diff --git a/vendor/rkh/patches/third_party/u-boot-2010.01/u-boot-2020.01.patch b/vendor/rkh/patches/third_party/u-boot-2010.01/u-boot-2020.01.patch new file mode 100644 index 0000000000000000000000000000000000000000..aa7d615912c679a83718601ead638b75f99b2aa8 --- /dev/null +++ b/vendor/rkh/patches/third_party/u-boot-2010.01/u-boot-2020.01.patch @@ -0,0 +1,402993 @@ +diff --git a/Kconfig b/Kconfig +index 92fc4fc..4a5d2c3 100644 +--- a/Kconfig ++++ b/Kconfig +@@ -309,8 +309,8 @@ config ANDROID_BOOT_IMAGE + + config FIT + bool "Support Flattened Image Tree" +- select MD5 +- select SHA1 ++ select MD5 if !VENDOR_MC ++ select SHA1 if !VENDOR_MC + help + This option allows you to boot the new uImage structure, + Flattened Image Tree. FIT is formally a FDT, which can include +@@ -347,11 +347,21 @@ config FIT_ENABLE_SHA256_SUPPORT + SHA256 variant is supported: SHA512 and others are not currently + supported in U-Boot. + ++config FIT_FULL_CHECK ++ bool "Do a full check of the FIT before using it" ++ default y ++ help ++ Enable this do a full check of the FIT to make sure it is valid. This ++ helps to protect against carefully crafted FITs which take advantage ++ of bugs or omissions in the code. This includes a bad structure, ++ multiple root nodes and the like. ++ + config FIT_SIGNATURE + bool "Enable signature verification of FIT uImages" + depends on DM + select HASH + select RSA ++ select FIT_FULL_CHECK + help + This option enables signature verification of FIT uImages, + using a hash signed and verified using RSA. If +@@ -383,6 +393,14 @@ config FIT_ENABLE_RSASSA_PSS_SUPPORT + Enable this to support the pss padding algorithm as described + in the rfc8017 (https://tools.ietf.org/html/rfc8017). + ++config FIT_CIPHER ++ bool "Enable ciphering data in a FIT uImages" ++ depends on DM ++ select AES ++ help ++ Enable the feature of data ciphering/unciphering in the tool mkimage ++ and in the u-boot support of the FIT image. ++ + config FIT_VERBOSE + bool "Show verbose messages when FIT images fail" + help +@@ -427,11 +445,20 @@ config SPL_FIT_PRINT + help + Support printing the content of the fitImage in a verbose manner in SPL. + ++config SPL_FIT_FULL_CHECK ++ bool "Do a full check of the FIT before using it" ++ help ++ Enable this do a full check of the FIT to make sure it is valid. This ++ helps to protect against carefully crafted FITs which take advantage ++ of bugs or omissions in the code. This includes a bad structure, ++ multiple root nodes and the like. ++ + config SPL_FIT_SIGNATURE + bool "Enable signature verification of FIT firmware within SPL" + depends on SPL_DM + select SPL_FIT + select SPL_RSA ++ select SPL_FIT_FULL_CHECK + + config SPL_LOAD_FIT + bool "Enable SPL loading U-Boot as a FIT (basic fitImage features)" +@@ -595,3 +622,5 @@ source "fs/Kconfig" + source "lib/Kconfig" + + source "test/Kconfig" ++ ++source "product/Kconfig" +diff --git a/Makefile b/Makefile +index 1766f5a..e1f1215 100644 +--- a/Makefile ++++ b/Makefile +@@ -16,7 +16,7 @@ NAME = + # (this increases performance and avoids hard-to-debug behaviour); + # o Look for make include files relative to root of kernel src + MAKEFLAGS += -rR --include-dir=$(CURDIR) +- ++-include include/autoconf.mk + # Determine host architecture + include include/host_arch.h + MK_ARCH="${shell uname -m}" +@@ -34,7 +34,7 @@ else ifeq ("riscv32", $(MK_ARCH)) + else ifeq ("riscv64", $(MK_ARCH)) + export HOST_ARCH=$(HOST_ARCH_RISCV64) + endif +-undefine MK_ARCH ++#undefine MK_ARCH #change open souce code warning + + # Avoid funny character set dependencies + unexport LC_ALL +@@ -311,7 +311,7 @@ os_x_before = $(shell if [ $(DARWIN_MAJOR_VERSION) -le $(1) -a \ + $(DARWIN_MINOR_VERSION) -le $(2) ] ; then echo "$(3)"; else echo "$(4)"; fi ;) + + os_x_after = $(shell if [ $(DARWIN_MAJOR_VERSION) -ge $(1) -a \ +- $(DARWIN_MINOR_VERSION) -ge $(2) ] ; then echo "$(3)"; else echo "$(4)"; fi ;) ++ $(DARWIN_MINOR_VERSION) -ge $(2) ] ; then echo "$(3)"; else echo "$(4)"; fi ;) + + # Snow Leopards build environment has no longer restrictions as described above + HOSTCC = $(call os_x_before, 10, 5, "cc", "gcc") +@@ -323,7 +323,7 @@ HOSTLDFLAGS += $(call os_x_before, 10, 5, "-multiply_defined suppress") + # tools + HOSTLDFLAGS += $(call os_x_before, 10, 7, "", "-Xlinker -no_pie") + +-# macOS Mojave (10.14.X) ++# macOS Mojave (10.14.X) + # Undefined symbols for architecture x86_64: "_PyArg_ParseTuple" + HOSTLDFLAGS += $(call os_x_after, 10, 14, "-lpython -dynamclib", "") + endif +@@ -407,6 +407,7 @@ PYTHON3 = python3 + DTC ?= $(objtree)/scripts/dtc/dtc + CHECK = sparse + ++HW_DIR = hw_compressed + CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ + -Wbitwise -Wno-return-void -D__CHECK_ENDIAN__ $(CF) + +@@ -415,7 +416,8 @@ KBUILD_CPPFLAGS := -D__KERNEL__ -D__UBOOT__ + KBUILD_CFLAGS := -Wall -Wstrict-prototypes \ + -Wno-format-security \ + -fno-builtin -ffreestanding $(CSTD_FLAG) +-KBUILD_CFLAGS += -fshort-wchar -fno-strict-aliasing ++# del -fshort-wchar Compiling Options change warning ++KBUILD_CFLAGS += -fno-strict-aliasing + KBUILD_AFLAGS := -D__ASSEMBLY__ + + # Don't generate position independent code +@@ -724,15 +726,15 @@ HAVE_VENDOR_COMMON_LIB = $(if $(wildcard $(srctree)/board/$(VENDOR)/common/Makef + libs-y += lib/ + libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/ + libs-$(CONFIG_OF_EMBED) += dts/ +-libs-y += fs/ +-libs-y += net/ +-libs-y += disk/ ++libs-$(CONFIG_PARTITIONS) += fs/ ++libs-$(CONFIG_NET) += net/ ++libs-$(CONFIG_PARTITIONS) += disk/ + libs-y += drivers/ + libs-y += drivers/dma/ + libs-y += drivers/gpio/ + libs-y += drivers/i2c/ +-libs-y += drivers/net/ +-libs-y += drivers/net/phy/ ++libs-$(CONFIG_NET) += drivers/net/ ++libs-$(CONFIG_NET) += drivers/net/phy/ + libs-y += drivers/power/ \ + drivers/power/domain/ \ + drivers/power/fuel_gauge/ \ +@@ -746,18 +748,19 @@ libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/ + libs-$(CONFIG_SYS_FSL_MMDC) += drivers/ddr/fsl/ + libs-$(CONFIG_$(SPL_)ALTERA_SDRAM) += drivers/ddr/altera/ + libs-y += drivers/serial/ +-libs-y += drivers/usb/cdns3/ +-libs-y += drivers/usb/dwc3/ +-libs-y += drivers/usb/common/ +-libs-y += drivers/usb/emul/ +-libs-y += drivers/usb/eth/ ++libs-$(CONFIG_USB) += drivers/usb/cdns3/ ++libs-$(CONFIG_USB) += drivers/usb/dwc3/ ++libs-$(CONFIG_USB) += drivers/usb/common/ ++libs-$(CONFIG_USB) += drivers/usb/emul/ ++libs-$(CONFIG_USB) += drivers/usb/eth/ + libs-$(CONFIG_USB_GADGET) += drivers/usb/gadget/ + libs-$(CONFIG_USB_GADGET) += drivers/usb/gadget/udc/ +-libs-y += drivers/usb/host/ +-libs-y += drivers/usb/musb/ +-libs-y += drivers/usb/musb-new/ +-libs-y += drivers/usb/phy/ +-libs-y += drivers/usb/ulpi/ ++libs-$(CONFIG_USB_GADGET) += drivers/usb/gadget/udc3/ ++libs-$(CONFIG_USB_HOST) += drivers/usb/host/ ++libs-$(CONFIG_USB) += drivers/usb/musb/ ++libs-$(CONFIG_USB) += drivers/usb/musb-new/ ++libs-$(CONFIG_USB) += drivers/usb/phy/ ++libs-$(CONFIG_USB) += drivers/usb/ulpi/ + libs-y += cmd/ + libs-y += common/ + libs-y += env/ +@@ -767,6 +770,26 @@ libs-$(CONFIG_UNIT_TEST) += test/ test/dm/ + libs-$(CONFIG_UT_ENV) += test/env/ + libs-$(CONFIG_UT_OPTEE) += test/optee/ + libs-$(CONFIG_UT_OVERLAY) += test/overlay/ ++# for ot_osd ++sinclude Makefile-otproduct ++ ++export CONFIG_PRODUCTNAME ++ ++ifdef CONFIG_CIPHER_ENABLE ++libs-$(CONFIG_CIPHER_ENABLE) += product/security_subsys/cipher/ ++endif ++ifdef CONFIG_KLAD_ENABLE ++libs-$(CONFIG_KLAD_ENABLE) += product/security_subsys/klad/src/ ++endif ++ifdef CONFIG_OTP_ENABLE ++libs-$(CONFIG_OTP_ENABLE) += product/security_subsys/otp/src/ ++endif ++ ++libs-$(CONFIG_I2C_BSP) += product/i2c/ ++ ++libs-$(CONFIG_AUTO_UPDATE) += product/update/ ++ ++libs-y += securec/src/ + + libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/) + +@@ -1148,7 +1171,16 @@ endif + ifeq ($(CONFIG_MULTI_DTB_FIT),y) + IMX_DEPS = u-boot-fit-dtb.bin + endif ++.PHONY: u-boot-z.bin ++u-boot-z.bin: $(CURDIR)/u-boot.bin ++ make -C $(CURDIR)/arch/$(ARCH)/cpu/$(CPU)/$(SOC)/$(HW_DIR)/ \ ++ CROSS_COMPILE=$(CROSS_COMPILE) \ ++ BINIMAGE=$(CURDIR)/u-boot.bin TOPDIR=$(CURDIR) + ++.PHONY: u-boot-z.clean ++u-boot-z.clean: ++ make -C $(CURDIR)/arch/$(ARCH)/cpu/$(CPU)/$(SOC)/$(HW_DIR) \ ++ CROSS_COMPILE=$(CROSS_COMPILE) clean + %.imx: $(IMX_DEPS) %.bin + $(Q)$(MAKE) $(build)=arch/arm/mach-imx $@ + $(BOARD_SIZE_CHECK) +@@ -1755,7 +1787,12 @@ prepare0: archprepare FORCE + $(Q)$(MAKE) $(build)=. + + # All the preparing.. ++ifdef CONFIG_DDR_TRAINING_V2 ++prepare: ddr_training_prepare ++ddr_training_prepare: prepare0 ++else + prepare: prepare0 ++endif + + # Generate some files + # --------------------------------------------------------------------------- +@@ -1950,7 +1987,7 @@ PHONY += $(clean-dirs) clean archclean + $(clean-dirs): + $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) + +-clean: $(clean-dirs) ++clean: $(clean-dirs) ddr_training_clean + $(call cmd,rmdirs) + $(call cmd,rmfiles) + @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ +@@ -2163,6 +2200,26 @@ endif + + endif # skip-makefile + ++ifneq ($(filter $(CONFIG_PRODUCTNAME), "ss928v100" "ss927v100"),) ++DDRT_DEFAULT := default_v2 ++else ++DDRT_DEFAULT := default ++endif ++ ++DDR_SSRC := ddr_training_custom.h ddr_training_custom.c ++ ++ddr_training_prepare:$(DDR_SSRC) ++ make -C $(srctree)/drivers/ddr/vendor/$(DDRT_DEFAULT)/cmd_bin all ++ ++ddr_training_clean: ++ make -C $(srctree)/drivers/ddr/vendor/$(DDRT_DEFAULT)/cmd_bin clean ++ @for file in $(DDR_SSRC); \ ++ do \ ++ rm -f $(srctree)/drivers/ddr/vendor/$(DDRT_DEFAULT)/$$file; \ ++ done ++ @if [ -f $(srctree)/ddr_cmd.bin ]; then rm $(srctree)/ddr_cmd.bin; fi; ++$(DDR_SSRC): ++ ln -sf ../$(SOC)/$@ $(CURDIR)/drivers/ddr/vendor/$(DDRT_DEFAULT)/$@ + PHONY += FORCE + FORCE: + +diff --git a/Makefile-otproduct b/Makefile-otproduct +new file mode 100644 +index 0000000..048d959 +--- /dev/null ++++ b/Makefile-otproduct +@@ -0,0 +1,100 @@ ++# FOR AUDIO ++ifeq ($(CONFIG_AUDIO_ENABLE), y) ++ifeq ($(CONFIG_PRODUCTNAME),"ss101v200") ++libs-y += product/audio/acodec/v750/ ++libs-y += product/audio/ao/ss101v200/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v500") ++libs-y += product/audio/acodec/v750/ ++libs-y += product/audio/ao/ss101v200/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v300") ++libs-y += product/audio/acodec/v750/ ++libs-y += product/audio/ao/ss101v200/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v600") ++libs-y += product/audio/acodec/v750/ ++libs-y += product/audio/ao/ss101v200/ ++endif ++endif ++ ++ifeq ($(CONFIG_OSD_ENABLE),y) ++# FOR DEC of all chip ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v200") ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v500") ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v300") ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v600") ++libs-y += ./product/ot_osd/dec/ ++endif ++endif ++endif ++endif ++ ++# FOR VO,HDMI,MIPI_Tx of ss919v100 ++ifeq ($(CONFIG_PRODUCTNAME),"ss919v100") ++libs-y += ./product/ot_osd/vo/ss919v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss919v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss918v100") ++libs-y += ./product/ot_osd/vo/ss918v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss918v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss013v100") ++libs-y += ./product/ot_osd/vo/ss918v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss918v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss812v100") ++libs-y += ./product/ot_osd/vo/ss812v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss812v100/ ++libs-y += ./product/ot_osd/rgb/ss812v100/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss813v100") ++libs-y += ./product/ot_osd/vo/ss812v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss812v100/ ++libs-y += ./product/ot_osd/rgb/ss812v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss815v100") ++libs-y += ./product/ot_osd/vo/ss812v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss812v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++libs-y += ./product/ot_osd/rgb/ss812v100/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v500") ++libs-y += ./product/ot_osd/vo/ss101v200/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v200") ++libs-y += ./product/ot_osd/vo/ss101v200/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v300") ++libs-y += ./product/ot_osd/vo/ss101v200/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v600") ++libs-y += ./product/ot_osd/vo/ss101v200/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss312v100") ++libs-y += ./product/ot_osd/vo/ss812v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss812v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++libs-y += ./product/ot_osd/rgb/ss812v100/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss313v100") ++libs-y += ./product/ot_osd/vo/ss812v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss812v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++libs-y += ./product/ot_osd/rgb/ss812v100/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss011v100") ++libs-y += ./product/ot_osd/vo/ss812v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss812v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++libs-y += ./product/ot_osd/rgb/ss812v100/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss012v100") ++libs-y += ./product/ot_osd/vo/ss812v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss812v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++libs-y += ./product/ot_osd/rgb/ss812v100/ ++else ifeq ($(CONFIG_PRODUCTNAME),"ss015v100") ++libs-y += ./product/ot_osd/vo/ss919v100/ ++libs-y += ./product/ot_osd/mipi_tx/ss919v100/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++else ifeq ($(CONFIG_PRODUCTNAME),$(filter $(CONFIG_PRODUCTNAME), "ss528v100" "ss625v100" "ss524v100" "ss522v101" "ss522v100" "ss615v100")) ++libs-y += ./product/ot_osd/vo/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++else ifeq ($(CONFIG_PRODUCTNAME),$(filter $(CONFIG_PRODUCTNAME), "ss928v100" "ss927v100")) ++libs-y += ./product/ot_osd/vo/ ++libs-y += ./product/ot_osd/hdmi/hdmi_2_0/drv/ ++libs-y += ./product/ot_osd/mipi_tx/ss928v100/ ++else ++$(warning "warning: "$(CONFIG_PRODUCTNAME)" is invalid") ++endif ++ ++endif # end of ifeq ($(CONFIG_OSD_ENABLE),y) +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 36c9c2f..d4a7fae 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1229,6 +1229,25 @@ config TARGET_HIKEY + Support for HiKey 96boards platform. It features a HI6220 + SoC, with 8xA53 CPU, mali450 gpu, and 1GB RAM. + ++config TARGET_SS928V100 ++ bool "Support SS928V100" ++ select ARM64 ++ select DM ++ help ++ Support for SS928V100 platform. ++ ++config TARGET_SS927V100 ++ bool "Support SS927V100" ++ select ARM64 ++ select DM ++ help ++ Support for SS927V100 platform. ++ ++config ARCH_VENDOR ++ bool "VENDOR SoCs" ++ select DM ++ ++ + config TARGET_HIKEY960 + bool "Support HiKey960 96boards Consumer Edition Platform" + select ARM64 +@@ -1690,6 +1709,8 @@ source "arch/arm/mach-exynos/Kconfig" + + source "arch/arm/mach-highbank/Kconfig" + ++source "arch/arm/mach-vendor/Kconfig" ++ + source "arch/arm/mach-integrator/Kconfig" + + source "arch/arm/mach-k3/Kconfig" +@@ -1816,6 +1837,8 @@ source "board/freescale/s32v234evb/Kconfig" + source "board/grinn/chiliboard/Kconfig" + source "board/gumstix/pepper/Kconfig" + source "board/hisilicon/hikey/Kconfig" ++source "board/vendor/ss928v100/Kconfig" ++source "board/vendor/ss927v100/Kconfig" + source "board/hisilicon/hikey960/Kconfig" + source "board/hisilicon/poplar/Kconfig" + source "board/isee/igep003x/Kconfig" +@@ -1846,6 +1869,6 @@ endmenu + config SPL_LDSCRIPT + default "arch/arm/cpu/arm926ejs/mxs/u-boot-spl.lds" if (ARCH_MX23 || ARCH_MX28) && !SPL_FRAMEWORK + default "arch/arm/cpu/arm1136/u-boot-spl.lds" if CPU_ARM1136 +- default "arch/arm/cpu/armv8/u-boot-spl.lds" if ARM64 ++ default "arch/arm/cpu/armv8/u-boot-spl.lds" if ARM64 && !TARGET_SS928V100 && !TARGET_SS927V100 + + +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 856f2d8..5990fe6 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -59,6 +59,7 @@ machine-$(CONFIG_ARCH_DAVINCI) += davinci + machine-$(CONFIG_ARCH_EXYNOS) += exynos + machine-$(CONFIG_ARCH_HIGHBANK) += highbank + machine-$(CONFIG_ARCH_K3) += k3 ++machine-$(CONFIG_ARCH_VENDOR) += vendor + machine-$(CONFIG_ARCH_KEYSTONE) += keystone + # TODO: rename CONFIG_KIRKWOOD -> CONFIG_ARCH_KIRKWOOD + machine-$(CONFIG_KIRKWOOD) += kirkwood +diff --git a/arch/arm/config.mk b/arch/arm/config.mk +index f256031..ea657e9 100644 +--- a/arch/arm/config.mk ++++ b/arch/arm/config.mk +@@ -132,11 +132,11 @@ endif + + # limit ourselves to the sections we want in the .bin. + ifdef CONFIG_ARM64 +-OBJCOPYFLAGS += -j .text -j .secure_text -j .secure_data -j .rodata -j .data \ ++OBJCOPYFLAGS += -j .text -j .image -j .secure_text -j .secure_data -j .rodata -j .data \ + -j .u_boot_list -j .rela.dyn -j .got -j .got.plt \ + -j .binman_sym_table -j .text_rest + else +-OBJCOPYFLAGS += -j .text -j .secure_text -j .secure_data -j .rodata -j .hash \ ++OBJCOPYFLAGS += -j .text -j .secure_text -j .image -j .secure_data -j .rodata -j .hash \ + -j .data -j .got -j .got.plt -j .u_boot_list -j .rel.dyn \ + -j .binman_sym_table -j .text_rest + endif +diff --git a/arch/arm/cpu/armv7/Kconfig b/arch/arm/cpu/armv7/Kconfig +index 73d57a2..61782d9 100644 +--- a/arch/arm/cpu/armv7/Kconfig ++++ b/arch/arm/cpu/armv7/Kconfig +@@ -11,7 +11,7 @@ config ARCH_SUPPORT_PSCI + + config ARMV7_NONSEC + bool "Enable support for booting in non-secure mode" if EXPERT +- depends on CPU_V7_HAS_NONSEC ++ depends on CPU_V7_HAS_NONSEC + default y + ---help--- + Say Y here to enable support for booting in non-secure / SVC mode. +diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c +index 99eb7db..b70a6da 100644 +--- a/arch/arm/cpu/armv7/cache_v7.c ++++ b/arch/arm/cpu/armv7/cache_v7.c +@@ -117,9 +117,32 @@ void flush_dcache_all(void) + */ + void invalidate_dcache_range(unsigned long start, unsigned long stop) + { +- check_cache_range(start, stop); ++ unsigned int align = 0; + +- v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE); ++ if (!IS_ALIGNED(start, CONFIG_SYS_CACHELINE_SIZE)){ ++ align = 1; ++ } ++ ++ if (!IS_ALIGNED(stop, CONFIG_SYS_CACHELINE_SIZE)){ ++ align = 1; ++ } ++ ++ if (!align){ ++ v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE); ++ } ++ else{ ++ u32 line_len, ccsidr; ++ ++ ccsidr = get_ccsidr(); ++ line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> ++ CCSIDR_LINE_SIZE_OFFSET) + 2; ++ /* Converting from words to bytes */ ++ line_len += 2; ++ /* converting from log2(linelen) to linelen */ ++ line_len = 1 << line_len; ++ ++ v7_dcache_clean_inval_range(start, stop, line_len); ++ } + + v7_outer_cache_inval_range(start, stop); + } +@@ -131,8 +154,6 @@ void invalidate_dcache_range(unsigned long start, unsigned long stop) + */ + void flush_dcache_range(unsigned long start, unsigned long stop) + { +- check_cache_range(start, stop); +- + v7_dcache_maint_range(start, stop, ARMV7_DCACHE_CLEAN_INVAL_RANGE); + + v7_outer_cache_flush_range(start, stop); +diff --git a/arch/arm/cpu/armv7/config.mk b/arch/arm/cpu/armv7/config.mk +index 68036d6..e85da3d 100644 +--- a/arch/arm/cpu/armv7/config.mk ++++ b/arch/arm/cpu/armv7/config.mk +@@ -8,4 +8,5 @@ + # the default so we must pass in -mno-unaligned-access so that it is aware + # of our decision. + PF_NO_UNALIGNED := $(call cc-option, -mno-unaligned-access,) ++PF_NO_UNALIGNED += $(call cc-option, -fno-store-merging,) + PLATFORM_CPPFLAGS += $(PF_NO_UNALIGNED) +diff --git a/arch/arm/cpu/armv7/cpu.c b/arch/arm/cpu/armv7/cpu.c +index 68807d2..24a7383 100644 +--- a/arch/arm/cpu/armv7/cpu.c ++++ b/arch/arm/cpu/armv7/cpu.c +@@ -55,8 +55,9 @@ int cleanup_before_linux_select(int flags) + * any static data) So just invalidate the entire d-cache again + * to avoid coherency problems for kernel + */ ++#ifndef CONFIG_SYS_LEVEL2_CACHE_SHARE + invalidate_dcache_all(); +- ++#endif + icache_disable(); + invalidate_icache_all(); + } else { +diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S +old mode 100644 +new mode 100755 +index dcb4195..e02cb55 +--- a/arch/arm/cpu/armv7/start.S ++++ b/arch/arm/cpu/armv7/start.S +@@ -88,7 +88,6 @@ switch_to_hypervisor_ret: + bl cpu_init_crit + #endif + #endif +- + bl _main + + /*------------------------------------------------------------------------------*/ +@@ -120,7 +119,6 @@ ENTRY(save_boot_params) + b save_boot_params_ret @ back to my caller + ENDPROC(save_boot_params) + .weak save_boot_params +- + #ifdef CONFIG_ARMV7_LPAE + ENTRY(switch_to_hypervisor) + b switch_to_hypervisor_ret +diff --git a/arch/arm/cpu/armv8/Kconfig b/arch/arm/cpu/armv8/Kconfig +index 16c83e8..24e6542 100644 +--- a/arch/arm/cpu/armv8/Kconfig ++++ b/arch/arm/cpu/armv8/Kconfig +@@ -110,7 +110,8 @@ config PSCI_RESET + !TARGET_LS1046AFRWY && \ + !TARGET_LS2081ARDB && !TARGET_LX2160ARDB && \ + !TARGET_LX2160AQDS && \ +- !ARCH_UNIPHIER && !TARGET_S32V234EVB ++ !ARCH_UNIPHIER && !TARGET_S32V234EVB && \ ++ !TARGET_SS928V100 && !TARGET_SS927V100 + help + Most armv8 systems have PSCI support enabled in EL3, either through + ARM Trusted Firmware or other firmware. +diff --git a/arch/arm/cpu/armv8/generic_timer.c b/arch/arm/cpu/armv8/generic_timer.c +index 46e6329..d673548 100644 +--- a/arch/arm/cpu/armv8/generic_timer.c ++++ b/arch/arm/cpu/armv8/generic_timer.c +@@ -14,7 +14,7 @@ DECLARE_GLOBAL_DATA_PTR; + /* + * Generic timer implementation of get_tbclk() + */ +-unsigned long get_tbclk(void) ++__weak unsigned long get_tbclk(void) + { + unsigned long cntfrq; + asm volatile("mrs %0, cntfrq_el0" : "=r" (cntfrq)); +@@ -32,7 +32,7 @@ unsigned long get_tbclk(void) + * was the same in both reads. + * Assumes that the CPU runs in much higher frequency than the timer. + */ +-unsigned long timer_read_counter(void) ++__weak unsigned long timer_read_counter(void) + { + unsigned long cntpct; + unsigned long temp; +@@ -86,13 +86,19 @@ unsigned long timer_read_counter(void) + } + #endif + +-uint64_t get_ticks(void) ++__weak uint64_t get_ticks(void) + { + unsigned long ticks = timer_read_counter(); + + gd->arch.tbl = ticks; + +- return ticks; ++ /* increment tbu if tbl has rolled over */ ++ if (ticks < gd->timebase_l) { ++ gd->timebase_h++; ++ } ++ ++ gd->timebase_l = ticks; ++ return ((uint64_t)gd->timebase_h << 32) | gd->timebase_l; + } + + unsigned long usec2ticks(unsigned long usec) +diff --git a/arch/arm/cpu/armv8/ss927v100/hw_compressed/Makefile b/arch/arm/cpu/armv8/ss927v100/hw_compressed/Makefile +new file mode 100644 +index 0000000..e5184b0 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/hw_compressed/Makefile +@@ -0,0 +1,154 @@ ++################################################################################ ++ ++PWD = $(shell pwd) ++CROSS_COMPILE =: aarch64-c01v01-linux-gnu- ++TOPDIR = ++ ++################################################################################ ++CC := $(CROSS_COMPILE)gcc ++AR := $(CROSS_COMPILE)ar ++LD := $(CROSS_COMPILE)ld ++OBJCOPY := $(CROSS_COMPILE)objcopy ++OBJDUMP := $(CROSS_COMPILE)objdump ++ ++ ++################################################################################ ++BOOT := u-boot-$(SOC) ++TEXTBASE := 0x48700000 ++CFLAGS :=-Os -fno-builtin -ffreestanding \ ++ -D__KERNEL__ -DTEXT_BASE=$(TEXTBASE) \ ++ -I$(TOPDIR)/include \ ++ -I$(TOPDIR)/include/linux \ ++ -I$(TOPDIR)/drivers/ddr/vendor/default_v2 \ ++ -I$(TOPDIR)/drivers/ddr/vendor/$(SOC) \ ++ -I$(TOPDIR)/arch/arm/include \ ++ -fno-pic -mstrict-align -ffunction-sections \ ++ -fdata-sections -fno-common -ffixed-r9 \ ++ -fno-common -ffixed-x18 -pipe -march=armv8-a \ ++ -Wall -Wstrict-prototypes -fno-stack-protector \ ++ -D__LINUX_ARM_ARCH__=8 -D__ARM__ \ ++ -DCONFIG_SS927V100 -DCONFIG_MMC -DDDR_TRAINING_MEM_FUNC \ ++ $(MKFLAGS) -fno-strict-aliasing -mcmodel=tiny ++ ++################################################################################ ++ ++START := start.o ++COBJS := lowlevel_init_v300.o \ ++ init_registers.o \ ++ sdhci_boot.o \ ++ uart.o \ ++ ddr_training_impl.o \ ++ ddr_ac_training.o \ ++ ddr_dcc_training.o \ ++ ddr_ddrt_training.o \ ++ ddr_gate_training.o \ ++ ddr_lpca_training.o \ ++ ddr_mpr_training.o \ ++ ddr_pcode_training.o \ ++ ddr_wl_training.o \ ++ ddr_training_ctl.o \ ++ ddr_training_boot.o \ ++ ddr_training_custom.o \ ++ ddr_training_console.o \ ++ startup.o \ ++ image_data.o \ ++ div0.o \ ++ reset.o ++ ++SSRC := arch/arm/cpu/armv8/$(SOC)/start.S \ ++ arch/arm/cpu/armv8/$(SOC)/reset.S \ ++ arch/arm/cpu/armv8/$(SOC)/sdhci_boot.c \ ++ arch/arm/cpu/armv8/$(SOC)/uart.S \ ++ arch/arm/cpu/armv8/$(SOC)/init_registers.c \ ++ arch/arm/cpu/armv8/$(SOC)/lowlevel_init_v300.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_impl.c \ ++ drivers/ddr/vendor/default_v2/ddr_ac_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_dcc_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_ddrt_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_gate_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_lpca_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_mpr_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_pcode_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_wl_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_ctl.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_boot.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_console.c \ ++ drivers/ddr/vendor/$(SOC)/ddr_training_custom.c \ ++ arch/arm/lib/div0.c \ ++ lib/hw_dec/hw_decompress.c \ ++ lib/hw_dec/hw_decompress_$(SOC).c \ ++ lib/hw_dec/hw_decompress_v2.c \ ++ lib/hw_dec/hw_decompress_v2.h ++ ++REG := $(wildcard $(TOPDIR)/*.reg $(TOPDIR)/.reg) ++SRC := $(notdir $(SSRC)) ++ ++################################################################################ ++.PHONY: $(BOOT).bin ++$(BOOT).bin: $(BOOT).tmp regfile ++ @dd if=./$(BOOT).tmp of=./tmp1 bs=1 count=64 2>/dev/null ++ @dd if=$(REG) of=./tmp2 bs=11008 conv=sync 2>/dev/null ++ @dd if=./$(BOOT).tmp of=./tmp3 bs=1 skip=11072 2>/dev/null ++ @cat tmp1 tmp2 tmp3 > $(BOOT).bin ++ @rm -f tmp1 tmp2 tmp3 ++ @chmod 754 $(BOOT).bin ++ @cp -fv $@ $(TOPDIR) ++ @echo $(BOOT).bin is Ready. ++ ++$(BOOT).tmp: $(BOOT).elf ++ $(OBJCOPY) -O srec $< $(BOOT).srec ++ $(OBJCOPY) -j .text -O binary $< $(BOOT).text ++ $(OBJCOPY) --gap-fill=0xff -O binary $< $@ ++ ++$(BOOT).elf: image_data.gzip $(SRC) $(START) $(COBJS) ++ $(LD) -Bstatic -T u-boot.lds -Ttext $(TEXTBASE) $(START) \ ++ $(COBJS) -Map $(BOOT).map -o $@ ++ $(OBJDUMP) -d $@ > $@.asm ++ ++.PHONY: regfile ++regfile: ++ @if [ "$(words $(REG))" = "0" ]; then ( \ ++ echo '***' Need '.reg' or '*.reg' file in directory $(TOPDIR); \ ++ exit 1; \ ++ ) fi ++ @if [ "$(words $(REG))" != "1" ]; then ( \ ++ echo '***' Found multi '.reg' or '*.reg' file in directory $(TOPDIR); \ ++ echo '***' Files: $(notdir $(REG)); \ ++ exit 1; \ ++ ) fi ++ ++################################################################################ ++start.o: start.S ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ $< -c ++ ++# -1 : --fast -9 : --best ++image_data.gzip: $(BINIMAGE) ++ gzip -fNqc -7 $< > $@ ++ ++%.o: %.c ++ $(CC) $(CFLAGS) -Wall -Wstrict-prototypes \ ++ -fno-stack-protector -o $@ $< -c ++ ++%.o: %.S ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ $< -c ++ ++image_data.o: image_data.S image_data.gzip ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ $< -c ++ ++############################################################################# ++ ++$(SRC): ++ ln -sf ../../../../../../$(filter %/$@,$(SSRC)) $@ ++################################################################################ ++TMPS := $(COBJS) start.o $(SRC) \ ++ $(BOOT).map $(BOOT).elf $(BOOT).srec $(BOOT).bin $(BOOT).text $(BOOT).tmp \ ++ image_data.gzip ++ ++distclean: clean ++ ++clean: ++ -rm -f $(TMPS) ++ ++################################################################################ ++.PHONY: clean ++################################################################################ +diff --git a/arch/arm/cpu/armv8/ss927v100/hw_compressed/image_data.S b/arch/arm/cpu/armv8/ss927v100/hw_compressed/image_data.S +new file mode 100644 +index 0000000..f8d2d36 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/hw_compressed/image_data.S +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ .section .image,"a" ++ .globl input_data ++ /*gzip source addr must be 16 bytes aligned*/ ++ .balign 16 ++input_data: ++ .incbin "image_data.gzip" ++ .globl input_data_end ++input_data_end: +diff --git a/arch/arm/cpu/armv8/ss927v100/hw_compressed/startup.c b/arch/arm/cpu/armv8/ss927v100/hw_compressed/startup.c +new file mode 100644 +index 0000000..4d1785c +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/hw_compressed/startup.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++const uintptr_t image_entry = (CONFIG_SYS_TEXT_BASE); ++ ++#define error(_s) uart_early_puts(_s) ++#define putstr(_s) uart_early_puts(_s) ++#define EXCEPTION_LEVEL1 1 ++#define EXCEPTION_LEVEL2 2 ++#include "hw_decompress.c" ++#define GZIP_SIZE_OFFSET 0x4 ++ ++void icache_enable(void); ++void invalidate_icache_all(void); ++void __asm_invalidate_icache_all(void); ++ ++void start_armboot(void) ++{ ++ unsigned char *pdst_h32 = NULL; ++ unsigned char *pdst_l32 = NULL; ++ unsigned char *input_data_h32 = NULL; ++ unsigned int image_data_len; ++ int pdst_len; ++ int ret; ++ uart_early_init(); ++ uart_early_puts("\r\nUncompress "); ++ ++ /* enable I-Cache */ ++ icache_enable(); ++ ++ /* use direct address mode */ ++ hw_dec_type = 0; ++ /* init hw decompress IP */ ++ hw_dec_init(); ++ ++ /* start decompress */ ++ pdst_l32 = (unsigned char *)(uintptr_t)image_entry; ++ image_data_len = input_data_end - input_data; ++ for (int i = 0; i < sizeof(pdst_len); i++) ++ *((char *)(&pdst_len) + i) = *((char *)(input_data_end - ++ GZIP_SIZE_OFFSET) + i); ++ ++ ret = hw_dec_decompress(pdst_h32, pdst_l32, &pdst_len, input_data_h32, ++ input_data, image_data_len, NULL); ++ if (!ret) { ++ uart_early_puts("Ok!"); ++ } else { ++ uart_early_puts("Fail!"); ++ while (1); ++ } ++ ++ /* uinit hw decompress IP */ ++ hw_dec_uinit(); ++ void (*uboot)(void); ++ uboot = (void (*))CONFIG_SYS_TEXT_BASE; ++ invalidate_icache_all(); ++ uboot(); ++} ++ ++void hang(void) ++{ ++ uart_early_puts("### ERROR ### Please RESET the board ###\n"); ++ for (; ;); ++} ++ ++void invalidate_icache_all(void) ++{ ++ __asm_invalidate_icache_all(); ++} ++ ++static inline unsigned int current_el(void) ++{ ++ unsigned int el; ++ asm volatile("mrs %0, CurrentEL" : "=r"(el) : : "cc"); ++ return el >> 2; /* Move Left 2bit */ ++} ++ ++static void set_sctlr(unsigned int val) ++{ ++ unsigned int el; ++ ++ el = current_el(); ++ if (el == EXCEPTION_LEVEL1) ++ asm volatile("msr sctlr_el1, %0" : : "r"(val) : "cc"); ++ else if (el == EXCEPTION_LEVEL2) ++ asm volatile("msr sctlr_el2, %0" : : "r"(val) : "cc"); ++ else ++ asm volatile("msr sctlr_el3, %0" : : "r"(val) : "cc"); ++ ++ asm volatile("isb"); ++} ++ ++static unsigned int get_sctlr(void) ++{ ++ unsigned int el, val; ++ ++ el = current_el(); ++ if (el == EXCEPTION_LEVEL1) ++ asm volatile("mrs %0, sctlr_el1" : "=r"(val) : : "cc"); ++ else if (el == EXCEPTION_LEVEL2) ++ asm volatile("mrs %0, sctlr_el2" : "=r"(val) : : "cc"); ++ else ++ asm volatile("mrs %0, sctlr_el3" : "=r"(val) : : "cc"); ++ ++ return val; ++} ++ ++void icache_enable(void) ++{ ++ invalidate_icache_all(); ++#define CR_I (1 << 12) /* Icache enable */ ++ set_sctlr(get_sctlr() | CR_I); ++} ++ ++void do_bad_sync(void) ++{ ++ uart_early_puts("bad sync abort\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_sync(void) ++{ ++ uart_early_puts("sync abort\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_bad_error(void) ++{ ++ uart_early_puts("bad error\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_error(void) ++{ ++ uart_early_puts("error\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_bad_fiq(void) ++{ ++ uart_early_puts("bad fast interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_bad_irq(void) ++{ ++ uart_early_puts("bad interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_fiq(void) ++{ ++ uart_early_puts("fast interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_irq(void) ++{ ++ uart_early_puts("interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} +diff --git a/arch/arm/cpu/armv8/ss927v100/hw_compressed/u-boot.lds b/arch/arm/cpu/armv8/ss927v100/hw_compressed/u-boot.lds +new file mode 100644 +index 0000000..e0f0fdc +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/hw_compressed/u-boot.lds +@@ -0,0 +1,83 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") ++OUTPUT_ARCH(aarch64) ++ENTRY(_start) ++SECTIONS ++{ ++ . = 0x48700000; ++ __image_copy_start =.; ++ . = ALIGN(8); ++ .text : ++ { ++ __text_start = .; ++ start.o (.text*) ++ init_registers.o (.text*) ++ lowlevel_init_v300.o (.text*) ++ ddr_training_impl.o (.text*) ++ ddr_ac_training.o (.text*) ++ ddr_dcc_training.o (.text*) ++ ddr_ddrt_training.o (.text*) ++ ddr_gate_training.o (.text*) ++ ddr_lpca_training.o (.text*) ++ ddr_mpr_training.o (.text*) ++ ddr_pcode_training.o (.text*) ++ ddr_wl_training.o (.text*) ++ ddr_training_console.o (.text*) ++ ddr_training_ctl.o (.text*) ++ ddr_training_boot.o (.text*) ++ ddr_training_custom.o (.text*) ++ uart.o (.text*) ++ div0.o (.text*) ++ sdhci_boot.o (.text*) ++ image_data.o (.text*) ++ startup.o(.text*) ++ reset.o(.text*) ++ __init_end = .; ++ ASSERT(((__init_end - __text_start) < 0x8000), "init sections too big!"); ++ *(.text*) ++ } ++ ++ . = ALIGN(8); ++ .image : { *(.image) } ++ ++ . = ALIGN(8); ++ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ++ ++ . = ALIGN(8); ++ .data : { ++ *(.data*) ++ } ++ ++ . = ALIGN(8); ++ ++ .got : { *(.got) } ++ ++ . = ALIGN(8); ++ __image_copy_end =.; ++ __bss_start = .; ++ .bss : ++ { ++ *(.bss) ++ } ++ __bss_end = .; ++ ++ ++ _end = .; ++} +diff --git a/arch/arm/cpu/armv8/ss927v100/init_registers.c b/arch/arm/cpu/armv8/ss927v100/init_registers.c +new file mode 100644 +index 0000000..08cc9b6 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/init_registers.c +@@ -0,0 +1,179 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#define W_WHETHER_WRITE (1 << 0) ++#define W_WHETHER_PM (1 << 1) ++#define W_WHETHER_BOOT_NORMAL (1 << 2) ++#define W_BIT_OFFSET 3 ++#define W_BIT_MASK (0x1f << W_BIT_OFFSET) ++#define W_REG_BIT_OFFSET 11 ++#define W_REG_BIT_MASK (0x1f << W_REG_BIT_OFFSET) ++ ++#define R_WHETHER_READ (W_WHETHER_WRITE << 16) ++#define R_WHETHER_PM (W_WHETHER_PM << 16) ++#define R_WHETHER_BOOT_NORMAL (W_WHETHER_BOOT_NORMAL << 16) ++#define R_BIT_OFFSET (W_BIT_OFFSET + 16) ++#define R_BIT_MASK (W_BIT_MASK << 16) ++#define R_REG_BIT_OFFSET (W_REG_BIT_OFFSET + 16) ++#define R_REG_BIT_MASK (W_REG_BIT_MASK << 16) ++ ++#define RW_BIT_NUM 32 ++ ++struct regentry { ++ unsigned int reg_addr; ++ unsigned int value; ++ unsigned int delay; ++ unsigned int attr; ++}; ++ ++static void dwb(void) /* drain write buffer */ ++{ ++} ++ ++static void writel(unsigned val, unsigned addr) ++{ ++ dwb(); ++ (*(volatile unsigned *)(uintptr_t)(addr)) = (val); ++ dwb(); ++} ++ ++static void delay(void) ++{ ++ __asm__ __volatile__("nop"); ++} ++ ++static void reg_read(struct regentry* const reg, unsigned int* const ret) ++{ ++ unsigned int reg_val_r; ++ unsigned int bit_start_r; ++ unsigned int bit_num_r; ++ ++ bit_start_r = ((reg->attr & R_REG_BIT_MASK) >> R_REG_BIT_OFFSET); ++ bit_num_r = ((reg->attr & R_BIT_MASK) >> R_BIT_OFFSET) + 1; ++ reg_val_r = (*(volatile unsigned *)(long)(reg->reg_addr)); ++ ++ if (bit_num_r != RW_BIT_NUM) { ++ reg_val_r >>= bit_start_r; ++ reg_val_r &= ((1 << bit_num_r) - 1); ++ } ++ ++ *ret = ((reg_val_r == reg->value) ? 0 : 1); ++} ++ ++static void reg_write(struct regentry *reg) ++{ ++ unsigned int reg_val_w; ++ unsigned int delay_2; ++ unsigned int bit_start_w; ++ unsigned int bit_num_w; ++ ++ delay_2 = reg->delay; ++ bit_start_w = ((reg->attr & W_REG_BIT_MASK) >> W_REG_BIT_OFFSET); ++ bit_num_w = ((reg->attr & W_BIT_MASK) >> W_BIT_OFFSET) + 1; ++ reg_val_w = (*(volatile unsigned *)(long)(reg->reg_addr)); ++ ++ if (bit_num_w == RW_BIT_NUM) { ++ reg_val_w = reg->value; ++ } else { ++ reg_val_w &= (~(((1 << bit_num_w) - 1) << bit_start_w)); ++ reg_val_w |= (reg->value) << bit_start_w; ++ } ++ writel(reg_val_w, reg->reg_addr); ++ ++ do { ++ delay(); ++ } while (delay_2--); ++} ++ ++static void read_write(struct regentry *reg, unsigned int pm) ++{ ++ unsigned int ret; ++ unsigned int delay_1; ++ ++ ret = 0; ++ delay_1 = reg->delay; ++ ++ if (pm) { ++ if (reg->attr & W_WHETHER_PM) { ++ reg_write(reg); ++ } else if (reg->attr & R_WHETHER_PM) { ++ do { ++ reg_read(reg, &ret); ++ delay(); ++ } while (ret); ++ ++ do { ++ delay(); ++ } while (delay_1--); ++ } else { ++ do { ++ delay(); ++ } while (delay_1--); ++ } ++ } else { ++ if (reg->attr & W_WHETHER_BOOT_NORMAL) { ++ reg_write(reg); ++ } else if (reg->attr & R_WHETHER_BOOT_NORMAL) { ++ do { ++ reg_read(reg, &ret); ++ delay(); ++ } while (ret); ++ ++ do { ++ delay(); ++ } while (delay_1--); ++ } else { ++ do { ++ delay(); ++ } while (delay_1--); ++ } ++ } ++} ++ ++static void part_read_write(struct regentry *reg_table, unsigned int pm) ++{ ++ unsigned int i; ++ ++ for (i = 0; ; i++) { ++ if ((!reg_table[i].reg_addr) && (!reg_table[i].value) && ++ (!reg_table[i].delay) && (!reg_table[i].attr)) ++ goto main_end; ++ ++ read_write(®_table[i], pm); ++ } ++ ++main_end: ++ delay(); ++} ++ ++/* ++ * base - reg base address ++ * pm - is suspend ++ * 0 normal ++ * 1 pm ++ */ ++void init_registers(unsigned long base, unsigned long pm) ++{ ++ struct regentry *reg_table = (struct regentry *)(uintptr_t)base; ++ ++ part_read_write(reg_table, pm); ++} ++ +diff --git a/arch/arm/cpu/armv8/ss927v100/lowlevel_init_v300.c b/arch/arm/cpu/armv8/ss927v100/lowlevel_init_v300.c +new file mode 100644 +index 0000000..4c048a1 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/lowlevel_init_v300.c +@@ -0,0 +1,516 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ddr_interface.h" ++ ++#define SYS_CTL_REG SYS_CTRL_REG_BASE ++ ++#define OK 0 ++#define ERROR (-1) ++#define CH0_DMC_CFG_DDRMODE 0x11148050 ++#define CH0_DMC_CFG_RNKVOL_0 0x11148060 ++#define CH0_DMC_CFG_RNKVOL_1 0x11148064 ++ ++#define CH1_DMC_CFG_DDRMODE 0x11149050 ++#define CH1_DMC_CFG_RNKVOL_0 0x11149060 ++#define CH1_DMC_CFG_RNKVOL_1 0x11149064 ++ ++#define CH2_DMC_CFG_DDRMODE 0x1114a050 ++#define CH2_DMC_CFG_RNKVOL_0 0x1114a060 ++#define CH2_DMC_CFG_RNKVOL_1 0x1114a064 ++ ++#define CH3_DMC_CFG_DDRMODE 0x1114b050 ++#define CH3_DMC_CFG_RNKVOL_0 0x1114b060 ++#define CH3_DMC_CFG_RNKVOL_1 0x1114b064 ++ ++#define CH0_QOS_CFG_DDRMODE 0x11144630 ++#define CH0_QOS_CFG_RNKVOL_0 0x11144634 ++#define CH0_QOS_CFG_RNKVOL_1 0x11144638 ++ ++#define CH1_QOS_CFG_DDRMODE 0x11144640 ++#define CH1_QOS_CFG_RNKVOL_0 0x11144644 ++#define CH1_QOS_CFG_RNKVOL_1 0x11144648 ++ ++#define CH2_QOS_CFG_DDRMODE 0x11144650 ++#define CH2_QOS_CFG_RNKVOL_0 0x11144654 ++#define CH2_QOS_CFG_RNKVOL_1 0x11144658 ++ ++#define CH3_QOS_CFG_DDRMODE 0x11144660 ++#define CH3_QOS_CFG_RNKVOL_0 0x11144664 ++#define CH3_QOS_CFG_RNKVOL_1 0x11144668 ++ ++static inline void delay(unsigned int num) ++{ ++ volatile unsigned int i; ++ ++ for (i = 0; i < (100 * num); i++) /* 100: Cycle */ ++ __asm__ __volatile__("nop"); ++} ++ ++static inline void dwb(void) /* drain write buffer */ ++{ ++} ++ ++static inline unsigned int readl(unsigned addr) ++{ ++ unsigned int val; ++ ++ val = (*(volatile unsigned int *)(long)(addr)); ++ return val; ++} ++ ++static inline void writel(unsigned val, unsigned addr) ++{ ++ dwb(); ++ (*(volatile unsigned *)(long)(addr)) = (val); ++ dwb(); ++} ++ ++#define REG_BASE_MISC 0x11020000 ++#define DDRCA_REE_RANDOM_L 0x4040 ++#define DDRCA_REE_RANDOM_H 0x4044 ++#define DDRCA_TEE_RANDOM_L 0x4048 ++#define DDRCA_TEE_RANDOM_H 0x404C ++#define DDRCA_EN 0x4050 ++#define DDRCA_REE_UPDATE 0x4054 ++#define DDRCA_TEE_UPDATE 0x4058 ++#define DDR_CA_LOCK 0x405c ++#define RANDOM_SIZE 4 ++ ++#define REG_TSENSOR_CTRL 0x1102e000 ++#define TSENSOR_CTRL0 0x0 ++#define TSENSOR_CTRL0_CFG 0xc0300000 ++#define TSENSOR_CTRL1 0x4 ++#define TSENSOR_CTRL1_CFG 0x8 ++ ++#define SYSCTRL_REG 0x11020000 ++#define HPM_CORE_VOL_REG (SYSCTRL_REG + 0x9004) ++#define HPM_MDA_VOL_REG (SYSCTRL_REG + 0x9104) ++#define HPM_NPU_VOL_REG (SYSCTRL_REG + 0x9204) ++#define HPM_MONITOR_CFG 0x60200001 ++#define HPM_CORE_OFFSET 0xb030 ++#define HPM_MDA_OFFSET 0xb020 ++#define HPM_NPU_OFFSET 0xb010 ++#define CYCLE_NUM 8 ++#define HPM_NPU_REG0 0xb018 ++#define HPM_NPU_REG1 0xb01c ++#define HPM_MDA_REG0 0xb028 ++#define HPM_MDA_REG1 0xb02c ++#define HPM_CORE_REG0 0xb038 ++#define HPM_CORE_REG1 0xb03c ++ ++#define HPM_CLK_REG 0x11014a80 ++#define HPM_CLK_CFG 0x30 ++ ++#define CPU_ISO_REG 0x1d821104 ++ ++#define TSENSOR_CTRL 0x70 ++#define TSENSOR_STATUS0 0x8 ++ ++#define OTP_CPU_IF_REG 0x10120000 ++#define OTP_HPM_CORE_OFFSET 0x1504 ++#define OTP_HPM_MDA_OFFSET 0x1534 ++#define OTP_HPM_NPU_OFFSET 0x1530 ++#define OTP_SHPM_MDA_OFFSET 0x153c ++ ++#define SYSCTRL_BASE_REG 0x11020000 ++#define HPM_CORE_STORAGE_REG 0x340 ++#define HPM_MDA_STORAGE_REG 0x344 ++#define HPM_NPU_STORAGE_REG 0x348 ++#define TEMPERATURE_STORAGE_REG 0x34c ++#define DELTA_V_STORAGE_REG 0x350 ++#define CORE_DUTY_STORAGE_REG 0x354 ++#define MDA_DUTY_STORAGE_REG 0x358 ++#define NPU_DUTY_STORAGE_REG 0x35c ++ ++/* physical max/min */ ++#define CORE_VOLT_MAX 920 ++#define CORE_VOLT_MIN 599 ++ ++#define MDA_VOLT_MAX 1049 ++#define MDA_VOLT_MIN 600 ++ ++#define NPU_VOLT_MAX 1049 ++#define NPU_VOLT_MIN 600 ++ ++/* curve max/min; voltage curve: v = (b - a * hpm) / 10 */ ++#define CORE_CURVE_VLOT_MAX 850 ++#define CORE_CURVE_VLOT_MIN 760 ++#define CORE_CURVE_B 16200 ++#define CORE_CURVE_A 20 ++ ++#define MEDIA_CURVE_VLOT_MAX 770 ++#define MEDIA_CURVE_VLOT_MIN 690 ++#define MEDIA_CURVE_B 11150 ++#define MEDIA_CURVE_A 10 ++ ++ ++#define NPU_CURVE_VLOT_MAX 810 ++#define NPU_CURVE_VLOT_MIN 730 ++#define NPU_CURVE_B 127500 ++#define NPU_CURVE_A 125 ++ ++#define SVB_VER_REG 0x11020168 ++#define SVB_VER 0x30303030 ++#define temperature_formula(val) (((((val) - 116) * 165) / 806) - 40) ++#define duty_formula(max, min, val) ((((max) - (val)) * 416 + ((max) - (min) + 1) / 2) / ((max) - (min)) - 1) ++#define volt_regval_formula(val) (((val) << 16) + ((416 - 1) << 4) + 0x5) ++ ++static unsigned hpm_value_avg(unsigned int *val, unsigned int num) ++{ ++ unsigned int i; ++ unsigned tmp = 0; ++ ++ for (i = 0; i < num; i++) /* 4: Cycle */ ++ tmp += val[i]; ++ ++ return (tmp / CYCLE_NUM) >> 2; ++} ++ ++static void get_hpm_value(unsigned int *hpm_core, unsigned int *hpm_npu, ++ unsigned int *hpm_mda) ++{ ++ int i; ++ unsigned int temp; ++ unsigned int core_value[4] = {0, 0, 0, 0}; ++ unsigned int mda_value[4] = {0, 0, 0, 0}; ++ unsigned int npu_value[4] = {0, 0, 0, 0}; ++ ++ for (i = 0; i < CYCLE_NUM; i++) { ++ /* (at least 16us*4) */ ++ delay(5); /* delay 5 s */ ++ /* npu */ ++ temp = readl(SYSCTRL_REG + HPM_NPU_REG0); ++ npu_value[1] += (temp >> 16) & 0x3ff; ++ npu_value[0] += temp & 0x3ff; ++ temp = readl(SYSCTRL_REG + HPM_NPU_REG1); ++ npu_value[3] += (temp >> 16) & 0x3ff; ++ npu_value[2] += temp & 0x3ff; ++ ++ /* mda */ ++ temp = readl(SYSCTRL_REG + HPM_MDA_REG0); ++ mda_value[1] += (temp >> 16) & 0x3ff; ++ mda_value[0] += temp & 0x3ff; ++ temp = readl(SYSCTRL_REG + HPM_MDA_REG1); ++ mda_value[3] += (temp >> 16) & 0x3ff; ++ mda_value[2] += temp & 0x3ff; ++ ++ /* core */ ++ temp = readl(SYSCTRL_REG + HPM_CORE_REG0); ++ core_value[1] += (temp >> 16) & 0x3ff; ++ core_value[0] += temp & 0x3ff; ++ temp = readl(SYSCTRL_REG + HPM_CORE_REG1); ++ core_value[3] += (temp >> 16) & 0x3ff; ++ core_value[2] += temp & 0x3ff; ++ } ++ ++ *hpm_core = hpm_value_avg(core_value, 4); /* 4 : Array size */ ++ *hpm_mda = hpm_value_avg(mda_value, 4); /* 4 : Array size */ ++ *hpm_npu = hpm_value_avg(npu_value, 4); /* 4 : Array size */ ++} ++ ++static void start_hpm(unsigned int *hpm_core, unsigned int *hpm_npu, ++ unsigned int *hpm_mda) ++{ ++ get_hpm_value(hpm_core, hpm_npu, hpm_mda); ++} ++ ++static void adjust_hpm(unsigned int *hpm_core, unsigned int *hpm_mda, ++ unsigned int *hpm_npu, int temperature) ++{ ++ unsigned int otp_hpm_core = readl(SYSCTRL_BASE_REG + OTP_HPM_CORE_OFFSET); ++ unsigned int otp_hpm_npu = readl(SYSCTRL_BASE_REG + OTP_HPM_NPU_OFFSET); ++ unsigned int otp_hpm_mda = readl(SYSCTRL_BASE_REG + OTP_HPM_MDA_OFFSET); ++ ++ if (temperature <= 0) { ++ *hpm_npu -= 2; /* 2 : Adjustment Value */ ++ *hpm_mda -= 3; /* 3 : Adjustment Value */ ++ *hpm_core -= 4; /* 4 : Adjustment Value */ ++ } else if (temperature > 100) { /* 100: temperature value */ ++ *hpm_npu += 5; /* 5 : Adjustment Value */ ++ *hpm_mda += 5; /* 5 : Adjustment Value */ ++ *hpm_core += 8; /* 8 : Adjustment Value */ ++ }else if(temperature > 70) { /* 70: temperature value */ ++ *hpm_npu += 3; /* 2 : Adjustment Value */ ++ *hpm_mda += 3; /* 2 : Adjustment Value */ ++ *hpm_core += 4; /* 3 : Adjustment Value */ ++ } ++ ++ ++ if (otp_hpm_core) { ++ if (*hpm_core > (otp_hpm_core + 59)) /* 10: Increment */ ++ *hpm_core = otp_hpm_core + 59; /* 5: Increment */ ++ } ++ ++ if (otp_hpm_npu) { ++ if (*hpm_npu > (otp_hpm_npu + 62)) /* 15: Increment */ ++ *hpm_npu = otp_hpm_npu + 62; ++ } ++ ++ if (otp_hpm_mda) { ++ if (*hpm_mda > (otp_hpm_mda + 61)) /* 15: Increment */ ++ *hpm_mda = otp_hpm_mda + 61; ++ } ++} ++ ++static void save_hpm(unsigned int hpm_core, unsigned int hpm_npu, ++ unsigned int hpm_mda) ++{ ++ writel(hpm_mda, SYSCTRL_BASE_REG + HPM_MDA_STORAGE_REG); ++ writel(hpm_npu, SYSCTRL_BASE_REG + HPM_NPU_STORAGE_REG); ++ writel(hpm_core, SYSCTRL_BASE_REG + HPM_CORE_STORAGE_REG); ++} ++ ++static unsigned int calc_volt_regval(unsigned int volt_val, unsigned int volt_max, ++ unsigned int volt_min) ++{ ++ unsigned int duty; ++ ++ if (volt_val >= volt_max) ++ volt_val = volt_max - 1; ++ if (volt_val <= volt_min) ++ volt_val = volt_min + 1; ++ duty = duty_formula(volt_max, volt_min, volt_val); ++ ++ return duty; ++} ++ ++static void set_hpm_core_volt(unsigned int hpm_core_value, int delta_v) ++{ ++ unsigned int volt_val; ++ unsigned int reg_val; ++ ++ volt_val = (CORE_CURVE_B - CORE_CURVE_A * hpm_core_value) / 10; ++ ++ if (volt_val > CORE_CURVE_VLOT_MAX) ++ volt_val = CORE_CURVE_VLOT_MAX; ++ else if (volt_val < CORE_CURVE_VLOT_MIN) ++ volt_val = CORE_CURVE_VLOT_MIN; ++ ++ volt_val += delta_v; ++ reg_val = calc_volt_regval(volt_val, CORE_VOLT_MAX, CORE_VOLT_MIN); ++ writel(reg_val, SYSCTRL_BASE_REG + CORE_DUTY_STORAGE_REG); ++ writel(reg_val, HPM_CORE_VOL_REG); ++} ++ ++static void set_hpm_mda_volt(unsigned int hpm_mda_value, int delta_v) ++{ ++ unsigned int volt_val; ++ unsigned int reg_val; ++ ++ volt_val = (MEDIA_CURVE_B - MEDIA_CURVE_A * hpm_mda_value) / 10; ++ if (volt_val > MEDIA_CURVE_VLOT_MAX) ++ volt_val = MEDIA_CURVE_VLOT_MAX; ++ else if (volt_val < MEDIA_CURVE_VLOT_MIN) ++ volt_val = MEDIA_CURVE_VLOT_MIN; ++ ++ volt_val += delta_v; ++ reg_val = calc_volt_regval(volt_val, MDA_VOLT_MAX, MDA_VOLT_MIN); ++ writel(reg_val, SYSCTRL_BASE_REG + MDA_DUTY_STORAGE_REG); ++ ++ writel(reg_val, HPM_MDA_VOL_REG); ++} ++ ++static void set_hpm_npu_volt(unsigned int hpm_npu_value, int delta_v) ++{ ++ unsigned int volt_val; ++ unsigned int reg_val; ++ ++ volt_val = (NPU_CURVE_B - NPU_CURVE_A * hpm_npu_value) / 100; ++ if (volt_val > NPU_CURVE_VLOT_MAX) ++ volt_val = NPU_CURVE_VLOT_MAX; ++ else if (volt_val < NPU_CURVE_VLOT_MIN) ++ volt_val = NPU_CURVE_VLOT_MIN; ++ ++ volt_val += delta_v; ++ reg_val = calc_volt_regval(volt_val, NPU_VOLT_MAX, NPU_VOLT_MIN); ++ writel(reg_val, SYSCTRL_BASE_REG + NPU_DUTY_STORAGE_REG); ++ ++ writel(reg_val, HPM_NPU_VOL_REG); ++} ++ ++static void get_delta_v(int *core_delta_v, int *npu_delta_v, int *mda_delta_v) ++{ ++ unsigned int value = readl(SYSCTRL_REG + OTP_SHPM_MDA_OFFSET); ++ writel(value, SYSCTRL_BASE_REG + DELTA_V_STORAGE_REG); ++ /* core:bit 11-8, ++ * bit11 equal to 1 means negative, equal to 0 means positive, ++ * bit 8-10 is the absolute delta_v ++ */ ++ int flag = (value & 0x800) ? -1 : 1; ++ *core_delta_v = flag * (int)((value >> 8) & 0x7) * 10; ++ ++ /* mda:bit 7-4, ++ * bit7 equal to 1 means negative, equal to 0 means positive, ++ * bit 4-6 is the absolute delta_v ++ */ ++ flag = (value & 0x80) ? -1 : 1; ++ *mda_delta_v = flag * (int)((value >> 4) & 0x7) * 10; ++ ++ /* npu:bit 3-0, ++ * bit3 equal to 1 means negative, equal to 0 means positive, ++ * bit 0-2 is the absolute delta_v ++ */ ++ flag = (value & 0x8) ? -1 : 1; ++ *npu_delta_v = flag * (int)(value & 0x7) * 10; ++} ++ ++ ++static void set_volt(unsigned int hpm_core, unsigned int hpm_npu, ++ unsigned int hpm_mda) ++{ ++ int core_delta_v = 0; ++ int npu_delta_v = 0; ++ int mda_delta_v = 0; ++ get_delta_v(&core_delta_v, &npu_delta_v, &mda_delta_v); ++ ++ set_hpm_core_volt(hpm_core, core_delta_v); ++ set_hpm_mda_volt(hpm_mda, mda_delta_v); ++ set_hpm_npu_volt(hpm_npu, npu_delta_v); ++ ++ /* delay 300 Cycle */ ++ delay(300); ++} ++ ++static void get_temperature(int *temperature) ++{ ++ int value = 0; ++ int tsensor_chn; ++ float m = 0.5; ++ float temperature_temp[3] = {0}; ++ for(tsensor_chn = 0; tsensor_chn < 3; tsensor_chn++) { ++ value = (int)(readl(REG_TSENSOR_CTRL + TSENSOR_STATUS0 + ++ 0x100 * tsensor_chn) & 0x3ff); ++ ++ m = ((float)value - 146) / 718 * 165 - 40; ++ ++ if(m > 0) ++ temperature_temp[tsensor_chn] = m + 0.5; ++ else ++ temperature_temp[tsensor_chn] = m - 0.5; ++ } ++ ++ *temperature = (temperature_temp[0]+temperature_temp[1]+temperature_temp[2])/3; ++ writel(*temperature, SYSCTRL_BASE_REG + TEMPERATURE_STORAGE_REG); ++} ++ ++void init_temperature(void) ++{ ++ int tsensor_chn; ++ ++ for(tsensor_chn = 0; tsensor_chn < 3; tsensor_chn++) { ++ writel(TSENSOR_CTRL0_CFG, REG_TSENSOR_CTRL + TSENSOR_CTRL0 + ++ 0x100 * tsensor_chn); ++ writel(TSENSOR_CTRL1_CFG, REG_TSENSOR_CTRL + TSENSOR_CTRL1 + ++ 0x100 * tsensor_chn); ++ } ++ ++} ++ ++void init_hpm(void) ++{ ++ /* open hmp clock */ ++ writel(HPM_CLK_CFG, HPM_CLK_REG); ++ ++ /* npu */ ++ writel(HPM_MONITOR_CFG, SYSCTRL_REG + HPM_NPU_OFFSET); ++ /* mda */ ++ writel(HPM_MONITOR_CFG, SYSCTRL_REG + HPM_MDA_OFFSET); ++ /* core */ ++ writel(HPM_MONITOR_CFG, SYSCTRL_REG + HPM_CORE_OFFSET); ++ ++} ++static void start_svb(void) ++{ ++ unsigned int hpm_core; ++ unsigned int hpm_npu; ++ unsigned int hpm_mda; ++ int temperature; ++ ++ /* init temperature and hpm*/ ++ init_temperature(); ++ init_hpm(); ++ delay(1000); ++ start_hpm(&hpm_core, &hpm_npu, &hpm_mda); ++ ++ /*get temperature */ ++ get_temperature(&temperature); ++ ++ adjust_hpm(&hpm_core, &hpm_mda, &hpm_npu, temperature); ++ set_volt(hpm_core, hpm_npu, hpm_mda); ++ save_hpm(hpm_core, hpm_npu, hpm_mda); ++ ++ /* add SVB VER*/ ++ writel(SVB_VER, SVB_VER_REG); ++} ++ ++static void set_qosbuf_cfg(void) ++{ ++ unsigned int val; ++ ++ val = readl(CH0_DMC_CFG_DDRMODE); ++ writel(val, CH0_QOS_CFG_DDRMODE); ++ val = readl(CH0_DMC_CFG_RNKVOL_0); ++ writel(val, CH0_QOS_CFG_RNKVOL_0); ++ val = readl(CH0_DMC_CFG_RNKVOL_1); ++ writel(val, CH0_QOS_CFG_RNKVOL_1); ++ ++ val = readl(CH1_DMC_CFG_DDRMODE); ++ writel(val, CH1_QOS_CFG_DDRMODE); ++ val = readl(CH1_DMC_CFG_RNKVOL_0); ++ writel(val, CH1_QOS_CFG_RNKVOL_0); ++ val = readl(CH1_DMC_CFG_RNKVOL_1); ++ writel(val, CH1_QOS_CFG_RNKVOL_1); ++ ++ val = readl(CH2_DMC_CFG_DDRMODE); ++ writel(val, CH2_QOS_CFG_DDRMODE); ++ val = readl(CH2_DMC_CFG_RNKVOL_0); ++ writel(val, CH2_QOS_CFG_RNKVOL_0); ++ val = readl(CH2_DMC_CFG_RNKVOL_1); ++ writel(val, CH2_QOS_CFG_RNKVOL_1); ++ ++ val = readl(CH3_DMC_CFG_DDRMODE); ++ writel(val, CH3_QOS_CFG_DDRMODE); ++ val = readl(CH3_DMC_CFG_RNKVOL_0); ++ writel(val, CH3_QOS_CFG_RNKVOL_0); ++ val = readl(CH3_DMC_CFG_RNKVOL_1); ++ writel(val, CH3_QOS_CFG_RNKVOL_1); ++} ++ ++void start_ddr_training(unsigned int base) ++{ ++ start_svb(); ++ ++ set_qosbuf_cfg(); ++ ++ ddr_set_rdqbdl_def_val(); ++ ++ /* ddr hw training */ ++ ddr_hw_training_init(); ++ ++ /* ddr sw training */ ++ ddr_sw_training_if(); ++ ++ ddr_retrain_anti_aging_enable(); ++ ++ /* ddr DMC auto power down config */ ++ ddr_dmc_auto_power_down_cfg(); ++} +diff --git a/arch/arm/cpu/armv8/ss927v100/reset.S b/arch/arm/cpu/armv8/ss927v100/reset.S +new file mode 100644 +index 0000000..ad86198 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/reset.S +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++ ++.global reset_cpu ++reset_cpu: ++ ldr x1, rstctl /* get addr for global reset */ ++ /* reg */ ++ mov w3, #0x2 /* full reset pll + mpu */ ++ str w3, [x1] /* force reset */ ++ mov x0, x0 ++ ++_loop_forever: ++ b _loop_forever ++.align 3 ++rstctl: ++ .quad SYS_CTRL_REG_BASE + REG_SC_SYSRES +diff --git a/arch/arm/cpu/armv8/ss927v100/sdhci_boot.c b/arch/arm/cpu/armv8/ss927v100/sdhci_boot.c +new file mode 100644 +index 0000000..c0036d6 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/sdhci_boot.c +@@ -0,0 +1,391 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++ ++#define DELAY_US 1000 ++ ++#define MMC_BLK_SZ 512 ++#define CP_STEP1_SIZE 0x7800 ++ ++/* ++ * Controller registers ++ */ ++#define SDHCI_DMA_ADDRESS 0x00 ++#define SDHCI_BLOCK_SIZE 0x04 ++#define SDHCI_BLOCK_COUNT 0x06 ++#define SDHCI_ARGUMENT 0x08 ++ ++#define SDHCI_TRANSFER_MODE 0x0C ++#define SDHCI_TRNS_DMA 0x0001 ++#define SDHCI_TRNS_BLK_CNT_EN 0x0002 ++#define SDHCI_TRNS_READ 0x0010 ++#define SDHCI_TRNS_MULTI 0x0020 ++ ++#define SDHCI_COMMAND 0x0E ++#define SDHCI_CMD_RESP_NONE 0x0000 ++#define SDHCI_CMD_RESP_LONG 0x0001 ++#define SDHCI_CMD_RESP_SHORT 0x0002 ++#define SDHCI_CMD_CRC 0x0008 ++#define SDHCI_CMD_DATA 0x0020 ++ ++#define SDHCI_RESPONSE 0x10 ++#define SDHCI_BUFFER 0x20 ++ ++#define SDHCI_PRESENT_STATE 0x24 ++#define SDHCI_PSTATE_DAT_0 0x00100000 ++ ++#define SDHCI_HOST_CONTROL 0x28 ++#define SDHCI_CTRL_4BITBUS 0x02 ++#define SDHCI_CTRL_8BITBUS 0x20 ++ ++#define SDHCI_POWER_CONTROL 0x29 ++#define SDHCI_POWER_ON 0x01 ++#define SDHCI_POWER_180 0x0A ++#define SDHCI_POWER_300 0x0C ++#define SDHCI_POWER_330 0x0E ++ ++#define SDHCI_CLOCK_CONTROL 0x2C ++#define SDHCI_CLOCK_INT_EN 0x0001 ++#define SDHCI_CLOCK_INT_STABLE 0x0002 ++#define SDHCI_CLOCK_CARD_EN 0x0004 ++ ++#define SDHCI_TIMEOUT_CONTROL 0x2E ++ ++#define SDHCI_SOFTWARE_RESET 0x2F ++#define SDHCI_RESET_ALL 0x01 ++#define SDHCI_RESET_CMD 0x02 ++#define SDHCI_RESET_DATA 0x04 ++ ++#define SDHCI_INT_STATUS 0x30 ++#define SDHCI_INT_RESPONSE 0x00000001 ++#define SDHCI_INT_DATA_END 0x00000002 ++#define SDHCI_INT_DMA 0x00000008 ++#define SDHCI_INT_DATA_AVAIL 0x00000020 ++#define SDHCI_INT_NORMAL_MASK 0x00007FFF ++#define SDHCI_INT_ERROR_MASK 0xFFFF8000 ++ ++#define SDHCI_INT_ENABLE 0x34 ++ ++#define SDHCI_EMMC_CTRL 0x52C ++#define SDHCI_CARD_IS_EMMC 0x0001 ++ ++#define SDHCI_BOOT_CTRL 0x52E ++#define MAN_BOOT_EN 0x0001 ++#define VALIDATE_BOOT 0x0080 ++ ++#define SDHCI_EMMC_HW_RESET 0x534 ++ ++#define sdhci_make_cmd(idx, param) ((((idx) & 0xFF) << 8) | ((param) & 0xFF)) ++#define sdhci_make_blksz(dma, blksz) ((((dma) & 0x7) << 12) | ((blksz) & 0xFFF)) ++ ++#define MMC_CMD_GO_IDLE_STATE 0 ++#define MMC_CMD_SEND_OP_COND 1 ++#define MMC_CMD_ALL_SEND_CID 2 ++#define MMC_CMD_SET_RELATIVE_ADDR 3 ++#define MMC_CMD_SWITCH 6 ++#define MMC_CMD_SELECT_CARD 7 ++#define MMC_CMD_SEND_CSD 9 ++#define MMC_CMD_STOP_TRANSMISSION 12 ++#define MMC_CMD_SET_BLOCKLEN 16 ++#define MMC_CMD_READ_SINGLE_BLOCK 17 ++#define MMC_CMD_READ_MULTIPLE_BLOCK 18 ++#define MMC_CMD_SET_BLOCK_COUNT 23 ++ ++#define MMC_SWITCH_MODE_WRITE_BYTE 0x3 ++#define MMC_SWITCH_ACCESS_SHIFT 24 ++#define MMC_SWITCH_INDEX_SHIFT 16 ++#define MMC_SWITCH_VALUE_SHIFT 8 ++ ++/* SDMA Configuration */ ++#define BOUNDARY_SIZE (512 * 1024) /* 512K */ ++#define BOUNDARY_ARG (0x7 << 12) /* 512K */ ++#define MMC_SDMA_ENABLE 1 ++ ++#define OCR_BUSY 0x80000000 ++#define OCR_HCS 0x40000000 ++ ++#define debug_printf(fmt, args...); ++#define reg_get(addr) (*(volatile unsigned int *)((uintptr_t)(addr))) ++ ++static unsigned int is_bootmode(void) ++{ ++ return !(reg_get(REG_BASE_SCTL + REG_PERI_EMMC_STAT) & EMMC_NORMAL_MODE); ++} ++ ++static unsigned int get_hcs(void) ++{ ++ return reg_get(REG_SAVE_HCS) & OCR_HCS; ++} ++ ++static inline void delay(unsigned int cnt) ++{ ++ while (cnt--) ++ __asm__ __volatile__("nop"); ++} ++ ++static inline unsigned int sdhci_readb(unsigned addr) ++{ ++ return *((volatile unsigned char *) (uintptr_t)(EMMC_BASE_REG + addr)); ++} ++ ++static inline void sdhci_writeb(unsigned val, unsigned addr) ++{ ++ (*(volatile unsigned char *) (uintptr_t)(EMMC_BASE_REG + addr)) = (val); ++} ++ ++static inline unsigned int sdhci_readw(unsigned addr) ++{ ++ return *((volatile unsigned short *) (uintptr_t)(EMMC_BASE_REG + addr)); ++} ++ ++static inline void sdhci_writew(unsigned val, unsigned addr) ++{ ++ (*(volatile unsigned short *) (uintptr_t)(EMMC_BASE_REG + addr)) = (val); ++} ++ ++static inline unsigned int sdhci_readl(unsigned addr) ++{ ++ return *((volatile unsigned int *) (uintptr_t)(EMMC_BASE_REG + addr)); ++} ++ ++static inline void sdhci_writel(unsigned val, unsigned addr) ++{ ++ (*(volatile unsigned int *) (uintptr_t)(EMMC_BASE_REG + addr)) = (val); ++} ++ ++static int sdhci_read_block_pio(void *data_addr, unsigned int block) ++{ ++ const unsigned int offset = sizeof(unsigned int); ++ unsigned int size; ++ unsigned char *buf; ++ ++ size = MMC_BLK_SZ; ++ buf = (unsigned char *)data_addr + MMC_BLK_SZ * block; ++ while (size) { ++ *(unsigned int *)buf = sdhci_readl(SDHCI_BUFFER); ++ buf += offset; ++ size -= offset; ++ } ++ ++ return 0; ++} ++ ++static int sdhci_check_int_status(unsigned int mask, unsigned int timeout) ++{ ++ unsigned int reg; ++ ++ timeout *= 1000; /* ms is converted to us multiplied by 1000 */ ++ for (;;) { ++ reg = sdhci_readl(SDHCI_INT_STATUS); ++ if (reg & mask) ++ break; ++ if (!(--timeout)) { ++ debug_printf("wait int status time out, reg = 0x%x, mask = 0x%x\n", ++ reg, mask); ++ return -1; ++ } ++ if (reg & SDHCI_INT_ERROR_MASK) { ++ debug_printf("int err: reg = 0x%x\n", reg); ++ return -1; ++ } ++ ++ delay(DELAY_US); ++ } ++ ++ return 0; ++} ++ ++static int sdhci_boot_read(void *data_addr, unsigned int read_block) ++{ ++ const unsigned int timeout = 2000; /* 2s timeout: 2000000 * 1us */ ++ int ret; ++ unsigned int blocks = 0; ++ ++ while (1) { ++ ret = sdhci_check_int_status(SDHCI_INT_DATA_AVAIL, timeout); ++ if (ret) { ++ debug_printf("wait data available int time out\n"); ++ return ret; ++ } ++ ++ sdhci_writel(SDHCI_INT_DATA_AVAIL, SDHCI_INT_STATUS); ++ sdhci_read_block_pio(data_addr, blocks); ++ ++ blocks++; ++ if (blocks == read_block) ++ break; ++ } ++ ++ return 0; ++} ++ ++static int mmc_send_cmd(unsigned int cmd, unsigned int arg) ++{ ++ const unsigned int timeout = 3000; /* 3s timeout: 3000000 * 1us */ ++ ++ sdhci_writel(0xFFFFFFFF, SDHCI_INT_STATUS); ++ sdhci_writel(arg, SDHCI_ARGUMENT); ++ sdhci_writew(cmd, SDHCI_COMMAND); ++ ++ if (sdhci_check_int_status(SDHCI_INT_RESPONSE, timeout)) { ++ debug_printf("send cmd error\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void mmc_read_cmd(unsigned long int start_addr, unsigned int src, ++ unsigned int size, unsigned int dma) ++{ ++ unsigned int cmd, hcs; ++ unsigned short mode; ++ ++ /* Send CMD16 to set blocksize */ ++ cmd = sdhci_make_cmd(MMC_CMD_SET_BLOCKLEN, SDHCI_CMD_CRC | ++ SDHCI_CMD_RESP_SHORT); ++ mmc_send_cmd(cmd, MMC_BLK_SZ); ++ ++ /* set data timeout */ ++ sdhci_writeb(0xE, SDHCI_TIMEOUT_CONTROL); ++ ++ /* set host count */ ++ sdhci_writew(size, SDHCI_BLOCK_COUNT); ++ ++ /* Send CMD23 to set blockcount */ ++ cmd = sdhci_make_cmd(MMC_CMD_SET_BLOCK_COUNT, ++ SDHCI_CMD_CRC | SDHCI_CMD_RESP_SHORT); ++ mmc_send_cmd(cmd, size); ++ ++ /* set transfer mode */ ++ mode = SDHCI_TRNS_READ | SDHCI_TRNS_MULTI | SDHCI_TRNS_BLK_CNT_EN; ++ if (dma) ++ mode |= SDHCI_TRNS_DMA; ++ ++ sdhci_writew(mode, SDHCI_TRANSFER_MODE); ++ ++ /* set SDMA address */ ++ if (dma) ++ sdhci_writel(start_addr, SDHCI_DMA_ADDRESS); ++ ++ /* Send CMD18 for multiple block read */ ++ hcs = get_hcs(); ++ if (hcs) { ++ cmd = sdhci_make_cmd(MMC_CMD_READ_MULTIPLE_BLOCK, ++ SDHCI_CMD_CRC | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_DATA); ++ mmc_send_cmd(cmd, src / MMC_BLK_SZ); ++ } else { ++ cmd = sdhci_make_cmd(MMC_CMD_READ_MULTIPLE_BLOCK, ++ SDHCI_CMD_CRC | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_DATA); ++ mmc_send_cmd(cmd, src); ++ } ++} ++ ++static int sdhci_normal_read_sdma(void *dst, unsigned int src, unsigned int size) ++{ ++ unsigned int stat; ++ unsigned int timeout; ++ unsigned long int start_addr; ++ ++ start_addr = (uintptr_t)dst; ++ ++ /* set host block size 512 */ ++ sdhci_writew(MMC_BLK_SZ | BOUNDARY_ARG, SDHCI_BLOCK_SIZE); ++ ++ mmc_read_cmd(start_addr, src, size, MMC_SDMA_ENABLE); ++ ++ sdhci_writel(SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); ++ ++ timeout = 300000; /* 3s timeout: 300000 * 10us */ ++ do { ++ stat = sdhci_readl(SDHCI_INT_STATUS); ++ if (stat & SDHCI_INT_ERROR_MASK) { ++ debug_printf("interrupt error\n"); ++ return -1; ++ } ++ ++ if (stat & SDHCI_INT_DMA) { ++ sdhci_writel(SDHCI_INT_DMA, SDHCI_INT_STATUS); ++ start_addr &= ~(BOUNDARY_SIZE - 1); ++ start_addr += BOUNDARY_SIZE; ++ sdhci_writel(start_addr, SDHCI_DMA_ADDRESS); ++ } ++ ++ if (timeout > 0) { ++ delay(10 * DELAY_US); /* delay 10us */ ++ timeout -= 1; ++ } else { ++ debug_printf("read timeout!\n"); ++ return -1; ++ } ++ } while (!(stat & SDHCI_INT_DATA_END)); ++ ++ sdhci_writel(SDHCI_INT_DATA_END, SDHCI_INT_STATUS); ++ ++ return 0; ++} ++ ++static void copy_step1_to_ddr(unsigned int *dst, unsigned int *src, unsigned int size) ++{ ++ unsigned int cycle = size / sizeof(unsigned int); ++ unsigned int i; ++ ++ for (i = 0; i < cycle; i++) ++ *dst++ = *src++; ++} ++ ++static int emmc_read_boot_data(void *data_addr, unsigned int data_size) ++{ ++ unsigned int read_block; ++ int bootmode; ++ ++ if (data_size <= CP_STEP1_SIZE) { ++ copy_step1_to_ddr((void *)data_addr, (void *)CP_STEP1_ADDR, data_size); ++ return 0; ++ } else { ++ copy_step1_to_ddr((void *)data_addr, (void *)CP_STEP1_ADDR, CP_STEP1_SIZE); ++ data_addr += CP_STEP1_SIZE; ++ data_size -= CP_STEP1_SIZE; ++ } ++ ++ if (data_size % MMC_BLK_SZ) { ++ debug_printf("sdhci_read_boot_data error\n"); ++ debug_printf("data_size:%d not round by block size\n", data_size); ++ read_block = data_size / MMC_BLK_SZ + 1; ++ } else { ++ read_block = data_size / MMC_BLK_SZ; ++ } ++ ++ bootmode = is_bootmode(); ++ if (bootmode) ++ return sdhci_boot_read(data_addr, read_block); ++ ++ return sdhci_normal_read_sdma(data_addr, CP_STEP1_SIZE, read_block); ++} ++ ++int emmc_boot_read(void *ptr, unsigned int size) ++{ ++ int ret; ++ ++ ret = emmc_read_boot_data(ptr, size); ++ return ret; ++} +diff --git a/arch/arm/cpu/armv8/ss927v100/start.S b/arch/arm/cpu/armv8/ss927v100/start.S +new file mode 100644 +index 0000000..b2cadac +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/start.S +@@ -0,0 +1,371 @@ ++/* ++ * (C) Copyright 2013 ++ * David Feng ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++/************************************************************************* ++ * ++ * Startup Code (reset vector) ++ * ++ *************************************************************************/ ++/* ++ * Branch according to exception level ++ */ ++.macro switch_el, xreg, el3_label, el2_label, el1_label ++ mrs \xreg, CurrentEL ++ cmp \xreg, 0xc ++ b.eq \el3_label ++ cmp \xreg, 0x8 ++ b.eq \el2_label ++ cmp \xreg, 0x4 ++ b.eq \el1_label ++.endm ++ ++/* ++ * Enter Exception. ++ * This will save the processor state that is ELR/X0~X30 ++ * to the stack frame. ++ */ ++.macro exception_entry ++ stp x29, x30, [sp, #-16]! ++ stp x27, x28, [sp, #-16]! ++ stp x25, x26, [sp, #-16]! ++ stp x23, x24, [sp, #-16]! ++ stp x21, x22, [sp, #-16]! ++ stp x19, x20, [sp, #-16]! ++ stp x17, x18, [sp, #-16]! ++ stp x15, x16, [sp, #-16]! ++ stp x13, x14, [sp, #-16]! ++ stp x11, x12, [sp, #-16]! ++ stp x9, x10, [sp, #-16]! ++ stp x7, x8, [sp, #-16]! ++ stp x5, x6, [sp, #-16]! ++ stp x3, x4, [sp, #-16]! ++ stp x1, x2, [sp, #-16]! ++ ++ /* Could be running at EL3/EL2/EL1 */ ++ switch_el x11, 3f, 2f, 1f ++3: mrs x1, esr_el3 ++ mrs x2, elr_el3 ++ b 0f ++2: mrs x1, esr_el2 ++ mrs x2, elr_el2 ++ b 0f ++1: mrs x1, esr_el1 ++ mrs x2, elr_el1 ++0: ++ stp x2, x0, [sp, #-16]! ++ mov x0, sp ++.endm ++ ++.globl _start ++_start: ++ b reset ++ ++.balignl 64,0xdeadbeef ++__blank_zone_start: ++.fill 11008,1,0 ++__blank_zone_end: ++ ++.globl _blank_zone_start ++_blank_zone_start: ++.quad __blank_zone_start ++ ++.globl _blank_zone_end ++_blank_zone_end: ++.quad __blank_zone_end ++.balignl 16,0xdeadbeef ++ ++.align 3 ++ ++.globl _TEXT_BASE ++_TEXT_BASE: ++ .quad TEXT_BASE ++ ++/* ++ * These are defined in the linker script. ++ */ ++.globl _end_ofs ++_end_ofs: ++ .quad _end - _start ++ ++.globl _bss_start_ofs ++_bss_start_ofs: ++ .quad __bss_start - _start ++ ++.globl _bss_end_ofs ++_bss_end_ofs: ++ .quad __bss_end - _start ++ ++reset: ++ /* ++ * Could be EL3/EL2/EL1, Initial State: ++ * Little Endian, MMU Disabled, i/dCache Disabled ++ */ ++ adr x0, vectors ++ switch_el x1, 3f, 2f, 1f ++3: msr vbar_el3, x0 ++ mrs x0, scr_el3 ++ orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */ ++ msr scr_el3, x0 ++ msr cptr_el3, xzr /* Enable FP/SIMD */ ++#ifdef COUNTER_FREQUENCY ++ ldr x0, =COUNTER_FREQUENCY ++ msr cntfrq_el0, x0 /* Initialize CNTFRQ */ ++#endif ++ b 0f ++2: msr vbar_el2, x0 ++ mov x0, #0x33ff ++ msr cptr_el2, x0 /* Enable FP/SIMD */ ++ b 0f ++1: msr vbar_el1, x0 ++ mov x0, #3 << 20 ++ msr cpacr_el1, x0 /* Enable FP/SIMD */ ++0: ++ ++ /* ++ * Cache/BPB/TLB Invalidate ++ * i-cache is invalidated before enabled in icache_enable() ++ * tlb is invalidated before mmu is enabled in dcache_enable() ++ * d-cache is invalidated before enabled in dcache_enable() ++ */ ++ ++#ifndef CONFIG_BSP_DISABLE_DOWNLOAD ++ /* ++ * read system register REG_SC_GEN2 ++ * check if ziju flag ++ */ ++ ldr x0, =SYS_CTRL_REG_BASE ++ ldr w1, [x0, #REG_SC_GEN2] ++ ldr w2, =0x7a696a75 /* magic for "ziju" */ ++ cmp w1, w2 ++ bne normal_start_flow ++ mov x1, sp /* save sp */ ++ str w1, [x0, #REG_SC_GEN2] /* clear ziju flag */ ++ ++ziju_flow: ++ ldr x2, =(STACK_TRAINING) ++ bic sp, x2, #0xf /* 16-byte alignment for ABI compliance */ ++ ldr x0, _blank_zone_start ++ ldr x1, _TEXT_BASE ++ sub x0, x0, x1 ++ adr x1, _start ++ add x0, x0, x1 ++ mov x1, #0x0 /* flags: 0->normal 1->pm */ ++ bl init_registers /* init PLL/DDRC/... */ ++ bl start_ddr_training /* DDR training */ ++ ++ ldr x0, =SYS_CTRL_REG_BASE ++ ldr w1, [x0, #REG_SC_GEN2] ++ mov sp, x1 /* restore sp */ ++ ldr w1, [x0, #REG_SC_GEN3] ++ mov x30, x1 ++ ret /* return to bootrom */ ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop ++ b . /* bug here */ ++ ++ ++normal_start_flow: ++#endif ++ /* set stack for C code */ ++ ldr x0, =(CONFIG_SYS_INIT_SP_ADDR) ++ bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */ ++ ++ bl uart_early_init ++ adr x0, Str_SystemSartup ++ bl uart_early_puts ++ ++running_addr_check: ++ adr x0,running_addr_check ++ lsr x0, x0, #28 ++ cmp x0, #4 ++ bge not_ddr_init ++ ++ /* read init table and config registers */ ++ ldr x0, _blank_zone_start ++ ldr x1, _TEXT_BASE ++ sub x0, x0, x1 ++ adr x1, _start ++ add x0, x0, x1 ++ mov x1, #0 /* flags: 0->normal 1->pm */ ++ bl init_registers ++ bl start_ddr_training ++check_boot_mode: ++ ldr x0, =SYS_CTRL_REG_BASE ++ ldr w0, [x0, #REG_SYSSTAT] ++ lsr w6, w0, #2 ++ and w6, w6, #0x3 ++ cmp w6, #BOOT_FROM_EMMC ++ bne not_ddr_init ++ ++#ifdef CONFIG_SDHCI ++emmc_boot: ++ ldr x0, _TEXT_BASE ++ ldr x1, =__image_copy_start /* x1 <- SRC &__image_copy_start */ ++ ldr x2, =__bss_start /* x2 <- SRC &__bss_start */ ++ sub x1, x2, x1 ++ bl emmc_boot_read ++ b no_copy ++#endif ++ ++not_ddr_init: ++master_cpu: ++relocate: ++ adr x0, _start /*runing addr start*/ ++ ldr x1, =__image_copy_start /* x1 <- SRC &__image_copy_start */ ++ cmp x0, x1 ++ b.eq no_copy /* skip relocation */ ++ ldr x2, =__bss_start /* x2 <- SRC &__bss_start*/ ++ ++copy_loop: ++ ldp x10, x11, [x0], #16 /* copy from source address [x0] */ ++ stp x10, x11, [x1], #16 /* copy to target address [x1] */ ++ cmp x1, x2 /* until source end address [x2] */ ++ b.lo copy_loop ++no_copy: ++clear_remap: ++ ldr x1, =SYS_CTRL_REG_BASE ++ ldr w0, [x1] ++ mov w2, #0x5 ++ bic w0, w0, #0xf ++ orr w0, w0, w2 ++ str w0, [x1] ++set_scs_finish: ++ ldr x1, =REG_SCS_CTRL ++ ldr w0, [x1] ++ mov w2, #0x5 ++ bic w0, w0, #0xf ++ orr w0, w0, w2 ++ str w0, [x1] ++jump_to_ddr: ++ adr x0, _start_armboot ++ ldr x30,[x0] ++ ret ++.align 3 ++_start_armboot: .quad start_armboot ++ ++/*-----------------------------------------------------------------------*/ ++ ++/* ++ * Exception vectors. ++ */ ++ .align 3 ++ .globl vectors ++vectors: ++ .align 3 ++ b _do_bad_sync /* Current EL Synchronous Thread */ ++ ++ .align 3 ++ b _do_bad_irq /* Current EL IRQ Thread */ ++ ++ .align 3 ++ b _do_bad_fiq /* Current EL FIQ Thread */ ++ ++ .align 3 ++ b _do_bad_error /* Current EL Error Thread */ ++ ++ .align 3 ++ b _do_sync /* Current EL Synchronous Handler */ ++ ++ .align 3 ++ b _do_irq /* Current EL IRQ Handler */ ++ ++ .align 3 ++ b _do_fiq /* Current EL FIQ Handler */ ++ ++ .align 3 ++ b _do_error /* Current EL Error Handler */ ++ ++ ++_do_bad_sync: ++ exception_entry ++ bl do_bad_sync ++ b exception_exit ++ ++_do_bad_irq: ++ exception_entry ++ bl do_bad_irq ++ b exception_exit ++ ++_do_bad_fiq: ++ exception_entry ++ bl do_bad_fiq ++ b exception_exit ++ ++_do_bad_error: ++ exception_entry ++ bl do_bad_error ++ b exception_exit ++ ++_do_sync: ++ exception_entry ++ bl do_sync ++ b exception_exit ++ ++_do_irq: ++ exception_entry ++ bl do_irq ++ b exception_exit ++ ++_do_fiq: ++ exception_entry ++ bl do_fiq ++ b exception_exit ++ ++_do_error: ++ exception_entry ++ bl do_error ++ b exception_exit ++ ++exception_exit: ++ ldp x2, x0, [sp],#16 ++ switch_el x11, 3f, 2f, 1f ++3: msr elr_el3, x2 ++ b 0f ++2: msr elr_el2, x2 ++ b 0f ++1: msr elr_el1, x2 ++0: ++ ldp x1, x2, [sp],#16 ++ ldp x3, x4, [sp],#16 ++ ldp x5, x6, [sp],#16 ++ ldp x7, x8, [sp],#16 ++ ldp x9, x10, [sp],#16 ++ ldp x11, x12, [sp],#16 ++ ldp x13, x14, [sp],#16 ++ ldp x15, x16, [sp],#16 ++ ldp x17, x18, [sp],#16 ++ ldp x19, x20, [sp],#16 ++ ldp x21, x22, [sp],#16 ++ ldp x23, x24, [sp],#16 ++ ldp x25, x26, [sp],#16 ++ ldp x27, x28, [sp],#16 ++ ldp x29, x30, [sp],#16 ++ eret ++ ++/* ++ * void __asm_invalidate_icache_all(void) ++ * ++ * invalidate all tlb entries. ++ */ ++ENTRY(__asm_invalidate_icache_all) ++ ic ialluis ++ isb sy ++ ret ++ENDPROC(__asm_invalidate_icache_all) ++ ++.align 3 ++Str_SystemSartup: ++.ascii "\r\n\r\nSystem startup\r\n\0" +diff --git a/arch/arm/cpu/armv8/ss927v100/uart.S b/arch/arm/cpu/armv8/ss927v100/uart.S +new file mode 100644 +index 0000000..e2d4b82 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss927v100/uart.S +@@ -0,0 +1,116 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++ ++#include ++ ++//****************************************************************************** ++// ++// void uart_early_init(void); ++// ++.text ++.align 4 ++.global uart_early_init ++.type uart_early_init, %function ++uart_early_init: ++#ifndef CONFIG_BSP_DISABLE_CONSOLE ++ ldr x4, io_base_addr_L0 ++ ldr w3, =0x1101 ++ str w3, [x4, #0X124] ++ ldr w3, =0x1011 ++ str w3, [x4, #0X128] ++ ++ ldr x4, uart_base_addr_L0 ++ mov w3, #0 ++ /* Disable UART */ ++ str w3, [x4, #48] ++ /* Set baud rate to 115200, uart clock:24M */ ++ add w3, w3, #13 ++ str w3, [x4, #36] ++ mov w3, #1 ++ str w3, [x4, #40] ++ /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled. */ ++ ldr w3, =112 ++ str w3, [x4, #44] ++ /* Enable UART */ ++ ldr w3, =769 ++ str w3, [x4, #48] ++#endif ++ ret ++.align 4 ++uart_base_addr_L0: ++ .quad CONFIG_CUR_UART_BASE ++io_base_addr_L0: ++ .quad 0x102f0000 ++ ++//****************************************************************************** ++// ++// void uart_early_puts(const char *ss); ++// ++.align 4 ++.global uart_early_puts ++.type uart_early_puts, %function ++uart_early_puts: ++#ifndef CONFIG_BSP_DISABLE_CONSOLE ++#if !defined(CONFIG_SUPPORT_CA_RELEASE) ++ ldr x2, uart_base_addr_L1 ++ b next_char ++output: ++ ldr w4, [x2, #24] ++ tst w4, #32 ++ bne output ++ str w3, [x2, #0] ++ add x0, x0, #1 ++next_char: ++ ldrb w3, [x0] ++ cmp w3, #0 ++ bne output ++#endif /* CONFIG_SUPPORT_CA_RELEASE */ ++#endif /* CONFIG_BSP_DISABLE_CONSOLE */ ++ ret ++.align 4 ++uart_base_addr_L1: ++ .quad CONFIG_CUR_UART_BASE ++ ++//****************************************************************************** ++// ++// void uart_early_putc(int chr); ++// ++// call example: ++// mov w0, #'A' ++// bl uart_early_putc ++// ++.align 4 ++.global uart_early_putc ++.type uart_early_putc, %function ++uart_early_putc: ++#ifndef CONFIG_BSP_DISABLE_CONSOLE ++#if !defined(CONFIG_SUPPORT_CA_RELEASE) ++ ldr x2, uart_base_addr_L3 ++wait3: ++ ldr w4, [x2, #24] ++ tst w4, #32 ++ bne wait3 ++ str w0, [x2, #0] ++ ++#endif /* CONFIG_SUPPORT_CA_RELEASE */ ++#endif /* CONFIG_BSP_DISABLE_CONSOLE */ ++ ret ++.align 4 ++uart_base_addr_L3: ++ .quad CONFIG_CUR_UART_BASE +diff --git a/arch/arm/cpu/armv8/ss928v100/hw_compressed/Makefile b/arch/arm/cpu/armv8/ss928v100/hw_compressed/Makefile +new file mode 100644 +index 0000000..9a57e6c +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/hw_compressed/Makefile +@@ -0,0 +1,154 @@ ++################################################################################ ++ ++PWD = $(shell pwd) ++CROSS_COMPILE =: aarch64-c01v01-linux-gnu- ++TOPDIR = ++ ++################################################################################ ++CC := $(CROSS_COMPILE)gcc ++AR := $(CROSS_COMPILE)ar ++LD := $(CROSS_COMPILE)ld ++OBJCOPY := $(CROSS_COMPILE)objcopy ++OBJDUMP := $(CROSS_COMPILE)objdump ++ ++ ++################################################################################ ++BOOT := u-boot-$(SOC) ++TEXTBASE := 0x48700000 ++CFLAGS :=-Os -fno-builtin -ffreestanding \ ++ -D__KERNEL__ -DTEXT_BASE=$(TEXTBASE) \ ++ -I$(TOPDIR)/include \ ++ -I$(TOPDIR)/include/linux \ ++ -I$(TOPDIR)/drivers/ddr/vendor/default_v2 \ ++ -I$(TOPDIR)/drivers/ddr/vendor/$(SOC) \ ++ -I$(TOPDIR)/arch/arm/include \ ++ -fno-pic -mstrict-align -ffunction-sections \ ++ -fdata-sections -fno-common -ffixed-r9 \ ++ -fno-common -ffixed-x18 -pipe -march=armv8-a \ ++ -Wall -Wstrict-prototypes -fno-stack-protector \ ++ -D__LINUX_ARM_ARCH__=8 -D__ARM__ \ ++ -DCONFIG_SS928V100 -DCONFIG_MMC -DDDR_TRAINING_MEM_FUNC \ ++ $(MKFLAGS) -fno-strict-aliasing -mcmodel=tiny ++ ++################################################################################ ++ ++START := start.o ++COBJS := lowlevel_init_v300.o \ ++ init_registers.o \ ++ sdhci_boot.o \ ++ uart.o \ ++ ddr_training_impl.o \ ++ ddr_ac_training.o \ ++ ddr_dcc_training.o \ ++ ddr_ddrt_training.o \ ++ ddr_gate_training.o \ ++ ddr_lpca_training.o \ ++ ddr_mpr_training.o \ ++ ddr_pcode_training.o \ ++ ddr_wl_training.o \ ++ ddr_training_ctl.o \ ++ ddr_training_boot.o \ ++ ddr_training_custom.o \ ++ ddr_training_console.o \ ++ startup.o \ ++ image_data.o \ ++ div0.o \ ++ reset.o ++ ++SSRC := arch/arm/cpu/armv8/$(SOC)/start.S \ ++ arch/arm/cpu/armv8/$(SOC)/reset.S \ ++ arch/arm/cpu/armv8/$(SOC)/sdhci_boot.c \ ++ arch/arm/cpu/armv8/$(SOC)/uart.S \ ++ arch/arm/cpu/armv8/$(SOC)/init_registers.c \ ++ arch/arm/cpu/armv8/$(SOC)/lowlevel_init_v300.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_impl.c \ ++ drivers/ddr/vendor/default_v2/ddr_ac_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_dcc_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_ddrt_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_gate_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_lpca_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_mpr_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_pcode_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_wl_training.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_ctl.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_boot.c \ ++ drivers/ddr/vendor/default_v2/ddr_training_console.c \ ++ drivers/ddr/vendor/$(SOC)/ddr_training_custom.c \ ++ arch/arm/lib/div0.c \ ++ lib/hw_dec/hw_decompress.c \ ++ lib/hw_dec/hw_decompress_$(SOC).c \ ++ lib/hw_dec/hw_decompress_v2.c \ ++ lib/hw_dec/hw_decompress_v2.h ++ ++REG := $(wildcard $(TOPDIR)/*.reg $(TOPDIR)/.reg) ++SRC := $(notdir $(SSRC)) ++ ++################################################################################ ++.PHONY: $(BOOT).bin ++$(BOOT).bin: $(BOOT).tmp regfile ++ @dd if=./$(BOOT).tmp of=./tmp1 bs=1 count=64 2>/dev/null ++ @dd if=$(REG) of=./tmp2 bs=11008 conv=sync 2>/dev/null ++ @dd if=./$(BOOT).tmp of=./tmp3 bs=1 skip=11072 2>/dev/null ++ @cat tmp1 tmp2 tmp3 > $(BOOT).bin ++ @rm -f tmp1 tmp2 tmp3 ++ @chmod 754 $(BOOT).bin ++ @cp -fv $@ $(TOPDIR) ++ @echo $(BOOT).bin is Ready. ++ ++$(BOOT).tmp: $(BOOT).elf ++ $(OBJCOPY) -O srec $< $(BOOT).srec ++ $(OBJCOPY) -j .text -O binary $< $(BOOT).text ++ $(OBJCOPY) --gap-fill=0xff -O binary $< $@ ++ ++$(BOOT).elf: image_data.gzip $(SRC) $(START) $(COBJS) ++ $(LD) -Bstatic -T u-boot.lds -Ttext $(TEXTBASE) $(START) \ ++ $(COBJS) -Map $(BOOT).map -o $@ ++ $(OBJDUMP) -d $@ > $@.asm ++ ++.PHONY: regfile ++regfile: ++ @if [ "$(words $(REG))" = "0" ]; then ( \ ++ echo '***' Need '.reg' or '*.reg' file in directory $(TOPDIR); \ ++ exit 1; \ ++ ) fi ++ @if [ "$(words $(REG))" != "1" ]; then ( \ ++ echo '***' Found multi '.reg' or '*.reg' file in directory $(TOPDIR); \ ++ echo '***' Files: $(notdir $(REG)); \ ++ exit 1; \ ++ ) fi ++ ++################################################################################ ++start.o: start.S ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ $< -c ++ ++# -1 : --fast -9 : --best ++image_data.gzip: $(BINIMAGE) ++ gzip -fNqc -7 $< > $@ ++ ++%.o: %.c ++ $(CC) $(CFLAGS) -Wall -Wstrict-prototypes \ ++ -fno-stack-protector -o $@ $< -c ++ ++%.o: %.S ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ $< -c ++ ++image_data.o: image_data.S image_data.gzip ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ $< -c ++ ++############################################################################# ++ ++$(SRC): ++ ln -sf ../../../../../../$(filter %/$@,$(SSRC)) $@ ++################################################################################ ++TMPS := $(COBJS) start.o $(SRC) \ ++ $(BOOT).map $(BOOT).elf $(BOOT).srec $(BOOT).bin $(BOOT).text $(BOOT).tmp \ ++ image_data.gzip ++ ++distclean: clean ++ ++clean: ++ -rm -f $(TMPS) ++ ++################################################################################ ++.PHONY: clean ++################################################################################ +diff --git a/arch/arm/cpu/armv8/ss928v100/hw_compressed/image_data.S b/arch/arm/cpu/armv8/ss928v100/hw_compressed/image_data.S +new file mode 100644 +index 0000000..f8d2d36 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/hw_compressed/image_data.S +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ .section .image,"a" ++ .globl input_data ++ /*gzip source addr must be 16 bytes aligned*/ ++ .balign 16 ++input_data: ++ .incbin "image_data.gzip" ++ .globl input_data_end ++input_data_end: +diff --git a/arch/arm/cpu/armv8/ss928v100/hw_compressed/startup.c b/arch/arm/cpu/armv8/ss928v100/hw_compressed/startup.c +new file mode 100644 +index 0000000..4d1785c +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/hw_compressed/startup.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++const uintptr_t image_entry = (CONFIG_SYS_TEXT_BASE); ++ ++#define error(_s) uart_early_puts(_s) ++#define putstr(_s) uart_early_puts(_s) ++#define EXCEPTION_LEVEL1 1 ++#define EXCEPTION_LEVEL2 2 ++#include "hw_decompress.c" ++#define GZIP_SIZE_OFFSET 0x4 ++ ++void icache_enable(void); ++void invalidate_icache_all(void); ++void __asm_invalidate_icache_all(void); ++ ++void start_armboot(void) ++{ ++ unsigned char *pdst_h32 = NULL; ++ unsigned char *pdst_l32 = NULL; ++ unsigned char *input_data_h32 = NULL; ++ unsigned int image_data_len; ++ int pdst_len; ++ int ret; ++ uart_early_init(); ++ uart_early_puts("\r\nUncompress "); ++ ++ /* enable I-Cache */ ++ icache_enable(); ++ ++ /* use direct address mode */ ++ hw_dec_type = 0; ++ /* init hw decompress IP */ ++ hw_dec_init(); ++ ++ /* start decompress */ ++ pdst_l32 = (unsigned char *)(uintptr_t)image_entry; ++ image_data_len = input_data_end - input_data; ++ for (int i = 0; i < sizeof(pdst_len); i++) ++ *((char *)(&pdst_len) + i) = *((char *)(input_data_end - ++ GZIP_SIZE_OFFSET) + i); ++ ++ ret = hw_dec_decompress(pdst_h32, pdst_l32, &pdst_len, input_data_h32, ++ input_data, image_data_len, NULL); ++ if (!ret) { ++ uart_early_puts("Ok!"); ++ } else { ++ uart_early_puts("Fail!"); ++ while (1); ++ } ++ ++ /* uinit hw decompress IP */ ++ hw_dec_uinit(); ++ void (*uboot)(void); ++ uboot = (void (*))CONFIG_SYS_TEXT_BASE; ++ invalidate_icache_all(); ++ uboot(); ++} ++ ++void hang(void) ++{ ++ uart_early_puts("### ERROR ### Please RESET the board ###\n"); ++ for (; ;); ++} ++ ++void invalidate_icache_all(void) ++{ ++ __asm_invalidate_icache_all(); ++} ++ ++static inline unsigned int current_el(void) ++{ ++ unsigned int el; ++ asm volatile("mrs %0, CurrentEL" : "=r"(el) : : "cc"); ++ return el >> 2; /* Move Left 2bit */ ++} ++ ++static void set_sctlr(unsigned int val) ++{ ++ unsigned int el; ++ ++ el = current_el(); ++ if (el == EXCEPTION_LEVEL1) ++ asm volatile("msr sctlr_el1, %0" : : "r"(val) : "cc"); ++ else if (el == EXCEPTION_LEVEL2) ++ asm volatile("msr sctlr_el2, %0" : : "r"(val) : "cc"); ++ else ++ asm volatile("msr sctlr_el3, %0" : : "r"(val) : "cc"); ++ ++ asm volatile("isb"); ++} ++ ++static unsigned int get_sctlr(void) ++{ ++ unsigned int el, val; ++ ++ el = current_el(); ++ if (el == EXCEPTION_LEVEL1) ++ asm volatile("mrs %0, sctlr_el1" : "=r"(val) : : "cc"); ++ else if (el == EXCEPTION_LEVEL2) ++ asm volatile("mrs %0, sctlr_el2" : "=r"(val) : : "cc"); ++ else ++ asm volatile("mrs %0, sctlr_el3" : "=r"(val) : : "cc"); ++ ++ return val; ++} ++ ++void icache_enable(void) ++{ ++ invalidate_icache_all(); ++#define CR_I (1 << 12) /* Icache enable */ ++ set_sctlr(get_sctlr() | CR_I); ++} ++ ++void do_bad_sync(void) ++{ ++ uart_early_puts("bad sync abort\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_sync(void) ++{ ++ uart_early_puts("sync abort\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_bad_error(void) ++{ ++ uart_early_puts("bad error\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_error(void) ++{ ++ uart_early_puts("error\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_bad_fiq(void) ++{ ++ uart_early_puts("bad fast interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_bad_irq(void) ++{ ++ uart_early_puts("bad interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_fiq(void) ++{ ++ uart_early_puts("fast interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} ++ ++void do_irq(void) ++{ ++ uart_early_puts("interrupt request\r\n"); ++ uart_early_puts("Resetting CPU ...\r\n"); ++ reset_cpu(0); ++} +diff --git a/arch/arm/cpu/armv8/ss928v100/hw_compressed/u-boot.lds b/arch/arm/cpu/armv8/ss928v100/hw_compressed/u-boot.lds +new file mode 100644 +index 0000000..e0f0fdc +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/hw_compressed/u-boot.lds +@@ -0,0 +1,83 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") ++OUTPUT_ARCH(aarch64) ++ENTRY(_start) ++SECTIONS ++{ ++ . = 0x48700000; ++ __image_copy_start =.; ++ . = ALIGN(8); ++ .text : ++ { ++ __text_start = .; ++ start.o (.text*) ++ init_registers.o (.text*) ++ lowlevel_init_v300.o (.text*) ++ ddr_training_impl.o (.text*) ++ ddr_ac_training.o (.text*) ++ ddr_dcc_training.o (.text*) ++ ddr_ddrt_training.o (.text*) ++ ddr_gate_training.o (.text*) ++ ddr_lpca_training.o (.text*) ++ ddr_mpr_training.o (.text*) ++ ddr_pcode_training.o (.text*) ++ ddr_wl_training.o (.text*) ++ ddr_training_console.o (.text*) ++ ddr_training_ctl.o (.text*) ++ ddr_training_boot.o (.text*) ++ ddr_training_custom.o (.text*) ++ uart.o (.text*) ++ div0.o (.text*) ++ sdhci_boot.o (.text*) ++ image_data.o (.text*) ++ startup.o(.text*) ++ reset.o(.text*) ++ __init_end = .; ++ ASSERT(((__init_end - __text_start) < 0x8000), "init sections too big!"); ++ *(.text*) ++ } ++ ++ . = ALIGN(8); ++ .image : { *(.image) } ++ ++ . = ALIGN(8); ++ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ++ ++ . = ALIGN(8); ++ .data : { ++ *(.data*) ++ } ++ ++ . = ALIGN(8); ++ ++ .got : { *(.got) } ++ ++ . = ALIGN(8); ++ __image_copy_end =.; ++ __bss_start = .; ++ .bss : ++ { ++ *(.bss) ++ } ++ __bss_end = .; ++ ++ ++ _end = .; ++} +diff --git a/arch/arm/cpu/armv8/ss928v100/init_registers.c b/arch/arm/cpu/armv8/ss928v100/init_registers.c +new file mode 100644 +index 0000000..08cc9b6 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/init_registers.c +@@ -0,0 +1,179 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#define W_WHETHER_WRITE (1 << 0) ++#define W_WHETHER_PM (1 << 1) ++#define W_WHETHER_BOOT_NORMAL (1 << 2) ++#define W_BIT_OFFSET 3 ++#define W_BIT_MASK (0x1f << W_BIT_OFFSET) ++#define W_REG_BIT_OFFSET 11 ++#define W_REG_BIT_MASK (0x1f << W_REG_BIT_OFFSET) ++ ++#define R_WHETHER_READ (W_WHETHER_WRITE << 16) ++#define R_WHETHER_PM (W_WHETHER_PM << 16) ++#define R_WHETHER_BOOT_NORMAL (W_WHETHER_BOOT_NORMAL << 16) ++#define R_BIT_OFFSET (W_BIT_OFFSET + 16) ++#define R_BIT_MASK (W_BIT_MASK << 16) ++#define R_REG_BIT_OFFSET (W_REG_BIT_OFFSET + 16) ++#define R_REG_BIT_MASK (W_REG_BIT_MASK << 16) ++ ++#define RW_BIT_NUM 32 ++ ++struct regentry { ++ unsigned int reg_addr; ++ unsigned int value; ++ unsigned int delay; ++ unsigned int attr; ++}; ++ ++static void dwb(void) /* drain write buffer */ ++{ ++} ++ ++static void writel(unsigned val, unsigned addr) ++{ ++ dwb(); ++ (*(volatile unsigned *)(uintptr_t)(addr)) = (val); ++ dwb(); ++} ++ ++static void delay(void) ++{ ++ __asm__ __volatile__("nop"); ++} ++ ++static void reg_read(struct regentry* const reg, unsigned int* const ret) ++{ ++ unsigned int reg_val_r; ++ unsigned int bit_start_r; ++ unsigned int bit_num_r; ++ ++ bit_start_r = ((reg->attr & R_REG_BIT_MASK) >> R_REG_BIT_OFFSET); ++ bit_num_r = ((reg->attr & R_BIT_MASK) >> R_BIT_OFFSET) + 1; ++ reg_val_r = (*(volatile unsigned *)(long)(reg->reg_addr)); ++ ++ if (bit_num_r != RW_BIT_NUM) { ++ reg_val_r >>= bit_start_r; ++ reg_val_r &= ((1 << bit_num_r) - 1); ++ } ++ ++ *ret = ((reg_val_r == reg->value) ? 0 : 1); ++} ++ ++static void reg_write(struct regentry *reg) ++{ ++ unsigned int reg_val_w; ++ unsigned int delay_2; ++ unsigned int bit_start_w; ++ unsigned int bit_num_w; ++ ++ delay_2 = reg->delay; ++ bit_start_w = ((reg->attr & W_REG_BIT_MASK) >> W_REG_BIT_OFFSET); ++ bit_num_w = ((reg->attr & W_BIT_MASK) >> W_BIT_OFFSET) + 1; ++ reg_val_w = (*(volatile unsigned *)(long)(reg->reg_addr)); ++ ++ if (bit_num_w == RW_BIT_NUM) { ++ reg_val_w = reg->value; ++ } else { ++ reg_val_w &= (~(((1 << bit_num_w) - 1) << bit_start_w)); ++ reg_val_w |= (reg->value) << bit_start_w; ++ } ++ writel(reg_val_w, reg->reg_addr); ++ ++ do { ++ delay(); ++ } while (delay_2--); ++} ++ ++static void read_write(struct regentry *reg, unsigned int pm) ++{ ++ unsigned int ret; ++ unsigned int delay_1; ++ ++ ret = 0; ++ delay_1 = reg->delay; ++ ++ if (pm) { ++ if (reg->attr & W_WHETHER_PM) { ++ reg_write(reg); ++ } else if (reg->attr & R_WHETHER_PM) { ++ do { ++ reg_read(reg, &ret); ++ delay(); ++ } while (ret); ++ ++ do { ++ delay(); ++ } while (delay_1--); ++ } else { ++ do { ++ delay(); ++ } while (delay_1--); ++ } ++ } else { ++ if (reg->attr & W_WHETHER_BOOT_NORMAL) { ++ reg_write(reg); ++ } else if (reg->attr & R_WHETHER_BOOT_NORMAL) { ++ do { ++ reg_read(reg, &ret); ++ delay(); ++ } while (ret); ++ ++ do { ++ delay(); ++ } while (delay_1--); ++ } else { ++ do { ++ delay(); ++ } while (delay_1--); ++ } ++ } ++} ++ ++static void part_read_write(struct regentry *reg_table, unsigned int pm) ++{ ++ unsigned int i; ++ ++ for (i = 0; ; i++) { ++ if ((!reg_table[i].reg_addr) && (!reg_table[i].value) && ++ (!reg_table[i].delay) && (!reg_table[i].attr)) ++ goto main_end; ++ ++ read_write(®_table[i], pm); ++ } ++ ++main_end: ++ delay(); ++} ++ ++/* ++ * base - reg base address ++ * pm - is suspend ++ * 0 normal ++ * 1 pm ++ */ ++void init_registers(unsigned long base, unsigned long pm) ++{ ++ struct regentry *reg_table = (struct regentry *)(uintptr_t)base; ++ ++ part_read_write(reg_table, pm); ++} ++ +diff --git a/arch/arm/cpu/armv8/ss928v100/lowlevel_init_v300.c b/arch/arm/cpu/armv8/ss928v100/lowlevel_init_v300.c +new file mode 100644 +index 0000000..4c048a1 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/lowlevel_init_v300.c +@@ -0,0 +1,516 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ddr_interface.h" ++ ++#define SYS_CTL_REG SYS_CTRL_REG_BASE ++ ++#define OK 0 ++#define ERROR (-1) ++#define CH0_DMC_CFG_DDRMODE 0x11148050 ++#define CH0_DMC_CFG_RNKVOL_0 0x11148060 ++#define CH0_DMC_CFG_RNKVOL_1 0x11148064 ++ ++#define CH1_DMC_CFG_DDRMODE 0x11149050 ++#define CH1_DMC_CFG_RNKVOL_0 0x11149060 ++#define CH1_DMC_CFG_RNKVOL_1 0x11149064 ++ ++#define CH2_DMC_CFG_DDRMODE 0x1114a050 ++#define CH2_DMC_CFG_RNKVOL_0 0x1114a060 ++#define CH2_DMC_CFG_RNKVOL_1 0x1114a064 ++ ++#define CH3_DMC_CFG_DDRMODE 0x1114b050 ++#define CH3_DMC_CFG_RNKVOL_0 0x1114b060 ++#define CH3_DMC_CFG_RNKVOL_1 0x1114b064 ++ ++#define CH0_QOS_CFG_DDRMODE 0x11144630 ++#define CH0_QOS_CFG_RNKVOL_0 0x11144634 ++#define CH0_QOS_CFG_RNKVOL_1 0x11144638 ++ ++#define CH1_QOS_CFG_DDRMODE 0x11144640 ++#define CH1_QOS_CFG_RNKVOL_0 0x11144644 ++#define CH1_QOS_CFG_RNKVOL_1 0x11144648 ++ ++#define CH2_QOS_CFG_DDRMODE 0x11144650 ++#define CH2_QOS_CFG_RNKVOL_0 0x11144654 ++#define CH2_QOS_CFG_RNKVOL_1 0x11144658 ++ ++#define CH3_QOS_CFG_DDRMODE 0x11144660 ++#define CH3_QOS_CFG_RNKVOL_0 0x11144664 ++#define CH3_QOS_CFG_RNKVOL_1 0x11144668 ++ ++static inline void delay(unsigned int num) ++{ ++ volatile unsigned int i; ++ ++ for (i = 0; i < (100 * num); i++) /* 100: Cycle */ ++ __asm__ __volatile__("nop"); ++} ++ ++static inline void dwb(void) /* drain write buffer */ ++{ ++} ++ ++static inline unsigned int readl(unsigned addr) ++{ ++ unsigned int val; ++ ++ val = (*(volatile unsigned int *)(long)(addr)); ++ return val; ++} ++ ++static inline void writel(unsigned val, unsigned addr) ++{ ++ dwb(); ++ (*(volatile unsigned *)(long)(addr)) = (val); ++ dwb(); ++} ++ ++#define REG_BASE_MISC 0x11020000 ++#define DDRCA_REE_RANDOM_L 0x4040 ++#define DDRCA_REE_RANDOM_H 0x4044 ++#define DDRCA_TEE_RANDOM_L 0x4048 ++#define DDRCA_TEE_RANDOM_H 0x404C ++#define DDRCA_EN 0x4050 ++#define DDRCA_REE_UPDATE 0x4054 ++#define DDRCA_TEE_UPDATE 0x4058 ++#define DDR_CA_LOCK 0x405c ++#define RANDOM_SIZE 4 ++ ++#define REG_TSENSOR_CTRL 0x1102e000 ++#define TSENSOR_CTRL0 0x0 ++#define TSENSOR_CTRL0_CFG 0xc0300000 ++#define TSENSOR_CTRL1 0x4 ++#define TSENSOR_CTRL1_CFG 0x8 ++ ++#define SYSCTRL_REG 0x11020000 ++#define HPM_CORE_VOL_REG (SYSCTRL_REG + 0x9004) ++#define HPM_MDA_VOL_REG (SYSCTRL_REG + 0x9104) ++#define HPM_NPU_VOL_REG (SYSCTRL_REG + 0x9204) ++#define HPM_MONITOR_CFG 0x60200001 ++#define HPM_CORE_OFFSET 0xb030 ++#define HPM_MDA_OFFSET 0xb020 ++#define HPM_NPU_OFFSET 0xb010 ++#define CYCLE_NUM 8 ++#define HPM_NPU_REG0 0xb018 ++#define HPM_NPU_REG1 0xb01c ++#define HPM_MDA_REG0 0xb028 ++#define HPM_MDA_REG1 0xb02c ++#define HPM_CORE_REG0 0xb038 ++#define HPM_CORE_REG1 0xb03c ++ ++#define HPM_CLK_REG 0x11014a80 ++#define HPM_CLK_CFG 0x30 ++ ++#define CPU_ISO_REG 0x1d821104 ++ ++#define TSENSOR_CTRL 0x70 ++#define TSENSOR_STATUS0 0x8 ++ ++#define OTP_CPU_IF_REG 0x10120000 ++#define OTP_HPM_CORE_OFFSET 0x1504 ++#define OTP_HPM_MDA_OFFSET 0x1534 ++#define OTP_HPM_NPU_OFFSET 0x1530 ++#define OTP_SHPM_MDA_OFFSET 0x153c ++ ++#define SYSCTRL_BASE_REG 0x11020000 ++#define HPM_CORE_STORAGE_REG 0x340 ++#define HPM_MDA_STORAGE_REG 0x344 ++#define HPM_NPU_STORAGE_REG 0x348 ++#define TEMPERATURE_STORAGE_REG 0x34c ++#define DELTA_V_STORAGE_REG 0x350 ++#define CORE_DUTY_STORAGE_REG 0x354 ++#define MDA_DUTY_STORAGE_REG 0x358 ++#define NPU_DUTY_STORAGE_REG 0x35c ++ ++/* physical max/min */ ++#define CORE_VOLT_MAX 920 ++#define CORE_VOLT_MIN 599 ++ ++#define MDA_VOLT_MAX 1049 ++#define MDA_VOLT_MIN 600 ++ ++#define NPU_VOLT_MAX 1049 ++#define NPU_VOLT_MIN 600 ++ ++/* curve max/min; voltage curve: v = (b - a * hpm) / 10 */ ++#define CORE_CURVE_VLOT_MAX 850 ++#define CORE_CURVE_VLOT_MIN 760 ++#define CORE_CURVE_B 16200 ++#define CORE_CURVE_A 20 ++ ++#define MEDIA_CURVE_VLOT_MAX 770 ++#define MEDIA_CURVE_VLOT_MIN 690 ++#define MEDIA_CURVE_B 11150 ++#define MEDIA_CURVE_A 10 ++ ++ ++#define NPU_CURVE_VLOT_MAX 810 ++#define NPU_CURVE_VLOT_MIN 730 ++#define NPU_CURVE_B 127500 ++#define NPU_CURVE_A 125 ++ ++#define SVB_VER_REG 0x11020168 ++#define SVB_VER 0x30303030 ++#define temperature_formula(val) (((((val) - 116) * 165) / 806) - 40) ++#define duty_formula(max, min, val) ((((max) - (val)) * 416 + ((max) - (min) + 1) / 2) / ((max) - (min)) - 1) ++#define volt_regval_formula(val) (((val) << 16) + ((416 - 1) << 4) + 0x5) ++ ++static unsigned hpm_value_avg(unsigned int *val, unsigned int num) ++{ ++ unsigned int i; ++ unsigned tmp = 0; ++ ++ for (i = 0; i < num; i++) /* 4: Cycle */ ++ tmp += val[i]; ++ ++ return (tmp / CYCLE_NUM) >> 2; ++} ++ ++static void get_hpm_value(unsigned int *hpm_core, unsigned int *hpm_npu, ++ unsigned int *hpm_mda) ++{ ++ int i; ++ unsigned int temp; ++ unsigned int core_value[4] = {0, 0, 0, 0}; ++ unsigned int mda_value[4] = {0, 0, 0, 0}; ++ unsigned int npu_value[4] = {0, 0, 0, 0}; ++ ++ for (i = 0; i < CYCLE_NUM; i++) { ++ /* (at least 16us*4) */ ++ delay(5); /* delay 5 s */ ++ /* npu */ ++ temp = readl(SYSCTRL_REG + HPM_NPU_REG0); ++ npu_value[1] += (temp >> 16) & 0x3ff; ++ npu_value[0] += temp & 0x3ff; ++ temp = readl(SYSCTRL_REG + HPM_NPU_REG1); ++ npu_value[3] += (temp >> 16) & 0x3ff; ++ npu_value[2] += temp & 0x3ff; ++ ++ /* mda */ ++ temp = readl(SYSCTRL_REG + HPM_MDA_REG0); ++ mda_value[1] += (temp >> 16) & 0x3ff; ++ mda_value[0] += temp & 0x3ff; ++ temp = readl(SYSCTRL_REG + HPM_MDA_REG1); ++ mda_value[3] += (temp >> 16) & 0x3ff; ++ mda_value[2] += temp & 0x3ff; ++ ++ /* core */ ++ temp = readl(SYSCTRL_REG + HPM_CORE_REG0); ++ core_value[1] += (temp >> 16) & 0x3ff; ++ core_value[0] += temp & 0x3ff; ++ temp = readl(SYSCTRL_REG + HPM_CORE_REG1); ++ core_value[3] += (temp >> 16) & 0x3ff; ++ core_value[2] += temp & 0x3ff; ++ } ++ ++ *hpm_core = hpm_value_avg(core_value, 4); /* 4 : Array size */ ++ *hpm_mda = hpm_value_avg(mda_value, 4); /* 4 : Array size */ ++ *hpm_npu = hpm_value_avg(npu_value, 4); /* 4 : Array size */ ++} ++ ++static void start_hpm(unsigned int *hpm_core, unsigned int *hpm_npu, ++ unsigned int *hpm_mda) ++{ ++ get_hpm_value(hpm_core, hpm_npu, hpm_mda); ++} ++ ++static void adjust_hpm(unsigned int *hpm_core, unsigned int *hpm_mda, ++ unsigned int *hpm_npu, int temperature) ++{ ++ unsigned int otp_hpm_core = readl(SYSCTRL_BASE_REG + OTP_HPM_CORE_OFFSET); ++ unsigned int otp_hpm_npu = readl(SYSCTRL_BASE_REG + OTP_HPM_NPU_OFFSET); ++ unsigned int otp_hpm_mda = readl(SYSCTRL_BASE_REG + OTP_HPM_MDA_OFFSET); ++ ++ if (temperature <= 0) { ++ *hpm_npu -= 2; /* 2 : Adjustment Value */ ++ *hpm_mda -= 3; /* 3 : Adjustment Value */ ++ *hpm_core -= 4; /* 4 : Adjustment Value */ ++ } else if (temperature > 100) { /* 100: temperature value */ ++ *hpm_npu += 5; /* 5 : Adjustment Value */ ++ *hpm_mda += 5; /* 5 : Adjustment Value */ ++ *hpm_core += 8; /* 8 : Adjustment Value */ ++ }else if(temperature > 70) { /* 70: temperature value */ ++ *hpm_npu += 3; /* 2 : Adjustment Value */ ++ *hpm_mda += 3; /* 2 : Adjustment Value */ ++ *hpm_core += 4; /* 3 : Adjustment Value */ ++ } ++ ++ ++ if (otp_hpm_core) { ++ if (*hpm_core > (otp_hpm_core + 59)) /* 10: Increment */ ++ *hpm_core = otp_hpm_core + 59; /* 5: Increment */ ++ } ++ ++ if (otp_hpm_npu) { ++ if (*hpm_npu > (otp_hpm_npu + 62)) /* 15: Increment */ ++ *hpm_npu = otp_hpm_npu + 62; ++ } ++ ++ if (otp_hpm_mda) { ++ if (*hpm_mda > (otp_hpm_mda + 61)) /* 15: Increment */ ++ *hpm_mda = otp_hpm_mda + 61; ++ } ++} ++ ++static void save_hpm(unsigned int hpm_core, unsigned int hpm_npu, ++ unsigned int hpm_mda) ++{ ++ writel(hpm_mda, SYSCTRL_BASE_REG + HPM_MDA_STORAGE_REG); ++ writel(hpm_npu, SYSCTRL_BASE_REG + HPM_NPU_STORAGE_REG); ++ writel(hpm_core, SYSCTRL_BASE_REG + HPM_CORE_STORAGE_REG); ++} ++ ++static unsigned int calc_volt_regval(unsigned int volt_val, unsigned int volt_max, ++ unsigned int volt_min) ++{ ++ unsigned int duty; ++ ++ if (volt_val >= volt_max) ++ volt_val = volt_max - 1; ++ if (volt_val <= volt_min) ++ volt_val = volt_min + 1; ++ duty = duty_formula(volt_max, volt_min, volt_val); ++ ++ return duty; ++} ++ ++static void set_hpm_core_volt(unsigned int hpm_core_value, int delta_v) ++{ ++ unsigned int volt_val; ++ unsigned int reg_val; ++ ++ volt_val = (CORE_CURVE_B - CORE_CURVE_A * hpm_core_value) / 10; ++ ++ if (volt_val > CORE_CURVE_VLOT_MAX) ++ volt_val = CORE_CURVE_VLOT_MAX; ++ else if (volt_val < CORE_CURVE_VLOT_MIN) ++ volt_val = CORE_CURVE_VLOT_MIN; ++ ++ volt_val += delta_v; ++ reg_val = calc_volt_regval(volt_val, CORE_VOLT_MAX, CORE_VOLT_MIN); ++ writel(reg_val, SYSCTRL_BASE_REG + CORE_DUTY_STORAGE_REG); ++ writel(reg_val, HPM_CORE_VOL_REG); ++} ++ ++static void set_hpm_mda_volt(unsigned int hpm_mda_value, int delta_v) ++{ ++ unsigned int volt_val; ++ unsigned int reg_val; ++ ++ volt_val = (MEDIA_CURVE_B - MEDIA_CURVE_A * hpm_mda_value) / 10; ++ if (volt_val > MEDIA_CURVE_VLOT_MAX) ++ volt_val = MEDIA_CURVE_VLOT_MAX; ++ else if (volt_val < MEDIA_CURVE_VLOT_MIN) ++ volt_val = MEDIA_CURVE_VLOT_MIN; ++ ++ volt_val += delta_v; ++ reg_val = calc_volt_regval(volt_val, MDA_VOLT_MAX, MDA_VOLT_MIN); ++ writel(reg_val, SYSCTRL_BASE_REG + MDA_DUTY_STORAGE_REG); ++ ++ writel(reg_val, HPM_MDA_VOL_REG); ++} ++ ++static void set_hpm_npu_volt(unsigned int hpm_npu_value, int delta_v) ++{ ++ unsigned int volt_val; ++ unsigned int reg_val; ++ ++ volt_val = (NPU_CURVE_B - NPU_CURVE_A * hpm_npu_value) / 100; ++ if (volt_val > NPU_CURVE_VLOT_MAX) ++ volt_val = NPU_CURVE_VLOT_MAX; ++ else if (volt_val < NPU_CURVE_VLOT_MIN) ++ volt_val = NPU_CURVE_VLOT_MIN; ++ ++ volt_val += delta_v; ++ reg_val = calc_volt_regval(volt_val, NPU_VOLT_MAX, NPU_VOLT_MIN); ++ writel(reg_val, SYSCTRL_BASE_REG + NPU_DUTY_STORAGE_REG); ++ ++ writel(reg_val, HPM_NPU_VOL_REG); ++} ++ ++static void get_delta_v(int *core_delta_v, int *npu_delta_v, int *mda_delta_v) ++{ ++ unsigned int value = readl(SYSCTRL_REG + OTP_SHPM_MDA_OFFSET); ++ writel(value, SYSCTRL_BASE_REG + DELTA_V_STORAGE_REG); ++ /* core:bit 11-8, ++ * bit11 equal to 1 means negative, equal to 0 means positive, ++ * bit 8-10 is the absolute delta_v ++ */ ++ int flag = (value & 0x800) ? -1 : 1; ++ *core_delta_v = flag * (int)((value >> 8) & 0x7) * 10; ++ ++ /* mda:bit 7-4, ++ * bit7 equal to 1 means negative, equal to 0 means positive, ++ * bit 4-6 is the absolute delta_v ++ */ ++ flag = (value & 0x80) ? -1 : 1; ++ *mda_delta_v = flag * (int)((value >> 4) & 0x7) * 10; ++ ++ /* npu:bit 3-0, ++ * bit3 equal to 1 means negative, equal to 0 means positive, ++ * bit 0-2 is the absolute delta_v ++ */ ++ flag = (value & 0x8) ? -1 : 1; ++ *npu_delta_v = flag * (int)(value & 0x7) * 10; ++} ++ ++ ++static void set_volt(unsigned int hpm_core, unsigned int hpm_npu, ++ unsigned int hpm_mda) ++{ ++ int core_delta_v = 0; ++ int npu_delta_v = 0; ++ int mda_delta_v = 0; ++ get_delta_v(&core_delta_v, &npu_delta_v, &mda_delta_v); ++ ++ set_hpm_core_volt(hpm_core, core_delta_v); ++ set_hpm_mda_volt(hpm_mda, mda_delta_v); ++ set_hpm_npu_volt(hpm_npu, npu_delta_v); ++ ++ /* delay 300 Cycle */ ++ delay(300); ++} ++ ++static void get_temperature(int *temperature) ++{ ++ int value = 0; ++ int tsensor_chn; ++ float m = 0.5; ++ float temperature_temp[3] = {0}; ++ for(tsensor_chn = 0; tsensor_chn < 3; tsensor_chn++) { ++ value = (int)(readl(REG_TSENSOR_CTRL + TSENSOR_STATUS0 + ++ 0x100 * tsensor_chn) & 0x3ff); ++ ++ m = ((float)value - 146) / 718 * 165 - 40; ++ ++ if(m > 0) ++ temperature_temp[tsensor_chn] = m + 0.5; ++ else ++ temperature_temp[tsensor_chn] = m - 0.5; ++ } ++ ++ *temperature = (temperature_temp[0]+temperature_temp[1]+temperature_temp[2])/3; ++ writel(*temperature, SYSCTRL_BASE_REG + TEMPERATURE_STORAGE_REG); ++} ++ ++void init_temperature(void) ++{ ++ int tsensor_chn; ++ ++ for(tsensor_chn = 0; tsensor_chn < 3; tsensor_chn++) { ++ writel(TSENSOR_CTRL0_CFG, REG_TSENSOR_CTRL + TSENSOR_CTRL0 + ++ 0x100 * tsensor_chn); ++ writel(TSENSOR_CTRL1_CFG, REG_TSENSOR_CTRL + TSENSOR_CTRL1 + ++ 0x100 * tsensor_chn); ++ } ++ ++} ++ ++void init_hpm(void) ++{ ++ /* open hmp clock */ ++ writel(HPM_CLK_CFG, HPM_CLK_REG); ++ ++ /* npu */ ++ writel(HPM_MONITOR_CFG, SYSCTRL_REG + HPM_NPU_OFFSET); ++ /* mda */ ++ writel(HPM_MONITOR_CFG, SYSCTRL_REG + HPM_MDA_OFFSET); ++ /* core */ ++ writel(HPM_MONITOR_CFG, SYSCTRL_REG + HPM_CORE_OFFSET); ++ ++} ++static void start_svb(void) ++{ ++ unsigned int hpm_core; ++ unsigned int hpm_npu; ++ unsigned int hpm_mda; ++ int temperature; ++ ++ /* init temperature and hpm*/ ++ init_temperature(); ++ init_hpm(); ++ delay(1000); ++ start_hpm(&hpm_core, &hpm_npu, &hpm_mda); ++ ++ /*get temperature */ ++ get_temperature(&temperature); ++ ++ adjust_hpm(&hpm_core, &hpm_mda, &hpm_npu, temperature); ++ set_volt(hpm_core, hpm_npu, hpm_mda); ++ save_hpm(hpm_core, hpm_npu, hpm_mda); ++ ++ /* add SVB VER*/ ++ writel(SVB_VER, SVB_VER_REG); ++} ++ ++static void set_qosbuf_cfg(void) ++{ ++ unsigned int val; ++ ++ val = readl(CH0_DMC_CFG_DDRMODE); ++ writel(val, CH0_QOS_CFG_DDRMODE); ++ val = readl(CH0_DMC_CFG_RNKVOL_0); ++ writel(val, CH0_QOS_CFG_RNKVOL_0); ++ val = readl(CH0_DMC_CFG_RNKVOL_1); ++ writel(val, CH0_QOS_CFG_RNKVOL_1); ++ ++ val = readl(CH1_DMC_CFG_DDRMODE); ++ writel(val, CH1_QOS_CFG_DDRMODE); ++ val = readl(CH1_DMC_CFG_RNKVOL_0); ++ writel(val, CH1_QOS_CFG_RNKVOL_0); ++ val = readl(CH1_DMC_CFG_RNKVOL_1); ++ writel(val, CH1_QOS_CFG_RNKVOL_1); ++ ++ val = readl(CH2_DMC_CFG_DDRMODE); ++ writel(val, CH2_QOS_CFG_DDRMODE); ++ val = readl(CH2_DMC_CFG_RNKVOL_0); ++ writel(val, CH2_QOS_CFG_RNKVOL_0); ++ val = readl(CH2_DMC_CFG_RNKVOL_1); ++ writel(val, CH2_QOS_CFG_RNKVOL_1); ++ ++ val = readl(CH3_DMC_CFG_DDRMODE); ++ writel(val, CH3_QOS_CFG_DDRMODE); ++ val = readl(CH3_DMC_CFG_RNKVOL_0); ++ writel(val, CH3_QOS_CFG_RNKVOL_0); ++ val = readl(CH3_DMC_CFG_RNKVOL_1); ++ writel(val, CH3_QOS_CFG_RNKVOL_1); ++} ++ ++void start_ddr_training(unsigned int base) ++{ ++ start_svb(); ++ ++ set_qosbuf_cfg(); ++ ++ ddr_set_rdqbdl_def_val(); ++ ++ /* ddr hw training */ ++ ddr_hw_training_init(); ++ ++ /* ddr sw training */ ++ ddr_sw_training_if(); ++ ++ ddr_retrain_anti_aging_enable(); ++ ++ /* ddr DMC auto power down config */ ++ ddr_dmc_auto_power_down_cfg(); ++} +diff --git a/arch/arm/cpu/armv8/ss928v100/reset.S b/arch/arm/cpu/armv8/ss928v100/reset.S +new file mode 100644 +index 0000000..ad86198 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/reset.S +@@ -0,0 +1,33 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++ ++.global reset_cpu ++reset_cpu: ++ ldr x1, rstctl /* get addr for global reset */ ++ /* reg */ ++ mov w3, #0x2 /* full reset pll + mpu */ ++ str w3, [x1] /* force reset */ ++ mov x0, x0 ++ ++_loop_forever: ++ b _loop_forever ++.align 3 ++rstctl: ++ .quad SYS_CTRL_REG_BASE + REG_SC_SYSRES +diff --git a/arch/arm/cpu/armv8/ss928v100/sdhci_boot.c b/arch/arm/cpu/armv8/ss928v100/sdhci_boot.c +new file mode 100644 +index 0000000..c0036d6 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/sdhci_boot.c +@@ -0,0 +1,391 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++ ++#define DELAY_US 1000 ++ ++#define MMC_BLK_SZ 512 ++#define CP_STEP1_SIZE 0x7800 ++ ++/* ++ * Controller registers ++ */ ++#define SDHCI_DMA_ADDRESS 0x00 ++#define SDHCI_BLOCK_SIZE 0x04 ++#define SDHCI_BLOCK_COUNT 0x06 ++#define SDHCI_ARGUMENT 0x08 ++ ++#define SDHCI_TRANSFER_MODE 0x0C ++#define SDHCI_TRNS_DMA 0x0001 ++#define SDHCI_TRNS_BLK_CNT_EN 0x0002 ++#define SDHCI_TRNS_READ 0x0010 ++#define SDHCI_TRNS_MULTI 0x0020 ++ ++#define SDHCI_COMMAND 0x0E ++#define SDHCI_CMD_RESP_NONE 0x0000 ++#define SDHCI_CMD_RESP_LONG 0x0001 ++#define SDHCI_CMD_RESP_SHORT 0x0002 ++#define SDHCI_CMD_CRC 0x0008 ++#define SDHCI_CMD_DATA 0x0020 ++ ++#define SDHCI_RESPONSE 0x10 ++#define SDHCI_BUFFER 0x20 ++ ++#define SDHCI_PRESENT_STATE 0x24 ++#define SDHCI_PSTATE_DAT_0 0x00100000 ++ ++#define SDHCI_HOST_CONTROL 0x28 ++#define SDHCI_CTRL_4BITBUS 0x02 ++#define SDHCI_CTRL_8BITBUS 0x20 ++ ++#define SDHCI_POWER_CONTROL 0x29 ++#define SDHCI_POWER_ON 0x01 ++#define SDHCI_POWER_180 0x0A ++#define SDHCI_POWER_300 0x0C ++#define SDHCI_POWER_330 0x0E ++ ++#define SDHCI_CLOCK_CONTROL 0x2C ++#define SDHCI_CLOCK_INT_EN 0x0001 ++#define SDHCI_CLOCK_INT_STABLE 0x0002 ++#define SDHCI_CLOCK_CARD_EN 0x0004 ++ ++#define SDHCI_TIMEOUT_CONTROL 0x2E ++ ++#define SDHCI_SOFTWARE_RESET 0x2F ++#define SDHCI_RESET_ALL 0x01 ++#define SDHCI_RESET_CMD 0x02 ++#define SDHCI_RESET_DATA 0x04 ++ ++#define SDHCI_INT_STATUS 0x30 ++#define SDHCI_INT_RESPONSE 0x00000001 ++#define SDHCI_INT_DATA_END 0x00000002 ++#define SDHCI_INT_DMA 0x00000008 ++#define SDHCI_INT_DATA_AVAIL 0x00000020 ++#define SDHCI_INT_NORMAL_MASK 0x00007FFF ++#define SDHCI_INT_ERROR_MASK 0xFFFF8000 ++ ++#define SDHCI_INT_ENABLE 0x34 ++ ++#define SDHCI_EMMC_CTRL 0x52C ++#define SDHCI_CARD_IS_EMMC 0x0001 ++ ++#define SDHCI_BOOT_CTRL 0x52E ++#define MAN_BOOT_EN 0x0001 ++#define VALIDATE_BOOT 0x0080 ++ ++#define SDHCI_EMMC_HW_RESET 0x534 ++ ++#define sdhci_make_cmd(idx, param) ((((idx) & 0xFF) << 8) | ((param) & 0xFF)) ++#define sdhci_make_blksz(dma, blksz) ((((dma) & 0x7) << 12) | ((blksz) & 0xFFF)) ++ ++#define MMC_CMD_GO_IDLE_STATE 0 ++#define MMC_CMD_SEND_OP_COND 1 ++#define MMC_CMD_ALL_SEND_CID 2 ++#define MMC_CMD_SET_RELATIVE_ADDR 3 ++#define MMC_CMD_SWITCH 6 ++#define MMC_CMD_SELECT_CARD 7 ++#define MMC_CMD_SEND_CSD 9 ++#define MMC_CMD_STOP_TRANSMISSION 12 ++#define MMC_CMD_SET_BLOCKLEN 16 ++#define MMC_CMD_READ_SINGLE_BLOCK 17 ++#define MMC_CMD_READ_MULTIPLE_BLOCK 18 ++#define MMC_CMD_SET_BLOCK_COUNT 23 ++ ++#define MMC_SWITCH_MODE_WRITE_BYTE 0x3 ++#define MMC_SWITCH_ACCESS_SHIFT 24 ++#define MMC_SWITCH_INDEX_SHIFT 16 ++#define MMC_SWITCH_VALUE_SHIFT 8 ++ ++/* SDMA Configuration */ ++#define BOUNDARY_SIZE (512 * 1024) /* 512K */ ++#define BOUNDARY_ARG (0x7 << 12) /* 512K */ ++#define MMC_SDMA_ENABLE 1 ++ ++#define OCR_BUSY 0x80000000 ++#define OCR_HCS 0x40000000 ++ ++#define debug_printf(fmt, args...); ++#define reg_get(addr) (*(volatile unsigned int *)((uintptr_t)(addr))) ++ ++static unsigned int is_bootmode(void) ++{ ++ return !(reg_get(REG_BASE_SCTL + REG_PERI_EMMC_STAT) & EMMC_NORMAL_MODE); ++} ++ ++static unsigned int get_hcs(void) ++{ ++ return reg_get(REG_SAVE_HCS) & OCR_HCS; ++} ++ ++static inline void delay(unsigned int cnt) ++{ ++ while (cnt--) ++ __asm__ __volatile__("nop"); ++} ++ ++static inline unsigned int sdhci_readb(unsigned addr) ++{ ++ return *((volatile unsigned char *) (uintptr_t)(EMMC_BASE_REG + addr)); ++} ++ ++static inline void sdhci_writeb(unsigned val, unsigned addr) ++{ ++ (*(volatile unsigned char *) (uintptr_t)(EMMC_BASE_REG + addr)) = (val); ++} ++ ++static inline unsigned int sdhci_readw(unsigned addr) ++{ ++ return *((volatile unsigned short *) (uintptr_t)(EMMC_BASE_REG + addr)); ++} ++ ++static inline void sdhci_writew(unsigned val, unsigned addr) ++{ ++ (*(volatile unsigned short *) (uintptr_t)(EMMC_BASE_REG + addr)) = (val); ++} ++ ++static inline unsigned int sdhci_readl(unsigned addr) ++{ ++ return *((volatile unsigned int *) (uintptr_t)(EMMC_BASE_REG + addr)); ++} ++ ++static inline void sdhci_writel(unsigned val, unsigned addr) ++{ ++ (*(volatile unsigned int *) (uintptr_t)(EMMC_BASE_REG + addr)) = (val); ++} ++ ++static int sdhci_read_block_pio(void *data_addr, unsigned int block) ++{ ++ const unsigned int offset = sizeof(unsigned int); ++ unsigned int size; ++ unsigned char *buf; ++ ++ size = MMC_BLK_SZ; ++ buf = (unsigned char *)data_addr + MMC_BLK_SZ * block; ++ while (size) { ++ *(unsigned int *)buf = sdhci_readl(SDHCI_BUFFER); ++ buf += offset; ++ size -= offset; ++ } ++ ++ return 0; ++} ++ ++static int sdhci_check_int_status(unsigned int mask, unsigned int timeout) ++{ ++ unsigned int reg; ++ ++ timeout *= 1000; /* ms is converted to us multiplied by 1000 */ ++ for (;;) { ++ reg = sdhci_readl(SDHCI_INT_STATUS); ++ if (reg & mask) ++ break; ++ if (!(--timeout)) { ++ debug_printf("wait int status time out, reg = 0x%x, mask = 0x%x\n", ++ reg, mask); ++ return -1; ++ } ++ if (reg & SDHCI_INT_ERROR_MASK) { ++ debug_printf("int err: reg = 0x%x\n", reg); ++ return -1; ++ } ++ ++ delay(DELAY_US); ++ } ++ ++ return 0; ++} ++ ++static int sdhci_boot_read(void *data_addr, unsigned int read_block) ++{ ++ const unsigned int timeout = 2000; /* 2s timeout: 2000000 * 1us */ ++ int ret; ++ unsigned int blocks = 0; ++ ++ while (1) { ++ ret = sdhci_check_int_status(SDHCI_INT_DATA_AVAIL, timeout); ++ if (ret) { ++ debug_printf("wait data available int time out\n"); ++ return ret; ++ } ++ ++ sdhci_writel(SDHCI_INT_DATA_AVAIL, SDHCI_INT_STATUS); ++ sdhci_read_block_pio(data_addr, blocks); ++ ++ blocks++; ++ if (blocks == read_block) ++ break; ++ } ++ ++ return 0; ++} ++ ++static int mmc_send_cmd(unsigned int cmd, unsigned int arg) ++{ ++ const unsigned int timeout = 3000; /* 3s timeout: 3000000 * 1us */ ++ ++ sdhci_writel(0xFFFFFFFF, SDHCI_INT_STATUS); ++ sdhci_writel(arg, SDHCI_ARGUMENT); ++ sdhci_writew(cmd, SDHCI_COMMAND); ++ ++ if (sdhci_check_int_status(SDHCI_INT_RESPONSE, timeout)) { ++ debug_printf("send cmd error\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void mmc_read_cmd(unsigned long int start_addr, unsigned int src, ++ unsigned int size, unsigned int dma) ++{ ++ unsigned int cmd, hcs; ++ unsigned short mode; ++ ++ /* Send CMD16 to set blocksize */ ++ cmd = sdhci_make_cmd(MMC_CMD_SET_BLOCKLEN, SDHCI_CMD_CRC | ++ SDHCI_CMD_RESP_SHORT); ++ mmc_send_cmd(cmd, MMC_BLK_SZ); ++ ++ /* set data timeout */ ++ sdhci_writeb(0xE, SDHCI_TIMEOUT_CONTROL); ++ ++ /* set host count */ ++ sdhci_writew(size, SDHCI_BLOCK_COUNT); ++ ++ /* Send CMD23 to set blockcount */ ++ cmd = sdhci_make_cmd(MMC_CMD_SET_BLOCK_COUNT, ++ SDHCI_CMD_CRC | SDHCI_CMD_RESP_SHORT); ++ mmc_send_cmd(cmd, size); ++ ++ /* set transfer mode */ ++ mode = SDHCI_TRNS_READ | SDHCI_TRNS_MULTI | SDHCI_TRNS_BLK_CNT_EN; ++ if (dma) ++ mode |= SDHCI_TRNS_DMA; ++ ++ sdhci_writew(mode, SDHCI_TRANSFER_MODE); ++ ++ /* set SDMA address */ ++ if (dma) ++ sdhci_writel(start_addr, SDHCI_DMA_ADDRESS); ++ ++ /* Send CMD18 for multiple block read */ ++ hcs = get_hcs(); ++ if (hcs) { ++ cmd = sdhci_make_cmd(MMC_CMD_READ_MULTIPLE_BLOCK, ++ SDHCI_CMD_CRC | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_DATA); ++ mmc_send_cmd(cmd, src / MMC_BLK_SZ); ++ } else { ++ cmd = sdhci_make_cmd(MMC_CMD_READ_MULTIPLE_BLOCK, ++ SDHCI_CMD_CRC | SDHCI_CMD_RESP_SHORT | SDHCI_CMD_DATA); ++ mmc_send_cmd(cmd, src); ++ } ++} ++ ++static int sdhci_normal_read_sdma(void *dst, unsigned int src, unsigned int size) ++{ ++ unsigned int stat; ++ unsigned int timeout; ++ unsigned long int start_addr; ++ ++ start_addr = (uintptr_t)dst; ++ ++ /* set host block size 512 */ ++ sdhci_writew(MMC_BLK_SZ | BOUNDARY_ARG, SDHCI_BLOCK_SIZE); ++ ++ mmc_read_cmd(start_addr, src, size, MMC_SDMA_ENABLE); ++ ++ sdhci_writel(SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); ++ ++ timeout = 300000; /* 3s timeout: 300000 * 10us */ ++ do { ++ stat = sdhci_readl(SDHCI_INT_STATUS); ++ if (stat & SDHCI_INT_ERROR_MASK) { ++ debug_printf("interrupt error\n"); ++ return -1; ++ } ++ ++ if (stat & SDHCI_INT_DMA) { ++ sdhci_writel(SDHCI_INT_DMA, SDHCI_INT_STATUS); ++ start_addr &= ~(BOUNDARY_SIZE - 1); ++ start_addr += BOUNDARY_SIZE; ++ sdhci_writel(start_addr, SDHCI_DMA_ADDRESS); ++ } ++ ++ if (timeout > 0) { ++ delay(10 * DELAY_US); /* delay 10us */ ++ timeout -= 1; ++ } else { ++ debug_printf("read timeout!\n"); ++ return -1; ++ } ++ } while (!(stat & SDHCI_INT_DATA_END)); ++ ++ sdhci_writel(SDHCI_INT_DATA_END, SDHCI_INT_STATUS); ++ ++ return 0; ++} ++ ++static void copy_step1_to_ddr(unsigned int *dst, unsigned int *src, unsigned int size) ++{ ++ unsigned int cycle = size / sizeof(unsigned int); ++ unsigned int i; ++ ++ for (i = 0; i < cycle; i++) ++ *dst++ = *src++; ++} ++ ++static int emmc_read_boot_data(void *data_addr, unsigned int data_size) ++{ ++ unsigned int read_block; ++ int bootmode; ++ ++ if (data_size <= CP_STEP1_SIZE) { ++ copy_step1_to_ddr((void *)data_addr, (void *)CP_STEP1_ADDR, data_size); ++ return 0; ++ } else { ++ copy_step1_to_ddr((void *)data_addr, (void *)CP_STEP1_ADDR, CP_STEP1_SIZE); ++ data_addr += CP_STEP1_SIZE; ++ data_size -= CP_STEP1_SIZE; ++ } ++ ++ if (data_size % MMC_BLK_SZ) { ++ debug_printf("sdhci_read_boot_data error\n"); ++ debug_printf("data_size:%d not round by block size\n", data_size); ++ read_block = data_size / MMC_BLK_SZ + 1; ++ } else { ++ read_block = data_size / MMC_BLK_SZ; ++ } ++ ++ bootmode = is_bootmode(); ++ if (bootmode) ++ return sdhci_boot_read(data_addr, read_block); ++ ++ return sdhci_normal_read_sdma(data_addr, CP_STEP1_SIZE, read_block); ++} ++ ++int emmc_boot_read(void *ptr, unsigned int size) ++{ ++ int ret; ++ ++ ret = emmc_read_boot_data(ptr, size); ++ return ret; ++} +diff --git a/arch/arm/cpu/armv8/ss928v100/start.S b/arch/arm/cpu/armv8/ss928v100/start.S +new file mode 100644 +index 0000000..b2cadac +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/start.S +@@ -0,0 +1,371 @@ ++/* ++ * (C) Copyright 2013 ++ * David Feng ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++/************************************************************************* ++ * ++ * Startup Code (reset vector) ++ * ++ *************************************************************************/ ++/* ++ * Branch according to exception level ++ */ ++.macro switch_el, xreg, el3_label, el2_label, el1_label ++ mrs \xreg, CurrentEL ++ cmp \xreg, 0xc ++ b.eq \el3_label ++ cmp \xreg, 0x8 ++ b.eq \el2_label ++ cmp \xreg, 0x4 ++ b.eq \el1_label ++.endm ++ ++/* ++ * Enter Exception. ++ * This will save the processor state that is ELR/X0~X30 ++ * to the stack frame. ++ */ ++.macro exception_entry ++ stp x29, x30, [sp, #-16]! ++ stp x27, x28, [sp, #-16]! ++ stp x25, x26, [sp, #-16]! ++ stp x23, x24, [sp, #-16]! ++ stp x21, x22, [sp, #-16]! ++ stp x19, x20, [sp, #-16]! ++ stp x17, x18, [sp, #-16]! ++ stp x15, x16, [sp, #-16]! ++ stp x13, x14, [sp, #-16]! ++ stp x11, x12, [sp, #-16]! ++ stp x9, x10, [sp, #-16]! ++ stp x7, x8, [sp, #-16]! ++ stp x5, x6, [sp, #-16]! ++ stp x3, x4, [sp, #-16]! ++ stp x1, x2, [sp, #-16]! ++ ++ /* Could be running at EL3/EL2/EL1 */ ++ switch_el x11, 3f, 2f, 1f ++3: mrs x1, esr_el3 ++ mrs x2, elr_el3 ++ b 0f ++2: mrs x1, esr_el2 ++ mrs x2, elr_el2 ++ b 0f ++1: mrs x1, esr_el1 ++ mrs x2, elr_el1 ++0: ++ stp x2, x0, [sp, #-16]! ++ mov x0, sp ++.endm ++ ++.globl _start ++_start: ++ b reset ++ ++.balignl 64,0xdeadbeef ++__blank_zone_start: ++.fill 11008,1,0 ++__blank_zone_end: ++ ++.globl _blank_zone_start ++_blank_zone_start: ++.quad __blank_zone_start ++ ++.globl _blank_zone_end ++_blank_zone_end: ++.quad __blank_zone_end ++.balignl 16,0xdeadbeef ++ ++.align 3 ++ ++.globl _TEXT_BASE ++_TEXT_BASE: ++ .quad TEXT_BASE ++ ++/* ++ * These are defined in the linker script. ++ */ ++.globl _end_ofs ++_end_ofs: ++ .quad _end - _start ++ ++.globl _bss_start_ofs ++_bss_start_ofs: ++ .quad __bss_start - _start ++ ++.globl _bss_end_ofs ++_bss_end_ofs: ++ .quad __bss_end - _start ++ ++reset: ++ /* ++ * Could be EL3/EL2/EL1, Initial State: ++ * Little Endian, MMU Disabled, i/dCache Disabled ++ */ ++ adr x0, vectors ++ switch_el x1, 3f, 2f, 1f ++3: msr vbar_el3, x0 ++ mrs x0, scr_el3 ++ orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */ ++ msr scr_el3, x0 ++ msr cptr_el3, xzr /* Enable FP/SIMD */ ++#ifdef COUNTER_FREQUENCY ++ ldr x0, =COUNTER_FREQUENCY ++ msr cntfrq_el0, x0 /* Initialize CNTFRQ */ ++#endif ++ b 0f ++2: msr vbar_el2, x0 ++ mov x0, #0x33ff ++ msr cptr_el2, x0 /* Enable FP/SIMD */ ++ b 0f ++1: msr vbar_el1, x0 ++ mov x0, #3 << 20 ++ msr cpacr_el1, x0 /* Enable FP/SIMD */ ++0: ++ ++ /* ++ * Cache/BPB/TLB Invalidate ++ * i-cache is invalidated before enabled in icache_enable() ++ * tlb is invalidated before mmu is enabled in dcache_enable() ++ * d-cache is invalidated before enabled in dcache_enable() ++ */ ++ ++#ifndef CONFIG_BSP_DISABLE_DOWNLOAD ++ /* ++ * read system register REG_SC_GEN2 ++ * check if ziju flag ++ */ ++ ldr x0, =SYS_CTRL_REG_BASE ++ ldr w1, [x0, #REG_SC_GEN2] ++ ldr w2, =0x7a696a75 /* magic for "ziju" */ ++ cmp w1, w2 ++ bne normal_start_flow ++ mov x1, sp /* save sp */ ++ str w1, [x0, #REG_SC_GEN2] /* clear ziju flag */ ++ ++ziju_flow: ++ ldr x2, =(STACK_TRAINING) ++ bic sp, x2, #0xf /* 16-byte alignment for ABI compliance */ ++ ldr x0, _blank_zone_start ++ ldr x1, _TEXT_BASE ++ sub x0, x0, x1 ++ adr x1, _start ++ add x0, x0, x1 ++ mov x1, #0x0 /* flags: 0->normal 1->pm */ ++ bl init_registers /* init PLL/DDRC/... */ ++ bl start_ddr_training /* DDR training */ ++ ++ ldr x0, =SYS_CTRL_REG_BASE ++ ldr w1, [x0, #REG_SC_GEN2] ++ mov sp, x1 /* restore sp */ ++ ldr w1, [x0, #REG_SC_GEN3] ++ mov x30, x1 ++ ret /* return to bootrom */ ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop ++ b . /* bug here */ ++ ++ ++normal_start_flow: ++#endif ++ /* set stack for C code */ ++ ldr x0, =(CONFIG_SYS_INIT_SP_ADDR) ++ bic sp, x0, #0xf /* 16-byte alignment for ABI compliance */ ++ ++ bl uart_early_init ++ adr x0, Str_SystemSartup ++ bl uart_early_puts ++ ++running_addr_check: ++ adr x0,running_addr_check ++ lsr x0, x0, #28 ++ cmp x0, #4 ++ bge not_ddr_init ++ ++ /* read init table and config registers */ ++ ldr x0, _blank_zone_start ++ ldr x1, _TEXT_BASE ++ sub x0, x0, x1 ++ adr x1, _start ++ add x0, x0, x1 ++ mov x1, #0 /* flags: 0->normal 1->pm */ ++ bl init_registers ++ bl start_ddr_training ++check_boot_mode: ++ ldr x0, =SYS_CTRL_REG_BASE ++ ldr w0, [x0, #REG_SYSSTAT] ++ lsr w6, w0, #2 ++ and w6, w6, #0x3 ++ cmp w6, #BOOT_FROM_EMMC ++ bne not_ddr_init ++ ++#ifdef CONFIG_SDHCI ++emmc_boot: ++ ldr x0, _TEXT_BASE ++ ldr x1, =__image_copy_start /* x1 <- SRC &__image_copy_start */ ++ ldr x2, =__bss_start /* x2 <- SRC &__bss_start */ ++ sub x1, x2, x1 ++ bl emmc_boot_read ++ b no_copy ++#endif ++ ++not_ddr_init: ++master_cpu: ++relocate: ++ adr x0, _start /*runing addr start*/ ++ ldr x1, =__image_copy_start /* x1 <- SRC &__image_copy_start */ ++ cmp x0, x1 ++ b.eq no_copy /* skip relocation */ ++ ldr x2, =__bss_start /* x2 <- SRC &__bss_start*/ ++ ++copy_loop: ++ ldp x10, x11, [x0], #16 /* copy from source address [x0] */ ++ stp x10, x11, [x1], #16 /* copy to target address [x1] */ ++ cmp x1, x2 /* until source end address [x2] */ ++ b.lo copy_loop ++no_copy: ++clear_remap: ++ ldr x1, =SYS_CTRL_REG_BASE ++ ldr w0, [x1] ++ mov w2, #0x5 ++ bic w0, w0, #0xf ++ orr w0, w0, w2 ++ str w0, [x1] ++set_scs_finish: ++ ldr x1, =REG_SCS_CTRL ++ ldr w0, [x1] ++ mov w2, #0x5 ++ bic w0, w0, #0xf ++ orr w0, w0, w2 ++ str w0, [x1] ++jump_to_ddr: ++ adr x0, _start_armboot ++ ldr x30,[x0] ++ ret ++.align 3 ++_start_armboot: .quad start_armboot ++ ++/*-----------------------------------------------------------------------*/ ++ ++/* ++ * Exception vectors. ++ */ ++ .align 3 ++ .globl vectors ++vectors: ++ .align 3 ++ b _do_bad_sync /* Current EL Synchronous Thread */ ++ ++ .align 3 ++ b _do_bad_irq /* Current EL IRQ Thread */ ++ ++ .align 3 ++ b _do_bad_fiq /* Current EL FIQ Thread */ ++ ++ .align 3 ++ b _do_bad_error /* Current EL Error Thread */ ++ ++ .align 3 ++ b _do_sync /* Current EL Synchronous Handler */ ++ ++ .align 3 ++ b _do_irq /* Current EL IRQ Handler */ ++ ++ .align 3 ++ b _do_fiq /* Current EL FIQ Handler */ ++ ++ .align 3 ++ b _do_error /* Current EL Error Handler */ ++ ++ ++_do_bad_sync: ++ exception_entry ++ bl do_bad_sync ++ b exception_exit ++ ++_do_bad_irq: ++ exception_entry ++ bl do_bad_irq ++ b exception_exit ++ ++_do_bad_fiq: ++ exception_entry ++ bl do_bad_fiq ++ b exception_exit ++ ++_do_bad_error: ++ exception_entry ++ bl do_bad_error ++ b exception_exit ++ ++_do_sync: ++ exception_entry ++ bl do_sync ++ b exception_exit ++ ++_do_irq: ++ exception_entry ++ bl do_irq ++ b exception_exit ++ ++_do_fiq: ++ exception_entry ++ bl do_fiq ++ b exception_exit ++ ++_do_error: ++ exception_entry ++ bl do_error ++ b exception_exit ++ ++exception_exit: ++ ldp x2, x0, [sp],#16 ++ switch_el x11, 3f, 2f, 1f ++3: msr elr_el3, x2 ++ b 0f ++2: msr elr_el2, x2 ++ b 0f ++1: msr elr_el1, x2 ++0: ++ ldp x1, x2, [sp],#16 ++ ldp x3, x4, [sp],#16 ++ ldp x5, x6, [sp],#16 ++ ldp x7, x8, [sp],#16 ++ ldp x9, x10, [sp],#16 ++ ldp x11, x12, [sp],#16 ++ ldp x13, x14, [sp],#16 ++ ldp x15, x16, [sp],#16 ++ ldp x17, x18, [sp],#16 ++ ldp x19, x20, [sp],#16 ++ ldp x21, x22, [sp],#16 ++ ldp x23, x24, [sp],#16 ++ ldp x25, x26, [sp],#16 ++ ldp x27, x28, [sp],#16 ++ ldp x29, x30, [sp],#16 ++ eret ++ ++/* ++ * void __asm_invalidate_icache_all(void) ++ * ++ * invalidate all tlb entries. ++ */ ++ENTRY(__asm_invalidate_icache_all) ++ ic ialluis ++ isb sy ++ ret ++ENDPROC(__asm_invalidate_icache_all) ++ ++.align 3 ++Str_SystemSartup: ++.ascii "\r\n\r\nSystem startup\r\n\0" +diff --git a/arch/arm/cpu/armv8/ss928v100/uart.S b/arch/arm/cpu/armv8/ss928v100/uart.S +new file mode 100644 +index 0000000..e2d4b82 +--- /dev/null ++++ b/arch/arm/cpu/armv8/ss928v100/uart.S +@@ -0,0 +1,116 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++ ++#include ++ ++//****************************************************************************** ++// ++// void uart_early_init(void); ++// ++.text ++.align 4 ++.global uart_early_init ++.type uart_early_init, %function ++uart_early_init: ++#ifndef CONFIG_BSP_DISABLE_CONSOLE ++ ldr x4, io_base_addr_L0 ++ ldr w3, =0x1101 ++ str w3, [x4, #0X124] ++ ldr w3, =0x1011 ++ str w3, [x4, #0X128] ++ ++ ldr x4, uart_base_addr_L0 ++ mov w3, #0 ++ /* Disable UART */ ++ str w3, [x4, #48] ++ /* Set baud rate to 115200, uart clock:24M */ ++ add w3, w3, #13 ++ str w3, [x4, #36] ++ mov w3, #1 ++ str w3, [x4, #40] ++ /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled. */ ++ ldr w3, =112 ++ str w3, [x4, #44] ++ /* Enable UART */ ++ ldr w3, =769 ++ str w3, [x4, #48] ++#endif ++ ret ++.align 4 ++uart_base_addr_L0: ++ .quad CONFIG_CUR_UART_BASE ++io_base_addr_L0: ++ .quad 0x102f0000 ++ ++//****************************************************************************** ++// ++// void uart_early_puts(const char *ss); ++// ++.align 4 ++.global uart_early_puts ++.type uart_early_puts, %function ++uart_early_puts: ++#ifndef CONFIG_BSP_DISABLE_CONSOLE ++#if !defined(CONFIG_SUPPORT_CA_RELEASE) ++ ldr x2, uart_base_addr_L1 ++ b next_char ++output: ++ ldr w4, [x2, #24] ++ tst w4, #32 ++ bne output ++ str w3, [x2, #0] ++ add x0, x0, #1 ++next_char: ++ ldrb w3, [x0] ++ cmp w3, #0 ++ bne output ++#endif /* CONFIG_SUPPORT_CA_RELEASE */ ++#endif /* CONFIG_BSP_DISABLE_CONSOLE */ ++ ret ++.align 4 ++uart_base_addr_L1: ++ .quad CONFIG_CUR_UART_BASE ++ ++//****************************************************************************** ++// ++// void uart_early_putc(int chr); ++// ++// call example: ++// mov w0, #'A' ++// bl uart_early_putc ++// ++.align 4 ++.global uart_early_putc ++.type uart_early_putc, %function ++uart_early_putc: ++#ifndef CONFIG_BSP_DISABLE_CONSOLE ++#if !defined(CONFIG_SUPPORT_CA_RELEASE) ++ ldr x2, uart_base_addr_L3 ++wait3: ++ ldr w4, [x2, #24] ++ tst w4, #32 ++ bne wait3 ++ str w0, [x2, #0] ++ ++#endif /* CONFIG_SUPPORT_CA_RELEASE */ ++#endif /* CONFIG_BSP_DISABLE_CONSOLE */ ++ ret ++.align 4 ++uart_base_addr_L3: ++ .quad CONFIG_CUR_UART_BASE +diff --git a/arch/arm/include/asm/arch-ss927v100/mmu.h b/arch/arm/include/asm/arch-ss927v100/mmu.h +new file mode 100644 +index 0000000..54debdf +--- /dev/null ++++ b/arch/arm/include/asm/arch-ss927v100/mmu.h +@@ -0,0 +1,214 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef __ARM_ARCH_MMU_H ++#define __ARM_ARCH_MMU_H ++ ++#include ++#ifdef CONFIG_ARCH_MMU ++/* ++ * Translation Table Base Bit Masks ++ */ ++#define ARM_TRANSLATION_TABLE_MASK 0xFFFFC000 ++ ++/* ++ * Domain Access Control Bit Masks ++ */ ++#define arm_access_type_no_access(domain_num) (0x0 << (domain_num)*2) ++#define arm_access_type_client(domain_num) (0x1 << (domain_num)*2) ++#define arm_access_type_manager(domain_num) (0x3 << (domain_num)*2) ++ ++ ++#define ARM_MMU_FIRST_LEVEL_FAULT_ID 0x0 ++ ++struct arm_mmu_first_level_fault { ++ unsigned int id: 2; ++ unsigned int sbz: 30; ++}; ++ ++#define ARM_MMU_FIRST_LEVEL_PAGE_TABLE_ID 0x1 ++ ++struct arm_mmu_first_level_page_table { ++ unsigned int id: 2; ++ unsigned int sbz0: 1; ++ unsigned int ns: 1; ++ unsigned int sbz1: 1; ++ unsigned int domain: 4; ++ unsigned int imp: 1; ++ unsigned int base_address: 22; ++}; ++ ++#define ARM_MMU_FIRST_LEVEL_SECTION_ID 0x2 ++ ++struct arm_mmu_first_level_section { ++ unsigned int id: 2; ++ unsigned int b: 1; ++ unsigned int c: 1; ++ unsigned int xn: 1; ++ unsigned int domain: 4; ++ unsigned int imp: 1; ++ unsigned int ap0: 2; ++ unsigned int tex: 3; ++ unsigned int ap1: 1; ++ unsigned int s: 1; ++ unsigned int ng: 1; ++ unsigned int reserved: 1; ++ unsigned int ns: 1; ++ unsigned int base_address: 12; ++}; ++ ++struct arm_mmu_first_level_reserved { ++ unsigned int id: 2; ++ unsigned int sbz: 30; ++}; ++ ++#define ARM_MMU_SECOND_LEVEL_FAULT_ID 0x0 ++ ++struct arm_mmu_second_level_fault { ++ unsigned int id: 2; ++ unsigned int sbz: 30; ++}; ++ ++#define ARM_MMU_SECOND_LEVEL_SMALL_ID 0x2 ++ ++struct arm_mmu_second_level_small { ++ unsigned int id: 2; ++ unsigned int b: 1; ++ unsigned int c: 1; ++ unsigned int ap0: 2; ++ unsigned int tex: 3; ++ unsigned int ap1: 1; ++ unsigned int s: 1; ++ unsigned int ng: 1; ++ unsigned int base_address: 20; ++}; ++ ++#define ARM_MMU_FIRST_LEVEL_RESERVED_ID 0x3 ++ ++#define arm_mmu_first_level_descriptor_address(ttb_base, table_index) \ ++ (unsigned long *)((unsigned long)(ttb_base) + ((table_index) << 2)) ++ ++#define ARM_FIRST_LEVEL_PAGE_TABLE_SIZE 0x4000 ++ ++union arm_mmu_first_level_descriptor { ++ unsigned long word; ++ struct arm_mmu_first_level_fault fault; ++ struct arm_mmu_first_level_page_table page_table; ++ struct arm_mmu_first_level_section section; ++ struct arm_mmu_first_level_reserved reserved; ++}; ++ ++union arm_mmu_second_level_descriptor { ++ unsigned long word; ++ struct arm_mmu_second_level_fault fault; ++ struct arm_mmu_second_level_small small; ++}; ++ ++static inline void arm_mmu_section(int ttb_base, int actual_base, ++ int virtual_base, unsigned int tex, unsigned int cacheable, ++ unsigned int bufferable, unsigned int perm, ++ unsigned int shareable) ++{ ++ register union arm_mmu_first_level_descriptor desc; ++ ++ desc.word = 0; ++ desc.section.id = ARM_MMU_FIRST_LEVEL_SECTION_ID; ++ desc.section.c = cacheable; ++ desc.section.b = bufferable; ++ desc.section.xn = 0; ++ desc.section.domain = 0; ++ desc.section.ap0 = perm; ++ desc.section.tex = tex; ++ desc.section.ap1 = 0; ++ desc.section.s = shareable; ++ desc.section.base_address = actual_base; ++ *arm_mmu_first_level_descriptor_address(ttb_base, (virtual_base)) ++ = desc.word; ++} ++ ++static inline void x_arm_mmu_section(int abase, int vbase, int size, ++ unsigned int tex, unsigned int cache, unsigned int buff, ++ unsigned int access, unsigned int shareable) ++{ ++ int i; ++ int j = abase; ++ int k = vbase; ++ unsigned long ttb_base = CONFIG_TTB_ADDR; ++ ++ for (i = size; i > 0 ; i--, j++, k++) ++ arm_mmu_section(ttb_base, j, k, tex, cache, ++ buff, access, shareable); ++} ++ ++#define ARM_UNCACHEABLE 0 ++#define ARM_CACHEABLE 1 ++#define ARM_UNBUFFERABLE 0 ++#define ARM_BUFFERABLE 1 ++ ++#define ARM_ACCESS_PERM_NONE_NONE 0 ++#define ARM_ACCESS_PERM_RW_NONE 1 ++#define ARM_ACCESS_PERM_RW_RO 2 ++#define ARM_ACCESS_PERM_RW_RW 3 ++ ++#define ARM_NOSHAREABLE 0 ++#define ARM_SHAREABLE 1 ++ ++#define ARM_MEMTYPE_STRONGORDER 0 ++#define ARM_MEMTYPE_DEVICE 1 ++#define ARM_MEMTYPE_NORMAL 2 ++#define ARM_MEMTYPE_RESERVED 3 ++ ++ ++#define ARM_CACHETYPE_NOCACHE 0 ++#define ARM_CACHETYPE_WRITEBACK 1 ++#define ARM_CACHETYPE_WRITETHROUGH 2 ++#define ARM_CACHETYPE_WRITEBACK_ONLY 3 ++ ++ ++/* ++ * Initialization for the Domain Access Control Register ++ */ ++#define ARM_ACCESS_DACR_DEFAULT ( \ ++ arm_access_type_manager(0) | \ ++ arm_access_type_no_access(1) | \ ++ arm_access_type_no_access(2) | \ ++ arm_access_type_no_access(3) | \ ++ arm_access_type_no_access(4) | \ ++ arm_access_type_no_access(5) | \ ++ arm_access_type_no_access(6) | \ ++ arm_access_type_no_access(7) | \ ++ arm_access_type_no_access(8) | \ ++ arm_access_type_no_access(9) | \ ++ arm_access_type_no_access(10) | \ ++ arm_access_type_no_access(11) | \ ++ arm_access_type_no_access(12) | \ ++ arm_access_type_no_access(13) | \ ++ arm_access_type_no_access(14) | \ ++ arm_access_type_no_access(15)) ++ ++#endif ++#endif +diff --git a/arch/arm/include/asm/arch-ss927v100/platform.h b/arch/arm/include/asm/arch-ss927v100/platform.h +new file mode 100644 +index 0000000..aa0dad1 +--- /dev/null ++++ b/arch/arm/include/asm/arch-ss927v100/platform.h +@@ -0,0 +1,447 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __CHIP_REGS_H__ ++#define __CHIP_REGS_H__ ++ ++/* -------------------------------------------------------------------- */ ++/* Communication Register and flag used by bootrom */ ++/* -------------------------------------------------------------------- */ ++#define REG_START_FLAG (SYS_CTRL_REG_BASE + REG_SC_GEN1) ++#define START_MAGIC (0x444f574e) ++#define SELF_BOOT_TYPE_USBDEV 0x2 ++#define CP_STEP1_ADDR 0x04010a00 ++#define PCIE_SLAVE_BOOT_CTL_REG 0x0134 ++#define DDR_INIT_DOWNLOAD_OK_FLAG 0xDCDFF001 /* step1:Ddrinit Code Download Finished Flag: DCDFF001 */ ++#define DDR_INIT_EXCUTE_OK_FLAG 0xDCEFF002 /* step2:Ddrinit Code Excute Finished Flag: DCEFF002 */ ++#define UBOOT_DOWNLOAD_OK_FLAG 0xBCDFF003 /* step3:Boot Code Download Finished Flag: BCDFF003 */ ++ ++/* -------------------------------------------------------------------- */ ++/* System Control */ ++/* -------------------------------------------------------------------- */ ++#define SYS_CTRL_REG_BASE 0x11020000 ++#define REG_BASE_SCTL SYS_CTRL_REG_BASE ++#define REG_SC_CTRL 0x0000 ++#define REG_SC_SYSRES 0x0004 ++#define REG_PERISTAT 0x0030 ++#define REG_SYSSTAT 0x0018 ++#define REG_DATA_CHANNEL_TYPE 0x31c ++#define EMMC_BOOT_8BIT (0x1 << 11) ++#define REG_PERI_EMMC_STAT 0x0404 ++#define mmc_boot_clk_sel(val) ((val) & 0x3) ++#define MMC_BOOT_CLK_50M 0x2 ++#define EMMC_NORMAL_MODE (0x1 << 3) ++#define get_spi_nor_addr_mode(_reg) (((_reg) >> 11) & 0x1) ++#define get_spi_device_type(_reg) (((_reg) >> 2) & 0x1) ++#define get_sys_boot_mode(_reg) (((_reg) >> 2) & 0x3) ++#define BOOT_FROM_SPI 0 ++#define BOOT_FROM_SPI_NAND 1 ++#define BOOT_FROM_NAND 2 ++#define BOOT_FROM_EMMC 3 ++ ++#define SEC_UBOOT_DATA_ADDR 0x41000000 ++#define OTP_USER_LOCKABLE14 0x10122090 ++#define get_sec_boot_mode(x) ((((x) & 0xf) == 0x5) ? 0 : 1) ++ ++#define REG_SC_GEN1 0x013c ++#define REG_SC_GEN2 0x0140 ++#define REG_SC_GEN3 0x0144 ++#define REG_SC_GEN4 0x0148 ++#define REG_SC_GEN9 0x0154 ++#define REG_SC_GEN20 0x0090 ++ ++/* -------------------------------------------------------------------- */ ++/* CPU SUBSYS */ ++/* -------------------------------------------------------------------- */ ++#define REG_CRG_CLUSTER0_CLK_RST 0x0190 /* CPU SUBSYS clock and reset control*/ ++#define CLUSTER0_GLB_SRST_REQ (0x1 << 17) ++ ++#define REG_PERI_CPU_RVBARADDR_SOC 0x12030020 ++ ++#define REG_CRG_CLUSTER1_CLK_RST 0x0194 ++#define CLUSTER1_GLB_SRST_REQ (0x1 << 9) ++#define CLUSTER1_GLB_CKEN (0x1 << 8) ++ ++/* -------------------------------------------------------------------- */ ++/* CRG */ ++/* -------------------------------------------------------------------- */ ++#define CRG_REG_BASE 0x11010000 ++ ++/* -------------------------------------------------------------------- */ ++/* Peripheral Control REG */ ++/* -------------------------------------------------------------------- */ ++#define MISC_REG_BASE 0x11024000 ++ ++/* -------------------------------------------------------------------- */ ++/* IO configuration REG:mux and driver */ ++/* -------------------------------------------------------------------- */ ++#define AHB_IO_CONFIG_REG_BASE 0x11180000 ++ ++/* -------------------------------------------------------------------- */ ++/* TIMER */ ++/* -------------------------------------------------------------------- */ ++#define TIMER0_REG_BASE 0x11000000 ++#define REG_TIMER_RELOAD 0x0 ++#define REG_TIMER_VALUE 0x4 ++#define REG_TIMER_CONTROL 0x8 ++#define CFG_TIMER_CLK (3000000) ++#define CFG_TIMERBASE TIMER0_REG_BASE ++/* enable timer.32bit, periodic,mask irq, 1 divider.*/ ++#define CFG_TIMER_CTRL 0xC2 ++ ++/* -------------------------------------------------------------------- */ ++/* UART */ ++/* -------------------------------------------------------------------- */ ++#define UART0_REG_BASE 0x11040000 ++#define UART1_REG_BASE 0x11041000 ++#define UART2_REG_BASE 0x11042000 ++#define UART3_REG_BASE 0x11043000 ++ ++/* -------------------------------------------------------------------- */ ++/* DDRC */ ++/* -------------------------------------------------------------------- */ ++#define STACK_TRAINING 0x0402a000 ++#define DDRC0_REG_BASE 0x11140000 ++#define DDR_MEM_BASE 0x40000000 ++ ++/* -------------------------------------------------------------------- */ ++/* FMC */ ++/* -------------------------------------------------------------------- */ ++#define FMC_REG_BASE 0x10000000 ++#define FMC_MEM_BASE 0x0f000000 ++ ++/* FMC CRG register offset */ ++#define FMC_CLK_REG 0x3F40 ++#define REG_FMC_CRG FMC_CLK_REG ++ ++#define fmc_clk_sel(_clk) (((_clk) & 0x7) << 12) ++#define FMC_CLK_SEL_MASK (0x7 << 12) ++#define get_fmc_clk_type(_reg) (((_reg) >> 12) & 0x7) ++ ++/* SDR/DDR clock */ ++#define FMC_CLK_24M 0 ++#define FMC_CLK_100M 1 ++#define FMC_CLK_150M 2 ++#define FMC_CLK_200M 3 ++/* Only DDR clock */ ++#define FMC_CLK_250M 4 ++#define FMC_CLK_300M 5 ++#define FMC_CLK_400M 6 ++ ++#define FMC_CLK_ENABLE BIT(4) ++#define FMC_SOFT_RST_REQ BIT(0) ++ ++/*--------------------------------------------------------------------- */ ++/* EMMC / SD */ ++/* -------------------------------------------------------------------- */ ++ ++/* eMMC CRG register offset */ ++#define REG_EMMC_CRG (CRG_REG_BASE + 0x34c0) ++#define mmc_clk_sel(_clk) (((_clk) & 0x7) << 24) ++#define MMC_CLK_SEL_MASK (0x7 << 24) ++#define REG_SAVE_HCS 0x11020300 ++ ++/* EMMC REG*/ ++#define EMMC_BASE_REG 0x10020000 ++#define NO_EMMC_PHY 1 ++ ++#define NF_BOOTBW_MASK (1 << 11) ++#define REG_BASE_PERI_CTRL REG_BASE_SCTL ++#define REG_BASE_IO_CONFIG IO_CONFIG_REG_BASE ++ ++/* SDIO0 REG */ ++#define REG_SDIO0_CRG (CRG_REG_BASE + 0x35c0) ++#define SDIO0_BASE_REG 0x10030000 ++ ++/*--------------------------------------------------------------------- */ ++/* GMAC */ ++/* -------------------------------------------------------------------- */ ++#define GMAC0_IOBASE 0x10290000 ++#define GMAC1_IOBASE 0x102A0000 ++ ++/* Ethernet MAC0 MAC_IF CRG register offset */ ++#define REG_ETH0_MACIF_CRG 0x37c0 ++/* Ethernet MAC0 MAC_If CRG register bit map*/ ++#define BIT_MACIF0_RST BIT(0) ++#define BIT_GMACIF0_CLK_EN BIT(4) ++#define BIT_RMII0_CLKSEL_PAD BIT(12) ++ ++/* Ethernet MAC0 GSF CRG register offset */ ++#define REG_ETH0_GSF_CRG 0x37c4 ++/* Ethernet MAC0 GSF CRG register bit map*/ ++#define BIT_GMAC0_RST BIT(0) ++#define BIT_GMAC0_CLK_EN BIT(4) ++ ++/* Ethernet MAC0 PHY CRG register offset */ ++#define REG_ETH0_PHY_CRG 0x37cc ++/* Ethernet MAC0 PHY CRG register bit map*/ ++#define BIT_EXT_PHY0_RST BIT(0) ++#define BIT_EXT_PHY0_CLK_SELECT BIT(12) ++ ++ ++/* Ethernet MAC1 MAC_IF CRG register offset */ ++#define REG_ETH1_MACIF_CRG 0x3800 ++/* Ethernet MAC1 MAC_If CRG register bit map*/ ++#define BIT_MACIF1_RST BIT(0) ++#define BIT_GMACIF1_CLK_EN BIT(4) ++#define BIT_RMII1_CLKSEL_PAD BIT(12) ++ ++/* Ethernet MAC1 GSF CRG register offset */ ++#define REG_ETH1_GSF_CRG 0x3804 ++/* Ethernet MAC1 GSF CRG register bit map*/ ++#define BIT_GMAC1_RST BIT(0) ++#define BIT_GMAC1_CLK_EN BIT(4) ++ ++/* Ethernet MAC1 PHY CRG register offset */ ++#define REG_ETH1_PHY_CRG 0x380c ++/* Ethernet MAC1 PHY CRG register bit map*/ ++#define BIT_EXT_PHY1_RST BIT(0) ++#define BIT_EXT_PHY1_CLK_SELECT BIT(12) ++ ++#define PHY0_CLK_25M 0 ++#define PHY0_CLK_50M BIT_EXT_PHY0_CLK_SELECT ++#define PHY1_CLK_25M 0 ++#define PHY1_CLK_50M BIT_EXT_PHY1_CLK_SELECT ++ ++#define GMAC_MACIF0_CTRL (GMAC0_IOBASE + 0x300c) ++#define GMAC_MACIF1_CTRL (GMAC1_IOBASE + 0x300c) ++#define GMAC_DUAL_MAC_CRF_ACK_TH (GMAC0_IOBASE + 0x3004) ++ ++/* Configure gmac pinctrl parameters in software */ ++#define CFG_NET_PINCTRL ++ ++/* MDIO0 pinctrl phyical addr */ ++#define PHY_ADDR_MDCK0 0x0102F0038 ++#define PHY_ADDR_MDIO0 0x0102F003C ++/* MDIO1 pinctrl phyical addr */ ++#define PHY_ADDR_MDCK1 0x0102F0068 ++#define PHY_ADDR_MDIO1 0x0102F006C ++ ++/* PHY0 pinctrl phyical addr */ ++#define PHY_ADDR_EPHY0_CLK 0x0102F0030 ++#define PHY_ADDR_EPHY0_RSTN 0x0102F0034 ++/* PHY1 pinctrl phyical addr */ ++#define PHY_ADDR_EPHY1_CLK 0x0102F005C ++#define PHY_ADDR_EPHY1_RSTN 0x0102F0064 ++ ++/* RGMII0 pinctrl phyical addr */ ++#define PHY_ADDR_RGMII0_TXCKOUT 0x0102F002C ++#define PHY_ADDR_RGMII0_TXD0 0x0102F0024 ++#define PHY_ADDR_RGMII0_TXD1 0x0102F0020 ++#define PHY_ADDR_RGMII0_TXD2 0x0102F001C ++#define PHY_ADDR_RGMII0_TXD3 0x0102F0018 ++#define PHY_ADDR_RGMII0_TXEN 0x0102F0028 ++#define PHY_ADDR_RGMII0_RXCK 0x0102F0014 ++#define PHY_ADDR_RGMII0_RXD0 0x0102F000C ++#define PHY_ADDR_RGMII0_RXD1 0x0102F0008 ++#define PHY_ADDR_RGMII0_RXD2 0x0102F0004 ++#define PHY_ADDR_RGMII0_RXD3 0x0102F0000 ++#define PHY_ADDR_RGMII0_RXDV 0x0102F0010 ++/* RGMII1 pinctrl phyical addr */ ++#define PHY_ADDR_RGMII1_TXCKOUT 0x0102F0050 ++#define PHY_ADDR_RGMII1_TXD0 0x0102F0040 ++#define PHY_ADDR_RGMII1_TXD1 0x0102F0044 ++#define PHY_ADDR_RGMII1_TXD2 0x0102F0054 ++#define PHY_ADDR_RGMII1_TXD3 0x0102F004C ++#define PHY_ADDR_RGMII1_TXEN 0x0102F0058 ++#define PHY_ADDR_RGMII1_RXCK 0x0102F0074 ++#define PHY_ADDR_RGMII1_RXD0 0x0102F0070 ++#define PHY_ADDR_RGMII1_RXD1 0x0102F0078 ++#define PHY_ADDR_RGMII1_RXD2 0x0102F007C ++#define PHY_ADDR_RGMII1_RXD3 0x0102F0060 ++#define PHY_ADDR_RGMII1_RXDV 0x0102F0048 ++ ++/* RMII0 pinctrl phyical addr */ ++#define PHY_ADDR_RMII0_CLK 0x0102F002C ++#define PHY_ADDR_RMII0_TXD0 0x0102F0024 ++#define PHY_ADDR_RMII0_TXD1 0x0102F0020 ++#define PHY_ADDR_RMII0_TXEN 0x0102F0028 ++#define PHY_ADDR_RMII0_RXD0 0x0102F000C ++#define PHY_ADDR_RMII0_RXD1 0x0102F0008 ++#define PHY_ADDR_RMII0_RXDV 0x0102F0010 ++ ++/* RMII1 pinctrl phyical addr */ ++#define PHY_ADDR_RMII1_CLK 0x0102F0050 ++#define PHY_ADDR_RMII1_TXD0 0x0102F0040 ++#define PHY_ADDR_RMII1_TXD1 0x0102F0044 ++#define PHY_ADDR_RMII1_TXEN 0x0102F0058 ++#define PHY_ADDR_RMII1_RXD0 0x0102F0070 ++#define PHY_ADDR_RMII1_RXD1 0x0102F0078 ++#define PHY_ADDR_RMII1_RXDV 0x0102F0048 ++ ++/* MDIO0 config value */ ++#define VALUE_MDCK0 0x1231 ++#define VALUE_MDIO0 0x1221 ++/* MDIO1 config value */ ++#define VALUE_MDCK1 0x0032 ++#define VALUE_MDIO1 0x0032 ++ ++/* PHY0 config value */ ++#define VALUE_EPHY0_CLK 0x1261 ++#define VALUE_EPHY0_RSTN 0x1201 ++/* PHY1 config value */ ++#define VALUE_EPHY1_CLK 0x0062 ++#define VALUE_EPHY1_RSTN 0x00F2 ++ ++/* RGMII0 config value */ ++#define VALUE_RGMII0_TXCKOUT 0x1261 ++#define VALUE_RGMII0_TXD0 0x1251 ++#define VALUE_RGMII0_TXD1 0x1251 ++#define VALUE_RGMII0_TXD2 0x1251 ++#define VALUE_RGMII0_TXD3 0x1261 ++#define VALUE_RGMII0_TXEN 0x1251 ++#define VALUE_RGMII0_RXCK 0x1271 ++#define VALUE_RGMII0_RXD0 0x1271 ++#define VALUE_RGMII0_RXD1 0x1271 ++#define VALUE_RGMII0_RXD2 0x1271 ++#define VALUE_RGMII0_RXD3 0x1271 ++#define VALUE_RGMII0_RXDV 0x1271 ++/* RGMII1 config value */ ++#define VALUE_RGMII1_TXCKOUT 0x00A2 ++#define VALUE_RGMII1_TXD0 0x0062 ++#define VALUE_RGMII1_TXD1 0x0062 ++#define VALUE_RGMII1_TXD2 0x0062 ++#define VALUE_RGMII1_TXD3 0x0062 ++#define VALUE_RGMII1_TXEN 0x0062 ++#define VALUE_RGMII1_RXCK 0x0072 ++#define VALUE_RGMII1_RXD0 0x0072 ++#define VALUE_RGMII1_RXD1 0x0072 ++#define VALUE_RGMII1_RXD2 0x0072 ++#define VALUE_RGMII1_RXD3 0x0072 ++#define VALUE_RGMII1_RXDV 0x0072 ++ ++/* RMII0 config value */ ++#define VALUE_RMII0_CLK 0x1262 ++#define VALUE_RMII0_TXD0 0x1251 ++#define VALUE_RMII0_TXD1 0x1251 ++#define VALUE_RMII0_TXEN 0x1251 ++#define VALUE_RMII0_RXD0 0x1271 ++#define VALUE_RMII0_RXD1 0x1271 ++#define VALUE_RMII0_RXDV 0x1271 ++/* RMII1 config value */ ++#define VALUE_RMII1_CLK 0x1063 ++#define VALUE_RMII1_TXD0 0x1242 ++#define VALUE_RMII1_TXD1 0x1242 ++#define VALUE_RMII1_TXEN 0x1242 ++#define VALUE_RMII1_RXD0 0x12F2 ++#define VALUE_RMII1_RXD1 0x12F2 ++#define VALUE_RMII1_RXDV 0x12F2 ++ ++/* -------------------------------------------------------------------- */ ++/* USB */ ++/* -------------------------------------------------------------------- */ ++#define USB3_CTRL_REG_BASE 0x10320000 ++#define USB3_CTRL_REG_BASE_1 0x10300000 ++ ++/* USB CRG register offset and config */ ++#define USB3_CTRL_CRG (CRG_REG_BASE + 0x3960) ++#define USB3_CTRL_CRG_1 (CRG_REG_BASE + 0x3940) ++#define USB2_PHY_CRG (CRG_REG_BASE + 0x38e0) ++#define USB2_PHY_CRG_1 (CRG_REG_BASE + 0x38c0) ++#define USB3_PHY_CRG (CRG_REG_BASE + 0x3964) ++#define USB3_PHY_CRG_1 (CRG_REG_BASE + 0x3944) ++ ++#define USB3_CTRL_CRG_DEFAULT_VALUE 0x30001 ++#define USB2_PHY_CRG_DEFAULT_VALUE 0x57 ++#define USB3_PHY_CRG_DEFAULT_VALUE 0x13 ++ ++#define USB3_CRG_PCLK_OCC_SEL (0x1 << 18) ++#define USB3_CRG_PIPE_CKEN (0x1 << 12) ++#define USB3_CRG_UTMI_CKEN (0x1 << 8) ++#define USB3_CRG_SUSPEND_CKEN (0x1 << 6) ++#define USB3_CRG_REF_CKEN (0x1 << 5) ++#define USB3_CRG_BUS_CKEN (0x1 << 4) ++#define USB3_CRG_SRST_REQ (0x1 << 0) ++ ++#define USB2_PHY_CRG_APB_SREQ (0x1 << 2) ++#define USB2_PHY_CRG_TREQ (0x1 << 1) ++#define USB2_PHY_CRG_REQ (0x1 << 0) ++ ++#define USB3_PHY_CRG_TREQ (0x1 << 1) ++#define USB3_PHY_CRG_REQ (0x1 << 0) ++ ++#define COMBPHY_REF_CKEN (0x1<<24) ++#define COMBPHY_SRST_REQ (0x1<<16) ++ ++#define USB3_VCC_SRST_REQ (0x1<<0) ++#define USB3_UTMI_CKSEL (0x1<<13) ++#define USB3_PCLK_OCC_SEL (0x1<<14) ++ ++/* USB CTRL register offset and config */ ++#define GTXTHRCFG 0xc108 ++#define GRXTHRCFG 0xc10c ++#define REG_GCTL 0xc110 ++ ++#define USB_TXPKT_CNT_SEL (0x1 << 29) ++#define USB_TXPKT_CNT (0x11 << 24) ++#define USB_MAXTX_BURST_SIZE (0x1 << 20) ++#define CLEAN_USB3_GTXTHRCFG 0x0 ++ ++#define REG_GUSB3PIPECTL0 0xc2c0 ++#define PCS_SSP_SOFT_RESET (0x1 << 31) ++#define SUSPEND_ENABLE (0x1 <<17) ++ ++#define PORT_CAP_DIR_MASK (0x3 << 12) ++#define PORT_CAP_DIR_HOST (0X1 << 12) ++ ++/* USB PHY register EYE diagram para offset and config */ ++#define USB2_PHY_BASE 0x10330000 ++#define USB2_PHY_BASE_1 0x10310000 ++ ++/* -------------------------------------------------------------------- */ ++/* PCIE */ ++/* -------------------------------------------------------------------- */ ++#define SYS_SATA 0x8c ++#define PCIE_MODE 12 ++ ++#define PERI_CRG98 0x188 ++#define PHY0_SRS_REQ 0 ++#define PHY0_SRS_REQ_SEL 1 ++#define PHY1_SRS_REQ 16 ++#define PHY1_SRS_REQ_SEL 17 ++ ++#define MISC_CTRL5 0x14 ++#define NUM_0 0 ++#define NUM_1 1 ++#define NUM_2 2 ++#define NUM_3 3 ++#define NUM_4 4 ++#define NUM_5 5 ++#define NUM_6 6 ++#define NUM_7 7 ++#define NUM_8 8 ++#define NUM_9 9 ++#define NUM_10 10 ++ ++/*----------------------------------------------------------------------------------- ++ * boot sel type ++ *-----------------------------------------------------------------------------------*/ ++#define BOOT_SEL_PCIE 0x965a4b87 ++#define BOOT_SEL_UART 0x69a5b478 ++#define BOOT_SEL_USB 0x965ab487 ++#define BOOT_SEL_SDIO 0x69a54b87 ++#define BOOT_SEL_FLASH 0x96a54b78 ++#define BOOT_SEL_EMMC 0x695ab487 ++#define BOOT_SEL_UNKNOW 0x965a4b78 ++ ++/* -------------------------------------------------------------------- */ ++/* GZIP */ ++/* -------------------------------------------------------------------- */ ++#define HW_DEC_INTR 97 ++#endif /* End of __CHIP_REGS_H__ */ +diff --git a/arch/arm/include/asm/arch-ss928v100/mmu.h b/arch/arm/include/asm/arch-ss928v100/mmu.h +new file mode 100644 +index 0000000..54debdf +--- /dev/null ++++ b/arch/arm/include/asm/arch-ss928v100/mmu.h +@@ -0,0 +1,214 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++/* ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef __ARM_ARCH_MMU_H ++#define __ARM_ARCH_MMU_H ++ ++#include ++#ifdef CONFIG_ARCH_MMU ++/* ++ * Translation Table Base Bit Masks ++ */ ++#define ARM_TRANSLATION_TABLE_MASK 0xFFFFC000 ++ ++/* ++ * Domain Access Control Bit Masks ++ */ ++#define arm_access_type_no_access(domain_num) (0x0 << (domain_num)*2) ++#define arm_access_type_client(domain_num) (0x1 << (domain_num)*2) ++#define arm_access_type_manager(domain_num) (0x3 << (domain_num)*2) ++ ++ ++#define ARM_MMU_FIRST_LEVEL_FAULT_ID 0x0 ++ ++struct arm_mmu_first_level_fault { ++ unsigned int id: 2; ++ unsigned int sbz: 30; ++}; ++ ++#define ARM_MMU_FIRST_LEVEL_PAGE_TABLE_ID 0x1 ++ ++struct arm_mmu_first_level_page_table { ++ unsigned int id: 2; ++ unsigned int sbz0: 1; ++ unsigned int ns: 1; ++ unsigned int sbz1: 1; ++ unsigned int domain: 4; ++ unsigned int imp: 1; ++ unsigned int base_address: 22; ++}; ++ ++#define ARM_MMU_FIRST_LEVEL_SECTION_ID 0x2 ++ ++struct arm_mmu_first_level_section { ++ unsigned int id: 2; ++ unsigned int b: 1; ++ unsigned int c: 1; ++ unsigned int xn: 1; ++ unsigned int domain: 4; ++ unsigned int imp: 1; ++ unsigned int ap0: 2; ++ unsigned int tex: 3; ++ unsigned int ap1: 1; ++ unsigned int s: 1; ++ unsigned int ng: 1; ++ unsigned int reserved: 1; ++ unsigned int ns: 1; ++ unsigned int base_address: 12; ++}; ++ ++struct arm_mmu_first_level_reserved { ++ unsigned int id: 2; ++ unsigned int sbz: 30; ++}; ++ ++#define ARM_MMU_SECOND_LEVEL_FAULT_ID 0x0 ++ ++struct arm_mmu_second_level_fault { ++ unsigned int id: 2; ++ unsigned int sbz: 30; ++}; ++ ++#define ARM_MMU_SECOND_LEVEL_SMALL_ID 0x2 ++ ++struct arm_mmu_second_level_small { ++ unsigned int id: 2; ++ unsigned int b: 1; ++ unsigned int c: 1; ++ unsigned int ap0: 2; ++ unsigned int tex: 3; ++ unsigned int ap1: 1; ++ unsigned int s: 1; ++ unsigned int ng: 1; ++ unsigned int base_address: 20; ++}; ++ ++#define ARM_MMU_FIRST_LEVEL_RESERVED_ID 0x3 ++ ++#define arm_mmu_first_level_descriptor_address(ttb_base, table_index) \ ++ (unsigned long *)((unsigned long)(ttb_base) + ((table_index) << 2)) ++ ++#define ARM_FIRST_LEVEL_PAGE_TABLE_SIZE 0x4000 ++ ++union arm_mmu_first_level_descriptor { ++ unsigned long word; ++ struct arm_mmu_first_level_fault fault; ++ struct arm_mmu_first_level_page_table page_table; ++ struct arm_mmu_first_level_section section; ++ struct arm_mmu_first_level_reserved reserved; ++}; ++ ++union arm_mmu_second_level_descriptor { ++ unsigned long word; ++ struct arm_mmu_second_level_fault fault; ++ struct arm_mmu_second_level_small small; ++}; ++ ++static inline void arm_mmu_section(int ttb_base, int actual_base, ++ int virtual_base, unsigned int tex, unsigned int cacheable, ++ unsigned int bufferable, unsigned int perm, ++ unsigned int shareable) ++{ ++ register union arm_mmu_first_level_descriptor desc; ++ ++ desc.word = 0; ++ desc.section.id = ARM_MMU_FIRST_LEVEL_SECTION_ID; ++ desc.section.c = cacheable; ++ desc.section.b = bufferable; ++ desc.section.xn = 0; ++ desc.section.domain = 0; ++ desc.section.ap0 = perm; ++ desc.section.tex = tex; ++ desc.section.ap1 = 0; ++ desc.section.s = shareable; ++ desc.section.base_address = actual_base; ++ *arm_mmu_first_level_descriptor_address(ttb_base, (virtual_base)) ++ = desc.word; ++} ++ ++static inline void x_arm_mmu_section(int abase, int vbase, int size, ++ unsigned int tex, unsigned int cache, unsigned int buff, ++ unsigned int access, unsigned int shareable) ++{ ++ int i; ++ int j = abase; ++ int k = vbase; ++ unsigned long ttb_base = CONFIG_TTB_ADDR; ++ ++ for (i = size; i > 0 ; i--, j++, k++) ++ arm_mmu_section(ttb_base, j, k, tex, cache, ++ buff, access, shareable); ++} ++ ++#define ARM_UNCACHEABLE 0 ++#define ARM_CACHEABLE 1 ++#define ARM_UNBUFFERABLE 0 ++#define ARM_BUFFERABLE 1 ++ ++#define ARM_ACCESS_PERM_NONE_NONE 0 ++#define ARM_ACCESS_PERM_RW_NONE 1 ++#define ARM_ACCESS_PERM_RW_RO 2 ++#define ARM_ACCESS_PERM_RW_RW 3 ++ ++#define ARM_NOSHAREABLE 0 ++#define ARM_SHAREABLE 1 ++ ++#define ARM_MEMTYPE_STRONGORDER 0 ++#define ARM_MEMTYPE_DEVICE 1 ++#define ARM_MEMTYPE_NORMAL 2 ++#define ARM_MEMTYPE_RESERVED 3 ++ ++ ++#define ARM_CACHETYPE_NOCACHE 0 ++#define ARM_CACHETYPE_WRITEBACK 1 ++#define ARM_CACHETYPE_WRITETHROUGH 2 ++#define ARM_CACHETYPE_WRITEBACK_ONLY 3 ++ ++ ++/* ++ * Initialization for the Domain Access Control Register ++ */ ++#define ARM_ACCESS_DACR_DEFAULT ( \ ++ arm_access_type_manager(0) | \ ++ arm_access_type_no_access(1) | \ ++ arm_access_type_no_access(2) | \ ++ arm_access_type_no_access(3) | \ ++ arm_access_type_no_access(4) | \ ++ arm_access_type_no_access(5) | \ ++ arm_access_type_no_access(6) | \ ++ arm_access_type_no_access(7) | \ ++ arm_access_type_no_access(8) | \ ++ arm_access_type_no_access(9) | \ ++ arm_access_type_no_access(10) | \ ++ arm_access_type_no_access(11) | \ ++ arm_access_type_no_access(12) | \ ++ arm_access_type_no_access(13) | \ ++ arm_access_type_no_access(14) | \ ++ arm_access_type_no_access(15)) ++ ++#endif ++#endif +diff --git a/arch/arm/include/asm/arch-ss928v100/platform.h b/arch/arm/include/asm/arch-ss928v100/platform.h +new file mode 100644 +index 0000000..1a49022 +--- /dev/null ++++ b/arch/arm/include/asm/arch-ss928v100/platform.h +@@ -0,0 +1,452 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __CHIP_REGS_H__ ++#define __CHIP_REGS_H__ ++ ++/* -------------------------------------------------------------------- */ ++/* Communication Register and flag used by bootrom */ ++/* -------------------------------------------------------------------- */ ++#define REG_START_FLAG (SYS_CTRL_REG_BASE + REG_SC_GEN1) ++#define START_MAGIC (0x444f574e) ++#define SELF_BOOT_TYPE_USBDEV 0x2 ++#define CP_STEP1_ADDR 0x04010a00 ++#define PCIE_SLAVE_BOOT_CTL_REG 0x0134 ++#define DDR_INIT_DOWNLOAD_OK_FLAG 0xDCDFF001 /* step1:Ddrinit Code Download Finished Flag: DCDFF001 */ ++#define DDR_INIT_EXCUTE_OK_FLAG 0xDCEFF002 /* step2:Ddrinit Code Excute Finished Flag: DCEFF002 */ ++#define UBOOT_DOWNLOAD_OK_FLAG 0xBCDFF003 /* step3:Boot Code Download Finished Flag: BCDFF003 */ ++ ++/* -------------------------------------------------------------------- */ ++/* System Control */ ++/* -------------------------------------------------------------------- */ ++#define SYS_CTRL_REG_BASE 0x11020000 ++#define REG_BASE_SCTL SYS_CTRL_REG_BASE ++#define REG_SC_CTRL 0x0000 ++#define REG_SC_SYSRES 0x0004 ++#define REG_PERISTAT 0x0030 ++#define REG_SYSSTAT 0x0018 ++#define REG_DATA_CHANNEL_TYPE 0x31c ++#define EMMC_BOOT_8BIT (0x1 << 11) ++#define REG_PERI_EMMC_STAT 0x0404 ++#define mmc_boot_clk_sel(val) ((val) & 0x3) ++#define MMC_BOOT_CLK_50M 0x2 ++#define EMMC_NORMAL_MODE (0x1 << 3) ++#define get_spi_nor_addr_mode(_reg) (((_reg) >> 11) & 0x1) ++#define get_spi_device_type(_reg) (((_reg) >> 2) & 0x1) ++#define get_sys_boot_mode(_reg) (((_reg) >> 2) & 0x3) ++#define BOOT_FROM_SPI 0 ++#define BOOT_FROM_SPI_NAND 1 ++#define BOOT_FROM_NAND 2 ++#define BOOT_FROM_EMMC 3 ++ ++#define SEC_UBOOT_DATA_ADDR 0x41000000 ++#define OTP_USER_LOCKABLE14 0x10122090 ++#define get_sec_boot_mode(x) ((((x) & 0xf) == 0x5) ? 0 : 1) ++ ++#define REG_SC_GEN1 0x013c ++#define REG_SC_GEN2 0x0140 ++#define REG_SC_GEN3 0x0144 ++#define REG_SC_GEN4 0x0148 ++#define REG_SC_GEN9 0x0154 ++#define REG_SC_GEN20 0x0090 ++ ++/* -------------------------------------------------------------------- */ ++/* CPU SUBSYS */ ++/* -------------------------------------------------------------------- */ ++#define REG_CRG_CLUSTER0_CLK_RST 0x0190 /* CPU SUBSYS clock and reset control*/ ++#define CLUSTER0_GLB_SRST_REQ (0x1 << 17) ++ ++#define REG_PERI_CPU_RVBARADDR_SOC 0x12030020 ++ ++#define REG_CRG_CLUSTER1_CLK_RST 0x0194 ++#define CLUSTER1_GLB_SRST_REQ (0x1 << 9) ++#define CLUSTER1_GLB_CKEN (0x1 << 8) ++ ++/* -------------------------------------------------------------------- */ ++/* CRG */ ++/* -------------------------------------------------------------------- */ ++#define CRG_REG_BASE 0x11010000 ++ ++/* -------------------------------------------------------------------- */ ++/* Peripheral Control REG */ ++/* -------------------------------------------------------------------- */ ++#define MISC_REG_BASE 0x11024000 ++ ++/* -------------------------------------------------------------------- */ ++/* IO configuration REG:mux and driver */ ++/* -------------------------------------------------------------------- */ ++#define AHB_IO_CONFIG_REG_BASE 0x11180000 ++ ++/* -------------------------------------------------------------------- */ ++/* TIMER */ ++/* -------------------------------------------------------------------- */ ++#define TIMER0_REG_BASE 0x11000000 ++#define REG_TIMER_RELOAD 0x0 ++#define REG_TIMER_VALUE 0x4 ++#define REG_TIMER_CONTROL 0x8 ++#define CFG_TIMER_CLK (3000000) ++#define CFG_TIMERBASE TIMER0_REG_BASE ++/* enable timer.32bit, periodic,mask irq, 1 divider.*/ ++#define CFG_TIMER_CTRL 0xC2 ++ ++/* -------------------------------------------------------------------- */ ++/* UART */ ++/* -------------------------------------------------------------------- */ ++#define UART0_REG_BASE 0x11040000 ++#define UART1_REG_BASE 0x11041000 ++#define UART2_REG_BASE 0x11042000 ++#define UART3_REG_BASE 0x11043000 ++ ++/* -------------------------------------------------------------------- */ ++/* DDRC */ ++/* -------------------------------------------------------------------- */ ++#define STACK_TRAINING 0x0402a000 ++#define DDRC0_REG_BASE 0x11140000 ++#define DDR_MEM_BASE 0x40000000 ++ ++/* -------------------------------------------------------------------- */ ++/* FMC */ ++/* -------------------------------------------------------------------- */ ++#define FMC_REG_BASE 0x10000000 ++#define FMC_MEM_BASE 0x0f000000 ++ ++/* FMC CRG register offset */ ++#define FMC_CLK_REG 0x3F40 ++#define REG_FMC_CRG FMC_CLK_REG ++ ++#define fmc_clk_sel(_clk) (((_clk) & 0x7) << 12) ++#define FMC_CLK_SEL_MASK (0x7 << 12) ++#define get_fmc_clk_type(_reg) (((_reg) >> 12) & 0x7) ++ ++/* SDR/DDR clock */ ++#define FMC_CLK_24M 0 ++#define FMC_CLK_100M 1 ++#define FMC_CLK_150M 2 ++#define FMC_CLK_200M 3 ++/* Only DDR clock */ ++#define FMC_CLK_250M 4 ++#define FMC_CLK_300M 5 ++#define FMC_CLK_400M 6 ++ ++#define FMC_CLK_ENABLE BIT(4) ++#define FMC_SOFT_RST_REQ BIT(0) ++ ++/*--------------------------------------------------------------------- */ ++/* EMMC / SD */ ++/* -------------------------------------------------------------------- */ ++ ++/* eMMC CRG register offset */ ++#define REG_EMMC_CRG (CRG_REG_BASE + 0x34c0) ++#define mmc_clk_sel(_clk) (((_clk) & 0x7) << 24) ++#define MMC_CLK_SEL_MASK (0x7 << 24) ++#define REG_SAVE_HCS 0x11020300 ++ ++/* EMMC REG*/ ++#define EMMC_BASE_REG 0x10020000 ++#define NO_EMMC_PHY 1 ++ ++#define NF_BOOTBW_MASK (1 << 11) ++#define REG_BASE_PERI_CTRL REG_BASE_SCTL ++#define REG_BASE_IO_CONFIG IO_CONFIG_REG_BASE ++ ++/* SDIO0 REG */ ++#define REG_SDIO0_CRG (CRG_REG_BASE + 0x35c0) ++#define SDIO0_BASE_REG 0x10030000 ++ ++/*--------------------------------------------------------------------- */ ++/* GMAC */ ++/* -------------------------------------------------------------------- */ ++#define GMAC0_IOBASE 0x10290000 ++#define GMAC1_IOBASE 0x102A0000 ++ ++/* Ethernet MAC0 MAC_IF CRG register offset */ ++#define REG_ETH0_MACIF_CRG 0x37c0 ++/* Ethernet MAC0 MAC_If CRG register bit map*/ ++#define BIT_MACIF0_RST BIT(0) ++#define BIT_GMACIF0_CLK_EN BIT(4) ++#define BIT_RMII0_CLKSEL_PAD BIT(12) ++ ++/* Ethernet MAC0 GSF CRG register offset */ ++#define REG_ETH0_GSF_CRG 0x37c4 ++/* Ethernet MAC0 GSF CRG register bit map*/ ++#define BIT_GMAC0_RST BIT(0) ++#define BIT_GMAC0_CLK_EN BIT(4) ++ ++/* Ethernet MAC0 PHY CRG register offset */ ++#define REG_ETH0_PHY_CRG 0x37cc ++/* Ethernet MAC0 PHY CRG register bit map*/ ++#define BIT_EXT_PHY0_RST BIT(0) ++#define BIT_EXT_PHY0_CLK_SELECT BIT(12) ++ ++ ++/* Ethernet MAC1 MAC_IF CRG register offset */ ++#define REG_ETH1_MACIF_CRG 0x3800 ++/* Ethernet MAC1 MAC_If CRG register bit map*/ ++#define BIT_MACIF1_RST BIT(0) ++#define BIT_GMACIF1_CLK_EN BIT(4) ++#define BIT_RMII1_CLKSEL_PAD BIT(12) ++ ++/* Ethernet MAC1 GSF CRG register offset */ ++#define REG_ETH1_GSF_CRG 0x3804 ++/* Ethernet MAC1 GSF CRG register bit map*/ ++#define BIT_GMAC1_RST BIT(0) ++#define BIT_GMAC1_CLK_EN BIT(4) ++ ++/* Ethernet MAC1 PHY CRG register offset */ ++#define REG_ETH1_PHY_CRG 0x380c ++/* Ethernet MAC1 PHY CRG register bit map*/ ++#define BIT_EXT_PHY1_RST BIT(0) ++#define BIT_EXT_PHY1_CLK_SELECT BIT(12) ++ ++#define PHY0_CLK_25M 0 ++#define PHY0_CLK_50M BIT_EXT_PHY0_CLK_SELECT ++#define PHY1_CLK_25M 0 ++#define PHY1_CLK_50M BIT_EXT_PHY1_CLK_SELECT ++ ++#define GMAC_MACIF0_CTRL (GMAC0_IOBASE + 0x300c) ++#define GMAC_MACIF1_CTRL (GMAC1_IOBASE + 0x300c) ++#define GMAC_DUAL_MAC_CRF_ACK_TH (GMAC0_IOBASE + 0x3004) ++ ++/* Configure gmac pinctrl parameters in software */ ++#define CFG_NET_PINCTRL ++ ++/* MDIO0 pinctrl phyical addr */ ++#define PHY_ADDR_MDCK0 0x0102F0038 ++#define PHY_ADDR_MDIO0 0x0102F003C ++/* MDIO1 pinctrl phyical addr */ ++#define PHY_ADDR_MDCK1 0x0102F0068 ++#define PHY_ADDR_MDIO1 0x0102F006C ++ ++/* PHY0 pinctrl phyical addr */ ++#define PHY_ADDR_EPHY0_CLK 0x0102F0030 ++#define PHY_ADDR_EPHY0_RSTN 0x0102F0034 ++/* PHY1 pinctrl phyical addr */ ++#define PHY_ADDR_EPHY1_CLK 0x0102F005C ++#define PHY_ADDR_EPHY1_RSTN 0x0102F0064 ++ ++/* RGMII0 pinctrl phyical addr */ ++#define PHY_ADDR_RGMII0_TXCKOUT 0x0102F002C ++#define PHY_ADDR_RGMII0_TXD0 0x0102F0024 ++#define PHY_ADDR_RGMII0_TXD1 0x0102F0020 ++#define PHY_ADDR_RGMII0_TXD2 0x0102F001C ++#define PHY_ADDR_RGMII0_TXD3 0x0102F0018 ++#define PHY_ADDR_RGMII0_TXEN 0x0102F0028 ++#define PHY_ADDR_RGMII0_RXCK 0x0102F0014 ++#define PHY_ADDR_RGMII0_RXD0 0x0102F000C ++#define PHY_ADDR_RGMII0_RXD1 0x0102F0008 ++#define PHY_ADDR_RGMII0_RXD2 0x0102F0004 ++#define PHY_ADDR_RGMII0_RXD3 0x0102F0000 ++#define PHY_ADDR_RGMII0_RXDV 0x0102F0010 ++/* RGMII1 pinctrl phyical addr */ ++#define PHY_ADDR_RGMII1_TXCKOUT 0x0102F0050 ++#define PHY_ADDR_RGMII1_TXD0 0x0102F0040 ++#define PHY_ADDR_RGMII1_TXD1 0x0102F0044 ++#define PHY_ADDR_RGMII1_TXD2 0x0102F0054 ++#define PHY_ADDR_RGMII1_TXD3 0x0102F004C ++#define PHY_ADDR_RGMII1_TXEN 0x0102F0058 ++#define PHY_ADDR_RGMII1_RXCK 0x0102F0074 ++#define PHY_ADDR_RGMII1_RXD0 0x0102F0070 ++#define PHY_ADDR_RGMII1_RXD1 0x0102F0078 ++#define PHY_ADDR_RGMII1_RXD2 0x0102F007C ++#define PHY_ADDR_RGMII1_RXD3 0x0102F0060 ++#define PHY_ADDR_RGMII1_RXDV 0x0102F0048 ++ ++/* RMII0 pinctrl phyical addr */ ++#define PHY_ADDR_RMII0_CLK 0x0102F002C ++#define PHY_ADDR_RMII0_TXD0 0x0102F0024 ++#define PHY_ADDR_RMII0_TXD1 0x0102F0020 ++#define PHY_ADDR_RMII0_TXEN 0x0102F0028 ++#define PHY_ADDR_RMII0_RXD0 0x0102F000C ++#define PHY_ADDR_RMII0_RXD1 0x0102F0008 ++#define PHY_ADDR_RMII0_RXDV 0x0102F0010 ++ ++/* RMII1 pinctrl phyical addr */ ++#define PHY_ADDR_RMII1_CLK 0x0102F0050 ++#define PHY_ADDR_RMII1_TXD0 0x0102F0040 ++#define PHY_ADDR_RMII1_TXD1 0x0102F0044 ++#define PHY_ADDR_RMII1_TXEN 0x0102F0058 ++#define PHY_ADDR_RMII1_RXD0 0x0102F0070 ++#define PHY_ADDR_RMII1_RXD1 0x0102F0078 ++#define PHY_ADDR_RMII1_RXDV 0x0102F0048 ++ ++/* MDIO0 config value */ ++#define VALUE_MDCK0 0x1231 ++#define VALUE_MDIO0 0x1221 ++/* MDIO1 config value */ ++#define VALUE_MDCK1 0x0032 ++#define VALUE_MDIO1 0x0032 ++ ++/* PHY0 config value */ ++#define VALUE_EPHY0_CLK 0x1261 ++#define VALUE_EPHY0_RSTN 0x1201 ++/* PHY1 config value */ ++#define VALUE_EPHY1_CLK 0x0062 ++#define VALUE_EPHY1_RSTN 0x00F2 ++ ++/* RGMII0 config value */ ++#define VALUE_RGMII0_TXCKOUT 0x1261 ++#define VALUE_RGMII0_TXD0 0x1251 ++#define VALUE_RGMII0_TXD1 0x1251 ++#define VALUE_RGMII0_TXD2 0x1251 ++#define VALUE_RGMII0_TXD3 0x1261 ++#define VALUE_RGMII0_TXEN 0x1251 ++#define VALUE_RGMII0_RXCK 0x1271 ++#define VALUE_RGMII0_RXD0 0x1271 ++#define VALUE_RGMII0_RXD1 0x1271 ++#define VALUE_RGMII0_RXD2 0x1271 ++#define VALUE_RGMII0_RXD3 0x1271 ++#define VALUE_RGMII0_RXDV 0x1271 ++/* RGMII1 config value */ ++#define VALUE_RGMII1_TXCKOUT 0x00A2 ++#define VALUE_RGMII1_TXD0 0x0062 ++#define VALUE_RGMII1_TXD1 0x0062 ++#define VALUE_RGMII1_TXD2 0x0062 ++#define VALUE_RGMII1_TXD3 0x0062 ++#define VALUE_RGMII1_TXEN 0x0062 ++#define VALUE_RGMII1_RXCK 0x0072 ++#define VALUE_RGMII1_RXD0 0x0072 ++#define VALUE_RGMII1_RXD1 0x0072 ++#define VALUE_RGMII1_RXD2 0x0072 ++#define VALUE_RGMII1_RXD3 0x0072 ++#define VALUE_RGMII1_RXDV 0x0072 ++ ++/* RMII0 config value */ ++#define VALUE_RMII0_CLK 0x1262 ++#define VALUE_RMII0_TXD0 0x1251 ++#define VALUE_RMII0_TXD1 0x1251 ++#define VALUE_RMII0_TXEN 0x1251 ++#define VALUE_RMII0_RXD0 0x1271 ++#define VALUE_RMII0_RXD1 0x1271 ++#define VALUE_RMII0_RXDV 0x1271 ++/* RMII1 config value */ ++#define VALUE_RMII1_CLK 0x1063 ++#define VALUE_RMII1_TXD0 0x1242 ++#define VALUE_RMII1_TXD1 0x1242 ++#define VALUE_RMII1_TXEN 0x1242 ++#define VALUE_RMII1_RXD0 0x12F2 ++#define VALUE_RMII1_RXD1 0x12F2 ++#define VALUE_RMII1_RXDV 0x12F2 ++ ++/* -------------------------------------------------------------------- */ ++/* USB */ ++/* -------------------------------------------------------------------- */ ++#define USB3_CTRL_REG_BASE 0x10320000 ++#define USB3_CTRL_REG_BASE_1 0x10300000 ++ ++#define MISC_USB3_CTRL_REG 0x10220024 ++#define MISC_USB3_CTRL_REG_1 0x1022003c ++#define USB3_DISABLE_U3_SPEED (0x1 << 9) ++#define USB3_DISABLE_U3_SPEED_1 (0x1 << 12) ++ ++/* USB CRG register offset and config */ ++#define USB3_CTRL_CRG (CRG_REG_BASE + 0x3960) ++#define USB3_CTRL_CRG_1 (CRG_REG_BASE + 0x3940) ++#define USB2_PHY_CRG (CRG_REG_BASE + 0x38e0) ++#define USB2_PHY_CRG_1 (CRG_REG_BASE + 0x38c0) ++#define USB3_PHY_CRG (CRG_REG_BASE + 0x3964) ++#define USB3_PHY_CRG_1 (CRG_REG_BASE + 0x3944) ++ ++#define USB3_CTRL_CRG_DEFAULT_VALUE 0x30001 ++#define USB2_PHY_CRG_DEFAULT_VALUE 0x57 ++#define USB3_PHY_CRG_DEFAULT_VALUE 0x13 ++ ++#define USB3_CRG_PCLK_OCC_SEL (0x1 << 18) ++#define USB3_CRG_PIPE_CKEN (0x1 << 12) ++#define USB3_CRG_UTMI_CKEN (0x1 << 8) ++#define USB3_CRG_SUSPEND_CKEN (0x1 << 6) ++#define USB3_CRG_REF_CKEN (0x1 << 5) ++#define USB3_CRG_BUS_CKEN (0x1 << 4) ++#define USB3_CRG_SRST_REQ (0x1 << 0) ++ ++#define USB2_PHY_CRG_APB_SREQ (0x1 << 2) ++#define USB2_PHY_CRG_TREQ (0x1 << 1) ++#define USB2_PHY_CRG_REQ (0x1 << 0) ++ ++#define USB3_PHY_CRG_TREQ (0x1 << 1) ++#define USB3_PHY_CRG_REQ (0x1 << 0) ++ ++#define COMBPHY_REF_CKEN (0x1<<24) ++#define COMBPHY_SRST_REQ (0x1<<16) ++ ++#define USB3_VCC_SRST_REQ (0x1<<0) ++#define USB3_UTMI_CKSEL (0x1<<13) ++#define USB3_PCLK_OCC_SEL (0x1<<14) ++ ++/* USB CTRL register offset and config */ ++#define GTXTHRCFG 0xc108 ++#define GRXTHRCFG 0xc10c ++#define REG_GCTL 0xc110 ++ ++#define USB_TXPKT_CNT_SEL (0x1 << 29) ++#define USB_TXPKT_CNT (0x11 << 24) ++#define USB_MAXTX_BURST_SIZE (0x1 << 20) ++#define CLEAN_USB3_GTXTHRCFG 0x0 ++ ++#define REG_GUSB3PIPECTL0 0xc2c0 ++#define PCS_SSP_SOFT_RESET (0x1 << 31) ++#define SUSPEND_ENABLE (0x1 <<17) ++ ++#define PORT_CAP_DIR_MASK (0x3 << 12) ++#define PORT_CAP_DIR_HOST (0X1 << 12) ++ ++/* USB PHY register EYE diagram para offset and config */ ++#define USB2_PHY_BASE 0x10330000 ++#define USB2_PHY_BASE_1 0x10310000 ++ ++/* -------------------------------------------------------------------- */ ++/* PCIE */ ++/* -------------------------------------------------------------------- */ ++#define SYS_SATA 0x8c ++#define PCIE_MODE 12 ++ ++#define PERI_CRG98 0x188 ++#define PHY0_SRS_REQ 0 ++#define PHY0_SRS_REQ_SEL 1 ++#define PHY1_SRS_REQ 16 ++#define PHY1_SRS_REQ_SEL 17 ++ ++#define MISC_CTRL5 0x14 ++#define NUM_0 0 ++#define NUM_1 1 ++#define NUM_2 2 ++#define NUM_3 3 ++#define NUM_4 4 ++#define NUM_5 5 ++#define NUM_6 6 ++#define NUM_7 7 ++#define NUM_8 8 ++#define NUM_9 9 ++#define NUM_10 10 ++ ++/*----------------------------------------------------------------------------------- ++ * boot sel type ++ *-----------------------------------------------------------------------------------*/ ++#define BOOT_SEL_PCIE 0x965a4b87 ++#define BOOT_SEL_UART 0x69a5b478 ++#define BOOT_SEL_USB 0x965ab487 ++#define BOOT_SEL_SDIO 0x69a54b87 ++#define BOOT_SEL_FLASH 0x96a54b78 ++#define BOOT_SEL_EMMC 0x695ab487 ++#define BOOT_SEL_UNKNOW 0x965a4b78 ++ ++/* -------------------------------------------------------------------- */ ++/* GZIP */ ++/* -------------------------------------------------------------------- */ ++#define HW_DEC_INTR 97 ++#endif /* End of __CHIP_REGS_H__ */ +diff --git a/arch/arm/include/asm/config.h b/arch/arm/include/asm/config.h +old mode 100644 +new mode 100755 +index bf692ce..d192ba8 +--- a/arch/arm/include/asm/config.h ++++ b/arch/arm/include/asm/config.h +@@ -5,6 +5,7 @@ + + #ifndef _ASM_CONFIG_H_ + #define _ASM_CONFIG_H_ ++#include + + #define CONFIG_LMB + #define CONFIG_SYS_BOOT_RAMDISK_HIGH +diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h +index 1774014..ba47c64 100644 +--- a/arch/arm/include/asm/global_data.h ++++ b/arch/arm/include/asm/global_data.h +@@ -71,6 +71,9 @@ struct arch_global_data { + u32 omap_boot_mode; + u8 omap_ch_flags; + #endif ++#ifdef CONFIG_VENDOR ++ u32 boot_media; ++#endif + #if defined(CONFIG_FSL_LSCH3) && defined(CONFIG_SYS_FSL_HAS_DP_DDR) + unsigned long mem2_clk; + #endif +diff --git a/arch/arm/include/asm/mach-types.h b/arch/arm/include/asm/mach-types.h +index 32532b3..f4918db 100644 +--- a/arch/arm/include/asm/mach-types.h ++++ b/arch/arm/include/asm/mach-types.h +@@ -5051,4 +5051,6 @@ + #define MACH_TYPE_NASM25 5112 + #define MACH_TYPE_TOMATO 5113 + #define MACH_TYPE_OMAP3_MRC3D 5114 ++#define MACH_TYPE_SS928V100 8000 ++ + #endif +diff --git a/arch/arm/include/asm/macro.h b/arch/arm/include/asm/macro.h +index bb33b4b..a58ba2b 100644 +--- a/arch/arm/include/asm/macro.h ++++ b/arch/arm/include/asm/macro.h +@@ -62,7 +62,8 @@ + /* + * Register aliases. + */ +-lr .req x30 ++/*fix warning:gcc 6.3.0 not support*/ ++/* lr .req x30 */ + + /* + * Branch according to exception level +diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile +index 9de9a9a..929adfd 100644 +--- a/arch/arm/lib/Makefile ++++ b/arch/arm/lib/Makefile +@@ -60,6 +60,8 @@ ifndef CONFIG_SYSRESET + obj-y += reset.o + endif + ++obj-y += process.o ++ + obj-y += cache.o + obj-$(CONFIG_SYS_ARM_CACHE_CP15) += cache-cp15.o + +diff --git a/arch/arm/lib/interrupts.c b/arch/arm/lib/interrupts.c +old mode 100644 +new mode 100755 +index 75b70d9..24a485a +--- a/arch/arm/lib/interrupts.c ++++ b/arch/arm/lib/interrupts.c +@@ -51,6 +51,7 @@ void bad_mode (void) + reset_cpu (0); + } + ++#ifndef CONFIG_DISABLE_INTERRUPTS + static void show_efi_loaded_images(struct pt_regs *regs) + { + efi_print_image_infos((void *)instruction_pointer(regs)); +@@ -79,9 +80,11 @@ static void dump_instr(struct pt_regs *regs) + } + printf("\n"); + } ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + + void show_regs (struct pt_regs *regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + unsigned long __maybe_unused flags; + const char __maybe_unused *processor_modes[] = { + "USER_26", "FIQ_26", "IRQ_26", "SVC_26", +@@ -121,81 +124,98 @@ void show_regs (struct pt_regs *regs) + processor_modes[processor_mode (regs)], + thumb_mode (regs) ? " (T)" : ""); + dump_instr(regs); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } + + /* fixup PC to point to the instruction leading to the exception */ ++#ifndef CONFIG_DISABLE_INTERRUPTS + static inline void fixup_pc(struct pt_regs *regs, int offset) + { + uint32_t pc = instruction_pointer(regs) + offset; + regs->ARM_pc = pc | (regs->ARM_pc & PCMASK); + } ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + + void do_undefined_instruction (struct pt_regs *pt_regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + efi_restore_gd(); + printf ("undefined instruction\n"); + fixup_pc(pt_regs, -4); + show_regs (pt_regs); + show_efi_loaded_images(pt_regs); + bad_mode (); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } + + void do_software_interrupt (struct pt_regs *pt_regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + efi_restore_gd(); + printf ("software interrupt\n"); + fixup_pc(pt_regs, -4); + show_regs (pt_regs); + show_efi_loaded_images(pt_regs); + bad_mode (); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } + + void do_prefetch_abort (struct pt_regs *pt_regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + efi_restore_gd(); + printf ("prefetch abort\n"); + fixup_pc(pt_regs, -8); + show_regs (pt_regs); + show_efi_loaded_images(pt_regs); + bad_mode (); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } + + void do_data_abort (struct pt_regs *pt_regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + efi_restore_gd(); + printf ("data abort\n"); + fixup_pc(pt_regs, -8); + show_regs (pt_regs); + show_efi_loaded_images(pt_regs); + bad_mode (); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } + + void do_not_used (struct pt_regs *pt_regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + efi_restore_gd(); + printf ("not used\n"); + fixup_pc(pt_regs, -8); + show_regs (pt_regs); + show_efi_loaded_images(pt_regs); + bad_mode (); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } + + void do_fiq (struct pt_regs *pt_regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + efi_restore_gd(); + printf ("fast interrupt request\n"); + fixup_pc(pt_regs, -8); + show_regs (pt_regs); + show_efi_loaded_images(pt_regs); + bad_mode (); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } + + void do_irq (struct pt_regs *pt_regs) + { ++#ifndef CONFIG_DISABLE_INTERRUPTS + efi_restore_gd(); + printf ("interrupt request\n"); + fixup_pc(pt_regs, -8); + show_regs (pt_regs); + show_efi_loaded_images(pt_regs); + bad_mode (); ++#endif /* CONFIG_DISABLE_INTERRUPTS */ + } +diff --git a/arch/arm/lib/process.c b/arch/arm/lib/process.c +new file mode 100644 +index 0000000..f41fbaf +--- /dev/null ++++ b/arch/arm/lib/process.c +@@ -0,0 +1,45 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++ ++#define CFG_MAX_SHUTDOWN 10 ++ ++static struct shutdown_ctrl { ++ int count; ++ void (*shutdown[CFG_MAX_SHUTDOWN])(void); ++} shutdown_ctrl = {0, {0}, }; ++ ++void add_shutdown(void (*shutdown)(void)) ++{ ++ if (shutdown_ctrl.count >= CFG_MAX_SHUTDOWN) { ++ printf("Can't add shutdown function," ++ "Please increase CFG_MAX_SHUTDOWN count\n"); ++ return; ++ } ++ shutdown_ctrl.shutdown[shutdown_ctrl.count++] ++ = shutdown; ++} ++ ++void do_shutdown(void) ++{ ++ int ix; ++ for (ix = 0; ix < shutdown_ctrl.count; ix++) ++ shutdown_ctrl.shutdown[ix](); ++} +diff --git a/arch/arm/lib/relocate.S b/arch/arm/lib/relocate.S +index e5f7267..2827d77 100644 +--- a/arch/arm/lib/relocate.S ++++ b/arch/arm/lib/relocate.S +@@ -94,6 +94,8 @@ copy_loop: + */ + ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ + ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */ ++ cmp r2, r3 ++ beq relocate_done + fixloop: + ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */ + and r1, r1, #0xff +diff --git a/arch/arm/lib/reset.c b/arch/arm/lib/reset.c +index 3c4512d..ee4ecff 100644 +--- a/arch/arm/lib/reset.c ++++ b/arch/arm/lib/reset.c +@@ -33,6 +33,8 @@ int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + + udelay (50000); /* wait 50 ms */ + ++ do_shutdown(); ++ + disable_interrupts(); + + reset_misc(); +diff --git a/arch/arm/mach-vendor/Kconfig b/arch/arm/mach-vendor/Kconfig +new file mode 100644 +index 0000000..0f82a80 +--- /dev/null ++++ b/arch/arm/mach-vendor/Kconfig +@@ -0,0 +1,43 @@ ++if ARCH_VENDOR ++ ++config VENDOR_COMMON ++ bool "VENDOR common options" ++ select DM ++ ++config SYS_VENDOR ++ default "vendor" ++ ++endif ++ ++config DISABLE_INTERRUPTS ++ bool "disable interrupts" ++ default n ++ ++config LOW_DELAY_INITIALIZATION ++ bool "low delay initialization" ++ default n ++ ++config INIT_TIMER_EARLY ++ bool "init timer early" ++ default n ++ ++config CMD_TIMESTAMP ++ bool "timestamp - record timestamp" ++ default n ++ help ++ Enable the 'timestamp' command which record timestamp in DDR since ++ Chip started running. ++ ++config CMD_TIMESTAMP_TEST ++ bool "timestamp_unit_test - record timestamp" ++ default n ++ depends on CMD_TIMESTAMP ++ help ++ Enable the 'timestamp_unit_test' command which record timestamp in DDR ++ since Chip started running. ++ ++config TIME_ADDR_OFFSET ++ hex "record timestamp addr offset" ++ default 0xC800 ++ help ++ record the timestamp addr,base addr is fdt, TIME_ADDR_OFFSET is offset +diff --git a/arch/arm/mach-vendor/Makefile b/arch/arm/mach-vendor/Makefile +new file mode 100644 +index 0000000..75c15fb +--- /dev/null ++++ b/arch/arm/mach-vendor/Makefile +@@ -0,0 +1,21 @@ ++# Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, see ++# . ++# ++ ++extra-$(CONFIG_VENDOR_COMMON) += init_regs.o uart.o soc.o util.o ++obj-y += dummy.o ++ ++ +diff --git a/arch/arm/mach-vendor/boot0_hook.S b/arch/arm/mach-vendor/boot0_hook.S +new file mode 100644 +index 0000000..e3c089f +--- /dev/null ++++ b/arch/arm/mach-vendor/boot0_hook.S +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++ ++VENDOR_BOOT0_HOOK ++ ++ .globl _blank_zone_start ++_blank_zone_start: .word __blank_zone_start ++ENTRY(get_blank_start) ++ ldr r0, _blank_zone_start ++ ldr r1, =__image_copy_start ++ sub r0, r0, r1 ++ adrl r1, _start ++ add r0, r0, r1 ++ mov pc, lr ++ENDPROC(get_blank_start) ++ ++ENTRY(get_code_start) ++ adrl r0, _start ++ mov pc, lr ++ENDPROC(get_code_start) +diff --git a/arch/arm/mach-vendor/dummy.c b/arch/arm/mach-vendor/dummy.c +new file mode 100644 +index 0000000..e69de29 +diff --git a/arch/arm/mach-vendor/init_regs.c b/arch/arm/mach-vendor/init_regs.c +new file mode 100644 +index 0000000..2a4cad5 +--- /dev/null ++++ b/arch/arm/mach-vendor/init_regs.c +@@ -0,0 +1,184 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include ++ ++#define W_WHETHER_WRITE (1<<0) ++#define W_WHETHER_PM (1<<1) ++#define W_WHETHER_BOOT_NORMAL (1<<2) ++#define W_BIT_OFFSET 3 ++#define W_BIT_MASK (0x1f<attr & R_REG_BIT_MASK) >> R_REG_BIT_OFFSET); ++ bit_num_r = ((reg->attr & R_BIT_MASK) >> R_BIT_OFFSET) + 1; ++ reg_val_r = (*(volatile unsigned *)(long)(reg->reg_addr)); ++ ++ if (bit_num_r != RW_BIT_NUM) { ++ reg_val_r >>= bit_start_r; ++ reg_val_r &= ((1 << bit_num_r) - 1); ++ } ++ ++ *ret = ((reg_val_r == reg->value) ? 0 : 1); ++} ++ ++static void reg_write(const struct regentry *reg) ++{ ++ unsigned int reg_val_w; ++ unsigned int delay_2; ++ unsigned int bit_start_w; ++ unsigned int bit_num_w; ++ ++ delay_2 = reg->delay; ++ bit_start_w = ((reg->attr & W_REG_BIT_MASK) >> W_REG_BIT_OFFSET); ++ bit_num_w = ((reg->attr & W_BIT_MASK) >> W_BIT_OFFSET) + 1; ++ reg_val_w = (*(volatile unsigned *)(long)(reg->reg_addr)); ++ ++ if (bit_num_w == RW_BIT_NUM) { ++ reg_val_w = reg->value; ++ } else { ++ reg_val_w &= (~(((1 << bit_num_w) - 1) << bit_start_w)); ++ reg_val_w |= (reg->value) << bit_start_w; ++ } ++ writel(reg_val_w, reg->reg_addr); ++ ++ do { ++ delay(); ++ } while (delay_2--); ++} ++ ++static void read_write(const struct regentry *reg, unsigned int pm) ++{ ++ unsigned int ret; ++ unsigned int delay_1; ++ ++ ret = 0; ++ delay_1 = reg->delay; ++ ++ if (pm) { ++ if (reg->attr & W_WHETHER_PM) { ++ reg_write(reg); ++ } else if (reg->attr & R_WHETHER_PM) { ++ do { ++ reg_read(reg, &ret); ++ delay(); ++ } while (ret); ++ ++ do { ++ delay(); ++ } while (delay_1--); ++ } else { ++ do { ++ delay(); ++ } while (delay_1--); ++ } ++ } else { ++ if (reg->attr & W_WHETHER_BOOT_NORMAL) { ++ reg_write(reg); ++ } else if (reg->attr & R_WHETHER_BOOT_NORMAL) { ++ do { ++ reg_read(reg, &ret); ++ delay(); ++ } while (ret); ++ ++ do { ++ delay(); ++ } while (delay_1--); ++ } else { ++ do { ++ delay(); ++ } while (delay_1--); ++ } ++ } ++} ++ ++static void part_read_write(const struct regentry *reg_table, unsigned int pm) ++{ ++ unsigned int i; ++ ++ for (i = 0; ; i++) { ++ if ((!reg_table[i].reg_addr) && (!reg_table[i].value) && ++ (!reg_table[i].delay) && (!reg_table[i].attr)) ++ goto main_end; ++ ++ read_write(®_table[i], pm); ++ } ++ ++main_end: ++ delay(); ++} ++ ++/* ++ * base - reg base address ++ * pm - is suspend ++ * 0 normal ++ * 1 pm ++ */ ++void init_registers(unsigned long base, unsigned long pm) ++{ ++ struct regentry *reg_table = (struct regentry *)(uintptr_t)base; ++ ++ part_read_write(reg_table, pm); ++} +diff --git a/arch/arm/mach-vendor/mci_boot.c b/arch/arm/mach-vendor/mci_boot.c +new file mode 100644 +index 0000000..8f993e8 +--- /dev/null ++++ b/arch/arm/mach-vendor/mci_boot.c +@@ -0,0 +1,131 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include "soc.h" ++#include ++ ++#define DELAY_US 1000 ++#define CP_STEP1_SIZE 0x6000 ++ ++static inline unsigned int mci_readl(unsigned addr) ++{ ++ return *((volatile unsigned *)(uintptr_t)(addr)); ++} ++ ++static inline void mci_writel(unsigned val, unsigned addr) ++{ ++ (*(volatile unsigned *)(uintptr_t)(addr)) = (val); ++} ++ ++static inline void delay(unsigned int cnt) ++{ ++ while (cnt--) ++ __asm__ __volatile__("nop"); ++} ++ ++static void emmc_deinit(void) ++{ ++ unsigned int mmc_base = EMMC_REG_BASE; ++ unsigned int val; ++ ++ val = mci_readl(mmc_base + MCI_CTRL); ++ val |= (CTRL_RESET | FIFO_RESET | DMA_RESET); ++ mci_writel(val, mmc_base + MCI_CTRL); ++ ++ /* clear MMC host intr */ ++ mci_writel(ALL_INT_CLR, mmc_base + MCI_RINTSTS); ++} ++ ++static void emmc_boot_mode_read(void *ptr, unsigned int size) ++{ ++ unsigned int count, val; ++ unsigned int tmp_reg; ++ unsigned int *buf = NULL; ++ unsigned int data; ++ ++ if (size <= CP_STEP1_SIZE) { ++ memcpy_32(ptr, (void *)RAM_START_ADRS, size); ++ return; ++ } ++ memcpy_32(ptr, (void *)RAM_START_ADRS, CP_STEP1_SIZE); ++ buf = (unsigned int *)(ptr + CP_STEP1_SIZE); ++ size = (size - CP_STEP1_SIZE) >> NUM_2; ++ ++ while (size > 0) { ++ tmp_reg = mci_readl(EMMC_REG_BASE + MCI_STATUS); ++ count = (tmp_reg >> FIFO_COUNT) & FIFO_COUNT_MASK; ++ ++ if (count > size) ++ count = size; ++ ++ /* start to read data */ ++ while (1) { ++ val = (DCRC_INT_STATUS | FRUN_INT_STATUS | HLE_INT_STATUS | ++ SBE_INT_STATUS | EBE_INT_STATUS); ++ tmp_reg = mci_readl(EMMC_REG_BASE + MCI_RINTSTS); ++ if (tmp_reg & val) ++ return; ++ ++ if (tmp_reg & RXDR_INT_STATUS) ++ break; ++ } ++ ++ mci_writel(ALL_INT_CLR, EMMC_REG_BASE + MCI_RINTSTS); ++ ++ for (; count != 0; --count) { ++ data = mci_readl(EMMC_REG_BASE + MCI_FIFO_START); ++ ++ *buf = data; ++ buf++; ++ --size; ++ } ++ } ++ ++ mci_writel(START_CMD | DISABLE_BOOT | STOP_ABORT_CMD, EMMC_REG_BASE + MCI_CMD); ++ count = 1000; /* 1000: Cycle */ ++ do { ++ delay(DELAY_US); ++ count--; ++ tmp_reg = mci_readl(EMMC_REG_BASE + MCI_CMD); ++ } while ((tmp_reg & START_CMD) && count); ++ ++ count = 1000; /* 1000: Cycle */ ++ do { ++ delay(DELAY_US); ++ count--; ++ tmp_reg = mci_readl(EMMC_REG_BASE + MCI_RINTSTS); ++ } while (!(tmp_reg & DTO_INT_STATUS) && count); ++} ++ ++static void emmc_boot_mode_deinit(void) ++{ ++ unsigned int mmc_base = EMMC_REG_BASE; ++ ++ mci_writel(START_CMD | DISABLE_BOOT | STOP_ABORT_CMD, mmc_base + MCI_CMD); ++} ++ ++void emmc_boot_read(void *ptr, unsigned int size) ++{ ++ emmc_boot_mode_read(ptr, size); ++ emmc_boot_mode_deinit(); ++ emmc_deinit(); ++} +diff --git a/arch/arm/mach-vendor/soc.c b/arch/arm/mach-vendor/soc.c +new file mode 100644 +index 0000000..b5640e6 +--- /dev/null ++++ b/arch/arm/mach-vendor/soc.c +@@ -0,0 +1,63 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "soc.h" ++#include ++#include ++#include ++#include ++#include ++ ++void s_init(void) ++{ ++ unsigned int blank_zone_start; ++ ++ blank_zone_start = get_blank_start(); ++ init_registers(blank_zone_start, 0); ++ ++#ifdef CONFIG_DDR_TRAINING_V2 ++ start_ddr_training(REG_BASE_SCTL); ++#endif ++} ++ ++void load_image(void) ++{ ++ unsigned int len = (unsigned int)&__bss_start - ++ (unsigned int)__image_copy_start; ++ ++ switch (get_boot_media()) { ++#ifdef CONFIG_SUPPORT_EMMC_BOOT ++ case BOOT_MEDIA_EMMC: ++ emmc_boot_read(__image_copy_start, len); ++ break; ++#endif ++ case BOOT_MEDIA_NAND: ++ case BOOT_MEDIA_SPIFLASH: ++ memcpy_32((void *)__image_copy_start, (void *)FMC_MEM_BASE, len); ++ break; ++ default: ++ break; ++ } ++} ++ ++void enable_syscounter(int hz) ++{ ++ writel(hz, REG_BASE_SYSCNT + CNTFID0); ++ writel(1, REG_BASE_SYSCNT + CNTCR); ++} +diff --git a/arch/arm/mach-vendor/soc.h b/arch/arm/mach-vendor/soc.h +new file mode 100644 +index 0000000..144906e +--- /dev/null ++++ b/arch/arm/mach-vendor/soc.h +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __BSP_SOC_H__ ++#define __BSP_SOC_H__ ++ ++extern unsigned int blank_zone_start; ++extern unsigned int get_blank_start(void); ++extern void init_registers(unsigned int base, unsigned int pm); ++extern void emmc_boot_read(void *ptr, unsigned int size); ++extern void memcpy_32(void *dst, void *src, unsigned int size); ++ ++#ifdef CONFIG_DDR_TRAINING_V2 ++ extern void start_ddr_training(unsigned int base); ++#endif /* CONFIG_DDR_TRAINING_V2 */ ++ ++#endif +diff --git a/arch/arm/mach-vendor/uart.S b/arch/arm/mach-vendor/uart.S +new file mode 100644 +index 0000000..8a9bc88 +--- /dev/null ++++ b/arch/arm/mach-vendor/uart.S +@@ -0,0 +1,129 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++ ++ ++#include ++ ++@****************************************************************************** ++@ ++@ void uart_early_init(void); ++@ ++.text ++.align 2 ++.global uart_early_init ++.type uart_early_init, %function ++uart_early_init: ++ ldr a4, uart_base_addr_L0 ++ mov a3, #0 ++ /* Disable UART */ ++ str a3, [a4, #48] ++ /* Set baud rate to 115200, uart clock:24M */ ++ add a3, a3, #13 ++ str a3, [a4, #36] ++ mov a3, #1 ++ str a3, [a4, #40] ++ /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled. */ ++ ldr a3, =112 ++ str a3, [a4, #44] ++ /* Enable UART */ ++ ldr a3, =769 ++ str a3, [a4, #48] ++ bx lr ++uart_base_addr_L0: ++ .word CONFIG_CUR_UART_BASE ++ ++@****************************************************************************** ++@ ++@ void uart_early_puts(const char *ss); ++@ ++.align 2 ++.global uart_early_puts ++.type uart_early_puts, %function ++uart_early_puts: ++ ldr a2, uart_base_addr_L1 ++ b next_char ++output: ++ ldr a4, [a2, #24] ++ tst a4, #32 ++ bne output ++ str a3, [a2, #0] ++ add a1, a1, #1 ++next_char: ++ ldrb a3, [a1] ++ cmp a3, #0 ++ bne output ++ bx lr ++uart_base_addr_L1: ++ .word CONFIG_CUR_UART_BASE ++ ++@****************************************************************************** ++@ ++@ void uart_early_put_hex(int hex); ++@ ++@ call example: ++@ mov r0, sp ++@ bl uart_early_put_hex ++@ ++.align 2 ++.global uart_early_put_hex ++.type uart_early_put_hex, %function ++uart_early_put_hex: ++ ldr a2, uart_base_addr_L2 ++ mov a3, #28 ++wait2: ++ ldr a4, [a2, #24] ++ tst a4, #32 ++ bne wait2 ++ ++ mov a4, #0xF ++ and a4, a4, a1, lsr a3 ++ cmp a4, #9 ++ addle a4, a4, #0x30 @ a4 = a4 + '0' ++ addgt a4, a4, #55 @ a4 = a4 - 10 + 'A' ++ str a4, [a2, #0] ++ cmp a3, #0 ++ beq exit2 ++ sub a3, a3, #4 ++ b wait2 ++exit2: ++ bx lr ++uart_base_addr_L2: ++ .word CONFIG_CUR_UART_BASE ++ ++@****************************************************************************** ++@ ++@ void uart_early_putc(int chr); ++@ ++@ call example: ++@ mov r0, #'A' ++@ bl uart_early_putc ++@ ++.align 2 ++.global uart_early_putc ++.type uart_early_putc, %function ++uart_early_putc: ++ ldr a2, uart_base_addr_L3 ++wait3: ++ ldr a4, [a2, #24] ++ tst a4, #32 ++ bne wait3 ++ str a1, [a2, #0] ++ bx lr ++uart_base_addr_L3: ++ .word CONFIG_CUR_UART_BASE +diff --git a/arch/arm/mach-vendor/util.S b/arch/arm/mach-vendor/util.S +new file mode 100644 +index 0000000..1bc1daa +--- /dev/null ++++ b/arch/arm/mach-vendor/util.S +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++ ++ENTRY(memcpy_32) ++memcpy: ++ push {r3 - r10} ++ cmp r0, r1 ++ beq copy_done ++ add r2, r0, r2 ++memcpy_loop: ++ ldmia r1!, {r3 - r10} ++ stmia r0!, {r3 - r10} ++ cmp r0, r2 ++ ble memcpy_loop ++copy_done: ++ pop {r3 - r10} ++ mov pc, lr ++ENDPROC(memcpy_32) +diff --git a/board/vendor/ss927v100/Kconfig b/board/vendor/ss927v100/Kconfig +new file mode 100644 +index 0000000..ca04827 +--- /dev/null ++++ b/board/vendor/ss927v100/Kconfig +@@ -0,0 +1,15 @@ ++if TARGET_SS927V100 ++ ++config SYS_BOARD ++ default "ss927v100" ++ ++config SYS_VENDOR ++ default "vendor" ++ ++config SYS_SOC ++ default "ss927v100" ++ ++config SYS_CONFIG_NAME ++ default "ss927v100" ++ ++endif +diff --git a/board/vendor/ss927v100/Makefile b/board/vendor/ss927v100/Makefile +new file mode 100644 +index 0000000..7b02198 +--- /dev/null ++++ b/board/vendor/ss927v100/Makefile +@@ -0,0 +1,8 @@ ++# ++# (C) Copyright 2000-2004 ++# Wolfgang Denk, DENX Software Engineering, wd@denx.de. ++# ++# SPDX-License-Identifier: GPL-2.0+ ++# ++ ++obj-y := ss927v100.o +diff --git a/board/vendor/ss927v100/ss927v100.c b/board/vendor/ss927v100/ss927v100.c +new file mode 100644 +index 0000000..e88f8b0 +--- /dev/null ++++ b/board/vendor/ss927v100/ss927v100.c +@@ -0,0 +1,792 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mm_region ss928v100_mem_map[] = { ++ { ++ .virt = 0x0UL, ++ .phys = 0x0UL, ++ .size = 0x05000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE ++ }, ++ { ++ .virt = 0x05000000UL, ++ .phys = 0x05000000UL, ++ .size = 0x40000000UL - 0x05000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, ++ { ++ .virt = 0x40000000UL, ++ .phys = 0x40000000UL, ++ .size = 0x200000000UL, /* PHYS_SDRAM_1_SIZE */ ++ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | ++ PTE_BLOCK_INNER_SHARE ++ }, ++ { ++ /* List terminator */ ++ 0, ++ } ++}; ++ ++struct mm_region *mem_map = ss928v100_mem_map; ++static int boot_media = BOOT_MEDIA_UNKNOWN; ++ ++#if defined(CONFIG_SHOW_BOOT_PROGRESS) ++void show_boot_progress(int progress) ++{ ++ printf("Boot reached stage %d\n", progress); ++} ++#endif ++ ++#define COMP_MODE_ENABLE ((unsigned int)0x0000EAEF) ++ ++static inline void delay(unsigned long loops) ++{ ++ __asm__ volatile ("1:\n" ++ "subs %0, %1, #1\n" ++ "bne 1b" : "=r" (loops) : "0" (loops)); ++} ++ ++int is_tee_enable_otp(void) ++{ ++ if ((NUM_FF & readl(OTP_USER_LOCKABLE0)) == OTP_SOC_TEE_DISABLE_FLAG) ++ return OTP_TEE_DISABLE; /* tee is disable */ ++ else ++ return OTP_TEE_ENABLE; /* tee is enable */ ++} ++ ++int check_otp_cmd_mode(void) ++{ ++ unsigned int el; ++ if (is_tee_enable_otp() == OTP_TEE_ENABLE) { /* tee is enable */ ++ asm volatile("mrs %0, CurrentEL" : "=r" (el) : : "cc"); ++ el >>= 2; /* Move Left 2 bit */ ++ if (el == EXCEPTION_LEVEL1) ++ return OTP_REE_CMD_MODE; ++ else if (el == EXCEPTION_LEVEL3) ++ return OTP_TEE_CMD_MODE; ++ } ++ ++ return OTP_TEE_CMD_MODE; /* TEE mode */ ++} ++ ++/* get uboot start media. */ ++int get_boot_media(void) ++{ ++ return boot_media; ++} ++ ++int get_text_base(void) ++{ ++ return CONFIG_SYS_TEXT_BASE; ++} ++ ++static void boot_flag_init(void) ++{ ++ unsigned int regval, boot_mode; ++ ++ /* get boot mode */ ++ regval = __raw_readl(SYS_CTRL_REG_BASE + REG_SYSSTAT); ++ boot_mode = get_sys_boot_mode(regval); ++ ++ switch (boot_mode) { ++ /* [3:2] 00b - boot from Spi Nor device */ ++ case BOOT_FROM_SPI: ++ boot_media = BOOT_MEDIA_SPIFLASH; ++ break; ++ /* [3:2] 01b - boot from Spi Nand device */ ++ case BOOT_FROM_SPI_NAND: ++ boot_media = BOOT_MEDIA_NAND; ++ break; ++ /* [3:2] 10b - boot from Nand device */ ++ case BOOT_FROM_NAND: ++ boot_media = BOOT_MEDIA_NAND; ++ break; ++ /* [3:2] 11b - boot from emmc */ ++ case BOOT_FROM_EMMC: ++ boot_media = BOOT_MEDIA_EMMC; ++ break; ++ default: ++ boot_media = BOOT_MEDIA_UNKNOWN; ++ break; ++ } ++} ++ ++int board_early_init_f(void) ++{ ++ return 0; ++} ++ ++#define UBOOT_DATA_ADDR 0x41000000UL ++#define UBOOT_DATA_SIZE 0x80000UL ++int data_to_spiflash(void) ++{ ++ static struct spi_flash *flash = NULL; ++ void *buf = NULL; ++ int spi_flash_erase_ret; ++ ssize_t val; ++ ++ /* 0:bus 0:cs 1000000:max_hz 0x3:spi_mode */ ++ flash = spi_flash_probe(0, 0, 1000000, 0x3); ++ if (!flash) { ++ printf("Failed to initialize SPI flash\n"); ++ return -1; ++ } ++ ++ /* erase the address range. */ ++ printf("Spi flash erase...\n"); ++ spi_flash_erase_ret = spi_flash_erase(flash, NUM_0, UBOOT_DATA_SIZE); ++ if (spi_flash_erase_ret) { ++ printf("SPI flash sector erase failed\n"); ++ return 1; ++ } ++ ++ buf = map_physmem((unsigned long)UBOOT_DATA_ADDR, ++ UBOOT_DATA_SIZE, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ /* copy the data from RAM to FLASH */ ++ printf("Spi flash write...\n"); ++ val = flash->write(flash, NUM_0, UBOOT_DATA_SIZE, buf); ++ if (val) { ++ printf("SPI flash write failed, return %zd\n", val); ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 1; ++ } ++ ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 0; /* 0:success */ ++} ++ ++int data_to_nandflash(void) ++{ ++ struct mtd_info *nand_flash = NULL; ++ void *buf = NULL; ++ size_t length = UBOOT_DATA_SIZE; ++ int val; ++ ++ nand_flash = nand_info[0]; ++ ++ printf("Nand flash erase...\n"); ++ val = nand_erase(nand_flash, 0, UBOOT_DATA_SIZE); ++ if (val) { ++ printf("Nand flash erase failed\n"); ++ return 1; ++ } ++ ++ buf = map_physmem((unsigned long)UBOOT_DATA_ADDR, ++ UBOOT_DATA_SIZE, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ printf("Nand flash write...\n"); ++ val = nand_write(nand_flash, 0, &length, buf); ++ if (val) { ++ printf("Nand flash write failed, return %d\n", val); ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 1; ++ } ++ ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 0; ++} ++ ++int data_to_emmc(void) ++{ ++ struct mmc *mmc = find_mmc_device(0); ++ void *buf = NULL; ++ ++ if (!mmc) ++ return 1; ++ ++ (void)mmc_init(mmc); ++ ++ buf = map_physmem((unsigned long)UBOOT_DATA_ADDR, ++ UBOOT_DATA_SIZE, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ printf("MMC write...\n"); ++ blk_dwrite(mmc_get_blk_desc(mmc), 0, (UBOOT_DATA_SIZE >> NUM_9), buf); ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 0; ++} ++ ++int save_bootdata_to_flash(void) ++{ ++ unsigned int sd_update_flag; ++ int ret; ++ sd_update_flag = readl(REG_BASE_SCTL + REG_SC_GEN4); ++ if (sd_update_flag == START_MAGIC) { ++#if defined(CONFIG_FMC) ++ if (boot_media == BOOT_MEDIA_SPIFLASH) { ++ ret = data_to_spiflash(); ++ if (ret != 0) ++ return ret; ++ } ++ if (boot_media == BOOT_MEDIA_NAND) { ++ ret = data_to_nandflash(); ++ if (ret != 0) ++ return ret; ++ } ++#endif ++#if defined(CONFIG_MMC) ++ if (boot_media == BOOT_MEDIA_EMMC) { ++ ret = data_to_emmc(); ++ if (ret != 0) ++ return ret; ++ } ++#endif ++ ++ printf("update success!\n"); ++ } ++ ++ return 0; ++} ++ ++int auto_update_flag = 0; ++int bare_chip_program = 0; ++ ++#define REG_BASE_GPIO0 0x11090000 ++#define GPIO0_0_DATA_OFST 0x4 ++#define GPIO_DIR_OFST 0x400 ++#define PROC_TIME_OUT 100 ++#define PROC_LOOP 5 ++#define DOWNLOAD_FLAG 0x1f ++#define ACK 0xAA ++ ++int is_bare_program(void) ++{ ++ return 1; ++} ++ ++/* Connect to TOOLS */ ++int uart_self_boot_check(void) ++{ ++ uint32_t count, i; ++ uint32_t timer_count; ++ unsigned char cr; ++ ++ for (count = 0; count < PROC_LOOP; ++count) { ++ for (i = 0; i < PROC_LOOP; i++) ++ serial_putc((unsigned char) DOWNLOAD_FLAG); ++ ++ timer_count = 0; ++ while (timer_count < PROC_TIME_OUT) { ++ if (serial_tstc()) { ++ cr = (unsigned char)serial_getc(); ++ if (cr == ACK) ++ return 1; ++ } ++ timer_count++; ++ udelay(100); /* delay 100 us */ ++ } ++ } ++ serial_putc((unsigned char) '\n'); ++ ++ return 0; ++} ++ ++/* return the flag for usb/sd update */ ++int check_usb_sd_update_flag(void) ++{ ++ u32 flag; ++ ++ writel(0x0, REG_BASE_GPIO0 + GPIO_DIR_OFST); ++ ++ flag = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ if (!flag) { ++ mdelay(10); /* delay 10 ms */ ++ flag = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ if (!flag) { ++ mdelay(10); /* delay 10 ms */ ++ flag = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ } ++ } ++ ++ return (!flag); ++} ++ ++void set_bootloader_download_process_flag(void) ++{ ++ uint32_t channel_type; ++ ++ if (readl(REG_START_FLAG) == START_MAGIC) ++ return; ++ ++ channel_type = readl(REG_BASE_SCTL + REG_DATA_CHANNEL_TYPE); ++ switch (channel_type) { ++ case BOOT_SEL_PCIE: ++ return; ++ default: ++ break; ++ } ++ ++ /* set download flag */ ++ if (uart_self_boot_check()) { ++ writel(START_MAGIC, REG_START_FLAG); ++ return; ++ } ++ ++ if (check_usb_sd_update_flag()) { ++ writel(START_MAGIC, REG_START_FLAG); ++ writel(SELF_BOOT_TYPE_USBDEV, SYS_CTRL_REG_BASE + REG_SC_GEN9); ++ return; ++ } ++} ++ ++int is_auto_update(void) ++{ ++#if (CONFIG_AUTO_SD_UPDATE == 1) || (CONFIG_AUTO_USB_UPDATE == 1) ++ /* to add some judgement if neccessary */ ++ unsigned int val[NUM_3]; ++ ++ writel(0, REG_BASE_GPIO0 + GPIO_DIR_OFST); ++ ++ val[NUM_0] = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ if (val[NUM_0]) ++ return 0; ++ ++ udelay(10000); /* delay 10000 us */ ++ val[NUM_1] = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ udelay(10000); /* delay 10000 us */ ++ val[NUM_2] = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ udelay(10000); /* delay 10000 us */ ++ ++ if (val[NUM_0] == val[NUM_1] && val[NUM_1] == val[NUM_2] && val[NUM_0] == NUM_0) ++ return 1; /* update enable */ ++ else ++ return 0; ++ ++#else ++ return 0; ++#endif ++} ++ ++void set_pcie_para_ss928v100(void) ++{ ++ unsigned int val; ++ val = readl(SYS_CTRL_REG_BASE + SYS_SATA); ++ if ((val & (0x3 << PCIE_MODE)) == 0) { ++ /* X2 release phy reset */ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val &= ((~(0x1 << PHY1_SRS_REQ)) & (~(0x1 << PHY0_SRS_REQ))); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ ++ /*X2 select phy reset from crg*/ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val |= (0x1 << PHY1_SRS_REQ_SEL) | (0x1 << PHY0_SRS_REQ_SEL); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X2 seperate_rate=1 ++ */ ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x94f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x92f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x96f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x92f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X2 split_cp_dis ++ */ ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd51, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd31, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd71, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd31, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ } else { ++ /*X1 release phy reset*/ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val &= ~(0x1 << PHY0_SRS_REQ); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ ++ /*X1 select phy reset from crg*/ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val |= (0x1 << PHY0_SRS_REQ_SEL); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X1 seperate_rate=1 ++ */ ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x94f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X1 split_cp_dis ++ */ ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd51, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ }; ++} ++ ++int misc_init_r(void) ++{ ++#ifdef CONFIG_RANDOM_ETHADDR ++ random_init_r(); ++#endif ++ env_set("verify", "n"); ++ ++#if (CONFIG_AUTO_UPDATE == 1) ++ /* auto update flag */ ++ if (is_auto_update()) ++ auto_update_flag = 1; ++ else ++ auto_update_flag = 0; ++ ++ /* bare chip program flag */ ++ if (is_bare_program()) ++ bare_chip_program = 1; ++ else ++ bare_chip_program = 0; ++ ++#ifdef CFG_MMU_HANDLEOK ++ dcache_stop(); ++#endif ++ ++#ifdef CFG_MMU_HANDLEOK ++ dcache_start(); ++#endif ++ ++#endif /*CONFIG_AUTO_UPDATE*/ ++ ++#if (CONFIG_AUTO_UPDATE == 1) ++ if (auto_update_flag) ++ do_auto_update(); ++ if (bare_chip_program && !auto_update_flag) ++ save_bootdata_to_flash(); ++#endif ++ set_bootloader_download_process_flag(); ++ return 0; ++} ++ ++int board_init(void) ++{ ++ DECLARE_GLOBAL_DATA_PTR; ++ ++ gd->bd->bi_arch_number = MACH_TYPE_SS928V100; ++ gd->bd->bi_boot_params = CFG_BOOT_PARAMS; ++ ++ boot_flag_init(); ++ ++ return 0; ++} ++ ++int dram_init(void) ++{ ++ DECLARE_GLOBAL_DATA_PTR; ++ ++ gd->ram_size = PHYS_SDRAM_1_SIZE; ++ return 0; ++} ++ ++void reset_cpu(ulong addr) ++{ ++ writel(0x12345678, REG_BASE_SCTL + REG_SC_SYSRES); ++ while (1); ++} ++ ++int timer_init(void) ++{ ++ /* ++ * *Under uboot, 0xffffffff is set to load register, ++ * * timer_clk = BUSCLK/2/256. ++ * * e.g. BUSCLK = 50M, it will roll back after 0xffffffff/timer_clk ++ * * = 43980s = 12hours ++ * */ ++ __raw_writel(0, CFG_TIMERBASE + REG_TIMER_CONTROL); ++ __raw_writel(~0, CFG_TIMERBASE + REG_TIMER_RELOAD); ++ ++ /*32 bit, periodic*/ ++ __raw_writel(CFG_TIMER_CTRL, CFG_TIMERBASE + REG_TIMER_CONTROL); ++ ++ return 0; ++} ++ ++int board_eth_init(bd_t *bis) ++{ ++ int rc = 0; ++ ++#ifdef CONFIG_GMACV300_ETH ++ rc = gmac_initialize(bis); ++#endif ++ return rc; ++} ++ ++#ifdef CONFIG_GENERIC_MMC ++int board_mmc_init(bd_t *bis) ++{ ++ int ret = 0; ++ ++#ifdef CONFIG_MMC_SDHCI ++ ++#if!defined(CONFIG_FMC) || defined(CONFIG_AUTO_SD_UPDATE) ++ int dev_num = 0; ++#endif ++ ++#ifndef CONFIG_FMC ++ ret = sdhci_add_port(0, EMMC_BASE_REG, MMC_TYPE_MMC); ++ if (!ret) { ++ ret = bsp_mmc_init(dev_num); ++ if (ret) ++ printf("No EMMC device found !\n"); ++ } ++ dev_num++; ++#endif ++ ++#ifdef CONFIG_AUTO_SD_UPDATE ++ if (is_auto_update()) { ++ ret = sdhci_add_port(1, SDIO0_BASE_REG, MMC_TYPE_SD); ++ if (ret) ++ return ret; ++ ++ ret = bsp_mmc_init(dev_num); ++ if (ret) ++ printf("No SD device found !\n"); ++ } ++#endif ++#endif ++ ++ return ret; ++} ++#endif ++ ++#define QOS_PRI_BASE 0x11140000 ++#define REG_STEP 0x10 ++ ++#define AXI_QOS_WRPRI0 0x0204 ++#define AXI_QOS_WRPRI12 0x02c4 ++ ++#define AXI_QOS_RDPRI0 0x0208 ++#define AXI_QOS_RDPRI12 0x02c8 ++ ++#define AXI_QOS_MAP0 0x0200 ++#define AXI_QOS_MAP12 0x02c0 ++ ++#define REG_VALUE 0x01234567 ++#define AXI_QOS_MAP_VALUE 0x00110000 ++ ++int config_qos_registers(void) ++{ ++ unsigned int i, len; ++ ++ /* AXI_QOS_WRPRI0 - AXI_QOS_WRPRI12 */ ++ len = (AXI_QOS_WRPRI12 - AXI_QOS_WRPRI0) / REG_STEP; ++ for (i = 0; i <= len; i++) ++ writel(REG_VALUE, (uintptr_t)(QOS_PRI_BASE + AXI_QOS_WRPRI0 + i * REG_STEP)); ++ ++ /* AXI_QOS_RDPRI0 - AXI_QOS_RDPRI12 */ ++ len = (AXI_QOS_RDPRI12 - AXI_QOS_RDPRI0) / REG_STEP; ++ for (i = 0; i <= len; i++) ++ writel(REG_VALUE, (uintptr_t)(QOS_PRI_BASE + AXI_QOS_RDPRI0 + i * REG_STEP)); ++ ++ /* AXI_QOS_MAP0 - AXI_QOS_MAP12 */ ++ len = (AXI_QOS_MAP12 - AXI_QOS_MAP0) / REG_STEP; ++ for (i = 0; i <= len; i++) ++ writel(AXI_QOS_MAP_VALUE, (uintptr_t)(QOS_PRI_BASE + AXI_QOS_MAP0 + ++ i * REG_STEP)); ++ ++ return 0; ++} ++ ++static unsigned int npu_get_stable_power_cpm(void) ++{ ++ unsigned int power_cpm = 0; ++ unsigned int val; ++ unsigned int mask = 1; ++ ++ do { ++ power_cpm += mask; ++ ++ if (power_cpm > NPU_CPM_POWER_MAX_VAL) ++ return 0; ++ ++ if (power_cpm == (mask << NPU_CPM_POWER_STEP) - 1) ++ mask <<= NPU_CPM_POWER_STEP; ++ ++ writel(power_cpm, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_POWER_CPM_OFFSET)); ++ udelay(1); /* delay 1 us */ ++ ++ val = (readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_MA_VAL_OFFSET)) & NPU_CPM_MA_MASK); ++ } while (val < NPU_CPM_MA_MIN_VAL || val > NPU_CPM_MA_MIN_MAX); ++ ++ return power_cpm; ++} ++ ++int config_pi_defense_registers(void) ++{ ++ unsigned int index; ++ unsigned int times = 0; ++ unsigned int val = 0; ++ unsigned int reg_val = 0; ++ ++ reg_val = readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ reg_val += NPU_VOL_OFFSET_VAL; ++ writel(reg_val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ udelay(NPU_DELAY_TIME_US); ++ ++ /* 1. FFS clk */ ++ writel(NPU_FFS_RESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CLK_OFFSET)); /* FFS reset */ ++ writel(NPU_CLK_CTRL_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CLK_CTRL_OFFSET)); ++ writel(NPU_DROP_FLAG_VAL, (uintptr_t)(SOC_NPU_TOP_BASE_ADDR + NPU_TOP_DROP_FLAG_OFFSET)); ++ ++ /* 2. static config */ ++ writel(NPU_FFS_DEF_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CONFIG_OFFSET)); /* FFS */ ++ writel(NPU_CPM_RESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CPM_CLK_OFFSET)); /* CPM reset */ ++ writel(NPU_CPM_DEF_VAL, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_CONFIG_OFFSET)); ++ ++ /* 3. detect ma */ ++ writel(NPU_CPM_UNRESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CPM_CLK_OFFSET)); /* CPM unreset */ ++ reg_val = npu_get_stable_power_cpm(); ++ if (reg_val == 0) ++ goto npu_clk_disable; ++ writel(reg_val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_POWER_CPM_OFFSET)); ++ ++ /* 4. Threshold */ ++ for (index = 0; index < NPU_GET_MA_TIMES; index++) { ++ reg_val = (readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_MA_VAL_OFFSET)) & NPU_CPM_MA_MASK); ++ if (reg_val < NPU_CPM_MA_MIN_VAL || reg_val > NPU_CPM_MA_MIN_MAX) ++ continue; ++ val += reg_val; ++ times++; ++ } ++ ++ if (times == 0) ++ goto npu_clk_disable; ++ ++ val /= times; ++ val = (val - NPU_CPM_THRESHOLD_DIFF) << NPU_CPM_THRESHOLD_BIT; ++ val += readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_CONFIG_OFFSET)); ++ writel(val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_CONFIG_OFFSET)); ++ ++ /* 5. freq enable */ ++ writel(NPU_FFS_UNRESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CLK_OFFSET)); /* FFS unreset */ ++ udelay(NPU_CRG_DELAY_TIME); ++ ++ val = readl((uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_STATE_OFFSET)); ++ if ((val & NPU_FFS_STATE_MASK_VAL) == 0) ++ writel(NPU_FFS_RESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CLK_OFFSET)); /* FFS reset */ ++ ++ writel(NPU_FFS_CLK_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CONFIG_OFFSET)); ++ ++npu_clk_disable: ++ /* 6. npu clk disable */ ++ writel(NPU_CLK_DISABLE_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CLK_CTRL_OFFSET)); ++ ++ reg_val = readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ reg_val -= NPU_VOL_OFFSET_VAL; ++ writel(reg_val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ ++ return 0; ++} ++ ++int start_riscv(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned long addr; ++ ++ if (argc < 2) ++ return CMD_RET_USAGE; ++ addr = simple_strtoul(argv[1], NULL, 16); /* 16 Hexadecimal */ ++ ++ /* start up riscv */ ++ { ++ printf ("## Starting RISCV UP at 0x%016lX ...\n", addr); ++ __asm_flush_dcache_all(); ++ writel(addr, 0x110D2004);//set start addr ++ ++ writel(0x10, 0x11024000);//jtag to mcu ++ writel(0x1, 0x110D2000);//core wait ++ writel(0x3, 0x11016400);//rst ++ ++ writel(0x0, 0x110D2000);//unwait ++ writel(0x4030, 0x11016400);//unrst ++ } ++ return 0; ++} ++ ++static int do_watch_dog_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int reg_val = 0; ++ ++ printf("watch dog reset ...\n"); ++ mdelay(10); /* delay 10 ms */ ++ reg_val = readl((uintptr_t)REG_MISC_CTRL3); ++ reg_val |= WATCH_DOG_MODE; ++ writel(reg_val, REG_MISC_CTRL3); ++ writel(WATCH_DOG_LOAD_VAL, REG_BASE_WATCH_DOG); ++ reg_val = readl((uintptr_t)WATCH_DOG_CONTROL); ++ reg_val |= WATCH_DOG_ENABLE; ++ writel(reg_val, WATCH_DOG_CONTROL); ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ go_riscv, CONFIG_SYS_MAXARGS, 1, start_riscv, ++ "start riscv at address 'addr'", ++ "addr [arg ...]\n - start riscv application at address 'addr'\n" ++ " passing 'arg' as arguments" ++); ++ ++U_BOOT_CMD( ++ dog_reset, CONFIG_SYS_MAXARGS, 0, do_watch_dog_reset, ++ "watchdog reset system", ++ "watchdog reset \n" ++); +diff --git a/board/vendor/ss928v100/Kconfig b/board/vendor/ss928v100/Kconfig +new file mode 100644 +index 0000000..9b5cf23 +--- /dev/null ++++ b/board/vendor/ss928v100/Kconfig +@@ -0,0 +1,15 @@ ++if TARGET_SS928V100 ++ ++config SYS_BOARD ++ default "ss928v100" ++ ++config SYS_VENDOR ++ default "vendor" ++ ++config SYS_SOC ++ default "ss928v100" ++ ++config SYS_CONFIG_NAME ++ default "ss928v100" ++ ++endif +diff --git a/board/vendor/ss928v100/Makefile b/board/vendor/ss928v100/Makefile +new file mode 100644 +index 0000000..cdd7b20 +--- /dev/null ++++ b/board/vendor/ss928v100/Makefile +@@ -0,0 +1,8 @@ ++# ++# (C) Copyright 2000-2004 ++# Wolfgang Denk, DENX Software Engineering, wd@denx.de. ++# ++# SPDX-License-Identifier: GPL-2.0+ ++# ++ ++obj-y := ss928v100.o +diff --git a/board/vendor/ss928v100/ss928v100.c b/board/vendor/ss928v100/ss928v100.c +new file mode 100644 +index 0000000..0e7f18b +--- /dev/null ++++ b/board/vendor/ss928v100/ss928v100.c +@@ -0,0 +1,792 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mm_region ss928v100_mem_map[] = { ++ { ++ .virt = 0x0UL, ++ .phys = 0x0UL, ++ .size = 0x05000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE ++ }, ++ { ++ .virt = 0x05000000UL, ++ .phys = 0x05000000UL, ++ .size = 0x40000000UL - 0x05000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, ++ { ++ .virt = 0x40000000UL, ++ .phys = 0x40000000UL, ++ .size = 0x200000000UL, /* PHYS_SDRAM_1_SIZE */ ++ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | ++ PTE_BLOCK_INNER_SHARE ++ }, ++ { ++ /* List terminator */ ++ 0, ++ } ++}; ++ ++struct mm_region *mem_map = ss928v100_mem_map; ++static int boot_media = BOOT_MEDIA_UNKNOWN; ++ ++#if defined(CONFIG_SHOW_BOOT_PROGRESS) ++void show_boot_progress(int progress) ++{ ++ printf("Boot reached stage %d\n", progress); ++} ++#endif ++ ++#define COMP_MODE_ENABLE ((unsigned int)0x0000EAEF) ++ ++static inline void delay(unsigned long loops) ++{ ++ __asm__ volatile ("1:\n" ++ "subs %0, %1, #1\n" ++ "bne 1b" : "=r" (loops) : "0" (loops)); ++} ++ ++int is_tee_enable_otp(void) ++{ ++ if ((NUM_FF & readl(OTP_USER_LOCKABLE0)) == OTP_SOC_TEE_DISABLE_FLAG) ++ return OTP_TEE_DISABLE; /* tee is disable */ ++ else ++ return OTP_TEE_ENABLE; /* tee is enable */ ++} ++ ++int check_otp_cmd_mode(void) ++{ ++ unsigned int el; ++ if (is_tee_enable_otp() == OTP_TEE_ENABLE) { /* tee is enable */ ++ asm volatile("mrs %0, CurrentEL" : "=r" (el) : : "cc"); ++ el >>= 2; /* Move Left 2 bit */ ++ if (el == EXCEPTION_LEVEL1) ++ return OTP_REE_CMD_MODE; ++ else if (el == EXCEPTION_LEVEL3) ++ return OTP_TEE_CMD_MODE; ++ } ++ ++ return OTP_TEE_CMD_MODE; /* TEE mode */ ++} ++ ++/* get uboot start media. */ ++int get_boot_media(void) ++{ ++ return boot_media; ++} ++ ++int get_text_base(void) ++{ ++ return CONFIG_SYS_TEXT_BASE; ++} ++ ++static void boot_flag_init(void) ++{ ++ unsigned int regval, boot_mode; ++ ++ /* get boot mode */ ++ regval = __raw_readl(SYS_CTRL_REG_BASE + REG_SYSSTAT); ++ boot_mode = get_sys_boot_mode(regval); ++ ++ switch (boot_mode) { ++ /* [3:2] 00b - boot from Spi Nor device */ ++ case BOOT_FROM_SPI: ++ boot_media = BOOT_MEDIA_SPIFLASH; ++ break; ++ /* [3:2] 01b - boot from Spi Nand device */ ++ case BOOT_FROM_SPI_NAND: ++ boot_media = BOOT_MEDIA_NAND; ++ break; ++ /* [3:2] 10b - boot from Nand device */ ++ case BOOT_FROM_NAND: ++ boot_media = BOOT_MEDIA_NAND; ++ break; ++ /* [3:2] 11b - boot from emmc */ ++ case BOOT_FROM_EMMC: ++ boot_media = BOOT_MEDIA_EMMC; ++ break; ++ default: ++ boot_media = BOOT_MEDIA_UNKNOWN; ++ break; ++ } ++} ++ ++int board_early_init_f(void) ++{ ++ return 0; ++} ++ ++#define UBOOT_DATA_ADDR 0x41000000UL ++#define UBOOT_DATA_SIZE 0x80000UL ++int data_to_spiflash(void) ++{ ++ static struct spi_flash *flash = NULL; ++ void *buf = NULL; ++ int spi_flash_erase_ret; ++ ssize_t val; ++ ++ /* 0:bus 0:cs 1000000:max_hz 0x3:spi_mode */ ++ flash = spi_flash_probe(0, 0, 1000000, 0x3); ++ if (!flash) { ++ printf("Failed to initialize SPI flash\n"); ++ return -1; ++ } ++ ++ /* erase the address range. */ ++ printf("Spi flash erase...\n"); ++ spi_flash_erase_ret = spi_flash_erase(flash, NUM_0, UBOOT_DATA_SIZE); ++ if (spi_flash_erase_ret) { ++ printf("SPI flash sector erase failed\n"); ++ return 1; ++ } ++ ++ buf = map_physmem((unsigned long)UBOOT_DATA_ADDR, ++ UBOOT_DATA_SIZE, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ /* copy the data from RAM to FLASH */ ++ printf("Spi flash write...\n"); ++ val = flash->write(flash, NUM_0, UBOOT_DATA_SIZE, buf); ++ if (val) { ++ printf("SPI flash write failed, return %zd\n", val); ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 1; ++ } ++ ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 0; /* 0:success */ ++} ++ ++int data_to_nandflash(void) ++{ ++ struct mtd_info *nand_flash = NULL; ++ void *buf = NULL; ++ size_t length = UBOOT_DATA_SIZE; ++ int val; ++ ++ nand_flash = nand_info[0]; ++ ++ printf("Nand flash erase...\n"); ++ val = nand_erase(nand_flash, 0, UBOOT_DATA_SIZE); ++ if (val) { ++ printf("Nand flash erase failed\n"); ++ return 1; ++ } ++ ++ buf = map_physmem((unsigned long)UBOOT_DATA_ADDR, ++ UBOOT_DATA_SIZE, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ printf("Nand flash write...\n"); ++ val = nand_write(nand_flash, 0, &length, buf); ++ if (val) { ++ printf("Nand flash write failed, return %d\n", val); ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 1; ++ } ++ ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 0; ++} ++ ++int data_to_emmc(void) ++{ ++ struct mmc *mmc = find_mmc_device(0); ++ void *buf = NULL; ++ ++ if (!mmc) ++ return 1; ++ ++ (void)mmc_init(mmc); ++ ++ buf = map_physmem((unsigned long)UBOOT_DATA_ADDR, ++ UBOOT_DATA_SIZE, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ printf("MMC write...\n"); ++ blk_dwrite(mmc_get_blk_desc(mmc), 0, (UBOOT_DATA_SIZE >> NUM_9), buf); ++ unmap_physmem(buf, UBOOT_DATA_SIZE); ++ return 0; ++} ++ ++int save_bootdata_to_flash(void) ++{ ++ unsigned int sd_update_flag; ++ int ret; ++ sd_update_flag = readl(REG_BASE_SCTL + REG_SC_GEN4); ++ if (sd_update_flag == START_MAGIC) { ++#if defined(CONFIG_FMC) ++ if (boot_media == BOOT_MEDIA_SPIFLASH) { ++ ret = data_to_spiflash(); ++ if (ret != 0) ++ return ret; ++ } ++ if (boot_media == BOOT_MEDIA_NAND) { ++ ret = data_to_nandflash(); ++ if (ret != 0) ++ return ret; ++ } ++#endif ++#if defined(CONFIG_MMC) ++ if (boot_media == BOOT_MEDIA_EMMC) { ++ ret = data_to_emmc(); ++ if (ret != 0) ++ return ret; ++ } ++#endif ++ ++ printf("update success!\n"); ++ } ++ ++ return 0; ++} ++ ++int auto_update_flag = 0; ++int bare_chip_program = 0; ++ ++#define REG_BASE_GPIO0 0x11090000 ++#define GPIO0_0_DATA_OFST 0x4 ++#define GPIO_DIR_OFST 0x400 ++#define PROC_TIME_OUT 100 ++#define PROC_LOOP 5 ++#define DOWNLOAD_FLAG 0x1f ++#define ACK 0xAA ++ ++int is_bare_program(void) ++{ ++ return 1; ++} ++ ++/* Connect to TOOLS */ ++int uart_self_boot_check(void) ++{ ++ uint32_t count, i; ++ uint32_t timer_count; ++ unsigned char cr; ++ ++ for (count = 0; count < PROC_LOOP; ++count) { ++ for (i = 0; i < PROC_LOOP; i++) ++ serial_putc((unsigned char) DOWNLOAD_FLAG); ++ ++ timer_count = 0; ++ while (timer_count < PROC_TIME_OUT) { ++ if (serial_tstc()) { ++ cr = (unsigned char)serial_getc(); ++ if (cr == ACK) ++ return 1; ++ } ++ timer_count++; ++ udelay(100); /* delay 100 us */ ++ } ++ } ++ serial_putc((unsigned char) '\n'); ++ ++ return 0; ++} ++ ++/* return the flag for usb/sd update */ ++int check_usb_sd_update_flag(void) ++{ ++ u32 flag; ++ ++ writel(0x0, REG_BASE_GPIO0 + GPIO_DIR_OFST); ++ ++ flag = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ if (!flag) { ++ mdelay(10); /* delay 10 ms */ ++ flag = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ if (!flag) { ++ mdelay(10); /* delay 10 ms */ ++ flag = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ } ++ } ++ ++ return (!flag); ++} ++ ++void set_bootloader_download_process_flag(void) ++{ ++ uint32_t channel_type; ++ ++ if (readl(REG_START_FLAG) == START_MAGIC) ++ return; ++ ++ channel_type = readl(REG_BASE_SCTL + REG_DATA_CHANNEL_TYPE); ++ switch (channel_type) { ++ case BOOT_SEL_PCIE: ++ return; ++ default: ++ break; ++ } ++ ++ /* set download flag */ ++ if (uart_self_boot_check()) { ++ writel(START_MAGIC, REG_START_FLAG); ++ return; ++ } ++ ++ if (check_usb_sd_update_flag()) { ++ writel(START_MAGIC, REG_START_FLAG); ++ writel(SELF_BOOT_TYPE_USBDEV, SYS_CTRL_REG_BASE + REG_SC_GEN9); ++ return; ++ } ++} ++ ++int is_auto_update(void) ++{ ++#if (CONFIG_AUTO_SD_UPDATE == 1) || (CONFIG_AUTO_USB_UPDATE == 1) ++ /* to add some judgement if neccessary */ ++ unsigned int val[NUM_3]; ++ ++ writel(0, REG_BASE_GPIO0 + GPIO_DIR_OFST); ++ ++ val[NUM_0] = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ if (val[NUM_0]) ++ return 0; ++ ++ udelay(10000); /* delay 10000 us */ ++ val[NUM_1] = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ udelay(10000); /* delay 10000 us */ ++ val[NUM_2] = readl(REG_BASE_GPIO0 + GPIO0_0_DATA_OFST); ++ udelay(10000); /* delay 10000 us */ ++ ++ if (val[NUM_0] == val[NUM_1] && val[NUM_1] == val[NUM_2] && val[NUM_0] == NUM_0) ++ return 1; /* update enable */ ++ else ++ return 0; ++ ++#else ++ return 0; ++#endif ++} ++ ++void set_pcie_para_ss928v100(void) ++{ ++ unsigned int val; ++ val = readl(SYS_CTRL_REG_BASE + SYS_SATA); ++ if ((val & (0x3 << PCIE_MODE)) == 0) { ++ /* X2 release phy reset */ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val &= ((~(0x1 << PHY1_SRS_REQ)) & (~(0x1 << PHY0_SRS_REQ))); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ ++ /*X2 select phy reset from crg*/ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val |= (0x1 << PHY1_SRS_REQ_SEL) | (0x1 << PHY0_SRS_REQ_SEL); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X2 seperate_rate=1 ++ */ ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x94f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x92f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x96f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x92f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X2 split_cp_dis ++ */ ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd51, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd31, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd71, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd31, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ } else { ++ /*X1 release phy reset*/ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val &= ~(0x1 << PHY0_SRS_REQ); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ ++ /*X1 select phy reset from crg*/ ++ val = readl(CRG_REG_BASE + PERI_CRG98); ++ val |= (0x1 << PHY0_SRS_REQ_SEL); ++ writel(val, CRG_REG_BASE + PERI_CRG98); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X1 seperate_rate=1 ++ */ ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x94f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x90f, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ ++ /* ++ * X1 split_cp_dis ++ */ ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd51, MISC_REG_BASE + MISC_CTRL5); ++ writel(0xd11, MISC_REG_BASE + MISC_CTRL5); ++ writel(0x0, MISC_REG_BASE + MISC_CTRL5); ++ mdelay(10); /* delay 10 ms */ ++ }; ++} ++ ++int misc_init_r(void) ++{ ++#ifdef CONFIG_RANDOM_ETHADDR ++ random_init_r(); ++#endif ++ env_set("verify", "n"); ++ ++#if (CONFIG_AUTO_UPDATE == 1) ++ /* auto update flag */ ++ if (is_auto_update()) ++ auto_update_flag = 1; ++ else ++ auto_update_flag = 0; ++ ++ /* bare chip program flag */ ++ if (is_bare_program()) ++ bare_chip_program = 1; ++ else ++ bare_chip_program = 0; ++ ++#ifdef CFG_MMU_HANDLEOK ++ dcache_stop(); ++#endif ++ ++#ifdef CFG_MMU_HANDLEOK ++ dcache_start(); ++#endif ++ ++#endif /*CONFIG_AUTO_UPDATE*/ ++ ++#if (CONFIG_AUTO_UPDATE == 1) ++ if (auto_update_flag) ++ do_auto_update(); ++ if (bare_chip_program && !auto_update_flag) ++ save_bootdata_to_flash(); ++#endif ++ set_bootloader_download_process_flag(); ++ return 0; ++} ++ ++int board_init(void) ++{ ++ DECLARE_GLOBAL_DATA_PTR; ++ ++ gd->bd->bi_arch_number = MACH_TYPE_SS928V100; ++ gd->bd->bi_boot_params = CFG_BOOT_PARAMS; ++ ++ boot_flag_init(); ++ ++ return 0; ++} ++ ++int dram_init(void) ++{ ++ DECLARE_GLOBAL_DATA_PTR; ++ ++ gd->ram_size = PHYS_SDRAM_1_SIZE; ++ return 0; ++} ++ ++void reset_cpu(ulong addr) ++{ ++ writel(0x12345678, REG_BASE_SCTL + REG_SC_SYSRES); ++ while (1); ++} ++ ++int timer_init(void) ++{ ++ /* ++ * *Under uboot, 0xffffffff is set to load register, ++ * * timer_clk = BUSCLK/2/256. ++ * * e.g. BUSCLK = 50M, it will roll back after 0xffffffff/timer_clk ++ * * = 43980s = 12hours ++ * */ ++ __raw_writel(0, CFG_TIMERBASE + REG_TIMER_CONTROL); ++ __raw_writel(~0, CFG_TIMERBASE + REG_TIMER_RELOAD); ++ ++ /*32 bit, periodic*/ ++ __raw_writel(CFG_TIMER_CTRL, CFG_TIMERBASE + REG_TIMER_CONTROL); ++ ++ return 0; ++} ++ ++int board_eth_init(bd_t *bis) ++{ ++ int rc = 0; ++ ++#ifdef CONFIG_GMACV300_ETH ++ rc = gmac_initialize(bis); ++#endif ++ return rc; ++} ++ ++#ifdef CONFIG_GENERIC_MMC ++int board_mmc_init(bd_t *bis) ++{ ++ int ret = 0; ++ ++#ifdef CONFIG_MMC_SDHCI ++ ++#if!defined(CONFIG_FMC) || defined(CONFIG_AUTO_SD_UPDATE) ++ int dev_num = 0; ++#endif ++ ++#ifndef CONFIG_FMC ++ ret = sdhci_add_port(0, EMMC_BASE_REG, MMC_TYPE_MMC); ++ if (!ret) { ++ ret = bsp_mmc_init(dev_num); ++ if (ret) ++ printf("No EMMC device found !\n"); ++ } ++ dev_num++; ++#endif ++ ++#ifdef CONFIG_AUTO_SD_UPDATE ++ if (is_auto_update()) { ++ ret = sdhci_add_port(1, SDIO0_BASE_REG, MMC_TYPE_SD); ++ if (ret) ++ return ret; ++ ++ ret = bsp_mmc_init(dev_num); ++ if (ret) ++ printf("No SD device found !\n"); ++ } ++#endif ++#endif ++ ++ return ret; ++} ++#endif ++ ++#define QOS_PRI_BASE 0x11140000 ++#define REG_STEP 0x10 ++ ++#define AXI_QOS_WRPRI0 0x0204 ++#define AXI_QOS_WRPRI12 0x02c4 ++ ++#define AXI_QOS_RDPRI0 0x0208 ++#define AXI_QOS_RDPRI12 0x02c8 ++ ++#define AXI_QOS_MAP0 0x0200 ++#define AXI_QOS_MAP12 0x02c0 ++ ++#define REG_VALUE 0x01234567 ++#define AXI_QOS_MAP_VALUE 0x00110000 ++ ++int config_qos_registers(void) ++{ ++ unsigned int i, len; ++ ++ /* AXI_QOS_WRPRI0 - AXI_QOS_WRPRI12 */ ++ len = (AXI_QOS_WRPRI12 - AXI_QOS_WRPRI0) / REG_STEP; ++ for (i = 0; i <= len; i++) ++ writel(REG_VALUE, (uintptr_t)(QOS_PRI_BASE + AXI_QOS_WRPRI0 + i * REG_STEP)); ++ ++ /* AXI_QOS_RDPRI0 - AXI_QOS_RDPRI12 */ ++ len = (AXI_QOS_RDPRI12 - AXI_QOS_RDPRI0) / REG_STEP; ++ for (i = 0; i <= len; i++) ++ writel(REG_VALUE, (uintptr_t)(QOS_PRI_BASE + AXI_QOS_RDPRI0 + i * REG_STEP)); ++ ++ /* AXI_QOS_MAP0 - AXI_QOS_MAP12 */ ++ len = (AXI_QOS_MAP12 - AXI_QOS_MAP0) / REG_STEP; ++ for (i = 0; i <= len; i++) ++ writel(AXI_QOS_MAP_VALUE, (uintptr_t)(QOS_PRI_BASE + AXI_QOS_MAP0 + ++ i * REG_STEP)); ++ ++ return 0; ++} ++ ++static unsigned int npu_get_stable_power_cpm(void) ++{ ++ unsigned int power_cpm = 0; ++ unsigned int val; ++ unsigned int mask = 1; ++ ++ do { ++ power_cpm += mask; ++ ++ if (power_cpm > NPU_CPM_POWER_MAX_VAL) ++ return 0; ++ ++ if (power_cpm == (mask << NPU_CPM_POWER_STEP) - 1) ++ mask <<= NPU_CPM_POWER_STEP; ++ ++ writel(power_cpm, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_POWER_CPM_OFFSET)); ++ udelay(1); /* delay 1 us */ ++ ++ val = (readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_MA_VAL_OFFSET)) & NPU_CPM_MA_MASK); ++ } while (val < NPU_CPM_MA_MIN_VAL || val > NPU_CPM_MA_MIN_MAX); ++ ++ return power_cpm; ++} ++ ++int config_pi_defense_registers(void) ++{ ++ unsigned int index; ++ unsigned int times = 0; ++ unsigned int val = 0; ++ unsigned int reg_val = 0; ++ ++ reg_val = readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ reg_val += NPU_VOL_OFFSET_VAL; ++ writel(reg_val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ udelay(NPU_DELAY_TIME_US); ++ ++ /* 1. FFS clk */ ++ writel(NPU_FFS_RESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CLK_OFFSET)); /* FFS reset */ ++ writel(NPU_CLK_CTRL_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CLK_CTRL_OFFSET)); ++ writel(NPU_DROP_FLAG_VAL, (uintptr_t)(SOC_NPU_TOP_BASE_ADDR + NPU_TOP_DROP_FLAG_OFFSET)); ++ ++ /* 2. static config */ ++ writel(NPU_FFS_DEF_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CONFIG_OFFSET)); /* FFS */ ++ writel(NPU_CPM_RESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CPM_CLK_OFFSET)); /* CPM reset */ ++ writel(NPU_CPM_DEF_VAL, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_CONFIG_OFFSET)); ++ ++ /* 3. detect ma */ ++ writel(NPU_CPM_UNRESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CPM_CLK_OFFSET)); /* CPM unreset */ ++ reg_val = npu_get_stable_power_cpm(); ++ if (reg_val == 0) ++ goto npu_clk_disable; ++ writel(reg_val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_POWER_CPM_OFFSET)); ++ ++ /* 4. Threshold */ ++ for (index = 0; index < NPU_GET_MA_TIMES; index++) { ++ reg_val = (readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_MA_VAL_OFFSET)) & NPU_CPM_MA_MASK); ++ if (reg_val < NPU_CPM_MA_MIN_VAL || reg_val > NPU_CPM_MA_MIN_MAX) ++ continue; ++ val += reg_val; ++ times++; ++ } ++ ++ if (times == 0) ++ goto npu_clk_disable; ++ ++ val /= times; ++ val = (val - NPU_CPM_THRESHOLD_DIFF) << NPU_CPM_THRESHOLD_BIT; ++ val += readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_CONFIG_OFFSET)); ++ writel(val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_CPM_CONFIG_OFFSET)); ++ ++ /* 5. freq enable */ ++ writel(NPU_FFS_UNRESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CLK_OFFSET)); /* FFS unreset */ ++ udelay(NPU_CRG_DELAY_TIME); ++ ++ val = readl((uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_STATE_OFFSET)); ++ if ((val & NPU_FFS_STATE_MASK_VAL) == 0) ++ writel(NPU_FFS_RESET_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CLK_OFFSET)); /* FFS reset */ ++ ++ writel(NPU_FFS_CLK_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_FFS_CONFIG_OFFSET)); ++ ++npu_clk_disable: ++ /* 6. npu clk disable */ ++ writel(NPU_CLK_DISABLE_VAL, (uintptr_t)(SOC_CRG_BASE_ADDR + CRG_NPU_CLK_CTRL_OFFSET)); ++ ++ reg_val = readl((uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ reg_val -= NPU_VOL_OFFSET_VAL; ++ writel(reg_val, (uintptr_t)(SOC_SYS_BASE_ADDR + SYS_NPU_VOL_OFFSET)); ++ ++ return 0; ++} ++ ++int start_riscv(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned long addr; ++ ++ if (argc < 2) ++ return CMD_RET_USAGE; ++ addr = simple_strtoul(argv[1], NULL, 16); /* 16 Hexadecimal */ ++ ++ /* start up riscv */ ++ { ++ printf ("## Starting RISCV UP at 0x%016lX ...\n", addr); ++ __asm_flush_dcache_all(); ++ writel(addr, 0x110D2004);//set start addr ++ ++ writel(0x10, 0x11024000);//jtag to mcu ++ writel(0x1, 0x110D2000);//core wait ++ writel(0x3, 0x11016400);//rst ++ ++ writel(0x0, 0x110D2000);//unwait ++ writel(0x4030, 0x11016400);//unrst ++ } ++ return 0; ++} ++ ++static int do_watch_dog_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int reg_val = 0; ++ ++ printf("watch dog reset ...\n"); ++ mdelay(10); /* delay 10 ms */ ++ reg_val = readl((uintptr_t)REG_MISC_CTRL3); ++ reg_val |= WATCH_DOG_MODE; ++ writel(reg_val, REG_MISC_CTRL3); ++ writel(WATCH_DOG_LOAD_VAL, REG_BASE_WATCH_DOG); ++ reg_val = readl((uintptr_t)WATCH_DOG_CONTROL); ++ reg_val |= WATCH_DOG_ENABLE; ++ writel(reg_val, WATCH_DOG_CONTROL); ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ go_riscv, CONFIG_SYS_MAXARGS, 1, start_riscv, ++ "start riscv at address 'addr'", ++ "addr [arg ...]\n - start riscv application at address 'addr'\n" ++ " passing 'arg' as arguments" ++); ++ ++U_BOOT_CMD( ++ dog_reset, CONFIG_SYS_MAXARGS, 0, do_watch_dog_reset, ++ "watchdog reset system", ++ "watchdog reset \n" ++); +diff --git a/cmd/Kconfig b/cmd/Kconfig +index 1e4cf14..380764e 100644 +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -553,6 +553,12 @@ config CMD_NVEDIT_INFO + [-p] : evaluate whether environment can be persisted + The result of multiple evaluations will be combined with AND. + ++config CMD_COPYENV ++ bool "copy env" ++ default n ++ help ++ copy specified env to dst memory. such as: envcopy bootargs 0x80000020 ++ + endmenu + + menu "Memory commands" +@@ -723,6 +729,12 @@ config CMD_STRINGS + within the range are displayed. The minimum number of characters + for a sequence to be considered a string can be provided. + ++config CMD_DDR_TRAINING ++ bool "ddr training" ++ default y ++ help ++ ddr training function ++ + endmenu + + menu "Compression commands" +@@ -746,6 +758,17 @@ config CMD_ZIP + help + Compress a memory region with zlib deflate method. + ++config CMD_CREAD ++ bool "Enable cycle read function" ++ help ++ Enable the function for cycle read. ++ ++config CMD_UGZIP ++ bool "ugzip" ++ select HWDEC ++ help ++ UnCompress a gzip file with hardware umcompress ip of chip. ++ + endmenu + + menu "Device access commands" +@@ -1048,7 +1071,7 @@ config CMD_MTD + config CMD_NAND + bool "nand" + default y if NAND_SUNXI +- depends on MTD_RAW_NAND ++ depends on MTD_RAW_NAND || FMC_SPI_NAND || FMC_NAND + help + NAND support. + +@@ -1172,7 +1195,7 @@ config CMD_SDRAM + + config CMD_SF + bool "sf" +- depends on DM_SPI_FLASH || SPI_FLASH ++ depends on DM_SPI_FLASH || SPI_FLASH || FMC_SPI_NOR + default y if DM_SPI_FLASH + help + SPI Flash support +@@ -1227,6 +1250,7 @@ config CMD_UNIVERSE + config CMD_USB + bool "usb" + select HAVE_BLOCK_DEVICE ++ depends on USB_GADGET + help + USB support. + +diff --git a/cmd/Makefile b/cmd/Makefile +index 3ac7104..654b76d 100644 +--- a/cmd/Makefile ++++ b/cmd/Makefile +@@ -184,6 +184,8 @@ endif # !CONFIG_SPL_BUILD + + # core command + obj-y += nvedit.o ++obj-y += vendor/getinfo.o ++obj-$(CONFIG_CMD_USB) += usbtftp.o + + obj-$(CONFIG_TI_COMMON_CMD_OPTIONS) += ti/ + +@@ -224,3 +226,11 @@ $(obj)/license_data_size.h: $(srctree)/Licenses/gpl-2.0.txt FORCE + $(call filechk,data_size) + + CFLAGS_ethsw.o := -Wno-enum-conversion ++ ++# osd command ++sinclude $(srctree)/cmd/Makefile-otproduct ++ ++obj-$(CONFIG_CMD_CREAD) += cmd_cycle.o ++obj-$(CONFIG_CMD_UGZIP) += cmd_ugzip.o ++obj-$(CONFIG_CMD_COPYENV) += nvcopy.o ++obj-$(CONFIG_CMD_TIMESTAMP) += cmd_timestamp.o +diff --git a/cmd/Makefile-otproduct b/cmd/Makefile-otproduct +new file mode 100644 +index 0000000..3f81448 +--- /dev/null ++++ b/cmd/Makefile-otproduct +@@ -0,0 +1,105 @@ ++# for audio ++ifeq ($(CONFIG_AUDIO_ENABLE),y) ++ifeq ($(CONFIG_PRODUCTNAME),$(filter $(CONFIG_PRODUCTNAME), "ss101v500" "ss101v200" "ss101v300" "ss101v600")) ++AO_ARCH_NAME = ss101v200 ++ACODEC_NAME = v750 ++endif ++cflags-y += -I$(srctree)/product/audio/ao/$(AO_ARCH_NAME) ++cflags-y += -I$(srctree)/product/audio/acodec/$(ACODEC_NAME) ++ccflags-y += $(cflags-y) ++obj-y += cmd_ao.o ++endif ++ ++# Osd command ++ifeq ($(CONFIG_OSD_ENABLE),y) ++# for dec ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v500") ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v200") ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v300") ++ifneq ($(CONFIG_PRODUCTNAME),"ss101v600") ++obj-y += cmd_dec.o ++endif ++endif ++endif ++endif ++ifeq ($(CONFIG_PRODUCTNAME),"ss919v100") ++EXTRA_CFLAGS += -DRGB_OUTPUT_ENABLE ++endif ++ifeq ($(CONFIG_PRODUCTNAME),"ss015v100") ++EXTRA_CFLAGS += -DRGB_OUTPUT_ENABLE ++endif ++# for vo ++ifeq ($(CONFIG_PRODUCTNAME),"ss919v100") ++ccflags-y += -DCONFIG_OT_HDMI_SUPPORT ++obj-y += cmd_vo_ss919v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss015v100") ++ccflags-y += -DCONFIG_OT_HDMI_SUPPORT ++obj-y += cmd_vo_ss015v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss918v100") ++obj-y += cmd_vo_ss918v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss013v100") ++obj-y += cmd_vo_ss918v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss813v100") ++obj-y += cmd_vo_ss812v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss815v100") ++obj-y += cmd_vo_ss812v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v200") ++obj-y += cmd_vo_ss101v200.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v500") ++obj-y += cmd_vo_ss101v200.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v300") ++obj-y += cmd_vo_ss101v200.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss101v600") ++obj-y += cmd_vo_ss101v200.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss313v100") ++obj-y += cmd_vo_ss812v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss011v100") ++obj-y += cmd_vo_ss812v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss012v100") ++obj-y += cmd_vo_ss812v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),"ss312v100") ++obj-y += cmd_vo_ss812v100.o ++else ifeq ($(CONFIG_PRODUCTNAME),$(filter $(CONFIG_PRODUCTNAME), "ss528v100" "ss625v100" "ss524v100" "ss522v101" "ss522v100" "ss615v100" "ss928v100" "ss927v100")) ++ ++ifeq ($(CONFIG_PRODUCTNAME),$(filter $(CONFIG_PRODUCTNAME), "ss524v100" "ss522v101" "ss522v100")) ++VO_ARCH_NAME = ss524v100 ++else ++VO_ARCH_NAME = $(CONFIG_PRODUCTNAME) ++endif ++ ++VO_SUB_ARCH_NAME = $(CONFIG_PRODUCTNAME) ++cflags-y += -I$(srctree)/product/ot_osd/vo/include ++cflags-y += -I$(srctree)/product/ot_osd/include ++cflags-y += -I$(srctree)/product/ot_osd/hdmi/hdmi_2_0 ++cflags-y += -I$(srctree)/product/ot_osd/vo/arch/$(VO_ARCH_NAME)/include ++cflags-y += -I$(srctree)/product/ot_osd/vo/arch/$(VO_ARCH_NAME)/include/$(VO_SUB_ARCH_NAME) ++ccflags-y += -DCONFIG_OT_HDMI_SUPPORT ++ccflags-y += $(cflags-y) ++obj-y += cmd_vo.o ++endif ++ ++ifeq ($(CONFIG_PRODUCTNAME), "ss528v100") ++ccflags-y += -DCHIP_SS528V100 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss625v100") ++ccflags-y += -DCHIP_SS625V100 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss524v100") ++ccflags-y += -DCHIP_SS524V100 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss615v100") ++ccflags-y += -DCHIP_SS615V100 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss522v100") ++ccflags-y += -DCHIP_SS524V100 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss522v101") ++ccflags-y += -DCHIP_SS522V101 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss928v100") ++ccflags-y += -DCHIP_SS928V100 ++ccflags-y += -DCONFIG_OT_MIPI_TX_SUPPORT ++cflags-y += -I$(srctree)/product/ot_osd/mipi_tx/ss928v100 ++ccflags-y += $(cflags-y) ++else ifeq ($(CONFIG_PRODUCTNAME), "ss927v100") ++ccflags-y += -DCHIP_SS927V100 ++ccflags-y += -DCONFIG_OT_MIPI_TX_SUPPORT ++cflags-y += -I$(srctree)/product/ot_osd/mipi_tx/ss928v100 ++ccflags-y += $(cflags-y) ++endif ++ ++endif +diff --git a/cmd/boot.c b/cmd/boot.c +index 9150fce..5d43df5 100644 +--- a/cmd/boot.c ++++ b/cmd/boot.c +@@ -12,6 +12,9 @@ + #include + + #ifdef CONFIG_CMD_GO ++#if defined(CONFIG_CMD_USB) ++#include ++#endif + + /* Allow ports to override the default behavior */ + __attribute__((weak)) +@@ -29,10 +32,24 @@ static int do_go(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + if (argc < 2) + return CMD_RET_USAGE; + ++#if defined(CONFIG_CMD_USB) ++ /* ++ * turn off USB to prevent the host controller from writing to the ++ * SDRAM while Linux is booting. This could happen (at least for OHCI ++ * controller), because the HCCA (Host Controller Communication Area) ++ * lies within the SDRAM and the host controller writes continously to ++ * this area (as busmaster!). The HccaFrameNumber is for example ++ * updated every 1 ms within the HCCA structure in SDRAM! For more ++ * details see the OpenHCI specification. ++ */ ++ usb_stop(); ++#endif ++ + addr = simple_strtoul(argv[1], NULL, 16); + + printf ("## Starting application at 0x%08lX ...\n", addr); + ++ cleanup_before_linux(); + /* + * pass address parameter as argv[0] (aka command name), + * and all remaining args +diff --git a/cmd/bootm.c b/cmd/bootm.c +index 62ee7c4..bc9b778 100644 +--- a/cmd/bootm.c ++++ b/cmd/bootm.c +@@ -20,9 +20,12 @@ + #include + #include + #include ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++#include "cmd_timestamp.h" ++#endif + + DECLARE_GLOBAL_DATA_PTR; +- ++#define TEE_ENABLE "tee_enable" + #if defined(CONFIG_CMD_IMI) + static int image_info(unsigned long addr); + #endif +@@ -37,6 +40,10 @@ extern flash_info_t flash_info[]; /* info for FLASH chips */ + static int do_imls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); + #endif + ++#ifdef CONFIG_CMD_USB ++#include ++#endif ++ + /* we overload the cmd field with our state machine info instead of a + * function pointer */ + static cmd_tbl_t cmd_bootm_sub[] = { +@@ -85,12 +92,66 @@ static int do_bootm_subcommand(cmd_tbl_t *cmdtp, int flag, int argc, + return ret; + } + ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++extern int is_tee_enable_otp(void); ++int tee_start_flow_enable(void) ++{ ++ char *bootargs = NULL; ++ ++ bootargs = env_get("bootargs"); ++ if (((bootargs == NULL)) || (strstr(bootargs ,TEE_ENABLE) == NULL)) ++ return 0; ++ ++ if (!is_tee_enable_otp()) { ++ printf("tee distable int otp\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++#endif ++ + /*******************************************************************/ + /* bootm - boot application image from image in memory */ + /*******************************************************************/ + + int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + { ++#ifdef CONFIG_CMD_USB ++ usb_stop(); ++#endif ++ ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ timestamp_mark("load_kernel_start", __LINE__); ++#endif ++ ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++ extern int load_fip_secure_os(char *common_os, char *secure_os); ++ extern long long kernel_load_addr; ++ if (0 != tee_start_flow_enable()) { ++ char *common_os = (char *)(uintptr_t)simple_strtoul(argv[1], NULL, 16); ++ char *secure_os = (char *)(uintptr_t)simple_strtoul(argv[2], NULL, 16); ++ kernel_load_addr = CONFIG_KERNEL_LOAD_ADDR; ++ return load_fip_secure_os(common_os, secure_os); ++ } ++#endif ++ ++#ifdef CONFIG_ARM64_SUPPORT_LOAD_FIP ++ extern int is_fip(const char *buf); ++ extern int load_fip(char *buf); ++ extern int load_fip_amp(char *buf); ++ extern long long kernel_load_addr; ++ if (argc == 2) { ++ char *buf = (char *)(uintptr_t)simple_strtoul(argv[1], NULL, 16); ++ /* Modify this configuration according to the system framework */ ++ kernel_load_addr = CONFIG_KERNEL_LOAD_ADDR; ++ if (is_fip(buf)) { ++ return load_fip(buf); ++ } ++ } ++#endif ++ + #ifdef CONFIG_NEEDS_MANUAL_RELOC + static int relocated = 0; + +@@ -123,6 +184,10 @@ int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + return do_bootm_subcommand(cmdtp, flag, argc, argv); + } + ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ timestamp_mark("load_kernel_end", __LINE__); ++#endif ++ + return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | + BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | + BOOTM_STATE_LOADOS | +diff --git a/cmd/cmd_ao.c b/cmd/cmd_ao.c +new file mode 100644 +index 0000000..3ebb150 +--- /dev/null ++++ b/cmd/cmd_ao.c +@@ -0,0 +1,145 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++ ++#include "audio_ao.h" ++#include "acodec.h" ++#include "amp.h" ++#include "ao.h" ++ ++#define CMD_AO_ARGS_BASE10 10 ++#define CMD_AO_ARGS_BASE16 16 ++ ++audio_sample_rate g_sample_rate_list[] = { ++ AUDIO_SAMPLE_RATE_8000, ++ AUDIO_SAMPLE_RATE_11025, ++ AUDIO_SAMPLE_RATE_12000, ++ AUDIO_SAMPLE_RATE_16000, ++ AUDIO_SAMPLE_RATE_22050, ++ AUDIO_SAMPLE_RATE_24000, ++ AUDIO_SAMPLE_RATE_32000, ++ AUDIO_SAMPLE_RATE_44100, ++ AUDIO_SAMPLE_RATE_48000 ++}; ++ ++int ao_check_param(unsigned int addr, unsigned int size, audio_sample_rate sample_rate, unsigned int chn_cnt, ++ int vol) ++{ ++ unsigned int rate_flag = 0; ++ unsigned int i; ++ ++ if (addr % 32) { /* 32: align */ ++ printf("[Error] Invalid addr parameter:0x%0x, address should be aligned by 32Byte!\n", addr); ++ return -1; ++ } ++ ++ if (size % 32) { /* 32: align */ ++ printf("[Warning] Invalid size parameter:0x%0x, size should be aligned by 32Byte!\n", size); ++ return -1; ++ } ++ ++ if (size == 0) { ++ printf("[Warning] Invalid size parameter:0x%0x, size should be greater than 0!\n", size); ++ return -1; ++ } ++ ++ for (i = 0; i < sizeof(g_sample_rate_list) / sizeof(g_sample_rate_list[0]); i++) { ++ if (sample_rate == g_sample_rate_list[i]) { ++ rate_flag = 1; ++ break; ++ } ++ } ++ ++ if (rate_flag == 0) { ++ printf("[Warning] Invalid samplerate parameter.\n"); ++ return -1; ++ } ++ ++ if (vol > 6 || vol < 0) { /* 6: max vol */ ++ printf("[Warning] Invalid volume parameter, range:[0, 6]dB!\n"); ++ return -1; ++ } ++ ++ if ((chn_cnt == 0) || (chn_cnt > 2)) { /* 2: max chn */ ++ printf("[Warning] Invalid channelnum parameter, range:[1, 2].\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int do_startao(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int addr, size; ++ int vol; ++ ++ audio_sample_rate sample_rate; ++ unsigned int chn_cnt; ++ ++ if (argc < 6) { /* 6: param num */ ++ printf("Insufficient parameter!\n"); ++ printf("Usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ addr = (unsigned int)simple_strtoul(argv[1], NULL, CMD_AO_ARGS_BASE16); ++ size = (unsigned int)simple_strtoul(argv[2], NULL, CMD_AO_ARGS_BASE16); /* 2th arg */ ++ sample_rate = (unsigned int)simple_strtoul(argv[3], NULL, CMD_AO_ARGS_BASE10); /* 3th arg */ ++ chn_cnt = (unsigned int)simple_strtoul(argv[4], NULL, CMD_AO_ARGS_BASE10); /* 4th arg */ ++ vol = (int)simple_strtol(argv[5], NULL, CMD_AO_ARGS_BASE10); /* 5th arg */ ++ if (ao_check_param(addr, size, sample_rate, chn_cnt, vol) != 0) { ++ return -1; ++ } ++ ++ acodec_device_init(); ++ acodec_i2s_set(sample_rate); ++ udelay(100 * 1000); /* 100 * 1000 us */ ++ start_ao(addr, size, sample_rate, chn_cnt, vol); ++ amp_unmute(); ++ ++ printf("ao dev start ok!\n"); ++ return 0; ++} ++ ++int do_stopao(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ amp_mute(); ++ stop_ao(); ++ ++ printf("ao dev closed!\n"); ++ return 0; ++} ++ ++U_BOOT_CMD( ++ startao, 10, 1, do_startao, ++ "startao - open interface of ao device.\n" ++ "\t- startao [addr size samplerate channelnum volume]", ++ "\nargs: [addr size samplerate channelnum volume]\n" ++ "\t- : address of raw audio data,align by 32Byte\n" ++ "\t-: size of raw audio data,align by 32Byte\n" ++ "\t-: sample rate of raw audio data\n" ++ "\t-: channel number of raw audio data\n" ++ "\t-: audio output volume default:0dB, range:0 ~ 6dB\n"); ++ ++U_BOOT_CMD( ++ stopao, 1, 0, do_stopao, ++ "stopao - close interface of ao device.\n", ++ ""); +diff --git a/cmd/cmd_cycle.c b/cmd/cmd_cycle.c +new file mode 100644 +index 0000000..7dcd3ce +--- /dev/null ++++ b/cmd/cmd_cycle.c +@@ -0,0 +1,251 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++/* ++ * Command for compress. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define CYCLE_MAGIC_HEAD 0x6379636c ++ ++#define CYCLE_MAGIC_ITEM_START 0x63796373 ++ ++#define CYCLE_MAGIC_ITEM_END 0x63796365 ++ ++/** Cycle Head Infomation */ ++typedef struct cycle_head_s { ++ unsigned int u32magichead; ++ unsigned int u32cycleflashsize; /* "fmt, __FUNCTION__, __LINE__, ##args) ++#define cycle_dbg(fmt, args...) printf("DBG: <%s:%d> "fmt, __FUNCTION__, __LINE__, ##args) ++ ++static int cycle_get_initdata(unsigned long ulsrc, unsigned int u32srclen, ++ cycle_item_start_s **ppstItem, ++ unsigned int *pu32compress) ++{ ++ cycle_head_s *psthead = NULL; ++ cycle_item_start_s *pstitem = NULL; ++ unsigned int u32itemmagicend; ++ unsigned long ulbuffer = ulsrc; ++ ++ /* Cycle Head */ ++ psthead = (cycle_head_s *)ulsrc; ++ ++ if ((psthead->u32magichead != CYCLE_MAGIC_HEAD) || ++ (psthead->u32cycleflashsize > u32srclen) || ++ (psthead->u32alignsize == 0)) { ++ cycle_err("psthead->u32magichead: %#x\n", psthead->u32magichead); ++ cycle_err("SrcLen[%u] CycleFlashSize[%u]\n", u32srclen, ++ psthead->u32cycleflashsize); ++ return -1; ++ } ++ ++ /* Compress Type */ ++ *pu32compress = psthead->u32compress; ++ cycle_dbg("Compress[%u]\n", *pu32compress); ++ ++ /* First Item */ ++ ulbuffer += sizeof(cycle_head_s); ++ ulbuffer = (ulbuffer + psthead->u32alignsize - 1) & ++ ~(psthead->u32alignsize - 1); ++ ++ pstitem = (cycle_item_start_s *)ulbuffer; ++ if ((pstitem->u32itemlen == 0) || ++ ((pstitem->u32itemlen % BYTE_ALIGN) != 0)) { ++ cycle_err("pstitem->u32itemlen: %#x\n", pstitem->u32itemlen); ++ return -1; ++ } ++ ++ u32itemmagicend = *(unsigned int *)(uintptr_t)(ulbuffer + ++ (pstitem->u32itemlen + sizeof(cycle_item_start_s))); ++ if ((pstitem->u32magicitemstart != CYCLE_MAGIC_ITEM_START) || ++ (pstitem->u32itemalllen >= (psthead->u32cycleflashsize / DIVIDE)) || ++ (u32itemmagicend != CYCLE_MAGIC_ITEM_END)) { ++ cycle_err("Item MagicStart[%#x] Len[%u] MagicEnd[%#x] CycleFlashSize[%u]\n", ++ pstitem->u32magicitemstart, pstitem->u32itemlen, u32itemmagicend, ++ psthead->u32cycleflashsize); ++ return -1; ++ } ++ ++ while (1) { ++ /* update item to last valid one */ ++ *ppstItem = (cycle_item_start_s *)ulbuffer; ++ ++ /* check next item valid or not */ ++ ulbuffer += pstitem->u32itemalllen; ++ ++ pstitem = (cycle_item_start_s *)ulbuffer; ++ ++ /* = psthead->u32cycleflashsize) ++ return 0; ++ ++ if (pstitem->u32magicitemstart != CYCLE_MAGIC_ITEM_START) { ++ /* u32magicitemstart == 0xffffffff) { ++ return 0; ++ } else { ++ cycle_err( ++ "pstitem->u32magicitemstart(0x%x) wrong!\n", ++ pstitem->u32magicitemstart); ++ return -1; ++ } ++ } ++ ++ u32itemmagicend = *(unsigned int *)(uintptr_t)(ulbuffer + ++ (pstitem->u32itemlen + sizeof(cycle_item_start_s))); ++ /* u32itemlen >= (psthead->u32cycleflashsize / DIVIDE)) || ++ (u32itemmagicend != CYCLE_MAGIC_ITEM_END)) { ++ cycle_err("\n"); ++ return -1; ++ } ++ } ++ ++ return -1; ++} ++ ++static int cycle_get_data(unsigned long ulsrc, unsigned int u32srclen, unsigned long uldst) ++{ ++ int s32ret; ++ ++ cycle_item_start_s *pstitem = NULL; ++ unsigned long ulitemdata = 0; ++ unsigned int ncompressed = 0; ++ ++ s32ret = cycle_get_initdata(ulsrc, u32srclen, &pstitem, &ncompressed); ++ if ((s32ret == 0) && pstitem) { ++ ulitemdata += (uintptr_t)pstitem + sizeof(cycle_item_start_s); ++ ++ if (ncompressed) { ++ hw_dec_type = 0; /**u32itemoriginlen; ++ s32ret = hw_dec_decompress_ex(HW_DECOMPRESS_OP_ONCE, (const unsigned char *)(uintptr_t)uldst, ++ &s32sizecompressed, (const unsigned char *)(uintptr_t)ulitemdata, pstitem->u32itemlen); ++ if (s32ret == 0 && s32sizecompressed == pstitem->u32itemoriginlen) { ++ cycle_dbg("decompress ok!\n"); ++ s32ret = 0; ++ } else { ++ (void)memset_s((void *)(uintptr_t)uldst, 16, 0, 16); /* 16 byte */ ++ cycle_err("decompress fail[%#x]! uncompress size[%#x]\n", ++ s32ret, s32sizecompressed); ++ s32ret = -1; ++ } ++ ++ hw_dec_uinit(); /**u32itemlen)) { ++ cycle_err("memcpy_s err : %s %d.\n", __func__, __LINE__); ++ s32ret = -1; ++ } ++ s32ret = 0; ++ } ++ } else { ++ (void)memset_s((void *)(uintptr_t)uldst, 16, 0, 16); /* 16 byte */ ++ cycle_err("Failed to get cycle data. dst: 0x%lx\n", uldst); ++ s32ret = -1; ++ } ++ ++ return s32ret; ++} ++ ++static int getcycledata(unsigned long ulsrc, unsigned long ulsrcbak, unsigned int u32srclen, ++ unsigned long uldst) ++{ ++ int s32ret = cycle_get_data(ulsrc, u32srclen, uldst); ++ if ((s32ret == -1) && (ulsrcbak != 0)) ++ s32ret = cycle_get_data(ulsrcbak, u32srclen, uldst); ++ ++ return s32ret; ++} ++ ++static int do_cycle(cmd_tbl_t *cmdtp, int flag, int argc, char* const argv[]) ++{ ++ unsigned long ulsrc; ++ unsigned long ulsrcbak; ++ unsigned long uldst; ++ unsigned int u32srclen; ++ ++ /* Check Input Args Count : four arguments needed */ ++ if (argc != 5) ++ goto usage; ++ ++ ulsrc = simple_strtoul(argv[1], NULL, 16); /* 16 byte */ ++ ulsrcbak = simple_strtoul(argv[2], NULL, 16); /* 16 byte */ ++ u32srclen = simple_strtoul(argv[3], NULL, 16); /* 16 byte */ ++ uldst = simple_strtoul(argv[4], NULL, 16); /* 16 byte */ ++ ++ if (ulsrc & 0XF) { ++ printf("ERR:\n src[0X%08lx] is not 16Byte-aligned!\n", ulsrc); ++ return 1; ++ } ++ ++ if (ulsrcbak & 0XF) { ++ printf("ERR:\n src_backup[0X%08lx] is not 16Byte-aligned!\n", ulsrcbak); ++ return 1; ++ } ++ ++ if (u32srclen & 0XFFFF) { ++ printf("ERR:\n src_len[0X%08x] is not 0x10000Byte-aligned!\n", u32srclen); ++ return 1; ++ } ++ ++ if (uldst & 0XF) { ++ printf("ERR:\n dst[0X%08lx] is not 16Byte-aligned!\n", uldst); ++ return 1; ++ } ++ ++ return getcycledata(ulsrc, ulsrcbak, u32srclen, uldst); ++ ++usage: ++ cmd_usage(cmdtp); ++ return 1; ++} ++ ++U_BOOT_CMD( ++ cread, 5, 1, do_cycle, ++ "get valid data from cycle_data buffer. 'cycle '", ++ "1. src_backup can be 0. 2. if src and src_backup are wrong, " ++ "dst head (16 byte) will be set to 0. 3. src and dst must be 16Byte-aligned" ++); ++ +diff --git a/cmd/cmd_dec.c b/cmd/cmd_dec.c +new file mode 100644 +index 0000000..ba1c6a1 +--- /dev/null ++++ b/cmd/cmd_dec.c +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++ ++extern int load_jpeg(void); ++extern int jpeg_decode(unsigned int format); ++ ++int do_jpgd(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int format; ++ ++ if (argc < 2) { ++ printf("Insufficient parameter!\n"); ++ printf("Usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ format = (unsigned int)simple_strtoul(argv[1], NULL, 10); ++#ifdef RGB_OUTPUT_ENABLE ++ if (format > 2) { ++#else ++ if (format != 0) { ++#endif ++ printf("Invalid parameter!\n"); ++ return -1; ++ } ++ ++#ifdef CONFIG_SYS_LONGHELP ++ printf("you should first set:\n%s\n", cmdtp->help); ++#endif ++ ++ ret = load_jpeg(); ++ if (ret != 0) { ++ printf("load jpeg err. \n"); ++ //todo return 0 or ret? ++ return 0; ++ } ++ jpeg_decode(format); ++ ++ printf("decode jpeg!\n"); ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ decjpg, CONFIG_SYS_MAXARGS, 1, do_jpgd, ++ "jpgd - decode jpeg picture.\n" ++ "decjpg [format]", ++ "\nargs: [format]\n" ++#ifdef RGB_OUTPUT_ENABLE ++ "\t- : 0: semi-planar yvu420, 1: ARGB1555, 2: ARGB8888\n" ++#else ++ "\t- : 0: semi-planar yvu420\n" ++#endif ++ "\t- setenv jpeg_addr 0x--------\n" ++ "\t- setenv jpeg_size 0x--------\n" ++ "\t- setenv vobuf 0x--------\n" ++ "\t- setenv jpeg_emar_buf 0x--------\n" ++); ++ ++ +diff --git a/cmd/cmd_timestamp.c b/cmd/cmd_timestamp.c +new file mode 100644 +index 0000000..fd3fd78 +--- /dev/null ++++ b/cmd/cmd_timestamp.c +@@ -0,0 +1,362 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "cmd_timestamp.h" ++#include "asm/barriers.h" ++#include "string.h" ++#include "stdio.h" ++#include "command.h" ++#include "securec.h" ++ ++#define SRAM_BASE 0x04010000 ++#define SRAM_SIZE 0x10000 ++#define SRAM_END (SRAM_BASE + SRAM_SIZE) ++#define SYSCNT_AREA_SIZE 0x200 ++#define SYSCNT_AREA_START_ADDR (SRAM_END - SYSCNT_AREA_SIZE) ++#define SYS_CNT_UNIT_HIGH_OFFSET (4) ++#define SYS_CNT_UNIT_LOW_OFFSET (0) ++#define SYS_CNT_BOOTROM_START_OFFSET (1) ++#define SYS_CNT_BOOTROM_END_OFFSET (2) ++#define SYS_CNT_GSL_START_OFFSET (3) ++#define SYS_CNT_UBOOT_START_OFFSET (4) ++#define SYS_CNT_FREQ_24MHZ (24) ++#define SYS_CNT_RAM_UNIT (8) ++#define SYS_CNT_UNIT_LOW_OFFSET (0) ++ ++#define SYS_CTRL_REG_BASE 0x11020000 ++#define SYSCNT_UPDATE_OFFSET 0x104C ++#define SYSCNT_REG0_OFFSET 0x1050 ++#define SYSCNT_REG1_OFFSET 0x1054 ++#define HIGH_BIT_SHIFT 32 ++#define SYSCNT_FREQ_24MHZ 24 ++ ++#define TIMESTAMP_MAGIC_VALUE 0x55aa55aa ++#define TIMESTAMP_MAGIC_OFFSET 0 ++#define TIMESTAMP_COUNT_OFFSET 4 ++#define TIMESTAMP_ITEM_OFFSET 8 ++#define TIMESTAMP_NAME_LEN 64 ++#define TIMESTAMP_COUNT_MAX 100 ++ ++#define FDR_ADDR_BASE 0x40000000 ++#define TIMESTAMP_MIN_ADDR 0x4000C800 ++#define TIMESTAMP_MAX_ADDR 0x40010000 ++#if defined(CONFIG_TIME_ADDR_OFFSET) ++#define TIME_RECORD_ADDR (FDR_ADDR_BASE + CONFIG_TIME_ADDR_OFFSET) ++#else ++#define TIME_RECORD_ADDR (FDR_ADDR_BASE + 0xC800) ++#endif ++ ++/* ++ * timestamps storage arrangement ++ * |--8 bytes--|--Timestamp Item--|--Timestamp Item--| ... ++ * |Magic|Count|name |line |stamp |name |line |stamp | ... ++ */ ++ ++typedef struct { ++ char name[TIMESTAMP_NAME_LEN]; ++ unsigned int line; ++ unsigned int stamp; ++} timestamp_item; ++ ++static unsigned long long get_timestamp_us(void) ++{ ++ unsigned int l_value; ++ unsigned int h_value; ++ unsigned long long value; ++ ++ *((volatile unsigned int *)(SYS_CTRL_REG_BASE + SYSCNT_UPDATE_OFFSET)) = SYSCNT_UPDATE_OFFSET; ++ l_value = *((volatile unsigned int *)(SYS_CTRL_REG_BASE + SYSCNT_REG0_OFFSET)); ++ h_value = *((volatile unsigned int *)(SYS_CTRL_REG_BASE + SYSCNT_REG1_OFFSET)); ++ ++ value = ((unsigned long long)h_value << HIGH_BIT_SHIFT) + l_value; ++ ++ return value / SYSCNT_FREQ_24MHZ; ++} ++ ++static bool timestamp_enough_memory_check(unsigned int count) ++{ ++ bool is_enough = false; ++ unsigned int count_addr = TIME_RECORD_ADDR + TIMESTAMP_ITEM_OFFSET + count * sizeof(timestamp_item); ++ if (count_addr < TIMESTAMP_MAX_ADDR) { ++ is_enough = true; ++ } ++ return is_enough; ++} ++ ++static void timestamp_clear_all_once_only(void) ++{ ++ static bool flag = false; ++ ++ if (flag) { ++ return; ++ } ++ ++ // should clear the memory area once that used to store timestamp logs at first. ++ const int timestamp_mem_count = TIMESTAMP_MAX_ADDR - TIME_RECORD_ADDR; ++ bool ret = memset_s((void *)TIME_RECORD_ADDR, timestamp_mem_count, 0xFF, timestamp_mem_count - 1); ++ if (ret != EOK) { ++ printf("memset_s error: %d!\n", ret); ++ return; ++ } ++ ++ flag = true; ++} ++ ++static bool timestamp_record_once_sram(unsigned int count, unsigned int offset, const char *name, unsigned int line) ++{ ++ if (!timestamp_enough_memory_check(count + 1)) { ++ printf("not enough memory to store the timestamp log from the sram!\n"); ++ return false; ++ } ++ ++ timestamp_item *item = (timestamp_item *)(TIME_RECORD_ADDR + TIMESTAMP_ITEM_OFFSET); ++ int name_len = strlen(name) + 1; ++ if (name_len > TIMESTAMP_NAME_LEN - 1) { ++ name_len = TIMESTAMP_NAME_LEN - 1; ++ } ++ int ret = memcpy_s(item[count].name, TIMESTAMP_NAME_LEN - 1, name, name_len); ++ if (ret != EOK) { ++ printf("memcpy_s error: %d!\n", ret); ++ return false; ++ } ++ item[count].name[TIMESTAMP_NAME_LEN - 1] = '\0'; ++ item[count].line = line; ++ // get the timestamp from sram stored during the booting stage of bootrom/gsl ++ unsigned long sram_timestamp_addr = ++ (unsigned long)(SYSCNT_AREA_START_ADDR + offset * SYS_CNT_RAM_UNIT + SYS_CNT_UNIT_LOW_OFFSET); ++ unsigned int l_value = *((volatile unsigned int *)sram_timestamp_addr); ++ item[count].stamp = l_value / SYS_CNT_FREQ_24MHZ; ++ return true; ++} ++ ++static bool timestamp_record_once(unsigned int count, const char *name, unsigned int line) ++{ ++ if (!timestamp_enough_memory_check(count + 1)) { ++ printf("not enough memory to store the timestamp log from the uboot callings!\n"); ++ return false; ++ } ++ ++ timestamp_item *item = (timestamp_item *)(TIME_RECORD_ADDR + TIMESTAMP_ITEM_OFFSET); ++ int name_len = strlen(name) + 1; ++ if (name_len > TIMESTAMP_NAME_LEN - 1) { ++ name_len = TIMESTAMP_NAME_LEN - 1; ++ } ++ int ret = memcpy_s(item[count].name, TIMESTAMP_NAME_LEN - 1, name, name_len); ++ if (ret != EOK) { ++ printf("memcpy_s error: %d!\n", ret); ++ return false; ++ } ++ item[count].name[TIMESTAMP_NAME_LEN - 1] = '\0'; ++ item[count].line = line; ++ // get the timestamp from timer directly for the booting stage of uboot. ++ item[count].stamp = (unsigned int)get_timestamp_us(); ++ return true; ++} ++ ++static bool timestamp_save_sram_timestamp_once_only(void) ++{ ++ static bool flag = false; ++ bool ret = false; ++ unsigned int count = 0; ++ unsigned int l_value; ++ unsigned int h_value; ++ ++ if (flag) ++ return flag; ++ ++ *((volatile unsigned int *)(SYSCNT_AREA_START_ADDR + SYS_CNT_UNIT_LOW_OFFSET)) = TIMESTAMP_MAGIC_VALUE; ++ *((volatile unsigned int *)(SYSCNT_AREA_START_ADDR + SYS_CNT_UNIT_HIGH_OFFSET)) = TIMESTAMP_MAGIC_VALUE; ++ ++ l_value = *((volatile unsigned int *)(SYSCNT_AREA_START_ADDR + SYS_CNT_UNIT_LOW_OFFSET)); ++ h_value = *((volatile unsigned int *)(SYSCNT_AREA_START_ADDR + SYS_CNT_UNIT_HIGH_OFFSET)); ++ ++ if (l_value != TIMESTAMP_MAGIC_VALUE || h_value != TIMESTAMP_MAGIC_VALUE) { ++ printf("sram head is invalid [0x%x][0x%x]\n", l_value, h_value); ++ return flag; ++ } ++ ++ *((volatile unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_MAGIC_OFFSET)) = TIMESTAMP_MAGIC_VALUE; ++ *((volatile unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_COUNT_OFFSET)) = 0; ++ ++ ret = timestamp_record_once_sram(count, SYS_CNT_BOOTROM_START_OFFSET, "bootrom_start", __LINE__); ++ if (!ret) { ++ printf("can't record SYS_CNT_BOOTROM_START_OFFSET\n"); ++ goto SUCCESS; ++ } ++ count++; ++ ++ ret = timestamp_record_once_sram(count, SYS_CNT_BOOTROM_END_OFFSET, "bootrom_end", __LINE__); ++ if (!ret) { ++ printf("can't record SYS_CNT_BOOTROM_END_OFFSET\n"); ++ goto SUCCESS; ++ } ++ count++; ++ ++ ret = timestamp_record_once_sram(count, SYS_CNT_GSL_START_OFFSET, "gsl_start", __LINE__); ++ if (!ret) { ++ printf("can't record SYS_CNT_GSL_START_OFFSET\n"); ++ goto SUCCESS; ++ } ++ count++; ++ ++ ret = timestamp_record_once_sram(count, SYS_CNT_UBOOT_START_OFFSET, "uboot_start", __LINE__); ++ if (!ret) { ++ printf("can't record SYS_CNT_UBOOT_START_OFFSET\n"); ++ goto SUCCESS; ++ } ++ count++; ++ ++SUCCESS: ++ *((volatile unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_COUNT_OFFSET)) = count; ++ flag = true; ++ return flag; ++} ++ ++void timestamp_mark(const char *name, unsigned int line) ++{ ++ unsigned int value; ++ unsigned int count; ++ ++ if (TIME_RECORD_ADDR >= TIMESTAMP_MAX_ADDR) { ++ printf("[info] invalid TIME_RECORD_ADDR (0x%x) used.\n", TIME_RECORD_ADDR); ++ return; ++ } ++ ++ if (TIME_RECORD_ADDR < TIMESTAMP_MIN_ADDR) { ++ printf("[info] invalid TIME_RECORD_ADDR (0x%x) used.\n", TIME_RECORD_ADDR); ++ return; ++ } ++ ++ timestamp_clear_all_once_only(); ++ ++ bool timerecord_ddr_init = timestamp_save_sram_timestamp_once_only(); ++ if (!timerecord_ddr_init) { ++ printf("[info] read sram time failed! continue to record other stage's timestamp.\n"); ++ } ++ ++ value = *((volatile unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_MAGIC_OFFSET)); ++ if (value == TIMESTAMP_MAGIC_VALUE) { ++ count = *((volatile unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_COUNT_OFFSET)); ++ } else { ++ count = 0; ++ *((volatile unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_MAGIC_OFFSET)) = TIMESTAMP_MAGIC_VALUE; ++ } ++ ++ if (count == TIMESTAMP_COUNT_MAX) { ++ count = 0; ++ } ++ ++ bool ret = timestamp_record_once(count, name, line); ++ if (!ret) { ++ printf("can't record timestamp for name(%s)\n", name); ++ return; ++ } ++ ++ *((volatile unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_COUNT_OFFSET)) = count + 1; ++} ++ ++static int do_timestamp_print(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int i; ++ unsigned int value; ++ unsigned int count; ++ ++ printf("TIME_RECORD_ADDR=0x%x\n", TIME_RECORD_ADDR); ++ ++ value = *((unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_MAGIC_OFFSET)); ++ if (value != TIMESTAMP_MAGIC_VALUE) { ++ printf("no timestamp items in ddr addr[0x%x]\n", TIME_RECORD_ADDR); ++ return -1; ++ } ++ count = *((unsigned int *)(TIME_RECORD_ADDR + TIMESTAMP_COUNT_OFFSET)); ++ printf("count=%d\n", count); ++ timestamp_item *item = (timestamp_item *)(TIME_RECORD_ADDR + TIMESTAMP_ITEM_OFFSET); ++ for (i = 0; i < count; i++) { ++ printf("time stamp[%-3u] = %-8uus line: %-5u name: %s\n", i, item[i].stamp, item[i].line, item[i].name); ++ } ++ return 0; ++} ++ ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP_TEST) ++static int do_timestamp_unit_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ const int timestamp_mem_count = TIMESTAMP_MAX_ADDR - TIME_RECORD_ADDR; ++ bool ret = false; ++ printf("TEST CASE 1: test enviroment checking\n"); ++ printf("TIME_RECORD_ADDR is 0x%x, TIMESTAMP_MAX_ADDR is 0x%x, TIMESTAMP_MIN_ADDR is 0x%x\n", ++ TIME_RECORD_ADDR, ++ TIMESTAMP_MAX_ADDR, ++ TIMESTAMP_MIN_ADDR); ++ timestamp_print(); ++ ++ printf("TEST CASE 2: normal test, timestamp TEST, expect TEST, check whether TEST timestamp print or not\n"); ++ // clear the timestamp memory area at first. ++ ret = memset_s((void *)TIME_RECORD_ADDR, timestamp_mem_count, 0xFF, timestamp_mem_count - 1); ++ if (ret != EOK) { ++ printf("memset_s error! test end\n"); ++ return -1; ++ } ++ timestamp_mark("TEST", 1); ++ timestamp_print(); ++ ++ printf("TEST CASE 3: long name test, over than 64\n"); ++ // clear the timestamp memory area at first. ++ ret = memset_s((void *)TIME_RECORD_ADDR, timestamp_mem_count, 0xFF, timestamp_mem_count - 1); ++ if (ret != EOK) { ++ printf("memset_s error! test end\n"); ++ return -1; ++ } ++ timestamp_mark("TEST65TEST65TEST65TEST65TEST65TEST65TEST65TEST65TEST65TEST65TEST65TEST65TEST65", 1); ++ timestamp_print(); ++ ++ printf("TEST CASE 4: less than times(TIMESTAMP_COUNT_MAX:%d) test\n", TIMESTAMP_COUNT_MAX); ++ // clear the timestamp memory area at first. ++ ret = memset_s((void *)TIME_RECORD_ADDR, timestamp_mem_count, 0xFF, timestamp_mem_count - 1); ++ if (ret != EOK) { ++ printf("memset_s error! test end\n"); ++ return -1; ++ } ++ for (int i = 0; i < TIMESTAMP_COUNT_MAX; i++) { ++ timestamp_mark("TEST", i); ++ } ++ timestamp_print(); ++ ++ printf("TEST CASE 5: less than two times(TIMESTAMP_COUNT_MAX:%d) test\n", TIMESTAMP_COUNT_MAX); ++ // clear the timestamp memory area at first. ++ ret = memset_s((void *)TIME_RECORD_ADDR, timestamp_mem_count, 0xFF, timestamp_mem_count - 1); ++ if (ret != EOK) { ++ printf("memset_s error! test end\n"); ++ return -1; ++ } ++ for (int i = 0; i < (TIMESTAMP_COUNT_MAX<<1); i++) { ++ timestamp_mark("TEST", i); ++ } ++ timestamp_print(); ++ return 0; ++} ++#endif ++ ++void timestamp_print(void) ++{ ++ do_timestamp_print(NULL, 0, 0, NULL); ++} ++ ++U_BOOT_CMD(timestamp, 1, 0, do_timestamp_print, "print timestamp items in ddr, must define TIME_RECORD_ADDR", "\n"); ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP_TEST) ++U_BOOT_CMD(timestamp_unit_test, 1, 0, do_timestamp_unit_test, "timestamp unit test", "\n"); ++#endif +diff --git a/cmd/cmd_ugzip.c b/cmd/cmd_ugzip.c +new file mode 100644 +index 0000000..61e40c8 +--- /dev/null ++++ b/cmd/cmd_ugzip.c +@@ -0,0 +1,296 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include "flash_read.h" ++ ++/* ++ * note: change it larger if rugzip cmd is abnormal ++ */ ++#define RUGZIP_MIN_FRAGMENTATION_SIZE 0x20000 ++#define RUGZIP_READ_TMP_SIZE MMC_MAX_BLOCK_LEN ++ ++static int check_and_get_gzip_info(unsigned long offset, unsigned int *size) ++{ ++ if (RUGZIP_READ_TMP_SIZE < GZIP_HEAD_SIZE) { ++ printf("[error] tmp size is smaller than gzip head size!\n"); ++ return -1; ++ } ++ ++ unsigned char *tmp_addr = memalign(16, RUGZIP_READ_TMP_SIZE); /* 16: aligned size */ ++ if (tmp_addr == NULL) { ++ printf("[error] memalign 0x%x memeroy failed!\n", RUGZIP_READ_TMP_SIZE); ++ return -1; ++ } ++ ++ int ret = flash_read(offset, RUGZIP_READ_TMP_SIZE, tmp_addr); ++ if (ret < 0) { ++ printf("[error] read kernel header from flash failed!\n"); ++ return -1; ++ } ++ ++ unsigned int magic_num0, magic_num1; ++ magic_num0 = *(unsigned int *)(tmp_addr + HEAD_MAGIC_NUM0_OFFSET); ++ magic_num1 = *(unsigned int *)(tmp_addr + HEAD_MAGIC_NUM1_OFFSET); ++ if ((magic_num0 != HEAD_MAGIC_NUM0) || (magic_num1 != HEAD_MAGIC_NUM1)) { ++ printf("[error] invalid gzip file(0x%x, 0x%x).\n", magic_num0, magic_num1); ++ return -1; ++ } ++ ++ unsigned int gzip_file_size; ++ gzip_file_size = *(unsigned int *)(tmp_addr + COMPRESSED_SIZE_OFFSET); ++ gzip_file_size += GZIP_HEAD_SIZE; ++ if (gzip_file_size > GZIP_MAX_LEN) { ++ printf("[error] gzip file(0x%x) greater than :0x%x.\n", gzip_file_size, GZIP_MAX_LEN); ++ return -1; ++ } ++ ++ *size = gzip_file_size; ++ ++ return 0; ++} ++ ++static int read_and_ugzip_once(unsigned long offset, unsigned int gzip_file_size, ++ unsigned char *src_addr, unsigned char *dst_addr) ++{ ++ int ret = flash_read(offset, gzip_file_size, src_addr); ++ if (ret < 0) { ++ printf("[error] read gzip file failed(0x%x).\n", ret); ++ return -1; ++ } ++ ++ int comparessed_size = *(int *)(src_addr + COMPRESSED_SIZE_OFFSET); ++ int uncomparessed_size = *(int *)(src_addr + UNCOMPRESSED_SIZE_OFFSET); ++ hw_dec_init(); ++ ret = hw_dec_decompress_ex(HW_DECOMPRESS_OP_ONCE, ++ dst_addr, &uncomparessed_size, src_addr + GZIP_HEAD_SIZE, comparessed_size); ++ if (ret != 0) { ++ printf("[error] decompress file failed.\n"); ++ hw_dec_uinit(); ++ return -1; ++ } ++ hw_dec_uinit(); ++ ++ return 0; ++} ++ ++static int read_and_ugzip_more_than_once(unsigned long offset, unsigned int gzip_file_size, ++ unsigned char *src_addr, unsigned char *dst_addr, unsigned int fragmentation_size) ++{ ++ if (gzip_file_size <= fragmentation_size) { ++ printf("[error] gzip file size should less than block size.\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ unsigned int cur_read_size = fragmentation_size; ++ int ret = flash_read(offset, cur_read_size, src_addr); ++ if (ret < 0) { ++ printf("[error] read gzip file failed(0x%x).\n", ret); ++ return CMD_RET_USAGE; ++ } ++ ++ int size_uncomparessed = *(int *)(src_addr + UNCOMPRESSED_SIZE_OFFSET); ++ ++ hw_dec_init(); ++ ret = hw_dec_decompress_ex(HW_DECOMPRESS_OP_START, ++ dst_addr, &size_uncomparessed, src_addr + GZIP_HEAD_SIZE, cur_read_size - GZIP_HEAD_SIZE); ++ if (ret != 0) { ++ printf("[error] [%s] [%d]decompress file failed.\n", __func__, __LINE__); ++ hw_dec_uinit(); ++ return CMD_RET_USAGE; ++ } ++ ++ hw_decompress_op_type op_type; ++ unsigned int cur_offset = cur_read_size; ++ while (cur_offset < gzip_file_size) { ++ if ((cur_offset + fragmentation_size) < gzip_file_size) { ++ cur_read_size = fragmentation_size; ++ op_type = HW_DECOMPRESS_OP_CONTINUE; ++ } else { ++ cur_read_size = gzip_file_size - cur_offset; ++ op_type = HW_DECOMPRESS_OP_END; ++ } ++ ++ ret = flash_read(offset + cur_offset, cur_read_size, src_addr + cur_offset); ++ if (ret < 0) { ++ printf("[error] read compress file failed(0x%x).\n", ret); ++ hw_dec_uinit(); ++ return CMD_RET_USAGE; ++ } ++ ++ ret = hw_dec_decompress_ex(op_type, ++ dst_addr, &size_uncomparessed, src_addr + cur_offset, cur_read_size); ++ if (ret != 0) { ++ printf("[error] [%s] [%d]decompress file failed.\n", __func__, __LINE__); ++ hw_dec_uinit(); ++ return CMD_RET_USAGE; ++ } ++ ++ cur_offset += cur_read_size; ++ } ++ ++ hw_dec_uinit(); ++ ++ return 0; ++} ++ ++static int do_ugzip(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ if (argc != 3) ++ return CMD_RET_USAGE; ++ ++ uintptr_t src = simple_strtoul(argv[1], NULL, 16); /* 1: src argc, 16: base */ ++ uintptr_t dst = simple_strtoul(argv[2], NULL, 16); /* 2: dst argc, 16: base */ ++ ++ if (src & 0XF) { ++ printf("[error] src[0X%08lx] is not 16Byte-aligned!\n", src); ++ return CMD_RET_USAGE; ++ } ++ if (dst & 0XF) { ++ printf("[error] dst[0X%08lx] is not 16Byte-aligned!\n", dst); ++ return CMD_RET_USAGE; ++ } ++ ++ unsigned int magic_num0 = *(unsigned int *)(src + HEAD_MAGIC_NUM0_OFFSET); ++ unsigned int magic_num1 = *(unsigned int *)(src + HEAD_MAGIC_NUM1_OFFSET); ++ if ((magic_num0 != HEAD_MAGIC_NUM0) || (magic_num1 != HEAD_MAGIC_NUM1)) { ++ printf("[error] The magic numbers are not correct!\n"\ ++ " Please check the source data!\n"); ++ return CMD_RET_USAGE; ++ } ++ int size_comparessed = *(int *)(src + COMPRESSED_SIZE_OFFSET); ++ int size_uncomparessed = *(int *)(src + UNCOMPRESSED_SIZE_OFFSET); ++ ++ dcache_disable(); /* Recommend to disable dcache */ ++ hw_dec_init(); ++ int ret = hw_dec_decompress_ex(HW_DECOMPRESS_OP_ONCE, (unsigned char *)dst, ++ &size_uncomparessed, (unsigned char *)(src + GZIP_HEAD_SIZE), size_comparessed); ++ if (ret != 0) { ++ printf("[error] decompress fail!\n"); ++ hw_dec_uinit(); ++ return CMD_RET_USAGE; ++ } ++ hw_dec_uinit(); ++ ++ return 1; ++} ++ ++static int do_read_gzip(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ if (argc != 3) { ++ return CMD_RET_USAGE; ++ } ++ ++ /* 1: offset argc, 16: base */ ++ unsigned long offset = (unsigned long)simple_strtoul(argv[1], NULL, 16); ++ /* 2: dst_addr argc, 16: base */ ++ unsigned char *dst_addr = (unsigned char *)simple_strtoul(argv[2], NULL, 16); ++ ++ unsigned int gzip_file_size; ++ int ret = check_and_get_gzip_info(offset, &gzip_file_size); ++ if (ret != 0) { ++ printf("[error] get gzip file size failed.\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ ret = flash_read(offset, gzip_file_size, dst_addr); ++ if (ret < 0) { ++ printf("[error] read gzip file failed!\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ return 0; ++} ++ ++/* ++ * read gzip file from flash and uncomress it ++ * arg: ++ * offset: gzip file offset in flash ++ * compress_addr: gzip file addr in ddr ++ * uncompress_addr: uncompress file addr in ddr ++ * fragmentation_size: fragmentation size for reading once ++ */ ++static int do_read_and_ugzip(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ if (argc != 5) { ++ return CMD_RET_USAGE; ++ } ++ ++ /* 1: offset argc, 16: base */ ++ unsigned long offset = (unsigned long)simple_strtoul(argv[1], NULL, 16); ++ /* 2: compress_addr argc, 16: base */ ++ unsigned char *compress_addr = (unsigned char *)(uintptr_t)simple_strtoul(argv[2], NULL, 16); ++ /* 3: uncompress_addr argc, 16: base */ ++ unsigned char *uncompress_addr = (unsigned char *)(uintptr_t)simple_strtoul(argv[3], NULL, 16); ++ /* 4: fragmentation_size argc, 16: base */ ++ unsigned int fragmentation_size = (unsigned int)(uintptr_t)simple_strtoul(argv[4], NULL, 16); ++ if (fragmentation_size < RUGZIP_MIN_FRAGMENTATION_SIZE) { ++ printf("[error] fragmentation_size less than 0x%x!\n", RUGZIP_MIN_FRAGMENTATION_SIZE); ++ return CMD_RET_USAGE; ++ } ++ ++ if ((fragmentation_size % RUGZIP_MIN_FRAGMENTATION_SIZE) != 0) { ++ printf("[error] fragmentation_size should been aligned with 0x%x!\n", RUGZIP_MIN_FRAGMENTATION_SIZE); ++ return CMD_RET_USAGE; ++ } ++ ++ unsigned int gzip_file_size; ++ int ret = check_and_get_gzip_info(offset, &gzip_file_size); ++ if (ret != 0) { ++ printf("[error] gzip file is invalid!\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ dcache_disable(); /* Recommend to disable dcache */ ++ if (gzip_file_size <= fragmentation_size) { ++ ret = read_and_ugzip_once(offset, gzip_file_size, compress_addr, uncompress_addr); ++ } else { ++ ret = read_and_ugzip_more_than_once(offset, gzip_file_size, ++ compress_addr, uncompress_addr, fragmentation_size); ++ } ++ if (ret != 0) { ++ printf("[error] read and decompress failed!\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ rgzip, 3, 0, do_read_gzip, ++ "read gzip file to a memory region, eg:[rgzip offset compress_addr]", ++ "offset dstaddr\n" ++); ++ ++U_BOOT_CMD( ++ ugzip, 3, 0, do_ugzip, ++ "uncompress file with hardware IP, eg:[ugzip compress_addr uncompress_addr]", ++ "ugzip \n" ++ "src and dst must be 16Byte-aligned" ++); ++ ++U_BOOT_CMD( ++ rugzip, 5, 0, do_read_and_ugzip, ++ "read and uncompress file, eg:[rugzip offset compress_addr uncompress_addr fragmentation_size]", ++ "rugzip offset src dst fragmentation_size\n" ++ "src and dst must be 16Byte-aligned" ++); +diff --git a/cmd/cmd_vo.c b/cmd/cmd_vo.c +new file mode 100644 +index 0000000..90a26c5 +--- /dev/null ++++ b/cmd/cmd_vo.c +@@ -0,0 +1,629 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ot_common_vo.h" ++#include "vo.h" ++#if CONFIG_OT_HDMI_SUPPORT ++#include "ot_hdmi.h" ++#endif ++ ++#if CONFIG_OT_MIPI_TX_SUPPORT ++#include "mipi_tx.h" ++#endif ++ ++#define CFG_MAXARGS_SETVOBG 3 ++#define CFG_MAXARGS_STARTVO 4 ++#define CFG_MAXARGS_STOPVO 2 ++#define CFG_MAXARGS_STARTVL 8 ++#define CFG_MAXARGS_STOPVL 2 ++#define CFG_MAXARGS_STARTGX 9 ++#define CFG_MAXARGS_STOPGX 2 ++#define CMD_VO_ARGS_BASE10 10 ++#define CMD_VO_ARGS_BASE16 16 ++#define CMD_VO_ARGS_BASE_ALL 0 ++#define INSUFFICIENT_PARAM_STR "insufficient parameter or operation not permitted!\n" ++ ++#define VO_DEV_MAX_NUM 3 ++ ++static unsigned int g_a_interface_type[VO_DEV_MAX_NUM] = { [0 ... (VO_DEV_MAX_NUM - 1)] = 0}; ++ ++static unsigned int vo_get_interface_type(unsigned int dev) ++{ ++ if (dev >= VO_DEV_MAX_NUM) { ++ return 0; ++ } ++ return g_a_interface_type[dev]; ++} ++ ++static void vo_set_interface_type(unsigned int dev, unsigned int type) ++{ ++ if (dev >= VO_DEV_MAX_NUM) { ++ return; ++ } ++ g_a_interface_type[dev] = type; ++} ++ ++static int vobg_parse(char *const argv[], unsigned int *dev, unsigned int *rgb) ++{ ++ int ret; ++ unsigned long dev_tmp, rgb_tmp; ++ ret = strict_strtoul(argv[1], CMD_VO_ARGS_BASE10, &dev_tmp); /* 1st arg */ ++ if (ret != 0) { ++ printf("parse dev failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[2], CMD_VO_ARGS_BASE_ALL, &rgb_tmp); /* 2nd arg */ ++ if (ret != 0) { ++ printf("parse color failed.\n"); ++ return -1; ++ } ++ *dev = (unsigned int)dev_tmp; ++ *rgb = (unsigned int)rgb_tmp; ++ return 0; ++} ++ ++static int do_vobg(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int dev, rgb; ++ ++ if (argc < 3) { /* max 3 args */ ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ret = vobg_parse(argv, &dev, &rgb); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ret = set_vobg(dev, rgb); ++ if (ret != 0) { ++ printf(INSUFFICIENT_PARAM_STR); ++ return -1; ++ } ++ ++ printf("dev %u set background color 0x%x!\n", dev, rgb); ++ ++ return 0; ++} ++ ++static int startvo_parse(char *const argv[], unsigned int *dev, unsigned int *intftype, unsigned int *sync) ++{ ++ int ret; ++ unsigned long dev_tmp, intftype_tmp, sync_tmp; ++ ret = strict_strtoul(argv[1], CMD_VO_ARGS_BASE10, &dev_tmp); /* 1st arg */ ++ if (ret != 0) { ++ printf("parse dev failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[2], CMD_VO_ARGS_BASE10, &intftype_tmp); /* 2nd arg */ ++ if (ret != 0) { ++ printf("parse intftype failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[3], CMD_VO_ARGS_BASE10, &sync_tmp); /* 3rd arg */ ++ if (ret != 0) { ++ printf("parse sync failed.\n"); ++ return -1; ++ } ++ *dev = (unsigned int)dev_tmp; ++ *intftype = (unsigned int)intftype_tmp; ++ *sync = (unsigned int)sync_tmp; ++ return 0; ++} ++ ++#if CONFIG_OT_HDMI_SUPPORT ++static void do_start_hdmi(unsigned int intftype, unsigned int sync) ++{ ++ if (intftype & OT_VO_INTF_HDMI) { ++ if (intftype == (OT_VO_INTF_HDMI | OT_VO_INTF_MIPI)) { ++ /* start hdmi */ ++ hdmi_display(sync, OT_HDMI_VIDEO_MODE_RGB444, OT_HDMI_VIDEO_MODE_YCBCR444); ++ } else { ++ /* start hdmi */ ++ hdmi_display(sync, OT_HDMI_VIDEO_MODE_YCBCR444, OT_HDMI_VIDEO_MODE_YCBCR444); ++ } ++ } ++} ++#endif ++ ++#if CONFIG_OT_MIPI_TX_SUPPORT ++static void do_start_mipi_tx(unsigned int intftype, unsigned int sync) ++{ ++ if (!((intftype & OT_VO_INTF_MIPI) || (intftype & OT_VO_INTF_MIPI_SLAVE))) { ++ return; ++ } ++ mipi_tx_display(intftype, sync); ++} ++#endif ++ ++static int do_startvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int dev, intftype, sync; ++ ++ if (argc < 4) { /* max 4 args */ ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ret = startvo_parse(argv, &dev, &intftype, &sync); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ++ ret = start_vo(dev, intftype, sync); ++ if (ret != 0) { ++ printf(INSUFFICIENT_PARAM_STR); ++ return -1; ++ } ++ ++#if CONFIG_OT_HDMI_SUPPORT ++ do_start_hdmi(intftype, sync); ++#endif ++ ++#if CONFIG_OT_MIPI_TX_SUPPORT ++ do_start_mipi_tx(intftype, sync); ++#endif ++ ++ vo_set_interface_type(dev, intftype); ++ ++ printf("dev %u opened!\n", dev); ++ ++ return 0; ++} ++ ++static void do_stop_hdmi(unsigned int dev) ++{ ++ if (vo_get_interface_type(dev) & OT_VO_INTF_HDMI) { ++ /* stop hdmi */ ++#if CONFIG_OT_HDMI_SUPPORT ++ hdmi_stop(); ++#endif ++ } ++} ++ ++static void do_stop_mipi_tx(unsigned int dev) ++{ ++ if ((vo_get_interface_type(dev) & OT_VO_INTF_MIPI) || ++ (vo_get_interface_type(dev) & OT_VO_INTF_MIPI_SLAVE)) { ++ /* stop mipi */ ++#if CONFIG_OT_MIPI_TX_SUPPORT ++ mipi_tx_stop(OT_VO_INTF_MIPI); ++#endif ++ } ++} ++ ++static void do_stop_intf(unsigned int dev) ++{ ++ do_stop_mipi_tx(dev); ++ do_stop_hdmi(dev); ++ vo_set_interface_type(dev, 0); ++} ++ ++static int stopvo_parse(char *const argv[], unsigned int *dev) ++{ ++ int ret; ++ unsigned long dev_tmp; ++ ret = strict_strtoul(argv[1], CMD_VO_ARGS_BASE10, &dev_tmp); ++ if (ret != 0) { ++ printf("parse dev failed.\n"); ++ return -1; ++ } ++ *dev = (unsigned int)dev_tmp; ++ return 0; ++} ++ ++static int do_stopvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int dev; ++ if (argc < 2) { /* max 2 args */ ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ret = stopvo_parse(argv, &dev); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ++ /* step1: stop vo */ ++ ret = stop_vo(dev); ++ if (ret != 0) { ++ printf(INSUFFICIENT_PARAM_STR); ++ return -1; ++ } ++ /* step2: stop intf */ ++ do_stop_intf(dev); ++ ++ printf("dev %u closed!\n", dev); ++ ++ return 0; ++} ++ ++static int start_layer_parse(char *const argv[], unsigned int *layer, ++ unsigned long *addr, unsigned int *strd, ot_rect *layer_rect) ++{ ++ int ret; ++ unsigned long layer_tmp, addr_tmp, strd_tmp, x_tmp, y_tmp, width_tmp, height_tmp; ++ ret = strict_strtoul(argv[1], CMD_VO_ARGS_BASE10, &layer_tmp); /* 1st arg */ ++ if (ret != 0) { ++ printf("parse layer failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[2], CMD_VO_ARGS_BASE16, &addr_tmp); /* 2nd arg */ ++ if (ret != 0) { ++ printf("parse addr failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[3], CMD_VO_ARGS_BASE10, &strd_tmp); /* 3rd arg */ ++ if (ret != 0) { ++ printf("parse strd failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[4], CMD_VO_ARGS_BASE10, &x_tmp); /* 4th arg */ ++ if (ret != 0) { ++ printf("parse x failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[5], CMD_VO_ARGS_BASE10, &y_tmp); /* 5th arg */ ++ if (ret != 0) { ++ printf("parse y failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[6], CMD_VO_ARGS_BASE10, &width_tmp); /* 6th arg */ ++ if (ret != 0) { ++ printf("parse width failed.\n"); ++ return -1; ++ } ++ ret = strict_strtoul(argv[7], CMD_VO_ARGS_BASE10, &height_tmp); /* 7th arg */ ++ if (ret != 0) { ++ printf("parse height failed.\n"); ++ return -1; ++ } ++ *layer = (unsigned int)layer_tmp; ++ *addr = addr_tmp; ++ *strd = (unsigned int)strd_tmp; ++ layer_rect->x = (unsigned int)x_tmp; ++ layer_rect->y = (unsigned int)y_tmp; ++ layer_rect->width = (unsigned int)width_tmp; ++ layer_rect->height = (unsigned int)height_tmp; ++ return 0; ++} ++ ++static int start_gx_type_parse(char *const argv[], unsigned int *type) ++{ ++ int ret; ++ unsigned long type_tmp; ++ ret = strict_strtoul(argv[8], CMD_VO_ARGS_BASE10, &type_tmp); /* 8th arg */ ++ if (ret != 0) { ++ printf("parse type failed.\n"); ++ return -1; ++ } ++ *type = (unsigned int)type_tmp; ++ return 0; ++} ++ ++static int do_startgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int layer, strd, type; ++ unsigned long addr; ++ ot_rect gx_rect; ++ if (argc < 9) { /* max 9 args */ ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ret = start_layer_parse(argv, &layer, &addr, &strd, &gx_rect); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ++ ret = start_gx_type_parse(argv, &type); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ++ ret = start_gx(layer, addr, strd, gx_rect, type); ++ if (ret != 0) { ++ printf(INSUFFICIENT_PARAM_STR); ++ return -1; ++ } ++ ++ printf("graphic layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int stopgx_parse(char *const argv[], unsigned int *layer) ++{ ++ int ret; ++ unsigned long layer_tmp; ++ ret = strict_strtoul(argv[1], CMD_VO_ARGS_BASE10, &layer_tmp); /* 1st arg */ ++ if (ret != 0) { ++ printf("parse layer failed.\n"); ++ return -1; ++ } ++ *layer = (unsigned int)layer_tmp; ++ return 0; ++} ++ ++static int do_stopgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int layer; ++ ++ if (argc < 2) { /* max 2 args */ ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ ret = stopgx_parse(argv, &layer); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ++ ret = stop_gx(layer); ++ if (ret != 0) { ++ printf(INSUFFICIENT_PARAM_STR); ++ return -1; ++ } ++ ++ printf("graphic layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++static int do_startvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int layer, strd; ++ unsigned long addr; ++ ot_rect layer_rect; ++ ++ if (argc < 8) { /* max 8 args */ ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ ret = start_layer_parse(argv, &layer, &addr, &strd, &layer_rect); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ++ ret = start_videolayer(layer, addr, strd, layer_rect); ++ if (ret != 0) { ++ printf(INSUFFICIENT_PARAM_STR); ++ return -1; ++ } ++ ++ printf("video layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int stopvl_parse(char *const argv[], unsigned int *layer) ++{ ++ int ret; ++ unsigned long layer_tmp; ++ ret = strict_strtoul(argv[1], CMD_VO_ARGS_BASE10, &layer_tmp); /* 1st arg */ ++ if (ret != 0) { ++ printf("parse layer failed.\n"); ++ return -1; ++ } ++ *layer = (unsigned int)layer_tmp; ++ return 0; ++} ++ ++static int do_stopvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ int ret; ++ unsigned int layer; ++ ++ if (argc < 2) { /* max 2 args */ ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ret = stopvl_parse(argv, &layer); ++ if (ret != 0) { ++ printf("insufficient parameter!\n"); ++ return -1; ++ } ++ ++ ret = stop_videolayer(layer); ++ if (ret != 0) { ++ printf(INSUFFICIENT_PARAM_STR); ++ return -1; ++ } ++ ++ printf("video layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++#if ((defined(CHIP_SS528V100)) || (defined(CHIP_SS625V100))) ++#define USAGE_DEV_STRING \ ++ "\t- : 0(DHD0), 1(DHD1), 2(DSD0)\n" ++ ++#define USAGE_INTF_STRING \ ++ "\t-: 1(CVBS), 2(VGA), 8(BT.1120), 16(HDMI)\n" \ ++ ++#define USAGE_GX_STRING \ ++ "\t- : 4(G0), 5(G1), 7(G3)\n" ++ ++#define USAGE_VL_STRING \ ++ "\t- : 0(V0), 1(V1), 3(V3)\n" ++ ++#elif ((defined(CHIP_SS524V100)) || (defined(CHIP_SS615V100))) ++ ++#define USAGE_DEV_STRING \ ++ "\t- : 0(DHD0), 1(DHD1), 2(DSD0)\n" ++ ++#define USAGE_INTF_STRING \ ++ "\t-: 1(CVBS), 2(VGA), 4(BT.656), 8(BT.1120), 16(HDMI)\n" \ ++ ++#define USAGE_GX_STRING \ ++ "\t- : 4(G0), 5(G1), 7(G3)\n" ++ ++#define USAGE_VL_STRING \ ++ "\t- : 0(V0), 1(V1), 3(V3)\n" ++ ++#elif (defined(CHIP_SS522V101)) ++ ++#define USAGE_DEV_STRING \ ++ "\t- : 0(DHD0), 2(DSD0)\n" ++ ++#define USAGE_INTF_STRING \ ++ "\t-: 1(CVBS), 2(VGA), 4(BT.656), 8(BT.1120), 16(HDMI)\n" \ ++ "\t\t128(RGB_16BIT), 256(RGB_18BIT), 512(RGB_24BIT)\n" ++ ++#define USAGE_GX_STRING \ ++ "\t- : 4(G0), 7(G3)\n" ++ ++#define USAGE_VL_STRING \ ++ "\t- : 0(V0), 3(V3)\n" ++ ++#elif (defined(CHIP_SS928V100)) || (defined(CHIP_SS927V100)) ++ ++#define USAGE_DEV_STRING \ ++ "\t- : 0(DHD0), 1(DHD1)\n" ++ ++#define USAGE_INTF_STRING \ ++ "\t-: 1(CVBS), 4(BT.656), 8(BT.1120), 16(HDMI)\n" \ ++ "\t\t32(RGB_6BIT), 64(RGB_8BIT), 128(RGB_16BIT), 256(RGB_18BIT), 512(RGB_24BIT)\n" \ ++ "\t\t1024(MIPI), 2048(MIPI_SLAVE)\n" ++ ++#define USAGE_GX_STRING \ ++ "\t- : 3(G0), 4(G1)\n" ++ ++#define USAGE_VL_STRING \ ++ "\t- : 0(V0), 1(V1)\n" ++ ++#else ++ ++#define USAGE_DEV_STRING \ ++ "\t- : 0(DHD0), 1(DHD1, 2(DSD0)\n" ++ ++#define USAGE_INTF_STRING \ ++ "\t-: 1(CVBS), 2(VGA), 8(BT.1120), 16(HDMI)\n" \ ++ "\t\t32(RGB_6BIT), 64(RGB_8BIT), 128(RGB_16BIT), 256(RGB_18BIT), 512(RGB_24BIT)\n" ++ ++#define USAGE_GX_STRING \ ++ "\t- : 4(G0), 5(G1), 7(G3)\n" ++ ++#define USAGE_VL_STRING \ ++ "\t- : 0(V0), 1(V1), 3(V3)\n" ++ ++#endif ++ ++#define USAGE_GX_TYPE \ ++ "\t- : 0(argb1555), 1(bmp1555)\n" ++ ++#define USAGE_START_VO \ ++ "\nargs: [dev, intftype, sync]\n" \ ++ USAGE_DEV_STRING \ ++ USAGE_INTF_STRING \ ++ "\t-:\n" \ ++ "\t\t0(PAL), 1(NTSC), 2(960H_PAL), 3(960H_NTSC)\n" \ ++ "\t\t4(640x480_60), 5(480P60), 6(576P50), 7(800x600_60)\n" \ ++ "\t\t8(1024x768_60), 9(720P50), 10(720P60), 11(1280x800_60)\n" \ ++ "\t\t12(1280x1024_60),13(1366x768_60), 14(1400x1050_60),15(1440x900_60)\n" \ ++ "\t\t16(1680x1050_60),17(1080P24), 18(1080P25), 19(1080P30)\n" \ ++ "\t\t20(1080I50), 21(1080I60), 22(1080P50), 23(1080P60)\n" \ ++ "\t\t24(1600x1200_60),25(1920x1200_60),26(1920x2160_30),27(2560x1440_30)\n" \ ++ "\t\t28(2560x1440_60),29(2560x1600_60),30(3840x2160_24),31(3840x2160_25)\n" \ ++ "\t\t32(3840x2160_30),33(3840x2160_50),34(3840x2160_60),35(4096x2160_24)\n" \ ++ "\t\t36(4096x2160_25),37(4096x2160_30),38(4096x2160_50),39(4096x2160_60)\n" \ ++ "\t\t40(7680x4320_30),41(240x320_50), 42(320x240_50), 43(240x320_60)\n" \ ++ "\t\t44(320x240_60), 45(800x600_50), 46(720x1280_60), 47(1080x1920_60)\n" \ ++ "\t\t48(user)\n" ++ ++#define USAGE_STOP_VO \ ++ "\nargs: [dev]\n" \ ++ USAGE_DEV_STRING ++ ++#define USAGE_START_GX \ ++ "\nargs: [layer, addr, stride, x, y, w, h, type]\n" \ ++ USAGE_GX_STRING \ ++ "\t- : picture address\n" \ ++ "\t- : picture stride\n" \ ++ "\t- : display area\n" \ ++ USAGE_GX_TYPE ++ ++#define USAGE_STOP_GX \ ++ "\nargs: [layer]\n" \ ++ USAGE_GX_STRING ++ ++#define USAGE_START_VL \ ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" \ ++ USAGE_VL_STRING \ ++ "\t- : picture address\n" \ ++ "\t- : picture stride\n" \ ++ "\t- : display area\n" ++ ++#define USAGE_STOP_VL \ ++ "\nargs: [layer]\n" \ ++ USAGE_VL_STRING ++ ++#define USAGE_SET_VO_BG \ ++ "\nargs: [dev, color]\n" \ ++ USAGE_DEV_STRING \ ++ "\t-: rgb color space\n" ++ ++U_BOOT_CMD(startvo, CFG_MAXARGS_STARTVO, 1, do_startvo, ++ "startvo - open vo device with a certain output interface.\n" ++ "\t- startvo [dev intftype sync]", USAGE_START_VO); ++ ++U_BOOT_CMD(stopvo, CFG_MAXARGS_STOPVO, 1, do_stopvo, ++ "stopvo - close interface of vo device.\n" ++ "\t- stopvo [dev]", USAGE_STOP_VO); ++ ++U_BOOT_CMD(startgx, CFG_MAXARGS_STARTGX, 1, do_startgx, ++ "startgx - open graphics layer.\n" ++ "\t- startgx [layer addr stride x y w h type]", USAGE_START_GX); ++ ++U_BOOT_CMD(stopgx, CFG_MAXARGS_STOPGX, 1, do_stopgx, ++ "stopgx - close graphics layer.\n" ++ "\t- stopgx [layer]", USAGE_STOP_GX); ++ ++U_BOOT_CMD(startvl, CFG_MAXARGS_STARTVL, 1, do_startvl, ++ "startvl - open video layer.\n" ++ "\t- startvl [layer addr stride x y w h]", USAGE_START_VL); ++ ++U_BOOT_CMD(stopvl, CFG_MAXARGS_STOPVL, 1, do_stopvl, ++ "stopvl - close video layer.\n" ++ "\t- stopvl [layer]", USAGE_STOP_VL); ++ ++U_BOOT_CMD(setvobg, CFG_MAXARGS_SETVOBG, 1, do_vobg, ++ "setvobg - set vo background color.\n" ++ "\t- setvobg [dev color]", USAGE_SET_VO_BG); +diff --git a/cmd/cmd_vo_ss015v100.c b/cmd/cmd_vo_ss015v100.c +new file mode 100644 +index 0000000..a8e5f83 +--- /dev/null ++++ b/cmd/cmd_vo_ss015v100.c +@@ -0,0 +1,559 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ss919v100_vo.h" ++ ++#define CMD_VO_ARGS_BASE10 10 ++#define CMD_VO_ARGS_BASE16 16 ++#define CMD_VO_ARGS_BASE_ALL 0 ++ ++extern int set_vobg(unsigned int dev, unsigned int rgb); ++extern int start_vo(unsigned int dev, unsigned int type, unsigned int sync); ++extern int stop_vo(unsigned int dev); ++extern int start_gx(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect gx_rect); ++extern int stop_gx(unsigned int layer); ++extern int start_videolayer(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect layer_rect); ++extern int stop_videolayer(unsigned int layer); ++ ++#if CONFIG_HDMI_SUPPORT ++extern int hdmi_display(unsigned int vosync, unsigned int input, unsigned int output); ++extern void hdmi_stop(void); ++#endif ++ ++extern int mipi_tx_display(unsigned int vosync); ++extern int mipi_tx_stop(void); ++ ++#define VO_DEV_MAX_NUM 3 ++static unsigned int g_a_interface_type[VO_DEV_MAX_NUM] = {[0 ... (VO_DEV_MAX_NUM - 1)] = 0}; ++ ++static int vo_is_lcd_intf(unsigned int intf_type) ++{ ++ if (VO_INTF_LCD & intf_type || ++ VO_INTF_LCD_8BIT & intf_type || ++ VO_INTF_LCD_6BIT & intf_type || ++ VO_INTF_LCD_16BIT & intf_type || ++ VO_INTF_LCD_18BIT & intf_type || ++ VO_INTF_LCD_24BIT & intf_type) { ++ return 1; ++ } ++ return 0; ++} ++ ++static int vou_drv_check_lcd_sync(vo_dev dev, unsigned int intf_type, unsigned int intf_sync) ++{ ++ if (dev == VO_DEV_DHD0) { ++ if (intf_sync != VO_OUTPUT_USER) { ++ printf("DHD0 only support VO_OUTPUT_USER when intf is LCD!\n"); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_8BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_320x240_60) { ++ printf("for LCD 8bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_6BIT & intf_type) { ++ if ((intf_sync < VO_OUTPUT_320x240_50) || (intf_sync > VO_OUTPUT_240x320_50)) { ++ printf("for LCD 6bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_16BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_240x320_60) { ++ printf("for LCD 16bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_18BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_240x320_60) { ++ printf("for LCD 18bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_24BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_800x600_50) { ++ printf("for LCD 24bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int check_vo_support(unsigned int dev, unsigned int type, unsigned int sync) ++{ ++ /* check interface type, ONLY VGA & HDMI interface is supported. */ ++ if (dev == VO_DEV_DHD0) { ++ if ((type & ~(VO_INTF_MIPI | VO_INTF_HDMI | VO_INTF_BT656 | ++ VO_INTF_BT1120 | VO_INTF_LCD_8BIT | VO_INTF_LCD_6BIT | ++ VO_INTF_LCD_16BIT | VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) || ++ (type == 0)) { ++ printf("hd%u only supports HDMI,BT.656,BT.1120,mipi_tx,lcd intftype, intf %u is illegal!\n", dev, type); ++ return -1; ++ } ++ /* just one interface at the the time for a dev. */ ++ if ((type & ~(VO_INTF_HDMI | VO_INTF_MIPI)) && ++ (type & ~VO_INTF_BT656) && ++ (type & ~VO_INTF_LCD_8BIT) && ++ (type & ~VO_INTF_LCD_6BIT) && ++ (type & ~VO_INTF_LCD_18BIT) && ++ (type & ~VO_INTF_LCD_16BIT) && ++ (type & ~VO_INTF_LCD_24BIT)) { ++ printf("for VO %u, only HDMI+MIPI can be used at the same time!\n", ++ dev); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((type & ~(VO_INTF_BT1120 | VO_INTF_BT656 | VO_INTF_MIPI | ++ VO_INTF_LCD_6BIT | VO_INTF_LCD_8BIT | VO_INTF_LCD_16BIT | ++ VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) || (type == 0)) { ++ printf("hd%u only supports BT.656,BT.1120,mipi_tx,LCD intftype, intf %u is illegal!\n", dev, type); ++ return -1; ++ } ++ /* just one interface at the the time for a dev. */ ++ if ((type & ~VO_INTF_BT1120) && (type & ~VO_INTF_BT656) && (type & ~VO_INTF_MIPI) && ++ (type & ~VO_INTF_LCD_6BIT) && (type & ~VO_INTF_LCD_8BIT) && ++ (type & ~VO_INTF_LCD_16BIT) && (type & ~VO_INTF_LCD_18BIT) && (type & ~VO_INTF_LCD_24BIT)) { ++ printf("vo(%u), none of (BT.1120,BT.656,MIPI,LCD) can use at the same time!\n", ++ dev); ++ return -1; ++ } ++ ++ } else { ++ printf("unknown dev(%u)!\n", dev); ++ return -1; ++ } ++ ++ if (sync == VO_OUTPUT_USER) { ++ return 0; ++ } ++ ++ /* check interface sync. */ ++ if (VO_INTF_HDMI & type) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (VO_OUTPUT_1080I50 == sync) || ++ (VO_OUTPUT_1080I60 == sync) || ++ (VO_OUTPUT_960H_PAL == sync) || ++ (VO_OUTPUT_960H_NTSC == sync) || ++ ((sync >= VO_OUTPUT_320x240_60) && (sync <= VO_OUTPUT_1080x1920_60)) || ++ (sync == VO_OUTPUT_7680x4320_30) || ++ (sync >= VO_OUTPUT_USER)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_BT1120 & type) { ++ if (dev == VO_DEV_DHD0) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (VO_OUTPUT_1080I60 == sync) || ++ (VO_OUTPUT_1080I50 == sync) || ++ (sync > VO_OUTPUT_640x480_60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (sync > VO_OUTPUT_480P60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ } ++ ++ if (VO_INTF_MIPI & type) { ++ if (dev == VO_DEV_DHD0) { ++ if ((sync != VO_OUTPUT_576P50) && ++ (sync != VO_OUTPUT_720P50) && ++ (sync != VO_OUTPUT_720P60) && ++ (sync != VO_OUTPUT_1024x768_60) && ++ (sync != VO_OUTPUT_1280x1024_60) && ++ (sync != VO_OUTPUT_720x1280_60) && ++ (sync != VO_OUTPUT_1080x1920_60) && ++ (sync != VO_OUTPUT_1080P60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((sync != VO_OUTPUT_576P50) && ++ (sync != VO_OUTPUT_720P50) && ++ (sync != VO_OUTPUT_720P60) && ++ (sync != VO_OUTPUT_1024x768_60) && ++ (sync != VO_OUTPUT_1280x1024_60) && ++ (sync != VO_OUTPUT_720x1280_60) && ++ (sync != VO_OUTPUT_1080x1920_60) && ++ (sync != VO_OUTPUT_1080P60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ } ++ ++ if (vo_is_lcd_intf(type)) { ++ if (vou_drv_check_lcd_sync(dev, type, sync) != 0) { ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int do_vobg(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev, rgb; ++ ++ if (argc < 3) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ rgb = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE_ALL); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ set_vobg(dev, rgb); ++ ++ printf("dev %u set background color!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev, intftype, sync; ++ ++ if (argc < 4) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ intftype = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE10); ++ sync = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ if ((dev >= VO_DEV_BUTT) || (sync >= VO_OUTPUT_BUTT)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if (check_vo_support(dev, intftype, sync)) { ++ printf("unsupported parameter!\n"); ++ return -1; ++ } ++ ++ start_vo(dev, intftype, sync); ++ ++ g_a_interface_type[dev] = intftype; ++ ++#if CONFIG_HDMI_SUPPORT ++ if (intftype & VO_INTF_HDMI) { ++ if (intftype == (VO_INTF_HDMI | VO_INTF_MIPI)) { ++ hdmi_display(sync, 0, 2); ++ } else { ++ hdmi_display(sync, 2, 2); ++ } ++ } ++#endif ++ ++ if (intftype & VO_INTF_MIPI) { ++ // to call mipi_display. ++ mipi_tx_display(sync); ++ } ++ ++ if (vo_is_lcd_intf(intftype)) { ++ g_a_interface_type[dev] = intftype; ++ } ++ ++ printf("dev %u opened!\n", dev); ++ ++ return 0; ++} ++ ++static int do_stopvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev; ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++#if CONFIG_HDMI_SUPPORT ++ if (g_a_interface_type[dev] & VO_INTF_HDMI) { ++ g_a_interface_type[dev] = 0; ++ hdmi_stop(); ++ } ++#endif ++ ++ if (g_a_interface_type[dev] & VO_INTF_MIPI) { ++ g_a_interface_type[dev] = 0; ++ mipi_tx_stop(); ++ } ++ ++ if (vo_is_lcd_intf(g_a_interface_type[dev])) { ++ g_a_interface_type[dev] = 0; ++ } ++ ++ stop_vo(dev); ++ ++ printf("dev %u closed!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect gx_rect; ++ int max_x, max_y, layer_max_w, layer_max_h; ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer == VO_GRAPHC_G0) { ++ max_x = PIC_MAX_WIDTH; ++ max_y = PIC_MAX_HEIGHT; ++ layer_max_w = GFX0_PIC_MAX_WIDTH; ++ layer_max_h = GFX0_PIC_MAX_HEIGHT; ++ } else if (layer == VO_GRAPHC_G1) { ++ max_x =GFX1_PIC_MAX_WIDTH; ++ max_y = GFX1_PIC_MAX_HEIGHT; ++ layer_max_w = GFX1_PIC_MAX_WIDTH; ++ layer_max_h = GFX1_PIC_MAX_HEIGHT; ++ } else { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if((strd > (layer_max_w * 2)) || ++ (x > max_x) || (x & 0x1) || ++ (y > max_y) || (y & 0x1) || ++ (w > layer_max_w) || (w & 0x1) || (w < PIC_MIN_LENTH) || ++ (h > layer_max_h) || (h & 0x1) || (h < PIC_MIN_LENTH)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ gx_rect.x = x; ++ gx_rect.y = y; ++ gx_rect.w = w; ++ gx_rect.h = h; ++ ++ start_gx(layer, addr, strd, gx_rect); ++ ++ printf("graphic layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer >= VO_GRAPHC_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_gx(layer); ++ ++ printf("graphic layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++static int do_startvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect layer_rect; ++ int max_x, max_y, layer_max_w, layer_max_h; ++ ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer == VO_LAYER_VHD0) { ++ max_x = VHD0_PIC_MAX_WIDTH; ++ max_y = VHD0_PIC_MAX_HEIGHT; ++ layer_max_w = VHD0_PIC_MAX_WIDTH; ++ layer_max_h = VHD0_PIC_MAX_HEIGHT; ++ } else if (layer == VO_LAYER_VHD1) { ++ max_x =VHD1_PIC_MAX_WIDTH; ++ max_y = VHD1_PIC_MAX_HEIGHT; ++ layer_max_w = VHD1_PIC_MAX_WIDTH; ++ layer_max_h = VHD1_PIC_MAX_HEIGHT; ++ } else { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if((strd > (layer_max_w * 2)) || ++ (x > max_x) || (x & 0x1) || ++ (y > max_y) || (y & 0x1) || ++ (w > layer_max_w) || (w & 0x1) || (w < PIC_MIN_LENTH) || ++ (h > layer_max_h) || (h & 0x1) || (h < PIC_MIN_LENTH)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ layer_rect.x = x; ++ layer_rect.y = y; ++ layer_rect.w = w; ++ layer_rect.h = h; ++ ++ start_videolayer(layer, addr, strd, layer_rect); ++ ++ printf("video layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer > VO_LAYER_VSD0) || (layer == VO_LAYER_VP)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_videolayer(layer); ++ ++ printf("video layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++U_BOOT_CMD(startvo, CFG_MAXARGS, 1, do_startvo, ++ "startvo - open vo device with a certain output interface.\n" ++ "\t- startvo [dev intftype sync]", ++ "\nargs: [dev, intftype, sync]\n" ++ "\t- : 0: DHD0,1: DHD1\n" ++ "\t-: 8(BT.656),16(BT.1120),32(HDMI),64(LCD),1024(LCD_8BIT),16384(mipi_tx)\n" ++ "\t-:\n" ++ "\t\t0(PAL), 1(NTSC), 2(1080P24), 3(1080P25)\n" ++ "\t\t4(1080P30), 5(720P50), 6(720P60), 7(1080I50)\n" ++ "\t\t8(1080I60), 9(1080P50), 10(1080P60), 11(576P50)\n" ++ "\t\t12(480P60), 13(800x600), 14(1024x768), 15(1280x1024)\n" ++ "\t\t16(1366x768), 17(1440x900), 18(1280x800), 19(1600x1200)\n" ++ "\t\t20(1680x1050), 21(1920x1200), 22(640x480), 23(960H_PAL)\n" ++ "\t\t24(960H_NTSC), 25(1920x2160), 26(2560x1440_30),27(2560x1440_60)\n" ++ "\t\t28(2560x1600_60),29(3840x2160_24),30(3840x2160_25),31(3840x2160_30)\n" ++ "\t\t32(3840x2160_50),33(3840x2160_60),34(4096x2160_24),35(4096x2160_25)\n" ++ "\t\t36(4096x2160_30),37(4096x2160_50),38(4096x2160_60),39(320x240_60)\n" ++ "\t\t40(320x240_50), 41(240x320_50), 42(240x320_60), 43(800x600_50)\n" ++ "\t\t44(720x1280_60), 45(1080x1920_60),46(7680x4320_30),47(user)\n"); ++ ++U_BOOT_CMD(stopvo, CFG_MAXARGS, 1, do_stopvo, ++ "stopvo - close interface of vo device.\n" ++ "\t- stopvo [dev]", ++ "\nargs: [dev]\n" ++ "\t- : 0~1(HD0~1)\n"); ++ ++U_BOOT_CMD(startgx, CFG_MAXARGS, 1, do_startgx, ++ "startgx - open graphics layer.\n" ++ "\t- startgx [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(G0), 1(G1)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD(stopgx, CFG_MAXARGS, 1, do_stopgx, ++ "stopgx - close graphics layer.\n" ++ "\t- stopgx [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(G0), 1(G1)\n"); ++ ++U_BOOT_CMD(startvl, CFG_MAXARGS, 1, do_startvl, ++ "startvl - open video layer.\n" ++ "\t- startvl [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(V0), 1(V1)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD(stopvl, CFG_MAXARGS, 1, do_stopvl, ++ "stopvl - close video layer.\n" ++ "\t- stopvl [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(V0), 1(V1)\n"); ++ ++U_BOOT_CMD(setvobg, CFG_MAXARGS, 1, do_vobg, ++ "setvobg - set vo background color.\n" ++ "\t- setvobg [dev color]", ++ "\nargs: [dev, color]\n" ++ "\t- : 0~1(HD0~1)\n" ++ "\t-: rgb color space\n"); +diff --git a/cmd/cmd_vo_ss101v200.c b/cmd/cmd_vo_ss101v200.c +new file mode 100644 +index 0000000..6bfb3d6 +--- /dev/null ++++ b/cmd/cmd_vo_ss101v200.c +@@ -0,0 +1,358 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ss101v200_vo.h" ++ ++#define CMD_VO_ARGS_BASE10 10 ++#define CMD_VO_ARGS_BASE16 16 ++#define CMD_VO_ARGS_BASE_ALL 0 ++ ++extern int set_vobg(unsigned int dev, unsigned int rgb); ++extern int start_vo(unsigned int dev, unsigned int type, unsigned int sync); ++extern int stop_vo(unsigned int dev); ++extern int start_videolayer(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect layer_rect); ++extern int stop_videolayer(unsigned int layer); ++extern int start_gx(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect gx_rect); ++extern int stop_gx(unsigned int layer); ++ ++static int check_vo_support(unsigned int dev, unsigned int type, unsigned int sync) ++{ ++ /* check interface type, ONLY VGA & HDMI interface is supported. */ ++ if (dev == VO_DEV_DHD0) { ++ if ((type & (~(VO_INTF_BT1120 | VO_INTF_BT656 | VO_INTF_LCD_6BIT | VO_INTF_LCD_8BIT | VO_INTF_LCD_16BIT))) || ++ (type == 0)) { ++ printf("hd%u only supports BT.656,BT.1120,LCD_6BIT/8BIT/16BIT intftype, intf %u is illegal!\n", ++ dev, type); ++ return -1; ++ } ++ } else { ++ printf("unknown dev(%u)!\n", dev); ++ return -1; ++ } ++ ++ if (sync == VO_OUTPUT_USER) { ++ return 0; ++ } ++ ++ if (VO_INTF_BT1120 & type) { ++ if ((((sync < VO_OUTPUT_1080P24) && (sync > VO_OUTPUT_1080I60)) || ++ ((sync < VO_OUTPUT_576P50) && (sync > VO_OUTPUT_1024x768_60))) && ++ (sync != VO_OUTPUT_640x480_60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_BT656 & type) { ++ if ((sync != VO_OUTPUT_PAL) && (sync != VO_OUTPUT_NTSC)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_8BIT & type) { ++ if (VO_OUTPUT_320x240_60 != sync) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_6BIT & type) { ++ if ((sync < VO_OUTPUT_320x240_50) || (sync > VO_OUTPUT_240x320_50)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_16BIT & type) { ++ if (VO_OUTPUT_240x320_60 != sync) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int do_vobg(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev, rgb; ++ ++ if (argc < 3) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ rgb = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE_ALL); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ set_vobg(dev, rgb); ++ ++ printf("dev %u set background color!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev, intftype, sync; ++ ++ if (argc < 4) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ intftype = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE10); ++ sync = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ if ((dev >= VO_DEV_BUTT) || (sync >= VO_OUTPUT_BUTT)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if (check_vo_support(dev, intftype, sync)) { ++ printf("unsupported parameter!\n"); ++ return -1; ++ } ++ ++ start_vo(dev, intftype, sync); ++ ++ printf("dev %u opened!\n", dev); ++ ++ return 0; ++} ++ ++static int do_stopvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev; ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_vo(dev); ++ ++ printf("dev %u closed!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect layer_rect; ++ ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer != VO_LAYER_VHD0) || ++ (strd > (PIC_MAX_WIDTH * 2)) || ++ ((x > PIC_MAX_WIDTH) || (x & 0x1)) || ++ ((y > PIC_MAX_HEIGHT) || (y & 0x1)) || ++ ((w > PIC_MAX_WIDTH) || (w & 0x1) || (w < PIC_MIN_LENTH)) || ++ ((h > PIC_MAX_HEIGHT) || (h & 0x1) || (h < PIC_MIN_LENTH))) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ layer_rect.x = x; ++ layer_rect.y = y; ++ layer_rect.w = w; ++ layer_rect.h = h; ++ ++ start_videolayer(layer, addr, strd, layer_rect); ++ ++ printf("video layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer > VO_LAYER_VHD0) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_videolayer(layer); ++ ++ printf("video layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++static int do_startgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect gx_rect; ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer != VO_GRAPHC_G0) || ++ ((x > PIC_MAX_WIDTH) || (x & 0x1)) || ++ ((y > PIC_MAX_HEIGHT) || (y & 0x1)) || ++ ((w > PIC_MAX_WIDTH) || (w & 0x1) || (w < PIC_MIN_LENTH)) || ++ ((h > PIC_MAX_HEIGHT) || (h & 0x1) || (h < PIC_MIN_LENTH))) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ gx_rect.x = x; ++ gx_rect.y = y; ++ gx_rect.w = w; ++ gx_rect.h = h; ++ ++ start_gx(layer, addr, strd, gx_rect); ++ ++ printf("graphic layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer >= VO_GRAPHC_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_gx(layer); ++ ++ printf("graphic layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++U_BOOT_CMD(startvo, CFG_MAXARGS, 1, do_startvo, ++ "startvo - open vo device with a certain output interface.\n" ++ "\t- startvo [dev intftype sync]", ++ "\nargs: [dev, intftype, sync]\n" ++ "\t- : 0: DHD0\n" ++ "\t-: 8(BT.656),16(BT.1120),512(LCD_6BIT),1024(LCD_8BIT),2048(LCD_16BIT)\n" ++ "\t-: typical value:\n" ++ "\t\tfor BT.656:\n" ++ "\t\t\t0(PAL), 1(NTSC)\n" ++ "\t\tfor BT.1120:\n" ++ "\t\t\t2(1080P24), 3(1080P25), 4(1080P30), 5(720P50)\n" ++ "\t\t\t6(720P60), 7(1080I50), 8(1080I60), 11(576P50)\n" ++ "\t\t\t12(480P60), 13(800x600), 14(1024x768), 22(640x480)\n" ++ "\t\tfor LCD:\n" ++ "\t\t\t39(320x240P60), 40(320x240P50), 41(240x320P50), 42(240x320P60)\n"); ++ ++U_BOOT_CMD(stopvo, CFG_MAXARGS, 1, do_stopvo, ++ "stopvo - close interface of vo device.\n" ++ "\t- stopvo [dev]", ++ "\nargs: [dev]\n" ++ "\t- : 0(HD0)\n"); ++ ++U_BOOT_CMD(startvl, CFG_MAXARGS, 1, do_startvl, ++ "startvl - open video layer.\n" ++ "\t- startvl [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(V0)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD(stopvl, CFG_MAXARGS, 1, do_stopvl, ++ "stopvl - close video layer.\n" ++ "\t- stopvl [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(V0)\n"); ++ ++U_BOOT_CMD(setvobg, CFG_MAXARGS, 1, do_vobg, ++ "setvobg - set vo background color.\n" ++ "\t- setvobg [dev color]", ++ "\nargs: [dev, color]\n" ++ "\t- : 0(HD0)\n" ++ "\t-: rgb color space\n"); ++ ++U_BOOT_CMD(startgx, CFG_MAXARGS, 1, do_startgx, ++ "startgx - open graphics layer.\n" ++ "\t- startgx [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(G0)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD(stopgx, CFG_MAXARGS, 1, do_stopgx, ++ "stopgx - close graphics layer.\n" ++ "\t- stopgx [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(G0)\n"); +diff --git a/cmd/cmd_vo_ss812v100.c b/cmd/cmd_vo_ss812v100.c +new file mode 100644 +index 0000000..20c1be9 +--- /dev/null ++++ b/cmd/cmd_vo_ss812v100.c +@@ -0,0 +1,462 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ss812v100_vo.h" ++ ++#define CMD_VO_ARGS_BASE10 10 ++#define CMD_VO_ARGS_BASE16 16 ++#define CMD_VO_ARGS_BASE_ALL 0 ++ ++extern int set_vobg(unsigned int dev, unsigned int rgb); ++extern int start_vo(unsigned int dev, unsigned int type, unsigned int sync); ++extern int stop_vo(unsigned int dev); ++extern int start_videolayer(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect layer_rect); ++extern int stop_videolayer(unsigned int layer); ++ ++extern int hdmi_display(unsigned int vosync, unsigned int input, unsigned int output); ++extern void hdmi_stop(void); ++ ++extern int mipi_tx_display(unsigned int vosync); ++extern int mipi_tx_stop(void); ++ ++extern int start_gx(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect gx_rect); ++extern int stop_gx(unsigned int layer); ++ ++extern void rgb_display(void); ++extern void rgb_stop(void); ++ ++static unsigned int g_a_interface_type = 0; ++ ++static int vo_check_lcd_sync(unsigned int dev, unsigned int type, unsigned int sync) ++{ ++ if (VO_INTF_LCD_8BIT & type) { ++ if (VO_OUTPUT_320x240_60 != sync) { ++ printf("Vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_6BIT & type) { ++ if (sync < VO_OUTPUT_320x240_50 || sync > VO_OUTPUT_240x320_50) { ++ printf("Vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_16BIT & type) { ++ if (VO_OUTPUT_240x320_60 != sync) { ++ printf("Vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_18BIT & type) { ++ if (sync != VO_OUTPUT_USER) { ++ printf("Vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_24BIT & type) { ++ if (VO_OUTPUT_800x600_50 != sync) { ++ printf("Vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int check_vo_support(unsigned int dev, unsigned int type, unsigned int sync) ++{ ++ /* check interface type, ONLY VGA & HDMI interface is supported. */ ++ if (dev == VO_DEV_DHD0) { ++ if ((type & ~(VO_INTF_HDMI | VO_INTF_BT1120 | VO_INTF_BT656 | VO_INTF_MIPI | VO_INTF_MIPI_SLAVE | VO_INTF_LCD_6BIT | ++ VO_INTF_LCD_8BIT | VO_INTF_LCD_16BIT | VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) || (0 == type)) { ++ printf("hd%u only supports HDMI, BT.656, BT.1120, mipi_tx, LCD intftype, intf %u is illegal!\n", dev, type); ++ return -1; ++ } ++ /* just one interface at the the time for a dev. */ ++ if ((type & ~VO_INTF_HDMI) && (type & ~VO_INTF_BT1120) && ++ (type & ~VO_INTF_BT656) && (type & ~VO_INTF_MIPI) && ++ (type & ~VO_INTF_MIPI_SLAVE) && (type & ~VO_INTF_LCD_6BIT) && ++ (type & ~VO_INTF_LCD_8BIT) && (type & ~VO_INTF_LCD_16BIT) && ++ (type & ~VO_INTF_LCD_18BIT) && (type & ~VO_INTF_LCD_24BIT)) { ++ printf("for VO %u, not support interface be used at the same time!\n", dev); ++ return -1; ++ } ++ } else { ++ printf("unknown dev(%u)!\n", dev); ++ return -1; ++ } ++ ++ if (sync & VO_OUTPUT_USER) { ++ return 0; ++ } ++ if (VO_INTF_HDMI & type) { ++ if (!(((sync >= VO_OUTPUT_1080P24) && (sync <= VO_OUTPUT_640x480_60)) || ++ ((sync >= VO_OUTPUT_1920x2160_30) && (sync <= VO_OUTPUT_4096x2160_60)))) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_BT1120 & type) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (sync > VO_OUTPUT_7680x4320_30)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_BT656 & type) { ++ if ((sync != VO_OUTPUT_PAL) && (sync != VO_OUTPUT_NTSC)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if(vo_check_lcd_sync(dev, type, sync)) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int do_vobg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int dev, rgb; ++ ++ if (argc < 3) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ rgb = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE_ALL); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ set_vobg(dev, rgb); ++ ++ printf("dev %u set background color!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int dev, intftype, sync; ++ ++ if (argc < 4) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ intftype = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE10); ++ sync = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ if ((dev >= VO_DEV_BUTT) || (sync >= VO_OUTPUT_BUTT)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if (check_vo_support(dev, intftype, sync)) { ++ printf("unsupported parameter!\n"); ++ return -1; ++ } ++ ++ start_vo(dev, intftype, sync); ++ ++ if (intftype & VO_INTF_HDMI) { ++ g_a_interface_type = VO_INTF_HDMI; ++#ifndef CONFIG_OSD_HDMI_DISABLE ++ hdmi_display(sync, 2, 2); ++#endif ++ } ++ ++ if (intftype & VO_INTF_MIPI) { ++ /* to call mipi_display. */ ++ g_a_interface_type = VO_INTF_MIPI; ++ mipi_tx_display(sync); ++ } ++ ++ if (intftype & (VO_INTF_LCD | VO_INTF_LCD_6BIT | VO_INTF_LCD_8BIT | VO_INTF_LCD_16BIT | ++ VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) { ++ g_a_interface_type = intftype; ++ rgb_display(); ++ } ++ ++ printf("dev %u opened!\n", dev); ++ ++ return 0; ++} ++ ++static int do_stopvo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int dev; ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if (g_a_interface_type & VO_INTF_HDMI) { ++ g_a_interface_type = 0; ++ hdmi_stop(); ++ } ++ ++ if (g_a_interface_type & VO_INTF_MIPI) { ++ g_a_interface_type = 0; ++ mipi_tx_stop(); ++ } ++ ++ if (g_a_interface_type & (VO_INTF_LCD | VO_INTF_LCD_6BIT | VO_INTF_LCD_8BIT | VO_INTF_LCD_16BIT | ++ VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) { ++ g_a_interface_type = 0; ++ rgb_stop(); ++ } ++ ++ stop_vo(dev); ++ ++ printf("dev %u closed!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvl(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect layer_rect; ++ ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer != VO_LAYER_VHD0) || ++ (strd > (PIC_MAX_WIDTH * 2)) || ++ ((x > PIC_MAX_WIDTH) || (x & 0x1)) || ++ ((y > PIC_MAX_HEIGHT) || (y & 0x1)) || ++ ((w > PIC_MAX_WIDTH) || (w & 0x1) || (w < PIC_MIN_LENTH)) || ++ ((h > PIC_MAX_HEIGHT) || (h & 0x1) || (h < PIC_MIN_LENTH)) ++ ) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ layer_rect.x = x; ++ layer_rect.y = y; ++ layer_rect.w = w; ++ layer_rect.h = h; ++ ++ start_videolayer(layer, addr, strd, layer_rect); ++ ++ printf("video layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopvl(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer > VO_LAYER_VSD0) || (layer == VO_LAYER_VP)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_videolayer(layer); ++ ++ printf("video layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++static int do_startgx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect gx_rect; ++ ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer != VO_GRAPHC_G0) || ++ ((x > PIC_MAX_WIDTH) || (x & 0x1)) || ++ ((y > PIC_MAX_HEIGHT) || (y & 0x1)) || ++ ((w > PIC_MAX_WIDTH) || (w & 0x1) || (w < PIC_MIN_LENTH)) || ++ ((h > PIC_MAX_HEIGHT) || (h & 0x1) || (h < PIC_MIN_LENTH)) ++ ) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ gx_rect.x = x; ++ gx_rect.y = y; ++ gx_rect.w = w; ++ gx_rect.h = h; ++ ++ start_gx(layer, addr, strd, gx_rect); ++ ++ printf("graphic layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopgx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer >= VO_GRAPHC_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_gx(layer); ++ ++ printf("graphic layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ startvo, CFG_MAXARGS, 1, do_startvo, ++ "startvo - open vo device with a certain output interface.\n" ++ "\t- startvo [dev intftype sync]", ++ "\nargs: [dev, intftype, sync]\n" ++ "\t- : 0: DHD0\n" ++ "\t-: 8(BT.656),16(BT.1120),32(HDMI),16384(mipi_tx)\n" ++ "\t\t512(LCD_6BIT),1024(LCD_8BIT),2048(LCD_16BIT),4096(LCD_18BIT), 8192(LCD_24BIT)\n" ++ "\t-: typical value:\n" ++ "\t\tfor BT.656:\n" ++ "\t\t\t0(PAL), 1(NTSC), 47(user)\n" ++ "\t\tfor HDMI and BT.1120:\n" ++ "\t\t\t2(1080P24), 3(1080P25), 4(1080P30), 5(720P50)\n" ++ "\t\t\t6(720P60), 7(1080I50) 8(1080I60) 9(1080P50)\n" ++ "\t\t\t10(1080P60), 11(576P50), 12(480P60), 13(800x600)\n" ++ "\t\t\t14(1024x768), 15(1280x1024), 16(1366x768), 17(1440x900)\n" ++ "\t\t\t18(1280x800), 19(1600x1200), 20(1680x1050), 21(1920x1200)\n" ++ "\t\t\t22(640x480), 47(user)\n" ++ "\t\tfor LCD:\n" ++ "\t\t\t39(320x240), 40(320x240), 41(240x320), 42(240x320)\n" ++ "\t\t\t43(800x600), 47(user)\n" ++ "\t\tfor MIPI:\n" ++ "\t\t\t44(720x1280), 45(1080x1920), 47(user)"); ++ ++ ++U_BOOT_CMD( ++ stopvo, CFG_MAXARGS, 1, do_stopvo, ++ "stopvo - close interface of vo device.\n" ++ "\t- stopvo [dev]", ++ "\nargs: [dev]\n" ++ "\t- : 0(HD0)\n"); ++ ++ ++U_BOOT_CMD( ++ startvl, CFG_MAXARGS, 1, do_startvl, ++ "startvl - open video layer.\n" ++ "\t- startvl [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(V0)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD( ++ stopvl, CFG_MAXARGS, 1, do_stopvl, ++ "stopvl - close video layer.\n" ++ "\t- stopvl [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(V0)\n"); ++ ++ ++U_BOOT_CMD( ++ setvobg, CFG_MAXARGS, 1, do_vobg, ++ "setvobg - set vo background color.\n" ++ "\t- setvobg [dev color]", ++ "\nargs: [dev, color]\n" ++ "\t- : 0(HD0)\n" ++ "\t-: rgb color space\n"); ++ ++U_BOOT_CMD( ++ startgx, CFG_MAXARGS, 1, do_startgx, ++ "startgx - open graphics layer.\n" ++ "\t- startgx [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(G0)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD( ++ stopgx, CFG_MAXARGS, 1, do_stopgx, ++ "stopgx - close graphics layer.\n" ++ "\t- stopgx [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(G0)\n"); +diff --git a/cmd/cmd_vo_ss918v100.c b/cmd/cmd_vo_ss918v100.c +new file mode 100644 +index 0000000..92a3f34 +--- /dev/null ++++ b/cmd/cmd_vo_ss918v100.c +@@ -0,0 +1,466 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ss918v100_vo.h" ++ ++#define CMD_VO_ARGS_BASE10 10 ++#define CMD_VO_ARGS_BASE16 16 ++#define CMD_VO_ARGS_BASE_ALL 0 ++ ++extern int set_vobg(unsigned int dev, unsigned int rgb); ++extern int start_vo(unsigned int dev, unsigned int type, unsigned int sync); ++extern int stop_vo(unsigned int dev); ++extern int start_videolayer(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect layer_rect); ++extern int stop_videolayer(unsigned int layer); ++ ++extern int hdmi_display(unsigned int vosync, unsigned int input, unsigned int output); ++extern void hdmi_stop(void); ++ ++extern int mipi_tx_display(unsigned int vosync); ++extern int mipi_tx_stop(void); ++ ++extern int start_gx(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect gx_rect); ++extern int stop_gx(unsigned int layer); ++ ++#define VO_DEV_MAX_NUM 3 ++static unsigned int g_a_interface_type[VO_DEV_MAX_NUM] = {[0 ... (VO_DEV_MAX_NUM - 1)] = 0}; ++ ++static int check_vo_support(unsigned int dev, unsigned int type, unsigned int sync) ++{ ++ /* check interface type, ONLY VGA & HDMI interface is supported. */ ++ if (dev == VO_DEV_DHD0) { ++ if ((type & ~(VO_INTF_HDMI | VO_INTF_BT1120 | VO_INTF_BT656 | VO_INTF_MIPI | VO_INTF_MIPI_SLAVE)) || ++ (type == 0)) { ++ printf("hd%u only supports HDMI, BT.656, BT.1120, mipi_tx intftype, intf %u is illegal!\n", dev, type); ++ return -1; ++ } ++ /* just one interface at the the time for a dev. */ ++ if (type & ~(VO_INTF_HDMI | VO_INTF_BT1120) && ++ (type & ~(VO_INTF_HDMI | VO_INTF_MIPI)) && ++ (type & ~VO_INTF_BT656) && ++ (type & ~VO_INTF_MIPI_SLAVE)) { ++ printf("for VO %u, only HDMI and BT.1120 can be used at the same time!\n", dev); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((type ++ & ~(VO_INTF_BT1120 | VO_INTF_BT656 | VO_INTF_MIPI | ++ VO_INTF_MIPI_SLAVE | VO_INTF_LCD_6BIT | VO_INTF_LCD_8BIT | ++ VO_INTF_LCD_16BIT | VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) || ++ (type == 0)) { ++ printf("hd%u only supports BT.656, BT.1120, mipi_tx, LCD intftype, intf %u is illegal!\n", dev, type); ++ return -1; ++ } ++ /* just one interface at the the time for a dev. */ ++ if ( ++ (type & ~VO_INTF_BT1120) && (type & ~VO_INTF_BT656) && ++ (type & ~VO_INTF_MIPI) && (type & ~VO_INTF_MIPI_SLAVE) && ++ (type & ~VO_INTF_LCD_6BIT) && (type & ~VO_INTF_LCD_8BIT) && ++ (type & ~VO_INTF_LCD_16BIT) && (type & ~VO_INTF_LCD_18BIT) && ++ (type & ~VO_INTF_LCD_24BIT) ++ ) { ++ printf("vo(%u), none of (BT.1120,BT.656,MIPI,LCD) can use at the same time!\n", dev); ++ return -1; ++ } ++ } else { ++ printf("unknown dev(%u)!\n", dev); ++ return -1; ++ } ++ ++ if (VO_INTF_HDMI & type) { ++ if (!(((sync >= VO_OUTPUT_1080P24) && (sync <= VO_OUTPUT_640x480_60)) || ++ ((sync >= VO_OUTPUT_1920x2160_30) && (sync <= VO_OUTPUT_4096x2160_60)))) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_BT1120 & type) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (sync > VO_OUTPUT_7680x4320_30)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if ((VO_INTF_MIPI & type) || (VO_INTF_MIPI_SLAVE & type)) { ++ if (dev == VO_DEV_DHD0) { ++ if ((sync != VO_OUTPUT_576P50) && ++ (sync != VO_OUTPUT_720P50) && ++ (sync != VO_OUTPUT_720P60) && ++ (sync != VO_OUTPUT_1024x768_60) && ++ (sync != VO_OUTPUT_1280x1024_60) && ++ (sync != VO_OUTPUT_720x1280_60) && ++ (sync != VO_OUTPUT_1080x1920_60) && ++ (sync != VO_OUTPUT_1080P60) && ++ (sync != VO_OUTPUT_3840x2160_30) && ++ (sync != VO_OUTPUT_3840x2160_60)) { ++ printf("for MIPI(mipi_tx) intface, vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((sync != VO_OUTPUT_576P50) && ++ (sync != VO_OUTPUT_720P50) && ++ (sync != VO_OUTPUT_720P60) && ++ (sync != VO_OUTPUT_1024x768_60) && ++ (sync != VO_OUTPUT_1280x1024_60) && ++ (sync != VO_OUTPUT_720x1280_60) && ++ (sync != VO_OUTPUT_1080x1920_60) && ++ (sync != VO_OUTPUT_1080P60)) { ++ printf("for MIPI(mipi_tx) intface, vo%u's intfsync %u illegal! \n", dev, sync); ++ return -1; ++ } ++ } ++ } ++ ++ if (type == (VO_INTF_HDMI | VO_INTF_MIPI)) { ++ if ((sync == VO_OUTPUT_576P50) || ++ (sync == VO_OUTPUT_1024x768_60)) { ++ printf("when HDMI+MIPI, not support 576P50 or 1024x768_60\n"); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int do_vobg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int dev, rgb; ++ ++ if (argc < 3) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ rgb = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE_ALL); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ set_vobg(dev, rgb); ++ ++ printf("dev %u set background color!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int dev, intftype, sync; ++ ++ if (argc < 4) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ intftype = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE10); ++ sync = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ if ((dev >= VO_DEV_BUTT) || (sync >= VO_OUTPUT_BUTT)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if (check_vo_support(dev, intftype, sync)) { ++ printf("unsupported parameter!\n"); ++ return -1; ++ } ++ ++ start_vo(dev, intftype, sync); ++ ++ g_a_interface_type[dev] = intftype; ++ ++ if (intftype & VO_INTF_HDMI) { ++ if (intftype == (VO_INTF_HDMI | VO_INTF_MIPI)) { ++ hdmi_display(sync, 0, 2); ++ } else { ++ hdmi_display(sync, 2, 2); ++ } ++ } ++ ++ if (intftype & VO_INTF_MIPI) { ++ /* to call mipi_display. */ ++ mipi_tx_display(sync); ++ } ++ ++ printf("dev %u opened!\n", dev); ++ ++ return 0; ++} ++ ++static int do_stopvo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int dev; ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if (g_a_interface_type[dev] & VO_INTF_HDMI) { ++ g_a_interface_type[dev] = 0; ++ hdmi_stop(); ++ } ++ ++ if (g_a_interface_type[dev] & VO_INTF_MIPI) { ++ mipi_tx_stop(); ++ } ++ ++ stop_vo(dev); ++ ++ printf("dev %u closed!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvl(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect layer_rect; ++ int max_x, max_y, layer_max_w, layer_max_h; ++ ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer == VO_LAYER_VHD0) { ++ max_x = VHD0_PIC_MAX_WIDTH; ++ max_y = VHD0_PIC_MAX_HEIGHT; ++ layer_max_w = VHD0_PIC_MAX_WIDTH; ++ layer_max_h = VHD0_PIC_MAX_HEIGHT; ++ } else if (layer == VO_LAYER_VHD1) { ++ max_x =VHD1_PIC_MAX_WIDTH; ++ max_y = VHD1_PIC_MAX_HEIGHT; ++ layer_max_w = VHD1_PIC_MAX_WIDTH; ++ layer_max_h = VHD1_PIC_MAX_HEIGHT; ++ } else { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if((strd > (layer_max_w * 2)) || ++ (x > max_x) || (x & 0x1) || ++ (y > max_y) || (y & 0x1) || ++ (w > layer_max_w) || (w & 0x1) || (w < PIC_MIN_LENTH) || ++ (h > layer_max_h) || (h & 0x1) || (h < PIC_MIN_LENTH)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ ++ layer_rect.x = x; ++ layer_rect.y = y; ++ layer_rect.w = w; ++ layer_rect.h = h; ++ ++ start_videolayer(layer, addr, strd, layer_rect); ++ ++ printf("video layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopvl(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer > VO_LAYER_VSD0) || (layer == VO_LAYER_VP)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_videolayer(layer); ++ ++ printf("video layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++static int do_startgx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect gx_rect; ++ int max_x, max_y, layer_max_w, layer_max_h; ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer == VO_GRAPHC_G0) { ++ max_x =GFX0_PIC_MAX_WIDTH; ++ max_y = GFX0_PIC_MAX_HEIGHT; ++ layer_max_w = GFX0_PIC_MAX_WIDTH; ++ layer_max_h = GFX0_PIC_MAX_HEIGHT; ++ } else if (layer == VO_GRAPHC_G1) { ++ max_x =GFX1_PIC_MAX_WIDTH; ++ max_y = GFX1_PIC_MAX_HEIGHT; ++ layer_max_w = GFX1_PIC_MAX_WIDTH; ++ layer_max_h = GFX1_PIC_MAX_HEIGHT; ++ } else { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if((strd > (layer_max_w * 2)) || ++ (x > max_x) || (x & 0x1) || ++ (y > max_y) || (y & 0x1) || ++ (w > layer_max_w) || (w & 0x1) || (w < PIC_MIN_LENTH) || ++ (h > layer_max_h) || (h & 0x1) || (h < PIC_MIN_LENTH)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ gx_rect.x = x; ++ gx_rect.y = y; ++ gx_rect.w = w; ++ gx_rect.h = h; ++ ++ start_gx(layer, addr, strd, gx_rect); ++ ++ printf("graphic layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopgx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf ("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer >= VO_GRAPHC_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_gx(layer); ++ ++ printf("graphic layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ startvo, CFG_MAXARGS, 1, do_startvo, ++ "startvo - open vo device with a certain output interface.\n" ++ "\t- startvo [dev intftype sync]", ++ "\nargs: [dev, intftype, sync]\n" ++ "\t- : 0: DHD0,1: DHD1\n" ++ "\t-: 8(BT.656),16(BT.1120),32(HDMI),16384(mipi_tx)\n" ++ "\t-: typical value:\n" ++ "\t\t0(PAL), 1(NTSC), 4(1080P30), 6(720P60)\n" ++ "\t\t10(1080P60), 21(1920x1200), 26(2560x1440_30),31(3840x2160_30)\n" ++ "\t\t33(3840x2160_60),45(1080x1920_60),47(user)\n"); ++ ++U_BOOT_CMD( ++ stopvo, CFG_MAXARGS, 1, do_stopvo, ++ "stopvo - close interface of vo device.\n" ++ "\t- stopvo [dev]", ++ "\nargs: [dev]\n" ++ "\t- : 0~1(HD0~HD1)\n"); ++ ++U_BOOT_CMD( ++ startvl, CFG_MAXARGS, 1, do_startvl, ++ "startvl - open video layer.\n" ++ "\t- startvl [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(V0), 1(V1)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD( ++ stopvl, CFG_MAXARGS, 1, do_stopvl, ++ "stopvl - close video layer.\n" ++ "\t- stopvl [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(V0), 1(V1)\n"); ++ ++U_BOOT_CMD( ++ setvobg, CFG_MAXARGS, 1, do_vobg, ++ "setvobg - set vo background color.\n" ++ "\t- setvobg [dev color]", ++ "\nargs: [dev, color]\n" ++ "\t- : 0~1(HD0~1)\n" ++ "\t-: rgb color space\n"); ++ ++U_BOOT_CMD( ++ startgx, CFG_MAXARGS, 1, do_startgx, ++ "startgx - open graphics layer.\n" ++ "\t- startgx [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(G0), 1(G1)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD( ++ stopgx, CFG_MAXARGS, 1, do_stopgx, ++ "stopgx - close graphics layer.\n" ++ "\t- stopgx [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(G0), 1(G1)\n"); +diff --git a/cmd/cmd_vo_ss919v100.c b/cmd/cmd_vo_ss919v100.c +new file mode 100644 +index 0000000..a8e5f83 +--- /dev/null ++++ b/cmd/cmd_vo_ss919v100.c +@@ -0,0 +1,559 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ss919v100_vo.h" ++ ++#define CMD_VO_ARGS_BASE10 10 ++#define CMD_VO_ARGS_BASE16 16 ++#define CMD_VO_ARGS_BASE_ALL 0 ++ ++extern int set_vobg(unsigned int dev, unsigned int rgb); ++extern int start_vo(unsigned int dev, unsigned int type, unsigned int sync); ++extern int stop_vo(unsigned int dev); ++extern int start_gx(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect gx_rect); ++extern int stop_gx(unsigned int layer); ++extern int start_videolayer(unsigned int layer, unsigned long addr, unsigned int strd, vo_rect layer_rect); ++extern int stop_videolayer(unsigned int layer); ++ ++#if CONFIG_HDMI_SUPPORT ++extern int hdmi_display(unsigned int vosync, unsigned int input, unsigned int output); ++extern void hdmi_stop(void); ++#endif ++ ++extern int mipi_tx_display(unsigned int vosync); ++extern int mipi_tx_stop(void); ++ ++#define VO_DEV_MAX_NUM 3 ++static unsigned int g_a_interface_type[VO_DEV_MAX_NUM] = {[0 ... (VO_DEV_MAX_NUM - 1)] = 0}; ++ ++static int vo_is_lcd_intf(unsigned int intf_type) ++{ ++ if (VO_INTF_LCD & intf_type || ++ VO_INTF_LCD_8BIT & intf_type || ++ VO_INTF_LCD_6BIT & intf_type || ++ VO_INTF_LCD_16BIT & intf_type || ++ VO_INTF_LCD_18BIT & intf_type || ++ VO_INTF_LCD_24BIT & intf_type) { ++ return 1; ++ } ++ return 0; ++} ++ ++static int vou_drv_check_lcd_sync(vo_dev dev, unsigned int intf_type, unsigned int intf_sync) ++{ ++ if (dev == VO_DEV_DHD0) { ++ if (intf_sync != VO_OUTPUT_USER) { ++ printf("DHD0 only support VO_OUTPUT_USER when intf is LCD!\n"); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_8BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_320x240_60) { ++ printf("for LCD 8bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_6BIT & intf_type) { ++ if ((intf_sync < VO_OUTPUT_320x240_50) || (intf_sync > VO_OUTPUT_240x320_50)) { ++ printf("for LCD 6bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_16BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_240x320_60) { ++ printf("for LCD 16bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_18BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_240x320_60) { ++ printf("for LCD 18bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_LCD_24BIT & intf_type) { ++ if (intf_sync != VO_OUTPUT_800x600_50) { ++ printf("for LCD 24bit intface,vo%u's intfsync %u illegal!\n", dev, intf_sync); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int check_vo_support(unsigned int dev, unsigned int type, unsigned int sync) ++{ ++ /* check interface type, ONLY VGA & HDMI interface is supported. */ ++ if (dev == VO_DEV_DHD0) { ++ if ((type & ~(VO_INTF_MIPI | VO_INTF_HDMI | VO_INTF_BT656 | ++ VO_INTF_BT1120 | VO_INTF_LCD_8BIT | VO_INTF_LCD_6BIT | ++ VO_INTF_LCD_16BIT | VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) || ++ (type == 0)) { ++ printf("hd%u only supports HDMI,BT.656,BT.1120,mipi_tx,lcd intftype, intf %u is illegal!\n", dev, type); ++ return -1; ++ } ++ /* just one interface at the the time for a dev. */ ++ if ((type & ~(VO_INTF_HDMI | VO_INTF_MIPI)) && ++ (type & ~VO_INTF_BT656) && ++ (type & ~VO_INTF_LCD_8BIT) && ++ (type & ~VO_INTF_LCD_6BIT) && ++ (type & ~VO_INTF_LCD_18BIT) && ++ (type & ~VO_INTF_LCD_16BIT) && ++ (type & ~VO_INTF_LCD_24BIT)) { ++ printf("for VO %u, only HDMI+MIPI can be used at the same time!\n", ++ dev); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((type & ~(VO_INTF_BT1120 | VO_INTF_BT656 | VO_INTF_MIPI | ++ VO_INTF_LCD_6BIT | VO_INTF_LCD_8BIT | VO_INTF_LCD_16BIT | ++ VO_INTF_LCD_18BIT | VO_INTF_LCD_24BIT)) || (type == 0)) { ++ printf("hd%u only supports BT.656,BT.1120,mipi_tx,LCD intftype, intf %u is illegal!\n", dev, type); ++ return -1; ++ } ++ /* just one interface at the the time for a dev. */ ++ if ((type & ~VO_INTF_BT1120) && (type & ~VO_INTF_BT656) && (type & ~VO_INTF_MIPI) && ++ (type & ~VO_INTF_LCD_6BIT) && (type & ~VO_INTF_LCD_8BIT) && ++ (type & ~VO_INTF_LCD_16BIT) && (type & ~VO_INTF_LCD_18BIT) && (type & ~VO_INTF_LCD_24BIT)) { ++ printf("vo(%u), none of (BT.1120,BT.656,MIPI,LCD) can use at the same time!\n", ++ dev); ++ return -1; ++ } ++ ++ } else { ++ printf("unknown dev(%u)!\n", dev); ++ return -1; ++ } ++ ++ if (sync == VO_OUTPUT_USER) { ++ return 0; ++ } ++ ++ /* check interface sync. */ ++ if (VO_INTF_HDMI & type) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (VO_OUTPUT_1080I50 == sync) || ++ (VO_OUTPUT_1080I60 == sync) || ++ (VO_OUTPUT_960H_PAL == sync) || ++ (VO_OUTPUT_960H_NTSC == sync) || ++ ((sync >= VO_OUTPUT_320x240_60) && (sync <= VO_OUTPUT_1080x1920_60)) || ++ (sync == VO_OUTPUT_7680x4320_30) || ++ (sync >= VO_OUTPUT_USER)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ ++ if (VO_INTF_BT1120 & type) { ++ if (dev == VO_DEV_DHD0) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (VO_OUTPUT_1080I60 == sync) || ++ (VO_OUTPUT_1080I50 == sync) || ++ (sync > VO_OUTPUT_640x480_60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((sync < VO_OUTPUT_1080P24) || ++ (sync > VO_OUTPUT_480P60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ } ++ ++ if (VO_INTF_MIPI & type) { ++ if (dev == VO_DEV_DHD0) { ++ if ((sync != VO_OUTPUT_576P50) && ++ (sync != VO_OUTPUT_720P50) && ++ (sync != VO_OUTPUT_720P60) && ++ (sync != VO_OUTPUT_1024x768_60) && ++ (sync != VO_OUTPUT_1280x1024_60) && ++ (sync != VO_OUTPUT_720x1280_60) && ++ (sync != VO_OUTPUT_1080x1920_60) && ++ (sync != VO_OUTPUT_1080P60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } else if (dev == VO_DEV_DHD1) { ++ if ((sync != VO_OUTPUT_576P50) && ++ (sync != VO_OUTPUT_720P50) && ++ (sync != VO_OUTPUT_720P60) && ++ (sync != VO_OUTPUT_1024x768_60) && ++ (sync != VO_OUTPUT_1280x1024_60) && ++ (sync != VO_OUTPUT_720x1280_60) && ++ (sync != VO_OUTPUT_1080x1920_60) && ++ (sync != VO_OUTPUT_1080P60)) { ++ printf("vo%u's intfsync %u illegal!\n", dev, sync); ++ return -1; ++ } ++ } ++ } ++ ++ if (vo_is_lcd_intf(type)) { ++ if (vou_drv_check_lcd_sync(dev, type, sync) != 0) { ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int do_vobg(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev, rgb; ++ ++ if (argc < 3) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ rgb = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE_ALL); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ set_vobg(dev, rgb); ++ ++ printf("dev %u set background color!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev, intftype, sync; ++ ++ if (argc < 4) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ intftype = (unsigned int)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE10); ++ sync = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ if ((dev >= VO_DEV_BUTT) || (sync >= VO_OUTPUT_BUTT)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if (check_vo_support(dev, intftype, sync)) { ++ printf("unsupported parameter!\n"); ++ return -1; ++ } ++ ++ start_vo(dev, intftype, sync); ++ ++ g_a_interface_type[dev] = intftype; ++ ++#if CONFIG_HDMI_SUPPORT ++ if (intftype & VO_INTF_HDMI) { ++ if (intftype == (VO_INTF_HDMI | VO_INTF_MIPI)) { ++ hdmi_display(sync, 0, 2); ++ } else { ++ hdmi_display(sync, 2, 2); ++ } ++ } ++#endif ++ ++ if (intftype & VO_INTF_MIPI) { ++ // to call mipi_display. ++ mipi_tx_display(sync); ++ } ++ ++ if (vo_is_lcd_intf(intftype)) { ++ g_a_interface_type[dev] = intftype; ++ } ++ ++ printf("dev %u opened!\n", dev); ++ ++ return 0; ++} ++ ++static int do_stopvo(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int dev; ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ dev = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ if (dev >= VO_DEV_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++#if CONFIG_HDMI_SUPPORT ++ if (g_a_interface_type[dev] & VO_INTF_HDMI) { ++ g_a_interface_type[dev] = 0; ++ hdmi_stop(); ++ } ++#endif ++ ++ if (g_a_interface_type[dev] & VO_INTF_MIPI) { ++ g_a_interface_type[dev] = 0; ++ mipi_tx_stop(); ++ } ++ ++ if (vo_is_lcd_intf(g_a_interface_type[dev])) { ++ g_a_interface_type[dev] = 0; ++ } ++ ++ stop_vo(dev); ++ ++ printf("dev %u closed!\n", dev); ++ ++ return 0; ++} ++ ++static int do_startgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect gx_rect; ++ int max_x, max_y, layer_max_w, layer_max_h; ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer == VO_GRAPHC_G0) { ++ max_x = PIC_MAX_WIDTH; ++ max_y = PIC_MAX_HEIGHT; ++ layer_max_w = GFX0_PIC_MAX_WIDTH; ++ layer_max_h = GFX0_PIC_MAX_HEIGHT; ++ } else if (layer == VO_GRAPHC_G1) { ++ max_x =GFX1_PIC_MAX_WIDTH; ++ max_y = GFX1_PIC_MAX_HEIGHT; ++ layer_max_w = GFX1_PIC_MAX_WIDTH; ++ layer_max_h = GFX1_PIC_MAX_HEIGHT; ++ } else { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if((strd > (layer_max_w * 2)) || ++ (x > max_x) || (x & 0x1) || ++ (y > max_y) || (y & 0x1) || ++ (w > layer_max_w) || (w & 0x1) || (w < PIC_MIN_LENTH) || ++ (h > layer_max_h) || (h & 0x1) || (h < PIC_MIN_LENTH)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ gx_rect.x = x; ++ gx_rect.y = y; ++ gx_rect.w = w; ++ gx_rect.h = h; ++ ++ start_gx(layer, addr, strd, gx_rect); ++ ++ printf("graphic layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopgx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer >= VO_GRAPHC_BUTT) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_gx(layer); ++ ++ printf("graphic layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++static int do_startvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer, strd, x, y, w, h; ++ unsigned long addr; ++ vo_rect layer_rect; ++ int max_x, max_y, layer_max_w, layer_max_h; ++ ++ if (argc < 8) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ addr = (unsigned long)simple_strtoul(argv[2], NULL, CMD_VO_ARGS_BASE16); ++ strd = (unsigned int)simple_strtoul(argv[3], NULL, CMD_VO_ARGS_BASE10); ++ x = (unsigned int)simple_strtoul(argv[4], NULL, CMD_VO_ARGS_BASE10); ++ y = (unsigned int)simple_strtoul(argv[5], NULL, CMD_VO_ARGS_BASE10); ++ w = (unsigned int)simple_strtoul(argv[6], NULL, CMD_VO_ARGS_BASE10); ++ h = (unsigned int)simple_strtoul(argv[7], NULL, CMD_VO_ARGS_BASE10); ++ ++ if (layer == VO_LAYER_VHD0) { ++ max_x = VHD0_PIC_MAX_WIDTH; ++ max_y = VHD0_PIC_MAX_HEIGHT; ++ layer_max_w = VHD0_PIC_MAX_WIDTH; ++ layer_max_h = VHD0_PIC_MAX_HEIGHT; ++ } else if (layer == VO_LAYER_VHD1) { ++ max_x =VHD1_PIC_MAX_WIDTH; ++ max_y = VHD1_PIC_MAX_HEIGHT; ++ layer_max_w = VHD1_PIC_MAX_WIDTH; ++ layer_max_h = VHD1_PIC_MAX_HEIGHT; ++ } else { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ if((strd > (layer_max_w * 2)) || ++ (x > max_x) || (x & 0x1) || ++ (y > max_y) || (y & 0x1) || ++ (w > layer_max_w) || (w & 0x1) || (w < PIC_MIN_LENTH) || ++ (h > layer_max_h) || (h & 0x1) || (h < PIC_MIN_LENTH)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ layer_rect.x = x; ++ layer_rect.y = y; ++ layer_rect.w = w; ++ layer_rect.h = h; ++ ++ start_videolayer(layer, addr, strd, layer_rect); ++ ++ printf("video layer %u opened!\n", layer); ++ ++ return 0; ++} ++ ++static int do_stopvl(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ unsigned int layer; ++ ++ if (argc < 2) { ++ printf("insufficient parameter!\n"); ++ printf("usage:\n%s\n", cmdtp->usage); ++ return -1; ++ } ++ ++ layer = (unsigned int)simple_strtoul(argv[1], NULL, CMD_VO_ARGS_BASE10); ++ ++ if ((layer > VO_LAYER_VSD0) || (layer == VO_LAYER_VP)) { ++ printf("invalid parameter!\n"); ++ return -1; ++ } ++ ++ stop_videolayer(layer); ++ ++ printf("video layer %u closed!\n", layer); ++ ++ return 0; ++} ++ ++U_BOOT_CMD(startvo, CFG_MAXARGS, 1, do_startvo, ++ "startvo - open vo device with a certain output interface.\n" ++ "\t- startvo [dev intftype sync]", ++ "\nargs: [dev, intftype, sync]\n" ++ "\t- : 0: DHD0,1: DHD1\n" ++ "\t-: 8(BT.656),16(BT.1120),32(HDMI),64(LCD),1024(LCD_8BIT),16384(mipi_tx)\n" ++ "\t-:\n" ++ "\t\t0(PAL), 1(NTSC), 2(1080P24), 3(1080P25)\n" ++ "\t\t4(1080P30), 5(720P50), 6(720P60), 7(1080I50)\n" ++ "\t\t8(1080I60), 9(1080P50), 10(1080P60), 11(576P50)\n" ++ "\t\t12(480P60), 13(800x600), 14(1024x768), 15(1280x1024)\n" ++ "\t\t16(1366x768), 17(1440x900), 18(1280x800), 19(1600x1200)\n" ++ "\t\t20(1680x1050), 21(1920x1200), 22(640x480), 23(960H_PAL)\n" ++ "\t\t24(960H_NTSC), 25(1920x2160), 26(2560x1440_30),27(2560x1440_60)\n" ++ "\t\t28(2560x1600_60),29(3840x2160_24),30(3840x2160_25),31(3840x2160_30)\n" ++ "\t\t32(3840x2160_50),33(3840x2160_60),34(4096x2160_24),35(4096x2160_25)\n" ++ "\t\t36(4096x2160_30),37(4096x2160_50),38(4096x2160_60),39(320x240_60)\n" ++ "\t\t40(320x240_50), 41(240x320_50), 42(240x320_60), 43(800x600_50)\n" ++ "\t\t44(720x1280_60), 45(1080x1920_60),46(7680x4320_30),47(user)\n"); ++ ++U_BOOT_CMD(stopvo, CFG_MAXARGS, 1, do_stopvo, ++ "stopvo - close interface of vo device.\n" ++ "\t- stopvo [dev]", ++ "\nargs: [dev]\n" ++ "\t- : 0~1(HD0~1)\n"); ++ ++U_BOOT_CMD(startgx, CFG_MAXARGS, 1, do_startgx, ++ "startgx - open graphics layer.\n" ++ "\t- startgx [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(G0), 1(G1)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD(stopgx, CFG_MAXARGS, 1, do_stopgx, ++ "stopgx - close graphics layer.\n" ++ "\t- stopgx [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(G0), 1(G1)\n"); ++ ++U_BOOT_CMD(startvl, CFG_MAXARGS, 1, do_startvl, ++ "startvl - open video layer.\n" ++ "\t- startvl [layer addr stride x y w h]\n", ++ "\nargs: [layer, addr, stride, x, y, w, h]\n" ++ "\t- : 0(V0), 1(V1)\n" ++ "\t- : picture address\n" ++ "\t- : picture stride\n" ++ "\t- : display area\n"); ++ ++U_BOOT_CMD(stopvl, CFG_MAXARGS, 1, do_stopvl, ++ "stopvl - close video layer.\n" ++ "\t- stopvl [layer]", ++ "\nargs: [layer]\n" ++ "\t- : 0(V0), 1(V1)\n"); ++ ++U_BOOT_CMD(setvobg, CFG_MAXARGS, 1, do_vobg, ++ "setvobg - set vo background color.\n" ++ "\t- setvobg [dev color]", ++ "\nargs: [dev, color]\n" ++ "\t- : 0~1(HD0~1)\n" ++ "\t-: rgb color space\n"); +diff --git a/cmd/gpt.c b/cmd/gpt.c +index 0c4349f..c4eb1ff 100644 +--- a/cmd/gpt.c ++++ b/cmd/gpt.c +@@ -633,20 +633,6 @@ static int do_disk_guid(struct blk_desc *dev_desc, char * const namestr) + } + + #ifdef CONFIG_CMD_GPT_RENAME +-/* +- * There are 3 malloc() calls in set_gpt_info() and there is no info about which +- * failed. +- */ +-static void set_gpt_cleanup(char **str_disk_guid, +- disk_partition_t **partitions) +-{ +-#ifdef CONFIG_RANDOM_UUID +- if (str_disk_guid) +- free(str_disk_guid); +-#endif +- if (partitions) +- free(partitions); +-} + + static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + char *name1, char *name2) +@@ -655,7 +641,7 @@ static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + struct disk_part *curr; + disk_partition_t *new_partitions = NULL; + char disk_guid[UUID_STR_LEN + 1]; +- char *partitions_list, *str_disk_guid; ++ char *partitions_list, *str_disk_guid = NULL; + u8 part_count = 0; + int partlistlen, ret, numparts = 0, partnum, i = 1, ctr1 = 0, ctr2 = 0; + +@@ -697,14 +683,8 @@ static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + /* set_gpt_info allocates new_partitions and str_disk_guid */ + ret = set_gpt_info(dev_desc, partitions_list, &str_disk_guid, + &new_partitions, &part_count); +- if (ret < 0) { +- del_gpt_info(); +- free(partitions_list); +- if (ret == -ENOMEM) +- set_gpt_cleanup(&str_disk_guid, &new_partitions); +- else +- goto out; +- } ++ if (ret < 0) ++ goto out; + + if (!strcmp(subcomm, "swap")) { + if ((strlen(name1) > PART_NAME_LEN) || (strlen(name2) > PART_NAME_LEN)) { +@@ -766,14 +746,8 @@ static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + * Even though valid pointers are here passed into set_gpt_info(), + * it mallocs again, and there's no way to tell which failed. + */ +- if (ret < 0) { +- del_gpt_info(); +- free(partitions_list); +- if (ret == -ENOMEM) +- set_gpt_cleanup(&str_disk_guid, &new_partitions); +- else +- goto out; +- } ++ if (ret < 0) ++ goto out; + + debug("Writing new partition table\n"); + ret = gpt_restore(dev_desc, disk_guid, new_partitions, numparts); +@@ -795,11 +769,14 @@ static int do_rename_gpt_parts(struct blk_desc *dev_desc, char *subcomm, + } + printf("new partition table with %d partitions is:\n", numparts); + print_gpt_info(); +- del_gpt_info(); + out: +- free(new_partitions); +- free(str_disk_guid); +- free(partitions_list); ++ del_gpt_info(); ++#ifdef CONFIG_RANDOM_UUID ++ if (str_disk_guid) ++ free(str_disk_guid); ++#endif ++ if (new_partitions) ++ free(new_partitions); + return ret; + } + #endif +diff --git a/cmd/i2c.c b/cmd/i2c.c +index 43a7629..6036ca9 100644 +--- a/cmd/i2c.c ++++ b/cmd/i2c.c +@@ -207,7 +207,7 @@ void i2c_init_board(void) + * + * Returns I2C bus speed in Hz. + */ +-#if !defined(CONFIG_SYS_I2C) && !defined(CONFIG_DM_I2C) ++#if !defined(CONFIG_SYS_I2C) && !defined(CONFIG_DM_I2C) && !defined(CONFIG_I2C_BSP) + /* + * TODO: Implement architecture-specific get/set functions + * Should go away, if we switched completely to new multibus support +@@ -246,10 +246,10 @@ int i2c_set_bus_speed(unsigned int speed) + * + * Returns the address length. + */ +-static uint get_alen(char *arg, int default_len) ++static uint get_alen(char *arg, uint default_len) + { +- int j; +- int alen; ++ uint j; ++ uint alen; + + alen = default_len; + for (j = 0; j < 8; j++) { +@@ -292,7 +292,7 @@ static int do_i2c_read ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv + { + uint chip; + uint devaddr, length; +- int alen; ++ uint alen; + u_char *memaddr; + int ret; + #ifdef CONFIG_DM_I2C +@@ -345,7 +345,7 @@ static int do_i2c_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[ + { + uint chip; + uint devaddr, length; +- int alen; ++ uint alen; + u_char *memaddr; + int ret; + #ifdef CONFIG_DM_I2C +@@ -511,8 +511,8 @@ static int do_i2c_md ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] + { + uint chip; + uint addr, length; +- int alen; +- int j, nbytes, linebytes; ++ uint alen; ++ uint j, nbytes, linebytes; + int ret; + #ifdef CONFIG_DM_I2C + struct udevice *dev; +@@ -630,9 +630,9 @@ static int do_i2c_mw ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] + { + uint chip; + ulong addr; +- int alen; ++ uint alen; + uchar byte; +- int count; ++ uint count; + int ret; + #ifdef CONFIG_DM_I2C + struct udevice *dev; +@@ -716,8 +716,8 @@ static int do_i2c_crc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] + { + uint chip; + ulong addr; +- int alen; +- int count; ++ uint alen; ++ uint count; + uchar byte; + ulong crc; + ulong err; +@@ -1023,7 +1023,7 @@ static int do_i2c_probe (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv + static int do_i2c_loop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + { + uint chip; +- int alen; ++ uint alen; + uint addr; + uint length; + u_char bytes[16]; +@@ -1801,7 +1801,7 @@ static int do_i2c_show_bus(cmd_tbl_t *cmdtp, int flag, int argc, + * on error. + */ + #if defined(CONFIG_SYS_I2C) || defined(CONFIG_I2C_MULTI_BUS) || \ +- defined(CONFIG_DM_I2C) ++ defined(CONFIG_DM_I2C) || defined(CONFIG_I2C_BSP) + static int do_i2c_bus_num(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) + { +@@ -1927,6 +1927,9 @@ static int do_i2c_nm(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) + */ + static int do_i2c_reset(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) + { ++#ifdef CONFIG_I2C_BSP ++ printf("Error: Not supported!!\n"); ++#else + #if defined(CONFIG_DM_I2C) + struct udevice *bus; + +@@ -1941,6 +1944,7 @@ static int do_i2c_reset(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv + #else + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + #endif ++#endif /* CONFIG_I2C_BSP */ + return 0; + } + +@@ -1950,7 +1954,7 @@ static cmd_tbl_t cmd_i2c_sub[] = { + #endif + U_BOOT_CMD_MKENT(crc32, 3, 1, do_i2c_crc, "", ""), + #if defined(CONFIG_SYS_I2C) || \ +- defined(CONFIG_I2C_MULTI_BUS) || defined(CONFIG_DM_I2C) ++ defined(CONFIG_I2C_MULTI_BUS) || defined(CONFIG_DM_I2C) || defined(CONFIG_I2C_BSP) + U_BOOT_CMD_MKENT(dev, 1, 1, do_i2c_bus_num, "", ""), + #endif /* CONFIG_I2C_MULTI_BUS */ + #if defined(CONFIG_I2C_EDID) +@@ -2027,7 +2031,7 @@ static char i2c_help_text[] = + #endif + "crc32 chip address[.0, .1, .2] count - compute CRC32 checksum\n" + #if defined(CONFIG_SYS_I2C) || \ +- defined(CONFIG_I2C_MULTI_BUS) || defined(CONFIG_DM_I2C) ++ defined(CONFIG_I2C_MULTI_BUS) || defined(CONFIG_DM_I2C) || defined(CONFIG_I2C_BSP) + "i2c dev [dev] - show or set current I2C bus\n" + #endif /* CONFIG_I2C_MULTI_BUS */ + #if defined(CONFIG_I2C_EDID) +diff --git a/cmd/mmc.c b/cmd/mmc.c +index 6f3cb85..eaeb9fb 100644 +--- a/cmd/mmc.c ++++ b/cmd/mmc.c +@@ -7,10 +7,17 @@ + #include + #include + #include ++#include + #include + #include + #include ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++#include "cmd_timestamp.h" ++#endif + ++#ifdef CONFIG_EXT4_SPARSE ++extern int ext4_unsparse(struct mmc *mmc, u32 dev, u8 *pbuf, u32 blk, u32 cnt); ++#endif + static int curr_device = -1; + + static void print_mmcinfo(struct mmc *mmc) +@@ -40,8 +47,7 @@ static void print_mmcinfo(struct mmc *mmc) + printf("\n"); + + printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); +- puts("Capacity: "); +- print_size(mmc->capacity, "\n"); ++ print_to_tool("Capacity: %lld\r\n", mmc->capacity); + + printf("Bus Width: %d-bit%s\n", mmc->bus_width, + mmc->ddr_mode ? " DDR" : ""); +@@ -92,6 +98,43 @@ static void print_mmcinfo(struct mmc *mmc) + } + } + } ++ ++static int print_mmcreg(struct mmc *mmc) ++{ ++ int i, err; ++ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); ++ ++ printf("OCR register: %08x\n", mmc->ocr); ++ printf("CID register: %08x %08x %08x %08x\n", ++ mmc->cid[0], mmc->cid[1], mmc->cid[2], mmc->cid[3]); ++ printf("CSD register: %08x %08x %08x %08x\n", ++ mmc->csd[0], mmc->csd[1], mmc->csd[2], mmc->csd[3]); ++ printf("RCA register: %08x\n", mmc->rca); ++ if (!IS_SD(mmc)) { ++ err = mmc_send_ext_csd(mmc, ext_csd); ++ if (err) { ++ printf("Get ext_csd fail!\n"); ++ return -1; ++ } ++ ++ printf("Extended CSD register:\n"); ++ for (i = 0; i < 512; i += 8) ++ printf("%03d: %02x %02x %02x %02x" ++ " %02x %02x %02x %02x\n", ++ i, ++ ext_csd[i], ++ ext_csd[i+1], ++ ext_csd[i+2], ++ ext_csd[i+3], ++ ext_csd[i+4], ++ ext_csd[i+5], ++ ext_csd[i+6], ++ ext_csd[i+7]); ++ } ++ printf("\n"); ++ return 0; ++} ++ + static struct mmc *init_mmc_device(int dev, bool force_init) + { + struct mmc *mmc; +@@ -137,6 +180,24 @@ static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + return CMD_RET_SUCCESS; + } + ++static int do_mmcreg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ struct mmc *mmc; ++ int dev; ++ ++ if (argc != 2) ++ return CMD_RET_USAGE; ++ ++ dev = simple_strtoul(argv[1], NULL, 16); ++ ++ mmc = init_mmc_device(dev, false); ++ if (!mmc) ++ return CMD_RET_FAILURE; ++ ++ print_mmcreg(mmc); ++ return CMD_RET_SUCCESS; ++} ++ + #if CONFIG_IS_ENABLED(CMD_MMC_RPMB) + static int confirm_key_prog(void) + { +@@ -296,24 +357,49 @@ static int do_mmc_read(cmd_tbl_t *cmdtp, int flag, + struct mmc *mmc; + u32 blk, cnt, n; + void *addr; ++ int dev; ++ unsigned long long start_ticks, end_ticks; ++ unsigned long long size, speed_byte, speed_tmp; + +- if (argc != 4) ++ if (argc != 5) + return CMD_RET_USAGE; + +- addr = (void *)simple_strtoul(argv[1], NULL, 16); +- blk = simple_strtoul(argv[2], NULL, 16); +- cnt = simple_strtoul(argv[3], NULL, 16); ++ dev = simple_strtoul(argv[1], NULL, 16); ++ addr = (void *)simple_strtoul(argv[2], NULL, 16); ++ blk = simple_strtoul(argv[3], NULL, 16); ++ cnt = simple_strtoul(argv[4], NULL, 16); + +- mmc = init_mmc_device(curr_device, false); ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ timestamp_mark("mmc_read_init_start", __LINE__); ++#endif ++ ++ mmc = init_mmc_device(dev, false); + if (!mmc) + return CMD_RET_FAILURE; + ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ timestamp_mark("mmc_read_real_start", __LINE__); ++#endif ++ ++ curr_device = dev; + printf("\nMMC read: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + ++ start_ticks = get_ticks(); + n = blk_dread(mmc_get_blk_desc(mmc), blk, cnt, addr); ++ end_ticks = get_ticks(); ++ + printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ timestamp_mark("mmc_read_real_end", __LINE__); ++#endif ++ ++ size = mmc->read_bl_len * cnt; ++ speed_byte = (size * CONFIG_SYS_TIMER_RATE) / (end_ticks - start_ticks); ++ speed_tmp = (speed_byte & 0xFFFFF) * 100; ++ printf("%llu.%02llu MB/s\n", speed_byte >> 20, speed_tmp >> 20); ++ + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; + } + +@@ -389,28 +475,51 @@ static int do_mmc_write(cmd_tbl_t *cmdtp, int flag, + struct mmc *mmc; + u32 blk, cnt, n; + void *addr; ++ int dev; ++ unsigned long long start_ticks, end_ticks; ++ unsigned long long size, speed_byte, speed_tmp; + +- if (argc != 4) ++ if (argc != 5) + return CMD_RET_USAGE; + +- addr = (void *)simple_strtoul(argv[1], NULL, 16); +- blk = simple_strtoul(argv[2], NULL, 16); +- cnt = simple_strtoul(argv[3], NULL, 16); ++ dev = simple_strtoul(argv[1], NULL, 16); ++ addr = (void *)simple_strtoul(argv[2], NULL, 16); ++ blk = simple_strtoul(argv[3], NULL, 16); ++ cnt = simple_strtoul(argv[4], NULL, 16); + +- mmc = init_mmc_device(curr_device, false); ++ mmc = init_mmc_device(dev, false); + if (!mmc) + return CMD_RET_FAILURE; + +- printf("\nMMC write: dev # %d, block # %d, count %d ... ", +- curr_device, blk, cnt); ++ curr_device = dev; + + if (mmc_getwp(mmc) == 1) { + printf("Error: card is write protected!\n"); + return CMD_RET_FAILURE; + } ++ ++#ifdef CONFIG_EXT4_SPARSE ++ if (!strcmp(argv[0], "write.ext4sp")) { ++ printf("\nMMC write ext4 sparse: dev # %d, block # %d, count %d ... ", ++ curr_device, blk, cnt); ++ return ext4_unsparse(mmc, dev, addr, blk, cnt); ++ } ++#endif ++ ++ printf("\nMMC write: dev # %d, block # %d, count %d ... ", ++ curr_device, blk, cnt); ++ ++ start_ticks = get_ticks(); + n = blk_dwrite(mmc_get_blk_desc(mmc), blk, cnt, addr); ++ end_ticks = get_ticks(); ++ + printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + ++ size = mmc->write_bl_len * cnt; ++ speed_byte = (size * CONFIG_SYS_TIMER_RATE) / (end_ticks - start_ticks); ++ speed_tmp = (speed_byte & 0xFFFFF) * 100; ++ printf("%llu.%02llu MB/s\n", speed_byte >> 20, speed_tmp >> 20); ++ + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; + } + static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag, +@@ -418,6 +527,8 @@ static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag, + { + struct mmc *mmc; + u32 blk, cnt, n; ++ unsigned long long start_ticks, end_ticks; ++ unsigned long long size, speed_byte, speed_tmp; + + if (argc != 3) + return CMD_RET_USAGE; +@@ -436,9 +547,18 @@ static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag, + printf("Error: card is write protected!\n"); + return CMD_RET_FAILURE; + } ++ ++ start_ticks = get_ticks(); + n = blk_derase(mmc_get_blk_desc(mmc), blk, cnt); ++ end_ticks = get_ticks(); ++ + printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + ++ size = mmc->read_bl_len * cnt; ++ speed_byte = (size * CONFIG_SYS_TIMER_RATE) / (end_ticks - start_ticks); ++ speed_tmp = (speed_byte & 0xFFFFF) * 100; ++ printf("%llu.%02llu MB/s\n", speed_byte >> 20, speed_tmp >> 20); ++ + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; + } + #endif +@@ -874,9 +994,10 @@ static int do_mmc_bkops_enable(cmd_tbl_t *cmdtp, int flag, + + static cmd_tbl_t cmd_mmc[] = { + U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""), +- U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""), ++ U_BOOT_CMD_MKENT(reg, 2, 0, do_mmcreg, "", ""), ++ U_BOOT_CMD_MKENT(read, 5, 1, do_mmc_read, "", ""), + #if CONFIG_IS_ENABLED(MMC_WRITE) +- U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""), ++ U_BOOT_CMD_MKENT(write, 5, 0, do_mmc_write, "", ""), + U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""), + #endif + #if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) +@@ -934,8 +1055,9 @@ U_BOOT_CMD( + mmc, 29, 1, do_mmcops, + "MMC sub system", + "info - display info of the current MMC device\n" +- "mmc read addr blk# cnt\n" +- "mmc write addr blk# cnt\n" ++ "mmc reg [dev] - display register of the current MMC device\n" ++ "mmc read dev addr blk# cnt\n" ++ "mmc write dev addr blk# cnt\n" + #if CONFIG_IS_ENABLED(CMD_MMC_SWRITE) + "mmc swrite addr blk#\n" + #endif +diff --git a/cmd/nand.c b/cmd/nand.c +index 5bda69e..1bb5628 100644 +--- a/cmd/nand.c ++++ b/cmd/nand.c +@@ -31,6 +31,11 @@ + #include + + #include "legacy-mtd-utils.h" ++#include ++ ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++#include "cmd_timestamp.h" ++#endif + + #if defined(CONFIG_CMD_MTDPARTS) + +@@ -40,6 +45,157 @@ int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); + #endif + ++/* ++ * fmc : ++ * v100: 2k4b 2k8b 2k24b 4k4b 4k8b 4k24b ++ */ ++ ++#define NFC_VER_VER (0xFFF00000) ++ ++#define SET_NFC_VER(_ver, _pagesize, _ecc) \ ++ ((((_ver) << 20) & NFC_VER_VER) | \ ++ (((_pagesize) & 0xFFFF) << 4) | ((_ecc) & 0xF)) ++ ++static unsigned int fmc100_support_yaffs2[] = { ++ SET_NFC_VER(FMC_VER_100, _2K, ET_ECC_4BIT), ++ SET_NFC_VER(FMC_VER_100, _2K, ET_ECC_8BIT), ++ SET_NFC_VER(FMC_VER_100, _2K, ET_ECC_24BIT1K), ++ ++ SET_NFC_VER(FMC_VER_100, _4K, ET_ECC_4BIT), ++ SET_NFC_VER(FMC_VER_100, _4K, ET_ECC_8BIT), ++ SET_NFC_VER(FMC_VER_100, _4K, ET_ECC_24BIT1K), ++ ++ SET_NFC_VER(FMC_VER_100, _8K, ET_ECC_24BIT1K), ++ SET_NFC_VER(FMC_VER_100, _8K, ET_ECC_40BIT1K), ++ SET_NFC_VER(FMC_VER_100, _8K, ET_ECC_64BIT1K), ++ ++ SET_NFC_VER(FMC_VER_100, _16K, ET_ECC_40BIT1K), ++ SET_NFC_VER(FMC_VER_100, _16K, ET_ECC_64BIT1K), ++ ++ 0, ++}; ++ ++static unsigned int *get_support_yaffs2(unsigned int nandip) ++{ ++ switch (nandip) { ++ default: ++ case FMC_VER_100: ++ return fmc100_support_yaffs2; ++ } ++} ++ ++static unsigned int get_yaffs2_version(unsigned int nandip, int pagesize, ++ int ecc) ++{ ++ int ix; ++ unsigned int *ver = get_support_yaffs2(nandip); ++ unsigned int tmp = SET_NFC_VER(0, (unsigned int)pagesize, (unsigned int)ecc); ++ ++ for (ix = 0; ver[ix]; ix++) { ++ if ((ver[ix] & ~NFC_VER_VER) == tmp) ++ return ver[ix]; ++ } ++ ++ return 0; ++} ++ ++static int yaffs_tag_check(unsigned char *buffer, unsigned int writesize, ++ unsigned int length) ++{ ++ unsigned int ip_version = readl(CONFIG_FMC_REG_BASE + FMC_VERSION); ++ unsigned int nfc_yaff_ver; ++ unsigned int yaffs_yaff_ver; ++ ++ static char *ecctype_str[] = { "0bit", "1bit", "4bit", "8bit", "24bits/1K", ++ "28bits/1K", "40bits/1K", "64bits/1K", "unknown"}; ++ ++ /* this follow must be consistent with mkyaffs2image !!! */ ++ struct yaffs2_tag { ++#define YAFFS2_SIGN_MAGIC "YFSS!V10" ++ unsigned char magic[8]; ++ unsigned int nandip; ++ unsigned char yaffs2ver[4]; ++ unsigned int pagesize; ++ unsigned int ecctype; ++ }; ++ ++ struct yaffs2_tag *tags = (struct yaffs2_tag *)buffer; ++ unsigned int ecctype; ++ int ret; ++ ++ ret = nand_get_ecctype(); ++ if (ret < 0) { ++ printf("Cannot get corret ecctype. \n"); ++ return -1; ++ } ++ ecctype = (unsigned int)ret; ++ ++ if (length < 512) { ++ printf("buffer length is too short.\n"); ++ return -1; ++ } ++ ++ if (memcmp(tags->magic, (unsigned char *)YAFFS2_SIGN_MAGIC, 8)) { ++ printf("!!! The yaffs2 filesystem image has no tag " \ ++ "information.\n please update your mkyaffs2image" \ ++ " tool, and remake yaffs2 filesystem image.\n"); ++ return -1; ++ } ++ ++ if (writesize != tags->pagesize) { ++ printf("!!! yaffs2 filesystem image pagesize(%d) is NOT" \ ++ " consistent with hardware pagesize(%d).\n", ++ tags->pagesize, writesize); ++ goto fail; ++ } ++ ++ if (ecctype != tags->ecctype) { ++ printf("!!! yaffs2 filesystem image ecctype(%s) is NOT" \ ++ " consistent with hardware ecctype(%s).\n", ++ ecctype_str[tags->ecctype & 0xF], ++ ecctype_str[ecctype & 0xF]); ++ goto fail; ++ } ++ ++ yaffs_yaff_ver = get_yaffs2_version(tags->nandip, writesize, ecctype); ++ if (!yaffs_yaff_ver) { ++ printf("!!! The yaffs2 filesystem image" \ ++ " has invalid tag information.\n"); ++ goto fail1; ++ } ++ ++ /* ++ * When write 8k40bit yaffs2 filesystem to nfc301, ++ * It will print this error, because cpu not support. ++ */ ++ nfc_yaff_ver = get_yaffs2_version(ip_version, writesize, ++ ecctype); ++ if (!nfc_yaff_ver) { ++ printf("!!! The yaffs2 filesystem " \ ++ "or mkyaffs2image for cpu ver(0x%X) " \ ++ "But your demo board cpu ver(0x%X).\n", ++ tags->nandip, ip_version); ++ goto fail1; ++ } ++ ++ return 0; ++ ++fail: ++ printf("Please remake yaffs2 filesystem image, " \ ++ "make sure your yaffs2 filesystem image pagesize and ecctype" \ ++ " is consistent with hardware config.\n"); ++ printf("Current hardware config, pagesize:%d, ecctype:%s\n", ++ writesize, ecctype_str[ecctype & 0xF]); ++ ++ return -1; ++fail1: ++ printf("1. Confirm your yaffs2 filesystem image version.\n" \ ++ "2. Update your mkyaffs2image tool," \ ++ " remake yaffs2 filesystem image.\n"); ++ ++ return -1; ++} ++ + static int nand_dump(struct mtd_info *mtd, ulong off, int only_oob, + int repeat) + { +@@ -373,6 +529,16 @@ static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) + } + } + ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++unsigned long ticks2usec(unsigned long ticks) ++{ ++ ulong tbclk = get_tbclk(); ++ tbclk/=1000000; ++ ticks /= tbclk; ++ return ((ulong)ticks); ++} ++#endif ++ + static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + { + int i, ret = 0; +@@ -380,6 +546,10 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + loff_t off, size, maxsize; + char *cmd, *s; + struct mtd_info *mtd; ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ unsigned long long time_s; ++ unsigned long long time_e; ++#endif + #ifdef CONFIG_SYS_NAND_QUIET + int quiet = CONFIG_SYS_NAND_QUIET; + #else +@@ -398,6 +568,12 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + + cmd = argv[1]; + ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ if (strcmp(cmd, "read") == 0) { ++ timestamp_mark("nand_read_start", __LINE__); ++ } ++#endif ++ + /* Only "dump" is repeatable. */ + if (repeat && strcmp(cmd, "dump")) + return 0; +@@ -515,7 +691,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + opts.jffs2 = clean; + opts.quiet = quiet; + opts.spread = spread; +- ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ loff_t erase_size = opts.length; ++#endif + if (scrub) { + if (scrub_yes) { + opts.scrub = 1; +@@ -529,8 +707,18 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + } + } + } ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_s=get_ticks(); ++#endif + ret = nand_erase_opts(mtd, &opts); ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_e=get_ticks(); ++#endif + printf("%s\n", ret ? "ERROR" : "OK"); ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ printf("\nnand erase time ::time= %lu usec;speed: %lu KB/S\n",ticks2usec(time_e - time_s), ++ (unsigned long)(erase_size >> 10)*1000000/ticks2usec(time_e - time_s)); ++#endif + + return ret == 0 ? 0 : 1; + } +@@ -606,6 +794,10 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + } + + mtd = get_nand_dev_by_index(dev); ++ ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_s=get_ticks(); ++#endif + + if (!s || !strcmp(s, ".jffs2") || + !strcmp(s, ".e") || !strcmp(s, ".i")) { +@@ -635,7 +827,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + .ooblen = rwsize, + .mode = MTD_OPS_RAW + }; +- ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_s=get_ticks(); ++#endif + if (read) + ret = mtd_read_oob(mtd, off, &ops); + else +@@ -643,14 +837,60 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + } else if (raw) { + ret = raw_access(mtd, addr, off, pagecount, read, + no_verify); ++ } else if ((s != NULL && !strcmp(s, ".yaffs"))) { ++ if (read) { ++ ret = nand_read_yaffs_skip_bad(mtd, off, ++ &rwsize, (u_char *)(uintptr_t)addr); ++ } else { ++ if (yaffs_tag_check((unsigned char *)(uintptr_t)addr, ++ mtd->writesize, size)) ++ return 1; ++ ++ ret = nand_write_yaffs_skip_bad(mtd, off, ++ &rwsize, (u_char *)(uintptr_t)addr); ++ } ++ } else if ((s != NULL && !strcmp(s, ".ecc0"))) { ++ ecc0_flag = 1; ++ if (read) { ++ ret = nand_read_yaffs_skip_bad(mtd, off, ++ &rwsize, (u_char *)(uintptr_t)addr); ++ } else { ++ ret = nand_write_yaffs_skip_bad(mtd, off, ++ &rwsize, (u_char *)(uintptr_t)addr); ++ } ++ ecc0_flag = 0; ++ } else if (((s != NULL && !strcmp(s, ".yaffsuc")))) { ++ if (read) { ++ printf("not support\n"); ++ return 1; ++ } else ++ ret = nand_write_yaffs_skip_bad(mtd, off, ++ &rwsize, (u_char *)(uintptr_t)addr); + } else { + printf("Unknown nand command suffix '%s'.\n", s); + return 1; + } +- ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_e=get_ticks(); ++ unsigned long speed_kb=lldiv((size>>10)*1000000,ticks2usec(time_e-time_s)); ++ unsigned int first_bit=lldiv(speed_kb,1024); ++ unsigned int second_bit=(speed_kb%1024)*10/1024; ++ unsigned int third_bit=((speed_kb%1024)*10%1024)*10/1024; ++ unsigned int forth_bit=(((speed_kb%1024)*10%1024)*10%1024)*10/1024; ++ ++ printf("\nnand %s::time = %lu usec.\n, size = %lu KB, speed: %u.%u%u%u MB/S\n", ++ read ? "read" : "written", ticks2usec(time_e - time_s), (unsigned long)(size >> 10), ++ first_bit,second_bit,third_bit,forth_bit); ++#endif + printf(" %zu bytes %s: %s\n", rwsize, + read ? "read" : "written", ret ? "ERROR" : "OK"); + ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ if (strcmp(cmd, "read") == 0) { ++ timestamp_mark("nand_read_end", __LINE__); ++ } ++#endif ++ + return ret == 0 ? 0 : 1; + } + +@@ -799,6 +1039,17 @@ static char nand_help_text[] = + "nand read.raw - addr off|partition [count]\n" + "nand write.raw[.noverify] - addr off|partition [count]\n" + " Use read.raw/write.raw to avoid ECC and access the flash as-is.\n" ++ "nand read.yaffs - addr off|partition size\n" ++ "nand write.yaffs - addr off|partition size\n" ++ " read/write 'size' bytes starting at offset 'off'\n" ++ " to/from memory address 'addr', skipping bad blocks.\n" ++ "nand write.yaffsuc - addr off|partition size\n" ++ " read/write 'size' bytes starting at offset 'off'\n" ++ " to/from memory address 'addr', skipping bad blocks.\n" ++ "nand read.ecc0 - addr off|partition size\n" ++ "nand write.ecc0 - addr off|partition size\n" ++ " read/write 'size' bytes starting at offset 'off'\n" ++ " to/from memory address 'addr', skipping bad blocks.\n" + #ifdef CONFIG_CMD_NAND_TRIMFFS + "nand write.trimffs - addr off|partition size\n" + " write 'size' bytes starting at offset 'off' from memory address\n" +diff --git a/cmd/nvcopy.c b/cmd/nvcopy.c +new file mode 100644 +index 0000000..f822ebd +--- /dev/null ++++ b/cmd/nvcopy.c +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#define ENV_MAX_LEN 4096 ++ ++static int env_copy(const char *name, char *addr) ++{ ++ struct env_entry e = {0}; ++ struct env_entry *ep = NULL; ++ e.key = name; ++ e.data = NULL; ++ hsearch_r(e, ENV_FIND, &ep, &env_htab, H_HIDE_DOT); ++ if (ep == NULL) ++ return -1; ++ int ret = 0; ++ size_t len = strlen(ep->data) + strlen("=") + strlen(ep->key) + 1; ++ if (len > ENV_MAX_LEN) { ++ printf("argc len should less than %d.\n", ENV_MAX_LEN); ++ return -1; ++ } ++ ++ char *buf = (char *)malloc(len); ++ if (buf == NULL) { ++ printf("malloc buf failed.\n"); ++ return -1; ++ } ++ (void)memset_s(buf, len, 0, len); ++ ++ if (strcat_s(buf, len, ep->key) < 0) { ++ printf("strcat failed.\n"); ++ free(buf); ++ return -1; ++ } ++ ++ if (strcat_s(buf, len, "=") < 0) { ++ printf("strcat failed.\n"); ++ free(buf); ++ return -1; ++ } ++ ++ if (strcat_s(buf, len, ep->data) < 0) { ++ printf("strcat failed.\n"); ++ free(buf); ++ return -1; ++ } ++ ++ printf("%s\n", buf); ++ if (memcpy_s(addr, len, buf, len) != EOK) { ++ printf("memcpy_s failed.\n"); ++ free(buf); ++ return -1; ++ } ++ ++ free(buf); ++ ++ return 0; ++} ++ ++static int do_env_copy(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ if (argc != 3) ++ return CMD_RET_USAGE; ++ ++ char *name = (char *)argv[1]; ++ char *addr = (char *)(uintptr_t)simple_strtoul(argv[2], NULL, 16); /* 2: addr argc, 16: base */ ++ ++ if ((name == NULL) || (addr == NULL)) { ++ printf("invalid arg!\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ int ret = env_copy(name, addr); ++ if (ret != 0) { ++ printf("copy env failed!\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ envcopy, 5, 0, do_env_copy, ++ "[envcopy name addr] copy env value to specified addr", ++ "copy env value to specified addr\n" ++); +diff --git a/cmd/nvedit.c b/cmd/nvedit.c +old mode 100644 +new mode 100755 +index 81d94cd..c097958 +--- a/cmd/nvedit.c ++++ b/cmd/nvedit.c +@@ -52,6 +52,7 @@ DECLARE_GLOBAL_DATA_PTR; + defined(CONFIG_ENV_IS_IN_SATA) || \ + defined(CONFIG_ENV_IS_IN_SPI_FLASH) || \ + defined(CONFIG_ENV_IS_IN_REMOTE) || \ ++ defined(CONFIG_ENV_IS_IN_UFS) || \ + defined(CONFIG_ENV_IS_IN_UBI) + + #define ENV_IS_IN_DEVICE +@@ -61,7 +62,7 @@ DECLARE_GLOBAL_DATA_PTR; + #if !defined(ENV_IS_IN_DEVICE) && \ + !defined(CONFIG_ENV_IS_NOWHERE) + # error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|\ +-NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE ++NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI|UFS} or CONFIG_ENV_IS_NOWHERE + #endif + + /* +@@ -1443,7 +1444,7 @@ U_BOOT_CMD_COMPLETE( + #endif + + U_BOOT_CMD_COMPLETE( +- printenv, CONFIG_SYS_MAXARGS, 1, do_env_print, ++ printenv, CONFIG_SYS_MAXARGS, 0, do_env_print, + "print environment variables", + "[-a]\n - print [all] values of all environment variables\n" + #if defined(CONFIG_CMD_NVEDIT_EFI) +diff --git a/cmd/sf.c b/cmd/sf.c +index e993b3e..cb2c629 100644 +--- a/cmd/sf.c ++++ b/cmd/sf.c +@@ -146,8 +146,6 @@ static int do_spi_flash_probe(int argc, char * const argv[]) + printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); + return 1; + } +- +- flash = new; + #endif + + return 0; +@@ -266,7 +264,11 @@ static int do_spi_flash_read_write(int argc, char * const argv[]) + char *endp; + int ret = 1; + int dev = 0; +- loff_t offset, len, maxsize; ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ unsigned long long time_s; ++ unsigned long long time_e; ++#endif ++ loff_t offset, len, maxsize, off_start; + + if (argc < 3) + return -1; +@@ -299,12 +301,73 @@ static int do_spi_flash_read_write(int argc, char * const argv[]) + int read; + + read = strncmp(argv[0], "read", 4) == 0; +- if (read) ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_s = get_ticks(); ++#endif ++ if (read) { + ret = spi_flash_read(flash, offset, len, buf); +- else +- ret = spi_flash_write(flash, offset, len, buf); ++ off_start = offset; ++ } else { ++ unsigned long write_start, write_len, write_step; ++ int percent_complete = -1; ++ char *pbuf = buf; ++ ++ off_start = offset; ++ ++ write_start = offset; ++ write_len=len; ++ write_step = flash->erase_size; ++ ++ while (write_len > 0) { ++ if (write_len < write_step) ++ write_step = write_len; ++ ++ ret = spi_flash_write(flash, offset, write_step, pbuf); ++ if (ret) ++ break; ++ ++ offset += write_step; ++ pbuf += write_step; ++ write_len -= write_step; ++ ++ do { ++ unsigned long long n = (unsigned long long) ++ (offset - write_start) * 100; ++ int percent; ++ ++ do_div(n, len); ++ percent = (int)n; ++ ++ /* output progress message only at whole percent ++ * * steps to reduce the number of messages ++ * * printed on (slow) serial consoles ++ * */ ++ if (percent != percent_complete) { ++ percent_complete = percent; ++ ++ printf("\rWriting at 0x%llx -- %3d%% " ++ "complete.", offset, percent); ++ } ++ } while (0); ++ } ++ } + +- printf("SF: %zu bytes @ %#x %s: ", (size_t)len, (u32)offset, ++ puts("\n"); ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_e = get_ticks(); ++ ++ unsigned long speed_kb=lldiv((len>>10)*1000000,ticks2usec(time_e-time_s)); ++ unsigned int first_bit=lldiv(speed_kb,1024); ++ unsigned int second_bit=(speed_kb%1024)*10/1024; ++ unsigned int third_bit=((speed_kb%1024)*10%1024)*10/1024; ++ unsigned int forth_bit=(((speed_kb%1024)*10%1024)*10%1024)*10/1024; ++ ++ printf("\nSF:%zu bytes @ %#x %s: cost:%lu usec;speed:%u.%u%u%u MB/S\n",(size_t)len,(u32)off_start, ++ read ? "Read" :"Written",(unsigned long )ticks2usec(time_e - time_s),first_bit, ++ second_bit,third_bit,forth_bit ++ ); ++#endif ++ printf("SF: %zu bytes @ %#x %s: ", (size_t)len, (u32)off_start, + read ? "Read" : "Written"); + if (ret) + printf("ERROR %d\n", ret); +@@ -323,7 +386,14 @@ static int do_spi_flash_erase(int argc, char * const argv[]) + int dev = 0; + loff_t offset, len, maxsize; + ulong size; +- ++ unsigned long erase_start, erase_len, erase_step; ++ unsigned long erase_end = 0; ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ unsigned long long time_s; ++ unsigned long long time_e; ++#endif ++ int percent_complete = -1; ++ + if (argc < 3) + return -1; + +@@ -341,14 +411,107 @@ static int do_spi_flash_erase(int argc, char * const argv[]) + argv[0], flash->size); + return 1; + } ++ ++ erase_start = offset; ++ erase_len = size; ++ erase_step = flash->erase_size; ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_s=get_ticks(); ++#endif ++ while (size > 0){ ++ if (size < erase_step) ++ erase_step = size; ++ ++ ret = spi_flash_erase(flash, offset, erase_step); ++ if (ret) { ++ printf("SPI flash %s failed\n", argv[0]); ++ return 1; ++ } ++ ++ size -= erase_step; ++ offset += erase_step; ++ erase_end += erase_step; ++ ++ do { ++ unsigned long long n = (unsigned long long) ++ (offset - erase_start) * 100; ++ int percent; ++ ++ do_div(n, erase_len); ++ percent = (int)n; ++ ++ /* output progress message only at whole percent ++ * * steps to reduce the number of messages printed ++ * * on (slow) serial consoles ++ * */ ++ if (percent != percent_complete) { ++ percent_complete = percent; + +- ret = spi_flash_erase(flash, offset, size); +- printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)size, (u32)offset, +- ret ? "ERROR" : "OK"); ++ printf("\rErasing at 0x%llx -- %3d%% complete.", ++ offset, percent); ++ } ++ } while (0); ++ } ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ time_e = get_ticks(); ++#endif ++ puts("\n"); ++ ++ printf("SF: %zu bytes @ %#x Erased: %s\n", (size_t)erase_end, (u32)erase_start, ++ ret ? "ERROR" : "OK"); ++#ifdef CONFIG_VENDOR_SPIFLASH_SPEED ++ printf("\nSF: %zu bytes @ %#x Erased cost:%lu usec;speed: %lu KB/S\n",(size_t)erase_end,(u32)erase_start, ++ (unsigned long )ticks2usec(time_e - time_s),(unsigned long)(erase_end >> 10)*1000000/ticks2usec(time_e - time_s)); ++#endif + + return ret == 0 ? 0 : 1; + } + ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++static int do_spi_flash_lock(int argc, char * const argv[]) ++{ ++ char *endp; ++ unsigned char level; ++ unsigned char cmp = BP_CMP_BOTTOM; ++ ++ if ((argc < 1) || (argc > 2)) ++ goto usage; ++ ++ /* sf lock */ ++ if (argc == 1) { ++ spi_flash_lock(0, 0, BP_OP_GET); ++ puts("\n"); ++ goto usage; ++ } ++ ++ /* sf lock all/level */ ++ if (argc == 2) { ++ if (strcmp(argv[1], "all") == 0) { ++ level = flash->bp_level_max; ++ } else { ++ level = simple_strtoul(argv[1], &endp, 0); ++ if (level > flash->bp_level_max) ++ goto usage; ++ if (*endp != 0) ++ goto usage; ++ } ++ } ++ ++ spi_flash_lock(cmp, level, BP_OP_SET); ++ ++ return 0; ++usage: ++ puts("\tsf lock level/all\n"); ++ printf("Usage:\n\t all: level(%d), lock all blocks.\n", ++ flash->bp_level_max); ++ puts("\tlevel(0): unlock all blocks.\n"); ++ printf("\tset spi nor chip block protection level(0 - %d).\n", ++ flash->bp_level_max); ++ printf("\tAs usual: lock_len = chipsize >> (%d - level)\n", ++ flash->bp_level_max); ++ return 1; ++} ++#else + static int do_spi_protect(int argc, char * const argv[]) + { + int ret = 0; +@@ -379,6 +542,7 @@ static int do_spi_protect(int argc, char * const argv[]) + + return ret == 0 ? 0 : 1; + } ++#endif /* CONFIG_SPI_BLOCK_PROTECT */ + + #ifdef CONFIG_CMD_SF_TEST + enum { +@@ -572,8 +736,13 @@ static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc, + ret = do_spi_flash_read_write(argc, argv); + else if (strcmp(cmd, "erase") == 0) + ret = do_spi_flash_erase(argc, argv); ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ else if (strcmp(cmd, "lock") == 0) ++ ret = do_spi_flash_lock(argc, argv); ++#else + else if (strcmp(cmd, "protect") == 0) + ret = do_spi_protect(argc, argv); ++#endif + #ifdef CONFIG_CMD_SF_TEST + else if (!strcmp(cmd, "test")) + ret = do_spi_flash_test(argc, argv); +@@ -615,5 +784,8 @@ U_BOOT_CMD( + " or to start of mtd `partition'\n" + "sf protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n" + " at address 'sector'\n" ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ "sf lock level|all - set spi block protection level\n" ++#endif + SF_TEST_HELP + ); +diff --git a/cmd/ufs.c b/cmd/ufs.c +index 5b25788..a82bfd8 100644 +--- a/cmd/ufs.c ++++ b/cmd/ufs.c +@@ -9,6 +9,7 @@ + #include + #include + ++#ifndef CONFIG_UFS + static int do_ufs(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + { + int dev, ret; +@@ -35,3 +36,345 @@ U_BOOT_CMD(ufs, 3, 1, do_ufs, + "UFS sub system", + "init [dev] - init UFS subsystem\n" + ); ++#else ++ ++#define UFS_BLKSIZE_SHIFT 12 ++#define b2m(a) (((a) >> 10) / 1000) ++#define b2k(a) (((a) >> 10) % 1000) ++static int curr_device = 0; ++ ++static int do_ufs_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ u32 blk, cnt; ++ u64 addr; ++ unsigned long long start_ticks, end_ticks; ++ unsigned long long size, speed; ++ ++ if (argc != 5) /* 5 arg */ ++ return CMD_RET_USAGE; ++ ++ addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */ ++ blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */ ++ cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */ ++ ++ printf("\nUFS read: dev # %d, block # %u, count %u ... ", curr_device, blk, cnt); ++ ++ ufs_storage_init(); ++ start_ticks = get_ticks(); ++ if (ufs_read_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT, cnt << UFS_BLKSIZE_SHIFT)) { ++ printf("0 blocks read: ERROR\n"); ++ return CMD_RET_FAILURE; ++ } ++ end_ticks = get_ticks(); ++ printf("%d blocks read: OK\n", cnt); ++ ++ size = cnt << UFS_BLKSIZE_SHIFT; ++ speed = (size * CONFIG_SYS_TIMER_RATE) / (end_ticks - start_ticks); ++ printf("%llu.%03llu MB/s\n", b2m(speed), b2k(speed)); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ u32 blk, cnt; ++ u64 addr; ++ unsigned long long start_ticks, end_ticks; ++ unsigned long long size, speed; ++ ++ if (argc != 5) /* 5 arg */ ++ return CMD_RET_USAGE; ++ ++ addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */ ++ blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */ ++ cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */ ++ ++ if (strncmp(argv[0], "write.ext4sp", sizeof("write.ext4sp")) == 0) { ++#ifdef CONFIG_EXT4_SPARSE ++ printf("\nUFS write ext4 sparse: dev # %d, block # %u, count %u ...\n", ++ curr_device, blk, cnt); ++ ++ return ufs_ext4_unsparse((void *)(uintptr_t)addr, blk, cnt); ++#else ++ printf("Not support.\n"); ++ return CMD_RET_SUCCESS; ++#endif ++ } ++ ++ printf("\nUFS write: dev # %d, block # %u, count %u ... ", curr_device, blk, cnt); ++ ++ ufs_storage_init(); ++ start_ticks = get_ticks(); ++ if (ufs_write_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT, cnt << UFS_BLKSIZE_SHIFT)) { ++ printf("0 blocks write: ERROR\n"); ++ return CMD_RET_FAILURE; ++ } ++ end_ticks = get_ticks(); ++ printf("%d blocks written: OK\n", cnt); ++ ++ size = cnt << UFS_BLKSIZE_SHIFT; ++ speed = (size * CONFIG_SYS_TIMER_RATE) / (end_ticks - start_ticks); ++ printf("%llu.%03llu MB/s\n", b2m(speed), b2k(speed)); ++ ++ /* must write boot data to boot lun due to bootup demand, ++ * start from block 0, no longer than 0x400 blocks(4M). ++ */ ++ if ((blk == 0) && (cnt <= 0x400)) { ++ /* write boot data */ ++ if (ufs_write_boot_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT, ++ cnt << UFS_BLKSIZE_SHIFT)) { ++ printf("boot write: ERROR\n"); ++ return CMD_RET_FAILURE; ++ } ++ } ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_bootread(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ u32 blk, cnt; ++ u64 addr; ++ ++ if (argc != 5) /* 5 arg */ ++ return CMD_RET_USAGE; ++ ++ addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */ ++ blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */ ++ cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */ ++ ++ printf("\nUFS read: dev # %d, block # %u, count %u ... ", curr_device, blk, cnt); ++ ++ ufs_storage_init(); ++ if (ufs_read_boot_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT, ++ cnt << UFS_BLKSIZE_SHIFT)) { ++ printf("boot read: ERROR\n"); ++ return CMD_RET_FAILURE; ++ } ++ printf("%d blocks read: OK\n", cnt); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_bootwrite(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ u32 blk, cnt; ++ u64 addr; ++ ++ if (argc != 5) /* 5 arg */ ++ return CMD_RET_USAGE; ++ ++ addr = (u64)simple_strtoul(argv[2], NULL, 16); /* arg 2: ddr addr, 16 in hex */ ++ blk = simple_strtoul(argv[3], NULL, 16); /* arg 3: start block, 16 in hex */ ++ cnt = simple_strtoul(argv[4], NULL, 16); /* arg 4: block count, 16 in hex */ ++ ++ printf("\nUFS write: dev # %d, block # %u, count %u ... ", curr_device, blk, cnt); ++ ++ ufs_storage_init(); ++ if (ufs_write_boot_storage(addr, (u64)blk << UFS_BLKSIZE_SHIFT, ++ cnt << UFS_BLKSIZE_SHIFT)) { ++ printf("boot write: ERROR\n"); ++ return CMD_RET_FAILURE; ++ } ++ printf("%d blocks write: OK\n", cnt); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_reinit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ if (argc != 2) /* 2 arg */ ++ return CMD_RET_USAGE; ++ ++ ufs_reinit(); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_reg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ if (argc != 2) /* 2 arg */ ++ return CMD_RET_USAGE; ++ ++ ufs_reg_dump(); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_setlun(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ u32 lun_num; ++ ++ if (argc != 2) /* 2 arg */ ++ return CMD_RET_USAGE; ++ ++ lun_num = simple_strtoul(argv[1], NULL, 16); /* arg 1: lun, 16 in hex */ ++ ufs_set_active_lun(lun_num); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_bootlun(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ u32 lun_num; ++ ++ if (argc != 2) /* 2 arg */ ++ return CMD_RET_USAGE; ++ ++ lun_num = simple_strtoul(argv[1], NULL, 16); /* arg 1: lun, 16 in hex */ ++ ufs_set_bootlun(lun_num); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_hi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ int ret; ++ ++ if (argc != 2) /* 2 arg */ ++ return CMD_RET_USAGE; ++ ++ ret = ufs_hibernate_enter(); ++ if (ret) ++ return CMD_RET_FAILURE; ++ printf("hibernate in ok\n"); ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_ho(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ int ret; ++ ++ if (argc != 2) /* 2 arg */ ++ return CMD_RET_USAGE; ++ ++ ret = ufs_hibernate_exit(); ++ if (ret) ++ return CMD_RET_FAILURE; ++ printf("hibernate out ok\n"); ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_ufs_mode(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ struct pwr_mode_params pmp; ++ uint32_t mode, gear, rate, lane; ++ ++ if (argc != 5) /* 5 arg */ ++ return CMD_RET_USAGE; ++ ++ mode = simple_strtoul(argv[1], NULL, 16); /* arg 1: mode, 16 in hex */ ++ gear = simple_strtoul(argv[2], NULL, 16); /* arg 2: gear, 16 in hex */ ++ rate = simple_strtoul(argv[3], NULL, 16); /* arg 3: rate, 16 in hex */ ++ lane = simple_strtoul(argv[4], NULL, 16); /* arg 4: lane, 16 in hex */ ++ mode = (mode << 4) | mode; /* rx shift 4 */ ++ ++ pmp.pwr_mode = mode; ++ pmp.tx_gear = gear; ++ pmp.rx_gear = gear; ++ pmp.hs_series = rate; ++ pmp.tx_lanes = lane; ++ pmp.rx_lanes = lane; ++ ++ printf("UFS %s Gear-%d Rate-%c Lane-%d\n", ++ ((mode == SLOW_MODE) ? "Slow" : ++ ((mode == SLOWAUTO_MODE) ? "SlowAuto" : ++ ((mode == FAST_MODE) ? "Fast" : "FastAuto"))), ++ gear, (rate == 1) ? 'A' : 'B', lane); ++ ++ if (do_mode_change(&pmp)) { ++ printf("power mode change fail\n"); ++ return CMD_RET_FAILURE; ++ } else { ++ printf("power mode change ok\n"); ++ } ++ return CMD_RET_SUCCESS; ++} ++ ++static cmd_tbl_t cmd_ufs[] = { ++ U_BOOT_CMD_MKENT(read, 5, 0, do_ufs_read, "", ""), ++ U_BOOT_CMD_MKENT(write, 5, 0, do_ufs_write, "", ""), ++ U_BOOT_CMD_MKENT(bootread, 5, 0, do_ufs_bootread, "", ""), ++ U_BOOT_CMD_MKENT(bootwrite, 5, 0, do_ufs_bootwrite, "", ""), ++ U_BOOT_CMD_MKENT(reinit, 2, 0, do_ufs_reinit, "", ""), ++ U_BOOT_CMD_MKENT(reg, 2, 0, do_ufs_reg, "", ""), ++ U_BOOT_CMD_MKENT(setlun, 2, 0, do_ufs_setlun, "", ""), ++ U_BOOT_CMD_MKENT(bootlun, 2, 0, do_ufs_bootlun, "", ""), ++ U_BOOT_CMD_MKENT(hi, 2, 0, do_ufs_hi, "", ""), ++ U_BOOT_CMD_MKENT(ho, 2, 0, do_ufs_ho, "", ""), ++ U_BOOT_CMD_MKENT(mode, 5, 0, do_ufs_mode, "", ""), ++}; ++ ++static int do_ufsops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ cmd_tbl_t *cp = NULL; ++ ++ if (argc == 1) { ++ cmd_usage(cmdtp); ++ return CMD_RET_SUCCESS; ++ } ++ ++ cp = find_cmd_tbl(argv[1], cmd_ufs, ARRAY_SIZE(cmd_ufs)); ++ ++ /* Drop the ufs command */ ++ argc--; ++ argv++; ++ ++ if (cp == NULL || argc > cp->maxargs) ++ return CMD_RET_USAGE; ++ ++ return cp->cmd(cmdtp, flag, argc, argv); ++} ++ ++U_BOOT_CMD( ++ ufs, 6, 0, do_ufsops, ++ "UFS sub system", ++ "read addr blk# cnt\n" ++ "ufs write addr blk# cnt\n" ++ "ufs write.ext4sp addr blk# cnt\n" ++ "ufs bootread addr blk# cnt\n" ++ "ufs bootwrite addr blk# cnt\n" ++ "ufs reinit \n" ++ "ufs reg \n" ++); ++ ++static int do_ufsinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ u32 type; ++ ++ switch (argc) { ++ case 0: /* 0 arg */ ++ case 1: /* 1 arg */ ++ ufs_show_desc_info(0xF); ++ break; ++ case 2: /* 2 arg */ ++ if (strncmp(argv[1], "-h", sizeof("-h")) == 0) { ++ cmd_usage(cmdtp); ++ break; ++ } ++ ++ type = simple_strtoul(argv[1], NULL, 16); /* arg 1: type, 16 in hex */ ++ ufs_show_desc_info(type); ++ break; ++ default: ++ return CMD_RET_USAGE; ++ } ++ ++ return CMD_RET_SUCCESS; ++} ++ ++U_BOOT_CMD( ++ ufsinfo, 2, 0, do_ufsinfo, ++ "display UFS info", ++ "\n" ++ " idn :0x0 -- device descriptor\n" ++ " idn :0x1 -- configuration descriptor\n" ++ " idn :0x2 -- unit descriptor\n" ++ " idn :0x4 -- interconnect descriptor\n" ++ " idn :0x5 -- string descriptor\n" ++ " idn :0x7 -- geometry descriptor\n" ++ " idn :0x9 -- health descriptor\n" ++ " idn :0xE -- show all info\n" ++ " idn :0xF -- show basic info\n" ++); ++#endif +diff --git a/cmd/usb.c b/cmd/usb.c +index dd9ac0b..f40c23f 100644 +--- a/cmd/usb.c ++++ b/cmd/usb.c +@@ -632,10 +632,16 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + if (argc < 2) + return CMD_RET_USAGE; + ++ if (strncmp(argv[1], "device", 6) == 0) { ++ printf("Install USB Device...\n"); ++ udc_connect(); ++ return 0; ++ } ++ + if (strncmp(argv[1], "start", 5) == 0) { +- if (usb_started) +- return 0; /* Already started */ ++ + printf("starting USB...\n"); ++ usb_stop(); + do_usb_start(); + return 0; + } +diff --git a/cmd/usbtftp.c b/cmd/usbtftp.c +new file mode 100644 +index 0000000..8ab2d6c +--- /dev/null ++++ b/cmd/usbtftp.c +@@ -0,0 +1,442 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "usbtftp.h" ++#include ++ ++#define DMABUF_SIZE 512 ++#define COMMAND_SIZE 256 ++ ++static unsigned int usb_open_flag = 0; ++static char command[COMMAND_SIZE] = {0}; ++ ++static int usb_int_fun(usb3_device_t *usb3_dev) ++{ ++ struct usb_device_descriptor *usb3_dev_desc = NULL; ++ uint8_t string_manu[] = {'V', 0, 'E', 0, 'N', 0, 'D', 0, 'O', 0, 'R', 0}; ++ uint8_t string_prod[] = {'U', 0, 'S', 0, 'B', 0, 'B', 0, 'u', 0, 'r', 0, 'n', 0}; ++ usb3_pcd_t *pcd = &usb3_dev->pcd; ++ usb3_pcd_ep_t *ep = &pcd->in_ep; ++ usb3_pcd_req_t *req = &ep->req; ++ ++ (void)memset_s((void *)usb3_dev, sizeof(usb3_device_t), 0, sizeof(usb3_device_t)); ++ usb3_dev_desc = (usb_device_descriptor_t *)malloc(sizeof( ++ struct usb_device_descriptor)); ++ if (usb3_dev_desc == NULL) { ++ debug("usb3_dev_desc: out of memory\n"); ++ return -ENOMEM; ++ } ++ req->bufdma = (uint8_t *)malloc(DMABUF_SIZE); ++ if (req->bufdma == NULL) { ++ printf("[%s : %d] malloc failed!\n", __func__, __LINE__); ++ return -1; ++ } ++ usb_info("size of usb3_dev %d\n", sizeof(*usb3_dev)); ++ usb3_dev->base = (volatile uint8_t *)USB3_CTRL_REG_BASE; ++ ++ usb3_dev->string_manu_len = sizeof(string_manu); ++ usb3_dev->string_prod_len = sizeof(string_prod); ++ usb3_dev->dev_desc = usb3_dev_desc; ++ if (memcpy_s(usb3_dev->string_manu, sizeof(usb3_dev->string_manu), string_manu, usb3_dev->string_manu_len)) { ++ printf("memcpy_s err : %s %d.\n", __func__, __LINE__); ++ return -ENOMEM; ++ } ++ if (memcpy_s(usb3_dev->string_prod, sizeof(usb3_dev->string_prod), string_prod, usb3_dev->string_prod_len)) { ++ printf("memcpy_s err : %s %d.\n", __func__, __LINE__); ++ return -ENOMEM; ++ } ++ usb3_dev->pcd.ep0_setup_desc = (usb3_dma_desc_t *) ++ ((uintptr_t)(usb3_dev->pcd.ep0_setup + 15) & (uint32_t)(~15)); /* augment 15*/ ++ usb3_dev->pcd.ep0_in_desc = (usb3_dma_desc_t *) ++ ((uintptr_t)(usb3_dev->pcd.ep0_in + 15) & (uint32_t)(~15)); /* augment 15*/ ++ usb3_dev->pcd.ep0_out_desc = (usb3_dma_desc_t *) ++ ((uintptr_t)(usb3_dev->pcd.ep0_out + 15) & (uint32_t)(~15)); /* augment 15*/ ++ usb3_dev->pcd.in_ep.ep_desc = (usb3_dma_desc_t *) ++ ((uintptr_t)(usb3_dev->pcd.in_ep.epx_desc + 15) & (uint32_t)(~15)); /* augment 15*/ ++ usb3_dev->pcd.out_ep.ep_desc = (usb3_dma_desc_t *) ++ ((uintptr_t)(usb3_dev->pcd.out_ep.epx_desc + 15) & (uint32_t)(~15)); /* augment 15*/ ++ ++ /* Release usb3.0 controller */ ++ phy_usb_init(0); ++ ++ /* Get usb3.0 version number */ ++ usb3_dev->snpsid = usb3_rd32((volatile uint32_t *) ++ (usb3_dev->base + USB3_CORE_REG_BASE + USB3_CORE_GSNPSID_REG_OFFSET)); ++ ++ /* Initialize usb3.0 core */ ++ usb3_common_init(usb3_dev, usb3_dev->base + USB3_CORE_REG_BASE); ++ ++ /* Initialize usb3.0 pcd */ ++ usb3_init(usb3_dev); ++ ++ usb_info("usb init done\n"); ++ ++ return 0; ++} ++ ++static int udc_request(void) ++{ ++ usb3_device_t *usb3_dev = NULL; ++ int ret; ++ unsigned long time_start; ++ ++ dcache_disable(); ++ ++ usb3_dev = (usb3_device_t *)malloc(sizeof(usb3_device_t)); ++ if (usb3_dev == NULL) { ++ debug("usb3_dev: out of memory\n"); ++ return -ENOMEM; ++ } ++ ret = usb_int_fun(usb3_dev); ++ if (ret == (-ENOMEM)) ++ goto free_usb3_dev; ++ ++ time_start = get_timer(0); ++ ++ /* inital the send buffer */ ++ (void)memset_s(tx_state, sizeof(tx_state), 0, sizeof(tx_state)); ++ ++ while (usb_open_flag) { ++ usb3_handle_event(usb3_dev); ++ ++ if (get_eventbuf_count(usb3_dev) != 0) { ++ time_start = get_timer(0); ++ continue; ++ } ++ ++ if (get_timer(time_start) > 5000) { /* 5000ms */ ++ printf("the USB has no data for about 5s, stop the USB Device\n"); ++ break; ++ } ++ } ++ usb_open_flag = 0; ++ ++ phy_usb_init(0); ++ usb_stop(); ++ ++ free(usb3_dev->pcd.in_ep.req.bufdma); ++ usb3_dev->pcd.in_ep.req.bufdma = NULL; ++ free(usb3_dev->dev_desc); ++ usb3_dev->dev_desc = NULL; ++ ++free_usb3_dev: ++ free(usb3_dev); ++ usb3_dev = NULL; ++ ++ dcache_enable(); ++ ++ return ret; ++} ++ ++static int do_usbtftp_download(int argc, char* const argv[]) ++{ ++ int ret; ++ ++ if (strlen(argv[1]) + strlen(argv[2]) + 15 >= 256) ++ return -1; ++ ++ char *endp = NULL; ++ (void)simple_strtoul(argv[1], &endp, 16); /* 16 Hexadecimal */ ++ if (*argv[1] == 0 || *endp != 0) ++ return -1; ++ ++ if (usb_open_flag == 1) { ++ printf("usbtftp is running.\n"); ++ return 1; ++ } ++ ++ (void)memset_s(command, sizeof(command), 0, sizeof(command)); ++ if (!sprintf_s(command, COMMAND_SIZE, "usbtftp %s %s", argv[1], argv[2])) { ++ printf("sprintf_s err : %s %d.\n", __func__, __LINE__); ++ return -1; ++ } ++ usb_open_flag = 1; ++ ++ ret = udc_request(); ++ ++ return ret; ++} ++ ++#if defined CONFIG_FMC_SPI_NOR ++static struct spi_flash *spiflash = NULL; ++ ++static int do_flash_probe(void) ++{ ++ unsigned int bus = CONFIG_SF_DEFAULT_BUS; ++ unsigned int cs = CONFIG_SF_DEFAULT_CS; ++ unsigned int speed = CONFIG_SF_DEFAULT_SPEED; ++ unsigned int mode = CONFIG_SF_DEFAULT_MODE; ++ ++#ifdef CONFIG_DM_SPI_FLASH ++ struct udevice *new = NULL; ++ struct udevice *bus_dev = NULL; ++ int ret; ++ ++ /* The speed and mode : In DM mode defaults will be taken from DT */ ++#else ++ struct spi_flash *new = NULL; ++#endif ++ ++#ifdef CONFIG_DM_SPI_FLASH ++ /* Remove the old device, otherwise probe will just be a nop */ ++ ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new); ++ if (!ret) ++ device_remove(new); ++ spiflash = NULL; ++ ret = spi_flash_probe_bus_cs(bus, cs, speed, mode, &new); ++ if (ret) { ++ printf("Failed to initialize SPI flash at %u:%u (error %d)\n", bus, cs, ret); ++ return 1; ++ } ++ ++ spiflash = dev_get_uclass_priv(new); ++#else ++ if (spiflash) ++ spi_flash_free(spiflash); ++ ++ new = spi_flash_probe(bus, cs, speed, mode); ++ if (!new) { ++ printf("Failed to initialize SPI flash at %u:%u\n", bus, cs); ++ return 1; ++ } ++ spiflash = new; ++#endif ++ ++ return 0; ++} ++ ++static unsigned char *membuf = NULL; ++static unsigned int mem_len = 0; ++static int do_spi_flash_read(int argc, char* const argv[]) ++{ ++ unsigned long size; ++ char *endp = NULL; ++ int ret; ++ int dev = 0; ++ loff_t offset, len, maxsize; ++ ++ if (argc < 3) ++ return -1; ++ ++ if (spiflash == NULL) { ++ printf("error, spiflash is null!\n"); ++ return -1; ++ } ++ ++ if (mtd_arg_off(argv[1], &dev, &offset, &len, &maxsize, MTD_DEV_TYPE_NOR, spiflash->size)) ++ return -1; ++ ++ size = simple_strtoul(argv[3], &endp, 16); /* 16 Hexadecimal */ ++ if (*argv[3] == 0 || *endp != 0) ++ return -1; ++ ++ /* Consistency checking */ ++ if (offset + size > spiflash->size) { ++ printf("ERROR: attempting %s past flash size (%#x)\n", argv[0], spiflash->size); ++ return 1; ++ } ++ ++ len = size; ++ ++ membuf = (unsigned char *)malloc(size); ++ if (!membuf) { ++ puts("Failed to malloc memory\n"); ++ return 1; ++ } ++ ++ ret = spi_flash_read(spiflash, offset, len, (void *)membuf); ++ ++ mem_len = size; ++ return ret == 0 ? 0 : 1; ++} ++ ++static int frame_count = 0; ++#define FRAME_LENGTH 200 ++static int do_upload(uint8_t* const buff, unsigned int *bufflen) ++{ ++ unsigned char *head = (unsigned char *)buff; ++ *bufflen = 0; ++ ++ /* the tail frame has been sent, send again */ ++ if (frame_count == -1) { ++ head[0] = 0xED; ++ *bufflen = 1; ++ return 0; ++ } ++ ++ if (frame_count == 0) { ++ head[0] = 0xFE; ++ head[1] = (mem_len >> 24) & 0xFF; ++ head[2] = (mem_len >> 16) & 0xFF; ++ head[3] = (mem_len >> 8) & 0xFF; ++ head[4] = mem_len & 0xFF; ++ ++ head[5] = (FRAME_LENGTH >> 24) & 0xFF; ++ head[6] = (FRAME_LENGTH >> 16) & 0xFF; ++ head[7] = (FRAME_LENGTH >> 8) & 0xFF; ++ head[8] = FRAME_LENGTH & 0xFF; ++ ++ *bufflen = 9; /* 9 byte */ ++ frame_count++; ++ return 0; ++ } ++ ++ if (FRAME_LENGTH * frame_count <= mem_len) { ++ head[0] = 0xDA; ++ *bufflen = FRAME_LENGTH; ++ } else if ((FRAME_LENGTH * frame_count > mem_len) && ++ (FRAME_LENGTH * (frame_count - 1) < mem_len)) { ++ head[0] = 0xDA; ++ *bufflen = mem_len - FRAME_LENGTH * (frame_count - 1); ++ } else if ((FRAME_LENGTH * (frame_count - 1) >= mem_len)) { ++ head[0] = 0xED; ++ *bufflen = 0; ++ frame_count = -1; ++ } ++ ++ if (*bufflen != 0) { ++ if (memcpy_s(head + 1, DMABUF_SIZE -1, membuf + FRAME_LENGTH * (frame_count - 1), *bufflen)) { ++ printf("memcpy_s err : %s %d.\n", __func__, __LINE__); ++ return 1; ++ } ++ frame_count++; ++ } ++ ++ /* cmd is one byte */ ++ *bufflen = *bufflen + 1; ++ return 0; ++} ++#endif ++ ++static int do_usbtftp_upload(int argc, char* const argv[]) ++{ ++ if (strlen(argv[1]) + strlen(argv[2]) + 15 >= 256) ++ return -1; ++ ++ if (usb_open_flag == 1) { ++ printf("usbtftp is running.\n"); ++ return -1; ++ } ++ ++#if defined CONFIG_FMC_SPI_NOR ++ int ret; ++ ++ ret = do_flash_probe(); ++ if (ret != 0) ++ goto done; ++ ++ ret = do_spi_flash_read(argc, argv); ++ if (ret != 0) ++ goto done; ++ ++ frame_count = 0; ++ ++ set_usb3_call_back_func(do_upload); ++ ++ (void)memset_s(command, sizeof(command), 0, sizeof(command)); ++ if (sprintf_s(command, COMMAND_SIZE, "usbtftp %s %s %s", argv[1], argv[2], argv[3]) < 0) { ++ printf("sprintf_s err : %s %d.\n", __func__, __LINE__); ++ goto done; ++ } ++ usb_open_flag = 1; ++ ++ ret = udc_request(); ++ ++ frame_count = 0; ++ ++ set_usb3_call_back_func(NULL); ++ ++done: ++ ++#ifdef CONFIG_DM_SPI_FLASH ++ spiflash = NULL; ++#else ++ if (spiflash) { ++ spi_flash_free(spiflash); ++ spiflash = NULL; ++ } ++#endif ++ ++ if (membuf) { ++ free(membuf); ++ membuf = NULL; ++ } ++ mem_len = 0; ++ ++ return ret; ++#else ++ printf("usbtftp upoload can not support emmc or ufs now.\n"); ++ return -1; ++#endif ++} ++ ++ ++static int do_usbtftp(cmd_tbl_t *cmdtp, int flag, int argc, char* const argv[]) ++{ ++ int ret = 0; ++ if (strncmp(argv[1], "start", 5) == 0) { ++ if (usb_open_flag == 0) ++ goto usage; ++ ++ printf(" %s\n", command); ++ goto done; ++ } ++ ++ if (strncmp(argv[1], "error", 5) == 0) { ++ if (usb_open_flag == 0) ++ goto usage; ++ ++ usb_open_flag = 0; ++ printf("usbtftp error\n"); ++ goto done; ++ } ++ ++ if (strncmp(argv[1], "end", 3) == 0) { ++ if (usb_open_flag == 0) ++ goto usage; ++ ++ usb_open_flag = 0; ++ printf("usbtftp end\n"); ++ goto done; ++ } ++ ++ if (argc == 3) { ++ ret = do_usbtftp_download(argc, argv); ++ goto done; ++ } ++ ++ if (argc == 4) { ++ ret = do_usbtftp_upload(argc, argv); ++ goto done; ++ } ++ ++usage: ++ return CMD_RET_USAGE; ++ ++done: ++ return ret; ++} ++ ++U_BOOT_CMD( ++ usbtftp, 4, 0, do_usbtftp, ++ "download or upload image using USB protocol", ++ "command to download or upload image using USB protocol\n" ++ "usbtftp addr file len - upload `len(hex)' bytes starting at addr to file\n" ++ "usbtftp addr file - download file to addr\n" ++); +diff --git a/cmd/usbtftp.h b/cmd/usbtftp.h +new file mode 100644 +index 0000000..04445b3 +--- /dev/null ++++ b/cmd/usbtftp.h +@@ -0,0 +1,51 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __USBTFTP_H__ ++#define __USBTFTP_H__ ++ ++#include "../drivers/usb/gadget/udc3/usb3_drv.h" ++#include "common.h" ++#include "legacy-mtd-utils.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++extern int get_eventbuf_count(usb3_device_t *dev); ++extern void dcache_disable(void); ++extern void usb3_common_init(usb3_device_t *dev, volatile uint8_t *base); ++extern void phy_usb_init(int index); ++extern void usb3_init(usb3_device_t *dev); ++extern void dcache_enable(void); ++extern int usb_stop(void); ++extern void usb3_handle_event(usb3_device_t *dev); ++ ++extern char tx_state[200]; ++#if defined CONFIG_FMC_SPI_NOR ++typedef int (*USB3_HANDLE_REQUEST)(uint8_t* const buff, unsigned int *bufflen); ++extern void set_usb3_call_back_func(USB3_HANDLE_REQUEST func); ++#endif ++ ++#endif /* __USBTFTP_H__ */ +diff --git a/cmd/vendor/getinfo.c b/cmd/vendor/getinfo.c +new file mode 100644 +index 0000000..6d2f299 +--- /dev/null ++++ b/cmd/vendor/getinfo.c +@@ -0,0 +1,143 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#if defined(CONFIG_FMC_SPI_NAND) \ ++ || defined(CONFIG_FMC_NAND) ++#include ++#endif ++ ++/*****************************************************************************/ ++#if defined(CONFIG_FMC_SPI_NAND) || defined(CONFIG_FMC_NAND) || defined(CONFIG_FMC_SPI_NOR) ++static int print_mtd_info(const struct mtd_info_ex *mtd_info, const char *cmd) ++{ ++ unsigned int len, ix; ++ ++ if (mtd_info == NULL || mtd_info->type == 0) { /* no find spi/nand */ ++ printf("no find "); ++ if (*cmd == 's') ++ printf("spi"); ++ else if (*cmd == 'n') ++ printf("nand"); ++ printf("\n"); ++ } else { ++ printf("Block:%sB ", ultohstr(mtd_info->erasesize)); ++ printf("Chip:%sB*%d ", ++ ultohstr(mtd_info->chipsize), ++ mtd_info->numchips); ++ ++ if (*cmd == 'n') { ++ printf("Page:%sB ", ultohstr(mtd_info->pagesize)); ++ printf("OOB:%sB ", ultohstr(mtd_info->oobsize)); ++#if defined(CONFIG_FMC_SPI_NAND) || defined(CONFIG_FMC_NAND) ++#ifdef CONFIG_CMD_NAND ++ printf("ECC:%s ", nand_ecc_name(mtd_info->ecctype)); ++#endif ++#else ++ printf("ECC:%s ", get_ecctype_str(mtd_info->ecctype & 0x7)); ++#endif ++ } ++ printf("\nID:"); ++ ++ len = (mtd_info->id_length > 8 ? 8 : mtd_info->id_length); /* 8:len of max id */ ++ for (ix = 0; ix < len; ix++) ++ printf("0x%02X ", mtd_info->ids[ix]); ++ printf("\nName:\"%s\"\n", mtd_info->name); ++ } ++ ++ return 0; ++} ++#endif ++ ++static int do_getinfo(cmd_tbl_t *cmdtp, int flag, int argc, char* const argv[]) ++{ ++ char *cmd = NULL; ++ ++ if (argc < 2) { /* 2:len of argv */ ++ cmd_usage(cmdtp); ++ return -1; ++ } ++ ++ cmd = argv[1]; ++ if (strcmp(cmd, "bootmode") == 0) { ++ switch (get_boot_media()) { ++ default: ++ case BOOT_MEDIA_UNKNOWN: ++ printf("Boot from unknown device," ++ " please check your hardware config.\n"); ++ return -1; ++ ++ case BOOT_MEDIA_NAND: ++ printf("nand\n"); ++ break; ++ ++ case BOOT_MEDIA_SPIFLASH: ++ printf("spi\n"); ++ break; ++ ++ case BOOT_MEDIA_EMMC: ++ printf("emmc\n"); ++ break; ++ ++ case BOOT_MEDIA_UFS: ++ printf("ufs\n"); ++ break; ++ } ++ return 0; ++ } ++ ++ if (strcmp(cmd, "version") == 0) { ++ printf("version: %s\n", U_BOOT_VERSION); ++ return 0; ++ } ++ ++#if defined(CONFIG_FMC_SPI_NAND) || defined(CONFIG_FMC_NAND) ++ if (strcmp(cmd, "nand") == 0) { ++ struct mtd_info_ex *mtd_info = NULL; ++ mtd_info = get_nand_info(); ++ return print_mtd_info(mtd_info, cmd); ++ } ++#endif ++ ++#ifdef CONFIG_FMC_SPI_NOR ++ if (strcmp(cmd, "spi") == 0) { ++ struct mtd_info_ex *mtd_info = NULL; ++ mtd_info = get_spiflash_info(); ++ return print_mtd_info(mtd_info, cmd); ++ } ++#endif ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ getinfo, CONFIG_SYS_MAXARGS, 1, do_getinfo, ++ "print hardware information", ++ "bootmode - get start memeory type e.g. nand/spi etc\n" ++ "getinfo nand - get nand flash information\n" ++ "getinfo spi - get spi flash information\n" ++ "getinfo version - get system version\n" ++); +diff --git a/common/Kconfig b/common/Kconfig +index a7c5ba2..0b84d05 100644 +--- a/common/Kconfig ++++ b/common/Kconfig +@@ -368,6 +368,58 @@ config BOOTDELAY + + See doc/README.autoboot for details. + ++menu "vendor_setup" ++ ++config VENDOR_MC ++ bool "mc platform solution" ++ default n ++ help ++ support for mc platform solution ++ ++config VENDOR_SPIFLASH_SPEED ++ bool "spinor and spinand speed " ++ default n ++ depends on FMC_SPI_NAND || FMC_SPI_NOR || FMC_NAND ++ help ++ Support for flash speed testing. ++ ++config VENDOR_UPGRADE_BY_SEGMENT ++ bool "Upgrade by segment write" ++ default n ++ help ++ Support for upgrade by segment,this option is used only when the memory ++ is small and the upgrade file is large. If you are not sure, select n by default. ++ ++config BSP_DISABLE_CONSOLE ++ bool "disable console" ++ default n ++ help ++ The console in uboot is not secure. In formal commercial products, ++ the input and output of the console should be turned off to avoid being attacked. ++ Only during debugging, enable the input and output functions of the console. ++ ++config BSP_DISABLE_DOWNLOAD ++ bool "disable chip download" ++ default n ++ help ++ The bare chip download programming function and the network download programming ++ function are not safe in a commercial environment and may be used by hackers. ++ Therefore, it needs to be disabled in the official product. ++ ++config DELAY_ENVIRONMENT ++ bool "delay load environment" ++ default n ++ help ++ Normally the environment is loaded from flash when the board is ++ initialised so that it is available to U-Boot. This inhibits ++ that so that the environment in flash is not available until ++ explicitly loaded later by U-Boot code and U-Boot will use the ++ default environment which defined in code. ++ It is recommended to set following configs if open DELAY_ENVIRONMENT: ++ USE_BOOTARGS and BOOTARGS and USE_BOOTCOMMAND and BOOTCOMMAND ++ ++endmenu ++ + config USE_BOOTARGS + bool "Enable boot arguments" + help +@@ -810,6 +862,12 @@ config DEFAULT_FDT_FILE + string "Default fdt file" + help + This option is used to set the default fdt file to boot OS. ++config KERNEL_LOAD_ADDR ++ hex "Set kernel load address" ++ default 0x42080000 ++ help ++ Set the address of kernel to be loaded, because the single and ++ big-little kernel should be loaded at diffrent address. + + config MISC_INIT_R + bool "Execute Misc Init" +@@ -956,12 +1014,21 @@ config SPL_HASH + + config TPL_HASH + bool # "Support hashing API (SHA1, SHA256, etc.)" ++ default n if VENDOR_MC + help + This provides a way to hash data in memory using various supported + algorithms (such as SHA1, MD5, CRC32). The API is defined in hash.h + and the algorithms it supports are defined in common/hash.c. See + also CMD_HASH for command-line access. + ++config SECURE_BOOT_SUPPORT ++ bool "Support secure boot" ++ default n ++ help ++ This provides a way to verify kernel and other images in U-boot. ++ This config depends on CONFIG_CIPHER_ENABLE. ++ See include/configs/${chip_type}.h to check if define CONFIG_CIPHER_ENABLE. ++ + endmenu + + menu "Update support" +diff --git a/common/Makefile b/common/Makefile +index 302d8be..129ce98 100644 +--- a/common/Makefile ++++ b/common/Makefile +@@ -113,6 +113,7 @@ obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o + obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o + obj-$(CONFIG_$(SPL_)MULTI_DTB_FIT) += boot_fit.o common_fit.o + obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-sig.o ++obj-$(CONFIG_$(SPL_TPL_)FIT_CIPHER) += image-cipher.o + obj-$(CONFIG_IO_TRACE) += iotrace.o + obj-y += memsize.o + obj-y += stdio.o +@@ -135,3 +136,8 @@ obj-$(CONFIG_CMD_LOADB) += xyzModem.o + obj-$(CONFIG_$(SPL_TPL_)YMODEM_SUPPORT) += xyzModem.o + + obj-$(CONFIG_AVB_VERIFY) += avb_verify.o ++ifndef CONFIG_BSP_DISABLE_DOWNLOAD ++obj-y += download_process.o ++endif ++obj-$(CONFIG_ARM64_SUPPORT_LOAD_FIP) += load_fip.o ++obj-y += flash_read.o +diff --git a/common/board_f.c b/common/board_f.c +index d66afb3..c117361 100644 +--- a/common/board_f.c ++++ b/common/board_f.c +@@ -93,6 +93,10 @@ __weak void blue_led_off(void) {} + * literal pool we get on ARM. Or perhaps just encourage each module to use + * a structure... + */ ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++extern int config_qos_registers(void); ++extern int config_pi_defense_registers(void); ++#endif + + #if defined(CONFIG_WATCHDOG) || defined(CONFIG_HW_WATCHDOG) + static int init_func_watchdog_init(void) +@@ -228,9 +232,9 @@ static int show_dram_config(void) + size = gd->ram_size; + #endif + +- print_size(size, ""); ++/* print_size(size, ""); + board_add_ram_info(0); +- putc('\n'); ++ putc('\n'); */ + + return 0; + } +@@ -746,11 +750,12 @@ static int setup_reloc(void) + #endif + memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); + +- debug("Relocation Offset is: %08lx\n", gd->reloc_off); +- debug("Relocating to %08lx, new gd at %08lx, sp at %08lx\n", ++#ifndef CONFIG_BSP_DISABLE_CONSOLE ++ printf("Relocation Offset is: %08lx\n", gd->reloc_off); ++ printf("Relocating to %08lx, new gd at %08lx, sp at %08lx\n", + gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd), + gd->start_addr_sp); +- ++#endif + return 0; + } + +@@ -938,7 +943,6 @@ static const init_fnc_t init_sequence_f[] = { + testdram, + #endif /* CONFIG_SYS_DRAM_TEST */ + INIT_FUNC_WATCHDOG_RESET +- + #ifdef CONFIG_POST + init_post, + #endif +@@ -1006,6 +1010,11 @@ static const init_fnc_t init_sequence_f[] = { + !CONFIG_IS_ENABLED(X86_64) + jump_to_copy, + #endif ++ ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++ config_qos_registers, ++ config_pi_defense_registers, ++#endif + NULL, + }; + +@@ -1016,7 +1025,6 @@ void board_init_f(ulong boot_flags) + + if (initcall_run_list(init_sequence_f)) + hang(); +- + #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \ + !defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64) && \ + !defined(CONFIG_ARC) +diff --git a/common/board_r.c b/common/board_r.c +index 5464172..2bff6d9 100644 +--- a/common/board_r.c ++++ b/common/board_r.c +@@ -18,6 +18,7 @@ + #if defined(CONFIG_CMD_BEDBUG) + #include + #endif ++#include + #include + #include + #include +@@ -36,6 +37,7 @@ + #include + #endif + #include ++#include + #include + #include + #include +@@ -59,6 +61,10 @@ + #include + #endif + ++#ifdef CONFIG_CMD_SF ++#include ++#endif ++ + DECLARE_GLOBAL_DATA_PTR; + + ulong monitor_flash_len; +@@ -104,7 +110,6 @@ static int initr_reloc(void) + { + /* tell others: relocation done */ + gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT; +- + return 0; + } + +@@ -347,6 +352,14 @@ static int initr_manual_reloc_cmdtable(void) + } + #endif + ++static int initr_binman(void) ++{ ++ if (!CONFIG_IS_ENABLED(BINMAN_FDT)) ++ return 0; ++ ++ return binman_init(); ++} ++ + #if defined(CONFIG_MTD_NOR_FLASH) + static int initr_flash(void) + { +@@ -399,6 +412,31 @@ static int initr_flash(void) + } + #endif + ++#ifdef CONFIG_CMD_SF ++ ++#ifndef CONFIG_ENV_SPI_BUS ++# define CONFIG_ENV_SPI_BUS 0 ++#endif ++#ifndef CONFIG_ENV_SPI_CS ++# define CONFIG_ENV_SPI_CS 0 ++#endif ++#ifndef CONFIG_ENV_SPI_MAX_HZ ++# define CONFIG_ENV_SPI_MAX_HZ 1000000 ++#endif ++#ifndef CONFIG_ENV_SPI_MODE ++# define CONFIG_ENV_SPI_MODE SPI_MODE_3 ++#endif ++ ++/* go init the SPI Nor */ ++static int initr_snor(void) ++{ ++ puts("SPI Nor: "); ++ spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, ++ CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); ++ return 0; ++} ++#endif /* CONFIG_CMD_SF */ ++ + #ifdef CONFIG_CMD_NAND + /* go init the NAND */ + static int initr_nand(void) +@@ -410,6 +448,15 @@ static int initr_nand(void) + } + #endif + ++#ifdef CONFIG_GENERIC_UFS ++static int initr_ufs(void) ++{ ++ puts("UFS: "); ++ ufs_storage_init(); ++ return 0; ++} ++#endif ++ + #if defined(CONFIG_CMD_ONENAND) + /* go init the NAND */ + static int initr_onenand(void) +@@ -633,6 +680,14 @@ static int initr_bedbug(void) + } + #endif + ++static int initr_download(void) ++{ ++#ifndef CONFIG_BSP_DISABLE_DOWNLOAD ++ extern void download_boot(const int (*handle)(void)); ++ download_boot(NULL); ++#endif ++ return 0; ++} + static int run_main_loop(void) + { + #ifdef CONFIG_SANDBOX +@@ -697,6 +752,7 @@ static init_fnc_t init_sequence_r[] = { + #ifdef CONFIG_EFI_LOADER + efi_memory_init, + #endif ++ initr_binman, + stdio_init_tables, + initr_serial, + initr_announce, +@@ -740,6 +796,9 @@ static init_fnc_t init_sequence_r[] = { + /* initialize higher level parts of CPU like time base and timers */ + cpu_init_r, + #endif ++#ifdef CONFIG_CMD_SF ++ initr_snor, ++#endif + #ifdef CONFIG_CMD_NAND + initr_nand, + #endif +@@ -748,6 +807,9 @@ static init_fnc_t init_sequence_r[] = { + #endif + #ifdef CONFIG_MMC + initr_mmc, ++#endif ++#ifdef CONFIG_GENERIC_UFS ++ initr_ufs, + #endif + initr_env, + #ifdef CONFIG_SYS_BOOTPARAMS_LEN +@@ -838,6 +900,8 @@ static init_fnc_t init_sequence_r[] = { + #if defined(CONFIG_PRAM) + initr_mem, + #endif ++ initr_download, ++ + run_main_loop, + }; + +diff --git a/common/bootm.c b/common/bootm.c +index 902c138..db4362a 100644 +--- a/common/bootm.c ++++ b/common/bootm.c +@@ -819,7 +819,8 @@ void __weak switch_to_non_secure_mode(void) + #else /* USE_HOSTCC */ + + #if defined(CONFIG_FIT_SIGNATURE) +-static int bootm_host_load_image(const void *fit, int req_image_type) ++static int bootm_host_load_image(const void *fit, int req_image_type, ++ int cfg_noffset) + { + const char *fit_uname_config = NULL; + ulong data, len; +@@ -831,6 +832,7 @@ static int bootm_host_load_image(const void *fit, int req_image_type) + void *load_buf; + int ret; + ++ fit_uname_config = fdt_get_name(fit, cfg_noffset, NULL); + memset(&images, '\0', sizeof(images)); + images.verify = 1; + noffset = fit_image_load(&images, (ulong)fit, +@@ -878,7 +880,7 @@ int bootm_host_load_images(const void *fit, int cfg_noffset) + for (i = 0; i < ARRAY_SIZE(image_types); i++) { + int ret; + +- ret = bootm_host_load_image(fit, image_types[i]); ++ ret = bootm_host_load_image(fit, image_types[i], cfg_noffset); + if (!err && ret && ret != -ENOENT) + err = ret; + } +diff --git a/common/cli_hush.c b/common/cli_hush.c +index cf1e273..5205da6 100644 +--- a/common/cli_hush.c ++++ b/common/cli_hush.c +@@ -1042,6 +1042,8 @@ static void get_user_input(struct in_str *i) + if (n == -1 ) { + flag_repeat = 0; + i->__promptme = 0; ++ } else if (n == -3) { ++ flag_repeat = 0; + } + n = strlen(console_buffer); + console_buffer[n] = '\n'; +diff --git a/common/cli_readline.c b/common/cli_readline.c +index 6ef7a3e..15633ab 100644 +--- a/common/cli_readline.c ++++ b/common/cli_readline.c +@@ -500,6 +500,9 @@ static int cread_line(const char *const prompt, char *buf, unsigned int *len, + *len = eol_num; + buf[eol_num] = '\0'; /* lose the newline */ + ++ if (buf[0] == '\0') { ++ return -3; ++ } + if (buf[0] && buf[0] != CREAD_HIST_CHAR) + cread_add_to_hist(buf); + hist_cur = hist_add_idx; +diff --git a/common/console.c b/common/console.c +index 168ba60..137fca4 100644 +--- a/common/console.c ++++ b/common/console.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + DECLARE_GLOBAL_DATA_PTR; + +@@ -987,4 +988,21 @@ int console_init_r(void) + return 0; + } + ++void print_to_tool(const char *fmt, ...) ++{ ++ va_list args; ++ char printbuffer[CONFIG_SYS_PBSIZE]; ++ ++ va_start(args, fmt); ++ ++ /* For this to work, printbuffer must be larger than ++ * anything we ever want to print. ++ */ ++ vsprintf(printbuffer, fmt, args); ++ va_end(args); ++ ++ /* Print the string */ ++ serial_puts_to_tool(printbuffer); ++} ++ + #endif /* CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) */ +diff --git a/common/download_process.c b/common/download_process.c +new file mode 100644 +index 0000000..9d8750e +--- /dev/null ++++ b/common/download_process.c +@@ -0,0 +1,227 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef DUMP_SWITCH ++static void dump_buf(char *buf, int len) ++{ ++ int i; ++ char *p = buf; ++ printf("%s->%d, buf=0x%.8x, len=%d\n", ++ __FUNCTION__, __LINE__, ++ (int)(buf), (int)(len)); ++ for (i = 0; i < (len); i++) { ++ printf("0x%.2x ", *(p + i)); ++ if (!((i + 1) & 0x07)) ++ printf("\n"); ++ } ++ printf("\n"); ++} ++#endif ++ ++#define XHEAD 0xAB ++#define XCMD 0xCD ++#define ACK 0xAA /* ACK VALUE */ ++#define NAK 0x55 /* NAK VALUE */ ++ ++#define START_FRAME_LEN 5 ++#define MAX_BUFF_SIZE 1024 ++#define MAX_SEND_SIZE 20 ++ ++static char recv_buf[MAX_BUFF_SIZE]; ++ ++static unsigned short calc_crc16(const unsigned char *packet, unsigned long length) ++{ ++ unsigned short crc16 = 0; ++ unsigned long i; ++ ++ const unsigned short *crc16_table = get_crc16_table(); ++ if (crc16_table == NULL) { ++ return 0; ++ } ++ ++ for (i = 0; i < length; i++) ++ crc16 = ((crc16 << 8) | /* fetches High or low 8 bit */ ++ packet[i]) ^ crc16_table[(crc16 >> 8) & 0xFF]; ++ ++ for (i = 0; i < 2; i++) /* 2: Cycle and fetches High or low 8 bit */ ++ crc16 = ((crc16 << 8) | 0) ^ crc16_table[(crc16 >> 8) & 0xFF]; ++ ++ return crc16; ++} ++ ++static int recv_byte(void) ++{ ++ if (serial_tstc()) ++ return serial_getc(); ++ ++ return -1; ++} ++ ++static char recv_data(void) ++{ ++ int ret = -1; ++ ++ while (ret == -1) ++ ret = recv_byte(); ++ ++ return (char)ret; ++} ++ ++void download_process(void) ++{ ++ int i; ++ int cr; ++ int ret; ++ unsigned int head_frame_len; ++ unsigned int cmd_len = 0; ++ unsigned char send_buf[MAX_SEND_SIZE] = {0}; ++ unsigned short cksum; ++ ++ while (1) { ++retry: ++ cr = recv_byte(); ++ if (cr == -1) ++ goto retry; ++ ++ if (cr == XHEAD) { ++ head_frame_len = START_FRAME_LEN; ++ recv_buf[0] = (char)cr; ++ ++ /* RECV: head frame */ ++ for (i = 0; i < (head_frame_len - 1); i++) ++ recv_buf[i + 1] = recv_data(); ++ ++ /* crc check 3 length */ ++ cksum = calc_crc16((unsigned char *)recv_buf, 3); ++ /* The most significant 8 bits of array 3 and array 4 are used. */ ++ if (cksum == (((unsigned char)recv_buf[3] << 8) | (unsigned char)recv_buf[4])) { ++ /* init, The most significant 8 bits of array 1 and array 2 Plus 3. */ ++ cmd_len = (((unsigned char)recv_buf[1] << 8) | (unsigned char)recv_buf[2]) + 3; ++ ++ /* SEND: ack */ ++ send_buf[0] = ACK; ++ } else { ++ /* init */ ++ cmd_len = 0; ++ ++ /* SEND: nak */ ++ send_buf[0] = NAK; ++ } ++ serial_putc(send_buf[0]); ++ } else if (cr == XCMD) { ++ recv_buf[0] = (char)cr; ++ ++ if (cmd_len > MAX_BUFF_SIZE) ++ goto retry; ++ ++ /* RECV: cmd data */ ++ for (i = 0; i < (cmd_len - 1); i++) ++ recv_buf[i + 1] = recv_data(); ++ ++ /* crc check cmd_len subtract 2 length */ ++ cksum = calc_crc16((unsigned char *)recv_buf, cmd_len - 2); ++ /* cmd_len subtract 2 length significant 8 bits */ ++ if (cksum == (((unsigned char)(recv_buf[cmd_len - 2]) << 8) | ++ (unsigned char)recv_buf[cmd_len - 1])) { ++ /* SEND: ack wait result */ ++ send_buf[0] = ACK; ++ serial_putc(send_buf[0]); ++ } else { ++ (void)memset_s(recv_buf, sizeof(recv_buf), 0, sizeof(recv_buf)); ++ /* SEND: nak */ ++ send_buf[0] = NAK; ++ serial_putc(send_buf[0]); ++ ++ goto retry; ++ } ++ ++ /* clean crc */ ++ recv_buf[cmd_len - 1] = 0; ++ /* cmd_len subtract 2 length */ ++ recv_buf[cmd_len - 2] = 0; ++ ++ /* cmd process */ ++ ret = run_command((recv_buf + 1), 0); ++ if (ret) ++ /* SEND: end flag */ ++ serial_puts("[EOT](ERROR)\n"); ++ else ++ /* SEND: end flag */ ++ serial_puts("[EOT](OK)\n"); ++ } else { ++ /* flush fifo */ ++ } ++ } ++} ++ ++__attribute__((weak)) int check_update_button(void) ++{ ++ return 0; ++} ++ ++__attribute__((weak)) void download_boot(const int (*handle)(void)) ++{ ++ if (check_update_button()) { ++ writel(START_MAGIC, REG_START_FLAG); ++ writel(SELF_BOOT_TYPE_USBDEV, SYS_CTRL_REG_BASE + REG_SC_GEN9); ++ } ++ ++ if ((*(volatile unsigned int *)(REG_START_FLAG)) != START_MAGIC) { ++ goto out; ++ } ++ ++ /* note: uart output is enable again for download */ ++ serial_enable_output(true); ++ ++ /* clear flag */ ++ *(volatile unsigned int *)(REG_START_FLAG) = 0; ++ ++ serial_puts("start download process.\n"); ++ ++ /* wait cmd from pc */ ++ for (;;) { ++ if ((*(volatile unsigned int *)(SYS_CTRL_REG_BASE + REG_SC_GEN9)) == ++ SELF_BOOT_TYPE_USBDEV) { ++#ifdef CONFIG_USB_GADGET ++ if (udc_connect() < 0) { ++ printf("[error] download_boot by usb error: init error"); ++ break; ++ } ++#else ++ printf("[error] download_boot by usb is not supported.\n"); ++#endif ++ } else { ++ download_process(); ++ } ++ } ++ ++ /* note: uart output is disable again */ ++ serial_enable_output(false); ++out: ++ if (handle) ++ handle(); ++} +diff --git a/common/flash_read.c b/common/flash_read.c +new file mode 100644 +index 0000000..a40e1df +--- /dev/null ++++ b/common/flash_read.c +@@ -0,0 +1,180 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "flash_read.h" ++#include ++#if defined (CONFIG_FMC_SPI_NOR) ++#include ++#endif ++#if defined (CONFIG_FMC_SPI_NAND) ++#include ++#endif ++#if defined(CONFIG_SUPPORT_EMMC_BOOT) ++#include ++#endif ++ ++/* ++ * read from spi nor flash by offset and size. ++ */ ++#if defined (CONFIG_FMC_SPI_NOR) ++static int read_flash_spi(unsigned long offset, unsigned int size, unsigned char *addr) ++{ ++ /* init env_flash */ ++ struct spi_flash *env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, ++ CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); ++ if (env_flash == NULL) { ++ printf("read_flash_spi spi_flash_probe failed\n"); ++ return -1; ++ } ++ ++ /* read flash */ ++ int ret = spi_flash_read(env_flash, offset, size, addr); ++ if (ret != 0) { ++ printf("read_flash_spi spi_flash_read failed\n"); ++ goto EXIT_FAILURE; ++ } ++ ++EXIT_FAILURE: ++ spi_flash_free(env_flash); ++ return ret; ++} ++#else ++static int read_flash_spi(unsigned long offset, unsigned int size, unsigned char *addr) ++{ ++ printf("Error: read_flash_spi not define CONFIG_CMD_SF\n"); ++ return -1; ++} ++#endif ++ ++/* ++ * read from nand flash by offset and size. ++ */ ++#if defined (CONFIG_FMC_SPI_NAND) ++static int read_flash_nand(unsigned long offset, unsigned int size, unsigned char *addr) ++{ ++ struct mtd_info *mtd; ++ size_t rwsize = size; ++ size_t block_size; ++ int ret; ++ ++ mtd = nand_info[0]; ++ if (mtd == NULL) { ++ printf("read_flash_nand mtd == NULL\n"); ++ return -1; ++ } ++ block_size = mtd->erasesize; ++ if (nand_block_isbad(mtd, offset)) ++ offset += block_size; ++ ++ ret = nand_read_skip_bad(mtd, offset, &rwsize, NULL, mtd->size, addr); ++ return ret; ++} ++#else ++static int read_flash_nand(unsigned long offset, unsigned int size, unsigned char *addr) ++{ ++ printf("Error: read_flash_nand not define CONFIG_CMD_NAND\n"); ++ return -1; ++} ++#endif ++ ++/* ++ * read from emmc flash by offset and size. ++ */ ++#if defined (CONFIG_SUPPORT_EMMC_BOOT) ++static int read_flash_mmc(unsigned long offset, unsigned int size, unsigned char *addr) ++{ ++ struct mmc *mmc = find_mmc_device(0); ++ if (mmc == NULL) { ++ printf("read_flash_mmc find_mmc_device failed\n"); ++ return -1; ++ } ++ ++ lbaint_t blks; ++ if ((size % MMC_MAX_BLOCK_LEN) != 0) ++ blks = size / MMC_MAX_BLOCK_LEN + 1; ++ else ++ blks = size / MMC_MAX_BLOCK_LEN; ++ ++ ulong blks_read = blk_dread(mmc_get_blk_desc(mmc), (offset / MMC_MAX_BLOCK_LEN), blks, (void *)addr); ++ if (blks_read != blks) { ++ printf("mmc blk read failed:%lu.\n", blks_read); ++ return -1; ++ } ++ ++ return 0; ++} ++#else ++static int read_flash_mmc(unsigned long offset, unsigned int size, unsigned char *addr) ++{ ++ printf("Error: read_flash_mmc not define CONFIG_CMD_NAND\n"); ++ return -1; ++} ++#endif ++ ++/* ++ * read flash into out_addr from offset. ++ */ ++int flash_read(unsigned long offset, unsigned int size, unsigned char *out_addr) ++{ ++ if ((offset % FLASH_ALIGNED_SIZE) != 0) { ++ printf("offset should aligned with %d.\n", FLASH_ALIGNED_SIZE); ++ return -1; ++ } ++ ++ if (out_addr == NULL) { ++ return -1; ++ } ++ ++ int (*read_flash_choose)(unsigned long offset, unsigned int size, unsigned char* addr) = NULL; ++ ++ switch (get_boot_media()) { ++ case BOOT_MEDIA_EMMC: ++ read_flash_choose = read_flash_mmc; ++ break; ++ case BOOT_MEDIA_NAND: ++ read_flash_choose = read_flash_nand; ++ break; ++ case BOOT_MEDIA_SPIFLASH: ++ read_flash_choose = read_flash_spi; ++ break; ++ default: ++ break; ++ } ++ ++ if (read_flash_choose == NULL) { ++ printf("flash_read not found flash type!\n"); ++ return -1; ++ } ++ return read_flash_choose(offset, size, out_addr); ++} ++ ++int flash_read_aligned(unsigned long offset, unsigned int size, unsigned char *out_addr) ++{ ++ if (out_addr == NULL) { ++ printf("out_addr is null.\n"); ++ return -1; ++ } ++ ++ if ((size % FLASH_ALIGNED_SIZE) != 0) { ++ printf("size should aligned with %d.\n", FLASH_ALIGNED_SIZE); ++ return -1; ++ } ++ ++ return flash_read(offset, size, out_addr); ++} +diff --git a/common/image-cipher.c b/common/image-cipher.c +new file mode 100644 +index 0000000..3abcf4a +--- /dev/null ++++ b/common/image-cipher.c +@@ -0,0 +1,182 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifdef USE_HOSTCC ++#include "mkimage.h" ++#include ++#else ++#include ++#include ++DECLARE_GLOBAL_DATA_PTR; ++#endif /* !USE_HOSTCC*/ ++#include ++#include ++#include ++#include ++ ++struct cipher_algo cipher_algos[] = { ++ { ++ .name = "aes128", ++ .key_len = AES128_KEY_LENGTH, ++ .iv_len = AES_BLOCK_LENGTH, ++#if IMAGE_ENABLE_ENCRYPT ++ .calculate_type = EVP_aes_128_cbc, ++#endif ++ .encrypt = image_aes_encrypt, ++ .decrypt = image_aes_decrypt, ++ .add_cipher_data = image_aes_add_cipher_data ++ }, ++ { ++ .name = "aes192", ++ .key_len = AES192_KEY_LENGTH, ++ .iv_len = AES_BLOCK_LENGTH, ++#if IMAGE_ENABLE_ENCRYPT ++ .calculate_type = EVP_aes_192_cbc, ++#endif ++ .encrypt = image_aes_encrypt, ++ .decrypt = image_aes_decrypt, ++ .add_cipher_data = image_aes_add_cipher_data ++ }, ++ { ++ .name = "aes256", ++ .key_len = AES256_KEY_LENGTH, ++ .iv_len = AES_BLOCK_LENGTH, ++#if IMAGE_ENABLE_ENCRYPT ++ .calculate_type = EVP_aes_256_cbc, ++#endif ++ .encrypt = image_aes_encrypt, ++ .decrypt = image_aes_decrypt, ++ .add_cipher_data = image_aes_add_cipher_data ++ } ++}; ++ ++struct cipher_algo *image_get_cipher_algo(const char *full_name) ++{ ++ int i; ++ const char *name; ++ ++ for (i = 0; i < ARRAY_SIZE(cipher_algos); i++) { ++ name = cipher_algos[i].name; ++ if (!strncmp(name, full_name, strlen(name))) ++ return &cipher_algos[i]; ++ } ++ ++ return NULL; ++} ++ ++static int fit_image_setup_decrypt(struct image_cipher_info *info, ++ const void *fit, int image_noffset, ++ int cipher_noffset) ++{ ++ const void *fdt = gd_fdt_blob(); ++ const char *node_name; ++ char node_path[128]; ++ int noffset; ++ char *algo_name; ++ int ret; ++ ++ node_name = fit_get_name(fit, image_noffset, NULL); ++ if (!node_name) { ++ printf("Can't get node name\n"); ++ return -1; ++ } ++ ++ if (fit_image_cipher_get_algo(fit, cipher_noffset, &algo_name)) { ++ printf("Can't get algo name for cipher '%s' in image '%s'\n", ++ node_name, node_name); ++ return -1; ++ } ++ ++ info->keyname = fdt_getprop(fit, cipher_noffset, FIT_KEY_HINT, NULL); ++ if (!info->keyname) { ++ printf("Can't get key name\n"); ++ return -1; ++ } ++ ++ info->ivname = fdt_getprop(fit, cipher_noffset, "iv-name-hint", NULL); ++ if (!info->ivname) { ++ printf("Can't get IV name\n"); ++ return -1; ++ } ++ ++ info->fit = fit; ++ info->node_noffset = image_noffset; ++ info->name = algo_name; ++ info->cipher = image_get_cipher_algo(algo_name); ++ if (!info->cipher) { ++ printf("Can't get cipher\n"); ++ return -1; ++ } ++ ++ ret = fit_image_get_data_size_unciphered(fit, image_noffset, ++ &info->size_unciphered); ++ if (ret) { ++ printf("Can't get size of unciphered data\n"); ++ return -1; ++ } ++ ++ /* ++ * Search the cipher node in the u-boot fdt ++ * the path should be: /cipher/key--- ++ */ ++ snprintf_s(node_path, sizeof(node_path), sizeof(node_path), "/%s/key-%s-%s-%s", ++ FIT_CIPHER_NODENAME, algo_name, info->keyname, info->ivname); ++ ++ noffset = fdt_path_offset(fdt, node_path); ++ if (noffset < 0) { ++ printf("Can't found cipher node offset\n"); ++ return -1; ++ } ++ ++ /* read key */ ++ info->key = fdt_getprop(fdt, noffset, "key", NULL); ++ if (!info->key) { ++ printf("Can't get key in cipher node '%s'\n", node_path); ++ return -1; ++ } ++ ++ /* read iv */ ++ info->iv = fdt_getprop(fdt, noffset, "iv", NULL); ++ if (!info->iv) { ++ printf("Can't get IV in cipher node '%s'\n", node_path); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int fit_image_decrypt_data(const void *fit, ++ int image_noffset, int cipher_noffset, ++ const void *data_ciphered, size_t size_ciphered, ++ void **data_unciphered, size_t *size_unciphered) ++{ ++ struct image_cipher_info info; ++ int ret; ++ ++ ret = fit_image_setup_decrypt(&info, fit, image_noffset, ++ cipher_noffset); ++ if (ret < 0) ++ goto out; ++ ++ ret = info.cipher->decrypt(&info, data_ciphered, size_ciphered, ++ data_unciphered, size_unciphered); ++ ++ out: ++ return ret; ++} +diff --git a/common/image-fit.c b/common/image-fit.c +index c52f945..35b93ff 100644 +--- a/common/image-fit.c ++++ b/common/image-fit.c +@@ -26,6 +26,7 @@ DECLARE_GLOBAL_DATA_PTR; + #include + #include + #include ++#include + #include + #include + #include +@@ -168,7 +169,7 @@ static void fit_image_print_data(const void *fit, int noffset, const char *p, + int value_len; + char *algo; + const char *padding; +- int required; ++ bool required; + int ret, i; + + debug("%s %s node: '%s'\n", p, type, +@@ -179,8 +180,8 @@ static void fit_image_print_data(const void *fit, int noffset, const char *p, + return; + } + printf("%s", algo); +- keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); +- required = fdt_getprop(fit, noffset, "required", NULL) != NULL; ++ keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); ++ required = fdt_getprop(fit, noffset, FIT_KEY_REQUIRED, NULL) != NULL; + if (keyname) + printf(":%s", keyname); + if (required) +@@ -947,6 +948,31 @@ int fit_image_get_data_size(const void *fit, int noffset, int *data_size) + return 0; + } + ++/** ++ * Get 'data-size-unciphered' property from a given image node. ++ * ++ * @fit: pointer to the FIT image header ++ * @noffset: component image node offset ++ * @data_size: holds the data-size property ++ * ++ * returns: ++ * 0, on success ++ * -ENOENT if the property could not be found ++ */ ++int fit_image_get_data_size_unciphered(const void *fit, int noffset, ++ size_t *data_size) ++{ ++ const fdt32_t *val; ++ ++ val = fdt_getprop(fit, noffset, "data-size-unciphered", NULL); ++ if (!val) ++ return -ENOENT; ++ ++ *data_size = (size_t)fdt32_to_cpu(*val); ++ ++ return 0; ++} ++ + /** + * fit_image_get_data_and_size - get data and its size including + * both embedded and external data +@@ -1080,6 +1106,33 @@ static int fit_image_hash_get_ignore(const void *fit, int noffset, int *ignore) + return 0; + } + ++/** ++ * fit_image_cipher_get_algo - get cipher algorithm name ++ * @fit: pointer to the FIT format image header ++ * @noffset: cipher node offset ++ * @algo: double pointer to char, will hold pointer to the algorithm name ++ * ++ * fit_image_cipher_get_algo() finds cipher algorithm property in a given ++ * cipher node. If the property is found its data start address is returned ++ * to the caller. ++ * ++ * returns: ++ * 0, on success ++ * -1, on failure ++ */ ++int fit_image_cipher_get_algo(const void *fit, int noffset, char **algo) ++{ ++ int len; ++ ++ *algo = (char *)fdt_getprop(fit, noffset, FIT_ALGO_PROP, &len); ++ if (!*algo) { ++ fit_get_debug(fit, noffset, FIT_ALGO_PROP, len); ++ return -1; ++ } ++ ++ return 0; ++} ++ + ulong fit_get_end(const void *fit) + { + return map_to_sysmem((void *)(fit + fdt_totalsize(fit))); +@@ -1143,16 +1196,31 @@ int calculate_hash(const void *data, int data_len, const char *algo, + *((uint32_t *)value) = cpu_to_uimage(*((uint32_t *)value)); + *value_len = 4; + } else if (IMAGE_ENABLE_SHA1 && strcmp(algo, "sha1") == 0) { ++#ifdef CONFIG_SHA1 + sha1_csum_wd((unsigned char *)data, data_len, +- (unsigned char *)value, CHUNKSZ_SHA1); ++ (unsigned char *)value, CHUNKSZ_SHA1); + *value_len = 20; ++#else ++ debug("Unsupported hash sha1 alogrithm\n"); ++ return -1; ++#endif + } else if (IMAGE_ENABLE_SHA256 && strcmp(algo, "sha256") == 0) { ++#ifdef CONFIG_SHA256 + sha256_csum_wd((unsigned char *)data, data_len, + (unsigned char *)value, CHUNKSZ_SHA256); + *value_len = SHA256_SUM_LEN; ++#else ++ debug("Unsupported hash sha256 alogrithm\n"); ++ return -1; ++#endif + } else if (IMAGE_ENABLE_MD5 && strcmp(algo, "md5") == 0) { ++#ifdef CONFIG_MD5 + md5_wd((unsigned char *)data, data_len, value, CHUNKSZ_MD5); + *value_len = 16; ++#else ++ debug("Unsupported hash mad5 alogrithm\n"); ++ return -1; ++#endif + } else { + debug("Unsupported hash alogrithm\n"); + return -1; +@@ -1287,21 +1355,31 @@ error: + */ + int fit_image_verify(const void *fit, int image_noffset) + { ++ const char *name = fit_get_name(fit, image_noffset, NULL); + const void *data; + size_t size; +- int noffset = 0; + char *err_msg = ""; + ++ if (strchr(name, '@')) { ++ /* ++ * We don't support this since libfdt considers names with the ++ * name root but different @ suffix to be equal ++ */ ++ err_msg = "Node name contains @"; ++ goto err; ++ } + /* Get image data and data length */ + if (fit_image_get_data_and_size(fit, image_noffset, &data, &size)) { + err_msg = "Can't get image data/size"; +- printf("error!\n%s for '%s' hash node in '%s' image node\n", +- err_msg, fit_get_name(fit, noffset, NULL), +- fit_get_name(fit, image_noffset, NULL)); +- return 0; ++ goto err; + } + + return fit_image_verify_with_data(fit, image_noffset, data, size); ++ ++err: ++ printf("error!\n%s in '%s' image node\n", err_msg, ++ fit_get_name(fit, image_noffset, NULL)); ++ return 0; + } + + /** +@@ -1354,6 +1432,32 @@ int fit_all_image_verify(const void *fit) + return 1; + } + ++#ifdef CONFIG_FIT_CIPHER ++static int fit_image_uncipher(const void *fit, int image_noffset, ++ void **data, size_t *size) ++{ ++ int cipher_noffset, ret; ++ void *dst; ++ size_t size_dst; ++ ++ cipher_noffset = fdt_subnode_offset(fit, image_noffset, ++ FIT_CIPHER_NODENAME); ++ if (cipher_noffset < 0) ++ return 0; ++ ++ ret = fit_image_decrypt_data(fit, image_noffset, cipher_noffset, ++ *data, *size, &dst, &size_dst); ++ if (ret) ++ goto out; ++ ++ *data = dst; ++ *size = size_dst; ++ ++ out: ++ return ret; ++} ++#endif /* CONFIG_FIT_CIPHER */ ++ + /** + * fit_image_check_os - check whether image node is of a given os type + * @fit: pointer to the FIT format image header +@@ -1451,6 +1555,34 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp) + return (comp == image_comp); + } + ++/** ++ * fdt_check_no_at() - Check for nodes whose names contain '@' ++ * ++ * This checks the parent node and all subnodes recursively ++ * ++ * @fit: FIT to check ++ * @parent: Parent node to check ++ * @return 0 if OK, -EADDRNOTAVAIL is a node has a name containing '@' ++ */ ++static int fdt_check_no_at(const void *fit, int parent) ++{ ++ const char *name; ++ int node; ++ int ret; ++ ++ name = fdt_get_name(fit, parent, NULL); ++ if (!name || strchr(name, '@')) ++ return -EADDRNOTAVAIL; ++ ++ fdt_for_each_subnode(node, fit, parent) { ++ ret = fdt_check_no_at(fit, node); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + /** + * fit_check_format - sanity check FIT image format + * @fit: pointer to the FIT format image header +@@ -1464,6 +1596,49 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp) + */ + int fit_check_format(const void *fit) + { ++ int ret; ++ ulong size; ++ ++ /* A FIT image must be a valid FDT */ ++ ret = fdt_check_header(fit); ++ if (ret) { ++ debug("Wrong FIT format: not a flattened device tree (err=%d)\n", ++ ret); ++ return -ENOEXEC; ++ } ++ ++ if (CONFIG_IS_ENABLED(FIT_FULL_CHECK)) { ++ /* ++ * If we are not given the size, make do wtih calculating it. ++ * This is not as secure, so we should consider a flag to ++ * control this. ++ */ ++ size = fdt_totalsize(fit); ++ ret = fdt_check_full(fit, size); ++ if (ret) ++ ret = -EINVAL; ++ ++ /* ++ * U-Boot stopped using unit addressed in 2017. Since libfdt ++ * can match nodes ignoring any unit address, signature ++ * verification can see the wrong node if one is inserted with ++ * the same name as a valid node but with a unit address ++ * attached. Protect against this by disallowing unit addresses. ++ */ ++ if (!ret && CONFIG_IS_ENABLED(FIT_SIGNATURE)) { ++ ret = fdt_check_no_at(fit, 0); ++ ++ if (ret) { ++ debug("FIT check error %d\n", ret); ++ return ret; ++ } ++ } ++ if (ret) { ++ debug("FIT check error %d\n", ret); ++ return ret; ++ } ++ } ++ + /* mandatory / node 'description' property */ + if (fdt_getprop(fit, 0, FIT_DESC_PROP, NULL) == NULL) { + debug("Wrong FIT format: no description\n"); +@@ -1632,24 +1807,6 @@ int fit_conf_find_compat(const void *fit, const void *fdt) + return best_match_offset; + } + +-/** +- * fit_conf_get_node - get node offset for configuration of a given unit name +- * @fit: pointer to the FIT format image header +- * @conf_uname: configuration node unit name +- * +- * fit_conf_get_node() finds a configuration (within the '/configurations' +- * parent node) of a provided unit name. If configuration is found its node +- * offset is returned to the caller. +- * +- * When NULL is provided in second argument fit_conf_get_node() will search +- * for a default configuration node instead. Default configuration node unit +- * name is retrieved from FIT_DEFAULT_PROP property of the '/configurations' +- * node. +- * +- * returns: +- * configuration node offset when found (>=0) +- * negative number on failure (FDT_ERR_* code) +- */ + int fit_conf_get_node(const void *fit, const char *conf_uname) + { + int noffset, confs_noffset; +@@ -1835,10 +1992,13 @@ int fit_image_load(bootm_headers_t *images, ulong addr, + printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr); + + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT); +- if (!fit_check_format(fit)) { +- printf("Bad FIT %s image format!\n", prop_name); ++ ret = fit_check_format(fit); ++ if (ret) { ++ printf("Bad FIT %s image format! (err=%d)\n", prop_name, ret); ++ if (CONFIG_IS_ENABLED(FIT_SIGNATURE) && ret == -EADDRNOTAVAIL) ++ printf("Signature checking prevents use of unit addresses (@) in nodes\n"); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT); +- return -ENOEXEC; ++ return ret; + } + bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK); + if (fit_uname) { +@@ -1889,7 +2049,7 @@ int fit_image_load(bootm_headers_t *images, ulong addr, + fit_uname = fit_get_name(fit, noffset, NULL); + } + if (noffset < 0) { +- puts("Could not find subimage node\n"); ++ printf("Could not find subimage node type '%s'\n", prop_name); + bootstage_error(bootstage_id + BOOTSTAGE_SUB_SUBNODE); + return -ENOENT; + } +@@ -1953,6 +2113,18 @@ int fit_image_load(bootm_headers_t *images, ulong addr, + return -ENOENT; + } + ++#ifdef CONFIG_FIT_CIPHER ++ /* Decrypt data before uncompress/move */ ++ if (IMAGE_ENABLE_DECRYPT) { ++ puts(" Decrypting Data ... "); ++ if (fit_image_uncipher(fit, noffset, &buf, &size)) { ++ puts("Error\n"); ++ return -EACCES; ++ } ++ puts("OK\n"); ++ } ++#endif ++ + #if !defined(USE_HOSTCC) && defined(CONFIG_FIT_IMAGE_POST_PROCESS) + /* perform any post-processing on the image data */ + board_fit_image_post_process(&buf, &size); +diff --git a/common/image-sig.c b/common/image-sig.c +index 639a112..b4daa13 100644 +--- a/common/image-sig.c ++++ b/common/image-sig.c +@@ -229,7 +229,7 @@ static int fit_image_setup_verify(struct image_sign_info *info, + padding_name = RSA_DEFAULT_PADDING_NAME; + + memset(info, '\0', sizeof(*info)); +- info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); ++ info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + info->fit = (void *)fit; + info->node_offset = noffset; + info->name = algo_name; +@@ -291,6 +291,14 @@ static int fit_image_verify_sig(const void *fit, int image_noffset, + fdt_for_each_subnode(noffset, fit, image_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + ++ /* ++ * We don't support this since libfdt considers names with the ++ * name root but different @ suffix to be equal ++ */ ++ if (strchr(name, '@')) { ++ err_msg = "Node name contains @"; ++ goto error; ++ } + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, +@@ -340,7 +348,8 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *required; + int ret; + +- required = fdt_getprop(sig_blob, noffset, "required", NULL); ++ required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, ++ NULL); + if (!required || strcmp(required, "image")) + continue; + ret = fit_image_verify_sig(fit, image_noffset, data, size, +@@ -359,20 +368,39 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, + return 0; + } + +-int fit_config_check_sig(const void *fit, int noffset, int required_keynode, +- char **err_msgp) ++/** ++ * fit_config_check_sig() - Check the signature of a config ++ * ++ * @fit: FIT to check ++ * @noffset: Offset of configuration node (e.g. /configurations/conf-1) ++ * @required_keynode: Offset in the control FDT of the required key node, ++ * if any. If this is given, then the configuration wil not ++ * pass verification unless that key is used. If this is ++ * -1 then any signature will do. ++ * @conf_noffset: Offset of the configuration subnode being checked (e.g. ++ * /configurations/conf-1/kernel) ++ * @err_msgp: In the event of an error, this will be pointed to a ++ * help error string to display to the user. ++ * @return 0 if all verified ok, <0 on error ++ */ ++static int fit_config_check_sig(const void *fit, int noffset, ++ int required_keynode, int conf_noffset, ++ char **err_msgp) + { + char * const exc_prop[] = {"data"}; + const char *prop, *end, *name; + struct image_sign_info info; + const uint32_t *strings; ++ const char *config_name; + uint8_t *fit_value; + int fit_value_len; ++ bool found_config; + int max_regions; + int i, prop_len; + char path[200]; + int count; + ++ config_name = fit_get_name(fit, conf_noffset, NULL); + debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), + fit_get_name(fit, noffset, NULL), + fit_get_name(gd_fdt_blob(), required_keynode, NULL)); +@@ -413,9 +441,20 @@ int fit_config_check_sig(const void *fit, int noffset, int required_keynode, + char *node_inc[count]; + + debug("Hash nodes (%d):\n", count); ++ found_config = false; + for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) { + debug(" '%s'\n", name); + node_inc[i] = (char *)name; ++ if (!strncmp(FIT_CONFS_PATH, name, strlen(FIT_CONFS_PATH)) && ++ name[sizeof(FIT_CONFS_PATH) - 1] == '/' && ++ !strcmp(name + sizeof(FIT_CONFS_PATH), config_name)) { ++ debug(" (found config node %s)", config_name); ++ found_config = true; ++ } ++ } ++ if (!found_config) { ++ *err_msgp = "Selected config not in hashed nodes"; ++ return -1; + } + + /* +@@ -483,7 +522,7 @@ static int fit_config_verify_sig(const void *fit, int conf_noffset, + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_check_sig(fit, noffset, sig_offset, +- &err_msg); ++ conf_noffset, &err_msg); + if (ret) { + puts("- "); + } else { +@@ -499,21 +538,32 @@ static int fit_config_verify_sig(const void *fit, int conf_noffset, + goto error; + } + +- return verified ? 0 : -EPERM; ++ if (verified) ++ return 0; + + error: + printf(" error!\n%s for '%s' hash node in '%s' config node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, conf_noffset, NULL)); +- return -1; ++ return -EPERM; + } + +-int fit_config_verify_required_sigs(const void *fit, int conf_noffset, +- const void *sig_blob) ++static int fit_config_verify_required_sigs(const void *fit, int conf_noffset, ++ const void *sig_blob) + { ++ const char *name = fit_get_name(fit, conf_noffset, NULL); + int noffset; + int sig_node; + ++ /* ++ * We don't support this since libfdt considers names with the ++ * name root but different @ suffix to be equal ++ */ ++ if (strchr(name, '@')) { ++ printf("Configuration node '%s' contains '@'\n", name); ++ return -EPERM; ++ } ++ + /* Work out what we need to verify */ + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { +@@ -526,7 +576,8 @@ int fit_config_verify_required_sigs(const void *fit, int conf_noffset, + const char *required; + int ret; + +- required = fdt_getprop(sig_blob, noffset, "required", NULL); ++ required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, ++ NULL); + if (!required || strcmp(required, "conf")) + continue; + ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, +diff --git a/common/load_fip.c b/common/load_fip.c +new file mode 100644 +index 0000000..7f1913c +--- /dev/null ++++ b/common/load_fip.c +@@ -0,0 +1,872 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SECURE 0x0 ++#define NON_SECURE 0x1 ++ ++#define EP_EE_MASK 0x2 ++#define EP_EE_LITTLE 0x0 ++#define EP_EE_BIG 0x2 ++#define ep_get_ee(x) ((x) & EP_EE_MASK) ++#define ep_set_ee(x, ee) ((x) = ((x) & ~EP_EE_MASK) | (ee)) ++ ++/* CPSR/SPSR definitions */ ++#define DAIF_FIQ_BIT (1 << 0) ++#define DAIF_IRQ_BIT (1 << 1) ++#define DAIF_ABT_BIT (1 << 2) ++#define DAIF_DBG_BIT (1 << 3) ++#define SPSR_DAIF_SHIFT 6 ++#define SPSR_DAIF_MASK 0xf ++ ++#define SPSR_AIF_SHIFT 6 ++#define SPSR_AIF_MASK 0x7 ++ ++#define SPSR_E_SHIFT 9 ++#define SPSR_E_MASK 0x1 ++#define SPSR_E_LITTLE 0x0 ++#define SPSR_E_BIG 0x1 ++ ++#define SPSR_T_SHIFT 5 ++#define SPSR_T_MASK 0x1 ++#define SPSR_T_ARM 0x0 ++#define SPSR_T_THUMB 0x1 ++ ++#define MODE_SP_SHIFT 0x0 ++#define MODE_SP_MASK 0x1 ++#define MODE_SP_EL0 0x0 ++#define MODE_SP_ELX 0x1 ++ ++#define MODE_RW_SHIFT 0x4 ++#define MODE_RW_MASK 0x1 ++#define MODE_RW_64 0x0 ++#define MODE_RW_32 0x1 ++ ++#define MODE_EL_SHIFT 0x2 ++#define MODE_EL_MASK 0x3 ++#define MODE_EL3 0x3 ++#define MODE_EL2 0x2 ++#define MODE_EL1 0x1 ++#define MODE_EL0 0x0 ++ ++#define MODE32_SHIFT 0 ++#define MODE32_MASK 0xf ++#define MODE32_USR 0x0 ++#define MODE32_FIQ 0x1 ++#define MODE32_IRQ 0x2 ++#define MODE32_SVC 0x3 ++#define MODE32_MON 0x6 ++#define MODE32_ABT 0x7 ++#define MODE32_HYP 0xa ++#define MODE32_UND 0xb ++#define MODE32_SYS 0xf ++ ++#define DISABLE_ALL_EXCEPTIONS \ ++ (DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT | DAIF_DBG_BIT) ++ ++#define PARAM_EP_SECURITY_MASK 0x1 ++#define get_security_state(x) ((x) & PARAM_EP_SECURITY_MASK) ++#define set_security_state(x, security) \ ++ ((x) = ((x) & ~PARAM_EP_SECURITY_MASK) | (security)) ++ ++#define spsr_64(el, sp, daif) \ ++ ((MODE_RW_64 << MODE_RW_SHIFT) | \ ++ (((el) & MODE_EL_MASK) << MODE_EL_SHIFT) | \ ++ (((sp) & MODE_SP_MASK) << MODE_SP_SHIFT) | \ ++ (((daif) & SPSR_DAIF_MASK) << SPSR_DAIF_SHIFT)) ++ ++#define spsr_mode32(mode, isa, endian, aif) \ ++ ((MODE_RW_32 << MODE_RW_SHIFT) | \ ++ (((mode) & MODE32_MASK) << MODE32_SHIFT) | \ ++ (((isa) & SPSR_T_MASK) << SPSR_T_SHIFT) | \ ++ (((endian) & SPSR_E_MASK) << SPSR_E_SHIFT) | \ ++ (((aif) & SPSR_AIF_MASK) << SPSR_AIF_SHIFT)) ++ ++/* Length of a node address (an IEEE 802 address). */ ++#define _UUID_NODE_LEN 6 ++ ++/* ++ * See also: ++ * http://www.opengroup.org/dce/info/draft-leach-uuids-guids-01.txt ++ * http://www.opengroup.org/onlinepubs/009629399/apdxa.htm ++ * ++ * A DCE 1.1 compatible source representation of UUIDs. ++ */ ++ ++/* XXX namespace pollution? */ ++typedef struct uuid uuid_t; ++ ++#define FIP_MAX_FILES 10 ++ ++/* Update this number as required */ ++#define TOC_HEADER_SERIAL_NUMBER 0x12345678 ++ ++#define FLAG_FILENAME (1 << 0) ++ ++#define WAIT_A53MP_UP_NUM_OF_TIMES 10700 ++ ++typedef struct entry_lookup_list { ++ const char *name; ++ uuid_t name_uuid; ++ const char *command_line_name; ++ struct file_info *info; ++ unsigned int flags; ++} entry_lookup_list_t; ++ ++typedef struct file_info { ++ uuid_t name_uuid; ++ const char *filename; ++ unsigned int size; ++ void *image_buffer; ++ entry_lookup_list_t *entry; ++} file_info_t; ++ ++ ++/* This is used as a signature to validate the blob header */ ++#define TOC_HEADER_NAME 0xAA640001 ++ ++ ++/* ToC Entry UUIDs */ ++#define UUID_TRUSTED_BOOT_FIRMWARE_BL2 \ ++ {0x0becf95f, 0x224d, 0x4d3e, 0xa5, 0x44, {0xc3, 0x9d, 0x81, 0xc7, 0x3f, 0x0a} } ++#define UUID_SCP_FIRMWARE_BL30 \ ++ {0x3dfd6697, 0xbe89, 0x49e8, 0xae, 0x5d, {0x78, 0xa1, 0x40, 0x60, 0x82, 0x13} } ++#define UUID_EL3_RUNTIME_FIRMWARE_BL31 \ ++ {0x6d08d447, 0xfe4c, 0x4698, 0x9b, 0x95, {0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x00} } ++#define UUID_SECURE_PAYLOAD_BL32 \ ++ {0x89e1d005, 0xdc53, 0x4713, 0x8d, 0x2b, {0x50, 0x0a, 0x4b, 0x7a, 0x3e, 0x38} } ++#define UUID_NON_TRUSTED_FIRMWARE_BL33 \ ++ {0xa7eed0d6, 0xeafc, 0x4bd5, 0x97, 0x82, {0x99, 0x34, 0xf2, 0x34, 0xb6, 0xe4} } ++ ++typedef struct fip_toc_header { ++ uint32_t name; ++ uint32_t serial_number; ++ uint64_t flags; ++} fip_toc_header_t; ++ ++typedef struct fip_toc_entry { ++ uuid_t uuid; ++ uint64_t offset_address; ++ uint64_t size; ++ uint64_t flags; ++} fip_toc_entry_t; ++ ++typedef struct aapcs64_params { ++ uint64_t arg0; ++ uint64_t arg1; ++ uint64_t arg2; ++ uint64_t arg3; ++ uint64_t arg4; ++ uint64_t arg5; ++ uint64_t arg6; ++ uint64_t arg7; ++} aapcs64_params_t; ++ ++/*************************************************************************** ++ * This structure provides version information and the size of the ++ * structure, attributes for the structure it represents ++ ***************************************************************************/ ++typedef struct param_header { ++ uint8_t type; /* type of the structure */ ++ uint8_t version; /* version of this structure */ ++ uint16_t size; /* size of this structure in bytes */ ++ uint32_t attr; /* attributes: unused bits SBZ */ ++} param_header_t; ++ ++/***************************************************************************** ++ * This structure represents the superset of information needed while ++ * switching exception levels. The only two mechanisms to do so are ++ * ERET & SMC. Security state is indicated using bit zero of header ++ * attribute ++ * NOTE: BL1 expects entrypoint followed by spsr while processing ++ * SMC to jump to BL31 from the start of entry_point_info ++ *****************************************************************************/ ++typedef struct entry_point_info { ++ param_header_t h; ++ uint64_t pc; ++ uint32_t spsr; ++ aapcs64_params_t args; ++} entry_point_info_t; ++ ++/***************************************************************************** ++ * Image info binary provides information from the image loader that ++ * can be used by the firmware to manage available trusted RAM. ++ * More advanced firmware image formats can provide additional ++ * information that enables optimization or greater flexibility in the ++ * common firmware code ++ *****************************************************************************/ ++typedef struct atf_image_info { ++ param_header_t h; ++ uint64_t image_base; /* physical address of base of image */ ++ uint32_t image_size; /* bytes read from image file */ ++ uint32_t copied_size; /* image size copied in blocks */ ++} atf_image_info_t; ++ ++/******************************************************************************* ++ * This structure represents the superset of information that can be passed to ++ * BL31 e.g. while passing control to it from BL2. The BL32 parameters will be ++ * populated only if BL2 detects its presence. A pointer to a structure of this ++ * type should be passed in X0 to BL31's cold boot entrypoint. ++ * ++ * Use of this structure and the X0 parameter is not mandatory: the BL31 ++ * platform code can use other mechanisms to provide the necessary information ++ * about BL32 and BL33 to the common and SPD code. ++ * ++ * BL31 image information is mandatory if this structure is used. If either of ++ * the optional BL32 and BL33 image information is not provided, this is ++ * indicated by the respective image_info pointers being zero. ++ ******************************************************************************/ ++typedef struct bl31_params { ++ param_header_t h; ++ uint64_t bl31_image_info; ++ uint64_t bl32_ep_info; ++ uint64_t bl32_image_info; ++ uint64_t bl33_ep_info; ++ uint64_t bl33_image_info; ++} bl31_params_t; ++ ++file_info_t files[FIP_MAX_FILES]; ++unsigned file_info_count = 0; ++ ++uuid_t uuid_null = {0}; ++uuid_t uuid_bl33 = UUID_NON_TRUSTED_FIRMWARE_BL33; ++uuid_t uuid_bl32 = UUID_SECURE_PAYLOAD_BL32; ++uuid_t uuid_bl31 = UUID_EL3_RUNTIME_FIRMWARE_BL31; ++ ++long long kernel_load_addr; ++/* kernel start addr - sizeof(header) */ ++#define BL33_LOAD_ADDR (kernel_load_addr - 0x40) ++#define FDT_LOAD_ADDR (kernel_load_addr + 0x2F00000) ++#define BL31_BASE (kernel_load_addr + 0x2F80000) ++#define BL31_SIZE 0x200000 ++#define BL33_EP_INFO_ADDR (kernel_load_addr + 0x4F80000) ++#define TEE_KEY_AREA_ADDR (kernel_load_addr + 0x4F85000) ++#define OS_SYS_CTRL_REG2 (0x11020000 + 0x308) ++#define OS_SYS_CTRL_REG4 (0x11020000 + 0x310) ++#define TEE_KEY_AREA_SIZE 0x600 ++#define TEE_CODE_AREA_LEN_ADDR_OFFSET 0x8 ++#define ATF_AREA_LEN_ADDR_OFFSET 0x2a4 ++ ++/* ++ * Add ability to specify and flag different file types. ++ * Add flags to the toc_entry? ++ * const char* format_type_str[] = { "RAW", "ELF", "PIC" }; ++ */ ++ ++/* The images used depends on the platform. */ ++static entry_lookup_list_t toc_entry_lookup_list[] = { ++ { ++ "Trusted Boot Firmware BL2", UUID_TRUSTED_BOOT_FIRMWARE_BL2, ++ "bl2", NULL, FLAG_FILENAME ++ }, ++ { ++ "SCP Firmware BL3-0", UUID_SCP_FIRMWARE_BL30, ++ "bl30", NULL, FLAG_FILENAME ++ }, ++ { ++ "EL3 Runtime Firmware BL3-1", UUID_EL3_RUNTIME_FIRMWARE_BL31, ++ "bl31", NULL, FLAG_FILENAME ++ }, ++ { ++ "Secure Payload BL3-2 (Trusted OS)", UUID_SECURE_PAYLOAD_BL32, ++ "bl32", NULL, FLAG_FILENAME ++ }, ++ { ++ "Non-Trusted Firmware BL3-3", UUID_NON_TRUSTED_FIRMWARE_BL33, ++ "bl33", NULL, FLAG_FILENAME ++ }, ++ { NULL, {0}, 0 } ++}; ++ ++/* Return 0 for equal uuids */ ++static inline int compare_uuids(const uuid_t *uuid1, const uuid_t *uuid2) ++{ ++ return memcmp(uuid1, uuid2, sizeof(uuid_t)); ++} ++ ++ ++static inline void copy_uuid(uuid_t* const to_uuid, uuid_t* const from_uuid) ++{ ++ (void)memcpy_s(to_uuid, sizeof(uuid_t), from_uuid, sizeof(uuid_t)); ++} ++ ++static entry_lookup_list_t *get_entry_lookup_from_uuid(uuid_t* const uuid) ++{ ++ unsigned int lookup_index = 0; ++ ++ while (toc_entry_lookup_list[lookup_index].command_line_name != NULL) { ++ if (compare_uuids(&toc_entry_lookup_list[lookup_index].name_uuid, uuid) == 0) ++ return &toc_entry_lookup_list[lookup_index]; ++ ++ lookup_index++; ++ } ++ return NULL; ++} ++ ++static void dump_toc(void) ++{ ++ unsigned int index; ++ unsigned int image_offset; ++ unsigned int image_size; ++ ++ image_offset = sizeof(fip_toc_header_t) + ++ (sizeof(fip_toc_entry_t) * (file_info_count + 1)); ++ ++ printf("Firmware Image Package ToC:\n"); ++ printf("---------------------------\n"); ++ for (index = 0; index < file_info_count; index++) { ++ if (files[index].entry) ++ printf("- %s: ", files[index].entry->name); ++ else ++ printf("- Unknown entry: "); ++ image_size = files[index].size; ++ ++ printf("offset=0x%X, size=0x%X\n", image_offset, image_size); ++ image_offset += image_size; ++ ++ if (files[index].filename) ++ printf(" file: '%s'\n", files[index].filename); ++ } ++ printf("---------------------------\n"); ++} ++ ++/* Read and load existing package into memory. */ ++static int parse_fip(char *fip_buffer) ++{ ++ fip_toc_header_t *toc_header = NULL; ++ fip_toc_entry_t *toc_entry = NULL; ++ int found_last_toc_entry = 0; ++ file_info_t *file_info_entry = NULL; ++ int ret = -1; ++ ++ if (fip_buffer == NULL) { ++ printf("ERROR: Invalid fip buffer addr.\n"); ++ ret = -EINVAL; ++ goto parse_fip_error; ++ } ++ ++ /* Set the ToC Header at the base of the buffer */ ++ toc_header = (fip_toc_header_t *)fip_buffer; ++ /* The first toc entry should be just after the ToC header */ ++ toc_entry = (fip_toc_entry_t *)(toc_header + 1); ++ ++ /* While the ToC entry is contained into the buffer */ ++ int cnt = 0; ++ while (cnt < FIP_MAX_FILES) { ++ /* Check if the ToC Entry is the last one */ ++ if (compare_uuids(&toc_entry->uuid, &uuid_null) == 0) { ++ found_last_toc_entry = 1; ++ ret = 0; ++ break; ++ } ++ ++ /* Add the entry into file_info */ ++ ++ /* Get the new entry in the array and clear it */ ++ file_info_entry = &files[file_info_count++]; ++ (void)memset_s(file_info_entry, sizeof(file_info_t), 0, sizeof(file_info_t)); ++ ++ /* Copy the info from the ToC entry */ ++ copy_uuid(&file_info_entry->name_uuid, &toc_entry->uuid); ++ file_info_entry->image_buffer = fip_buffer + ++ toc_entry->offset_address; ++ file_info_entry->size = toc_entry->size; ++ ++ /* Check if there is a corresponding entry in lookup table */ ++ file_info_entry->entry = ++ get_entry_lookup_from_uuid(&toc_entry->uuid); ++ ++ /* Go to the next ToC entry */ ++ toc_entry++; ++ cnt++; ++ } ++ ++ if (!found_last_toc_entry) { ++ printf("ERROR: Given FIP does not have an end ToC entry.\n"); ++ ret = -EINVAL; ++ goto parse_fip_error; ++ } ++ ++parse_fip_error: ++ return ret; ++} ++ ++int is_fip(const char *buf) ++{ ++ fip_toc_header_t *header = (fip_toc_header_t *)buf; ++ ++ if (buf == NULL) { ++ printf("Invalid fip buffer address\n"); ++ return 0; ++ } ++ ++ if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0)) ++ return 1; ++ else ++ return 0; ++} ++ ++/* for a53up boot up*/ ++entry_point_info_t bl32_ep_info; ++entry_point_info_t bl33_ep_info; ++bl31_params_t bl31_params; ++ ++/*for a53mp boot up*/ ++entry_point_info_t amp_bl32_ep_info; ++entry_point_info_t amp_bl33_ep_info; ++bl31_params_t amp_bl31_params; ++ ++void *g_fdt_addr = NULL; ++ ++int early_init_dt_scan_chosen(unsigned long node, const char *uname, ++ int depth, void *data) ++{ ++ unsigned long l = 0; ++ char *p = NULL; ++ char *cmdline = data; ++ ++ if (depth != 1 || !cmdline || ++ (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) ++ return 0; ++ ++ p = (char *)fdt_getprop(g_fdt_addr, node, "bootargs", (int *)&l); ++ if (p != NULL && l > 0) ++ strlcpy(p, cmdline, COMMAND_LINE_SIZE); ++ ++ /* break now */ ++ return 1; ++} ++ ++/** ++ * kbasename - return the last part of a pathname. ++ * ++ * @path: path to extract the filename from. ++ */ ++static inline const char *kbasename(const char *path) ++{ ++ const char *tail = strrchr(path, '/'); ++ return tail ? tail + 1 : path; ++} ++ ++/** ++ * scan_flat_dt_save_bootargs - scan flattened tree blob and save bootargs to ++ * chosen args. ++ * @data: context data pointer ++ * ++ * This function is used to scan the flattened device-tree, ++ * find the 'chosen' args, and save save bootargs to chosen args. ++ */ ++int scan_flat_dt_save_bootargs(char *data) ++{ ++ const void *blob = g_fdt_addr; ++ const char *pathp = NULL; ++ int offset; ++ int rc = 0; ++ int depth = -1; ++ ++ for (offset = fdt_next_node(blob, -1, &depth); ++ offset >= 0 && depth >= 0 && !rc; ++ offset = fdt_next_node(blob, offset, &depth)) { ++ pathp = fdt_get_name(blob, offset, NULL); ++ if (*pathp == '/') ++ pathp = kbasename(pathp); ++ ++ if (!strcmp(pathp, "chosen") || !strcmp(pathp, "chosen@0")) { ++ rc = early_init_dt_scan_chosen(offset, pathp, depth, data); ++ break; ++ } ++ } ++ ++ return rc; ++} ++ ++int boot_a53mp_flag = 0; ++uint64_t boot_a53mp_addr = 0; ++static int create_entry_bl33_info(entry_point_info_t* const bl33_ep) ++{ ++ int ret; ++ image_header_t *hdr; ++ char *fdt; ++ uint32_t fdt_size; ++ uint32_t fdt_magic_check; ++ ++ if (!bl33_ep) { ++ return -EINVAL; ++ } ++ ++ printf("Create BL33 Entry Point info ...\n"); ++ ++ // 1. 处理 BL33 (U-Boot) 镜像头部和入口点 ++ // BL33_LOAD_ADDR 是 uImage 的基地址,包含 64 字节头部 ++ hdr = (image_header_t *)(uintptr_t)(BL33_LOAD_ADDR); ++ ++ // 获取 BL33 的执行入口地址 (PC) ++ // image_get_data 通常返回跳过 Header 后的数据起始地址 ++ bl33_ep->pc = (uint64_t)(uintptr_t)image_get_data(hdr); ++ ++ if (!bl33_ep->pc) { ++ printf("Error: Invalid BL33 image, no valid entry point found at 0x%lx\n", ++ (unsigned long)BL33_LOAD_ADDR); ++ return -EINVAL; ++ } ++ ++ printf("BL33 PC: 0x%llx\n", bl33_ep->pc); ++ ++ // 2. 处理 FDT (设备树) ++ // 直接使用宏定义的独立加载地址,不再依赖 files 数组或从 BL33 末尾推算 ++ fdt = (char *)(uintptr_t)image_get_image_end(hdr); ++ fdt_size = fdt_totalsize(fdt); ++ printf("FDT loaded at 0x%pK, size: 0x%x bytes\n", fdt, fdt_size); ++ (void)memmove_s((void *)(uintptr_t)FDT_LOAD_ADDR, fdt_size, fdt, fdt_size); ++ fdt = (char *)(uintptr_t)FDT_LOAD_ADDR; ++ g_fdt_addr = fdt; ++ memcpy(&fdt_magic_check, fdt, sizeof(uint32_t)); ++ // 验证 FDT 头部合法性 ++ ret = fdt_check_header(fdt); ++ if (ret != 0) { ++ printf("Error: Invalid FDT header at 0x%pK, ret=%d\n", fdt, ret); ++ printf("Error: Invalid FDT header at 0x%pK, ret=%d (%s)\n", ++ fdt, ret, ++ (ret == -FDT_ERR_BADMAGIC) ? "BAD MAGIC" : ++ (ret == -FDT_ERR_BADVERSION) ? "BAD VERSION" : ++ "UNKNOWN ERROR"); ++ ++ // 如果魔数是 0,说明该地址没烧录数据 ++ if (fdt_magic_check == 0x00000000) { ++ printf("Hint: The address 0x%pK contains zeros. Did you flash the DTB file?\n", fdt); ++ } ++ // 如果魔数是 uImage 的魔数 (0x27051956),说明烧录错了文件 ++ else if (fdt_magic_check == 0x27051956 || fdt_magic_check == 0x56190527) { ++ printf("Hint: The address 0x%pK contains a U-Boot Image header. Did you flash uImage instead of DTB?\n", fdt); ++ } ++ ++ return ret; ++ } ++ ++ // 获取 FDT 总大小,用于调试或后续内存保护检查 ++ ++ // 3. 设置 BL33 启动参数 ++ // ARM64 规范中,x0 寄存器通常传递 FDT 地址给内核/Bootloader ++ bl33_ep->args.arg0 = (uint64_t)(uintptr_t)fdt; ++ ++ // 设置 SPSR (Saved Program Status Register) ++ // MODE_EL1: 非安全世界异常级别。若 U-Boot 配置为 EL2,需改为 MODE_EL2 ++ bl33_ep->spsr = spsr_64(MODE_EL1, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); ++ ++ // 设置安全状态为非安全 (Non-Secure) ++ set_security_state(bl33_ep->h.attr, NON_SECURE); ++ ++ printf("BL33 Setup Complete:\n"); ++ printf(" SPSR : 0x%x\n", bl33_ep->spsr); ++ printf(" Arg0 : 0x%llx (FDT)\n", bl33_ep->args.arg0); ++ ++ return 0; ++} ++ ++static int create_entry_bl31_info(uint64_t* const bl31_pc) ++{ ++ unsigned int index; ++ if (!bl31_pc) { ++ printf("No ATF found!\n"); ++ return -EINVAL; ++ } ++ ++ printf("Create BL31 Entry Point info ...\n"); ++ ++ for (index = 0; index < file_info_count; index++) { ++ if (!files[index].entry) { ++ continue; ++ } ++ ++ if (files[index].image_buffer == NULL) { ++ printf("global pointer is null: load_fip_amp %d ", __LINE__); ++ continue; ++ } ++ if (files[index].entry->name == NULL) { ++ printf("global pointer is null: load_fip_amp %d ", __LINE__); ++ continue; ++ } ++ if (compare_uuids(&files[index].name_uuid, &uuid_bl31) == 0) { ++ printf("Get - %s \n", files[index].entry->name); ++ if (memmove_s((void *)(uintptr_t)BL31_BASE, files[index].size, ++ files[index].image_buffer, files[index].size)) { ++ printf("%s %d : memmove_s err!\n", __func__, __LINE__); ++ return -EINVAL; ++ } ++ *bl31_pc = BL31_BASE; ++ /* BL31通常只有一个,找到后可选择break,但保留循环以兼容潜在的多段处理或日志完整性 */ ++ break; ++ } ++ } ++ ++ if (!bl31_pc) { ++ printf("No ATF found!\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++int load_fip_amp(char *buf) ++{ ++ int ret; ++ entry_point_info_t *bl32_ep = &_bl32_ep_info; ++ entry_point_info_t *bl33_ep = &_bl33_ep_info; ++ bl31_params_t *bl31_p = &_bl31_params; ++ uint64_t bl31_pc = 0; ++ ++ (void)memset_s(bl32_ep, sizeof(entry_point_info_t), 0, sizeof(entry_point_info_t)); ++ (void)memset_s(bl33_ep, sizeof(entry_point_info_t), 0, sizeof(entry_point_info_t)); ++ (void)memset_s(bl31_p, sizeof(bl31_params_t), 0, sizeof(bl31_params_t)); ++ bl31_p->bl32_ep_info = (uint64_t)(uintptr_t)bl32_ep; ++ bl31_p->bl33_ep_info = (uint64_t)(uintptr_t)bl33_ep; ++ ++ printf("Load fip from 0x%pK ...\n", buf); ++ if (buf == NULL) { ++ printf("Invalid fip buffer address\n"); ++ ret = -EINVAL; ++ goto exit; ++ } ++ ret = parse_fip(buf); ++ if (ret) ++ goto exit; ++ ++ dump_toc(); ++ ++ ++ ret = create_entry_info(bl33_ep, &bl31_pc); ++ if (ret) ++ goto exit; ++ ++ /* save a53mp boot flag and addr, retrun success, ++ * a53mp would boot up while a53up be booting up*/ ++ boot_a53mp_flag = 1; ++ boot_a53mp_addr = bl31_pc; ++ file_info_count = 0; ++ ++ flush_dcache_all(); ++ ++ return 0; ++ ++exit: ++ return ret; ++} ++ ++unsigned int get_tee_image_size(char* const secure_os) ++{ ++ unsigned int tee_image_size; ++ unsigned int tee_code_area_size; ++ unsigned int atf_area_size; ++ ++ tee_code_area_size = *(uint32_t *)((uintptr_t)secure_os + TEE_CODE_AREA_LEN_ADDR_OFFSET); ++ atf_area_size = *(uint32_t *)((uintptr_t)secure_os + ATF_AREA_LEN_ADDR_OFFSET); ++ tee_image_size = tee_code_area_size + atf_area_size + TEE_KEY_AREA_SIZE; ++ ++ printf("tee_code_area_size[0x%x] atf_area_size[0x%x] tee_image_size[0x%x]", ++ tee_code_area_size, atf_area_size, tee_image_size); ++ ++ return tee_image_size; ++} ++ ++void go_to_gsl(void) ++{ ++#if CONFIG_ARM_SMCCC ++ struct arm_smccc_res res; ++ unsigned long funcid = TEGRA_SMC_GSL_FUNCID; ++ ++ printf("Run the smc command to switch to the GSL.\n"); ++ arm_smccc_smc(funcid, 0, 0, 0, 0, 0, 0, 0, &res); ++#else ++ printf("Error, Please Enable the SMC service.\n"); ++#endif ++ ++ return; ++} ++ ++/* to do: The Linux kernel has an independent image, which cannot be packaged with the atf file. ++ The U-Boot can parse the linux image. */ ++int load_fip_secure_os(const char *common_os, char* const secure_os) ++{ ++ int ret; ++ unsigned long kernel_size; ++ int fdt_size; ++ char *fdt = NULL; ++ unsigned int tee_image_size; ++ entry_point_info_t *bl33_ep = (entry_point_info_t *)(uintptr_t)BL33_EP_INFO_ADDR; ++ ++ printf("Load common_os from 0x%pK ...\n", common_os); ++ printf("Load secure_os from 0x%pK ...\n", secure_os); ++ ++ tee_image_size = get_tee_image_size(secure_os); ++ (void)memmove_s((void *)(uintptr_t)TEE_KEY_AREA_ADDR, tee_image_size, secure_os, tee_image_size); ++ (void)memset_s(bl33_ep, sizeof(entry_point_info_t), 0, sizeof(entry_point_info_t)); ++ printf("Create Entry Point info ...\n"); ++ ++ image_header_t *hdr = (image_header_t *)(common_os); ++ fdt = (char *)(uintptr_t)image_get_image_end(hdr); ++ fdt_size = fdt_totalsize(fdt); ++ kernel_size = sizeof(image_header_t) + image_get_data_size(hdr) + fdt_size; ++ ++ printf("kernel_size[0x%x] fdt_size[0x%x] fdt_addr[0x%pK]\n", ++ (int)kernel_size, fdt_size, fdt); ++ ++ if (memmove_s((void *)(uintptr_t)BL33_LOAD_ADDR, kernel_size, common_os, kernel_size)) { ++ printf("%s %d : memmove_s err!\n", __func__, __LINE__); ++ return -1; ++ } ++ hdr = (image_header_t *)(uintptr_t)(BL33_LOAD_ADDR); ++ fdt = (char *)(uintptr_t)image_get_image_end(hdr); ++ ++ (void)memmove_s((void *)(uintptr_t)FDT_LOAD_ADDR, fdt_size, fdt, fdt_size); ++ fdt = (char *)(uintptr_t)FDT_LOAD_ADDR; ++ g_fdt_addr = fdt; ++ ret = fdt_check_header(fdt); ++ if (ret) { ++ printf("Invalid FDT at 0x%pK, hdr at 0x%pK\n", fdt, hdr); ++ return -1; ++ } ++ ++ bl33_ep->pc = (uint64_t)image_get_data( ++ (const image_header_t *)(uintptr_t)BL33_LOAD_ADDR); ++ bl33_ep->args.arg0 = (uint64_t)(uintptr_t)fdt; ++ bl33_ep->spsr = spsr_64(MODE_EL1, ++ MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); ++ set_security_state(bl33_ep->h.attr, NON_SECURE); ++ ++ printf("hdr[0x%pK] header_size[0x%x] image_size[0x%x]\n", ++ hdr, image_get_header_size(), image_get_image_size(hdr)); ++ printf("bl33_ep->spsr = 0x%x bl33_ep->pc = 0x%llx\n", ++ bl33_ep->spsr, bl33_ep->pc); ++ ++ if (!bl33_ep->pc) { ++ printf("Invalid fip image, no kernel found!\n"); ++ return -1; ++ } ++ ++ scan_flat_dt_save_bootargs(env_get("bootargs")); ++ cleanup_before_linux(); ++ go_to_gsl(); ++ ++ return 0; ++} ++ ++static void boot_up_a53mp(void) ++{ ++ unsigned int jump_cmd = 0xea000004; ++ unsigned int warm_reset_cmd[] = { ++ 0xe3a03000, 0xe3413d83, 0xf57ff06f, 0xf57ff04f, ++ 0xee1c3f50, 0xe3833003, 0xee0c3f50, 0xf57ff06f, ++ 0xe320f003, 0xeafffffe, 0x0 ++ }; ++ volatile uint32_t *p = (volatile uint32_t *)0x18; ++ unsigned int *cmd = warm_reset_cmd; ++ unsigned int reg; ++ ++ if (boot_a53mp_flag && boot_a53mp_addr) { ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++ (*(volatile uint64_t *)OS_SYS_CTRL_REG2) = (uint64_t)(uintptr_t)&_bl31_params; ++ (*(volatile uint64_t *)OS_SYS_CTRL_REG4) = (uint64_t)NULL; ++#else ++ (*(volatile uint64_t *)8) = (uint64_t)(uintptr_t)&_bl31_params; ++ (*(volatile uint64_t *)16) = (uint64_t)NULL; ++#endif ++ ++ *((volatile uint32_t *)0) = jump_cmd; ++ while (*cmd) ++ *p++ = *cmd++; ++ ++ writel((boot_a53mp_addr >> 2) & 0xffffffff, REG_PERI_CPU_RVBARADDR_SOC); ++ ++ reg = readl(CRG_REG_BASE + REG_CRG_CLUSTER0_CLK_RST); ++ reg &= ~CLUSTER0_GLB_SRST_REQ; ++ writel(reg, CRG_REG_BASE + REG_CRG_CLUSTER0_CLK_RST); ++ ++ /* wait A53MP to boot up*/ ++ for (reg = 0; reg < WAIT_A53MP_UP_NUM_OF_TIMES; reg++) ++ udelay(1000); /* 1000 us */ ++ } ++} ++ ++int load_fip(char *buf) ++{ ++ int ret; ++ entry_point_info_t *bl32_ep = &bl32_ep_info; ++ entry_point_info_t *bl33_ep = &bl33_ep_info; ++ bl31_params_t *bl31_p = &bl31_params; ++ uint64_t bl31_pc = 0; ++ ++ (void)memset_s(bl32_ep, sizeof(entry_point_info_t), 0, sizeof(entry_point_info_t)); ++ (void)memset_s(bl33_ep, sizeof(entry_point_info_t), 0, sizeof(entry_point_info_t)); ++ (void)memset_s(bl31_p, sizeof(bl31_params_t), 0, sizeof(bl31_params_t)); ++ bl31_p->bl32_ep_info = (uint64_t)(uintptr_t)bl32_ep; ++ bl31_p->bl33_ep_info = (uint64_t)(uintptr_t)bl33_ep; ++ ++ printf("Load fip from 0x%pK ...\n", buf); ++ if (buf == NULL) { ++ printf("Invalid fip buffer address\n"); ++ return -EINVAL; ++ } ++ ret = parse_fip(buf); ++ if (ret) ++ goto error; ++ ++ dump_toc(); ++ ++ ret = create_entry_bl31_info(&bl31_pc); ++ if (ret) ++ goto error; ++ ++ ret = create_entry_bl33_info(bl33_ep); ++ if (ret) ++ goto error; ++ ++ scan_flat_dt_save_bootargs(env_get("bootargs")); ++ ++ cleanup_before_linux(); ++ ++ /* boot up a53mp*/ ++ boot_up_a53mp(); ++ ++ /*boot up a53up*/ ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++ (*(volatile uint64_t *)OS_SYS_CTRL_REG2) = (uint64_t)bl31_p; ++ (*(volatile uint64_t *)OS_SYS_CTRL_REG4) = (uint64_t)NULL; ++#else ++ (*(volatile uint64_t *)8) = (uint64_t)(uintptr_t)bl31_p; ++ (*(volatile uint64_t *)16) = (uint64_t)NULL; ++#endif ++ ++ void (*atf_entry)(void) = (void(*)(void))(uintptr_t)(bl31_pc); ++ atf_entry(); ++ ++error: ++ return ret; ++} +diff --git a/common/main.c b/common/main.c +index a94df7a..3f68ac3 100644 +--- a/common/main.c ++++ b/common/main.c +@@ -13,6 +13,9 @@ + #include + #include + #include ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++#include "cmd_timestamp.h" ++#endif + + /* + * Board-specific Platform code can reimplement show_boot_progress () if needed +@@ -44,6 +47,10 @@ void main_loop(void) + + bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); + ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ timestamp_mark("main_loop_enter", __LINE__); ++#endif ++ + if (IS_ENABLED(CONFIG_VERSION_VARIABLE)) + env_set("ver", version_string); /* set version variable */ + +@@ -59,6 +66,10 @@ void main_loop(void) + if (cli_process_fdt(&s)) + cli_secure_boot_cmd(s); + ++#if CONFIG_IS_ENABLED(CMD_TIMESTAMP) ++ timestamp_mark("main_loop_autoboot_cmd_start", __LINE__); ++#endif ++ + autoboot_command(s); + + cli_loop(); +diff --git a/common/usb.c b/common/usb.c +index d9bcb5a..fe39fdf 100644 +--- a/common/usb.c ++++ b/common/usb.c +@@ -140,6 +140,8 @@ int usb_stop(void) + if (usb_lowlevel_stop(i)) + printf("failed to stop USB controller %d\n", i); + } ++ /* FIXME:Wait for the stop operation to complete. */ ++ mdelay(100); + } + + return 0; +@@ -220,6 +222,8 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, + ALLOC_CACHE_ALIGN_BUFFER(struct devrequest, setup_packet, 1); + int err; + ++ /* FIXME:Avoid some devices not recognized. */ ++ dcache_disable(); + if ((timeout == 0) && (!asynch_allowed)) { + /* request for a asynch control pipe is not allowed */ + return -EINVAL; +@@ -255,6 +259,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, + if (dev->status) + return -1; + ++ dcache_enable();/* FIXME:dcache_disable() */ + return dev->act_len; + + } +@@ -267,6 +272,8 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, + int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) + { ++ /* FIXME:Prevent some U disk from reading and writing errors. */ ++ dcache_disable(); + if (len < 0) + return -EINVAL; + dev->status = USB_ST_NOT_PROC; /*not yet processed */ +@@ -278,6 +285,8 @@ int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, + mdelay(1); + } + *actual_length = dev->act_len; ++ ++ dcache_enable();/* FIXME:dcache_disable() */ + if (dev->status == 0) + return 0; + else +@@ -927,6 +936,14 @@ static int get_descriptor_len(struct usb_device *dev, int len, int expect_len) + + desc = (struct usb_device_descriptor *)tmpbuf; + ++ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, tmpbuf, 8); ++ if (err < 8) { ++ printf("\nUSB device not responding, giving up (status=%lX)\n", ++ dev->status); ++ return 1; ++ } ++ memcpy(&dev->descriptor, tmpbuf, 8); ++ + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, len); + if (err < expect_len) { + if (err < 0) { +diff --git a/common/usb_hub.c b/common/usb_hub.c +index 25c2ac4..c5ddf86 100644 +--- a/common/usb_hub.c ++++ b/common/usb_hub.c +@@ -296,6 +296,12 @@ static int usb_hub_port_reset(struct usb_device *dev, int port, + (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); + ++ if ((portchange & USB_PORT_STAT_C_CONNECTION) || ++ !(portstatus & USB_PORT_STAT_CONNECTION)) { ++ usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); ++ usb_clear_port_feature(dev, port + 1, USB_PORT_STAT_CONNECTION); ++ continue; ++ } + /* + * Perhaps we should check for the following here: + * - C_CONNECTION hasn't been set. +@@ -318,6 +324,7 @@ static int usb_hub_port_reset(struct usb_device *dev, int port, + + /* Switch to long reset delay for the next round */ + delay = HUB_LONG_RESET_TIME; ++ mdelay(delay); + } + + if (tries == MAX_TRIES) { +@@ -337,8 +344,10 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port) + ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1); + unsigned short portstatus; + int ret, speed; ++ int retry_count = 0; + + /* Check status */ ++re_enumerate: + ret = usb_get_port_status(dev, port + 1, portsts); + if (ret < 0) { + debug("get_port_status failed\n"); +@@ -369,7 +378,7 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port) + if (ret < 0) { + if (ret != -ENXIO) + printf("cannot reset port %i!?\n", port + 1); +- return ret; ++ goto port_reset; + } + + switch (portstatus & USB_PORT_STAT_SPEED_MASK) { +@@ -414,7 +423,14 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port) + #endif + if (ret < 0) { + debug("hub: disabling port %d\n", port + 1); ++port_reset: + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); ++ retry_count++; ++ if (retry_count <= 5) { ++ usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); ++ mdelay(1000); ++ goto re_enumerate; ++ } + } + + return ret; +diff --git a/common/usb_storage.c b/common/usb_storage.c +index 097b672..a189e24 100644 +--- a/common/usb_storage.c ++++ b/common/usb_storage.c +@@ -724,7 +724,7 @@ static int usb_stor_BBB_transport(struct scsi_cmd *srb, struct us_data *us) + return USB_STOR_TRANSPORT_FAILED; + } + if (!(us->flags & USB_READY)) +- mdelay(5); ++ mdelay(1); + pipein = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + pipeout = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + /* DATA phase + error handling */ +diff --git a/configs/ss927v100_defconfig b/configs/ss927v100_defconfig +new file mode 100644 +index 0000000..492b701 +--- /dev/null ++++ b/configs/ss927v100_defconfig +@@ -0,0 +1,1167 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss927v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss927v100" ++CONFIG_SYS_CONFIG_NAME="ss927v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++# CONFIG_TARGET_SS928V100 is not set ++CONFIG_TARGET_SS927V100=y ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++# CONFIG_CMD_TIMESTAMP is not set ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_ENV_SECT_SIZE=0x10000 ++CONFIG_IDENT_STRING="ss927v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_SPIFLASH_SPEED is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="mem=256M console=ttyAMA0,115200n8" ++# CONFIG_USE_BOOTCOMMAND is not set ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++CONFIG_HUSH_PARSER=y ++CONFIG_CMDLINE_EDITING=y ++CONFIG_AUTO_COMPLETE=y ++CONFIG_SYS_LONGHELP=y ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++CONFIG_CMD_BDI=y ++# CONFIG_CMD_CONFIG is not set ++CONFIG_CMD_CONSOLE=y ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++CONFIG_CMD_BOOTD=y ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++CONFIG_CMD_BOOTI=y ++CONFIG_BOOTM_LINUX=y ++CONFIG_BOOTM_NETBSD=y ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++CONFIG_BOOTM_PLAN9=y ++CONFIG_BOOTM_RTEMS=y ++CONFIG_BOOTM_VXWORKS=y ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++CONFIG_CMD_ELF=y ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++CONFIG_CMD_IMI=y ++# CONFIG_CMD_IMLS is not set ++CONFIG_CMD_XIMG=y ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++CONFIG_CMD_DDR_TRAINING=y ++ ++# ++# Compression commands ++# ++CONFIG_CMD_LZMADEC=y ++CONFIG_CMD_UNZIP=y ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++CONFIG_CMD_FLASH=y ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++CONFIG_CMD_LOADB=y ++CONFIG_CMD_LOADS=y ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_MTD is not set ++# CONFIG_CMD_NAND is not set ++# CONFIG_CMD_ONENAND is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_SF is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++CONFIG_CMD_ECHO=y ++CONFIG_CMD_ITEST=y ++CONFIG_CMD_SOURCE=y ++CONFIG_CMD_SETEXPR=y ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++CONFIG_CMD_CACHE=y ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_MTDPARTS is not set ++CONFIG_MTDIDS_DEFAULT="" ++CONFIG_MTDPARTS_DEFAULT="" ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_NAND=y ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++CONFIG_ENV_IS_IN_SPI_FLASH=y ++# CONFIG_USE_ENV_SPI_BUS is not set ++# CONFIG_USE_ENV_SPI_CS is not set ++# CONFIG_USE_ENV_SPI_MAX_HZ is not set ++# CONFIG_USE_ENV_SPI_MODE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++CONFIG_ENV_ADDR=0x80000 ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++# CONFIG_MMC is not set ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++CONFIG_MTD=y ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++CONFIG_FMC=y ++# CONFIG_BSP_NAND_SPL is not set ++CONFIG_MTD_RAW_NAND=y ++# CONFIG_SYS_NAND_USE_FLASH_BBT is not set ++# CONFIG_NAND_LPC32XX_SLC is not set ++# CONFIG_NAND_VF610_NFC is not set ++# CONFIG_NAND_PXA3XX is not set ++# CONFIG_NAND_ARASAN is not set ++CONFIG_FMC_SPI_NAND=y ++# CONFIG_FMC_NAND is not set ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_FMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_FMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_FMC100_PAGESIZE_AUTO_ECC_NONE is not set ++ ++# ++# Generic NAND options ++# ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++CONFIG_FMC_SPI_NOR=y ++# CONFIG_SPI_BLOCK_PROTECT is not set ++# CONFIG_DTR_MODE_SUPPORT is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++CONFIG_LZMA=y ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/configs/ss927v100_emmc_defconfig b/configs/ss927v100_emmc_defconfig +new file mode 100644 +index 0000000..60d5843 +--- /dev/null ++++ b/configs/ss927v100_emmc_defconfig +@@ -0,0 +1,1163 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss927v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss927v100" ++CONFIG_SYS_CONFIG_NAME="ss927v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++# CONFIG_TARGET_SS928V100 is not set ++CONFIG_TARGET_SS927V100=y ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++# CONFIG_CMD_TIMESTAMP is not set ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_IDENT_STRING="ss927v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++# CONFIG_USE_BOOTARGS is not set ++# CONFIG_USE_BOOTCOMMAND is not set ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++CONFIG_HUSH_PARSER=y ++CONFIG_CMDLINE_EDITING=y ++CONFIG_AUTO_COMPLETE=y ++CONFIG_SYS_LONGHELP=y ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++CONFIG_CMD_BDI=y ++# CONFIG_CMD_CONFIG is not set ++CONFIG_CMD_CONSOLE=y ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++CONFIG_CMD_BOOTD=y ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++CONFIG_CMD_BOOTI=y ++CONFIG_BOOTM_LINUX=y ++CONFIG_BOOTM_NETBSD=y ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++CONFIG_BOOTM_PLAN9=y ++CONFIG_BOOTM_RTEMS=y ++CONFIG_BOOTM_VXWORKS=y ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++CONFIG_CMD_ELF=y ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++CONFIG_CMD_IMI=y ++# CONFIG_CMD_IMLS is not set ++CONFIG_CMD_XIMG=y ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++CONFIG_CMD_DDR_TRAINING=y ++ ++# ++# Compression commands ++# ++CONFIG_CMD_LZMADEC=y ++CONFIG_CMD_UNZIP=y ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BCB is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++CONFIG_CMD_LOADB=y ++CONFIG_CMD_LOADS=y ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++CONFIG_CMD_ECHO=y ++CONFIG_CMD_ITEST=y ++CONFIG_CMD_SOURCE=y ++CONFIG_CMD_SETEXPR=y ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++CONFIG_CMD_CACHE=y ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_MMC=y ++# CONFIG_ENV_IS_IN_NAND is not set ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++CONFIG_MMC=y ++CONFIG_MMC_WRITE=y ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++CONFIG_MMC_QUIRKS=y ++CONFIG_MMC_HW_PARTITIONING=y ++# CONFIG_SUPPORT_EMMC_RPMB is not set ++# CONFIG_SUPPORT_EMMC_BOOT is not set ++# CONFIG_MMC_IO_VOLTAGE is not set ++# CONFIG_SPL_MMC_IO_VOLTAGE is not set ++CONFIG_MMC_HS400_ES_SUPPORT=y ++# CONFIG_SPL_MMC_HS400_ES_SUPPORT is not set ++CONFIG_MMC_HS400_SUPPORT=y ++# CONFIG_SPL_MMC_HS400_SUPPORT is not set ++CONFIG_MMC_HS200_SUPPORT=y ++# CONFIG_SPL_MMC_HS200_SUPPORT is not set ++CONFIG_MMC_VERBOSE=y ++# CONFIG_MMC_TRACE is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_MXC is not set ++# CONFIG_MMC_PCI is not set ++# CONFIG_MMC_OMAP_HS is not set ++CONFIG_MMC_SDHCI=y ++# CONFIG_MMC_SDHCI_SDMA is not set ++CONFIG_MMC_SDHCI_ADMA=y ++# CONFIG_SPL_MMC_SDHCI_ADMA is not set ++# CONFIG_MMC_SDHCI_BCMSTB is not set ++# CONFIG_MMC_SDHCI_IPROC is not set ++# CONFIG_MMC_SDHCI_KONA is not set ++# CONFIG_MMC_SDHCI_S5P is not set ++# CONFIG_MMC_SDHCI_SPEAR is not set ++# CONFIG_FTSDC010 is not set ++# CONFIG_MCI is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++# CONFIG_MTD is not set ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++# CONFIG_FMC is not set ++# CONFIG_MTD_RAW_NAND is not set ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++CONFIG_LZMA=y ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/configs/ss927v100_nand_defconfig b/configs/ss927v100_nand_defconfig +new file mode 100644 +index 0000000..bd9392b +--- /dev/null ++++ b/configs/ss927v100_nand_defconfig +@@ -0,0 +1,1167 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss927v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss927v100" ++CONFIG_SYS_CONFIG_NAME="ss927v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++# CONFIG_TARGET_SS928V100 is not set ++CONFIG_TARGET_SS927V100=y ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++# CONFIG_CMD_TIMESTAMP is not set ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_ENV_SECT_SIZE=0x10000 ++CONFIG_IDENT_STRING="ss927v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_SPIFLASH_SPEED is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="mem=256M console=ttyAMA0,115200n8" ++# CONFIG_USE_BOOTCOMMAND is not set ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++CONFIG_HUSH_PARSER=y ++CONFIG_CMDLINE_EDITING=y ++CONFIG_AUTO_COMPLETE=y ++CONFIG_SYS_LONGHELP=y ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++CONFIG_CMD_BDI=y ++# CONFIG_CMD_CONFIG is not set ++CONFIG_CMD_CONSOLE=y ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++CONFIG_CMD_BOOTD=y ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++CONFIG_CMD_BOOTI=y ++CONFIG_BOOTM_LINUX=y ++CONFIG_BOOTM_NETBSD=y ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++CONFIG_BOOTM_PLAN9=y ++CONFIG_BOOTM_RTEMS=y ++CONFIG_BOOTM_VXWORKS=y ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++CONFIG_CMD_ELF=y ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++CONFIG_CMD_IMI=y ++# CONFIG_CMD_IMLS is not set ++CONFIG_CMD_XIMG=y ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++CONFIG_CMD_DDR_TRAINING=y ++ ++# ++# Compression commands ++# ++CONFIG_CMD_LZMADEC=y ++CONFIG_CMD_UNZIP=y ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++CONFIG_CMD_FLASH=y ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++CONFIG_CMD_LOADB=y ++CONFIG_CMD_LOADS=y ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_MTD is not set ++# CONFIG_CMD_NAND is not set ++# CONFIG_CMD_ONENAND is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_SF is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++CONFIG_CMD_ECHO=y ++CONFIG_CMD_ITEST=y ++CONFIG_CMD_SOURCE=y ++CONFIG_CMD_SETEXPR=y ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++CONFIG_CMD_CACHE=y ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_MTDPARTS is not set ++CONFIG_MTDIDS_DEFAULT="" ++CONFIG_MTDPARTS_DEFAULT="" ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_NAND=y ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++CONFIG_ENV_IS_IN_SPI_FLASH=y ++# CONFIG_USE_ENV_SPI_BUS is not set ++# CONFIG_USE_ENV_SPI_CS is not set ++# CONFIG_USE_ENV_SPI_MAX_HZ is not set ++# CONFIG_USE_ENV_SPI_MODE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++CONFIG_ENV_ADDR=0x80000 ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++# CONFIG_MMC is not set ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++CONFIG_MTD=y ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++CONFIG_FMC=y ++# CONFIG_BSP_NAND_SPL is not set ++CONFIG_MTD_RAW_NAND=y ++# CONFIG_SYS_NAND_USE_FLASH_BBT is not set ++# CONFIG_NAND_LPC32XX_SLC is not set ++# CONFIG_NAND_VF610_NFC is not set ++# CONFIG_NAND_PXA3XX is not set ++# CONFIG_NAND_ARASAN is not set ++# CONFIG_FMC_SPI_NAND is not set ++CONFIG_FMC_NAND=y ++CONFIG_NAND_MAX_CHIP_NUM=1 ++# CONFIG_FMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_FMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_FMC100_PAGESIZE_AUTO_ECC_NONE is not set ++ ++# ++# Generic NAND options ++# ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++CONFIG_FMC_SPI_NOR=y ++# CONFIG_SPI_BLOCK_PROTECT is not set ++# CONFIG_DTR_MODE_SUPPORT is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++CONFIG_LZMA=y ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/configs/ss928v100_defconfig b/configs/ss928v100_defconfig +new file mode 100644 +index 0000000..bb0dc0a +--- /dev/null ++++ b/configs/ss928v100_defconfig +@@ -0,0 +1,1167 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss928v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss928v100" ++CONFIG_SYS_CONFIG_NAME="ss928v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++CONFIG_TARGET_SS928V100=y ++# CONFIG_TARGET_SS927V100 is not set ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++# CONFIG_CMD_TIMESTAMP is not set ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_ENV_SECT_SIZE=0x10000 ++CONFIG_IDENT_STRING="ss928v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_SPIFLASH_SPEED is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="mem=256M console=ttyAMA0,115200n8" ++# CONFIG_USE_BOOTCOMMAND is not set ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++CONFIG_HUSH_PARSER=y ++CONFIG_CMDLINE_EDITING=y ++CONFIG_AUTO_COMPLETE=y ++CONFIG_SYS_LONGHELP=y ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++CONFIG_CMD_BDI=y ++# CONFIG_CMD_CONFIG is not set ++CONFIG_CMD_CONSOLE=y ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++CONFIG_CMD_BOOTD=y ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++CONFIG_CMD_BOOTI=y ++CONFIG_BOOTM_LINUX=y ++CONFIG_BOOTM_NETBSD=y ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++CONFIG_BOOTM_PLAN9=y ++CONFIG_BOOTM_RTEMS=y ++CONFIG_BOOTM_VXWORKS=y ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++CONFIG_CMD_ELF=y ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++CONFIG_CMD_IMI=y ++# CONFIG_CMD_IMLS is not set ++CONFIG_CMD_XIMG=y ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++CONFIG_CMD_DDR_TRAINING=y ++ ++# ++# Compression commands ++# ++CONFIG_CMD_LZMADEC=y ++CONFIG_CMD_UNZIP=y ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++CONFIG_CMD_FLASH=y ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++CONFIG_CMD_LOADB=y ++CONFIG_CMD_LOADS=y ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_MTD is not set ++# CONFIG_CMD_NAND is not set ++# CONFIG_CMD_ONENAND is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_SF is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++CONFIG_CMD_ECHO=y ++CONFIG_CMD_ITEST=y ++CONFIG_CMD_SOURCE=y ++CONFIG_CMD_SETEXPR=y ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++CONFIG_CMD_CACHE=y ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_MTDPARTS is not set ++CONFIG_MTDIDS_DEFAULT="" ++CONFIG_MTDPARTS_DEFAULT="" ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_NAND=y ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++CONFIG_ENV_IS_IN_SPI_FLASH=y ++# CONFIG_USE_ENV_SPI_BUS is not set ++# CONFIG_USE_ENV_SPI_CS is not set ++# CONFIG_USE_ENV_SPI_MAX_HZ is not set ++# CONFIG_USE_ENV_SPI_MODE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++CONFIG_ENV_ADDR=0x80000 ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++# CONFIG_MMC is not set ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++CONFIG_MTD=y ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++CONFIG_FMC=y ++# CONFIG_BSP_NAND_SPL is not set ++CONFIG_MTD_RAW_NAND=y ++# CONFIG_SYS_NAND_USE_FLASH_BBT is not set ++# CONFIG_NAND_LPC32XX_SLC is not set ++# CONFIG_NAND_VF610_NFC is not set ++# CONFIG_NAND_PXA3XX is not set ++# CONFIG_NAND_ARASAN is not set ++CONFIG_FMC_SPI_NAND=y ++# CONFIG_FMC_NAND is not set ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_FMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_FMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_FMC100_PAGESIZE_AUTO_ECC_NONE is not set ++ ++# ++# Generic NAND options ++# ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++CONFIG_FMC_SPI_NOR=y ++# CONFIG_SPI_BLOCK_PROTECT is not set ++# CONFIG_DTR_MODE_SUPPORT is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++CONFIG_LZMA=y ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/configs/ss928v100_emmc_defconfig b/configs/ss928v100_emmc_defconfig +new file mode 100644 +index 0000000..1d03f8b +--- /dev/null ++++ b/configs/ss928v100_emmc_defconfig +@@ -0,0 +1,1163 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss928v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss928v100" ++CONFIG_SYS_CONFIG_NAME="ss928v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++CONFIG_TARGET_SS928V100=y ++# CONFIG_TARGET_SS927V100 is not set ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++# CONFIG_CMD_TIMESTAMP is not set ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_IDENT_STRING="ss928v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++# CONFIG_USE_BOOTARGS is not set ++# CONFIG_USE_BOOTCOMMAND is not set ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++CONFIG_HUSH_PARSER=y ++CONFIG_CMDLINE_EDITING=y ++CONFIG_AUTO_COMPLETE=y ++CONFIG_SYS_LONGHELP=y ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++CONFIG_CMD_BDI=y ++# CONFIG_CMD_CONFIG is not set ++CONFIG_CMD_CONSOLE=y ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++CONFIG_CMD_BOOTD=y ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++CONFIG_CMD_BOOTI=y ++CONFIG_BOOTM_LINUX=y ++CONFIG_BOOTM_NETBSD=y ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++CONFIG_BOOTM_PLAN9=y ++CONFIG_BOOTM_RTEMS=y ++CONFIG_BOOTM_VXWORKS=y ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++CONFIG_CMD_ELF=y ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++CONFIG_CMD_IMI=y ++# CONFIG_CMD_IMLS is not set ++CONFIG_CMD_XIMG=y ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++CONFIG_CMD_DDR_TRAINING=y ++ ++# ++# Compression commands ++# ++CONFIG_CMD_LZMADEC=y ++CONFIG_CMD_UNZIP=y ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BCB is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++CONFIG_CMD_LOADB=y ++CONFIG_CMD_LOADS=y ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++CONFIG_CMD_ECHO=y ++CONFIG_CMD_ITEST=y ++CONFIG_CMD_SOURCE=y ++CONFIG_CMD_SETEXPR=y ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++CONFIG_CMD_CACHE=y ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_MMC=y ++# CONFIG_ENV_IS_IN_NAND is not set ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++CONFIG_MMC=y ++CONFIG_MMC_WRITE=y ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++CONFIG_MMC_QUIRKS=y ++CONFIG_MMC_HW_PARTITIONING=y ++# CONFIG_SUPPORT_EMMC_RPMB is not set ++# CONFIG_SUPPORT_EMMC_BOOT is not set ++# CONFIG_MMC_IO_VOLTAGE is not set ++# CONFIG_SPL_MMC_IO_VOLTAGE is not set ++CONFIG_MMC_HS400_ES_SUPPORT=y ++# CONFIG_SPL_MMC_HS400_ES_SUPPORT is not set ++CONFIG_MMC_HS400_SUPPORT=y ++# CONFIG_SPL_MMC_HS400_SUPPORT is not set ++CONFIG_MMC_HS200_SUPPORT=y ++# CONFIG_SPL_MMC_HS200_SUPPORT is not set ++CONFIG_MMC_VERBOSE=y ++# CONFIG_MMC_TRACE is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_MXC is not set ++# CONFIG_MMC_PCI is not set ++# CONFIG_MMC_OMAP_HS is not set ++CONFIG_MMC_SDHCI=y ++# CONFIG_MMC_SDHCI_SDMA is not set ++CONFIG_MMC_SDHCI_ADMA=y ++# CONFIG_SPL_MMC_SDHCI_ADMA is not set ++# CONFIG_MMC_SDHCI_BCMSTB is not set ++# CONFIG_MMC_SDHCI_IPROC is not set ++# CONFIG_MMC_SDHCI_KONA is not set ++# CONFIG_MMC_SDHCI_S5P is not set ++# CONFIG_MMC_SDHCI_SPEAR is not set ++# CONFIG_FTSDC010 is not set ++# CONFIG_MCI is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++# CONFIG_MTD is not set ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++# CONFIG_FMC is not set ++# CONFIG_MTD_RAW_NAND is not set ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++CONFIG_LZMA=y ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/configs/ss928v100_emmc_mini_defconfig b/configs/ss928v100_emmc_mini_defconfig +new file mode 100644 +index 0000000..e7daffb +--- /dev/null ++++ b/configs/ss928v100_emmc_mini_defconfig +@@ -0,0 +1,1166 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss928v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss928v100" ++CONFIG_SYS_CONFIG_NAME="ss928v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++CONFIG_TARGET_SS928V100=y ++# CONFIG_TARGET_SS927V100 is not set ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++CONFIG_CMD_TIMESTAMP=y ++CONFIG_CMD_TIMESTAMP_TEST=y ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_IDENT_STRING="ss928v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="quiet initcall_debug timestamp=0x4000C800,0x3800 mem=128M console=ttyAMA0,115200 clk_ignore_unused root=/dev/mmcblk0p3 rootfstype=ext4 rw rootwait blkdevparts=mmcblk0:1M(boot),11M(kernel),96M(rootfs.ext4),1M(sample)" ++CONFIG_USE_BOOTCOMMAND=y ++CONFIG_BOOTCOMMAND="mmc read 0 0x44000000 0x36000 0x800;go_riscv 0x44000000;mmc read 0 0x50000000 0x800 0x5800;bootm 0x50000000" ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++# CONFIG_HUSH_PARSER is not set ++# CONFIG_CMDLINE_EDITING is not set ++# CONFIG_AUTO_COMPLETE is not set ++# CONFIG_SYS_LONGHELP is not set ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++# CONFIG_CMD_BDI is not set ++# CONFIG_CMD_CONFIG is not set ++# CONFIG_CMD_CONSOLE is not set ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++# CONFIG_CMD_BOOTD is not set ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++# CONFIG_CMD_BOOTI is not set ++# CONFIG_BOOTM_LINUX is not set ++# CONFIG_BOOTM_NETBSD is not set ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++# CONFIG_BOOTM_PLAN9 is not set ++# CONFIG_BOOTM_RTEMS is not set ++# CONFIG_BOOTM_VXWORKS is not set ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++# CONFIG_CMD_ELF is not set ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++# CONFIG_CMD_IMI is not set ++# CONFIG_CMD_IMLS is not set ++# CONFIG_CMD_XIMG is not set ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++# CONFIG_CMD_DDR_TRAINING is not set ++ ++# ++# Compression commands ++# ++# CONFIG_CMD_LZMADEC is not set ++# CONFIG_CMD_UNZIP is not set ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BCB is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++# CONFIG_CMD_LOADB is not set ++# CONFIG_CMD_LOADS is not set ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++# CONFIG_CMD_ECHO is not set ++# CONFIG_CMD_ITEST is not set ++# CONFIG_CMD_SOURCE is not set ++# CONFIG_CMD_SETEXPR is not set ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++# CONFIG_CMD_CACHE is not set ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_MMC=y ++# CONFIG_ENV_IS_IN_NAND is not set ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++CONFIG_MMC=y ++CONFIG_MMC_WRITE=y ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++CONFIG_MMC_QUIRKS=y ++CONFIG_MMC_HW_PARTITIONING=y ++# CONFIG_SUPPORT_EMMC_RPMB is not set ++# CONFIG_SUPPORT_EMMC_BOOT is not set ++# CONFIG_MMC_IO_VOLTAGE is not set ++# CONFIG_SPL_MMC_IO_VOLTAGE is not set ++CONFIG_MMC_HS400_ES_SUPPORT=y ++# CONFIG_SPL_MMC_HS400_ES_SUPPORT is not set ++CONFIG_MMC_HS400_SUPPORT=y ++# CONFIG_SPL_MMC_HS400_SUPPORT is not set ++CONFIG_MMC_HS200_SUPPORT=y ++# CONFIG_SPL_MMC_HS200_SUPPORT is not set ++CONFIG_MMC_VERBOSE=y ++# CONFIG_MMC_TRACE is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_MXC is not set ++# CONFIG_MMC_PCI is not set ++# CONFIG_MMC_OMAP_HS is not set ++CONFIG_MMC_SDHCI=y ++# CONFIG_MMC_SDHCI_SDMA is not set ++CONFIG_MMC_SDHCI_ADMA=y ++# CONFIG_SPL_MMC_SDHCI_ADMA is not set ++# CONFIG_MMC_SDHCI_BCMSTB is not set ++# CONFIG_MMC_SDHCI_IPROC is not set ++# CONFIG_MMC_SDHCI_KONA is not set ++# CONFIG_MMC_SDHCI_S5P is not set ++# CONFIG_MMC_SDHCI_SPEAR is not set ++# CONFIG_FTSDC010 is not set ++# CONFIG_MCI is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++# CONFIG_MTD is not set ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++# CONFIG_FMC is not set ++# CONFIG_MTD_RAW_NAND is not set ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++# CONFIG_LZMA is not set ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/configs/ss928v100_mini_defconfig b/configs/ss928v100_mini_defconfig +new file mode 100644 +index 0000000..dd4bfa1 +--- /dev/null ++++ b/configs/ss928v100_mini_defconfig +@@ -0,0 +1,1169 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss928v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss928v100" ++CONFIG_SYS_CONFIG_NAME="ss928v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++CONFIG_TARGET_SS928V100=y ++# CONFIG_TARGET_SS927V100 is not set ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++CONFIG_CMD_TIMESTAMP=y ++CONFIG_CMD_TIMESTAMP_TEST=y ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_ENV_SECT_SIZE=0x10000 ++CONFIG_IDENT_STRING="ss928v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_SPIFLASH_SPEED is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="quiet initcall_debug timestamp=0x4000C800,0x3800 mem=128M console=ttyAMA0,115200 clk_ignore_unused ubi.mtd=2 root=ubi0:ubifs rootfstype=ubifs rw mtdparts=nand:1M(boot),11M(kernel),32M(rootfs.ubifs),1M(sample)" ++CONFIG_USE_BOOTCOMMAND=y ++CONFIG_BOOTCOMMAND="nand read 0x44000000 0x2c00000 0x100000;go_riscv 0x44000000;nand read 0x50000000 0x100000 0xb00000;bootm 0x50000000" ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++# CONFIG_HUSH_PARSER is not set ++# CONFIG_CMDLINE_EDITING is not set ++# CONFIG_AUTO_COMPLETE is not set ++# CONFIG_SYS_LONGHELP is not set ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++# CONFIG_CMD_BDI is not set ++# CONFIG_CMD_CONFIG is not set ++# CONFIG_CMD_CONSOLE is not set ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++# CONFIG_CMD_BOOTD is not set ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++# CONFIG_CMD_BOOTI is not set ++# CONFIG_BOOTM_LINUX is not set ++# CONFIG_BOOTM_NETBSD is not set ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++# CONFIG_BOOTM_PLAN9 is not set ++# CONFIG_BOOTM_RTEMS is not set ++# CONFIG_BOOTM_VXWORKS is not set ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++# CONFIG_CMD_ELF is not set ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++# CONFIG_CMD_IMI is not set ++# CONFIG_CMD_IMLS is not set ++# CONFIG_CMD_XIMG is not set ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++# CONFIG_CMD_DDR_TRAINING is not set ++ ++# ++# Compression commands ++# ++# CONFIG_CMD_LZMADEC is not set ++# CONFIG_CMD_UNZIP is not set ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++CONFIG_CMD_FLASH=y ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++# CONFIG_CMD_LOADB is not set ++# CONFIG_CMD_LOADS is not set ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_MTD is not set ++# CONFIG_CMD_NAND is not set ++# CONFIG_CMD_ONENAND is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_SF is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++# CONFIG_CMD_ECHO is not set ++# CONFIG_CMD_ITEST is not set ++# CONFIG_CMD_SOURCE is not set ++# CONFIG_CMD_SETEXPR is not set ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++# CONFIG_CMD_CACHE is not set ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_MTDPARTS is not set ++CONFIG_MTDIDS_DEFAULT="" ++CONFIG_MTDPARTS_DEFAULT="" ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_NAND=y ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++CONFIG_ENV_IS_IN_SPI_FLASH=y ++# CONFIG_USE_ENV_SPI_BUS is not set ++# CONFIG_USE_ENV_SPI_CS is not set ++# CONFIG_USE_ENV_SPI_MAX_HZ is not set ++# CONFIG_USE_ENV_SPI_MODE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++CONFIG_ENV_ADDR=0x80000 ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++# CONFIG_MMC is not set ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++CONFIG_MTD=y ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++CONFIG_FMC=y ++# CONFIG_BSP_NAND_SPL is not set ++CONFIG_MTD_RAW_NAND=y ++# CONFIG_SYS_NAND_USE_FLASH_BBT is not set ++# CONFIG_NAND_LPC32XX_SLC is not set ++# CONFIG_NAND_VF610_NFC is not set ++# CONFIG_NAND_PXA3XX is not set ++# CONFIG_NAND_ARASAN is not set ++CONFIG_FMC_SPI_NAND=y ++# CONFIG_FMC_NAND is not set ++CONFIG_SPI_NAND_MAX_CHIP_NUM=1 ++# CONFIG_FMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_FMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_FMC100_PAGESIZE_AUTO_ECC_NONE is not set ++ ++# ++# Generic NAND options ++# ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++CONFIG_FMC_SPI_NOR=y ++# CONFIG_SPI_BLOCK_PROTECT is not set ++# CONFIG_DTR_MODE_SUPPORT is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++# CONFIG_LZMA is not set ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/configs/ss928v100_nand_defconfig b/configs/ss928v100_nand_defconfig +new file mode 100644 +index 0000000..06255ee +--- /dev/null ++++ b/configs/ss928v100_nand_defconfig +@@ -0,0 +1,1167 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# U-Boot 2020.01 Configuration ++# ++CONFIG_CREATE_ARCH_SYMLINK=y ++# CONFIG_ARC is not set ++CONFIG_ARM=y ++# CONFIG_M68K is not set ++# CONFIG_MICROBLAZE is not set ++# CONFIG_MIPS is not set ++# CONFIG_NDS32 is not set ++# CONFIG_NIOS2 is not set ++# CONFIG_PPC is not set ++# CONFIG_RISCV is not set ++# CONFIG_SANDBOX is not set ++# CONFIG_SH is not set ++# CONFIG_X86 is not set ++# CONFIG_XTENSA is not set ++CONFIG_SYS_ARCH="arm" ++CONFIG_SYS_CPU="armv8" ++CONFIG_SYS_SOC="ss928v100" ++CONFIG_SYS_VENDOR="vendor" ++CONFIG_SYS_BOARD="ss928v100" ++CONFIG_SYS_CONFIG_NAME="ss928v100" ++# CONFIG_SYS_ICACHE_OFF is not set ++# CONFIG_SYS_DCACHE_OFF is not set ++ ++# ++# ARM architecture ++# ++CONFIG_ARM64=y ++# CONFIG_POSITION_INDEPENDENT is not set ++# CONFIG_INIT_SP_RELATIVE is not set ++CONFIG_STATIC_RELA=y ++CONFIG_DMA_ADDR_T_64BIT=y ++CONFIG_ARM_ASM_UNIFIED=y ++# CONFIG_SYS_ARM_CACHE_CP15 is not set ++# CONFIG_SYS_ARM_MMU is not set ++# CONFIG_SYS_ARM_MPU is not set ++CONFIG_SYS_ARM_ARCH=8 ++CONFIG_SYS_CACHE_SHIFT_6=y ++CONFIG_SYS_CACHELINE_SIZE=64 ++# CONFIG_ARCH_CPU_INIT is not set ++# CONFIG_SYS_ARCH_TIMER is not set ++CONFIG_ARM_SMCCC=y ++# CONFIG_SEMIHOSTING is not set ++# CONFIG_SYS_L2CACHE_OFF is not set ++# CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK is not set ++# CONFIG_SET_STACK_SIZE is not set ++CONFIG_ARM64_SUPPORT_AARCH32=y ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_TARGET_EDB93XX is not set ++# CONFIG_TARGET_ASPENITE is not set ++# CONFIG_TARGET_GPLUGD is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_KIRKWOOD is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_TARGET_APF27 is not set ++# CONFIG_ORION5X is not set ++# CONFIG_TARGET_SPEAR300 is not set ++# CONFIG_TARGET_SPEAR310 is not set ++# CONFIG_TARGET_SPEAR320 is not set ++# CONFIG_TARGET_SPEAR600 is not set ++# CONFIG_TARGET_STV0991 is not set ++# CONFIG_TARGET_X600 is not set ++# CONFIG_TARGET_WOODBURN is not set ++# CONFIG_TARGET_WOODBURN_SD is not set ++# CONFIG_TARGET_FLEA3 is not set ++# CONFIG_TARGET_MX35PDK is not set ++# CONFIG_ARCH_BCM283X is not set ++# CONFIG_ARCH_BCM63158 is not set ++# CONFIG_ARCH_BCM6858 is not set ++# CONFIG_TARGET_VEXPRESS_CA15_TC2 is not set ++# CONFIG_ARCH_BCMSTB is not set ++# CONFIG_TARGET_VEXPRESS_CA5X2 is not set ++# CONFIG_TARGET_VEXPRESS_CA9X4 is not set ++# CONFIG_TARGET_BCM23550_W1D is not set ++# CONFIG_TARGET_BCM28155_AP is not set ++# CONFIG_TARGET_BCMCYGNUS is not set ++# CONFIG_TARGET_BCMNSP is not set ++# CONFIG_TARGET_BCMNS2 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_S5PC1XX is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_K3 is not set ++# CONFIG_ARCH_OMAP2PLUS is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MEDIATEK is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_IMX8 is not set ++# CONFIG_ARCH_IMX8M is not set ++# CONFIG_ARCH_MX23 is not set ++# CONFIG_ARCH_MX25 is not set ++# CONFIG_ARCH_MX28 is not set ++# CONFIG_ARCH_MX31 is not set ++# CONFIG_ARCH_MX7ULP is not set ++# CONFIG_ARCH_MX7 is not set ++# CONFIG_ARCH_MX6 is not set ++# CONFIG_ARCH_MX5 is not set ++# CONFIG_ARCH_OWL is not set ++# CONFIG_ARCH_QEMU is not set ++# CONFIG_ARCH_RMOBILE is not set ++# CONFIG_TARGET_S32V234EVB is not set ++# CONFIG_ARCH_SNAPDRAGON is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_VERSAL is not set ++# CONFIG_ARCH_VF610 is not set ++# CONFIG_ARCH_ZYNQ is not set ++# CONFIG_ARCH_ZYNQMP_R5 is not set ++# CONFIG_ARCH_ZYNQMP is not set ++# CONFIG_TEGRA is not set ++# CONFIG_TARGET_VEXPRESS64_AEMV8A is not set ++# CONFIG_TARGET_VEXPRESS64_BASE_FVP is not set ++# CONFIG_TARGET_VEXPRESS64_JUNO is not set ++# CONFIG_TARGET_LS2080A_EMU is not set ++# CONFIG_TARGET_LS2080A_SIMU is not set ++# CONFIG_TARGET_LS1088AQDS is not set ++# CONFIG_TARGET_LS2080AQDS is not set ++# CONFIG_TARGET_LS2080ARDB is not set ++# CONFIG_TARGET_LS2081ARDB is not set ++# CONFIG_TARGET_LX2160ARDB is not set ++# CONFIG_TARGET_LX2160AQDS is not set ++# CONFIG_TARGET_HIKEY is not set ++CONFIG_TARGET_SS928V100=y ++# CONFIG_TARGET_SS927V100 is not set ++# CONFIG_ARCH_VENDOR is not set ++# CONFIG_TARGET_HIKEY960 is not set ++# CONFIG_TARGET_POPLAR is not set ++# CONFIG_TARGET_LS1012AQDS is not set ++# CONFIG_TARGET_LS1012ARDB is not set ++# CONFIG_TARGET_LS1012A2G5RDB is not set ++# CONFIG_TARGET_LS1012AFRWY is not set ++# CONFIG_TARGET_LS1012AFRDM is not set ++# CONFIG_TARGET_LS1028AQDS is not set ++# CONFIG_TARGET_LS1028ARDB is not set ++# CONFIG_TARGET_LS1088ARDB is not set ++# CONFIG_TARGET_LS1021AQDS is not set ++# CONFIG_TARGET_LS1021ATWR is not set ++# CONFIG_TARGET_LS1021ATSN is not set ++# CONFIG_TARGET_LS1021AIOT is not set ++# CONFIG_TARGET_LS1043AQDS is not set ++# CONFIG_TARGET_LS1043ARDB is not set ++# CONFIG_TARGET_LS1046AQDS is not set ++# CONFIG_TARGET_LS1046ARDB is not set ++# CONFIG_TARGET_LS1046AFRWY is not set ++# CONFIG_TARGET_COLIBRI_PXA270 is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_STM32 is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_STM32MP is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_TARGET_THUNDERX_88XX is not set ++# CONFIG_ARCH_ASPEED is not set ++# CONFIG_TARGET_DURIAN is not set ++CONFIG_SYS_TEXT_BASE=0x48800000 ++# CONFIG_DISABLE_INTERRUPTS is not set ++# CONFIG_LOW_DELAY_INITIALIZATION is not set ++# CONFIG_INIT_TIMER_EARLY is not set ++# CONFIG_CMD_TIMESTAMP is not set ++CONFIG_TIME_ADDR_OFFSET=0xC800 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_ENV_OFFSET=0x80000 ++CONFIG_ERR_PTR_OFFSET=0x0 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_BOOTSTAGE_STASH_ADDR=0 ++CONFIG_ENV_SECT_SIZE=0x10000 ++CONFIG_IDENT_STRING="ss928v100" ++# CONFIG_ARMV8_MULTIENTRY is not set ++# CONFIG_ARMV8_SET_SMPEN is not set ++ ++# ++# ARMv8 secure monitor firmware ++# ++# CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_SPL_ARMV8_SEC_FIRMWARE_SUPPORT is not set ++# CONFIG_ARMV8_PSCI is not set ++# CONFIG_ARMV8_EA_EL3_FIRST is not set ++CONFIG_CSF_SIZE=0x2060 ++# CONFIG_CMD_DEKBLOB is not set ++# CONFIG_CMD_HDMIDETECT is not set ++CONFIG_IMX_DCD_ADDR=0x00910000 ++ ++# ++# ARM debug ++# ++# CONFIG_DEBUG_UART is not set ++# CONFIG_AHCI is not set ++ ++# ++# General setup ++# ++CONFIG_LOCALVERSION="" ++CONFIG_LOCALVERSION_AUTO=y ++CONFIG_CC_OPTIMIZE_FOR_SIZE=y ++# CONFIG_DISTRO_DEFAULTS is not set ++# CONFIG_ENV_VARS_UBOOT_CONFIG is not set ++# CONFIG_SYS_BOOT_GET_CMDLINE is not set ++# CONFIG_SYS_BOOT_GET_KBD is not set ++CONFIG_SYS_MALLOC_F=y ++CONFIG_EXPERT=y ++CONFIG_SYS_MALLOC_CLEAR_ON_INIT=y ++# CONFIG_TOOLS_DEBUG is not set ++CONFIG_PHYS_64BIT=y ++CONFIG_BUILD_TARGET="" ++# CONFIG_SYS_CUSTOM_LDSCRIPT is not set ++ ++# ++# Boot images ++# ++# CONFIG_ANDROID_BOOT_IMAGE is not set ++CONFIG_FIT=y ++CONFIG_FIT_EXTERNAL_OFFSET=0x0 ++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y ++CONFIG_FIT_FULL_CHECK=y ++# CONFIG_FIT_SIGNATURE is not set ++# CONFIG_FIT_CIPHER is not set ++# CONFIG_FIT_VERBOSE is not set ++# CONFIG_FIT_BEST_MATCH is not set ++CONFIG_LEGACY_IMAGE_FORMAT=y ++CONFIG_SYS_EXTRA_OPTIONS="" ++CONFIG_ARCH_FIXUP_FDT_MEMORY=y ++ ++# ++# API ++# ++# CONFIG_API is not set ++ ++# ++# Boot timing ++# ++# CONFIG_BOOTSTAGE is not set ++CONFIG_BOOTSTAGE_RECORD_COUNT=30 ++CONFIG_SPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_TPL_BOOTSTAGE_RECORD_COUNT=5 ++CONFIG_BOOTSTAGE_STASH_SIZE=4096 ++# CONFIG_SHOW_BOOT_PROGRESS is not set ++ ++# ++# Boot media ++# ++# CONFIG_NAND_BOOT is not set ++# CONFIG_ONENAND_BOOT is not set ++# CONFIG_QSPI_BOOT is not set ++# CONFIG_SATA_BOOT is not set ++# CONFIG_SD_BOOT is not set ++# CONFIG_SPI_BOOT is not set ++CONFIG_BOOTDELAY=2 ++ ++# ++# vendor_setup ++# ++# CONFIG_VENDOR_MC is not set ++# CONFIG_VENDOR_SPIFLASH_SPEED is not set ++# CONFIG_VENDOR_UPGRADE_BY_SEGMENT is not set ++# CONFIG_BSP_DISABLE_CONSOLE is not set ++# CONFIG_BSP_DISABLE_DOWNLOAD is not set ++# CONFIG_DELAY_ENVIRONMENT is not set ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="mem=256M console=ttyAMA0,115200n8" ++# CONFIG_USE_BOOTCOMMAND is not set ++# CONFIG_USE_PREBOOT is not set ++ ++# ++# Console ++# ++# CONFIG_CONSOLE_RECORD is not set ++# CONFIG_DISABLE_CONSOLE is not set ++CONFIG_LOGLEVEL=4 ++CONFIG_SPL_LOGLEVEL=4 ++CONFIG_TPL_LOGLEVEL=4 ++# CONFIG_SILENT_CONSOLE is not set ++# CONFIG_PRE_CONSOLE_BUFFER is not set ++# CONFIG_CONSOLE_MUX is not set ++# CONFIG_SYS_CONSOLE_IS_IN_ENV is not set ++# CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE is not set ++# CONFIG_SYS_CONSOLE_ENV_OVERWRITE is not set ++# CONFIG_SYS_CONSOLE_INFO_QUIET is not set ++# CONFIG_SYS_STDIO_DEREGISTER is not set ++ ++# ++# Logging ++# ++# CONFIG_LOG is not set ++CONFIG_LOG_DEFAULT_LEVEL=6 ++# CONFIG_SUPPORT_RAW_INITRD is not set ++CONFIG_DEFAULT_FDT_FILE="" ++CONFIG_KERNEL_LOAD_ADDR=0x50080000 ++# CONFIG_MISC_INIT_R is not set ++# CONFIG_VERSION_VARIABLE is not set ++# CONFIG_BOARD_LATE_INIT is not set ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++# CONFIG_DISPLAY_BOARDINFO_LATE is not set ++# CONFIG_BOUNCE_BUFFER is not set ++# CONFIG_BOARD_TYPES is not set ++ ++# ++# Start-up hooks ++# ++# CONFIG_ARCH_EARLY_INIT_R is not set ++# CONFIG_ARCH_MISC_INIT is not set ++# CONFIG_BOARD_EARLY_INIT_F is not set ++# CONFIG_BOARD_EARLY_INIT_R is not set ++# CONFIG_LAST_STAGE_INIT is not set ++ ++# ++# Security support ++# ++CONFIG_HASH=y ++# CONFIG_SECURE_BOOT_SUPPORT is not set ++ ++# ++# Update support ++# ++# CONFIG_UPDATE_TFTP is not set ++# CONFIG_ANDROID_AB is not set ++ ++# ++# Blob list ++# ++# CONFIG_BLOBLIST is not set ++ ++# ++# SPL / TPL ++# ++CONFIG_SPL_SYS_STACK_F_CHECK_BYTE=0xaa ++# CONFIG_SPL_SYS_REPORT_STACK_F_USAGE is not set ++ ++# ++# Command line interface ++# ++CONFIG_CMDLINE=y ++CONFIG_HUSH_PARSER=y ++CONFIG_CMDLINE_EDITING=y ++CONFIG_AUTO_COMPLETE=y ++CONFIG_SYS_LONGHELP=y ++CONFIG_SYS_PROMPT="# " ++CONFIG_SYS_XTRACE="y" ++ ++# ++# Autoboot options ++# ++CONFIG_AUTOBOOT=y ++# CONFIG_AUTOBOOT_KEYED is not set ++# CONFIG_AUTOBOOT_USE_MENUKEY is not set ++ ++# ++# Commands ++# ++ ++# ++# Info commands ++# ++CONFIG_CMD_BDI=y ++# CONFIG_CMD_CONFIG is not set ++CONFIG_CMD_CONSOLE=y ++# CONFIG_CMD_CPU is not set ++# CONFIG_CMD_LICENSE is not set ++ ++# ++# Boot commands ++# ++CONFIG_CMD_BOOTD=y ++CONFIG_CMD_BOOTM=y ++# CONFIG_CMD_BOOTZ is not set ++CONFIG_CMD_BOOTI=y ++CONFIG_BOOTM_LINUX=y ++CONFIG_BOOTM_NETBSD=y ++# CONFIG_BOOTM_OPENRTOS is not set ++# CONFIG_BOOTM_OSE is not set ++CONFIG_BOOTM_PLAN9=y ++CONFIG_BOOTM_RTEMS=y ++CONFIG_BOOTM_VXWORKS=y ++# CONFIG_CMD_BOOTMENU is not set ++# CONFIG_CMD_DTIMG is not set ++CONFIG_CMD_ELF=y ++CONFIG_CMD_GO=y ++CONFIG_CMD_RUN=y ++CONFIG_CMD_IMI=y ++# CONFIG_CMD_IMLS is not set ++CONFIG_CMD_XIMG=y ++# CONFIG_CMD_FITUPD is not set ++# CONFIG_CMD_THOR_DOWNLOAD is not set ++# CONFIG_CMD_ZBOOT is not set ++ ++# ++# Environment commands ++# ++# CONFIG_CMD_ASKENV is not set ++CONFIG_CMD_EXPORTENV=y ++CONFIG_CMD_IMPORTENV=y ++CONFIG_CMD_EDITENV=y ++# CONFIG_CMD_GREPENV is not set ++CONFIG_CMD_SAVEENV=y ++# CONFIG_CMD_ERASEENV is not set ++CONFIG_CMD_ENV_EXISTS=y ++# CONFIG_CMD_ENV_CALLBACK is not set ++# CONFIG_CMD_ENV_FLAGS is not set ++# CONFIG_CMD_NVEDIT_INFO is not set ++# CONFIG_CMD_COPYENV is not set ++ ++# ++# Memory commands ++# ++# CONFIG_CMD_BINOP is not set ++CONFIG_CMD_CRC32=y ++# CONFIG_CRC32_VERIFY is not set ++# CONFIG_CMD_EEPROM is not set ++# CONFIG_LOOPW is not set ++# CONFIG_CMD_MD5SUM is not set ++# CONFIG_CMD_MEMINFO is not set ++CONFIG_CMD_MEMORY=y ++# CONFIG_MX_CYCLIC is not set ++# CONFIG_CMD_MEMTEST is not set ++# CONFIG_CMD_MX_CYCLIC is not set ++# CONFIG_CMD_SHA1SUM is not set ++# CONFIG_CMD_STRINGS is not set ++CONFIG_CMD_DDR_TRAINING=y ++ ++# ++# Compression commands ++# ++CONFIG_CMD_LZMADEC=y ++CONFIG_CMD_UNZIP=y ++# CONFIG_CMD_ZIP is not set ++# CONFIG_CMD_CREAD is not set ++# CONFIG_CMD_UGZIP is not set ++ ++# ++# Device access commands ++# ++# CONFIG_CMD_ARMFLASH is not set ++# CONFIG_CMD_ADC is not set ++# CONFIG_CMD_BIND is not set ++# CONFIG_CMD_CLK is not set ++# CONFIG_CMD_DEMO is not set ++# CONFIG_CMD_DFU is not set ++# CONFIG_CMD_DM is not set ++# CONFIG_CMD_FDC is not set ++CONFIG_CMD_FLASH=y ++# CONFIG_CMD_FPGAD is not set ++# CONFIG_CMD_FUSE is not set ++# CONFIG_CMD_GPIO is not set ++# CONFIG_CMD_GPT is not set ++# CONFIG_RANDOM_UUID is not set ++# CONFIG_CMD_IDE is not set ++# CONFIG_CMD_IO is not set ++# CONFIG_CMD_IOTRACE is not set ++# CONFIG_CMD_I2C is not set ++CONFIG_CMD_LOADB=y ++CONFIG_CMD_LOADS=y ++# CONFIG_CMD_MMC is not set ++# CONFIG_CMD_MTD is not set ++# CONFIG_CMD_NAND is not set ++# CONFIG_CMD_ONENAND is not set ++# CONFIG_CMD_OSD is not set ++# CONFIG_CMD_PART is not set ++# CONFIG_CMD_PCI is not set ++# CONFIG_CMD_PINMUX is not set ++# CONFIG_CMD_POWEROFF is not set ++# CONFIG_CMD_READ is not set ++# CONFIG_CMD_SATA is not set ++# CONFIG_CMD_SAVES is not set ++# CONFIG_CMD_SCSI is not set ++# CONFIG_CMD_SDRAM is not set ++# CONFIG_CMD_SF is not set ++# CONFIG_CMD_TSI148 is not set ++# CONFIG_CMD_UNIVERSE is not set ++CONFIG_CMD_USB=y ++# CONFIG_CMD_USB_SDP is not set ++# CONFIG_CMD_USB_MASS_STORAGE is not set ++ ++# ++# Shell scripting commands ++# ++CONFIG_CMD_ECHO=y ++CONFIG_CMD_ITEST=y ++CONFIG_CMD_SOURCE=y ++CONFIG_CMD_SETEXPR=y ++ ++# ++# Android support commands ++# ++CONFIG_CMD_NET=y ++CONFIG_CMD_BOOTP=y ++CONFIG_CMD_DHCP=y ++CONFIG_BOOTP_BOOTPATH=y ++CONFIG_BOOTP_DNS=y ++# CONFIG_BOOTP_DNS2 is not set ++CONFIG_BOOTP_GATEWAY=y ++CONFIG_BOOTP_HOSTNAME=y ++# CONFIG_BOOTP_PREFER_SERVERIP is not set ++CONFIG_BOOTP_SUBNETMASK=y ++# CONFIG_BOOTP_NTPSERVER is not set ++# CONFIG_CMD_PCAP is not set ++CONFIG_BOOTP_VCI_STRING="U-Boot.armv8" ++CONFIG_CMD_TFTPBOOT=y ++# CONFIG_CMD_TFTPPUT is not set ++# CONFIG_CMD_TFTPSRV is not set ++CONFIG_NET_TFTP_VARS=y ++# CONFIG_CMD_RARP is not set ++CONFIG_CMD_NFS=y ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++# CONFIG_CMD_CDP is not set ++# CONFIG_CMD_SNTP is not set ++# CONFIG_CMD_DNS is not set ++# CONFIG_CMD_LINK_LOCAL is not set ++# CONFIG_CMD_ETHSW is not set ++# CONFIG_CMD_PXE is not set ++# CONFIG_CMD_WOL is not set ++ ++# ++# Misc commands ++# ++# CONFIG_CMD_BSP is not set ++CONFIG_CMD_CACHE=y ++# CONFIG_CMD_CONITRACE is not set ++# CONFIG_CMD_EXCEPTION is not set ++# CONFIG_CMD_DATE is not set ++# CONFIG_CMD_TIME is not set ++# CONFIG_CMD_GETTIME is not set ++CONFIG_CMD_MISC=y ++# CONFIG_MP is not set ++# CONFIG_CMD_TIMER is not set ++# CONFIG_CMD_SYSBOOT is not set ++# CONFIG_CMD_QFW is not set ++# CONFIG_CMD_TERMINAL is not set ++# CONFIG_CMD_UUID is not set ++ ++# ++# TI specific command line interface ++# ++# CONFIG_CMD_DDR3 is not set ++ ++# ++# Power commands ++# ++ ++# ++# Security commands ++# ++# CONFIG_CMD_AES is not set ++# CONFIG_CMD_BLOB is not set ++# CONFIG_CMD_HASH is not set ++# CONFIG_CMD_HVC is not set ++# CONFIG_CMD_SMC is not set ++ ++# ++# Firmware commands ++# ++ ++# ++# Filesystem commands ++# ++# CONFIG_CMD_BTRFS is not set ++# CONFIG_CMD_EXT2 is not set ++# CONFIG_CMD_EXT4 is not set ++# CONFIG_CMD_FAT is not set ++# CONFIG_CMD_FS_GENERIC is not set ++# CONFIG_CMD_FS_UUID is not set ++# CONFIG_CMD_JFFS2 is not set ++# CONFIG_CMD_MTDPARTS is not set ++CONFIG_MTDIDS_DEFAULT="" ++CONFIG_MTDPARTS_DEFAULT="" ++# CONFIG_CMD_REISER is not set ++# CONFIG_CMD_ZFS is not set ++ ++# ++# Debug commands ++# ++# CONFIG_CMD_BEDBUG is not set ++# CONFIG_CMD_DIAG is not set ++# CONFIG_CMD_LOG is not set ++# CONFIG_CMD_TRACE is not set ++# CONFIG_CMD_UBI is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITIONS=y ++# CONFIG_MAC_PARTITION is not set ++CONFIG_DOS_PARTITION=y ++# CONFIG_ISO_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_EFI_PARTITION is not set ++# CONFIG_PARTITION_UUIDS is not set ++CONFIG_SUPPORT_OF_CONTROL=y ++ ++# ++# Device Tree Control ++# ++# CONFIG_OF_CONTROL is not set ++# CONFIG_OF_BOARD_FIXUP is not set ++# CONFIG_MULTI_DTB_FIT is not set ++CONFIG_MKIMAGE_DTC_PATH="dtc" ++ ++# ++# Environment ++# ++# CONFIG_ENV_IS_NOWHERE is not set ++# CONFIG_ENV_IS_IN_EEPROM is not set ++# CONFIG_ENV_IS_IN_FAT is not set ++# CONFIG_ENV_IS_IN_EXT4 is not set ++# CONFIG_ENV_IS_IN_FLASH is not set ++CONFIG_ENV_IS_IN_NAND=y ++# CONFIG_ENV_IS_IN_NVRAM is not set ++# CONFIG_ENV_IS_IN_ONENAND is not set ++# CONFIG_ENV_IS_IN_REMOTE is not set ++CONFIG_ENV_IS_IN_SPI_FLASH=y ++# CONFIG_USE_ENV_SPI_BUS is not set ++# CONFIG_USE_ENV_SPI_CS is not set ++# CONFIG_USE_ENV_SPI_MAX_HZ is not set ++# CONFIG_USE_ENV_SPI_MODE is not set ++# CONFIG_SYS_REDUNDAND_ENVIRONMENT is not set ++CONFIG_ENV_ADDR=0x80000 ++# CONFIG_SYS_RELOC_GD_ENV_ADDR is not set ++# CONFIG_USE_DEFAULT_ENV_FILE is not set ++# CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG is not set ++CONFIG_NET=y ++# CONFIG_NET_RANDOM_ETHADDR is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_IP_DEFRAG is not set ++CONFIG_TFTP_BLOCKSIZE=1468 ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_DM=y ++CONFIG_DM_WARN=y ++# CONFIG_DM_DEBUG is not set ++CONFIG_DM_DEVICE_REMOVE=y ++CONFIG_DM_STDIO=y ++CONFIG_DM_SEQ_ALIAS=y ++# CONFIG_REGMAP is not set ++# CONFIG_DEVRES is not set ++CONFIG_DM_DEV_READ_INLINE=y ++# CONFIG_ADC is not set ++# CONFIG_ADC_EXYNOS is not set ++# CONFIG_ADC_SANDBOX is not set ++# CONFIG_SARADC_MESON is not set ++# CONFIG_SARADC_ROCKCHIP is not set ++# CONFIG_SATA is not set ++# CONFIG_SCSI_AHCI is not set ++ ++# ++# SATA/SCSI device support ++# ++# CONFIG_DWC_AHSATA is not set ++# CONFIG_FSL_SATA is not set ++# CONFIG_MVSATA_IDE is not set ++# CONFIG_SATA_SIL is not set ++# CONFIG_SATA_SIL3114 is not set ++# CONFIG_AXI is not set ++# CONFIG_BLK is not set ++CONFIG_HAVE_BLOCK_DEVICE=y ++# CONFIG_IDE is not set ++# CONFIG_BOOTCOUNT_LIMIT is not set ++ ++# ++# Cache Controller drivers ++# ++# CONFIG_CACHE is not set ++# CONFIG_L2X0_CACHE is not set ++ ++# ++# Clock ++# ++# CONFIG_CLK is not set ++# CONFIG_CLK_CCF is not set ++# CONFIG_CPU is not set ++ ++# ++# Hardware crypto devices ++# ++# CONFIG_FSL_CAAM is not set ++# CONFIG_SYS_FSL_SEC_BE is not set ++# CONFIG_SYS_FSL_SEC_LE is not set ++ ++# ++# Demo for driver model ++# ++# CONFIG_DM_DEMO is not set ++# CONFIG_BOARD is not set ++ ++# ++# DFU support ++# ++ ++# ++# DMA Support ++# ++# CONFIG_DMA is not set ++# CONFIG_TI_EDMA3 is not set ++ ++# ++# Fastboot support ++# ++# CONFIG_USB_FUNCTION_FASTBOOT is not set ++# CONFIG_UDP_FUNCTION_FASTBOOT is not set ++CONFIG_FIRMWARE=y ++# CONFIG_SPL_FIRMWARE is not set ++CONFIG_ARM_PSCI_FW=y ++# CONFIG_ZYNQMP_FIRMWARE is not set ++ ++# ++# FPGA support ++# ++# CONFIG_FPGA_ALTERA is not set ++# CONFIG_FPGA_SOCFPGA is not set ++# CONFIG_FPGA_XILINX is not set ++ ++# ++# GPIO Support ++# ++# CONFIG_DM_GPIO is not set ++# CONFIG_DA8XX_GPIO is not set ++# CONFIG_INTEL_BROADWELL_GPIO is not set ++# CONFIG_IMX_RGPIO2P is not set ++# CONFIG_LPC32XX_GPIO is not set ++# CONFIG_MXC_GPIO is not set ++# CONFIG_MXS_GPIO is not set ++# CONFIG_CMD_PCA953X is not set ++# CONFIG_CMD_TCA642X is not set ++# CONFIG_VYBRID_GPIO is not set ++ ++# ++# Hardware Spinlock Support ++# ++# CONFIG_DM_HWSPINLOCK is not set ++ ++# ++# I2C support ++# ++# CONFIG_DM_I2C is not set ++# CONFIG_SYS_I2C_DW is not set ++# CONFIG_SYS_I2C_IMX_LPI2C is not set ++# CONFIG_SYS_I2C_MXC is not set ++CONFIG_INPUT=y ++# CONFIG_DM_KEYBOARD is not set ++# CONFIG_CROS_EC_KEYB is not set ++# CONFIG_TEGRA_KEYBOARD is not set ++# CONFIG_TWL4030_INPUT is not set ++ ++# ++# LED Support ++# ++# CONFIG_LED is not set ++# CONFIG_LED_STATUS is not set ++ ++# ++# Mailbox Controller Support ++# ++ ++# ++# Memory Controller drivers ++# ++ ++# ++# Multifunction device drivers ++# ++# CONFIG_MISC is not set ++# CONFIG_CROS_EC is not set ++# CONFIG_DS4510 is not set ++# CONFIG_FSL_SEC_MON is not set ++# CONFIG_NUVOTON_NCT6102D is not set ++# CONFIG_PWRSEQ is not set ++# CONFIG_PCA9551_LED is not set ++# CONFIG_TWL4030_LED is not set ++# CONFIG_FS_LOADER is not set ++ ++# ++# MMC Host controller Support ++# ++# CONFIG_MMC is not set ++# CONFIG_MMC_BROKEN_CD is not set ++# CONFIG_DM_MMC is not set ++# CONFIG_FSL_ESDHC is not set ++# CONFIG_FSL_ESDHC_IMX is not set ++ ++# ++# MTD Support ++# ++CONFIG_MTD=y ++# CONFIG_DM_MTD is not set ++# CONFIG_MTD_NOR_FLASH is not set ++# CONFIG_FLASH_CFI_DRIVER is not set ++CONFIG_FMC=y ++# CONFIG_BSP_NAND_SPL is not set ++CONFIG_MTD_RAW_NAND=y ++# CONFIG_SYS_NAND_USE_FLASH_BBT is not set ++# CONFIG_NAND_LPC32XX_SLC is not set ++# CONFIG_NAND_VF610_NFC is not set ++# CONFIG_NAND_PXA3XX is not set ++# CONFIG_NAND_ARASAN is not set ++# CONFIG_FMC_SPI_NAND is not set ++CONFIG_FMC_NAND=y ++CONFIG_NAND_MAX_CHIP_NUM=1 ++# CONFIG_FMC100_HARDWARE_PAGESIZE_ECC is not set ++CONFIG_FMC100_AUTO_PAGESIZE_ECC=y ++# CONFIG_FMC100_PAGESIZE_AUTO_ECC_NONE is not set ++ ++# ++# Generic NAND options ++# ++ ++# ++# SPI Flash Support ++# ++# CONFIG_SPI_FLASH is not set ++CONFIG_FMC_SPI_NOR=y ++# CONFIG_SPI_BLOCK_PROTECT is not set ++# CONFIG_DTR_MODE_SUPPORT is not set ++ ++# ++# UBI support ++# ++# CONFIG_UBI_SILENCE_MSG is not set ++# CONFIG_MTD_UBI is not set ++# CONFIG_BITBANGMII is not set ++# CONFIG_MV88E6352_SWITCH is not set ++# CONFIG_PHYLIB is not set ++# CONFIG_FSL_PFE is not set ++# CONFIG_DM_ETH is not set ++CONFIG_NETDEVICES=y ++# CONFIG_PHY_GIGE is not set ++# CONFIG_BCM_SF2_ETH is not set ++# CONFIG_E1000 is not set ++# CONFIG_ETH_DESIGNWARE is not set ++# CONFIG_ETHOC is not set ++# CONFIG_FMAN_ENET is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_RGMII is not set ++# CONFIG_MII is not set ++# CONFIG_RTL8139 is not set ++# CONFIG_RTL8169 is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SUN7I_GMAC is not set ++# CONFIG_SH_ETHER is not set ++# CONFIG_DRIVER_TI_CPSW is not set ++# CONFIG_DRIVER_TI_EMAC is not set ++# CONFIG_DRIVER_TI_KEYSTONE_NET is not set ++# CONFIG_SYS_DPAA_QBMAN is not set ++# CONFIG_TSEC_ENET is not set ++# CONFIG_SFV300_ETH is not set ++CONFIG_GMACV300_ETH=y ++# CONFIG_PCI is not set ++ ++# ++# PCI Endpoint ++# ++# CONFIG_PCI_ENDPOINT is not set ++ ++# ++# PHY Subsystem ++# ++# CONFIG_PHY is not set ++# CONFIG_MVEBU_COMPHY_SUPPORT is not set ++CONFIG_PHY_USB=y ++ ++# ++# Pin controllers ++# ++# CONFIG_PINCTRL is not set ++ ++# ++# Power ++# ++ ++# ++# Power Domain Support ++# ++# CONFIG_DM_PMIC is not set ++# CONFIG_PMIC_AS3722 is not set ++# CONFIG_POWER_MC34VR500 is not set ++# CONFIG_DM_REGULATOR is not set ++# CONFIG_DM_PWM is not set ++# CONFIG_PWM_IMX is not set ++# CONFIG_PWM_SANDBOX is not set ++# CONFIG_U_QE is not set ++# CONFIG_RAM is not set ++CONFIG_RAM_ROCKCHIP_DEBUG=y ++ ++# ++# Remote Processor drivers ++# ++ ++# ++# Reset Controller Support ++# ++ ++# ++# Real Time Clock ++# ++# CONFIG_DM_RTC is not set ++# CONFIG_RTC_ENABLE_32KHZ_OUTPUT is not set ++# CONFIG_RTC_RX8025 is not set ++# CONFIG_RTC_PL031 is not set ++# CONFIG_RTC_S35392A is not set ++# CONFIG_RTC_MC146818 is not set ++# CONFIG_RTC_M41T62 is not set ++# CONFIG_SCSI is not set ++ ++# ++# Serial drivers ++# ++CONFIG_BAUDRATE=115200 ++CONFIG_SPECIFY_CONSOLE_INDEX=y ++CONFIG_CONS_INDEX=0 ++# CONFIG_DM_SERIAL is not set ++# CONFIG_ATMEL_USART is not set ++# CONFIG_FSL_LPUART is not set ++# CONFIG_MVEBU_A3700_UART is not set ++# CONFIG_MCFUART is not set ++# CONFIG_NULLDEV_SERIAL is not set ++# CONFIG_SYS_NS16550 is not set ++# CONFIG_PL010_SERIAL is not set ++CONFIG_PL011_SERIAL=y ++# CONFIG_PXA_SERIAL is not set ++# CONFIG_SMEM is not set ++ ++# ++# Sound support ++# ++# CONFIG_SOUND is not set ++ ++# ++# SOC (System On Chip) specific Drivers ++# ++# CONFIG_SOC_TI is not set ++# CONFIG_SPI is not set ++ ++# ++# SPMI support ++# ++# CONFIG_SPMI is not set ++ ++# ++# System reset device drivers ++# ++# CONFIG_SYSRESET is not set ++# CONFIG_SYSRESET_SYSCON is not set ++# CONFIG_SYSRESET_WATCHDOG is not set ++# CONFIG_SYSRESET_MPC83XX is not set ++# CONFIG_TEE is not set ++# CONFIG_OPTEE is not set ++# CONFIG_DM_THERMAL is not set ++ ++# ++# Timer Support ++# ++# CONFIG_TIMER is not set ++ ++# ++# TPM support ++# ++CONFIG_USB=y ++# CONFIG_DM_USB is not set ++ ++# ++# USB Host Controller Drivers ++# ++CONFIG_USB_HOST=y ++CONFIG_USB_XHCI_HCD=y ++# CONFIG_USB_XHCI_DWC3 is not set ++# CONFIG_USB_XHCI_FSL is not set ++# CONFIG_USB_EHCI_HCD is not set ++# CONFIG_USB_OHCI_HCD is not set ++# CONFIG_USB_UHCI_HCD is not set ++# CONFIG_USB_DWC2 is not set ++# CONFIG_USB_CDNS3 is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Legacy MUSB Support ++# ++# CONFIG_USB_MUSB_HCD is not set ++# CONFIG_USB_MUSB_UDC is not set ++ ++# ++# MUSB Controller Driver ++# ++# CONFIG_USB_MUSB_HOST is not set ++# CONFIG_USB_MUSB_GADGET is not set ++# CONFIG_USB_MUSB_AM35X is not set ++# CONFIG_USB_MUSB_DSPS is not set ++# CONFIG_USB_MUSB_PIO_ONLY is not set ++ ++# ++# USB Phy ++# ++# CONFIG_TWL4030_USB is not set ++# CONFIG_OMAP_USB_PHY is not set ++# CONFIG_ROCKCHIP_USB2_PHY is not set ++ ++# ++# ULPI drivers ++# ++ ++# ++# USB peripherals ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_KEYBOARD is not set ++CONFIG_USB_GADGET=y ++CONFIG_USB_GADGET_MANUFACTURER="U-Boot" ++CONFIG_USB_GADGET_VENDOR_NUM=0x0 ++CONFIG_USB_GADGET_PRODUCT_NUM=0x0 ++# CONFIG_USB_GADGET_ATMEL_USBA is not set ++# CONFIG_USB_GADGET_BCM_UDC_OTG_PHY is not set ++# CONFIG_USB_GADGET_DWC2_OTG is not set ++# CONFIG_CI_UDC is not set ++CONFIG_USB_GADGET_VBUS_DRAW=2 ++# CONFIG_USB_GADGET_DOWNLOAD is not set ++# CONFIG_USB_ETHER is not set ++# CONFIG_USB_HOST_ETHER is not set ++ ++# ++# UFS Host Controller Support ++# ++# CONFIG_TI_J721E_UFS is not set ++# CONFIG_UFS is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DM_VIDEO is not set ++# CONFIG_SYS_WHITE_ON_BLACK is not set ++# CONFIG_NO_FB_CLEAR is not set ++ ++# ++# TrueType Fonts ++# ++# CONFIG_VIDEO_VESA is not set ++# CONFIG_VIDEO_LCD_ANX9804 is not set ++# CONFIG_VIDEO_LCD_SSD2828 is not set ++# CONFIG_VIDEO_MVEBU is not set ++# CONFIG_I2C_EDID is not set ++# CONFIG_DISPLAY is not set ++# CONFIG_VIDEO_BRIDGE is not set ++# CONFIG_VIDEO is not set ++# CONFIG_LCD is not set ++# CONFIG_VIDEO_SIMPLE is not set ++# CONFIG_VIDEO_DT_SIMPLEFB is not set ++# CONFIG_OSD is not set ++ ++# ++# VirtIO Drivers ++# ++# CONFIG_VIRTIO_MMIO is not set ++ ++# ++# 1-Wire support ++# ++# CONFIG_W1 is not set ++ ++# ++# 1-wire EEPROM support ++# ++# CONFIG_W1_EEPROM is not set ++ ++# ++# Watchdog Timer Support ++# ++# CONFIG_WATCHDOG is not set ++CONFIG_WATCHDOG_TIMEOUT_MSECS=60000 ++# CONFIG_WATCHDOG_RESET_DISABLE is not set ++# CONFIG_IMX_WATCHDOG is not set ++# CONFIG_ULP_WATCHDOG is not set ++# CONFIG_WDT is not set ++# CONFIG_PHYS_TO_BUS is not set ++ ++# ++# File systems ++# ++# CONFIG_FS_BTRFS is not set ++# CONFIG_FS_CBFS is not set ++# CONFIG_SPL_FS_CBFS is not set ++# CONFIG_FS_EXT4 is not set ++# CONFIG_FS_FAT is not set ++# CONFIG_FS_JFFS2 is not set ++# CONFIG_UBIFS_SILENCE_MSG is not set ++# CONFIG_FS_CRAMFS is not set ++# CONFIG_YAFFS2 is not set ++ ++# ++# Library routines ++# ++# CONFIG_BCH is not set ++# CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set ++# CONFIG_DYNAMIC_CRC_TABLE is not set ++CONFIG_PRINTF=y ++CONFIG_SPRINTF=y ++CONFIG_STRTO=y ++CONFIG_SYS_HZ=1000 ++# CONFIG_PANIC_HANG is not set ++CONFIG_REGEX=y ++# CONFIG_SPL_TINY_MEMSET is not set ++# CONFIG_TPL_TINY_MEMSET is not set ++# CONFIG_BITREVERSE is not set ++# CONFIG_TRACE is not set ++# CONFIG_CMD_DHRYSTONE is not set ++ ++# ++# Security support ++# ++# CONFIG_AES is not set ++# CONFIG_RSA is not set ++# CONFIG_ASYMMETRIC_KEY_TYPE is not set ++# CONFIG_TPM is not set ++ ++# ++# Android Verified Boot ++# ++ ++# ++# Hashing Support ++# ++CONFIG_SHA1=y ++CONFIG_SHA256=y ++# CONFIG_SHA_HW_ACCEL is not set ++CONFIG_MD5=y ++ ++# ++# Compression Support ++# ++# CONFIG_LZ4 is not set ++CONFIG_LZMA=y ++# CONFIG_LZO is not set ++CONFIG_GZIP=y ++CONFIG_ZLIB=y ++# CONFIG_ZSTD is not set ++# CONFIG_SPL_LZ4 is not set ++# CONFIG_SPL_LZO is not set ++# CONFIG_SPL_GZIP is not set ++# CONFIG_SPL_ZSTD is not set ++CONFIG_HWDEC=y ++# CONFIG_ERRNO_STR is not set ++# CONFIG_HEXDUMP is not set ++# CONFIG_OF_LIBFDT is not set ++CONFIG_OF_LIBFDT_ASSUME_MASK=0 ++# CONFIG_SPL_OF_LIBFDT is not set ++CONFIG_SPL_OF_LIBFDT_ASSUME_MASK=0xff ++# CONFIG_TPL_OF_LIBFDT is not set ++CONFIG_TPL_OF_LIBFDT_ASSUME_MASK=0xff ++ ++# ++# System tables ++# ++# CONFIG_UNIT_TEST is not set ++ ++# ++# Product ++# ++# CONFIG_OSD_ENABLE is not set ++# CONFIG_AUTO_UPDATE is not set ++# CONFIG_CIPHER_ENABLE is not set ++# CONFIG_KLAD_ENABLE is not set ++# CONFIG_OTP_ENABLE is not set +diff --git a/disk/part.c b/disk/part.c +index 8982ef3..64137fb 100644 +--- a/disk/part.c ++++ b/disk/part.c +@@ -24,28 +24,16 @@ + /* Check all partition types */ + #define PART_TYPE_ALL -1 + +-static struct part_driver *part_driver_lookup_type(struct blk_desc *dev_desc) ++static struct part_driver *part_driver_lookup_type(int part_type) + { + struct part_driver *drv = + ll_entry_start(struct part_driver, part_driver); + const int n_ents = ll_entry_count(struct part_driver, part_driver); + struct part_driver *entry; + +- if (dev_desc->part_type == PART_TYPE_UNKNOWN) { +- for (entry = drv; entry != drv + n_ents; entry++) { +- int ret; +- +- ret = entry->test(dev_desc); +- if (!ret) { +- dev_desc->part_type = entry->part_type; +- return entry; +- } +- } +- } else { +- for (entry = drv; entry != drv + n_ents; entry++) { +- if (dev_desc->part_type == entry->part_type) +- return entry; +- } ++ for (entry = drv; entry != drv + n_ents; entry++) { ++ if (part_type == entry->part_type) ++ return entry; + } + + /* Not found */ +@@ -301,7 +289,7 @@ void part_print(struct blk_desc *dev_desc) + { + struct part_driver *drv; + +- drv = part_driver_lookup_type(dev_desc); ++ drv = part_driver_lookup_type(dev_desc->part_type); + if (!drv) { + printf("## Unknown partition table type %x\n", + dev_desc->part_type); +@@ -330,7 +318,7 @@ int part_get_info(struct blk_desc *dev_desc, int part, + info->type_guid[0] = 0; + #endif + +- drv = part_driver_lookup_type(dev_desc); ++ drv = part_driver_lookup_type(dev_desc->part_type); + if (!drv) { + debug("## Unknown partition table type %x\n", + dev_desc->part_type); +@@ -651,7 +639,7 @@ int part_get_info_by_name_type(struct blk_desc *dev_desc, const char *name, + int ret; + int i; + +- part_drv = part_driver_lookup_type(dev_desc); ++ part_drv = part_driver_lookup_type(dev_desc->part_type); + if (!part_drv) + return -1; + for (i = 1; i < part_drv->max_entries; i++) { +diff --git a/drivers/Kconfig b/drivers/Kconfig +index 9d99ce0..34e63a0 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -76,6 +76,8 @@ source "drivers/phy/allwinner/Kconfig" + + source "drivers/phy/marvell/Kconfig" + ++source "drivers/phy/vendor/Kconfig" ++ + source "drivers/pinctrl/Kconfig" + + source "drivers/power/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index e977f19..657d1a7 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -89,6 +89,7 @@ obj-y += dfu/ + obj-$(CONFIG_PCH) += pch/ + obj-y += phy/allwinner/ + obj-y += phy/marvell/ ++obj-y += phy/vendor/ + obj-y += rtc/ + obj-y += scsi/ + obj-y += sound/ +@@ -116,3 +117,8 @@ obj-$(CONFIG_W1_EEPROM) += w1-eeprom/ + obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ + obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock/ + endif ++ifneq ($(filter $(CONFIG_PRODUCTNAME), "ss928v100" "ss927v100"),) ++obj-$(CONFIG_DDR_TRAINING_V2) += ddr/vendor/default_v2/ ++else ++obj-$(CONFIG_DDR_TRAINING_V2) += ddr/vendor/default/ ++endif +diff --git a/drivers/ddr/vendor/default/Makefile b/drivers/ddr/vendor/default/Makefile +new file mode 100644 +index 0000000..e12f718 +--- /dev/null ++++ b/drivers/ddr/vendor/default/Makefile +@@ -0,0 +1,17 @@ ++obj-y := ddr_training_impl.o ++obj-y += ddr_ac_training.o ++obj-y += ddr_dcc_training.o ++obj-y += ddr_lpca_training.o ++obj-y += ddr_mpr_training.o ++obj-y += ddr_wl_training.o ++obj-y += ddr_training_ctl.o ++obj-y += ddr_training_boot.o ++obj-y += ddr_training_console.o ++obj-y += ddr_training_custom.o ++ifdef CONFIG_CMD_DDR_TRAINING ++obj-y += ddr_cmd_loc.o ++obj-y += ddr_cmd_ctl.o ++obj-y += cmd_ddr_training_v2.o ++else ++obj-y += ddr_cmd_null.o ++endif +diff --git a/drivers/ddr/vendor/default/cmd_bin/Makefile b/drivers/ddr/vendor/default/cmd_bin/Makefile +new file mode 100755 +index 0000000..91cb3a0 +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_bin/Makefile +@@ -0,0 +1,93 @@ ++################################################################################ ++ifeq ($(TOPDIR),) ++TOPDIR := ../../../../.. ++endif ++sinclude $(TOPDIR)/arch/arm/cpu/$(CPU)/config.mk # include architecture dependend rules ++ ++PWD := $(shell pwd) ++OPPDIR := $(subst $(TOPDIR),,$(PWD)) ++ ++CC := $(CROSS_COMPILE)gcc ++AR := $(CROSS_COMPILE)ar ++LD := $(CROSS_COMPILE)ld ++OBJCOPY := $(CROSS_COMPILE)objcopy ++OBJDUMP := $(CROSS_COMPILE)objdump ++ ++################################################################################ ++DDR_CMD := ddr_cmd ++sinclude $(TOPDIR)/include/config/auto.conf #include CONFIG_ARM/CONFIG_ARM64 define ++ ++ifneq ("$(MAKECMDGOALS)","clean") ++CMD_TEXT_BASE := $(shell grep '^\#define.*DDR_TRAINING_RUN_STACK' $(TOPDIR)/drivers/ddr/vendor/$(SOC)/ddr_training_custom.h|awk '{print $$3}') ++endif ++ ++STACK_POINT := $(CMD_TEXT_BASE) ++ ++COBJS := ddr_training_uart.o ddr_training_custom.o ddr_training_cmd.o ddr_training_impl.o ddr_ac_training.o ddr_dcc_training.o \ ++ ddr_lpca_training.o ddr_mpr_training.o ddr_wl_training.o ddr_training_ctl.o ddr_training_console.o ++DEPS := $(COBJS:.o=.d) $(START:.o=.d) ++SSRC := ddr_training_impl.c ddr_ac_training.c ddr_dcc_training.c ddr_lpca_training.c \ ++ ddr_mpr_training.c ddr_wl_training.c ddr_training_ctl.c ddr_training_console.c ++ ++CFLAGS := -Os -pipe \ ++ -DCMD_TEXT_BASE=$(CMD_TEXT_BASE) -DSTACK_POINT=$(STACK_POINT) -DDDR_TRAINING_CMD -DDDR_TRAINING_MEM_FUNC -D__KERNEL__ -D__UBOOT__ \ ++ -fno-builtin -ffreestanding -I$(TOPDIR)/arch/$(ARCH)/include -I$(TOPDIR)/include \ ++ -I../ -I./ -I$(TOPDIR)/drivers/ddr/vendor/$(SOC)/ ++ ++CFLAGS += $(PLATFORM_RELFLAGS) $(PLATFORM_CPPFLAGS) ++ ++ifeq ("$(CONFIG_ARM64)","y") ++START := cmd_entry_64.o ++LDS_SCRIPT := ddr_cmd_64.lds ++CFLAGS += -DCONFIG_ARM64 ++else ++START := cmd_entry_32.o ++LDS_SCRIPT := ddr_cmd_32.lds ++endif ++ ++################################################################################ ++ ++LINK_FILES = $(SSRC) ddr_training_custom.c ++ ++.PHONY: $(DDR_CMD).bin ++all: $(DDR_CMD).bin ++ #remove soft link files ++ @rm -f $(LINK_FILES) *.o *.d *.elf *.map *.srec ++ ++$(DDR_CMD).bin: $(DDR_CMD).elf ++ $(OBJCOPY) -O srec $(PWD)/$(DDR_CMD).elf $(DDR_CMD).srec ++ $(OBJCOPY) --gap-fill=0xff -O binary $(PWD)/$(DDR_CMD).elf $@ ++ ++$(DDR_CMD).elf: $(START) $(COBJS) $(LDS_SCRIPT) ++ #@echo CMD_TEXT_BASE=$(CMD_TEXT_BASE) ++ $(LD) -Bstatic -T $(LDS_SCRIPT) -Ttext $(CMD_TEXT_BASE) $(START) \ ++ $(COBJS) $(AOBJS) -Map $(DDR_CMD).map -o $@ ++ ++$(SSRC): ++ rm -rf $@ ++ rm -rf ddr_training_custom.c ++ ln -sf ../$@ $@ ++ ln -sf $(TOPDIR)/drivers/ddr/vendor/$(SOC)/ddr_training_custom.c ddr_training_custom.c ++ ++.PHONY: clean ++clean: ++ @rm -vf *.o *.d *.elf *.map *.srec $(LINK_FILES) $(DDR_CMD).bin ++ ++%.o : %.S ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ -c $*.S ++ ++%.o : %.c ++ $(CC) $(CFLAGS) -Wall -Wstrict-prototypes -fno-stack-protector \ ++ -o $@ -c $*.c ++ ++ifneq ("$(MAKECMDGOALS)","clean") ++sinclude $(DEPS) ++endif ++ ++%.d : %.c ++ set -e; $(CC) $(CFLAGS) -MM $< | sed 's,$*.o:,$*.o $*.d:,g' > $@ ++ ++%.d : %.S ++ set -e; $(CC) $(CFLAGS) -MM $< | sed 's,$*.o:,$*.o $*.d:,g' > $@ ++ ++################################################################################ +diff --git a/drivers/ddr/vendor/default/cmd_bin/cmd_entry_32.S b/drivers/ddr/vendor/default/cmd_bin/cmd_entry_32.S +new file mode 100644 +index 0000000..37660b9 +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_bin/cmd_entry_32.S +@@ -0,0 +1,96 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++.section .text ++.type _start, %function ++.globl _start ++_start: ++ push {r0 - r10} ++ b flying ++ ++. = 0x10 ++_CMD_TEXT_BASE: ++ .word CMD_TEXT_BASE ++ ++_arm_start: ++ .word _start ++_flying: ++ .word flying ++_STACK_POINT: ++ .word STACK_POINT ++ ++.globl _bss_start ++_bss_start: .word __bss_start ++.globl _bss_end ++_bss_end: .word _end ++ ++_real_start: ++ .word real_start ++flying: ++ mov r2, pc ++ sub r2, r2, #8 ++ ldr r1, _arm_start ++ ldr r0, _flying ++ sub r1, r0, r1 ++ sub r0, r2, r1 ++ ldr r1, _CMD_TEXT_BASE ++ cmp r0, r1 ++ beq real_start ++ ++ /* need relocation */ ++ ldr r2, _arm_start ++ ldr r3, _bss_start ++ sub r2, r3, r2 ++ add r2, r0, r2 ++self_move: ++ ldmia r0!, {r3 - r10} ++ stmia r1!, {r3 - r10} ++ cmp r0, r2 ++ ble self_move ++ ldr pc, _real_start ++ ++real_start: ++ ldr r0, _bss_start ++ ldr r1, _bss_end ++ mov r2, #0x00000000 ++ ++clear_bss_loop: ++ str r2, [r0] ++ cmp r0, r1 ++ add r0, r0, #4 ++ bne clear_bss_loop ++ ++ ldr r4, =_lr ++ str lr, [r4] ++ pop {r0 - r10} ++ ++ ldr lr, =_sp ++ str sp, [lr] ++ ldr sp, _STACK_POINT ++ ++ bl ddr_training_cmd_entry ++ ++ ldr lr, =_sp ++ ldr sp, [lr] ++ ++ ldr lr, =_lr ++ ldr pc, [lr] ++ ++.section .data ++_lr: .word 0 ++_sp: .word 0 +diff --git a/drivers/ddr/vendor/default/cmd_bin/cmd_entry_64.S b/drivers/ddr/vendor/default/cmd_bin/cmd_entry_64.S +new file mode 100644 +index 0000000..37f56ae +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_bin/cmd_entry_64.S +@@ -0,0 +1,99 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++.section .text ++.type _start, %function ++.globl _start ++_start: ++ stp x29, x30, [sp, #-16]! ++ stp x27, x28, [sp, #-16]! ++ stp x25, x26, [sp, #-16]! ++ stp x23, x24, [sp, #-16]! ++ stp x21, x22, [sp, #-16]! ++ stp x19, x20, [sp, #-16]! ++ stp x17, x18, [sp, #-16]! ++ stp x15, x16, [sp, #-16]! ++ stp x13, x14, [sp, #-16]! ++ stp x11, x12, [sp, #-16]! ++ stp x9, x10, [sp, #-16]! ++ stp x7, x8, [sp, #-16]! ++ stp x5, x6, [sp, #-16]! ++ stp x3, x4, [sp, #-16]! ++ stp x1, x2, [sp, #-16]! ++ ++ b real_start ++ ++_STACK_POINT: ++ .quad STACK_POINT ++ ++.globl _bss_start ++_bss_start: .quad __bss_start ++.globl _bss_end ++_bss_end: .quad _end ++ ++_real_start: ++ .quad real_start ++real_start: ++ ldr x3, _bss_start ++ ldr x1, _bss_end ++ mov x2, #0x00000000 ++ ++clear_bss_loop: ++ str x2, [x3] ++ cmp x3, x1 ++ add x3, x3, #4 ++ bne clear_bss_loop ++ ++ ldr x4, =_x30 ++ str x30, [x4] /* x30 is lr, save lr */ ++ ++ ldp x1, x2, [sp],#16 ++ ldp x3, x4, [sp],#16 ++ ldp x5, x6, [sp],#16 ++ ldp x7, x8, [sp],#16 ++ ldp x9, x10, [sp],#16 ++ ldp x11, x12, [sp],#16 ++ ldp x13, x14, [sp],#16 ++ ldp x15, x16, [sp],#16 ++ ldp x17, x18, [sp],#16 ++ ldp x19, x20, [sp],#16 ++ ldp x21, x22, [sp],#16 ++ ldp x23, x24, [sp],#16 ++ ldp x25, x26, [sp],#16 ++ ldp x27, x28, [sp],#16 ++ ldp x29, x30, [sp],#16 ++ ++ ldr x30, =_sp ++ mov x29, sp ++ str x29, [x30] /* save sp */ ++ ldr x29, _STACK_POINT ++ mov sp, x29 ++ ++ bl ddr_training_cmd_entry ++ ++ ldr x30, =_sp /* restore sp */ ++ ldr x28, [x30] ++ mov sp, x28 ++ ++ ldr x28, =_x30 ++ ldr x30, [x28] /* restore lr */ ++ ret ++ ++.section .data ++_x30: .quad 0 ++_sp: .quad 0 +diff --git a/drivers/ddr/vendor/default/cmd_bin/ddr_cmd_32.lds b/drivers/ddr/vendor/default/cmd_bin/ddr_cmd_32.lds +new file mode 100644 +index 0000000..1f2536f +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_bin/ddr_cmd_32.lds +@@ -0,0 +1,28 @@ ++ ++OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") ++OUTPUT_ARCH(arm) ++ENTRY(_start) ++SECTIONS ++{ ++ . = 0x00000000; ++ ++ . = ALIGN(4); ++ .text : { ++ cmd_entry_32.o (.text) ++ *(.text) ++ } ++ ++ . = ALIGN(4); ++ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ++ ++ . = ALIGN(4); ++ .data : { *(.data) } ++ ++ . = ALIGN(4); ++ .got : { *(.got) } ++ ++ . = ALIGN(4); ++ __bss_start = .; ++ .bss : { *(.bss) } ++ _end = .; ++} +diff --git a/drivers/ddr/vendor/default/cmd_bin/ddr_cmd_64.lds b/drivers/ddr/vendor/default/cmd_bin/ddr_cmd_64.lds +new file mode 100644 +index 0000000..313e1d0 +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_bin/ddr_cmd_64.lds +@@ -0,0 +1,28 @@ ++ ++OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") ++OUTPUT_ARCH(aarch64) ++ENTRY(_start) ++SECTIONS ++{ ++ . = 0x00000000; ++ ++ . = ALIGN(8); ++ .text : { ++ cmd_entry_64.o (.text) ++ *(.text) ++ } ++ ++ . = ALIGN(8); ++ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ++ ++ . = ALIGN(8); ++ .data : { *(.data) } ++ ++ . = ALIGN(8); ++ .got : { *(.got) } ++ ++ . = ALIGN(8); ++ __bss_start = .; ++ .bss : { *(.bss) } ++ _end = .; ++} +diff --git a/drivers/ddr/vendor/default/cmd_bin/ddr_training_cmd.c b/drivers/ddr/vendor/default/cmd_bin/ddr_training_cmd.c +new file mode 100644 +index 0000000..e4bcfdc +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_bin/ddr_training_cmd.c +@@ -0,0 +1,611 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++/* ddr training cmd result */ ++struct ddr_training_result_st g_ddrt_result_sram; ++static unsigned int g_ddr_training_addr_start; ++static unsigned int g_ddr_training_addr_end; ++static int g_ddr_print_level = DDR_LOG_ERROR; ++ ++#ifdef DDR_TRAINING_LOG_CONFIG ++static void ddr_training_log_level(const char *func, int level) ++{ ++ DDR_PUTC('['); ++ switch (level) { ++ case DDR_LOG_INFO: ++ DDR_PUTS("INFO"); ++ break; ++ case DDR_LOG_DEBUG: ++ DDR_PUTS("DEBUG"); ++ break; ++ case DDR_LOG_WARNING: ++ DDR_PUTS("WARNING"); ++ break; ++ case DDR_LOG_ERROR: ++ DDR_PUTS("ERROR"); ++ break; ++ case DDR_LOG_FATAL: ++ DDR_PUTS("FATAL"); ++ break; ++ default: ++ break; ++ } ++ DDR_PUTC(']'); ++ DDR_PUTC('['); ++ DDR_PUTS(func); ++ DDR_PUTC(']'); ++} ++ ++/* Log ddr training info */ ++void ddr_training_log(const char *func, int level, const char *fmt, ...) ++{ ++ va_list args; ++ ++ if (fmt == NULL) ++ return; ++ if (g_ddr_print_level > level) ++ return; ++ ++ ddr_training_log_level(func, level); ++ ++ va_start(args, fmt); ++ while (*fmt != '\0') { ++ if (*fmt != '%') { ++ DDR_PUTC(*fmt); ++ } else { ++ fmt++; ++ switch (*fmt) { ++ case 'x': ++ case 'X': ++ DDR_PUTS("0x"); ++ DDR_PUT_HEX(va_arg(args, int)); ++ break; ++ default: ++ DDR_PUTC('%'); ++ DDR_PUTC(*fmt); ++ break; ++ } /* switch */ ++ } ++ fmt++; ++ } /* while */ ++ va_end(args); ++ DDR_PUTS("\r\n"); ++} ++ ++/* Nothing to do in DDR command when defined DDR_TRAINING_LOG_CONFIG */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ return; ++} ++#else ++/* Display DDR training error */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ switch (mask) { ++ case DDR_ERR_WL: ++ DDR_PUTS("WL"); ++ break; ++ case DDR_ERR_HW_GATING: ++ DDR_PUTS("HW Gate"); ++ break; ++ case DDR_ERR_GATING: ++ DDR_PUTS("Gate"); ++ break; ++ case DDR_ERR_DDRT_TIME_OUT: ++ DDR_PUTS("DDRT"); ++ break; ++ case DDR_ERR_HW_RD_DATAEYE: ++ DDR_PUTS("HW Dataeye"); ++ break; ++ case DDR_ERR_MPR: ++ DDR_PUTS("MPR"); ++ break; ++ case DDR_ERR_DATAEYE: ++ DDR_PUTS("Dataeye"); ++ break; ++ case DDR_ERR_LPCA: ++ DDR_PUTS("LPCA"); ++ break; ++ default: ++ break; ++ } ++ ++ DDR_PUTS(" Err:"); ++ ++ if (phy != 0) { ++ DDR_PUTS(" Phy:"); ++ DDR_PUT_HEX(phy); ++ } ++ ++ if (byte != -1) { ++ DDR_PUTS(" Byte:"); ++ DDR_PUT_HEX(byte); ++ } ++ ++ if (dq != -1) { ++ DDR_PUTS(" DQ:"); ++ DDR_PUT_HEX(dq); ++ } ++ ++ DDR_PUTS("\r\n"); ++} ++#endif ++ ++/* Inint ddr training cmd result */ ++static void ddr_training_result_init(const struct ddr_cfg_st *cfg, struct ddr_training_result_st *ddrtr_res) ++{ ++ int i, j; ++ ++ memset(ddrtr_res, 0, sizeof(struct ddr_training_result_st)); ++ if (cfg->phy_num > DDR_PHY_NUM) { ++ ddr_error("loop upper limit phy_num or rank_num out of range"); ++ return; ++ } ++ ddrtr_res->phy_num = cfg->phy_num; ++ for (i = 0; i < cfg->phy_num; i++) { ++ if (cfg->phy[i].rank_num > DDR_SUPPORT_RANK_MAX) { ++ ddr_error("loop upper limit rank_num out of range"); ++ return; ++ } ++ ddrtr_res->phy_st[i].rank_num = cfg->phy[i].rank_num; ++ ++ for (j = 0; j < cfg->phy[i].rank_num; j++) { ++ ddrtr_res->phy_st[i].rank_st[j].item = cfg->phy[i].rank[j].item; ++ ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.base_phy = cfg->phy[i].addr; ++ ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.byte_num = cfg->phy[i].total_byte_num; ++ ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.rank_idx = j; ++ } ++ } ++} ++ ++/* Save ddr training cmd result */ ++void ddr_result_data_save(struct ddr_cfg_st *cfg, const struct training_data *training) ++{ ++ unsigned int i; ++ unsigned int offset; ++ struct training_data *dest = NULL; ++ struct ddr_training_result_st *ddrtr_res = NULL; ++ ++ if (cfg == NULL || training == NULL) { ++ ddr_error("Pointer parameter training or ddrtr_res is NULL!"); ++ return; ++ } ++ ++ ddrtr_res = (struct ddr_training_result_st *)cfg->res_st; ++ if (ddrtr_res == NULL) ++ return; ++ ++ if ((cfg->phy_idx >= DDR_PHY_NUM) || (cfg->dmc_idx >= DDR_DMC_PER_PHY_MAX)) { ++ ddr_error("Array index phy_idx or dmc_idx out of range!"); ++ return; ++ } ++ ++ if (cfg->cur_mode == DDR_MODE_READ) ++ dest = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data.read; ++ else ++ dest = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data.write; ++ ++ if (cfg->phy[cfg->phy_idx].dmc_num == 1) { ++ memcpy(dest, training, sizeof(struct training_data)); ++ } else { ++ /* dmc[0] + dmc[1] */ ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) { ++ ddr_error("get byte num fail, byte_num = %x", get_byte_num(cfg)); ++ return; ++ } ++ if ((get_byte_num(cfg) << DDR_BYTE_DQ) > DDR_PHY_BIT_MAX) { ++ ddr_error("get bit max num fail"); ++ return; ++ } ++ offset = cfg->dmc_idx << 4; /* Shift left 4:16 */ ++ for (i = 0; i < (get_byte_num(cfg) << DDR_BYTE_DQ); i++) { ++ dest->ddr_bit_result[i + offset] = training->ddr_bit_result[i + offset]; ++ dest->ddr_bit_best[i + offset] = training->ddr_bit_best[i + offset]; ++ } ++ dest->ddr_win_sum += training->ddr_win_sum; ++ } ++} ++ ++/* Save lpca training data */ ++void ddr_lpca_data_save(struct ddr_cfg_st *cfg, const struct ca_data_st *data) ++{ ++ unsigned int index; ++ struct ddr_training_result_st *ddrtr_res = NULL; ++ struct ddr_training_data_st *tr_data = NULL; ++ ++ if (cfg == NULL || data == NULL) { ++ ddr_error("Pointer parameter is NULL"); ++ return; ++ } ++ ++ ddrtr_res = (struct ddr_training_result_st *)cfg->res_st; ++ if (ddrtr_res == NULL) ++ return; ++ ++ tr_data = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data; ++ if (tr_data == NULL) ++ return; ++ ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) ++ tr_data->ca_addr[index] = ((unsigned int)data->left[index] << ++ DDR_DATAEYE_RESULT_BIT) | (unsigned int)data->right[index]; ++} ++ ++/* Get DDRT test addrress */ ++unsigned int ddr_ddrt_get_test_addr(void) ++{ ++ if (g_ddr_training_addr_start <= DDRT_CFG_TEST_ADDR_CMD && ++ g_ddr_training_addr_end >= DDRT_CFG_TEST_ADDR_CMD) { ++ return DDRT_CFG_TEST_ADDR_CMD; ++ } else { ++ ddr_error("DDRT test address[%x] out of range[%x, %x]", ++ DDRT_CFG_TEST_ADDR_CMD, ++ g_ddr_training_addr_start, ++ g_ddr_training_addr_end); ++ return g_ddr_training_addr_start; ++ } ++} ++ ++/* Nothing to do in DDR command */ ++void ddr_training_suc(void) ++{ ++ return; ++} ++ ++/* Nothing to do in DDR command */ ++void ddr_training_start(void) ++{ ++ return; ++} ++ ++/* dump WDQS result */ ++static void ddr_dump_wdqs_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] WDQS Byte(%x) ", ++ base_phy + ddr_phy_dxwdqsdly(rank, i), ++ reg_read(base_phy + ddr_phy_dxwdqsdly(rank, i)), i); ++} ++ ++/* dump WDQ Phase result */ ++static void ddr_dump_wdq_phase_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] WDQ Phase Byte(%x)", ++ base_phy + ddr_phy_dxnwdqdly(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqdly(rank, i)), i); ++} ++ ++/* dump WDQ BDL result */ ++static void ddr_dump_wdq_bdl_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < byte_num; i++) { ++ /* DQ0-DQ3 */ ++ ddr_info("[%x = %x] WDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnwdqnbdl0(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqnbdl0(rank, i)), ++ (i << 3), ((i << 3) + 3)); /* DQ0-DQ3, Shift left 3:8 dq */ ++ ++ /* DQ4-DQ7 */ ++ ddr_info("[%x = %x] WDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnwdqnbdl1(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqnbdl1(rank, i)), ++ ((i << 3) + 4), ((i << 3) + 7)); /* DQ4-DQ7, Shift left 3:8 dq */ ++ } ++} ++ ++/* dump WDM result */ ++static void ddr_dump_wdm_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] WDM Byte(%x)", ++ base_phy + ddr_phy_dxnwdqnbdl2(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank, i)), i); ++} ++ ++/* dump RDQS result */ ++static void ddr_dump_rdqs_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] RDQS Byte(%x)", ++ base_phy + ddr_phy_dxnrdqsdly(i), ++ reg_read(base_phy + ddr_phy_dxnrdqsdly(i)), i); ++} ++ ++/* RDQ BDL */ ++static void ddr_dump_rdq_bdl_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < byte_num; i++) { ++ /* DQ0-DQ3 */ ++ ddr_info("[%x = %x] RDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnrdqnbdl0(rank, i), ++ reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank, i)), ++ (i << 3), ((i << 3) + 3)); /* DQ0-DQ3, Shift left 3:8 dq */ ++ ++ /* DQ4-DQ7 */ ++ ddr_info("[%x = %x] RDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnrdqnbdl1(rank, i), ++ reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank, i)), ++ ((i << 3) + 4), ((i << 3) + 7)); /* DQ4-DQ7, Shift left 3:8 dq */ ++ } ++} ++ ++static void dump_result(const struct ddr_training_data_st *ddrtr_data) ++{ ++ unsigned int i; ++ unsigned int base_phy = ddrtr_data->base_phy; ++ unsigned int byte_num = ddrtr_data->byte_num; ++ unsigned int rank = ddrtr_data->rank_idx; ++ unsigned int acphyctl7; ++ ++ /* Static register have to read two times to get the right value. */ ++ acphyctl7 = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ acphyctl7 = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ ++ if ((byte_num > DDR_PHY_BYTE_MAX) || ((byte_num << DDR_BYTE_DQ) > DDR_PHY_BIT_MAX)) { ++ ddr_error("loop upper limit out of range, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < (byte_num << DDR_BYTE_DQ); i++) ++ ddr_info("Byte[%x] Write[%x][%x] Read[%x][%x]", ++ i, ddrtr_data->write.ddr_bit_result[i], ddrtr_data->write.ddr_bit_best[i], ++ ddrtr_data->read.ddr_bit_result[i], ddrtr_data->read.ddr_bit_best[i]); ++ ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ if (ddrtr_data->ca_addr[i] != 0) ++ ddr_info("CA[%x] Range[%x]", i, ddrtr_data->ca_addr[i]); ++ } ++ ++ /* WDQS */ ++ ddr_dump_wdqs_result(base_phy, byte_num, rank); ++ ++ /* WDQ Phase */ ++ ddr_dump_wdq_phase_result(base_phy, byte_num, rank); ++ ++ /* WDQ BDL */ ++ ddr_dump_wdq_bdl_result(base_phy, byte_num, rank); ++ ++ /* WDM */ ++ ddr_dump_wdm_result(base_phy, byte_num, rank); ++ ++ /* Write DO/DOS OE */ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] Write DQ/DQS OE Byte(%x)", ++ base_phy + ddr_phy_dxnoebdl(rank, i), ++ reg_read(base_phy + ddr_phy_dxnoebdl(rank, i)), i); ++ ++ /* RDQS */ ++ ddr_dump_rdqs_result(base_phy, byte_num, rank); ++ ++ /* RDQ BDL */ ++ ddr_dump_rdq_bdl_result(base_phy, byte_num, rank); ++ ++ /* Gate */ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] Gate Byte(%x)", ++ base_phy + ddr_phy_dxnrdqsgdly(rank, i), ++ reg_read(base_phy + ddr_phy_dxnrdqsgdly(rank, i)), i); ++ ++ ddr_info("[%x = %x] CS", base_phy + DDR_PHY_ACCMDBDL2, ++ reg_read(base_phy + DDR_PHY_ACCMDBDL2)); ++ ++ ddr_info("[%x = %x] CLK", base_phy + DDR_PHY_ACPHYCTL7, acphyctl7); ++ ++ ddr_phy_switch_rank(base_phy, rank); ++ ++ /* HOST Vref */ ++ ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num); ++ ++ /* DRAM Vref */ ++ ddr_phy_vref_dram_display_cmd(base_phy, byte_num); ++ ++ /* DPMC */ ++ ddr_dx_dpmc_display_cmd(base_phy, byte_num); ++ ++ /* Addr Phase */ ++ ddr_phy_addrph_display_cmd(base_phy); ++ ++ /* Addr BDL */ ++ ddr_phy_addrbdl_display_cmd(base_phy); ++ ++ /* DCC */ ++ ddr_phy_dcc_display_cmd(base_phy); ++} ++ ++static void dump_result_by_rank(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int phy_index, unsigned int rank_index) ++{ ++ unsigned int mask = 1 << phy_index; /* DDR_BYPASS_PHY0_MASK DDR_BYPASS_PHY1_MASK */ ++ const struct rank_data_st *rank_st = &ddrtr_result->phy_st[phy_index].rank_st[rank_index]; ++ ++ if (rank_st->item & mask) ++ return; ++ ++ ddr_info("PHY[%x] RANK[%x]:", phy_index, rank_index); ++ dump_result(&rank_st->ddrtr_data); ++} ++ ++static void dump_result_by_phy(const struct ddr_training_result_st *ddrtr_result, unsigned int phy_index) ++{ ++ int i; ++ const struct phy_data_st *phy_st = &ddrtr_result->phy_st[phy_index]; ++ ++ if (phy_st->rank_num > DDR_SUPPORT_RANK_MAX) { ++ ddr_error("loop upper limit cfg->rank_num out of range!"); ++ return; ++ } ++ for (i = 0; i < phy_st->rank_num; i++) ++ dump_result_by_rank(ddrtr_result, phy_index, i); ++} ++ ++/* Display ddr training result before return to DDR */ ++static void dump_result_all(const struct ddr_training_result_st *ddrtr_result) ++{ ++ int i; ++ ++ if (ddrtr_result->phy_num > DDR_PHY_NUM) { ++ ddr_error("loop upper limit phy number out of range, phy_num = %x", ++ ddrtr_result->phy_num); ++ return; ++ } ++ for (i = 0; i < ddrtr_result->phy_num; i++) ++ dump_result_by_phy(ddrtr_result, i); ++} ++ ++/* ++ * If you need to execute mpr, add DDR_TRAINING_CMD_MPR -- ddr_mpr_training_func(cfg) ++ * lpca:DDR_TRAINING_CMD_LPCA -- ddr_lpca_training_func(cfg) ++ */ ++int ddr_training_cmd_func(struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int item; ++ ++ if ((cfg == NULL) || (cfg->cmd_st == NULL)) ++ return -1; ++ ++ item = cfg->cur_item; ++ struct ddr_cmd_st *cmd_st = (struct ddr_cmd_st *)cfg->cmd_st; ++ ++ if (cmd_st == NULL) ++ return -1; ++ ++ ddr_debug("DDR training cmd[%x]", cmd_st->cmd); ++ ++ switch (cmd_st->cmd) { ++ case DDR_TRAINING_CMD_SW: ++ result = ddr_dataeye_training_func(cfg); ++ result += ddr_vref_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_DATAEYE: ++ result = ddr_dataeye_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_HW: ++ result = ddr_hw_training_if(); ++ break; ++ case DDR_TRAINING_CMD_WL: ++ result = ddr_wl_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_GATE: ++ result = ddr_gating_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_VREF: ++ result = ddr_vref_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_AC: ++ result = ddr_ac_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_SW_NO_WL: ++ /* wl bypass */ ++ reg_write(item | DDR_BYPASS_WL_MASK, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ result = ddr_dataeye_training_func(cfg); ++ result += ddr_vref_training_func(cfg); ++ /* restore cfg */ ++ reg_write(item, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ break; ++ case DDR_TRAINING_CMD_CONSOLE: ++ result = ddr_training_console_if(); ++ break; ++ default: ++ result = -1; ++ break; ++ } ++ ++ return result; ++} ++ ++/* DDR training command entry. Call by cmd_ddr_handle(). */ ++struct ddr_training_result_st *ddr_training_cmd_entry(const struct ddr_cmd_st *cmd_st) ++{ ++ int result; ++ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ struct ddr_cmd_st cmd_in_sram; ++ ++ if (cmd_st == NULL) { ++ ddr_error("Pointer parameter cmd_st is NULL!"); ++ return NULL; ++ } ++ ++ g_ddr_training_addr_start = cmd_st->start; ++ g_ddr_training_addr_end = cmd_st->start + cmd_st->length; ++ g_ddr_print_level = cmd_st->level; ++ ++ ddr_info("DDR Training Version: "DDR_TRAINING_VER); ++ ddr_debug("DDR training command entry. Sysctl[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG)); ++ ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ ddr_debug("Rank1 Sysctl[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC)); ++#endif ++ ++ ddr_training_cfg_init(cfg); ++ memcpy(&cmd_in_sram, cmd_st, sizeof(struct ddr_cmd_st)); ++ cfg->cmd_st = (void *)&cmd_in_sram; ++ cfg->res_st = (void *)&g_ddrt_result_sram; ++ ++ ddr_training_result_init(cfg, &g_ddrt_result_sram); ++ ++ if (cmd_st->cmd == DDR_TRAINING_CMD_HW) ++ result = ddr_hw_training(cfg); ++ else if (cmd_st->cmd == DDR_TRAINING_CMD_PCODE) ++ result = ddr_pcode_training(cfg); ++ else if (cmd_st->cmd == DDR_TRAINING_CMD_DCC) ++ result = ddr_dcc_training_func(cfg); ++ else if (cmd_st->cmd == DDR_TRAINING_CMD_CONSOLE) ++ result = ddr_training_console_if(); ++ else ++ result = ddr_training_all(cfg); ++ ++ dump_result_all(&g_ddrt_result_sram); ++ ++ if (!result) { ++ return &g_ddrt_result_sram; ++ } else { ++ ddr_debug("DDR training result[%x]", result); ++ return 0; ++ } ++} +diff --git a/drivers/ddr/vendor/default/cmd_bin/ddr_training_uart.c b/drivers/ddr/vendor/default/cmd_bin/ddr_training_uart.c +new file mode 100644 +index 0000000..b339d56 +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_bin/ddr_training_uart.c +@@ -0,0 +1,60 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "ddr_training_custom.h" ++ ++#define UART_PL01X_DR 0x00 /* Data read or written from the interface. */ ++#define UART_PL01X_FR 0x18 /* Flag register (Read only). */ ++#define UART_PL01X_FR_TXFF 0x20 ++ ++#define io_write(addr, val) (*(volatile unsigned int *)(addr) = (val)) ++#define io_read(addr) (*(volatile unsigned int *)(addr)) ++ ++void uart_early_putc(const char c) ++{ ++ /* Wait until there is space in the FIFO */ ++ while (io_read(DDR_REG_BASE_UART0 + UART_PL01X_FR) & UART_PL01X_FR_TXFF); ++ ++ /* Send the character */ ++ io_write(DDR_REG_BASE_UART0 + UART_PL01X_DR, c); ++} ++ ++void uart_early_puts(const char *s) ++{ ++ if (s == NULL) ++ return; ++ while (*s) ++ uart_early_putc (*s++); ++} ++ ++void uart_early_put_hex(const unsigned int hex) ++{ ++ int i; ++ char c; ++ ++ for (i = 28; i >= 0; i -= 4) { /* start from bit28, 4 bit per char */ ++ c = (hex >> (unsigned int)i) & 0x0F; ++ if (c < 10) /* Decimal 10 */ ++ c += '0'; ++ else ++ c += 'A' - 10; /* Decimal 10 */ ++ uart_early_putc(c); ++ } ++} +diff --git a/drivers/ddr/vendor/default/cmd_ddr_training_v2.c b/drivers/ddr/vendor/default/cmd_ddr_training_v2.c +new file mode 100644 +index 0000000..ec60176 +--- /dev/null ++++ b/drivers/ddr/vendor/default/cmd_ddr_training_v2.c +@@ -0,0 +1,296 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ddr_training_impl.h" ++ ++#ifndef TEXT_BASE ++#define TEXT_BASE (CONFIG_SYS_TEXT_BASE) /* for arm64 u-boot-2016.11 */ ++#endif ++ ++#define DDR_TRAINING_ENV "ddrtr" ++#define DDR_TRAINING_ENV_UN "unddrtr" ++ ++#define DDR_TRAINING_DDRT_START_OFFSET 0x400000 /* 4M */ ++#define DDR_TRAINING_DDRT_LENGTH 0x400000 /* 4M at lease 0x8000 */ ++ ++#define DDR_CMD_SW_STR "training" ++#define DDR_CMD_TR_STR "tr" ++#define DDR_CMD_HW_STR "hw" ++#define DDR_CMD_MPR_STR "mpr" ++#define DDR_CMD_WL_STR "wl" ++#define DDR_CMD_GATE_STR "gate" ++#define DDR_CMD_DATAEYE_STR "dataeye" ++#define DDR_CMD_VREF_STR "vref" ++#define DDR_CMD_DPMC_STR "dpmc" ++#define DDR_CMD_DCC_STR "dcc" ++#define DDR_CMD_PCODE_STR "pcode" ++#define DDR_CMD_AC_STR "ac" ++#define DDR_CMD_LPCA_STR "lpca" ++#define DDR_CMD_LOG_STR "log" ++#define DDR_CMD_BOOT_STR "boot" ++#define DDR_CMD_CONSOLE_STR "console" ++ ++static struct ddr_training_result_st g_ddrtr_result_st; /* DDR training result */ ++static int g_ddr_log_level = DDR_LOG_ERROR; /* DDR training log level */ ++ ++#ifdef DDR_TRAINING_EXEC_TIME ++/* ++ * Start timer for calculate DDR training execute time. ++ * NOTE: Just only for debug. ++ */ ++static void cmd_exec_timer_start(void) ++{ ++ /* timer start */ ++ reg_write(0, 0xF8002000); /* REG_BASE_TIMER01 + REG_TIMER_RELOAD */ ++ /* TIMER_EN | TIMER_MODE |TIMER_PRE | TIMER_SIZE, REG_TIMER_CONTROL */ ++ reg_write(0xc2, 0xF8002008); ++ reg_write(0xffffffff, 0xF8002000); ++} ++ ++/* ++ * Stop timer for calculate DDR training execute time. ++ * NOTE: Just only for debug. ++ */ ++static void cmd_exec_timer_stop(void) ++{ ++ /* timer stop */ ++ reg_write(0, 0xF8002008); /* REG_TIMER_CONTROL */ ++ /* REG_TIMER_VALUE, 24MHz */ ++ printf("DDR training execute time: [%d]us\n", ++ (0xffffffff - reg_read(0xF8002004)) / 24); /* 24MHz */ ++} ++#endif ++ ++/* ++ * Match string command. ++ * NOTE: Write leveling not support run repeatedly, ++ * so limit WL only run one time. ++ */ ++static int cmd_ddr_match(const char *str, int *cmd) ++{ ++ static int wl_done; /* Write leveling control */ ++ ++ if (!strncmp(str, DDR_CMD_SW_STR, sizeof(DDR_CMD_SW_STR))) { ++ *cmd = DDR_TRAINING_CMD_SW_NO_WL; ++ } else if (!strncmp(str, DDR_CMD_TR_STR, sizeof(DDR_CMD_TR_STR))) { ++ if (wl_done) { ++ *cmd = DDR_TRAINING_CMD_SW_NO_WL; ++ } else { ++ wl_done++; ++ *cmd = DDR_TRAINING_CMD_SW; ++ } ++ } else if (!strncmp(str, DDR_CMD_HW_STR, sizeof(DDR_CMD_HW_STR))) { ++ *cmd = DDR_TRAINING_CMD_HW; ++ } else if (!strncmp(str, DDR_CMD_MPR_STR, sizeof(DDR_CMD_MPR_STR))) { ++ *cmd = DDR_TRAINING_CMD_MPR; ++ } else if (!strncmp(str, DDR_CMD_WL_STR, sizeof(DDR_CMD_WL_STR))) { ++ if (wl_done) { ++ printf("WL not support run repeatedly. %s", ++ "Already done once, can not do again\n"); ++ return -1; ++ } else { ++ *cmd = DDR_TRAINING_CMD_WL; ++ wl_done++; ++ } ++ } else if (!strncmp(str, DDR_CMD_GATE_STR, sizeof(DDR_CMD_GATE_STR))) { ++ *cmd = DDR_TRAINING_CMD_GATE; ++ } else if (!strncmp(str, DDR_CMD_DATAEYE_STR, sizeof(DDR_CMD_DATAEYE_STR))) { ++ *cmd = DDR_TRAINING_CMD_DATAEYE; ++ } else if (!strncmp(str, DDR_CMD_VREF_STR, sizeof(DDR_CMD_VREF_STR))) { ++ *cmd = DDR_TRAINING_CMD_VREF; ++ } else if (!strncmp(str, DDR_CMD_DPMC_STR, sizeof(DDR_CMD_DPMC_STR))) { ++ *cmd = DDR_TRAINING_CMD_DPMC; ++ } else if (!strncmp(str, DDR_CMD_AC_STR, sizeof(DDR_CMD_AC_STR))) { ++ *cmd = DDR_TRAINING_CMD_AC; ++ } else if (!strncmp(str, DDR_CMD_LPCA_STR, sizeof(DDR_CMD_LPCA_STR))) { ++ *cmd = DDR_TRAINING_CMD_LPCA; ++ } else if (!strncmp(str, DDR_CMD_DCC_STR, sizeof(DDR_CMD_DCC_STR))) { ++ *cmd = DDR_TRAINING_CMD_DCC; ++ } else if (!strncmp(str, DDR_CMD_PCODE_STR, sizeof(DDR_CMD_PCODE_STR))) { ++ *cmd = DDR_TRAINING_CMD_PCODE; ++ } else if (!strncmp(str, DDR_CMD_CONSOLE_STR, sizeof(DDR_CMD_CONSOLE_STR))) { ++ *cmd = DDR_TRAINING_CMD_CONSOLE; ++ } else { ++ printf("Command [ddr %s] is unsupport\n", str); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Handle DDR training. ++ * Copy training codes from DDR to SRAM. ++ */ ++static int cmd_ddr_handle(int cmd) ++{ ++ struct ddr_training_result_st *result_st = NULL; ++ struct ddr_cmd_st cmd_st; ++ ++ cmd_st.cmd = cmd; ++ cmd_st.level = g_ddr_log_level; ++ cmd_st.start = TEXT_BASE + DDR_TRAINING_DDRT_START_OFFSET; ++ cmd_st.length = DDR_TRAINING_DDRT_LENGTH; ++ ++ printf("DDR training area: 0x%08X - 0x%08X\n", ++ cmd_st.start, cmd_st.start + cmd_st.length); ++ ++#ifdef DDR_TRAINING_EXEC_TIME ++ cmd_exec_timer_start(); ++#endif ++ ++ result_st = ddr_cmd_training_if(&cmd_st); ++ ++#ifdef DDR_TRAINING_EXEC_TIME ++ cmd_exec_timer_stop(); ++#endif ++ ++ if (result_st == NULL) ++ return -1; ++ ++ /* copy training result from SRAM to DDR */ ++ memcpy((void *)&g_ddrtr_result_st, result_st, ++ sizeof(*result_st)); ++ printf("DDR training finished\n"); ++ ++ return 0; ++} ++ ++/* DDR training cmd dispatch */ ++static int cmd_ddr_dispatch(int cmd) ++{ ++ int result; ++ ++ result = cmd_ddr_handle(cmd); ++ switch (cmd) { ++ case DDR_TRAINING_CMD_SW: ++ case DDR_TRAINING_CMD_SW_NO_WL: ++ case DDR_TRAINING_CMD_CONSOLE: ++ ddr_cmd_result_display(&g_ddrtr_result_st, DDR_TRAINING_CMD_DATAEYE | DDR_TRAINING_CMD_LPCA); ++ break; ++ case DDR_TRAINING_CMD_DATAEYE: ++ case DDR_TRAINING_CMD_VREF: ++ ddr_cmd_result_display(&g_ddrtr_result_st, DDR_TRAINING_CMD_DATAEYE); ++ break; ++ case DDR_TRAINING_CMD_WL: ++ case DDR_TRAINING_CMD_GATE: ++ case DDR_TRAINING_CMD_HW: ++ case DDR_TRAINING_CMD_DPMC: ++ case DDR_TRAINING_CMD_DCC: ++ case DDR_TRAINING_CMD_PCODE: ++ break; ++ case DDR_TRAINING_CMD_LPCA: ++ ddr_cmd_result_display(&g_ddrtr_result_st, DDR_TRAINING_CMD_LPCA); ++ break; ++ default: ++ break; ++ } ++ ++ ddr_reg_result_display(&g_ddrtr_result_st); ++ return result; ++} ++ ++/* Set DDR training log level */ ++static int cmd_ddr_set_log_level(char * const argv[]) ++{ ++ int level; ++ const char *str; ++ ++ str = argv[1]; ++ if (strncmp(str, DDR_CMD_LOG_STR, sizeof(DDR_CMD_LOG_STR))) { ++ printf("Command [ddr %s] is unsupport\n", str); ++ return -1; ++ } ++ ++ str = argv[2]; /* argv[2]:Third string: ddr log level */ ++ if (!strncmp(str, DDR_LOG_INFO_STR, sizeof(DDR_LOG_INFO_STR))) { ++ level = DDR_LOG_INFO; ++ } else if (!strncmp(str, DDR_LOG_DEBUG_STR, sizeof(DDR_LOG_DEBUG_STR))) { ++ level = DDR_LOG_DEBUG; ++ } else if (!strncmp(str, DDR_LOG_WARNING_STR, sizeof(DDR_LOG_WARNING_STR))) { ++ level = DDR_LOG_WARNING; ++ } else if (!strncmp(str, DDR_LOG_ERROR_STR, sizeof(DDR_LOG_ERROR_STR))) { ++ level = DDR_LOG_ERROR; ++ } else if (!strncmp(str, DDR_LOG_FATAL_STR, sizeof(DDR_LOG_FATAL_STR))) { ++ level = DDR_LOG_FATAL; ++ } else { ++ printf("Command [ddr log %s] is unsupport\n", str); ++ return -1; ++ } ++ ++ g_ddr_log_level = level; ++ printf("Set DDR training log level [%s] suc\n", str); ++ ++ return 0; ++} ++ ++/* ++ * Accept DDR training cmd. ++ * Set training result to env without save. ++ */ ++static int do_ddr_training(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ const char *str = NULL; ++ int cmd; ++ ++ struct tr_relate_reg relate_reg; ++ struct tr_relate_reg *reg = &relate_reg; ++ ++ if (argc < 2 || argc > 3) { /* cmd string should be 2 or 3 */ ++ return -1; /* add cmd_usage(cmdtp) if needed */ ++ } else if (argc == 3) { /* 3 cmd string means ddr log level */ ++ return cmd_ddr_set_log_level(argv); ++ } ++ ++ str = argv[1]; ++ ++ if (cmd_ddr_match(str, &cmd)) ++ return -1; ++ ++ ddr_boot_cmd_save_func(reg); ++ ++ if (cmd_ddr_dispatch(cmd)) ++ return -1; ++ ++ ddr_boot_cmd_restore_func(reg); ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ ddr, CONFIG_SYS_MAXARGS, 1, do_ddr_training, ++ "ddr training function", ++ "training - DDR sofeware(Gate/Dataeye/Vref) training.\n" ++ "ddr tr - DDR sofeware(WL/Gate/Dataeye/Vref) training.\n" ++ "ddr wl - DDR Write leveling training.\n" ++ "ddr gate - DDR gate training.\n" ++ "ddr dataeye - DDR dataeye training and display training result.\n" ++ "ddr vref - DDR vref training.\n" ++ "ddr dpmc - DDR dpmc training.\n" ++ "ddr hw - DDR hardware training.\n" ++ "ddr mpr - DDR Multi-Purpose Register training.\n" ++ "ddr ac - DDR address command training.\n" ++ "ddr lpca - LPDDR command address training.\n" ++ "ddr dcc - DDR Duty Correction Control training.\n" ++ "ddr pcode - DDR io pcode training.\n" ++ "ddr console - DDR do training in SRAM.\n" ++ "ddr log [level] - DDR log level. [info,debug,warning,error,fatal]\n" ++); +diff --git a/drivers/ddr/vendor/default/ddr_ac_training.c b/drivers/ddr/vendor/default/ddr_ac_training.c +new file mode 100644 +index 0000000..9bec785 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ac_training.c +@@ -0,0 +1,308 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __AC_TRAINING__ ++#ifdef DDR_AC_TRAINING_CONFIG ++/* ++ * Get clk value. ++ * Assume clk0 and clk1 is the same. ++ */ ++static int ddr_ac_get_clk(unsigned int base_phy) ++{ ++ unsigned int val; ++ unsigned int ac_phy_ctl; ++ /* Static register have to read two times to get the right value. */ ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ /* halft_dramclk0 */ ++ val = (ac_phy_ctl >> PHY_ACPHY_DRAMCLK0_BIT) & PHY_ACPHY_DRAMCLK_MASK; ++ val = (val << PHY_ACPHY_DRAMCLK_EXT_BIT) | ++ ((ac_phy_ctl >> PHY_ACPHY_DCLK0_BIT) & PHY_ACPHY_DCLK_MASK); ++ return val; ++} ++ ++/* Set clk0 and clk1 the same value */ ++static void ddr_ac_set_clk(unsigned int base_phy, unsigned int val) ++{ ++ unsigned int ac_phy_ctl, dramclk, dclk; ++ ++ dclk = val & PHY_ACPHY_DCLK_MASK; ++ dramclk = (val >> PHY_ACPHY_DRAMCLK_EXT_BIT) & PHY_ACPHY_DRAMCLK_MASK; ++ /* Static register have to read two times to get the right value. */ ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ /* clear cp1p_dclk0 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK0_BIT)); ++ /* clear ck2p_dclk1 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK1_BIT)); ++ /* clear halft_dramclk0 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DRAMCLK_MASK << PHY_ACPHY_DRAMCLK0_BIT)); ++ /* clear halft_dramclk1 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DRAMCLK_MASK << PHY_ACPHY_DRAMCLK1_BIT)); ++ ++ ac_phy_ctl |= (dclk << PHY_ACPHY_DCLK0_BIT); /* set cp1p_dclk0 */ ++ ac_phy_ctl |= (dclk << PHY_ACPHY_DCLK1_BIT); /* set cp2p_dclk1 */ ++ /* set halft_dramclk0 */ ++ ac_phy_ctl |= (dramclk << PHY_ACPHY_DRAMCLK0_BIT); ++ /* set halft_dramclk1 */ ++ ac_phy_ctl |= (dramclk << PHY_ACPHY_DRAMCLK1_BIT); ++ reg_write(ac_phy_ctl, base_phy + DDR_PHY_ACPHYCTL7); ++} ++ ++/* ++ * Get cs bdl value. ++ * Assume cs0 and cs 1 is the same. ++ */ ++static int ddr_ac_get_cs(unsigned int base_phy) ++{ ++ return (reg_read(base_phy + DDR_PHY_ACCMDBDL2) >> 1) & PHY_BDL_MASK; ++} ++ ++/* Set CS value */ ++static void ddr_ac_set_cs(unsigned int base_phy, unsigned int val) ++{ ++ unsigned int ac_cmd_bdl; ++ ++ ac_cmd_bdl = reg_read(base_phy + DDR_PHY_ACCMDBDL2); ++ ac_cmd_bdl &= (~(PHY_BDL_MASK << PHY_ACCMD_CS0_BIT)); /* clear cs0_bdl */ ++ ac_cmd_bdl &= (~(PHY_BDL_MASK << PHY_ACCMD_CS1_BIT)); /* clear cs1_bdl */ ++ ac_cmd_bdl |= (val << PHY_ACCMD_CS0_BIT); /* set cs0_bdl */ ++ ac_cmd_bdl |= (val << PHY_ACCMD_CS1_BIT); /* set cs1_bdl */ ++ reg_write(ac_cmd_bdl, base_phy + DDR_PHY_ACCMDBDL2); ++} ++ ++static int ddr_ac_ddrt_test(unsigned int mask, unsigned int base_phy) ++{ ++ unsigned int regval; ++ unsigned int times = 0; ++ ++ ddrt_reg_write(mask | DDRT_CFG_START, DDR_REG_BASE_DDRT + DDRT_OP); ++ ddrt_reg_write(0, DDR_REG_BASE_DDRT + DDRT_STATUS); ++ ++ do { ++ regval = ddrt_reg_read(DDR_REG_BASE_DDRT + DDRT_STATUS); ++ times++; ++ } while ((!(regval & DDRT_TEST_DONE_MASK)) && (times < DDRT_WAIT_TIMEOUT)); ++ ++ if (times >= DDRT_WAIT_TIMEOUT) { ++ ddr_fatal("DDRT wait timeout"); ++ ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, base_phy, -1, -1); ++ return -1; ++ } ++ ++ /* DDRT_WRITE_ONLY_MODE */ ++ if ((mask & DDRT_TEST_MODE_MASK) == DDRT_WRITE_ONLY_MODE) ++ return 0; ++ ++ /* DDRT_READ_ONLY_MODE */ ++ if (regval & DDRT_TEST_PASS_MASK) /* No error occurred, test pass. */ ++ return 0; ++ else ++ return -1; ++} ++ ++/* Check CS value */ ++static int ddr_ac_check_cs(unsigned int base_phy, unsigned int def_cs, unsigned int step) ++{ ++ ddr_ac_set_cs(base_phy, def_cs + step); ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_ac_ddrt_test(DDRT_WRITE_ONLY_MODE, base_phy); ++ ++ ddr_ac_set_cs(base_phy, def_cs); /* restore default to check */ ++ ddr_phy_cfg_update(base_phy); ++ ++ return ddr_ac_ddrt_test(DDRT_READ_ONLY_MODE, base_phy); ++} ++ ++/* Check CLK value */ ++static int ddr_ac_check_clk(const struct ddr_cfg_st *cfg, unsigned int def_clk, ++ struct ddr_delay_st *def_phase, unsigned int step) ++{ ++ int i; ++ unsigned int wdqs_phase_range, wdq_phase_range, phase_range; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ ++ /* set new value */ ++ ddr_ac_set_clk(base_phy, def_clk + step); ++ for (i = 0; i < byte_num; i++) { ++ wdqs_phase_range = PHY_WDQS_PHASE_MASK - ++ ((def_phase->phase[i] >> PHY_WDQS_PHASE_BIT) & PHY_WDQS_PHASE_MASK); ++ wdq_phase_range = PHY_WDQ_PHASE_MASK - ++ ((def_phase->bdl[i] >> PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK); ++ phase_range = (wdqs_phase_range < wdq_phase_range) ? ++ wdqs_phase_range : wdq_phase_range; ++ phase_range = (phase_range < step) ? phase_range : step; ++ ++ reg_write(def_phase->phase[i] + (phase_range << PHY_WDQS_PHASE_BIT), ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ reg_write(def_phase->bdl[i] + (phase_range << PHY_WDQ_PHASE_BIT), ++ base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_ac_ddrt_test(DDRT_WRITE_ONLY_MODE, base_phy); ++ ++ /* restore default to check */ ++ ddr_ac_set_clk(base_phy, def_clk); ++ for (i = 0; i < byte_num; i++) { ++ reg_write(def_phase->phase[i], ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ reg_write(def_phase->bdl[i], ++ base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return ddr_ac_ddrt_test(DDRT_READ_ONLY_MODE, base_phy); ++} ++ ++/* Find CS difference */ ++static int ddr_ac_find_cs(unsigned int base_phy) ++{ ++ unsigned int def_cs, step; ++ ++ def_cs = ddr_ac_get_cs(base_phy); ++ for (step = 1; step <= (PHY_BDL_MASK - def_cs); step++) { ++ if (ddr_ac_check_cs(base_phy, def_cs, step)) { ++ ddr_debug("PHY[%x] default cs[%x], find diff_cs[%x]", base_phy, def_cs, step); ++ break; ++ } ++ } ++ ++ return step; ++} ++ ++/* Find CLK difference */ ++static int ddr_ac_find_clk(const struct ddr_cfg_st *cfg) ++{ ++ int i; ++ unsigned int def_clk, step; ++ struct ddr_delay_st def_phase; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ ++ def_clk = ddr_ac_get_clk(base_phy); ++ for (i = 0; i < byte_num; i++) { ++ /* WDQS phase */ ++ def_phase.phase[i] = reg_read(base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ /* WDQ phase */ ++ def_phase.bdl[i] = reg_read(base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ++ for (step = 1; step <= (PHY_ACPHY_CLK_MAX - def_clk); step++) { ++ if (ddr_ac_check_clk(cfg, def_clk, &def_phase, step)) { ++ ddr_debug("PHY[%x] default clk[%x], find diff_clk[%x]", base_phy, def_clk, step); ++ break; ++ } ++ } ++ ++ return step; ++} ++ ++static void ddr_ac_set_phase_range(unsigned int base_phy, unsigned int def_clk, ++ unsigned int diff_clk, unsigned int phase_tmp, const struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ unsigned int clk_phase; ++ unsigned int wdqs_phase, wdq_phase; ++ unsigned int wdqs_phase_range, wdq_phase_range, phase_range; ++ ++ clk_phase = (diff_clk - phase_tmp) >> 1; ++ ++ /* set new value */ ++ ddr_ac_set_clk(base_phy, def_clk + clk_phase); ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ wdqs_phase = reg_read(base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ wdq_phase = reg_read(base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ ++ wdqs_phase_range = PHY_WDQS_PHASE_MASK - ++ ((wdqs_phase >> PHY_WDQS_PHASE_BIT) & PHY_WDQS_PHASE_MASK); ++ wdq_phase_range = PHY_WDQ_PHASE_MASK - ++ ((wdq_phase >> PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK); ++ phase_range = (wdqs_phase_range < wdq_phase_range) ? wdqs_phase_range : wdq_phase_range; ++ phase_range = (phase_range < clk_phase) ? phase_range : clk_phase; ++ reg_write(wdqs_phase + (phase_range << PHY_WDQS_PHASE_BIT), ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ reg_write(wdq_phase + (phase_range << PHY_WDQ_PHASE_BIT), ++ base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ddr_debug("PHY[%x] def clk[%x] add phase[%x]", base_phy, def_clk, clk_phase); ++} ++ ++/* DDR AC training */ ++static int ddr_ac_training(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int diff_cs, diff_clk; ++ unsigned int cs_bdl, phase_tmp; ++ unsigned int def_clk, def_cs; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ ddr_debug("DDR AC training"); ++ def_clk = ddr_ac_get_clk(base_phy); ++ diff_cs = ddr_ac_find_cs(base_phy); /* setup time(bdl) */ ++ diff_clk = ddr_ac_find_clk(cfg); /* hold time(phase) */ ++ /* cs bdl transform to clk phase */ ++ phase_tmp = diff_cs >> DDR_BDL_PHASE_REL; ++ ++ if (diff_clk > phase_tmp) { ++ ddr_ac_set_phase_range(base_phy, def_clk, diff_clk, phase_tmp, cfg); ++ } else { ++ def_cs = ddr_ac_get_cs(base_phy); ++ cs_bdl = 0; ++ if (diff_cs > (diff_clk << DDR_BDL_PHASE_REL)) ++ cs_bdl = diff_cs - (diff_clk << DDR_BDL_PHASE_REL); ++ ++ ddr_ac_set_cs(base_phy, def_cs + cs_bdl); ++ ddr_debug("PHY[%x] def cs[%x] add bdl[%x]", base_phy, def_cs, cs_bdl); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return 0; ++} ++ ++int ddr_ac_training_func(const struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ struct tr_relate_reg relate_reg; ++ ++ /* AC training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_AC_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_AC_MASK); ++ ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ result += ddr_ac_training(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_ac_training_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR AC training"); ++ ++ return 0; ++} ++#endif /* DDR_AC_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default/ddr_cmd_ctl.c b/drivers/ddr/vendor/default/ddr_cmd_ctl.c +new file mode 100644 +index 0000000..b365a05 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_cmd_ctl.c +@@ -0,0 +1,636 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#if PHY_DQ_BDL_LEVEL == 32 ++static void print_dataeye_win(unsigned int dq_num, unsigned int range, ++ unsigned int dqs, unsigned int dq, unsigned int win) ++{ ++ unsigned int k; ++ ++ printf("%-4u", dq_num); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) { ++ if (k >= (range >> DDR_DATAEYE_RESULT_BIT) && ++ k <= (range & DDR_DATAEYE_RESULT_MASK)) { ++ printf("%-3s", "-"); ++ } else { ++ printf("%-3s", "X"); ++ } ++ } ++ printf(" 0x%08x 0x%-4x%-4u%-4u\n", range, dqs, dq, win); ++} ++ ++static void print_dataeye_title(const char *phase) ++{ ++ unsigned int k; ++ ++ printf("%-4s", "DQ"); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) ++ printf("%-3u", k); ++ ++ printf(" %-10s %-6s%-4s%-4s\n", "RANGE", phase, "DQ", "WIN"); ++} ++#else ++static void print_dataeye_win(unsigned int dq_num, unsigned int range, ++ unsigned int dqs, unsigned int dq, unsigned int win) ++{ ++ unsigned int k; ++ ++ printf("%-4u", dq_num); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) { ++ if (k >= (range >> DDR_DATAEYE_RESULT_BIT) && ++ k <= (range & DDR_DATAEYE_RESULT_MASK)) ++ printf("%-1s", "-"); ++ else ++ printf("%-1s", "X"); ++ } ++ printf(" 0x%08x 0x%-4x%-4u%-4u\n", range, dqs, dq, win); ++} ++ ++static void print_dataeye_title(const char *phase) ++{ ++ unsigned int k; ++ ++ printf("%-4s", "DQ"); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) { ++ if (k % 4 == 0) /* Print out the CA number which is a multiple of 4 */ ++ printf("%-4u", k); ++ } ++ printf(" %-10s %-6s%-4s%-4s\n", "RANGE", phase, "DQ", "WIN"); ++} ++#endif ++ ++static struct ddr_reg_val_st training_reg_val_rank0[] = { ++ /* rank, byte, offset, value, name */ ++ {0, 0, ddr_phy_dxwdqsdly(0, 0), 0, "WDQS Byte0"}, ++ {0, 1, ddr_phy_dxwdqsdly(0, 1), 0, "WDQS Byte1"}, ++ {0, 2, ddr_phy_dxwdqsdly(0, 2), 0, "WDQS Byte2"}, ++ {0, 3, ddr_phy_dxwdqsdly(0, 3), 0, "WDQS Byte3"}, ++ {0, 0, ddr_phy_dxnwdqdly(0, 0), 0, "WDQ Phase Byte0"}, ++ {0, 1, ddr_phy_dxnwdqdly(0, 1), 0, "WDQ Phase Byte1"}, ++ {0, 2, ddr_phy_dxnwdqdly(0, 2), 0, "WDQ Phase Byte2"}, ++ {0, 3, ddr_phy_dxnwdqdly(0, 3), 0, "WDQ Phase Byte3"}, ++ {0, 0, ddr_phy_dxnwdqnbdl0(0, 0), 0, "WDQ BDL DQ0-DQ3"}, ++ {0, 0, ddr_phy_dxnwdqnbdl1(0, 0), 0, "WDQ BDL DQ4-DQ7"}, ++ {0, 1, ddr_phy_dxnwdqnbdl0(0, 1), 0, "WDQ BDL DQ8-DQ11"}, ++ {0, 1, ddr_phy_dxnwdqnbdl1(0, 1), 0, "WDQ BDL DQ12-DQ15"}, ++ {0, 2, ddr_phy_dxnwdqnbdl0(0, 2), 0, "WDQ BDL DQ16-DQ19"}, ++ {0, 2, ddr_phy_dxnwdqnbdl1(0, 2), 0, "WDQ BDL DQ20-DQ23"}, ++ {0, 3, ddr_phy_dxnwdqnbdl0(0, 3), 0, "WDQ BDL DQ24-DQ27"}, ++ {0, 3, ddr_phy_dxnwdqnbdl1(0, 3), 0, "WDQ BDL DQ28-DQ31"}, ++ {0, 0, ddr_phy_dxnwdqnbdl2(0, 0), 0, "WDM Byte0"}, ++ {0, 1, ddr_phy_dxnwdqnbdl2(0, 1), 0, "WDM Byte1"}, ++ {0, 2, ddr_phy_dxnwdqnbdl2(0, 2), 0, "WDM Byte2"}, ++ {0, 3, ddr_phy_dxnwdqnbdl2(0, 3), 0, "WDM Byte3"}, ++ {0, 0, ddr_phy_dxnoebdl(0, 0), 0, "Write DQ/DQS OE Byte0"}, ++ {0, 1, ddr_phy_dxnoebdl(0, 1), 0, "Write DQ/DQS OE Byte1"}, ++ {0, 2, ddr_phy_dxnoebdl(0, 2), 0, "Write DQ/DQS OE Byte2"}, ++ {0, 3, ddr_phy_dxnoebdl(0, 3), 0, "Write DQ/DQS OE Byte3"}, ++ {0, 0, ddr_phy_dxnrdqsdly(0), 0, "RDQS Byte0"}, ++ {0, 1, ddr_phy_dxnrdqsdly(1), 0, "RDQS Byte1"}, ++ {0, 2, ddr_phy_dxnrdqsdly(2), 0, "RDQS Byte2"}, ++ {0, 3, ddr_phy_dxnrdqsdly(3), 0, "RDQS Byte3"}, ++ {0, 0, ddr_phy_dxnrdqnbdl0(0, 0), 0, "RDQ BDL DQ0-DQ3"}, ++ {0, 0, ddr_phy_dxnrdqnbdl1(0, 0), 0, "RDQ BDL DQ4-DQ7"}, ++ {0, 1, ddr_phy_dxnrdqnbdl0(0, 1), 0, "RDQ BDL DQ8-DQ11"}, ++ {0, 1, ddr_phy_dxnrdqnbdl1(0, 1), 0, "RDQ BDL DQ12-DQ15"}, ++ {0, 2, ddr_phy_dxnrdqnbdl0(0, 2), 0, "RDQ BDL DQ16-DQ19"}, ++ {0, 2, ddr_phy_dxnrdqnbdl1(0, 2), 0, "RDQ BDL DQ20-DQ23"}, ++ {0, 3, ddr_phy_dxnrdqnbdl0(0, 3), 0, "RDQ BDL DQ24-DQ27"}, ++ {0, 3, ddr_phy_dxnrdqnbdl1(0, 3), 0, "RDQ BDL DQ28-DQ31"}, ++ {0, 0, ddr_phy_dxnrdqsgdly(0, 0), 0, "Gate Byte0"}, ++ {0, 1, ddr_phy_dxnrdqsgdly(0, 1), 0, "Gate Byte1"}, ++ {0, 2, ddr_phy_dxnrdqsgdly(0, 2), 0, "Gate Byte2"}, ++ {0, 3, ddr_phy_dxnrdqsgdly(0, 3), 0, "Gate Byte3"}, ++ {0, 0, DDR_PHY_ACCMDBDL2, 0, "CS"}, ++ {0, 0, DDR_PHY_ACPHYCTL7, 0, "CLK"}, ++ DDR_PHY_VREF_HOST_DISPLAY ++ DDR_PHY_VREF_DRAM_DISPLAY ++ DDR_DX_DPMC_DISPLAY ++ DDR_PHY_ADDRPH_DISPLAY ++ DDR_PHY_ADDRBDL_DISPLAY ++ DDR_PHY_DCC_DISPLAY ++}; ++ ++/* rank 1 */ ++static struct ddr_reg_val_st training_reg_val_rank1[] = { ++ {1, 0, ddr_phy_dxwdqsdly(1, 0), 0, "WDQS Byte0"}, ++ {1, 1, ddr_phy_dxwdqsdly(1, 1), 0, "WDQS Byte1"}, ++ {1, 2, ddr_phy_dxwdqsdly(1, 2), 0, "WDQS Byte2"}, ++ {1, 3, ddr_phy_dxwdqsdly(1, 3), 0, "WDQS Byte3"}, ++ {1, 0, ddr_phy_dxnwdqdly(1, 0), 0, "WDQ Phase Byte0"}, ++ {1, 1, ddr_phy_dxnwdqdly(1, 1), 0, "WDQ Phase Byte1"}, ++ {1, 2, ddr_phy_dxnwdqdly(1, 2), 0, "WDQ Phase Byte2"}, ++ {1, 3, ddr_phy_dxnwdqdly(1, 3), 0, "WDQ Phase Byte3"}, ++ {1, 0, ddr_phy_dxnwdqnbdl0(1, 0), 0, "WDQ BDL DQ0-DQ3"}, ++ {1, 0, ddr_phy_dxnwdqnbdl1(1, 0), 0, "WDQ BDL DQ4-DQ7"}, ++ {1, 1, ddr_phy_dxnwdqnbdl0(1, 1), 0, "WDQ BDL DQ8-DQ11"}, ++ {1, 1, ddr_phy_dxnwdqnbdl1(1, 1), 0, "WDQ BDL DQ12-DQ15"}, ++ {1, 2, ddr_phy_dxnwdqnbdl0(1, 2), 0, "WDQ BDL DQ16-DQ19"}, ++ {1, 2, ddr_phy_dxnwdqnbdl1(1, 2), 0, "WDQ BDL DQ20-DQ23"}, ++ {1, 3, ddr_phy_dxnwdqnbdl0(1, 3), 0, "WDQ BDL DQ24-DQ27"}, ++ {1, 3, ddr_phy_dxnwdqnbdl1(1, 3), 0, "WDQ BDL DQ28-DQ31"}, ++ {1, 0, ddr_phy_dxnwdqnbdl2(1, 0), 0, "WDM Byte0"}, ++ {1, 1, ddr_phy_dxnwdqnbdl2(1, 1), 0, "WDM Byte1"}, ++ {1, 2, ddr_phy_dxnwdqnbdl2(1, 2), 0, "WDM Byte2"}, ++ {1, 3, ddr_phy_dxnwdqnbdl2(1, 3), 0, "WDM Byte3"}, ++ {1, 0, ddr_phy_dxnoebdl(1, 0), 0, "Write DQ/DQS OE Byte0"}, ++ {1, 1, ddr_phy_dxnoebdl(1, 1), 0, "Write DQ/DQS OE Byte1"}, ++ {1, 2, ddr_phy_dxnoebdl(1, 2), 0, "Write DQ/DQS OE Byte2"}, ++ {1, 3, ddr_phy_dxnoebdl(1, 3), 0, "Write DQ/DQS OE Byte3"}, ++ ++ {1, 0, ddr_phy_dxnrdqsdly(0), 0, "RDQS Byte0"}, ++ {1, 1, ddr_phy_dxnrdqsdly(1), 0, "RDQS Byte1"}, ++ {1, 2, ddr_phy_dxnrdqsdly(2), 0, "RDQS Byte2"}, ++ {1, 3, ddr_phy_dxnrdqsdly(3), 0, "RDQS Byte3"}, ++ ++ {1, 0, ddr_phy_dxnrdqnbdl0(1, 0), 0, "RDQ BDL DQ0-DQ3"}, ++ {1, 0, ddr_phy_dxnrdqnbdl1(1, 0), 0, "RDQ BDL DQ4-DQ7"}, ++ {1, 1, ddr_phy_dxnrdqnbdl0(1, 1), 0, "RDQ BDL DQ8-DQ11"}, ++ {1, 1, ddr_phy_dxnrdqnbdl1(1, 1), 0, "RDQ BDL DQ12-DQ15"}, ++ {1, 2, ddr_phy_dxnrdqnbdl0(1, 2), 0, "RDQ BDL DQ16-DQ19"}, ++ {1, 2, ddr_phy_dxnrdqnbdl1(1, 2), 0, "RDQ BDL DQ20-DQ23"}, ++ {1, 3, ddr_phy_dxnrdqnbdl0(1, 3), 0, "RDQ BDL DQ24-DQ27"}, ++ {1, 3, ddr_phy_dxnrdqnbdl1(1, 3), 0, "RDQ BDL DQ28-DQ31"}, ++ {1, 0, ddr_phy_dxnrdqsgdly(1, 0), 0, "Gate Byte0"}, ++ {1, 1, ddr_phy_dxnrdqsgdly(1, 1), 0, "Gate Byte1"}, ++ {1, 2, ddr_phy_dxnrdqsgdly(1, 2), 0, "Gate Byte2"}, ++ {1, 3, ddr_phy_dxnrdqsgdly(1, 3), 0, "Gate Byte3"}, ++ {1, 0, DDR_PHY_ACCMDBDL2, 0, "CS"}, ++ {1, 0, DDR_PHY_ACPHYCTL7, 0, "CLK"}, ++ ++ DDR_PHY_VREF_HOST_DISPLAY_RANK1 ++ DDR_PHY_VREF_DRAM_DISPLAY ++ DDR_DX_DPMC_DISPLAY ++ DDR_PHY_ADDRPH_DISPLAY ++ DDR_PHY_ADDRBDL_DISPLAY ++ DDR_PHY_DCC_DISPLAY ++}; ++ ++static void ddr_cmd_result_print_write_dataeye(const struct ddr_training_data_st *ddrtr_data, ++ unsigned int win_min, unsigned int win_max, unsigned int win_sum) ++{ ++ unsigned int i, j; ++ unsigned int dq_num, dqs, dq, win; ++ ++ printf("Write window of prebit-deskew:\n"); ++ printf("--------------------------------------------------------\n"); ++ print_dataeye_title("DQPH"); ++ if (ddrtr_data->byte_num > DDR_PHY_BYTE_MAX) { ++ printf("byte num error, byte_num = %x", ddrtr_data->byte_num); ++ return; ++ } ++ for (j = 0; j < ddrtr_data->byte_num; j++) { ++ dqs = (reg_read(ddrtr_data->base_phy + ddr_phy_dxnwdqdly(ddrtr_data->rank_idx, j)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ dq_num = (j << 3) + i; /* shift left 3: 8 bit */ ++ if (dq_num >= DDR_PHY_BIT_MAX) ++ return; ++ ++ win = ddrtr_data->write.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ if (win < win_min) ++ win_min = win; ++ if (win > win_max) ++ win_max = win; ++ win_sum += win; ++ dq = ddrtr_data->write.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ print_dataeye_win(dq_num, ++ ddrtr_data->write.ddr_bit_result[dq_num], dqs, dq, win); ++ } ++ } ++ printf("--------------------------------------------------------\n"); ++ printf("Sum WIN: %u. Avg WIN: %u\n", win_sum, ++ win_sum / (ddrtr_data->byte_num * DDR_PHY_BIT_NUM)); ++ printf("Min WIN: %u. DQ Index: ", win_min); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->write.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_min) ++ printf("%u ", i); ++ } ++ printf("\nMax WIN: %u. DQ Index: ", win_max); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->write.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_max) ++ printf("%u ", i); ++ } ++ printf("\n\n"); ++} ++ ++static void ddr_cmd_result_print_read_dataeye(const struct ddr_training_data_st *ddrtr_data, ++ unsigned int win_min, unsigned int win_max, unsigned int win_sum) ++{ ++ unsigned int i, j; ++ unsigned int dq_num, dqs, dq, win; ++ ++ if (ddrtr_data->byte_num > DDR_PHY_BYTE_MAX) { ++ printf("get byte num fail, byte_num = %x", ddrtr_data->byte_num); ++ return; ++ } ++ printf("Read window of prebit-deskew:\n"); ++ printf("--------------------------------------------------------\n"); ++ print_dataeye_title("DQS"); ++ for (j = 0; j < ddrtr_data->byte_num; j++) { ++ dqs = reg_read(ddrtr_data->base_phy + ddr_phy_dxnrdqsdly(j)) & PHY_RDQS_BDL_MASK; ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ dq_num = (j << 3) + i; /* shift left 3: 8 bit */ ++ if (dq_num >= DDR_PHY_BIT_MAX) ++ return; ++ ++ win = ddrtr_data->read.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ if (win < win_min) ++ win_min = win; ++ if (win > win_max) ++ win_max = win; ++ win_sum += win; ++ dq = ddrtr_data->read.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ print_dataeye_win(dq_num, ++ ddrtr_data->read.ddr_bit_result[dq_num], dqs, dq, win); ++ } ++ } ++ printf("--------------------------------------------------------\n"); ++ printf("Sum WIN: %u. Avg WIN: %u\n", win_sum, ++ win_sum / (ddrtr_data->byte_num * DDR_PHY_BIT_NUM)); ++ printf("Min WIN: %u. DQ Index: ", win_min); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->read.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_min) ++ printf("%u ", i); ++ } ++ printf("\nMax WIN: %u. DQ Index: ", win_max); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->read.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_max) ++ printf("%u ", i); ++ } ++ printf("\n\n"); ++} ++ ++static void ddr_cmd_result_print_dataeye(const struct ddr_training_data_st *ddrtr_data) ++{ ++ /* Write window of prebit-deskew */ ++ ddr_cmd_result_print_write_dataeye(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0); ++ ++ /* Read window of prebit-deskew */ ++ ddr_cmd_result_print_read_dataeye(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0); ++} ++ ++static void ddr_cmd_result_print_ca_data(const struct ddr_training_data_st *ddrtr_data, ++ unsigned int min, unsigned int max, unsigned int sum) ++{ ++ unsigned int i, j; ++ unsigned int left, right, mid, win; ++ ++ /* data */ ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ left = ddrtr_data->ca_addr[i] >> DDR_DATAEYE_RESULT_BIT; ++ right = ddrtr_data->ca_addr[i] & DDR_DATAEYE_RESULT_MASK; ++ mid = (left + right) >> 1; ++ win = right - left + 1; ++ ++ printf("%-4u", i); ++ for (j = 0; j < PHY_DQ_BDL_LEVEL; j++) { ++ if (j >= left && j <= right) ++ printf("%-1s", "-"); ++ else ++ printf("%-1s", "X"); ++ } ++ printf(" 0x%08x %-6u%-4u\n", ddrtr_data->ca_addr[i], mid, win); ++ ++ if (win < min) ++ min = win; ++ if (win > max) ++ max = win; ++ sum += win; ++ } ++ printf("--------------------------------------------------------\n"); ++ ++ printf("Sum WIN: %u. Avg WIN: %u\n", sum, sum / DDR_PHY_CA_MAX); ++ printf("Min WIN: %u. CA Index: ", min); ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ win = (ddrtr_data->ca_addr[i] & DDR_DATAEYE_RESULT_MASK) - ++ (ddrtr_data->ca_addr[i] >> DDR_DATAEYE_RESULT_BIT) + 1; ++ if (win == min) ++ printf("%u ", i); ++ } ++ printf("\nMax WIN: %u. CA Index: ", max); ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ win = (ddrtr_data->ca_addr[i] & DDR_DATAEYE_RESULT_MASK) - ++ (ddrtr_data->ca_addr[i] >> DDR_DATAEYE_RESULT_BIT) + 1; ++ if (win == max) ++ printf("%u ", i); ++ } ++} ++ ++static void ddr_cmd_result_print_ca(const struct ddr_training_data_st *ddrtr_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ if (ddrtr_data->ca_addr[i] != 0) ++ break; ++ ++ if (i == (DDR_PHY_CA_MAX - 1)) ++ return; /* no result to print */ ++ } ++ ++ printf("Command address window:\n"); ++ printf("--------------------------------------------------------\n"); ++ ++ /* title: CA 0 4...124 */ ++ printf("%-4s", "CA"); ++ for (i = 0; i < PHY_DQ_BDL_LEVEL; i++) { ++ if (i % 4 == 0) /* Print out the CA number which is a multiple of 4 */ ++ printf("%-4u", i); ++ } ++ printf(" %-10s %-6s%-4s\n", "RANGE", "BDL", "WIN"); ++ ++ /* data */ ++ ddr_cmd_result_print_ca_data(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0); ++ printf("\n\n"); ++} ++ ++static void ddr_cmd_result_print_by_rank(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int cmd, unsigned int phy_index, unsigned int rank_index) ++{ ++ unsigned int mask = 1 << phy_index; /* DDR_BYPASS_PHY0_MASK DDR_BYPASS_PHY1_MASK */ ++ const struct rank_data_st *rank_st = &ddrtr_result->phy_st[phy_index].rank_st[rank_index]; ++ ++ if (rank_st->item & mask) ++ return; ++ ++ printf("\r\n[PHY%u][RANK%u]:\r\n", phy_index, rank_index); ++ if (DDR_TRAINING_CMD_DATAEYE & cmd) ++ ddr_cmd_result_print_dataeye(&rank_st->ddrtr_data); ++ ++ if (DDR_TRAINING_CMD_LPCA & cmd) ++ ddr_cmd_result_print_ca(&rank_st->ddrtr_data); ++} ++ ++static void ddr_cmd_result_print_by_phy(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int cmd, unsigned int phy_index) ++{ ++ int i; ++ ++ if (phy_index >= DDR_PHY_NUM) { ++ printf("Array index phy_idx out of range"); ++ return; ++ } ++ if (ddrtr_result->phy_st[phy_index].rank_num > DDR_SUPPORT_RANK_MAX) { ++ printf("loop upper limit rank number out of range, rank_num = %x", ++ ddrtr_result->phy_st[phy_index].rank_num); ++ return; ++ } ++ ++ for (i = 0; i < ddrtr_result->phy_st[phy_index].rank_num; i++) ++ ddr_cmd_result_print_by_rank(ddrtr_result, cmd, phy_index, i); ++} ++ ++void ddr_cmd_result_display(const struct ddr_training_result_st *ddrtr_result, unsigned int cmd) ++{ ++ int i; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ if (ddrtr_result->phy_num > DDR_PHY_NUM) ++ return; ++ ++ for (i = 0; i < ddrtr_result->phy_num; i++) ++ ddr_cmd_result_print_by_phy(ddrtr_result, cmd, i); ++} ++ ++static void ddr_reg_result_display_by_rank(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int phy_index, unsigned int rank_index) ++{ ++ int i; ++ int num; ++ unsigned int base_phy; ++ unsigned int byte_num; ++ unsigned int rank_num; ++ struct ddr_reg_val_st *ddr_reg = NULL; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ base_phy = ddrtr_result->phy_st[phy_index].rank_st[rank_index].ddrtr_data.base_phy; ++ byte_num = ddrtr_result->phy_st[phy_index].rank_st[rank_index].ddrtr_data.byte_num; ++ rank_num = ddrtr_result->phy_st[phy_index].rank_num; ++ if (rank_index == 0) { ++ num = sizeof(training_reg_val_rank0) / sizeof(struct ddr_reg_val_st); ++ ddr_reg = &training_reg_val_rank0[0]; ++ } else { ++ num = sizeof(training_reg_val_rank1) / sizeof(struct ddr_reg_val_st); ++ ddr_reg = &training_reg_val_rank1[0]; ++ } ++ ++ printf("\r\n[PHY%u][RANK%u]:\r\n", phy_index, rank_index); ++ for (i = 0; i < num; i++) { ++ if (i != 0) ++ ddr_reg++; ++ if (ddr_reg->offset == 0) ++ continue; ++ if (ddr_reg->byte_index >= byte_num) ++ continue; ++ if (ddr_reg->rank_index >= rank_num) ++ continue; ++ ddr_reg->val = reg_read(base_phy + ddr_reg->offset); ++ ++ printf("[0x%08x = 0x%08x] %-32s", base_phy + ddr_reg->offset, ++ ddr_reg->val, ddr_reg->name); ++ ++ if ((i + 1) % 2 == 0) /* 2: Print information for 2 registers per line */ ++ printf("\r\n"); ++ } ++} ++ ++void ddr_reg_result_display_by_phy(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int phy_index) ++{ ++ unsigned int i; ++ unsigned int mask = 1 << phy_index; /* DDR_BYPASS_PHY0_MASK or DDR_BYPASS_PHY1_MASK */ ++ unsigned int item; ++ unsigned int enable = 0; ++ unsigned int rank_num; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ ++ rank_num = ddrtr_result->phy_st[phy_index].rank_num; ++ if (rank_num > DDR_SUPPORT_RANK_MAX) { ++ printf("loop upper limit rank number out of range, rank_num = %x", rank_num); ++ return; ++ } ++ /* check rank0 and rank1 training item */ ++ for (i = 0; i < rank_num; i++) { ++ item = ddrtr_result->phy_st[phy_index].rank_st[i].item; ++ if (!(item & mask)) ++ enable = 1; ++ } ++ ++ if (!enable) ++ return; ++ ++ for (i = 0; i < rank_num; i++) { ++ ddr_phy_switch_rank((unsigned int)ddrtr_result->phy_st[phy_index].rank_st[i].ddrtr_data.base_phy, i); ++ ddr_reg_result_display_by_rank(ddrtr_result, phy_index, i); ++ } ++} ++ ++/* Display DDR training register */ ++void ddr_reg_result_display(const struct ddr_training_result_st *ddrtr_result) ++{ ++ int i; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ if (ddrtr_result->phy_num > DDR_PHY_NUM) { ++ printf("loop upper limit phy number out of range, phy_num = %x", ++ ddrtr_result->phy_num); ++ return; ++ } ++ for (i = 0; i < ddrtr_result->phy_num; i++) ++ ddr_reg_result_display_by_phy(ddrtr_result, i); ++ ++ printf("\r\n"); ++} ++ ++static int ddr_cmd_is_disable(void) ++{ ++ unsigned int cfg; ++ unsigned int mask; ++ unsigned int i; ++ int disable = 1; ++ ++ cfg = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ for (i = 0; i < DDR_PHY_NUM; i++) { ++ mask = 1 << i; ++ if (!(cfg & mask)) ++ disable = 0; ++ } ++ ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ cfg = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC); ++ for (i = 0; i < DDR_PHY_NUM; i++) { ++ mask = 1 << i; ++ if (!(cfg & mask)) ++ disable = 0; ++ } ++#endif ++ ++ return disable; ++} ++ ++/* Get DDR training command function entry address */ ++void *ddr_cmd_get_entry(void) ++{ ++ char *src_ptr = 0; ++ char *dst_ptr; ++ unsigned int length; ++ ++ src_ptr = g_ddr_training_cmd_start; ++ dst_ptr = (char *)(DDR_TRAINING_RUN_STACK); ++ length = (uintptr_t)g_ddr_training_cmd_end - (uintptr_t)src_ptr; ++ ++ if ((src_ptr == NULL) || !length) { ++ printf("DDR training is unsupport.\n"); ++ return 0; ++ } ++ ++ printf("DDR training cmd entry[0x%08X] size[%u]byte cfg[0x%08X = 0x%08X]", ++ DDR_TRAINING_RUN_STACK, length, (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG)); ++ ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ printf("[0x%08X = 0x%08X]", (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC)); ++#endif ++ printf("\n"); ++ ++ if (ddr_cmd_is_disable() != 0) { ++ printf("Please config DDR training item. Bypass bit:\n" ++ "[0]PHY0 : 0x1\n" ++ "[1]PHY1 : 0x2\n" ++ "[2]PHY2 : 0x4\n" ++ "[4]Write Leveling : 0x10\n" ++ "[8]Gate : 0x100\n" ++ "[16]Dataeye : 0x10000\n" ++ "[18]Pcode : 0x40000\n" ++ "[20]HW : 0x100000\n" ++ "[21]MPR : 0x200000\n" ++ "[22]AC : 0x400000\n" ++ "[23]LPCA : 0x800000\n" ++ "[24]Host Vref : 0x1000000\n" ++ "[25]Dram Vref : 0x2000000\n" ++ "[26]DPMC : 0x4000000\n" ++ "[27]DCC : 0x8000000\n" ++ "[28]Dataeye Adjust : 0x10000000\n" ++ "[29]WL Write Adjust: 0x20000000\n" ++ "[30]HW Read Adjust : 0x40000000\n"); ++ return 0; ++ } ++ ++ ddr_cmd_prepare_copy(); ++ memcpy(dst_ptr, src_ptr, length); ++ return (void *)dst_ptr; ++} ++ ++/* Copy training codes from DDR to SRAM and do ddr training */ ++struct ddr_training_result_st *ddr_cmd_training_if(const struct ddr_cmd_st *cmd_st) ++{ ++ ddr_cmd_entry_func entry; ++ struct ddr_training_result_st *result_st = NULL; ++ ++ entry = (ddr_cmd_entry_func)ddr_cmd_get_entry(); ++ if (entry == NULL) ++ return 0; ++ ++#ifdef CONFIG_ARM64 ++ asm("isb"); ++ asm("dsb sy"); ++#else ++ asm("mcr p15, 0, r0, c7, c5, 0"); /* instruction cache invalidate all to PoU */ ++ asm("mcr p15, 0, r0, c7, c10, 4"); /* data synchronization barrier operation */ ++#endif ++ ++ /* save site before execute cmd */ ++ ddr_cmd_site_save(); ++ ++ /* entry: ddr_training_cmd_entry() */ ++ result_st = entry(cmd_st); ++ ++ /* restore site before execute cmd */ ++ ddr_cmd_site_restore(); ++ ++ if (result_st == NULL) { ++ printf("DDR training fail\n"); ++ return 0; ++ } ++ ++ return result_st; ++} +diff --git a/drivers/ddr/vendor/default/ddr_cmd_loc.S b/drivers/ddr/vendor/default/ddr_cmd_loc.S +new file mode 100644 +index 0000000..fb7b716 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_cmd_loc.S +@@ -0,0 +1,9 @@ ++/* DDR Training command codes location. Copy from DDR to SRAM to train DDR. */ ++ ++.section .image,#alloc ++ ++.globl g_ddr_training_cmd_start ++g_ddr_training_cmd_start: ++.incbin "drivers/ddr/vendor/default/cmd_bin/ddr_cmd.bin" ++.globl g_ddr_training_cmd_end ++g_ddr_training_cmd_end: +diff --git a/drivers/ddr/vendor/default/ddr_cmd_null.c b/drivers/ddr/vendor/default/ddr_cmd_null.c +new file mode 100644 +index 0000000..67d30a9 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_cmd_null.c +@@ -0,0 +1,18 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ +diff --git a/drivers/ddr/vendor/default/ddr_dcc_training.c b/drivers/ddr/vendor/default/ddr_dcc_training.c +new file mode 100644 +index 0000000..40fb1d9 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_dcc_training.c +@@ -0,0 +1,534 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++/* s40/t28/t16 not support dcc training */ ++#define __DCC_TRAINING__ ++#ifdef DDR_DCC_TRAINING_CONFIG ++/* Save two rank RDET result */ ++static void ddr_save_two_rank_bdl(const struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data) ++{ ++ unsigned int byte_idx; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank_idx = cfg->rank_idx; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ for (byte_idx = 0; byte_idx < byte_num; byte_idx++) { ++ dcc_data->rank[rank_idx].dq03[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank_idx, byte_idx)); ++ dcc_data->rank[rank_idx].dq47[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank_idx, byte_idx)); ++ dcc_data->rank[rank_idx].rdm[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqnbdl2(rank_idx, byte_idx)); ++ dcc_data->rank[rank_idx].rdqs[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqsdly(byte_idx)); ++ ++ ddr_debug("rank[%x] dq03[%x] dq47[%x] rdm[%x] rdqs[%x]", rank_idx, ++ dcc_data->rank[rank_idx].dq03[byte_idx], ++ dcc_data->rank[rank_idx].dq47[byte_idx], ++ dcc_data->rank[rank_idx].rdm[byte_idx], ++ dcc_data->rank[rank_idx].rdqs[byte_idx]); ++ } ++} ++ ++/* Restore two rank RDET result */ ++static void ddr_restore_two_rank_bdl(const struct ddr_cfg_st *cfg, const struct dcc_data_st *dcc_data) ++{ ++ unsigned int byte_idx; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank_idx = cfg->rank_idx; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ for (byte_idx = 0; byte_idx < byte_num; byte_idx++) { ++ reg_write(dcc_data->rank[rank_idx].dq03[byte_idx], base_phy + ddr_phy_dxnrdqnbdl0(rank_idx, byte_idx)); ++ reg_write(dcc_data->rank[rank_idx].dq47[byte_idx], base_phy + ddr_phy_dxnrdqnbdl1(rank_idx, byte_idx)); ++ reg_write(dcc_data->rank[rank_idx].rdm[byte_idx], base_phy + ddr_phy_dxnrdqnbdl2(rank_idx, byte_idx)); ++ reg_write(dcc_data->rank[rank_idx].rdqs[byte_idx], base_phy + ddr_phy_dxnrdqsdly(byte_idx)); ++ } ++} ++ ++/* DCC RDET training */ ++static int ddr_dcc_dataeye_read(const struct ddr_cfg_st *cfg) ++{ ++ /* 0:PHY_TRAINCTRL0_DTR_RANK0, 1:PHY_TRAINCTRL0_DTR_RANK1 */ ++ ddr_phy_switch_rank(cfg->cur_phy, cfg->rank_idx); ++ return ddr_hw_training_process(cfg, PHY_PHYINITCTRL_RDET_EN); ++} ++ ++/* Duty direction control */ ++static unsigned int ddr_dcc_ck_ctl(const struct ddr_cfg_st *cfg, unsigned int ioctl21_def, ++ unsigned int ctl_index) ++{ ++ unsigned int ioctl21; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ ioctl21 = (ioctl21_def & (~(1 << PHY_ACIOCTL21_CTL0_BIT)) & ++ (~(1 << PHY_ACIOCTL21_CTL1_BIT))) | ++ (ctl_index << PHY_ACIOCTL21_CTL0_BIT) | ++ (ctl_index << PHY_ACIOCTL21_CTL1_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } else { ++ ioctl21 = (ioctl21_def & (~(1 << PHY_ACIOCTL21_CTL0_BIT))) | ++ (ctl_index << PHY_ACIOCTL21_CTL0_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } ++ return ioctl21; ++} ++ ++/* Duty Correction. */ ++static unsigned int ddr_dcc_correct_duty(const struct ddr_cfg_st *cfg, unsigned int cur_duty, ++ unsigned int duty_def) ++{ ++ unsigned int ioctl21; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ /* Correct CK0 & CK1 duty */ ++ ioctl21 = (duty_def & (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK0_BIT)) & ++ (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK1_BIT))) | ++ (cur_duty << PHY_ACIOCTL21_CK0_BIT) | ++ (cur_duty << PHY_ACIOCTL21_CK1_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } else { ++ /* Correct CK0 duty */ ++ ioctl21 = (duty_def & (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK0_BIT))) | ++ (cur_duty << PHY_ACIOCTL21_CK0_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } ++ ++ return ioctl21; ++} ++ ++/* Duty Correction Control get win data */ ++static unsigned int ddr_dcc_get_win(const struct dcc_data_st *dcc_data, int ck_index, int val_index) ++{ ++ unsigned int win; ++ unsigned int rdqsbdl_right; ++ unsigned int rdqsbdl_left; ++ ++ rdqsbdl_right = (dcc_data->ck[ck_index].val[val_index] >> PHY_DXNRDBOUND_RIGHT_BIT) & ++ PHY_DXNRDBOUND_MASK; ++ rdqsbdl_left = (dcc_data->ck[ck_index].val[val_index] >> PHY_DXNRDBOUND_LEFT_BIT) & ++ PHY_DXNRDBOUND_MASK; ++ win = rdqsbdl_right - rdqsbdl_left; ++ ++ return win; ++} ++ ++/* Duty Correction Control get the min win of two byte */ ++static unsigned int ddr_dcc_get_min_win(const struct dcc_data_st *dcc_data, int ck_index) ++{ ++ int i; ++ unsigned int win_min; ++ unsigned int cur_win; ++ ++ win_min = ddr_dcc_get_win(dcc_data, ck_index, 0); ++ for (i = 0; i < DDR_CK_RESULT_MAX; i++) { ++ cur_win = ddr_dcc_get_win(dcc_data, ck_index, i); ++ ddr_debug("CK win[%x] = [%x]", i, cur_win); ++ if (cur_win < win_min) ++ win_min = cur_win; ++ } ++ ++ return win_min; ++} ++ ++/* Duty Correction Control get ck0 min win */ ++static unsigned int ddr_dcc_get_ck0_win(const struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data, ++ unsigned int ck0_win_min) ++{ ++ unsigned int byte_index; ++ unsigned int ck0_win; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return 0; ++ } ++ if ((byte_num / 2) > (DDR_PHY_BYTE_MAX / 2)) { /* byte_num/2:ck0 value include byte0 and byte1 */ ++ ddr_error("loop upper limit out of range"); ++ return 0; ++ } ++ for (byte_index = 0; byte_index < (byte_num / 2); byte_index++) /* byte_num/2:ck0 value include byte0 and byte1 */ ++ dcc_data->ck[0].val[byte_index] = reg_read(cfg->cur_phy + ddr_phy_dxnrdbound(byte_index)); ++ ++ ck0_win = ddr_dcc_get_min_win(dcc_data, 0); ++ if (ck0_win < ck0_win_min) ++ ck0_win_min = ck0_win; ++ ++ return ck0_win_min; ++} ++/* Duty Correction Control get ck1 min win. */ ++static unsigned int ddr_dcc_get_ck1_win(const struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data, ++ unsigned int ck1_win_min) ++{ ++ unsigned int byte_index; ++ unsigned int ck1_win; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return 0; ++ } ++ for (byte_index = 2; byte_index < byte_num; byte_index++) /* ck1 value include byte2 and byte3 */ ++ dcc_data->ck[1].val[byte_index - 2] = /* store value from byte_idex-2 */ ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdbound(byte_index)); /* one ck include two value */ ++ ++ ck1_win = ddr_dcc_get_min_win(dcc_data, 1); ++ if (ck1_win < ck1_win_min) ++ ck1_win_min = ck1_win; ++ ++ return ck1_win_min; ++} ++ ++/* dcc training data init */ ++static void dcc_data_init(struct dcc_data_st *dcc_data) ++{ ++ dcc_data->ck[0].win_min_ctl = 0xffffffff; ++ dcc_data->ck[0].win_max_ctl = 0x0; ++ dcc_data->ck[1].win_min_ctl = 0xffffffff; ++ dcc_data->ck[1].win_max_ctl = 0x0; ++ dcc_data->ck[0].idx_duty = 0; ++ dcc_data->ck[0].idx_duty_ctl = 0; ++ dcc_data->ck[0].idx_ctl = 0; ++ dcc_data->ck[1].idx_duty = 0; ++ dcc_data->ck[1].idx_duty_ctl = 0; ++ dcc_data->ck[1].idx_ctl = 0; ++ dcc_data->ck[0].bypass_ck_bit = PHY_BYPASS_CK0_BIT; ++ dcc_data->ck[0].acioctl21_ctl_bit = PHY_ACIOCTL21_CTL0_BIT; ++ dcc_data->ck[0].acioctl21_ck_bit = PHY_ACIOCTL21_CK0_BIT; ++ dcc_data->ck[1].bypass_ck_bit = PHY_BYPASS_CK1_BIT; ++ dcc_data->ck[1].acioctl21_ctl_bit = PHY_ACIOCTL21_CTL1_BIT; ++ dcc_data->ck[1].acioctl21_ck_bit = PHY_ACIOCTL21_CK1_BIT; ++} ++ ++/* dcc training get window by rank */ ++static int ddr_dcc_get_win_by_rank(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data) ++{ ++ unsigned int i; ++ int result = 0; ++ unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ for (i = 0; i < rank_num; i++) { ++ ddr_debug("cur_rank = [%x]", i); ++ cfg->rank_idx = i; ++ /* RDET */ ++ result += ddr_dcc_dataeye_read(cfg); ++ ++ /* Get win */ ++ dcc_data->ck[0].win = ddr_dcc_get_ck0_win(cfg, dcc_data, dcc_data->ck[0].win); ++ ddr_debug("ck0 win = [%x]", dcc_data->ck[0].win); ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ dcc_data->ck[1].win = ddr_dcc_get_ck1_win(cfg, dcc_data, dcc_data->ck[1].win); ++ ddr_debug("ck1 win = [%x]", dcc_data->ck[1].win); ++ } ++ ++ /* Restore two rank bdl */ ++ ddr_restore_two_rank_bdl(cfg, dcc_data); ++ } ++ ++ return result; ++} ++ ++/* ddr dcc training compare result */ ++static void ddr_dcc_compare_result(struct dcc_data_st *dcc_data, int ck_num, ++ unsigned int base_phy, unsigned int gated_bypass_def, unsigned int ioctl21_def) ++{ ++ int ck_idx; ++ ++ if (ck_num > DDR_CK_MAX_NUM) { ++ ddr_error("ck number out of range"); ++ return; ++ } ++ for (ck_idx = 0; ck_idx < ck_num; ck_idx++) { ++ /* Config ck0 duty */ ++ if (dcc_data->ck[ck_idx].win_max_ctl - dcc_data->ck[ck_idx].win_min_ctl <= DDR_DCC_CTL_WIN_DIFF) { ++ dcc_data->ck[ck_idx].def_bp = (gated_bypass_def >> dcc_data->ck[ck_idx].bypass_ck_bit) & 0x1; ++ dcc_data->ck[ck_idx].def_ctl = (ioctl21_def >> dcc_data->ck[ck_idx].acioctl21_ctl_bit) & 0x1; ++ dcc_data->ck[ck_idx].def_duty = (ioctl21_def >> dcc_data->ck[ck_idx].acioctl21_ck_bit) & PHY_ACIOCTL21_MASK; ++ ++ gated_bypass_def = (gated_bypass_def & (~(1 << dcc_data->ck[ck_idx].bypass_ck_bit))) | ++ (dcc_data->ck[ck_idx].def_bp << dcc_data->ck[ck_idx].bypass_ck_bit); ++ reg_write(gated_bypass_def, base_phy + DDR_PHY_AC_GATED_BYPASS); ++ ++ ioctl21_def = (ioctl21_def & (~(1 << dcc_data->ck[ck_idx].acioctl21_ctl_bit)) & ++ (~(PHY_ACIOCTL21_MASK << dcc_data->ck[ck_idx].acioctl21_ck_bit))) | ++ (dcc_data->ck[ck_idx].def_ctl << dcc_data->ck[ck_idx].acioctl21_ctl_bit) | ++ (dcc_data->ck[ck_idx].def_duty << dcc_data->ck[ck_idx].acioctl21_ck_bit); ++ reg_write(ioctl21_def, base_phy + DDR_PHY_ACIOCTL21); ++ ++ ddr_debug("ck[%x] Final AC_GATED_BYPASS[%x]", ck_idx, gated_bypass_def); ++ ddr_debug("ck[%x] Final ACIOCTL21[%x]", ck_idx, ioctl21_def); ++ } else { ++ ioctl21_def = (ioctl21_def & (~(1 << dcc_data->ck[ck_idx].acioctl21_ctl_bit)) & ++ (~(PHY_ACIOCTL21_MASK << dcc_data->ck[ck_idx].acioctl21_ck_bit))) | ++ (dcc_data->ck[ck_idx].idx_ctl << dcc_data->ck[ck_idx].acioctl21_ctl_bit) | ++ (dcc_data->ck[ck_idx].idx_duty_ctl << dcc_data->ck[ck_idx].acioctl21_ck_bit); ++ reg_write(ioctl21_def, base_phy + DDR_PHY_ACIOCTL21); ++ ++ ddr_debug("ck[%x] Final ACIOCTL21[%x]", ck_idx, ioctl21_def); ++ } ++ } ++} ++ ++static void ddr_dcc_get_duty(struct dcc_data_st *dcc_data, int ck_num, unsigned int cur_duty) ++{ ++ int ck_idx; ++ ++ if (ck_num > DDR_CK_MAX_NUM) { ++ ddr_error("ck number out of range"); ++ return; ++ } ++ for (ck_idx = 0; ck_idx < ck_num; ck_idx++) { ++ if (dcc_data->ck[ck_idx].win < dcc_data->ck[ck_idx].win_min_duty) ++ dcc_data->ck[ck_idx].win_min_duty = dcc_data->ck[ck_idx].win; ++ ++ if (dcc_data->ck[ck_idx].win > dcc_data->ck[ck_idx].win_max_duty) { ++ dcc_data->ck[ck_idx].win_max_duty = dcc_data->ck[ck_idx].win; ++ dcc_data->ck[ck_idx].idx_duty = cur_duty; ++ } ++ ddr_debug("ck[%x] duty_win_min[%x] duty_win_max[%x] duty_index[%x]", ck_idx, ++ dcc_data->ck[ck_idx].win_min_duty, ++ dcc_data->ck[ck_idx].win_max_duty, ++ dcc_data->ck[ck_idx].idx_duty); ++ } ++} ++ ++/* Get ck0/ck1 duty_win_min/duty_win_max/duty_index */ ++static void ddr_dcc_get_ctrl(struct dcc_data_st *dcc_data, int ck_num, unsigned int cur_ctl) ++{ ++ int ck_idx; ++ ++ if (ck_num > DDR_CK_MAX_NUM) { ++ ddr_error("ck number out of range"); ++ return; ++ } ++ for (ck_idx = 0; ck_idx < ck_num; ck_idx++) { ++ if (dcc_data->ck[ck_idx].win_min_duty < dcc_data->ck[ck_idx].win_min_ctl) ++ dcc_data->ck[ck_idx].win_min_ctl = dcc_data->ck[ck_idx].win_min_duty; ++ ++ if (dcc_data->ck[ck_idx].win_max_duty > dcc_data->ck[ck_idx].win_max_ctl) { ++ dcc_data->ck[ck_idx].win_max_ctl = dcc_data->ck[ck_idx].win_max_duty; ++ dcc_data->ck[ck_idx].idx_duty_ctl = dcc_data->ck[ck_idx].idx_duty; ++ dcc_data->ck[ck_idx].idx_ctl = cur_ctl; ++ } ++ ddr_debug("ck[%x] win_min_ctl[%x] win_max_ctl[%x] ctl_index0[%x] duty_ctl_idx0[%x]", ck_idx, ++ dcc_data->ck[ck_idx].win_min_ctl, ++ dcc_data->ck[ck_idx].win_max_ctl, ++ dcc_data->ck[ck_idx].idx_ctl, ++ dcc_data->ck[ck_idx].idx_duty_ctl); ++ } ++} ++ ++static int ddr_dcc_process(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data, ++ int ck_num, unsigned int ioctl21_def) ++{ ++ unsigned int cur_ctl; ++ unsigned int cur_duty; ++ int result; ++ ++ for (cur_ctl = 0; cur_ctl < DDR_DUTY_CTL_NUM; cur_ctl++) { ++ dcc_data->ck[0].win_min_duty = 0xffffffff; ++ dcc_data->ck[0].win_max_duty = 0x0; ++ if (ck_num > 1) { ++ dcc_data->ck[1].win_min_duty = 0xffffffff; ++ dcc_data->ck[1].win_max_duty = 0x0; ++ } ++ ddr_debug("cur_ctl = [%x]", cur_ctl); ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ /* Correct CK duty dirrection control */ ++ dcc_data->ioctl21_tmp = ddr_dcc_ck_ctl(cfg, ioctl21_def, cur_ctl); ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ for (cur_duty = 0; cur_duty < DDR_DUTY_NUM; cur_duty += PHY_AC_IOCTL21_STEP) { ++ dcc_data->ck[0].win = 0xffffffff; ++ if (ck_num > 1) ++ dcc_data->ck[1].win = 0xffffffff; ++ ++ ddr_debug("cur_duty = [%x]", cur_duty); ++ /* Correct ck0 and ck1 duty */ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ dcc_data->ioctl21_tmp = ddr_dcc_correct_duty(cfg, cur_duty, dcc_data->ioctl21_tmp); ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ ddr_debug("Cur ACIOCTL21[%x]", dcc_data->ioctl21_tmp); ++ result = ddr_dcc_get_win_by_rank(cfg, dcc_data); ++ ddr_dcc_get_duty(dcc_data, ck_num, cur_duty); ++ } ++ ++ ddr_dcc_get_ctrl(dcc_data, ck_num, cur_ctl); ++ } ++ ++ return result; ++} ++ ++static int ddr_dcc_get_best_duty(struct ddr_cfg_st *cfg, ++ struct dmc_cfg_sref_st *cfg_sref, struct dcc_data_st *dcc_data) ++{ ++ int ck_num; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int ioctl21_def; ++ unsigned int gated_bypass_def, gated_bypass_temp; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ck_num = DDR_CK_NUM_LPDDR4; /* lpddr4: 2 ck */ ++ else ++ ck_num = DDR_CK_NUM_NONLPDDR4; /* other: 1 ck */ ++ ++ dcc_data_init(dcc_data); ++ ++ /* Save ck duty default config. Read two times to get the right static register value. */ ++ gated_bypass_def = reg_read(base_phy + DDR_PHY_AC_GATED_BYPASS); ++ gated_bypass_def = reg_read(base_phy + DDR_PHY_AC_GATED_BYPASS); ++ ioctl21_def = reg_read(base_phy + DDR_PHY_ACIOCTL21); ++ ioctl21_def = reg_read(base_phy + DDR_PHY_ACIOCTL21); ++ ++ ddr_debug("gated_bypass_def[%x] ioctl21_def[%x]", gated_bypass_def, ioctl21_def); ++ ++ /* DCC training exit self-refresa enter powerdown. */ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); ++ ++ /* DDR dcc training enter auto self-refresh. */ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ /* Enable ck0 & ck1 duty. */ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ gated_bypass_temp = gated_bypass_def | PHY_CK1_IOCTL_DUTY_EN | PHY_CK_IOCTL_DUTY_EN; ++ reg_write(gated_bypass_temp, base_phy + DDR_PHY_AC_GATED_BYPASS); ++ } else { ++ gated_bypass_temp = gated_bypass_def | PHY_CK_IOCTL_DUTY_EN; ++ reg_write(gated_bypass_temp, base_phy + DDR_PHY_AC_GATED_BYPASS); ++ } ++ ddr_debug("Cur GATED_BYPASS[%x]", gated_bypass_temp); ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ if (ddr_dcc_process(cfg, dcc_data, ck_num, ioctl21_def)) ++ return -1; ++ ++ /* Config ck duty */ ++ /* DCC training exit self-refresa enter powerdown. */ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); ++ ++ /* DDR dcc training enter auto self-refresh. */ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ /* DDR dcc training compare result. */ ++ ddr_dcc_compare_result(dcc_data, ck_num, base_phy, gated_bypass_def, ioctl21_def); ++ ++ /* DDR dcc training exit auto self-refresh. */ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ return 0; ++} ++ ++static int ddr_dcc_training(struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ int result = 0; ++ unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ struct dmc_cfg_sref_st cfg_sref; ++ struct ddr_timing_st timing_st; ++ struct dcc_data_st dcc_st; ++ struct dcc_data_st *dcc_data = &dcc_st; ++ ++ memset(&cfg_sref, 0, sizeof(struct dmc_cfg_sref_st)); ++ memset(&timing_st, 0, sizeof(struct ddr_timing_st)); ++ memset(dcc_data, 0, sizeof(struct dcc_data_st)); ++ ++ ddr_debug("dram_type[%x]", cfg->phy[cfg->phy_idx].dram_type); ++ ddr_debug("rank num[%x]", rank_num); ++ ++ /* Save two rank DERT default result: rdq/rdqs/rdm/ bdl */ ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ ddr_save_two_rank_bdl(cfg, dcc_data); ++ } ++ ++ /* Disable auto refresh */ ++ ddr_training_save_timing(cfg, &timing_st); ++ ++ /* Duty Correction Control training. */ ++ result += ddr_dcc_get_best_duty(cfg, &cfg_sref, dcc_data); ++ ++ /* Do DERT training again */ ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ dcc_data->item[i] = cfg->phy[cfg->phy_idx].rank[i].item_hw; ++ cfg->phy[cfg->phy_idx].rank[i].item_hw = PHY_PHYINITCTRL_HVREFT_EN; ++ ddr_debug("item_hw[%x]=[%x]", i, cfg->phy[cfg->phy_idx].rank[i].item_hw); ++ } ++ result += ddr_hw_training_by_phy(cfg); ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ cfg->phy[cfg->phy_idx].rank[i].item_hw = dcc_data->item[i]; ++ } ++ ++ /* Enable auto refresh */ ++ ddr_training_restore_timing(cfg, &timing_st); ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ /* DCC restore DMC_CFG_SREF config. */ ++ ddr_sref_cfg_restore(cfg, &cfg_sref); ++ ++ return result; ++} ++ ++int ddr_dcc_training_func(struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ int result = 0; ++ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL"); ++ return -1; ++ } ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ cfg->cur_item = cfg->phy[i].rank[0].item; ++ ++ if (ddr_training_check_bypass(cfg, 1 << (cfg->phy_idx)) != DDR_FALSE) ++ continue; ++ /* dpmc training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DCC_MASK) == DDR_FALSE) ++ result += ddr_dcc_training(cfg); ++ } ++ return result; ++} ++ ++#else ++int ddr_dcc_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DCC training"); ++ return 0; ++} ++#endif /* DDR_DCC_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrc_v500.h b/drivers/ddr/vendor/default/ddr_ddrc_v500.h +new file mode 100644 +index 0000000..720378d +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrc_v500.h +@@ -0,0 +1,145 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRC_V500_H ++#define DDR_DDRC_V500_H ++ ++/******** DMC **************************/ ++/* base address: DDR_REG_BASE_DMC0 DDR_REG_BASE_DMC1 */ ++/* register offset address */ ++#define DDR_DMC_CTRL_SREF 0x0 /* DDRC self-refresh control. */ ++#define DDR_DMC_CFG_PD 0x28 /* PowerDown */ ++#define DDR_DMC_CFG_DDRMODE 0x50 ++#define ddr_dmc_cfg_rnkvol(n) (0x60 + ((n) << 2)) ++#define DDR_DMC_CFG_EMRS01 0x70 ++#define DDR_DMC_TIMING2 0x88 ++#define DDR_DMC_SFCREQ 0xc ++#define DDR_DMC_SFCCMD 0x210 ++#define DDR_DMC_SFCADDR 0x214 /* read col and row */ ++#define DDR_DMC_SFCBANK 0x218 ++#define DDR_DMC_CURR_FUNC 0x294 ++#ifndef DDR_DMC_SFC_RDATA0 ++#define DDR_DMC_SFC_RDATA0 0x4A8 /* SFC read data[127:96] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA1 ++#define DDR_DMC_SFC_RDATA1 0x4AC /* SFC read data[95:64] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA2 ++#define DDR_DMC_SFC_RDATA2 0x4B0 /* SFC read data[63:32] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA3 ++#define DDR_DMC_SFC_RDATA3 0x4B4 /* SFC read data[31:0] */ ++#endif ++ ++/* register mask */ ++#define DMC_CMD_MRS_MASK 0xffff ++/* storing data bus width. [00]8bit, [01]16bit, [10]32bit, [11]64bit */ ++#define DMC_MEM_WIDTH_MASK 0x3 ++#define DMC_MRS_MASK 0xffff /* [15:0] Mode Register mask */ ++#define DMC_MR0_BL_MASK 0x3 ++#define DMC_CFG_DRAM_TYPE_MASK 0x7 /* [2:0]101:DDR2, 110:DDR3, 111:DDR4 */ ++#define DMC_CFG_MEM_BG_MASK 0x3 /* [11:10]0:1, 1:2, 2:4 Bank Group */ ++#define DMC_CURR_FUNC_IN_SREF_MASK 0x1 ++#define DMC_RNKVOL_MEM_BANK_MASK 0x3 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_MASK 0x7 /* [6:4] */ ++#define DMC_RNKVOL_MEM_COL_MASK 0x7 /* [2:0] */ ++ ++/* register bit */ ++#define DMC_MEM_WIDTH_BIT 4 /* storing data bus width */ ++#define DMC_SFC_PRE_DIS_BIT 0 /* ddrcv500 not use */ ++/* [CUSTOM] [31:16]config MR when LMR command */ ++#define DMC_SFC_CMD_MRS_BIT 16 ++#define DMC_SFC_RANK_BIT 4 /* [CUSTOM] [7:4]cmd_rank */ ++#define DMC_CFG_MEM_BG_BIT 10 /* [11:10] mem_bankgroup */ ++#define DMC_RNKVOL_MEM_BANK_BIT 8 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_BIT 4 /* [6:4] */ ++ ++/* register value */ ++#define DMC_BANK_MR1 1 ++#define DMC_BANK_MR3 0x3 ++#define DMC_CMD_TYPE_LMR 0x2 ++#define DMC_CMD_TYPE_READ 0x5 /* read */ ++#define DMC_CMD_TYPE_PRECHARGE_ALL 0x6 /* precharge all */ ++#define DMC_CMD_MRS_MR3 0x4 /* MR3: 0x4 */ ++#define DMC_CMD_MRS_A7 0x80 ++/* value 1 means exexute command. cmd_rank[0] control DDR RANK0 */ ++#define DMC_CMD_RANK0 0x1 ++#define DMC_MR0_BL_BUST8 0x0 /* BC8 (fixed) */ ++#define DMC_MR0_BL_BUST4 0x2 /* BC4 (fixed) */ ++#define DMC_AUTO_TIMING_DIS 0xfffff000 /* auto refresh disable */ ++#define DMC_POWER_DOWN_DIS 0xfffffffe /* powerDown disable */ ++#define DMC_SCRAMB_DIS 0xffffffff /* v500 no scramb */ ++#define DMC_CFG_DRAM_TYPE_DDR4 0x7 /* DDR4 */ ++#define DMC_CTRL_SREF_ENTER 0x1 /* 1 Enter Auto-self refresh */ ++#define DMC_CTRL_SREF_EXIT 0x2 /* 2 Exit Auto-self refresh */ ++#define DMC_CFG_MEM_2BG 0x1 /* 2 Bank Group */ ++ ++#ifndef DDR_RANK_NUM ++#define DDR_RANK_NUM 1 /* rank number */ ++#endif ++ ++#define dmc_sfc_cmd_write(sfc_cmd, addr) \ ++ reg_write((sfc_cmd) | (DMC_CMD_RANK0 << DMC_SFC_RANK_BIT), addr) ++#define dmc_sfc_bank_write(sfc_bank, addr) reg_write(sfc_bank, addr) ++ ++#define dmc_mpr_check_bit_0_127(cfg) \ ++ ddr_mpr_extract(cfg, \ ++ DDR_DMC_SFC_RDATA0, DDR_DMC_SFC_RDATA1, \ ++ DDR_DMC_SFC_RDATA2, DDR_DMC_SFC_RDATA3) ++/* ddrcv500 not have [128, 255] */ ++#define dmc_mpr_check_bit_128_255(cfg) 0 ++ ++/* ddrcv500 0x50 not support scramb */ ++#define dmc_save_scramb(relate_reg, i, base_dmc) ++#define dmc_disable_scramb(relate_reg, i, base_dmc) ++#define dmc_restore_scramb(relate_reg, i, base_dmc) ++/******** AXI **************************/ ++/** ++ * DMC -- PHY ++ * / ++ * DDRT -- AXI ++ * \ ++ * DMC -- PHY ++ */ ++/* base address: DDR_REG_BASE_AXI */ ++/* register offset address */ ++#define DDR_AXI_REGION_ATTRIB0 0x104 /* region 0 */ ++#define DDR_AXI_REGION_ATTRIB1 0x114 /* region 1 */ ++ ++/* register mask */ ++#define AXI_REGION_ATTRIB_CH_MASK 0xfffffff0 /* channel mask */ ++ ++/* register value */ ++/* Map to the single channel, independent address */ ++#define AXI_RNG_ATTR_CH_MODE 0x4 ++#define AXI_RNG_ATTR_CH_START_0 0x0 ++#define AXI_RNG_ATTR_CH_START_1 0x1 ++ ++/********data define************************************/ ++struct ddr_ddrc_data { ++}; ++#define ddr_axi_save_func(relate_reg) ++#define ddr_axi_restore_func(relate_reg) ++#define ddr_axi_switch_func(cfg) ++ ++/* ddrc v500 not support two rank */ ++#define ddr_rnkvol_save_func(relate_reg, base_dmc) ++#define ddr_rnkvol_restore_func(relate_reg, base_dmc) ++#define ddr_rnkvol_set_func(cfg) ++#endif /* DDR_DDRC_V500_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrc_v510.h b/drivers/ddr/vendor/default/ddr_ddrc_v510.h +new file mode 100644 +index 0000000..9cea766 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrc_v510.h +@@ -0,0 +1,204 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRC_V510_H ++#define DDR_DDRC_V510_H ++ ++/******** DMC **************************/ ++/* base address: DDR_REG_BASE_DMC0 DDR_REG_BASE_DMC1 */ ++/* register offset address */ ++#define DDR_DMC_CTRL_SREF 0x0 /* DDRC self-refresh control. */ ++#define DDR_DMC_CFG_SREF 0x20 /* DDRC self-refresh config. */ ++#define DDR_DMC_CFG_PD 0x28 /* PowerDown */ ++#define DDR_DMC_CFG_DDRMODE 0x50 ++#define DDR_DMC_CFG_SCRAMB 0x58 /* DDR scramb config */ ++#define ddr_dmc_cfg_rnkvol(n) (0x60 + ((n) << 2)) ++#define DDR_DMC_CFG_EMRS01 0xf0 ++#define DDR_DMC_TIMING2 0x108 ++#define DDR_DMC_SFCREQ 0xc ++#define DDR_DMC_SFCCMD 0x210 ++#define DDR_DMC_SFCADDR 0x214 /* read col and row */ ++#define DDR_DMC_SFCBANK 0x218 ++#define DDR_DMC_CURR_FUNC 0x294 ++#ifndef DDR_DMC_SFC_RDATA0 ++#define DDR_DMC_SFC_RDATA0 0x4A8 /* SFC read data[31:0] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA1 ++#define DDR_DMC_SFC_RDATA1 0x4AC /* SFC read data[63:32] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA2 ++#define DDR_DMC_SFC_RDATA2 0x4B0 /* SFC read data[95:64] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA3 ++#define DDR_DMC_SFC_RDATA3 0x4B4 /* SFC read data[127:96] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA4 ++#define DDR_DMC_SFC_RDATA4 0x4B8 /* SFC read data[159:128] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA5 ++#define DDR_DMC_SFC_RDATA5 0x4BC /* SFC read data[191:160] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA6 ++#define DDR_DMC_SFC_RDATA6 0x4C0 /* SFC read data[223:192] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA7 ++#define DDR_DMC_SFC_RDATA7 0x4C4 /* SFC read data[255:224] */ ++#endif ++ ++/* register mask */ ++#define DMC_CMD_MRS_MASK 0xffff ++/* storing data bus width. [00]8bit, [01]16bit, [10]32bit, [11]64bit */ ++#define DMC_MEM_WIDTH_MASK 0x3 ++#define DMC_MRS_MASK 0xffff /* [15:0] Mode Register mask */ ++#define DMC_MR0_BL_MASK 0x3 ++#define DMC_CFG_DRAM_TYPE_MASK 0xf /* [3:0]101:DDR2, 110:DDR3, 111:DDR4 */ ++#define DMC_CFG_MEM_BG_MASK 0x3 /* [11:10]0:1, 1:2, 2:4 Bank Group */ ++#define DMC_CURR_FUNC_IN_SREF_MASK 0x1 ++#define DMC_RNKVOL_MEM_BANK_MASK 0x3 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_MASK 0x7 /* [6:4] */ ++#define DMC_RNKVOL_MEM_COL_MASK 0x7 /* [2:0] */ ++ ++/* register bit */ ++#define DMC_MEM_WIDTH_BIT 4 /* storing data bus width */ ++/* [CUSTOM] precharge disable/enable bit */ ++#define DMC_SFC_PRE_DIS_BIT 30 ++/* [CUSTOM] [29:12]config MR when LMR command */ ++#define DMC_SFC_CMD_MRS_BIT 12 ++#define DMC_SFC_RANK_BIT 16 /* [CUSTOM] [31:16]sfc_rank */ ++#define DMC_CFG_MEM_BG_BIT 10 /* [11:10] mem_bankgroup */ ++#define DMC_RNKVOL_MEM_BANK_BIT 8 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_BIT 4 /* [6:4] */ ++ ++/* register value */ ++#define DMC_BANK_MR1 1 ++#define DMC_BANK_MR3 0x3 ++#define DMC_CMD_TYPE_LMR 0x2 ++#define DMC_CMD_TYPE_READ 0x5 /* read */ ++#define DMC_CMD_TYPE_PRECHARGE_ALL 0x6 /* precharge all */ ++#define DMC_CMD_MRS_MR3 0x4 /* MR3: 0x4 */ ++#define DMC_CMD_MRS_A7 0x80 ++/* value 1 means exexute command. cmd_rank[0] control DDR RANK0 */ ++#define DMC_CMD_RANK0 0x1 ++#define DMC_MR0_BL_BUST8 0x0 /* BC8 (fixed) */ ++#define DMC_MR0_BL_BUST4 0x2 /* BC4 (fixed) */ ++#define DMC_AUTO_TIMING_DIS 0xfffff000 /* auto refresh disable */ ++#define DMC_POWER_DOWN_DIS 0xfffffffe /* powerDown disable */ ++#define DMC_SCRAMB_DIS 0xffffbfff /* [14] scramb disable */ ++/* [4] scramb_seed_type, [2:0] scramb_seed_sort */ ++#define DMC_SCRAMB_CFG 0xffffffe8 ++#define DMC_CFG_DRAM_TYPE_DDR4 0x7 /* DDR4 */ ++#define DMC_CFG_DRAM_TYPE_LPDDR4 0x8 /* LPDDR4 */ ++#define DMC_CFG_MEM_2BG 0x1 /* 2 Bank Group */ ++#define DMC_CTRL_SREF_ENTER 0x1 /* 1 Enter Auto-self refresh */ ++#define DMC_CTRL_SREF_EXIT 0x2 /* 2 Exit Auto-self refresh */ ++ ++#ifndef DDR_RANK_NUM ++#define DDR_RANK_NUM 1 /* rank number */ ++#endif ++ ++#define dmc_sfc_cmd_write(sfc_cmd, addr) \ ++ reg_write((sfc_cmd) | (1 << DMC_SFC_PRE_DIS_BIT), addr) ++#define dmc_sfc_bank_write(sfc_bank, addr) \ ++ reg_write((sfc_bank) | (DMC_CMD_RANK0 << DMC_SFC_RANK_BIT), addr) ++ ++#define dmc_mpr_check_bit_0_127(cfg) \ ++ ddr_mpr_extract(cfg, DDR_DMC_SFC_RDATA3, DDR_DMC_SFC_RDATA2, \ ++ DDR_DMC_SFC_RDATA1, DDR_DMC_SFC_RDATA0) ++#define dmc_mpr_check_bit_128_255(cfg) \ ++ ddr_mpr_extract(cfg, DDR_DMC_SFC_RDATA7, DDR_DMC_SFC_RDATA6, \ ++ DDR_DMC_SFC_RDATA5, DDR_DMC_SFC_RDATA4) ++ ++/* dmc scramb */ ++#define dmc_save_scramb(relate_reg, i, base_dmc) do { \ ++ (relate_reg)->dmc_scramb[i] = \ ++ reg_read((base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ (relate_reg)->dmc_scramb_cfg[i] = \ ++ reg_read((base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++#define dmc_disable_scramb(relate_reg, i, base_dmc) do { \ ++ reg_write((relate_reg)->dmc_scramb[i] & DMC_SCRAMB_DIS, \ ++ (base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ reg_write((relate_reg)->dmc_scramb_cfg[i] & DMC_SCRAMB_CFG, \ ++ (base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++#define dmc_restore_scramb(relate_reg, i, base_dmc) do { \ ++ reg_write((relate_reg)->dmc_scramb[i], \ ++ (base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ reg_write((relate_reg)->dmc_scramb_cfg[i], \ ++ (base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++/******** AXI **************************/ ++/** ++ * DMC -- PHY ++ * / ++ * DDRT -- AXI ++ * \ ++ * DMC -- PHY ++ */ ++/* base address: DDR_REG_BASE_AXI */ ++/* register offset address */ ++#define DDR_AXI_REGION_ATTRIB0 0x104 /* region 0 */ ++#define DDR_AXI_REGION_ATTRIB1 0x114 /* region 1 */ ++ ++/* register mask */ ++#define AXI_REGION_ATTRIB_CH_MASK 0xfffffff0 /* channel mask */ ++ ++/* register value */ ++/* Map to the single channel, independent address */ ++#define AXI_RNG_ATTR_CH_MODE 0x4 ++#define AXI_RNG_ATTR_CH_START_0 0x0 ++#define AXI_RNG_ATTR_CH_START_1 0x1 ++#define AXI_RNG_NUM 2 /* region number */ ++ ++/********data define************************************/ ++struct ddr_ddrc_data { ++ unsigned int region_attrib[AXI_RNG_NUM]; ++}; ++ ++#define ddr_axi_save_func(relate_reg) do { \ ++ (relate_reg)->ddrc.region_attrib[0] = \ ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ (relate_reg)->ddrc.region_attrib[1] = \ ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++} while (0) ++ ++#define ddr_axi_restore_func(relate_reg) do { \ ++ reg_write((relate_reg)->ddrc.region_attrib[0], \ ++ DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ reg_write((relate_reg)->ddrc.region_attrib[1], \ ++ DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++} while (0) ++ ++#define ddr_axi_switch_func(cfg) do { \ ++ unsigned int ch_start = ((cfg)->phy_idx == 0 ? \ ++ AXI_RNG_ATTR_CH_START_0 : AXI_RNG_ATTR_CH_START_1); \ ++ reg_write((reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0) & \ ++ AXI_REGION_ATTRIB_CH_MASK) | AXI_RNG_ATTR_CH_MODE | ch_start, DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ reg_write((reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1) & \ ++ AXI_REGION_ATTRIB_CH_MASK) | AXI_RNG_ATTR_CH_MODE | ch_start, DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++} while (0) ++ ++/* ddrc v510 not support two rank */ ++#define ddr_rnkvol_save_func(relate_reg, base_dmc) ++#define ddr_rnkvol_restore_func(relate_reg, base_dmc) ++#define ddr_rnkvol_set_func(cfg) ++#endif /* DDR_DDRC_V510_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrc_v520.h b/drivers/ddr/vendor/default/ddr_ddrc_v520.h +new file mode 100644 +index 0000000..b22875b +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrc_v520.h +@@ -0,0 +1,228 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRC_V520_H ++#define DDR_DDRC_V520_H ++ ++/******** DMC **************************/ ++/* base address: DDR_REG_BASE_DMC0 DDR_REG_BASE_DMC1 */ ++/* register offset address */ ++#define DDR_DMC_CTRL_SREF 0x0 /* DDRC self-refresh control. */ ++#define DDR_DMC_CFG_SREF 0x20 /* DDRC self-refresh config. */ ++#define DDR_DMC_CFG_PD 0x28 /* PowerDown */ ++#define DDR_DMC_CFG_DDRMODE 0x50 ++#define DDR_DMC_CFG_SCRAMB 0x58 /* DDR scramb config */ ++#define ddr_dmc_cfg_rnkvol(n) (0x60 + ((n) << 2)) ++#define DDR_DMC_CFG_EMRS01 0xf0 ++#define DDR_DMC_TIMING2 0x108 ++#define DDR_DMC_SFCREQ 0xc ++#define DDR_DMC_SFCCMD 0x210 ++#define DDR_DMC_SFCADDR 0x214 /* read col and row */ ++#define DDR_DMC_SFCBANK 0x218 ++#define DDR_DMC_CURR_FUNC 0x294 ++#ifndef DDR_DMC_SFC_RDATA0 ++#define DDR_DMC_SFC_RDATA0 0x4A8 /* SFC read data[31:0] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA1 ++#define DDR_DMC_SFC_RDATA1 0x4AC /* SFC read data[63:32] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA2 ++#define DDR_DMC_SFC_RDATA2 0x4B0 /* SFC read data[95:64] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA3 ++#define DDR_DMC_SFC_RDATA3 0x4B4 /* SFC read data[127:96] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA4 ++#define DDR_DMC_SFC_RDATA4 0x4B8 /* SFC read data[159:128] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA5 ++#define DDR_DMC_SFC_RDATA5 0x4BC /* SFC read data[191:160] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA6 ++#define DDR_DMC_SFC_RDATA6 0x4C0 /* SFC read data[223:192] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA7 ++#define DDR_DMC_SFC_RDATA7 0x4C4 /* SFC read data[255:224] */ ++#endif ++ ++/* register mask */ ++#define DMC_CMD_MRS_MASK 0xffff ++/* storing data bus width. [00]8bit, [01]16bit, [10]32bit, [11]64bit */ ++#define DMC_MEM_WIDTH_MASK 0x3 ++#define DMC_MRS_MASK 0xffff /* [15:0] Mode Register mask */ ++#define DMC_MR0_BL_MASK 0x3 ++#define DMC_CFG_DRAM_TYPE_MASK 0xf /* [3:0]101:DDR2, 110:DDR3, 111:DDR4 */ ++#define DMC_CFG_MEM_BG_MASK 0x3 /* [11:10]0:1, 1:2, 2:4 Bank Group */ ++#define DMC_CURR_FUNC_IN_SREF_MASK 0x1 ++#define DMC_RNKVOL_MEM_BANK_MASK 0x3 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_MASK 0x7 /* [6:4] */ ++#define DMC_RNKVOL_MEM_COL_MASK 0x7 /* [2:0] */ ++#define DMC_CFG_INIT_XSREF_PD_MASK 0xc /* [3:2] */ ++ ++/* register bit */ ++#define DMC_MEM_WIDTH_BIT 4 /* storing data bus width */ ++/* [CUSTOM] precharge disable/enable bit */ ++#define DMC_SFC_PRE_DIS_BIT 30 ++/* [CUSTOM] [29:12]config MR when LMR command */ ++#define DMC_SFC_CMD_MRS_BIT 12 ++#define DMC_SFC_RANK_BIT 16 /* [CUSTOM] [31:16]sfc_rank */ ++#define DMC_CFG_MEM_BG_BIT 10 /* [11:10] mem_bankgroup */ ++#define DMC_RNKVOL_MEM_BANK_BIT 8 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_BIT 4 /* [6:4] */ ++ ++/* register value */ ++#define DMC_BANK_MR1 1 ++#define DMC_BANK_MR3 0x3 ++#define DMC_CMD_TYPE_LMR 0x2 ++#define DMC_CMD_TYPE_READ 0x5 /* read */ ++#define DMC_CMD_TYPE_PRECHARGE_ALL 0x6 /* precharge all */ ++#define DMC_CMD_MRS_MR3 0x4 /* MR3: 0x4 */ ++#define DMC_CMD_MRS_A7 0x80 ++/* value 1 means exexute command. cmd_rank[0] control DDR RANK0 */ ++#define DMC_CMD_RANK0 0x1 ++#define DMC_MR0_BL_BUST8 0x0 /* BC8 (fixed) */ ++#define DMC_MR0_BL_BUST4 0x2 /* BC4 (fixed) */ ++#define DMC_AUTO_TIMING_DIS 0xfffff000 /* auto refresh disable */ ++#define DMC_POWER_DOWN_DIS 0xfffffffe /* powerDown disable */ ++#define DMC_SCRAMB_DIS 0xffffbfff /* [14] scramb disable */ ++/* [4] scramb_seed_type, [2:0] scramb_seed_sort */ ++#define DMC_SCRAMB_CFG 0xffffffe8 ++#define DMC_CFG_DRAM_TYPE_DDR4 0x7 /* DDR4 */ ++#define DMC_CFG_DRAM_TYPE_LPDDR4 0x8 /* LPDDR4 */ ++#define DMC_CFG_MEM_2BG 0x1 /* 2 Bank Group */ ++#define DMC_CFG_INIT_XSREF_PD 0xc /* LPDDR4:Exit Auto-self refresh enter powerdown */ ++#define DMC_CTRL_SREF_ENTER 0x1 /* 1 Enter Auto-self refresh */ ++#define DMC_CTRL_SREF_EXIT 0x2 /* 2 Exit Auto-self refresh */ ++#define DMC_RNKVOL_MEM_ROW_11 0x0 /* 000: 11 bit */ ++#define DMC_CFG_INIT_XSREF 0x8 /* bit[3] */ ++#define DMC_CFG_SREF_PD 0x4 /* bit[2] */ ++ ++#ifndef DDR_AXI_SWITCH_NUM ++#define DDR_AXI_SWITCH_NUM 4 /* ddr training axi switch number */ ++#endif ++ ++#ifndef DDR_RANK_NUM ++#define DDR_RANK_NUM 2 /* rank number */ ++#endif ++ ++#define dmc_sfc_cmd_write(sfc_cmd, addr) \ ++ reg_write((sfc_cmd) | (1 << DMC_SFC_PRE_DIS_BIT), addr) ++#define dmc_sfc_bank_write(sfc_bank, addr) \ ++ reg_write((sfc_bank) | (DMC_CMD_RANK0 << DMC_SFC_RANK_BIT), addr) ++ ++#define dmc_mpr_check_bit_0_127(cfg) \ ++ ddr_mpr_extract(cfg, DDR_DMC_SFC_RDATA3, DDR_DMC_SFC_RDATA2, \ ++ DDR_DMC_SFC_RDATA1, DDR_DMC_SFC_RDATA0) ++#define dmc_mpr_check_bit_128_255(cfg) \ ++ ddr_mpr_extract(cfg, DDR_DMC_SFC_RDATA7, DDR_DMC_SFC_RDATA6, \ ++ DDR_DMC_SFC_RDATA5, DDR_DMC_SFC_RDATA4) ++ ++/* dmc scramb */ ++#define dmc_save_scramb(relate_reg, i, base_dmc) do { \ ++ (relate_reg)->dmc_scramb[i] = \ ++ reg_read((base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ (relate_reg)->dmc_scramb_cfg[i] = \ ++ reg_read((base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++#define dmc_disable_scramb(relate_reg, i, base_dmc) do { \ ++ reg_write((relate_reg)->dmc_scramb[i] & DMC_SCRAMB_DIS, \ ++ (base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ reg_write((relate_reg)->dmc_scramb_cfg[i] & DMC_SCRAMB_CFG, \ ++ (base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++#define dmc_restore_scramb(relate_reg, i, base_dmc) do { \ ++ reg_write((relate_reg)->dmc_scramb[i], \ ++ (base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ reg_write((relate_reg)->dmc_scramb_cfg[i], \ ++ (base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++/******** AXI **************************/ ++/** ++ * DMC -- PHY ++ * / ++ * DDRT -- AXI ++ * \ ++ * DMC -- PHY ++ */ ++/* base address: DDR_REG_BASE_AXI */ ++/* register offset address */ ++#define DDR_AXI_REGION_ATTRIB0 0x104 /* region 0 */ ++#define DDR_AXI_REGION_ATTRIB1 0x114 /* region 1 */ ++ ++/* register mask */ ++#define AXI_REGION_ATTRIB_CH_MASK 0xfffffff0 /* channel mask */ ++ ++/* register value */ ++/* Map to the single channel, independent address */ ++#define AXI_RNG_ATTR_CH_MODE 0x4 ++#define AXI_RNG_ATTR_CH_START_0 0x0 ++#define AXI_RNG_ATTR_CH_START_1 0x1 ++#define AXI_RNG_ATTR_CH_START_2 0x2 ++#define AXI_RNG_ATTR_CH_START_3 0x3 ++#define AXI_RNG_NUM 2 /* region number */ ++ ++/********data define************************************/ ++struct ddr_ddrc_data { ++ unsigned int region_attrib[AXI_RNG_NUM]; ++ unsigned int rnkvol; ++}; ++ ++#define ddr_axi_save_func(relate_reg) do { \ ++ (relate_reg)->ddrc.region_attrib[0] = \ ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ (relate_reg)->ddrc.region_attrib[1] = \ ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++} while (0) ++ ++#define ddr_axi_restore_func(relate_reg) do { \ ++ reg_write((relate_reg)->ddrc.region_attrib[0], \ ++ DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ reg_write((relate_reg)->ddrc.region_attrib[1], \ ++ DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++} while (0) ++ ++#define ddr_axi_switch_func(cfg) do { \ ++ unsigned int ch_start = (cfg)->phy_idx; \ ++ if ((cfg)->phy[(cfg)->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) \ ++ ch_start = ((cfg)->phy_idx << 1) + (cfg)->dmc_idx; \ ++ reg_write((reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0) & \ ++ AXI_REGION_ATTRIB_CH_MASK) | AXI_RNG_ATTR_CH_MODE | ch_start, DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ reg_write((reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1) & \ ++ AXI_REGION_ATTRIB_CH_MASK) | AXI_RNG_ATTR_CH_MODE | ch_start, DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++} while (0) ++ ++/* save rank0 for ddrt address */ ++#define ddr_rnkvol_save_func(relate_reg, base_dmc) \ ++ (relate_reg)->ddrc.rnkvol = reg_read((base_dmc) + ddr_dmc_cfg_rnkvol(0)); ++ ++#define ddr_rnkvol_restore_func(relate_reg, base_dmc) \ ++ reg_write((relate_reg)->ddrc.rnkvol, (base_dmc) + ddr_dmc_cfg_rnkvol(0)); ++ ++/* set mem_row to 0 */ ++#define ddr_rnkvol_set_func(cfg) do { \ ++ if ((cfg)->rank_idx == 1) { \ ++ reg_write(reg_read((cfg)->cur_dmc + ddr_dmc_cfg_rnkvol(0)) & \ ++ (~DMC_RNKVOL_MEM_ROW_MASK), \ ++ (cfg)->cur_dmc + ddr_dmc_cfg_rnkvol(0)); \ ++ } \ ++} while (0) ++#endif /* DDR_DDRC_V520_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrt_s40.h b/drivers/ddr/vendor/default/ddr_ddrt_s40.h +new file mode 100644 +index 0000000..9ebf97a +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrt_s40.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_S40_H ++#define DDR_DDRT_S40_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0xf /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) (addr) ++#endif /* DDR_DDRT_S40_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrt_t12_v100.h b/drivers/ddr/vendor/default/ddr_ddrt_t12_v100.h +new file mode 100644 +index 0000000..5caedc4 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrt_t12_v100.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_T12_V100_H ++#define DDR_DDRT_T12_V100_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0x7f /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) ((addr) >> 2) ++#endif /* DDR_DDRT_T12_V100_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrt_t16.h b/drivers/ddr/vendor/default/ddr_ddrt_t16.h +new file mode 100644 +index 0000000..9426836 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrt_t16.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_T16_H ++#define DDR_DDRT_T16_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0xf /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) ((addr) >> 2) ++#endif /* DDR_DDRT_T16_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrt_t28.h b/drivers/ddr/vendor/default/ddr_ddrt_t28.h +new file mode 100644 +index 0000000..ce4099f +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrt_t28.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_T28_H ++#define DDR_DDRT_T28_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0xf /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) (addr) ++#endif /* DDR_DDRT_T28_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf0.h b/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf0.h +new file mode 100644 +index 0000000..8e030c5 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf0.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_V2_0_SHF0_H ++#define DDR_DDRT_V2_0_SHF0_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0x7f /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) (addr) ++#endif /* DDR_DDRT_V2_0_SHF0_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf1.h b/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf1.h +new file mode 100644 +index 0000000..35157a0 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf1.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_V2_0_SHF1_H ++#define DDR_DDRT_V2_0_SHF1_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0x7f /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) ((addr) >> 1) ++#endif /* DDR_DDRT_V2_0_SHF1_H */ +diff --git a/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf2.h b/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf2.h +new file mode 100644 +index 0000000..17c8a81 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_ddrt_v2_0_shf2.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_V2_0_SHF2_H ++#define DDR_DDRT_V2_0_SHF2_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0x7f /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) ((addr) >> 2) ++#endif /* DDR_DDRT_V2_0_SHF2_H */ +diff --git a/drivers/ddr/vendor/default/ddr_interface.h b/drivers/ddr/vendor/default/ddr_interface.h +new file mode 100644 +index 0000000..16e86c1 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_interface.h +@@ -0,0 +1,166 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_INTERFACE_H ++#define DDR_INTERFACE_H ++ ++#include ++ ++extern char g_ddr_training_cmd_start[]; /* DDR training code start address */ ++extern char g_ddr_training_cmd_end[]; /* DDR training code end address */ ++ ++#define reg_read(addr) (*(volatile unsigned int *)((uintptr_t)(addr))) ++#define reg_write(val, addr) ((*(volatile unsigned int *)((uintptr_t)(addr))) = (val)) ++ ++#define DDR_PHY_BYTE_MAX 4 ++#define DDR_PHY_BIT_NUM 8 ++/* support max bit 32 */ ++#define DDR_PHY_BIT_MAX (DDR_PHY_BYTE_MAX * DDR_PHY_BIT_NUM) ++ ++#define DDR_REG_NAME_MAX 32 /* register name */ ++#define DDR_CA_ADDR_MAX 10 ++ ++#define DDR_SUPPORT_PHY_MAX 2 /* support max phy number */ ++#define DDR_SUPPORT_RANK_MAX 2 /* support max rank number */ ++#define DDR_SUPPORT_DMC_MAX 4 /* support max dmc number */ ++#define DDR_CK_RESULT_MAX 2 /* DCC CK result number */ ++ ++/* ++ * DDR training register number: ++ * WDQS 4 ++ * WDQ Phase 4 ++ * WDQ BDL 8 ++ * WDM 4 ++ * Write DQ/DQS OE 4 ++ * RDQS 4 ++ * RDQ BDL 8 ++ * Gate 4 ++ * CS 1 ++ * CLK 1 ++ * Host Vref 4 ++ * DRAM Vref 4 ++ * CA Phase 1 ++ * CA BDL 5 ++ * ------------------- ++ * 60 ++ */ ++#define DDR_TRAINING_REG_NUM 60 ++/* register max. */ ++#define DDR_TRAINING_REG_MAX (DDR_TRAINING_REG_NUM * DDR_SUPPORT_PHY_MAX) ++ ++#define DDR_TRAINING_CMD_SW (1 << 0) ++#define DDR_TRAINING_CMD_HW (1 << 1) ++#define DDR_TRAINING_CMD_MPR (1 << 2) ++#define DDR_TRAINING_CMD_WL (1 << 3) ++#define DDR_TRAINING_CMD_GATE (1 << 4) ++#define DDR_TRAINING_CMD_DATAEYE (1 << 5) ++#define DDR_TRAINING_CMD_VREF (1 << 6) ++#define DDR_TRAINING_CMD_AC (1 << 7) ++#define DDR_TRAINING_CMD_LPCA (1 << 8) ++#define DDR_TRAINING_CMD_SW_NO_WL (1 << 9) ++#define DDR_TRAINING_CMD_CONSOLE (1 << 10) ++#define DDR_TRAINING_CMD_DCC (1 << 11) ++#define DDR_TRAINING_CMD_PCODE (1 << 12) ++#define DDR_TRAINING_CMD_DPMC (1 << 13) ++ ++/*******log level ********************/ ++#define DDR_LOG_INFO_STR "info" ++#define DDR_LOG_DEBUG_STR "debug" ++#define DDR_LOG_WARNING_STR "warning" ++#define DDR_LOG_ERROR_STR "error" ++#define DDR_LOG_FATAL_STR "fatal" ++ ++#define DDR_LOG_INFO (1 << 0) ++#define DDR_LOG_DEBUG (1 << 1) ++#define DDR_LOG_WARNING (1 << 2) ++#define DDR_LOG_ERROR (1 << 3) ++#define DDR_LOG_FATAL (1 << 4) ++ ++#define DDR_TRAINING_BOOT_RESULT_ADDR (TEXT_BASE + 0x1000000) /* boot + 16M */ ++ ++#define DDR_TRAINING_VER "V2.2.0 20200826" ++#define DDR_VERSION 0x220 ++struct training_data { ++ unsigned int ddr_bit_result[DDR_PHY_BIT_MAX]; ++ unsigned int ddr_bit_best[DDR_PHY_BIT_MAX]; ++ unsigned int ddr_win_sum; ++}; ++ ++struct ddr_training_data_st { ++ unsigned int base_phy; ++ unsigned int byte_num; ++ unsigned int rank_idx; ++ struct training_data read; ++ struct training_data write; ++ unsigned int ca_addr[DDR_CA_ADDR_MAX]; ++}; ++ ++struct rank_data_st { ++ unsigned int item; ++ struct ddr_training_data_st ddrtr_data; ++}; ++ ++struct phy_data_st { ++ unsigned int rank_num; ++ struct rank_data_st rank_st[DDR_SUPPORT_RANK_MAX]; ++}; ++ ++struct ddr_training_result_st { ++ unsigned int phy_num; ++ struct phy_data_st phy_st[DDR_SUPPORT_PHY_MAX]; ++}; ++ ++struct ddr_reg_val_st { ++ unsigned int rank_index; ++ unsigned int byte_index; ++ unsigned int offset; ++ unsigned int val; ++ char name[DDR_REG_NAME_MAX]; ++}; ++ ++struct ddr_cmd_st { ++ unsigned int cmd; ++ unsigned int level; ++ unsigned int start; ++ unsigned int length; ++}; ++ ++typedef struct ddr_training_result_st *(*ddr_cmd_entry_func) ++ (const struct ddr_cmd_st *cmd_st); ++ ++/* DDR training interface before boot */ ++int ddr_pcode_training_if(void); ++int ddr_sw_training_if(void); ++int ddr_hw_training_if(void); ++int ddr_training_console_if(void); ++void ddr_dmc_auto_power_down_cfg(void); ++ ++/* DDR training check interface when boot */ ++struct ddr_training_result_st *ddr_cmd_training_if(const struct ddr_cmd_st *cmd_st); ++int check_ddr_training(void); ++ ++/* DDR training command interface after boot */ ++void ddr_reg_result_display(const struct ddr_training_result_st *ddrtr_result); ++void ddr_cmd_result_display(const struct ddr_training_result_st *ddrtr_result, unsigned int cmd); ++void *ddr_cmd_get_entry(void); ++void ddr_cmd_prepare_copy(void); ++void ddr_cmd_site_save(void); ++void ddr_cmd_site_restore(void); ++#endif /* DDR_INTERFACE_H */ ++ +diff --git a/drivers/ddr/vendor/default/ddr_lpca_training.c b/drivers/ddr/vendor/default/ddr_lpca_training.c +new file mode 100644 +index 0000000..1db475a +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_lpca_training.c +@@ -0,0 +1,486 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __LPCA_TRAINING__ ++#ifdef DDR_LPCA_TRAINING_CONFIG ++/* Reset address bdl training data */ ++static void ddr_lpca_reset(struct ca_data_st *data) ++{ ++ unsigned int index; ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) { ++ data->left[index] = -1; ++ data->right[index] = -1; ++ } ++ ++ data->min = PHY_ACADDR_BDL_MASK; ++ data->max = 0; ++ data->done = 0; ++} ++ ++/* Get ca bit relation */ ++static void ddr_lpca_get_bit(struct ca_data_st *data) ++{ ++ unsigned int index; ++ ++ /* get ca bit in four register */ ++#ifdef DDR_LPCA_GET_BIT ++ unsigned int swap_sel; ++ for (index = 0; index < (DDR_PHY_CA_REG_MAX - 1); index++) { ++ reg_write(index + 1, data->base_phy + DDR_PHY_CATSWAPINDEX); ++ swap_sel = reg_read(data->base_phy + DDR_PHY_CATSWAPSEL); ++ ++ data->bits[index * 2].bit_p = /* ca 0/2/4/6 Rising edge */ ++ swap_sel & PHY_CATSWAPSEL_BIT_MASK; ++ data->bits[index * 2].bit_n = /* ca 0/2/4/6 Falling edge */ ++ (swap_sel >> 8) & PHY_CATSWAPSEL_BIT_MASK; /* bit8 */ ++ data->bits[index * 2 + 1].bit_p = /* ca * 2 + 1: bit 1/3/5/7 Rising edge */ ++ (swap_sel >> 16) & PHY_CATSWAPSEL_BIT_MASK; /* bit16 */ ++ data->bits[index * 2 + 1].bit_n = /* ca * 2 + 1: bit 1/3/5/7 Falling edge */ ++ (swap_sel >> 24) & PHY_CATSWAPSEL_BIT_MASK; /* bit24 */ ++ } ++#else ++ for (index = 0; index < (DDR_PHY_CA_REG_MAX - 1); index++) { ++ data->bits[index * 2].bit_p = index * 4 + 0; /* ca 0/2/4/6 Rising edge, dq*4+0:0/4/8/12 */ ++ data->bits[index * 2].bit_n = index * 4 + 1; /* ca 0/2/4/6 Falling edge, dq*4+1:1/5/9/13 */ ++ data->bits[index * 2 + 1].bit_p = index * 4 + 2; /* ca*2+1: bit 1/3/5/7 Rising edge, dq*4+2:2/6/10/14 */ ++ data->bits[index * 2 + 1].bit_n = index * 4 + 3; /* ca*2+1: bit 1/3/5/7 Falling edge, dq*4+3:2/6/10/14 */ ++ } ++#endif ++ ++ /* ++ * set ca bit for ca4 and ca9 ++ * ca4 equal ca0 ca9 equal ca5 ++ */ ++ for (index = 8; index > 4; index--) { /* set ca bit for ca5 to ca8 equal ca4 to ca7 */ ++ data->bits[index].bit_p = data->bits[index - 1].bit_p; ++ data->bits[index].bit_n = data->bits[index - 1].bit_n; ++ } ++ ++ data->bits[4].bit_p = data->bits[0].bit_p; /* ca4 equal ca0 */ ++ data->bits[4].bit_n = data->bits[0].bit_n; /* ca4 equal ca0 */ ++ data->bits[9].bit_p = data->bits[5].bit_p; /* ca9 equal ca5 */ ++ data->bits[9].bit_n = data->bits[5].bit_n; /* ca9 equal ca5 */ ++ ++#if defined(DDR_TRAINING_CMD) ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) { ++ ddr_info("CA[%x] bit_p[%x]", index, data->bits[index].bit_p); ++ ddr_info("CA[%x] bit_n[%x]", index, data->bits[index].bit_n); ++ } ++#endif ++} ++ ++/* Get address bdl default value */ ++static void ddr_lpca_get_def(struct ca_data_st *data) ++{ ++ unsigned int index; ++ ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) ++ data->def[index] = reg_read(data->base_phy + ddr_phy_acaddrbdl(index)); ++} ++ ++/* Restore address bdl default value */ ++static void ddr_lpca_restore_def(struct ca_data_st *data) ++{ ++ unsigned int index; ++ ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) ++ reg_write(data->def[index], data->base_phy + ddr_phy_acaddrbdl(index)); ++ ++ ddr_phy_cfg_update(data->base_phy); ++} ++ ++/* Set address bdl value */ ++static void ddr_lpca_set_bdl(unsigned int base_phy, unsigned int bdl) ++{ ++ unsigned int index; ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) ++ reg_write(bdl | (bdl << PHY_ACADDRBDL_ADDR1_BIT), ++ base_phy + ddr_phy_acaddrbdl(index)); ++ ++ ddr_phy_cfg_update(base_phy); ++} ++ ++/* Update address bdl value with training result */ ++static void ddr_lpca_update_bdl(struct ca_data_st *data) ++{ ++ unsigned int index; ++ unsigned int addr0, addr1; ++ ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) { ++ addr0 = (data->left[index * 2] + data->right[index * 2]) >> 1; /* 2:ca middle value */ ++ addr1 = (data->left[index * 2 + 1] + data->right[index * 2 + 1]) >> 1; /* 2:ca middle value */ ++ reg_write(addr0 | (addr1 << PHY_ACADDRBDL_ADDR1_BIT), ++ data->base_phy + ddr_phy_acaddrbdl(index)); ++ } ++ ++ ddr_phy_cfg_update(data->base_phy); ++} ++ ++/* Init data before training */ ++static void ddr_lpca_init(unsigned int base_dmc, unsigned int base_phy, struct ca_data_st *data) ++{ ++ data->base_dmc = base_dmc; ++ data->base_phy = base_phy; ++ ++ /* gat ca bit relation */ ++ ddr_lpca_get_bit(data); ++ ++ /* get ac addr bdl default value */ ++ ddr_lpca_get_def(data); ++ ++ /* reset training data */ ++ ddr_lpca_reset(data); ++} ++ ++/* Display training result */ ++static void ddr_lpca_display(struct ca_data_st *data) ++{ ++#if defined(DDR_TRAINING_CMD) ++ unsigned int index; ++ ++ ddr_debug("CA phase[%x = %x]", ++ data->base_phy + DDR_PHY_ADDRPHBOUND, ++ reg_read(data->base_phy + DDR_PHY_ADDRPHBOUND)); ++ ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) ++ ddr_debug("CA[%x] left[%x] right[%x]", ++ index, data->left[index], data->right[index]); ++ ++ ddr_debug("min[%x] max[%x] done[%x]", data->min, data->max, data->done); ++#endif ++} ++ ++/* Wait lpca command done */ ++static void ddr_lpca_wait(volatile union u_phy_catconfig *ca) ++{ ++ unsigned int count = 0; ++ while (count < DDR_LPCA_WAIT_TIMEOUT) { ++ if (ca->bits.sw_cat_dqvalid == 1) { ++ ca->bits.sw_cat_dqvalid = 0; /* clear */ ++ break; ++ } ++ count++; ++ } ++ ++ /* generally, count is 0 */ ++ if (count >= DDR_LPCA_WAIT_TIMEOUT) ++ ddr_error("LPCA wait timeout"); ++} ++ ++/* Compare dq result and pattern */ ++static int ddr_lpca_compare(struct ca_bit_st *ca_bit, ++ unsigned int dq_result, unsigned int pattern_p, ++ unsigned int pattern_n, unsigned int index) ++{ ++ if (((dq_result >> ca_bit->bit_p) & 0x1) != ((pattern_p >> index) & 0x1)) ++ return -1; ++ ++ if (((dq_result >> ca_bit->bit_n) & 0x1) != ((pattern_n >> index) & 0x1)) ++ return -1; ++ ++ return 0; ++} ++ ++static void ddr_lpca_get_data(struct ca_data_st *data, unsigned int index, unsigned int bdl) ++{ ++ /* pass */ ++ if (data->left[index] == -1) { ++ data->left[index] = bdl; ++ /* set min left bound */ ++ if (bdl < data->min) ++ data->min = bdl; ++ } ++ ++ /* unstable border value or abnormal value */ ++ if ((data->right[index] != -1) && ((bdl - data->right[index]) > 1)) ++ ddr_warning("CA[%x] bdl[%x] right[%x] ph[%x]", ++ index, bdl, data->right[index], ++ reg_read(data->base_phy + DDR_PHY_ADDRPHBOUND)); ++ ++ data->right[index] = bdl; ++ data->done |= (0x1 << index); ++ ++ /* set max right bound */ ++ if (data->right[index] > data->max) ++ data->max = data->right[index]; ++} ++ ++/* Check each CA whether pass */ ++static void ddr_lpca_check(struct ca_data_st *data, unsigned int bdl, unsigned int is_ca49) ++{ ++ unsigned int dq_result = reg_read(data->base_phy + DDR_PHY_PHYDQRESULT); ++ unsigned int pattern_p = reg_read(data->base_phy + ++ DDR_PHY_SWCATPATTERN_P) & PHY_CAT_PATTERN_MASK; ++ unsigned int pattern_n = reg_read(data->base_phy + ++ DDR_PHY_SWCATPATTERN_N) & PHY_CAT_PATTERN_MASK; ++ unsigned int index; ++ ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) { ++ if (is_ca49) { ++ if (index != 4 && index != 9) /* ca4 ca9 */ ++ continue; ++ } else { ++ if (index == 4 || index == 9) /* ca4 ca9 */ ++ continue; ++ } ++ ++ /* compare result and pattern */ ++ if (!ddr_lpca_compare(&data->bits[index], dq_result, pattern_p, pattern_n, index)) ++ ddr_lpca_get_data(data, index, bdl); /* pass */ ++ } ++} ++ ++/* Excute lpca command and check result */ ++static void ddr_lpca_excute(struct ca_data_st *data, unsigned int bdl, unsigned int is_ca49) ++{ ++ volatile union u_phy_catconfig *ca = (union u_phy_catconfig *) ++ (data->base_phy + DDR_PHY_CATCONFIG); ++ ++ if (is_ca49) ++ ca->bits.sw_cat_mrw48 = 1; ++ else ++ ca->bits.sw_cat_mrw41 = 1; ++ ++ ddr_lpca_wait(ca); ++ ca->bits.sw_cat_cke_low = 1; ++ ddr_lpca_wait(ca); ++ ca->bits.sw_cat_strobe = 1; ++ ddr_lpca_wait(ca); ++ ++ /* check PHYDQRESULT */ ++ ddr_lpca_check(data, bdl, is_ca49); ++ ++ ca->bits.sw_cat_cke_high = 1; ++ ddr_lpca_wait(ca); ++ ca->bits.sw_cat_mrw42 = 1; ++ ddr_lpca_wait(ca); ++} ++ ++/* Find address bdl */ ++static int ddr_lpca_find_bdl(struct ca_data_st *data) ++{ ++ unsigned int bdl; ++ ++ for (bdl = 0; bdl <= PHY_ACADDR_BDL_MASK; bdl++) { ++ /* update bdl */ ++ ddr_lpca_set_bdl(data->base_phy, bdl); ++ ++ /* ca0~ca3, ca5~ca8 */ ++ ddr_lpca_excute(data, bdl, DDR_FALSE); ++ ++ /* ca4, ca9 */ ++ ddr_lpca_excute(data, bdl, DDR_TRUE); ++ } ++ ++ if (data->done == PHY_CAT_PATTERN_MASK) ++ return 0; ++ ++ return -1; ++} ++ ++/* Loop phase to find valid bdl and phase */ ++static int ddr_lpca_loop_phase(struct ca_data_st *data, int step) ++{ ++ volatile union u_phy_addrphbound *ph = (union u_phy_addrphbound *) ++ (data->base_phy + DDR_PHY_ADDRPHBOUND); ++ unsigned int phase; ++ unsigned int addrph_def = ph->bits.addrph_a; ++ int addrph = addrph_def; ++ ++ for (phase = 0; phase <= PHY_ADDRPH_MASK; phase++) { ++ /* reset ca training data */ ++ ddr_lpca_reset(data); ++ ++ /* find bdl */ ++ if (!ddr_lpca_find_bdl(data)) ++ return 0; ++ ++ addrph += step; ++ if (addrph < 0 || addrph > PHY_ADDRPH_MASK) ++ break; ++ ++ ph->bits.addrph_a = addrph; ++ ddr_phy_cfg_update(data->base_phy); ++ } ++ ++ /* restore default value */ ++ ddr_debug("current phase[%x = %x], restore default[%x]", ph, *ph, addrph_def); ++ ph->bits.addrph_a = addrph_def; ++ ++ return -1; ++} ++ ++/* Find a valid phase */ ++static int ddr_lpca_find_phase(struct ca_data_st *data) ++{ ++ /* increase default value to find */ ++ if (!ddr_lpca_loop_phase(data, 1)) ++ return 0; ++ ++ /* decrease default value to find */ ++ if (!ddr_lpca_loop_phase(data, -1)) ++ return 0; ++ ++ return -1; ++} ++ ++/* Set step to adjust address window */ ++static int ddr_lpca_set_step(struct ca_data_st *data) ++{ ++ /* max window, no need to found */ ++ if (data->min == 0 && data->max == PHY_ACADDR_BDL_MASK) ++ return 0; ++ ++ if (data->min == 0) ++ return -1; /* window on left, move to right */ ++ else ++ return 1; /* window on right, move to left */ ++} ++ ++/* ++ * Adjust address window via change phase. ++ * Increase phase, window will move to left. ++ */ ++static void ddr_lpca_adjust(struct ca_data_st *data) ++{ ++ int step; ++ volatile union u_phy_addrphbound *ph = (union u_phy_addrphbound *) ++ (data->base_phy + DDR_PHY_ADDRPHBOUND); ++ unsigned int phase; ++ unsigned int addrph_last = ph->bits.addrph_a; ++ int addrph_cur = addrph_last; ++ ++ /* set step to increase or decrease phase */ ++ step = ddr_lpca_set_step(data); ++ if (!step) ++ return; ++ ++ for (phase = 0; phase <= PHY_ADDRPH_MASK; phase++) { ++ addrph_cur += step; ++ if (addrph_cur < 0 || addrph_cur > PHY_ADDRPH_MASK) ++ return; ++ ++ ph->bits.addrph_a = addrph_cur; ++ ddr_phy_cfg_update(data->base_phy); ++ ++ /* reset ca training data */ ++ ddr_lpca_reset(data); ++ ++ if (ddr_lpca_find_bdl(data)) { ++ /* not find bdl, restore last value */ ++ addrph_cur -= step; ++ ddr_lpca_find_bdl(data); ++ return; ++ } ++ ++ /* max window: ------- */ ++ if (data->min == 0 && data->max == PHY_ACADDR_BDL_MASK) ++ return; ++ ++ /* last window: -----xx */ ++ if (data->min == 0 && step == 1) { ++ /* last value is best */ ++ addrph_cur -= step; ++ ph->bits.addrph_a = addrph_cur; ++ ddr_phy_cfg_update(data->base_phy); ++ ddr_lpca_reset(data); ++ ddr_lpca_find_bdl(data); ++ return; ++ } ++ ++ /* best window: x-----x */ ++ if (data->min > 0 && step == -1) ++ return; ++ } ++} ++ ++/* Low power DDR CA training */ ++int ddr_lpca_training(const struct ddr_cfg_st *cfg) ++{ ++ volatile union u_phy_catconfig *ca = (union u_phy_catconfig *) ++ (cfg->cur_phy + DDR_PHY_CATCONFIG); ++ ++ struct ca_data_st data; ++ int ret; ++ ++ ddr_debug("DDR LPCA training"); ++ ++ /* init data */ ++ ddr_lpca_init(cfg->cur_dmc, cfg->cur_phy, &data); ++ ++ /* enable sw ca training, wait 62.5ns */ ++ ca->bits.sw_cat_en = 1; ++ ++ /* find a valid phase first */ ++ ret = ddr_lpca_find_phase(&data); ++ ++ /* display training result */ ++ ddr_lpca_display(&data); ++ ++ if (ret) { ++ /* restore default value when fail */ ++ ddr_lpca_restore_def(&data); ++ ddr_error("PHY[%x] found phase fail, result[%x]", cfg->cur_phy, data.done); ++ ddr_training_stat(DDR_ERR_LPCA, cfg->cur_phy, -1, -1); ++ } else { ++ /* adjust window via phase */ ++ ddr_lpca_adjust(&data); ++ ddr_lpca_display(&data); ++ /* set training result */ ++ ddr_lpca_update_bdl(&data); ++ } ++ ++ /* disable sw ca training */ ++ ca->bits.sw_cat_en = 0; ++ ++ /* save lpca result data to printf */ ++ ddr_lpca_data_save(cfg, &data); ++ ++ return ret; ++} ++ ++int ddr_lpca_training_func(const struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ struct tr_relate_reg relate_reg; ++ ++ /* LPCA training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_LPCA_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_LPCA_MASK); ++ ++ /* only lowpower ddr3 support */ ++ if ((reg_read(cfg->cur_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_LPDDR3) == ++ PHY_DRAMCFG_TYPE_LPDDR3) ++ result += ddr_lpca_training(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_lpca_training_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support LPDDR CA training"); ++ return 0; ++} ++#endif /* DDR_LPCA_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default/ddr_mpr_training.c b/drivers/ddr/vendor/default/ddr_mpr_training.c +new file mode 100644 +index 0000000..3970ce6 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_mpr_training.c +@@ -0,0 +1,306 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __MPR_TRAINING__ ++#ifdef DDR_MPR_TRAINING_CONFIG ++/* Switch MPR function */ ++static void ddr_mpr_switch(unsigned int base_dmc, int val) ++{ ++ unsigned int sfc_cmd; ++ if (val == DDR_TRUE) ++ sfc_cmd = (DMC_CMD_MRS_MR3 << DMC_SFC_CMD_MRS_BIT) | DMC_CMD_TYPE_LMR; ++ else ++ sfc_cmd = DMC_CMD_TYPE_LMR; ++ ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, DMC_BANK_MR3); ++ ++ /* clear */ ++ if (val == DDR_FALSE) { ++ reg_write(0x0, base_dmc + DDR_DMC_SFCBANK); ++ reg_write(0x0, base_dmc + DDR_DMC_SFCREQ); ++ } ++} ++ ++/* Judge MPR data */ ++static int ddr_mpr_judge(unsigned int data1, unsigned int data2, ++ unsigned int data3, unsigned int data4, unsigned int dq_index) ++{ ++ /* check byte */ ++ if (dq_index == -1) { ++ if (data1 == DDR_MPR_BYTE_MASK && data2 == 0x0 && ++ data3 == DDR_MPR_BYTE_MASK && data4 == 0x0) ++ return 0; ++ else ++ return -1; ++ } else { ++ /* check DQ */ ++ data1 = (data1 >> dq_index) & DDR_MPR_BIT_MASK; ++ data2 = (data2 >> dq_index) & DDR_MPR_BIT_MASK; ++ data3 = (data3 >> dq_index) & DDR_MPR_BIT_MASK; ++ data4 = (data4 >> dq_index) & DDR_MPR_BIT_MASK; ++ if (data1 == DDR_MPR_BIT_MASK && data2 == 0x0 && ++ data3 == DDR_MPR_BIT_MASK && data4 == 0x0) ++ return 0; ++ else ++ return -1; ++ } ++} ++ ++/* Extract MPR read data to judge */ ++static int ddr_mpr_extract(struct ddr_cfg_st *cfg, ++ unsigned int offset0, unsigned int offset1, ++ unsigned int offset2, unsigned int offset3) ++{ ++ unsigned int data1, data2, data3, data4; ++ unsigned int base_dmc = cfg->cur_dmc; ++ unsigned int byte_index = cfg->cur_byte; ++ ++ data1 = reg_read(base_dmc + offset0); /* SFC read data [127:96] or [255:224] */ ++ data2 = reg_read(base_dmc + offset1); /* SFC read data [95:64] or [223:192] */ ++ data3 = reg_read(base_dmc + offset2); /* SFC read data [63:32] or [191:160] */ ++ data4 = reg_read(base_dmc + offset3); /* SFC read data [31:0] or [159:128] */ ++ ++ ddr_info("byte[%x] data[%x=%x][%x=%x][%x=%x][%x=%x]", ++ byte_index, ++ base_dmc + offset0, data1, base_dmc + offset1, data2, ++ base_dmc + offset2, data3, base_dmc + offset3, data4); ++ ++ if (get_byte_num(cfg) == DDR_PHY_BYTE_MAX) { ++ /* four byte: data1[0xFFFFFFFF] data2[0x00000000] ++ data3[0xFFFFFFFF] data4[0x00000000] */ ++ data1 = (data1 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data2 = (data2 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data3 = (data3 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data4 = (data4 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ } else { ++ /* two byte: data1[0xFFFF0000] data2[0xFFFF0000] ++ data3[0xFFFF0000] data4[0xFFFF0000] */ ++ data1 = (data1 >> DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data2 = (data2 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data3 = (data3 >> DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data4 = (data4 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ if (ddr_mpr_judge(data1, data2, data3, data4, cfg->cur_dq)) ++ return -1; ++ ++ /* two byte need to swap data and check again */ ++ data1 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA1) >> ++ DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data2 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA0) >> ++ (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data3 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA3) >> ++ DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data4 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA2) >> ++ (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ } ++ ++ return ddr_mpr_judge(data1, data2, data3, data4, cfg->cur_dq); ++} ++ ++/* Check MPR read data */ ++int ddr_mpr_check(const struct ddr_cfg_st *cfg) ++{ ++ /* read data */ ++ ddr_dmc_sfc_cmd(cfg->cur_dmc, DMC_CMD_TYPE_READ, 0x0, 0x0); ++ ++ return dmc_mpr_check_bit_0_127(cfg); ++} ++ ++/* Find RDQ via MPR */ ++static int ddr_mpr_find_rdq(struct ddr_cfg_st *cfg) ++{ ++ struct training_data training; ++ unsigned int dq_num; ++ unsigned int win_num; ++ unsigned int def_dq, best_dq; ++ unsigned int byte_index, dq_index; ++ ++ memset(&training, 0, sizeof(struct training_data)); ++ /* find rdq via mpr */ ++ cfg->dq_check_type = DDR_CHECK_TYPE_MPR; ++ ++ /* find rdq */ ++ for (byte_index = 0; ++ byte_index < get_byte_num(cfg); byte_index++) { ++ for (dq_index = 0; dq_index < DDR_PHY_BIT_NUM; dq_index++) { ++ dq_num = (byte_index << DDR_BYTE_DQ) + dq_index; ++ def_dq = ddr_phy_get_dq_bdl(cfg); ++ ddr_dataeye_find_dq(cfg, &training); ++ win_num = training.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ best_dq = training.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ if (win_num > 0) { ++ ddr_phy_set_dq_bdl(cfg, best_dq); ++ } else { ++ /* In normal case, not reach here */ ++ /* restore default value */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ++ ddr_fatal("PHY[%x] Byte[%x] DQ[%x] MPR fail", ++ cfg->cur_phy, byte_index, dq_index); ++ ddr_training_stat(DDR_ERR_MPR, cfg->cur_phy, ++ byte_index, dq_index); ++ return -1; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* Find RDQS via MPR */ ++static int ddr_mpr_find_rdqs(struct ddr_cfg_st *cfg) ++{ ++ unsigned int rdqs_start = 0; ++ unsigned int rdqs_end = PHY_RDQS_BDL_MASK; ++ unsigned int rdqs_mid; ++ unsigned int val, delay; ++ unsigned int count = 0; ++ int found = DDR_FALSE; ++ ++ /* set rdq to middle value */ ++ reg_write(PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte)); ++ reg_write(PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte)); ++ ++ /* clear rdqs */ ++ delay = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)) >> PHY_RDQS_BDL_BIT; ++ rdqs_mid = delay; /* if not found, restore default value */ ++ delay = delay & (~PHY_RDQS_BDL_MASK); ++ ++ /* find rdqs */ ++ for (val = 0; val <= PHY_RDQS_BDL_MASK; val++) { ++ reg_write(delay | (val << PHY_RDQS_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ddr_phy_cfg_update(cfg->cur_phy); ++ /* check ok */ ++ if (!ddr_mpr_check(cfg)) { ++ if (found == DDR_TRUE) ++ continue; ++ ++ rdqs_start = val; /* found start value */ ++ count++; ++ if (count == DDR_MPR_RDQS_FIND_TIMES) ++ found = DDR_TRUE; ++ } else { ++ if (found == DDR_TRUE) { ++ rdqs_end = val; /* found end value */ ++ break; ++ } ++ } ++ } ++ ++ if (found == DDR_TRUE) { ++ rdqs_mid = ((rdqs_end - rdqs_start) >> 1) + rdqs_start; ++ ddr_info("PHY[%x] Byte[%x] rdqs_middle[%x]", ++ cfg->cur_phy, cfg->cur_byte, rdqs_mid); ++ ddr_info("rdqs_start[%x] rdqs_end[%x]", rdqs_start, rdqs_end); ++ } else { ++ ddr_fatal("PHY[%x] Byte[%x] not find RDQS, restore", ++ cfg->cur_phy, cfg->cur_byte); ++ ddr_training_stat(DDR_ERR_MPR, cfg->cur_phy, cfg->cur_byte, -1); ++ } ++ ++ reg_write(delay | (rdqs_mid << PHY_RDQS_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ddr_phy_cfg_update(cfg->cur_phy); ++ ++ return ((found == DDR_TRUE) ? 0 : -1); ++} ++ ++/* Multi Purpose Register(MPR) */ ++static int ddr_mpr_training(struct ddr_cfg_st *cfg) ++{ ++ int i; ++ int result = 0; ++ unsigned int byte_num = get_byte_num(cfg); ++ unsigned int mr0; ++ unsigned int sfc_cmd; ++ unsigned int base_dmc = cfg->cur_dmc; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ ddr_debug("DDR MPR training"); ++ ++ /* set DDR bust */ ++ if (byte_num == DDR_PHY_BYTE_MAX) { ++ mr0 = (reg_read(base_phy + DDR_PHY_MODEREG01) & ++ DMC_MRS_MASK) & (~DMC_MR0_BL_MASK); ++ sfc_cmd = ((mr0 | DMC_MR0_BL_BUST4) << DMC_SFC_CMD_MRS_BIT) | ++ DMC_CMD_TYPE_LMR; ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, 0x0); ++ } ++ ++ /* precharge all */ ++ ddr_dmc_sfc_cmd(base_dmc, DMC_CMD_TYPE_PRECHARGE_ALL, 0x0, 0x0); ++ ++ /* enable MPR */ ++ ddr_mpr_switch(base_dmc, DDR_TRUE); ++ ++ /* find rdqs */ ++ for (i = 0; i < byte_num; i++) ++ result += ddr_mpr_find_rdqs(cfg); ++ ++ /* find rdq */ ++ if (!result) ++ result = ddr_mpr_find_rdq(cfg); ++ ++ /* disable MPR */ ++ ddr_mpr_switch(base_dmc, DDR_FALSE); ++ ++ /* restore DDR bust */ ++ if (byte_num == DDR_PHY_BYTE_MAX) { ++ mr0 = (reg_read(base_phy + DDR_PHY_MODEREG01) & DMC_MRS_MASK); ++ sfc_cmd = (mr0 << DMC_SFC_CMD_MRS_BIT) | DMC_CMD_TYPE_LMR; ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, 0x0); ++ } ++ ++ return result; ++} ++ ++int ddr_mpr_training_func(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result; ++ ++ /* MPR training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_MPR_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_MPR_MASK); ++ result = ddr_mpr_training(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_mpr_check(const struct ddr_cfg_st *cfg) ++{ ++ return 0; ++} ++int ddr_mpr_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR MPR training"); ++ return 0; ++} ++#endif /* DDR_MPR_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default/ddr_phy_s28_v300.h b/drivers/ddr/vendor/default/ddr_phy_s28_v300.h +new file mode 100644 +index 0000000..70a4575 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_phy_s28_v300.h +@@ -0,0 +1,510 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_PHY_S28_V300_H ++#define DDR_PHY_S28_V300_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_PHY0 DDR_REG_BASE_PHY1 */ ++/* control the initialization of the PHY */ ++#define DDR_PHY_PHYINITCTRL 0x4 ++#define DDR_PHY_TIMER0 0x10 /* This register specified the timing parameter required by the PHY */ ++#define DDR_PHY_TIMER1 0x14 ++#define DDR_PHY_PHYINITSTATUS 0x8 /* Read Data Eye Calibration Error */ ++#define DDR_PHY_IMPSTATUS 0x28 /* This register specify the ZQ calibration result. */ ++#define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ ++#define DDR_PHY_DRAMTIMER1 0x34 /* This register specify the DRAM timing parameters */ ++#define DDR_PHY_TRAINCTRL0 0x48 /* hw training control */ ++#define DDR_PHY_MODEREG01 0x64 /* Extend Mode Register 01 */ ++#define DDR_PHY_MODEREG23 0x68 /* Extend Mode Register 23 */ ++/* update delay setting in registers to PHY */ ++#define DDR_PHY_MISC 0x70 ++#define DDR_PHY_DMSEL 0x84 /* DM Swap Selection */ ++#define DDR_PHY_SWTMODE 0xa0 /* S/W training mode */ ++/* issue one DQS pulse from PHY to DRAM */ ++#define DDR_PHY_SWTWLDQS 0xa4 ++#define DDR_PHY_SWTRLT 0xa8 /* S/W training result */ ++/* Host vref. [5:0]range [17:12]refsel */ ++#define DDR_PHY_PHYRSCTRL 0xB0 /* PHY Register Slice Contrl */ ++#define DDR_PHY_IOCTL2 0xB4 ++#define DDR_PHY_VREFTCTRL 0xc0 /* VREF Training Control Register. */ ++#define DDR_PHY_DVRFTCTRL 0xC4 /* DRAM VREF Training */ ++#define DDR_PHY_TRAINCTRL2 0xd4 /* This register control the data training */ ++#define DDR_PHY_TRAINCTRL3 0xdc /* This register control the data training */ ++#define DDR_PHY_MODEREG45 0xe0 /* defines the contents of the Mode Register */ ++#define DDR_PHY_MODEREG67 0xe4 /* This register defines the contents of the Mode Register */ ++#define DDR_PHY_TRAINCTRL5 0x118 /* This register control the data training */ ++#define ddr_phy_acaddrbdl(n) (0x140 + ((n) << 2)) ++#define DDR_PHY_IMP_CTRL1 0x170 /* AC/DX ZQ selection */ ++#define DDR_PHY_IMP_STATUS1 0x174 /* AC ZCAL status */ ++#define DDR_PHY_CATSWAPINDEX 0x01B8 /* CA SWAP index register */ ++#define DDR_PHY_CATSWAPSEL 0x01BC /* CA SWAP select register */ ++#define DDR_PHY_CATCONFIG 0x1C8 /* CA Training Configuration */ ++#define DDR_PHY_PHYDQRESULT 0x1D0 /* SW CA Training DQ result from PHY */ ++#define DDR_PHY_ADDRPHBOUND 0x1D4 /* CA Training addr phase boundary */ ++#define DDR_PHY_SWCATPATTERN_P 0x1D8 /* pattern for positive CK edge */ ++#define DDR_PHY_SWCATPATTERN_N 0x1DC /* pattern for negative CK edge */ ++#define DDR_PHY_MRS_SEQ_PROG 0x1e0 /* Programmed MRS sequence in the DRAM initialization */ ++/* AC command bit delay line setting */ ++#define DDR_PHY_ACCMDBDL2 0x128 ++ ++/* WR DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++on write path */ ++#define ddr_phy_dxnwdqnbdl0(m, n) (0x210 + (((m) & 0) << 10) + ((n) << 7)) ++/* WR DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] */ ++#define ddr_phy_dxnwdqnbdl1(m, n) (0x214 + (((m) & 0) << 10) + ((n) << 7)) ++/* WR DM [6:0] the delay value of the bit delay line on DQM */ ++#define ddr_phy_dxnwdqnbdl2(m, n) (0x218 + (((m) & 0) << 10) + ((n) << 7)) ++/* RD DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl0(m, n) (0x21C + ((m) << 10) + ((n) << 7)) ++/* RD DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl1(m, n) (0x220 + ((m) << 10) + ((n) << 7)) ++/* [6:0]RD DM */ ++#define ddr_phy_dxnrdqnbdl2(m, n) (0x224 + ((m) << 10) + ((n) << 7)) ++ ++/* [CUSTOM] */ ++#define ddr_phy_dxnoebdl(m, n) (0x228 + (((m) & 0) << 10) + ((n) << 7)) ++/* [8:0] rdqs_bdl [24:16]rdqs_cyc. ++phase shift of the Read DQS to create 90 degree delays */ ++#define ddr_phy_dxnrdqsdly(n) (0x22C + ((n) << 7)) ++/* [6:0] the delay value of delay applied on WDQS for write leveling */ ++#define ddr_phy_dxwdqsdly(m, n) (0x230 + (((m) & 0) << 10) + ((n) << 7)) ++/* WR DQ phase BIT 12:8 */ ++#define ddr_phy_dxnwdqdly(m, n) (0x234 + (((m) & 0) << 10) + ((n) << 7)) ++/* [CUSTOM] rddqs gating */ ++#define ddr_phy_dxnrdqsgdly(m, n) (0x240 + ((m) << 10) + ((n) << 7)) ++/* read boundary right 8:0 left 24:16 */ ++#define ddr_phy_dxnrdbound(n) (0x250 + ((n) << 7)) ++/* write boundary right 4:0 left 20:16 */ ++#define ddr_phy_dxnwdbound(n) (0x254 + ((n) << 7)) ++/* [5:0] DRAM VREF(DQ) training result */ ++#define ddr_phy_dvreft_status(n) (0x270 + ((n) << 7)) ++/* [4:0] Host PHY VREF(DQ) training result */ ++#define ddr_phy_hvreft_status(m, n) (0x274 + (((m) & 0) << 10) + ((n) << 7)) ++ ++/* DDRPHY AC static register */ ++#define DDR_PHY_ACIOCTL 0x1018 /* IO control register */ ++#define DDR_PHY_CORNER_DETECTOR 0x104C /* cfg of corner detector */ ++#define DDR_PHY_ACPHYCTL4 0x1064 /* AC block PHY control register */ ++#define DDR_PHY_ACPHYCTL7 0x1070 ++/* DDRPHY_DX_STATIC_REG_DDR4 */ ++/* Data block PHY miscellaneous control register. */ ++#define dx_dxnmiscctrl0(p) (0x1238 + ((p) << 7)) ++/* Data block PHY debug/miscellaneous control register. dxctl_pre_margin_code [24:22] */ ++#define dx_dxnmiscctrl3(p) (0x1254 + ((p) << 7)) ++ ++#ifndef DDR_VREF_HOST_VAL_MAX ++#define DDR_VREF_HOST_VAL_MAX 0x3f /* 78.75%*VDDIO */ ++#endif ++#define DDR_VREF_HOST_VAL_MIN 0x0 /* 40.00%*VDDIO */ ++ ++/* register mask */ ++#define PHY_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_WDQS_PHASE_MASK 0xf /* [11:8] */ ++#define PHY_RDQS_BDL_MASK 0x1ff /* [CUSTOM] [8:0] rdqsbdl */ ++#define PHY_RDQSG_PHASE_MASK 0x3f /* [14:9] rdqsgphase */ ++#define PHY_RDM_BDL_MASK 0x7f /* [6:0] */ ++/* hardware gate training result */ ++#define PHY_INITSTATUS_GT_MASK 0x20 ++#define PHY_SWTRLT_WL_MASK 0xf ++#define PHY_SWTRLT_GATE_MASK 0xf ++#define PHY_WDQ_PHASE_MASK 0x3f ++#define PHY_PHYINITCTRL_MASK 0x1 /* [15:0] all stat */ ++/* Read Data Eye Calibration Error */ ++#define PHY_PHYINITSTATUS_RDET_ERR 0x100 ++#define PHY_ACPHY_DCLK_MASK 0x7 /* cp1p_dclk0 mask */ ++#define PHY_ACPHY_DRAMCLK_MASK 0x1 /* halft_dramclk0 mask */ ++#define PHY_VRFTRES_DVREF_MASK 0x3f /* [5:0] */ ++#ifndef PHY_VRFTRES_HVREF_MASK ++#define PHY_VRFTRES_HVREF_MASK 0x3f /* [4:0] */ ++#endif ++#define PHY_VRFTRES_RXDIFFCAL_MASK 0xf /* [24:21] */ ++#define PHY_ADDRPH_MASK 0x1f /* [20:16] */ ++#define PHY_ACADDR_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_CATSWAPSEL_BIT_MASK 0xff ++#define PHY_CAT_PATTERN_MASK 0x3ff ++#define PHY_TRAINCTRL0_MASK 0xf /* [3:0] */ ++#define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ ++#define PHY_VREFS_MRS_ENTER_MASK 0x1 /* [31] */ ++#define PHY_OSC_START_MASK 0x1 /* [0] */ ++#define PHY_OSC_RPT_VLD_MASK 0x1 /* [15] */ ++#define PHY_OSC_CNT_RDATA_MASK 0xffff /* [31:16] */ ++#define PHY_ZCODE_PDRV_MASK 0x3f /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_MASK 0x3f /* [29:24] */ ++#define DX_DXNMISCCTRL0_DQ_MASK 0Xff /* [15:8] dxctl_rxp_2nd_dq */ ++#define DX_DXNMISCCTRL0_DM_MASK 0X1 /* [20] dxctl_rxp_2nd_dm */ ++#define DX_DXNMISCCTRL3_MASK 0X7 /* [24:22] dxctl_pre_margin_code */ ++#define DDR_PHY_T_MOD_MASK 0x1f /* bit[8:4]t_mod */ ++#define PHY_AC_IOCTL_TX_MODE_MASK 0x3 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* register bit */ ++#define PHY_MISC_UPDATE_BIT 19 /* [CUSTOM] delay config update bit */ ++#define PHY_PHYCONN_RST_BIT 15 /* issue reset signal to PHY counter */ ++#define PHY_RST_BIT 13 /* bit[13] issue reset signal to PHY */ ++#define PHY_RDQSG_PHASE_BIT 9 /* [CUSTOM] */ ++#define PHY_RDQSG_TX_BDL_BIT 16 /* [22:16] rdqsgtxbdl */ ++#define PHY_WDQS_PHASE_BIT 8 ++#define PHY_WDQS_BDL_BIT 0 ++#define PHY_WDQ_PHASE_BIT 8 ++#define PHY_WDM_BDL_BIT 0 ++/* [22:16] Write DQS Output Enable Delay Control */ ++#define PHY_WDQSOE_BDL_BIT 16 ++#define PHY_OEN_BDL_BIT 0 ++/* Mode Register 1. Defines the MR3/MR9 of the mode register */ ++#define PHY_MODEREG01_MR1_BIT 16 ++/* Bit delay line setting of CS1 */ ++#define PHY_ACCMD_CS0_BIT 0 ++#define PHY_ACCMD_CS1_BIT 16 ++#define PHY_ACPHY_DCLK0_BIT 6 /* [8:6] cp1p_dclk0 */ ++#define PHY_ACPHY_DCLK1_BIT 9 /* [11:9] ck2p_dclk1 */ ++#define PHY_ACPHY_DRAMCLK0_BIT 25 /* [25] halft_dramclk0 */ ++#define PHY_ACPHY_DRAMCLK1_BIT 24 /* [24] halft_dramclk1 */ ++#define PHY_ACPHY_DRAMCLK_EXT_BIT 3 /* [3] halft_dramclk0 */ ++#define PHY_SWTMODE_SW_GTMODE_BIT 1 /* [1] SW gate training */ ++#define PHY_ACADDRBDL_ADDR1_BIT 16 /* [16] ADDR1 delay line */ ++#define PHY_VRFTRES_RXDIFFCAL_BIT 21 /* [24:21] */ ++#define PHY_VREFS_MRS_ENTER_BIT 31 /* [31] */ ++#define PHY_OSC_RPT_VLD 15 /* [15] */ ++#define PHY_OSC_CNT_RDATA_BIT 16 /* [31:16] */ ++#define PHY_ZCODE_PDRV_BIT 16 /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_BIT 24 /* [29:24] */ ++#define PHY_AC_VDDQ_CAL_EN_BIT 8 /* [8] AC ZQ calibration enable */ ++#define DX_DXNMISCCTRL0_DQ_BIT 8 /* [15:8] dxctl_rxp_2nd_dq */ ++#define DX_DXNMISCCTRL0_DM_BIT 20 /* [20] dxctl_rxp_2nd_dm */ ++#define DX_DXNMISCCTRL3_BIT 22 /* [24:22] dxctl_pre_margin_code */ ++#define PHY_CFG_RX_AGE_COMPST_EN_BIT 31 /* Enable rdqs age compensation function */ ++#define DDR_PHY_T_MOD_BIT 4 /* bit[8:4]t_mod */ ++#define PHY_MODEREG67_LP4_FSPWR_BIT 6 /* bit[6] FSPWR */ ++#define PHY_AC_IOCTL_TX_MODE_BIT 12 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* BDL register bit */ ++#define PHY_BDL_DQ_BIT 0 ++#define PHY_BDL_DQ0_BIT 0 ++#define PHY_BDL_DQ1_BIT 8 ++#define PHY_BDL_DQ2_BIT 16 ++#define PHY_BDL_DQ3_BIT 24 ++#define PHY_RDM_BDL_BIT 0 ++#define PHY_RDQS_BDL_BIT 0 ++ ++/* value */ ++#define PHY_PHYINITCTRL_DVREFT_SYNC 0x40000 /* DRAM VREF Synchronize */ ++/* hw training item defined in PHYINITCTRL */ ++#define PHY_PHYINITCTRL_CTL_CKE_BYPASS (1 << 31) /* PACK's CKE bypass function enable. */ ++#define PHY_PHYINITCTRL_PIC_PHYUPD_REQ (1 << 30) /* PACK's DFI PHY UPDATAE request by SW. */ ++#define PHY_PHYINITCTRL_PIC_TDQSST (1 << 28) /* TDQSS training Enable. */ ++#define PHY_PHYINITCTRL_CFG_LPBK_COMPST_EN (1 << 27) /* RDQS/CK loopback delay compensate enable. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_SFT (1 << 26) /* Update delay line(switch op_sel) during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_WR (1 << 25) /* Retraining with MPC write during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_RD (1 << 24) /* Retraining with MPC read during tRFC. */ ++#define PHY_PHYINITCTRL_JTMT_EN (1 << 23) /* PLL Jitter Meter Enable. */ ++#define PHY_PHYINITCTRL_CST_EN (1 << 22) /* HW CS Traninig Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFS_EN (1 << 21) /* DRAM VREF(AC) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_ACHVREFT_EN (1 << 20) /* Host VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFT_EN (1 << 19) /* DRAM VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_DXDVREFS_EN (1 << 18) /* DRAM VREF(DQ) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_HVREFT_EN (1 << 17) /* Host VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_DVREFT_EN (1 << 16) /* DRAM VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_PHYCONN_RST (1 << 15) /* PHY Counter Reset. */ ++#define PHY_PHYINITCTRL_PACK_RST (1 << 14) /* PACK Reset. */ ++#define PHY_PHYINITCTRL_PHY_RST (1 << 13) /* PHY Reset. */ ++#define PHY_PHYINITCTRL_DRAM_RST (1 << 12) /* DRAM Reset. */ ++#define PHY_PHYINITCTRL_CAT_EN (1 << 11) /* HW CA Traninig Enable. */ ++#define PHY_PHYINITCTRL_DRAM_INIT_EN (1 << 10) /* DRAM Initialization Enable. */ ++#define PHY_PHYINITCTRL_WDET_EN (1 << 9) /* Write Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_RDET_EN (1 << 8) /* Read Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_WL2_EN (1 << 7) /* Second Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_GDST_EN (1 << 6) /* PHY Read Data Latch Train Enable. */ ++#define PHY_PHYINITCTRL_GT_EN (1 << 5) /* Gate Training Enable. */ ++#define PHY_PHYINITCTRL_WL_EN (1 << 4) /* Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_ZCAL_EN (1 << 3) /* Impedance Calibration Enable. */ ++#define PHY_PHYINITCTRL_DLYMEAS_EN (1 << 2) /* Delay Measurement Enable. */ ++#define PHY_PHYINITCTRL_PLL_INIT_EN (1 << 1) /* PLL Initialization Enable. */ ++#define PHY_PHYINITCTRL_INIT_EN (1 << 0) /* PHY Initialization Enable. */ ++ ++#define PHY_HW_GP_PHY_RESET (PHY_PHYINITCTRL_PHY_RST) ++#define PHY_HW_GP_CNT_RESET_START (PHY_PHYINITCTRL_PHYCONN_RST) ++#define PHY_HW_GP_PLL (PHY_PHYINITCTRL_PLL_INIT_EN | PHY_PHYINITCTRL_ZCAL_EN | PHY_PHYINITCTRL_DLYMEAS_EN) ++#define PHY_HW_GP_DRAM_RESET (PHY_PHYINITCTRL_DRAM_RST | PHY_PHYINITCTRL_DRAM_INIT_EN) ++#define PHY_HW_GP_VREF_AC (PHY_PHYINITCTRL_ACDVREFS_EN) ++#define PHY_HW_GP_CS (PHY_PHYINITCTRL_CST_EN) ++#define PHY_HW_GP_VREF_DQ (PHY_PHYINITCTRL_DVREFT_SYNC) ++#define PHY_HW_GP_NORMAL (PHY_PHYINITCTRL_WL_EN | \ ++ PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN | \ ++ PHY_PHYINITCTRL_DVREFT_EN | \ ++ PHY_PHYINITCTRL_HVREFT_EN | \ ++ PHY_PHYINITCTRL_PIC_TDQSST) ++#define PHY_HW_GP_CNT_RESET_END (PHY_PHYINITCTRL_PHYCONN_RST) ++ ++/* RDQS range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_RDQS_MIDDLE_VAL 0x30 ++/* DQ range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_DQ_MIDDLE_VAL 0x30303030 ++#define PHY_MISC_SCRAMB_DIS 0xfffeffff /* scrambler disable */ ++#define PHY_GATE_BDL_MAX 0xfe /* [6:0]rdqsg_bdl + [22:16]rdqsgtxbdl */ ++#define PHY_DVRFTCTRL_PDAEN_EN 0x80000000 /* pda enable */ ++/* [5] two cycle on address or command.(2T timing) */ ++#define PHY_DRAMCFG_MA2T 0x20 ++ ++#define PHY_DRAMCFG_TYPE_DDR1 0x0 /* [2:0] 000 DDR1 */ ++#define PHY_DRAMCFG_TYPE_DDR2 0x1 /* [2:0] 001 DDR2 */ ++#define PHY_DRAMCFG_TYPE_DDR3 0x2 /* [2:0] 010 DDR3 */ ++#define PHY_DRAMCFG_TYPE_DDR3L 0x3 /* [2:0] 011 DDR3L */ ++#define PHY_DRAMCFG_TYPE_LPDDR1 0x4 /* [2:0] 100 LPDDR1 */ ++#define PHY_DRAMCFG_TYPE_LPDDR2 0x5 /* [2:0] 101 LPDDR2 */ ++#define PHY_DRAMCFG_TYPE_LPDDR3 0x5 /* [2:0] 101 LPDDR3 */ ++#define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ ++#define PHY_DRAMCFG_TYPE_DDR4 0xa /* [3] 1010 DDR4 */ ++ ++#define PHY_DMSEL_SWAPDFIBYTE 0xf8ffffff /* [24:26] No Swap */ ++/* other */ ++#define PHY_RDQSG_PHASE_STEP 2 /* gate training phase step. */ ++#define PHY_GATE_PHASE_MARGIN 8 /* gate phase margin */ ++#define PHY_DQ_BDL_LEVEL 128 /* [CUSTOM] DQ BDL range */ ++#define PHY_DQ_BDL_MIDDLE 64 /* special middle DQ BDL value */ ++#define PHY_RDQSG_PHASE_MAX 0x3c /* RDQSG phase max value */ ++#define PHY_ACPHY_CLK_MAX 0xf /* halft_dramclk0 + cp1p_dclk0 */ ++#define PHY_PCODE_MIN 0x14 ++#define PHY_PCODE_MAX 0x24 ++ ++/* AC_DDRPHY_GATED_BYPASS */ ++#define PHY_CK_IOCTL_DUTY_EN 0x4 /* enable ck_ioctl_DUTY_EN_v */ ++#define PHY_CK1_IOCTL_DUTY_EN 0x8 /* enable ck1_ioctl_DUTY_EN_v */ ++/* CK AC_IOCTL22 */ ++#define DDR_DUTY_NUM 8 /* CK duty number */ ++#define DDR_CK_MAX_NUM 2 /* DDR CK max number */ ++#define DDR_CK_NUM_LPDDR4 2 /* LPDDR4 CK number */ ++#define DDR_CK_NUM_NONLPDDR4 1 /* NONLPDDR4 CK number */ ++#define DDR_DUTY_CTL_NUM 2 /* CK duty has two control direction */ ++/* CK duty step. */ ++#define PHY_AC_IOCTL21_STEP 1 ++#define DDR_DCC_CTL_WIN_DIFF 2 ++ ++#define PHY_MRS_SEQ_PROG_VAL 0x05555555 ++#define PHY_WDM_DISABLE_VAL 0x00004000 ++ ++/* ++ * DDR_BDL_PHASE_REL Calculation Method: ++ * 1. Calculation How many picosecond to one phase. ++ * PICOSECOND : 1 second is (1000 * 1000 * 1000) picosecond ++ * WAVE : 1 cycle is 2 ++ * RATE : DDR rate is 1600 Mbps, is (1600 * 1000) bps ++ * PHASE : 1 wave is 12 phase ++ * phase equal (((PICOSECOND * WAVE) / RATE) / PHASE) ++ * = (((1000 * 1000 * 1000 * 2) / (1600 * 1000)) / 12) ++ * = 104.17 ps. ++ * 2. Calculation How many bdl to one phase. ++ * one BDL is 6 ps. ++ * result = phase/bdl = 104.17 / 6 = 17.36 approximately equal to 17 ~= 16 ++ * 3. 16 = 1 << 4, so the relation is 4. ++ */ ++#ifndef DDR_BDL_PHASE_TRANSFORM ++/* [CUSTOM] one Phase equal how much BDL. 1 phase = 16 bdl */ ++#define DDR_BDL_PHASE_TRANSFORM 16 ++#endif ++#ifndef DDR_BDL_PHASE_REL ++/* [CUSTOM] relation between BDL and Phase. 1 phase = 16 bdl, 16 = 1 << 4 */ ++#define DDR_BDL_PHASE_REL 4 ++#endif ++ ++#define ddr_variable_declare(var) \ ++ unsigned int var; ++ ++#define ddr_vref_get_host_max(rank, val) do { \ ++ if ((rank) == 0) \ ++ (val) = PHY_VRFTRES_HVREF_MASK; \ ++ else \ ++ (val) = PHY_VRFTRES_RXDIFFCAL_MASK; \ ++} while (0) ++ ++/* PHY t28 all byte use a same value */ ++#define ddr_phy_vref_host_set(base_phy, rank, bytenum, byte_index, val) do { \ ++ unsigned int hvreft; \ ++ hvreft = reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) & \ ++ (~PHY_VRFTRES_HVREF_MASK); \ ++ reg_write(hvreft | (val), (base_phy) + ddr_phy_hvreft_status(rank, byte_index)); \ ++ reg_write(hvreft | (val), (base_phy) + ddr_phy_hvreft_status(rank, (byte_index) + 1)); \ ++} while (0) ++ ++#define ddr_phy_vref_host_get(base_phy, rank, byte_index, val) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) & \ ++ PHY_VRFTRES_HVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_HOST_DISPLAY \ ++ {0, 0, ddr_phy_hvreft_status(0, 0), 0, "Host Vref Byte0"}, \ ++ {0, 1, ddr_phy_hvreft_status(0, 1), 0, "Host Vref Byte1"}, \ ++ {0, 2, ddr_phy_hvreft_status(0, 2), 0, "Host Vref Byte2"}, \ ++ {0, 3, ddr_phy_hvreft_status(0, 3), 0, "Host Vref Byte3"}, ++ ++#define DDR_PHY_VREF_HOST_DISPLAY_RANK1 \ ++ {1, 0, ddr_phy_hvreft_status(1, 0), 0, "Host Vref Byte0"}, \ ++ {1, 1, ddr_phy_hvreft_status(1, 1), 0, "Host Vref Byte1"}, \ ++ {1, 2, ddr_phy_hvreft_status(1, 2), 0, "Host Vref Byte2"}, \ ++ {1, 3, ddr_phy_hvreft_status(1, 3), 0, "Host Vref Byte3"}, ++ ++#define ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] Host Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_hvreft_status(rank, _i), \ ++ reg_read((base_phy) + ddr_phy_hvreft_status(rank, _i)), _i); \ ++ } \ ++} while (0) ++ ++/* DRAM vref operations */ ++#define ddr_phy_vref_dram_set(base_phy, val, byte_index) \ ++ ddr_vref_dram_set_process(base_phy, val, byte_index) ++ ++#define ddr_phy_vref_dram_get(base_phy, val, byte_index) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_dvreft_status(byte_index)) & \ ++ PHY_VRFTRES_DVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_DRAM_DISPLAY \ ++ {0, 0, ddr_phy_dvreft_status(0), 0, "DRAM Vref Byte0"}, \ ++ {0, 1, ddr_phy_dvreft_status(1), 0, "DRAM Vref Byte1"}, \ ++ {0, 2, ddr_phy_dvreft_status(2), 0, "DRAM Vref Byte2"}, \ ++ {0, 3, ddr_phy_dvreft_status(3), 0, "DRAM Vref Byte3"}, ++ ++#define ddr_phy_vref_dram_display_cmd(base_phy, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] DRAM Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_dvreft_status(_i), \ ++ reg_read((base_phy) + ddr_phy_dvreft_status(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* Dx dpmc operations */ ++#define DDR_DX_DPMC_DISPLAY \ ++ {0, 0, dx_dxnmiscctrl3(0), 0, "Dpmc Byte0"}, \ ++ {0, 1, dx_dxnmiscctrl3(1), 0, "Dpmc Byte1"}, \ ++ {0, 2, dx_dxnmiscctrl3(2), 0, "Dpmc Byte2"}, \ ++ {0, 3, dx_dxnmiscctrl3(3), 0, "Dpmc Byte3"}, ++ ++#define ddr_dx_dpmc_display_cmd(base_phy, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] Dpmc Byte(%x)", \ ++ (base_phy) + dx_dxnmiscctrl3(_i), \ ++ reg_read((base_phy) + dx_dxnmiscctrl3(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* phy t28 not support DCC training */ ++#define DDR_PHY_DCC_DISPLAY ++#define ddr_phy_dcc_display_cmd(base_phy) ++ ++/* lowpower ddr ca operations */ ++#define DDR_PHY_ADDRPH_DISPLAY \ ++ {0, 0, DDR_PHY_ADDRPHBOUND, 0, "CA Phase"}, ++ ++#define DDR_PHY_ADDRBDL_DISPLAY \ ++ {0, 0, ddr_phy_acaddrbdl(0), 0, "CA BDL(0)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(1), 0, "CA BDL(1)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(2), 0, "CA BDL(2)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(3), 0, "CA BDL(3)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(4), 0, "CA BDL(4)"}, ++ ++#define ddr_phy_addrph_display_cmd(base_phy) do { \ ++ ddr_info("[%x = %x] CA Phase", (base_phy) + DDR_PHY_ADDRPHBOUND, \ ++ reg_read((base_phy) + DDR_PHY_ADDRPHBOUND)); \ ++} while (0) ++ ++#define ddr_phy_addrbdl_display_cmd(base_phy) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < DDR_PHY_CA_REG_MAX; _i++) { \ ++ ddr_info("[%x = %x] ACADDRBDL(%x)", \ ++ (base_phy) + ddr_phy_acaddrbdl(_i), \ ++ reg_read((base_phy) + ddr_phy_acaddrbdl(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* PHY t28 DDR4 RDQS synchronize to RDM */ ++#define ddr_phy_rdqs_sync_rdm(cfg, val) \ ++ ddr_rdqs_sync(cfg, val) ++ ++/* dqs swap */ ++#define ddr_dqsswap_save_func(swapdfibyte_en, base_phy) do { \ ++ (swapdfibyte_en) = reg_read((base_phy) + DDR_PHY_DMSEL); \ ++ reg_write((swapdfibyte_en) & PHY_DMSEL_SWAPDFIBYTE, \ ++ (base_phy) + DDR_PHY_DMSEL); \ ++} while (0) ++ ++#define ddr_dqsswap_restore_func(swapdfibyte_en, base_phy) \ ++ reg_write(swapdfibyte_en, (base_phy) + DDR_PHY_DMSEL); ++ ++#define ddr_phy_switch_rank(base_phy, val) do { \ ++ reg_write((reg_read((base_phy) + DDR_PHY_TRAINCTRL0) & (~PHY_TRAINCTRL0_MASK)) | (val), \ ++ (base_phy) + DDR_PHY_TRAINCTRL0); \ ++} while (0) ++ ++/* Define the union u_phy_catconfig */ ++union u_phy_catconfig { ++ /* Define the struct bits */ ++ struct { ++ unsigned int ca_samp_num_bdl : 4; /* [3:0] */ ++ unsigned int ca_samp_num_ph : 4; /* [7:4] */ ++ unsigned int ca_trysamp_num : 4; /* [11:8] */ ++ unsigned int cat_rb_backtap : 4; /* [15:12] */ ++ unsigned int reserved : 1; /* [16] */ ++ unsigned int cat_openeye_en : 1; /* [17] */ ++ unsigned int cat_cat_phydq_sel : 1; /* [18] */ ++ unsigned int cat_restore_en : 1; /* [19] */ ++ unsigned int cat_lb_backtap : 4; /* [23:20] */ ++ unsigned int sw_cat_mrw42 : 1; /* [24] */ ++ unsigned int sw_cat_mrw48 : 1; /* [25] */ ++ unsigned int sw_cat_mrw41 : 1; /* [26] */ ++ unsigned int sw_cat_strobe : 1; /* [27] */ ++ unsigned int sw_cat_cke_high : 1; /* [28] */ ++ unsigned int sw_cat_cke_low : 1; /* [29] */ ++ unsigned int sw_cat_dqvalid : 1; /* [30] */ ++ unsigned int sw_cat_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++ ++/* Define the union u_phy_addrphbound */ ++union u_phy_addrphbound { ++ /* Define the struct bits */ ++ struct { ++ unsigned int addrph_a_right : 5; /* [4:0] */ ++ unsigned int reserved0 : 3; /* [7:5] */ ++ unsigned int addrph_a_left : 5; /* [12:8] */ ++ unsigned int reserved1 : 3; /* [15:13] */ ++ unsigned int addrph_a : 5; /* [20:16] */ ++ unsigned int reserved2 : 3; /* [23:21] */ ++ unsigned int addrph_a_ori : 5; /* [28:24] */ ++ unsigned int reserved3 : 3; /* [31:29] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++#endif /* DDR_PHY_S28_V300_H */ +diff --git a/drivers/ddr/vendor/default/ddr_phy_s40.h b/drivers/ddr/vendor/default/ddr_phy_s40.h +new file mode 100644 +index 0000000..3142719 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_phy_s40.h +@@ -0,0 +1,333 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_PHY_S40_H ++#define DDR_PHY_S40_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_PHY0 DDR_REG_BASE_PHY1 */ ++/* control the initialization of the PHY */ ++#define DDR_PHY_PHYINITCTRL 0x4 ++#define DDR_PHY_PHYINITSTATUS 0x8 /* Read Data Eye Calibration Error */ ++#define DDR_PHY_IMPSTATUS 0x28 /* This register specify the ZQ calibration result. */ ++#define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ ++#define DDR_PHY_DRAMTIMER1 0x34 /* This register specify the DRAM timing parameters */ ++#define DDR_PHY_TRAINCTRL0 0x48 /* hw training control */ ++#define DDR_PHY_MODEREG01 0x64 /* Extend Mode Register 01 */ ++#define DDR_PHY_MODEREG23 0x68 /* Extend Mode Register 23 */ ++/* update delay setting in registers to PHY */ ++#define DDR_PHY_MISC 0x70 ++#define DDR_PHY_SWTMODE 0xa0 /* S/W training mode */ ++/* issue one DQS pulse from PHY to DRAM */ ++#define DDR_PHY_SWTWLDQS 0xa4 ++#define DDR_PHY_SWTRLT 0xa8 /* S/W training result */ ++/* Host vref. [5:0]range [17:12]refsel */ ++#define DDR_PHY_IOCTL2 0xB4 ++#define DDR_PHY_VREFTCTRL 0xc0 /* VREF Training Control Register. */ ++#define DDR_PHY_MODEREG45 0xe0 /* defines the contents of the Mode Register */ ++#define DDR_PHY_MODEREG67 0xe4 /* This register defines the contents of the Mode Register */ ++#define DDR_PHY_DVRFTCTRL 0xC4 /* DRAM VREF Training */ ++/* AC command bit delay line setting */ ++#define DDR_PHY_ACCMDBDL2 0x128 ++#define DDR_PHY_IMP_CTRL1 0x170 /* AC/DX ZQ selection */ ++#define DDR_PHY_IMP_STATUS1 0x174 /* AC ZCAL status */ ++#define DDR_PHY_ACPHYCTL4 0x180 /* AC block PHY control register */ ++/* AC block PHY control register */ ++#define DDR_PHY_ACPHYCTL7 0x18C ++ ++/* WR DQ0-DQ3 [4:0] [12:8] [20:16] [28:24] delay value of the bit delay line ++ on write path */ ++#define ddr_phy_dxnwdqnbdl0(m, n) (0x210 + ((m) << 10) + ((n) << 7)) ++/* WR DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] */ ++#define ddr_phy_dxnwdqnbdl1(m, n) (0x214 + ((m) << 10) + ((n) << 7)) ++/* WR DM [6:0] the delay value of the bit delay line on DQM */ ++#define ddr_phy_dxnwdqnbdl2(m, n) (0x218 + ((m) << 10) + ((n) << 7)) ++/* RD DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl0(m, n) (0x21C + ((m) << 10) + ((n) << 7)) ++/* RD DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl1(m, n) (0x220 + ((m) << 10) + ((n) << 7)) ++/* [6:0]RD DM */ ++#define ddr_phy_dxnrdqnbdl2(m, n) (0x224 + ((m) << 10) + ((n) << 7)) ++ ++/* [CUSTOM] */ ++#define ddr_phy_dxnoebdl(m, n) (0x228 + ((m) << 10) + ((n) << 7)) ++/* [8:0] rdqs_bdl [24:16]rdqs_cyc. ++phase shift of the Read DQS to create 90 degree delays */ ++#define ddr_phy_dxnrdqsdly(n) (0x22C + ((n) << 7)) ++/* [6:0] the delay value of delay applied on WDQS for write leveling */ ++#define ddr_phy_dxwdqsdly(m, n) (0x230 + ((m) << 10) + ((n) << 7)) ++/* WR DQ phase BIT 12:8 */ ++#define ddr_phy_dxnwdqdly(m, n) (0x234 + ((m) << 10) + ((n) << 7)) ++/* [CUSTOM] rddqs gating */ ++#define ddr_phy_dxnrdqsgdly(m, n) (0x240 + ((m) << 10) + ((n) << 7)) ++/* read boundary right 8:0 left 24:16 */ ++#define ddr_phy_dxnrdbound(n) (0x250 + ((n) << 7)) ++/* write boundary right 4:0 left 20:16 */ ++#define ddr_phy_dxnwdbound(n) (0x254 + ((n) << 7)) ++ ++#define DDR_PHY_ACIOCTL 0x1018 /* IO control register */ ++#define DDR_PHY_CORNER_DETECTOR 0x104C /* cfg of corner detector */ ++ ++#define DDR_VREF_HOST_VAL_MAX 0x1f /* 78.75%*VDDIO */ ++#define DDR_VREF_HOST_VAL_MIN 0x0 /* 40.00%*VDDIO */ ++ ++/* register mask */ ++#define PHY_BDL_MASK 0x1f /* [4:0] */ ++#define PHY_WDQS_PHASE_MASK 0xf /* [11:8] */ ++#define PHY_RDQS_BDL_MASK 0x7f /* [CUSTOM] [6:0] rdqsbdl */ ++#define PHY_RDQSG_PHASE_MASK 0x3f /* [14:9] rdqsgphase */ ++#define PHY_RDM_BDL_MASK 0x7f /* [6:0] */ ++/* hardware gate training result */ ++#define PHY_INITSTATUS_GT_MASK 0x20 ++#define PHY_SWTRLT_WL_MASK 0xf ++#define PHY_SWTRLT_GATE_MASK 0xf ++#define PHY_WDQ_PHASE_MASK 0x1f ++#define PHY_PHYINITCTRL_MASK 0xffff /* [15:0] all stat */ ++/* Read Data Eye Calibration Error */ ++#define PHY_PHYINITSTATUS_RDET_ERR 0x100 ++#define PHY_ACPHY_DCLK_MASK 0x7 /* cp1p_dclk0 mask */ ++#define PHY_ACPHY_DRAMCLK_MASK 0x1 /* halft_dramclk0 mask */ ++#define PHY_VRFTRES_HVREF_MASK 0x1f /* [4:0] */ ++#define PHY_VRFTRES_RXDIFFCAL_MASK 0xf /* [24:21] */ ++#define PHY_ACADDR_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ ++#define PHY_VREFS_MRS_ENTER_MASK 0x1 /* [31] */ ++#define PHY_TRAINCTRL0_MASK 0xf /* [3:0] */ ++#define PHY_OSC_START_MASK 0x1 /* [0] */ ++#define PHY_OSC_RPT_VLD_MASK 0x1 /* [15] */ ++#define PHY_OSC_CNT_RDATA_MASK 0xffff /* [31:16] */ ++#define PHY_ZCODE_PDRV_MASK 0x3f /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_MASK 0x3f /* [29:24] */ ++#define DDR_PHY_T_MOD_MASK 0x1f /* bit[8:4]t_mod */ ++#define PHY_AC_IOCTL_TX_MODE_MASK 0x3 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* register bit */ ++#define PHY_MISC_UPDATE_BIT 20 /* [CUSTOM] delay config update bit */ ++#define PHY_PHYCONN_RST_BIT 15 /* issue reset signal to PHY counter */ ++#define PHY_RST_BIT 13 /* bit[13] issue reset signal to PHY */ ++#define PHY_RDQSG_PHASE_BIT 8 /* [CUSTOM] */ ++#define PHY_RDQSG_TX_BDL_BIT 16 /* [22:16] rdqsgtxbdl */ ++#define PHY_WDQS_PHASE_BIT 8 ++#define PHY_WDQS_BDL_BIT 0 ++#define PHY_WDQ_PHASE_BIT 8 ++#define PHY_WDM_BDL_BIT 0 ++/* [20:16] Write DQS Output Enable Delay Control */ ++#define PHY_WDQSOE_BDL_BIT 16 ++#define PHY_OEN_BDL_BIT 0 ++/* Mode Register 1. Defines the MR3/MR9 of the mode register */ ++#define PHY_MODEREG01_MR1_BIT 16 ++#define PHY_ACADDRBDL_ADDR1_BIT 16 /* [16] ADDR1 delay line */ ++/* Bit delay line setting of CS1 */ ++#define PHY_ACCMD_CS0_BIT 0 ++#define PHY_ACCMD_CS1_BIT 16 ++#define PHY_ACPHY_DCLK0_BIT 6 /* [8:6] cp1p_dclk0 */ ++#define PHY_ACPHY_DCLK1_BIT 9 /* [11:9] ck2p_dclk1 */ ++#define PHY_ACPHY_DRAMCLK0_BIT 25 /* [25] halft_dramclk0 */ ++#define PHY_ACPHY_DRAMCLK1_BIT 24 /* [24] halft_dramclk1 */ ++#define PHY_ACPHY_DRAMCLK_EXT_BIT 3 /* [3] halft_dramclk0 */ ++#define PHY_SWTMODE_SW_GTMODE_BIT 1 /* [1] SW gate training */ ++#define PHY_VREFS_MRS_ENTER_BIT 31 /* [31] */ ++#define PHY_OSC_RPT_VLD 15 /* [15] */ ++#define PHY_OSC_CNT_RDATA_BIT 16 /* [31:16] */ ++#define PHY_ZCODE_PDRV_BIT 16 /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_BIT 24 /* [29:24] */ ++#define PHY_AC_VDDQ_CAL_EN_BIT 8 /* [8] AC ZQ calibration enable */ ++#define PHY_CFG_RX_AGE_COMPST_EN_BIT 31 /* Enable rdqs age compensation function */ ++#define DDR_PHY_T_MOD_BIT 4 /* bit[8:4]t_mod */ ++#define PHY_MODEREG67_LP4_FSPWR_BIT 6 /* bit[6] FSPWR */ ++#define PHY_AC_IOCTL_TX_MODE_BIT 12 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* BDL register bit */ ++#define PHY_BDL_DQ_BIT 0 ++#define PHY_BDL_DQ0_BIT 0 ++#define PHY_BDL_DQ1_BIT 8 ++#define PHY_BDL_DQ2_BIT 16 ++#define PHY_BDL_DQ3_BIT 24 ++#define PHY_RDM_BDL_BIT 0 ++#define PHY_RDQS_BDL_BIT 0 ++ ++/* value */ ++#define PHY_PHYINITCTRL_DVREFT_SYNC 0x40000 /* DRAM VREF Synchronize */ ++/* hw training item defined in PHYINITCTRL */ ++#define PHY_PHYINITCTRL_CTL_CKE_BYPASS (1 << 31) /* PACK's CKE bypass function enable. */ ++#define PHY_PHYINITCTRL_PIC_PHYUPD_REQ (1 << 30) /* PACK's DFI PHY UPDATAE request by SW. */ ++#define PHY_PHYINITCTRL_PIC_TDQSST (1 << 29) /* TDQSS training Enable. */ ++#define PHY_PHYINITCTRL_LP4_CHB_DIS (1 << 28) /* LPDDR4 channel-B disable. */ ++#define PHY_PHYINITCTRL_LP4_CHA_DIS (1 << 27) /* LPDDR4 channel-A disable. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_SFT (1 << 26) /* Update delay line(switch op_sel) during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_WR (1 << 25) /* Retraining with MPC write during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_RD (1 << 24) /* Retraining with MPC read during tRFC. */ ++#define PHY_PHYINITCTRL_JTMT_EN (1 << 23) /* PLL Jitter Meter Enable. */ ++#define PHY_PHYINITCTRL_CST_EN (1 << 22) /* HW CS Traninig Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFS_EN (1 << 21) /* DRAM VREF(AC) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_ACHVREFT_EN (1 << 20) /* Host VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFT_EN (1 << 19) /* DRAM VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_DXDVREFS_EN (1 << 18) /* DRAM VREF(DQ) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_HVREFT_EN (1 << 17) /* Host VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_DVREFT_EN (1 << 16) /* DRAM VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_PHYCONN_RST (1 << 15) /* PHY Counter Reset. */ ++#define PHY_PHYINITCTRL_PACK_RST (1 << 14) /* PACK Reset. */ ++#define PHY_PHYINITCTRL_PHY_RST (1 << 13) /* PHY Reset. */ ++#define PHY_PHYINITCTRL_DRAM_RST (1 << 12) /* DRAM Reset. */ ++#define PHY_PHYINITCTRL_CAT_EN (1 << 11) /* HW CA Traninig Enable. */ ++#define PHY_PHYINITCTRL_DRAM_INIT_EN (1 << 10) /* DRAM Initialization Enable. */ ++#define PHY_PHYINITCTRL_WDET_EN (1 << 9) /* Write Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_RDET_EN (1 << 8) /* Read Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_WL2_EN (1 << 7) /* Second Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_GDST_EN (1 << 6) /* PHY Read Data Latch Train Enable. */ ++#define PHY_PHYINITCTRL_GT_EN (1 << 5) /* Gate Training Enable. */ ++#define PHY_PHYINITCTRL_WL_EN (1 << 4) /* Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_ZCAL_EN (1 << 3) /* Impedance Calibration Enable. */ ++#define PHY_PHYINITCTRL_DLYMEAS_EN (1 << 2) /* Delay Measurement Enable. */ ++#define PHY_PHYINITCTRL_PLL_INIT_EN (1 << 1) /* PLL Initialization Enable. */ ++#define PHY_PHYINITCTRL_INIT_EN (1 << 0) /* PHY Initialization Enable. */ ++ ++#define PHY_HW_GP_PHY_RESET (PHY_PHYINITCTRL_PHY_RST) ++#define PHY_HW_GP_CNT_RESET_START (PHY_PHYINITCTRL_PHYCONN_RST) ++#define PHY_HW_GP_PLL (PHY_PHYINITCTRL_PLL_INIT_EN | PHY_PHYINITCTRL_ZCAL_EN | PHY_PHYINITCTRL_DLYMEAS_EN) ++#define PHY_HW_GP_DRAM_RESET (PHY_PHYINITCTRL_DRAM_RST | PHY_PHYINITCTRL_DRAM_INIT_EN) ++#define PHY_HW_GP_VREF_AC (PHY_PHYINITCTRL_ACDVREFS_EN) ++#define PHY_HW_GP_CS (PHY_PHYINITCTRL_CST_EN) ++#define PHY_HW_GP_VREF_DQ (PHY_PHYINITCTRL_DVREFT_SYNC) ++#define PHY_HW_GP_NORMAL (PHY_PHYINITCTRL_WL_EN | \ ++ PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN | \ ++ PHY_PHYINITCTRL_DVREFT_EN | \ ++ PHY_PHYINITCTRL_HVREFT_EN | \ ++ PHY_PHYINITCTRL_PIC_TDQSST) ++#define PHY_HW_GP_CNT_RESET_END (PHY_PHYINITCTRL_PHYCONN_RST) ++ ++/* RDQS range[0, 0x1f], middle value is 0x10 */ ++#define PHY_RDQS_MIDDLE_VAL 0x10 ++/* DQ range[0, 0x1f], middle value is 0x10 */ ++#define PHY_DQ_MIDDLE_VAL 0x10101010 ++#define PHY_MISC_SCRAMB_DIS 0xfffeffff /* scrambler disable */ ++/* NOTE: rdqsg_bdl is [5:0] in register manual, actual use [4:0] */ ++#define PHY_GATE_BDL_MAX 0x40 /* [4:0]rdqsg_bdl + [20:16]rdqsgtxbdl */ ++#define PHY_DVRFTCTRL_PDAEN_EN 0x80000000 /* pda enable */ ++/* [5] two cycle on address or command.(2T timing) */ ++#define PHY_DRAMCFG_MA2T 0x20 ++ ++#define PHY_DRAMCFG_TYPE_DDR1 0x0 /* [2:0] 000 DDR1 */ ++#define PHY_DRAMCFG_TYPE_DDR2 0x1 /* [2:0] 001 DDR2 */ ++#define PHY_DRAMCFG_TYPE_DDR3 0x2 /* [2:0] 010 DDR3 */ ++#define PHY_DRAMCFG_TYPE_DDR3L 0x3 /* [2:0] 011 DDR3L */ ++#define PHY_DRAMCFG_TYPE_LPDDR1 0x4 /* [2:0] 100 LPDDR1 */ ++#define PHY_DRAMCFG_TYPE_LPDDR2 0x5 /* [2:0] 101 LPDDR2 */ ++#define PHY_DRAMCFG_TYPE_LPDDR3 0x5 /* [2:0] 101 LPDDR3 */ ++#define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ ++#define PHY_DRAMCFG_TYPE_DDR4 0xa /* [3] 1010 DDR4 */ ++ ++/* other */ ++#define PHY_RDQSG_PHASE_STEP 4 /* gate training phase step. */ ++#define PHY_GATE_PHASE_MARGIN 8 /* gate phase margin */ ++#define PHY_DQ_BDL_LEVEL 32 /* [CUSTOM] DQ BDL range */ ++#define PHY_DQ_BDL_MIDDLE 15 /* middle DQ BDL value */ ++#define PHY_RDQSG_PHASE_MAX 0x3c /* RDQSG phase max value */ ++#define PHY_ACPHY_CLK_MAX 0xf /* halft_dramclk0 + cp1p_dclk0 */ ++#define PHY_PCODE_MIN 0x14 ++#define PHY_PCODE_MAX 0x24 ++/* ++ * DDR_BDL_PHASE_REL Calculation Method: ++ * 1. Calculation How many picosecond to one phase. ++ * PICOSECOND : 1 second is (1000 * 1000 * 1000) picosecond ++ * WAVE : 1 cycle is 2 ++ * RATE : DDR rate is 1600 Mbps, is (1600 * 1000) bps ++ * PHASE : 1 wave is 16 phase ++ * phase equal (((PICOSECOND * WAVE) / RATE) / PHASE) ++ * = (((1000 * 1000 * 1000 * 2) / (1600 * 1000)) / 16) ++ * = 78.125 ps. ++ * 2. Calculation How many bdl to one phase. ++ * one BDL is 20 ps. ++ * result = phase/bdl = 78.125 / 20 = 3.9 approximately equal to 4 ++ * 3. 4 = 1 << 2, so the relation is 2. ++ */ ++#ifndef DDR_BDL_PHASE_TRANSFORM ++/* [CUSTOM] one Phase equal how much BDL. 1 phase = 4 bdl */ ++#define DDR_BDL_PHASE_TRANSFORM 4 ++#endif ++#ifndef DDR_BDL_PHASE_REL ++/* [CUSTOM] relation between BDL and Phase. 1 phase = 4 bdl, 4 = 1 << 2 */ ++#define DDR_BDL_PHASE_REL 2 ++#endif ++ ++#define ddr_variable_declare(var) ++ ++#define ddr_vref_get_host_max(rank, val) do { \ ++ if ((rank) == 0) \ ++ (val) = PHY_VRFTRES_HVREF_MASK; \ ++ else \ ++ (val) = PHY_VRFTRES_RXDIFFCAL_MASK; \ ++} while (0) ++ ++#define ddr_phy_vref_host_set(base_phy, rank, bytenum, byte_index, val) \ ++ reg_write((((val) & 0x7) << 12) | (((val) >> 3) & 0x3), \ ++ (base_phy) + DDR_PHY_IOCTL2) ++ ++#define ddr_phy_vref_host_get(base_phy, rank, byte_index, val) do { \ ++ unsigned int ref_range; \ ++ unsigned int ref_sel; \ ++ (val) = reg_read((base_phy) + DDR_PHY_IOCTL2); \ ++ ref_range = (val) & 0x3; \ ++ ref_sel = ((val) >> 12) & 0x7; \ ++ (val) = (ref_range << 3) | ref_sel; \ ++} while (0) ++ ++#define DDR_PHY_VREF_HOST_DISPLAY \ ++ {0, 0, DDR_PHY_IOCTL2, 0, "Host Vref"}, ++ ++#define DDR_PHY_VREF_HOST_DISPLAY_RANK1 ++ ++#define ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num) \ ++ DDR_INFO("[%x = %x] Host Vref", \ ++ (base_phy) + DDR_PHY_IOCTL2, \ ++ reg_read((base_phy) + DDR_PHY_IOCTL2)); ++ ++/* phy s40 not support DRAM vref */ ++#define ddr_phy_vref_dram_set(base_phy, val, byte_index) ++#define ddr_phy_vref_dram_get(base_phy, val, byte_index) ++#define DDR_PHY_VREF_DRAM_DISPLAY ++#define ddr_phy_vref_dram_display_cmd(base_phy, byte_num) ++#define DDR_DX_DPMC_DISPLAY ++#define ddr_dx_dpmc_display_cmd(base_phy, byte_num) ++/* phy s40 not support DCC training */ ++#define DDR_PHY_DCC_DISPLAY ++#define ddr_phy_dcc_display_cmd(base_phy) ++/* phy s40 not support Lowpower ddr ca */ ++#define DDR_PHY_ADDRPH_DISPLAY ++#define DDR_PHY_ADDRBDL_DISPLAY ++#define ddr_phy_addrph_display_cmd(base_phy) ++#define ddr_phy_addrbdl_display_cmd(base_phy) ++ ++/* phy s40 not support DDR4 */ ++#define ddr_phy_rdqs_sync_rdm(cfg, val) ++ ++/* phy s40 not support dqs swap */ ++#define ddr_dqsswap_save_func(swapdfibyte_en, base_phy) ++#define ddr_dqsswap_restore_func(swapdfibyte_en, base_phy) ++ ++/* phy s40 not support rank switch */ ++#define ddr_phy_switch_rank(base_phy, val) ++ ++#endif /* DDR_PHY_S40_H */ +diff --git a/drivers/ddr/vendor/default/ddr_phy_t12_v100.h b/drivers/ddr/vendor/default/ddr_phy_t12_v100.h +new file mode 100644 +index 0000000..cfbd057 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_phy_t12_v100.h +@@ -0,0 +1,479 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_PHY_T12_V100_H ++#define DDR_PHY_T12_V100_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_PHY0 DDR_REG_BASE_PHY1 */ ++/* control the initialization of the PHY */ ++#define DDR_PHY_PHYINITCTRL 0x4 ++#define DDR_PHY_PHYINITSTATUS 0x8 /* Read Data Eye Calibration Error */ ++#define DDR_PHY_IMPSTATUS 0x28 /* This register specify the ZQ calibration result. */ ++#define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ ++#define DDR_PHY_DRAMTIMER1 0x34 /* This register specify the DRAM timing parameters */ ++#define DDR_PHY_TRAINCTRL0 0x48 /* hw training control */ ++#define DDR_PHY_MODEREG01 0x64 /* Extend Mode Register 01 */ ++#define DDR_PHY_MODEREG23 0x68 /* Extend Mode Register 23 */ ++/* update delay setting in registers to PHY */ ++#define DDR_PHY_MISC 0x70 ++#define DDR_PHY_DMSEL 0x84 /* DM Swap Selection */ ++#define DDR_PHY_SWTMODE 0xa0 /* S/W training mode */ ++/* issue one DQS pulse from PHY to DRAM */ ++#define DDR_PHY_SWTWLDQS 0xa4 ++#define DDR_PHY_SWTRLT 0xa8 /* S/W training result */ ++/* Host vref. [5:0]range [17:12]refsel */ ++#define DDR_PHY_IOCTL2 0xB4 ++#define DDR_PHY_VREFTCTRL 0xc0 /* VREF Training Control Register. */ ++#define DDR_PHY_HVRFTCTRL 0xc8 /* Host VREF Training Control Regiser. */ ++#define DDR_PHY_DVRFTCTRL 0xC4 /* DRAM VREF Training */ ++#define DDR_PHY_MODEREG45 0xe0 /* defines the contents of the Mode Register */ ++#define DDR_PHY_MODEREG67 0xe4 /* This register defines the contents of the Mode Register */ ++#define ddr_phy_acaddrbdl(n) (0x140 + ((n) << 2)) ++#define DDR_PHY_IMP_CTRL1 0x170 /* AC/DX ZQ selection */ ++#define DDR_PHY_IMP_STATUS1 0x174 /* AC ZCAL status */ ++#define DDR_PHY_CATSWAPINDEX 0x01B8 /* CA SWAP index register */ ++#define DDR_PHY_CATSWAPSEL 0x01BC /* CA SWAP select register */ ++#define DDR_PHY_CATCONFIG 0x1C8 /* CA Training Configuration */ ++#define DDR_PHY_PHYDQRESULT 0x1D0 /* SW CA Training DQ result from PHY */ ++#define DDR_PHY_ADDRPHBOUND 0x1D4 /* CA Training addr phase boundary */ ++#define DDR_PHY_SWCATPATTERN_P 0x1D8 /* pattern for positive CK edge */ ++#define DDR_PHY_SWCATPATTERN_N 0x1DC /* pattern for negative CK edge */ ++#define DDR_PHY_MRS_SEQ_PROG 0x1e0 /* Programmed MRS sequence in the DRAM initialization */ ++/* AC command bit delay line setting */ ++#define DDR_PHY_ACCMDBDL2 0x128 ++ ++#define ddr_phy_dxnwdqnbdl0(m, n) (0x210 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnwdqnbdl1(m, n) (0x214 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnwdqnbdl2(m, n) (0x218 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqnbdl0(m, n) (0x21C + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqnbdl1(m, n) (0x220 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqnbdl2(m, n) (0x224 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnoebdl(m, n) (0x228 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqsdly(n) (0x22C + ((n) << 7)) ++#define ddr_phy_dxwdqsdly(m, n) (0x230 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnwdqdly(m, n) (0x234 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqsgdly(m, n) (0x240 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdbound(n) (0x250 + ((n) << 7)) ++#define ddr_phy_dxnwdbound(n) (0x254 + ((n) << 7)) ++#define ddr_phy_dvreft_status(n) (0x270 + ((n) << 7)) ++#define ddr_phy_hvreft_status(m, n) (0x274 + ((m) << 10) + ((n) << 7)) ++ ++/* DDRPHY AC static register */ ++#define DDR_PHY_ACIOCTL 0x1018 /* IO control register */ ++#define DDR_PHY_CORNER_DETECTOR 0x104C /* cfg of corner detector */ ++#define DDR_PHY_ACPHYCTL4 0x1064 /* AC block PHY control register */ ++#define DDR_PHY_ACPHYCTL7 0x1070 ++#define DDR_PHY_ACIOCTL21 0x10F4 ++#define DDR_PHY_AC_GATED_BYPASS 0x10FC /* bypass clock gated function */ ++ ++/* register mask */ ++#define PHY_BDL_MASK 0x7f /* [7:1] */ ++#define PHY_WDQS_PHASE_MASK 0x1f /* [13:9] */ ++#define PHY_RDQS_BDL_MASK 0x1ff /* [CUSTOM] [9:1] rdqsbdl */ ++#define PHY_RDQSG_PHASE_MASK 0x1ff /* [18:10] rdqsgphase */ ++#define PHY_RDM_BDL_MASK 0x7f /* [7:1] */ ++/* hardware gate training result */ ++#define PHY_INITSTATUS_GT_MASK 0x20 ++#define PHY_SWTRLT_WL_MASK 0xf ++#define PHY_SWTRLT_GATE_MASK 0xf ++#define PHY_WDQ_PHASE_MASK 0x7f ++#define PHY_PHYINITCTRL_MASK 0x1 ++/* Read Data Eye Calibration Error */ ++#define PHY_PHYINITSTATUS_RDET_ERR 0x100 ++#define PHY_ACPHY_DCLK_MASK 0x7 /* cp1p_dclk0 mask */ ++#define PHY_ACPHY_DRAMCLK_MASK 0x1 /* halft_dramclk0 mask */ ++#define PHY_VRFTRES_DVREF_MASK 0x3f /* [5:0] */ ++#define PHY_VRFTRES_HVREF_MASK 0x7f /* [6:0] */ ++#define PHY_VRFTRES_RXDIFFCAL_MASK 0xf /* [24:21] */ ++#define PHY_ADDRPH_MASK 0x1f /* [20:16] */ ++#define PHY_ACADDR_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_CATSWAPSEL_BIT_MASK 0xff ++#define PHY_CAT_PATTERN_MASK 0x3ff ++#define PHY_TRAINCTRL0_MASK 0xf /* [3:0] */ ++#define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ ++#define PHY_ACIOCTL21_MASK 0x7 /* [30:28],[14:12] */ ++#define PHY_DXNRDBOUND_MASK 0x1ff /* [25:17],[9:1] */ ++#define PHY_VREFS_MRS_ENTER_MASK 0x1 /* [31] */ ++#define PHY_OSC_START_MASK 0x1 /* [0] */ ++#define PHY_OSC_RPT_VLD_MASK 0x1 /* [15] */ ++#define PHY_OSC_CNT_RDATA_MASK 0xffff /* [31:16] */ ++#define PHY_ZCODE_PDRV_MASK 0x3f /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_MASK 0x3f /* [29:24] */ ++#define DDR_PHY_T_MOD_MASK 0x1f /* bit[8:4]t_mod */ ++#define PHY_AC_IOCTL_TX_MODE_MASK 0x3 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* register bit */ ++#define PHY_MISC_UPDATE_BIT 19 /* [CUSTOM] delay config update bit */ ++#define PHY_PHYCONN_RST_BIT 15 /* issue reset signal to PHY counter */ ++#define PHY_RST_BIT 13 /* bit[13] issue reset signal to PHY */ ++#define PHY_RDQSG_PHASE_BIT 10 /* [CUSTOM] */ ++#define PHY_RDQSG_TX_BDL_BIT 16 /* [22:16] rdqsgtxbdl */ ++#define PHY_WDQS_PHASE_BIT 9 ++#define PHY_WDQS_BDL_BIT 1 ++#define PHY_WDQ_PHASE_BIT 9 ++#define PHY_WDM_BDL_BIT 1 ++/* [22:16] Write DQS Output Enable Delay Control */ ++#define PHY_WDQSOE_BDL_BIT 17 ++#define PHY_OEN_BDL_BIT 1 ++/* Mode Register 1. Defines the MR3/MR9 of the mode register */ ++#define PHY_MODEREG01_MR1_BIT 16 ++/* Bit delay line setting of CS1 */ ++#define PHY_ACCMD_CS0_BIT 1 ++#define PHY_ACCMD_CS1_BIT 17 ++#define PHY_ACPHY_DCLK0_BIT 6 /* [8:6] cp1p_dclk0 */ ++#define PHY_ACPHY_DCLK1_BIT 9 /* [11:9] ck2p_dclk1 */ ++#define PHY_ACPHY_DRAMCLK0_BIT 25 /* [25] halft_dramclk0 */ ++#define PHY_ACPHY_DRAMCLK1_BIT 24 /* [24] halft_dramclk1 */ ++#define PHY_ACPHY_DRAMCLK_EXT_BIT 3 /* [3] halft_dramclk0 */ ++#define PHY_SWTMODE_SW_GTMODE_BIT 1 /* [1] SW gate training */ ++#define PHY_ACADDRBDL_ADDR1_BIT 17 /* [17] ADDR1 delay line */ ++#define PHY_VRFTRES_RXDIFFCAL_BIT 21 /* [24:21] */ ++#define PHY_BYPASS_CK0_BIT 2 /* [2]ck_ioctl_DUTY_EN */ ++#define PHY_BYPASS_CK1_BIT 3 /* [3]ck1_ioctl_DUTY_EN */ ++#define PHY_ACIOCTL21_CK0_BIT 12 /* [14:12] */ ++#define PHY_ACIOCTL21_CK1_BIT 28 /* [30:28] */ ++#define PHY_ACIOCTL21_CTL0_BIT 15 /* [15] */ ++#define PHY_ACIOCTL21_CTL1_BIT 31 /* [31] */ ++#define PHY_DXNRDBOUND_RIGHT_BIT 1 /* [9:1] */ ++#define PHY_DXNRDBOUND_LEFT_BIT 17 /* [25:17] */ ++#define PHY_VREFS_MRS_ENTER_BIT 31 /* [31] */ ++#define PHY_HRXDIFFCAL_EN_BIT 31 /* [31] */ ++#define PHY_OSC_RPT_VLD 15 /* [15] */ ++#define PHY_OSC_CNT_RDATA_BIT 16 /* [31:16] */ ++#define PHY_ZCODE_PDRV_BIT 16 /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_BIT 24 /* [29:24] */ ++#define PHY_AC_VDDQ_CAL_EN_BIT 8 /* [8] AC ZQ calibration enable */ ++#define PHY_CFG_RX_AGE_COMPST_EN_BIT 31 /* Enable rdqs age compensation function */ ++#define DDR_PHY_T_MOD_BIT 4 /* bit[8:4]t_mod */ ++#define PHY_MODEREG67_LP4_FSPWR_BIT 6 /* bit[6] FSPWR */ ++#define PHY_AC_IOCTL_TX_MODE_BIT 12 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* BDL register bit */ ++#define PHY_BDL_DQ_BIT 1 ++#define PHY_BDL_DQ0_BIT 1 ++#define PHY_BDL_DQ1_BIT 9 ++#define PHY_BDL_DQ2_BIT 17 ++#define PHY_BDL_DQ3_BIT 25 ++#define PHY_RDM_BDL_BIT 1 ++#define PHY_RDQS_BDL_BIT 1 ++ ++/* value */ ++#define PHY_PHYINITCTRL_DVREFT_SYNC 0x40000 /* DRAM VREF Synchronize */ ++#define PHY_TRAINCTRL0_DTR_RANK0 0x0 /* Training Rank0. */ ++#define PHY_TRAINCTRL0_DTR_RANK1 0x1 /* Training Rank1. */ ++/* hw training item defined in PHYINITCTRL */ ++#define PHY_PHYINITCTRL_CTL_CKE_BYPASS (1 << 31) /* PACK's CKE bypass function enable. */ ++#define PHY_PHYINITCTRL_PIC_PHYUPD_REQ (1 << 30) /* PACK's DFI PHY UPDATAE request by SW. */ ++#define PHY_PHYINITCTRL_PIC_TDQSST (1 << 29) /* TDQSS training Enable. */ ++#define PHY_PHYINITCTRL_LP4_CHB_DIS (1 << 28) /* LPDDR4 channel-B disable. */ ++#define PHY_PHYINITCTRL_LP4_CHA_DIS (1 << 27) /* LPDDR4 channel-A disable. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_SFT (1 << 26) /* Update delay line(switch op_sel) during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_WR (1 << 25) /* Retraining with MPC write during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_RD (1 << 24) /* Retraining with MPC read during tRFC. */ ++#define PHY_PHYINITCTRL_JTMT_EN (1 << 23) /* PLL Jitter Meter Enable. */ ++#define PHY_PHYINITCTRL_CST_EN (1 << 22) /* HW CS Traninig Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFS_EN (1 << 21) /* DRAM VREF(AC) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_ACHVREFT_EN (1 << 20) /* Host VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFT_EN (1 << 19) /* DRAM VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_DXDVREFS_EN (1 << 18) /* DRAM VREF(DQ) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_HVREFT_EN (1 << 17) /* Host VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_DVREFT_EN (1 << 16) /* DRAM VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_PHYCONN_RST (1 << 15) /* PHY Counter Reset. */ ++#define PHY_PHYINITCTRL_PACK_RST (1 << 14) /* PACK Reset. */ ++#define PHY_PHYINITCTRL_PHY_RST (1 << 13) /* PHY Reset. */ ++#define PHY_PHYINITCTRL_DRAM_RST (1 << 12) /* DRAM Reset. */ ++#define PHY_PHYINITCTRL_CAT_EN (1 << 11) /* HW CA Traninig Enable. */ ++#define PHY_PHYINITCTRL_DRAM_INIT_EN (1 << 10) /* DRAM Initialization Enable. */ ++#define PHY_PHYINITCTRL_WDET_EN (1 << 9) /* Write Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_RDET_EN (1 << 8) /* Read Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_WL2_EN (1 << 7) /* Second Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_GDST_EN (1 << 6) /* PHY Read Data Latch Train Enable. */ ++#define PHY_PHYINITCTRL_GT_EN (1 << 5) /* Gate Training Enable. */ ++#define PHY_PHYINITCTRL_WL_EN (1 << 4) /* Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_ZCAL_EN (1 << 3) /* Impedance Calibration Enable. */ ++#define PHY_PHYINITCTRL_DLYMEAS_EN (1 << 2) /* Delay Measurement Enable. */ ++#define PHY_PHYINITCTRL_PLL_INIT_EN (1 << 1) /* PLL Initialization Enable. */ ++#define PHY_PHYINITCTRL_INIT_EN (1 << 0) /* PHY Initialization Enable. */ ++ ++#define PHY_HW_GP_PHY_RESET (PHY_PHYINITCTRL_PHY_RST) ++#define PHY_HW_GP_CNT_RESET_START (PHY_PHYINITCTRL_PHYCONN_RST) ++#define PHY_HW_GP_PLL (PHY_PHYINITCTRL_PLL_INIT_EN | PHY_PHYINITCTRL_ZCAL_EN | PHY_PHYINITCTRL_DLYMEAS_EN) ++#define PHY_HW_GP_DRAM_RESET (PHY_PHYINITCTRL_DRAM_RST | PHY_PHYINITCTRL_DRAM_INIT_EN) ++#define PHY_HW_GP_VREF_AC (PHY_PHYINITCTRL_ACDVREFS_EN) ++#define PHY_HW_GP_CS (PHY_PHYINITCTRL_CST_EN) ++#define PHY_HW_GP_VREF_DQ (PHY_PHYINITCTRL_DVREFT_SYNC) ++#define PHY_HW_GP_NORMAL (PHY_PHYINITCTRL_WL_EN | \ ++ PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN | \ ++ PHY_PHYINITCTRL_DVREFT_EN | \ ++ PHY_PHYINITCTRL_HVREFT_EN | \ ++ PHY_PHYINITCTRL_PIC_TDQSST) ++#define PHY_HW_GP_CNT_RESET_END (PHY_PHYINITCTRL_PHYCONN_RST) ++ ++/* RDQS range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_RDQS_MIDDLE_VAL 0x30 ++/* DQ range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_DQ_MIDDLE_VAL 0x30303030 ++#define PHY_MISC_SCRAMB_DIS 0xfffeffff /* scrambler disable */ ++#define PHY_GATE_BDL_MAX 0xfe /* [6:0]rdqsg_bdl + [22:16]rdqsgtxbdl */ ++#define PHY_DVRFTCTRL_PDAEN_EN 0x80000000 /* pda enable */ ++/* [5] two cycle on address or command.(2T timing) */ ++#define PHY_DRAMCFG_MA2T 0x20 ++ ++#define PHY_DRAMCFG_TYPE_DDR1 0x0 /* [2:0] 000 DDR1 */ ++#define PHY_DRAMCFG_TYPE_DDR2 0x1 /* [2:0] 001 DDR2 */ ++#define PHY_DRAMCFG_TYPE_DDR3 0x2 /* [2:0] 010 DDR3 */ ++#define PHY_DRAMCFG_TYPE_DDR3L 0x3 /* [2:0] 011 DDR3L */ ++#define PHY_DRAMCFG_TYPE_LPDDR1 0x4 /* [2:0] 100 LPDDR1 */ ++#define PHY_DRAMCFG_TYPE_LPDDR2 0x5 /* [2:0] 101 LPDDR2 */ ++#define PHY_DRAMCFG_TYPE_LPDDR3 0x5 /* [2:0] 101 LPDDR3 */ ++#define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ ++#define PHY_DRAMCFG_TYPE_DDR4 0xa /* [3] 1010 DDR4 */ ++ ++#define PHY_DMSEL_SWAPDFIBYTE 0xf8ffffff /* [24:26] No Swap */ ++/* other */ ++#define PHY_RDQSG_PHASE_STEP 2 /* gate training phase step. */ ++#define PHY_GATE_PHASE_MARGIN 8 /* gate phase margin */ ++#define PHY_DQ_BDL_LEVEL 128 /* [CUSTOM] DQ BDL range */ ++#define PHY_DQ_BDL_MIDDLE 64 /* special middle DQ BDL value */ ++#define PHY_RDQSG_PHASE_MAX 0x3c /* RDQSG phase max value */ ++#define PHY_ACPHY_CLK_MAX 0xf /* halft_dramclk0 + cp1p_dclk0 */ ++#define PHY_PCODE_MIN 0x14 ++#define PHY_PCODE_MAX 0x24 ++ ++/* AC_DDRPHY_GATED_BYPASS */ ++#define PHY_CK_IOCTL_DUTY_EN 0x4 /* enable ck_ioctl_DUTY_EN_v */ ++#define PHY_CK1_IOCTL_DUTY_EN 0x8 /* enable ck1_ioctl_DUTY_EN_v */ ++/* CK AC_IOCTL22 */ ++#define DDR_DUTY_NUM 8 /* CK duty number */ ++#define DDR_CK_MAX_NUM 2 /* DDR CK max number */ ++#define DDR_CK_NUM_LPDDR4 2 /* LPDDR4 CK number */ ++#define DDR_CK_NUM_NONLPDDR4 1 /* NONLPDDR4 CK number */ ++#define DDR_DUTY_CTL_NUM 2 /* CK duty has two control direction */ ++/* CK duty step. */ ++#define PHY_AC_IOCTL21_STEP 1 ++#define DDR_DCC_CTL_WIN_DIFF 2 ++ ++#define PHY_MRS_SEQ_PROG_VAL 0x05555555 ++#define PHY_WDM_DISABLE_VAL 0x00004000 ++ ++/* ++ * DDR_BDL_PHASE_REL Calculation Method: ++ * 1. Calculation How many picosecond to one phase. ++ * PICOSECOND : 1 second is (1000 * 1000 * 1000) picosecond ++ * WAVE : 1 cycle is 2 ++ * RATE : DDR rate is 1600 Mbps, is (1600 * 1000) bps ++ * PHASE : 1 wave is 12 phase ++ * phase equal (((PICOSECOND * WAVE) / RATE) / PHASE) ++ * = (((1000 * 1000 * 1000 * 2) / (1600 * 1000)) / 12) ++ * = 104.17 ps. ++ * 2. Calculation How many bdl to one phase. ++ * one BDL is 6 ps. ++ * result = phase/bdl = 104.17 / 6 = 17.36 approximately equal to 17 ~= 16 ++ * 3. 16 = 1 << 4, so the relation is 4. ++ */ ++#ifndef DDR_BDL_PHASE_TRANSFORM ++/* [CUSTOM] one Phase equal how much BDL. 1 phase = 16 bdl */ ++#define DDR_BDL_PHASE_TRANSFORM 16 ++#endif ++#ifndef DDR_BDL_PHASE_REL ++/* [CUSTOM] relation between BDL and Phase. 1 phase = 16 bdl, 16 = 1 << 4 */ ++#define DDR_BDL_PHASE_REL 4 ++#endif ++ ++#define ddr_variable_declare(var) \ ++ unsigned int var; ++ ++#define ddr_vref_get_host_max(rank, val) do { \ ++ if ((rank) == 0) \ ++ (val) = PHY_VRFTRES_HVREF_MASK; \ ++ else \ ++ (val) = PHY_VRFTRES_RXDIFFCAL_MASK; \ ++} while (0) ++ ++/* PHY t28 all byte use a same value */ ++#define ddr_phy_vref_host_set(base_phy, rank, bytenum, byte_index, val) \ ++ ddr_phy_vref_host_set_process(base_phy, rank, bytenum, byte_index, val) ++ ++#define ddr_phy_vref_host_get(base_phy, rank, byte_index, val) do { \ ++ if ((rank) == 0) { \ ++ (val) = reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) & \ ++ PHY_VRFTRES_HVREF_MASK; \ ++ } else { \ ++ (val) = (reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) >> PHY_VRFTRES_RXDIFFCAL_BIT) & \ ++ PHY_VRFTRES_RXDIFFCAL_MASK; \ ++ } \ ++} while (0) ++ ++#define DDR_PHY_VREF_HOST_DISPLAY \ ++ {0, 0, ddr_phy_hvreft_status(0, 0), 0, "Host Vref Byte0"}, \ ++ {0, 1, ddr_phy_hvreft_status(0, 1), 0, "Host Vref Byte1"}, \ ++ {0, 2, ddr_phy_hvreft_status(0, 2), 0, "Host Vref Byte2"}, \ ++ {0, 3, ddr_phy_hvreft_status(0, 3), 0, "Host Vref Byte3"}, ++ ++#define DDR_PHY_VREF_HOST_DISPLAY_RANK1 \ ++ {1, 0, ddr_phy_hvreft_status(1, 0), 0, "Host Vref Byte0"}, \ ++ {1, 1, ddr_phy_hvreft_status(1, 1), 0, "Host Vref Byte1"}, \ ++ {1, 2, ddr_phy_hvreft_status(1, 2), 0, "Host Vref Byte2"}, \ ++ {1, 3, ddr_phy_hvreft_status(1, 3), 0, "Host Vref Byte3"}, ++ ++#define ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] Host Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_hvreft_status(rank, _i), \ ++ reg_read((base_phy) + ddr_phy_hvreft_status(rank, _i)), _i); \ ++ } \ ++} while (0) ++ ++/* DRAM vref operations */ ++#define ddr_phy_vref_dram_set(base_phy, val, byte_index) \ ++ ddr_vref_dram_set_process(base_phy, val, byte_index) ++ ++#define ddr_phy_vref_dram_get(base_phy, val, byte_index) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_dvreft_status(byte_index)) & \ ++ PHY_VRFTRES_DVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_DRAM_DISPLAY \ ++ {0, 0, ddr_phy_dvreft_status(0), 0, "DRAM Vref Byte0"}, \ ++ {0, 1, ddr_phy_dvreft_status(1), 0, "DRAM Vref Byte1"}, \ ++ {0, 2, ddr_phy_dvreft_status(2), 0, "DRAM Vref Byte2"}, \ ++ {0, 3, ddr_phy_dvreft_status(3), 0, "DRAM Vref Byte3"}, ++ ++#define ddr_phy_vref_dram_display_cmd(base_phy, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] DRAM Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_dvreft_status(_i), \ ++ reg_read((base_phy) + ddr_phy_dvreft_status(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* Dx dpmc operations */ ++#define DDR_DX_DPMC_DISPLAY ++#define ddr_dx_dpmc_display_cmd(base_phy, byte_num) ++ ++#define DDR_PHY_DCC_DISPLAY \ ++ {0, 0, DDR_PHY_ACIOCTL21, 0, "CK DUTY"}, ++ ++#define ddr_phy_dcc_display_cmd(base_phy) do { \ ++ unsigned int val; \ ++ val = reg_read((base_phy) + DDR_PHY_ACIOCTL21); \ ++ val = reg_read((base_phy) + DDR_PHY_ACIOCTL21); \ ++ ddr_info("[%x = %x] DCC duty", \ ++ (base_phy) + DDR_PHY_ACIOCTL21, val); \ ++} while (0) ++ ++#define DDR_PHY_ADDRPH_DISPLAY \ ++ {0, 0, DDR_PHY_ADDRPHBOUND, 0, "CA Phase"}, ++ ++#define DDR_PHY_ADDRBDL_DISPLAY \ ++ {0, 0, ddr_phy_acaddrbdl(0), 0, "CA BDL(0)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(1), 0, "CA BDL(1)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(2), 0, "CA BDL(2)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(3), 0, "CA BDL(3)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(4), 0, "CA BDL(4)"}, ++ ++#define ddr_phy_addrph_display_cmd(base_phy) do { \ ++ ddr_info("[%x = %x] CA Phase", (base_phy) + DDR_PHY_ADDRPHBOUND, \ ++ reg_read((base_phy) + DDR_PHY_ADDRPHBOUND)); \ ++} while (0) ++ ++#define ddr_phy_addrbdl_display_cmd(base_phy) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < DDR_PHY_CA_REG_MAX; _i++) { \ ++ ddr_info("[%x = %x] ACADDRBDL(%x)", \ ++ (base_phy) + ddr_phy_acaddrbdl(_i), \ ++ reg_read((base_phy) + ddr_phy_acaddrbdl(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* PHY t28 DDR4 RDQS synchronize to RDM */ ++#define ddr_phy_rdqs_sync_rdm(cfg, val) \ ++ ddr_rdqs_sync(cfg, val) ++ ++/* dqs swap */ ++#define ddr_dqsswap_save_func(swapdfibyte_en, base_phy) do { \ ++ (swapdfibyte_en) = reg_read((base_phy) + DDR_PHY_DMSEL); \ ++ reg_write((swapdfibyte_en) & PHY_DMSEL_SWAPDFIBYTE, \ ++ (base_phy) + DDR_PHY_DMSEL); \ ++} while (0) ++ ++#define ddr_dqsswap_restore_func(swapdfibyte_en, base_phy) \ ++ reg_write(swapdfibyte_en, (base_phy) + DDR_PHY_DMSEL); ++ ++#define ddr_phy_switch_rank(base_phy, val) do { \ ++ reg_write((reg_read((base_phy) + DDR_PHY_TRAINCTRL0) & (~PHY_TRAINCTRL0_MASK)) | (val), \ ++ (base_phy) + DDR_PHY_TRAINCTRL0); \ ++ reg_write((reg_read((base_phy) + DDR_PHY_HVRFTCTRL) & (~(0x1 << PHY_HRXDIFFCAL_EN_BIT))) | \ ++ ((val) << PHY_HRXDIFFCAL_EN_BIT), (base_phy) + DDR_PHY_HVRFTCTRL); \ ++} while (0) ++ ++/* Define the union u_phy_catconfig */ ++union u_phy_catconfig { ++ /* Define the struct bits */ ++ struct { ++ unsigned int ca_samp_num_bdl : 4; /* [3:0] */ ++ unsigned int ca_samp_num_ph : 4; /* [7:4] */ ++ unsigned int ca_trysamp_num : 4; /* [11:8] */ ++ unsigned int cat_rb_backtap : 4; /* [15:12] */ ++ unsigned int reserved : 1; /* [16] */ ++ unsigned int cat_openeye_en : 1; /* [17] */ ++ unsigned int cat_cat_phydq_sel : 1; /* [18] */ ++ unsigned int cat_restore_en : 1; /* [19] */ ++ unsigned int cat_lb_backtap : 4; /* [23:20] */ ++ unsigned int sw_cat_mrw42 : 1; /* [24] */ ++ unsigned int sw_cat_mrw48 : 1; /* [25] */ ++ unsigned int sw_cat_mrw41 : 1; /* [26] */ ++ unsigned int sw_cat_strobe : 1; /* [27] */ ++ unsigned int sw_cat_cke_high : 1; /* [28] */ ++ unsigned int sw_cat_cke_low : 1; /* [29] */ ++ unsigned int sw_cat_dqvalid : 1; /* [30] */ ++ unsigned int sw_cat_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++ ++/* Define the union u_phy_addrphbound */ ++union u_phy_addrphbound { ++ /* Define the struct bits */ ++ struct { ++ unsigned int addrph_a_right : 5; /* [4:0] */ ++ unsigned int reserved0 : 3; /* [7:5] */ ++ unsigned int addrph_a_left : 5; /* [12:8] */ ++ unsigned int reserved1 : 3; /* [15:13] */ ++ unsigned int addrph_a : 5; /* [20:16] */ ++ unsigned int reserved2 : 3; /* [23:21] */ ++ unsigned int addrph_a_ori : 5; /* [28:24] */ ++ unsigned int reserved3 : 3; /* [31:29] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++#endif /* DDR_PHY_T12_V100_H */ +diff --git a/drivers/ddr/vendor/default/ddr_phy_t12_v101.h b/drivers/ddr/vendor/default/ddr_phy_t12_v101.h +new file mode 100644 +index 0000000..d11abb8 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_phy_t12_v101.h +@@ -0,0 +1,479 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_PHY_T12_V101_H ++#define DDR_PHY_T12_V101_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_PHY0 DDR_REG_BASE_PHY1 */ ++/* control the initialization of the PHY */ ++#define DDR_PHY_PHYINITCTRL 0x4 ++#define DDR_PHY_PHYINITSTATUS 0x8 /* Read Data Eye Calibration Error */ ++#define DDR_PHY_IMPSTATUS 0x28 /* This register specify the ZQ calibration result. */ ++#define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ ++#define DDR_PHY_DRAMTIMER1 0x34 /* This register specify the DRAM timing parameters */ ++#define DDR_PHY_TRAINCTRL0 0x48 /* hw training control */ ++#define DDR_PHY_MODEREG01 0x64 /* Extend Mode Register 01 */ ++#define DDR_PHY_MODEREG23 0x68 /* Extend Mode Register 23 */ ++/* update delay setting in registers to PHY */ ++#define DDR_PHY_MISC 0x70 ++#define DDR_PHY_DMSEL 0x84 /* DM Swap Selection */ ++#define DDR_PHY_SWTMODE 0xa0 /* S/W training mode */ ++/* issue one DQS pulse from PHY to DRAM */ ++#define DDR_PHY_SWTWLDQS 0xa4 ++#define DDR_PHY_SWTRLT 0xa8 /* S/W training result */ ++/* Host vref. [5:0]range [17:12]refsel */ ++#define DDR_PHY_IOCTL2 0xB4 ++#define DDR_PHY_VREFTCTRL 0xc0 /* VREF Training Control Register. */ ++#define DDR_PHY_HVRFTCTRL 0xc8 /* Host VREF Training Control Regiser. */ ++#define DDR_PHY_DVRFTCTRL 0xC4 /* DRAM VREF Training */ ++#define DDR_PHY_MODEREG45 0xe0 /* defines the contents of the Mode Register */ ++#define DDR_PHY_MODEREG67 0xe4 /* This register defines the contents of the Mode Register */ ++#define ddr_phy_acaddrbdl(n) (0x140 + ((n) << 2)) ++#define DDR_PHY_IMP_CTRL1 0x170 /* AC/DX ZQ selection */ ++#define DDR_PHY_IMP_STATUS1 0x174 /* AC ZCAL status */ ++#define DDR_PHY_CATSWAPINDEX 0x01B8 /* CA SWAP index register */ ++#define DDR_PHY_CATSWAPSEL 0x01BC /* CA SWAP select register */ ++#define DDR_PHY_CATCONFIG 0x1C8 /* CA Training Configuration */ ++#define DDR_PHY_PHYDQRESULT 0x1D0 /* SW CA Training DQ result from PHY */ ++#define DDR_PHY_ADDRPHBOUND 0x1D4 /* CA Training addr phase boundary */ ++#define DDR_PHY_SWCATPATTERN_P 0x1D8 /* pattern for positive CK edge */ ++#define DDR_PHY_SWCATPATTERN_N 0x1DC /* pattern for negative CK edge */ ++#define DDR_PHY_MRS_SEQ_PROG 0x1e0 /* Programmed MRS sequence in the DRAM initialization */ ++/* AC command bit delay line setting */ ++#define DDR_PHY_ACCMDBDL2 0x128 ++ ++#define ddr_phy_dxnwdqnbdl0(m, n) (0x210 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnwdqnbdl1(m, n) (0x214 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnwdqnbdl2(m, n) (0x218 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqnbdl0(m, n) (0x21C + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqnbdl1(m, n) (0x220 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqnbdl2(m, n) (0x224 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnoebdl(m, n) (0x228 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqsdly(n) (0x22C + ((n) << 7)) ++#define ddr_phy_dxwdqsdly(m, n) (0x230 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnwdqdly(m, n) (0x234 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdqsgdly(m, n) (0x240 + ((m) << 10) + ((n) << 7)) ++#define ddr_phy_dxnrdbound(n) (0x250 + ((n) << 7)) ++#define ddr_phy_dxnwdbound(n) (0x254 + ((n) << 7)) ++#define ddr_phy_dvreft_status(n) (0x270 + ((n) << 7)) ++#define ddr_phy_hvreft_status(m, n) (0x274 + ((m) << 10) + ((n) << 7)) ++ ++/* DDRPHY AC static register */ ++#define DDR_PHY_ACIOCTL 0x1018 /* IO control register */ ++#define DDR_PHY_CORNER_DETECTOR 0x104C /* cfg of corner detector */ ++#define DDR_PHY_ACPHYCTL4 0x1064 /* AC block PHY control register */ ++#define DDR_PHY_ACPHYCTL7 0x1070 ++#define DDR_PHY_ACIOCTL21 0x10F4 ++#define DDR_PHY_AC_GATED_BYPASS 0x10FC /* bypass clock gated function */ ++ ++/* register mask */ ++#define PHY_BDL_MASK 0x7f /* [7:1] */ ++#define PHY_WDQS_PHASE_MASK 0x1f /* [13:9] */ ++#define PHY_RDQS_BDL_MASK 0x1ff /* [CUSTOM] [9:1] rdqsbdl */ ++#define PHY_RDQSG_PHASE_MASK 0x1ff /* [18:10] rdqsgphase */ ++#define PHY_RDM_BDL_MASK 0x7f /* [7:1] */ ++/* hardware gate training result */ ++#define PHY_INITSTATUS_GT_MASK 0x20 ++#define PHY_SWTRLT_WL_MASK 0xf ++#define PHY_SWTRLT_GATE_MASK 0xf ++#define PHY_WDQ_PHASE_MASK 0x7f ++#define PHY_PHYINITCTRL_MASK 0x1 ++/* Read Data Eye Calibration Error */ ++#define PHY_PHYINITSTATUS_RDET_ERR 0x100 ++#define PHY_ACPHY_DCLK_MASK 0x7 /* cp1p_dclk0 mask */ ++#define PHY_ACPHY_DRAMCLK_MASK 0x1 /* halft_dramclk0 mask */ ++#define PHY_VRFTRES_DVREF_MASK 0x3f /* [5:0] */ ++#define PHY_VRFTRES_HVREF_MASK 0x7f /* [6:0] */ ++#define PHY_VRFTRES_RXDIFFCAL_MASK 0xf /* [24:21] */ ++#define PHY_ADDRPH_MASK 0x1f /* [20:16] */ ++#define PHY_ACADDR_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_CATSWAPSEL_BIT_MASK 0xff ++#define PHY_CAT_PATTERN_MASK 0x3ff ++#define PHY_TRAINCTRL0_MASK 0xf /* [3:0] */ ++#define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ ++#define PHY_ACIOCTL21_MASK 0xf /* [31:28],[15:12] */ ++#define PHY_DXNRDBOUND_MASK 0x1ff /* [25:17],[9:1] */ ++#define PHY_VREFS_MRS_ENTER_MASK 0x1 /* [31] */ ++#define PHY_OSC_START_MASK 0x1 /* [0] */ ++#define PHY_OSC_RPT_VLD_MASK 0x1 /* [15] */ ++#define PHY_OSC_CNT_RDATA_MASK 0xffff /* [31:16] */ ++#define PHY_ZCODE_PDRV_MASK 0x3f /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_MASK 0x3f /* [29:24] */ ++#define DDR_PHY_T_MOD_MASK 0x1f /* bit[8:4]t_mod */ ++#define PHY_AC_IOCTL_TX_MODE_MASK 0x3 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* register bit */ ++#define PHY_MISC_UPDATE_BIT 19 /* [CUSTOM] delay config update bit */ ++#define PHY_PHYCONN_RST_BIT 15 /* issue reset signal to PHY counter */ ++#define PHY_RST_BIT 13 /* bit[13] issue reset signal to PHY */ ++#define PHY_RDQSG_PHASE_BIT 10 /* [CUSTOM] */ ++#define PHY_RDQSG_TX_BDL_BIT 16 /* [22:16] rdqsgtxbdl */ ++#define PHY_WDQS_PHASE_BIT 9 ++#define PHY_WDQS_BDL_BIT 1 ++#define PHY_WDQ_PHASE_BIT 9 ++#define PHY_WDM_BDL_BIT 1 ++/* [22:16] Write DQS Output Enable Delay Control */ ++#define PHY_WDQSOE_BDL_BIT 17 ++#define PHY_OEN_BDL_BIT 1 ++/* Mode Register 1. Defines the MR3/MR9 of the mode register */ ++#define PHY_MODEREG01_MR1_BIT 16 ++/* Bit delay line setting of CS1 */ ++#define PHY_ACCMD_CS0_BIT 1 ++#define PHY_ACCMD_CS1_BIT 17 ++#define PHY_ACPHY_DCLK0_BIT 6 /* [8:6] cp1p_dclk0 */ ++#define PHY_ACPHY_DCLK1_BIT 9 /* [11:9] ck2p_dclk1 */ ++#define PHY_ACPHY_DRAMCLK0_BIT 25 /* [25] halft_dramclk0 */ ++#define PHY_ACPHY_DRAMCLK1_BIT 24 /* [24] halft_dramclk1 */ ++#define PHY_ACPHY_DRAMCLK_EXT_BIT 3 /* [3] halft_dramclk0 */ ++#define PHY_SWTMODE_SW_GTMODE_BIT 1 /* [1] SW gate training */ ++#define PHY_ACADDRBDL_ADDR1_BIT 17 /* [17] ADDR1 delay line */ ++#define PHY_VRFTRES_RXDIFFCAL_BIT 21 /* [24:21] */ ++#define PHY_BYPASS_CK0_BIT 2 /* [2]ck_ioctl_DUTY_EN */ ++#define PHY_BYPASS_CK1_BIT 3 /* [3]ck1_ioctl_DUTY_EN */ ++#define PHY_ACIOCTL21_CK0_BIT 12 /* [15:12] */ ++#define PHY_ACIOCTL21_CK1_BIT 28 /* [31:28] */ ++#define PHY_ACIOCTL21_CTL0_BIT 10 /* [10] */ ++#define PHY_ACIOCTL21_CTL1_BIT 26 /* [26] */ ++#define PHY_DXNRDBOUND_RIGHT_BIT 1 /* [9:1] */ ++#define PHY_DXNRDBOUND_LEFT_BIT 17 /* [25:17] */ ++#define PHY_VREFS_MRS_ENTER_BIT 31 /* [31] */ ++#define PHY_HRXDIFFCAL_EN_BIT 31 /* [31] */ ++#define PHY_OSC_RPT_VLD 15 /* [15] */ ++#define PHY_OSC_CNT_RDATA_BIT 16 /* [31:16] */ ++#define PHY_ZCODE_PDRV_BIT 16 /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_BIT 24 /* [29:24] */ ++#define PHY_AC_VDDQ_CAL_EN_BIT 8 /* [8] AC ZQ calibration enable */ ++#define PHY_CFG_RX_AGE_COMPST_EN_BIT 31 /* Enable rdqs age compensation function */ ++#define DDR_PHY_T_MOD_BIT 4 /* bit[8:4]t_mod */ ++#define PHY_MODEREG67_LP4_FSPWR_BIT 6 /* bit[6] FSPWR */ ++#define PHY_AC_IOCTL_TX_MODE_BIT 12 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* BDL register bit */ ++#define PHY_BDL_DQ_BIT 1 ++#define PHY_BDL_DQ0_BIT 1 ++#define PHY_BDL_DQ1_BIT 9 ++#define PHY_BDL_DQ2_BIT 17 ++#define PHY_BDL_DQ3_BIT 25 ++#define PHY_RDM_BDL_BIT 1 ++#define PHY_RDQS_BDL_BIT 1 ++ ++/* value */ ++#define PHY_PHYINITCTRL_DVREFT_SYNC 0x40000 /* DRAM VREF Synchronize */ ++#define PHY_TRAINCTRL0_DTR_RANK0 0x0 /* Training Rank0. */ ++#define PHY_TRAINCTRL0_DTR_RANK1 0x1 /* Training Rank1. */ ++/* hw training item defined in PHYINITCTRL */ ++#define PHY_PHYINITCTRL_CTL_CKE_BYPASS (1 << 31) /* PACK's CKE bypass function enable. */ ++#define PHY_PHYINITCTRL_PIC_PHYUPD_REQ (1 << 30) /* PACK's DFI PHY UPDATAE request by SW. */ ++#define PHY_PHYINITCTRL_PIC_TDQSST (1 << 29) /* TDQSS training Enable. */ ++#define PHY_PHYINITCTRL_LP4_CHB_DIS (1 << 28) /* LPDDR4 channel-B disable. */ ++#define PHY_PHYINITCTRL_LP4_CHA_DIS (1 << 27) /* LPDDR4 channel-A disable. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_SFT (1 << 26) /* Update delay line(switch op_sel) during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_WR (1 << 25) /* Retraining with MPC write during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_RD (1 << 24) /* Retraining with MPC read during tRFC. */ ++#define PHY_PHYINITCTRL_JTMT_EN (1 << 23) /* PLL Jitter Meter Enable. */ ++#define PHY_PHYINITCTRL_CST_EN (1 << 22) /* HW CS Traninig Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFS_EN (1 << 21) /* DRAM VREF(AC) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_ACHVREFT_EN (1 << 20) /* Host VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFT_EN (1 << 19) /* DRAM VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_DXDVREFS_EN (1 << 18) /* DRAM VREF(DQ) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_HVREFT_EN (1 << 17) /* Host VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_DVREFT_EN (1 << 16) /* DRAM VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_PHYCONN_RST (1 << 15) /* PHY Counter Reset. */ ++#define PHY_PHYINITCTRL_PACK_RST (1 << 14) /* PACK Reset. */ ++#define PHY_PHYINITCTRL_PHY_RST (1 << 13) /* PHY Reset. */ ++#define PHY_PHYINITCTRL_DRAM_RST (1 << 12) /* DRAM Reset. */ ++#define PHY_PHYINITCTRL_CAT_EN (1 << 11) /* HW CA Traninig Enable. */ ++#define PHY_PHYINITCTRL_DRAM_INIT_EN (1 << 10) /* DRAM Initialization Enable. */ ++#define PHY_PHYINITCTRL_WDET_EN (1 << 9) /* Write Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_RDET_EN (1 << 8) /* Read Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_WL2_EN (1 << 7) /* Second Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_GDST_EN (1 << 6) /* PHY Read Data Latch Train Enable. */ ++#define PHY_PHYINITCTRL_GT_EN (1 << 5) /* Gate Training Enable. */ ++#define PHY_PHYINITCTRL_WL_EN (1 << 4) /* Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_ZCAL_EN (1 << 3) /* Impedance Calibration Enable. */ ++#define PHY_PHYINITCTRL_DLYMEAS_EN (1 << 2) /* Delay Measurement Enable. */ ++#define PHY_PHYINITCTRL_PLL_INIT_EN (1 << 1) /* PLL Initialization Enable. */ ++#define PHY_PHYINITCTRL_INIT_EN (1 << 0) /* PHY Initialization Enable. */ ++ ++#define PHY_HW_GP_PHY_RESET (PHY_PHYINITCTRL_PHY_RST) ++#define PHY_HW_GP_CNT_RESET_START (PHY_PHYINITCTRL_PHYCONN_RST) ++#define PHY_HW_GP_PLL (PHY_PHYINITCTRL_PLL_INIT_EN | PHY_PHYINITCTRL_ZCAL_EN | PHY_PHYINITCTRL_DLYMEAS_EN) ++#define PHY_HW_GP_DRAM_RESET (PHY_PHYINITCTRL_DRAM_RST | PHY_PHYINITCTRL_DRAM_INIT_EN) ++#define PHY_HW_GP_VREF_AC (PHY_PHYINITCTRL_ACDVREFS_EN) ++#define PHY_HW_GP_CS (PHY_PHYINITCTRL_CST_EN) ++#define PHY_HW_GP_VREF_DQ (PHY_PHYINITCTRL_DVREFT_SYNC) ++#define PHY_HW_GP_NORMAL (PHY_PHYINITCTRL_WL_EN | \ ++ PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN | \ ++ PHY_PHYINITCTRL_DVREFT_EN | \ ++ PHY_PHYINITCTRL_HVREFT_EN | \ ++ PHY_PHYINITCTRL_PIC_TDQSST) ++#define PHY_HW_GP_CNT_RESET_END (PHY_PHYINITCTRL_PHYCONN_RST) ++ ++/* RDQS range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_RDQS_MIDDLE_VAL 0x30 ++/* DQ range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_DQ_MIDDLE_VAL 0x30303030 ++#define PHY_MISC_SCRAMB_DIS 0xfffeffff /* scrambler disable */ ++#define PHY_GATE_BDL_MAX 0xfe /* [6:0]rdqsg_bdl + [22:16]rdqsgtxbdl */ ++#define PHY_DVRFTCTRL_PDAEN_EN 0x80000000 /* pda enable */ ++/* [5] two cycle on address or command.(2T timing) */ ++#define PHY_DRAMCFG_MA2T 0x20 ++ ++#define PHY_DRAMCFG_TYPE_DDR1 0x0 /* [2:0] 000 DDR1 */ ++#define PHY_DRAMCFG_TYPE_DDR2 0x1 /* [2:0] 001 DDR2 */ ++#define PHY_DRAMCFG_TYPE_DDR3 0x2 /* [2:0] 010 DDR3 */ ++#define PHY_DRAMCFG_TYPE_DDR3L 0x3 /* [2:0] 011 DDR3L */ ++#define PHY_DRAMCFG_TYPE_LPDDR1 0x4 /* [2:0] 100 LPDDR1 */ ++#define PHY_DRAMCFG_TYPE_LPDDR2 0x5 /* [2:0] 101 LPDDR2 */ ++#define PHY_DRAMCFG_TYPE_LPDDR3 0x5 /* [2:0] 101 LPDDR3 */ ++#define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ ++#define PHY_DRAMCFG_TYPE_DDR4 0xa /* [3] 1010 DDR4 */ ++ ++#define PHY_DMSEL_SWAPDFIBYTE 0xf8ffffff /* [24:26] No Swap */ ++/* other */ ++#define PHY_RDQSG_PHASE_STEP 2 /* gate training phase step. */ ++#define PHY_GATE_PHASE_MARGIN 8 /* gate phase margin */ ++#define PHY_DQ_BDL_LEVEL 128 /* [CUSTOM] DQ BDL range */ ++#define PHY_DQ_BDL_MIDDLE 64 /* special middle DQ BDL value */ ++#define PHY_RDQSG_PHASE_MAX 0x3c /* RDQSG phase max value */ ++#define PHY_ACPHY_CLK_MAX 0xf /* halft_dramclk0 + cp1p_dclk0 */ ++#define PHY_PCODE_MIN 0x14 ++#define PHY_PCODE_MAX 0x24 ++ ++/* AC_DDRPHY_GATED_BYPASS */ ++#define PHY_CK_IOCTL_DUTY_EN 0x4 /* enable ck_ioctl_DUTY_EN_v */ ++#define PHY_CK1_IOCTL_DUTY_EN 0x8 /* enable ck1_ioctl_DUTY_EN_v */ ++/* CK AC_IOCTL22 */ ++#define DDR_DUTY_NUM 13 /* CK duty number */ ++#define DDR_CK_MAX_NUM 2 /* DDR CK max number */ ++#define DDR_CK_NUM_LPDDR4 2 /* LPDDR4 CK number */ ++#define DDR_CK_NUM_NONLPDDR4 1 /* NONLPDDR4 CK number */ ++#define DDR_DUTY_CTL_NUM 2 /* CK duty has two control direction */ ++/* CK duty step. */ ++#define PHY_AC_IOCTL21_STEP 1 ++#define DDR_DCC_CTL_WIN_DIFF 2 ++ ++#define PHY_MRS_SEQ_PROG_VAL 0x05555555 ++#define PHY_WDM_DISABLE_VAL 0x00004000 ++ ++/* ++ * DDR_BDL_PHASE_REL Calculation Method: ++ * 1. Calculation How many picosecond to one phase. ++ * PICOSECOND : 1 second is (1000 * 1000 * 1000) picosecond ++ * WAVE : 1 cycle is 2 ++ * RATE : DDR rate is 1600 Mbps, is (1600 * 1000) bps ++ * PHASE : 1 wave is 12 phase ++ * phase equal (((PICOSECOND * WAVE) / RATE) / PHASE) ++ * = (((1000 * 1000 * 1000 * 2) / (1600 * 1000)) / 12) ++ * = 104.17 ps. ++ * 2. Calculation How many bdl to one phase. ++ * one BDL is 6 ps. ++ * result = phase/bdl = 104.17 / 6 = 17.36 approximately equal to 17 ~= 16 ++ * 3. 16 = 1 << 4, so the relation is 4. ++ */ ++#ifndef DDR_BDL_PHASE_TRANSFORM ++/* [CUSTOM] one Phase equal how much BDL. 1 phase = 16 bdl */ ++#define DDR_BDL_PHASE_TRANSFORM 16 ++#endif ++#ifndef DDR_BDL_PHASE_REL ++/* [CUSTOM] relation between BDL and Phase. 1 phase = 16 bdl, 16 = 1 << 4 */ ++#define DDR_BDL_PHASE_REL 4 ++#endif ++ ++#define ddr_variable_declare(var) \ ++ unsigned int var; ++ ++#define ddr_vref_get_host_max(rank, val) do { \ ++ if ((rank) == 0) \ ++ (val) = PHY_VRFTRES_HVREF_MASK; \ ++ else \ ++ (val) = PHY_VRFTRES_RXDIFFCAL_MASK; \ ++} while (0) ++ ++/* PHY t28 all byte use a same value */ ++#define ddr_phy_vref_host_set(base_phy, rank, bytenum, byte_index, val) \ ++ ddr_phy_vref_host_set_process(base_phy, rank, bytenum, byte_index, val) ++ ++#define ddr_phy_vref_host_get(base_phy, rank, byte_index, val) do { \ ++ if ((rank) == 0) { \ ++ (val) = reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) & \ ++ PHY_VRFTRES_HVREF_MASK; \ ++ } else { \ ++ (val) = (reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) >> PHY_VRFTRES_RXDIFFCAL_BIT) & \ ++ PHY_VRFTRES_RXDIFFCAL_MASK; \ ++ } \ ++} while (0) ++ ++#define DDR_PHY_VREF_HOST_DISPLAY \ ++ {0, 0, ddr_phy_hvreft_status(0, 0), 0, "Host Vref Byte0"}, \ ++ {0, 1, ddr_phy_hvreft_status(0, 1), 0, "Host Vref Byte1"}, \ ++ {0, 2, ddr_phy_hvreft_status(0, 2), 0, "Host Vref Byte2"}, \ ++ {0, 3, ddr_phy_hvreft_status(0, 3), 0, "Host Vref Byte3"}, ++ ++#define DDR_PHY_VREF_HOST_DISPLAY_RANK1 \ ++ {1, 0, ddr_phy_hvreft_status(1, 0), 0, "Host Vref Byte0"}, \ ++ {1, 1, ddr_phy_hvreft_status(1, 1), 0, "Host Vref Byte1"}, \ ++ {1, 2, ddr_phy_hvreft_status(1, 2), 0, "Host Vref Byte2"}, \ ++ {1, 3, ddr_phy_hvreft_status(1, 3), 0, "Host Vref Byte3"}, ++ ++#define ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] Host Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_hvreft_status(rank, _i), \ ++ reg_read((base_phy) + ddr_phy_hvreft_status(rank, _i)), _i); \ ++ } \ ++} while (0) ++ ++/* DRAM vref operations */ ++#define ddr_phy_vref_dram_set(base_phy, val, byte_index) \ ++ ddr_vref_dram_set_process(base_phy, val, byte_index) ++ ++#define ddr_phy_vref_dram_get(base_phy, val, byte_index) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_dvreft_status(byte_index)) & \ ++ PHY_VRFTRES_DVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_DRAM_DISPLAY \ ++ {0, 0, ddr_phy_dvreft_status(0), 0, "DRAM Vref Byte0"}, \ ++ {0, 1, ddr_phy_dvreft_status(1), 0, "DRAM Vref Byte1"}, \ ++ {0, 2, ddr_phy_dvreft_status(2), 0, "DRAM Vref Byte2"}, \ ++ {0, 3, ddr_phy_dvreft_status(3), 0, "DRAM Vref Byte3"}, ++ ++#define ddr_phy_vref_dram_display_cmd(base_phy, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] DRAM Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_dvreft_status(_i), \ ++ reg_read((base_phy) + ddr_phy_dvreft_status(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* Dx dpmc operations */ ++#define DDR_DX_DPMC_DISPLAY ++#define ddr_dx_dpmc_display_cmd(base_phy, byte_num) ++ ++#define DDR_PHY_DCC_DISPLAY \ ++ {0, 0, DDR_PHY_ACIOCTL21, 0, "CK DUTY"}, ++ ++#define ddr_phy_dcc_display_cmd(base_phy) do { \ ++ unsigned int val; \ ++ (val) = reg_read((base_phy) + DDR_PHY_ACIOCTL21); \ ++ (val) = reg_read((base_phy) + DDR_PHY_ACIOCTL21); \ ++ ddr_info("[%x = %x] DCC duty", \ ++ (base_phy) + DDR_PHY_ACIOCTL21, val); \ ++} while (0) ++ ++#define DDR_PHY_ADDRPH_DISPLAY \ ++ {0, 0, DDR_PHY_ADDRPHBOUND, 0, "CA Phase"}, ++ ++#define DDR_PHY_ADDRBDL_DISPLAY \ ++ {0, 0, ddr_phy_acaddrbdl(0), 0, "CA BDL(0)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(1), 0, "CA BDL(1)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(2), 0, "CA BDL(2)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(3), 0, "CA BDL(3)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(4), 0, "CA BDL(4)"}, ++ ++#define ddr_phy_addrph_display_cmd(base_phy) do { \ ++ ddr_info("[%x = %x] CA Phase", (base_phy) + DDR_PHY_ADDRPHBOUND, \ ++ reg_read((base_phy) + DDR_PHY_ADDRPHBOUND)); \ ++} while (0) ++ ++#define ddr_phy_addrbdl_display_cmd(base_phy) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < DDR_PHY_CA_REG_MAX; _i++) { \ ++ ddr_info("[%x = %x] ACADDRBDL(%x)", \ ++ (base_phy) + ddr_phy_acaddrbdl(_i), \ ++ reg_read((base_phy) + ddr_phy_acaddrbdl(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* PHY t28 DDR4 RDQS synchronize to RDM */ ++#define ddr_phy_rdqs_sync_rdm(cfg, val) \ ++ ddr_rdqs_sync(cfg, val) ++ ++/* dqs swap */ ++#define ddr_dqsswap_save_func(swapdfibyte_en, base_phy) do { \ ++ (swapdfibyte_en) = reg_read((base_phy) + DDR_PHY_DMSEL); \ ++ reg_write((swapdfibyte_en) & PHY_DMSEL_SWAPDFIBYTE, \ ++ (base_phy) + DDR_PHY_DMSEL); \ ++} while (0) ++ ++#define ddr_dqsswap_restore_func(swapdfibyte_en, base_phy) \ ++ reg_write(swapdfibyte_en, (base_phy) + DDR_PHY_DMSEL); ++ ++#define ddr_phy_switch_rank(base_phy, val) do { \ ++ reg_write((reg_read((base_phy) + DDR_PHY_TRAINCTRL0) & (~PHY_TRAINCTRL0_MASK)) | (val), \ ++ (base_phy) + DDR_PHY_TRAINCTRL0); \ ++ reg_write((reg_read((base_phy) + DDR_PHY_HVRFTCTRL) & (~(0x1 << PHY_HRXDIFFCAL_EN_BIT))) | \ ++ ((val) << PHY_HRXDIFFCAL_EN_BIT), (base_phy) + DDR_PHY_HVRFTCTRL); \ ++} while (0) ++ ++/* Define the union u_phy_catconfig */ ++union u_phy_catconfig { ++ /* Define the struct bits */ ++ struct { ++ unsigned int ca_samp_num_bdl : 4; /* [3:0] */ ++ unsigned int ca_samp_num_ph : 4; /* [7:4] */ ++ unsigned int ca_trysamp_num : 4; /* [11:8] */ ++ unsigned int cat_rb_backtap : 4; /* [15:12] */ ++ unsigned int reserved : 1; /* [16] */ ++ unsigned int cat_openeye_en : 1; /* [17] */ ++ unsigned int cat_cat_phydq_sel : 1; /* [18] */ ++ unsigned int cat_restore_en : 1; /* [19] */ ++ unsigned int cat_lb_backtap : 4; /* [23:20] */ ++ unsigned int sw_cat_mrw42 : 1; /* [24] */ ++ unsigned int sw_cat_mrw48 : 1; /* [25] */ ++ unsigned int sw_cat_mrw41 : 1; /* [26] */ ++ unsigned int sw_cat_strobe : 1; /* [27] */ ++ unsigned int sw_cat_cke_high : 1; /* [28] */ ++ unsigned int sw_cat_cke_low : 1; /* [29] */ ++ unsigned int sw_cat_dqvalid : 1; /* [30] */ ++ unsigned int sw_cat_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++ ++/* Define the union u_phy_addrphbound */ ++union u_phy_addrphbound { ++ /* Define the struct bits */ ++ struct { ++ unsigned int addrph_a_right : 5; /* [4:0] */ ++ unsigned int reserved0 : 3; /* [7:5] */ ++ unsigned int addrph_a_left : 5; /* [12:8] */ ++ unsigned int reserved1 : 3; /* [15:13] */ ++ unsigned int addrph_a : 5; /* [20:16] */ ++ unsigned int reserved2 : 3; /* [23:21] */ ++ unsigned int addrph_a_ori : 5; /* [28:24] */ ++ unsigned int reserved3 : 3; /* [31:29] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++#endif /* DDR_PHY_T12_V101_H */ +diff --git a/drivers/ddr/vendor/default/ddr_phy_t16.h b/drivers/ddr/vendor/default/ddr_phy_t16.h +new file mode 100644 +index 0000000..9e885d5 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_phy_t16.h +@@ -0,0 +1,454 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_PHY_T16_H ++#define DDR_PHY_T16_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_PHY0 DDR_REG_BASE_PHY1 */ ++/* control the initialization of the PHY */ ++#define DDR_PHY_PHYINITCTRL 0x4 ++#define DDR_PHY_PHYINITSTATUS 0x8 /* Read Data Eye Calibration Error */ ++#define DDR_PHY_IMPSTATUS 0x28 /* This register specify the ZQ calibration result. */ ++#define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ ++#define DDR_PHY_DRAMTIMER1 0x34 /* This register specify the DRAM timing parameters */ ++#define DDR_PHY_TRAINCTRL0 0x48 /* hw training control */ ++#define DDR_PHY_MODEREG01 0x64 /* Extend Mode Register 01 */ ++#define DDR_PHY_MODEREG23 0x68 /* Extend Mode Register 23 */ ++/* update delay setting in registers to PHY */ ++#define DDR_PHY_MISC 0x70 ++#define DDR_PHY_DMSEL 0x84 /* DM Swap Selection */ ++#define DDR_PHY_SWTMODE 0xa0 /* S/W training mode */ ++/* issue one DQS pulse from PHY to DRAM */ ++#define DDR_PHY_SWTWLDQS 0xa4 ++#define DDR_PHY_SWTRLT 0xa8 /* S/W training result */ ++/* Host vref. [5:0]range [17:12]refsel */ ++#define DDR_PHY_IOCTL2 0xB4 ++#define DDR_PHY_VREFTCTRL 0xc0 /* VREF Training Control Register. */ ++#define DDR_PHY_DVRFTCTRL 0xC4 /* DRAM VREF Training */ ++#define DDR_PHY_MODEREG45 0xe0 /* defines the contents of the Mode Register */ ++#define DDR_PHY_MODEREG67 0xe4 /* This register defines the contents of the Mode Register */ ++#define ddr_phy_acaddrbdl(n) (0x140 + ((n) << 2)) ++#define DDR_PHY_IMP_CTRL1 0x170 /* AC/DX ZQ selection */ ++#define DDR_PHY_IMP_STATUS1 0x174 /* AC ZCAL status */ ++#define DDR_PHY_CATSWAPINDEX 0x01B8 /* CA SWAP index register */ ++#define DDR_PHY_CATSWAPSEL 0x01BC /* CA SWAP select register */ ++#define DDR_PHY_CATCONFIG 0x1C8 /* CA Training Configuration */ ++#define DDR_PHY_PHYDQRESULT 0x1D0 /* SW CA Training DQ result from PHY */ ++#define DDR_PHY_ADDRPHBOUND 0x1D4 /* CA Training addr phase boundary */ ++#define DDR_PHY_SWCATPATTERN_P 0x1D8 /* pattern for positive CK edge */ ++#define DDR_PHY_SWCATPATTERN_N 0x1DC /* pattern for negative CK edge */ ++ ++/* AC command bit delay line setting */ ++#define DDR_PHY_ACCMDBDL2 0x128 ++ ++/* WR DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++on write path */ ++#define ddr_phy_dxnwdqnbdl0(m, n) (0x210 + ((m) << 10) + ((n) << 7)) ++/* WR DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] */ ++#define ddr_phy_dxnwdqnbdl1(m, n) (0x214 + ((m) << 10) + ((n) << 7)) ++/* WR DM [6:0] the delay value of the bit delay line on DQM */ ++#define ddr_phy_dxnwdqnbdl2(m, n) (0x218 + ((m) << 10) + ((n) << 7)) ++/* RD DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl0(m, n) (0x21C + ((m) << 10) + ((n) << 7)) ++/* RD DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl1(m, n) (0x220 + ((m) << 10) + ((n) << 7)) ++/* [6:0]RD DM */ ++#define ddr_phy_dxnrdqnbdl2(m, n) (0x224 + ((m) << 10) + ((n) << 7)) ++ ++/* [CUSTOM] */ ++#define ddr_phy_dxnoebdl(m, n) (0x228 + ((m) << 10) + ((n) << 7)) ++/* [8:0] rdqs_bdl [24:16]rdqs_cyc. ++phase shift of the Read DQS to create 90 degree delays */ ++#define ddr_phy_dxnrdqsdly(n) (0x22C + ((n) << 7)) ++/* [6:0] the delay value of delay applied on WDQS for write leveling */ ++#define ddr_phy_dxwdqsdly(m, n) (0x230 + ((m) << 10) + ((n) << 7)) ++/* WR DQ phase BIT 12:8 */ ++#define ddr_phy_dxnwdqdly(m, n) (0x234 + ((m) << 10) + ((n) << 7)) ++/* [CUSTOM] rddqs gating */ ++#define ddr_phy_dxnrdqsgdly(m, n) (0x240 + ((m) << 10) + ((n) << 7)) ++/* read boundary right 8:0 left 24:16 */ ++#define ddr_phy_dxnrdbound(n) (0x250 + ((n) << 7)) ++/* write boundary right 4:0 left 20:16 */ ++#define ddr_phy_dxnwdbound(n) (0x254 + ((n) << 7)) ++/* [5:0] DRAM VREF(DQ) training result */ ++#define ddr_phy_dvreft_status(n) (0x270 + ((n) << 7)) ++/* [4:0] Host PHY VREF(DQ) training result */ ++#define ddr_phy_hvreft_status(n) (0x274 + ((n) << 7)) ++ ++/* DDRPHY AC static register */ ++#define DDR_PHY_ACIOCTL 0x1018 /* IO control register */ ++#define DDR_PHY_CORNER_DETECTOR 0x104C /* cfg of corner detector */ ++#define DDR_PHY_ACPHYCTL4 0x1064 /* AC block PHY control register */ ++#define DDR_PHY_ACPHYCTL7 0x1070 ++ ++#define DDR_VREF_HOST_VAL_MAX 0x3f /* 78.75%*VDDIO */ ++#define DDR_VREF_HOST_VAL_MIN 0x0 /* 40.00%*VDDIO */ ++ ++/* register mask */ ++#define PHY_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_WDQS_PHASE_MASK 0xf /* [11:8] */ ++#define PHY_RDQS_BDL_MASK 0x1ff /* [CUSTOM] [8:0] rdqsbdl */ ++#define PHY_RDQSG_PHASE_MASK 0x3f /* [14:9] rdqsgphase */ ++#define PHY_RDM_BDL_MASK 0x7f /* [6:0] */ ++/* hardware gate training result */ ++#define PHY_INITSTATUS_GT_MASK 0x20 ++#define PHY_SWTRLT_WL_MASK 0xf ++#define PHY_SWTRLT_GATE_MASK 0xf ++#define PHY_WDQ_PHASE_MASK 0x1f ++#define PHY_PHYINITCTRL_MASK 0xffff /* [15:0] all stat */ ++/* Read Data Eye Calibration Error */ ++#define PHY_PHYINITSTATUS_RDET_ERR 0x100 ++#define PHY_ACPHY_DCLK_MASK 0x7 /* cp1p_dclk0 mask */ ++#define PHY_ACPHY_DRAMCLK_MASK 0x1 /* halft_dramclk0 mask */ ++#define PHY_VRFTRES_DVREF_MASK 0x3f /* [5:0] */ ++#define PHY_VRFTRES_HVREF_MASK 0x3f /* [4:0] */ ++#define PHY_VRFTRES_RXDIFFCAL_MASK 0xf /* [24:21] */ ++#define PHY_ADDRPH_MASK 0x1f /* [20:16] */ ++#define PHY_ACADDR_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_CATSWAPSEL_BIT_MASK 0xff ++#define PHY_CAT_PATTERN_MASK 0x3ff ++#define PHY_TRAINCTRL0_MASK 0xf /* [3:0] */ ++#define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ ++#define PHY_VREFS_MRS_ENTER_MASK 0x1 /* [31] */ ++#define PHY_OSC_START_MASK 0x1 /* [0] */ ++#define PHY_OSC_RPT_VLD_MASK 0x1 /* [15] */ ++#define PHY_OSC_CNT_RDATA_MASK 0xffff /* [31:16] */ ++#define PHY_ZCODE_PDRV_MASK 0x3f /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_MASK 0x3f /* [29:24] */ ++#define DDR_PHY_T_MOD_MASK 0x1f /* bit[8:4]t_mod */ ++#define PHY_AC_IOCTL_TX_MODE_MASK 0x3 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* register bit */ ++#define PHY_MISC_UPDATE_BIT 19 /* [CUSTOM] delay config update bit */ ++#define PHY_PHYCONN_RST_BIT 15 /* issue reset signal to PHY counter */ ++#define PHY_RST_BIT 13 /* bit[13] issue reset signal to PHY */ ++#define PHY_RDQSG_PHASE_BIT 9 /* [CUSTOM] */ ++#define PHY_RDQSG_TX_BDL_BIT 16 /* [22:16] rdqsgtxbdl */ ++#define PHY_WDQS_PHASE_BIT 8 ++#define PHY_WDQS_BDL_BIT 0 ++#define PHY_WDQ_PHASE_BIT 8 ++#define PHY_WDM_BDL_BIT 0 ++/* [22:16] Write DQS Output Enable Delay Control */ ++#define PHY_WDQSOE_BDL_BIT 16 ++#define PHY_OEN_BDL_BIT 0 ++/* Mode Register 1. Defines the MR3/MR9 of the mode register */ ++#define PHY_MODEREG01_MR1_BIT 16 ++/* Bit delay line setting of CS1 */ ++#define PHY_ACCMD_CS0_BIT 0 ++#define PHY_ACCMD_CS1_BIT 16 ++#define PHY_ACPHY_DCLK0_BIT 6 /* [8:6] cp1p_dclk0 */ ++#define PHY_ACPHY_DCLK1_BIT 9 /* [11:9] ck2p_dclk1 */ ++#define PHY_ACPHY_DRAMCLK0_BIT 25 /* [25] halft_dramclk0 */ ++#define PHY_ACPHY_DRAMCLK1_BIT 24 /* [24] halft_dramclk1 */ ++#define PHY_ACPHY_DRAMCLK_EXT_BIT 3 /* [3] halft_dramclk0 */ ++#define PHY_SWTMODE_SW_GTMODE_BIT 1 /* [1] SW gate training */ ++#define PHY_ACADDRBDL_ADDR1_BIT 16 /* [16] ADDR1 delay line */ ++#define PHY_VREFS_MRS_ENTER_BIT 31 /* [31] */ ++#define PHY_OSC_RPT_VLD 15 /* [15] */ ++#define PHY_OSC_CNT_RDATA_BIT 16 /* [31:16] */ ++#define PHY_ZCODE_PDRV_BIT 16 /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_BIT 24 /* [29:24] */ ++#define PHY_AC_VDDQ_CAL_EN_BIT 8 /* [8] AC ZQ calibration enable */ ++#define PHY_CFG_RX_AGE_COMPST_EN_BIT 31 /* Enable rdqs age compensation function */ ++#define DDR_PHY_T_MOD_BIT 4 /* bit[8:4]t_mod */ ++#define PHY_MODEREG67_LP4_FSPWR_BIT 6 /* bit[6] FSPWR */ ++#define PHY_AC_IOCTL_TX_MODE_BIT 12 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* BDL register bit */ ++#define PHY_BDL_DQ_BIT 0 ++#define PHY_BDL_DQ0_BIT 0 ++#define PHY_BDL_DQ1_BIT 8 ++#define PHY_BDL_DQ2_BIT 16 ++#define PHY_BDL_DQ3_BIT 24 ++#define PHY_RDM_BDL_BIT 0 ++#define PHY_RDQS_BDL_BIT 0 ++ ++/* value */ ++#define PHY_PHYINITCTRL_DVREFT_SYNC 0x40000 /* DRAM VREF Synchronize */ ++#define PHY_TRAINCTRL0_DTR_RANK0 0x0 /* Training Rank0. */ ++#define PHY_TRAINCTRL0_DTR_RANK1 0x1 /* Training Rank1. */ ++/* hw training item defined in PHYINITCTRL */ ++#define PHY_PHYINITCTRL_CTL_CKE_BYPASS (1 << 31) /* PACK's CKE bypass function enable. */ ++#define PHY_PHYINITCTRL_PIC_PHYUPD_REQ (1 << 30) /* PACK's DFI PHY UPDATAE request by SW. */ ++#define PHY_PHYINITCTRL_PIC_TDQSST (1 << 29) /* TDQSS training Enable. */ ++#define PHY_PHYINITCTRL_LP4_CHB_DIS (1 << 28) /* LPDDR4 channel-B disable. */ ++#define PHY_PHYINITCTRL_LP4_CHA_DIS (1 << 27) /* LPDDR4 channel-A disable. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_SFT (1 << 26) /* Update delay line(switch op_sel) during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_WR (1 << 25) /* Retraining with MPC write during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_RD (1 << 24) /* Retraining with MPC read during tRFC. */ ++#define PHY_PHYINITCTRL_JTMT_EN (1 << 23) /* PLL Jitter Meter Enable. */ ++#define PHY_PHYINITCTRL_CST_EN (1 << 22) /* HW CS Traninig Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFS_EN (1 << 21) /* DRAM VREF(AC) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_ACHVREFT_EN (1 << 20) /* Host VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFT_EN (1 << 19) /* DRAM VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_DXDVREFS_EN (1 << 18) /* DRAM VREF(DQ) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_HVREFT_EN (1 << 17) /* Host VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_DVREFT_EN (1 << 16) /* DRAM VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_PHYCONN_RST (1 << 15) /* PHY Counter Reset. */ ++#define PHY_PHYINITCTRL_PACK_RST (1 << 14) /* PACK Reset. */ ++#define PHY_PHYINITCTRL_PHY_RST (1 << 13) /* PHY Reset. */ ++#define PHY_PHYINITCTRL_DRAM_RST (1 << 12) /* DRAM Reset. */ ++#define PHY_PHYINITCTRL_CAT_EN (1 << 11) /* HW CA Traninig Enable. */ ++#define PHY_PHYINITCTRL_DRAM_INIT_EN (1 << 10) /* DRAM Initialization Enable. */ ++#define PHY_PHYINITCTRL_WDET_EN (1 << 9) /* Write Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_RDET_EN (1 << 8) /* Read Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_WL2_EN (1 << 7) /* Second Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_GDST_EN (1 << 6) /* PHY Read Data Latch Train Enable. */ ++#define PHY_PHYINITCTRL_GT_EN (1 << 5) /* Gate Training Enable. */ ++#define PHY_PHYINITCTRL_WL_EN (1 << 4) /* Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_ZCAL_EN (1 << 3) /* Impedance Calibration Enable. */ ++#define PHY_PHYINITCTRL_DLYMEAS_EN (1 << 2) /* Delay Measurement Enable. */ ++#define PHY_PHYINITCTRL_PLL_INIT_EN (1 << 1) /* PLL Initialization Enable. */ ++#define PHY_PHYINITCTRL_INIT_EN (1 << 0) /* PHY Initialization Enable. */ ++ ++#define PHY_HW_GP_PHY_RESET (PHY_PHYINITCTRL_PHY_RST) ++#define PHY_HW_GP_CNT_RESET_START (PHY_PHYINITCTRL_PHYCONN_RST) ++#define PHY_HW_GP_PLL (PHY_PHYINITCTRL_PLL_INIT_EN | PHY_PHYINITCTRL_ZCAL_EN | PHY_PHYINITCTRL_DLYMEAS_EN) ++#define PHY_HW_GP_DRAM_RESET (PHY_PHYINITCTRL_DRAM_RST | PHY_PHYINITCTRL_DRAM_INIT_EN) ++#define PHY_HW_GP_VREF_AC (PHY_PHYINITCTRL_ACDVREFS_EN) ++#define PHY_HW_GP_CS (PHY_PHYINITCTRL_CST_EN) ++#define PHY_HW_GP_VREF_DQ (PHY_PHYINITCTRL_DVREFT_SYNC) ++#define PHY_HW_GP_NORMAL (PHY_PHYINITCTRL_WL_EN | \ ++ PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN | \ ++ PHY_PHYINITCTRL_DVREFT_EN | \ ++ PHY_PHYINITCTRL_HVREFT_EN | \ ++ PHY_PHYINITCTRL_PIC_TDQSST) ++#define PHY_HW_GP_CNT_RESET_END (PHY_PHYINITCTRL_PHYCONN_RST) ++ ++/* RDQS range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_RDQS_MIDDLE_VAL 0x30 ++/* DQ range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_DQ_MIDDLE_VAL 0x30303030 ++#define PHY_MISC_SCRAMB_DIS 0xfffeffff /* scrambler disable */ ++#define PHY_GATE_BDL_MAX 0xfe /* [6:0]rdqsg_bdl + [22:16]rdqsgtxbdl */ ++#define PHY_DVRFTCTRL_PDAEN_EN 0x80000000 /* pda enable */ ++/* [5] two cycle on address or command.(2T timing) */ ++#define PHY_DRAMCFG_MA2T 0x20 ++ ++#define PHY_DRAMCFG_TYPE_DDR1 0x0 /* [2:0] 000 DDR1 */ ++#define PHY_DRAMCFG_TYPE_DDR2 0x1 /* [2:0] 001 DDR2 */ ++#define PHY_DRAMCFG_TYPE_DDR3 0x2 /* [2:0] 010 DDR3 */ ++#define PHY_DRAMCFG_TYPE_DDR3L 0x3 /* [2:0] 011 DDR3L */ ++#define PHY_DRAMCFG_TYPE_LPDDR1 0x4 /* [2:0] 100 LPDDR1 */ ++#define PHY_DRAMCFG_TYPE_LPDDR2 0x5 /* [2:0] 101 LPDDR2 */ ++#define PHY_DRAMCFG_TYPE_LPDDR3 0x5 /* [2:0] 101 LPDDR3 */ ++#define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ ++#define PHY_DRAMCFG_TYPE_DDR4 0xa /* [3] 1010 DDR4 */ ++ ++#define PHY_DMSEL_SWAPDFIBYTE 0xf8ffffff /* [24:26] No Swap */ ++/* other */ ++#define PHY_RDQSG_PHASE_STEP 2 /* gate training phase step. */ ++#define PHY_GATE_PHASE_MARGIN 8 /* gate phase margin */ ++#define PHY_DQ_BDL_LEVEL 128 /* [CUSTOM] DQ BDL range */ ++#define PHY_DQ_BDL_MIDDLE 48 /* special middle DQ BDL value */ ++#define PHY_RDQSG_PHASE_MAX 0x3c /* RDQSG phase max value */ ++#define PHY_ACPHY_CLK_MAX 0xf /* halft_dramclk0 + cp1p_dclk0 */ ++#define PHY_PCODE_MIN 0x14 ++#define PHY_PCODE_MAX 0x24 ++/* ++ * DDR_BDL_PHASE_REL Calculation Method: ++ * 1. Calculation How many picosecond to one phase. ++ * PICOSECOND : 1 second is (1000 * 1000 * 1000) picosecond ++ * WAVE : 1 cycle is 2 ++ * RATE : DDR rate is 1600 Mbps, is (1600 * 1000) bps ++ * PHASE : 1 wave is 12 phase ++ * phase equal (((PICOSECOND * WAVE) / RATE) / PHASE) ++ * = (((1000 * 1000 * 1000 * 2) / (1600 * 1000)) / 12) ++ * = 104.17 ps. ++ * 2. Calculation How many bdl to one phase. ++ * one BDL is 6 ps. ++ * result = phase/bdl = 104.17 / 6 = 17.36 approximately equal to 17 ~= 16 ++ * 3. 16 = 1 << 4, so the relation is 4. ++ */ ++#ifndef DDR_BDL_PHASE_TRANSFORM ++/* [CUSTOM] one Phase equal how much BDL. 1 phase = 16 bdl */ ++#define DDR_BDL_PHASE_TRANSFORM 16 ++#endif ++#ifndef DDR_BDL_PHASE_REL ++/* [CUSTOM] relation between BDL and Phase. 1 phase = 16 bdl, 16 = 1 << 4 */ ++#define DDR_BDL_PHASE_REL 4 ++#endif ++ ++#define ddr_variable_declare(var) \ ++ unsigned int var; ++ ++#define ddr_vref_get_host_max(rank, val) do { \ ++ if ((rank) == 0) \ ++ (val) = PHY_VRFTRES_HVREF_MASK; \ ++ else \ ++ (val) = PHY_VRFTRES_RXDIFFCAL_MASK; \ ++} while (0) ++ ++/* PHY t28 all byte use a same value */ ++#define ddr_phy_vref_host_set(base_phy, rank, bytenum, byte_index, val) do { \ ++ int i = 0; \ ++ unsigned int hvreft; \ ++ for (i = 0; i < (bytenum); i++) { \ ++ hvreft = reg_read((base_phy) + ddr_phy_hvreft_status(i)) & \ ++ (~PHY_VRFTRES_HVREF_MASK); \ ++ reg_write(hvreft | (val), (base_phy) + ddr_phy_hvreft_status(i)); \ ++ } \ ++} while (0) ++ ++#define ddr_phy_vref_host_get(base_phy, rank, byte_index, val) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_hvreft_status(0)) & \ ++ PHY_VRFTRES_HVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_HOST_DISPLAY \ ++ {0, 0, ddr_phy_hvreft_status(0), 0, "Host Vref Byte0"}, \ ++ {0, 1, ddr_phy_hvreft_status(1), 0, "Host Vref Byte1"}, \ ++ {0, 2, ddr_phy_hvreft_status(2), 0, "Host Vref Byte2"}, \ ++ {0, 3, ddr_phy_hvreft_status(3), 0, "Host Vref Byte3"}, ++ ++#define DDR_PHY_VREF_HOST_DISPLAY_RANK1 ++ ++#define ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num) do { \ ++ for (i = 0; i < (byte_num); i++) { \ ++ ddr_info("[%x = %x] Host Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_hvreft_status(i), \ ++ reg_read((base_phy) + ddr_phy_hvreft_status(i)), i); \ ++ } \ ++} while (0) ++ ++/* DRAM vref operations */ ++#define ddr_phy_vref_dram_set(base_phy, val, byte_index) \ ++ ddr_vref_dram_set_process(base_phy, val, byte_index) ++ ++#define ddr_phy_vref_dram_get(base_phy, val, byte_index) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_dvreft_status(byte_index)) & \ ++ PHY_VRFTRES_DVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_DRAM_DISPLAY \ ++ {0, 0, ddr_phy_dvreft_status(0), 0, "DRAM Vref Byte0"}, \ ++ {0, 1, ddr_phy_dvreft_status(1), 0, "DRAM Vref Byte1"}, \ ++ {0, 2, ddr_phy_dvreft_status(2), 0, "DRAM Vref Byte2"}, \ ++ {0, 3, ddr_phy_dvreft_status(3), 0, "DRAM Vref Byte3"}, ++ ++#define ddr_phy_vref_dram_display_cmd(base_phy, byte_num) do { \ ++ for (i = 0; i < (byte_num); i++) { \ ++ ddr_info("[%x = %x] DRAM Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_dvreft_status(i), \ ++ reg_read((base_phy) + ddr_phy_dvreft_status(i)), i); \ ++ } \ ++} while (0) ++ ++/* Dx dpmc operations */ ++#define DDR_DX_DPMC_DISPLAY ++#define ddr_dx_dpmc_display_cmd(base_phy, byte_num) ++/* phy t16 not support DCC training */ ++#define DDR_PHY_DCC_DISPLAY ++#define ddr_phy_dcc_display_cmd(base_phy) ++ ++/* lowpower ddr ca operations */ ++#define DDR_PHY_ADDRPH_DISPLAY \ ++ {0, 0, DDR_PHY_ADDRPHBOUND, 0, "CA Phase"}, ++ ++#define DDR_PHY_ADDRBDL_DISPLAY \ ++ {0, 0, ddr_phy_acaddrbdl(0), 0, "CA BDL(0)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(1), 0, "CA BDL(1)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(2), 0, "CA BDL(2)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(3), 0, "CA BDL(3)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(4), 0, "CA BDL(4)"}, ++ ++#define ddr_phy_addrph_display_cmd(base_phy) \ ++ ddr_info("[%x = %x] CA Phase", \ ++ (base_phy) + DDR_PHY_ADDRPHBOUND, \ ++ reg_read((base_phy) + DDR_PHY_ADDRPHBOUND)); ++ ++#define ddr_phy_addrbdl_display_cmd(base_phy) do { \ ++ for (i = 0; i < DDR_PHY_CA_REG_MAX; i++) { \ ++ ddr_info("[%x = %x] ACADDRBDL(%x)", \ ++ (base_phy) + ddr_phy_acaddrbdl(i), \ ++ reg_read((base_phy) + ddr_phy_acaddrbdl(i)), i); \ ++ } \ ++} while (0) ++ ++/* PHY t28 DDR4 RDQS synchronize to RDM */ ++#define ddr_phy_rdqs_sync_rdm(cfg, val) \ ++ ddr_rdqs_sync(cfg, val) ++ ++/* dqs swap */ ++#define ddr_dqsswap_save_func(swapdfibyte_en, base_phy) do { \ ++ (swapdfibyte_en) = reg_read((base_phy) + DDR_PHY_DMSEL); \ ++ reg_write((swapdfibyte_en) & PHY_DMSEL_SWAPDFIBYTE, \ ++ (base_phy) + DDR_PHY_DMSEL); \ ++} while (0) ++ ++#define ddr_dqsswap_restore_func(swapdfibyte_en, base_phy) \ ++ reg_write(swapdfibyte_en, (base_phy) + DDR_PHY_DMSEL); ++ ++/* phy t16 not support rank switch */ ++#define ddr_phy_switch_rank(base_phy, val) ++ ++/* Define the union u_phy_catconfig */ ++union u_phy_catconfig { ++ /* Define the struct bits */ ++ struct { ++ unsigned int ca_samp_num_bdl : 4; /* [3:0] */ ++ unsigned int ca_samp_num_ph : 4; /* [7:4] */ ++ unsigned int ca_trysamp_num : 4; /* [11:8] */ ++ unsigned int cat_rb_backtap : 4; /* [15:12] */ ++ unsigned int reserved : 1; /* [16] */ ++ unsigned int cat_openeye_en : 1; /* [17] */ ++ unsigned int cat_cat_phydq_sel : 1; /* [18] */ ++ unsigned int cat_restore_en : 1; /* [19] */ ++ unsigned int cat_lb_backtap : 4; /* [23:20] */ ++ unsigned int sw_cat_mrw42 : 1; /* [24] */ ++ unsigned int sw_cat_mrw48 : 1; /* [25] */ ++ unsigned int sw_cat_mrw41 : 1; /* [26] */ ++ unsigned int sw_cat_strobe : 1; /* [27] */ ++ unsigned int sw_cat_cke_high : 1; /* [28] */ ++ unsigned int sw_cat_cke_low : 1; /* [29] */ ++ unsigned int sw_cat_dqvalid : 1; /* [30] */ ++ unsigned int sw_cat_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++ ++/* Define the union u_phy_addrphbound */ ++union u_phy_addrphbound { ++ /* Define the struct bits */ ++ struct { ++ unsigned int addrph_a_right : 5; /* [4:0] */ ++ unsigned int reserved0 : 3; /* [7:5] */ ++ unsigned int addrph_a_left : 5; /* [12:8] */ ++ unsigned int reserved1 : 3; /* [15:13] */ ++ unsigned int addrph_a : 5; /* [20:16] */ ++ unsigned int reserved2 : 3; /* [23:21] */ ++ unsigned int addrph_a_ori : 5; /* [28:24] */ ++ unsigned int reserved3 : 3; /* [31:29] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++#endif /* DDR_PHY_T16_H */ +diff --git a/drivers/ddr/vendor/default/ddr_phy_t28.h b/drivers/ddr/vendor/default/ddr_phy_t28.h +new file mode 100644 +index 0000000..5928042 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_phy_t28.h +@@ -0,0 +1,459 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_PHY_T28_H ++#define DDR_PHY_T28_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_PHY0 DDR_REG_BASE_PHY1 */ ++/* control the initialization of the PHY */ ++#define DDR_PHY_PHYINITCTRL 0x4 ++#define DDR_PHY_PHYINITSTATUS 0x8 /* Read Data Eye Calibration Error */ ++#define DDR_PHY_IMPSTATUS 0x28 /* This register specify the ZQ calibration result. */ ++#define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ ++#define DDR_PHY_DRAMTIMER1 0x34 /* This register specify the DRAM timing parameters */ ++#define DDR_PHY_TRAINCTRL0 0x48 /* hw training control */ ++#define DDR_PHY_MODEREG01 0x64 /* Extend Mode Register 01 */ ++#define DDR_PHY_MODEREG23 0x68 /* Extend Mode Register 23 */ ++/* update delay setting in registers to PHY */ ++#define DDR_PHY_MISC 0x70 ++#define DDR_PHY_DMSEL 0x84 /* DM Swap Selection */ ++#define DDR_PHY_SWTMODE 0xa0 /* S/W training mode */ ++/* issue one DQS pulse from PHY to DRAM */ ++#define DDR_PHY_SWTWLDQS 0xa4 ++#define DDR_PHY_SWTRLT 0xa8 /* S/W training result */ ++/* Host vref. [5:0]range [17:12]refsel */ ++#define DDR_PHY_PHYRSCTRL 0xB0 /* PHY Register Slice Contrl */ ++#define DDR_PHY_IOCTL2 0xB4 ++#define DDR_PHY_VREFTCTRL 0xc0 /* VREF Training Control Register. */ ++#define DDR_PHY_DVRFTCTRL 0xC4 /* DRAM VREF Training */ ++#define DDR_PHY_MODEREG45 0xe0 /* defines the contents of the Mode Register */ ++#define DDR_PHY_MODEREG67 0xe4 /* This register defines the contents of the Mode Register */ ++#define ddr_phy_acaddrbdl(n) (0x140 + ((n) << 2)) ++#define DDR_PHY_IMP_CTRL1 0x170 /* AC/DX ZQ selection */ ++#define DDR_PHY_IMP_STATUS1 0x174 /* AC ZCAL status */ ++#define DDR_PHY_CATSWAPINDEX 0x01B8 /* CA SWAP index register */ ++#define DDR_PHY_CATSWAPSEL 0x01BC /* CA SWAP select register */ ++#define DDR_PHY_CATCONFIG 0x1C8 /* CA Training Configuration */ ++#define DDR_PHY_PHYDQRESULT 0x1D0 /* SW CA Training DQ result from PHY */ ++#define DDR_PHY_ADDRPHBOUND 0x1D4 /* CA Training addr phase boundary */ ++#define DDR_PHY_SWCATPATTERN_P 0x1D8 /* pattern for positive CK edge */ ++#define DDR_PHY_SWCATPATTERN_N 0x1DC /* pattern for negative CK edge */ ++ ++/* AC command bit delay line setting */ ++#define DDR_PHY_ACCMDBDL2 0x128 ++ ++/* WR DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++on write path */ ++#define ddr_phy_dxnwdqnbdl0(m, n) (0x210 + ((m) << 10) + ((n) << 7)) ++/* WR DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] */ ++#define ddr_phy_dxnwdqnbdl1(m, n) (0x214 + ((m) << 10) + ((n) << 7)) ++/* WR DM [6:0] the delay value of the bit delay line on DQM */ ++#define ddr_phy_dxnwdqnbdl2(m, n) (0x218 + ((m) << 10) + ((n) << 7)) ++/* RD DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl0(m, n) (0x21C + ((m) << 10) + ((n) << 7)) ++/* RD DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl1(m, n) (0x220 + ((m) << 10) + ((n) << 7)) ++/* [6:0]RD DM */ ++#define ddr_phy_dxnrdqnbdl2(m, n) (0x224 + ((m) << 10) + ((n) << 7)) ++ ++/* [CUSTOM] */ ++#define ddr_phy_dxnoebdl(m, n) (0x228 + ((m) << 10) + ((n) << 7)) ++/* [8:0] rdqs_bdl [24:16]rdqs_cyc. ++phase shift of the Read DQS to create 90 degree delays */ ++#define ddr_phy_dxnrdqsdly(n) (0x22C + ((n) << 7)) ++/* [6:0] the delay value of delay applied on WDQS for write leveling */ ++#define ddr_phy_dxwdqsdly(m, n) (0x230 + ((m) << 10) + ((n) << 7)) ++/* WR DQ phase BIT 12:8 */ ++#define ddr_phy_dxnwdqdly(m, n) (0x234 + ((m) << 10) + ((n) << 7)) ++/* [CUSTOM] rddqs gating */ ++#define ddr_phy_dxnrdqsgdly(m, n) (0x240 + ((m) << 10) + ((n) << 7)) ++/* read boundary right 8:0 left 24:16 */ ++#define ddr_phy_dxnrdbound(n) (0x250 + ((n) << 7)) ++/* write boundary right 4:0 left 20:16 */ ++#define ddr_phy_dxnwdbound(n) (0x254 + ((n) << 7)) ++/* [5:0] DRAM VREF(DQ) training result */ ++#define ddr_phy_dvreft_status(n) (0x270 + ((n) << 7)) ++/* [4:0] Host PHY VREF(DQ) training result */ ++#define ddr_phy_hvreft_status(m, n) (0x274 + ((m) << 10) + ((n) << 7)) ++ ++/* DDRPHY AC static register */ ++#define DDR_PHY_ACIOCTL 0x1018 /* IO control register */ ++#define DDR_PHY_CORNER_DETECTOR 0x104C /* cfg of corner detector */ ++#define DDR_PHY_ACPHYCTL4 0x1064 /* AC block PHY control register */ ++#define DDR_PHY_ACPHYCTL7 0x1070 ++ ++#define DDR_VREF_HOST_VAL_MAX 0x1f /* 78.75%*VDDIO */ ++#define DDR_VREF_HOST_VAL_MIN 0x0 /* 40.00%*VDDIO */ ++ ++/* register mask */ ++#define PHY_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_WDQS_PHASE_MASK 0xf /* [11:8] */ ++#define PHY_RDQS_BDL_MASK 0x1ff /* [CUSTOM] [8:0] rdqsbdl */ ++#define PHY_RDQSG_PHASE_MASK 0x3f /* [14:9] rdqsgphase */ ++#define PHY_RDM_BDL_MASK 0x7f /* [6:0] */ ++/* hardware gate training result */ ++#define PHY_INITSTATUS_GT_MASK 0x20 ++#define PHY_SWTRLT_WL_MASK 0xf ++#define PHY_SWTRLT_GATE_MASK 0xf ++#define PHY_WDQ_PHASE_MASK 0x1f ++#define PHY_PHYINITCTRL_MASK 0x1 /* [15:0] all stat */ ++/* Read Data Eye Calibration Error */ ++#define PHY_PHYINITSTATUS_RDET_ERR 0x100 ++#define PHY_ACPHY_DCLK_MASK 0x7 /* cp1p_dclk0 mask */ ++#define PHY_ACPHY_DRAMCLK_MASK 0x1 /* halft_dramclk0 mask */ ++#define PHY_VRFTRES_DVREF_MASK 0x3f /* [5:0] */ ++#define PHY_VRFTRES_HVREF_MASK 0x1f /* [4:0] */ ++#define PHY_VRFTRES_RXDIFFCAL_MASK 0xf /* [24:21] */ ++#define PHY_ADDRPH_MASK 0x1f /* [20:16] */ ++#define PHY_ACADDR_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_VREFS_MRS_ENTER_MASK 0x1 /* [31] */ ++#define PHY_CATSWAPSEL_BIT_MASK 0xff ++#define PHY_CAT_PATTERN_MASK 0x3ff ++#define PHY_TRAINCTRL0_MASK 0xf /* [3:0] */ ++#define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ ++#define PHY_OSC_START_MASK 0x1 /* [0] */ ++#define PHY_OSC_RPT_VLD_MASK 0x1 /* [15] */ ++#define PHY_OSC_CNT_RDATA_MASK 0xffff /* [31:16] */ ++#define PHY_ZCODE_PDRV_MASK 0x3f /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_MASK 0x3f /* [29:24] */ ++#define DDR_PHY_T_MOD_MASK 0x1f /* bit[8:4]t_mod */ ++#define PHY_AC_IOCTL_TX_MODE_MASK 0x3 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* register bit */ ++#define PHY_MISC_UPDATE_BIT 19 /* [CUSTOM] delay config update bit */ ++#define PHY_PHYCONN_RST_BIT 15 /* issue reset signal to PHY counter */ ++#define PHY_RST_BIT 13 /* bit[13] issue reset signal to PHY */ ++#define PHY_RDQSG_PHASE_BIT 9 /* [CUSTOM] */ ++#define PHY_RDQSG_TX_BDL_BIT 16 /* [22:16] rdqsgtxbdl */ ++#define PHY_WDQS_PHASE_BIT 8 ++#define PHY_WDQS_BDL_BIT 0 ++#define PHY_WDQ_PHASE_BIT 8 ++#define PHY_WDM_BDL_BIT 0 ++/* [22:16] Write DQS Output Enable Delay Control */ ++#define PHY_WDQSOE_BDL_BIT 16 ++#define PHY_OEN_BDL_BIT 0 ++/* Mode Register 1. Defines the MR3/MR9 of the mode register */ ++#define PHY_MODEREG01_MR1_BIT 16 ++/* Bit delay line setting of CS1 */ ++#define PHY_ACCMD_CS0_BIT 0 ++#define PHY_ACCMD_CS1_BIT 16 ++#define PHY_ACPHY_DCLK0_BIT 6 /* [8:6] cp1p_dclk0 */ ++#define PHY_ACPHY_DCLK1_BIT 9 /* [11:9] ck2p_dclk1 */ ++#define PHY_ACPHY_DRAMCLK0_BIT 25 /* [25] halft_dramclk0 */ ++#define PHY_ACPHY_DRAMCLK1_BIT 24 /* [24] halft_dramclk1 */ ++#define PHY_ACPHY_DRAMCLK_EXT_BIT 3 /* [3] halft_dramclk0 */ ++#define PHY_SWTMODE_SW_GTMODE_BIT 1 /* [1] SW gate training */ ++#define PHY_ACADDRBDL_ADDR1_BIT 16 /* [16] ADDR1 delay line */ ++#define PHY_VREFS_MRS_ENTER_BIT 31 /* [31] */ ++#define PHY_OSC_RPT_VLD 15 /* [15] */ ++#define PHY_OSC_CNT_RDATA_BIT 16 /* [31:16] */ ++#define PHY_ZCODE_PDRV_BIT 16 /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_BIT 24 /* [29:24] */ ++#define PHY_AC_VDDQ_CAL_EN_BIT 8 /* [8] AC ZQ calibration enable */ ++#define PHY_CFG_RX_AGE_COMPST_EN_BIT 31 /* Enable rdqs age compensation function */ ++#define DDR_PHY_T_MOD_BIT 4 /* bit[8:4]t_mod */ ++#define PHY_MODEREG67_LP4_FSPWR_BIT 6 /* bit[6] FSPWR */ ++#define PHY_AC_IOCTL_TX_MODE_BIT 12 /* bit[13:12]ac_ioctl_tx_mode */ ++ ++/* BDL register bit */ ++#define PHY_BDL_DQ_BIT 0 ++#define PHY_BDL_DQ0_BIT 0 ++#define PHY_BDL_DQ1_BIT 8 ++#define PHY_BDL_DQ2_BIT 16 ++#define PHY_BDL_DQ3_BIT 24 ++#define PHY_RDM_BDL_BIT 0 ++#define PHY_RDQS_BDL_BIT 0 ++ ++/* value */ ++#define PHY_PHYINITCTRL_DVREFT_SYNC 0x40000 /* DRAM VREF Synchronize */ ++/* hw training item defined in PHYINITCTRL */ ++#define PHY_PHYINITCTRL_CTL_CKE_BYPASS (1 << 31) /* PACK's CKE bypass function enable. */ ++#define PHY_PHYINITCTRL_PIC_PHYUPD_REQ (1 << 30) /* PACK's DFI PHY UPDATAE request by SW. */ ++#define PHY_PHYINITCTRL_PIC_TDQSST (1 << 28) /* TDQSS training Enable. */ ++#define PHY_PHYINITCTRL_CFG_LPBK_COMPST_EN (1 << 27) /* RDQS/CK loopback delay compensate enable. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_SFT (1 << 26) /* Update delay line(switch op_sel) during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_WR (1 << 25) /* Retraining with MPC write during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_RD (1 << 24) /* Retraining with MPC read during tRFC. */ ++#define PHY_PHYINITCTRL_JTMT_EN (1 << 23) /* PLL Jitter Meter Enable. */ ++#define PHY_PHYINITCTRL_CST_EN (1 << 22) /* HW CS Traninig Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFS_EN (1 << 21) /* DRAM VREF(AC) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_ACHVREFT_EN (1 << 20) /* Host VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFT_EN (1 << 19) /* DRAM VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_DXDVREFS_EN (1 << 18) /* DRAM VREF(DQ) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_HVREFT_EN (1 << 17) /* Host VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_DVREFT_EN (1 << 16) /* DRAM VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_PHYCONN_RST (1 << 15) /* PHY Counter Reset. */ ++#define PHY_PHYINITCTRL_PACK_RST (1 << 14) /* PACK Reset. */ ++#define PHY_PHYINITCTRL_PHY_RST (1 << 13) /* PHY Reset. */ ++#define PHY_PHYINITCTRL_DRAM_RST (1 << 12) /* DRAM Reset. */ ++#define PHY_PHYINITCTRL_CAT_EN (1 << 11) /* HW CA Traninig Enable. */ ++#define PHY_PHYINITCTRL_DRAM_INIT_EN (1 << 10) /* DRAM Initialization Enable. */ ++#define PHY_PHYINITCTRL_WDET_EN (1 << 9) /* Write Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_RDET_EN (1 << 8) /* Read Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_WL2_EN (1 << 7) /* Second Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_GDST_EN (1 << 6) /* PHY Read Data Latch Train Enable. */ ++#define PHY_PHYINITCTRL_GT_EN (1 << 5) /* Gate Training Enable. */ ++#define PHY_PHYINITCTRL_WL_EN (1 << 4) /* Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_ZCAL_EN (1 << 3) /* Impedance Calibration Enable. */ ++#define PHY_PHYINITCTRL_DLYMEAS_EN (1 << 2) /* Delay Measurement Enable. */ ++#define PHY_PHYINITCTRL_PLL_INIT_EN (1 << 1) /* PLL Initialization Enable. */ ++#define PHY_PHYINITCTRL_INIT_EN (1 << 0) /* PHY Initialization Enable. */ ++ ++#define PHY_HW_GP_PHY_RESET (PHY_PHYINITCTRL_PHY_RST) ++#define PHY_HW_GP_CNT_RESET_START (PHY_PHYINITCTRL_PHYCONN_RST) ++#define PHY_HW_GP_PLL (PHY_PHYINITCTRL_PLL_INIT_EN | PHY_PHYINITCTRL_ZCAL_EN | PHY_PHYINITCTRL_DLYMEAS_EN) ++#define PHY_HW_GP_DRAM_RESET (PHY_PHYINITCTRL_DRAM_RST | PHY_PHYINITCTRL_DRAM_INIT_EN) ++#define PHY_HW_GP_VREF_AC (PHY_PHYINITCTRL_ACDVREFS_EN) ++#define PHY_HW_GP_CS (PHY_PHYINITCTRL_CST_EN) ++#define PHY_HW_GP_VREF_DQ (PHY_PHYINITCTRL_DVREFT_SYNC) ++#define PHY_HW_GP_NORMAL (PHY_PHYINITCTRL_WL_EN | \ ++ PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN | \ ++ PHY_PHYINITCTRL_DVREFT_EN | \ ++ PHY_PHYINITCTRL_HVREFT_EN | \ ++ PHY_PHYINITCTRL_PIC_TDQSST) ++#define PHY_HW_GP_CNT_RESET_END (PHY_PHYINITCTRL_PHYCONN_RST) ++ ++/* RDQS range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_RDQS_MIDDLE_VAL 0x30 ++/* DQ range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_DQ_MIDDLE_VAL 0x30303030 ++#define PHY_MISC_SCRAMB_DIS 0xfffeffff /* scrambler disable */ ++#define PHY_GATE_BDL_MAX 0xfe /* [6:0]rdqsg_bdl + [22:16]rdqsgtxbdl */ ++#define PHY_DVRFTCTRL_PDAEN_EN 0x80000000 /* pda enable */ ++/* [5] two cycle on address or command.(2T timing) */ ++#define PHY_DRAMCFG_MA2T 0x20 ++ ++#define PHY_DRAMCFG_TYPE_DDR1 0x0 /* [2:0] 000 DDR1 */ ++#define PHY_DRAMCFG_TYPE_DDR2 0x1 /* [2:0] 001 DDR2 */ ++#define PHY_DRAMCFG_TYPE_DDR3 0x2 /* [2:0] 010 DDR3 */ ++#define PHY_DRAMCFG_TYPE_DDR3L 0x3 /* [2:0] 011 DDR3L */ ++#define PHY_DRAMCFG_TYPE_LPDDR1 0x4 /* [2:0] 100 LPDDR1 */ ++#define PHY_DRAMCFG_TYPE_LPDDR2 0x5 /* [2:0] 101 LPDDR2 */ ++#define PHY_DRAMCFG_TYPE_LPDDR3 0x5 /* [2:0] 101 LPDDR3 */ ++#define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ ++#define PHY_DRAMCFG_TYPE_DDR4 0xa /* [3] 1010 DDR4 */ ++ ++#define PHY_DMSEL_SWAPDFIBYTE 0xf8ffffff /* [24:26] No Swap */ ++/* other */ ++#define PHY_RDQSG_PHASE_STEP 2 /* gate training phase step. */ ++#define PHY_GATE_PHASE_MARGIN 8 /* gate phase margin */ ++#define PHY_DQ_BDL_LEVEL 128 /* [CUSTOM] DQ BDL range */ ++#define PHY_DQ_BDL_MIDDLE 64 /* special middle DQ BDL value */ ++#define PHY_RDQSG_PHASE_MAX 0x3c /* RDQSG phase max value */ ++#define PHY_ACPHY_CLK_MAX 0xf /* halft_dramclk0 + cp1p_dclk0 */ ++#define PHY_PCODE_MIN 0x14 ++#define PHY_PCODE_MAX 0x24 ++/* ++ * DDR_BDL_PHASE_REL Calculation Method: ++ * 1. Calculation How many picosecond to one phase. ++ * PICOSECOND : 1 second is (1000 * 1000 * 1000) picosecond ++ * WAVE : 1 cycle is 2 ++ * RATE : DDR rate is 1600 Mbps, is (1600 * 1000) bps ++ * PHASE : 1 wave is 12 phase ++ * phase equal (((PICOSECOND * WAVE) / RATE) / PHASE) ++ * = (((1000 * 1000 * 1000 * 2) / (1600 * 1000)) / 12) ++ * = 104.17 ps. ++ * 2. Calculation How many bdl to one phase. ++ * one BDL is 6 ps. ++ * result = phase/bdl = 104.17 / 6 = 17.36 approximately equal to 17 ~= 16 ++ * 3. 16 = 1 << 4, so the relation is 4. ++ */ ++#ifndef DDR_BDL_PHASE_TRANSFORM ++/* [CUSTOM] one Phase equal how much BDL. 1 phase = 16 bdl */ ++#define DDR_BDL_PHASE_TRANSFORM 16 ++#endif ++#ifndef DDR_BDL_PHASE_REL ++/* [CUSTOM] relation between BDL and Phase. 1 phase = 16 bdl, 16 = 1 << 4 */ ++#define DDR_BDL_PHASE_REL 4 ++#endif ++ ++#define ddr_variable_declare(var) \ ++ unsigned int var; ++ ++#define ddr_vref_get_host_max(rank, val) do { \ ++ if ((rank) == 0) \ ++ (val) = PHY_VRFTRES_HVREF_MASK; \ ++ else \ ++ (val) = PHY_VRFTRES_RXDIFFCAL_MASK; \ ++} while (0) ++ ++/* PHY t28 all byte use a same value */ ++#define ddr_phy_vref_host_set(base_phy, rank, bytenum, byte_index, val) do { \ ++ unsigned int hvreft; \ ++ hvreft = reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) & \ ++ (~PHY_VRFTRES_HVREF_MASK); \ ++ reg_write(hvreft | (val), (base_phy) + ddr_phy_hvreft_status(rank, byte_index)); \ ++ reg_write(hvreft | (val), (base_phy) + ddr_phy_hvreft_status(rank, (byte_index) + 1)); \ ++} while (0) ++ ++#define ddr_phy_vref_host_get(base_phy, rank, byte_index, val) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) & \ ++ PHY_VRFTRES_HVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_HOST_DISPLAY \ ++ {0, 0, ddr_phy_hvreft_status(0, 0), 0, "Host Vref Byte0"}, \ ++ {0, 1, ddr_phy_hvreft_status(0, 1), 0, "Host Vref Byte1"}, \ ++ {0, 2, ddr_phy_hvreft_status(0, 2), 0, "Host Vref Byte2"}, \ ++ {0, 3, ddr_phy_hvreft_status(0, 3), 0, "Host Vref Byte3"}, ++ ++#define DDR_PHY_VREF_HOST_DISPLAY_RANK1 \ ++ {1, 0, ddr_phy_hvreft_status(1, 0), 0, "Host Vref Byte0"}, \ ++ {1, 1, ddr_phy_hvreft_status(1, 1), 0, "Host Vref Byte1"}, \ ++ {1, 2, ddr_phy_hvreft_status(1, 2), 0, "Host Vref Byte2"}, \ ++ {1, 3, ddr_phy_hvreft_status(1, 3), 0, "Host Vref Byte3"}, ++ ++#define ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] Host Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_hvreft_status(rank, _i), \ ++ reg_read((base_phy) + ddr_phy_hvreft_status(rank, _i)), _i); \ ++ } \ ++} while (0) ++ ++/* DRAM vref operations */ ++#define ddr_phy_vref_dram_set(base_phy, val, byte_index) \ ++ ddr_vref_dram_set_process(base_phy, val, byte_index) ++ ++#define ddr_phy_vref_dram_get(base_phy, val, byte_index) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_dvreft_status(byte_index)) & \ ++ PHY_VRFTRES_DVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_DRAM_DISPLAY \ ++ {0, 0, ddr_phy_dvreft_status(0), 0, "DRAM Vref Byte0"}, \ ++ {0, 1, ddr_phy_dvreft_status(1), 0, "DRAM Vref Byte1"}, \ ++ {0, 2, ddr_phy_dvreft_status(2), 0, "DRAM Vref Byte2"}, \ ++ {0, 3, ddr_phy_dvreft_status(3), 0, "DRAM Vref Byte3"}, ++ ++#define ddr_phy_vref_dram_display_cmd(base_phy, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] DRAM Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_dvreft_status(_i), \ ++ reg_read((base_phy) + ddr_phy_dvreft_status(_i)), _i); \ ++ } \ ++} while (0) ++ ++#define DDR_DX_DPMC_DISPLAY ++#define ddr_dx_dpmc_display_cmd(base_phy, byte_num) ++ ++/* phy t28 not support DCC training */ ++#define DDR_PHY_DCC_DISPLAY ++#define ddr_phy_dcc_display_cmd(base_phy) ++ ++/* lowpower ddr ca operations */ ++#define DDR_PHY_ADDRPH_DISPLAY \ ++ {0, 0, DDR_PHY_ADDRPHBOUND, 0, "CA Phase"}, ++ ++#define DDR_PHY_ADDRBDL_DISPLAY \ ++ {0, 0, ddr_phy_acaddrbdl(0), 0, "CA BDL(0)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(1), 0, "CA BDL(1)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(2), 0, "CA BDL(2)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(3), 0, "CA BDL(3)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(4), 0, "CA BDL(4)"}, ++ ++#define ddr_phy_addrph_display_cmd(base_phy) do { \ ++ ddr_info("[%x = %x] CA Phase", (base_phy) + DDR_PHY_ADDRPHBOUND, \ ++ reg_read((base_phy) + DDR_PHY_ADDRPHBOUND)); \ ++} while (0) ++ ++#define ddr_phy_addrbdl_display_cmd(base_phy) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < DDR_PHY_CA_REG_MAX; _i++) { \ ++ ddr_info("[%x = %x] ACADDRBDL(%x)", \ ++ (base_phy) + ddr_phy_acaddrbdl(_i), \ ++ reg_read((base_phy) + ddr_phy_acaddrbdl(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* PHY t28 DDR4 RDQS synchronize to RDM */ ++#define ddr_phy_rdqs_sync_rdm(cfg, val) \ ++ ddr_rdqs_sync(cfg, val) ++ ++/* dqs swap */ ++#define ddr_dqsswap_save_func(swapdfibyte_en, base_phy) do { \ ++ (swapdfibyte_en) = reg_read((base_phy) + DDR_PHY_DMSEL); \ ++ reg_write((swapdfibyte_en) & PHY_DMSEL_SWAPDFIBYTE, \ ++ (base_phy) + DDR_PHY_DMSEL); \ ++} while (0) ++ ++#define ddr_dqsswap_restore_func(swapdfibyte_en, base_phy) \ ++ reg_write(swapdfibyte_en, (base_phy) + DDR_PHY_DMSEL); ++ ++#define ddr_phy_switch_rank(base_phy, val) do { \ ++ reg_write((reg_read((base_phy) + DDR_PHY_TRAINCTRL0) & (~PHY_TRAINCTRL0_MASK)) | (val), \ ++ (base_phy) + DDR_PHY_TRAINCTRL0); \ ++} while (0) ++ ++/* Define the union u_phy_catconfig */ ++union u_phy_catconfig { ++ /* Define the struct bits */ ++ struct { ++ unsigned int ca_samp_num_bdl : 4; /* [3:0] */ ++ unsigned int ca_samp_num_ph : 4; /* [7:4] */ ++ unsigned int ca_trysamp_num : 4; /* [11:8] */ ++ unsigned int cat_rb_backtap : 4; /* [15:12] */ ++ unsigned int reserved : 1; /* [16] */ ++ unsigned int cat_openeye_en : 1; /* [17] */ ++ unsigned int cat_cat_phydq_sel : 1; /* [18] */ ++ unsigned int cat_restore_en : 1; /* [19] */ ++ unsigned int cat_lb_backtap : 4; /* [23:20] */ ++ unsigned int sw_cat_mrw42 : 1; /* [24] */ ++ unsigned int sw_cat_mrw48 : 1; /* [25] */ ++ unsigned int sw_cat_mrw41 : 1; /* [26] */ ++ unsigned int sw_cat_strobe : 1; /* [27] */ ++ unsigned int sw_cat_cke_high : 1; /* [28] */ ++ unsigned int sw_cat_cke_low : 1; /* [29] */ ++ unsigned int sw_cat_dqvalid : 1; /* [30] */ ++ unsigned int sw_cat_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++ ++/* Define the union u_phy_addrphbound */ ++union u_phy_addrphbound { ++ /* Define the struct bits */ ++ struct { ++ unsigned int addrph_a_right : 5; /* [4:0] */ ++ unsigned int reserved0 : 3; /* [7:5] */ ++ unsigned int addrph_a_left : 5; /* [12:8] */ ++ unsigned int reserved1 : 3; /* [15:13] */ ++ unsigned int addrph_a : 5; /* [20:16] */ ++ unsigned int reserved2 : 3; /* [23:21] */ ++ unsigned int addrph_a_ori : 5; /* [28:24] */ ++ unsigned int reserved3 : 3; /* [31:29] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++#endif /* DDR_PHY_T28_H */ +diff --git a/drivers/ddr/vendor/default/ddr_training_boot.c b/drivers/ddr/vendor/default/ddr_training_boot.c +new file mode 100644 +index 0000000..2be6c61 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_training_boot.c +@@ -0,0 +1,231 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++#include "ddr_interface.h" ++ ++/* Save DDR tarining result */ ++void ddr_result_data_save(struct ddr_cfg_st *cfg, const struct training_data *training) ++{ ++ /* nothing to do when ddr training on power up */ ++} ++ ++void ddr_lpca_data_save(struct ddr_cfg_st *cfg, const struct ca_data_st *data) ++{ ++ /* nothing to do when ddr training on power up */ ++} ++ ++/* Get DDRT test address */ ++unsigned int ddr_ddrt_get_test_addr(void) ++{ ++ return DDRT_CFG_TEST_ADDR_BOOT; ++} ++ ++#ifdef DDR_TRAINING_UART_CONFIG ++#ifdef DDR_TRAINING_MINI_LOG_CONFIG ++/* Display DDR training error when boot */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ uart_early_putc('E'); ++ uart_early_put_hex(mask); ++ uart_early_putc('P'); ++ uart_early_put_hex(phy); ++ uart_early_putc('B'); ++ uart_early_put_hex(byte); ++ uart_early_putc('D'); ++ uart_early_put_hex(dq); ++} ++void ddr_training_start(void) ++{ ++ uart_early_putc('D'); ++ uart_early_putc('D'); ++ uart_early_putc('R'); ++} ++void ddr_training_suc(void) ++{ ++ uart_early_putc('S'); ++} ++#else ++/* Define string to print */ ++void ddr_training_local_str(void) ++{ ++ asm volatile( ++ "str_wl:\n\t" ++ ".asciz \"WL\"\n\t" ++ ".align 2\n\t" ++ ++ "str_hwg:\n\t" ++ ".asciz \"HWG\"\n\t" ++ ".align 2\n\t" ++ ++ "str_gate:\n\t" ++ ".asciz \"Gate\"\n\t" ++ ".align 2\n\t" ++ ++ "str_ddrt:\n\t" ++ ".asciz \"DDRT\"\n\t" ++ ".align 2\n\t" ++ ++ "str_hwrd:\n\t" ++ ".asciz \"HWRD\"\n\t" ++ ".align 2\n\t" ++ ++ "str_mpr:\n\t" ++ ".asciz \"MPR\"\n\t" ++ ".align 2\n\t" ++ ++ "str_dataeye:\n\t" ++ ".asciz \"Dataeye\"\n\t" ++ ".align 2\n\t" ++ ++ "str_lpca:\n\t" ++ ".asciz \"LPCA\"\n\t" ++ ".align 2\n\t" ++ ++ "str_err:\n\t" ++ ".asciz \" Err:\"\n\t" ++ ".align 2\n\t" ++ ++ "str_phy:\n\t" ++ ".asciz \"Phy\"\n\t" ++ ".align 2\n\t" ++ ++ "str_byte:\n\t" ++ ".asciz \"Byte\"\n\t" ++ ".align 2\n\t" ++ ++ "str_dq:\n\t" ++ ".asciz \"DQ\"\n\t" ++ ".align 2\n\t" ++ ++ "str_ddrtr_start:\n\t" ++ ".asciz \"\r\\nDDRTR \"\n\t" ++ ".align 2\n\t" ++ ++ "str_ddrtr_suc:\n\t" ++ ".asciz \"Suc\"\n\t" ++ ".align 2\n\t" ++ ); ++} ++ ++/* Display DDR training error when boot */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ uart_early_putc('\r'); ++ uart_early_putc('\n'); ++ /* error type */ ++ switch (mask) { ++ case DDR_ERR_WL: ++ asm volatile("adr r0, str_wl\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_HW_GATING: ++ asm volatile("adr r0, str_hwg\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_GATING: ++ asm volatile("adr r0, str_gate\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_DDRT_TIME_OUT: ++ asm volatile("adr r0, str_ddrt\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_HW_RD_DATAEYE: ++ asm volatile("adr r0, str_hwrd\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_MPR: ++ asm volatile("adr r0, str_mpr\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_DATAEYE: ++ asm volatile("adr r0, str_dataeye\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_LPCA: ++ asm volatile("adr r0, str_lpca\n\t" ++ "bl uart_early_puts"); ++ break; ++ default: ++ break; ++ } ++ ++ /* error string */ ++ asm volatile("adr r0, str_err\n\t" ++ "bl uart_early_puts"); ++ ++ /* error phy */ ++ if (phy != 0) { ++ asm volatile("adr r0, str_phy\n\t" ++ "bl uart_early_puts"); ++ uart_early_put_hex(phy); ++ } ++ ++ /* error byte */ ++ if (byte != -1) { ++ asm volatile("adr r0, str_byte\n\t" ++ "bl uart_early_puts"); ++ uart_early_put_hex(byte); ++ } ++ ++ /* error dq */ ++ if (dq != -1) { ++ asm volatile("adr r0, str_dq\n\t" ++ "bl uart_early_puts"); ++ uart_early_put_hex(dq); ++ } ++} ++ ++/* Display DDR training start when boot */ ++void ddr_training_start(void) ++{ ++ asm volatile( ++ "push {lr}\n\t" ++ "adr r0, str_ddrtr_start\n\t" ++ "bl uart_early_puts\n\t" ++ "pop {lr}" ++ ); ++} ++ ++/* Display DDR training result when boot */ ++void ddr_training_suc(void) ++{ ++ asm volatile( ++ "push {lr}\n\t" ++ "adr r0, str_ddrtr_suc\n\t" ++ "bl uart_early_puts\n\t" ++ "pop {lr}" ++ ); ++} ++#endif /* DDR_TRAINING_CUT_CODE_CONFIG */ ++#else ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ return; ++} ++void ddr_training_suc(void) ++{ ++ return; ++} ++void ddr_training_start(void) ++{ ++ return; ++} ++#endif /* DDR_TRAINING_UART_CONFIG */ +diff --git a/drivers/ddr/vendor/default/ddr_training_console.c b/drivers/ddr/vendor/default/ddr_training_console.c +new file mode 100644 +index 0000000..980d2a1 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_training_console.c +@@ -0,0 +1,271 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#define __DDR_TRAINING_CONSOLE__ ++#ifdef DDR_TRAINING_CONSOLE_CONFIG ++ ++#define DDR_UART_BASE_REG 0x12090000 ++#define UART_PL01X_FR 0x18 /* Flag register (Read only). */ ++#define UART_PL01X_FR_RXFE 0x10 ++#define UART_PL01X_DR 0x00 /* Data read or written from the interface. */ ++#define UART_PL01X_ECR 0x04 /* Error clear register (Write). */ ++ ++#define ALIGN_COUNT 4 ++ ++#define isprint(c) ((c) >= ' ' && (c) <= '~') ++#define isspace(c) ((c) == ' ' || ((c) >= '\t' && (c) <= '\r')) ++#define isdigit(c) ((c) >= '0' && (c) <= '9') ++#define isxdigit(c) (isdigit(c) || \ ++ ((c) >= 'A' && (c) <= 'F') || \ ++ ((c) >= 'a' && (c) <= 'f')) ++ ++/* DDR console get char */ ++static int ddr_console_getc(void) ++{ ++ unsigned int data; ++ ++ /* Wait until there is data in the FIFO */ ++ while (reg_read(DDR_UART_BASE_REG + UART_PL01X_FR) & UART_PL01X_FR_RXFE) { ++ } ++ ++ data = reg_read(DDR_UART_BASE_REG + UART_PL01X_DR); ++ /* Check for an error flag */ ++ if (data & 0xFFFFFF00) { ++ /* Clear the error */ ++ reg_write(0xFFFFFFFF, DDR_UART_BASE_REG + UART_PL01X_ECR); ++ return -1; ++ } ++ ++ return (int)data; ++} ++ ++/* DDR read line */ ++static char *ddr_readline(char *str, int len) ++{ ++ unsigned int c; ++ char *p = str; ++ while (len > 0) { ++ c = ddr_console_getc(); ++ switch (c) { ++ case '\r': ++ case '\n': ++ *p = '\0'; ++ DDR_PUTC('\r'); ++ DDR_PUTC('\n'); ++ return str; ++ case 0x08: ++ case 0x7F: ++ if (p > str) { ++ p--; ++ len++; ++ DDR_PUTC('\b'); ++ DDR_PUTC(' '); ++ DDR_PUTC('\b'); ++ } ++ break; ++ default: ++ if (isprint(c)) { ++ (*p++) = (char)c; ++ len--; ++ DDR_PUTC(c); ++ } ++ break; ++ } ++ } ++ (*--p) = '\0'; ++ return str; ++} ++ ++/* HEX to INT */ ++static int hex2int(char **ss, unsigned int *n) ++{ ++ unsigned char *s = (unsigned char *)(*ss); ++ ++ while (isspace(*s)) ++ s++; ++ ++ if (!(*s)) ++ return -1; ++ ++ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ++ s += 2; /* Skip the first 2 characters: 0x */ ++ ++ for ((*n) = 0; isxdigit(*s); s++) { ++ (*n) = ((*n) << 4); /* shift left 4 */ ++ if ((*s) >= '0' && (*s) <= '9') ++ (*n) |= ((*s) - '0'); ++ else if ((*s) >= 'a' && (*s) <= 'f') ++ (*n) |= ((*s) + 10 - 'a'); /* transfer a-f to 10-15 */ ++ else if ((*s) >= 'A' && (*s) <= 'F') ++ (*n) |= ((*s) + 10 - 'A'); /* transfer A-F to 10-15 */ ++ } ++ ++ if (isspace(*s) || !(*s)) { ++ while (isspace(*s)) ++ s++; ++ (*ss) = (char *)s; ++ return 0; ++ } ++ ++ return -1; ++} ++/* ++ * DDR do memory write. ++ * mw address value [count] ++ */ ++static int ddr_do_memory_write(char *cmd) ++{ ++ unsigned int address; ++ unsigned int value; ++ unsigned int count = ALIGN_COUNT; ++ ++ if (hex2int(&cmd, &address)) ++ return -1; ++ ++ if (hex2int(&cmd, &value)) ++ return -1; ++ ++ if ((*cmd) && hex2int(&cmd, &count)) ++ return -1; ++ ++ if ((address & 0x03) || (count & 0x03)) { ++ ddr_info("parameter should align with 4 bytes.\n"); ++ return -1; ++ } ++ for (; count > 0; count -= ALIGN_COUNT, address += ALIGN_COUNT) /* align with 4 bytes */ ++ reg_write(value, address); ++ ++ return 0; ++} ++/* ++ * DDR do memory display. ++ * md address [count] ++ */ ++static int ddr_do_memory_display(char *cmd) ++{ ++ unsigned int ix; ++ unsigned int loop; ++ unsigned int address; ++ unsigned int count = 64; ++ ++ if (hex2int(&cmd, &address)) ++ return -1; ++ ++ if ((*cmd) && hex2int(&cmd, &count)) ++ return -1; ++ ++ if (count < ALIGN_COUNT) ++ count = ALIGN_COUNT; ++ ++ address &= ~0x03; ++ loop = (count & ~0x03); ++ ++ while (loop > 0) { ++ DDR_PUTC('0'); ++ DDR_PUTC('x'); ++ DDR_PUT_HEX(address); ++ DDR_PUTC(':'); ++ ++ for (ix = 0; ++ ix < ALIGN_COUNT && loop > 0; ++ ix++, loop -= ALIGN_COUNT, address += ALIGN_COUNT) { ++ DDR_PUTC(' '); ++ DDR_PUT_HEX(reg_read(address)); ++ } ++ DDR_PUTC('\r'); ++ DDR_PUTC('\n'); ++ } ++ ++ return 0; ++} ++ ++static int ddr_do_sw_training(char *cmd) ++{ ++ int result; ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++#ifdef DDR_TRAINING_CMD ++ struct ddr_training_result_st ddrt_result_sram; ++#endif ++ ++ ddr_training_cfg_init(cfg); ++#ifdef DDR_TRAINING_CMD ++ cfg->res_st = (void *)&ddrt_result_sram; ++#endif ++ result = ddr_training_all(cfg); ++ ++ result += ddr_dcc_training_func(cfg); ++ ++ return 0; ++} ++ ++static int ddr_do_hw_training(char *cmd) ++{ ++ int result; ++ ++ result = ddr_hw_training_func(); ++ ++ return result; ++} ++ ++/* Do DDR training console if sw training or hw training fail */ ++static int ddr_training_console(void) ++{ ++ char str[256]; /* 256: Command length range */ ++ char *p = NULL; ++ ++ while (1) { ++ DDR_PUTC('d'); ++ DDR_PUTC('d'); ++ DDR_PUTC('r'); ++ DDR_PUTC('#'); ++ ++ p = ddr_readline(str, sizeof(str)); ++ while (isspace(*p)) ++ p++; ++ if (p[0] == 'q') ++ break; ++ if (p[0] == 'm' && p[1] == 'w') { ++ ddr_do_memory_write(p + 2); /* p[2]:Third character */ ++ } else if (p[0] == 'm' && p[1] == 'd') { ++ ddr_do_memory_display(p + 2); /* p[2]:Third character */ ++ } else if (p[0] == 's' && p[1] == 'w') { ++ ddr_do_sw_training(p + 2); /* p[2]:Third character */ ++ } else if (p[0] == 'h' && p[1] == 'w') { ++ ddr_do_hw_training(p + 2); /* p[2]:Third character */ ++ } ++ } ++ ++ return 0; ++} ++#else ++static int ddr_training_console(void) ++{ ++ ddr_warning("Not support DDR training console"); ++ return 0; ++} ++#endif /* DDR_TRAINING_CONSOLE_CONFIG */ ++ ++int ddr_training_console_if(void) ++{ ++ return DDR_TRAINING_CONSOLE(); ++} +diff --git a/drivers/ddr/vendor/default/ddr_training_ctl.c b/drivers/ddr/vendor/default/ddr_training_ctl.c +new file mode 100644 +index 0000000..579f334 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_training_ctl.c +@@ -0,0 +1,403 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#ifdef DDR_SW_TRAINING_FUNC_PUBLIC ++#ifdef DDR_TRAINING_CUT_CODE_CONFIG ++/* lpca */ ++static int ddr_lpca_for_sw(struct ddr_cfg_st *cfg, unsigned int auto_ref_timing) ++{ ++ int result = 0; ++ ++ if ((ddr_training_check_bypass(cfg, DDR_BYPASS_LPCA_MASK) == DDR_FALSE) && ++ ((reg_read(cfg->cur_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_LPDDR3) == ++ PHY_DRAMCFG_TYPE_LPDDR3)) { ++ /* disable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ ++ result += ddr_lpca_training(cfg); ++ ++ /* enable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing); ++ } ++ ++ return result; ++} ++ ++/* write leveling */ ++static int ddr_wl_for_sw(struct ddr_cfg_st *cfg, unsigned int auto_ref_timing) ++{ ++ int result = 0; ++ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_WL_MASK) == DDR_FALSE) { ++ /* disable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ result += ddr_write_leveling(cfg); ++ /* enable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing); ++ } ++ ++ return result; ++} ++ ++/* dataeye */ ++static int ddr_dataeye_for_sw(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DATAEYE_MASK) == DDR_FALSE) { ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ result += ddr_dataeye_training(cfg); ++ } ++ ++ return result; ++} ++ ++/* hardware read */ ++static int ddr_hw_read_for_sw(struct ddr_cfg_st *cfg, unsigned int acphyctl, int result) ++{ ++ unsigned int dramcfg_ma2t; ++ ++ dramcfg_ma2t = reg_read(cfg->cur_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T; ++ ++ if (result && (ddr_training_check_bypass(cfg, DDR_BYPASS_HW_MASK) == DDR_FALSE)) { ++ if (!dramcfg_ma2t) /* set 1T */ ++ reg_write(0x0, cfg->cur_phy + DDR_PHY_ACPHYCTL4); ++ ++ result = ddr_hw_dataeye_read(cfg); ++ if (!dramcfg_ma2t) /* restore */ ++ reg_write(acphyctl, cfg->cur_phy + DDR_PHY_ACPHYCTL4); ++ ++ result += ddr_dataeye_training(cfg); ++ } ++ ++ return result; ++} ++ ++/* mpr */ ++#ifdef DDR_MPR_TRAINING_CONFIG ++static int ddr_mpr_for_sw(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ ++ if (result && (ddr_training_check_bypass(cfg, DDR_BYPASS_MPR_MASK) == DDR_FALSE)) { ++ result = ddr_mpr_training(cfg); ++ result += ddr_dataeye_training(cfg); ++ } ++ ++ return result; ++} ++#endif ++ ++/* gate */ ++static int ddr_gate_for_sw(struct ddr_cfg_st *cfg, ++ unsigned int acphyctl, unsigned int auto_ref_timing) ++{ ++ int result = 0; ++ unsigned int dramcfg_ma2t; ++ ++ dramcfg_ma2t = reg_read(cfg->cur_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T; ++ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_GATE_MASK) == DDR_FALSE) { ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_GATE); ++ /* disable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, ++ auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ ++ if (!dramcfg_ma2t) /* set 1T */ ++ reg_write(0x0, cfg->cur_phy + DDR_PHY_ACPHYCTL4); ++ ++ result += ddr_gate_training(cfg); ++ ++ /* enable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing); ++ ++ if (!dramcfg_ma2t) /* restore */ ++ reg_write(acphyctl, cfg->cur_phy + DDR_PHY_ACPHYCTL4); ++ } ++ ++ return result; ++} ++ ++/* vref */ ++static int ddr_vref_for_sw(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_VREF_HOST_MASK) == DDR_FALSE) { ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ result += ddr_vref_training(cfg); ++ } ++ ++ return result; ++} ++ ++static int ddr_training_boot_func(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int auto_ref_timing; ++ unsigned int acphyctl; ++ ++ auto_ref_timing = reg_read(cfg->cur_dmc + DDR_DMC_TIMING2); ++ /* Static register have to read two times to get the right value. */ ++ acphyctl = reg_read(cfg->cur_phy + DDR_PHY_ACPHYCTL4); ++ acphyctl = reg_read(cfg->cur_phy + DDR_PHY_ACPHYCTL4); ++ ++ /* check hardware gating */ ++ if (reg_read(cfg->cur_phy + DDR_PHY_PHYINITSTATUS) & PHY_INITSTATUS_GT_MASK) { ++ ddr_fatal("PHY[%x] hw gating fail", cfg->cur_phy); ++ ddr_training_stat(DDR_ERR_HW_GATING, cfg->cur_phy, -1, -1); ++ } ++ ++#ifdef DDR_LPCA_TRAINING_CONFIG ++ /* lpca */ ++ result += ddr_lpca_for_sw(cfg, auto_ref_timing); ++#endif ++ ++#ifdef DDR_WL_TRAINING_CONFIG ++ /* write leveling */ ++ result += ddr_wl_for_sw(cfg, auto_ref_timing); ++#endif ++ ++#ifdef DDR_DATAEYE_TRAINING_CONFIG ++ /* dataeye */ ++ result += ddr_dataeye_for_sw(cfg); ++#endif ++ ++#ifdef DDR_HW_TRAINING_CONFIG ++ /* hardware read */ ++ result += ddr_hw_read_for_sw(cfg, acphyctl, result); ++#endif ++ ++#ifdef DDR_MPR_TRAINING_CONFIG ++ /* mpr */ ++ result += ddr_mpr_for_sw(cfg); ++#endif ++ ++#ifdef DDR_GATE_TRAINING_CONFIG ++ /* gate */ ++ result += ddr_gate_for_sw(cfg, acphyctl, auto_ref_timing); ++#endif ++ ++#ifdef DDR_VREF_TRAINING_CONFIG ++ result += ddr_vref_for_sw(cfg); ++#endif ++ ++ return result; ++} ++ ++/* ++ * Cut ddr training control code for less SRAM. ++ * Support DDRC500. ++ * Support DDRC510 with one PHY. ++ */ ++int ddr_sw_training_func(void) ++{ ++ int result; ++ unsigned int misc_scramb; ++ ++ ddr_variable_declare(swapdfibyte_en); ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ ++ /* check sw ddr training enable */ ++ if (DDR_BYPASS_ALL_MASK == reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG)) ++ return 0; ++ ++ ddr_training_start(); ++ ddr_training_cfg_init(cfg); ++ ++ cfg->phy_idx = 0; ++ cfg->cur_phy = cfg->phy[0].addr; ++ cfg->dmc_idx = 0; ++ cfg->cur_dmc = cfg->phy[0].dmc[0].addr; ++ ++ misc_scramb = reg_read(cfg->cur_phy + DDR_PHY_MISC); ++ ++#ifdef DDR_TRAINING_STAT_CONFIG ++ /* clear stat register */ ++ reg_write(0x0, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT); ++#endif ++ ++ /* disable scramb */ ++ reg_write(misc_scramb & PHY_MISC_SCRAMB_DIS, cfg->cur_phy + DDR_PHY_MISC); ++ ++ /* disable rdqs swap */ ++ ddr_dqsswap_save_func(swapdfibyte_en, cfg->cur_phy); ++ ++ result = ddr_training_boot_func(cfg); ++ ++ /* restore scramb */ ++ reg_write(misc_scramb, cfg->cur_phy + DDR_PHY_MISC); ++ ++ /* restore rdqs swap */ ++ ddr_dqsswap_restore_func(swapdfibyte_en, cfg->cur_phy); ++ ++ if (!result) ++ ddr_training_suc(); ++ ++ return result; ++} ++#else ++int ddr_training_boot_func(struct ddr_cfg_st *cfg) ++{ ++ int result; ++ ++ /* check hardware gating */ ++ if (reg_read(cfg->cur_phy + DDR_PHY_PHYINITSTATUS) & ++ PHY_INITSTATUS_GT_MASK) { ++ ddr_fatal("PHY[%x] hw gating fail", cfg->cur_phy); ++ ddr_training_stat(DDR_ERR_HW_GATING, cfg->cur_phy, -1, -1); ++ } ++ ++ /* lpca */ ++ result = ddr_lpca_training_func(cfg); ++ /* write leveling */ ++ result += ddr_wl_func(cfg); ++ /* dataeye/gate/vref need switch axi */ ++ /* dataeye */ ++ result += ddr_dataeye_training_func(cfg); ++#ifdef DDR_HW_TRAINING_CONFIG ++ /* hardware read */ ++ if (result && (ddr_training_check_bypass(cfg, DDR_BYPASS_HW_MASK) == DDR_FALSE)) { ++ struct tr_relate_reg relate_reg_ac; ++ ddr_training_save_reg(cfg, &relate_reg_ac, DDR_BYPASS_HW_MASK); ++ result = ddr_hw_dataeye_read(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg_ac); ++ cfg->adjust = DDR_DATAEYE_ABNORMAL_ADJUST; ++ result += ddr_dataeye_training(cfg); ++ } ++#endif ++ /* mpr */ ++ result += ddr_mpr_training_func(cfg); ++ /* gate */ ++ result += ddr_gating_func(cfg); ++ /* vref */ ++ result += ddr_vref_training_func(cfg); ++ ++ return result; ++} ++ ++/* Support DDRC510 with two PHY */ ++int ddr_sw_training_func(void) ++{ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ struct tr_relate_reg reg; ++ int result; ++ ++#ifdef SYSCTRL_DDR_TRAINING_VERSION_FLAG ++ /* DDR training version flag */ ++ unsigned int tmp_reg = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_VERSION_FLAG); ++ tmp_reg = (tmp_reg & 0xffff0000) | DDR_VERSION; ++ reg_write(tmp_reg, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_VERSION_FLAG); ++#endif ++ ++ /* check sw ddr training enable */ ++ if (DDR_BYPASS_ALL_MASK == reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG) ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ && DDR_BYPASS_ALL_MASK == reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC) ++#endif ++ ) ++ return 0; ++ ++ ddr_training_start(); ++ ++ /* save customer reg */ ++ ddr_boot_cmd_save_func(®); ++ ++#ifdef DDR_TRAINING_STAT_CONFIG ++ /* clear stat register */ ++ reg_write(0x0, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT); ++#endif ++ ++ ddr_training_cfg_init(cfg); ++ cfg->cmd_st = 0; ++ ++ result = ddr_training_all(cfg); ++ result += ddr_dcc_training_func(cfg); ++ if (!result) ++ ddr_training_suc(); ++ else ++ ddr_training_console_if(); ++ ++ /* restore customer reg */ ++ ddr_boot_cmd_restore_func(®); ++ ++ return result; ++} ++#endif /* DDR_TRAINING_CUT_CODE_CONFIG */ ++#endif /* DDR_SW_TRAINING_FUNC_PUBLIC */ ++ ++#ifdef DDR_PCODE_TRAINING_CONFIG ++int ddr_pcode_training_func(void) ++{ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ ++ ddr_training_cfg_init(cfg); ++ return ddr_pcode_training(cfg); ++} ++#else ++int ddr_pcode_training_func(void) ++{ ++ ddr_warning("Not support DDR pcode training"); ++ return 0; ++} ++#endif ++ ++#ifdef DDR_HW_TRAINING_CONFIG ++int ddr_hw_training_func(void) ++{ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ ++ ddr_training_cfg_init(cfg); ++ ++ return ddr_hw_training(cfg); ++} ++#else ++int ddr_hw_training_func(void) ++{ ++ ddr_warning("Not support DDR HW training"); ++ return 0; ++} ++#endif /* DDR_HW_TRAINING_CONFIG */ ++ ++int ddr_sw_training_if(void) ++{ ++ return DDR_SW_TRAINING_FUNC(); ++} ++ ++int ddr_hw_training_if(void) ++{ ++ return DDR_HW_TRAINING_FUNC(); ++} ++ ++int ddr_pcode_training_if(void) ++{ ++ return DDR_PCODE_TRAINING_FUNC(); ++} ++ +diff --git a/drivers/ddr/vendor/default/ddr_training_impl.c b/drivers/ddr/vendor/default/ddr_training_impl.c +new file mode 100644 +index 0000000..def1b21 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_training_impl.c +@@ -0,0 +1,2749 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++#include "ddr_interface.h" ++ ++#define __COMMON__ ++#ifdef DDR_TRAINING_MEM_FUNC ++void* memcpy(void *dst, const void *src, size_t len) ++{ ++ const char *s = src; ++ char *d = dst; ++ ++ if ((dst == NULL) || (src == NULL)) ++ return NULL; ++ ++ while (len--) ++ *d++ = *s++; ++ ++ return dst; ++} ++ ++void* memset(void *b, int c, size_t len) ++{ ++ char *bp = b; ++ ++ if (b == NULL) ++ return NULL; ++ ++ while (len--) ++ *bp++ = (unsigned char)c; ++ ++ return b; ++} ++#endif ++ ++static int ddr_training_by_dmc(struct ddr_cfg_st *cfg) ++{ ++ if (cfg->cmd_st != NULL) { ++#ifdef DDR_TRAINING_CMD ++ return ddr_training_cmd_func(cfg); ++#endif ++ } else { ++ return ddr_training_boot_func(cfg); ++ } ++ ++ return 0; ++} ++ ++static int ddr_training_by_rank(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ ddr_phy_switch_rank(cfg->cur_phy, cfg->rank_idx); ++ if (cfg->phy[cfg->phy_idx].dmc_num > DDR_DMC_PER_PHY_MAX) ++ return -1; ++ ++ for (i = 0; i < cfg->phy[cfg->phy_idx].dmc_num; i++) { ++ cfg->dmc_idx = i; ++ cfg->cur_dmc = cfg->phy[cfg->phy_idx].dmc[i].addr; ++ cfg->cur_pattern = cfg->phy[cfg->phy_idx].dmc[i].ddrt_pattern; ++ result += ddr_training_by_dmc(cfg); ++ } ++ ++ return result; ++} ++ ++static int ddr_training_by_phy(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ unsigned int phy_mask; ++ unsigned int rank_num; ++ ++ phy_mask = 1 << (cfg->phy_idx); ++ rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ cfg->cur_item = cfg->phy[cfg->phy_idx].rank[i].item; ++ if (ddr_training_check_bypass(cfg, phy_mask) != DDR_FALSE) ++ continue; ++ result += ddr_training_by_rank(cfg); ++ } ++ ++ return result; ++} ++ ++int ddr_training_all(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ if (cfg == NULL) ++ return -1; ++ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ result += ddr_training_by_phy(cfg); ++ } ++ ++ return result; ++} ++ ++/* DDR training phy/dmc/dram_type config init */ ++static void ddr_training_cfg_set_dmc(struct ddr_cfg_st *cfg) ++{ ++ unsigned int ddrt_pattern; ++ ++ if (cfg->phy[0].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ cfg->phy[0].dmc_num = 2; /* lpddr4: 2 dmc per phy */ ++ ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN); ++ cfg->phy[0].dmc[0].addr = DDR_REG_BASE_DMC0; ++ cfg->phy[0].dmc[0].ddrt_pattern = ddrt_pattern & 0xffff; /* bit[15-0]:dmc0 ddrt pattern */ ++ cfg->phy[0].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC0); ++ cfg->phy[0].dmc[1].addr = DDR_REG_BASE_DMC1; ++ cfg->phy[0].dmc[1].ddrt_pattern = ddrt_pattern >> 16; /* bit[31-16]:dmc1 ddrt pattern */ ++ cfg->phy[0].dmc[1].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC1); ++ cfg->phy[0].total_byte_num = cfg->phy[0].dmc[0].byte_num + cfg->phy[0].dmc[1].byte_num; ++ } else { ++ cfg->phy[0].dmc_num = 1; /* other: 1 dmc per phy */ ++ cfg->phy[0].dmc[0].addr = DDR_REG_BASE_DMC0; ++ cfg->phy[0].dmc[0].ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN); ++ cfg->phy[0].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC0); ++ cfg->phy[0].total_byte_num = cfg->phy[0].dmc[0].byte_num; ++ } ++ ddr_info("phy[0] total_byte_num[%x] dram_type[%x]", cfg->phy[0].total_byte_num, cfg->phy[0].dram_type); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ if (cfg->phy[1].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ cfg->phy[1].dmc_num = 2; /* lpddr4: 2 dmc per phy */ ++ ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_SEC); ++ cfg->phy[1].dmc[0].addr = DDR_REG_BASE_DMC2; ++ cfg->phy[1].dmc[0].ddrt_pattern = ddrt_pattern & 0xffff; /* bit[15-0]:dmc0 ddrt pattern */ ++ cfg->phy[1].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC2); ++ cfg->phy[1].dmc[1].addr = DDR_REG_BASE_DMC3; ++ cfg->phy[1].dmc[1].ddrt_pattern = ddrt_pattern >> 16; /* bit[31-16]:dmc1 ddrt pattern */ ++ cfg->phy[1].dmc[1].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC3); ++ cfg->phy[1].total_byte_num = cfg->phy[1].dmc[0].byte_num + cfg->phy[1].dmc[1].byte_num; ++ } else { ++ cfg->phy[1].dmc_num = 1; /* other: 1 dmc per phy */ ++ cfg->phy[1].dmc[0].addr = DDR_REG_BASE_DMC1; ++ cfg->phy[1].dmc[0].ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_SEC); ++ cfg->phy[1].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC1); ++ cfg->phy[1].total_byte_num = cfg->phy[1].dmc[0].byte_num; ++ } ++ ddr_info("phy[1] total_byte_num[%x] dram_type[%x]", cfg->phy[1].total_byte_num, cfg->phy[1].dram_type); ++#endif ++} ++ ++static void ddr_training_cfg_set_rank(struct ddr_cfg_st *cfg) ++{ ++ cfg->phy[0].rank_num = 1; ++ cfg->phy[0].rank[0].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ cfg->phy[0].rank[0].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK0); ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ cfg->phy[0].rank[1].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC); ++#endif ++ cfg->phy[0].rank[1].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1); ++ ++ if (reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1)) ++ cfg->phy[0].rank_num = 2; /* rank number equal 2 if SYSCTRL_DDR_HW_PHY0_RANK1 has bean define in boot table */ ++ ++ ddr_info("Rank number PHY0 [%x]", cfg->phy[0].rank_num); ++ ddr_info("HW training item PHY0[%x = %x][%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK0), cfg->phy[0].rank[0].item_hw, ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1), cfg->phy[0].rank[1].item_hw); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ cfg->phy[1].rank_num = 1; ++ cfg->phy[1].rank[0].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ cfg->phy[1].rank[0].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK0); ++ ++ cfg->phy[1].rank[1].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC); ++ cfg->phy[1].rank[1].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1); ++ ++ if (reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1)) ++ cfg->phy[1].rank_num = 2; /* rank number equal 2 if SYSCTRL_DDR_HW_PHY1_RANK1 has bean define in boot table */ ++ ++ ddr_info("Rank number PHY1[%x]", cfg->phy[1].rank_num); ++ ddr_info("HW training item PHY1[%x = %x][%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK0), cfg->phy[1].rank[0].item_hw, ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1), cfg->phy[1].rank[1].item_hw); ++#endif ++ ++ ddr_info("SW training item Rank0[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG), cfg->phy[0].rank[0].item); ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ ddr_info("SW training item Rank1[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC), cfg->phy[0].rank[1].item); ++#endif ++} ++ ++static void ddr_training_cfg_set_phy(struct ddr_cfg_st *cfg) ++{ ++ cfg->phy_num = DDR_PHY_NUM; ++ cfg->phy[0].addr = DDR_REG_BASE_PHY0; ++ cfg->phy[0].dram_type = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_DRAMCFG) & ++ PHY_DRAMCFG_TYPE_MASK; ++#ifdef DDR_REG_BASE_PHY1 ++ cfg->phy[1].addr = DDR_REG_BASE_PHY1; ++ cfg->phy[1].dram_type = reg_read(DDR_REG_BASE_PHY1 + DDR_PHY_DRAMCFG) & ++ PHY_DRAMCFG_TYPE_MASK; ++#endif ++} ++ ++void ddr_training_cfg_init(struct ddr_cfg_st *cfg) ++{ ++ memset(cfg, 0, sizeof(struct ddr_cfg_st)); ++ ddr_training_cfg_set_phy(cfg); ++ ddr_training_cfg_set_dmc(cfg); ++ ddr_training_cfg_set_rank(cfg); ++} ++ ++/* 2GHz CPU run 2000 "nop" in 1 ns */ ++static inline void ddr_training_delay(unsigned int cnt) ++{ ++ while (cnt--) ++ asm("nop"); ++} ++ ++/* set auto refresh */ ++void ddr_training_set_timing(unsigned int base_dmc, unsigned int timing) ++{ ++ ddr_training_delay(DDR_AUTO_TIMING_DELAY); ++ reg_write(timing, base_dmc + DDR_DMC_TIMING2); ++ /* need to delay 1 ns */ ++ ddr_training_delay(DDR_AUTO_TIMING_DELAY); ++} ++ ++#ifdef DDR_TRAINING_STAT_CONFIG ++/* Save training result in stat register */ ++static void ddr_training_save(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ unsigned int stat; ++ unsigned int phy_index; ++ ++ stat = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT); ++ /* only record the first error */ ++ if (stat) ++ return; ++ ++ stat = mask; ++ if (phy != 0) { ++ phy_index = ((phy == DDR_REG_BASE_PHY0) ? DDR_ERR_PHY0 : DDR_ERR_PHY1); ++ stat |= phy_index; ++ } ++ ++ if (byte != -1) ++ stat |= ((unsigned int)byte << DDR_ERR_BYTE_BIT); ++ ++ if (dq != -1) ++ stat |= ((unsigned int)dq << DDR_ERR_DQ_BIT); ++ ++ reg_write(stat, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT); ++} ++#endif ++ ++/* Record error code in register */ ++void ddr_training_stat(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ ddr_training_error(mask, phy, byte, dq); ++#ifdef DDR_TRAINING_STAT_CONFIG ++ ddr_training_save(mask, phy, byte, dq); ++#endif ++} ++ ++/* Check DDR training item whether by pass */ ++int ddr_training_check_bypass(const struct ddr_cfg_st *cfg, unsigned int mask) ++{ ++ /* training item disable */ ++ if ((cfg->cur_item) & mask) { ++ ddr_debug("DDR training [%x] is disable, rank[%x] cfg[%x]", ++ mask, cfg->rank_idx, cfg->cur_item); ++ return DDR_TRUE; ++ } else { ++ return DDR_FALSE; ++ } ++} ++ ++#if !defined(DDR_TRAINING_CUT_CODE_CONFIG) || defined(DDR_TRAINING_CMD) ++/* Save register value before training */ ++void ddr_training_save_reg(const struct ddr_cfg_st *cfg, ++ struct tr_relate_reg *relate_reg, unsigned int mask) ++{ ++ unsigned int base_dmc; ++ unsigned int base_phy; ++ ++ base_dmc = cfg->cur_dmc; ++ base_phy = cfg->cur_phy; ++ ++ /* save reg value */ ++ relate_reg->auto_ref_timing = reg_read(base_dmc + DDR_DMC_TIMING2); ++ relate_reg->power_down = reg_read(base_dmc + DDR_DMC_CFG_PD); ++ relate_reg->misc_scramb = reg_read(base_phy + DDR_PHY_MISC); ++ /* Static register have to read two times to get the right value. */ ++ relate_reg->ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL4); ++ relate_reg->ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL4); ++ ++ /* set new value */ ++ switch (mask) { ++ case DDR_BYPASS_WL_MASK: ++ case DDR_BYPASS_LPCA_MASK: ++ /* disable auto refresh */ ++ ddr_training_set_timing(base_dmc, relate_reg->auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ break; ++ case DDR_BYPASS_GATE_MASK: ++ /* disable auto refresh */ ++ ddr_training_set_timing(base_dmc, relate_reg->auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ if (!(reg_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) /* set 1T */ ++ reg_write(0x0, base_phy + DDR_PHY_ACPHYCTL4); ++ break; ++ case DDR_BYPASS_HW_MASK: ++ if (!(reg_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) /* set 1T */ ++ reg_write(0x0, base_phy + DDR_PHY_ACPHYCTL4); ++ break; ++ default: ++ break; ++ } ++ ++ reg_write(relate_reg->power_down & DMC_POWER_DOWN_DIS, ++ base_dmc + DDR_DMC_CFG_PD); ++ reg_write(relate_reg->misc_scramb & PHY_MISC_SCRAMB_DIS, ++ base_phy + DDR_PHY_MISC); ++ ++ ddr_dqsswap_save_func(relate_reg->swapdfibyte_en, base_phy); ++ ++ ddr_axi_save_func(relate_reg); ++ ++ ddr_rnkvol_save_func(relate_reg, base_dmc); ++ ++ /* save customer reg */ ++ ddr_training_save_reg_func((void *)relate_reg, mask); ++ ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_asm_dsb(); ++} ++ ++/* Restore register value after training */ ++void ddr_training_restore_reg(const struct ddr_cfg_st *cfg, ++ const struct tr_relate_reg *relate_reg) ++{ ++ unsigned int base_dmc; ++ unsigned int base_phy; ++ ++ base_dmc = cfg->cur_dmc; ++ base_phy = cfg->cur_phy; ++ ++ /* enable auto refresh */ ++ ddr_training_set_timing(base_dmc, relate_reg->auto_ref_timing); ++ reg_write(relate_reg->power_down, base_dmc + DDR_DMC_CFG_PD); ++ reg_write(relate_reg->misc_scramb, base_phy + DDR_PHY_MISC); ++ if (!(reg_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) ++ reg_write(relate_reg->ac_phy_ctl, base_phy + DDR_PHY_ACPHYCTL4); ++ ++ ddr_dqsswap_restore_func(relate_reg->swapdfibyte_en, base_phy); ++ ++ ddr_axi_restore_func(relate_reg); ++ ++ ddr_rnkvol_restore_func(relate_reg, base_dmc); ++ ++ /* restore customer reg */ ++ ddr_training_restore_reg_func((void *)relate_reg); ++ ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_asm_dsb(); ++} ++ ++/* Switch AXI to DMC0/DMC1/DMC2/DMC3 for DDRT test */ ++void ddr_training_switch_axi(const struct ddr_cfg_st *cfg) ++{ ++ ddr_axi_switch_func(cfg); ++ ddr_debug("AXI region0[%x = %x]", ++ (DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0), ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0)); ++ ddr_debug("AXI region1[%x = %x]", ++ (DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1), ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1)); ++ ++ ddr_rnkvol_set_func(cfg); ++} ++#endif ++ ++#if defined(DDR_WL_TRAINING_CONFIG) || defined(DDR_MPR_TRAINING_CONFIG) ++/* Excute DMC sfc command. */ ++static void ddr_dmc_sfc_cmd(unsigned int base_dmc, unsigned int sfc_cmd, ++ unsigned int sfc_addr, unsigned int sfc_bank) ++{ ++ unsigned int count = 0; ++ ++ /* set sfc cmd */ ++ dmc_sfc_cmd_write(sfc_cmd, base_dmc + DDR_DMC_SFCCMD); ++ /* set col and row */ ++ reg_write(sfc_addr, base_dmc + DDR_DMC_SFCADDR); ++ /* set bank */ ++ dmc_sfc_bank_write(sfc_bank, base_dmc + DDR_DMC_SFCBANK); ++ /* excute cmd */ ++ reg_write(0x1, base_dmc + DDR_DMC_SFCREQ); ++ ++ ddr_asm_dsb(); ++ ++ while (count < DDR_SFC_WAIT_TIMEOUT) { /* wait command finished */ ++ if (!(reg_read(base_dmc + DDR_DMC_SFCREQ) & 0x1)) ++ break; ++ count++; ++ } ++ ++ if (count >= DDR_HWR_WAIT_TIMEOUT) ++ ddr_error("SFC cmd wait timeout"); ++} ++#endif ++ ++#if defined(DDR_HW_TRAINING_CONFIG) || defined(DDR_DCC_TRAINING_CONFIG) ++/* Exit or enter auto self-refresh */ ++static int ddr_training_easr(unsigned int base_dmc, unsigned int sref_req) ++{ ++ unsigned int count; ++ ++ count = DDR_HWR_WAIT_TIMEOUT; ++ if (sref_req == DDR_EXIT_SREF) { ++ /* Exit Auto-self refresh */ ++ reg_write(DMC_CTRL_SREF_EXIT, base_dmc + DDR_DMC_CTRL_SREF); ++ while (count--) { ++ if (!(reg_read(base_dmc + DDR_DMC_CURR_FUNC) & ++ DMC_CURR_FUNC_IN_SREF_MASK)) ++ break; ++ } ++ } else if (sref_req == DDR_ENTER_SREF) { ++ /* Enter Auto-self refresh */ ++ reg_write(DMC_CTRL_SREF_ENTER, base_dmc + DDR_DMC_CTRL_SREF); ++ while (count--) { ++ if (reg_read(base_dmc + DDR_DMC_CURR_FUNC) & ++ DMC_CURR_FUNC_IN_SREF_MASK) ++ break; ++ } ++ } ++ ++ if (count == 0xffffffff) { ++ ddr_fatal("SREF wait timeout"); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, -1, -1, -1); ++ return -1; ++ } ++ return 0; ++} ++ ++/* DDR hw/dcc training exit or enter auto self-refresh */ ++int ddr_training_ctrl_easr(const struct ddr_cfg_st *cfg, unsigned int sref_req) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ ++ for (i = 0; i < phy_st->dmc_num; i++) ++ result += ddr_training_easr(phy_st->dmc[i].addr, sref_req); ++ ++ return result; ++} ++ ++void ddr_training_save_timing(const struct ddr_cfg_st *cfg, struct ddr_timing_st *timing_st) ++{ ++ unsigned int i; ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ ++ for (i = 0; i < phy_st->dmc_num; i++) { ++ timing_st->val[i] = reg_read(phy_st->dmc[i].addr + DDR_DMC_TIMING2); ++ /* disable auto refresh */ ++ ddr_training_set_timing(phy_st->dmc[i].addr, timing_st->val[i] & DMC_AUTO_TIMING_DIS); ++ } ++} ++ ++void ddr_training_restore_timing(const struct ddr_cfg_st *cfg, ++ const struct ddr_timing_st *timing_st) ++{ ++ unsigned int i; ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ ++ for (i = 0; i < phy_st->dmc_num; i++) ++ ddr_training_set_timing(phy_st->dmc[i].addr, timing_st->val[i]); ++} ++#endif /* DDR_HW_TRAINING_CONFIG || DDR_DCC_TRAINING_CONFIG */ ++ ++/* ++ * config ddrc exit self-refresh or exit powerdown ++ * bit[3] 0x1:exit self-refresh ++ * bit[3] 0x0:exit powerdown ++ */ ++void ddr_sref_cfg(const struct ddr_cfg_st *cfg, struct dmc_cfg_sref_st *cfg_sref, unsigned int value) ++{ ++ unsigned int i; ++ const struct ddr_phy_st *phy_st = NULL; ++ ++ phy_st = &cfg->phy[cfg->phy_idx]; ++ for (i = 0; i < phy_st->dmc_num; i++) { ++ cfg_sref->val[i] = reg_read(phy_st->dmc[i].addr + DDR_DMC_CFG_SREF); ++ reg_write((cfg_sref->val[i] & (~DMC_CFG_INIT_XSREF_PD_MASK)) | value, ++ phy_st->dmc[i].addr + DDR_DMC_CFG_SREF); ++ } ++} ++ ++/* Restore DMC_CFG_SREF config */ ++void ddr_sref_cfg_restore(const struct ddr_cfg_st *cfg, const struct dmc_cfg_sref_st *cfg_sref) ++{ ++ unsigned int i; ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ ++ for (i = 0; i < phy_st->dmc_num; i++) ++ reg_write(cfg_sref->val[i], phy_st->dmc[i].addr + DDR_DMC_CFG_SREF); ++} ++ ++/* ++ * PHY reset ++ * To issue reset signal to PHY, this field should be set to a '1'. ++ */ ++void ddr_phy_reset(unsigned int base_phy) ++{ ++ unsigned int tmp; ++ ++ tmp = reg_read(base_phy + DDR_PHY_PHYINITCTRL); ++ /* set 1 to issue PHY reset signal */ ++ tmp |= (1 << PHY_RST_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_PHYINITCTRL); ++ /* set 0 to end the PHY reset signal */ ++ tmp &= ~(1 << PHY_RST_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_PHYINITCTRL); ++} ++ ++/* ++ * Update delay setting in registers to PHY immediately. ++ * Make delay setting take effect. ++ */ ++void ddr_phy_cfg_update(unsigned int base_phy) ++{ ++ unsigned int tmp; ++ ++ tmp = reg_read(base_phy + DDR_PHY_MISC); ++ tmp |= (1 << PHY_MISC_UPDATE_BIT); ++ /* update new config to PHY */ ++ reg_write(tmp, base_phy + DDR_PHY_MISC); ++ tmp &= ~(1 << PHY_MISC_UPDATE_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_MISC); ++ tmp = reg_read(base_phy + DDR_PHY_PHYINITCTRL); ++ /* set 1 to issue PHY counter reset signal */ ++ tmp |= (1 << PHY_PHYCONN_RST_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_PHYINITCTRL); ++ /* set 0 to end the reset signal */ ++ tmp &= ~(1 << PHY_PHYCONN_RST_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_PHYINITCTRL); ++ ++ ddr_asm_dsb(); ++} ++ ++/* Set delay value of the bit delay line of the DATA block */ ++void ddr_phy_set_dq_bdl(const struct ddr_cfg_st *cfg, unsigned int value) ++{ ++ unsigned int val; ++ unsigned int offset; ++ unsigned int dq; ++ unsigned int base_phy; ++ unsigned int byte_index; ++ unsigned int rank; ++ ++ base_phy = cfg->cur_phy; ++ byte_index = cfg->cur_byte; ++ rank = cfg->rank_idx; ++ dq = cfg->cur_dq & 0x7; ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNWDQNBDL0] 4 bdl: wdq0bdl-wdq3bdl */ ++ offset = ddr_phy_dxnwdqnbdl0(rank, byte_index); ++ else /* [DXNWDQNBDL1] 4 bdl: wdq4bdl-wdq7bdl */ ++ offset = ddr_phy_dxnwdqnbdl1(rank, byte_index); ++ } else { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNRDQNBDL0] 4 bdl: rdq0bdl-rdq3bdl */ ++ offset = ddr_phy_dxnrdqnbdl0(rank, byte_index); ++ else /* [DXNRDQNBDL1] 4 bdl: rdq4bdl-rdq7bdl */ ++ offset = ddr_phy_dxnrdqnbdl1(rank, byte_index); ++ } ++ dq &= 0x3; /* one register contains 4 dq */ ++ val = reg_read(base_phy + offset); ++ val &= ~(0xFF << (dq << DDR_DQBDL_SHIFT_BIT)); ++ val |= ((PHY_BDL_MASK & value) << ((dq << DDR_DQBDL_SHIFT_BIT) + PHY_BDL_DQ_BIT)); ++ reg_write(val, base_phy + offset); ++ ++ ddr_phy_cfg_update(base_phy); ++} ++ ++/* Get PHY DQ value */ ++unsigned int ddr_phy_get_dq_bdl(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int val; ++ unsigned int offset; ++ unsigned int dq; ++ unsigned int byte_index; ++ unsigned int rank; ++ ++ byte_index = cfg->cur_byte; ++ rank = cfg->rank_idx; ++ dq = cfg->cur_dq & 0x7; ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNWDQNBDL0] 4 bdl: wdq0bdl-wdq3bdl */ ++ offset = ddr_phy_dxnwdqnbdl0(rank, byte_index); ++ else /* [DXNWDQNBDL1] 4 bdl: wdq4bdl-wdq7bdl */ ++ offset = ddr_phy_dxnwdqnbdl1(rank, byte_index); ++ } else { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNRDQNBDL0] 4 bdl: rdq0bdl-rdq3bdl */ ++ offset = ddr_phy_dxnrdqnbdl0(rank, byte_index); ++ else /* [DXNRDQNBDL1] 4 bdl: rdq4bdl-rdq7bdl */ ++ offset = ddr_phy_dxnrdqnbdl1(rank, byte_index); ++ } ++ ++ dq &= 0x3; /* one register contains 4 dq */ ++ val = (reg_read(cfg->cur_phy + offset) >> ++ ((dq << DDR_DQBDL_SHIFT_BIT) + PHY_BDL_DQ_BIT)) & PHY_BDL_MASK; ++ ++ return val; ++} ++ ++/* Get byte number */ ++unsigned int ddr_phy_get_byte_num(unsigned int base_dmc) ++{ ++ unsigned int byte_num; ++ ++ /* memery width -> byte number */ ++ byte_num = ((reg_read(base_dmc + DDR_DMC_CFG_DDRMODE) >> ++ DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK) << 1; ++ /* for codedex */ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ byte_num = DDR_PHY_BYTE_MAX; ++ ddr_error("get byte num fail"); ++ } ++ ++ return byte_num; ++} ++ ++static void ddr_rdqs_sync_rdm(const struct ddr_cfg_st *cfg, int offset) ++{ ++ unsigned int rdqnbdl; ++ int rdm; ++ ++ rdqnbdl = reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++ rdm = (rdqnbdl >> PHY_RDM_BDL_BIT) & PHY_RDM_BDL_MASK; ++ rdm += offset; ++ rdm = ((rdm < 0) ? 0 : rdm); ++ rdm = ((rdm > PHY_RDM_BDL_MASK) ? PHY_RDM_BDL_MASK : rdm); ++ rdqnbdl = rdqnbdl & (~(PHY_RDM_BDL_MASK << PHY_RDM_BDL_BIT)); ++ reg_write(rdqnbdl | ((unsigned int)rdm << PHY_RDM_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++} ++ ++static void ddr_rdqs_sync_rank_rdq(struct ddr_cfg_st *cfg, int offset) ++{ ++ int dq_val; ++ int i; ++ unsigned int cur_mode = cfg->cur_mode; ++ ++ cfg->cur_mode = DDR_MODE_READ; ++ ++ /* sync other rank rdm */ ++ ddr_rdqs_sync_rdm(cfg, offset); ++ ++ /* sync other rank rdq */ ++ ddr_debug("Before sync rank[%x] byte[%x] dq[%x = %x][%x = %x] offset[%x]", ++ cfg->rank_idx, cfg->cur_byte, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte)), ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte)), offset); ++ ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ dq_val = (int)ddr_phy_get_dq_bdl(cfg); ++ dq_val += offset; ++ dq_val = ((dq_val < 0) ? 0 : dq_val); ++ dq_val = ((dq_val > PHY_BDL_MASK) ? PHY_BDL_MASK : dq_val); ++ ddr_phy_set_dq_bdl(cfg, dq_val); ++ } ++ ++ cfg->cur_mode = cur_mode; /* restore to current mode */ ++ ++ ddr_debug("After sync rank[%x] byte[%x] dq[%x = %x][%x = %x]", ++ cfg->rank_idx, cfg->cur_byte, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte)), ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte))); ++} ++ ++static void ddr_rdqbdl_adj(struct ddr_cfg_st *cfg, struct ddr_bdl_dly_st *bdl_dly_s) ++{ ++ int i; ++ const int value_num = 10; ++ unsigned int rank = cfg->rank_idx; ++ unsigned int min = 0xffffffff; ++ unsigned int rdm, rdqs; ++ unsigned int cur_mode = cfg->cur_mode; ++ ++ cfg->cur_mode = DDR_MODE_READ; ++ ++ rdm = reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl2(rank, cfg->cur_byte)); ++ rdqs = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ++ /* get dq value */ ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ bdl_dly_s->value[i] = ddr_phy_get_dq_bdl(cfg); ++ } ++ bdl_dly_s->value[8] = (rdm >> PHY_RDM_BDL_BIT) & PHY_RDM_BDL_MASK; /* bdl[8]: save rdmbdl */ ++ bdl_dly_s->value[9] = (rdqs >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; /* bdl[9]: rdqsbdl */ ++ ++ for (i = 0; i < value_num; i++) { ++ if (bdl_dly_s->value[i] < min) ++ min = bdl_dly_s->value[i]; ++ } ++ ++ /* subtract minimum */ ++ for (i = 0; i < value_num; i++) ++ bdl_dly_s->value[i] = bdl_dly_s->value[i] - min; ++ ++ /* set dq value */ ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ ddr_phy_set_dq_bdl(cfg, bdl_dly_s->value[i]); ++ } ++ ++ rdm = (rdm & (~(PHY_RDM_BDL_MASK << PHY_RDM_BDL_BIT))) | ++ (bdl_dly_s->value[8] << PHY_RDM_BDL_BIT); /* bdl[8]: save rdmbdl */ ++ rdqs = (rdqs & (~(PHY_RDQS_BDL_MASK << PHY_RDQS_BDL_BIT))) | ++ (bdl_dly_s->value[9] << PHY_RDQS_BDL_BIT); /* bdl[9]: rdqsbdl */ ++ ++ reg_write(rdm, cfg->cur_phy + ddr_phy_dxnrdqnbdl2(rank, cfg->cur_byte)); ++ reg_write(rdqs, cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ++ cfg->cur_mode = cur_mode; /* restore to current mode */ ++} ++ ++#ifdef DDR_TRAINING_DEBUG ++#define ddr_trining_break_point_func(name) ddr_training_break_point(name) ++#else ++#define ddr_trining_break_point_func(name) ++#endif ++ ++void ddr_training_break_point(const char* name) ++{ ++ ddr_info(name); ++ ddr_training_console_if(); ++} ++ ++#define __DDRT__ ++#ifdef DDR_DDRT_SPECIAL_CONFIG ++/* Some special DDRT need read register repeatedly */ ++static unsigned int ddr_ddrt_read(unsigned int addr) ++{ ++ int times = 0; ++ unsigned int data0, data1, data2; ++ do { ++ data0 = reg_read(addr); ++ data1 = reg_read(addr); ++ data2 = reg_read(addr); ++ times++; ++ } while (((data0 != data1) || (data1 != data2)) && ++ (times < DDRT_READ_TIMEOUT)); ++ ++ if (times >= DDRT_READ_TIMEOUT) { ++ ddr_fatal("DDRT wait timeout"); ++ ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, 0, -1, -1); ++ } ++ ++ return data0; ++} ++ ++/* Some special DDRT need write twice register */ ++static void ddr_ddrt_write(unsigned int data, unsigned int addr) ++{ ++ unsigned int tmp; ++ tmp = reg_read(addr); ++ reg_write(data, addr); ++ reg_write(data, addr); ++} ++#endif /* DDR_DDRT_SPECIAL_CONFIG */ ++ ++static unsigned int ddr_get_rank_size(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int base_dmc = cfg->cur_dmc; ++ unsigned int rnkvol; ++ unsigned int mem_bank, mem_row, mem_col, mem_width; ++ unsigned int size; ++ ++ mem_width = (reg_read(base_dmc + DDR_DMC_CFG_DDRMODE) >> DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK; ++ rnkvol = reg_read(base_dmc + ddr_dmc_cfg_rnkvol(0)); ++ mem_bank = (rnkvol >> DMC_RNKVOL_MEM_BANK_BIT) & DMC_RNKVOL_MEM_BANK_MASK; ++ mem_row = (rnkvol >> DMC_RNKVOL_MEM_ROW_BIT) & DMC_RNKVOL_MEM_ROW_MASK; ++ mem_col = rnkvol & DMC_RNKVOL_MEM_COL_MASK; ++ ++ /* ++ * mem_bank ++ * 0: 4 Bank(2 bank address) ++ * 1: 8 Bank(3 bank address) ++ * 2: 16 BANK(4 bank address) ++ * 3: reserved ++ * mem_row ++ * 000: 11 bit ++ * 001: 12 bit ++ * 010: 13 bit ++ * 011: 14 bit ++ * 100: 15 bit ++ * 101: 16 bit ++ * 110: 17 bit ++ * 111: 18 bit ++ * mem_width ++ * 000: 8 bit ++ * 001: 9 bit ++ * 010: 10 bit ++ * 011: 11 bit ++ * 100: 12 bit ++ */ ++ size = 1UL << ((mem_bank + 2) + (mem_row + 11) + (mem_col + 8) + mem_width); /* 2:2 bank; 11:11 bit; 8:8 bit */ ++ ddr_debug("rank size[%x]", size); ++ ++ return size; ++} ++ ++/* Init DDRT register before DDRT test */ ++void ddr_ddrt_init(const struct ddr_cfg_st *cfg, unsigned int mode) ++{ ++ unsigned int mem_width; ++ unsigned int mem_config; ++ unsigned int offset = 0; ++ ++ if (cfg->rank_idx == 1) ++ offset = ddr_get_rank_size(cfg); ++ ++ ddr_training_ddrt_prepare_func(); ++ ++ mem_width = ((reg_read(cfg->cur_dmc + DDR_DMC_CFG_DDRMODE) >> ++ DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK); ++ mem_config = ((mem_width - 1) << DDRT_DDR_MEM_WIDTH) | ++ DDRT_DDR_COL_WIDTH | DDRT_DDR_ROW_WIDTH | DDRT_DDR_BANK_WIDTH; ++ /* DDRT SDRAM config */ ++ ddrt_reg_write(mem_config, DDR_REG_BASE_DDRT + DDRT_MEM_CONFIG); ++ /* DDR Address Base */ ++ ddrt_reg_write(ddrt_get_test_addr(DDRT_CFG_BASE_ADDR), ++ DDR_REG_BASE_DDRT + DDRT_DDR_BASE_ADDR); ++ /* DDRT test DDR using space */ ++ ddrt_reg_write(ddrt_get_test_addr(ddr_ddrt_get_test_addr() + offset), ++ DDR_REG_BASE_DDRT + DDRT_ADDR); ++ ddrt_reg_write(DDRT_CFG_SEED, DDR_REG_BASE_DDRT + DDRT_SEED); ++ ++ if (mode == DDR_DDRT_MODE_GATE) { ++ /* Read or Write Once */ ++ ddrt_reg_write(DDRT_CFG_BURST_CFG_GATE, ++ DDR_REG_BASE_DDRT + DDRT_BURST_CONFIG); ++ ddrt_reg_write(0x0, DDR_REG_BASE_DDRT + DDRT_BURST_NUM); ++ ddrt_reg_write(0x0, DDR_REG_BASE_DDRT + DDRT_ADDR_NUM); ++ ddrt_reg_write(0x0, DDR_REG_BASE_DDRT + DDRT_LOOP_NUM); ++ ddrt_reg_write(DDRT_CFG_REVERSED, ++ DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ } else { ++ /* reversed data form register init table */ ++ /* 128bit BURST4 */ ++ ddrt_reg_write(DDRT_CFG_BURST_CFG_DATAEYE, ++ DDR_REG_BASE_DDRT + DDRT_BURST_CONFIG); ++ ddrt_reg_write(cfg->phy[cfg->phy_idx].dmc[cfg->dmc_idx].ddrt_pattern, ++ DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ ddrt_reg_write(DDRT_CFG_BURST_NUM, ++ DDR_REG_BASE_DDRT + DDRT_BURST_NUM); ++ ddrt_reg_write(DDRT_CFG_ADDR_NUM, ++ DDR_REG_BASE_DDRT + DDRT_ADDR_NUM); ++ ddrt_reg_write(DDRT_CFG_LOOP_NUM, ++ DDR_REG_BASE_DDRT + DDRT_LOOP_NUM); ++ } ++ ++ ddr_debug("DDRT ADDR[%x = %x]", (DDR_REG_BASE_DDRT + DDRT_ADDR), ++ reg_read(DDR_REG_BASE_DDRT + DDRT_ADDR)); ++} ++ ++static int ddr_ddrt_test_process(int byte, int dq) ++{ ++ unsigned int err_ovfl; ++ unsigned int err_cnt; ++ unsigned int dq_num; ++ unsigned int dq_tmp; ++ ++ if (dq != -1) { /* check for dq */ ++ dq_num = ((unsigned int)byte << DDR_BYTE_DQ) + dq; ++ err_ovfl = ddrt_reg_read(DDR_REG_BASE_DDRT + ++ DDRT_DQ_ERR_OVFL) & (1 << dq_num); ++ if (err_ovfl) ++ return -1; ++ ++ if (dq > 3) /* 3: dq0-dq3 */ ++ dq_tmp = (unsigned int)(dq - 4) << DDR_BYTE_DQ; /* 4 dq: dq0-dq3,dq4-dq7 */ ++ else ++ dq_tmp = (unsigned int)dq << DDR_BYTE_DQ; ++ ++ err_cnt = ddrt_reg_read(DDR_REG_BASE_DDRT + ++ ddrt_dq_err_cnt(((unsigned int)byte << 1) + ((unsigned int)dq >> 2))); /* shift left 2: 4 dq */ ++ err_cnt = err_cnt & (0xff << dq_tmp); ++ if (err_cnt) ++ return -1; ++ } else if (byte != -1) { /* check for byte */ ++ err_ovfl = ddrt_reg_read(DDR_REG_BASE_DDRT + ++ DDRT_DQ_ERR_OVFL) & (0xff << ((unsigned int)byte << DDR_BYTE_DQ)); ++ if (err_ovfl) ++ return -1; ++ ++ err_cnt = ddrt_reg_read(DDR_REG_BASE_DDRT + ++ ddrt_dq_err_cnt((unsigned int)byte << 1)); ++ err_cnt += ddrt_reg_read(DDR_REG_BASE_DDRT + ++ ddrt_dq_err_cnt(((unsigned int)byte << 1) + 1)); ++ if (err_cnt) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * @mask : DDRT option mask. ++ * @byte : DDR byte index. ++ * @dq : DDR dq index. ++ * DDRT test. Support read_only mode and write_read_compare mode. ++ * Success return 0, fail return -1. ++ */ ++int ddr_ddrt_test(unsigned int mask, int byte, int dq) ++{ ++ int result; ++ unsigned int regval; ++ unsigned int times = 0; ++ ++ ddrt_reg_write(mask | DDRT_CFG_START, DDR_REG_BASE_DDRT + DDRT_OP); ++ ddrt_reg_write(0, DDR_REG_BASE_DDRT + DDRT_STATUS); ++ ++ ddr_asm_dsb(); ++ ++ do { ++ regval = ddrt_reg_read(DDR_REG_BASE_DDRT + DDRT_STATUS); ++ times++; ++ } while ((!(regval & DDRT_TEST_DONE_MASK)) && (times < DDRT_WAIT_TIMEOUT)); ++ ++ if (times >= DDRT_WAIT_TIMEOUT) { ++ ddr_fatal("DDRT wait timeout"); ++ ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, 0, -1, -1); ++ return -1; ++ } ++ ++ /* DDRT_READ_ONLY_MODE */ ++ if ((mask & DDRT_TEST_MODE_MASK) == DDRT_READ_ONLY_MODE) ++ return 0; /* return when DDRT finish */ ++ ++ /* DDRT_WR_COMPRARE_MODE No error occurred, test pass. */ ++ if (regval & DDRT_TEST_PASS_MASK) ++ return 0; ++ ++ result = ddr_ddrt_test_process(byte, dq); ++ ++ return result; ++} ++ ++/* Check ddrt test result. Success return 0, fail return -1 */ ++static int ddr_ddrt_check(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int byte_index_to_dmc = cfg->cur_byte; ++ ++ /* ddrt test the byte relate to dmc, make sure not overflow */ ++ if (cfg->cur_byte >= (cfg->dmc_idx << 1)) ++ byte_index_to_dmc = cfg->cur_byte - (cfg->dmc_idx << 1); ++ ++ ddrt_reg_write(0, DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE | DDRT_PATTERM_PRBS9, byte_index_to_dmc, cfg->cur_dq)) ++ return -1; ++ ++ ddrt_reg_write(cfg->cur_pattern, DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE | DDRT_PATTERM_PRBS11, byte_index_to_dmc, cfg->cur_dq)) ++ return -1; ++ ++ return 0; ++} ++ ++#define __DATAEYE_ADJUST__ ++#ifdef DDR_TRAINING_ADJUST_CONFIG ++static unsigned int ddr_adjust_get_average(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int dq0_3, dq4_7, val; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_index = cfg->cur_byte; ++ unsigned int rank = cfg->rank_idx; ++ ++ if (cfg->cur_mode == DDR_MODE_WRITE) ++ return (reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank, byte_index)) >> ++ PHY_WDM_BDL_BIT) & PHY_BDL_MASK; ++ ++ /* read */ ++ dq0_3 = reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank, byte_index)); ++ dq4_7 = reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank, byte_index)); ++ ++ val = ((dq0_3 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK) + ++ ((dq0_3 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK) + ++ ((dq0_3 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK) + ++ ((dq0_3 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK); ++ ++ val = val >> 3; /* shift 3: 8 dq */ ++ ++ return val; ++} ++ ++/* ++ * @accel : Return a value to adjust quickly. ++ * Check dataeye DQ window on left or right or middle. ++ */ ++static unsigned int ddr_adjust_trend_check(const struct ddr_cfg_st *cfg, int *accel) ++{ ++ unsigned int dq_bdl; ++ unsigned int size; ++ ++ /* 32 BDL middle[13, 17]. 128 BDL middle[40, 56] */ ++ /* 1 Phase = (DDR_BDL_PHASE_TRANSFORM) BDL */ ++ size = DDR_BDL_PHASE_TRANSFORM >> 1; ++ dq_bdl = ddr_adjust_get_average(cfg); ++ ++ /* increase adjust step to accelerate */ ++ if (accel != NULL) { ++ if (dq_bdl > PHY_DQ_BDL_MIDDLE) ++ *accel = dq_bdl - PHY_DQ_BDL_MIDDLE; ++ else if (dq_bdl < PHY_DQ_BDL_MIDDLE) ++ *accel = PHY_DQ_BDL_MIDDLE - dq_bdl; ++ ++ ddr_info("byte[%x] bdl[%x] middle[%x] accel[%x] rdqs[%x]", ++ cfg->cur_byte, dq_bdl, PHY_DQ_BDL_MIDDLE, *accel, ++ (reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)) >> ++ PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK); ++ } ++ ++ /* window on left */ ++ if (dq_bdl < (PHY_DQ_BDL_MIDDLE - size)) ++ return DDR_WIN_LEFT; ++ /* on right */ ++ else if (dq_bdl > (PHY_DQ_BDL_MIDDLE + size)) ++ return DDR_WIN_RIGHT; ++ else ++ return DDR_WIN_MIDDLE; ++} ++ ++/* Check adjust value whether valid */ ++static int ddr_adjust_check_val(int val, unsigned int mode) ++{ ++ if (mode == DDR_MODE_READ) { ++ if (val < 0 || val > PHY_RDQS_BDL_MASK) ++ return DDR_FALSE; ++ } else { ++ if (val < 0 || val > PHY_WDQ_PHASE_MASK) ++ return DDR_FALSE; ++ } ++ ++ return DDR_TRUE; ++} ++ ++static void ddr_rdqs_sync(struct ddr_cfg_st *cfg, int val) ++{ ++ unsigned int rdqsdly; ++ unsigned int cur_rank = cfg->rank_idx; ++ int old, offset; ++ ++ rdqsdly = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ old = (rdqsdly >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ offset = val - old; ++ ++ /* sync rdm */ ++ ddr_rdqs_sync_rank_rdq(cfg, offset); ++ ++ if (cfg->phy[cfg->phy_idx].rank_num == 1) { ++ ddr_debug("Rank number[%x] not need sync another rank", cfg->phy[cfg->phy_idx].rank_num); ++ return; ++ } ++ ++ /* sync other rank rdm and rdq */ ++ cfg->rank_idx = DDR_SUPPORT_RANK_MAX - 1 - cur_rank; /* switch to another rank */ ++ ddr_rdqs_sync_rank_rdq(cfg, offset); ++ cfg->rank_idx = cur_rank; /* resotre to cur rank */ ++} ++ ++static void ddr_set_rdqs(struct ddr_cfg_st *cfg, int val) ++{ ++ unsigned int delay; ++ delay = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ++ ddr_phy_rdqs_sync_rdm(cfg, val); ++ ++ /* clear rdqs bdl */ ++ delay = delay & (~(PHY_RDQS_BDL_MASK << PHY_RDQS_BDL_BIT)); ++ ++ reg_write(delay | ((unsigned int)val << PHY_RDQS_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++} ++ ++/* Get value which need to adjust */ ++static int ddr_adjust_get_val(const struct ddr_cfg_st *cfg) ++{ ++ if (cfg->cur_mode == DDR_MODE_READ) ++ return (reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)) >> ++ PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ else ++ return (reg_read(cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, cfg->cur_byte)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++} ++ ++/* Set value which need to adjust */ ++static void ddr_adjust_set_val(struct ddr_cfg_st *cfg, int val) ++{ ++ unsigned int delay; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) { ++ ddr_set_rdqs(cfg, val); ++ } else { ++ delay = reg_read(cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, cfg->cur_byte)); ++ /* clear wdq phase */ ++ delay = delay & (~(PHY_WDQ_PHASE_MASK << PHY_WDQ_PHASE_BIT)); ++ ++ reg_write(delay | ((unsigned int)val << PHY_WDQ_PHASE_BIT), ++ cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, cfg->cur_byte)); ++ } ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++} ++ ++/* Add or delete value to adjust */ ++static void ddr_adjust_change_val(unsigned int dir, int *val, ++ int step, unsigned int mode) ++{ ++ if (mode == DDR_MODE_READ) { ++ if (dir == DDR_WIN_RIGHT) ++ (*val) = (*val) + step; ++ else ++ (*val) = (*val) - step; ++ } else { ++ /* decrease wdq phase, window move to right */ ++ if (dir == DDR_WIN_RIGHT) ++ (*val) = (*val) - step; ++ else ++ (*val) = (*val) + step; ++ } ++} ++ ++/* ++ * @dir : move direction. DDR_TRUE move to right, DDR_FALSE move to left. ++ * Move window to specified direction until the best DQ bdl beyond the midline. ++ */ ++static void ddr_adjust_move_win(struct ddr_cfg_st *cfg, ++ struct training_data *training, int step, unsigned int dir) ++{ ++ int cur_val, def_val; ++ int accel; ++ unsigned int i; ++ unsigned int trend; ++ unsigned int max_value; ++ ++ max_value = ((cfg->cur_mode == DDR_MODE_WRITE) ? PHY_WDQ_PHASE_MASK : PHY_RDQS_BDL_MASK); ++ ++ def_val = ddr_adjust_get_val(cfg); ++ cur_val = def_val; ++ for (i = 0; i <= max_value; i++) { ++ accel = step; ++ /* write mode no need to accelerate */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) ++ trend = ddr_adjust_trend_check(cfg, 0); ++ else ++ trend = ddr_adjust_trend_check(cfg, &accel); ++ ++ if ((trend == DDR_WIN_MIDDLE) || (trend == dir)) { ++ ddr_debug("Move byte[%x] window to middle suc", cfg->cur_byte); ++ break; ++ } ++ ++ ddr_adjust_change_val(dir, &cur_val, accel, cfg->cur_mode); ++ if (ddr_adjust_check_val(cur_val, cfg->cur_mode) == DDR_FALSE) { ++ ddr_warning("Move byte[%x] to middle fail. value[%x]", ++ cfg->cur_byte, cur_val); ++ break; ++ } ++ ++ ddr_debug("Byte[%x] mode[%x] set value[%x]", ++ cfg->cur_byte, cfg->cur_mode, cur_val); ++ ddr_adjust_set_val(cfg, cur_val); ++ if (ddr_dataeye_deskew(cfg, training)) { ++ ddr_adjust_set_val(cfg, def_val); ++ /* MUST deskew dataeye after restore rdqs */ ++ ddr_dataeye_deskew(cfg, training); ++ ddr_error("Byte[%x] deskew fail, restore[%x]", ++ cfg->cur_byte, def_val); ++ break; ++ } ++ } ++} ++ ++/* Adjust specified byte winodw to middle */ ++static void ddr_adjust_byte(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int trend; ++ ++ trend = ddr_adjust_trend_check(cfg, 0); ++ /* window on left, move to right */ ++ if (trend == DDR_WIN_LEFT) ++ ddr_adjust_move_win(cfg, training, DDR_DQS_ADJ_STEP, DDR_WIN_RIGHT); ++ /* window on right, move to left */ ++ else if (trend == DDR_WIN_RIGHT) ++ ddr_adjust_move_win(cfg, training, DDR_DQS_ADJ_STEP, DDR_WIN_LEFT); ++ /* window on middle, no need to move */ ++ else ++ ddr_debug("Byte[%x] mode[%x] win on middle", ++ cfg->cur_byte, cfg->cur_mode); ++} ++ ++/* ++ * Adjust PHY dataeye. On normal case, ++ * read dateeye window on left after read dataeye hardware training, ++ * write dataeye window on left after write leveling training. ++ */ ++void ddr_adjust_dataeye(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int i; ++ ++ /* dataeye adjust disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DATAEYE_ADJ_MASK) != DDR_FALSE) ++ return; ++ ++ ddr_debug("DDR dataeye adjust PHY[%x][%x] DMC[%x][%x] Rank[%x]", ++ cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx); ++ ++ if (cfg->adjust == DDR_FALSE) ++ return; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ ddr_adjust_byte(cfg, training); ++ } ++} ++#else ++#define ddr_adjust_dataeye(cfg, training) ++#endif /* DDR_TRAINING_ADJUST_CONFIG */ ++ ++#define __DATAEYE_TRAINING__ ++#ifdef DDR_DATAEYE_TRAINING_CONFIG ++/* Check dataeye dq */ ++int ddr_dataeye_check_dq(const struct ddr_cfg_st *cfg) ++{ ++ if (cfg->dq_check_type == DDR_CHECK_TYPE_DDRT) ++ return ddr_ddrt_check(cfg); ++ else if (cfg->dq_check_type == DDR_CHECK_TYPE_MPR) ++ return ddr_mpr_check(cfg); ++ else ++ ddr_error("DDR dataeye dq check type not set"); ++ ++ return 0; ++} ++ ++/* Check dq whether valid and set mask to reduce search time */ ++static int ddr_dataeye_check_dir(unsigned int direction, unsigned int left, ++ unsigned int right, unsigned int *mask, const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ ++ result = ddr_dataeye_check_dq(cfg); ++ switch (direction) { ++ case DDR_FIND_DQ_BOTH: ++ *mask = DDR_FIND_DQ_LEFT | DDR_FIND_DQ_RIGHT; ++ break; ++ case DDR_FIND_DQ_LEFT: ++ if (result) { ++ /* ddr test error, search opposite side */ ++ *mask = DDR_FIND_DQ_RIGHT; ++ } else { /* ddr test ok */ ++ ddr_phy_set_dq_bdl(cfg, left); ++ if (!ddr_dataeye_check_dq(cfg)) ++ /* test ok, go on search this side */ ++ *mask = DDR_FIND_DQ_LEFT; ++ } ++ break; ++ case DDR_FIND_DQ_RIGHT: ++ if (result) { /* ddr test error, search opposite side */ ++ *mask = DDR_FIND_DQ_LEFT; ++ } else { /* ddr test ok */ ++ ddr_phy_set_dq_bdl(cfg, right); ++ if (!ddr_dataeye_check_dq(cfg)) ++ /* test OK, go on search this side */ ++ *mask = DDR_FIND_DQ_RIGHT; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return result; ++} ++ ++/* Binary search the valid dq bdl */ ++static void ddr_dataeye_search_dq(unsigned int left, unsigned int right, ++ int *target, unsigned int direction, const struct ddr_cfg_st *cfg) ++{ ++ unsigned int middle; ++ unsigned int mask = 0; ++ ++ middle = left + ((right - left) >> 1); ++ ++ ddr_phy_set_dq_bdl(cfg, middle); ++ if (!ddr_dataeye_check_dir(direction, left, right, &mask, cfg)) { /* test ok */ ++ *target = (int)middle; ++ return; ++ } ++ ++ if (left == middle || middle == right) /* not found */ ++ return; ++ ++ /* find left side */ ++ if (DDR_FIND_DQ_LEFT & mask) ++ ddr_dataeye_search_dq(left, middle, target, direction, cfg); ++ ++ /* find right side */ ++ if (DDR_FIND_DQ_RIGHT & mask) ++ ddr_dataeye_search_dq(middle, right, target, direction, cfg); ++ ++ return; ++} ++ ++/* Find DQ valid range */ ++static void ddr_dataeye_find_dq(const struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ int cur_dq, left_dq, right_dq, def_dq; ++ unsigned int dq_num; ++ unsigned int win_num; ++ ++ dq_num = (cfg->cur_byte << DDR_BYTE_DQ) + cfg->cur_dq; ++ def_dq = (int)ddr_phy_get_dq_bdl(cfg); ++ cur_dq = def_dq; ++ ++ /* check default dq */ ++ if (ddr_dataeye_check_dq(cfg)) { ++ /* test error */ ++ cur_dq = -1; ++ ddr_dataeye_search_dq(0, PHY_BDL_MASK, &cur_dq, DDR_FIND_DQ_BOTH, cfg); ++ ddr_debug("DQ[%x] def[%x] not ok, find new value[%x]", dq_num, def_dq, cur_dq); ++ if (cur_dq == -1) { /* no valid dq */ ++ training->ddr_bit_result[dq_num] = 0; ++ training->ddr_bit_best[dq_num] = 0; ++ /* restore default value */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ddr_warning("DQ[%x] not found dq. restore[%x]", dq_num, def_dq); ++ return; ++ } ++ } ++ /* find the left boundary */ ++ left_dq = cur_dq; ++ ddr_dataeye_search_dq(0, cur_dq, &left_dq, DDR_FIND_DQ_LEFT, cfg); ++ while (left_dq > 0) { ++ left_dq--; ++ ddr_phy_set_dq_bdl(cfg, left_dq); ++ if (ddr_dataeye_check_dq(cfg)) { ++ /* test error */ ++ left_dq++; ++ break; ++ } ++ } ++ /* find the right boundary */ ++ right_dq = cur_dq; ++ ddr_dataeye_search_dq(cur_dq, PHY_BDL_MASK, &right_dq, DDR_FIND_DQ_RIGHT, cfg); ++ while (right_dq < PHY_BDL_MASK) { ++ right_dq++; ++ ddr_phy_set_dq_bdl(cfg, right_dq); ++ if (ddr_dataeye_check_dq(cfg)) { ++ /* test error */ ++ right_dq--; ++ break; ++ } ++ } ++ /* reset dq */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ++ /* ++ * 0 1 2 3 4 5 6 7 8 9 ++ * x x - - - - - x x x ++ * | | ++ * left_dq right_dq ++ * ++ * so left_dq = 2, right_dq = 6 ++ */ ++ win_num = right_dq - left_dq + 1; ++ training->ddr_bit_result[dq_num] = ((unsigned int)left_dq << DDR_DATAEYE_RESULT_BIT) | ++ (unsigned int)right_dq; ++ training->ddr_bit_best[dq_num] = (win_num << DDR_DATAEYE_RESULT_BIT) | ++ ((win_num >> 1) + (unsigned int)left_dq); ++ ++ ddr_info("DQ[%x] range: left[%x] right[%x] best[%x] mode[%x] rank[%x]", dq_num, ++ left_dq, right_dq, training->ddr_bit_best[dq_num], cfg->cur_mode, cfg->rank_idx); ++} ++ ++/* DDR dataeye training one byte. */ ++int ddr_dataeye_deskew(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int dq_num; ++ unsigned int loop_times = 0; ++ unsigned int win_num, dq_sum; ++ unsigned int def_dq, best_dq; ++ int i; ++ ++ if (training == NULL) ++ return -1; ++ ++ dq_sum = 0; ++ training->ddr_win_sum = 0; ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ dq_num = (cfg->cur_byte << DDR_BYTE_DQ) + i; ++ def_dq = ddr_phy_get_dq_bdl(cfg); ++ ddr_dataeye_find_dq(cfg, training); ++ win_num = training->ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ best_dq = training->ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ /* check window number */ ++ if (win_num < DDR_DATAEYE_WIN_NUM) { ++ if (loop_times < DDR_LOOP_TIMES_LMT) { ++ loop_times++; ++ i--; ++ continue; ++ } else if (win_num == 0) { ++ ddr_warning("Byte[%x] DQ[%x] no win", cfg->cur_byte, dq_num); ++ /* restore default value */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ddr_training_stat(DDR_ERR_DATAEYE, cfg->cur_phy, cfg->cur_byte, i); ++ continue; ++ } ++ } ++ loop_times = 0; ++ ddr_phy_set_dq_bdl(cfg, best_dq); ++ dq_sum = dq_sum + best_dq; ++ training->ddr_win_sum = training->ddr_win_sum + win_num; ++ } ++ dq_sum = dq_sum >> DDR_BYTE_DQ; ++ ++ /* only DDR_MODE_WRITE need to set */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) ++ reg_write((dq_sum & PHY_BDL_MASK) << PHY_WDM_BDL_BIT, cfg->cur_phy + ++ ddr_phy_dxnwdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++ ++ return 0; ++} ++ ++/* DDR write or read dataeye training */ ++static int ddr_dataeye_process(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ /* dataeye training */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ result += ddr_dataeye_deskew(cfg, training); ++ } ++ ++ if (result) { ++ result = -1; ++ ddr_error("PHY[%x] mode[%x] dataeye training fail", cfg->cur_phy, cfg->cur_mode); ++ } else { ++ /* dataeye training result adjust */ ++ ddr_adjust_dataeye(cfg, training); ++ } ++ /* save training result to printf */ ++ ddr_result_data_save(cfg, training); ++ ++ return result; ++} ++ ++int ddr_dataeye_training(struct ddr_cfg_st *cfg) ++{ ++ struct training_data tmp_result; ++ struct training_data *training = &tmp_result; ++ int result_read, result_write; ++ ++ ddr_debug("DDR dataeye training PHY[%x][%x] DMC[%x][%x] Rank[%x]", ++ cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx); ++ ++ /* write dataeye training */ ++ cfg->cur_mode = DDR_MODE_WRITE; ++ memset(training, 0, sizeof(struct training_data)); ++ result_write = ddr_dataeye_process(cfg, training); ++ ++ /* read dataeye training */ ++ cfg->cur_mode = DDR_MODE_READ; ++ memset(training, 0, sizeof(struct training_data)); ++ result_read = ddr_dataeye_process(cfg, training); ++ if (result_read || result_write) ++ return -1; ++ else ++ return 0; ++} ++ ++int ddr_dataeye_training_func(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result; ++ ++ /* dataeye training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DATAEYE_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_DATAEYE_MASK); ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ cfg->adjust = DDR_DATAEYE_NORMAL_ADJUST; ++ cfg->dq_check_type = DDR_CHECK_TYPE_DDRT; ++ result = ddr_dataeye_training(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_dataeye_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR dataeye training"); ++ ++ return 0; ++} ++#endif /* DDR_DATAEYE_TRAINING_CONFIG */ ++ ++#define __HARDWARE_TRAINING__ ++#ifdef DDR_HW_TRAINING_CONFIG ++#ifdef DDR_HW_READ_ADJ_CONFIG ++/* ++ * Adjust rdqs and dq after hw read training. ++ * When define DDR_TRAINING_ADJUST_DISABLE, MUST define DDR_HW_READ_ADJ_CONFIG. ++ */ ++static void ddr_hw_read_adj(const struct ddr_cfg_st *cfg) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ ddr_debug("DDR hw read adjust"); ++ /* check hw read adjust bypass bit */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_HW_ADJ_MASK) != DDR_FALSE) ++ return; ++ ++ /* assume read dataeye window on left */ ++ for (i = 0; i < byte_num; i++) { ++ reg_write(reg_read(base_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, i)) + ++ (PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT), ++ base_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, i)); ++ reg_write(reg_read(base_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, i)) + ++ (PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT), ++ base_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, i)); ++ reg_write(reg_read(base_phy + ddr_phy_dxnrdqsdly(i)) + ++ (PHY_RDQS_MIDDLE_VAL << PHY_RDQS_BDL_BIT), ++ base_phy + ddr_phy_dxnrdqsdly(i)); ++ } ++} ++#else ++static void ddr_hw_read_adj(const struct ddr_cfg_st *cfg) ++{ ++} ++#endif /* DDR_HW_READ_ADJ_CONFIG */ ++ ++static void ddr_training_get_rdqs(const struct ddr_cfg_st *cfg, struct ddr_bdl_st *rdqs) ++{ ++ unsigned int i; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ for (i = 0; i < byte_num; i++) ++ rdqs->bdl[i] = reg_read(base_phy + ddr_phy_dxnrdqsdly(i)); ++} ++ ++static void ddr_training_set_rdqs(const struct ddr_cfg_st *cfg, const struct ddr_bdl_st *rdqs) ++{ ++ unsigned int i; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ for (i = 0; i < byte_num; i++) ++ reg_write(rdqs->bdl[i], base_phy + ddr_phy_dxnrdqsdly(i)); ++} ++ ++static void ddr_hw_training_adjust_rdqs(struct ddr_cfg_st *cfg, const struct rdqs_data_st *rdqs_st) ++{ ++ unsigned int i; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ unsigned int rdqs_rank0, rdqs_rank1; ++ unsigned int cur_rank = cfg->rank_idx; ++ int offset; ++ ++ for (i = 0; i < byte_num; i++) { ++ /* struct rdqs_data_st store the whole register value */ ++ rdqs_rank0 = (rdqs_st->rank[0].bdl[i] >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ rdqs_rank1 = (rdqs_st->rank[1].bdl[i] >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ ++ cfg->cur_byte = i; ++ if (rdqs_rank0 > rdqs_rank1) { ++ offset = rdqs_rank0 - rdqs_rank1; ++ reg_write(rdqs_st->rank[0].bdl[i], cfg->cur_phy + ddr_phy_dxnrdqsdly(i)); ++ cfg->rank_idx = 1; /* switch to rank1 for sync rank1 rdq */ ++ } else { ++ offset = rdqs_rank1 - rdqs_rank0; ++ reg_write(rdqs_st->rank[1].bdl[i], cfg->cur_phy + ddr_phy_dxnrdqsdly(i)); ++ cfg->rank_idx = 0; /* switch to rank0 for sync rank0 rdq */ ++ } ++ ddr_rdqs_sync_rank_rdq(cfg, offset); ++ } ++ cfg->rank_idx = cur_rank; /* restore to current rank */ ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++} ++ ++/* DDR HW training process */ ++int ddr_hw_training_process(const struct ddr_cfg_st *cfg, unsigned int item) ++{ ++ unsigned int count; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int init_ctrl = reg_read(base_phy + DDR_PHY_PHYINITCTRL); ++ ++ if (!item) ++ return 0; ++ ++ ddr_debug("base_phy[%x] itme[%x]", base_phy, item); ++ /* hardware training enable */ ++ reg_write(item | PHY_PHYINITCTRL_INIT_EN | init_ctrl, base_phy + DDR_PHY_PHYINITCTRL); ++ ++ if ((item & PHY_PHYINITCTRL_DRAM_RST) && (item & PHY_PHYINITCTRL_DRAM_INIT_EN)) { ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ } ++ ++ count = DDR_HWR_WAIT_TIMEOUT; ++ /* auto cleared to 0 after training finished */ ++ while (count--) { ++ if (!(reg_read(base_phy + DDR_PHY_PHYINITCTRL) & PHY_PHYINITCTRL_MASK)) ++ break; ++ } ++ ++ if (count == 0xffffffff) { ++ ddr_fatal("HWR wait timeout"); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, item, reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ return -1; ++ } ++ ++ if (reg_read(base_phy + DDR_PHY_PHYINITSTATUS)) { ++ ddr_fatal("Phy[%x] hw[%x] failed[%x]", base_phy, item, reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, item, reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Dataeye hardware training */ ++int ddr_hw_dataeye_read(struct ddr_cfg_st *cfg) ++{ ++ unsigned int base_phy; ++ unsigned int byte_num; ++ ++ unsigned int i; ++ int result; ++ ++ base_phy = cfg->cur_phy; ++ byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ ddr_training_cfg_init(cfg); ++ ++ /* clear */ ++ for (i = 0; i < byte_num; i++) { ++ reg_write(0, base_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, i)); ++ reg_write(0, base_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, i)); ++ reg_write(0, base_phy + ddr_phy_dxnrdqsdly(i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ result = ddr_hw_training_process(cfg, PHY_PHYINITCTRL_RDET_EN); ++ ++ ddr_hw_read_adj(cfg); ++ ++ return result; ++} ++ ++/* ca odt disable, DRAM_RST and DRAM_INIT are required to take effect ++ * The DRAM_RST cannot be performed more than once ++ */ ++static int ddr_hw_ca_odt_disable(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int temp; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ temp = reg_read(base_phy + DDR_PHY_MODEREG01); ++ reg_write(temp & 0x8fffffff, base_phy + DDR_PHY_MODEREG01); /* ca odt disable */ ++ result = ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_DRAM_RESET); ++ ++ reg_write(temp, base_phy + DDR_PHY_MODEREG01); /* restore */ ++ ++ return result; ++} ++ ++/* CA Vref Sync, rank0 and rank1 */ ++static int ddr_hw_ca_vref_sync(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int temp; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int item = cfg->cur_item; ++ ++ temp = reg_read(base_phy + DDR_PHY_TRAINCTRL0); ++ reg_write(temp & (~PHY_TRAINCTRL0_MASK), base_phy + DDR_PHY_TRAINCTRL0); /* select rank0 */ ++ result = ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_AC); ++ ++ reg_write((temp & (~PHY_TRAINCTRL0_MASK)) | 0x1, ++ base_phy + DDR_PHY_TRAINCTRL0); /* select rank1 */ ++ result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_AC); ++ ++ reg_write(temp, base_phy + DDR_PHY_TRAINCTRL0); /* restore */ ++ ++ return result; ++} ++ ++static int ddr_hw_dram_mr_init(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int item = cfg->cur_item; ++ ++ result = ddr_hw_training_process(cfg, item & PHY_PHYINITCTRL_DRAM_INIT_EN); ++ ++ return result; ++} ++ ++/* DDR HW training adapt dram type */ ++static int ddr_hw_dataeye_adapt(const struct ddr_cfg_st *cfg, unsigned int *modereg45_value) ++{ ++ int result; ++ unsigned int modereg45; ++ unsigned int modereg67; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ modereg45 = 0; ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ unsigned int dramtimer1; ++ ++ dramtimer1 = reg_read(base_phy + DDR_PHY_DRAMTIMER1); ++ reg_write(dramtimer1 & (~(DDR_PHY_T_MOD_MASK << DDR_PHY_T_MOD_BIT)), ++ base_phy + DDR_PHY_DRAMTIMER1); /* TMOD:0 */ ++ ++ result = ddr_hw_ca_odt_disable(cfg); /* CA odt disable */ ++ result += ddr_hw_ca_vref_sync(cfg); /* CA vref sync */ ++ result += ddr_hw_dram_mr_init(cfg); /* in WR0 */ ++ ++ modereg67 = reg_read(base_phy + DDR_PHY_MODEREG67); ++ /* turn to WR1 */ ++ reg_write(modereg67 | (0x1 << PHY_MODEREG67_LP4_FSPWR_BIT), ++ base_phy + DDR_PHY_MODEREG67); /* bit6 set 1 */ ++ result += ddr_hw_dram_mr_init(cfg); ++ result += ddr_hw_ca_vref_sync(cfg); /* CA vref sync */ ++ ++ /* turn to WR0 */ ++ reg_write(modereg67 & (~(0x1 << PHY_MODEREG67_LP4_FSPWR_BIT)), ++ base_phy + DDR_PHY_MODEREG67); /* bit6 set 0 */ ++ result += ddr_hw_dram_mr_init(cfg); ++ ++ /* restore DRAMTIMER1 */ ++ reg_write(dramtimer1, base_phy + DDR_PHY_DRAMTIMER1); ++ } else { ++#ifdef DDR_WRITE_DM_DISABLE ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_DDR4) { ++ modereg45 = reg_read(base_phy + DDR_PHY_MODEREG45); ++ reg_write((modereg45 & 0xFBFFFFFF) | 0x8000000, base_phy + DDR_PHY_MODEREG45); /* write dm disable */ ++ } ++#endif ++ *modereg45_value = modereg45; /* for restore 0xe0 in ddr_hw_training_ctl */ ++ result = ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_DRAM_RESET); ++ } ++ ++ return result; ++} ++ ++static int ddr_hw_dataeye_vref_set(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int item = cfg->cur_item; ++ unsigned int dvrft_ctrl; ++ ++ dvrft_ctrl = reg_read(base_phy + DDR_PHY_DVRFTCTRL); ++ reg_write(dvrft_ctrl & (~PHY_DVRFTCTRL_PDAEN_EN), base_phy + DDR_PHY_DVRFTCTRL); ++ /* DDR_PHY_VREFTCTRL 31bit:1 do vref dram set twice */ ++ reg_write((reg_read(base_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT))) | ++ (PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT), ++ base_phy + DDR_PHY_VREFTCTRL); ++ result = ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ); ++ result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ); ++ /* DDR_PHY_VREFTCTRL 31bit:0 do vref dram set once */ ++ reg_write(reg_read(base_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT)), ++ base_phy + DDR_PHY_VREFTCTRL); ++ result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ); ++ reg_write(dvrft_ctrl, base_phy + DDR_PHY_DVRFTCTRL); ++ ++ return result; ++} ++ ++#ifdef DDR_WRITE_DM_DISABLE ++static int ddr_hw_write_dm_disable(const struct ddr_cfg_st *cfg, const unsigned int modereg45_value) ++{ ++ int result = 0; ++ unsigned int temp; ++ unsigned int temp1; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_DDR4) { ++ reg_write(modereg45_value, cfg->cur_phy + DDR_PHY_MODEREG45); /* restore */ ++ temp = reg_read(cfg->cur_phy + DDR_PHY_MRS_SEQ_PROG); ++ temp1 = reg_read(cfg->cur_phy + DDR_PHY_DRAMCFG); ++ reg_write(PHY_MRS_SEQ_PROG_VAL, cfg->cur_phy + DDR_PHY_MRS_SEQ_PROG); /* inti MR5 */ ++ reg_write(temp1 | PHY_WDM_DISABLE_VAL, cfg->cur_phy + DDR_PHY_DRAMCFG); /* write dm disable */ ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_DRAM_INIT_EN); ++ reg_write(temp, cfg->cur_phy + DDR_PHY_MRS_SEQ_PROG); /* restore */ ++ reg_write(temp1, cfg->cur_phy + DDR_PHY_DRAMCFG); /* restore */ ++ } ++ ++ return result; ++} ++#endif ++ ++/* DDR HW training control */ ++int ddr_hw_training_ctl(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int byte_idx; ++ unsigned int modereg45_value = 0; ++ struct rdqs_data_st *rdqs_st = NULL; ++ struct ddr_bdl_dly_st bdl_dly_s; ++ ++ rdqs_st = (struct rdqs_data_st *)cfg->res_st; ++ ++ if (cfg->cur_item == 0 || rdqs_st == NULL) ++ return 0; ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++ /* NOTE: not support array when boot */ ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_CNT_RESET_START); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_PLL); ++ ++ /* save rdqs bdl after PHY_PHYINITCTRL_DLYMEAS_EN */ ++ if (cfg->rank_idx == 0) ++ ddr_training_get_rdqs(cfg, &rdqs_st->origin); ++ if ((cfg->phy_idx >= DDR_PHY_NUM) || (cfg->phy[cfg->phy_idx].total_byte_num > DDR_PHY_BYTE_MAX)) { ++ ddr_error("phy_idx or byte number error"); ++ return -1; ++ } ++ for (byte_idx = 0; byte_idx < (cfg->phy[cfg->phy_idx].total_byte_num); byte_idx++) { ++ cfg->cur_byte = byte_idx; ++ ddr_rdqbdl_adj(cfg, &bdl_dly_s); ++ } ++ result += ddr_hw_dataeye_adapt(cfg, &modereg45_value); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_CAT_EN); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_CS); ++ result += ddr_hw_dataeye_vref_set(cfg); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_NORMAL); ++ ++#ifdef DDR_WRITE_DM_DISABLE ++ result += ddr_hw_write_dm_disable(cfg, modereg45_value); ++#endif ++ ddr_phy_cfg_update(cfg->cur_phy); ++ ++ return result; ++} ++ ++static int ddr_hw_training_by_rank(struct ddr_cfg_st *cfg) ++{ ++ ddr_debug("PHY[%x][%x] Rank[%x] itme[%x]", ++ cfg->phy_idx, cfg->cur_phy, cfg->rank_idx, cfg->cur_item); ++ ++ /* 0:PHY_TRAINCTRL0_DTR_RANK0, 1:PHY_TRAINCTRL0_DTR_RANK1 */ ++ ddr_phy_switch_rank(cfg->cur_phy, cfg->rank_idx); ++ ++ return ddr_hw_training_ctl(cfg); ++} ++ ++int ddr_hw_training_by_phy(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ struct rdqs_data_st rdqs_data; ++ struct rdqs_data_st *rdqs_st = &rdqs_data; ++ struct ddr_timing_st timing_st; ++ unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ cfg->res_st = rdqs_st; ++ ++ /* disable auto refresh */ ++ ddr_training_save_timing(cfg, &timing_st); ++ ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ cfg->cur_item = cfg->phy[cfg->phy_idx].rank[i].item_hw; ++ ++ result += ddr_hw_training_by_rank(cfg); ++ ++ if (rank_num != DDR_SUPPORT_RANK_MAX) ++ break; ++ ++ /* save rank rdqs bdl */ ++ ddr_training_get_rdqs(cfg, &(rdqs_st->rank[i])); ++ ++ /* restore PHY_PHYINITCTRL_DLYMEAS_EN rdqs before training next rank */ ++ if ((rank_num - 1) != i) ++ ddr_training_set_rdqs(cfg, &(rdqs_st->origin)); ++ } ++ ++ if (rank_num == DDR_SUPPORT_RANK_MAX) ++ ddr_hw_training_adjust_rdqs(cfg, rdqs_st); ++ ++ /* restore auto refresh */ ++ ddr_training_restore_timing(cfg, &timing_st); ++ ++ cfg->res_st = 0; ++ ++ return result; ++} ++ ++/* DDR hardware training */ ++int ddr_hw_training(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ struct tr_relate_reg reg; ++ ++ /* save customer reg */ ++ ddr_boot_cmd_save_func(®); ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ result += ddr_hw_training_by_phy(cfg); ++ } ++ /* restore customer reg */ ++ ddr_boot_cmd_restore_func(®); ++ ++ return result; ++} ++#endif /* DDR_HW_TRAINING_CONFIG */ ++ ++#define __DPMC_TRAINING__ ++#ifdef DDR_DPMC_TRAINING_CONFIG ++/* Compare dpmc training result. */ ++static void ddr_dpmc_compare_result(unsigned int base_phy, unsigned int win_def, unsigned int win_max, ++ int byte_index, int ctrl3_index, struct dpmc_data_st *dpmc) ++{ ++ ddr_debug("win_def:[%x] win_max:[%x]", win_def, win_max); ++ ddr_debug("ctrl3_index:[%x]", ctrl3_index); ++ ++ if (win_def > win_max) { ++ reg_write(dpmc->ctrl0_def, base_phy + dx_dxnmiscctrl0(byte_index)); ++ reg_write(dpmc->ctrl3_def, base_phy + dx_dxnmiscctrl3(byte_index)); ++ } else { ++ reg_write(dpmc->ctrl0_cfg, base_phy + dx_dxnmiscctrl0(byte_index)); ++ reg_write(dpmc->ctrl3_cfg_clear | (ctrl3_index << DX_DXNMISCCTRL3_BIT), ++ base_phy + dx_dxnmiscctrl3(byte_index)); ++ } ++} ++ ++static int ddr_dpmc_process(struct ddr_cfg_st *cfg, unsigned int byte_index) ++{ ++ int result; ++ int ctrl3_index = 0; ++ unsigned int i; ++ unsigned int win_def; ++ unsigned int win_max = 0; ++ struct training_data training; ++ struct dpmc_data_st dpmc; ++ ++ ddr_info("PHY[%X] byte[%X] mode[%X]", cfg->cur_phy, byte_index, cfg->cur_mode); ++ dpmc.ctrl0_def = reg_read(cfg->cur_phy + dx_dxnmiscctrl0(byte_index)); ++ dpmc.ctrl3_def = reg_read(cfg->cur_phy + dx_dxnmiscctrl3(byte_index)); ++ ddr_debug("ctrl0_def:[%x] ctrl3_def:[%x]", dpmc.ctrl0_def, dpmc.ctrl3_def); ++ ++ result = ddr_dataeye_deskew(cfg, &training); ++ if (result) { ++ result = -1; ++ ddr_error("PHY[%x] mode[%x] dataeye training fail", cfg->cur_phy, cfg->cur_mode); ++ } ++ win_def = training.ddr_win_sum; ++ ++ /* Set dxctl_rxp_2nd_dq(15:8) and dxctl_rxp_2nd_dm(20) to 1 */ ++ dpmc.ctrl0_cfg = (reg_read(cfg->cur_phy + dx_dxnmiscctrl0(byte_index)) & ++ (~(DX_DXNMISCCTRL0_DQ_MASK << DX_DXNMISCCTRL0_DQ_BIT))) | ++ (DX_DXNMISCCTRL0_DQ_MASK << DX_DXNMISCCTRL0_DQ_BIT); ++ dpmc.ctrl0_cfg = (dpmc.ctrl0_cfg & (~(DX_DXNMISCCTRL0_DM_MASK << DX_DXNMISCCTRL0_DM_BIT))) | ++ (DX_DXNMISCCTRL0_DM_MASK << DX_DXNMISCCTRL0_DM_BIT); ++ ++ ddr_debug("ctrl0_cfg:[%x]", dpmc.ctrl0_cfg); ++ reg_write(dpmc.ctrl0_cfg, cfg->cur_phy + dx_dxnmiscctrl0(byte_index)); ++ ++ /* Set dxctl_pre_margin_code to 0-7, 24:22 */ ++ for (i = 0; i < DX_DXNMISCCTRL3_MAX; i++) { ++ dpmc.ctrl3_cfg_clear = reg_read(cfg->cur_phy + dx_dxnmiscctrl3(byte_index)) & ++ (~(DX_DXNMISCCTRL3_MASK << DX_DXNMISCCTRL3_BIT)); ++ reg_write(dpmc.ctrl3_cfg_clear | (i << DX_DXNMISCCTRL3_BIT), ++ cfg->cur_phy + dx_dxnmiscctrl3(byte_index)); ++ ddr_debug("ctrl3_cfg:[%x]", reg_read(cfg->cur_phy + dx_dxnmiscctrl3(byte_index))); ++ ++ ddr_dataeye_deskew(cfg, &training); ++ ++ ddr_info("win_sum[%x]:%x", i, training.ddr_win_sum); ++ if (training.ddr_win_sum > win_max) { ++ win_max = training.ddr_win_sum; ++ ctrl3_index = i; ++ } ++ } ++ ddr_dpmc_compare_result(cfg->cur_phy, win_def, win_max, byte_index, ctrl3_index, &dpmc); ++ ++ return result; ++} ++ ++/* dxctl pre margin code training. */ ++int ddr_dpmc_training(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int byte_index; ++ cfg->dq_check_type = DDR_CHECK_TYPE_DDRT; ++ cfg->cur_mode = DDR_MODE_READ; ++ struct tr_dq_data dq_data; ++ ++ ddr_save_dq_bdl(cfg, &dq_data); ++ for (byte_index = 0; byte_index < get_byte_num(cfg); byte_index++) { ++ if (byte_index == 0 || byte_index == 2) /* only training byte0 and byte2 */ ++ result = ddr_dpmc_process(cfg, byte_index); ++ else ++ continue; ++ } ++ ddr_restore_dq_bdl(cfg, &dq_data); ++ ++ return result; ++} ++ ++int ddr_dpmc_training_func(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ ++ /* dataeye training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DPMC_MASK)) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_DPMC_MASK); ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ result += ddr_dpmc_training(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_dpmc_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR dpmc training"); ++ return 0; ++} ++#endif /* DDR_DPMC_TRAINING_CONFIG */ ++ ++#define __VREF_TRAINING__ ++#ifdef DDR_VREF_TRAINING_CONFIG ++#ifdef DDR_VREF_WITHOUT_BDL_CONFIG ++/* Save dataeye dq bdl before vref training */ ++static void ddr_vref_save_bdl(const struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank = cfg->rank_idx; ++ unsigned int byte_index; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ byte_index = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ dq_data->dq03[i] = reg_read(base_phy + ddr_phy_dxnwdqnbdl0(rank, byte_index)); ++ dq_data->dq47[i] = reg_read(base_phy + ddr_phy_dxnwdqnbdl1(rank, byte_index)); ++ dq_data->wdm[i] = reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank, byte_index)); ++ } else { ++ dq_data->dq03[i] = reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank, byte_index)); ++ dq_data->dq47[i] = reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank, byte_index)); ++ } ++ } ++} ++ ++/* Restore dataeye dq bdl after vref training */ ++static void ddr_vref_restore_bdl(const struct ddr_cfg_st *cfg, const struct tr_dq_data *dq_data) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank = cfg->rank_idx; ++ unsigned int byte_index; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ byte_index = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ reg_write(dq_data->dq03[i], base_phy + ddr_phy_dxnwdqnbdl0(rank, byte_index)); ++ reg_write(dq_data->dq47[i], base_phy + ddr_phy_dxnwdqnbdl1(rank, byte_index)); ++ reg_write(dq_data->wdm[i], base_phy + ddr_phy_dxnwdqnbdl2(rank, byte_index)); ++ } else { ++ reg_write(dq_data->dq03[i], base_phy + ddr_phy_dxnrdqnbdl0(rank, byte_index)); ++ reg_write(dq_data->dq47[i], base_phy + ddr_phy_dxnrdqnbdl1(rank, byte_index)); ++ } ++ } ++} ++#else ++static void ddr_vref_save_bdl(const struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data) ++{ ++} ++static void ddr_vref_restore_bdl(const struct ddr_cfg_st *cfg, const struct tr_dq_data *dq_data) ++{ ++} ++#endif /* DDR_VREF_WITHOUT_BDL_CONFIG */ ++ ++/* phy s40 not support DRAM vref */ ++static int ddr_vref_dram_set_process(unsigned int base_phy, unsigned int val, unsigned int byte_index) ++{ ++ unsigned int count; ++ unsigned int dvrftctrl = reg_read(base_phy + DDR_PHY_DVRFTCTRL); ++ unsigned int dvreft = reg_read(base_phy + ddr_phy_dvreft_status(byte_index)) & ++ (~PHY_VRFTRES_DVREF_MASK); ++ ++ reg_write(dvrftctrl | PHY_DVRFTCTRL_PDAEN_EN, base_phy + DDR_PHY_DVRFTCTRL); ++ reg_write(dvreft | val, base_phy + ddr_phy_dvreft_status(byte_index)); ++ reg_write(PHY_PHYINITCTRL_DVREFT_SYNC | PHY_PHYINITCTRL_INIT_EN, ++ base_phy + DDR_PHY_PHYINITCTRL); ++ ++ count = DDR_HWR_WAIT_TIMEOUT; ++ /* auto cleared to 0 after training finished */ ++ while (count--) { ++ if (!(reg_read(base_phy + DDR_PHY_PHYINITCTRL) & PHY_PHYINITCTRL_INIT_EN)) ++ break; ++ } ++ ++ if (count == 0xffffffff) { ++ ddr_fatal("vref dram set wait timeout"); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, byte_index, ++ reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ return -1; ++ } ++ ++ reg_write(dvrftctrl & (~PHY_DVRFTCTRL_PDAEN_EN), base_phy + DDR_PHY_DVRFTCTRL); ++ ++ return 0; ++} ++ ++#if defined(DDR_PHY_T12_V100_CONFIG) || defined(DDR_PHY_T12_V101_CONFIG) ++static void ddr_phy_vref_host_set_process(unsigned int base_phy, unsigned int rank, ++ unsigned int bytenum, unsigned int byte_index, unsigned int val) ++{ ++ unsigned int hvreft; ++ ++ if (rank == 0) { ++ hvreft = reg_read(base_phy + ddr_phy_hvreft_status(rank, byte_index)) & ++ (~PHY_VRFTRES_HVREF_MASK); ++ reg_write(hvreft | val, base_phy + ddr_phy_hvreft_status(rank, byte_index)); ++ reg_write(hvreft | val, base_phy + ddr_phy_hvreft_status(rank, byte_index + 1)); ++ } else { ++ hvreft = reg_read(base_phy + ddr_phy_hvreft_status(rank, byte_index)) & ++ (~(PHY_VRFTRES_RXDIFFCAL_MASK << PHY_VRFTRES_RXDIFFCAL_BIT)); ++ reg_write(hvreft | (val << PHY_VRFTRES_RXDIFFCAL_BIT), ++ base_phy + ddr_phy_hvreft_status(rank, byte_index)); ++ reg_write(hvreft | (val << PHY_VRFTRES_RXDIFFCAL_BIT), ++ base_phy + ddr_phy_hvreft_status(rank, byte_index + 1)); ++ } ++} ++#endif ++ ++/* Set DDR Vref value */ ++static void ddr_vref_set(const struct ddr_cfg_st *cfg, unsigned int val) ++{ ++ if (cfg->cur_mode == DDR_MODE_READ) { /* HOST vref */ ++ ddr_phy_vref_host_set(cfg->cur_phy, cfg->rank_idx, get_byte_num(cfg), cfg->cur_byte, val); ++ } else { /* DRAM vref */ ++ unsigned int auto_ref_timing = reg_read(cfg->cur_dmc + DDR_DMC_TIMING2); ++ /* disable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ ++ /* DDR_PHY_VREFTCTRL 31bit:1 do vref dram set twice */ ++ reg_write((reg_read(cfg->cur_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT))) | ++ (PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT), ++ cfg->cur_phy + DDR_PHY_VREFTCTRL); ++ /* DRAM vref operations */ ++ ddr_phy_vref_dram_set(cfg->cur_phy, val, cfg->cur_byte); ++ ddr_phy_vref_dram_set(cfg->cur_phy, val, cfg->cur_byte); ++ /* DDR_PHY_VREFTCTRL 31bit:0 do vref dram set once */ ++ reg_write(reg_read(cfg->cur_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT)), ++ cfg->cur_phy + DDR_PHY_VREFTCTRL); ++ /* DRAM vref operations */ ++ ddr_phy_vref_dram_set(cfg->cur_phy, val, cfg->cur_byte); ++ /* enable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing); ++ } ++ ddr_info("byte[%x] mode[%x] set vref [%x]", cfg->cur_byte, cfg->cur_mode, val); ++} ++ ++/* Get DDR Vref value */ ++static unsigned int ddr_vref_get(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int val = 0; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) /* HOST vref */ ++ ddr_phy_vref_host_get(cfg->cur_phy, cfg->rank_idx, cfg->cur_byte, val); ++ else /* DRAM vref */ ++ ddr_phy_vref_dram_get(cfg->cur_phy, val, cfg->cur_byte); ++ ++ ddr_info("byte[%x] mode[%x] get vref [%x]", cfg->cur_byte, cfg->cur_mode, val); ++ ++ return val; ++} ++ ++/* Get totol win number of training result */ ++static unsigned int ddr_vref_get_win(struct ddr_cfg_st *cfg, ++ struct training_data *training, int vref) ++{ ++ unsigned int vref_min = 0; ++ unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX; ++ int vref_set; ++ ++ training->ddr_win_sum = 0; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) ++ ddr_vref_get_host_max(cfg->rank_idx, vref_max); ++ ++ if (vref < vref_min) ++ vref_set = vref_min; ++ else if (vref > vref_max) ++ vref_set = vref_max; ++ else ++ vref_set = vref; ++ ++ ddr_vref_set(cfg, vref_set); ++ ddr_dataeye_deskew(cfg, training); ++ ++ return training->ddr_win_sum; ++} ++ ++/* Find the best vref which win number is max */ ++static unsigned int ddr_vref_find_best(struct ddr_cfg_st *cfg, ++ struct training_data *training, unsigned int vref, int step) ++{ ++ int cur_vref; ++ unsigned int best_vref; ++ unsigned int cur_win; ++ unsigned int max_win; ++ unsigned int lower_times = 0; ++ unsigned int vref_min = 0; ++ unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) ++ ddr_vref_get_host_max(cfg->rank_idx, vref_max); ++ ++ max_win = 0; ++ cur_vref = vref + step; ++ ++ if (vref < vref_min) ++ best_vref = vref_min; ++ else if (vref > vref_max) ++ best_vref = vref_max; ++ else ++ best_vref = vref; ++ ++ /* find parabola vertex */ ++ while (cur_vref >= vref_min && cur_vref <= vref_max) { ++ cur_win = ddr_vref_get_win(cfg, training, cur_vref); ++ ddr_debug("byte[%x] vref[%x] win[%x] mode[%x]", ++ cfg->cur_byte, cur_vref, cur_win, cfg->cur_mode); ++ if (cur_win < max_win) { ++ lower_times++; ++ if (lower_times == DDR_VREF_COMPARE_TIMES) ++ /* Continuous decline, mean found vertex */ ++ break; ++ } else { ++ lower_times = 0; ++ max_win = cur_win; ++ best_vref = cur_vref; ++ } ++ cur_vref = cur_vref + step; ++ } ++ ++ return best_vref; ++} ++ ++/* DDR Vref calibrate and set the best value */ ++static void ddr_vref_cal(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int def_vref; ++ unsigned int best_vref; ++ unsigned int left_win; ++ unsigned int right_win; ++ ++ def_vref = ddr_vref_get(cfg); ++ left_win = ddr_vref_get_win(cfg, training, def_vref - DDR_VREF_COMPARE_STEP); ++ right_win = ddr_vref_get_win(cfg, training, def_vref + DDR_VREF_COMPARE_STEP); ++ ++ ddr_debug("byte[%x] default vref[%x] win[%x][%x] mode[%x]", ++ cfg->cur_byte, def_vref, left_win, right_win, cfg->cur_mode); ++ ++ /* With vref increments, WIN number is a parabola. ++ So firstly determine the result on left or right. */ ++ /* parabola vertex */ ++ if (left_win < right_win) { /* the result on right */ ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, 1); ++ } else if (left_win > right_win) { /* the result on left */ ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, -1); ++ } else { ++ /* when (left_win == right_win), check def_vref */ ++ unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX; ++ if (cfg->cur_mode == DDR_MODE_READ) ++ ddr_vref_get_host_max(cfg->rank_idx, vref_max); ++ ++ if (def_vref < (vref_max >> 1)) ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, 1); ++ else ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, -1); ++ } ++ ++ ddr_debug("byte[%x] best vref[%x] mode[%x]", ++ cfg->cur_byte, best_vref, cfg->cur_mode); ++ ddr_vref_set(cfg, best_vref); ++} ++ ++/* vref write calibrate: support DDR4 and LPDDR4 ++ * if the dram type is not ddr4 or lpddr4, do nothing ++ */ ++static int ddr_vref_write(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int i; ++ unsigned int dram_type = cfg->phy[cfg->phy_idx].dram_type; ++ unsigned int bank_group = (reg_read(cfg->cur_dmc + ++ ddr_dmc_cfg_rnkvol(cfg->rank_idx)) >> DMC_CFG_MEM_BG_BIT) & DMC_CFG_MEM_BG_MASK; ++ ++ if (dram_type != PHY_DRAMCFG_TYPE_LPDDR4 && ++ dram_type != PHY_DRAMCFG_TYPE_DDR4) ++ return -1; ++ ++ if (dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ bank_group = DMC_CFG_MEM_2BG; /* lpddr4 not training byte1 byte3 */ ++ ++ if (cfg->dmc_idx >= DDR_DMC_PER_PHY_MAX) ++ return -1; ++ ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) ++ return -1; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ /* byte1 and byte3 bypass when 2 Bank Group */ ++ if ((bank_group == DMC_CFG_MEM_2BG) && ++ ((i == 1) || (i == 3))) /* bypass byte1 and byte3 */ ++ continue; ++ ++ ddr_vref_cal(cfg, training); ++ } ++ ++ return 0; ++} ++ ++int ddr_vref_training(struct ddr_cfg_st *cfg) ++{ ++ struct training_data tmp_result; ++ struct training_data *training = &tmp_result; ++ struct tr_dq_data dq_data; ++ int result = 0; ++ unsigned int i; ++ ++ ddr_debug("DDR Vref[%x] training PHY[%x][%x] DMC[%x][%x] Rank[%x]", ++ cfg->cur_mode, cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx); ++ ++ ddr_vref_save_bdl(cfg, &dq_data); ++ memset(training, 0, sizeof(struct training_data)); ++ ++ /* vref calibrate */ ++ if (cfg->cur_mode == DDR_MODE_READ) { ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ if (cfg->cur_byte == 1 || cfg->cur_byte == 3) /* not training byte 1 and byte 3 */ ++ continue; ++ ++ ddr_vref_cal(cfg, training); ++ } ++ } else { ++ if (ddr_vref_write(cfg, training)) ++ return 0; /* do nothing */ ++ } ++ ++#if !defined(DDR_VREF_WITHOUT_BDL_CONFIG) || defined(DDR_TRAINING_CMD) ++ /* dataeye deskew again on best vref. */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ result += ddr_dataeye_deskew(cfg, training); ++ } ++#endif ++ ++ ddr_vref_restore_bdl(cfg, &dq_data); ++ ddr_result_data_save(cfg, training); ++ ++ return result; ++} ++ ++int ddr_vref_training_func(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL!"); ++ return -1; ++ } ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_VREF_HOST_MASK); ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ cfg->dq_check_type = DDR_CHECK_TYPE_DDRT; ++ ++ /* host vref training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_VREF_HOST_MASK) == DDR_FALSE) { ++ cfg->cur_mode = DDR_MODE_READ; ++ result += ddr_vref_training(cfg); ++ } ++ ++ /* dram vref training enable && DDR4 */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_VREF_DRAM_MASK) == DDR_FALSE) { ++ cfg->cur_mode = DDR_MODE_WRITE; ++ result += ddr_vref_training(cfg); ++ } ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_vref_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR vref training"); ++ ++ return 0; ++} ++#endif /* DDR_VREF_TRAINING_CONFIG */ ++ ++#define __GATE_TRAINING__ ++#ifdef DDR_GATE_TRAINING_CONFIG ++/* Find gate phase */ ++static int ddr_gate_find_phase(const struct ddr_cfg_st *cfg, struct ddr_delay_st *rdqsg) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ for (rdqsg->phase[i] = PHY_RDQSG_PHASE_MAX; ++ rdqsg->phase[i] > PHY_GATE_PHASE_MARGIN; ++ rdqsg->phase[i] -= PHY_RDQSG_PHASE_STEP) { ++ reg_write(rdqsg->phase[i] << PHY_RDQSG_PHASE_BIT, ++ base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ ddr_phy_cfg_update(base_phy); ++ if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE, i, -1) == 0) ++ break; ++ } ++ if (rdqsg->phase[i] <= PHY_GATE_PHASE_MARGIN) { ++ /* find gate phase fail */ ++ ddr_fatal("find gate phase[%x] fail", rdqsg->phase[i]); ++ ddr_training_stat(DDR_ERR_GATING, base_phy, -1, -1); ++ return -1; ++ } else { ++ /* decrease one setp to find bdl */ ++ rdqsg->phase[i] -= PHY_RDQSG_PHASE_STEP; ++ reg_write(rdqsg->phase[i] << PHY_RDQSG_PHASE_BIT, ++ base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ } ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return 0; ++} ++ ++static void ddr_gate_find_bdl_by_byte(const struct ddr_cfg_st *cfg, struct ddr_delay_st *rdqsg, ++ unsigned int byte_num, unsigned int gate_result) ++{ ++ int j; ++ unsigned int tmp; ++ ++ for (j = 0; j < byte_num; j++) { ++ if (!(gate_result & (1 << j))) { ++ rdqsg->bdl[j] += DDR_GATE_BDL_STEP; ++ if (rdqsg->bdl[j] > PHY_BDL_MASK) ++ tmp = ((rdqsg->bdl[j] - PHY_BDL_MASK - 1) << PHY_RDQSG_TX_BDL_BIT) + ++ (rdqsg->phase[j] << PHY_RDQSG_PHASE_BIT) + (PHY_BDL_MASK - 1); ++ else ++ tmp = (rdqsg->phase[j] << PHY_RDQSG_PHASE_BIT) + rdqsg->bdl[j]; ++ ++ reg_write(tmp, cfg->cur_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, j)); ++ } ++ } ++} ++ ++/* Find gate bdl */ ++static int ddr_gate_find_bdl(const struct ddr_cfg_st *cfg, struct ddr_delay_st *rdqsg) ++{ ++ int i, j; ++ unsigned int gate_result; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ unsigned int swtmode = reg_read(base_phy + DDR_PHY_SWTMODE); ++ ++ for (i = 0; i < byte_num; i++) ++ rdqsg->bdl[i] = 0; ++ ++ /* enable phy sw gate training mode */ ++ reg_write(swtmode | (1 << PHY_SWTMODE_SW_GTMODE_BIT), base_phy + DDR_PHY_SWTMODE); ++ ++ for (i = 0; i < PHY_GATE_BDL_MAX; i++) { ++ ddr_phy_cfg_update(base_phy); ++ ddr_ddrt_test(DDRT_READ_ONLY_MODE, -1, -1); ++ gate_result = (reg_read(base_phy + DDR_PHY_SWTRLT) >> PHY_SWTRLT_GATE_BIT) & ++ PHY_SWTRLT_GATE_MASK; ++ if (gate_result == ((1 << byte_num) - 1)) ++ break; ++ ++ ddr_gate_find_bdl_by_byte(cfg, rdqsg, byte_num, gate_result); ++ } ++ ++ /* disable phy sw gate training mode */ ++ reg_write(swtmode & (~(1 << PHY_SWTMODE_SW_GTMODE_BIT)), ++ base_phy + DDR_PHY_SWTMODE); ++ ++ if (i == PHY_GATE_BDL_MAX) { /* find gate bdl fail */ ++ ddr_fatal("PHY[%x] find gate bdl fail. result[%x]", base_phy, gate_result); ++ for (j = 0; j < byte_num; j++) { ++ if (!(gate_result & (1 << j))) ++ ddr_training_stat(DDR_ERR_GATING, base_phy, j, -1); ++ } ++ return -1; ++ } else { ++ return 0; ++ } ++} ++ ++int ddr_gate_training(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int i, tmp; ++ unsigned int byte_num; ++ struct ddr_delay_st rdqsg; ++ unsigned int def_delay[DDR_PHY_BYTE_MAX]; ++ int result; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ ddr_debug("DDR Gate training"); ++ ++ byte_num = get_byte_num(cfg); ++ ++ for (i = 0; i < byte_num; i++) ++ def_delay[i] = reg_read(base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ ++ /* find phase first */ ++ result = ddr_gate_find_phase(cfg, &rdqsg); ++ /* find bdl */ ++ if (!result) ++ result = ddr_gate_find_bdl(cfg, &rdqsg); ++ ++ /* set new phase */ ++ if (!result) { ++ for (i = 0; i < byte_num; i++) { ++ rdqsg.phase[i] -= PHY_GATE_PHASE_MARGIN; ++ tmp = reg_read(base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ tmp &= ~(PHY_RDQSG_PHASE_MASK << PHY_RDQSG_PHASE_BIT); ++ tmp |= rdqsg.phase[i] << PHY_RDQSG_PHASE_BIT; ++ reg_write(tmp, base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ } ++ } else { ++ /* restore default value */ ++ for (i = 0; i < byte_num; i++) ++ reg_write(def_delay[i], ++ base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return 0; /* use default value and not reset */ ++} ++ ++int ddr_gating_func(const struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ ++ /* gate training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_GATE_MASK) != DDR_FALSE) { ++ /* check hardware gating */ ++ if (reg_read(cfg->cur_phy + DDR_PHY_PHYINITSTATUS) & ++ PHY_INITSTATUS_GT_MASK) { ++ ddr_fatal("PHY[%x] hw gating fail", cfg->cur_phy); ++ ddr_training_stat(DDR_ERR_HW_GATING, cfg->cur_phy, -1, -1); ++ return -1; ++ } ++ return 0; ++ } ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_GATE_MASK); ++ ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_GATE); ++ result += ddr_gate_training(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_gating_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR gate training"); ++ ++ return 0; ++} ++#endif /* DDR_GATE_TRAINING_CONFIG */ ++ ++#define __PCODE_TRAINING__ ++#ifdef DDR_PCODE_TRAINING_CONFIG ++/* Set pcode value to register IMPSTATUS and DDR_PHY_IMP_STATUS1 */ ++static void ddr_pcode_set_value(unsigned int base_phy, unsigned int pcode_value) ++{ ++ unsigned int imp_ctrl1; ++ ++ reg_write((reg_read(base_phy + DDR_PHY_IMPSTATUS) & ++ (~(PHY_ZCODE_PDRV_MASK << PHY_ZCODE_PDRV_BIT))) | ++ (pcode_value << PHY_ZCODE_PDRV_BIT), base_phy + DDR_PHY_IMPSTATUS); ++ ddr_debug("cur IMPSTATUS [%x] = [%x]", ++ base_phy + DDR_PHY_IMPSTATUS, reg_read(base_phy + DDR_PHY_IMPSTATUS)); ++ ++ imp_ctrl1 = reg_read(base_phy + DDR_PHY_IMP_CTRL1); ++ /* ac_vddq_cal_en set 0 */ ++ reg_write(imp_ctrl1 & (~(0x1 << PHY_AC_VDDQ_CAL_EN_BIT)), base_phy + DDR_PHY_IMP_CTRL1); ++ ++ reg_write((reg_read(base_phy + DDR_PHY_IMP_STATUS1) & ++ (~(PHY_ACCTL_PDRV_LATCH_MASK << PHY_ACCTL_PDRV_LATCH_BIT))) | ++ (pcode_value << PHY_ACCTL_PDRV_LATCH_BIT), base_phy + DDR_PHY_IMP_STATUS1); ++ ddr_debug("cur IMP_STATUS1 [%x] = [%x]", ++ base_phy + DDR_PHY_IMP_STATUS1, reg_read(base_phy + DDR_PHY_IMP_STATUS1)); ++ ++ /* restore ac_vddq_cal_en */ ++ reg_write(imp_ctrl1, base_phy + DDR_PHY_IMP_CTRL1); ++} ++ ++static int ddr_pcode_trainig_by_phy(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int times = 0; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int pcode_value; ++ unsigned int osc_rpt_vld; ++ unsigned int osc_cnt_rdata; ++ int ddr_freq; ++ ++ /* test start */ ++ reg_write(reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) | PHY_OSC_START_MASK, ++ base_phy + DDR_PHY_CORNER_DETECTOR); ++ ++ do { ++ osc_rpt_vld = (reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) >> ++ PHY_OSC_RPT_VLD) & PHY_OSC_RPT_VLD_MASK; ++ times++; ++ } while ((!osc_rpt_vld) && (times < DDRT_PCODE_WAIT_TIMEOUT)); ++ ++ if (times >= DDRT_PCODE_WAIT_TIMEOUT) { ++ ddr_fatal("IO pcode training wait timeout"); ++ return -1; ++ } ++ ++ osc_cnt_rdata = (reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) >> ++ PHY_OSC_CNT_RDATA_BIT) & PHY_OSC_CNT_RDATA_MASK; ++ ++ /* test stop */ ++ reg_write(reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) & ++ (~PHY_OSC_START_MASK), base_phy + DDR_PHY_CORNER_DETECTOR); ++ ++ ddr_freq = ddr_get_cksel(); ++ ++ /* get pcode_value: a formula based on simulation */ ++ pcode_value = ++ (490960 - (89 * osc_cnt_rdata * ddr_freq) / 300) / 10000; /* y equal (490960 - (89*x*fre)/300)/10000 */ ++ ++ ddr_debug("pcode value[%x]", pcode_value); ++ if (pcode_value < PHY_PCODE_MIN) { ++ pcode_value = PHY_PCODE_MIN; ++ } else if (pcode_value > PHY_PCODE_MAX) { ++ pcode_value = PHY_PCODE_MAX; ++ } ++ ++ /* set pcode value */ ++ ddr_pcode_set_value(base_phy, pcode_value); ++ return 0; ++} ++ ++int ddr_pcode_training(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ int i; ++ ++ if (cfg == NULL) ++ return -1; ++ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ cfg->cur_item = cfg->phy[i].rank[0].item; ++ ++ if (ddr_training_check_bypass(cfg, 1 << (cfg->phy_idx)) != DDR_FALSE) ++ continue; ++ ++ /* pcode training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_PCODE_MASK) != DDR_FALSE) ++ continue; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_PCODE_MASK); ++ result += ddr_pcode_trainig_by_phy(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg); ++ } ++ ++ return result; ++} ++#else ++int ddr_pcode_training(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR pcode training"); ++ return 0; ++} ++#endif +diff --git a/drivers/ddr/vendor/default/ddr_training_impl.h b/drivers/ddr/vendor/default/ddr_training_impl.h +new file mode 100644 +index 0000000..6f2c111 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_training_impl.h +@@ -0,0 +1,476 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_TRAINING_IMPL_H ++#define DDR_TRAINING_IMPL_H ++ ++#ifndef __ASSEMBLY__ ++ ++#include "ddr_interface.h" ++#include "ddr_training_custom.h" ++#include "ddr_training_internal_config.h" ++ ++/****** special config define*******************************************/ ++#ifdef DDR_DATAEYE_NORMAL_NOT_ADJ_CONFIG ++/* Adjust dataeye window consume a lot of time, disable it will make boot ++ * faster. ++ * NOTE: The WDQ Phase and RDQS MUST be config a good value in the init table ++ * to avoid window trend to one side. ++ */ ++#define DDR_DATAEYE_NORMAL_ADJUST (DDR_FALSE) ++#else ++#define DDR_DATAEYE_NORMAL_ADJUST (DDR_TRUE) ++#endif ++/* MUST adjust dataeye window after HW or MPR training */ ++#define DDR_DATAEYE_ABNORMAL_ADJUST (DDR_TRUE) ++ ++/****** ddr training item bypass mask define ****************************/ ++#define DDR_BYPASS_PHY0_MASK 0x1 /* [0]PHY0 training */ ++#define DDR_BYPASS_PHY1_MASK 0x2 /* [1]PHY1 training */ ++#define DDR_BYPASS_PHY2_MASK 0x4 /* [2]PHY2 training */ ++#define DDR_BYPASS_WL_MASK 0x10 /* [4]Write leveling */ ++#define DDR_BYPASS_GATE_MASK 0x100 /* [8]Gate training */ ++#define DDR_BYPASS_DATAEYE_MASK 0x10000 /* [16]Dataeye training */ ++#define DDR_BYPASS_PCODE_MASK 0x40000 /* [18]Pcode training */ ++#define DDR_BYPASS_HW_MASK 0x100000 /* [20]Hardware read training */ ++#define DDR_BYPASS_MPR_MASK 0x200000 /* [21]MPR training */ ++#define DDR_BYPASS_AC_MASK 0x400000 /* [22]AC training */ ++#define DDR_BYPASS_LPCA_MASK 0x800000 /* [23]LPDDR CA training */ ++#define DDR_BYPASS_VREF_HOST_MASK 0x1000000 /* [24]Host Vref training */ ++#define DDR_BYPASS_VREF_DRAM_MASK 0x2000000 /* [25]DRAM Vref training */ ++#define DDR_BYPASS_DCC_MASK 0x08000000 /* [27]DCC training */ ++#define DDR_BYPASS_DATAEYE_ADJ_MASK 0x10000000 /* [28]Dataeye adjust */ ++#define DDR_BYPASS_WL_ADJ_MASK 0x20000000 /* [29]WL write adjust */ ++#define DDR_BYPASS_HW_ADJ_MASK 0x40000000 /* [30]HW read adjust */ ++#define DDR_BYPASS_ALL_MASK 0xffffffff /* all bypass */ ++ ++/****** common define **********************************************/ ++/* special ddrt need special read and write register */ ++#ifdef DDR_DDRT_SPECIAL_CONFIG ++#define ddrt_reg_read(addr) ddr_ddrt_read(addr) ++#define ddrt_reg_write(val, addr) ddr_ddrt_write(val, addr) ++#else ++#define ddrt_reg_read(addr) reg_read(addr) ++#define ddrt_reg_write(val, addr) reg_write(val, addr) ++#endif ++ ++#define DDR_MODE_READ (1 << 0) ++#define DDR_MODE_WRITE (1 << 1) ++ ++#define DDR_ENTER_SREF (1 << 0) ++#define DDR_EXIT_SREF (1 << 1) ++ ++/* DSB to make sure the operation is complete */ ++#ifndef ddr_asm_dsb ++#if (__LINUX_ARM_ARCH__ >= 8) ++#define ddr_asm_dsb() { __asm__ __volatile__("dsb sy"); } ++#else ++#define ddr_asm_dsb() { __asm__ __volatile__("dsb"); } ++#endif ++#endif ++ ++#define DDR_HWR_WAIT_TIMEOUT 0xffffffff ++#define DDR_SFC_WAIT_TIMEOUT 1000 ++#define DDR_LPCA_WAIT_TIMEOUT 1000 ++#define DDR_LOOP_COUNT 10000000 ++ ++#ifdef CFG_EDA_VERIFY ++#define DDR_AUTO_TIMING_DELAY 1 ++#define DDR_SET_FRE_DELAY_100NS 10 ++#define DDR_SET_FRE_DELAY_1US 2000 ++#define DDR_SET_FRE_DELAY_10US 2000 ++#define DDR_SET_FRE_DELAY_100US 5000 ++#else ++#define DDR_AUTO_TIMING_DELAY 1000 ++#define DDR_SET_FRE_DELAY_100NS 200 /* wait 100ns */ ++#define DDR_SET_FRE_DELAY_1US 2000 /* wait 1 us */ ++#define DDR_SET_FRE_DELAY_10US 20000 /* wait 10 us */ ++#define DDR_SET_FRE_DELAY_100US 200000 /* wait 100 us */ ++#endif ++ ++#define DDR_FIND_DQ_BOTH (1 << 0) /* find a valid value */ ++/* x is valid, (x-1) is invalid */ ++#define DDR_FIND_DQ_LEFT (1 << 1) ++/* x is valid, (x+1) is invalid */ ++#define DDR_FIND_DQ_RIGHT (1 << 2) ++ ++#define DDR_VREF_DRAM_VAL_MAX 0x32 /* 92.50%*VDDIO */ ++#define DDR_VREF_DRAM_VAL_MIN 0x0 /* 60.00%*VDDIO */ ++ ++#define DDR_PHY_REG_DQ_NUM 4 /* one register has 4 DQ BDL */ ++#define DDR_PHY_BDL_DLY_NUM 12 /* 10 bdl and 2 phase */ ++ ++#define DDR_PHY_CA_MAX 10 ++#define DDR_PHY_CA_REG_MAX (DDR_PHY_CA_MAX >> 1) ++ ++#define DDR_TRUE 1 ++#define DDR_FALSE 0 ++ ++#define DDR_WIN_MIDDLE (1 << 0) ++#define DDR_WIN_LEFT (1 << 1) ++#define DDR_WIN_RIGHT (1 << 2) ++ ++#define DDR_DELAY_PHASE 1 ++#define DDR_DELAY_BDL 2 ++ ++#ifndef DDR_DATAEYE_WIN_NUM ++/* Dateeye window number. More bigger more slower when Vref training. */ ++#define DDR_DATAEYE_WIN_NUM 8 ++#endif ++#ifndef DDR_LOOP_TIMES_LMT ++/* Dataeye DQ deskew times for best result. More bigger more slower. */ ++#define DDR_LOOP_TIMES_LMT 1 ++#endif ++#ifndef DDR_VREF_COMPARE_TIMES ++/* Compare times when find best vref value. More bigger more slower. */ ++#define DDR_VREF_COMPARE_TIMES 3 ++#endif ++#ifndef DDR_MPR_RDQS_FIND_TIMES ++/* MPR Find first start rdqs times. More bigger, start rdqs more bigger. */ ++#define DDR_MPR_RDQS_FIND_TIMES 3 ++#endif ++#ifndef DDR_VREF_COMPARE_STEP ++/* Compare step when begin to find. More bigger, more mistake, more stable. */ ++#define DDR_VREF_COMPARE_STEP 3 ++#endif ++ ++#define DDR_DQ_NUM_EACH_REG 4 /* Each bdl register includes four dqbdl */ ++#define DDR_BYTE_DQ 3 /* Shift left or shift right 3: 8 dq(1 byte) */ ++#define DDR_DQBDL_SHIFT_BIT 3 /* Shift left or shift right 3: 8 bit */ ++ ++#define DDR_DATAEYE_RESULT_MASK 0xffff ++#define DDR_DATAEYE_RESULT_BIT 16 ++ ++#define DDR_WL_BDL_STEP 2 /* wl bdl step */ ++#define DDR_GATE_BDL_STEP 2 /* gate bdl step */ ++#define DDR_DQS_ADJ_STEP 1 /* WR/RD DQS adjust step */ ++ ++#define DDR_DDRT_MODE_GATE (1 << 0) ++#define DDR_DDRT_MODE_DATAEYE (1 << 1) ++ ++#define DDR_CHECK_TYPE_DDRT (1 << 0) ++#define DDR_CHECK_TYPE_MPR (1 << 1) ++ ++#define DDR_MPR_BYTE_MASK 0xff ++#define DDR_MPR_BIT_MASK 0x1 ++#define DDR_MPR_BYTE_BIT 16 /* 16 bit (2 byte) */ ++#define DDR_MPR_BYTE_SHIFT_BIT 3 /* 8 bit */ ++ ++#define DDR_PHY_AC_TEST_VAL0 0x0 ++#define DDR_PHY_AC_TEST_VAL1 0xffffffff ++#define DDR_PHY_AC_TEST_VAL2 0x55555555 ++#define DDR_PHY_AC_TEST_VAL3 0xaaaaaaaa ++ ++/*******log define ***********************************************/ ++#if defined(DDR_TRAINING_CMD) && defined(DDR_TRAINING_LOG_CONFIG) ++#define ddr_info(fmt...) ddr_training_log(__func__, DDR_LOG_INFO, fmt) ++#define ddr_debug(fmt...) ddr_training_log(__func__, DDR_LOG_DEBUG, fmt) ++#define ddr_warning(fmt...) ddr_training_log(__func__, DDR_LOG_WARNING, fmt) ++#define ddr_error(fmt...) ddr_training_log(__func__, DDR_LOG_ERROR, fmt) ++#define ddr_fatal(fmt...) ddr_training_log(__func__, DDR_LOG_FATAL, fmt) ++#else ++#define ddr_info(fmt...) ++#define ddr_debug(fmt...) ++#define ddr_warning(fmt...) ++#define ddr_error(fmt...) ++#define ddr_fatal(fmt...) ++#endif /* DDR_TRAINING_CMD && DDR_TRAINING_LOG_CONFIG */ ++ ++/* [11:0] Error type */ ++/* 0x00000001 Write Leveling error */ ++#define DDR_ERR_WL (1 << 0) ++/* 0x00000002 Hardware Gatining error */ ++#define DDR_ERR_HW_GATING (1 << 1) ++/* 0x00000004 Sofeware Gatining error */ ++#define DDR_ERR_GATING (1 << 2) ++/* 0x00000008 DDRT test time out */ ++#define DDR_ERR_DDRT_TIME_OUT (1 << 3) ++/* 0x00000010 Hardware read dataeye error */ ++#define DDR_ERR_HW_RD_DATAEYE (1 << 4) ++/* 0x00000020 MPR error */ ++#define DDR_ERR_MPR (1 << 5) ++/* 0x00000040 Dataeye error */ ++#define DDR_ERR_DATAEYE (1 << 6) ++/* 0x00000080 LPDDR CA error */ ++#define DDR_ERR_LPCA (1 << 7) ++ ++/* [13:12] Error phy */ ++/* 0x00001000 PHY0 training error */ ++#define DDR_ERR_PHY0 (1 << 12) ++/* 0x00002000 PHY1 training error */ ++#define DDR_ERR_PHY1 (1 << 13) ++ ++#define DDR_ERR_BYTE_BIT 24 /* [28:24] Error DQ0-31 */ ++#define DDR_ERR_DQ_BIT 20 /* [22:20] Error Byte0-3 */ ++ ++/*******data define*********************************************/ ++#define get_byte_num(cfg) ((cfg)->phy[(cfg)->phy_idx].dmc[(cfg)->dmc_idx].byte_num) ++ ++#ifndef DDR_RELATE_REG_DECLARE ++struct tr_custom_reg { ++}; ++#endif ++ ++struct dmc_cfg_sref_st { ++ unsigned int val[DDR_DMC_PER_PHY_MAX]; ++}; ++ ++struct ddr_bdl_st { ++ unsigned int bdl[DDR_PHY_BYTE_MAX]; ++}; ++ ++struct ddr_timing_st { ++ unsigned int val[DDR_DMC_PER_PHY_MAX]; ++}; ++ ++struct ddr_tmp_st { ++ unsigned int temp; ++}; ++ ++struct rdqs_data_st { ++ struct ddr_bdl_st origin; ++ struct ddr_bdl_st rank[DDR_SUPPORT_RANK_MAX]; ++}; ++ ++struct ddr_delay_st { ++ unsigned int phase[DDR_PHY_BYTE_MAX]; ++ unsigned int bdl[DDR_PHY_BYTE_MAX]; ++}; ++ ++struct tr_relate_reg { ++ unsigned int auto_ref_timing; ++ unsigned int power_down; ++ unsigned int dmc_scramb; ++ unsigned int dmc_scramb_cfg; ++ unsigned int misc_scramb; ++ unsigned int ac_phy_ctl; ++ unsigned int swapdfibyte_en; ++ struct tr_custom_reg custom; ++ struct ddr_ddrc_data ddrc; ++}; ++ ++struct tr_dq_data { ++ unsigned int dq03[DDR_PHY_BYTE_MAX]; /* DQ0-DQ3 BDL */ ++ unsigned int dq47[DDR_PHY_BYTE_MAX]; /* DQ4-DQ7 BDL */ ++ unsigned int rdqs[DDR_PHY_BYTE_MAX]; /* RDQS */ ++ unsigned int rdm[DDR_PHY_BYTE_MAX]; /* RDM */ ++ unsigned int wdm[DDR_PHY_BYTE_MAX]; /* WDM */ ++}; ++ ++struct ca_bit_st { ++ unsigned int bit_p; ++ unsigned int bit_n; ++}; ++ ++struct ca_data_st { ++ unsigned int base_dmc; ++ unsigned int base_phy; ++ unsigned int done; /* whether all ca found bdl range */ ++ unsigned int min; /* min left bound */ ++ unsigned int max; /* max right bound */ ++ unsigned def[DDR_PHY_CA_REG_MAX]; ++ int left[DDR_PHY_CA_MAX]; ++ int right[DDR_PHY_CA_MAX]; ++ struct ca_bit_st bits[DDR_PHY_CA_MAX]; ++}; ++ ++struct ddr_dmc_st { ++ unsigned int addr; ++ unsigned int byte_num; ++ unsigned int ddrt_pattern; /* ddrt reversed data */ ++}; ++ ++struct ddr_rank_st { ++ unsigned int item; /* software training item */ ++ unsigned int item_hw; /* hardware training item */ ++}; ++ ++struct ddr_phy_st { ++ unsigned int addr; ++ unsigned int dram_type; ++ unsigned int dmc_num; ++ unsigned int rank_num; ++ unsigned int total_byte_num; ++ struct ddr_dmc_st dmc[DDR_DMC_PER_PHY_MAX]; ++ struct ddr_rank_st rank[DDR_SUPPORT_RANK_MAX]; ++}; ++ ++struct ddr_cfg_st { ++ struct ddr_phy_st phy[DDR_PHY_NUM]; ++ unsigned int phy_num; ++ unsigned int cur_phy; /* current training phy addr */ ++ unsigned int cur_dmc; /* current training dmc addr */ ++ unsigned int cur_item; /* current SW or HW training item */ ++ unsigned int cur_pattern; /* current ddrt pattern */ ++ unsigned int cur_mode; /* read or write */ ++ unsigned int cur_byte; /* current training byte index */ ++ unsigned int cur_dq; /* current training dq index */ ++ unsigned int phy_idx; /* current training phy index */ ++ unsigned int rank_idx; /* current training rank index */ ++ unsigned int dmc_idx; /* current training dmc index */ ++ unsigned int adjust; /* whether need to adjust dataeye window */ ++ unsigned int dq_check_type; /* ddrt or mpr */ ++ void *cmd_st; /* struct ddr_cmd_st */ ++ void *res_st; /* SW: struct ddr_training_result_st, HW: struct rdqs_data_st */ ++}; ++ ++struct dcc_ck_st { ++ unsigned int val[DDR_CK_RESULT_MAX]; ++ unsigned int win; ++ unsigned int win_min_ctl; ++ unsigned int win_max_ctl; ++ unsigned int win_min_duty; ++ unsigned int win_max_duty; ++ unsigned int def_bp; ++ unsigned int def_ctl; ++ unsigned int def_duty; ++ unsigned int idx_duty; ++ unsigned int idx_duty_ctl; ++ unsigned int idx_ctl; ++ unsigned int bypass_ck_bit; ++ unsigned int acioctl21_ctl_bit; ++ unsigned int acioctl21_ck_bit; ++}; ++ ++#ifdef DDR_DCC_TRAINING_CONFIG ++struct dcc_data_st { ++ struct tr_dq_data rank[DDR_SUPPORT_RANK_MAX]; ++ struct dcc_ck_st ck[DDR_CK_MAX_NUM]; ++ unsigned int item[DDR_CK_MAX_NUM]; ++ unsigned int ioctl21_tmp; ++}; ++#endif ++ ++struct ddr_bdl_dly_st { ++ unsigned int value[DDR_PHY_BDL_DLY_NUM]; ++}; ++ ++/*******Uart early function ***********************************************/ ++#ifndef DDR_PUTS ++#define DDR_PUTS uart_early_puts ++#endif ++#ifndef DDR_PUT_HEX ++#define DDR_PUT_HEX uart_early_put_hex ++#endif ++#ifndef DDR_PUTC ++#define DDR_PUTC uart_early_putc ++#endif ++ ++#if defined(DDR_TRAINING_UART_CONFIG) || defined(DDR_TRAINING_LOG_CONFIG) ++extern void uart_early_puts(const char *s); ++extern void uart_early_put_hex(const unsigned int hex); ++extern void uart_early_putc(int chr); ++#else ++#undef DDR_PUTS ++#undef DDR_PUT_HEX ++#undef DDR_PUTC ++#endif ++/*******function interface define*********************************************/ ++#ifndef DDR_SW_TRAINING_FUNC ++#define DDR_SW_TRAINING_FUNC_PUBLIC ++#define DDR_SW_TRAINING_FUNC ddr_sw_training_func ++#endif ++ ++#ifndef DDR_HW_TRAINING_FUNC ++#define DDR_HW_TRAINING_FUNC_PUBLIC ++#define DDR_HW_TRAINING_FUNC ddr_hw_training_func ++#endif ++ ++#ifndef DDR_PCODE_TRAINING_FUNC ++#define DDR_PCODE_TRAINING_FUNC ddr_pcode_training_func ++#endif ++ ++#ifndef DDR_TRAINING_CONSOLE ++#define DDR_TRAINING_CONSOLE_PUBLIC ++#define DDR_TRAINING_CONSOLE ddr_training_console ++#endif ++/*******Custom function ***********************************************/ ++#ifndef ddr_training_ddrt_prepare_func ++#define ddr_training_ddrt_prepare_func() ++#endif ++#ifndef ddr_training_save_reg_func ++#define ddr_training_save_reg_func(relate_reg, mask) ++#endif ++#ifndef ddr_training_restore_reg_func ++#define ddr_training_restore_reg_func(relate_reg) ++#endif ++#ifndef ddr_boot_cmd_save_func ++#define ddr_boot_cmd_save_func(relate_reg) ((void)(relate_reg)) ++#endif ++#ifndef ddr_boot_cmd_restore_func ++#define ddr_boot_cmd_restore_func(relate_reg) ((void)(relate_reg)) ++#endif ++/*******function define*********************************************/ ++int ddr_sw_training_func(void); ++int ddr_training_boot_func(struct ddr_cfg_st *cfg); ++int ddr_training_cmd_func(struct ddr_cfg_st *cfg); ++ ++void* memset(void *b, int c, size_t len); ++void* memcpy(void *dst, const void *src, size_t len); ++void ddr_training_cfg_init(struct ddr_cfg_st *cfg); ++int ddr_training_all(struct ddr_cfg_st *cfg); ++int ddr_dataeye_training_func(struct ddr_cfg_st *cfg); ++int ddr_vref_training_func(struct ddr_cfg_st *cfg); ++int ddr_wl_func(const struct ddr_cfg_st *cfg); ++int ddr_gating_func(const struct ddr_cfg_st *cfg); ++int ddr_ac_training_func(const struct ddr_cfg_st *cfg); ++int ddr_lpca_training_func(const struct ddr_cfg_st *cfg); ++int ddr_dcc_training_func(struct ddr_cfg_st *cfg); ++int ddr_mpr_training_func(struct ddr_cfg_st *cfg); ++ ++int ddr_training_ctrl_easr(const struct ddr_cfg_st *cfg, unsigned int sref_req); ++void ddr_training_save_timing(const struct ddr_cfg_st *cfg, struct ddr_timing_st *timing_st); ++void ddr_training_restore_timing(const struct ddr_cfg_st *cfg, const struct ddr_timing_st *timing_st); ++void ddr_sref_cfg(const struct ddr_cfg_st *cfg, struct dmc_cfg_sref_st *cfg_sref, unsigned int value); ++void ddr_sref_cfg_restore(const struct ddr_cfg_st *cfg, const struct dmc_cfg_sref_st *cfg_sref); ++void ddr_phy_cfg_update(unsigned int base_phy); ++void ddr_phy_set_dq_bdl(const struct ddr_cfg_st *cfg, unsigned int value); ++int ddr_hw_training(struct ddr_cfg_st *cfg); ++int ddr_hw_training_by_phy(struct ddr_cfg_st *cfg); ++int ddr_hw_training_process(const struct ddr_cfg_st *cfg, unsigned int item); ++int ddr_pcode_training(struct ddr_cfg_st *cfg); ++int ddr_mpr_check(const struct ddr_cfg_st *cfg); ++int ddr_write_leveling(const struct ddr_cfg_st *cfg); ++int ddr_gate_training(const struct ddr_cfg_st *cfg); ++int ddr_dataeye_training(struct ddr_cfg_st *cfg); ++int ddr_vref_training(struct ddr_cfg_st *cfg); ++int ddr_lpca_training(struct ddr_cfg_st *cfg); ++int ddr_dataeye_deskew(struct ddr_cfg_st *cfg, struct training_data *training); ++void ddr_adjust_dataeye(struct ddr_cfg_st *cfg, struct training_data *training); ++void ddr_result_data_save(struct ddr_cfg_st *cfg, const struct training_data *training); ++void ddr_lpca_data_save(struct ddr_cfg_st *cfg, const struct ca_data_st *data); ++unsigned int ddr_ddrt_get_test_addr(void); ++int ddr_ddrt_test(unsigned int mask, int byte, int dq); ++int ddr_dataeye_check_dq(const struct ddr_cfg_st *cfg); ++void ddr_ddrt_init(const struct ddr_cfg_st *cfg, unsigned int mode); ++int ddr_training_check_bypass(const struct ddr_cfg_st *cfg, unsigned int mask); ++void ddr_training_save_reg(const struct ddr_cfg_st *cfg, struct tr_relate_reg *relate_reg, ++ unsigned int mask); ++void ddr_training_restore_reg(const struct ddr_cfg_st *cfg, const struct tr_relate_reg *relate_reg); ++void ddr_training_switch_axi(const struct ddr_cfg_st *cfg); ++void ddr_training_log(const char *func, int level, const char *fmt, ...); ++void ddr_training_stat(unsigned int mask, unsigned int phy, int byte, int dq); ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq); ++void ddr_training_start(void); ++void ddr_training_suc(void); ++unsigned int ddr_phy_get_byte_num(unsigned int base_dmc); ++void ddr_training_set_timing(unsigned int base_dmc, unsigned int timing); ++int ddr_hw_dataeye_read(struct ddr_cfg_st *cfg); ++#endif /* __ASSEMBLY__ */ ++#endif /* DDR_TRAINING_IMPL_H */ +diff --git a/drivers/ddr/vendor/default/ddr_training_internal_config.h b/drivers/ddr/vendor/default/ddr_training_internal_config.h +new file mode 100644 +index 0000000..0eacb4c +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_training_internal_config.h +@@ -0,0 +1,184 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_TRAINING_INTERNAL_CONFIG_H ++#define DDR_TRAINING_INTERNAL_CONFIG_H ++ ++/****** include ddrc,phy,dmc define files *******************/ ++#if defined(DDR_DDRC_V500_CONFIG) ++#include "ddr_ddrc_v500.h" ++#elif defined(DDR_DDRC_V510_CONFIG) ++#include "ddr_ddrc_v510.h" ++#elif defined(DDR_DDRC_V520_CONFIG) ++#include "ddr_ddrc_v520.h" ++#else ++# error Unknown DDRC Type ++#endif ++ ++#if defined(DDR_PHY_S40_CONFIG) ++#include "ddr_phy_s40.h" ++#elif defined(DDR_PHY_T28_CONFIG) ++#include "ddr_phy_t28.h" ++#elif defined(DDR_PHY_T16_CONFIG) ++#include "ddr_phy_t16.h" ++#elif defined(DDR_PHY_T12_V100_CONFIG) ++#include "ddr_phy_t12_v100.h" ++#elif defined(DDR_PHY_T12_V101_CONFIG) ++#include "ddr_phy_t12_v101.h" ++#elif defined(DDR_PHY_S28_V300_CONFIG) ++#include "ddr_phy_s28_v300.h" ++#else ++# error Unknown DDR PHY Type ++#endif ++ ++#if defined(DDR_DDRT_S40_CONFIG) ++#include "ddr_ddrt_s40.h" ++#elif defined(DDR_DDRT_T28_CONFIG) ++#include "ddr_ddrt_t28.h" ++#elif defined(DDR_DDRT_T16_CONFIG) ++#include "ddr_ddrt_t16.h" ++#elif defined(DDR_DDRT_T12_V100_CONFIG) ++#include "ddr_ddrt_t12_v100.h" ++#elif defined(DDR_DDRT_V2_0_SHF0_CONFIG) ++#include "ddr_ddrt_v2_0_shf0.h" ++#elif defined(DDR_DDRT_V2_0_SHF1_CONFIG) ++#include "ddr_ddrt_v2_0_shf1.h" ++#elif defined(DDR_DDRT_V2_0_SHF2_CONFIG) ++#include "ddr_ddrt_v2_0_shf2.h" ++#else ++# error Unknown DDR PHY Type ++#endif ++ ++/****** training item define *******************/ ++/* enable all config by default */ ++#define DDR_WL_TRAINING_CONFIG ++#define DDR_GATE_TRAINING_CONFIG ++#define DDR_DATAEYE_TRAINING_CONFIG ++#define DDR_HW_TRAINING_CONFIG ++#define DDR_TRAINING_ADJUST_CONFIG ++#define DDR_TRAINING_LOG_CONFIG ++#define DDR_TRAINING_UART_CONFIG ++#define DDR_TRAINING_STAT_CONFIG ++ ++/* defined in ddr_training_custom.h to disable this item */ ++#ifdef DDR_VREF_TRAINING_DISABLE ++#undef DDR_VREF_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_WL_TRAINING_DISABLE ++#undef DDR_WL_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_GATE_TRAINING_DISABLE ++#undef DDR_GATE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_DATAEYE_TRAINING_DISABLE ++#undef DDR_DATAEYE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_HW_TRAINING_DISABLE ++#undef DDR_HW_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_MPR_TRAINING_DISABLE ++#undef DDR_MPR_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_ADJUST_DISABLE ++#undef DDR_TRAINING_ADJUST_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_LOG_DISABLE ++#undef DDR_TRAINING_LOG_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_UART_DISABLE ++#undef DDR_TRAINING_UART_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_STAT_DISABLE ++#undef DDR_TRAINING_STAT_CONFIG ++#endif ++ ++/* for training cmd */ ++#ifdef DDR_TRAINING_CMD ++/* defined in ddr_training_custom.h to disable this item */ ++#ifdef DDR_VREF_TRAINING_CMD_DISABLE ++#undef DDR_VREF_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_WL_TRAINING_CMD_DISABLE ++#undef DDR_WL_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_GATE_TRAINING_CMD_DISABLE ++#undef DDR_GATE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_DATAEYE_TRAINING_CMD_DISABLE ++#undef DDR_DATAEYE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_HW_TRAINING_CMD_DISABLE ++#undef DDR_HW_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_MPR_TRAINING_CMD_DISABLE ++#undef DDR_MPR_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_ADJUST_CMD_DISABLE ++#undef DDR_TRAINING_ADJUST_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_LOG_CMD_DISABLE ++#undef DDR_TRAINING_LOG_CONFIG ++#endif ++#endif /* DDR_TRAINING_CMD */ ++ ++/* check config */ ++#if defined(DDR_TRAINING_ADJUST_DISABLE) && defined(DDR_HW_TRAINING_CONFIG) && \ ++ !defined(DDR_HW_READ_ADJ_CONFIG) ++#error when defined DDR_TRAINING_ADJUST_DISABLE, \ ++ MUST define DDR_HW_READ_ADJ_CONFIG. ++#endif ++ ++#if (defined(DDR_HW_TRAINING_CONFIG) || defined(DDR_MPR_TRAINING_CONFIG) || \ ++ defined(DDR_VREF_TRAINING_CONFIG) || \ ++ defined(DDR_TRAINING_ADJUST_CONFIG)) && \ ++ !defined(DDR_DATAEYE_TRAINING_CONFIG) ++#error when enable HW/GATE/VREF training or dataeye adjust, \ ++ MUST define DDR_DATAEYE_TRAINING_CONFIG. ++#endif ++ ++/* reserve config */ ++/* DDR_WL_DATAEYE_ADJUST_CONFIG: Adjust WDQ phase/bdl after WL training. */ ++/* DDR_VREF_TRAINING_CONFIG : DDR Vref training. */ ++/* DDR_MPR_TRAINING_CONFIG : DDR MPR training. */ ++/* DDR_AC_TRAINING_CONFIG : DDR AC training. */ ++/* DDR_LPCA_TRAINING_CONFIG : LPDDR CA training. */ ++/* DDR_DDRT_SPECIAL_CONFIG : DDRT read and write special operate. */ ++/* DDR_DDR4_CONFIG : DDR4 special operate. */ ++/* DDR_TRAINING_CUT_CODE_CONFIG: Cut code for small SRAM. */ ++/* DDR_TRAINING_MINI_LOG_CONFIG: Less code to log */ ++/* DDR_HW_READ_ADJ_CONFIG : Adjust read dataeye after hw read training */ ++/* DDR_VREF_WITHOUT_BDL_CONFIG : Vref not modify DQ bdl */ ++/* DDR_DATAEYE_NORMAL_NOT_ADJ_CONFIG : Do not adjust window on normal case */ ++#endif /* DDR_TRAINING_INTERNAL_CONFIG_H */ +diff --git a/drivers/ddr/vendor/default/ddr_wl_training.c b/drivers/ddr/vendor/default/ddr_wl_training.c +new file mode 100644 +index 0000000..ff83351 +--- /dev/null ++++ b/drivers/ddr/vendor/default/ddr_wl_training.c +@@ -0,0 +1,442 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __WRITE_LEVELING__ ++#ifdef DDR_WL_TRAINING_CONFIG ++static void ddr_bdl_add(unsigned int *raw, unsigned int val) ++{ ++ if (((*raw) + val) > PHY_BDL_MASK) ++ *raw = PHY_BDL_MASK; ++ else ++ *raw += val; ++} ++ ++static void ddr_bdl_sub(unsigned int *raw, unsigned int val) ++{ ++ if ((*raw) > val) ++ *raw -= val; ++ else ++ *raw = 0; ++} ++ ++/* DDR PHY DQ phase increase */ ++static void ddr_phase_inc(unsigned int *raw) ++{ ++#if defined (DDR_PHY_T28_CONFIG) || defined(DDR_PHY_T16_CONFIG) || \ ++ defined (DDR_PHY_T12_V100_CONFIG) || defined (DDR_PHY_T12_V101_CONFIG) ++ if ((*raw) < (PHY_WDQS_PHASE_MASK - 1)) { ++ if (((*raw) & 0x3) == 0x2) ++ *raw += 0x2; ++ else ++ *raw += 0x1; ++ } ++#else ++ if ((*raw) < PHY_WDQS_PHASE_MASK) ++ *raw += 0x1; ++#endif ++} ++ ++/* DDR PHY DQ phase decrease */ ++static void ddr_phase_dec(unsigned int *raw) ++{ ++#if defined (DDR_PHY_T28_CONFIG) || defined(DDR_PHY_T16_CONFIG) || \ ++ defined (DDR_PHY_T12_V100_CONFIG) || defined (DDR_PHY_T12_V101_CONFIG) ++ if ((*raw) > 0x1) { ++ if (((*raw) & 0x3) == 0x3) ++ *raw -= 0x2; ++ else ++ *raw -= 0x1; ++ } ++#else ++ if ((*raw) > 0x0) ++ *raw -= 0x1; ++#endif ++} ++ ++/* DQ bdl add or sub */ ++static void ddr_dq_bdl_operate(unsigned int base_phy, ++ unsigned int addr_offset, unsigned int val, unsigned int is_add) ++{ ++ unsigned int tmp; ++ unsigned int dq_bdl[DDR_PHY_REG_DQ_NUM]; ++ int i; ++ ++ tmp = reg_read(base_phy + addr_offset); ++ dq_bdl[0] = (tmp >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK; /* wdq0bdl */ ++ dq_bdl[1] = (tmp >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK; /* wdq1bdl */ ++ dq_bdl[2] = (tmp >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK; /* wdq2bdl */ ++ dq_bdl[3] = (tmp >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK; /* wdq3bdl */ ++ ++ for (i = 0; i < DDR_PHY_REG_DQ_NUM; i++) { ++ if (is_add) ++ ddr_bdl_add(&dq_bdl[i], val); ++ else ++ ddr_bdl_sub(&dq_bdl[i], val); ++ } ++ ++ tmp = (dq_bdl[3] << PHY_BDL_DQ3_BIT) + (dq_bdl[2] << PHY_BDL_DQ2_BIT) + /* wdq3bdl and wdq2bdl */ ++ (dq_bdl[1] << PHY_BDL_DQ1_BIT) + (dq_bdl[0] << PHY_BDL_DQ0_BIT); /* wdq1bdl and wdq0bdl */ ++ reg_write(tmp, base_phy + addr_offset); ++} ++ ++/* Disable or enable DDR write leveling mode */ ++static void ddr_wl_switch(unsigned int base_dmc, unsigned int base_phy, int val) ++{ ++ unsigned int mr1_raw; ++ unsigned int sfc_cmd; ++ unsigned int sfc_bank; ++ ++ /* Set Rank = 0, Cmd = MRS, No Precharch CMD */ ++ mr1_raw = reg_read(base_phy + DDR_PHY_MODEREG01) >> PHY_MODEREG01_MR1_BIT; ++ sfc_cmd = DMC_CMD_TYPE_LMR; ++ sfc_bank = DMC_BANK_MR1; ++ ++ if (val == DDR_TRUE) /* enable DDR wl */ ++ /* Set A7 as 1 */ ++ sfc_cmd += (mr1_raw | DMC_CMD_MRS_A7) << DMC_SFC_CMD_MRS_BIT; ++ else ++ /* Set A7 as 0 */ ++ sfc_cmd += (mr1_raw & ((~DMC_CMD_MRS_A7) & DMC_CMD_MRS_MASK)) << DMC_SFC_CMD_MRS_BIT; ++ ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, sfc_bank); ++ ++ /* clear */ ++ if (val == DDR_FALSE) { ++ reg_write(0x0, base_dmc + DDR_DMC_SFCBANK); ++ reg_write(0x0, base_dmc + DDR_DMC_SFCREQ); ++ } ++ ++ /* phy sw write leveling mode */ ++ reg_write(val, base_phy + DDR_PHY_SWTMODE); ++} ++ ++#ifdef DDR_WL_DATAEYE_ADJUST_CONFIG ++static void ddr_wl_wdq_cmp(const struct ddr_cfg_st *cfg, struct ddr_delay_st *wdqs_new, ++ struct ddr_delay_st *wdqs_old, unsigned int byte_idx) ++{ ++ unsigned int val; ++ unsigned int phase_adj; ++ unsigned int wdq_phase; ++ unsigned int wdm_bdl; ++ unsigned int bdl_adj = 0; /* for write dataeye */ ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank_idx = cfg->rank_idx; ++ ++ phase_adj = 0; ++ wdq_phase = (reg_read(base_phy + ddr_phy_dxnwdqdly(rank_idx, byte_idx)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++ wdm_bdl = (reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank_idx, byte_idx)) >> ++ PHY_WDM_BDL_BIT) & PHY_BDL_MASK; ++ ++ if (wdqs_new->bdl[byte_idx] > wdqs_old->bdl[byte_idx]) { ++ val = wdqs_new->bdl[byte_idx] - wdqs_old->bdl[byte_idx]; ++ phase_adj = val >> DDR_BDL_PHASE_REL; ++ wdq_phase = wdq_phase + phase_adj; ++ ++ if (wdq_phase > PHY_WDQ_PHASE_MASK) ++ wdq_phase = PHY_WDQ_PHASE_MASK; ++ ++ /* adjust wdq bdl and dm bdl in opposite direction */ ++ bdl_adj = phase_adj << DDR_BDL_PHASE_REL; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(rank_idx, byte_idx), ++ bdl_adj, DDR_FALSE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(rank_idx, byte_idx), ++ bdl_adj, DDR_FALSE); ++ ddr_bdl_sub(&wdm_bdl, bdl_adj); ++ } else if (wdqs_new->bdl[byte_idx] < wdqs_old->bdl[byte_idx]) { ++ val = wdqs_old->bdl[byte_idx] - wdqs_new->bdl[byte_idx]; ++ phase_adj = val >> DDR_BDL_PHASE_REL; ++ wdq_phase = (wdq_phase > phase_adj) ? (wdq_phase - phase_adj) : 0; ++ ++ /* adjust wdq bdl and dm bdl in opposite direction */ ++ bdl_adj = phase_adj << DDR_BDL_PHASE_REL; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(rank_idx, byte_idx), ++ bdl_adj, DDR_TRUE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(rank_idx, byte_idx), ++ bdl_adj, DDR_TRUE); ++ ddr_bdl_add(&wdm_bdl, bdl_adj); ++ } ++ ++ ddr_info("Byte[%x] WDQ adjust phase[%x] bdl[%x]", ++ byte_idx, phase_adj, bdl_adj); ++ ++ reg_write(wdq_phase << PHY_WDQ_PHASE_BIT, ++ base_phy + ddr_phy_dxnwdqdly(rank_idx, byte_idx)); ++ reg_write(wdm_bdl << PHY_WDM_BDL_BIT, base_phy + ddr_phy_dxnwdqnbdl2(rank_idx, byte_idx)); ++} ++ ++/* Adjust dataeye WDQ after Write leveling */ ++static void ddr_wl_wdq_adjust(const struct ddr_cfg_st *cfg, ++ struct ddr_delay_st *wdqs_new, struct ddr_delay_st *wdqs_old) ++{ ++ unsigned int i; ++ unsigned int byte_num = get_byte_num(cfg); ++ ++ ddr_debug("DDR WL write adjust"); ++ ++ /* check wl write adjust bypass bit */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_WL_ADJ_MASK) != DDR_FALSE) ++ return; ++ ++ /* adjust wdq phase, wdq bdl, wdm bdl */ ++ for (i = 0; i < byte_num; i++) { ++ if (wdqs_new->phase[i] == wdqs_old->phase[i] && ++ wdqs_new->bdl[i] == wdqs_old->bdl[i]) { ++ continue; ++ } ++ ddr_wl_wdq_cmp(cfg, wdqs_new, wdqs_old, i); ++ } ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++} ++#endif /* DDR_WL_DATAEYE_ADJUST_CONFIG */ ++ ++/* Sync WDQ phase, WDQ bdl, WDM bdl, OEN bdl, WDQ SOE bdl by WDQS value */ ++static void ddr_wl_bdl_sync(const struct ddr_cfg_st *cfg, ++ const struct ddr_delay_st *wdqs_new, const struct ddr_delay_st *wdqs_old) ++{ ++ int i; ++ unsigned int val; ++ unsigned int oen_bdl, wdqsoe_bdl, wdm_bdl; ++ unsigned int wdq_phase; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ /* sync wdq phase, wdq bdl, wdm bdl, oen bdl, wdq soe bdl */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ if (wdqs_new->phase[i] == wdqs_old->phase[i] && wdqs_new->bdl[i] == wdqs_old->bdl[i]) ++ continue; ++ ++ ddr_debug("Byte[%x] new[%x][%x] old[%x][%x]", i, ++ wdqs_new->phase[i], wdqs_new->bdl[i], wdqs_old->phase[i], wdqs_old->bdl[i]); ++ ++ /* wdq phase */ ++ wdq_phase = (reg_read(base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++ /* always new_phase >= old_phase */ ++ wdq_phase = wdq_phase + (wdqs_new->phase[i] - wdqs_old->phase[i]); ++ ++ /* bdl */ ++ oen_bdl = (reg_read(base_phy + ddr_phy_dxnoebdl(cfg->rank_idx, i)) >> ++ PHY_OEN_BDL_BIT) & PHY_BDL_MASK; ++ wdqsoe_bdl = (reg_read(base_phy + ddr_phy_dxnoebdl(cfg->rank_idx, i)) >> ++ PHY_WDQSOE_BDL_BIT) & PHY_BDL_MASK; ++ wdm_bdl = (reg_read(base_phy + ddr_phy_dxnwdqnbdl2(cfg->rank_idx, i)) >> ++ PHY_WDM_BDL_BIT) & PHY_BDL_MASK; ++ ++ if (wdqs_new->bdl[i] > wdqs_old->bdl[i]) { ++ val = wdqs_new->bdl[i] - wdqs_old->bdl[i]; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(cfg->rank_idx, i), val, DDR_TRUE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(cfg->rank_idx, i), val, DDR_TRUE); ++ ddr_bdl_add(&oen_bdl, val); ++ ddr_bdl_add(&wdqsoe_bdl, val); ++ ddr_bdl_add(&wdm_bdl, val); ++ } else if (wdqs_new->bdl[i] < wdqs_old->bdl[i]) { ++ val = wdqs_old->bdl[i] - wdqs_new->bdl[i]; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(cfg->rank_idx, i), val, DDR_FALSE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(cfg->rank_idx, i), val, DDR_FALSE); ++ ddr_bdl_sub(&oen_bdl, val); ++ ddr_bdl_sub(&wdqsoe_bdl, val); ++ ddr_bdl_sub(&wdm_bdl, val); ++ } ++ ++ if (wdq_phase > PHY_WDQ_PHASE_MASK) ++ wdq_phase = PHY_WDQ_PHASE_MASK; ++ ++ reg_write(wdq_phase << PHY_WDQ_PHASE_BIT, base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ reg_write((wdqsoe_bdl << PHY_WDQSOE_BDL_BIT) + (oen_bdl << PHY_OEN_BDL_BIT), ++ base_phy + ddr_phy_dxnoebdl(cfg->rank_idx, i)); ++ reg_write((wdm_bdl << PHY_WDM_BDL_BIT), base_phy + ddr_phy_dxnwdqnbdl2(cfg->rank_idx, i)); ++ } ++ ++ ddr_phy_cfg_update(base_phy); ++} ++ ++static void ddr_wl_error(unsigned int type, unsigned int byte_num, ++ unsigned int wl_result, unsigned int base_phy) ++{ ++ int j; ++ ++ if (type == DDR_DELAY_BDL) { ++ ddr_fatal("PHY[%x] WL fail, result[%x]", base_phy, wl_result); ++ for (j = 0; j < byte_num; j++) { ++ if (!(wl_result & (1 << j))) ++ ddr_training_stat(DDR_ERR_WL, base_phy, j, -1); ++ } ++ } else { ++ ddr_debug("PHY[%x] WL not found phase, result[%x]", base_phy, wl_result); ++ } ++} ++ ++/* ++ * Write leveling process. ++ * WL depend default WDQS phase value in register init table. ++ */ ++static int ddr_wl_process(const struct ddr_cfg_st *cfg, unsigned int type, struct ddr_delay_st *wdqs) ++{ ++ int i, j; ++ unsigned int wl_result; ++ unsigned int length; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ unsigned int wdqsbdl_max; ++ ++ wdqsbdl_max = PHY_BDL_MASK - DDR_WL_BDL_STEP; ++ ++ if (type == DDR_DELAY_PHASE) ++ length = PHY_WDQS_PHASE_MASK; ++ else ++ length = PHY_BDL_MASK; ++ ++ /* find WDQS phase or bdl, assume CLK Delay > DQS Delay */ ++ for (i = 0; i <= length; i++) { ++ ddr_phy_cfg_update(base_phy); ++ reg_write(0x1, base_phy + DDR_PHY_SWTWLDQS); ++ ddr_asm_dsb(); ++ wl_result = reg_read(base_phy + DDR_PHY_SWTRLT) & PHY_SWTRLT_WL_MASK; ++ reg_write(0x0, base_phy + DDR_PHY_SWTWLDQS); ++ ++ if ((wl_result & ((1 << byte_num) - 1)) == ((1 << byte_num) - 1)) ++ break; ++ ++ for (j = 0; j < byte_num; j++) { ++ ddr_info("type[0x%x] byte[0x%x] phase[0x%x] bdl[0x%x] wl_result[0x%x]", ++ type, j, wdqs->phase[j], wdqs->bdl[j], wl_result); ++ if (wl_result & (1 << j)) ++ continue; ++ ++ if (type == DDR_DELAY_PHASE) ++ ddr_phase_inc(&wdqs->phase[j]); ++ else if (wdqs->bdl[j] <= wdqsbdl_max) ++ wdqs->bdl[j] += DDR_WL_BDL_STEP; ++ ++ reg_write((wdqs->phase[j] << PHY_WDQS_PHASE_BIT) + ++ (wdqs->bdl[j] << PHY_WDQS_BDL_BIT), ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, j)); ++ } ++ } ++ ++ if (i > length) { /* wl error, not find wdqs delay */ ++ ddr_wl_error(type, byte_num, wl_result, base_phy); ++ return -1; ++ } else { ++ return 0; ++ } ++} ++ ++/* ++ * Find WDQS delay, sync to WDQ delay and OE delay. ++ * WL depend default WDQS phase value in register init table. ++ */ ++int ddr_write_leveling(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int i, tmp; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int base_dmc = cfg->cur_dmc; ++ struct ddr_delay_st wdqs_old; ++ struct ddr_delay_st wdqs_new; ++ ++ ddr_debug("DDR Write Leveling training"); ++ ++ /* init wdqs */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ tmp = reg_read(base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ ++ wdqs_old.phase[i] = (tmp >> PHY_WDQS_PHASE_BIT) & PHY_WDQS_PHASE_MASK; ++ wdqs_old.bdl[i] = (tmp >> PHY_WDQS_BDL_BIT) & PHY_BDL_MASK; ++ ++ wdqs_new.phase[i] = wdqs_old.phase[i]; ++ wdqs_new.bdl[i] = 0; ++ ++ /* clear wdqs bdl */ ++ reg_write(wdqs_new.phase[i] << PHY_WDQS_PHASE_BIT, ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ } ++ ++ /* enable sw write leveling mode */ ++ ddr_wl_switch(base_dmc, base_phy, DDR_TRUE); ++ ++ /* find first WDQS phase, assume CLK delay > DQS delay. */ ++ result = ddr_wl_process(cfg, DDR_DELAY_PHASE, &wdqs_new); ++ ++ /* check phase result */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ /* find phase error, keep max value to find bdl. */ ++ /* find phase ok, decrease to find bdl. */ ++ if (!result) ++ ddr_phase_dec(&wdqs_new.phase[i]); ++ ++ reg_write(wdqs_new.phase[i] << PHY_WDQS_PHASE_BIT, ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ } ++ ++ /* find WDQS bdl */ ++ result = ddr_wl_process(cfg, DDR_DELAY_BDL, &wdqs_new); ++ ++ /* disable sw write leveling mode */ ++ ddr_wl_switch(base_dmc, base_phy, DDR_FALSE); ++ ++ if (result) { ++ /* restore default value when find WDQS fail */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ tmp = (wdqs_old.phase[i] << PHY_WDQS_PHASE_BIT) + ++ (wdqs_old.bdl[i] << PHY_WDQS_BDL_BIT); ++ reg_write(tmp, base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ return -1; ++ } ++ ++ /* sync delay */ ++ ddr_wl_bdl_sync(cfg, &wdqs_new, &wdqs_old); ++ ++#ifdef DDR_WL_DATAEYE_ADJUST_CONFIG ++ /* adjust WDQ for dataeye */ ++ ddr_wl_wdq_adjust(cfg, &wdqs_new, &wdqs_old); ++#endif ++ ++ return 0; ++} ++ ++int ddr_wl_func(const struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ ++ /* write leveling disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_WL_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_WL_MASK); ++ ++ result += ddr_write_leveling(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_wl_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR WL training"); ++ return 0; ++} ++#endif /* DDR_WL_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/Makefile b/drivers/ddr/vendor/default_v2/Makefile +new file mode 100644 +index 0000000..f79b2b0 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/Makefile +@@ -0,0 +1,16 @@ ++obj-y := ddr_cmd_loc.o ++obj-y += ddr_training_impl.o ++obj-y += ddr_ac_training.o ++obj-y += ddr_dcc_training.o ++obj-y += ddr_ddrt_training.o ++obj-y += ddr_gate_training.o ++obj-y += ddr_lpca_training.o ++obj-y += ddr_mpr_training.o ++obj-y += ddr_pcode_training.o ++obj-y += ddr_wl_training.o ++obj-y += ddr_training_ctl.o ++obj-y += ddr_training_boot.o ++obj-y += ddr_training_custom.o ++obj-y += ddr_training_console.o ++obj-y += ddr_cmd_ctl.o ++obj-y += cmd_ddr_training_v2.o +diff --git a/drivers/ddr/vendor/default_v2/cmd_bin/Makefile b/drivers/ddr/vendor/default_v2/cmd_bin/Makefile +new file mode 100755 +index 0000000..a561663 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_bin/Makefile +@@ -0,0 +1,97 @@ ++sinclude $(TOPDIR)/arch/$(ARCH)/config.mk # include architecture dependend rules ++################################################################################ ++ ++ifeq ($(TOPDIR),) ++TOPDIR := ../../../../.. ++endif ++sinclude $(TOPDIR)/arch/arm/cpu/$(CPU)/config.mk # include architecture dependend rules ++ ++PWD := $(shell pwd) ++OPPDIR := $(subst $(TOPDIR),,$(PWD)) ++ ++CC := $(CROSS_COMPILE)gcc ++AR := $(CROSS_COMPILE)ar ++LD := $(CROSS_COMPILE)ld ++OBJCOPY := $(CROSS_COMPILE)objcopy ++OBJDUMP := $(CROSS_COMPILE)objdump ++ ++################################################################################ ++DDR_CMD := ddr_cmd ++sinclude $(TOPDIR)/include/config/auto.conf #include CONFIG_ARM/CONFIG_ARM64 define ++ ++ifneq ("$(MAKECMDGOALS)","clean") ++CMD_TEXT_BASE := $(shell grep '^\#define.*DDR_TRAINING_RUN_STACK' $(TOPDIR)/drivers/ddr/vendor/$(SOC)/ddr_training_custom.h|awk '{print $$3}') ++endif ++ ++STACK_POINT := $(CMD_TEXT_BASE) ++ ++COBJS := ddr_training_uart.o ddr_training_custom.o ddr_training_cmd.o ddr_training_impl.o ddr_ac_training.o ddr_dcc_training.o ddr_ddrt_training.o \ ++ ddr_gate_training.o ddr_lpca_training.o ddr_mpr_training.o ddr_pcode_training.o ddr_wl_training.o ddr_training_ctl.o ddr_training_console.o ++DEPS := $(COBJS:.o=.d) $(START:.o=.d) ++SSRC := ddr_training_impl.c ddr_ac_training.c ddr_dcc_training.c ddr_ddrt_training.c ddr_gate_training.c \ ++ ddr_lpca_training.c ddr_mpr_training.c ddr_pcode_training.c ddr_wl_training.c ddr_training_ctl.c ddr_training_console.c ++ ++CFLAGS := -Os -pipe \ ++ -DCMD_TEXT_BASE=$(CMD_TEXT_BASE) -DSTACK_POINT=$(STACK_POINT) -DDDR_TRAINING_CMD -DDDR_TRAINING_MEM_FUNC -D__KERNEL__ \ ++ -fno-builtin -ffreestanding -I$(TOPDIR)/arch/$(ARCH)/include -I$(TOPDIR)/include \ ++ -I../ -I./ -I$(TOPDIR)/drivers/ddr/vendor/$(SOC)/ ++ ++ifneq ($(OBJTREE),$(SRCTREE)) ++CFLAGS += -I$(OBJTREE)/include2 -I$(OBJTREE)/include ++endif ++CFLAGS += $(PLATFORM_RELFLAGS) $(PLATFORM_CPPFLAGS) ++ ++ifeq ("$(CONFIG_ARM64)","y") ++START := cmd_entry_64.o ++LDS_SCRIPT := ddr_cmd_64.lds ++CFLAGS += -DCONFIG_ARM64 ++else ++START := cmd_entry_32.o ++LDS_SCRIPT := ddr_cmd_32.lds ++endif ++################################################################################ ++ ++LINK_FILES = $(SSRC) ddr_training_custom.c ++ ++.PHONY: $(DDR_CMD).bin ++all: $(DDR_CMD).bin ++ #remove soft link files ++ @rm -f $(LINK_FILES) *.o *.d *.elf *.map *.srec ++ ++$(DDR_CMD).bin: $(DDR_CMD).elf ++ $(OBJCOPY) -O srec $(PWD)/$(DDR_CMD).elf $(DDR_CMD).srec ++ $(OBJCOPY) --gap-fill=0xff -O binary $(PWD)/$(DDR_CMD).elf $@ ++ ++$(DDR_CMD).elf: $(START) $(COBJS) $(LDS_SCRIPT) ++ #@echo CMD_TEXT_BASE=$(CMD_TEXT_BASE) ++ $(LD) -Bstatic -T $(LDS_SCRIPT) -Ttext $(CMD_TEXT_BASE) $(START) \ ++ $(COBJS) $(AOBJS) -Map $(DDR_CMD).map -o $@ ++ ++$(SSRC): ++ rm -rf $@ ++ rm -rf ddr_training_custom.c ++ ln -sf ../$@ $@ ++ ln -sf $(TOPDIR)/drivers/ddr/vendor/$(SOC)/ddr_training_custom.c ddr_training_custom.c ++ ++.PHONY: clean ++clean: ++ @rm -vf *.o *.d *.elf *.map *.srec $(LINK_FILES) $(DDR_CMD).bin ++ ++%.o : %.S ++ $(CC) -D__ASSEMBLY__ $(CFLAGS) -o $@ -c $*.S ++ ++%.o : %.c ++ $(CC) $(CFLAGS) -Wall -Wstrict-prototypes -fno-stack-protector \ ++ -o $@ -c $*.c ++ ++ifneq ("$(MAKECMDGOALS)","clean") ++sinclude $(DEPS) ++endif ++ ++%.d : %.c ++ set -e; $(CC) $(CFLAGS) -MM $< | sed 's,$*.o:,$*.o $*.d:,g' > $@ ++ ++%.d : %.S ++ set -e; $(CC) $(CFLAGS) -MM $< | sed 's,$*.o:,$*.o $*.d:,g' > $@ ++ ++################################################################################ +diff --git a/drivers/ddr/vendor/default_v2/cmd_bin/cmd_entry_32.S b/drivers/ddr/vendor/default_v2/cmd_bin/cmd_entry_32.S +new file mode 100644 +index 0000000..37660b9 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_bin/cmd_entry_32.S +@@ -0,0 +1,96 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++.section .text ++.type _start, %function ++.globl _start ++_start: ++ push {r0 - r10} ++ b flying ++ ++. = 0x10 ++_CMD_TEXT_BASE: ++ .word CMD_TEXT_BASE ++ ++_arm_start: ++ .word _start ++_flying: ++ .word flying ++_STACK_POINT: ++ .word STACK_POINT ++ ++.globl _bss_start ++_bss_start: .word __bss_start ++.globl _bss_end ++_bss_end: .word _end ++ ++_real_start: ++ .word real_start ++flying: ++ mov r2, pc ++ sub r2, r2, #8 ++ ldr r1, _arm_start ++ ldr r0, _flying ++ sub r1, r0, r1 ++ sub r0, r2, r1 ++ ldr r1, _CMD_TEXT_BASE ++ cmp r0, r1 ++ beq real_start ++ ++ /* need relocation */ ++ ldr r2, _arm_start ++ ldr r3, _bss_start ++ sub r2, r3, r2 ++ add r2, r0, r2 ++self_move: ++ ldmia r0!, {r3 - r10} ++ stmia r1!, {r3 - r10} ++ cmp r0, r2 ++ ble self_move ++ ldr pc, _real_start ++ ++real_start: ++ ldr r0, _bss_start ++ ldr r1, _bss_end ++ mov r2, #0x00000000 ++ ++clear_bss_loop: ++ str r2, [r0] ++ cmp r0, r1 ++ add r0, r0, #4 ++ bne clear_bss_loop ++ ++ ldr r4, =_lr ++ str lr, [r4] ++ pop {r0 - r10} ++ ++ ldr lr, =_sp ++ str sp, [lr] ++ ldr sp, _STACK_POINT ++ ++ bl ddr_training_cmd_entry ++ ++ ldr lr, =_sp ++ ldr sp, [lr] ++ ++ ldr lr, =_lr ++ ldr pc, [lr] ++ ++.section .data ++_lr: .word 0 ++_sp: .word 0 +diff --git a/drivers/ddr/vendor/default_v2/cmd_bin/cmd_entry_64.S b/drivers/ddr/vendor/default_v2/cmd_bin/cmd_entry_64.S +new file mode 100644 +index 0000000..37f56ae +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_bin/cmd_entry_64.S +@@ -0,0 +1,99 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++.section .text ++.type _start, %function ++.globl _start ++_start: ++ stp x29, x30, [sp, #-16]! ++ stp x27, x28, [sp, #-16]! ++ stp x25, x26, [sp, #-16]! ++ stp x23, x24, [sp, #-16]! ++ stp x21, x22, [sp, #-16]! ++ stp x19, x20, [sp, #-16]! ++ stp x17, x18, [sp, #-16]! ++ stp x15, x16, [sp, #-16]! ++ stp x13, x14, [sp, #-16]! ++ stp x11, x12, [sp, #-16]! ++ stp x9, x10, [sp, #-16]! ++ stp x7, x8, [sp, #-16]! ++ stp x5, x6, [sp, #-16]! ++ stp x3, x4, [sp, #-16]! ++ stp x1, x2, [sp, #-16]! ++ ++ b real_start ++ ++_STACK_POINT: ++ .quad STACK_POINT ++ ++.globl _bss_start ++_bss_start: .quad __bss_start ++.globl _bss_end ++_bss_end: .quad _end ++ ++_real_start: ++ .quad real_start ++real_start: ++ ldr x3, _bss_start ++ ldr x1, _bss_end ++ mov x2, #0x00000000 ++ ++clear_bss_loop: ++ str x2, [x3] ++ cmp x3, x1 ++ add x3, x3, #4 ++ bne clear_bss_loop ++ ++ ldr x4, =_x30 ++ str x30, [x4] /* x30 is lr, save lr */ ++ ++ ldp x1, x2, [sp],#16 ++ ldp x3, x4, [sp],#16 ++ ldp x5, x6, [sp],#16 ++ ldp x7, x8, [sp],#16 ++ ldp x9, x10, [sp],#16 ++ ldp x11, x12, [sp],#16 ++ ldp x13, x14, [sp],#16 ++ ldp x15, x16, [sp],#16 ++ ldp x17, x18, [sp],#16 ++ ldp x19, x20, [sp],#16 ++ ldp x21, x22, [sp],#16 ++ ldp x23, x24, [sp],#16 ++ ldp x25, x26, [sp],#16 ++ ldp x27, x28, [sp],#16 ++ ldp x29, x30, [sp],#16 ++ ++ ldr x30, =_sp ++ mov x29, sp ++ str x29, [x30] /* save sp */ ++ ldr x29, _STACK_POINT ++ mov sp, x29 ++ ++ bl ddr_training_cmd_entry ++ ++ ldr x30, =_sp /* restore sp */ ++ ldr x28, [x30] ++ mov sp, x28 ++ ++ ldr x28, =_x30 ++ ldr x30, [x28] /* restore lr */ ++ ret ++ ++.section .data ++_x30: .quad 0 ++_sp: .quad 0 +diff --git a/drivers/ddr/vendor/default_v2/cmd_bin/ddr_cmd_32.lds b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_cmd_32.lds +new file mode 100644 +index 0000000..1f2536f +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_cmd_32.lds +@@ -0,0 +1,28 @@ ++ ++OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") ++OUTPUT_ARCH(arm) ++ENTRY(_start) ++SECTIONS ++{ ++ . = 0x00000000; ++ ++ . = ALIGN(4); ++ .text : { ++ cmd_entry_32.o (.text) ++ *(.text) ++ } ++ ++ . = ALIGN(4); ++ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ++ ++ . = ALIGN(4); ++ .data : { *(.data) } ++ ++ . = ALIGN(4); ++ .got : { *(.got) } ++ ++ . = ALIGN(4); ++ __bss_start = .; ++ .bss : { *(.bss) } ++ _end = .; ++} +diff --git a/drivers/ddr/vendor/default_v2/cmd_bin/ddr_cmd_64.lds b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_cmd_64.lds +new file mode 100644 +index 0000000..313e1d0 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_cmd_64.lds +@@ -0,0 +1,28 @@ ++ ++OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") ++OUTPUT_ARCH(aarch64) ++ENTRY(_start) ++SECTIONS ++{ ++ . = 0x00000000; ++ ++ . = ALIGN(8); ++ .text : { ++ cmd_entry_64.o (.text) ++ *(.text) ++ } ++ ++ . = ALIGN(8); ++ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ++ ++ . = ALIGN(8); ++ .data : { *(.data) } ++ ++ . = ALIGN(8); ++ .got : { *(.got) } ++ ++ . = ALIGN(8); ++ __bss_start = .; ++ .bss : { *(.bss) } ++ _end = .; ++} +diff --git a/drivers/ddr/vendor/default_v2/cmd_bin/ddr_training_cmd.c b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_training_cmd.c +new file mode 100644 +index 0000000..241d7b5 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_training_cmd.c +@@ -0,0 +1,636 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++/* ddr training cmd result */ ++struct ddr_training_result_st g_ddrt_result_sram; ++static unsigned int g_ddr_training_addr_start; ++static unsigned int g_ddr_training_addr_end; ++static int g_ddr_print_level = DDR_LOG_ERROR; ++ ++#ifdef DDR_TRAINING_LOG_CONFIG ++static void ddr_training_log_level(const char *func, int level) ++{ ++ DDR_PUTC('['); ++ switch (level) { ++ case DDR_LOG_INFO: ++ DDR_PUTS("INFO"); ++ break; ++ case DDR_LOG_DEBUG: ++ DDR_PUTS("DEBUG"); ++ break; ++ case DDR_LOG_WARNING: ++ DDR_PUTS("WARNING"); ++ break; ++ case DDR_LOG_ERROR: ++ DDR_PUTS("ERROR"); ++ break; ++ case DDR_LOG_FATAL: ++ DDR_PUTS("FATAL"); ++ break; ++ default: ++ break; ++ } ++ DDR_PUTC(']'); ++ DDR_PUTC('['); ++ DDR_PUTS(func); ++ DDR_PUTC(']'); ++} ++ ++/* Log ddr training info */ ++void ddr_training_log(const char *func, int level, const char *fmt, ...) ++{ ++ va_list args; ++ ++ if (fmt == NULL) ++ return; ++ if (g_ddr_print_level > level) ++ return; ++ ++ ddr_training_log_level(func, level); ++ ++ va_start(args, fmt); ++ while (*fmt != '\0') { ++ if (*fmt != '%') { ++ DDR_PUTC(*fmt); ++ } else { ++ fmt++; ++ switch (*fmt) { ++ case 'x': ++ case 'X': ++ DDR_PUTS("0x"); ++ DDR_PUT_HEX(va_arg(args, int)); ++ break; ++ default: ++ DDR_PUTC('%'); ++ DDR_PUTC(*fmt); ++ break; ++ } /* switch */ ++ } ++ fmt++; ++ } /* while */ ++ va_end(args); ++ DDR_PUTS("\r\n"); ++} ++ ++/* Nothing to do in DDR command when defined DDR_TRAINING_LOG_CONFIG */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ return; ++} ++#else ++/* Display DDR training error */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ switch (mask) { ++ case DDR_ERR_WL: ++ DDR_PUTS("WL"); ++ break; ++ case DDR_ERR_HW_GATING: ++ DDR_PUTS("HW Gate"); ++ break; ++ case DDR_ERR_GATING: ++ DDR_PUTS("Gate"); ++ break; ++ case DDR_ERR_DDRT_TIME_OUT: ++ DDR_PUTS("DDRT"); ++ break; ++ case DDR_ERR_HW_RD_DATAEYE: ++ DDR_PUTS("HW Dataeye"); ++ break; ++ case DDR_ERR_MPR: ++ DDR_PUTS("MPR"); ++ break; ++ case DDR_ERR_DATAEYE: ++ DDR_PUTS("Dataeye"); ++ break; ++ case DDR_ERR_LPCA: ++ DDR_PUTS("LPCA"); ++ break; ++ default: ++ break; ++ } ++ ++ DDR_PUTS(" Err:"); ++ ++ if (phy != 0) { ++ DDR_PUTS(" Phy:"); ++ DDR_PUT_HEX(phy); ++ } ++ ++ if (byte != -1) { ++ DDR_PUTS(" Byte:"); ++ DDR_PUT_HEX(byte); ++ } ++ ++ if (dq != -1) { ++ DDR_PUTS(" DQ:"); ++ DDR_PUT_HEX(dq); ++ } ++ ++ DDR_PUTS("\r\n"); ++} ++#endif ++ ++/* Inint ddr training cmd result */ ++static void ddr_training_result_init(const struct ddr_cfg_st *cfg, struct ddr_training_result_st *ddrtr_res) ++{ ++ int i, j; ++ ++ memset(ddrtr_res, 0, sizeof(struct ddr_training_result_st)); ++ if (cfg->phy_num > DDR_PHY_NUM) { ++ ddr_error("loop upper limit phy_num or rank_num out of range"); ++ return; ++ } ++ ddrtr_res->phy_num = cfg->phy_num; ++ for (i = 0; i < cfg->phy_num; i++) { ++ if (cfg->phy[i].rank_num > DDR_SUPPORT_RANK_MAX) { ++ ddr_error("loop upper limit rank_num out of range"); ++ return; ++ } ++ ddrtr_res->phy_st[i].rank_num = cfg->phy[i].rank_num; ++ ++ for (j = 0; j < cfg->phy[i].rank_num; j++) { ++ ddrtr_res->phy_st[i].rank_st[j].item = cfg->phy[i].rank[j].item; ++ ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.base_phy = cfg->phy[i].addr; ++ ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.byte_num = cfg->phy[i].total_byte_num; ++ ddrtr_res->phy_st[i].rank_st[j].ddrtr_data.rank_idx = j; ++ } ++ } ++} ++ ++/* Save ddr training cmd result */ ++void ddr_result_data_save(struct ddr_cfg_st *cfg, const struct training_data *training) ++{ ++ unsigned int i; ++ unsigned int offset; ++ struct training_data *dest = NULL; ++ struct ddr_training_result_st *ddrtr_res = NULL; ++ ++ if (cfg == NULL || training == NULL) { ++ ddr_error("Pointer parameter training or ddrtr_res is NULL!"); ++ return; ++ } ++ ++ ddrtr_res = (struct ddr_training_result_st *)cfg->res_st; ++ if (ddrtr_res == NULL) ++ return; ++ ++ if ((cfg->phy_idx >= DDR_PHY_NUM) || (cfg->dmc_idx >= DDR_DMC_PER_PHY_MAX)) { ++ ddr_error("Array index phy_idx or dmc_idx out of range!"); ++ return; ++ } ++ ++ if (cfg->cur_mode == DDR_MODE_READ) ++ dest = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data.read; ++ else ++ dest = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data.write; ++ ++ if (cfg->phy[cfg->phy_idx].dmc_num == 1) { ++ memcpy(dest, training, sizeof(struct training_data)); ++ } else { ++ /* dmc[0] + dmc[1] */ ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) { ++ ddr_error("get byte num fail, byte_num = %x", get_byte_num(cfg)); ++ return; ++ } ++ if ((get_byte_num(cfg) << DDR_BYTE_DQ) > DDR_PHY_BIT_MAX) { ++ ddr_error("get bit max num fail"); ++ return; ++ } ++ offset = cfg->dmc_idx << 4; /* Shift left 4:16 */ ++ for (i = 0; i < (get_byte_num(cfg) << DDR_BYTE_DQ); i++) { ++ dest->ddr_bit_result[i + offset] = training->ddr_bit_result[i + offset]; ++ dest->ddr_bit_best[i + offset] = training->ddr_bit_best[i + offset]; ++ } ++ dest->ddr_win_sum += training->ddr_win_sum; ++ } ++} ++ ++/* Save lpca training data */ ++void ddr_lpca_data_save(struct ddr_cfg_st *cfg, const struct ca_data_st *data) ++{ ++ unsigned int index; ++ struct ddr_training_result_st *ddrtr_res = NULL; ++ struct ddr_training_data_st *tr_data = NULL; ++ ++ if (cfg == NULL || data == NULL) { ++ ddr_error("Pointer parameter is NULL"); ++ return; ++ } ++ ++ ddrtr_res = (struct ddr_training_result_st *)cfg->res_st; ++ if (ddrtr_res == NULL) ++ return; ++ ++ tr_data = &ddrtr_res->phy_st[cfg->phy_idx].rank_st[cfg->rank_idx].ddrtr_data; ++ if (tr_data == NULL) ++ return; ++ ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) ++ tr_data->ca_addr[index] = ((unsigned int)data->left[index] << ++ DDR_DATAEYE_RESULT_BIT) | (unsigned int)data->right[index]; ++} ++ ++/* Get DDRT test addrress */ ++unsigned int ddr_ddrt_get_test_addr(void) ++{ ++ if (g_ddr_training_addr_start <= DDRT_CFG_TEST_ADDR_CMD && ++ g_ddr_training_addr_end >= DDRT_CFG_TEST_ADDR_CMD) { ++ return DDRT_CFG_TEST_ADDR_CMD; ++ } else { ++ ddr_error("DDRT test address[%x] out of range[%x, %x]", ++ DDRT_CFG_TEST_ADDR_CMD, ++ g_ddr_training_addr_start, ++ g_ddr_training_addr_end); ++ return g_ddr_training_addr_start; ++ } ++} ++ ++/* Nothing to do in DDR command */ ++void ddr_training_suc(void) ++{ ++ return; ++} ++ ++/* Nothing to do in DDR command */ ++void ddr_training_start(void) ++{ ++ return; ++} ++ ++/* dump WDQS result */ ++static void ddr_dump_wdqs_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] WDQS Byte(%x) ", ++ base_phy + ddr_phy_dxwdqsdly(rank, i), ++ reg_read(base_phy + ddr_phy_dxwdqsdly(rank, i)), i); ++} ++ ++/* dump WDQ Phase result */ ++static void ddr_dump_wdq_phase_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] WDQ Phase Byte(%x)", ++ base_phy + ddr_phy_dxnwdqdly(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqdly(rank, i)), i); ++} ++ ++/* dump WDQ BDL result */ ++static void ddr_dump_wdq_bdl_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte number error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) { ++ /* DQ0-DQ3 */ ++ ddr_info("[%x = %x] WDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnwdqnbdl0(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqnbdl0(rank, i)), ++ (i << 3), ((i << 3) + 3)); /* DQ0-DQ3, Shift left 3:8 dq */ ++ ++ /* DQ4-DQ7 */ ++ ddr_info("[%x = %x] WDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnwdqnbdl1(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqnbdl1(rank, i)), ++ ((i << 3) + 4), ((i << 3) + 7)); /* DQ4-DQ7, Shift left 3:8 dq */ ++ } ++} ++ ++/* dump WDM result */ ++static void ddr_dump_wdm_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] WDM Byte(%x)", ++ base_phy + ddr_phy_dxnwdqnbdl2(rank, i), ++ reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank, i)), i); ++} ++ ++/* dump RDQS result */ ++static void ddr_dump_rdqs_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] RDQS Byte(%x)", ++ base_phy + ddr_phy_dxnrdqsdly(i), ++ reg_read(base_phy + ddr_phy_dxnrdqsdly(i)), i); ++} ++ ++/* RDQ BDL */ ++static void ddr_dump_rdq_bdl_result(unsigned int base_phy, unsigned int byte_num, ++ unsigned int rank) ++{ ++ unsigned int i; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) { ++ /* DQ0-DQ3 */ ++ ddr_info("[%x = %x] RDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnrdqnbdl0(rank, i), ++ reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank, i)), ++ (i << 3), ((i << 3) + 3)); /* DQ0-DQ3, Shift left 3:8 dq */ ++ ++ /* DQ4-DQ7 */ ++ ddr_info("[%x = %x] RDQ BDL DQ(%x-%x)", ++ base_phy + ddr_phy_dxnrdqnbdl1(rank, i), ++ reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank, i)), ++ ((i << 3) + 4), ((i << 3) + 7)); /* DQ4-DQ7, Shift left 3:8 dq */ ++ } ++} ++ ++static void dump_result(const struct ddr_training_data_st *ddrtr_data) ++{ ++ unsigned int i; ++ unsigned int base_phy = ddrtr_data->base_phy; ++ unsigned int byte_num = ddrtr_data->byte_num; ++ unsigned int rank = ddrtr_data->rank_idx; ++ unsigned int acphyctl7; ++ ++ /* Static register have to read two times to get the right value. */ ++ acphyctl7 = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ acphyctl7 = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ ++ if ((byte_num > DDR_PHY_BYTE_MAX) || ((byte_num << DDR_BYTE_DQ) > DDR_PHY_BIT_MAX)) { ++ ddr_error("loop upper limit out of range, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < (byte_num << DDR_BYTE_DQ); i++) ++ ddr_info("Byte[%x] Write[%x][%x] Read[%x][%x]", ++ i, ddrtr_data->write.ddr_bit_result[i], ddrtr_data->write.ddr_bit_best[i], ++ ddrtr_data->read.ddr_bit_result[i], ddrtr_data->read.ddr_bit_best[i]); ++ ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ if (ddrtr_data->ca_addr[i] != 0) ++ ddr_info("CA[%x] Range[%x]", i, ddrtr_data->ca_addr[i]); ++ } ++ ++ /* WDQS */ ++ ddr_dump_wdqs_result(base_phy, byte_num, rank); ++ ++ /* WDQ Phase */ ++ ddr_dump_wdq_phase_result(base_phy, byte_num, rank); ++ ++ /* WDQ BDL */ ++ ddr_dump_wdq_bdl_result(base_phy, byte_num, rank); ++ ++ /* WDM */ ++ ddr_dump_wdm_result(base_phy, byte_num, rank); ++ ++ /* Write DO/DOS OE */ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] Write DQ/DQS OE Byte(%x)", ++ base_phy + ddr_phy_dxnoebdl(rank, i), ++ reg_read(base_phy + ddr_phy_dxnoebdl(rank, i)), i); ++ ++ /* RDQS */ ++ ddr_dump_rdqs_result(base_phy, byte_num, rank); ++ ++ /* RDQ BDL */ ++ ddr_dump_rdq_bdl_result(base_phy, byte_num, rank); ++ ++ /* Gate */ ++ for (i = 0; i < byte_num; i++) ++ ddr_info("[%x = %x] Gate Byte(%x)", ++ base_phy + ddr_phy_dxnrdqsgdly(rank, i), ++ reg_read(base_phy + ddr_phy_dxnrdqsgdly(rank, i)), i); ++ ++ ddr_info("[%x = %x] CS", base_phy + DDR_PHY_ACCMDBDL2, ++ reg_read(base_phy + DDR_PHY_ACCMDBDL2)); ++ ++ ddr_info("[%x = %x] CLK", base_phy + DDR_PHY_ACPHYCTL7, acphyctl7); ++ ++ ddr_phy_switch_rank(base_phy, rank); ++ ++ /* HOST Vref */ ++ ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num); ++ ++ /* DRAM Vref */ ++ ddr_phy_vref_dram_display_cmd(base_phy, byte_num); ++ ++ /* DPMC */ ++ ddr_dx_dpmc_display_cmd(base_phy, byte_num); ++ ++ /* Addr Phase */ ++ ddr_phy_addrph_display_cmd(base_phy); ++ ++ /* Addr BDL */ ++ ddr_phy_addrbdl_display_cmd(base_phy); ++ ++ /* DCC */ ++ ddr_phy_dcc_display_cmd(base_phy); ++ ++ ddr_phy_switch_rank(base_phy, 0); ++} ++ ++static void dump_result_by_rank(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int phy_index, unsigned int rank_index) ++{ ++ unsigned int mask = 1 << phy_index; /* DDR_BYPASS_PHY0_MASK DDR_BYPASS_PHY1_MASK */ ++ const struct rank_data_st *rank_st = &ddrtr_result->phy_st[phy_index].rank_st[rank_index]; ++ ++ if (rank_st->item & mask) ++ return; ++ ++ ddr_info("PHY[%x] RANK[%x]:", phy_index, rank_index); ++ dump_result(&rank_st->ddrtr_data); ++} ++ ++static void dump_result_by_phy(const struct ddr_training_result_st *ddrtr_result, unsigned int phy_index) ++{ ++ int i; ++ const struct phy_data_st *phy_st = &ddrtr_result->phy_st[phy_index]; ++ ++ if (phy_st->rank_num > DDR_SUPPORT_RANK_MAX) { ++ ddr_error("loop upper limit cfg->rank_num out of range!"); ++ return; ++ } ++ for (i = 0; i < phy_st->rank_num; i++) ++ dump_result_by_rank(ddrtr_result, phy_index, i); ++} ++ ++/* Display ddr training result before return to DDR */ ++static void dump_result_all(const struct ddr_training_result_st *ddrtr_result) ++{ ++ int i; ++ ++ if (ddrtr_result->phy_num > DDR_PHY_NUM) { ++ ddr_error("loop upper limit phy number out of range, phy_num = %x", ++ ddrtr_result->phy_num); ++ return; ++ } ++ for (i = 0; i < ddrtr_result->phy_num; i++) ++ dump_result_by_phy(ddrtr_result, i); ++} ++ ++/* ++ * If you need to execute mpr, add DDR_TRAINING_CMD_MPR -- ddr_mpr_training_func(cfg) ++ * lpca:DDR_TRAINING_CMD_LPCA -- ddr_lpca_training_func(cfg) ++ */ ++int ddr_training_cmd_func(struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int item; ++ ++ if ((cfg == NULL) || (cfg->cmd_st == NULL)) ++ return -1; ++ ++ item = cfg->cur_item; ++ struct ddr_cmd_st *cmd_st = (struct ddr_cmd_st *)cfg->cmd_st; ++ ++ if (cmd_st == NULL) ++ return -1; ++ ++ ddr_debug("DDR training cmd[%x]", cmd_st->cmd); ++ ++ switch (cmd_st->cmd) { ++ case DDR_TRAINING_CMD_SW: ++ result = ddr_dataeye_training_func(cfg); ++ result += ddr_vref_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_DATAEYE: ++ result = ddr_dataeye_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_HW: ++ result = ddr_hw_training_if(); ++ break; ++ case DDR_TRAINING_CMD_WL: ++ result = ddr_wl_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_GATE: ++ result = ddr_gating_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_VREF: ++ result = ddr_vref_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_AC: ++ result = ddr_ac_training_func(cfg); ++ break; ++ case DDR_TRAINING_CMD_SW_NO_WL: ++ /* wl bypass */ ++ reg_write(item | DDR_BYPASS_WL_MASK, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ result = ddr_dataeye_training_func(cfg); ++ result += ddr_vref_training_func(cfg); ++ /* restore cfg */ ++ reg_write(item, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ break; ++ case DDR_TRAINING_CMD_CONSOLE: ++ result = ddr_training_console_if(); ++ break; ++ default: ++ result = -1; ++ break; ++ } ++ ++ return result; ++} ++ ++/* DDR training command entry. Call by cmd_ddr_handle(). */ ++struct ddr_training_result_st *ddr_training_cmd_entry(const struct ddr_cmd_st *cmd_st) ++{ ++ int result; ++ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ struct ddr_cmd_st cmd_in_sram; ++ ++ if (cmd_st == NULL) { ++ ddr_error("Pointer parameter cmd_st is NULL!"); ++ return NULL; ++ } ++ ++ g_ddr_training_addr_start = cmd_st->start; ++ g_ddr_training_addr_end = cmd_st->start + cmd_st->length; ++ g_ddr_print_level = cmd_st->level; ++ ++ ddr_info("DDR Training Version: "DDR_TRAINING_VER); ++ ddr_debug("DDR training command entry. Sysctl[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG)); ++ ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ ddr_debug("Rank1 Sysctl[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC)); ++#endif ++ ++ ddr_training_cfg_init(cfg); ++ memcpy(&cmd_in_sram, cmd_st, sizeof(struct ddr_cmd_st)); ++ cfg->cmd_st = (void *)&cmd_in_sram; ++ cfg->res_st = (void *)&g_ddrt_result_sram; ++ ++ ddr_training_result_init(cfg, &g_ddrt_result_sram); ++ ++ if (cmd_st->cmd == DDR_TRAINING_CMD_HW) ++ result = ddr_hw_training(cfg); ++ else if (cmd_st->cmd == DDR_TRAINING_CMD_PCODE) ++ result = ddr_pcode_training(cfg); ++ else if (cmd_st->cmd == DDR_TRAINING_CMD_DCC) ++ result = ddr_dcc_training_func(cfg); ++ else if (cmd_st->cmd == DDR_TRAINING_CMD_CONSOLE) ++ result = ddr_training_console_if(); ++ else ++ result = ddr_training_all(cfg); ++ ++ dump_result_all(&g_ddrt_result_sram); ++ ++ if (!result) { ++ return &g_ddrt_result_sram; ++ } else { ++ ddr_debug("DDR training result[%x]", result); ++ return 0; ++ } ++} +diff --git a/drivers/ddr/vendor/default_v2/cmd_bin/ddr_training_uart.c b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_training_uart.c +new file mode 100644 +index 0000000..b339d56 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_bin/ddr_training_uart.c +@@ -0,0 +1,60 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "ddr_training_custom.h" ++ ++#define UART_PL01X_DR 0x00 /* Data read or written from the interface. */ ++#define UART_PL01X_FR 0x18 /* Flag register (Read only). */ ++#define UART_PL01X_FR_TXFF 0x20 ++ ++#define io_write(addr, val) (*(volatile unsigned int *)(addr) = (val)) ++#define io_read(addr) (*(volatile unsigned int *)(addr)) ++ ++void uart_early_putc(const char c) ++{ ++ /* Wait until there is space in the FIFO */ ++ while (io_read(DDR_REG_BASE_UART0 + UART_PL01X_FR) & UART_PL01X_FR_TXFF); ++ ++ /* Send the character */ ++ io_write(DDR_REG_BASE_UART0 + UART_PL01X_DR, c); ++} ++ ++void uart_early_puts(const char *s) ++{ ++ if (s == NULL) ++ return; ++ while (*s) ++ uart_early_putc (*s++); ++} ++ ++void uart_early_put_hex(const unsigned int hex) ++{ ++ int i; ++ char c; ++ ++ for (i = 28; i >= 0; i -= 4) { /* start from bit28, 4 bit per char */ ++ c = (hex >> (unsigned int)i) & 0x0F; ++ if (c < 10) /* Decimal 10 */ ++ c += '0'; ++ else ++ c += 'A' - 10; /* Decimal 10 */ ++ uart_early_putc(c); ++ } ++} +diff --git a/drivers/ddr/vendor/default_v2/cmd_ddr_training_v2.c b/drivers/ddr/vendor/default_v2/cmd_ddr_training_v2.c +new file mode 100644 +index 0000000..cd6c012 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/cmd_ddr_training_v2.c +@@ -0,0 +1,302 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "ddr_training_impl.h" ++ ++#ifndef TEXT_BASE ++#define TEXT_BASE (CONFIG_SYS_TEXT_BASE) /* for arm64 u-boot-2016.11 */ ++#endif ++ ++#define DDR_TRAINING_ENV "ddrtr" ++#define DDR_TRAINING_ENV_UN "unddrtr" ++ ++#define DDR_TRAINING_DDRT_START_OFFSET 0x400000 /* 4M */ ++#define DDR_TRAINING_DDRT_LENGTH 0x400000 /* 4M at lease 0x8000 */ ++ ++#define DDR_CMD_SW_STR "training" ++#define DDR_CMD_TR_STR "tr" ++#define DDR_CMD_HW_STR "hw" ++#define DDR_CMD_MPR_STR "mpr" ++#define DDR_CMD_WL_STR "wl" ++#define DDR_CMD_GATE_STR "gate" ++#define DDR_CMD_DATAEYE_STR "dataeye" ++#define DDR_CMD_VREF_STR "vref" ++#define DDR_CMD_DPMC_STR "dpmc" ++#define DDR_CMD_DCC_STR "dcc" ++#define DDR_CMD_PCODE_STR "pcode" ++#define DDR_CMD_AC_STR "ac" ++#define DDR_CMD_LPCA_STR "lpca" ++#define DDR_CMD_LOG_STR "log" ++#define DDR_CMD_BOOT_STR "boot" ++#define DDR_CMD_CONSOLE_STR "console" ++ ++#ifndef CONFIG_MINI_BOOT ++static struct ddr_training_result_st g_ddrtr_result_st; /* DDR training result */ ++static int g_ddr_log_level = DDR_LOG_ERROR; /* DDR training log level */ ++#endif ++ ++#ifdef DDR_TRAINING_EXEC_TIME ++/* ++ * Start timer for calculate DDR training execute time. ++ * NOTE: Just only for debug. ++ */ ++static void cmd_exec_timer_start(void) ++{ ++ /* timer start */ ++ reg_write(0, 0xF8002000); /* REG_BASE_TIMER01 + REG_TIMER_RELOAD */ ++ /* TIMER_EN | TIMER_MODE |TIMER_PRE | TIMER_SIZE, REG_TIMER_CONTROL */ ++ reg_write(0xc2, 0xF8002008); ++ reg_write(0xffffffff, 0xF8002000); ++} ++ ++/* ++ * Stop timer for calculate DDR training execute time. ++ * NOTE: Just only for debug. ++ */ ++static void cmd_exec_timer_stop(void) ++{ ++ /* timer stop */ ++ reg_write(0, 0xF8002008); /* REG_TIMER_CONTROL */ ++ /* REG_TIMER_VALUE, 24MHz */ ++ printf("DDR training execute time: [%d]us\n", ++ (0xffffffff - reg_read(0xF8002004)) / 24); /* 24MHz */ ++} ++#endif ++ ++#ifndef CONFIG_MINI_BOOT ++/* ++ * Match string command. ++ * NOTE: Write leveling not support run repeatedly, ++ * so limit WL only run one time. ++ */ ++static int cmd_ddr_match(const char *str, int *cmd) ++{ ++ static int wl_done; /* Write leveling control */ ++ ++ if (!strncmp(str, DDR_CMD_SW_STR, sizeof(DDR_CMD_SW_STR))) { ++ *cmd = DDR_TRAINING_CMD_SW_NO_WL; ++ } else if (!strncmp(str, DDR_CMD_TR_STR, sizeof(DDR_CMD_TR_STR))) { ++ if (wl_done) { ++ *cmd = DDR_TRAINING_CMD_SW_NO_WL; ++ } else { ++ wl_done++; ++ *cmd = DDR_TRAINING_CMD_SW; ++ } ++ } else if (!strncmp(str, DDR_CMD_HW_STR, sizeof(DDR_CMD_HW_STR))) { ++ *cmd = DDR_TRAINING_CMD_HW; ++ } else if (!strncmp(str, DDR_CMD_MPR_STR, sizeof(DDR_CMD_MPR_STR))) { ++ *cmd = DDR_TRAINING_CMD_MPR; ++ } else if (!strncmp(str, DDR_CMD_WL_STR, sizeof(DDR_CMD_WL_STR))) { ++ if (wl_done) { ++ printf("WL not support run repeatedly. %s", ++ "Already done once, can not do again\n"); ++ return -1; ++ } else { ++ *cmd = DDR_TRAINING_CMD_WL; ++ wl_done++; ++ } ++ } else if (!strncmp(str, DDR_CMD_GATE_STR, sizeof(DDR_CMD_GATE_STR))) { ++ *cmd = DDR_TRAINING_CMD_GATE; ++ } else if (!strncmp(str, DDR_CMD_DATAEYE_STR, sizeof(DDR_CMD_DATAEYE_STR))) { ++ *cmd = DDR_TRAINING_CMD_DATAEYE; ++ } else if (!strncmp(str, DDR_CMD_VREF_STR, sizeof(DDR_CMD_VREF_STR))) { ++ *cmd = DDR_TRAINING_CMD_VREF; ++ } else if (!strncmp(str, DDR_CMD_DPMC_STR, sizeof(DDR_CMD_DPMC_STR))) { ++ *cmd = DDR_TRAINING_CMD_DPMC; ++ } else if (!strncmp(str, DDR_CMD_AC_STR, sizeof(DDR_CMD_AC_STR))) { ++ *cmd = DDR_TRAINING_CMD_AC; ++ } else if (!strncmp(str, DDR_CMD_LPCA_STR, sizeof(DDR_CMD_LPCA_STR))) { ++ *cmd = DDR_TRAINING_CMD_LPCA; ++ } else if (!strncmp(str, DDR_CMD_DCC_STR, sizeof(DDR_CMD_DCC_STR))) { ++ *cmd = DDR_TRAINING_CMD_DCC; ++ } else if (!strncmp(str, DDR_CMD_PCODE_STR, sizeof(DDR_CMD_PCODE_STR))) { ++ *cmd = DDR_TRAINING_CMD_PCODE; ++ } else if (!strncmp(str, DDR_CMD_CONSOLE_STR, sizeof(DDR_CMD_CONSOLE_STR))) { ++ *cmd = DDR_TRAINING_CMD_CONSOLE; ++ } else { ++ printf("Command [ddr %s] is unsupport\n", str); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Handle DDR training. ++ * Copy training codes from DDR to SRAM. ++ */ ++static int cmd_ddr_handle(int cmd) ++{ ++ struct ddr_training_result_st *result_st = NULL; ++ struct ddr_cmd_st cmd_st; ++ ++ cmd_st.cmd = cmd; ++ cmd_st.level = g_ddr_log_level; ++ cmd_st.start = TEXT_BASE + DDR_TRAINING_DDRT_START_OFFSET; ++ cmd_st.length = DDR_TRAINING_DDRT_LENGTH; ++ ++ printf("DDR training area: 0x%08X - 0x%08X\n", ++ cmd_st.start, cmd_st.start + cmd_st.length); ++ ++#ifdef DDR_TRAINING_EXEC_TIME ++ cmd_exec_timer_start(); ++#endif ++ ++ result_st = ddr_cmd_training_if(&cmd_st); ++ ++#ifdef DDR_TRAINING_EXEC_TIME ++ cmd_exec_timer_stop(); ++#endif ++ ++ if (result_st == NULL) ++ return -1; ++ ++ /* copy training result from SRAM to DDR */ ++ memcpy((void *)&g_ddrtr_result_st, result_st, ++ sizeof(*result_st)); ++ printf("DDR training finished\n"); ++ ++ return 0; ++} ++ ++/* DDR training cmd dispatch */ ++static int cmd_ddr_dispatch(int cmd) ++{ ++ int result; ++ ++ result = cmd_ddr_handle(cmd); ++ switch (cmd) { ++ case DDR_TRAINING_CMD_SW: ++ case DDR_TRAINING_CMD_SW_NO_WL: ++ case DDR_TRAINING_CMD_CONSOLE: ++ ddr_cmd_result_display(&g_ddrtr_result_st, DDR_TRAINING_CMD_DATAEYE | DDR_TRAINING_CMD_LPCA); ++ break; ++ case DDR_TRAINING_CMD_DATAEYE: ++ case DDR_TRAINING_CMD_VREF: ++ ddr_cmd_result_display(&g_ddrtr_result_st, DDR_TRAINING_CMD_DATAEYE); ++ break; ++ case DDR_TRAINING_CMD_WL: ++ case DDR_TRAINING_CMD_GATE: ++ case DDR_TRAINING_CMD_HW: ++ case DDR_TRAINING_CMD_DPMC: ++ case DDR_TRAINING_CMD_DCC: ++ case DDR_TRAINING_CMD_PCODE: ++ break; ++ case DDR_TRAINING_CMD_LPCA: ++ ddr_cmd_result_display(&g_ddrtr_result_st, DDR_TRAINING_CMD_LPCA); ++ break; ++ default: ++ break; ++ } ++ ++ ddr_reg_result_display(&g_ddrtr_result_st); ++ return result; ++} ++ ++/* Set DDR training log level */ ++static int cmd_ddr_set_log_level(char * const argv[]) ++{ ++ int level; ++ const char *str; ++ ++ str = argv[1]; ++ if (strncmp(str, DDR_CMD_LOG_STR, sizeof(DDR_CMD_LOG_STR))) { ++ printf("Command [ddr %s] is unsupport\n", str); ++ return -1; ++ } ++ ++ str = argv[2]; /* argv[2]:Third string: ddr log level */ ++ if (!strncmp(str, DDR_LOG_INFO_STR, sizeof(DDR_LOG_INFO_STR))) { ++ level = DDR_LOG_INFO; ++ } else if (!strncmp(str, DDR_LOG_DEBUG_STR, sizeof(DDR_LOG_DEBUG_STR))) { ++ level = DDR_LOG_DEBUG; ++ } else if (!strncmp(str, DDR_LOG_WARNING_STR, sizeof(DDR_LOG_WARNING_STR))) { ++ level = DDR_LOG_WARNING; ++ } else if (!strncmp(str, DDR_LOG_ERROR_STR, sizeof(DDR_LOG_ERROR_STR))) { ++ level = DDR_LOG_ERROR; ++ } else if (!strncmp(str, DDR_LOG_FATAL_STR, sizeof(DDR_LOG_FATAL_STR))) { ++ level = DDR_LOG_FATAL; ++ } else { ++ printf("Command [ddr log %s] is unsupport\n", str); ++ return -1; ++ } ++ ++ g_ddr_log_level = level; ++ printf("Set DDR training log level [%s] suc\n", str); ++ ++ return 0; ++} ++ ++/* ++ * Accept DDR training cmd. ++ * Set training result to env without save. ++ */ ++static int do_ddr_training(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) ++{ ++ const char *str = NULL; ++ int cmd; ++ struct tr_custom_reg reg; ++ ++ if (argc < 2 || argc > 3) { /* cmd string should be 2 or 3 */ ++ return -1; /* add cmd_usage(cmdtp) if needed */ ++ } else if (argc == 3) { /* 3 cmd string means ddr log level */ ++ return cmd_ddr_set_log_level(argv); ++ } ++ ++ str = argv[1]; ++ ++ if (cmd_ddr_match(str, &cmd)) ++ return -1; ++ ++ memset(®, 0, sizeof(struct tr_custom_reg)); ++ if (ddr_boot_cmd_save(®) != 0) { ++ printf("DDR cmd save wait tiemout!\n"); ++ return -1; ++ } ++ ++ if (cmd_ddr_dispatch(cmd)) ++ return -1; ++ ++ ddr_boot_cmd_restore(®); ++ ++ return 0; ++} ++ ++U_BOOT_CMD( ++ ddr, CONFIG_SYS_MAXARGS, 1, do_ddr_training, ++ "ddr training function", ++ "training - DDR sofeware(Gate/Dataeye/Vref) training.\n" ++ "ddr tr - DDR sofeware(WL/Gate/Dataeye/Vref) training.\n" ++ "ddr wl - DDR Write leveling training.\n" ++ "ddr gate - DDR gate training.\n" ++ "ddr dataeye - DDR dataeye training and display training result.\n" ++ "ddr vref - DDR vref training.\n" ++ "ddr dpmc - DDR dpmc training.\n" ++ "ddr hw - DDR hardware training.\n" ++ "ddr mpr - DDR Multi-Purpose Register training.\n" ++ "ddr ac - DDR address command training.\n" ++ "ddr lpca - LPDDR command address training.\n" ++ "ddr dcc - DDR Duty Correction Control training.\n" ++ "ddr pcode - DDR io pcode training.\n" ++ "ddr console - DDR do training in SRAM.\n" ++ "ddr log [level] - DDR log level. [info,debug,warning,error,fatal]\n" ++); ++#endif +diff --git a/drivers/ddr/vendor/default_v2/ddr_ac_training.c b/drivers/ddr/vendor/default_v2/ddr_ac_training.c +new file mode 100644 +index 0000000..59cbba4 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_ac_training.c +@@ -0,0 +1,308 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __AC_TRAINING__ ++#ifdef DDR_AC_TRAINING_CONFIG ++/* ++ * Get clk value. ++ * Assume clk0 and clk1 is the same. ++ */ ++static int ddr_ac_get_clk(unsigned int base_phy) ++{ ++ unsigned int val; ++ unsigned int ac_phy_ctl; ++ /* Static register have to read two times to get the right value. */ ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ /* halft_dramclk0 */ ++ val = (ac_phy_ctl >> PHY_ACPHY_DRAMCLK0_BIT) & PHY_ACPHY_DRAMCLK_MASK; ++ val = (val << PHY_ACPHY_DRAMCLK_EXT_BIT) | ++ ((ac_phy_ctl >> PHY_ACPHY_DCLK0_BIT) & PHY_ACPHY_DCLK_MASK); ++ return val; ++} ++ ++/* Set clk0 and clk1 the same value */ ++static void ddr_ac_set_clk(unsigned int base_phy, unsigned int val) ++{ ++ unsigned int ac_phy_ctl, dramclk, dclk; ++ ++ dclk = val & PHY_ACPHY_DCLK_MASK; ++ dramclk = (val >> PHY_ACPHY_DRAMCLK_EXT_BIT) & PHY_ACPHY_DRAMCLK_MASK; ++ /* Static register have to read two times to get the right value. */ ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ /* clear cp1p_dclk0 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK0_BIT)); ++ /* clear ck2p_dclk1 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK1_BIT)); ++ /* clear halft_dramclk0 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DRAMCLK_MASK << PHY_ACPHY_DRAMCLK0_BIT)); ++ /* clear halft_dramclk1 */ ++ ac_phy_ctl &= (~(PHY_ACPHY_DRAMCLK_MASK << PHY_ACPHY_DRAMCLK1_BIT)); ++ ++ ac_phy_ctl |= (dclk << PHY_ACPHY_DCLK0_BIT); /* set cp1p_dclk0 */ ++ ac_phy_ctl |= (dclk << PHY_ACPHY_DCLK1_BIT); /* set cp2p_dclk1 */ ++ /* set halft_dramclk0 */ ++ ac_phy_ctl |= (dramclk << PHY_ACPHY_DRAMCLK0_BIT); ++ /* set halft_dramclk1 */ ++ ac_phy_ctl |= (dramclk << PHY_ACPHY_DRAMCLK1_BIT); ++ reg_write(ac_phy_ctl, base_phy + DDR_PHY_ACPHYCTL7); ++} ++ ++/* ++ * Get cs bdl value. ++ * Assume cs0 and cs 1 is the same. ++ */ ++static int ddr_ac_get_cs(unsigned int base_phy) ++{ ++ return (reg_read(base_phy + DDR_PHY_ACCMDBDL2) >> 1) & PHY_BDL_MASK; ++} ++ ++/* Set CS value */ ++static void ddr_ac_set_cs(unsigned int base_phy, unsigned int val) ++{ ++ unsigned int ac_cmd_bdl; ++ ++ ac_cmd_bdl = reg_read(base_phy + DDR_PHY_ACCMDBDL2); ++ ac_cmd_bdl &= (~(PHY_BDL_MASK << PHY_ACCMD_CS0_BIT)); /* clear cs0_bdl */ ++ ac_cmd_bdl &= (~(PHY_BDL_MASK << PHY_ACCMD_CS1_BIT)); /* clear cs1_bdl */ ++ ac_cmd_bdl |= (val << PHY_ACCMD_CS0_BIT); /* set cs0_bdl */ ++ ac_cmd_bdl |= (val << PHY_ACCMD_CS1_BIT); /* set cs1_bdl */ ++ reg_write(ac_cmd_bdl, base_phy + DDR_PHY_ACCMDBDL2); ++} ++ ++static int ddr_ac_ddrt_test(unsigned int mask, unsigned int base_phy) ++{ ++ unsigned int regval; ++ unsigned int times = 0; ++ ++ ddrt_reg_write(mask | DDRT_CFG_START, DDR_REG_BASE_DDRT + DDRT_OP); ++ ddrt_reg_write(0, DDR_REG_BASE_DDRT + DDRT_STATUS); ++ ++ do { ++ regval = ddrt_reg_read(DDR_REG_BASE_DDRT + DDRT_STATUS); ++ times++; ++ } while ((!(regval & DDRT_TEST_DONE_MASK)) && (times < DDRT_WAIT_TIMEOUT)); ++ ++ if (times >= DDRT_WAIT_TIMEOUT) { ++ ddr_fatal("DDRT wait timeout"); ++ ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, base_phy, -1, -1); ++ return -1; ++ } ++ ++ /* DDRT_WRITE_ONLY_MODE */ ++ if ((mask & DDRT_TEST_MODE_MASK) == DDRT_WRITE_ONLY_MODE) ++ return 0; ++ ++ /* DDRT_READ_ONLY_MODE */ ++ if (regval & DDRT_TEST_PASS_MASK) /* No error occurred, test pass. */ ++ return 0; ++ else ++ return -1; ++} ++ ++/* Check CS value */ ++static int ddr_ac_check_cs(unsigned int base_phy, unsigned int def_cs, unsigned int step) ++{ ++ ddr_ac_set_cs(base_phy, def_cs + step); ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_ac_ddrt_test(DDRT_WRITE_ONLY_MODE, base_phy); ++ ++ ddr_ac_set_cs(base_phy, def_cs); /* restore default to check */ ++ ddr_phy_cfg_update(base_phy); ++ ++ return ddr_ac_ddrt_test(DDRT_READ_ONLY_MODE, base_phy); ++} ++ ++/* Check CLK value */ ++static int ddr_ac_check_clk(const struct ddr_cfg_st *cfg, unsigned int def_clk, ++ struct ddr_delay_st *def_phase, unsigned int step) ++{ ++ int i; ++ unsigned int wdqs_phase_range, wdq_phase_range, phase_range; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ ++ /* set new value */ ++ ddr_ac_set_clk(base_phy, def_clk + step); ++ for (i = 0; i < byte_num; i++) { ++ wdqs_phase_range = PHY_WDQS_PHASE_MASK - ++ ((def_phase->phase[i] >> PHY_WDQS_PHASE_BIT) & PHY_WDQS_PHASE_MASK); ++ wdq_phase_range = PHY_WDQ_PHASE_MASK - ++ ((def_phase->bdl[i] >> PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK); ++ phase_range = (wdqs_phase_range < wdq_phase_range) ? ++ wdqs_phase_range : wdq_phase_range; ++ phase_range = (phase_range < step) ? phase_range : step; ++ ++ reg_write(def_phase->phase[i] + (phase_range << PHY_WDQS_PHASE_BIT), ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ reg_write(def_phase->bdl[i] + (phase_range << PHY_WDQ_PHASE_BIT), ++ base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_ac_ddrt_test(DDRT_WRITE_ONLY_MODE, base_phy); ++ ++ /* restore default to check */ ++ ddr_ac_set_clk(base_phy, def_clk); ++ for (i = 0; i < byte_num; i++) { ++ reg_write(def_phase->phase[i], ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ reg_write(def_phase->bdl[i], ++ base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return ddr_ac_ddrt_test(DDRT_READ_ONLY_MODE, base_phy); ++} ++ ++/* Find CS difference */ ++static int ddr_ac_find_cs(unsigned int base_phy) ++{ ++ unsigned int def_cs, step; ++ ++ def_cs = ddr_ac_get_cs(base_phy); ++ for (step = 1; step <= (PHY_BDL_MASK - def_cs); step++) { ++ if (ddr_ac_check_cs(base_phy, def_cs, step)) { ++ ddr_debug("PHY[%x] default cs[%x], find diff_cs[%x]", base_phy, def_cs, step); ++ break; ++ } ++ } ++ ++ return step; ++} ++ ++/* Find CLK difference */ ++static int ddr_ac_find_clk(const struct ddr_cfg_st *cfg) ++{ ++ int i; ++ unsigned int def_clk, step; ++ struct ddr_delay_st def_phase; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ ++ def_clk = ddr_ac_get_clk(base_phy); ++ for (i = 0; i < byte_num; i++) { ++ /* WDQS phase */ ++ def_phase.phase[i] = reg_read(base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ /* WDQ phase */ ++ def_phase.bdl[i] = reg_read(base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ++ for (step = 1; step <= (PHY_ACPHY_CLK_MAX - def_clk); step++) { ++ if (ddr_ac_check_clk(cfg, def_clk, &def_phase, step)) { ++ ddr_debug("PHY[%x] default clk[%x], find diff_clk[%x]", base_phy, def_clk, step); ++ break; ++ } ++ } ++ ++ return step; ++} ++ ++static void ddr_ac_set_phase_range(unsigned int base_phy, unsigned int def_clk, ++ unsigned int diff_clk, unsigned int phase_tmp, const struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ unsigned int clk_phase; ++ unsigned int wdqs_phase, wdq_phase; ++ unsigned int wdqs_phase_range, wdq_phase_range, phase_range; ++ ++ clk_phase = (diff_clk - phase_tmp) >> 1; ++ ++ /* set new value */ ++ ddr_ac_set_clk(base_phy, def_clk + clk_phase); ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ wdqs_phase = reg_read(base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ wdq_phase = reg_read(base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ ++ wdqs_phase_range = PHY_WDQS_PHASE_MASK - ++ ((wdqs_phase >> PHY_WDQS_PHASE_BIT) & PHY_WDQS_PHASE_MASK); ++ wdq_phase_range = PHY_WDQ_PHASE_MASK - ++ ((wdq_phase >> PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK); ++ phase_range = (wdqs_phase_range < wdq_phase_range) ? wdqs_phase_range : wdq_phase_range; ++ phase_range = (phase_range < clk_phase) ? phase_range : clk_phase; ++ reg_write(wdqs_phase + (phase_range << PHY_WDQS_PHASE_BIT), ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ reg_write(wdq_phase + (phase_range << PHY_WDQ_PHASE_BIT), ++ base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ } ++ ddr_debug("PHY[%x] def clk[%x] add phase[%x]", base_phy, def_clk, clk_phase); ++} ++ ++/* DDR AC training */ ++static int ddr_ac_training(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int diff_cs, diff_clk; ++ unsigned int cs_bdl, phase_tmp; ++ unsigned int def_clk, def_cs; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ ddr_debug("DDR AC training"); ++ def_clk = ddr_ac_get_clk(base_phy); ++ diff_cs = ddr_ac_find_cs(base_phy); /* setup time(bdl) */ ++ diff_clk = ddr_ac_find_clk(cfg); /* hold time(phase) */ ++ /* cs bdl transform to clk phase */ ++ phase_tmp = diff_cs >> DDR_BDL_PHASE_REL; ++ ++ if (diff_clk > phase_tmp) { ++ ddr_ac_set_phase_range(base_phy, def_clk, diff_clk, phase_tmp, cfg); ++ } else { ++ def_cs = ddr_ac_get_cs(base_phy); ++ cs_bdl = 0; ++ if (diff_cs > (diff_clk << DDR_BDL_PHASE_REL)) ++ cs_bdl = diff_cs - (diff_clk << DDR_BDL_PHASE_REL); ++ ++ ddr_ac_set_cs(base_phy, def_cs + cs_bdl); ++ ddr_debug("PHY[%x] def cs[%x] add bdl[%x]", base_phy, def_cs, cs_bdl); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return 0; ++} ++ ++int ddr_ac_training_func(const struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ struct tr_relate_reg relate_reg; ++ ++ /* AC training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_AC_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_AC_MASK); ++ ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ result += ddr_ac_training(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_ac_training_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR AC training"); ++ ++ return 0; ++} ++#endif /* DDR_AC_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_cmd_ctl.c b/drivers/ddr/vendor/default_v2/ddr_cmd_ctl.c +new file mode 100644 +index 0000000..573801f +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_cmd_ctl.c +@@ -0,0 +1,644 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#if PHY_DQ_BDL_LEVEL == 32 ++static void print_dataeye_win(unsigned int dq_num, unsigned int range, ++ unsigned int dqs, unsigned int dq, unsigned int win) ++{ ++ unsigned int k; ++ ++ printf("%-4u", dq_num); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) { ++ if (k >= (range >> DDR_DATAEYE_RESULT_BIT) && ++ k <= (range & DDR_DATAEYE_RESULT_MASK)) { ++ printf("%-3s", "-"); ++ } else { ++ printf("%-3s", "X"); ++ } ++ } ++ printf(" 0x%08x 0x%-4x%-4u%-4u\n", range, dqs, dq, win); ++} ++ ++static void print_dataeye_title(const char *phase) ++{ ++ unsigned int k; ++ ++ printf("%-4s", "DQ"); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) ++ printf("%-3u", k); ++ printf(" %-10s %-6s%-4s%-4s\n", "RANGE", phase, "DQ", "WIN"); ++} ++#else ++static void print_dataeye_win(unsigned int dq_num, unsigned int range, ++ unsigned int dqs, unsigned int dq, unsigned int win) ++{ ++ unsigned int k; ++ ++ printf("%-4u", dq_num); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) { ++ if (k >= (range >> DDR_DATAEYE_RESULT_BIT) && ++ k <= (range & DDR_DATAEYE_RESULT_MASK)) ++ printf("%-1s", "-"); ++ else ++ printf("%-1s", "X"); ++ } ++ printf(" 0x%08x 0x%-4x%-4u%-4u\n", range, dqs, dq, win); ++} ++ ++static void print_dataeye_title(const char *phase) ++{ ++ unsigned int k; ++ ++ printf("%-4s", "DQ"); ++ for (k = 0; k < PHY_DQ_BDL_LEVEL; k++) { ++ if (k % 4 == 0) /* Print out the CA number which is a multiple of 4 */ ++ printf("%-4u", k); ++ } ++ printf(" %-10s %-6s%-4s%-4s\n", "RANGE", phase, "DQ", "WIN"); ++} ++#endif ++ ++static struct ddr_reg_val_st training_reg_val_rank0[] = { ++ /* rank, byte, offset, value, name */ ++ {0, 0, ddr_phy_dxwdqsdly(0, 0), 0, "WDQS Byte0"}, ++ {0, 1, ddr_phy_dxwdqsdly(0, 1), 0, "WDQS Byte1"}, ++ {0, 2, ddr_phy_dxwdqsdly(0, 2), 0, "WDQS Byte2"}, ++ {0, 3, ddr_phy_dxwdqsdly(0, 3), 0, "WDQS Byte3"}, ++ {0, 0, ddr_phy_dxnwdqdly(0, 0), 0, "WDQ Phase Byte0"}, ++ {0, 1, ddr_phy_dxnwdqdly(0, 1), 0, "WDQ Phase Byte1"}, ++ {0, 2, ddr_phy_dxnwdqdly(0, 2), 0, "WDQ Phase Byte2"}, ++ {0, 3, ddr_phy_dxnwdqdly(0, 3), 0, "WDQ Phase Byte3"}, ++ {0, 0, ddr_phy_dxnwdqnbdl0(0, 0), 0, "WDQ BDL DQ0-DQ3"}, ++ {0, 0, ddr_phy_dxnwdqnbdl1(0, 0), 0, "WDQ BDL DQ4-DQ7"}, ++ {0, 1, ddr_phy_dxnwdqnbdl0(0, 1), 0, "WDQ BDL DQ8-DQ11"}, ++ {0, 1, ddr_phy_dxnwdqnbdl1(0, 1), 0, "WDQ BDL DQ12-DQ15"}, ++ {0, 2, ddr_phy_dxnwdqnbdl0(0, 2), 0, "WDQ BDL DQ16-DQ19"}, ++ {0, 2, ddr_phy_dxnwdqnbdl1(0, 2), 0, "WDQ BDL DQ20-DQ23"}, ++ {0, 3, ddr_phy_dxnwdqnbdl0(0, 3), 0, "WDQ BDL DQ24-DQ27"}, ++ {0, 3, ddr_phy_dxnwdqnbdl1(0, 3), 0, "WDQ BDL DQ28-DQ31"}, ++ {0, 0, ddr_phy_dxnwdqnbdl2(0, 0), 0, "WDM Byte0"}, ++ {0, 1, ddr_phy_dxnwdqnbdl2(0, 1), 0, "WDM Byte1"}, ++ {0, 2, ddr_phy_dxnwdqnbdl2(0, 2), 0, "WDM Byte2"}, ++ {0, 3, ddr_phy_dxnwdqnbdl2(0, 3), 0, "WDM Byte3"}, ++ {0, 0, ddr_phy_dxnoebdl(0, 0), 0, "Write DQ/DQS OE Byte0"}, ++ {0, 1, ddr_phy_dxnoebdl(0, 1), 0, "Write DQ/DQS OE Byte1"}, ++ {0, 2, ddr_phy_dxnoebdl(0, 2), 0, "Write DQ/DQS OE Byte2"}, ++ {0, 3, ddr_phy_dxnoebdl(0, 3), 0, "Write DQ/DQS OE Byte3"}, ++ {0, 0, ddr_phy_dxnrdqsdly(0), 0, "RDQS Byte0"}, ++ {0, 1, ddr_phy_dxnrdqsdly(1), 0, "RDQS Byte1"}, ++ {0, 2, ddr_phy_dxnrdqsdly(2), 0, "RDQS Byte2"}, ++ {0, 3, ddr_phy_dxnrdqsdly(3), 0, "RDQS Byte3"}, ++ {0, 0, ddr_phy_dxnrdqnbdl0(0, 0), 0, "RDQ BDL DQ0-DQ3"}, ++ {0, 0, ddr_phy_dxnrdqnbdl1(0, 0), 0, "RDQ BDL DQ4-DQ7"}, ++ {0, 1, ddr_phy_dxnrdqnbdl0(0, 1), 0, "RDQ BDL DQ8-DQ11"}, ++ {0, 1, ddr_phy_dxnrdqnbdl1(0, 1), 0, "RDQ BDL DQ12-DQ15"}, ++ {0, 2, ddr_phy_dxnrdqnbdl0(0, 2), 0, "RDQ BDL DQ16-DQ19"}, ++ {0, 2, ddr_phy_dxnrdqnbdl1(0, 2), 0, "RDQ BDL DQ20-DQ23"}, ++ {0, 3, ddr_phy_dxnrdqnbdl0(0, 3), 0, "RDQ BDL DQ24-DQ27"}, ++ {0, 3, ddr_phy_dxnrdqnbdl1(0, 3), 0, "RDQ BDL DQ28-DQ31"}, ++ {0, 0, ddr_phy_dxnrdqsgdly(0, 0), 0, "Gate Byte0"}, ++ {0, 1, ddr_phy_dxnrdqsgdly(0, 1), 0, "Gate Byte1"}, ++ {0, 2, ddr_phy_dxnrdqsgdly(0, 2), 0, "Gate Byte2"}, ++ {0, 3, ddr_phy_dxnrdqsgdly(0, 3), 0, "Gate Byte3"}, ++ {0, 0, DDR_PHY_ACCMDBDL2, 0, "CS"}, ++ {0, 0, DDR_PHY_ACPHYCTL7, 0, "CLK"}, ++ DDR_PHY_VREF_HOST_DISPLAY ++ DDR_PHY_VREF_DRAM_DISPLAY ++ DDR_DX_DPMC_DISPLAY ++ DDR_PHY_ADDRPH_DISPLAY ++ DDR_PHY_ADDRBDL_DISPLAY ++ DDR_PHY_DCC_DISPLAY ++}; ++ ++/* rank 1 */ ++static struct ddr_reg_val_st training_reg_val_rank1[] = { ++ {1, 0, ddr_phy_dxwdqsdly(1, 0), 0, "WDQS Byte0"}, ++ {1, 1, ddr_phy_dxwdqsdly(1, 1), 0, "WDQS Byte1"}, ++ {1, 2, ddr_phy_dxwdqsdly(1, 2), 0, "WDQS Byte2"}, ++ {1, 3, ddr_phy_dxwdqsdly(1, 3), 0, "WDQS Byte3"}, ++ {1, 0, ddr_phy_dxnwdqdly(1, 0), 0, "WDQ Phase Byte0"}, ++ {1, 1, ddr_phy_dxnwdqdly(1, 1), 0, "WDQ Phase Byte1"}, ++ {1, 2, ddr_phy_dxnwdqdly(1, 2), 0, "WDQ Phase Byte2"}, ++ {1, 3, ddr_phy_dxnwdqdly(1, 3), 0, "WDQ Phase Byte3"}, ++ {1, 0, ddr_phy_dxnwdqnbdl0(1, 0), 0, "WDQ BDL DQ0-DQ3"}, ++ {1, 0, ddr_phy_dxnwdqnbdl1(1, 0), 0, "WDQ BDL DQ4-DQ7"}, ++ {1, 1, ddr_phy_dxnwdqnbdl0(1, 1), 0, "WDQ BDL DQ8-DQ11"}, ++ {1, 1, ddr_phy_dxnwdqnbdl1(1, 1), 0, "WDQ BDL DQ12-DQ15"}, ++ {1, 2, ddr_phy_dxnwdqnbdl0(1, 2), 0, "WDQ BDL DQ16-DQ19"}, ++ {1, 2, ddr_phy_dxnwdqnbdl1(1, 2), 0, "WDQ BDL DQ20-DQ23"}, ++ {1, 3, ddr_phy_dxnwdqnbdl0(1, 3), 0, "WDQ BDL DQ24-DQ27"}, ++ {1, 3, ddr_phy_dxnwdqnbdl1(1, 3), 0, "WDQ BDL DQ28-DQ31"}, ++ {1, 0, ddr_phy_dxnwdqnbdl2(1, 0), 0, "WDM Byte0"}, ++ {1, 1, ddr_phy_dxnwdqnbdl2(1, 1), 0, "WDM Byte1"}, ++ {1, 2, ddr_phy_dxnwdqnbdl2(1, 2), 0, "WDM Byte2"}, ++ {1, 3, ddr_phy_dxnwdqnbdl2(1, 3), 0, "WDM Byte3"}, ++ {1, 0, ddr_phy_dxnoebdl(1, 0), 0, "Write DQ/DQS OE Byte0"}, ++ {1, 1, ddr_phy_dxnoebdl(1, 1), 0, "Write DQ/DQS OE Byte1"}, ++ {1, 2, ddr_phy_dxnoebdl(1, 2), 0, "Write DQ/DQS OE Byte2"}, ++ {1, 3, ddr_phy_dxnoebdl(1, 3), 0, "Write DQ/DQS OE Byte3"}, ++ ++ {1, 0, ddr_phy_dxnrdqsdly(0), 0, "RDQS Byte0"}, ++ {1, 1, ddr_phy_dxnrdqsdly(1), 0, "RDQS Byte1"}, ++ {1, 2, ddr_phy_dxnrdqsdly(2), 0, "RDQS Byte2"}, ++ {1, 3, ddr_phy_dxnrdqsdly(3), 0, "RDQS Byte3"}, ++ ++ {1, 0, ddr_phy_dxnrdqnbdl0(1, 0), 0, "RDQ BDL DQ0-DQ3"}, ++ {1, 0, ddr_phy_dxnrdqnbdl1(1, 0), 0, "RDQ BDL DQ4-DQ7"}, ++ {1, 1, ddr_phy_dxnrdqnbdl0(1, 1), 0, "RDQ BDL DQ8-DQ11"}, ++ {1, 1, ddr_phy_dxnrdqnbdl1(1, 1), 0, "RDQ BDL DQ12-DQ15"}, ++ {1, 2, ddr_phy_dxnrdqnbdl0(1, 2), 0, "RDQ BDL DQ16-DQ19"}, ++ {1, 2, ddr_phy_dxnrdqnbdl1(1, 2), 0, "RDQ BDL DQ20-DQ23"}, ++ {1, 3, ddr_phy_dxnrdqnbdl0(1, 3), 0, "RDQ BDL DQ24-DQ27"}, ++ {1, 3, ddr_phy_dxnrdqnbdl1(1, 3), 0, "RDQ BDL DQ28-DQ31"}, ++ {1, 0, ddr_phy_dxnrdqsgdly(1, 0), 0, "Gate Byte0"}, ++ {1, 1, ddr_phy_dxnrdqsgdly(1, 1), 0, "Gate Byte1"}, ++ {1, 2, ddr_phy_dxnrdqsgdly(1, 2), 0, "Gate Byte2"}, ++ {1, 3, ddr_phy_dxnrdqsgdly(1, 3), 0, "Gate Byte3"}, ++ {1, 0, DDR_PHY_ACCMDBDL2, 0, "CS"}, ++ {1, 0, DDR_PHY_ACPHYCTL7, 0, "CLK"}, ++ ++ DDR_PHY_VREF_HOST_DISPLAY_RANK1 ++ DDR_PHY_VREF_DRAM_DISPLAY ++ DDR_DX_DPMC_DISPLAY ++ DDR_PHY_ADDRPH_DISPLAY ++ DDR_PHY_ADDRBDL_DISPLAY ++ DDR_PHY_DCC_DISPLAY ++}; ++ ++static void ddr_cmd_result_print_write_dataeye(const struct ddr_training_data_st *ddrtr_data, ++ unsigned int win_min, unsigned int win_max, unsigned int win_sum) ++{ ++ unsigned int i, j; ++ unsigned int dq_num, dqs, dq, win; ++ ++ printf("Write window of prebit-deskew:\n"); ++ printf("--------------------------------------------------------\n"); ++ print_dataeye_title("DQPH"); ++ if (ddrtr_data->byte_num > DDR_PHY_BYTE_MAX) { ++ printf("byte num error, byte_num = %x", ddrtr_data->byte_num); ++ return; ++ } ++ for (j = 0; j < ddrtr_data->byte_num; j++) { ++ dqs = (reg_read(ddrtr_data->base_phy + ddr_phy_dxnwdqdly(ddrtr_data->rank_idx, j)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ dq_num = (j << 3) + i; /* shift left 3: 8 bit */ ++ if (dq_num >= DDR_PHY_BIT_MAX) ++ return; ++ ++ win = ddrtr_data->write.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ if (win < win_min) ++ win_min = win; ++ if (win > win_max) ++ win_max = win; ++ win_sum += win; ++ dq = ddrtr_data->write.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ print_dataeye_win(dq_num, ++ ddrtr_data->write.ddr_bit_result[dq_num], dqs, dq, win); ++ } ++ } ++ printf("--------------------------------------------------------\n"); ++ printf("Sum WIN: %u. Avg WIN: %u\n", win_sum, ++ win_sum / (ddrtr_data->byte_num * DDR_PHY_BIT_NUM)); ++ printf("Min WIN: %u. DQ Index: ", win_min); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->write.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_min) ++ printf("%u ", i); ++ } ++ printf("\nMax WIN: %u. DQ Index: ", win_max); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->write.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_max) ++ printf("%u ", i); ++ } ++ printf("\n\n"); ++} ++ ++static void ddr_cmd_result_print_read_dataeye(const struct ddr_training_data_st *ddrtr_data, ++ unsigned int win_min, unsigned int win_max, unsigned int win_sum) ++{ ++ unsigned int i, j; ++ unsigned int dq_num, dqs, dq, win; ++ ++ if (ddrtr_data->byte_num > DDR_PHY_BYTE_MAX) { ++ printf("get byte num fail, byte_num = %x", ddrtr_data->byte_num); ++ return; ++ } ++ printf("Read window of prebit-deskew:\n"); ++ printf("--------------------------------------------------------\n"); ++ print_dataeye_title("DQS"); ++ for (j = 0; j < ddrtr_data->byte_num; j++) { ++ dqs = reg_read(ddrtr_data->base_phy + ddr_phy_dxnrdqsdly(j)) & PHY_RDQS_BDL_MASK; ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ dq_num = (j << 3) + i; /* shift left 3: 8 bit */ ++ if (dq_num >= DDR_PHY_BIT_MAX) ++ return; ++ ++ win = ddrtr_data->read.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ if (win < win_min) ++ win_min = win; ++ if (win > win_max) ++ win_max = win; ++ win_sum += win; ++ dq = ddrtr_data->read.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ print_dataeye_win(dq_num, ++ ddrtr_data->read.ddr_bit_result[dq_num], dqs, dq, win); ++ } ++ } ++ printf("--------------------------------------------------------\n"); ++ printf("Sum WIN: %u. Avg WIN: %u\n", win_sum, ++ win_sum / (ddrtr_data->byte_num * DDR_PHY_BIT_NUM)); ++ printf("Min WIN: %u. DQ Index: ", win_min); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->read.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_min) ++ printf("%u ", i); ++ } ++ printf("\nMax WIN: %u. DQ Index: ", win_max); ++ for (i = 0; i < DDR_PHY_BIT_MAX; i++) { ++ win = ddrtr_data->read.ddr_bit_best[i] >> DDR_DATAEYE_RESULT_BIT; ++ if (win == win_max) ++ printf("%u ", i); ++ } ++ printf("\n\n"); ++} ++ ++static void ddr_cmd_result_print_dataeye(const struct ddr_training_data_st *ddrtr_data) ++{ ++ /* Write window of prebit-deskew */ ++ ddr_cmd_result_print_write_dataeye(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0); ++ ++ /* Read window of prebit-deskew */ ++ ddr_cmd_result_print_read_dataeye(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0); ++} ++ ++static void ddr_cmd_result_print_ca_data(const struct ddr_training_data_st *ddrtr_data, ++ unsigned int min, unsigned int max, unsigned int sum) ++{ ++ unsigned int i, j; ++ unsigned int left, right, mid, win; ++ ++ /* data */ ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ left = ddrtr_data->ca_addr[i] >> DDR_DATAEYE_RESULT_BIT; ++ right = ddrtr_data->ca_addr[i] & DDR_DATAEYE_RESULT_MASK; ++ mid = (left + right) >> 1; ++ win = right - left + 1; ++ ++ printf("%-4u", i); ++ for (j = 0; j < PHY_DQ_BDL_LEVEL; j++) { ++ if (j >= left && j <= right) ++ printf("%-1s", "-"); ++ else ++ printf("%-1s", "X"); ++ } ++ printf(" 0x%08x %-6u%-4u\n", ddrtr_data->ca_addr[i], mid, win); ++ ++ if (win < min) ++ min = win; ++ if (win > max) ++ max = win; ++ sum += win; ++ } ++ printf("--------------------------------------------------------\n"); ++ ++ printf("Sum WIN: %u. Avg WIN: %u\n", sum, sum / DDR_PHY_CA_MAX); ++ printf("Min WIN: %u. CA Index: ", min); ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ win = (ddrtr_data->ca_addr[i] & DDR_DATAEYE_RESULT_MASK) - ++ (ddrtr_data->ca_addr[i] >> DDR_DATAEYE_RESULT_BIT) + 1; ++ if (win == min) ++ printf("%u ", i); ++ } ++ printf("\nMax WIN: %u. CA Index: ", max); ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ win = (ddrtr_data->ca_addr[i] & DDR_DATAEYE_RESULT_MASK) - ++ (ddrtr_data->ca_addr[i] >> DDR_DATAEYE_RESULT_BIT) + 1; ++ if (win == max) ++ printf("%u ", i); ++ } ++} ++ ++static void ddr_cmd_result_print_ca(const struct ddr_training_data_st *ddrtr_data) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < DDR_PHY_CA_MAX; i++) { ++ if (ddrtr_data->ca_addr[i] != 0) ++ break; ++ ++ if (i == (DDR_PHY_CA_MAX - 1)) ++ return; /* no result to print */ ++ } ++ ++ printf("Command address window:\n"); ++ printf("--------------------------------------------------------\n"); ++ ++ /* title: CA 0 4...124 */ ++ printf("%-4s", "CA"); ++ for (i = 0; i < PHY_DQ_BDL_LEVEL; i++) { ++ if (i % 4 == 0) /* Print out the CA number which is a multiple of 4 */ ++ printf("%-4u", i); ++ } ++ printf(" %-10s %-6s%-4s\n", "RANGE", "BDL", "WIN"); ++ ++ /* data */ ++ ddr_cmd_result_print_ca_data(ddrtr_data, PHY_DQ_BDL_LEVEL, 0, 0); ++ printf("\n\n"); ++} ++ ++static void ddr_cmd_result_print_by_rank(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int cmd, unsigned int phy_index, unsigned int rank_index) ++{ ++ /* DDR_BYPASS_PHY0_MASK/DDR_BYPASS_PHY1_MASK/DDR_BYPASS_PHY2_MASK */ ++ unsigned int mask = 1 << phy_index; ++ const struct rank_data_st *rank_st = &ddrtr_result->phy_st[phy_index].rank_st[rank_index]; ++ ++ if (rank_st->item & mask) ++ return; ++ ++ printf("\r\n[PHY%u][RANK%u]:\r\n", phy_index, rank_index); ++ if (DDR_TRAINING_CMD_DATAEYE & cmd) ++ ddr_cmd_result_print_dataeye(&rank_st->ddrtr_data); ++ ++ if (DDR_TRAINING_CMD_LPCA & cmd) ++ ddr_cmd_result_print_ca(&rank_st->ddrtr_data); ++} ++ ++static void ddr_cmd_result_print_by_phy(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int cmd, unsigned int phy_index) ++{ ++ int i; ++ ++ if (phy_index >= DDR_PHY_NUM) { ++ printf("Array index phy_idx out of range"); ++ return; ++ } ++ if (ddrtr_result->phy_st[phy_index].rank_num > DDR_SUPPORT_RANK_MAX) { ++ printf("loop upper limit rank number out of range, rank_num = %x", ++ ddrtr_result->phy_st[phy_index].rank_num); ++ return; ++ } ++ ++ for (i = 0; i < ddrtr_result->phy_st[phy_index].rank_num; i++) ++ ddr_cmd_result_print_by_rank(ddrtr_result, cmd, phy_index, i); ++} ++ ++void ddr_cmd_result_display(const struct ddr_training_result_st *ddrtr_result, unsigned int cmd) ++{ ++ int i; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ if (ddrtr_result->phy_num > DDR_PHY_NUM) ++ return; ++ ++ for (i = 0; i < ddrtr_result->phy_num; i++) ++ ddr_cmd_result_print_by_phy(ddrtr_result, cmd, i); ++} ++ ++static void ddr_reg_result_display_by_rank(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int phy_index, unsigned int rank_index) ++{ ++ int i; ++ int num; ++ unsigned int base_phy; ++ unsigned int byte_num; ++ unsigned int rank_num; ++ struct ddr_reg_val_st *ddr_reg = NULL; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ ++ if (rank_index >= DDR_SUPPORT_RANK_MAX) { ++ printf("rank index error, rank_index = %x", rank_index); ++ return; ++ } ++ base_phy = ddrtr_result->phy_st[phy_index].rank_st[rank_index].ddrtr_data.base_phy; ++ byte_num = ddrtr_result->phy_st[phy_index].rank_st[rank_index].ddrtr_data.byte_num; ++ rank_num = ddrtr_result->phy_st[phy_index].rank_num; ++ if (rank_index == 0) { ++ num = sizeof(training_reg_val_rank0) / sizeof(struct ddr_reg_val_st); ++ ddr_reg = &training_reg_val_rank0[0]; ++ } else { ++ num = sizeof(training_reg_val_rank1) / sizeof(struct ddr_reg_val_st); ++ ddr_reg = &training_reg_val_rank1[0]; ++ } ++ ++ printf("\r\n[PHY%u][RANK%u]:\r\n", phy_index, rank_index); ++ for (i = 0; i < num; i++) { ++ if (i != 0) ++ ddr_reg++; ++ if (ddr_reg->offset == 0) ++ continue; ++ if (ddr_reg->byte_index >= byte_num) ++ continue; ++ if (ddr_reg->rank_index >= rank_num) ++ continue; ++ ddr_reg->val = reg_read(base_phy + ddr_reg->offset); ++ ++ printf("[0x%08x = 0x%08x] %-32s", base_phy + ddr_reg->offset, ++ ddr_reg->val, ddr_reg->name); ++ ++ if ((i + 1) % 2 == 0) /* 2: Print information for 2 registers per line */ ++ printf("\r\n"); ++ } ++} ++ ++void ddr_reg_result_display_by_phy(const struct ddr_training_result_st *ddrtr_result, ++ unsigned int phy_index) ++{ ++ unsigned int i; ++ unsigned int mask = 1 << phy_index; /* DDR_BYPASS_PHY0_MASK or DDR_BYPASS_PHY1_MASK */ ++ unsigned int item; ++ unsigned int enable = 0; ++ unsigned int rank_num; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ ++ rank_num = ddrtr_result->phy_st[phy_index].rank_num; ++ if (rank_num > DDR_SUPPORT_RANK_MAX) { ++ printf("loop upper limit rank number out of range, rank_num = %x", rank_num); ++ return; ++ } ++ /* check rank0 and rank1 training item */ ++ for (i = 0; i < rank_num; i++) { ++ item = ddrtr_result->phy_st[phy_index].rank_st[i].item; ++ if (!(item & mask)) ++ enable = 1; ++ } ++ ++ if (!enable) ++ return; ++ ++ for (i = 0; i < rank_num; i++) { ++ ddr_phy_switch_rank((unsigned int)ddrtr_result->phy_st[phy_index].rank_st[i].ddrtr_data.base_phy, i); ++ ddr_reg_result_display_by_rank(ddrtr_result, phy_index, i); ++ ddr_phy_switch_rank((unsigned int)ddrtr_result->phy_st[phy_index].rank_st[i].ddrtr_data.base_phy, 0); ++ } ++} ++ ++/* Display DDR training register */ ++void ddr_reg_result_display(const struct ddr_training_result_st *ddrtr_result) ++{ ++ int i; ++ ++ if (ddrtr_result == NULL) { ++ printf("Pointer parameter ddrtr_result is NULL!"); ++ return; ++ } ++ if (ddrtr_result->phy_num > DDR_PHY_NUM) { ++ printf("loop upper limit phy number out of range, phy_num = %x", ++ ddrtr_result->phy_num); ++ return; ++ } ++ for (i = 0; i < ddrtr_result->phy_num; i++) ++ ddr_reg_result_display_by_phy(ddrtr_result, i); ++ ++ printf("\r\n"); ++} ++ ++#ifndef CONFIG_MINI_BOOT ++static int ddr_cmd_is_disable(void) ++{ ++ unsigned int cfg; ++ unsigned int mask; ++ unsigned int i; ++ int disable = 1; ++ ++ cfg = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ for (i = 0; i < DDR_PHY_NUM; i++) { ++ mask = 0x1 << i; ++ if (!(cfg & mask)) ++ disable = 0; ++ } ++ ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ cfg = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC); ++ for (i = 0; i < DDR_PHY_NUM; i++) { ++ mask = 0x1 << i; ++ if (!(cfg & mask)) ++ disable = 0; ++ } ++#endif ++ ++ return disable; ++} ++ ++/* Get DDR training command function entry address */ ++void *ddr_cmd_get_entry(void) ++{ ++ char *src_ptr = 0; ++ char *dst_ptr; ++ unsigned int length; ++ ++ src_ptr = g_ddr_training_cmd_start; ++ dst_ptr = (char *)(DDR_TRAINING_RUN_STACK); ++ length = (uintptr_t)g_ddr_training_cmd_end - (uintptr_t)src_ptr; ++ ++ if ((src_ptr == NULL) || !length) { ++ printf("DDR training is unsupport.\n"); ++ return 0; ++ } ++ ++ printf("DDR training cmd entry[0x%08X] size[%u]byte cfg[0x%08X = 0x%08X]", ++ DDR_TRAINING_RUN_STACK, length, (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG)); ++ ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ printf("[0x%08X = 0x%08X]", (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC), ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC)); ++#endif ++ printf("\n"); ++ ++ if (ddr_cmd_is_disable() != 0) { ++ printf("Please config DDR training item. Bypass bit:\n" ++ "[0]PHY0 : 0x1\n" ++ "[1]PHY1 : 0x2\n" ++ "[2]PHY2 : 0x4\n" ++ "[4]Write Leveling : 0x10\n" ++ "[8]Gate : 0x100\n" ++ "[16]Dataeye : 0x10000\n" ++ "[18]Pcode : 0x40000\n" ++ "[20]HW : 0x100000\n" ++ "[21]MPR : 0x200000\n" ++ "[22]AC : 0x400000\n" ++ "[23]LPCA : 0x800000\n" ++ "[24]Host Vref : 0x1000000\n" ++ "[25]Dram Vref : 0x2000000\n" ++ "[26]DPMC : 0x4000000\n" ++ "[27]DCC : 0x8000000\n" ++ "[28]Dataeye Adjust : 0x10000000\n" ++ "[29]WL Write Adjust: 0x20000000\n" ++ "[30]HW Read Adjust : 0x40000000\n"); ++ return 0; ++ } ++ ++ ddr_cmd_prepare_copy(); ++ memcpy(dst_ptr, src_ptr, length); ++ return (void *)dst_ptr; ++} ++ ++/* Copy training codes from DDR to SRAM and do ddr training */ ++struct ddr_training_result_st *ddr_cmd_training_if(const struct ddr_cmd_st *cmd_st) ++{ ++ ddr_cmd_entry_func entry; ++ struct ddr_training_result_st *result_st = NULL; ++ ++ entry = (ddr_cmd_entry_func)ddr_cmd_get_entry(); ++ if (entry == NULL) ++ return 0; ++ ++#ifdef CONFIG_ARM64 ++ asm("isb"); ++ asm("dsb sy"); ++#else ++ asm("mcr p15, 0, r0, c7, c5, 0"); /* instruction cache invalidate all to PoU */ ++ asm("mcr p15, 0, r0, c7, c10, 4"); /* data synchronization barrier operation */ ++#endif ++ ++ /* save site before execute cmd */ ++ ddr_cmd_site_save(); ++ ++ /* entry equal ddr_training_cmd_entry() */ ++ result_st = entry(cmd_st); ++ ++ /* restore site before execute cmd */ ++ ddr_cmd_site_restore(); ++ ++ if (result_st == NULL) { ++ printf("DDR training fail\n"); ++ return 0; ++ } ++ ++ return result_st; ++} ++#endif +diff --git a/drivers/ddr/vendor/default_v2/ddr_cmd_loc.S b/drivers/ddr/vendor/default_v2/ddr_cmd_loc.S +new file mode 100644 +index 0000000..b908da1 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_cmd_loc.S +@@ -0,0 +1,9 @@ ++/* DDR Training command codes location. Copy from DDR to SRAM to train DDR. */ ++ ++.section .image,"a" ++ ++.globl g_ddr_training_cmd_start ++g_ddr_training_cmd_start: ++.incbin "drivers/ddr/vendor/default_v2/cmd_bin/ddr_cmd.bin" ++.globl g_ddr_training_cmd_end ++g_ddr_training_cmd_end: +diff --git a/drivers/ddr/vendor/default_v2/ddr_dcc_training.c b/drivers/ddr/vendor/default_v2/ddr_dcc_training.c +new file mode 100644 +index 0000000..2edc605 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_dcc_training.c +@@ -0,0 +1,533 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++/* s40/t28/t16 not support dcc training */ ++#define __DCC_TRAINING__ ++#ifdef DDR_DCC_TRAINING_CONFIG ++/* Save two rank RDET result */ ++static void ddr_save_two_rank_bdl(const struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data) ++{ ++ unsigned int byte_idx; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank_idx = cfg->rank_idx; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ for (byte_idx = 0; byte_idx < byte_num; byte_idx++) { ++ dcc_data->rank[rank_idx].dq03[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank_idx, byte_idx)); ++ dcc_data->rank[rank_idx].dq47[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank_idx, byte_idx)); ++ dcc_data->rank[rank_idx].rdm[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqnbdl2(rank_idx, byte_idx)); ++ dcc_data->rank[rank_idx].rdqs[byte_idx] = reg_read(base_phy + ddr_phy_dxnrdqsdly(byte_idx)); ++ ++ ddr_debug("rank[%x] dq03[%x] dq47[%x] rdm[%x] rdqs[%x]", rank_idx, ++ dcc_data->rank[rank_idx].dq03[byte_idx], dcc_data->rank[rank_idx].dq47[byte_idx], ++ dcc_data->rank[rank_idx].rdm[byte_idx], dcc_data->rank[rank_idx].rdqs[byte_idx]); ++ } ++} ++ ++/* Restore two rank RDET result */ ++static void ddr_restore_two_rank_bdl(const struct ddr_cfg_st *cfg, const struct dcc_data_st *dcc_data) ++{ ++ unsigned int byte_idx; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank_idx = cfg->rank_idx; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ for (byte_idx = 0; byte_idx < byte_num; byte_idx++) { ++ reg_write(dcc_data->rank[rank_idx].dq03[byte_idx], base_phy + ddr_phy_dxnrdqnbdl0(rank_idx, byte_idx)); ++ reg_write(dcc_data->rank[rank_idx].dq47[byte_idx], base_phy + ddr_phy_dxnrdqnbdl1(rank_idx, byte_idx)); ++ reg_write(dcc_data->rank[rank_idx].rdm[byte_idx], base_phy + ddr_phy_dxnrdqnbdl2(rank_idx, byte_idx)); ++ reg_write(dcc_data->rank[rank_idx].rdqs[byte_idx], base_phy + ddr_phy_dxnrdqsdly(byte_idx)); ++ } ++} ++ ++/* DCC RDET training */ ++static int ddr_dcc_dataeye_read(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ ++ /* 0:PHY_TRAINCTRL0_DTR_RANK0, 1:PHY_TRAINCTRL0_DTR_RANK1 */ ++ ddr_phy_switch_rank(cfg->cur_phy, cfg->rank_idx); ++ result = ddr_hw_training_process(cfg, PHY_PHYINITCTRL_RDET_EN); ++ reg_write(PHY_PHYINITCTRL_RDET_EN, cfg->cur_phy + DDR_PHY_PHYINITSTATUS); ++ ++ return result; ++} ++ ++/* Duty direction control */ ++static unsigned int ddr_dcc_ck_ctl(const struct ddr_cfg_st *cfg, unsigned int ioctl21_def, ++ unsigned int ctl_index) ++{ ++ unsigned int ioctl21; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ ioctl21 = (ioctl21_def & (~(1 << PHY_ACIOCTL21_CTL0_BIT)) & (~(1 << PHY_ACIOCTL21_CTL1_BIT))) | ++ (ctl_index << PHY_ACIOCTL21_CTL0_BIT) | (ctl_index << PHY_ACIOCTL21_CTL1_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } else { ++ ioctl21 = (ioctl21_def & (~(1 << PHY_ACIOCTL21_CTL0_BIT))) | ++ (ctl_index << PHY_ACIOCTL21_CTL0_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } ++ return ioctl21; ++} ++ ++/* Duty Correction. */ ++static unsigned int ddr_dcc_correct_duty(const struct ddr_cfg_st *cfg, unsigned int cur_duty, ++ unsigned int duty_def) ++{ ++ unsigned int ioctl21; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ /* Correct CK0 & CK1 duty */ ++ ioctl21 = (duty_def & (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK0_BIT)) & ++ (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK1_BIT))) | ++ (cur_duty << PHY_ACIOCTL21_CK0_BIT) | ++ (cur_duty << PHY_ACIOCTL21_CK1_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } else { ++ /* Correct CK0 duty */ ++ ioctl21 = (duty_def & (~(PHY_ACIOCTL21_MASK << PHY_ACIOCTL21_CK0_BIT))) | ++ (cur_duty << PHY_ACIOCTL21_CK0_BIT); ++ reg_write(ioctl21, cfg->cur_phy + DDR_PHY_ACIOCTL21); ++ } ++ ++ return ioctl21; ++} ++ ++/* Duty Correction Control get win data */ ++static unsigned int ddr_dcc_get_win(const struct dcc_data_st *dcc_data, int ck_index, int val_index) ++{ ++ unsigned int win; ++ unsigned int rdqsbdl_right; ++ unsigned int rdqsbdl_left; ++ ++ rdqsbdl_right = (dcc_data->ck[ck_index].val[val_index] >> PHY_DXNRDBOUND_RIGHT_BIT) & ++ PHY_DXNRDBOUND_MASK; ++ rdqsbdl_left = (dcc_data->ck[ck_index].val[val_index] >> PHY_DXNRDBOUND_LEFT_BIT) & ++ PHY_DXNRDBOUND_MASK; ++ win = rdqsbdl_right - rdqsbdl_left; ++ ++ return win; ++} ++ ++/* Duty Correction Control get the min win of two byte */ ++static unsigned int ddr_dcc_get_min_win(const struct dcc_data_st *dcc_data, int ck_index) ++{ ++ int i; ++ unsigned int win_min; ++ unsigned int cur_win; ++ ++ win_min = ddr_dcc_get_win(dcc_data, ck_index, 0); ++ for (i = 0; i < DDR_CK_RESULT_MAX; i++) { ++ cur_win = ddr_dcc_get_win(dcc_data, ck_index, i); ++ ddr_debug("CK win[%x] = [%x]", i, cur_win); ++ if (cur_win < win_min) ++ win_min = cur_win; ++ } ++ ++ return win_min; ++} ++ ++/* Duty Correction Control get ck0 min win */ ++static unsigned int ddr_dcc_get_ck0_win(const struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data, ++ unsigned int ck0_win_min) ++{ ++ unsigned int byte_index; ++ unsigned int ck0_win; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return 0; ++ } ++ if ((byte_num / 2) > (DDR_PHY_BYTE_MAX / 2)) { /* byte_num/2:ck0 value include byte0 and byte1 */ ++ ddr_error("loop upper limit out of range"); ++ return 0; ++ } ++ for (byte_index = 0; byte_index < (byte_num / 2); byte_index++) /* byte_num/2:ck0 value include byte0 and byte1 */ ++ dcc_data->ck[0].val[byte_index] = reg_read(cfg->cur_phy + ddr_phy_dxnrdbound(byte_index)); ++ ++ ck0_win = ddr_dcc_get_min_win(dcc_data, 0); ++ if (ck0_win < ck0_win_min) ++ ck0_win_min = ck0_win; ++ ++ return ck0_win_min; ++} ++/* Duty Correction Control get ck1 min win. */ ++static unsigned int ddr_dcc_get_ck1_win(const struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data, ++ unsigned int ck1_win_min) ++{ ++ unsigned int byte_index; ++ unsigned int ck1_win; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ for (byte_index = 2; byte_index < byte_num; byte_index++) /* ck1 value include byte2 and byte3 */ ++ dcc_data->ck[1].val[byte_index - 2] = /* store value from byte_idex-2 */ ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdbound(byte_index)); /* one ck include two value */ ++ ++ ck1_win = ddr_dcc_get_min_win(dcc_data, 1); ++ if (ck1_win < ck1_win_min) ++ ck1_win_min = ck1_win; ++ ++ return ck1_win_min; ++} ++ ++/* dcc training data init */ ++static void dcc_data_init(struct dcc_data_st *dcc_data) ++{ ++ dcc_data->ck[0].win_min_ctl = 0xffffffff; ++ dcc_data->ck[0].win_max_ctl = 0x0; ++ dcc_data->ck[1].win_min_ctl = 0xffffffff; ++ dcc_data->ck[1].win_max_ctl = 0x0; ++ dcc_data->ck[0].idx_duty = 0; ++ dcc_data->ck[0].idx_duty_ctl = 0; ++ dcc_data->ck[0].idx_ctl = 0; ++ dcc_data->ck[1].idx_duty = 0; ++ dcc_data->ck[1].idx_duty_ctl = 0; ++ dcc_data->ck[1].idx_ctl = 0; ++ dcc_data->ck[0].bypass_ck_bit = PHY_BYPASS_CK0_BIT; ++ dcc_data->ck[0].acioctl21_ctl_bit = PHY_ACIOCTL21_CTL0_BIT; ++ dcc_data->ck[0].acioctl21_ck_bit = PHY_ACIOCTL21_CK0_BIT; ++ dcc_data->ck[1].bypass_ck_bit = PHY_BYPASS_CK1_BIT; ++ dcc_data->ck[1].acioctl21_ctl_bit = PHY_ACIOCTL21_CTL1_BIT; ++ dcc_data->ck[1].acioctl21_ck_bit = PHY_ACIOCTL21_CK1_BIT; ++} ++ ++/* dcc training get window by rank */ ++static int ddr_dcc_get_win_by_rank(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data) ++{ ++ unsigned int i; ++ unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ for (i = 0; i < rank_num; i++) { ++ ddr_debug("cur_rank = [%x]", i); ++ cfg->rank_idx = i; ++ ++ if (ddr_dcc_dataeye_read(cfg)) { ++ dcc_data->ck[0].win = 0x0; ++ dcc_data->ck[1].win = 0x0; ++ } else { ++ /* Get win */ ++ dcc_data->ck[0].win = ddr_dcc_get_ck0_win(cfg, dcc_data, dcc_data->ck[0].win); ++ ddr_debug("ck0 win = [%x]", dcc_data->ck[0].win); ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ dcc_data->ck[1].win = ddr_dcc_get_ck1_win(cfg, dcc_data, dcc_data->ck[1].win); ++ ddr_debug("ck1 win = [%x]", dcc_data->ck[1].win); ++ } ++ } ++ /* Restore two rank bdl */ ++ ddr_restore_two_rank_bdl(cfg, dcc_data); ++ } ++ ++ return 0; ++} ++ ++/* ddr dcc training compare result */ ++static void ddr_dcc_compare_result(struct dcc_data_st *dcc_data, int ck_num, ++ unsigned int base_phy, unsigned int gated_bypass_def, unsigned int ioctl21_def) ++{ ++ int ck_idx; ++ ++ if (ck_num > DDR_CK_MAX_NUM) { ++ ddr_error("ck number out of range"); ++ return; ++ } ++ for (ck_idx = 0; ck_idx < ck_num; ck_idx++) { ++ /* Config ck0 duty */ ++ if (dcc_data->ck[ck_idx].win_max_ctl - dcc_data->ck[ck_idx].win_min_ctl <= DDR_DCC_CTL_WIN_DIFF) { ++ dcc_data->ck[ck_idx].def_bp = (gated_bypass_def >> dcc_data->ck[ck_idx].bypass_ck_bit) & 0x1; ++ dcc_data->ck[ck_idx].def_ctl = (ioctl21_def >> dcc_data->ck[ck_idx].acioctl21_ctl_bit) & 0x1; ++ dcc_data->ck[ck_idx].def_duty = (ioctl21_def >> dcc_data->ck[ck_idx].acioctl21_ck_bit) & PHY_ACIOCTL21_MASK; ++ ++ gated_bypass_def = (gated_bypass_def & (~(1 << dcc_data->ck[ck_idx].bypass_ck_bit))) | ++ (dcc_data->ck[ck_idx].def_bp << dcc_data->ck[ck_idx].bypass_ck_bit); ++ reg_write(gated_bypass_def, base_phy + DDR_PHY_AC_GATED_BYPASS); ++ ++ ioctl21_def = (ioctl21_def & (~(1 << dcc_data->ck[ck_idx].acioctl21_ctl_bit)) & ++ (~(PHY_ACIOCTL21_MASK << dcc_data->ck[ck_idx].acioctl21_ck_bit))) | ++ (dcc_data->ck[ck_idx].def_ctl << dcc_data->ck[ck_idx].acioctl21_ctl_bit) | ++ (dcc_data->ck[ck_idx].def_duty << dcc_data->ck[ck_idx].acioctl21_ck_bit); ++ reg_write(ioctl21_def, base_phy + DDR_PHY_ACIOCTL21); ++ ++ ddr_debug("ck[%x] Final AC_GATED_BYPASS[%x]", ck_idx, gated_bypass_def); ++ ddr_debug("ck[%x] Final ACIOCTL21[%x]", ck_idx, ioctl21_def); ++ } else { ++ ioctl21_def = (ioctl21_def & (~(1 << dcc_data->ck[ck_idx].acioctl21_ctl_bit)) & ++ (~(PHY_ACIOCTL21_MASK << dcc_data->ck[ck_idx].acioctl21_ck_bit))) | ++ (dcc_data->ck[ck_idx].idx_ctl << dcc_data->ck[ck_idx].acioctl21_ctl_bit) | ++ (dcc_data->ck[ck_idx].idx_duty_ctl << dcc_data->ck[ck_idx].acioctl21_ck_bit); ++ reg_write(ioctl21_def, base_phy + DDR_PHY_ACIOCTL21); ++ ++ ddr_debug("ck[%x] Final ACIOCTL21[%x]", ck_idx, ioctl21_def); ++ } ++ } ++} ++ ++static void ddr_dcc_get_duty(struct dcc_data_st *dcc_data, int ck_num, unsigned int cur_duty) ++{ ++ int ck_idx; ++ ++ if (ck_num > DDR_CK_MAX_NUM) { ++ ddr_error("ck number out of range"); ++ return; ++ } ++ for (ck_idx = 0; ck_idx < ck_num; ck_idx++) { ++ if (dcc_data->ck[ck_idx].win < dcc_data->ck[ck_idx].win_min_duty) ++ dcc_data->ck[ck_idx].win_min_duty = dcc_data->ck[ck_idx].win; ++ ++ if (dcc_data->ck[ck_idx].win > dcc_data->ck[ck_idx].win_max_duty) { ++ dcc_data->ck[ck_idx].win_max_duty = dcc_data->ck[ck_idx].win; ++ dcc_data->ck[ck_idx].idx_duty = cur_duty; ++ } ++ ddr_debug("ck[%x] duty_win_min[%x] duty_win_max[%x] duty_index[%x]", ck_idx, ++ dcc_data->ck[ck_idx].win_min_duty, ++ dcc_data->ck[ck_idx].win_max_duty, ++ dcc_data->ck[ck_idx].idx_duty); ++ } ++} ++ ++/* Get ck0/ck1 duty_win_min/duty_win_max/duty_index */ ++static void ddr_dcc_get_ctrl(struct dcc_data_st *dcc_data, int ck_num, unsigned int cur_ctl) ++{ ++ int ck_idx; ++ ++ if (ck_num > DDR_CK_MAX_NUM) { ++ ddr_error("ck number out of range"); ++ return; ++ } ++ for (ck_idx = 0; ck_idx < ck_num; ck_idx++) { ++ if (dcc_data->ck[ck_idx].win_min_duty < dcc_data->ck[ck_idx].win_min_ctl) ++ dcc_data->ck[ck_idx].win_min_ctl = dcc_data->ck[ck_idx].win_min_duty; ++ ++ if (dcc_data->ck[ck_idx].win_max_duty > dcc_data->ck[ck_idx].win_max_ctl) { ++ dcc_data->ck[ck_idx].win_max_ctl = dcc_data->ck[ck_idx].win_max_duty; ++ dcc_data->ck[ck_idx].idx_duty_ctl = dcc_data->ck[ck_idx].idx_duty; ++ dcc_data->ck[ck_idx].idx_ctl = cur_ctl; ++ } ++ ddr_debug("ck[%x] win_min_ctl[%x] win_max_ctl[%x] ctl_index0[%x] duty_ctl_idx0[%x]", ck_idx, ++ dcc_data->ck[ck_idx].win_min_ctl, ++ dcc_data->ck[ck_idx].win_max_ctl, ++ dcc_data->ck[ck_idx].idx_ctl, ++ dcc_data->ck[ck_idx].idx_duty_ctl); ++ } ++} ++ ++static int ddr_dcc_process(struct ddr_cfg_st *cfg, struct dcc_data_st *dcc_data, ++ int ck_num, unsigned int ioctl21_def) ++{ ++ unsigned int cur_ctl; ++ unsigned int cur_duty; ++ int result; ++ ++ for (cur_ctl = 0; cur_ctl < DDR_DUTY_CTL_NUM; cur_ctl++) { ++ dcc_data->ck[0].win_min_duty = 0xffffffff; ++ dcc_data->ck[0].win_max_duty = 0x0; ++ if (ck_num > 1) { ++ dcc_data->ck[1].win_min_duty = 0xffffffff; ++ dcc_data->ck[1].win_max_duty = 0x0; ++ } ++ ddr_debug("cur_ctl = [%x]", cur_ctl); ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ /* Correct CK duty dirrection control */ ++ dcc_data->ioctl21_tmp = ddr_dcc_ck_ctl(cfg, ioctl21_def, cur_ctl); ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ for (cur_duty = 0; cur_duty < DDR_DUTY_NUM; cur_duty += PHY_AC_IOCTL21_STEP) { ++ dcc_data->ck[0].win = 0xffffffff; ++ if (ck_num > 1) ++ dcc_data->ck[1].win = 0xffffffff; ++ ++ ddr_debug("cur_duty = [%x]", cur_duty); ++ /* Correct ck0 and ck1 duty */ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ dcc_data->ioctl21_tmp = ddr_dcc_correct_duty(cfg, cur_duty, dcc_data->ioctl21_tmp); ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ ddr_debug("Cur ACIOCTL21[%x]", dcc_data->ioctl21_tmp); ++ result = ddr_dcc_get_win_by_rank(cfg, dcc_data); ++ ddr_dcc_get_duty(dcc_data, ck_num, cur_duty); ++ } ++ ++ ddr_dcc_get_ctrl(dcc_data, ck_num, cur_ctl); ++ } ++ ++ return result; ++} ++ ++static int ddr_dcc_get_best_duty(struct ddr_cfg_st *cfg, ++ struct dmc_cfg_sref_st *cfg_sref, struct dcc_data_st *dcc_data) ++{ ++ int ck_num; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int ioctl21_def; ++ unsigned int gated_bypass_def, gated_bypass_temp; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ck_num = DDR_CK_NUM_LPDDR4; /* lpddr4: 2 ck */ ++ else ++ ck_num = DDR_CK_NUM_NONLPDDR4; /* other: 1 ck */ ++ ++ dcc_data_init(dcc_data); ++ ++ /* Save ck duty default config. Read two times to get the right static register value. */ ++ gated_bypass_def = reg_read(base_phy + DDR_PHY_AC_GATED_BYPASS); ++ gated_bypass_def = reg_read(base_phy + DDR_PHY_AC_GATED_BYPASS); ++ ioctl21_def = reg_read(base_phy + DDR_PHY_ACIOCTL21); ++ ioctl21_def = reg_read(base_phy + DDR_PHY_ACIOCTL21); ++ ++ ddr_debug("gated_bypass_def[%x] ioctl21_def[%x]", gated_bypass_def, ioctl21_def); ++ ++ /* DCC training exit self-refresa enter powerdown. */ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); ++ ++ /* DDR dcc training enter auto self-refresh. */ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ /* Enable ck0 & ck1 duty. */ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ gated_bypass_temp = gated_bypass_def | PHY_CK1_IOCTL_DUTY_EN | PHY_CK_IOCTL_DUTY_EN; ++ reg_write(gated_bypass_temp, base_phy + DDR_PHY_AC_GATED_BYPASS); ++ } else { ++ gated_bypass_temp = gated_bypass_def | PHY_CK_IOCTL_DUTY_EN; ++ reg_write(gated_bypass_temp, base_phy + DDR_PHY_AC_GATED_BYPASS); ++ } ++ ddr_debug("Cur GATED_BYPASS[%x]", gated_bypass_temp); ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ if (ddr_dcc_process(cfg, dcc_data, ck_num, ioctl21_def)) ++ return -1; ++ ++ /* Config ck duty */ ++ /* DCC training exit self-refresa enter powerdown. */ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); ++ ++ /* DDR dcc training enter auto self-refresh. */ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ ++ /* DDR dcc training compare result. */ ++ ddr_dcc_compare_result(dcc_data, ck_num, base_phy, gated_bypass_def, ioctl21_def); ++ ++ /* DDR dcc training exit auto self-refresh. */ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ ++ return 0; ++} ++ ++static int ddr_dcc_training(struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ int result = 0; ++ unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ struct dmc_cfg_sref_st cfg_sref; ++ struct ddr_timing_st timing_st; ++ struct dcc_data_st dcc_st; ++ struct dcc_data_st *dcc_data = &dcc_st; ++ ++ memset(&cfg_sref, 0, sizeof(struct dmc_cfg_sref_st)); ++ memset(&timing_st, 0, sizeof(struct ddr_timing_st)); ++ memset(dcc_data, 0, sizeof(struct dcc_data_st)); ++ ++ ddr_debug("dram_type[%x]", cfg->phy[cfg->phy_idx].dram_type); ++ ddr_debug("rank num[%x]", rank_num); ++ ++ /* Save two rank DERT default result: rdq/rdqs/rdm/ bdl */ ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ ddr_save_two_rank_bdl(cfg, dcc_data); ++ } ++ ++ /* Disable auto refresh */ ++ ddr_training_save_timing(cfg, &timing_st); ++ ++ /* Duty Correction Control training. */ ++ result += ddr_dcc_get_best_duty(cfg, &cfg_sref, dcc_data); ++ ++ /* Do DERT training again */ ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ dcc_data->item[i] = cfg->phy[cfg->phy_idx].rank[i].item_hw; ++ cfg->phy[cfg->phy_idx].rank[i].item_hw = PHY_PHYINITCTRL_HVREFT_EN; ++ ddr_debug("item_hw[%x]=[%x]", i, cfg->phy[cfg->phy_idx].rank[i].item_hw); ++ } ++ result += ddr_hw_training_by_phy(cfg); ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ cfg->phy[cfg->phy_idx].rank[i].item_hw = dcc_data->item[i]; ++ } ++ ++ /* Enable auto refresh */ ++ ddr_training_restore_timing(cfg, &timing_st); ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ /* DCC restore DMC_CFG_SREF config. */ ++ ddr_sref_cfg_restore(cfg, &cfg_sref); ++ ++ return result; ++} ++ ++int ddr_dcc_training_func(struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ int result = 0; ++ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL"); ++ return -1; ++ } ++ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ cfg->cur_item = cfg->phy[i].rank[0].item; ++ ++ if (ddr_training_check_bypass(cfg, 1 << (cfg->phy_idx)) != DDR_FALSE) ++ continue; ++ /* dpmc training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DCC_MASK) == DDR_FALSE) ++ result += ddr_dcc_training(cfg); ++ } ++ return result; ++} ++ ++#else ++int ddr_dcc_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DCC training"); ++ return 0; ++} ++#endif /* DDR_DCC_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_ddrc_v520_s14.h b/drivers/ddr/vendor/default_v2/ddr_ddrc_v520_s14.h +new file mode 100644 +index 0000000..750f02d +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_ddrc_v520_s14.h +@@ -0,0 +1,244 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRC_V520_H ++#define DDR_DDRC_V520_H ++ ++/******** DMC **************************/ ++/* base address: DDR_REG_BASE_DMC0 DDR_REG_BASE_DMC1 */ ++/* register offset address */ ++#define DDR_DMC_CTRL_SREF 0x0 /* DDRC self-refresh control. */ ++#define DDR_DMC_CFG_SREF 0x20 /* DDRC self-refresh config. */ ++#define DDR_DMC_CFG_PD 0x28 /* PowerDown */ ++#define DDR_DMC_CFG_DDRMODE 0x50 ++#define DDR_DMC_CFG_SCRAMB 0x58 /* DDR scramb config */ ++#define ddr_dmc_cfg_rnkvol(n) (0x60 + ((n) << 2)) ++#define DDR_DMC_CFG_EMRS01 0xf0 ++#define DDR_DMC_TIMING2 0x108 ++#define DDR_DMC_SFCREQ 0xc ++#define DDR_DMC_SFCCMD 0x210 ++#define DDR_DMC_SFCADDR 0x214 /* read col and row */ ++#define DDR_DMC_SFCBANK 0x218 ++#define DDR_DMC_CURR_FUNC 0x294 ++#ifndef DDR_DMC_SFC_RDATA0 ++#define DDR_DMC_SFC_RDATA0 0x4A8 /* SFC read data[31:0] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA1 ++#define DDR_DMC_SFC_RDATA1 0x4AC /* SFC read data[63:32] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA2 ++#define DDR_DMC_SFC_RDATA2 0x4B0 /* SFC read data[95:64] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA3 ++#define DDR_DMC_SFC_RDATA3 0x4B4 /* SFC read data[127:96] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA4 ++#define DDR_DMC_SFC_RDATA4 0x4B8 /* SFC read data[159:128] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA5 ++#define DDR_DMC_SFC_RDATA5 0x4BC /* SFC read data[191:160] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA6 ++#define DDR_DMC_SFC_RDATA6 0x4C0 /* SFC read data[223:192] */ ++#endif ++#ifndef DDR_DMC_SFC_RDATA7 ++#define DDR_DMC_SFC_RDATA7 0x4C4 /* SFC read data[255:224] */ ++#endif ++ ++/* register mask */ ++#define DMC_CMD_MRS_MASK 0xffff ++/* storing data bus width. [00]8bit, [01]16bit, [10]32bit, [11]64bit */ ++#define DMC_MEM_WIDTH_MASK 0x3 ++#define DMC_MRS_MASK 0xffff /* [15:0] Mode Register mask */ ++#define DMC_MR0_BL_MASK 0x3 ++#define DMC_CFG_DRAM_TYPE_MASK 0xf /* [3:0]101:DDR2, 110:DDR3, 111:DDR4 */ ++#define DMC_CFG_MEM_BG_MASK 0x3 /* [11:10]0:1, 1:2, 2:4 Bank Group */ ++#define DMC_CURR_FUNC_IN_SREF_MASK 0x1 ++#define DMC_RNKVOL_MEM_BANK_MASK 0x3 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_MASK 0x7 /* [6:4] */ ++#define DMC_RNKVOL_MEM_COL_MASK 0x7 /* [2:0] */ ++#define DMC_CFG_INIT_XSREF_PD_MASK 0xc /* [3:2] */ ++#define DMC_CFG_PD_PRD_MASK 0xfff /* [15:4]pd_prd */ ++ ++/* register bit */ ++#define DMC_MEM_WIDTH_BIT 4 /* storing data bus width */ ++/* [CUSTOM] precharge disable/enable bit */ ++#define DMC_SFC_PRE_DIS_BIT 30 ++/* [CUSTOM] [29:12]config MR when LMR command */ ++#define DMC_SFC_CMD_MRS_BIT 12 ++#define DMC_SFC_RANK_BIT 16 /* [CUSTOM] [31:16]sfc_rank */ ++#define DMC_CFG_MEM_BG_BIT 10 /* [11:10] mem_bankgroup */ ++#define DMC_RNKVOL_MEM_BANK_BIT 8 /* [9:8] */ ++#define DMC_RNKVOL_MEM_ROW_BIT 4 /* [6:4] */ ++#define DMC_CFG_PD_PRD_BIT 4 /* [15:4]pd_prd */ ++#define DMC_CFG_PD_EN_BIT 0 /* [0] pd_en */ ++ ++/* register value */ ++#define DMC_BANK_MR1 1 ++#define DMC_BANK_MR3 0x3 ++#define DMC_CMD_TYPE_LMR 0x2 ++#define DMC_CMD_TYPE_READ 0x5 /* read */ ++#define DMC_CMD_TYPE_PRECHARGE_ALL 0x6 /* precharge all */ ++#define DMC_CMD_MRS_MR3 0x4 /* MR3 equal 0x4 */ ++#define DMC_CMD_MRS_A7 0x80 ++/* value 1 means exexute command. cmd_rank[0] control DDR RANK0 */ ++#define DMC_CMD_RANK0 0x1 ++#define DMC_MR0_BL_BUST8 0x0 /* BC8 (fixed) */ ++#define DMC_MR0_BL_BUST4 0x2 /* BC4 (fixed) */ ++#define DMC_AUTO_TIMING_DIS 0xfffff000 /* auto refresh disable */ ++#define DMC_POWER_DOWN_DIS 0xfffffffe /* powerDown disable */ ++#define DMC_SCRAMB_DIS 0xffffbfff /* [14] scramb disable */ ++/* [4] scramb_seed_type, [2:0] scramb_seed_sort */ ++#define DMC_SCRAMB_CFG 0xffffffe8 ++#define DMC_CFG_DRAM_TYPE_DDR4 0x7 /* DDR4 */ ++#define DMC_CFG_DRAM_TYPE_LPDDR4 0x8 /* LPDDR4 */ ++#define DMC_CFG_MEM_2BG 0x1 /* 2 Bank Group */ ++#define DMC_CFG_INIT_XSREF 0x8 /* bit[3] */ ++#define DMC_CFG_SREF_PD 0x4 /* bit[2] */ ++ ++#define DMC_CTRL_SREF_ENTER 0x1 /* 1 Enter Auto-self refresh */ ++#define DMC_CTRL_SREF_EXIT 0x2 /* 2 Exit Auto-self refresh */ ++#define DMC_RNKVOL_MEM_ROW_11 0x0 /* 000: 11 bit */ ++ ++#ifndef DDR_AXI_SWITCH_NUM ++#define DDR_AXI_SWITCH_NUM 4 /* ddr training axi switch number */ ++#endif ++ ++#ifndef DDR_RANK_NUM ++#define DDR_RANK_NUM 2 /* rank number */ ++#endif ++ ++#define dmc_sfc_cmd_write(sfc_cmd, addr) \ ++ reg_write((sfc_cmd) | (1 << DMC_SFC_PRE_DIS_BIT), addr) ++#define dmc_sfc_bank_write(sfc_bank, addr) \ ++ reg_write((sfc_bank) | (DMC_CMD_RANK0 << DMC_SFC_RANK_BIT), addr) ++ ++#define dmc_mpr_check_bit_0_127(cfg) \ ++ ddr_mpr_extract(cfg, DDR_DMC_SFC_RDATA3, DDR_DMC_SFC_RDATA2, \ ++ DDR_DMC_SFC_RDATA1, DDR_DMC_SFC_RDATA0) ++#define dmc_mpr_check_bit_128_255(cfg) \ ++ ddr_mpr_extract(cfg, DDR_DMC_SFC_RDATA7, DDR_DMC_SFC_RDATA6, \ ++ DDR_DMC_SFC_RDATA5, DDR_DMC_SFC_RDATA4) ++ ++/* dmc scramb */ ++#define dmc_save_scramb(relate_reg, i, base_dmc) do { \ ++ (relate_reg)->dmc_scramb[i] = \ ++ reg_read((base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ (relate_reg)->dmc_scramb_cfg[i] = \ ++ reg_read((base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++#define dmc_disable_scramb(relate_reg, i, base_dmc) do { \ ++ reg_write((relate_reg)->dmc_scramb[i] & DMC_SCRAMB_DIS, \ ++ (base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ reg_write((relate_reg)->dmc_scramb_cfg[i] & DMC_SCRAMB_CFG, \ ++ (base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++#define dmc_restore_scramb(relate_reg, i, base_dmc) do { \ ++ reg_write((relate_reg)->dmc_scramb[i], \ ++ (base_dmc) + DDR_DMC_CFG_DDRMODE); \ ++ reg_write((relate_reg)->dmc_scramb_cfg[i], \ ++ (base_dmc) + DDR_DMC_CFG_SCRAMB); \ ++} while (0) ++ ++/******** AXI **************************/ ++/** ++ * DMC -- PHY ++ * / ++ * DDRT -- AXI ++ * \ ++ * DMC -- PHY ++ */ ++/* base address: DDR_REG_BASE_AXI */ ++/* register offset address */ ++#define DDR_AXI_CHSEL_REMAP 0x40 /* Channel selection remapping */ ++#define DDR_AXI_REGION_ATTRIB0 0x104 /* region 0 */ ++#define DDR_AXI_REGION_ATTRIB1 0x114 /* region 1 */ ++ ++/* register mask */ ++#define AXI_REGION_ATTRIB_CH_MASK 0xffffffc0 /* channel mask */ ++ ++/* register value */ ++/* Map to the single channel, independent address */ ++#define AXI_RNG_ATTR_CH_MODE 0x8 ++#define AXI_RNG_ATTR_CH_START_0 0x0 ++#define AXI_RNG_ATTR_CH_START_1 0x1 ++#define AXI_RNG_ATTR_CH_START_2 0x2 ++#define AXI_RNG_ATTR_CH_START_3 0x3 ++#define AXI_RNG_NUM 2 /* region number */ ++ ++/********data define************************************/ ++struct ddr_ddrc_data { ++ unsigned int region_attrib[AXI_RNG_NUM]; ++ unsigned int rnkvol; ++ unsigned int chsel_remap; ++}; ++ ++#define ddr_axi_save_func(relate_reg) do { \ ++ (relate_reg)->ddrc.region_attrib[0] = \ ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ (relate_reg)->ddrc.region_attrib[1] = \ ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++ (relate_reg)->ddrc.chsel_remap = \ ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_CHSEL_REMAP); \ ++} while (0) ++ ++#define ddr_axi_restore_func(relate_reg) do { \ ++ reg_write((relate_reg)->ddrc.region_attrib[0], \ ++ DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ reg_write((relate_reg)->ddrc.region_attrib[1], \ ++ DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++ reg_write((relate_reg)->ddrc.chsel_remap, \ ++ DDR_REG_BASE_AXI + DDR_AXI_CHSEL_REMAP); \ ++} while (0) ++ ++#define ddr_axi_chsel_remap_func(cfg) do { \ ++ if ((cfg)->phy[(cfg)->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { \ ++ reg_write(0x76543210, DDR_REG_BASE_AXI + DDR_AXI_CHSEL_REMAP); \ ++ } else { \ ++ reg_write(0x76531420, DDR_REG_BASE_AXI + DDR_AXI_CHSEL_REMAP); \ ++ } \ ++} while (0) ++ ++#define ddr_axi_switch_func(cfg) do { \ ++ unsigned int ch_start = (cfg)->phy_idx; \ ++ if ((cfg)->phy[(cfg)->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) \ ++ ch_start = ((cfg)->phy_idx << 1) + (cfg)->dmc_idx; \ ++ reg_write((reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0) & AXI_REGION_ATTRIB_CH_MASK) | \ ++ AXI_RNG_ATTR_CH_MODE | ch_start, DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0); \ ++ reg_write((reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1) & AXI_REGION_ATTRIB_CH_MASK) | \ ++ AXI_RNG_ATTR_CH_MODE | ch_start, DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1); \ ++} while (0) ++ ++/* save rank0 for ddrt address */ ++#define ddr_rnkvol_save_func(relate_reg, base_dmc) \ ++ (relate_reg)->ddrc.rnkvol = reg_read((base_dmc) + ddr_dmc_cfg_rnkvol(0)); ++ ++#define ddr_rnkvol_restore_func(relate_reg, base_dmc) \ ++ reg_write((relate_reg)->ddrc.rnkvol, (base_dmc) + ddr_dmc_cfg_rnkvol(0)); ++ ++/* set mem_row to 0 */ ++#define ddr_rnkvol_set_func(cfg) do { \ ++ if ((cfg)->rank_idx == 1) { \ ++ reg_write(reg_read((cfg)->cur_dmc + ddr_dmc_cfg_rnkvol(0)) & (~DMC_RNKVOL_MEM_ROW_MASK), \ ++ (cfg)->cur_dmc + ddr_dmc_cfg_rnkvol(0)); \ ++ } \ ++} while (0) ++#endif /* DDR_DDRC_V520_H */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_ddrt_training.c b/drivers/ddr/vendor/default_v2/ddr_ddrt_training.c +new file mode 100644 +index 0000000..a36d19e +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_ddrt_training.c +@@ -0,0 +1,253 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __DDRT__ ++#ifdef DDR_DDRT_SPECIAL_CONFIG ++/* Some special DDRT need read register repeatedly */ ++static unsigned int ddr_ddrt_read(unsigned int addr) ++{ ++ int times = 0; ++ unsigned int data0, data1, data2; ++ ++ do { ++ data0 = reg_read(addr); ++ data1 = reg_read(addr); ++ data2 = reg_read(addr); ++ times++; ++ } while (((data0 != data1) || (data1 != data2)) && (times < DDRT_READ_TIMEOUT)); ++ ++ if (times >= DDRT_READ_TIMEOUT) { ++ ddr_fatal("DDRT wait timeout"); ++ ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, 0, -1, -1); ++ } ++ ++ return data0; ++} ++ ++/* Some special DDRT need write twice register */ ++static void ddr_ddrt_write(unsigned int data, unsigned int addr) ++{ ++ unsigned int tmp; ++ tmp = reg_read(addr); ++ reg_write(data, addr); ++ reg_write(data, addr); ++} ++#endif /* DDR_DDRT_SPECIAL_CONFIG */ ++ ++static unsigned int ddr_get_rank_size(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int base_dmc = cfg->cur_dmc; ++ unsigned int rnkvol; ++ unsigned int mem_bank, mem_row, mem_col, mem_width; ++ unsigned int size; ++ ++ mem_width = (reg_read(base_dmc + DDR_DMC_CFG_DDRMODE) >> DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK; ++ rnkvol = reg_read(base_dmc + ddr_dmc_cfg_rnkvol(0)); ++ mem_bank = (rnkvol >> DMC_RNKVOL_MEM_BANK_BIT) & DMC_RNKVOL_MEM_BANK_MASK; ++ mem_row = (rnkvol >> DMC_RNKVOL_MEM_ROW_BIT) & DMC_RNKVOL_MEM_ROW_MASK; ++ mem_col = rnkvol & DMC_RNKVOL_MEM_COL_MASK; ++ ++ /* ++ * mem_bank ++ * 0: 4 Bank(2 bank address) ++ * 1: 8 Bank(3 bank address) ++ * 2: 16 BANK(4 bank address) ++ * 3: reserved ++ * mem_row ++ * 000: 11 bit ++ * 001: 12 bit ++ * 010: 13 bit ++ * 011: 14 bit ++ * 100: 15 bit ++ * 101: 16 bit ++ * 110: 17 bit ++ * 111: 18 bit ++ * mem_width ++ * 000: 8 bit ++ * 001: 9 bit ++ * 010: 10 bit ++ * 011: 11 bit ++ * 100: 12 bit ++ */ ++ size = 1UL << ((mem_bank + 2) + (mem_row + 11) + (mem_col + 8) + mem_width); /* 2:2 bank; 11:11 bit; 8:8 bit */ ++ ddr_debug("rank size[%x]", size); ++ ++ return size; ++} ++ ++/* Init DDRT register before DDRT test */ ++void ddr_ddrt_init(const struct ddr_cfg_st *cfg, unsigned int mode) ++{ ++ unsigned int mem_width; ++ unsigned int mem_config; ++ unsigned int offset = 0; ++ ++ if (cfg == NULL) ++ return; ++ ++ if (cfg->rank_idx == 1) ++ offset = ddr_get_rank_size(cfg); ++ ++ ddr_training_ddrt_prepare_func(); ++ ++ mem_width = ((reg_read(cfg->cur_dmc + DDR_DMC_CFG_DDRMODE) >> ++ DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK); ++ mem_config = ((mem_width - 1) << DDRT_DDR_MEM_WIDTH) | ++ DDRT_DDR_COL_WIDTH | DDRT_DDR_ROW_WIDTH | DDRT_DDR_BANK_WIDTH; ++ /* DDRT SDRAM config */ ++ ddrt_reg_write(mem_config, DDR_REG_BASE_DDRT + DDRT_MEM_CONFIG); ++ /* DDR Address Base */ ++ ddrt_reg_write(ddrt_get_test_addr(DDRT_CFG_BASE_ADDR), ++ DDR_REG_BASE_DDRT + DDRT_DDR_BASE_ADDR); ++ /* DDRT test DDR using space */ ++ ddrt_reg_write(ddrt_get_test_addr(ddr_ddrt_get_test_addr() + offset), ++ DDR_REG_BASE_DDRT + DDRT_ADDR); ++ ddrt_reg_write(DDRT_CFG_SEED, DDR_REG_BASE_DDRT + DDRT_SEED); ++ ++ if (mode == DDR_DDRT_MODE_GATE) { ++ /* Read or Write Once */ ++ ddrt_reg_write(DDRT_CFG_BURST_CFG_GATE, ++ DDR_REG_BASE_DDRT + DDRT_BURST_CONFIG); ++ ddrt_reg_write(0x0, DDR_REG_BASE_DDRT + DDRT_BURST_NUM); ++ ddrt_reg_write(0x0, DDR_REG_BASE_DDRT + DDRT_ADDR_NUM); ++ ddrt_reg_write(0x0, DDR_REG_BASE_DDRT + DDRT_LOOP_NUM); ++ ddrt_reg_write(DDRT_CFG_REVERSED, ++ DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ } else { ++ /* reversed data form register init table */ ++ /* 128bit BURST4 */ ++ ddrt_reg_write(DDRT_CFG_BURST_CFG_DATAEYE, ++ DDR_REG_BASE_DDRT + DDRT_BURST_CONFIG); ++ ddrt_reg_write(cfg->phy[cfg->phy_idx].dmc[cfg->dmc_idx].ddrt_pattern, ++ DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ ddrt_reg_write(DDRT_CFG_BURST_NUM, ++ DDR_REG_BASE_DDRT + DDRT_BURST_NUM); ++ ddrt_reg_write(DDRT_CFG_ADDR_NUM, ++ DDR_REG_BASE_DDRT + DDRT_ADDR_NUM); ++ ddrt_reg_write(DDRT_CFG_LOOP_NUM, ++ DDR_REG_BASE_DDRT + DDRT_LOOP_NUM); ++ } ++ ++ ddr_debug("DDRT ADDR[%x = %x]", (DDR_REG_BASE_DDRT + DDRT_ADDR), ++ reg_read(DDR_REG_BASE_DDRT + DDRT_ADDR)); ++} ++ ++static int ddr_ddrt_test_process(int byte, int dq) ++{ ++ unsigned int err_ovfl; ++ unsigned int err_cnt; ++ unsigned int dq_num; ++ unsigned int dq_tmp; ++ ++ if (dq != -1) { /* check for dq */ ++ dq_num = ((unsigned int)byte << DDR_BYTE_DQ) + dq; ++ err_ovfl = ddrt_reg_read(DDR_REG_BASE_DDRT + DDRT_DQ_ERR_OVFL) & (1 << dq_num); ++ if (err_ovfl) ++ return -1; ++ ++ if (dq > 3) /* 3: dq0-dq3 */ ++ dq_tmp = (unsigned int)(dq - 4) << DDR_BYTE_DQ; /* 4 dq: dq0-dq3,dq4-dq7 */ ++ else ++ dq_tmp = (unsigned int)dq << DDR_BYTE_DQ; ++ ++ err_cnt = ddrt_reg_read(DDR_REG_BASE_DDRT + ++ ddrt_dq_err_cnt(((unsigned int)byte << 1) + ((unsigned int)dq >> 2))); /* shift left 2: 4 dq */ ++ err_cnt = err_cnt & (0xff << dq_tmp); ++ if (err_cnt) ++ return -1; ++ } else if (byte != -1) { /* check for byte */ ++ err_ovfl = ddrt_reg_read(DDR_REG_BASE_DDRT + ++ DDRT_DQ_ERR_OVFL) & (0xff << ((unsigned int)byte << DDR_BYTE_DQ)); ++ if (err_ovfl) ++ return -1; ++ ++ err_cnt = ddrt_reg_read(DDR_REG_BASE_DDRT + ++ ddrt_dq_err_cnt((unsigned int)byte << 1)); ++ err_cnt += ddrt_reg_read(DDR_REG_BASE_DDRT + ++ ddrt_dq_err_cnt(((unsigned int)byte << 1) + 1)); ++ if (err_cnt) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * @mask : DDRT option mask. ++ * @byte : DDR byte index. ++ * @dq : DDR dq index. ++ * DDRT test. Support read_only mode and write_read_compare mode. ++ * Success return 0, fail return -1. ++ */ ++int ddr_ddrt_test(unsigned int mask, int byte, int dq) ++{ ++ int result; ++ unsigned int regval; ++ unsigned int times = 0; ++ ++ ddrt_reg_write(mask | DDRT_CFG_START, DDR_REG_BASE_DDRT + DDRT_OP); ++ ddrt_reg_write(0, DDR_REG_BASE_DDRT + DDRT_STATUS); ++ ++ ddr_asm_dsb(); ++ ++ do { ++ regval = ddrt_reg_read(DDR_REG_BASE_DDRT + DDRT_STATUS); ++ times++; ++ } while ((!(regval & DDRT_TEST_DONE_MASK)) && (times < DDRT_WAIT_TIMEOUT)); ++ ++ if (times >= DDRT_WAIT_TIMEOUT) { ++ ddr_fatal("DDRT wait timeout"); ++ ddr_training_stat(DDR_ERR_DDRT_TIME_OUT, 0, -1, -1); ++ return -1; ++ } ++ ++ /* DDRT_READ_ONLY_MODE */ ++ if ((mask & DDRT_TEST_MODE_MASK) == DDRT_READ_ONLY_MODE) ++ return 0; /* return when DDRT finish */ ++ ++ /* DDRT_WR_COMPRARE_MODE No error occurred, test pass. */ ++ if (regval & DDRT_TEST_PASS_MASK) ++ return 0; ++ ++ result = ddr_ddrt_test_process(byte, dq); ++ ++ return result; ++} ++ ++/* Check ddrt test result. Success return 0, fail return -1 */ ++int ddr_ddrt_check(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int byte_index_to_dmc = cfg->cur_byte; ++ ++ /* ddrt test the byte relate to dmc, make sure not overflow */ ++ if (cfg->cur_byte >= (cfg->dmc_idx << 1)) ++ byte_index_to_dmc = cfg->cur_byte - (cfg->dmc_idx << 1); ++ ++ ddrt_reg_write(0, DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE | DDRT_PATTERM_PRBS9, byte_index_to_dmc, cfg->cur_dq)) ++ return -1; ++ ++ ddrt_reg_write(cfg->cur_pattern, DDR_REG_BASE_DDRT + DDRT_REVERSED_DQ); ++ if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE | DDRT_PATTERM_PRBS11, byte_index_to_dmc, cfg->cur_dq)) ++ return -1; ++ ++ return 0; ++} +diff --git a/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf0.h b/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf0.h +new file mode 100644 +index 0000000..8e030c5 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf0.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_V2_0_SHF0_H ++#define DDR_DDRT_V2_0_SHF0_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal. */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0x7f /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) (addr) ++#endif /* DDR_DDRT_V2_0_SHF0_H */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf1.h b/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf1.h +new file mode 100644 +index 0000000..db4cad5 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf1.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_V2_0_SHF1_H ++#define DDR_DDRT_V2_0_SHF1_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0x7f /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) ((addr) >> 1) ++#endif /* DDR_DDRT_V2_0_SHF1_H */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf2.h b/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf2.h +new file mode 100644 +index 0000000..36c5153 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_ddrt_v2_0_shf2.h +@@ -0,0 +1,106 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_DDRT_V2_0_SHF2_H ++#define DDR_DDRT_V2_0_SHF2_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_DDRT */ ++#define DDRT_OP 0x0 /* DDRT operation config */ ++#define DDRT_STATUS 0x4 /* DDRT status indicating */ ++#define DDRT_BURST_CONFIG 0x8 /* DDRT burst transfer config */ ++#define DDRT_MEM_CONFIG 0xc /* DDRT SDRAM config */ ++#define DDRT_BURST_NUM 0x10 /* DDRT burst number config */ ++/* DDRT burst number config register while testing address */ ++#define DDRT_ADDR_NUM 0x14 ++#define DDRT_LOOP_NUM 0x18 /* DDRT loop number config */ ++/* This register specified the system DDR starting address */ ++#define DDRT_DDR_BASE_ADDR 0x1c ++#define DDRT_ADDR 0x20 /* DDRT test start address config */ ++#define DDRT_REVERSED_DQ 0x30 /* DDRT reversed DQ indicating */ ++#define DDRT_SEED 0x38 /* DDRT starting random seed */ ++#define DDRT_KDATA 0x3c /* DDRT kdata config */ ++#define DDRT_DATA0 0x40 /* DDRT PRBS7 data config register0 */ ++#define DDRT_DATA1 0x44 /* DDRT PRBS7 data config register1 */ ++#define DDRT_DATA2 0x48 /* DDRT PRBS7 data config register2 */ ++#define DDRT_DATA3 0x4c /* DDRT PRBS7 data config register3 */ ++ ++/* DQ3~DQ0 error number indicator, every 8bit for each DQ */ ++#define ddrt_dq_err_cnt(n) (0x60 + ((n) << 2)) ++/* DQ31~DQ0 error number overflow indicator, every bit for each DQ. */ ++#define DDRT_DQ_ERR_OVFL 0x80 ++ ++/* register mask */ ++#define DDRT_TEST_MODE_MASK 0x300 /* DDRT Test Mode */ ++#define DDRT_TEST_DONE_MASK 0x1 /* [0] DDRT operation finish signal */ ++/* [1] DDRT Test result indicator. No error occurred, test pass. */ ++#define DDRT_TEST_PASS_MASK 0x2 ++ ++/* register bit */ ++#define DDRT_DDR_MEM_WIDTH 12 /* SDRAM total width */ ++ ++/* register value */ ++#define DDRT_CFG_START 0x1 ++#define DDRT_CFG_BURST_CFG_DATAEYE 0x4f ++#define DDRT_CFG_BURST_CFG_GATE 0x43 ++#ifdef CFG_EDA_VERIFY ++#define DDRT_CFG_BURST_NUM 0x5 /* ddrt test number */ ++#else ++#define DDRT_CFG_BURST_NUM 0x7f /* ddrt test number */ ++#endif ++#define DDRT_CFG_SEED 0x6d6d6d6d ++#define DDRT_CFG_REVERSED 0x55aa55aa ++#ifndef DDRT_CFG_BASE_ADDR ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x0 ++#endif ++/* [CUSTOM] DDRT test address. 0x800000 = 8M */ ++#define DDRT_CFG_TEST_ADDR_CMD (DDRT_CFG_BASE_ADDR + 0x800000) ++/* [CUSTOM] DDRT test start address. */ ++#define DDRT_CFG_TEST_ADDR_BOOT DDRT_CFG_BASE_ADDR ++#define DDRT_CFG_ADDR_NUM 0xffffffff ++#define DDRT_CFG_LOOP_NUM 0x0 ++ ++/* [2:0]000:8 bit; 001:9 bit; 010:10 bit; 011:11 bit; 100:12 bit. ++single SDRAM column number. */ ++#define DDRT_DDR_COL_WIDTH 0x2 ++/* [6:4]000:11 bit; 001:12 bit; 010:13 bit; 011:14 bit; 100:15 bit; 101:16 bit. ++single SDRAM row number */ ++#define DDRT_DDR_ROW_WIDTH 0x50 ++/* [8]0:4 Bank; 1:8 Bank. single SDRAM bank number */ ++#define DDRT_DDR_BANK_WIDTH 0x100 ++ ++#define DDRT_WR_COMPRARE_MODE (0 << 8) /* Write read & compare mode */ ++#define DDRT_WRITE_ONLY_MODE (1 << 8) /* Write only mode */ ++#define DDRT_READ_ONLY_MODE (2 << 8) /* Read only mode */ ++#define DDRT_RANDOM_WR_MODE (3 << 8) /* Random write & read mode */ ++ ++#define DDRT_PATTERM_PRBS9 (0 << 12) ++#define DDRT_PATTERM_PRBS7 (1 << 12) ++#define DDRT_PATTERM_PRBS11 (2 << 12) ++#define DDRT_PATTERM_K28_5 (3 << 12) ++ ++/* other */ ++#define DDRT_WAIT_TIMEOUT 1000000 ++#define DDRT_READ_TIMEOUT 20 ++#define DDRT_PCODE_WAIT_TIMEOUT 100000 ++ ++/* DDRT test DDR using space */ ++#define ddrt_get_test_addr(addr) ((addr) >> 2) ++#endif /* DDR_DDRT_V2_0_SHF2_H */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_gate_training.c b/drivers/ddr/vendor/default_v2/ddr_gate_training.c +new file mode 100644 +index 0000000..5a8e79b +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_gate_training.c +@@ -0,0 +1,192 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __GATE_TRAINING__ ++#ifdef DDR_GATE_TRAINING_CONFIG ++/* Find gate phase */ ++static int ddr_gate_find_phase(const struct ddr_cfg_st *cfg, struct ddr_delay_st *rdqsg) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ for (rdqsg->phase[i] = PHY_RDQSG_PHASE_MAX; ++ rdqsg->phase[i] > PHY_GATE_PHASE_MARGIN; ++ rdqsg->phase[i] -= PHY_RDQSG_PHASE_STEP) { ++ reg_write(rdqsg->phase[i] << PHY_RDQSG_PHASE_BIT, ++ base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ ddr_phy_cfg_update(base_phy); ++ if (ddr_ddrt_test(DDRT_WR_COMPRARE_MODE, i, -1) == 0) ++ break; ++ } ++ if (rdqsg->phase[i] <= PHY_GATE_PHASE_MARGIN) { ++ /* find gate phase fail */ ++ ddr_fatal("find gate phase[%x] fail", rdqsg->phase[i]); ++ ddr_training_stat(DDR_ERR_GATING, base_phy, -1, -1); ++ return -1; ++ } else { ++ /* decrease one setp to find bdl */ ++ rdqsg->phase[i] -= PHY_RDQSG_PHASE_STEP; ++ reg_write(rdqsg->phase[i] << PHY_RDQSG_PHASE_BIT, ++ base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ } ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return 0; ++} ++ ++static void ddr_gate_find_bdl_by_byte(const struct ddr_cfg_st *cfg, struct ddr_delay_st *rdqsg, ++ unsigned int byte_num, unsigned int gate_result) ++{ ++ int j; ++ unsigned int tmp; ++ ++ for (j = 0; j < byte_num; j++) { ++ if (!(gate_result & (1 << j))) { ++ rdqsg->bdl[j] += DDR_GATE_BDL_STEP; ++ if (rdqsg->bdl[j] > PHY_BDL_MASK) ++ tmp = ((rdqsg->bdl[j] - PHY_BDL_MASK - 1) << PHY_RDQSG_TX_BDL_BIT) + ++ (rdqsg->phase[j] << PHY_RDQSG_PHASE_BIT) + (PHY_BDL_MASK - 1); ++ else ++ tmp = (rdqsg->phase[j] << PHY_RDQSG_PHASE_BIT) + rdqsg->bdl[j]; ++ ++ reg_write(tmp, cfg->cur_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, j)); ++ } ++ } ++} ++ ++/* Find gate bdl */ ++static int ddr_gate_find_bdl(const struct ddr_cfg_st *cfg, struct ddr_delay_st *rdqsg) ++{ ++ int i, j; ++ unsigned int gate_result; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ unsigned int swtmode = reg_read(base_phy + DDR_PHY_SWTMODE); ++ ++ for (i = 0; i < byte_num; i++) ++ rdqsg->bdl[i] = 0; ++ ++ /* enable phy sw gate training mode */ ++ reg_write(swtmode | (1 << PHY_SWTMODE_SW_GTMODE_BIT), base_phy + DDR_PHY_SWTMODE); ++ ++ for (i = 0; i < PHY_GATE_BDL_MAX; i++) { ++ ddr_phy_cfg_update(base_phy); ++ ddr_ddrt_test(DDRT_READ_ONLY_MODE, -1, -1); ++ gate_result = (reg_read(base_phy + DDR_PHY_SWTRLT) >> PHY_SWTRLT_GATE_BIT) & ++ PHY_SWTRLT_GATE_MASK; ++ if (gate_result == ((1 << byte_num) - 1)) ++ break; ++ ++ ddr_gate_find_bdl_by_byte(cfg, rdqsg, byte_num, gate_result); ++ } ++ ++ /* disable phy sw gate training mode */ ++ reg_write(swtmode & (~(1 << PHY_SWTMODE_SW_GTMODE_BIT)), base_phy + DDR_PHY_SWTMODE); ++ ++ if (i == PHY_GATE_BDL_MAX) { /* find gate bdl fail */ ++ ddr_fatal("PHY[%x] find gate bdl fail. result[%x]", base_phy, gate_result); ++ for (j = 0; j < byte_num; j++) { ++ if (!(gate_result & (1 << j))) ++ ddr_training_stat(DDR_ERR_GATING, base_phy, j, -1); ++ } ++ return -1; ++ } else { ++ return 0; ++ } ++} ++ ++static int ddr_gate_training(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int i, tmp; ++ unsigned int byte_num; ++ struct ddr_delay_st rdqsg; ++ unsigned int def_delay[DDR_PHY_BYTE_MAX]; ++ int result; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ ddr_debug("DDR Gate training"); ++ ++ byte_num = get_byte_num(cfg); ++ ++ for (i = 0; i < byte_num; i++) ++ def_delay[i] = reg_read(base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ ++ /* find phase first */ ++ result = ddr_gate_find_phase(cfg, &rdqsg); ++ /* find bdl */ ++ if (!result) ++ result = ddr_gate_find_bdl(cfg, &rdqsg); ++ ++ /* set new phase */ ++ if (!result) { ++ for (i = 0; i < byte_num; i++) { ++ rdqsg.phase[i] -= PHY_GATE_PHASE_MARGIN; ++ tmp = reg_read(base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ tmp &= ~(PHY_RDQSG_PHASE_MASK << PHY_RDQSG_PHASE_BIT); ++ tmp |= rdqsg.phase[i] << PHY_RDQSG_PHASE_BIT; ++ reg_write(tmp, base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ } ++ } else { ++ /* restore default value */ ++ for (i = 0; i < byte_num; i++) ++ reg_write(def_delay[i], base_phy + ddr_phy_dxnrdqsgdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ return 0; /* use default value and not reset */ ++} ++ ++int ddr_gating_func(const struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ ++ /* gate training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_GATE_MASK) != DDR_FALSE) { ++ /* check hardware gating */ ++ if (reg_read(cfg->cur_phy + DDR_PHY_PHYINITSTATUS) & PHY_INITSTATUS_GT_MASK) { ++ ddr_fatal("PHY[%x] hw gating fail", cfg->cur_phy); ++ ddr_training_stat(DDR_ERR_HW_GATING, cfg->cur_phy, -1, -1); ++ return -1; ++ } ++ return 0; ++ } ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_GATE_MASK); ++ ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_GATE); ++ result += ddr_gate_training(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_gating_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR gate training"); ++ ++ return 0; ++} ++#endif /* DDR_GATE_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_interface.h b/drivers/ddr/vendor/default_v2/ddr_interface.h +new file mode 100644 +index 0000000..b515808 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_interface.h +@@ -0,0 +1,171 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_INTERFACE_H ++#define DDR_INTERFACE_H ++ ++#include ++ ++#ifndef CONFIG_MINI_BOOT ++extern char g_ddr_training_cmd_start[]; /* DDR training code start address */ ++extern char g_ddr_training_cmd_end[]; /* DDR training code end address */ ++#endif ++ ++#define reg_read(addr) (*(volatile unsigned int *)((uintptr_t)(addr))) ++#define reg_write(val, addr) ((*(volatile unsigned int *)((uintptr_t)(addr))) = (val)) ++ ++#define DDR_PHY_BYTE_MAX 4 ++#define DDR_PHY_BIT_NUM 8 ++/* support max bit 32 */ ++#define DDR_PHY_BIT_MAX (DDR_PHY_BYTE_MAX * DDR_PHY_BIT_NUM) ++ ++#define DDR_REG_NAME_MAX 32 /* register name */ ++#define DDR_CA_ADDR_MAX 10 ++ ++#define DDR_SUPPORT_PHY_MAX 3 /* support max phy number */ ++#define DDR_SUPPORT_RANK_MAX 2 /* support max rank number */ ++#define DDR_SUPPORT_DMC_MAX 4 /* support max dmc number */ ++#define DDR_CK_RESULT_MAX 2 /* DCC CK result number */ ++ ++/* ++ * DDR training register number: ++ * WDQS 4 ++ * WDQ Phase 4 ++ * WDQ BDL 8 ++ * WDM 4 ++ * Write DQ/DQS OE 4 ++ * RDQS 4 ++ * RDQ BDL 8 ++ * Gate 4 ++ * CS 1 ++ * CLK 1 ++ * Host Vref 4 ++ * DRAM Vref 4 ++ * CA Phase 1 ++ * CA BDL 5 ++ * ------------------- ++ * 60 ++ */ ++#define DDR_TRAINING_REG_NUM 60 ++/* register max. */ ++#define DDR_TRAINING_REG_MAX (DDR_TRAINING_REG_NUM * DDR_SUPPORT_PHY_MAX) ++ ++#define DDR_TRAINING_CMD_SW (1 << 0) ++#define DDR_TRAINING_CMD_HW (1 << 1) ++#define DDR_TRAINING_CMD_MPR (1 << 2) ++#define DDR_TRAINING_CMD_WL (1 << 3) ++#define DDR_TRAINING_CMD_GATE (1 << 4) ++#define DDR_TRAINING_CMD_DATAEYE (1 << 5) ++#define DDR_TRAINING_CMD_VREF (1 << 6) ++#define DDR_TRAINING_CMD_AC (1 << 7) ++#define DDR_TRAINING_CMD_LPCA (1 << 8) ++#define DDR_TRAINING_CMD_SW_NO_WL (1 << 9) ++#define DDR_TRAINING_CMD_CONSOLE (1 << 10) ++#define DDR_TRAINING_CMD_DCC (1 << 11) ++#define DDR_TRAINING_CMD_PCODE (1 << 12) ++#define DDR_TRAINING_CMD_DPMC (1 << 13) ++ ++/*******log level ********************/ ++#define DDR_LOG_INFO_STR "info" ++#define DDR_LOG_DEBUG_STR "debug" ++#define DDR_LOG_WARNING_STR "warning" ++#define DDR_LOG_ERROR_STR "error" ++#define DDR_LOG_FATAL_STR "fatal" ++ ++#define DDR_LOG_INFO (1 << 0) ++#define DDR_LOG_DEBUG (1 << 1) ++#define DDR_LOG_WARNING (1 << 2) ++#define DDR_LOG_ERROR (1 << 3) ++#define DDR_LOG_FATAL (1 << 4) ++ ++#define DDR_TRAINING_BOOT_RESULT_ADDR (TEXT_BASE + 0x1000000) /* boot + 16M */ ++ ++#define DDR_TRAINING_VER "V2.2.0 20200826" ++#define DDR_VERSION 0x220 ++struct training_data { ++ unsigned int ddr_bit_result[DDR_PHY_BIT_MAX]; ++ unsigned int ddr_bit_best[DDR_PHY_BIT_MAX]; ++ unsigned int ddr_win_sum; ++}; ++ ++struct ddr_training_data_st { ++ unsigned int base_phy; ++ unsigned int byte_num; ++ unsigned int rank_idx; ++ struct training_data read; ++ struct training_data write; ++ unsigned int ca_addr[DDR_CA_ADDR_MAX]; ++}; ++ ++struct rank_data_st { ++ unsigned int item; ++ struct ddr_training_data_st ddrtr_data; ++}; ++ ++struct phy_data_st { ++ unsigned int rank_num; ++ struct rank_data_st rank_st[DDR_SUPPORT_RANK_MAX]; ++}; ++ ++struct ddr_training_result_st { ++ unsigned int phy_num; ++ struct phy_data_st phy_st[DDR_SUPPORT_PHY_MAX]; ++}; ++ ++struct ddr_reg_val_st { ++ unsigned int rank_index; ++ unsigned int byte_index; ++ unsigned int offset; ++ unsigned int val; ++ char name[DDR_REG_NAME_MAX]; ++}; ++ ++struct ddr_cmd_st { ++ unsigned int cmd; ++ unsigned int level; ++ unsigned int start; ++ unsigned int length; ++}; ++ ++typedef struct ddr_training_result_st *(*ddr_cmd_entry_func) ++ (const struct ddr_cmd_st *cmd_st); ++ ++/* DDR training interface before boot */ ++int ddr_pcode_training_if(void); ++int ddr_sw_training_if(void); ++int ddr_hw_training_if(void); ++int ddr_training_console_if(void); ++int ddr_hw_training_init(void); ++void ddr_retrain_anti_aging_enable(void); ++void ddr_dmc_auto_power_down_cfg(void); ++void ddr_set_rdqbdl_def_val(void); ++ ++/* DDR training check interface when boot */ ++struct ddr_training_result_st *ddr_cmd_training_if(const struct ddr_cmd_st *cmd_st); ++int check_ddr_training(void); ++ ++/* DDR training command interface after boot */ ++void ddr_reg_result_display(const struct ddr_training_result_st *ddrtr_result); ++void ddr_cmd_result_display(const struct ddr_training_result_st *ddrtr_result, unsigned int cmd); ++void *ddr_cmd_get_entry(void); ++void ddr_cmd_prepare_copy(void); ++void ddr_cmd_site_save(void); ++void ddr_cmd_site_restore(void); ++#endif /* DDR_INTERFACE_H */ ++ +diff --git a/drivers/ddr/vendor/default_v2/ddr_lpca_training.c b/drivers/ddr/vendor/default_v2/ddr_lpca_training.c +new file mode 100644 +index 0000000..25b45dc +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_lpca_training.c +@@ -0,0 +1,486 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __LPCA_TRAINING__ ++#ifdef DDR_LPCA_TRAINING_CONFIG ++/* Reset address bdl training data */ ++static void ddr_lpca_reset(struct ca_data_st *data) ++{ ++ unsigned int index; ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) { ++ data->left[index] = -1; ++ data->right[index] = -1; ++ } ++ ++ data->min = PHY_ACADDR_BDL_MASK; ++ data->max = 0; ++ data->done = 0; ++} ++ ++/* Get ca bit relation */ ++static void ddr_lpca_get_bit(struct ca_data_st *data) ++{ ++ unsigned int index; ++ ++ /* get ca bit in four register */ ++#ifdef DDR_LPCA_GET_BIT ++ unsigned int swap_sel; ++ for (index = 0; index < (DDR_PHY_CA_REG_MAX - 1); index++) { ++ reg_write(index + 1, data->base_phy + DDR_PHY_CATSWAPINDEX); ++ swap_sel = reg_read(data->base_phy + DDR_PHY_CATSWAPSEL); ++ ++ data->bits[index * 2].bit_p = /* ca 0/2/4/6 Rising edge */ ++ swap_sel & PHY_CATSWAPSEL_BIT_MASK; ++ data->bits[index * 2].bit_n = /* ca 0/2/4/6 Falling edge */ ++ (swap_sel >> 8) & PHY_CATSWAPSEL_BIT_MASK; /* bit8 */ ++ data->bits[index * 2 + 1].bit_p = /* ca * 2 + 1: bit 1/3/5/7 Rising edge */ ++ (swap_sel >> 16) & PHY_CATSWAPSEL_BIT_MASK; /* bit16 */ ++ data->bits[index * 2 + 1].bit_n = /* ca * 2 + 1: bit 1/3/5/7 Falling edge */ ++ (swap_sel >> 24) & PHY_CATSWAPSEL_BIT_MASK; /* bit24 */ ++ } ++#else /* for MVPV200 */ ++ for (index = 0; index < (DDR_PHY_CA_REG_MAX - 1); index++) { ++ data->bits[index * 2].bit_p = index * 4 + 0; /* ca 0/2/4/6 Rising edge, dq*4+0:0/4/8/12 */ ++ data->bits[index * 2].bit_n = index * 4 + 1; /* ca 0/2/4/6 Falling edge, dq*4+1:1/5/9/13 */ ++ data->bits[index * 2 + 1].bit_p = index * 4 + 2; /* ca*2+1: bit 1/3/5/7 Rising edge, dq*4+2:2/6/10/14 */ ++ data->bits[index * 2 + 1].bit_n = index * 4 + 3; /* ca*2+1: bit 1/3/5/7 Falling edge, dq*4+3:2/6/10/14 */ ++ } ++#endif ++ ++ /* ++ * set ca bit for ca4 and ca9 ++ * ca4 equal ca0 ca9 equal ca5 ++ */ ++ for (index = 8; index > 4; index--) { /* set ca bit for ca5 to ca8 equal ca4 to ca7 */ ++ data->bits[index].bit_p = data->bits[index - 1].bit_p; ++ data->bits[index].bit_n = data->bits[index - 1].bit_n; ++ } ++ ++ data->bits[4].bit_p = data->bits[0].bit_p; /* ca4 equal ca0 */ ++ data->bits[4].bit_n = data->bits[0].bit_n; /* ca4 equal ca0 */ ++ data->bits[9].bit_p = data->bits[5].bit_p; /* ca9 equal ca5 */ ++ data->bits[9].bit_n = data->bits[5].bit_n; /* ca9 equal ca5 */ ++ ++#if defined(DDR_TRAINING_CMD) ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) { ++ ddr_info("CA[%x] bit_p[%x]", index, data->bits[index].bit_p); ++ ddr_info("CA[%x] bit_n[%x]", index, data->bits[index].bit_n); ++ } ++#endif ++} ++ ++/* Get address bdl default value */ ++static void ddr_lpca_get_def(struct ca_data_st *data) ++{ ++ unsigned int index; ++ ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) ++ data->def[index] = reg_read(data->base_phy + ddr_phy_acaddrbdl(index)); ++} ++ ++/* Restore address bdl default value */ ++static void ddr_lpca_restore_def(struct ca_data_st *data) ++{ ++ unsigned int index; ++ ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) ++ reg_write(data->def[index], data->base_phy + ddr_phy_acaddrbdl(index)); ++ ++ ddr_phy_cfg_update(data->base_phy); ++} ++ ++/* Set address bdl value */ ++static void ddr_lpca_set_bdl(unsigned int base_phy, unsigned int bdl) ++{ ++ unsigned int index; ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) ++ reg_write(bdl | (bdl << PHY_ACADDRBDL_ADDR1_BIT), ++ base_phy + ddr_phy_acaddrbdl(index)); ++ ++ ddr_phy_cfg_update(base_phy); ++} ++ ++/* Update address bdl value with training result */ ++static void ddr_lpca_update_bdl(struct ca_data_st *data) ++{ ++ unsigned int index; ++ unsigned int addr0, addr1; ++ ++ for (index = 0; index < DDR_PHY_CA_REG_MAX; index++) { ++ addr0 = (data->left[index * 2] + data->right[index * 2]) >> 1; /* 2:ca middle value */ ++ addr1 = (data->left[index * 2 + 1] + data->right[index * 2 + 1]) >> 1; /* 2:ca middle value */ ++ reg_write(addr0 | (addr1 << PHY_ACADDRBDL_ADDR1_BIT), ++ data->base_phy + ddr_phy_acaddrbdl(index)); ++ } ++ ++ ddr_phy_cfg_update(data->base_phy); ++} ++ ++/* Init data before training */ ++static void ddr_lpca_init(unsigned int base_dmc, unsigned int base_phy, struct ca_data_st *data) ++{ ++ data->base_dmc = base_dmc; ++ data->base_phy = base_phy; ++ ++ /* gat ca bit relation */ ++ ddr_lpca_get_bit(data); ++ ++ /* get ac addr bdl default value */ ++ ddr_lpca_get_def(data); ++ ++ /* reset training data */ ++ ddr_lpca_reset(data); ++} ++ ++/* Display training result */ ++static void ddr_lpca_display(struct ca_data_st *data) ++{ ++#if defined(DDR_TRAINING_CMD) ++ unsigned int index; ++ ++ ddr_debug("CA phase[%x = %x]", ++ data->base_phy + DDR_PHY_ADDRPHBOUND, ++ reg_read(data->base_phy + DDR_PHY_ADDRPHBOUND)); ++ ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) ++ ddr_debug("CA[%x] left[%x] right[%x]", ++ index, data->left[index], data->right[index]); ++ ++ ddr_debug("min[%x] max[%x] done[%x]", data->min, data->max, data->done); ++#endif ++} ++ ++/* Wait lpca command done */ ++static void ddr_lpca_wait(volatile union u_phy_catconfig *ca) ++{ ++ unsigned int count = 0; ++ while (count < DDR_LPCA_WAIT_TIMEOUT) { ++ if (ca->bits.sw_cat_dqvalid == 1) { ++ ca->bits.sw_cat_dqvalid = 0; /* clear */ ++ break; ++ } ++ count++; ++ } ++ ++ /* generally, count is 0 */ ++ if (count >= DDR_LPCA_WAIT_TIMEOUT) ++ ddr_error("LPCA wait timeout"); ++} ++ ++/* Compare dq result and pattern */ ++static int ddr_lpca_compare(struct ca_bit_st *ca_bit, ++ unsigned int dq_result, unsigned int pattern_p, ++ unsigned int pattern_n, unsigned int index) ++{ ++ if (((dq_result >> ca_bit->bit_p) & 0x1) != ((pattern_p >> index) & 0x1)) ++ return -1; ++ ++ if (((dq_result >> ca_bit->bit_n) & 0x1) != ((pattern_n >> index) & 0x1)) ++ return -1; ++ ++ return 0; ++} ++ ++static void ddr_lpca_get_data(struct ca_data_st *data, unsigned int index, unsigned int bdl) ++{ ++ /* pass */ ++ if (data->left[index] == -1) { ++ data->left[index] = bdl; ++ /* set min left bound */ ++ if (bdl < data->min) ++ data->min = bdl; ++ } ++ ++ /* unstable border value or abnormal value */ ++ if ((data->right[index] != -1) && ((bdl - data->right[index]) > 1)) ++ ddr_warning("CA[%x] bdl[%x] right[%x] ph[%x]", ++ index, bdl, data->right[index], ++ reg_read(data->base_phy + DDR_PHY_ADDRPHBOUND)); ++ ++ data->right[index] = bdl; ++ data->done |= (0x1 << index); ++ ++ /* set max right bound */ ++ if (data->right[index] > data->max) ++ data->max = data->right[index]; ++} ++ ++/* Check each CA whether pass */ ++static void ddr_lpca_check(struct ca_data_st *data, unsigned int bdl, unsigned int is_ca49) ++{ ++ unsigned int dq_result = reg_read(data->base_phy + DDR_PHY_PHYDQRESULT); ++ unsigned int pattern_p = reg_read(data->base_phy + ++ DDR_PHY_SWCATPATTERN_P) & PHY_CAT_PATTERN_MASK; ++ unsigned int pattern_n = reg_read(data->base_phy + ++ DDR_PHY_SWCATPATTERN_N) & PHY_CAT_PATTERN_MASK; ++ unsigned int index; ++ ++ for (index = 0; index < DDR_PHY_CA_MAX; index++) { ++ if (is_ca49) { ++ if (index != 4 && index != 9) /* ca4 ca9 */ ++ continue; ++ } else { ++ if (index == 4 || index == 9) /* ca4 ca9 */ ++ continue; ++ } ++ ++ /* compare result and pattern */ ++ if (!ddr_lpca_compare(&data->bits[index], dq_result, pattern_p, pattern_n, index)) ++ ddr_lpca_get_data(data, index, bdl); /* pass */ ++ } ++} ++ ++/* Excute lpca command and check result */ ++static void ddr_lpca_excute(struct ca_data_st *data, unsigned int bdl, unsigned int is_ca49) ++{ ++ volatile union u_phy_catconfig *ca = (union u_phy_catconfig *) ++ (data->base_phy + DDR_PHY_CATCONFIG); ++ ++ if (is_ca49) ++ ca->bits.sw_cat_mrw48 = 1; ++ else ++ ca->bits.sw_cat_mrw41 = 1; ++ ++ ddr_lpca_wait(ca); ++ ca->bits.sw_cat_cke_low = 1; ++ ddr_lpca_wait(ca); ++ ca->bits.sw_cat_strobe = 1; ++ ddr_lpca_wait(ca); ++ ++ /* check PHYDQRESULT */ ++ ddr_lpca_check(data, bdl, is_ca49); ++ ++ ca->bits.sw_cat_cke_high = 1; ++ ddr_lpca_wait(ca); ++ ca->bits.sw_cat_mrw42 = 1; ++ ddr_lpca_wait(ca); ++} ++ ++/* Find address bdl */ ++static int ddr_lpca_find_bdl(struct ca_data_st *data) ++{ ++ unsigned int bdl; ++ ++ for (bdl = 0; bdl <= PHY_ACADDR_BDL_MASK; bdl++) { ++ /* update bdl */ ++ ddr_lpca_set_bdl(data->base_phy, bdl); ++ ++ /* ca0~ca3, ca5~ca8 */ ++ ddr_lpca_excute(data, bdl, DDR_FALSE); ++ ++ /* ca4, ca9 */ ++ ddr_lpca_excute(data, bdl, DDR_TRUE); ++ } ++ ++ if (data->done == PHY_CAT_PATTERN_MASK) ++ return 0; ++ ++ return -1; ++} ++ ++/* Loop phase to find valid bdl and phase */ ++static int ddr_lpca_loop_phase(struct ca_data_st *data, int step) ++{ ++ volatile union u_phy_addrphbound *ph = (union u_phy_addrphbound *) ++ (data->base_phy + DDR_PHY_ADDRPHBOUND); ++ unsigned int phase; ++ unsigned int addrph_def = ph->bits.addrph_a; ++ int addrph = addrph_def; ++ ++ for (phase = 0; phase <= PHY_ADDRPH_MASK; phase++) { ++ /* reset ca training data */ ++ ddr_lpca_reset(data); ++ ++ /* find bdl */ ++ if (!ddr_lpca_find_bdl(data)) ++ return 0; ++ ++ addrph += step; ++ if (addrph < 0 || addrph > PHY_ADDRPH_MASK) ++ break; ++ ++ ph->bits.addrph_a = addrph; ++ ddr_phy_cfg_update(data->base_phy); ++ } ++ ++ /* restore default value */ ++ ddr_debug("current phase[%x = %x], restore default[%x]", ph, *ph, addrph_def); ++ ph->bits.addrph_a = addrph_def; ++ ++ return -1; ++} ++ ++/* Find a valid phase */ ++static int ddr_lpca_find_phase(struct ca_data_st *data) ++{ ++ /* increase default value to find */ ++ if (!ddr_lpca_loop_phase(data, 1)) ++ return 0; ++ ++ /* decrease default value to find */ ++ if (!ddr_lpca_loop_phase(data, -1)) ++ return 0; ++ ++ return -1; ++} ++ ++/* Set step to adjust address window */ ++static int ddr_lpca_set_step(struct ca_data_st *data) ++{ ++ /* max window, no need to found */ ++ if (data->min == 0 && data->max == PHY_ACADDR_BDL_MASK) ++ return 0; ++ ++ if (data->min == 0) ++ return -1; /* window on left, move to right */ ++ else ++ return 1; /* window on right, move to left */ ++} ++ ++/* ++ * Adjust address window via change phase. ++ * Increase phase, window will move to left. ++ */ ++static void ddr_lpca_adjust(struct ca_data_st *data) ++{ ++ int step; ++ volatile union u_phy_addrphbound *ph = (union u_phy_addrphbound *) ++ (data->base_phy + DDR_PHY_ADDRPHBOUND); ++ unsigned int phase; ++ unsigned int addrph_last = ph->bits.addrph_a; ++ int addrph_cur = addrph_last; ++ ++ /* set step to increase or decrease phase */ ++ step = ddr_lpca_set_step(data); ++ if (!step) ++ return; ++ ++ for (phase = 0; phase <= PHY_ADDRPH_MASK; phase++) { ++ addrph_cur += step; ++ if (addrph_cur < 0 || addrph_cur > PHY_ADDRPH_MASK) ++ return; ++ ++ ph->bits.addrph_a = addrph_cur; ++ ddr_phy_cfg_update(data->base_phy); ++ ++ /* reset ca training data */ ++ ddr_lpca_reset(data); ++ ++ if (ddr_lpca_find_bdl(data)) { ++ /* not find bdl, restore last value */ ++ addrph_cur -= step; ++ ddr_lpca_find_bdl(data); ++ return; ++ } ++ ++ /* max window: ------- */ ++ if (data->min == 0 && data->max == PHY_ACADDR_BDL_MASK) ++ return; ++ ++ /* last window: -----xx */ ++ if (data->min == 0 && step == 1) { ++ /* last value is best */ ++ addrph_cur -= step; ++ ph->bits.addrph_a = addrph_cur; ++ ddr_phy_cfg_update(data->base_phy); ++ ddr_lpca_reset(data); ++ ddr_lpca_find_bdl(data); ++ return; ++ } ++ ++ /* best window: x-----x */ ++ if (data->min > 0 && step == -1) ++ return; ++ } ++} ++ ++/* Low power DDR CA training */ ++int ddr_lpca_training(const struct ddr_cfg_st *cfg) ++{ ++ volatile union u_phy_catconfig *ca = (union u_phy_catconfig *) ++ (cfg->cur_phy + DDR_PHY_CATCONFIG); ++ ++ struct ca_data_st data; ++ int ret; ++ ++ ddr_debug("DDR LPCA training"); ++ ++ /* init data */ ++ ddr_lpca_init(cfg->cur_dmc, cfg->cur_phy, &data); ++ ++ /* enable sw ca training, wait 62.5ns */ ++ ca->bits.sw_cat_en = 1; ++ ++ /* find a valid phase first */ ++ ret = ddr_lpca_find_phase(&data); ++ ++ /* display training result */ ++ ddr_lpca_display(&data); ++ ++ if (ret) { ++ /* restore default value when fail */ ++ ddr_lpca_restore_def(&data); ++ ddr_error("PHY[%x] found phase fail, result[%x]", cfg->cur_phy, data.done); ++ ddr_training_stat(DDR_ERR_LPCA, cfg->cur_phy, -1, -1); ++ } else { ++ /* adjust window via phase */ ++ ddr_lpca_adjust(&data); ++ ddr_lpca_display(&data); ++ /* set training result */ ++ ddr_lpca_update_bdl(&data); ++ } ++ ++ /* disable sw ca training */ ++ ca->bits.sw_cat_en = 0; ++ ++ /* save lpca result data to printf */ ++ ddr_lpca_data_save(cfg, &data); ++ ++ return ret; ++} ++ ++int ddr_lpca_training_func(const struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ struct tr_relate_reg relate_reg; ++ ++ /* LPCA training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_LPCA_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_LPCA_MASK); ++ ++ /* only lowpower ddr3 support */ ++ if ((reg_read(cfg->cur_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_LPDDR3) == ++ PHY_DRAMCFG_TYPE_LPDDR3) ++ result += ddr_lpca_training(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_lpca_training_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support LPDDR CA training"); ++ return 0; ++} ++#endif /* DDR_LPCA_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_mpr_training.c b/drivers/ddr/vendor/default_v2/ddr_mpr_training.c +new file mode 100644 +index 0000000..2f5beb6 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_mpr_training.c +@@ -0,0 +1,293 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __MPR_TRAINING__ ++#ifdef DDR_MPR_TRAINING_CONFIG ++/* Switch MPR function */ ++static void ddr_mpr_switch(unsigned int base_dmc, int val) ++{ ++ unsigned int sfc_cmd; ++ if (val == DDR_TRUE) ++ sfc_cmd = (DMC_CMD_MRS_MR3 << DMC_SFC_CMD_MRS_BIT) | DMC_CMD_TYPE_LMR; ++ else ++ sfc_cmd = DMC_CMD_TYPE_LMR; ++ ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, DMC_BANK_MR3); ++ ++ /* clear */ ++ if (val == DDR_FALSE) { ++ reg_write(0x0, base_dmc + DDR_DMC_SFCBANK); ++ reg_write(0x0, base_dmc + DDR_DMC_SFCREQ); ++ } ++} ++ ++/* Judge MPR data */ ++static int ddr_mpr_judge(unsigned int data1, unsigned int data2, ++ unsigned int data3, unsigned int data4, unsigned int dq_index) ++{ ++ /* check byte */ ++ if (dq_index == -1) { ++ if (data1 == DDR_MPR_BYTE_MASK && data2 == 0x0 && ++ data3 == DDR_MPR_BYTE_MASK && data4 == 0x0) ++ return 0; ++ else ++ return -1; ++ } else { ++ /* check DQ */ ++ data1 = (data1 >> dq_index) & DDR_MPR_BIT_MASK; ++ data2 = (data2 >> dq_index) & DDR_MPR_BIT_MASK; ++ data3 = (data3 >> dq_index) & DDR_MPR_BIT_MASK; ++ data4 = (data4 >> dq_index) & DDR_MPR_BIT_MASK; ++ if (data1 == DDR_MPR_BIT_MASK && data2 == 0x0 && ++ data3 == DDR_MPR_BIT_MASK && data4 == 0x0) ++ return 0; ++ else ++ return -1; ++ } ++} ++ ++/* Extract MPR read data to judge */ ++static int ddr_mpr_extract(struct ddr_cfg_st *cfg, ++ unsigned int offset0, unsigned int offset1, ++ unsigned int offset2, unsigned int offset3) ++{ ++ unsigned int data1, data2, data3, data4; ++ unsigned int base_dmc = cfg->cur_dmc; ++ unsigned int byte_index = cfg->cur_byte; ++ ++ data1 = reg_read(base_dmc + offset0); /* SFC read data [127:96] or [255:224] */ ++ data2 = reg_read(base_dmc + offset1); /* SFC read data [95:64] or [223:192] */ ++ data3 = reg_read(base_dmc + offset2); /* SFC read data [63:32] or [191:160] */ ++ data4 = reg_read(base_dmc + offset3); /* SFC read data [31:0] or [159:128] */ ++ ++ ddr_info("byte[%x] data[%x=%x][%x=%x][%x=%x][%x=%x]", ++ byte_index, ++ base_dmc + offset0, data1, base_dmc + offset1, data2, ++ base_dmc + offset2, data3, base_dmc + offset3, data4); ++ ++ if (get_byte_num(cfg) == DDR_PHY_BYTE_MAX) { ++ /* four byte: data1[0xFFFFFFFF] data2[0x00000000] ++ data3[0xFFFFFFFF] data4[0x00000000] */ ++ data1 = (data1 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data2 = (data2 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data3 = (data3 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data4 = (data4 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ } else { ++ /* two byte: data1[0xFFFF0000] data2[0xFFFF0000] ++ data3[0xFFFF0000] data4[0xFFFF0000] */ ++ data1 = (data1 >> DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data2 = (data2 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data3 = (data3 >> DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data4 = (data4 >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ if (ddr_mpr_judge(data1, data2, data3, data4, cfg->cur_dq)) ++ return -1; ++ ++ /* two byte need to swap data and check again */ ++ data1 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA1) >> ++ DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data2 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA0) >> ++ (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ data3 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA3) >> ++ DDR_MPR_BYTE_BIT >> (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & ++ DDR_MPR_BYTE_MASK; ++ data4 = (reg_read(base_dmc + DDR_DMC_SFC_RDATA2) >> ++ (byte_index << DDR_MPR_BYTE_SHIFT_BIT)) & DDR_MPR_BYTE_MASK; ++ } ++ ++ return ddr_mpr_judge(data1, data2, data3, data4, cfg->cur_dq); ++} ++ ++/* Find RDQ via MPR */ ++static int ddr_mpr_find_rdq(struct ddr_cfg_st *cfg) ++{ ++ struct training_data training; ++ unsigned int dq_num; ++ unsigned int win_num; ++ unsigned int def_dq, best_dq; ++ unsigned int byte_index, dq_index; ++ ++ memset(&training, 0, sizeof(struct training_data)); ++ /* find rdq via mpr */ ++ cfg->dq_check_type = DDR_CHECK_TYPE_MPR; ++ ++ /* find rdq */ ++ for (byte_index = 0; ++ byte_index < get_byte_num(cfg); byte_index++) { ++ for (dq_index = 0; dq_index < DDR_PHY_BIT_NUM; dq_index++) { ++ dq_num = (byte_index << DDR_BYTE_DQ) + dq_index; ++ def_dq = ddr_phy_get_dq_bdl(cfg); ++ ddr_dataeye_find_dq(cfg, &training); ++ win_num = training.ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ best_dq = training.ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ if (win_num > 0) { ++ ddr_phy_set_dq_bdl(cfg, best_dq); ++ } else { ++ /* In normal case, not reach here */ ++ /* restore default value */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ++ ddr_fatal("PHY[%x] Byte[%x] DQ[%x] MPR fail", ++ cfg->cur_phy, byte_index, dq_index); ++ ddr_training_stat(DDR_ERR_MPR, cfg->cur_phy, ++ byte_index, dq_index); ++ return -1; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* Find RDQS via MPR */ ++static int ddr_mpr_find_rdqs(struct ddr_cfg_st *cfg) ++{ ++ unsigned int rdqs_start = 0; ++ unsigned int rdqs_end = PHY_RDQS_BDL_MASK; ++ unsigned int rdqs_mid; ++ unsigned int val, delay; ++ unsigned int count = 0; ++ int found = DDR_FALSE; ++ ++ /* set rdq to middle value */ ++ reg_write(PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte)); ++ reg_write(PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte)); ++ ++ /* clear rdqs */ ++ delay = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)) >> PHY_RDQS_BDL_BIT; ++ rdqs_mid = delay; /* if not found, restore default value */ ++ delay = delay & (~PHY_RDQS_BDL_MASK); ++ ++ /* find rdqs */ ++ for (val = 0; val <= PHY_RDQS_BDL_MASK; val++) { ++ reg_write(delay | (val << PHY_RDQS_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ddr_phy_cfg_update(cfg->cur_phy); ++ /* check ok */ ++ if (!ddr_mpr_check(cfg)) { ++ if (found == DDR_TRUE) ++ continue; ++ ++ rdqs_start = val; /* found start value */ ++ count++; ++ if (count == DDR_MPR_RDQS_FIND_TIMES) ++ found = DDR_TRUE; ++ } else { ++ if (found == DDR_TRUE) { ++ rdqs_end = val; /* found end value */ ++ break; ++ } ++ } ++ } ++ ++ if (found == DDR_TRUE) { ++ rdqs_mid = ((rdqs_end - rdqs_start) >> 1) + rdqs_start; ++ ddr_info("PHY[%x] Byte[%x] rdqs_middle[%x]", ++ cfg->cur_phy, cfg->cur_byte, rdqs_mid); ++ ddr_info("rdqs_start[%x] rdqs_end[%x]", rdqs_start, rdqs_end); ++ } else { ++ ddr_fatal("PHY[%x] Byte[%x] not find RDQS, restore", ++ cfg->cur_phy, cfg->cur_byte); ++ ddr_training_stat(DDR_ERR_MPR, cfg->cur_phy, cfg->cur_byte, -1); ++ } ++ ++ reg_write(delay | (rdqs_mid << PHY_RDQS_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ddr_phy_cfg_update(cfg->cur_phy); ++ ++ return ((found == DDR_TRUE) ? 0 : -1); ++} ++ ++/* Multi Purpose Register(MPR) */ ++static int ddr_mpr_training(struct ddr_cfg_st *cfg) ++{ ++ int i; ++ int result = 0; ++ unsigned int byte_num = get_byte_num(cfg); ++ unsigned int mr0; ++ unsigned int sfc_cmd; ++ unsigned int base_dmc = cfg->cur_dmc; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ ddr_debug("DDR MPR training"); ++ ++ /* set DDR bust */ ++ if (byte_num == DDR_PHY_BYTE_MAX) { ++ mr0 = (reg_read(base_phy + DDR_PHY_MODEREG01) & ++ DMC_MRS_MASK) & (~DMC_MR0_BL_MASK); ++ sfc_cmd = ((mr0 | DMC_MR0_BL_BUST4) << DMC_SFC_CMD_MRS_BIT) | ++ DMC_CMD_TYPE_LMR; ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, 0x0); ++ } ++ ++ /* precharge all */ ++ ddr_dmc_sfc_cmd(base_dmc, DMC_CMD_TYPE_PRECHARGE_ALL, 0x0, 0x0); ++ ++ /* enable MPR */ ++ ddr_mpr_switch(base_dmc, DDR_TRUE); ++ ++ /* find rdqs */ ++ for (i = 0; i < byte_num; i++) ++ result += ddr_mpr_find_rdqs(cfg); ++ ++ /* find rdq */ ++ if (!result) ++ result = ddr_mpr_find_rdq(cfg); ++ ++ /* disable MPR */ ++ ddr_mpr_switch(base_dmc, DDR_FALSE); ++ ++ /* restore DDR bust */ ++ if (byte_num == DDR_PHY_BYTE_MAX) { ++ mr0 = (reg_read(base_phy + DDR_PHY_MODEREG01) & DMC_MRS_MASK); ++ sfc_cmd = (mr0 << DMC_SFC_CMD_MRS_BIT) | DMC_CMD_TYPE_LMR; ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, 0x0); ++ } ++ ++ return result; ++} ++ ++int ddr_mpr_training_func(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result; ++ ++ /* MPR training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_MPR_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_MPR_MASK); ++ result = ddr_mpr_training(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_mpr_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR MPR training"); ++ return 0; ++} ++#endif /* DDR_MPR_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_pcode_training.c b/drivers/ddr/vendor/default_v2/ddr_pcode_training.c +new file mode 100644 +index 0000000..575a617 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_pcode_training.c +@@ -0,0 +1,132 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __PCODE_TRAINING__ ++#ifdef DDR_PCODE_TRAINING_CONFIG ++/* Set pcode value to register IMPSTATUS and DDR_PHY_IMP_STATUS1 */ ++static void ddr_pcode_set_value(unsigned int base_phy, unsigned int pcode_value) ++{ ++ unsigned int imp_ctrl1; ++ ++ reg_write((reg_read(base_phy + DDR_PHY_IMPSTATUS) & ++ (~(PHY_ZCODE_PDRV_MASK << PHY_ZCODE_PDRV_BIT))) | ++ (pcode_value << PHY_ZCODE_PDRV_BIT), base_phy + DDR_PHY_IMPSTATUS); ++ ddr_debug("cur IMPSTATUS [%x] = [%x]", ++ base_phy + DDR_PHY_IMPSTATUS, reg_read(base_phy + DDR_PHY_IMPSTATUS)); ++ ++ imp_ctrl1 = reg_read(base_phy + DDR_PHY_IMP_CTRL1); ++ /* ac_vddq_cal_en set 0 */ ++ reg_write(imp_ctrl1 & (~(0x1 << PHY_AC_VDDQ_CAL_EN_BIT)), base_phy + DDR_PHY_IMP_CTRL1); ++ ++ reg_write((reg_read(base_phy + DDR_PHY_IMP_STATUS1) & ++ (~(PHY_ACCTL_PDRV_LATCH_MASK << PHY_ACCTL_PDRV_LATCH_BIT))) | ++ (pcode_value << PHY_ACCTL_PDRV_LATCH_BIT), base_phy + DDR_PHY_IMP_STATUS1); ++ ddr_debug("cur IMP_STATUS1 [%x] = [%x]", ++ base_phy + DDR_PHY_IMP_STATUS1, reg_read(base_phy + DDR_PHY_IMP_STATUS1)); ++ ++ /* restore ac_vddq_cal_en */ ++ reg_write(imp_ctrl1, base_phy + DDR_PHY_IMP_CTRL1); ++} ++ ++static int ddr_pcode_trainig_by_phy(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int times = 0; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int pcode_value; ++ unsigned int osc_rpt_vld; ++ unsigned int osc_cnt_rdata; ++ int ddr_freq; ++ ++ /* test start */ ++ reg_write(reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) | PHY_OSC_START_MASK, ++ base_phy + DDR_PHY_CORNER_DETECTOR); ++ ++ do { ++ osc_rpt_vld = (reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) >> ++ PHY_OSC_RPT_VLD) & PHY_OSC_RPT_VLD_MASK; ++ times++; ++ } while ((!osc_rpt_vld) && (times < DDRT_PCODE_WAIT_TIMEOUT)); ++ ++ if (times >= DDRT_PCODE_WAIT_TIMEOUT) { ++ ddr_fatal("IO pcode training wait timeout"); ++ return -1; ++ } ++ ++ osc_cnt_rdata = (reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) >> ++ PHY_OSC_CNT_RDATA_BIT) & PHY_OSC_CNT_RDATA_MASK; ++ ++ /* test stop */ ++ reg_write(reg_read(base_phy + DDR_PHY_CORNER_DETECTOR) & ++ (~PHY_OSC_START_MASK), base_phy + DDR_PHY_CORNER_DETECTOR); ++ ++ ddr_freq = ddr_get_cksel(); ++ ++ /* get pcode_value: a formula based on simulation */ ++ pcode_value = ++ (490960 - (89 * osc_cnt_rdata * ddr_freq) / 300) / 10000; /* y equal (490960 - (89*x*fre)/300)/10000 */ ++ ++ ddr_debug("pcode value[%x]", pcode_value); ++ if (pcode_value < PHY_PCODE_MIN) { ++ pcode_value = PHY_PCODE_MIN; ++ } else if (pcode_value > PHY_PCODE_MAX) { ++ pcode_value = PHY_PCODE_MAX; ++ } ++ ++ /* set pcode value */ ++ ddr_pcode_set_value(base_phy, pcode_value); ++ return 0; ++} ++ ++int ddr_pcode_training(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ int i; ++ ++ if (cfg == NULL) ++ return -1; ++ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ cfg->cur_item = cfg->phy[i].rank[0].item; ++ ++ if (ddr_training_check_bypass(cfg, 1 << (cfg->phy_idx)) != DDR_FALSE) ++ continue; ++ ++ /* pcode training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_PCODE_MASK) != DDR_FALSE) ++ continue; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_PCODE_MASK); ++ result += ddr_pcode_trainig_by_phy(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg); ++ } ++ ++ return result; ++} ++#else ++int ddr_pcode_training(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR pcode training"); ++ return 0; ++} ++#endif +diff --git a/drivers/ddr/vendor/default_v2/ddr_phy_s14.h b/drivers/ddr/vendor/default_v2/ddr_phy_s14.h +new file mode 100644 +index 0000000..a183ec7 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_phy_s14.h +@@ -0,0 +1,578 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_PHY_S14_H ++#define DDR_PHY_S14_H ++ ++/* register offset address */ ++/* base address: DDR_REG_BASE_PHY0 DDR_REG_BASE_PHY1 */ ++/* control the initialization of the PHY */ ++#define DDR_PHY_PHYINITCTRL 0x4 ++#define DDR_PHY_PHYINITSTATUS 0x8 /* Read Data Eye Calibration Error */ ++#define DDR_PHY_CLKGATED 0xc /* This register control the clock gated of PHY */ ++#define DDR_PHY_PLLCTRL 0x18 /* specified the timing paramters for PLL in both address/command, and data block */ ++#define DDR_PHY_IMPSTATUS 0x28 /* This register specify the ZQ calibration result. */ ++#define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ ++#define DDR_PHY_DRAMTIMER1 0x34 /* This register specify the DRAM timing parameters */ ++#define DDR_PHY_TRAINCTRL0 0x48 /* hw training control */ ++#define DDR_PHY_RANKEN 0x4c /* This register specify the training rank control */ ++#define DDR_PHY_MODEREG01 0x64 /* Extend Mode Register 01 */ ++#define DDR_PHY_MODEREG23 0x68 /* Extend Mode Register 23 */ ++/* update delay setting in registers to PHY */ ++#define DDR_PHY_MISC 0x70 ++#define DDR_PHY_PHYCTRL0 0x78 /* Register in this field are connected to PHY interface directly */ ++#define DDR_PHY_DMSEL 0x84 /* DM Swap Selection */ ++#define DDR_PHY_SWTMODE 0xa0 /* S/W training mode */ ++/* issue one DQS pulse from PHY to DRAM */ ++#define DDR_PHY_SWTWLDQS 0xa4 ++#define DDR_PHY_SWTRLT 0xa8 /* S/W training result */ ++/* Host vref. [5:0]range [17:12]refsel */ ++#define DDR_PHY_PHYRSCTRL 0xB0 /* PHY Register Slice Contrl */ ++#define DDR_PHY_VREFTCTRL 0xc0 /* VREF Training Control Register. */ ++#define DDR_PHY_HVRFTCTRL 0xc8 /* Host VREF Training Control Regiser. */ ++#define DDR_PHY_DVRFTCTRL 0xC4 /* DRAM VREF Training */ ++#define DDR_PHY_TRAINCTRL2 0xd4 /* This register control the data training */ ++#define DDR_PHY_TRAINCTRL3 0xdc /* This register control the data training */ ++#define DDR_PHY_MODEREG45 0xe0 /* This register defines the contents of the Mode Register */ ++#define DDR_PHY_MODEREG67 0xe4 /* This register defines the contents of the Mode Register */ ++#define DDR_PHY_TRAINCTRL5 0x118 /* This register control the data training */ ++#define DDR_PHY_ACCMDBDL2 0x128 /* AC command bit delay line setting */ ++#define ddr_phy_acaddrbdl(n) (0x140 + ((n) << 2)) ++#define DDR_PHY_IMP_CTRL1 0x170 /* AC/DX ZQ selection */ ++#define DDR_PHY_IMP_STATUS1 0x174 /* AC ZCAL status */ ++#define DDR_PHY_CATSWAPINDEX 0x01B8 /* CA SWAP index register */ ++#define DDR_PHY_CATSWAPSEL 0x01BC /* CA SWAP select register */ ++#define DDR_PHY_CATCONFIG 0x1C8 /* CA Training Configuration */ ++#define DDR_PHY_PHYDQRESULT 0x1D0 /* SW CA Training DQ result from PHY */ ++#define DDR_PHY_ADDRPHBOUND 0x1D4 /* CA Training addr phase boundary */ ++#define DDR_PHY_SWCATPATTERN_P 0x1D8 /* pattern for positive CK edge */ ++#define DDR_PHY_SWCATPATTERN_N 0x1DC /* pattern for negative CK edge */ ++#define DDR_PHY_MRS_SEQ_PROG 0x1e0 /* Programmed MRS sequence in the DRAM initialization */ ++#define DDR_PHY_TRAINCTRL12 0x1e8 /* TRAINCTRL 12 Register */ ++#define DDR_PHY_TRFC_THRESHOLD1 0x8d0 /* trfc retrain/tracking threshold setting 1 */ ++#define DDR_PHY_TRFC_CTRL 0x8e0 /* specify the trfc retrain/tracking control */ ++ ++/* WR DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++on write path */ ++#define ddr_phy_dxnwdqnbdl0(m, n) (0x210 + ((m) << 10) + ((n) << 7)) ++/* WR DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] */ ++#define ddr_phy_dxnwdqnbdl1(m, n) (0x214 + ((m) << 10) + ((n) << 7)) ++/* WR DM [6:0] the delay value of the bit delay line on DQM */ ++#define ddr_phy_dxnwdqnbdl2(m, n) (0x218 + ((m) << 10) + ((n) << 7)) ++/* RD DQ0-DQ3 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl0(m, n) (0x21C + ((m) << 10) + ((n) << 7)) ++/* RD DQ4-DQ7 [6:0] [14:8] [22:16] [30:24] delay value of the bit delay line ++ on read path */ ++#define ddr_phy_dxnrdqnbdl1(m, n) (0x220 + ((m) << 10) + ((n) << 7)) ++/* [6:0]RD DM */ ++#define ddr_phy_dxnrdqnbdl2(m, n) (0x224 + ((m) << 10) + ((n) << 7)) ++ ++/* [CUSTOM] */ ++#define ddr_phy_dxnoebdl(m, n) (0x228 + ((m) << 10) + ((n) << 7)) ++/* [8:0] rdqs_bdl [24:16]rdqs_cyc. ++phase shift of the Read DQS to create 90 degree delays */ ++#define ddr_phy_dxnrdqsdly(n) (0x22C + ((n) << 7)) ++/* [6:0] the delay value of delay applied on WDQS for write leveling */ ++#define ddr_phy_dxwdqsdly(m, n) (0x230 + ((m) << 10) + ((n) << 7)) ++/* WR DQ phase BIT 12:8 */ ++#define ddr_phy_dxnwdqdly(m, n) (0x234 + ((m) << 10) + ((n) << 7)) ++/* DXNWLSL:This reister is used to control if PHY controller add extra system latency */ ++#define ddr_phy_dxnwlsl(m, n) (0x238 + ((m) << 10) + ((n) << 7)) ++/* [CUSTOM] rdqs gating */ ++#define ddr_phy_dxnrdqsgdly(m, n) (0x240 + ((m) << 10) + ((n) << 7)) ++/* read boundary right 8:0 left 24:16 */ ++#define ddr_phy_dxnrdbound(n) (0x250 + ((n) << 7)) ++/* write boundary right 4:0 left 20:16 */ ++#define ddr_phy_dxnwdbound(n) (0x254 + ((n) << 7)) ++/* [5:0] DRAM VREF(DQ) training result */ ++#define ddr_phy_dvreft_status(n) (0x270 + ((n) << 7)) ++/* [4:0] Host PHY VREF(DQ) training result */ ++#define ddr_phy_hvreft_status(m, n) (0x274 + ((m) << 10) + ((n) << 7)) ++ ++/* DDRPHY AC static register */ ++/* #define DDR_PHY_CORNER_DETECTOR 0x104C cfg of corner detector */ ++#define DDR_PHY_ACIOCTL 0x1018 /* IO control register */ ++#define DDR_PHY_ACPHYCTL4 0x1064 /* AC block PHY control register */ ++#define DDR_PHY_ACPHYCTL7 0x1070 ++#define DDR_PHY_ACIOCTL21 0x10A4 ++#define DDR_PHY_AC_GATED_BYPASS 0x10A4 /* bypass clock gated function */ ++#define ddr_phy_dxphyctrl2(q) (0x1000 + 0x214 + ((q) << 8)) ++/* Data block PHY debug/miscellaneous control register. dxctl_pre_margin_code [24:22] */ ++#define dx_dxnmiscctrl3(p) (0x1254 + ((p) << 7)) ++ ++#define DDR_VREF_HOST_VAL_MAX 0x3f /* 78.75%*VDDIO */ ++#define DDR_VREF_HOST_VAL_MIN 0x0 /* 40.00%*VDDIO */ ++ ++/* register mask */ ++#define PHY_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_WDQ_PHASE_MASK 0x3f /* [13:8] */ ++#define PHY_WDQS_PHASE_MASK 0xf /* [11:8] */ ++#define PHY_WDQS_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_WDM_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_RDQS_BDL_MASK 0x1ff /* [CUSTOM] [8:0] rdqsbdl */ ++#define PHY_RDQSG_PHASE_MASK 0xff /* [16:9] rdqsgphase */ ++#define PHY_RDM_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_RDQS_CYC_MASK 0x1ff /* [24:16]Read DQS Clock Phase Select */ ++/* hardware gate training result */ ++#define PHY_INITSTATUS_GT_MASK 0x20 ++#define PHY_SWTRLT_WL_MASK 0xf ++#define PHY_SWTRLT_GATE_MASK 0xf ++#define PHY_PHYINITCTRL_MASK 0x1 /* [15:0] all stat */ ++/* Read Data Eye Calibration Error */ ++#define PHY_PHYINITSTATUS_RDET_ERR 0x100 ++#define PHY_ACPHY_DCLK_MASK 0x7 /* cp1p_dclk0 mask */ ++#define PHY_ACPHY_DRAMCLK_MASK 0x1 /* halft_dramclk0 mask */ ++#define PHY_VRFTRES_DVREF_MASK 0x3f /* [5:0] */ ++#define PHY_VRFTRES_HVREF_MASK 0x3f /* [5:0] */ ++#define PHY_VRFTRES_RXDIFFCAL_MASK 0xf /* [27:24] */ ++#define PHY_ADDRPH_MASK 0x3f /* [21:16] */ ++#define PHY_ACADDR_BDL_MASK 0x7f /* [6:0] */ ++#define PHY_CATSWAPSEL_BIT_MASK 0xff ++#define PHY_CAT_PATTERN_MASK 0xfff ++#define PHY_TRAINCTRL0_MASK 0xf /* [3:0] */ ++#define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ ++#define PHY_ACIOCTL21_MASK 0x7 /* [14:12], [10:8] */ ++#define PHY_VREFS_MRS_ENTER_MASK 0x1 /* [31] */ ++#define PHY_DXNRDBOUND_MASK 0x3ff /* [25:16], [9:0] */ ++#define PHY_OSC_START_MASK 0x1 /* [0] */ ++#define PHY_OSC_RPT_VLD_MASK 0x1 /* [15] */ ++#define PHY_OSC_CNT_RDATA_MASK 0xffff /* [31:16] */ ++#define PHY_ZCODE_PDRV_MASK 0x3f /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_MASK 0x3f /* [29:24] */ ++#define PHY_IMP_STATUS1_USED_MASK 0xffff /* [15:0]/[31:16] */ ++#define PHY_WLSL_MASK 0x3 /* [17:16] valid value:0/1/2 */ ++#define PHY_CLKGATED_MASK 0x80007fff /* bit[31], bit[14:0] */ ++#define PHY_PLL_PWDN_MASK 0x7 /* bit[2:0] pll_pwdn */ ++#define PHY_DFICLK_RATIO_MASK 0x3 /* [1:0] dficlk_ratio */ ++#define PHY_WL_FALLEDGE_BDL_JSTEP_R_MASK 0x7f /* [16:10] wl_falledge_bdl_jstep_r */ ++#define PHY_MISC_ADDR_DELAY_MASK 0x1 /* [18]Add 1-T delay on address/command */ ++#define PHY_MISC_CFG_WL_MASK 0xff /* [7:0]PHY write latency */ ++#define PHY_MISC_CFG_RL_MASK 0xff /* [15:8]PHY read latency */ ++#define PHY_DMSEL_TPHY_WRDATA_MASK 0x1 /* bit[8]tphy_wrdata */ ++#define PHY_TRFC_MPC_CMD_DLY_MASK 0x3 /* bit[2:1]trfc mpc command delay */ ++#define PHY_AC_IOCTL_TX_MODE_MASK 0x3 /* bit[13:12]ac_ioctl_tx_mode */ ++#define DDR_PHY_T_MOD_MASK 0x1f /* bit[8:4]t_mod */ ++#define DDR_PHY_RANKEN_MASK 0xffff /* rank_en:bit[15:0] */ ++ ++/* register bit */ ++#define PHY_MISC_CFG_WL_BIT 0 /* [7:0]PHY write latency */ ++#define PHY_MISC_CFG_RL_BIT 8 /* [15:8]PHY read latency */ ++#define PHY_MISC_ADDR_DELAY_BIT 18 /* Add 1-T delay on address/command */ ++#define PHY_MISC_UPDATE_BIT 19 /* [CUSTOM] delay config update bit */ ++#define PHY_PHYCONN_RST_BIT 15 /* issue reset signal to PHY counter */ ++#define PHY_RST_BIT 13 /* bit[13] issue reset signal to PHY */ ++#define PHY_RDQSG_PHASE_BIT 9 /* [CUSTOM] */ ++#define PHY_RDQSG_TX_BDL_BIT 24 /* [30:24] rdqsgtxbdl */ ++#define PHY_WDQS_PHASE_BIT 8 ++#define PHY_WDQS_BDL_BIT 0 ++#define PHY_WDQ_PHASE_BIT 8 ++#define PHY_WDM_BDL_BIT 0 ++/* [22:16] Write DQS Output Enable Delay Control */ ++#define PHY_WDQSOE_BDL_BIT 16 ++#define PHY_OEN_BDL_BIT 0 ++/* Mode Register 1. Defines the MR3/MR9 of the mode register */ ++#define PHY_MODEREG01_MR1_BIT 16 ++/* Bit delay line setting of CS1 */ ++#define PHY_ACCMD_CS0_BIT 0 ++#define PHY_ACCMD_CS1_BIT 16 ++#define PHY_ACPHY_DCLK0_BIT 6 /* [8:6] cp1p_dclk0 */ ++#define PHY_ACPHY_DCLK1_BIT 9 /* [11:9] ck2p_dclk1 */ ++#define PHY_ACPHY_DRAMCLK0_BIT 25 /* [25] halft_dramclk0 */ ++#define PHY_ACPHY_DRAMCLK1_BIT 24 /* [24] halft_dramclk1 */ ++#define PHY_ACPHY_DRAMCLK_EXT_BIT 3 /* [3] halft_dramclk0 */ ++#define PHY_SWTMODE_SW_GTMODE_BIT 1 /* [1] SW gate training */ ++#define PHY_ACADDRBDL_ADDR1_BIT 16 /* [16] ADDR1 delay line */ ++#define PHY_VRFTRES_RXDIFFCAL_BIT 24 /* [27:24] */ ++#define PHY_BYPASS_CK0_BIT 16 /* [2]ck_ioctl_DUTY_EN reserver */ ++#define PHY_BYPASS_CK1_BIT 17 /* [3]ck1_ioctl_DUTY_EN reserver */ ++#define PHY_ACIOCTL21_CK0_BIT 8 /* [10:8] */ ++#define PHY_ACIOCTL21_CK1_BIT 12 /* [14:12] */ ++#define PHY_ACIOCTL21_CTL0_BIT 11 /* [11] */ ++#define PHY_ACIOCTL21_CTL1_BIT 15 /* [15] */ ++#define PHY_DXNRDBOUND_RIGHT_BIT 0 /* [9:0] */ ++#define PHY_DXNRDBOUND_LEFT_BIT 16 /* [25:16] */ ++#define PHY_VREFS_MRS_ENTER_BIT 31 /* [31] */ ++#define PHY_HRXDIFFCAL_EN_BIT 31 /* [31] */ ++#define PHY_OSC_RPT_VLD 15 /* [15] */ ++#define PHY_OSC_CNT_RDATA_BIT 16 /* [31:16] */ ++#define PHY_ZCODE_PDRV_BIT 16 /* [21:16] */ ++#define PHY_ACCTL_PDRV_LATCH_BIT 24 /* [29:24] */ ++#define PHY_AC_VDDQ_CAL_EN_BIT 8 /* [8] AC ZQ calibration enable */ ++#define PHY_WLSL_BIT 16 /* [17:16] wlsl */ ++#define PHY_IMP_STATUS1_USED_BIT 16 /* [15:0]/[31:16] */ ++#define PHY_RDET_METHOD_SEL_BIT 31 /* [31] rdet_method_sel */ ++#define PHY_NOPRE4WRDET_BIT 15 /* [15] nopre4wrdet */ ++#define PHY_WDET_METROD_SEL_BIT 23 /* [23] wdet_method_sel */ ++#define PHY_TRAINCTL_NOPOSTPRE_BIT 19 /* [19] trainctl_nopostpre */ ++#define PHY_PLL_PWDN_BIT 0 /* bit[2:0] pll_pwdn */ ++#define PHY_DXCTL_REG_TX_PHASE_RNK_BIT 14 /* dxctl_reg_tx_phase_rnk */ ++#define PHY_DFICLK_RATIO_BIT 0 /* [1:0] dficlk_ratio */ ++#define PHY_SWTRLT_GATE_BIT 8 /* [15:8] gt_result */ ++#define PHY_WL_FALLEDGE_BDL_JSTEP_R_BIT 10 /* [16:10] wl_falledge_bdl_jstep_r */ ++#define PHY_DMSEL_TPHY_WRDATA_BIT 8 /* bit[8]tphy_wrdata */ ++#define PHY_TRFC_MPC_CMD_DLY_BIT 1 /* bit[2:1]trfc mpc command delay */ ++#define PHY_AC_IOCTL_TX_MODE_BIT 12 /* bit[13:12]ac_ioctl_tx_mode */ ++#define DDR_PHY_T_MOD_BIT 4 /* bit[8:4]t_mod */ ++#define PHY_MODEREG67_LP4_FSPWR_BIT 6 /* bit[6] FSPWR */ ++ ++/* BDL register bit */ ++#define PHY_BDL_DQ_BIT 0 ++#define PHY_BDL_DQ0_BIT 0 ++#define PHY_BDL_DQ1_BIT 8 ++#define PHY_BDL_DQ2_BIT 16 ++#define PHY_BDL_DQ3_BIT 24 ++#define PHY_RDM_BDL_BIT 0 ++#define PHY_RDQS_BDL_BIT 0 ++#define PHY_RDQS_CYC_BIT 16 ++ ++/* value */ ++#define PHY_PHYINITCTRL_DVREFT_SYNC 0x40000 /* DRAM VREF Synchronize */ ++/* hw training item defined in PHYINITCTRL */ ++#define PHY_PHYINITCTRL_CTL_CKE_BYPASS (1 << 31) /* PACK's CKE bypass function enable. */ ++#define PHY_PHYINITCTRL_PIC_PHYUPD_REQ (1 << 30) /* PACK's DFI PHY UPDATAE request by SW. */ ++#define PHY_PHYINITCTRL_PIC_TDQSST (1 << 28) /* TDQSS training Enable. */ ++#define PHY_PHYINITCTRL_CFG_LPBK_COMPST_EN (1 << 27) /* RDQS/CK loopback delay compensate enable. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_SFT (1 << 26) /* Update delay line(switch op_sel) during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_WR (1 << 25) /* Retraining with MPC write during tRFC. */ ++#define PHY_PHYINITCTRL_PIC_REFRET_RD (1 << 24) /* Retraining with MPC read during tRFC. */ ++#define PHY_PHYINITCTRL_JTMT_EN (1 << 23) /* PLL Jitter Meter Enable. */ ++#define PHY_PHYINITCTRL_CST_EN (1 << 22) /* HW CS Traninig Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFS_EN (1 << 21) /* DRAM VREF(AC) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_ACHVREFT_EN (1 << 20) /* Host VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_ACDVREFT_EN (1 << 19) /* DRAM VREF(AC) Training Enable. */ ++#define PHY_PHYINITCTRL_DXDVREFS_EN (1 << 18) /* DRAM VREF(DQ) Synchronize Operations. */ ++#define PHY_PHYINITCTRL_HVREFT_EN (1 << 17) /* Host VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_DVREFT_EN (1 << 16) /* DRAM VREF(DQ) Training Enable. */ ++#define PHY_PHYINITCTRL_PHYCONN_RST (1 << 15) /* PHY Counter Reset. */ ++#define PHY_PHYINITCTRL_PACK_RST (1 << 14) /* PACK Reset. */ ++#define PHY_PHYINITCTRL_PHY_RST (1 << 13) /* PHY Reset. */ ++#define PHY_PHYINITCTRL_DRAM_RST (1 << 12) /* DRAM Reset. */ ++#define PHY_PHYINITCTRL_CAT_EN (1 << 11) /* HW CA Traninig Enable. */ ++#define PHY_PHYINITCTRL_DRAM_INIT_EN (1 << 10) /* DRAM Initialization Enable. */ ++#define PHY_PHYINITCTRL_WDET_EN (1 << 9) /* Write Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_RDET_EN (1 << 8) /* Read Data Eye Training Enable. */ ++#define PHY_PHYINITCTRL_WL2_EN (1 << 7) /* Second Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_GDST_EN (1 << 6) /* PHY Read Data Latch Train Enable. */ ++#define PHY_PHYINITCTRL_GT_EN (1 << 5) /* Gate Training Enable. */ ++#define PHY_PHYINITCTRL_WL_EN (1 << 4) /* Write Leveling Enable. */ ++#define PHY_PHYINITCTRL_ZCAL_EN (1 << 3) /* Impedance Calibration Enable. */ ++#define PHY_PHYINITCTRL_DLYMEAS_EN (1 << 2) /* Delay Measurement Enable. */ ++#define PHY_PHYINITCTRL_PLL_INIT_EN (1 << 1) /* PLL Initialization Enable. */ ++#define PHY_PHYINITCTRL_INIT_EN (1 << 0) /* PHY Initialization Enable. */ ++ ++#define PHY_HW_GP_PHY_RESET (PHY_PHYINITCTRL_PHY_RST) ++#define PHY_HW_GP_CNT_RESET_START (PHY_PHYINITCTRL_PHYCONN_RST) ++#define PHY_HW_GP_PLL (PHY_PHYINITCTRL_PLL_INIT_EN | PHY_PHYINITCTRL_ZCAL_EN | PHY_PHYINITCTRL_DLYMEAS_EN) ++#define PHY_HW_GP_DRAM_RESET (PHY_PHYINITCTRL_DRAM_RST | PHY_PHYINITCTRL_DRAM_INIT_EN) ++#define PHY_HW_GP_VREF_AC (PHY_PHYINITCTRL_ACDVREFS_EN) ++#define PHY_HW_GP_CS (PHY_PHYINITCTRL_CST_EN) ++#define PHY_HW_GP_VREF_DQ (PHY_PHYINITCTRL_DVREFT_SYNC) ++#define PHY_HW_GP_NORMAL (PHY_PHYINITCTRL_WL_EN | \ ++ PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN | \ ++ PHY_PHYINITCTRL_DVREFT_EN | \ ++ PHY_PHYINITCTRL_HVREFT_EN | \ ++ PHY_PHYINITCTRL_PIC_TDQSST) ++#define PHY_HW_GP_NORMAL_RANK1 (PHY_PHYINITCTRL_GT_EN | \ ++ PHY_PHYINITCTRL_GDST_EN | \ ++ PHY_PHYINITCTRL_WL2_EN | \ ++ PHY_PHYINITCTRL_RDET_EN | \ ++ PHY_PHYINITCTRL_WDET_EN) ++#define PHY_HW_GP_CNT_RESET_END (PHY_PHYINITCTRL_PHYCONN_RST) ++ ++#define PHY_PHYINITSTATUS_ZCAL_ERROR (1 << 3) /* Impedance Calibration Error. */ ++ ++/* RDQS range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_RDQS_MIDDLE_VAL 0x40 ++/* DQ range[0, 0x7f], middle value is 0x40, but it affected by ++ temperature, so middle value change to 0x30 */ ++#define PHY_DQ_MIDDLE_VAL 0x40404040 ++#define PHY_MISC_SCRAMB_DIS 0xfffeffff /* scrambler disable */ ++#define PHY_GATE_BDL_MAX 0xfe /* [6:0]rdqsg_bdl + [22:16]rdqsgtxbdl */ ++#define PHY_DVRFTCTRL_PDAEN_EN 0x80000000 /* pda enable */ ++/* [5] two cycle on address or command.(2T timing) */ ++#define PHY_DRAMCFG_MA2T 0x20 ++ ++#define PHY_DRAMCFG_TYPE_DDR1 0x0 /* [2:0] 000 DDR1 */ ++#define PHY_DRAMCFG_TYPE_DDR2 0x1 /* [2:0] 001 DDR2 */ ++#define PHY_DRAMCFG_TYPE_DDR3 0x2 /* [2:0] 010 DDR3 */ ++#define PHY_DRAMCFG_TYPE_DDR3L 0x3 /* [2:0] 011 DDR3L */ ++#define PHY_DRAMCFG_TYPE_LPDDR1 0x4 /* [2:0] 100 LPDDR1 */ ++#define PHY_DRAMCFG_TYPE_LPDDR2 0x5 /* [2:0] 101 LPDDR2 */ ++#define PHY_DRAMCFG_TYPE_LPDDR3 0x5 /* [2:0] 101 LPDDR3 */ ++#define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ ++#define PHY_DRAMCFG_TYPE_DDR4 0xa /* [3] 1010 DDR4 */ ++ ++#define DDR_PHY_LPDDR4X_MODE 0x0 /* LPDDR4X */ ++ ++#define PHY_DMSEL_SWAPDFIBYTE 0xf8ffffff /* [24:26] No Swap */ ++ ++/* AC_DDRPHY_GATED_BYPASS */ ++#define PHY_CK_IOCTL_DUTY_EN 0x0 /* enable ck_ioctl_DUTY_EN_v */ ++#define PHY_CK1_IOCTL_DUTY_EN 0x0 /* enable ck1_ioctl_DUTY_EN_v */ ++/* CK AC_IOCTL22 */ ++#define DDR_DUTY_NUM 4 /* CK duty number */ ++#define DDR_CK_MAX_NUM 2 /* DDR CK max number */ ++#define DDR_CK_NUM_LPDDR4 2 /* LPDDR4 CK number */ ++#define DDR_CK_NUM_NONLPDDR4 1 /* NONLPDDR4 CK number */ ++#define DDR_DUTY_CTL_NUM 2 /* CK duty has two control direction */ ++/* CK duty step. */ ++#define PHY_AC_IOCTL21_STEP 1 ++#define DDR_DCC_CTL_WIN_DIFF 2 ++ ++#define PHY_CLK_GATED_CLOSE 0x0 ++#define PHY_CLK_GATED_OPEN 0x80007fff ++#define PHY_PLL_POWER_DOWN 0x7 ++#define PHY_PLL_POWER_UP 0x0 ++ ++/* other */ ++#define PHY_RDQSG_PHASE_STEP 2 /* gate training phase step. */ ++#define PHY_GATE_PHASE_MARGIN 8 /* gate phase margin */ ++#define PHY_DQ_BDL_LEVEL 128 /* [CUSTOM] DQ BDL range */ ++#define PHY_DQ_BDL_MIDDLE 64 /* special middle DQ BDL value */ ++#define PHY_RDQSG_PHASE_MAX 0xff /* RDQSG phase max value */ ++#define PHY_ACPHY_CLK_MAX 0xf /* halft_dramclk0 + cp1p_dclk0 */ ++#define PHY_PCODE_MIN 0x14 ++#define PHY_PCODE_MAX 0x24 ++#define PHY_WLSL_MAX 0x2 /* valid wlsl value:0/1/2 */ ++#define PHY_WDQPHASE_REG_MAX 0x3e /* 0x3f - 1 */ ++#define PHY_WDQPHASE_NUM_T 12 /* wdqphase num of 1T */ ++#define PHY_WDQPHASE_REG_NUM_T 16 /* wdqphase reg num of 1T */ ++#define PHY_WDQSPHASE_REG_MIN 0x0 ++#define PHY_WDQSPHASE_REG_MAX 0xe /* 0xf - 1 */ ++#define PHY_WDQSPHASE_NUM_T 12 /* wdqsphase num of 1T */ ++#define PHY_WDQSPHASE_REG_NUM_T 16 /* wdqsphase reg num of 1T */ ++ ++/* ++ * DDR_BDL_PHASE_REL Calculation Method: ++ * 1. Calculation How many picosecond to one phase. ++ * PICOSECOND : 1 second is (1000 * 1000 * 1000) picosecond ++ * WAVE : 1 cycle is 2 ++ * RATE : DDR rate is 1600 Mbps, is (1600 * 1000) bps ++ * PHASE : 1 wave is 12 phase ++ * phase equal (((PICOSECOND * WAVE) / RATE) / PHASE) ++ * = (((1000 * 1000 * 1000 * 2) / (1600 * 1000)) / 12) ++ * = 104.17 ps. ++ * 2. Calculation How many bdl to one phase. ++ * one BDL is 6 ps. ++ * result = phase/bdl = 104.17 / 6 = 17.36 approximately equal to 17 ~= 16 ++ * 3. 16 = 1 << 4, so the relation is 4. ++ */ ++#ifndef DDR_BDL_PHASE_TRANSFORM ++/* [CUSTOM] one Phase equal how much BDL. 1 phase = 16 bdl */ ++#define DDR_BDL_PHASE_TRANSFORM 16 ++#endif ++#ifndef DDR_BDL_PHASE_REL ++/* [CUSTOM] relation between BDL and Phase. 1 phase = 16 bdl, 16 = 1 << 4 */ ++#define DDR_BDL_PHASE_REL 4 ++#endif ++ ++#define ddr_variable_declare(var) \ ++ unsigned int var; ++ ++#define ddr_vref_get_host_max(rank, val) do { \ ++ if ((rank) == 0) \ ++ (val) = PHY_VRFTRES_HVREF_MASK; \ ++ else \ ++ (val) = PHY_VRFTRES_RXDIFFCAL_MASK; \ ++} while (0) ++ ++#define ddr_phy_vref_host_set(base_phy, rank, bytenum, byte_index, val) \ ++ ddr_phy_vref_host_set_process(base_phy, rank, bytenum, byte_index, val) ++ ++#define ddr_phy_vref_host_get(base_phy, rank, byte_index, val) do { \ ++ if ((rank) == 0) { \ ++ (val) = reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) & \ ++ PHY_VRFTRES_HVREF_MASK; \ ++ } else { \ ++ (val) = (reg_read((base_phy) + ddr_phy_hvreft_status(rank, byte_index)) >> PHY_VRFTRES_RXDIFFCAL_BIT) & \ ++ PHY_VRFTRES_RXDIFFCAL_MASK; \ ++ } \ ++} while (0) ++ ++#define DDR_PHY_VREF_HOST_DISPLAY \ ++ {0, 0, ddr_phy_hvreft_status(0, 0), 0, "Host Vref Byte0"}, \ ++ {0, 1, ddr_phy_hvreft_status(0, 1), 0, "Host Vref Byte1"}, \ ++ {0, 2, ddr_phy_hvreft_status(0, 2), 0, "Host Vref Byte2"}, \ ++ {0, 3, ddr_phy_hvreft_status(0, 3), 0, "Host Vref Byte3"}, ++ ++#define DDR_PHY_VREF_HOST_DISPLAY_RANK1 \ ++ {1, 0, ddr_phy_hvreft_status(1, 0), 0, "Host Vref Byte0"}, \ ++ {1, 1, ddr_phy_hvreft_status(1, 1), 0, "Host Vref Byte1"}, \ ++ {1, 2, ddr_phy_hvreft_status(1, 2), 0, "Host Vref Byte2"}, \ ++ {1, 3, ddr_phy_hvreft_status(1, 3), 0, "Host Vref Byte3"}, ++ ++#define ddr_phy_vref_host_display_cmd(base_phy, rank, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] Host Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_hvreft_status(rank, _i), \ ++ reg_read((base_phy) + ddr_phy_hvreft_status(rank, _i)), _i); \ ++ } \ ++} while (0) ++ ++/* DRAM vref operations */ ++#define ddr_phy_vref_dram_set(base_phy, val, byte_index) \ ++ ddr_vref_dram_set_process(base_phy, val, byte_index) ++ ++#define ddr_phy_vref_dram_get(base_phy, val, byte_index) do { \ ++ (val) = reg_read((base_phy) + ddr_phy_dvreft_status(byte_index)) & \ ++ PHY_VRFTRES_DVREF_MASK; \ ++} while (0) ++ ++#define DDR_PHY_VREF_DRAM_DISPLAY \ ++ {0, 0, ddr_phy_dvreft_status(0), 0, "DRAM Vref Byte0"}, \ ++ {0, 1, ddr_phy_dvreft_status(1), 0, "DRAM Vref Byte1"}, \ ++ {0, 2, ddr_phy_dvreft_status(2), 0, "DRAM Vref Byte2"}, \ ++ {0, 3, ddr_phy_dvreft_status(3), 0, "DRAM Vref Byte3"}, ++ ++#define ddr_phy_vref_dram_display_cmd(base_phy, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] DRAM Vref Byte(%x)", \ ++ (base_phy) + ddr_phy_dvreft_status(_i), \ ++ reg_read((base_phy) + ddr_phy_dvreft_status(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* Dx dpmc operations */ ++#define DDR_DX_DPMC_DISPLAY \ ++ {0, 0, dx_dxnmiscctrl3(0), 0, "Dpmc Byte0"}, \ ++ {0, 1, dx_dxnmiscctrl3(1), 0, "Dpmc Byte1"}, \ ++ {0, 2, dx_dxnmiscctrl3(2), 0, "Dpmc Byte2"}, \ ++ {0, 3, dx_dxnmiscctrl3(3), 0, "Dpmc Byte3"}, ++ ++#define ddr_dx_dpmc_display_cmd(base_phy, byte_num) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < (byte_num); _i++) { \ ++ ddr_info("[%x = %x] Dpmc Byte(%x)", \ ++ (base_phy) + dx_dxnmiscctrl3(_i), \ ++ reg_read((base_phy) + dx_dxnmiscctrl3(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* phy t28 not support DCC training */ ++#define DDR_PHY_DCC_DISPLAY \ ++ {0, 0, DDR_PHY_ACIOCTL21, 0, "CK DUTY"}, ++ ++#define ddr_phy_dcc_display_cmd(base_phy) do { \ ++ unsigned int val; \ ++ val = reg_read((base_phy) + DDR_PHY_ACIOCTL21); \ ++ val = reg_read((base_phy) + DDR_PHY_ACIOCTL21); \ ++ ddr_info("[%x = %x] DCC duty", \ ++ (base_phy) + DDR_PHY_ACIOCTL21, val); \ ++} while (0) ++ ++/* lowpower ddr ca operations */ ++#define DDR_PHY_ADDRPH_DISPLAY \ ++ {0, 0, DDR_PHY_ADDRPHBOUND, 0, "CA Phase"}, ++ ++#define DDR_PHY_ADDRBDL_DISPLAY \ ++ {0, 0, ddr_phy_acaddrbdl(0), 0, "CA BDL(0)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(1), 0, "CA BDL(1)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(2), 0, "CA BDL(2)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(3), 0, "CA BDL(3)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(4), 0, "CA BDL(4)"}, \ ++ {0, 0, ddr_phy_acaddrbdl(6), 0, "CA BDL(5)"}, ++ ++#define ddr_phy_addrph_display_cmd(base_phy) do { \ ++ ddr_info("[%x = %x] CA Phase", (base_phy) + DDR_PHY_ADDRPHBOUND, \ ++ reg_read((base_phy) + DDR_PHY_ADDRPHBOUND)); \ ++} while (0) ++ ++#define ddr_phy_addrbdl_display_cmd(base_phy) do { \ ++ unsigned int _i; \ ++ for (_i = 0; _i < DDR_PHY_CA_REG_MAX; _i++) { \ ++ ddr_info("[%x = %x] ACADDRBDL(%x)", \ ++ (base_phy) + ddr_phy_acaddrbdl(_i), \ ++ reg_read((base_phy) + ddr_phy_acaddrbdl(_i)), _i); \ ++ } \ ++} while (0) ++ ++/* PHY t28 DDR4 RDQS synchronize to RDM */ ++#define ddr_phy_rdqs_sync_rdm(cfg, val) \ ++ ddr_rdqs_sync(cfg, val) ++ ++/* dqs swap */ ++#define ddr_dqsswap_save_func(swapdfibyte_en, base_phy) do { \ ++ (swapdfibyte_en) = reg_read((base_phy) + DDR_PHY_DMSEL); \ ++ reg_write((swapdfibyte_en) & PHY_DMSEL_SWAPDFIBYTE, \ ++ (base_phy) + DDR_PHY_DMSEL); \ ++} while (0) ++ ++#define ddr_dqsswap_restore_func(swapdfibyte_en, base_phy) \ ++ reg_write(swapdfibyte_en, (base_phy) + DDR_PHY_DMSEL); ++ ++#define ddr_phy_switch_rank(base_phy, val) do { \ ++ reg_write((reg_read((base_phy) + DDR_PHY_TRAINCTRL0) & (~PHY_TRAINCTRL0_MASK)) | (val), \ ++ (base_phy) + DDR_PHY_TRAINCTRL0); \ ++ reg_write((reg_read((base_phy) + DDR_PHY_HVRFTCTRL) & (~(0x1 << PHY_HRXDIFFCAL_EN_BIT))) | \ ++ ((val) << PHY_HRXDIFFCAL_EN_BIT), (base_phy) + DDR_PHY_HVRFTCTRL); \ ++} while (0) ++ ++/* Define the union u_phy_catconfig */ ++union u_phy_catconfig { ++ /* Define the struct bits */ ++ struct { ++ unsigned int ca_samp_num_bdl : 4; /* [3:0] */ ++ unsigned int ca_samp_num_ph : 4; /* [7:4] */ ++ unsigned int ca_trysamp_num : 4; /* [11:8] */ ++ unsigned int cat_rb_backtap : 4; /* [15:12] */ ++ unsigned int reserved : 1; /* [16] */ ++ unsigned int cat_openeye_en : 1; /* [17] */ ++ unsigned int cat_cat_phydq_sel : 1; /* [18] */ ++ unsigned int cat_restore_en : 1; /* [19] */ ++ unsigned int cat_lb_backtap : 4; /* [23:20] */ ++ unsigned int sw_cat_mrw42 : 1; /* [24] */ ++ unsigned int sw_cat_mrw48 : 1; /* [25] */ ++ unsigned int sw_cat_mrw41 : 1; /* [26] */ ++ unsigned int sw_cat_strobe : 1; /* [27] */ ++ unsigned int sw_cat_cke_high : 1; /* [28] */ ++ unsigned int sw_cat_cke_low : 1; /* [29] */ ++ unsigned int sw_cat_dqvalid : 1; /* [30] */ ++ unsigned int sw_cat_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++ ++/* Define the union u_phy_addrphbound */ ++union u_phy_addrphbound { ++ /* Define the struct bits */ ++ struct { ++ unsigned int addrph_a_right : 5; /* [4:0] */ ++ unsigned int reserved0 : 3; /* [7:5] */ ++ unsigned int addrph_a_left : 5; /* [12:8] */ ++ unsigned int reserved1 : 3; /* [15:13] */ ++ unsigned int addrph_a : 5; /* [20:16] */ ++ unsigned int reserved2 : 3; /* [23:21] */ ++ unsigned int addrph_a_ori : 5; /* [28:24] */ ++ unsigned int reserved3 : 3; /* [31:29] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++}; ++#endif /* DDR_PHY_S14_H */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_training_boot.c b/drivers/ddr/vendor/default_v2/ddr_training_boot.c +new file mode 100644 +index 0000000..2be6c61 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_training_boot.c +@@ -0,0 +1,231 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++#include "ddr_interface.h" ++ ++/* Save DDR tarining result */ ++void ddr_result_data_save(struct ddr_cfg_st *cfg, const struct training_data *training) ++{ ++ /* nothing to do when ddr training on power up */ ++} ++ ++void ddr_lpca_data_save(struct ddr_cfg_st *cfg, const struct ca_data_st *data) ++{ ++ /* nothing to do when ddr training on power up */ ++} ++ ++/* Get DDRT test address */ ++unsigned int ddr_ddrt_get_test_addr(void) ++{ ++ return DDRT_CFG_TEST_ADDR_BOOT; ++} ++ ++#ifdef DDR_TRAINING_UART_CONFIG ++#ifdef DDR_TRAINING_MINI_LOG_CONFIG ++/* Display DDR training error when boot */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ uart_early_putc('E'); ++ uart_early_put_hex(mask); ++ uart_early_putc('P'); ++ uart_early_put_hex(phy); ++ uart_early_putc('B'); ++ uart_early_put_hex(byte); ++ uart_early_putc('D'); ++ uart_early_put_hex(dq); ++} ++void ddr_training_start(void) ++{ ++ uart_early_putc('D'); ++ uart_early_putc('D'); ++ uart_early_putc('R'); ++} ++void ddr_training_suc(void) ++{ ++ uart_early_putc('S'); ++} ++#else ++/* Define string to print */ ++void ddr_training_local_str(void) ++{ ++ asm volatile( ++ "str_wl:\n\t" ++ ".asciz \"WL\"\n\t" ++ ".align 2\n\t" ++ ++ "str_hwg:\n\t" ++ ".asciz \"HWG\"\n\t" ++ ".align 2\n\t" ++ ++ "str_gate:\n\t" ++ ".asciz \"Gate\"\n\t" ++ ".align 2\n\t" ++ ++ "str_ddrt:\n\t" ++ ".asciz \"DDRT\"\n\t" ++ ".align 2\n\t" ++ ++ "str_hwrd:\n\t" ++ ".asciz \"HWRD\"\n\t" ++ ".align 2\n\t" ++ ++ "str_mpr:\n\t" ++ ".asciz \"MPR\"\n\t" ++ ".align 2\n\t" ++ ++ "str_dataeye:\n\t" ++ ".asciz \"Dataeye\"\n\t" ++ ".align 2\n\t" ++ ++ "str_lpca:\n\t" ++ ".asciz \"LPCA\"\n\t" ++ ".align 2\n\t" ++ ++ "str_err:\n\t" ++ ".asciz \" Err:\"\n\t" ++ ".align 2\n\t" ++ ++ "str_phy:\n\t" ++ ".asciz \"Phy\"\n\t" ++ ".align 2\n\t" ++ ++ "str_byte:\n\t" ++ ".asciz \"Byte\"\n\t" ++ ".align 2\n\t" ++ ++ "str_dq:\n\t" ++ ".asciz \"DQ\"\n\t" ++ ".align 2\n\t" ++ ++ "str_ddrtr_start:\n\t" ++ ".asciz \"\r\\nDDRTR \"\n\t" ++ ".align 2\n\t" ++ ++ "str_ddrtr_suc:\n\t" ++ ".asciz \"Suc\"\n\t" ++ ".align 2\n\t" ++ ); ++} ++ ++/* Display DDR training error when boot */ ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ uart_early_putc('\r'); ++ uart_early_putc('\n'); ++ /* error type */ ++ switch (mask) { ++ case DDR_ERR_WL: ++ asm volatile("adr r0, str_wl\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_HW_GATING: ++ asm volatile("adr r0, str_hwg\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_GATING: ++ asm volatile("adr r0, str_gate\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_DDRT_TIME_OUT: ++ asm volatile("adr r0, str_ddrt\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_HW_RD_DATAEYE: ++ asm volatile("adr r0, str_hwrd\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_MPR: ++ asm volatile("adr r0, str_mpr\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_DATAEYE: ++ asm volatile("adr r0, str_dataeye\n\t" ++ "bl uart_early_puts"); ++ break; ++ case DDR_ERR_LPCA: ++ asm volatile("adr r0, str_lpca\n\t" ++ "bl uart_early_puts"); ++ break; ++ default: ++ break; ++ } ++ ++ /* error string */ ++ asm volatile("adr r0, str_err\n\t" ++ "bl uart_early_puts"); ++ ++ /* error phy */ ++ if (phy != 0) { ++ asm volatile("adr r0, str_phy\n\t" ++ "bl uart_early_puts"); ++ uart_early_put_hex(phy); ++ } ++ ++ /* error byte */ ++ if (byte != -1) { ++ asm volatile("adr r0, str_byte\n\t" ++ "bl uart_early_puts"); ++ uart_early_put_hex(byte); ++ } ++ ++ /* error dq */ ++ if (dq != -1) { ++ asm volatile("adr r0, str_dq\n\t" ++ "bl uart_early_puts"); ++ uart_early_put_hex(dq); ++ } ++} ++ ++/* Display DDR training start when boot */ ++void ddr_training_start(void) ++{ ++ asm volatile( ++ "push {lr}\n\t" ++ "adr r0, str_ddrtr_start\n\t" ++ "bl uart_early_puts\n\t" ++ "pop {lr}" ++ ); ++} ++ ++/* Display DDR training result when boot */ ++void ddr_training_suc(void) ++{ ++ asm volatile( ++ "push {lr}\n\t" ++ "adr r0, str_ddrtr_suc\n\t" ++ "bl uart_early_puts\n\t" ++ "pop {lr}" ++ ); ++} ++#endif /* DDR_TRAINING_CUT_CODE_CONFIG */ ++#else ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ return; ++} ++void ddr_training_suc(void) ++{ ++ return; ++} ++void ddr_training_start(void) ++{ ++ return; ++} ++#endif /* DDR_TRAINING_UART_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_training_console.c b/drivers/ddr/vendor/default_v2/ddr_training_console.c +new file mode 100644 +index 0000000..980d2a1 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_training_console.c +@@ -0,0 +1,271 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#define __DDR_TRAINING_CONSOLE__ ++#ifdef DDR_TRAINING_CONSOLE_CONFIG ++ ++#define DDR_UART_BASE_REG 0x12090000 ++#define UART_PL01X_FR 0x18 /* Flag register (Read only). */ ++#define UART_PL01X_FR_RXFE 0x10 ++#define UART_PL01X_DR 0x00 /* Data read or written from the interface. */ ++#define UART_PL01X_ECR 0x04 /* Error clear register (Write). */ ++ ++#define ALIGN_COUNT 4 ++ ++#define isprint(c) ((c) >= ' ' && (c) <= '~') ++#define isspace(c) ((c) == ' ' || ((c) >= '\t' && (c) <= '\r')) ++#define isdigit(c) ((c) >= '0' && (c) <= '9') ++#define isxdigit(c) (isdigit(c) || \ ++ ((c) >= 'A' && (c) <= 'F') || \ ++ ((c) >= 'a' && (c) <= 'f')) ++ ++/* DDR console get char */ ++static int ddr_console_getc(void) ++{ ++ unsigned int data; ++ ++ /* Wait until there is data in the FIFO */ ++ while (reg_read(DDR_UART_BASE_REG + UART_PL01X_FR) & UART_PL01X_FR_RXFE) { ++ } ++ ++ data = reg_read(DDR_UART_BASE_REG + UART_PL01X_DR); ++ /* Check for an error flag */ ++ if (data & 0xFFFFFF00) { ++ /* Clear the error */ ++ reg_write(0xFFFFFFFF, DDR_UART_BASE_REG + UART_PL01X_ECR); ++ return -1; ++ } ++ ++ return (int)data; ++} ++ ++/* DDR read line */ ++static char *ddr_readline(char *str, int len) ++{ ++ unsigned int c; ++ char *p = str; ++ while (len > 0) { ++ c = ddr_console_getc(); ++ switch (c) { ++ case '\r': ++ case '\n': ++ *p = '\0'; ++ DDR_PUTC('\r'); ++ DDR_PUTC('\n'); ++ return str; ++ case 0x08: ++ case 0x7F: ++ if (p > str) { ++ p--; ++ len++; ++ DDR_PUTC('\b'); ++ DDR_PUTC(' '); ++ DDR_PUTC('\b'); ++ } ++ break; ++ default: ++ if (isprint(c)) { ++ (*p++) = (char)c; ++ len--; ++ DDR_PUTC(c); ++ } ++ break; ++ } ++ } ++ (*--p) = '\0'; ++ return str; ++} ++ ++/* HEX to INT */ ++static int hex2int(char **ss, unsigned int *n) ++{ ++ unsigned char *s = (unsigned char *)(*ss); ++ ++ while (isspace(*s)) ++ s++; ++ ++ if (!(*s)) ++ return -1; ++ ++ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ++ s += 2; /* Skip the first 2 characters: 0x */ ++ ++ for ((*n) = 0; isxdigit(*s); s++) { ++ (*n) = ((*n) << 4); /* shift left 4 */ ++ if ((*s) >= '0' && (*s) <= '9') ++ (*n) |= ((*s) - '0'); ++ else if ((*s) >= 'a' && (*s) <= 'f') ++ (*n) |= ((*s) + 10 - 'a'); /* transfer a-f to 10-15 */ ++ else if ((*s) >= 'A' && (*s) <= 'F') ++ (*n) |= ((*s) + 10 - 'A'); /* transfer A-F to 10-15 */ ++ } ++ ++ if (isspace(*s) || !(*s)) { ++ while (isspace(*s)) ++ s++; ++ (*ss) = (char *)s; ++ return 0; ++ } ++ ++ return -1; ++} ++/* ++ * DDR do memory write. ++ * mw address value [count] ++ */ ++static int ddr_do_memory_write(char *cmd) ++{ ++ unsigned int address; ++ unsigned int value; ++ unsigned int count = ALIGN_COUNT; ++ ++ if (hex2int(&cmd, &address)) ++ return -1; ++ ++ if (hex2int(&cmd, &value)) ++ return -1; ++ ++ if ((*cmd) && hex2int(&cmd, &count)) ++ return -1; ++ ++ if ((address & 0x03) || (count & 0x03)) { ++ ddr_info("parameter should align with 4 bytes.\n"); ++ return -1; ++ } ++ for (; count > 0; count -= ALIGN_COUNT, address += ALIGN_COUNT) /* align with 4 bytes */ ++ reg_write(value, address); ++ ++ return 0; ++} ++/* ++ * DDR do memory display. ++ * md address [count] ++ */ ++static int ddr_do_memory_display(char *cmd) ++{ ++ unsigned int ix; ++ unsigned int loop; ++ unsigned int address; ++ unsigned int count = 64; ++ ++ if (hex2int(&cmd, &address)) ++ return -1; ++ ++ if ((*cmd) && hex2int(&cmd, &count)) ++ return -1; ++ ++ if (count < ALIGN_COUNT) ++ count = ALIGN_COUNT; ++ ++ address &= ~0x03; ++ loop = (count & ~0x03); ++ ++ while (loop > 0) { ++ DDR_PUTC('0'); ++ DDR_PUTC('x'); ++ DDR_PUT_HEX(address); ++ DDR_PUTC(':'); ++ ++ for (ix = 0; ++ ix < ALIGN_COUNT && loop > 0; ++ ix++, loop -= ALIGN_COUNT, address += ALIGN_COUNT) { ++ DDR_PUTC(' '); ++ DDR_PUT_HEX(reg_read(address)); ++ } ++ DDR_PUTC('\r'); ++ DDR_PUTC('\n'); ++ } ++ ++ return 0; ++} ++ ++static int ddr_do_sw_training(char *cmd) ++{ ++ int result; ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++#ifdef DDR_TRAINING_CMD ++ struct ddr_training_result_st ddrt_result_sram; ++#endif ++ ++ ddr_training_cfg_init(cfg); ++#ifdef DDR_TRAINING_CMD ++ cfg->res_st = (void *)&ddrt_result_sram; ++#endif ++ result = ddr_training_all(cfg); ++ ++ result += ddr_dcc_training_func(cfg); ++ ++ return 0; ++} ++ ++static int ddr_do_hw_training(char *cmd) ++{ ++ int result; ++ ++ result = ddr_hw_training_func(); ++ ++ return result; ++} ++ ++/* Do DDR training console if sw training or hw training fail */ ++static int ddr_training_console(void) ++{ ++ char str[256]; /* 256: Command length range */ ++ char *p = NULL; ++ ++ while (1) { ++ DDR_PUTC('d'); ++ DDR_PUTC('d'); ++ DDR_PUTC('r'); ++ DDR_PUTC('#'); ++ ++ p = ddr_readline(str, sizeof(str)); ++ while (isspace(*p)) ++ p++; ++ if (p[0] == 'q') ++ break; ++ if (p[0] == 'm' && p[1] == 'w') { ++ ddr_do_memory_write(p + 2); /* p[2]:Third character */ ++ } else if (p[0] == 'm' && p[1] == 'd') { ++ ddr_do_memory_display(p + 2); /* p[2]:Third character */ ++ } else if (p[0] == 's' && p[1] == 'w') { ++ ddr_do_sw_training(p + 2); /* p[2]:Third character */ ++ } else if (p[0] == 'h' && p[1] == 'w') { ++ ddr_do_hw_training(p + 2); /* p[2]:Third character */ ++ } ++ } ++ ++ return 0; ++} ++#else ++static int ddr_training_console(void) ++{ ++ ddr_warning("Not support DDR training console"); ++ return 0; ++} ++#endif /* DDR_TRAINING_CONSOLE_CONFIG */ ++ ++int ddr_training_console_if(void) ++{ ++ return DDR_TRAINING_CONSOLE(); ++} +diff --git a/drivers/ddr/vendor/default_v2/ddr_training_ctl.c b/drivers/ddr/vendor/default_v2/ddr_training_ctl.c +new file mode 100644 +index 0000000..19a6074 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_training_ctl.c +@@ -0,0 +1,163 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#ifdef DDR_SW_TRAINING_FUNC_PUBLIC ++int ddr_training_boot_func(struct ddr_cfg_st *cfg) ++{ ++ int result; ++ ++ /* check hardware gating */ ++ if (reg_read(cfg->cur_phy + DDR_PHY_PHYINITSTATUS) & PHY_INITSTATUS_GT_MASK) { ++ ddr_fatal("PHY[%x] hw gating fail", cfg->cur_phy); ++ ddr_training_stat(DDR_ERR_HW_GATING, cfg->cur_phy, -1, -1); ++ } ++ ++ /* lpca */ ++ result = ddr_lpca_training_func(cfg); ++ /* write leveling */ ++ result += ddr_wl_func(cfg); ++ /* dataeye/gate/vref need switch axi */ ++ /* dataeye */ ++ result += ddr_dataeye_training_func(cfg); ++#ifdef DDR_HW_TRAINING_CONFIG ++ /* hardware read */ ++ if (result && (ddr_training_check_bypass(cfg, DDR_BYPASS_HW_MASK) == DDR_FALSE)) { ++ struct tr_relate_reg relate_reg_ac; ++ ddr_training_save_reg(cfg, &relate_reg_ac, DDR_BYPASS_HW_MASK); ++ result = ddr_hw_dataeye_read(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg_ac); ++ cfg->adjust = DDR_DATAEYE_ABNORMAL_ADJUST; ++ result += ddr_dataeye_training(cfg); ++ } ++#endif ++ /* mpr */ ++ result += ddr_mpr_training_func(cfg); ++ /* gate */ ++ result += ddr_gating_func(cfg); ++ /* vref */ ++ result += ddr_vref_training_func(cfg); ++ ++ return result; ++} ++ ++/* Support DDRC510 with two PHY */ ++int ddr_sw_training_func(void) ++{ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ struct tr_custom_reg reg; ++ int result; ++ ++#ifdef SYSCTRL_DDR_TRAINING_VERSION_FLAG ++ /* DDR training version flag */ ++ unsigned int tmp_reg = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_VERSION_FLAG); ++ tmp_reg = (tmp_reg & 0xffff0000) | DDR_VERSION; ++ reg_write(tmp_reg, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_VERSION_FLAG); ++#endif ++ ++ /* check sw ddr training enable */ ++ if (DDR_BYPASS_ALL_MASK == reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG) ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ && DDR_BYPASS_ALL_MASK == reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC) ++#endif ++ ) ++ return 0; ++ ++ ddr_training_start(); ++ ++ memset(®, 0, sizeof(struct tr_custom_reg)); ++ /* save customer reg */ ++ if (ddr_boot_cmd_save(®) != 0) ++ return -1; ++ ++#ifdef DDR_TRAINING_STAT_CONFIG ++ /* clear stat register */ ++ reg_write(0x0, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT); ++#endif ++ ++ ddr_training_cfg_init(cfg); ++ cfg->cmd_st = 0; ++ ++ result = ddr_training_all(cfg); ++ result += ddr_dcc_training_func(cfg); ++ if (!result) ++ ddr_training_suc(); ++ else ++ ddr_training_console_if(); ++ ++ /* restore customer reg */ ++ ddr_boot_cmd_restore(®); ++ ++ return result; ++} ++#endif /* DDR_SW_TRAINING_FUNC_PUBLIC */ ++ ++#ifdef DDR_PCODE_TRAINING_CONFIG ++int ddr_pcode_training_func(void) ++{ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ ++ ddr_training_cfg_init(cfg); ++ return ddr_pcode_training(cfg); ++} ++#else ++int ddr_pcode_training_func(void) ++{ ++ ddr_warning("Not support DDR pcode training"); ++ return 0; ++} ++#endif ++ ++#ifdef DDR_HW_TRAINING_CONFIG ++int ddr_hw_training_func(void) ++{ ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ ++ ddr_training_cfg_init(cfg); ++ ++ return ddr_hw_training(cfg); ++} ++#else ++int ddr_hw_training_func(void) ++{ ++ ddr_warning("Not support DDR HW training"); ++ return 0; ++} ++#endif /* DDR_HW_TRAINING_CONFIG */ ++ ++int ddr_sw_training_if(void) ++{ ++ return DDR_SW_TRAINING_FUNC(); ++} ++ ++int ddr_hw_training_if(void) ++{ ++ return DDR_HW_TRAINING_FUNC(); ++} ++ ++int ddr_pcode_training_if(void) ++{ ++ return DDR_PCODE_TRAINING_FUNC(); ++} ++ +diff --git a/drivers/ddr/vendor/default_v2/ddr_training_impl.c b/drivers/ddr/vendor/default_v2/ddr_training_impl.c +new file mode 100644 +index 0000000..44c34a9 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_training_impl.c +@@ -0,0 +1,2699 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++#include "ddr_interface.h" ++ ++#define __COMMON__ ++ ++static void ddr_training_adjust_wdq(struct ddr_cfg_st *cfg); ++static void ddr_training_adjust_wdqs(struct ddr_cfg_st *cfg); ++#ifdef DDR_TRAINING_MEM_FUNC ++void *memcpy(void *dst, const void *src, size_t len) ++{ ++ const char *s = src; ++ char *d = dst; ++ ++ if ((dst == NULL) || (src == NULL)) ++ return NULL; ++ ++ while (len--) ++ *d++ = *s++; ++ ++ return dst; ++} ++ ++void *memset(void *b, int c, size_t len) ++{ ++ char *bp = b; ++ ++ if (b == NULL) ++ return NULL; ++ ++ while (len--) ++ *bp++ = (unsigned char)c; ++ ++ return b; ++} ++#endif ++ ++/* Get byte number */ ++static unsigned int ddr_phy_get_byte_num(unsigned int base_dmc) ++{ ++ unsigned int byte_num; ++ ++ /* memery width -> byte number */ ++ byte_num = ((reg_read(base_dmc + DDR_DMC_CFG_DDRMODE) >> ++ DMC_MEM_WIDTH_BIT) & DMC_MEM_WIDTH_MASK) << 1; ++ /* for codedex */ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ byte_num = DDR_PHY_BYTE_MAX; ++ ddr_error("get byte num fail"); ++ } ++ ++ return byte_num; ++} ++ ++static int ddr_training_by_dmc(struct ddr_cfg_st *cfg) ++{ ++ if (cfg->cmd_st != NULL) { ++#ifdef DDR_TRAINING_CMD ++ return ddr_training_cmd_func(cfg); ++#endif ++ } else { ++ return ddr_training_boot_func(cfg); ++ } ++ ++ return 0; ++} ++ ++static int ddr_training_by_rank(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ ddr_phy_switch_rank(cfg->cur_phy, cfg->rank_idx); ++ ++ for (i = 0; i < cfg->phy[cfg->phy_idx].dmc_num; i++) { ++ cfg->dmc_idx = i; ++ cfg->cur_dmc = cfg->phy[cfg->phy_idx].dmc[i].addr; ++ cfg->cur_pattern = cfg->phy[cfg->phy_idx].dmc[i].ddrt_pattern; ++ result += ddr_training_by_dmc(cfg); ++ } ++ ++ return result; ++} ++ ++static int ddr_training_by_phy(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ unsigned int phy_mask; ++ unsigned int rank_num; ++ ++ phy_mask = 1 << (cfg->phy_idx); ++ rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ cfg->cur_item = cfg->phy[cfg->phy_idx].rank[i].item; ++ if (ddr_training_check_bypass(cfg, phy_mask) != DDR_FALSE) ++ continue; ++ result += ddr_training_by_rank(cfg); ++ } ++ ++ if (rank_num == DDR_SUPPORT_RANK_MAX) { ++ ddr_training_adjust_wdq(cfg); ++ ddr_training_adjust_wdqs(cfg); ++ ddr_phy_switch_rank(cfg->cur_phy, 0x0); /* switch to rank0 */ ++ } ++ return result; ++} ++ ++int ddr_training_all(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ if ((cfg == NULL) || (cfg->phy_num > DDR_PHY_NUM)) ++ return -1; ++ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ result += ddr_training_by_phy(cfg); ++ } ++ ++ return result; ++} ++ ++/* DDR training phy/dmc/dram_type config init */ ++static void ddr_training_cfg_set_phy0_dmc(struct ddr_cfg_st *cfg) ++{ ++ unsigned int ddrt_pattern; ++ ++ if (cfg->phy[0].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ cfg->phy[0].dmc_num = 2; /* lpddr4: 2 dmc per phy */ ++ ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN); ++ cfg->phy[0].dmc[0].addr = DDR_REG_BASE_DMC0; ++ cfg->phy[0].dmc[0].ddrt_pattern = ddrt_pattern & 0xffff; /* bit[15-0]:dmc0 ddrt pattern */ ++ cfg->phy[0].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC0); ++ cfg->phy[0].dmc[1].addr = DDR_REG_BASE_DMC1; ++ cfg->phy[0].dmc[1].ddrt_pattern = ddrt_pattern >> 16; /* bit[31-16]:dmc1 ddrt pattern */ ++ cfg->phy[0].dmc[1].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC1); ++ cfg->phy[0].total_byte_num = cfg->phy[0].dmc[0].byte_num + cfg->phy[0].dmc[1].byte_num; ++ } else { ++ cfg->phy[0].dmc_num = 1; /* other: 1 dmc per phy */ ++ cfg->phy[0].dmc[0].addr = DDR_REG_BASE_DMC0; ++ cfg->phy[0].dmc[0].ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN); ++ cfg->phy[0].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC0); ++ cfg->phy[0].total_byte_num = cfg->phy[0].dmc[0].byte_num; ++ } ++ ddr_info("phy[0] total_byte_num[%x] dram_type[%x]", cfg->phy[0].total_byte_num, cfg->phy[0].dram_type); ++} ++ ++#ifdef DDR_REG_BASE_PHY1 ++static void ddr_training_cfg_set_phy1_dmc(struct ddr_cfg_st *cfg) ++{ ++ unsigned int ddrt_pattern; ++ ++ if (cfg->phy[1].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ cfg->phy[1].dmc_num = 2; /* lpddr4: 2 dmc per phy */ ++ ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_SEC); ++ cfg->phy[1].dmc[0].addr = DDR_REG_BASE_DMC2; ++ cfg->phy[1].dmc[0].ddrt_pattern = ddrt_pattern & 0xffff; /* bit[15-0]:dmc0 ddrt pattern */ ++ cfg->phy[1].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC2); ++ cfg->phy[1].dmc[1].addr = DDR_REG_BASE_DMC3; ++ cfg->phy[1].dmc[1].ddrt_pattern = ddrt_pattern >> 16; /* bit[31-16]:dmc1 ddrt pattern */ ++ cfg->phy[1].dmc[1].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC3); ++ cfg->phy[1].total_byte_num = cfg->phy[1].dmc[0].byte_num + cfg->phy[1].dmc[1].byte_num; ++ } else { ++ cfg->phy[1].dmc_num = 1; /* other: 1 dmc per phy */ ++#ifdef DDR_CHANNEL_MAP_PHY0_DMC0_PHY1_DMC2 ++ cfg->phy[1].dmc[0].addr = DDR_REG_BASE_DMC2; ++ cfg->phy[1].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC2); ++#else ++ cfg->phy[1].dmc[0].addr = DDR_REG_BASE_DMC1; ++ cfg->phy[1].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC1); ++#endif ++ cfg->phy[1].dmc[0].ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_SEC); ++ cfg->phy[1].total_byte_num = cfg->phy[1].dmc[0].byte_num; ++ } ++ ddr_info("phy[1] total_byte_num[%x] dram_type[%x]", cfg->phy[1].total_byte_num, cfg->phy[1].dram_type); ++} ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++static void ddr_training_cfg_set_phy2_dmc(struct ddr_cfg_st *cfg) ++{ ++ unsigned int ddrt_pattern; ++ ++ if (cfg->phy[2].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { /* phy2 */ ++ cfg->phy[2].dmc_num = 2; /* lpddr4: 2 dmc per phy */ ++ ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_THIRD); ++ cfg->phy[2].dmc[0].addr = DDR_REG_BASE_DMC4; /* phy2 */ ++ cfg->phy[2].dmc[0].ddrt_pattern = ddrt_pattern & 0xffff; /* phy2, bit[15-0]:dmc0 ddrt pattern */ ++ cfg->phy[2].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC4); ++ cfg->phy[2].dmc[1].addr = DDR_REG_BASE_DMC5; /* phy2 */ ++ cfg->phy[2].dmc[1].ddrt_pattern = ddrt_pattern >> 16; /* phy2, bit[31-16]:dmc1 ddrt pattern */ ++ cfg->phy[2].dmc[1].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC5); ++ cfg->phy[2].total_byte_num = cfg->phy[2].dmc[0].byte_num + cfg->phy[2].dmc[1].byte_num; /* phy2 */ ++ } else { ++ cfg->phy[2].dmc_num = 1; /* phy2, other: 1 dmc per phy */ ++#ifdef DDR_CHANNEL_MAP_PHY0_DMC0_PHY1_DMC2 ++ cfg->phy[2].dmc[0].addr = DDR_REG_BASE_DMC4; ++ cfg->phy[2].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC4); /* phy2 */ ++#else ++ cfg->phy[2].dmc[0].addr = DDR_REG_BASE_DMC2; /* phy2 */ ++ cfg->phy[2].dmc[0].byte_num = ddr_phy_get_byte_num(DDR_REG_BASE_DMC2); /* phy2 */ ++#endif ++ cfg->phy[2].dmc[0].ddrt_pattern = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_PATTERN_THIRD); /* phy2 */ ++ cfg->phy[2].total_byte_num = cfg->phy[2].dmc[0].byte_num; /* phy2 */ ++ } ++ ddr_info("phy[2] total_byte_num[%x] dram_type[%x]", cfg->phy[2].total_byte_num, cfg->phy[2].dram_type); /* phy2 */ ++} ++#endif ++ ++/* DDR training phy/dmc/dram_type config init */ ++static void ddr_training_cfg_set_dmc(struct ddr_cfg_st *cfg) ++{ ++ ddr_training_cfg_set_phy0_dmc(cfg); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_training_cfg_set_phy1_dmc(cfg); ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++ ddr_training_cfg_set_phy2_dmc(cfg); ++#endif ++} ++ ++static void ddr_training_cfg_set_rank(struct ddr_cfg_st *cfg) ++{ ++ cfg->phy[0].rank_num = 1; ++ cfg->phy[0].rank[0].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ cfg->phy[0].rank[0].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK0); ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ cfg->phy[0].rank[1].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC); ++#endif ++ cfg->phy[0].rank[1].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1); ++ ++ if (reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1)) ++ cfg->phy[0].rank_num = 2; /* rank number equal 2 if SYSCTRL_DDR_HW_PHY0_RANK1 has bean define in boot table */ ++ ++ ddr_info("Rank number PHY0 [%x]", cfg->phy[0].rank_num); ++ ddr_info("HW training item PHY0[%x = %x][%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK0), cfg->phy[0].rank[0].item_hw, ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1), cfg->phy[0].rank[1].item_hw); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ cfg->phy[1].rank_num = 1; ++ cfg->phy[1].rank[0].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); ++ cfg->phy[1].rank[0].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK0); ++ ++ cfg->phy[1].rank[1].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC); ++ cfg->phy[1].rank[1].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1); ++ ++ if (reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1)) ++ cfg->phy[1].rank_num = 2; /* rank number equal 2 if SYSCTRL_DDR_HW_PHY1_RANK1 has bean define in boot table */ ++ ++ ddr_info("Rank number PHY1[%x]", cfg->phy[1].rank_num); ++ ddr_info("HW training item PHY1[%x = %x][%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK0), cfg->phy[1].rank[0].item_hw, ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1), cfg->phy[1].rank[1].item_hw); ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++ cfg->phy[2].rank_num = 1; ++ cfg->phy[2].rank[0].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG); /* phy2 */ ++ cfg->phy[2].rank[0].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY2_RANK0); /* phy2 */ ++ ++ cfg->phy[2].rank[1].item = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC); /* phy2 */ ++ cfg->phy[2].rank[1].item_hw = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY2_RANK1); /* phy2 */ ++ ++ if (reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY2_RANK1)) ++ cfg->phy[2].rank_num = 2; /* rank number equal 2 if SYSCTRL_DDR_HW_PHY1_RANK1 has bean define in boot table */ ++ ++ ddr_info("Rank number PHY2[%x]", cfg->phy[2].rank_num); /* phy2 */ ++ ddr_info("HW training item PHY2[%x = %x][%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY2_RANK0), cfg->phy[2].rank[0].item_hw, /* phy2 */ ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY2_RANK1), cfg->phy[2].rank[1].item_hw); /* phy2 */ ++#endif ++ ++ ddr_info("SW training item Rank0[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG), cfg->phy[0].rank[0].item); ++#ifdef SYSCTRL_DDR_TRAINING_CFG_SEC ++ ddr_info("SW training item Rank1[%x = %x]", ++ (DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_CFG_SEC), cfg->phy[0].rank[1].item); ++#endif ++} ++ ++static void ddr_training_cfg_set_phy(struct ddr_cfg_st *cfg) ++{ ++ cfg->phy_num = DDR_PHY_NUM; ++ cfg->phy[0].addr = DDR_REG_BASE_PHY0; ++ cfg->phy[0].dram_type = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_MASK; ++#ifdef DDR_REG_BASE_PHY1 ++ cfg->phy[1].addr = DDR_REG_BASE_PHY1; ++ cfg->phy[1].dram_type = reg_read(DDR_REG_BASE_PHY1 + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_MASK; ++#endif ++#ifdef DDR_REG_BASE_PHY2 ++ cfg->phy[2].addr = DDR_REG_BASE_PHY2; /* phy2 */ ++ cfg->phy[2].dram_type = reg_read(DDR_REG_BASE_PHY2 + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_MASK; /* phy2 */ ++#endif ++} ++ ++void ddr_training_cfg_init(struct ddr_cfg_st *cfg) ++{ ++ memset(cfg, 0, sizeof(struct ddr_cfg_st)); ++ ddr_training_cfg_set_phy(cfg); ++ ddr_training_cfg_set_dmc(cfg); ++ ddr_training_cfg_set_rank(cfg); ++} ++ ++/* config DDR hw item */ ++void ddr_training_hw_item_cfg(struct ddr_cfg_st *cfg, unsigned int form_value) ++{ ++ cfg->phy[0].rank[0].item_hw = ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK0) & form_value; ++ cfg->phy[0].rank[1].item_hw = ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY0_RANK1) & form_value; ++ ++#ifdef DDR_REG_BASE_PHY1 ++ cfg->phy[1].rank[0].item_hw = ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK0) & form_value; ++ cfg->phy[1].rank[1].item_hw = ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY1_RANK1) & form_value; ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++ cfg->phy[2].rank[0].item_hw = /* phy2 */ ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY2_RANK0) & form_value; ++ cfg->phy[2].rank[1].item_hw = /* phy2 */ ++ reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_HW_PHY2_RANK1) & form_value; ++#endif ++} ++ ++/* 2GHz CPU run 2000 "nop" in 1 ns */ ++void ddr_training_delay(unsigned int cnt) ++{ ++ while (cnt--) ++ asm("nop"); ++} ++ ++/* set auto refresh */ ++static void ddr_training_set_timing(unsigned int base_dmc, unsigned int timing) ++{ ++ ddr_training_delay(DDR_AUTO_TIMING_DELAY); ++ reg_write(timing, base_dmc + DDR_DMC_TIMING2); ++ /* need to delay 1 ns */ ++ ddr_training_delay(DDR_AUTO_TIMING_DELAY); ++} ++ ++#ifdef DDR_TRAINING_STAT_CONFIG ++/* Save training result in stat register */ ++static void ddr_training_save(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ unsigned int stat; ++ unsigned int phy_index; ++ ++ stat = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT); ++ /* only record the first error */ ++ if (stat) ++ return; ++ ++ stat = mask; ++ if (phy != 0) { ++ phy_index = ((phy == DDR_REG_BASE_PHY0) ? DDR_ERR_PHY0 : DDR_ERR_PHY1); ++ stat |= phy_index; ++ } ++ ++ if (byte != -1) ++ stat |= ((unsigned int)byte << DDR_ERR_BYTE_BIT); ++ ++ if (dq != -1) ++ stat |= ((unsigned int)dq << DDR_ERR_DQ_BIT); ++ ++ reg_write(stat, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDR_TRAINING_STAT); ++} ++#endif ++ ++/* Record error code in register */ ++void ddr_training_stat(unsigned int mask, unsigned int phy, int byte, int dq) ++{ ++ ddr_training_error(mask, phy, byte, dq); ++#ifdef DDR_TRAINING_STAT_CONFIG ++ ddr_training_save(mask, phy, byte, dq); ++#endif ++} ++ ++/* Check DDR training item whether by pass */ ++int ddr_training_check_bypass(const struct ddr_cfg_st *cfg, unsigned int mask) ++{ ++ /* training item disable */ ++ if ((cfg->cur_item) & mask) { ++ ddr_debug("DDR training [%x] is disable, rank[%x] cfg[%x]", mask, cfg->rank_idx, cfg->cur_item); ++ return DDR_TRUE; ++ } else { ++ return DDR_FALSE; ++ } ++} ++ ++#if !defined(DDR_TRAINING_CUT_CODE_CONFIG) || defined(DDR_TRAINING_CMD) ++/* Save register value before training */ ++void ddr_training_save_reg(const struct ddr_cfg_st *cfg, struct tr_relate_reg *relate_reg, unsigned int mask) ++{ ++ unsigned int base_dmc; ++ unsigned int base_phy; ++ ++ if ((cfg == NULL) || (relate_reg == NULL)) { ++ ddr_error("Pointer parameter is NULL!"); ++ return; ++ } ++ base_dmc = cfg->cur_dmc; ++ base_phy = cfg->cur_phy; ++ ++ /* save reg value */ ++ relate_reg->auto_ref_timing = reg_read(base_dmc + DDR_DMC_TIMING2); ++ relate_reg->power_down = reg_read(base_dmc + DDR_DMC_CFG_PD); ++ relate_reg->misc_scramb = reg_read(base_phy + DDR_PHY_MISC); ++ /* Static register have to read two times to get the right value. */ ++ relate_reg->ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL4); ++ relate_reg->ac_phy_ctl = reg_read(base_phy + DDR_PHY_ACPHYCTL4); ++ ++ /* set new value */ ++ switch (mask) { ++ case DDR_BYPASS_WL_MASK: ++ case DDR_BYPASS_LPCA_MASK: ++ /* disable auto refresh */ ++ ddr_training_set_timing(base_dmc, relate_reg->auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ break; ++ case DDR_BYPASS_GATE_MASK: ++ /* disable auto refresh */ ++ ddr_training_set_timing(base_dmc, relate_reg->auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ if (!(reg_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) /* set 1T */ ++ reg_write(0x0, base_phy + DDR_PHY_ACPHYCTL4); ++ break; ++ case DDR_BYPASS_HW_MASK: ++ if (!(reg_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) /* set 1T */ ++ reg_write(0x0, base_phy + DDR_PHY_ACPHYCTL4); ++ break; ++ default: ++ break; ++ } ++ ++ reg_write(relate_reg->power_down & DMC_POWER_DOWN_DIS, base_dmc + DDR_DMC_CFG_PD); ++ reg_write(relate_reg->misc_scramb & PHY_MISC_SCRAMB_DIS, base_phy + DDR_PHY_MISC); ++ ++ ddr_dqsswap_save_func(relate_reg->swapdfibyte_en, base_phy); ++ ++ ddr_axi_save_func(relate_reg); ++ ++ ddr_rnkvol_save_func(relate_reg, base_dmc); ++ ++ /* save customer reg */ ++ ddr_training_save_reg_func((void *)relate_reg, mask); ++ ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_asm_dsb(); ++} ++ ++/* Restore register value after training */ ++void ddr_training_restore_reg(const struct ddr_cfg_st *cfg, ++ const struct tr_relate_reg *relate_reg) ++{ ++ unsigned int base_dmc; ++ unsigned int base_phy; ++ ++ if ((cfg == NULL) || (relate_reg == NULL)) { ++ ddr_error("Pointer parameter is NULL"); ++ return; ++ } ++ ++ base_dmc = cfg->cur_dmc; ++ base_phy = cfg->cur_phy; ++ ++ /* enable auto refresh */ ++ ddr_training_set_timing(base_dmc, relate_reg->auto_ref_timing); ++ reg_write(relate_reg->power_down, base_dmc + DDR_DMC_CFG_PD); ++ reg_write(relate_reg->misc_scramb, base_phy + DDR_PHY_MISC); ++ if (!(reg_read(base_phy + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T)) ++ reg_write(relate_reg->ac_phy_ctl, base_phy + DDR_PHY_ACPHYCTL4); ++ ++ ddr_dqsswap_restore_func(relate_reg->swapdfibyte_en, base_phy); ++ ++ ddr_axi_restore_func(relate_reg); ++ ++ ddr_rnkvol_restore_func(relate_reg, base_dmc); ++ ++ /* restore customer reg */ ++ ddr_training_restore_reg_func((void *)relate_reg); ++ ++ ddr_phy_cfg_update(base_phy); ++ ++ ddr_asm_dsb(); ++} ++ ++/* Switch AXI to DMC0/DMC1/DMC2/DMC3 for DDRT test */ ++void ddr_training_switch_axi(const struct ddr_cfg_st *cfg) ++{ ++ ddr_axi_chsel_remap_func(cfg); ++ ddr_axi_switch_func(cfg); ++ ddr_debug("AXI region0[%x = %x]", ++ (DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0), ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB0)); ++ ddr_debug("AXI region1[%x = %x]", ++ (DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1), ++ reg_read(DDR_REG_BASE_AXI + DDR_AXI_REGION_ATTRIB1)); ++ ++ ddr_rnkvol_set_func(cfg); ++} ++#endif ++ ++#if defined(DDR_WL_TRAINING_CONFIG) || defined(DDR_MPR_TRAINING_CONFIG) ++/* Excute DMC sfc command */ ++static void ddr_dmc_sfc_cmd(unsigned int base_dmc, unsigned int sfc_cmd, ++ unsigned int sfc_addr, unsigned int sfc_bank) ++{ ++ unsigned int count = 0; ++ ++ /* set sfc cmd */ ++ dmc_sfc_cmd_write(sfc_cmd, base_dmc + DDR_DMC_SFCCMD); ++ /* set col and row */ ++ reg_write(sfc_addr, base_dmc + DDR_DMC_SFCADDR); ++ /* set bank */ ++ dmc_sfc_bank_write(sfc_bank, base_dmc + DDR_DMC_SFCBANK); ++ /* excute cmd */ ++ reg_write(0x1, base_dmc + DDR_DMC_SFCREQ); ++ ++ ddr_asm_dsb(); ++ ++ while (count < DDR_SFC_WAIT_TIMEOUT) { /* wait command finished */ ++ if (!(reg_read(base_dmc + DDR_DMC_SFCREQ) & 0x1)) ++ break; ++ count++; ++ } ++ ++ if (count >= DDR_HWR_WAIT_TIMEOUT) ++ ddr_error("SFC cmd wait timeout"); ++} ++#endif ++ ++#if defined(DDR_HW_TRAINING_CONFIG) || defined(DDR_DCC_TRAINING_CONFIG) ++/* Exit or enter auto self-refresh */ ++static int ddr_training_easr(unsigned int base_dmc, unsigned int sref_req) ++{ ++ unsigned int count; ++ ++ count = DDR_HWR_WAIT_TIMEOUT; ++ if (sref_req == DDR_EXIT_SREF) { ++ /* Exit Auto-self refresh */ ++ reg_write(DMC_CTRL_SREF_EXIT, base_dmc + DDR_DMC_CTRL_SREF); ++ while (count--) { ++ if (!(reg_read(base_dmc + DDR_DMC_CURR_FUNC) & DMC_CURR_FUNC_IN_SREF_MASK)) ++ break; ++ } ++ } else if (sref_req == DDR_ENTER_SREF) { ++ /* Enter Auto-self refresh */ ++ reg_write(DMC_CTRL_SREF_ENTER, base_dmc + DDR_DMC_CTRL_SREF); ++ while (count--) { ++ if (reg_read(base_dmc + DDR_DMC_CURR_FUNC) & DMC_CURR_FUNC_IN_SREF_MASK) ++ break; ++ } ++ } ++ ++ if (count == 0xffffffff) { ++ ddr_fatal("SREF wait timeout"); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, -1, -1, -1); ++ return -1; ++ } ++ return 0; ++} ++ ++/* DDR hw/dcc training exit or enter auto self-refresh */ ++int ddr_training_ctrl_easr(const struct ddr_cfg_st *cfg, unsigned int sref_req) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ ++ if (phy_st->dmc_num > DDR_DMC_PER_PHY_MAX) { ++ ddr_error("loop upper limit cfg->dmc_num out of range"); ++ return -1; ++ } ++ for (i = 0; i < phy_st->dmc_num; i++) ++ result += ddr_training_easr(phy_st->dmc[i].addr, sref_req); ++ ++ return result; ++} ++ ++void ddr_training_save_timing(const struct ddr_cfg_st *cfg, struct ddr_timing_st *timing_st) ++{ ++ unsigned int i; ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ ++ for (i = 0; i < phy_st->dmc_num; i++) { ++ timing_st->val[i] = reg_read(phy_st->dmc[i].addr + DDR_DMC_TIMING2); ++ /* disable auto refresh */ ++ ddr_training_set_timing(phy_st->dmc[i].addr, timing_st->val[i] & DMC_AUTO_TIMING_DIS); ++ } ++} ++ ++void ddr_training_restore_timing(const struct ddr_cfg_st *cfg, ++ const struct ddr_timing_st *timing_st) ++{ ++ unsigned int i; ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ ++ for (i = 0; i < phy_st->dmc_num; i++) ++ ddr_training_set_timing(phy_st->dmc[i].addr, timing_st->val[i]); ++} ++#endif /* DDR_HW_TRAINING_CONFIG || DDR_DCC_TRAINING_CONFIG */ ++ ++/* ++ * config ddrc exit self-refresh or exit powerdown ++ * bit[3] 0x1:exit self-refresh ++ * bit[3] 0x0:exit powerdown ++ */ ++void ddr_sref_cfg(const struct ddr_cfg_st *cfg, struct dmc_cfg_sref_st *cfg_sref, ++ unsigned int value) ++{ ++ unsigned int i; ++ const struct ddr_phy_st *phy_st = NULL; ++ ++ if (cfg == NULL || cfg_sref == NULL) { ++ ddr_error("Pointer parameter is NULL!"); ++ return; ++ } ++ ++ phy_st = &cfg->phy[cfg->phy_idx]; ++ for (i = 0; i < phy_st->dmc_num; i++) { ++ cfg_sref->val[i] = reg_read(phy_st->dmc[i].addr + DDR_DMC_CFG_SREF); ++ reg_write((cfg_sref->val[i] & (~DMC_CFG_INIT_XSREF_PD_MASK)) | value, ++ phy_st->dmc[i].addr + DDR_DMC_CFG_SREF); ++ } ++} ++ ++/* Restore DMC_CFG_SREF config */ ++void ddr_sref_cfg_restore(const struct ddr_cfg_st *cfg, const struct dmc_cfg_sref_st *cfg_sref) ++{ ++ unsigned int i; ++ ++ const struct ddr_phy_st *phy_st = &cfg->phy[cfg->phy_idx]; ++ for (i = 0; i < phy_st->dmc_num; i++) ++ reg_write(cfg_sref->val[i], phy_st->dmc[i].addr + DDR_DMC_CFG_SREF); ++} ++ ++/* ++ * Update delay setting in registers to PHY immediately. ++ * Make delay setting take effect. ++ */ ++void ddr_phy_cfg_update(unsigned int base_phy) ++{ ++ unsigned int tmp; ++ ++ tmp = reg_read(base_phy + DDR_PHY_MISC); ++ tmp |= (1 << PHY_MISC_UPDATE_BIT); ++ /* update new config to PHY */ ++ reg_write(tmp, base_phy + DDR_PHY_MISC); ++ tmp &= ~(1 << PHY_MISC_UPDATE_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_MISC); ++ tmp = reg_read(base_phy + DDR_PHY_PHYINITCTRL); ++ /* set 1 to issue PHY counter reset signal */ ++ tmp |= (1 << PHY_PHYCONN_RST_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_PHYINITCTRL); ++ /* set 0 to end the reset signal */ ++ tmp &= ~(1 << PHY_PHYCONN_RST_BIT); ++ reg_write(tmp, base_phy + DDR_PHY_PHYINITCTRL); ++ ++ ddr_asm_dsb(); ++} ++ ++/* Set delay value of the bit delay line of the DATA block */ ++static void ddr_phy_set_dq_bdl(const struct ddr_cfg_st *cfg, unsigned int value) ++{ ++ unsigned int val; ++ unsigned int offset; ++ unsigned int dq; ++ unsigned int base_phy; ++ unsigned int byte_index; ++ unsigned int rank; ++ ++ base_phy = cfg->cur_phy; ++ byte_index = cfg->cur_byte; ++ rank = cfg->rank_idx; ++ dq = cfg->cur_dq & 0x7; ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNWDQNBDL0] 4 bdl: wdq0bdl-wdq3bdl */ ++ offset = ddr_phy_dxnwdqnbdl0(rank, byte_index); ++ else /* [DXNWDQNBDL1] 4 bdl: wdq4bdl-wdq7bdl */ ++ offset = ddr_phy_dxnwdqnbdl1(rank, byte_index); ++ } else { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNRDQNBDL0] 4 bdl: rdq0bdl-rdq3bdl */ ++ offset = ddr_phy_dxnrdqnbdl0(rank, byte_index); ++ else /* [DXNRDQNBDL1] 4 bdl: rdq4bdl-rdq7bdl */ ++ offset = ddr_phy_dxnrdqnbdl1(rank, byte_index); ++ } ++ dq &= 0x3; /* one register contains 4 dq */ ++ val = reg_read(base_phy + offset); ++ val &= ~(0xFF << (dq << DDR_DQBDL_SHIFT_BIT)); ++ val |= ((PHY_BDL_MASK & value) << ((dq << DDR_DQBDL_SHIFT_BIT) + PHY_BDL_DQ_BIT)); ++ reg_write(val, base_phy + offset); ++ ++ ddr_phy_cfg_update(base_phy); ++} ++ ++/* Get PHY DQ value */ ++static unsigned int ddr_phy_get_dq_bdl(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int val; ++ unsigned int offset; ++ unsigned int dq; ++ unsigned int byte_index; ++ unsigned int rank; ++ ++ byte_index = cfg->cur_byte; ++ rank = cfg->rank_idx; ++ dq = cfg->cur_dq & 0x7; ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNWDQNBDL0] 4 bdl: wdq0bdl-wdq3bdl */ ++ offset = ddr_phy_dxnwdqnbdl0(rank, byte_index); ++ else /* [DXNWDQNBDL1] 4 bdl: wdq4bdl-wdq7bdl */ ++ offset = ddr_phy_dxnwdqnbdl1(rank, byte_index); ++ } else { ++ if (dq < DDR_DQ_NUM_EACH_REG) /* [DXNRDQNBDL0] 4 bdl: rdq0bdl-rdq3bdl */ ++ offset = ddr_phy_dxnrdqnbdl0(rank, byte_index); ++ else /* [DXNRDQNBDL1] 4 bdl: rdq4bdl-rdq7bdl */ ++ offset = ddr_phy_dxnrdqnbdl1(rank, byte_index); ++ } ++ ++ dq &= 0x3; /* one register contains 4 dq */ ++ val = (reg_read(cfg->cur_phy + offset) >> ++ ((dq << DDR_DQBDL_SHIFT_BIT) + PHY_BDL_DQ_BIT)) & PHY_BDL_MASK; ++ ++ return val; ++} ++ ++static void ddr_rdqs_sync_rdm(const struct ddr_cfg_st *cfg, int offset) ++{ ++ unsigned int rdqnbdl; ++ int rdm; ++ ++ rdqnbdl = reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++ rdm = (rdqnbdl >> PHY_RDM_BDL_BIT) & PHY_RDM_BDL_MASK; ++ rdm += offset; ++ rdm = ((rdm < 0) ? 0 : rdm); ++ rdm = ((rdm > PHY_RDM_BDL_MASK) ? PHY_RDM_BDL_MASK : rdm); ++ rdqnbdl = rdqnbdl & (~(PHY_RDM_BDL_MASK << PHY_RDM_BDL_BIT)); ++ reg_write(rdqnbdl | ((unsigned int)rdm << PHY_RDM_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++} ++ ++static void ddr_wdqs_sync_wdm(const struct ddr_cfg_st *cfg, int offset) ++{ ++ unsigned int wdqnbdl; ++ int wdm; ++ ++ wdqnbdl = reg_read(cfg->cur_phy + ddr_phy_dxnwdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++ wdm = (wdqnbdl >> PHY_WDM_BDL_BIT) & PHY_WDM_BDL_MASK; ++ wdm += offset; ++ wdm = ((wdm < 0) ? 0 : wdm); ++ wdm = ((wdm > PHY_WDM_BDL_MASK) ? PHY_WDM_BDL_MASK : wdm); ++ wdqnbdl = wdqnbdl & (~(PHY_WDM_BDL_MASK << PHY_WDM_BDL_BIT)); ++ reg_write(wdqnbdl | ((unsigned int)wdm << PHY_WDM_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnwdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++} ++ ++static void ddr_sync_wdqsbdl(const struct ddr_cfg_st *cfg, int offset) ++{ ++ unsigned int wdqsdly; ++ int wdqsbdl; ++ ++ wdqsdly = reg_read(cfg->cur_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, cfg->cur_byte)); ++ wdqsbdl = (wdqsdly >> PHY_WDQS_BDL_BIT) & PHY_WDQS_BDL_MASK; ++ wdqsbdl += offset; ++ wdqsbdl = ((wdqsbdl < 0) ? 0 : wdqsbdl); ++ wdqsbdl = ((wdqsbdl > PHY_WDQS_BDL_MASK) ? PHY_WDQS_BDL_MASK : wdqsbdl); ++ wdqsdly = wdqsdly & (~(PHY_WDQS_BDL_MASK << PHY_WDQS_BDL_BIT)); ++ reg_write(wdqsdly | ((unsigned int)wdqsbdl << PHY_WDQS_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, cfg->cur_byte)); ++} ++ ++static void ddr_rdqs_sync_rank_rdq(struct ddr_cfg_st *cfg, int offset) ++{ ++ int dq_val; ++ int i; ++ unsigned int cur_mode = cfg->cur_mode; ++ ++ cfg->cur_mode = DDR_MODE_READ; ++ ++ /* sync other rank rdm */ ++ ddr_rdqs_sync_rdm(cfg, offset); ++ ++ /* sync other rank rdq */ ++ ddr_debug("Before sync rank[%x] byte[%x] dq[%x = %x][%x = %x] offset[%x]", ++ cfg->rank_idx, cfg->cur_byte, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte)), ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte)), offset); ++ ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ dq_val = (int)ddr_phy_get_dq_bdl(cfg); ++ dq_val += offset; ++ dq_val = ((dq_val < 0) ? 0 : dq_val); ++ dq_val = ((dq_val > PHY_BDL_MASK) ? PHY_BDL_MASK : dq_val); ++ ddr_phy_set_dq_bdl(cfg, dq_val); ++ } ++ ++ cfg->cur_mode = cur_mode; /* restore to current mode */ ++ ++ ddr_debug("After sync rank[%x] byte[%x] dq[%x = %x][%x = %x]", ++ cfg->rank_idx, cfg->cur_byte, ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, cfg->cur_byte)), ++ cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, cfg->cur_byte))); ++} ++ ++static void ddr_wdqs_sync_rank_wdq(struct ddr_cfg_st *cfg, int offset) ++{ ++ int dq_val; ++ int i; ++ unsigned int cur_mode = cfg->cur_mode; ++ ++ cfg->cur_mode = DDR_MODE_WRITE; ++ ++ /* sync other rank wdm */ ++ ddr_wdqs_sync_wdm(cfg, offset); ++ ++ /* sync other rank wdq */ ++ ddr_debug("Before sync rank[%x] byte[%x] dq[%x = %x][%x = %x] offset[%x]", ++ cfg->rank_idx, cfg->cur_byte, ++ cfg->cur_phy + ddr_phy_dxnwdqnbdl0(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnwdqnbdl0(cfg->rank_idx, cfg->cur_byte)), ++ cfg->cur_phy + ddr_phy_dxnwdqnbdl1(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnwdqnbdl1(cfg->rank_idx, cfg->cur_byte)), offset); ++ ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ dq_val = (int)ddr_phy_get_dq_bdl(cfg); ++ dq_val += offset; ++ dq_val = ((dq_val < 0) ? 0 : dq_val); ++ dq_val = ((dq_val > PHY_BDL_MASK) ? PHY_BDL_MASK : dq_val); ++ ddr_phy_set_dq_bdl(cfg, dq_val); ++ } ++ ++ cfg->cur_mode = cur_mode; /* restore to current mode */ ++ ++ ddr_debug("After sync rank[%x] byte[%x] dq[%x = %x][%x = %x]", ++ cfg->rank_idx, cfg->cur_byte, ++ cfg->cur_phy + ddr_phy_dxnwdqnbdl0(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnwdqnbdl0(cfg->rank_idx, cfg->cur_byte)), ++ cfg->cur_phy + ddr_phy_dxnwdqnbdl1(cfg->rank_idx, cfg->cur_byte), ++ reg_read(cfg->cur_phy + ddr_phy_dxnwdqnbdl1(cfg->rank_idx, cfg->cur_byte))); ++} ++ ++static void ddr_rdqbdl_adj(struct ddr_cfg_st *cfg, struct ddr_bdl_dly_st *bdl_dly_s) ++{ ++ int i; ++ const int value_num = 10; ++ unsigned int rank = cfg->rank_idx; ++ unsigned int min = 0xffffffff; ++ unsigned int rdm, rdqs; ++ unsigned int cur_mode = cfg->cur_mode; ++ ++ cfg->cur_mode = DDR_MODE_READ; ++ ++ rdm = reg_read(cfg->cur_phy + ddr_phy_dxnrdqnbdl2(rank, cfg->cur_byte)); ++ rdqs = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ++ /* get dq value */ ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ bdl_dly_s->value[i] = ddr_phy_get_dq_bdl(cfg); ++ } ++ bdl_dly_s->value[8] = (rdm >> PHY_RDM_BDL_BIT) & PHY_RDM_BDL_MASK; /* bdl[8]: save rdmbdl */ ++ bdl_dly_s->value[9] = (rdqs >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; /* bdl[9]: rdqsbdl */ ++ ++ for (i = 0; i < value_num; i++) { ++ if (bdl_dly_s->value[i] < min) ++ min = bdl_dly_s->value[i]; ++ } ++ ++ /* subtract minimum */ ++ for (i = 0; i < value_num; i++) ++ bdl_dly_s->value[i] = bdl_dly_s->value[i] - min; ++ ++ /* set dq value */ ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ ddr_phy_set_dq_bdl(cfg, bdl_dly_s->value[i]); ++ } ++ ++ rdm = (rdm & (~(PHY_RDM_BDL_MASK << PHY_RDM_BDL_BIT))) | ++ (bdl_dly_s->value[8] << PHY_RDM_BDL_BIT); /* bdl[8]: save rdmbdl */ ++ rdqs = (rdqs & (~(PHY_RDQS_BDL_MASK << PHY_RDQS_BDL_BIT))) | ++ (bdl_dly_s->value[9] << PHY_RDQS_BDL_BIT); /* bdl[9]: rdqsbdl */ ++ ++ reg_write(rdm, cfg->cur_phy + ddr_phy_dxnrdqnbdl2(rank, cfg->cur_byte)); ++ reg_write(rdqs, cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ++ cfg->cur_mode = cur_mode; /* restore to current mode */ ++} ++ ++static void ddr_get_dly_value(struct tr_dq_adj_st *wdq_st, ++ unsigned int base_phy, unsigned int rank_idx, unsigned int byte_idx) ++{ ++ /* wdqs */ ++ wdq_st->wdqsdly = reg_read(base_phy + ddr_phy_dxwdqsdly(rank_idx, byte_idx)); ++ wdq_st->wdqsphase = (wdq_st->wdqsdly >> PHY_WDQS_PHASE_BIT) & PHY_WDQS_PHASE_MASK; ++ /* wdq */ ++ wdq_st->wdqdly = reg_read(base_phy + ddr_phy_dxnwdqdly(rank_idx, byte_idx)); ++ wdq_st->wdqphase = (wdq_st->wdqdly >> PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++ /* wlsl */ ++ wdq_st->dxnwlsl = reg_read(base_phy + ddr_phy_dxnwlsl(rank_idx, byte_idx)); ++ wdq_st->wlsl = (wdq_st->dxnwlsl >> PHY_WLSL_BIT) & PHY_WLSL_MASK; ++} ++ ++static void ddr_restore_dly_value(const struct tr_dq_adj_st *wdq_st, ++ unsigned int base_phy, unsigned int rank_idx, unsigned int byte_idx) ++{ ++ reg_write(wdq_st->wdqsdly, base_phy + ddr_phy_dxwdqsdly(rank_idx, byte_idx)); ++ reg_write(wdq_st->wdqdly, base_phy + ddr_phy_dxnwdqdly(rank_idx, byte_idx)); ++ reg_write(wdq_st->dxnwlsl, base_phy + ddr_phy_dxnwlsl(rank_idx, byte_idx)); ++ ddr_phy_cfg_update(base_phy); ++} ++ ++static void ddr_judge_wdq_rank(struct ddr_cfg_st *cfg, unsigned int byte_idx, ++ struct tr_dq_adj_st *wdq_rank0, struct tr_dq_adj_st *wdq_rank1) ++{ ++ int skew; ++ int phase2bdl; ++ int wdqphase_rank0_tmp0, wdqphase_rank1_tmp0, wdqphase_rank0_tmp1, wdqphase_rank1_tmp1; ++ unsigned int cur_rank; ++ ++ cur_rank = cfg->rank_idx; ++ phase2bdl = ((reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(byte_idx)) >> PHY_RDQS_CYC_BIT) & ++ PHY_RDQS_CYC_MASK) / PHY_WDQSPHASE_NUM_T; ++ wdqphase_rank0_tmp0 = wdq_rank0->wdqphase & 0xf; /* 0xf:bit[3:0] */ ++ wdqphase_rank1_tmp0 = wdq_rank1->wdqphase & 0xf; /* 0xf:bit[3:0] */ ++ ++ /* ++ * Remove phase holes ++ * phase hole in every 4 wdqphase reg value ++ */ ++ wdqphase_rank0_tmp1 = wdqphase_rank0_tmp0 - (wdqphase_rank0_tmp0 + 1) / 4; /* 4 wdqphase */ ++ wdqphase_rank1_tmp1 = wdqphase_rank1_tmp0 - (wdqphase_rank1_tmp0 + 1) / 4; /* 4 wdqphase */ ++ ++ if (wdqphase_rank0_tmp1 >= wdqphase_rank1_tmp1) { ++ skew = wdqphase_rank0_tmp1 - wdqphase_rank1_tmp1; ++ cfg->rank_idx = 0; /* 0: adjust rank0 */ ++ if ((skew > (PHY_WDQPHASE_NUM_T >> 1)) && (wdq_rank1->wdqphase > PHY_WDQSPHASE_REG_NUM_T)) { ++ skew = PHY_WDQPHASE_NUM_T - skew; ++ wdq_rank1->wdqphase = wdq_rank1->wdqphase - PHY_WDQSPHASE_REG_NUM_T; ++ cfg->rank_idx = 1; /* 1: adjust rank1 */ ++ } ++ } else { ++ skew = wdqphase_rank1_tmp1 - wdqphase_rank0_tmp1; ++ cfg->rank_idx = 1; /* 1: adjust rank1 */ ++ if ((skew > (PHY_WDQPHASE_NUM_T >> 1)) && (wdq_rank0->wdqphase > PHY_WDQSPHASE_REG_NUM_T)) { ++ skew = PHY_WDQPHASE_NUM_T - skew; ++ wdq_rank0->wdqphase = wdq_rank0->wdqphase - PHY_WDQSPHASE_REG_NUM_T; ++ cfg->rank_idx = 0; /* 0: adjust rank0 */ ++ } ++ } ++ if (cfg->rank_idx == 0) { ++ wdq_rank0->wdqphase = wdq_rank0->wdqphase - wdqphase_rank0_tmp0 + wdqphase_rank1_tmp0; ++ reg_write((wdq_rank0->wdqdly & (~(PHY_WDQ_PHASE_MASK << PHY_WDQ_PHASE_BIT))) | ++ (wdq_rank0->wdqphase << PHY_WDQ_PHASE_BIT), cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, byte_idx)); ++ } else if (cfg->rank_idx == 1) { ++ wdq_rank1->wdqphase = wdq_rank1->wdqphase - wdqphase_rank1_tmp0 + wdqphase_rank0_tmp0; ++ reg_write((wdq_rank1->wdqdly & (~(PHY_WDQ_PHASE_MASK << PHY_WDQ_PHASE_BIT))) | ++ (wdq_rank1->wdqphase << PHY_WDQ_PHASE_BIT), cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, byte_idx)); ++ } ++ ddr_wdqs_sync_rank_wdq(cfg, phase2bdl * skew); ++ cfg->rank_idx = cur_rank; /* restore to current rank */ ++} ++ ++static void ddr_training_adjust_wdq(struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ unsigned int base_phy; ++ ++ struct tr_dq_adj_st wdq_rank0; ++ struct tr_dq_adj_st wdq_rank1; ++ ++ base_phy = cfg->cur_phy; ++ if (cfg->phy[cfg->phy_idx].total_byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x"); ++ return; ++ } ++ for (i = 0; i < cfg->phy[cfg->phy_idx].total_byte_num; i++) { ++ cfg->cur_byte = i; ++ ++ ddr_get_dly_value(&wdq_rank0, base_phy, 0, i); ++ ddr_get_dly_value(&wdq_rank1, base_phy, 1, i); ++ ++ /* select which rank to adjust */ ++ ddr_judge_wdq_rank(cfg, i, &wdq_rank0, &wdq_rank1); ++ } ++ ddr_phy_cfg_update(base_phy); ++} ++ ++/* select which rank to adjust */ ++static int ddr_adjust_wdqs_select_rank(unsigned int byte_idx, struct ddr_cfg_st *cfg, ++ struct tr_dq_adj_st *wdqs_rank0, struct tr_dq_adj_st *wdqs_rank1) ++{ ++ int skew; ++ int wdqsphase_rank0_tmp0, wdqsphase_rank1_tmp0; ++ int wdqsphase_rank0_tmp1, wdqsphase_rank1_tmp1; ++ ++ wdqsphase_rank0_tmp0 = wdqs_rank0->wdqsphase & 0xf; /* 0xf:bit[4:0] */ ++ wdqsphase_rank1_tmp0 = wdqs_rank1->wdqsphase & 0xf; /* 0xf:bit[4:0] */ ++ /* ++ * Remove phase holes ++ * phase hole in every 4 wdqsphase reg value ++ */ ++ wdqsphase_rank0_tmp1 = wdqsphase_rank0_tmp0 - (wdqsphase_rank0_tmp0 + 1) / 4; /* 4 wdqsphase */ ++ wdqsphase_rank1_tmp1 = wdqsphase_rank1_tmp0 - (wdqsphase_rank1_tmp0 + 1) / 4; /* 4 wdqsphase */ ++ ++ if (wdqsphase_rank0_tmp1 >= wdqsphase_rank1_tmp1) { ++ skew = wdqsphase_rank0_tmp1 - wdqsphase_rank1_tmp1; ++ cfg->rank_idx = 0; /* 0: adjust rank0 */ ++ if ((skew > (PHY_WDQPHASE_NUM_T >> 1)) && (wdqs_rank1->wlsl >= 1) && ++ (wdqs_rank1->wdqphase < (PHY_WDQ_PHASE_MASK - PHY_WDQPHASE_REG_NUM_T))) { ++ skew = PHY_WDQSPHASE_NUM_T - skew; ++ wdqs_rank1->wlsl = wdqs_rank1->wlsl - 1; ++ wdqs_rank1->wdqphase = wdqs_rank1->wdqphase + PHY_WDQPHASE_REG_NUM_T; ++ reg_write((wdqs_rank1->dxnwlsl & (~(PHY_WLSL_MASK << PHY_WLSL_BIT))) | ++ (wdqs_rank1->wlsl << PHY_WLSL_BIT), cfg->cur_phy + ddr_phy_dxnwlsl(1, byte_idx)); ++ reg_write((wdqs_rank1->wdqdly & (~(PHY_WDQ_PHASE_MASK << PHY_WDQ_PHASE_BIT))) | ++ (wdqs_rank1->wdqphase << PHY_WDQS_PHASE_BIT), cfg->cur_phy + ddr_phy_dxnwdqdly(1, byte_idx)); ++ cfg->rank_idx = 1; /* 1: adjust rank1 */ ++ } ++ } else { ++ skew = wdqsphase_rank1_tmp1 - wdqsphase_rank0_tmp1; ++ cfg->rank_idx = 1; /* 1: adjust rank1 */ ++ if ((skew > (PHY_WDQPHASE_NUM_T >> 1)) && (wdqs_rank0->wlsl >= 1) && ++ (wdqs_rank0->wdqphase < (PHY_WDQ_PHASE_MASK - PHY_WDQPHASE_REG_NUM_T))) { ++ skew = PHY_WDQSPHASE_NUM_T - skew; ++ wdqs_rank0->wlsl = wdqs_rank0->wlsl - 1; ++ wdqs_rank0->wdqphase = wdqs_rank0->wdqphase + PHY_WDQPHASE_REG_NUM_T; ++ reg_write((wdqs_rank0->dxnwlsl & (~(PHY_WLSL_MASK << PHY_WLSL_BIT))) | ++ (wdqs_rank0->wlsl << PHY_WLSL_BIT), cfg->cur_phy + ddr_phy_dxnwlsl(0, byte_idx)); ++ reg_write((wdqs_rank0->wdqdly & (~(PHY_WDQ_PHASE_MASK << PHY_WDQ_PHASE_BIT))) | ++ (wdqs_rank0->wdqphase << PHY_WDQS_PHASE_BIT), cfg->cur_phy + ddr_phy_dxnwdqdly(0, byte_idx)); ++ cfg->rank_idx = 0; /* 0: adjust rank0 */ ++ } ++ } ++ ++ return skew; ++} ++ ++static void ddr_judge_wdqs_rank(struct ddr_cfg_st *cfg, unsigned int byte_idx, ++ struct tr_dq_adj_st *wdqs_rank0, struct tr_dq_adj_st *wdqs_rank1) ++{ ++ int skew; ++ int phase2bdl; ++ int wdqsphase_rank0_tmp0, wdqsphase_rank1_tmp0; ++ unsigned int cur_rank; ++ ++ cur_rank = cfg->rank_idx; ++ phase2bdl = ((reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(byte_idx)) >> PHY_RDQS_CYC_BIT) & ++ PHY_RDQS_CYC_MASK) / PHY_WDQSPHASE_NUM_T; ++ ++ wdqsphase_rank0_tmp0 = wdqs_rank0->wdqsphase & 0xf; /* 0xf:bit[4:0] */ ++ wdqsphase_rank1_tmp0 = wdqs_rank1->wdqsphase & 0xf; /* 0xf:bit[4:0] */ ++ ++ skew = ddr_adjust_wdqs_select_rank(byte_idx, cfg, wdqs_rank0, wdqs_rank1); ++ ++ if (cfg->rank_idx == 0) { ++ wdqs_rank0->wdqsphase = wdqs_rank0->wdqsphase - wdqsphase_rank0_tmp0 + wdqsphase_rank1_tmp0; ++ reg_write((wdqs_rank0->wdqsdly & (~(PHY_WDQS_PHASE_MASK << PHY_WDQS_PHASE_BIT))) | ++ (wdqs_rank0->wdqsphase << PHY_WDQS_PHASE_BIT), cfg->cur_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, byte_idx)); ++ } else if (cfg->rank_idx == 1) { ++ wdqs_rank1->wdqsphase = wdqs_rank1->wdqsphase - wdqsphase_rank1_tmp0 + wdqsphase_rank0_tmp0; ++ reg_write((wdqs_rank1->wdqsdly & (~(PHY_WDQS_PHASE_MASK << PHY_WDQS_PHASE_BIT))) | ++ (wdqs_rank1->wdqsphase << PHY_WDQS_PHASE_BIT), cfg->cur_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, byte_idx)); ++ } ++ ++ ddr_sync_wdqsbdl(cfg, phase2bdl * skew); ++ cfg->rank_idx = cur_rank; /* restore to current rank */ ++} ++ ++static void ddr_training_adjust_wdqs(struct ddr_cfg_st *cfg) ++{ ++ unsigned int i; ++ unsigned int base_phy; ++ struct tr_dq_adj_st wdqs_rank0; ++ struct tr_dq_adj_st wdqs_rank1; ++ ++ base_phy = cfg->cur_phy; ++ if (cfg->phy[cfg->phy_idx].total_byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", cfg->phy[cfg->phy_idx].total_byte_num); ++ return; ++ } ++ for (i = 0; i < cfg->phy[cfg->phy_idx].total_byte_num; i++) { ++ cfg->cur_byte = i; ++ ++ ddr_get_dly_value(&wdqs_rank0, base_phy, 0, i); ++ ddr_get_dly_value(&wdqs_rank1, base_phy, 1, i); ++ ++ /* select which rank to adjust */ ++ ddr_judge_wdqs_rank(cfg, i, &wdqs_rank0, &wdqs_rank1); ++ } ++ ddr_phy_cfg_update(base_phy); ++} ++ ++#ifdef DDR_MPR_TRAINING_CONFIG ++/* Check MPR read data */ ++int ddr_mpr_check(const struct ddr_cfg_st *cfg) ++{ ++ /* read data */ ++ ddr_dmc_sfc_cmd(cfg->cur_dmc, DMC_CMD_TYPE_READ, 0x0, 0x0); ++ ++ return dmc_mpr_check_bit_0_127(cfg); ++} ++#else ++int ddr_mpr_check(const struct ddr_cfg_st *cfg) ++{ ++ return 0; ++} ++#endif ++ ++#ifdef DDR_TRAINING_DEBUG ++#define ddr_trining_break_point_func(name) ddr_training_break_point(name) ++#else ++#define ddr_trining_break_point_func(name) ++#endif ++ ++void ddr_training_break_point(const char *name) ++{ ++ ddr_info(name); ++ ddr_training_console_if(); ++} ++ ++#define __DATAEYE_ADJUST__ ++#ifdef DDR_TRAINING_ADJUST_CONFIG ++static unsigned int ddr_adjust_get_average(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int dq0_3, dq4_7, val; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_index = cfg->cur_byte; ++ unsigned int rank = cfg->rank_idx; ++ ++ if (cfg->cur_mode == DDR_MODE_WRITE) ++ return (reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank, byte_index)) >> ++ PHY_WDM_BDL_BIT) & PHY_BDL_MASK; ++ ++ /* read */ ++ dq0_3 = reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank, byte_index)); ++ dq4_7 = reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank, byte_index)); ++ ++ val = ((dq0_3 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK) + ++ ((dq0_3 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK) + ++ ((dq0_3 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK) + ++ ((dq0_3 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK) + ++ ((dq4_7 >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK); ++ ++ val = val >> 3; /* shift 3: 8 dq */ ++ ++ return val; ++} ++ ++/* ++ * @accel : Return a value to adjust quickly. ++ * Check dataeye DQ window on left or right or middle. ++ */ ++static unsigned int ddr_adjust_trend_check(const struct ddr_cfg_st *cfg, int *accel) ++{ ++ unsigned int dq_bdl; ++ unsigned int size; ++ ++ /* 32 BDL middle[13, 17]. 128 BDL middle[40, 56] */ ++ /* 1 Phase = (DDR_BDL_PHASE_TRANSFORM) BDL */ ++ size = DDR_BDL_PHASE_TRANSFORM >> 1; ++ dq_bdl = ddr_adjust_get_average(cfg); ++ ++ /* increase adjust step to accelerate */ ++ if (accel != NULL) { ++ if (dq_bdl > PHY_DQ_BDL_MIDDLE) ++ *accel = dq_bdl - PHY_DQ_BDL_MIDDLE; ++ else if (dq_bdl < PHY_DQ_BDL_MIDDLE) ++ *accel = PHY_DQ_BDL_MIDDLE - dq_bdl; ++ ++ ddr_info("byte[%x] bdl[%x] middle[%x] accel[%x] rdqs[%x]", ++ cfg->cur_byte, dq_bdl, PHY_DQ_BDL_MIDDLE, *accel, ++ (reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)) >> ++ PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK); ++ } ++ ++ /* window on left */ ++ if (dq_bdl < (PHY_DQ_BDL_MIDDLE - size)) ++ return DDR_WIN_LEFT; ++ /* on right */ ++ else if (dq_bdl > (PHY_DQ_BDL_MIDDLE + size)) ++ return DDR_WIN_RIGHT; ++ else ++ return DDR_WIN_MIDDLE; ++} ++ ++/* Check adjust value whether valid */ ++static int ddr_adjust_check_val(int val, unsigned int mode) ++{ ++ if (mode == DDR_MODE_READ) { ++ if (val < 0 || val > PHY_RDQS_BDL_MASK) ++ return DDR_FALSE; ++ } else { ++ if (val < 0 || val > PHY_WDQ_PHASE_MASK) ++ return DDR_FALSE; ++ } ++ ++ return DDR_TRUE; ++} ++ ++static void ddr_rdqs_sync(struct ddr_cfg_st *cfg, int val) ++{ ++ unsigned int rdqsdly; ++ unsigned int cur_rank = cfg->rank_idx; ++ int old, offset; ++ ++ rdqsdly = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ old = (rdqsdly >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ offset = val - old; ++ ++ /* sync rdm */ ++ ddr_rdqs_sync_rank_rdq(cfg, offset); ++ ++ if (cfg->phy[cfg->phy_idx].rank_num == 1) { ++ ddr_debug("Rank number[%x] not need sync another rank", cfg->phy[cfg->phy_idx].rank_num); ++ return; ++ } ++ ++ /* sync other rank rdm and rdq */ ++ cfg->rank_idx = DDR_SUPPORT_RANK_MAX - 1 - cur_rank; /* switch to another rank */ ++ ddr_rdqs_sync_rank_rdq(cfg, offset); ++ cfg->rank_idx = cur_rank; /* resotre to cur rank */ ++} ++ ++static void ddr_set_rdqs(struct ddr_cfg_st *cfg, int val) ++{ ++ unsigned int delay; ++ delay = reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++ ++ ddr_phy_rdqs_sync_rdm(cfg, val); ++ ++ /* clear rdqs bdl */ ++ delay = delay & (~(PHY_RDQS_BDL_MASK << PHY_RDQS_BDL_BIT)); ++ ++ reg_write(delay | ((unsigned int)val << PHY_RDQS_BDL_BIT), ++ cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)); ++} ++ ++/* Get value which need to adjust */ ++static int ddr_adjust_get_val(const struct ddr_cfg_st *cfg) ++{ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL!"); ++ return 0; ++ } ++ if (cfg->cur_mode == DDR_MODE_READ) ++ return (reg_read(cfg->cur_phy + ddr_phy_dxnrdqsdly(cfg->cur_byte)) >> ++ PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ else ++ return (reg_read(cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, cfg->cur_byte)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++} ++ ++/* Set value which need to adjust */ ++static void ddr_adjust_set_val(struct ddr_cfg_st *cfg, int val) ++{ ++ unsigned int delay; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) { ++ ddr_set_rdqs(cfg, val); ++ } else { ++ delay = reg_read(cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, cfg->cur_byte)); ++ /* clear wdq phase */ ++ delay = delay & (~(PHY_WDQ_PHASE_MASK << PHY_WDQ_PHASE_BIT)); ++ ++ reg_write(delay | ((unsigned int)val << PHY_WDQ_PHASE_BIT), ++ cfg->cur_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, cfg->cur_byte)); ++ } ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++} ++ ++/* Add or delete value to adjust */ ++static void ddr_adjust_change_val(unsigned int dir, int *val, ++ int step, unsigned int mode) ++{ ++ if (mode == DDR_MODE_READ) { ++ if (dir == DDR_WIN_RIGHT) ++ (*val) = (*val) + step; ++ else ++ (*val) = (*val) - step; ++ } else { ++ /* decrease wdq phase, window move to right */ ++ if (dir == DDR_WIN_RIGHT) ++ (*val) = (*val) - step; ++ else ++ (*val) = (*val) + step; ++ } ++} ++ ++/* ++ * @dir : move direction. DDR_TRUE move to right, DDR_FALSE move to left. ++ * Move window to specified direction until the best DQ bdl beyond the midline. ++ */ ++static void ddr_adjust_move_win(struct ddr_cfg_st *cfg, ++ struct training_data *training, int step, unsigned int dir) ++{ ++ int cur_val, def_val; ++ int accel; ++ unsigned int i; ++ unsigned int trend; ++ unsigned int max_value; ++ ++ max_value = ((cfg->cur_mode == DDR_MODE_WRITE) ? PHY_WDQ_PHASE_MASK : PHY_RDQS_BDL_MASK); ++ ++ def_val = ddr_adjust_get_val(cfg); ++ cur_val = def_val; ++ for (i = 0; i <= max_value; i++) { ++ accel = step; ++ /* write mode no need to accelerate */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) ++ trend = ddr_adjust_trend_check(cfg, 0); ++ else ++ trend = ddr_adjust_trend_check(cfg, &accel); ++ ++ if (trend == DDR_WIN_MIDDLE || trend == dir) { ++ ddr_debug("Move byte[%x] window to middle suc", cfg->cur_byte); ++ break; ++ } ++ ++ ddr_adjust_change_val(dir, &cur_val, accel, cfg->cur_mode); ++ if (ddr_adjust_check_val(cur_val, cfg->cur_mode) == DDR_FALSE) { ++ ddr_warning("Move byte[%x] to middle fail. value[%x]", ++ cfg->cur_byte, cur_val); ++ break; ++ } ++ ++ ddr_debug("Byte[%x] mode[%x] set value[%x]", ++ cfg->cur_byte, cfg->cur_mode, cur_val); ++ ddr_adjust_set_val(cfg, cur_val); ++ if (ddr_dataeye_deskew(cfg, training)) { ++ ddr_adjust_set_val(cfg, def_val); ++ /* MUST deskew dataeye after restore rdqs */ ++ ddr_dataeye_deskew(cfg, training); ++ ddr_error("Byte[%x] deskew fail, restore[%x]", cfg->cur_byte, def_val); ++ break; ++ } ++ } ++} ++ ++/* Adjust specified byte winodw to middle */ ++static void ddr_adjust_byte(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int trend; ++ ++ trend = ddr_adjust_trend_check(cfg, 0); ++ /* window on left, move to right */ ++ if (trend == DDR_WIN_LEFT) ++ ddr_adjust_move_win(cfg, training, DDR_DQS_ADJ_STEP, DDR_WIN_RIGHT); ++ /* window on right, move to left */ ++ else if (trend == DDR_WIN_RIGHT) ++ ddr_adjust_move_win(cfg, training, DDR_DQS_ADJ_STEP, DDR_WIN_LEFT); ++ /* window on middle, no need to move */ ++ else ++ ddr_debug("Byte[%x] mode[%x] win on middle", cfg->cur_byte, cfg->cur_mode); ++} ++ ++/* ++ * Adjust PHY dataeye. On normal case, ++ * read dateeye window on left after read dataeye hardware training, ++ * write dataeye window on left after write leveling training. ++ */ ++static void ddr_adjust_dataeye(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int i; ++ ++ /* dataeye adjust disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DATAEYE_ADJ_MASK) != DDR_FALSE) ++ return; ++ ++ ddr_debug("DDR dataeye adjust PHY[%x][%x] DMC[%x][%x] Rank[%x]", ++ cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx); ++ ++ if (cfg->adjust == DDR_FALSE) ++ return; ++ ++ if (cfg->phy_idx >= DDR_PHY_NUM || cfg->dmc_idx >= DDR_DMC_PER_PHY_MAX) { ++ ddr_error("Array index phy_idx or dmc_idx out of range!"); ++ return; ++ } ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) { ++ ddr_error("get byte num fail, byte_num = %x", get_byte_num(cfg)); ++ return; ++ } ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ ddr_adjust_byte(cfg, training); ++ } ++} ++#else ++#define ddr_adjust_dataeye(cfg, training) ++#endif /* DDR_TRAINING_ADJUST_CONFIG */ ++ ++#define __DATAEYE_TRAINING__ ++#ifdef DDR_DATAEYE_TRAINING_CONFIG ++/* Check dataeye dq */ ++static int ddr_dataeye_check_dq(const struct ddr_cfg_st *cfg) ++{ ++ if (cfg->dq_check_type == DDR_CHECK_TYPE_DDRT) ++ return ddr_ddrt_check(cfg); ++ else if (cfg->dq_check_type == DDR_CHECK_TYPE_MPR) ++ return ddr_mpr_check(cfg); ++ else ++ ddr_error("DDR dataeye dq check type not set"); ++ ++ return 0; ++} ++ ++/* Check dq whether valid and set mask to reduce search time */ ++static int ddr_dataeye_check_dir(unsigned int direction, unsigned int left, ++ unsigned int right, unsigned int *mask, const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ ++ result = ddr_dataeye_check_dq(cfg); ++ switch (direction) { ++ case DDR_FIND_DQ_BOTH: ++ *mask = DDR_FIND_DQ_LEFT | DDR_FIND_DQ_RIGHT; ++ break; ++ case DDR_FIND_DQ_LEFT: ++ if (result) { ++ /* ddr test error, search opposite side */ ++ *mask = DDR_FIND_DQ_RIGHT; ++ } else { /* ddr test ok */ ++ ddr_phy_set_dq_bdl(cfg, left); ++ if (!ddr_dataeye_check_dq(cfg)) ++ /* test ok, go on search this side */ ++ *mask = DDR_FIND_DQ_LEFT; ++ } ++ break; ++ case DDR_FIND_DQ_RIGHT: ++ if (result) { /* ddr test error, search opposite side */ ++ *mask = DDR_FIND_DQ_LEFT; ++ } else { /* ddr test ok */ ++ ddr_phy_set_dq_bdl(cfg, right); ++ if (!ddr_dataeye_check_dq(cfg)) ++ /* test OK, go on search this side */ ++ *mask = DDR_FIND_DQ_RIGHT; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return result; ++} ++ ++/* Binary search the valid dq bdl */ ++static void ddr_dataeye_search_dq(unsigned int left, unsigned int right, ++ int *target, unsigned int direction, const struct ddr_cfg_st *cfg) ++{ ++ unsigned int middle; ++ unsigned int mask = 0; ++ ++ middle = left + ((right - left) >> 1); ++ ++ ddr_phy_set_dq_bdl(cfg, middle); ++ if (!ddr_dataeye_check_dir(direction, left, right, &mask, cfg)) { /* test ok */ ++ *target = (int)middle; ++ return; ++ } ++ ++ if (left == middle || middle == right) /* not found */ ++ return; ++ ++ /* find left side */ ++ if (DDR_FIND_DQ_LEFT & mask) ++ ddr_dataeye_search_dq(left, middle, target, direction, cfg); ++ ++ /* find right side */ ++ if (DDR_FIND_DQ_RIGHT & mask) ++ ddr_dataeye_search_dq(middle, right, target, direction, cfg); ++ ++ return; ++} ++ ++/* Find DQ valid range */ ++static void ddr_dataeye_find_dq(const struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ int cur_dq, left_dq, right_dq, def_dq; ++ unsigned int dq_num; ++ unsigned int win_num; ++ ++ dq_num = (cfg->cur_byte << DDR_BYTE_DQ) + cfg->cur_dq; ++ def_dq = (int)ddr_phy_get_dq_bdl(cfg); ++ cur_dq = def_dq; ++ ++ /* check default dq */ ++ if (ddr_dataeye_check_dq(cfg)) { ++ /* test error */ ++ cur_dq = -1; ++ ddr_dataeye_search_dq(0, PHY_BDL_MASK, &cur_dq, DDR_FIND_DQ_BOTH, cfg); ++ ddr_debug("DQ[%x] def[%x] not ok, find new value[%x]", dq_num, def_dq, cur_dq); ++ if (cur_dq == -1) { /* no valid dq */ ++ training->ddr_bit_result[dq_num] = 0; ++ training->ddr_bit_best[dq_num] = 0; ++ /* restore default value */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ddr_warning("DQ[%x] not found dq. restore[%x]", dq_num, def_dq); ++ return; ++ } ++ } ++ /* find the left boundary */ ++ left_dq = cur_dq; ++ ddr_dataeye_search_dq(0, cur_dq, &left_dq, DDR_FIND_DQ_LEFT, cfg); ++ while (left_dq > 0) { ++ left_dq--; ++ ddr_phy_set_dq_bdl(cfg, left_dq); ++ if (ddr_dataeye_check_dq(cfg)) { ++ /* test error */ ++ left_dq++; ++ break; ++ } ++ } ++ /* find the right boundary */ ++ right_dq = cur_dq; ++ ddr_dataeye_search_dq(cur_dq, PHY_BDL_MASK, &right_dq, DDR_FIND_DQ_RIGHT, cfg); ++ while (right_dq < PHY_BDL_MASK) { ++ right_dq++; ++ ddr_phy_set_dq_bdl(cfg, right_dq); ++ if (ddr_dataeye_check_dq(cfg)) { ++ /* test error */ ++ right_dq--; ++ break; ++ } ++ } ++ /* reset dq */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ++ /* ++ * 0 1 2 3 4 5 6 7 8 9 ++ * x x - - - - - x x x ++ * | | ++ * left_dq right_dq ++ * ++ * so left_dq = 2, right_dq = 6 ++ */ ++ win_num = right_dq - left_dq + 1; ++ training->ddr_bit_result[dq_num] = ((unsigned int)left_dq << DDR_DATAEYE_RESULT_BIT) | ++ (unsigned int)right_dq; ++ training->ddr_bit_best[dq_num] = (win_num << DDR_DATAEYE_RESULT_BIT) | ++ ((win_num >> 1) + (unsigned int)left_dq); ++ ++ ddr_info("DQ[%x] range: left[%x] right[%x] best[%x] mode[%x] rank[%x]", dq_num, ++ left_dq, right_dq, training->ddr_bit_best[dq_num], cfg->cur_mode, cfg->rank_idx); ++} ++ ++/* DDR dataeye training one byte. */ ++int ddr_dataeye_deskew(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int dq_num; ++ unsigned int loop_times = 0; ++ unsigned int win_num, dq_sum; ++ unsigned int def_dq, best_dq; ++ int i; ++ ++ if (cfg == NULL || training == NULL) { ++ ddr_error("Pointer parameter is NULL"); ++ return -1; ++ } ++ dq_sum = 0; ++ training->ddr_win_sum = 0; ++ for (i = 0; i < DDR_PHY_BIT_NUM; i++) { ++ cfg->cur_dq = i; ++ dq_num = (cfg->cur_byte << DDR_BYTE_DQ) + i; ++ if (dq_num >= DDR_PHY_BIT_MAX) { ++ ddr_error("Array index dq_num out of range"); ++ return -1; ++ } ++ def_dq = ddr_phy_get_dq_bdl(cfg); ++ ddr_dataeye_find_dq(cfg, training); ++ win_num = training->ddr_bit_best[dq_num] >> DDR_DATAEYE_RESULT_BIT; ++ best_dq = training->ddr_bit_best[dq_num] & DDR_DATAEYE_RESULT_MASK; ++ /* check window number */ ++ if (win_num < DDR_DATAEYE_WIN_NUM) { ++ if (loop_times < DDR_LOOP_TIMES_LMT) { ++ loop_times++; ++ i--; ++ continue; ++ } else if (win_num == 0) { ++ ddr_warning("Byte[%x] DQ[%x] no win", cfg->cur_byte, dq_num); ++ /* restore default value */ ++ ddr_phy_set_dq_bdl(cfg, def_dq); ++ ddr_training_stat(DDR_ERR_DATAEYE, cfg->cur_phy, cfg->cur_byte, i); ++ continue; ++ } ++ } ++ loop_times = 0; ++ ddr_phy_set_dq_bdl(cfg, best_dq); ++ dq_sum = dq_sum + best_dq; ++ training->ddr_win_sum = training->ddr_win_sum + win_num; ++ } ++ dq_sum = dq_sum >> DDR_BYTE_DQ; ++ ++ /* only DDR_MODE_WRITE need to set */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) ++ reg_write((dq_sum & PHY_BDL_MASK) << PHY_WDM_BDL_BIT, cfg->cur_phy + ++ ddr_phy_dxnwdqnbdl2(cfg->rank_idx, cfg->cur_byte)); ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++ ++ return 0; ++} ++ ++/* DDR write or read dataeye training */ ++static int ddr_dataeye_process(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ int result = 0; ++ unsigned int i; ++ ++ if (cfg->phy_idx >= DDR_PHY_NUM || cfg->dmc_idx >= DDR_DMC_PER_PHY_MAX) { ++ ddr_error("Array index phy_idx or dmc_idx out of range!"); ++ return -1; ++ } ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) { ++ ddr_error("get byte num fail, byte_num = %x", get_byte_num(cfg)); ++ return -1; ++ } ++ /* dataeye training */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ result += ddr_dataeye_deskew(cfg, training); ++ } ++ ++ if (result) { ++ result = -1; ++ ddr_error("PHY[%x] mode[%x] dataeye training fail", cfg->cur_phy, cfg->cur_mode); ++ } else { ++ /* dataeye training result adjust */ ++ ddr_adjust_dataeye(cfg, training); ++ } ++ /* save training result to printf */ ++ ddr_result_data_save(cfg, training); ++ ++ return result; ++} ++ ++int ddr_dataeye_training(struct ddr_cfg_st *cfg) ++{ ++ struct training_data tmp_result; ++ struct training_data *training = &tmp_result; ++ int result_read, result_write; ++ ++ ddr_debug("DDR dataeye training PHY[%x][%x] DMC[%x][%x] Rank[%x]", ++ cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx); ++ ++ /* write dataeye training */ ++ cfg->cur_mode = DDR_MODE_WRITE; ++ memset(training, 0, sizeof(struct training_data)); ++ result_write = ddr_dataeye_process(cfg, training); ++ ++ /* read dataeye training */ ++ cfg->cur_mode = DDR_MODE_READ; ++ memset(training, 0, sizeof(struct training_data)); ++ result_read = ddr_dataeye_process(cfg, training); ++ if (result_read || result_write) ++ return -1; ++ else ++ return 0; ++} ++ ++int ddr_dataeye_training_func(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result; ++ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL!"); ++ return -1; ++ } ++ /* dataeye training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_DATAEYE_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_DATAEYE_MASK); ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ cfg->adjust = DDR_DATAEYE_NORMAL_ADJUST; ++ cfg->dq_check_type = DDR_CHECK_TYPE_DDRT; ++ result = ddr_dataeye_training(cfg); ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_dataeye_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR dataeye training"); ++ ++ return 0; ++} ++#endif /* DDR_DATAEYE_TRAINING_CONFIG */ ++ ++#define __HARDWARE_TRAINING__ ++#ifdef DDR_HW_TRAINING_CONFIG ++#ifdef DDR_HW_READ_ADJ_CONFIG ++/* ++ * Adjust rdqs and dq after hw read training. ++ * When define DDR_TRAINING_ADJUST_DISABLE, MUST define DDR_HW_READ_ADJ_CONFIG. ++ */ ++static void ddr_hw_read_adj(const struct ddr_cfg_st *cfg) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ ddr_debug("DDR hw read adjust"); ++ /* check hw read adjust bypass bit */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_HW_ADJ_MASK) != DDR_FALSE) ++ return; ++ ++ /* assume read dataeye window on left */ ++ for (i = 0; i < byte_num; i++) { ++ reg_write(reg_read(base_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, i)) + ++ (PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT), ++ base_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, i)); ++ reg_write(reg_read(base_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, i)) + ++ (PHY_DQ_MIDDLE_VAL << PHY_BDL_DQ_BIT), ++ base_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, i)); ++ reg_write(reg_read(base_phy + ddr_phy_dxnrdqsdly(i)) + ++ (PHY_RDQS_MIDDLE_VAL << PHY_RDQS_BDL_BIT), ++ base_phy + ddr_phy_dxnrdqsdly(i)); ++ } ++} ++#else ++static void ddr_hw_read_adj(const struct ddr_cfg_st *cfg) ++{ ++} ++#endif /* DDR_HW_READ_ADJ_CONFIG */ ++ ++static void ddr_training_get_rdqs(const struct ddr_cfg_st *cfg, struct ddr_bdl_st *rdqs) ++{ ++ unsigned int i; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) ++ rdqs->bdl[i] = reg_read(base_phy + ddr_phy_dxnrdqsdly(i)); ++} ++ ++static void ddr_training_set_rdqs(const struct ddr_cfg_st *cfg, const struct ddr_bdl_st *rdqs) ++{ ++ unsigned int i; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) ++ reg_write(rdqs->bdl[i], base_phy + ddr_phy_dxnrdqsdly(i)); ++} ++ ++static void ddr_hw_training_adjust_rdqs(struct ddr_cfg_st *cfg, const struct rdqs_data_st *rdqs_st) ++{ ++ unsigned int i; ++ unsigned int byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ unsigned int rdqs_rank0, rdqs_rank1; ++ unsigned int cur_rank = cfg->rank_idx; ++ int offset; ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return; ++ } ++ for (i = 0; i < byte_num; i++) { ++ /* struct rdqs_data_st store the whole register value */ ++ rdqs_rank0 = (rdqs_st->rank[0].bdl[i] >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ rdqs_rank1 = (rdqs_st->rank[1].bdl[i] >> PHY_RDQS_BDL_BIT) & PHY_RDQS_BDL_MASK; ++ ++ cfg->cur_byte = i; ++ if (rdqs_rank0 > rdqs_rank1) { ++ offset = rdqs_rank0 - rdqs_rank1; ++ reg_write(rdqs_st->rank[0].bdl[i], cfg->cur_phy + ddr_phy_dxnrdqsdly(i)); ++ cfg->rank_idx = 1; /* switch to rank1 for sync rank1 rdq */ ++ } else { ++ offset = rdqs_rank1 - rdqs_rank0; ++ reg_write(rdqs_st->rank[1].bdl[i], cfg->cur_phy + ddr_phy_dxnrdqsdly(i)); ++ cfg->rank_idx = 0; /* switch to rank0 for sync rank0 rdq */ ++ } ++ ddr_rdqs_sync_rank_rdq(cfg, offset); ++ } ++ cfg->rank_idx = cur_rank; /* restore to current rank */ ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++} ++ ++/* DDR HW training process */ ++int ddr_hw_training_process(const struct ddr_cfg_st *cfg, unsigned int item) ++{ ++ unsigned int count; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int init_ctrl = reg_read(base_phy + DDR_PHY_PHYINITCTRL); ++ ++ if (!item) ++ return 0; ++ ++ ddr_debug("base_phy[%x] itme[%x]", base_phy, item); ++ /* hardware training enable */ ++ reg_write(item | PHY_PHYINITCTRL_INIT_EN | init_ctrl, base_phy + DDR_PHY_PHYINITCTRL); ++ ++ if ((item & PHY_PHYINITCTRL_DRAM_RST) && (item & PHY_PHYINITCTRL_DRAM_INIT_EN)) { ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ } ++ ++ count = DDR_HWR_WAIT_TIMEOUT; ++ /* auto cleared to 0 after training finished */ ++ while (count--) { ++ if (!(reg_read(base_phy + DDR_PHY_PHYINITCTRL) & PHY_PHYINITCTRL_MASK)) ++ break; ++ } ++ ++ if (count == 0xffffffff) { ++ ddr_fatal("HWR wait timeout"); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, item, reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ return -1; ++ } ++ ++ if (reg_read(base_phy + DDR_PHY_PHYINITSTATUS)) { ++ ddr_fatal("Phy[%x] hw[%x] failed[%x]", base_phy, item, reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, item, reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Dataeye hardware training */ ++int ddr_hw_dataeye_read(struct ddr_cfg_st *cfg) ++{ ++ unsigned int base_phy; ++ unsigned int byte_num; ++ ++ unsigned int i; ++ int result; ++ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL!"); ++ return -1; ++ } ++ base_phy = cfg->cur_phy; ++ byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ ddr_training_cfg_init(cfg); ++ ++ if (byte_num > DDR_PHY_BYTE_MAX) { ++ ddr_error("byte num error, byte_num = %x", byte_num); ++ return -1; ++ } ++ /* clear */ ++ for (i = 0; i < byte_num; i++) { ++ reg_write(0, base_phy + ddr_phy_dxnrdqnbdl0(cfg->rank_idx, i)); ++ reg_write(0, base_phy + ddr_phy_dxnrdqnbdl1(cfg->rank_idx, i)); ++ reg_write(0, base_phy + ddr_phy_dxnrdqsdly(i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ ++ result = ddr_hw_training_process(cfg, PHY_PHYINITCTRL_RDET_EN); ++ ++ ddr_hw_read_adj(cfg); ++ ++ return result; ++} ++ ++/* ca odt disable, DRAM_RST and DRAM_INIT are required to take effect ++ * The DRAM_RST cannot be performed more than once ++ */ ++static int ddr_hw_ca_odt_disable(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int temp; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ temp = reg_read(base_phy + DDR_PHY_MODEREG01); ++ reg_write(temp & 0x8fffffff, base_phy + DDR_PHY_MODEREG01); /* ca odt disable:bit[30:28] set 0 */ ++ result = ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_DRAM_RESET); ++ ++ reg_write(temp, base_phy + DDR_PHY_MODEREG01); /* restore */ ++ ++ return result; ++} ++ ++/* CA Vref Sync, rank0 and rank1 */ ++static int ddr_hw_ca_vref_sync(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int temp; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int item = cfg->cur_item; ++ ++ temp = reg_read(base_phy + DDR_PHY_TRAINCTRL0); ++ reg_write(temp & (~PHY_TRAINCTRL0_MASK), base_phy + DDR_PHY_TRAINCTRL0); /* select rank0 */ ++ result = ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_AC); ++ ++ reg_write((temp & (~PHY_TRAINCTRL0_MASK)) | 0x1, ++ base_phy + DDR_PHY_TRAINCTRL0); /* select rank1 */ ++ result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_AC); ++ ++ reg_write(temp, base_phy + DDR_PHY_TRAINCTRL0); /* restore */ ++ ++ return result; ++} ++ ++static int ddr_hw_dram_mr_init(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int tx_odt_mode; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int item = cfg->cur_item; ++ ++ tx_odt_mode = (reg_read(base_phy + DDR_PHY_ACIOCTL) >> PHY_AC_IOCTL_TX_MODE_BIT) & ++ PHY_AC_IOCTL_TX_MODE_MASK; ++ if (tx_odt_mode == DDR_PHY_LPDDR4X_MODE) { ++ unsigned int temp; ++ unsigned int temp1; ++ ++ /* rank0 */ ++ temp = reg_read(base_phy + DDR_PHY_RANKEN); ++ reg_write((temp & (~DDR_PHY_RANKEN_MASK)) | 0x1, ++ base_phy + DDR_PHY_RANKEN); /* select rank0 */ ++ ++ temp1 = reg_read(base_phy + DDR_PHY_MODEREG23); /* store the contents of the Mode Register */ ++ reg_write(temp1 & 0xffffffc7, base_phy + DDR_PHY_MODEREG23); /* rank0 ck/cs/ca odt enable */ ++ result = ddr_hw_training_process(cfg, item & PHY_PHYINITCTRL_DRAM_INIT_EN); /* rank0 draminit */ ++ /* restore */ ++ reg_write(temp, base_phy + DDR_PHY_RANKEN); ++ reg_write(temp1, base_phy + DDR_PHY_MODEREG23); ++ ++ /* rank1 */ ++ temp = reg_read(base_phy + DDR_PHY_RANKEN); ++ reg_write((temp & (~DDR_PHY_RANKEN_MASK)) | 0x2, /* 0x2:bit1 set 1 */ ++ base_phy + DDR_PHY_RANKEN); /* select rank1 */ ++ ++ temp1 = reg_read(base_phy + DDR_PHY_MODEREG23); ++ reg_write((temp1 & 0xffffffc7) | 0x28, base_phy + DDR_PHY_MODEREG23); /* rank1 ck/caodt diable, rank1 cs odt enable */ ++ result += ddr_hw_training_process(cfg, item & PHY_PHYINITCTRL_DRAM_INIT_EN); ++ /* restore */ ++ reg_write(temp, base_phy + DDR_PHY_RANKEN); ++ reg_write(temp1, base_phy + DDR_PHY_MODEREG23); ++ } else { ++ result = ddr_hw_training_process(cfg, item & PHY_PHYINITCTRL_DRAM_INIT_EN); ++ } ++ ++ return result; ++} ++ ++/* DDR HW training adapt dram type */ ++static int ddr_hw_dataeye_adapt(const struct ddr_cfg_st *cfg, struct ddr_tmp_st *ddr_temp) ++{ ++ int result; ++ unsigned int dramtimer1 = 0; ++ unsigned int modereg67; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ dramtimer1 = reg_read(base_phy + DDR_PHY_DRAMTIMER1); ++ reg_write(dramtimer1 & (~(DDR_PHY_T_MOD_MASK << DDR_PHY_T_MOD_BIT)), ++ base_phy + DDR_PHY_DRAMTIMER1); /* TMOD:0 */ ++ ++ result = ddr_hw_ca_odt_disable(cfg); /* CA odt disable */ ++ result += ddr_hw_ca_vref_sync(cfg); /* CA vref sync */ ++ result += ddr_hw_dram_mr_init(cfg); /* in WR0 */ ++ ++ modereg67 = reg_read(base_phy + DDR_PHY_MODEREG67); ++ /* turn to WR1 */ ++ reg_write(modereg67 | (0x1 << PHY_MODEREG67_LP4_FSPWR_BIT), ++ base_phy + DDR_PHY_MODEREG67); /* bit6 set 1 */ ++ result += ddr_hw_dram_mr_init(cfg); ++ result += ddr_hw_ca_vref_sync(cfg); /* CA vref sync */ ++ ++ /* turn to WR0 */ ++ reg_write(modereg67 & (~(0x1 << PHY_MODEREG67_LP4_FSPWR_BIT)), ++ base_phy + DDR_PHY_MODEREG67); /* bit6 set 0 */ ++ result += ddr_hw_dram_mr_init(cfg); ++ ++ /* restore DRAMTIMER1 */ ++ reg_write(dramtimer1, base_phy + DDR_PHY_DRAMTIMER1); ++ } else { ++#ifdef DDR_WRITE_DM_DISABLE ++ unsigned int modereg45 = 0; ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_DDR4) { ++ modereg45 = reg_read(base_phy + DDR_PHY_MODEREG45); ++ reg_write((modereg45 & 0xFBFFFFFF) | 0x8000000, base_phy + DDR_PHY_MODEREG45); /* write dm disable */ ++ } ++ ddr_temp->temp = modereg45; /* for restore 0xe0 in ddr_hw_training_ctl */ ++#else ++ ddr_temp->temp = 0; ++#endif ++ result = ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_DRAM_RESET); ++ } ++ ++ return result; ++} ++ ++static int ddr_hw_dataeye_vref_set(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int item = cfg->cur_item; ++ unsigned int dvrft_ctrl; ++ ++ dvrft_ctrl = reg_read(base_phy + DDR_PHY_DVRFTCTRL); ++ reg_write(dvrft_ctrl & (~PHY_DVRFTCTRL_PDAEN_EN), base_phy + DDR_PHY_DVRFTCTRL); ++ /* DDR_PHY_VREFTCTRL 31bit:1 do vref dram set twice */ ++ reg_write((reg_read(base_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT))) | ++ (PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT), ++ base_phy + DDR_PHY_VREFTCTRL); ++ result = ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ); ++ result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ); ++ /* DDR_PHY_VREFTCTRL 31bit:0 do vref dram set once */ ++ reg_write(reg_read(base_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT)), ++ base_phy + DDR_PHY_VREFTCTRL); ++ result += ddr_hw_training_process(cfg, item & PHY_HW_GP_VREF_DQ); ++ reg_write(dvrft_ctrl, base_phy + DDR_PHY_DVRFTCTRL); ++ ++ return result; ++} ++ ++#ifdef DDR_WRITE_DM_DISABLE ++static int ddr_hw_write_dm_disable(const struct ddr_cfg_st *cfg, const struct ddr_tmp_st *ddr_temp) ++{ ++ int result = 0; ++ unsigned int temp; ++ unsigned int temp1; ++ ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_DDR4) { ++ reg_write(ddr_temp->temp, cfg->cur_phy + DDR_PHY_MODEREG45); /* restore */ ++ temp = reg_read(cfg->cur_phy + DDR_PHY_MRS_SEQ_PROG); ++ temp1 = reg_read(cfg->cur_phy + DDR_PHY_DRAMCFG); ++ reg_write(PHY_MRS_SEQ_PROG_VAL, cfg->cur_phy + DDR_PHY_MRS_SEQ_PROG); /* inti MR5 */ ++ reg_write(temp1 | PHY_WDM_DISABLE_VAL, cfg->cur_phy + DDR_PHY_DRAMCFG); /* write dm disable */ ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_DRAM_INIT_EN); ++ reg_write(temp, cfg->cur_phy + DDR_PHY_MRS_SEQ_PROG); /* restore */ ++ reg_write(temp1, cfg->cur_phy + DDR_PHY_DRAMCFG); /* restore */ ++ } ++ ++ return result; ++} ++#endif ++ ++/* sync rank1 WDQSPH/WDQPH to rank0 */ ++static void ddr_set_rank1_wdq_to_rank0(unsigned int base_phy, unsigned int byte_num) ++{ ++ unsigned int byte_idx; ++ ++ for (byte_idx = 0; byte_idx < byte_num; byte_idx++) { ++ reg_write(reg_read(base_phy + ddr_phy_dxwdqsdly(1, byte_idx)), ++ base_phy + ddr_phy_dxwdqsdly(0, byte_idx)); ++ reg_write(reg_read(base_phy + ddr_phy_dxnwdqdly(1, byte_idx)), ++ base_phy + ddr_phy_dxnwdqdly(0, byte_idx)); ++ } ++ ddr_phy_cfg_update(base_phy); ++} ++ ++/* This function is used to prevent logic bugs */ ++static int ddr_hw_training_normal_conf(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int byte_idx; ++ unsigned int byte_num; ++ unsigned int base_phy; ++ struct tr_dq_byte_st wdq_rank0_byte; ++ ++ memset(&wdq_rank0_byte, 0, sizeof(struct tr_dq_byte_st)); ++ base_phy = cfg->cur_phy; ++ byte_num = cfg->phy[cfg->phy_idx].total_byte_num; ++ ++ if (cfg->rank_idx == 0) { ++ result = ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_NORMAL); ++ } else { /* rank1 */ ++ /* save rank0 WDQSPH/WDQPH of all byte */ ++ for (byte_idx = 0; byte_idx < byte_num; byte_idx++) ++ ddr_get_dly_value(&wdq_rank0_byte.dq_val[byte_idx], base_phy, 0, byte_idx); ++ /* WL */ ++ result = ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_WL_EN); ++ /* sync rank1 WDQSPH/WDQPH to rank0 */ ++ ddr_set_rank1_wdq_to_rank0(base_phy, byte_num); ++ ++ /* GATE/GDS/WL2/RDET/WDET */ ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_NORMAL_RANK1); ++ /* sync rank1 WDQSPH/WDQPH to rank0 */ ++ ddr_set_rank1_wdq_to_rank0(base_phy, byte_num); ++ ++ /* HVREFT/DVREFT */ ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_HVREFT_EN); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_DVREFT_EN); ++ /* sync rank1 WDQSPH/WDQPH to rank0 */ ++ ddr_set_rank1_wdq_to_rank0(base_phy, byte_num); ++ ++ /* TDQSST */ ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_PIC_TDQSST); ++ ++ /* restore rank0 WDQSPH/WDQPH of all byte */ ++ for (byte_idx = 0; byte_idx < byte_num; byte_idx++) ++ ddr_restore_dly_value(&wdq_rank0_byte.dq_val[byte_idx], base_phy, 0, byte_idx); ++ } ++ ++ return result; ++} ++ ++void ddr_ck_cfg(unsigned int base_phy) ++{ ++ unsigned int acphyctl7, acphyctl7_tmp; ++ unsigned int ck0, ck1; ++ ++ acphyctl7 = reg_read(base_phy + DDR_PHY_ACPHYCTL7); ++ ck0 = (acphyctl7 >> PHY_ACPHY_DCLK0_BIT) & PHY_ACPHY_DCLK_MASK; ++ ck1 = (acphyctl7 >> PHY_ACPHY_DCLK1_BIT) & PHY_ACPHY_DCLK_MASK; ++ /* clear ck0 ck1 */ ++ acphyctl7_tmp = acphyctl7 & (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK0_BIT)) & ++ (~(PHY_ACPHY_DCLK_MASK << PHY_ACPHY_DCLK1_BIT)); ++ /* set the opposite val of ck */ ++ reg_write(acphyctl7_tmp | ((~ck0) << PHY_ACPHY_DCLK0_BIT) | ((~ck1) << PHY_ACPHY_DCLK1_BIT), ++ base_phy + DDR_PHY_ACPHYCTL7); ++ /* restore acphyctl7 */ ++ reg_write(acphyctl7, base_phy + DDR_PHY_ACPHYCTL7); ++ ++ ddr_phy_cfg_update(base_phy); ++} ++ ++/* DDR HW training control */ ++static int ddr_hw_training_ctl(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int byte_idx; ++ unsigned int temp; ++ unsigned int base_phy = cfg->cur_phy; ++ struct ddr_tmp_st ddr_temp; ++ struct rdqs_data_st *rdqs_st = NULL; ++ struct ddr_bdl_dly_st bdl_dly_s; ++ ++ rdqs_st = (struct rdqs_data_st *)cfg->res_st; ++ ++ if (cfg->cur_item == 0 || rdqs_st == NULL) ++ return 0; ++ ++ ddr_phy_cfg_update(base_phy); ++ /* NOTE: not support array when boot */ ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_CNT_RESET_START); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_PLL); ++ ++ ddr_ck_cfg(base_phy); ++ ++ /* save rdqs bdl after PHY_PHYINITCTRL_DLYMEAS_EN */ ++ if (cfg->rank_idx == 0) { ++ ddr_training_get_rdqs(cfg, &rdqs_st->origin); ++ temp = ((reg_read(base_phy + ddr_phy_dxnrdqsdly(0)) >> PHY_RDQS_CYC_BIT) & ++ PHY_RDQS_CYC_MASK) >> 2; /* right shift 2: 1/4T */ ++ temp = (reg_read(base_phy + DDR_PHY_TRAINCTRL12) & (~(PHY_WL_FALLEDGE_BDL_JSTEP_R_MASK << ++ PHY_WL_FALLEDGE_BDL_JSTEP_R_BIT))) | (temp << PHY_WL_FALLEDGE_BDL_JSTEP_R_BIT); ++ reg_write(temp, base_phy + DDR_PHY_TRAINCTRL12); ++ } ++ if ((cfg->phy_idx >= DDR_PHY_NUM) || (cfg->phy[cfg->phy_idx].total_byte_num > DDR_PHY_BYTE_MAX)) { ++ ddr_error("phy_idx or byte number error"); ++ return -1; ++ } ++ for (byte_idx = 0; byte_idx < (cfg->phy[cfg->phy_idx].total_byte_num); byte_idx++) { ++ cfg->cur_byte = byte_idx; ++ ddr_rdqbdl_adj(cfg, &bdl_dly_s); ++ } ++ result += ddr_hw_dataeye_adapt(cfg, &ddr_temp); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_PHYINITCTRL_CAT_EN); ++ result += ddr_hw_training_process(cfg, cfg->cur_item & PHY_HW_GP_CS); ++ result += ddr_hw_dataeye_vref_set(cfg); ++ result += ddr_hw_training_normal_conf(cfg); ++ ++#ifdef DDR_WRITE_DM_DISABLE ++ result += ddr_hw_write_dm_disable(cfg, &ddr_temp); ++#endif ++ ddr_phy_cfg_update(base_phy); ++ ++ return result; ++} ++ ++static int ddr_hw_training_by_rank(struct ddr_cfg_st *cfg) ++{ ++ ddr_debug("PHY[%x][%x] Rank[%x] itme[%x]", ++ cfg->phy_idx, cfg->cur_phy, cfg->rank_idx, cfg->cur_item); ++ ++ /* 0:PHY_TRAINCTRL0_DTR_RANK0, 1:PHY_TRAINCTRL0_DTR_RANK1 */ ++ ddr_phy_switch_rank(cfg->cur_phy, cfg->rank_idx); ++ ++ return ddr_hw_training_ctl(cfg); ++} ++ ++int ddr_hw_training_by_phy(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ struct rdqs_data_st rdqs_data; ++ struct rdqs_data_st *rdqs_st = &rdqs_data; ++ struct ddr_timing_st timing_st; ++ unsigned int rank_num = cfg->phy[cfg->phy_idx].rank_num; ++ ++ cfg->res_st = rdqs_st; ++ ++ /* disable auto refresh */ ++ ddr_training_save_timing(cfg, &timing_st); ++ ++ if (rank_num > DDR_SUPPORT_RANK_MAX) { ++ ddr_error("loop upper limit rank number out of range, rank_num = %x", rank_num); ++ return -1; ++ } ++ for (i = 0; i < rank_num; i++) { ++ cfg->rank_idx = i; ++ if (cfg->phy_idx >= DDR_PHY_NUM) { ++ ddr_error("Array index phy_idx out of range!"); ++ return -1; ++ } ++ cfg->cur_item = cfg->phy[cfg->phy_idx].rank[i].item_hw; ++ ++ result += ddr_hw_training_by_rank(cfg); ++ ++ if (rank_num != DDR_SUPPORT_RANK_MAX) ++ break; ++ ++ /* save rank rdqs bdl */ ++ ddr_training_get_rdqs(cfg, &(rdqs_st->rank[i])); ++ ++ /* restore PHY_PHYINITCTRL_DLYMEAS_EN rdqs before training next rank */ ++ if ((rank_num - 1) != i) ++ ddr_training_set_rdqs(cfg, &(rdqs_st->origin)); ++ } ++ ++ if (rank_num == DDR_SUPPORT_RANK_MAX) { ++ ddr_hw_training_adjust_rdqs(cfg, rdqs_st); ++ ddr_training_adjust_wdq(cfg); ++ ddr_training_adjust_wdqs(cfg); ++ ddr_phy_switch_rank(cfg->cur_phy, 0x0); /* switch to rank0 */ ++ } ++ ++ /* restore auto refresh */ ++ ddr_training_restore_timing(cfg, &timing_st); ++ ++ cfg->res_st = 0; ++ ++ return result; ++} ++ ++/* DDR hardware training */ ++int ddr_hw_training(struct ddr_cfg_st *cfg) ++{ ++ int result = 0; ++ unsigned int i; ++ struct tr_custom_reg reg; ++ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL!"); ++ return -1; ++ } ++ ++ memset(®, 0, sizeof(struct tr_custom_reg)); ++ /* save customer reg */ ++ ddr_boot_cmd_save(®); ++ if (cfg->phy_num > DDR_PHY_NUM) { ++ ddr_error("loop upper limit cfg->phy_num out of range!"); ++ return -1; ++ } ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ cfg->cur_phy = cfg->phy[i].addr; ++ result += ddr_hw_training_by_phy(cfg); ++ } ++ /* restore customer reg */ ++ ddr_boot_cmd_restore(®); ++ ++ return result; ++} ++#endif /* DDR_HW_TRAINING_CONFIG */ ++ ++#define __VREF_TRAINING__ ++#ifdef DDR_VREF_TRAINING_CONFIG ++#ifdef DDR_VREF_WITHOUT_BDL_CONFIG ++/* Save dataeye dq bdl before vref training */ ++static void ddr_vref_save_bdl(const struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank = cfg->rank_idx; ++ unsigned int byte_index; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ byte_index = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ dq_data->dq03[i] = reg_read(base_phy + ddr_phy_dxnwdqnbdl0(rank, byte_index)); ++ dq_data->dq47[i] = reg_read(base_phy + ddr_phy_dxnwdqnbdl1(rank, byte_index)); ++ dq_data->wdm[i] = reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank, byte_index)); ++ } else { ++ dq_data->dq03[i] = reg_read(base_phy + ddr_phy_dxnrdqnbdl0(rank, byte_index)); ++ dq_data->dq47[i] = reg_read(base_phy + ddr_phy_dxnrdqnbdl1(rank, byte_index)); ++ } ++ } ++} ++ ++/* Restore dataeye dq bdl after vref training */ ++static void ddr_vref_restore_bdl(const struct ddr_cfg_st *cfg, const struct tr_dq_data *dq_data) ++{ ++ int i; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank = cfg->rank_idx; ++ unsigned int byte_index; ++ ++ if ((cfg->phy_idx >= DDR_PHY_NUM) || (cfg->dmc_idx >= DDR_DMC_PER_PHY_MAX)) ++ return; ++ ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) ++ return; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ byte_index = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ if (cfg->cur_mode == DDR_MODE_WRITE) { ++ reg_write(dq_data->dq03[i], base_phy + ddr_phy_dxnwdqnbdl0(rank, byte_index)); ++ reg_write(dq_data->dq47[i], base_phy + ddr_phy_dxnwdqnbdl1(rank, byte_index)); ++ reg_write(dq_data->wdm[i], base_phy + ddr_phy_dxnwdqnbdl2(rank, byte_index)); ++ } else { ++ reg_write(dq_data->dq03[i], base_phy + ddr_phy_dxnrdqnbdl0(rank, byte_index)); ++ reg_write(dq_data->dq47[i], base_phy + ddr_phy_dxnrdqnbdl1(rank, byte_index)); ++ } ++ } ++} ++#else ++static void ddr_vref_save_bdl(const struct ddr_cfg_st *cfg, struct tr_dq_data *dq_data) ++{ ++} ++static void ddr_vref_restore_bdl(const struct ddr_cfg_st *cfg, const struct tr_dq_data *dq_data) ++{ ++} ++#endif /* DDR_VREF_WITHOUT_BDL_CONFIG */ ++ ++/* phy s40 not support DRAM vref */ ++static int ddr_vref_dram_set_process(unsigned int base_phy, unsigned int val, unsigned int byte_index) ++{ ++ unsigned int count; ++ unsigned int dvrftctrl = reg_read(base_phy + DDR_PHY_DVRFTCTRL); ++ unsigned int dvreft = reg_read(base_phy + ddr_phy_dvreft_status(byte_index)) & ++ (~PHY_VRFTRES_DVREF_MASK); ++ ++ reg_write(dvrftctrl | PHY_DVRFTCTRL_PDAEN_EN, base_phy + DDR_PHY_DVRFTCTRL); ++ reg_write(dvreft | val, base_phy + ddr_phy_dvreft_status(byte_index)); ++ reg_write(PHY_PHYINITCTRL_DVREFT_SYNC | PHY_PHYINITCTRL_INIT_EN, ++ base_phy + DDR_PHY_PHYINITCTRL); ++ ++ count = DDR_HWR_WAIT_TIMEOUT; ++ /* auto cleared to 0 after training finished */ ++ while (count--) { ++ if (!(reg_read(base_phy + DDR_PHY_PHYINITCTRL) & PHY_PHYINITCTRL_INIT_EN)) ++ break; ++ } ++ ++ if (count == 0xffffffff) { ++ ddr_fatal("vref dram set wait timeout"); ++ ddr_training_stat(DDR_ERR_HW_RD_DATAEYE, base_phy, byte_index, ++ reg_read(base_phy + DDR_PHY_PHYINITSTATUS)); ++ return -1; ++ } ++ ++ reg_write(dvrftctrl & (~PHY_DVRFTCTRL_PDAEN_EN), base_phy + DDR_PHY_DVRFTCTRL); ++ ++ return 0; ++} ++ ++#if defined(DDR_PHY_T12_V100_CONFIG) || defined(DDR_PHY_T12_V101_CONFIG) || defined(DDR_PHY_S14_CONFIG) ++static void ddr_phy_vref_host_set_process(unsigned int base_phy, unsigned int rank, ++ unsigned int bytenum, unsigned int byte_index, unsigned int val) ++{ ++ unsigned int hvreft; ++ ++ if (rank == 0) { ++ hvreft = reg_read(base_phy + ddr_phy_hvreft_status(rank, byte_index)) & ++ (~PHY_VRFTRES_HVREF_MASK); ++ reg_write(hvreft | val, base_phy + ddr_phy_hvreft_status(rank, byte_index)); ++ reg_write(hvreft | val, base_phy + ddr_phy_hvreft_status(rank, byte_index + 1)); ++ } else { ++ hvreft = reg_read(base_phy + ddr_phy_hvreft_status(rank, byte_index)) & ++ (~(PHY_VRFTRES_RXDIFFCAL_MASK << PHY_VRFTRES_RXDIFFCAL_BIT)); ++ reg_write(hvreft | (val << PHY_VRFTRES_RXDIFFCAL_BIT), ++ base_phy + ddr_phy_hvreft_status(rank, byte_index)); ++ reg_write(hvreft | (val << PHY_VRFTRES_RXDIFFCAL_BIT), ++ base_phy + ddr_phy_hvreft_status(rank, byte_index + 1)); ++ } ++} ++#endif ++ ++/* Set DDR Vref value */ ++static void ddr_vref_set(const struct ddr_cfg_st *cfg, unsigned int val) ++{ ++ if (cfg->cur_mode == DDR_MODE_READ) { /* HOST vref */ ++ ddr_phy_vref_host_set(cfg->cur_phy, cfg->rank_idx, get_byte_num(cfg), cfg->cur_byte, val); ++ } else { /* DRAM vref */ ++ unsigned int auto_ref_timing = reg_read(cfg->cur_dmc + DDR_DMC_TIMING2); ++ /* disable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing & DMC_AUTO_TIMING_DIS); ++ ++ /* DDR_PHY_VREFTCTRL 31bit:1 do vref dram set twice */ ++ reg_write((reg_read(cfg->cur_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT))) | ++ (PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT), ++ cfg->cur_phy + DDR_PHY_VREFTCTRL); ++ /* DRAM vref operations */ ++ ddr_phy_vref_dram_set(cfg->cur_phy, val, cfg->cur_byte); ++ ddr_phy_vref_dram_set(cfg->cur_phy, val, cfg->cur_byte); ++ /* DDR_PHY_VREFTCTRL 31bit:0 do vref dram set once */ ++ reg_write(reg_read(cfg->cur_phy + DDR_PHY_VREFTCTRL) & ++ (~(PHY_VREFS_MRS_ENTER_MASK << PHY_VREFS_MRS_ENTER_BIT)), ++ cfg->cur_phy + DDR_PHY_VREFTCTRL); ++ /* DRAM vref operations */ ++ ddr_phy_vref_dram_set(cfg->cur_phy, val, cfg->cur_byte); ++ /* enable auto refresh */ ++ ddr_training_set_timing(cfg->cur_dmc, auto_ref_timing); ++ } ++ ddr_info("byte[%x] mode[%x] set vref [%x]", cfg->cur_byte, cfg->cur_mode, val); ++} ++ ++/* Get DDR Vref value */ ++static unsigned int ddr_vref_get(const struct ddr_cfg_st *cfg) ++{ ++ unsigned int val = 0; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) /* HOST vref */ ++ ddr_phy_vref_host_get(cfg->cur_phy, cfg->rank_idx, cfg->cur_byte, val); ++ else /* DRAM vref */ ++ ddr_phy_vref_dram_get(cfg->cur_phy, val, cfg->cur_byte); ++ ++ ddr_info("byte[%x] mode[%x] get vref [%x]", cfg->cur_byte, cfg->cur_mode, val); ++ ++ return val; ++} ++ ++/* Get totol win number of training result */ ++static unsigned int ddr_vref_get_win(struct ddr_cfg_st *cfg, ++ struct training_data *training, int vref) ++{ ++ unsigned int vref_min = 0; ++ unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX; ++ int vref_set; ++ ++ training->ddr_win_sum = 0; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) ++ ddr_vref_get_host_max(cfg->rank_idx, vref_max); ++ ++ if (vref < vref_min) ++ vref_set = vref_min; ++ else if (vref > vref_max) ++ vref_set = vref_max; ++ else ++ vref_set = vref; ++ ++ ddr_vref_set(cfg, vref_set); ++ ddr_dataeye_deskew(cfg, training); ++ ++ return training->ddr_win_sum; ++} ++ ++/* Find the best vref which win number is max */ ++static unsigned int ddr_vref_find_best(struct ddr_cfg_st *cfg, ++ struct training_data *training, unsigned int vref, int step) ++{ ++ int cur_vref; ++ unsigned int best_vref; ++ unsigned int cur_win; ++ unsigned int max_win; ++ unsigned int lower_times = 0; ++ unsigned int vref_min = 0; ++ unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX; ++ ++ if (cfg->cur_mode == DDR_MODE_READ) ++ ddr_vref_get_host_max(cfg->rank_idx, vref_max); ++ ++ max_win = 0; ++ cur_vref = vref + step; ++ ++ if (vref < vref_min) ++ best_vref = vref_min; ++ else if (vref > vref_max) ++ best_vref = vref_max; ++ else ++ best_vref = vref; ++ ++ /* find parabola vertex */ ++ while (cur_vref >= vref_min && cur_vref <= vref_max) { ++ cur_win = ddr_vref_get_win(cfg, training, cur_vref); ++ ddr_debug("byte[%x] vref[%x] win[%x] mode[%x]", ++ cfg->cur_byte, cur_vref, cur_win, cfg->cur_mode); ++ if (cur_win < max_win) { ++ lower_times++; ++ if (lower_times == DDR_VREF_COMPARE_TIMES) ++ /* Continuous decline, mean found vertex */ ++ break; ++ } else { ++ lower_times = 0; ++ max_win = cur_win; ++ best_vref = cur_vref; ++ } ++ cur_vref = cur_vref + step; ++ } ++ ++ return best_vref; ++} ++ ++/* DDR Vref calibrate and set the best value */ ++static void ddr_vref_cal(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int def_vref; ++ unsigned int best_vref; ++ unsigned int left_win; ++ unsigned int right_win; ++ ++ def_vref = ddr_vref_get(cfg); ++ left_win = ddr_vref_get_win(cfg, training, def_vref - DDR_VREF_COMPARE_STEP); ++ right_win = ddr_vref_get_win(cfg, training, def_vref + DDR_VREF_COMPARE_STEP); ++ ++ ddr_debug("byte[%x] default vref[%x] win[%x][%x] mode[%x]", ++ cfg->cur_byte, def_vref, left_win, right_win, cfg->cur_mode); ++ ++ /* With vref increments, WIN number is a parabola. ++ So firstly determine the result on left or right. */ ++ /* parabola vertex */ ++ if (left_win < right_win) { /* the result on right */ ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, 1); ++ } else if (left_win > right_win) { /* the result on left */ ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, -1); ++ } else { ++ /* when (left_win == right_win), check def_vref */ ++ unsigned int vref_max = DDR_VREF_DRAM_VAL_MAX; ++ if (cfg->cur_mode == DDR_MODE_READ) ++ ddr_vref_get_host_max(cfg->rank_idx, vref_max); ++ ++ if (def_vref < (vref_max >> 1)) ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, 1); ++ else ++ best_vref = ddr_vref_find_best(cfg, training, def_vref, -1); ++ } ++ ++ ddr_debug("byte[%x] best vref[%x] mode[%x]", cfg->cur_byte, best_vref, cfg->cur_mode); ++ ddr_vref_set(cfg, best_vref); ++} ++ ++/* vref write calibrate: support DDR4 and LPDDR4 ++ * if the dram type is not ddr4 or lpddr4, do nothing ++ */ ++static int ddr_vref_write(struct ddr_cfg_st *cfg, struct training_data *training) ++{ ++ unsigned int i; ++ unsigned int dram_type = cfg->phy[cfg->phy_idx].dram_type; ++ unsigned int bank_group = (reg_read(cfg->cur_dmc + ++ ddr_dmc_cfg_rnkvol(cfg->rank_idx)) >> DMC_CFG_MEM_BG_BIT) & DMC_CFG_MEM_BG_MASK; ++ ++ if (dram_type != PHY_DRAMCFG_TYPE_LPDDR4 && dram_type != PHY_DRAMCFG_TYPE_DDR4) ++ return -1; ++ ++ if (dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ bank_group = DMC_CFG_MEM_2BG; /* lpddr4 not training byte1 byte3 */ ++ ++ if (cfg->dmc_idx >= DDR_DMC_PER_PHY_MAX) ++ return -1; ++ ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) ++ return -1; ++ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ /* byte1 and byte3 bypass when 2 Bank Group */ ++ if ((bank_group == DMC_CFG_MEM_2BG) && ((i == 1) || (i == 3))) /* bypass byte1 and byte3 */ ++ continue; ++ ++ ddr_vref_cal(cfg, training); ++ } ++ ++ return 0; ++} ++ ++static int ddr_vref_training(struct ddr_cfg_st *cfg) ++{ ++ struct training_data tmp_result; ++ struct training_data *training = &tmp_result; ++ struct tr_dq_data dq_data; ++ int result = 0; ++ unsigned int i; ++ ++ ddr_debug("DDR Vref[%x] training PHY[%x][%x] DMC[%x][%x] Rank[%x]", ++ cfg->cur_mode, cfg->phy_idx, cfg->cur_phy, cfg->dmc_idx, cfg->cur_dmc, cfg->rank_idx); ++ ++ ddr_vref_save_bdl(cfg, &dq_data); ++ memset(training, 0, sizeof(struct training_data)); ++ ++ if (get_byte_num(cfg) > DDR_PHY_BYTE_MAX) ++ return -1; ++ ++ /* vref calibrate */ ++ if (cfg->cur_mode == DDR_MODE_READ) { ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ if (cfg->cur_byte == 1 || cfg->cur_byte == 3) /* not training byte 1 and byte 3 */ ++ continue; ++ ++ ddr_vref_cal(cfg, training); ++ } ++ } else { ++ if (ddr_vref_write(cfg, training)) ++ return 0; /* do nothing */ ++ } ++ ++#if !defined(DDR_VREF_WITHOUT_BDL_CONFIG) || defined(DDR_TRAINING_CMD) ++ /* dataeye deskew again on best vref. */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ cfg->cur_byte = i + (cfg->dmc_idx << 1); /* byte index accord to phy */ ++ result += ddr_dataeye_deskew(cfg, training); ++ } ++#endif ++ ++ ddr_vref_restore_bdl(cfg, &dq_data); ++ ddr_result_data_save(cfg, training); ++ ++ return result; ++} ++ ++int ddr_vref_training_func(struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ ++ if (cfg == NULL) { ++ ddr_error("Pointer parameter cfg is NULL!"); ++ return -1; ++ } ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_VREF_HOST_MASK); ++ ddr_training_switch_axi(cfg); ++ ddr_ddrt_init(cfg, DDR_DDRT_MODE_DATAEYE); ++ cfg->dq_check_type = DDR_CHECK_TYPE_DDRT; ++ ++ /* host vref training disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_VREF_HOST_MASK) == DDR_FALSE) { ++ cfg->cur_mode = DDR_MODE_READ; ++ result += ddr_vref_training(cfg); ++ } ++ ++ /* dram vref training enable && DDR4 */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_VREF_DRAM_MASK) == DDR_FALSE) { ++ cfg->cur_mode = DDR_MODE_WRITE; ++ result += ddr_vref_training(cfg); ++ } ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_vref_training_func(struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR vref training"); ++ ++ return 0; ++} ++#endif /* DDR_VREF_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_training_impl.h b/drivers/ddr/vendor/default_v2/ddr_training_impl.h +new file mode 100644 +index 0000000..e49e7a0 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_training_impl.h +@@ -0,0 +1,481 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_TRAINING_IMPL_H ++#define DDR_TRAINING_IMPL_H ++ ++#ifndef __ASSEMBLY__ ++ ++#include "ddr_interface.h" ++#include "ddr_training_custom.h" ++#include "ddr_training_internal_config.h" ++ ++/****** special config define*******************************************/ ++#ifdef DDR_DATAEYE_NORMAL_NOT_ADJ_CONFIG ++/* Adjust dataeye window consume a lot of time, disable it will make boot ++ * faster. ++ * NOTE: The WDQ Phase and RDQS MUST be config a good value in the init table ++ * to avoid window trend to one side. ++ */ ++#define DDR_DATAEYE_NORMAL_ADJUST (DDR_FALSE) ++#else ++#define DDR_DATAEYE_NORMAL_ADJUST (DDR_TRUE) ++#endif ++/* MUST adjust dataeye window after HW or MPR training */ ++#define DDR_DATAEYE_ABNORMAL_ADJUST (DDR_TRUE) ++ ++/****** ddr training item bypass mask define ****************************/ ++#define DDR_BYPASS_PHY0_MASK 0x1 /* [0]PHY0 training */ ++#define DDR_BYPASS_PHY1_MASK 0x2 /* [1]PHY1 training */ ++#define DDR_BYPASS_PHY2_MASK 0x4 /* [2]PHY2 training */ ++#define DDR_BYPASS_WL_MASK 0x10 /* [4]Write leveling */ ++#define DDR_BYPASS_GATE_MASK 0x100 /* [8]Gate training */ ++#define DDR_BYPASS_DATAEYE_MASK 0x10000 /* [16]Dataeye training */ ++#define DDR_BYPASS_PCODE_MASK 0x40000 /* [18]Pcode training */ ++#define DDR_BYPASS_HW_MASK 0x100000 /* [20]Hardware read training */ ++#define DDR_BYPASS_MPR_MASK 0x200000 /* [21]MPR training */ ++#define DDR_BYPASS_AC_MASK 0x400000 /* [22]AC training */ ++#define DDR_BYPASS_LPCA_MASK 0x800000 /* [23]LPDDR CA training */ ++#define DDR_BYPASS_VREF_HOST_MASK 0x1000000 /* [24]Host Vref training */ ++#define DDR_BYPASS_VREF_DRAM_MASK 0x2000000 /* [25]DRAM Vref training */ ++#define DDR_BYPASS_DCC_MASK 0x08000000 /* [27]DCC training */ ++#define DDR_BYPASS_DATAEYE_ADJ_MASK 0x10000000 /* [28]Dataeye adjust */ ++#define DDR_BYPASS_WL_ADJ_MASK 0x20000000 /* [29]WL write adjust */ ++#define DDR_BYPASS_HW_ADJ_MASK 0x40000000 /* [30]HW read adjust */ ++#define DDR_BYPASS_ALL_MASK 0xffffffff /* all bypass */ ++ ++/****** common define **********************************************/ ++/* special ddrt need special read and write register */ ++#ifdef DDR_DDRT_SPECIAL_CONFIG ++#define ddrt_reg_read(addr) ddr_ddrt_read(addr) ++#define ddrt_reg_write(val, addr) ddr_ddrt_write(val, addr) ++#else ++#define ddrt_reg_read(addr) reg_read(addr) ++#define ddrt_reg_write(val, addr) reg_write(val, addr) ++#endif ++ ++#define DDR_MODE_READ (1 << 0) ++#define DDR_MODE_WRITE (1 << 1) ++ ++#define DDR_ENTER_SREF (1 << 0) ++#define DDR_EXIT_SREF (1 << 1) ++ ++/* DSB to make sure the operation is complete */ ++#ifndef ddr_asm_dsb ++#if (__LINUX_ARM_ARCH__ >= 8) ++#define ddr_asm_dsb() { __asm__ __volatile__("dsb sy"); } ++#else ++#define ddr_asm_dsb() { __asm__ __volatile__("dsb"); } ++#endif ++#endif ++ ++#define DDR_HWR_WAIT_TIMEOUT 0xffffffff ++#define DDR_SFC_WAIT_TIMEOUT 1000 ++#define DDR_LPCA_WAIT_TIMEOUT 1000 ++#define DDR_LOOP_COUNT 0xffffffff ++ ++#ifdef CFG_EDA_VERIFY ++#define DDR_AUTO_TIMING_DELAY 10 ++#define DDR_SET_FRE_DELAY_100NS 10 ++#define DDR_SET_FRE_DELAY_1US 2000 ++#define DDR_SET_FRE_DELAY_10US 2000 ++#define DDR_SET_FRE_DELAY_100US 5000 ++#else ++#define DDR_AUTO_TIMING_DELAY 1000 ++#define DDR_SET_FRE_DELAY_100NS 200 /* wait 100ns */ ++#define DDR_SET_FRE_DELAY_1US 2000 /* wait 1 us */ ++#define DDR_SET_FRE_DELAY_10US 20000 /* wait 10 us */ ++#define DDR_SET_FRE_DELAY_100US 200000 /* wait 100 us */ ++#endif ++ ++#define DDR_FIND_DQ_BOTH (1 << 0) /* find a valid value */ ++/* x is valid, (x-1) is invalid */ ++#define DDR_FIND_DQ_LEFT (1 << 1) ++/* x is valid, (x+1) is invalid */ ++#define DDR_FIND_DQ_RIGHT (1 << 2) ++ ++#define DDR_VREF_DRAM_VAL_MAX 0x32 /* 92.50%*VDDIO */ ++#define DDR_VREF_DRAM_VAL_MIN 0x0 /* 60.00%*VDDIO */ ++ ++#define DDR_PHY_REG_DQ_NUM 4 /* one register has 4 DQ BDL */ ++#define DDR_PHY_BDL_DLY_NUM 12 /* 10 bdl and 2 phase */ ++ ++#define DDR_PHY_CA_MAX 10 ++#define DDR_PHY_CA_REG_MAX (DDR_PHY_CA_MAX >> 1) ++ ++#define DDR_TRUE 1 ++#define DDR_FALSE 0 ++ ++#define DDR_WIN_MIDDLE (1 << 0) ++#define DDR_WIN_LEFT (1 << 1) ++#define DDR_WIN_RIGHT (1 << 2) ++ ++#define DDR_DELAY_PHASE 1 ++#define DDR_DELAY_BDL 2 ++ ++#ifndef DDR_DATAEYE_WIN_NUM ++/* Dateeye window number. More bigger more slower when Vref training. */ ++#define DDR_DATAEYE_WIN_NUM 8 ++#endif ++#ifndef DDR_LOOP_TIMES_LMT ++/* Dataeye DQ deskew times for best result. More bigger more slower. */ ++#define DDR_LOOP_TIMES_LMT 1 ++#endif ++#ifndef DDR_VREF_COMPARE_TIMES ++/* Compare times when find best vref value. More bigger more slower. */ ++#define DDR_VREF_COMPARE_TIMES 3 ++#endif ++#ifndef DDR_MPR_RDQS_FIND_TIMES ++/* MPR Find first start rdqs times. More bigger, start rdqs more bigger. */ ++#define DDR_MPR_RDQS_FIND_TIMES 3 ++#endif ++#ifndef DDR_VREF_COMPARE_STEP ++/* Compare step when begin to find. More bigger, more mistake, more stable. */ ++#define DDR_VREF_COMPARE_STEP 3 ++#endif ++ ++#define DDR_DQ_NUM_EACH_REG 4 /* Each bdl register includes four dqbdl */ ++#define DDR_BYTE_DQ 3 /* Shift left or shift right 3: 8 dq(1 byte) */ ++#define DDR_DQBDL_SHIFT_BIT 3 /* Shift left or shift right 3: 8 bit */ ++ ++#define DDR_DATAEYE_RESULT_MASK 0xffff ++#define DDR_DATAEYE_RESULT_BIT 16 ++ ++#define DDR_WL_BDL_STEP 2 /* wl bdl step */ ++#define DDR_GATE_BDL_STEP 2 /* gate bdl step */ ++#define DDR_DQS_ADJ_STEP 1 /* WR/RD DQS adjust step */ ++ ++#define DDR_DDRT_MODE_GATE (1 << 0) ++#define DDR_DDRT_MODE_DATAEYE (1 << 1) ++ ++#define DDR_CHECK_TYPE_DDRT (1 << 0) ++#define DDR_CHECK_TYPE_MPR (1 << 1) ++ ++#define DDR_MPR_BYTE_MASK 0xff ++#define DDR_MPR_BIT_MASK 0x1 ++#define DDR_MPR_BYTE_BIT 16 /* 16 bit (2 byte) */ ++#define DDR_MPR_BYTE_SHIFT_BIT 3 /* 8 bit */ ++ ++#define DDR_PHY_AC_TEST_VAL0 0x0 ++#define DDR_PHY_AC_TEST_VAL1 0xffffffff ++#define DDR_PHY_AC_TEST_VAL2 0x55555555 ++#define DDR_PHY_AC_TEST_VAL3 0xaaaaaaaa ++ ++/*******log define ***********************************************/ ++#if defined(DDR_TRAINING_CMD) && defined(DDR_TRAINING_LOG_CONFIG) ++#define ddr_info(fmt...) ddr_training_log(__func__, DDR_LOG_INFO, fmt) ++#define ddr_debug(fmt...) ddr_training_log(__func__, DDR_LOG_DEBUG, fmt) ++#define ddr_warning(fmt...) ddr_training_log(__func__, DDR_LOG_WARNING, fmt) ++#define ddr_error(fmt...) ddr_training_log(__func__, DDR_LOG_ERROR, fmt) ++#define ddr_fatal(fmt...) ddr_training_log(__func__, DDR_LOG_FATAL, fmt) ++#else ++#define ddr_info(fmt...) ++#define ddr_debug(fmt...) ++#define ddr_warning(fmt...) ++#define ddr_error(fmt...) ++#define ddr_fatal(fmt...) ++#endif /* DDR_TRAINING_CMD && DDR_TRAINING_LOG_CONFIG */ ++ ++/* [11:0] Error type */ ++/* 0x00000001 Write Leveling error */ ++#define DDR_ERR_WL (1 << 0) ++/* 0x00000002 Hardware Gatining error */ ++#define DDR_ERR_HW_GATING (1 << 1) ++/* 0x00000004 Sofeware Gatining error */ ++#define DDR_ERR_GATING (1 << 2) ++/* 0x00000008 DDRT test time out */ ++#define DDR_ERR_DDRT_TIME_OUT (1 << 3) ++/* 0x00000010 Hardware read dataeye error */ ++#define DDR_ERR_HW_RD_DATAEYE (1 << 4) ++/* 0x00000020 MPR error */ ++#define DDR_ERR_MPR (1 << 5) ++/* 0x00000040 Dataeye error */ ++#define DDR_ERR_DATAEYE (1 << 6) ++/* 0x00000080 LPDDR CA error */ ++#define DDR_ERR_LPCA (1 << 7) ++ ++/* [13:12] Error phy */ ++/* 0x00001000 PHY0 training error */ ++#define DDR_ERR_PHY0 (1 << 12) ++/* 0x00002000 PHY1 training error */ ++#define DDR_ERR_PHY1 (1 << 13) ++ ++#define DDR_ERR_BYTE_BIT 24 /* [28:24] Error DQ0-31 */ ++#define DDR_ERR_DQ_BIT 20 /* [22:20] Error Byte0-3 */ ++ ++/*******data define*********************************************/ ++#define get_byte_num(cfg) ((cfg)->phy[(cfg)->phy_idx].dmc[(cfg)->dmc_idx].byte_num) ++ ++#ifndef DDR_RELATE_REG_DECLARE ++struct tr_custom_reg { ++}; ++#endif ++ ++struct dmc_cfg_sref_st { ++ unsigned int val[DDR_DMC_PER_PHY_MAX]; ++}; ++ ++struct ddr_bdl_st { ++ unsigned int bdl[DDR_PHY_BYTE_MAX]; ++}; ++ ++struct ddr_timing_st { ++ unsigned int val[DDR_DMC_PER_PHY_MAX]; ++}; ++ ++struct ddr_tmp_st { ++ unsigned int temp; ++}; ++ ++struct rdqs_data_st { ++ struct ddr_bdl_st origin; ++ struct ddr_bdl_st rank[DDR_SUPPORT_RANK_MAX]; ++}; ++ ++struct ddr_delay_st { ++ unsigned int phase[DDR_PHY_BYTE_MAX]; ++ unsigned int bdl[DDR_PHY_BYTE_MAX]; ++}; ++ ++struct tr_relate_reg { ++ unsigned int auto_ref_timing; ++ unsigned int power_down; ++ unsigned int dmc_scramb; ++ unsigned int dmc_scramb_cfg; ++ unsigned int misc_scramb; ++ unsigned int ac_phy_ctl; ++ unsigned int swapdfibyte_en; ++ struct tr_custom_reg custom; ++ struct ddr_ddrc_data ddrc; ++}; ++ ++struct tr_dq_data { ++ unsigned int dq03[DDR_PHY_BYTE_MAX]; /* DQ0-DQ3 BDL */ ++ unsigned int dq47[DDR_PHY_BYTE_MAX]; /* DQ4-DQ7 BDL */ ++ unsigned int rdqs[DDR_PHY_BYTE_MAX]; /* RDQS */ ++ unsigned int rdm[DDR_PHY_BYTE_MAX]; /* RDM */ ++ unsigned int wdm[DDR_PHY_BYTE_MAX]; /* WDM */ ++}; ++ ++struct tr_dq_adj_st { ++ unsigned int wdqsdly; /* DXNWDQSDLY */ ++ unsigned int wdqsphase; /* wdqsphase */ ++ unsigned int wdqsbdl; /* wdqsbdl */ ++ unsigned int wdqdly; /* DXNWDQDLY */ ++ unsigned int wdqphase; /* wdqsphase */ ++ unsigned int dxnwlsl; /* DXNWLSL */ ++ unsigned int wlsl; /* wlsl */ ++}; ++ ++struct tr_dq_byte_st { ++ struct tr_dq_adj_st dq_val[DDR_PHY_BYTE_MAX]; ++}; ++ ++struct ca_bit_st { ++ unsigned int bit_p; ++ unsigned int bit_n; ++}; ++ ++struct ca_data_st { ++ unsigned int base_dmc; ++ unsigned int base_phy; ++ unsigned int done; /* whether all ca found bdl range */ ++ unsigned int min; /* min left bound */ ++ unsigned int max; /* max right bound */ ++ unsigned def[DDR_PHY_CA_REG_MAX]; ++ int left[DDR_PHY_CA_MAX]; ++ int right[DDR_PHY_CA_MAX]; ++ struct ca_bit_st bits[DDR_PHY_CA_MAX]; ++}; ++ ++struct ddr_dmc_st { ++ unsigned int addr; ++ unsigned int byte_num; ++ unsigned int ddrt_pattern; /* ddrt reversed data */ ++}; ++ ++struct ddr_rank_st { ++ unsigned int item; /* software training item */ ++ unsigned int item_hw; /* hardware training item */ ++}; ++ ++struct ddr_phy_st { ++ unsigned int addr; ++ unsigned int dram_type; ++ unsigned int dmc_num; ++ unsigned int rank_num; ++ unsigned int total_byte_num; ++ struct ddr_dmc_st dmc[DDR_DMC_PER_PHY_MAX]; ++ struct ddr_rank_st rank[DDR_SUPPORT_RANK_MAX]; ++}; ++ ++struct ddr_cfg_st { ++ struct ddr_phy_st phy[DDR_PHY_NUM]; ++ unsigned int phy_num; ++ unsigned int cur_phy; /* current training phy addr */ ++ unsigned int cur_dmc; /* current training dmc addr */ ++ unsigned int cur_item; /* current SW or HW training item */ ++ unsigned int cur_pattern; /* current ddrt pattern */ ++ unsigned int cur_mode; /* read or write */ ++ unsigned int cur_byte; /* current training byte index */ ++ unsigned int cur_dq; /* current training dq index */ ++ unsigned int phy_idx; /* current training phy index */ ++ unsigned int rank_idx; /* current training rank index */ ++ unsigned int dmc_idx; /* current training dmc index */ ++ unsigned int adjust; /* whether need to adjust dataeye window */ ++ unsigned int dq_check_type; /* ddrt or mpr */ ++ void *cmd_st; /* struct ddr_cmd_st */ ++ void *res_st; /* SW: struct ddr_training_result_st, HW: struct rdqs_data_st */ ++}; ++ ++struct dcc_ck_st { ++ unsigned int val[DDR_CK_RESULT_MAX]; ++ unsigned int win; ++ unsigned int win_min_ctl; ++ unsigned int win_max_ctl; ++ unsigned int win_min_duty; ++ unsigned int win_max_duty; ++ unsigned int def_bp; ++ unsigned int def_ctl; ++ unsigned int def_duty; ++ unsigned int idx_duty; ++ unsigned int idx_duty_ctl; ++ unsigned int idx_ctl; ++ unsigned int bypass_ck_bit; ++ unsigned int acioctl21_ctl_bit; ++ unsigned int acioctl21_ck_bit; ++}; ++ ++#ifdef DDR_DCC_TRAINING_CONFIG ++struct dcc_data_st { ++ struct tr_dq_data rank[DDR_SUPPORT_RANK_MAX]; ++ struct dcc_ck_st ck[DDR_CK_MAX_NUM]; ++ unsigned int item[DDR_CK_MAX_NUM]; ++ unsigned int ioctl21_tmp; ++}; ++#endif ++ ++struct ddr_bdl_dly_st { ++ unsigned int value[DDR_PHY_BDL_DLY_NUM]; ++}; ++ ++/*******Uart early function ***********************************************/ ++#ifndef DDR_PUTS ++#define DDR_PUTS uart_early_puts ++#endif ++#ifndef DDR_PUT_HEX ++#define DDR_PUT_HEX uart_early_put_hex ++#endif ++#ifndef DDR_PUTC ++#define DDR_PUTC uart_early_putc ++#endif ++ ++#if defined(DDR_TRAINING_UART_CONFIG) || defined(DDR_TRAINING_LOG_CONFIG) ++extern void uart_early_puts(const char *s); ++extern void uart_early_put_hex(int hex); ++extern void uart_early_putc(int chr); ++#else ++#undef DDR_PUTS ++#undef DDR_PUT_HEX ++#undef DDR_PUTC ++#endif ++/*******function interface define*********************************************/ ++#ifndef DDR_SW_TRAINING_FUNC ++#define DDR_SW_TRAINING_FUNC_PUBLIC ++#define DDR_SW_TRAINING_FUNC ddr_sw_training_func ++#endif ++ ++#ifndef DDR_HW_TRAINING_FUNC ++#define DDR_HW_TRAINING_FUNC_PUBLIC ++#define DDR_HW_TRAINING_FUNC ddr_hw_training_func ++#endif ++ ++#ifndef DDR_PCODE_TRAINING_FUNC ++#define DDR_PCODE_TRAINING_FUNC ddr_pcode_training_func ++#endif ++ ++#ifndef DDR_TRAINING_CONSOLE ++#define DDR_TRAINING_CONSOLE_PUBLIC ++#define DDR_TRAINING_CONSOLE ddr_training_console ++#endif ++/*******Custom function ***********************************************/ ++#ifndef ddr_training_ddrt_prepare_func ++#define ddr_training_ddrt_prepare_func() ++#endif ++#ifndef ddr_training_save_reg_func ++#define ddr_training_save_reg_func(relate_reg, mask) ++#endif ++#ifndef ddr_training_restore_reg_func ++#define ddr_training_restore_reg_func(relate_reg) ++#endif ++/*******function define*********************************************/ ++int ddr_sw_training_func(void); ++int ddr_training_boot_func(struct ddr_cfg_st *cfg); ++int ddr_training_cmd_func(struct ddr_cfg_st *cfg); ++ ++void *memset(void *b, int c, size_t len); ++void *memcpy(void *dst, const void *src, size_t len); ++void ddr_training_cfg_init(struct ddr_cfg_st *cfg); ++void ddr_training_hw_item_cfg(struct ddr_cfg_st *cfg, unsigned int form_value); ++void ddr_training_delay(unsigned int cnt); ++int ddr_training_all(struct ddr_cfg_st *cfg); ++int ddr_dataeye_training_func(struct ddr_cfg_st *cfg); ++int ddr_vref_training_func(struct ddr_cfg_st *cfg); ++int ddr_wl_func(const struct ddr_cfg_st *cfg); ++int ddr_gating_func(const struct ddr_cfg_st *cfg); ++int ddr_ac_training_func(const struct ddr_cfg_st *cfg); ++int ddr_lpca_training_func(const struct ddr_cfg_st *cfg); ++int ddr_dcc_training_func(struct ddr_cfg_st *cfg); ++int ddr_mpr_training_func(struct ddr_cfg_st *cfg); ++ ++int ddr_training_ctrl_easr(const struct ddr_cfg_st *cfg, unsigned int sref_req); ++void ddr_training_save_timing(const struct ddr_cfg_st *cfg, struct ddr_timing_st *timing_st); ++void ddr_training_restore_timing(const struct ddr_cfg_st *cfg, const struct ddr_timing_st *timing_st); ++void ddr_sref_cfg(const struct ddr_cfg_st *cfg, struct dmc_cfg_sref_st *cfg_sref, unsigned int value); ++void ddr_sref_cfg_restore(const struct ddr_cfg_st *cfg, const struct dmc_cfg_sref_st *cfg_sref); ++void ddr_phy_cfg_update(unsigned int base_phy); ++int ddr_hw_training(struct ddr_cfg_st *cfg); ++int ddr_hw_training_by_phy(struct ddr_cfg_st *cfg); ++int ddr_hw_training_process(const struct ddr_cfg_st *cfg, unsigned int item); ++int ddr_pcode_training(struct ddr_cfg_st *cfg); ++int ddr_mpr_check(const struct ddr_cfg_st *cfg); ++int ddr_dataeye_training(struct ddr_cfg_st *cfg); ++int ddr_ac_training(const struct ddr_cfg_st *cfg); ++int ddr_lpca_training(struct ddr_cfg_st *cfg); ++int ddr_dataeye_deskew(struct ddr_cfg_st *cfg, struct training_data *training); ++void ddr_result_data_save(struct ddr_cfg_st *cfg, const struct training_data *training); ++void ddr_lpca_data_save(struct ddr_cfg_st *cfg, const struct ca_data_st *data); ++unsigned int ddr_ddrt_get_test_addr(void); ++int ddr_ddrt_test(unsigned int mask, int byte, int dq); ++void ddr_ddrt_init(const struct ddr_cfg_st *cfg, unsigned int mode); ++int ddr_ddrt_check(const struct ddr_cfg_st *cfg); ++int ddr_training_check_bypass(const struct ddr_cfg_st *cfg, unsigned int mask); ++void ddr_training_save_reg(const struct ddr_cfg_st *cfg, struct tr_relate_reg *relate_reg, ++ unsigned int mask); ++void ddr_training_restore_reg(const struct ddr_cfg_st *cfg, const struct tr_relate_reg *relate_reg); ++void ddr_training_switch_axi(const struct ddr_cfg_st *cfg); ++void ddr_training_log(const char *func, int level, const char *fmt, ...); ++void ddr_training_stat(unsigned int mask, unsigned int phy, int byte, int dq); ++void ddr_training_error(unsigned int mask, unsigned int phy, int byte, int dq); ++void ddr_training_start(void); ++void ddr_training_suc(void); ++int ddr_hw_dataeye_read(struct ddr_cfg_st *cfg); ++void ddr_ck_cfg(unsigned int base_phy); ++#endif /* __ASSEMBLY__ */ ++#endif /* DDR_TRAINING_IMPL_H */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_training_internal_config.h b/drivers/ddr/vendor/default_v2/ddr_training_internal_config.h +new file mode 100644 +index 0000000..bed0baa +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_training_internal_config.h +@@ -0,0 +1,185 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_TRAINING_INTERNAL_CONFIG_H ++#define DDR_TRAINING_INTERNAL_CONFIG_H ++ ++/****** include ddrc,phy,dmc define files *******************/ ++#if defined(DDR_DDRC_V500_CONFIG) ++#include "ddr_ddrc_v500.h" ++#elif defined(DDR_DDRC_V510_CONFIG) ++#include "ddr_ddrc_v510.h" ++#elif defined(DDR_DDRC_V520_CONFIG) ++#include "ddr_ddrc_v520.h" ++#elif defined(DDR_DDRC_V520_S14_CONFIG) ++#include "ddr_ddrc_v520_s14.h" ++#else ++# error Unknown DDRC Type ++#endif ++ ++#if defined(DDR_PHY_S40_CONFIG) ++#include "ddr_phy_s40.h" ++#elif defined(DDR_PHY_T28_CONFIG) ++#include "ddr_phy_t28.h" ++#elif defined(DDR_PHY_T16_CONFIG) ++#include "ddr_phy_t16.h" ++#elif defined(DDR_PHY_T12_V100_CONFIG) ++#include "ddr_phy_t12_v100.h" ++#elif defined(DDR_PHY_T12_V101_CONFIG) ++#include "ddr_phy_t12_v101.h" ++#elif defined(DDR_PHY_S14_CONFIG) ++#include "ddr_phy_s14.h" ++#else ++# error Unknown DDR PHY Type ++#endif ++ ++#if defined(DDR_DDRT_S40_CONFIG) ++#include "ddr_ddrt_s40.h" ++#elif defined(DDR_DDRT_T28_CONFIG) ++#include "ddr_ddrt_t28.h" ++#elif defined(DDR_DDRT_T16_CONFIG) ++#include "ddr_ddrt_t16.h" ++#elif defined(DDR_DDRT_T12_V100_CONFIG) ++#include "ddr_ddrt_t12_v100.h" ++#elif defined(DDR_DDRT_V2_0_SHF0_CONFIG) ++#include "ddr_ddrt_v2_0_shf0.h" ++#elif defined(DDR_DDRT_V2_0_SHF1_CONFIG) ++#include "ddr_ddrt_v2_0_shf1.h" ++#elif defined(DDR_DDRT_V2_0_SHF2_CONFIG) ++#include "ddr_ddrt_v2_0_shf2.h" ++#else ++# error Unknown DDR PHY Type ++#endif ++ ++/****** training item define *******************/ ++/* enable all config by default */ ++#define DDR_GATE_TRAINING_CONFIG ++#define DDR_DATAEYE_TRAINING_CONFIG ++#define DDR_HW_TRAINING_CONFIG ++#define DDR_TRAINING_ADJUST_CONFIG ++#define DDR_TRAINING_LOG_CONFIG ++#define DDR_TRAINING_UART_CONFIG ++#define DDR_TRAINING_STAT_CONFIG ++ ++/* defined in ddr_training_custom.h to disable this item */ ++#ifdef DDR_VREF_TRAINING_DISABLE ++#undef DDR_VREF_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_WL_TRAINING_DISABLE ++#undef DDR_WL_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_GATE_TRAINING_DISABLE ++#undef DDR_GATE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_DATAEYE_TRAINING_DISABLE ++#undef DDR_DATAEYE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_HW_TRAINING_DISABLE ++#undef DDR_HW_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_MPR_TRAINING_DISABLE ++#undef DDR_MPR_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_ADJUST_DISABLE ++#undef DDR_TRAINING_ADJUST_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_LOG_DISABLE ++#undef DDR_TRAINING_LOG_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_UART_DISABLE ++#undef DDR_TRAINING_UART_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_STAT_DISABLE ++#undef DDR_TRAINING_STAT_CONFIG ++#endif ++ ++/* for training cmd */ ++#ifdef DDR_TRAINING_CMD ++/* defined in ddr_training_custom.h to disable this item */ ++#ifdef DDR_VREF_TRAINING_CMD_DISABLE ++#undef DDR_VREF_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_WL_TRAINING_CMD_DISABLE ++#undef DDR_WL_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_GATE_TRAINING_CMD_DISABLE ++#undef DDR_GATE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_DATAEYE_TRAINING_CMD_DISABLE ++#undef DDR_DATAEYE_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_HW_TRAINING_CMD_DISABLE ++#undef DDR_HW_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_MPR_TRAINING_CMD_DISABLE ++#undef DDR_MPR_TRAINING_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_ADJUST_CMD_DISABLE ++#undef DDR_TRAINING_ADJUST_CONFIG ++#endif ++ ++#ifdef DDR_TRAINING_LOG_CMD_DISABLE ++#undef DDR_TRAINING_LOG_CONFIG ++#endif ++#endif /* DDR_TRAINING_CMD */ ++ ++/* check config */ ++#if defined(DDR_TRAINING_ADJUST_DISABLE) && defined(DDR_HW_TRAINING_CONFIG) && \ ++ !defined(DDR_HW_READ_ADJ_CONFIG) ++#error when defined DDR_TRAINING_ADJUST_DISABLE, \ ++ MUST define DDR_HW_READ_ADJ_CONFIG. ++#endif ++ ++#if (defined(DDR_HW_TRAINING_CONFIG) || defined(DDR_MPR_TRAINING_CONFIG) || \ ++ defined(DDR_VREF_TRAINING_CONFIG) || \ ++ defined(DDR_TRAINING_ADJUST_CONFIG)) && \ ++ !defined(DDR_DATAEYE_TRAINING_CONFIG) ++#error when enable HW/GATE/VREF training or dataeye adjust, \ ++ MUST define DDR_DATAEYE_TRAINING_CONFIG. ++#endif ++ ++/* reserve config */ ++/* DDR_WL_DATAEYE_ADJUST_CONFIG: Adjust WDQ phase/bdl after WL training. */ ++/* DDR_VREF_TRAINING_CONFIG : DDR Vref training. */ ++/* DDR_MPR_TRAINING_CONFIG : DDR MPR training. */ ++/* DDR_AC_TRAINING_CONFIG : DDR AC training. */ ++/* DDR_LPCA_TRAINING_CONFIG : LPDDR CA training. */ ++/* DDR_DDRT_SPECIAL_CONFIG : DDRT read and write special operate. */ ++/* DDR_DDR4_CONFIG : DDR4 special operate. */ ++/* DDR_TRAINING_CUT_CODE_CONFIG: Cut code for small SRAM. */ ++/* DDR_TRAINING_MINI_LOG_CONFIG: Less code to log */ ++/* DDR_HW_READ_ADJ_CONFIG : Adjust read dataeye after hw read training */ ++/* DDR_VREF_WITHOUT_BDL_CONFIG : Vref not modify DQ bdl */ ++/* DDR_DATAEYE_NORMAL_NOT_ADJ_CONFIG : Do not adjust window on normal case */ ++#endif /* DDR_TRAINING_INTERNAL_CONFIG_H */ +diff --git a/drivers/ddr/vendor/default_v2/ddr_wl_training.c b/drivers/ddr/vendor/default_v2/ddr_wl_training.c +new file mode 100644 +index 0000000..a5f9448 +--- /dev/null ++++ b/drivers/ddr/vendor/default_v2/ddr_wl_training.c +@@ -0,0 +1,439 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_training_impl.h" ++ ++#define __WRITE_LEVELING__ ++#ifdef DDR_WL_TRAINING_CONFIG ++static void ddr_bdl_add(unsigned int *raw, unsigned int val) ++{ ++ if (((*raw) + val) > PHY_BDL_MASK) ++ *raw = PHY_BDL_MASK; ++ else ++ *raw += val; ++} ++ ++static void ddr_bdl_sub(unsigned int *raw, unsigned int val) ++{ ++ if ((*raw) > val) ++ *raw -= val; ++ else ++ *raw = 0; ++} ++ ++/* DDR PHY DQ phase increase */ ++static void ddr_phase_inc(unsigned int *raw) ++{ ++#if defined (DDR_PHY_T28_CONFIG) || defined(DDR_PHY_T16_CONFIG) || \ ++ defined (DDR_PHY_T12_V100_CONFIG) || defined (DDR_PHY_T12_V101_CONFIG) ++ if ((*raw) < (PHY_WDQS_PHASE_MASK - 1)) { ++ if (((*raw) & 0x3) == 0x2) ++ *raw += 0x2; ++ else ++ *raw += 0x1; ++ } ++#else ++ if ((*raw) < PHY_WDQS_PHASE_MASK) ++ *raw += 0x1; ++#endif ++} ++ ++/* DDR PHY DQ phase decrease */ ++static void ddr_phase_dec(unsigned int *raw) ++{ ++#if defined (DDR_PHY_T28_CONFIG) || defined(DDR_PHY_T16_CONFIG) || \ ++ defined (DDR_PHY_T12_V100_CONFIG) || defined (DDR_PHY_T12_V101_CONFIG) ++ if ((*raw) > 0x1) { ++ if (((*raw) & 0x3) == 0x3) ++ *raw -= 0x2; ++ else ++ *raw -= 0x1; ++ } ++#else ++ if ((*raw) > 0x0) ++ *raw -= 0x1; ++#endif ++} ++ ++/* DQ bdl add or sub */ ++static void ddr_dq_bdl_operate(unsigned int base_phy, ++ unsigned int addr_offset, unsigned int val, unsigned int is_add) ++{ ++ unsigned int tmp; ++ unsigned int dq_bdl[DDR_PHY_REG_DQ_NUM]; ++ int i; ++ ++ tmp = reg_read(base_phy + addr_offset); ++ dq_bdl[0] = (tmp >> PHY_BDL_DQ0_BIT) & PHY_BDL_MASK; /* wdq0bdl */ ++ dq_bdl[1] = (tmp >> PHY_BDL_DQ1_BIT) & PHY_BDL_MASK; /* wdq1bdl */ ++ dq_bdl[2] = (tmp >> PHY_BDL_DQ2_BIT) & PHY_BDL_MASK; /* wdq2bdl */ ++ dq_bdl[3] = (tmp >> PHY_BDL_DQ3_BIT) & PHY_BDL_MASK; /* wdq3bdl */ ++ ++ for (i = 0; i < DDR_PHY_REG_DQ_NUM; i++) { ++ if (is_add) ++ ddr_bdl_add(&dq_bdl[i], val); ++ else ++ ddr_bdl_sub(&dq_bdl[i], val); ++ } ++ ++ tmp = (dq_bdl[3] << PHY_BDL_DQ3_BIT) + (dq_bdl[2] << PHY_BDL_DQ2_BIT) + /* wdq3bdl and wdq2bdl */ ++ (dq_bdl[1] << PHY_BDL_DQ1_BIT) + (dq_bdl[0] << PHY_BDL_DQ0_BIT); /* wdq1bdl and wdq0bdl */ ++ reg_write(tmp, base_phy + addr_offset); ++} ++ ++/* Disable or enable DDR write leveling mode */ ++static void ddr_wl_switch(unsigned int base_dmc, unsigned int base_phy, int val) ++{ ++ unsigned int mr1_raw; ++ unsigned int sfc_cmd; ++ unsigned int sfc_bank; ++ ++ /* Set Rank = 0, Cmd = MRS, No Precharch CMD */ ++ mr1_raw = reg_read(base_phy + DDR_PHY_MODEREG01) >> PHY_MODEREG01_MR1_BIT; ++ sfc_cmd = DMC_CMD_TYPE_LMR; ++ sfc_bank = DMC_BANK_MR1; ++ ++ if (val == DDR_TRUE) /* enable DDR wl */ ++ /* Set A7 as 1 */ ++ sfc_cmd += (mr1_raw | DMC_CMD_MRS_A7) << DMC_SFC_CMD_MRS_BIT; ++ else ++ /* Set A7 as 0 */ ++ sfc_cmd += (mr1_raw & ((~DMC_CMD_MRS_A7) & DMC_CMD_MRS_MASK)) << DMC_SFC_CMD_MRS_BIT; ++ ++ ddr_dmc_sfc_cmd(base_dmc, sfc_cmd, 0x0, sfc_bank); ++ ++ /* clear */ ++ if (val == DDR_FALSE) { ++ reg_write(0x0, base_dmc + DDR_DMC_SFCBANK); ++ reg_write(0x0, base_dmc + DDR_DMC_SFCREQ); ++ } ++ ++ /* phy sw write leveling mode */ ++ reg_write(val, base_phy + DDR_PHY_SWTMODE); ++} ++ ++#ifdef DDR_WL_DATAEYE_ADJUST_CONFIG ++static void ddr_wl_wdq_cmp(const struct ddr_cfg_st *cfg, struct ddr_delay_st *wdqs_new, ++ struct ddr_delay_st *wdqs_old, unsigned int byte_idx) ++{ ++ unsigned int val; ++ unsigned int phase_adj; ++ unsigned int wdq_phase; ++ unsigned int wdm_bdl; ++ unsigned int bdl_adj = 0; /* for write dataeye */ ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int rank_idx = cfg->rank_idx; ++ ++ phase_adj = 0; ++ wdq_phase = (reg_read(base_phy + ddr_phy_dxnwdqdly(rank_idx, byte_idx)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++ wdm_bdl = (reg_read(base_phy + ddr_phy_dxnwdqnbdl2(rank_idx, byte_idx)) >> ++ PHY_WDM_BDL_BIT) & PHY_BDL_MASK; ++ ++ if (wdqs_new->bdl[byte_idx] > wdqs_old->bdl[byte_idx]) { ++ val = wdqs_new->bdl[byte_idx] - wdqs_old->bdl[byte_idx]; ++ phase_adj = val >> DDR_BDL_PHASE_REL; ++ wdq_phase = wdq_phase + phase_adj; ++ ++ if (wdq_phase > PHY_WDQ_PHASE_MASK) ++ wdq_phase = PHY_WDQ_PHASE_MASK; ++ ++ /* adjust wdq bdl and dm bdl in opposite direction */ ++ bdl_adj = phase_adj << DDR_BDL_PHASE_REL; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(rank_idx, byte_idx), ++ bdl_adj, DDR_FALSE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(rank_idx, byte_idx), ++ bdl_adj, DDR_FALSE); ++ ddr_bdl_sub(&wdm_bdl, bdl_adj); ++ } else if (wdqs_new->bdl[byte_idx] < wdqs_old->bdl[byte_idx]) { ++ val = wdqs_old->bdl[byte_idx] - wdqs_new->bdl[byte_idx]; ++ phase_adj = val >> DDR_BDL_PHASE_REL; ++ wdq_phase = (wdq_phase > phase_adj) ? (wdq_phase - phase_adj) : 0; ++ ++ /* adjust wdq bdl and dm bdl in opposite direction */ ++ bdl_adj = phase_adj << DDR_BDL_PHASE_REL; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(rank_idx, byte_idx), ++ bdl_adj, DDR_TRUE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(rank_idx, byte_idx), ++ bdl_adj, DDR_TRUE); ++ ddr_bdl_add(&wdm_bdl, bdl_adj); ++ } ++ ++ ddr_info("Byte[%x] WDQ adjust phase[%x] bdl[%x]", ++ byte_idx, phase_adj, bdl_adj); ++ ++ reg_write(wdq_phase << PHY_WDQ_PHASE_BIT, ++ base_phy + ddr_phy_dxnwdqdly(rank_idx, byte_idx)); ++ reg_write(wdm_bdl << PHY_WDM_BDL_BIT, base_phy + ddr_phy_dxnwdqnbdl2(rank_idx, byte_idx)); ++} ++ ++/* Adjust dataeye WDQ after Write leveling */ ++static void ddr_wl_wdq_adjust(const struct ddr_cfg_st *cfg, ++ struct ddr_delay_st *wdqs_new, struct ddr_delay_st *wdqs_old) ++{ ++ unsigned int i; ++ unsigned int byte_num = get_byte_num(cfg); ++ ++ ddr_debug("DDR WL write adjust"); ++ ++ /* check wl write adjust bypass bit */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_WL_ADJ_MASK) != DDR_FALSE) ++ return; ++ ++ /* adjust wdq phase, wdq bdl, wdm bdl */ ++ for (i = 0; i < byte_num; i++) { ++ if (wdqs_new->phase[i] == wdqs_old->phase[i] && ++ wdqs_new->bdl[i] == wdqs_old->bdl[i]) { ++ continue; ++ } ++ ddr_wl_wdq_cmp(cfg, wdqs_new, wdqs_old, i); ++ } ++ ++ ddr_phy_cfg_update(cfg->cur_phy); ++} ++#endif /* DDR_WL_DATAEYE_ADJUST_CONFIG */ ++ ++/* Sync WDQ phase, WDQ bdl, WDM bdl, OEN bdl, WDQ SOE bdl by WDQS value */ ++static void ddr_wl_bdl_sync(const struct ddr_cfg_st *cfg, ++ const struct ddr_delay_st *wdqs_new, const struct ddr_delay_st *wdqs_old) ++{ ++ int i; ++ unsigned int val; ++ unsigned int oen_bdl, wdqsoe_bdl, wdm_bdl; ++ unsigned int wdq_phase; ++ unsigned int base_phy = cfg->cur_phy; ++ ++ /* sync wdq phase, wdq bdl, wdm bdl, oen bdl, wdq soe bdl */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ if (wdqs_new->phase[i] == wdqs_old->phase[i] && wdqs_new->bdl[i] == wdqs_old->bdl[i]) ++ continue; ++ ++ ddr_debug("Byte[%x] new[%x][%x] old[%x][%x]", i, ++ wdqs_new->phase[i], wdqs_new->bdl[i], wdqs_old->phase[i], wdqs_old->bdl[i]); ++ ++ /* wdq phase */ ++ wdq_phase = (reg_read(base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)) >> ++ PHY_WDQ_PHASE_BIT) & PHY_WDQ_PHASE_MASK; ++ /* always new_phase >= old_phase */ ++ wdq_phase = wdq_phase + (wdqs_new->phase[i] - wdqs_old->phase[i]); ++ ++ /* bdl */ ++ oen_bdl = (reg_read(base_phy + ddr_phy_dxnoebdl(cfg->rank_idx, i)) >> ++ PHY_OEN_BDL_BIT) & PHY_BDL_MASK; ++ wdqsoe_bdl = (reg_read(base_phy + ddr_phy_dxnoebdl(cfg->rank_idx, i)) >> ++ PHY_WDQSOE_BDL_BIT) & PHY_BDL_MASK; ++ wdm_bdl = (reg_read(base_phy + ddr_phy_dxnwdqnbdl2(cfg->rank_idx, i)) >> ++ PHY_WDM_BDL_BIT) & PHY_BDL_MASK; ++ ++ if (wdqs_new->bdl[i] > wdqs_old->bdl[i]) { ++ val = wdqs_new->bdl[i] - wdqs_old->bdl[i]; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(cfg->rank_idx, i), val, DDR_TRUE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(cfg->rank_idx, i), val, DDR_TRUE); ++ ddr_bdl_add(&oen_bdl, val); ++ ddr_bdl_add(&wdqsoe_bdl, val); ++ ddr_bdl_add(&wdm_bdl, val); ++ } else if (wdqs_new->bdl[i] < wdqs_old->bdl[i]) { ++ val = wdqs_old->bdl[i] - wdqs_new->bdl[i]; ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl0(cfg->rank_idx, i), val, DDR_FALSE); ++ ddr_dq_bdl_operate(base_phy, ddr_phy_dxnwdqnbdl1(cfg->rank_idx, i), val, DDR_FALSE); ++ ddr_bdl_sub(&oen_bdl, val); ++ ddr_bdl_sub(&wdqsoe_bdl, val); ++ ddr_bdl_sub(&wdm_bdl, val); ++ } ++ ++ if (wdq_phase > PHY_WDQ_PHASE_MASK) ++ wdq_phase = PHY_WDQ_PHASE_MASK; ++ ++ reg_write(wdq_phase << PHY_WDQ_PHASE_BIT, base_phy + ddr_phy_dxnwdqdly(cfg->rank_idx, i)); ++ reg_write((wdqsoe_bdl << PHY_WDQSOE_BDL_BIT) + (oen_bdl << PHY_OEN_BDL_BIT), ++ base_phy + ddr_phy_dxnoebdl(cfg->rank_idx, i)); ++ reg_write((wdm_bdl << PHY_WDM_BDL_BIT), base_phy + ddr_phy_dxnwdqnbdl2(cfg->rank_idx, i)); ++ } ++ ++ ddr_phy_cfg_update(base_phy); ++} ++ ++static void ddr_wl_error(unsigned int type, unsigned int byte_num, ++ unsigned int wl_result, unsigned int base_phy) ++{ ++ int j; ++ ++ if (type == DDR_DELAY_BDL) { ++ ddr_fatal("PHY[%x] WL fail, result[%x]", base_phy, wl_result); ++ for (j = 0; j < byte_num; j++) { ++ if (!(wl_result & (1 << j))) ++ ddr_training_stat(DDR_ERR_WL, base_phy, j, -1); ++ } ++ } else { ++ ddr_debug("PHY[%x] WL not found phase, result[%x]", base_phy, wl_result); ++ } ++} ++ ++/* ++ * Write leveling process. ++ * WL depend default WDQS phase value in register init table. ++ */ ++static int ddr_wl_process(const struct ddr_cfg_st *cfg, unsigned int type, struct ddr_delay_st *wdqs) ++{ ++ int i, j; ++ unsigned int wl_result; ++ unsigned int length; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int byte_num = get_byte_num(cfg); ++ ++ if (type == DDR_DELAY_PHASE) ++ length = PHY_WDQS_PHASE_MASK; ++ else ++ length = PHY_BDL_MASK; ++ ++ /* find WDQS phase or bdl, assume CLK Delay > DQS Delay */ ++ for (i = 0; i <= length; i++) { ++ ddr_phy_cfg_update(base_phy); ++ reg_write(0x1, base_phy + DDR_PHY_SWTWLDQS); ++ ddr_asm_dsb(); ++ wl_result = reg_read(base_phy + DDR_PHY_SWTRLT) & PHY_SWTRLT_WL_MASK; ++ reg_write(0x0, base_phy + DDR_PHY_SWTWLDQS); ++ ++ if ((wl_result & ((1 << byte_num) - 1)) == ((1 << byte_num) - 1)) ++ break; ++ ++ for (j = 0; j < byte_num; j++) { ++ ddr_info("type[0x%x] byte[0x%x] phase[0x%x] bdl[0x%x] wl_result[0x%x]", ++ type, j, wdqs->phase[j], wdqs->bdl[j], wl_result); ++ if (wl_result & (1 << j)) ++ continue; ++ ++ if (type == DDR_DELAY_PHASE) ++ ddr_phase_inc(&wdqs->phase[j]); ++ else ++ wdqs->bdl[j] += DDR_WL_BDL_STEP; ++ ++ reg_write((wdqs->phase[j] << PHY_WDQS_PHASE_BIT) + ++ (wdqs->bdl[j] << PHY_WDQS_BDL_BIT), ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, j)); ++ } ++ } ++ ++ if (i > length) { /* wl error, not find wdqs delay */ ++ ddr_wl_error(type, byte_num, wl_result, base_phy); ++ return -1; ++ } else { ++ return 0; ++ } ++} ++ ++/* ++ * Find WDQS delay, sync to WDQ delay and OE delay. ++ * WL depend default WDQS phase value in register init table. ++ */ ++static int ddr_write_leveling(const struct ddr_cfg_st *cfg) ++{ ++ int result; ++ unsigned int i, tmp; ++ unsigned int base_phy = cfg->cur_phy; ++ unsigned int base_dmc = cfg->cur_dmc; ++ struct ddr_delay_st wdqs_old; ++ struct ddr_delay_st wdqs_new; ++ ++ ddr_debug("DDR Write Leveling training"); ++ ++ /* init wdqs */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ tmp = reg_read(base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ ++ wdqs_old.phase[i] = (tmp >> PHY_WDQS_PHASE_BIT) & PHY_WDQS_PHASE_MASK; ++ wdqs_old.bdl[i] = (tmp >> PHY_WDQS_BDL_BIT) & PHY_BDL_MASK; ++ ++ wdqs_new.phase[i] = wdqs_old.phase[i]; ++ wdqs_new.bdl[i] = 0; ++ ++ /* clear wdqs bdl */ ++ reg_write(wdqs_new.phase[i] << PHY_WDQS_PHASE_BIT, ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ } ++ ++ /* enable sw write leveling mode */ ++ ddr_wl_switch(base_dmc, base_phy, DDR_TRUE); ++ ++ /* find first WDQS phase, assume CLK delay > DQS delay. */ ++ result = ddr_wl_process(cfg, DDR_DELAY_PHASE, &wdqs_new); ++ ++ /* check phase result */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ /* find phase error, keep max value to find bdl. */ ++ /* find phase ok, decrease to find bdl. */ ++ if (!result) ++ ddr_phase_dec(&wdqs_new.phase[i]); ++ ++ reg_write(wdqs_new.phase[i] << PHY_WDQS_PHASE_BIT, ++ base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ } ++ ++ /* find WDQS bdl */ ++ result = ddr_wl_process(cfg, DDR_DELAY_BDL, &wdqs_new); ++ ++ /* disable sw write leveling mode */ ++ ddr_wl_switch(base_dmc, base_phy, DDR_FALSE); ++ ++ if (result) { ++ /* restore default value when find WDQS fail */ ++ for (i = 0; i < get_byte_num(cfg); i++) { ++ tmp = (wdqs_old.phase[i] << PHY_WDQS_PHASE_BIT) + ++ (wdqs_old.bdl[i] << PHY_WDQS_BDL_BIT); ++ reg_write(tmp, base_phy + ddr_phy_dxwdqsdly(cfg->rank_idx, i)); ++ } ++ ddr_phy_cfg_update(base_phy); ++ return -1; ++ } ++ ++ /* sync delay */ ++ ddr_wl_bdl_sync(cfg, &wdqs_new, &wdqs_old); ++ ++#ifdef DDR_WL_DATAEYE_ADJUST_CONFIG ++ /* adjust WDQ for dataeye */ ++ ddr_wl_wdq_adjust(cfg, &wdqs_new, &wdqs_old); ++#endif ++ ++ return 0; ++} ++ ++int ddr_wl_func(const struct ddr_cfg_st *cfg) ++{ ++ struct tr_relate_reg relate_reg; ++ int result = 0; ++ ++ /* write leveling disable */ ++ if (ddr_training_check_bypass(cfg, DDR_BYPASS_WL_MASK) != DDR_FALSE) ++ return 0; ++ ++ ddr_training_save_reg(cfg, &relate_reg, DDR_BYPASS_WL_MASK); ++ ++ result += ddr_write_leveling(cfg); ++ ++ ddr_training_restore_reg(cfg, &relate_reg); ++ ++ return result; ++} ++#else ++int ddr_wl_func(const struct ddr_cfg_st *cfg) ++{ ++ ddr_warning("Not support DDR WL training"); ++ return 0; ++} ++#endif /* DDR_WL_TRAINING_CONFIG */ +diff --git a/drivers/ddr/vendor/ss927v100/ddr_training_custom.c b/drivers/ddr/vendor/ss927v100/ddr_training_custom.c +new file mode 100644 +index 0000000..e3f925e +--- /dev/null ++++ b/drivers/ddr/vendor/ss927v100/ddr_training_custom.c +@@ -0,0 +1,535 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#define CRG_REG_BASE_ADDR 0x11010000 ++#define PERI_CRG_PLL6 0x180 /* DPLL-1 configuration register */ ++#define PERI_CRG_PLL7 0x184 /* DPLL-2 configuration register */ ++#define PERI_CRG_DDRT 0x22a0 /* DDRT clock and soft reset control register */ ++#define CRG_SOC_FREQ_CONFIG 0x2000 /* SOC frequency configuration register */ ++#define CRG_SOC_FREQ_INDICATE 0x2020 /* SOC frequency indication register */ ++#define CRG_CLK_RESET 0x2280 /* DDR clock reset configuration register */ ++#define SYSCTRL_DDRT_CTRL 0x4030 /* DDRT control register */ ++ ++/* mask */ ++#define DDR_CKSEL_MASK 0x3 /* [13:12]ddr_cksel */ ++#define DDR_AXI_CKSEL_MASK 0x3 /* [13:12]ddraxi_cksel */ ++#define DDR_AXI_SC_SELED_MASK 0x3 /* [13:12]ddraxi_sc_seled */ ++#define DDR_TRFC_EN_MASK 0x1 /* bit[0] trfc_en */ ++ ++/* bit */ ++#define DDR_CKSEL_BIT 12 /* [13:12]ddr_cksel */ ++#define DDR_AXI_CKSEL_BIT 12 /* [13:12]ddraxi_cksel */ ++#define DDR_AXI_SC_SELED_BIT 12 /* [13:12]ddraxi_sc_seled */ ++#define DDR_TRFC_EN_BIT 0 /* bit[0] trfc_en */ ++#define DDR_CFG_RX_AGE_COMPST_EN_BIT 31 /* bit[31] cfg_rx_age_compst_en */ ++#define DDR_TEST0_CKEN_BIT 4 /* ddrtest0_cken */ ++#define DDR_DDRT_CTRL_DDRT0_BIT 0 /* bit[0]ddrt0_mst_sel */ ++#define DDR_DDRT_CTRL_DDRT1_BIT 1 /* bit[1]ddrt1_mst_sel */ ++ ++#define CRG_DDR_CKSEL_24M 0x0 /* ddr_cksel 00:24MHz */ ++#define CRG_DDRAXI_CKSEL_24M 0x0 /* ddraxi_cksel 00:24MHz */ ++#define CRG_DDR_CKSEL_PLL 0x1 /* ddr_cksel 001:clk_dpll_pst */ ++/* PLL 400MHz */ ++#define CRG_PLL7_TMP_1TO4 0x02001032 ++#define CRG_PLL6_TMP_1TO4 0x23000000 ++/* PLL 800MHz */ ++#define CRG_PLL7_TMP_1TO2 0x02001032 ++#define CRG_PLL6_TMP_1TO2 0x13000000 ++/* PLL 1600MHz */ ++#define CRG_PLL7_TMP_1TO1 0x02001064 ++#define CRG_PLL6_TMP_1TO1 0x13000000 ++ ++#define DDR_FROM_VALUE0 0x0024140B /* partial boot form value */ ++#define DDR_FROM_VALUE1 0xFFDBEBF5 /* partial boot form value */ ++ ++/* ++ * Do some prepare before copy code from DDR to SRAM. ++ * Keep empty when nothing to do. ++ */ ++void ddr_cmd_prepare_copy(void) ++{ ++ return; ++} ++ ++/* ++ * Save site before DDR training command execute . ++ * Keep empty when nothing to do. ++ */ ++void ddr_cmd_site_save(void) ++{ ++ return; ++} ++ ++/* ++ * Restore site after DDR training command execute. ++ * Keep empty when nothing to do. ++ */ ++void ddr_cmd_site_restore(void) ++{ ++ return; ++} ++ ++static void ddr_rdqs_anti_aging(struct tr_custom_reg *custom_reg, ++ unsigned int base_phy, unsigned int phy_idx) ++{ ++ /* disable rdqs anti-aging */ ++ custom_reg->age_compst_en[phy_idx] = reg_read(base_phy + DDR_PHY_PHYRSCTRL); ++ reg_write(custom_reg->age_compst_en[phy_idx] & (~(0x1 << DDR_CFG_RX_AGE_COMPST_EN_BIT)), ++ base_phy + DDR_PHY_PHYRSCTRL); ++} ++ ++static int ddr_disable_retrain(struct tr_custom_reg *custom_reg, ++ unsigned int base_phy, unsigned int phy_idx) ++{ ++ unsigned int cnt = DDR_LOOP_COUNT; ++ ++ custom_reg->trfc_en[phy_idx] = reg_read(base_phy + DDR_PHY_TRFC_CTRL); ++ reg_write(custom_reg->trfc_en[phy_idx] & (~(DDR_TRFC_EN_MASK << DDR_TRFC_EN_BIT)), ++ base_phy + DDR_PHY_TRFC_CTRL); ++ ++ while (cnt--) { ++ if (!((reg_read(base_phy + DDR_PHY_TRFC_CTRL) >> DDR_TRFC_EN_BIT) & DDR_TRFC_EN_MASK)) ++ break; ++ } ++ ++ if (cnt == DDR_LOOP_COUNT) ++ return -1; ++ ++ return 0; ++} ++ ++/* ++ * Save site before DDR training:include boot and command execute. ++ * Keep empty when nothing to do. ++ */ ++int ddr_boot_cmd_save(struct tr_custom_reg *custom_reg) ++{ ++ int result; ++ ++ if (custom_reg == NULL) ++ return -1; ++ ++ /* enable ddrt control */ ++ custom_reg->ddrt_ctrl = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_CTRL); ++ reg_write(custom_reg->ddrt_ctrl | (0x1 << DDR_DDRT_CTRL_DDRT0_BIT) | (0x1 << DDR_DDRT_CTRL_DDRT1_BIT), ++ DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_CTRL); ++ /* turn on ddrt clock */ ++ custom_reg->ddrt_clk_reg = reg_read(CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ /* enable ddrt0 clock */ ++ reg_write(custom_reg->ddrt_clk_reg | (0x1 << DDR_TEST0_CKEN_BIT), ++ CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ __asm__ __volatile__("nop"); ++ /* disable ddrt0 soft reset */ ++ reg_write(reg_read(CRG_REG_BASE_ADDR + PERI_CRG_DDRT) & (~(0x1 << 0)), ++ CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ ++ /* disable rdqs anti-aging */ ++ ddr_rdqs_anti_aging(custom_reg, DDR_REG_BASE_PHY0, 0); /* 0: phy0 */ ++ /* disable retrain */ ++ result = ddr_disable_retrain(custom_reg, DDR_REG_BASE_PHY0, 0); /* 0: phy0 */ ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_rdqs_anti_aging(custom_reg, DDR_REG_BASE_PHY1, 1); /* 1: phy1 */ ++ result += ddr_disable_retrain(custom_reg, DDR_REG_BASE_PHY1, 1); /* 1: phy1 */ ++#endif ++ ++ return result; ++} ++ ++/* ++ * Restore site after DDR training:include boot and command execute. ++ * Keep empty when nothing to do. ++ */ ++void ddr_boot_cmd_restore(const struct tr_custom_reg *custom_reg) ++{ ++ if (custom_reg == NULL) ++ return; ++ ++ /* restore ddrt control */ ++ reg_write(custom_reg->ddrt_ctrl, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_CTRL); ++ ++ /* restore ddrt clock */ ++ reg_write(custom_reg->ddrt_clk_reg, CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ ++ /* restore ddrt clock */ ++ reg_write(custom_reg->ddrt_clk_reg, CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ ++ /* restore rdqs anti-aging */ ++ reg_write(custom_reg->age_compst_en[0], DDR_REG_BASE_PHY0 + DDR_PHY_PHYRSCTRL); ++ /* restore retrain */ ++ reg_write(custom_reg->trfc_en[0], DDR_REG_BASE_PHY0 + DDR_PHY_TRFC_CTRL); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ reg_write(custom_reg->age_compst_en[1], DDR_REG_BASE_PHY1 + DDR_PHY_PHYRSCTRL); ++ reg_write(custom_reg->trfc_en[1], DDR_REG_BASE_PHY1 + DDR_PHY_TRFC_CTRL); ++#endif ++} ++ ++/* ++ * DDR clock select. ++ * For ddr osc training. ++ */ ++#ifdef DDR_PCODE_TRAINING_CONFIG ++int ddr_get_cksel(void) ++{ ++ int freq; ++ unsigned int ddr_cksel; ++ ++ ddr_cksel = (reg_read(CRG_REG_BASE_ADDR + PERI_CRG_DDRCKSEL) >> 0x3) & 0x7; ++ switch (ddr_cksel) { ++ case 0x0: ++ freq = 24; /* 24 MHz */ ++ break; ++ case 0x1: ++ freq = 450; /* 450 MHz */ ++ break; ++ case 0x3: ++ freq = 300; /* 300 MHz */ ++ break; ++ case 0x4: ++ freq = 297; /* 297 MHz */ ++ break; ++ default: ++ freq = 300; /* 300 MHz */ ++ break; ++ } ++ return freq; ++} ++#endif ++ ++/* Configure ddr frequency */ ++static void ddr_cfg_freq_process(unsigned int crg_pll7, unsigned int crg_pll6) ++{ ++ unsigned int clk_reset; ++ unsigned int soc_freq_conf; ++ unsigned int ddraxi_sc_seled; ++ ++ clk_reset = reg_read(CRG_REG_BASE_ADDR + CRG_CLK_RESET); ++ soc_freq_conf = reg_read(CRG_REG_BASE_ADDR + CRG_SOC_FREQ_CONFIG); ++ ++ /* DDR cksel switch to 24MHZ */ ++ reg_write((clk_reset & (~(DDR_CKSEL_MASK << DDR_CKSEL_BIT))) | (CRG_DDR_CKSEL_24M << DDR_CKSEL_BIT), ++ CRG_REG_BASE_ADDR + CRG_CLK_RESET); ++ ddr_training_delay(DDR_SET_FRE_DELAY_10US); /* wait 10us */ ++ /* ddraxi cksel switch to 24MHZ */ ++ reg_write((soc_freq_conf & (~(DDR_AXI_CKSEL_MASK << DDR_AXI_CKSEL_BIT))) | ++ (CRG_DDRAXI_CKSEL_24M << DDR_AXI_CKSEL_BIT), ++ CRG_REG_BASE_ADDR + CRG_SOC_FREQ_CONFIG); ++ ddr_training_delay(DDR_SET_FRE_DELAY_10US); /* wait 10us */ ++ /* check ddr axi clock stat */ ++ ddraxi_sc_seled = (reg_read(CRG_REG_BASE_ADDR + CRG_SOC_FREQ_INDICATE) >> ++ DDR_AXI_SC_SELED_BIT) & DDR_AXI_SC_SELED_MASK; ++ if (ddraxi_sc_seled != CRG_DDRAXI_CKSEL_24M) ++ return; ++ ++ /* Configure DPLL */ ++ reg_write(crg_pll7 | (0x1 << 20), CRG_REG_BASE_ADDR + PERI_CRG_PLL7); /* bit[20] */ ++ reg_write(crg_pll6, CRG_REG_BASE_ADDR + PERI_CRG_PLL6); ++ ddr_training_delay(DDR_SET_FRE_DELAY_1US); /* wait 1us */ ++ reg_write(crg_pll7 & (~(0x1 << 20)), CRG_REG_BASE_ADDR + PERI_CRG_PLL7); /* bit[20] */ ++ ddr_training_delay(DDR_SET_FRE_DELAY_100US); /* wait 100us */ ++ ++ /* restore CRG_CLK_RESET and CRG_SOC_FREQ_CONFIG */ ++ reg_write(clk_reset, CRG_REG_BASE_ADDR + CRG_CLK_RESET); ++ reg_write(soc_freq_conf, CRG_REG_BASE_ADDR + CRG_SOC_FREQ_CONFIG); ++ ddr_training_delay(DDR_SET_FRE_DELAY_10US); ++} ++ ++static void ddr_cfg_freq(unsigned int base_phy) ++{ ++ unsigned int dficlk_ratio; ++ unsigned int crg_pll7_tmp; ++ unsigned int crg_pll6_tmp; ++ ++ /* dramCK Clock Ratio */ ++ dficlk_ratio = (reg_read(base_phy + DDR_PHY_PHYCTRL0) >> PHY_DFICLK_RATIO_BIT) & ++ PHY_DFICLK_RATIO_MASK; ++ switch (dficlk_ratio) { ++ case 0x0: /* 1:1 */ ++ crg_pll7_tmp = CRG_PLL7_TMP_1TO1; /* PLL 1600MHz */ ++ crg_pll6_tmp = CRG_PLL6_TMP_1TO1; ++ break; ++ case 0x1: /* 1:2 */ ++ crg_pll7_tmp = CRG_PLL7_TMP_1TO2; /* PLL 800MHz */ ++ crg_pll6_tmp = CRG_PLL6_TMP_1TO2; ++ break; ++ case 0x2: /* 1:4 */ ++ /* Fall through to PLL 400MHz */ ++ default: ++ crg_pll7_tmp = CRG_PLL7_TMP_1TO4; /* PLL 400MHz */ ++ crg_pll6_tmp = CRG_PLL6_TMP_1TO4; ++ break; ++ } ++ ++ /* Configure DDR low frequency */ ++ ddr_cfg_freq_process(crg_pll7_tmp, crg_pll6_tmp); ++} ++ ++static void ddr_cfg_phy_clk(unsigned int clk_status) ++{ ++ unsigned int gated0, gated1; ++ /* bit[31], bit[14:0] */ ++ gated0 = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_CLKGATED); ++ reg_write((gated0 & (~PHY_CLKGATED_MASK)) | clk_status, ++ DDR_REG_BASE_PHY0 + DDR_PHY_CLKGATED); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ gated1 = reg_read(DDR_REG_BASE_PHY1 + DDR_PHY_CLKGATED); ++ reg_write((gated1 & (~PHY_CLKGATED_MASK)) | clk_status, ++ DDR_REG_BASE_PHY1 + DDR_PHY_CLKGATED); ++#endif ++} ++ ++/* config DDR PLL power down or power up */ ++static void ddr_cfg_pll_power(unsigned int pll_status) ++{ ++ unsigned int pll_ctrl_0, pll_ctrl_1; ++ ++ pll_ctrl_0 = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_PLLCTRL); ++ reg_write((pll_ctrl_0 & (~(PHY_PLL_PWDN_MASK << PHY_PLL_PWDN_BIT))) | ++ (pll_status << PHY_PLL_PWDN_BIT), DDR_REG_BASE_PHY0 + DDR_PHY_PLLCTRL); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ pll_ctrl_1 = reg_read(DDR_REG_BASE_PHY1 + DDR_PHY_PLLCTRL); ++ reg_write((pll_ctrl_1 & (~(PHY_PLL_PWDN_MASK << PHY_PLL_PWDN_BIT))) | ++ (pll_status << PHY_PLL_PWDN_BIT), DDR_REG_BASE_PHY1 + DDR_PHY_PLLCTRL); ++#endif ++ ddr_training_delay(DDR_SET_FRE_DELAY_100US); /* wait 100us */ ++} ++ ++static int ddr_low_fre_start(struct tr_custom_reg *reg) ++{ ++ int result; ++ unsigned int i; ++ unsigned int crg_pll7_init_val, crg_pll6_init_val; ++ ++ struct dmc_cfg_sref_st cfg_sref; ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ ++ crg_pll7_init_val = reg_read(CRG_REG_BASE_ADDR + PERI_CRG_PLL7); ++ crg_pll6_init_val = reg_read(CRG_REG_BASE_ADDR + PERI_CRG_PLL6); ++ ++ ddr_training_cfg_init(cfg); ++ ++ if (ddr_boot_cmd_save(reg) != 0) ++ return -1; ++ ++ /* Configure DDR low frequency, does not distinguish PHY */ ++ ddr_cfg_freq(cfg->phy[0].addr); ++ ++ /* Perform partial initialization of DDR */ ++ ddr_training_hw_item_cfg(cfg, DDR_FROM_VALUE0); ++ result = ddr_hw_training(cfg); ++ /* enter auto self-refresh */ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, &cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); /* bit[3:2] 0x3 */ ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ } ++ ++ /* close phy clock gated */ ++ ddr_cfg_phy_clk(PHY_CLK_GATED_CLOSE); ++ ++ /* DDR PLL power down */ ++ ddr_cfg_pll_power(PHY_PLL_POWER_DOWN); ++ ++ /* Configure DDR high frequency */ ++ ddr_cfg_freq_process(crg_pll7_init_val, crg_pll6_init_val); ++ ++ /* DDR PLL power up */ ++ ddr_cfg_pll_power(PHY_PLL_POWER_UP); ++ ++ /* open phy clock gated */ ++ ddr_cfg_phy_clk(PHY_CLK_GATED_OPEN); ++ ++ for (i = 0; i < cfg->phy_num; i++) ++ ddr_ck_cfg(cfg->phy[i].addr); ++ ++ /* exit auto self-refresh */ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, &cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); /* bit[3:2] 0x3 */ ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ } ++ ++ /* Perform partial initialization of DDR */ ++ ddr_training_hw_item_cfg(cfg, DDR_FROM_VALUE1); ++ result += ddr_hw_training(cfg); ++ /* open rdqs anti-aging */ ++ ddr_boot_cmd_restore(reg); ++ ++ return result; ++} ++ ++int ddr_hw_training_init(void) ++{ ++ int result; ++ unsigned int dram_type; ++ struct tr_custom_reg reg; ++ ++ memset(®, 0, sizeof(struct tr_custom_reg)); ++ dram_type = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_MASK; ++ if (dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ result = ddr_low_fre_start(®); ++ } else { ++ if (ddr_boot_cmd_save(®) != 0) ++ return -1; ++ ++ result = ddr_hw_training_if(); ++ ddr_boot_cmd_restore(®); ++ } ++ return result; ++} ++ ++static void ddr_retrain_anti_aging_enable_process(unsigned int base_phy) ++{ ++ unsigned int age_compst_en; ++ unsigned int trfc_ctrl; ++ unsigned int addr_delay; ++ unsigned int trfc_mpc_cmd_dly, tphy_wrdata, dficlk_ratio_sel; ++ unsigned int cfg_rl, cfg_wl; ++ unsigned int ddr_phy_misc; ++ ++ /* enable rdqs anti-aging */ ++ age_compst_en = reg_read(base_phy + DDR_PHY_PHYRSCTRL); ++ reg_write((age_compst_en & (~(0x1 << DDR_CFG_RX_AGE_COMPST_EN_BIT))) | ++ (0x1 << DDR_CFG_RX_AGE_COMPST_EN_BIT), base_phy + DDR_PHY_PHYRSCTRL); ++ ++ /* control by reg */ ++ trfc_ctrl = reg_read(base_phy + DDR_PHY_TRFC_CTRL); ++ if (trfc_ctrl == 0) ++ return; ++ ++ /* update cfg_wl & cfg_rl */ ++ ddr_phy_misc = reg_read(base_phy + DDR_PHY_MISC); ++ addr_delay = (ddr_phy_misc >> PHY_MISC_ADDR_DELAY_BIT) & PHY_MISC_ADDR_DELAY_MASK; ++ if (addr_delay > PHY_MISC_ADDR_DELAY_MASK) ++ return; ++ ++ trfc_mpc_cmd_dly = (reg_read(base_phy + DDR_PHY_TRFC_THRESHOLD1) >> ++ PHY_TRFC_MPC_CMD_DLY_BIT) & PHY_TRFC_MPC_CMD_DLY_MASK; ++ if (trfc_mpc_cmd_dly > PHY_TRFC_MPC_CMD_DLY_MASK) ++ return; ++ ++ tphy_wrdata = (reg_read(base_phy + DDR_PHY_DMSEL) >> PHY_DMSEL_TPHY_WRDATA_BIT) & ++ PHY_DMSEL_TPHY_WRDATA_MASK; ++ if (tphy_wrdata > PHY_DMSEL_TPHY_WRDATA_MASK) ++ return; ++ ++ dficlk_ratio_sel = (reg_read(base_phy + DDR_PHY_PHYCTRL0) >> ++ PHY_DFICLK_RATIO_BIT) & PHY_DFICLK_RATIO_MASK; ++ if (dficlk_ratio_sel > PHY_DFICLK_RATIO_MASK) ++ return; ++ ++ cfg_rl = (ddr_phy_misc >> PHY_MISC_CFG_RL_BIT) & PHY_MISC_CFG_RL_MASK; ++ cfg_wl = (ddr_phy_misc >> PHY_MISC_CFG_WL_BIT) & PHY_MISC_CFG_WL_MASK; ++ cfg_rl = cfg_rl - ++ (4 + addr_delay - trfc_mpc_cmd_dly * tphy_wrdata) * dficlk_ratio_sel * 2; /* 4,2: Logical design requirements */ ++ cfg_wl = cfg_wl - ++ (4 + addr_delay - trfc_mpc_cmd_dly * tphy_wrdata) * dficlk_ratio_sel * 2; /* 4,2: Logical design requirements */ ++ ddr_phy_misc &= ~((PHY_MISC_CFG_RL_MASK << PHY_MISC_CFG_RL_BIT) | (PHY_MISC_CFG_WL_MASK << PHY_MISC_CFG_WL_BIT)); ++ ddr_phy_misc |= (cfg_rl << PHY_MISC_CFG_RL_BIT) | (cfg_wl << PHY_MISC_CFG_WL_BIT); ++ reg_write(ddr_phy_misc, base_phy + DDR_PHY_MISC); ++ ++ /* enable retrain */ ++ reg_write((trfc_ctrl & (~(DDR_TRFC_EN_MASK << DDR_TRFC_EN_BIT))) | (0x1 << DDR_TRFC_EN_BIT), ++ base_phy + DDR_PHY_TRFC_CTRL); ++} ++ ++/* enable retrain and rdqs anti-aging after ddr training */ ++void ddr_retrain_anti_aging_enable(void) ++{ ++ ddr_retrain_anti_aging_enable_process(DDR_REG_BASE_PHY0); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_retrain_anti_aging_enable_process(DDR_REG_BASE_PHY1); ++#endif ++} ++ ++static void ddr_dmc_auto_pd_by_phy(unsigned int base_phy, unsigned int dmc0, unsigned int dmc1) ++{ ++ unsigned int dmc_pd0; ++ unsigned int pd_prd; ++ ++ dmc_pd0 = reg_read(dmc0 + DDR_DMC_CFG_PD); ++ pd_prd = (dmc_pd0 >> DMC_CFG_PD_PRD_BIT) & DMC_CFG_PD_PRD_MASK; ++ if (pd_prd == 0) ++ return; ++ ++ if ((reg_read(base_phy + DDR_PHY_DRAMCFG) & ++ PHY_DRAMCFG_TYPE_MASK) == PHY_DRAMCFG_TYPE_LPDDR4) { ++ reg_write(dmc_pd0 | (0x1 << DMC_CFG_PD_EN_BIT), dmc0 + DDR_DMC_CFG_PD); ++ reg_write(reg_read(dmc1 + DDR_DMC_CFG_PD) | (0x1 << DMC_CFG_PD_EN_BIT), dmc1 + DDR_DMC_CFG_PD); ++ } else { ++ reg_write(dmc_pd0 | (0x1 << DMC_CFG_PD_EN_BIT), dmc0 + DDR_DMC_CFG_PD); ++ } ++} ++ ++/* ddr DMC auto power down config ++ * the value should config after trainning, or it will cause chip compatibility problems ++ */ ++void ddr_dmc_auto_power_down_cfg(void) ++{ ++ ddr_dmc_auto_pd_by_phy(DDR_REG_BASE_PHY0, DDR_REG_BASE_DMC0, DDR_REG_BASE_DMC1); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_dmc_auto_pd_by_phy(DDR_REG_BASE_PHY1, DDR_REG_BASE_DMC2, DDR_REG_BASE_DMC3); ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++ ddr_dmc_auto_pd_by_phy(DDR_REG_BASE_PHY2, DDR_REG_BASE_DMC4, DDR_REG_BASE_DMC5); ++#endif ++} ++ ++static void ddr_set_rdqbdl_def_val_by_phy(unsigned int base_phy) ++{ ++ unsigned int rank_idx; ++ unsigned int byte_idx; ++ ++ for (rank_idx = 0; rank_idx < 2; rank_idx++) { /* 2:rank number */ ++ for (byte_idx = 0; byte_idx < 4; byte_idx++) { /* 4:byte number */ ++ reg_write(0x20202020, base_phy + ddr_phy_dxnrdqnbdl0(rank_idx, byte_idx)); ++ reg_write(0x20202020, base_phy + ddr_phy_dxnrdqnbdl1(rank_idx, byte_idx)); ++ reg_write(0x20, base_phy + ddr_phy_dxnrdqnbdl2(rank_idx, byte_idx)); ++ } ++ } ++} ++ ++void ddr_set_rdqbdl_def_val(void) ++{ ++ ddr_set_rdqbdl_def_val_by_phy(DDR_REG_BASE_PHY0); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_set_rdqbdl_def_val_by_phy(DDR_REG_BASE_PHY1); ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++ ddr_set_rdqbdl_def_val_by_phy(DDR_REG_BASE_PHY2); ++#endif ++} +diff --git a/drivers/ddr/vendor/ss927v100/ddr_training_custom.h b/drivers/ddr/vendor/ss927v100/ddr_training_custom.h +new file mode 100644 +index 0000000..f10c2ed +--- /dev/null ++++ b/drivers/ddr/vendor/ss927v100/ddr_training_custom.h +@@ -0,0 +1,100 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_TRAINING_CUSTOM_H ++#define DDR_TRAINING_CUSTOM_H ++ ++/* config DDRC, PHY, DDRT typte */ ++#define DDR_DDRC_V520_S14_CONFIG ++#define DDR_PHY_S14_CONFIG ++#define DDR_DDRT_V2_0_SHF0_CONFIG ++ ++/* config special item */ ++/* s40/t28/t16 not support dcc training */ ++#define DDR_CHANNEL_MAP_PHY0_DMC0_PHY1_DMC2 ++#define DDR_DCC_TRAINING_CONFIG ++ ++#define DDR_WL_TRAINING_DISABLE ++#define DDR_GATE_TRAINING_DISABLE ++#define DDR_TRAINING_UART_DISABLE ++ ++#define DDR_PHY_NUM 2 /* phy number */ ++ ++#define DDR_DMC_PER_PHY_MAX 2 /* dmc number per phy max */ ++ ++/* config DDRC, PHY, DDRT base address */ ++/* [CUSTOM] DDR PHY0 base register */ ++#define DDR_REG_BASE_PHY0 0x11150000 ++/* [CUSTOM] DDR PHY1 base register */ ++#define DDR_REG_BASE_PHY1 0x11152000 ++/* [CUSTOM] DDR DMC0 base register */ ++#define DDR_REG_BASE_DMC0 0x11148000 ++/* [CUSTOM] DDR DMC1 base register */ ++#define DDR_REG_BASE_DMC1 0x11149000 ++/* [CUSTOM] DDR DMC2 base register */ ++#define DDR_REG_BASE_DMC2 0x1114a000 ++/* [CUSTOM] DDR DMC3 base register */ ++#define DDR_REG_BASE_DMC3 0x1114b000 ++/* [CUSTOM] DDR DDRT base register */ ++#define DDR_REG_BASE_DDRT 0x11160000 ++/* [CUSTOM] DDR training item system control */ ++#define DDR_REG_BASE_SYSCTRL 0x11020000 ++#define DDR_REG_BASE_AXI 0x11140000 ++/* Serial Configuration */ ++#define DDR_REG_BASE_UART0 0x11040000 ++ ++/* config offset address */ ++/* Assume sysctrl offset address for DDR training as follows, ++if not please define. */ ++/* [CUSTOM] PHY1 ddrt reversed data */ ++#define SYSCTRL_DDRT_PATTERN 0xa8 ++/* [CUSTOM] PHY2 ddrt reversed data */ ++#define SYSCTRL_DDRT_PATTERN_SEC 0xac ++/* [CUSTOM] ddr sw training item */ ++#define SYSCTRL_DDR_TRAINING_CFG 0xa0 /* RANK0 */ ++#define SYSCTRL_DDR_TRAINING_CFG_SEC 0xa4 /* RANK1 */ ++/* [CUSTOM] ddr training stat */ ++#define SYSCTRL_DDR_TRAINING_STAT 0xb0 ++/* [CUSTOM] ddr training version flag */ ++#define SYSCTRL_DDR_TRAINING_VERSION_FLAG 0xb4 ++ ++/* [CUSTOM] ddr hw training item */ ++#define SYSCTRL_DDR_HW_PHY0_RANK0 0x90 ++#define SYSCTRL_DDR_HW_PHY0_RANK1 0x94 ++#define SYSCTRL_DDR_HW_PHY1_RANK0 0x98 ++#define SYSCTRL_DDR_HW_PHY1_RANK1 0x9c ++ ++/* config other special */ ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x40000000 ++/* [CUSTOM] SRAM start address. ++NOTE: Makefile will parse it, plase define it as Hex. eg: 0xFFFF0C00 */ ++#define DDR_TRAINING_RUN_STACK 0x04010c00 ++ ++#define DDR_RELATE_REG_DECLARE ++struct tr_custom_reg { ++ unsigned int ddrt_clk_reg; ++ unsigned int age_compst_en[DDR_PHY_NUM]; ++ unsigned int trfc_en[DDR_PHY_NUM]; ++ unsigned int ddrt_ctrl; ++}; ++int ddr_boot_cmd_save(struct tr_custom_reg *custom_reg); ++void ddr_boot_cmd_restore(const struct tr_custom_reg *custom_reg); ++int ddr_get_cksel(void); ++#endif /* DDR_TRAINING_CUSTOM_H */ +diff --git a/drivers/ddr/vendor/ss928v100/ddr_training_custom.c b/drivers/ddr/vendor/ss928v100/ddr_training_custom.c +new file mode 100644 +index 0000000..e3f925e +--- /dev/null ++++ b/drivers/ddr/vendor/ss928v100/ddr_training_custom.c +@@ -0,0 +1,535 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ddr_interface.h" ++#include "ddr_training_impl.h" ++ ++#define CRG_REG_BASE_ADDR 0x11010000 ++#define PERI_CRG_PLL6 0x180 /* DPLL-1 configuration register */ ++#define PERI_CRG_PLL7 0x184 /* DPLL-2 configuration register */ ++#define PERI_CRG_DDRT 0x22a0 /* DDRT clock and soft reset control register */ ++#define CRG_SOC_FREQ_CONFIG 0x2000 /* SOC frequency configuration register */ ++#define CRG_SOC_FREQ_INDICATE 0x2020 /* SOC frequency indication register */ ++#define CRG_CLK_RESET 0x2280 /* DDR clock reset configuration register */ ++#define SYSCTRL_DDRT_CTRL 0x4030 /* DDRT control register */ ++ ++/* mask */ ++#define DDR_CKSEL_MASK 0x3 /* [13:12]ddr_cksel */ ++#define DDR_AXI_CKSEL_MASK 0x3 /* [13:12]ddraxi_cksel */ ++#define DDR_AXI_SC_SELED_MASK 0x3 /* [13:12]ddraxi_sc_seled */ ++#define DDR_TRFC_EN_MASK 0x1 /* bit[0] trfc_en */ ++ ++/* bit */ ++#define DDR_CKSEL_BIT 12 /* [13:12]ddr_cksel */ ++#define DDR_AXI_CKSEL_BIT 12 /* [13:12]ddraxi_cksel */ ++#define DDR_AXI_SC_SELED_BIT 12 /* [13:12]ddraxi_sc_seled */ ++#define DDR_TRFC_EN_BIT 0 /* bit[0] trfc_en */ ++#define DDR_CFG_RX_AGE_COMPST_EN_BIT 31 /* bit[31] cfg_rx_age_compst_en */ ++#define DDR_TEST0_CKEN_BIT 4 /* ddrtest0_cken */ ++#define DDR_DDRT_CTRL_DDRT0_BIT 0 /* bit[0]ddrt0_mst_sel */ ++#define DDR_DDRT_CTRL_DDRT1_BIT 1 /* bit[1]ddrt1_mst_sel */ ++ ++#define CRG_DDR_CKSEL_24M 0x0 /* ddr_cksel 00:24MHz */ ++#define CRG_DDRAXI_CKSEL_24M 0x0 /* ddraxi_cksel 00:24MHz */ ++#define CRG_DDR_CKSEL_PLL 0x1 /* ddr_cksel 001:clk_dpll_pst */ ++/* PLL 400MHz */ ++#define CRG_PLL7_TMP_1TO4 0x02001032 ++#define CRG_PLL6_TMP_1TO4 0x23000000 ++/* PLL 800MHz */ ++#define CRG_PLL7_TMP_1TO2 0x02001032 ++#define CRG_PLL6_TMP_1TO2 0x13000000 ++/* PLL 1600MHz */ ++#define CRG_PLL7_TMP_1TO1 0x02001064 ++#define CRG_PLL6_TMP_1TO1 0x13000000 ++ ++#define DDR_FROM_VALUE0 0x0024140B /* partial boot form value */ ++#define DDR_FROM_VALUE1 0xFFDBEBF5 /* partial boot form value */ ++ ++/* ++ * Do some prepare before copy code from DDR to SRAM. ++ * Keep empty when nothing to do. ++ */ ++void ddr_cmd_prepare_copy(void) ++{ ++ return; ++} ++ ++/* ++ * Save site before DDR training command execute . ++ * Keep empty when nothing to do. ++ */ ++void ddr_cmd_site_save(void) ++{ ++ return; ++} ++ ++/* ++ * Restore site after DDR training command execute. ++ * Keep empty when nothing to do. ++ */ ++void ddr_cmd_site_restore(void) ++{ ++ return; ++} ++ ++static void ddr_rdqs_anti_aging(struct tr_custom_reg *custom_reg, ++ unsigned int base_phy, unsigned int phy_idx) ++{ ++ /* disable rdqs anti-aging */ ++ custom_reg->age_compst_en[phy_idx] = reg_read(base_phy + DDR_PHY_PHYRSCTRL); ++ reg_write(custom_reg->age_compst_en[phy_idx] & (~(0x1 << DDR_CFG_RX_AGE_COMPST_EN_BIT)), ++ base_phy + DDR_PHY_PHYRSCTRL); ++} ++ ++static int ddr_disable_retrain(struct tr_custom_reg *custom_reg, ++ unsigned int base_phy, unsigned int phy_idx) ++{ ++ unsigned int cnt = DDR_LOOP_COUNT; ++ ++ custom_reg->trfc_en[phy_idx] = reg_read(base_phy + DDR_PHY_TRFC_CTRL); ++ reg_write(custom_reg->trfc_en[phy_idx] & (~(DDR_TRFC_EN_MASK << DDR_TRFC_EN_BIT)), ++ base_phy + DDR_PHY_TRFC_CTRL); ++ ++ while (cnt--) { ++ if (!((reg_read(base_phy + DDR_PHY_TRFC_CTRL) >> DDR_TRFC_EN_BIT) & DDR_TRFC_EN_MASK)) ++ break; ++ } ++ ++ if (cnt == DDR_LOOP_COUNT) ++ return -1; ++ ++ return 0; ++} ++ ++/* ++ * Save site before DDR training:include boot and command execute. ++ * Keep empty when nothing to do. ++ */ ++int ddr_boot_cmd_save(struct tr_custom_reg *custom_reg) ++{ ++ int result; ++ ++ if (custom_reg == NULL) ++ return -1; ++ ++ /* enable ddrt control */ ++ custom_reg->ddrt_ctrl = reg_read(DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_CTRL); ++ reg_write(custom_reg->ddrt_ctrl | (0x1 << DDR_DDRT_CTRL_DDRT0_BIT) | (0x1 << DDR_DDRT_CTRL_DDRT1_BIT), ++ DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_CTRL); ++ /* turn on ddrt clock */ ++ custom_reg->ddrt_clk_reg = reg_read(CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ /* enable ddrt0 clock */ ++ reg_write(custom_reg->ddrt_clk_reg | (0x1 << DDR_TEST0_CKEN_BIT), ++ CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ __asm__ __volatile__("nop"); ++ /* disable ddrt0 soft reset */ ++ reg_write(reg_read(CRG_REG_BASE_ADDR + PERI_CRG_DDRT) & (~(0x1 << 0)), ++ CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ ++ /* disable rdqs anti-aging */ ++ ddr_rdqs_anti_aging(custom_reg, DDR_REG_BASE_PHY0, 0); /* 0: phy0 */ ++ /* disable retrain */ ++ result = ddr_disable_retrain(custom_reg, DDR_REG_BASE_PHY0, 0); /* 0: phy0 */ ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_rdqs_anti_aging(custom_reg, DDR_REG_BASE_PHY1, 1); /* 1: phy1 */ ++ result += ddr_disable_retrain(custom_reg, DDR_REG_BASE_PHY1, 1); /* 1: phy1 */ ++#endif ++ ++ return result; ++} ++ ++/* ++ * Restore site after DDR training:include boot and command execute. ++ * Keep empty when nothing to do. ++ */ ++void ddr_boot_cmd_restore(const struct tr_custom_reg *custom_reg) ++{ ++ if (custom_reg == NULL) ++ return; ++ ++ /* restore ddrt control */ ++ reg_write(custom_reg->ddrt_ctrl, DDR_REG_BASE_SYSCTRL + SYSCTRL_DDRT_CTRL); ++ ++ /* restore ddrt clock */ ++ reg_write(custom_reg->ddrt_clk_reg, CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ ++ /* restore ddrt clock */ ++ reg_write(custom_reg->ddrt_clk_reg, CRG_REG_BASE_ADDR + PERI_CRG_DDRT); ++ ++ /* restore rdqs anti-aging */ ++ reg_write(custom_reg->age_compst_en[0], DDR_REG_BASE_PHY0 + DDR_PHY_PHYRSCTRL); ++ /* restore retrain */ ++ reg_write(custom_reg->trfc_en[0], DDR_REG_BASE_PHY0 + DDR_PHY_TRFC_CTRL); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ reg_write(custom_reg->age_compst_en[1], DDR_REG_BASE_PHY1 + DDR_PHY_PHYRSCTRL); ++ reg_write(custom_reg->trfc_en[1], DDR_REG_BASE_PHY1 + DDR_PHY_TRFC_CTRL); ++#endif ++} ++ ++/* ++ * DDR clock select. ++ * For ddr osc training. ++ */ ++#ifdef DDR_PCODE_TRAINING_CONFIG ++int ddr_get_cksel(void) ++{ ++ int freq; ++ unsigned int ddr_cksel; ++ ++ ddr_cksel = (reg_read(CRG_REG_BASE_ADDR + PERI_CRG_DDRCKSEL) >> 0x3) & 0x7; ++ switch (ddr_cksel) { ++ case 0x0: ++ freq = 24; /* 24 MHz */ ++ break; ++ case 0x1: ++ freq = 450; /* 450 MHz */ ++ break; ++ case 0x3: ++ freq = 300; /* 300 MHz */ ++ break; ++ case 0x4: ++ freq = 297; /* 297 MHz */ ++ break; ++ default: ++ freq = 300; /* 300 MHz */ ++ break; ++ } ++ return freq; ++} ++#endif ++ ++/* Configure ddr frequency */ ++static void ddr_cfg_freq_process(unsigned int crg_pll7, unsigned int crg_pll6) ++{ ++ unsigned int clk_reset; ++ unsigned int soc_freq_conf; ++ unsigned int ddraxi_sc_seled; ++ ++ clk_reset = reg_read(CRG_REG_BASE_ADDR + CRG_CLK_RESET); ++ soc_freq_conf = reg_read(CRG_REG_BASE_ADDR + CRG_SOC_FREQ_CONFIG); ++ ++ /* DDR cksel switch to 24MHZ */ ++ reg_write((clk_reset & (~(DDR_CKSEL_MASK << DDR_CKSEL_BIT))) | (CRG_DDR_CKSEL_24M << DDR_CKSEL_BIT), ++ CRG_REG_BASE_ADDR + CRG_CLK_RESET); ++ ddr_training_delay(DDR_SET_FRE_DELAY_10US); /* wait 10us */ ++ /* ddraxi cksel switch to 24MHZ */ ++ reg_write((soc_freq_conf & (~(DDR_AXI_CKSEL_MASK << DDR_AXI_CKSEL_BIT))) | ++ (CRG_DDRAXI_CKSEL_24M << DDR_AXI_CKSEL_BIT), ++ CRG_REG_BASE_ADDR + CRG_SOC_FREQ_CONFIG); ++ ddr_training_delay(DDR_SET_FRE_DELAY_10US); /* wait 10us */ ++ /* check ddr axi clock stat */ ++ ddraxi_sc_seled = (reg_read(CRG_REG_BASE_ADDR + CRG_SOC_FREQ_INDICATE) >> ++ DDR_AXI_SC_SELED_BIT) & DDR_AXI_SC_SELED_MASK; ++ if (ddraxi_sc_seled != CRG_DDRAXI_CKSEL_24M) ++ return; ++ ++ /* Configure DPLL */ ++ reg_write(crg_pll7 | (0x1 << 20), CRG_REG_BASE_ADDR + PERI_CRG_PLL7); /* bit[20] */ ++ reg_write(crg_pll6, CRG_REG_BASE_ADDR + PERI_CRG_PLL6); ++ ddr_training_delay(DDR_SET_FRE_DELAY_1US); /* wait 1us */ ++ reg_write(crg_pll7 & (~(0x1 << 20)), CRG_REG_BASE_ADDR + PERI_CRG_PLL7); /* bit[20] */ ++ ddr_training_delay(DDR_SET_FRE_DELAY_100US); /* wait 100us */ ++ ++ /* restore CRG_CLK_RESET and CRG_SOC_FREQ_CONFIG */ ++ reg_write(clk_reset, CRG_REG_BASE_ADDR + CRG_CLK_RESET); ++ reg_write(soc_freq_conf, CRG_REG_BASE_ADDR + CRG_SOC_FREQ_CONFIG); ++ ddr_training_delay(DDR_SET_FRE_DELAY_10US); ++} ++ ++static void ddr_cfg_freq(unsigned int base_phy) ++{ ++ unsigned int dficlk_ratio; ++ unsigned int crg_pll7_tmp; ++ unsigned int crg_pll6_tmp; ++ ++ /* dramCK Clock Ratio */ ++ dficlk_ratio = (reg_read(base_phy + DDR_PHY_PHYCTRL0) >> PHY_DFICLK_RATIO_BIT) & ++ PHY_DFICLK_RATIO_MASK; ++ switch (dficlk_ratio) { ++ case 0x0: /* 1:1 */ ++ crg_pll7_tmp = CRG_PLL7_TMP_1TO1; /* PLL 1600MHz */ ++ crg_pll6_tmp = CRG_PLL6_TMP_1TO1; ++ break; ++ case 0x1: /* 1:2 */ ++ crg_pll7_tmp = CRG_PLL7_TMP_1TO2; /* PLL 800MHz */ ++ crg_pll6_tmp = CRG_PLL6_TMP_1TO2; ++ break; ++ case 0x2: /* 1:4 */ ++ /* Fall through to PLL 400MHz */ ++ default: ++ crg_pll7_tmp = CRG_PLL7_TMP_1TO4; /* PLL 400MHz */ ++ crg_pll6_tmp = CRG_PLL6_TMP_1TO4; ++ break; ++ } ++ ++ /* Configure DDR low frequency */ ++ ddr_cfg_freq_process(crg_pll7_tmp, crg_pll6_tmp); ++} ++ ++static void ddr_cfg_phy_clk(unsigned int clk_status) ++{ ++ unsigned int gated0, gated1; ++ /* bit[31], bit[14:0] */ ++ gated0 = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_CLKGATED); ++ reg_write((gated0 & (~PHY_CLKGATED_MASK)) | clk_status, ++ DDR_REG_BASE_PHY0 + DDR_PHY_CLKGATED); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ gated1 = reg_read(DDR_REG_BASE_PHY1 + DDR_PHY_CLKGATED); ++ reg_write((gated1 & (~PHY_CLKGATED_MASK)) | clk_status, ++ DDR_REG_BASE_PHY1 + DDR_PHY_CLKGATED); ++#endif ++} ++ ++/* config DDR PLL power down or power up */ ++static void ddr_cfg_pll_power(unsigned int pll_status) ++{ ++ unsigned int pll_ctrl_0, pll_ctrl_1; ++ ++ pll_ctrl_0 = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_PLLCTRL); ++ reg_write((pll_ctrl_0 & (~(PHY_PLL_PWDN_MASK << PHY_PLL_PWDN_BIT))) | ++ (pll_status << PHY_PLL_PWDN_BIT), DDR_REG_BASE_PHY0 + DDR_PHY_PLLCTRL); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ pll_ctrl_1 = reg_read(DDR_REG_BASE_PHY1 + DDR_PHY_PLLCTRL); ++ reg_write((pll_ctrl_1 & (~(PHY_PLL_PWDN_MASK << PHY_PLL_PWDN_BIT))) | ++ (pll_status << PHY_PLL_PWDN_BIT), DDR_REG_BASE_PHY1 + DDR_PHY_PLLCTRL); ++#endif ++ ddr_training_delay(DDR_SET_FRE_DELAY_100US); /* wait 100us */ ++} ++ ++static int ddr_low_fre_start(struct tr_custom_reg *reg) ++{ ++ int result; ++ unsigned int i; ++ unsigned int crg_pll7_init_val, crg_pll6_init_val; ++ ++ struct dmc_cfg_sref_st cfg_sref; ++ struct ddr_cfg_st ddr_cfg; ++ struct ddr_cfg_st *cfg = &ddr_cfg; ++ ++ crg_pll7_init_val = reg_read(CRG_REG_BASE_ADDR + PERI_CRG_PLL7); ++ crg_pll6_init_val = reg_read(CRG_REG_BASE_ADDR + PERI_CRG_PLL6); ++ ++ ddr_training_cfg_init(cfg); ++ ++ if (ddr_boot_cmd_save(reg) != 0) ++ return -1; ++ ++ /* Configure DDR low frequency, does not distinguish PHY */ ++ ddr_cfg_freq(cfg->phy[0].addr); ++ ++ /* Perform partial initialization of DDR */ ++ ddr_training_hw_item_cfg(cfg, DDR_FROM_VALUE0); ++ result = ddr_hw_training(cfg); ++ /* enter auto self-refresh */ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, &cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); /* bit[3:2] 0x3 */ ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_ENTER_SREF)) ++ return -1; ++ } ++ ++ /* close phy clock gated */ ++ ddr_cfg_phy_clk(PHY_CLK_GATED_CLOSE); ++ ++ /* DDR PLL power down */ ++ ddr_cfg_pll_power(PHY_PLL_POWER_DOWN); ++ ++ /* Configure DDR high frequency */ ++ ddr_cfg_freq_process(crg_pll7_init_val, crg_pll6_init_val); ++ ++ /* DDR PLL power up */ ++ ddr_cfg_pll_power(PHY_PLL_POWER_UP); ++ ++ /* open phy clock gated */ ++ ddr_cfg_phy_clk(PHY_CLK_GATED_OPEN); ++ ++ for (i = 0; i < cfg->phy_num; i++) ++ ddr_ck_cfg(cfg->phy[i].addr); ++ ++ /* exit auto self-refresh */ ++ for (i = 0; i < cfg->phy_num; i++) { ++ cfg->phy_idx = i; ++ if (cfg->phy[cfg->phy_idx].dram_type == PHY_DRAMCFG_TYPE_LPDDR4) ++ ddr_sref_cfg(cfg, &cfg_sref, DMC_CFG_INIT_XSREF | DMC_CFG_SREF_PD); /* bit[3:2] 0x3 */ ++ ++ if (ddr_training_ctrl_easr(cfg, DDR_EXIT_SREF)) ++ return -1; ++ } ++ ++ /* Perform partial initialization of DDR */ ++ ddr_training_hw_item_cfg(cfg, DDR_FROM_VALUE1); ++ result += ddr_hw_training(cfg); ++ /* open rdqs anti-aging */ ++ ddr_boot_cmd_restore(reg); ++ ++ return result; ++} ++ ++int ddr_hw_training_init(void) ++{ ++ int result; ++ unsigned int dram_type; ++ struct tr_custom_reg reg; ++ ++ memset(®, 0, sizeof(struct tr_custom_reg)); ++ dram_type = reg_read(DDR_REG_BASE_PHY0 + DDR_PHY_DRAMCFG) & PHY_DRAMCFG_TYPE_MASK; ++ if (dram_type == PHY_DRAMCFG_TYPE_LPDDR4) { ++ result = ddr_low_fre_start(®); ++ } else { ++ if (ddr_boot_cmd_save(®) != 0) ++ return -1; ++ ++ result = ddr_hw_training_if(); ++ ddr_boot_cmd_restore(®); ++ } ++ return result; ++} ++ ++static void ddr_retrain_anti_aging_enable_process(unsigned int base_phy) ++{ ++ unsigned int age_compst_en; ++ unsigned int trfc_ctrl; ++ unsigned int addr_delay; ++ unsigned int trfc_mpc_cmd_dly, tphy_wrdata, dficlk_ratio_sel; ++ unsigned int cfg_rl, cfg_wl; ++ unsigned int ddr_phy_misc; ++ ++ /* enable rdqs anti-aging */ ++ age_compst_en = reg_read(base_phy + DDR_PHY_PHYRSCTRL); ++ reg_write((age_compst_en & (~(0x1 << DDR_CFG_RX_AGE_COMPST_EN_BIT))) | ++ (0x1 << DDR_CFG_RX_AGE_COMPST_EN_BIT), base_phy + DDR_PHY_PHYRSCTRL); ++ ++ /* control by reg */ ++ trfc_ctrl = reg_read(base_phy + DDR_PHY_TRFC_CTRL); ++ if (trfc_ctrl == 0) ++ return; ++ ++ /* update cfg_wl & cfg_rl */ ++ ddr_phy_misc = reg_read(base_phy + DDR_PHY_MISC); ++ addr_delay = (ddr_phy_misc >> PHY_MISC_ADDR_DELAY_BIT) & PHY_MISC_ADDR_DELAY_MASK; ++ if (addr_delay > PHY_MISC_ADDR_DELAY_MASK) ++ return; ++ ++ trfc_mpc_cmd_dly = (reg_read(base_phy + DDR_PHY_TRFC_THRESHOLD1) >> ++ PHY_TRFC_MPC_CMD_DLY_BIT) & PHY_TRFC_MPC_CMD_DLY_MASK; ++ if (trfc_mpc_cmd_dly > PHY_TRFC_MPC_CMD_DLY_MASK) ++ return; ++ ++ tphy_wrdata = (reg_read(base_phy + DDR_PHY_DMSEL) >> PHY_DMSEL_TPHY_WRDATA_BIT) & ++ PHY_DMSEL_TPHY_WRDATA_MASK; ++ if (tphy_wrdata > PHY_DMSEL_TPHY_WRDATA_MASK) ++ return; ++ ++ dficlk_ratio_sel = (reg_read(base_phy + DDR_PHY_PHYCTRL0) >> ++ PHY_DFICLK_RATIO_BIT) & PHY_DFICLK_RATIO_MASK; ++ if (dficlk_ratio_sel > PHY_DFICLK_RATIO_MASK) ++ return; ++ ++ cfg_rl = (ddr_phy_misc >> PHY_MISC_CFG_RL_BIT) & PHY_MISC_CFG_RL_MASK; ++ cfg_wl = (ddr_phy_misc >> PHY_MISC_CFG_WL_BIT) & PHY_MISC_CFG_WL_MASK; ++ cfg_rl = cfg_rl - ++ (4 + addr_delay - trfc_mpc_cmd_dly * tphy_wrdata) * dficlk_ratio_sel * 2; /* 4,2: Logical design requirements */ ++ cfg_wl = cfg_wl - ++ (4 + addr_delay - trfc_mpc_cmd_dly * tphy_wrdata) * dficlk_ratio_sel * 2; /* 4,2: Logical design requirements */ ++ ddr_phy_misc &= ~((PHY_MISC_CFG_RL_MASK << PHY_MISC_CFG_RL_BIT) | (PHY_MISC_CFG_WL_MASK << PHY_MISC_CFG_WL_BIT)); ++ ddr_phy_misc |= (cfg_rl << PHY_MISC_CFG_RL_BIT) | (cfg_wl << PHY_MISC_CFG_WL_BIT); ++ reg_write(ddr_phy_misc, base_phy + DDR_PHY_MISC); ++ ++ /* enable retrain */ ++ reg_write((trfc_ctrl & (~(DDR_TRFC_EN_MASK << DDR_TRFC_EN_BIT))) | (0x1 << DDR_TRFC_EN_BIT), ++ base_phy + DDR_PHY_TRFC_CTRL); ++} ++ ++/* enable retrain and rdqs anti-aging after ddr training */ ++void ddr_retrain_anti_aging_enable(void) ++{ ++ ddr_retrain_anti_aging_enable_process(DDR_REG_BASE_PHY0); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_retrain_anti_aging_enable_process(DDR_REG_BASE_PHY1); ++#endif ++} ++ ++static void ddr_dmc_auto_pd_by_phy(unsigned int base_phy, unsigned int dmc0, unsigned int dmc1) ++{ ++ unsigned int dmc_pd0; ++ unsigned int pd_prd; ++ ++ dmc_pd0 = reg_read(dmc0 + DDR_DMC_CFG_PD); ++ pd_prd = (dmc_pd0 >> DMC_CFG_PD_PRD_BIT) & DMC_CFG_PD_PRD_MASK; ++ if (pd_prd == 0) ++ return; ++ ++ if ((reg_read(base_phy + DDR_PHY_DRAMCFG) & ++ PHY_DRAMCFG_TYPE_MASK) == PHY_DRAMCFG_TYPE_LPDDR4) { ++ reg_write(dmc_pd0 | (0x1 << DMC_CFG_PD_EN_BIT), dmc0 + DDR_DMC_CFG_PD); ++ reg_write(reg_read(dmc1 + DDR_DMC_CFG_PD) | (0x1 << DMC_CFG_PD_EN_BIT), dmc1 + DDR_DMC_CFG_PD); ++ } else { ++ reg_write(dmc_pd0 | (0x1 << DMC_CFG_PD_EN_BIT), dmc0 + DDR_DMC_CFG_PD); ++ } ++} ++ ++/* ddr DMC auto power down config ++ * the value should config after trainning, or it will cause chip compatibility problems ++ */ ++void ddr_dmc_auto_power_down_cfg(void) ++{ ++ ddr_dmc_auto_pd_by_phy(DDR_REG_BASE_PHY0, DDR_REG_BASE_DMC0, DDR_REG_BASE_DMC1); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_dmc_auto_pd_by_phy(DDR_REG_BASE_PHY1, DDR_REG_BASE_DMC2, DDR_REG_BASE_DMC3); ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++ ddr_dmc_auto_pd_by_phy(DDR_REG_BASE_PHY2, DDR_REG_BASE_DMC4, DDR_REG_BASE_DMC5); ++#endif ++} ++ ++static void ddr_set_rdqbdl_def_val_by_phy(unsigned int base_phy) ++{ ++ unsigned int rank_idx; ++ unsigned int byte_idx; ++ ++ for (rank_idx = 0; rank_idx < 2; rank_idx++) { /* 2:rank number */ ++ for (byte_idx = 0; byte_idx < 4; byte_idx++) { /* 4:byte number */ ++ reg_write(0x20202020, base_phy + ddr_phy_dxnrdqnbdl0(rank_idx, byte_idx)); ++ reg_write(0x20202020, base_phy + ddr_phy_dxnrdqnbdl1(rank_idx, byte_idx)); ++ reg_write(0x20, base_phy + ddr_phy_dxnrdqnbdl2(rank_idx, byte_idx)); ++ } ++ } ++} ++ ++void ddr_set_rdqbdl_def_val(void) ++{ ++ ddr_set_rdqbdl_def_val_by_phy(DDR_REG_BASE_PHY0); ++ ++#ifdef DDR_REG_BASE_PHY1 ++ ddr_set_rdqbdl_def_val_by_phy(DDR_REG_BASE_PHY1); ++#endif ++ ++#ifdef DDR_REG_BASE_PHY2 ++ ddr_set_rdqbdl_def_val_by_phy(DDR_REG_BASE_PHY2); ++#endif ++} +diff --git a/drivers/ddr/vendor/ss928v100/ddr_training_custom.h b/drivers/ddr/vendor/ss928v100/ddr_training_custom.h +new file mode 100644 +index 0000000..f10c2ed +--- /dev/null ++++ b/drivers/ddr/vendor/ss928v100/ddr_training_custom.h +@@ -0,0 +1,100 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DDR_TRAINING_CUSTOM_H ++#define DDR_TRAINING_CUSTOM_H ++ ++/* config DDRC, PHY, DDRT typte */ ++#define DDR_DDRC_V520_S14_CONFIG ++#define DDR_PHY_S14_CONFIG ++#define DDR_DDRT_V2_0_SHF0_CONFIG ++ ++/* config special item */ ++/* s40/t28/t16 not support dcc training */ ++#define DDR_CHANNEL_MAP_PHY0_DMC0_PHY1_DMC2 ++#define DDR_DCC_TRAINING_CONFIG ++ ++#define DDR_WL_TRAINING_DISABLE ++#define DDR_GATE_TRAINING_DISABLE ++#define DDR_TRAINING_UART_DISABLE ++ ++#define DDR_PHY_NUM 2 /* phy number */ ++ ++#define DDR_DMC_PER_PHY_MAX 2 /* dmc number per phy max */ ++ ++/* config DDRC, PHY, DDRT base address */ ++/* [CUSTOM] DDR PHY0 base register */ ++#define DDR_REG_BASE_PHY0 0x11150000 ++/* [CUSTOM] DDR PHY1 base register */ ++#define DDR_REG_BASE_PHY1 0x11152000 ++/* [CUSTOM] DDR DMC0 base register */ ++#define DDR_REG_BASE_DMC0 0x11148000 ++/* [CUSTOM] DDR DMC1 base register */ ++#define DDR_REG_BASE_DMC1 0x11149000 ++/* [CUSTOM] DDR DMC2 base register */ ++#define DDR_REG_BASE_DMC2 0x1114a000 ++/* [CUSTOM] DDR DMC3 base register */ ++#define DDR_REG_BASE_DMC3 0x1114b000 ++/* [CUSTOM] DDR DDRT base register */ ++#define DDR_REG_BASE_DDRT 0x11160000 ++/* [CUSTOM] DDR training item system control */ ++#define DDR_REG_BASE_SYSCTRL 0x11020000 ++#define DDR_REG_BASE_AXI 0x11140000 ++/* Serial Configuration */ ++#define DDR_REG_BASE_UART0 0x11040000 ++ ++/* config offset address */ ++/* Assume sysctrl offset address for DDR training as follows, ++if not please define. */ ++/* [CUSTOM] PHY1 ddrt reversed data */ ++#define SYSCTRL_DDRT_PATTERN 0xa8 ++/* [CUSTOM] PHY2 ddrt reversed data */ ++#define SYSCTRL_DDRT_PATTERN_SEC 0xac ++/* [CUSTOM] ddr sw training item */ ++#define SYSCTRL_DDR_TRAINING_CFG 0xa0 /* RANK0 */ ++#define SYSCTRL_DDR_TRAINING_CFG_SEC 0xa4 /* RANK1 */ ++/* [CUSTOM] ddr training stat */ ++#define SYSCTRL_DDR_TRAINING_STAT 0xb0 ++/* [CUSTOM] ddr training version flag */ ++#define SYSCTRL_DDR_TRAINING_VERSION_FLAG 0xb4 ++ ++/* [CUSTOM] ddr hw training item */ ++#define SYSCTRL_DDR_HW_PHY0_RANK0 0x90 ++#define SYSCTRL_DDR_HW_PHY0_RANK1 0x94 ++#define SYSCTRL_DDR_HW_PHY1_RANK0 0x98 ++#define SYSCTRL_DDR_HW_PHY1_RANK1 0x9c ++ ++/* config other special */ ++/* [CUSTOM] DDR training start address. MEM_BASE_DDR */ ++#define DDRT_CFG_BASE_ADDR 0x40000000 ++/* [CUSTOM] SRAM start address. ++NOTE: Makefile will parse it, plase define it as Hex. eg: 0xFFFF0C00 */ ++#define DDR_TRAINING_RUN_STACK 0x04010c00 ++ ++#define DDR_RELATE_REG_DECLARE ++struct tr_custom_reg { ++ unsigned int ddrt_clk_reg; ++ unsigned int age_compst_en[DDR_PHY_NUM]; ++ unsigned int trfc_en[DDR_PHY_NUM]; ++ unsigned int ddrt_ctrl; ++}; ++int ddr_boot_cmd_save(struct tr_custom_reg *custom_reg); ++void ddr_boot_cmd_restore(const struct tr_custom_reg *custom_reg); ++int ddr_get_cksel(void); ++#endif /* DDR_TRAINING_CUSTOM_H */ +diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c +index 394f30f..5feea56 100644 +--- a/drivers/firmware/psci.c ++++ b/drivers/firmware/psci.c +@@ -47,6 +47,7 @@ unsigned long __efi_runtime invoke_psci_fn + + static int psci_bind(struct udevice *dev) + { ++#if !defined(CONFIG_TARGET_SS928V100) && !defined(CONFIG_TARGET_SS927V100) + /* No SYSTEM_RESET support for PSCI 0.1 */ + if (device_is_compatible(dev, "arm,psci-0.2") || + device_is_compatible(dev, "arm,psci-1.0")) { +@@ -58,7 +59,7 @@ static int psci_bind(struct udevice *dev) + if (ret) + pr_debug("PSCI System Reset was not bound.\n"); + } +- ++#endif + return 0; + } + +diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig +index 85fd190..06dfca9 100644 +--- a/drivers/mmc/Kconfig ++++ b/drivers/mmc/Kconfig +@@ -706,7 +706,13 @@ config MMC_MTK + If you have a machine with a integrated SD/MMC card reader, say Y or M here. + This is needed if support for any SD/SDIO/MMC devices is required. + If unsure, say N. +- ++ ++config MCI ++ bool "MMC controller for VENDOR support" ++ depends on MMC ++ help ++ Support for MMC host controller on VENDOR ARM SoCs platform ++ + endif + + config FSL_ESDHC +diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile +index 9c1f8e5..34d5ff6 100644 +--- a/drivers/mmc/Makefile ++++ b/drivers/mmc/Makefile +@@ -71,3 +71,4 @@ obj-$(CONFIG_MMC_UNIPHIER) += tmio-common.o uniphier-sd.o + obj-$(CONFIG_RENESAS_SDHI) += tmio-common.o renesas-sdhi.o + obj-$(CONFIG_MMC_BCM2835) += bcm2835_sdhost.o + obj-$(CONFIG_MMC_MTK) += mtk-sd.o ++obj-$(CONFIG_BSP_SDHCI) += bsp_sdhci.o +diff --git a/drivers/mmc/bsp_sdhci.c b/drivers/mmc/bsp_sdhci.c +new file mode 100644 +index 0000000..9389187 +--- /dev/null ++++ b/drivers/mmc/bsp_sdhci.c +@@ -0,0 +1,398 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include "mmc_private.h" ++ ++#define MIN_FREQ 400000 ++#define NOT_FOUND (-1) ++#define PHASE_SCALE 32 ++#define EDGE_TUNING_PHASE_STEP 4 ++ ++static void enable_sample(struct sdhci_host *host); ++static void set_drv_phase(struct sdhci_host *host, unsigned int phase); ++static void set_sampl_phase(struct sdhci_host *host, unsigned int phase); ++static void wait_sampl_dll_slave_ready(struct sdhci_host *host); ++static void enable_card_clk(struct sdhci_host *host); ++static void disable_card_clk(struct sdhci_host *host); ++static void enable_internal_clk(struct sdhci_host *host); ++static void disable_internal_clk(struct sdhci_host *host); ++ ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++#include ++#include "bsp_ss928v100.c" ++#endif ++ ++static void enable_card_clk(struct sdhci_host *host) ++{ ++ u16 clk; ++ ++ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); ++ clk |= SDHCI_CLOCK_CARD_EN; ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++} ++ ++static void disable_card_clk(struct sdhci_host *host) ++{ ++ u16 clk; ++ ++ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); ++ clk &= ~SDHCI_CLOCK_CARD_EN; ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++} ++ ++static void enable_internal_clk(struct sdhci_host *host) ++{ ++ u16 clk; ++ u16 timeout = 20; ++ ++ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); ++ clk |= SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN; ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ /* Wait max 20 ms */ ++ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); ++ while (!(clk & SDHCI_CLOCK_INT_STABLE)) { ++ if (timeout == 0) { ++ printf("%s: Internal clock never stabilised.\n", ++ __func__); ++ return; ++ } ++ timeout--; ++ udelay(1000); /* delay 1000us */ ++ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); ++ } ++} ++ ++static void __maybe_unused disable_internal_clk(struct sdhci_host *host) ++{ ++ u16 clk; ++ ++ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); ++ clk &= ~SDHCI_CLOCK_INT_EN; ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++} ++ ++static void set_drv_phase(struct sdhci_host *host, unsigned int phase) ++{ ++ uintptr_t crg_addr; ++ unsigned int reg; ++ ++ crg_addr = (host->type == MMC_TYPE_MMC) ? ++ REG_EMMC_DRV_DLL_CTRL : REG_SDIO0_DRV_DLL_CTRL; ++ reg = readl(crg_addr); ++ reg &= ~SDIO_DRV_PHASE_SEL_MASK; ++ reg |= sdio_drv_sel(phase); ++ writel(reg, crg_addr); ++} ++ ++static void enable_sample(struct sdhci_host *host) ++{ ++ unsigned int reg; ++ ++ reg = sdhci_readl(host, SDHCI_AT_CTRL); ++ reg |= SDHCI_SAMPLE_EN; ++ sdhci_writel(host, reg, SDHCI_AT_CTRL); ++} ++ ++static void set_sampl_phase(struct sdhci_host *host, unsigned int phase) ++{ ++ unsigned int reg; ++ ++ reg = sdhci_readl(host, SDHCI_AT_STAT); ++ reg &= ~SDHCI_PHASE_SEL_MASK; ++ reg |= phase; ++ sdhci_writel(host, reg, SDHCI_AT_STAT); ++} ++ ++static void wait_sampl_dll_slave_ready(struct sdhci_host *host) ++{ ++ unsigned int reg; ++ unsigned int timeout = 20; ++ uintptr_t reg_addr; ++ ++ reg_addr = (host->type == MMC_TYPE_MMC) ? ++ REG_EMMC_SAMPL_DLL_STATUS : REG_SDIO0_SAMPL_DLL_STATUS; ++ do { ++ reg = readl(reg_addr); ++ if (reg & SDIO_SAMPL_DLL_SLAVE_READY) ++ return; ++ ++ udelay(1000); /* delay 1000us */ ++ timeout--; ++ } while (timeout > 0); ++ ++ printf("sdhci-bsp: SAMPL DLL slave not ready.\n"); ++} ++ ++static void enable_edge_tuning(struct sdhci_host *host) ++{ ++ unsigned int reg; ++ ++ reg = readl(REG_EMMC_SAMPLB_DLL_CTRL); ++ reg &= ~SDIO_SAMPLB_DLL_CLK_MASK; ++ reg |= sdio_samplb_sel(8); /* sel 8 */ ++ writel(reg, REG_EMMC_SAMPLB_DLL_CTRL); ++ ++ reg = sdhci_readl(host, SDHCI_MULTI_CYCLE); ++ reg |= SDHCI_EDGE_DETECT_EN; ++ sdhci_writel(host, reg, SDHCI_MULTI_CYCLE); ++} ++ ++static void disable_edge_tuning(struct sdhci_host *host) ++{ ++ unsigned int reg; ++ ++ reg = sdhci_readl(host, SDHCI_MULTI_CYCLE); ++ reg &= ~SDHCI_EDGE_DETECT_EN; ++ sdhci_writel(host, reg, SDHCI_MULTI_CYCLE); ++} ++ ++static void select_sampl_phase(struct sdhci_host *host, unsigned int phase) ++{ ++ disable_card_clk(host); ++ set_sampl_phase(host, phase); ++ wait_sampl_dll_slave_ready(host); ++ enable_card_clk(host); ++ udelay(1); ++} ++ ++static int send_tuning(struct sdhci_host *host, u32 opcode, int* cmd_error) ++{ ++ int count, err; ++ const int tuning_num = 1; ++ ++ count = 0; ++ do { ++ err = mmc_send_tuning(host->mmc, opcode, NULL); ++ if (err) ++ break; ++ ++ count++; ++ } while (count < tuning_num); ++ ++ return err; ++} ++ ++static int bsp_mmc_exec_tuning(struct sdhci_host *host, unsigned int opcode) ++{ ++ unsigned int index, val; ++ unsigned int edge_p2f, edge_f2p, start, end, phase; ++ unsigned int fall, rise, fall_updat_flag; ++ unsigned int found; ++ unsigned int prev_found = 0; ++ int err; ++ int prev_err = 0; ++ unsigned short ctrl; ++ ++ wait_drv_dll_lock(host); ++ enable_sampl_dll_slave(host); ++ enable_sample(host); ++ enable_edge_tuning(host); ++ host->is_tuning = 1; ++ ++ start = 0; ++ end = PHASE_SCALE / EDGE_TUNING_PHASE_STEP; ++ ++ edge_p2f = start; ++ edge_f2p = end; ++ for (index = 0; index <= end; index++) { ++ select_sampl_phase(host, index * EDGE_TUNING_PHASE_STEP); ++ err = mmc_send_tuning(host->mmc, opcode, NULL); ++ if (!err) { ++ val = sdhci_readl(host, SDHCI_MULTI_CYCLE); ++ found = val & SDHCI_FOUND_EDGE; ++ } else { ++ found = 1; ++ } ++ ++ if (prev_found && !found) ++ edge_f2p = index; ++ else if (!prev_found && found) ++ edge_p2f = index; ++ ++ if ((edge_p2f != start) && (edge_f2p != end)) ++ break; ++ ++ prev_found = found; ++ } ++ ++ if ((edge_p2f == start) && (edge_f2p == end)) { ++ printf("sdhci-bsp: tuning failed! can not found edge!\n"); ++ return -1; ++ } ++ ++ disable_edge_tuning(host); ++ ++ start = edge_p2f * EDGE_TUNING_PHASE_STEP; ++ end = edge_f2p * EDGE_TUNING_PHASE_STEP; ++ if (end <= start) ++ end += PHASE_SCALE; ++ ++ fall = start; ++ rise = end; ++ fall_updat_flag = 0; ++ for (index = start; index <= end; index++) { ++ select_sampl_phase(host, index % PHASE_SCALE); ++ ++ err = send_tuning(host, opcode, NULL); ++ if (err) ++ debug("sdhci-bsp: send tuning CMD%u fail! phase:%u err:%d\n", ++ opcode, index, err); ++ ++ if (err && index == start) { ++ if (!fall_updat_flag) { ++ fall_updat_flag = 1; ++ fall = start; ++ } ++ } else { ++ if (!prev_err && err) { ++ if (!fall_updat_flag) { ++ fall_updat_flag = 1; ++ fall = index; ++ } ++ } ++ } ++ ++ if (prev_err && !err) ++ rise = index; ++ ++ if (err && index == end) ++ rise = end; ++ ++ prev_err = err; ++ } ++ ++ phase = ((fall + rise) / 2 + PHASE_SCALE / 2) % PHASE_SCALE; /* 2 for cal average */ ++ ++ printf("sdhci-bsp: tuning done! valid phase shift [%u, %u] Final Phase:%u\n", ++ rise % PHASE_SCALE, fall % PHASE_SCALE, phase); ++ ++ host->tuning_phase = phase; ++ select_sampl_phase(host, phase); ++ ++ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ++ ctrl |= SDHCI_CTRL_TUNED_CLK; ++ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); ++ host->is_tuning = 0; ++ ++ return 0; ++} ++ ++void sdhci_set_host_caps(struct sdhci_host *host) ++{ ++ host->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | ++ MMC_MODE_HS200 | MMC_MODE_4BIT | ++ MMC_MODE_HS400_ES | MMC_MODE_HS400 | ++ MMC_MODE_8BIT; ++} ++ ++int sdhci_add_port(int index, uintptr_t regbase, u32 type) ++{ ++ struct sdhci_host *host = NULL; ++ ++ if (type == MMC_TYPE_MMC) ++ emmc_hardware_init(); ++ else ++ sd_hardware_init(); ++ ++ host = calloc(1, sizeof(struct sdhci_host)); ++ if (host == NULL) { ++ puts("sdhci_host malloc fail!\n"); ++ return -ENOMEM; ++ } ++ ++ host->name = "bsp-sdhci"; ++ host->index = index; ++ host->type = type; ++ host->ioaddr = (void *)regbase; ++ host->quirks = 0; ++ host->set_clock = bsp_mmc_set_clk; ++ host->priv_init = bsp_mmc_priv_init; ++ host->set_control_reg = bsp_mmc_set_ioconfig; ++#ifdef MMC_SUPPORTS_TUNING ++ host->execute_tuning = bsp_mmc_exec_tuning; ++#endif ++ sdhci_set_host_caps(host); ++ add_sdhci(host, CONFIG_BSP_SDHCI_MAX_FREQ, MIN_FREQ); ++ return 0; ++} ++ ++static void print_mmcinfo(struct mmc *mmc) ++{ ++ printf("MMC/SD Card:\n"); ++ printf(" MID: 0x%x\n", mmc->cid[0] >> 24); /* bit24 - 31 */ ++ printf(" Read Block: %d Bytes\n", mmc->read_bl_len); ++ printf(" Write Block: %d Bytes\n", mmc->write_bl_len); ++ printf(" Chip Size: %s Bytes (%s)\n", ++ ultohstr(mmc->capacity), ++ mmc->high_capacity ? "High Capacity" : "Low Capacity"); ++ printf(" Name: \"%c%c%c%c%c\"\n", ++ mmc->cid[0] & 0xff, /* bit0 - 7 */ ++ (mmc->cid[1] >> 24), /* bit24 - 32 */ ++ (mmc->cid[1] >> 16) & 0xff, /* bit16 - 23 */ ++ (mmc->cid[1] >> 8) & 0xff, /* bit8 - 15 */ ++ mmc->cid[1] & 0xff); /* bit0 - 7 */ ++ ++ printf(" Chip Type: %s\n" ++ " Version: %d.%d\n", ++ IS_SD(mmc) ? "SD" : "MMC", ++ EXTRACT_SDMMC_MAJOR_VERSION(mmc->version), ++ EXTRACT_SDMMC_MINOR_VERSION(mmc->version)); ++ ++ printf(" Speed: %sHz\n", ultohstr(mmc->clock)); ++ printf(" Bus Width: %dbit\n", mmc->bus_width); ++ printf(" Mode: %s\n", mmc->strobe_enhanced ? "HS400ES" : ++ (mmc->selected_mode == MMC_HS_400) ? "HS400" : ++ (mmc->selected_mode == MMC_HS_200) ? "HS200" : ++ (mmc->selected_mode == MMC_HS_52) ? "HS_52" : ++ (mmc->selected_mode == MMC_HS) ? "HS" : "DS"); ++} ++ ++int bsp_mmc_init(int dev_num) ++{ ++ struct mmc *mmc = find_mmc_device(dev_num); ++ int ret; ++ ++ if (mmc == NULL) { ++ printf("mmc device not found!!\n"); ++ return -EINVAL; ++ } ++ ++ ret = mmc_init(mmc); ++ if (ret) ++ return ret; ++ ++ if (!IS_SD(mmc)) { ++ print_mmcinfo(mmc); ++ return mmc_set_boot_config(mmc); ++ } ++ ++ return 0; ++} ++ ++void printf_mmc(int dev_num) ++{ ++ struct mmc *mmc = find_mmc_device(dev_num); ++ ++ if (mmc != NULL) ++ print_mmcinfo(mmc); ++} +diff --git a/drivers/mmc/bsp_ss928v100.c b/drivers/mmc/bsp_ss928v100.c +new file mode 100644 +index 0000000..8045417 +--- /dev/null ++++ b/drivers/mmc/bsp_ss928v100.c +@@ -0,0 +1,630 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#define REG_BASE_EMMC_PHY 0x10010000 ++#define EMMC_PHY_INITCTRL (REG_BASE_EMMC_PHY + 0x4) ++#define EMMC_INIT_EN 0x1 ++#define EMMC_DLYMEAS_EN (0x1 << 2) ++#define EMMC_ZCAL_EN (0x1 << 3) ++#define INITCTRL_CHECK_TIMES 100 ++ ++/* CRG register */ ++#define PERI_CRG_MMC_CLK (CRG_REG_BASE + 0x34c0) ++#define PERI_CRG_SDIO0_CLK (CRG_REG_BASE + 0x35c0) ++#define CRG_CLK_SEL_SHIFT 24 ++#define CRG_CLK_SEL_MASK (0x7 << CRG_CLK_SEL_SHIFT) ++#define CRG_SRST_REQ (1 << 16) ++#define CRG_AHB_CLK_EN (1 << 1) ++#define CRG_CLK_EN (1 << 0) ++ ++#define PERI_CRG_MMC_P4_DLL (CRG_REG_BASE + 0x34c4) ++#define CRG_P4_DLL_SRST_REQ (1 << 1) ++ ++#define PERI_CRG_MMC_DRV_DLL (CRG_REG_BASE + 0x34c8) ++#define PERI_CRG_SDIO0_DRV_DLL (CRG_REG_BASE + 0x35c8) ++#define CRG_DRV_PHASE_SEL_SHIFT 15 ++#define CRG_DRV_PHASE_SEL_MASK (0x1F << CRG_DRV_PHASE_SEL_SHIFT) ++ ++#define PERI_CRG_MMC_STAT (CRG_REG_BASE + 0x34d8) ++#define PERI_CRG_SDIO0_STAT (CRG_REG_BASE + 0x35d8) ++#define CRG_SAM_DLL_READY (1 << 12) ++#define CRG_DS_DLL_READY (1 << 10) ++#define CRG_P4_DLL_LOCKED (1 << 9) ++ ++#define REG_EMMC_SAMPL_DLL_STATUS PERI_CRG_MMC_STAT ++#define REG_SDIO0_SAMPL_DLL_STATUS PERI_CRG_SDIO0_STAT ++#define SDIO_SAMPL_DLL_SLAVE_READY CRG_SAM_DLL_READY ++ ++#define REG_EMMC_DRV_DLL_CTRL PERI_CRG_MMC_DRV_DLL ++#define REG_SDIO0_DRV_DLL_CTRL PERI_CRG_SDIO0_DRV_DLL ++#define SDIO_DRV_PHASE_SEL_MASK CRG_DRV_PHASE_SEL_MASK ++#define sdio_drv_sel(phase) ((phase) << CRG_DRV_PHASE_SEL_SHIFT) ++ ++#define REG_EMMC_SAMPLB_DLL_CTRL 0x34d4 ++#define REG_SDIO0_SAMPLB_DLL_CTRL 0x35d4 ++#define SDIO_SAMPLB_DLL_CLK_MASK 0xf ++#define sdio_samplb_sel(phase) (((phase) & 0xf) << 0) ++ ++#define REG_VDP_AIAO_MCU_AHB1_MISC 0x102E0000 ++#define REG_PWR_SWITCH_MISC (REG_VDP_AIAO_MCU_AHB1_MISC + 0x10) ++#define SDIO0_PWRSW_SEL_1V8 BIT(5) ++#define SDIO0_PWR_EN BIT(4) ++#define SDIO0_IO_MODE_SEL_1V8 BIT(1) ++#define SDIO0_PWR_CTRL_BY_MISC BIT(0) ++ ++/* eMMC io reg */ ++#define REG_MMC_IO_CFG_BASE 0x10230000 ++#define REG_MMC_CLK_IO 0x00 ++#define REG_MMC_CMD_IO 0x04 ++#define REG_MMC_D0_IO 0x08 ++#define REG_MMC_D1_IO 0x0c ++#define REG_MMC_D2_IO 0x10 ++#define REG_MMC_D3_IO 0x14 ++#define REG_MMC_D4_IO 0x18 ++#define REG_MMC_D5_IO 0x1c ++#define REG_MMC_D6_IO 0x20 ++#define REG_MMC_D7_IO 0x24 ++#define REG_MMC_DQS_IO 0x28 ++#define REG_MMC_RSTN_IO 0x2c ++ ++/* SDIO0 IO */ ++#define REG_SD_IO_CFG_BASE 0x102f0000 ++#define REG_SDIO0_PWEN_N 0x84 ++#define REG_SDIO0_CMD_IO 0x88 ++#define REG_SDIO0_D0_IO 0x8c ++#define REG_SDIO0_D1_IO 0x90 ++#define REG_SDIO0_D2_IO 0x94 ++#define REG_SDIO0_D3_IO 0x98 ++#define REG_SDIO0_CLK_IO 0x9c ++ ++ /* IO CFG */ ++#define IO_CFG_DRV_STR_MASK (0xf << 4) ++#define io_cfg_drv_str_sel(str) ((str) << 4) ++#define IO_CFG_PULL_UPDOWN_EN (0x1 << 8) ++#define IO_CFG_PULL_UP (0x1 << 9) ++#define IO_CFG_PULL_SDIO_DOWN (0x1 << 9) ++#define IO_CFG_PULL_SDIO_UP (0x1 << 8) ++#define IO_CFG_SDIO_MASK (IO_CFG_DRV_STR_MASK | IO_CFG_PULL_UP | \ ++ IO_CFG_PULL_UPDOWN_EN) ++ ++#define SD_IO_MUX 0x1 ++#define MMC_IO_MUX 0x2 ++#define IO_MUX_MASK 0xF ++#define MMC_BUS_WIDTH_4_BIT 4 ++#define MMC_BUS_WIDTH_8_BIT 8 ++#define SD_IO_NUM 4 ++ ++#define BOOT_FROM_EMMC_FLAG (0x3 << 2) ++#define BOOT_FLAG_MASK (0x3 << 2) ++#define EMMC_BOOT_8BIT (0x1 << 11) ++ ++#define IO_CLK 0 ++#define IO_CMD 1 ++#define IO_DATA 2 ++#define IO_RST 3 ++#define IO_DS 4 ++#define EMMC_IO_TYPE_NUM 5 ++#define SDIO_IO_TYPE_NUM 3 ++ ++static unsigned int reg_data_io[] = { ++ REG_MMC_D0_IO, REG_MMC_D1_IO, ++ REG_MMC_D2_IO, REG_MMC_D3_IO, ++ REG_MMC_D4_IO, REG_MMC_D5_IO, ++ REG_MMC_D6_IO, REG_MMC_D7_IO ++}; ++ ++static unsigned int reg_sdio0_data_io[] = { ++ REG_SDIO0_D0_IO, REG_SDIO0_D1_IO, ++ REG_SDIO0_D2_IO, REG_SDIO0_D3_IO, ++}; ++ ++static u32 mmc_io_cfg[][EMMC_IO_TYPE_NUM] = { /* CLK CMD DATA RST DQS */ ++ [MMC_LEGACY] = { ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN, /* 0x4 is 0b100 */ ++ io_cfg_drv_str_sel(0x2) | IO_CFG_PULL_UPDOWN_EN | /* 0x2 is 0b010 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x2) | IO_CFG_PULL_UPDOWN_EN | /* 0x2 is 0b010 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP ++ }, ++ [MMC_HS] = { ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN, /* 0x4 is 0b100 */ ++ io_cfg_drv_str_sel(0x2) | IO_CFG_PULL_UPDOWN_EN | /* 0x2 is 0b010 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x2) | IO_CFG_PULL_UPDOWN_EN | /* 0x2 is 0b010 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP ++ }, ++ [MMC_HS_52] = { ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN, /* 0x4 is 0b100 */ ++ io_cfg_drv_str_sel(0x2) | IO_CFG_PULL_UPDOWN_EN | /* 0x2 is 0b010 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x2) | IO_CFG_PULL_UPDOWN_EN | /* 0x2 is 0b010 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP ++ }, ++ [MMC_HS_200] = { ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN, /* 0x4 is 0b100 */ ++ io_cfg_drv_str_sel(0x5) | IO_CFG_PULL_UPDOWN_EN | /* 0x5 is 0b101 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x5) | IO_CFG_PULL_UPDOWN_EN | /* 0x5 is 0b101 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP ++ }, ++ [MMC_HS_400] = { ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN, /* 0x4 is 0b100 */ ++ io_cfg_drv_str_sel(0x5) | IO_CFG_PULL_UPDOWN_EN | /* 0x5 is 0b101 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x5) | IO_CFG_PULL_UPDOWN_EN | /* 0x5 is 0b101 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP, ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN /* 0x4 is 0b100 */ ++ }, ++ [MMC_HS_400_ES] = { ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN, /* 0x4 is 0b100 */ ++ io_cfg_drv_str_sel(0x5) | IO_CFG_PULL_UPDOWN_EN | /* 0x5 is 0b101 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x5) | IO_CFG_PULL_UPDOWN_EN | /* 0x5 is 0b101 */ ++ IO_CFG_PULL_UP, ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP, ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_UPDOWN_EN /* 0x4 is 0b100 */ ++ } ++}; ++ ++static u32 sdio0_io_cfg[][SDIO_IO_TYPE_NUM] = { /* CLK CMD DATA */ ++ [SD_LEGACY] = { ++ io_cfg_drv_str_sel(0x9) | IO_CFG_PULL_SDIO_DOWN, /* 0x9 is 0b1001 */ ++ io_cfg_drv_str_sel(0x3) | IO_CFG_PULL_SDIO_UP, /* 0x3 is 0b0011 */ ++ io_cfg_drv_str_sel(0x3) | IO_CFG_PULL_SDIO_UP /* 0x3 is 0b0011 */ ++ }, ++ [SD_HS] = { ++ io_cfg_drv_str_sel(0xa) | IO_CFG_PULL_SDIO_DOWN, /* 0xa is 0b1010 */ ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_SDIO_UP, /* 0x4 is 0b0100 */ ++ io_cfg_drv_str_sel(0x4) | IO_CFG_PULL_SDIO_UP /* 0x4 is 0b0100 */ ++ }, ++ [UHS_SDR12] = { ++ io_cfg_drv_str_sel(0x5) | IO_CFG_PULL_SDIO_DOWN, /* 0x5 is 0b0101 */ ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP, ++ io_cfg_drv_str_sel(0x0) | IO_CFG_PULL_SDIO_UP ++ }, ++ [UHS_SDR25] = { ++ io_cfg_drv_str_sel(0x7) | IO_CFG_PULL_SDIO_DOWN, /* 0x7 is 0b0111 */ ++ io_cfg_drv_str_sel(0x1) | IO_CFG_PULL_SDIO_UP, /* 0x1 is 0b0001 */ ++ io_cfg_drv_str_sel(0x1) | IO_CFG_PULL_SDIO_UP /* 0x1 is 0b0001 */ ++ }, ++ [UHS_SDR50] = { ++ io_cfg_drv_str_sel(0x7) | IO_CFG_PULL_SDIO_DOWN, /* 0x7 is 0b0111 */ ++ io_cfg_drv_str_sel(0x3) | IO_CFG_PULL_SDIO_UP, /* 0x3 is 0b0011 */ ++ io_cfg_drv_str_sel(0x3) | IO_CFG_PULL_SDIO_UP /* 0x3 is 0b0011 */ ++ }, ++ [UHS_SDR104] = { ++ io_cfg_drv_str_sel(0xd) | IO_CFG_PULL_SDIO_DOWN, /* 0xd is 0b1101 */ ++ io_cfg_drv_str_sel(0x7) | IO_CFG_PULL_SDIO_UP, /* 0x7 is 0b0111 */ ++ io_cfg_drv_str_sel(0x7) | IO_CFG_PULL_SDIO_UP /* 0x7 is 0b0111 */ ++ } ++}; ++ ++/* avaliable frequency */ ++#define CLK_400K 400000 ++#define CLK_25M 25000000 ++#define CLK_50M 50000000 ++#define CLK_100M 100000000 ++#define CLK_150M 150000000 ++#define CLK_187P5M 187500000 ++#define CLK_200M 200000000 ++ ++/* sample drive phase */ ++#define DRIVE 0 ++#define SAMPLE 1 ++#define PHASE_TYPE_NUM 2 ++ ++static u32 mmc_phase_cfg[][PHASE_TYPE_NUM] = { /* drv, sampl phase */ ++ [MMC_LEGACY] = { 16, 0 }, /* 16 for 180 degree */ ++ [MMC_HS] = { 16, 0 }, /* 16 for 180 degree */ ++ [MMC_HS_52] = { 16, 4 }, /* 16 for 180 degree */ ++ [MMC_HS_200] = { 18, 0 }, /* 18 for 202.5 degree, 4 for 45 degree */ ++ [MMC_HS_400] = { 7, 0 }, /* 7 for 78.75 degree */ ++ [MMC_HS_400_ES] = { 7, 0 } /* 7 for 78.75 degree */ ++}; ++ ++static u32 sd_phase_cfg[][PHASE_TYPE_NUM] = { ++ [SD_LEGACY] = { 16, 0 }, /* 16 for 180 degree */ ++ [SD_HS] = { 18, 4 }, /* 18 for 202.5 degree, 4 for 45 degree */ ++ [UHS_SDR12] = { 16, 0 }, /* 16 for 180 degree */ ++ [UHS_SDR25] = { 16, 4 }, /* 16 for 180 degree, 4 for 45 degree */ ++ [UHS_SDR50] = { 20, 0 }, /* 20 for 225 degree */ ++ [UHS_SDR104] = { 20, 0 } /* 20 for 225 degree */ ++}; ++ ++/* Do ZQ resistance calibration for eMMC PHY IO */ ++static int resistance_calibration(void) ++{ ++ int i; ++ u32 reg_val; ++ ++ reg_val = readl(EMMC_PHY_INITCTRL); ++ reg_val |= EMMC_INIT_EN | EMMC_ZCAL_EN; ++ writel(reg_val, EMMC_PHY_INITCTRL); ++ ++ for (i = 0; i < INITCTRL_CHECK_TIMES; i++) { ++ reg_val = readl(EMMC_PHY_INITCTRL); ++ if ((reg_val & (EMMC_INIT_EN | EMMC_ZCAL_EN)) == 0) ++ return 0; ++ udelay(10); /* delay 10 us */ ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++unsigned int get_ocr_from_bootrom(void) ++{ ++ return readl(REG_SAVE_HCS); ++} ++ ++void wait_drv_dll_lock(struct sdhci_host *host) ++{ ++ /* doing nothing */ ++} ++ ++void enable_sampl_dll_slave(struct sdhci_host *host) ++{ ++ /* doing nothing */ ++} ++ ++static void dll_reset_assert(void) ++{ ++ const uintptr_t crg_addr = PERI_CRG_MMC_P4_DLL; ++ unsigned int reg; ++ ++ reg = readl(crg_addr); ++ reg |= CRG_P4_DLL_SRST_REQ; ++ writel(reg, crg_addr); ++} ++ ++static void dll_reset_deassert(void) ++{ ++ const uintptr_t crg_addr = PERI_CRG_MMC_P4_DLL; ++ unsigned int reg; ++ ++ reg = readl(crg_addr); ++ reg &= ~CRG_P4_DLL_SRST_REQ; ++ writel(reg, crg_addr); ++} ++ ++static void wait_p4_dll_lock(void) ++{ ++ const uintptr_t reg_addr = PERI_CRG_MMC_STAT; ++ unsigned int timeout = 20; ++ unsigned int reg; ++ ++ do { ++ reg = readl(reg_addr); ++ if (reg & CRG_P4_DLL_LOCKED) ++ return; ++ ++ udelay(1000); /* delay 1000 us */ ++ timeout--; ++ } while (timeout > 0); ++ ++ printf("sdhci-bsp: P4 DLL master not locked.\n"); ++} ++ ++static void wait_ds_dll_ready(void) ++{ ++ const uintptr_t reg_addr = PERI_CRG_MMC_STAT; ++ unsigned int timeout = 20; ++ unsigned int reg; ++ ++ do { ++ reg = readl(reg_addr); ++ if (reg & CRG_DS_DLL_READY) ++ return; ++ ++ udelay(1000); /* delay 1000 us */ ++ timeout--; ++ } while (timeout > 0); ++ ++ printf("sdhci-bsp: DS DLL slave not ready.\n"); ++} ++ ++static void bsp_mmc_priv_init(struct sdhci_host *host) ++{ ++ unsigned int reg; ++ ++ reg = sdhci_readl(host, SDHCI_AXI_MBIIU_CTRL); ++ reg &= ~SDHCI_UNDEFL_INCR_EN; ++ sdhci_writel(host, reg, SDHCI_AXI_MBIIU_CTRL); ++ ++ if (host->type == MMC_TYPE_MMC) { ++ reg = sdhci_readl(host, SDHCI_EMMC_CTRL); ++ reg |= SDHCI_CARD_IS_EMMC; ++ sdhci_writel(host, reg, SDHCI_EMMC_CTRL); ++ } ++} ++ ++static unsigned int get_mmc_bus_width(void) ++{ ++ unsigned int sys_stat; ++ unsigned int bus_width; ++ ++ sys_stat = readl(SYS_CTRL_REG_BASE + REG_SYSSTAT); ++ if ((sys_stat & BOOT_FLAG_MASK) == BOOT_FROM_EMMC_FLAG) { ++ bus_width = (sys_stat & EMMC_BOOT_8BIT) ? ++ MMC_BUS_WIDTH_8_BIT : MMC_BUS_WIDTH_4_BIT; ++ } else { ++ /* up to 4 bit mode support when spi nand start up */ ++ bus_width = MMC_BUS_WIDTH_4_BIT; ++ } ++ ++ bus_width = MMC_BUS_WIDTH_8_BIT; ++ return bus_width; ++} ++ ++static void set_drv_str(unsigned int offset, unsigned int drv_str) ++{ ++ unsigned int reg; ++ const uintptr_t io_addr = REG_MMC_IO_CFG_BASE + offset; ++ ++ reg = readl(io_addr); ++ reg &= ~IO_CFG_SDIO_MASK; ++ reg |= drv_str; ++ writel(reg, io_addr); ++} ++ ++static void set_sd_drv_str(unsigned int offset, unsigned int drv_str) ++{ ++ unsigned int reg; ++ const uintptr_t io_addr = REG_SD_IO_CFG_BASE + offset; ++ ++ reg = readl(io_addr); ++ reg &= ~IO_CFG_SDIO_MASK; ++ reg |= drv_str; ++ writel(reg, io_addr); ++} ++ ++static void bsp_mmc_set_ioconfig(struct sdhci_host *host) ++{ ++ unsigned int selected_mode = host->mmc->selected_mode; ++ unsigned int bus_width; ++ unsigned int i; ++ ++ if (host->type == MMC_TYPE_MMC) { ++ bus_width = get_mmc_bus_width(); ++ ++ set_drv_str(REG_MMC_CLK_IO, mmc_io_cfg[selected_mode][IO_CLK]); ++ set_drv_str(REG_MMC_CMD_IO, mmc_io_cfg[selected_mode][IO_CMD]); ++ for (i = 0; i < bus_width; i++) ++ set_drv_str(reg_data_io[i], ++ mmc_io_cfg[selected_mode][IO_DATA]); ++ set_drv_str(REG_MMC_RSTN_IO, mmc_io_cfg[selected_mode][IO_RST]); ++ ++ if (selected_mode == MMC_HS_400 || ++ selected_mode == MMC_HS_400_ES) ++ set_drv_str(REG_MMC_DQS_IO, ++ mmc_io_cfg[selected_mode][IO_DS]); ++ } else if (host->type == MMC_TYPE_SD) { ++ switch (selected_mode) { ++ case SD_LEGACY: ++ case SD_HS: ++ set_sd_drv_str(REG_SDIO0_CLK_IO, sdio0_io_cfg[selected_mode][IO_CLK]); ++ set_sd_drv_str(REG_SDIO0_CMD_IO, sdio0_io_cfg[selected_mode][IO_CMD]); ++ for (i = 0; i < SD_IO_NUM; i++) ++ set_sd_drv_str(reg_sdio0_data_io[i], sdio0_io_cfg[selected_mode][IO_DATA]); ++ break; ++ default: ++ selected_mode = SD_LEGACY; ++ set_sd_drv_str(REG_SDIO0_CLK_IO, sdio0_io_cfg[selected_mode][IO_CLK]); ++ set_sd_drv_str(REG_SDIO0_CMD_IO, sdio0_io_cfg[selected_mode][IO_CMD]); ++ for (i = 0; i < SD_IO_NUM; i++) ++ set_sd_drv_str(reg_sdio0_data_io[i], sdio0_io_cfg[selected_mode][IO_DATA]); ++ break; ++ } ++ } ++ sdhci_set_uhs_timing(host); ++} ++ ++static void set_phase(struct sdhci_host *host) ++{ ++ unsigned int drv_phase, sample_phase; ++ unsigned int selected_mode = host->mmc->selected_mode; ++ ++ drv_phase = 0; ++ sample_phase = 0; ++ if (host->type == MMC_TYPE_MMC) { ++ if (selected_mode == MMC_HS_400 || ++ selected_mode == MMC_HS_200) ++ sample_phase = host->tuning_phase; ++ else ++ sample_phase = mmc_phase_cfg[selected_mode][SAMPLE]; ++ ++ drv_phase = mmc_phase_cfg[selected_mode][DRIVE]; ++ } else if (host->type == MMC_TYPE_SD) { ++ switch (selected_mode) { ++ case SD_LEGACY: ++ case SD_HS: ++ sample_phase = sd_phase_cfg[selected_mode][SAMPLE]; ++ drv_phase = sd_phase_cfg[selected_mode][DRIVE]; ++ break; ++ default: ++ selected_mode = SD_LEGACY; ++ sample_phase = sd_phase_cfg[selected_mode][SAMPLE]; ++ drv_phase = sd_phase_cfg[selected_mode][DRIVE]; ++ break; ++ } ++ } ++ ++ set_drv_phase(host, drv_phase); ++ enable_sample(host); ++ set_sampl_phase(host, sample_phase); ++ ++ udelay(25); /* delay 25 us */ ++} ++ ++static void set_crg(struct sdhci_host *host, unsigned int clk) ++{ ++ uintptr_t crg_addr; ++ unsigned int sel, reg; ++ unsigned int clk_mux[] = { ++ CLK_400K, CLK_25M, CLK_50M, ++ CLK_100M, CLK_150M, CLK_187P5M, CLK_200M ++ }; ++ ++ crg_addr = (host->type == MMC_TYPE_MMC) ? PERI_CRG_MMC_CLK : PERI_CRG_SDIO0_CLK; ++ for (sel = 0x6; sel > 0; sel--) { ++ if (clk >= clk_mux[sel]) ++ break; ++ } ++ ++ reg = readl(crg_addr); ++ reg &= ~CRG_CLK_SEL_MASK; ++ reg |= mmc_clk_sel(sel); ++ writel(reg, crg_addr); ++} ++ ++static int bsp_mmc_set_clk(struct sdhci_host *host, unsigned int clk) ++{ ++ disable_card_clk(host); ++ udelay(25); /* delay 25 us */ ++ disable_internal_clk(host); ++ ++ if (clk == 0) ++ return 0; ++ ++ set_crg(host, clk); ++ set_phase(host); ++ ++ udelay(5); /* delay 5 us */ ++ ++ enable_internal_clk(host); ++ ++ if ((host->mmc->selected_mode == MMC_HS_400) || ++ (host->mmc->selected_mode == MMC_HS_400_ES) || ++ (host->mmc->selected_mode == MMC_HS_200)) { ++ dll_reset_assert(); ++ dll_reset_deassert(); ++ wait_p4_dll_lock(); ++ wait_sampl_dll_slave_ready(host); ++ } ++ ++ if ((host->mmc->selected_mode == MMC_HS_400) || ++ (host->mmc->selected_mode == MMC_HS_400_ES)) ++ wait_ds_dll_ready(); ++ ++ enable_card_clk(host); ++ udelay(75); /* delay 75 us */ ++ ++ return 0; ++} ++ ++static void set_pin_mux(unsigned int offset, unsigned int pin_mux) ++{ ++ unsigned int reg; ++ const uintptr_t io_addr = REG_MMC_IO_CFG_BASE + offset; ++ ++ reg = readl(io_addr); ++ reg &= ~IO_MUX_MASK; ++ reg |= pin_mux; ++ ++ writel(reg, io_addr); ++} ++ ++static void emmc_pin_mux_config(void) ++{ ++ unsigned int bus_width, i; ++ ++ bus_width = get_mmc_bus_width(); ++ set_pin_mux(REG_MMC_CLK_IO, MMC_IO_MUX); ++ set_pin_mux(REG_MMC_CMD_IO, MMC_IO_MUX); ++ for (i = 0; i < bus_width; i++) ++ set_pin_mux(reg_data_io[i], MMC_IO_MUX); ++ set_pin_mux(REG_MMC_RSTN_IO, MMC_IO_MUX); ++ ++ if (bus_width == MMC_BUS_WIDTH_8_BIT) ++ set_pin_mux(REG_MMC_DQS_IO, MMC_IO_MUX); ++} ++ ++static void bsp_mmc_crg_init(void) ++{ ++ unsigned int reg; ++ ++ /* eMMC clk enable */ ++ reg = readl(PERI_CRG_MMC_CLK); ++ reg |= CRG_CLK_EN; ++ writel(reg, PERI_CRG_MMC_CLK); ++ ++ /* eMMC reset assert*/ ++ reg = readl(PERI_CRG_MMC_CLK); ++ reg |= CRG_SRST_REQ; ++ writel(reg, PERI_CRG_MMC_CLK); ++ udelay(25); /* delay 25 us */ ++ ++ reg = readl(PERI_CRG_MMC_CLK); ++ reg &= ~CRG_CLK_SEL_MASK; ++ reg |= (0x1 << CRG_CLK_SEL_SHIFT); //select 25M clk ++ writel(reg, PERI_CRG_MMC_CLK); ++ udelay(25); /* delay 25 us */ ++ ++ /* eMMC reset deassert */ ++ reg = readl(PERI_CRG_MMC_CLK); ++ reg &= ~CRG_SRST_REQ; ++ writel(reg, PERI_CRG_MMC_CLK); ++ udelay(1); ++} ++ ++static int emmc_hardware_init(void) ++{ ++ int ret = -1; ++ emmc_pin_mux_config(); ++ ret = resistance_calibration(); ++ if (ret != 0) ++ return ret; ++ bsp_mmc_crg_init(); ++ ++ udelay(5000); /* delay 5000 us */ ++ return 0; ++} ++ ++static int sd_hardware_init(void) ++{ ++ unsigned int reg; ++ /* clk enable */ ++ reg = readl(PERI_CRG_SDIO0_CLK); ++ reg |= CRG_CLK_EN; ++ writel(reg, PERI_CRG_SDIO0_CLK); ++ ++ /* reset assert*/ ++ reg = readl(PERI_CRG_SDIO0_CLK); ++ reg |= CRG_SRST_REQ; ++ writel(reg, PERI_CRG_SDIO0_CLK); ++ udelay(25); /* delay 25 us */ ++ ++ /* reset deassert */ ++ reg &= ~CRG_SRST_REQ; ++ writel(reg, PERI_CRG_SDIO0_CLK); ++ udelay(1); ++ ++ reg = readl(REG_PWR_SWITCH_MISC); ++ reg |= SDIO0_PWR_EN | SDIO0_PWR_CTRL_BY_MISC; ++ reg &= ~(SDIO0_IO_MODE_SEL_1V8 | SDIO0_PWRSW_SEL_1V8); ++ writel(reg, REG_PWR_SWITCH_MISC); ++ ++ udelay(5000); /* delay 5000 us */ ++ ++ return 0; ++} +diff --git a/drivers/mmc/mci.h b/drivers/mmc/mci.h +new file mode 100644 +index 0000000..c68d7cc +--- /dev/null ++++ b/drivers/mmc/mci.h +@@ -0,0 +1,149 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef _MCI_V200_H_ ++#define _MCI_V200_H_ ++ ++#define POWER_ON 0x1 ++#define POWER_OFF 0x0 ++ ++#define CARD_UNPLUGED 0x1 ++#define CARD_PLUGED 0x0 ++ ++#define ENABLE 0x1 ++#define DISABLE 0x0 ++ ++#define MCI_DETECT_TIMEOUT (HZ / 2) ++ ++#define MCI_REQUEST_TIMEOUT (5 * HZ) ++ ++#define MAX_RETRY_COUNT 100000 ++ ++#define MMC_CLK 100000000 ++#define MMC_CCLK_MIN 400000 ++ ++#define MCI_DEBUG DISABLE ++/* Open it as you need #define MCI_DEBUG ENABLE */ ++ ++#if MCI_DEBUG ++extern int debug_type; ++#define MCI_DEBUG_TYPE (MCI_DEBUG_TYPE_REG | \ ++ MCI_DEBUG_TYPE_FUN | \ ++ MCI_DEBUG_TYPE_CMD | \ ++ MCI_DEBUG_TYPE_INFO | \ ++ MCI_DEBUG_TYPE_ERR) ++ ++#define MCI_DEBUG_TYPE_REG (0x01 << 0) ++#define MCI_DEBUG_TYPE_FUN (0x01 << 1) ++#define MCI_DEBUG_TYPE_CMD (0x01 << 2) ++#define MCI_DEBUG_TYPE_INFO (0x01 << 3) ++#define MCI_DEBUG_TYPE_ERR (0x01 << 4) ++ ++#define MCI_DEBUG_FMT "MCI_DEBUG " ++ ++extern char *get_debug_type_string(int type); ++#define mci_debug(type, msg...) do { \ ++ if (debug_type & type) { \ ++ printf(MCI_DEBUG_FMT "(%s) %s:%d: ", \ ++ get_debug_type_string(type), \ ++ __func__, __LINE__); \ ++ printf(msg); \ ++ printf("\n"); \ ++ } \ ++} while (0) ++ ++#define mci_debug_reg(msg...) mci_debug(MCI_DEBUG_TYPE_REG, msg) ++#define mci_debug_fun(msg...) mci_debug(MCI_DEBUG_TYPE_FUN, msg) ++#define mci_debug_cmd(msg...) mci_debug(MCI_DEBUG_TYPE_CMD, msg) ++#define mci_debug_info(msg...) mci_debug(MCI_DEBUG_TYPE_INFO, msg) ++#define mci_debug_err(msg...) mci_debug(MCI_DEBUG_TYPE_ERR, msg) ++ ++#define MCI_ASSERT_FMT "MCI_ASSERT " ++ ++#define mci_assert(cond) do { \ ++ if (!(cond)) { \ ++ printf(MCI_ASSERT_FMT "%s:%d\n", __func__, __LINE__); \ ++ BUG(); \ ++ } \ ++} while (0) ++#else ++#define mci_debug(type, msg...) ++#define mci_debug_reg(msg...) ++#define mci_debug_fun(msg...) ++#define mci_debug_cmd(msg...) ++#define mci_debug_info(msg...) ++#define mci_debug_err(msg...) ++#define mci_assert(cond) ++#endif ++ ++#define MCI_ERROR_FMT "MCI_ERROR " ++ ++#define mci_error(s...) do { \ ++ printf(MCI_ERROR_FMT "%s:%d: ", __func__, __LINE__); \ ++ printf(s); \ ++ printf("\n"); \ ++} while (0) ++ ++#define mci_readl(addr) ((unsigned int)(readl((uintptr_t)(addr)))) ++ ++#define mci_writel(v, addr) do {writel(v, (uintptr_t)(addr)); \ ++ mci_debug_reg("writel(0x%04X) = 0x%08X", (uintptr_t)(addr), (unsigned int)(v)); \ ++} while (0) ++ ++struct mci_dma_des { ++ unsigned long idmac_des_ctrl; ++ unsigned long idmac_des_buf_size; ++ unsigned long idmac_des_buf_addr; ++ unsigned long idmac_des_next_addr; ++}; ++ ++struct mci_host { ++ const char *name; ++ struct mmc *mmc; ++ unsigned long base; ++ unsigned int card_status; ++ unsigned int dev_id; ++ unsigned int port; ++ struct mmc_cmd *cmd; ++ struct mci_dma_des *dma_des; ++ struct mmc_config cfg; ++ unsigned int is_tuning; ++}; ++ ++typedef union { ++ unsigned int cmd_arg; ++ struct cmd_bits_arg { ++ unsigned int cmd_index : 6; ++ unsigned int response_expect : 1; ++ unsigned int response_length : 1; ++ unsigned int check_response_crc : 1; ++ unsigned int data_transfer_expected : 1; ++ unsigned int read_write : 1; ++ unsigned int transfer_mode : 1; ++ unsigned int send_auto_stop : 1; ++ unsigned int wait_prvdata_complete : 1; ++ unsigned int stop_abort_cmd : 1; ++ unsigned int send_initialization : 1; ++ unsigned int card_number : 5; ++ unsigned int update_clk_reg_only : 1; /* bit 21 */ ++ unsigned int reserved : 9; ++ unsigned int start_cmd : 1; /* HSB */ ++ } bits; ++} cmd_arg_s; ++ ++#endif +diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c +old mode 100644 +new mode 100755 +index f683b52..08f5e70 +--- a/drivers/mmc/mmc.c ++++ b/drivers/mmc/mmc.c +@@ -364,8 +364,17 @@ int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error) + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); +- if (err) ++ if (err) { ++ /* ++ * Send STOP command after tuning fail to stop transmission on card ++ * we don't care if this STOP command fails or not ++ */ ++ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; ++ cmd.cmdarg = 0; ++ cmd.resp_type = MMC_RSP_R1; ++ mmc_send_cmd(mmc, &cmd, NULL); + return err; ++ } + + if (memcmp(data_buf, tuning_block_pattern, size)) + return -EIO; +@@ -724,7 +733,7 @@ static int mmc_complete_op_cond(struct mmc *mmc) + } + + +-static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) ++int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) + { + struct mmc_cmd cmd; + struct mmc_data data; +@@ -787,8 +796,10 @@ static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, + * capable of polling by using mmc_wait_dat0, then rely on waiting the + * stated timeout to be sufficient. + */ +- if (ret == -ENOSYS && !send_status) ++ if (ret == -ENOSYS && !send_status) { + mdelay(timeout_ms); ++ return 0; ++ } + + /* Finally wait until the card is ready or indicates a failure + * to switch. It doesn't hurt to use CMD13 here even if send_status +@@ -1533,6 +1544,9 @@ static inline int bus_width(uint cap) + #ifdef MMC_SUPPORTS_TUNING + static int mmc_execute_tuning(struct mmc *mmc, uint opcode) + { ++ if (mmc->cfg->ops->execute_tuning) ++ return mmc->cfg->ops->execute_tuning(mmc, opcode); ++ + return -ENOTSUPP; + } + #endif +@@ -1971,12 +1985,6 @@ static int mmc_select_hs400(struct mmc *mmc) + #endif + + #if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) +-#if !CONFIG_IS_ENABLED(DM_MMC) +-static int mmc_set_enhanced_strobe(struct mmc *mmc) +-{ +- return -ENOTSUPP; +-} +-#endif + static int mmc_select_hs400es(struct mmc *mmc) + { + int err; +@@ -2002,7 +2010,14 @@ static int mmc_select_hs400es(struct mmc *mmc) + if (err) + return err; + +- return mmc_set_enhanced_strobe(mmc); ++ mmc->strobe_enhanced = 1; ++ ++ if (mmc->cfg->ops->hs400_enable_es) { ++ mmc->cfg->ops->hs400_enable_es(mmc, true); ++ return 0; ++ } else { ++ return -ENOTSUPP; ++ } + } + #else + static int mmc_select_hs400es(struct mmc *mmc) +@@ -2139,6 +2154,8 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) + if (!err) + return 0; + error: ++ mmc->strobe_enhanced = 0; ++ + mmc_set_signal_voltage(mmc, old_voltage); + /* if an error occured, revert to a safer bus mode */ + mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, +diff --git a/drivers/mmc/mmc_boot.c b/drivers/mmc/mmc_boot.c +index 64dc147..a31bace 100644 +--- a/drivers/mmc/mmc_boot.c ++++ b/drivers/mmc/mmc_boot.c +@@ -8,6 +8,9 @@ + #include + #include "mmc_private.h" + ++#include ++#include ++ + /* + * This function changes the size of boot partition and the size of rpmb + * partition present on EMMC devices. +@@ -125,3 +128,102 @@ int mmc_set_rst_n_function(struct mmc *mmc, u8 enable) + return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION, + enable); + } ++ ++u8 mmc_get_boot_mode(void) ++{ ++ u8 boot_mode; ++ boot_mode = 0; ++ return boot_mode; ++} ++ ++#define EXT_CSD_REV_4_41 5 ++ ++int mmc_set_boot_config(struct mmc *mmc) ++{ ++ int err, changed = 0; ++ u8 val, rev, rst_n_en; ++ u8 boot_part, boot_bus_width, part_conf, bus_cond, boot_mode; ++ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); ++ ++ err = mmc_send_ext_csd(mmc, ext_csd); ++ if (err) { ++ printf("Get ext_csd error!\n"); ++ return err; ++ } ++ ++ rev = ext_csd[EXT_CSD_REV]; ++ rst_n_en = ext_csd[EXT_CSD_RST_N_FUNCTION] & EXT_CSD_RST_N_EN_MASK; ++ if ((rev >= EXT_CSD_REV_4_41) && (rst_n_en != EXT_CSD_RST_N_ENABLED)) { ++ err = mmc_set_rst_n_function(mmc, EXT_CSD_RST_N_ENABLED); ++ if (err) { ++ printf("mmc_set_rst_n_function error!\n"); ++ return err; ++ } ++ } ++ ++ if ((rev >= EXT_CSD_REV_4_41) && ++ (ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) { ++ val = ext_csd[EXT_CSD_WR_REL_SET]; ++ if (val != EXT_CSD_WR_REL_VALUE) { ++ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, ++ EXT_CSD_WR_REL_SET, ++ EXT_CSD_WR_REL_VALUE); ++ if (err) { ++ printf("Set EXT_CSD_WR_REL_SET error!\n"); ++ return err; ++ } ++ } ++ } ++ ++ boot_part = 0x7; /* user area enable for boot */ ++ if (readl(REG_BASE_SCTL + REG_SYSSTAT) & NF_BOOTBW_MASK) ++ boot_bus_width = EXT_CSD_BUS_WIDTH_8; /* 8bits */ ++ else ++ boot_bus_width = EXT_CSD_BUS_WIDTH_4; /* 4bits */ ++ ++ boot_mode = mmc_get_boot_mode(); ++ part_conf = EXT_CSD_BOOT_ACK(0) | ++ EXT_CSD_BOOT_PART_NUM(boot_part) | ++ EXT_CSD_PARTITION_ACCESS(0); ++ bus_cond = EXT_CSD_BOOT_BUS_WIDTH_MODE(boot_mode) | ++ EXT_CSD_BOOT_BUS_WIDTH_RESET(0) | ++ EXT_CSD_BOOT_BUS_WIDTH_WIDTH(boot_bus_width); ++ ++ if (ext_csd[EXT_CSD_PART_CONF] != part_conf) { ++ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, ++ EXT_CSD_PART_CONF, part_conf); ++ if (err) { ++ printf("Set EXT_CSD_PART_CONF error!\n"); ++ return err; ++ } ++ changed = 1; ++ } ++ ++ if (ext_csd[EXT_CSD_BOOT_BUS_WIDTH] != bus_cond) { ++ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, ++ EXT_CSD_BOOT_BUS_WIDTH, bus_cond); ++ if (err) { ++ printf("Set EXT_CSD_BOOT_BUS_WIDTH error!\n"); ++ return err; ++ } ++ changed = 1; ++ } ++ ++ if (!changed) ++ return 0; ++ ++ err = mmc_send_ext_csd(mmc, ext_csd); ++ if (err) { ++ printf("Check ext_csd error!\n"); ++ return err; ++ } ++ ++ if (part_conf != ext_csd[EXT_CSD_PART_CONF] || ++ bus_cond != ext_csd[EXT_CSD_BOOT_BUS_WIDTH]) { ++ printf("EXT_CSD CONFIG Fail!\n"); ++ return -1; ++ } ++ ++ printf("EXT_CSD CONFIG OK!\n"); ++ return 0; ++} +diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c +index 01fa5a9..4e5f1cf 100644 +--- a/drivers/mmc/sdhci.c ++++ b/drivers/mmc/sdhci.c +@@ -22,6 +22,66 @@ void *aligned_buffer = (void *)CONFIG_FIXED_SDHCI_ALIGNED_BUFFER; + void *aligned_buffer; + #endif + ++static void sdhci_dumpregs(struct sdhci_host *host) ++{ ++ printf("=========== REGISTER DUMP (mmc%d)===========\n", host->index); ++ ++ printf("Sys addr: 0x%08x | Version: 0x%08x\n", ++ sdhci_readl(host, SDHCI_DMA_ADDRESS), ++ sdhci_readw(host, SDHCI_HOST_VERSION)); ++ printf("Blk size: 0x%08x | Blk cnt: 0x%08x\n", ++ sdhci_readw(host, SDHCI_BLOCK_SIZE), ++ sdhci_readw(host, SDHCI_BLOCK_COUNT)); ++ printf("Argument: 0x%08x | Trn mode: 0x%08x\n", ++ sdhci_readl(host, SDHCI_ARGUMENT), ++ sdhci_readw(host, SDHCI_TRANSFER_MODE)); ++ printf("Present: 0x%08x | Host ctl: 0x%08x\n", ++ sdhci_readl(host, SDHCI_PRESENT_STATE), ++ sdhci_readb(host, SDHCI_HOST_CONTROL)); ++ printf("Power: 0x%08x | Blk gap: 0x%08x\n", ++ sdhci_readb(host, SDHCI_POWER_CONTROL), ++ sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); ++ printf("Wake-up: 0x%08x | Clock: 0x%08x\n", ++ sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), ++ sdhci_readw(host, SDHCI_CLOCK_CONTROL)); ++ printf("Timeout: 0x%08x | Int stat: 0x%08x\n", ++ sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), ++ sdhci_readl(host, SDHCI_INT_STATUS)); ++ printf("Int enab: 0x%08x | Sig enab: 0x%08x\n", ++ sdhci_readl(host, SDHCI_INT_ENABLE), ++ sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); ++ printf("ACMD stat:0x%08x | Slot int: 0x%08x\n", ++ sdhci_readw(host, SDHCI_ACMD12_ERR), ++ sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); ++ printf("Caps: 0x%08x | Caps_1: 0x%08x\n", ++ sdhci_readl(host, SDHCI_CAPABILITIES), ++ sdhci_readl(host, SDHCI_CAPABILITIES_1)); ++ printf("Cmd: 0x%08x | Max curr: 0x%08x\n", ++ sdhci_readw(host, SDHCI_COMMAND), ++ sdhci_readl(host, SDHCI_MAX_CURRENT)); ++ printf("Resp[0]: 0x%08x | Resp[1]: 0x%08x\n", ++ sdhci_readl(host, SDHCI_RESPONSE), ++ sdhci_readl(host, SDHCI_RESPONSE + 4)); ++ printf("Resp[2]: 0x%08x | Resp[3]: 0x%08x\n", ++ sdhci_readl(host, SDHCI_RESPONSE + 8), ++ sdhci_readl(host, SDHCI_RESPONSE + 12)); ++ printf("Host ctl2:0x%08x\n", ++ sdhci_readw(host, SDHCI_HOST_CONTROL2)); ++ ++ if (host->flags & USE_ADMA64) { ++ printf("ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n", ++ sdhci_readl(host, SDHCI_ADMA_ERROR), ++ sdhci_readl(host, SDHCI_ADMA_ADDRESS_HI), ++ sdhci_readl(host, SDHCI_ADMA_ADDRESS)); ++ } else if (host->flags & USE_ADMA) { ++ printf("ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", ++ sdhci_readl(host, SDHCI_ADMA_ERROR), ++ sdhci_readl(host, SDHCI_ADMA_ADDRESS)); ++ } ++ ++ printf("===========================================\n"); ++} ++ + static void sdhci_reset(struct sdhci_host *host, u8 mask) + { + unsigned long timeout; +@@ -71,7 +131,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) + } + + #if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) +-static void sdhci_adma_desc(struct sdhci_host *host, char *buf, u16 len, ++static void _sdhci_adma_desc(struct sdhci_host *host, char *buf, u16 len, + bool end) + { + struct sdhci_adma_desc *desc; +@@ -94,6 +154,23 @@ static void sdhci_adma_desc(struct sdhci_host *host, char *buf, u16 len, + #endif + } + ++static void sdhci_adma_desc(struct sdhci_host *host, char *buf, u16 len, ++ bool end) ++{ ++ u16 split_len; ++ ++ if (((uintptr_t)buf & (SDHCI_DMA_BOUNDARY_SIZE - 1)) + len > ++ SDHCI_DMA_BOUNDARY_SIZE) { ++ split_len = SDHCI_DMA_BOUNDARY_SIZE - ++ ((uintptr_t)buf & (SDHCI_DMA_BOUNDARY_SIZE - 1)); ++ _sdhci_adma_desc(host, buf, split_len, false); ++ buf += split_len; ++ len -= split_len; ++ } ++ ++ _sdhci_adma_desc(host, buf, len, end); ++} ++ + static void sdhci_prepare_adma_table(struct sdhci_host *host, + struct mmc_data *data) + { +@@ -118,7 +195,7 @@ static void sdhci_prepare_adma_table(struct sdhci_host *host, + sdhci_adma_desc(host, buf, trans_bytes, true); + + flush_cache((dma_addr_t)host->adma_desc_table, +- ROUND(desc_count * sizeof(struct sdhci_adma_desc), ++ ROUND((host->desc_slot + 1) * sizeof(struct sdhci_adma_desc), + ARCH_DMA_MINALIGN)); + } + #elif defined(CONFIG_MMC_SDHCI_SDMA) +@@ -175,7 +252,7 @@ static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, + SDHCI_ADMA_ADDRESS_HI); + } + +- flush_cache(host->start_addr, ROUND(trans_bytes, ARCH_DMA_MINALIGN)); ++ flush_cache(rounddown(host->start_addr, ARCH_DMA_MINALIGN), roundup(trans_bytes + ARCH_DMA_MINALIGN, ARCH_DMA_MINALIGN)); + } + #else + static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, +@@ -194,8 +271,11 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) + do { + stat = sdhci_readl(host, SDHCI_INT_STATUS); + if (stat & SDHCI_INT_ERROR) { +- pr_debug("%s: Error detected in status(0x%X)!\n", +- __func__, stat); ++ if (!host->is_tuning) { ++ pr_debug("%s: Error detected in status(0x%X)!\n", ++ __func__, stat); ++ sdhci_dumpregs(host); ++ } + return -EIO; + } + if (!transfer_done && (stat & rdy)) { +@@ -228,6 +308,7 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) + udelay(10); + else { + printf("%s: Transfer data timeout\n", __func__); ++ sdhci_dumpregs(host); + return -ETIMEDOUT; + } + } while (!(stat & SDHCI_INT_DATA_END)); +@@ -393,21 +474,36 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, + return -ECOMM; + } + +-#if defined(CONFIG_DM_MMC) && defined(MMC_SUPPORTS_TUNING) ++static void sdhci_hs400_enhanced_stobe(struct mmc *mmc, bool enable) ++{ ++ struct sdhci_host *host = mmc->priv; ++ u32 ctrl; ++ ++ ctrl = sdhci_readl(host, SDHCI_EMMC_CTRL); ++ if (enable) ++ ctrl |= SDHCI_ENH_STROBE_EN; ++ else ++ ctrl &= ~SDHCI_ENH_STROBE_EN; ++ ++ sdhci_writel(host, ctrl, SDHCI_EMMC_CTRL); ++} ++ ++#if defined(MMC_SUPPORTS_TUNING) ++#if defined(CONFIG_DM_MMC) + static int sdhci_execute_tuning(struct udevice *dev, uint opcode) + { +- int err; + struct mmc *mmc = mmc_get_mmc_dev(dev); ++#else ++static int sdhci_execute_tuning(struct mmc *mmc, uint opcode) ++{ ++#endif + struct sdhci_host *host = mmc->priv; + + debug("%s\n", __func__); + +- if (host->ops && host->ops->platform_execute_tuning) { +- err = host->ops->platform_execute_tuning(mmc, opcode); +- if (err) +- return err; +- return 0; +- } ++ if (host->execute_tuning) ++ return host->execute_tuning(host, opcode); ++ + return 0; + } + #endif +@@ -478,8 +574,8 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock) + div >>= 1; + } + +- if (host->ops && host->ops->set_clock) +- host->ops->set_clock(host, div); ++ if (host->set_clock) ++ return host->set_clock(host, clock); + + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; + clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) +@@ -544,8 +640,13 @@ void sdhci_set_uhs_timing(struct sdhci_host *host) + reg &= ~SDHCI_CTRL_UHS_MASK; + + switch (mmc->selected_mode) { +- case UHS_SDR50: ++ case MMC_HS: + case MMC_HS_52: ++ case SD_HS: ++ case UHS_SDR25: ++ reg |= SDHCI_CTRL_UHS_SDR25; ++ break; ++ case UHS_SDR50: + reg |= SDHCI_CTRL_UHS_SDR50; + break; + case UHS_DDR50: +@@ -556,6 +657,10 @@ void sdhci_set_uhs_timing(struct sdhci_host *host) + case MMC_HS_200: + reg |= SDHCI_CTRL_UHS_SDR104; + break; ++ case MMC_HS_400: ++ case MMC_HS_400_ES: ++ reg |= SDHCI_CTRL_HS400; ++ break; + default: + reg |= SDHCI_CTRL_UHS_SDR12; + } +@@ -574,8 +679,8 @@ static int sdhci_set_ios(struct mmc *mmc) + u32 ctrl; + struct sdhci_host *host = mmc->priv; + +- if (host->ops && host->ops->set_control_reg) +- host->ops->set_control_reg(host); ++ if (host->set_control_reg) ++ host->set_control_reg(host); + + if (mmc->clock != host->clock) + sdhci_set_clock(mmc, mmc->clock); +@@ -641,6 +746,9 @@ static int sdhci_init(struct mmc *mmc) + + sdhci_set_power(host, fls(mmc->cfg->voltages) - 1); + ++ if (host->priv_init) ++ host->priv_init(host); ++ + if (host->ops && host->ops->get_cd) + host->ops->get_cd(host); + +@@ -704,6 +812,10 @@ static const struct mmc_ops sdhci_ops = { + .send_cmd = sdhci_send_command, + .set_ios = sdhci_set_ios, + .init = sdhci_init, ++#ifdef MMC_SUPPORTS_TUNING ++ .execute_tuning = sdhci_execute_tuning, ++#endif ++ .hs400_enable_es = sdhci_hs400_enhanced_stobe, + }; + #endif + +diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig +index 5e7571c..b7ce910 100644 +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -101,6 +101,22 @@ config HBMC_AM654 + This is the driver for HyperBus controller on TI's AM65x and + other SoCs + ++config FMC ++ bool "Enable FMC - Flash Memory Controller" ++ depends on TARGET_SS928V100 || TARGET_SS927V100 ++ help ++ Flash Memory Controller support SPI Nor SPI Nand and parallel ++ Nand Flash. often used on embedded chip. This option will provide the ++ generic support for FMC drivers to register. ++ ++config BSP_NAND_SPL ++ bool "u-boot image backup support" ++ depends on FMC ++ default n ++ help ++ u-boot image backup support ++ if unsure, say N ++ + source "drivers/mtd/nand/Kconfig" + + source "drivers/mtd/spi/Kconfig" +diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile +index 318788c..349b963 100644 +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -17,6 +17,9 @@ mtd-$(CONFIG_ST_SMI) += st_smi.o + mtd-$(CONFIG_STM32_FLASH) += stm32_flash.o + mtd-$(CONFIG_RENESAS_RPC_HF) += renesas_rpc_hf.o + mtd-$(CONFIG_HBMC_AM654) += hbmc-am654.o ++obj-$(CONFIG_FMC) += fmc_common.o ++obj-$(CONFIG_TARGET_SS928V100) += fmc_ss928v100.o ++obj-$(CONFIG_TARGET_SS927V100) += fmc_ss928v100.o + + # U-Boot build + ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TPL_BUILD),) +diff --git a/drivers/mtd/fmc_common.c b/drivers/mtd/fmc_common.c +new file mode 100644 +index 0000000..2ae64f7 +--- /dev/null ++++ b/drivers/mtd/fmc_common.c +@@ -0,0 +1,143 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include "securec.h" ++#define BUFF_LEN 20 ++ ++static unsigned char fmc_current_dev_type = FLASH_TYPE_DEFAULT; ++static unsigned char fmc_boot_dev_type = FLASH_TYPE_DEFAULT; ++ ++unsigned char fmc_ip_user; ++unsigned char fmc_cs_user[CONFIG_FMC_MAX_CS_NUM]; ++ ++void *get_fmc_ip(void) ++{ ++ return &fmc_ip_user; ++} ++ ++unsigned char *get_cs_number(unsigned char cs) ++{ ++ return fmc_cs_user + cs; ++} ++ ++int fmc_ip_ver_check(void) ++{ ++ unsigned int fmc_ip_ver; ++ ++ fmc_pr(FMC_INFO, "Check Flash Memory Controller v100 ..."); ++ fmc_ip_ver = readl(CONFIG_FMC_REG_BASE + FMC_VERSION); ++ if (fmc_ip_ver != FMC_VER_100) { ++ printf("\n"); ++ return -EFAULT; ++ } ++ fmc_pr(FMC_INFO, " Found\n"); ++ ++ return 0; ++} ++ ++void fmc_dev_type_switch(unsigned char type) ++{ ++ unsigned int reg, spi_device_type, flash_type; ++ const char *str[] = {"SPI nor", "SPI nand", "Nand", "Boot"}; ++ ++ if (fmc_current_dev_type == type) ++ return; ++ ++ fmc_pr(BT_DBG, "\t|*-Start switch current device type\n"); ++ ++ if (type > FLASH_TYPE_DEFAULT) { ++ fmc_pr(BT_DBG, "\t||-Switch unknown device type %d\n", type); ++ return; ++ } ++ ++ if (fmc_boot_dev_type == FLASH_TYPE_DEFAULT) { ++ reg = readl((void *)(SYS_CTRL_REG_BASE + REG_SYSSTAT)); ++ fmc_pr(BT_DBG, "\t||-Get system STATUS[%#x]%#x\n", ++ SYS_CTRL_REG_BASE + REG_SYSSTAT, reg); ++ fmc_boot_dev_type = get_spi_device_type(reg); ++ fmc_pr(BT_DBG, "\t||-Init boot device type to %s flash\n", ++ str[fmc_boot_dev_type]); ++ } ++ ++ if (type == FLASH_TYPE_DEFAULT) ++ spi_device_type = fmc_boot_dev_type; ++ else ++ spi_device_type = type; ++ ++ fmc_pr(BT_DBG, "\t||-Switch type to %s flash\n", str[type]); ++ reg = readl((void *)(CONFIG_FMC_REG_BASE + FMC_CFG)); ++ fmc_pr(BT_DBG, "\t||-Get FMC CFG[%#x]%#x\n", FMC_CFG, reg); ++ flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT; ++ if (spi_device_type != flash_type) { ++ reg &= ~FLASH_SEL_MASK; ++ reg |= fmc_cfg_flash_sel(spi_device_type); ++ writel(reg, (void *)(CONFIG_FMC_REG_BASE + FMC_CFG)); ++ fmc_pr(BT_DBG, "\t||-Set FMC CFG[%#x]%#x\n", FMC_CFG, reg); ++ } ++ fmc_current_dev_type = spi_device_type; ++ ++ fmc_pr(BT_DBG, "\t|*-End switch current device type\n"); ++} ++ ++char *ulltostr(unsigned long long size) ++{ ++ int ix; ++ static char buffer[BUFF_LEN]; ++ char *fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT"}; ++ /* 4 size type ,0x3ff Obtains the lower six bits*/ ++ for (ix = 0; (ix < 4) && !(size & 0x3FF) && size; ix++) ++ size = (size >> 10); /* byte to kb, right left 10 */ ++ ++ if (sprintf_s(buffer, BUFF_LEN, fmt[ix], (unsigned)size) < 0) { ++ printf("ERR:sprintf_s fail %s %d", __func__, __LINE__); ++ return NULL; ++ } ++ ++ return buffer; ++} ++ ++void debug_register_dump(void) ++{ ++ unsigned int ix; ++ unsigned long base = CONFIG_FMC_REG_BASE; ++ ++ printf("Register dump:"); ++ /* 0x98 Register length , 4 is Byte */ ++ for (ix = 0; ix <= 0x98; ix += 0x04) { ++ if (!(ix & 0x0F)) ++ printf("\n0x%08lX: ", base + ix); ++ printf("%08X ", readl((void *)(uintptr_t)(base + ix))); ++ } ++ printf("\n"); ++} ++ ++/* REG_SYSSTAT 0: 3 Bytes address boot mode; 1: 4Bytes address boot mode */ ++unsigned int get_fmc_boot_mode(void) ++{ ++ unsigned int regval; ++ unsigned int boot_mode; ++ ++ regval = readl(SYS_CTRL_REG_BASE + REG_SYSSTAT); ++ boot_mode = get_spi_nor_addr_mode(regval); ++ return boot_mode; ++} +diff --git a/drivers/mtd/fmc_spi_ids.h b/drivers/mtd/fmc_spi_ids.h +new file mode 100644 +index 0000000..674c9db +--- /dev/null ++++ b/drivers/mtd/fmc_spi_ids.h +@@ -0,0 +1,437 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __FMC_SPI_IDS_H__ ++#define __FMC_SPI_IDS_H__ ++ ++#include "fmc_common.h" ++ ++#define INFINITE 0xFFFFFFFF ++ ++#define SPI_IF_READ_STD 0x01 ++#define SPI_IF_READ_FAST 0x02 ++#define SPI_IF_READ_DUAL 0x04 ++#define SPI_IF_READ_DUAL_ADDR 0x08 ++#define SPI_IF_READ_QUAD 0x10 ++#define SPI_IF_READ_QUAD_ADDR 0x20 ++#define SPI_IF_READ_QUAD_DTR 0x40 ++ ++#define SPI_IF_WRITE_STD 0x01 ++#define SPI_IF_WRITE_DUAL 0x02 ++#define SPI_IF_WRITE_DUAL_ADDR 0x04 ++#define SPI_IF_WRITE_QUAD 0x08 ++#define SPI_IF_WRITE_QUAD_ADDR 0x10 ++ ++#define SPI_IF_ERASE_SECTOR_4K 0x01 ++#define SPI_IF_ERASE_SECTOR_32K 0x02 ++#define SPI_IF_ERASE_SECTOR_64K 0x04 ++#define SPI_IF_ERASE_SECTOR_128K 0x08 ++#define SPI_IF_ERASE_SECTOR_256K 0x10 ++ ++#define FMC_SPI_NOR_SUPPORT_READ (SPI_IF_READ_STD | \ ++ SPI_IF_READ_FAST | \ ++ SPI_IF_READ_DUAL | \ ++ SPI_IF_READ_DUAL_ADDR | \ ++ SPI_IF_READ_QUAD | \ ++ SPI_IF_READ_QUAD_ADDR | \ ++ SPI_IF_READ_QUAD_DTR) ++ ++#define FMC_SPI_NOR_SUPPORT_WRITE (SPI_IF_WRITE_STD | \ ++ SPI_IF_WRITE_DUAL | \ ++ SPI_IF_WRITE_DUAL_ADDR | \ ++ SPI_IF_WRITE_QUAD | \ ++ SPI_IF_WRITE_QUAD_ADDR) ++ ++#define FMC_SPI_NOR_STR_MAX_DUMMY 7 ++#define FMC_SPI_NOR_DTR_MAX_DUMMY 12 ++ ++#define FMC_SPI_NAND_SUPPORT_READ (SPI_IF_READ_STD | \ ++ SPI_IF_READ_FAST | \ ++ SPI_IF_READ_DUAL | \ ++ SPI_IF_READ_DUAL_ADDR | \ ++ SPI_IF_READ_QUAD | \ ++ SPI_IF_READ_QUAD_ADDR) ++ ++#define FMC_SPI_NAND_SUPPORT_WRITE (SPI_IF_WRITE_STD | SPI_IF_WRITE_QUAD) ++ ++#define FMC_SPI_NAND_SUPPORT_MAX_DUMMY 8 ++ ++#define SPI_CMD_READ_STD 0x03 /* Standard read cache */ ++#define SPI_CMD_READ_STD4B 0x13 /* Standard read cache 4byte mode */ ++#define SPI_CMD_READ_FAST 0x0B /* Higher speed read cache */ ++#define SPI_CMD_READ_FAST4B 0x0C /* Higher speed read cache 4byte mode */ ++#define SPI_CMD_READ_DUAL 0x3B /* 2 IO read cache only date */ ++#define SPI_CMD_READ_DUAL4B 0x3C /* 2 IO read cache only date 4byte mode */ ++#define SPI_CMD_READ_DUAL_ADDR 0xBB /* 2 IO read cache date&addr */ ++#define SPI_CMD_READ_DUAL_ADDR4B 0xBC /* 2 IO read cache date&addr 4byte mode */ ++#define SPI_CMD_READ_QUAD 0x6B /* 4 IO read cache only date */ ++#define SPI_CMD_READ_QUAD4B 0x6C /* 4 IO read cache only date 4byte mode */ ++#define SPI_CMD_READ_QUAD_ADDR 0xEB /* 4 IO read cache date&addr */ ++#define SPI_CMD_READ_QUAD_ADDR4B 0xEC /* 4 IO read cache date&addr 4byte mode */ ++#define SPI_CMD_READ_QUAD_DTR 0xED /* 4DTR MODE */ ++#define SPI_CMD_READ_QUAD_DTR4B 0xEE /* 4DTR MODE 4byte mode */ ++ ++#define SPI_CMD_WRITE_STD 0x02 /* Standard page program */ ++#define SPI_CMD_WRITE_STD4B 0x12 /* Standard page program 4byte mode */ ++#define SPI_CMD_WRITE_DUAL 0xA2 /* 2 IO program only date */ ++#define SPI_CMD_WRITE_DUAL4B 0xA2 /* 2 IO program only date 4byte mode */ ++#define SPI_CMD_WRITE_DUAL_ADDR 0xD2 /* 2 IO program date&addr */ ++#define SPI_CMD_WRITE_DUAL_ADDR4B 0xD2 /* 2 IO program date&addr 4byte mode */ ++#define SPI_CMD_WRITE_QUAD 0x32 /* 4 IO program only date */ ++#define SPI_CMD_WRITE_QUAD4B 0x34 /* 4 IO program only date 4byte mode */ ++#define SPI_CMD_WRITE_QUAD_ADDR 0x38 /* 4 IO program date&addr */ ++#define SPI_CMD_WRITE_QUAD_ADDR4B 0x3E /* 4 IO program date&addr 4byte mode */ ++ ++#define SPI_CMD_SE_4K 0x20 /* 4KB sector Erase */ ++#define SPI_CMD_SE_4K4B 0x21 /* 4KB sector Erase 4byte mode */ ++#define SPI_CMD_SE_32K 0x52 /* 32KB sector Erase */ ++#define SPI_CMD_SE_32K4B 0x5C /* 32KB sector Erase 4byte mode */ ++#define SPI_CMD_SE_64K 0xD8 /* 64KB sector Erase */ ++#define SPI_CMD_SE_64K4B 0xDC /* 64KB sector Erase 4byte mode */ ++#define SPI_CMD_SE_128K 0xD8 /* 128KB sector Erase */ ++#define SPI_CMD_SE_128K4B 0xD8 /* 128KB sector Erase 4byte mode */ ++#define SPI_CMD_SE_256K 0xD8 /* 256KB sector Erase */ ++#define SPI_CMD_SE_256K4B 0xD8 /* 256KB sector Erase 4byte mode */ ++ ++#define set_read_std(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_std_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_STD, SPI_CMD_READ_STD, _dummy_, _size_, _clk_ } ++ ++#define set_read_std4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_std4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_STD, SPI_CMD_READ_STD4B, _dummy_, _size_, _clk_ } ++ ++#define set_read_fast(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_fast_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_FAST, SPI_CMD_READ_FAST, _dummy_, _size_, _clk_ } ++ ++#define set_read_fast4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_fast4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_FAST, SPI_CMD_READ_FAST4B, _dummy_, _size_, _clk_ } ++ ++#define set_read_dual(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_dual_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL, _dummy_, _size_, _clk_ } ++ ++#define set_read_dual4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_dual4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_DUAL, SPI_CMD_READ_DUAL4B, _dummy_, _size_, _clk_ } ++ ++#define set_read_dual_addr(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_dual_addr_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR, _dummy_, _size_, _clk_ } ++ ++#define set_read_dual_addr4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_dual_addr4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_DUAL_ADDR, SPI_CMD_READ_DUAL_ADDR4B, _dummy_, _size_, _clk_ } ++ ++#define set_read_quad(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD, _dummy_, _size_, _clk_ } ++ ++#define set_read_quad4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD, SPI_CMD_READ_QUAD4B, _dummy_, _size_, _clk_ } ++ ++#define set_read_quad_addr(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad_addr_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR, _dummy_, _size_, _clk_ } ++ ++#define set_read_quad_addr4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad_addr4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD_ADDR, SPI_CMD_READ_QUAD_ADDR4B, _dummy_, _size_, _clk_ } ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++#define set_read_quad_dtr(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad_dtr_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR, _dummy_, _size_, _clk_ } ++ ++#define set_read_quad_dtr4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op read_quad_dtr4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_READ_QUAD_DTR, SPI_CMD_READ_QUAD_DTR4B, _dummy_, _size_, _clk_ } ++#endif ++ ++#define set_write_std(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_std_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD, _dummy_, _size_, _clk_ } ++ ++#define set_write_std4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_std4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_STD, SPI_CMD_WRITE_STD4B, _dummy_, _size_, _clk_ } ++ ++#define set_write_dual(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_dual_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL, _dummy_, _size_, _clk_ } ++ ++#define set_write_dual4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_dual4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_DUAL, SPI_CMD_WRITE_DUAL4B, _dummy_, _size_, _clk_ } ++ ++#define set_write_dual_addr(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_dual_addr_##_dummy_##_size_##_clk_ = { \ ++SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR, _dummy_, _size_, _clk_ } ++ ++#define set_write_dual_addr4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_dual_addr4b_##_dummy_##_size_##_clk_ = { \ ++SPI_IF_WRITE_DUAL_ADDR, SPI_CMD_WRITE_DUAL_ADDR4B, _dummy_, _size_, _clk_ } ++ ++#define set_write_quad(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_quad_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD, _dummy_, _size_, _clk_ } ++ ++#define set_write_quad4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_quad4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_WRITE_QUAD, SPI_CMD_WRITE_QUAD4B, _dummy_, _size_, _clk_ } ++ ++#define set_write_quad_addr(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_quad_addr_##_dummy_##_size_##_clk_ = { \ ++SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR, _dummy_, _size_, _clk_ } ++ ++#define set_write_quad_addr4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op write_quad_addr4b_##_dummy_##_size_##_clk_ = { \ ++SPI_IF_WRITE_QUAD_ADDR, SPI_CMD_WRITE_QUAD_ADDR4B, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_4k(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_4k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_4k4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_4k4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_4K, SPI_CMD_SE_4K4B, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_32k(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_32k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_32k4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_32k4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_32K, SPI_CMD_SE_32K4B, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_64k(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_64k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_64k4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_64k4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_64K, SPI_CMD_SE_64K4B, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_128k(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_128k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_128k4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_128k4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_128K, SPI_CMD_SE_128K4B, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_256k(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_256k_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K, _dummy_, _size_, _clk_ } ++ ++#define set_erase_sector_256k4b(_dummy_, _size_, _clk_) \ ++ static struct spi_op erase_sector_256k4b_##_dummy_##_size_##_clk_ = { \ ++ SPI_IF_ERASE_SECTOR_256K, SPI_CMD_SE_256K4B, _dummy_, _size_, _clk_ } ++ ++#define read_std(_dummy_, _size_, _clk_) read_std_##_dummy_##_size_##_clk_ ++#define read_std4b(_dummy_, _size_, _clk_) read_std4b_##_dummy_##_size_##_clk_ ++#define read_fast(_dummy_, _size_, _clk_) read_fast_##_dummy_##_size_##_clk_ ++#define read_fast4b(_dummy_, _size_, _clk_) \ ++ read_fast4b_##_dummy_##_size_##_clk_ ++#define read_dual(_dummy_, _size_, _clk_) read_dual_##_dummy_##_size_##_clk_ ++#define read_dual4b(_dummy_, _size_, _clk_) \ ++ read_dual4b_##_dummy_##_size_##_clk_ ++#define read_dual_addr(_dummy_, _size_, _clk_) \ ++ read_dual_addr_##_dummy_##_size_##_clk_ ++#define read_dual_addr4b(_dummy_, _size_, _clk_) \ ++ read_dual_addr4b_##_dummy_##_size_##_clk_ ++#define read_quad(_dummy_, _size_, _clk_) read_quad_##_dummy_##_size_##_clk_ ++#define read_quad4b(_dummy_, _size_, _clk_) \ ++ read_quad4b_##_dummy_##_size_##_clk_ ++#define read_quad_addr(_dummy_, _size_, _clk_) \ ++ read_quad_addr_##_dummy_##_size_##_clk_ ++#define read_quad_addr4b(_dummy_, _size_, _clk_) \ ++ read_quad_addr4b_##_dummy_##_size_##_clk_ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++#define read_quad_dtr(_dummy_, _size_, _clk_) \ ++ read_quad_dtr_##_dummy_##_size_##_clk_ ++#define read_quad_dtr4b(_dummy_, _size_, _clk_) \ ++ read_quad_dtr4b_##_dummy_##_size_##_clk_ ++#endif ++ ++#define write_std(_dummy_, _size_, _clk_) write_std_##_dummy_##_size_##_clk_ ++#define write_std4b(_dummy_, _size_, _clk_) \ ++ write_std4b_##_dummy_##_size_##_clk_ ++#define write_dual(_dummy_, _size_, _clk_) write_dual_##_dummy_##_size_##_clk_ ++#define write_dual4b(_dummy_, _size_, _clk_) \ ++ write_dual4b_##_dummy_##_size_##_clk_ ++#define write_dual_addr(_dummy_, _size_, _clk_) \ ++ write_dual_addr_##_dummy_##_size_##_clk_ ++#define write_dual_addr4b(_dummy_, _size_, _clk_) \ ++ write_dual_addr4b_##_dummy_##_size_##_clk_ ++#define write_quad(_dummy_, _size_, _clk_) write_quad_##_dummy_##_size_##_clk_ ++#define write_quad4b(_dummy_, _size_, _clk_) \ ++ write_quad4b_##_dummy_##_size_##_clk_ ++#define write_quad_addr(_dummy_, _size_, _clk_) \ ++ write_quad_addr_##_dummy_##_size_##_clk_ ++#define write_quad_addr4b(_dummy_, _size_, _clk_) \ ++ write_quad_addr4b_##_dummy_##_size_##_clk_ ++ ++#define erase_sector_4k(_dummy_, _size_, _clk_) \ ++ erase_sector_4k_##_dummy_##_size_##_clk_ ++#define erase_sector_4k4b(_dummy_, _size_, _clk_) \ ++ erase_sector_4k4b_##_dummy_##_size_##_clk_ ++#define erase_sector_32k(_dummy_, _size_, _clk_) \ ++ erase_sector_32k_##_dummy_##_size_##_clk_ ++#define erase_sector_32k4b(_dummy_, _size_, _clk_) \ ++ erase_sector_32k4b_##_dummy_##_size_##_clk_ ++#define erase_sector_64k(_dummy_, _size_, _clk_) \ ++ erase_sector_64k_##_dummy_##_size_##_clk_ ++#define erase_sector_64k4b(_dummy_, _size_, _clk_) \ ++ erase_sector_64k4b_##_dummy_##_size_##_clk_ ++#define erase_sector_128k(_dummy_, _size_, _clk_) \ ++ erase_sector_128k_##_dummy_##_size_##_clk_ ++#define erase_sector_128k4b(_dummy_, _size_, _clk_) \ ++ erase_sector_128k4b_##_dummy_##_size_##_clk_ ++#define erase_sector_256k(_dummy_, _size_, _clk_) \ ++ erase_sector_256k_##_dummy_##_size_##_clk_ ++#define erase_sector_256k4b(_dummy_, _size_, _clk_) \ ++ erase_sector_256k4b_##_dummy_##_size_##_clk_ ++ ++#define SPI_CMD_WREN 0x06 /* Write Enable */ ++#define SPI_CMD_WRDI 0x04 /* Write Disable */ ++ ++#define SPI_CMD_WRSR 0x01 /* Write Status Register */ ++#define SPI_CMD_WRSR2 0x31 /* Write Status Register-2 */ ++#define SPI_CMD_WRSR3 0x11 /* Write Status Register-3 */ ++ ++#define SPI_CMD_RDSR 0x05 /* Read Status Register */ ++#define SPI_CMD_RDSR2 0x35 /* Read Status Register-2 */ ++#define SPI_CMD_RDSR3 0x15 /* Read Status Register-3 */ ++ ++#define SPI_CMD_RDCR 0x35 /* Read Config Register */ ++ ++#define SPI_CMD_RDID 0x9F /* Read Identification */ ++ ++#define SPI_CMD_RD_SFDP 0x5A /* Read SFDP */ ++ ++#define SPI_CMD_GET_FEATURES 0x0F /* Get Features */ ++#define SPI_CMD_SET_FEATURE 0x1F /* Set Feature */ ++ ++#define SPI_CMD_PAGE_READ 0x13 /* Page Read to Cache */ ++ ++#define SPI_CMD_RESET 0xff /* Reset the device */ ++ ++#define SPI_CMD_EN4B 0xB7 /* enter 4 bytes mode and set 4 byte bit as '1' */ ++#define SPI_CMD_EX4B 0xE9 /* exit 4 bytes mode and clear 4 byte bit */ ++ ++#define MAX_SPI_OP 8 ++ ++/* SPI general operation parameter */ ++struct spi_op { ++ unsigned char iftype; ++ unsigned char cmd; ++ unsigned char dummy; ++ unsigned int size; ++ unsigned int clock; ++}; ++ ++struct spi_drv; ++ ++/* SPI interface all operation */ ++struct fmc_spi { ++ char *name; ++ unsigned int chipselect; ++ unsigned long long chipsize; ++ unsigned int erasesize; ++#define SPI_NOR_3BYTE_ADDR_LEN 3 /* address len 3Bytes */ ++#define SPI_NOR_4BYTE_ADDR_LEN 4 /* address len 4Bytes for 32MB */ ++ unsigned int addrcycle; ++ ++ struct spi_op read[1]; ++ struct spi_op write[1]; ++ struct spi_op erase[MAX_SPI_OP]; ++ ++ void *host; ++ ++ struct spi_drv *driver; ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ unsigned int dtr_mode_support; ++ /* @dtr_cookie: Some device must set some registers when wants to ++ * work on DTR mode, so this cookie tells us to set s.th */ ++ unsigned int dtr_cookie; ++#define DTR_MODE_SET_NONE 0x0 /* Need not set anything */ ++#define DTR_MODE_SET_ODS 0x1 /* Need to set output driver strength */ ++#endif ++}; ++ ++/* SPI interface special operation function hook */ ++struct spi_drv { ++ int (*wait_ready)(struct fmc_spi *spi); ++ int (*write_enable)(struct fmc_spi *spi); ++ int (*qe_enable)(struct fmc_spi *spi); ++ int (*bus_prepare)(struct fmc_spi *spi, int op); ++ int (*entry_4addr)(struct fmc_spi *spi, int en); ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ int (*dtr_set_device)(struct fmc_spi *spi, int dtr_en); ++#endif ++}; ++#ifndef MAX_SPI_NAND_ID_LEN ++#define MAX_SPI_NAND_ID_LEN 1 ++#endif ++struct spi_nand_info { ++ char *name; ++ unsigned char id[MAX_SPI_NAND_ID_LEN]; ++ unsigned char id_len; ++ unsigned long long chipsize; ++ unsigned int erasesize; ++ unsigned int pagesize; ++ unsigned int oobsize; ++#define BBP_LAST_PAGE 0x01 ++#define BBP_FIRST_PAGE 0x02 ++ unsigned int badblock_pos; ++ struct spi_op *read[MAX_SPI_OP]; ++ struct spi_op *write[MAX_SPI_OP]; ++ struct spi_op *erase[MAX_SPI_OP]; ++ struct spi_drv *driver; ++}; ++ ++#ifndef MAX_SPI_NOR_ID_LEN ++#define MAX_SPI_NOR_ID_LEN 8 ++#endif ++ ++struct spi_nor_info { ++ char *name; ++ unsigned char id[MAX_SPI_NOR_ID_LEN]; ++ unsigned int id_len; ++ unsigned long chipsize; ++ unsigned int erasesize; ++ unsigned int addrcycle; ++ struct spi_op *read[MAX_SPI_OP]; ++ struct spi_op *write[MAX_SPI_OP]; ++ struct spi_op *erase[MAX_SPI_OP]; ++ struct spi_drv *driver; ++}; ++ ++void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en); ++ ++void fmc_get_fmc_best_2x_clock(unsigned int *clock); ++#ifdef CONFIG_DTR_MODE_SUPPORT ++void fmc_get_fmc_best_4x_clock(unsigned int *clock); ++#endif ++ ++#endif /* End of __FMC_SPI_IDS_H__ */ +diff --git a/drivers/mtd/fmc_ss928v100.c b/drivers/mtd/fmc_ss928v100.c +new file mode 100644 +index 0000000..d583077 +--- /dev/null ++++ b/drivers/mtd/fmc_ss928v100.c +@@ -0,0 +1,209 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fmc_spi_ids.h" ++ ++#define REG_IO_BASE 0x10230000 ++ ++#define REG_NF_DQ6 (REG_IO_BASE + 0x0) ++#define REG_NF_DQ7 (REG_IO_BASE + 0x4) ++#define REG_NF_WEN (REG_IO_BASE + 0x8) ++#define REG_NF_ALE (REG_IO_BASE + 0xc) ++#define REG_NF_CLE (REG_IO_BASE + 0x10) ++#define REG_NF_DQ3 (REG_IO_BASE + 0x14) ++#define REG_NF_DQ0 (REG_IO_BASE + 0x18) ++#define REG_SPI_WP_IO2 (REG_IO_BASE + 0x18) ++#define REG_NF_DQ2 (REG_IO_BASE + 0x1c) ++#define REG_SPI_HOLD_IO3 (REG_IO_BASE + 0x1c) ++#define REG_NF_DQ1 (REG_IO_BASE + 0x20) ++#define REG_SPI_MISO_IO1 (REG_IO_BASE + 0x20) ++#define REG_NF_CSN (REG_IO_BASE + 0x24) ++#define REG_SPI_CLK (REG_IO_BASE + 0x24) ++#define REG_NF_DQ4 (REG_IO_BASE + 0x28) ++#define REG_SPI_MOSI_IO0 (REG_IO_BASE + 0x28) ++#define REG_NF_DQ5 (REG_IO_BASE + 0x2c) ++#define REG_NF_REN (REG_IO_BASE + 0x30) ++#define REG_SPI_CS0 (REG_IO_BASE + 0x30) ++#define REG_NF_RDY (REG_IO_BASE + 0x34) ++#define REG_SPI_CS1 (REG_IO_BASE + 0x34) ++ ++static void ss928v100_nand_io_config(void) ++{ ++ writel(0x1021, REG_NF_CSN); ++ writel(0x1041, REG_NF_REN); ++ writel(0x1021, REG_NF_WEN); ++ writel(0x1021, REG_NF_ALE); ++ writel(0x1021, REG_NF_CLE); ++ writel(0x1101, REG_NF_RDY); ++ writel(0x1021, REG_NF_DQ6); ++ writel(0x1021, REG_NF_DQ7); ++ writel(0x1021, REG_NF_DQ3); ++ writel(0x1021, REG_NF_DQ0); ++ writel(0x1021, REG_NF_DQ2); ++ writel(0x1021, REG_NF_DQ1); ++ writel(0x1021, REG_NF_DQ4); ++ writel(0x1021, REG_NF_DQ5); ++} ++ ++void fmc100_nand_controller_enable(int enable) ++{ ++ unsigned int old_val; ++ unsigned int regval; ++ ++ old_val = regval = readl(CRG_REG_BASE + REG_FMC_CRG); ++ ++ regval &= ~FMC_CLK_SEL_MASK; ++ regval |= fmc_clk_sel(FMC_CLK_200M); ++ ++ if (enable) ++ regval |= FMC_CLK_ENABLE; ++ else ++ regval &= ~FMC_CLK_ENABLE; ++ ++ regval &= ~FMC_SOFT_RST_REQ; ++ if (regval != old_val) ++ writel(regval, (CRG_REG_BASE + REG_FMC_CRG)); ++ ++ ss928v100_nand_io_config(); ++} ++ ++static void ss928v100_spi_io_config(void) ++{ ++ static unsigned int io_config_flag = 1; ++ ++ if (!io_config_flag) ++ return; ++ ++ writel(0x1370, REG_SPI_CLK); ++ writel(0x1150, REG_SPI_CS0); ++ writel(0x1150, REG_SPI_CS1); ++ writel(0x1150, REG_SPI_MOSI_IO0); ++ writel(0x1050, REG_SPI_MISO_IO1); ++ writel(0x1050, REG_SPI_WP_IO2); ++ writel(0x1050, REG_SPI_HOLD_IO3); ++ io_config_flag = 0; ++} ++ ++void fmc_set_fmc_system_clock(struct spi_op *op, int clk_en) ++{ ++ unsigned int old_val; ++ unsigned int regval; ++ ++ old_val = regval = readl(CRG_REG_BASE + REG_FMC_CRG); ++ ++ regval &= ~FMC_CLK_SEL_MASK; ++ ++ if (op && op->clock) { ++ regval |= op->clock & FMC_CLK_SEL_MASK; ++ fmc_pr(DTR_DB, "\t|||*-get the setting clock value: %#x\n", ++ op->clock); ++ } else { ++ regval |= fmc_clk_sel(FMC_CLK_24M); /* Default Clock */ ++ ss928v100_spi_io_config(); ++ } ++ ++ if (clk_en) ++ regval |= FMC_CLK_ENABLE; ++ else ++ regval &= ~FMC_CLK_ENABLE; ++ ++ if (regval != old_val) { ++ fmc_pr(DTR_DB, "\t|||*-setting system clock [%#x]%#x\n", ++ REG_FMC_CRG, regval); ++ writel(regval, (CRG_REG_BASE + REG_FMC_CRG)); ++ } ++} ++ ++void fmc_get_fmc_best_2x_clock(unsigned int *clock) ++{ ++ int ix; ++ unsigned int clk_reg; ++ unsigned int clk_type; ++ const char *str[] = {"12", "50", "75", "100"}; ++ ++ unsigned int sys_2x_clk[] = { ++ clk_2x(24), fmc_clk_sel(FMC_CLK_24M), ++ clk_2x(100), fmc_clk_sel(FMC_CLK_100M), ++ clk_2x(150), fmc_clk_sel(FMC_CLK_150M), ++ clk_2x(200), fmc_clk_sel(FMC_CLK_200M), ++ 0, 0, ++ }; ++ ++ clk_type = FMC_CLK_24M; ++ clk_reg = fmc_clk_sel(clk_type); ++ fmc_pr(QE_DBG, "\t|||*-matching flash clock %d\n", *clock); ++ for (ix = 0; sys_2x_clk[ix]; ix += _2B) { ++ if (*clock < sys_2x_clk[ix]) ++ break; ++ clk_reg = sys_2x_clk[ix + 1]; ++ clk_type = get_fmc_clk_type(clk_reg); ++ fmc_pr(QE_DBG, "\t||||-select system clock: %sMHz\n", ++ str[clk_type]); ++ } ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ fmc_pr(DTR_DB, "best system clock for SDR.\n"); ++#endif ++ fmc_pr(QE_DBG, "\t|||*-matched best system clock: %sMHz\n", ++ str[clk_type]); ++ *clock = clk_reg; ++} ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ ++void fmc_get_fmc_best_4x_clock(unsigned int *clock) ++{ ++ int ix; ++ unsigned int clk_reg; ++ unsigned int clk_type; ++ char* const str[] = {"6", "25", "37.5", "50", ++ "62.5", "75", "100"}; ++ ++ unsigned int sys_4x_clk[] = { ++ clk_4x(24), fmc_clk_sel(FMC_CLK_24M), ++ clk_4x(100), fmc_clk_sel(FMC_CLK_100M), ++ clk_4x(150), fmc_clk_sel(FMC_CLK_150M), ++ clk_4x(200), fmc_clk_sel(FMC_CLK_200M), ++ clk_4x(250), fmc_clk_sel(FMC_CLK_250M), ++ clk_4x(300), fmc_clk_sel(FMC_CLK_300M), ++ clk_4x(400), fmc_clk_sel(FMC_CLK_400M), ++ 0, 0, ++ }; ++ ++ clk_type = FMC_CLK_24M; ++ clk_reg = fmc_clk_sel(clk_type); ++ fmc_pr(DTR_DB, "\t|||*-matching flash clock %d\n", *clock); ++ for (ix = 0; sys_4x_clk[ix]; ix += _2B) { ++ if (*clock < sys_4x_clk[ix]) ++ break; ++ clk_reg = sys_4x_clk[ix + 1]; ++ clk_type = get_fmc_clk_type(clk_reg); ++ fmc_pr(DTR_DB, "\t||||-select system clock: %sMHz\n", ++ str[clk_type]); ++ } ++ fmc_pr(DTR_DB, "best system clock for DTR.\n"); ++ fmc_pr(DTR_DB, "\t|||*-matched best system clock: %sMHz\n", ++ str[clk_type]); ++ *clock = clk_reg; ++} ++#endif/* CONFIG_DTR_MODE_SUPPORT */ +diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig +index 16165f8..0b41c34 100644 +--- a/drivers/mtd/nand/raw/Kconfig ++++ b/drivers/mtd/nand/raw/Kconfig +@@ -262,6 +262,70 @@ config NAND_MXS + This enables NAND driver for the NAND flash controller on the + MXS processors. + ++config FMC_SPI_NAND ++ bool "SPI Nand Flash Interface support" ++ depends on FMC ++ help ++ Enable the SPI Nand flash support. ++ ++ If unsure, say N ++ ++config FMC_NAND ++ bool "Nand support" ++ depends on FMC ++ help ++ This enables the NAND flash support. ++ ++ If unsure, say N ++ ++config SPI_NAND_MAX_CHIP_NUM ++ int "Support max number of SPI Nand flash chip (1, 2)" ++ depends on FMC_SPI_NAND ++ default 1 ++ help ++ flash memory controller v100 device only support 1 or 2 SPI nand flash ++ chip, your should not config other value. ++ ++config NAND_MAX_CHIP_NUM ++ int "Support max number of Nand flash chip (1, 2)" ++ depends on FMC_NAND ++ default 1 ++ help ++ flash memory controller v100 device only support 1 or 2 nand flash ++ chip, your should not config other value. ++ ++if FMC_SPI_NAND || FMC_NAND ++ ++choice ++ prompt "Page Size and Ecc Type Select" ++ default FMC100_AUTO_PAGESIZE_ECC ++ ++config FMC100_HARDWARE_PAGESIZE_ECC ++ bool "Hardware" ++ help ++ the configure of page size and ecc type lie on switch on the board. ++ so the page size and ecc type is controlled by Hardware see demo ++ board of SOC. ++ ++config FMC100_AUTO_PAGESIZE_ECC ++ bool "Auto" ++ help ++ auto-sensed the page size and ecc type value. driver will try each of ++ page size and ecc type one by one till flash can be read and wrote ++ accurately. so the page size and ecc type is match adaptively without ++ switch on the board ++ ++config FMC100_PAGESIZE_AUTO_ECC_NONE ++ bool "Pagesize Auto, Ecc None" ++ help ++ auto-sensed the page size and select ecc none. driver will try each ++ of page size one by one till flash can be read and wrote accurately. ++ so the page size is match adaptively without switch on the board ++ ++endchoice ++ ++endif ++ + if NAND_MXS + + config NAND_MXS_DT +diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile +index 9337f64..9934fa4 100644 +--- a/drivers/mtd/nand/raw/Makefile ++++ b/drivers/mtd/nand/raw/Makefile +@@ -32,6 +32,9 @@ obj-y += nand_util.o + obj-y += nand_ecc.o + obj-y += nand_base.o + obj-y += nand_timings.o ++obj-$(CONFIG_CMD_NAND) += nfc_common.o ++obj-$(CONFIG_FMC_SPI_NAND) += fmc100/ ++obj-$(CONFIG_FMC_NAND) += fmc100_nand/ + + endif # not spl + +diff --git a/drivers/mtd/nand/raw/fmc100/Makefile b/drivers/mtd/nand/raw/fmc100/Makefile +new file mode 100644 +index 0000000..143e9fe +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100/Makefile +@@ -0,0 +1,21 @@ ++# ++# The Flash Memory Controller v100 Device Driver for vendor ++# ++# Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, see ++# . ++# ++ ++obj-y += fmc100.o fmc100_os.o fmc_spi_nand_ids.o +diff --git a/drivers/mtd/nand/raw/fmc100/fmc100.c b/drivers/mtd/nand/raw/fmc100/fmc100.c +new file mode 100644 +index 0000000..4d4019a +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100/fmc100.c +@@ -0,0 +1,1004 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "fmc100.h" ++#include ++#include ++#include ++#include ++#include ++#include ++ ++void fmc100_ecc0_switch(struct fmc_host* const host, unsigned char op) ++{ ++ unsigned int config; ++ if (host == NULL || host->regbase == NULL) { ++ printf("para err\n"); ++ return; ++ } ++#if EC_DBG ++ unsigned int cmp_cfg; ++ ++ config = fmc_read(host, FMC_CFG); ++ fmc_pr(EC_DBG, "\t *-Get CFG[%#x]%#x\n", FMC_CFG, config); ++ ++ if (op) ++ cmp_cfg = host->fmc_cfg; ++ else ++ cmp_cfg = host->fmc_cfg_ecc0; ++ ++ if (cmp_cfg != config) ++ db_msg("Warning: FMC config[%#x] is different.\n", ++ cmp_cfg); ++#endif ++ if (op == ENABLE) { ++ config = host->fmc_cfg_ecc0; ++ } else if (op == DISABLE) { ++ config = host->fmc_cfg; ++ } else { ++ db_msg("Error: Invalid opcode: %d\n", op); ++ return; ++ } ++ fmc_write(host, FMC_CFG, config); ++ fmc_pr(EC_DBG, "\t *-Set CFG[%#x]%#x\n", FMC_CFG, config); ++} ++ ++static void set_dma_addr_reg(struct fmc_host *host) ++{ ++ unsigned int reg; ++ ++ if (!host) { ++ printf("%s:host data is NULL, please check input pointer parameter\n", __func__); ++ return; ++ } ++ ++ reg = host->dma_buffer; ++ fmc_write(host, FMC_DMA_SADDR_D0, reg); ++ fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_D0[%#x]%#x\n", FMC_DMA_SADDR_D0, reg); ++ /* get hight 32 bits */ ++ reg = ((unsigned long)host->dma_buffer & FMC_DMA_SADDRH_MASK) >> 32; ++ fmc_write(host, FMC_DMA_SADDRH_D0, reg); ++ fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_D0[%#x]%#x\n", FMC_DMA_SADDRH_D0, reg); ++ reg = host->dma_oob; ++ fmc_write(host, FMC_DMA_SADDR_OOB, reg); ++ fmc_pr(DMA_DB, "\t|-Set DMA_SADDR_OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB, ++ reg); ++ /* get hight 32 bits */ ++ reg = ((unsigned long)host->dma_oob & FMC_DMA_SADDRH_MASK) >> 32; ++ fmc_write(host, FMC_DMA_SADDRH_OOB, reg); ++ fmc_pr(DMA_DB, "\t|-Set DMA_SADDRH_OOB[%#x]%#x\n", FMC_DMA_SADDRH_OOB, ++ reg); ++} ++ ++static void set_addr_reg(struct fmc_host *host) ++{ ++ unsigned int reg; ++ struct nand_chip *chip = NULL; ++ unsigned char pages_per_block_shift; ++ unsigned int block_num; ++ unsigned int block_num_h; ++ unsigned int page_num; ++ ++ if (!host || !host->chip) { ++ printf("%s:data is NULL, please check input pointer parameter\n", __func__); ++ return; ++ } ++ ++ chip = host->chip; ++ pages_per_block_shift = chip->phys_erase_shift - chip->page_shift; ++ block_num = host->addr_value[1] >> pages_per_block_shift; ++ block_num_h = block_num >> REG_CNT_HIGH_BLOCK_NUM_SHIFT; ++ reg = fmc_addrh_set(block_num_h); ++ fmc_write(host, FMC_ADDRH, reg); ++ fmc_pr(REG_DB, "|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg); ++ ++ page_num = host->addr_value[1] - (block_num << pages_per_block_shift); ++ reg = ((block_num & REG_CNT_BLOCK_NUM_MASK) << REG_CNT_BLOCK_NUM_SHIFT) | ++ ((page_num & REG_CNT_PAGE_NUM_MASK) << REG_CNT_PAGE_NUM_SHIFT); ++ fmc_write(host, FMC_ADDRL, reg); ++ fmc_pr(REG_DB, "|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++} ++ ++static void set_cs_addr_reg(enum OP op, struct fmc_host *host) ++{ ++ unsigned int reg; ++ struct fmc_spi *spi = host->spi; ++ unsigned char iftype = 0; ++ unsigned char dummy = 0; ++ ++ reg = FMC_INT_CLR_ALL; ++ fmc_write(host, FMC_INT_CLR, reg); ++ fmc_pr(WR_DBG, "|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg); ++ ++ if (op == READ) { ++ iftype = spi->read->iftype; ++ dummy = spi->read->dummy; ++ } else if (op == WRITE) { ++ iftype = spi->write->iftype; ++ } else { ++ iftype = spi->erase->iftype; ++ } ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | ++ OP_CFG_OEN_EN | ++ op_cfg_mem_if_type(iftype) | ++ op_cfg_dummy_num(dummy); ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(REG_DB, "|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ set_addr_reg(host); ++} ++ ++static void pageprog_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi) ++{ ++ unsigned int reg; ++ ++ reg = op_ctrl_wr_opcode(spi->write->cmd) | op_ctrl_dma_op(OP_TYPE_DMA) | ++ op_ctrl_rw_op(RW_OP_WRITE) | OP_CTRL_DMA_OP_READY; ++ fmc_write(host, FMC_OP_CTRL, reg); ++ fmc_pr(WR_DBG, "|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg); ++ fmc_dma_wait_int_finish(host); ++} ++ ++static void fmc100_send_cmd_pageprog(struct fmc_host *host) ++{ ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip = NULL; ++ int ret; ++#ifndef CONFIG_SYS_DCACHE_OFF ++ unsigned int dma_align_len; ++#endif ++ fmc_pr(WR_DBG, "\n*-Enter Dma page program!\n"); ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(1); /* delay 1 us */ ++ return; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NAND); ++ (*fmc_ip)++; ++ } ++ ++ ret = spi->driver->write_enable(spi); ++ if (ret) { ++ db_msg("Error: Dma program write enable failed! ret: %#x\n", ret); ++ goto end; ++ } ++ host->set_system_clock(spi->write, ENABLE); ++ if (ecc0_flag == 1) { ++ fmc100_ecc0_switch(host, ENABLE); ++ fmc_write(host, FMC_DMA_LEN, host->oobsize); ++ } ++ set_cs_addr_reg(WRITE, host); ++ if (ecc0_flag != 1) ++ *host->epm = 0x0000; ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ dma_align_len = ((host->pagesize + host->oobsize + ++ CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1)); ++ flush_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len); ++#endif ++ set_dma_addr_reg(host); ++ ++ pageprog_fmc_op(host, spi); ++ ++ if (ecc0_flag == 1) ++ fmc100_ecc0_switch(host, DISABLE); ++ ret = spi->driver->wait_ready(spi); ++ if (ret) ++ db_msg("Error: Dma program wait ready failed! status: %#x\n", ret); ++end: ++ (*fmc_ip)--; ++ fmc_pr(WR_DBG, "*-End Dma page program!\n"); ++} ++ ++static void fmc100_send_cmd_readstart(struct fmc_host *host) ++{ ++ unsigned int reg; ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip = NULL; ++#ifndef CONFIG_SYS_DCACHE_OFF ++ unsigned int dma_align_len; ++#endif ++ fmc_pr(RD_DBG, "\n\t*-Start Dma page read\n"); ++ ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(1); /* delay 1 us */ ++ return; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NAND); ++ (*fmc_ip)++; ++ } ++ ++ host->set_system_clock(spi->read, ENABLE); ++ ++ if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB)) { ++ fmc100_ecc0_switch(host, ENABLE); ++ fmc_write(host, FMC_DMA_LEN, host->oobsize); ++ } ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READOOB) ++ host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_OOB); ++ else ++ host->cmd_op.op_cfg = op_ctrl_rd_op_sel(RD_OP_READ_ALL_PAGE); ++ ++ set_cs_addr_reg(READ, host); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ dma_align_len = ((host->pagesize + host->oobsize + ++ CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1)); ++ invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len); ++#endif ++ set_dma_addr_reg(host); ++ ++ reg = op_ctrl_rd_opcode(spi->read->cmd) | ++ host->cmd_op.op_cfg | op_ctrl_dma_op(OP_TYPE_DMA) | ++ op_ctrl_rw_op(RW_OP_READ) | OP_CTRL_DMA_OP_READY; ++ fmc_write(host, FMC_OP_CTRL, reg); ++ fmc_pr(RD_DBG, "\t|-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg); ++ fmc_dma_wait_int_finish(host); ++ ++ if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB)) ++ fmc100_ecc0_switch(host, DISABLE); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range(host->dma_buffer, host->dma_buffer + dma_align_len); ++#endif ++ ++ (*fmc_ip)--; ++ fmc_pr(RD_DBG, "\t*-End Dma page read\n"); ++} ++ ++static void erase_fmc_op(struct fmc_host* const host, struct fmc_spi* const spi) ++{ ++ unsigned int reg; ++ ++ reg = FMC_INT_CLR_ALL; ++ fmc_write(host, FMC_INT_CLR, reg); ++ fmc_pr(ER_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg); ++ ++ reg = spi->erase->cmd; ++ fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg)); ++ fmc_pr(ER_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = fmc_addrl_block_h_mask(host->addr_value[1]) | ++ fmc_addrl_block_l_mask(host->addr_value[0]); ++ fmc_write(host, FMC_ADDRL, reg); ++ fmc_pr(ER_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN | ++ op_cfg_mem_if_type(spi->erase->iftype) | ++ op_cfg_addr_num(STD_OP_ADDR_NUM) | ++ op_cfg_dummy_num(spi->erase->dummy); ++ ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(ER_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(ER_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++} ++ ++static void fmc100_send_cmd_erase(struct fmc_host *host) ++{ ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip = NULL; ++ int ret; ++ ++ if (ER_DBG) ++ printf("\n"); ++ fmc_pr(ER_DBG, "\t*-Start send cmd erase!\n"); ++ ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(1); /* delay 1 us */ ++ return; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NAND); ++ (*fmc_ip)++; ++ } ++ ++ ret = spi->driver->write_enable(spi); ++ if (ret) { ++ db_msg("Error: Erase write enable failed! ret: %#x\n", ret); ++ goto end; ++ } ++ ++ host->set_system_clock(spi->erase, ENABLE); ++ ++ erase_fmc_op(host, spi); ++ ++ ret = spi->driver->wait_ready(spi); ++ fmc_pr(ER_DBG, "\t|-Erase wait ready, ret: %#x\n", ret); ++ if (ret) ++ db_msg("Error: Erase wait ready fail! status: %#x\n", ret); ++ ++end: ++ (*fmc_ip)--; ++ ++ fmc_pr(ER_DBG, "\t*-End send cmd erase!\n"); ++} ++ ++static void fmc100_send_cmd_status(struct fmc_host* const host) ++{ ++ unsigned int status; ++ unsigned char addr = STATUS_ADDR; ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip = get_fmc_ip(); ++ ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(1); /* delay 1 us */ ++ return; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NAND); ++ (*fmc_ip)++; ++ } ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES) ++ addr = PROTECT_ADDR; ++ ++ if (spi_nand_feature_op(spi, GET_OP, addr, &status)) { ++ printf("get protect addr failed!\n"); ++ (*fmc_ip)--; ++ return; ++ } ++ ++ fmc_pr((ER_DBG || WR_DBG), "\t*-Get status[%#x]: %#x\n", addr, status); ++ ++ (*fmc_ip)--; ++} ++ ++static void fmc100_send_cmd_readid(struct fmc_host *host) ++{ ++ unsigned int reg; ++ ++ fmc_pr(BT_DBG, "\t|*-Start send cmd read ID\n"); ++ ++ fmc100_ecc0_switch(host, ENABLE); ++ ++ reg = fmc_cmd_cmd1(SPI_CMD_RDID); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = READ_ID_ADDR; ++ fmc_write(host, FMC_ADDRL, reg); ++ fmc_pr(BT_DBG, "\t||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN | ++ op_cfg_addr_num(READ_ID_ADDR_NUM); ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_data_num_cnt(MAX_SPI_NAND_ID_LEN); ++ fmc_write(host, FMC_DATA_NUM, reg); ++ fmc_pr(BT_DBG, "\t||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) | ++ fmc_op_read_data_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ host->addr_cycle = 0x0; ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ fmc100_ecc0_switch(host, DISABLE); ++ ++ fmc_pr(BT_DBG, "\t|*-End read flash ID\n"); ++} ++ ++static void fmc100_send_cmd_reset(struct fmc_host* const host) ++{ ++ unsigned int reg; ++ ++ fmc_pr(BT_DBG, "\t|*-Start send cmd reset\n"); ++ ++ reg = fmc_cmd_cmd1(SPI_CMD_RESET); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(BT_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(BT_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(BT_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ fmc_pr(BT_DBG, "\t|*-End send cmd reset\n"); ++} ++ ++static unsigned char fmc100_read_byte(struct mtd_info* const mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ unsigned char value; ++ unsigned char ret_val = 0; ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READID) { ++ value = readb(host->iobase + host->offset); ++ host->offset++; ++ if (host->cmd_op.data_no == host->offset) ++ host->cmd_op.l_cmd = 0; ++ return value; ++ } ++ ++ if (host->cmd_op.cmd == NAND_CMD_STATUS) { ++ value = fmc_read(host, FMC_STATUS); ++ if (host->cmd_op.l_cmd == NAND_CMD_GET_FEATURES) { ++ fmc_pr((ER_DBG || WR_DBG), "\t\tRead BP status: %#x\n", ++ value); ++ if (any_bp_enable(value)) ++ ret_val |= NAND_STATUS_WP; ++ ++ host->cmd_op.l_cmd = NAND_CMD_STATUS; ++ } ++ ++ if (!(value & STATUS_OIP_MASK)) ++ ret_val |= NAND_STATUS_READY; ++ ++ if ((chip->state == FL_ERASING) && ++ (value & STATUS_E_FAIL_MASK)) { ++ fmc_pr(ER_DBG, "\t\tGet erase status: %#x\n", value); ++ ret_val |= NAND_STATUS_FAIL; ++ } ++ ++ if ((chip->state == FL_WRITING) && ++ (value & STATUS_P_FAIL_MASK)) { ++ fmc_pr(WR_DBG, "\t\tGet write status: %#x\n", value); ++ ret_val |= NAND_STATUS_FAIL; ++ } ++ ++ return ret_val; ++ } ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READOOB) { ++ value = readb((unsigned char *) ++ ((unsigned char *)(uintptr_t)host->dma_oob + host->offset)); ++ host->offset++; ++ return value; ++ } ++ ++ host->offset++; ++ ++ return readb(host->buffer + host->column + host->offset - 1); ++} ++ ++static unsigned short fmc100_read_word(struct mtd_info *mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ return readw(host->buffer + host->column + host->offset); ++} ++ ++static void fmc100_write_buf(struct mtd_info* const mtd, const u_char* const buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ if (buf == chip->oob_poi) { ++ if (memcpy_s((unsigned char *)(uintptr_t)host->dma_oob, len, buf, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } else { ++ if (memcpy_s((unsigned char *)(uintptr_t)host->dma_buffer, len, buf, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } ++ return; ++} ++ ++static void fmc100_read_buf(struct mtd_info* const mtd, u_char* const buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ if (buf == chip->oob_poi) { ++ if (memcpy_s(buf, len, (unsigned char *)(uintptr_t)host->dma_oob, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } else { ++ if (memcpy_s(buf, len, (unsigned char *)(uintptr_t)host->dma_buffer, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } ++ return; ++} ++ ++static void fmc100_select_chip(struct mtd_info* const mtd, int chipselect) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ if (chipselect < 0) ++ return; ++ ++ if (chipselect > CONFIG_SPI_NAND_MAX_CHIP_NUM) ++ db_bug("Error: Invalid chipselect: %d\n", chipselect); ++ ++ if (host->mtd != mtd) ++ host->mtd = mtd; ++ ++ if (!(chip->options & NAND_BROKEN_XD)) ++ if ((chip->state == FL_ERASING) || (chip->state == FL_WRITING)) ++ host->cmd_op.l_cmd = NAND_CMD_GET_FEATURES; ++} ++ ++static void read_nand_id_op(struct fmc_host *host, unsigned int command) ++{ ++ host->offset = 0; ++ host->cmd_op.l_cmd = command & 0xff; ++ if (memset_s((u_char *)(host->iobase), MAX_SPI_NAND_ID_LEN, 0, MAX_SPI_NAND_ID_LEN)) ++ printf("%s %d ERR:memset_s fail\n", __func__, __LINE__); ++ host->cmd_op.data_no = MAX_SPI_NAND_ID_LEN; ++ host->send_cmd_readid(host); ++} ++ ++static void fmc100_cmdfunc(struct mtd_info* const mtd, ++ unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ switch (command) { ++ case NAND_CMD_RESET: ++ host->send_cmd_reset(host); ++ chip->dev_ready(mtd); ++ break; ++ case NAND_CMD_READID: ++ read_nand_id_op(host, command); ++ break; ++ case NAND_CMD_GET_FEATURES: ++ case NAND_CMD_STATUS: ++ host->cmd_op.l_cmd = command & 0xff; ++ host->cmd_op.cmd = NAND_CMD_STATUS; ++ host->send_cmd_status(host); ++ break; ++ case NAND_CMD_READOOB: ++ host->offset = 0; ++ host->cmd_op.l_cmd = command & 0xff; ++ /* use same command as normal read */ ++ host->cmd_op.cmd = command & 0xff; ++ case NAND_CMD_READ0: ++ if (command == NAND_CMD_READ0) ++ host->cmd_op.l_cmd = command & 0xff; ++ host->addr_value[1] = page_addr; ++ host->send_cmd_readstart(host); ++ break; ++ case NAND_CMD_SEQIN: ++ host->addr_value[1] = page_addr; ++ break; ++ case NAND_CMD_PAGEPROG: ++ host->offset = 0; ++ host->send_cmd_pageprog(host); ++ break; ++ case NAND_CMD_ERASE1: ++ host->cmd_op.l_cmd = command & 0xff; ++ host->addr_value[0] = page_addr; ++ /* page_addr to block_addr, move right 16 bits */ ++ host->addr_value[1] = (unsigned int)page_addr >> 16; ++ /* erase operation need a seral of command sequences */ ++ host->send_cmd_erase(host); ++ break; ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_READSTART: ++ break; ++ default: ++ printf("%s not support command 0x%08x:\n", mtd->name, command); ++ break; ++ } ++} ++ ++static int fmc100_dev_ready(struct mtd_info* const mtd) ++{ ++ unsigned int reg; ++ /* just a big number, so move 12 bits */ ++ unsigned long deadline = 1 << 12; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ do { ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ ++ reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ reg = fmc_read(host, FMC_STATUS); ++ if (!(reg & STATUS_OIP_MASK)) ++ return 1; ++ ++ udelay(1); /* delay 1 us */ ++ } while (deadline--); ++ ++#ifndef CONFIG_SYS_NAND_QUIET_TEST ++ printf("Warning: Wait SPI nand ready timeout, status: %#x\n", reg); ++#endif ++ ++ return 0; ++} ++ ++/* ++ * 'host->epm' only use the first oobfree[0] field, it looks very simple, But.. ++ */ ++static struct nand_ecclayout nand_ecc_default = { ++ .oobfree = {{2, 30} } ++}; ++ ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++static struct nand_ecclayout nand_ecc_2k16bit = { ++ .oobfree = {{2, 6} } ++}; ++ ++static struct nand_ecclayout nand_ecc_4k16bit = { ++ .oobfree = {{2, 14} } ++}; ++#endif ++ ++static struct nand_config_info fmc_spi_nand_config_table[] = { ++ {NAND_PAGE_4K, NAND_ECC_24BIT, 200, &nand_ecc_default}, ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++ {NAND_PAGE_4K, NAND_ECC_16BIT, 128, &nand_ecc_4k16bit}, ++#endif ++ {NAND_PAGE_4K, NAND_ECC_8BIT, 128, &nand_ecc_default}, ++ {NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_2K, NAND_ECC_24BIT, 128, &nand_ecc_default}, ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++ {NAND_PAGE_2K, NAND_ECC_16BIT, 64, &nand_ecc_2k16bit}, ++#endif ++ {NAND_PAGE_2K, NAND_ECC_8BIT, 64, &nand_ecc_default}, ++ {NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {0, 0, 0, NULL}, ++}; ++ ++/* ++ * Auto-sensed the page size and ecc type value. driver will try each of page ++ * size and ecc type one by one till flash can be read and wrote accurately. ++ * so the page size and ecc type is match adaptively without switch on the board ++ */ ++static struct nand_config_info *fmc100_get_config_type_info(struct mtd_info *mtd) ++{ ++ struct nand_config_info *best = NULL; ++ struct nand_config_info *info = fmc_spi_nand_config_table; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ for (; info->layout; info++) { ++ if (match_page_type_to_size(info->pagetype) != mtd->writesize) ++ continue; ++ ++ if (mtd->oobsize < info->oobsize) ++ continue; ++ ++ if (!best || (best->ecctype < info->ecctype)) ++ best = info; ++ } ++ /* All SPI NAND are small-page,SLC */ ++ chip->bits_per_cell = 1; ++ return best; ++} ++ ++static void fmc100_set_oob_info(struct mtd_info* const mtd, ++ struct nand_config_info* const info) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ if (info->ecctype != NAND_ECC_0BIT) ++ mtd->oobsize = info->oobsize; ++ ++ host->oobsize = mtd->oobsize; ++ host->dma_oob = host->dma_buffer + host->pagesize; ++ host->bbm = (u_char *)(host->buffer + host->pagesize + ++ FMC_BAD_BLOCK_POS); ++ ++ chip->ecc.layout = info->layout; ++ ++ /* EB bytes locate in the bottom two of CTRL(30) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize + ++ chip->ecc.layout->oobfree[0].offset + EB_NORMAL); ++ ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++ if (info->ecctype == NAND_ECC_16BIT) { ++ if (host->pagesize == _2K) ++ /* EB bits locate in the bottom two of CTRL(4) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize + ++ chip->ecc.layout->oobfree[0].offset + EB_2K_16_BIT); ++ else if (host->pagesize == _4K) ++ /* EB bit locate in the bottom two of CTRL(14) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize + ++ chip->ecc.layout->oobfree[0].offset + EB_4K_16_BIT); ++ } ++#endif ++} ++ ++static unsigned int fmc100_get_ecc_reg(struct fmc_host* const host, ++ struct nand_config_info* const info) ++{ ++ host->ecctype = info->ecctype; ++ ++ return fmc_cfg_ecc_type(match_ecc_type_to_reg(info->ecctype)); ++} ++ ++static unsigned int fmc100_get_page_reg(struct fmc_host* const host, ++ struct nand_config_info* const info) ++{ ++ host->pagesize = match_page_type_to_size(info->pagetype); ++ ++ return fmc_cfg_page_size(match_page_type_to_reg(info->pagetype)); ++} ++ ++static int fmc100_get_block_reg(struct fmc_host* const host, ++ struct nand_config_info* const info, unsigned int* const block_val) ++{ ++ unsigned int block_reg = 0; ++ unsigned int page_per_block; ++ struct mtd_info *mtd = NULL; ++ ++ if (info == NULL || host == NULL || host->mtd == NULL) { ++ printf("para err!\n"); ++ return -1; ++ } ++ mtd = host->mtd; ++ host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1); ++ page_per_block = mtd->erasesize / match_page_type_to_size(info->pagetype); ++ switch (page_per_block) { ++ case _64_PAGES: ++ block_reg = BLOCK_SIZE_64_PAGE; ++ break; ++ case _128_PAGES: ++ block_reg = BLOCK_SIZE_128_PAGE; ++ break; ++ case _256_PAGES: ++ block_reg = BLOCK_SIZE_256_PAGE; ++ break; ++ case _512_PAGES: ++ block_reg = BLOCK_SIZE_512_PAGE; ++ break; ++ default: ++ db_msg("Can't support block %#x and page %#x size\n", ++ mtd->erasesize, mtd->writesize); ++ } ++ *block_val = fmc_cfg_block_size(block_reg); ++ return 0; ++} ++ ++static void fmc100_set_fmc_cfg_reg(struct mtd_info* const mtd, ++ struct nand_config_info* const type_info) ++{ ++ struct nand_chip *chip = NULL; ++ struct fmc_host *host = NULL; ++ unsigned int page_reg; ++ unsigned int ecc_reg; ++ unsigned int block_reg; ++ unsigned int reg_fmc_cfg; ++ if (mtd == NULL || type_info == NULL) ++ return; ++ chip = mtd_to_nand(mtd); ++ if (chip == NULL || chip->priv == NULL) ++ return; ++ host = chip->priv; ++ ecc_reg = fmc100_get_ecc_reg(host, type_info); ++ page_reg = fmc100_get_page_reg(host, type_info); ++ if (fmc100_get_block_reg(host, type_info, &block_reg)) ++ return; ++ ++ reg_fmc_cfg = fmc_read(host, FMC_CFG); ++ reg_fmc_cfg &= ~(PAGE_SIZE_MASK | ECC_TYPE_MASK | BLOCK_SIZE_MASK); ++ reg_fmc_cfg |= ecc_reg | page_reg | block_reg; ++ fmc_write(host, FMC_CFG, reg_fmc_cfg); ++ ++ /* max number of correctible bit errors per ecc step */ ++ mtd->ecc_strength = host->ecctype; ++ ++ /* Save value of FMC_CFG and FMC_CFG_ECC0 to turn on/off ECC */ ++ host->fmc_cfg = reg_fmc_cfg; ++ host->fmc_cfg_ecc0 = (host->fmc_cfg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT; ++ fmc_pr(BT_DBG, "\t|-Save FMC_CFG[%#x]: %#x and FMC_CFG_ECC0: %#x\n", ++ FMC_CFG, host->fmc_cfg, host->fmc_cfg_ecc0); ++} ++ ++static int fmc100_set_config_info(struct mtd_info* const mtd) ++{ ++ struct nand_config_info *type_info = NULL; ++ ++ fmc_pr(BT_DBG, "\t*-Start match PageSize and EccType\n"); ++ ++ type_info = fmc100_get_config_type_info(mtd); ++ if (!type_info) ++ db_bug(ERR_STR_DRIVER "pagesize: %d and oobsize: %d.\n", ++ mtd->writesize, mtd->oobsize); ++ ++ /* Set the page_size, ecc_type, block_size of FMC_CFG[0x0] register */ ++ fmc100_set_fmc_cfg_reg(mtd, type_info); ++ ++ fmc_pr(BT_DBG, "\t|- PageSize %s EccType %s OOB Size %d\n", ++ nand_page_name(type_info->pagetype), ++ nand_ecc_name(type_info->ecctype), type_info->oobsize); ++ ++ fmc100_set_oob_info(mtd, type_info); ++ ++ fmc_pr(BT_DBG, "\t*-End match PageSize and EccType\n"); ++ ++ return 0; ++} ++ ++static void fmc100_chip_init(struct nand_chip* const chip) ++{ ++ if (!chip->IO_ADDR_R) ++ chip->IO_ADDR_R = (void __iomem *)CONFIG_FMC_BUFFER_BASE; ++ chip->IO_ADDR_W = chip->IO_ADDR_R; ++ if (memset_s((char *)chip->IO_ADDR_R, FMC100_BUFFER_LEN, 0xff, FMC100_BUFFER_LEN)) ++ printf("%s %d ERR:memset_s fail\n", __func__, __LINE__); ++ ++ chip->read_byte = fmc100_read_byte; ++ chip->read_word = fmc100_read_word; ++ chip->write_buf = fmc100_write_buf; ++ chip->read_buf = fmc100_read_buf; ++ ++ chip->select_chip = fmc100_select_chip; ++ ++ chip->cmdfunc = fmc100_cmdfunc; ++ chip->dev_ready = fmc100_dev_ready; ++ ++ chip->chip_delay = FMC_CHIP_DELAY; ++ ++ chip->options = NAND_BBT_SCANNED | NAND_BROKEN_XD; ++ ++ chip->ecc.layout = NULL; ++ chip->ecc.mode = NAND_ECC_NONE; ++} ++ ++int host_data_init(struct fmc_host *host) ++{ ++ unsigned long align_mask; ++ int ret; ++ ++ if (!host) ++ return -1; ++ host->addr_cycle = 0; ++ host->addr_value[0] = 0; ++ host->addr_value[1] = 0; ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ ++ fmc_pr(BT_DBG, "\t|||-Malloc memory for dma buffer\n"); ++ host->buforg = kmalloc((FMC100_BUFFER_LEN + FMC_DMA_ALIGN), ++ GFP_KERNEL); ++ if (!host->buforg) { ++ db_msg("Error: Can't malloc memory for SPI Nand driver.\n"); ++ return -ENOMEM; ++ } ++ ret = memset_s(host->buforg, FMC100_BUFFER_LEN + FMC_DMA_ALIGN, 0xff, ++ FMC100_BUFFER_LEN + FMC_DMA_ALIGN); ++ if (ret) { ++ printf("%s %d ERR:memset_s fail\n", __func__, __LINE__); ++ return -ret; ++ } ++ ++ /* DMA need 32 bytes alignment */ ++ align_mask = FMC_DMA_ALIGN - 1; ++ host->dma_buffer = (uintptr_t)(host->buforg + align_mask) & ~align_mask; ++ ++ host->buffer = (char *)(uintptr_t)host->dma_buffer; ++ ret = memset_s(host->buffer, FMC100_BUFFER_LEN, 0xff, FMC100_BUFFER_LEN); ++ if (ret) { ++ printf("%s %d ERR:memset_s fail\n", __func__, __LINE__); ++ return -ret; ++ } ++ host->send_cmd_pageprog = fmc100_send_cmd_pageprog; ++ host->send_cmd_status = fmc100_send_cmd_status; ++ host->send_cmd_readstart = fmc100_send_cmd_readstart; ++ host->send_cmd_erase = fmc100_send_cmd_erase; ++ host->send_cmd_readid = fmc100_send_cmd_readid; ++ host->send_cmd_reset = fmc100_send_cmd_reset; ++ host->set_system_clock = fmc_set_fmc_system_clock; ++ ++ return 0; ++} ++ ++int fmc100_host_init(struct fmc_host *host) ++{ ++ unsigned int reg; ++ unsigned int flash_type; ++ int ret; ++ ++ if (!host) ++ return -1; ++ fmc_pr(BT_DBG, "\t||*-Start SPI Nand host init\n"); ++ host->iobase = (void __iomem *)CONFIG_FMC_BUFFER_BASE; ++ host->regbase = (void __iomem *)CONFIG_FMC_REG_BASE; ++ if (!host->iobase || !host->regbase) ++ return -1; ++ reg = fmc_read(host, FMC_CFG); ++ flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT; ++ if (flash_type != FLASH_TYPE_SPI_NAND) { ++ db_msg("Error: Flash type isn't SPI Nand. reg: %#x\n", reg); ++ return -ENODEV; ++ } ++ ++ if ((reg & OP_MODE_MASK) == OP_MODE_BOOT) { ++ reg |= fmc_cfg_op_mode(OP_MODE_NORMAL); ++ fmc_write(host, FMC_CFG, reg); ++ fmc_pr(BT_DBG, "\t|||-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ } ++ ++ host->fmc_cfg = reg; ++ host->fmc_cfg_ecc0 = (reg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT; ++ ++ reg = fmc_read(host, FMC_GLOBAL_CFG); ++ if (reg & FMC_GLOBAL_CFG_WP_ENABLE) { ++ reg &= ~FMC_GLOBAL_CFG_WP_ENABLE; ++ fmc_write(host, FMC_GLOBAL_CFG, reg); ++ } ++ ++ ret = host_data_init(host); ++ if (ret) ++ return ret; ++ ++ /* ecc0_flag for ecc0 read/write */ ++ ecc0_flag = 0; ++ ++ reg = timing_cfg_tcsh(CS_HOLD_TIME) | ++ timing_cfg_tcss(CS_SETUP_TIME) | ++ timing_cfg_tshsl(CS_DESELECT_TIME); ++ fmc_write(host, FMC_SPI_TIMING_CFG, reg); ++ fmc_pr(BT_DBG, "\t|||-Set TIMING[%#x]%#x\n", FMC_SPI_TIMING_CFG, reg); ++ ++ reg = ALL_BURST_ENABLE; ++ fmc_write(host, FMC_DMA_AHB_CTRL, reg); ++ fmc_pr(BT_DBG, "\t|||-Set DMA_AHB[%#x]%#x\n", FMC_DMA_AHB_CTRL, reg); ++ ++ fmc_pr(BT_DBG, "\t|||-Register SPI Nand ID table and ecc probe\n"); ++ fmc_spi_nand_ids_register(); ++ nand_oob_resize = fmc100_set_config_info; ++ ++ fmc_pr(BT_DBG, "\t||*-End SPI Nand host init\n"); ++ ++ return 0; ++} ++ ++void fmc100_spi_nand_init(struct fmc_host *host) ++{ ++ struct nand_chip *chip = NULL; ++ if (host == NULL || host->chip == NULL) { ++ printf("para err!\n"); ++ return; ++ } ++ chip = host->chip; ++ fmc_pr(BT_DBG, "\t|*-Start fmc100 SPI Nand init\n"); ++ /* Set system clock and enable controller */ ++ fmc_pr(BT_DBG, "\t||-Set system clock and Enable Controller\n"); ++ if (host->set_system_clock) ++ host->set_system_clock(NULL, ENABLE); ++ /* FMC nand_chip struct init */ ++ fmc_pr(BT_DBG, "\t||-FMC100 struct nand_chip init\n"); ++ chip->priv = host; ++ fmc100_chip_init(chip); ++ fmc_pr(BT_DBG, "\t|*-End fmc100 SPI Nand init\n"); ++} +diff --git a/drivers/mtd/nand/raw/fmc100/fmc100.h b/drivers/mtd/nand/raw/fmc100/fmc100.h +new file mode 100644 +index 0000000..5f93be4 +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100/fmc100.h +@@ -0,0 +1,119 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __FMC100_H__ ++#define __FMC100_H__ ++ ++#include ++#include ++#include ++#include "../../../fmc_spi_ids.h" ++#include ++#include "securec.h" ++ ++/* These macroes are for debug only, reg option is slower then dma option */ ++#undef FMC100_SPI_NAND_SUPPORT_REG_READ ++#undef FMC100_SPI_NAND_SUPPORT_REG_WRITE ++/* open CONFIG_FS_MAY_NOT_YAFFS2 as you need ,just use for spi_nand */ ++#undef CONFIG_FS_MAY_NOT_YAFFS2 ++ ++#define REG_CNT_HIGH_BLOCK_NUM_SHIFT 10 ++ ++#define REG_CNT_BLOCK_NUM_MASK 0x3ff ++#define REG_CNT_BLOCK_NUM_SHIFT 22 ++ ++#define REG_CNT_PAGE_NUM_MASK 0x3f ++#define REG_CNT_PAGE_NUM_SHIFT 16 ++ ++#define REG_CNT_WRAP_MASK 0xf ++#define REG_CNT_WRAP_SHIFT 12 ++ ++#define ERR_STR_DRIVER "Driver does not support this configure " ++#define ERR_STR_CHECK "Please make sure the hardware configuration is correct" ++ ++#define SPI_NAND_MAX_PAGESIZE 4096 ++#define SPI_NAND_MAX_OOBSIZE 256 ++ ++#define FMC100_BUFFER_LEN (SPI_NAND_MAX_PAGESIZE + SPI_NAND_MAX_OOBSIZE) ++ ++#define FMC100_ADDR_CYCLE_MASK 0x2 ++ ++struct fmc_host { ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ struct fmc_spi spi[CONFIG_SPI_NAND_MAX_CHIP_NUM]; ++ struct fmc_cmd_op cmd_op; ++ ++ void __iomem *iobase; ++ void __iomem *regbase; ++ ++ unsigned int fmc_cfg; ++ unsigned int fmc_cfg_ecc0; ++ ++ unsigned int offset; ++ ++ struct device *dev; ++ ++ /* This is maybe an un-aligment address, only for malloc or free */ ++ char *buforg; ++ char *buffer; ++ ++ unsigned long dma_buffer; ++ unsigned long dma_oob; ++ ++ unsigned int addr_cycle; ++ unsigned int addr_value[2]; /* 2 addr */ ++ unsigned int cache_addr_value[2]; /* 2 addr */ ++ ++ unsigned int column; ++ unsigned int block_page_mask; ++ ++ unsigned int ecctype; ++ unsigned int pagesize; ++ unsigned int oobsize; ++ ++ int add_partition; ++ ++ /* BOOTROM read two bytes to detect the bad block flag */ ++#define FMC_BAD_BLOCK_POS 0 ++ unsigned char *bbm; /* nand bad block mark */ ++ unsigned short *epm; /* nand empty page mark */ ++ ++ unsigned int uc_er; ++ ++ void (*send_cmd_pageprog)(struct fmc_host *host); ++ void (*send_cmd_status)(struct fmc_host *host); ++ void (*send_cmd_readstart)(struct fmc_host *host); ++ void (*send_cmd_erase)(struct fmc_host *host); ++ void (*send_cmd_readid)(struct fmc_host *host); ++ void (*send_cmd_reset)(struct fmc_host *host); ++ void (*set_system_clock)(struct spi_op *op, int clk_en); ++}; ++ ++void fmc100_ecc0_switch(struct fmc_host *host, unsigned char op); ++ ++int fmc100_host_init(struct fmc_host *host); ++ ++void fmc100_spi_nand_init(struct fmc_host *host); ++ ++void fmc_spi_nand_ids_register(void); ++char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op, ++ unsigned char addr, unsigned int* const val); ++ ++#endif /* End of __FMC100_H__ */ +diff --git a/drivers/mtd/nand/raw/fmc100/fmc100_os.c b/drivers/mtd/nand/raw/fmc100/fmc100_os.c +new file mode 100644 +index 0000000..ad985b3 +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100/fmc100_os.c +@@ -0,0 +1,136 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "fmc100_os.h" ++#include ++ ++static struct fmc_host fmc100_host = { ++ .chip = NULL, ++}; ++ ++static void fmc100_driver_probe(struct nand_chip *chip, unsigned char cs) ++{ ++ int ret; ++ struct fmc_host *host = &fmc100_host; ++ ++ fmc_pr(BT_DBG, "\t*-Start SPI Nand flash driver probe\n"); ++ ++ if (!host->chip) { ++ /* FMC ip version check */ ++ if (fmc_ip_ver_check()) ++ db_bug("Error: fmc IP version unknown!\n"); ++ ++ /* FMC current SPI device type check */ ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NAND); ++ ++ /* FMC SPI nand init */ ++ if (memset_s((char *)host, sizeof(struct fmc_host), 0, ++ sizeof(struct fmc_host))) { ++ db_bug("Error: memset_s fail! %s %d\n", __func__, __LINE__); ++ return; ++ } ++ ret = fmc100_host_init(host); ++ if (ret) { ++ db_msg("Error: Host init failed, result: %d\n", ret); ++ /* Change SPI device type to default */ ++ fmc_dev_type_switch(FLASH_TYPE_DEFAULT); ++ return; ++ } ++ } else { ++ fmc_pr(BT_DBG, "\t*-SPI Nand host is initialized.\n"); ++ } ++ ++ host->cmd_op.cs = cs; ++ host->chip = chip; ++ fmc100_spi_nand_init(host); ++ ++ fmc_pr(BT_DBG, "\t*-End SPI Nand flash driver probe.\n"); ++ ++ return; ++} ++ ++static int fmc100_spi_nand_pre_probe(struct nand_chip *chip) ++{ ++ uint8_t nand_maf_id; ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ struct fmc_host *host = chip->priv; ++ int ret; ++ /* Reset the chip first */ ++ host->send_cmd_reset(host); ++ chip->dev_ready(mtd); ++ ++ /* Check the ID */ ++ host->offset = 0; ++ ret = memset_s((unsigned char *)(chip->IO_ADDR_R), 0x10, 0, 0x10); ++ if (ret) ++ printf("ERR:memset_s fail %s %d\n", __func__, __LINE__); ++ ++ host->send_cmd_readid(host); ++ nand_maf_id = readb(chip->IO_ADDR_R); ++ if (nand_maf_id == 0x00 || nand_maf_id == 0xff) { ++ printf("Cannot found a valid SPI Nand Device\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int board_nand_init(struct nand_chip *chip) ++{ ++ unsigned char chip_num = CONFIG_SPI_NAND_MAX_CHIP_NUM; ++ static unsigned char cs = 0; ++ unsigned char *fmc_cs = NULL; ++ ++ for (cs = 0; chip_num && (cs < CONFIG_FMC_MAX_CS_NUM); cs++) { ++ fmc_cs = get_cs_number(cs); ++ if (*fmc_cs) { ++ fmc_pr(BT_DBG, "\t\t*-Current CS(%d) is occupied.\n", ++ cs); ++ continue; ++ } ++ ++ fmc100_driver_probe(chip, cs); ++ chip_num--; ++ } ++ ++ if (chip_num) ++ return 1; ++ ++ if (fmc100_spi_nand_pre_probe(chip)) ++ return 1; ++ ++ return 0; ++} ++ ++static int fmc100_spi_nand_get_ecctype(void) ++{ ++ struct fmc_host *host = &fmc100_host; ++ ++ if (!host->chip) { ++ printf("SPI Nand flash uninitialized.\n"); ++ return -1; ++ } ++ ++ return match_ecc_type_to_yaffs(fmc100_host.ecctype); ++} ++ ++int nand_get_ecctype(void) ++{ ++ return fmc100_spi_nand_get_ecctype(); ++} +diff --git a/drivers/mtd/nand/raw/fmc100/fmc100_os.h b/drivers/mtd/nand/raw/fmc100/fmc100_os.h +new file mode 100644 +index 0000000..029cd81 +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100/fmc100_os.h +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __FMC100_OS_H__ ++#define __FMC100_OS_H__ ++ ++#include "fmc100.h" ++ ++int board_nand_init(struct nand_chip *chip); ++ ++#endif /* End of __FMC100_OS_H__ */ +diff --git a/drivers/mtd/nand/raw/fmc100/fmc100_spi_general.c b/drivers/mtd/nand/raw/fmc100/fmc100_spi_general.c +new file mode 100644 +index 0000000..5d9403c +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100/fmc100_spi_general.c +@@ -0,0 +1,290 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++/* ++ * Send set/get features command to SPI Nand flash ++ */ ++ ++void spi_nand_set_cmd(struct fmc_host* const host, u_char op, u_char addr, ++ u_char val) ++{ ++ unsigned int reg; ++ ++ reg = fmc_cmd_cmd1(op ? SPI_CMD_SET_FEATURE : SPI_CMD_GET_FEATURES); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(FT_DBG, "\t||||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ fmc_write(host, FMC_ADDRL, addr); ++ fmc_pr(FT_DBG, "\t||||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, addr); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN | ++ op_cfg_addr_num(FEATURES_OP_ADDR_NUM); ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(FT_DBG, "\t||||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_data_num_cnt(FEATURES_DATA_LEN); ++ fmc_write(host, FMC_DATA_NUM, reg); ++ fmc_pr(FT_DBG, "\t||||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | fmc_op_addr_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ ++ if (op == SET_OP) { ++ reg |= fmc_op_write_data_en(ENABLE); ++ writeb(val, host->iobase); ++ fmc_pr(FT_DBG, "\t||||-Write IO[%p]%#x\n", host->iobase, ++ *(u_char *)host->iobase); ++ } else { ++ reg |= fmc_op_read_data_en(ENABLE); ++ } ++ ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(FT_DBG, "\t||||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++} ++ ++char spi_nand_feature_op(struct fmc_spi* const spi, unsigned char op, ++ unsigned char addr, unsigned int* const val) ++{ ++ unsigned int reg; ++ const char *str[] = {"Get", "Set"}; ++ struct fmc_host *host = NULL; ++ unsigned char regval; ++ ++ if (spi == NULL || spi->host == NULL || val == NULL) { ++ printf("para err!\n"); ++ return -1; ++ } ++ host = (struct fmc_host *)spi->host; ++ if ((op == GET_OP) && (addr == STATUS_ADDR)) { ++ fmc_pr(SR_DBG, "\n\t\t|*-Start Get Status\n"); ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(SR_DBG, "\t\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(SR_DBG, "\t\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ fmc_cmd_wait_cpu_finish(host); ++ *val = fmc_read(host, FMC_STATUS); ++ fmc_pr(SR_DBG, "\t\t|*-End Get Status, result: %#x\n", *val); ++ return 0; ++ } ++ fmc_pr(FT_DBG, "\t|||*-Start %s feature, addr[%#x]\n", str[op], addr); ++ fmc100_ecc0_switch(host, ENABLE); ++ regval = *val & 0xff; ++ spi_nand_set_cmd(host, op, addr, regval); ++ if (op == GET_OP) { ++ if (host->iobase == NULL) { ++ printf("para err!"); ++ return -1; ++ } ++ *val = readb(host->iobase); ++ fmc_pr(FT_DBG, "\t||||-Read IO[%p]%#x\n", host->iobase, ++ *(u_char *)host->iobase); ++ } ++ fmc100_ecc0_switch(host, DISABLE); ++ fmc_pr(FT_DBG, "\t|||*-End %s Feature[%#x]:%#x\n", str[op], addr, *val); ++ return 0; ++} ++ ++/* ++ * Read status[C0H]:[0]bit OIP, judge whether the device is busy or not ++ */ ++static int spi_general_wait_ready(struct fmc_spi* const spi) ++{ ++ unsigned int status; ++ /* just get a big number, so move left 12 bits */ ++ unsigned int deadline = 1 << 12; ++ struct fmc_host *host = (struct fmc_host *)spi->host; ++ ++ do { ++ if (spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, &status)) { ++ printf("get feature failed!\n"); ++ return 1; ++ } ++ if (!(status & STATUS_OIP_MASK)) { ++ if ((host->cmd_op.l_cmd == NAND_CMD_ERASE2) && (status & STATUS_E_FAIL_MASK)) ++ return status; ++ ++ if ((host->cmd_op.l_cmd == NAND_CMD_PAGEPROG) && (status & STATUS_P_FAIL_MASK)) ++ return status; ++ return 0; ++ } ++ ++ udelay(1); /* delay 1 us */ ++ } while (deadline--); ++ ++ db_msg("Error: SPI Nand wait ready timeout, status: %#x\n", status); ++ ++ return 1; ++} ++ ++static void write_enable_fmc_op(struct fmc_host* const host) ++{ ++ unsigned int reg; ++ ++ reg = fmc_read(host, FMC_GLOBAL_CFG); ++ fmc_pr(WE_DBG, "\t||-Get GLOBAL_CFG[%#x]%#x\n", FMC_GLOBAL_CFG, reg); ++ if (reg & FMC_GLOBAL_CFG_WP_ENABLE) { ++ reg &= ~FMC_GLOBAL_CFG_WP_ENABLE; ++ fmc_write(host, FMC_GLOBAL_CFG, reg); ++ fmc_pr(WE_DBG, "\t||-Set GLOBAL_CFG[%#x]%#x\n", ++ FMC_GLOBAL_CFG, reg); ++ } ++ ++ reg = fmc_cmd_cmd1(SPI_CMD_WREN); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(WE_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(WE_DBG, "\t||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(WE_DBG, "\t||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++} ++ ++/* ++ * Send write enable cmd to SPI Nand, status[C0H]:[2]bit WEL must be set 1 ++ */ ++static int spi_general_write_enable(struct fmc_spi *spi) ++{ ++ unsigned int reg; ++ struct fmc_host *host = (struct fmc_host *)spi->host; ++ int ret; ++ ++ if (WE_DBG) ++ printf("\n"); ++ fmc_pr(WE_DBG, "\t|*-Start Write Enable\n"); ++ ++ ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, ®); ++ if (ret) { ++ db_msg("Error: Get status reg failed,line:%d\n", __LINE__); ++ return ret; ++ } ++ if (reg & STATUS_WEL_MASK) { ++ fmc_pr(WE_DBG, "\t||-Write Enable was opened! reg: %#x\n", reg); ++ return 0; ++ } ++ write_enable_fmc_op(host); ++#if WE_DBG ++ spi->driver->wait_ready(spi); ++ ++ ret = spi_nand_feature_op(spi, GET_OP, STATUS_ADDR, ®); ++ if (ret) { ++ db_msg("Error: Get status reg failed,line:%d\n", __LINE__); ++ return ret; ++ } ++ if (reg & STATUS_WEL_MASK) { ++ fmc_pr(WE_DBG, "\t||-Write Enable success. reg: %#x\n", reg); ++ } else { ++ db_msg("Error: Write Enable failed! reg: %#x\n", reg); ++ return ret; ++ } ++#endif ++ ++ fmc_pr(WE_DBG, "\t|*-End Write Enable\n"); ++ return 0; ++} ++ ++/* ++ * judge whether SPI Nand support QUAD read/write or not ++ */ ++static int spi_is_quad(struct fmc_spi *spi) ++{ ++ const char *if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"}; ++ ++ fmc_pr(QE_DBG, "\t\t|||*-SPI read iftype: %s write iftype: %s\n", ++ if_str[spi->read->iftype], if_str[spi->write->iftype]); ++ ++ if ((spi->read->iftype == IF_TYPE_QUAD) || ++ (spi->read->iftype == IF_TYPE_QIO) || ++ (spi->write->iftype == IF_TYPE_QUAD) || ++ (spi->write->iftype == IF_TYPE_QIO)) ++ return 1; ++ ++ return 0; ++} ++ ++/* ++ * Send set features cmd to SPI Nand, feature[B0H]:[0]bit QE would be set ++ */ ++static int spi_general_qe_enable(struct fmc_spi *spi) ++{ ++ unsigned int reg; ++ int op; ++ int ret; ++ ++ const char *str[] = {"Disable", "Enable"}; ++ ++ fmc_pr(QE_DBG, "\t||*-Start SPI Nand flash QE\n"); ++ ++ op = spi_is_quad(spi); ++ ++ fmc_pr(QE_DBG, "\t|||*-End Quad check, SPI Nand %s Quad.\n", str[op]); ++ ++ ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, ®); ++ if (ret) { ++ db_msg("Error: Get feature reg failed,line:%d\n", __LINE__); ++ return ret; ++ } ++ fmc_pr(QE_DBG, "\t|||-Get [%#x]feature: %#x\n", FEATURE_ADDR, reg); ++ if ((reg & FEATURE_QE_ENABLE) == op) { ++ fmc_pr(QE_DBG, "\t||*-SPI Nand quad was %sd!\n", str[op]); ++ return op; ++ } ++ ++ if (op == ENABLE) ++ reg |= FEATURE_QE_ENABLE; ++ else ++ reg &= ~FEATURE_QE_ENABLE; ++ ++ ret = spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, ®); ++ if (ret) { ++ db_msg("Error: set feature reg failed,line:%d\n", __LINE__); ++ return ret; ++ } ++ fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad\n", str[op]); ++ ++ spi->driver->wait_ready(spi); ++ ++ ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, ®); ++ if (ret) { ++ db_msg("Error: Get feature reg failed,line:%d\n", __LINE__); ++ return ret; ++ } ++ if ((reg & FEATURE_QE_ENABLE) == op) ++ fmc_pr(QE_DBG, "\t|||-SPI Nand %s Quad succeed!\n", str[op]); ++ else ++ db_msg("Error: %s Quad failed! reg: %#x\n", str[op], reg); ++ ++ fmc_pr(QE_DBG, "\t||*-End SPI Nand %s Quad.\n", str[op]); ++ ++ return op; ++} ++ ++/* some spi nand flash don't QUAD enable */ ++static int spi_do_not_qe_enable(struct fmc_spi *spi) ++{ ++ return 0; ++} +diff --git a/drivers/mtd/nand/raw/fmc100/fmc_spi_nand_ids.c b/drivers/mtd/nand/raw/fmc100/fmc_spi_nand_ids.c +new file mode 100644 +index 0000000..4a8fe46 +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100/fmc_spi_nand_ids.c +@@ -0,0 +1,360 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "../../../fmc_spi_ids.h" ++#include "nfc_common.h" ++#include "fmc100.h" ++ ++set_read_quad(1, INFINITE, 75); ++ ++set_write_quad(0, 256, 75); ++ ++set_erase_sector_128k(0, _128K, 75); ++ ++#include "fmc100_spi_general.c" ++static struct spi_drv spi_driver_general = { ++ .wait_ready = spi_general_wait_ready, ++ .write_enable = spi_general_write_enable, ++ .qe_enable = spi_general_qe_enable, ++}; ++ ++__maybe_unused static struct spi_drv spi_driver_no_qe = { ++ .wait_ready = spi_general_wait_ready, ++ .write_enable = spi_general_write_enable, ++ .qe_enable = spi_do_not_qe_enable, ++}; ++ ++#define SPI_NAND_ID_TAB_VER "2.7" ++ ++/****************************************************************************** ++ * We do not guarantee the compatibility of the following device models in the ++ * table.Device compatibility is based solely on the list of compatible devices ++ * in the release package. ++ ******************************************************************************/ ++ ++struct spi_nand_info fmc_spi_nand_flash_table[] = { ++ /* Dosilicon 1.8V DS35M4GM-IB 4Gb */ ++ { ++ .name = "DS35M4GM-IB", ++ .id = {0xe5, 0xA4}, ++ .id_len = _2B, ++ .chipsize = _512M, ++ .erasesize = _128K, ++ .pagesize = _2K, ++ .oobsize = _128B, ++ .badblock_pos = BBP_FIRST_PAGE, ++ .read = { ++ &read_quad(1, INFINITE, 75), /* 104MHz */ ++ 0 ++ }, ++ .write = { ++ &write_quad(0, 256, 75), /* 104MHz */ ++ 0 ++ }, ++ .erase = { ++ &erase_sector_128k(0, _128K, 75), /* 104MHz */ ++ 0 ++ }, ++ .driver = &spi_driver_general, ++ }, ++ ++ { .id_len = 0, }, ++}; ++ ++static void fmc100_spi_nand_search_rw(struct spi_nand_info *spiinfo, ++ struct spi_op *spiop_rw, u_int iftype, ++ u_int max_dummy, int rw_type) ++{ ++ int ix = 0; ++ struct spi_op **spiop = NULL; ++ struct spi_op **fitspiop = NULL; ++ ++ for (fitspiop = spiop = (rw_type ? spiinfo->write : spiinfo->read); ++ (*spiop) && ix < MAX_SPI_OP; spiop++, ix++) ++ if (((*spiop)->iftype & iftype) && ++ ((*spiop)->dummy <= max_dummy) && ++ ((*fitspiop)->iftype < (*spiop)->iftype)) ++ fitspiop = spiop; ++ ++ if (memcpy_s(spiop_rw, sizeof(struct spi_op), (*fitspiop), sizeof(struct spi_op))) ++ printf("ERR:memcpy_s fail %s %d", __func__, __LINE__); ++} ++ ++static void fmc100_spi_nand_get_erase(struct spi_nand_info *spiinfo, ++ struct spi_op *spiop_erase) ++{ ++ int ix; ++ ++ spiop_erase->size = 0; ++ for (ix = 0; ix < MAX_SPI_OP; ix++) { ++ if (spiinfo->erase[ix] == NULL) ++ break; ++ if (spiinfo->erasesize == spiinfo->erase[ix]->size) { ++ if (memcpy_s(&spiop_erase[ix], sizeof(struct spi_op), ++ spiinfo->erase[ix], sizeof(struct spi_op))) ++ printf("ERR:memcpy_s fail %s %d", __func__, __LINE__); ++ break; ++ } ++ } ++} ++ ++static void fmc100_map_spi_op(struct fmc_spi *spi) ++{ ++ unsigned char ix; ++ ++ const int iftype_read[] = { ++ SPI_IF_READ_STD, IF_TYPE_STD, ++ SPI_IF_READ_FAST, IF_TYPE_STD, ++ SPI_IF_READ_DUAL, IF_TYPE_DUAL, ++ SPI_IF_READ_DUAL_ADDR, IF_TYPE_DIO, ++ SPI_IF_READ_QUAD, IF_TYPE_QUAD, ++ SPI_IF_READ_QUAD_ADDR, IF_TYPE_QIO, ++ 0, 0, ++ }; ++ const int iftype_write[] = { ++ SPI_IF_WRITE_STD, IF_TYPE_STD, ++ SPI_IF_WRITE_QUAD, IF_TYPE_QUAD, ++ 0, 0, ++ }; ++ const char *if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"}; ++ ++ if (!spi->write || !spi->read || !spi->erase) { ++ printf("spi nand ids err:spi->func is null!\n"); ++ return; ++ } ++ fmc_pr(BT_DBG, "\t||*-Start Get SPI operation iftype & clock\n"); ++ /* the element with an even number of arrays, so increase is 2 */ ++ for (ix = 0; iftype_write[ix]; ix += 2) { ++ if (spi->write->iftype == iftype_write[ix]) { ++ spi->write->iftype = iftype_write[ix + 1]; ++ break; ++ } ++ } ++ fmc_get_fmc_best_2x_clock(&spi->write->clock); ++ fmc_pr(BT_DBG, "\t|||-Get best write iftype: %s clock type: %d\n", ++ if_str[spi->write->iftype], ++ get_fmc_clk_type(spi->write->clock)); ++ /* the element with an even number of arrays, so increase is 2 */ ++ for (ix = 0; iftype_read[ix]; ix += 2) { ++ if (spi->read->iftype == iftype_read[ix]) { ++ spi->read->iftype = iftype_read[ix + 1]; ++ break; ++ } ++ } ++ fmc_get_fmc_best_2x_clock(&spi->read->clock); ++ fmc_pr(BT_DBG, "\t|||-Get best read iftype: %s clock type: %d\n", ++ if_str[spi->read->iftype], ++ get_fmc_clk_type(spi->read->clock)); ++ ++ fmc_get_fmc_best_2x_clock(&spi->erase->clock); ++ spi->erase->iftype = IF_TYPE_STD; ++ fmc_pr(BT_DBG, "\t|||-Get best erase iftype: %s clock type: %d\n", ++ if_str[spi->erase->iftype], ++ get_fmc_clk_type(spi->erase->clock)); ++ ++ fmc_pr(BT_DBG, "\t||*-End Get SPI operation iftype & clock\n"); ++} ++ ++static char fmc100_disable_wr_protect(struct fmc_spi* const spi, unsigned int* const reg) ++{ ++ char ret; ++ ++ ret = spi_nand_feature_op(spi, GET_OP, PROTECT_ADDR, reg); ++ if (ret) { ++ printf("get protect reg failed!\n"); ++ return ret; ++ } ++ fmc_pr(BT_DBG, "\t||-Get protect status[%#x]: %#x\n", PROTECT_ADDR, *reg); ++ if (any_bp_enable(*reg)) { ++ *reg &= ~ALL_BP_MASK; ++ ret = spi_nand_feature_op(spi, SET_OP, PROTECT_ADDR, reg); ++ if (ret) { ++ printf("set protect reg failed!\n"); ++ return ret; ++ } ++ fmc_pr(BT_DBG, "\t||-Set [%#x]FT %#x\n", PROTECT_ADDR, *reg); ++ ++ spi->driver->wait_ready(spi); ++ ++ ret = spi_nand_feature_op(spi, GET_OP, PROTECT_ADDR, reg); ++ if (ret) { ++ printf("get protect reg failed!\n"); ++ return ret; ++ } ++ fmc_pr(BT_DBG, "\t||-Check BP disable result: %#x\n", *reg); ++ if (any_bp_enable(*reg)) ++ db_msg("Error: Write protection disable failed!\n"); ++ } ++ ++ return ret; ++} ++ ++static char fmc100_disable_inner_ecc(struct fmc_spi* const spi, unsigned int* const reg) ++{ ++ char ret; ++ ++ ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, reg); ++ if (ret) { ++ printf("get feature reg failed!\n"); ++ return ret; ++ } ++ fmc_pr(BT_DBG, "\t||-Get feature status[%#x]: %#x\n", FEATURE_ADDR, *reg); ++ if (*reg & FEATURE_ECC_ENABLE) { ++ *reg &= ~FEATURE_ECC_ENABLE; ++ ret = spi_nand_feature_op(spi, SET_OP, FEATURE_ADDR, reg); ++ if (ret) { ++ printf("set feature reg failed!\n"); ++ return ret; ++ } ++ fmc_pr(BT_DBG, "\t||-Set [%#x]FT: %#x\n", FEATURE_ADDR, *reg); ++ ++ spi->driver->wait_ready(spi); ++ ++ ret = spi_nand_feature_op(spi, GET_OP, FEATURE_ADDR, reg); ++ if (ret) { ++ printf("get feature reg failed!\n"); ++ return ret; ++ } ++ fmc_pr(BT_DBG, "\t||-Check internal ECC disable result: %#x\n", ++ *reg); ++ if (*reg & FEATURE_ECC_ENABLE) ++ db_msg("Error: Chip internal ECC disable failed!\n"); ++ } ++ ++ return ret; ++} ++ ++static void fmc100_spi_ids_probe(struct fmc_host *host, ++ struct spi_nand_info *spi_dev) ++{ ++ unsigned int reg; ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_cs = get_cs_number(host->cmd_op.cs); ++ char ret; ++ ++ fmc_pr(BT_DBG, "\t|*-Start match SPI operation & chip init\n"); ++ ++ spi->host = host; ++ spi->name = spi_dev->name; ++ spi->driver = spi_dev->driver; ++ ++ fmc100_spi_nand_search_rw(spi_dev, spi->read, ++ FMC_SPI_NAND_SUPPORT_READ, ++ FMC_SPI_NAND_SUPPORT_MAX_DUMMY, RW_OP_READ); ++ fmc_pr(BT_DBG, "\t||-Save spi->read op cmd:%#x\n", spi->read->cmd); ++ ++ fmc100_spi_nand_search_rw(spi_dev, spi->write, ++ FMC_SPI_NAND_SUPPORT_WRITE, ++ FMC_SPI_NAND_SUPPORT_MAX_DUMMY, RW_OP_WRITE); ++ fmc_pr(BT_DBG, "\t||-Save spi->write op cmd:%#x\n", spi->write->cmd); ++ ++ fmc100_spi_nand_get_erase(spi_dev, spi->erase); ++ fmc_pr(BT_DBG, "\t||-Save spi->erase op cmd:%#x\n", spi->erase->cmd); ++ ++ fmc100_map_spi_op(spi); ++ ++ spi->driver->qe_enable(spi); ++ ++ /* Disable write protection */ ++ ret = fmc100_disable_wr_protect(spi, ®); ++ if (ret) ++ return; ++ ++ /* Disable chip internal ECC */ ++ ret = fmc100_disable_inner_ecc(spi, ®); ++ if (ret) ++ return; ++ ++ (*fmc_cs)++; ++ ++ fmc_pr(BT_DBG, "\t|*-End match SPI operation & chip init\n"); ++} ++ ++static struct nand_flash_dev spi_nand_dev; ++ ++static struct nand_flash_dev *spi_nand_get_flash_info(struct mtd_info *mtd, ++ struct nand_chip *chip, unsigned char *id) ++{ ++ unsigned char ix; ++ int len; ++ char buffer[TMP_BUF_LEN]; ++ struct fmc_host *host = chip->priv; ++ struct spi_nand_info *spi_dev = fmc_spi_nand_flash_table; ++ struct nand_flash_dev *type = &spi_nand_dev; ++ int ret; ++ ++ fmc_pr(BT_DBG, "\t*-Start find SPI Nand flash\n"); ++ ++ ret = sprintf_s(buffer, TMP_BUF_LEN, "SPI Nand(cs %d) ID: %#x %#x", ++ host->cmd_op.cs, id[0], id[1]); ++ if (ret < 0) ++ return NULL; ++ len = ret; ++ for (; spi_dev->id_len; spi_dev++) { ++ if (memcmp(id, spi_dev->id, spi_dev->id_len)) ++ continue; ++ /* element with an even number of arrays, so increase is 2 */ ++ for (ix = 2; ix < spi_dev->id_len; ix++) { ++ ret = sprintf_s(buffer + len, TMP_BUF_LEN, " %#x", id[ix]); ++ if (ret < 0) ++ return NULL; ++ len += ret; ++ } ++ printf("%s Name:\"%s\"\n", buffer, spi_dev->name); ++ ++ fmc_pr(BT_DBG, "\t||-CS(%d) found SPI Nand: %s\n", ++ host->cmd_op.cs, spi_dev->name); ++ ++ type->name = spi_dev->name; ++ if (memcpy_s(type->id, NAND_MAX_ID_LEN, spi_dev->id, spi_dev->id_len)) ++ printf("ERR:%s %d\n", __func__, __LINE__); ++ type->pagesize = spi_dev->pagesize; ++ type->chipsize = byte_to_mb(spi_dev->chipsize); ++ type->erasesize = spi_dev->erasesize; ++ type->id_len = spi_dev->id_len; ++ type->oobsize = spi_dev->oobsize; ++ type->options = chip->options; ++ fmc_pr(BT_DBG, "\t|-Save struct spi_nand_info info\n"); ++ ++ mtd->size = spi_dev->chipsize; ++ ++ fmc100_spi_ids_probe(host, spi_dev); ++ ++ fmc_pr(BT_DBG, "\t*-Found SPI nand: %s\n", spi_dev->name); ++ ++ return type; ++ } ++ ++ fmc_pr(BT_DBG, "\t*-Not found SPI nand flash, ID: %s\n", buffer); ++ ++ return NULL; ++} ++ ++void fmc_spi_nand_ids_register(void) ++{ ++#ifndef CONFIG_SYS_NAND_QUIET_TEST ++ printf("SPI Nand ID Table Version %s\n", SPI_NAND_ID_TAB_VER); ++#endif ++ get_flash_type = spi_nand_get_flash_info; ++} +diff --git a/drivers/mtd/nand/raw/fmc100_nand/Makefile b/drivers/mtd/nand/raw/fmc100_nand/Makefile +new file mode 100644 +index 0000000..cacbf4a +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100_nand/Makefile +@@ -0,0 +1,21 @@ ++# ++# The Flash Memory Controller v100 Device Driver for vendor ++# ++# Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, see ++# . ++# ++ ++obj-y += fmc100_nand.o fmc_nand_spl_ids.o +diff --git a/drivers/mtd/nand/raw/fmc100_nand/fmc100_nand.c b/drivers/mtd/nand/raw/fmc100_nand/fmc100_nand.c +new file mode 100644 +index 0000000..10ae58c +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100_nand/fmc100_nand.c +@@ -0,0 +1,1027 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "fmc100_nand.h" ++#include ++#include ++#include ++#include ++#include ++ ++static struct fmc_host fmc_host = { ++ .chip = NULL, ++}; ++ ++static void fmc100_dma_transfer(struct fmc_host* const host, unsigned int todev) ++{ ++ unsigned int reg; ++ char *op = todev ? "write" : "read"; ++ ++ fmc_pr(DMA_DB, "\t\t *-Start %s page dma transfer\n", op); ++ /* get hight 32 bits addr */ ++ reg = (host->dma_buffer & FMC_DMA_SADDRH_MASK) >> 32; ++ fmc_write(host, FMC_DMA_SADDRH_D0, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set ADDR0[%#x]%#x\n", FMC_DMA_SADDRH_D0, reg); ++ ++ reg = (unsigned int)host->dma_buffer; ++ fmc_write(host, FMC_DMA_SADDR_D0, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set ADDR0[%#x]%#x\n", FMC_DMA_SADDR_D0, reg); ++ ++ reg += FMC_DMA_ADDR_OFFSET; ++ fmc_write(host, FMC_DMA_SADDR_D1, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set ADDR1[%#x]%#x\n", FMC_DMA_SADDR_D1, reg); ++ ++ reg += FMC_DMA_ADDR_OFFSET; ++ fmc_write(host, FMC_DMA_SADDR_D2, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set ADDR2[%#x]%#x\n", FMC_DMA_SADDR_D2, reg); ++ ++ reg += FMC_DMA_ADDR_OFFSET; ++ fmc_write(host, FMC_DMA_SADDR_D3, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set ADDR3[%#x]%#x\n", FMC_DMA_SADDR_D3, reg); ++ /* get hight 32 bits addr */ ++ reg = (host->dma_oob & FMC_DMA_SADDRH_MASK) >> 32; ++ fmc_write(host, FMC_DMA_SADDRH_OOB, reg); ++ ++ reg = (unsigned int)host->dma_oob; ++ fmc_write(host, FMC_DMA_SADDR_OOB, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set OOB[%#x]%#x\n", FMC_DMA_SADDR_OOB, reg); ++ ++ if (host->ecctype == NAND_ECC_0BIT) { ++ fmc_write(host, FMC_DMA_LEN, fmc_dma_len_set(host->oobsize)); ++ fmc_pr(DMA_DB, "\t\t |-Set LEN[%#x]%#x\n", FMC_DMA_LEN, reg); ++ } ++ reg = fmc_op_read_data_en(ENABLE) | fmc_op_write_data_en(ENABLE); ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ reg = FMC_DMA_AHB_CTRL_DMA_PP_EN | FMC_DMA_AHB_CTRL_BURST16_EN | ++ FMC_DMA_AHB_CTRL_BURST8_EN | FMC_DMA_AHB_CTRL_BURST4_EN; ++ fmc_write(host, FMC_DMA_AHB_CTRL, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set AHBCTRL[%#x]%#x\n", FMC_DMA_AHB_CTRL, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | op_cfg_addr_num(host->addr_cycle); ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = OP_CTRL_DMA_OP_READY; ++ if (todev) ++ reg |= op_ctrl_rw_op(todev); ++ fmc_write(host, FMC_OP_CTRL, reg); ++ fmc_pr(DMA_DB, "\t\t |-Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, reg); ++ ++ fmc_dma_wait_cpu_finish(host); ++ ++ fmc_pr(DMA_DB, "\t\t *-End %s page dma transfer\n", op); ++ ++ return; ++} ++ ++static void fmc100_send_cmd_write(struct fmc_host *host) ++{ ++ unsigned int reg; ++ ++ fmc_pr(WR_DBG, "\t|*-Start send page programme cmd\n"); ++ ++ if (*host->bbm != 0xFF && *host->bbm != 0x00) ++ printf("WARNING: attempt to write an invalid bbm. " \ ++ "page: 0x%08x, mark: 0x%02x,\n", ++ get_page_index(host), *host->bbm); ++ ++ if (ecc0_flag == 1) { ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ fmc_write(host, FMC_DMA_LEN, host->oobsize); ++ } else { ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ } ++ ++ reg = host->addr_value[1]; ++ fmc_write(host, FMC_ADDRH, reg); ++ fmc_pr(WR_DBG, "\t||-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg); ++ ++ reg = host->addr_value[0] & 0xffff0000; ++ fmc_write(host, FMC_ADDRL, reg); ++ fmc_pr(WR_DBG, "\t||-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = fmc_cmd_cmd2(NAND_CMD_PAGEPROG) | fmc_cmd_cmd1(NAND_CMD_SEQIN); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(WR_DBG, "\t||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ if (ecc0_flag != 1) ++ *host->epm = 0x0000; ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ flush_dcache_range(host->dma_buffer, ++ host->dma_buffer + host->pagesize + host->oobsize); ++#endif ++ ++ fmc100_dma_transfer(host, RW_OP_WRITE); ++ ++ if (ecc0_flag == 1) ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ fmc_pr(WR_DBG, "\t|*-End send page read cmd\n"); ++} ++ ++static void fmc100_send_cmd_read(struct fmc_host *host) ++{ ++ unsigned int reg; ++ ++ fmc_pr(RD_DBG, "\t*-Start send page read cmd\n"); ++ ++ host->page_status = 0; ++ ++ if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB)) { ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ fmc_write(host, FMC_DMA_LEN, host->oobsize); ++ } else { ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ reg = host->nand_cfg; ++ fmc_write(host, FMC_CFG, reg); ++ fmc_pr(RD_DBG, "\t|-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ } ++ reg = FMC_INT_CLR_ALL; ++ fmc_write(host, FMC_INT_CLR, reg); ++ fmc_pr(RD_DBG, "\t|-Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, reg); ++ ++ reg = host->addr_value[1]; ++ fmc_write(host, FMC_ADDRH, reg); ++ fmc_pr(RD_DBG, "\t|-Set ADDRH[%#x]%#x\n", FMC_ADDRH, reg); ++ ++ reg = host->addr_value[0] & 0xffff0000; ++ fmc_write(host, FMC_ADDRL, reg); ++ fmc_pr(RD_DBG, "\t|-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = fmc_cmd_cmd2(NAND_CMD_READSTART) | fmc_cmd_cmd1(NAND_CMD_READ0); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(RD_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range(host->dma_buffer, ++ host->dma_buffer + host->pagesize + host->oobsize); ++#endif ++ ++ fmc100_dma_transfer(host, RW_OP_READ); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range(host->dma_buffer, ++ host->dma_buffer + host->pagesize + host->oobsize); ++#endif ++ ++ if (fmc_read(host, FMC_INT) & FMC_INT_ERR_INVALID) ++ host->page_status |= FMC100_PS_UC_ECC; ++ ++ if (ecc0_flag == 1 && (host->cmd_op.l_cmd != NAND_CMD_READOOB)) ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ fmc_pr(RD_DBG, "\t*-End send page read cmd\n"); ++} ++ ++static void fmc100_send_cmd_erase(struct fmc_host* const host) ++{ ++ unsigned int reg; ++ ++ fmc_pr(ER_DBG, "\t *-Start send cmd erase\n"); ++ ++ /* Don't case the read retry config */ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ reg = host->addr_value[0]; ++ fmc_write(host, FMC_ADDRL, reg); ++ fmc_pr(ER_DBG, "\t |-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = fmc_cmd_cmd2(NAND_CMD_ERASE2) | fmc_cmd_cmd1(NAND_CMD_ERASE1); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(ER_DBG, "\t |-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | ++ op_cfg_addr_num(host->addr_cycle); ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(ER_DBG, "\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ /* need to config WAIT_READY_EN */ ++ reg = fmc_op_wait_ready_en(ENABLE) | ++ fmc_op_cmd1_en(ENABLE) | ++ fmc_op_cmd2_en(ENABLE) | ++ fmc_op_addr_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(ER_DBG, "\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ fmc_pr(ER_DBG, "\t |*-End send cmd erase\n"); ++} ++ ++static void fmc100_ecc_randomizer(struct fmc_host* const host, int ecc_en, ++ int randomizer_en) ++{ ++ unsigned int old_reg; ++ unsigned int reg; ++ unsigned int change = 0; ++ char *ecc_op = ecc_en ? "Quit" : "Enter"; ++ char *rand_op = randomizer_en ? "Enable" : "Disable"; ++ ++ if (is_randomizer(host)) { ++ reg = old_reg = fmc_read(host, FMC_GLOBAL_CFG); ++ if (randomizer_en) ++ reg |= FMC_GLOBAL_CFG_RANDOMIZER_EN; ++ else ++ reg &= ~FMC_GLOBAL_CFG_RANDOMIZER_EN; ++ ++ if (old_reg != reg) { ++ fmc_pr(EC_DBG, "\t |*-Start %s randomizer\n", rand_op); ++ fmc_pr(EC_DBG, "\t ||-Get global CFG[%#x]%#x\n", ++ FMC_GLOBAL_CFG, old_reg); ++ fmc_write(host, FMC_GLOBAL_CFG, reg); ++ fmc_pr(EC_DBG, "\t ||-Set global CFG[%#x]%#x\n", ++ FMC_GLOBAL_CFG, reg); ++ change++; ++ } ++ } ++ ++ old_reg = fmc_read(host, FMC_CFG); ++ reg = (ecc_en ? host->nand_cfg : host->nand_cfg_ecc0); ++ ++ if (old_reg != reg) { ++ fmc_pr(EC_DBG, "\t |%s-Start %s ECC0 mode\n", ++ change ? "|" : "*", ecc_op); ++ fmc_pr(EC_DBG, "\t ||-Get CFG[%#x]%#x\n", FMC_CFG, old_reg); ++ fmc_write(host, FMC_CFG, reg); ++ fmc_pr(EC_DBG, "\t ||-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ change++; ++ } ++ ++ if (EC_DBG && change) ++ fmc_pr(EC_DBG, "\t |*-End randomizer and ECC0 mode config\n"); ++} ++ ++static void fmc100_send_cmd_status(struct fmc_host* const host) ++{ ++ unsigned int regval; ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ regval = op_cfg_fm_cs(host->cmd_op.cs); ++ fmc_write(host, FMC_OP_CFG, regval); ++ ++ regval = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, regval); ++ ++ fmc_cmd_wait_cpu_finish(host); ++} ++ ++static void fmc100_send_cmd_readid(struct fmc_host *host) ++{ ++ unsigned int reg; ++ ++ fmc_pr(BT_DBG, "\t *-Start read nand flash ID\n"); ++ ++ host->enable_ecc_randomizer(host, DISABLE, DISABLE); ++ ++ reg = fmc_data_num_cnt(host->cmd_op.data_no); ++ fmc_write(host, FMC_DATA_NUM, reg); ++ fmc_pr(BT_DBG, "\t |-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = fmc_cmd_cmd1(NAND_CMD_READID); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(BT_DBG, "\t |-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = 0; ++ fmc_write(host, FMC_ADDRL, reg); ++ fmc_pr(BT_DBG, "\t |-Set ADDRL[%#x]%#x\n", FMC_ADDRL, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs) | ++ op_cfg_addr_num(READ_ID_ADDR_NUM); ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(BT_DBG, "\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | ++ fmc_op_addr_en(ENABLE) | ++ fmc_op_read_data_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(BT_DBG, "\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ host->addr_cycle = 0x0; ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ fmc_pr(BT_DBG, "\t *-End read nand flash ID\n"); ++} ++ ++static void fmc100_send_cmd_reset(struct fmc_host* const host) ++{ ++ unsigned int reg; ++ ++ fmc_pr(BT_DBG, "\t *-Start reset nand flash\n"); ++ ++ reg = fmc_cmd_cmd1(NAND_CMD_RESET); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(BT_DBG, "\t |-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(host->cmd_op.cs); ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(BT_DBG, "\t |-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | ++ fmc_op_wait_ready_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(BT_DBG, "\t |-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ fmc_pr(BT_DBG, "\t *-End reset nand flash\n"); ++} ++ ++static unsigned char fmc100_read_byte(struct mtd_info* const mtd) ++{ ++ unsigned char value; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READID) { ++ value = readb(chip->IO_ADDR_R + host->offset); ++ host->offset++; ++ if (host->cmd_op.data_no == host->offset) ++ host->cmd_op.l_cmd = 0; ++ return value; ++ } ++ ++ if (host->cmd_op.cmd == NAND_CMD_STATUS) { ++ value = fmc_read(host, FMC_STATUS); ++ if (host->cmd_op.l_cmd == NAND_CMD_ERASE1) ++ fmc_pr(ER_DBG, "\t*-Erase WP status: %#x\n", value); ++ if (host->cmd_op.l_cmd == NAND_CMD_PAGEPROG) ++ fmc_pr(WR_DBG, "\t*-Write WP status: %#x\n", value); ++ return value; ++ } ++ ++ if (host->cmd_op.l_cmd == NAND_CMD_READOOB) { ++ value = readb(host->dma_oob + host->offset); ++ host->offset++; ++ return value; ++ } ++ ++ host->offset++; ++ ++ return readb(host->buffer + host->column + host->offset - 1); ++} ++ ++static unsigned short fmc100_read_word(struct mtd_info* const mtd) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ return readw(host->buffer + host->column + host->offset); ++} ++ ++static void fmc100_write_buf(struct mtd_info* const mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++#ifdef FMC100_NAND_SUPPORT_REG_WRITE ++ if (buf == chip->oob_poi) { ++ if (memcpy_s((char *)host->iobase + host->pagesize, len, buf, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } else { ++ if (memcpy_s((char *)host->iobase, len, buf, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } ++#else ++ if (buf == chip->oob_poi) { ++ if (memcpy_s((char *)host->dma_oob, len, buf, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } else { ++ if (memcpy_s((char *)host->dma_buffer, len, buf, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } ++#endif ++ return; ++} ++ ++static void fmc100_read_buf(struct mtd_info* const mtd, u_char* const buf, int len) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++#ifdef FMC100_NAND_SUPPORT_REG_READ ++ if (buf == chip->oob_poi) { ++ if (memcpy_s(buf, (char *)host->iobase + host->pagesize, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } else { ++ if (memcpy_s(buf, len, (char *)host->iobase, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } ++#else ++ if (buf == chip->oob_poi) { ++ if (memcpy_s(buf, len, (char *)host->dma_oob, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } else { ++ if (memcpy_s(buf, len, (char *)host->dma_buffer, len)) ++ printf("%s %d ERR:memcpy_s fail\n", __func__, __LINE__); ++ } ++#endif ++ return; ++} ++ ++static void fmc100_select_chip(struct mtd_info *mtd, int chipselect) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ if (chipselect < 0) ++ return; ++ ++ if (chipselect > CONFIG_SYS_MAX_NAND_DEVICE) ++ db_bug("Error: Invalid chip select: %d\n", chipselect); ++ ++ host->cmd_op.cs = chipselect; ++ if (host->mtd != mtd) ++ host->mtd = mtd; ++ ++ switch (chip->state) { ++ case FL_ERASING: ++ host->cmd_op.l_cmd = NAND_CMD_ERASE1; ++ if (ER_DBG) ++ printf("\n"); ++ fmc_pr(ER_DBG, "\t*-Erase chip: %d\n", chipselect); ++ break; ++ case FL_WRITING: ++ host->cmd_op.l_cmd = NAND_CMD_PAGEPROG; ++ if (WR_DBG) ++ printf("\n"); ++ fmc_pr(WR_DBG, "\t*-Write chip: %d\n", chipselect); ++ break; ++ case FL_READING: ++ host->cmd_op.l_cmd = NAND_CMD_READ0; ++ if (RD_DBG) ++ printf("\n"); ++ fmc_pr(RD_DBG, "\t*-Read chip: %d\n", chipselect); ++ break; ++ default: ++ break; ++ } ++} ++ ++static int fmc100_dev_ready(struct mtd_info* const mtd) ++{ ++ return 0x1; ++} ++ ++/* ++ * 'host->epm' only use the first oobfree[0] field, it looks very simple, But... ++ */ ++static struct nand_ecclayout nand_ecc_default = { ++ .oobfree = {{2, 30} } ++}; ++ ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++static struct nand_ecclayout nand_ecc_2k16bit = { ++ .oobfree = {{2, 6} } ++}; ++ ++static struct nand_ecclayout nand_ecc_4k16bit = { ++ .oobfree = {{2, 14} } ++}; ++#endif ++ ++/* ecc/pagesize get from NAND controller */ ++static struct nand_config_info fmc100_nand_hw_auto_config_table[] = { ++ /* pagetype , ecctype , oob size , ecc layout */ ++ {NAND_PAGE_16K, NAND_ECC_64BIT, 1824, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_40BIT, 1200, &nand_ecc_default}, ++ {NAND_PAGE_16K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_8K, NAND_ECC_64BIT, 928, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_40BIT, 600, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_24BIT, 368, &nand_ecc_default}, ++ {NAND_PAGE_8K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_4K, NAND_ECC_24BIT, 200, &nand_ecc_default}, ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++ {NAND_PAGE_4K, NAND_ECC_16BIT, 128, &nand_ecc_4k16bit}, ++#endif ++ {NAND_PAGE_4K, NAND_ECC_8BIT, 128, &nand_ecc_default}, ++ {NAND_PAGE_4K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {NAND_PAGE_2K, NAND_ECC_24BIT, 128, &nand_ecc_default}, ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++ {NAND_PAGE_2K, NAND_ECC_16BIT, 64, &nand_ecc_2k16bit}, ++#endif ++ {NAND_PAGE_2K, NAND_ECC_8BIT, 64, &nand_ecc_default}, ++ {NAND_PAGE_2K, NAND_ECC_0BIT, 32, &nand_ecc_default}, ++ ++ {0, 0, 0, NULL}, ++}; ++ ++/* ++ * 0 - This NAND NOT support randomizer ++ * 1 - This NAND support randomizer. ++ */ ++static int fmc100_nand_support_randomizer(u_int pageisze, u_int ecctype) ++{ ++ switch (pageisze) { ++ case _8K: ++ return (ecctype >= NAND_ECC_24BIT && ecctype <= NAND_ECC_80BIT); ++ case _16K: ++ return (ecctype >= NAND_ECC_40BIT && ecctype <= NAND_ECC_80BIT); ++ case _32K: ++ return (ecctype >= NAND_ECC_40BIT && ecctype <= NAND_ECC_80BIT); ++ default: ++ return 0; ++ } ++} ++ ++/* used the best correct arithmetic. */ ++static struct nand_config_info *fmc100_get_config_type_info( ++ struct nand_config_info *config, struct mtd_info* const mtd) ++{ ++ struct nand_config_info *best = NULL; ++ ++ for (; config->layout; config++) { ++ if (match_page_type_to_size(config->pagetype) != mtd->writesize) ++ continue; ++ ++ if (mtd->oobsize < config->oobsize) ++ continue; ++ ++ if (!best || (best->ecctype < config->ecctype)) ++ best = config; ++ } ++ ++ return best; ++} ++ ++static unsigned int fmc100_get_ecc_reg(struct fmc_host* const host, ++ struct nand_config_info* const info) ++{ ++ struct mtd_info *mtd = host->mtd; ++ ++ host->ecctype = info->ecctype; ++ fmc_pr(BT_DBG, "\t |-Save best EccType %d(%s)\n", host->ecctype, ++ match_ecc_type_to_str(info->ecctype)); ++ ++ mtd->ecc_strength = host->ecctype; ++ ++ return fmc_cfg_ecc_type(match_ecc_type_to_reg(info->ecctype)); ++} ++ ++static unsigned int fmc100_get_page_reg(struct fmc_host* const host, ++ struct nand_config_info* const info) ++{ ++ host->pagesize = match_page_type_to_size(info->pagetype); ++ fmc_pr(BT_DBG, "\t |-Save best PageSize %d(%s)\n", host->pagesize, ++ match_page_type_to_str(info->pagetype)); ++ ++ return fmc_cfg_page_size(match_page_type_to_reg(info->pagetype)); ++} ++ ++static unsigned int fmc100_get_block_reg(struct fmc_host* const host, ++ struct nand_config_info* const info) ++{ ++ unsigned int block_reg = 0; ++ unsigned int page_per_block; ++ struct mtd_info *mtd = host->mtd; ++ ++ host->block_page_mask = ((mtd->erasesize / mtd->writesize) - 1); ++ page_per_block = mtd->erasesize / match_page_type_to_size(info->pagetype); ++ switch (page_per_block) { ++ case _64_PAGES: ++ block_reg = BLOCK_SIZE_64_PAGE; ++ break; ++ case _128_PAGES: ++ block_reg = BLOCK_SIZE_128_PAGE; ++ break; ++ case _256_PAGES: ++ block_reg = BLOCK_SIZE_256_PAGE; ++ break; ++ case _512_PAGES: ++ block_reg = BLOCK_SIZE_512_PAGE; ++ break; ++ default: ++ db_msg("Can't support block %#x and page %#x size\n", ++ mtd->erasesize, mtd->writesize); ++ } ++ ++ return fmc_cfg_block_size(block_reg); ++} ++ ++static void fmc100_set_fmc_cfg_reg(struct fmc_host *host, ++ struct nand_config_info *type_info) ++{ ++ unsigned int page_reg, ecc_reg, block_reg, reg_fmc_cfg; ++ ++ ecc_reg = fmc100_get_ecc_reg(host, type_info); ++ page_reg = fmc100_get_page_reg(host, type_info); ++ block_reg = fmc100_get_block_reg(host, type_info); ++ ++ if (fmc100_nand_support_randomizer(host->pagesize, host->ecctype)) ++ host->flags |= NAND_RANDOMIZER; ++ ++ /* Save value of FMC_CFG and FMC_CFG_ECC0 to turn on/off ECC */ ++ reg_fmc_cfg = fmc_read(host, FMC_CFG); ++ reg_fmc_cfg &= ~(PAGE_SIZE_MASK | ECC_TYPE_MASK | BLOCK_SIZE_MASK); ++ reg_fmc_cfg |= ecc_reg | page_reg | block_reg; ++ host->nand_cfg = reg_fmc_cfg; ++ host->nand_cfg_ecc0 = (host->nand_cfg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT; ++ fmc_pr(BT_DBG, "\t|-Save FMC_CFG[%#x]: %#x and FMC_CFG_ECC0: %#x\n", ++ FMC_CFG, host->nand_cfg, host->nand_cfg_ecc0); ++ ++ /* pass pagesize and ecctype to kernel when spiflash startup. */ ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ ++ /* ++ * If it want to support the 'read retry' feature, the 'randomizer' ++ * feature must be support first. ++ */ ++ host->read_retry = NULL; ++ ++ if (host->read_retry && !is_randomizer(host)) { ++ db_bug(ERSTR_HARDWARE ++ "This Nand flash need to enable 'randomizer' feature. " ++ "Please configure hardware randomizer PIN."); ++ } ++} ++ ++static void fmc100_set_oob_info(struct mtd_info* const mtd, ++ struct nand_config_info* const info) ++{ ++ int buffer_len; ++ long long align_mask; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ if (info->ecctype != NAND_ECC_0BIT) ++ mtd->oobsize = info->oobsize; ++ mtd->oobavail = FMC100_NAND_OOBSIZE_FOR_YAFFS; ++ ++ host->oobsize = mtd->oobsize; ++ ++ if (mtd->writesize > NAND_MAX_PAGESIZE || ++ mtd->oobsize > NAND_MAX_OOBSIZE) { ++ db_bug(ERSTR_DRIVER ++ "Driver does not support this Nand Flash. Please " \ ++ "increase NAND_MAX_PAGESIZE and NAND_MAX_OOBSIZE.\n"); ++ return; ++ } ++ ++ buffer_len = host->pagesize + host->oobsize; ++ ++ host->buforg = kmalloc(buffer_len + FMC_DMA_ALIGN, GFP_KERNEL); ++ if (!host->buforg) { ++ db_bug(ERSTR_DRIVER "Error: Can't malloc memory for fmc100 driver.\n"); ++ return; ++ } ++ ++ /* DMA need 32 bytes alignment */ ++ align_mask = FMC_DMA_ALIGN - 1; ++ host->dma_buffer = ((long)(host->buforg + align_mask)) & ~align_mask; ++ host->buffer = (char *)host->dma_buffer; ++ if (memset_s(host->buffer, buffer_len, 0xff, buffer_len)) ++ db_bug(ERSTR_DRIVER "Error: Can't memset_s. %s %d\n", __func__, __LINE__); ++ host->dma_oob = host->dma_buffer + host->pagesize; ++ ++ chip->ecc.layout = info->layout; ++ ++ host->bbm = (unsigned char *)(host->buffer + host->pagesize + ++ FMC100_BAD_BLOCK_POS); ++ ++ /* EB bits locate in the bottom two of CTRL(30) */ ++ host->epm = (unsigned short *)(host->buffer + host->pagesize + ++ chip->ecc.layout->oobfree[0].offset + EB_NORMAL); ++ ++#ifdef CONFIG_FS_MAY_NOT_YAFFS2 ++ if (info->ecctype == NAND_ECC_16BIT) { ++ if (host->pagesize == _2K) ++ /* EB bits locate in the bottom two of CTRL(4) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize + ++ chip->ecc.layout->oobfree[0].offset + EB_2K_16_BIT); ++ else if (host->pagesize == _4K) ++ /* EB bit locate in the bottom two of CTRL(14) */ ++ host->epm = (u_short *)(host->buffer + host->pagesize + ++ chip->ecc.layout->oobfree[0].offset + EB_4K_16_BIT); ++ } ++#endif ++} ++ ++static int fmc100_set_config_info(struct mtd_info *mtd) ++{ ++ struct nand_config_info *type_info = NULL; ++ struct nand_config_info *config = fmc100_nand_hw_auto_config_table; ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ fmc_pr(BT_DBG, "\t*-Start config Block Page OOB and Ecc\n"); ++ ++ type_info = fmc100_get_config_type_info(config, mtd); ++ if (!type_info) ++ db_bug(ERSTR_DRIVER " the pagesize(%d) and oobsize(%d).\n", ++ mtd->writesize, mtd->oobsize); ++ ++ fmc_pr(BT_DBG, "\t|-PageSize %s EccType %s OobSize %d\n", ++ nand_page_name(type_info->pagetype), ++ nand_ecc_name(type_info->ecctype), type_info->oobsize); ++ ++ /* Set the page_size, ecc_type, block_size of FMC_CFG[0x0] register */ ++ fmc100_set_fmc_cfg_reg(host, type_info); ++ ++ fmc100_set_oob_info(mtd, type_info); ++ ++ if (mtd->writesize != host->pagesize) { ++ unsigned int shift = 0; ++ unsigned int writesize = mtd->writesize; ++ ++ while (writesize > host->pagesize) { ++ writesize >>= 1; ++ shift++; ++ } ++ chip->chipsize = chip->chipsize >> shift; ++ mtd->erasesize = mtd->erasesize >> shift; ++ mtd->writesize = host->pagesize; ++ printf("Nand divide into 1/%d\n", (1 << shift)); ++ } ++ ++ return 0; ++} ++ ++static void read_nand_id_op(struct fmc_host* const host, ++ struct nand_chip* const chip, ++ unsigned int command) ++{ ++ host->offset = 0; ++ host->cmd_op.l_cmd = command & 0xff; ++ if (memset_s((u_char *)(chip->IO_ADDR_R), MAX_NAND_ID_LEN, 0, MAX_NAND_ID_LEN)) ++ printf("ERR:memset_s fail %s %d\n", __func__, __LINE__); ++ ++ host->cmd_op.data_no = MAX_NAND_ID_LEN; ++ host->send_cmd_readid(host); ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++} ++ ++static void fmc100_cmdfunc(struct mtd_info *mtd, unsigned int command, ++ int column, int page_addr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct fmc_host *host = chip->priv; ++ ++ host->cmd_op.cmd = command & 0xff; ++ ++ switch (command) { ++ case NAND_CMD_READOOB: ++ host->offset = 0; /* same command as read */ ++ case NAND_CMD_READ0: ++ host->cmd_op.l_cmd = command & 0xff; ++ /* get addr low 16 bits */ ++ host->addr_value[0] = page_addr << 16; ++ /* get addr hight 16 bits */ ++ host->addr_value[1] = page_addr >> 16; ++ host->send_cmd_readstart(host); ++ break; ++ ++ case NAND_CMD_ERASE1: ++ host->cmd_op.l_cmd = command & 0xff; ++ host->addr_cycle = 3; /* block addr have 3*8 bits */ ++ host->addr_value[0] = page_addr; ++ host->send_cmd_erase(host); ++ break; ++ ++ case NAND_CMD_READSTART: ++ case NAND_CMD_ERASE2: ++ break; ++ ++ case NAND_CMD_SEQIN: ++ host->addr_value[0] = page_addr << 16; /* get addr low 16 bits */ ++ host->addr_value[1] = page_addr >> 16; /* get addr hight 16 bits */ ++ break; ++ ++ case NAND_CMD_PAGEPROG: ++ host->offset = 0; ++ host->send_cmd_pageprog(host); ++ break; ++ ++ case NAND_CMD_STATUS: ++ host->send_cmd_status(host); ++ host->enable_ecc_randomizer(host, ENABLE, ENABLE); ++ break; ++ ++ case NAND_CMD_READID: ++ read_nand_id_op(host, chip, command); ++ break; ++ ++ case NAND_CMD_RESET: ++ host->cmd_op.l_cmd = command & 0xff; ++ host->send_cmd_reset(host); ++ chip->dev_ready(mtd); ++ break; ++ ++ default: ++#ifdef CONFIG_FMC_DEBUG ++ printf("%s not support command 0x%08x:\n", mtd->name, command); ++#endif ++ break; ++ } ++} ++ ++static void fmc100_chip_init(struct nand_chip* const chip) ++{ ++ if (memset_s((char *)chip->IO_ADDR_R, NAND_BUFFER_LEN, 0xff, NAND_BUFFER_LEN)) ++ printf("ERR:memcpy_s fail %s %d", __func__, __LINE__); ++ ++ chip->read_byte = fmc100_read_byte; ++ chip->read_word = fmc100_read_word; ++ chip->write_buf = fmc100_write_buf; ++ chip->read_buf = fmc100_read_buf; ++ ++ chip->select_chip = fmc100_select_chip; ++ ++ chip->cmdfunc = fmc100_cmdfunc; ++ chip->dev_ready = fmc100_dev_ready; ++ ++ chip->chip_delay = FMC_CHIP_DELAY; ++ ++ chip->options = NAND_BBT_SCANNED | NAND_BROKEN_XD; ++ ++ chip->ecc.layout = NULL; ++ chip->ecc.mode = NAND_ECC_NONE; ++} ++ ++static void fmc100_host_hook(struct fmc_host *host) ++{ ++ unsigned int reg; ++ ++ host->addr_cycle = 0; ++ host->addr_value[0] = 0; ++ host->addr_value[1] = 0; ++ host->cache_addr_value[0] = ~0; ++ host->cache_addr_value[1] = ~0; ++ host->flags |= NAND_HW_AUTO; ++ ++ host->send_cmd_pageprog = fmc100_send_cmd_write; ++ host->send_cmd_status = fmc100_send_cmd_status; ++ host->send_cmd_readstart = fmc100_send_cmd_read; ++ host->send_cmd_erase = fmc100_send_cmd_erase; ++ host->send_cmd_readid = fmc100_send_cmd_readid; ++ host->send_cmd_reset = fmc100_send_cmd_reset; ++ ++ /* ++ * check if start from nand. ++ * This register REG_SYSSTAT is set in start.S ++ * When start in NAND (Auto), the ECC/PAGESIZE driver don't detect. ++ */ ++ host->flags |= NAND_HW_AUTO; ++ reg = readl(SYS_CTRL_REG_BASE + REG_SYSSTAT); ++ fmc_pr(BT_DBG, "\t |-Read SYSTEM STATUS CFG[%#x]%#x\n", REG_SYSSTAT, ++ reg); ++ if (get_sys_boot_mode(reg) == BOOT_FROM_NAND) { ++ host->flags |= NAND_CONFIG_DONE; ++ fmc_pr(BT_DBG, "\t |-Auto config pagesize and ecctype\n"); ++ } ++ ++ host->enable_ecc_randomizer = fmc100_ecc_randomizer; ++} ++ ++static int fmc100_host_init(struct fmc_host *host) ++{ ++ unsigned int reg; ++ unsigned int flash_type; ++ ++ fmc_pr(BT_DBG, "\t *-Start nand host init\n"); ++ ++ host->regbase = (void __iomem *)CONFIG_FMC_REG_BASE; ++ ++ reg = fmc_read(host, FMC_CFG); ++ fmc_pr(BT_DBG, "\t |-Read FMC CFG[%#x]%#x\n", FMC_CFG, reg); ++ flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT; ++ if (flash_type != FLASH_TYPE_NAND) { ++ db_msg("Error: Flash type isn't Nand flash. reg[%#x]\n", reg); ++ reg |= fmc_cfg_flash_sel(FLASH_TYPE_NAND); ++ fmc_pr(BT_DBG, "\t |-Change flash type to Nand flash\n"); ++ } ++ ++ if ((reg & OP_MODE_MASK) == OP_MODE_BOOT) { ++ reg |= fmc_cfg_op_mode(OP_MODE_NORMAL); ++ fmc_pr(BT_DBG, "\t |-Controller enter normal mode\n"); ++ } ++ fmc_write(host, FMC_CFG, reg); ++ fmc_pr(BT_DBG, "\t |-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ ++ host->nand_cfg = reg; ++ host->nand_cfg_ecc0 = (reg & ~ECC_TYPE_MASK) | ECC_TYPE_0BIT; ++ ++ reg = fmc_read(host, FMC_GLOBAL_CFG); ++ fmc_pr(BT_DBG, "\t |-Read global CFG[%#x]%#x\n", FMC_GLOBAL_CFG, reg); ++ if (reg & FMC_GLOBAL_CFG_RANDOMIZER_EN) { ++ host->flags &= ~NAND_RANDOMIZER; ++ fmc_pr(BT_DBG, "\t |-Default disable randomizer\n"); ++ reg &= ~FMC_GLOBAL_CFG_RANDOMIZER_EN; ++ fmc_write(host, FMC_GLOBAL_CFG, reg); ++ fmc_pr(BT_DBG, "\t |-Set global CFG[%#x]%#x\n", FMC_GLOBAL_CFG, ++ reg); ++ } ++ ++#ifdef CONFIG_NAND_EDO_MODE ++ /* enable EDO node */ ++ reg = fmc_read(host, FMC_GLOBAL_CFG); ++ fmc_write(host, FMC_GLOBAL_CFG, set_nand_edo_mode_en(reg)); ++#endif ++ /* ecc0_flag for ecc0 read/write */ ++ ecc0_flag = 0; ++ fmc100_host_hook(host); ++ ++ fmc_pr(BT_DBG, "\t *-End nand host init\n"); ++ ++ return 0; ++} ++ ++int board_nand_init(struct nand_chip *chip) ++{ ++ struct fmc_host *host = &fmc_host; ++ ++ /* boot flash type check */ ++ if (get_boot_media() != BOOT_MEDIA_NAND) { ++ printf("Error: Boot flash type isn't Nand flash.\n"); ++ return -ENODEV; ++ } ++ ++ /* spi nand is initialized */ ++ if (host->version == FMC_VER_100) { ++ printf("Host was initialized.\n"); ++ return 0; ++ } ++ ++ /* FMC ip version check */ ++ if (fmc_ip_ver_check()) ++ db_bug("Error: fmc IP version unknown!\n"); ++ ++ /* fmc host init */ ++ if (memset_s((char *)host, sizeof(struct fmc_host), 0, sizeof(struct fmc_host))) ++ db_msg("Error: Nand host memset_s failed!\n"); ++ if (fmc100_host_init(host)) { ++ db_msg("Error: Nand host init failed!\n"); ++ return -EFAULT; ++ } ++ host->version = readl(CONFIG_FMC_REG_BASE + FMC_VERSION); ++ host->chip = chip; ++ ++ fmc_write(host, FMC_PND_PWIDTH_CFG, pwidth_cfg_rw_hcnt(RW_H_WIDTH) | ++ pwidth_cfg_r_lcnt(R_L_WIDTH) | ++ pwidth_cfg_w_lcnt(W_L_WIDTH)); ++ ++ /* enable system clock */ ++ fmc100_nand_controller_enable(ENABLE); ++ ++ chip->priv = host; ++ fmc100_chip_init(chip); ++ ++ fmc_spl_ids_register(); ++ nand_oob_resize = fmc100_set_config_info; ++ ++ return 0; ++} ++ ++void *fmc100_nand_get_host(void) ++{ ++ return ((fmc_host.chip) ? (void *)&fmc_host : NULL); ++} ++ ++static int fmc100_nand_get_ecctype(void) ++{ ++ if (!fmc_host.chip) { ++ printf("Nand flash uninitialized.\n"); ++ return -1; ++ } ++ ++ return match_ecc_type_to_yaffs(fmc_host.ecctype); ++} ++ ++int nand_get_ecctype(void) ++{ ++ return fmc100_nand_get_ecctype(); ++} ++ ++int fmc100_nand_get_rr_param(char* const param) ++{ ++ int ret = 0; ++ int retlen; ++ ++ return ret; ++} +diff --git a/drivers/mtd/nand/raw/fmc100_nand/fmc100_nand.h b/drivers/mtd/nand/raw/fmc100_nand/fmc100_nand.h +new file mode 100644 +index 0000000..0c933e7 +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100_nand/fmc100_nand.h +@@ -0,0 +1,118 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __FMC100_NAND_H__ ++#define __FMC100_NAND_H__ ++ ++#include ++#include ++#include ++#include "securec.h" ++/* These macroes are for debug only, reg option is slower then dma option */ ++#undef FMC100_NAND_SUPPORT_REG_READ ++#undef FMC100_NAND_SUPPORT_REG_WRITE ++/* open CONFIG_FS_MAY_NOT_YAFFS2 as you need, just use for nand */ ++#undef CONFIG_FS_MAY_NOT_YAFFS2 ++#define FMC100_NAND_OOBSIZE_FOR_YAFFS 32 ++ ++#define REG_CNT_HIGH_BLOCK_NUM_SHIFT 10 ++ ++#define REG_CNT_BLOCK_NUM_MASK 0x3ff ++#define REG_CNT_BLOCK_NUM_SHIFT 22 ++ ++#define REG_CNT_PAGE_NUM_MASK 0x3f ++#define REG_CNT_PAGE_NUM_SHIFT 16 ++ ++#define REG_CNT_WRAP_MASK 0xf ++#define REG_CNT_WRAP_SHIFT 12 ++ ++#define NAND_BUFFER_LEN (2048 + 128) ++ ++#define FMC100_ADDR_CYCLE_MASK 0x4 ++ ++struct fmc_host { ++ struct nand_chip *chip; ++ struct mtd_info *mtd; ++ ++ struct fmc_cmd_op cmd_op; ++ void __iomem *regbase; ++ ++ /* Controller config option nand flash */ ++ unsigned int nand_cfg; ++ unsigned int nand_cfg_ecc0; ++ ++ unsigned int offset; ++ /* This is maybe an un-aligment address, only for malloc or free */ ++ char *buforg; ++ char *buffer; ++ ++ unsigned long dma_buffer; ++ unsigned long dma_oob; ++ ++ unsigned int addr_cycle; ++ unsigned int addr_value[2]; /* 2 addr */ ++ unsigned int cache_addr_value[2]; /* 2 addr */ ++ ++ unsigned int column; ++ unsigned int block_page_mask; ++ ++ unsigned int ecctype; ++ unsigned int pagesize; ++ unsigned int oobsize; ++ ++ int need_rr_data; ++#define FMC100_READ_RETRY_DATA_LEN 128 ++ char rr_data[FMC100_READ_RETRY_DATA_LEN]; ++ struct read_retry_t *read_retry; ++ ++ int version; ++ ++ /* BOOTROM read two bytes to detect the bad block flag */ ++#define FMC100_BAD_BLOCK_POS 0 ++ unsigned char *bbm; /* nand bad block mark */ ++ unsigned short *epm; /* nand empty page mark */ ++ unsigned int flags; ++ ++#define FMC100_PS_UC_ECC 0x01 /* page has ecc error */ ++#define FMC100_PS_BAD_BLOCK 0x02 /* bad block */ ++#define FMC100_PS_EMPTY_PAGE 0x04 /* page is empty */ ++#define FMC100_PS_EPM_ERROR 0x0100 /* empty page mark word has error. */ ++#define FMC100_PS_BBM_ERROR 0x0200 /* bad block mark word has error. */ ++ unsigned int page_status; ++ ++ void (*send_cmd_pageprog)(struct fmc_host *host); ++ void (*send_cmd_status)(struct fmc_host *host); ++ void (*send_cmd_readstart)(struct fmc_host *host); ++ void (*send_cmd_erase)(struct fmc_host *host); ++ void (*send_cmd_readid)(struct fmc_host *host); ++ void (*send_cmd_reset)(struct fmc_host *host); ++ ++ void (*enable_ecc_randomizer)(struct fmc_host *host, ++ int ecc_en, int randomizer_en); ++ ++ void (*detect_ecc)(struct fmc_host *host); ++}; ++ ++int fmc100_nand_init(struct nand_chip *chip); ++ ++void fmc_spl_ids_register(void); ++ ++void fmc100_nand_controller_enable(int enable); ++ ++#endif /* End of__FMC100_NAND_H__ */ +diff --git a/drivers/mtd/nand/raw/fmc100_nand/fmc_nand_spl_ids.c b/drivers/mtd/nand/raw/fmc100_nand/fmc_nand_spl_ids.c +new file mode 100644 +index 0000000..aacdf5c +--- /dev/null ++++ b/drivers/mtd/nand/raw/fmc100_nand/fmc_nand_spl_ids.c +@@ -0,0 +1,176 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "fmc100_nand.h" ++ ++#define _768K (_256K + _512K) ++ ++struct nand_flash_special_dev { ++ unsigned char id[_8B]; ++ int length; /* length of id. */ ++ unsigned long long chipsize; ++ struct nand_flash_dev *(*probe)(struct nand_flash_dev_ex *nand_dev); ++ char *name; ++ ++ unsigned long pagesize; ++ unsigned long erasesize; ++ unsigned long oobsize; ++ unsigned long options; ++ unsigned int read_retry_type; ++ ++#define BBP_LAST_PAGE 0x01 ++#define BBP_FIRST_PAGE 0x02 ++ unsigned int badblock_pos; ++ int flags; ++}; ++ ++/* ++ * this is nand probe function. ++ */ ++static struct nand_flash_dev *hynix_probe_v02( ++ struct nand_flash_dev_ex *nand_dev) ++{ ++ unsigned char *id = nand_dev->ids; ++ struct nand_flash_dev *type = &nand_dev->flash_dev; ++ ++ int pagesizes[] = {_2K, _4K, _8K, 0}; ++ int oobsizes[] = {128, 224, 448, 0, 0, 0, 0, 0}; ++ int blocksizes[] = {_128K, _256K, _512K, _768K, _1M, _2M, 0, 0}; ++ ++ /* get blocktype from 3th id's bit 4 and bit 5 */ ++ int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03)); ++ /* get oobtype from 3th id's bit 2 and bit 4 */ ++ int oobtype = (((id[3] >> 2) & 0x03) | ((id[3] >> 4) & 0x04)); ++ ++ type->options = 0; ++ /* get pagesize from 3th id's bit 0 and bit 1 */ ++ type->pagesize = pagesizes[(id[3] & 0x03)]; ++ type->erasesize = blocksizes[blocktype]; ++ nand_dev->oobsize = oobsizes[oobtype]; ++ ++ return type; ++} ++ ++static struct nand_flash_dev *samsung_probe_v02( ++ struct nand_flash_dev_ex *nand_dev) ++{ ++ unsigned char *id = nand_dev->ids; ++ struct nand_flash_dev *type = &nand_dev->flash_dev; ++ ++ int pagesizes[] = {_2K, _4K, _8K, 0}; ++ int oobsizes[] = {0, 128, 218, 400, 436, 0, 0, 0}; ++ int blocksizes[] = {_128K, _256K, _512K, _1M, 0, 0, 0, 0}; ++ ++ /* get blocktype from 3th id's bit 4 and bit 5 */ ++ int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03)); ++ /* get oobtype from 3th id's bit 2 and bit 4 */ ++ int oobtype = (((id[3] >> 4) & 0x04) | ((id[3] >> 2) & 0x03)); ++ ++ type->options = 0; ++ /* get pagesize from 3th id's bit 0 and bit 1 */ ++ type->pagesize = pagesizes[(id[3] & 0x03)]; ++ type->erasesize = blocksizes[blocktype]; ++ nand_dev->oobsize = oobsizes[oobtype]; ++ ++ return type; ++} ++ ++#define DRV_VERSION "1.40" ++ ++/****************************************************************************** ++ * We do not guarantee the compatibility of the following device models in the ++ * table.Device compatibility is based solely on the list of compatible devices ++ * in the release package. ++ ******************************************************************************/ ++ ++static struct nand_flash_special_dev nand_flash_special_table[] = { ++ ++ {{0}, 0, 0, 0, 0, 0, 0, 0, 0}, ++}; ++ ++struct nand_flash_dev_ex g_nand_dev; ++ ++struct nand_flash_dev *fmc_get_spl_flash_type(struct mtd_info *mtd, ++ struct nand_chip *chip, unsigned char *id) ++{ ++ struct nand_flash_special_dev *spl_dev = nand_flash_special_table; ++ struct nand_flash_dev *type = &g_nand_dev.flash_dev; ++ struct nand_flash_dev_ex *nand_dev = &g_nand_dev; ++ ++ if (!mtd || !chip || !id) { ++ printf("err:mtd or chip or id is null\n"); ++ return NULL; ++ } ++ fmc_pr(BT_DBG, "\t *-Start find special nand flash\n"); ++ ++ printf("Nand ID: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X " \ ++ "0x%02X\n", id[0], id[1], id[2], id[3], id[4], \ ++ id[5], id[6], id[7]); ++ ++ for (; spl_dev->length; spl_dev++) { ++ if (memcmp(id, spl_dev->id, spl_dev->length)) ++ continue; ++ ++ fmc_pr(BT_DBG, "\t |-Found special Nand flash: %s\n", ++ spl_dev->name); ++ ++ if (spl_dev->probe) { ++ type = spl_dev->probe((struct nand_flash_dev_ex *)id); ++ } else { ++ type->options = spl_dev->options; ++ type->pagesize = spl_dev->pagesize; ++ type->erasesize = spl_dev->erasesize; ++ type->oobsize = spl_dev->oobsize; ++ } ++ ++ type->name = spl_dev->name; ++ type->id_len = spl_dev->length; ++ if (memcpy_s(type->id, NAND_MAX_ID_LEN, id, type->id_len)) ++ return NULL; ++ type->chipsize = (unsigned long)(byte_to_mb(spl_dev->chipsize)); ++ fmc_pr(BT_DBG, "\t |-Save struct nand_flash_dev info\n"); ++ ++ if (memcpy_s(nand_dev->ids, NAND_MAX_ID_LEN, id, MAX_NAND_ID_LEN)) ++ return NULL; ++ nand_dev->oobsize = type->oobsize; ++ nand_dev->flags = spl_dev->flags; ++ nand_dev->read_retry_type = spl_dev->read_retry_type; ++ fmc_pr(BT_DBG, "\t |-Save struct nand_dev_t information\n"); ++ ++ mtd->size = spl_dev->chipsize; ++ ++ return type; ++ } ++ nand_dev->read_retry_type = NAND_RR_NONE; ++ ++ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); ++ chip->read_byte(mtd); ++ chip->read_byte(mtd); ++ ++ fmc_pr(BT_DBG, "\t *-Not found special nand flash\n"); ++ ++ return NULL; ++} ++ ++void fmc_spl_ids_register(void) ++{ ++ printf("Special NAND id table Version %s\n", DRV_VERSION); ++ get_flash_type = fmc_get_spl_flash_type; ++} +diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c +index 026419e..b57ee52 100644 +--- a/drivers/mtd/nand/raw/nand.c ++++ b/drivers/mtd/nand/raw/nand.c +@@ -16,7 +16,7 @@ + + int nand_curr_device = -1; + +-static struct mtd_info *nand_info[CONFIG_SYS_MAX_NAND_DEVICE]; ++struct mtd_info *nand_info[CONFIG_SYS_MAX_NAND_DEVICE]; + + #ifndef CONFIG_SYS_NAND_SELF_INIT + static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; +diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c +index aba8ac0..db2c4b3 100644 +--- a/drivers/mtd/nand/raw/nand_base.c ++++ b/drivers/mtd/nand/raw/nand_base.c +@@ -45,6 +45,7 @@ + #endif + #include + #include ++#include + + /* Define default oob placement schemes for large and small page devices */ + #ifndef CONFIG_SYS_NAND_DRIVER_ECC_LAYOUT +@@ -1435,8 +1436,13 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) + if (status < 0) + return status; + +- if (status & NAND_STATUS_FAIL) ++ if (status & NAND_STATUS_FAIL) { ++#if defined(CONFIG_FMC_SPI_NAND) || defined(CONFIG_FMC_NAND) ++ printf("\nmakbad erase fail blk: 0x%x\n", page * mtd->writesize); ++ chip->block_markbad(mtd, page * mtd->writesize); ++#endif + return -EIO; ++ } + + return 0; + } +@@ -3203,6 +3209,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + int ret; + int oob_required = oob ? 1 : 0; + ++#if defined(CONFIG_FMC_SPI_NAND)|| defined(CONFIG_FMC_NAND) ++ oob_required = 1; ++#endif ++ + ops->retlen = 0; + if (!writelen) + return 0; +@@ -4388,6 +4398,13 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, + return false; + } + ++/* The info for burn */ ++static struct mtd_info_ex nand_info_ex = {.type = 0, }; ++ ++struct mtd_info_ex *get_nand_info(void) ++{ ++ return &nand_info_ex; ++} + /* + * Get the flash and manufacturer id and lookup if the type is supported. + */ +@@ -4438,6 +4455,10 @@ struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + return ERR_PTR(-ENODEV); + } + ++#ifdef CONFIG_FMC ++ if (get_flash_type) ++ type = get_flash_type(mtd, chip, id_data); ++#endif + if (!type) + type = nand_flash_ids; + +@@ -4485,6 +4506,11 @@ struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + ident_done: ++#ifdef CONFIG_FMC ++ if (nand_oob_resize ++ && nand_oob_resize(mtd)) ++ return ERR_PTR(-ENODEV); ++#endif + + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { +@@ -4510,6 +4536,30 @@ ident_done: + return ERR_PTR(-EINVAL); + } + ++ if (nand_info_ex.type == 0) { ++ memset(&nand_info_ex, 0, sizeof(struct mtd_info_ex)); ++ ++ nand_info_ex.type = MTD_NANDFLASH; ++ nand_info_ex.chipsize = chip->chipsize; ++ nand_info_ex.erasesize = mtd->erasesize; ++ nand_info_ex.pagesize = mtd->writesize; ++ /* smaller than nand chip space area */ ++ nand_info_ex.oobsize = mtd->oobsize; ++ ++ nand_info_ex.ecctype = mtd->ecc_strength; ++ ++ nand_info_ex.id_length = type->id_len; ++ nand_info_ex.numchips = 1; ++ ++ memcpy(nand_info_ex.ids, id_data, ++ nand_info_ex.id_length); ++ ++ strncpy(nand_info_ex.name, mtd->name, ++ sizeof(nand_info_ex.name)); ++ ++ nand_info_ex.name[sizeof(nand_info_ex.name)-1] = '\0'; ++ } ++ + nand_decode_bbm_options(mtd, chip, id_data); + + /* Calculate the address shift from the page size */ +@@ -4715,7 +4765,19 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, + /* Store the number of chips and calc total size for mtd */ + chip->numchips = i; + mtd->size = i * chip->chipsize; +- ++ ++ if (nand_info_ex.type != MTD_NANDFLASH) ++ BUG(); ++ ++ nand_info_ex.numchips = chip->numchips; ++ ++ printf("Block:%sB ", ultohstr(mtd->erasesize)); ++ printf("Page:%sB ", ultohstr(mtd->writesize)); ++ printf("OOB:%sB ", ultohstr(mtd->oobsize)); ++ printf("ECC:%s ", nand_ecc_name(nand_info_ex.ecctype)); ++ printf("\n"); ++ printf("Chipsize:"); ++ + return 0; + } + EXPORT_SYMBOL(nand_scan_ident); +diff --git a/drivers/mtd/nand/raw/nand_util.c b/drivers/mtd/nand/raw/nand_util.c +index fc2235c..5677129 100644 +--- a/drivers/mtd/nand/raw/nand_util.c ++++ b/drivers/mtd/nand/raw/nand_util.c +@@ -33,6 +33,9 @@ + typedef struct erase_info erase_info_t; + typedef struct mtd_info mtd_info_t; + ++/* for ecc0 read and write */ ++unsigned int ecc0_flag; ++ + /* support only for native endian JFFS2 */ + #define cpu_to_je16(x) (x) + #define cpu_to_je32(x) (x) +@@ -601,6 +604,10 @@ int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, + + need_skip = check_skip_len(mtd, offset, *length, &used_for_write); + ++ /* add to cooperate with tool */ ++ print_to_tool("pure data length is %d, len_incl_bad is %d\r\n", ++ (u_int)*length, (u_int)used_for_write); ++ + if (actual) + *actual = used_for_write; + +@@ -781,6 +788,308 @@ int nand_read_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, + return 0; + } + ++/** ++ * nand_write_yaffs_skip_bad: ++ * ++ * Write image to NAND flash. ++ * Blocks that are marked bad are skipped and the is written to the next ++ * block instead as long as the image is short enough to fit even after ++ * skipping the bad blocks. ++ * ++ * @param nand NAND device ++ * @param offset offset in flash ++ * @param length buffer length ++ * @param buf buffer to read from ++ * @return 0 in case of success ++ */ ++int nand_write_yaffs_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, ++ u_char *buffer) ++{ ++ int rval; ++ size_t left_to_write; ++ size_t len_incl_bad = 0; ++ u_char *p_buffer = buffer; ++ mtd_oob_ops_t ops = {0}; ++ int i; ++ size_t length_data; /* length without oob */ ++ u_int32_t oobsize; ++ int need_skip; ++ ++ oobsize = mtd->oobsize; ++ /* Reject writes, which are not page aligned */ ++ if (((unsigned long long)offset & (mtd->writesize - 1)) != 0) { ++ printf("Attempt to write non page aligned data, offset %#x\n", ++ (u_int)offset); ++ return -EINVAL; ++ } ++ if ((*length % (mtd->writesize + oobsize)) != 0) { ++ printf("Attempt to write non page aligned data, length %#x\n", ++ (u_int)*length); ++ printf("Page size %#x, OOB size %d\n", (u_int)mtd->writesize, ++ oobsize); ++ return -EINVAL; ++ } ++ ++ length_data = (*length / (mtd->writesize + oobsize)) ++ * mtd->writesize; ++ ++ need_skip = check_skip_len(mtd, offset, length_data, &len_incl_bad); ++ if (need_skip < 0) { ++ printf("Attempt to read outside the flash area\n"); ++ *length = 0; ++ return -EINVAL; ++ } ++ ++ if (len_incl_bad != length_data) ++ printf("data length:%#x, include bad block length: %#x\n", ++ (u_int)length_data, (u_int)len_incl_bad); ++ ++ if ((offset + len_incl_bad) >= mtd->size) { ++ printf("Attempt to write outside the flash area\n"); ++ return -EINVAL; ++ } ++ ++ /* add to cooperate with tool */ ++ print_to_tool("pure data length is %d, len_incl_bad is %d\r\n", ++ length_data, len_incl_bad); ++ ++ if (len_incl_bad == length_data) { ++ for (i = 0; i < length_data / mtd->writesize; i++) { ++ ops.datbuf = buffer ++ + i * (mtd->writesize + oobsize); ++ ops.oobbuf = buffer ++ + i * (mtd->writesize + oobsize) ++ + mtd->writesize; ++ ops.len = mtd->writesize; ++ ops.ooblen = oobsize; ++ ops.mode = MTD_OPS_RAW; ++ ++ rval = mtd_write_oob(mtd, offset ++ + i * mtd->writesize, &ops); ++ if (rval != 0) { ++ printf("NAND write to offset %llx failed %d\n", ++ offset + i*mtd->writesize, rval); ++ return rval; ++ } ++ } ++ ++ printf("NAND write yaffs finished\n"); ++ return 0; ++ } ++ ++ left_to_write = length_data; ++ ++ while (left_to_write > 0) { ++ size_t block_offset = (unsigned long long)offset & (mtd->erasesize - 1); ++ size_t write_size; ++ ++ if (nand_block_isbad(mtd, (unsigned long long)offset ++ & ~((loff_t)mtd->erasesize - 1))) { ++ printf("Skip bad block 0x%08llx\n", ++ (unsigned long long)offset & ~((loff_t)mtd->erasesize - 1)); ++ offset += mtd->erasesize - block_offset; ++ continue; ++ } ++ ++ if (left_to_write < (mtd->erasesize - block_offset)) ++ write_size = left_to_write; ++ else ++ write_size = mtd->erasesize - block_offset; ++ ++ for (i = 0; i < write_size / mtd->writesize; i++) { ++ ops.datbuf = p_buffer; ++ ops.oobbuf = p_buffer + mtd->writesize; ++ ops.len = mtd->writesize; ++ ops.ooblen = oobsize; ++ ops.ooboffs = 0; ++ ops.mode = MTD_OPS_RAW; ++ ++ rval = mtd_write_oob(mtd, offset, &ops); ++ if (rval != 0) { ++ printf("NAND write to offset %llx failed %d\n", ++ offset, rval); ++ *length -= left_to_write; ++ return rval; ++ } ++ ++ p_buffer += mtd->writesize + oobsize; ++ left_to_write -= mtd->writesize; ++ offset += mtd->writesize; ++ } ++ } ++ ++ printf("\n"); ++ return 0; ++} ++ ++ ++/** ++ * nand_fill_ecc - [Internal] Process oob to client buffer ++ * @chip: nand chip structure ++ * @oob: oob destination address ++ * @len: size of oob to process ++ */ ++void nand_fill_ecc(struct nand_chip *chip, uint8_t *oob, size_t len) ++{ ++ struct nand_oobfree *free = chip->ecc.layout->oobfree; ++ uint32_t offset = 0, bytes; ++ ++ for (; free->length && offset < len; free++) { ++ if (offset < free->offset) { ++ bytes = min_t(size_t, len, free->offset) - offset; ++ memset(oob + offset, 0xff, bytes); ++ } ++ ++ offset = free->offset + free->length; ++ } ++ ++ if (offset < len) ++ memset(oob + offset, 0xff, len - offset); ++} ++ ++/** ++ * nand_read_yaffs_skip_bad: ++ * Read image from NAND flash. ++ * Blocks that are marked bad are skipped and the next block is readen ++ * instead as long as the image is short enough to fit even after skipping the ++ * bad blocks. ++ * ++ * @param nand NAND device ++ * @param offset offset in flash, alignment with pagesize ++ * @param length buffer length, on return holds remaining bytes to read, ++ * alignment with (pagesize + oobsize) ++ * @param buffer buffer to read to ++ * @return 0 in case of success ++ */ ++int nand_read_yaffs_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, ++ u_char *buffer) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ u_int32_t oobsize; ++ size_t left_to_read; ++ size_t len_incl_bad = 0; ++ size_t length_data; /* length without oob */ ++ u_char *p_buffer = buffer; ++ mtd_oob_ops_t ops = {0}; ++ int need_skip; ++ int rval; ++ int i; ++ ++ oobsize = mtd->oobsize; ++ ++ /* Reject reads, which are not page aligned */ ++ if (((unsigned long long)offset & (mtd->writesize - 1)) != 0) { ++ printf("Attempt to read non page aligned data, offset %lld\n", ++ offset); ++ return -EINVAL; ++ } ++ ++ if ((*length % (mtd->writesize + oobsize)) != 0) { ++ printf("Attempt to read non aligned data, read length should "\ ++ "be aligned with (pagesize + oobsize), length:%lu " ++ "pagesize:%d oobsize:%d\n", ++ (unsigned long)*length, mtd->writesize, oobsize); ++ return -EINVAL; ++ } ++ ++ length_data = *length / (mtd->writesize + oobsize) * mtd->writesize; ++ ++ need_skip = check_skip_len(mtd, offset, length_data, &len_incl_bad); ++ if (need_skip < 0) { ++ printf("Attempt to read outside the flash area\n"); ++ *length = 0; ++ return -EINVAL; ++ } ++ ++ if (len_incl_bad != length_data) ++ printf("data length:%#x, include bad block length: %#x\n", ++ (u_int)length_data, (u_int)len_incl_bad); ++ ++ if ((offset + len_incl_bad) >= mtd->size) { ++ printf("Attempt to read outside the flash area\n"); ++ return -EINVAL; ++ } ++ ++ /* add to cooperate with tool */ ++ print_to_tool("pure data length is %d, len_incl_bad is %d\r\n", ++ length_data, len_incl_bad); ++ ++ if (len_incl_bad == length_data) { ++ for (i = 0; i < length_data / mtd->writesize; i++) { ++ ops.datbuf = buffer + ++ i*(mtd->writesize + oobsize); ++ ops.oobbuf = buffer + ++ i*(mtd->writesize + oobsize) + ++ mtd->writesize; ++ ops.len = mtd->writesize; ++ ops.ooblen = oobsize; ++ ops.mode = MTD_OPS_RAW; ++ ++ rval = mtd_read_oob(mtd, offset + i*mtd->writesize,&ops); ++ if (rval != 0) { ++ printf("NAND read to offset %llx failed %d\n", ++ offset + i*mtd->writesize, rval); ++ return rval; ++ } ++ ++ if (ecc0_flag != 1) ++ nand_fill_ecc(chip, ops.oobbuf, ops.ooblen); ++ ++ *length -= mtd->writesize + oobsize; ++ } ++ ++ ++ return 0; ++ } ++ ++ left_to_read = length_data; ++ ++ while (left_to_read > 0) { ++ size_t block_offset = (unsigned long long)offset & (mtd->erasesize - 1); ++ size_t read_size; ++ ++ if (nand_block_isbad(mtd, (unsigned long long)offset & ++ ~((loff_t)mtd->erasesize - 1))) { ++ printf("Skip bad block 0x%08llx\n", ++ (unsigned long long)offset & ~((loff_t)mtd->erasesize - 1)); ++ offset += mtd->erasesize - block_offset; ++ continue; ++ } ++ ++ if (left_to_read < (mtd->erasesize - block_offset)) ++ read_size = left_to_read; ++ else ++ read_size = mtd->erasesize - block_offset; ++ ++ for (i = 0; i < read_size / mtd->writesize; i++) { ++ ops.datbuf = p_buffer; ++ ops.oobbuf = p_buffer + mtd->writesize; ++ ops.len = mtd->writesize; ++ ops.ooblen = oobsize; ++ ops.ooboffs = 0; ++ ops.mode = MTD_OPS_RAW; ++ ++ rval = mtd_read_oob(mtd, offset, &ops); ++ if (rval != 0) { ++ printf("NAND read to offset %llx failed %d\n", ++ offset, rval); ++ return rval; ++ } ++ ++ if (ecc0_flag != 1) ++ nand_fill_ecc(chip, ops.oobbuf, ops.ooblen); ++ ++ p_buffer += mtd->writesize + oobsize; ++ left_to_read -= mtd->writesize; ++ offset += mtd->writesize; ++ *length -= mtd->writesize + oobsize; ++ } ++ } ++ ++ printf("\n"); ++ return 0; ++} ++ + #ifdef CONFIG_CMD_NAND_TORTURE + + /** +diff --git a/drivers/mtd/nand/raw/nfc_common.c b/drivers/mtd/nand/raw/nfc_common.c +new file mode 100644 +index 0000000..3651d81 +--- /dev/null ++++ b/drivers/mtd/nand/raw/nfc_common.c +@@ -0,0 +1,176 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++ ++struct nand_flash_dev *(*get_flash_type)( ++ struct mtd_info *mtd, ++ struct nand_chip *chip, ++ unsigned char *id) = NULL; ++ ++int (*nand_oob_resize)(struct mtd_info *mtd) = NULL; ++ ++#if defined(CONFIG_NAND_FLASH_SNFC100) || \ ++ defined(CONFIG_NAND_FLASH_NFC610) || \ ++ defined(CONFIG_FMC_SPI_NAND) || \ ++ defined(CONFIG_FMC_NAND) ++static struct match_type_str ecc2name[] = { ++ {NAND_ECC_0BIT, "none" }, ++ {NAND_ECC_8BIT, "4bit/512" }, ++ {NAND_ECC_16BIT, "8bit/512" }, ++ {NAND_ECC_24BIT, "24bit/1K" }, ++ {NAND_ECC_28BIT, "28bit/1K" }, ++ {NAND_ECC_40BIT, "40bit/1K" }, ++ {NAND_ECC_42BIT, "42bit/1K" }, ++ {NAND_ECC_64BIT, "64bit/1K" }, ++}; ++ ++const char *nand_ecc_name(int type) ++{ ++ return type2str(ecc2name, ARRAY_SIZE(ecc2name), type, "unknown"); ++} ++ ++static struct match_type_str page2name[] = { ++ { NAND_PAGE_512B, "512" }, ++ { NAND_PAGE_2K, "2K" }, ++ { NAND_PAGE_4K, "4K" }, ++ { NAND_PAGE_8K, "8K" }, ++ { NAND_PAGE_16K, "16K" }, ++ { NAND_PAGE_32K, "32K" }, ++}; ++ ++const char *nand_page_name(int type) ++{ ++ return type2str(page2name, ARRAY_SIZE(page2name), type, "unknown"); ++} ++ ++static struct match_reg_type page2size[] = { ++ { _512B, NAND_PAGE_512B }, ++ { _2K, NAND_PAGE_2K }, ++ { _4K, NAND_PAGE_4K }, ++ { _8K, NAND_PAGE_8K }, ++ { _16K, NAND_PAGE_16K }, ++ { _32K, NAND_PAGE_32K }, ++}; ++ ++int nandpage_size2type(int size) ++{ ++ return reg2type(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K); ++} ++ ++int nandpage_type2size(int size) ++{ ++ return type2reg(page2size, ARRAY_SIZE(page2size), size, NAND_PAGE_2K); ++} ++#endif ++ ++#define ET_ECC_NONE 0x00 ++#define ET_ECC_4BIT 0x02 ++#define ET_ECC_8BIT 0x03 ++#define ET_ECC_24BIT 0x04 ++#define ET_ECC_40BIT1K 0x05 ++#define ET_ECC_64BIT1K 0x06 ++ ++static struct match_reg_type ecc_yaffs_type_t[] = { ++ {ET_ECC_NONE, NAND_ECC_0BIT}, ++ {ET_ECC_4BIT, NAND_ECC_8BIT}, ++ {ET_ECC_8BIT, NAND_ECC_16BIT}, ++ {ET_ECC_24BIT, NAND_ECC_24BIT}, ++ {ET_ECC_40BIT1K, NAND_ECC_40BIT}, ++ {ET_ECC_64BIT1K, NAND_ECC_64BIT} ++}; ++ ++unsigned char match_ecc_type_to_yaffs(unsigned char type) ++{ ++ return type2reg(ecc_yaffs_type_t, ARRAY_SIZE(ecc_yaffs_type_t), type, ++ ET_ECC_4BIT); ++} ++ ++static struct match_t page_table[] = { ++ {NAND_PAGE_2K, PAGE_SIZE_2KB, "2K"}, ++ {NAND_PAGE_4K, PAGE_SIZE_4KB, "4K"}, ++ {NAND_PAGE_8K, PAGE_SIZE_8KB, "8K"}, ++ {NAND_PAGE_16K, PAGE_SIZE_16KB, "16K"}, ++}; ++ ++unsigned char match_page_reg_to_type(unsigned char reg) ++{ ++ return match_reg_to_type(page_table, ARRAY_SIZE(page_table), reg, ++ NAND_PAGE_2K); ++} ++ ++unsigned char match_page_type_to_reg(unsigned char type) ++{ ++ return match_type_to_reg(page_table, ARRAY_SIZE(page_table), type, ++ PAGE_SIZE_2KB); ++} ++ ++const char *match_page_type_to_str(unsigned char type) ++{ ++ return match_type_to_data(page_table, ARRAY_SIZE(page_table), type, ++ "unknown"); ++} ++ ++static struct match_t ecc_table[] = { ++ {NAND_ECC_0BIT, ECC_TYPE_0BIT, "none"}, ++ {NAND_ECC_8BIT, ECC_TYPE_8BIT, "4bit/512"}, ++ {NAND_ECC_16BIT, ECC_TYPE_16BIT, "8bit/512"}, ++ {NAND_ECC_24BIT, ECC_TYPE_24BIT, "24bit/1K"}, ++ {NAND_ECC_28BIT, ECC_TYPE_28BIT, "28bit/1K"}, ++ {NAND_ECC_40BIT, ECC_TYPE_40BIT, "40bit/1K"}, ++ {NAND_ECC_64BIT, ECC_TYPE_64BIT, "64bit/1K"}, ++}; ++ ++unsigned char match_ecc_reg_to_type(unsigned char reg) ++{ ++ return match_reg_to_type(ecc_table, ARRAY_SIZE(ecc_table), reg, ++ NAND_ECC_8BIT); ++} ++ ++unsigned char match_ecc_type_to_reg(unsigned char type) ++{ ++ return match_type_to_reg(ecc_table, ARRAY_SIZE(ecc_table), type, ++ ECC_TYPE_8BIT); ++} ++ ++const char *match_ecc_type_to_str(unsigned char type) ++{ ++ return match_type_to_data(ecc_table, ARRAY_SIZE(ecc_table), type, ++ "unknown"); ++} ++ ++static struct match_t page_type_size_table[] = { ++ {NAND_PAGE_2K, _2K, NULL}, ++ {NAND_PAGE_4K, _4K, NULL}, ++ {NAND_PAGE_8K, _8K, NULL}, ++ {NAND_PAGE_16K, _16K, NULL}, ++}; ++ ++unsigned char match_page_size_to_type(unsigned int size) ++{ ++ return match_reg_to_type(page_type_size_table, ++ ARRAY_SIZE(page_type_size_table), size, NAND_PAGE_2K); ++} ++ ++unsigned int match_page_type_to_size(unsigned char type) ++{ ++ return match_type_to_reg(page_type_size_table, ++ ARRAY_SIZE(page_type_size_table), type, _2K); ++} +diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig +index 018e8c5..5b9d36e 100644 +--- a/drivers/mtd/spi/Kconfig ++++ b/drivers/mtd/spi/Kconfig +@@ -203,5 +203,33 @@ config SPL_SPI_FLASH_MTD + Enable the MTD support for the SPI flash layer in SPL. + + If unsure, say N ++ ++config FMC_SPI_NOR ++ bool "SPI Nor Flash Interface support" ++ depends on FMC ++ help ++ Enable the SPI Nor flash support. ++ ++ If unsure, say N ++ ++config SPI_BLOCK_PROTECT ++ bool "Spi Nor Device BP(Block Protect) Support" ++ depends on FMC_SPI_NOR ++ help ++ SFC supports BP(Block Protect) feature to preestablish a series ++ area to avoid writing and erasing, except to reading. With this macro ++ definition we can get the BP info which was setted before. The ++ BOTTOM/TOP bit is setted to BOTTOM, it means the lock area starts ++ from 0 address. + ++ If unsure, say N ++ ++config DTR_MODE_SUPPORT ++ bool "Spi Nor Device DTR mode Support" ++ depends on FMC_SPI_NOR ++ default n ++ help ++ To support DTR mode ++ ++ If unsure, say N + endmenu # menu "SPI Flash Support" +diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile +index b5dfa30..4ddb5e6 100644 +--- a/drivers/mtd/spi/Makefile ++++ b/drivers/mtd/spi/Makefile +@@ -3,6 +3,10 @@ + # (C) Copyright 2006 + # Wolfgang Denk, DENX Software Engineering, wd@denx.de. + ++obj-y = vendor_spi_nor.o ++ ++obj-$(CONFIG_FMC_SPI_NOR) += fmc100/ ++ + obj-$(CONFIG_DM_SPI_FLASH) += sf-uclass.o + spi-nor-y := sf_probe.o spi-nor-ids.o + +diff --git a/drivers/mtd/spi/fmc100/Makefile b/drivers/mtd/spi/fmc100/Makefile +new file mode 100644 +index 0000000..478f20c +--- /dev/null ++++ b/drivers/mtd/spi/fmc100/Makefile +@@ -0,0 +1,20 @@ ++# ++# The Flash Memory Controller v100 Device Driver for vendor ++# ++# Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version 2 ++# of the License, or (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, see ++# . ++# ++obj-y += fmc100.o fmc100_os.o fmc_spi_nor_ids.o +diff --git a/drivers/mtd/spi/fmc100/fmc100.c b/drivers/mtd/spi/fmc100/fmc100.c +new file mode 100644 +index 0000000..edc200e +--- /dev/null ++++ b/drivers/mtd/spi/fmc100/fmc100.c +@@ -0,0 +1,1399 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "fmc100.h" ++#include ++#include ++#include ++#include ++#include ++#include ++ ++int write_bp_area_check(struct fmc_host* const host, u_int to, size_t len) ++{ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ if (host->level) { ++ if ((host->cmp == BP_CMP_TOP) && ((to + len) > host->start_addr)) { ++ puts("\n"); ++ db_msg("Reg write area[%#x => %#lx] was locked\n", ++ host->start_addr, (to + len)); ++ return -EINVAL; ++ } ++ ++ if ((host->cmp == BP_CMP_BOTTOM) && (to < host->end_addr)) { ++ unsigned end = ((to + len) > host->end_addr) ? ++ host->end_addr : (to + len); ++ ++ puts("\n"); ++ db_msg("Reg write area[%#x => %#x] was locked\n", to, ++ end); ++ return -EINVAL; ++ } ++ } ++#endif ++ return 0; ++} ++ ++static void fmc100_dma_transfer(struct fmc_spi* const spi, ++ unsigned int spi_start_addr, unsigned char* const dma_buffer, ++ unsigned char rw_op, unsigned int size) ++{ ++ unsigned char if_type = 0; ++ unsigned char dummy = 0; ++ unsigned char w_cmd = 0; ++ unsigned char r_cmd = 0; ++ unsigned int regval; ++ struct fmc_host *host = spi->host; ++ ++ fmc_pr(DMA_DB, "\t\t *-Start dma transfer => [%#x], len[%#x], buf = %p\n", ++ spi_start_addr, size, dma_buffer); ++ ++ regval = FMC_INT_CLR_ALL; ++ fmc_write(host, FMC_INT_CLR, regval); ++ fmc_pr(DMA_DB, "\t\t Set INT_CLR[%#x]%#x\n", FMC_INT_CLR, regval); ++ ++ regval = spi_start_addr; ++ fmc_write(host, FMC_ADDRL, regval); ++ fmc_pr(DMA_DB, "\t\t Set ADDRL[%#x]%#x\n", FMC_ADDRL, regval); ++ ++ if (rw_op == RW_OP_WRITE) { ++ if_type = spi->write->iftype; ++ dummy = spi->write->dummy; ++ w_cmd = spi->write->cmd; ++ } else if (rw_op == RW_OP_READ) { ++ if_type = spi->read->iftype; ++ dummy = spi->read->dummy; ++ r_cmd = spi->read->cmd; ++ } ++ ++ regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN | ++ op_cfg_mem_if_type(if_type) | op_cfg_addr_num(spi->addrcycle) | ++ op_cfg_dummy_num(dummy); ++ fmc_write(host, FMC_OP_CFG, regval); ++ fmc_pr(DMA_DB, "\t\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval); ++ ++ regval = fmc_dma_len_set(size); ++ fmc_write(host, FMC_DMA_LEN, regval); ++ fmc_pr(DMA_DB, "\t\t Set DMA_LEN[%#x]%#x\n", FMC_DMA_LEN, regval); ++ /* get hight 32 bits */ ++ regval = (((uintptr_t)dma_buffer & FMC_DMA_SADDRH_MASK) >> 32); ++ fmc_write(host, FMC_DMA_SADDRH_D0, regval); ++ fmc_pr(DMA_DB, "\t\t Set DMA_SADDRH_D0[%#x]%#x\n", FMC_DMA_SADDRH_D0, ++ regval); ++ ++ regval = (unsigned int)((uintptr_t)dma_buffer); ++ fmc_write(host, FMC_DMA_SADDR_D0, regval); ++ fmc_pr(DMA_DB, "\t\t Set DMA_SADDR_D0[%#x]%#x\n", FMC_DMA_SADDR_D0, ++ regval); ++ ++ regval = op_ctrl_rd_opcode(r_cmd) | op_ctrl_wr_opcode(w_cmd) | ++ op_ctrl_rw_op(rw_op) | OP_CTRL_DMA_OP_READY; ++ ++ fmc_write(host, FMC_OP_CTRL, regval); ++ fmc_pr(DMA_DB, "\t\t Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, regval); ++ ++ fmc_dma_wait_int_finish(host); ++ ++ fmc_pr(DMA_DB, "\t\t *-End dma transfer.\n"); ++} ++ ++#ifdef FMC100_SPI_NOR_SUPPORT_REG_READ ++static char *fmc100_reg_read_buf(struct fmc_host *host, ++ struct fmc_spi *spi, ++ unsigned int spi_start_addr, ++ unsigned int size, ++ unsigned char *buffer) ++{ ++ unsigned int regval; ++ ++ fmc_pr(DMA_DB, "* Start reg read, from:%#x len:%#x\n", spi_start_addr, ++ size); ++ ++ if (size > FMC100_REG_RD_MAX_SIZE) ++ db_bug("reg read out of reg range.\n"); ++ ++ fmc_write(host, FMC_ADDRL, spi_start_addr); ++ fmc_pr(DMA_DB, " Set ADDRL[%#x]%#x\n", FMC_ADDRL, spi_start_addr); ++ ++ regval = fmc_data_num_cnt(size); ++ fmc_write(host, FMC_DATA_NUM, regval); ++ fmc_pr(DMA_DB, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval); ++ ++ regval = op_ctrl_rd_opcode(spi->read->cmd) | ++ op_ctrl_dma_op(OP_TYPE_REG) | ++ op_ctrl_rw_op(RW_OP_READ) | ++ OP_CTRL_DMA_OP_READY; ++ fmc_write(host, FMC_OP_CTRL, regval); ++ fmc_pr(DMA_DB, " Set OP_CTRL[%#x]%#x\n", FMC_OP_CTRL, regval); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ if (!host->iobase) ++ return NULL; ++ if (memcpy_s(buffer, size, host->iobase, size)) ++ return NULL; ++ ++ fmc_pr(DMA_DB, "*-End reg page read.\n"); ++ ++ return (char*)buffer; ++} ++ ++static int read_to_buff(struct fmc_host *host, ++ struct fmc_spi *spi, ++ loff_t from, ++ size_t len, ++ char *buf) ++{ ++ int num; ++ int chip_idx = 0; ++ unsigned char *ptr = (u_char *)buf; ++ int ret = 0; ++ ++ while (len > 0) { ++ while (from >= spi->chipsize) { ++ from -= spi->chipsize; ++ chip_idx++; ++ spi++; ++ if ((chip_idx == CONFIG_SPI_NOR_MAX_CHIP_NUM) || (!spi->name)) { ++ db_bug("read memory out of range.\n"); ++ ret = -1; ++ return ret; ++ } ++ if (spi->driver->wait_ready(spi)) { ++ ret = -1; ++ return ret; ++ } ++ host->set_system_clock(spi->read, ENABLE); ++ } ++ ++ num = ((from + len) >= spi->chipsize) ? (spi->chipsize - from) : len; ++ ++ while (num >= FMC100_REG_RD_MAX_SIZE) { ++ fmc100_reg_read_buf(host, spi, ++ from, FMC100_REG_RD_MAX_SIZE, ptr); ++ ptr += FMC100_REG_RD_MAX_SIZE; ++ from += FMC100_REG_RD_MAX_SIZE; ++ len -= FMC100_REG_RD_MAX_SIZE; ++ num -= FMC100_REG_RD_MAX_SIZE; ++ } ++ ++ if (num) { ++ fmc100_reg_read_buf(host, spi, from, num, ptr); ++ from += num; ++ ptr += num; ++ len -= num; ++ } ++ } ++ return ret; ++} ++ ++static ssize_t fmc100_reg_read(struct spi_flash *spiflash, loff_t from, ++ size_t len, char *buf) ++{ ++ int result = -EIO; ++ struct fmc_host *host = spiflash_to_host(spiflash); ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip; ++ int ret; ++ ++ if (((from + len) > spiflash->size) || (!len)) { ++ db_msg("read area out of range or len is zero\n"); ++ return -EINVAL; ++ } ++ ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(100); /* delay 100 us */ ++ return -EBUSY; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NOR); ++ (*fmc_ip)++; ++ } ++ ++ if (spi->driver->wait_ready(spi)) { ++ db_msg("Error: Dma read wait ready fail!\n"); ++ result = -EBUSY; ++ goto fail; ++ } ++ ++ host->set_system_clock(spi->read, ENABLE); ++ ++ ret = read_to_buff(host, spi, from, len, buf); ++ if (ret) ++ goto fail; ++ ++ result = 0; ++ ++fail: ++ (*fmc_ip)--; ++ return result; ++} ++#endif ++static int dma_cycle_op(struct spi_flash *spiflash, unsigned char rw_op, ++ loff_t from, loff_t len, const void *buf) ++{ ++ int op_len; ++ loff_t num; ++ int chip_idx = 0; ++ struct spi_op *op = NULL; ++ struct fmc_host *host = spiflash_to_host(spiflash); ++ struct fmc_spi *spi = host->spi; ++ ++ if (rw_op == RW_OP_READ) { ++ op_len = FMC100_DMA_RD_MAX_SIZE; ++ op = spi->read; ++ } else { ++ op_len = FMC100_DMA_WR_MAX_SIZE; ++ op = spi->write; ++ } ++ ++ while (len) { ++ num = ((len >= op_len) ? op_len : len); ++ ++ while (from >= spi->chipsize) { ++ from -= spi->chipsize; ++ chip_idx++; ++ spi++; ++ if ((chip_idx == CONFIG_SPI_NOR_MAX_CHIP_NUM) || (!spi->name)) { ++ db_bug("read memory out of range.\n"); ++ return -1; ++ } ++ ++ if (spi->driver->wait_ready(spi)) { ++ db_msg("Error: Dma op wait ready fail!!\n"); ++ return -EBUSY; ++ } ++ ++ host->set_system_clock(op, ENABLE); ++ } ++ ++ if (from + num > spi->chipsize) ++ num = spi->chipsize - from; ++ ++ fmc100_dma_transfer(spi, from, (u_char *)buf, ++ rw_op, num); ++ from += num; ++ buf += num; ++ len -= num; ++ } ++ return 0; ++} ++ ++#ifndef FMC100_SPI_NOR_SUPPORT_REG_READ ++static ssize_t fmc100_dma_read(struct spi_flash* const spiflash, loff_t from, size_t len, ++ unsigned char* const buf) ++{ ++ int result = -EIO; ++ struct fmc_host *host = spiflash_to_host(spiflash); ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip = NULL; ++ ++ fmc_pr(RD_DBG, "\t|*-Start dma read, from:%#llx len:%#lx\n", from, (long)len); ++ ++ if (((from + len) > spiflash->size) || (!len)) { ++ db_msg("read area out of range[%#x].\n", spiflash->size); ++ return -EINVAL; ++ } ++ ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(100); /* delay 100 us */ ++ return -EBUSY; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NOR); ++ (*fmc_ip)++; ++ } ++ ++ if (spi->driver->wait_ready(spi)) { ++ db_msg("Error: Dma read wait ready fail!\n"); ++ result = -EBUSY; ++ goto fail; ++ } ++ ++ host->set_system_clock(spi->read, ENABLE); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range((uintptr_t)buf, (uintptr_t)(buf + len)); ++#endif ++ if (dma_cycle_op(spiflash, RW_OP_READ, from, len, buf)) ++ goto fail; ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range((uintptr_t)buf, (uintptr_t)(buf + len)); ++#endif ++ ++ result = 0; ++fail: ++ (*fmc_ip)--; ++ ++ fmc_pr(RD_DBG, "\t|*-End dma read.\n"); ++ ++ return result; ++} ++#endif ++ ++void fmc100_read_ids(const struct fmc_spi *spi, u_char cs, u_char* const id) ++{ ++ unsigned int reg; ++ struct fmc_host *host = NULL; ++ ++ if (!spi || !spi->host || !id) ++ return; ++ host = spi->host; ++ if (!host->iobase) ++ return; ++ fmc_pr(BT_DBG, "\t|||*-Start Read SPI(cs:%d) ID.\n", cs); ++ ++ reg = fmc_cmd_cmd1(SPI_CMD_RDID); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(BT_DBG, "\t||||-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(cs) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(BT_DBG, "\t||||-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_data_num_cnt(MAX_SPI_NOR_ID_LEN); ++ fmc_write(host, FMC_DATA_NUM, reg); ++ fmc_pr(BT_DBG, "\t||||-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | ++ fmc_op_read_data_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(BT_DBG, "\t||||-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ if (memcpy_s(id, MAX_SPI_NOR_ID_LEN, host->iobase, MAX_SPI_NOR_ID_LEN)) ++ return; ++ ++ fmc_pr(BT_DBG, "\t|||*-Read CS: %d ID: %#X %#X %#X %#X %#X %#X\n", ++ cs, id[0], id[1], id[2], id[3], id[4], id[5]); ++} ++ ++static int fmc100_reg_erase_one_block(struct spi_flash *spiflash, loff_t offs) ++{ ++ unsigned int regval; ++ int ret; ++ ++ if (!spiflash) { ++ printf("%s:data is NULL, please check input pointer parmeter\n", __func__); ++ return -1; ++ } ++ ++ struct fmc_host *host = spiflash_to_host(spiflash); ++ struct fmc_spi *spi = host->spi; ++ ++ fmc_pr(OP_DBG, "\t\t * Start erase one block, offset:%#llx\n", offs); ++ ++ ret = spi->driver->wait_ready(spi); ++ if (ret) { ++ db_msg("Error: Erase wait ready fail! reg:%d\n", ret); ++ return 1; ++ } ++ ++ spi->driver->write_enable(spi); ++ ++ host->set_system_clock(spi->erase, ENABLE); ++ ++ regval = fmc_cmd_cmd1(spi->erase->cmd); ++ fmc_write(host, FMC_CMD, regval); ++ fmc_pr(OP_DBG, "\t\t Set CMD[%#x]%#x\n", FMC_CMD, regval); ++ ++ regval = offs; ++ fmc_write(host, FMC_ADDRL, regval); ++ fmc_pr(OP_DBG, "\t\t Set ADDRL[%#x]%#x\n", FMC_ADDRL, regval); ++ ++ regval = op_cfg_fm_cs(spi->chipselect) | ++ OP_CFG_OEN_EN | ++ op_cfg_mem_if_type(spi->erase->iftype) | ++ op_cfg_addr_num(spi->addrcycle) | ++ op_cfg_dummy_num(spi->erase->dummy); ++ fmc_write(host, FMC_OP_CFG, regval); ++ fmc_pr(OP_DBG, "\t\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval); ++ ++ regval = fmc_op_cmd1_en(ENABLE) | ++ fmc_op_addr_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, regval); ++ fmc_pr(OP_DBG, "\t\t Set OP[%#x]%#x\n", FMC_OP, regval); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ fmc_pr(OP_DBG, "\t\t * End erase one block.\n"); ++ ++ return 0; ++} ++ ++#ifndef FMC100_SPI_NOR_SUPPORT_REG_WRITE ++static ssize_t fmc100_dma_write(struct spi_flash *spiflash, loff_t to, size_t len, ++ const unsigned char *buf) ++{ ++ int result = -EIO; ++ struct fmc_host *host = spiflash_to_host(spiflash); ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip = NULL; ++ ++ fmc_pr(WR_DBG, "\n\t\t* Start dma write, to:%#llx len:%#lx\n", to, (long)len); ++ ++ if (((to + len) > spiflash->size) || (!len)) { ++ db_msg("write data out of range or len is zero.\n"); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ if (host->level && (to < host->end_addr)) { ++ printf("\nERROR: The DMA write area was locked.\n"); ++ return -EINVAL; ++ } ++#endif ++ ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(100); /* delay 100 us */ ++ return -EBUSY; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NOR); ++ (*fmc_ip)++; ++ } ++ ++ if (spi->driver->wait_ready(spi)) ++ goto fail; ++ ++ spi->driver->write_enable(spi); ++ host->set_system_clock(spi->write, ENABLE); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ flush_dcache_range((uintptr_t)buf, (uintptr_t)(buf + len)); ++#endif ++ ++ if (dma_cycle_op(spiflash, RW_OP_WRITE, to, len, buf)) ++ goto fail; ++ ++ result = 0; ++fail: ++ (*fmc_ip)--; ++ fmc_pr(WR_DBG, "\t\t* End dma write.\n"); ++ return result; ++} ++#endif ++ ++#ifdef FMC100_SPI_NOR_SUPPORT_REG_WRITE ++static int fmc100_reg_write_buf(struct fmc_host *host, ++ struct fmc_spi *spi, u_int spi_start_addr, ++ size_t size, unsigned char *buffer) ++{ ++ int ret; ++ ++ if (size > FMC100_DMA_WR_MAX_SIZE) ++ db_bug("reg read out of reg range.\n"); ++ ++ if (spi->driver->wait_ready(spi)) ++ return 1; ++ ++ ret = memcpy_s((char *)host->iobase, size, buffer, size) ++ if (ret) ++ return -ret; ++ ++ spi->driver->write_enable(spi); ++ ++ fmc_write(host, FMC_INT_CLR, FMC_INT_CLR_ALL); ++ ++ fmc_write(host, FMC_CMD, fmc_cmd_cmd1(spi->write->cmd)); ++ ++ fmc_write(host, FMC_OP_CFG, op_cfg_fm_cs(spi->chipselect) | ++ op_cfg_mem_if_type(spi->write->iftype) | OP_CFG_OEN_EN); ++ ++ fmc_write(host, FMC_ADDRL, spi_start_addr); ++ ++ fmc_write(host, FMC_OP_CTRL, (op_ctrl_wr_opcode(spi->write->cmd) | ++ op_ctrl_dma_op(OP_TYPE_REG) | ++ op_ctrl_rw_op(RW_OP_WRITE) | ++ OP_CTRL_DMA_OP_READY)); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ return 0; ++} ++ ++static int write_to_buf(struct fmc_host *host, ++ struct fmc_spi *spi, ++ loff_t to, ++ size_t len, ++ const unsigned char *buf) ++{ ++ int num; ++ unsigned char *ptr = (unsigned char *)buf; ++ int ret = -1; ++ ++ if (to & FMC100_DMA_WR_MASK) { ++ num = FMC100_DMA_WR_MAX_SIZE - (to & FMC100_DMA_WR_MASK); ++ if (num > (int)len) ++ num = (int)len; ++ ++ while (to >= spi->chipsize) { ++ to -= spi->chipsize; ++ spi++; ++ if (!spi->name) ++ db_bug("write memory out of range.\n"); ++ ++ if (spi->driver->wait_ready(spi)) ++ return ret; ++ host->set_system_clock(spi->write, ENABLE); ++ } ++ ++ if (fmc100_reg_write_buf(host, spi, to, num, ptr)) ++ return ret; ++ ++ to += num; ++ ptr += num; ++ len -= num; ++ } ++ ++ while (len > 0) { ++ num = ((len >= FMC100_DMA_WR_MAX_SIZE) ? ++ FMC100_DMA_WR_MAX_SIZE : len); ++ while (to >= spi->chipsize) { ++ to -= spi->chipsize; ++ spi++; ++ if (!spi->name) ++ db_bug("write memory out of range.\n"); ++ ++ if (spi->driver->wait_ready(spi)) ++ return ret; ++ host->set_system_clock(spi->write, ENABLE); ++ } ++ if (fmc100_reg_write_buf(host, spi, to, num, ptr)) ++ return ret; ++ to += num; ++ ptr += num; ++ len -= num; ++ } ++ return 0; ++} ++ ++static ssize_t fmc100_reg_write(struct spi_flash *spiflash, loff_t to, size_t len, ++ const unsigned char *buf) ++{ ++ int result = -EIO; ++ int ret; ++ struct fmc_host *host = spiflash_to_host(spiflash); ++ struct fmc_spi *spi = host->spi; ++ unsigned char *fmc_ip; ++ ++ if ((to + len) > spiflash->size) { ++ db_msg("write data out of range.\n"); ++ return -EINVAL; ++ } ++ ++ if (!len) { ++ db_msg("write length is 0.\n"); ++ return 0; ++ } ++ ++ if (write_bp_area_check(host, to, len)) ++ return -EINVAL; ++ ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(100); /* delay 100 us */ ++ return -EBUSY; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NOR); ++ (*fmc_ip)++; ++ } ++ ++ if (spi->driver->wait_ready(spi)) ++ goto fail; ++ ++ host->set_system_clock(spi->write, ENABLE); ++ ++ ret = write_to_buf(host, spi, to, len, buf); ++ if (ret) ++ goto fail; ++ ++ result = 0; ++fail: ++ (*fmc_ip)--; ++ return result; ++} ++#endif /* FMC100_SPI_NOR_SUPPORT_REG_WRITE */ ++ ++static int fmc100_erase_parm_check(struct spi_flash* const spiflash, ++ u32 offset, ++ size_t length, ++ struct fmc_host** const host, ++ struct fmc_spi** const spi) ++{ ++ if (!spiflash) { ++ db_msg("Error: spiflash is NULL.\n"); ++ return -EINVAL; ++ } ++ *host = spiflash_to_host(spiflash); ++ if (!(*host) || !(*host)->spi) { ++ db_msg("Error: host or host->sp is NULL.\n"); ++ return -EINVAL; ++ } ++ *spi = (*host)->spi; ++ if (offset + length > spiflash->size) { ++ db_msg("Error: Erase area out of range of mtd.\n"); ++ return -EINVAL; ++ } ++ ++ if (offset & ((*spi)->erasesize - 1)) { ++ db_msg("Error: Erase start address is not alignment.\n"); ++ return -EINVAL; ++ } ++ ++ if (length & ((*spi)->erasesize - 1)) { ++ db_msg("Error: Erase length is not alignment.\n"); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ if (((*host)->level) && (offset < (*host)->end_addr)) { ++ printf("\nERROR: The erase area was locked.\n"); ++ return -EINVAL; ++ } ++#endif ++ ++ return 0; ++} ++ ++int fmc100_reg_erase(struct spi_flash* const spiflash, u32 offset, ++ size_t length) ++{ ++ int chip_idx = 0; ++ struct fmc_host *host = NULL; ++ struct fmc_spi *spi = NULL; ++ unsigned char *fmc_ip = NULL; ++ int ret; ++ ++ ret = fmc100_erase_parm_check(spiflash, offset, length, &host, &spi); ++ if (ret) ++ return ret; ++ ++ fmc_ip = get_fmc_ip(); ++ if (*fmc_ip) { ++ printf("Warning: FMC IP is busy, Please try again.\n"); ++ udelay(100); /* delay 100 us */ ++ return -EBUSY; ++ } else { ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NOR); ++ (*fmc_ip)++; ++ } ++ ++ while (length) { ++ if (spi->chipsize <= offset) { ++ offset -= (u32)spi->chipsize; ++ chip_idx++; ++ spi++; ++ if ((chip_idx == CONFIG_SPI_NOR_MAX_CHIP_NUM) || (!spi->name)) { ++ db_bug("Error: Erase memory out of range.\n"); ++ (*fmc_ip)--; ++ return -1; ++ } ++ } ++ ++ if (fmc100_reg_erase_one_block(spiflash, (loff_t)offset)) { ++ (*fmc_ip)--; ++ return -1; ++ } ++ ++ offset += spi->erase->size; ++ length -= spi->erase->size; ++ } ++ ++ (*fmc_ip)--; ++ ++ return 0; ++} ++ ++static void spi_nor_func_hook(struct fmc_host* const host) ++{ ++ struct spi_flash *spi_nor_flash = host->spi_nor_flash; ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ host->cmp = BP_CMP_UPDATE_FLAG; ++ fmc100_get_bp_lock_level(host); ++ spi_nor_flash->lock = fmc100_spi_flash_lock; ++#endif ++ fmc_pr(BT_DBG, "\t||-Initial spi_flash structure\n"); ++ spi_nor_flash->name = "fmc"; ++ spi_nor_flash->erase = fmc100_reg_erase_one_block; ++#ifdef FMC100_SPI_NOR_SUPPORT_REG_WRITE ++ spi_nor_flash->write = fmc100_reg_write; ++#else ++ spi_nor_flash->write = fmc100_dma_write; ++#endif ++ ++#ifdef FMC100_SPI_NOR_SUPPORT_REG_READ ++ spi_nor_flash->read = fmc100_reg_read; ++#else ++ spi_nor_flash->read = fmc100_dma_read; ++#endif ++} ++ ++struct spi_flash *fmc100_spi_nor_scan(struct fmc_host *host) ++{ ++ unsigned char cs; ++ if (!host) ++ return NULL; ++ ++ struct spi_flash *spi_nor_flash = host->spi_nor_flash; ++ struct fmc_spi *spi = host->spi; ++ struct mtd_info_ex *spi_nor_info = host->spi_nor_info; ++ if (!spi_nor_flash || !spi || !spi_nor_info) ++ return NULL; ++ fmc_pr(BT_DBG, "\t|*-Start Scan SPI nor flash\n"); ++ ++ spi_nor_flash->size = 0; ++ ++ for (cs = 0; cs < CONFIG_SPI_NOR_MAX_CHIP_NUM; cs++) ++ host->spi[cs].host = host; ++ ++ fmc_pr(BT_DBG, "\t||-Initial mtd_info_ex structure\n"); ++ if (memset_s(spi_nor_info, sizeof(struct mtd_info_ex), 0, sizeof(struct mtd_info_ex))) ++ return NULL; ++ ++ if (!fmc_spi_nor_probe(spi_nor_info, spi)) { ++#ifndef CONFIG_SPI_NOR_QUIET_TEST ++ printf("Can't find a valid spi nor flash chip.\n"); ++#endif ++ return NULL; ++ } ++ ++ if (spi->addrcycle == SPI_NOR_4BYTE_ADDR_LEN) { ++ host->set_host_addr_mode(host, ENABLE); ++ fmc_pr(AC_DBG, "\t* Controller entry 4-byte mode.\n"); ++ } ++ ++ spi_nor_func_hook(host); ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ if (spi->dtr_mode_support) { ++ int ret; ++ ++ host->dtr_training_flag = 0; ++ /* setting DTR mode dummy and traning */ ++ ret = spi_dtr_dummy_training_set(host, ENABLE); ++ /* If training setting fail, must reset back to SDR mode, ++ * Note: logic auto turn on DTR when read ++ * auto turn off DTR when write and erase */ ++ if (ret) { ++ printf("Reset to STR mode.\n"); ++ /* switch DTR mode to SDR mode */ ++ fmc_dtr_mode_ctrl(spi, DISABLE); ++ spi_dtr_to_sdr_switch(spi); ++ spi_dtr_dummy_training_set(host, DISABLE); ++ } ++ } ++#endif ++ ++ fmc_pr(BT_DBG, "\t|*-Found SPI nor flash: %s\n", spi_nor_info->name); ++ ++ return spi_nor_flash; ++} ++ ++static void fmc100_set_host_addr_mode(struct fmc_host* const host, int enable) ++{ ++ unsigned int regval = fmc_read(host, FMC_CFG); ++ ++ /* 1: SPI_NOR_ADDR_MODE_4_BYTES 0: SPI_NOR_ADDR_MODE_3_BYTES */ ++ if (enable) ++ regval |= SPI_NOR_ADDR_MODE_MASK; ++ else ++ regval &= ~SPI_NOR_ADDR_MODE_MASK; ++ ++ fmc_write(host, FMC_CFG, regval); ++} ++ ++static int fmc100_host_init(struct fmc_host *host) ++{ ++ unsigned int reg, flash_type; ++ ++ fmc_pr(BT_DBG, "\t|||*-Start SPI Nor host init\n"); ++ ++ host->regbase = (void *)CONFIG_FMC_REG_BASE; ++ host->iobase = (void *)CONFIG_FMC_BUFFER_BASE; ++ ++ reg = fmc_read(host, FMC_CFG); ++ fmc_pr(BT_DBG, "\t||||-Get CFG[%#x]%#x\n", FMC_CFG, reg); ++ flash_type = (reg & FLASH_SEL_MASK) >> FLASH_SEL_SHIFT; ++ if (flash_type != FLASH_TYPE_SPI_NOR) { ++ db_msg("Error: Flash type isn't SPI Nor. reg: %#x\n", reg); ++ return -ENODEV; ++ } ++ ++ if ((reg & OP_MODE_MASK) == OP_MODE_BOOT) { ++ reg |= fmc_cfg_op_mode(OP_MODE_NORMAL); ++ fmc_pr(BT_DBG, "\t||||-Controller enter normal mode\n"); ++ fmc_write(host, FMC_CFG, reg); ++ fmc_pr(BT_DBG, "\t||||-Set CFG[%#x]%#x\n", FMC_CFG, reg); ++ } ++ ++ host->set_system_clock = fmc_set_fmc_system_clock; ++ host->set_host_addr_mode = fmc100_set_host_addr_mode; ++ ++ reg = timing_cfg_tcsh(CS_HOLD_TIME) | ++ timing_cfg_tcss(CS_SETUP_TIME) | ++ timing_cfg_tshsl(CS_DESELECT_TIME); ++ fmc_write(host, FMC_SPI_TIMING_CFG, reg); ++ fmc_pr(BT_DBG, "\t||||-Set TIMING[%#x]%#x\n", FMC_SPI_TIMING_CFG, reg); ++ ++ fmc_pr(BT_DBG, "\t|||*-End SPI Nor host init\n"); ++ ++ return 0; ++} ++ ++int fmc100_spi_nor_init(struct fmc_host *host) ++{ ++ int ret; ++ if (!host) { ++ db_msg("Error: host is NULL, please check input parameter\n"); ++ return -1; ++ } ++ ++ fmc_pr(BT_DBG, "\t||*-Start fmc100 SPI Nor init\n"); ++ ++ fmc_pr(BT_DBG, "\t|||-FMC100 host structure init\n"); ++ ret = fmc100_host_init(host); ++ if (ret) { ++ db_msg("Error: SPI Nor host init failed, result: %d\n", ret); ++ return ret; ++ } ++ ++ fmc_pr(BT_DBG, "\t|||-Set default system clock, Enable controller\n"); ++ if (host->set_system_clock) ++ host->set_system_clock(NULL, ENABLE); ++ ++ fmc_pr(BT_DBG, "\t||*-End fmc100 SPI Nor init\n"); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ ++void spi_lock_update_address(struct fmc_host *host) ++{ ++ unsigned int lock_level_max, erasesize, chipsize; ++ unsigned char mid = host->spi_nor_info->ids[0]; ++ struct fmc_spi *spi = host->spi; ++ ++ if (!host->level) { ++ host->end_addr = 0; ++ fmc_pr(BP_DBG, "all blocks is unlocked.\n"); ++ return; ++ } ++ ++ chipsize = spi->chipsize; ++ erasesize = spi->erasesize; ++ lock_level_max = host->spi_nor_flash[0].bp_level_max; ++ ++ /* general case */ ++ host->end_addr = chipsize >> (lock_level_max - host->level); ++} ++ ++static void bp_lock_addr_updata(struct fmc_host *host, ++ struct fmc_spi *spi, ++ unsigned char mid) ++{ ++ unsigned int lock_level_max; ++ ++ if (host->cmp == BP_CMP_UPDATE_FLAG) { ++ /* get the max block protect level of current manufacture ID */ ++ lock_level_max = lock_level_max(host->bp_num); ++ ++ host->spi_nor_flash[0].bp_level_max = lock_level_max; ++ fmc_pr(BP_DBG, "Get the max bp level: [%d]\n", lock_level_max); ++ ++ spi_lock_update_address(host); ++ if (host->end_addr) ++ printf("Spi is locked. lock address[0 => %#x]\n", ++ host->end_addr); ++ } ++} ++ ++void fmc100_get_bp_lock_level(struct fmc_host *host) ++{ ++ struct fmc_spi *spi = host->spi; ++ unsigned char mid = host->spi_nor_info->ids[0]; ++ ++ fmc_pr(BP_DBG, "Get manufacturer ID: [%#x]\n", mid); ++ ++ /* match the manufacture ID to get the block protect info */ ++ switch (mid) { ++ default: ++ goto usage; ++ } ++ ++ /* this branch only for initialization */ ++ bp_lock_addr_updata(host, spi, mid); ++ ++ return; ++usage: ++ db_msg("Error:The ID: %#x isn't in the BP table,\n", mid); ++ db_msg("Current device can't not protect\n"); ++} ++ ++unsigned short fmc100_set_spi_lock_info(struct fmc_host *host) ++{ ++ unsigned short val; ++ unsigned char mid = host->spi_nor_info->ids[0]; ++ struct fmc_spi *spi = host->spi; ++ ++ fmc_pr(BP_DBG, "Get manufacturer ID: [%#x]\n", mid); ++ ++ /* match the manufacture ID to get the block protect set info */ ++ switch (mid) { ++ default: ++ goto usage; ++ } ++ ++ return val; ++usage: ++ db_msg("Error: The manufacture ID is change,\n Pleaer check!!\n"); ++ db_msg("Error: The ID: %#x isn't in the BP table,\n", mid); ++ return 1; ++} ++ ++unsigned short fmc100_handle_bp_rdcr_info(struct fmc_host *host, u_char cmd) ++{ ++ unsigned char status, config; ++ struct fmc_spi *spi = host->spi; ++ unsigned char mid = host->spi_nor_info->ids[0]; ++ ++ /* this macro definition is for determining the writing length */ ++ host->bt_loc = BT_LOC_RDCR; ++ spi->driver->wait_ready(spi); ++ ++ /* get the block protect B/P info in config register */ ++ config = spi_general_get_flash_register(spi, cmd); ++ fmc_pr(BP_DBG, "Get Config Register[%#x]\n", config); ++ ++ config = spi_bp_bottom_rdcr_set(config); ++ fmc_pr(BP_DBG, "Set Config Register[%#x]\n", config); ++ ++ /* get the block protect level info in status register */ ++ status = spi_general_get_flash_register(spi, SPI_CMD_RDSR); ++ fmc_pr(BP_DBG, "Get Status Register[%#x]\n", status); ++ ++ return ((unsigned short)config << SPI_NOR_CR_SHIFT) | status; ++} ++ ++unsigned short fmc100_handle_bp_rdsr_info(struct fmc_host *host, ++ u_char cmd) ++{ ++ unsigned char val; ++ struct fmc_spi *spi = host->spi; ++ unsigned char mid = host->spi_nor_info->ids[0]; ++ ++ /* this macro definition is for determining the writing length */ ++ host->bt_loc = BT_LOC_RDSR; ++ spi->driver->wait_ready(spi); ++ ++ /* get the block protect level and B/T info in status register */ ++ val = spi_general_get_flash_register(spi, cmd); ++ fmc_pr(BP_DBG, "Get Status Register[%#x]\n", val); ++ val |= spi_bp_bottom_rdsr_set_1(host->bp_num); ++ fmc_pr(BP_DBG, "Set Config Register[%#x]\n", val); ++ ++ return val; ++} ++ ++static void fmc100_set_bp_val(struct fmc_host *host, unsigned short val) ++{ ++ unsigned int reg; ++ struct fmc_spi *spi = host->spi; ++ ++ reg = fmc_read(host, FMC_GLOBAL_CFG); ++ if (reg & FMC_GLOBAL_CFG_WP_ENABLE) { ++ fmc_pr(BP_DBG, " Hardware protected enable!, reg[%#x]\n", reg); ++ reg &= ~FMC_GLOBAL_CFG_WP_ENABLE; ++ fmc_write(host, FMC_GLOBAL_CFG, reg); ++ val &= ~SPI_NOR_SR_SRWD_MASK; ++ fmc_pr(BP_DBG, "Disable SR[%d]:SRWD and WP#\n", ++ SPI_NOR_SR_SRWD_SHIFT); ++ } ++ ++ if (host->bt_loc == BT_LOC_RDSR) { ++ writeb(val, host->iobase); ++ fmc_pr(BP_DBG, "Write IO[%p]%#x\n", host->iobase, ++ *(unsigned char *)host->iobase); ++ } else { ++ writew(val, host->iobase); ++ fmc_pr(BP_DBG, "Write IO[%p]%#x\n", host->iobase, ++ *(unsigned short *)host->iobase); ++ } ++ ++ reg = fmc_cmd_cmd1(SPI_CMD_WRSR); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(BP_DBG, " Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(BP_DBG, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ if (host->bt_loc == BT_LOC_RDSR) ++ reg = fmc_data_num_cnt(SPI_NOR_SR_LEN); ++ else ++ reg = fmc_data_num_cnt(SPI_NOR_SR_LEN + SPI_NOR_CR_LEN); ++ ++ fmc_write(host, FMC_DATA_NUM, reg); ++ fmc_pr(BP_DBG, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | ++ fmc_op_write_data_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(BP_DBG, " Set OP[%#x]%#x\n", FMC_OP, reg); ++} ++ ++static void fmc100_set_bp_level(struct fmc_host *host, unsigned char level) ++{ ++ unsigned char old_level; ++ unsigned short val; ++ struct fmc_spi *spi = host->spi; ++ unsigned char mid = host->spi_nor_info->ids[0]; ++ ++ fmc_pr(BP_DBG, "* Start BP bottom level %d\n", level); ++ ++ val = fmc100_set_spi_lock_info(host); ++ old_level = host->level; ++ fmc_pr(BP_DBG, " Read CR:SR[%#x]\n", val); ++ ++ if (old_level != level) { ++ if (host->bp_num == BP_NUM_3) ++ val &= ~SPI_NOR_SR_BP_MASK_3; ++ else ++ val &= ~SPI_NOR_SR_BP_MASK_4; ++ val |= level << SPI_NOR_SR_BP0_SHIFT; ++ fmc_pr(BP_DBG, "Set Status Register[%#x]\n", val); ++ } else { ++ fmc_pr(BP_DBG, "NOTES: old_level[%#x] = level[%#x]\n", ++ old_level, level); ++ return; ++ } ++ ++ spi->driver->write_enable(spi); ++ fmc100_set_bp_val(host, val); ++ fmc_cmd_wait_cpu_finish(host); ++ fmc_pr(BP_DBG, "* Set BP level end.\n"); ++} ++ ++void fmc100_spi_lock(struct fmc_host *host, unsigned char level) ++{ ++ unsigned char current_level; ++ if (!host || !host->spi || !host->spi->host || !host->iobase) ++ return; ++ fmc100_set_bp_level(host, level); ++ ++ /* check if we have set successfully or not */ ++ current_level = fmc100_bp_to_level(host); ++ if (current_level != level) { ++ db_msg("Error: Current lock level: %d, but set value: %d\n", ++ current_level, level); ++ return; ++ } ++ ++ host->level = level; ++ spi_lock_update_address(host); ++ ++ if (host->end_addr) ++ printf("Spi is locked. lock address[0 => %#x]\n", ++ host->end_addr); ++ ++ return; ++} ++ ++unsigned char fmc100_bp_to_level(struct fmc_host *host) ++{ ++ unsigned char val; ++ unsigned char level; ++ struct fmc_spi *spi = host->spi; ++ ++ spi->driver->wait_ready(spi); ++ val = spi_general_get_flash_register(spi, SPI_CMD_RDSR); ++ fmc_pr(BP_DBG, "Get Status Register[%#x]\n", val); ++ ++ fmc_pr(BP_DBG, "the bp_num[%d]\n", host->bp_num); ++ ++ if (host->bp_num == BP_NUM_3) ++ level = (val & SPI_NOR_SR_BP_MASK_3) >> SPI_NOR_SR_BP0_SHIFT; ++ else ++ level = (val & SPI_NOR_SR_BP_MASK_4) >> SPI_NOR_SR_BP0_SHIFT; ++ ++ fmc_pr(BP_DBG, "the current level[%d]\n", level); ++ ++ return level; ++} ++#endif /* CONFIG_SPI_BLOCK_PROTECT */ ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++int spi_dtr_dummy_training_set(struct fmc_host *host, int dtr_en) ++{ ++ struct fmc_spi *spi = host->spi; ++ int ret; ++ ++ switch (spi->dtr_cookie) { ++ case DTR_MODE_SET_ODS: ++ if (spi->driver->dtr_set_device) ++ spi->driver->dtr_set_device(spi, dtr_en); ++ break; ++ case DTR_MODE_SET_NONE: ++ default: ++ break; ++ } ++ ++ /* disable DTR mode without training */ ++ /* dtr dummy training is done, return it */ ++ if ((host->dtr_training_flag == 1) || (dtr_en == DISABLE)) ++ return 0; ++ ++ /* enable DTR mode and set sample point */ ++ fmc_dtr_mode_ctrl(spi, ENABLE); ++ ++ /* set training */ ++ ret = spi_dtr_training(host); ++ if (ret) { ++ fmc_pr(DTR_DB, " * Set dtr training fail.\n"); ++ return 1; ++ } ++ fmc_pr(DTR_DB, "* Set dtr and dummy end.\n"); ++ return 0; ++} ++ ++static int select_sample_point(unsigned char *status, int len) ++{ ++ int ix; ++ unsigned int p_count = 0; ++ unsigned int p_temp = 0; ++ unsigned int reg = 0; ++ ++ if (len <= 0) ++ return 0; ++ ++ /* select the best smaple point */ ++ for (ix = 0; ix < len;) { ++ if (status[ix] == 1) { ++ p_count++; ++ ix++; ++ if ((status[ix] == 0) && (p_count > p_temp)) { ++ p_temp = p_count; ++ p_count = 0; ++ reg = ix - ((p_temp + 1) >> 1); ++ fmc_pr(DTR_DB, "the sample point choice: %#x\n", reg); ++ break; ++ } ++ continue; ++ } ++ ix++; ++ } ++ return reg; ++} ++ ++static int training_read_process(struct fmc_spi *spi, ++ unsigned char *buf) ++{ ++ int ret; ++ unsigned int sec_boot_mode; ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range((uintptr_t)buf, ++ (uintptr_t)(buf + DTR_TRAINING_CMP_LEN)); ++#endif ++ ++ fmc100_dma_transfer(spi, DTR_TRAINING_CMP_ADDR_SHIFT, buf, ++ RW_OP_READ, DTR_TRAINING_CMP_LEN); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range((uintptr_t)buf, ++ (uintptr_t)(buf + DTR_TRAINING_CMP_LEN)); ++#endif ++ ++ sec_boot_mode = get_sec_boot_mode(sec_boot_mode); ++ if (sec_boot_mode == 0) ++ ret = memcmp((const void *)buf, ++ (const void *)DTR_TRAINING_CMP_ADDR_S, ++ DTR_TRAINING_CMP_LEN); ++ else ++ ret = memcmp((const void *)buf, ++ (const void *)SEC_UBOOT_DATA_ADDR, ++ DTR_TRAINING_CMP_LEN); ++ ++ return ret; ++} ++ ++ ++static int dtr_training_handle(struct fmc_host *host) ++{ ++ int ret; ++ int ix; ++ unsigned int reg = 0; ++ unsigned int regval; ++ unsigned char *buf = NULL; ++ unsigned char status[DTR_TRAINING_POINT_NUM] = {0}; ++ struct fmc_spi *spi = host->spi; ++ ++ spi->driver->wait_ready(spi); ++ ++ /* set div 4 clock */ ++ host->set_system_clock(spi->read, ENABLE); ++ ++ buf = memalign(CONFIG_SYS_CACHELINE_SIZE, DTR_TRAINING_CMP_LEN); ++ if (!buf) ++ return -1; ++ ++ /* start training to check every sample point */ ++ regval = fmc_read(host, FMC_GLOBAL_CFG); ++ for (ix = 0; ix < DTR_TRAINING_POINT_NUM; ix++) { ++ regval = dtr_training_point_clr(regval); ++ regval |= (ix << DTR_TRAINING_POINT_MASK); ++ fmc_pr(DTR_DB, " setting the dtr training point:%d\n", ix); ++ fmc_write(host, FMC_GLOBAL_CFG, regval); ++ fmc_pr(DTR_DB, " Set dtr_training[%#x]%#x\n", ++ FMC_GLOBAL_CFG, regval); ++ /* read */ ++ ret = training_read_process(spi, buf); ++ if (ret == 0) { ++ /* Just to reduce the use of variables, no other reasion */ ++ reg = 1; ++ status[ix] = 1; /* like */ ++ fmc_pr(DTR_DB, " status[%d] = 1\n", ix); ++ } ++ if (!reg && (ix == DTR_TRAINING_POINT_NUM - 1)) ++ goto fail_training; ++ } ++ ++ kfree(buf); ++ ++ /* select the best smaple point */ ++ reg = 0; ++ reg = select_sample_point(status, DTR_TRAINING_POINT_NUM); ++ ++ /* to set the best sample point */ ++ regval = dtr_training_point_clr(regval); ++ regval |= (reg << DTR_TRAINING_POINT_MASK); ++ fmc_pr(DTR_DB, " set the sample point[%#x]%#x\n", ++ FMC_GLOBAL_CFG, regval); ++ fmc_write(host, FMC_GLOBAL_CFG, regval); ++ ++ /* training handle end */ ++ return regval; ++ ++fail_training: ++ printf("Cannot find an useful sample point.\n"); ++ kfree(buf); ++ return -1; ++} ++ ++unsigned int spi_dtr_training(struct fmc_host *host) ++{ ++ int reg, cur_reg; ++ ++ fmc_pr(DTR_DB, "DTR traiining start ...\n"); ++ /* DTR traiining start */ ++ reg = dtr_training_handle(host); ++ if (reg == -1) { ++ host->dtr_training_flag = 0; ++ printf("DTR traiining fail.\n"); ++ return 1; ++ } ++ fmc_pr(DTR_DB, "* DTR traiining end.\n"); ++ cur_reg = fmc_read(host, FMC_GLOBAL_CFG); ++ /* to check whether training is done */ ++ if (cur_reg == reg) { ++ host->dtr_training_flag = 1; ++ fmc_pr(DTR_DB, "* Set dtr_training success.\n"); ++ return 0; ++ } ++ return 1; ++} ++ ++void fmc_dtr_mode_ctrl(struct fmc_spi *spi, int dtr_en) ++{ ++ unsigned int regval; ++ struct fmc_host *host = (struct fmc_host *)spi->host; ++ ++ host->dtr_mode_en = dtr_en; ++ regval = fmc_read(host, FMC_GLOBAL_CFG); ++ if (dtr_en == ENABLE) { ++ /* enable DTR mode and set the DC value */ ++ regval |= (1 << DTR_MODE_REQUEST_SHIFT); ++ fmc_write(host, FMC_GLOBAL_CFG, regval); ++ fmc_pr(DTR_DB, " enable dtr mode[%#x]%#x\n", ++ FMC_GLOBAL_CFG, regval); ++ } else { ++ /* disable DTR mode */ ++ regval &= (~(1 << DTR_MODE_REQUEST_SHIFT)); ++ fmc_write(host, FMC_GLOBAL_CFG, regval); ++ fmc_pr(DTR_DB, " disable dtr mode[%#x]%#x\n", ++ FMC_GLOBAL_CFG, regval); ++ } ++} ++ ++void fmc_check_spi_dtr_support(struct fmc_spi *spi, u_char *ids, int len) ++{ ++ unsigned char manu_id; ++ unsigned char dev_id; ++ ++ if (len < 2) ++ return; ++ ++ manu_id = ids[0]; ++ dev_id = ids[1]; ++ ++ spi->dtr_mode_support = 0; ++ spi->dtr_cookie = DTR_MODE_SET_NONE; ++ ++ fmc_pr(DTR_DB, "The Double Transfer Rate Read Mode isn't supported.\n"); ++ return; ++ ++dtr_on: ++ fmc_pr(FMC_INFO, "The Double Transfer Rate Read Mode is supported.\n"); ++} ++#endif /* CONFIG_DTR_MODE_SUPPORT */ ++ ++void fmc100_op_reg(struct fmc_spi *spi, unsigned char opcode, ++ unsigned int len, unsigned char optype) ++{ ++ struct fmc_host *host = NULL; ++ u32 regval; ++ ++ if (!spi || !spi->host) ++ return; ++ ++ host = (struct fmc_host *)spi->host; ++ regval = fmc_cmd_cmd1(opcode); ++ fmc_write(host, FMC_CMD, regval); ++ ++ regval = fmc_data_num_cnt(len); ++ fmc_write(host, FMC_DATA_NUM, regval); ++ ++ regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, regval); ++ ++ regval = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START | optype; ++ fmc_write(host, FMC_OP, regval); ++ ++ fmc_cmd_wait_cpu_finish(host); ++} +diff --git a/drivers/mtd/spi/fmc100/fmc100.h b/drivers/mtd/spi/fmc100/fmc100.h +new file mode 100644 +index 0000000..dd818c6 +--- /dev/null ++++ b/drivers/mtd/spi/fmc100/fmc100.h +@@ -0,0 +1,177 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __FMC100_H__ ++#define __FMC100_H__ ++ ++#include ++#include ++#include "../../fmc_spi_ids.h" ++#include ++#include "securec.h" ++ ++/* These macroes are for debug only, reg read is slower then dma read, ++ so we don't define it */ ++#undef FMC100_SPI_NOR_SUPPORT_REG_READ ++#undef FMC100_SPI_NOR_SUPPORT_REG_WRITE ++ ++#define FMC100_DMA_WR_MAX_SIZE 4096 ++#define FMC100_DMA_WR_MASK (FMC100_DMA_WR_MAX_SIZE - 1) ++#define FMC100_DMA_RD_MAX_SIZE (_2M) ++#define FMC100_DMA_RD_MASK (FMC100_DMA_RD_MAX_SIZE - 1) ++#define FMC100_REG_RD_MAX_SIZE (_16K) ++#define FMC100_REG_RD_MASK (FMC100_REG_RD_MAX_SIZE - 1) ++ ++#define SPI_NOR_CR_SHIFT 8 /* Config Register shift(bit) */ ++ ++#define SPI_NOR_CR_4BYTE_SHIFT 5 ++#define SPI_NOR_CR_4BYTE_MASK (1 << SPI_NOR_CR_4BYTE_SHIFT) ++#define spi_nor_get_4byte_by_cr(cr) (((cr) & SPI_NOR_CR_4BYTE_MASK) \ ++ >> SPI_NOR_CR_4BYTE_SHIFT) ++ ++#define SPI_NOR_CR_QE_SHIFT 1 ++#define SPI_NOR_CR_QE_MASK (1 << SPI_NOR_CR_QE_SHIFT) ++#define spi_nor_get_qe_by_cr(cr) (((cr) & SPI_NOR_CR_QE_MASK) \ ++ >> SPI_NOR_CR_QE_SHIFT) ++ ++#define SPI_NOR_CR_RST_HOLD_SHIFT 7 ++#define SPI_NOR_CR_RST_HOLD_MASK (1 << SPI_NOR_CR_RST_HOLD_SHIFT) ++#define SPI_NOR_CR_HOLD_MASK (~(1 << SPI_NOR_CR_RST_HOLD_SHIFT)) ++#define spi_nor_get_rst_hold_by_cr(cr) (((cr) & SPI_NOR_CR_RST_HOLD_MASK) \ ++ >> SPI_NOR_CR_RST_HOLD_SHIFT) ++#define spi_nor_set_rst_by_cr(cr) ((cr) | SPI_NOR_CR_RST_HOLD_MASK) ++#define spi_nor_set_hold_by_cr(cr) ((cr) & SPI_NOR_CR_HOLD_MASK) ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++#define DEBUG_SPI_NOR_BP 0 ++ ++#define SPI_NOR_SR_SRWD_SHIFT 7 ++#define SPI_NOR_SR_SRWD_MASK (1 << SPI_NOR_SR_SRWD_SHIFT) ++ ++#define SPI_NOR_SR_BP0_SHIFT 2 ++#define SPI_NOR_SR_BP_WIDTH_4 0xf ++#define SPI_NOR_SR_BP_MASK_4 (SPI_NOR_SR_BP_WIDTH_4 << SPI_NOR_SR_BP0_SHIFT) ++ ++#define SPI_NOR_SR_BP_WIDTH_3 0x7 ++#define SPI_NOR_SR_BP_MASK_3 (SPI_NOR_SR_BP_WIDTH_3 << SPI_NOR_SR_BP0_SHIFT) ++ ++#define SPI_NOR_SR_TB_SHIFT 3 ++#define SPI_NOR_SR_TB_MASK (1 << SPI_NOR_SR_TB_SHIFT) ++ ++#define SPI_NOR_SR_TB_SHIFT_S 5 ++#define SPI_NOR_SR_TB_MASK_S (1 << SPI_NOR_SR_TB_SHIFT_S) ++ ++#define spi_bp_bottom_rdcr_set_s(config) ((config) | \ ++ (0x01 << SPI_NOR_SR_TB_SHIFT_S)) ++#define spi_bp_bottom_rdcr_set(config) ((config) | \ ++ (0x01 << SPI_NOR_SR_TB_SHIFT)) ++ ++#define spi_bp_bottom_rdsr_set_1(bp_num) (0x1 << (2 + bp_num)) ++#define spi_bp_bottom_rdsr_set_0(bp_num) (~(0x1 << (2 + bp_num))) ++ ++#define lock_level_max(bp_num) (((0x01) << bp_num) - 1) ++ ++#endif /* CONFIG_SPI_BLOCK_PROTECT */ ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++#define DTR_DUMMY_CYCLES_6 6 ++#define DTR_DUMMY_CYCLES_8 8 ++#define DTR_DUMMY_CYCLES_10 10 ++#define dtr_rdcr_dc_mask(_val) (_val) ++#define DTR_RDSR_DC_SHIFT 14 ++#define DTR_RDCR_DC_SHIFT 6 ++#define dtr_rdcr_dc_bit_clr(_reg) ((_reg) & (~(3 << DTR_RDSR_DC_SHIFT))) ++#define DTR_MODE_REQUEST_SHIFT 11 ++ ++#define DTR_TRAINING_POINT_NUM 12 ++#define DTR_TRAINING_POINT_MASK 12 ++#define dtr_training_point_clr(_reg) ((_reg) & (~(0xf << 12))) ++#define DTR_TRAINING_CMP_ADDR_SHIFT (0) ++#define DTR_TRAINING_CMP_ADDR_S (CONFIG_SYS_TEXT_BASE_ORI + \ ++ DTR_TRAINING_CMP_ADDR_SHIFT) ++#define DTR_TRAINING_CMP_LEN 0x100 ++#define SFDP_BUF_LEN 0x33 ++#define SFDP_DTR_BIT_SHIFT 3 ++#define SFDP_DTR_BYTE_SHIFT 0x32 ++#define SFDP_DTR_BIT_MASK 0x1 ++#endif /* CONFIG_DTR_MODE_SUPPORT */ ++ ++#define CR_DUMMY_CYCLE (0x03 << 6) ++#define DTR_MODE_REQUEST_SHIFT 11 ++#define SPI_NOR_SR_WIP_MASK (1 << 0) ++ ++struct fmc_host { ++ struct spi_flash spi_nor_flash[1]; ++ struct mtd_info_ex *spi_nor_info; ++ struct fmc_spi spi[CONFIG_SPI_NOR_MAX_CHIP_NUM]; ++ ++ void *regbase; ++ void *iobase; ++ ++ void (*set_system_clock)(struct spi_op *op, int clk_en); ++ void (*set_host_addr_mode)(struct fmc_host *host, int enable); ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ unsigned int start_addr; ++ unsigned int end_addr; ++ unsigned char cmp; ++ unsigned int bp_num; ++ /* the BT bit location, decide the data num count */ ++ unsigned int bt_loc; ++ unsigned char level; ++#endif ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ unsigned int dtr_mode_en; ++ unsigned int dtr_training_flag; ++#endif ++}; ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++unsigned short fmc100_set_spi_lock_info(struct fmc_host *host); ++void fmc100_get_bp_lock_level(struct fmc_host *host); ++void fmc100_spi_lock(struct fmc_host *host, unsigned char level); ++void fmc100_spi_flash_lock(unsigned char cmp, unsigned char level, ++ unsigned char op); ++unsigned short fmc100_handle_bp_rdcr_info(struct fmc_host *host, ++ u_char cmd); ++unsigned char fmc100_bp_to_level(struct fmc_host *host); ++unsigned short fmc100_handle_bp_rdsr_info(struct fmc_host *host, ++ u_char cmd); ++#endif ++unsigned char spi_general_get_flash_register(struct fmc_spi *spi, ++ u_char cmd); ++ ++#define spiflash_to_host(_spiflash) ((struct fmc_host *)(_spiflash)) ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++void fmc_dtr_mode_ctrl(struct fmc_spi *spi, int dtr_en); ++unsigned int spi_dtr_training(struct fmc_host *host); ++void spi_dtr_to_sdr_switch(struct fmc_spi *spi); ++int spi_dtr_dummy_training_set(struct fmc_host *host, int dtr_en); ++void fmc_check_spi_dtr_support(struct fmc_spi *spi, u_char *ids, int len); ++#endif ++ ++void fmc100_read_ids(const struct fmc_spi *, u_char, u_char* const); ++void fmc100_op_reg(struct fmc_spi *spi, unsigned char opcode, ++ unsigned int len, unsigned char optype); ++int fmc_spi_nor_probe(struct mtd_info_ex *mtd, struct fmc_spi *spi); ++int fmc100_spi_nor_init(struct fmc_host *); ++struct spi_flash *fmc100_spi_nor_scan(struct fmc_host *host); ++ ++#endif /* End of __FMC100_H__ */ +diff --git a/drivers/mtd/spi/fmc100/fmc100_os.c b/drivers/mtd/spi/fmc100/fmc100_os.c +new file mode 100644 +index 0000000..51535c1 +--- /dev/null ++++ b/drivers/mtd/spi/fmc100/fmc100_os.c +@@ -0,0 +1,202 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "fmc100_os.h" ++#include ++#include ++#include ++ ++static struct fmc_host fmc100_host; ++static struct mtd_info_ex fmc100_spi_nor_info = {.type = 0, }; ++ ++static void fmc100_driver_shutdown(void) ++{ ++ unsigned int start_up_addr_mode = get_fmc_boot_mode(); ++ if (start_up_addr_mode == SPI_NOR_ADDR_MODE_3_BYTES) { ++ int ix; ++ struct fmc_host *host = &fmc100_host; ++ struct fmc_spi *spi = host->spi; ++ struct mtd_info_ex *spi_nor_info = &fmc100_spi_nor_info; ++ ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NOR); ++ ++ for (ix = 0; ix < spi_nor_info->numchips; ix++, spi++) { ++ /* 4 byte addr mode */ ++ if (spi->addrcycle == 4) { ++ spi->driver->wait_ready(spi); ++ spi->driver->entry_4addr(spi, DISABLE); ++ } ++ } ++ } ++} ++ ++static int fmc100_driver_probe(void) ++{ ++ int ret; ++ struct fmc_host *host = &fmc100_host; ++ ++ fmc_pr(BT_DBG, "\t|*-Start SPI nor flash driver probe\n"); ++ ++ /* FMC ip version check */ ++ ret = fmc_ip_ver_check(); ++ if (ret) { ++ fmc_pr(BT_DBG, "\t|*-IP version unknown, result: %d\n", ret); ++ return ret; ++ } ++ ++ fmc_pr(BT_DBG, "\t||-SPI nor host init\n"); ++ ret = memset_s((char *)host, sizeof(struct fmc_host), 0, sizeof(struct fmc_host)); ++ if (ret) { ++ fmc_pr(BT_DBG, "Error: host buf memset_s failed\n"); ++ return -ret; ++ } ++ ret = fmc100_spi_nor_init(host); ++ if (ret) { ++ fmc_pr(BT_DBG, "Error: SPI Nor init failed, ret: %d\n", ret); ++ goto end; ++ } ++ ++end: ++ fmc_pr(BT_DBG, "\t|*-End SPI nor flash driver probe\n"); ++ ++ return ret; ++} ++ ++struct mtd_info_ex *fmc100_get_spi_nor_info(struct spi_flash *spi_nor_flash) ++{ ++ if (fmc100_spi_nor_info.type == 0) { ++ if (fmc100_spi_nor_probe(NULL) == NULL) ++ return NULL; ++ } ++ ++ return &fmc100_spi_nor_info; ++} ++ ++static void fmc100_probe_spi_size(struct spi_flash* const spi_nor_flash) ++{ ++ struct fmc_host *host = &fmc100_host; ++ struct fmc_spi *spi = host->spi; ++ unsigned int ix; ++ unsigned int total = 0; ++ struct mtd_info_ex *spi_nor_info = host->spi_nor_info; ++ ++ fmc_pr(BT_DBG, "\t|*-Start probe SPI nor flash total size\n"); ++ for (ix = 0; ix < spi_nor_info->numchips; ix++, spi++) { ++ fmc_pr(BT_DBG, "\t||-SPI nor flash[%d]: %dMB\n", ix, ++ (u_int)byte_to_mb(spi->chipsize)); ++ total += spi->chipsize; ++ } ++ ++ spi_nor_flash->size = total; ++ ++ fmc_pr(BT_DBG, "\t|*-Probe SPI nor total size: %dMB, chip num: %d\n", ++ byte_to_mb(spi_nor_flash->size), spi_nor_info->numchips); ++} ++ ++struct spi_flash *fmc100_spi_nor_probe(struct mtd_info_ex **spi_nor_info) ++{ ++ static struct spi_flash *spi_nor_flash = NULL; ++ ++ fmc_pr(BT_DBG, "\t*-Start SPI Nor flash probe\n"); ++ ++ if (spi_nor_flash) { ++ fmc_pr(BT_DBG, "\t*-SPI Nor flash is initialized.\n"); ++ return spi_nor_flash; ++ } ++ ++ /* Check current SPI device type whether SPI nor */ ++ fmc_dev_type_switch(FLASH_TYPE_SPI_NOR); ++ ++ fmc_pr(BT_DBG, "\t|-SPI Nor flash driver probe\n"); ++ if (!fmc100_driver_probe()) { ++ struct fmc_host *host = &fmc100_host; ++ ++ fmc_pr(BT_DBG, "\t|-SPI nor flash scanning\n"); ++ host->spi_nor_info = &fmc100_spi_nor_info; ++ spi_nor_flash = fmc100_spi_nor_scan(host); ++ if (spi_nor_flash) { ++ *spi_nor_info = ++ fmc100_get_spi_nor_info(spi_nor_flash); ++ if (*spi_nor_info == NULL) ++ return NULL; ++ ++ fmc100_probe_spi_size(spi_nor_flash); ++ fmc_pr(FMC_INFO, "SPI Nor total size: %uMB\n", ++ byte_to_mb(spi_nor_flash->size)); ++ fmc_pr(BT_DBG, "\t|-Add func hook for Reset cmd\n"); ++ add_shutdown(fmc100_driver_shutdown); ++ ++ goto end; ++ } ++ } ++ ++ spi_nor_flash = NULL; ++ fmc100_spi_nor_info.type = 0; ++ ++end: ++ /* Change SPI device type to default */ ++ fmc_dev_type_switch(FLASH_TYPE_DEFAULT); ++ ++ fmc_pr(BT_DBG, "\t*-End SPI Nor flash probe\n"); ++ ++ return spi_nor_flash; ++} ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++void fmc100_spi_flash_lock(unsigned char cmp, unsigned char level, ++ unsigned char op) ++{ ++ struct fmc_host *host = &fmc100_host; ++ struct spi_flash *nor = host->spi_nor_flash; ++ ++ host->cmp = cmp; ++ ++ if (op == BP_OP_GET) { ++ puts("Get spi lock information\n"); ++ if (host->level) { ++ if (host->level == nor->bp_level_max) ++ puts("all blocks are locked.\n"); ++ else ++ printf("level: %d\n", host->level); ++ printf("Spi is locked. lock address[0 => %#x]\n", ++ host->end_addr); ++ } else { ++ puts("all blocks are unlocked.\n"); ++ } ++ ++ return; ++ } ++ ++ if (op == BP_OP_SET) { ++ if (level) { ++ if (level == nor->bp_level_max) ++ puts("lock all blocks.\n"); ++ else ++ printf("lock level: %d\n", level); ++ } else { ++ puts("unlock all block.\n"); ++ } ++ ++ fmc100_spi_lock(host, level); ++ return; ++ } ++ ++ printf("%s ERROR: Invalid optin argument!", __func__); ++} ++#endif /* CONFIG_SPI_BLOCK_PROTECT */ +diff --git a/drivers/mtd/spi/fmc100/fmc100_os.h b/drivers/mtd/spi/fmc100/fmc100_os.h +new file mode 100644 +index 0000000..e0eb096 +--- /dev/null ++++ b/drivers/mtd/spi/fmc100/fmc100_os.h +@@ -0,0 +1,28 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __FMC100_OS_H__ ++#define __FMC100_OS_H__ ++ ++#include "fmc100.h" ++#include "../vendor_spi.h" ++ ++/*****************************************************************************/ ++#endif /* End of __FMC100_OS_H__ */ ++ +diff --git a/drivers/mtd/spi/fmc100/fmc100_spi_general.c b/drivers/mtd/spi/fmc100/fmc100_spi_general.c +new file mode 100644 +index 0000000..e7c2a05 +--- /dev/null ++++ b/drivers/mtd/spi/fmc100/fmc100_spi_general.c +@@ -0,0 +1,416 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++/* ++ Get status/config register value from SPI Nor flash ++*/ ++unsigned char spi_general_get_flash_register(struct fmc_spi * const spi, u_char cmd) ++{ ++ unsigned char status; ++ unsigned int reg; ++ ++ struct fmc_host *host = (struct fmc_host *)spi->host; ++ ++ host->set_system_clock(NULL, ENABLE); ++ ++ fmc_pr(SR_DBG, "\t * Start get flash Register[%#x]\n", cmd); ++ ++ reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ ++ if (cmd == SPI_CMD_RDSR) { ++ reg = fmc_op_read_status_en(ENABLE) | FMC_OP_REG_OP_START; ++ goto cmd_config_done; ++ } ++ ++ fmc_write(host, FMC_CMD, cmd); ++ fmc_pr(SR_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, cmd); ++ ++ reg = fmc_data_num_cnt(SPI_NOR_CR_LEN); ++ fmc_write(host, FMC_DATA_NUM, reg); ++ fmc_pr(SR_DBG, "\t Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | fmc_op_read_data_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ ++cmd_config_done: ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(SR_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ if (cmd == SPI_CMD_RDSR) ++ status = fmc_read(host, FMC_STATUS); ++ else ++ status = readb(host->iobase); ++ fmc_pr(SR_DBG, "\t * End get flash Register[%#x], val: %#x\n", cmd, ++ status); ++ ++ return status; ++} ++ ++/* ++ Read status[C0H]:[0]bit OIP, judge whether the device is busy or not ++*/ ++static int spi_general_wait_ready(struct fmc_spi * const spi) ++{ ++ unsigned char status; ++ /* need a big number,so move left 20 bit */ ++ unsigned int deadline = 1 << 20; ++ if (!spi || !spi->host) ++ return -1; ++ do { ++ status = spi_general_get_flash_register(spi, SPI_CMD_RDSR); ++ if (!(status & SPI_NOR_SR_WIP_MASK)) ++ return 0; ++ ++ udelay(1); /* delay 1 us */ ++ } while (deadline--); ++ ++ db_msg("Error: SPI nor wait ready timeout, status[%#x]\n", status); ++ ++ return 1; ++} ++ ++/* ++ Send write enable cmd to SPI Nor, status[C0H]:[2]bit WEL must be set 1 ++*/ ++static int spi_general_write_enable(struct fmc_spi *spi) ++{ ++ unsigned char status; ++ unsigned int reg; ++ if (!spi || !spi->driver || !spi->host) { ++ printf("%s:spi data is NULL, please check input parameter\n", __func__); ++ return -1; ++ } ++ struct fmc_host *host = (struct fmc_host *)spi->host; ++ ++ if (WE_DBG) ++ printf("\n"); ++ fmc_pr(WE_DBG, "\t * Start Write Enable\n"); ++ ++ status = spi_general_get_flash_register(spi, SPI_CMD_RDSR); ++ fmc_pr(WE_DBG, "\t Read Status Register[%#x]:%#x\n", SPI_CMD_RDSR, ++ status); ++ if (status & STATUS_WEL_MASK) { ++ fmc_pr(WE_DBG, "\t Write Enable was opened! reg: %#x\n", ++ status); ++ return 0; ++ } ++ ++ reg = fmc_read(host, FMC_GLOBAL_CFG); ++ if (reg & FMC_GLOBAL_CFG_WP_ENABLE) { ++ reg &= ~FMC_GLOBAL_CFG_WP_ENABLE; ++ fmc_write(host, FMC_GLOBAL_CFG, reg); ++ fmc_pr(WE_DBG, "\t Set GLOBAL_CFG[%#x]%#x\n", ++ FMC_GLOBAL_CFG, reg); ++ } ++ ++ reg = fmc_cmd_cmd1(SPI_CMD_WREN); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(WE_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(WE_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(WE_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ spi->driver->wait_ready(spi); ++ ++ reg = spi_general_get_flash_register(spi, SPI_CMD_RDSR); ++ if (reg & STATUS_WEL_MASK) { ++ fmc_pr(WE_DBG, "\t Write Enable success.reg: %#x\n", reg); ++ } else { ++ db_msg("Error: Write Enable failed! status: %#x\n", reg); ++ return status; ++ } ++ ++ fmc_pr(WE_DBG, "\t * End Write Enable\n"); ++ ++ return 0; ++} ++ ++/* ++ enable 4byte address for SPI which memory more than 16M ++*/ ++static int spi_general_entry_4addr(struct fmc_spi *spi, int enable) ++{ ++ unsigned char status; ++ unsigned int reg; ++ const char *str[] = {"Disable", "Enable"}; ++ struct fmc_host *host = NULL; ++ if (!spi || !spi->driver) ++ return -1; ++ host = (struct fmc_host*)spi->host; ++ if (!host) ++ return -1; ++ fmc_pr(AC_DBG, "\t* Start SPI Nor flash %s 4-byte mode.\n", ++ str[enable]); ++ ++ if (spi->addrcycle != SPI_NOR_4BYTE_ADDR_LEN) { ++ fmc_pr(AC_DBG, "\t* Flash isn't support entry 4-byte mode.\n"); ++ return 0; ++ } ++ ++ status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3); ++ fmc_pr(AC_DBG, "\t Read Status Register-3[%#x]:%#x\n", SPI_CMD_RDSR3, ++ status); ++ if (spi_nor_get_4byte_by_cr(status) == enable) { ++ fmc_pr(AC_DBG, "\t* 4-byte was %sd, reg:%#x\n", str[enable], ++ status); ++ return 0; ++ } ++ ++ if (enable) ++ reg = SPI_CMD_EN4B; ++ else ++ reg = SPI_CMD_EX4B; ++ fmc_write(host, FMC_CMD, fmc_cmd_cmd1(reg)); ++ fmc_pr(AC_DBG, "\t Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(AC_DBG, "\t Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(AC_DBG, "\t Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++ ++ spi->driver->wait_ready(spi); ++ ++ status = spi_general_get_flash_register(spi, SPI_CMD_RDSR3); ++ fmc_pr(AC_DBG, "\t Read SR-3[%#x]:%#x\n", SPI_CMD_RDSR3, ++ status); ++ if (spi_nor_get_4byte_by_cr(status) != enable) { ++ db_msg("Error: %s 4-byte failed! SR3:%#x\n", ++ str[enable], status); ++ return status; ++ } ++ ++ fmc_pr(AC_DBG, "\t %s 4-byte success, SR3:%#x\n", str[enable], status); ++ fmc_pr(AC_DBG, "\t* End SPI Nor flash %s 4-byte mode.\n", str[enable]); ++ ++ return 0; ++} ++ ++/* ++ judge whether SPI Nor support QUAD read write or not ++*/ ++unsigned char spi_is_quad(const struct fmc_spi * const spi) ++{ ++ char *const if_str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO", "DTR"}; ++ if (!spi || !spi->read || !spi->write) { ++ printf("%s:spi is NULL, please check input parameter\n", __func__); ++ return 0; ++ } ++ ++ fmc_pr(QE_DBG, "\t\t|*-SPI read iftype: %s write iftype: %s\n", ++ if_str[spi->read->iftype], if_str[spi->write->iftype]); ++ ++ if ((spi->read->iftype == IF_TYPE_QUAD) || ++ (spi->read->iftype == IF_TYPE_QIO) || ++ (spi->write->iftype == IF_TYPE_QUAD) || ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ (spi->read->iftype == IF_TYPE_DTR) || ++#endif ++ (spi->write->iftype == IF_TYPE_QIO) ++ ) ++ return 1; ++ ++ return 0; ++} ++ ++static void spi_general_set_cmd(struct fmc_spi * const spi) ++{ ++ unsigned int reg; ++ struct fmc_host *host = (struct fmc_host *)spi->host; ++ ++ reg = fmc_cmd_cmd1(SPI_CMD_WRSR); ++ fmc_write(host, FMC_CMD, reg); ++ fmc_pr(QE_DBG, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, reg); ++ ++ reg = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, reg); ++ fmc_pr(QE_DBG, "\t|-Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, reg); ++ ++ reg = fmc_data_num_cnt(SPI_NOR_SR_LEN + SPI_NOR_CR_LEN); ++ fmc_write(host, FMC_DATA_NUM, reg); ++ fmc_pr(QE_DBG, "\t|-Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, reg); ++ ++ reg = fmc_op_cmd1_en(ENABLE) | ++ fmc_op_write_data_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, reg); ++ fmc_pr(QE_DBG, "\t|-Set OP[%#x]%#x\n", FMC_OP, reg); ++ ++ fmc_cmd_wait_cpu_finish(host); ++} ++/* ++ * enable QE bit if QUAD read write is supported by SPI ++ */ ++static int spi_general_qe_enable(struct fmc_spi *spi) ++{ ++ unsigned char status; ++ unsigned char config; ++ unsigned char op; ++ ++ const char *str[] = {"Disable", "Enable"}; ++ struct fmc_host *host = NULL; ++ if (!spi || !spi->driver) ++ return -1; ++ host = (struct fmc_host *)spi->host; ++ if (!host || !host->iobase) ++ return -1; ++ op = spi_is_quad(spi); ++ ++ fmc_pr(QE_DBG, "\t*-Start SPI Nor %s Quad.\n", str[op]); ++ ++ config = spi_general_get_flash_register(spi, SPI_CMD_RDCR); ++ fmc_pr(QE_DBG, "\t|-Read Config Register[%#x]%#x\n", SPI_CMD_RDCR, ++ config); ++ if (op == spi_nor_get_qe_by_cr(config)) { ++ fmc_pr(QE_DBG, "\t* Quad was %sd, config:%#x\n", str[op], ++ config); ++ return op; ++ } ++ ++ status = spi_general_get_flash_register(spi, SPI_CMD_RDSR); ++ fmc_pr(QE_DBG, "\t|-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR, ++ status); ++ ++ spi->driver->write_enable(spi); ++ ++ if (op) ++ config |= SPI_NOR_CR_QE_MASK; ++ else ++ config &= ~SPI_NOR_CR_QE_MASK; ++ writeb(status, host->iobase); ++ writeb(config, host->iobase + SPI_NOR_SR_LEN); ++ fmc_pr(QE_DBG, "\t|-Write IO[%p]%#x\n", host->iobase, ++ *(unsigned short *)host->iobase); ++ ++ spi_general_set_cmd(spi); ++ ++ spi->driver->wait_ready(spi); ++ ++ config = spi_general_get_flash_register(spi, SPI_CMD_RDCR); ++ if (op == spi_nor_get_qe_by_cr(config)) { ++ fmc_pr(QE_DBG, "\t|-%s Quad success, config: %#x\n", str[op], ++ config); ++ } else { ++ db_msg("Error: %s Quad failed! reg: %#x\n", str[op], config); ++ } ++ ++ fmc_pr(QE_DBG, "\t* End SPI Nor %s Quad.\n", str[op]); ++ ++ return op; ++} ++ ++/* ++ some chip don't QUAD enable ++*/ ++static int spi_do_not_qe_enable(struct fmc_spi *spi) ++{ ++ return 0; ++} ++ ++static void reset_pin_enable_fmc_op(struct fmc_host* const host, ++ struct fmc_spi* const spi) ++{ ++ unsigned int regval; ++ ++ regval = fmc_cmd_cmd1(SPI_CMD_WRSR3); ++ fmc_write(host, FMC_CMD, regval); ++ fmc_pr(RST_DB, "\t|-Set CMD[%#x]%#x\n", FMC_CMD, regval); ++ ++ regval = op_cfg_fm_cs(spi->chipselect) | OP_CFG_OEN_EN; ++ fmc_write(host, FMC_OP_CFG, regval); ++ fmc_pr(RST_DB, "\t|-Set OP_CFG[%#x]%#x\n", ++ FMC_OP_CFG, regval); ++ ++ regval = fmc_data_num_cnt(SPI_NOR_CR_LEN); ++ fmc_write(host, FMC_DATA_NUM, regval); ++ fmc_pr(RST_DB, "\t|-Set DATA_NUM[%#x]%#x\n", ++ FMC_DATA_NUM, regval); ++ ++ regval = fmc_op_cmd1_en(ENABLE) | ++ fmc_op_write_data_en(ENABLE) | ++ FMC_OP_REG_OP_START; ++ fmc_write(host, FMC_OP, regval); ++ fmc_pr(RST_DB, "\t|-Set OP[%#x]%#x\n", FMC_OP, regval); ++ ++ fmc_cmd_wait_cpu_finish(host); ++} ++ ++/* ++ * some chip set the mux HOLD#/RESET#/IO3 pin to RESET#, as it is HOLD# default. ++ */ ++__maybe_unused static void spi_nor_reset_pin_enable(struct fmc_spi *spi, int enable) ++{ ++ unsigned char config; ++ const char *str[] = {"HOLD#", "RESET#"}; ++ struct fmc_host *host = NULL; ++ ++ if (!spi || !spi->driver) ++ return; ++ ++ host = (struct fmc_host *)spi->host; ++ if (!host || !host->iobase) ++ return; ++ ++ config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3); ++ fmc_pr(RST_DB, "\t|-Read SR-3[%#x], val: %#x\n", ++ SPI_CMD_RDSR3, config); ++ ++ if (enable == spi_nor_get_rst_hold_by_cr(config)) { ++ fmc_pr(RST_DB, " Device has worked on %s.\n", str[enable]); ++ return; ++ } ++ ++ fmc_pr(RST_DB, " Start to enable %s function.\n", str[enable]); ++ spi->driver->write_enable(spi); ++ ++ if (enable) ++ config = spi_nor_set_rst_by_cr(config); ++ else ++ config = spi_nor_set_hold_by_cr(config); ++ ++ writeb(config, host->iobase); ++ fmc_pr(RST_DB, "\t|-Write IO[%p]%#x\n", host->iobase, ++ *(unsigned char *)host->iobase); ++ ++ reset_pin_enable_fmc_op(host, spi); ++ ++ spi->driver->wait_ready(spi); ++ ++ config = spi_general_get_flash_register(spi, SPI_CMD_RDSR3); ++ fmc_pr(RST_DB, "\t|-Read SR-3[%#x], val: %#x\n", ++ SPI_CMD_RDSR3, config); ++ if (enable == spi_nor_get_rst_hold_by_cr(config)) ++ fmc_pr(RST_DB, "\t|- Set the MUX pin to RESET# success!\n"); ++ else ++ fmc_pr(RST_DB, "\t|- The MUX pin works on HOLD# or DNU!\n"); ++} +diff --git a/drivers/mtd/spi/fmc100/fmc_spi_nor_ids.c b/drivers/mtd/spi/fmc100/fmc_spi_nor_ids.c +new file mode 100644 +index 0000000..26aa9d3 +--- /dev/null ++++ b/drivers/mtd/spi/fmc100/fmc_spi_nor_ids.c +@@ -0,0 +1,436 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include "fmc100.h" ++ ++struct spi_nor_info *spiinfo; ++ ++#include "fmc100_spi_general.c" ++__maybe_unused static struct spi_drv spi_driver_general = { ++ .wait_ready = spi_general_wait_ready, ++ .write_enable = spi_general_write_enable, ++ .entry_4addr = spi_general_entry_4addr, ++ .qe_enable = spi_general_qe_enable, ++}; ++ ++__maybe_unused static struct spi_drv spi_driver_no_qe = { ++ .wait_ready = spi_general_wait_ready, ++ .write_enable = spi_general_write_enable, ++ .entry_4addr = spi_general_entry_4addr, ++ .qe_enable = spi_do_not_qe_enable, ++}; ++ ++#define SPI_NOR_ID_TBL_VER "1.0" ++ ++/****************************************************************************** ++ * We do not guarantee the compatibility of the following device models in the ++ * table.Device compatibility is based solely on the list of compatible devices ++ * in the release package. ++ ******************************************************************************/ ++ ++static struct spi_nor_info fmc_spi_nor_info_table[] = { ++ ++ {0, {0}, 0, 0, 0, 0, {0}, {0}, {0}, NULL}, ++}; ++ ++static struct spi_nor_info *fmc_spi_nor_serach_ids(u_char* const ids, int len) ++{ ++ struct spi_nor_info *info = fmc_spi_nor_info_table; ++ struct spi_nor_info *fit_info = NULL; ++ ++ if (len <= 0) ++ return NULL; ++ ++ for (; info->name; info++) { ++ if (memcmp(info->id, ids, info->id_len)) ++ continue; ++ ++ if ((fit_info == NULL) || (fit_info->id_len < info->id_len)) ++ fit_info = info; ++ } ++ return fit_info; ++} ++ ++static void fmc_spi_nor_search_rw(struct spi_nor_info *info, ++ struct spi_op *spiop_rw, ++ u_int iftype, ++ u_int max_dummy, ++ int rw_type) ++{ ++ int ix = 0; ++ struct spi_op **spiop, **fitspiop; ++ ++ for (fitspiop = spiop = (rw_type ? info->write : info->read); ++ (*spiop) && ix < MAX_SPI_OP; spiop++, ix++) ++ if (((*spiop)->iftype & iftype) && ++ ((*spiop)->dummy <= max_dummy) && ++ ((*fitspiop)->iftype < (*spiop)->iftype)) ++ fitspiop = spiop; ++ ++ if (memcpy_s(spiop_rw, sizeof(struct spi_op), (*fitspiop), sizeof(struct spi_op))) ++ printf("%s %d ERR:memcpy_s fail !\n", __func__, __LINE__); ++} ++ ++static void fmc_map_iftype_and_clock(struct fmc_spi *spi) ++{ ++ int ix; ++ const int iftype_read[] = { ++ SPI_IF_READ_STD, IF_TYPE_STD, ++ SPI_IF_READ_FAST, IF_TYPE_STD, ++ SPI_IF_READ_DUAL, IF_TYPE_DUAL, ++ SPI_IF_READ_DUAL_ADDR, IF_TYPE_DIO, ++ SPI_IF_READ_QUAD, IF_TYPE_QUAD, ++ SPI_IF_READ_QUAD_ADDR, IF_TYPE_QIO, ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ SPI_IF_READ_QUAD_DTR, IF_TYPE_DTR, ++#endif ++ 0, 0, ++ }; ++ const int iftype_write[] = { ++ SPI_IF_WRITE_STD, IF_TYPE_STD, ++ SPI_IF_WRITE_DUAL, IF_TYPE_DUAL, ++ SPI_IF_WRITE_DUAL_ADDR, IF_TYPE_DIO, ++ SPI_IF_WRITE_QUAD, IF_TYPE_QUAD, ++ SPI_IF_WRITE_QUAD_ADDR, IF_TYPE_QIO, ++ 0, 0, ++ }; ++ ++ if (!spi->write || !spi->read || !spi->erase) { ++ printf("spi nor err:spi->func is null!\n"); ++ return; ++ } ++ /* Only an even number of values is required,so increase length is 2 */ ++ for (ix = 0; iftype_write[ix]; ix += 2) { ++ if (spi->write->iftype == iftype_write[ix]) { ++ spi->write->iftype = iftype_write[ix + 1]; ++ break; ++ } ++ } ++ fmc_get_fmc_best_2x_clock(&spi->write->clock); ++ ++ /* Only an even number of values is required,so increase length is 2 */ ++ for (ix = 0; iftype_read[ix]; ix += 2) { ++ if (spi->read->iftype == iftype_read[ix]) { ++ spi->read->iftype = iftype_read[ix + 1]; ++ break; ++ } ++ } ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ if (spi->dtr_mode_support) ++ /* get the div4 clock */ ++ fmc_get_fmc_best_4x_clock(&spi->read->clock); ++ else ++ fmc_get_fmc_best_2x_clock(&spi->read->clock); ++#else ++ fmc_get_fmc_best_2x_clock(&spi->read->clock); ++#endif ++ ++ fmc_get_fmc_best_2x_clock(&spi->erase->clock); ++ spi->erase->iftype = IF_TYPE_STD; ++} ++ ++void fmc_spi_nor_get_erase(struct spi_nor_info *info, ++ struct spi_op *spiop_erase) ++{ ++ int ix; ++ if (!info || !spiop_erase) ++ return; ++ ++ spiop_erase->size = 0; ++ for (ix = 0; ix < MAX_SPI_OP; ix++) { ++ if (info->erase[ix] == NULL) ++ break; ++ if (info->erasesize == info->erase[ix]->size) { ++ if (memcpy_s(&spiop_erase[ix], sizeof(struct spi_op), ++ info->erase[ix], sizeof(struct spi_op))) ++ printf("%s %d ERR:memcpy_s fail !\n", __func__, __LINE__); ++ break; ++ } ++ } ++} ++ ++static void switch_to_4byte(struct fmc_spi* const spi, u_char* const ids, int len) ++{ ++ /* auto check fmc_addr_mode 3 bytes or 4 bytes */ ++ unsigned int start_up_addr_mode = get_fmc_boot_mode(); ++ ++ if (len < 3) ++ return; ++ ++ if ((spi->addrcycle == SPI_NOR_3BYTE_ADDR_LEN) ++ && (start_up_addr_mode == SPI_NOR_ADDR_MODE_4_BYTES)) ++ printf("\nError!!! the flash's addres len is 3bytes and start \ ++ up address mode is 4bytes,please set the start up \ ++ address mode to 3bytes mode"); ++ if ((spi->addrcycle == SPI_NOR_4BYTE_ADDR_LEN) ++ && (start_up_addr_mode == SPI_NOR_ADDR_MODE_3_BYTES)) { ++ fmc_pr(BT_DBG, "\t|||-start up: 3-Byte mode\n"); ++ spi->driver->entry_4addr(spi, ENABLE); ++ } else { ++ fmc_pr(BT_DBG, "\t|||-start up: 4-Byte mode or 4-Byte Command\n"); ++ } ++} ++ ++static void spi_data_init(struct fmc_spi *spi, struct spi_nor_info *spiinfo, ++ unsigned char cs) ++{ ++ spi->name = spiinfo->name; ++ spi->chipselect = cs; ++ spi->chipsize = spiinfo->chipsize; ++ spi->erasesize = spiinfo->erasesize; ++ spi->addrcycle = spiinfo->addrcycle; ++ spi->driver = spiinfo->driver; ++} ++static void mtd_data_set(struct mtd_info_ex* const mtd, struct spi_nor_info* const spiinfo, ++ struct fmc_spi *spi) ++{ ++ if (mtd->type == 0) { ++ mtd->type = MTD_NORFLASH; ++ mtd->chipsize = spi->chipsize; ++ mtd->erasesize = spi->erasesize; ++ mtd->pagesize = 1; ++ mtd->addrcycle = spi->addrcycle; ++ ++ if (spiinfo->id_len > sizeof(mtd->ids)) { ++ printf("BUG! ID len out of range.\n"); ++ BUG(); ++ } ++ ++ mtd->id_length = spiinfo->id_len; ++ if (memcpy_s(mtd->ids, spiinfo->id_len, spiinfo->id, spiinfo->id_len)) ++ printf("%s %d ERR:memcpy_s fail !\n", __func__, __LINE__); ++ if (sizeof(mtd->name) < strlen(spi->name)) { ++ printf("BUG! spi->name len err %d.\n", __LINE__); ++ return; ++ } ++ ++ if (strncpy_s(mtd->name, sizeof(mtd->name) - 1, ++ spi->name, sizeof(mtd->name) - 1)) { ++ printf("BUG! mtd->name len err %s %d.\n", __func__, __LINE__); ++ return; ++ } ++ mtd->name[sizeof(mtd->name) - 1] = '\0'; ++ } ++} ++ ++static void fmc_init_print(struct fmc_spi* const spi) ++{ ++ const char *str[] = {"STD", "DUAL", "DIO", "QUAD", "QIO"}; ++ ++ fmc_pr(FMC_INFO, "Block:%sB ", ulltostr(spi->erasesize)); ++ fmc_pr(FMC_INFO, "Chip:%sB ", ulltostr(spi->chipsize)); ++ fmc_pr(FMC_INFO, "Name:\"%s\"\n", spi->name); ++ ++ fmc_pr(BT_DBG, "\t|||-Read if: %s, cmd: %#X, clock reg: %#x\n", ++ str[spi->read->iftype], ++ spi->read->cmd, spi->read->clock); ++ fmc_pr(BT_DBG, "\t|||-Write if: %s, cmd: %#X, clock reg: %#x\n", ++ str[spi->write->iftype], ++ spi->write->cmd, spi->write->clock); ++ fmc_pr(BT_DBG, "\t|||-Erase if: %s, cmd: %#X, clock reg: %#x\n", ++ str[spi->erase[0].iftype], ++ spi->erase[0].cmd, spi->erase[0].clock); ++} ++ ++static void fmc_spi_map_op(struct spi_nor_info *spiinfo, struct fmc_spi *spi) ++{ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ if (spi->dtr_mode_support) { ++ /* to match the best dummy/if_type/clock */ ++ fmc_spi_nor_search_rw(spiinfo, spi->read, ++ FMC_SPI_NOR_SUPPORT_READ, ++ FMC_SPI_NOR_DTR_MAX_DUMMY, RW_OP_READ); ++ } else { ++ fmc_spi_nor_search_rw(spiinfo, spi->read, ++ FMC_SPI_NOR_SUPPORT_READ, ++ FMC_SPI_NOR_STR_MAX_DUMMY, RW_OP_READ); ++ } ++#else ++ /* to match the best dummy/if_type/clock */ ++ fmc_spi_nor_search_rw(spiinfo, spi->read, ++ FMC_SPI_NOR_SUPPORT_READ, ++ FMC_SPI_NOR_STR_MAX_DUMMY, RW_OP_READ); ++#endif ++ fmc_spi_nor_search_rw(spiinfo, spi->write, ++ FMC_SPI_NOR_SUPPORT_WRITE, ++ FMC_SPI_NOR_STR_MAX_DUMMY, RW_OP_WRITE); ++ ++ fmc_spi_nor_get_erase(spiinfo, spi->erase); ++ fmc_map_iftype_and_clock(spi); ++} ++ ++static int chip_spi_init(struct mtd_info_ex *mtd, ++ struct fmc_spi *spi, ++ struct spi_nor_info *spiinfo, ++ unsigned char cs, ++ unsigned char *ids) ++{ ++ int ret = 0; ++ ++ spi_data_init(spi, spiinfo, cs); ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++ /* to check weather current device support DTR mode */ ++ fmc_check_spi_dtr_support(spi, ids, MAX_SPI_NOR_ID_LEN); ++#endif ++ fmc_spi_map_op(spiinfo, spi); ++ if (!spi->driver) { ++ printf("err:spi->driver is NULL"); ++ ret = -1; ++ return ret; ++ } ++ if (spi->driver->qe_enable == NULL) { ++ ret = -1; ++ return ret; ++ } ++ spi->driver->qe_enable(spi); ++ ++ switch_to_4byte(spi, ids, MAX_SPI_NOR_ID_LEN); ++ ++ fmc_init_print(spi); ++ mtd_data_set(mtd, spiinfo, spi); ++ ++ return ret; ++} ++ ++static int match_chip_id(struct mtd_info_ex *mtd, struct fmc_spi *spi) ++{ ++ unsigned char cs = 0; ++ unsigned char ids[MAX_SPI_NOR_ID_LEN] = {0}; ++ unsigned char ix; ++ int len; ++ unsigned int total = 0; ++ char buffer[TMP_BUF_LEN]; ++ unsigned char *fmc_cs = NULL; ++ int ret = 0; ++ ++ for (cs = 0; cs < CONFIG_SPI_NOR_MAX_CHIP_NUM; cs++) { ++ fmc_cs = get_cs_number(cs); ++ if (*fmc_cs) { ++ fmc_pr(BT_DBG, "\t|||-CS(%d) is occupied\n", cs); ++ continue; ++ } ++ ++ fmc100_read_ids(spi, cs, ids); ++ ++ /* can't find spi flash device, for id 0-2 */ ++ if (!(ids[0] | ids[1] | ids[2]) || ++ ((ids[0] & ids[1] & ids[2]) == 0xFF)) /* id 0-2 */ ++ continue; ++ ++ ret = sprintf_s(buffer, TMP_BUF_LEN, "SPI Nor(cs %d) ID: %#x %#x %#x", cs, ++ ids[0], ids[1], ids[2]); /* id 0-2 */ ++ if (ret < 0) ++ return ret; ++ len = ret; ++ spiinfo = fmc_spi_nor_serach_ids(ids, MAX_SPI_NOR_ID_LEN); ++ /* id 3-7th */ ++ for (ix = 3; (spiinfo) && (ix < spiinfo->id_len); ix++) { ++ ret = sprintf_s(buffer + len, TMP_BUF_LEN, " %#x", ids[ix]); ++ if (ret < 0) ++ return ret; ++ len += ret; ++ } ++ ++ if (spiinfo) { ++ fmc_pr(FMC_INFO, "%s\n", buffer); ++ ++ fmc_pr(BT_DBG, "\t|||-CS-%d found SPI nor flash: %s\n", ++ cs, spiinfo->name); ++ ++ ret = chip_spi_init(mtd, spi, spiinfo, cs, ids); ++ if (ret) ++ return ret; ++ ++ mtd->numchips++; ++ total += (unsigned int)spi->chipsize; ++ spi++; ++ (*fmc_cs)++; ++ } else { ++ printf("SPI Nor(cs %d) ID: %#x %#x %#x can't find" ++ " in the ID table !!!\n", cs, ids[0], ids[1], ids[2]); ++ } ++ } ++ ++ return ret; ++} ++ ++int fmc_spi_nor_probe(struct mtd_info_ex *mtd, struct fmc_spi *spi) ++{ ++ int ret; ++ ++ if (!mtd || !spi) { ++ printf("err:mtd or spi is NULL"); ++ return -1; ++ } ++ ++ mtd->numchips = 0; ++ ++ fmc_pr(FMC_INFO, "SPI Nor ID Table Version %s\n", SPI_NOR_ID_TBL_VER); ++ ++ ret = match_chip_id(mtd, spi); ++ if (ret) ++ return ret; ++ ++ fmc_pr(BT_DBG, "\t||*-End probe SPI nor flash, num: %d\n", ++ mtd->numchips); ++ ++ return mtd->numchips; ++} ++ ++#ifdef CONFIG_DTR_MODE_SUPPORT ++void spi_dtr_to_sdr_switch(struct fmc_spi *spi) ++{ ++ unsigned int ix = 0; ++ unsigned int spi_dtr_dummy; ++ struct spi_op **spiop, **fitspiop; ++ const int iftype_read[] = { ++ SPI_IF_READ_QUAD, IF_TYPE_QUAD, ++ SPI_IF_READ_QUAD_ADDR, IF_TYPE_QIO, ++ 0, 0, ++ }; ++ ++ /* the dummy in SDR mode is impossible equal to DTR */ ++ spi_dtr_dummy = spi->read->dummy; ++ ++ /* match the best clock and dummy value agian */ ++ for (fitspiop = spiop = spiinfo->read; ++ (*spiop) && ix < MAX_SPI_OP; spiop++, ix++) ++ if (((*spiop)->iftype & FMC_SPI_NOR_SUPPORT_READ) && ++ ((*spiop)->dummy != spi_dtr_dummy) && ++ ((*fitspiop)->iftype < (*spiop)->iftype)) ++ fitspiop = spiop; ++ ++ if (memcpy_s(spi->read, sizeof(struct spi_op), (*fitspiop), sizeof(struct spi_op))) ++ printf("%s %d ERR:memcpy_s fail !\n", __func__, __LINE__); ++ ++ /* to map the iftype and clock of SDR mode */ ++ /* Only an even number of values is required,so increase length is 2 */ ++ for (ix = 0; iftype_read[ix]; ix += 2) { ++ if (spi->read->iftype == iftype_read[ix]) { ++ spi->read->iftype = iftype_read[ix + 1]; ++ break; ++ } ++ } ++ fmc_get_fmc_best_2x_clock(&spi->read->clock); ++} ++#endif /* CONFIG_DTR_MODE_SUPPORT */ +diff --git a/drivers/mtd/spi/vendor_spi.h b/drivers/mtd/spi/vendor_spi.h +new file mode 100644 +index 0000000..4d97f63 +--- /dev/null ++++ b/drivers/mtd/spi/vendor_spi.h +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#ifndef __FMC100_SPI_H__ ++#define __FMC100_SPI_H__ ++ ++#include ++#include ++ ++struct spi_flash *fmc100_spi_nor_probe(struct mtd_info_ex **); ++struct mtd_info_ex *fmc100_get_spi_nor_info(struct spi_flash *); ++ ++#endif ++ +diff --git a/drivers/mtd/spi/vendor_spi_nor.c b/drivers/mtd/spi/vendor_spi_nor.c +new file mode 100644 +index 0000000..3e4ac5d +--- /dev/null ++++ b/drivers/mtd/spi/vendor_spi_nor.c +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "vendor_spi.h" ++ ++static struct spi_flash *spiflash; ++static struct mtd_info_ex *spiinfo_ex; ++ ++struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, ++ unsigned int max_hz, unsigned int spi_mode) ++{ ++ if (get_boot_media() != BOOT_MEDIA_SPIFLASH) { ++ puts("Boot Media isn't SPI Nor\n"); ++ return NULL; ++ } ++ ++ if (spiflash) ++ return spiflash; ++ ++#ifdef CONFIG_FMC_SPI_NOR ++ spiflash = fmc100_spi_nor_probe(&spiinfo_ex); ++ spiflash->erase_size = spiinfo_ex->erasesize; ++#endif ++ ++ return spiflash; ++} ++ ++struct mtd_info_ex *get_spiflash_info(void) ++{ ++ if (spiinfo_ex) ++ return spiinfo_ex; ++ ++#ifdef CONFIG_FMC_SPI_NOR ++ spiinfo_ex = fmc100_get_spi_nor_info(spiflash); ++#endif ++ ++ return spiinfo_ex; ++} ++ ++void spi_flash_free(struct spi_flash *flash) ++{ ++} ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++void spi_flash_lock(unsigned char cmp, unsigned char level, unsigned char op) ++{ ++ cmp = BP_CMP_BOTTOM; ++ ++ if (spiflash->lock) ++ spiflash->lock(cmp, level, op); ++ ++ return; ++} ++ ++#endif /* CONFIG_SPI_BLOCK_PROTECT */ ++ +diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig +index 142a2c6..a5d5807 100644 +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -592,6 +592,18 @@ config HIGMACV300_ETH + This driver supports HIGMACV300 Ethernet controller found on + HiSilicon SoCs. + ++config SFV300_ETH ++ bool "Fast Ethernet Controller" ++ help ++ This driver supports SFV300 Ethernet controller found on ++ SoCs. ++ ++config GMACV300_ETH ++ bool "Gigabit Ethernet Controller" ++ help ++ This driver supports GMACV300 Ethernet controller found on ++ SoCs. ++ + config FSL_ENETC + bool "NXP ENETC Ethernet controller" + depends on DM_PCI && DM_ETH && DM_MDIO +diff --git a/drivers/net/Makefile b/drivers/net/Makefile +index 3099183..513e49f 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -79,6 +79,7 @@ obj-$(CONFIG_SNI_AVE) += sni_ave.o + obj-y += ti/ + obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o + obj-y += mscc_eswitch/ ++obj-$(CONFIG_GMACV300_ETH) += gmacv300/ + obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o + obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o + obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o +diff --git a/drivers/net/gmacv300/Makefile b/drivers/net/gmacv300/Makefile +new file mode 100644 +index 0000000..569f9ca +--- /dev/null ++++ b/drivers/net/gmacv300/Makefile +@@ -0,0 +1,8 @@ ++# ++# (C) Copyright 2008 ++# Wolfgang Denk, DENX Software Engineering, wd@denx.de. ++# ++# SPDX-License-Identifier: GPL-2.0+ ++# ++ ++obj-y += mdio.o ctrl.o gmac.o +diff --git a/drivers/net/gmacv300/ctrl.c b/drivers/net/gmacv300/ctrl.c +new file mode 100644 +index 0000000..9fa2711 +--- /dev/null ++++ b/drivers/net/gmacv300/ctrl.c +@@ -0,0 +1,542 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "util.h" ++#include "gmac.h" ++#include "ctrl.h" ++ ++void gmac_set_macif(struct gmac_netdev_local const *ld, int mode, unsigned int speed) ++{ ++ void __iomem *p = (void __iomem *)CRG_REG_BASE; ++ unsigned long v; ++ ++ /* enable change: port_mode */ ++ gmac_writel_bits(ld, 1, MODE_CHANGE_EN, BIT_MODE_CHANGE_EN); ++ if (speed == SPEED_MODE_1000M) ++ speed = 5; /* regval is 5 for 1000M */ ++ gmac_writel_bits(ld, speed, PORT_MODE, BITS_PORT_MODE); ++ /* disable change: port_mode */ ++ gmac_writel_bits(ld, 0, MODE_CHANGE_EN, BIT_MODE_CHANGE_EN); ++ ++ /* soft reset mac_if */ ++ if (ld->index) { ++#if GMAC_AT_LEAST_2PORT ++ v = readl(p + REG_ETH1_MACIF_CRG); ++ v |= BIT_MACIF1_RST; ++ writel(v, p + REG_ETH1_MACIF_CRG); ++#endif ++ } else { ++ v = readl(p + REG_ETH0_MACIF_CRG); ++ v |= BIT_MACIF0_RST; ++ writel(v, p + REG_ETH0_MACIF_CRG); ++ } ++ ++ /* config mac_if */ ++ if (ld->index) { ++#if GMAC_AT_LEAST_2PORT ++ writel(mode, GMAC_MACIF1_CTRL); ++#endif ++ } else { ++ writel(mode, GMAC_MACIF0_CTRL); ++ } ++ ++ if (ld->index) { ++#if GMAC_AT_LEAST_2PORT ++ v = readl(p + REG_ETH1_MACIF_CRG); ++ v &= ~BIT_MACIF1_RST; ++ writel(v, p + REG_ETH1_MACIF_CRG); ++#endif ++ } else { ++ v = readl(p + REG_ETH0_MACIF_CRG); ++ v &= ~BIT_MACIF0_RST; ++ writel(v, p + REG_ETH0_MACIF_CRG); ++ } ++} ++ ++int gmac_hw_set_macaddress(struct gmac_netdev_local const *ld, const unsigned char *mac, ++ int len) ++{ ++ unsigned long reg; ++ ++ if (len < MAC_LEN || mac == NULL) ++ return -1; ++ ++ reg = mac[1] | (mac[0] << 8); /* mac 0 [8:15], mac 1 [0:7] */ ++ gmac_writel(ld, reg, STATION_ADDR_HIGH); ++ /* mac 2 [24:31], mac 3 [16:23], mac 4 [8:15], mac 5 [0:7] */ ++ reg = mac[5] | (mac[4] << 8) | (mac[3] << 16) | (mac[2] << 24); ++ gmac_writel(ld, reg, STATION_ADDR_LOW); ++ ++ return 0; ++} ++ ++int gmac_hw_get_macaddress(struct gmac_netdev_local const *ld, unsigned char *mac, ++ int len) ++{ ++ unsigned long reg; ++ ++ if (len < MAC_LEN || mac == NULL) ++ return -1; ++ ++ reg = gmac_readl(ld, STATION_ADDR_HIGH); ++ mac[0] = (reg >> 8) & 0xff; /* mac[0] [8:15] bit */ ++ mac[1] = reg & 0xff; /* mac[1] [0:7] bit */ ++ ++ reg = gmac_readl(ld, STATION_ADDR_LOW); ++ mac[2] = (reg >> 24) & 0xff; /* mac[2] [24:31] bit */ ++ mac[3] = (reg >> 16) & 0xff; /* mac[3] [16:23] bit */ ++ mac[4] = (reg >> 8) & 0xff; /* mac[4] [8:15] bit */ ++ mac[5] = reg & 0xff; /* mac[5] [0:7] bit */ ++ ++ return 0; ++} ++ ++static inline int _gmac_read_irqstatus(struct gmac_netdev_local const *ld) ++{ ++ u32 status; ++ ++ status = gmac_readl(ld, STATUS_PMU_INT); ++ ++ return status; ++} ++ ++int gmac_clear_irqstatus(struct gmac_netdev_local const *ld, int irqs) ++{ ++ int status; ++ ++ gmac_writel(ld, irqs, RAW_PMU_INT); ++ status = _gmac_read_irqstatus(ld); ++ ++ return status; ++} ++ ++int gmac_glb_preinit_dummy(struct gmac_netdev_local const *ld) ++{ ++ /* drop packet enable */ ++ gmac_writel(ld, 0x3F, REC_FILT_CONTROL); ++ gmac_writel_bits(ld, 0, REC_FILT_CONTROL, BIT_BC_DROP_EN); ++ ++ /* clear all interrupt status */ ++ gmac_clear_irqstatus(ld, RAW_INT_ALL_MASK); ++ ++ /* disable interrupts */ ++ gmac_writel(ld, ~RAW_INT_ALL_MASK, ENA_PMU_INT); ++ ++ return 0; ++} ++ ++#ifdef GAMC_USE_GPIO_RESET_PHY ++static unsigned int gmac_external_phy_reset_by_gpio(void) ++{ ++ unsigned int v; ++ /* use GPIO0_1 to reset external phy */ ++ /* Set Direction output */ ++ v = readl(GMAC_RESET_GPIO_BASE + GMAC_RESET_GPIO_DIR_OFS); ++ v |= GMAC_RESET_GPIO_DIR_OUT; ++ writel(v, GMAC_RESET_GPIO_BASE + GMAC_RESET_GPIO_DIR_OFS); ++ ++ /* Set GPIO0_1 to 1 */ ++ writel(GMAC_RESET_GPIO_VALUE, GMAC_RESET_GPIO_BASE ++ + GMAC_RESET_GPIO_DATA_OFS); ++ udelay(50000); /* wait 50000us */ ++ /* Set GPIO0_1=0 to reset phy */ ++ writel(~GMAC_RESET_GPIO_VALUE, GMAC_RESET_GPIO_BASE ++ + GMAC_RESET_GPIO_DATA_OFS); ++ udelay(200000); /* wait 200000us */ ++ ++ /* Set GPIO0_1=1 to cancel reset phy */ ++ writel(GMAC_RESET_GPIO_VALUE, GMAC_RESET_GPIO_BASE ++ + GMAC_RESET_GPIO_DATA_OFS); ++ udelay(50000); /* wait 50000us */ ++ return v; ++} ++#endif ++ ++void gmac_external_phy_reset(void) ++{ ++ unsigned int v; ++ ++#ifdef GAMC_USE_GPIO_RESET_PHY ++ v = gmac_external_phy_reset_by_gpio(); ++#else ++ /* use CRG register to reset external phy */ ++ v = readl(CRG_REG_BASE + REG_ETH0_PHY_CRG); ++ v |= BIT_EXT_PHY0_RST; /* reset */ ++ writel(v, CRG_REG_BASE + REG_ETH0_PHY_CRG); ++#if GMAC_AT_LEAST_2PORT ++ v = readl(CRG_REG_BASE + REG_ETH1_PHY_CRG); ++ v |= BIT_EXT_PHY1_RST; ++ writel(v, CRG_REG_BASE + REG_ETH1_PHY_CRG); ++#endif ++ ++ udelay(50 * 1000); /* wait 50 * 1000us for phy reset time */ ++ ++ v = readl(CRG_REG_BASE + REG_ETH0_PHY_CRG); ++ v &= ~BIT_EXT_PHY0_RST; /* undo reset */ ++ writel(v, CRG_REG_BASE + REG_ETH0_PHY_CRG); ++#if GMAC_AT_LEAST_2PORT ++ v = readl(CRG_REG_BASE + REG_ETH1_PHY_CRG); ++ v &= ~BIT_EXT_PHY1_RST; ++ writel(v, CRG_REG_BASE + REG_ETH1_PHY_CRG); ++#endif ++ ++ udelay(60 * 1000); /* wait 60 * 1000us for future MDIO operation */ ++#endif ++} ++ ++void gmac_init_phy(void) ++{ ++ uintptr_t p; ++ volatile unsigned int v; ++ ++ p = (unsigned long)(CRG_REG_BASE); ++ ++ v = readl(p + REG_ETH0_PHY_CRG); ++ ++ /* phy clk select 25MHz */ ++ v &= ~BIT_EXT_PHY0_CLK_SELECT; ++ v |= PHY0_CLK_25M; ++ writel(v, p + REG_ETH0_PHY_CRG); ++#if GMAC_AT_LEAST_2PORT ++ v = readl(p + REG_ETH1_PHY_CRG); ++ v &= ~BIT_EXT_PHY1_CLK_SELECT; ++ v |= PHY1_CLK_25M; ++ writel(v, p + REG_ETH1_PHY_CRG); ++#endif ++} ++ ++void gmac_soft_reset_eth(void) ++{ ++ uintptr_t p; ++ volatile unsigned int v; ++ ++ p = (unsigned long)(CRG_REG_BASE); ++ ++ /* soft reset */ ++ v = readl(p + REG_ETH0_GSF_CRG); ++ v |= BIT_GMAC0_RST; ++ writel(v, p + REG_ETH0_GSF_CRG); ++#if GMAC_AT_LEAST_2PORT ++ v = readl(p + REG_ETH1_GSF_CRG); ++ v |= BIT_GMAC1_RST; ++ writel(v, p + REG_ETH1_GSF_CRG); ++#endif ++ ++ udelay(100); /* wait 100us */ ++ ++ v = readl(p + REG_ETH0_GSF_CRG); ++ v &= ~BIT_GMAC0_RST; ++ writel(v, p + REG_ETH0_GSF_CRG); ++#if GMAC_AT_LEAST_2PORT ++ v = readl(p + REG_ETH1_GSF_CRG); ++ v &= ~BIT_GMAC1_RST; ++ writel(v, p + REG_ETH1_GSF_CRG); ++#endif ++} ++ ++ ++void gmac_sys_init(void) ++{ ++ uintptr_t p; ++ volatile unsigned int v; ++ ++ p = (unsigned long)(CRG_REG_BASE); ++ ++ gmac_init_phy(); ++ ++ /* enable clk */ ++ v = readl(p + REG_ETH0_GSF_CRG); ++ v |= BIT_GMAC0_CLK_EN; ++ writel(v, p + REG_ETH0_GSF_CRG); ++ ++ v = readl(p + REG_ETH0_MACIF_CRG); ++ v |= BIT_GMACIF0_CLK_EN; ++ writel(v, p + REG_ETH0_MACIF_CRG); ++ ++#if GMAC_AT_LEAST_2PORT ++ v = readl(p + REG_ETH1_GSF_CRG); ++ v |= BIT_GMAC1_CLK_EN; ++ writel(v, p + REG_ETH1_GSF_CRG); ++ ++ v = readl(p + REG_ETH1_MACIF_CRG); ++ v |= BIT_GMACIF1_CLK_EN; ++ writel(v, p + REG_ETH1_MACIF_CRG); ++#endif ++ ++#ifdef CONFIG_GMAC_RMII0_CLK_USE_EXTERNAL_PAD ++ v = readl(p + REG_ETH0_MACIF_CRG); ++ if (gmac_board_info[0].phy_intf == INTERFACE_MODE_RMII) ++ v |= BIT_RMII0_CLKSEL_PAD; /* rmii select pad clk */ ++ writel(v, p + REG_ETH0_MACIF_CRG); ++#endif ++#if GMAC_AT_LEAST_2PORT ++#ifdef CONFIG_GMAC_RMII1_CLK_USE_EXTERNAL_PAD ++ v = readl(p + REG_ETH1_MACIF_CRG); ++ if (gmac_board_info[1].phy_intf == INTERFACE_MODE_RMII) ++ v |= BIT_RMII1_CLKSEL_PAD; /* rmii select pad clk */ ++ writel(v, p + REG_ETH1_MACIF_CRG); ++#endif ++#endif ++ ++ gmac_soft_reset_eth(); ++ ++ writel(0xe, GMAC_DUAL_MAC_CRF_ACK_TH); ++ ++ gmac_external_phy_reset(); ++} ++ ++void gmac_sys_exit(void) ++{ ++} ++ ++void gmac_sys_allstop(void) ++{ ++} ++ ++int gmac_set_hwq_depth(struct gmac_netdev_local const *ld) ++{ ++ if (GMAC_HWQ_RX_FQ_DEPTH > GMAC_MAX_QUEUE_DEPTH) { ++ BUG(); ++ return -1; ++ } ++ ++ gmac_writel_bits(ld, 1, RX_FQ_REG_EN, \ ++ BITS_RX_FQ_DEPTH_EN); ++ ++ gmac_writel_bits(ld, GMAC_HWQ_RX_FQ_DEPTH << DESC_WORD_SHIFT, ++ RX_FQ_DEPTH, BITS_RX_FQ_DEPTH); ++ ++ gmac_writel_bits(ld, 0, RX_FQ_REG_EN, \ ++ BITS_RX_FQ_DEPTH_EN); ++ ++ if (GMAC_HWQ_RX_BQ_DEPTH > GMAC_MAX_QUEUE_DEPTH) { ++ BUG(); ++ return -1; ++ } ++ ++ gmac_writel_bits(ld, 1, RX_BQ_REG_EN, \ ++ BITS_RX_BQ_DEPTH_EN); ++ ++ gmac_writel_bits(ld, GMAC_HWQ_RX_BQ_DEPTH << DESC_WORD_SHIFT, ++ RX_BQ_DEPTH, BITS_RX_BQ_DEPTH); ++ ++ gmac_writel_bits(ld, 0, RX_BQ_REG_EN, \ ++ BITS_RX_BQ_DEPTH_EN); ++ ++ if (GMAC_HWQ_TX_BQ_DEPTH > GMAC_MAX_QUEUE_DEPTH) { ++ BUG(); ++ return -1; ++ } ++ ++ gmac_writel_bits(ld, 1, TX_BQ_REG_EN, \ ++ BITS_TX_BQ_DEPTH_EN); ++ ++ gmac_writel_bits(ld, GMAC_HWQ_TX_BQ_DEPTH << DESC_WORD_SHIFT, ++ TX_BQ_DEPTH, BITS_TX_BQ_DEPTH); ++ ++ gmac_writel_bits(ld, 0, TX_BQ_REG_EN, \ ++ BITS_TX_BQ_DEPTH_EN); ++ ++ if (GMAC_HWQ_TX_RQ_DEPTH > GMAC_MAX_QUEUE_DEPTH) { ++ BUG(); ++ return -1; ++ } ++ ++ gmac_writel_bits(ld, 1, TX_RQ_REG_EN, \ ++ BITS_TX_RQ_DEPTH_EN); ++ ++ gmac_writel_bits(ld, GMAC_HWQ_TX_RQ_DEPTH << DESC_WORD_SHIFT, ++ TX_RQ_DEPTH, BITS_TX_RQ_DEPTH); ++ ++ gmac_writel_bits(ld, 0, TX_RQ_REG_EN, \ ++ BITS_TX_RQ_DEPTH_EN); ++ ++ return 0; ++} ++ ++int gmac_set_rx_fq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr) ++{ ++ gmac_writel_bits(ld, 1, RX_FQ_REG_EN, \ ++ BITS_RX_FQ_START_ADDR_EN); ++ ++ gmac_writel(ld, (u32)phy_addr, RX_FQ_START_ADDR); ++#if defined(CONFIG_64BIT) ++ gmac_writel_bits(ld, (u32)((u64)phy_addr >> REG_BIT_WIDTH), RX_FQ_DEPTH, ++ BITS_RX_FQ_ADDR_HI8); ++#endif ++ ++ gmac_writel_bits(ld, 0, RX_FQ_REG_EN, \ ++ BITS_RX_FQ_START_ADDR_EN); ++ ++ return 0; ++} ++ ++int gmac_set_rx_bq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr) ++{ ++ gmac_writel_bits(ld, 1, RX_BQ_REG_EN, \ ++ BITS_RX_BQ_START_ADDR_EN); ++ ++ gmac_writel(ld, (u32)phy_addr, RX_BQ_START_ADDR); ++#if defined(CONFIG_64BIT) ++ gmac_writel_bits(ld, (u32)((u64)phy_addr >> REG_BIT_WIDTH), RX_BQ_DEPTH, ++ BITS_RX_BQ_ADDR_HI8); ++#endif ++ ++ gmac_writel_bits(ld, 0, RX_BQ_REG_EN, \ ++ BITS_RX_BQ_START_ADDR_EN); ++ ++ return 0; ++} ++ ++int gmac_set_tx_bq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr) ++{ ++ gmac_writel_bits(ld, 1, TX_BQ_REG_EN, \ ++ BITS_TX_BQ_START_ADDR_EN); ++ ++ gmac_writel(ld, (u32)phy_addr, TX_BQ_START_ADDR); ++#if defined(CONFIG_64BIT) ++ gmac_writel_bits(ld, (u32)((u64)phy_addr >> REG_BIT_WIDTH), TX_BQ_DEPTH, ++ BITS_TX_BQ_ADDR_HI8); ++#endif ++ ++ gmac_writel_bits(ld, 0, TX_BQ_REG_EN, \ ++ BITS_TX_BQ_START_ADDR_EN); ++ ++ return 0; ++} ++ ++int gmac_set_tx_rq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr) ++{ ++ gmac_writel_bits(ld, 1, TX_RQ_REG_EN, \ ++ BITS_TX_RQ_START_ADDR_EN); ++ ++ gmac_writel(ld, (u32)phy_addr, TX_RQ_START_ADDR); ++#if defined(CONFIG_64BIT) ++ gmac_writel_bits(ld, (u32)((u64)phy_addr >> REG_BIT_WIDTH), TX_RQ_DEPTH, ++ BITS_TX_RQ_ADDR_HI8); ++#endif ++ ++ gmac_writel_bits(ld, 0, TX_RQ_REG_EN, \ ++ BITS_TX_RQ_START_ADDR_EN); ++ ++ return 0; ++} ++ ++void gmac_desc_enable(struct gmac_netdev_local const *ld, u32 desc_ena) ++{ ++ u32 old; ++ ++ old = gmac_readl(ld, DESC_WR_RD_ENA); ++ gmac_writel(ld, old | desc_ena, DESC_WR_RD_ENA); ++} ++ ++void gmac_desc_disable(struct gmac_netdev_local const *ld, u32 desc_dis) ++{ ++ u32 old; ++ ++ old = gmac_readl(ld, DESC_WR_RD_ENA); ++ gmac_writel(ld, old & (~desc_dis), DESC_WR_RD_ENA); ++} ++ ++void gmac_desc_flush(struct gmac_netdev_local const *ld) ++{ ++ gmac_writel_bits(ld, 1, STOP_CMD, BITS_TX_STOP_EN); ++ while (gmac_readl_bits(ld, FLUSH_CMD, BITS_TX_FLUSH_FLAG) != 1); ++ gmac_writel_bits(ld, 1, FLUSH_CMD, BITS_TX_FLUSH_CMD); ++ while (gmac_readl_bits(ld, FLUSH_CMD, BITS_TX_FLUSH_FLAG) != 0); ++ gmac_writel_bits(ld, 0, FLUSH_CMD, BITS_TX_FLUSH_CMD); ++ gmac_writel_bits(ld, 0, STOP_CMD, BITS_TX_STOP_EN); ++ ++ gmac_writel_bits(ld, 1, STOP_CMD, BITS_RX_STOP_EN); ++ while (gmac_readl_bits(ld, FLUSH_CMD, BITS_RX_FLUSH_FLAG) != 1); ++ gmac_writel_bits(ld, 1, FLUSH_CMD, BITS_RX_FLUSH_CMD); ++ while (gmac_readl_bits(ld, FLUSH_CMD, BITS_RX_FLUSH_FLAG) != 0); ++ gmac_writel_bits(ld, 0, FLUSH_CMD, BITS_RX_FLUSH_CMD); ++ gmac_writel_bits(ld, 0, STOP_CMD, BITS_RX_STOP_EN); ++} ++ ++u32 gmac_readl(struct gmac_netdev_local const *ld, u32 ofs) ++{ ++ u32 reg; ++ if (ld == NULL) ++ return -1; ++ reg = readl((uintptr_t)(ld->iobase + ofs)); ++ gmac_trace(GMAC_TRACE_ETH, "readl(0x%04X) = 0x%08X", ofs, reg); ++ return reg; ++} ++ ++void gmac_writel(struct gmac_netdev_local const *ld, u32 v, u32 ofs) ++{ ++ if (ld == NULL) ++ return; ++ writel(v, (uintptr_t)(ld->iobase + ofs)); ++ gmac_trace(GMAC_TRACE_ETH, "writel(0x%04X) = 0x%08X", ofs, v); ++} ++ ++u32 gmac_readl_bits(struct gmac_netdev_local const *ld, u32 ofs, u32 bits_desc) ++{ ++ u32 _bits_desc = bits_desc; ++ u32 _shift = _bits_desc >> 16; /* shift 16 bit */ ++ u32 _mask = ((_bits_desc & 0x3F) < 32) ? /* 32bit register */ ++ (((1 << (_bits_desc & 0x3F)) - 1) << _shift) : 0xffffffff; ++ u32 reg = (gmac_readl(ld, ofs) & _mask) >> _shift; ++ return reg; ++} ++ ++void gmac_writel_bits(struct gmac_netdev_local const *ld, u32 v, u32 ofs, u32 bits_desc) ++{ ++ u32 _bits_desc = bits_desc; ++ u32 _shift = _bits_desc >> 16; /* shift 16 bit */ ++ u32 _reg = gmac_readl(ld, ofs); ++ u32 _mask = ((_bits_desc & 0x3F) < 32) ? /* 32bit register */ ++ (((1 << (_bits_desc & 0x3F)) - 1) << _shift) : 0xffffffff; ++ gmac_writel(ld, (_reg & (~_mask)) | ((v << _shift) & _mask), ofs); ++} ++ ++void gmac_trace(int level, const char *fmt, ...) ++{ ++ if (level >= GMAC_TRACE_LEVEL) { ++ va_list args; ++ va_start(args, fmt); ++ printf("gmac_trace:"); ++ printf(fmt, args); ++ printf("\n"); ++ va_end(args); ++ } ++} ++ ++void gmac_error(const char *fmt, ...) ++{ ++ va_list args; ++ va_start(args, fmt); ++ printf("gmac:"); ++ printf(fmt, args); ++ printf("\n"); ++ va_end(args); ++} ++ ++void gmac_assert(bool cond) ++{ ++ if (!cond) ++ printf("Assert:gmac:%s:%d\n", __FILE__, __LINE__); ++} +diff --git a/drivers/net/gmacv300/ctrl.h b/drivers/net/gmacv300/ctrl.h +new file mode 100644 +index 0000000..d96db51 +--- /dev/null ++++ b/drivers/net/gmacv300/ctrl.h +@@ -0,0 +1,268 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __GMAC_CTRL_H__ ++#define __GMAC_CTRL_H__ ++ ++#include "gmac.h" ++#include "util.h" ++ ++#define STATION_ADDR_LOW 0x0000 ++#define STATION_ADDR_HIGH 0x0004 ++#define MAC_DUPLEX_HALF_CTRL 0x0008 ++ ++#define MAX_FRM_SIZE 0x003c ++#define BITS_MAX_FRM_SIZE mk_bits(0, 14) ++ ++#define PORT_MODE 0x0040 ++#define BITS_PORT_MODE mk_bits(0, 3) ++ ++#define PORT_EN 0x0044 ++#define BITS_TX_EN mk_bits(2, 1) ++#define BITS_RX_EN mk_bits(1, 1) ++ ++#define REC_FILT_CONTROL 0x0064 ++#define BIT_CRC_ERR_PASS mk_bits(5, 1) ++#define BIT_PAUSE_FRM_PASS mk_bits(4, 1) ++#define BIT_VLAN_DROP_EN mk_bits(3, 1) ++#define BIT_BC_DROP_EN mk_bits(2, 1) ++#define BIT_MC_MATCH_EN mk_bits(1, 1) ++#define BIT_UC_MATCH_EN mk_bits(0, 1) ++ ++#define CF_CRC_STRIP 0x01B0 ++#define BIT_CF_CRC_STRIP mk_bits(0, 1) ++ ++#define MODE_CHANGE_EN 0x01b4 ++#define BIT_MODE_CHANGE_EN mk_bits(0, 1) ++ ++#define RECV_CONTROL 0x01e0 ++#define BIT_STRIP_PAD_EN mk_bits(3, 1) ++#define BIT_RUNT_PKT_EN mk_bits(4, 1) ++ ++#define CRF_MIN_PACKET 0x0210 ++#define BIT_TSO_VERSION mk_bits(20, 12) ++ ++#define RX_FQ_START_ADDR 0x0500 ++#define RX_FQ_DEPTH 0x0504 ++#define BITS_RX_FQ_DEPTH mk_bits(0, 19) ++#define BITS_RX_FQ_ADDR_HI8 mk_bits(24, 8) ++#define RX_FQ_WR_ADDR 0x0508 ++#define BITS_RX_FQ_WR_ADDR mk_bits(0, 21) ++#define RX_FQ_RD_ADDR 0x050c ++#define BITS_RX_FQ_RD_ADDR mk_bits(0, 21) ++#define RX_FQ_VLDDESC_CNT 0x0510 ++#define BITS_RX_FQ_VLDDESC_CNT mk_bits(0, 16) ++#define RX_FQ_ALEMPTY_TH 0x0514 ++#define BITS_RX_FQ_ALEMPTY_TH mk_bits(0, 16) ++#define RX_FQ_REG_EN 0x0518 ++#define BITS_RX_FQ_START_ADDR_EN mk_bits(2, 1) ++#define BITS_RX_FQ_DEPTH_EN mk_bits(1, 1) ++#define BITS_RX_FQ_RD_ADDR_EN mk_bits(0, 1) ++#define RX_FQ_ALFULL_TH 0x051c ++#define BITS_RX_FQ_ALFULL_TH mk_bits(0, 16) ++ ++#define RX_BQ_START_ADDR 0x0520 ++#define RX_BQ_DEPTH 0x0524 ++#define BITS_RX_BQ_DEPTH mk_bits(0, 19) ++#define BITS_RX_BQ_ADDR_HI8 mk_bits(24, 8) ++#define RX_BQ_WR_ADDR 0x0528 ++#define BITS_RX_BQ_WR_ADDR mk_bits(0, 21) ++#define RX_BQ_RD_ADDR 0x052c ++#define BITS_RX_BQ_RD_ADDR mk_bits(0, 21) ++#define RX_BQ_FREE_DESC_CNT 0x0530 ++#define BITS_RX_BQ_FREE_DESC_CNT mk_bits(0, 16) ++#define RX_BQ_ALEMPTY_TH 0x0534 ++#define BITS_RX_BQ_ALEMPTY_TH mk_bits(0, 16) ++#define RX_BQ_REG_EN 0x0538 ++#define BITS_RX_BQ_START_ADDR_EN mk_bits(2, 1) ++#define BITS_RX_BQ_DEPTH_EN mk_bits(1, 1) ++#define BITS_RX_BQ_WR_ADDR_EN mk_bits(0, 1) ++#define RX_BQ_ALFULL_TH 0x053c ++#define BITS_RX_BQ_ALFULL_TH mk_bits(0, 16) ++ ++#define TX_BQ_START_ADDR 0x0580 ++#define TX_BQ_DEPTH 0x0584 ++#define BITS_TX_BQ_DEPTH mk_bits(0, 19) ++#define BITS_TX_BQ_ADDR_HI8 mk_bits(24, 8) ++#define TX_BQ_WR_ADDR 0x0588 ++#define BITS_TX_BQ_WR_ADDR mk_bits(0, 21) ++#define TX_BQ_RD_ADDR 0x058c ++#define BITS_TX_BQ_RD_ADDR mk_bits(0, 21) ++#define TX_BQ_VLDDESC_CNT 0x0590 ++#define BITS_TX_BQ_VLDDESC_CNT mk_bits(0, 16) ++#define TX_BQ_ALEMPTY_TH 0x0594 ++#define BITS_TX_BQ_ALEMPTY_TH mk_bits(0, 16) ++#define TX_BQ_REG_EN 0x0598 ++#define BITS_TX_BQ_START_ADDR_EN mk_bits(2, 1) ++#define BITS_TX_BQ_DEPTH_EN mk_bits(1, 1) ++#define BITS_TX_BQ_RD_ADDR_EN mk_bits(0, 1) ++#define TX_BQ_ALFULL_TH 0x059c ++#define BITS_TX_BQ_ALFULL_TH mk_bits(0, 16) ++ ++#define TX_RQ_START_ADDR 0x05a0 ++#define TX_RQ_DEPTH 0x05a4 ++#define BITS_TX_RQ_DEPTH mk_bits(0, 19) ++#define BITS_TX_RQ_ADDR_HI8 mk_bits(24, 8) ++#define TX_RQ_WR_ADDR 0x05a8 ++#define BITS_TX_RQ_WR_ADDR mk_bits(0, 21) ++#define TX_RQ_RD_ADDR 0x05ac ++#define BITS_TX_RQ_RD_ADDR mk_bits(0, 21) ++#define TX_RQ_FREE_DESC_CNT 0x05b0 ++#define BITS_TX_RQ_FREE_DESC_CNT mk_bits(0, 16) ++#define TX_RQ_ALEMPTY_TH 0x05b4 ++#define BITS_TX_RQ_ALEMPTY_TH mk_bits(0, 16) ++#define TX_RQ_REG_EN 0x05b8 ++#define BITS_TX_RQ_START_ADDR_EN mk_bits(2, 1) ++#define BITS_TX_RQ_DEPTH_EN mk_bits(1, 1) ++#define BITS_TX_RQ_WR_ADDR_EN mk_bits(0, 1) ++#define TX_RQ_ALFULL_TH 0x05bc ++#define BITS_TX_RQ_ALFULL_TH mk_bits(0, 16) ++ ++#define RAW_PMU_INT 0x05c0 ++ ++#define ENA_PMU_INT 0x05c4 ++#define ENA_BITS_MAC_FIFO_ERR_INT mk_bits(30, 1) ++#define ENA_BITS_TX_RQ_IN_TIMEOUT_INT mk_bits(29, 1) ++#define ENA_BITS_RX_BQ_IN_TIMEOUT_INT mk_bits(28, 1) ++#define ENA_BITS_TXOUTCFF_FULL_INT mk_bits(27, 1) ++#define ENA_BITS_TXOUTCFF_EMPTY_INT mk_bits(26, 1) ++#define ENA_BITS_TXCFF_FULL_INT mk_bits(25, 1) ++#define ENA_BITS_TXCFF_EMPTY_INT mk_bits(24, 1) ++#define ENA_BITS_RXOUTCFF_FULL_INT mk_bits(23, 1) ++#define ENA_BITS_RXOUTCFF_EMPTY_INT mk_bits(22, 1) ++#define ENA_BITS_RXCFF_FULL_INT mk_bits(21, 1) ++#define ENA_BITS_RXCFF_EMPTY_INT mk_bits(20, 1) ++#define ENA_BITS_TX_RQ_IN_INT mk_bits(19, 1) ++#define ENA_BITS_TX_BQ_OUT_INT mk_bits(18, 1) ++#define ENA_BITS_RX_BQ_IN_INT mk_bits(17, 1) ++#define ENA_BITS_RX_FQ_OUT_INT mk_bits(16, 1) ++#define ENA_BITS_TX_RQ_EMPTY_INT mk_bits(15, 1) ++#define ENA_BITS_TX_RQ_FULL_INT mk_bits(14, 1) ++#define ENA_BITS_TX_RQ_ALEMPTY_INT mk_bits(13, 1) ++#define ENA_BITS_TX_RQ_ALFULL_INT mk_bits(12, 1) ++#define ENA_BITS_TX_BQ_EMPTY_INT mk_bits(11, 1) ++#define ENA_BITS_TX_BQ_FULL_INT mk_bits(10, 1) ++#define ENA_BITS_TX_BQ_ALEMPTY_INT mk_bits(9, 1) ++#define ENA_BITS_TX_BQ_ALFULL_INT mk_bits(8, 1) ++#define ENA_BITS_RX_BQ_EMPTY_INT mk_bits(7, 1) ++#define ENA_BITS_RX_BQ_FULL_INT mk_bits(6, 1) ++#define ENA_BITS_RX_BQ_ALEMPTY_INT mk_bits(5, 1) ++#define ENA_BITS_RX_BQ_ALFULL_INT mk_bits(4, 1) ++#define ENA_BITS_RX_FQ_EMPTY_INT mk_bits(3, 1) ++#define ENA_BITS_RX_FQ_FULL_INT mk_bits(2, 1) ++#define ENA_BITS_RX_FQ_ALEMPTY_INT mk_bits(1, 1) ++#define ENA_BITS_RX_FQ_ALFULL_INT mk_bits(0, 1) ++ ++#define STATUS_PMU_INT 0x05c8 ++#define STATUS_BITS_MAC_FIFO_ERR_INT mk_bits(30, 1) ++#define STATUS_BITS_TX_RQ_IN_TIMEOUT_INT mk_bits(29, 1) ++#define STATUS_BITS_RX_BQ_IN_TIMEOUT_INT mk_bits(28, 1) ++#define STATUS_BITS_TXOUTCFF_FULL_INT mk_bits(27, 1) ++#define STATUS_BITS_TXOUTCFF_EMPTY_INT mk_bits(26, 1) ++#define STATUS_BITS_TXCFF_FULL_INT mk_bits(25, 1) ++#define STATUS_BITS_TXCFF_EMPTY_INT mk_bits(24, 1) ++#define STATUS_BITS_RXOUTCFF_FULL_INT mk_bits(23, 1) ++#define STATUS_BITS_RXOUTCFF_EMPTY_INT mk_bits(22, 1) ++#define STATUS_BITS_RXCFF_FULL_INT mk_bits(21, 1) ++#define STATUS_BITS_RXCFF_EMPTY_INT mk_bits(20, 1) ++#define STATUS_BITS_TX_RQ_IN_INT mk_bits(19, 1) ++#define STATUS_BITS_TX_BQ_OUT_INT mk_bits(18, 1) ++#define STATUS_BITS_RX_BQ_IN_INT mk_bits(17, 1) ++#define STATUS_BITS_RX_FQ_OUT_INT mk_bits(16, 1) ++#define STATUS_BITS_TX_RQ_EMPTY_INT mk_bits(15, 1) ++#define STATUS_BITS_TX_RQ_FULL_INT mk_bits(14, 1) ++#define STATUS_BITS_TX_RQ_ALEMPTY_INT mk_bits(13, 1) ++#define STATUS_BITS_TX_RQ_ALFULL_INT mk_bits(12, 1) ++#define STATUS_BITS_TX_BQ_EMPTY_INT mk_bits(11, 1) ++#define STATUS_BITS_TX_BQ_FULL_INT mk_bits(10, 1) ++#define STATUS_BITS_TX_BQ_ALEMPTY_INT mk_bits(9, 1) ++#define STATUS_BITS_TX_BQ_ALFULL_INT mk_bits(8, 1) ++#define STATUS_BITS_RX_BQ_EMPTY_INT mk_bits(7, 1) ++#define STATUS_BITS_RX_BQ_FULL_INT mk_bits(6, 1) ++#define STATUS_BITS_RX_BQ_ALEMPTY_INT mk_bits(5, 1) ++#define STATUS_BITS_RX_BQ_ALFULL_INT mk_bits(4, 1) ++#define STATUS_BITS_RX_FQ_EMPTY_INT mk_bits(3, 1) ++#define STATUS_BITS_RX_FQ_FULL_INT mk_bits(2, 1) ++#define STATUS_BITS_RX_FQ_ALEMPTY_INT mk_bits(1, 1) ++#define STATUS_BITS_RX_FQ_ALFULL_INT mk_bits(0, 1) ++ ++#define DESC_WR_RD_ENA 0x05CC ++ ++#define IN_QUEUE_TH 0x05d8 ++#define BITS_TX_RQ_IN_TH mk_bits(16, 8) ++#define BITS_RX_BQ_IN_TH mk_bits(0, 8) ++ ++#define OUT_QUEUE_TH 0x05dc ++#define BITS_TX_BQ_OUT_TH mk_bits(16, 8) ++#define BITS_RX_FQ_OUT_TH mk_bits(0, 8) ++ ++#define RX_BQ_IN_TIMEOUT_TH 0x05E0 ++#define BITS_RX_BQ_IN_TIMEOUT_TH mk_bits(0, 24) ++ ++#define TX_RQ_IN_TIMEOUT_TH 0x05e4 ++#define BITS_TX_RQ_IN_TIMEOUT_TH mk_bits(0, 24) ++ ++#define STOP_CMD 0x05e8 ++#define BITS_TX_STOP_EN mk_bits(1, 1) ++#define BITS_RX_STOP_EN mk_bits(0, 1) ++ ++#define FLUSH_CMD 0x05EC ++#define BITS_TX_FLUSH_CMD mk_bits(3, 1) ++#define BITS_RX_FLUSH_CMD mk_bits(2, 1) ++#define BITS_TX_FLUSH_FLAG mk_bits(1, 1) ++#define BITS_RX_FLUSH_FLAG mk_bits(0, 1) ++ ++#define RAW_INT_ALL_MASK 0xffffffff ++ ++void gmac_set_macif(struct gmac_netdev_local const *ld, ++ int mode, unsigned int speed); ++int gmac_hw_set_macaddress(struct gmac_netdev_local const *ld, ++ const unsigned char *mac, int len); ++int gmac_hw_get_macaddress(struct gmac_netdev_local const *ld, ++ unsigned char *mac, int len); ++int gmac_glb_preinit_dummy(struct gmac_netdev_local const *ld); ++void gmac_sys_init(void); ++void gmac_sys_exit(void); ++void gmac_sys_allstop(void); ++int gmac_set_hwq_depth(struct gmac_netdev_local const *ld); ++int gmac_set_rx_fq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr); ++int gmac_set_rx_bq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr); ++int gmac_set_tx_bq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr); ++int gmac_set_tx_rq_hwq_addr(struct gmac_netdev_local const *ld, ++ phys_addr_t phy_addr); ++int gmac_clear_irqstatus(struct gmac_netdev_local const *ld, int irqs); ++int gmac_read_irqstatus(struct gmac_netdev_local *ld); ++int gmac_irq_enable(struct gmac_netdev_local *ld, int irqs); ++int gmac_irq_disable(struct gmac_netdev_local *ld, int irqs); ++void gmac_desc_enable(struct gmac_netdev_local const *ld, u32 desc_ena); ++void gmac_desc_disable(struct gmac_netdev_local const *ld, u32 desc_dis); ++void gmac_desc_flush(struct gmac_netdev_local const *ld); ++ ++u32 gmac_readl(struct gmac_netdev_local const *ld, u32 ofs); ++void gmac_writel(struct gmac_netdev_local const *ld, u32 v, u32 ofs); ++u32 gmac_readl_bits(struct gmac_netdev_local const *ld, u32 ofs, u32 bits_desc); ++void gmac_writel_bits(struct gmac_netdev_local const *ld, u32 v, u32 ofs, u32 bits_desc); ++void gmac_trace(int level, const char *fmt, ...); ++void gmac_error(const char *fmt, ...); ++void gmac_assert(bool cond); ++#endif ++ +diff --git a/drivers/net/gmacv300/gmac.c b/drivers/net/gmacv300/gmac.c +new file mode 100644 +index 0000000..e28b30b +--- /dev/null ++++ b/drivers/net/gmacv300/gmac.c +@@ -0,0 +1,1127 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include "gmac.h" ++#include "ctrl.h" ++#include "mdio.h" ++ ++typedef enum { ++ GSF0_PORT0, ++#if GMAC_AT_LEAST_2PORT ++ GSF0_PORT1, ++#endif ++#if GMAC_AT_LEAST_3PORT ++ GSF1_PORT0, ++#endif ++ MAX_PORT_NUM, ++} port_id_t; ++ ++static int mac_iobase[MAX_PORT_NUM] = { ++ GMAC0_IOBASE, ++#if GMAC_AT_LEAST_2PORT ++ GMAC1_IOBASE, ++#endif ++#if GMAC_AT_LEAST_3PORT ++ GMAC2_IOBASE, ++#endif ++}; ++ ++/* this driver only support GMAC_NUMS = 2 or 1 */ ++#define MDIO_BUS_NUMS 2 ++struct gmac_board_info g_gmac_board_info[CONFIG_GMAC_NUMS] = { ++ { ++ { ++ .index = GSF0_PORT0, ++ .iobase = GMAC0_IOBASE, ++ .iobase_phys = GMAC0_IOBASE, ++ }, ++ .mii_name = "mdio0", ++ .phy_intf = CONFIG_GMAC_PHY0_INTERFACE_MODE, ++ .phy_addr = CONFIG_GMAC_PHY0_ADDR, ++ }, ++#if GMAC_AT_LEAST_2PORT ++ { ++ { ++ .index = GSF0_PORT1, ++ .iobase = GMAC1_IOBASE, ++ .iobase_phys = GMAC1_IOBASE, ++ }, ++ .mii_name = "mdio1", ++ .phy_intf = CONFIG_GMAC_PHY1_INTERFACE_MODE, ++ .phy_addr = CONFIG_GMAC_PHY1_ADDR, ++ } ++#endif ++}; ++ ++static int g_phy_link_times; ++static int g_gmac_debug; ++ ++static char *phy_intf_str[INTERFACE_MODE_BUTT] = { ++ "mii", ++ "rmii", ++ "rgmii", ++}; ++static char *mdio_bus[CONFIG_GMAC_NUMS] = { ++ "mdio0", ++#if GMAC_AT_LEAST_2PORT ++ "mdio1", ++#endif ++#if GMAC_AT_LEAST_3PORT ++ "mdio2" ++#endif ++}; ++ ++#if defined(CONFIG_MACH_GODEYES) ++/* ++ * port mode def. ++ * PORT_MODE_BUTT means not support this mode. ++ */ ++static int g_speed_portmode_table[SPEED_MODE_BUTT][INTERFACE_MODE_BUTT] = { ++ {PORT_MODE_10_MII, PORT_MODE_BUTT, PORT_MODE_10_RGMII}, ++ {PORT_MODE_100_MII, PORT_MODE_BUTT, PORT_MODE_100_RGMII}, ++ {PORT_MODE_BUTT, PORT_MODE_BUTT, PORT_MODE_1000_RGMII} ++}; ++#else ++static int g_speed_portmode_table[SPEED_MODE_BUTT][INTERFACE_MODE_BUTT] = { ++ {PORT_MODE_10_MII, PORT_MODE_10_RMII, PORT_MODE_10_RGMII}, ++ {PORT_MODE_100_MII, PORT_MODE_100_RMII, PORT_MODE_100_RGMII}, ++ {PORT_MODE_BUTT, PORT_MODE_BUTT, PORT_MODE_1000_RGMII} ++}; ++#endif ++ ++static int calculate_port_mode(enum speed_mode speed, enum if_mode if_mode, ++ int is_duplex_half) ++{ ++ if (speed < SPEED_MODE_BUTT && if_mode < INTERFACE_MODE_BUTT) { ++ int ret = g_speed_portmode_table[speed][if_mode]; ++ ++ if (is_duplex_half) ++ ret = ((unsigned int)ret) & (~bit(4)); /* bit 4 see mac_if reg */ ++ return ret; ++ } ++ ++ printf("Invalid speed=%d & interface=%d mode.\n", speed, if_mode); ++ printf("Please assign which mode our eth will correctly work at.\n" ++ "It may support 10M/100M MII, 10M/100M RMII, " ++ "10M/100M/1000M RGMII interface.\n" ++ "eg. if your board have two NICs, connecting each phy using " ++ "mii and rgmii interface, you can use the module param " ++ "'port_mode=mii,rgmii' to tell the driver.\n"); ++ BUG(); ++ ++ /* can't reach here */ ++ return -1; ++} ++ ++static void set_gmac_debug_switch(void) ++{ ++ char *s = NULL; ++ char *e = NULL; ++ char evn_buf[ENV_BUF_LEN] = {0}; ++ int i = 0; ++ ++ s = env_get("gmac_debug"); ++ if (s != NULL) { ++ while (*s != '\0' && i < sizeof(evn_buf) - 1) ++ evn_buf[i++] = *s++; ++ evn_buf[i] = '\0'; ++ g_gmac_debug = (int)simple_strtoul(evn_buf, &e, 10); /* Base 10 */ ++ } else { ++ g_gmac_debug = 0; ++ } ++} ++ ++static void set_phy_link_times(void) ++{ ++ char *s = NULL; ++ char *e = NULL; ++ char evn_buf[ENV_BUF_LEN] = {0}; ++ int i = 0; ++ ++ s = env_get("phy_link_times"); ++ if (s != NULL) { ++ while (*s != '\0' && i < sizeof(evn_buf) - 1) ++ evn_buf[i++] = *s++; ++ evn_buf[i] = '\0'; ++ g_phy_link_times = (int)simple_strtoul(evn_buf, &e, 10); /* Base 10 */ ++ } else { ++ g_phy_link_times = DEFAULT_PHY_LINK_TIMES; ++ } ++} ++ ++static void set_mdio_intf(void) ++{ ++ int gmac = 0; ++ char *s = NULL; ++ char evn_buf[ENV_BUF_LEN] = {0}; ++ int i = 0; ++ ++ s = env_get("mdio_intf"); ++ if (s != NULL) { ++ while (*s != '\0' && i < sizeof(evn_buf) - 1) ++ evn_buf[i++] = *s++; ++ evn_buf[i] = '\0'; ++ s = evn_buf; ++ } ++next_mii: ++ if (s != NULL) { ++ enum if_mode if_mode; ++ ++ while (*s == ' ' || *s == ',') ++ s++; ++ ++ if (!strncmp(s, "mii", strlen("mii"))) { ++ if_mode = INTERFACE_MODE_MII; ++ } else if (!strncmp(s, "rmii", strlen("rmii"))) { ++ if_mode = INTERFACE_MODE_RMII; ++ } else if (!strncmp(s, "rgmii", strlen("rgmii"))) { ++ if_mode = INTERFACE_MODE_RGMII; ++ } else { ++ printf("Invalid phy_intf(mii, rmii, or rgmii), " \ ++ "Set ETH%d default to mii\n", gmac); ++ if_mode = INTERFACE_MODE_MII; ++ } ++ ++ if (!gmac) { /* first time */ ++ gmac = 1; ++ g_gmac_board_info[0].phy_intf = if_mode; ++ /* in case phy_intf=mii */ ++#if GMAC_AT_LEAST_2PORT ++ g_gmac_board_info[1].phy_intf = if_mode; ++#endif ++ s = strchr(s, ','); ++ goto next_mii; ++ } else { ++#if GMAC_AT_LEAST_2PORT ++ g_gmac_board_info[1].phy_intf = if_mode; ++#endif ++ } ++ } ++} ++ ++#if GMAC_AT_LEAST_2PORT ++static void set_use_mdio(void) ++{ ++ int gmac = 0; ++ unsigned long tmp; ++ char *s = NULL; ++ char *e = NULL; ++ ++ /* use_mdio=0 or use_mdio=1 or use_mdio=0,1 or ... */ ++ s = env_get("use_mdio"); ++next_mdio: ++ if (s && gmac < CONFIG_GMAC_NUMS) { ++ while (*s == ' ' || *s == ',') ++ s++; ++ ++ tmp = simple_strtoul(s, &e, 10); /* Base 10 */ ++ if (tmp >= MDIO_BUS_NUMS) { ++ printf("Invalid use_mdio, Set GMAC%d use mdio%d.\n", ++ gmac, gmac); ++ tmp = gmac; ++ } ++ ++ if (gmac == 0) { /* first time */ ++ g_gmac_board_info[0].mii_name = mdio_bus[tmp]; ++ g_gmac_board_info[1].mii_name = mdio_bus[tmp]; ++ gmac++; ++ s = e; ++ s = strchr(s, ','); ++ goto next_mdio; ++ } else { ++ g_gmac_board_info[1].mii_name = mdio_bus[tmp]; ++ } ++ } ++} ++#endif ++ ++static void set_phy_addr(void) ++{ ++ int gmac = 0; ++ unsigned long tmp; ++ char *s = NULL; ++ char *e = NULL; ++ ++ /* get phy addr */ ++ s = env_get("phy_addr"); ++next_phyaddr: ++ if (s && gmac < CONFIG_GMAC_NUMS) { ++ while (*s == ' ' || *s == ',') ++ s++; ++ ++ tmp = simple_strtoul(s, &e, 10); /* Base 10 */ ++ if (tmp == 0) { ++ printf("Dectected gmac%d phyaddr set to 0, " \ ++ "is it right?\n", gmac); ++ } else if (tmp >= 32) { /* must less than 32 */ ++ printf("Dectected gmac%d phyaddr " \ ++ "set to val(%lu) >= 32, " \ ++ "This may not correct, " \ ++ "use default phyaddr=%d\n", ++ gmac, tmp, ++ g_gmac_board_info[gmac].phy_addr); ++ ++ tmp = g_gmac_board_info[gmac].phy_addr; ++ } ++ if (gmac == 0) { /* gmac0 */ ++ g_gmac_board_info[gmac].phy_addr = tmp; ++ gmac++; ++ s = e; ++ s = strchr(s, ','); ++ goto next_phyaddr; ++ } else { /* gmac1 */ ++#if GMAC_AT_LEAST_2PORT ++ g_gmac_board_info[gmac].phy_addr = tmp; ++#endif ++ } ++ } ++} ++ ++/* module parameter ++ * 1) phy_intf=mii,rgmii if eth0=mii, eth1=rgmii ++ * phy_intf=mii if eth0=mii, eth1=mii; ++ * 2) use_internal_phy=1 if eth1 use internal phy; ++ * 3) phy0_addr=1, phy1_addr=2 ++ * 4) g_gmac_debug=1 ++ */ ++static void parse_module_parameters(void) ++{ ++ u32 gmac; ++ ++ set_gmac_debug_switch(); ++ set_phy_link_times(); ++ ++ /* phy_intf = {mii | rgmii | rmii} [,{mii | rgmii | rmii}] */ ++ set_mdio_intf(); ++ ++#if GMAC_AT_LEAST_2PORT ++ set_use_mdio(); ++#endif ++ set_phy_addr(); ++ if (g_gmac_debug) { ++ printf("phy_link_times=%d\n", g_phy_link_times); ++ for (gmac = 0; gmac < CONFIG_GMAC_NUMS; gmac++) ++ printf("ETH%d: port_mode=%s, " \ ++ "mii_name=%s, phy_addr=%d\n", ++ gmac, ++ phy_intf_str[g_gmac_board_info[gmac].phy_intf], ++ g_gmac_board_info[gmac].mii_name, ++ g_gmac_board_info[gmac].phy_addr); ++ printf("\n"); ++ } ++} ++ ++static int gmac_net_set_mac_address(struct eth_device* const dev) ++{ ++ struct gmac_netdev_local *ld = (struct gmac_netdev_local *)dev->priv; ++ unsigned char mac[MAC_LEN] = {0}; ++ int ret; ++ ++ ret = eth_env_get_enetaddr("ethaddr", mac); ++ if (ret == 0) { ++ printf("MAC address invalid!\n"); ++#ifdef CONFIG_NET_RANDOM_ETHADDR ++ net_random_ethaddr(mac); ++ printf("Set Random MAC address!\n"); ++ eth_setenv_enetaddr("ethaddr", mac); ++#endif ++ } ++ ++ gmac_hw_set_macaddress(ld, mac, MAC_LEN); ++ if (memcpy_s(dev->enetaddr, sizeof(dev->enetaddr), mac, MAC_LEN)) ++ printf("memcpy_s failed!\n"); ++ ++ return 0; ++} ++ ++static gmac_desc* gmac_memalign_queue_addr(unsigned int queue_count) ++{ ++ unsigned int size = queue_count * sizeof(gmac_desc); ++ gmac_desc *queue_phy_addr = NULL; ++ queue_phy_addr = (gmac_desc *)memalign(ARCH_DMA_MINALIGN, size); ++ if (queue_phy_addr == NULL) ++ return NULL; ++ ++ if (memset_s((void *)queue_phy_addr, size, 0, size) < 0) ++ printf("memset_s failed!\n"); ++#ifndef CONFIG_SYS_DCACHE_OFF ++ flush_cache((uintptr_t)queue_phy_addr, ALIGN(size, ARCH_DMA_MINALIGN)); ++#endif ++ return queue_phy_addr; ++} ++ ++static int gmac_init_hw_desc_queue(struct gmac_netdev_local *ld) ++{ ++ gmac_desc *queue_phy_addr = NULL; ++ ++ /* init rx fq */ ++ queue_phy_addr = gmac_memalign_queue_addr(GMAC_HWQ_RX_FQ_DEPTH); ++ if (queue_phy_addr == NULL) { ++ printf("alloc rx fq error!\n"); ++ goto _error_alloc_rx_fq; ++ } ++ ++ ld->rx_fq_addr = queue_phy_addr; ++ gmac_set_rx_fq_hwq_addr(ld, (phys_addr_t)(uintptr_t)queue_phy_addr); ++ ++ /* init rx bq */ ++ queue_phy_addr = gmac_memalign_queue_addr(GMAC_HWQ_RX_BQ_DEPTH); ++ if (queue_phy_addr == NULL) { ++ printf("alloc rx bq error!\n"); ++ goto _error_alloc_rx_bq; ++ } ++ ++ ld->rx_bq_addr = queue_phy_addr; ++ gmac_set_rx_bq_hwq_addr(ld, (phys_addr_t)(uintptr_t)queue_phy_addr); ++ ++ /* init tx bq */ ++ queue_phy_addr = gmac_memalign_queue_addr(GMAC_HWQ_TX_BQ_DEPTH); ++ if (queue_phy_addr == NULL) { ++ printf("alloc tx bq error!\n"); ++ goto _error_alloc_tx_bq; ++ } ++ ++ ld->tx_bq_addr = queue_phy_addr; ++ gmac_set_tx_bq_hwq_addr(ld, (phys_addr_t)(uintptr_t)queue_phy_addr); ++ ++ /* init tx rq */ ++ queue_phy_addr = gmac_memalign_queue_addr(GMAC_HWQ_TX_RQ_DEPTH); ++ if (queue_phy_addr == NULL) { ++ printf("alloc tx rq error!\n"); ++ goto _error_alloc_tx_rq; ++ } ++ ++ ld->tx_rq_addr = queue_phy_addr; ++ gmac_set_tx_rq_hwq_addr(ld, (phys_addr_t)(uintptr_t)queue_phy_addr); ++ ++ return 0; ++ ++_error_alloc_tx_rq: ++ free(ld->tx_bq_addr); ++ ld->tx_bq_addr = NULL; ++ ++_error_alloc_tx_bq: ++ free(ld->rx_bq_addr); ++ ld->rx_bq_addr = NULL; ++ ++_error_alloc_rx_bq: ++ free(ld->rx_fq_addr); ++ ld->rx_fq_addr = NULL; ++ ++_error_alloc_rx_fq: ++ return -1; ++} ++ ++#define PHY_ID_KSZ8051 0x00221550 ++#define PHY_ID_KSZ8081 0x00221560 ++#define PHY_ID_KSZ9031 0x00221620 ++#define PHY_ID_RTL8201 0x001c1100 ++#define PHY_ID_MASK 0xFFFFFFF0 ++ ++/* MMD: MDIO Manageable Device */ ++#define MACR 0x0D ++#define MAADR 0x0E ++static void phy_mmd_read(const char *devname, u32 phyaddr, ++ u32 mmd_device, u32 regnum, u32 const *val) ++{ ++ int ret; ++ ++ miiphy_write(devname, phyaddr, MACR, mmd_device); ++ miiphy_write(devname, phyaddr, MAADR, regnum); ++ miiphy_write(devname, phyaddr, MACR, 0x4000 | mmd_device); ++ ++ ret = miiphy_read(devname, phyaddr, MAADR, (unsigned short *)val); ++ if (ret != 0) ++ printf("%s mmd read phy %d dev %d reg 0x%x failed\n", ++ devname, phyaddr, mmd_device, regnum); ++} ++ ++static void phy_mmd_write(const char *devname, u32 phyaddr, ++ u32 mmd_device, u32 regnum, u32 val) ++{ ++ miiphy_write(devname, phyaddr, MACR, mmd_device); ++ miiphy_write(devname, phyaddr, MAADR, regnum); ++ miiphy_write(devname, phyaddr, MACR, 0x4000 | mmd_device); ++ ++ miiphy_write(devname, phyaddr, MAADR, val); ++} ++ ++static int phy_detected(const char* devname, unsigned int phyaddr, u32 *const phy_id) ++{ ++ u16 id1 = 0; ++ u16 id2 = 0; ++ ++ if (miiphy_read(devname, phyaddr, MII_PHYSID1, &id1) != 0) { ++ printf("PHY IDR1 read failed\n"); ++ return 0; ++ }; ++ if (miiphy_read(devname, phyaddr, MII_PHYSID2, &id2) != 0) { ++ printf("PHY IDR2 read failed\n"); ++ return 0; ++ }; ++ ++ *phy_id = (id1 & 0xffff) << 16; /* shift 16 bit */ ++ *phy_id |= (id2 & 0xffff); ++ ++ /* If the phy_id is all Fs, there is no device there */ ++ if (*phy_id == 0xffffffff || *phy_id == 0 || ++ *phy_id == 0xFFFF || *phy_id == 0xFFFF0000) ++ return 0; ++ ++ return 1; ++} ++ ++static int phy_fixup(const char *devname, unsigned int phyaddr, enum if_mode phymode) ++{ ++ u32 phy_id = 0; ++ ++ if (!phy_detected(devname, phyaddr, &phy_id)) ++ return -1; ++ ++ /* PHY-KSZ8051 */ ++ if (((phy_id & PHY_ID_MASK) == PHY_ID_KSZ8051) && ++ (phymode == INTERFACE_MODE_RMII)) { ++ unsigned int val = 0; ++ ++ if (miiphy_read(devname, phyaddr, 0x1F, ++ (unsigned short *)&val) != 0) { ++ printf("PHY reg read failed\n"); ++ return -1; ++ }; ++ val |= bit(7); /* set (bit7) phy RMII 50MHz clk; */ ++ if (miiphy_write(devname, phyaddr, 0x1F, val) != 0) ++ return -1; ++ ++ if (miiphy_read(devname, phyaddr, 0x16, ++ (unsigned short *)&val) != 0) { ++ printf("PHY reg read failed\n"); ++ return -1; ++ }; ++ val |= bit(1); /* set phy RMII override; */ ++ if (miiphy_write(devname, phyaddr, 0x16, val) != 0) ++ return -1; ++ } ++ ++ /* PHY-KSZ8081 */ ++ if (((phy_id & PHY_ID_MASK) == PHY_ID_KSZ8081) && ++ (phymode == INTERFACE_MODE_RMII)) { ++ unsigned int val = 0; ++ ++ if (miiphy_read(devname, phyaddr, 0x1F, ++ (unsigned short *)&val) != 0) { ++ printf("PHY IDR1 read failed\n"); ++ return -1; ++ }; ++ val |= bit(7); /* set (bit7) phy RMII 50MHz clk; */ ++ if (miiphy_write(devname, phyaddr, 0x1F, val) != 0) ++ return -1; ++ } ++ ++ /* PHY-KSZ9031 */ ++ if ((phy_id & PHY_ID_MASK) == PHY_ID_KSZ9031) { ++ unsigned int val = 0; ++ ++ /* RX_CLK Pad Skew: 1_1101(+0.84) */ ++ phy_mmd_read(devname, phyaddr, 0x2, 0x8, &val); ++ val = (val & ~0x1F) | 0x1D; ++ phy_mmd_write(devname, phyaddr, 0x2, 0x8, val); ++ } ++ /* PHY-RTL-8201 */ ++ if ((phy_id & PHY_ID_MASK) == PHY_ID_RTL8201) { ++ unsigned int val = 0x07; ++ ++ miiphy_write(devname, phyaddr, 0x1F, val); ++ miiphy_write(devname, phyaddr, 0x10, 0x1ffa); ++ } ++ return 0; ++} ++ ++static void gmac_net_adjust_speed(struct gmac_netdev_local *ld, ++ unsigned int *link_stat, int is_duplex_half) ++{ ++ char *mii_name = g_gmac_board_info[ld->index].mii_name; ++ int phy_addr = g_gmac_board_info[ld->index].phy_addr; ++ int speed, port_mode; ++ unsigned int stat = *link_stat; ++ enum speed_mode speed_mode = SPEED_MODE_100M; ++ ++ speed = miiphy_speed(mii_name, phy_addr); ++ switch (speed) { ++ case _10BASET: ++ stat |= GMAC_SPD_10M; ++ speed_mode = SPEED_MODE_10M; ++ break; ++ case _100BASET: ++ stat |= GMAC_SPD_100M; ++ speed_mode = SPEED_MODE_100M; ++ break; ++ case _1000BASET: ++ stat |= GMAC_SPD_1000M; ++ speed_mode = SPEED_MODE_1000M; ++ break; ++ default: ++ printf("wired, phy speed!\n"); ++ break; ++ } ++ ++ if (ld->link_stat != stat) { ++ if (stat & GMAC_LINKED) { ++ port_mode = calculate_port_mode(speed_mode, ++ g_gmac_board_info[ld->index].phy_intf, is_duplex_half); ++ ++ gmac_set_macif(ld, port_mode, speed_mode); ++ ++ gmac_writel(ld, is_duplex_half ? 0x0 : 0x1, ++ MAC_DUPLEX_HALF_CTRL); ++ } ++ ld->link_stat = stat; ++ } ++ *link_stat = stat; ++} ++ ++static int gmac_net_adjust_link(struct gmac_netdev_local *ld) ++{ ++ char *mii_name = g_gmac_board_info[ld->index].mii_name; ++ int phy_addr = g_gmac_board_info[ld->index].phy_addr; ++ enum if_mode phy_mode = g_gmac_board_info[ld->index].phy_intf; ++ unsigned int stat = INVALID_LINK_STATUS; ++ int phy_duplex; ++ int is_duplex_half = 1; ++ u32 phy_id = 0; ++ ++ if (phy_addr == INVALID_PHY_ADDR) ++ return stat; ++ ++ phy_fixup(mii_name, phy_addr, phy_mode); ++ ++ if (!phy_detected(mii_name, phy_addr, &phy_id)) ++ return stat; ++ ++ stat |= miiphy_link(mii_name, phy_addr) ? GMAC_LINKED : 0; ++ ++ phy_duplex = miiphy_duplex(mii_name, phy_addr); ++ if (phy_duplex == FULL) { ++ stat |= GMAC_DUP_FULL; ++ is_duplex_half = 0; ++ } ++ ++ gmac_net_adjust_speed(ld, &stat, is_duplex_half); ++ ++ return stat; ++} ++ ++static int select_current_linked_phy(struct gmac_netdev_local *ld) ++{ ++ int count = g_phy_link_times ? : DEFAULT_PHY_LINK_TIMES; ++ unsigned int status; ++ ++ for (; count > 0; count--) { ++ if (ctrlc()) { ++ puts("\nAbort\n"); ++ goto link_failed; ++ } ++ ++ status = (unsigned int)gmac_net_adjust_link(ld); ++ if (status & GMAC_LINKED) ++ goto link_on; ++ mdelay(100); /* wait 100ms */ ++ } ++ ++link_failed: ++ printf("ETH%d: PHY(%s, phyaddr=%d, %s) not link!\n", ++ ld->index, g_gmac_board_info[ld->index].mii_name, ++ g_gmac_board_info[ld->index].phy_addr, ++ phy_intf_str[g_gmac_board_info[ld->index].phy_intf]); ++ ++ return -1; ++ ++link_on: ++ printf("ETH%d: PHY(phyaddr=%d, %s) link UP: DUPLEX=%s : SPEED=%s\n", ++ ld->index, g_gmac_board_info[ld->index].phy_addr, ++ phy_intf_str[g_gmac_board_info[ld->index].phy_intf], ++ (status & GMAC_DUP_FULL) ? "FULL" : "HALF", ++ (status & GMAC_SPD_100M) ? "100M" : ++ ((status & GMAC_SPD_10M) ? "10M" : "1000M")); ++ ++ miiphy_set_current_dev(g_gmac_board_info[ld->index].mii_name); ++ ++ return 0; ++} ++ ++static unsigned int get_wr_rd_dist(u32 rx_fq_wr_offset, u32 rx_fq_rd_offset) ++{ ++ unsigned int wr_rd_dist; ++ if (rx_fq_wr_offset >= rx_fq_rd_offset) ++ wr_rd_dist = (GMAC_HWQ_RX_FQ_DEPTH << DESC_BYTE_SHIFT) ++ - (rx_fq_wr_offset - rx_fq_rd_offset); ++ else ++ wr_rd_dist = rx_fq_rd_offset - rx_fq_wr_offset; ++ ++ /* offset was counted on bytes, desc size = 2^5 */ ++ wr_rd_dist >>= DESC_BYTE_SHIFT; ++ return wr_rd_dist; ++} ++ ++static void show_gmac_recv_msg(const char *addr, int len) ++{ ++ if (g_gmac_debug) { ++ int i; ++ printf("got packet!\n"); ++ printf("[%s:%d]Packet length = %#4x\n", __FUNCTION__, __LINE__, len); ++ for (i = 0; i < len; i++) { ++ if (i % 16 == 0) printf("%#4.4x", i); /* Each 16 print i */ ++ if (i % 2 == 0) printf(" "); /* Each 2 print space */ ++ printf("%2.2x", ((unsigned char *)addr)[i]); ++ if (i % 16 == 15) printf("\n"); /* 15 is last line of 16 */ ++ } ++ printf("\n\n\n\n"); ++ } ++} ++ ++#define NET_IP_ALIGN 2 ++static void gmac_recv_rx_fq(struct gmac_netdev_local const *ld) ++{ ++ gmac_desc *rx_fq_desc = ld->rx_fq_addr; ++ u32 rx_fq_wr_offset, rx_fq_rd_offset; ++ unsigned int wr_rd_dist; ++ phys_addr_t addr; ++ int i; ++ ++ rx_fq_wr_offset = gmac_readl_bits(ld, RX_FQ_WR_ADDR, BITS_RX_FQ_WR_ADDR); ++ rx_fq_rd_offset = gmac_readl_bits(ld, RX_FQ_RD_ADDR, BITS_RX_FQ_RD_ADDR); ++ ++ wr_rd_dist = get_wr_rd_dist(rx_fq_wr_offset, rx_fq_rd_offset); ++ ++ /* wr_rd_dist - 1 for logic reason. ++ * Logic think the desc pool is full filled, ...? ++ */ ++ for (i = 0; i < wr_rd_dist - 1; i++) { ++ rx_fq_desc = ld->rx_fq_addr + (rx_fq_wr_offset >> DESC_BYTE_SHIFT); ++ addr = (phys_addr_t)(uintptr_t)memalign(ARCH_DMA_MINALIGN, ++ ETH_BUFFER_SIZE); ++ if (!addr) ++ break; ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range(addr, ++ ALIGN(addr + ETH_BUFFER_SIZE, ARCH_DMA_MINALIGN)); ++#endif ++ addr += NET_IP_ALIGN; ++ rx_fq_desc->data_buff_addr = (u32)addr; ++#if defined(CONFIG_64BIT) ++ rx_fq_desc->reserve4 = ((u64)addr >> REG_BIT_WIDTH) << Q_ADDR_HI8_OFFSET; ++#endif ++ rx_fq_desc->descvid = DESC_VLD_FREE; ++ rx_fq_desc->buffer_len = (ETH_MAX_FRAME_SIZE - 1); ++ rx_fq_desc->data_len = 8; /* data length 8 */ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ flush_cache((uintptr_t)rx_fq_desc & ~(ARCH_DMA_MINALIGN - 1), ++ ALIGN(sizeof(*rx_fq_desc), ARCH_DMA_MINALIGN)); ++#endif ++ ++ rx_fq_wr_offset += DESC_SIZE; ++ if (rx_fq_wr_offset >= (GMAC_HWQ_RX_FQ_DEPTH << DESC_BYTE_SHIFT)) ++ rx_fq_wr_offset = 0; ++ gmac_writel_bits(ld, rx_fq_wr_offset, RX_FQ_WR_ADDR, ++ BITS_RX_FQ_WR_ADDR); ++ } ++} ++ ++static int gmac_recv(struct eth_device *dev) ++{ ++ struct gmac_netdev_local *ld = (struct gmac_netdev_local *)dev->priv; ++ gmac_desc *rx_bq_desc = ld->rx_bq_addr; ++ u32 rx_bq_wr_offset, rx_bq_rd_offset; ++ int timeout_us = 100000; ++ phys_addr_t addr; ++ int len; ++ ++ gmac_recv_rx_fq(ld); ++ ++ rx_bq_wr_offset = gmac_readl_bits(ld, RX_BQ_WR_ADDR, BITS_RX_BQ_WR_ADDR); ++ rx_bq_rd_offset = gmac_readl_bits(ld, RX_BQ_RD_ADDR, BITS_RX_BQ_RD_ADDR); ++ rx_bq_desc += (rx_bq_rd_offset >> DESC_BYTE_SHIFT); ++ ++ while (--timeout_us && (rx_bq_wr_offset == rx_bq_rd_offset)) { ++ udelay(1); ++ rx_bq_wr_offset = gmac_readl_bits(ld, RX_BQ_WR_ADDR, BITS_RX_BQ_WR_ADDR); ++ } ++ if (!timeout_us) ++ return -1; ++ ++ rx_bq_rd_offset += DESC_SIZE; ++ if (rx_bq_rd_offset >= (GMAC_HWQ_RX_BQ_DEPTH << DESC_BYTE_SHIFT)) ++ rx_bq_rd_offset = 0; ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range((uintptr_t)rx_bq_desc & ~(ARCH_DMA_MINALIGN - 1), ++ ALIGN((uintptr_t)rx_bq_desc + sizeof(*rx_bq_desc), ARCH_DMA_MINALIGN)); ++#endif ++ addr = rx_bq_desc->data_buff_addr; ++#if defined(CONFIG_64BIT) ++ addr |= (u64)(rx_bq_desc->reserve4 >> Q_ADDR_HI8_OFFSET) << REG_BIT_WIDTH; ++#endif ++ len = rx_bq_desc->data_len; ++ if (gmac_invalid_rxpkg_len(len)) { ++ gmac_writel_bits(ld, rx_bq_rd_offset, RX_BQ_RD_ADDR, BITS_RX_BQ_RD_ADDR); ++ addr -= NET_IP_ALIGN; ++ free((void *)(uintptr_t)addr); ++ addr = 0; ++ return -1; ++ } ++ ++ show_gmac_recv_msg((char *)(uintptr_t)addr, len); ++ ++#ifndef CONFIG_SYS_DCACHE_OFF ++ invalidate_dcache_range(addr - NET_IP_ALIGN, ++ ALIGN((unsigned long)(addr + len), ARCH_DMA_MINALIGN)); ++#endif ++ if (memcpy_s((void *)net_rx_packets[0], len, (const void *)(uintptr_t)addr, len)) ++ printf("memcpy_s failed!\n"); ++ ++ addr -= NET_IP_ALIGN; ++ free((void *)(uintptr_t)addr); ++ addr = 0; ++ ++ gmac_writel_bits(ld, rx_bq_rd_offset, RX_BQ_RD_ADDR, BITS_RX_BQ_RD_ADDR); ++ ++ /* NetRecive */ ++ net_process_received_packet(net_rx_packets[0], len); ++ ++ return 0; ++} ++ ++static int gmac_wait_tx_rq_wr(struct gmac_netdev_local const *ld) ++{ ++ u32 tx_rq_wr_offset; ++ u32 tx_rq_rd_offset; ++ int timeout_us = 1000; ++ ++ tx_rq_wr_offset = gmac_readl_bits(ld, ++ TX_RQ_WR_ADDR, BITS_TX_RQ_WR_ADDR); ++ tx_rq_rd_offset = gmac_readl_bits(ld, ++ TX_RQ_RD_ADDR, BITS_TX_RQ_RD_ADDR); ++ ++ tx_rq_rd_offset += DESC_SIZE; ++ if (tx_rq_rd_offset >= (GMAC_HWQ_TX_RQ_DEPTH << DESC_BYTE_SHIFT)) ++ tx_rq_rd_offset = 0; ++ ++ while (--timeout_us && (tx_rq_rd_offset != tx_rq_wr_offset)) { ++ udelay(1); ++ tx_rq_wr_offset = gmac_readl_bits(ld, ++ TX_RQ_WR_ADDR, BITS_TX_RQ_WR_ADDR); ++ } ++ ++ if (!timeout_us) ++ return -1; ++ ++ gmac_writel_bits(ld, tx_rq_rd_offset, TX_RQ_RD_ADDR, BITS_TX_RQ_RD_ADDR); ++ if (g_gmac_debug) ++ printf("send packet!\n"); ++ ++ return 0; ++} ++ ++static int gmac_send(struct eth_device *dev, void *packet, int length) ++{ ++ struct gmac_netdev_local *ld = (struct gmac_netdev_local *)dev->priv; ++ u32 tx_bq_wr_offset, tx_bq_rd_offset; ++ unsigned int tso_ver; ++ gmac_desc *tx_bq_desc = ld->tx_bq_addr; ++ phys_addr_t addr; ++ ++ tx_bq_wr_offset = gmac_readl_bits(ld, TX_BQ_WR_ADDR, BITS_TX_BQ_WR_ADDR); ++ tx_bq_rd_offset = gmac_readl_bits(ld, TX_BQ_RD_ADDR, BITS_TX_BQ_RD_ADDR); ++ if (tx_bq_rd_offset != tx_bq_wr_offset) { ++ gmac_writel_bits(ld, tx_bq_rd_offset, ++ TX_BQ_WR_ADDR, BITS_TX_BQ_WR_ADDR); ++ return -1; ++ } ++ ++ tso_ver = gmac_readl_bits(ld, CRF_MIN_PACKET, BIT_TSO_VERSION); ++ ++ tx_bq_desc += (tx_bq_wr_offset >> DESC_BYTE_SHIFT); ++ addr = (phys_addr_t)(uintptr_t)packet; ++ tx_bq_desc->data_buff_addr = (u32)addr; ++#if defined(CONFIG_64BIT) ++ tx_bq_desc->reserve3 = ((u64)addr >> REG_BIT_WIDTH) & TX_DESC_HI8_MASK; ++#endif ++ tx_bq_desc->descvid = DESC_VLD_BUSY; ++ ++ tx_bq_desc->fl = tso_ver ? 0 : DESC_FL_FULL; ++ ++ tx_bq_desc->reserve1 = 0; ++ tx_bq_desc->data_len = length; ++ tx_bq_desc->buffer_len = ETH_MAX_FRAME_SIZE - 1; ++#ifndef CONFIG_SYS_DCACHE_OFF ++ flush_cache((uintptr_t)packet, ALIGN((u32)length, ARCH_DMA_MINALIGN)); ++ flush_cache((uintptr_t)tx_bq_desc & ~(ARCH_DMA_MINALIGN - 1), ++ ALIGN(sizeof(*tx_bq_desc), ARCH_DMA_MINALIGN)); ++#endif ++ ++ tx_bq_wr_offset += DESC_SIZE; ++ if (tx_bq_wr_offset >= (GMAC_HWQ_TX_BQ_DEPTH << DESC_BYTE_SHIFT)) ++ tx_bq_wr_offset = 0; ++ ++ gmac_writel_bits(ld, tx_bq_wr_offset, ++ TX_BQ_WR_ADDR, BITS_TX_BQ_WR_ADDR); ++ ++ return gmac_wait_tx_rq_wr(ld); ++} ++ ++#ifdef CFG_NET_PINCTRL ++static void gmac_config_rgmii0(void) ++{ ++ writel(VALUE_MDCK0, PHY_ADDR_MDCK0); ++ writel(VALUE_MDIO0, PHY_ADDR_MDIO0); ++ writel(VALUE_EPHY0_CLK, PHY_ADDR_EPHY0_CLK); ++ writel(VALUE_EPHY0_RSTN, PHY_ADDR_EPHY0_RSTN); ++ writel(VALUE_RGMII0_TXCKOUT, PHY_ADDR_RGMII0_TXCKOUT); ++ writel(VALUE_RGMII0_TXD0, PHY_ADDR_RGMII0_TXD0); ++ writel(VALUE_RGMII0_TXD1, PHY_ADDR_RGMII0_TXD1); ++ writel(VALUE_RGMII0_TXD2, PHY_ADDR_RGMII0_TXD2); ++ writel(VALUE_RGMII0_TXD3, PHY_ADDR_RGMII0_TXD3); ++ writel(VALUE_RGMII0_TXEN, PHY_ADDR_RGMII0_TXEN); ++ writel(VALUE_RGMII0_RXCK, PHY_ADDR_RGMII0_RXCK); ++ writel(VALUE_RGMII0_RXD0, PHY_ADDR_RGMII0_RXD0); ++ writel(VALUE_RGMII0_RXD1, PHY_ADDR_RGMII0_RXD1); ++ writel(VALUE_RGMII0_RXD2, PHY_ADDR_RGMII0_RXD2); ++ writel(VALUE_RGMII0_RXD3, PHY_ADDR_RGMII0_RXD3); ++ writel(VALUE_RGMII0_RXDV, PHY_ADDR_RGMII0_RXDV); ++} ++ ++static void gmac_config_rmii0(void) ++{ ++ writel(VALUE_MDCK0, PHY_ADDR_MDCK0); ++ writel(VALUE_MDIO0, PHY_ADDR_MDIO0); ++ writel(VALUE_EPHY0_CLK, PHY_ADDR_EPHY0_CLK); ++ writel(VALUE_EPHY0_RSTN, PHY_ADDR_EPHY0_RSTN); ++ writel(VALUE_RMII0_CLK, PHY_ADDR_RMII0_CLK); ++ writel(VALUE_RMII0_TXD0, PHY_ADDR_RMII0_TXD0); ++ writel(VALUE_RMII0_TXD1, PHY_ADDR_RMII0_TXD1); ++ writel(VALUE_RMII0_TXEN, PHY_ADDR_RMII0_TXEN); ++ writel(VALUE_RMII0_RXD0, PHY_ADDR_RMII0_RXD0); ++ writel(VALUE_RMII0_RXD1, PHY_ADDR_RMII0_RXD1); ++ writel(VALUE_RMII0_RXDV, PHY_ADDR_RMII0_RXDV); ++} ++ ++#if GMAC_AT_LEAST_2PORT ++static void gmac_config_rgmii1(void) ++{ ++ writel(VALUE_MDCK1, PHY_ADDR_MDCK1); ++ writel(VALUE_MDIO1, PHY_ADDR_MDIO1); ++ writel(VALUE_EPHY1_CLK, PHY_ADDR_EPHY1_CLK); ++ writel(VALUE_EPHY1_RSTN, PHY_ADDR_EPHY1_RSTN); ++ writel(VALUE_RGMII1_TXCKOUT, PHY_ADDR_RGMII1_TXCKOUT); ++ writel(VALUE_RGMII1_TXD0, PHY_ADDR_RGMII1_TXD0); ++ writel(VALUE_RGMII1_TXD1, PHY_ADDR_RGMII1_TXD1); ++ writel(VALUE_RGMII1_TXD2, PHY_ADDR_RGMII1_TXD2); ++ writel(VALUE_RGMII1_TXD3, PHY_ADDR_RGMII1_TXD3); ++ writel(VALUE_RGMII1_TXEN, PHY_ADDR_RGMII1_TXEN); ++ writel(VALUE_RGMII1_RXCK, PHY_ADDR_RGMII1_RXCK); ++ writel(VALUE_RGMII1_RXD0, PHY_ADDR_RGMII1_RXD0); ++ writel(VALUE_RGMII1_RXD1, PHY_ADDR_RGMII1_RXD1); ++ writel(VALUE_RGMII1_RXD2, PHY_ADDR_RGMII1_RXD2); ++ writel(VALUE_RGMII1_RXD3, PHY_ADDR_RGMII1_RXD3); ++ writel(VALUE_RGMII1_RXDV, PHY_ADDR_RGMII1_RXDV); ++} ++ ++static void gmac_config_rmii1(void) ++{ ++ writel(VALUE_MDCK1, PHY_ADDR_MDCK1); ++ writel(VALUE_MDIO1, PHY_ADDR_MDIO1); ++ writel(VALUE_EPHY1_CLK, PHY_ADDR_EPHY1_CLK); ++ writel(VALUE_EPHY1_RSTN, PHY_ADDR_EPHY1_RSTN); ++ writel(VALUE_RMII1_CLK, PHY_ADDR_RMII1_CLK); ++ writel(VALUE_RMII1_TXD0, PHY_ADDR_RMII1_TXD0); ++ writel(VALUE_RMII1_TXD1, PHY_ADDR_RMII1_TXD1); ++ writel(VALUE_RMII1_TXEN, PHY_ADDR_RMII1_TXEN); ++ writel(VALUE_RMII1_RXD0, PHY_ADDR_RMII1_RXD0); ++ writel(VALUE_RMII1_RXD1, PHY_ADDR_RMII1_RXD1); ++ writel(VALUE_RMII1_RXDV, PHY_ADDR_RMII1_RXDV); ++} ++#endif ++ ++static void gmac_pinctrl_config(void) ++{ ++ if (g_gmac_board_info[0].phy_intf == INTERFACE_MODE_RMII) // 0:mac0 ++ gmac_config_rmii0(); ++ else if (g_gmac_board_info[0].phy_intf == INTERFACE_MODE_RGMII) // 0:mac0 ++ gmac_config_rgmii0(); ++ ++#if GMAC_AT_LEAST_2PORT ++ if (g_gmac_board_info[1].phy_intf == INTERFACE_MODE_RMII) // 1:mac1 ++ gmac_config_rmii1(); ++ else if (g_gmac_board_info[1].phy_intf == INTERFACE_MODE_RGMII) // 1:mac1 ++ gmac_config_rgmii1(); ++#endif ++} ++#endif ++static int g_gmac_hw_inited = 0; ++static void gmac_hw_init(void) ++{ ++ /* init once to save time */ ++ if (!g_gmac_hw_inited) { ++ gmac_sys_init(); ++ ++ g_gmac_hw_inited = 1; ++ } ++} ++ ++static int gmac_init(struct eth_device *dev, bd_t *bd) ++{ ++ struct gmac_netdev_local *ld = (struct gmac_netdev_local *)dev->priv; ++ char *mii_devname = g_gmac_board_info[ld->index].mii_name; ++ int phy_addr = g_gmac_board_info[ld->index].phy_addr; ++ enum if_mode phy_intf = g_gmac_board_info[ld->index].phy_intf; ++ u32 phy_id = 0; ++ int ret; ++ ++ gmac_hw_init(); ++ ++ /* init once to save time */ ++ if (!ld->initalized) { ++ if (!phy_detected(mii_devname, phy_addr, &phy_id)) ++ return -1; ++ ++ miiphy_reset(mii_devname, phy_addr); ++ ++ if (phy_intf != INTERFACE_MODE_RGMII) { ++ unsigned short val = 0; ++ if (!miiphy_read(mii_devname, phy_addr, MII_CTRL1000, &val)) { ++ val &= ~(PHY_1000BTCR_1000FD | PHY_1000BTCR_1000HD); ++ miiphy_write(mii_devname, phy_addr, MII_CTRL1000, val); ++ } ++ } ++ ++ gmac_glb_preinit_dummy(ld); ++ ++ ret = gmac_set_hwq_depth(ld); ++ if (ret) { ++ printf("init eth%d hw desc queue depth fail!\n", ld->index); ++ return ret; ++ } ++ ++ ret = gmac_init_hw_desc_queue(ld); ++ if (ret) { ++ printf("init eth%d hw desc queue fail!\n", ld->index); ++ return ret; ++ } ++ ld->initalized = 1; ++ } ++ ++ ret = select_current_linked_phy(ld); ++ if (ret) ++ return ret; ++ ++ gmac_net_set_mac_address(dev); ++ gmac_desc_enable(ld, RX_OUTCFF_WR_DESC_ENA | RX_CFF_RD_DESC_ENA); ++ gmac_writel_bits(ld, 1, PORT_EN, BITS_RX_EN); ++ ++ gmac_writel_bits(ld, 1, PORT_EN, BITS_TX_EN); ++ gmac_desc_enable(ld, TX_OUTCFF_WR_DESC_ENA | TX_CFF_RD_DESC_ENA); ++ ++ return 0; ++} ++ ++static void gmac_halt(struct eth_device *dev) ++{ ++ struct gmac_netdev_local *ld = (struct gmac_netdev_local *)dev->priv; ++ ++ gmac_writel_bits(ld, 0, PORT_EN, BITS_RX_EN); ++ gmac_desc_disable(ld, RX_OUTCFF_WR_DESC_ENA | RX_CFF_RD_DESC_ENA); ++ ++ gmac_desc_disable(ld, TX_OUTCFF_WR_DESC_ENA | TX_CFF_RD_DESC_ENA); ++ gmac_writel_bits(ld, 0, PORT_EN, BITS_TX_EN); ++} ++ ++static int gmac_register_dev(port_id_t port_id) ++{ ++ struct eth_device *dev; ++ ++ dev = malloc(sizeof(*dev)); ++ if (dev == NULL) ++ return -1; ++ if (memset_s(dev, sizeof(*dev), 0, sizeof(*dev)) < 0) ++ printf("memset_s failed!\n"); ++ ++ dev->iobase = mac_iobase[port_id]; ++ dev->init = gmac_init; ++ dev->halt = gmac_halt; ++ dev->send = gmac_send; ++ dev->recv = gmac_recv; ++ dev->priv = &g_gmac_board_info[port_id].gmac_ld; ++ if (snprintf_s(dev->name, sizeof(dev->name), sizeof(dev->name) - 1, "gmac%d", port_id) < 0) ++ printf("snprintf_s failed!func:%s, line: %d\n", __func__, __LINE__); ++ ++#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) ++ struct mii_dev *bus = mdio_alloc(); ++ ++ if (!bus) { ++ free(dev); ++ debug("Failed to allocate MDIO bus\n"); ++ return -ENOMEM; ++ } ++ ++ bus->read = gmac_mdiobus_read; ++ bus->write = gmac_mdiobus_write; ++ if (snprintf_s(bus->name, sizeof(bus->name), sizeof(bus->name), mdio_bus[port_id]) < 0) ++ printf("snprintf_s failed!func:%s, line: %d\n", __func__, __LINE__); ++ if (mdio_register(bus)) { ++ mdio_free(bus); ++ free(dev); ++ return -1; ++ } ++#endif ++ ++ eth_register(dev); ++ return 0; ++} ++ ++int gmac_initialize(bd_t const *bis) ++{ ++ int ret; ++ ++ parse_module_parameters(); ++ ++#ifdef CFG_NET_PINCTRL ++ /* Configure gmac pinctrl parameters in software */ ++ gmac_pinctrl_config(); ++#endif ++ ++ ret = gmac_register_dev(GSF0_PORT0); ++ if (ret) ++ return ret; ++ ++#if GMAC_AT_LEAST_2PORT ++ ret = gmac_register_dev(GSF0_PORT1); ++ if (ret) ++ return ret; ++#endif ++ ++#if GMAC_AT_LEAST_3PORT ++ ret = gmac_register_dev(GSF1_PORT0); ++ if (ret) ++ return ret; ++#endif ++ ++ return 0; ++} +diff --git a/drivers/net/gmacv300/gmac.h b/drivers/net/gmacv300/gmac.h +new file mode 100644 +index 0000000..864a64f +--- /dev/null ++++ b/drivers/net/gmacv300/gmac.h +@@ -0,0 +1,177 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __GMAC_H__ ++#define __GMAC_H__ ++ ++#include ++#include ++#include ++#include ++#include /* malloc, free, realloc */ ++#include ++#include ++ ++#define bit(nr) (1UL << (nr)) ++ ++#define GMAC_AT_LEAST_2PORT (CONFIG_GMAC_NUMS >= 2) ++#define GMAC_AT_LEAST_3PORT (CONFIG_GMAC_NUMS >= 3) ++#define ENV_BUF_LEN 32 ++ ++/* board parameters */ ++#define DEFAULT_PHY_LINK_TIMES 20000 ++ ++enum if_mode { ++ INTERFACE_MODE_MII, ++ INTERFACE_MODE_RMII, ++ INTERFACE_MODE_RGMII, ++ INTERFACE_MODE_BUTT ++}; ++ ++enum speed_mode { ++ SPEED_MODE_10M, ++ SPEED_MODE_100M, ++ SPEED_MODE_1000M, ++ SPEED_MODE_BUTT ++}; ++ ++enum { /* DEFAULT: DUPLEX_FULL */ ++ PORT_MODE_10_MII = 0x1D, ++ PORT_MODE_100_MII = 0x1F, ++ PORT_MODE_10_RGMII = 0x3D, ++ PORT_MODE_100_RGMII = 0x3F, ++ PORT_MODE_1000_RGMII = 0x3C, ++ PORT_MODE_10_RMII = 0x9D, ++ PORT_MODE_100_RMII = 0x9F, ++ PORT_MODE_BUTT ++}; ++ ++#define INVALID_PHY_ADDR 0xFF ++#define INVALID_LINK_STATUS 0 ++ ++#define MAC_LEN 6 ++#define MAX_PHY_NAME_LEN 15 ++#define MAX_ETH_ADDR 20 ++ ++#define MIN_PKG_LEN 42 ++#define MAX_PKG_LEN 1600 ++#define gmac_invalid_rxpkg_len(len) \ ++ (!(((len) >= MIN_PKG_LEN) && ((len) <= MAX_PKG_LEN))) ++ ++#define PORT_MOD_10M_MII 0 ++#define PORT_MOD_100M_MII 1 ++#define PORT_MOD_1000M_GMII 2 ++#define PORT_MOD_10M_RGMII 3 ++#define PORT_MOD_100M_RGMII 4 ++#define PORT_MOD_1000M_RGMII 5 ++ ++#define GMAC_LINKED bit(0) ++#define GMAC_DUP_FULL bit(1) ++#define GMAC_SPD_10M bit(2) ++#define GMAC_SPD_100M bit(3) ++#define GMAC_SPD_1000M bit(4) ++ ++#define RX_OUTCFF_WR_DESC_ENA bit(3) ++#define RX_CFF_RD_DESC_ENA bit(2) ++#define TX_OUTCFF_WR_DESC_ENA bit(1) ++#define TX_CFF_RD_DESC_ENA bit(0) ++ ++#define GMAC_MAX_QUEUE_DEPTH (SZ_1K*2) ++ ++#define GMAC_HWQ_RX_FQ_DEPTH 64 ++#define GMAC_HWQ_RX_BQ_DEPTH 64 ++#define GMAC_HWQ_TX_BQ_DEPTH 2 ++#define GMAC_HWQ_TX_RQ_DEPTH 2 ++ ++#define GMAC_MONITOR_TIMER (msecs_to_jiffies(10)) ++ ++#define MAX_RX_POOLS (SZ_1K) ++#define ETH_MAX_FRAME_SIZE (SZ_1K*2) ++/* mac rx buffer size should be no less than 1600, ++ * reserve more 64 bytes for the max frame length 1518. ++ */ ++#define ETH_BUFFER_SIZE 1600 ++#define SKB_SIZE (ETH_MAX_FRAME_SIZE) ++ ++#if defined(CONFIG_64BIT) ++#define REG_BIT_WIDTH 32 ++#define Q_ADDR_HI8_OFFSET 24 ++#define Q_ADDR_HI8_MASK (bit(Q_ADDR_HI8_OFFSET) - 1) ++#define TX_DESC_HI8_MASK 0xff ++#define SG_DESC_HI8_OFFSET 8 ++#endif ++ ++#define DESC_VLD_FREE 0 ++#define DESC_VLD_BUSY 1 ++#define DESC_FL_FIRST 2 ++#define DESC_FL_MID 0 ++#define DESC_FL_LAST 1 ++#define DESC_FL_FULL 3 ++ ++#ifdef CONFIG_GMAC_DESC_4_WORD ++#define DESC_WORD_SHIFT 2 ++#else ++#define DESC_WORD_SHIFT 3 ++#endif ++#define DESC_BYTE_SHIFT (DESC_WORD_SHIFT + 2) ++#define DESC_WORD_CNT (1 << DESC_WORD_SHIFT) ++#define DESC_SIZE (1 << DESC_BYTE_SHIFT) ++ ++typedef struct gmac_descriptor { ++ unsigned int data_buff_addr; ++ ++ unsigned int buffer_len : 11; ++ unsigned int reserve2 : 5; ++ unsigned int data_len : 11; ++ unsigned int reserve1 : 2; ++ unsigned int fl : 2; ++ unsigned int descvid : 1; ++ unsigned int reserve3; ++ unsigned int reserve4; ++#ifndef CONFIG_GMAC_DESC_4_WORD ++ unsigned int reserve5; ++ unsigned int reserve6; ++ unsigned int reserve7; ++ unsigned int reserve8; ++#endif ++} gmac_desc; ++ ++struct gmac_netdev_local { ++ unsigned long iobase; /* virtual io addr */ ++ unsigned long iobase_phys; /* physical io addr */ ++ ++ gmac_desc *rx_fq_addr; ++ gmac_desc *rx_bq_addr; ++ gmac_desc *tx_bq_addr; ++ gmac_desc *tx_rq_addr; ++ ++ int link_stat; ++ int index; ++ int initalized; ++}; ++ ++struct gmac_board_info { ++ struct gmac_netdev_local gmac_ld; ++ enum if_mode phy_intf; ++ char *mii_name; ++ int phy_addr; ++}; ++ ++extern struct gmac_board_info g_gmac_board_info[]; ++#endif +diff --git a/drivers/net/gmacv300/mdio.c b/drivers/net/gmacv300/mdio.c +new file mode 100644 +index 0000000..5979166 +--- /dev/null ++++ b/drivers/net/gmacv300/mdio.c +@@ -0,0 +1,132 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "gmac.h" ++#include "ctrl.h" ++#include "mdio.h" ++#include "util.h" ++ ++static void mdio_set_phywrite_val(struct gmac_netdev_local const *ld, unsigned short val) ++{ ++ u32 reg; ++ ++ gmac_writel_bits(ld, val, REG_MDIO_SINGLE_DATA, MDIO_WR_DATA); ++ reg = gmac_readl(ld, REG_MDIO_SINGLE_DATA); ++ gmac_trace(GMAC_TRACE_MDIO, "write reg 0x%x, bits:0x%x= 0x%x, then read = 0x%x", ++ REG_MDIO_SINGLE_DATA, MDIO_WR_DATA, val, reg); ++} ++ ++static int mdio_wait_ready(struct gmac_netdev_local const *ld) ++{ ++ int timeout_us = 1000; ++ ++ while (--timeout_us && !test_mdio_ready(ld)) ++ udelay(1); ++ ++ return timeout_us; ++} ++ ++static int gmac_mdio_read(struct gmac_netdev_local const *ld, ++ char const *devname, unsigned char phy, ++ unsigned char reg, unsigned short *value) ++{ ++ int timeout = 1000; ++ ++ if (!mdio_wait_ready(ld)) ++ return -1; ++ ++ mdio_start_phyread(ld, phy, reg); ++ ++ while (!mdio_wait_ready(ld) && timeout-- > 0) ++ udelay(1); ++ ++ if (timeout <= 0 || !test_mdio_read_data_done(ld)) { ++ *value = 0; ++ /* it should return Error(-1), but miiphy_read() will ++ * print error info, it's annoying ++ */ ++ return 0; ++ } ++ ++ *value = mdio_get_phyread_val(ld); ++ ++ gmac_trace(GMAC_TRACE_MDIO, "mdio read phy:%x, reg:%x = %x\n", ++ phy, reg, *value); ++ ++ return 0; ++} ++ ++static int gmac_mdio_write(struct gmac_netdev_local *ld, ++ char const *devname, unsigned char phy, ++ unsigned char reg, unsigned short val) ++{ ++ if (!mdio_wait_ready(ld)) ++ return -1; ++ ++ gmac_trace(GMAC_TRACE_MDIO, "mdio write phy:%x, reg:%x = %x\n", ++ phy, reg, val); ++ ++ mdio_set_phywrite_val(ld, val); ++ mdio_phywrite(ld, phy, reg); ++ ++ return 0; ++} ++ ++static struct gmac_netdev_local *gmac_get_netdev_by_name(const char *devname) ++{ ++ int i; ++ ++ for (i = 0; i < CONFIG_GMAC_NUMS; i++) { ++ if (!strcmp(devname, g_gmac_board_info[i].mii_name)) ++ return &g_gmac_board_info[i].gmac_ld; ++ } ++ ++ return NULL; ++} ++ ++int gmac_mdiobus_read(struct mii_dev* const bus, int addr, int devad, int reg) ++{ ++ struct gmac_netdev_local *ld = NULL; ++ unsigned short value; ++ int ret; ++ ++ if (bus == NULL) ++ return -EINVAL; ++ ld = gmac_get_netdev_by_name(bus->name); ++ if (ld == NULL) ++ return -EINVAL; ++ ret = gmac_mdio_read(ld, bus->name, addr, reg, &value); ++ ++ return ret ? -1 : (int)value; ++} ++ ++int gmac_mdiobus_write(struct mii_dev* const bus, int addr, int devad, ++ int reg, unsigned short val) ++{ ++ struct gmac_netdev_local *ld = NULL; ++ if (bus == NULL) ++ return -EINVAL; ++ ld = gmac_get_netdev_by_name(bus->name); ++ if (ld == NULL) ++ return -EINVAL; ++ ++ return gmac_mdio_write(ld, bus->name, addr, reg, val); ++} +diff --git a/drivers/net/gmacv300/mdio.h b/drivers/net/gmacv300/mdio.h +new file mode 100644 +index 0000000..9b153c0 +--- /dev/null ++++ b/drivers/net/gmacv300/mdio.h +@@ -0,0 +1,61 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __GMAC_MDIO_H__ ++#define __GMAC_MDIO_H__ ++ ++#include "util.h" ++ ++#define REG_MDIO_SINGLE_CMD 0x000003C0 ++#define REG_MDIO_SINGLE_DATA 0x000003C4 ++#define REG_MDIO_RDATA_STATUS 0x000003D0 ++ ++/* 0:mdio operation done,1: start mdio operation */ ++#define MDIO_CMD mk_bits(20, 1) ++ ++#define MDIO_WR_DATA mk_bits(0, 16) ++#define MDIO_RDATA_STATUS mk_bits(0, 1) ++ ++#define MDIO_CMD_READ 2 ++#define MDIO_CMD_WRITE 1 ++ ++#define mdio_mk_rwctl(rw, phy_exaddr, phy_regnum) \ ++ (((0x1)<<20) | (((rw)&0x3)<<16) | \ ++ (((phy_exaddr)&0x1f)<<8) | ((phy_regnum)&0x1f)) ++ ++#define mdio_start_phyread(ld, phy_addr, regnum) \ ++ gmac_writel(ld, mdio_mk_rwctl(MDIO_CMD_READ, phy_addr, regnum), \ ++ REG_MDIO_SINGLE_CMD) ++ ++#define mdio_get_phyread_val(ld) (gmac_readl(ld, REG_MDIO_SINGLE_DATA) >> 16) ++ ++#define mdio_phywrite(ld, phy_addr, regnum) \ ++ gmac_writel(ld, mdio_mk_rwctl(MDIO_CMD_WRITE, phy_addr, regnum), \ ++ REG_MDIO_SINGLE_CMD) ++ ++#define test_mdio_ready(ld) (gmac_readl_bits((ld), \ ++ REG_MDIO_SINGLE_CMD, MDIO_CMD) == 0) ++ ++#define test_mdio_read_data_done(ld) (gmac_readl_bits(ld, \ ++ REG_MDIO_RDATA_STATUS, MDIO_RDATA_STATUS) == 0) ++ ++int gmac_mdiobus_read(struct mii_dev *bus, int addr, int devad, int reg); ++int gmac_mdiobus_write(struct mii_dev *bus, int addr, int devad, ++ int reg, unsigned short val); ++#endif +diff --git a/drivers/net/gmacv300/util.h b/drivers/net/gmacv300/util.h +new file mode 100644 +index 0000000..b860ebc +--- /dev/null ++++ b/drivers/net/gmacv300/util.h +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __GMAC_UTIL_H__ ++#define __GMAC_UTIL_H__ ++ ++#include ++ ++#define GMAC_TRACE_ETH 2 ++#define GMAC_TRACE_MDIO 4 ++#define GMAC_TRACE_DRV 7 ++#define GMAC_TRACE_LEVEL 8 ++ ++#define mk_bits(shift, nbits) ((((shift) & 0x1F) << 16) | ((nbits) & 0x3F)) ++ ++#endif ++ +diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig +index 13603b9..879a9b4 100644 +--- a/drivers/pci/Kconfig ++++ b/drivers/pci/Kconfig +@@ -173,4 +173,11 @@ config PCIE_MEDIATEK + Say Y here if you want to enable Gen2 PCIe controller, + which could be found on MT7623 SoC family. + ++config PCIE_BSP ++ bool "BSP PCIE support" ++ depends on PCI_PNP ++ default y ++ help ++ Enable support for PCI express ++ + endif +diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile +index da8b826..756005f 100644 +--- a/drivers/pci/Makefile ++++ b/drivers/pci/Makefile +@@ -42,3 +42,4 @@ obj-$(CONFIG_PCI_PHYTIUM) += pcie_phytium.o + obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o + obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o + obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o ++obj-$(CONFIG_PCIE_BSP) += pcie_bsp.o +\ No newline at end of file +diff --git a/drivers/pci/pci_auto_old.c b/drivers/pci/pci_auto_old.c +index b566705..0414f17 100644 +--- a/drivers/pci/pci_auto_old.c ++++ b/drivers/pci/pci_auto_old.c +@@ -51,7 +51,12 @@ void pciauto_setup_device(struct pci_controller *hose, + bar < PCI_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) { + /* Tickle the BAR and get the response */ + #ifndef CONFIG_PCI_ENUM_ONLY ++#ifdef CONFIG_PCIE_BSP ++ pci_hose_write_config_dword(hose, dev, bar, 0xfffffff0); ++ ++#else + pci_hose_write_config_dword(hose, dev, bar, 0xffffffff); ++#endif + #endif + pci_hose_read_config_dword(hose, dev, bar, &bar_response); + +diff --git a/drivers/pci/pci_common.c b/drivers/pci/pci_common.c +index 5231b69..c84db27 100644 +--- a/drivers/pci/pci_common.c ++++ b/drivers/pci/pci_common.c +@@ -89,8 +89,11 @@ __weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) + /* + * Only skip configuration if "pciconfighost" is not set + */ ++#ifndef CONFIG_PCIE_BSP + if (env_get("pciconfighost") == NULL) + return 1; ++#endif ++ return 0; + #else + return 1; + #endif +diff --git a/drivers/pci/pcie_bsp.c b/drivers/pci/pcie_bsp.c +new file mode 100644 +index 0000000..dded759 +--- /dev/null ++++ b/drivers/pci/pcie_bsp.c +@@ -0,0 +1,317 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PERI_CRG_BASE 0x12010000 ++#define CONF_BASE_ADDR 0x12200000 ++#define PCIE_EP_CONF_BASE 0x20000000 ++#define PCI_MEM_ADDR 0x30000000 ++ ++#define __128MB__ 0x8000000 ++ ++#define PCI_MEM_SIZE 0x8000000 ++ ++#define PCIE_SYS_STATE0 0xf00 ++#define PCIE_XMLH_LINK_UP 15 ++#define PCIE_RDLH_LINK_UP 5 ++ ++#define pcie_cfg_reg(reg) ((reg) & 0xffc) /* set dword align */ ++ ++#define PCIE_SYS_CTRL7 0xc1c ++#define PCIE_APP_LTSSM_ENBALE 11 ++ ++#define PERI_CRG44 0x18c ++#define PCIE_X2_SRST_REQ 2 ++ ++#define PCI_CARD 0x44 ++ ++#define PCIE_SYS_CTRL0 0xc00 ++#define PCIE_DEVICE_TYPE 28 ++#define PCIE_WM_RC 0x4 ++ ++#define PCIE_X2_AUX_CKEN 7 ++#define PCIE_X2_PIPE_CKEN 6 ++#define PCIE_X2_SYS_CKEN 5 ++#define PCIE_X2_BUS_CKEN 4 ++ ++#define PCI_CLASS_REVISION 0x08 ++#define PCI_COMMAND 0x04 ++ ++static int pcie_link_up(void) ++{ ++ unsigned int val; ++ ++ val = readl(CONF_BASE_ADDR + PCIE_SYS_STATE0); ++ return ((val & (1 << PCIE_XMLH_LINK_UP)) && ++ (val & (1 << PCIE_RDLH_LINK_UP))) ? 1 : 0; ++} ++ ++static int is_pcie_link_up(void) ++{ ++ int i; ++ for (i = 0; i < 5000000; i++) { /* 5000000: Cycle */ ++ if (pcie_link_up()) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int pcie_read_from_device(const struct pci_controller *hose, unsigned int d, ++ int where, u32 * const val) ++{ ++ unsigned long addr; ++ ++ addr = PCIE_EP_CONF_BASE + (d | pcie_cfg_reg(where)); ++ *val = readl(addr); ++ ++ return 0; ++} ++ ++static int pcie_read_from_dbi(const struct pci_controller *hose, unsigned int d, ++ int where, u32 * const val) ++{ ++ *val = (uintptr_t)readl(CONF_BASE_ADDR + (where & ~0x3)); ++ ++ return 0; ++} ++ ++static int bsp_pcie_read_config(const struct pci_controller *hose, pci_dev_t d, ++ int where, u32 * const val) ++{ ++ int ret; ++ ++ if (PCI_BUS(d) == 0) ++ ret = pcie_read_from_dbi(hose, d, where, val); ++ else ++ ret = pcie_read_from_device(hose, d, where, val); ++ ++ return ret; ++} ++ ++static int pcie_write_to_dbi(const struct pci_controller *hose, pci_dev_t d, ++ int where, u32 val) ++{ ++ if (PCI_DEV(d) == 0) ++ writel(val, (CONF_BASE_ADDR + (where & (~3)))); /* 3:Reflexive */ ++ ++ return 0; ++} ++ ++static int pcie_write_to_device(const struct pci_controller *hose, pci_dev_t d, ++ int where, u32 val) ++{ ++ unsigned long addr; ++ ++ if (PCI_DEV(d) == 0) { ++ addr = PCIE_EP_CONF_BASE + (d | pcie_cfg_reg(where)); ++ writel(val, addr); ++ } ++ ++ return 0; ++} ++ ++static int bsp_pcie_write_config(const struct pci_controller *hose, pci_dev_t d, ++ int where, u32 val) ++{ ++ if (PCI_BUS(d) == 0) ++ return pcie_write_to_dbi(hose, d, where, val); ++ else ++ return pcie_write_to_device(hose, d, where, val); ++} ++ ++static int __arch_pcie_sys_init(void) ++{ ++ unsigned int val; ++ ++ /* ++ * * Disable PCIE ++ * */ ++ val = readl(CONF_BASE_ADDR + PCIE_SYS_CTRL7); ++ val &= (~(1 << PCIE_APP_LTSSM_ENBALE)); ++ writel(val, CONF_BASE_ADDR + PCIE_SYS_CTRL7); ++ ++ /* ++ * * Reset ++ * */ ++ val = readl(PERI_CRG_BASE + PERI_CRG44); ++ val |= (1 << PCIE_X2_SRST_REQ); ++ writel(val, PERI_CRG_BASE + PERI_CRG44); ++ ++ /* ++ * * Retreat from the reset state ++ * */ ++ udelay(500); /* delay 500us */ ++ val = readl(PERI_CRG_BASE + PERI_CRG44); ++ val &= ~(1 << PCIE_X2_SRST_REQ); ++ writel(val, PERI_CRG_BASE + PERI_CRG44); ++ mdelay(10); /* delay 10ms */ ++ ++ /* ++ * * PCIE RC work mode ++ * */ ++ val = readl(CONF_BASE_ADDR + PCIE_SYS_CTRL0); ++ val &= (~(0xf << PCIE_DEVICE_TYPE)); ++ val |= (PCIE_WM_RC << PCIE_DEVICE_TYPE); ++ writel(val, CONF_BASE_ADDR + PCIE_SYS_CTRL0); ++ ++ /* ++ * * Enable clk ++ * */ ++ val = readl(PERI_CRG_BASE + PERI_CRG44); ++ val |= ((1 << PCIE_X2_BUS_CKEN) | ++ (1 << PCIE_X2_SYS_CKEN) | ++ (1 << PCIE_X2_PIPE_CKEN) | ++ (1 << PCIE_X2_AUX_CKEN)); ++ writel(val, PERI_CRG_BASE + PERI_CRG44); ++ ++ mdelay(10); /* delay 10ms */ ++ ++ /* ++ * * Set PCIE Support Transfer Card ++ * */ ++ val = readl(PERI_CRG_BASE + PCI_CARD); ++ val |= (1 << 3); /* Move Right 3bit */ ++ writel(val, PERI_CRG_BASE + PCI_CARD); ++ mdelay; ++ ++ /* ++ * * Set PCIE controller class code to be PCI-PCI bridge device ++ * */ ++ val = readl(CONF_BASE_ADDR + PCI_CLASS_REVISION); ++ val &= ~(0xffffff00); ++ val |= (0x60400 << 8); /* Move Right 8bit */ ++ writel(val, CONF_BASE_ADDR + PCI_CLASS_REVISION); ++ udelay(1000); /* delay 1000us */ ++ ++ /* ++ * * Enable controller ++ * */ ++ val = readl(CONF_BASE_ADDR + PCIE_SYS_CTRL7); ++ val |= (1 << PCIE_APP_LTSSM_ENBALE); ++ writel(val, CONF_BASE_ADDR + PCIE_SYS_CTRL7); ++ udelay(1000); /* delay 1000us */ ++ ++ val = readl(CONF_BASE_ADDR + PCI_COMMAND); ++ val |= 7; /* 7: 00000111 */ ++ writel(val, CONF_BASE_ADDR + PCI_COMMAND); ++ ++ return 0; ++} ++ ++struct pcie_iatu { ++ unsigned int viewport; /* iATU Viewport Register */ ++ unsigned int region_ctrl_1; /* Region Control 1 Register */ ++ unsigned int region_ctrl_2; /* Region Control 2 Register */ ++ unsigned int lbar; /* Lower Base Address Register */ ++ unsigned int ubar; /* Upper Base Address Register */ ++ unsigned int lar; /* Limit Address Register */ ++ unsigned int ltar; /* Lower Target Address Register */ ++ unsigned int utar; /* Upper Target Address Register */ ++}; ++ ++struct pcie_iatu iatu_table[] = { ++ { ++ .viewport = 0, ++ .region_ctrl_1 = 0x00000004, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (1 << 20), /* Move Right 20bit */ ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (2 << 20) - 1, /* Move Right 20bit */ ++ .ltar = 0x01000000, ++ .utar = 0x00000000, ++ }, ++ { ++ .viewport = 1, ++ .region_ctrl_1 = 0x00000005, ++ .region_ctrl_2 = 0x90000000, ++ .lbar = PCIE_EP_CONF_BASE + (2 << 20), /* Move Right 20bit */ ++ .ubar = 0x0, ++ .lar = PCIE_EP_CONF_BASE + (__128MB__ - 1), ++ .ltar = 0x02000000, ++ .utar = 0x00000000, ++ }, ++}; ++ ++static void __arch_config_iatu_tbl(void) ++{ ++ int i; ++ struct pcie_iatu *ptable = iatu_table; ++ int table_size = ARRAY_SIZE(iatu_table); ++ ++ for (i = 0; i < table_size; i++) { ++ writel((ptable + i)->viewport, CONF_BASE_ADDR + 0x900); ++ writel((ptable + i)->lbar, CONF_BASE_ADDR + 0x90c); ++ writel((ptable + i)->ubar, CONF_BASE_ADDR + 0x910); ++ writel((ptable + i)->lar, CONF_BASE_ADDR + 0x914); ++ writel((ptable + i)->ltar, CONF_BASE_ADDR + 0x918); ++ writel((ptable + i)->utar, CONF_BASE_ADDR + 0x91c); ++ writel((ptable + i)->region_ctrl_1, CONF_BASE_ADDR + 0x904); ++ writel((ptable + i)->region_ctrl_2, CONF_BASE_ADDR + 0x908); ++ } ++} ++ ++void bsp_pcie_init(void) ++{ ++ /* Static instance of the controller. */ ++ static struct pci_controller pcc; ++ struct pci_controller *hose = &pcc; ++ int ret; ++ (void)memset_s(&pcc, sizeof(pcc), 0, sizeof(pcc)); ++ ++ /* PCI memory space */ ++ pci_set_region(&hose->regions[0], ++ PCI_MEM_ADDR, PCI_MEM_ADDR, ++ PCI_MEM_SIZE, PCI_REGION_MEM); ++ ++ hose->region_count = 1; ++ ++ /* Start the controller. */ ++ __arch_pcie_sys_init(); ++ ++ pci_set_ops(hose, ++ pci_hose_read_config_byte_via_dword, ++ pci_hose_read_config_word_via_dword, ++ bsp_pcie_read_config, ++ pci_hose_write_config_byte_via_dword, ++ pci_hose_write_config_word_via_dword, ++ bsp_pcie_write_config ++ ); ++ ++ __arch_config_iatu_tbl(); ++ ++ /* To see the link state is ready. */ ++ ret = is_pcie_link_up(); ++ if (ret) { ++ pci_register_hose(hose); ++ hose->last_busno = pci_hose_scan(hose); ++ } else { ++ printf("link_up is error !\n"); ++ } ++} ++/* Probe function. */ ++void pci_init_board(void) ++{ ++ bsp_pcie_init(); ++} +diff --git a/drivers/phy/vendor/Kconfig b/drivers/phy/vendor/Kconfig +new file mode 100644 +index 0000000..532728d +--- /dev/null ++++ b/drivers/phy/vendor/Kconfig +@@ -0,0 +1,9 @@ ++ ++config PHY_USB ++ bool "vendor usb phy driver" ++ default y ++ ---help--- ++ support for PHY on Socs. This Phy supports ++ USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It suppots one ++ USB host port to accept one USB device. Support init the phy ++ and adjust phy Eye Diagram. +diff --git a/drivers/phy/vendor/Makefile b/drivers/phy/vendor/Makefile +new file mode 100644 +index 0000000..05bf28a +--- /dev/null ++++ b/drivers/phy/vendor/Makefile +@@ -0,0 +1,2 @@ ++ ++obj-$(CONFIG_PHY_USB) += phy-usb.o +diff --git a/drivers/phy/vendor/phy-usb-ss928v100.c b/drivers/phy/vendor/phy-usb-ss928v100.c +new file mode 100644 +index 0000000..a1f544f +--- /dev/null ++++ b/drivers/phy/vendor/phy-usb-ss928v100.c +@@ -0,0 +1,280 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++/* Attention: For ss928v100 USB3_CTRL_REG_BASE is the drd controler with ++ * base address: 0x10320000 ; USB3_CTRL_REG_BASE_1 is the host-only controler ++ * with base address: 0x10300000 ++ */ ++#include ++#include ++#include ++#include ++#include "phy-usb.h" ++#define PINOUT_REG_BASE (0x10230000) ++#define PITOUT_CTRL0_PWREN_OFFSET (0x44) ++#define PITOUT_CTRL1_PWREN_OFFSET (0X3C) ++#define PITOUT_CTRL1_VBUS_OFFSET (0x38) ++#define PINOUT_USB_VAL (0x1201) ++ ++#define SYS_STAT 0x18 ++#define PCIE_USB3_MODE (0x3<<0) ++#define PCIE_USB3_MODE_OFFSET 16 ++#define PCIE_X2 0x0 ++#define USB3_MODE 0x1 ++#define PORT0U2_PORT1U3_MODE 0x2 ++ ++#define USB2_PHY_PLLCK_ADDR_OFFSET 0x14 ++#define USB2_PHY_PLLCK_MASK 0x00000003 ++#define USB2_PHY_PLLCK_VAL ((0x3 << 0) & USB2_PHY_PLLCK_MASK) ++ ++#define USB3_GUSB2PHYCFGN 0xc200 ++#define USB3_SUSPENDUSB20_PHY (0x1 << 6) ++ ++#define ANA_CFG0_OFFSET (0x0) ++#define TX_DEEMPHASIS_ENABLE (0x1 << 5) ++#define TX_DEEMPHASIS_STRENGTH_MASK (0xF << 8) ++#define TX_DEEMPHASIS_STRENGTH_VAL (0xC << 8) ++#define MBIAS_MASK (0xF << 0) ++#define MBIAS_VAL (0xB << 0) ++#define ANA_CFG2_OFFSET (0x8) ++#define DEEMPHASIS_HALF_BIT_MASK (0xFF << 20) ++#define DEEMPHASIS_HALF_BIT_VAL (0x2 << 20) ++#define DISCONNECT_VREF_MASK (0x7 << 16) ++#define DISCONNECT_VREF_VAL (0x6 << 16) ++ ++static long ctrl_base = 0; ++static long u2_phy_base = 0; ++static long ctrl_crg_base = 0; ++static long u2_phy_crg_base = 0; ++static long u3_phy_crg_base = 0; ++int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor) ++{ ++ if (index == 0) ++ ctrl_base = USB3_CTRL_REG_BASE; ++ if (index == 1) ++ ctrl_base = USB3_CTRL_REG_BASE_1; ++ ++ *hccr = (struct xhci_hccr *)(ctrl_base); ++ *hcor = (struct xhci_hcor *)((long) *hccr ++ + HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase))); ++ ++ return 0; ++} ++ ++void usb3_eye_config(void) ++{ ++ int reg; ++ ++ reg = readl(USB2_PHY_BASE + ANA_CFG0_OFFSET); ++ reg |= TX_DEEMPHASIS_ENABLE; ++ reg &= ~(TX_DEEMPHASIS_STRENGTH_MASK | MBIAS_MASK); ++ reg |= (TX_DEEMPHASIS_STRENGTH_VAL | MBIAS_VAL); ++ writel(reg, USB2_PHY_BASE + ANA_CFG0_OFFSET); ++ udelay(U_LEVEL6); ++ ++ reg = readl(USB2_PHY_BASE + ANA_CFG2_OFFSET); ++ reg &= ~(DEEMPHASIS_HALF_BIT_MASK | DISCONNECT_VREF_MASK); ++ reg |= (DEEMPHASIS_HALF_BIT_VAL | DISCONNECT_VREF_VAL); ++ writel(reg, USB2_PHY_BASE + ANA_CFG2_OFFSET); ++ udelay(U_LEVEL6); ++ ++ reg = readl(USB2_PHY_BASE_1 + ANA_CFG0_OFFSET); ++ reg |= TX_DEEMPHASIS_ENABLE; ++ reg &= ~(TX_DEEMPHASIS_STRENGTH_MASK | MBIAS_MASK); ++ reg |= (TX_DEEMPHASIS_STRENGTH_VAL | MBIAS_VAL); ++ writel(reg, USB2_PHY_BASE_1 + ANA_CFG0_OFFSET); ++ udelay(U_LEVEL6); ++ ++ reg = readl(USB2_PHY_BASE_1 + ANA_CFG2_OFFSET); ++ reg &= ~(DEEMPHASIS_HALF_BIT_MASK | DISCONNECT_VREF_MASK); ++ reg |= (DEEMPHASIS_HALF_BIT_VAL | DISCONNECT_VREF_VAL); ++ writel(reg, USB2_PHY_BASE_1 + ANA_CFG2_OFFSET); ++ udelay(U_LEVEL6); ++} ++ ++void phy_usb_init(int index) ++{ ++ unsigned long flags; ++ unsigned int reg, cbp_mode; ++ long misc_base; ++ ++ writel(PINOUT_USB_VAL, PINOUT_REG_BASE + PITOUT_CTRL0_PWREN_OFFSET); ++ writel(PINOUT_USB_VAL, PINOUT_REG_BASE + PITOUT_CTRL1_PWREN_OFFSET); ++ writel(PINOUT_USB_VAL, PINOUT_REG_BASE + PITOUT_CTRL1_VBUS_OFFSET); ++ udelay(U_LEVEL6); ++ ++ local_irq_save(flags); ++ ++ if (index == 0) { ++ ctrl_base = USB3_CTRL_REG_BASE; ++ u2_phy_base = USB2_PHY_BASE; ++ ctrl_crg_base = USB3_CTRL_CRG; ++ u2_phy_crg_base = USB2_PHY_CRG; ++ u3_phy_crg_base = USB3_PHY_CRG; ++ misc_base = MISC_USB3_CTRL_REG; ++ } else if (index == 1) { ++ ctrl_base = USB3_CTRL_REG_BASE_1; ++ u2_phy_base = USB2_PHY_BASE_1; ++ ctrl_crg_base = USB3_CTRL_CRG_1; ++ u2_phy_crg_base = USB2_PHY_CRG_1; ++ u3_phy_crg_base = USB3_PHY_CRG_1; ++ misc_base = MISC_USB3_CTRL_REG_1; ++ } else { ++ return; ++ } ++ ++ /* judgement pcie usb3 mode */ ++ cbp_mode = readl(SYS_CTRL_REG_BASE + SYS_STAT); ++ cbp_mode = cbp_mode >> PCIE_USB3_MODE_OFFSET; ++ cbp_mode &= PCIE_USB3_MODE; ++ ++ /* write default crg value */ ++ writel(USB3_CTRL_CRG_DEFAULT_VALUE, ctrl_crg_base); ++ writel(USB2_PHY_CRG_DEFAULT_VALUE, u2_phy_crg_base); ++ writel(USB3_PHY_CRG_DEFAULT_VALUE, u3_phy_crg_base); ++ udelay(U_LEVEL6); ++ ++ /* u-boot should disable U3 speed */ ++ reg = readl(misc_base); ++ if (index == 0) { ++ reg |= (USB3_DISABLE_U3_SPEED); ++ } else { ++ reg |= (USB3_DISABLE_U3_SPEED_1); ++ } ++ writel(reg, misc_base); ++ udelay(U_LEVEL6); ++ ++ /* phy crg setting */ ++ reg = readl(u2_phy_crg_base); ++ reg &= ~(USB2_PHY_CRG_APB_SREQ); ++ writel(reg, u2_phy_crg_base); ++ udelay(U_LEVEL6); ++ ++ /* ctrl crg setting */ ++ /* usb3 occ pclk sel */ ++ reg = readl(ctrl_crg_base); ++ if (cbp_mode == PCIE_X2) ++ reg |= USB3_CRG_PCLK_OCC_SEL; ++ else if (cbp_mode == PORT0U2_PORT1U3_MODE && index == 1) ++ reg |= USB3_CRG_PCLK_OCC_SEL; ++ else ++ reg &= ~(USB3_CRG_PCLK_OCC_SEL); ++ ++ reg |= (USB3_CRG_PIPE_CKEN | ++ USB3_CRG_UTMI_CKEN | ++ USB3_CRG_SUSPEND_CKEN | ++ USB3_CRG_REF_CKEN | ++ USB3_CRG_BUS_CKEN); ++ writel(reg, ctrl_crg_base); ++ udelay(U_LEVEL6); // delay 200us ++ ++ /* U3 phy TPOR &POR reset */ ++ reg = readl(u3_phy_crg_base); ++ reg &= ~(USB3_PHY_CRG_TREQ | USB3_PHY_CRG_REQ); ++ writel(reg, u3_phy_crg_base); ++ udelay(U_LEVEL6); // delay 200us ++ ++ /* U2 phy POR reset */ ++ reg = readl(u2_phy_crg_base); ++ reg &= ~USB2_PHY_CRG_REQ; ++ writel(reg, u2_phy_crg_base); ++ ++ reg = readl(u2_phy_base + USB2_PHY_PLLCK_ADDR_OFFSET); ++ reg &= ~USB2_PHY_PLLCK_MASK; ++ reg |= USB2_PHY_PLLCK_VAL; ++ writel(reg, u2_phy_base + USB2_PHY_PLLCK_ADDR_OFFSET); ++ udelay(U_LEVEL10); // delay 2ms ++ ++ /* U2 phy TPOR reset */ ++ reg = readl(u2_phy_crg_base); ++ reg &= ~USB2_PHY_CRG_TREQ; ++ writel(reg, u2_phy_crg_base); ++ udelay(U_LEVEL6); // delay 200us ++ ++ /* ctrl crg reset release*/ ++ reg = readl(ctrl_crg_base); ++ reg &= ~USB3_CRG_SRST_REQ; ++ writel(reg, ctrl_crg_base); ++ udelay(U_LEVEL6); // delay 200us ++ ++ /* CTRL set */ ++ reg = readl(ctrl_base + USB3_GUSB2PHYCFGN); ++ if (cbp_mode == PCIE_X2) ++ reg &= ~(USB3_SUSPENDUSB20_PHY); ++ else if (cbp_mode == PORT0U2_PORT1U3_MODE && index == 1) ++ reg &= ~(USB3_SUSPENDUSB20_PHY); ++ else ++ reg |= (USB3_SUSPENDUSB20_PHY); ++ writel(reg, ctrl_base + USB3_GUSB2PHYCFGN); ++ udelay(U_LEVEL6); ++ ++ /* Host mode */ ++ reg = readl(ctrl_base + REG_GUSB3PIPECTL0); ++ reg |= PCS_SSP_SOFT_RESET; ++ writel(reg, ctrl_base + REG_GUSB3PIPECTL0); ++ udelay(U_LEVEL6); ++ reg = readl(ctrl_base + REG_GCTL); ++ reg &= ~PORT_CAP_DIR_MASK; ++ reg |= PORT_CAP_DIR_HOST; ++ writel(reg, ctrl_base + REG_GCTL); ++ udelay(U_LEVEL6); // delay 200us ++ ++ reg = readl(ctrl_base + REG_GUSB3PIPECTL0); ++ reg &= ~PCS_SSP_SOFT_RESET; ++ reg &= ~SUSPEND_ENABLE; ++ writel(reg, ctrl_base + REG_GUSB3PIPECTL0); ++ udelay(U_LEVEL6); // delay 200us ++ ++ reg &= CLEAN_USB3_GTXTHRCFG; ++ reg |= USB_TXPKT_CNT_SEL; ++ reg |= USB_TXPKT_CNT; ++ reg |= USB_MAXTX_BURST_SIZE; ++ writel(reg, ctrl_base + GTXTHRCFG); ++ udelay(U_LEVEL6); // delay 200us ++ writel(reg, ctrl_base + GRXTHRCFG); ++ udelay(U_LEVEL6); // delay 200us ++ ++ usb3_eye_config(); ++ ++ local_irq_restore(flags); ++} ++ ++void xhci_hcd_stop(int index) ++{ ++ if (index == 0) { ++ ctrl_base = USB3_CTRL_REG_BASE; ++ u2_phy_base = USB2_PHY_BASE; ++ ctrl_crg_base = USB3_CTRL_CRG; ++ u2_phy_crg_base = USB2_PHY_CRG; ++ u3_phy_crg_base = USB3_PHY_CRG; ++ } else if (index == 1) { ++ ctrl_base = USB3_CTRL_REG_BASE_1; ++ u2_phy_base = USB2_PHY_BASE_1; ++ ctrl_crg_base = USB3_CTRL_CRG_1; ++ u2_phy_crg_base = USB2_PHY_CRG_1; ++ u3_phy_crg_base = USB3_PHY_CRG_1; ++ } else { ++ return; ++ } ++ ++ /* write default crg value */ ++ writel(USB3_CTRL_CRG_DEFAULT_VALUE, ctrl_crg_base); ++ writel(USB2_PHY_CRG_DEFAULT_VALUE, u2_phy_crg_base); ++ writel(USB3_PHY_CRG_DEFAULT_VALUE, u3_phy_crg_base); ++ udelay(U_LEVEL6); // delay 200us ++} +diff --git a/drivers/phy/vendor/phy-usb.c b/drivers/phy/vendor/phy-usb.c +new file mode 100644 +index 0000000..86d21be +--- /dev/null ++++ b/drivers/phy/vendor/phy-usb.c +@@ -0,0 +1,22 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++#include "phy-usb-ss928v100.c" ++#endif +diff --git a/drivers/phy/vendor/phy-usb.h b/drivers/phy/vendor/phy-usb.h +new file mode 100644 +index 0000000..4eb66e6 +--- /dev/null ++++ b/drivers/phy/vendor/phy-usb.h +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef PHY_USB ++#define PHY_USB ++ ++#define U_LEVEL1 10 ++#define U_LEVEL2 20 ++#define U_LEVEL3 30 ++#define U_LEVEL4 50 ++#define U_LEVEL5 100 ++#define U_LEVEL6 200 ++#define U_LEVEL7 300 ++#define U_LEVEL8 500 ++#define U_LEVEL9 1000 ++#define U_LEVEL10 2000 ++ ++ ++#endif // PHY_USB +diff --git a/drivers/phy/vendor/usb.h b/drivers/phy/vendor/usb.h +new file mode 100644 +index 0000000..782912e +--- /dev/null ++++ b/drivers/phy/vendor/usb.h +@@ -0,0 +1,25 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef USB_H ++#define USB_H ++ ++extern void phy_usb_init(int index); ++ ++#endif // USB_H +diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig +old mode 100644 +new mode 100755 +diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c +old mode 100644 +new mode 100755 +index bf5f392..ab9a671 +--- a/drivers/serial/serial.c ++++ b/drivers/serial/serial.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + + DECLARE_GLOBAL_DATA_PTR; + +@@ -446,6 +447,9 @@ void serial_puts(const char *s) + */ + void default_serial_puts(const char *s) + { ++#ifdef CONFIG_USB_GADGET ++ udc_puts(s); ++#endif + struct serial_device *dev = get_current(); + while (*s) + dev->putc(*s++); +diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c +index 2a5f256..515c9e4 100644 +--- a/drivers/serial/serial_pl01x.c ++++ b/drivers/serial/serial_pl01x.c +@@ -19,6 +19,7 @@ + #include + #include + #include "serial_pl01x_internal.h" ++#include + + DECLARE_GLOBAL_DATA_PTR; + +@@ -31,8 +32,18 @@ static struct pl01x_regs *base_regs __attribute__ ((section(".data"))); + + #endif + ++#ifdef CONFIG_BSP_DISABLE_CONSOLE ++static bool g_uart_fputc_en = false; ++#else ++static bool g_uart_fputc_en = true; ++#endif ++ + static int pl01x_putc(struct pl01x_regs *regs, char c) + { ++ if (g_uart_fputc_en == false) { ++ return 0; ++ } ++ + /* Wait until there is space in the FIFO */ + if (readl(®s->fr) & UART_PL01x_FR_TXFF) + return -EAGAIN; +@@ -270,6 +281,17 @@ __weak struct serial_device *default_serial_console(void) + + #endif /* nCONFIG_DM_SERIAL */ + ++void serial_puts_to_tool(const char *s) ++{ ++#ifdef CONFIG_USB_GADGET ++ udc_puts(s); ++#endif ++ while (*s) { ++ while (pl01x_putc(base_regs, *s) == -EAGAIN); ++ s++; ++ } ++} ++ + #ifdef CONFIG_DM_SERIAL + + int pl01x_serial_setbrg(struct udevice *dev, int baudrate) +@@ -396,3 +418,8 @@ static inline void _debug_uart_putc(int ch) + DEBUG_UART_FUNCS + + #endif ++ ++void serial_enable_output(bool is_enable) ++{ ++ g_uart_fputc_en = is_enable; ++} +diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig +old mode 100644 +new mode 100755 +index c2aafd3..2908d88 +--- a/drivers/ufs/Kconfig ++++ b/drivers/ufs/Kconfig +@@ -1,6 +1,6 @@ + menu "UFS Host Controller Support" + +-config UFS ++config SCSI_UFS + bool "Support UFS controllers" + depends on DM_SCSI + help +@@ -20,4 +20,11 @@ config TI_J721E_UFS + This selects the glue layer driver for Cadence controller + present on TI's J721E devices. + ++config UFS ++ bool "UFS card support" ++ select HAVE_BLOCK_DEVICE ++ help ++ This selects Universal Flash Storage support. ++ If you want UFS support, you should say Y here and ++ also to your specific host controller driver. + endmenu +diff --git a/drivers/ufs/Makefile b/drivers/ufs/Makefile +index 62ed016..caf5c81 100644 +--- a/drivers/ufs/Makefile ++++ b/drivers/ufs/Makefile +@@ -3,6 +3,7 @@ + # Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com + # + +-obj-$(CONFIG_UFS) += ufs.o ufs-uclass.o ++obj-$(CONFIG_SCSI_UFS) += ufs.o ufs-uclass.o + obj-$(CONFIG_CADENCE_UFS) += cdns-platform.o + obj-$(CONFIG_TI_J721E_UFS) += ti-j721e-ufs.o ++obj-$(CONFIG_UFS) += ufs_bsp.o scsi.o +diff --git a/drivers/ufs/scsi.c b/drivers/ufs/scsi.c +new file mode 100644 +index 0000000..adc1c5c +--- /dev/null ++++ b/drivers/ufs/scsi.c +@@ -0,0 +1,168 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "scsi.h" ++ ++struct scsi_adapt_func { ++ uint32_t opcode; ++ void (*func)(uint32_t lba, uint32_t size, uint8_t *cmd); ++}; ++ ++static void scsi_read_10(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_READ_10; /* 0: opcode */ ++ cmd[1] = 0; /* 1: DPO FUA */ ++ cmd[2] = (uint8_t)((lba >> 24) & 0xff); /* 2: MSB, shift 24 */ ++ cmd[3] = (uint8_t)((lba >> 16) & 0xff); /* 3: MSB, shift 16 */ ++ cmd[4] = (uint8_t)((lba >> 8) & 0xff); /* 4: LSB, shift 8 */ ++ cmd[5] = (uint8_t)(lba & 0xff); /* 5: LSB */ ++ cmd[6] = 0; /* 6: group */ ++ cmd[7] = (uint8_t)((size >> 8) & 0xff); /* 7: MSB, shift 8 */ ++ cmd[8] = (uint8_t)(size & 0xff); /* 8: LSB */ ++ cmd[9] = 0; /* 9: control */ ++} ++ ++static void scsi_write_10(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_WRITE_10; /* 0: opcode */ ++ cmd[1] = 0; /* 1: DPO FUA */ ++ cmd[2] = (uint8_t)((lba >> 24) & 0xff); /* 2: MSB, shift 24 */ ++ cmd[3] = (uint8_t)((lba >> 16) & 0xff); /* 3: MSB, shift 16 */ ++ cmd[4] = (uint8_t)((lba >> 8) & 0xff); /* 4: LSB, shift 8 */ ++ cmd[5] = (uint8_t)(lba & 0xff); /* 5: LSB */ ++ cmd[6] = 0; /* 6: group */ ++ cmd[7] = (uint8_t)((size >> 8) & 0xff); /* 7: MSB, shift 8 */ ++ cmd[8] = (uint8_t)(size & 0xff); /* 8: LSB */ ++ cmd[9] = 0; /* 9: control */ ++} ++ ++static void scsi_sync_cache_10(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_SYNCHRONIZE_CACHE_10; /* 0: opcode */ ++ cmd[1] = 0; /* 1: IMMED */ ++ cmd[2] = (uint8_t)((lba >> 24) & 0xff); /* 2: MSB, shift 24 */ ++ cmd[3] = (uint8_t)((lba >> 16) & 0xff); /* 3: MSB, shift 16 */ ++ cmd[4] = (uint8_t)((lba >> 8) & 0xff); /* 4: LSB, shift 8 */ ++ cmd[5] = (uint8_t)(lba & 0xff); /* 5: LSB */ ++ cmd[6] = 0; /* 6: group */ ++ cmd[7] = (uint8_t)((size >> 8) & 0xff); /* 7: MSB, shift 8 */ ++ cmd[8] = (uint8_t)(size & 0xff); /* 8: LSB */ ++ cmd[9] = 0; /* 9: control */ ++} ++ ++static void scsi_read_capacity_10(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_READ_CAPACITY_10; /* 0: opcode */ ++ cmd[1] = 0; /* 1: reserved */ ++ cmd[2] = 0; /* 2: MSB, 0 for UFS */ ++ cmd[3] = 0; /* 3: MSB, 0 for UFS */ ++ cmd[4] = 0; /* 4: LSB, 0 for UFS */ ++ cmd[5] = 0; /* 5: LSB, 0 for UFS */ ++ cmd[6] = 0; /* 6: reserved */ ++ cmd[7] = 0; /* 7: reserved */ ++ cmd[8] = 0; /* 8: reserved */ ++ cmd[9] = 0; /* 9: control */ ++} ++ ++static void scsi_request_sense(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_REQUEST_SENSE; /* 0: opcode */ ++ cmd[1] = 0; /* 1: reserved */ ++ cmd[2] = 0; /* 2: reserved */ ++ cmd[3] = 0; /* 3: reserved */ ++ cmd[4] = (uint8_t)(size & 0xff); /* 4: allocation length */ ++ cmd[5] = 0; /* 5: control */ ++} ++ ++static void scsi_unmap(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_UNMAP; /* 0: opcode */ ++ cmd[1] = 0; /* 1: reserved */ ++ cmd[2] = 0; /* 2: reserved */ ++ cmd[3] = 0; /* 3: reserved */ ++ cmd[4] = 0; /* 4: reserved */ ++ cmd[5] = 0; /* 5: reserved */ ++ cmd[6] = 0; /* 6: group */ ++ cmd[7] = (uint8_t)((size >> 8) & 0xff); /* 7: MSB, shift 8 */ ++ cmd[8] = (uint8_t)(size & 0xff); /* 8: LSB */ ++ cmd[9] = 0; /* 9: control */ ++} ++ ++static void scsi_secproc_in(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_SECURITY_PROTOCOL_IN; /* 0: opcode */ ++ cmd[1] = 0xEC; /* 1: security protocal */ ++ cmd[2] = 0; /* 2: specific */ ++ cmd[3] = 0x1; /* 3: specific */ ++ cmd[4] = 0; /* 4: reserved */ ++ cmd[5] = 0; /* 5: reserved */ ++ cmd[6] = (uint8_t)((lba >> 24) & 0xff); /* 6: MSB, shift 24 */ ++ cmd[7] = (uint8_t)((lba >> 16) & 0xff); /* 7: MSB, shift 16 */ ++ cmd[8] = (uint8_t)((lba >> 8) & 0xff); /* 8: LSB, shift 8 */ ++ cmd[9] = (uint8_t)(lba & 0xff); /* 9: LSB */ ++ cmd[10] = 0; /* 10: reserved */ ++ cmd[11] = 0; /* 11: control */ ++} ++ ++static void scsi_secproc_out(uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ cmd[0] = UFS_OP_SECURITY_PROTOCOL_OUT; /* 0: opcode */ ++ cmd[1] = 0xEC; /* 1: security protocal */ ++ cmd[2] = 0; /* 2: specific */ ++ cmd[3] = 0x1; /* 3: specific */ ++ cmd[4] = 0; /* 4: reserved */ ++ cmd[5] = 0; /* 5: reserved */ ++ cmd[6] = (uint8_t)((lba >> 24) & 0xff); /* 6: MSB, shift 24 */ ++ cmd[7] = (uint8_t)((lba >> 16) & 0xff); /* 7: MSB, shift 16 */ ++ cmd[8] = (uint8_t)((lba >> 8) & 0xff); /* 8: LSB, shift 8 */ ++ cmd[9] = (uint8_t)(lba & 0xff); /* 9: LSB */ ++ cmd[10] = 0; /* 10: reserved */ ++ cmd[11] = 0; /* 11: control */ ++} ++ ++void get_cmnd(uint32_t opcode, uint32_t lba, uint32_t size, uint8_t *cmd) ++{ ++ int i; ++ ++ struct scsi_adapt_func funclist[] = { ++ {UFS_OP_READ_10, scsi_read_10}, ++ {UFS_OP_WRITE_10, scsi_write_10}, ++ {UFS_OP_SYNCHRONIZE_CACHE_10, scsi_sync_cache_10}, ++ {UFS_OP_READ_CAPACITY_10, scsi_read_capacity_10}, ++ {UFS_OP_REQUEST_SENSE, scsi_request_sense}, ++ {UFS_OP_UNMAP, scsi_unmap}, ++ {UFS_OP_SECURITY_PROTOCOL_IN, scsi_secproc_in}, ++ {UFS_OP_SECURITY_PROTOCOL_OUT, scsi_secproc_out} ++ }; ++ ++ if (cmd == NULL) { ++ printf("get_cmd invalid cmd buffer\n"); ++ return; ++ } ++ for (i = 0; i < sizeof(funclist) / sizeof(funclist[0]); i++) { ++ if (funclist[i].opcode == opcode) { ++ funclist[i].func(lba, size, cmd); ++ return; ++ } ++ } ++ printf("opcode: 0x%x not support\n", opcode); ++} ++ +diff --git a/drivers/ufs/scsi.h b/drivers/ufs/scsi.h +new file mode 100644 +index 0000000..21dbb3a +--- /dev/null ++++ b/drivers/ufs/scsi.h +@@ -0,0 +1,66 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __UFS_SCSI_H__ ++#define __UFS_SCSI_H__ ++ ++/* Ref [2], Sec 11.3 */ ++#define UFS_OP_FORMAT_UNIT 0x04 ++#define UFS_OP_INQUIRY 0x12 ++#define UFS_OP_MODE_SELECT_10 0x55 ++#define UFS_OP_MODE_SENSE_10 0x5A ++#define UFS_OP_PRE_FETCH_10 0x34 ++#define UFS_OP_PRE_FETCH_16 0x90 ++#define UFS_OP_READ_6 0x08 ++#define UFS_OP_READ_10 0x28 ++#define UFS_OP_READ_16 0x88 ++#define UFS_OP_READ_BUFFER 0x3C ++#define UFS_OP_READ_CAPACITY_10 0x25 ++#define UFS_OP_READ_CAPACITY_16 0x9E ++#define UFS_OP_REPORT_LUNS 0xA0 ++#define UFS_OP_REQUEST_SENSE 0x03 ++#define UFS_OP_SECURITY_PROTOCOL_IN 0xA2 ++#define UFS_OP_SECURITY_PROTOCOL_OUT 0xB5 ++#define UFS_OP_SEND_DIAGNOSTIC 0x1D ++#define UFS_OP_START_STOP_UNIT 0x1B ++#define UFS_OP_SYNCHRONIZE_CACHE_10 0x35 ++#define UFS_OP_SYNCHRONIZE_CACHE_16 0x91 ++#define UFS_OP_TEST_UNIT_READY 0x00 ++#define UFS_OP_UNMAP 0x42 ++#define UFS_OP_VERIFY_10 0x2F ++#define UFS_OP_WRITE_6 0x0A ++#define UFS_OP_WRITE_10 0x2A ++#define UFS_OP_WRITE_16 0x8A ++#define UFS_OP_WRITE_BUFFER 0x3B ++#define SECURITY_PROTOCOL 0xEC ++ ++/* SCSI Status Macros */ ++#define SAM_STAT_GOOD 0x00 ++#define SAM_STAT_CHECK_CONDITION 0x02 ++#define SAM_STAT_CONDITION_MET 0x04 ++#define SAM_STAT_BUSY 0x08 ++#define SAM_STAT_RESERVATION_CONFLICT 0x18 ++#define SAM_STAT_TASK_SET_FULL 0x28 ++#define SAM_STAT_ACA_ACTIVE 0x30 ++#define SAM_STAT_TASK_ABORTED 0x40 ++ ++void get_cmnd(uint32_t opcode, uint32_t lba, uint32_t size, uint8_t *cmd); ++ ++#endif /*__UFS_SCSI_H__*/ ++ +diff --git a/drivers/ufs/ufs_bsp.c b/drivers/ufs/ufs_bsp.c +new file mode 100644 +index 0000000..2d45dd3 +--- /dev/null ++++ b/drivers/ufs/ufs_bsp.c +@@ -0,0 +1,3427 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "ufs_bsp.h" ++#include "scsi.h" ++ ++#define UFS_CID_SIZE 4 ++#define SERIAL_NUM_SIZE 12 ++ ++struct dwc_ufs_hba g_dwc_host[MAX_DEVICE]; ++struct ufs_descriptor g_ufs_desc; ++struct ufs g_ufs_info; ++ ++static uint8_t g_tx_lane_num[MAX_DEVICE] = {0}; ++static uint8_t g_rx_lane_num[MAX_DEVICE] = {0}; ++static int g_wlun = 0; ++static unsigned int g_ufs_cid[UFS_CID_SIZE]; ++ ++struct ufs_adapt_reg { ++ unsigned int addr; ++ unsigned int value[7]; /* 7 mode param */ ++}; ++ ++static struct ufs_adapt_reg reglist_pmc[] = { ++ /* addr G3RB G3RA G2RB G2RA G1RB G1RA PWM */ ++ {0x007e0000, { 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}}, ++ {0x007e0001, { 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}}, ++ {0x00fc0004, { 0x1f, 0x1f, 0x1b, 0x1b, 0x1b, 0x1b, 0x00}}, ++ {0x00fc0005, { 0x1f, 0x1f, 0x1b, 0x1b, 0x1b, 0x1b, 0x00}}, ++ {0x00fd0004, { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}}, ++ {0x00fd0005, { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}}, ++ {0x007f0000, { 0x24, 0x22, 0x21, 0x21, 0x13, 0x13, 0x00}}, ++ {0x007f0001, { 0x24, 0x22, 0x21, 0x21, 0x13, 0x13, 0x00}}, ++ {0x007d0000, { 0x24, 0x22, 0x21, 0x21, 0x13, 0x13, 0x00}}, ++ {0x007d0001, { 0x24, 0x22, 0x21, 0x21, 0x13, 0x13, 0x00}}, ++ {0x00370000, { 0x26, 0x26, 0x23, 0x23, 0x20, 0x20, 0x00}}, ++ {0x00370001, { 0x26, 0x26, 0x23, 0x23, 0x20, 0x20, 0x00}}, ++ {0x007b0000, { 0x26, 0x26, 0x23, 0x23, 0x20, 0x20, 0x00}}, ++ {0x007b0001, { 0x26, 0x26, 0x23, 0x23, 0x20, 0x20, 0x00}} ++}; ++ ++static void adapt_mode_change(struct pwr_mode_params *pmp) ++{ ++ int i, mode; ++ ++ get_local_dwc_host(); ++ if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_HYNIX) { ++ printf("H**** device must set VS_DebugSaveConfigTime 0x10\n"); ++ /* VS_DebugSaveConfigTime */ ++ send_uic_command(DME_SET, 0xd0a00000, 0x0, 0x10); ++ /* sync length */ ++ send_uic_command(DME_SET, 0x15560000, 0x0, 0x48); ++ } ++ ++#if defined(COMBO_PHY_V120) ++ if (pmp->pwr_mode == FAST_MODE || pmp->pwr_mode == FASTAUTO_MODE) { ++ mode = 8 - pmp->tx_gear * 2 - pmp->hs_series; /* mode[8 - gear * 2 - rate] */ ++ } else { ++ mode = 6; /* mode[6] : PWM */ ++ return; /* PWM is unused here */ ++ } ++ ++ for (i = 0; i < sizeof(reglist_pmc) / sizeof(reglist_pmc[0]); i++) ++ send_uic_command(DME_SET, reglist_pmc[i].addr, 0x0, reglist_pmc[i].value[mode]); ++#endif ++} ++ ++static int ufs_read_string_index(char *dest, uint8_t desc_index) ++{ ++ struct desc_params params; ++ struct dwc_ufs_query_upiu *req_upiu = NULL; ++ void *resp_upiu = NULL; ++ uint8_t *p = NULL; ++ int ret; ++ int len; ++ int i; ++ ++ get_local_dwc_host(); ++ /* use slot 0 default */ ++ resp_upiu = dwc_host->lrb[0].resp_upiu; ++ req_upiu = (struct dwc_ufs_query_upiu *)(dwc_host->lrb[0].cmd_upiu); ++ ++ params.req_upiu = req_upiu; ++ params.part_desc = NULL; ++ params.opcode = READ_DESC_OPCODE; ++ params.desc_idn = STRING_DESC; ++ params.desc_index = desc_index; ++ params.length = STRING_DESC_LENGTH; ++ modify_desc_upiu(¶ms); ++ ++ ret = read_descriptor(req_upiu, &resp_upiu); ++ if (ret != UFS_OK) { ++ printf("read descriptor fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ /* get string info */ ++ p = ((u8 *)resp_upiu + QUERY_RESPONSE_HEAD_OFFSET); ++ len = p[0] / 2; /* 2 byte unicode */ ++ ++ for (i = 1; i < len; i++) ++ dest[i - 1] = p[2 * i + 1]; /* 2 byte unicode */ ++ dest[len - 1] = '\0'; ++ ++ return UFS_OK; ++} ++ ++static int ufs_read_string_descriptor(void) ++{ ++ int ret; ++ ++ /* manufacturer name */ ++ ret = ufs_read_string_index(g_ufs_desc.str_desc.manufacturer_name, ++ g_ufs_desc.dev_desc.i_manufacturer_name); ++ if (ret != UFS_OK) { ++ printf("get manufacturer name fail\n"); ++ return UFS_ERR; ++ } ++ ++ /* product name */ ++ ret = ufs_read_string_index(g_ufs_desc.str_desc.product_name, ++ g_ufs_desc.dev_desc.i_product_name); ++ if (ret != UFS_OK) { ++ printf("get product name fail\n"); ++ return UFS_ERR; ++ } ++ ++ /* serial number */ ++ ret = ufs_read_string_index(g_ufs_desc.str_desc.serial_number, ++ g_ufs_desc.dev_desc.i_serial_number); ++ if (ret != UFS_OK) { ++ printf("get serial number fail\n"); ++ return UFS_ERR; ++ } ++ ++ /* oem id */ ++ ret = ufs_read_string_index(g_ufs_desc.str_desc.oem_id, ++ g_ufs_desc.dev_desc.i_oem_id); ++ if (ret != UFS_OK) { ++ printf("get oem id fail\n"); ++ return UFS_ERR; ++ } ++ ++ return UFS_OK; ++} ++ ++static int ufs_read_descriptor(void *dest, uint8_t idn, uint8_t index, uint16_t length) ++{ ++ struct desc_params params; ++ struct dwc_ufs_query_upiu *req_upiu = NULL; ++ void *resp_upiu = NULL; ++ int ret; ++ ++ get_local_dwc_host(); ++ /* use slot 0 default */ ++ resp_upiu = dwc_host->lrb[0].resp_upiu; ++ req_upiu = (struct dwc_ufs_query_upiu *)(dwc_host->lrb[0].cmd_upiu); ++ ++ params.req_upiu = req_upiu; ++ params.part_desc = NULL; ++ params.opcode = READ_DESC_OPCODE; ++ params.desc_idn = idn; ++ params.desc_index = index; ++ params.length = length; ++ modify_desc_upiu(¶ms); ++ ++ ret = read_descriptor(req_upiu, &resp_upiu); ++ if (ret != UFS_OK) { ++ printf("read descriptor fail. ret = %d\n", ret); ++ return ret; ++ } ++ ret = memcpy_s(dest, length, (void *)((u8 *)resp_upiu + QUERY_RESPONSE_HEAD_OFFSET), length); ++ if (ret != UFS_OK) { ++ printf("memcpy_s fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ return UFS_OK; ++} ++ ++static int ufs_read_unit_descriptor(void) ++{ ++ int ret; ++ int i; ++ ++ /* Unit Descriptor */ ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ ret = ufs_read_descriptor((void *)(&g_ufs_desc.unit_desc.unit_index_desc[i]), ++ UNIT_DESC, i, UNIT_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("get unit[%d] fail\n", i); ++ return UFS_ERR; ++ } ++ } ++ ++ /* RPMB Descriptor */ ++ ret = ufs_read_descriptor((void *)(&g_ufs_desc.unit_desc.unit_rpmb_desc), ++ UNIT_DESC, 0xC4, UNIT_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("get rpmb fail\n"); ++ return UFS_ERR; ++ } ++ ++ return UFS_OK; ++} ++ ++static void ufs_info_show_portion(void) ++{ ++ printf("Manufacturer ID: 0x%x\n", to_bigendian16(g_ufs_desc.dev_desc.w_manufacturer_id)); ++ printf("Product Name: %s\n", g_ufs_desc.str_desc.product_name); ++ ++ printf("Speed Mode: %s Gear-%d Rate-%c Lanes-%d\n", ++ ((DEFAULT_MODE == SLOW_MODE) ? "Slow" : ++ ((DEFAULT_MODE == SLOWAUTO_MODE) ? "SlowAuto" : ++ ((DEFAULT_MODE == FAST_MODE) ? "Fast" : "FastAuto"))), ++ DEFAULT_GEAR, (DEFAULT_RATE == 1) ? 'A' : 'B', DEFAULT_LANE); ++ printf("High Capacity: Yes\n"); ++ printf("Capacity: %lld\n", g_ufs_info.capacity); ++} ++ ++static void ufs_info_show_dev_desc(void) ++{ ++ struct ufs_device_descriptor dev; ++ ++ dev = g_ufs_desc.dev_desc; ++ ++ printf("---------------------------\n"); ++ printf("---UFS Device Descriptor---\n"); ++ printf("---------------------------\n"); ++ printf("bLength: 0x%x\n", dev.b_length); ++ printf("bDescriptorIDN: 0x%x\n", dev.b_descriptor_idn); ++ printf("bDevice: 0x%x\n", dev.b_device); ++ printf("bDeviceClass: 0x%x\n", dev.b_device_class); ++ printf("bDeviceSubClass: 0x%x\n", dev.b_device_sub_class); ++ printf("bProtocol: 0x%x\n", dev.b_protocol); ++ printf("bNumberLU: 0x%x\n", dev.b_number_lu); ++ printf("bNumberWLU: 0x%x\n", dev.b_number_wlu); ++ printf("bBootEnable: 0x%x\n", dev.b_boot_enable); ++ printf("bDescrAccessEn: 0x%x\n", dev.b_descr_access_en); ++ printf("bInitPowerMode: 0x%x\n", dev.b_init_power_mode); ++ printf("bHighPriorityLUN: 0x%x\n", dev.b_high_priority_lun); ++ printf("bSecureRemovalType: 0x%x\n", dev.b_secure_removal_type); ++ printf("bSecurityLU: 0x%x\n", dev.b_security_lu); ++ printf("bBackgroundOpsTermLat: 0x%x\n", dev.b_background_ops_term_lat); ++ printf("bInitActiveICCLevel: 0x%x\n", dev.b_init_active_icc_level); ++ printf("wSpecVersion: 0x%x\n", to_bigendian16(dev.w_spec_version)); ++ printf("wManufactureDate: 0x%x\n", to_bigendian16(dev.w_manufacture_date)); ++ printf("iManufacturerName: 0x%x\n", dev.i_manufacturer_name); ++ printf("iProductName: 0x%x\n", dev.i_product_name); ++ printf("iSerialNumber: 0x%x\n", dev.i_serial_number); ++ printf("iOemID: 0x%x\n", dev.i_oem_id); ++ printf("wManufacturerID: 0x%x\n", to_bigendian16(dev.w_manufacturer_id)); ++ printf("bUD0BaseOffset: 0x%x\n", dev.b_ud_0base_offset); ++ printf("bUDConfigPLength: 0x%x\n", dev.b_ud_config_plength); ++ printf("bDeviceRTTCap: 0x%x\n", dev.b_device_rtt_cap); ++ printf("wPeriodicRTCUpdate: 0x%x\n", to_bigendian16(dev.w_periodic_rtc_update)); ++ printf("bUFSFeatureSupport: 0x%x\n", dev.b_ufs_feature_support); ++ printf("bFFUTimeout: 0x%x\n", dev.b_ffu_timeout); ++ printf("bQueueDepth: 0x%x\n", dev.b_queue_depth); ++ printf("wDeviceVersion: 0x%x\n", to_bigendian16(dev.w_device_version)); ++ printf("bNumSecureWPArea: 0x%x\n", dev.b_num_secure_wp_area); ++ printf("dPSAMaxDataSize: 0x%x\n", to_bigendian32(dev.d_psa_max_data_size)); ++ printf("bPSAStateTimeout: 0x%x\n", dev.b_psa_state_timeout); ++ printf("iProductRevisionLevel: 0x%x\n", dev.i_product_revision_level); ++} ++ ++static void ufs_info_show_geo_desc(void) ++{ ++ struct ufs_geometry_descriptor geo; ++ ++ geo = g_ufs_desc.geo_desc; ++ ++ printf("-----------------------------\n"); ++ printf("---UFS Geometry Descriptor---\n"); ++ printf("-----------------------------\n"); ++ printf("bLength: 0x%x\n", geo.b_length); ++ printf("bDescriptorIDN: 0x%x\n", geo.b_descriptor_idn); ++ printf("bMediaTechnology: 0x%x\n", geo.b_media_technology); ++ printf("qTotalRawDeviceCapacity: 0x%llx\n", cpu_to_be64(geo.q_total_raw_device_capacity)); ++ printf("bMaxNumberLU: 0x%x\n", geo.b_max_number_lu); ++ printf("dSegmentSize: 0x%x\n", to_bigendian32(geo.d_segment_size)); ++ printf("bAllocationUnitSize: 0x%x\n", geo.b_allocation_unit_size); ++ printf("bMinAddrBlockSize: 0x%x\n", geo.b_min_addr_block_size); ++ printf("bOptimalReadBlockSize: 0x%x\n", geo.b_optimal_read_block_size); ++ printf("bOptimalWriteBlockSize: 0x%x\n", geo.b_optimal_write_block_size); ++ printf("bMaxInBufferSize: 0x%x\n", geo.b_max_in_buffer_size); ++ printf("bMaxOutBufferSize: 0x%x\n", geo.b_max_out_buffer_size); ++ printf("bRPMB_ReadWriteSize: 0x%x\n", geo.b_rpmb_read_write_size); ++ printf("bDynamicCapacityResourcePolicy: 0x%x\n", geo.b_dynamic_capacity_resource_policy); ++ printf("bDataOrdering: 0x%x\n", geo.b_data_ordering); ++ printf("bMaxContexIDNumber: 0x%x\n", geo.b_max_contex_id_number); ++ printf("bSysDataTagUnitSize: 0x%x\n", geo.b_sys_data_tag_unit_size); ++ printf("bSysDataTagResSize: 0x%x\n", geo.b_sys_data_tag_res_size); ++ printf("bSupportedSecRTypes: 0x%x\n", geo.b_supported_sec_rtypes); ++ printf("wSupportedMemoryTypes: 0x%x\n", to_bigendian16(geo.w_supported_memory_types)); ++ printf("dSystemCodeMaxNAllocU: 0x%x\n", to_bigendian32(geo.d_system_code_max_alloc_u)); ++ printf("wSystemCodeCapAdjFac: 0x%x\n", to_bigendian16(geo.w_system_code_cap_adj_fac)); ++ printf("dNonPersistMaxNAllocU: 0x%x\n", to_bigendian32(geo.d_non_persist_max_alloc_u)); ++ printf("wNonPersistCapAdjFac: 0x%x\n", to_bigendian16(geo.w_non_persist_cap_adj_fac)); ++ printf("dEnhanced1MaxNAllocU: 0x%x\n", to_bigendian32(geo.d_enhanced1_max_alloc_u)); ++ printf("wEnhanced1CapAdjFac: 0x%x\n", to_bigendian16(geo.w_enhanced1_cap_adj_fac)); ++ printf("dEnhanced2MaxNAllocU: 0x%x\n", to_bigendian32(geo.d_enhanced2_max_alloc_u)); ++ printf("wEnhanced2CapAdjFac: 0x%x\n", to_bigendian16(geo.w_enhanced2_cap_adj_fac)); ++ printf("dEnhanced3MaxNAllocU: 0x%x\n", to_bigendian32(geo.d_enhanced3_max_alloc_u)); ++ printf("wEnhanced3CapAdjFac: 0x%x\n", to_bigendian16(geo.w_enhanced3_cap_adj_fac)); ++ printf("dEnhanced4MaxNAllocU: 0x%x\n", to_bigendian32(geo.d_enhanced4_max_alloc_u)); ++ printf("wEnhanced4CapAdjFac: 0x%x\n", to_bigendian16(geo.w_enhanced4_cap_adj_fac)); ++ printf("dOptimalLogicalBlockSize: 0x%x\n", to_bigendian32(geo.d_optimal_logical_block_size)); ++} ++ ++static void ufs_info_show_unit_desc(void) ++{ ++ struct ufs_unit_index_descriptror unit; ++ struct ufs_unit_rpmb_descriptror rpmb; ++ int i; ++ ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ unit = g_ufs_desc.unit_desc.unit_index_desc[i]; ++ ++ printf("----------------------------\n"); ++ printf("---UFS Unit %d Descriptor---\n", i); ++ printf("----------------------------\n"); ++ printf("bLength: 0x%x\n", unit.b_length); ++ printf("bDescriptorIDN: 0x%x\n", unit.b_descriptor_idn); ++ printf("bUnitIndex: 0x%x\n", unit.b_unit_index); ++ printf("bLUEnable: 0x%x\n", unit.b_lu_enable); ++ printf("bBootLunID: 0x%x\n", unit.b_boot_lun_id); ++ printf("bLUWriteProtect: 0x%x\n", unit.b_lu_write_protect); ++ printf("bLUQueueDepth: 0x%x\n", unit.b_lu_queue_depth); ++ printf("bPSASensitive: 0x%x\n", unit.b_psa_sensitive); ++ printf("bMemoryType: 0x%x\n", unit.b_memory_type); ++ printf("bDataReliability: 0x%x\n", unit.b_data_reliability); ++ printf("bLogicalBlockSize: 0x%x\n", unit.b_logical_block_size); ++ printf("qLogicalBlockCount: 0x%llx\n", cpu_to_be64(unit.q_logical_block_count)); ++ printf("dEraseBlockSize: 0x%x\n", to_bigendian32(unit.d_erase_block_size)); ++ printf("bProvisioningType: 0x%x\n", unit.b_provisioning_type); ++ printf("qPhyMemResourceCount: 0x%x\n", to_bigendian32(unit.q_phy_mem_resource_count)); ++ printf("wContextCapabilities: 0x%x\n", to_bigendian16(unit.w_context_capabilities)); ++ printf("bLargeUnitGranularity_M1: 0x%x\n", unit.b_large_unit_granularity_m1); ++ } ++ ++ rpmb = g_ufs_desc.unit_desc.unit_rpmb_desc; ++ ++ printf("------------------------------\n"); ++ printf("---UFS Unit RPMB Descriptor---\n"); ++ printf("------------------------------\n"); ++ printf("bLength: 0x%x\n", rpmb.b_length); ++ printf("bDescriptorIDN: 0x%x\n", rpmb.b_descriptor_idn); ++ printf("bUnitIndex: 0x%x\n", rpmb.b_unit_index); ++ printf("bLUEnable: 0x%x\n", rpmb.b_lu_enable); ++ printf("bBootLunID: 0x%x\n", rpmb.b_boot_lun_id); ++ printf("bLUWriteProtect: 0x%x\n", rpmb.b_lu_write_protect); ++ printf("bLUQueueDepth: 0x%x\n", rpmb.b_lu_queue_depth); ++ printf("bPSASensitive: 0x%x\n", rpmb.b_psa_sensitive); ++ printf("bMemoryType: 0x%x\n", rpmb.b_memory_type); ++ printf("bLogicalBlockSize: 0x%x\n", rpmb.b_logical_block_size); ++ printf("qLogicalBlockCount: 0x%llx\n", cpu_to_be64(rpmb.q_logical_block_count)); ++ printf("dEraseBlockSize: 0x%x\n", to_bigendian32(rpmb.d_erase_block_size)); ++ printf("bProvisioningType: 0x%x\n", rpmb.b_provisioning_type); ++ printf("qPhyMemResourceCount: 0x%x\n", to_bigendian32(rpmb.q_phy_mem_resource_count)); ++} ++ ++static void ufs_info_show_conf_desc(void) ++{ ++ struct ufs_dev_desc_configuration_param dev; ++ struct ufs_unit_desc_configuration_param unit; ++ int i; ++ ++ dev = g_ufs_desc.conf_desc.dev_desc_conf_param; ++ ++ printf("----------------------------------------\n"); ++ printf("---UFS Device Descriptor Config Param---\n"); ++ printf("----------------------------------------\n"); ++ printf("bLength: 0x%x\n", dev.b_length); ++ printf("bDescriptorIDN: 0x%x\n", dev.b_descriptor_idn); ++ printf("bConfDescContinue: 0x%x\n", dev.b_conf_desc_continue); ++ printf("bBootEnable: 0x%x\n", dev.b_boot_enable); ++ printf("bDescrAccessEn: 0x%x\n", dev.b_descr_access_en); ++ printf("bInitPowerMode: 0x%x\n", dev.b_init_power_mode); ++ printf("bHighPriorityLUN: 0x%x\n", dev.b_high_priority_lun); ++ printf("bSecureRemovalType: 0x%x\n", dev.b_secure_removal_type); ++ printf("bInitActiveICCLevel: 0x%x\n", dev.b_init_active_icc_level); ++ printf("wPeriodicRTCUpdate: 0x%x\n", to_bigendian16(dev.w_periodic_rtc_update)); ++ ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ unit = g_ufs_desc.conf_desc.unit_desc_conf_param[i]; ++ ++ printf("-----------------------------------------\n"); ++ printf("---UFS Unit %d Descriptor Config Param---\n", i); ++ printf("-----------------------------------------\n"); ++ printf("bLUEnable: 0x%x\n", unit.b_lu_enable); ++ printf("bBootLunID: 0x%x\n", unit.b_boot_lun_id); ++ printf("bLUWriteProtect: 0x%x\n", unit.b_lu_write_protect); ++ printf("bMemoryType: 0x%x\n", unit.b_memory_type); ++ printf("dNumAllocUnits: 0x%x\n", to_bigendian32(unit.d_num_alloc_units)); ++ printf("bDataReliability: 0x%x\n", unit.b_data_reliability); ++ printf("bLogicalBlockSize: 0x%x\n", unit.b_logical_block_size); ++ printf("bProvisioningType: 0x%x\n", unit.b_provisioning_type); ++ printf("wContextCapabilities: 0x%x\n", to_bigendian16(unit.w_context_capabilities)); ++ } ++} ++ ++static void ufs_info_show_str_desc(void) ++{ ++ struct ufs_string_descriptor str; ++ ++ str = g_ufs_desc.str_desc; ++ ++ printf("---------------------------\n"); ++ printf("---UFS String Descriptor---\n"); ++ printf("---------------------------\n"); ++ printf("Manufacturer Name: %s\n", str.manufacturer_name); ++ printf("Product Name: %s\n", str.product_name); ++ printf("Serial Number: %s\n", str.serial_number); ++ printf("Oem ID: %s\n", str.oem_id); ++} ++ ++static void ufs_info_show_heal_desc(void) ++{ ++ struct ufs_health_descriptor heal; ++ ++ heal = g_ufs_desc.heal_desc; ++ ++ printf("---------------------------\n"); ++ printf("---UFS Health Descriptor---\n"); ++ printf("---------------------------\n"); ++ printf("bLength: 0x%x\n", heal.b_length); ++ printf("bDescriptorIDN: 0x%x\n", heal.b_descriptor_idn); ++ printf("bPreEOLInfo: 0x%x\n", heal.b_pre_eol_info); ++ printf("bDeviceLifeTimeEstA: 0x%x\n", heal.b_device_life_time_est_a); ++ printf("bDeviceLifeTimeEstB: 0x%x\n", heal.b_device_life_time_est_b); ++} ++ ++static void ufs_info_show_intr_desc(void) ++{ ++ struct ufs_interconnect_descriptor intr; ++ ++ intr = g_ufs_desc.intr_desc; ++ ++ printf("---------------------------------\n"); ++ printf("---UFS Interconnect Descriptor---\n"); ++ printf("---------------------------------\n"); ++ printf("bLength: 0x%x\n", intr.b_length); ++ printf("bDescriptorIDN: 0x%x\n", intr.b_descriptor_idn); ++ printf("bcdUniproVersion: 0x%x\n", to_bigendian16(intr.bcd_unipro_version)); ++ printf("bcdMphyVersion: 0x%x\n", to_bigendian16(intr.bcd_mphy_version)); ++} ++ ++static void ufs_info_show_all(void) ++{ ++ ufs_info_show_portion(); ++ ufs_info_show_dev_desc(); ++ ufs_info_show_conf_desc(); ++ ufs_info_show_unit_desc(); ++ ufs_info_show_intr_desc(); ++ ufs_info_show_str_desc(); ++ ufs_info_show_geo_desc(); ++ ufs_info_show_heal_desc(); ++} ++ ++static int ufs_desc_init(void) ++{ ++ int ret; ++ ++ if (g_ufs_desc.desc_is_init == 1) ++ return UFS_OK; ++ ++ ret = ufs_read_descriptor((void *)(&g_ufs_desc.conf_desc), ++ CONFIGURATION_DESC, 0, CONFIGURATION_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("read configuration descriptor fail\n"); ++ return UFS_ERR; ++ } ++ ++ ret = ufs_read_unit_descriptor(); ++ if (ret != UFS_OK) { ++ printf("read unit descriptor fail\n"); ++ return UFS_ERR; ++ } ++ ++ ret = ufs_read_descriptor((void *)(&g_ufs_desc.intr_desc), ++ INTERCONNECT_DESC, 0, INTERCONNECT_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("read interconnect descriptor fail\n"); ++ return UFS_ERR; ++ } ++ ++ ret = ufs_read_descriptor((void *)(&g_ufs_desc.heal_desc), ++ HEALTH_DESC, 0, HEALTH_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("read health descriptor fail\n"); ++ return UFS_ERR; ++ } ++ ++ g_ufs_desc.desc_is_init = 1; ++ ++ return UFS_OK; ++} ++ ++int ufs_show_desc_info(enum info_show_type type) ++{ ++ int ret; ++ ++ ret = ufs_desc_init(); ++ if (ret != UFS_OK) ++ return ret; ++ ++ switch (type) { ++ case UFS_INFO_SHOW_BASIC: ++ ufs_info_show_portion(); ++ break; ++ case UFS_INFO_SHOW_ALL: ++ ufs_info_show_all(); ++ break; ++ case UFS_INFO_SHOW_DEVICE_DESC: ++ ufs_info_show_dev_desc(); ++ break; ++ case UFS_INFO_SHOW_CONFIGURATION_DESC: ++ ufs_info_show_conf_desc(); ++ break; ++ case UFS_INFO_SHOW_UNIT_DESC: ++ ufs_info_show_unit_desc(); ++ break; ++ case UFS_INFO_SHOW_INTERCONNECT_DESC: ++ ufs_info_show_intr_desc(); ++ break; ++ case UFS_INFO_SHOW_STRING_DESC: ++ ufs_info_show_str_desc(); ++ break; ++ case UFS_INFO_SHOW_GEOMETRY_DESC: ++ ufs_info_show_geo_desc(); ++ break; ++ case UFS_INFO_SHOW_HEALTH_DESC: ++ ufs_info_show_heal_desc(); ++ break; ++ default: ++ printf("unknown cmd\n"); ++ break; ++ } ++ ++ return UFS_OK; ++} ++ ++static void ufs_info_init(void) ++{ ++ uint32_t lba; ++ int ret; ++ ++ g_ufs_info.blocksize = LOGICAL_BLK_SIZE; ++ g_ufs_info.block_read = ufs_read_storage; ++ g_ufs_info.block_write = ufs_write_storage; ++ g_ufs_info.boot_block_write = ufs_write_boot_storage; ++ ++ ret = ufs_read_capacity(&lba); ++ if (ret != UFS_SUCCESS) ++ return; ++ ++ g_ufs_info.capacity = (uint64_t)lba * LOGICAL_BLK_SIZE; ++} ++ ++struct ufs *get_ufs_info(void) ++{ ++ return &g_ufs_info; ++} ++ ++void ufshci_dump(void) ++{ ++ printf("===== UFSHCI REGISTER DUMP =====\n"); ++ printf("CAP: 0x%08x||VER: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_CAP_OFF), ++ dwc_ufs_read_reg(UFS_VER_OFF)); ++ printf("HCPID: 0x%08x||HCMID: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_HCPID_OFF), ++ dwc_ufs_read_reg(UFS_HCMID_OFF)); ++ printf("AHIT: 0x%08x||IS: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_AHIT_OFF), ++ dwc_ufs_read_reg(UFS_IS_OFF)); ++ printf("IE: 0x%08x||HCS: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_IE_OFF), ++ dwc_ufs_read_reg(UFS_HCS_OFF)); ++ printf("HCE: 0x%08x||UECPA: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_HCE_OFF), ++ dwc_ufs_read_reg(UFS_UECPA_OFF)); ++ printf("UECDL: 0x%08x||UECN: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UECDL_OFF), ++ dwc_ufs_read_reg(UFS_UECN_OFF)); ++ printf("UECT: 0x%08x||UECDME: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UECT_OFF), ++ dwc_ufs_read_reg(UFS_UECDME_OFF)); ++ printf("UTRIACR: 0x%08x||UTRLBA: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UTRIACR_OFF), ++ dwc_ufs_read_reg(UFS_UTRLBA_OFF)); ++ printf("UTRLBAU: 0x%08x||UTRLDBR: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UTRLBAU_OFF), ++ dwc_ufs_read_reg(UFS_UTRLDBR_OFF)); ++ printf("UTRLCLR: 0x%08x||UTRLRSR: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UTRLCLR_OFF), ++ dwc_ufs_read_reg(UFS_UTRLRSR_OFF)); ++ printf("UTMRLBA: 0x%08x||UTMRLBAU: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UTMRLBA_OFF), ++ dwc_ufs_read_reg(UFS_UTMRLBAU_OFF)); ++ printf("UTMRLDBR: 0x%08x||UTMRLCLR: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UTMRLDBR_OFF), ++ dwc_ufs_read_reg(UFS_UTMRLCLR_OFF)); ++ printf("UTMRLRSR: 0x%08x\n", ++ dwc_ufs_read_reg(UFS_UTMRLRSR_OFF)); ++ printf("================================\n"); ++} ++ ++void ufs_reg_dump(void) ++{ ++ ufshci_dump(); ++} ++ ++static uint8_t dwc_ufshcd_get_xfer_req_free_slot(struct dwc_ufs_hba *hba) ++{ ++ uint8_t slot; ++ ++ for (slot = 0; slot < hba->nutrs; slot++) { ++ if ((hba->outstanding_xfer_reqs & BIT(slot)) == 0) ++ return slot; ++ } ++ ++ printf("get xfer free_slot fail\n"); ++ return BAD_SLOT; ++} ++ ++uint32_t uic_cmd_read(uint32_t command, uint32_t arg1) ++{ ++ int retry; ++ uint32_t reg; ++ ++ retry = 100; /* retry 100 times */ ++ while (--retry) { ++ if (dwc_ufs_read_reg(UFS_HCS_OFF) & UFS_HCS_UCRDY_BIT) ++ break; ++ ufs_waitms(1); ++ } ++ if (retry <= 0) { ++ printf("%s: wait HCS.UCRDY timeout\n", __func__); ++ ufshci_dump(); ++ } ++ ++ dwc_ufs_write_reg(UFS_IS_OFF, 0xFFFFFFFF); ++ dwc_ufs_write_reg(UFS_UICCMDARG1_OFF, arg1); ++ dwc_ufs_write_reg(UFS_UICCMDARG2_OFF, 0x0); ++ dwc_ufs_write_reg(UFS_UICCMDARG3_OFF, 0x0); ++ ++ dwc_ufs_write_reg(UFS_UICCMD_OFF, (command & 0xFF)); ++ ++ retry = 100; /* retry 100 times */ ++ while (--retry) { ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UCCS_BIT) ++ break; ++ ufs_waitms(1); ++ } ++ ++ if (retry <= 0) { ++ printf("%s: timeout, cmd:0x%x, arg1:0x%x\n", ++ __func__, command, arg1); ++ ufshci_dump(); ++ } ++ ++ /* clear interrupt status */ ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_UCCS_BIT); ++ ++ if (dwc_ufs_read_reg(UFS_UICCMDARG2_OFF) & 0xFF) { ++ printf("%s:response error\n", __func__); ++ ufshci_dump(); ++ } ++ ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UE_BIT) { ++ printf("%s:UFS_IS_UE_BIT error\n", __func__); ++ /* clear interrupt status */ ++ /* the UE error cause by PA_Init or some other reason, ++ * should clear it, or it will repeatly come out ! */ ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_UE_BIT); ++ ufs_waitms(1); ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UE_BIT) ++ printf("%s:can not clear the UE_BIT\n", ++ __func__); ++ } ++ ++ /* get uic result */ ++ reg = dwc_ufs_read_reg(UFS_UICCMDARG3_OFF); ++ ++ return reg; ++} ++ ++/*************************************************************** ++ * send_uic_command ++ * Description: Programs the Command Argument and the Command ++ * Register to send the DME_LINK_STARTUP command ++ * to the device ++ * ++ ***************************************************************/ ++void send_uic_command(uint32_t command, uint32_t arg1, uint32_t arg2, uint32_t arg3) ++{ ++ int retry; ++ int val; ++ ++ retry = 100; /* retry 100 times */ ++ while (--retry) { ++ if (dwc_ufs_read_reg(UFS_HCS_OFF) & UFS_HCS_UCRDY_BIT) ++ break; ++ ufs_waitms(1); ++ } ++ if (retry <= 0) { ++ printf("%s: wait HCS.UCRDY timeout\n", __func__); ++ ufshci_dump(); ++ } ++ ++ dwc_ufs_write_reg(UFS_IS_OFF, 0xFFFFFFFF); ++ dwc_ufs_write_reg(UFS_UICCMDARG1_OFF, arg1); ++ dwc_ufs_write_reg(UFS_UICCMDARG2_OFF, arg2); ++ dwc_ufs_write_reg(UFS_UICCMDARG3_OFF, arg3); ++ ++ dwc_ufs_write_reg(UFS_UICCMD_OFF, (command & 0xFF)); ++ ++ retry = 500; /* retry 500 times */ ++ while (--retry) { ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UCCS_BIT) ++ break; ++ ufs_waitms(1); ++ } ++ ++ if (retry <= 0) ++ printf("%s: timeout cmd:0x%x, arg1:0x%x, " ++ "arg2:0x%x, arg3:0x%x\n", __func__, ++ command, arg1, arg2, arg3); ++ ++ /* clear interrupt status */ ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_UCCS_BIT); ++ ++ val = dwc_ufs_read_reg(UFS_UICCMDARG2_OFF); ++ if (val & 0xFF) ++ printf("%s: response error, cmd:0x%x, arg1 is 0x%x, " ++ "response is 0x%x\n", __func__, command, arg1, val); ++ ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UE_BIT) { ++ printf("%s: UFS_IS_UE_BIT error, cmd:0x%x, arg1 is 0x%x\n", ++ __func__, command, arg1); ++ /* the UE error cause by PA_Init or some other reason, ++ * should clear it, or it will repeatly come out ! */ ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_UE_BIT); ++ ufs_waitms(1); ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UE_BIT) ++ printf("%s:can not clear the UE_BIT\n", ++ __func__); ++ } ++} ++ ++/*************************************************************** ++ * setup_snps_mphy_tc ++ * Description: Programs the Unipro and Synopsys Mphy for ++ * ++ ***************************************************************/ ++void setup_snps_mphy_tc(void) ++{ ++ uint32_t retry = 10; /* retry 10 times */ ++ ++ /* Read the DME_Resest (0xD010) attribute. It must return 0 ++ indicating that the reset sequence is completed. */ ++ do { ++ if (uic_cmd_read(0x01, 0xD0100000) == 1) ++ break; ++ ufs_waitms(1); ++ } while (retry--); ++ /* DME layer enable */ ++ send_uic_command(DME_SET, 0xd0000000, 0x0, 0x01); ++} ++ ++static int ufs_hc_enable(void) ++{ ++ int retry = 3; /* retry 3 times */ ++ int timeout; ++ ++ do { ++ dwc_ufs_write_reg(UFS_HCE_OFF, UFS_HCE_RESET_BIT); ++ timeout = 3; /* 3ms timeout */ ++ for (;;) { ++ if (dwc_ufs_read_reg(UFS_HCE_OFF) & UFS_HCE_RESET_BIT) ++ return UFS_SUCCESS; ++ ufs_waitms(1); ++ ++ if (--timeout == 0) { ++ printf("wait HCE time out\n"); ++ break; ++ } ++ } ++ } while (--retry > 0); ++ ++ return UFS_FAILURE; ++} ++ ++static void ufs_hardware_init(void) ++{ ++ uint32_t reg; ++ ++ reg = readl(CRG_REG_BASE + PERI_CRG96); ++ reg |= BIT_UFS_CLK_EN; ++ writel(reg, CRG_REG_BASE + PERI_CRG96); ++ ++ reg = readl(CRG_REG_BASE + PERI_CRG96); ++ reg |= BIT_UFS_SRST_REQ; ++ writel(reg, CRG_REG_BASE + PERI_CRG96); ++ ++ udelay(1); ++ ++ reg = readl(CRG_REG_BASE + PERI_CRG96); ++ reg &= ~BIT_UFS_SRST_REQ; ++ writel(reg, CRG_REG_BASE + PERI_CRG96); ++ ++ udelay(1); ++ ++ reg = readl(CRG_REG_BASE + PERI_CRG96); ++ reg |= BIT_UFS_AXI_SRST_REQ; ++ writel(reg, CRG_REG_BASE + PERI_CRG96); ++ ++ udelay(1); ++ ++ reg = readl(CRG_REG_BASE + PERI_CRG96); ++ reg &= ~BIT_UFS_AXI_SRST_REQ; ++ writel(reg, CRG_REG_BASE + PERI_CRG96); ++ ++ reg = readl(MISC_REG_BASE + MISC_CTRL17); ++ reg |= BIT_UFS_ENABLE; ++ writel(reg, MISC_REG_BASE + MISC_CTRL17); ++ ++ reg = readl(MISC_REG_BASE + MISC_CTRL17); ++ reg &= ~BIT_DA_UFS_REFCLK_OEN; ++ reg &= ~MASK_DA_UFS_REFCLK_DS; ++ reg |= (BIT_DA_UFS_REFCLK_DS0 | ++ BIT_DA_UFS_REFCLK_DS1 | ++ BIT_DA_UFS_REFCLK_SL); ++ writel(reg, MISC_REG_BASE + MISC_CTRL17); ++ ++ reg = readl(MISC_REG_BASE + MISC_CTRL17); ++ reg &= ~BIT_DA_UFS_RESET_OEN; ++ reg &= ~MASK_DA_UFS_RESET_DS; ++ reg |= BIT_DA_UFS_RESET_SL; ++ writel(reg, MISC_REG_BASE + MISC_CTRL17); ++ ++ reg = readl(MISC_REG_BASE + MISC_CTRL17); ++ reg &= ~BIT_UFS_PAD_RESET; ++ writel(reg, MISC_REG_BASE + MISC_CTRL17); ++ ++ udelay(10); /* delay 10 us */ ++ ++ reg = readl(MISC_REG_BASE + MISC_CTRL17); ++ reg |= BIT_UFS_PAD_RESET; ++ writel(reg, MISC_REG_BASE + MISC_CTRL17); ++} ++ ++static void phy_init_config(void) ++{ ++#if defined(COMBO_PHY_V120) ++ /* Rx SKP_DET_SEL, lane0 */ ++ send_uic_command(DME_SET, attr_mrx0(SKP_DET_SEL), 0x0, SKP_DET_SEL_EN); ++ /* Rx SKP_DET_SEL, lane1 */ ++ send_uic_command(DME_SET, attr_mrx1(SKP_DET_SEL), 0x0, SKP_DET_SEL_EN); ++ ++ /* VCO_AUTO_CHG */ ++ send_uic_command(DME_SET, attr_mcb(VCO_AUTO_CHG), 0x0, (VCO_AUTO_CHG_EN | VCO_FORCE_ON_EN)); ++ /* RX_SQ_VREF, lane0 */ ++ send_uic_command(DME_SET, attr_mrx0(RX_SQ_VREF), 0x0, RX_SQ_VREF_175); ++ /* RX_SQ_VREF, lane1 */ ++ send_uic_command(DME_SET, attr_mrx1(RX_SQ_VREF), 0x0, RX_SQ_VREF_175); ++ ++ /* Dif_N debouse */ ++ send_uic_command(DME_SET, attr_mrx0(0xeb), 0x0, 0x64); ++ /* Dif_N debouse */ ++ send_uic_command(DME_SET, attr_mrx1(0xeb), 0x0, 0x64); ++ ++ /* dvalid timer */ ++ send_uic_command(DME_SET, attr_mrx0(0x0e), 0x0, 0xF0); ++ /* dvalid timer */ ++ send_uic_command(DME_SET, attr_mrx1(0x0e), 0x0, 0xF0); ++ ++ /* AD_DIF_P_LS_TIMEOUT_VAL, lane0 */ ++ send_uic_command(DME_SET, attr_mrx0(AD_DIF_P_LS_TIMEOUT_VAL), 0x0, PWM_PREPARE_TO); ++ /* AD_DIF_P_LS_TIMEOUT_VAL, lane1 */ ++ send_uic_command(DME_SET, attr_mrx1(AD_DIF_P_LS_TIMEOUT_VAL), 0x0, PWM_PREPARE_TO); ++ ++ send_uic_command(DME_SET, 0x00F40004, 0x0, 0x1); /* RX_EQ_SEL_R */ ++ send_uic_command(DME_SET, 0x00F40005, 0x0, 0x1); /* RX_EQ_SEL_R */ ++ ++ send_uic_command(DME_SET, 0x00F20004, 0x0, 0x3); /* RX_EQ_SEL_C */ ++ send_uic_command(DME_SET, 0x00F20005, 0x0, 0x3); /* RX_EQ_SEL_C */ ++ ++ send_uic_command(DME_SET, 0x00FB0004, 0x0, 0x3); /* RX_VSEL */ ++ send_uic_command(DME_SET, 0x00FB0005, 0x0, 0x3); /* RX_VSEL */ ++ ++ send_uic_command(DME_SET, 0x00f60004, 0x0, 0x2); /* RX_DLF Lane 0 */ ++ send_uic_command(DME_SET, 0x00f60005, 0x0, 0x2); /* RX_DLF Lane 1 */ ++ ++ send_uic_command(DME_SET, 0x000a0004, 0x0, 0x2); /* RX_TIMEOUT_VAL, Lane 0 */ ++ send_uic_command(DME_SET, 0x000a0005, 0x0, 0x2); /* RX_TIMEOUT_VAL, Lane 1 */ ++ ++ /* in low temperature to solve the PLL's starting of oscillation */ ++ send_uic_command(DME_SET, 0x00d40000, 0x0, 0x31); /* RG_PLL_DMY0 */ ++ send_uic_command(DME_SET, 0x00730000, 0x0, 0x4); /* TX_PHY_CONFIG II */ ++ send_uic_command(DME_SET, 0x00730001, 0x0, 0x4); /* TX_PHY_CONFIG II */ ++#endif /* end of COMBO_PHY_V120 */ ++} ++ ++static void ufs_hc_init(void) ++{ ++ uint32_t reg; ++ ++ /* get the 1us tick clock, the HCLK is 266Mhz? */ ++ dwc_ufs_write_reg(UFS_HCLKDIV_OFF, UFS_HCLKDIV_NORMAL_VALUE); ++ ++ phy_init_config(); ++ ++ send_uic_command(DME_SET, attr_mrx0(MRX_EN), 0x0, MRX_ENABLE); /* RX enable, lane0 */ ++ send_uic_command(DME_SET, attr_mrx1(MRX_EN), 0x0, MRX_ENABLE); /* RX enable, lane1 */ ++ ++ /* disable auto */ ++ reg = dwc_ufs_read_reg(UFS_AHIT_OFF); ++ reg = reg & (~UFS_AHIT_AH8ITV_MASK); ++ dwc_ufs_write_reg(UFS_AHIT_OFF, reg); ++ ++ setup_snps_mphy_tc(); ++ /* disable Vswing change */ ++ /* measure the power, can close it */ ++ send_uic_command(DME_SET, 0x00C70000, 0x0, 0x3); ++ send_uic_command(DME_SET, 0x00C80000, 0x0, 0x3); ++ ++#ifdef CLOSE_CLK_GATING ++ send_uic_command(DME_SET, 0x00cf0004, 0x0, 0x02); /* RX_STALL */ ++ send_uic_command(DME_SET, 0x00cf0005, 0x0, 0x02); ++ send_uic_command(DME_SET, 0x00d00004, 0x0, 0x02); /* RX_SLEEP */ ++ send_uic_command(DME_SET, 0x00d00005, 0x0, 0x02); ++ send_uic_command(DME_SET, 0x00cc0004, 0x0, 0x03); /* RX_HS_CLK_EN */ ++ send_uic_command(DME_SET, 0x00cc0005, 0x0, 0x03); ++ send_uic_command(DME_SET, 0x00cd0004, 0x0, 0x03); /* RX_LS_CLK_EN */ ++ send_uic_command(DME_SET, 0x00cd0005, 0x0, 0x03); ++#endif ++ ++#if defined(COMBO_PHY_V120) ++ send_uic_command(DME_SET, 0x00c50000, 0x0, 0x03); /* RG_PLL_RXHS_EN */ ++ send_uic_command(DME_SET, 0x00c60000, 0x0, 0x03); ++ send_uic_command(DME_SET, 0x00E90004, 0x0, 0x00); /* RX_HS_DATA_VALID_TIMER_VAL0 */ ++ send_uic_command(DME_SET, 0x00E90005, 0x0, 0x00); ++ send_uic_command(DME_SET, 0x00EA0004, 0x0, 0x10); /* RX_HS_DATA_VALID_TIMER_VAL1 */ ++ send_uic_command(DME_SET, 0x00EA0005, 0x0, 0x10); ++#endif ++ /* set the HS-prepare length and sync length to MAX value, try to solve ++ the data check error problem, the device seems not receive the write ++ cmd. */ ++ /* PA_TxHsG1SyncLength , can not set MPHY's register directly */ ++ send_uic_command(DME_SET, 0x15520000, 0x0, 0x4F); ++ /* PA_TxHsG2SyncLength , can not set MPHY's register directly */ ++ send_uic_command(DME_SET, 0x15540000, 0x0, 0x4F); ++ /* PA_TxHsG3SyncLength , can not set MPHY's register directly */ ++ send_uic_command(DME_SET, 0x15560000, 0x0, 0x4F); ++ ++ send_uic_command(DME_SET, 0x00ca0000, 0x0, 0x3); /* pll always on */ ++ send_uic_command(DME_SET, 0xD0850000, 0x0, 0x1); /* update */ ++ ++ /* to check if the unipro have to close the LCC */ ++ /* Unipro PA_Local_TX_LCC_Enable */ ++ send_uic_command(DME_SET, 0x155E0000, 0x0, 0x0); ++ /* close Unipro VS_Mk2ExtnSupport */ ++ send_uic_command(DME_SET, 0xD0AB0000, 0x0, 0x0); ++ ++ if (uic_cmd_read(DME_GET, 0xD0AB0000) != 0) ++ printf("Warring!!! close VS_Mk2ExtnSupport failed\n"); ++} ++ ++int ufs_link_startup(void) ++{ ++ int retry = 4; /* retry 4 times */ ++ int i; ++ ++ dwc_ufs_write_reg(UFS_IS_OFF, 0xFFFFFFFF); ++ while (retry-- > 0) { ++ dwc_ufs_write_reg(UFS_UICCMDARG1_OFF, 0); ++ dwc_ufs_write_reg(UFS_UICCMDARG2_OFF, 0); ++ dwc_ufs_write_reg(UFS_UICCMDARG3_OFF, 0); ++ ++ dwc_ufs_write_reg(UFS_UICCMD_OFF, (UIC_LINK_STARTUP_CMD & 0xFF)); ++ ++ i = 0; ++ for (;;) { ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UCCS_BIT) { ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_UCCS_BIT); ++ break; ++ } ++ ufs_waitms(1); ++ if (i++ > 200) { /* 200ms timeout */ ++ printf("ufs link startup wait UCCS timeout\n"); ++ break; ++ } ++ } ++ ++ if (dwc_ufs_read_reg(UFS_HCS_OFF) & UFS_HCS_DP_BIT) { ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_ULSS_BIT) ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_ULSS_BIT); ++ ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_UE_BIT); ++ return UFS_SUCCESS; ++ } ++ ++ printf("ufs link startup check DP fail\n"); ++ if (retry <= 0) { ++ printf("ufs link startup fail\n"); ++ return UFS_LINK_STARTUP_FAIL; ++ } ++ i = 0; ++ for (;;) { ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & ++ UFS_IS_ULSS_BIT) ++ break; ++ ufs_waitms(1); ++ if (i++ > 50) { /* 50ms timeout */ ++ printf("ufs link startup wait ULSS timeout\n"); ++ break; ++ } ++ } ++ } ++ ++ printf("ufs link startup fail\n"); ++ return UFS_LINK_STARTUP_FAIL; ++} ++ ++static int update_snum_buff(uint8_t *snum_buf, int sunm_buf_len, ++ uint8_t *resp_buf, int resp_buf_len) ++{ ++ uint8_t i; ++ ++ get_local_dwc_host(); ++ ++ (void)sunm_buf_len; ++ (void)resp_buf_len; ++ ++ if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_TOSHIBA) { ++ /* ++ * toshiba: 20 Byte, every two byte has a prefix of 0x00, ++ * trim the 0x00 bytes ++ */ ++ for (i = 0; i < SERIAL_NUM_SIZE - 2; i++) /* exclude last 2 */ ++ snum_buf[i] = resp_buf[2 * i + 1]; /* 2 byte unicode */ ++ /* append two 0x00 byte in the end */ ++ snum_buf[SERIAL_NUM_SIZE - 2] = 0; /* end 2 */ ++ snum_buf[SERIAL_NUM_SIZE - 1] = 0; /* end 1 */ ++ } else if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_SAMSUNG) { ++ /* ++ * Samsung new ufs device, need 24 Bytes for serial ++ * number, transfer unicode to 12 bytes ++ */ ++ for (i = 0; i < SERIAL_NUM_SIZE; i++) ++ snum_buf[i] = resp_buf[i * 2 + 1]; /* 2 byte unicode */ ++ } else if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_HYNIX) { ++ /* hynix only have 6 Byte, add a 0x00 before every byte */ ++ for (i = 0; i * 2 < SERIAL_NUM_SIZE; i++) { /* 2 byte unicode */ ++ snum_buf[i * 2] = 0x0; /* 2 byte unicode */ ++ snum_buf[i * 2 + 1] = resp_buf[i]; /* 2 byte unicode */ ++ } ++ } else if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_HI1861) { ++ for (i = 0; i < SERIAL_NUM_SIZE; i++) ++ snum_buf[i] = resp_buf[i]; ++ } else if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_MICRON) { ++ /* Micron only need 4 Byte */ ++ for (i = 0; i < 4; i++) ++ snum_buf[i] = resp_buf[i]; ++ for (i = 4; i < SERIAL_NUM_SIZE; i++) /* 4 byte */ ++ snum_buf[i] = 0; ++ } else if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_SANDISK) { ++ /* ++ * Sandisk need 24 Bytes for serial number, ++ * transfer unicode to 12 bytes ++ */ ++ for (i = 0; i < SERIAL_NUM_SIZE; i++) ++ snum_buf[i] = resp_buf[i * 2 + 1]; /* 2 byte unicode */ ++ } else { ++ printf("%s: unknown manufacturer_id (0x%x)\n", ++ __func__, dwc_host->manufacturer_id); ++ return -1; ++ } ++ return 0; ++} ++ ++unsigned int *ufs_pack_cid(void) ++{ ++ u8 resp_upiu[STRING_DESC_LENGTH]; /* use the slot 0 default */ ++ u8 snum_buf[SERIAL_NUM_SIZE] = {0}; ++ u8 resp_buf[2 * SERIAL_NUM_SIZE + 2]; /* 2 byte unicode */ ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ ret = ufs_read_descriptor((void *)resp_upiu, STRING_DESC, ++ g_ufs_desc.dev_desc.i_serial_number, STRING_DESC_LENGTH); ++ if (ret != UFS_SUCCESS) { ++ printf("%s: read_descriptor fail. ret = %d\n", __func__, ret); ++ return NULL; ++ } ++ /* 32B query response head + 2B string descriptor head */ ++ ret = memcpy_s((void *)resp_buf, 2 * SERIAL_NUM_SIZE + 2, (void *)(resp_upiu + 2), SERIAL_NUM_SIZE * 2); /* 2 byte unicode */ ++ if (ret != UFS_OK) { ++ printf("memcpy_s fail. ret = %d\n", ret); ++ return NULL; ++ } ++ ++ /* clear and init ufs_cid value */ ++ g_ufs_cid[0] = 0; /* dw 0 */ ++ g_ufs_cid[1] = 0; /* dw 1 */ ++ g_ufs_cid[2] = 0; /* dw 2 */ ++ ++ ret = update_snum_buff(snum_buf, SERIAL_NUM_SIZE, (u8 *)resp_buf, ++ 2 * SERIAL_NUM_SIZE + 2); /* 2 byte unicode */ ++ if (ret != 0) ++ return NULL; ++ ++ ret = memcpy_s(g_ufs_cid, UFS_CID_SIZE * sizeof(unsigned int), (unsigned int *)snum_buf, (unsigned int)sizeof(snum_buf)); ++ if (ret != UFS_OK) { ++ printf("memcpy_s fail. ret = %d\n", ret); ++ return NULL; ++ } ++ /* dw 3, date 31 - 16, id 15 - 0 */ ++ g_ufs_cid[3] = ((uint32_t)(dwc_host->manufacturer_date) << 16) | dwc_host->manufacturer_id; ++ ++ return g_ufs_cid; ++} ++ ++/** ++ * Number of UTP Transfer Request Slots (NUTRS) ++ * Number of UTP Task management Request Slots(NUTMRS) ++ * Auto Hibernate Support (AUTOH8) ++ */ ++static void dwc_ufshcd_read_caps(struct dwc_ufs_hba *hba) ++{ ++ hba->caps = dwc_ufs_read_reg(UFS_CAP_OFF); ++ ++ hba->nutrs = (uint8_t)(hba->caps & DWC_UFS_NUTRS_MASK) + 1; ++ hba->nutmrs = (uint8_t) ++ ((hba->caps & DWC_UFS_NUTMRS_MASK) >> DWC_UFS_NUTMRS_SHIFT) + 1; ++ hba->autoh8 = (uint8_t) ++ ((hba->caps & DWC_UFS_AUTOH8) >> DWC_UFS_AUTOH8_SHIFT); ++} ++ ++static void ufshcd_memory_align(struct dwc_ufs_hba *hba) ++{ ++ uint32_t utrl_size, utmrl_size, ucdl_size; ++ uint64_t cur_pool = (uint64_t)(uintptr_t)hba->mem_pool; ++ ++ utrl_size = sizeof(struct dwc_ufs_utrd) * hba->nutrs; ++ utmrl_size = sizeof(struct dwc_ufs_utmrd) * hba->nutmrs; ++ ucdl_size = sizeof(struct dwc_ufs_ucd) * hba->nutrs; ++ ++ /* Allocate Dma'able memory for UTP Transfer Request List ++ * UFS spec constraints: Base of List should be aligned to 1024 byte ++ * (1K boundary) ++ */ ++ cur_pool = bytes_align_1024(cur_pool); ++ hba->utrl_base_addr = (struct dwc_ufs_utrd *)(uintptr_t)cur_pool; ++ ++ ufs_pr_mem("utrl start:0x%llx end:0x%llx sz:0x%x\n", ++ cur_pool, cur_pool + utrl_size, utrl_size); ++ ++ /* Allocate Dma'able memory for UTP Task management Request List ++ * UFS spec constraints: Base of list should be aligned to 1024 byte ++ * (1K boundary) ++ */ ++ cur_pool += utrl_size; ++ cur_pool = bytes_align_1024(cur_pool); ++ hba->utmrl_base_addr = (struct dwc_ufs_utmrd *)(uintptr_t)cur_pool; ++ ++ ufs_pr_mem("utmrl start:0x%llx end:0x%llx sz:0x%x\n", ++ cur_pool, cur_pool + utmrl_size, utmrl_size); ++ ++ /* Allocate Dma'able memory for UTP Command Descriptor List ++ * Every Command Descriptor block should be aligned to 128 byte ++ */ ++ cur_pool += utmrl_size; ++ cur_pool = bytes_align_128(cur_pool); ++ hba->ucdl_base_addr = (struct dwc_ufs_ucd *)(uintptr_t)cur_pool; ++ ++ ufs_pr_mem("ucdl start:0x%llx end:0x%llx sz:0x%x\n", ++ cur_pool, cur_pool + ucdl_size, ucdl_size); ++ ++ /* Allocate memory for local reference block */ ++ cur_pool += ucdl_size; ++ hba->lrb = (struct dwc_ufs_hcd_lrb *)(uintptr_t)cur_pool; ++ ++ ufs_pr_mem("lrb start:0x%llx sz:0x%x\n", ++ cur_pool, sizeof(struct dwc_ufs_hcd_lrb) * hba->nutrs); ++} ++ ++/** ++ * Allocate memory for Host controller interface. ++ * Following are the memories allocation by this function. ++ * - DMA'able memory for UTP transfer request descriptor list ++ * - DMA'able memory for UTP task management request list ++ * - DMA'able memory for command table ++ * - Command UPIU's ++ * - Response UPIU's ++ * - PRD tables ++ * - Non-DMA'able memory for local reference blocks; House keeping ++ * @hba: Pointer to private structure ++ * ++ * Returns 0 for success, non-zero in case of failure ++ */ ++static int dwc_ufshcd_alloc_interface_memory(struct dwc_ufs_hba *hba) ++{ ++ uint32_t utrl_size, utmrl_size, ucdl_size, lrb_size, total_size; ++ int ret; ++ ++ utrl_size = sizeof(struct dwc_ufs_utrd) * hba->nutrs; ++ utmrl_size = sizeof(struct dwc_ufs_utmrd) * hba->nutmrs; ++ ucdl_size = sizeof(struct dwc_ufs_ucd) * hba->nutrs; ++ lrb_size = sizeof(struct dwc_ufs_hcd_lrb) * hba->nutrs; ++ ++ total_size = utrl_size + DWC_UTRL_BASE_ALIGN + ++ utmrl_size + DWC_UTRL_BASE_ALIGN + ++ ucdl_size + DWC_CMD_BASE_ALIGN + lrb_size; ++ ++ hba->mem_pool = malloc(total_size); ++ if (hba->mem_pool == NULL) { ++ printf("%s: Memory Allocation Failed\n", __func__); ++ return UFS_ERR; ++ } ++ ret = memset_s(hba->mem_pool, total_size, 0, total_size); ++ if (ret != UFS_OK) { ++ printf("memset_s fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ ufs_pr_mem("\n@@ ufs memory pool info @@\n"); ++ ufs_pr_mem("poll start:0x%llx sz:0x%x\n", ++ (uint64_t)(uintptr_t)hba->mem_pool, total_size); ++ ufshcd_memory_align(hba); ++ ++ /* Allocate memory for wr_buf, LOGICAL_BLK_SIZE aligned */ ++ hba->wr_buf = memalign(LOGICAL_BLK_SIZE, LOGICAL_BLK_SIZE); ++ if (hba->wr_buf == NULL) { ++ printf("%s: Write Read Memory Allocation Failed\n", ++ __func__); ++ goto err_alloc; ++ } ++ ufs_pr_mem("wrbuf start:0x%llx end:0x%llx sz:0x%x\n", ++ (uint64_t)(uintptr_t)hba->wr_buf, ++ (uint64_t)(uintptr_t)hba->wr_buf + LOGICAL_BLK_SIZE, ++ LOGICAL_BLK_SIZE); ++ ++ return UFS_OK; ++ ++err_alloc: ++ if (hba->mem_pool) ++ free(hba->mem_pool); ++ hba->mem_pool = NULL; ++ ++ return UFS_ERR; ++} ++ ++/** ++ * This function configures interface memory ++ * - For every UTRD, ++ * - initializes the Command UPIU base address (Lo and High) ++ * - response upiu length and offset ++ * - prdt offset ++ * - Some key fields are updated in respective lrbs ++ * - utrd addresses ++ * - command upiu addresses ++ * - response upiu addresses ++ * - prdt base address ++ * @hba: Pointer to private structure ++ * ++ * Returns void ++ */ ++static void dwc_ufshcd_configure_interface_memory(const struct dwc_ufs_hba *hba) ++{ ++ uint32_t i; ++ struct dwc_ufs_utrd *utrl; /* Pointer to UTR List */ ++ struct dwc_ufs_ucd *ucdl; /* Pointer to UCD List */ ++ uint64_t ucdl_dma_addr; ++ uint64_t ucd_dma_addr; ++ ++ utrl = hba->utrl_base_addr; ++ ucdl = hba->ucdl_base_addr; ++ ucdl_dma_addr = (uint64_t)(uintptr_t)hba->ucdl_base_addr; /* UCD list Base address */ ++ ++ /* For as many UTP Transfer Requests in the list */ ++ for (i = 0; i < hba->nutrs; i++) { ++ /* Configure UTRD with UCD base address */ ++ ucd_dma_addr = ucdl_dma_addr + (uint64_t)sizeof(struct dwc_ufs_ucd) * i; ++ utrl[i].ucdba = (lower_32_bits(ucd_dma_addr)); ++ utrl[i].ucdbau = (upper_32_bits(ucd_dma_addr)); ++ ++ /* Configure Response UPIU offset and length */ ++ /* These fields are in Dword format */ ++ utrl[i].resp_upiu_offset = to_littleendian16((uint16_t) ++ ((u32)offsetof(struct dwc_ufs_ucd, resp_upiu) >> UFS_DWORD_SHIFT)); ++ utrl[i].resp_upiu_length = to_littleendian16((uint16_t) ++ (DWC_UCD_ALIGN >> UFS_DWORD_SHIFT)); ++ /* Configure prdt length and offset */ ++ utrl[i].prdt_offset = to_littleendian16((uint16_t) ++ ((u32)offsetof(struct dwc_ufs_ucd, prdt) >> UFS_DWORD_SHIFT)); ++ utrl[i].prdt_length = to_littleendian16(0); ++ ++ /* Update LRB */ ++ hba->lrb[i].utrd = (utrl + i); ++ hba->lrb[i].cmd_upiu = (struct dwc_ufs_cmd_upiu *)(ucdl + i); ++ hba->lrb[i].resp_upiu = (struct dwc_ufs_resp_upiu *)(ucdl[i].resp_upiu); ++ hba->lrb[i].prdt = (struct dwc_ufs_prd *)(ucdl[i].prdt); ++ } ++} ++ ++/** ++ * Get UFS controller state ++ * @hba: Private structure pointer ++ * ++ * Returns TRUE if controller is active, FALSE otherwise ++ */ ++static int dwc_ufshcd_is_hba_active(void) ++{ ++ return (dwc_ufs_read_reg(UFS_HCE_OFF) & 0x1) ? UFS_OK : UFS_ERR; ++} ++ ++/** ++ * This function performs the initialization of DWC UFS HC descriptors ++ * with memory base addresses ++ * Before updating the descriptor addresses, it checks host controller is ++ * enabled. If not returns error. If enabled, both transfer descriptor ++ * pointers and tm descriptor pointers are programmed from the drivers ++ * private structure ++ * @hba: pointer to drivers private structure ++ * ++ * Returns void ++ */ ++static void dwc_ufshcd_initialize_hba_desc_pointers(struct dwc_ufs_hba *hba) ++{ ++ /* If the Host Controller is not active, return error */ ++ if (dwc_ufshcd_is_hba_active()) { ++ printf(" not active , error\n"); ++ return; ++ } ++ ++ /* Configure UTRL and UTMRL base address registers */ ++ dwc_ufs_write_reg(UFS_UTRLBA_OFF, ++ lower_32_bits((uintptr_t)hba->utrl_base_addr)); ++ dwc_ufs_write_reg(UFS_UTRLBAU_OFF, ++ upper_32_bits((uintptr_t)hba->utrl_base_addr)); ++ ++ dwc_ufs_write_reg(UFS_UTMRLBA_OFF, ++ lower_32_bits((uintptr_t)hba->utmrl_base_addr)); ++ dwc_ufs_write_reg(UFS_UTMRLBAU_OFF, ++ upper_32_bits((uintptr_t)hba->utmrl_base_addr)); ++} ++ ++static void create_prdt_part(struct cmd_param *param, uint32_t buf_size, uint8_t free_slot) ++{ ++ int i; ++ uint32_t size = param->size; ++ struct dwc_ufs_prd *prdt = NULL; ++ ++ get_local_dwc_host(); ++ ++ prdt = dwc_host->lrb[free_slot].prdt; ++ /* Fill PRD Table Info */ ++ for (i = 0; (size); i++) { ++ prdt[i].base_addr = (lower_32_bits(param->addr + (i * buf_size))); ++ prdt[i].upper_addr = (upper_32_bits(param->addr + (i * buf_size))); ++ prdt[i].reserved1 = 0x0; ++ prdt[i].size = to_littleendian32(((buf_size < size) ? buf_size : size) - 1); ++ size -= (buf_size < size) ? buf_size : size; ++ } ++} ++ ++static void create_cmd_part(uint32_t opcode, uint8_t upiu_flags, ++ struct cmd_param *param, uint8_t free_slot) ++{ ++ struct dwc_ufs_cmd_upiu *cmd_upiu_ptr = NULL; ++ struct dwc_ufs_ucd *ucd = NULL; ++ ++ get_local_dwc_host(); ++ ++ ucd = dwc_host->ucdl_base_addr; ++ ucd += free_slot; ++ cmd_upiu_ptr = (struct dwc_ufs_cmd_upiu *)ucd->cmd_upiu; ++ ++ cmd_upiu_ptr->trans_type = 0x01; ++ cmd_upiu_ptr->flags = upiu_flags; ++ cmd_upiu_ptr->lun = dwc_host->active_lun; ++ cmd_upiu_ptr->task_tag = free_slot; ++ cmd_upiu_ptr->cmd_set_type = 0x0; ++ cmd_upiu_ptr->reserved_1_0 = 0x0; ++ cmd_upiu_ptr->reserved_1_1 = 0x0; ++ cmd_upiu_ptr->reserved_1_2 = 0x0; ++ cmd_upiu_ptr->tot_ehs_len = 0x0; ++ cmd_upiu_ptr->reserved_2 = 0x0; ++ cmd_upiu_ptr->data_seg_len = 0x0; ++ cmd_upiu_ptr->exp_data_xfer_len = to_bigendian32(param->size); ++ ++ if (opcode == UFS_OP_READ_10 || opcode == UFS_OP_WRITE_10 || ++ opcode == UFS_OP_SYNCHRONIZE_CACHE_10) { ++ get_cmnd(opcode, (uint32_t)(param->src / LOGICAL_BLK_SIZE), ++ (param->size / LOGICAL_BLK_SIZE), cmd_upiu_ptr->cdb); ++ } else { ++ get_cmnd(opcode, 0, param->size, cmd_upiu_ptr->cdb); ++ } ++ ++ /* In Power Management Command: START STOP UNIT, the LUN is fixed ++ to 0x50 or 0xD0 for W-LUN */ ++ if (opcode == UFS_OP_START_STOP_UNIT && ((cmd_upiu_ptr->cdb[0x4] & 0xF0) != 0)) ++ cmd_upiu_ptr->lun = 0xD0; ++} ++ ++/*************************************************************** ++ * create_cmd_upiu ++ * Description: Fills the Command UPIU memory, ++ * updates prdt entries ++ * free_slot:free slot in memory, 0~31 in UTRL, 0~7 in UTMRL ++ * ++ ***************************************************************/ ++static int create_cmd_upiu(uint32_t opcode, enum dma_data_direction direction, ++ struct cmd_param *param, uint8_t free_slot) ++{ ++ uint32_t data_direction; ++ uint8_t upiu_flags; ++ struct dwc_ufs_utrd *utrd = NULL; ++ struct dwc_ufs_ucd *ucd = NULL; ++ uint32_t buf_size = PRDT_BUFFER_SIZE; ++ ++ get_local_dwc_host(); ++ ++ if (opcode == UFS_OP_READ_10 || opcode == UFS_OP_WRITE_10 || ++ opcode == UFS_OP_SYNCHRONIZE_CACHE_10) { ++ if ((param->src % LOGICAL_BLK_SIZE) || (param->size % LOGICAL_BLK_SIZE)) { ++ printf("!!!!!access ufs is not round with ufs blocksize," ++ "src is 0x%llx, size is 0x%x, blocksize is 0x%x\n", ++ param->src, param->size, LOGICAL_BLK_SIZE); ++ return UFS_SOFTWARE_ERROR; ++ } ++ } ++ if (opcode == UFS_OP_SECURITY_PROTOCOL_IN || opcode == UFS_OP_SECURITY_PROTOCOL_OUT) ++ buf_size = RPMB_FRAME_SIZE; ++ ++ /* Get the ucd of the free slot */ ++ ucd = dwc_host->ucdl_base_addr; ++ ucd += free_slot; ++ /* Get the xfer descriptor of the free slot */ ++ utrd = dwc_host->utrl_base_addr; ++ utrd += free_slot; ++ ++ if (direction == DMA_FROM_DEVICE) { ++ data_direction = UTP_DEVICE_TO_HOST; ++ upiu_flags = UPIU_CMD_FLAGS_READ; ++ } else if (direction == DMA_TO_DEVICE) { ++ data_direction = UTP_HOST_TO_DEVICE; ++ upiu_flags = UPIU_CMD_FLAGS_WRITE; ++ } else { ++ data_direction = UTP_NO_DATA_TRANSFER; ++ upiu_flags = UPIU_CMD_FLAGS_NONE; ++ } ++ ++ /* Update cmd_type, flags and response upiu length */ ++ utrd->ct_and_flags = (uint8_t)(data_direction | UTP_UFS_STORAGE_COMMAND); ++ utrd->resp_upiu_length = to_littleendian16((uint16_t) ++ (sizeof(struct dwc_ufs_resp_upiu) >> UFS_DWORD_SHIFT)); ++ utrd->prdt_length = to_littleendian16((uint16_t)((param->size & (buf_size - 1)) ? ++ ((param->size / buf_size) + 1) : (param->size / buf_size))); ++ utrd->ocs = 0xf; ++ ++ create_cmd_part(opcode, upiu_flags, param, free_slot); ++ create_prdt_part(param, buf_size, free_slot); ++ ++ /* occupy this slot */ ++ dwc_host->outstanding_xfer_reqs |= BIT(free_slot); ++ ++ return UFS_SUCCESS; ++} ++ ++/**************************************************************** ++ * ufs_soft_init ++ * Description: alloc memory for dwc_host ++ * ++ ****************************************************************/ ++static int ufs_soft_init(void) ++{ ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ ret = memset_s((void *)dwc_host, sizeof(struct dwc_ufs_hba), 0, sizeof(struct dwc_ufs_hba)); ++ if (ret != UFS_OK) { ++ printf("memset_s fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ /* Read host Controller Capabilities */ ++ dwc_ufshcd_read_caps(dwc_host); ++ ++ /* Allocate memory required to interface with host */ ++ ret = dwc_ufshcd_alloc_interface_memory(dwc_host); ++ if (ret != UFS_OK) { ++ printf("%s: allocating required memory error\n", __func__); ++ return ret; ++ } ++ ++ /* Configure the HC memory with required information and the LRB */ ++ dwc_ufshcd_configure_interface_memory(dwc_host); ++ ++ return UFS_OK; ++} ++ ++static void ufs_config_init(void) ++{ ++ int i; ++ uint32_t value; ++ ++ get_local_dwc_host(); ++ ++ /* Unipro DL_AFC0CreditThreshold */ ++ send_uic_command(DME_SET, 0x20440000, 0, 0x0); ++ /* Unipro DL_TC0OutAckThreshold */ ++ send_uic_command(DME_SET, 0x20450000, 0, 0x0); ++ /* Unipro DL_TC0TXFCThreshold */ ++ send_uic_command(DME_SET, 0x20400000, 0, 0x9); ++ ++#ifdef UFS_USE_VENDOR_MPHY_TC ++#if defined(COMBO_PHY_V120) ++ /* PA_TActivate */ ++ send_uic_command(DME_SET, 0x15a80000, 0x0, 0xa); ++ /* RX_TIMEOUT_VAL, Lane 0 */ ++ send_uic_command(DME_SET, 0x000a0004, 0x0, 0x1E); ++ /* RX_TIMEOUT_VAL, Lane 1 */ ++ send_uic_command(DME_SET, 0x000a0005, 0x0, 0x1E); ++#endif ++ ++ /* If the PLL is slow and needs more than 10 us ++ * then this field can be used to specify the wait period unit. If ++ * the value of this field */ ++ value = uic_cmd_read(DME_GET, 0xd0a00000); /* VS_DebugSaveConfigTime */ ++ value &= (uint32_t)(~(0x7 << 2)); /* bit[4:2] = 0 */ ++ value |= 0x3 << 2; /* bit[4:2] = 3 */ ++ /* enlarge for default's 10us to 31250ns, ++ can make rateA->B change smoothly */ ++ send_uic_command(DME_SET, 0xd0a00000, 0x0, value); ++#endif ++ ++ /* the ufs register will be reset, so set the ufs register always */ ++ dwc_ufshcd_initialize_hba_desc_pointers(dwc_host); ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) ++ dwc_host->lu_request_sense_sent[i] = 0; ++ ++ dwc_ufs_write_reg(UFS_UTRLRSR_OFF, UFS_UTP_RUN_BIT); ++ dwc_ufs_write_reg(UFS_UTMRLRSR_OFF, UFS_UTP_RUN_BIT); ++} ++ ++/*************************************************************** ++ * read_nop_rsp ++ * Description: The function reads and validates the response ++ * received for NOP command ++ * ++ ***************************************************************/ ++static int read_nop_rsp(uint8_t free_slot) ++{ ++ struct dwc_ufs_nop_resp_upiu *resp_upiu = NULL; ++ struct dwc_ufs_utrd *utrd = NULL; ++ ++ get_local_dwc_host(); ++ ++ resp_upiu = (struct dwc_ufs_nop_resp_upiu *)dwc_host->lrb[free_slot].resp_upiu; ++ utrd = dwc_host->lrb[free_slot].utrd; ++ ++ if (utrd->ocs != UFS_SUCCESS) { ++ printf("send nop out ocs err\n"); ++ ufs_reg_dump(); ++ return -((utrd->ocs & 0xf) + RET_UTRD_OCS_ERROR_OFF); ++ } ++ ++ if ((resp_upiu->trans_type & 0x3F) != NOP_TRANS_TYPE) { ++ printf("invalid nop in trans_type 0x%x\n", (resp_upiu->trans_type & 0x3F)); ++ ufs_reg_dump(); ++ return UFS_INVALID_NOP_IN; ++ } ++ ++ if (resp_upiu->response != UFS_SUCCESS) { ++ printf("nop in response error, resp = 0x%x\n", resp_upiu->response); ++ return UFS_NOP_RESP_FAIL; ++ } ++ ++ return UFS_SUCCESS; ++} ++ ++/*************************************************************** ++ * wait_for_cmd_completion ++ * Description: Sets the DoorBell Register and waits for the ++ * Doorbell to Clear. Returns Zero on Success or ++ * error number on Failure ++ * ++ ***************************************************************/ ++int wait_for_cmd_completion(uint32_t slot_mask) ++{ ++ int retry = 5000; /* 5000ms timeout */ ++ ++ get_local_dwc_host(); ++ ++ flush_dcache_all(); ++ /* Set the doorbell for processing the request */ ++ dwc_ufs_write_reg(UFS_UTRLDBR_OFF, slot_mask); ++ ++ /* Wait for the DoorBell to clear */ ++ for (;;) { ++ if ((dwc_ufs_read_reg(UFS_UTRLDBR_OFF) & slot_mask) == 0) ++ break; ++ if (--retry == 0) { ++ printf("UTRL DoorBell Not Cleared and Timed Out, " ++ "DoorBell is 0x%x, slot_mask is 0x%x\n", ++ dwc_ufs_read_reg(UFS_UTRLDBR_OFF), slot_mask); ++ ufs_reg_dump(); ++ ++ dwc_ufs_write_reg(UFS_UTRLCLR_OFF, 0); ++ dwc_host->outstanding_xfer_reqs = 0; ++ ++ return UFS_UTRD_DOORBELL_TIMEOUT; ++ } ++ ufs_waitms(1); ++ } ++ invalidate_dcache_all(); ++ return UFS_SUCCESS; ++} ++ ++/*************************************************************** ++ * create_nop_out_upiu ++ * Description: Fills the UPIU memory for NOP OUT command ++ * ++ ***************************************************************/ ++static void create_nop_out_upiu(uint8_t free_slot) ++{ ++ struct dwc_ufs_nop_req_upiu *cmd_upiu_ptr = NULL; ++ struct dwc_ufs_nop_resp_upiu *resp_upiu = NULL; ++ int i; ++ int ret; ++ struct dwc_ufs_utrd *utrd = NULL; ++ ++ get_local_dwc_host(); ++ ++ cmd_upiu_ptr = (struct dwc_ufs_nop_req_upiu *)dwc_host->lrb[free_slot].cmd_upiu; ++ ++ utrd = dwc_host->lrb[free_slot].utrd; ++ ++ utrd->ct_and_flags = (uint8_t)(UTP_NO_DATA_TRANSFER | UTP_UFS_STORAGE_COMMAND); ++ utrd->resp_upiu_length = to_littleendian16((uint16_t) ++ (sizeof(struct dwc_ufs_nop_resp_upiu) >> UFS_DWORD_SHIFT)); ++ utrd->prdt_length = 0; ++ utrd->ocs = 0xf; ++ ++ cmd_upiu_ptr->trans_type = 0x00; ++ cmd_upiu_ptr->flags = 0x00; ++ cmd_upiu_ptr->reserved_1 = 0x00; ++ cmd_upiu_ptr->task_tag = free_slot; ++ cmd_upiu_ptr->reserved_2 = 0x0; ++ cmd_upiu_ptr->tot_ehs_len = 0x00; ++ cmd_upiu_ptr->reserved_3 = 0x00; ++ cmd_upiu_ptr->data_seg_len = 0x00; ++ for (i = 0; i < 20; i++) /* reserved: 20 bytes */ ++ cmd_upiu_ptr->reserved_4[i] = 0x00; ++ ++ resp_upiu = (struct dwc_ufs_nop_resp_upiu *)dwc_host->lrb[free_slot].resp_upiu; ++ ret = memset_s((void *)resp_upiu, sizeof(struct dwc_ufs_nop_resp_upiu), 0, sizeof(struct dwc_ufs_nop_resp_upiu)); ++ if (ret != UFS_OK) { ++ printf("memset_s fail. ret = %d\n", ret); ++ return; ++ } ++} ++ ++static int send_nop_out_cmd(void) ++{ ++ int ret; ++ int i; ++ uint8_t free_slot; ++ ++ get_local_dwc_host(); ++ for (i = 0; i < 5; i++) { /* retry 5 times */ ++ free_slot = dwc_ufshcd_get_xfer_req_free_slot(dwc_host); ++ if (free_slot == BAD_SLOT) ++ return UFS_ERR; ++ ++ create_nop_out_upiu(free_slot); ++ ret = wait_for_cmd_completion(BIT(free_slot)); ++ if (ret == UFS_SUCCESS) ++ ret = read_nop_rsp(free_slot); ++ ++ if (ret == UFS_SUCCESS) ++ break; ++ ++ printf("nop out nop in fail, ret = %d, retry = %d\n", ret, i); ++ ufs_waitms(10); /* delay 10ms */ ++ } ++ ++ return ret; ++} ++ ++/*************************************************************** ++ * handle_query_response ++ * Description: The functon does the following ++ * 1. validates the query command's response received ++ * 2. updates the ret argument with query data ++ * 3. returns the status ++ * ++ ***************************************************************/ ++static int handle_query_response(uint8_t *ret, uint8_t free_slot) ++{ ++ struct dwc_ufs_query_upiu *resp_upiu = NULL; ++ ++ get_local_dwc_host(); ++ ++ resp_upiu = (struct dwc_ufs_query_upiu *)dwc_host->lrb[free_slot].resp_upiu; ++ ++ /* Update the return value */ ++ if (ret != NULL) { ++ ret[0] = resp_upiu->tsf[11]; /* val 0: tsf 11 */ ++ ret[1] = resp_upiu->tsf[10]; /* val 1: tsf 10 */ ++ ret[2] = resp_upiu->tsf[9]; /* val 2: tsf 9 */ ++ ret[3] = resp_upiu->tsf[8]; /* val 3: tsf 8 */ ++ } ++ ++ if (resp_upiu->query_resp == UFS_SUCCESS) ++ return UFS_SUCCESS; ++ ++ printf("Query Response error: 0x%x\n", resp_upiu->query_resp); ++ return -(resp_upiu->query_resp); ++} ++ ++/*************************************************************** ++ * create_query_upiu ++ * Description: Populates the UPIU memory for all query Operations ++ * ++ ***************************************************************/ ++static void create_query_upiu(uint8_t query_func, struct query_param *param, ++ const uint8_t *val, uint8_t free_slot) ++{ ++ struct dwc_ufs_query_upiu *cmd_upiu_ptr = NULL; ++ struct dwc_ufs_utrd *utrd = NULL; ++ int i; ++ ++ get_local_dwc_host(); ++ cmd_upiu_ptr = (struct dwc_ufs_query_upiu *)dwc_host->lrb[free_slot].cmd_upiu; ++ ++ utrd = dwc_host->lrb[free_slot].utrd; ++ ++ /* UTRD Descriptor Programming for processing command */ ++ utrd->ct_and_flags = (uint8_t) ++ (UTP_NO_DATA_TRANSFER | UTP_UFS_STORAGE_COMMAND); ++ utrd->resp_upiu_length = to_littleendian16((uint16_t) ++ (sizeof(struct dwc_ufs_query_upiu) >> UFS_DWORD_SHIFT)); ++ utrd->prdt_length = 0; ++ ++ /* Command Descriptor Programming */ ++ cmd_upiu_ptr->trans_type = 0x16; ++ cmd_upiu_ptr->flags = UPIU_CMD_FLAGS_NONE; ++ cmd_upiu_ptr->reserved_1 = 0x00; ++ cmd_upiu_ptr->task_tag = free_slot; ++ cmd_upiu_ptr->reserved_2 = 0x00; ++ cmd_upiu_ptr->query_func = query_func; ++ cmd_upiu_ptr->query_resp = 0x00; ++ cmd_upiu_ptr->reserved_3 = 0x00; ++ cmd_upiu_ptr->tot_ehs_len = 0x00; ++ cmd_upiu_ptr->data_seg_len = 0x00; ++ ++ for (i = 0; i < 16; i++) /* clear 16 byte */ ++ cmd_upiu_ptr->tsf[i] = 0x0; ++ cmd_upiu_ptr->tsf[0] = param->opcode; /* 0: opcode */ ++ cmd_upiu_ptr->tsf[1] = param->idn; /* 1: idn */ ++ cmd_upiu_ptr->tsf[2] = param->index; /* 2: index */ ++ cmd_upiu_ptr->tsf[3] = param->selector; /* 3: selector */ ++ /* Value/Flag Updation */ ++ cmd_upiu_ptr->tsf[8] = val[3]; /* 8: val 3 */ ++ cmd_upiu_ptr->tsf[9] = val[2]; /* 9: val 2 */ ++ cmd_upiu_ptr->tsf[10] = val[1]; /* 10: val 1 */ ++ cmd_upiu_ptr->tsf[11] = val[0]; /* 11: val 0 */ ++ cmd_upiu_ptr->reserved_5 = 0x0; ++} ++ ++/*************************************************************** ++ * send_scsi_cmd ++ * Description: function creates UPIU for scsi commands sends it ++ * to the device by setting the DoorBell. ++ * ++ ****************************************************************/ ++static int send_scsi_cmd(uint32_t opcode, enum dma_data_direction direction, ++ uint64_t buf_addr, uint64_t rel_addr, uint32_t size) ++{ ++ int ret; ++ uint8_t free_slot; ++ struct cmd_param param = {buf_addr, rel_addr, size}; ++ ++ get_local_dwc_host(); ++ if (lower_32_bits(buf_addr) % 0x4) { ++ printf("fail! buf_addr:0x%llx is not round to D-Words\n", buf_addr); ++ return UFS_SOFTWARE_ERROR; ++ } ++ if (size % 0x4) { ++ printf("fail! size:0x%x is not round to D-Words\n", size); ++ return UFS_SOFTWARE_ERROR; ++ } ++ if (size / PRDT_BUFFER_SIZE > DWC_UFSHCD_MAX_PRD_SIZE) { ++ printf("fail! size:0x%x is bigger than PRDT alloc\n", size); ++ return UFS_SOFTWARE_ERROR; ++ } ++ ++ free_slot = dwc_ufshcd_get_xfer_req_free_slot(dwc_host); ++ if (free_slot == BAD_SLOT) ++ return UFS_ERR; ++ ++ ret = create_cmd_upiu(opcode, direction, ¶m, free_slot); ++ if (ret != UFS_SUCCESS) { ++ printf("create_cmd_upiu fail! ret %d\n", ret); ++ return ret; ++ } ++ ++ ret = wait_for_cmd_completion(BIT(free_slot)); ++ return ret; ++} ++ ++static int handle_response_status(struct dwc_ufs_resp_upiu *resp_upiu) ++{ ++ uint8_t status; ++ int ret; ++ ++ status = resp_upiu->status; ++ if (status == SAM_STAT_GOOD) { ++ /* do nothing */ ++ ret = 0; ++ } else if (status == SAM_STAT_CHECK_CONDITION) { ++ /* 0x20 is added to sense key to return ++ * unique error code */ ++ ret = -(RET_SENSE_KEY_OFF + ++ (resp_upiu->sense_data[2] & 0xf)); /* byte 2: sense key */ ++ /* because other slot to check, so not ++ * return here */ ++ printf("The sense key is 0x%x, ASC is 0x%x, ASCQ is 0x%x\n", ++ ((uint8_t *)resp_upiu)[SENSE_KEY_INDEX] & 0xf, ++ ((uint8_t *)resp_upiu)[SENSE_KEY_INDEX + 0xa], ++ ((uint8_t *)resp_upiu)[SENSE_KEY_INDEX + 0xb]); ++ } else { ++ switch (status) { ++ case SAM_STAT_CONDITION_MET: ++ ret = RESP_STAT_CONDITION_MET; ++ break; ++ case SAM_STAT_BUSY: ++ ret = RESP_STAT_BUSY; ++ break; ++ case SAM_STAT_RESERVATION_CONFLICT: ++ ret = RESP_STAT_RESERVATION_CONFLICT; ++ break; ++ case SAM_STAT_TASK_SET_FULL: ++ ret = RESP_STAT_TASK_SET_FULL; ++ break; ++ case SAM_STAT_ACA_ACTIVE: ++ ret = RESP_STAT_ACA_ACTIVE; ++ break; ++ case SAM_STAT_TASK_ABORTED: ++ ret = RESP_STAT_TASK_ABORTED; ++ break; ++ default: ++ ret = RESP_STAT_UNKNOWN; ++ } ++ } ++ ++ return ret; ++} ++ ++/*************************************************************** ++ * handle_scsi_completion ++ * Description: The functon validates the SCSI command's ++ * response received ++ * ++ ***************************************************************/ ++static int handle_scsi_completion(void) ++{ ++ struct dwc_ufs_resp_upiu *resp_upiu = NULL; ++ struct dwc_ufs_utrd *utrd = NULL; ++ uint8_t slot_index; ++ int i; ++ int ret = 0; ++ ++ get_local_dwc_host(); ++ for (i = 0; i < dwc_host->nutrs; i++) { ++ slot_index = (uint8_t)i; ++ /* the outstanding is set on, so dump the response, ++ and then clear the bit */ ++ if (dwc_host->outstanding_xfer_reqs & BIT(slot_index)) { ++ resp_upiu = dwc_host->lrb[slot_index].resp_upiu; ++ utrd = dwc_host->lrb[slot_index].utrd; ++ ++ /* finish the requset here */ ++ dwc_host->outstanding_xfer_reqs ^= BIT(slot_index); ++ ++ if (utrd->ocs == UFS_SUCCESS) { ++ ret = handle_response_status(resp_upiu); ++ } else { ++ ret = -((utrd->ocs & 0xf) + RET_UTRD_OCS_ERROR_OFF); ++ ufs_reg_dump(); ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++static int do_scsi_cmd(uint32_t opcode, enum dma_data_direction direction, ++ uint64_t buf_addr, uint64_t rel_addr, uint32_t size) ++{ ++ int ret; ++ ++ ret = send_scsi_cmd(opcode, direction, buf_addr, rel_addr, size); ++ if (ret != UFS_SUCCESS) { ++ printf("send scsi cmd[0x%x] error: %d\n", opcode, ret); ++ return ret; ++ } ++ ++ ret = handle_scsi_completion(); ++ if (ret != UFS_SUCCESS) { ++ printf("handle scsi cmd[0x%x] completion error: %d\n", ++ opcode, ret); ++ return ret; ++ } ++ return ret; ++} ++ ++/*************************************************************** ++ * create_desc_upiu ++ * Description: The function does a simple memcpy of the req_upiu ++ * content passed to UPIU memory for Read/Write ++ * descriptor Operation ++ * ++ ***************************************************************/ ++static void create_desc_upiu(const void *req_upiu, uint8_t free_slot) ++{ ++ unsigned int len; ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ len = (unsigned int)(sizeof(struct dwc_ufs_query_upiu) + ++ to_bigendian16(((struct dwc_ufs_query_upiu *)req_upiu)->data_seg_len)); ++ ++ if (dwc_host->lrb[free_slot].cmd_upiu == req_upiu) ++ return; ++ ret = memcpy_s(dwc_host->lrb[free_slot].cmd_upiu, len, req_upiu, len); ++ if (ret != UFS_OK) { ++ printf("memcpy_s fail. ret = %d\n", ret); ++ return; ++ } ++} ++ ++/*************************************************************** ++ * write_descriptor ++ * Description: The functon populates the request upiu structure ++ * with upiu info passed in 1st argument ++ * ++ ***************************************************************/ ++int write_descriptor(const void *req_upiu) ++{ ++ int ret; ++ struct dwc_ufs_utrd *utrd = NULL; ++ uint8_t free_slot; ++ ++ get_local_dwc_host(); ++ ++ free_slot = dwc_ufshcd_get_xfer_req_free_slot(dwc_host); ++ if (free_slot == BAD_SLOT) ++ return UFS_ERR; ++ utrd = dwc_host->lrb[free_slot].utrd; ++ ++ /* UTRD Descriptor Programming for processing command */ ++ utrd->ct_and_flags = (uint8_t) ++ (UTP_NO_DATA_TRANSFER | UTP_UFS_STORAGE_COMMAND); ++ utrd->resp_upiu_length = to_littleendian16((uint16_t) ++ (sizeof(struct dwc_ufs_query_upiu) >> UFS_DWORD_SHIFT)); ++ utrd->prdt_length = 0; ++ ++ create_desc_upiu(req_upiu, free_slot); ++ ++ ret = wait_for_cmd_completion(BIT(free_slot)); ++ if (ret != UFS_SUCCESS) ++ return ret; ++ ++ ret = handle_query_response(NULL, free_slot); ++ ++ return ret; ++} ++ ++/*************************************************************** ++ * read_attribute ++ * Description: The functon sends the query command to read an ++ * attribute and updates the ret_value with the ++ * content of the attribute ++ * ++ ***************************************************************/ ++int read_attribute(enum attr_id idn, uint8_t index, uint8_t selector, uint32_t *ret_value) ++{ ++ int ret; ++ uint8_t free_slot; ++ struct query_param param = {READ_ATTR_OPCODE, idn, index, selector}; ++ ++ get_local_dwc_host(); ++ free_slot = dwc_ufshcd_get_xfer_req_free_slot(dwc_host); ++ if (free_slot == BAD_SLOT) ++ return UFS_ERR; ++ *ret_value = 0; ++ ++ create_query_upiu(STANDARD_RD_REQ, ¶m, (uint8_t *)ret_value, free_slot); ++ ret = wait_for_cmd_completion(BIT(free_slot)); ++ if (ret != UFS_SUCCESS) ++ return ret; ++ ++ ret = handle_query_response((uint8_t *)ret_value, free_slot); ++ return ret; ++} ++ ++/*************************************************************** ++ * write_attribute ++ * Description: The functon sends the query command to write an ++ * attribute with the value passed as second argument ++ * ++ ***************************************************************/ ++int write_attribute(enum attr_id idn, uint8_t index, uint8_t selector, uint32_t *value) ++{ ++ int ret; ++ uint8_t free_slot; ++ struct query_param param = {WRITE_ATTR_OPCODE, idn, index, selector}; ++ ++ get_local_dwc_host(); ++ free_slot = dwc_ufshcd_get_xfer_req_free_slot(dwc_host); ++ if (free_slot == BAD_SLOT) ++ return UFS_ERR; ++ ++ create_query_upiu(STANDARD_WR_REQ, ¶m, (uint8_t *)value, free_slot); ++ ret = wait_for_cmd_completion(BIT(free_slot)); ++ if (ret != UFS_SUCCESS) ++ return ret; ++ ++ ret = handle_query_response((uint8_t *)value, free_slot); ++ return ret; ++} ++ ++static int update_lu_memory_type(struct partition_desc_table *pdt, int index, ++ uint32_t *p_max_alloc_unit, uint32_t *numerator) ++{ ++ uint16_t cap_adj_fac = 0; ++ const uint16_t fac_div = 256; /* factor in descriptor defined as 256 times */ ++ ++ if ((BIT(pdt->partition_desc_ptr[index]->memory_type) & ++ pdt->w_supported_memory_types) == 0) { ++ printf("device not support memory type: %d\n", ++ pdt->partition_desc_ptr[index]->memory_type); ++ return -1; ++ } ++ ++ switch (pdt->partition_desc_ptr[index]->memory_type) { ++ case 0x0: ++ cap_adj_fac = 1; ++ p_max_alloc_unit = NULL; ++ break; ++ case 0x1: ++ cap_adj_fac = pdt->w_system_code_cap_adj_fac / fac_div; ++ p_max_alloc_unit = &(pdt->d_system_code_max_alloc_u); ++ break; ++ case 0x2: ++ cap_adj_fac = pdt->w_non_persist_cap_adj_fac / fac_div; ++ p_max_alloc_unit = &(pdt->d_non_persist_max_alloc_u); ++ break; ++ case 0x3: ++ cap_adj_fac = pdt->w_enhanced1_cap_adj_fac / fac_div; ++ p_max_alloc_unit = &(pdt->d_enhanced1_max_alloc_u); ++ break; ++ case 0x4: ++ cap_adj_fac = pdt->w_enhanced2_cap_adj_fac / fac_div; ++ p_max_alloc_unit = &(pdt->d_enhanced2_max_alloc_u); ++ break; ++ default: ++ printf("unknown memory type\n"); ++ return -1; ++ } ++ if (pdt->partition_desc_ptr[index]->lu_capacity * cap_adj_fac >= 0x200000) { ++ printf("input lu_capacity set too large\n"); ++ return -1; ++ } ++ *numerator = (pdt->partition_desc_ptr[index]->lu_capacity) * 0x800 * cap_adj_fac; ++ return 0; ++} ++ ++/*************************************************************** ++ * update_lu_capacity_info ++ * Updates the dNumAllocUnits information for the Logic Unit ++ * indexed by index. In addition, this also tracks the number ++ * of LUs that will be provisioned in the device. ++ * ++ ***************************************************************/ ++static int update_lu_capacity_info(struct partition_desc_table *pdt) ++{ ++ uint32_t numerator; ++ const uint32_t denominator = (pdt->b_allocation_unit_size) * (pdt->d_segment_size); ++ uint32_t alloced_units = 0; ++ uint32_t *p_max_alloc_unit = NULL; ++ int max_cap_lu = -1; ++ int i; ++ ++ /* Initialize the LUN tracker to zero */ ++ pdt->b_number_lu = 0; ++ ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ if (pdt->partition_desc_ptr[i] == NULL) ++ continue; ++ if (pdt->partition_desc_ptr[i]->lu_capacity == UFS_MAX_LU_CAP) { ++ if (max_cap_lu != -1) { ++ printf("more than one lu set UFS_MAX_LU_CAP\n"); ++ return -1; ++ } ++ max_cap_lu = i; ++ continue; ++ } ++ if (update_lu_memory_type(pdt, i, p_max_alloc_unit, &numerator)) ++ return -1; ++ pdt->partition_desc_ptr[i]->num_alloc_units = ++ (numerator + denominator - 1) / denominator; ++ ufs_pr_dbg("lun %d-NumAllocUnits: 0x%x\n", i, ++ pdt->partition_desc_ptr[i]->num_alloc_units); ++ if (p_max_alloc_unit != NULL) { ++ if (pdt->partition_desc_ptr[i]->num_alloc_units > *p_max_alloc_unit) { ++ printf("alloc units more than this type 0x%x\n", ++ pdt->partition_desc_ptr[i]->memory_type); ++ return -1; ++ } ++ *p_max_alloc_unit -= pdt->partition_desc_ptr[i]->num_alloc_units; ++ } ++ alloced_units += pdt->partition_desc_ptr[i]->num_alloc_units; ++ pdt->b_number_lu += 1; ++ } ++ ++ if (max_cap_lu != -1) { ++ numerator = lower_32_bits(pdt->q_total_raw_device_capacity); ++ pdt->partition_desc_ptr[max_cap_lu]->num_alloc_units = ++ numerator / denominator - alloced_units; ++ pdt->b_number_lu += 1; ++ } ++ ++ return 0; ++} ++ ++static int compair_unit_part(struct partition_desc_table *pdt) ++{ ++ struct partition_desc *desc = NULL; ++ struct ufs_unit_desc_configuration_param unit; ++ int i, ret; ++ ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ desc = pdt->partition_desc_ptr[i]; ++ unit = g_ufs_desc.conf_desc.unit_desc_conf_param[i]; ++ ret = 0x10 * (i + 1); ++ if (desc == NULL) { ++ /* the Lun should be disabled */ ++ if (unit.b_lu_enable != 0x0) ++ return ret; ++ continue; ++ } ++ if (unit.b_lu_enable != 0x1) ++ return ret; ++ if (unit.b_boot_lun_id != desc->boot_lun_id) ++ return ret + 0x1; ++ if (unit.b_lu_write_protect != desc->write_protect) ++ return ret + 0x2; ++ if (unit.b_memory_type != desc->memory_type) ++ return ret + 0x3; ++ if (to_bigendian32(unit.d_num_alloc_units) != desc->num_alloc_units) ++ return ret + 0x4; ++ if (unit.b_data_reliability != desc->data_reliability) ++ return ret + 0x8; ++ if (unit.b_logical_block_size != desc->block_size) ++ return ret + 0x9; ++ if (unit.b_provisioning_type != desc->prov_type) ++ return ret + 0xA; ++ if (to_bigendian16(unit.w_context_capabilities) != desc->context_capabilities) ++ return ret + 0xB; ++ } ++ return 0; ++} ++ ++static int compair_conf_desp(struct partition_desc_table *pdt) ++{ ++ struct ufs_dev_desc_configuration_param dev; ++ ++ dev = g_ufs_desc.conf_desc.dev_desc_conf_param; ++ ++ if (pdt->p_conf_header->b_boot_enable != dev.b_boot_enable) ++ return 0x3; ++ if (pdt->p_conf_header->b_descr_access_en != dev.b_descr_access_en) ++ return 0x4; ++ if (pdt->p_conf_header->b_init_power_mode != dev.b_init_power_mode) ++ return 0x5; ++ if (pdt->p_conf_header->b_high_priority_lun != dev.b_high_priority_lun) ++ return 0x6; ++ if (pdt->p_conf_header->b_secure_removal_type != dev.b_secure_removal_type) ++ return 0x7; ++ if (pdt->p_conf_header->b_init_active_icc_level != dev.b_init_active_icc_level) ++ return 0x8; ++ if (pdt->p_conf_header->w_periodic_rtc_update != dev.w_periodic_rtc_update) ++ return 0x9; ++ ++ return compair_unit_part(pdt); ++} ++ ++/*************************************************************** ++ * ufs_device_init ++ * Description: The function does the following ++ * 1. Sends the NOP Out command continuosly till it passes ++ * 2. Sets the Device init Flag for UFS Device initialization ++ * ++ ****************************************************************/ ++static int ufs_device_init(void) ++{ ++ int ret; ++ uint8_t flags_ret_val = 1; ++ int retry = 3000; /* retry 3000 times */ ++ ++ /* Send continuos NOP OUT Command unless Response is Valid */ ++ ret = send_nop_out_cmd(); ++ if (ret != UFS_SUCCESS) { ++ printf("nop out fail\n"); ++ return ret; ++ } ++ ++ /* Set the Device Init Flag */ ++ ret = ufs_set_flag(FDEVICE_INIT, &flags_ret_val); ++ if (ret) { ++ printf("set device init flag fail\n"); ++ return ret; ++ } ++ ++ do { ++ ret = ufs_read_flag(FDEVICE_INIT, &flags_ret_val); ++ if ((ret == UFS_SUCCESS) && (flags_ret_val == 0)) ++ return UFS_SUCCESS; ++ ufs_waitms(1); ++ } while (--retry > 0); ++ ++ if (flags_ret_val != 0) ++ ret = UFS_FDEVICE_INIT_FAIL; ++ ++ printf("read device init fail\n"); ++ ++ return ret; ++} ++ ++static int ufs_host_init(void) ++{ ++ int ret; ++ ++ ret = ufs_soft_init(); ++ if (ret) { ++ printf("ufs_soft_init fail\n"); ++ return ret; ++ } ++ ++ ret = ufs_hc_enable(); ++ if (ret) { ++ printf("HC enable fail\n"); ++ return ret; ++ } ++ ufs_hc_init(); ++ ++ ret = ufs_link_startup(); ++ if (ret) { ++ printf("link start up fail\n"); ++ return ret; ++ } ++ ++ ufs_config_init(); ++ printf("\nUFS Linkup!\n"); ++ ++ return UFS_SUCCESS; ++} ++ ++/* ++ * some device need a full reset after lu config ++ * And this funciton does not switch to hs mode ++ * after lu config and ufs_reset init would continue and switch to hs mode ++ */ ++static int ufs_reset(void) ++{ ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ ufs_hardware_init(); ++ ++ ret = ufs_host_init(); ++ if (ret) { ++ printf("UfsHostInit ret:%d\n", ret); ++ return ret; ++ } ++ ++ /* device reset will cause a new UAC */ ++ ret = memset_s(dwc_host->lu_request_sense_sent, UNIT_DESCS_COUNT, 0, UNIT_DESCS_COUNT); ++ if (ret != UFS_OK) { ++ printf("memset_s fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ ret = ufs_device_init(); ++ if (ret) ++ printf("UfsDeviceInit ret:%d\n", ret); ++ ++ return ret; ++} ++ ++static void update_geometry_info(struct partition_desc_table *pdt) ++{ ++ struct ufs_geometry_descriptor geo; ++ ++ geo = g_ufs_desc.geo_desc; ++ ++ pdt->q_total_raw_device_capacity = cpu_to_be64(geo.q_total_raw_device_capacity); ++ pdt->d_segment_size = to_bigendian32(geo.d_segment_size); ++ pdt->b_allocation_unit_size = geo.b_allocation_unit_size; ++ pdt->b_data_ordering = geo.b_data_ordering; ++ pdt->b_max_contex_id_number = geo.b_max_contex_id_number; ++ pdt->w_supported_memory_types = to_bigendian16(geo.w_supported_memory_types); ++ pdt->d_system_code_max_alloc_u = to_bigendian32(geo.d_system_code_max_alloc_u); ++ pdt->w_system_code_cap_adj_fac = to_bigendian16(geo.w_system_code_cap_adj_fac); ++ pdt->d_non_persist_max_alloc_u = to_bigendian32(geo.d_non_persist_max_alloc_u); ++ pdt->w_non_persist_cap_adj_fac = to_bigendian16(geo.w_non_persist_cap_adj_fac); ++ pdt->d_enhanced1_max_alloc_u = to_bigendian32(geo.d_enhanced1_max_alloc_u); ++ pdt->w_enhanced1_cap_adj_fac = to_bigendian16(geo.w_enhanced1_cap_adj_fac); ++ pdt->d_enhanced2_max_alloc_u = to_bigendian32(geo.d_enhanced2_max_alloc_u); ++ pdt->w_enhanced2_cap_adj_fac = to_bigendian16(geo.w_enhanced2_cap_adj_fac); ++} ++ ++static int ufs_get_device_info(void) ++{ ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ ret = memset_s((void *)&g_ufs_desc, (unsigned int)sizeof(struct ufs_descriptor), 0, (unsigned int)sizeof(struct ufs_descriptor)); ++ if (ret != UFS_OK) { ++ printf("memset_s fail. ret = %d\n", ret); ++ return; ++ } ++ ++ ret = ufs_read_descriptor((void *)(&g_ufs_desc.dev_desc), ++ DEVICE_DESC, 0, DEVICE_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("read device descriptor fail\n"); ++ return UFS_ERR; ++ } ++ ++ ret = ufs_read_descriptor((void *)(&g_ufs_desc.geo_desc), ++ GEOMETRY_DESC, 0, GEOMETRY_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("read geometry descriptor fail\n"); ++ return ret; ++ } ++ ++ ret = ufs_read_string_descriptor(); ++ if (ret != UFS_OK) { ++ printf("read string descriptor fail\n"); ++ return UFS_ERR; ++ } ++ ++ /* configuration unit descriptor offset/length */ ++ dwc_host->unit_offset = g_ufs_desc.dev_desc.b_ud_0base_offset; ++ dwc_host->unit_length = g_ufs_desc.dev_desc.b_ud_config_plength; ++ ++ /* device specification version */ ++ dwc_host->dev_spec_version = ++ to_bigendian16(g_ufs_desc.dev_desc.w_spec_version); ++ dwc_host->manufacturer_id = ++ to_bigendian16(g_ufs_desc.dev_desc.w_manufacturer_id); ++ dwc_host->manufacturer_date = ++ to_bigendian16(g_ufs_desc.dev_desc.w_manufacture_date); ++ ++ printf(" MID: 0x%x\n", dwc_host->manufacturer_id); ++ printf(" Version: 0x%x\n", dwc_host->dev_spec_version); ++ printf(" Device: %s\n", g_ufs_desc.str_desc.product_name); ++ printf(" Size: 0x%llx\n", cpu_to_be64(g_ufs_desc.geo_desc.q_total_raw_device_capacity)); ++ printf(" Block: %d Bytes\n", LOGICAL_BLK_SIZE); ++ ++ return UFS_OK; ++} ++ ++static int ufs_device_configuration(struct partition_desc_table *pdt, ++ struct desc_params *params, ++ struct dwc_ufs_query_upiu *resp_upiu) ++{ ++ int ret; ++ uint32_t value; ++ ++ ret = read_attribute(B_CONFIG_DESC_LOCK, 0, 0, &value); ++ if (ret != UFS_SUCCESS) { ++ printf("read bConfigDescrLock fail. ret = %d\n", ret); ++ } else { ++ printf("bConfigDescrLock = 0x%x\n", value); ++ if (value == 1) { ++ printf("write config desc fail: descriptor locked\n"); ++ return -1; ++ } ++ } ++ ++ /* response of configuration descriptor sent as request UPIU */ ++ params->req_upiu = resp_upiu; ++ params->conf_head = pdt->p_conf_header; ++ params->part_desc = pdt->partition_desc_ptr; ++ params->opcode = WRITE_DESC_OPCODE; ++ params->desc_idn = CONFIGURATION_DESC; ++ params->desc_index = 0; ++ params->length = CONFIGURATION_DESC_LENGTH; ++ modify_desc_upiu(params); ++ ++ ret = write_descriptor(params->req_upiu); ++ if (ret != UFS_SUCCESS) { ++ printf("write configuration fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ ret = ufs_reset(); ++ if (ret) { ++ printf("ufs_reset fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/*************************************************************** ++ * ufs_create_partition_inventory ++ * Description: The Function does the following actions ++ * ++ * 1. Reads UNIT desc's and stores the qphymemrescnt of each lun ++ * 2. Reads the Device Descriptor to know the Configureable Unit ++ * desc zero's Offset and length of all unit descriptors. ++ * 3. Validates the qmemrescnt with the configuration desc contents ++ * 4. Reads and edits the content of configuration desc as per the ++ * input arguments ++ * ++ ***************************************************************/ ++static int ufs_create_partition_inventory(uint8_t partition_mask, struct partition_desc_table *pdt) ++{ ++ int ret; ++ struct desc_params params; ++ struct dwc_ufs_query_upiu *req_upiu = NULL; ++ void *resp_upiu = NULL; ++ uint8_t i; ++ ++ get_local_dwc_host(); ++ /* use slot 0 default */ ++ resp_upiu = dwc_host->lrb[0].resp_upiu; ++ req_upiu = (struct dwc_ufs_query_upiu *)(dwc_host->lrb[0].cmd_upiu); ++ ++ /* update the geometry information */ ++ update_geometry_info(pdt); ++ ++ /* Validate the Input Arguments */ ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ if ((partition_mask & (BIT(i))) == 0) ++ pdt->partition_desc_ptr[i] = NULL; ++ } ++ ++ update_lu_capacity_info(pdt); ++ ++ /* read configuration descriptor */ ++ params.req_upiu = req_upiu; ++ params.part_desc = NULL; ++ params.opcode = READ_DESC_OPCODE; ++ params.desc_idn = CONFIGURATION_DESC; ++ params.desc_index = 0; ++ params.length = CONFIGURATION_DESC_LENGTH; ++ modify_desc_upiu(¶ms); ++ ++ ret = read_descriptor(req_upiu, &resp_upiu); ++ if (ret != UFS_SUCCESS) { ++ printf("read configuration fail. ret = %d\n", ret); ++ return ret; ++ } ++ ret = memcpy_s(&g_ufs_desc.conf_desc, CONFIGURATION_DESC_LENGTH, (void *)((u8 *)resp_upiu + QUERY_RESPONSE_HEAD_OFFSET), CONFIGURATION_DESC_LENGTH); ++ if (ret != UFS_OK) { ++ printf("memcpy_s fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ ret = compair_conf_desp(pdt); ++ if (!ret) ++ return 0; ++ ++ printf("compair conf desp fail. ret=%d\n", ret); ++ printf("UFS enter device configuration\n"); ++ ++ ret = ufs_device_configuration(pdt, ¶ms, resp_upiu); ++ if (ret) { ++ printf("ufs device configuration fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int ufs_set_bootlun(uint8_t lun) ++{ ++ uint32_t value; ++ int ret; ++ ++ ret = read_attribute(B_BOOT_LUNEN, 0, 0, &value); ++ if (ret != UFS_SUCCESS) { ++ printf("read bBootLunEn fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ if ((lun + 1) == value) { ++ printf("Same UFS Lu%u Boot W-Lun\n", (value - 1)); ++ return 0; ++ } ++ ++ /* set default boot from Boot LU A */ ++ value = lun + 1; ++ ret = write_attribute(B_BOOT_LUNEN, 0, 0, &value); ++ if (ret) { ++ printf("write bBootLunEn attribute fail. ret = %d\n", ret); ++ return ret; ++ } ++ printf("new UFS Lu%u Boot W-Lun\n", (value - 1)); ++ return ret; ++} ++ ++static int set_boot_lu_enable(void) ++{ ++ uint32_t value = 0; ++ uint32_t target_value = DEFAULT_BOOT_LUN; ++ int ret; ++ ++ ret = read_attribute(B_BOOT_LUNEN, 0, 0, &value); ++ if (ret != UFS_SUCCESS) { ++ printf("read bBootLunEn fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ if (value != 0) ++ printf("UFS get boot W-LU-%c\n", ++ (value == WELL_BOOT_LU_A) ? 'A' : 'B'); ++ ++ if (value == target_value) ++ return 0; ++ ++ /* set default boot from Boot LU A */ ++ value = target_value; ++ ret = write_attribute(B_BOOT_LUNEN, 0, 0, &value); ++ if (ret) { ++ printf("write bBootLunEn attribute fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ printf("UFS set boot W-LU(%c)\n", (value == WELL_BOOT_LU_A) ? 'A' : 'B'); ++ return ret; ++} ++ ++static void ufs_lu_configuration(struct partition_desc_table *pdt) ++{ ++ int i; ++ ++ get_local_dwc_host(); ++ ++ /* configuration header */ ++ pdt->p_conf_header->b_boot_enable = 0x1; ++ pdt->p_conf_header->b_descr_access_en = 0x0; ++ pdt->p_conf_header->b_init_power_mode = 0x1; ++ pdt->p_conf_header->b_high_priority_lun = 0x7F; ++ pdt->p_conf_header->b_secure_removal_type = 0x0; ++ pdt->p_conf_header->b_init_active_icc_level = 0x0; ++ pdt->p_conf_header->w_periodic_rtc_update = 0x0; ++ ++ /* lu 0: boot lu A */ ++ pdt->partition_desc_ptr[0]->boot_lun_id = WELL_BOOT_LU_A; /* lu 0, boot a */ ++ if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_TOSHIBA) ++ pdt->partition_desc_ptr[0]->memory_type = 0x4; /* lu 0, Enhanced Memory */ ++ else ++ pdt->partition_desc_ptr[0]->memory_type = 0x3; /* lu 0, Enhanced Memory */ ++ pdt->partition_desc_ptr[0]->lu_capacity = 4; /* lu 0, 4MB */ ++ ++ /* lu 1: boot lu B */ ++ pdt->partition_desc_ptr[1]->boot_lun_id = WELL_BOOT_LU_B; /* lu 1, boot b */ ++ if (dwc_host->manufacturer_id == UFS_MANUFACTURER_ID_TOSHIBA) ++ pdt->partition_desc_ptr[0]->memory_type = 0x4; /* lu 0, Enhanced Memory */ ++ else ++ pdt->partition_desc_ptr[0]->memory_type = 0x3; /* lu 0, Enhanced Memory */ ++ pdt->partition_desc_ptr[1]->lu_capacity = 4; /* lu 1, 4MB */ ++ ++ /* lu 2: data lu */ ++ pdt->partition_desc_ptr[2]->boot_lun_id = 0x0; /* lu 2 */ ++ pdt->partition_desc_ptr[2]->memory_type = 0x3; /* lu 2, Enhanced Memory */ ++ pdt->partition_desc_ptr[2]->lu_capacity = 8; /* lu 2, 8MB */ ++ ++ /* lu 3: data lu */ ++ pdt->partition_desc_ptr[3]->boot_lun_id = 0x0; /* lu 3 */ ++ pdt->partition_desc_ptr[3]->memory_type = 0x0; /* lu 3, Normal Memory */ ++ pdt->partition_desc_ptr[3]->lu_capacity = UFS_MAX_LU_CAP; /* lu 3, max capacity */ ++ ++ for (i = 0; i <= 3; i++) { /* lu 0 - 3 */ ++ pdt->partition_desc_ptr[i]->write_protect = 0x0; ++ pdt->partition_desc_ptr[i]->data_reliability = 0x1; ++ pdt->partition_desc_ptr[i]->block_size = 0x0c; ++ pdt->partition_desc_ptr[i]->prov_type = 0x2; ++ pdt->partition_desc_ptr[i]->context_capabilities = 0x0; ++ } ++} ++ ++static int ufs_lu_init(void) ++{ ++ struct partition_desc_table pdt = {0}; ++ struct partition_desc partition_desc_ptr[UNIT_DESCS_COUNT] = {{0}}; ++ struct configuration_header config_header = {0}; ++ unsigned int bootlun = 0; ++ int i, ret; ++ ++ get_local_dwc_host(); ++ ++ pdt.p_conf_header = &config_header; ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) ++ pdt.partition_desc_ptr[i] = &partition_desc_ptr[i]; ++ ++ ufs_lu_configuration(&pdt); ++ ++ ret = ufs_create_partition_inventory(0xf, &pdt); ++ if (ret) ++ goto out; ++ ++ ret = set_boot_lu_enable(); ++ if (ret) ++ goto out; ++ ++ ret = read_attribute(B_BOOT_LUNEN, 0, 0, &bootlun); ++ if (ret != UFS_SUCCESS) { ++ printf("read bBootLunEn fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ if (pdt.partition_desc_ptr[i] && ++ pdt.partition_desc_ptr[i]->boot_lun_id == bootlun) { ++ printf("UFS config LU-%d to W-LU-%c\n", i, ++ (bootlun == WELL_BOOT_LU_A) ? 'A' : 'B'); ++ dwc_host->active_bootlun = i; ++ break; ++ } ++ } ++out: ++ return ret; ++} ++ ++static int ufs_set_ref_clk(void) ++{ ++ uint32_t value; ++ int ret; ++ uint32_t target_ref_clk; ++ ++ target_ref_clk = 0; /* 19.2MHz */ ++ ++ ret = read_attribute(B_REFCLK_FREQ, 0, 0, &value); ++ if (ret != UFS_SUCCESS) { ++ printf("read bRefClkFreq fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ if (target_ref_clk == value) ++ return 0; ++ ++ /* set default boot from Boot LU A */ ++ ret = write_attribute(B_REFCLK_FREQ, 0, 0, &target_ref_clk); ++ if (ret) { ++ printf("write bRefClkFreq attribute fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ ret = read_attribute(B_REFCLK_FREQ, 0, 0, &value); ++ if (ret != UFS_SUCCESS) { ++ printf("read bRefClkFreq fail. ret = %d\n", ret); ++ return ret; ++ } ++ ++ if (target_ref_clk == value) ++ return UFS_OK; ++ ++ return UFS_ERR; ++} ++ ++int ufs_read_capacity(uint32_t *lba) ++{ ++ uint32_t *data = NULL; ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ data = (uint32_t *)dwc_host->wr_buf; ++ ++ ret = do_scsi_cmd(UFS_OP_READ_CAPACITY_10, DMA_FROM_DEVICE, ++ (uint64_t)(uintptr_t)data, 0, CAPACITY_DATA_LENGTH); ++ if (ret != UFS_SUCCESS) { ++ printf("send READ_CAPACITY_10 error: %d\n", ret); ++ return ret; ++ } ++ ++ *lba = to_bigendian32(*data) + 1; ++ return ret; ++} ++ ++static int ufs_request_sense(void) ++{ ++ uint8_t *sense_data = NULL; ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ sense_data = (uint8_t *)dwc_host->wr_buf; ++ ++ ret = do_scsi_cmd(UFS_OP_REQUEST_SENSE, DMA_FROM_DEVICE, ++ (uint64_t)(uintptr_t)sense_data, 0, SENSE_DATA_LENGTH); ++ ++ return ret; ++} ++ ++/**************************************************************** ++ * ufs_set_active_lun ++ * Description: Sets the Lun Number for further Transactions ++ * ++ ***************************************************************/ ++int ufs_set_active_lun(uint8_t lun) ++{ ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ dwc_host->active_lun = lun; ++ if (lun >= UNIT_DESCS_COUNT) { ++ printf("well known lun: 0x%x\n", lun); ++ ++ if (g_wlun == lun) ++ return UFS_SUCCESS; ++ ++ /* issue REQUEST SENSE command once for lu before accessing it */ ++ ret = ufs_request_sense(); ++ if (ret != 0) { ++ printf("send UFS_OP_REQUEST_SENSE error: %d\n", ret); ++ return ret; ++ } ++ printf("send UFS_OP_REQUEST_SENSE success: %d\n", ret); ++ g_wlun = lun; ++ return UFS_SUCCESS; ++ } ++ ++ printf("UFS set active LU-%d\n", lun); ++ if (dwc_host->lu_request_sense_sent[lun]) ++ return UFS_SUCCESS; ++ ++ /* issue REQUEST SENSE command once for lu before accessing it */ ++ ret = ufs_request_sense(); ++ if (ret != 0) { ++ printf("send UFS_OP_REQUEST_SENSE error: %d\n", ret); ++ return ret; ++ } ++ ++ dwc_host->lu_request_sense_sent[lun] = 1; ++ return ret; ++} ++ ++/*************************************************************** ++ * ufs_sync_storage ++ * Description: Synchronizes the content of the cache to the media. ++ * ++ ****************************************************************/ ++static int ufs_sync_storage(void) ++{ ++ int ret; ++ ++ /* Send Synchronize Cache 10 Command and handle its completion */ ++ ret = do_scsi_cmd(UFS_OP_SYNCHRONIZE_CACHE_10, 0, 0, 0, 0); ++ if (ret != UFS_SUCCESS) { ++ printf("send SYNCHRONIZE_CACHE_10 error: %d\n", ret); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++/*************************************************************** ++ * ufs_write_storage ++ * Description: Writes the content of the source Address to ++ * destination offset of the active lun. ++ * The APi assumes the data buffes if unaligned have ++ * valid data above the start address ++ * ++ ****************************************************************/ ++int ufs_write_storage(uint64_t src_addr, uint64_t dest_offset, uint32_t size) ++{ ++ int ret; ++ uint32_t len; ++ uint64_t src_addr_tmp = src_addr; ++ uint32_t size_tmp = size; ++ uint32_t maxsize = PRDT_BUFFER_SIZE * DWC_UFSHCD_MAX_PRD_SIZE; ++ ++ while (size_tmp) { ++ if (size_tmp < maxsize) ++ len = size_tmp; ++ else ++ len = maxsize; ++ ++ /* Send Write 10 Command and handle its completion */ ++ ret = do_scsi_cmd(UFS_OP_WRITE_10, DMA_TO_DEVICE, ++ src_addr_tmp, dest_offset, len); ++ if (ret != UFS_SUCCESS) { ++ printf("send WRITE_10 error: %d\n", ret); ++ return ret; ++ } ++ size_tmp -= len; ++ src_addr_tmp += len; ++ dest_offset += len; ++ } ++ ++ ret = ufs_sync_storage(); ++ return ret; ++} ++ ++/******************************************************************** ++ * ufs_read_storage ++ * Description: Reads the content of the source offset of active lun ++ * and copies the content to dest_addr. ++ * The API assumes there is enough memory allocated and ++ * available above and below start address for unaligned ++ * and multi block access ++ * ++ ********************************************************************/ ++int ufs_read_storage(uint64_t dest_addr, uint64_t src_offset, uint32_t size) ++{ ++ int ret = -1; ++ uint32_t len; ++ uint64_t dest_addr_tmp = dest_addr; ++ uint32_t size_tmp = size; ++ uint32_t maxsize = (PRDT_BUFFER_SIZE * DWC_UFSHCD_MAX_PRD_SIZE); ++ ++ while (size_tmp) { ++ if (size_tmp < maxsize) ++ len = size_tmp; ++ else ++ len = maxsize; ++ ++ /* Send Read 10 Command and handle its completion */ ++ ret = do_scsi_cmd(UFS_OP_READ_10, DMA_FROM_DEVICE, ++ dest_addr_tmp, src_offset, len); ++ if (ret != UFS_SUCCESS) { ++ printf("send READ_10 error: %d\n", ret); ++ return ret; ++ } ++ size_tmp -= len; ++ dest_addr_tmp += len; ++ src_offset += len; ++ } ++ ++ return ret; ++} ++ ++int ufs_write_boot_storage(uint64_t dest_addr, uint64_t src_offset, uint32_t size) ++{ ++ int ret; ++ int prelun; ++ ++ get_local_dwc_host(); ++ ++ prelun = dwc_host->active_lun; ++ ret = ufs_set_active_lun(dwc_host->active_bootlun); ++ if (ret != 0) ++ return ret; ++ ++ ret = ufs_write_storage(dest_addr, src_offset, size); ++ if (ret != 0) ++ return ret; ++ ++ ret = ufs_set_active_lun(prelun); ++ ++ return ret; ++} ++ ++int ufs_read_boot_storage(uint64_t dest_addr, uint64_t src_offset, uint32_t size) ++{ ++ int ret; ++ int prelun; ++ ++ get_local_dwc_host(); ++ ++ prelun = dwc_host->active_lun; ++ ret = ufs_set_active_lun(dwc_host->active_bootlun); ++ if (ret != 0) ++ return ret; ++ ++ ret = ufs_read_storage(dest_addr, src_offset, size); ++ if (ret != 0) ++ return ret; ++ ++ ret = ufs_set_active_lun(prelun); ++ ++ return ret; ++} ++ ++int ufs_hibernate_enter(void) ++{ ++ unsigned int value; ++ uint32_t retry = 500; /* wait 500ms to let device finish the device hibernate exit */ ++ ++ /* 1. BUSTHRTL bit12 clear to 0 */ ++ value = dwc_ufs_read_reg(UFS_BUSTHRTL_OFF); ++ if (value & CGE) { ++ value &= (~CGE); ++ dwc_ufs_write_reg(UFS_BUSTHRTL_OFF, value); ++ } ++ ++ /* 2. enter hibernate */ ++ send_uic_command(DME_HIBERNATE_ENTER, 0, 0, 0); ++ ++ /* 3. detect enter success or not */ ++ while (retry--) { ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UHES_BIT) ++ break; ++ ufs_waitms(1); ++ } ++ /* 3.1 if fail */ ++ if (retry == 0xFFFFFFFF) { ++ printf("ufs_hibernate_enter fail, UHES wait 500ms timeout\n"); ++ return UFS_ERR; ++ } ++ /* 3.2 if success */ ++ printf("ufs_hibernate_enter success.\n"); ++ return UFS_OK; ++} ++ ++int ufs_hibernate_exit(void) ++{ ++ unsigned int value; ++ uint32_t retry = 500; /* wait 500ms to let device finish the device hibernate exit */ ++ ++ /* 1. BUSTHRTL bit12 clear to 0 */ ++ value = dwc_ufs_read_reg(UFS_BUSTHRTL_OFF); ++ if (value & CGE) { ++ value &= (~CGE); ++ dwc_ufs_write_reg(UFS_BUSTHRTL_OFF, value); ++ } ++ ++ /* 2. exit hibernate */ ++ send_uic_command(DME_HIBERNATE_EXIT, 0, 0, 0); ++ ++ /* 3. detect exit success or not */ ++ while (retry--) { ++ if (dwc_ufs_read_reg(UFS_IS_OFF) & UFS_IS_UHXS_BIT) ++ break; ++ ufs_waitms(1); ++ } ++ /* 3.1 if fail, dump reg */ ++ if (retry == 0xFFFFFFFF) { ++ printf("ufs_hibernate_exit fail, UHXS wait 500ms timeout\n"); ++ return UFS_ERR; ++ } ++ /* 3.2 if success */ ++ printf("ufs_hibernate_exit success.\n"); ++ return UFS_OK; ++} ++ ++/*************************************************************** ++ * do_flag_operation ++ * Description: The function can be used for any operation on flags ++ * 1. fills the query upiu memory content ++ * 2. Handles the response of query command sent ++ * 3. updates the flag return value ++ * ++ ***************************************************************/ ++static int do_flag_operation(int opcode, int query_func, enum flags_id idn, uint8_t *flags_ret) ++{ ++ int ret; ++ uint8_t val[4] = {0}; /* 4 byte value */ ++ uint8_t free_slot; ++ struct query_param param = {(uint8_t)opcode, (uint8_t)idn, 0, 0}; ++ ++ get_local_dwc_host(); ++ ++ free_slot = dwc_ufshcd_get_xfer_req_free_slot(dwc_host); ++ if (free_slot == BAD_SLOT) ++ return UFS_ERR; ++ ++ create_query_upiu((uint8_t)query_func, ¶m, val, free_slot); ++ ++ ret = wait_for_cmd_completion(BIT(free_slot)); ++ if (ret != UFS_SUCCESS) ++ return ret; ++ ++ ret = handle_query_response(val, free_slot); ++ *flags_ret = val[0]; ++ ++ return ret; ++} ++ ++/*************************************************************** ++ * ufs_set_flag ++ * Description: The function invokes do_flag_operation for set ++ * flag operation ++ * ++ ***************************************************************/ ++int ufs_set_flag(enum flags_id idn, uint8_t *flags_ret) ++{ ++ int ret; ++ ++ ret = do_flag_operation(SET_FLAG_OPCODE, STANDARD_WR_REQ, idn, flags_ret); ++ ++ return ret; ++} ++ ++static void modify_write_conf_desc(const struct desc_params *params) ++{ ++ uint8_t *data = NULL; ++ struct partition_desc **desc = params->part_desc; ++ struct dwc_ufs_query_upiu *upiu_ptr = (struct dwc_ufs_query_upiu *)params->req_upiu; ++ uint32_t i, offset; ++ ++ get_local_dwc_host(); ++ ++ data = (uint8_t *)(upiu_ptr + 1); ++ /* boot enable */ ++ data[0x3] = params->conf_head->b_boot_enable; ++ data[0x4] = params->conf_head->b_descr_access_en; ++ data[0x5] = params->conf_head->b_init_power_mode; ++ data[0x6] = params->conf_head->b_high_priority_lun; ++ data[0x7] = params->conf_head->b_secure_removal_type; ++ data[0x8] = params->conf_head->b_init_active_icc_level; ++ data[0x9] = (uint8_t)(((params->conf_head->w_periodic_rtc_update) >> 8) & 0xff); /* shift 8 */ ++ data[0xA] = (uint8_t)((params->conf_head->w_periodic_rtc_update) & 0xff); ++ for (i = 0; i < UNIT_DESCS_COUNT; i++) { ++ offset = dwc_host->unit_length * i + dwc_host->unit_offset; ++ if (desc[i] == NULL) { ++ /* Disable the Lun */ ++ data[offset] = 0x0; ++ continue; ++ } ++ data[offset] = 0x1; ++ data[offset + 0x1] = desc[i]->boot_lun_id; ++ data[offset + 0x2] = desc[i]->write_protect; ++ data[offset + 0x3] = desc[i]->memory_type; ++ data[offset + 0x4] = (uint8_t)(((desc[i]->num_alloc_units) >> 24) & 0xff); /* shift 24 */ ++ data[offset + 0x5] = (uint8_t)(((desc[i]->num_alloc_units) >> 16) & 0xff); /* shift 16 */ ++ data[offset + 0x6] = (uint8_t)(((desc[i]->num_alloc_units) >> 8) & 0xff); /* shift 8 */ ++ data[offset + 0x7] = (uint8_t)((desc[i]->num_alloc_units) & 0xff); ++ data[offset + 0x8] = desc[i]->data_reliability; ++ data[offset + 0x9] = desc[i]->block_size; ++ data[offset + 0xa] = desc[i]->prov_type; ++ data[offset + 0xb] = (uint8_t)(((desc[i]->context_capabilities) >> 8) & 0xff); /* shift 8 */ ++ data[offset + 0xc] = (uint8_t)((desc[i]->context_capabilities) & 0xff); ++ } ++} ++ ++/*************************************************************** ++ * modify_desc_upiu ++ * Description: The function performs the following ++ * 1.Fills the request upiu for read/write desc operation ++ * 2.Updates the Data Memory below the request UPIU ++ * ++ ***************************************************************/ ++void modify_desc_upiu(const struct desc_params *params) ++{ ++ uint32_t i; ++ struct dwc_ufs_query_upiu *upiu_ptr = NULL; ++ ++ /* Command Descriptor Programming */ ++ upiu_ptr = (struct dwc_ufs_query_upiu *)params->req_upiu; ++ upiu_ptr->trans_type = 0x16; ++ upiu_ptr->flags = UPIU_CMD_FLAGS_NONE; ++ upiu_ptr->reserved_1 = 0x00; ++ upiu_ptr->task_tag = 0x01; ++ upiu_ptr->reserved_2 = 0x00; ++ ++ if (params->opcode == READ_DESC_OPCODE) ++ upiu_ptr->query_func = STANDARD_RD_REQ; ++ else ++ upiu_ptr->query_func = STANDARD_WR_REQ; ++ ++ upiu_ptr->query_resp = 0x00; ++ upiu_ptr->reserved_3 = 0x00; ++ upiu_ptr->tot_ehs_len = 0x00; ++ if (params->opcode == READ_DESC_OPCODE) ++ upiu_ptr->data_seg_len = 0x00; ++ else ++ upiu_ptr->data_seg_len = (to_bigendian16(params->length)); ++ ++ for (i = 0; i < 16; i++) /* clear 16 tsf */ ++ upiu_ptr->tsf[i] = 0x0; ++ upiu_ptr->tsf[0] = params->opcode; /* 0: opcode */ ++ upiu_ptr->tsf[1] = params->desc_idn; /* 1: idn */ ++ upiu_ptr->tsf[2] = params->desc_index; /* 2: index */ ++ upiu_ptr->tsf[6] = (uint8_t)(params->length >> 8); /* 6: MSB shift 8 */ ++ upiu_ptr->tsf[7] = params->length & 0xff; /* 7: LSB */ ++ upiu_ptr->reserved_5 = 0x0; ++ ++ if ((params->opcode == WRITE_DESC_OPCODE) && (params->desc_idn == CONFIGURATION_DESC)) ++ modify_write_conf_desc(params); ++} ++ ++/*************************************************************** ++ * read_descriptor ++ * Description: The functon populates the request upiu structure ++ * with upiu info passed in 1st argument ++ * ++ ***************************************************************/ ++int read_descriptor(const void *req_upiu, void **resp_upiu_out) ++{ ++ int ret; ++ struct dwc_ufs_utrd *utrd = NULL; ++ struct dwc_ufs_resp_upiu *resp_upiu = NULL; ++ uint8_t free_slot; ++ ++ get_local_dwc_host(); ++ ++ free_slot = dwc_ufshcd_get_xfer_req_free_slot(dwc_host); ++ if (free_slot == BAD_SLOT) ++ return UFS_ERR; ++ ++ utrd = dwc_host->lrb[free_slot].utrd; ++ resp_upiu = dwc_host->lrb[free_slot].resp_upiu; ++ ++ /* UTRD Descriptor Programming for processing command */ ++ utrd->ct_and_flags = (uint8_t) ++ (UTP_NO_DATA_TRANSFER | UTP_UFS_STORAGE_COMMAND); ++ utrd->resp_upiu_length = to_littleendian16((uint16_t) ++ (DWC_UCD_ALIGN >> UFS_DWORD_SHIFT)); ++ utrd->prdt_length = 0; ++ ++ create_desc_upiu(req_upiu, free_slot); ++ ++ ret = wait_for_cmd_completion(BIT(free_slot)); ++ if (ret != UFS_SUCCESS) ++ return ret; ++ ++ *(resp_upiu_out) = resp_upiu; ++ ++ ret = handle_query_response(NULL, free_slot); ++ return ret; ++} ++ ++/*************************************************************** ++ * ufs_read_flag ++ * Description: The function invokes do_flag_operation for Read ++ * flag operation ++ * ++ ***************************************************************/ ++int ufs_read_flag(enum flags_id idn, uint8_t *flags_ret) ++{ ++ int ret; ++ ++ ret = do_flag_operation(READ_FLAG_OPCODE, STANDARD_RD_REQ, idn, flags_ret); ++ return ret; ++} ++ ++/*************************************************************** ++ * do_mode_change ++ * Description: The function issues the the powermode change to ++ * UFS device to work using RX and Tx lanes and Gears ++ * as provided in pwr_mode_params structure. ++ * ++ ****************************************************************/ ++int do_mode_change(const struct pwr_mode_params *pmp) ++{ ++ send_uic_command(DME_SET, 0x155c0000, 0x0, 0x0); /* PA_TxSkip */ ++ send_uic_command(DME_SET, 0x15680000, 0x0, pmp->tx_gear); /* PA_TxGear */ ++ send_uic_command(DME_SET, 0x15830000, 0x0, pmp->rx_gear); /* PA_RxGear */ ++ ++ if (pmp->pwr_mode == FAST_MODE || pmp->pwr_mode == FASTAUTO_MODE) { ++ send_uic_command(DME_SET, 0x156a0000, 0x0, pmp->hs_series); /* PA_HSSeries */ ++ send_uic_command(DME_SET, 0x15690000, 0x0, 0x1); /* PA_TxTermination */ ++ send_uic_command(DME_SET, 0x15840000, 0x0, 0x1); /* PA_RxTermination */ ++ send_uic_command(DME_SET, 0x15850000, 0x0, 0x1); /* PA_Scrambling */ ++ } else if (pmp->pwr_mode == SLOW_MODE || pmp->pwr_mode == SLOWAUTO_MODE) { ++ send_uic_command(DME_SET, 0x15690000, 0x0, 0x0); /* PA_TxTermination */ ++ send_uic_command(DME_SET, 0x15840000, 0x0, 0x0); /* PA_RxTermination */ ++ send_uic_command(DME_SET, 0x15850000, 0x0, 0x0); /* PA_Scrambling */ ++ } ++ ++ send_uic_command(DME_SET, 0x15600000, 0x0, pmp->tx_lanes); /* PA_ActiveTxDataLanes */ ++ send_uic_command(DME_SET, 0x15800000, 0x0, pmp->rx_lanes); /* PA_ActiveRxDataLanes */ ++ ++ send_uic_command(DME_SET, 0x15b00000, 0x0, 0x1FFF); /* PA_PWRModeUserData0 */ ++ send_uic_command(DME_SET, 0x15b10000, 0x0, 0xFFFF); /* PA_PWRModeUserData1 */ ++ send_uic_command(DME_SET, 0x15b20000, 0x0, 0x7FFF); /* PA_PWRModeUserData2 */ ++ send_uic_command(DME_SET, 0x15b30000, 0x0, 0x1FFF); /* PA_PWRModeUserData3 */ ++ send_uic_command(DME_SET, 0x15b40000, 0x0, 0xFFFF); /* PA_PWRModeUserData4 */ ++ send_uic_command(DME_SET, 0x15b50000, 0x0, 0x7FFF); /* PA_PWRModeUserData5 */ ++ send_uic_command(DME_SET, 0xd0410000, 0x0, 0x1FFF); /* DME_FC0ProtectionTimeOutVal */ ++ send_uic_command(DME_SET, 0xd0420000, 0x0, 0xFFFF); /* DME_TC0ReplayTimeOutVal */ ++ send_uic_command(DME_SET, 0xd0430000, 0x0, 0x7FFF); /* DME_AFC0ReqTimeOutVal */ ++ send_uic_command(DME_SET, 0xd0440000, 0x0, 0x1FFF); /* DME_FC1ProtectionTimeOutVal */ ++ send_uic_command(DME_SET, 0xd0450000, 0x0, 0xFFFF); /* DME_TC1ReplayTimeOutVal */ ++ send_uic_command(DME_SET, 0xd0460000, 0x0, 0x7FFF); /* DME_AFC1ReqTimeOutVal */ ++ ++ /* VS_DebugCounter0Mask */ ++ send_uic_command(DME_SET, 0xd09a0000, 0x0, 0x80000000); ++ /* VS_DebugCounter1Mask */ ++ send_uic_command(DME_SET, 0xd09b0000, 0x0, 0x78000000); ++ send_uic_command(DME_SET, 0x15710000, 0x0, pmp->pwr_mode); /* PA_PWRMode */ ++ ++ return 0; ++} ++ ++void adapt_pll_to_power_mode(enum POWER_MODE pwrmode, uint8_t gear, uint8_t rate) ++{ ++ if (pwrmode == FAST_MODE || pwrmode == FASTAUTO_MODE) { ++ if (gear == 1) { /* hs-g1 */ ++ if (rate == 2) { /* 2: rateb */ ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_PRE_DIV), 0x0, 0x0); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_SWC_EN), 0x0, 0x0); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_FBK_S), 0x0, 0x01); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_FBK_P), 0x0, 0x4c); ++ } ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_TXHSGR), 0x0, 0x02); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_RXHSGR), 0x0, 0x02); ++ } else if (gear == 2) { /* hs-g2 */ ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_TXHSGR), 0x0, 0x01); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_RXHSGR), 0x0, 0x01); ++ } else if (gear == 3) { /* hs-g3 */ ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_TXHSGR), 0x0, 0x0); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_RXHSGR), 0x0, 0x0); ++ } ++ } ++ ++ /* the PWM's clk is been derived from the pll above */ ++ if (pwrmode == SLOW_MODE || pwrmode == SLOWAUTO_MODE) { ++ if (gear == 1) { /* pwm-g1 */ ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_TXLSGR), 0x0, 0x07); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_RXLSGR), 0x0, 0x06); ++ } else if (gear == 2) { /* pwm-g2 */ ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_TXLSGR), 0x0, 0x06); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_RXLSGR), 0x0, 0x05); ++ } else if (gear == 3) { /* pwm-g3 */ ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_TXLSGR), 0x0, 0x05); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_RXLSGR), 0x0, 0x04); ++ } else if (gear == 4) { /* pwm-g4 */ ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_TXLSGR), 0x0, 0x04); ++ send_uic_command(DME_SET, attr_mcb(RG_PLL_RXLSGR), 0x0, 0x03); ++ } ++ } ++} ++ ++static int change_power_mode(enum POWER_MODE pwrmode, uint8_t gear, uint8_t rate, uint8_t lane) ++{ ++ struct pwr_mode_params pmp; ++ uint32_t value; ++ int retry = 1000; /* retry 1000 times */ ++ int index = find_device_index(); ++ int ret; ++ ++ printf("UFS %s Gear-%d Rate-%c Lane-%d\n", ++ ((pwrmode == SLOW_MODE) ? "Slow" : ++ ((pwrmode == SLOWAUTO_MODE) ? "SlowAuto" : ++ ((pwrmode == FAST_MODE) ? "Fast" : "FastAuto"))), ++ gear, (rate == 1) ? 'A' : 'B', lane); ++ ++ pmp.tx_gear = gear; ++ pmp.rx_gear = gear; ++ pmp.hs_series = rate; ++ ++ if (lane == 1) { ++ g_tx_lane_num[index] = 1; ++ g_rx_lane_num[index] = 1; ++ } else { ++ /* PA_ConnectedTxDataLanes */ ++ g_tx_lane_num[index] = (uint8_t)uic_cmd_read(0x1, 0x15610000); ++ ++ /* PA_ConnectedRxDataLanes */ ++ g_rx_lane_num[index] = (uint8_t)uic_cmd_read(0x1, 0x15810000); ++ ++ printf("UFS connected lanes Tx-%d Rx-%d\n", g_tx_lane_num[index], g_rx_lane_num[index]); ++ } ++ ++ pmp.tx_lanes = g_tx_lane_num[index]; ++ pmp.rx_lanes = g_rx_lane_num[index]; ++ pmp.pwr_mode = pwrmode; ++ ++ adapt_mode_change(&pmp); ++ adapt_pll_to_power_mode(pwrmode, gear, rate); ++ ++ ret = do_mode_change(&pmp); ++ if (ret) ++ return ret; ++ ++ while (--retry) { ++ value = dwc_ufs_read_reg(UFS_IS_OFF); ++ if (value & UFS_IS_UPMS_BIT) { ++ dwc_ufs_write_reg(UFS_IS_OFF, UFS_IS_UPMS_BIT); ++ break; ++ } ++ ufs_waitms(1); ++ } ++ value = dwc_ufs_read_reg(UFS_HCS_OFF); ++ if (((value & UFS_HCS_UPMCRS_MASK) >> UFS_HCS_UPMCRS_OFF) != 0x1) { ++ printf("check HCS.UPMCRS error, HCS = 0x%x\n", value); ++ return -1; ++ } ++ ++ /* set 0xc4 to 0x80 after mode changes */ ++ /* RX_ERR_STATUS */ ++ send_uic_command(DME_SET, 0x00c40004, 0x0, 0x80); ++ send_uic_command(DME_SET, 0x00c50004, 0x0, 0x01); ++ /* RX_ERR_STATUS */ ++ send_uic_command(DME_SET, 0x00c40005, 0x0, 0x80); ++ send_uic_command(DME_SET, 0x00c50005, 0x0, 0x01); ++ return UFS_OK; ++} ++ ++static int ufs_init(enum POWER_MODE mode, int hs_gear, int rate, int lane) ++{ ++ int ret; ++ ++ get_local_dwc_host(); ++ ++ if (dwc_host->is_init) ++ return 0; ++ ++ ufs_hardware_init(); ++ ++ ret = ufs_host_init(); ++ if (ret) { ++ printf("ufs host init fail. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ufs_device_init(); ++ if (ret) { ++ printf("ufs device init fail. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ufs_get_device_info(); ++ if (ret) { ++ printf("ufs get device info fail. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ufs_lu_init(); ++ if (ret) { ++ printf("ufs lu init fail. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ufs_set_ref_clk(); ++ if (ret) { ++ printf("ufs set ref clk fail. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ret = change_power_mode(mode, hs_gear, rate, lane); ++ if (ret) { ++ printf("ufs change mode fail. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ret = ufs_set_active_lun(DEFAULT_ACTIVE_LUN); ++ if (ret) { ++ printf("ufs set active lun fail. ret: %d\n", ret); ++ return ret; ++ } ++ ++ ufs_info_init(); ++ dwc_host->is_init = 1; ++ ++ return ret; ++} ++ ++int ufs_storage_init(void) ++{ ++ get_local_dwc_host(); ++ ++ if (ufs_init(DEFAULT_MODE, DEFAULT_GEAR, DEFAULT_RATE, DEFAULT_LANE) != 0) { ++ /* release resource */ ++ if (dwc_host->mem_pool) ++ free(dwc_host->mem_pool); ++ if (dwc_host->wr_buf) ++ free(dwc_host->wr_buf); ++ ++ dwc_host->mem_pool = NULL; ++ dwc_host->wr_buf = NULL; ++ printf("ufs release memory pool\n"); ++ } ++ ++ return 0; ++} ++ ++void ufs_reinit(void) ++{ ++ get_local_dwc_host(); ++ ++ if (dwc_host->mem_pool) ++ free(dwc_host->mem_pool); ++ if (dwc_host->wr_buf) ++ free(dwc_host->wr_buf); ++ ++ dwc_host->mem_pool = NULL; ++ dwc_host->wr_buf = NULL; ++ dwc_host->is_init = 0; ++ ++ ufs_storage_init(); ++} ++ +diff --git a/drivers/ufs/ufs_bsp.h b/drivers/ufs/ufs_bsp.h +new file mode 100644 +index 0000000..5e16ebc +--- /dev/null ++++ b/drivers/ufs/ufs_bsp.h +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __UFS_BSP_H__ ++#define __UFS_BSP_H__ ++ ++#include ++#include ++ ++#define COMBO_PHY_V120 ++#define UFS_USE_VENDOR_MPHY_TC ++#define CLOSE_CLK_GATING ++ ++#define REG_BASE_UFSHCI_ADDRESS 0x10010000 ++/* SYNOPSYS UniPro register */ ++#define UNIPRO_DME_RESET 0xD010 ++#define UNIPRO_DME_LAYBER_ENABLE 0xD000 ++ ++/* MPHY registers addr */ ++#define AD_DIF_P_LS_TIMEOUT_VAL 0x0003 ++#define PWM_PREPARE_TO 0x00000080 ++#define SKP_DET_SEL 0x0009 ++#define SKP_DET_SEL_EN 0x00000001 ++ ++#define MRX_EN 0x00F0 ++#define MRX_ENABLE (0x01 << 0) ++#define RX_SQ_VREF 0x00F1 ++#define RX_SQ_VREF_175 0x00000002 ++#define VCO_AUTO_CHG 0x00DF ++#define VCO_AUTO_CHG_EN (0x01 << 0) ++#define VCO_FORCE_ON_EN (0x01 << 1) ++ ++#define RG_PLL_PRE_DIV 0x00C2 ++#define RG_PLL_FBK_P 0x00C3 ++#define RG_PLL_FBK_S 0x00C4 ++#define RG_PLL_SWC_EN 0x00C9 ++#define RG_PLL_RXHSGR 0x00CD ++#define RG_PLL_RXLSGR 0x00CE ++#define RG_PLL_TXHSGR 0x00CF ++#define HS_R_A_FBK_P 0x41 ++#define HS_R_B_FBK_P 0x4C ++#define RG_PLL_TXLSGR 0x00D0 ++ ++#define RG_PLL_TXHS_EN 0x00C7 ++#define RG_PLL_TXHS_ENANBLE (0x01 << 0) ++#define RG_PLL_TXHS_EN_CONTROL (0x01 << 1) ++#define RG_PLL_TXLS_EN 0x00C8 ++#define RG_PLL_TXLS_ENABLE (0x01 << 0) ++#define RG_PLL_TXLS_EN_CONTROL (0x01 << 1) ++ ++#define CGE BIT(12) ++#define LP_AH8_PGE BIT(17) ++ ++static inline void dwc_ufs_write_reg(uint32_t reg_offset, uint32_t value) ++{ ++ writel(value, (uintptr_t)(REG_BASE_UFSHCI_ADDRESS + reg_offset)); ++} ++ ++static inline uint32_t dwc_ufs_read_reg(uint32_t reg_offset) ++{ ++ return readl((uintptr_t)(REG_BASE_UFSHCI_ADDRESS + reg_offset)); ++} ++ ++#endif +diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +old mode 100644 +new mode 100755 +diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c +index 6756155..f1cd40f 100644 +--- a/drivers/usb/gadget/f_dfu.c ++++ b/drivers/usb/gadget/f_dfu.c +@@ -320,21 +320,27 @@ static int state_dfu_idle(struct f_dfu *f_dfu, + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + ++ len = len > DFU_USB_BUFSIZ ? DFU_USB_BUFSIZ : len; ++ + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: +- if (len == 0) { +- f_dfu->dfu_state = DFU_STATE_dfuERROR; +- value = RET_STALL; +- break; ++ if (ctrl->bRequestType == USB_DIR_OUT) { ++ if (len == 0) { ++ f_dfu->dfu_state = DFU_STATE_dfuERROR; ++ value = RET_STALL; ++ break; ++ } ++ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; ++ f_dfu->blk_seq_num = w_value; ++ value = handle_dnload(gadget, len); + } +- f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; +- f_dfu->blk_seq_num = w_value; +- value = handle_dnload(gadget, len); + break; + case USB_REQ_DFU_UPLOAD: +- f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; +- f_dfu->blk_seq_num = 0; +- value = handle_upload(req, len); ++ if (ctrl->bRequestType == USB_DIR_IN) { ++ f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE; ++ f_dfu->blk_seq_num = 0; ++ value = handle_upload(req, len); ++ } + break; + case USB_REQ_DFU_ABORT: + /* no zlp? */ +@@ -423,11 +429,15 @@ static int state_dfu_dnload_idle(struct f_dfu *f_dfu, + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + ++ len = len > DFU_USB_BUFSIZ ? DFU_USB_BUFSIZ : len; ++ + switch (ctrl->bRequest) { + case USB_REQ_DFU_DNLOAD: +- f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; +- f_dfu->blk_seq_num = w_value; +- value = handle_dnload(gadget, len); ++ if (ctrl->bRequestType == USB_DIR_OUT) { ++ f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC; ++ f_dfu->blk_seq_num = w_value; ++ value = handle_dnload(gadget, len); ++ } + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; +@@ -510,13 +520,17 @@ static int state_dfu_upload_idle(struct f_dfu *f_dfu, + u16 len = le16_to_cpu(ctrl->wLength); + int value = 0; + ++ len = len > DFU_USB_BUFSIZ ? DFU_USB_BUFSIZ : len; ++ + switch (ctrl->bRequest) { + case USB_REQ_DFU_UPLOAD: +- /* state transition if less data then requested */ +- f_dfu->blk_seq_num = w_value; +- value = handle_upload(req, len); +- if (value >= 0 && value < len) +- f_dfu->dfu_state = DFU_STATE_dfuIDLE; ++ if (ctrl->bRequestType == USB_DIR_IN) { ++ /* state transition if less data then requested */ ++ f_dfu->blk_seq_num = w_value; ++ value = handle_upload(req, len); ++ if (value >= 0 && value < len) ++ f_dfu->dfu_state = DFU_STATE_dfuIDLE; ++ } + break; + case USB_REQ_DFU_ABORT: + f_dfu->dfu_state = DFU_STATE_dfuIDLE; +@@ -592,6 +606,8 @@ dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) + int value = 0; + u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + ++ len = len > DFU_USB_BUFSIZ ? DFU_USB_BUFSIZ : len; ++ + debug("w_value: 0x%x len: 0x%x\n", w_value, len); + debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n", + req_type, ctrl->bRequest, f_dfu->dfu_state); +@@ -606,7 +622,7 @@ dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl) + value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req); + + if (value >= 0) { +- req->length = value; ++ req->length = value > DFU_USB_BUFSIZ ? DFU_USB_BUFSIZ : value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { +diff --git a/drivers/usb/gadget/udc3/Makefile b/drivers/usb/gadget/udc3/Makefile +new file mode 100644 +index 0000000..576015c +--- /dev/null ++++ b/drivers/usb/gadget/udc3/Makefile +@@ -0,0 +1,3 @@ ++# Makefile - make file for onchiprom ++ ++obj-y += usb3_drv.o usb3_pcd.o usb3_intr.o usb3_pcd_intr.o usb3_prot.o +diff --git a/drivers/usb/gadget/udc3/debug.h b/drivers/usb/gadget/udc3/debug.h +new file mode 100644 +index 0000000..ce200b1 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/debug.h +@@ -0,0 +1,172 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __DEBUG_H__ ++#define __DEBUG_H__ ++ ++#include "types.h" ++#include "platform.h" ++#include ++ ++#ifdef DEBUG ++ ++#define MHZ (1000 * 1000) ++ ++#define CFG_CLK_OTHER (19200000) ++ ++/* ----------------------------------------------------------------------- ++ * Serial Configuration ++ */ ++#define CONFIG_PL011_CLOCK CFG_CLK_OTHER ++ ++#define CONFIG_CONS_INDEX 0 /* select the default console */ ++#define CONFIG_BAUDRATE 115200 ++#define CFG_SERIAL0 REG_BASE_UART6 ++ ++/* ++ * ARM PrimeCell UART's (PL010 & PL011) ++ * ------------------------------------ ++ * ++ * Definitions common to both PL010 & PL011 ++ * ++ */ ++#define UART_PL01X_DR 0x00 /* Data read or written from the interface. */ ++#define UART_PL01X_RSR 0x04 /* Receive status register (Read). */ ++#define UART_PL01X_ECR 0x04 /* Error clear register (Write). */ ++#define UART_PL01X_ECR 0x18 /* Flag register (Read only). */ ++ ++#define UART_PL01X_RSR_OE 0x08 ++#define UART_PL01X_RSR_BE 0x04 ++#define UART_PL01X_RSR_PE 0x02 ++#define UART_PL01X_RSR_FE 0x01 ++ ++#define UART_PL01X_FR_TXFE 0x80 ++#define UART_PL01X_FR_RXFF 0x40 ++#define UART_PL01X_FR_TXFF 0x20 ++#define UART_PL01X_FR_RXFE 0x10 ++#define UART_PL01X_FR_BUSY 0x08 ++#define UART_PL01X_FR_TMSK (UART_PL01X_FR_TXFF + UART_PL01X_FR_BUSY) ++ ++/* ++ * PL010 definitions ++ * ++ */ ++#define UART_PL010_LCRH 0x08 /* Line control register, high byte. */ ++#define UART_PL010_LCRM 0x0C /* Line control register, middle byte. */ ++#define UART_PL010_LCRL 0x10 /* Line control register, low byte. */ ++#define UART_PL010_CR 0x14 /* Control register. */ ++#define UART_PL010_IIR 0x1C /* Interrupt indentification register (Read). */ ++#define UART_PL010_ICR 0x1C /* Interrupt clear register (Write). */ ++#define UART_PL010_ILPR 0x20 /* IrDA low power counter register. */ ++ ++#define UART_PL010_CR_LPE (1 << 7) ++#define UART_PL010_CR_RTIE (1 << 6) ++#define UART_PL010_CR_TIE (1 << 5) ++#define UART_PL010_CR_RIE (1 << 4) ++#define UART_PL010_CR_MSIE (1 << 3) ++#define UART_PL010_CR_IIRLP (1 << 2) ++#define UART_PL010_CR_SIREN (1 << 1) ++#define UART_PL010_CR_UARTEN (1 << 0) ++ ++#define UART_PL010_LCRH_WLEN_8 (3 << 5) ++#define UART_PL010_LCRH_WLEN_7 (2 << 5) ++#define UART_PL010_LCRH_WLEN_6 (1 << 5) ++#define UART_PL010_LCRH_WLEN_5 (0 << 5) ++#define UART_PL010_LCRH_FEN (1 << 4) ++#define UART_PL010_LCRH_STP2 (1 << 3) ++#define UART_PL010_LCRH_EPS (1 << 2) ++#define UART_PL010_LCRH_PEN (1 << 1) ++#define UART_PL010_LCRH_BRK (1 << 0) ++ ++#define UART_PL010_BAUD_460800 1 ++#define UART_PL010_BAUD_230400 3 ++#define UART_PL010_BAUD_115200 7 ++#define UART_PL010_BAUD_57600 15 ++#define UART_PL010_BAUD_38400 23 ++#define UART_PL010_BAUD_19200 47 ++#define UART_PL010_BAUD_14400 63 ++#define UART_PL010_BAUD_9600 95 ++#define UART_PL010_BAUD_4800 191 ++#define UART_PL010_BAUD_2400 383 ++#define UART_PL010_BAUD_1200 767 ++/* ++ * PL011 definitions ++ * ++ */ ++#define UART_PL011_IBRD 0x24 ++#define UART_PL011_FBRD 0x28 ++#define UART_PL011_LCRH 0x2C ++#define UART_PL011_CR 0x30 ++#define UART_PL011_IMSC 0x38 ++#define UART_PL011_PERIPH_ID0 0xFE0 ++ ++#define UART_PL011_LCRH_SPS (1 << 7) ++#define UART_PL011_LCRH_WLEN_8 (3 << 5) ++#define UART_PL011_LCRH_WLEN_7 (2 << 5) ++#define UART_PL011_LCRH_WLEN_6 (1 << 5) ++#define UART_PL011_LCRH_WLEN_5 (0 << 5) ++#define UART_PL011_LCRH_FEN (1 << 4) ++#define UART_PL011_LCRH_STP2 (1 << 3) ++#define UART_PL011_LCRH_EPS (1 << 2) ++#define UART_PL011_LCRH_PEN (1 << 1) ++#define UART_PL011_LCRH_BRK (1 << 0) ++ ++#define UART_PL011_CR_CTSEN (1 << 15) ++#define UART_PL011_CR_RTSEN (1 << 14) ++#define UART_PL011_CR_OUT2 (1 << 13) ++#define UART_PL011_CR_OUT1 (1 << 12) ++#define UART_PL011_CR_RTS (1 << 11) ++#define UART_PL011_CR_DTR (1 << 10) ++#define UART_PL011_CR_RXE (1 << 9) ++#define UART_PL011_CR_TXE (1 << 8) ++#define UART_PL011_CR_LPE (1 << 7) ++#define UART_PL011_CR_IIRLP (1 << 2) ++#define UART_PL011_CR_SIREN (1 << 1) ++#define UART_PL011_CR_UARTEN (1 << 0) ++ ++#define UART_PL011_IMSC_OEIM (1 << 10) ++#define UART_PL011_IMSC_BEIM (1 << 9) ++#define UART_PL011_IMSC_PEIM (1 << 8) ++#define UART_PL011_IMSC_FEIM (1 << 7) ++#define UART_PL011_IMSC_RTIM (1 << 6) ++#define UART_PL011_IMSC_TXIM (1 << 5) ++#define UART_PL011_IMSC_RXIM (1 << 4) ++#define UART_PL011_IMSC_DSRMIM (1 << 3) ++#define UART_PL011_IMSC_DCDMIM (1 << 2) ++#define UART_PL011_IMSC_CTSMIM (1 << 1) ++#define UART_PL011_IMSC_RIMIM (1 << 0) ++ ++void console_init(); ++ ++void __xprintf(const char *fmt, va_list ap, void (*xputc)(unsigned n, void *cookie), void *cookie); ++void cprintf(const char *fmt, ...); ++ ++#define debug_init() console_init() ++#define DEBUG_PRINTF cprintf ++#define msglogstr(x) cprintf(x); ++ ++#else ++ ++#define debug_init() ++#define debug_printf(exp, ...) ++#define msglogstr(x) ++ ++#endif ++ ++#endif /* end of __DEBUG_H__ */ +diff --git a/drivers/usb/gadget/udc3/onchiprom.h b/drivers/usb/gadget/udc3/onchiprom.h +new file mode 100644 +index 0000000..8db4390 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/onchiprom.h +@@ -0,0 +1,80 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __ONCHIPROM_H__ ++#define __ONCHIPROM_H__ ++ ++#include "platform.h" ++ ++/* define vrl table of xloader loaded address & size */ ++#define VRL_TABLE_LOAD_ADDRESS REG_BASE_LP_RAM ++#define VRL_TABLE_LOAD_SIZE (4 * 1024) ++ ++/* define xloader loaded address & size */ ++#define XLOADER_IMAGE_LOAD_ADDRESS (VRL_TABLE_LOAD_ADDRESS + VRL_TABLE_LOAD_SIZE) ++#ifdef EDA_SPEEDUP ++#define XLOADER_IMAGE_LOAD_SIZE (4 * 1024) ++#else ++#define XLOADER_IMAGE_LOAD_SIZE (28 * 1024) ++#endif ++ ++#define ONCHIPROM_IMAGE_LOAD_MAX (100 * 1024) ++ ++/* define ufs work space area */ ++#define UFS_STATIC_WORK_SPACE_ADDRESS (REG_BASE_LP_RAM + 0x1C000) ++#define UFS_STATIC_WORK_SPACE_SIZE (0xE00) ++ ++/* define onchiprom static area */ ++#define ONCHIPROM_STATIC_AREA_ADDRESS (REG_BASE_LP_RAM + 0x1CE00) ++#define ONCHIPROM_STATIC_AREA_SIZE (0x100) ++ ++/* define secure engine static area */ ++#define SECURE_ENGINE_STATIC_AREA_ADDRESS (REG_BASE_LP_RAM + 0x1CF00) ++#define SECURE_ENGINE_STATIC_AREA_SIZE (0x100) ++ ++/* define secure engine work space address & size */ ++#define SECURE_ENGINE_WORK_SPACE_ADDRESS (REG_BASE_LP_RAM + 0x1D000) ++#define SECURE_ENGINE_WORK_SPACE_SIZE (0x1800) ++ ++/* define secure engine work space address & size */ ++#define USB_STATIC_WORK_SPACE_ADDRESS (REG_BASE_LP_RAM + 0x1E800) ++#define USB_STATIC_WORK_SPACE_SIZE (0x1800) ++ ++/* xloader image length address */ ++#define VRL_TABLE_IMAGE_LENGTH_OFFEST_ADDR (0xFFC) ++ ++#define LPRAM_MEMORY_MAP_OFFEST_ADDRESS SECURE_ENGINE_STATIC_AREA_ADDRESS ++#define LPRAM_MEMORY_MAP_OFFEST (REG_BASE_LP_RAM_ACORE - REG_BASE_LP_RAM) ++ ++/* static variable */ ++#define STATIC_ERROR_CODE_ADDR (ONCHIPROM_STATIC_AREA_ADDRESS + 0x4) ++#define STATIC_TIMER_STATUS_ADDR (ONCHIPROM_STATIC_AREA_ADDRESS + 0x8) ++#define STATIC_TIMER_COUNT_ADDR (ONCHIPROM_STATIC_AREA_ADDRESS + 0xC) ++#define STATIC_UFS_TAG_ADDR (ONCHIPROM_STATIC_AREA_ADDRESS + 0x10) ++ ++/* define boot mode */ ++#define BOOT_MODE_USB_DOWNLOAD 0 ++#define BOOT_MODE_NORMAL_BOOT 1 ++ ++#define BOOT_MODE_EMMC_BOOT 0 ++#define BOOT_MODE_UFS_BOOT 1 ++ ++typedef void (*PJUMPTOADDR)(void); /* addr of xloader */ ++ ++#endif /* end of __ONCHIPROM_H__ */ +diff --git a/drivers/usb/gadget/udc3/platform.h b/drivers/usb/gadget/udc3/platform.h +new file mode 100644 +index 0000000..48dcad0 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/platform.h +@@ -0,0 +1,50 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __PLATFORM_H__ ++#define __PLATFORM_H__ ++ ++/* fpga memory map */ ++#define REG_BASE_EMMC0 0xBF3FB000 ++ ++#define REG_BASE_USB_OTG 0x10180000 ++#define REG_BASE_USB_OTG_BC 0XBF200000 ++#define REG_BASE_LP_RAM 0x00020000 ++ ++#define REG_BASE_UFS_SYS 0xBF3B1000 ++#define REG_BASE_UFS 0xBF3B0000 ++ ++#define REG_BASE_PCTRL 0xA8A09000 ++#define REG_BASE_IOC 0xA896C000 ++#define REG_BASE_PMC 0x40231000 ++#define REG_BASE_UART6 0x40232000 ++#define REG_BASE_PERI_CRG 0x40235000 ++#define REG_BASE_LP_TIMER 0x4023E000 ++#define REG_BASE_SCTRL 0x4020A000 ++#define REG_BASE_AO_IOC 0x40211000 ++ ++#define REG_BASE_GPIO 0xA8A0B000 /* GPIO0~21 */ ++#define reg_gpio(x) (REG_BASE_GPIO + (0x1000 * (x))) ++#define REG_GPIO22 0x4020B000 ++ ++#define LP_RAM_SIZE (96*1024) ++ ++#define REG_BASE_LP_RAM_ACORE 0xFFF50000 ++ ++#endif /* end of __PLATFORM_H__ */ +diff --git a/drivers/usb/gadget/udc3/sys.h b/drivers/usb/gadget/udc3/sys.h +new file mode 100644 +index 0000000..d2a3c6e +--- /dev/null ++++ b/drivers/usb/gadget/udc3/sys.h +@@ -0,0 +1,71 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __SYS_H__ ++#define __SYS_H__ ++#include "types.h" ++static inline void writel(unsigned int val, unsigned long int addr) ++{ ++ (*(volatile unsigned int *)(addr)) = (val); ++} ++ ++static inline void writew(unsigned short val, unsigned long int addr) ++{ ++ (*(volatile unsigned short *)(addr)) = (val); ++} ++ ++static inline void writeb(unsigned char val, unsigned long int addr) ++{ ++ (*(volatile unsigned char *)(addr)) = (val); ++} ++ ++static inline unsigned int readl(unsigned long int addr) ++{ ++ return (*(volatile unsigned int *)(addr)); ++} ++ ++static inline unsigned short readw(unsigned long int addr) ++{ ++ return (*(volatile unsigned short *)(addr)); ++} ++ ++static inline unsigned char readb(unsigned long int addr) ++{ ++ return (*(volatile unsigned char *)(addr)); ++} ++ ++/* Set the bits of addr to 1, which in mask is 1 */ ++static inline void set_bits(unsigned int mask, unsigned long int addr) ++{ ++ (*(volatile unsigned int *)(addr)) |= mask; ++} ++ ++/* Clear the bits of addr to 0, which in mask is 1 */ ++static inline void clr_bits(unsigned int mask, unsigned long int addr) ++{ ++ (*(volatile unsigned int *)(addr)) &= ~(mask); ++} ++ ++/* Return True if all the bits of addr are 1, which is 1 in mask. ++ Else, return False. */ ++static inline unsigned int is_bits_set(unsigned int mask, unsigned long int addr) ++{ ++ return (((*(volatile unsigned int *)(addr)) & (mask)) == (mask)); ++} ++#endif /* end of __SYS_H__ */ +diff --git a/drivers/usb/gadget/udc3/types.h b/drivers/usb/gadget/udc3/types.h +new file mode 100644 +index 0000000..a3e140b +--- /dev/null ++++ b/drivers/usb/gadget/udc3/types.h +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __TYPES_H__ ++#define __TYPES_H__ ++ ++void udc_puts(const char *s); ++ ++#ifndef NULL ++#define NULL (void *)0 ++#endif ++ ++#define TRUE 1 ++#define FALSE 0 ++ ++#endif /* end of __TYPES_H__ */ ++ +diff --git a/drivers/usb/gadget/udc3/usb3.h b/drivers/usb/gadget/udc3/usb3.h +new file mode 100644 +index 0000000..84985d9 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3.h +@@ -0,0 +1,261 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __USB3_H__ ++#define __USB3_H__ ++ ++#include "asm/types.h" ++#include "types.h" ++ ++#define USB_VENDOR_ID 0x1D6B ++#define USB_PRODUCT_ID 0xD001 ++ ++#define STRING_LANGUAGE 0 ++#define STRING_MANUFACTURER 1 ++#define STRING_PRODUCT 2 ++ ++#define USB_SPEED_UNKNOWN 0x00 ++#define USB_SPEED_LOW 0x01 ++#define USB_SPEED_FULL 0x02 ++#define USB_SPEED_HIGH 0x03 ++#define USB_SPEED_VARIABLE 0x04 ++#define USB_SPEED_SUPER 0x05 ++ ++#define UDESC_DEVICE 0x01 ++#define UDESC_CONFIG 0x02 ++#define UDESC_STRING 0x03 ++#define UDESC_INTERFACE 0x04 ++#define UDESC_ENDPOINT 0x05 ++#define UDESC_SS_USB_COMPANION 0x30 ++#define UDESC_DEVICE_QUALIFIER 0x06 ++#define UDESC_BOS 0x0f ++#define UDESC_DEVICE_CAPABILITY 0x10 ++ ++#define ut_get_dir(a) ((a)&0x80) ++#define UT_WRITE 0x00 ++#define UT_READ 0x80 ++ ++#define ut_get_type(a) ((a)&0x60) ++#define UT_STANDARD 0x00 ++#define UT_CLASS 0x20 ++#define UT_VENDOR 0x40 ++ ++#define ut_get_recipient(a) ((a)&0x1f) ++#define UT_DEVICE 0x00 ++#define UT_INTERFACE 0x01 ++#define UT_ENDPOINT 0x02 ++#define UT_OTHER 0x03 ++ ++#define UR_GET_STATUS 0x00 ++#define UR_CLEAR_FEATURE 0x01 ++#define UR_SET_FEATURE 0x03 ++#define UR_SET_ADDRESS 0x05 ++#define UR_GET_DESCRIPTOR 0x06 ++#define UR_SET_DESCRIPTOR 0x07 ++#define UR_GET_CONFIG 0x08 ++#define UR_SET_CONFIG 0x09 ++#define UR_GET_INTERFACE 0x0a ++#define UR_SET_INTERFACE 0x0b ++#define UR_SYNCH_FRAME 0x0c ++#define UR_SET_SEL 0x30 ++#define UR_SET_ISOC_DELAY 0x31 ++ ++/* Feature numbers */ ++#define UF_ENDPOINT_HALT 0 ++#define UF_DEVICE_REMOTE_WAKEUP 1 ++#define UF_TEST_MODE 2 ++#define UF_DEVICE_B_HNP_ENABLE 3 ++#define UF_DEVICE_A_HNP_SUPPORT 4 ++#define UF_DEVICE_A_ALT_HNP_SUPPORT 5 ++#define UF_FUNCTION_SUSPEND 0 ++#define UF_U1_ENABLE 48 ++#define UF_U2_ENABLE 49 ++#define UF_LTM_ENABLE 50 ++ ++/* OTG feature selectors */ ++#define UOTG_B_HNP_ENABLE 3 ++#define UOTG_A_HNP_SUPPORT 4 ++#define UOTG_A_ALT_HNP_SUPPORT 5 ++#define UOTG_NTF_HOST_REL 51 ++#define UOTG_B3_RSP_ENABLE 52 ++ ++#define ue_get_dir(a) ((a)&0x80) ++#define ue_set_dir(a, d) ((a) | (((d)&1) << 7)) ++#define UE_DIR_IN 0x80 ++#define UE_DIR_OUT 0x00 ++#define UE_ADDR 0x0f ++#define ue_get_addr(a) ((a)&UE_ADDR) ++ ++/* * Maxpacket size for EP0, defined by USB3 spec */ ++#define USB3_MAX_EP0_SIZE 512 ++ ++/* * Maxpacket size for any EP, defined by USB3 spec */ ++#define USB3_MAX_PACKET_SIZE 1024 ++#define USB2_HS_MAX_PACKET_SIZE 512 ++#define USB2_FS_MAX_PACKET_SIZE 64 ++ ++#define USB3_BULK_IN_EP 1 ++#define USB3_BULK_OUT_EP 1 ++ ++typedef struct usb_device_request { ++ unsigned char bm_request_type; ++ unsigned char b_request; ++ unsigned short w_value; ++ unsigned short w_index; ++ unsigned short w_length; ++} usb_device_request_t; ++ ++#pragma pack(1) ++/* * USB_DT_DEVICE: Device descriptor */ ++typedef struct usb_device_descriptor { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ ++ unsigned short bcd_usb; ++#define USB_CLASS_COMM 0x02 ++#define USB_CLASS_VENDOR_SPEC 0xFF ++#define USB_SC_VENDOR_SPEC 0xFF ++#define USB_PR_VENDOR_SPEC 0xFF ++ unsigned char b_device_class; ++ unsigned char b_device_sub_class; ++ unsigned char b_device_protocol; ++ unsigned char b_max_packet_size0; ++ unsigned short id_vendor; ++ unsigned short id_product; ++ unsigned short bcd_device; ++ unsigned char i_manufacturer; ++ unsigned char i_product; ++ unsigned char i_serial_number; ++ unsigned char b_num_configurations; ++} usb_device_descriptor_t; ++ ++/* USB_DT_CONFIG: Config descriptor */ ++typedef struct usb_config_descriptor { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ ++ unsigned short w_total_length; ++ unsigned char b_num_interfaces; ++#define CONFIG_VALUE 1 ++ unsigned char b_configuration_value; ++ unsigned char i_configuration; ++#define USB_CONFIG_ATT_ONE (1 << 7) ++ unsigned char bm_attributes; ++#define USB_CONFIG_VBUS_DRAW (0xFA) ++ unsigned char b_max_power; ++} usb_config_descriptor_t; ++ ++/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */ ++typedef struct usb_qualifier_descriptor { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ ++ unsigned short bcd_usb; ++ unsigned char b_device_class; ++ unsigned char b_device_sub_class; ++ unsigned char b_device_protocol; ++ unsigned char b_max_packet_size0; ++ unsigned char b_num_configurations; ++ unsigned char b_reserved; ++} usb_qualifier_descriptor_t; ++ ++/* USB_DT_INTERFACE: Interface descriptor */ ++typedef struct usb_interface_descriptor { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ ++ unsigned char b_interface_number; ++ unsigned char b_alternate_setting; ++ unsigned char b_num_endpoints; ++ unsigned char b_interface_class; ++ unsigned char b_interface_subclass; ++ unsigned char b_interface_protocol; ++ unsigned char i_interface; ++} usb_interface_descriptor_t; ++ ++/* USB_DT_ENDPOINT: Endpoint descriptor */ ++typedef struct usb_endpoint_descriptor { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ ++ unsigned char b_endpoint_address; ++ unsigned char bm_attributes; ++#define USB_ENDPOINT_XFER_CONTROL 0x00 ++#define USB_ENDPOINT_XFER_ISOC 0x01 ++#define USB_ENDPOINT_XFER_BULK 0x02 ++#define USB_ENDPOINT_XFER_INT 0x03 ++ unsigned short w_max_packet_size; ++ unsigned char b_interval; ++} usb_endpoint_descriptor_t; ++ ++/* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */ ++typedef struct usb_ss_ep_comp_descriptor { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ ++ unsigned char b_max_burst; ++ unsigned char bm_attributes; ++ unsigned short w_bytes_per_interval; ++} usb_ss_ep_comp_descriptor_t; ++ ++/* WUSB BOS Descriptor (Binary device Object Store) */ ++typedef struct wusb_bos_desc { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ unsigned short w_total_length; ++ unsigned char b_num_device_caps; ++} wusb_bos_desc_t; ++ ++#define USB_DEVICE_CAPABILITY_20_EXTENSION 0x02 ++typedef struct usb_dev_cap_20_ext_desc { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ unsigned char b_devcapability_type; ++#define USB_20_EXT_LPM 0x02 ++ unsigned int bm_attributes; ++} usb_dev_cap_20_ext_desc_t; ++ ++#define USB_DEVICE_CAPABILITY_SS_USB 0x03 ++typedef struct usb_dev_cap_ss_usb { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ unsigned char b_devcapability_type; ++#define USB_DC_SS_USB_LTM_CAPABLE 0x02 ++ unsigned char bm_attributes; ++#define USB_DC_SS_USB_SPEED_SUPPORT_LOW 0x01 ++#define USB_DC_SS_USB_SPEED_SUPPORT_FULL 0x02 ++#define USB_DC_SS_USB_SPEED_SUPPORT_HIGH 0x04 ++#define USB_DC_SS_USB_SPEED_SUPPORT_SS 0x08 ++ unsigned int w_speeds_supported; ++ unsigned char b_functionality_support; ++ unsigned char b_u1dev_exit_lat; ++ unsigned int w_u2dev_exit_lat; ++} usb_dev_cap_ss_usb_t; ++ ++#define USB_DEVICE_CAPABILITY_CONTAINER_ID 0x04 ++typedef struct usb_dev_cap_container_id { ++ unsigned char b_length; ++ unsigned char b_descriptor_type; ++ unsigned char b_devcapability_type; ++ unsigned char b_reserved; ++ unsigned char container_id[16]; ++} usb_dev_cap_container_id_t; ++#pragma pack() ++ ++#endif /* __USB3_H__ */ +diff --git a/drivers/usb/gadget/udc3/usb3_drv.c b/drivers/usb/gadget/udc3/usb3_drv.c +new file mode 100644 +index 0000000..6a019c7 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_drv.c +@@ -0,0 +1,770 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include "usb3_drv.h" ++#include "common.h" ++ ++#include ++#include ++#include "../../../phy/vendor/usb.h" ++ ++#include ++ ++void usb3_handle_event(usb3_device_t *dev); ++void usb3_enable_device_interrupts(usb3_device_t *dev); ++void usb3_dis_flush_eventbuf_intr(usb3_device_t *dev); ++void usb3_init_eventbuf(usb3_device_t *dev, unsigned int size, phys_addr_t dma_addr); ++ ++unsigned int usb3_rd32(volatile unsigned int *addr) ++{ ++ return *(volatile unsigned int *)(addr); ++} ++ ++void usb3_wr32(volatile unsigned int *addr, unsigned int val) ++{ ++ *(volatile unsigned int *)(addr) = val; ++ udelay(200); /* delay 200us */ ++} ++ ++unsigned int usb3_is_host_mode(usb3_device_t *dev) ++{ ++ return usb3_rd32(&dev->core_global_regs->gsts) & 0x01; ++} ++ ++void pcd_epinit(usb3_pcd_t *pcd) ++{ ++ usb3_pcd_ep_t *ep; ++ ++ /* Init EP0 */ ++ do { ++ ep = &pcd->ep0; ++ ++ ep->pcd = pcd; ++ ep->stopped = 1; ++ ep->is_in = 0; ++ ep->active = 0; ++ ep->phys = 0; ++ ep->num = 0; ++ ep->tx_fifo_num = 0; ++ ep->out_ep_reg = &pcd->out_ep_regs[0]; ++ ep->in_ep_reg = &pcd->in_ep_regs[0]; ++ ++ ep->type = USB3_EP_TYPE_CONTROL; ++ ep->maxburst = 0; ++ ep->maxpacket = 64; /* 64 bytes */ ++ ep->send_zlp = 0; ++ ++ ep->req.length = 0; ++ ep->req.actual = 0; ++ ++ pcd->ep0_req.length = 0; ++ pcd->ep0_req.actual = 0; ++ } while (0); ++ ++ /* Init EP1-OUT */ ++ do { ++ ep = &pcd->out_ep; ++ ++ ep->pcd = pcd; ++ ep->stopped = 1; ++ ep->is_in = 0; ++ ep->active = 0; ++ ep->phys = USB3_BULK_OUT_EP << 1; ++ ep->num = 1; ++ ep->tx_fifo_num = 0; ++ ep->out_ep_reg = &pcd->out_ep_regs[USB3_BULK_OUT_EP]; ++ ++ /* Bulk EP is activated */ ++ ep->type = USB3_EP_TYPE_BULK; ++ ep->maxburst = 0; ++ ep->maxpacket = USB2_FS_MAX_PACKET_SIZE; ++ ep->send_zlp = 0; ++ ++ ep->req.length = 0; ++ ep->req.actual = 0; ++ } while (0); ++ ++ /* Init EP1-IN */ ++ do { ++ ep = &pcd->in_ep; ++ ++ ep->pcd = pcd; ++ ep->stopped = 1; ++ ep->is_in = 1; ++ ep->active = 0; ++ ep->phys = (USB3_BULK_IN_EP << 1) | 1; ++ ep->num = 1; ++ ep->tx_fifo_num = USB3_BULK_IN_EP; ++ ep->in_ep_reg = &pcd->in_ep_regs[USB3_BULK_IN_EP]; ++ ++ /* Bulk EP is activated */ ++ ep->type = USB3_EP_TYPE_BULK; ++ ep->maxburst = 0; ++ ep->maxpacket = USB2_FS_MAX_PACKET_SIZE; ++ ep->send_zlp = 0; ++ ++ ep->req.length = 0; ++ ep->req.actual = 0; ++ } while (0); ++ ++ pcd->ep0state = EP0_IDLE; ++} ++ ++void usb3_set_address(usb3_pcd_t *pcd, unsigned int addr) ++{ ++ unsigned int dcfg; ++ ++ dcfg = usb3_rd32(&pcd->dev_global_regs->dcfg); ++ dcfg &= ~USB3_DCFG_DEVADDR_BITS; ++ dcfg |= addr << USB3_DCFG_DEVADDR_SHIFT; ++ usb3_wr32(&pcd->dev_global_regs->dcfg, dcfg); ++} ++ ++#define RAM_WIDTH 8 ++#define RAM_RX_DEPTH 4096 ++#define RAM_TX0_DEPTH 1024 ++#define RAM_TX1_DEPTH 2048 ++void usb3_set_tx_fifo_size(usb3_device_t *dev) ++{ ++ usb3_core_global_regs_t *global_regs = dev->core_global_regs; ++ unsigned int prev_start = 0; ++ /* Set 1K for tx fifo0 */ ++ usb3_wr32(&global_regs->gtxfifosiz[0], ++ ((RAM_TX0_DEPTH / RAM_WIDTH) << USB3_FIFOSZ_DEPTH_SHIFT) | (prev_start << USB3_FIFOSZ_STARTADDR_SHIFT)); ++ ++ prev_start += RAM_TX0_DEPTH / RAM_WIDTH; ++ /* Set 2K for tx fifo1 */ ++ usb3_wr32(&global_regs->gtxfifosiz[1], ++ ((RAM_TX1_DEPTH / RAM_WIDTH) << USB3_FIFOSZ_DEPTH_SHIFT) | (prev_start << USB3_FIFOSZ_STARTADDR_SHIFT)); ++} ++ ++void usb3_set_rx_fifo_size(usb3_device_t *dev) ++{ ++ usb3_core_global_regs_t *global_regs = dev->core_global_regs; ++ /* Set 4K for rx fifo */ ++ usb3_wr32(&global_regs->grxfifosiz[0], ((RAM_RX_DEPTH / RAM_WIDTH) << USB3_FIFOSZ_DEPTH_SHIFT)); ++} ++ ++void usb3_resume_usb2_phy(usb3_pcd_t *pcd) ++{ ++ unsigned int usb2phycfg; ++ ++ usb2phycfg = usb3_rd32(&pcd->usb3_dev->core_global_regs->gusb2phycfg[0]); ++ usb2phycfg |= USB3_USB2PHYCFG_SUS_PHY_BIT; ++ usb3_wr32(&pcd->usb3_dev->core_global_regs->gusb2phycfg[0], usb2phycfg); ++} ++ ++void usb3_resume_usb3_phy(usb3_pcd_t *pcd) ++{ ++ unsigned int pipectl; ++ ++ pipectl = usb3_rd32(&pcd->usb3_dev->core_global_regs->gusb3pipectl[0]); ++ pipectl |= USB3_PIPECTL_SUS_PHY_BIT; ++ usb3_wr32(&pcd->usb3_dev->core_global_regs->gusb3pipectl[0], pipectl); ++} ++ ++void usb3_accept_u1(usb3_pcd_t *pcd) ++{ ++ unsigned int dctl; ++ ++ dctl = usb3_rd32(&pcd->dev_global_regs->dctl); ++ dctl |= USB3_DCTL_ACCEPT_U1_EN_BIT; ++ usb3_wr32(&pcd->dev_global_regs->dctl, dctl); ++} ++ ++void usb3_accept_u2(usb3_pcd_t *pcd) {} ++ ++void usb3_enable_u1(usb3_pcd_t *pcd) ++{ ++ unsigned int dctl; ++ ++ dctl = usb3_rd32(&pcd->dev_global_regs->dctl); ++ dctl |= USB3_DCTL_INIT_U1_EN_BIT; ++ usb3_wr32(&pcd->dev_global_regs->dctl, dctl); ++} ++ ++void usb3_enable_u2(usb3_pcd_t *pcd) {} ++ ++void usb3_disable_u1(usb3_pcd_t *pcd) {} ++ ++void usb3_disable_u2(usb3_pcd_t *pcd) ++{ ++ unsigned int dctl; ++ ++ dctl = usb3_rd32(&pcd->dev_global_regs->dctl); ++ dctl &= ~USB3_DCTL_INIT_U2_EN_BIT; ++ usb3_wr32(&pcd->dev_global_regs->dctl, dctl); ++} ++ ++unsigned int usb3_u1_enabled(usb3_pcd_t *pcd) ++{ ++ unsigned int dctl; ++ ++ dctl = usb3_rd32(&pcd->dev_global_regs->dctl); ++ return !!(dctl & USB3_DCTL_INIT_U1_EN_BIT); ++} ++ ++unsigned int usb3_u2_enabled(usb3_pcd_t *pcd) ++{ ++ unsigned int dctl; ++ ++ dctl = usb3_rd32(&pcd->dev_global_regs->dctl); ++ return !!(dctl & USB3_DCTL_INIT_U2_EN_BIT); ++} ++ ++void usb3_dep_cstall(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg) ++{ ++ /* Start the command */ ++ usb3_wr32(&ep_reg->depcmd, USB3_EPCMD_CLR_STALL | USB3_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, USB3_EPCMD_ACT_BIT, 0); ++} ++ ++void usb3_dep_sstall(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg) ++{ ++ /* Start the command */ ++ usb3_wr32(&ep_reg->depcmd, USB3_EPCMD_SET_STALL | USB3_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, USB3_EPCMD_ACT_BIT, 0); ++} ++ ++unsigned int handshake(usb3_device_t *dev, volatile unsigned int *ptr, unsigned int mask, unsigned int done) ++{ ++ unsigned int usec = 1000; ++ unsigned int result; ++ ++ do { ++ result = usb3_rd32(ptr); ++ if ((result & mask) == done) ++ return 1; ++ ++ udelay(1); ++ usec -= 1; ++ } while (usec > 0); ++ ++ return 0; ++} ++ ++void usb3_fill_desc(usb3_dma_desc_t *desc, phys_addr_t dma_addr, unsigned int dma_len, unsigned int stream, unsigned int type, ++ unsigned int ctrlbits, int own) ++{ ++ dma_addr = map_to_dma_addr(dma_addr); ++ ++ desc->bptl = (phys_addr_t)(dma_addr & 0xffffffffU); ++ desc->bpth = 0; ++ desc->status &= ~USB3_DSCSTS_XFRCNT_BITS; ++ desc->status |= ((dma_len << USB3_DSCSTS_XFRCNT_SHIFT) & USB3_DSCSTS_XFRCNT_BITS); ++ /* Note: If type is 0, leave original control bits intact (for isoc) */ ++ if (type) ++ desc->control = type << USB3_DSCCTL_TRBCTL_SHIFT; ++ ++ desc->control |= (stream << USB3_DSCCTL_STRMID_SOFN_SHIFT) | ctrlbits; ++ ++ /* Must do this last! */ ++ if (own) ++ desc->control |= USB3_DSCCTL_HWO_BIT; ++ flush_dcache_all(); ++} ++ ++ ++void usb3_dep_startnewcfg(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int rsrcidx) ++{ ++ /* Start the command */ ++ usb3_wr32(&ep_reg->depcmd, ++ (rsrcidx << USB3_EPCMD_XFER_RSRC_IDX_SHIFT) | USB3_EPCMD_START_NEW_CFG | USB3_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, USB3_EPCMD_ACT_BIT, 0); ++} ++ ++void usb3_dep_cfg(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int depcfg0, unsigned int depcfg1, unsigned int depcfg2) ++{ ++ /* Set param 2 */ ++ usb3_wr32(&ep_reg->depcmdpar2, depcfg2); ++ ++ /* Set param 1 */ ++ usb3_wr32(&ep_reg->depcmdpar1, depcfg1); ++ ++ /* Set param 0 */ ++ usb3_wr32(&ep_reg->depcmdpar0, depcfg0); /* mm 0x1018c808 0x200 */ ++ ++ /* Start the command */ ++ usb3_wr32(&ep_reg->depcmd, USB3_EPCMD_SET_EP_CFG | USB3_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, USB3_EPCMD_ACT_BIT, 0); ++} ++ ++void usb3_dep_xfercfg(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int depstrmcfg) ++{ ++ /* Set param 0 */ ++ usb3_wr32(&ep_reg->depcmdpar0, depstrmcfg); ++ ++ /* Start the command */ ++ usb3_wr32(&ep_reg->depcmd, USB3_EPCMD_SET_XFER_CFG | USB3_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, USB3_EPCMD_ACT_BIT, 0); ++} ++ ++unsigned char usb3_dep_startxfer(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, phys_addr_t dma_addr, unsigned int stream_or_uf) ++{ ++ unsigned int depcmd; ++ ++ dma_addr = map_to_dma_addr(dma_addr); ++ ++ /* Set param 1 */ ++ usb3_wr32(&ep_reg->depcmdpar1, dma_addr & 0xffffffffU); ++ ++ /* Set param 0 */ ++ usb3_wr32(&ep_reg->depcmdpar0, 0); ++ ++ usb3_wr32(&ep_reg->depcmd, ++ (stream_or_uf << USB3_EPCMD_STR_NUM_OR_UF_SHIFT) | USB3_EPCMD_START_XFER | USB3_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, USB3_EPCMD_ACT_BIT, 0); ++ ++ depcmd = usb3_rd32(&ep_reg->depcmd); ++ ++ return (depcmd >> USB3_EPCMD_XFER_RSRC_IDX_SHIFT) & ++ (USB3_EPCMD_XFER_RSRC_IDX_BITS >> USB3_EPCMD_XFER_RSRC_IDX_SHIFT); ++} ++ ++void usb3_dep_updatexfer(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int tri) ++{ ++ /* Start the command */ ++ usb3_wr32(&ep_reg->depcmd, (tri << USB3_EPCMD_XFER_RSRC_IDX_SHIFT) | USB3_EPCMD_UPDATE_XFER | USB3_EPCMD_ACT_BIT); ++ ++ /* Wait for command completion */ ++ handshake(pcd->usb3_dev, &ep_reg->depcmd, USB3_EPCMD_ACT_BIT, 0); ++} ++ ++void usb3_enable_ep(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) ++{ ++ unsigned int ep_index_num, dalepena; ++ ++ ep_index_num = ep->num * 0x2; ++ if (ep->is_in) ++ ep_index_num += 1; ++ ++ dalepena = usb3_rd32(&pcd->dev_global_regs->dalepena); ++ /* If we have already enabled this EP, leave it alone ++ * (shouldn't happen) ++ */ ++ if (dalepena & (1 << ep_index_num)) ++ return; ++ ++ dalepena |= 1 << ep_index_num; ++ usb3_wr32(&pcd->dev_global_regs->dalepena, dalepena); ++ return; ++} ++ ++void usb3_dis_usb2_suspend(usb3_pcd_t *pcd) ++{ ++ unsigned int usb2phycfg; ++ ++ if (pcd->speed == USB_SPEED_SUPER) ++ return; ++ ++ usb2phycfg = usb3_rd32(&pcd->usb3_dev->core_global_regs->gusb2phycfg[0]); ++ usb2phycfg &= ~USB3_USB2PHYCFG_SUS_PHY_BIT; ++ usb2phycfg &= ~USB3_USB2PHYCFG_ENBL_SLP_M_BIT; ++ usb3_wr32(&pcd->usb3_dev->core_global_regs->gusb2phycfg[0], usb2phycfg); ++} ++ ++void usb3_ep0_activate(usb3_pcd_t *pcd) ++{ ++ unsigned int diepcfg0, doepcfg0, diepcfg1, doepcfg1; ++ unsigned int diepcfg2 = 0; ++ unsigned int doepcfg2 = 0; ++ usb3_dev_ep_regs_t *ep_reg; ++ ++ diepcfg0 = USB3_EP_TYPE_CONTROL << USB3_EPCFG0_EPTYPE_SHIFT; ++ diepcfg1 = ++ USB3_EPCFG1_XFER_CMPL_BIT | USB3_EPCFG1_XFER_IN_PROG_BIT | USB3_EPCFG1_XFER_NRDY_BIT | USB3_EPCFG1_EP_DIR_BIT; ++ ++ doepcfg0 = USB3_EP_TYPE_CONTROL << USB3_EPCFG0_EPTYPE_SHIFT; ++ doepcfg1 = USB3_EPCFG1_XFER_CMPL_BIT | USB3_EPCFG1_XFER_IN_PROG_BIT | USB3_EPCFG1_XFER_NRDY_BIT; ++ ++ /* Default to MPS of 512 (will reconfigure after ConnectDone event) */ ++ diepcfg0 |= 512 << USB3_EPCFG0_MPS_SHIFT; ++ /* Default to MPS of 512 (will reconfigure after ConnectDone event) */ ++ doepcfg0 |= 512 << USB3_EPCFG0_MPS_SHIFT; ++ ++ diepcfg0 |= pcd->ep0.tx_fifo_num << USB3_EPCFG0_TXFNUM_SHIFT; ++ ++ /* Issue "DEPCFG" command to EP0-OUT */ ++ ++ ep_reg = &pcd->out_ep_regs[0]; ++ usb3_dis_usb2_suspend(pcd); /* mm 0x1018c200 0x102400 */ ++ /* If core is version 1.09a or later */ ++ /* Must issue DEPSTRTNEWCFG command first */ ++ usb3_dep_startnewcfg(pcd, ep_reg, 0); ++ ++ usb3_dep_cfg(pcd, ep_reg, doepcfg0, doepcfg1, doepcfg2); ++ ++ /* Issue "DEPSTRMCFG" command to EP0-OUT */ ++ ++ /* One stream */ ++ usb3_dep_xfercfg(pcd, ep_reg, 1); ++ ++ /* Issue "DEPCFG" command to EP0-IN */ ++ ++ ep_reg = &pcd->in_ep_regs[0]; ++ usb3_dep_cfg(pcd, ep_reg, diepcfg0, diepcfg1, diepcfg2); ++ ++ /* Issue "DEPSTRMCFG" command to EP0-IN */ ++ ++ /* One stream */ ++ usb3_dep_xfercfg(pcd, ep_reg, 1); ++ ++ pcd->ep0.active = 1; ++} ++ ++void usb3_ep_activate(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) ++{ ++ usb3_dev_ep_regs_t *ep_reg, *ep0_reg; ++ unsigned int depcfg0; ++ unsigned int depcfg1; ++ unsigned int depcfg2 = 0; ++ /* ++ * Get the appropriate EP registers ++ */ ++ if (ep->is_in) ++ ep_reg = ep->in_ep_reg; ++ else ++ ep_reg = ep->out_ep_reg; ++ ++ /* If this is first EP enable (ie. start of a new configuration) */ ++ if (!pcd->eps_enabled) { ++ pcd->eps_enabled = 1; ++ ++ /* NOTE: When setting a new configuration, we must issue a ++ * "DEPCFG" command to physical EP1 (logical EP0-IN) first. ++ * This resets the core's Tx FIFO mapping table ++ */ ++ depcfg0 = USB3_EP_TYPE_CONTROL << USB3_EPCFG0_EPTYPE_SHIFT; ++ depcfg0 |= USB3_CFG_ACTION_MODIFY << USB3_EPCFG0_CFG_ACTION_SHIFT; ++ depcfg1 = USB3_EPCFG1_XFER_CMPL_BIT | USB3_EPCFG1_XFER_NRDY_BIT | USB3_EPCFG1_EP_DIR_BIT; ++ ++ switch (pcd->speed) { ++ case USB_SPEED_SUPER: ++ /* Default to MPS of 512 (will reconfigure after ConnectDone event) */ ++ depcfg0 |= 512 << USB3_EPCFG0_MPS_SHIFT; ++ break; ++ ++ case USB_SPEED_HIGH: ++ case USB_SPEED_FULL: ++ /* Default to MPS of 64 (will reconfigure after ConnectDone event) */ ++ depcfg0 |= 64 << USB3_EPCFG0_MPS_SHIFT; ++ break; ++ ++ case USB_SPEED_LOW: ++ /* Default to MPS of 8 (will reconfigure after ConnectDone event) */ ++ depcfg0 |= 8 << USB3_EPCFG0_MPS_SHIFT; ++ break; ++ default: ++ break; ++ } ++ ++ ep0_reg = &pcd->in_ep_regs[0]; ++ usb3_dep_cfg(pcd, ep0_reg, depcfg0, depcfg1, 0); ++ ++ /* If core is version 1.09a or later */ ++ /* Must issue DEPSTRTNEWCFG command first */ ++ ep0_reg = &pcd->out_ep_regs[0]; ++ usb3_dep_startnewcfg(pcd, ep0_reg, 0x2); ++ } ++ ++ /* ++ * Issue "DEPCFG" command to EP ++ */ ++ depcfg0 = ep->type << USB3_EPCFG0_EPTYPE_SHIFT; ++ depcfg0 |= ep->maxpacket << USB3_EPCFG0_MPS_SHIFT; ++ ++ if (ep->is_in) ++ depcfg0 |= ep->tx_fifo_num << USB3_EPCFG0_TXFNUM_SHIFT; ++ ++ depcfg0 |= ep->maxburst << USB3_EPCFG0_BRSTSIZ_SHIFT; ++ ++ depcfg1 = ep->num << USB3_EPCFG1_EP_NUM_SHIFT; ++ ++ if (ep->is_in) ++ depcfg1 |= USB3_EPCFG1_EP_DIR_BIT; ++ ++ depcfg1 |= USB3_EPCFG1_XFER_CMPL_BIT; ++ ++ usb3_dep_cfg(pcd, ep_reg, depcfg0, depcfg1, depcfg2); ++ ++ /* ++ * Issue "DEPSTRMCFG" command to EP ++ */ ++ /* Setting 1 stream resource */ ++ usb3_dep_xfercfg(pcd, ep_reg, 1); ++ ++ /* Enable EP in DALEPENA reg */ ++ usb3_enable_ep(pcd, ep); ++ ++ ep->active = 1; ++} ++ ++void usb3_ep0_out_start(usb3_pcd_t *pcd) ++{ ++ usb3_dev_ep_regs_t *ep_reg; ++ usb3_dma_desc_t *desc; ++ unsigned int desc_dma; ++ unsigned char tri; ++ ++ /* Get the SETUP packet DMA Descriptor (TRB) */ ++ desc = pcd->ep0_setup_desc; ++ desc_dma = (phys_addr_t)pcd->ep0_setup_desc; ++ ++ /* DMA Descriptor setup */ ++ usb3_fill_desc(desc, (phys_addr_t)pcd->ep0_setup_pkt, pcd->ep0.maxpacket, 0, USB3_DSCCTL_TRBCTL_SETUP, ++ USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_LST_BIT, 1); ++ ++ ep_reg = &pcd->out_ep_regs[0]; ++ ++ /* Issue "DEPSTRTXFER" command to EP0-OUT */ ++ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ pcd->ep0.tri_out = tri; ++} ++ ++void usb3_core_dev_init(usb3_device_t *dev) ++{ ++ usb3_core_global_regs_t *global_regs = dev->core_global_regs; ++ usb3_pcd_t *pcd = &dev->pcd; ++ usb3_dev_global_regs_t *dev_global_regs = pcd->dev_global_regs; ++ unsigned int temp_t; ++ ++ /* Soft-reset the core */ ++ do { ++ temp_t = usb3_rd32(&dev_global_regs->dctl); ++ temp_t &= ~USB3_DCTL_RUN_STOP_BIT; ++ temp_t |= USB3_DCTL_CSFT_RST_BIT; ++ usb3_wr32(&dev_global_regs->dctl, temp_t); ++ do { ++ udelay(1); ++ temp_t = usb3_rd32(&dev_global_regs->dctl); ++ } while (temp_t & USB3_DCTL_CSFT_RST_BIT); ++ ++ /* Wait for at least 3 PHY clocks */ ++ udelay(1); ++ } while (0); ++ ++ pcd->link_state = 0; ++ ++ temp_t = usb3_rd32(&global_regs->gusb2phycfg[0]); ++ temp_t &= ~USB3_USB2PHYCFG_USB_TRD_TIM_BITS; ++ temp_t |= 9 << USB3_USB2PHYCFG_USB_TRD_TIM_SHIFT; /* Set Turnaround Time = 9 (8-bit UTMI+ / ULPI) */ ++ usb3_wr32(&global_regs->gusb2phycfg[0], temp_t); ++ ++ temp_t = 0x13802004; ++ usb3_wr32(&global_regs->gctl, temp_t); ++ ++ usb_info("evnt buffer addr: 0x%x\n", dev->event_buf); ++ ++ usb3_init_eventbuf(dev, USB3_EVENT_BUF_SIZE, (phys_addr_t)dev->event_buf); ++ dev->event_ptr = dev->event_buf; ++ ++ /* Set speed to Super */ ++ temp_t = usb3_rd32(&pcd->dev_global_regs->dcfg); ++ temp_t &= ~(USB3_DCFG_DEVSPD_BITS << USB3_DCFG_DEVSPD_SHIFT); ++ temp_t |= USB3_SPEED_HS_PHY_30MHZ_OR_60MHZ << USB3_DCFG_DEVSPD_SHIFT; ++ usb3_wr32(&pcd->dev_global_regs->dcfg, temp_t); ++ ++ /* If LPM enable was requested */ ++ temp_t = usb3_rd32(&pcd->dev_global_regs->dcfg); ++ temp_t |= USB3_DCFG_LPM_CAP_BIT; ++ usb3_wr32(&pcd->dev_global_regs->dcfg, temp_t); /* mm 0x1018c700 0x480804 */ ++ ++ /* Set Nump */ ++ temp_t = usb3_rd32(&pcd->dev_global_regs->dcfg); ++ temp_t &= ~USB3_DCFG_NUM_RCV_BUF_BITS; ++ temp_t |= 0x10 << USB3_DCFG_NUM_RCV_BUF_SHIFT; ++ usb3_wr32(&pcd->dev_global_regs->dcfg, temp_t); ++ ++ usb3_set_address(pcd, 0); ++ ++ /* disable Phy suspend */ ++ usb3_resume_usb3_phy(pcd); ++ usb3_resume_usb2_phy(pcd); /* mm 0x1018c200 0x102540 */ ++ /* Enable Global and Device interrupts */ ++ usb3_enable_device_interrupts(dev); ++ ++ /* Activate EP0 */ ++ usb3_ep0_activate(pcd); ++ ++ /* Start EP0 to receive SETUP packets */ ++ usb3_ep0_out_start(pcd); ++ ++ /* Enable EP0-OUT/IN in DALEPENA register */ ++ usb3_wr32(&pcd->dev_global_regs->dalepena, 0x3); ++ ++ /* Set Run/Stop bit */ ++ temp_t = usb3_rd32(&pcd->dev_global_regs->dctl); ++ temp_t |= USB3_DCTL_RUN_STOP_BIT; ++ usb3_wr32(&pcd->dev_global_regs->dctl, temp_t); ++} ++ ++void usb3_pcd_init(usb3_device_t *dev) ++{ ++ usb3_pcd_t *pcd = &dev->pcd; ++ ++ pcd->usb3_dev = dev; ++ pcd->speed = USB_SPEED_UNKNOWN; ++ ++ /* Initialize EP structures */ ++ pcd_epinit(pcd); ++ ++ /* Initialize the Core (also enables interrupts and sets Run/Stop bit) */ ++ usb3_core_dev_init(dev); ++} ++ ++void usb3_common_init(usb3_device_t *dev, volatile unsigned char *base) ++{ ++ usb3_pcd_t *pcd; ++ ++ dev->core_global_regs = (usb3_core_global_regs_t *)(base + USB3_CORE_GLOBAL_REG_OFFSET); ++ ++ pcd = &dev->pcd; ++ ++ pcd->dev_global_regs = (usb3_dev_global_regs_t *)(base + USB3_DEV_GLOBAL_REG_OFFSET); ++ pcd->out_ep_regs = (usb3_dev_ep_regs_t *)(base + USB3_DEV_OUT_EP_REG_OFFSET); ++ pcd->in_ep_regs = (usb3_dev_ep_regs_t *)(base + USB3_DEV_IN_EP_REG_OFFSET); ++} ++ ++void usb3_init(usb3_device_t *dev) ++{ ++ /* Init the PCD (also enables interrupts and sets Run/Stop bit) */ ++ usb3_pcd_init(dev); ++} ++ ++static unsigned char string_manu[] = {'V', 0, 'E', 0, 'N', 0, 'D', 0, 'O', 0, 'R', 0}; ++static unsigned char string_prod[] = {'U', 0, 'S', 0, 'B', 0, 'B', 0, 'u', 0, 'r', 0, 'n', 0}; ++int usb3_driver_init(void) ++{ ++ usb3_device_t *usb3_dev; ++ struct usb_device_descriptor *usb3_dev_desc; ++ unsigned long ts = 0; ++ int retries = 0; ++ int ret = 0; ++ ++ usb3_dev = malloc(sizeof(usb3_device_t)); ++ if (!usb3_dev) { ++ debug("usb3_dev: out of memory\n"); ++ return -ENOMEM; ++ } ++ (void)memset_s((void *)usb3_dev, sizeof(usb3_device_t), 0, sizeof(usb3_device_t)); ++ usb3_dev_desc = malloc(sizeof(struct usb_device_descriptor)); ++ if (usb3_dev_desc == NULL) { ++ debug("usb3_dev_desc: out of memory\n"); ++ ret = -ENOMEM; ++ goto free_dev; ++ } ++ ++ usb3_pcd_t *pcd = &usb3_dev->pcd; ++ usb3_pcd_ep_t *ep = &pcd->in_ep; ++ usb3_pcd_req_t *req = &ep->req; ++ req->bufdma = (unsigned char *)malloc(512); /* 512 bytes */ ++ if (req->bufdma == NULL) { ++ debug("req->bufdma: out of memory\n"); ++ ret = -ENOMEM; ++ goto free_desc; ++ } ++ ++ usb_info("size of usb3_dev %d\n", sizeof(*usb3_dev)); ++ usb3_dev->base = (volatile unsigned char *)USB3_CTRL_REG_BASE; ++ usb3_dev->string_manu_len = sizeof(string_manu); ++ usb3_dev->string_prod_len = sizeof(string_prod); ++ usb3_dev->dev_desc = usb3_dev_desc; ++ (void)memcpy_s(usb3_dev->string_manu, usb3_dev->string_manu_len, string_manu, usb3_dev->string_manu_len); ++ (void)memcpy_s(usb3_dev->string_prod, usb3_dev->string_prod_len, string_prod, usb3_dev->string_prod_len); ++ usb3_dev->pcd.ep0_setup_desc = (usb3_dma_desc_t *)((phys_addr_t)(usb3_dev->pcd.ep0_setup + 0xf) & (unsigned int)(~0xf)); ++ usb3_dev->pcd.ep0_in_desc = (usb3_dma_desc_t *)((phys_addr_t)(usb3_dev->pcd.ep0_in + 0xf) & (unsigned int)(~0xf)); ++ usb3_dev->pcd.ep0_out_desc = (usb3_dma_desc_t *)((phys_addr_t)(usb3_dev->pcd.ep0_out + 0xf) & (unsigned int)(~0xf)); ++ usb3_dev->pcd.in_ep.ep_desc = ++ (usb3_dma_desc_t *)((phys_addr_t)(usb3_dev->pcd.in_ep.epx_desc + 0xf) & (unsigned int)(~0xf)); ++ usb3_dev->pcd.out_ep.ep_desc = ++ (usb3_dma_desc_t *)((phys_addr_t)(usb3_dev->pcd.out_ep.epx_desc + 0xf) & (unsigned int)(~0xf)); ++ ++tryagain: ++ /* Release usb3.0 controller */ ++ phy_usb_init(0); ++ ++ /* Get usb3.0 version number */ ++ usb3_dev->snpsid = ++ usb3_rd32((volatile unsigned int *)(usb3_dev->base + USB3_CORE_REG_BASE + USB3_CORE_GSNPSID_REG_OFFSET)); ++ ++ /* Initialize usb3.0 core */ ++ usb3_common_init(usb3_dev, usb3_dev->base + USB3_CORE_REG_BASE); ++ ++ /* Initialize usb3.0 pcd */ ++ usb3_init(usb3_dev); ++ ++ usb3_handle_event(usb3_dev); ++ ts = get_timer(0); ++ while ((get_timer(ts) < USB_WAIT_FOR_ENUM_DONE_TIME_MS) && ((usb3_dev->pcd).state != USB3_STATE_CONFIGURED)) { ++ usb3_handle_event(usb3_dev); ++ } ++ ++ /* Retry 2 times */ ++ if (((usb3_dev->pcd).state != USB3_STATE_CONFIGURED) && (retries < 2)) { ++ retries++; ++ goto tryagain; ++ } ++ ++ if ((usb3_dev->pcd).state != USB3_STATE_CONFIGURED) { ++ debug("Not handshake with Host\n"); ++ ret = -1; ++ goto free_dma; ++ } ++ ++ for (;;) ++ usb3_handle_event(usb3_dev); ++ ++free_dma: ++ free(req->bufdma); ++ ++free_desc: ++ free(usb3_dev->dev_desc); ++ ++free_dev: ++ free(usb3_dev); ++ ++ return ret; ++} ++ ++/* ++ * Interface func for download the mirror, address from return. ++ */ ++int udc_connect(void) ++{ ++ dcache_disable(); ++ if(usb3_driver_init() < 0) { ++ usb_info("usb3 driver init error\n"); ++ dcache_enable(); ++ return -1; ++ } ++ dcache_enable(); ++ return 0; ++} ++EXPORT_SYMBOL(udc_connect); +diff --git a/drivers/usb/gadget/udc3/usb3_drv.h b/drivers/usb/gadget/udc3/usb3_drv.h +new file mode 100644 +index 0000000..661a1fe +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_drv.h +@@ -0,0 +1,242 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __USB3_DRIVER_H__ ++#define __USB3_DRIVER_H__ ++ ++#include "sys.h" ++#include "usb3.h" ++#include "usb3_hw.h" ++#include "debug.h" ++ ++/* time limit of waiting for key enents */ ++#define USB_WAIT_FOR_ENUM_DONE_TIME_MS (3 * 1000) ++#define USB_WAIT_FOR_RX_NEXT_TIME_MS (60 * 1000) ++ ++enum usb_error_type { ++ USB_RX_NEXT_TIMEOUT = -5, ++ USB_ENUM_DONE_TIMEOUT = -3, ++ USB_PROCESS_ERR = -1, ++ USB_NO_ERR = 0, ++}; ++ ++struct usb_data_handle { ++ unsigned int u_file_type; ++ unsigned int u_file_address; ++ unsigned int u_file_length; ++ ++ unsigned int is_rx_start; ++ ++ unsigned int init_time_stamp; ++ unsigned int rx_time_stamp; ++}; ++ ++#define usb_info(format, arg...) debug_printf("[USBINFO]" format, ##arg); ++#define usb_err(format, arg...) debug_printf("[USBERR]" format, ##arg); ++ ++typedef enum pcd_state { ++ USB3_STATE_UNCONNECTED, /* no host */ ++ USB3_STATE_DEFAULT, ++ USB3_STATE_ADDRESSED, ++ USB3_STATE_CONFIGURED, ++} pcdstate_e; ++ ++typedef enum ep0_state { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_IN_WAIT_NRDY, ++ EP0_OUT_WAIT_NRDY, ++ EP0_IN_STATUS_PHASE, ++ EP0_OUT_STATUS_PHASE, ++ EP0_STALL, ++} ep0state_e; ++ ++typedef union usb_setup_pkt { ++ usb_device_request_t req; ++ unsigned int d32[2]; ++ unsigned char d8[8]; ++} usb_setup_pkt_t; ++ ++typedef struct usb3_pcd_req { ++ usb3_dma_desc_t *trb; ++ phys_addr_t trbdma; ++ ++ unsigned int length; ++ unsigned int actual; ++ ++ unsigned char *bufdma; ++ void (*complete)(void *); ++} usb3_pcd_req_t; ++ ++typedef struct usb3_pcd_ep { ++ struct usb3_pcd *pcd; ++ ++ usb3_dev_ep_regs_t *out_ep_reg; ++ usb3_dev_ep_regs_t *in_ep_reg; ++ ++ unsigned char phys; ++ unsigned char num; ++ unsigned char type; ++ unsigned char maxburst; ++ unsigned short maxpacket; ++ /* Tx FIFO # for IN EPs */ ++ unsigned char tx_fifo_num; ++ ++ /* The Transfer Resource Index from the Start Transfer command */ ++ unsigned char tri_out; ++ unsigned char tri_in; ++ ++ unsigned char stopped; ++ /* Send ZLP */ ++ unsigned char send_zlp; ++ /* True if 3-stage control transfer */ ++ unsigned char three_stage; ++ /* True if transfer has been started on EP */ ++ unsigned char xfer_started; ++ /* EP direction 0 = OUT */ ++ unsigned char is_in; ++ /* True if endpoint is active */ ++ unsigned char active; ++ /* Initial data pid of bulk endpoint */ ++ unsigned char data_pid_start; ++ ++ /* ep_desc (excluding ep0) */ ++ usb3_dma_desc_t *ep_desc; ++ ++ /* TRB descriptor must be aligned to 16 bytes */ ++ unsigned char epx_desc[32]; ++ ++ /* request (excluding ep0) */ ++ usb3_pcd_req_t req; ++} usb3_pcd_ep_t; ++ ++typedef struct usb3_pcd { ++ struct usb3_device *usb3_dev; ++ ++ signed int link_state; ++ pcdstate_e state; ++ unsigned char new_config; ++ ep0state_e ep0state; ++ ++ unsigned int eps_enabled; ++ unsigned int ltm_enable; ++ ++ usb3_pcd_ep_t ep0; ++ usb3_pcd_ep_t out_ep; ++ usb3_pcd_ep_t in_ep; ++ ++ usb3_dev_global_regs_t *dev_global_regs; ++ usb3_dev_ep_regs_t *out_ep_regs; ++ usb3_dev_ep_regs_t *in_ep_regs; ++ ++ usb3_pcd_req_t ep0_req; ++ ++ unsigned char speed; ++ ++ usb3_dma_desc_t *ep0_setup_desc; ++ usb3_dma_desc_t *ep0_in_desc; ++ usb3_dma_desc_t *ep0_out_desc; ++ ++ /* TRB descriptor must be aligned to 16 bytes */ ++ unsigned char ep0_setup[32]; ++ unsigned char ep0_in[32]; ++ unsigned char ep0_out[32]; ++ ++ usb_setup_pkt_t ep0_setup_pkt[5]; ++ ++#define USB3_STATUS_BUF_SIZE 512 ++ unsigned char ep0_status_buf[USB3_STATUS_BUF_SIZE]; ++ ++#define USB3_BULK_BUF_SIZE 512 ++ unsigned char ss_bulk_buf[USB3_BULK_BUF_SIZE]; ++ ++ unsigned int file_type; ++ unsigned int file_address; ++ unsigned int file_capacity; ++ unsigned int file_total_frame; ++ unsigned int file_curr_frame; ++ unsigned int file_next_frame; ++ unsigned int file_received; ++ unsigned int file_complete; ++} usb3_pcd_t; ++ ++#define usb3_pcd_ep_to_pcd(pcd_ep) ((pcd_ep)->usb3_pcd_ep_t.pcd) ++#define usb3_pcd_ep_num(pcd_ep) ((pcd_ep)->usb3_pcd_ep_t.num) ++#define usb3_pcd_ep_type(pcd_ep) ((pcd_ep)->usb3_pcd_ep_t.type) ++#define usb3_pcd_ep_is_in(pcd_ep) ((pcd_ep)->usb3_pcd_ep_t.is_in) ++ ++#define dwc_usb3_is_hwo(desc) ((desc)->control & USB3_DSCCTL_HWO_BIT) ++#define dwc_usb3_is_ioc(desc) ((desc)->control & USB3_DSCCTL_IOC_BIT) ++ ++#define usb3_get_xfercnt(desc) \ ++ (((desc)->status >> USB3_DSCSTS_XFRCNT_SHIFT) & (USB3_DSCSTS_XFRCNT_BITS >> USB3_DSCSTS_XFRCNT_SHIFT)) ++#define usb3_get_xfersts(desc) \ ++ (((desc)->status >> USB3_DSCSTS_TRBRSP_SHIFT) & (USB3_DSCSTS_TRBRSP_BITS >> USB3_DSCSTS_TRBRSP_SHIFT)) ++ ++#define map_to_dma_addr(addr) ((addr)) ++// ((addr) + (REG_BASE_LP_RAM_ACORE - REG_BASE_LP_RAM)) ++ ++typedef struct usb3_device { ++ volatile unsigned char *base; ++ void *dev_desc; ++ unsigned char string_manu[32]; ++ unsigned char string_prod[32]; ++ unsigned int string_manu_len; ++ unsigned int string_prod_len; ++ usb3_pcd_t pcd; ++ unsigned int snpsid; ++ usb3_core_global_regs_t *core_global_regs; ++ ++#define USB3_EVENT_BUF_SIZE 256 ++ unsigned int event_buf[USB3_EVENT_BUF_SIZE]; ++ unsigned int *event_ptr; ++ struct usb_data_handle *p_handle; ++} usb3_device_t; ++ ++unsigned int usb3_rd32(volatile unsigned int *addr); ++void usb3_wr32(volatile unsigned int *addr, unsigned int val); ++void usb3_set_address(usb3_pcd_t *pcd, unsigned int addr); ++void usb3_accept_u1(usb3_pcd_t *pcd); ++void usb3_accept_u2(usb3_pcd_t *pcd); ++void usb3_enable_u1(usb3_pcd_t *pcd); ++void usb3_enable_u2(usb3_pcd_t *pcd); ++void usb3_disable_u1(usb3_pcd_t *pcd); ++void usb3_disable_u2(usb3_pcd_t *pcd); ++unsigned int usb3_u1_enabled(usb3_pcd_t *pcd); ++unsigned int usb3_u2_enabled(usb3_pcd_t *pcd); ++void usb3_dep_cstall(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg); ++void usb3_dep_sstall(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg); ++unsigned int handshake(usb3_device_t *dev, volatile unsigned int *ptr, unsigned int mask, unsigned int done); ++void usb3_fill_desc(usb3_dma_desc_t *desc, phys_addr_t dma_addr, unsigned int dma_len, unsigned int stream, unsigned int type, ++ unsigned int ctrlbits, int own); ++void usb3_dep_startnewcfg(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int rsrcidx); ++void usb3_dep_cfg(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int depcfg0, unsigned int depcfg1, unsigned int depcfg2); ++void usb3_dep_xfercfg(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int depstrmcfg); ++unsigned char usb3_dep_startxfer(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, phys_addr_t dma_addr, unsigned int stream_or_uf); ++void usb3_dep_updatexfer(usb3_pcd_t *pcd, usb3_dev_ep_regs_t *ep_reg, unsigned int tri); ++void usb3_ep_activate(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep); ++void usb3_ep0_out_start(usb3_pcd_t *pcd); ++void usb3_ep0_start_transfer(usb3_pcd_t *pcd, usb3_pcd_req_t *req); ++void usb3_resume_usb2_phy(usb3_pcd_t *pcd); ++void usb3_handle_dev_intr(usb3_pcd_t *pcd, unsigned int event); ++int usb3_handle_ep_intr(usb3_pcd_t *pcd, unsigned int physep, unsigned int event); ++ ++ ++#endif /* __USB3_DRIVER_H */ +diff --git a/drivers/usb/gadget/udc3/usb3_hw.h b/drivers/usb/gadget/udc3/usb3_hw.h +new file mode 100644 +index 0000000..c57800f +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_hw.h +@@ -0,0 +1,1744 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __USB3_HW_H__ ++#define __USB3_HW_H__ ++ ++#include "usb3.h" ++ ++/* ************************************************************************** */ ++/* Core Global Registers */ ++ ++/* * ++ * This enum represents the bit fields of the Core SoC Bus Configuration 0 ++ * Register (GSBUSCFG0). ++ */ ++typedef enum gsbuscfg0_data { ++ /* * Bus Burst Len Access: R_W. ++ * - 0: single ++ * - 1: incr ++ * - 3: incr4 ++ * - 7: incr8 ++ * - 15: incr16 ++ * - 31: incr32 (non-AHB mode only) ++ * - 63: incr64 (non-AHB mode only) ++ * - 127: incr128 (non-AHB mode only) ++ * - 255: incr256 (non-AHB mode only) ++ */ ++ USB3_SBUSCFG0_HBURSTLEN_BITS = 0x000000ff, ++ USB3_SBUSCFG0_HBURSTLEN_SHIFT = 0, ++ ++ USB3_SBUSCFG0_INT_DMA_BURST_SINGLE = 0, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR = 1, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR4 = 3, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR8 = 7, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR16 = 15, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR32 = 31, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR64 = 63, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR128 = 127, ++ USB3_SBUSCFG0_INT_DMA_BURST_INCR256 = 255, ++ ++ /* * Descriptor Write is Posted Access: R_W */ ++ USB3_SBUSCFG0_DES_WR_POST_BIT = 0x00000100, ++ USB3_SBUSCFG0_DES_WR_POST_SHIFT = 8, ++ ++ /* * Data Write is Posted Access: R_W */ ++ USB3_SBUSCFG0_DAT_WR_POST_BIT = 0x00000200, ++ USB3_SBUSCFG0_DAT_WR_POST_SHIFT = 9, ++ ++ /* * Descriptor Access is Big-Endian Access: R_W */ ++ USB3_SBUSCFG0_DES_BIG_END_BIT = 0x00000400, ++ USB3_SBUSCFG0_DES_BIG_END_SHIFT = 10, ++ ++ /* * Data Access is Big-Endian Access: R_W */ ++ USB3_SBUSCFG0_DAT_BIG_END_BIT = 0x00000800, ++ USB3_SBUSCFG0_DAT_BIG_END_SHIFT = 11, ++ ++ /* * Store and Forward Mode Access: R_W */ ++ USB3_SBUSCFG0_STORE_AND_FORWARD_BIT = 0x00001000, ++ USB3_SBUSCFG0_STORE_AND_FORWARD_SHIFT = 12, ++ ++ /* * Force Single Request Access: R_W */ ++ USB3_SBUSCFG0_SING_REQ_BIT = 0x00004000, ++ USB3_SBUSCFG0_SING_REQ_SHIFT = 14, ++ ++ /* * Descriptor Readback Enable Access: R_W */ ++ USB3_SBUSCFG0_READ_AFTER_WRITE_BIT = 0x00008000, ++ USB3_SBUSCFG0_READ_AFTER_WRITE_SHIFT = 15, ++ ++ /* * Descriptor Write Request Info Access: R_W */ ++ USB3_SBUSCFG0_DES_WR_REQ_INFO_BITS = 0x000f0000, ++ USB3_SBUSCFG0_DES_WR_REQ_INFO_SHIFT = 16, ++ ++ /* * Data Write Request Info Access: R_W */ ++ USB3_SBUSCFG0_DAT_WR_REQ_INFO_BITS = 0x00f00000, ++ USB3_SBUSCFG0_DAT_WR_REQ_INFO_SHIFT = 20, ++ ++ /* * Descriptor Read Request Info Access: R_W */ ++ USB3_SBUSCFG0_DES_RD_REQ_INFO_BITS = 0x0f000000, ++ USB3_SBUSCFG0_DES_RD_REQ_INFO_SHIFT = 24, ++ ++ /* * Data Read Request Info Access: R_W */ ++ USB3_SBUSCFG0_DAT_RD_REQ_INFO_BITS = 0xf0000000, ++ USB3_SBUSCFG0_DAT_RD_REQ_INFO_SHIFT = 28, ++} gsbuscfg0_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Core SoC Bus Configuration 1 ++ * Register (GSBUSCFG1). ++ */ ++typedef enum gsbuscfg1_data { ++ /* * OCP Address Space For Descriptor Access: R_W */ ++ USB3_SBUSCFG1_DES_ADDR_SPC_BITS = 0x0000000f, ++ USB3_SBUSCFG1_DES_ADDR_SPC_SHIFT = 0, ++ ++ /* * OCP Address Space For Data Access: R_W */ ++ USB3_SBUSCFG1_DAT_ADDR_SPC_BITS = 0x000000f0, ++ USB3_SBUSCFG1_DAT_ADDR_SPC_SHIFT = 4, ++} gsbuscfg1_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Core Tx Threshold Control ++ * Register (GTXTHRCFG). ++ */ ++typedef enum gtxthrcfg_data { ++ /* * SoC Bus Transmit Threshold Length Access: R_W */ ++ USB3_TXTHRCFG_SBUS_THR_LEN_BITS = 0x000007ff, ++ USB3_TXTHRCFG_SBUS_THR_LEN_SHIFT = 0, ++ ++ /* * SoC Bus Non-ISO Transmit Threshold Enable Access: R_W */ ++ USB3_TXTHRCFG_SBUS_NON_ISO_THR_EN_BIT = 0x00004000, ++ USB3_TXTHRCFG_SBUS_NON_ISO_THR_EN_SHIFT = 14, ++ ++ /* * SoC Bus ISO Transmit Threshold Enable Access: R_W */ ++ USB3_TXTHRCFG_SBUS_ISO_THR_EN_BIT = 0x00008000, ++ USB3_TXTHRCFG_SBUS_ISO_THR_EN_SHIFT = 15, ++ ++ /* * USB Transmit Threshold Length Access: R_W */ ++ USB3_TXTHRCFG_USB_THR_LEN_BITS = 0x1fff0000, ++ USB3_TXTHRCFG_USB_THR_LEN_SHIFT = 16, ++ ++ /* * USB Non-ISO Transmit Threshold Enable Access: R_W */ ++ USB3_TXTHRCFG_USB_NON_ISO_THR_EN_BIT = 0x40000000, ++ USB3_TXTHRCFG_USB_NON_ISO_THR_EN_SHIFT = 30, ++ ++ /* * USB ISO Transmit Threshold Enable Access: R_W */ ++ USB3_TXTHRCFG_USB_ISO_THR_EN_BIT = 0x80000000, ++ USB3_TXTHRCFG_USB_ISO_THR_EN_SHIFT = 31, ++} gtxthrcfg_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Core Rx Threshold Control ++ * Register (GRXTHRCFG). ++ */ ++typedef enum grxthrcfg_data { ++ /* * Receive Threshold Length Access: R_W */ ++ USB3_RXTHRCTL_THR_LEN_BITS = 0x07ff, ++ USB3_RXTHRCTL_THR_LEN_SHIFT = 0, ++ ++ /* * Receive Threshold Enable Access: R_W */ ++ USB3_RXTHRCTL_THR_EN_BIT = 0x8000, ++ USB3_RXTHRCTL_THR_EN_SHIFT = 15, ++} grxthrcfg_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Core Control ++ * Register (GCTL). ++ */ ++typedef enum gctl_data { ++ /* * Disable Clock Gating Access: R_W */ ++ USB3_GCTL_DSBL_CLCK_GTNG_BIT = 0x00000001, ++ USB3_GCTL_DSBL_CLCK_GTNG_SHIFT = 0, ++ ++ /* * Global Hibernation Enable Access: R_W */ ++ USB3_GCTL_GBL_HIBER_EN_BIT = 0x00000002, ++ USB3_GCTL_GBL_HIBER_EN_SHIFT = 1, ++ ++ /* * U2Exit LFPS Access: R_W */ ++ USB3_GCTL_GBL_U2EXIT_LFPS = 0x00000004, ++ USB3_GCTL_GBL_U2EXIT_LFPS_SHIFT = 2, ++ ++ /* * Disable Scrambling Access: R_W */ ++ USB3_GCTL_DIS_SCRAMBLE_BIT = 0x00000008, ++ USB3_GCTL_DIS_SCRAMBLE_SHIFT = 3, ++ ++ /* * Scale-down Mode Access: R_W */ ++ USB3_GCTL_SCALE_DOWN_BITS = 0x00000030, ++ USB3_GCTL_SCALE_DOWN_SHIFT = 4, ++ ++ /* * RAM Clock Select Access: R_W */ ++ USB3_GCTL_RAM_CLK_SEL_BITS = 0x000000c0, ++ USB3_GCTL_RAM_CLK_SEL_SHIFT = 6, ++ ++ /* * Debug Attach Access: R_W */ ++ USB3_GCTL_DEBUG_ATTACH_BIT = 0x00000100, ++ USB3_GCTL_DEBUG_ATTACH_SHIFT = 8, ++ ++ /* * Loopback Enable Access: R_W */ ++ USB3_GCTL_LPBK_EN_BIT = 0x00000200, ++ USB3_GCTL_LPBK_EN_SHIFT = 9, ++ ++ /* * Local Loopback Enable Access: R_W */ ++ USB3_GCTL_LOCAL_LPBK_EN_BIT = 0x00000400, ++ USB3_GCTL_LOCAL_LPBK_EN_SHIFT = 10, ++ ++ /* * Core Soft Reset Access: R_W */ ++ USB3_GCTL_CORE_SOFT_RST_BIT = 0x00000800, ++ USB3_GCTL_CORE_SOFT_RST_SHIFT = 11, ++ ++ /* * Port Capability Direction Access: R_W */ ++ USB3_GCTL_PRT_CAP_DIR_BITS = 0x00003000, ++ USB3_GCTL_PRT_CAP_DIR_SHIFT = 12, ++ ++ /* * Port Capability Values */ ++ USB3_GCTL_PRT_CAP_HOST = 1, ++ USB3_GCTL_PRT_CAP_DEVICE = 2, ++ USB3_GCTL_PRT_CAP_OTG = 3, ++ ++ /* * Frame Scale Down Access: R_W */ ++ USB3_GCTL_FRMSCLDWN_BITS = 0x0000c000, ++ USB3_GCTL_FRMSCLDWN_SHIFT = 14, ++ ++ /* * U2 Reset ECN Access: R_W */ ++ USB3_GCTL_U2RSTECN_BIT = 0x00010000, ++ USB3_GCTL_U2RSTECN_SHIFT = 16, ++ ++ /* * Power Down Scale Access: R_W */ ++ USB3_GCTL_PWR_DN_SCALE_BITS = 0xfff80000, ++ USB3_GCTL_PWR_DN_SCALE_SHIFT = 19, ++} gctl_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Core Interrupt Mask ++ * Register (GEVTEN). ++ */ ++typedef enum gevten_data { ++ /* * ULPI Carkit Event Enable Access: R_W */ ++ USB3_GEVTEN_ULPI_CK_EVT_EN_BIT = 0x00000001, ++ USB3_GEVTEN_ULPI_CK_EVT_SHIFT = 0, ++ ++ /* * I2C Event Enable Access: R_W */ ++ USB3_GEVTEN_I2C_EVT_EN_BIT = 0x00000002, ++ USB3_GEVTEN_I2C_EVT_EN_SHIFT = 1, ++} gevten_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Core Status ++ * Register (GSTS). ++ */ ++typedef enum gsts_data { ++ /* * Current Mode Access: RO. ++ * - 0: Device Mode ++ * - 1: Host Mode ++ * - 2: DRD Mode ++ */ ++ USB3_GSTS_CURMODE_BITS = 0x00000003, ++ USB3_GSTS_CURMODE_SHIFT = 0, ++ ++ USB3_GSTS_DEVICE_MODE = 0, ++ USB3_GSTS_HOST_MODE = 1, ++ USB3_GSTS_DRD_MODE = 2, ++ ++ /* * Bus Error Address Valid Access: RO */ ++ USB3_GSTS_BUS_ERR_ADDR_VLD_BIT = 0x00000010, ++ USB3_GSTS_BUS_ERR_ADDR_VLD_SHIFT = 4, ++ ++ /* * CSR Timeout */ ++ USB3_GSTS_CSR_TIMEOUT_BIT = 0x00000020, ++ USB3_GSTS_CSR_TIMEOUT_SHIFT = 5, ++ ++ /* * Device Interrupt Pending */ ++ USB3_GSTS_DEV_EVT_PENDING_BIT = 0x00000040, ++ USB3_GSTS_DEV_EVT_PENDING_SHIFT = 6, ++ ++ /* * Host Interrupt Pending */ ++ USB3_GSTS_HOST_EVT_PENDING_BIT = 0x00000080, ++ USB3_GSTS_HOST_EVT_PENDING_SHIFT = 7, ++ ++ /* * ADP Interrupt Pending */ ++ USB3_GSTS_ADP_EVT_PENDING_BIT = 0x00000100, ++ USB3_GSTS_ADP_EVT_PENDING_SHIFT = 8, ++ ++ /* * BC Interrupt Pending */ ++ USB3_GSTS_BC_EVT_PENDING_BIT = 0x00000200, ++ USB3_GSTS_BC_EVT_PENDING_SHIFT = 9, ++ ++ /* * OTG Interrupt Pending */ ++ USB3_GSTS_OTG_EVT_PENDING_BIT = 0x00000400, ++ USB3_GSTS_OTG_EVT_PENDING_SHIFT = 10, ++ ++ /* * Current BELT Value Access: RO */ ++ USB3_GSTS_CBELT_BITS = 0xfff00000, ++ USB3_GSTS_CBELT_SHIFT = 20, ++} gsts_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 0 ++ * Register (GHWPARAMS0). ++ */ ++typedef enum ghwparams0_data { ++ USB3_HWP0_MODE_BITS = 0x00000007, ++ USB3_HWP0_MODE_SHIFT = 0, ++ ++ USB3_HWP0_MBUS_TYPE_BITS = 0x00000038, ++ USB3_HWP0_MBUS_TYPE_SHIFT = 3, ++ ++ USB3_HWP0_SBUS_TYPE_BITS = 0x000000c0, ++ USB3_HWP0_SBUS_TYPE_SHIFT = 6, ++ ++ USB3_HWP0_MDWIDTH_BITS = 0x0000ff00, ++ USB3_HWP0_MDWIDTH_SHIFT = 8, ++ ++ USB3_HWP0_SDWIDTH_BITS = 0x00ff0000, ++ USB3_HWP0_SDWIDTH_SHIFT = 16, ++ ++ USB3_HWP0_AWIDTH_BITS = 0x3f000000, ++ USB3_HWP0_AWIDTH_SHIFT = 24, ++} ghwparams0_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 1 ++ * Register (GHWPARAMS1). ++ */ ++typedef enum ghwparams1_data { ++ USB3_HWP1_IDWIDTH_M1_BITS = 0x00000007, ++ USB3_HWP1_IDWIDTH_M1_SHIFT = 0, ++ ++ USB3_HWP1_BURSTWIDTH_M1_BITS = 0x00000038, ++ USB3_HWP1_BURSTWIDTH_M1_SHIFT = 3, ++ ++ USB3_HWP1_DATAINFOWIDTH_BITS = 0x000001c0, ++ USB3_HWP1_DATAINFOWIDTH_SHIFT = 6, ++ ++ USB3_HWP1_REQINFOWIDTH_BITS = 0x00000e00, ++ USB3_HWP1_REQINFOWIDTH_SHIFT = 9, ++ ++ USB3_HWP1_ASPACEWIDTH_BITS = 0x00007000, ++ USB3_HWP1_ASPACEWIDTH_SHIFT = 12, ++ ++ USB3_HWP1_DEV_NUM_INT_BITS = 0x001f8000, ++ USB3_HWP1_DEV_NUM_INT_SHIFT = 15, ++ ++ USB3_HWP1_NUM_RAMS_BITS = 0x00600000, ++ USB3_HWP1_NUM_RAMS_SHIFT = 21, ++ ++ USB3_HWP1_SPRAM_TYP_BIT = 0x00800000, ++ USB3_HWP1_SPRAM_TYP_SHIFT = 23, ++ ++ USB3_HWP1_EN_PWROPT_BITS = 0x03000000, ++ USB3_HWP1_EN_PWROPT_SHIFT = 24, ++ ++ USB3_EN_PWROPT_NONE = 0, ++ USB3_EN_PWROPT_CLK_GATING_ONLY = 1, ++ USB3_EN_PWROPT_HIBERNATION = 2, ++ ++ USB3_HWP1_MAC_PHY_CLKS_SYNC_BIT = 0x04000000, ++ USB3_HWP1_MAC_PHY_CLKS_SYNC_SHIFT = 26, ++ ++ USB3_HWP1_MAC_RAM_CLKS_SYNC_BIT = 0x08000000, ++ USB3_HWP1_MAC_RAM_CLKS_SYNC_SHIFT = 27, ++ ++ USB3_HWP1_RAM_BUS_CLKS_SYNC_BIT = 0x10000000, ++ USB3_HWP1_RAM_BUS_CLKS_SYNC_SHIFT = 28, ++ ++ USB3_HWP1_RM_OPT_FEATURES_BIT = 0x40000000, ++ USB3_HWP1_RM_OPT_FEATURES_SHIFT = 30, ++} ghwparams1_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 2 ++ * Register (GHWPARAMS2). ++ */ ++typedef enum ghwparams2_data { ++ USB3_HWP2_USERID_BITS = 0xffffffff, ++ USB3_HWP2_USERID_SHIFT = 0, ++} ghwparams2_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 3 ++ * Register (GHWPARAMS3). ++ */ ++typedef enum ghwparams3_data { ++ USB3_HWP3_SSPHY_IFC_BITS = 0x00000003, ++ USB3_HWP3_SSPHY_IFC_SHIFT = 0, ++ ++ USB3_HWP3_HSPHY_IFC_BITS = 0x0000000c, ++ USB3_HWP3_HSPHY_IFC_SHIFT = 2, ++ ++ USB3_HWP3_FSPHY_IFC_BITS = 0x00000030, ++ USB3_HWP3_FSPHY_IFC_SHIFT = 4, ++ ++ USB3_HWP3_HSPHY_DWIDTH_BITS = 0x000000c0, ++ USB3_HWP3_HSPHY_DWIDTH_SHIFT = 6, ++ ++ USB3_HWP3_VEND_CTL_IFC_BIT = 0x00000400, ++ USB3_HWP3_VEND_CTL_IFC_SHIFT = 10, ++ ++ USB3_HWP3_ULPI_CARKIT_BIT = 0x00000800, ++ USB3_HWP3_ULPI_CARKIT_SHIFT = 11, ++ ++ USB3_HWP3_NUM_EPS_BITS = 0x0003f000, ++ USB3_HWP3_NUM_EPS_SHIFT = 12, ++ ++ USB3_HWP3_NUM_IN_EPS_BITS = 0x007c0000, ++ USB3_HWP3_NUM_IN_EPS_SHIFT = 18, ++ ++ USB3_HWP3_TOT_XFR_RSRC_BITS = 0x7f800000, ++ USB3_HWP3_TOT_XFR_RSRC_SHIFT = 23, ++} ghwparams3_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 4 ++ * Register (GHWPARAMS4). ++ */ ++typedef enum ghwparams4_data { ++ USB3_HWP4_TRBS_PER_XFER_BITS = 0x0000003f, ++ USB3_HWP4_TRBS_PER_XFER_SHIFT = 0, ++ ++ USB3_HWP4_HIBER_SPAD_BITS = 0x0001e000, ++ USB3_HWP4_HIBER_SPAD_SHIFT = 13, ++ ++ USB3_HWP4_NUM_SS_USB_INST_BITS = 0x001e0000, ++ USB3_HWP4_NUM_SS_USB_INST_SHIFT = 17, ++ ++ USB3_HWP4_EN_ISOC_SUPT_BIT = 0x00800000, ++ USB3_HWP4_EN_ISOC_SUPT_SHIFT = 23, ++ ++ USB3_HWP4_BMU_PTL_DEPTH_BITS = 0x0f000000, ++ USB3_HWP4_BMU_PTL_DEPTH_SHIFT = 24, ++ ++ USB3_HWP4_BMU_LSP_DEPTH_BITS = 0xf0000000, ++ USB3_HWP4_BMU_LSP_DEPTH_SHIFT = 28, ++} ghwparams4_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 5 ++ * Register (GHWPARAMS5). ++ */ ++typedef enum ghwparams5_data { ++ USB3_HWP5_BMU_BUSGM_DEPTH_BITS = 0x0000000f, ++ USB3_HWP5_BMU_BUSGM_DEPTH_SHIFT = 0, ++ ++ USB3_HWP5_RXQ_FIFO_DEPTH_BITS = 0x000003f0, ++ USB3_HWP5_RXQ_FIFO_DEPTH_SHIFT = 4, ++ ++ USB3_HWP5_TXQ_FIFO_DEPTH_BITS = 0x0000fc00, ++ USB3_HWP5_TXQ_FIFO_DEPTH_SHIFT = 10, ++ ++ USB3_HWP5_DWQ_FIFO_DEPTH_BITS = 0x003f0000, ++ USB3_HWP5_DWQ_FIFO_DEPTH_SHIFT = 16, ++ ++ USB3_HWP5_DFQ_FIFO_DEPTH_BITS = 0x0fc00000, ++ USB3_HWP5_DFQ_FIFO_DEPTH_SHIFT = 22, ++} ghwparams5_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 6 ++ * Register (GHWPARAMS6). ++ */ ++typedef enum ghwparams6_data { ++ USB3_HWP6_PSQ_FIFO_DEPTH_BITS = 0x0000003f, ++ USB3_HWP6_PSQ_FIFO_DEPTH_SHIFT = 0, ++ ++ USB3_HWP6_EN_DBG_PORTS_BIT = 0x00000040, ++ USB3_HWP6_EN_DBG_PORTS_SHIFT = 6, ++ ++ USB3_HWP6_EN_FPGA_BIT = 0x00000080, ++ USB3_HWP6_EN_FPGA_SHIFT = 7, ++ ++ USB3_HWP6_EN_SRP_BIT = 0x00000400, ++ USB3_HWP6_EN_SRP_SHIFT = 10, ++ ++ USB3_HWP6_EN_HNP_BIT = 0x00000800, ++ USB3_HWP6_EN_HNP_SHIFT = 11, ++ ++ USB3_HWP6_EN_ADP_BIT = 0x00001000, ++ USB3_HWP6_EN_ADP_SHIFT = 12, ++ ++ USB3_HWP6_EN_OTG_BIT = 0x00002000, ++ USB3_HWP6_EN_OTG_SHIFT = 13, ++ ++ USB3_HWP6_EN_BC_BIT = 0x00004000, ++ USB3_HWP6_EN_BC_SHIFT = 14, ++ ++ USB3_HWP6_EN_BUS_FILTERS_BIT = 0x00008000, ++ USB3_HWP6_EN_BUS_FILTERS_SHIFT = 15, ++ ++ USB3_HWP6_RAM0_DEPTH_BITS = 0xffff0000, ++ USB3_HWP6_RAM0_DEPTH_SHIFT = 16, ++} ghwparams6_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 7 ++ * Register (GHWPARAMS7). ++ */ ++typedef enum ghwparams7_data { ++ USB3_HWP7_RAM1_DEPTH_BITS = 0x0000ffff, ++ USB3_HWP7_RAM1_DEPTH_SHIFT = 0, ++ ++ USB3_HWP7_RAM2_DEPTH_BITS = 0xffff0000, ++ USB3_HWP7_RAM2_DEPTH_SHIFT = 16, ++} ghwparams7_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Hardware Parameters 8 ++ * Register (GHWPARAMS8). ++ */ ++typedef enum ghwparams8_data { ++ USB3_HWP8_DCACHE_DEPTH_BITS = 0xffffffff, ++ USB3_HWP8_DCACHE_DEPTH_SHIFT = 0, ++} ghwparams8_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Debug Queue/FIFO Space ++ * Register (GDBGFIFOSPACE). ++ */ ++typedef enum gdbgfifospace_data { ++ /* * FIFO/Queue Select Access: R_W */ ++ USB3_DBGFIFOSPACE_FIFO_QUEUE_SEL_BITS = 0x000000ff, ++ USB3_DBGFIFOSPACE_FIFO_QUEUE_SEL_SHIFT = 0, ++ ++ /* 0 - 31 TxFIFO Number */ ++ /* 32 - 63 RxFIFO Number */ ++ /* 64 - 95 TxReqQ Number */ ++ /* 96 - 127 RxReqQ Number */ ++ /* 128 - 159 RxInfoQ Number */ ++ /* 160 DescFetchQ */ ++ /* 161 EventQ */ ++ /* 162 ProtocolStatusQ */ ++ ++ /* * Space Available Access: R */ ++ USB3_DBGFIFOSPACE_SPACE_AVAIL_BITS = 0xffff0000, ++ USB3_DBGFIFOSPACE_SPACE_AVAIL_SHIFT = 16, ++} gdbgfifospace_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Debug LTSSM ++ * Register (GDBGLTSSM). ++ */ ++typedef enum gdbgltssm_data { ++ /* * Pipe Status Access: R */ ++ USB3_DBGLTSSM_PIPE_STATUS_BITS = 0x0003ffff, ++ USB3_DBGLTSSM_PIPE_STATUS_SHIFT = 0, ++ ++ /* * LTDB SubState Access: R */ ++ USB3_DBGLTSSM_LTDB_SUB_STATE_BITS = 0x003c0000, ++ USB3_DBGLTSSM_LTDB_SUB_STATE_SHIFT = 18, ++ ++ /* * LTDB State Access: R */ ++ USB3_DBGLTSSM_LTDB_STATE_BITS = 0x03c00000, ++ USB3_DBGLTSSM_LTDB_STATE_SHIFT = 22, ++ ++ /* * LTDB Timeout Access: R */ ++ USB3_DBGLTSSM_LTDB_TIMEOUT_BIT = 0x04000000, ++ USB3_DBGLTSSM_LTDB_TIMEOUT_SHIFT = 26, ++} gdbgltssm_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Core USB2 PHY Configuration ++ * Registers (GUSB2PHYCFGn). ++ */ ++typedef enum gusb2phycfg_data { ++ /* * HS/FS Timeout Calibration Access: R_W */ ++ USB3_USB2PHYCFG_TOUT_CAL_BITS = 0x00000007, ++ USB3_USB2PHYCFG_TOUT_CAL_SHIFT = 0, ++ ++ /* * UTMI+ PHY Intf Width (8-bit/16-bit) SelecT Access: R_W */ ++ USB3_USB2PHYCFG_PHY_IF_BIT = 0x00000008, ++ USB3_USB2PHYCFG_PHY_IF_SHIFT = 3, ++ /* -------- */ ++ /* * ULPI DDR Select Access: R_W */ ++ USB3_USB2PHYCFG_DDR_SEL_BIT = 0x00000008, ++ USB3_USB2PHYCFG_DDR_SEL_SHIFT = 3, ++ ++ /* * UTMI+ / ULPI Select Access: R_W */ ++ USB3_USB2PHYCFG_UTMI_ULPI_BIT = 0x00000010, ++ USB3_USB2PHYCFG_UTMI_ULPI_SHIFT = 4, ++ ++ /* * Full-speed Serial Interface Select Access: R_W */ ++ USB3_USB2PHYCFG_FSINTF_BIT = 0x00000020, ++ USB3_USB2PHYCFG_FSINTF_SHIFT = 5, ++ ++ /* * Suspend USB2 Phy Access: R_W */ ++ USB3_USB2PHYCFG_SUS_PHY_BIT = 0x00000040, ++ USB3_USB2PHYCFG_SUS_PHY_SHIFT = 6, ++ ++ /* * USB2.0 HS PHY/USB1.1 FS Serial Xcvr Select Access: R_W */ ++ USB3_USB2PHYCFG_PHY_SEL_BIT = 0x00000080, ++ USB3_USB2PHYCFG_PHY_SEL_SHIFT = 7, ++ ++ /* * Enable UTMI Sleep Access: R_W */ ++ USB3_USB2PHYCFG_ENBL_SLP_M_BIT = 0x00000100, ++ USB3_USB2PHYCFG_ENBL_SLP_M_SHIFT = 8, ++ ++ /* * USB2.0 Turnaround Time Access: R_W */ ++ USB3_USB2PHYCFG_USB_TRD_TIM_BITS = 0x00003c00, ++ USB3_USB2PHYCFG_USB_TRD_TIM_SHIFT = 10, ++ ++ /* * PHY Low-power Clock Select Access: R_W */ ++ USB3_USB2PHYCFG_PHY_LPWR_CLK_SEL_BIT = 0x00004000, ++ USB3_USB2PHYCFG_PHY_LPWR_CLK_SEL_SHIFT = 14, ++ ++ /* * ULPI Auto Resume Access: R_W */ ++ USB3_USB2PHYCFG_ULPI_AUTO_RES_BIT = 0x00008000, ++ USB3_USB2PHYCFG_ULPI_AUTO_RES_SHIFT = 15, ++ ++ /* * ULPI Clock SuspendM Access: R_W */ ++ USB3_USB2PHYCFG_ULPI_CLK_SUS_M_BIT = 0x00010000, ++ USB3_USB2PHYCFG_ULPI_CLK_SUS_M_SHIFT = 16, ++ ++ /* * ULPI External Vbus Drive Access: R_W */ ++ USB3_USB2PHYCFG_ULPI_EXT_VBUS_DRV_BIT = 0x00020000, ++ USB3_USB2PHYCFG_ULPI_EXT_VBUS_DRV_SHIFT = 17, ++ ++ /* * ULPI External Vbus Indicator Access: R_W */ ++ USB3_USB2PHYCFG_ULPI_EXT_VBUS_IND_BIT = 0x00040000, ++ USB3_USB2PHYCFG_ULPI_EXT_VBUS_IND_SHIFT = 18, ++ ++ /* * PHY Interrupt Number Access: R_W */ ++ USB3_USB2PHYCFG_PHY_INTR_NUM_BITS = 0x01f80000, ++ USB3_USB2PHYCFG_PHY_INTR_NUM_SHIFT = 19, ++ ++ /* * OTG Interrupt Number Access: R_W */ ++ USB3_USB2PHYCFG_OTG_INTR_NUM_BITS = 0x7e000000, ++ USB3_USB2PHYCFG_OTG_INTR_NUM_SHIFT = 25, ++ ++ /* * PHY Soft Reset Access: R_W */ ++ USB3_USB2PHYCFG_PHY_SOFT_RST_BIT = 0x80000000, ++ USB3_USB2PHYCFG_PHY_SOFT_RST_SHIFT = 31, ++} gusb2phycfg_data_t; ++ ++/* * ++ * This enum represents the bit fields in the USB2 I2C Control ++ * Registers (GUSB2I2CCTLn). ++ */ ++typedef enum gusb2i2cctl_data { ++ /* * All bits are reserved */ ++ USB3_USB2I2C_RSVD_BITS = 0xffffffff, ++ USB3_USB2I2C_RSVD_SHIFT = 0, ++} gusb2i2cctl_data_t; ++ ++/* * ++ * This enum represents the bit fields in the USB2 Phy Vendor Control ++ * Registers (GUSB2PHYACCn). ++ */ ++typedef enum gusb2phyacc_data { ++ /* * Register Data Access: R_W */ ++ USB3_USB2PHY_REGDATA_BITS = 0x000000ff, ++ USB3_USB2PHY_REGDATA_SHIFT = 0, ++ ++ /* * UTMI+ Vendor Ctrl Register Address Access: R_W */ ++ USB3_USB2PHY_VCTRL_BITS = 0x0000ff00, ++ USB3_USB2PHY_VCTRL_SHIFT = 8, ++ /* -------- */ ++ /* * ULPI Extended Register Address Access: R_W */ ++ USB3_USB2PHY_EXTREGADDR_BITS = 0x00003f00, ++ USB3_USB2PHY_EXTREGADDR_SHIFT = 8, ++ ++ /* * Register Address Access: R_W */ ++ USB3_USB2PHY_REGADDR_BITS = 0x003f0000, ++ USB3_USB2PHY_REGADDR_SHIFT = 16, ++ ++ /* * Register Write Access: R_W */ ++ USB3_USB2PHY_REGWR_BIT = 0x00400000, ++ USB3_USB2PHY_REGWR_SHIFT = 22, ++ ++ /* * VStatus Busy Access: RO */ ++ USB3_USB2PHY_VSTSBSY_BIT = 0x00800000, ++ USB3_USB2PHY_VSTSBSY_SHIFT = 23, ++ ++ /* * VStatus Done Access: R_SS_SC */ ++ USB3_USB2PHY_VSTSDONE_BIT = 0x01000000, ++ USB3_USB2PHY_VSTSDONE_SHIFT = 24, ++ ++ /* * New Register Request Access: R_WS_SC */ ++ USB3_USB2PHY_NEWREGREQ_BIT = 0x02000000, ++ USB3_USB2PHY_NEWREGREQ_SHIFT = 25, ++ ++ /* * Disable ULPI Drivers Access: R_WS_SC */ ++ USB3_USB2PHY_DIS_ULPI_DRVR_BIT = 0x04000000, ++ USB3_USB2PHY_DIS_ULPI_DRVR_SHIFT = 26, ++} gusb2phyacc_data_t; ++ ++/* * ++ * This enum represents the bit fields of the USB3 Pipe Control ++ * Registers (GUSB3PIPECTLn). ++ */ ++typedef enum gusb3pipectl_data { ++ /* * Elastic Buffer Mode Access: R_W */ ++ USB3_PIPECTL_ELAS_BUF_MODE_BIT = 0x00000001, ++ USB3_PIPECTL_ELAS_BUF_MODE_SHIFT = 0, ++ ++ /* * Tx De-Emphasis Access: R_W */ ++ USB3_PIPECTL_TX_DEMPH_BITS = 0x00000006, ++ USB3_PIPECTL_TX_DEMPH_SHIFT = 1, ++ ++ /* * Tx Margin Access: R_W */ ++ USB3_PIPECTL_TX_MARGIN_BITS = 0x00000038, ++ USB3_PIPECTL_TX_MARGIN_SHIFT = 3, ++ ++ /* * Tx Swing Access: R_W */ ++ USB3_PIPECTL_TX_SWING_BIT = 0x00000040, ++ USB3_PIPECTL_TX_SWING_SHIFT = 6, ++ ++ /* * Port Operation Direction Access: R_W */ ++ USB3_PIPECTL_PRT_OP_DIR_BITS = 0x00000180, ++ USB3_PIPECTL_PRT_OP_DIR_SHIFT = 7, ++ ++ /* * LFPS Filter Access: R_W */ ++ USB3_PIPECTL_LFPS_FILTER_BIT = 0x00000200, ++ USB3_PIPECTL_LFPS_FILTER_SHIFT = 9, ++ ++ /* * P3 Exit Signal In P2 Access: R_W */ ++ USB3_PIPECTL_P3_EX_SIG_P2_BIT = 0x00000400, ++ USB3_PIPECTL_P3_EX_SIG_P2_SHIFT = 10, ++ ++ /* * P3-P2 Transitions OK Access: R_W */ ++ USB3_PIPECTL_P3_P2_TRAN_OK_BIT = 0x00000800, ++ USB3_PIPECTL_P3_P2_TRAN_OK_SHIFT = 11, ++ ++ /* * LFPS P0 Align Access: R_W */ ++ USB3_PIPECTL_LFPS_P0_ALGN_BIT = 0x00001000, ++ USB3_PIPECTL_LFPS_P0_ALGN_SHIFT = 12, ++ ++ /* * Pipe Data Width Access: R_W */ ++ USB3_PIPECTL_DATA_WIDTH_BITS = 0x00018000, ++ USB3_PIPECTL_DATA_WIDTH_SHIFT = 15, ++ ++ /* * Suspend USB3 Phy Access: R_W */ ++ USB3_PIPECTL_SUS_PHY_BIT = 0x00020000, ++ USB3_PIPECTL_SUS_PHY_SHIFT = 17, ++ ++ /* * PHY Soft Reset Access: R_W */ ++ USB3_PIPECTL_PHY_SOFT_RST_BIT = 0x80000000, ++ USB3_PIPECTL_PHY_SOFT_RST_SHIFT = 31, ++} gusb3pipectl_data_t; ++ ++/* * ++ * This enum represents the bit fields in the FIFO Size Registers. ++ */ ++typedef enum gfifosize_data { ++ /* * Depth Access: R_W */ ++ USB3_FIFOSZ_DEPTH_BITS = 0x0000ffff, ++ USB3_FIFOSZ_DEPTH_SHIFT = 0, ++ ++ /* * Starting Address Access: RO or R_W */ ++ USB3_FIFOSZ_STARTADDR_BITS = 0xffff0000, ++ USB3_FIFOSZ_STARTADDR_SHIFT = 16, ++} gfifosize_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Event Buffer Size ++ * Registers (GEVENTSIZn). ++ */ ++typedef enum geventsiz_data { ++ /* * Event Buffer Size Access: R_W */ ++ USB3_EVENTSIZ_SIZ_BITS = 0x0000ffff, ++ USB3_EVENTSIZ_SIZ_SHIFT = 0, ++ ++ /* * Event Interrupt Mask (1 == disable) Access: R_W */ ++ USB3_EVENTSIZ_INT_MSK_BIT = 0x80000000, ++ USB3_EVENTSIZ_INT_MSK_SHIFT = 31, ++} geventsiz_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Event Buffer Count ++ * Registers (GEVENTCNTn). ++ */ ++typedef enum geventcnt_data { ++ /* * Event Count Access: R_W */ ++ USB3_EVENTCNT_CNT_BITS = 0x0000ffff, ++ USB3_EVENTCNT_CNT_SHIFT = 0, ++} geventcnt_data_t; ++ ++/* * ++ * This enum represents the bit fields of a generic Event Buffer entry. ++ */ ++typedef enum gevent_data { ++ /* * Non-Endpoint Specific Event flag */ ++ USB3_EVENT_NON_EP_BIT = 0x01, ++ USB3_EVENT_NON_EP_SHIFT = 0, ++ ++ /* * Non-Endpoint Specific Event Type */ ++ USB3_EVENT_INTTYPE_BITS = 0xfe, ++ USB3_EVENT_INTTYPE_SHIFT = 1, ++ ++ /* * Non-Endpoint Specific Event Type values */ ++ USB3_EVENT_DEV_INT = 0, /* * @< */ ++ USB3_EVENT_OTG_INT = 1, /* * @< */ ++ USB3_EVENT_CARKIT_INT = 3, /* * @< */ ++ USB3_EVENT_I2C_INT = 4, ++} gevent_data_t; ++ ++/* * ++ * This enum represents the non-generic bit fields of an Event Buffer entry ++ * for Device Specific events (DEVT). ++ */ ++typedef enum devt_data { ++ /* * Device Specific Event Type */ ++ USB3_DEVT_BITS = 0x00000f00, ++ USB3_DEVT_SHIFT = 8, ++ ++ /* * Device Specific Event Type values */ ++ USB3_DEVT_DISCONN = 0, /* * @< */ ++ USB3_DEVT_USBRESET = 1, /* * @< */ ++ USB3_DEVT_CONNDONE = 2, /* * @< */ ++ USB3_DEVT_ULST_CHNG = 3, /* * @< */ ++ USB3_DEVT_WKUP = 4, /* * @< */ ++ USB3_DEVT_HIBER_REQ = 5, /* * @< */ ++ USB3_DEVT_EOPF = 6, /* * @< */ ++ USB3_DEVT_SOF = 7, /* * @< */ ++ USB3_DEVT_ERRATICERR = 9, /* * @< */ ++ USB3_DEVT_CMD_CMPL = 10, /* * @< */ ++ USB3_DEVT_OVERFLOW = 11, /* * @< */ ++ USB3_DEVT_VNDR_DEV_TST_RCVD = 12, /* * @< */ ++ USB3_DEVT_INACT_TIMEOUT_RCVD = 13, ++ ++ /* * Event Information */ ++ USB3_DEVT_EVT_INFO_BITS = 0xffff0000, ++ USB3_DEVT_EVT_INFO_SHIFT = 16, ++ ++ /* * USB/Link State */ ++ USB3_DEVT_ULST_STATE_BITS = 0x000f0000, ++ USB3_DEVT_ULST_STATE_SHIFT = 16, ++ ++ /* * USB/Link State values in SS */ ++ USB3_LINK_STATE_U0 = 0, /* * @< */ ++ USB3_LINK_STATE_U1 = 1, /* * @< */ ++ USB3_LINK_STATE_U2 = 2, /* * @< */ ++ USB3_LINK_STATE_U3 = 3, /* * @< */ ++ USB3_LINK_STATE_SS_DIS = 4, /* * @< */ ++ USB3_LINK_STATE_RX_DET = 5, /* * @< */ ++ USB3_LINK_STATE_SS_INACT = 6, /* * @< */ ++ USB3_LINK_STATE_POLL = 7, /* * @< */ ++ USB3_LINK_STATE_RECOV = 8, /* * @< */ ++ USB3_LINK_STATE_HRESET = 9, /* * @< */ ++ USB3_LINK_STATE_CMPLY = 10, /* * @< */ ++ USB3_LINK_STATE_LPBK = 11, /* * @< */ ++ USB3_LINK_STATE_RESET = 14, /* * @< */ ++ USB3_LINK_STATE_RESUME = 15, ++ ++ /* * USB/Link State values in HS/FS/LS */ ++ USB3_LINK_STATE_ON = 0, /* * @< */ ++ USB3_LINK_STATE_SLEEP = 2, /* * @< */ ++ USB3_LINK_STATE_SUSPEND = 3, /* * @< */ ++ USB3_LINK_STATE_EARLY_SUSPEND = 5, ++ ++ USB3_DEVT_ULST_SS_BIT = 0x00100000, ++ USB3_DEVT_ULST_SS_SHIFT = 20, ++ ++#define USB3_DEVT_HIBER_STATE_BITS USB3_DEVT_ULST_STATE_BITS ++#define USB3_DEVT_HIBER_STATE_SHIFT USB3_DEVT_ULST_STATE_SHIFT ++ ++#define USB3_DEVT_HIBER_SS_BIT USB3_DEVT_ULST_SS_BIT ++#define USB3_DEVT_HIBER_SS_SHIFT USB3_DEVT_ULST_SS_SHIFT ++ ++ USB3_DEVT_HIBER_HIRD_BITS = 0x0f000000, ++ USB3_DEVT_HIBER_HIRD_SHIFT = 24, ++} devt_data_t; ++ ++/* * ++ * This enum represents the bit fields of an Event Buffer entry for ++ * Endpoint Specific events (DEPEVT). ++ */ ++typedef enum depevt_data { ++ /* * Endpoint Number */ ++ USB3_DEPEVT_EPNUM_BITS = 0x0000003e, ++ USB3_DEPEVT_EPNUM_SHIFT = 1, ++ ++ /* * Endpoint Event Type */ ++ USB3_DEPEVT_INTTYPE_BITS = 0x000003c0, ++ USB3_DEPEVT_INTTYPE_SHIFT = 6, ++ ++ /* * Endpoint Event Type values */ ++ USB3_DEPEVT_XFER_CMPL = 1, /* * @< */ ++ USB3_DEPEVT_XFER_IN_PROG = 2, /* * @< */ ++ USB3_DEPEVT_XFER_NRDY = 3, /* * @< */ ++ USB3_DEPEVT_FIFOXRUN = 4, /* * @< */ ++ USB3_DEPEVT_STRM_EVT = 6, /* * @< */ ++ USB3_DEPEVT_EPCMD_CMPL = 7, ++ ++ /* * Event Status for Start Xfer Command */ ++ USB3_DEPEVT_NO_MORE_RSCS_BIT = 0x00001000, ++ USB3_DEPEVT_NO_MORE_RSCS_SHIFT = 12, ++ USB3_DEPEVT_ISOC_TIME_PASSED_BIT = 0x00002000, ++ USB3_DEPEVT_ISOC_TIME_PASSED_SHIFT = 13, ++ ++ /* * Event Status for Stream Event */ ++ USB3_DEPEVT_STRM_EVT_BITS = 0x0000f000, ++ USB3_DEPEVT_STRM_EVT_SHIFT = 12, ++ ++ /* * Stream Event Status values */ ++ USB3_DEPEVT_STRM_FOUND = 1, /* * @< */ ++ USB3_DEPEVT_STRM_NOT_FOUND = 2, ++ ++ /* * Event Status for Xfer Complete or Xfer In Progress Event */ ++ USB3_DEPEVT_BUS_ERR_BIT = 0x00001000, ++ USB3_DEPEVT_BUS_ERR_SHIFT = 12, ++ USB3_DEPEVT_SHORT_PKT_BIT = 0x00002000, ++ USB3_DEPEVT_SHORT_PKT_SHIFT = 13, ++ USB3_DEPEVT_IOC_BIT = 0x00004000, ++ USB3_DEPEVT_IOC_SHIFT = 14, ++ USB3_DEPEVT_LST_BIT = 0x00008000, ++ USB3_DEPEVT_LST_SHIFT = 15, ++#define USB3_DEPEVT_MISSED_ISOC_BIT USB3_DEPEVT_LST_BIT ++#define USB3_DEPEVT_MISSED_ISOC_SHIFT USB3_DEPEVT_LST_SHIFT ++ ++ /* * Event Status for Xfer Not Ready Event */ ++ USB3_DEPEVT_CTRL_BITS = 0x00003000, ++ USB3_DEPEVT_CTRL_SHIFT = 12, ++ USB3_DEPEVT_XFER_ACTIVE_BIT = 0x00008000, ++ USB3_DEPEVT_XFER_ACTIVE_SHIFT = 15, ++ ++ /* * Xfer Not Ready Event Status values */ ++ USB3_DEPEVT_CTRL_SETUP = 0, /* * @< */ ++ USB3_DEPEVT_CTRL_DATA = 1, /* * @< */ ++ USB3_DEPEVT_CTRL_STATUS = 2, ++ ++ /* * Stream ID */ ++ USB3_DEPEVT_STRM_ID_BITS = 0xffff0000, ++ USB3_DEPEVT_STRM_ID_SHIFT = 16, ++ ++ /* * Isoc uFrame Number (for Xfer Not Ready on Isoc EP) */ ++ USB3_DEPEVT_ISOC_UFRAME_NUM_BITS = 0xffff0000, ++ USB3_DEPEVT_ISOC_UFRAME_NUM_SHIFT = 16, ++ ++ /* * Xfer Resource Index (for Start Xfer Command) */ ++ USB3_DEPEVT_XFER_RSC_IDX_BITS = 0x007f0000, ++ USB3_DEPEVT_XFER_RSC_IDX_SHIFT = 16, ++ ++ /* * Current Data Sequence Number (for Get Endpoint State Command) */ ++ USB3_DEPEVT_CUR_DAT_SEQ_NUM_BITS = 0x001f0000, ++ USB3_DEPEVT_CUR_DAT_SEQ_NUM_SHIFT = 16, ++ ++ /* * Flow Control State (for Get Endpoint State Command) */ ++ USB3_DEPEVT_FLOW_CTRL_BIT = 0x00200000, ++ USB3_DEPEVT_FLOW_CTRL_SHIFT = 21, ++} depevt_data_t; ++ ++/* * ++ * This enum represents the non-generic bit fields of an Event Buffer entry ++ * for other Core events (GEVT). ++ */ ++typedef enum gevt_data { ++ /* * PHY Port Number */ ++ USB3_GINT_PHY_PORT_BITS = 0xf00, ++ USB3_GINT_PHY_PORT_SHIFT = 8, ++} gevt_data_t; ++ ++/* * ++ * This struct represents the 32-bit register fields of the Event Buffer ++ * Registers (GEVENTBUFn). ++ */ ++typedef struct geventbuf_data { ++ /* * Event Buffer Address Register Low Word */ ++ volatile unsigned int geventadr_lo; ++ ++ /* * Event Buffer Address Register High Word */ ++ volatile unsigned int geventadr_hi; ++ ++ /* * Event Buffer Size Register. ++ * Fields defined in enum @ref geventsiz_data. */ ++ volatile unsigned int geventsiz; ++ ++ /* * Event Buffer Count Register. ++ * Fields defined in enum @ref geventcnt_data. */ ++ volatile unsigned int geventcnt; ++} geventbuf_data_t; ++ ++#define USB3_CORE_REG_BASE 0xC000 ++ ++/* * ++ * Core Global Registers Offsets 100h-5FCh. ++ * ++ * The dwc_usb3_core_global_regs structure defines the size ++ * and relative field offsets for the Core Global Registers. ++ */ ++typedef struct usb3_core_global_regs { ++#define USB3_CORE_GLOBAL_REG_OFFSET 0x100 ++ ++ /* * Core BIU Configuration 0 Register Offset: 100h. ++ * Fields defined in enum @ref gsbuscfg0_data. */ ++ volatile unsigned int gsbuscfg0; ++ ++ /* * Core BIU Configuration 1 Register Offset: 104h. ++ * Fields defined in enum @ref gsbuscfg1_data. */ ++ volatile unsigned int gsbuscfg1; ++ ++ /* * Core Tx Threshold Control Register Offset: 108h. ++ * Fields defined in enum @ref gtxthrcfg_data. */ ++ volatile unsigned int gtxthrcfg; ++ ++ /* * Core Threshold Control Register Offset: 10Ch. ++ * Fields defined in enum @ref grxthrcfg_data. */ ++ volatile unsigned int grxthrcfg; ++ ++ /* * Core Control Register Offset: 110h. ++ * Fields defined in enum @ref gctl_data. */ ++ volatile unsigned int gctl; ++ ++ /* * Core Interrupt Mask Register Offset: 114h. ++ * Fields defined in enum @ref gevten_data. */ ++ volatile unsigned int gevten; ++ ++ /* * Core Status Register Offset: 118h. ++ * Fields defined in enum @ref gsts_data. */ ++ volatile unsigned int gsts; ++ ++ /* * reserved Offset: 11Ch */ ++ volatile unsigned int reserved0; ++ ++#define USB3_CORE_GSNPSID_REG_OFFSET 0x120 ++ ++ /* * Synopsys ID Register Offset: 120h */ ++ volatile unsigned int gsnpsid; ++ ++ /* * General Purpose I/O Register Offset: 124h */ ++ volatile unsigned int ggpio; ++ ++ /* * User ID Register Offset: 128h */ ++ volatile unsigned int guid; ++ ++ /* * reserved Offset: 12Ch */ ++ volatile unsigned int reserved1; ++ ++ /* * Bus Error Address Register Offset: 130h */ ++ volatile unsigned int gbuserraddrlo; ++ ++ /* * Bus Error Address Register Offset: 134h */ ++ volatile unsigned int gbuserraddrhi; ++ ++ /* * reserved Offset: 138h-13Ch */ ++ volatile unsigned int reserved2[2]; ++ ++ /* * Hardware Parameter 0 Register Offset: 140h. ++ * Fields defined in enum @ref ghwparams0_data. */ ++ volatile unsigned int ghwparams0; ++ ++ /* * Hardware Parameter 1 Register Offset: 144h. ++ * Fields defined in enum @ref ghwparams1_data. */ ++ volatile unsigned int ghwparams1; ++ ++ /* * Hardware Parameter 2 Register Offset: 148h. ++ * Fields defined in enum @ref ghwparams2_data. */ ++ volatile unsigned int ghwparams2; ++ ++ /* * Hardware Parameter 3 Register Offset: 14Ch. ++ * Fields defined in enum @ref ghwparams3_data. */ ++ volatile unsigned int ghwparams3; ++ ++ /* * Hardware Parameter 4 Register Offset: 150h. ++ * Fields defined in enum @ref ghwparams4_data. */ ++ volatile unsigned int ghwparams4; ++ ++ /* * Hardware Parameter 5 Register Offset: 154h. ++ * Fields defined in enum @ref ghwparams5_data. */ ++ volatile unsigned int ghwparams5; ++ ++ /* * Hardware Parameter 6 Register Offset: 158h. ++ * Fields defined in enum @ref ghwparams6_data. */ ++ volatile unsigned int ghwparams6; ++ ++ /* * Hardware Parameter 7 Register Offset: 15Ch. ++ * Fields defined in enum @ref ghwparams7_data. */ ++ volatile unsigned int ghwparams7; ++ ++ /* * Debug Queue/FIFO Space Register Offset: 160h. ++ * Fields defined in enum @ref gdbgfifospace_data. */ ++ volatile unsigned int gdbgfifospace; ++ ++ /* * Debug LTSSM Register Offset: 164h. ++ * Fields defined in enum @ref gdbgltssm_data */ ++ volatile unsigned int gdbgltssm; ++ ++ /* * reserved Offset: 168h-1FCh */ ++ volatile unsigned int reserved3[38]; ++ ++ /* * USB2 Configuration Registers Offset: 200h-23Ch. ++ * Fields defined in enum @ref gusb2phycfg_data. */ ++ volatile unsigned int gusb2phycfg[16]; ++ ++ /* * USB2 I2C Access Registers Offset: 240h-27Ch. ++ * Fields defined in enum @ref gusb2i2cctl_data. */ ++ volatile unsigned int gusb2i2cctl[16]; ++ ++ /* * USB2 PHY Vendor Control Registers Offset: 280h-2BCh. ++ * Fields defined in enum @ref gusb2phyacc_data. */ ++ volatile unsigned int gusb2phyacc[16]; ++ ++ /* * USB3 Pipe Control Registers Offset: 2C0h-2FCh. ++ * Fields defined in enum @ref gusb3pipectl_data. */ ++ volatile unsigned int gusb3pipectl[16]; ++ ++ /* * Transmit FIFO Size Registers Offset: 300h-37Ch. ++ * Fields defined in enum @ref gfifosize_data. */ ++ volatile unsigned int gtxfifosiz[32]; ++ ++ /* * Receive FIFO Size Registers Offset: 380h-3FC0h. ++ * Fields defined in enum @ref gfifosize_data. */ ++ volatile unsigned int grxfifosiz[32]; ++ ++ /* * Event Buffer Registers Offset: 400h-5FCh. ++ * Fields defined in struct @ref geventbuf_data. */ ++ struct geventbuf_data geventbuf[32]; ++ ++ /* * Hardware Parameter 8 Register Offset: 600h. ++ * Fields defined in enum @ref ghwparams8_data. */ ++ volatile unsigned int ghwparams8; ++} usb3_core_global_regs_t; ++ ++ ++/* ************************************************************************** */ ++/* Device Global Registers */ ++ ++/* * ++ * This enum represents the bit fields in the Device Configuration ++ * Register (DCFG). ++ */ ++typedef enum dcfg_data { ++ /* * Device Speed Access: R_W */ ++ USB3_DCFG_DEVSPD_BITS = 0x000007, ++ USB3_DCFG_DEVSPD_SHIFT = 0, ++ ++ /* * Device Speed values */ ++ USB3_SPEED_HS_PHY_30MHZ_OR_60MHZ = 0, /* * @< */ ++ USB3_SPEED_FS_PHY_30MHZ_OR_60MHZ = 1, /* * @< */ ++ USB3_SPEED_LS_PHY_6MHZ = 2, /* * @< */ ++ USB3_SPEED_FS_PHY_48MHZ = 3, /* * @< */ ++ USB3_SPEED_SS_PHY_125MHZ_OR_250MHZ = 4, ++ ++ /* * Device Address Access: R_W */ ++ USB3_DCFG_DEVADDR_BITS = 0x0003f8, ++ USB3_DCFG_DEVADDR_SHIFT = 3, ++ ++ /* * Periodic Frame Interval Access: R_W */ ++ USB3_DCFG_PER_FR_INTVL_BITS = 0x000c00, ++ USB3_DCFG_PER_FR_INTVL_SHIFT = 10, ++ ++ /* * Periodic Frame Interval values */ ++ USB3_DCFG_PER_FR_INTVL_80 = 0, /* * @< */ ++ USB3_DCFG_PER_FR_INTVL_85 = 1, /* * @< */ ++ USB3_DCFG_PER_FR_INTVL_90 = 2, /* * @< */ ++ USB3_DCFG_PER_FR_INTVL_95 = 3, ++ ++ /* * Device Interrupt Number Access: R_W */ ++ USB3_DCFG_DEV_INTR_NUM_BITS = 0x01f000, ++ USB3_DCFG_DEV_INTR_NUM_SHIFT = 12, ++ ++ /* * Number of Receive Buffers Access: R_W */ ++ USB3_DCFG_NUM_RCV_BUF_BITS = 0x3e0000, ++ USB3_DCFG_NUM_RCV_BUF_SHIFT = 17, ++ ++ /* * LPM Capable Access: R_W */ ++ USB3_DCFG_LPM_CAP_BIT = 0x400000, ++ USB3_DCFG_LPM_CAP_SHIFT = 22, ++} dcfg_data_t; ++ ++/* * ++ * This enum represents the bit fields in the Device Control ++ * Register (DCTL). ++ */ ++typedef enum dctl_data { ++ /* * Soft Disconnect Access: R_W */ ++ USB3_DCTL_SFT_DISCONN_BIT = 0x00000001, ++ USB3_DCTL_SFT_DISCONN_SHIFT = 0, ++ ++ /* * Test Control Access: R_W */ ++ USB3_DCTL_TSTCTL_BITS = 0x0000001e, ++ USB3_DCTL_TSTCTL_SHIFT = 1, ++ ++ /* * USB/Link State Change Request Access: R_W */ ++ USB3_DCTL_ULST_CHNG_REQ_BITS = 0x000001e0, ++ USB3_DCTL_ULST_CHNG_REQ_SHIFT = 5, ++ ++ /* * Requested Link State Transition/Action In SS Mode */ ++ USB3_LINK_STATE_REQ_NO_ACTION = 0, ++ USB3_LINK_STATE_REQ_SS_DISABLED = 4, ++ USB3_LINK_STATE_REQ_RX_DETECT = 5, ++ USB3_LINK_STATE_REQ_INACTIVE = 6, ++ USB3_LINK_STATE_REQ_RECOVERY = 8, ++ USB3_LINK_STATE_REQ_COMPLIANCE = 10, ++ USB3_LINK_STATE_REQ_LOOPBACK = 11, ++ USB3_LINK_STATE_REQ_HOST_MODE_ONLY = 15, ++ ++ /* * Requested Link State Transition/Action In HS/FS/LS Mode */ ++ USB3_LINK_STATE_REQ_REMOTE_WAKEUP = 8, ++ ++ /* * U1/U2 control Access: R_W */ ++ USB3_DCTL_ACCEPT_U1_EN_BIT = 0x00000200, ++ USB3_DCTL_ACCEPT_U1_EN_SHIFT = 9, ++ USB3_DCTL_INIT_U1_EN_BIT = 0x00000400, ++ USB3_DCTL_INIT_U1_EN_SHIFT = 10, ++ USB3_DCTL_ACCEPT_U2_EN_BIT = 0x00000800, ++ USB3_DCTL_ACCEPT_U2_EN_SHIFT = 11, ++ USB3_DCTL_INIT_U2_EN_BIT = 0x00001000, ++ USB3_DCTL_INIT_U2_EN_SHIFT = 12, ++ ++ /* * Controller Save State Access: R_W */ ++ USB3_DCTL_CSS_BIT = 0x00010000, ++ USB3_DCTL_CSS_SHIFT = 16, ++ ++ /* * Controller Restore State Access: R_W */ ++ USB3_DCTL_CRS_BIT = 0x00020000, ++ USB3_DCTL_CRS_SHIFT = 17, ++ ++ /* * L1 Hibernation Enable Access: R_W */ ++ USB3_DCTL_L1_HIBER_EN_BIT = 0x00040000, ++ USB3_DCTL_L1_HIBER_EN_RES_SHIFT = 18, ++ ++ /* * Keep Connect (for hibernation) Access: R_W */ ++ USB3_DCTL_KEEP_CONNECT_BIT = 0x00080000, ++ USB3_DCTL_KEEP_CONNECT_SHIFT = 19, ++ ++ /* * LPM Response Access: R_W */ ++ USB3_DCTL_APP_L1_RES_BIT = 0x00800000, ++ USB3_DCTL_APP_L1_RES_SHIFT = 23, ++ ++ /* HIRD Threshold Access: R_W */ ++ USB3_DCTL_HIRD_THR_BITS = 0x1f000000, ++ USB3_DCTL_HIRD_THR_SHIFT = 24, ++ ++ /* * Light Soft Reset Access: R_W */ ++ USB3_DCTL_LSFT_RST_BIT = 0x20000000, ++ USB3_DCTL_LSFT_RST_SHIFT = 29, ++ ++ /* * Core Soft Reset Access: R_W */ ++ USB3_DCTL_CSFT_RST_BIT = 0x40000000, ++ USB3_DCTL_CSFT_RST_SHIFT = 30, ++ ++ /* * Run/Stop Access: R_W */ ++ USB3_DCTL_RUN_STOP_BIT = 0x80000000, ++ USB3_DCTL_RUN_STOP_SHIFT = 31, ++} dctl_data_t; ++ ++/* * ++ * This enum represents the bit fields of the Device Event Enable ++ * Register (DEVTEN). ++ */ ++typedef enum devten_data { ++ /* * Disconnect Detected Event Enable Access: R_W */ ++ USB3_DEVTEN_DISCONN_BIT = 0x0001, ++ USB3_DEVTEN_DISCONN_SHIFT = 0, ++ ++ /* * USB Reset Enable Access: R_W */ ++ USB3_DEVTEN_USBRESET_BIT = 0x0002, ++ USB3_DEVTEN_USBRESET_SHIFT = 1, ++ ++ /* * Connect Done Enable Access: R_W */ ++ USB3_DEVTEN_CONNDONE_BIT = 0x0004, ++ USB3_DEVTEN_CONNDONE_SHIFT = 2, ++ ++ /* * USB/Link State Change Event Enable Access: R_W */ ++ USB3_DEVTEN_ULST_CHNG_BIT = 0x0008, ++ USB3_DEVTEN_ULST_CHNG_SHIFT = 3, ++ ++ /* * Resume/Remote-Wakeup Event Enable Access: R_W */ ++ USB3_DEVTEN_WKUP_BIT = 0x0010, ++ USB3_DEVTEN_WKUP_SHIFT = 4, ++ ++ /* * Hibernation Request Event Enable Access: R_W */ ++ USB3_DEVTEN_HIBER_REQ_EVT_BIT = 0x0020, ++ USB3_DEVTEN_HIBER_REQ_EVT_SHIFT = 5, ++ ++ /* * End of Periodic Frame Event Enable Access: R_W */ ++ USB3_DEVTEN_EOPF_BIT = 0x0040, ++ USB3_DEVTEN_EOPF_SHIFT = 6, ++ ++ /* * Start of (Micro)Frame Enable Access: R_W */ ++ USB3_DEVTEN_SOF_BIT = 0x0080, ++ USB3_DEVTEN_SOF_SHIFT = 7, ++ ++ /* * Erratic Error Event Enable Access: R_W */ ++ USB3_DEVTEN_ERRATICERR_BIT = 0x0200, ++ USB3_DEVTEN_ERRATICERR_SHIFT = 9, ++ ++ /* * U2 Inactivity Timeout Enable Access: R_W */ ++ USB3_DEVTEN_INACT_TIMEOUT_BIT = 0x2000, ++ USB3_DEVTEN_INACT_TIMEOUT_SHIFT = 13, ++} devten_data_t; ++ ++/* * ++ * This enum represents the bit fields in the Device Status ++ * Register (DSTS). ++ */ ++typedef enum dsts_data { ++ /* * Connected Speed Access: RO. ++ * (see enum @ref dcfg_data for values) */ ++ USB3_DSTS_CONNSPD_BITS = 0x00000007, ++ USB3_DSTS_CONNSPD_SHIFT = 0, ++ ++ /* * (Micro)Frame Number of Received SOF Access: RO */ ++ USB3_DSTS_SOF_FN_BITS = 0x0001fff8, ++ USB3_DSTS_SOF_FN_SHIFT = 3, ++ ++ /* * RX Fifo Empty Access: RO */ ++ USB3_DSTS_RXFIFO_EMPTY_BIT = 0x00020000, ++ USB3_DSTS_RXFIFO_EMPTY_SHIFT = 17, ++ ++ /* * USB/Link State Access: RO */ ++ USB3_DSTS_USBLNK_STATE_BITS = 0x003c0000, ++ USB3_DSTS_USBLNK_STATE_SHIFT = 18, ++ ++ /* * USB/Link State values same as for devt_data_t */ ++ ++ /* * Device Controller Halted Access: RO */ ++ USB3_DSTS_DEV_CTRL_HLT_BIT = 0x00400000, ++ USB3_DSTS_DEV_CTRL_HLT_SHIFT = 22, ++ ++ /* * Core Idle Access: RO */ ++ USB3_DSTS_CORE_IDLE_BIT = 0x00800000, ++ USB3_DSTS_CORE_IDLE_SHIFT = 23, ++ ++ /* * Save State Status Access: RO */ ++ USB3_DSTS_SSS_BIT = 0x01000000, ++ USB3_DSTS_SSS_SHIFT = 24, ++ ++ /* * Restore State Status Access: RO */ ++ USB3_DSTS_RSS_BIT = 0x02000000, ++ USB3_DSTS_RSS_SHIFT = 25, ++ ++ /* * Save/Restore Error Access: RO */ ++ USB3_DSTS_SRE_BIT = 0x10000000, ++ USB3_DSTS_SRE_SHIFT = 28, ++ ++ /* * Link-state Not Ready Access: RO */ ++ USB3_DSTS_LNR_BIT = 0x20000000, ++ USB3_DSTS_LNR_SHIFT = 29, ++} dsts_data_t; ++ ++/* * ++ * This enum represents the bit fields in the Device Generic Command Parameter ++ * Register (DGCMDPARn) for the various commands. ++ */ ++typedef enum dgcmdpar_data { ++ /* * Force Link PM Accept ++ * (for USB3_DGCMD_XMIT_SET_LINK_FUNC_LMP command) */ ++ USB3_DGCMDPAR_FORCE_LINK_PM_ACCEPT_BIT = 0x0001, ++ USB3_DGCMDPAR_FORCE_LINK_PM_ACCEPT_SHIFT = 0, ++ ++ /* * Vendor Specific Test Select ++ * (for USB3_DGCMD_XMIT_VEND_DEV_TST_LMP command) */ ++ USB3_DGCMDPAR_VEND_SPEC_TST_BITS = 0x00ff, ++ USB3_DGCMDPAR_VEND_SPEC_TST_SHIFT = 0, ++ ++ /* * Interface Number (for USB3_DGCMD_XMIT_RMT_WKUP_SIG command) */ ++ USB3_DGCMDPAR_INTF_NUM_BITS = 0x00ff, ++ USB3_DGCMDPAR_INTF_NUM_SHIFT = 0, ++ ++ /* * Best Effort Latency Tolerance Value ++ * (for USB3_DGCMD_XMIT_LAT_TOL_MSG command) */ ++ USB3_DGCMDPAR_BELT_VALUE_BITS = 0x03ff, ++ USB3_DGCMDPAR_BELT_VALUE_SHIFT = 0, ++ ++ /* * Best Effort Latency Tolerance Scale ++ * (for USB3_DGCMD_XMIT_LAT_TOL_MSG command) */ ++ USB3_DGCMDPAR_BELT_SCALE_BITS = 0x0c00, ++ USB3_DGCMDPAR_BELT_SCALE_SHIFT = 10, ++ ++ /* * Latency Scale values (ns) */ ++ USB3_LATENCY_VALUE_MULT_1024 = 1, /* * @< */ ++ USB3_LATENCY_VALUE_MULT_32768 = 2, /* * @< */ ++ USB3_LATENCY_VALUE_MULT_1048576 = 3, ++ ++ /* * Bus Interval Adjustment ++ * (for USB3_DGCMD_XMIT_BUS_INTVL_ADJ_MSG command) */ ++ USB3_DGCMDPAR_BUS_INTVL_ADJ_BITS = 0xffff, ++ USB3_DGCMDPAR_BUS_INTVL_ADJ_SHIFT = 0, ++ ++ /* * Bus Interval Adjustment values (units) */ ++ USB3_BUS_INTVL_ADJ_DEC_1 = 0xffff, /* * @< */ ++ USB3_BUS_INTVL_ADJ_DEC_32768 = 0x8000, /* * @< */ ++ USB3_BUS_INTVL_ADJ_INC_32767 = 0x7fff, /* * @< */ ++ USB3_BUS_INTVL_ADJ_NO_CHNG = 0x0000, ++ ++ USB3_DGCMDPAR_HOST_ROLE_REQ_INITIATE = 0x01, ++ USB3_DGCMDPAR_HOST_ROLE_REQ_CONFIRM = 0x02, ++} dgcmdpar_data_t; ++ ++/* * ++ * This enum represents the bit fields in the Device Generic Command ++ * Register (DGCMDn). ++ */ ++typedef enum dgcmd_data { ++ /* * Command Type Access: R_W */ ++ USB3_DGCMD_TYP_BITS = 0x0ff, ++ USB3_DGCMD_TYP_SHIFT = 0, ++ ++ /* * Command Type values */ ++ USB3_DGCMD_XMIT_SET_LINK_FUNC_LMP = 1, /* * @< */ ++ USB3_DGCMD_SET_PERIODIC_PARAMS = 2, /* * @< */ ++ USB3_DGCMD_XMIT_FUNC_WAKE_DEV_NOTIF = 3, /* * @< */ ++ USB3_DGCMD_SET_SCRATCHPAD_ARRAY_ADR_LO = 4, /* * @< */ ++ USB3_DGCMD_SET_SCRATCHPAD_ARRAY_ADR_HI = 5, /* * @< */ ++ USB3_DGCMD_XMIT_HOST_ROLE_REQUEST = 6, /* * @< */ ++ USB3_DGCMD_SET_EP_NRDY = 12, /* * @< */ ++ USB3_DGCMD_RUN_SOC_BUS_LOOPBK_TST = 16, /* * @< */ ++ ++ /* * Command Interrupt on Complete Access: R_W */ ++ USB3_DGCMD_IOC_BIT = 0x100, ++ USB3_DGCMD_IOC_SHIFT = 8, ++ ++ /* * Command Active Access: R_W */ ++ USB3_DGCMD_ACT_BIT = 0x400, ++ USB3_DGCMD_ACT_SHIFT = 10, ++ ++ /* * Command Status Access: R_W */ ++ USB3_DGCMD_STS_BITS = 0xf000, ++ USB3_DGCMD_STS_SHIFT = 12, ++ ++ /* * Command Status values */ ++ USB3_DGCMD_STS_ERROR = 15, ++} dgcmd_data_t; ++ ++/* * ++ * This enum represents the bit fields in the Device Endpoint Mapping ++ * Registers (DEPMAPn). ++ */ ++typedef enum depmap_data { ++ /* * Resource Number Access: R_W / RO */ ++ USB3_EPMAP_RES_NUM_BITS = 0x1f, ++ USB3_EPMAP_RES_NUM_SHIFT = 0, ++} depmap_data_t; ++ ++/* * ++ * Device Global Registers Offsets 700h-7FCh. ++ * ++ * The following structures define the size and relative field offsets ++ * for the Device Mode Global Registers. ++ */ ++typedef struct usb3_dev_global_regs { ++#define USB3_DEV_GLOBAL_REG_OFFSET 0x700 ++ ++ /* * Device Configuration Register Offset: 700h. ++ * Fields defined in enum @ref dcfg_data. */ ++ volatile unsigned int dcfg; ++ ++ /* * Device Control Register Offset: 704h. ++ * Fields defined in enum @ref dctl_data. */ ++ volatile unsigned int dctl; ++ ++ /* * Device All Endpoints Interrupt Mask Register Offset: 708h. ++ * Fields defined in enum @ref devten_data. */ ++ volatile unsigned int devten; ++ ++ /* * Device Status Register Offset: 70Ch. ++ * Fields defined in enum @ref dsts_data. */ ++ volatile unsigned int dsts; ++ ++ /* * Device Generic Command Parameter Register Offset: 710h. ++ * Fields defined in enum @ref dgcmdpar_data. */ ++ volatile unsigned int dgcmdpar; ++ ++ /* * Device Generic Command Register Offset: 714h. ++ * Fields defined in enum @ref dgcmd_data. */ ++ volatile unsigned int dgcmd; ++ ++ /* * reserved Offset: 718h-71Ch */ ++ volatile unsigned int reserved[2]; ++ ++ /* * Device Active Logical Endpoint Enable Register Offset: 720h. ++ * One bit per logical endpoint, bit0=EP0 ... bit31=EP31. */ ++ volatile unsigned int dalepena; ++} usb3_dev_global_regs_t; ++ ++ ++/* ************************************************************************** */ ++/* Device Endpoint Specific Registers */ ++ ++/* * ++ * This enum represents the bit fields in the Device Endpoint Command ++ * Parameter 1 Register (DEPCMDPAR1n) for the Set Endpoint Configuration ++ * (DEPCFG) command. ++ */ ++typedef enum depcfgpar1_data { ++ /* * Interrupt number */ ++ USB3_EPCFG1_INTRNUM_BITS = 0x0000003f, ++ USB3_EPCFG1_INTRNUM_SHIFT = 0, ++ ++ /* * Stream Completed */ ++ USB3_EPCFG1_XFER_CMPL_BIT = 0x00000100, ++ USB3_EPCFG1_XFER_CMPL_SHIFT = 8, ++ ++ /* * Stream In Progress */ ++ USB3_EPCFG1_XFER_IN_PROG_BIT = 0x00000200, ++ USB3_EPCFG1_XFER_IN_PROG_SHIFT = 9, ++ ++ /* * Stream Not Ready */ ++ USB3_EPCFG1_XFER_NRDY_BIT = 0x00000400, ++ USB3_EPCFG1_XFER_NRDY_SHIFT = 10, ++ ++ /* * Rx FIFO Underrun / Tx FIFO Overrun */ ++ USB3_EPCFG1_FIFOXRUN_BIT = 0x00000800, ++ USB3_EPCFG1_FIFOXRUN_SHIFT = 11, ++ ++ /* * Back-to-Back Setup Packets Received */ ++ USB3_EPCFG1_SETUP_PNDG_BIT = 0x00001000, ++ USB3_EPCFG1_SETUP_PNDG_SHIFT = 12, ++ ++ /* * Endpoint Command Complete */ ++ USB3_EPCFG1_EPCMD_CMPL_BIT = 0x00002000, ++ USB3_EPCFG1_EPCMD_CMPL_SHIFT = 13, ++ ++ /* * Endpoint b_interval */ ++ USB3_EPCFG1_BINTERVAL_BITS = 0x00ff0000, ++ USB3_EPCFG1_BINTERVAL_SHIFT = 16, ++ ++ /* * Endpoint Stream Capability */ ++ USB3_EPCFG1_STRM_CAP_BIT = 0x01000000, ++ USB3_EPCFG1_STRM_CAP_SHIFT = 24, ++ ++ /* * Endpoint Direction */ ++ USB3_EPCFG1_EP_DIR_BIT = 0x02000000, ++ USB3_EPCFG1_EP_DIR_SHIFT = 25, ++ ++ /* * Endpoint Number */ ++ USB3_EPCFG1_EP_NUM_BITS = 0x3c000000, ++ USB3_EPCFG1_EP_NUM_SHIFT = 26, ++} depcfgpar1_data_t; ++ ++/* * ++ * This enum represents the bit fields in the Device Endpoint Command ++ * Parameter 0 Register (DEPCMDPAR0n) for the Set Endpoint Configuration ++ * (DWC_EPCMD_SET_EP_CFG) command. ++ */ ++typedef enum depcfgpar0_data { ++ /* * Endpoint Type Access: R_W */ ++ USB3_EPCFG0_EPTYPE_BITS = 0x00000006, ++ USB3_EPCFG0_EPTYPE_SHIFT = 1, ++ ++ /* * Endpoint Type values */ ++ USB3_EP_TYPE_CONTROL = 0, /* * @< */ ++ USB3_EP_TYPE_ISOC = 1, /* * @< */ ++ USB3_EP_TYPE_BULK = 2, /* * @< */ ++ USB3_EP_TYPE_INTR = 3, ++ ++ /* * Maximum Packet Size Access: R_W */ ++ USB3_EPCFG0_MPS_BITS = 0x00003ff8, ++ USB3_EPCFG0_MPS_SHIFT = 3, ++ ++ /* * Flow Control State Access: R_W */ ++ USB3_EPCFG0_FLOW_CTRL_STATE_BIT = 0x00010000, ++ USB3_EPCFG0_FLOW_CTRL_STATE_SHIFT = 16, ++ ++ /* * Tx Fifo Number (IN endpoints only) Access: R_W */ ++ USB3_EPCFG0_TXFNUM_BITS = 0x003e0000, ++ USB3_EPCFG0_TXFNUM_SHIFT = 17, ++ ++ /* * Burst Size Access: R_W */ ++ USB3_EPCFG0_BRSTSIZ_BITS = 0x03c00000, ++ USB3_EPCFG0_BRSTSIZ_SHIFT = 22, ++ ++ /* * Data Sequence Num (old) Access: R_W */ ++ USB3_EPCFG0_DSNUM_BITS = 0x7c000000, ++ USB3_EPCFG0_DSNUM_SHIFT = 26, ++ ++ /* * Ignore Data Sequence Num (old) Access: R_W */ ++ USB3_EPCFG0_IGN_DSNUM_BIT = 0x80000000, ++ USB3_EPCFG0_IGN_DSNUM_SHIFT = 31, ++ ++ /* * Config Action (new) Access: R_W */ ++ USB3_EPCFG0_CFG_ACTION_BITS = 0xc0000000, ++ USB3_EPCFG0_CFG_ACTION_SHIFT = 30, ++ ++ /* * Config Action values (new) */ ++ USB3_CFG_ACTION_INIT = 0, /* * @< */ ++ USB3_CFG_ACTION_RESTORE = 1, /* * @< */ ++ USB3_CFG_ACTION_MODIFY = 2, ++} depcfgpar0_data_t; ++ ++/* * ++ * This enum represents the bit fields in the Device Endpoint Command ++ * Register (DEPCMDn). ++ */ ++typedef enum depcmd_data { ++ /* * Command Type Access: R_W */ ++ USB3_EPCMD_TYP_BITS = 0x0ff, ++ USB3_EPCMD_TYP_SHIFT = 0, ++ ++ /* * Command Type values */ ++ USB3_EPCMD_SET_EP_CFG = 1, /* * @< */ ++ USB3_EPCMD_SET_XFER_CFG = 2, /* * @< */ ++ USB3_EPCMD_GET_EP_STATE = 3, /* * @< */ ++ USB3_EPCMD_SET_STALL = 4, /* * @< */ ++ USB3_EPCMD_CLR_STALL = 5, /* * @< */ ++ USB3_EPCMD_START_XFER = 6, /* * @< */ ++ USB3_EPCMD_UPDATE_XFER = 7, /* * @< */ ++ USB3_EPCMD_END_XFER = 8, /* * @< */ ++ USB3_EPCMD_START_NEW_CFG = 9, ++ ++ /* * Command Interrupt on Complete Access: R_W */ ++ USB3_EPCMD_IOC_BIT = 0x100, ++ USB3_EPCMD_IOC_SHIFT = 8, ++ ++ /* * Command Active Access: R_W */ ++ USB3_EPCMD_ACT_BIT = 0x400, ++ USB3_EPCMD_ACT_SHIFT = 10, ++ ++ /* * High Priority / Force RM Bit Access: R_W */ ++ USB3_EPCMD_HP_FRM_BIT = 0x800, ++ USB3_EPCMD_HP_FRM_SHIFT = 11, ++ ++ /* * Command Completion Status Access: R_W */ ++ USB3_EPCMD_CMPL_STS_BITS = 0xf000, ++ USB3_EPCMD_CMPL_STS_SHIFT = 12, ++ ++ /* * Stream Number or uFrame (input) Access: R_W */ ++ USB3_EPCMD_STR_NUM_OR_UF_BITS = 0xffff0000, ++ USB3_EPCMD_STR_NUM_OR_UF_SHIFT = 16, ++ ++ /* * Transfer Resource Index (output) Access: R_W */ ++ USB3_EPCMD_XFER_RSRC_IDX_BITS = 0x007f0000, ++ USB3_EPCMD_XFER_RSRC_IDX_SHIFT = 16, ++} depcmd_data_t; ++ ++/* * ++ * Device Endpoint Specific Registers Offsets 800h-9ECh for OUT, ++ * 810h-9FCh for IN. ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ */ ++typedef struct usb3_dev_ep_regs { ++#define USB3_DEV_OUT_EP_REG_OFFSET 0x800 ++#define USB3_DEV_IN_EP_REG_OFFSET 0x810 ++#define USB3_EP_REG_OFFSET 0x20 ++ ++ /* * Device Endpoint Command Parameter 2 Register Offset: 800h/810h + ++ * (ep_num * 20h) + 00h */ ++ volatile unsigned int depcmdpar2; ++ ++ /* * Device Endpoint Command Parameter 1 Register Offset: 800h/810h + ++ * (ep_num * 20h) + 04h */ ++ volatile unsigned int depcmdpar1; ++ ++ /* * Device Endpoint Command Parameter 0 Register Offset: 800h/810h + ++ * (ep_num * 20h) + 08h */ ++ volatile unsigned int depcmdpar0; ++ ++ /* * Device Endpoint Command Register Offset: 800h/810h + ++ * (ep_num * 20h) + 0Ch. ++ * Fields defined in enum @ref depcmd_data. */ ++ volatile unsigned int depcmd; ++ ++ /* * reserved Offset: 800h/810h + ++ * (ep_num * 20h) + 10h-1Ch */ ++ volatile unsigned int reserved[4]; ++} usb3_dev_ep_regs_t; ++ ++ ++/* ************************************************************************** */ ++/* DMA Descriptor Specific Structures */ ++ ++/* * ++ * This enum represents the bit fields in the DMA Descriptor ++ * Status quadlet. ++ */ ++typedef enum desc_sts_data { ++ /* * Transfer Count */ ++ USB3_DSCSTS_XFRCNT_MAX = 0x1000000, // 16MB ++ USB3_DSCSTS_XFRCNT_BITS = 0x00ffffff, ++ USB3_DSCSTS_XFRCNT_SHIFT = 0, ++ ++ /* * Packet Count Minus 1 (for HS IN transfers) */ ++ USB3_DSCSTS_PCM1_BITS = 0x03000000, ++ USB3_DSCSTS_PCM1_SHIFT = 24, ++ ++ /* * Transfer Request Block Response */ ++ USB3_DSCSTS_TRBRSP_BITS = 0xf0000000, ++ USB3_DSCSTS_TRBRSP_SHIFT = 28, ++ ++ USB3_TRBRSP_MISSED_ISOC_IN = 1, ++ USB3_TRBRSP_SETUP_PEND = 2, ++ USB3_TRBRSP_XFER_IN_PROG = 4, ++} desc_sts_data_t; ++ ++/* * ++ * This enum represents the bit fields in the DMA Descriptor ++ * Control quadlet. ++ */ ++typedef enum desc_ctl_data { ++ /* * Hardware-Owned bit */ ++ USB3_DSCCTL_HWO_BIT = 0x00000001, ++ USB3_DSCCTL_HWO_SHIFT = 0, ++ ++ /* * Last Descriptor bit */ ++ USB3_DSCCTL_LST_BIT = 0x00000002, ++ USB3_DSCCTL_LST_SHIFT = 1, ++ ++ /* * Chain Buffer bit */ ++ USB3_DSCCTL_CHN_BIT = 0x00000004, ++ USB3_DSCCTL_CHN_SHIFT = 2, ++ ++ /* * Continue on Short Packet bit */ ++ USB3_DSCCTL_CSP_BIT = 0x00000008, ++ USB3_DSCCTL_CSP_SHIFT = 3, ++ ++ /* * Transfer Request Block Control field */ ++ USB3_DSCCTL_TRBCTL_BITS = 0x000003f0, ++ USB3_DSCCTL_TRBCTL_SHIFT = 4, ++ ++ /* * Transfer Request Block Control types */ ++ USB3_DSCCTL_TRBCTL_NORMAL = 1, /* * @< */ ++ USB3_DSCCTL_TRBCTL_SETUP = 2, /* * @< */ ++ USB3_DSCCTL_TRBCTL_STATUS_2 = 3, /* * @< */ ++ USB3_DSCCTL_TRBCTL_STATUS_3 = 4, /* * @< */ ++ USB3_DSCCTL_TRBCTL_CTLDATA_1ST = 5, /* * @< */ ++ USB3_DSCCTL_TRBCTL_ISOC_1ST = 6, /* * @< */ ++ USB3_DSCCTL_TRBCTL_ISOC = 7, /* * @< */ ++ USB3_DSCCTL_TRBCTL_LINK = 8, ++ ++ /* * Interrupt on Short Packet bit */ ++ USB3_DSCCTL_ISP_BIT = 0x00000400, ++ USB3_DSCCTL_ISP_SHIFT = 10, ++#define USB3_DSCCTL_IMI_BIT USB3_DSCCTL_ISP_BIT ++#define USB3_DSCCTL_IMI_SHIFT USB3_DSCCTL_ISP_SHIFT ++ ++ /* * Interrupt on Completion bit */ ++ USB3_DSCCTL_IOC_BIT = 0x00000800, ++ USB3_DSCCTL_IOC_SHIFT = 11, ++ ++ /* * Stream ID / SOF Number */ ++ USB3_DSCCTL_STRMID_SOFN_BITS = 0x3fffc000, ++ USB3_DSCCTL_STRMID_SOFN_SHIFT = 14, ++} desc_ctl_data_t; ++ ++/* * ++ * DMA Descriptor structure ++ * ++ * DMA Descriptor structure contains 4 quadlets: ++ * Buffer Pointer Low address, Buffer Pointer High address, Status, and Control. ++ */ ++typedef struct usb3_dma_desc { ++ /* * Buffer Pointer - Low address quadlet */ ++ unsigned int bptl; ++ ++ /* * Buffer Pointer - High address quadlet */ ++ unsigned int bpth; ++ ++ /* * Status quadlet. Fields defined in enum @ref desc_sts_data. */ ++ unsigned int status; ++ ++ /* * Control quadlet. Fields defined in enum @ref desc_ctl_data. */ ++ unsigned int control; ++} usb3_dma_desc_t; ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* __USB3_HW_H__ */ +diff --git a/drivers/usb/gadget/udc3/usb3_intr.c b/drivers/usb/gadget/udc3/usb3_intr.c +new file mode 100644 +index 0000000..169715f +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_intr.c +@@ -0,0 +1,148 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include "usb3_hw.h" ++#include "usb3_drv.h" ++#include ++ ++void ena_eventbuf_intr(usb3_device_t *dev) ++{ ++ unsigned int eventsiz; ++ ++ eventsiz = usb3_rd32(&dev->core_global_regs->geventbuf[0].geventsiz); ++ eventsiz &= ~USB3_EVENTSIZ_INT_MSK_BIT; ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventsiz, eventsiz); ++} ++ ++void dis_eventbuf_intr(usb3_device_t *dev) ++{ ++ unsigned int eventsiz; ++ ++ eventsiz = usb3_rd32(&dev->core_global_regs->geventbuf[0].geventsiz); ++ eventsiz |= USB3_EVENTSIZ_INT_MSK_BIT; ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventsiz, eventsiz); ++} ++ ++void usb3_dis_flush_eventbuf_intr(usb3_device_t *dev) ++{ ++ unsigned int cnt; ++ unsigned int gevntsize; ++ ++ dis_eventbuf_intr(dev); ++ cnt = usb3_rd32(&dev->core_global_regs->geventbuf[0].geventcnt); ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventcnt, cnt); ++ ++ if (cnt != 0) { ++ gevntsize = sizeof(dev->event_buf[0]) * USB3_EVENT_BUF_SIZE; ++ usb_info("evnt count 0x%x, evnt buf size 0x%x\n", cnt, gevntsize); ++ dev->event_ptr += cnt % gevntsize; ++ } ++} ++ ++int get_eventbuf_count(usb3_device_t *dev) ++{ ++ unsigned int cnt; ++ ++ cnt = usb3_rd32(&dev->core_global_regs->geventbuf[0].geventcnt); ++ ++ return cnt & USB3_EVENTCNT_CNT_BITS; ++} ++ ++void update_eventbuf_count(usb3_device_t *dev, int cnt) ++{ ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventcnt, cnt); ++} ++ ++unsigned int get_eventbuf_event(usb3_device_t *dev, int size) ++{ ++ unsigned int event; ++ ++ event = *dev->event_ptr++; ++ ++ if (dev->event_ptr >= dev->event_buf + size) ++ dev->event_ptr = dev->event_buf; ++ return event; ++} ++ ++void usb3_init_eventbuf(usb3_device_t *dev, unsigned int size, phys_addr_t dma_addr) ++{ ++ dma_addr = map_to_dma_addr(dma_addr); ++ ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventadr_lo, dma_addr & 0xffffffff); ++ ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventadr_hi, 0); ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventsiz, size << 2); /* left shift 2bit */ ++ ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventcnt, 0); ++} ++ ++void usb3_enable_device_interrupts(usb3_device_t *dev) ++{ ++ unsigned int eventsiz; ++ ++ /* Clear any pending interrupts */ ++ usb3_dis_flush_eventbuf_intr(dev); ++ /* * ++ * This routine enables the Event Buffer interrupt. ++ */ ++ eventsiz = usb3_rd32(&dev->core_global_regs->geventbuf[0].geventsiz); ++ eventsiz &= ~USB3_EVENTSIZ_INT_MSK_BIT; ++ usb3_wr32(&dev->core_global_regs->geventbuf[0].geventsiz, eventsiz); ++ /* Enable device interrupts */ ++ usb3_wr32(&dev->pcd.dev_global_regs->devten, USB3_DEVTEN_DISCONN_BIT | USB3_DEVTEN_CONNDONE_BIT | ++ USB3_DEVTEN_USBRESET_BIT | USB3_DEVTEN_HIBER_REQ_EVT_BIT | USB3_DEVTEN_WKUP_BIT | USB3_DEVTEN_EOPF_BIT); ++} ++ ++void usb3_handle_event(usb3_device_t *dev) ++{ ++ usb3_pcd_t *pcd = &dev->pcd; ++ unsigned int event, physep; ++ int intr, count, i; ++ intr = 0; ++ ++ invalidate_dcache_all(); ++ count = get_eventbuf_count(dev); ++ if ((count & USB3_EVENTCNT_CNT_BITS) == USB3_EVENTCNT_CNT_BITS || count >= USB3_EVENT_BUF_SIZE * 0x4) { ++ update_eventbuf_count(dev, count); ++ count = 0; ++ } ++ ++ for (i = 0; i < count; i += 0x4) { ++ event = get_eventbuf_event(dev, USB3_EVENT_BUF_SIZE); ++ update_eventbuf_count(dev, 0x4); ++ if (event == 0) { ++ /* Ignore null events */ ++ continue; ++ } ++ ++ if (event & USB3_EVENT_NON_EP_BIT) { ++ intr = event & USB3_EVENT_INTTYPE_BITS; ++ ++ if (intr == (USB3_EVENT_DEV_INT << USB3_EVENT_INTTYPE_SHIFT)) { ++ usb3_handle_dev_intr(pcd, event); ++ } else { ++ /* @todo Handle non-Device interrupts ++ * (OTG, CarKit, I2C) ++ */ ++ } ++ } else { ++ physep = (event >> USB3_DEPEVT_EPNUM_SHIFT) & (USB3_DEPEVT_EPNUM_BITS >> USB3_DEPEVT_EPNUM_SHIFT); ++ usb3_handle_ep_intr(pcd, physep, event); ++ } ++ } ++} +diff --git a/drivers/usb/gadget/udc3/usb3_pcd.c b/drivers/usb/gadget/udc3/usb3_pcd.c +new file mode 100644 +index 0000000..d01b963 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_pcd.c +@@ -0,0 +1,1288 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "usb3.h" ++#include "usb3_hw.h" ++#include "usb3_drv.h" ++#include "usb3_prot.h" ++#include "usb3_pcd.h" ++#include ++#include ++ ++#include ++ ++#define BUFFER_SIZE 512 ++extern void usb3_bulk_out_transfer(void *dev); ++ ++const struct usb_interface_descriptor interface = { ++ sizeof(struct usb_interface_descriptor), /* b_length */ ++ UDESC_INTERFACE, /* b_descriptor_type */ ++ 0, /* b_interface_number */ ++ 0, /* b_alternate_setting */ ++ 2, /* b_num_endpoints */ ++ USB_CLASS_VENDOR_SPEC, /* b_interface_class */ ++ USB_SC_VENDOR_SPEC, /* b_interface_subclass */ ++ USB_PR_VENDOR_SPEC, /* b_interface_protocol */ ++ 0, /* i_interface */ ++}; ++ ++/* Two endpoint descriptors: bulk-in, bulk-out. */ ++ ++const struct usb_ss_ep_comp_descriptor ep_comp = { ++ sizeof(struct usb_ss_ep_comp_descriptor), /* b_length */ ++ UDESC_SS_USB_COMPANION, /* b_descriptor_type */ ++ 0, /* b_max_burst */ ++ 0, /* bm_attributes */ ++ 0, /* w_bytes_per_interval */ ++}; ++ ++const struct usb_endpoint_descriptor hs_bulk_in = { ++ sizeof(struct usb_endpoint_descriptor), /* b_length */ ++ UDESC_ENDPOINT, /* b_descriptor_type */ ++ UE_DIR_IN | USB3_BULK_IN_EP, /* b_endpoint_address */ ++ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ ++ 0x200, /* w_max_packet_size: 512 of high-speed */ ++ 0, /* b_interval */ ++}; ++ ++const struct usb_endpoint_descriptor hs_bulk_out = { ++ sizeof(struct usb_endpoint_descriptor), /* b_length */ ++ UDESC_ENDPOINT, /* b_descriptor_type */ ++ UE_DIR_OUT | USB3_BULK_OUT_EP, /* b_endpoint_address */ ++ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ ++ 0x200, /* w_max_packet_size: 512 of high-speed */ ++ 1, /* b_interval */ ++}; ++ ++const struct usb_endpoint_descriptor ss_bulk_in = { ++ sizeof(struct usb_endpoint_descriptor), /* b_length */ ++ UDESC_ENDPOINT, /* b_descriptor_type */ ++ UE_DIR_IN | USB3_BULK_IN_EP, /* b_endpoint_address */ ++ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ ++ 0x400, /* w_max_packet_size: 1024 of super-speed */ ++ 0, /* b_interval */ ++}; ++ ++const struct usb_endpoint_descriptor ss_bulk_out = { ++ sizeof(struct usb_endpoint_descriptor), /* b_length */ ++ UDESC_ENDPOINT, /* b_descriptor_type */ ++ UE_DIR_OUT | USB3_BULK_OUT_EP, /* b_endpoint_address */ ++ USB_ENDPOINT_XFER_BULK, /* bm_attributes */ ++ 0x400, /* w_max_packet_size: 1024 of super-speed */ ++ 0, /* b_interval */ ++}; ++ ++/* * The BOS Descriptor */ ++ ++const struct usb_dev_cap_20_ext_desc capability1 = { ++ sizeof(struct usb_dev_cap_20_ext_desc), /* b_length */ ++ UDESC_DEVICE_CAPABILITY, /* b_descriptor_type */ ++ USB_DEVICE_CAPABILITY_20_EXTENSION, /* b_devcapability_type */ ++ 0x2, /* bm_attributes */ ++}; ++ ++const struct usb_dev_cap_ss_usb capability2 = { ++ sizeof(struct usb_dev_cap_ss_usb), /* b_length */ ++ UDESC_DEVICE_CAPABILITY, /* b_descriptor_type */ ++ USB_DEVICE_CAPABILITY_SS_USB, /* b_devcapability_type */ ++ 0x0, /* bm_attributes */ ++ (USB_DC_SS_USB_SPEED_SUPPORT_SS | USB_DC_SS_USB_SPEED_SUPPORT_HIGH), /* w_speeds_supported */ ++ 0x2, /* b_functionality_support */ ++ /* @todo set these to correct value */ ++ 0xa, /* b_u1dev_exit_lat */ ++ 0x100, /* w_u2dev_exit_lat */ ++}; ++ ++const struct usb_dev_cap_container_id capability3 = { ++ sizeof(struct usb_dev_cap_container_id), /* b_length */ ++ UDESC_DEVICE_CAPABILITY, /* b_descriptor_type */ ++ USB_DEVICE_CAPABILITY_CONTAINER_ID, /* b_devcapability_type */ ++ 0, /* b_reserved */ ++ /* @todo Create UUID */ ++ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* container_id */ ++}; ++ ++const struct wusb_bos_desc usb_bos = { ++ sizeof(struct wusb_bos_desc), /* b_length */ ++ UDESC_BOS, /* b_descriptor_type */ ++ (sizeof(struct wusb_bos_desc) /* w_total_length */ ++ + sizeof(capability1) + sizeof(capability2) + sizeof(capability3)), ++ 3, /* b_num_device_caps */ ++}; ++ ++usb3_pcd_ep_t *usb3_get_out_ep(usb3_pcd_t *pcd, unsigned int ep_num) ++{ ++ if (ep_num == 0) { ++ return &pcd->ep0; ++ } else { ++ return &pcd->out_ep; ++ } ++} ++ ++usb3_pcd_ep_t *usb3_get_in_ep(usb3_pcd_t *pcd, unsigned int ep_num) ++{ ++ if (ep_num == 0) { ++ return &pcd->ep0; ++ } else { ++ return &pcd->in_ep; ++ } ++} ++ ++usb3_pcd_ep_t *usb3_get_ep_by_addr(usb3_pcd_t *pcd, unsigned short index) ++{ ++ unsigned int ep_num = ue_get_addr(index); ++ ++ if (ue_get_dir(index) == UE_DIR_IN) { ++ return usb3_get_in_ep(pcd, ep_num); ++ } else { ++ return usb3_get_out_ep(pcd, ep_num); ++ } ++} ++ ++ ++void usb3_ep_clear_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) ++{ ++ usb3_dev_ep_regs_t *ep_reg; ++ ++ if (ep->is_in) { ++ ep_reg = ep->in_ep_reg; ++ } else { ++ ep_reg = ep->out_ep_reg; ++ } ++ ++ usb3_dep_cstall(pcd, ep_reg); ++} ++ ++ ++unsigned int usb3_get_device_speed(usb3_pcd_t *pcd) ++{ ++ unsigned int dsts; ++ unsigned int speed = USB_SPEED_UNKNOWN; ++ ++ dsts = usb3_rd32(&pcd->dev_global_regs->dsts); ++ ++ switch ((dsts >> USB3_DSTS_CONNSPD_SHIFT) & (USB3_DSTS_CONNSPD_BITS >> USB3_DSTS_CONNSPD_SHIFT)) { ++ case USB3_SPEED_HS_PHY_30MHZ_OR_60MHZ: ++ speed = USB_SPEED_HIGH; ++ break; ++ ++ case USB3_SPEED_FS_PHY_30MHZ_OR_60MHZ: ++ case USB3_SPEED_FS_PHY_48MHZ: ++ speed = USB_SPEED_FULL; ++ break; ++ ++ case USB3_SPEED_LS_PHY_6MHZ: ++ speed = USB_SPEED_LOW; ++ break; ++ ++ case USB3_SPEED_SS_PHY_125MHZ_OR_250MHZ: ++ speed = USB_SPEED_SUPER; ++ break; ++ default: ++ break; ++ } ++ ++ return speed; ++} ++ ++void usb3_pcd_set_speed(usb3_pcd_t *pcd, int speed) ++{ ++ /* Set the MPS of EP0 based on the connection speed */ ++ switch (speed) { ++ case USB_SPEED_SUPER: ++ pcd->ep0.maxpacket = 512; /* 512: 512bytes */ ++ pcd->in_ep.maxpacket = USB3_MAX_PACKET_SIZE; ++ pcd->out_ep.maxpacket = USB3_MAX_PACKET_SIZE; ++ break; ++ ++ case USB_SPEED_HIGH: ++ pcd->ep0.maxpacket = 64; /* 64: 64bytes */ ++ pcd->in_ep.maxpacket = USB2_HS_MAX_PACKET_SIZE; ++ pcd->out_ep.maxpacket = USB2_HS_MAX_PACKET_SIZE; ++ break; ++ ++ case USB_SPEED_FULL: ++ pcd->ep0.maxpacket = 64; /* 64: 64bytes */ ++ pcd->in_ep.maxpacket = USB2_FS_MAX_PACKET_SIZE; ++ pcd->out_ep.maxpacket = USB2_FS_MAX_PACKET_SIZE; ++ break; ++ ++ case USB_SPEED_LOW: ++ pcd->ep0.maxpacket = 8; /* 8: 8bytes */ ++ pcd->in_ep.maxpacket = 0; ++ pcd->out_ep.maxpacket = 0; ++ break; ++ default: ++ break; ++ } ++} ++ ++void usb3_ep_set_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) ++{ ++ usb3_dev_ep_regs_t *ep_reg; ++ ++ if (ep->is_in) { ++ ep_reg = ep->in_ep_reg; ++ } else { ++ ep_reg = ep->out_ep_reg; ++ } ++ ++ usb3_dep_sstall(pcd, ep_reg); ++} ++ ++static void ep0_do_stall(usb3_pcd_t *pcd) ++{ ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ ++ /* Stall EP0 IN & OUT simultanelusly */ ++ ep0->is_in = 1; ++ usb3_ep_set_stall(pcd, ep0); ++ ep0->is_in = 0; ++ usb3_ep_set_stall(pcd, ep0); ++ ++ /* Prepare for the next setup transfer */ ++ ep0->stopped = 1; ++ pcd->ep0state = EP0_IDLE; ++ usb3_ep0_out_start(pcd); ++} ++ ++static void do_clear_halt(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) ++{ ++ usb3_ep_clear_stall(pcd, ep); ++ ++ if (ep->stopped) ++ ep->stopped = 0; ++} ++ ++void usb3_pcd_ep_enable(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) ++{ ++ /* ++ * Activate the EP ++ */ ++ ep->stopped = 0; ++ ep->xfer_started = 0; ++ ++ /* Set initial data PID. */ ++ if (ep->type == USB3_EP_TYPE_BULK) ++ ep->data_pid_start = 0; ++ ++ usb3_ep_activate(pcd, ep); ++} ++ ++void usb3_do_status_ut_device(usb3_pcd_t *pcd) ++{ ++ if (pcd == NULL) ++ return; ++ ++ unsigned char *status = pcd->ep0_status_buf; ++ ++ if (pcd->speed != USB_SPEED_SUPER) { ++ *(status + 1) = 0; ++ return; ++ } ++ if (pcd->state == USB3_STATE_CONFIGURED) { ++ if (usb3_u1_enabled(pcd)) ++ *status |= 1 << 2; /* left shift 2 bit */ ++ ++ if (usb3_u2_enabled(pcd)) ++ *status |= 1 << 3; /* left shift 3 bit */ ++ ++ *status |= pcd->ltm_enable << 4; /* left shift 4 bit */ ++ } ++ ++ *(status + 1) = 0; ++} ++ ++static void usb3_do_get_status(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ unsigned char *status = pcd->ep0_status_buf; ++ usb3_pcd_ep_t *ep; ++ ++ if (ctrl.w_length != 0x2) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ ++ switch (ut_get_recipient(ctrl.bm_request_type)) { ++ case UT_DEVICE: ++ usb3_do_status_ut_device(pcd); ++ break; ++ ++ case UT_INTERFACE: ++ *status = 0; ++ *(status + 1) = 0; ++ break; ++ ++ case UT_ENDPOINT: ++ ep = usb3_get_ep_by_addr(pcd, ctrl.w_index); ++ ++ /* @todo check for EP stall */ ++ *status = ep->stopped; ++ *(status + 1) = 0; ++ break; ++ ++ default: ++ ep0_do_stall(pcd); ++ return; ++ } ++ ++ pcd->ep0_req.bufdma = status; ++ pcd->ep0_req.length = 0x2; ++ pcd->ep0_req.actual = 0; ++ usb3_ep0_start_transfer(pcd, &pcd->ep0_req); ++} ++ ++static void usb3_do_clear_feature(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ usb3_pcd_ep_t *ep; ++ ++ switch (ut_get_recipient(ctrl.bm_request_type)) { ++ case UT_DEVICE: ++ switch (ctrl.w_value) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ break; ++ ++ case UF_TEST_MODE: ++ /* @todo Add CLEAR_FEATURE for TEST modes. */ ++ break; ++ ++ case UF_U1_ENABLE: ++ if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ usb3_disable_u1(pcd); ++ break; ++ ++ case UF_U2_ENABLE: ++ if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ usb3_disable_u2(pcd); ++ break; ++ ++ case UF_LTM_ENABLE: ++ if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED || ctrl.w_index != 0) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ pcd->ltm_enable = 0; ++ break; ++ ++ default: ++ ep0_do_stall(pcd); ++ return; ++ } ++ break; ++ ++ case UT_INTERFACE: ++ /* if FUNCTION_SUSPEND ... */ ++ if (ctrl.w_value) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ break; ++ ++ case UT_ENDPOINT: ++ ep = usb3_get_ep_by_addr(pcd, ctrl.w_index); ++ if (ctrl.w_value != UF_ENDPOINT_HALT) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ do_clear_halt(pcd, ep); ++ break; ++ ++ default: ++ break; ++ } ++ ++ pcd->ep0.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++} ++ ++static void usb3_do_set_feature(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ usb3_pcd_ep_t *ep; ++ ++ switch (ut_get_recipient(ctrl.bm_request_type)) { ++ case UT_DEVICE: ++ switch (ctrl.w_value) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ break; ++ ++ case UF_TEST_MODE: ++ break; ++ ++ case UF_DEVICE_B_HNP_ENABLE: ++ break; ++ ++ case UOTG_NTF_HOST_REL: ++ break; ++ ++ case UOTG_B3_RSP_ENABLE: ++ break; ++ ++ case UF_DEVICE_A_HNP_SUPPORT: ++ /* RH port supports HNP */ ++ break; ++ ++ case UF_DEVICE_A_ALT_HNP_SUPPORT: ++ /* other RH port does */ ++ break; ++ ++ case UF_U1_ENABLE: ++ if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ usb3_enable_u1(pcd); ++ break; ++ ++ case UF_U2_ENABLE: ++ if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ usb3_enable_u2(pcd); ++ break; ++ ++ case UF_LTM_ENABLE: ++ if (pcd->speed != USB_SPEED_SUPER || pcd->state != USB3_STATE_CONFIGURED || ctrl.w_index != 0) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ pcd->ltm_enable = 1; ++ break; ++ ++ default: ++ ep0_do_stall(pcd); ++ return; ++ } ++ break; ++ ++ case UT_INTERFACE: ++ /* if FUNCTION_SUSPEND ... */ ++ if (ctrl.w_value) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ break; ++ ++ case UT_ENDPOINT: ++ ep = usb3_get_ep_by_addr(pcd, ctrl.w_index); ++ if (ctrl.w_value != UF_ENDPOINT_HALT) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ ep->stopped = 1; ++ usb3_ep_set_stall(pcd, ep); ++ break; ++ ++ default: ++ break; ++ } ++ ++ pcd->ep0.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++} ++ ++static void usb3_do_set_address(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ ++ if (ctrl.bm_request_type == UT_DEVICE) { ++ usb3_set_address(pcd, ctrl.w_value); ++ pcd->ep0.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ if (ctrl.w_value) { ++ pcd->state = USB3_STATE_ADDRESSED; ++ } else { ++ pcd->state = USB3_STATE_DEFAULT; ++ } ++ } ++} ++ ++static void usb3_do_set_config(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ unsigned short wvalue = ctrl.w_value; ++ usb3_pcd_ep_t *ep; ++ ++ if (ctrl.bm_request_type != (UT_WRITE | UT_STANDARD | UT_DEVICE)) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ ++ if (!wvalue || (wvalue == CONFIG_VALUE)) { ++ pcd->new_config = wvalue; ++ /* Set new configuration */ ++ if (wvalue) { ++ /* Activate bulk in endpoint */ ++ ep = &pcd->in_ep; ++ usb3_pcd_ep_enable(pcd, ep); ++ ++ /* Activate bulk out endpoint */ ++ ep = &pcd->out_ep; ++ usb3_pcd_ep_enable(pcd, ep); ++ ++ /* Prepare for next bulk transfer */ ++ usb3_bulk_out_transfer((void *)pcd); ++ usb3_bulk_in_transfer((void *)pcd, "start download process."); ++ pcd->state = USB3_STATE_CONFIGURED; ++ } else { ++ pcd->state = USB3_STATE_ADDRESSED; ++ } ++ ++ pcd->ep0.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ } else { ++ ep0_do_stall(pcd); ++ } ++} ++ ++static void usb3_do_get_config(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ unsigned char *status = pcd->ep0_status_buf; ++ ++ if (ctrl.bm_request_type != (UT_READ | UT_STANDARD | UT_DEVICE)) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ ++ /* Notify host the current config value */ ++ *status = pcd->new_config; ++ ++ pcd->ep0_req.bufdma = status; ++ pcd->ep0_req.length = 1; ++ pcd->ep0_req.actual = 0; ++ usb3_ep0_start_transfer(pcd, &pcd->ep0_req); ++} ++ ++static void usb3_do_get_descriptor(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ usb3_device_t *usb3_dev = pcd->usb3_dev; ++ unsigned char dt = ctrl.w_value >> 8; /* 8bit */ ++ unsigned char index = (unsigned char)ctrl.w_value; ++ unsigned short len = ctrl.w_length; ++ unsigned char *buf = pcd->ep0_status_buf; ++ unsigned short value = 0; ++ ++ if (ctrl.bm_request_type != (UT_READ | UT_STANDARD | UT_DEVICE)) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ ++ switch (dt) { ++ case UDESC_DEVICE: { ++ struct usb_device_descriptor *dev = (struct usb_device_descriptor *)usb3_dev->dev_desc; ++ ++ dev->b_length = sizeof(struct usb_device_descriptor); ++ dev->b_descriptor_type = UDESC_DEVICE; ++ ++ dev->b_device_class = 0; ++ dev->b_device_sub_class = 0; ++ dev->b_device_protocol = 0; ++ ++ if (pcd->speed == USB_SPEED_SUPER) { ++ dev->bcd_usb = 0x300; ++ dev->b_max_packet_size0 = 9; // NOTE! 2 ^ 9 = 512 for USB3 ++ } else if (pcd->speed == USB_SPEED_HIGH) { ++ dev->bcd_usb = 0x0200; ++ dev->b_max_packet_size0 = pcd->ep0.maxpacket; ++ } else { ++ dev->bcd_usb = 0x0110; ++ dev->b_max_packet_size0 = pcd->ep0.maxpacket; ++ } ++ ++ dev->id_vendor = USB_VENDOR_ID; ++ dev->id_product = USB_PRODUCT_ID; ++ ++ dev->bcd_device = 0x0100; ++ ++ dev->i_manufacturer = STRING_MANUFACTURER; ++ dev->i_product = STRING_PRODUCT; ++ dev->i_serial_number = 0; ++ ++ dev->b_num_configurations = 1; ++ ++ value = sizeof(struct usb_device_descriptor); ++ (void)memcpy_s((void *)buf, value, (void *)dev, value); ++ } break; ++ ++ case UDESC_DEVICE_QUALIFIER: { ++ struct usb_qualifier_descriptor *qual = (struct usb_qualifier_descriptor *)buf; ++ struct usb_device_descriptor *dev = (struct usb_device_descriptor *)usb3_dev->dev_desc; ++ ++ qual->b_length = sizeof(*qual); ++ qual->b_descriptor_type = UDESC_DEVICE_QUALIFIER; ++ qual->bcd_usb = dev->bcd_usb; ++ qual->b_device_class = dev->b_device_class; ++ qual->b_device_sub_class = dev->b_device_sub_class; ++ qual->b_device_protocol = dev->b_device_protocol; ++ qual->b_max_packet_size0 = dev->b_max_packet_size0; ++ qual->b_num_configurations = 1; ++ qual->b_reserved = 0; ++ ++ value = sizeof(usb_qualifier_descriptor_t); ++ } break; ++ ++ case UDESC_CONFIG: { ++ struct usb_config_descriptor *config = (struct usb_config_descriptor *)buf; ++ ++ config->b_length = sizeof(*config); ++ config->b_descriptor_type = UDESC_CONFIG; ++ config->b_num_interfaces = 1; ++ config->b_configuration_value = CONFIG_VALUE; ++ config->i_configuration = 0; ++ config->bm_attributes = USB_CONFIG_ATT_ONE; ++ ++ if (pcd->speed == USB_SPEED_SUPER) { ++ config->b_max_power = USB_CONFIG_VBUS_DRAW / 8; /* divided 8 */ ++ } else { ++ config->b_max_power = USB_CONFIG_VBUS_DRAW / 2; /* divided 2 */ ++ } ++ ++ buf += sizeof(*config); ++ (void)memcpy_s((void *)buf, sizeof(interface), (void *)&interface, sizeof(interface)); ++ buf += sizeof(interface); ++ ++ switch (pcd->speed) { ++ case USB_SPEED_SUPER: ++ (void)memcpy_s((void *)buf, sizeof(ss_bulk_in), (void *)&ss_bulk_in, sizeof(ss_bulk_in)); ++ buf += sizeof(ss_bulk_in); ++ (void)memcpy_s((void *)buf, sizeof(ep_comp), (void *)&ep_comp, sizeof(ep_comp)); ++ buf += sizeof(ep_comp); ++ (void)memcpy_s((void *)buf, sizeof(ss_bulk_out), (void *)&ss_bulk_out, sizeof(ss_bulk_out)); ++ buf += sizeof(ss_bulk_out); ++ (void)memcpy_s((void *)buf, sizeof(ep_comp), (void *)&ep_comp, sizeof(ep_comp)); ++ ++ config->w_total_length = sizeof(*config) + sizeof(interface) + sizeof(ss_bulk_in) + sizeof(ep_comp) + ++ sizeof(ss_bulk_out) + sizeof(ep_comp); ++ break; ++ ++ default: /* HS/FS */ ++ { ++ struct usb_endpoint_descriptor *endp = (struct usb_endpoint_descriptor *)buf; ++ ++ (void)memcpy_s((void *)buf, sizeof(hs_bulk_in), (void *)&hs_bulk_in, sizeof(hs_bulk_in)); ++ (endp++)->w_max_packet_size = pcd->in_ep.maxpacket; ++ buf += sizeof(hs_bulk_in); ++ (void)memcpy_s((void *)buf, sizeof(hs_bulk_out), (void *)&hs_bulk_out, sizeof(hs_bulk_out)); ++ (endp++)->w_max_packet_size = pcd->out_ep.maxpacket; ++ } ++ config->w_total_length = sizeof(*config) + sizeof(interface) + sizeof(hs_bulk_in) + sizeof(hs_bulk_out); ++ break; ++ } ++ value = config->w_total_length; ++ } break; ++ ++ case UDESC_STRING: { ++ switch (index) { ++ case STRING_LANGUAGE: ++ buf[0] = 0x04; ++ buf[1] = UDESC_STRING; ++ buf[2] = 0x09; /* buf number 2 data */ ++ buf[3] = 0x04; /* buf number 3 data */ ++ ++ value = 0x04; ++ break; ++ ++ case STRING_MANUFACTURER: ++ buf[0] = usb3_dev->string_manu_len + 0x2; ++ buf[1] = UDESC_STRING; ++ (void)memcpy_s((void *)(buf + 0x2), usb3_dev->string_manu_len, (void *)usb3_dev->string_manu, usb3_dev->string_manu_len); ++ ++ value = usb3_dev->string_manu_len + 0x2; ++ break; ++ ++ case STRING_PRODUCT: ++ buf[0] = usb3_dev->string_prod_len + 0x2; ++ buf[1] = UDESC_STRING; ++ ++ (void)memcpy_s((void *)(buf + 0x2), usb3_dev->string_prod_len, (void *)usb3_dev->string_prod, usb3_dev->string_prod_len); ++ ++ value = usb3_dev->string_prod_len + 0x2; ++ break; ++ ++ default: ++ ep0_do_stall(pcd); ++ return; ++ } ++ } break; ++ ++ case UDESC_BOS: ++ if (pcd->speed != USB_SPEED_SUPER) { ++ /* ++ * The USB compliance test (USB 2.0 Command Verifier) ++ * issues this request. We should not run into the ++ * default path here. But return for now until ++ * the superspeed support is added. ++ */ ++ } ++ ++ value = usb_bos.w_total_length; ++ ++ (void)memcpy_s((void *)buf, sizeof(usb_bos), (void *)&usb_bos, sizeof(usb_bos)); ++ buf += sizeof(usb_bos); ++ (void)memcpy_s((void *)buf, sizeof(capability1), (void *)&capability1, sizeof(capability1)); ++ buf += sizeof(capability1); ++ (void)memcpy_s((void *)buf, sizeof(capability2), (void *)&capability2, sizeof(capability2)); ++ buf += sizeof(capability2); ++ (void)memcpy_s((void *)buf, sizeof(capability3), (void *)&capability3, sizeof(capability3)); ++ ++ break; ++ ++ default: ++ ep0_do_stall(pcd); ++ return; ++ } ++ ++ pcd->ep0_req.bufdma = pcd->ep0_status_buf; ++ pcd->ep0_req.length = value < len ? value : len; ++ pcd->ep0_req.actual = 0; ++ usb3_ep0_start_transfer(pcd, &pcd->ep0_req); ++} ++ ++void usb3_do_setup(usb3_pcd_t *pcd) ++{ ++ usb_device_request_t ctrl = pcd->ep0_setup_pkt->req; ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ unsigned short wlength; ++ ++ wlength = ctrl.w_length; ++ ++ ep0->stopped = 0; ++ ep0->three_stage = 1; ++ ++ if (ctrl.bm_request_type & UE_DIR_IN) { ++ ep0->is_in = 1; ++ pcd->ep0state = EP0_IN_DATA_PHASE; ++ } else { ++ ep0->is_in = 0; ++ pcd->ep0state = EP0_OUT_DATA_PHASE; ++ } ++ ++ if (wlength == 0) { ++ ep0->is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ ep0->three_stage = 0; ++ } ++ ++ if ((ut_get_type(ctrl.bm_request_type)) != UT_STANDARD) { ++ ep0_do_stall(pcd); ++ return; ++ } ++ ++ switch (ctrl.b_request) { ++ case UR_GET_STATUS: ++ usb3_do_get_status(pcd); ++ break; ++ ++ case UR_CLEAR_FEATURE: ++ usb3_do_clear_feature(pcd); ++ break; ++ ++ case UR_SET_FEATURE: ++ usb3_do_set_feature(pcd); ++ break; ++ ++ case UR_SET_ADDRESS: ++ usb3_do_set_address(pcd); ++ break; ++ ++ case UR_SET_CONFIG: ++ usb3_do_set_config(pcd); ++ ++ /* Must wait until SetConfig before accepting U1/U2 link ++ * control, otherwise we have problems with VIA hubs ++ */ ++ usb3_accept_u1(pcd); ++ usb3_accept_u2(pcd); ++ ++ usb_info("usb enum done\n"); ++ ++ pcd->ltm_enable = 0; ++ break; ++ ++ case UR_GET_CONFIG: ++ usb3_do_get_config(pcd); ++ break; ++ ++ case UR_GET_DESCRIPTOR: ++ usb3_do_get_descriptor(pcd); ++ break; ++ ++ case UR_SET_SEL: ++ /* For now this is a no-op */ ++ pcd->ep0_req.bufdma = pcd->ep0_status_buf; ++ pcd->ep0_req.length = USB3_STATUS_BUF_SIZE; ++ pcd->ep0_req.actual = 0; ++ ep0->send_zlp = 0; ++ usb3_ep0_start_transfer(pcd, &pcd->ep0_req); ++ break; ++ ++ case UR_SET_ISOC_DELAY: ++ /* For now this is a no-op */ ++ pcd->ep0.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ break; ++ ++ default: ++ ep0_do_stall(pcd); ++ break; ++ } ++} ++ ++void usb3_os_get_trb(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep, usb3_pcd_req_t *req) ++{ ++ /* If EP0, fill request with EP0 IN/OUT data TRB */ ++ if (ep == &pcd->ep0) { ++ if (ep->is_in) { ++ req->trb = pcd->ep0_in_desc; ++ req->trbdma = (phys_addr_t)pcd->ep0_in_desc; ++ } else { ++ req->trb = pcd->ep0_out_desc; ++ req->trbdma = (phys_addr_t)pcd->ep0_out_desc; ++ } ++ /* Else fill request with TRB from the non-EP0 allocation */ ++ } else { ++ req->trb = ep->ep_desc; ++ req->trbdma = (phys_addr_t)ep->ep_desc; ++ } ++} ++ ++void usb3_ep0_start_transfer(usb3_pcd_t *pcd, usb3_pcd_req_t *req) ++{ ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ usb3_dev_ep_regs_t *ep_reg; ++ usb3_dma_desc_t *desc; ++ phys_addr_t desc_dma; ++ unsigned int desc_type, len; ++ unsigned char tri; ++ ++ /* Get the DMA Descriptor (TRB) for this request */ ++ usb3_os_get_trb(pcd, ep0, req); ++ desc = req->trb; ++ desc_dma = req->trbdma; ++ ++ if (ep0->is_in) { ++ /* ++ * Start DMA on EP0-IN ++ */ ++ ep_reg = ep0->in_ep_reg; ++ ++ /* DMA Descriptor (TRB) setup */ ++ len = req->length; ++ ++ if (pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ if (ep0->three_stage) ++ desc_type = USB3_DSCCTL_TRBCTL_STATUS_3; ++ else ++ desc_type = USB3_DSCCTL_TRBCTL_STATUS_2; ++ } else { ++ desc_type = USB3_DSCCTL_TRBCTL_CTLDATA_1ST; ++ } ++ ++ usb3_fill_desc(desc, (phys_addr_t)req->bufdma, len, 0, desc_type, ++ USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_LST_BIT, 1); ++ /* Issue "DEPSTRTXFER" command to EP0-IN */ ++ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep0->tri_in = tri; ++ } else { ++ /* ++ * Start DMA on EP0-OUT ++ */ ++ ep_reg = ep0->out_ep_reg; ++ ++ /* DMA Descriptor (TRB) setup */ ++ len = (req->length + ep0->maxpacket - 1) & ~(ep0->maxpacket - 1); ++ ++ if (pcd->ep0state == EP0_OUT_STATUS_PHASE) { ++ if (ep0->three_stage) ++ desc_type = USB3_DSCCTL_TRBCTL_STATUS_3; ++ else ++ desc_type = USB3_DSCCTL_TRBCTL_STATUS_2; ++ } else { ++ desc_type = USB3_DSCCTL_TRBCTL_CTLDATA_1ST; ++ } ++ ++ usb3_fill_desc(desc, (phys_addr_t)req->bufdma, len, 0, desc_type, ++ USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_LST_BIT, 1); ++ /* Issue "DEPSTRTXFER" command to EP0-OUT */ ++ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep0->tri_out = tri; ++ } ++} ++ ++static void ep0_continue_transfer(usb3_pcd_t *pcd, usb3_pcd_req_t *req) ++{ ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ usb3_dev_ep_regs_t *ep_reg; ++ usb3_dma_desc_t *desc; ++ phys_addr_t desc_dma; ++ unsigned char tri; ++ ++ /* It can be called to send a 0-length packet after the end of a transfer, so the code here ++ * only supports that case. ++ */ ++ ++ if (ep0->is_in) { ++ desc = pcd->ep0_in_desc; ++ desc_dma = (phys_addr_t)(pcd->ep0_in_desc); ++ ep_reg = ep0->in_ep_reg; ++ ++ /* DMA Descriptor Setup */ ++ usb3_fill_desc(desc, (phys_addr_t)req->bufdma, 0, 0, USB3_DSCCTL_TRBCTL_NORMAL, ++ USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_LST_BIT, 1); ++ ++ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep0->tri_in = tri; ++ } ++} ++ ++void usb3_ep_start_transfer(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep) ++{ ++ usb3_pcd_req_t *req = &ep->req; ++ usb3_dev_ep_regs_t *ep_reg; ++ usb3_dma_desc_t *desc; ++ phys_addr_t desc_dma; ++ unsigned int len; ++ unsigned char tri; ++ ++ /* Get the TRB for this request */ ++ usb3_os_get_trb(pcd, ep, req); ++ ++ ep->send_zlp = 0; ++ desc = req->trb; ++ desc_dma = req->trbdma; ++ ++ if (ep->is_in) { ++ /* For IN, TRB length is just xfer length */ ++ len = req->length; ++ } else { ++ /* For OUT, TRB length must be multiple of maxpacket */ ++ /* Must be power of 2, use cheap AND */ ++ len = (req->length + ep->maxpacket - 1) & ~(ep->maxpacket - 1); ++ ++ req->length = len; ++ } ++ ++ /* DMA Descriptor Setup */ ++ usb3_fill_desc(desc, (phys_addr_t)req->bufdma, len, 0, USB3_DSCCTL_TRBCTL_NORMAL, ++ USB3_DSCCTL_ISP_BIT | USB3_DSCCTL_IOC_BIT | USB3_DSCCTL_LST_BIT, 1); ++ ++ if (ep->is_in) { ++ /* ++ * Start DMA on EPn-IN ++ */ ++ ep_reg = ep->in_ep_reg; ++ /* Issue "DEPSTRTXFER" command to EP */ ++ ++ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep->tri_in = tri; ++ } else { ++ /* ++ * Start DMA on EPn-OUT ++ */ ++ ep_reg = ep->out_ep_reg; ++#if 1 ++ if (ep->xfer_started) { ++ /* Issue "DEPUPDTXFER" command to EP */ ++ usb3_dep_updatexfer(pcd, ep_reg, ep->tri_out); ++ } else { ++#endif ++ /* Issue "DEPSTRTXFER" command to EP */ ++ tri = usb3_dep_startxfer(pcd, ep_reg, desc_dma, 0); ++ ep->tri_out = tri; ++ ep->xfer_started = 1; ++ } ++ } ++} ++ ++void usb3_bulk_out_transfer(void *dev) ++{ ++ usb3_pcd_t *pcd = (usb3_pcd_t *)dev; ++ usb3_pcd_ep_t *ep = &pcd->out_ep; ++ usb3_pcd_req_t *req = &ep->req; ++ ++ req->length = ep->maxpacket; ++ req->bufdma = pcd->ss_bulk_buf; ++ req->complete = usb3_handle_protocol; ++ ++ usb3_ep_start_transfer(pcd, ep); ++ ++ return; ++} ++ ++void usb3_bulk_out_continue_transfer(void *dev) ++{ ++ usb3_pcd_t *pcd = (usb3_pcd_t *)dev; ++ usb3_pcd_ep_t *ep = &pcd->out_ep; ++ ++ usb3_ep_start_transfer(pcd, ep); ++} ++ ++void usb_tx_status_complete(void *dev) {} ++ ++void usb3_bulk_in_transfer(void *dev, const char *status) ++{ ++ usb3_pcd_t *pcd = (usb3_pcd_t *)dev; ++ usb3_pcd_ep_t *ep = &pcd->in_ep; ++ usb3_pcd_req_t *req = &ep->req; ++ int len; ++ ++ len = strlen(status) + 1; ++ (void)memset_s(req->bufdma, BUFFER_SIZE, 0, BUFFER_SIZE); ++ (void)memcpy_s(req->bufdma, len, status, len); ++ req->length = len; ++ ++ req->complete = usb_tx_status_complete; ++ ++ usb3_ep_start_transfer(pcd, ep); ++} ++ ++static void ep0_complete_request(usb3_pcd_t *pcd, usb3_pcd_req_t *req, usb3_dma_desc_t *desc, int status) ++{ ++ usb3_pcd_ep_t *ep = &pcd->ep0; ++ ++ if (!req) ++ return; ++ ++ if (pcd->ep0state == EP0_OUT_DATA_PHASE || pcd->ep0state == EP0_IN_DATA_PHASE) { ++ if (ep->is_in) { ++ if (usb3_get_xfercnt(desc) == 0) { ++ pcd->ep0.is_in = 0; ++ pcd->ep0state = EP0_OUT_WAIT_NRDY; ++ } ++ } else { ++ pcd->ep0.is_in = 1; ++ pcd->ep0state = EP0_IN_WAIT_NRDY; ++ } ++ } ++} ++ ++static void setup_in_status_phase(usb3_pcd_t *pcd, void *buf) ++{ ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ ++ if (pcd->ep0state == EP0_STALL) ++ return; ++ ++ ep0->is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ ++ pcd->ep0_req.bufdma = buf; ++ pcd->ep0_req.length = 0; ++ pcd->ep0_req.actual = 0; ++ usb3_ep0_start_transfer(pcd, &pcd->ep0_req); ++} ++ ++static void setup_out_status_phase(usb3_pcd_t *pcd, void *buf) ++{ ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ ++ if (pcd->ep0state == EP0_STALL) ++ return; ++ ++ ep0->is_in = 0; ++ pcd->ep0state = EP0_OUT_STATUS_PHASE; ++ ++ pcd->ep0_req.bufdma = buf; ++ pcd->ep0_req.length = 0; ++ pcd->ep0_req.actual = 0; ++ usb3_ep0_start_transfer(pcd, &pcd->ep0_req); ++} ++ ++void usb3_handle_ep0(usb3_pcd_t *pcd, usb3_pcd_req_t *req, unsigned int event) ++{ ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ usb3_dma_desc_t *desc; ++ unsigned int byte_count; ++ unsigned int len = 0; ++ unsigned int status; ++ ++ switch (pcd->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ if (!req) ++ req = &pcd->ep0_req; ++ desc = pcd->ep0_in_desc; ++ ++ if (dwc_usb3_is_hwo(desc)) ++ goto out; ++ ++ status = usb3_get_xfersts(desc); ++ if (status & USB3_TRBRSP_SETUP_PEND) ++ /* Start of a new Control transfer */ ++ desc->status = 0; ++ ++ byte_count = req->length - usb3_get_xfercnt(desc); ++ req->actual += byte_count; ++ req->bufdma += byte_count; ++ ++ if (req->actual < req->length) { ++ /* IN CONTINUE, Stall EP0 */ ++ ep0_do_stall(pcd); ++ } else if (ep0->send_zlp) { ++ /* CONTINUE TRANSFER IN ZLP */ ++ ep0_continue_transfer(pcd, req); ++ ep0->send_zlp = 0; ++ } else { ++ /* COMPLETE IN TRANSFER */ ++ ep0_complete_request(pcd, req, desc, 0); ++ } ++ ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ if (!req) ++ req = &pcd->ep0_req; ++ desc = pcd->ep0_out_desc; ++ ++ if (dwc_usb3_is_hwo(desc)) ++ goto out; ++ ++ status = usb3_get_xfersts(desc); ++ if (status & USB3_TRBRSP_SETUP_PEND) ++ /* Start of a new Control transfer */ ++ ++ len = (req->length + ep0->maxpacket - 1) & ~(ep0->maxpacket - 1); ++ byte_count = len - usb3_get_xfercnt(desc); ++ req->actual += byte_count; ++ req->bufdma += byte_count; ++ ++ if (ep0->send_zlp) { ++ /* CONTINUE TRANSFER OUT ZLP */ ++ ep0_continue_transfer(pcd, req); ++ ep0->send_zlp = 0; ++ } else { ++ /* COMPLETE OUT TRANSFER */ ++ ep0_complete_request(pcd, req, desc, 0); ++ } ++ ++ break; ++ ++ case EP0_IN_WAIT_NRDY: ++ case EP0_OUT_WAIT_NRDY: ++ if (ep0->is_in) ++ setup_in_status_phase(pcd, pcd->ep0_setup_pkt); ++ else ++ setup_out_status_phase(pcd, pcd->ep0_setup_pkt); ++ ++ break; ++ ++ case EP0_IN_STATUS_PHASE: ++ case EP0_OUT_STATUS_PHASE: ++ if (ep0->is_in) ++ desc = pcd->ep0_in_desc; ++ else ++ desc = pcd->ep0_out_desc; ++ ep0_complete_request(pcd, req, desc, 0); ++ ++ pcd->ep0state = EP0_IDLE; ++ ep0->stopped = 1; ++ ep0->is_in = 0; /* OUT for next SETUP */ ++ ++ /* Prepare for more SETUP Packets */ ++ usb3_ep0_out_start(pcd); ++ break; ++ ++ case EP0_STALL: ++ break; ++ ++ case EP0_IDLE: ++ break; ++ default: ++ break; ++ } ++out: ++ return; ++} ++ ++void usb3_os_handle_ep0(usb3_pcd_t *pcd, unsigned int event) ++{ ++ usb3_pcd_req_t *req = NULL; ++ ++ if (pcd->ep0state == EP0_IDLE) { ++ usb3_do_setup(pcd); ++ } else { ++ usb3_handle_ep0(pcd, req, event); ++ } ++} ++ ++void usb3_request_done(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep, usb3_pcd_req_t *req, int status) ++{ ++ if (ep != &pcd->ep0) ++ req->trb = NULL; ++ if (req->complete) ++ req->complete(pcd); ++ ++ req->actual = 0; ++ ++ return; ++} ++ ++int usb3_ep_complete_request(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep, unsigned int event) ++{ ++ usb3_pcd_req_t *req = &ep->req; ++ usb3_dma_desc_t *desc = req->trb; ++ unsigned int byte_count; ++ int error = USB_NO_ERR; ++ ++ ep->send_zlp = 0; ++ ++ if (!desc) ++ return USB_PROCESS_ERR; ++ ++ if (dwc_usb3_is_hwo(desc)) ++ return USB_PROCESS_ERR; ++ ++ if (ep->is_in) { ++ /* IN endpoint */ ++ if (usb3_get_xfercnt(desc) == 0) ++ req->actual += req->length; ++ /* Reset IN tri */ ++ ep->tri_in = 0; ++ ++ /* Complete the IN request */ ++ usb3_request_done(pcd, ep, req, 0); ++ } else { /* OUT endpoint */ ++ byte_count = req->length - usb3_get_xfercnt(desc); ++ req->actual += byte_count; ++ req->bufdma += byte_count; ++ pcd->file_received += byte_count; ++ /* Reset OUT tri */ ++ ep->tri_out = 0; ++ ++ /* OUT transfer complete or not */ ++ if ((byte_count < ep->maxpacket) || (pcd->file_capacity <= pcd->file_received - FRAME_HEAD_LEN)) { ++ /* Complete the OUT request */ ++ usb3_request_done(pcd, ep, req, 0); ++ } else { ++ usb3_bulk_out_continue_transfer((void *)pcd); ++ } ++ } ++ ++ return error; ++} +diff --git a/drivers/usb/gadget/udc3/usb3_pcd.h b/drivers/usb/gadget/udc3/usb3_pcd.h +new file mode 100644 +index 0000000..7753fe6 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_pcd.h +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __USB3_PCD_H__ ++#define __USB3_PCD_H__ ++ ++usb3_pcd_ep_t *usb3_get_out_ep(usb3_pcd_t *pcd, unsigned int ep_num); ++usb3_pcd_ep_t *usb3_get_in_ep(usb3_pcd_t *pcd, unsigned int ep_num); ++usb3_pcd_ep_t *usb3_get_ep_by_addr(usb3_pcd_t *pcd, unsigned short index); ++void usb3_ep_clear_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep); ++unsigned int usb3_get_device_speed(usb3_pcd_t *pcd); ++void usb3_pcd_set_speed(usb3_pcd_t *pcd, int speed); ++void usb3_ep_set_stall(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep); ++void usb3_bulk_in_transfer(void *dev, const char *status); ++void usb3_bulk_out_transfer_cmd(void *dev); ++int usb3_bulk_out_transfer_data(void *dev); ++void usb3_bulk_out_transfer(void *dev); ++void usb3_ep_start_transfer(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep); ++int usb3_ep_complete_request(usb3_pcd_t *pcd, usb3_pcd_ep_t *ep, unsigned int event); ++void usb3_os_handle_ep0(usb3_pcd_t *pcd, unsigned int event); ++void usb_tx_status_complete(void *dev); ++ ++#endif /* __USB3_PCD_H__ */ +diff --git a/drivers/usb/gadget/udc3/usb3_pcd_intr.c b/drivers/usb/gadget/udc3/usb3_pcd_intr.c +new file mode 100644 +index 0000000..7e88285 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_pcd_intr.c +@@ -0,0 +1,174 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include "usb3_drv.h" ++#include "usb3_pcd.h" ++ ++static void handle_usb_reset_intr(usb3_pcd_t *pcd) ++{ ++ usb3_pcd_ep_t *ep; ++ ++ /* Clear stall on each EP */ ++ ep = &pcd->in_ep; ++ if (ep->stopped) ++ usb3_ep_clear_stall(pcd, ep); ++ ++ ep = &pcd->out_ep; ++ if (ep->stopped) ++ usb3_ep_clear_stall(pcd, ep); ++ ++ /* Set Device Address to 0 */ ++ usb3_set_address(pcd, 0); ++ ++ pcd->ltm_enable = 0; ++} ++ ++static void handle_connect_done_intr(usb3_pcd_t *pcd) ++{ ++ usb3_pcd_ep_t *ep0 = &pcd->ep0; ++ unsigned int diepcfg0, doepcfg0, diepcfg1, doepcfg1; ++ usb3_dev_ep_regs_t *ep_reg; ++ int speed; ++ ++ ep0->stopped = 0; ++ speed = usb3_get_device_speed(pcd); ++ pcd->speed = speed; ++ ++ usb3_pcd_set_speed(pcd, speed); ++ ++ /* ++ * Set the MPS of EP0 based on the connection speed ++ */ ++ diepcfg0 = USB3_EP_TYPE_CONTROL << USB3_EPCFG0_EPTYPE_SHIFT; ++ diepcfg0 |= USB3_CFG_ACTION_MODIFY << USB3_EPCFG0_CFG_ACTION_SHIFT; ++ diepcfg1 = ++ USB3_EPCFG1_XFER_CMPL_BIT | USB3_EPCFG1_XFER_IN_PROG_BIT | USB3_EPCFG1_XFER_NRDY_BIT | USB3_EPCFG1_EP_DIR_BIT; ++ ++ doepcfg0 = USB3_EP_TYPE_CONTROL << USB3_EPCFG0_EPTYPE_SHIFT; ++ doepcfg0 |= USB3_CFG_ACTION_MODIFY << USB3_EPCFG0_CFG_ACTION_SHIFT; ++ doepcfg1 = USB3_EPCFG1_XFER_CMPL_BIT | USB3_EPCFG1_XFER_IN_PROG_BIT | USB3_EPCFG1_XFER_NRDY_BIT; ++ ++ switch (speed) { ++ case USB_SPEED_SUPER: ++ diepcfg0 |= 512 << USB3_EPCFG0_MPS_SHIFT; /* 512 for super speed */ ++ doepcfg0 |= 512 << USB3_EPCFG0_MPS_SHIFT; /* 512 for super speed */ ++ break; ++ ++ case USB_SPEED_HIGH: ++ case USB_SPEED_FULL: ++ diepcfg0 |= 64 << USB3_EPCFG0_MPS_SHIFT; /* 64 for high\full speed */ ++ doepcfg0 |= 64 << USB3_EPCFG0_MPS_SHIFT; /* 64 for high\full speed */ ++ break; ++ ++ case USB_SPEED_LOW: ++ diepcfg0 |= 8 << USB3_EPCFG0_MPS_SHIFT; /* 8 for low speed */ ++ doepcfg0 |= 8 << USB3_EPCFG0_MPS_SHIFT; /* 8 for low speed */ ++ break; ++ default: ++ break; ++ } ++ ++ diepcfg0 |= ep0->tx_fifo_num << USB3_EPCFG0_TXFNUM_SHIFT; ++ ++ /* Issue "DEPCFG" command to EP0-OUT */ ++ ep_reg = &pcd->out_ep_regs[0]; ++ usb3_dep_cfg(pcd, ep_reg, doepcfg0, doepcfg1, 0); ++ ++ /* Issue "DEPCFG" command to EP0-IN */ ++ ep_reg = &pcd->in_ep_regs[0]; ++ usb3_dep_cfg(pcd, ep_reg, diepcfg0, diepcfg1, 0); ++ ++ pcd->state = USB3_STATE_DEFAULT; ++} ++ ++int usb3_handle_ep_intr(usb3_pcd_t *pcd, unsigned int physep, unsigned int event) ++{ ++ usb3_pcd_ep_t *ep; ++ int epnum, is_in; ++ int error = USB_NO_ERR; ++ ++ if (pcd == NULL) ++ return USB_PROCESS_ERR; ++ ++ /* Physical Out EPs are even, physical In EPs are odd */ ++ is_in = physep & 1; ++ epnum = (physep >> 1) & 0xf; ++ ++ /* Get EP pointer */ ++ if (is_in) { ++ ep = usb3_get_in_ep(pcd, epnum); ++ } else { ++ ep = usb3_get_out_ep(pcd, epnum); ++ } ++ if (ep == NULL) ++ return USB_PROCESS_ERR; ++ ++ // printf("\n##########%s,%d,event=0x%x\n",__func__,__LINE__,event); ++ switch (event & USB3_DEPEVT_INTTYPE_BITS) { ++ case USB3_DEPEVT_XFER_CMPL << USB3_DEPEVT_INTTYPE_SHIFT: ++ ep->xfer_started = 0; ++ ++ /* Complete the transfer */ ++ if (epnum == 0) { ++ usb3_os_handle_ep0(pcd, event); ++ } else { ++ // printf("\n##########%s,%d,event=0x%x\n",__func__,__LINE__,event); ++ error = usb3_ep_complete_request(pcd, ep, event); ++ } ++ break; ++ ++ case USB3_DEPEVT_XFER_NRDY << USB3_DEPEVT_INTTYPE_SHIFT: ++ if (epnum != 0) ++ break; ++ switch (pcd->ep0state) { ++ case EP0_IN_WAIT_NRDY: ++ if (is_in) ++ usb3_os_handle_ep0(pcd, event); ++ break; ++ case EP0_OUT_WAIT_NRDY: ++ if (!is_in) ++ usb3_os_handle_ep0(pcd, event); ++ break; ++ default: ++ break; ++ } ++ ++ break; ++ default: ++ break; ++ } ++ ++ return error; ++} ++ ++void usb3_handle_dev_intr(usb3_pcd_t *pcd, unsigned int event) ++{ ++ unsigned int dint = (event >> USB3_DEVT_SHIFT) & (USB3_DEVT_BITS >> USB3_DEVT_SHIFT); ++ ++ switch (dint) { ++ case USB3_DEVT_USBRESET: ++ handle_usb_reset_intr(pcd); ++ break; ++ ++ case USB3_DEVT_CONNDONE: ++ handle_connect_done_intr(pcd); ++ break; ++ default: ++ break; ++ } ++} +diff --git a/drivers/usb/gadget/udc3/usb3_prot.c b/drivers/usb/gadget/udc3/usb3_prot.c +new file mode 100644 +index 0000000..098595f +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_prot.c +@@ -0,0 +1,208 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "usb3_drv.h" ++#include "usb3_pcd.h" ++#include "usb3_prot.h" ++#include "onchiprom.h" ++ ++#include ++#include ++#include ++ ++#define USTART 0xFA ++#define UHEAD 0xFE ++#define UDATA 0xDA ++#define UTAIL 0xED ++#define UCMD 0XAB ++#define UREQ 0XFB ++ ++#define TX_STATE_BUFFER 500 ++#define TX_STATE_LIMIT 400 ++#define BUFFER_SIZE 512 ++ ++char u_ack[2] = {0xAA, '\0'}; ++char u_nak[2] = {0x55, '\0'}; ++char tx_state[TX_STATE_BUFFER] = {0, 0x55, 0}; ++static phys_addr_t rx_addr; ++static unsigned int rx_length; ++int usb_connected; ++int usb_out_open = 0; ++ ++void usb3_bulk_out_transfer_cmd(void *dev) ++{ ++ usb3_pcd_t *pcd = (usb3_pcd_t *)dev; ++ usb3_pcd_ep_t *ep = &pcd->out_ep; ++ usb3_pcd_req_t *req = &ep->req; ++ ++ (void)memset_s(pcd->ss_bulk_buf, USB3_BULK_BUF_SIZE, 0x0, USB3_BULK_BUF_SIZE); ++ ++ req->length = ep->maxpacket; ++ req->bufdma = pcd->ss_bulk_buf; ++ req->complete = usb3_handle_protocol; ++ usb3_ep_start_transfer(pcd, ep); ++ ++ return; ++} ++ ++void usb3_handle_data(void *dev) ++{ ++ usb3_pcd_t *pcd = NULL; ++ usb3_pcd_ep_t *ep = NULL; ++ usb3_pcd_req_t *req = NULL; ++ if (dev == NULL) ++ return; ++ ++ pcd = (usb3_pcd_t *)dev; ++ ep = &pcd->out_ep; ++ req = &ep->req; ++ ++ if (req->actual > rx_length) ++ req->actual = rx_length; ++ ++ rx_addr += req->actual; ++ rx_length -= req->actual; ++ ++ if (rx_length > 0) { ++ usb3_bulk_out_transfer_data(dev); ++ } else { ++ usb3_bulk_out_transfer_cmd(dev); ++ } ++} ++ ++int usb3_bulk_out_transfer_data(void *dev) ++{ ++ usb3_pcd_t *pcd = NULL; ++ usb3_pcd_ep_t *ep = NULL; ++ usb3_pcd_req_t *req = NULL; ++ if (dev == NULL) ++ return USB_PROCESS_ERR; ++ ++ pcd = (usb3_pcd_t *)dev; ++ ep = &pcd->out_ep; ++ req = &ep->req; ++ ++ /* For DWC3 controller,If CHN=0 and HWO=0 for the TRB, this field represents the total remaining Buffer ++ * Descriptor buffer size in bytes. Valid Range: 0 bytes to (16 MB - 1 byte). ++ * The hardware decrements this field to represent the remaining buffer size after data is ++ * transferred. ++ * Note: for bulk transfer, max length = 16MB - maxpacket; ++ * */ ++ if (rx_length >= (USB3_DSCSTS_XFRCNT_MAX - ep->maxpacket)) { ++ req->length = USB3_DSCSTS_XFRCNT_MAX - ep->maxpacket; ++ } else { ++ req->length = rx_length; ++ } ++ ++ req->bufdma = (unsigned char *)rx_addr; ++ req->complete = usb3_handle_data; ++ ++ usb3_ep_start_transfer(pcd, ep); ++ ++ return USB_NO_ERR; ++} ++ ++void udc_puts(const char *s) ++{ ++ if (s == NULL) ++ return; ++ ++ if (strlen(tx_state) + strlen(s) > TX_STATE_LIMIT) ++ (void)memset_s(tx_state, TX_STATE_BUFFER, 0, TX_STATE_BUFFER); ++ ++ if (strlen(s) > TX_STATE_BUFFER) { ++ return; ++ } else { ++ if (usb_out_open == 1) ++ if (strcat_s(tx_state, TX_STATE_BUFFER, s) < 0) ++ return; ++ } ++} ++ ++typedef int (*USB3_HANDLE_REQUEST)(unsigned char * const buff, unsigned int *bufflen); ++USB3_HANDLE_REQUEST handle_request = NULL; ++void set_usb3_call_back_func(USB3_HANDLE_REQUEST func) ++{ ++ handle_request = func; ++} ++ ++void usb3_handle_protocol(void *dev) ++{ ++ usb3_pcd_t *pcd = (usb3_pcd_t *)dev; ++ int ret; ++ char *buf = (char *)pcd->ss_bulk_buf; ++ ++ if (buf[0] == USTART) { ++ usb3_bulk_out_transfer_cmd(pcd); ++ usb3_bulk_in_transfer(dev, u_ack); ++ } else if (buf[0] == UHEAD) { ++ /*left shift 8/16/24 bit [5][6][7][8]:buffer number */ ++ rx_addr = (buf[5] << 24) | (buf[6] << 16) | (buf[7] << 8) | (buf[8]); ++ /*left shift 8/16/24 bit [1][2][3][4]:buffer number */ ++ rx_length = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | (buf[4]); ++ if (rx_addr == rx_length) { ++ usb3_bulk_out_transfer_cmd(pcd); ++ usb_connected = 1; ++ usb_out_open = 1; ++ } else { ++ usb3_bulk_out_transfer_data(pcd); ++ } ++ usb3_bulk_in_transfer(dev, u_ack); ++ return; ++ } else if (buf[0] == UCMD) { ++ if (strcat_s(tx_state, TX_STATE_BUFFER, " ") < 0) ++ return; ++ ++ usb_out_open = 1; ++ ret = run_command(buf + 0x3, 0); ++ if (ret) { ++ usb3_bulk_in_transfer(dev, "[EOT](ERROR)\r\n"); ++ } else { ++ if (strcat_s(tx_state, TX_STATE_BUFFER, "[EOT](OK)\r\n") < 0) ++ return; ++ ++ usb3_bulk_out_transfer_cmd(pcd); ++ usb3_bulk_in_transfer(dev, tx_state); ++ (void)memset_s(tx_state, TX_STATE_BUFFER, 0, TX_STATE_BUFFER); ++ } ++ } else if (buf[0] == UTAIL) { ++ if (rx_length > 0) { ++ usb3_bulk_out_transfer_cmd(pcd); ++ usb3_bulk_in_transfer(dev, u_nak); ++ } else { ++ usb3_bulk_in_transfer(dev, u_ack); ++ usb3_bulk_out_transfer_cmd(pcd); ++ } ++ } else if (buf[0] == UREQ) { ++ usb_out_open = 0; ++ if (handle_request != NULL) { ++ usb3_pcd_ep_t *ep = &pcd->in_ep; ++ usb3_pcd_req_t *req = &ep->req; ++ ++ (void)memset_s(req->bufdma, BUFFER_SIZE, 0, BUFFER_SIZE); ++ req->length = 0; ++ ret = (*handle_request)(req->bufdma, &req->length); ++ ++ req->complete = usb_tx_status_complete; ++ usb3_ep_start_transfer(pcd, ep); ++ ++ usb3_bulk_out_transfer_cmd(pcd); ++ } ++ } ++} +diff --git a/drivers/usb/gadget/udc3/usb3_prot.h b/drivers/usb/gadget/udc3/usb3_prot.h +new file mode 100644 +index 0000000..dbb0a27 +--- /dev/null ++++ b/drivers/usb/gadget/udc3/usb3_prot.h +@@ -0,0 +1,64 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __USB3_PROT_H__ ++#define __USB3_PROT_H__ ++ ++ ++/* FILE FRAME: TYPE(1)+SEQ(1)+CSEQ(1)+FILE(1)+LENGTH(4)+ADDRESS(4)+CRC(2) */ ++/* DATA FRAME: TYPE(1)+SEQ(1)+CSEQ(1)+DATA(0~1024)+CRC(2) */ ++/* EOT FRAME: TYPE(1)+SEQ(1)+CSEQ(1)+CRC(2) */ ++#define FRAME_HEAD 0 ++#define FRAME_SEQ 1 ++#define FRAME_CSEQ 2 ++#define FRAME_TYPE 3 ++#define FRAME_DATA_START 3 ++#define FRAME_LENGTH 4 ++#define FRAME_ADDRESS 8 ++ ++#define FRAME_FILE 0xFE ++#define FRAME_DATA 0xDA ++#define FRAME_EOT 0xED ++#define FRAME_INQUIRE 0xCD ++ ++#define USB3_RESPONSE_ACK 0xAA ++#define USB3_RESPONSE_NAK 0x55 ++ ++#define FRAME_FILE_LEN 14 ++#define FRAME_EOT_LEN 5 ++ ++#define FRAME_HEAD_LEN 5 ++#define FRAME_DATA_LEN 1024 ++ ++#define FILE_RAMINIT 1 ++#define FILE_USB 2 ++ ++#define USB3_XFR_PROT_OK 0 ++#define USB3_XFR_PROT_ERR 1 ++#define USB3_XFR_PROT_SKIP 2 ++#define USB3_XFR_PROT_COMPLETE 3 ++#define USB3_XFR_PROT_INPROGRESS 4 ++#define USB3_XFR_PROT_WAIT 0x05 ++#define USB3_XFR_PROT_INQUIRE 0x06 ++#define USB3_XFR_PROT_ADDR_ERROR 0x07 ++ ++ ++void usb3_handle_protocol(void *dev); ++ ++#endif /* __USB3_PROT_H__ */ +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +index 40dee2e..b964b34 100644 +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include "../drivers/phy/vendor/usb.h" + + #ifndef CONFIG_USB_MAX_CONTROLLER_COUNT + #define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +@@ -1301,6 +1302,8 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) + + *controller = NULL; + ++ phy_usb_init(index); ++ + if (xhci_hcd_init(index, &hccr, (struct xhci_hcor **)&hcor) != 0) + return -ENODEV; + +diff --git a/env/Kconfig b/env/Kconfig +index ed12609..3a04660 100644 +--- a/env/Kconfig ++++ b/env/Kconfig +@@ -7,7 +7,7 @@ config ENV_IS_NOWHERE + !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \ + !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \ + !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \ +- !ENV_IS_IN_UBI ++ !ENV_IS_IN_UBI && !ENV_IS_IN_UFS + help + Define this if you don't want to or can't have an environment stored + on a storage medium. In this case the environment will still exist +@@ -194,6 +194,14 @@ config ENV_IS_IN_MMC + This value is also in units of bytes, but must also be aligned to + an MMC sector boundary. + ++config ENV_IS_IN_UFS ++ bool "Environment in an UFS device" ++ depends on !CHAIN_OF_TRUST ++ depends on UFS ++ help ++ Define this if you have an UFS device which you want to use for the ++ environment. ++ + config ENV_IS_IN_NAND + bool "Environment in a NAND device" + depends on !CHAIN_OF_TRUST +@@ -280,7 +288,7 @@ config ENV_IS_IN_REMOTE + + config ENV_IS_IN_SPI_FLASH + bool "Environment is in SPI flash" +- depends on !CHAIN_OF_TRUST && SPI ++ depends on !CHAIN_OF_TRUST && SPI || FMC + default y if ARMADA_XP + default y if INTEL_BAYTRAIL + default y if INTEL_BRASWELL +diff --git a/env/Makefile b/env/Makefile +old mode 100644 +new mode 100755 +index 90144d6..6ba6901 +--- a/env/Makefile ++++ b/env/Makefile +@@ -27,6 +27,7 @@ endif + + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE) += nowhere.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o ++obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_UFS) += ufs.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o +diff --git a/env/env.c b/env/env.c +old mode 100644 +new mode 100755 +index 9237bb9..47e7053 +--- a/env/env.c ++++ b/env/env.c +@@ -65,6 +65,12 @@ static enum env_location env_locations[] = { + #ifdef CONFIG_ENV_IS_IN_MMC + ENVL_MMC, + #endif ++/* Prevents the environment variables from being saved to the ++ * spinand when the system boots from the SPI NOR flash. ++ */ ++#ifdef CONFIG_ENV_IS_IN_SPI_FLASH ++ ENVL_SPI_FLASH, ++#endif + #ifdef CONFIG_ENV_IS_IN_NAND + ENVL_NAND, + #endif +@@ -77,12 +83,12 @@ static enum env_location env_locations[] = { + #ifdef CONFIG_ENV_IS_IN_SATA + ENVL_ESATA, + #endif +-#ifdef CONFIG_ENV_IS_IN_SPI_FLASH +- ENVL_SPI_FLASH, +-#endif + #ifdef CONFIG_ENV_IS_IN_UBI + ENVL_UBI, + #endif ++#ifdef CONFIG_ENV_IS_IN_UFS ++ ENVL_UFS, ++#endif + #ifdef CONFIG_ENV_IS_NOWHERE + ENVL_NOWHERE, + #endif +diff --git a/env/ufs.c b/env/ufs.c +new file mode 100644 +index 0000000..efd1878 +--- /dev/null ++++ b/env/ufs.c +@@ -0,0 +1,147 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++#if !defined(CONFIG_ENV_OFFSET) ++#define CONFIG_ENV_OFFSET 0 ++#endif ++ ++#define UFS_BLK_LEN 4096 ++ ++static inline s64 ufs_offset(void) ++{ ++ return CONFIG_ENV_OFFSET; ++} ++ ++__weak int ufs_get_env_addr(u32 *env_addr) ++{ ++ s64 offset = ufs_offset(); ++ ++ *env_addr = offset; ++ ++ return 0; ++} ++ ++#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD) ++static int write_env(unsigned long size, unsigned long offset, const void *buffer) ++{ ++ uint start, cnt, n; ++ ++ start = ALIGN(offset, UFS_BLK_LEN); ++ cnt = ALIGN(size, UFS_BLK_LEN); ++ ++ n = ufs_write_storage((uint64_t)(uintptr_t)buffer, start, cnt); ++ ++ return (n == 0) ? 0 : -1; ++} ++ ++static int env_ufs_save(void) ++{ ++ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); ++ u32 offset; ++ int ret; ++ ++ ufs_storage_init(); ++ ++ ret = env_export(env_new); ++ if (ret) ++ goto err; ++ ++ if (ufs_get_env_addr(&offset)) { ++ ret = 1; ++ goto err; ++ } ++ ++ printf("Writing to UFS... "); ++ if (write_env(CONFIG_ENV_SIZE, offset, (u_char *)env_new)) { ++ puts("failed\n"); ++ ret = 1; ++ goto err; ++ } ++ ++ ret = 0; ++ ++err: ++ return ret; ++} ++#endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */ ++ ++static int read_env(unsigned long size, unsigned long offset, const void *buffer) ++{ ++ uint start, cnt, n; ++ ++ start = ALIGN(offset, UFS_BLK_LEN); ++ cnt = ALIGN(size, UFS_BLK_LEN); ++ ++ n = ufs_read_storage((uint64_t)(uintptr_t)buffer, start, cnt); ++ ++ return (n == 0) ? 0 : -1; ++} ++ ++static int env_ufs_load(void) ++{ ++#if !defined(ENV_IS_EMBEDDED) ++ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); ++ u32 offset; ++ int ret; ++ char *errmsg = NULL; ++ ++ ufs_storage_init(); ++ ++ if (ufs_get_env_addr(&offset)) { ++ ret = -EIO; ++ goto err; ++ } ++ ++ if (read_env(CONFIG_ENV_SIZE, offset, buf)) { ++ errmsg = "!read failed"; ++ ret = -EIO; ++ goto err; ++ } ++ ++ ret = env_import(buf, 1); ++ ++err: ++ if (ret) ++ env_set_default(errmsg, 0); ++#endif ++ return ret; ++} ++ ++U_BOOT_ENV_LOCATION(ufs) = { ++ .location = ENVL_UFS, ++ ENV_NAME("UFS") ++ .load = env_ufs_load, ++#ifndef CONFIG_SPL_BUILD ++ .save = env_save_ptr(env_ufs_save), ++#endif ++}; +diff --git a/examples/Makefile b/examples/Makefile +index d440bc5..818719d 100644 +--- a/examples/Makefile ++++ b/examples/Makefile +@@ -5,7 +5,8 @@ ifndef CONFIG_SANDBOX + ifdef FTRACE + subdir-ccflags-y += -finstrument-functions -DFTRACE + endif +- ++ifndef CONFIG_VENDOR_MC + subdir-y += standalone ++endif + subdir-$(CONFIG_API) += api + endif +diff --git a/examples/standalone/Makefile b/examples/standalone/Makefile +index 0b17a91..4a7e8ac 100644 +--- a/examples/standalone/Makefile ++++ b/examples/standalone/Makefile +@@ -19,12 +19,14 @@ ELF := $(strip $(extra-y)) + extra-y += $(addsuffix .srec,$(extra-y)) $(addsuffix .bin,$(extra-y)) + clean-files := *.srec *.bin + ++ifndef CONFIG_VENDOR_MC + COBJS := $(ELF:=.o) + + LIB = $(obj)/libstubs.o ++LIBOBJS-y += stubs.o ++endif + + LIBOBJS-$(CONFIG_PPC) += ppc_longjmp.o ppc_setjmp.o +-LIBOBJS-y += stubs.o + + targets += $(patsubst $(obj)/%,%,$(LIB)) $(COBJS) $(LIBOBJS-y) + +diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile +index c2544be..e65fcd1 100644 +--- a/fs/ext4/Makefile ++++ b/fs/ext4/Makefile +@@ -9,3 +9,4 @@ + + obj-y := ext4fs.o ext4_common.o dev.o + obj-$(CONFIG_EXT4_WRITE) += ext4_write.o ext4_journal.o crc16.o ++obj-$(CONFIG_EXT4_SPARSE) += unsparse.o +diff --git a/fs/ext4/unsparse.c b/fs/ext4/unsparse.c +new file mode 100644 +index 0000000..ab0af78 +--- /dev/null ++++ b/fs/ext4/unsparse.c +@@ -0,0 +1,456 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FILLBUF_SIZE 4096 ++ ++#define EMMC_BLKSIZE_SHIFT 9 ++#define SZ_1M_SHIFT 20 ++#define FILLBUF_NUM_MMC_BLKS FILLBUF_SIZE >> EMMC_BLKSIZE_SHIFT ++ ++#ifdef CONFIG_CMD_UFS ++#include ++#define UFS_BLKSIZE_SHIFT 12 ++#define FILLBUF_NUM_UFS_BLKS FILLBUF_SIZE >> UFS_BLKSIZE_SHIFT ++#endif ++ ++/* Open it as you need #define DEBUG_EXT4 */ ++ ++void print_header_info(const sparse_header_t *header) ++{ ++#ifdef DEBUG_EXT4 ++ printf("sparse header info:\n"); ++ printf("magic: 0x%x\n", header->magic); ++ printf("major_version: 0x%x\n", header->major_version); ++ printf("minor_version: 0x%x\n", header->minor_version); ++ printf("file_hdr_sz: %d\n", header->file_hdr_sz); ++ printf("chunk_hdr_sz: %d\n", header->chunk_hdr_sz); ++ printf("blk_sz: %d\n", header->blk_sz); ++ printf("total_blks: %d\n", header->total_blks); ++ printf("total_chunks: %d\n", header->total_chunks); ++ printf("image_checksum: %d\n", header->image_checksum); ++#endif ++} ++ ++void print_chunk_info(const chunk_header_t *chunk) ++{ ++#ifdef DEBUG_EXT4 ++ printf("\n"); ++ printf("chunk header info:\n"); ++ printf("chunk_type: 0x%x\n", chunk->chunk_type); ++ printf("chunk_sz: %d\n", chunk->chunk_sz); ++ printf("total_sz: %d\n", chunk->total_sz); ++#endif ++} ++ ++int get_unspare_header_info(const u8 *pbuf, sparse_header_t *sparse_header, char* is_sparse) ++{ ++ (void)memcpy_s(sparse_header, sizeof(sparse_header_t), pbuf, sizeof(sparse_header_t)); ++ ++ if (!is_sparse_image(sparse_header)) { ++ *is_sparse = 0; ++ return 0; ++ } ++ *is_sparse = 1; ++ print_header_info(sparse_header); ++ return 0; ++} ++static int check_parameter(sparse_header_t *header, u32 cnt, unsigned int type) ++{ ++ u64 img_size; ++ ++ if (!is_sparse_image(header)) { ++ printf("Invalid sparse format.\n"); ++ return 1; ++ } ++ ++ /* check header->blk_sz align to emmc block size */ ++ if (type == 0) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ if (header->blk_sz & ((1 << EMMC_BLKSIZE_SHIFT) - 1)) { ++ printf("image blk size %d is not aligned to 512Byte.\n", header->blk_sz); ++ return 1; ++ } ++#ifdef CONFIG_CMD_UFS ++ if (type == 1) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ if (header->blk_sz & ((1 << UFS_BLKSIZE_SHIFT) - 1)) { ++ printf("image blk size %d is not aligned to 512Byte.\n", header->blk_sz); ++ return 1; ++ } ++#endif ++ ++ /* check fs img's real size is larger than partition size */ ++ img_size = (u64)(header->total_blks * header->blk_sz); ++ if (type == 0) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ if ((img_size >> EMMC_BLKSIZE_SHIFT) > (u64)cnt) { ++ printf("partition size %d MB not enough.\n", ++ (int)(cnt >> (SZ_1M_SHIFT - EMMC_BLKSIZE_SHIFT))); ++ print_header_info(header); ++ return 1; ++ } ++#ifdef CONFIG_CMD_UFS ++ if (type == 1) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ if ((img_size >> UFS_BLKSIZE_SHIFT) > (u64)cnt) { ++ printf("partition size %d MB not enough.\n", ++ (int)(cnt >> (SZ_1M_SHIFT - UFS_BLKSIZE_SHIFT))); ++ print_header_info(header); ++ return 1; ++ } ++#endif ++ return 0; ++} ++ ++static void print_writing_complete(u64 sparse_len, s32 *percent_complete, u32 cnt, u32 blk, unsigned int type) ++{ ++ u64 n; ++ int percent; ++ ++ if (type == 0) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ n = (u64)(sparse_len >> EMMC_BLKSIZE_SHIFT) * 100; /* 100: Percentage for complete */ ++#ifdef CONFIG_CMD_UFS ++ if (type == 1) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ n = (u64)(sparse_len >> UFS_BLKSIZE_SHIFT) * 100; /* 100: Percentage for complete */ ++#endif ++ ++ do_div(n, cnt); ++ percent = (int)n; ++ if (percent != *percent_complete) { ++ *percent_complete = percent; ++ if (type == 0) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ printf("\rWriting at %d blk# " ++ "-- %3d%% complete. \n", ++ (int)(blk + (sparse_len >> EMMC_BLKSIZE_SHIFT)), percent); ++#ifdef CONFIG_CMD_UFS ++ if (type == 1) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ printf("\rWriting at %d blk# " ++ "-- %3d%% complete.\n", ++ (int)(blk + (sparse_len >> UFS_BLKSIZE_SHIFT)), percent); ++#endif ++ } ++} ++ ++static int check_print_results(u64 dense_len, u64 sparse_len, u32 cnt, unsigned int type) ++{ ++ if (type == 0) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ if (((u64)cnt << EMMC_BLKSIZE_SHIFT) != sparse_len) { ++ printf("error: partition size %d MB != ext4 image size %d MB\n", ++ (int)(cnt >> (SZ_1M_SHIFT - EMMC_BLKSIZE_SHIFT)), ++ (int)(sparse_len >> SZ_1M_SHIFT)); ++ return 1; ++ } ++#ifdef CONFIG_CMD_UFS ++ if (type == 1) /* 0: ext4_unsparse 1: ufs_ext4_unsparse */ ++ if (((u64)cnt << UFS_BLKSIZE_SHIFT) != sparse_len) { ++ printf("error: partition size %d MB != ext4 image size %d MB\n", ++ (int)(cnt >> (SZ_1M_SHIFT - UFS_BLKSIZE_SHIFT)), ++ (int)(sparse_len >> SZ_1M_SHIFT)); ++ return 1; ++ } ++#endif ++ ++ printf("sparse: %d MB / %d MB.\n", (int)(dense_len >> SZ_1M_SHIFT), (int)(sparse_len >> SZ_1M_SHIFT)); ++ return 0; ++} ++ ++int ext4_unsparse(struct mmc *mmc, u32 dev, u8 *pbuf, u32 blk, u32 cnt) ++{ ++ u32 i, num; ++ u64 dense_len = 0; ++ u64 sparse_len = 0; ++ u32 chunk_len; ++ s32 percent_complete = -1; ++ chunk_header_t *chunk = NULL; ++ uint32_t fill_buf[FILLBUF_SIZE / sizeof(uint32_t)]; ++ sparse_header_t *header = (sparse_header_t *)pbuf; ++ ++ check_parameter(header, cnt, 0); ++ ++ /* skip the sparse header,to visit first chunk */ ++ pbuf += header->file_hdr_sz; ++ ++ /* to visit each chunk */ ++ for (i = 0; i < header->total_chunks; i++) { ++ /* here the chunk_header */ ++ chunk = (chunk_header_t *)pbuf; ++ ++ /* go to next chunk's data */ ++ pbuf += header->chunk_hdr_sz; ++ ++ switch (chunk->chunk_type) { ++ case CHUNK_TYPE_RAW: ++ ++ /* to calculate the length of each chunk */ ++ chunk_len = chunk->chunk_sz * header->blk_sz; ++ ++ /* verify every chunk to asure it is valid */ ++ if (chunk->total_sz != (chunk_len + header->chunk_hdr_sz)) { ++ printf("No.%d chunk size error.\n", i); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ dense_len += chunk_len; ++ sparse_len += chunk_len; ++ ++ if (sparse_len > ((u64)cnt << EMMC_BLKSIZE_SHIFT)) { ++ printf("error: sparse size %d MB is " ++ "larger than partiton size %d MB.\n", ++ (int)(sparse_len >> SZ_1M_SHIFT), ++ (int)(cnt >> (SZ_1M_SHIFT - EMMC_BLKSIZE_SHIFT))); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ num = blk_dwrite(mmc_get_blk_desc(mmc), blk, ++ (chunk_len >> EMMC_BLKSIZE_SHIFT), pbuf); ++ if (num != (chunk_len >> EMMC_BLKSIZE_SHIFT)) { ++ printf("Raw data: No.%d blocks written: %s.\n", num, "ERROR"); ++ return 1; ++ } ++ ++ pbuf += chunk_len; ++ blk += (chunk_len >> EMMC_BLKSIZE_SHIFT); ++ break; ++ ++ case CHUNK_TYPE_DONT_CARE: ++ if (chunk->total_sz != header->chunk_hdr_sz) { ++ printf("No.%d chunk size error.\n", i); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ chunk_len = chunk->chunk_sz * header->blk_sz; ++ sparse_len += chunk_len; ++ ++ if (sparse_len > ((u64)cnt << EMMC_BLKSIZE_SHIFT)) { ++ printf("error: sparse size %d MB is " ++ "larger than partiton size %d MB.\n", ++ (int)(sparse_len >> SZ_1M_SHIFT), ++ (int)(cnt >> (SZ_1M_SHIFT - EMMC_BLKSIZE_SHIFT))); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ blk += (chunk_len >> EMMC_BLKSIZE_SHIFT); ++ break; ++ ++ case CHUNK_TYPE_FILL: { ++ uint32_t fill_val = *(uint32_t *)pbuf; ++ u32 blkcnt, blk_writed, j; ++ ++ pbuf = (u8 *)pbuf + sizeof(uint32_t); ++ ++ if (chunk->total_sz != (header->chunk_hdr_sz + sizeof(uint32_t))) { ++ print_chunk_info(chunk); ++ printf("Bogus chunk size for chunk type FILL"); ++ return 1; ++ } ++ ++ for (j = 0; j < FILLBUF_SIZE / sizeof(uint32_t); j++) ++ fill_buf[j] = fill_val; ++ ++ chunk_len = chunk->chunk_sz * header->blk_sz; ++ blkcnt = chunk_len >> EMMC_BLKSIZE_SHIFT; ++ if (blk + blkcnt > blk + cnt) { ++ print_chunk_info(chunk); ++ printf( ++ "%s: Request would exceed partition size!\n", ++ __func__); ++ return 1; ++ } ++ ++ for (blk_writed = 0; blk_writed < blkcnt;) { ++ unsigned int write_blks = FILLBUF_NUM_MMC_BLKS < blkcnt - blk_writed ? blkcnt - blk_writed : FILLBUF_NUM_MMC_BLKS; ++ num = blk_dwrite(mmc_get_blk_desc(mmc), blk, ++ write_blks, fill_buf); ++ if (num != write_blks) { ++ print_chunk_info(chunk); ++ printf("Raw data: No.%d blocks written: %s.\n", num, "ERROR"); ++ return 1; ++ } ++ blk_writed += write_blks; ++ blk += write_blks; ++ } ++ sparse_len += chunk_len; ++ break; ++ } ++ default: ++ printf("sparse: unknow chunk type %04x.\n", ++ chunk->chunk_type); ++ return 1; ++ } ++ print_writing_complete(sparse_len, &percent_complete, cnt, blk, 0); ++ } ++ puts("\n"); ++ ++ if (check_print_results(dense_len, sparse_len, cnt, 0)) ++ return 1; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_CMD_UFS ++ ++int ufs_ext4_unsparse(const u8 *pbuf, u32 blk, u32 cnt) ++{ ++ u32 i, num; ++ u64 dense_len = 0; ++ u64 sparse_len = 0; ++ u32 chunk_len; ++ s32 percent_complete = -1; ++ chunk_header_t *chunk = NULL; ++ sparse_header_t *header = (sparse_header_t *)pbuf; ++ uint32_t fill_buf[FILLBUF_SIZE / sizeof(uint32_t)]; ++ ++ check_parameter(header, cnt, 1); ++ /* skip the sparse header,to visit first chunk */ ++ pbuf += header->file_hdr_sz; ++ ++ /* to visit each chunk */ ++ for (i = 0; i < header->total_chunks; i++) { ++ /* here the chunk_header */ ++ chunk = (chunk_header_t *)pbuf; ++ /* go to next chunk's data */ ++ pbuf += header->chunk_hdr_sz; ++ ++ switch (chunk->chunk_type) { ++ case CHUNK_TYPE_RAW: ++ ++ /* to calculate the length of each chunk */ ++ chunk_len = chunk->chunk_sz * header->blk_sz; ++ ++ /* verify every chunk to asure it is valid */ ++ if (chunk->total_sz ++ != (chunk_len + header->chunk_hdr_sz)) { ++ printf("No.%d chunk size error.\n", i); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ dense_len += chunk_len; ++ sparse_len += chunk_len; ++ ++ if (sparse_len > ((u64)cnt << UFS_BLKSIZE_SHIFT)) { ++ printf("error: sparse size %d MB is " ++ "larger than partiton size %d MB.\n", ++ (int)(sparse_len >> SZ_1M_SHIFT), ++ (int)(cnt >> (SZ_1M_SHIFT - UFS_BLKSIZE_SHIFT))); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ if (ufs_write_storage((uint64_t)pbuf, ++ (blk << UFS_BLKSIZE_SHIFT), ++ chunk_len) == 0) ++ num = chunk_len >> UFS_BLKSIZE_SHIFT; ++ else ++ num = 0; ++ ++ if (num != (chunk_len >> UFS_BLKSIZE_SHIFT)) { ++ printf("Raw data: No.%d blocks written: %s.\n", ++ num, "ERROR"); ++ return 1; ++ } ++ ++ pbuf += chunk_len; ++ blk += (chunk_len >> UFS_BLKSIZE_SHIFT); ++ break; ++ ++ case CHUNK_TYPE_DONT_CARE: ++ if (chunk->total_sz != header->chunk_hdr_sz) { ++ printf("No.%d chunk size error.\n", i); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ chunk_len = chunk->chunk_sz * header->blk_sz; ++ sparse_len += chunk_len; ++ ++ if (sparse_len > ((u64)cnt << UFS_BLKSIZE_SHIFT)) { ++ printf("error: sparse size %d MB is " ++ "larger than partiton size %d MB.\n", ++ (int)(sparse_len >> SZ_1M_SHIFT), ++ (int)(cnt >> (SZ_1M_SHIFT - UFS_BLKSIZE_SHIFT))); ++ print_chunk_info(chunk); ++ return 1; ++ } ++ ++ blk += (chunk_len >> UFS_BLKSIZE_SHIFT); ++ break; ++ case CHUNK_TYPE_FILL: { ++ uint32_t fill_val = *(uint32_t *)pbuf; ++ u32 blkcnt, blk_writed, j; ++ ++ pbuf = (u8 *)pbuf + sizeof(uint32_t); ++ ++ if (chunk->total_sz != (header->chunk_hdr_sz + sizeof(uint32_t))) { ++ print_chunk_info(chunk); ++ printf("Bogus chunk size for chunk type FILL"); ++ return 1; ++ } ++ ++ for (j = 0; j < FILLBUF_SIZE / sizeof(uint32_t); j++) ++ fill_buf[j] = fill_val; ++ ++ chunk_len = chunk->chunk_sz * header->blk_sz; ++ blkcnt = chunk_len >> EMMC_BLKSIZE_SHIFT; ++ if (blk + blkcnt > blk + cnt) { ++ print_chunk_info(chunk); ++ printf( ++ "%s: Request would exceed partition size!\n", ++ __func__); ++ return 1; ++ } ++ ++ for (blk_writed = 0; blk_writed < blkcnt;) { ++ unsigned int write_blks = FILLBUF_NUM_MMC_BLKS < blkcnt - blk_writed ? blkcnt - blk_writed : FILLBUF_NUM_MMC_BLKS; ++ num = blk_dwrite(mmc_get_blk_desc(mmc), blk, ++ write_blks, fill_buf); ++ if (num != write_blks) { ++ print_chunk_info(chunk); ++ printf("Raw data: No.%d blocks written: %s.\n", num, "ERROR"); ++ return 1; ++ } ++ blk_writed += write_blks; ++ blk += write_blks; ++ } ++ sparse_len += chunk_len; ++ break; ++ } ++ default: ++ printf("sparse: unknown chunk type %04x.\n", ++ chunk->chunk_type); ++ return 1; ++ } ++ ++ print_writing_complete(sparse_len, &percent_complete, cnt, blk, 1); ++ } ++ puts("\n"); ++ ++ if (check_print_results(dense_len, sparse_len, cnt, 1)) ++ return 1; ++ ++ return 0; ++} ++#endif +diff --git a/fs/ext4/unsparse.h b/fs/ext4/unsparse.h +new file mode 100644 +index 0000000..39d1813 +--- /dev/null ++++ b/fs/ext4/unsparse.h +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __UNSPARSE__ ++#define __UNSPARSE__ ++ ++int get_unspare_header_info(const u8 *pbuf, sparse_header_t *sparse_header, char *is_sparse); ++void print_chunk_info(chunk_header_t *chunk); ++int ext4_unsparse(struct mmc *mmc, u32 dev, u8 *pbuf, u32 blk, u32 cnt); ++void print_header_info(sparse_header_t *header); ++#endif +diff --git a/fs/fat/fat.c b/fs/fat/fat.c +index 68ce658..622ef58 100644 +--- a/fs/fat/fat.c ++++ b/fs/fat/fat.c +@@ -94,7 +94,7 @@ int fat_register_device(struct blk_desc *dev_desc, int part_no) + + /* Read the partition table, if present */ + if (part_get_info(dev_desc, part_no, &info)) { +- if (part_no != 0) { ++ if (part_no != 1) { + printf("** Partition %d not valid on device %d **\n", + part_no, dev_desc->devnum); + return -1; +diff --git a/include/audio_ao.h b/include/audio_ao.h +new file mode 100644 +index 0000000..a69c381 +--- /dev/null ++++ b/include/audio_ao.h +@@ -0,0 +1,36 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __AUDIO_AO_H__ ++#define __AUDIO_AO_H__ ++ ++typedef enum { ++ AUDIO_SAMPLE_RATE_8000 = 8000, /* 8kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_12000 = 12000, /* 12kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_11025 = 11025, /* 11.025kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_16000 = 16000, /* 16kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_22050 = 22050, /* 22.050kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_24000 = 24000, /* 24kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_32000 = 32000, /* 32kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_44100 = 44100, /* 44.1kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_48000 = 48000, /* 48kHz samplerate*/ ++ AUDIO_SAMPLE_RATE_BUTT, ++} audio_sample_rate; ++ ++#endif +diff --git a/include/binman.h b/include/binman.h +new file mode 100644 +index 0000000..29b42e8 +--- /dev/null ++++ b/include/binman.h +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef _BINMAN_H_ ++#define _BINMAN_H_ ++ ++/** ++ *struct binman_entry - information about a binman entry ++ * ++ * @image_pos: Position of entry in the image ++ * @size: Size of entry ++ */ ++struct binman_entry { ++ u32 image_pos; ++ u32 size; ++}; ++ ++/** ++ * binman_entry_find() - Find a binman symbol ++ * ++ * This searches the binman information in the device tree for a symbol of the ++ * given name ++ * ++ * @name: Path to entry to examine (e.g. "/read-only/u-boot") ++ * @entry: Returns information about the entry ++ * @return 0 if OK, -ENOENT if the path is not found, other -ve value if the ++ * binman information is invalid (missing image-pos or size) ++ */ ++int binman_entry_find(const char *name, struct binman_entry *entry); ++ ++/** ++ * binman_init() - Set up the binman symbol information ++ * ++ * This locates the binary symbol information in the device tree ready for use ++ * ++ * @return 0 if OK, -ENOMEM if out of memory, -EINVAL if there is no binman node ++ */ ++int binman_init(void); ++ ++#endif +diff --git a/include/bootm.h b/include/bootm.h +index edeeacb..21dd987 100644 +--- a/include/bootm.h ++++ b/include/bootm.h +@@ -72,4 +72,9 @@ void board_quiesce_devices(void); + */ + void switch_to_non_secure_mode(void); + ++#if defined(CONFIG_TARGET_SS928V100) || defined(CONFIG_TARGET_SS927V100) ++extern int is_quick_boot_enable_otp(void); ++extern void set_scs_finish(void); ++#endif ++ + #endif +diff --git a/include/cmd_timestamp.h b/include/cmd_timestamp.h +new file mode 100644 +index 0000000..c7c0c82 +--- /dev/null ++++ b/include/cmd_timestamp.h +@@ -0,0 +1,23 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef _CMD_TIMESTAMP_H_ ++#define _CMD_TIMESTAMP_H_ ++void timestamp_mark(const char *name, unsigned int line); ++void timestamp_print(void); ++#endif +diff --git a/include/common.h b/include/common.h +index 52c0218..7386d26 100644 +--- a/include/common.h ++++ b/include/common.h +@@ -15,6 +15,7 @@ + + #ifndef __ASSEMBLY__ /* put C only stuff in this section */ + ++typedef unsigned char uchar; + typedef volatile unsigned long vu_long; + typedef volatile unsigned short vu_short; + typedef volatile unsigned char vu_char; +@@ -72,6 +73,7 @@ extern u8 __dtb_dt_begin[]; /* embedded device tree blob */ + extern u8 __dtb_dt_spl_begin[]; /* embedded device tree blob for SPL/TPL */ + int mdm_init(void); + ++extern int auto_update_flag; + /** + * arch_fixup_fdt() - Write arch-specific information to fdt + * +@@ -123,6 +125,9 @@ void fdc_hw_init (void); + int testdram(void); + #endif /* CONFIG_SYS_DRAM_TEST */ + ++void dcache_enable (void); ++void dcache_disable(void); ++ + #if defined(CONFIG_ARM) + void relocate_code(ulong); + #else +@@ -143,6 +148,10 @@ int get_clocks (void); + ulong get_bus_freq (ulong); + int get_serial_clock(void); + ++void print_to_tool(const char *fmt, ...); ++void add_shutdown(void (*shutdown)(void)); ++void do_shutdown(void); ++ + /* lib/uuid.c */ + #include + +@@ -154,6 +163,16 @@ int get_serial_clock(void); + + #include + ++#define BOOT_MEDIA_UNKNOWN (0) ++#define BOOT_MEDIA_UFS (1) ++#define BOOT_MEDIA_NAND (2) ++#define BOOT_MEDIA_SPIFLASH (3) ++#define BOOT_MEDIA_EMMC (4) ++ ++/* get uboot start media. */ ++int get_boot_media(void); ++unsigned int get_ddr_size(void); ++ + #else /* __ASSEMBLY__ */ + + #endif /* __ASSEMBLY__ */ +diff --git a/include/configs/ss927v100.h b/include/configs/ss927v100.h +new file mode 100644 +index 0000000..ef11e38 +--- /dev/null ++++ b/include/configs/ss927v100.h +@@ -0,0 +1,318 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __SS927V100_H ++#define __SS927V100_H ++ ++#include ++#include ++ ++#define CONFIG_REMAKE_ELF ++ ++#define CONFIG_SUPPORT_RAW_INITRD ++ ++#define CONFIG_BOARD_EARLY_INIT_F ++ ++#define CONFIG_TFTP_PORT ++ ++/* Physical Memory Map */ ++ ++/* CONFIG_SYS_TEXT_BASE needs to align with where ATF loads bl33.bin */ ++#define CONFIG_SYS_TEXT_BASE 0x48800000 ++#define CONFIG_SYS_TEXT_BASE_ORI 0x48700000 ++ ++ ++#define PHYS_SDRAM_1 0x40000000 ++#define PHYS_SDRAM_1_SIZE 0x20000000 ++ ++#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 ++ ++#define CONFIG_SYS_INIT_SP_ADDR 0x0402a000 ++ ++#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + 0x80000) ++#define CONFIG_SYS_GBL_DATA_SIZE 128 ++ ++/* Generic Timer Definitions */ ++#define COUNTER_FREQUENCY 24000000 ++ ++#define CONFIG_SYS_TIMER_RATE CFG_TIMER_CLK ++#define CONFIG_SYS_TIMER_COUNTER (CFG_TIMERBASE + REG_TIMER_VALUE) ++#define CONFIG_SYS_TIMER_COUNTS_DOWN ++ ++ ++/* Generic Interrupt Controller Definitions */ ++ ++/* Size of malloc() pool */ ++#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K) ++ ++/* PL011 Serial Configuration */ ++ ++#define CONFIG_PL011_CLOCK 24000000 ++ ++#define CONFIG_PL01x_PORTS \ ++{(void *)UART0_REG_BASE, (void *)UART1_REG_BASE, \ ++ (void *)UART2_REG_BASE, (void *)UART3_REG_BASE} ++ ++#define CONFIG_CUR_UART_BASE UART0_REG_BASE ++ ++#define CONFIG_64BIT ++ ++/*Network configuration*/ ++ ++#define CONFIG_PHY_GIGE ++#ifdef CONFIG_GMACV300_ETH ++#define CONFIG_GMAC_NUMS 1 ++#define CONFIG_GMAC_PHY0_ADDR 1 ++#define CONFIG_GMAC_PHY0_INTERFACE_MODE 2 /* rgmii 2, rmii 1, mii 0 */ ++#define CONFIG_GMAC_PHY1_ADDR 3 ++#define CONFIG_GMAC_PHY1_INTERFACE_MODE 2 /* rgmii 2, rmii 1, mii 0 */ ++#define CONFIG_GMAC_DESC_4_WORD ++#define CONFIG_SYS_FAULT_ECHO_LINK_DOWN 1 ++#endif ++ ++/* Flash Memory Configuration v100 */ ++#ifdef CONFIG_FMC ++#define CONFIG_FMC_REG_BASE FMC_REG_BASE ++#define CONFIG_FMC_BUFFER_BASE FMC_MEM_BASE ++#define CONFIG_FMC_MAX_CS_NUM 1 ++#endif ++ ++#ifdef CONFIG_FMC_SPI_NOR ++#define CONFIG_CMD_SF ++#define CONFIG_SPI_NOR_MAX_CHIP_NUM 1 ++#define CONFIG_SPI_NOR_QUIET_TEST ++#endif ++ ++#ifdef CONFIG_FMC_SPI_NAND ++#define CONFIG_CMD_NAND ++#define CONFIG_SPI_NAND_MAX_CHIP_NUM 1 ++#define CONFIG_SYS_MAX_NAND_DEVICE CONFIG_SPI_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_MAX_CHIPS CONFIG_SPI_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_BASE FMC_MEM_BASE ++#endif ++ ++#ifdef CONFIG_FMC_NAND ++/* #define CONFIG_NAND_EDO_MODE */ ++#define CONFIG_CMD_NAND ++#define CONFIG_NAND_MAX_CHIP_NUM 1 ++#define CONFIG_SYS_MAX_NAND_DEVICE CONFIG_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_MAX_CHIPS CONFIG_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_BASE FMC_MEM_BASE ++#endif ++ ++/* the flag for auto update. 1:enable; 0:disable */ ++#define CONFIG_AUTO_UPDATE 0 ++ ++#if (CONFIG_AUTO_UPDATE == 1) ++#define CONFIG_AUTO_UPDATE_ADAPTATION 1 ++#define CONFIG_AUTO_SD_UPDATE 1 ++#define CONFIG_AUTO_USB_UPDATE 1 ++#define CONFIG_FS_FAT 1 ++#define CONFIG_FS_FAT_MAX_CLUSTSIZE 65536 ++#endif ++ ++/*--------------------------------------------------------------------- ++ * sdcard system updae ++e* ---------------------------------------------------------------------*/ ++ ++#if (CONFIG_AUTO_SD_UPDATE == 1) ++#ifndef CONFIG_MMC ++#define CONFIG_MMC 1 ++#define CONFIG_MMC_WRITE 1 ++#define CONFIG_MMC_QUIRKS 1 ++#define CONFIG_MMC_HW_PARTITIONING 1 ++#define CONFIG_MMC_HS400_ES_SUPPORT 1 ++#define CONFIG_MMC_HS400_SUPPORT 1 ++#define CONFIG_MMC_HS200_SUPPORT 1 ++#define CONFIG_MMC_VERBOSE 1 ++#define CONFIG_MMC_SDHCI 1 ++#define CONFIG_MMC_SDHCI_ADMA 1 ++#endif ++#endif ++ ++/* SD/MMC configuration */ ++ ++#ifdef CONFIG_MMC ++/*#define CONFIG_MMC_SDMA*/ ++#define CONFIG_EMMC ++#define CONFIG_SUPPORT_EMMC_BOOT ++#define CONFIG_GENERIC_MMC ++#define CONFIG_CMD_MMC ++#define CONFIG_SYS_MMC_ENV_DEV 0 ++#define CONFIG_EXT4_SPARSE ++#define CONFIG_SDHCI ++#define CONFIG_BSP_SDHCI ++#define CONFIG_BSP_SDHCI_MAX_FREQ 200000000 ++#define CONFIG_FS_EXT4 ++#define CONFIG_SDHCI_ADMA ++#define CONFIG_SUPPORT_EMMC_RPMB ++#endif ++ ++#if defined(CONFIG_FMC) || defined(CONFIG_FMC_NAND) ++#undef CONFIG_EMMC ++#endif ++ ++ ++#define CONFIG_MISC_INIT_R ++ ++/* Command line configuration */ ++#define CONFIG_MENU ++/* Open it as you need #define CONFIG_CMD_UNZIP */ ++#define CONFIG_CMD_ENV ++ ++#define CONFIG_MTD_PARTITIONS ++ ++/* BOOTP options */ ++#define CONFIG_BOOTP_BOOTFILESIZE ++ ++ ++/* Initial environment variables */ ++ ++/* ++ * Defines where the kernel and FDT will be put in RAM ++ */ ++ ++/* Assume we boot with root on the seventh partition of eMMC */ ++#define CONFIG_BOOTCOMMAND "bootm 0x42000000" ++#define CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS 2 ++#define CONFIG_USB_MAX_CONTROLLER_COUNT 2 ++#define BOOT_TARGET_DEVICES(func) \ ++ func(USB, usb, 0) \ ++func(MMC, mmc, 1) \ ++func(DHCP, dhcp, na) ++#include ++ ++/*allow change env*/ ++#define CONFIG_ENV_OVERWRITE ++ ++#define CONFIG_COMMAND_HISTORY ++ ++/* env in flash instead of CFG_ENV_IS_NOWHERE */ ++ ++#define CONFIG_ENV_VARS_UBOOT_CONFIG ++ ++/* kernel parameter list phy addr */ ++#define CFG_BOOT_PARAMS (CONFIG_SYS_SDRAM_BASE + 0x0100) ++ ++/* Monitor Command Prompt */ ++#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ ++#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ ++ sizeof(CONFIG_SYS_PROMPT) + 16) ++#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE ++#define CONFIG_SYS_MAXARGS 64 /* max command args */ ++ ++#define CONFIG_SYS_NO_FLASH ++ ++#define CONFIG_ARM64_SUPPORT_LOAD_FIP ++ ++#define TEGRA_SMC_GSL_FUNCID 0xc2fffa00 ++ ++#define CONFIG_DDR_TRAINING_V2 ++ ++/* Osd enable */ ++#define CONFIG_OSD_ENABLE ++/* Open it as you need #define CONFIG_CIPHER_ENABLE */ ++/* Open it as you need #define CONFIG_KLAD_ENABLE */ ++/* Open it as you need #define CONFIG_OTP_ENABLE */ ++#define CONFIG_PRODUCTNAME "ss927v100" ++ ++/* Open it as you need #define CONFIG_PCI_CONFIG_HOST_BRIDGE */ ++ ++/*----------------------------------------------------------------------------------- ++ * npu pi defense ++ *-----------------------------------------------------------------------------------*/ ++#define SOC_CRG_BASE_ADDR 0x11010000 ++#define CRG_NPU_CPM_CLK_OFFSET 0x4C84 ++#define CRG_NPU_FFS_CONFIG_OFFSET 0x4E80 ++#define CRG_NPU_FFS_CLK_OFFSET 0x4E84 ++#define CRG_NPU_FFS_STATE_OFFSET 0x4E88 ++#define CRG_NPU_CLK_CTRL_OFFSET 0x6680 ++ ++#define SOC_SYS_BASE_ADDR 0x11020000 ++#define SYS_NPU_VOL_OFFSET 0x9204 ++#define SYS_NPU_CPM_CONFIG_OFFSET 0xB220 ++#define SYS_NPU_POWER_CPM_OFFSET 0xB224 ++#define SYS_NPU_CPM_MA_VAL_OFFSET 0xB230 ++ ++#define SOC_NPU_TOP_BASE_ADDR 0x14000000 ++#define NPU_TOP_DROP_FLAG_OFFSET 0x28 ++ ++#define NPU_CPM_POWER_MAX_VAL 0xFFFF ++#define NPU_CPM_MA_MASK 0x1F ++#define NPU_CPM_POWER_STEP 4 ++ ++#define NPU_CPM_MA_MIN_VAL 21 ++#define NPU_CPM_MA_MIN_MAX 30 ++#define NPU_CPM_THRESHOLD_DIFF 20 ++#define NPU_CPM_THRESHOLD_BIT 12 ++ ++#define NPU_VOL_OFFSET_VAL 70 ++#define NPU_DELAY_TIME_US 10000 ++#define NPU_CRG_DELAY_TIME 4 ++#define NPU_GET_MA_TIMES 5 ++ ++#define NPU_FFS_RESET_VAL 0x00001001 ++#define NPU_FFS_UNRESET_VAL 0x00001000 ++#define NPU_CPM_RESET_VAL 0x00000011 ++#define NPU_CPM_UNRESET_VAL 0x00000010 ++#define NPU_CLK_CTRL_VAL 0x01000010 ++#define NPU_CLK_DISABLE_VAL 0x01000000 ++#define NPU_DROP_FLAG_VAL 0x0 ++#define NPU_FFS_DEF_VAL 0x000c1090 ++#define NPU_FFS_CLK_VAL 0x000c1290 ++#define NPU_CPM_DEF_VAL 0x00800000 ++#define NPU_FFS_STATE_MASK_VAL 0x1000 ++ ++/*----------------------------------------------------------------------------------- ++ * otp user interface register ++ *-----------------------------------------------------------------------------------*/ ++#define REG_BASE_OTP_USER_IF 0x10120000 ++#define OTP_USER_LOCKABLE0 (REG_BASE_OTP_USER_IF + 0x2058) ++#define OTP_SOC_TEE_DISABLE_FLAG 0x42 ++#define NUM_FF 0xff ++#define EXCEPTION_LEVEL1 1 ++#define EXCEPTION_LEVEL2 2 ++#define EXCEPTION_LEVEL3 3 ++ ++#define OTP_REE_CMD_MODE 0 ++#define OTP_TEE_CMD_MODE 1 ++ ++#define OTP_TEE_DISABLE 0 ++#define OTP_TEE_ENABLE 1 ++ ++/*----------------------------------------------------------------------------------- ++ * CA_MISC interface register ++ *-----------------------------------------------------------------------------------*/ ++#define REG_BASE_CA_MISC 0x10115000 ++#define REG_SCS_CTRL (REG_BASE_CA_MISC + 0x400) ++#define SCS_FINISH_FLAG 0x5 ++#define SCS_FINISH_MASK 0xF ++ ++/*----------------------------------------------------------------------------------- ++ * WATCH DOG interface register ++ *-----------------------------------------------------------------------------------*/ ++#define REG_BASE_SOC_MISC 0x11020000 ++#define REG_MISC_CTRL3 (REG_BASE_SOC_MISC + 0x400c) ++#define WATCH_DOG_MODE 0x1 ++#define REG_BASE_WATCH_DOG 0x11030000 ++#define WATCH_DOG_LOAD_VAL 0x30 ++#define WATCH_DOG_CONTROL (REG_BASE_WATCH_DOG + 0x8) ++#define WATCH_DOG_ENABLE (0x3) ++ ++#endif /* __SS927V100_H */ +diff --git a/include/configs/ss928v100.h b/include/configs/ss928v100.h +new file mode 100644 +index 0000000..cc1c186 +--- /dev/null ++++ b/include/configs/ss928v100.h +@@ -0,0 +1,318 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __SS928V100_H ++#define __SS928V100_H ++ ++#include ++#include ++ ++#define CONFIG_REMAKE_ELF ++ ++#define CONFIG_SUPPORT_RAW_INITRD ++ ++#define CONFIG_BOARD_EARLY_INIT_F ++ ++#define CONFIG_TFTP_PORT ++ ++/* Physical Memory Map */ ++ ++/* CONFIG_SYS_TEXT_BASE needs to align with where ATF loads bl33.bin */ ++#define CONFIG_SYS_TEXT_BASE 0x48800000 ++#define CONFIG_SYS_TEXT_BASE_ORI 0x48700000 ++ ++ ++#define PHYS_SDRAM_1 0x40000000 ++#define PHYS_SDRAM_1_SIZE 0x20000000 ++ ++#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 ++ ++#define CONFIG_SYS_INIT_SP_ADDR 0x0402a000 ++ ++#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + 0x80000) ++#define CONFIG_SYS_GBL_DATA_SIZE 128 ++ ++/* Generic Timer Definitions */ ++#define COUNTER_FREQUENCY 24000000 ++ ++#define CONFIG_SYS_TIMER_RATE CFG_TIMER_CLK ++#define CONFIG_SYS_TIMER_COUNTER (CFG_TIMERBASE + REG_TIMER_VALUE) ++#define CONFIG_SYS_TIMER_COUNTS_DOWN ++ ++ ++/* Generic Interrupt Controller Definitions */ ++ ++/* Size of malloc() pool */ ++#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K) ++ ++/* PL011 Serial Configuration */ ++ ++#define CONFIG_PL011_CLOCK 24000000 ++ ++#define CONFIG_PL01x_PORTS \ ++{(void *)UART0_REG_BASE, (void *)UART1_REG_BASE, \ ++ (void *)UART2_REG_BASE, (void *)UART3_REG_BASE} ++ ++#define CONFIG_CUR_UART_BASE UART0_REG_BASE ++ ++#define CONFIG_64BIT ++ ++/*Network configuration*/ ++ ++#define CONFIG_PHY_GIGE ++#ifdef CONFIG_GMACV300_ETH ++#define CONFIG_GMAC_NUMS 1 ++#define CONFIG_GMAC_PHY0_ADDR 1 ++#define CONFIG_GMAC_PHY0_INTERFACE_MODE 2 /* rgmii 2, rmii 1, mii 0 */ ++#define CONFIG_GMAC_PHY1_ADDR 3 ++#define CONFIG_GMAC_PHY1_INTERFACE_MODE 2 /* rgmii 2, rmii 1, mii 0 */ ++#define CONFIG_GMAC_DESC_4_WORD ++#define CONFIG_SYS_FAULT_ECHO_LINK_DOWN 1 ++#endif ++ ++/* Flash Memory Configuration v100 */ ++#ifdef CONFIG_FMC ++#define CONFIG_FMC_REG_BASE FMC_REG_BASE ++#define CONFIG_FMC_BUFFER_BASE FMC_MEM_BASE ++#define CONFIG_FMC_MAX_CS_NUM 1 ++#endif ++ ++#ifdef CONFIG_FMC_SPI_NOR ++#define CONFIG_CMD_SF ++#define CONFIG_SPI_NOR_MAX_CHIP_NUM 1 ++#define CONFIG_SPI_NOR_QUIET_TEST ++#endif ++ ++#ifdef CONFIG_FMC_SPI_NAND ++#define CONFIG_CMD_NAND ++#define CONFIG_SPI_NAND_MAX_CHIP_NUM 1 ++#define CONFIG_SYS_MAX_NAND_DEVICE CONFIG_SPI_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_MAX_CHIPS CONFIG_SPI_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_BASE FMC_MEM_BASE ++#endif ++ ++#ifdef CONFIG_FMC_NAND ++/* #define CONFIG_NAND_EDO_MODE */ ++#define CONFIG_CMD_NAND ++#define CONFIG_NAND_MAX_CHIP_NUM 1 ++#define CONFIG_SYS_MAX_NAND_DEVICE CONFIG_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_MAX_CHIPS CONFIG_NAND_MAX_CHIP_NUM ++#define CONFIG_SYS_NAND_BASE FMC_MEM_BASE ++#endif ++ ++/* the flag for auto update. 1:enable; 0:disable */ ++#define CONFIG_AUTO_UPDATE 0 ++ ++#if (CONFIG_AUTO_UPDATE == 1) ++#define CONFIG_AUTO_UPDATE_ADAPTATION 1 ++#define CONFIG_AUTO_SD_UPDATE 1 ++#define CONFIG_AUTO_USB_UPDATE 1 ++#define CONFIG_FS_FAT 1 ++#define CONFIG_FS_FAT_MAX_CLUSTSIZE 65536 ++#endif ++ ++/*--------------------------------------------------------------------- ++ * sdcard system updae ++e* ---------------------------------------------------------------------*/ ++ ++#if (CONFIG_AUTO_SD_UPDATE == 1) ++#ifndef CONFIG_MMC ++#define CONFIG_MMC 1 ++#define CONFIG_MMC_WRITE 1 ++#define CONFIG_MMC_QUIRKS 1 ++#define CONFIG_MMC_HW_PARTITIONING 1 ++#define CONFIG_MMC_HS400_ES_SUPPORT 1 ++#define CONFIG_MMC_HS400_SUPPORT 1 ++#define CONFIG_MMC_HS200_SUPPORT 1 ++#define CONFIG_MMC_VERBOSE 1 ++#define CONFIG_MMC_SDHCI 1 ++#define CONFIG_MMC_SDHCI_ADMA 1 ++#endif ++#endif ++ ++/* SD/MMC configuration */ ++ ++#ifdef CONFIG_MMC ++/*#define CONFIG_MMC_SDMA*/ ++#define CONFIG_EMMC ++#define CONFIG_SUPPORT_EMMC_BOOT ++#define CONFIG_GENERIC_MMC ++#define CONFIG_CMD_MMC ++#define CONFIG_SYS_MMC_ENV_DEV 0 ++#define CONFIG_EXT4_SPARSE ++#define CONFIG_SDHCI ++#define CONFIG_BSP_SDHCI ++#define CONFIG_BSP_SDHCI_MAX_FREQ 200000000 ++#define CONFIG_FS_EXT4 ++#define CONFIG_SDHCI_ADMA ++#define CONFIG_SUPPORT_EMMC_RPMB ++#endif ++ ++#if defined(CONFIG_FMC) || defined(CONFIG_FMC_NAND) ++#undef CONFIG_EMMC ++#endif ++ ++ ++#define CONFIG_MISC_INIT_R ++ ++/* Command line configuration */ ++#define CONFIG_MENU ++/* Open it as you need #define CONFIG_CMD_UNZIP */ ++#define CONFIG_CMD_ENV ++ ++#define CONFIG_MTD_PARTITIONS ++ ++/* BOOTP options */ ++#define CONFIG_BOOTP_BOOTFILESIZE ++ ++ ++/* Initial environment variables */ ++ ++/* ++ * Defines where the kernel and FDT will be put in RAM ++ */ ++ ++/* Assume we boot with root on the seventh partition of eMMC */ ++#define CONFIG_BOOTCOMMAND "bootm 0x42000000" ++#define CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS 2 ++#define CONFIG_USB_MAX_CONTROLLER_COUNT 2 ++#define BOOT_TARGET_DEVICES(func) \ ++ func(USB, usb, 0) \ ++func(MMC, mmc, 1) \ ++func(DHCP, dhcp, na) ++#include ++ ++/*allow change env*/ ++#define CONFIG_ENV_OVERWRITE ++ ++#define CONFIG_COMMAND_HISTORY ++ ++/* env in flash instead of CFG_ENV_IS_NOWHERE */ ++ ++#define CONFIG_ENV_VARS_UBOOT_CONFIG ++ ++/* kernel parameter list phy addr */ ++#define CFG_BOOT_PARAMS (CONFIG_SYS_SDRAM_BASE + 0x0100) ++ ++/* Monitor Command Prompt */ ++#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ ++#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ ++ sizeof(CONFIG_SYS_PROMPT) + 16) ++#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE ++#define CONFIG_SYS_MAXARGS 64 /* max command args */ ++ ++#define CONFIG_SYS_NO_FLASH ++ ++#define CONFIG_ARM64_SUPPORT_LOAD_FIP ++ ++#define TEGRA_SMC_GSL_FUNCID 0xc2fffa00 ++ ++#define CONFIG_DDR_TRAINING_V2 ++ ++/* Osd enable */ ++#define CONFIG_OSD_ENABLE ++/* Open it as you need #define CONFIG_CIPHER_ENABLE */ ++/* Open it as you need #define CONFIG_KLAD_ENABLE */ ++/* Open it as you need #define CONFIG_OTP_ENABLE */ ++#define CONFIG_PRODUCTNAME "ss928v100" ++ ++/* Open it as you need #define CONFIG_PCI_CONFIG_HOST_BRIDGE */ ++ ++/*----------------------------------------------------------------------------------- ++ * npu pi defense ++ *-----------------------------------------------------------------------------------*/ ++#define SOC_CRG_BASE_ADDR 0x11010000 ++#define CRG_NPU_CPM_CLK_OFFSET 0x4C84 ++#define CRG_NPU_FFS_CONFIG_OFFSET 0x4E80 ++#define CRG_NPU_FFS_CLK_OFFSET 0x4E84 ++#define CRG_NPU_FFS_STATE_OFFSET 0x4E88 ++#define CRG_NPU_CLK_CTRL_OFFSET 0x6680 ++ ++#define SOC_SYS_BASE_ADDR 0x11020000 ++#define SYS_NPU_VOL_OFFSET 0x9204 ++#define SYS_NPU_CPM_CONFIG_OFFSET 0xB220 ++#define SYS_NPU_POWER_CPM_OFFSET 0xB224 ++#define SYS_NPU_CPM_MA_VAL_OFFSET 0xB230 ++ ++#define SOC_NPU_TOP_BASE_ADDR 0x14000000 ++#define NPU_TOP_DROP_FLAG_OFFSET 0x28 ++ ++#define NPU_CPM_POWER_MAX_VAL 0xFFFF ++#define NPU_CPM_MA_MASK 0x1F ++#define NPU_CPM_POWER_STEP 4 ++ ++#define NPU_CPM_MA_MIN_VAL 21 ++#define NPU_CPM_MA_MIN_MAX 30 ++#define NPU_CPM_THRESHOLD_DIFF 20 ++#define NPU_CPM_THRESHOLD_BIT 12 ++ ++#define NPU_VOL_OFFSET_VAL 70 ++#define NPU_DELAY_TIME_US 10000 ++#define NPU_CRG_DELAY_TIME 4 ++#define NPU_GET_MA_TIMES 5 ++ ++#define NPU_FFS_RESET_VAL 0x00001001 ++#define NPU_FFS_UNRESET_VAL 0x00001000 ++#define NPU_CPM_RESET_VAL 0x00000011 ++#define NPU_CPM_UNRESET_VAL 0x00000010 ++#define NPU_CLK_CTRL_VAL 0x01000010 ++#define NPU_CLK_DISABLE_VAL 0x01000000 ++#define NPU_DROP_FLAG_VAL 0x0 ++#define NPU_FFS_DEF_VAL 0x000c1090 ++#define NPU_FFS_CLK_VAL 0x000c1290 ++#define NPU_CPM_DEF_VAL 0x00800000 ++#define NPU_FFS_STATE_MASK_VAL 0x1000 ++ ++/*----------------------------------------------------------------------------------- ++ * otp user interface register ++ *-----------------------------------------------------------------------------------*/ ++#define REG_BASE_OTP_USER_IF 0x10120000 ++#define OTP_USER_LOCKABLE0 (REG_BASE_OTP_USER_IF + 0x2058) ++#define OTP_SOC_TEE_DISABLE_FLAG 0x42 ++#define NUM_FF 0xff ++#define EXCEPTION_LEVEL1 1 ++#define EXCEPTION_LEVEL2 2 ++#define EXCEPTION_LEVEL3 3 ++ ++#define OTP_REE_CMD_MODE 0 ++#define OTP_TEE_CMD_MODE 1 ++ ++#define OTP_TEE_DISABLE 0 ++#define OTP_TEE_ENABLE 1 ++ ++/*----------------------------------------------------------------------------------- ++ * CA_MISC interface register ++ *-----------------------------------------------------------------------------------*/ ++#define REG_BASE_CA_MISC 0x10115000 ++#define REG_SCS_CTRL (REG_BASE_CA_MISC + 0x400) ++#define SCS_FINISH_FLAG 0x5 ++#define SCS_FINISH_MASK 0xF ++ ++/*----------------------------------------------------------------------------------- ++ * WATCH DOG interface register ++ *-----------------------------------------------------------------------------------*/ ++#define REG_BASE_SOC_MISC 0x11020000 ++#define REG_MISC_CTRL3 (REG_BASE_SOC_MISC + 0x400c) ++#define WATCH_DOG_MODE 0x1 ++#define REG_BASE_WATCH_DOG 0x11030000 ++#define WATCH_DOG_LOAD_VAL 0x30 ++#define WATCH_DOG_CONTROL (REG_BASE_WATCH_DOG + 0x8) ++#define WATCH_DOG_ENABLE (0x3) ++ ++#endif /* __SS928V100_H */ +diff --git a/include/cpu_common.h b/include/cpu_common.h +new file mode 100644 +index 0000000..462f4fb +--- /dev/null ++++ b/include/cpu_common.h +@@ -0,0 +1,113 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __CPU_COMMON_H__ ++#define __CPU_COMMON_H__ ++ ++#include ++#include ++ ++#ifdef CONFIG_EMMC ++extern int target_dev; ++extern int target_paratition; ++#endif ++ ++void reset_cpu(ulong addr); ++void uart_early_init(void); ++void uart_early_puts(const char *ss); ++#ifdef CONFIG_BSP_NAND_SPL ++extern uint32_t crc32(uint32_t crc, const char *p, unsigned int len); ++ ++extern unsigned long __bss_start; ++extern unsigned char _blank_zone_end[]; ++extern unsigned long _blank_crc_start; ++#endif ++extern unsigned char input_data[]; ++extern unsigned char input_data_end[]; ++extern unsigned long _armboot_start; ++ ++extern int sdhci_add_port(int index, u32 regbase, u32 freq); ++extern int bsp_mmc_init(int index); ++ ++#if (CONFIG_AUTO_UPDATE == 1) ++extern int do_auto_update(void); ++extern int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]); ++#endif /* CONFIG_AUTO_UPDATE */ ++ ++#ifdef CONFIG_GENERIC_MMC ++extern int mci_probe(int dev_num); ++#endif /* CONFIG_GENERIC_MMC */ ++ ++#ifdef CONFIG_SUPPORT_EMMC_BOOT ++extern int mci_add_port(int index, u32 reg_base, u32 freq); ++#endif /* CONFIG_SUPPORT_EMMC_BOOT */ ++ ++#ifdef CONFIG_AUTO_SD_UPDATE ++extern int mci_add_port(int index, u32 reg_base, u32 freq); ++#endif /* CONFIG_AUTO_SD_UPDATE */ ++ ++extern int mmc_phy_init(void); ++ ++#ifdef CONFIG_CMD_NAND ++extern int nand_saveenv(void); ++extern void nand_env_relocate_spec(void); ++#endif /* CONFIG_CMD_NAND */ ++ ++#ifdef CONFIG_ENV_IS_IN_SPI_FLASH ++extern int sf_saveenv(void); ++extern void sf_env_relocate_spec(void); ++#endif /* CONFIG_ENV_IS_IN_SPI_FLASH */ ++ ++#ifdef CONFIG_ENV_IS_IN_MMC ++extern int emmc_saveenv(void); ++extern void emmc_env_relocate_spec(void); ++#endif /* CONFIG_ENV_IS_IN_MMC */ ++ ++#ifdef CONFIG_ENV_IS_IN_UFS ++extern int ufs_saveenv(void); ++extern void ufs_env_relocate_spec(void); ++#endif /* CONFIG_ENV_IS_IN_UFS */ ++ ++#ifdef CONFIG_HWDEC ++#if (defined CONFIG_SS928V100) || (defined CONFIG_SS927V100) ++extern int hw_dec_decompress(const unsigned char *dst_h32, const unsigned char *dst_l32, ++ int * const dstlen, const unsigned char *src_h32, ++ const unsigned char *src_l32, int srclen, const void *unused); ++#ifndef HW_DECOMPRESS_V2 ++#define HW_DECOMPRESS_V2 ++#endif ++#endif ++ ++extern unsigned int hw_dec_type; ++extern void hw_dec_init(void); ++extern void hw_dec_uinit(void); ++ ++#endif /* CONFIG_HWDEC */ ++ ++#ifdef CONFIG_ARM64_SUPPORT_LOAD_FIP ++extern const void *fdt_getprop(const void *fdt, int nodeoffset, ++ const char *name, int *lenp); ++extern int fdt_next_node(const void *fdt, int offset, int *depth); ++extern const char *fdt_get_name(const void *fdt, int nodeoffset, int *len); ++extern int fdt_check_header(const void *fdt); ++extern int do_load_secure_os(ulong addr, ulong org_offset, ulong img_dst, ++ int run, uint32_t *rtos_load_addr); ++#endif /* CONFIG_ARM64_SUPPORT_LOAD_FIP */ ++#endif /* __CPU_COMMON_H__ */ ++ +diff --git a/include/crc16.h b/include/crc16.h +new file mode 100644 +index 0000000..d99194b +--- /dev/null ++++ b/include/crc16.h +@@ -0,0 +1,25 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef CRC16_H ++#define CRC16_H ++ ++const unsigned short *get_crc16_table(void); ++ ++#endif +diff --git a/include/env_internal.h b/include/env_internal.h +old mode 100644 +new mode 100755 +index 90a4df8..9855027 +--- a/include/env_internal.h ++++ b/include/env_internal.h +@@ -133,6 +133,7 @@ enum env_location { + ENVL_REMOTE, + ENVL_SPI_FLASH, + ENVL_UBI, ++ ENVL_UFS, + ENVL_NOWHERE, + + ENVL_COUNT, +diff --git a/include/flash_read.h b/include/flash_read.h +new file mode 100644 +index 0000000..0bad874 +--- /dev/null ++++ b/include/flash_read.h +@@ -0,0 +1,43 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++ ++#ifndef __FLASH_READ_H ++#define __FLASH_READ_H ++ ++#define EMMC_BLOCK_SHIFT 9 /* change it if MMC_MAX_BLOCK_LEN changed */ ++#define FLASH_ALIGNED_SIZE (1 << EMMC_BLOCK_SHIFT) /* 512 Byte */ ++ ++/* ++ * read flash into out_addr from offset. ++ * note: ++ * 1. offset should aligned with FLASH_ALIGNED_SIZE ++ * 2. if size is not aligned with FLASH_ALIGNED_SIZE, ++ * system will read (((size / FLASH_ALIGNED_SIZE) + 1) * FLASH_ALIGNED_SIZE) content to out_addr ++ */ ++int flash_read(unsigned long offset, unsigned int size, unsigned char *out_addr); ++ ++/* ++ * read flash into out_addr from offset. ++ * note: ++ * 1. offset and size should aligned with FLASH_ALIGNED_SIZE ++ */ ++int flash_read_aligned(unsigned long offset, unsigned int size, unsigned char *out_addr); ++ ++#endif +diff --git a/include/fmc_common.h b/include/fmc_common.h +new file mode 100644 +index 0000000..d72890b +--- /dev/null ++++ b/include/fmc_common.h +@@ -0,0 +1,507 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#ifndef __FMC_COMMON_H__ ++#define __FMC_COMMON_H__ ++ ++#include "securec.h" ++ ++#define _32B 32 ++#define _64B 64 ++#define _128B 128 ++#define _218B 218 ++#define _224B 224 ++#define _232B 232 ++#define _256B 256 ++#define _448B 448 ++#define _512B 512 ++#define _640B 640 ++#define _744B 744 ++#define _1K 1024 ++#define _1280B 1280 ++#define _2K 2048 ++#define _4K 4096 ++#define _8K 8192 ++#define _16K 16384 ++#define _32K 32768 ++#define _64K 0x10000UL ++#define _128K 0x20000UL ++#define _256K 0x40000UL ++#define _512K 0x80000UL ++#define _1M 0x100000UL ++#define _2M 0x200000UL ++#define _4M 0x400000UL ++#define _8M 0x800000UL ++#define _16M 0x1000000UL ++#define _32M 0x2000000UL ++#define _64M 0x4000000UL ++#define _128M 0x8000000UL ++#define _256M 0x10000000UL ++#define _512M 0x20000000UL ++#define _1G 0x40000000ULL ++#define _2G 0x80000000ULL ++#define _4G 0x100000000ULL ++#define _8G 0x200000000ULL ++#define _16G 0x400000000ULL ++#define _64G 0x1000000000ULL ++ ++#define ECC_TYPE_0BIT 0x0 ++#define ECC_TYPE_8BIT 0x1 ++#define ECC_TYPE_16BIT 0x2 ++#define ECC_TYPE_24BIT 0x3 ++#define ECC_TYPE_28BIT 0x4 ++#define ECC_TYPE_40BIT 0x5 ++#define ECC_TYPE_64BIT 0x6 ++ ++#define PAGE_SIZE_2KB 0x0 ++#define PAGE_SIZE_4KB 0x1 ++#define PAGE_SIZE_8KB 0x2 ++#define PAGE_SIZE_16KB 0x3 ++ ++/* id len */ ++#define _2B 2 ++#define _3B 3 ++#define _4B 4 ++#define _5B 5 ++#define _6B 6 ++#define _7B 7 ++#define _8B 8 ++#define _9B 9 ++#define _10B 10 ++#define _11B 11 ++#define _12B 12 ++ ++#ifdef CONFIG_FMC ++ ++/* FMC REG */ ++#define FMC_CFG 0x00 ++#define fmc_cfg_spi_nand_sel(_type) (((_size) & 0x3) << 11) ++#define fmc_cfg_spi_nor_addr_mode(_mode) ((_mode) << 10) ++#define fmc_cfg_block_size(_size) (((_size) & 0x3) << 8) ++#define fmc_cfg_ecc_type(_type) (((_type) & 0x7) << 5) ++#define fmc_cfg_page_size(_size) (((_size) & 0x3) << 3) ++#define fmc_cfg_flash_sel(_type) (((_type) & 0x3) << 1) ++#define fmc_cfg_op_mode(_mode) ((_mode) & 0x1) ++ ++#define SPI_NAND_MFR_OTHER 0x0 ++ ++#define SPI_NAND_SEL_SHIFT 11 ++#define SPI_NAND_SEL_MASK (0x3 << SPI_NAND_SEL_SHIFT) ++ ++#define SPI_NOR_ADDR_MODE_3_BYTES 0x0 ++#define SPI_NOR_ADDR_MODE_4_BYTES 0x1 ++ ++#define SPI_NOR_ADDR_MODE_SHIFT 10 ++#define SPI_NOR_ADDR_MODE_MASK (0x1 << SPI_NOR_ADDR_MODE_SHIFT) ++ ++#define BLOCK_SIZE_64_PAGE 0x0 ++#define BLOCK_SIZE_128_PAGE 0x1 ++#define BLOCK_SIZE_256_PAGE 0x2 ++#define BLOCK_SIZE_512_PAGE 0x3 ++ ++#define _64_PAGES 64 ++#define _128_PAGES 128 ++#define _256_PAGES 256 ++#define _512_PAGES 512 ++ ++#define EB_NORMAL 28 ++#define EB_2K_16_BIT 4 ++#define EB_4K_16_BIT 12 ++ ++#define BLOCK_SIZE_MASK (0x3 << 8) ++ ++#define ECC_TYPE_SHIFT 5 ++#define ECC_TYPE_MASK (0x7 << ECC_TYPE_SHIFT) ++ ++#define PAGE_SIZE_SHIFT 3 ++#define PAGE_SIZE_MASK (0x3 << PAGE_SIZE_SHIFT) ++ ++#define FLASH_TYPE_SPI_NOR 0x0 ++#define FLASH_TYPE_SPI_NAND 0x1 ++#define FLASH_TYPE_NAND 0x2 ++#define FLASH_TYPE_DEFAULT 0x3 ++ ++#define FLASH_SEL_SHIFT 1 ++#define FLASH_SEL_MASK (0x3 << FLASH_SEL_SHIFT) ++ ++#define OP_MODE_BOOT 0x0 ++#define OP_MODE_NORMAL 0x1 ++ ++#define OP_MODE_MASK 0x1 ++ ++#define FMC_GLOBAL_CFG 0x04 ++#define FMC_GLOBAL_CFG_WP_ENABLE (1 << 6) ++#define FMC_GLOBAL_CFG_RANDOMIZER_EN (1 << 2) ++ ++#define FMC_SPI_TIMING_CFG 0x08 ++#define timing_cfg_tcsh(_n) (((_n) & 0xf) << 8) ++#define timing_cfg_tcss(_n) (((_n) & 0xf) << 4) ++#define timing_cfg_tshsl(_n) ((_n) & 0xf) ++ ++#define CS_HOLD_TIME 0x6 ++#define CS_SETUP_TIME 0x6 ++#define CS_DESELECT_TIME 0xf ++ ++#define FMC_PND_PWIDTH_CFG 0x0c ++#define pwidth_cfg_rw_hcnt(_n) (((_n) & 0xf) << 8) ++#define pwidth_cfg_r_lcnt(_n) (((_n) & 0xf) << 4) ++#define pwidth_cfg_w_lcnt(_n) ((_n) & 0xf) ++ ++#ifdef CONFIG_NAND_EDO_MODE ++#define RW_H_WIDTH 0x3 ++#define R_L_WIDTH 0x2 ++#define W_L_WIDTH 0x2 ++#define NAND_EDO_MODE_SHIFT 9 ++#define NAND_EDO_MODE_MASK (1<> 20) ++ ++#define FMC_DMA_AHB_CTRL 0x48 ++#define FMC_DMA_AHB_CTRL_DMA_PP_EN (1 << 3) ++#define FMC_DMA_AHB_CTRL_BURST16_EN (1 << 2) ++#define FMC_DMA_AHB_CTRL_BURST8_EN (1 << 1) ++#define FMC_DMA_AHB_CTRL_BURST4_EN 1 ++ ++#define ALL_BURST_ENABLE (FMC_DMA_AHB_CTRL_BURST16_EN \ ++ | FMC_DMA_AHB_CTRL_BURST8_EN \ ++ | FMC_DMA_AHB_CTRL_BURST4_EN) ++ ++#define FMC_DMA_ADDR_OFFSET 4096 ++ ++#define FMC_DMA_SADDR_D0 0x4c ++ ++#define FMC_DMA_SADDR_D1 0x50 ++ ++#define FMC_DMA_SADDR_D2 0x54 ++ ++#define FMC_DMA_SADDR_D3 0x58 ++ ++#define FMC_DMA_SADDR_OOB 0x5c ++ ++#define FMC_DMA_SADDRH_D0 0x200 ++#define FMC_DMA_SADDRH_SHIFT 0x3LL ++#define FMC_DMA_SADDRH_MASK (FMC_DMA_SADDRH_SHIFT << 32) ++ ++#define FMC_DMA_SADDRH_OOB 0x210 ++ ++#define FMC_DMA_BLK_SADDR 0x60 ++#define fmc_dma_blk_saddr_set(_addr) ((_addr) & 0xffffff) ++ ++#define FMC_DMA_BLK_LEN 0x64 ++#define fmc_dma_blk_len_set(_len) ((_len) & 0xffff) ++ ++#define FMC_OP_CTRL 0x68 ++#define op_ctrl_rd_opcode(_code) (((_code) & 0xff) << 16) ++#define op_ctrl_wr_opcode(_code) (((_code) & 0xff) << 8) ++#define op_ctrl_rd_op_sel(_op) (((_op) & 0x3) << 4) ++#define op_ctrl_dma_op(_type) ((_type) << 2) ++#define op_ctrl_rw_op(_op) ((_op) << 1) ++#define OP_CTRL_DMA_OP_READY 1 ++ ++#define RD_OP_READ_ALL_PAGE 0x0 ++#define RD_OP_READ_OOB 0x1 ++#define RD_OP_BLOCK_READ 0x2 ++ ++#define RD_OP_SHIFT 4 ++#define RD_OP_MASK (0x3 << RD_OP_SHIFT) ++ ++#define OP_TYPE_DMA 0x0 ++#define OP_TYPE_REG 0x1 ++ ++#define RW_OP_READ 0x0 ++#define RW_OP_WRITE 0x1 ++ ++#define FMC_OP_PARA 0x70 ++#define FMC_OP_PARA_RD_OOB_ONLY (1 << 1) ++ ++#define FMC_BOOT_SET 0x74 ++#define FMC_BOOT_SET_DEVICE_ECC_EN (1 << 3) ++#define FMC_BOOT_SET_BOOT_QUAD_EN (1 << 1) ++ ++#define FMC_STATUS 0xac ++ ++#define GET_OP 0 ++#define SET_OP 1 ++ ++#define STATUS_ECC_MASK (0x3 << 4) ++#define STATUS_P_FAIL_MASK (1 << 3) ++#define STATUS_E_FAIL_MASK (1 << 2) ++#define STATUS_WEL_MASK (1 << 1) ++#define STATUS_OIP_MASK (1 << 0) ++ ++#define FMC_VERSION 0xbc ++ ++/* FMC IP version */ ++#define FMC_VER_100 0x100 ++ ++#endif /* End of CONFIG_FMC */ ++ ++#define DISABLE 0 ++#define ENABLE 1 ++ ++/* DMA address align with 32 bytes. */ ++#define FMC_DMA_ALIGN 32 ++ ++#define FMC_CHIP_DELAY 25 ++ ++#define TMP_BUF_LEN 128 ++ ++#define fmc_read(_host, _reg) \ ++ readl((uintptr_t)((char *)_host->regbase + (_reg))) ++ ++#define fmc_write(_host, _reg, _value) \ ++ writel((u_int)(_value), (uintptr_t)((char *)_host->regbase + (_reg))) ++ ++#define get_page_index(host) \ ++ ((host->addr_value[0] >> 16) | (host->addr_value[1] << 16)) ++ ++#define db_msg(_fmt, arg...) \ ++ printf("%s(%d): " _fmt, __func__, __LINE__, ##arg); ++ ++#define db_bug(fmt, args...) \ ++ do { \ ++ printf("%s(%d): BUG: " fmt, __FILE__, __LINE__, ##args); \ ++ while (1); \ ++ } while (0) ++ ++#define FMC_INFO 1 ++ ++#define BT_DBG 0 /* Boot init debug print */ ++#define ER_DBG 0 /* Erase debug print */ ++#define WR_DBG 0 /* Write debug print */ ++#define RD_DBG 0 /* Read debug print */ ++#define QE_DBG 0 /* Quad Enable debug print */ ++#define OP_DBG 0 /* OP command debug print */ ++#define DMA_DB 0 /* DMA read or write debug print */ ++#define AC_DBG 0 /* 3-4byte Address Cycle */ ++#define SR_DBG 0 /* Status Register debug print */ ++#define CR_DBG 0 /* Config Register debug print */ ++#define FT_DBG 0 /* Features debug print */ ++#define WE_DBG 0 /* Write Enable debug print */ ++#define BP_DBG 0 /* Block Protection debug print */ ++#define EC_DBG 0 /* enable/disable ecc0 and randomizer */ ++#define DTR_DB 0 /* 4DTR debug print */ ++#define RST_DB 0 /* enable/disable reset pin */ ++#define REG_DB 0 /* operation debug print */ ++ ++#define fmc_pr(_type, _fmt, arg...) \ ++ do { \ ++ if (_type) \ ++ db_msg(_fmt, ##arg) \ ++ } while (0) ++ ++#define FMC_WAIT_TIMEOUT 400000 /* 4s equals 400000*10us */ ++ ++#define fmc_cmd_wait_cpu_finish(_host) \ ++ do { \ ++ unsigned regval, timeout = FMC_WAIT_TIMEOUT; \ ++ do { \ ++ udelay(10); \ ++ regval = fmc_read((_host), FMC_OP); \ ++ --timeout; \ ++ } while ((regval & FMC_OP_REG_OP_START) && timeout); \ ++ if (!timeout) \ ++ db_msg("Error: Wait cmd cpu finish timeout!\n"); \ ++ } while (0) ++ ++#define fmc_dma_wait_int_finish(_host) \ ++ do { \ ++ unsigned regval, timeout = FMC_WAIT_TIMEOUT; \ ++ do { \ ++ udelay(10); \ ++ regval = fmc_read((_host), FMC_INT); \ ++ --timeout; \ ++ } while ((!(regval & FMC_INT_OP_DONE) && timeout)); \ ++ if (!timeout) { \ ++ debug_register_dump(); \ ++ db_msg("Error: Wait dma int finish timeout!\n"); \ ++ } \ ++ } while (0) ++ ++#define fmc_dma_wait_cpu_finish(_host) \ ++ do { \ ++ unsigned regval, timeout = FMC_WAIT_TIMEOUT; \ ++ do { \ ++ udelay(10); \ ++ regval = fmc_read((_host), FMC_OP_CTRL); \ ++ --timeout; \ ++ } while ((regval & OP_CTRL_DMA_OP_READY) && timeout); \ ++ if (!timeout) { \ ++ debug_register_dump(); \ ++ db_msg("Error: Wait dma cpu finish timeout!\n"); \ ++ } \ ++ } while (0) ++ ++#define clk_2x(_clk) (((_clk) + 1) >> 1) ++#define clk_4x(_clk) (((_clk) + 1) >> 2) ++ ++enum OP { ++ READ = 1, ++ WRITE, ++ ERASE, ++}; ++ ++struct fmc_cmd_op { ++ unsigned char cs; ++ unsigned char cmd; ++ unsigned char l_cmd; ++ unsigned char addr_h; ++ unsigned int addr_l; ++ unsigned int data_no; ++ unsigned short option; ++ unsigned short op_cfg; ++}; ++ ++char *ulltostr(unsigned long long size); ++ ++void debug_register_dump(void); ++ ++int fmc_ip_ver_check(void); ++ ++void fmc_dev_type_switch(unsigned char type); ++ ++void *get_fmc_ip(void); ++ ++unsigned char *get_cs_number(unsigned char cs); ++ ++unsigned int get_fmc_boot_mode (void); ++ ++#endif /* End of __FMC_COMMON_H__ */ +diff --git a/include/hw_decompress.h b/include/hw_decompress.h +new file mode 100644 +index 0000000..8efbdc2 +--- /dev/null ++++ b/include/hw_decompress.h +@@ -0,0 +1,47 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#ifndef HW_DECOMPRESS_H ++#define HW_DECOMPRESS_H ++ ++#define GZIP_MAX_LEN 0xffffff /* (16MB - 1Byte) */ ++ ++typedef enum { ++ HW_DECOMPRESS_OP_START = 0, /* decompress operation start */ ++ HW_DECOMPRESS_OP_CONTINUE, /* decompress operation continue */ ++ HW_DECOMPRESS_OP_END, /* decompress operation end */ ++ HW_DECOMPRESS_OP_ONCE, /* decompress operation just once */ ++} hw_decompress_op_type; ++ ++extern unsigned int hw_dec_type; ++void hw_dec_init(void); ++void hw_dec_uinit(void); ++ ++/* ++ * Support decompressing gzip file by sections ++ * op_type: operation type ++ * dst: addr of uncompressed file ++ * dstlen: len of uncompressed file ++ * src: addr of compressed file ++ * srclen: len of compressed file ++ * ++ * note: only support v1 version now for sections feature ++ */ ++int hw_dec_decompress_ex(hw_decompress_op_type op_type, const unsigned char *dst, ++ int *dstlen, const unsigned char *src, int srclen); ++#endif +diff --git a/include/image.h b/include/image.h +index f4d2aaf..da3b8c3 100644 +--- a/include/image.h ++++ b/include/image.h +@@ -922,12 +922,18 @@ int booti_setup(ulong image, ulong *relocated_addr, ulong *size, + #define FIT_IMAGES_PATH "/images" + #define FIT_CONFS_PATH "/configurations" + +-/* hash/signature node */ ++/* hash/signature/key node */ + #define FIT_HASH_NODENAME "hash" + #define FIT_ALGO_PROP "algo" + #define FIT_VALUE_PROP "value" + #define FIT_IGNORE_PROP "uboot-ignore" + #define FIT_SIG_NODENAME "signature" ++#define FIT_KEY_REQUIRED "required" ++#define FIT_KEY_HINT "key-name-hint" ++ ++/* cipher node */ ++#define FIT_CIPHER_NODENAME "cipher" ++#define FIT_ALGO_PROP "algo" + + /* image node */ + #define FIT_DATA_PROP "data" +@@ -1018,6 +1024,8 @@ int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset); + int fit_image_get_data_position(const void *fit, int noffset, + int *data_position); + int fit_image_get_data_size(const void *fit, int noffset, int *data_size); ++int fit_image_get_data_size_unciphered(const void *fit, int noffset, ++ size_t *data_size); + int fit_image_get_data_and_size(const void *fit, int noffset, + const void **data, size_t *size); + +@@ -1027,6 +1035,10 @@ int fit_image_hash_get_value(const void *fit, int noffset, uint8_t **value, + + int fit_set_timestamp(void *fit, int noffset, time_t timestamp); + ++int fit_cipher_data(const char *keydir, void *keydest, void *fit, ++ const char *comment, int require_keys, ++ const char *engine_id, const char *cmdname); ++ + /** + * fit_add_verification_data() - add verification data to FIT image nodes + * +@@ -1057,6 +1069,7 @@ int fit_image_verify_with_data(const void *fit, int image_noffset, + int fit_image_verify(const void *fit, int noffset); + int fit_config_verify(const void *fit, int conf_noffset); + int fit_all_image_verify(const void *fit); ++int fit_config_decrypt(const void *fit, int conf_noffset); + int fit_image_check_os(const void *fit, int noffset, uint8_t os); + int fit_image_check_arch(const void *fit, int noffset, uint8_t arch); + int fit_image_check_type(const void *fit, int noffset, uint8_t type); +@@ -1064,7 +1077,27 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp); + int fit_check_format(const void *fit); + + int fit_conf_find_compat(const void *fit, const void *fdt); ++ ++/** ++ * fit_conf_get_node - get node offset for configuration of a given unit name ++ * @fit: pointer to the FIT format image header ++ * @conf_uname: configuration node unit name (NULL to use default) ++ * ++ * fit_conf_get_node() finds a configuration (within the '/configurations' ++ * parent node) of a provided unit name. If configuration is found its node ++ * offset is returned to the caller. ++ * ++ * When NULL is provided in second argument fit_conf_get_node() will search ++ * for a default configuration node instead. Default configuration node unit ++ * name is retrieved from FIT_DEFAULT_PROP property of the '/configurations' ++ * node. ++ * ++ * returns: ++ * configuration node offset when found (>=0) ++ * negative number on failure (FDT_ERR_* code) ++ */ + int fit_conf_get_node(const void *fit, const char *conf_uname); ++ + int fit_conf_get_prop_node_count(const void *fit, int noffset, + const char *prop_name); + int fit_conf_get_prop_node_index(const void *fit, int noffset, +@@ -1137,6 +1170,7 @@ struct image_sign_info { + const char *require_keys; /* Value for 'required' property */ + const char *engine_id; /* Engine to use for signing */ + }; ++ + #endif /* Allow struct image_region to always be defined for rsa.h */ + + /* A part of an image, used for hashing */ +@@ -1283,6 +1317,11 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, + int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp); + ++int fit_image_decrypt_data(const void *fit, ++ int image_noffset, int cipher_noffset, ++ const void *data, size_t size, ++ void **data_unciphered, size_t *size_unciphered); ++ + /** + * fit_region_make_list() - Make a list of regions to hash + * +@@ -1309,6 +1348,64 @@ static inline int fit_image_check_target_arch(const void *fdt, int node) + #endif + } + ++/* ++ * At present we only support ciphering on the host, and unciphering on the ++ * device ++ */ ++#if defined(USE_HOSTCC) ++# if defined(CONFIG_FIT_CIPHER) ++# define IMAGE_ENABLE_ENCRYPT 1 ++# define IMAGE_ENABLE_DECRYPT 1 ++# include ++# else ++# define IMAGE_ENABLE_ENCRYPT 0 ++# define IMAGE_ENABLE_DECRYPT 0 ++# endif ++#else ++# define IMAGE_ENABLE_ENCRYPT 0 ++# define IMAGE_ENABLE_DECRYPT CONFIG_IS_ENABLED(FIT_CIPHER) ++#endif ++ ++/* Information passed to the ciphering routines */ ++struct image_cipher_info { ++ const char *keydir; /* Directory containing keys */ ++ const char *keyname; /* Name of key to use */ ++ const char *ivname; /* Name of IV to use */ ++ const void *fit; /* Pointer to FIT blob */ ++ int node_noffset; /* Offset of the cipher node */ ++ const char *name; /* Algorithm name */ ++ struct cipher_algo *cipher; /* Cipher algorithm information */ ++ const void *fdt_blob; /* FDT containing key and IV */ ++ const void *key; /* Value of the key */ ++ const void *iv; /* Value of the IV */ ++ size_t size_unciphered; /* Size of the unciphered data */ ++}; ++ ++struct cipher_algo { ++ const char *name; /* Name of algorithm */ ++ int key_len; /* Length of the key */ ++ int iv_len; /* Length of the IV */ ++ ++#if IMAGE_ENABLE_ENCRYPT ++ const EVP_CIPHER * (*calculate_type)(void); ++#endif ++ ++ int (*encrypt)(struct image_cipher_info *info, ++ const unsigned char *data, int data_len, ++ unsigned char **cipher, int *cipher_len); ++ ++ int (*add_cipher_data)(struct image_cipher_info *info, ++ void *keydest); ++ ++ int (*decrypt)(struct image_cipher_info *info, ++ const void *cipher, size_t cipher_len, ++ void **data, size_t *data_len); ++}; ++ ++int fit_image_cipher_get_algo(const void *fit, int noffset, char **algo); ++ ++struct cipher_algo *image_get_cipher_algo(const char *full_name); ++ + #ifdef CONFIG_FIT_VERBOSE + #define fit_unsupported(msg) printf("! %s:%d " \ + "FIT images not supported for '%s'\n", \ +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index ceffd99..ecc4d9c 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -445,6 +445,29 @@ int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs); + int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs); + int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs); + ++/* ++ * this interface for iTools and application used. ++ */ ++struct mtd_info_ex ++{ ++ u_char type; /* chip type MTD_NORFLASH / MTD_NANDFLASH */ ++ uint64_t chipsize; /* total size of the nand/spi chip */ ++ uint32_t erasesize; ++ uint32_t pagesize; ++ uint32_t numchips; /* number of nand chips */ ++ ++ uint32_t oobsize; ++ uint32_t addrcycle; ++ uint32_t ecctype; ++ ++ u_char ids[8]; ++ uint32_t id_length; ++ char name[16]; /* chip names */ ++ int hostver; /* host controller version */ ++}; ++ ++extern struct mtd_info_ex * get_nand_info(void); ++extern struct mtd_info_ex * get_spiflash_info(void); + #ifndef __UBOOT__ + static inline int mtd_suspend(struct mtd_info *mtd) + { +diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h +index bd373b9..33a39b9 100644 +--- a/include/linux/mtd/rawnand.h ++++ b/include/linux/mtd/rawnand.h +@@ -944,7 +944,7 @@ struct nand_chip { + int jedec_version; + struct nand_onfi_params onfi_params; + struct nand_jedec_params jedec_params; +- ++ + struct nand_data_interface *data_interface; + + int read_retries; +@@ -1329,4 +1329,18 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, + int nand_write_data_op(struct nand_chip *chip, const void *buf, + unsigned int len, bool force_8bit); + ++static inline char *get_ecctype_str(int ecctype) ++{ ++#if defined(CONFIG_FMC_SPI_NAND) ++ static char *ecctype_string[] = { ++ "None", "1bit/512Byte", "4bits/512Byte", "8bits/512Byte", ++ "24bits/1K", "unknown", "40bits/1K", "unknown"}; ++ return ecctype_string[(ecctype + 1) / 2]; ++#else ++ static char *ecctype_string[] = { ++ "None", "1bit/512Byte", "4bits/512Byte", "8bits/512Byte", ++ "24bits/1K", "40bits/1K", "unknown", "unknown"}; ++ return ecctype_string[((unsigned int)ecctype & 0x0F)]; ++#endif ++} + #endif /* __LINUX_MTD_RAWNAND_H */ +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index f9964a7..b54f160 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -328,6 +328,12 @@ struct spi_nor { + size_t len, const u_char *write_buf); + int (*erase)(struct spi_nor *nor, loff_t offs); + ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++ unsigned int bp_level_max; ++ ++ void (*lock)(unsigned char cmp, unsigned char level, ++ unsigned char op); ++#endif + int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); + int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); + int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); +diff --git a/include/match_table.h b/include/match_table.h +new file mode 100644 +index 0000000..139025b +--- /dev/null ++++ b/include/match_table.h +@@ -0,0 +1,63 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __MATCH_TABLE_H__ ++#define __MATCH_TABLE_H__ ++ ++struct match_reg_type { ++ int reg; ++ int type; ++}; ++ ++struct match_type_str { ++ int type; ++ const char *str; ++}; ++ ++struct match_t { ++ int type; ++ int reg; ++ void *data; ++}; ++ ++#define match_set_type_reg(_type, _reg) {(_type), (_reg), (void *)0} ++#define match_set_type_data(_type, _data) {(_type), 0, (void *)(_data)} ++#define match_set(_type, _reg, _data) {(_type), (_reg), (void *)(_data)} ++ ++int reg2type(const struct match_reg_type *table, int length, int reg, int def); ++ ++int type2reg(const struct match_reg_type *table, int length, int type, int def); ++ ++int str2type(const struct match_type_str *table, int length, const char *str, ++ int size, int def); ++ ++const char *type2str(const struct match_type_str *table, int length, int type, ++ const char *def); ++ ++int match_reg_to_type(const struct match_t *table, int nr_table, int reg, int def); ++ ++int match_type_to_reg(const struct match_t *table, int nr_table, int type, int def); ++ ++int match_data_to_type(const struct match_t *table, int nr_table, const char *data, ++ int size, int def); ++ ++void *match_type_to_data(const struct match_t *table, int nr_table, int type, ++ void *def); ++ ++#endif /* End of __MATCH_TABLE_H__ */ +diff --git a/include/mci_reg.h b/include/mci_reg.h +new file mode 100644 +index 0000000..602f00f +--- /dev/null ++++ b/include/mci_reg.h +@@ -0,0 +1,238 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef _MCI_REG_H_ ++#define _MCI_REG_H_ ++#include ++ ++#define MCI_CTRL 0x00 ++#define MCI_PWREN 0x04 ++#define MCI_CLKDIV 0x08 ++#define MCI_CLKSRC 0x0C ++#define MCI_CLKENA 0x10 ++#define MCI_TIMEOUT 0x14 ++#define MCI_CTYPE 0x18 ++#define MCI_BLKSIZ 0x1c ++#define MCI_BYTCNT 0x20 ++#define MCI_INTMASK 0x24 ++#define MCI_CMDARG 0x28 ++#define MCI_CMD 0x2C ++#define MCI_RESP0 0x30 ++#define MCI_RESP1 0x34 ++#define MCI_RESP2 0x38 ++#define MCI_RESP3 0x3C ++#define MCI_MINTSTS 0x40 ++#define MCI_RINTSTS 0x44 ++#define MCI_STATUS 0x48 ++#define MCI_FIFOTH 0x4C ++#define MCI_CDETECT 0x50 ++#define MCI_WRTPRT 0x54 ++#define MCI_GPIO 0x58 ++#define MCI_TCBCNT 0x5C ++#define MCI_TBBCNT 0x60 ++#define MCI_DEBNCE 0x64 ++#define MCI_USRID 0x68 ++#define MCI_VERID 0x6C ++#define MCI_HCON 0x70 ++#define MCI_UHS_REG 0x74 ++#define MCI_RESET_N 0x78 ++#define MCI_BMOD 0x80 ++#define MCI_DBADDR 0x88 ++#define MCI_IDSTS 0x8C ++#define MCI_IDINTEN 0x90 ++#define MCI_DSCADDR 0x94 ++#define MCI_BUFADDR 0x98 ++#define MMC_CARDTHRCTL 0x100 ++#define MCI_UHS_REG_EXT 0x108 ++ ++/* MCI_UHS_REG_EXT(0x108) details */ ++/* bit[19:16] sampling phase */ ++#define CLK_SMPL_PHS_SHIFT 16 ++#define CLK_SMPL_PHS_MASK (0x7<<16) ++#define CLK_DRV_PHS_MASK (0x7<<23) ++ ++#define MCI_TUNING_CTRL 0x118 ++ ++/* MCI_TUNING_CTRL(0x118) details */ ++#define HW_TUNING_EN (0x1 << 0) ++#define EDGE_CTRL (0x1 << 1) ++#define FOUND_EDGE (0x1 << 5) ++ ++#define MCI_FIFO_START 0x200 ++ ++/* IDMAC DEST1 details */ ++#define DMA_BUFFER 0x2000 ++#define MAX_DMA_DES 20480 ++ ++/* IDMAC DEST0 details */ ++#define DMA_DES_OWN (0x1<<31) ++#define DMA_DES_NEXT_DES (0x1<<4) ++#define DMA_DES_FIRST_DES (0x1<<3) ++#define DMA_DES_LAST_DES (0x1<<2) ++ ++/* MCI_CTRL(0x00) details */ ++#define USE_INTERNAL_DMA (0x1<<25) ++#define INTR_EN (0x1<<4) ++#define DMA_RESET (0x1<<2) ++#define FIFO_RESET (0x1<<1) ++#define CTRL_RESET (0x1<<0) ++ ++/* MCI_CLKENA(0x10) details */ ++/* bit 0: enable of card clk */ ++#define CCLK_ENABLE (0x1<<0) ++ ++/* MCI_TIMEOUT(0x14) details: */ ++#define DATA_TIMEOUT (0xffffff<<8) /* bit 31-8: data read timeout param */ ++#define RESPONSE_TIMEOUT 0xff /* bit 7-0: response timeout param */ ++ ++/* MCI_CTYPE(0x18) details */ ++#define CARD_WIDTH_MASK 0x10001UL ++#define CARD_WIDTH_8BIT 0x10000UL ++#define CARD_WIDTH_4BIT 0x01UL ++#define CARD_WIDTH_1BIT 0x00UL ++ ++/* MCI_INTMASK(0x24) details: ++ bit 16-1: mask MMC host controller each interrupt ++*/ ++#define ALL_INT_MASK 0x1fffe ++ ++/* MCI_CMD(0x2c) details: ++ bit 31: cmd execute or load start param of interface clk bit ++*/ ++#define MCI_CMD_MASK 0x803FFFFFUL ++#define START_CMD (0x1<<31) ++#define USE_HOLD_REG (0x1<<29) ++#define DISABLE_BOOT (0x1<<26) ++#define ENABLE_BOOT (0x1<<24) ++#define UP_CLK_ONLY (0x1<<21) ++#define CARD_NUM (0x1<<16) ++#define SEND_INIT (0x1<<15) ++#define STOP_ABORT_CMD (0x1<<14) ++#define WT_PD_CPT (0x1<<13) ++#define SEND_AUTO_STOP (0x1<<12) ++#define DATA_EXPECT (0x1<<9) ++#define CHECK_RESP_CRC (0x1<<8) ++#define RESP_LENGTH (0x1<<7) ++#define RESP_EXPECT (0x1<<6) ++ ++/* MCI_INTSTS(0x44) details */ ++/* ************************************************************* */ ++/* bit 16: sdio interrupt status */ ++#define SDIO_INT_STATUS (0x1<<16) ++ ++/* bit 15: end-bit error (read)/write no CRC interrupt status */ ++#define EBE_INT_STATUS (0x1<<15) ++ ++/* bit 14: auto command done interrupt status */ ++#define ACD_INT_STATUS (0x1<<14) ++ ++/* bit 13: start bit error interrupt status */ ++#define SBE_INT_STATUS (0x1<<13) ++ ++/* bit 12: hardware locked write error interrupt status */ ++#define HLE_INT_STATUS (0x1<<12) ++ ++/* bit 11: FIFO underrun/overrun error interrupt status */ ++#define FRUN_INT_STATUS (0x1<<11) ++ ++/* bit 10: data starvation-by-host timeout interrupt status */ ++#define HTO_INT_STATUS (0x1<<10) ++ ++/* bit 9: data read timeout interrupt status */ ++#define DRTO_INT_STATUS (0x1<<9) ++#define BDS_INT_STATUS (0x1<<9) ++ ++/* bit 8: response timeout interrupt status */ ++#define RTO_INT_STATUS (0x1<<8) ++ ++/* bit 7: data CRC error interrupt status */ ++#define DCRC_INT_STATUS (0x1<<7) ++ ++/* bit 6: response CRC error interrupt status */ ++#define RCRC_INT_STATUS (0x1<<6) ++ ++/* bit 5: receive FIFO data request interrupt status */ ++#define RXDR_INT_STATUS (0x1<<5) ++ ++/* bit 4: transmit FIFO data request interrupt status */ ++#define TXDR_INT_STATUS (0x1<<4) ++ ++/* bit 3: data transfer Over interrupt status */ ++#define DTO_INT_STATUS (0x1<<3) ++ ++/* bit 2: command done interrupt status */ ++#define CD_INT_STATUS (0x1<<2) ++ ++/* bit 1: response error interrupt status */ ++#define RE_INT_STATUS (0x1<<1) ++/* ************************************************************* */ ++ ++/* MCI_RINTSTS(0x44) details:bit 16-1: clear ++ MMC host controller each interrupt but ++ hardware locked write error interrupt ++*/ ++#define ALL_INT_CLR 0x1affe ++ ++/* MCI_STATUS(0x48) details */ ++#define DATA_BUSY (0x1<<9) ++ ++/* MCI_FIFOTH(0x4c) details */ ++#define BURST_SIZE (0x2<<28) ++#define RX_WMARK (0x7<<16) ++#define TX_WMARK 0x8 ++ ++/* MCI_CDETECT(0x50) details */ ++#define MCI_CARD0 (0x1<<0) ++ ++/* MCI_GPIO(0x58) details */ ++#define DTO_FIX_BYPASS (0x1 << 23) ++#define CMD_OUT_EN_FIX_BYPASS (0x1 << 8) ++ ++/* MCI_VERID(0x6c) details */ ++#define MCI_VERID_VALUE 0x5342250A ++#define MCI_VERID_VALUE2 0x5342270A ++#define MCI_VERID_VALUE3 0x5342290A ++ ++/* MCI_BMOD(0x80) details */ ++#define BURST_16 (0x3<<8) ++#define BURST_8 (0x2<<8) ++#define BMOD_DMA_EN (0x1<<7) ++#define BURST_INCR (0x1<<1) ++#define BMOD_SWR (0x1<<0) ++ ++/* MCI_IDINTEN(0x90) details */ ++#define MCI_IDINTEN_MASK 0x00000337UL ++#define TI (0x1<<0) ++#define RI (0x1<<1) ++#define NI (0x1<<8) ++ ++/* MCI MMC_CARDTHRCTL(0x100) details */ ++#define RW_THRESHOLD_SIZE 0x2000005 ++ ++/* MCI_UHS_REG_EXT(0x108) details */ ++#define DRV_PHASE_MASK (0x7<<23) ++#define DRV_PHASE_SHIFT (0x4<<23) ++#define SMPL_PHASE_MASK (0x7<<16) ++#define SMPL_PHASE_SHIFT (0x1<<16) ++ ++/* MCI_FIFO_START(0x200) details */ ++#define FIFO_COUNT 17 /* fifo count [bit 17] */ ++#define FIFO_COUNT_MASK 0x1fff /* fifo count [bit 29:17]>>17 */ ++ ++#endif +diff --git a/include/mmc.h b/include/mmc.h +index 1a9efe4..cd5302b 100644 +--- a/include/mmc.h ++++ b/include/mmc.h +@@ -239,6 +239,9 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx) + /* + * EXT_CSD field definitions + */ ++#define EXT_CSD_WR_REL_VALUE (0x1f) ++#define EXT_CSD_RST_N_EN_MASK 0x3 ++#define EXT_CSD_RST_N_ENABLED (1 << 0) /* RST_n is enabled on card */ + + #define EXT_CSD_CMD_SET_NORMAL (1 << 0) + #define EXT_CSD_CMD_SET_SECURE (1 << 1) +@@ -507,6 +510,9 @@ struct mmc_ops { + int (*getcd)(struct mmc *mmc); + int (*getwp)(struct mmc *mmc); + int (*host_power_cycle)(struct mmc *mmc); ++ int (*execute_tuning)(struct mmc *mmc, u32 opcode); ++ void (*hs400_enable_es)(struct mmc *mmc, bool enable); ++ int (*card_busy)(struct mmc *mmc); + }; + #endif + +@@ -601,6 +607,7 @@ struct mmc { + bool clk_disable; /* true if the clock can be turned off */ + uint bus_width; + uint clock; ++#define MMC_HIGH_52_MAX_DTR 52000000 + enum mmc_voltage signal_voltage; + uint card_caps; + uint host_caps; +@@ -653,6 +660,9 @@ struct mmc { + struct udevice *vqmmc_supply; /* IO voltage regulator (Vccq)*/ + #endif + #endif ++ u8 strobe_enhanced; ++ u8 dev_num; ++ u32 ocr_from_bootrom; + u8 *ext_csd; + u32 cardtype; /* cardtype read from the MMC */ + enum mmc_voltage current_voltage; +@@ -773,6 +783,8 @@ int mmc_getwp(struct mmc *mmc); + int board_mmc_getwp(struct mmc *mmc); + #endif + ++int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd); ++int mmc_set_boot_config(struct mmc *mmc); + int mmc_set_dsr(struct mmc *mmc, u16 val); + /* Function to change the size of boot partition and rpmb partitions */ + int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, +diff --git a/include/nand.h b/include/nand.h +index 93cbe1e..2b08569 100644 +--- a/include/nand.h ++++ b/include/nand.h +@@ -43,6 +43,9 @@ extern int board_nand_init(struct nand_chip *nand); + #endif + + extern int nand_curr_device; ++extern struct mtd_info *nand_info[]; ++ ++extern unsigned int ecc0_flag; + + static inline int nand_read(struct mtd_info *info, loff_t ofs, size_t *len, + u_char *buf) +@@ -97,6 +100,13 @@ struct nand_erase_options { + + typedef struct nand_erase_options nand_erase_options_t; + ++void nand_fill_ecc(struct nand_chip *chip, uint8_t *oob, size_t len); ++ ++int nand_read_yaffs_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, ++ u_char *buffer); ++int nand_write_yaffs_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, ++ u_char *buffer); ++ + int nand_read_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, + size_t *actual, loff_t lim, u_char *buffer); + +diff --git a/include/net.h b/include/net.h +index 834f244..34f8968 100644 +--- a/include/net.h ++++ b/include/net.h +@@ -373,6 +373,7 @@ struct ip_hdr { + + #define IP_HDR_SIZE (sizeof(struct ip_hdr)) + ++#define IP_MIN_FRAG_DATAGRAM_SIZE (IP_HDR_SIZE + 8) + /* + * Internet Protocol (IP) + UDP header. + */ +diff --git a/include/netdev.h b/include/netdev.h +index 68a3fce..8ce759c 100644 +--- a/include/netdev.h ++++ b/include/netdev.h +@@ -73,6 +73,10 @@ int uec_standard_init(bd_t *bis); + int uli526x_initialize(bd_t *bis); + int armada100_fec_register(unsigned long base_addr); + ++#ifdef CONFIG_GMACV300_ETH ++int gmac_initialize(bd_t *bis); ++#endif ++ + /* Boards with PCI network controllers can call this from their board_eth_init() + * function to initialize whatever's on board. + * Return value is total # of devices found */ +diff --git a/include/nfc_common.h b/include/nfc_common.h +new file mode 100644 +index 0000000..ca88202 +--- /dev/null ++++ b/include/nfc_common.h +@@ -0,0 +1,182 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __NFC_COMMON_H__ ++#define __NFC_COMMON_H__ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define _512B 512 ++#define _2K 2048 ++#define _4K 4096 ++#define _8K 8192 ++#define _16K 16384 ++ ++#define NAND_PAGE_512B 0 ++#define NAND_PAGE_1K 1 ++#define NAND_PAGE_2K 2 ++#define NAND_PAGE_4K 3 ++#define NAND_PAGE_8K 4 ++#define NAND_PAGE_16K 5 ++#define NAND_PAGE_32K 6 ++ ++#define NAND_ECC_NONE 0 ++#define NAND_ECC_0BIT 0 ++#define NAND_ECC_1BIT 1 ++#define NAND_ECC_1BIT_512 1 ++#define NAND_ECC_4BIT 2 ++#define NAND_ECC_4BIT_512 2 ++#define NAND_ECC_4BYTE 2 ++#define NAND_ECC_8BIT 2 ++#define NAND_ECC_8BIT_512 3 ++#define NAND_ECC_8BYTE 3 ++#define NAND_ECC_13BIT 4 ++#define NAND_ECC_16BIT 5 ++#define NAND_ECC_18BIT 6 ++#define NAND_ECC_24BIT 7 ++#define NAND_ECC_27BIT 8 ++#define NAND_ECC_28BIT 9 ++#define NAND_ECC_32BIT 10 ++#define NAND_ECC_40BIT 11 ++#define NAND_ECC_41BIT 12 ++#define NAND_ECC_42BIT 13 ++#define NAND_ECC_48BIT 14 ++#define NAND_ECC_60BIT 15 ++#define NAND_ECC_64BIT 16 ++#define NAND_ECC_72BIT 17 ++#define NAND_ECC_80BIT 18 ++ ++#define ERSTR_HARDWARE "Hardware configuration error." ++#define ERSTR_DRIVER "Driver does not support." ++ ++#define DISABLE 0 ++#define ENABLE 1 ++ ++#define is_randomizer(_dev) ((_dev)->flags & NAND_RANDOMIZER) ++#define is_hw_auto(_dev) ((_dev)->flags & NAND_HW_AUTO) ++#define is_synchronous(_dev) ((_dev)->flags & NAND_SYNCHRONOUS) ++#define is_config_done(_dev) ((_dev)->flags & NAND_CONFIG_DONE) ++#define is_synchronous_boot(_dev) ((_dev)->flags & NAND_SYNCHRONOUS_BOOT) ++ ++#define NAND_PAGE_SHIFT 9 /* 512 */ ++ ++#if defined(CONFIG_NAND_FLASH_SNFC100) || \ ++ defined(CONFIG_NAND_FLASH_NFC610) || \ ++ defined(CONFIG_FMC_SPI_NAND) || \ ++ defined(CONFIG_FMC_NAND) ++ ++enum ecc_type { ++ ET_ECC_NONE = 0x00, ++ ET_ECC_1BIT = 0x01, ++ ET_ECC_4BIT = 0x02, ++ ET_ECC_8BIT = 0x03, ++ ET_ECC_24BIT1K = 0x04, ++ ET_ECC_40BIT1K = 0x05, ++ ET_ECC_64BIT1K = 0x06, ++}; ++ ++enum page_type { ++ PT_PAGESIZE_512 = 0x00, ++ PT_PAGESIZE_2K = 0x01, ++ PT_PAGESIZE_4K = 0x02, ++ PT_PAGESIZE_8K = 0x03, ++ PT_PAGESIZE_16K = 0x04, ++}; ++ ++struct nand_config_info { ++ unsigned int pagetype; ++ unsigned int ecctype; ++ unsigned int oobsize; ++ struct nand_ecclayout *layout; ++}; ++ ++struct nfc_host; ++ ++struct read_retry_t { ++ int type; ++ int count; ++ int (*set_rr_param)(struct nfc_host *host, int param); ++ int (*get_rr_param)(struct nfc_host *host); ++ int (*reset_rr_param)(struct nfc_host *host); ++ int (*enable_enhanced_slc)(struct nfc_host *host, int enable); ++}; ++#endif ++ ++struct nand_flash_dev_ex { ++ struct nand_flash_dev flash_dev; ++ ++ char *start_type; ++ unsigned char ids[8]; /* 8Byte */ ++ int oobsize; ++ int ecctype; ++ ++#define NAND_RANDOMIZER 0x01 /* nand chip need randomizer */ ++#define NAND_HW_AUTO 0x02 /* controller support hardware auto config */ ++#define NAND_SYNCHRONOUS 0x04 /* nand chip support synchronous */ ++#define NAND_ASYNCHRONOUS 0x08 /* nand chip support asynchronous */ ++#define NAND_SYNCHRONOUS_BOOT 0x10 /* nand boot from synchronous mode */ ++#define NAND_CONFIG_DONE 0x20 /* current controller config finish */ ++ int flags; ++ int is_randomizer; ++#define NAND_RR_NONE 0x00 ++#define NAND_RR_MASK 0xF0 ++ int read_retry_type; ++ ++ int hostver; /* host controller version. */ ++}; ++ ++const char *nand_ecc_name(int type); ++ ++const char *nand_page_name(int type); ++ ++int nandpage_size2type(int size); ++ ++int nandpage_type2size(int size); ++ ++extern int nand_get_ecctype(void); ++ ++extern struct nand_flash_dev *(*get_flash_type)(struct mtd_info *mtd, ++ struct nand_chip *chip, unsigned char *id); ++ ++extern int (*nand_oob_resize)(struct mtd_info *mtd); ++ ++extern unsigned char match_ecc_type_to_yaffs(unsigned char type); ++ ++extern unsigned char match_page_reg_to_type(unsigned char reg); ++ ++extern unsigned char match_page_type_to_reg(unsigned char type); ++ ++extern const char *match_page_type_to_str(unsigned char type); ++ ++extern unsigned char match_ecc_reg_to_type(unsigned char reg); ++ ++extern unsigned char match_ecc_type_to_reg(unsigned char type); ++ ++extern const char *match_ecc_type_to_str(unsigned char type); ++ ++extern unsigned char match_page_size_to_type(unsigned int size); ++ ++extern unsigned int match_page_type_to_size(unsigned char type); ++ ++#endif /* End of __NFC_COMMON_H__ */ ++ +diff --git a/include/pci.h b/include/pci.h +index ff59ac0..1525d9d 100644 +--- a/include/pci.h ++++ b/include/pci.h +@@ -520,6 +520,17 @@ static inline void pci_set_region(struct pci_region *reg, + + typedef int pci_dev_t; + ++#ifdef CONFIG_PCIE_BSP ++#define PCI_BUS(d) (((d) >> 20) & 0xff) ++#define PCI_DEV(d) (((d) >> 15) & 0x1f) ++#define PCI_FUNC(d) (((d) >> 12) & 0x7) ++#define PCI_DEVFN(d, f) ((d) << 15 | (f) << 12) ++#define PCI_MASK_BUS(bdf) ((bdf) & 0xffff) ++#define PCI_ADD_BUS(bus, devfn) (((bus) << 20) | (devfn)) ++#define PCI_BDF(b, d, f) ((b) << 20 | PCI_DEVFN(d, f)) ++#define PCI_VENDEV(v, d) (((v) << 20) | (d)) ++#define PCI_ANY_ID (~0) ++#else + #define PCI_BUS(d) (((d) >> 16) & 0xff) + + /* +@@ -540,6 +551,7 @@ typedef int pci_dev_t; + #define PCI_BDF(b, d, f) ((b) << 16 | PCI_DEVFN(d, f)) + #define PCI_VENDEV(v, d) (((v) << 16) | (d)) + #define PCI_ANY_ID (~0) ++#endif + + struct pci_device_id { + unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */ +diff --git a/include/sdhci.h b/include/sdhci.h +old mode 100644 +new mode 100755 +index 01addb7..67524c5 +--- a/include/sdhci.h ++++ b/include/sdhci.h +@@ -11,7 +11,7 @@ + + #include + #include +-#include ++#include + + /* + * Controller registers +@@ -97,6 +97,7 @@ + #define SDHCI_DIV_MASK_LEN 8 + #define SDHCI_DIV_HI_MASK 0x300 + #define SDHCI_PROG_CLOCK_MODE BIT(5) ++#define SDHCI_CLOCK_PLL_EN BIT(3) + #define SDHCI_CLOCK_CARD_EN BIT(2) + #define SDHCI_CLOCK_INT_STABLE BIT(1) + #define SDHCI_CLOCK_INT_EN BIT(0) +@@ -151,7 +152,7 @@ + #define SDHCI_CTRL_UHS_SDR50 0x0002 + #define SDHCI_CTRL_UHS_SDR104 0x0003 + #define SDHCI_CTRL_UHS_DDR50 0x0004 +-#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */ ++#define SDHCI_CTRL_HS400 0x0007 /* Non-standard */ + #define SDHCI_CTRL_VDD_180 0x0008 + #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 + #define SDHCI_CTRL_DRV_TYPE_B 0x0000 +@@ -216,6 +217,40 @@ + #define SDHCI_SPEC_100 0 + #define SDHCI_SPEC_200 1 + #define SDHCI_SPEC_300 2 ++#define SDHCI_SPEC_400 3 ++#define SDHCI_SPEC_420 5 ++ ++/* 0x508 */ ++#define SDHCI_MSHC_CTRL 0x508 ++#define SDHCI_CMD_CONFLIT_CHECK 0x01 ++ ++/* 0x510 */ ++#define SDHCI_AXI_MBIIU_CTRL 0x510 ++#define SDHCI_GM_WR_OSRC_LMT 0x03000000 ++#define SDHCI_GM_RD_OSRC_LMT 0x00030000 ++#define SDHCI_UNDEFL_INCR_EN 0x00000001 ++ ++/* 0x52c */ ++#define SDHCI_EMMC_CTRL 0x52c ++#define SDHCI_ENH_STROBE_EN 0x0100 ++#define SDHCI_CARD_IS_EMMC 0x0001 ++ ++/* 0x540 */ ++#define SDHCI_AT_CTRL 0x540 ++#define SDHCI_SAMPLE_EN 0x00000010 ++ ++/* 0x544 */ ++#define SDHCI_AT_STAT 0x544 ++#define SDHCI_PHASE_SEL_MASK 0x000000ff ++ ++/* 0x54c */ ++#define SDHCI_MULTI_CYCLE 0x54c ++#define SDHCI_FIND_EDGE_CLR 0x00004000 ++#define SDHCI_FOUND_EDGE 0x00000800 ++#define SDHCI_DOUT_EN_F_EDGE 0x00000040 ++#define SDHCI_EDGE_DETECT_EN 0x00000100 ++#define SDHCI_DATA_DLY_EN 0x00000008 ++#define SDHCI_CMD_DLY_EN 0x00000004 + + #define SDHCI_GET_VERSION(x) (x->version & SDHCI_SPEC_VER_MASK) + +@@ -226,6 +261,8 @@ + #define SDHCI_MAX_DIV_SPEC_200 256 + #define SDHCI_MAX_DIV_SPEC_300 2046 + ++#define SDHCI_DMA_BOUNDARY_SIZE (0x1 << 27) ++ + /* + * quirks + */ +@@ -265,7 +302,6 @@ struct sdhci_ops { + void (*set_control_reg)(struct sdhci_host *host); + int (*set_ios_post)(struct sdhci_host *host); + void (*set_clock)(struct sdhci_host *host, u32 div); +- int (*platform_execute_tuning)(struct mmc *host, u8 opcode); + void (*set_delay)(struct sdhci_host *host); + }; + +@@ -279,7 +315,8 @@ struct sdhci_ops { + #define ADMA_TABLE_NO_ENTRIES (CONFIG_SYS_MMC_MAX_BLK_COUNT * \ + MMC_MAX_BLOCK_LEN) / ADMA_MAX_LEN + +-#define ADMA_TABLE_SZ (ADMA_TABLE_NO_ENTRIES * ADMA_DESC_LEN) ++/* alloc more than 2 descs */ ++#define ADMA_TABLE_SZ ((ADMA_TABLE_NO_ENTRIES + 2) * ADMA_DESC_LEN) + + /* Decriptor table defines */ + #define ADMA_DESC_ATTR_VALID BIT(0) +@@ -318,6 +355,12 @@ struct sdhci_host { + struct gpio_desc pwr_gpio; /* Power GPIO */ + struct gpio_desc cd_gpio; /* Card Detect GPIO */ + ++ void (*set_control_reg)(struct sdhci_host *host); ++ int (*set_clock)(struct sdhci_host *host, unsigned int clk); ++#ifdef MMC_SUPPORTS_TUNING ++ int (*execute_tuning)(struct sdhci_host *host, unsigned int opcode); ++#endif ++ void (*priv_init)(struct sdhci_host *host); + uint voltages; + + struct mmc_config cfg; +@@ -332,6 +375,11 @@ struct sdhci_host { + struct sdhci_adma_desc *adma_desc_table; + uint desc_slot; + #endif ++ unsigned int type; ++#define MMC_TYPE_MMC 0 /* MMC card */ ++#define MMC_TYPE_SD 1 /* SD card */ ++ unsigned int is_tuning; ++ unsigned int tuning_phase; + }; + + #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +@@ -491,5 +539,5 @@ int sdhci_set_clock(struct mmc *mmc, unsigned int clock); + extern const struct dm_mmc_ops sdhci_ops; + #else + #endif +- ++void bsp_find_edge_clear(struct sdhci_host *host); + #endif /* __SDHCI_HW_H */ +diff --git a/include/securec.h b/include/securec.h +new file mode 100644 +index 0000000..d933056 +--- /dev/null ++++ b/include/securec.h +@@ -0,0 +1,629 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: The user of this secure c library should include this header file in you source code. ++ * This header file declare all supported API prototype of the library, ++ * such as memcpy_s, strcpy_s, wcscpy_s,strcat_s, strncat_s, sprintf_s, scanf_s, and so on. ++ * Create: 2014-02-25 ++ * Notes: Do not modify this file by yourself. ++ */ ++ ++#ifndef SECUREC_H_5D13A042_DC3F_4ED9_A8D1_882811274C27 ++#define SECUREC_H_5D13A042_DC3F_4ED9_A8D1_882811274C27 ++ ++#include "securectype.h" ++#ifndef SECUREC_HAVE_STDARG_H ++#define SECUREC_HAVE_STDARG_H 1 ++#endif ++ ++#if SECUREC_HAVE_STDARG_H ++#include ++#endif ++ ++#ifndef SECUREC_HAVE_ERRNO_H ++#define SECUREC_HAVE_ERRNO_H 1 ++#endif ++ ++/* EINVAL ERANGE may defined in errno.h */ ++#if SECUREC_HAVE_ERRNO_H ++#if SECUREC_IN_KERNEL ++#include ++#else ++#include ++#endif ++#endif ++ ++/* Define error code */ ++#if defined(SECUREC_NEED_ERRNO_TYPE) || !defined(__STDC_WANT_LIB_EXT1__) || \ ++ (defined(__STDC_WANT_LIB_EXT1__) && (!__STDC_WANT_LIB_EXT1__)) ++#ifndef SECUREC_DEFINED_ERRNO_TYPE ++#define SECUREC_DEFINED_ERRNO_TYPE ++/* Just check whether macrodefinition exists. */ ++#ifndef errno_t ++typedef int errno_t; ++#endif ++#endif ++#endif ++ ++/* Success */ ++#ifndef EOK ++#define EOK 0 ++#endif ++ ++#ifndef EINVAL ++/* The src buffer is not correct and destination buffer can not be reset */ ++#define EINVAL 22 ++#endif ++ ++#ifndef EINVAL_AND_RESET ++/* Once the error is detected, the dest buffer must be reset! Value is 22 or 128 */ ++#define EINVAL_AND_RESET 150 ++#endif ++ ++#ifndef ERANGE ++/* The destination buffer is not long enough and destination buffer can not be reset */ ++#define ERANGE 34 ++#endif ++ ++#ifndef ERANGE_AND_RESET ++/* Once the error is detected, the dest buffer must be reset! Value is 34 or 128 */ ++#define ERANGE_AND_RESET 162 ++#endif ++ ++#ifndef EOVERLAP_AND_RESET ++/* Once the buffer overlap is detected, the dest buffer must be reset! Value is 54 or 128 */ ++#define EOVERLAP_AND_RESET 182 ++#endif ++ ++/* If you need export the function of this library in Win32 dll, use __declspec(dllexport) */ ++#ifndef SECUREC_API ++#if defined(SECUREC_DLL_EXPORT) ++#define SECUREC_API __declspec(dllexport) ++#elif defined(SECUREC_DLL_IMPORT) ++#define SECUREC_API __declspec(dllimport) ++#else ++/* ++ * Standardized function declaration. If a security function is declared in the your code, ++ * it may cause a compilation alarm,Please delete the security function you declared. ++ * Adding extern under windows will cause the system to have inline functions to expand, ++ * so do not add the extern in default ++ */ ++#if defined(_MSC_VER) ++#define SECUREC_API ++#else ++#define SECUREC_API extern ++#endif ++#endif ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++/* ++ * Description: The GetHwSecureCVersion function get SecureC Version string and version number. ++ * Parameter: verNumber - to store version number (for example value is 0x500 | 0xa) ++ * Return: version string ++ */ ++SECUREC_API const char *GetHwSecureCVersion(unsigned short *verNumber); ++ ++#if SECUREC_ENABLE_MEMSET ++/* ++ * Description: The memset_s function copies the value of c (converted to an unsigned char) into each of ++ * the first count characters of the object pointed to by dest. ++ * Parameter: dest - destination address ++ * Parameter: destMax - The maximum length of destination buffer ++ * Parameter: c - the value to be copied ++ * Parameter: count - copies count bytes of value to dest ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t memset_s(void *dest, size_t destMax, int c, size_t count); ++#endif ++ ++#ifndef SECUREC_ONLY_DECLARE_MEMSET ++#define SECUREC_ONLY_DECLARE_MEMSET 0 ++#endif ++ ++#if !SECUREC_ONLY_DECLARE_MEMSET ++ ++#if SECUREC_ENABLE_MEMMOVE ++/* ++ * Description: The memmove_s function copies n characters from the object pointed to by src ++ * into the object pointed to by dest. ++ * Parameter: dest - destination address ++ * Parameter: destMax - The maximum length of destination buffer ++ * Parameter: src - source address ++ * Parameter: count - copies count bytes from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t memmove_s(void *dest, size_t destMax, const void *src, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_MEMCPY ++/* ++ * Description: The memcpy_s function copies n characters from the object pointed to ++ * by src into the object pointed to by dest. ++ * Parameter: dest - destination address ++ * Parameter: destMax - The maximum length of destination buffer ++ * Parameter: src - source address ++ * Parameter: count - copies count bytes from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t memcpy_s(void *dest, size_t destMax, const void *src, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_STRCPY ++/* ++ * Description: The strcpy_s function copies the string pointed to by strSrc (including ++ * the terminating null character) into the array pointed to by strDest ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null character) ++ * Parameter: strSrc - source address ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t strcpy_s(char *strDest, size_t destMax, const char *strSrc); ++#endif ++ ++#if SECUREC_ENABLE_STRNCPY ++/* ++ * Description: The strncpy_s function copies not more than n successive characters (not including ++ * the terminating null character) from the array pointed to by strSrc to the array pointed to by strDest. ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null character) ++ * Parameter: strSrc - source address ++ * Parameter: count - copies count characters from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t strncpy_s(char *strDest, size_t destMax, const char *strSrc, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_STRCAT ++/* ++ * Description: The strcat_s function appends a copy of the string pointed to by strSrc (including ++ * the terminating null character) to the end of the string pointed to by strDest. ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null wide character) ++ * Parameter: strSrc - source address ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t strcat_s(char *strDest, size_t destMax, const char *strSrc); ++#endif ++ ++#if SECUREC_ENABLE_STRNCAT ++/* ++ * Description: The strncat_s function appends not more than n successive characters (not including ++ * the terminating null character) ++ * from the array pointed to by strSrc to the end of the string pointed to by strDest. ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null character) ++ * Parameter: strSrc - source address ++ * Parameter: count - copies count characters from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t strncat_s(char *strDest, size_t destMax, const char *strSrc, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_VSPRINTF ++/* ++ * Description: The vsprintf_s function is equivalent to the vsprintf function except for the parameter destMax ++ * and the explicit runtime-constraints violation ++ * Parameter: strDest - produce output according to a format,write to the character string strDest. ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null wide character) ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of characters printed(not including the terminating null byte '\0'), ++ * If an error occurred Return: -1. ++ */ ++SECUREC_API int vsprintf_s(char *strDest, size_t destMax, const char *format, ++ va_list argList) SECUREC_ATTRIBUTE(3, 0); ++#endif ++ ++#if SECUREC_ENABLE_SPRINTF ++/* ++ * Description: The sprintf_s function is equivalent to the sprintf function except for the parameter destMax ++ * and the explicit runtime-constraints violation ++ * Parameter: strDest - produce output according to a format ,write to the character string strDest. ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null byte '\0') ++ * Parameter: format - format string ++ * Return: the number of characters printed(not including the terminating null byte '\0'), ++ * If an error occurred Return: -1. ++*/ ++SECUREC_API int sprintf_s(char *strDest, size_t destMax, const char *format, ...) SECUREC_ATTRIBUTE(3, 4); ++#endif ++ ++#if SECUREC_ENABLE_VSNPRINTF ++/* ++ * Description: The vsnprintf_s function is equivalent to the vsnprintf function except for ++ * the parameter destMax/count and the explicit runtime-constraints violation ++ * Parameter: strDest - produce output according to a format ,write to the character string strDest. ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null byte '\0') ++ * Parameter: count - do not write more than count bytes to strDest(not including the terminating null byte '\0') ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of characters printed(not including the terminating null byte '\0'), ++ * If an error occurred Return: -1.Pay special attention to returning -1 when truncation occurs. ++ */ ++SECUREC_API int vsnprintf_s(char *strDest, size_t destMax, size_t count, const char *format, ++ va_list argList) SECUREC_ATTRIBUTE(4, 0); ++#endif ++ ++#if SECUREC_ENABLE_SNPRINTF ++/* ++ * Description: The snprintf_s function is equivalent to the snprintf function except for ++ * the parameter destMax/count and the explicit runtime-constraints violation ++ * Parameter: strDest - produce output according to a format ,write to the character string strDest. ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null byte '\0') ++ * Parameter: count - do not write more than count bytes to strDest(not including the terminating null byte '\0') ++ * Parameter: format - format string ++ * Return: the number of characters printed(not including the terminating null byte '\0'), ++ * If an error occurred Return: -1.Pay special attention to returning -1 when truncation occurs. ++ */ ++SECUREC_API int snprintf_s(char *strDest, size_t destMax, size_t count, const char *format, ++ ...) SECUREC_ATTRIBUTE(4, 5); ++#endif ++ ++#if SECUREC_SNPRINTF_TRUNCATED ++/* ++ * Description: The vsnprintf_truncated_s function is equivalent to the vsnprintf_s function except ++ * no count parameter and return value ++ * Parameter: strDest - produce output according to a format ,write to the character string strDest ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null byte '\0') ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of characters printed(not including the terminating null byte '\0'), ++ * If an error occurred Return: -1.Pay special attention to returning destMax - 1 when truncation occurs ++*/ ++SECUREC_API int vsnprintf_truncated_s(char *strDest, size_t destMax, const char *format, ++ va_list argList) SECUREC_ATTRIBUTE(3, 0); ++ ++/* ++ * Description: The snprintf_truncated_s function is equivalent to the snprintf_s function except ++ * no count parameter and return value ++ * Parameter: strDest - produce output according to a format,write to the character string strDest. ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null byte '\0') ++ * Parameter: format - format string ++ * Return: the number of characters printed(not including the terminating null byte '\0'), ++ * If an error occurred Return: -1.Pay special attention to returning destMax - 1 when truncation occurs. ++ */ ++SECUREC_API int snprintf_truncated_s(char *strDest, size_t destMax, ++ const char *format, ...) SECUREC_ATTRIBUTE(3, 4); ++#endif ++ ++#if SECUREC_ENABLE_SCANF ++/* ++ * Description: The scanf_s function is equivalent to fscanf_s with the argument stdin ++ * interposed before the arguments to scanf_s ++ * Parameter: format - format string ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int scanf_s(const char *format, ...); ++#endif ++ ++#if SECUREC_ENABLE_VSCANF ++/* ++ * Description: The vscanf_s function is equivalent to scanf_s, with the variable argument list replaced by argList ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int vscanf_s(const char *format, va_list argList); ++#endif ++ ++#if SECUREC_ENABLE_SSCANF ++/* ++ * Description: The sscanf_s function is equivalent to fscanf_s, except that input is obtained from a ++ * string (specified by the argument buffer) rather than from a stream ++ * Parameter: buffer - read character from buffer ++ * Parameter: format - format string ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int sscanf_s(const char *buffer, const char *format, ...); ++#endif ++ ++#if SECUREC_ENABLE_VSSCANF ++/* ++ * Description: The vsscanf_s function is equivalent to sscanf_s, with the variable argument list ++ * replaced by argList ++ * Parameter: buffer - read character from buffer ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int vsscanf_s(const char *buffer, const char *format, va_list argList); ++#endif ++ ++#if SECUREC_ENABLE_FSCANF ++/* ++ * Description: The fscanf_s function is equivalent to fscanf except that the c, s, and [ conversion specifiers ++ * apply to a pair of arguments (unless assignment suppression is indicated by a *) ++ * Parameter: stream - stdio file stream ++ * Parameter: format - format string ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int fscanf_s(FILE *stream, const char *format, ...); ++#endif ++ ++#if SECUREC_ENABLE_VFSCANF ++/* ++ * Description: The vfscanf_s function is equivalent to fscanf_s, with the variable argument list ++ * replaced by argList ++ * Parameter: stream - stdio file stream ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int vfscanf_s(FILE *stream, const char *format, va_list argList); ++#endif ++ ++#if SECUREC_ENABLE_STRTOK ++/* ++ * Description: The strtok_s function parses a string into a sequence of strToken, ++ * replace all characters in strToken string that match to strDelimit set with 0. ++ * On the first call to strtok_s the string to be parsed should be specified in strToken. ++ * In each subsequent call that should parse the same string, strToken should be NULL ++ * Parameter: strToken - the string to be delimited ++ * Parameter: strDelimit - specifies a set of characters that delimit the tokens in the parsed string ++ * Parameter: context - is a pointer to a char * variable that is used internally by strtok_s function ++ * Return: On the first call returns the address of the first non \0 character, otherwise NULL is returned. ++ * In subsequent calls, the strtoken is set to NULL, and the context set is the same as the previous call, ++ * return NULL if the *context string length is equal 0, otherwise return *context. ++ */ ++SECUREC_API char *strtok_s(char *strToken, const char *strDelimit, char **context); ++#endif ++ ++#if SECUREC_ENABLE_GETS && !SECUREC_IN_KERNEL ++/* ++ * Description: The gets_s function reads at most one less than the number of characters specified ++ * by destMax from the stream pointed to by stdin, into the array pointed to by buffer ++ * Parameter: buffer - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null character) ++ * Return: buffer if there was no runtime-constraint violation,If an error occurred Return: NULL. ++ */ ++SECUREC_API char *gets_s(char *buffer, size_t destMax); ++#endif ++ ++#if SECUREC_ENABLE_WCHAR_FUNC ++#if SECUREC_ENABLE_MEMCPY ++/* ++ * Description: The wmemcpy_s function copies n successive wide characters from the object pointed to ++ * by src into the object pointed to by dest. ++ * Parameter: dest - destination address ++ * Parameter: destMax - The maximum length of destination buffer ++ * Parameter: src - source address ++ * Parameter: count - copies count wide characters from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t wmemcpy_s(wchar_t *dest, size_t destMax, const wchar_t *src, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_MEMMOVE ++/* ++ * Description: The wmemmove_s function copies n successive wide characters from the object ++ * pointed to by src into the object pointed to by dest. ++ * Parameter: dest - destination address ++ * Parameter: destMax - The maximum length of destination buffer ++ * Parameter: src - source address ++ * Parameter: count - copies count wide characters from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t wmemmove_s(wchar_t *dest, size_t destMax, const wchar_t *src, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_STRCPY ++/* ++ * Description: The wcscpy_s function copies the wide string pointed to by strSrc(including the terminating ++ * null wide character) into the array pointed to by strDest ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer ++ * Parameter: strSrc - source address ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t wcscpy_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc); ++#endif ++ ++#if SECUREC_ENABLE_STRNCPY ++/* ++ * Description: The wcsncpy_s function copies not more than n successive wide characters (not including the ++ * terminating null wide character) from the array pointed to by strSrc to the array pointed to by strDest ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating wide character) ++ * Parameter: strSrc - source address ++ * Parameter: count - copies count wide characters from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t wcsncpy_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_STRCAT ++/* ++ * Description: The wcscat_s function appends a copy of the wide string pointed to by strSrc (including the ++ * terminating null wide character) to the end of the wide string pointed to by strDest ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating wide character) ++ * Parameter: strSrc - source address ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t wcscat_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc); ++#endif ++ ++#if SECUREC_ENABLE_STRNCAT ++/* ++ * Description: The wcsncat_s function appends not more than n successive wide characters (not including the ++ * terminating null wide character) from the array pointed to by strSrc to the end of the wide string pointed to ++ * by strDest. ++ * Parameter: strDest - destination address ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating wide character) ++ * Parameter: strSrc - source address ++ * Parameter: count - copies count wide characters from the src ++ * Return: EOK if there was no runtime-constraint violation ++ */ ++SECUREC_API errno_t wcsncat_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc, size_t count); ++#endif ++ ++#if SECUREC_ENABLE_STRTOK ++/* ++ * Description: The wcstok_s function is the wide-character equivalent of the strtok_s function ++ * Parameter: strToken - the string to be delimited ++ * Parameter: strDelimit - specifies a set of characters that delimit the tokens in the parsed string ++ * Parameter: context - is a pointer to a char * variable that is used internally by strtok_s function ++ * Return: a pointer to the first character of a token, or a null pointer if there is no token ++ * or there is a runtime-constraint violation. ++ */ ++SECUREC_API wchar_t *wcstok_s(wchar_t *strToken, const wchar_t *strDelimit, wchar_t **context); ++#endif ++ ++#if SECUREC_ENABLE_VSPRINTF ++/* ++ * Description: The vswprintf_s function is the wide-character equivalent of the vsprintf_s function ++ * Parameter: strDest - produce output according to a format,write to the character string strDest ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null) ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of characters printed(not including the terminating null wide character), ++ * If an error occurred Return: -1. ++ */ ++SECUREC_API int vswprintf_s(wchar_t *strDest, size_t destMax, const wchar_t *format, va_list argList); ++#endif ++ ++#if SECUREC_ENABLE_SPRINTF ++/* ++ * Description: The swprintf_s function is the wide-character equivalent of the sprintf_s function ++ * Parameter: strDest - produce output according to a format,write to the character string strDest ++ * Parameter: destMax - The maximum length of destination buffer(including the terminating null) ++ * Parameter: format - format string ++ * Return: the number of characters printed(not including the terminating null wide character), ++ * If an error occurred Return: -1. ++ */ ++SECUREC_API int swprintf_s(wchar_t *strDest, size_t destMax, const wchar_t *format, ...); ++#endif ++ ++#if SECUREC_ENABLE_FSCANF ++/* ++ * Description: The fwscanf_s function is the wide-character equivalent of the fscanf_s function ++ * Parameter: stream - stdio file stream ++ * Parameter: format - format string ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int fwscanf_s(FILE *stream, const wchar_t *format, ...); ++#endif ++ ++#if SECUREC_ENABLE_VFSCANF ++/* ++ * Description: The vfwscanf_s function is the wide-character equivalent of the vfscanf_s function ++ * Parameter: stream - stdio file stream ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int vfwscanf_s(FILE *stream, const wchar_t *format, va_list argList); ++#endif ++ ++#if SECUREC_ENABLE_SCANF ++/* ++ * Description: The wscanf_s function is the wide-character equivalent of the scanf_s function ++ * Parameter: format - format string ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int wscanf_s(const wchar_t *format, ...); ++#endif ++ ++#if SECUREC_ENABLE_VSCANF ++/* ++ * Description: The vwscanf_s function is the wide-character equivalent of the vscanf_s function ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int vwscanf_s(const wchar_t *format, va_list argList); ++#endif ++ ++#if SECUREC_ENABLE_SSCANF ++/* ++ * Description: The swscanf_s function is the wide-character equivalent of the sscanf_s function ++ * Parameter: buffer - read character from buffer ++ * Parameter: format - format string ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int swscanf_s(const wchar_t *buffer, const wchar_t *format, ...); ++#endif ++ ++#if SECUREC_ENABLE_VSSCANF ++/* ++ * Description: The vswscanf_s function is the wide-character equivalent of the vsscanf_s function ++ * Parameter: buffer - read character from buffer ++ * Parameter: format - format string ++ * Parameter: argList - instead of a variable number of arguments ++ * Return: the number of input items assigned, If an error occurred Return: -1. ++ */ ++SECUREC_API int vswscanf_s(const wchar_t *buffer, const wchar_t *format, va_list argList); ++#endif ++#endif /* SECUREC_ENABLE_WCHAR_FUNC */ ++#endif ++ ++/* Those functions are used by macro,must declare hare, also for without function declaration warning */ ++extern errno_t strncpy_error(char *strDest, size_t destMax, const char *strSrc, size_t count); ++extern errno_t strcpy_error(char *strDest, size_t destMax, const char *strSrc); ++ ++#if SECUREC_WITH_PERFORMANCE_ADDONS ++/* Those functions are used by macro */ ++extern errno_t memset_sOptAsm(void *dest, size_t destMax, int c, size_t count); ++extern errno_t memset_sOptTc(void *dest, size_t destMax, int c, size_t count); ++extern errno_t memcpy_sOptAsm(void *dest, size_t destMax, const void *src, size_t count); ++extern errno_t memcpy_sOptTc(void *dest, size_t destMax, const void *src, size_t count); ++ ++/* The strcpy_sp is a macro, not a function in performance optimization mode. */ ++#define strcpy_sp(dest, destMax, src) ((__builtin_constant_p((destMax)) && \ ++ __builtin_constant_p((src))) ? \ ++ SECUREC_STRCPY_SM((dest), (destMax), (src)) : \ ++ strcpy_s((dest), (destMax), (src))) ++ ++/* The strncpy_sp is a macro, not a function in performance optimization mode. */ ++#define strncpy_sp(dest, destMax, src, count) ((__builtin_constant_p((count)) && \ ++ __builtin_constant_p((destMax)) && \ ++ __builtin_constant_p((src))) ? \ ++ SECUREC_STRNCPY_SM((dest), (destMax), (src), (count)) : \ ++ strncpy_s((dest), (destMax), (src), (count))) ++ ++/* The strcat_sp is a macro, not a function in performance optimization mode. */ ++#define strcat_sp(dest, destMax, src) ((__builtin_constant_p((destMax)) && \ ++ __builtin_constant_p((src))) ? \ ++ SECUREC_STRCAT_SM((dest), (destMax), (src)) : \ ++ strcat_s((dest), (destMax), (src))) ++ ++/* The strncat_sp is a macro, not a function in performance optimization mode. */ ++#define strncat_sp(dest, destMax, src, count) ((__builtin_constant_p((count)) && \ ++ __builtin_constant_p((destMax)) && \ ++ __builtin_constant_p((src))) ? \ ++ SECUREC_STRNCAT_SM((dest), (destMax), (src), (count)) : \ ++ strncat_s((dest), (destMax), (src), (count))) ++ ++/* The memcpy_sp is a macro, not a function in performance optimization mode. */ ++#define memcpy_sp(dest, destMax, src, count) (__builtin_constant_p((count)) ? \ ++ (SECUREC_MEMCPY_SM((dest), (destMax), (src), (count))) : \ ++ (__builtin_constant_p((destMax)) ? \ ++ (((size_t)(destMax) > 0 && \ ++ (((unsigned long long)(destMax) & (unsigned long long)(-2)) < SECUREC_MEM_MAX_LEN)) ? \ ++ memcpy_sOptTc((dest), (destMax), (src), (count)) : ERANGE) : \ ++ memcpy_sOptAsm((dest), (destMax), (src), (count)))) ++ ++/* The memset_sp is a macro, not a function in performance optimization mode. */ ++#define memset_sp(dest, destMax, c, count) (__builtin_constant_p((count)) ? \ ++ (SECUREC_MEMSET_SM((dest), (destMax), (c), (count))) : \ ++ (__builtin_constant_p((destMax)) ? \ ++ (((((unsigned long long)(destMax) & (unsigned long long)(-2)) < SECUREC_MEM_MAX_LEN)) ? \ ++ memset_sOptTc((dest), (destMax), (c), (count)) : ERANGE) : \ ++ memset_sOptAsm((dest), (destMax), (c), (count)))) ++ ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ +diff --git a/include/securectype.h b/include/securectype.h +new file mode 100644 +index 0000000..d4155e8 +--- /dev/null ++++ b/include/securectype.h +@@ -0,0 +1,585 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: Define internal used macro and data type. The marco of SECUREC_ON_64BITS ++ * will be determined in this header file, which is a switch for part ++ * of code. Some macro are used to suppress warning by MS compiler. ++ * Create: 2014-02-25 ++ * Notes: User can change the value of SECUREC_STRING_MAX_LEN and SECUREC_MEM_MAX_LEN ++ * macro to meet their special need, but The maximum value should not exceed 2G. ++ */ ++/* ++ * [Standardize-exceptions]: Performance-sensitive ++ * [reason]: Strict parameter verification has been done before use ++ */ ++ ++#ifndef SECURECTYPE_H_A7BBB686_AADA_451B_B9F9_44DACDAE18A7 ++#define SECURECTYPE_H_A7BBB686_AADA_451B_B9F9_44DACDAE18A7 ++ ++#ifndef SECUREC_USING_STD_SECURE_LIB ++#if defined(_MSC_VER) && _MSC_VER >= 1400 ++#if defined(__STDC_WANT_SECURE_LIB__) && (!__STDC_WANT_SECURE_LIB__) ++/* Security functions have been provided since vs2005, default use of system library functions */ ++#define SECUREC_USING_STD_SECURE_LIB 0 ++#else ++#define SECUREC_USING_STD_SECURE_LIB 1 ++#endif ++#else ++#define SECUREC_USING_STD_SECURE_LIB 0 ++#endif ++#endif ++ ++/* Compatibility with older Secure C versions, shielding VC symbol redefinition warning */ ++#if defined(_MSC_VER) && (_MSC_VER >= 1400) && (!SECUREC_USING_STD_SECURE_LIB) ++#ifndef SECUREC_DISABLE_CRT_FUNC ++#define SECUREC_DISABLE_CRT_FUNC 1 ++#endif ++#ifndef SECUREC_DISABLE_CRT_IMP ++#define SECUREC_DISABLE_CRT_IMP 1 ++#endif ++#else /* MSC VER */ ++#ifndef SECUREC_DISABLE_CRT_FUNC ++#define SECUREC_DISABLE_CRT_FUNC 0 ++#endif ++#ifndef SECUREC_DISABLE_CRT_IMP ++#define SECUREC_DISABLE_CRT_IMP 0 ++#endif ++#endif ++ ++#if SECUREC_DISABLE_CRT_FUNC ++#ifdef __STDC_WANT_SECURE_LIB__ ++#undef __STDC_WANT_SECURE_LIB__ ++#endif ++#define __STDC_WANT_SECURE_LIB__ 0 ++#endif ++ ++#if SECUREC_DISABLE_CRT_IMP ++#ifdef _CRTIMP_ALTERNATIVE ++#undef _CRTIMP_ALTERNATIVE ++#endif ++#define _CRTIMP_ALTERNATIVE /* Comment Microsoft *_s function */ ++#endif ++ ++/* Compile in kernel under macro control */ ++#ifndef SECUREC_IN_KERNEL ++#ifdef __KERNEL__ ++#define SECUREC_IN_KERNEL 0 ++#else ++#define SECUREC_IN_KERNEL 0 ++#endif ++#endif ++ ++/* make kernel symbols of functions available to loadable modules */ ++#ifndef SECUREC_EXPORT_KERNEL_SYMBOL ++#if SECUREC_IN_KERNEL ++#define SECUREC_EXPORT_KERNEL_SYMBOL 1 ++#else ++#define SECUREC_EXPORT_KERNEL_SYMBOL 0 ++#endif ++#endif ++ ++#if SECUREC_IN_KERNEL ++#ifndef SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_SCANF_FILE 0 ++#endif ++#ifndef SECUREC_ENABLE_WCHAR_FUNC ++#define SECUREC_ENABLE_WCHAR_FUNC 0 ++#endif ++#else /* SECUREC_IN_KERNEL */ ++#ifndef SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_SCANF_FILE 0 ++#endif ++#ifndef SECUREC_ENABLE_WCHAR_FUNC ++#define SECUREC_ENABLE_WCHAR_FUNC 1 ++#endif ++#endif ++ ++/* Default secure function declaration, default declarations for non-standard functions */ ++#ifndef SECUREC_SNPRINTF_TRUNCATED ++#define SECUREC_SNPRINTF_TRUNCATED 1 ++#endif ++ ++#if SECUREC_USING_STD_SECURE_LIB ++#if defined(_MSC_VER) && _MSC_VER >= 1400 ++/* Declare secure functions that are not available in the VS compiler */ ++#ifndef SECUREC_ENABLE_MEMSET ++#define SECUREC_ENABLE_MEMSET 1 ++#endif ++/* VS 2005 have vsnprintf_s function */ ++#ifndef SECUREC_ENABLE_VSNPRINTF ++#define SECUREC_ENABLE_VSNPRINTF 0 ++#endif ++#ifndef SECUREC_ENABLE_SNPRINTF ++/* VS 2005 have vsnprintf_s function Adapt the snprintf_s of the security function */ ++#define snprintf_s _snprintf_s ++#define SECUREC_ENABLE_SNPRINTF 0 ++#endif ++/* Before VS 2010 do not have v functions */ ++#if _MSC_VER <= 1600 || defined(SECUREC_FOR_V_SCANFS) ++#ifndef SECUREC_ENABLE_VFSCANF ++#define SECUREC_ENABLE_VFSCANF 1 ++#endif ++#ifndef SECUREC_ENABLE_VSCANF ++#define SECUREC_ENABLE_VSCANF 1 ++#endif ++#ifndef SECUREC_ENABLE_VSSCANF ++#define SECUREC_ENABLE_VSSCANF 1 ++#endif ++#endif ++ ++#else /* MSC VER */ ++#ifndef SECUREC_ENABLE_MEMSET ++#define SECUREC_ENABLE_MEMSET 0 ++#endif ++#ifndef SECUREC_ENABLE_SNPRINTF ++#define SECUREC_ENABLE_SNPRINTF 0 ++#endif ++#ifndef SECUREC_ENABLE_VSNPRINTF ++#define SECUREC_ENABLE_VSNPRINTF 0 ++#endif ++#endif ++ ++#ifndef SECUREC_ENABLE_MEMMOVE ++#define SECUREC_ENABLE_MEMMOVE 0 ++#endif ++#ifndef SECUREC_ENABLE_MEMCPY ++#define SECUREC_ENABLE_MEMCPY 0 ++#endif ++#ifndef SECUREC_ENABLE_STRCPY ++#define SECUREC_ENABLE_STRCPY 0 ++#endif ++#ifndef SECUREC_ENABLE_STRNCPY ++#define SECUREC_ENABLE_STRNCPY 0 ++#endif ++#ifndef SECUREC_ENABLE_STRCAT ++#define SECUREC_ENABLE_STRCAT 0 ++#endif ++#ifndef SECUREC_ENABLE_STRNCAT ++#define SECUREC_ENABLE_STRNCAT 0 ++#endif ++#ifndef SECUREC_ENABLE_SPRINTF ++#define SECUREC_ENABLE_SPRINTF 0 ++#endif ++#ifndef SECUREC_ENABLE_VSPRINTF ++#define SECUREC_ENABLE_VSPRINTF 0 ++#endif ++#ifndef SECUREC_ENABLE_SSCANF ++#define SECUREC_ENABLE_SSCANF 0 ++#endif ++#ifndef SECUREC_ENABLE_VSSCANF ++#define SECUREC_ENABLE_VSSCANF 0 ++#endif ++#ifndef SECUREC_ENABLE_SCANF ++#define SECUREC_ENABLE_SCANF 0 ++#endif ++#ifndef SECUREC_ENABLE_VSCANF ++#define SECUREC_ENABLE_VSCANF 0 ++#endif ++ ++#ifndef SECUREC_ENABLE_FSCANF ++#define SECUREC_ENABLE_FSCANF 0 ++#endif ++#ifndef SECUREC_ENABLE_VFSCANF ++#define SECUREC_ENABLE_VFSCANF 0 ++#endif ++#ifndef SECUREC_ENABLE_STRTOK ++#define SECUREC_ENABLE_STRTOK 0 ++#endif ++#ifndef SECUREC_ENABLE_GETS ++#define SECUREC_ENABLE_GETS 0 ++#endif ++ ++#else /* SECUREC USE STD SECURE LIB */ ++ ++#ifndef SECUREC_ENABLE_MEMSET ++#define SECUREC_ENABLE_MEMSET 1 ++#endif ++#ifndef SECUREC_ENABLE_MEMMOVE ++#define SECUREC_ENABLE_MEMMOVE 1 ++#endif ++#ifndef SECUREC_ENABLE_MEMCPY ++#define SECUREC_ENABLE_MEMCPY 1 ++#endif ++#ifndef SECUREC_ENABLE_STRCPY ++#define SECUREC_ENABLE_STRCPY 1 ++#endif ++#ifndef SECUREC_ENABLE_STRNCPY ++#define SECUREC_ENABLE_STRNCPY 1 ++#endif ++#ifndef SECUREC_ENABLE_STRCAT ++#define SECUREC_ENABLE_STRCAT 1 ++#endif ++#ifndef SECUREC_ENABLE_STRNCAT ++#define SECUREC_ENABLE_STRNCAT 1 ++#endif ++#ifndef SECUREC_ENABLE_SPRINTF ++#define SECUREC_ENABLE_SPRINTF 1 ++#endif ++#ifndef SECUREC_ENABLE_VSPRINTF ++#define SECUREC_ENABLE_VSPRINTF 1 ++#endif ++#ifndef SECUREC_ENABLE_SNPRINTF ++#define SECUREC_ENABLE_SNPRINTF 1 ++#endif ++#ifndef SECUREC_ENABLE_VSNPRINTF ++#define SECUREC_ENABLE_VSNPRINTF 1 ++#endif ++#ifndef SECUREC_ENABLE_SSCANF ++#define SECUREC_ENABLE_SSCANF 1 ++#endif ++#ifndef SECUREC_ENABLE_VSSCANF ++#define SECUREC_ENABLE_VSSCANF 1 ++#endif ++#ifndef SECUREC_ENABLE_SCANF ++#if SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_SCANF 1 ++#else ++#define SECUREC_ENABLE_SCANF 0 ++#endif ++#endif ++#ifndef SECUREC_ENABLE_VSCANF ++#if SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_VSCANF 1 ++#else ++#define SECUREC_ENABLE_VSCANF 0 ++#endif ++#endif ++ ++#ifndef SECUREC_ENABLE_FSCANF ++#if SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_FSCANF 1 ++#else ++#define SECUREC_ENABLE_FSCANF 0 ++#endif ++#endif ++#ifndef SECUREC_ENABLE_VFSCANF ++#if SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_VFSCANF 1 ++#else ++#define SECUREC_ENABLE_VFSCANF 0 ++#endif ++#endif ++ ++#ifndef SECUREC_ENABLE_STRTOK ++#define SECUREC_ENABLE_STRTOK 1 ++#endif ++#ifndef SECUREC_ENABLE_GETS ++#define SECUREC_ENABLE_GETS 1 ++#endif ++#endif /* SECUREC_USE_STD_SECURE_LIB */ ++ ++#if !SECUREC_ENABLE_SCANF_FILE ++#if SECUREC_ENABLE_FSCANF ++#undef SECUREC_ENABLE_FSCANF ++#define SECUREC_ENABLE_FSCANF 0 ++#endif ++#if SECUREC_ENABLE_VFSCANF ++#undef SECUREC_ENABLE_VFSCANF ++#define SECUREC_ENABLE_VFSCANF 0 ++#endif ++#if SECUREC_ENABLE_SCANF ++#undef SECUREC_ENABLE_SCANF ++#define SECUREC_ENABLE_SCANF 0 ++#endif ++#if SECUREC_ENABLE_FSCANF ++#undef SECUREC_ENABLE_FSCANF ++#define SECUREC_ENABLE_FSCANF 0 ++#endif ++ ++#endif ++ ++#if SECUREC_IN_KERNEL ++#include ++#include ++#else ++#ifndef SECUREC_HAVE_STDIO_H ++#define SECUREC_HAVE_STDIO_H 1 ++#endif ++#ifndef SECUREC_HAVE_STRING_H ++#define SECUREC_HAVE_STRING_H 1 ++#endif ++#ifndef SECUREC_HAVE_STDLIB_H ++#define SECUREC_HAVE_STDLIB_H 1 ++#endif ++#if SECUREC_HAVE_STDIO_H ++#include ++#endif ++#if SECUREC_HAVE_STRING_H ++#include ++#endif ++#if SECUREC_HAVE_STDLIB_H ++#include ++#endif ++#endif ++ ++/* ++ * If you need high performance, enable the SECUREC_WITH_PERFORMANCE_ADDONS macro, default is enable. ++ * The macro is automatically closed on the windows platform and linux kernel ++ */ ++#ifndef SECUREC_WITH_PERFORMANCE_ADDONS ++#if SECUREC_IN_KERNEL ++#define SECUREC_WITH_PERFORMANCE_ADDONS 0 ++#else ++#define SECUREC_WITH_PERFORMANCE_ADDONS 1 ++#endif ++#endif ++ ++/* If enable SECUREC_COMPATIBLE_WIN_FORMAT, the output format will be compatible to Windows. */ ++#if (defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)) && !defined(SECUREC_COMPATIBLE_LINUX_FORMAT) ++#ifndef SECUREC_COMPATIBLE_WIN_FORMAT ++#define SECUREC_COMPATIBLE_WIN_FORMAT ++#endif ++#endif ++ ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++/* On windows platform, can't use optimized function for there is no __builtin_constant_p like function */ ++/* If need optimized macro, can define this: define __builtin_constant_p(x) 0 */ ++#ifdef SECUREC_WITH_PERFORMANCE_ADDONS ++#undef SECUREC_WITH_PERFORMANCE_ADDONS ++#define SECUREC_WITH_PERFORMANCE_ADDONS 0 ++#endif ++#endif ++ ++#if defined(__VXWORKS__) || defined(__vxworks) || defined(__VXWORKS) || defined(_VXWORKS_PLATFORM_) || \ ++ defined(SECUREC_VXWORKS_VERSION_5_4) ++#ifndef SECUREC_VXWORKS_PLATFORM ++#define SECUREC_VXWORKS_PLATFORM ++#endif ++#endif ++ ++/* If enable SECUREC_COMPATIBLE_LINUX_FORMAT, the output format will be compatible to Linux. */ ++#if !defined(SECUREC_COMPATIBLE_WIN_FORMAT) && !defined(SECUREC_VXWORKS_PLATFORM) ++#ifndef SECUREC_COMPATIBLE_LINUX_FORMAT ++#define SECUREC_COMPATIBLE_LINUX_FORMAT ++#endif ++#endif ++ ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++#ifndef SECUREC_HAVE_STDDEF_H ++#define SECUREC_HAVE_STDDEF_H 1 ++#endif ++/* Some system may no stddef.h */ ++#if SECUREC_HAVE_STDDEF_H ++#if !SECUREC_IN_KERNEL ++#include ++#endif ++#endif ++#endif ++ ++/* ++ * Add the -DSECUREC_SUPPORT_FORMAT_WARNING=1 compiler option to supoort -Wformat=2. ++ * Default does not check the format is that the same data type in the actual code. ++ * In the product is different in the original data type definition of VxWorks and Linux. ++ */ ++#ifndef SECUREC_SUPPORT_FORMAT_WARNING ++#define SECUREC_SUPPORT_FORMAT_WARNING 0 ++#endif ++ ++#if SECUREC_SUPPORT_FORMAT_WARNING ++#define SECUREC_ATTRIBUTE(x, y) __attribute__((format(printf, (x), (y)))) ++#else ++#define SECUREC_ATTRIBUTE(x, y) ++#endif ++ ++/* ++ * Add the -DSECUREC_SUPPORT_BUILTIN_EXPECT=0 compiler option, if compiler can not support __builtin_expect. ++ */ ++#ifndef SECUREC_SUPPORT_BUILTIN_EXPECT ++#define SECUREC_SUPPORT_BUILTIN_EXPECT 1 ++#endif ++ ++#if SECUREC_SUPPORT_BUILTIN_EXPECT && defined(__GNUC__) && ((__GNUC__ > 3) || \ ++ (defined(__GNUC_MINOR__) && (__GNUC__ == 3 && __GNUC_MINOR__ > 3))) ++/* ++ * This is a built-in function that can be used without a declaration, if warning for declaration not found occurred, ++ * you can add -DSECUREC_NEED_BUILTIN_EXPECT_DECLARE to compiler options ++ */ ++#ifdef SECUREC_NEED_BUILTIN_EXPECT_DECLARE ++long __builtin_expect(long exp, long c); ++#endif ++ ++#define SECUREC_LIKELY(x) __builtin_expect(!!(x), 1) ++#define SECUREC_UNLIKELY(x) __builtin_expect(!!(x), 0) ++#else ++#define SECUREC_LIKELY(x) (x) ++#define SECUREC_UNLIKELY(x) (x) ++#endif ++ ++/* Define the max length of the string */ ++#ifndef SECUREC_STRING_MAX_LEN ++#define SECUREC_STRING_MAX_LEN 0x7fffffffUL ++#endif ++#define SECUREC_WCHAR_STRING_MAX_LEN (SECUREC_STRING_MAX_LEN / sizeof(wchar_t)) ++ ++/* Add SECUREC_MEM_MAX_LEN for memcpy and memmove */ ++#ifndef SECUREC_MEM_MAX_LEN ++#define SECUREC_MEM_MAX_LEN 0x7fffffffUL ++#endif ++#define SECUREC_WCHAR_MEM_MAX_LEN (SECUREC_MEM_MAX_LEN / sizeof(wchar_t)) ++ ++#if SECUREC_STRING_MAX_LEN > 0x7fffffffUL ++#error "max string is 2G" ++#endif ++ ++#if (defined(__GNUC__) && defined(__SIZEOF_POINTER__)) ++#if (__SIZEOF_POINTER__ != 4) && (__SIZEOF_POINTER__ != 8) ++#error "unsupported system" ++#endif ++#endif ++ ++#if defined(_WIN64) || defined(WIN64) || defined(__LP64__) || defined(_LP64) ++#define SECUREC_ON_64BITS ++#endif ++ ++#if (!defined(SECUREC_ON_64BITS) && defined(__GNUC__) && defined(__SIZEOF_POINTER__)) ++#if __SIZEOF_POINTER__ == 8 ++#define SECUREC_ON_64BITS ++#endif ++#endif ++ ++#if defined(__SVR4) || defined(__svr4__) ++#define SECUREC_ON_SOLARIS ++#endif ++ ++#if (defined(__hpux) || defined(_AIX) || defined(SECUREC_ON_SOLARIS)) ++#define SECUREC_ON_UNIX ++#endif ++ ++/* ++ * Codes should run under the macro SECUREC_COMPATIBLE_LINUX_FORMAT in unknown system on default, ++ * and strtold. ++ * The function strtold is referenced first at ISO9899:1999(C99), and some old compilers can ++ * not support these functions. Here provides a macro to open these functions: ++ * SECUREC_SUPPORT_STRTOLD -- If defined, strtold will be used ++ */ ++#ifndef SECUREC_SUPPORT_STRTOLD ++#define SECUREC_SUPPORT_STRTOLD 0 ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT)) ++#if defined(__USE_ISOC99) || \ ++ (defined(_AIX) && defined(_ISOC99_SOURCE)) || \ ++ (defined(__hpux) && defined(__ia64)) || \ ++ (defined(SECUREC_ON_SOLARIS) && (!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || \ ++ defined(_STDC_C99) || defined(__EXTENSIONS__)) ++#undef SECUREC_SUPPORT_STRTOLD ++#define SECUREC_SUPPORT_STRTOLD 1 ++#endif ++#endif ++#if ((defined(SECUREC_WRLINUX_BELOW4) || defined(_WRLINUX_BELOW4_))) ++#undef SECUREC_SUPPORT_STRTOLD ++#define SECUREC_SUPPORT_STRTOLD 0 ++#endif ++#endif ++ ++#if SECUREC_WITH_PERFORMANCE_ADDONS ++ ++#ifndef SECUREC_TWO_MIN ++#define SECUREC_TWO_MIN(a, b) ((a) < (b) ? (a) : (b)) ++#endif ++ ++/* For strncpy_s performance optimization */ ++#define SECUREC_STRNCPY_SM(dest, destMax, src, count) \ ++ (((void *)(dest) != NULL && (const void *)(src) != NULL && (size_t)(destMax) > 0 && \ ++ (((unsigned long long)(destMax) & (unsigned long long)(-2)) < SECUREC_STRING_MAX_LEN) && \ ++ (SECUREC_TWO_MIN((size_t)(count), strlen(src)) + 1) <= (size_t)(destMax)) ? \ ++ (((size_t)(count) < strlen(src)) ? (memcpy((dest), (src), (count)), *((char *)(dest) + (count)) = '\0', EOK) : \ ++ (memcpy((dest), (src), strlen(src) + 1), EOK)) : (strncpy_error((dest), (destMax), (src), (count)))) ++ ++#define SECUREC_STRCPY_SM(dest, destMax, src) \ ++ (((void *)(dest) != NULL && (const void *)(src) != NULL && (size_t)(destMax) > 0 && \ ++ (((unsigned long long)(destMax) & (unsigned long long)(-2)) < SECUREC_STRING_MAX_LEN) && \ ++ (strlen(src) + 1) <= (size_t)(destMax)) ? (memcpy((dest), (src), strlen(src) + 1), EOK) : \ ++ (strcpy_error((dest), (destMax), (src)))) ++ ++/* For strcat_s performance optimization */ ++#if defined(__GNUC__) ++#define SECUREC_STRCAT_SM(dest, destMax, src) ({ \ ++ int catRet_ = EOK; \ ++ if ((void *)(dest) != NULL && (const void *)(src) != NULL && (size_t)(destMax) > 0 && \ ++ (((unsigned long long)(destMax) & (unsigned long long)(-2)) < SECUREC_STRING_MAX_LEN)) { \ ++ char *catTmpDst_ = (char *)(dest); \ ++ size_t catRestSize_ = (destMax); \ ++ while (catRestSize_ > 0 && *catTmpDst_ != '\0') { \ ++ ++catTmpDst_; \ ++ --catRestSize_; \ ++ } \ ++ if (catRestSize_ == 0) { \ ++ catRet_ = EINVAL; \ ++ } else if ((strlen(src) + 1) <= catRestSize_) { \ ++ memcpy(catTmpDst_, (src), strlen(src) + 1); \ ++ catRet_ = EOK; \ ++ } else { \ ++ catRet_ = ERANGE; \ ++ } \ ++ if (catRet_ != EOK) { \ ++ catRet_ = strcat_s((dest), (destMax), (src)); \ ++ } \ ++ } else { \ ++ catRet_ = strcat_s((dest), (destMax), (src)); \ ++ } \ ++ catRet_; \ ++}) ++#else ++#define SECUREC_STRCAT_SM(dest, destMax, src) strcat_s((dest), (destMax), (src)) ++#endif ++ ++/* For strncat_s performance optimization */ ++#if defined(__GNUC__) ++#define SECUREC_STRNCAT_SM(dest, destMax, src, count) ({ \ ++ int ncatRet_ = EOK; \ ++ if ((void *)(dest) != NULL && (const void *)(src) != NULL && (size_t)(destMax) > 0 && \ ++ (((unsigned long long)(destMax) & (unsigned long long)(-2)) < SECUREC_STRING_MAX_LEN) && \ ++ (((unsigned long long)(count) & (unsigned long long)(-2)) < SECUREC_STRING_MAX_LEN)) { \ ++ char *ncatTmpDest_ = (char *)(dest); \ ++ size_t ncatRestSize_ = (size_t)(destMax); \ ++ while (ncatRestSize_ > 0 && *ncatTmpDest_ != '\0') { \ ++ ++ncatTmpDest_; \ ++ --ncatRestSize_; \ ++ } \ ++ if (ncatRestSize_ == 0) { \ ++ ncatRet_ = EINVAL; \ ++ } else if ((SECUREC_TWO_MIN((count), strlen(src)) + 1) <= ncatRestSize_) { \ ++ if ((size_t)(count) < strlen(src)) { \ ++ memcpy(ncatTmpDest_, (src), (count)); \ ++ *(ncatTmpDest_ + (count)) = '\0'; \ ++ } else { \ ++ memcpy(ncatTmpDest_, (src), strlen(src) + 1); \ ++ } \ ++ } else { \ ++ ncatRet_ = ERANGE; \ ++ } \ ++ if (ncatRet_ != EOK) { \ ++ ncatRet_ = strncat_s((dest), (destMax), (src), (count)); \ ++ } \ ++ } else { \ ++ ncatRet_ = strncat_s((dest), (destMax), (src), (count)); \ ++ } \ ++ ncatRet_; \ ++}) ++#else ++#define SECUREC_STRNCAT_SM(dest, destMax, src, count) strncat_s((dest), (destMax), (src), (count)) ++#endif ++ ++/* This macro do not check buffer overlap by default */ ++#define SECUREC_MEMCPY_SM(dest, destMax, src, count) \ ++ (!(((size_t)(destMax) == 0) || \ ++ (((unsigned long long)(destMax) & (unsigned long long)(-2)) > SECUREC_MEM_MAX_LEN) || \ ++ ((size_t)(count) > (size_t)(destMax)) || ((void *)(dest)) == NULL || ((const void *)(src) == NULL)) ? \ ++ (memcpy((dest), (src), (count)), EOK) : \ ++ (memcpy_s((dest), (destMax), (src), (count)))) ++ ++#define SECUREC_MEMSET_SM(dest, destMax, c, count) \ ++ (!((((unsigned long long)(destMax) & (unsigned long long)(-2)) > SECUREC_MEM_MAX_LEN) || \ ++ ((void *)(dest) == NULL) || ((size_t)(count) > (size_t)(destMax))) ? \ ++ (memset((dest), (c), (count)), EOK) : \ ++ (memset_s((dest), (destMax), (c), (count)))) ++ ++#endif ++#endif ++ +diff --git a/include/serial.h b/include/serial.h +index 104f34f..2668515 100644 +--- a/include/serial.h ++++ b/include/serial.h +@@ -342,5 +342,7 @@ void serial_putc_raw(const char ch); + void serial_puts(const char *str); + int serial_getc(void); + int serial_tstc(void); ++void serial_puts_to_tool(const char *); ++void serial_enable_output(bool is_enable); + + #endif +diff --git a/include/spi_flash.h b/include/spi_flash.h +index 55b4721..b022872 100644 +--- a/include/spi_flash.h ++++ b/include/spi_flash.h +@@ -12,6 +12,56 @@ + #include /* Because we dereference struct udevice here */ + #include + #include ++#include ++ ++#ifdef CONFIG_SPI_BLOCK_PROTECT ++#define BP_OP_SET 0 ++#define BP_OP_GET 1 ++ ++#define BT_LOC_RDSR 0 ++#define BT_LOC_RDCR 1 ++ ++#define BP_CMP_TOP 0 ++#define BP_CMP_BOTTOM 1 ++#define BP_CMP_UPDATE_FLAG 0xff ++ ++enum block_protection_level { ++ BP_LEVEL_0 = 0, ++ BP_LEVEL_1 = 1, ++ BP_LEVEL_2 = 2, ++ BP_LEVEL_3 = 3, ++ BP_LEVEL_4 = 4, ++ BP_LEVEL_5 = 5, ++ BP_LEVEL_6 = 6, ++ BP_LEVEL_7 = 7, ++ BP_LEVEL_8 = 8, ++ BP_LEVEL_9 = 9, ++ BP_LEVEL_10 = 10, ++ BP_LEVEL_END, ++}; ++ ++#define BP_LEVEL_MAX (BP_LEVEL_END - 1) ++ ++#define BP_NUM_3 3 ++#define BP_NUM_4 4 ++#define BP_NUM_5 5 ++ ++void spi_flash_lock(unsigned char cmp, unsigned char level, unsigned char op); ++ ++#endif /* CONFIG_SPI_BLOCK_PROTECT */ ++ ++#ifndef CONFIG_SF_DEFAULT_SPEED ++# define CONFIG_SF_DEFAULT_SPEED 1000000 ++#endif ++#ifndef CONFIG_SF_DEFAULT_MODE ++# define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 ++#endif ++#ifndef CONFIG_SF_DEFAULT_CS ++# define CONFIG_SF_DEFAULT_CS 0 ++#endif ++#ifndef CONFIG_SF_DEFAULT_BUS ++# define CONFIG_SF_DEFAULT_BUS 0 ++#endif + + /* by default ENV use the same parameters than SF command */ + #ifndef CONFIG_ENV_SPI_BUS +@@ -147,24 +197,33 @@ void spi_flash_free(struct spi_flash *flash); + static inline int spi_flash_read(struct spi_flash *flash, u32 offset, + size_t len, void *buf) + { ++#ifndef CONFIG_FMC + struct mtd_info *mtd = &flash->mtd; + size_t retlen; + + return mtd->_read(mtd, offset, len, &retlen, buf); ++#else ++ return flash->read(flash, offset, len, buf); ++#endif + } + + static inline int spi_flash_write(struct spi_flash *flash, u32 offset, + size_t len, const void *buf) + { ++#ifndef CONFIG_FMC + struct mtd_info *mtd = &flash->mtd; + size_t retlen; + + return mtd->_write(mtd, offset, len, &retlen, buf); ++#else ++ return flash->write(flash, offset, len, buf); ++#endif + } + + static inline int spi_flash_erase(struct spi_flash *flash, u32 offset, + size_t len) + { ++#ifndef CONFIG_FMC + struct mtd_info *mtd = &flash->mtd; + struct erase_info instr; + +@@ -178,6 +237,15 @@ static inline int spi_flash_erase(struct spi_flash *flash, u32 offset, + instr.len = len; + + return mtd->_erase(mtd, &instr); ++#else ++#ifdef CONFIG_FMC_SPI_NOR ++ extern int fmc100_reg_erase(struct spi_flash *spiflash, u32 offset, ++ size_t length); ++ return fmc100_reg_erase(flash, offset, len); ++#else ++ return 0; ++#endif ++#endif + } + #endif + +diff --git a/include/u-boot/aes.h b/include/u-boot/aes.h +new file mode 100644 +index 0000000..8e93dc5 +--- /dev/null ++++ b/include/u-boot/aes.h +@@ -0,0 +1,58 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef _AES_H ++#define _AES_H ++ ++#include ++#include ++ ++#if IMAGE_ENABLE_ENCRYPT ++int image_aes_encrypt(struct image_cipher_info *info, ++ const unsigned char *data, int size, ++ unsigned char **cipher, int *cipher_len); ++int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest); ++#else ++int image_aes_encrypt(struct image_cipher_info *info, ++ const unsigned char *data, int size, ++ unsigned char **cipher, int *cipher_len) ++{ ++ return -ENXIO; ++} ++ ++int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest) ++{ ++ return -ENXIO; ++} ++#endif /* IMAGE_ENABLE_ENCRYPT */ ++ ++#if IMAGE_ENABLE_DECRYPT ++int image_aes_decrypt(struct image_cipher_info *info, ++ const void *cipher, size_t cipher_len, ++ void **data, size_t *size); ++#else ++int image_aes_decrypt(struct image_cipher_info *info, ++ const void *cipher, size_t cipher_len, ++ void **data, size_t *size) ++{ ++ return -ENXIO; ++} ++#endif /* IMAGE_ENABLE_DECRYPT */ ++ ++#endif +diff --git a/include/ufs.h b/include/ufs.h +old mode 100644 +new mode 100755 +index 0592a76..7c1f21e +--- a/include/ufs.h ++++ b/include/ufs.h +@@ -1,6 +1,7 @@ + /* SPDX-License-Identifier: GPL-2.0+ */ + #ifndef _UFS_H + #define _UFS_H ++#ifndef CONFIG_UFS + /** + * ufs_probe() - initialize all devices in the UFS uclass + * +@@ -26,4 +27,1149 @@ int ufs_probe_dev(int index); + * @return 0 if Ok, -ve on error + */ + int ufs_scsi_bind(struct udevice *ufs_dev, struct udevice **scsi_devp); ++#else ++ ++#include ++#include ++ ++#define MAX_DEVICE 1 ++#define find_device_index() (0) ++#define get_local_dwc_host() \ ++ struct dwc_ufs_hba *dwc_host = &g_dwc_host[find_device_index()]; ++ ++#define UFS_OK 0 ++#define UFS_ERR (-1) ++ ++#define bytes_align_128(x) ((x + 128 - 1) & ~(128 - 1)) ++#define bytes_align_1024(x) ((x + 1024 - 1) & ~(1024 - 1)) ++ ++/* UFSHCD Registers Offsets */ ++#define UFS_CAP_OFF 0x00 ++#define UFS_VER_OFF 0x08 ++#define UFS_HCPID_OFF 0x10 ++#define UFS_HCMID_OFF 0x14 ++#define UFS_AHIT_OFF 0x18 ++#define UFS_IS_OFF 0x20 ++#define UFS_IE_OFF 0x24 ++#define UFS_HCS_OFF 0x30 ++#define UFS_HCE_OFF 0x34 ++#define UFS_UECPA_OFF 0x38 ++#define UFS_UECDL_OFF 0x3C ++#define UFS_UECN_OFF 0x40 ++#define UFS_UECT_OFF 0x44 ++#define UFS_UECDME_OFF 0x48 ++#define UFS_UTRIACR_OFF 0x4C ++#define UFS_UTRLBA_OFF 0x50 ++#define UFS_UTRLBAU_OFF 0x54 ++#define UFS_UTRLDBR_OFF 0x58 ++#define UFS_UTRLCLR_OFF 0x5C ++#define UFS_UTRLRSR_OFF 0x60 ++#define UFS_UTMRLBA_OFF 0x70 ++#define UFS_UTMRLBAU_OFF 0x74 ++#define UFS_UTMRLDBR_OFF 0x78 ++#define UFS_UTMRLCLR_OFF 0x7C ++#define UFS_UTMRLRSR_OFF 0x80 ++#define UFS_UICCMD_OFF 0x90 ++#define UFS_UICCMDARG1_OFF 0x94 ++#define UFS_UICCMDARG2_OFF 0x98 ++#define UFS_UICCMDARG3_OFF 0x9C ++#define UFS_BUSTHRTL_OFF 0xC0 ++#define UFS_OOCPR_OFF 0xC4 ++#define UFS_FEIE_OFF 0xC8 ++#define UFS_CDACFG_OFF 0xD0 ++#define UFS_CDATX1_OFF 0xD4 ++#define UFS_CDATX2_OFF 0xD8 ++#define UFS_CDARX1_OFF 0xDC ++#define UFS_CDARX2_OFF 0xE0 ++#define UFS_CDASTA_OFF 0xE4 ++#define UFS_LBMCFG_OFF 0xF0 ++#define UFS_LBMSTA_OFF 0xF4 ++#define UFS_DBG_OFF 0xF8 ++#define UFS_HCLKDIV_OFF 0xFC ++ ++/* Controller capability masks and shift value */ ++#define DWC_UFS_NUTRS_MASK 0x0000001f ++#define DWC_UFS_NUTMRS_MASK 0x00070000 ++#define DWC_UFS_NUTMRS_SHIFT 16 ++#define DWC_UFS_AUTOH8 0x00800000 ++#define DWC_UFS_AUTOH8_SHIFT 23 ++ ++/* GenSelectorIndex */ ++#define MTX_L0 0x0000 ++#define MTX_L1 0x0001 ++#define MRX_L0 0x0004 ++#define MRX_L1 0x0005 ++ ++#define ATTR_M_SHIFT 16 ++#define attr_mtx0(x) ((x << ATTR_M_SHIFT) | MTX_L0) ++#define attr_mtx1(x) ((x << ATTR_M_SHIFT) | MTX_L1) ++#define attr_mrx0(x) ((x << ATTR_M_SHIFT) | MRX_L0) ++#define attr_mrx1(x) ((x << ATTR_M_SHIFT) | MRX_L1) ++#define attr_mcb(x) ((u32)((u16)(x) << ATTR_M_SHIFT)) ++ ++/* ++ * Register Fields ++ */ ++#define UFS_AUTO_HIBERNATE_BIT BIT(23) ++#define UFS_CAPS_64AS_BIT BIT(24) ++#define UFS_HCE_RESET_BIT BIT(0) ++#define UFS_HCS_DP_BIT BIT(0) ++#define UFS_HCS_UCRDY_BIT BIT(3) ++#define UFS_HCS_UPMCRS_OFF (8) ++#define UFS_HCS_UPMCRS_MASK (0x3 << UFS_HCS_UPMCRS_OFF) ++#define UFS_IS_UE_BIT BIT(2) ++#define UFS_IS_UPMS_BIT BIT(4) ++#define UFS_IS_UHXS_BIT BIT(5) ++#define UFS_IS_UHES_BIT BIT(6) ++#define UFS_IS_ULSS_BIT BIT(8) ++#define UFS_IS_UCCS_BIT BIT(10) ++#define UFS_UTP_RUN_BIT BIT(0) ++#define UFS_LBMCFG_DEFAULT_VALUE 0xb01 ++#define UFS_HCLKDIV_NORMAL_VALUE 0xFA ++#define UFS_HCLKDIV_SLOW_VALUE 0x14 ++#define UFS_HCLKDIV_FPGA_VALUE 0x12 ++ ++#define UFS_AHIT_AH8ITV_MASK (0x3FF) ++#define UFS_AHIT_AUTOH8_TIMER (0x1001) ++ ++#define UIC_LINK_STARTUP_CMD 0x16 ++#define NOP_TRANS_TYPE 0x20 ++#define BAD_SLOT 0x55 ++#define NOP_TRANS_TYPE 0x20 ++#define PRDT_BUFFER_SIZE 0x40000 ++#define UNIT_DESCS_COUNT 8 ++#define CAPACITY_DATA_LENGTH 8 ++#define SENSE_DATA_LENGTH 20 ++#define SENSE_KEY_INDEX 36 ++#define RPMB_DATA_SIZE 256 ++#define RPMB_FRAME_SIZE 512 ++#define LOGICAL_BLK_SIZE 4096 ++#define UFS_DWORD_SHIFT 2 ++ ++#define QUERY_DESC_MANUFACTURER_NAME_MAX_SIZE 0x12 ++#define QUERY_DESC_PRODUCT_NAME_MAX_SIZE 0x22 ++#define QUERY_DESC_STRING_MAX_SIZE 0xFE ++ ++#ifdef UFS_DEBUG ++#define ufs_pr_dbg(fmt, s...) printf(fmt, ##s) ++#define ufs_pr_mem(fmt, s...) printf(fmt, ##s) ++#define ufs_pr_reg(fmt, s...) printf(fmt, ##s) ++#define ufs_pos() printf("pos -- %s: %d\n", __func__, __LINE__) ++#else ++#define ufs_pr_dbg(s...) ++#define ufs_pr_mem(s...) ++#define ufs_pr_reg(s...) ++#define ufs_pos() ++#endif ++ ++/* Data structure sizes in bytes */ ++enum { ++ DWC_UFS_BASIC_UPIU_SIZE = 32, ++ DWC_UFS_UTRD_SIZE = 32, ++ DWC_UFS_TSF_SIZE = 16, ++ DWC_UFS_PRD_SIZE = 16, ++ ++ DWC_UFSHCD_MAX_PRD_SIZE = 8, /* 128 (Linux) */ ++ ++ DWC_MAX_QUERY_DATA = 256, ++ DWC_UFS_CDB_SIZE = 16, ++ DWC_UFS_SENSE_DATA_SIZE = 18, ++ ++ DWC_UFS_UTMRD_HDR_SIZE = 16, ++}; ++ ++/* Alignment Requirement in bytes */ ++enum { ++ DWC_UTRL_BASE_ALIGN = 1024, ++ DWC_UCD_ALIGN = 512, ++ DWC_CMD_BASE_ALIGN = 128, ++}; ++ ++/* Rate */ ++enum UFS_RATE { ++ UFS_RATE_A = 1, ++ UFS_RATE_B = 2 ++}; ++ ++/* Power mode */ ++enum POWER_MODE { ++ FAST_MODE = 0x11, ++ SLOW_MODE = 0x22, ++ FASTAUTO_MODE = 0x44, ++ SLOWAUTO_MODE = 0x55, ++ INVALID_MODE = 0xFF ++}; ++ ++enum ufs_pwm_gear_tag { ++ UFS_PWM_DONT_CHANGE, ++ UFS_PWM_G1, ++ UFS_PWM_G2, ++ UFS_PWM_G3, ++ UFS_PWM_G4, ++ UFS_PWM_G5, ++ UFS_PWM_G6, ++ UFS_PWM_G7, ++}; ++ ++enum ufs_hs_gear_tag { ++ UFS_HS_DONT_CHANGE, ++ UFS_HS_G1, ++ UFS_HS_G2, ++ UFS_HS_G3, ++}; ++ ++struct pwr_mode_params { ++ uint8_t tx_gear; ++ uint8_t rx_gear; ++ uint8_t hs_series; ++ uint8_t tx_lanes; ++ uint8_t rx_lanes; ++ uint8_t pwr_mode; ++}; ++ ++#define WELL_BOOT_LU_A 0x01 ++#define WELL_BOOT_LU_B 0x02 ++ ++#define DEFAULT_BOOT_LUN WELL_BOOT_LU_A ++#define DEFAULT_ACTIVE_LUN 3 ++ ++#define DEFAULT_MODE FAST_MODE ++#define DEFAULT_GEAR UFS_HS_G3 ++#define DEFAULT_RATE UFS_RATE_B ++#define DEFAULT_LANE 2 ++ ++#pragma pack(push) ++#pragma pack(1) ++ ++/* ++ * Command UPIU Structure ++ */ ++struct dwc_ufs_cmd_upiu { ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t lun; ++ uint8_t task_tag; ++ uint8_t cmd_set_type; ++ uint8_t reserved_1_0; ++ uint8_t reserved_1_1; ++ uint8_t reserved_1_2; ++ uint8_t tot_ehs_len; ++ uint8_t reserved_2; ++ uint16_t data_seg_len; ++ uint32_t exp_data_xfer_len; ++ uint8_t cdb[16]; /* 16 cdb size */ ++}; ++ ++/* ++ * Query UPIU Structure ++ */ ++struct dwc_ufs_query_upiu { ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t reserved_1; ++ uint8_t task_tag; ++ uint8_t reserved_2; ++ uint8_t query_func; ++ uint8_t query_resp; ++ uint8_t reserved_3; ++ uint8_t tot_ehs_len; ++ uint8_t reserved_4; ++ uint16_t data_seg_len; ++ uint8_t tsf[16]; /* 16 tsf size */ ++ uint32_t reserved_5; ++}; ++ ++/* ++ * NOP OUT UPIU Structure ++ */ ++struct dwc_ufs_nop_req_upiu { ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t reserved_1; ++ uint8_t task_tag; ++ uint32_t reserved_2; ++ uint8_t tot_ehs_len; ++ uint8_t reserved_3; ++ uint16_t data_seg_len; ++ uint8_t reserved_4[20]; /* 20 reserved */ ++}; ++ ++/* ++ * NOP IN UPIU Structure ++ */ ++struct dwc_ufs_nop_resp_upiu { ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t reserved_1; ++ uint8_t task_tag; ++ uint8_t reserved_2_0; ++ uint8_t reserved_2_1; ++ uint8_t response; ++ uint8_t reserved_3; ++ uint8_t tot_ehs_len; ++ uint8_t device_info; ++ uint16_t data_seg_len; ++ uint8_t reserved_4[20]; /* 20 reserved */ ++}; ++ ++/* ++ * Transfer Response UPIU Structure ++ */ ++struct dwc_ufs_resp_upiu { ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t lun; ++ uint8_t task_tag; ++ uint8_t cmd_set_type; ++ uint8_t reserved_1; ++ uint8_t response; ++ uint8_t status; ++ uint8_t tot_ehs_len; ++ uint8_t device_info; ++ uint16_t data_seg_len; ++ uint32_t residual_xfer_count; ++ uint32_t reserved_2; ++ uint32_t reserved_3; ++ uint32_t reserved_4; ++ uint32_t reserved_5; ++ uint16_t sense_data_len; ++ uint8_t sense_data[18]; /* 18 sense size */ ++}; ++ ++struct dwc_ufs_prd { ++ uint32_t base_addr; ++ uint32_t upper_addr; ++ uint32_t reserved1; ++ uint32_t size; ++}; ++ ++/** ++ * dwc_ufs_ucd ++ * UTP Command Descriptor (UCD) structure. ++ * Every UTRD contains a pointer for this data structure ++ * This structure logically consists of 3 parts ++ * 1. "Transfer Request" or "Command UPIU" (SCSI, Native UFS & DM) ++ * 2. "Transfer Response" or "Response UPIU" ++ * 3. "Physical Region Description Table"(PRDT). ++ * The data structure should support 32 bit or 64 bit memory buffer address ++ * space. ++ * "Transfer Request" and "Transfer Response" are in BIG ENDIAN Format ++ * "PRDT" is in LITTLE ENDIAN Format ++ */ ++struct dwc_ufs_ucd { ++ uint8_t cmd_upiu[DWC_UCD_ALIGN]; ++ uint8_t resp_upiu[DWC_UCD_ALIGN]; ++ struct dwc_ufs_prd prdt[DWC_UFSHCD_MAX_PRD_SIZE]; ++}; ++ ++struct dwc_ufs_utrd { ++ uint8_t reserved_1_0:8; ++ uint8_t reserved_1_1:8; ++ uint8_t reserved_1_2:8; ++ uint8_t ct_and_flags:8; ++ uint32_t reserved_2:32; ++ uint8_t ocs:8; ++ uint8_t reserved_3_0:8; ++ uint8_t reserved_3_1:8; ++ uint8_t reserved_3_2:8; ++ uint32_t reserved_4:32; ++ ++ /* Only bits 31:7 are valid; 128B Aligned addr */ ++ uint32_t ucdba:32; ++ uint32_t ucdbau:32; ++ uint16_t resp_upiu_length:16; ++ uint16_t resp_upiu_offset:16; ++ ++ uint16_t prdt_length:16; ++ uint16_t prdt_offset:16; ++}; ++ ++/** ++ * dwc_ufs_tm_req_upiu ++ * Task Management Request UPIU structure ++ * Size of this structure is 32 bytes ++ * The data structure should support 32 bit or 64 bit memory buffer address ++ * space. This structure is in BIG ENDINA Format ++ */ ++struct dwc_ufs_tm_req_upiu { /* BIG ENDIAN */ ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t lun; ++ uint8_t task_tag; ++ uint8_t reserved_1; ++ uint8_t tm_fn; ++ uint8_t reserved_2_0; ++ uint8_t reserved_2_1; ++ uint8_t tot_ehs_len; ++ uint8_t reserved_3; ++ uint16_t data_seg_len; ++ uint32_t input_param_1; ++ uint32_t input_param_2; ++ uint32_t input_param_3; ++ uint32_t reserved_4; ++ uint32_t reserved_5; ++}; ++ ++/** ++ * dwc_ufs_tm_resp_upiu ++ * Task Management Response UPIU structure ++ * Size of this structure is 32 bytes ++ * The data structure should support 32 bit or 64 bit memory buffer address ++ * space. This structure is in BIG ENDINA Format ++ */ ++struct dwc_ufs_tm_resp_upiu { /* BIG ENDIAN */ ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t lun; ++ uint8_t task_tag; ++ uint8_t reserved_1_0; ++ uint8_t reserved_1_1; ++ uint8_t response; ++ uint8_t reserved_2; ++ uint8_t tot_ehs_len; ++ uint8_t reserved_3; ++ uint16_t data_seg_len; ++ uint32_t output_param_1; ++ uint32_t output_param_2; ++ uint32_t reserved_4; ++ uint32_t reserved_5; ++ uint32_t reserved_6; ++}; ++ ++struct dwc_ufs_utmrd { ++ uint8_t reserved_1_0:8; ++ uint8_t reserved_1_1:8; ++ uint8_t reserved_1_2:8; ++ uint8_t intr_flag:8; ++ uint32_t reserved_2:32; ++ uint8_t ocs:8; ++ uint8_t reserved_3_0:8; ++ uint8_t reserved_3_1:8; ++ uint8_t reserved_3_2:8; ++ uint32_t reserved_4:32; ++ ++ struct dwc_ufs_tm_req_upiu tm_req_upiu; ++ struct dwc_ufs_tm_resp_upiu tm_resp_upiu; ++}; ++ ++struct query_param { ++ uint8_t opcode; ++ uint8_t idn; ++ uint8_t index; ++ uint8_t selector; ++}; ++ ++struct cmd_param { ++ uint64_t addr; ++ uint64_t src; ++ uint32_t size; ++}; ++ ++#pragma pack(pop) ++ ++struct partition_desc { ++ uint8_t boot_lun_id; ++ uint8_t write_protect; ++ uint8_t memory_type; ++ uint8_t data_reliability; ++ uint8_t block_size; ++ uint8_t prov_type; ++ uint16_t context_capabilities; ++ uint32_t lu_capacity; ++#define UFS_MAX_LU_CAP 0xFFFFFFFF ++ uint32_t num_alloc_units; ++}; ++ ++struct configuration_header { ++ uint8_t b_boot_enable; ++ uint8_t b_descr_access_en; ++ uint8_t b_init_power_mode; ++ uint8_t b_high_priority_lun; ++ uint8_t b_secure_removal_type; ++ uint8_t b_init_active_icc_level; ++ uint16_t w_periodic_rtc_update; ++}; ++ ++struct partition_desc_table { ++ uint8_t b_number_lu; ++ uint8_t b_allocation_unit_size; ++ uint32_t d_segment_size; ++ uint64_t q_total_raw_device_capacity; ++ uint8_t b_data_ordering; ++ uint8_t b_max_contex_id_number; ++ uint16_t w_supported_memory_types; ++ uint32_t d_system_code_max_alloc_u; ++ uint16_t w_system_code_cap_adj_fac; ++ uint32_t d_non_persist_max_alloc_u; ++ uint16_t w_non_persist_cap_adj_fac; ++ uint32_t d_enhanced1_max_alloc_u; ++ uint16_t w_enhanced1_cap_adj_fac; ++ uint32_t d_enhanced2_max_alloc_u; ++ uint16_t w_enhanced2_cap_adj_fac; ++ struct configuration_header *p_conf_header; ++ struct partition_desc *partition_desc_ptr[UNIT_DESCS_COUNT]; ++}; ++ ++struct desc_params { ++ void *req_upiu; ++ struct configuration_header *conf_head; ++ struct partition_desc **part_desc; ++ uint8_t opcode; ++ uint8_t desc_idn; ++ uint8_t desc_index; ++ uint16_t length; ++}; ++ ++/* UFS: Device descriptor */ ++struct ufs_device_descriptor { ++ uint8_t b_length; ++ uint8_t b_descriptor_idn; ++ uint8_t b_device; ++ uint8_t b_device_class; ++ uint8_t b_device_sub_class; ++ uint8_t b_protocol; ++ uint8_t b_number_lu; ++ uint8_t b_number_wlu; ++ uint8_t b_boot_enable; ++ uint8_t b_descr_access_en; ++ uint8_t b_init_power_mode; ++ uint8_t b_high_priority_lun; ++ uint8_t b_secure_removal_type; ++ uint8_t b_security_lu; ++ uint8_t b_background_ops_term_lat; ++ uint8_t b_init_active_icc_level; ++ uint16_t w_spec_version; ++ uint16_t w_manufacture_date; ++ uint8_t i_manufacturer_name; ++ uint8_t i_product_name; ++ uint8_t i_serial_number; ++ uint8_t i_oem_id; ++ uint16_t w_manufacturer_id; ++ uint8_t b_ud_0base_offset; ++ uint8_t b_ud_config_plength; ++ uint8_t b_device_rtt_cap; ++ uint16_t w_periodic_rtc_update; ++ uint8_t b_ufs_feature_support; ++ uint8_t b_ffu_timeout; ++ uint8_t b_queue_depth; ++ uint16_t w_device_version; ++ uint8_t b_num_secure_wp_area; ++ uint32_t d_psa_max_data_size; ++ uint8_t b_psa_state_timeout; ++ uint8_t i_product_revision_level; ++ uint8_t reserved[5]; /* 5 reserved */ ++ uint8_t reserved_ume[16]; /* 16 reserved */ ++} __attribute__ ((packed)); ++ ++/* UFS: Geometry descriptor */ ++struct ufs_geometry_descriptor { ++ uint8_t b_length; ++ uint8_t b_descriptor_idn; ++ uint8_t b_media_technology; ++ uint8_t reserved; ++ uint64_t q_total_raw_device_capacity; ++ uint8_t b_max_number_lu; ++ uint32_t d_segment_size; ++ uint8_t b_allocation_unit_size; ++ uint8_t b_min_addr_block_size; ++ uint8_t b_optimal_read_block_size; ++ uint8_t b_optimal_write_block_size; ++ uint8_t b_max_in_buffer_size; ++ uint8_t b_max_out_buffer_size; ++ uint8_t b_rpmb_read_write_size; ++ uint8_t b_dynamic_capacity_resource_policy; ++ uint8_t b_data_ordering; ++ uint8_t b_max_contex_id_number; ++ uint8_t b_sys_data_tag_unit_size; ++ uint8_t b_sys_data_tag_res_size; ++ uint8_t b_supported_sec_rtypes; ++ uint16_t w_supported_memory_types; ++ uint32_t d_system_code_max_alloc_u; ++ uint16_t w_system_code_cap_adj_fac; ++ uint32_t d_non_persist_max_alloc_u; ++ uint16_t w_non_persist_cap_adj_fac; ++ uint32_t d_enhanced1_max_alloc_u; ++ uint16_t w_enhanced1_cap_adj_fac; ++ uint32_t d_enhanced2_max_alloc_u; ++ uint16_t w_enhanced2_cap_adj_fac; ++ uint32_t d_enhanced3_max_alloc_u; ++ uint16_t w_enhanced3_cap_adj_fac; ++ uint32_t d_enhanced4_max_alloc_u; ++ uint16_t w_enhanced4_cap_adj_fac; ++ uint32_t d_optimal_logical_block_size; ++} __attribute__ ((packed)); ++ ++/* UFS: String descriptor */ ++struct ufs_string_descriptor { ++ char manufacturer_name[QUERY_DESC_MANUFACTURER_NAME_MAX_SIZE]; ++ char product_name[QUERY_DESC_PRODUCT_NAME_MAX_SIZE]; ++ char serial_number[QUERY_DESC_STRING_MAX_SIZE]; ++ char oem_id[QUERY_DESC_STRING_MAX_SIZE]; ++}; ++ ++/* UFS: Health descriptor */ ++struct ufs_health_descriptor { ++ uint8_t b_length; ++ uint8_t b_descriptor_idn; ++ uint8_t b_pre_eol_info; ++ uint8_t b_device_life_time_est_a; ++ uint8_t b_device_life_time_est_b; ++ uint8_t reserved[32]; /* 32 reserved */ ++} __attribute__ ((packed)); ++ ++/* UFS: Interconnect descriptor */ ++struct ufs_interconnect_descriptor { ++ uint8_t b_length; ++ uint8_t b_descriptor_idn; ++ uint16_t bcd_unipro_version; ++ uint16_t bcd_mphy_version; ++} __attribute__ ((packed)); ++ ++struct ufs_unit_index_descriptror { ++ uint8_t b_length; ++ uint8_t b_descriptor_idn; ++ uint8_t b_unit_index; ++ uint8_t b_lu_enable; ++ uint8_t b_boot_lun_id; ++ uint8_t b_lu_write_protect; ++ uint8_t b_lu_queue_depth; ++ uint8_t b_psa_sensitive; ++ uint8_t b_memory_type; ++ uint8_t b_data_reliability; ++ uint8_t b_logical_block_size; ++ uint64_t q_logical_block_count; ++ uint32_t d_erase_block_size; ++ uint8_t b_provisioning_type; ++ uint64_t q_phy_mem_resource_count; ++ uint16_t w_context_capabilities; ++ uint8_t b_large_unit_granularity_m1; ++} __attribute__ ((packed)); ++ ++struct ufs_unit_rpmb_descriptror { ++ uint8_t b_length; ++ uint8_t b_descriptor_idn; ++ uint8_t b_unit_index; ++ uint8_t b_lu_enable; ++ uint8_t b_boot_lun_id; ++ uint8_t b_lu_write_protect; ++ uint8_t b_lu_queue_depth; ++ uint8_t b_psa_sensitive; ++ uint8_t b_memory_type; ++ uint8_t reserved1; ++ uint8_t b_logical_block_size; ++ uint64_t q_logical_block_count; ++ uint32_t d_erase_block_size; ++ uint8_t b_provisioning_type; ++ uint64_t q_phy_mem_resource_count; ++ uint8_t reserved2[3]; /* 3 reserved */ ++} __attribute__ ((packed)); ++ ++struct ufs_unit_descriptor { ++ struct ufs_unit_index_descriptror unit_index_desc[UNIT_DESCS_COUNT]; ++ struct ufs_unit_rpmb_descriptror unit_rpmb_desc; ++} __attribute__ ((packed)); ++ ++struct ufs_dev_desc_configuration_param { ++ uint8_t b_length; ++ uint8_t b_descriptor_idn; ++ uint8_t b_conf_desc_continue; ++ uint8_t b_boot_enable; ++ uint8_t b_descr_access_en; ++ uint8_t b_init_power_mode; ++ uint8_t b_high_priority_lun; ++ uint8_t b_secure_removal_type; ++ uint8_t b_init_active_icc_level; ++ uint16_t w_periodic_rtc_update; ++ uint8_t reserved[5]; /* 5 reserved, 11 in ufs3.1 */ ++} __attribute__ ((packed)); ++ ++struct ufs_unit_desc_configuration_param { ++ uint8_t b_lu_enable; ++ uint8_t b_boot_lun_id; ++ uint8_t b_lu_write_protect; ++ uint8_t b_memory_type; ++ uint32_t d_num_alloc_units; ++ uint8_t b_data_reliability; ++ uint8_t b_logical_block_size; ++ uint8_t b_provisioning_type; ++ uint16_t w_context_capabilities; ++ uint8_t reserved[3]; /* 3 reserved, 13 in ufs3.1 */ ++} __attribute__ ((packed)); ++ ++struct ufs_configuration_descriptor { ++ struct ufs_dev_desc_configuration_param dev_desc_conf_param; ++ struct ufs_unit_desc_configuration_param unit_desc_conf_param[UNIT_DESCS_COUNT]; ++} __attribute__ ((packed)); ++ ++struct ufs_descriptor { ++ uint32_t desc_is_init; ++ struct ufs_device_descriptor dev_desc; ++ struct ufs_string_descriptor str_desc; ++ struct ufs_geometry_descriptor geo_desc; ++ struct ufs_unit_descriptor unit_desc; ++ struct ufs_configuration_descriptor conf_desc; ++ struct ufs_health_descriptor heal_desc; ++ struct ufs_interconnect_descriptor intr_desc; ++}; ++ ++/** ++ * struct dwc_ufs_hcd_lrb ++ * Local Reference Block for application commands (eg:scsi) ++ * Maintained for every utrd slot ++ * @command_type: Maintained to abstract the application layer out of core ++ * @data_direction: whether command is a read/write or no-data command ++ * @ocs: ocs from utrd is read and kept here for future analysis ++ * @xfer_command_status: holds the response from response-upiu(eg: Scsi status) ++ * @transfer_size: total size of transfer in bytes ++ * @task_tag: task_tag ++ * @lun: lun ++ * @scmd: scsi command; should be null if its not a scsi command ++ * @utrd: transfer descriptor address pointer ++ * @cmd_upiu: address of command upiu ++ * @resp_upiu: address of response upiu ++ * @prdt: base address of prd table ++ * @sense_buffer_len: sense buffer length in bytes ++ * @sense_buffer: pointer to sense buffer for the command ++ */ ++struct dwc_ufs_hcd_lrb { ++ uint8_t command_type; ++ uint8_t data_direction; ++ uint8_t read_write_flags; ++ uint8_t ocs; ++ uint8_t xfer_command_status; ++ ++ uint32_t transfer_size; ++ uint32_t task_tag; ++ uint32_t lun; ++ ++ struct dwc_ufs_utrd *utrd; ++ struct dwc_ufs_cmd_upiu *cmd_upiu; ++ struct dwc_ufs_resp_upiu *resp_upiu; ++ struct dwc_ufs_prd *prdt; ++ ++ uint16_t sense_buffer_len; ++ uint8_t *sense_buffer; ++}; ++ ++/** ++ * struct dwc_ufshcd_dm_lrb ++ * Local Reference Block for Device management commands (eg: nopout, query ..) ++ * Maintained one per driver instance ++ * @trans_type: Transaction Type (query/nopout ..) ++ * @flags:flags indicating Read/Write ++ * @lun: lun to be addressed through this command ++ * @query_fn: query_function ++ * @tot_ehs_len: total ehs length ++ * @data_seg_len: data segment length for this command ++ * @tsf: transaction specific field for this command ++ * @dm_cmd_results: Device management function result updated after ++ * post processing ++ */ ++struct dwc_ufshcd_dm_lrb { ++ uint8_t trans_type; ++ uint8_t flags; ++ uint8_t lun; ++ /* We cant fix task tag; it is dynamically set */ ++ uint8_t query_fn; ++ uint8_t tot_ehs_len; ++ uint16_t data_seg_len; ++ uint8_t tsf[DWC_UFS_TSF_SIZE]; ++ ++ int dm_cmd_results; ++}; ++ ++/** ++ * struct dwc_ufs_hba ++ * Private structure of the host bus adapter ++ * @caps: DWC UFS HC capabilities stored here for reference ++ * @ufs_version: UFS version read adn kept here ++ * @nutrs: Transfer Request Queue depth supported by DWC UFS HC ++ * @nutmrs: Task Management Queue depth supported by DWC UFS HC ++ * @utrl_base_addr: UTP Transfer Request Descriptor List base address (virtual) ++ * @utmrl_base_addr: UTP Task Management Descriptor List base address (virtual) ++ * @ucdl_base_addr: UFS Command Descriptor List Base Address (virtual) ++ * @lrb: pointer to local reference block list ++ * @dm_lrb: local reference block device management commands ++ * @outstanding_xfer_reqs: outstanding transfer requests to be processed ++ */ ++struct dwc_ufs_hba { ++ uint32_t caps; ++ uint8_t nutrs; ++ uint8_t nutmrs; ++ uint8_t autoh8; ++ ++ uint8_t active_lun; ++ uint8_t active_bootlun; ++ uint8_t lu_request_sense_sent[UNIT_DESCS_COUNT]; ++ uint8_t unit_offset; ++ uint8_t unit_length; ++ uint16_t manufacturer_id; ++#define UFS_MANUFACTURER_ID_TOSHIBA 0x0198 ++#define UFS_MANUFACTURER_ID_HYNIX 0x01AD ++#define UFS_MANUFACTURER_ID_SAMSUNG 0x01CE ++#define UFS_MANUFACTURER_ID_HI1861 0x08B6 ++#define UFS_MANUFACTURER_ID_MICRON 0x012C ++#define UFS_MANUFACTURER_ID_SANDISK 0x0145 ++ uint16_t dev_spec_version; ++ uint16_t manufacturer_date; ++ ++ /* Virtual memory reference for driver */ ++ void *mem_pool; ++ void *wr_buf; ++ struct dwc_ufs_utrd *utrl_base_addr; ++ struct dwc_ufs_utmrd *utmrl_base_addr; ++ struct dwc_ufs_ucd *ucdl_base_addr; ++ ++ struct dwc_ufs_hcd_lrb *lrb; ++ struct dwc_ufshcd_dm_lrb dm_lrb; ++ ++ /* Outstanding requests */ ++ uint32_t outstanding_xfer_reqs; ++ ++ uint8_t is_init; ++}; ++ ++/* Byte swap u16 */ ++static inline uint16_t swap_16(uint16_t val) ++{ ++ return (uint16_t)((val << 8) | (val >> 8)); /* shift 8 */ ++} ++ ++/* Byte swap unsigned int */ ++static inline uint32_t swap_32(uint32_t val) ++{ ++ val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); /* shift 8 */ ++ return (uint32_t)((val << 16) | (val >> 16)); /* shift 16 */ ++} ++ ++static inline uint32_t to_bigendian32(uint32_t val) ++{ ++#ifdef HOST_BIG_ENDIAN ++ return val; ++#else ++ return swap_32(val); ++#endif ++} ++ ++static inline uint32_t to_littleendian32(uint32_t val) ++{ ++#ifdef HOST_BIG_ENDIAN ++ return swap_32(val); ++#else ++ return val; ++#endif ++} ++ ++static inline uint16_t to_bigendian16(uint16_t val) ++{ ++#ifdef HOST_BIG_ENDIAN ++ return val; ++#else ++ return swap_16(val); ++#endif ++} ++ ++static inline uint16_t to_littleendian16(uint16_t val) ++{ ++#ifdef HOST_BIG_ENDIAN ++ return swap_16(val); ++#else ++ return val; ++#endif ++} ++ ++static inline void ufs_waitms(uint32_t delay) ++{ ++ while (delay--) ++ udelay(1000); /* delay 1000us */ ++} ++ ++/* UFS Command Opcodes */ ++#define READ_DESC_OPCODE 0x1 ++#define WRITE_DESC_OPCODE 0x2 ++#define READ_ATTR_OPCODE 0x3 ++#define WRITE_ATTR_OPCODE 0x4 ++#define READ_FLAG_OPCODE 0x5 ++#define SET_FLAG_OPCODE 0x6 ++#define CLEAR_FLAG_OPCODE 0x7 ++#define TOGGLE_FLAG_OPCODE 0x8 ++ ++/* Descriptor Idn's */ ++#define DEVICE_DESC 0x00 ++#define CONFIGURATION_DESC 0x01 ++#define UNIT_DESC 0x02 ++#define INTERCONNECT_DESC 0x04 ++#define STRING_DESC 0x05 ++#define GEOMETRY_DESC 0x07 ++#define POWER_DESC 0x08 ++#define HEALTH_DESC 0x09 ++ ++#define DEVICE_DESC_LENGTH 0x40 ++#define UNIT_DESC_LENGTH 0x23 ++#define GEOMETRY_DESC_LENGTH 0x44 ++#define CONFIGURATION_DESC_LENGTH 0x90 ++#define INTERCONNECT_DESC_LENGTH 0x06 ++#define STRING_DESC_LENGTH 0xFE ++#define POWER_DESC_LENGTH 0x62 ++#define HEALTH_DESC_LENGTH 0x25 ++#define MAX_DESC_LENGTH 0xFF ++ ++#define STANDARD_RD_REQ 0x01 ++#define STANDARD_WR_REQ 0x81 ++ ++#define QUERY_RESPONSE_HEAD_OFFSET 32 ++#define QUERY_RESPONSE_DATA_OFFSET 2 ++ ++#define UTP_UFS_STORAGE_COMMAND (1 << 4) ++ ++enum info_show_type { ++ UFS_INFO_SHOW_DEVICE_DESC = 0x00, ++ UFS_INFO_SHOW_CONFIGURATION_DESC = 0x01, ++ UFS_INFO_SHOW_UNIT_DESC = 0x02, ++ UFS_INFO_SHOW_INTERCONNECT_DESC = 0x04, ++ UFS_INFO_SHOW_STRING_DESC = 0x05, ++ UFS_INFO_SHOW_GEOMETRY_DESC = 0x07, ++ UFS_INFO_SHOW_HEALTH_DESC = 0x09, ++ UFS_INFO_SHOW_ALL = 0x0E, ++ UFS_INFO_SHOW_BASIC = 0x0F, ++}; ++ ++/* ++ * UFS Status/Error Macros used as return values for all functions ++ */ ++enum { ++ UFS_SUCCESS = 0x00, ++ UFS_FAILURE = -0x01, ++ UFS_LINK_STARTUP_FAIL = -0x02, ++ UFS_UTRD_DOORBELL_TIMEOUT = -0x03, ++ UFS_NOP_RESP_FAIL = -0x04, ++ UFS_INVALID_NOP_IN = -0x05, ++ UFS_UTMRD_DOORBELL_TIMEOUT = -0x06, ++ UFS_FDEVICE_INIT_FAIL = -0x07, ++ ++ UFS_SOFTWARE_ERROR = -0x0F, ++ ++ /* response upiu status error */ ++ RESP_STAT_CONDITION_MET = -0x14, ++ RESP_STAT_BUSY = -0x15, ++ RESP_STAT_RESERVATION_CONFLICT = -0x16, ++ RESP_STAT_TASK_SET_FULL = -0x17, ++ RESP_STAT_ACA_ACTIVE = -0x18, ++ RESP_STAT_TASK_ABORTED = -0x19, ++ RESP_STAT_UNKNOWN = -0x1F, ++ ++#define RET_SENSE_KEY_OFF (0x20) ++ /* sense key */ ++ NO_SENSE = -0x20, ++ RECOVERED_ERROR = -0x21, ++ NOT_READY = -0x22, ++ MEDIUM_ERROR = -0x23, ++ HARDWARE_ERROR = -0x24, ++ ILLEGAL_REQUEST = -0x25, ++ UNIT_ATTENTION = -0x26, ++ DATA_PROTECT = -0x27, ++ BLANK_CHECK = -0x28, ++ VENDOR_SPECIFIC = -0x29, ++ ABORTED_COMMAND = -0x2B, ++ VOLUME_OVERFLOW = -0x2D, ++ MISCOMPARE = -0x2E, ++ ++ /* RPMB Operation Results */ ++ UFS_RPMB_GENERAL_FAILURE = -0x31, ++ UFS_AUTHENTICATION_FAILURE = -0x32, ++ UFS_COUNTER_FAILURE = -0x33, ++ UFS_ADDRESS_FAILURE = -0x34, ++ UFS_WRITE_FAILURE = -0x35, ++ UFS_READ_FAILURE = -0x36, ++ UFS_AUTH_KEY_NOT_PROGRAMMED = -0x37, ++ UFS_RPMB_WR_COUNTER_EXPIRED = -0x38, ++ UFS_NONCE_MISMATCH = -0x39, ++ UFS_MAC_MISMATCH = -0x3a, ++ ++#define RET_UIC_CONFIG_ERROR_OFF (0xA0) ++ /* UIC Config Result Error */ ++ UFS_UIC_TIMEOUT = -0xA0, ++ INVALID_MIB_ATTRIBUTE = -0xA1, ++ INVALID_MIB_ATTRIBUTE_VALUE = -0xA2, ++ READ_ONLY_MIB_ATTRIBUTE = -0xA3, ++ WRITE_ONLY_MIB_ATTRIBUTE = -0xA4, ++ BAD_INDEX = -0xA5, ++ LOCKED_MIB_ATTRIBUTE = -0xA6, ++ BAD_TEST_FEATURE_INDEX = -0xA7, ++ PEER_COMMUNICATION_FAILURE = -0xA8, ++ BUSY = -0xA9, ++ DME_FAILURE = -0xAA, ++ ++#define RET_UTRD_OCS_ERROR_OFF (0xB0) ++ /* utrd ocs error */ ++ INVALID_COMMAND_TABLE_ATTRIBUTES = -0xB1, ++ INVALID_PRDT_ATTRIBUTES = -0xB2, ++ MISMATCH_DATA_BUFFER_SIZE = -0xB3, ++ MISMATCH_RESPONSE_UPIU_SIZE = -0xB4, ++ COMMUNICATION_FAILURE = -0xB5, ++ ABORTED = -0xB6, ++ FATAL_ERROR = -0xB7, ++ INVALID_OCS_VALUE = -0xBF, ++ ++ /* Query response code */ ++ QUERY_PARAMETER_NOT_READABLE = -0xF6, ++ QUERY_PARAMETER_NOT_WRITEABLE = -0xF7, ++ QUERY_PARAMETER_ALREADY_WRITTEN = -0xF8, ++ QUERY_INVALID_LENGTH = -0xF9, ++ QUERY_INVALID_VALUE = -0xFA, ++ QUERY_INVALID_SELECTOR = -0xFB, ++ QUERY_INVALID_INDEX = -0xFC, ++ QUERY_INVALID_IDN = -0xFD, ++ QUERY_INVALID_OPCODE = -0xFE, ++ QUERY_GENERAL_FAILURE = -0xFF ++}; ++ ++/* DME Commands */ ++enum { ++ DWC_UFS_DME_GET = 0x01, ++ DWC_UFS_DME_SET = 0x02, ++ DWC_UFS_DME_PEER_GET = 0x03, ++ DWC_UFS_DME_PEER_SET = 0x04, ++ DWC_UFS_DME_POWERON = 0x10, ++ DWC_UFS_DME_POWEROFF = 0x11, ++ DWC_UFS_DME_ENABLE = 0x12, ++ DWC_UFS_DME_RESET = 0x14, ++ DWC_UFS_DME_ENDPOINTRESET = 0x15, ++ DWC_UFS_DME_LINKSTARTUP = 0x16, ++ DWC_UFS_DME_HIBERNATE_ENTER = 0x17, ++ DWC_UFS_DME_HIBERNATE_EXIT = 0x18, ++ DWC_UFS_DME_TEST_MODE = 0x1A, ++}; ++ ++/* DME Result Codes */ ++enum { ++ DWC_UFS_DME_SUCCESS = 0x00, ++ DWC_UFS_DME_INV_MIB_ATTR = 0x01, ++ DWC_UFS_DME_INV_MIB_ATTR_VAL = 0x02, ++ DWC_UFS_DME_READ_ONLY_MIB_ATTR = 0x03, ++ DWC_UFS_DME_WRITE_ONLY_MIB_ATTR = 0x04, ++ DWC_UFS_DME_BAD_INDEX = 0x05, ++ DWC_UFS_DME_LOCKED_MIB_ATTR = 0x06, ++ DWC_UFS_DME_BAD_TEST_FEAT_INDEX = 0x07, ++ DWC_UFS_DME_PEER_COMM_FAILURE = 0x08, ++ DWC_UFS_DME_BUSY = 0x09, ++ DWC_UFS_DME_FAILURE = 0x0a, ++ ++ DWC_UFS_DME_RESULT_CODE_MASK = 0xff, ++}; ++ ++/* UTP Transfer Request Data Direction (DD) */ ++enum { ++ UTP_NO_DATA_TRANSFER = 0x00, ++ UTP_HOST_TO_DEVICE = 0x02, ++ UTP_DEVICE_TO_HOST = 0x04 ++}; ++ ++/* UPIU Read/Write flags */ ++enum { ++ UPIU_CMD_FLAGS_NONE = 0x00, ++ UPIU_CMD_FLAGS_WRITE = 0x20, ++ UPIU_CMD_FLAGS_READ = 0x40 ++}; ++ ++enum flags_id { ++ FDEVICE_INIT = 0x1, ++ FPERMANANT_WPEN = 0x2, ++ FPOWERON_WPEN = 0x3, ++ FBG_OPSEN = 0x4, ++ FPURGE_OPSEN = 0x6, ++ FPHYRES_REMOVAL = 0x8, ++ FBUSY_RTC = 0x9 ++}; ++ ++/* use for unipro & M-PHY 's configuration and control */ ++enum uic_dme_type { ++ /* Configuration */ ++ DME_GET = 0x01, ++ DME_SET = 0x02, ++ DME_PEER_GET = 0x03, ++ DME_PEER_SET = 0x04, ++ /* Control */ ++ DME_POWERON = 0x10, ++ DME_POWEROFF = 0x11, ++ DME_ENABLE = 0x12, ++ ++ DME_RESERVE_1 = 0x13, ++ DME_RESET = 0x14, ++ DME_ENDPOINTRESET = 0x15, ++ DME_LINKSTARTUP = 0x16, ++ DME_HIBERNATE_ENTER = 0x17, ++ DME_HIBERNATE_EXIT = 0x18, ++ DME_RESERVE_2 = 0x19, ++ DME_TEST_MODE = 0x1A, ++}; ++ ++enum attr_id { ++ B_BOOT_LUNEN = 0x0, ++ B_CURRENT_PM = 0x2, ++ B_ACTIV_ICC_LEVEL = 0x3, ++ B_OUT_OF_ORDER_DATAEN = 0x4, ++ B_BCKGND_OPS_STATUS = 0x5, ++ B_PURGE_STATUS = 0x6, ++ B_MAX_DATA_IN_SIZE = 0x7, ++ B_MAX_DATA_OUT_SIZE = 0x8, ++ D_DYN_CAP_NEEDED = 0x9, ++ B_REFCLK_FREQ = 0xA, ++ B_CONFIG_DESC_LOCK = 0xB, ++ B_MAX_NUM_OF_RTT = 0xC, ++ W_EXCEPTION_EVENT_CONTROL = 0xD, ++ W_EXCEPTION_EVENT_STATUS = 0xE, ++ D_SECONDS_PASSED = 0xF, ++ W_CONTEXT_CONF = 0x10, ++ D_CORR_PRG_BLKNUM = 0x11 ++}; ++ ++struct ufs { ++ uint64_t capacity; ++ unsigned long long blocksize; ++ int (*block_read)(uint64_t dest_addr, uint64_t src_offset, uint32_t lenth); ++ int (*block_write)(uint64_t src_addr, uint64_t dest_offset, uint32_t lenth); ++ int (*boot_block_write)(uint64_t src_addr, uint64_t dest_offset, uint32_t lenth); ++}; ++ ++unsigned int *ufs_pack_cid(void); ++struct ufs *get_ufs_info(void); ++int ufs_read_capacity(uint32_t *lba); ++ ++int ufs_set_bootlun(uint8_t lun); ++int ufs_set_active_lun(uint8_t lun); ++ ++int ufs_read_boot_storage(uint64_t dest_addr, uint64_t src_offset, uint32_t size); ++int ufs_write_boot_storage(uint64_t dest_addr, uint64_t src_offset, uint32_t size); ++ ++int ufs_read_storage(uint64_t dest_addr, uint64_t src_offset, uint32_t size); ++int ufs_write_storage(uint64_t src_addr, uint64_t dest_offset, uint32_t size); ++void send_uic_command(uint32_t command, uint32_t arg1, uint32_t arg2, uint32_t arg3); ++uint32_t uic_cmd_read(uint32_t command, uint32_t arg1); ++void ufs_readreg_all(void); ++ ++int ufs_set_flag(enum flags_id idn, uint8_t *flags_ret); ++int ufs_read_flag(enum flags_id idn, uint8_t *flags_ret); ++ ++int read_descriptor(const void *req_upiu, void **resp_upiu); ++int write_descriptor(const void *req_upiu); ++ ++int read_attribute(enum attr_id idn, uint8_t indexx, uint8_t selector, uint32_t *ret_value); ++int write_attribute(enum attr_id idn, uint8_t indexx, uint8_t selector, uint32_t *value); ++ ++int ufs_storage_init(void); ++ ++int ufs_hibernate_exit(void); ++int ufs_hibernate_enter(void); ++ ++int do_mode_change(const struct pwr_mode_params *pmp); ++int ufs_show_desc_info(enum info_show_type type); ++ ++void modify_desc_upiu(const struct desc_params *params); ++void ufs_reg_dump(void); ++void ufs_reinit(void); ++#ifdef CONFIG_EXT4_SPARSE ++int ufs_ext4_unsparse(const u8 *pbuf, u32 blk, u32 cnt); ++#endif ++#endif + #endif +diff --git a/include/usb.h b/include/usb.h +index efb67ea..4db1866 100644 +--- a/include/usb.h ++++ b/include/usb.h +@@ -1080,4 +1080,7 @@ struct usb_generic_descriptor **usb_emul_find_descriptor( + */ + void usb_show_tree(void); + ++int udc_connect(void); ++void udc_puts(const char *s); ++ + #endif /*_USB_H_ */ +diff --git a/include/vsprintf.h b/include/vsprintf.h +index 56844dd..d1516e2 100644 +--- a/include/vsprintf.h ++++ b/include/vsprintf.h +@@ -222,4 +222,5 @@ bool str2long(const char *p, ulong *num); + * @hz: Value to convert + */ + char *strmhz(char *buf, unsigned long hz); ++char *ultohstr(unsigned long long size); + #endif +diff --git a/lib/Kconfig b/lib/Kconfig +index 965cf7b..9ec73dc 100644 +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -7,6 +7,16 @@ config BCH + This is used by SoC platforms which do not have built-in ELM + hardware engine required for BCH ECC correction. + ++config BINMAN_FDT ++ bool "Allow access to binman information in the device tree" ++ depends on BINMAN && OF_CONTROL ++ default y ++ help ++ This enables U-Boot to access information about binman entries, ++ stored in the device tree in a binman node. Typical uses are to ++ locate entries in the firmware image. See binman.h for the available ++ functionality. ++ + config CC_OPTIMIZE_LIBS_FOR_SPEED + bool "Optimize libraries for speed" + help +@@ -371,7 +381,7 @@ config LZ4 + is included. The LZ4 algorithm can run in-place as long as the + compressed image is loaded to the end of the output buffer, and + trades lower compression ratios for much faster decompression. +- ++ + NOTE: This implements the release version of the LZ4 frame + format as generated by default by the 'lz4' command line tool. + This is not the same as the outdated, less efficient legacy +@@ -440,6 +450,13 @@ config SPL_ZSTD + help + This enables Zstandard decompression library in the SPL. + ++config HWDEC ++ bool "hw decompress" ++ default y ++ depends on TARGET_SS928V100 || TARGET_SS927V100 ++ help ++ hw decompress. ++ + endmenu + + config ERRNO_STR +diff --git a/lib/Makefile b/lib/Makefile +index 1fb650c..4262fbf 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -21,6 +21,8 @@ obj-$(CONFIG_ASN1_DECODER) += asn1_decoder.o + obj-y += crypto/ + + obj-$(CONFIG_AES) += aes.o ++obj-$(CONFIG_AES) += aes/ ++obj-$(CONFIG_$(SPL_TPL_)BINMAN_FDT) += binman.o + + ifndef API_BUILD + ifneq ($(CONFIG_UT_UNICODE)$(CONFIG_EFI_LOADER),) +@@ -28,8 +30,10 @@ obj-y += charset.o + endif + endif + obj-$(CONFIG_USB_TTY) += circbuf.o ++ifndef CONFIG_VENDOR_MC + obj-y += crc7.o + obj-y += crc8.o ++endif + obj-y += crc16.o + obj-$(CONFIG_ERRNO_STR) += errno_str.o + obj-$(CONFIG_FIT) += fdtdec_common.o +@@ -38,7 +42,9 @@ obj-$(CONFIG_GZIP_COMPRESSED) += gzip.o + obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += smbios.o + obj-$(CONFIG_IMAGE_SPARSE) += image-sparse.o + obj-y += ldiv.o ++ifndef CONFIG_VENDOR_MC + obj-$(CONFIG_MD5) += md5.o ++endif + obj-$(CONFIG_XXHASH) += xxhash.o + obj-y += net_utils.o + obj-$(CONFIG_PHYSMEM) += physmem.o +@@ -48,7 +54,7 @@ obj-$(CONFIG_RBTREE) += rbtree.o + obj-$(CONFIG_BITREVERSE) += bitrev.o + obj-y += list_sort.o + endif +- ++obj-$(CONFIG_HWDEC) += hw_dec/ + obj-$(CONFIG_$(SPL_TPL_)TPM) += tpm-common.o + ifeq ($(CONFIG_$(SPL_TPL_)TPM),y) + obj-y += crc8.o +@@ -57,8 +63,10 @@ obj-$(CONFIG_TPM_V2) += tpm-v2.o + endif + + obj-$(CONFIG_RSA) += rsa/ ++ifndef CONFIG_VENDOR_MC + obj-$(CONFIG_SHA1) += sha1.o + obj-$(CONFIG_SHA256) += sha256.o ++endif + + obj-$(CONFIG_$(SPL_)ZLIB) += zlib/ + obj-$(CONFIG_$(SPL_)ZSTD) += zstd/ +@@ -118,7 +126,7 @@ else + # Main U-Boot always uses the full printf support + obj-y += vsprintf.o strto.o + endif +- ++obj-$(CONFIG_CMD_NAND) +=match_table.o + obj-y += date.o + + # +diff --git a/lib/aes/Makefile b/lib/aes/Makefile +new file mode 100644 +index 0000000..daed52a +--- /dev/null ++++ b/lib/aes/Makefile +@@ -0,0 +1,5 @@ ++# SPDX-License-Identifier: GPL-2.0+ ++# ++# Copyright (c) 2019, Softathome ++ ++obj-$(CONFIG_$(SPL_)FIT_CIPHER) += aes-decrypt.o +diff --git a/lib/aes/aes-decrypt.c b/lib/aes/aes-decrypt.c +new file mode 100644 +index 0000000..ff180fb +--- /dev/null ++++ b/lib/aes/aes-decrypt.c +@@ -0,0 +1,54 @@ ++// SPDX-License-Identifier: GPL-2.0. ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#ifndef USE_HOSTCC ++#include ++#include ++#endif ++#include ++#include ++ ++int image_aes_decrypt(struct image_cipher_info *info, ++ const void *cipher, size_t cipher_len, ++ void **data, size_t *size) ++{ ++#ifndef USE_HOSTCC ++ unsigned char key_exp[AES256_EXPAND_KEY_LENGTH]; ++ unsigned int aes_blocks, key_len = info->cipher->key_len; ++ ++ *data = malloc(cipher_len); ++ if (!*data) { ++ printf("Can't allocate memory to decrypt\n"); ++ return -ENOMEM; ++ } ++ *size = info->size_unciphered; ++ ++ memcpy(&key_exp[0], info->key, key_len); ++ ++ /* First we expand the key. */ ++ aes_expand_key((u8 *)info->key, key_len, key_exp); ++ ++ /* Calculate the number of AES blocks to encrypt. */ ++ aes_blocks = DIV_ROUND_UP(cipher_len, AES_BLOCK_LENGTH); ++ ++ aes_cbc_decrypt_blocks(key_len, key_exp, (u8 *)info->iv, ++ (u8 *)cipher, *data, aes_blocks); ++#endif ++ ++ return 0; ++} +diff --git a/lib/aes/aes-encrypt.c b/lib/aes/aes-encrypt.c +new file mode 100644 +index 0000000..8d715f2 +--- /dev/null ++++ b/lib/aes/aes-encrypt.c +@@ -0,0 +1,150 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include "mkimage.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if OPENSSL_VERSION_NUMBER >= 0x10000000L ++#define HAVE_ERR_REMOVE_THREAD_STATE ++#endif ++ ++int image_aes_encrypt(struct image_cipher_info *info, ++ unsigned char *data, int size, ++ unsigned char **cipher, int *cipher_len) ++{ ++ EVP_CIPHER_CTX *ctx; ++ unsigned char *buf = NULL; ++ int buf_len, len, ret = 0; ++ ++ /* create and initialise the context */ ++ ctx = EVP_CIPHER_CTX_new(); ++ if (!ctx) { ++ printf("Can't create context\n"); ++ return -1; ++ } ++ ++ /* allocate a buffer for the result */ ++ buf = malloc(size + AES_BLOCK_LENGTH); ++ if (!buf) { ++ printf("Can't allocate memory to encrypt\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (EVP_EncryptInit_ex(ctx, info->cipher->calculate_type(), ++ NULL, info->key, info->iv) != 1) { ++ printf("Can't init encryption\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (EVP_EncryptUpdate(ctx, buf, &len, data, size) != 1) { ++ printf("Can't encrypt data\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ buf_len = len; ++ ++ if (EVP_EncryptFinal_ex(ctx, buf + len, &len) != 1) { ++ printf("Can't finalise the encryption\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ buf_len += len; ++ ++ *cipher = buf; ++ *cipher_len = buf_len; ++ ++out: ++ EVP_CIPHER_CTX_free(ctx); ++ return ret; ++} ++ ++int image_aes_add_cipher_data(struct image_cipher_info *info, void *keydest) ++{ ++ int parent, node; ++ char name[128]; ++ int ret = 0; ++ ++ /* Either create or overwrite the named cipher node */ ++ parent = fdt_subnode_offset(keydest, 0, FIT_CIPHER_NODENAME); ++ if (parent == -FDT_ERR_NOTFOUND) { ++ parent = fdt_add_subnode(keydest, 0, FIT_CIPHER_NODENAME); ++ if (parent < 0) { ++ ret = parent; ++ if (ret != -FDT_ERR_NOSPACE) { ++ fprintf(stderr, ++ "Couldn't create cipher node: %s\n", ++ fdt_strerror(parent)); ++ } ++ } ++ } ++ if (ret) ++ goto done; ++ ++ /* Either create or overwrite the named key node */ ++ snprintf(name, sizeof(name), "key-%s-%s-%s", ++ info->name, info->keyname, info->ivname); ++ node = fdt_subnode_offset(keydest, parent, name); ++ if (node == -FDT_ERR_NOTFOUND) { ++ node = fdt_add_subnode(keydest, parent, name); ++ if (node < 0) { ++ ret = node; ++ if (ret != -FDT_ERR_NOSPACE) { ++ fprintf(stderr, ++ "Could not create key subnode: %s\n", ++ fdt_strerror(node)); ++ } ++ } ++ } else if (node < 0) { ++ fprintf(stderr, "Cannot select keys parent: %s\n", ++ fdt_strerror(node)); ++ ret = node; ++ } ++ ++ if (!ret) ++ ret = fdt_setprop(keydest, node, "iv", ++ info->iv, info->cipher->iv_len); ++ ++ if (!ret) ++ ret = fdt_setprop(keydest, node, "key", ++ info->key, info->cipher->key_len); ++ ++ if (!ret) ++ ret = fdt_setprop_u32(keydest, node, "key-len", ++ info->cipher->key_len); ++ ++done: ++ if (ret) ++ ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; ++ ++ return ret; ++} +diff --git a/lib/binman.c b/lib/binman.c +new file mode 100644 +index 0000000..649468f +--- /dev/null ++++ b/lib/binman.c +@@ -0,0 +1,59 @@ ++// SPDX-License-Identifier: Intel ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++ ++struct binman_info { ++ ofnode image; ++}; ++ ++static struct binman_info *binman; ++ ++int binman_entry_find(const char *name, struct binman_entry *entry) ++{ ++ ofnode node; ++ int ret; ++ ++ node = ofnode_find_subnode(binman->image, name); ++ if (!ofnode_valid(node)) ++ return log_msg_ret("no binman node", -ENOENT); ++ ++ ret = ofnode_read_u32(node, "image-pos", &entry->image_pos); ++ if (ret) ++ return log_msg_ret("bad binman node1", ret); ++ ret = ofnode_read_u32(node, "size", &entry->size); ++ if (ret) ++ return log_msg_ret("bad binman node2", ret); ++ ++ return 0; ++} ++ ++int binman_init(void) ++{ ++ binman = malloc(sizeof(struct binman_info)); ++ if (!binman) ++ return log_msg_ret("space for binman", -ENOMEM); ++ binman->image = ofnode_path("/binman"); ++ if (!ofnode_valid(binman->image)) ++ return log_msg_ret("binman node", -EINVAL); ++ ++ return 0; ++} +diff --git a/lib/crc16.c b/lib/crc16.c +old mode 100644 +new mode 100755 +index f46ba72..c091e30 +--- a/lib/crc16.c ++++ b/lib/crc16.c +@@ -82,3 +82,8 @@ void crc16_ccitt_wd_buf(const uint8_t *in, uint len, + crc = htons(crc); + memcpy(out, &crc, sizeof(crc)); + } ++ ++const unsigned short *get_crc16_table(void) ++{ ++ return &crc16_tab[0]; ++} +diff --git a/lib/crc32.c b/lib/crc32.c +index e9be3bf..c3f961a 100644 +--- a/lib/crc32.c ++++ b/lib/crc32.c +@@ -255,7 +255,18 @@ uint32_t crc32_wd(uint32_t crc, const unsigned char *buf, uInt len, + + return crc; + } ++#ifdef CONFIG_BSP_NAND_SPL ++void * crc_output(void *dest, const void *src, size_t count) ++{ ++ char *d8, *s8; ++ d8 = (char *)dest; ++ s8 = (char *)src; ++ while (count--) ++ *d8++ = *s8++; + ++ return dest; ++} ++#endif + void crc32_wd_buf(const unsigned char *input, unsigned int ilen, + unsigned char *output, unsigned int chunk_sz) + { +@@ -263,5 +274,9 @@ void crc32_wd_buf(const unsigned char *input, unsigned int ilen, + + crc = crc32_wd(0, input, ilen, chunk_sz); + crc = htonl(crc); ++#ifdef CONFIG_BSP_NAND_SPL ++ crc_output(output, &crc, sizeof(crc)); ++#else + memcpy(output, &crc, sizeof(crc)); ++#endif + } +diff --git a/lib/hw_dec/Makefile b/lib/hw_dec/Makefile +new file mode 100644 +index 0000000..6003ee3 +--- /dev/null ++++ b/lib/hw_dec/Makefile +@@ -0,0 +1 @@ ++obj-y += hw_decompress.o +diff --git a/lib/hw_dec/hw_decompress.c b/lib/hw_dec/hw_decompress.c +new file mode 100644 +index 0000000..f12d3ff +--- /dev/null ++++ b/lib/hw_dec/hw_decompress.c +@@ -0,0 +1,23 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#if (defined CONFIG_TARGET_SS928V100) || (defined CONFIG_TARGET_SS927V100) ++#include "hw_decompress_v2.c" ++#endif ++ +diff --git a/lib/hw_dec/hw_decompress_ss927v100.c b/lib/hw_dec/hw_decompress_ss927v100.c +new file mode 100644 +index 0000000..b7aa4db +--- /dev/null ++++ b/lib/hw_dec/hw_decompress_ss927v100.c +@@ -0,0 +1,48 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++ ++#define PERI_CRG_GZIP 0x2b80 ++#define GZIP_CLKEN (1<<4) ++ ++#ifndef GZIP_REG_BASE ++#define GZIP_REG_BASE 0x17050000 ++#endif ++ ++#define HW_DEC_REG_BASE_ADDR (GZIP_REG_BASE) ++ ++static void disable_decompress_clock(void) ++{ ++ unsigned int regval; ++ ++ regval = readl(CRG_REG_BASE + PERI_CRG_GZIP); ++ regval &= ~GZIP_CLKEN; ++ writel(regval, CRG_REG_BASE + PERI_CRG_GZIP); ++} ++ ++static void enable_decompress_clock(void) ++{ ++ unsigned int regval; ++ ++ regval = readl(CRG_REG_BASE + PERI_CRG_GZIP); ++ regval |= GZIP_CLKEN; ++ writel(regval, CRG_REG_BASE + PERI_CRG_GZIP); ++} +diff --git a/lib/hw_dec/hw_decompress_ss928v100.c b/lib/hw_dec/hw_decompress_ss928v100.c +new file mode 100644 +index 0000000..b7aa4db +--- /dev/null ++++ b/lib/hw_dec/hw_decompress_ss928v100.c +@@ -0,0 +1,48 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++ ++#define PERI_CRG_GZIP 0x2b80 ++#define GZIP_CLKEN (1<<4) ++ ++#ifndef GZIP_REG_BASE ++#define GZIP_REG_BASE 0x17050000 ++#endif ++ ++#define HW_DEC_REG_BASE_ADDR (GZIP_REG_BASE) ++ ++static void disable_decompress_clock(void) ++{ ++ unsigned int regval; ++ ++ regval = readl(CRG_REG_BASE + PERI_CRG_GZIP); ++ regval &= ~GZIP_CLKEN; ++ writel(regval, CRG_REG_BASE + PERI_CRG_GZIP); ++} ++ ++static void enable_decompress_clock(void) ++{ ++ unsigned int regval; ++ ++ regval = readl(CRG_REG_BASE + PERI_CRG_GZIP); ++ regval |= GZIP_CLKEN; ++ writel(regval, CRG_REG_BASE + PERI_CRG_GZIP); ++} +diff --git a/lib/hw_dec/hw_decompress_v2.c b/lib/hw_dec/hw_decompress_v2.c +new file mode 100644 +index 0000000..441e10c +--- /dev/null ++++ b/lib/hw_dec/hw_decompress_v2.c +@@ -0,0 +1,340 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "hw_decompress_v2.h" ++#include ++#include ++#ifndef error ++#define error(fmt, args...) do { \ ++ printf("ERROR: " pr_fmt(fmt) "\nat %s:%d/%s()\n", \ ++ ##args, __FILE__, __LINE__, __func__); \ ++} while (0) ++#endif ++ ++#if (defined CONFIG_TARGET_SS928V100) ++#include "hw_decompress_ss928v100.c" ++#endif ++ ++#if (defined CONFIG_TARGET_SS927V100) ++#include "hw_decompress_ss927v100.c" ++#endif ++ ++unsigned int hw_dec_type = 0; ++static unsigned int hw_dec_sop; ++static unsigned int hw_dec_eop; ++static unsigned int hw_dec_cur_blk; ++static unsigned int hw_blk_total_num; ++ ++void hw_dec_sop_eop_first_set(int block_num) ++{ ++ if (block_num == 1) { ++ hw_dec_sop = 1; ++ hw_dec_eop = 1; ++ } else { ++ hw_dec_sop = 1; ++ hw_dec_eop = 0; ++ } ++ ++ hw_dec_cur_blk = 0; ++ hw_blk_total_num = block_num; ++} ++ ++static inline void hw_dec_work_en_set(int work_en_flg) ++{ ++ /* Enable the emar */ ++ writel(work_en_flg, HW_DEC_REG_BASE_ADDR + EAMR_WORK_EN_REG_OFST); ++} ++ ++static inline void hw_dec_rtn_baddr_h32_set(unsigned int addr) ++{ ++ writel(addr, HW_DEC_REG_BASE_ADDR + DPRS_DATA_RTN_BADDR_H32); ++} ++ ++static inline void hw_dec_rtn_baddr_l32_set(unsigned int addr) ++{ ++ writel(addr, HW_DEC_REG_BASE_ADDR + DPRS_DATA_RTN_BADDR_L32); ++} ++ ++static inline void hw_dec_dprs_data_baddr_h32_set(unsigned int addr) ++{ ++ writel(addr, HW_DEC_REG_BASE_ADDR + DPRS_DATA_INFO_BADDR_H32); ++} ++ ++static inline void hw_dec_dprs_data_baddr_l32_set(unsigned int addr) ++{ ++ writel(addr, HW_DEC_REG_BASE_ADDR + DPRS_DATA_INFO_BADDR_L32); ++} ++ ++static inline void hw_dec_data_rtn_len_set(unsigned int len) ++{ ++ writel(len, HW_DEC_REG_BASE_ADDR + DPRS_DATA_RTN_LEN); ++} ++ ++static inline void hw_dec_dprs_data_len_set(unsigned int len) ++{ ++ writel(len, HW_DEC_REG_BASE_ADDR + DPRS_DATA_INFO_LEN); ++} ++ ++static inline void hw_dec_crc_check_en(unsigned int crc_en) ++{ ++ writel(crc_en, HW_DEC_REG_BASE_ADDR + CRC_CHECK_EN); ++} ++ ++static inline void hw_dec_data_crc32_set(unsigned int crc32) ++{ ++ writel(crc32, HW_DEC_REG_BASE_ADDR + DPRS_DATA_CRC32); ++} ++ ++static inline unsigned int hw_dec_buf_status_get(void) ++{ ++ return readl(HW_DEC_REG_BASE_ADDR + BUF_INFO); ++} ++ ++static inline unsigned int hw_dec_dprs_rtn_status_get(void) ++{ ++ return readl(HW_DEC_REG_BASE_ADDR + DPRS_RTN_INFO); ++} ++ ++static inline void hw_dec_buf_status_clr(void) ++{ ++ writel(0x1, HW_DEC_REG_BASE_ADDR + BUF_INFO_CLR); ++} ++ ++static inline void hw_dec_dprs_rtn_status_clr(void) ++{ ++ writel(0x1, HW_DEC_REG_BASE_ADDR + RLT_INFO_CLR); ++} ++ ++static void hw_dec_intr_en_set(int blk_intr_en, int task_intr_en) ++{ ++ u_intr_en intr_en; ++ intr_en.bits.task_intrpt_en = task_intr_en; ++ intr_en.bits.block_intrpt_en = blk_intr_en; ++ writel(intr_en.u32, HW_DEC_REG_BASE_ADDR + INT_EN_REG_ADDR); ++} ++ ++static inline unsigned int hw_dec_intr_status_get(void) ++{ ++ return readl(HW_DEC_REG_BASE_ADDR + INT_STATUS_REG_ADDR); ++} ++ ++static void hw_dec_block_intr_status_clr(void) ++{ ++ u_intr_clr intr_clr; ++ ++ intr_clr.u32 = readl(HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR); ++ intr_clr.bits.block_intrpt_clr = 0x1; ++ writel(intr_clr.u32, HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR); ++} ++ ++static void hw_dec_task_intr_status_clr(void) ++{ ++ u_intr_clr intr_clr; ++ ++ intr_clr.u32 = readl(HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR); ++ intr_clr.bits.task_intrpt_clr = 0x1; ++ writel(intr_clr.u32, HW_DEC_REG_BASE_ADDR + INT_CLEAR_REG_ADDR); ++} ++ ++int hw_dec_intr_proc(int irq, const void *para) ++{ ++ u_buf_status buf_status; ++ u_intr_status intr_status; ++ u_dprs_rtn_status dprs_status; ++ int ret = 0; ++ ++ intr_status.u32 = hw_dec_intr_status_get(); ++ if (intr_status.bits.block_intrpt) { ++ buf_status.u32 = hw_dec_buf_status_get(); ++ if (buf_status.bits.aval_flg) ++ hw_dec_buf_status_clr(); ++ ++ hw_dec_block_intr_status_clr(); ++ } ++ ++ if (!intr_status.bits.task_intrpt) ++ return -1; ++ ++ dprs_status.u32 = hw_dec_dprs_rtn_status_get(); ++ if (dprs_status.bits.aval_flg) { ++ if (dprs_status.bits.err_info) { ++ if (dprs_status.bits.err_info == 0x81) ++ error("\n[hw_dec_intr_proc] error = 0x81\n"); ++ else if (dprs_status.bits.err_info == 0x85) ++ error("\n[hw_dec_intr_proc] error = 0x85\n"); ++ else if (dprs_status.bits.err_info == 0x87) ++ error("\n[hw_dec_intr_proc] error = 0x87\n"); ++ else if (dprs_status.bits.err_info == 0x89) ++ error("\n[hw_dec_intr_proc] error = 0x89\n"); ++ else if (dprs_status.bits.err_info == 0x8a) ++ error("\n[hw_dec_intr_proc] error = 0x8a\n"); ++ else if (dprs_status.bits.err_info == 0x8b) ++ error("\n[hw_dec_intr_proc] error = 0x8b\n"); ++ else ++ error("\n[hw_dec_intr_proc] other error !!\n"); ++ ret = -2; /* -2:failed */ ++ } ++ ++ hw_dec_dprs_rtn_status_clr(); ++ } ++ ++ hw_dec_task_intr_status_clr(); ++ return ret; ++} ++ ++void hw_dec_start(unsigned int src_baddr_h32, ++ unsigned int src_baddr_l32, ++ unsigned int dst_baddr_h32, ++ unsigned int dst_baddr_l32, ++ unsigned int src_len, ++ unsigned int dst_len, ++ unsigned int crc_en, ++ unsigned int crc32, ++ unsigned int dec_type) ++{ ++ unsigned int val; ++ ++ if (hw_dec_sop) { ++ if (!dec_type) { ++ /* set the parameters of output buffer */ ++ hw_dec_rtn_baddr_h32_set(dst_baddr_h32); ++ hw_dec_rtn_baddr_l32_set(dst_baddr_l32); ++ hw_dec_data_rtn_len_set(dst_len); ++ } else { ++ /* set the parameter of output buffer */ ++ hw_dec_dprs_data_baddr_h32_set(dst_baddr_h32); ++ hw_dec_dprs_data_baddr_l32_set(dst_baddr_l32); ++ hw_dec_dprs_data_len_set(page_nr(dst_len) * 4); /* 4:Align */ ++ } ++ } ++ ++ /* set the parameter of input buffer */ ++ writel(src_baddr_l32, HW_DEC_REG_BASE_ADDR + DPRS_DATA_SRC_BADDR_L32); ++ ++ val = src_len | ++ (hw_dec_sop << 28) | (hw_dec_eop << 29) | (!dec_type << 31); /* 28,29,31 Move Left bit */ ++ writel(val, HW_DEC_REG_BASE_ADDR + DPRS_DATA_SRC_LEN); ++ ++ hw_dec_crc_check_en(crc_en); ++} ++ ++static inline void delay(unsigned int num) ++{ ++ volatile unsigned int i; ++ ++ for (i = 0; i < (100 * num); i++) /* 100: Cycle */ ++ __asm__ __volatile__("nop"); ++} ++ ++int hw_dec_wait_finish(void) ++{ ++ int ret; ++ int times = 0; ++ ++ do { ++ ret = hw_dec_intr_proc(HW_DEC_INTR, NULL); ++ times++; ++ if (times > 2000000) { /* 2000000 ms */ ++ error("\nhardware decompress overtime!\n"); ++ break; ++ } ++ delay(1); ++ } while (-1 == ret); ++ ++ return ret; ++} ++ ++int hw_dec_decompress(const unsigned char *dst_h32, const unsigned char *dst_l32, ++ int* const dstlen, const unsigned char *src_h32, ++ const unsigned char *src_l32, int srclen, const void *unused) ++{ ++ int ret; ++ ++ if (srclen > GZIP_MAX_LEN) { ++ error("\nThe max lenth GZIP decompress supported is (16MB - 1) !!!!\n"); ++ return -1; ++ } ++ ++ hw_dec_sop_eop_first_set(1); ++ ++ if (dstlen == NULL) { ++ error("\ndstlen == NULL\n"); ++ return -1; ++ } ++ ++ hw_dec_start((unsigned int)(uintptr_t)src_h32, (unsigned int)(uintptr_t)src_l32, ++ (unsigned int)(uintptr_t)dst_h32, (unsigned int)(uintptr_t)dst_l32, ++ srclen, *dstlen, 0, 0, hw_dec_type); ++ ++ ret = hw_dec_wait_finish(); ++ ++ *dstlen = readl(HW_DEC_REG_BASE_ADDR + DPRS_RTN_LEN); ++ ++ if (ret) ++ return -1; ++ ++ return 0; ++} ++ ++void hw_dec_init(void) ++{ ++ /* enable decompress clock */ ++ enable_decompress_clock(); ++ /* Init the emar interface */ ++ /* bit[31:28] : Emar Write Outstanding ++ bit[27:24] : Emar Read Outstanding ++ bit[23: 0] : reserve */ ++ writel(0, HW_DEC_REG_BASE_ADDR + EAMR_OSD_REG_OFST); ++ writel((0x3 << 24), HW_DEC_REG_BASE_ADDR + EAMR_OSD_REG_OFST); /* 0x3 Move Left 24bit */ ++ writel((0x3 << 28), HW_DEC_REG_BASE_ADDR + EAMR_OSD_REG_OFST); /* 0x3 Move Left 28bit */ ++ ++ /* Enable interrupt */ ++ hw_dec_intr_en_set(0x1, 0x1); ++ ++ /* Enable emar */ ++ hw_dec_work_en_set(0x1); ++} ++ ++void hw_dec_uinit(void) ++{ ++ hw_dec_work_en_set(0x0); ++ hw_dec_intr_en_set(0x0, 0x0); ++ ++ /* disable decompress clock */ ++ disable_decompress_clock(); ++} ++ ++/* ++ * Support decompressing gzip file by sections ++ * op_type: operation type ++ * dst: addr of uncompressed file ++ * dstlen: len of uncompressed file ++ * src: addr of compressed file ++ * srclen: len of compressed file ++ */ ++int hw_dec_decompress_ex(hw_decompress_op_type op_type, const unsigned char *dst, ++ int *dstlen, const unsigned char *src, int srclen) ++{ ++ if (op_type != HW_DECOMPRESS_OP_ONCE) { ++ error("only support HW_DECOMPRESS_OP_ONCE now!\n"); ++ return -1; ++ } ++ ++ return hw_dec_decompress(NULL, dst, (int * const)dstlen, NULL, src, srclen, NULL); ++} +diff --git a/lib/hw_dec/hw_decompress_v2.h b/lib/hw_dec/hw_decompress_v2.h +new file mode 100644 +index 0000000..3647533 +--- /dev/null ++++ b/lib/hw_dec/hw_decompress_v2.h +@@ -0,0 +1,140 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HW_DECOMPRESS_V2_H ++#define HW_DECOMPRESS_V2_H ++ ++/* The base address for emar */ ++/* Reserved macro HW_DEC_REG_BASE_ADDR (GZIP_REG_BASE) */ ++ ++/* The global init registers for emar interface */ ++#define EAMR_OSD_REG_OFST 0x020cUL ++ ++/* The enable register */ ++#define EAMR_WORK_EN_REG_OFST 0x0100UL ++ ++#define DPRS_DATA_SRC_BADDR_H32 0x2040UL ++#define DPRS_DATA_SRC_BADDR_L32 0x2044UL ++#define DPRS_DATA_SRC_LEN 0x2048UL ++ ++/* Decompress parameter reigsters for page address */ ++#define DPRS_DATA_RTN_BADDR_H32 0x2020UL ++#define DPRS_DATA_RTN_BADDR_L32 0x2024UL ++#define DPRS_DATA_RTN_LEN 0x2028UL ++ ++/* Decompress parameter registers for page data */ ++#define DPRS_DATA_INFO_BADDR_H32 0x202cUL ++#define DPRS_DATA_INFO_BADDR_L32 0x2030UL ++#define DPRS_DATA_INFO_LEN 0x2034UL ++ ++#define DPRS_DATA_CRC32 0x2030UL ++ ++#define CRC_CHECK_EN 0x4000UL ++ ++/* The status registers */ ++#define BUF_INFO 0x2080UL ++#define DPRS_RTN_INFO 0x2084UL ++#define DPRS_RTN_LEN 0x2088UL ++#define BUF_INFO_CLR 0x2090UL ++#define RLT_INFO_CLR 0x2094UL ++ ++/* The intr registers */ ++#define INT_EN_REG_ADDR 0x0128UL ++#define INT_STATUS_REG_ADDR 0x0124UL ++#define INT_CLEAR_REG_ADDR 0x0130UL ++ ++#define TASK_MODE_REG 0x2134UL ++ ++ ++#define PAGE_SIZE 4096 ++#define page_nr(x) (((x) + PAGE_SIZE - 1) / PAGE_SIZE) ++/* Define the union u_dprs_data_buf_info */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int buf_len : 24; /* [23..0] */ ++ unsigned int buf_id : 2; /* [25..24] */ ++ unsigned int reserved_1 : 2; /* [27..26] */ ++ unsigned int eop : 1; /* [28] */ ++ unsigned int sop : 1; /* [29] */ ++ unsigned int reserved_0 : 1; /* [30] */ ++ unsigned int mode : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_dprs_data_buf_info; ++ ++typedef union { ++ struct { ++ unsigned int buf_id : 2; /* [1:0] */ ++ unsigned int rsv : 29; /* [30:2] */ ++ unsigned int aval_flg : 1; /* [31] */ ++ } bits; ++ unsigned int u32; ++} u_buf_status; ++ ++typedef union { ++ struct { ++ unsigned int err_info : 8; /* [7:0] */ ++ unsigned int rsv : 23; /* [30:8] */ ++ unsigned int aval_flg : 1; /* [31] */ ++ } bits; ++ ++ unsigned int u32; ++} u_dprs_rtn_status; ++ ++/* Define the union U_INT_EN */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int task_intrpt_en : 1; /* [0] */ ++ unsigned int block_intrpt_en : 1; /* [1] */ ++ unsigned int reserved_0 : 30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_intr_en; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int task_intrpt : 1; /* [0] */ ++ unsigned int block_intrpt : 1; /* [1] */ ++ unsigned int reserved_0 : 30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_intr_status; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int task_intrpt_clr : 1; /* [0] */ ++ unsigned int block_intrpt_clr : 1; /* [1] */ ++ unsigned int reserved_0 : 30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_intr_clr; ++ ++#endif /* end HW_DECOMPRESS_V2_H */ +diff --git a/lib/libfdt/fdt_region.c b/lib/libfdt/fdt_region.c +index 7e9fa92..e21bf4a 100644 +--- a/lib/libfdt/fdt_region.c ++++ b/lib/libfdt/fdt_region.c +@@ -41,6 +41,7 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + int depth = -1; + int want = 0; + int base = fdt_off_dt_struct(fdt); ++ bool expect_end = false; + + end = path; + *end = '\0'; +@@ -57,12 +58,18 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + tag = fdt_next_tag(fdt, offset, &nextoffset); + stop_at = nextoffset; + ++ /* If we see two root nodes, something is wrong */ ++ if (expect_end && tag != FDT_END) ++ return -FDT_ERR_BADLAYOUT; ++ + switch (tag) { + case FDT_PROP: + include = want >= 2; + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); ++ if (!str) ++ return -FDT_ERR_BADSTRUCTURE; + if (str_in_list(str, exc_prop, exc_prop_count)) + include = 0; + break; +@@ -77,6 +84,10 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + if (depth == FDT_MAX_DEPTH) + return -FDT_ERR_BADSTRUCTURE; + name = fdt_get_name(fdt, offset, &len); ++ ++ /* The root node must have an empty name */ ++ if (!depth && *name) ++ return -FDT_ERR_BADLAYOUT; + if (end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + if (end != path + 1) +@@ -104,6 +115,8 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + while (end > path && *--end != '/') + ; + *end = '\0'; ++ if (depth == -1) ++ expect_end = true; + break; + + case FDT_END: +diff --git a/lib/match_table.c b/lib/match_table.c +new file mode 100644 +index 0000000..9ae4d58 +--- /dev/null ++++ b/lib/match_table.c +@@ -0,0 +1,107 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++ ++#include ++ ++int reg2type(const struct match_reg_type *table, int length, int reg, int def) ++{ ++ while (length-- > 0) { ++ if (table->reg == reg) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++int type2reg(const struct match_reg_type *table, int length, int type, int def) ++{ ++ while (length-- > 0) { ++ if (table->type == type) ++ return table->reg; ++ table++; ++ } ++ return def; ++} ++ ++int str2type(const struct match_type_str *table, int length, const char *str, ++ int size, int def) ++{ ++ while (length-- > 0) { ++ if (!strncmp(table->str, str, size)) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++const char *type2str(const struct match_type_str *table, int length, int type, ++ const char *def) ++{ ++ while (length-- > 0) { ++ if (table->type == type) ++ return table->str; ++ table++; ++ } ++ return def; ++} ++ ++int match_reg_to_type(const struct match_t *table, int nr_table, int reg, int def) ++{ ++ while (nr_table-- > 0) { ++ if (table->reg == reg) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++int match_type_to_reg(const struct match_t *table, int nr_table, int type, int def) ++{ ++ while (nr_table-- > 0) { ++ if (table->type == type) ++ return table->reg; ++ table++; ++ } ++ return def; ++} ++ ++int match_data_to_type(const struct match_t *table, int nr_table, const char *data, ++ int size, int def) ++{ ++ while (nr_table-- > 0) { ++ if (!memcmp(table->data, data, size)) ++ return table->type; ++ table++; ++ } ++ return def; ++} ++ ++void *match_type_to_data(const struct match_t *table, int nr_table, int type, ++ void *def) ++{ ++ while (nr_table-- > 0) { ++ if (table->type == type) ++ return table->data; ++ table++; ++ } ++ return def; ++} ++ +diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c +index 5b5905a..f1c1016 100644 +--- a/lib/rsa/rsa-sign.c ++++ b/lib/rsa/rsa-sign.c +@@ -791,8 +791,8 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) + } + + if (!ret) { +- ret = fdt_setprop_string(keydest, node, "key-name-hint", +- info->keyname); ++ ret = fdt_setprop_string(keydest, node, FIT_KEY_HINT, ++ info->keyname); + } + if (!ret) + ret = fdt_setprop_u32(keydest, node, "rsa,num-bits", bits); +@@ -814,7 +814,7 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) + info->name); + } + if (!ret && info->require_keys) { +- ret = fdt_setprop_string(keydest, node, "required", ++ ret = fdt_setprop_string(keydest, node, FIT_KEY_REQUIRED, + info->require_keys); + } + done: +diff --git a/lib/unlzma.c b/lib/unlzma.c +new file mode 100644 +index 0000000..d8dfb6f +--- /dev/null ++++ b/lib/unlzma.c +@@ -0,0 +1,626 @@ ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#define uint8_t unsigned char ++#define uint16_t unsigned short ++#define uint32_t unsigned int ++#define uint64_t unsigned long long ++#define size_t unsigned long ++#define int32_t int ++ ++#define NULL ((void *)0) ++ ++#define MIN(a, b) (((a) < (b)) ? (a) : (b)) ++ ++static long long read_int(unsigned char *ptr, int size) ++{ ++ int i; ++ long long ret = 0; ++ ++ for (i = 0; i < size; i++) ++ ret = ((unsigned long long)ret << 8) | ptr[size - i - 1]; ++ return ret; ++} ++ ++#define ENDIAN_CONVERT(x) \ ++ x = (typeof(x))read_int((unsigned char *)&x, sizeof(x)) ++ ++/* Small range coder implementation for lzma. ++ * Copyright (C) 2006 Aurelien Jacobs < aurel@gnuage.org > ++ * ++ * Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/) ++ * Copyright (c) 1999-2005 Igor Pavlov ++ */ ++ ++#define LZMA_IOBUF_SIZE 0x10000 ++ ++struct rc { ++ int (*fill)(void *, unsigned int); ++ uint8_t *ptr; ++ uint8_t *buffer; ++ uint8_t *buffer_end; ++ int buffer_size; ++ uint32_t code; ++ uint32_t range; ++ uint32_t bound; ++}; ++ ++ ++#define RC_TOP_BITS 24 ++#define RC_MOVE_BITS 5 ++#define RC_MODEL_TOTAL_BITS 11 ++ ++static int nofill(void *buffer, unsigned int len) ++{ ++ return -1; ++} ++ ++/* Called twice: once at startup and once in rc_normalize() */ ++static void rc_read(struct rc *rc) ++{ ++ rc->buffer_size = rc->fill((char *)rc->buffer, LZMA_IOBUF_SIZE); ++ if (rc->buffer_size <= 0) ++ return; ++ rc->ptr = rc->buffer; ++ rc->buffer_end = rc->buffer + rc->buffer_size; ++} ++ ++/* Called once */ ++static inline void rc_init(struct rc *rc, unsigned char *buffer, ++ int buffer_size) ++{ ++ rc->fill = nofill; ++ rc->buffer = (uint8_t *)buffer; ++ rc->buffer_size = buffer_size; ++ rc->buffer_end = rc->buffer + rc->buffer_size; ++ rc->ptr = rc->buffer; ++ ++ rc->code = 0; ++ rc->range = 0xFFFFFFFF; ++} ++ ++static inline void rc_init_code(struct rc *rc) ++{ ++ int i; ++ ++ for (i = 0; i < 5; i++) { ++ if (rc->ptr >= rc->buffer_end) ++ rc_read(rc); ++ rc->code = (rc->code << 8) | *rc->ptr++; ++ } ++} ++ ++/* Called twice, but one callsite is in inline'd rc_is_bit_0_helper() */ ++static void rc_do_normalize(struct rc *rc) ++{ ++ if (rc->ptr >= rc->buffer_end) ++ rc_read(rc); ++ rc->range <<= 8; ++ rc->code = (rc->code << 8) | *rc->ptr++; ++} ++ ++static inline void rc_normalize(struct rc *rc) ++{ ++ if (rc->range < (1 << RC_TOP_BITS)) ++ rc_do_normalize(rc); ++} ++ ++/* Called 9 times */ ++/* Why rc_is_bit_0_helper exists? ++ * Because we want to always expose (rc->code < rc->bound) to optimizer ++ */ ++static inline uint32_t rc_is_bit_0_helper(struct rc *rc, uint16_t *p) ++{ ++ rc_normalize(rc); ++ rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS); ++ return rc->bound; ++} ++static inline int rc_is_bit_0(struct rc *rc, uint16_t *p) ++{ ++ uint32_t t = rc_is_bit_0_helper(rc, p); ++ return rc->code < t; ++} ++ ++/* Called ~10 times, but very small, thus inlined */ ++static inline void rc_update_bit_0(struct rc *rc, uint16_t *p) ++{ ++ rc->range = rc->bound; ++ *p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS; ++} ++static inline void rc_update_bit_1(struct rc *rc, uint16_t *p) ++{ ++ rc->range -= rc->bound; ++ rc->code -= rc->bound; ++ *p -= *p >> RC_MOVE_BITS; ++} ++ ++/* Called 4 times in unlzma loop */ ++static int rc_get_bit(struct rc *rc, uint16_t *p, int *symbol) ++{ ++ if (rc_is_bit_0(rc, p)) { ++ rc_update_bit_0(rc, p); ++ *symbol *= 2; ++ return 0; ++ } else { ++ rc_update_bit_1(rc, p); ++ *symbol = *symbol * 2 + 1; ++ return 1; ++ } ++} ++ ++/* Called once */ ++static inline int rc_direct_bit(struct rc *rc) ++{ ++ rc_normalize(rc); ++ rc->range >>= 1; ++ if (rc->code >= rc->range) { ++ rc->code -= rc->range; ++ return 1; ++ } ++ return 0; ++} ++ ++/* Called twice */ ++static inline void rc_bit_tree_decode(struct rc *rc, uint16_t *p, ++ int num_levels, int *symbol) ++{ ++ int i = num_levels; ++ ++ *symbol = 1; ++ while (i--) ++ rc_get_bit(rc, p + *symbol, symbol); ++ *symbol -= 1 << (unsigned int)num_levels; ++} ++ ++ ++/* ++ * Small lzma deflate implementation. ++ * Copyright (C) 2006 Aurelien Jacobs < aurel@gnuage.org > ++ * ++ * Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/) ++ * Copyright (C) 1999-2005 Igor Pavlov ++ */ ++ ++ ++struct lzma_header { ++ uint8_t pos; ++ uint32_t dict_size; ++ uint64_t dst_size; ++} __attribute__((packed)) ; ++ ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++#define LZMA_NUM_POS_BITS_MAX 4 ++ ++#define LZMA_LEN_NUM_LOW_BITS 3 ++#define LZMA_LEN_NUM_MID_BITS 3 ++#define LZMA_LEN_NUM_HIGH_BITS 8 ++ ++#define LZMA_LEN_CHOICE 0 ++#define LZMA_LEN_CHOICE_2 (LZMA_LEN_CHOICE + 1) ++#define LZMA_LEN_LOW (LZMA_LEN_CHOICE_2 + 1) ++#define LZMA_LEN_MID (LZMA_LEN_LOW \ ++ + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_LOW_BITS))) ++#define LZMA_LEN_HIGH (LZMA_LEN_MID \ ++ +(1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_MID_BITS))) ++#define LZMA_NUM_LEN_PROBS (LZMA_LEN_HIGH + (1 << LZMA_LEN_NUM_HIGH_BITS)) ++ ++#define LZMA_NUM_STATES 12 ++#define LZMA_NUM_LIT_STATES 7 ++ ++#define LZMA_START_POS_MODEL_INDEX 4 ++#define LZMA_END_POS_MODEL_INDEX 14 ++#define LZMA_NUM_FULL_DISTANCES (1 << (LZMA_END_POS_MODEL_INDEX >> 1)) ++ ++#define LZMA_NUM_POS_SLOT_BITS 6 ++#define LZMA_NUM_LEN_TO_POS_STATES 4 ++ ++#define LZMA_NUM_ALIGN_BITS 4 ++ ++#define LZMA_MATCH_MIN_LEN 2 ++ ++#define LZMA_IS_MATCH 0 ++#define LZMA_IS_REP (LZMA_IS_MATCH + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)) ++#define LZMA_IS_REP_G0 (LZMA_IS_REP + LZMA_NUM_STATES) ++#define LZMA_IS_REP_G1 (LZMA_IS_REP_G0 + LZMA_NUM_STATES) ++#define LZMA_IS_REP_G2 (LZMA_IS_REP_G1 + LZMA_NUM_STATES) ++#define LZMA_IS_REP_0_LONG (LZMA_IS_REP_G2 + LZMA_NUM_STATES) ++#define LZMA_POS_SLOT (LZMA_IS_REP_0_LONG \ ++ + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)) ++#define LZMA_SPEC_POS (LZMA_POS_SLOT \ ++ +(LZMA_NUM_LEN_TO_POS_STATES << LZMA_NUM_POS_SLOT_BITS)) ++#define LZMA_ALIGN (LZMA_SPEC_POS \ ++ + LZMA_NUM_FULL_DISTANCES - LZMA_END_POS_MODEL_INDEX) ++#define LZMA_LEN_CODER (LZMA_ALIGN + (1 << LZMA_NUM_ALIGN_BITS)) ++#define LZMA_REP_LEN_CODER (LZMA_LEN_CODER + LZMA_NUM_LEN_PROBS) ++#define LZMA_LITERAL (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS) ++ ++ ++struct writer { ++ uint8_t *buffer; ++ uint8_t previous_byte; ++ size_t buffer_pos; ++ int bufsize; ++ size_t global_pos; ++ int(*flush)(void *, unsigned int); ++ struct lzma_header *header; ++}; ++ ++struct cstate { ++ int state; ++ uint32_t rep0, rep1, rep2, rep3; ++}; ++ ++static inline size_t get_pos(struct writer *wr) ++{ ++ return wr->global_pos + wr->buffer_pos; ++} ++ ++static inline uint8_t peek_old_byte(struct writer *wr, uint32_t offs) ++{ ++ if (!wr->flush) { ++ int32_t pos; ++ while (offs > wr->header->dict_size) ++ offs -= wr->header->dict_size; ++ pos = wr->buffer_pos - offs; ++ return wr->buffer[pos]; ++ } else { ++ uint32_t pos = wr->buffer_pos - offs; ++ while (pos >= wr->header->dict_size) ++ pos += wr->header->dict_size; ++ return wr->buffer[pos]; ++ } ++} ++ ++static inline void write_byte(struct writer *wr, uint8_t byte) ++{ ++ wr->buffer[wr->buffer_pos++] = wr->previous_byte = byte; ++ if (wr->flush && wr->buffer_pos == wr->header->dict_size) { ++ wr->buffer_pos = 0; ++ wr->global_pos += wr->header->dict_size; ++ wr->flush((char *)wr->buffer, wr->header->dict_size); ++ } ++} ++ ++ ++static inline void copy_byte(struct writer *wr, uint32_t offs) ++{ ++ write_byte(wr, peek_old_byte(wr, offs)); ++} ++ ++static inline void copy_bytes(struct writer *wr, uint32_t rep0, int len) ++{ ++ do { ++ copy_byte(wr, rep0); ++ len--; ++ } while (len != 0 && wr->buffer_pos < wr->header->dst_size); ++} ++ ++static inline void process_bit0(struct writer *wr, struct rc *rc, ++ struct cstate *cst, uint16_t *p, int pos_state, uint16_t *prob, ++ int lc, uint32_t literal_pos_mask) ++{ ++ int mi = 1; ++ rc_update_bit_0(rc, prob); ++ prob = (p + LZMA_LITERAL + ++ (LZMA_LIT_SIZE ++ * (((get_pos(wr) & literal_pos_mask) << (unsigned int)lc) ++ + (wr->previous_byte >> (unsigned int)(8 - lc)))) ++ ); ++ ++ if (cst->state >= LZMA_NUM_LIT_STATES) { ++ unsigned int match_byte = peek_old_byte(wr, cst->rep0); ++ do { ++ int bit; ++ uint16_t *prob_lit; ++ ++ match_byte <<= 1; ++ bit = match_byte & 0x100; ++ prob_lit = prob + 0x100 + bit + mi; ++ if (rc_get_bit(rc, prob_lit, &mi)) { ++ if (!bit) ++ break; ++ } else { ++ if (bit) ++ break; ++ } ++ } while (mi < 0x100); ++ } ++ while (mi < 0x100) { ++ uint16_t *prob_lit = prob + mi; ++ rc_get_bit(rc, prob_lit, &mi); ++ } ++ write_byte(wr, mi); ++ if (cst->state < 4) ++ cst->state = 0; ++ else if (cst->state < 10) ++ cst->state -= 3; ++ else ++ cst->state -= 6; ++} ++ ++static inline void process_bit1(struct writer *wr, struct rc *rc, ++ struct cstate *cst, uint16_t *p, int pos_state, uint16_t *prob) ++{ ++ int offset; ++ uint16_t *prob_len = NULL; ++ int num_bits; ++ int len; ++ ++ rc_update_bit_1(rc, prob); ++ prob = p + LZMA_IS_REP + cst->state; ++ if (rc_is_bit_0(rc, prob)) { ++ rc_update_bit_0(rc, prob); ++ cst->rep3 = cst->rep2; ++ cst->rep2 = cst->rep1; ++ cst->rep1 = cst->rep0; ++ cst->state = cst->state < LZMA_NUM_LIT_STATES ? 0 : 3; ++ prob = p + LZMA_LEN_CODER; ++ } else { ++ rc_update_bit_1(rc, prob); ++ prob = p + LZMA_IS_REP_G0 + cst->state; ++ if (rc_is_bit_0(rc, prob)) { ++ rc_update_bit_0(rc, prob); ++ prob = (p + LZMA_IS_REP_0_LONG ++ + ((unsigned int)cst->state << ++ LZMA_NUM_POS_BITS_MAX) + ++ pos_state); ++ if (rc_is_bit_0(rc, prob)) { ++ rc_update_bit_0(rc, prob); ++ ++ cst->state = cst->state < LZMA_NUM_LIT_STATES ? ++ 9 : 11; ++ copy_byte(wr, cst->rep0); ++ return; ++ } else { ++ rc_update_bit_1(rc, prob); ++ } ++ } else { ++ uint32_t distance; ++ ++ rc_update_bit_1(rc, prob); ++ prob = p + LZMA_IS_REP_G1 + cst->state; ++ if (rc_is_bit_0(rc, prob)) { ++ rc_update_bit_0(rc, prob); ++ distance = cst->rep1; ++ } else { ++ rc_update_bit_1(rc, prob); ++ prob = p + LZMA_IS_REP_G2 + cst->state; ++ if (rc_is_bit_0(rc, prob)) { ++ rc_update_bit_0(rc, prob); ++ distance = cst->rep2; ++ } else { ++ rc_update_bit_1(rc, prob); ++ distance = cst->rep3; ++ cst->rep3 = cst->rep2; ++ } ++ cst->rep2 = cst->rep1; ++ } ++ cst->rep1 = cst->rep0; ++ cst->rep0 = distance; ++ } ++ cst->state = cst->state < LZMA_NUM_LIT_STATES ? 8 : 11; ++ prob = p + LZMA_REP_LEN_CODER; ++ } ++ ++ prob_len = prob + LZMA_LEN_CHOICE; ++ if (rc_is_bit_0(rc, prob_len)) { ++ rc_update_bit_0(rc, prob_len); ++ prob_len = (prob + LZMA_LEN_LOW ++ + ((unsigned int)pos_state << ++ LZMA_LEN_NUM_LOW_BITS)); ++ offset = 0; ++ num_bits = LZMA_LEN_NUM_LOW_BITS; ++ } else { ++ rc_update_bit_1(rc, prob_len); ++ prob_len = prob + LZMA_LEN_CHOICE_2; ++ if (rc_is_bit_0(rc, prob_len)) { ++ rc_update_bit_0(rc, prob_len); ++ prob_len = (prob + LZMA_LEN_MID ++ + ((unsigned int)pos_state << ++ LZMA_LEN_NUM_MID_BITS)); ++ offset = 1 << LZMA_LEN_NUM_LOW_BITS; ++ num_bits = LZMA_LEN_NUM_MID_BITS; ++ } else { ++ rc_update_bit_1(rc, prob_len); ++ prob_len = prob + LZMA_LEN_HIGH; ++ offset = ((1 << LZMA_LEN_NUM_LOW_BITS) ++ + (1 << LZMA_LEN_NUM_MID_BITS)); ++ num_bits = LZMA_LEN_NUM_HIGH_BITS; ++ } ++ } ++ ++ rc_bit_tree_decode(rc, prob_len, num_bits, &len); ++ len += offset; ++ ++ if (cst->state < 4) { ++ int pos_slot; ++ ++ cst->state += LZMA_NUM_LIT_STATES; ++ prob = ++ p + LZMA_POS_SLOT + ++ ((unsigned int)(len < ++ LZMA_NUM_LEN_TO_POS_STATES ? len : ++ LZMA_NUM_LEN_TO_POS_STATES - 1) ++ << LZMA_NUM_POS_SLOT_BITS); ++ rc_bit_tree_decode(rc, prob, ++ LZMA_NUM_POS_SLOT_BITS, ++ &pos_slot); ++ if (pos_slot >= LZMA_START_POS_MODEL_INDEX) { ++ unsigned int i; ++ int mi; ++ num_bits = ((unsigned int)pos_slot >> 1) - 1; ++ cst->rep0 = 2 | ((unsigned int)pos_slot & 1); ++ if (pos_slot < LZMA_END_POS_MODEL_INDEX) { ++ cst->rep0 <<= (unsigned int)num_bits; ++ prob = p + LZMA_SPEC_POS + ++ cst->rep0 - pos_slot - 1; ++ } else { ++ num_bits -= LZMA_NUM_ALIGN_BITS; ++ while (num_bits--) ++ cst->rep0 = (cst->rep0 << 1) | ++ (unsigned int)rc_direct_bit(rc); ++ prob = p + LZMA_ALIGN; ++ cst->rep0 <<= LZMA_NUM_ALIGN_BITS; ++ num_bits = LZMA_NUM_ALIGN_BITS; ++ } ++ i = 1; ++ mi = 1; ++ while (num_bits--) { ++ if (rc_get_bit(rc, prob + mi, &mi)) ++ cst->rep0 |= i; ++ i <<= 1; ++ } ++ } else { ++ cst->rep0 = pos_slot; ++ } ++ if (++(cst->rep0) == 0) ++ return; ++ } ++ ++ len += LZMA_MATCH_MIN_LEN; ++ ++ copy_bytes(wr, cst->rep0, len); ++} ++ ++static inline int unlzma(unsigned char *buf, int in_len, unsigned char *output, ++ int *posp) ++{ ++ struct lzma_header header; ++ unsigned int lc, pb, lp; ++ uint32_t pos_state_mask; ++ uint32_t literal_pos_mask; ++ uint16_t *p = NULL; ++ int num_probs; ++ struct rc rc; ++ int i, mi; ++ struct writer wr; ++ struct cstate cst; ++ unsigned char *inbuf = NULL; ++ int ret = -1; ++ int ix = 0; ++ ++ if (buf) ++ inbuf = buf; ++ if (!inbuf) { ++ error("Could not allocate input bufer"); ++ goto exit_0; ++ } ++ ++ cst.state = 0; ++ cst.rep0 = cst.rep1 = cst.rep2 = cst.rep3 = 1; ++ ++ wr.header = &header; ++ wr.flush = NULL; ++ wr.global_pos = 0; ++ wr.previous_byte = 0; ++ wr.buffer_pos = 0; ++ ++ rc_init(&rc, inbuf, in_len); ++ ++ for (i = 0; i < sizeof(header); i++) { ++ if (rc.ptr >= rc.buffer_end) ++ rc_read(&rc); ++ ((unsigned char *)&header)[i] = *rc.ptr++; ++ } ++ ++ if (header.pos >= (9 * 5 * 5)) ++ error("bad header"); ++ ++ mi = 0; ++ lc = header.pos; ++ while (lc >= 9) { ++ mi++; ++ lc -= 9; ++ } ++ pb = 0; ++ lp = mi; ++ while (lp >= 5) { ++ pb++; ++ lp -= 5; ++ } ++ pos_state_mask = (1 << pb) - 1; ++ literal_pos_mask = (1 << lp) - 1; ++ ++ ENDIAN_CONVERT(header.dict_size); ++ ENDIAN_CONVERT(header.dst_size); ++ ++ if (header.dict_size == 0) ++ header.dict_size = 1; ++ ++ if (output) { ++ wr.buffer = output; ++ } else { ++ wr.bufsize = MIN(header.dst_size, header.dict_size); ++ wr.buffer = large_malloc(wr.bufsize); ++ } ++ if (wr.buffer == NULL) ++ goto exit_1; ++ ++ num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)); ++ p = (uint16_t *) large_malloc(num_probs * sizeof(*p)); ++ if (p == 0) ++ goto exit_2; ++ num_probs = LZMA_LITERAL + (LZMA_LIT_SIZE << (lc + lp)); ++ for (i = 0; i < num_probs; i++) ++ p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1; ++ ++ rc_init_code(&rc); ++ ++ while (get_pos(&wr) < header.dst_size) { ++ int pos_state = get_pos(&wr) & pos_state_mask; ++ uint16_t *prob = p + LZMA_IS_MATCH + ++ (cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state; ++ if (rc_is_bit_0(&rc, prob)) { ++ process_bit0(&wr, &rc, &cst, p, pos_state, prob, ++ lc, literal_pos_mask); ++ } else { ++ process_bit1(&wr, &rc, &cst, p, pos_state, prob); ++ if (cst.rep0 == 0) ++ break; ++ } ++ while (ix++ > 10240) { ++ ix = 0; ++ putstr("."); ++ } ++ } ++ ++ if (posp) ++ *posp = rc.ptr - rc.buffer; ++ if (wr.flush) ++ wr.flush(wr.buffer, wr.buffer_pos); ++ ret = 0; ++ large_free(p); ++exit_2: ++ if (!output) ++ large_free(wr.buffer); ++exit_1: ++ /* ++ if (!buf) ++ free(inbuf); ++ */ ++exit_0: ++ return ret; ++} ++ ++static int decompress(unsigned char *buf, int in_len, unsigned char *output) ++{ ++ return unlzma(buf, in_len - 4, output, NULL); ++} +diff --git a/lib/vsprintf.c b/lib/vsprintf.c +index b4edee2..7cd601c 100644 +--- a/lib/vsprintf.c ++++ b/lib/vsprintf.c +@@ -888,3 +888,16 @@ char *strmhz(char *buf, unsigned long hz) + + return buf; + } ++ ++char *ultohstr(unsigned long long size) ++{ ++ int ix; ++ static char buffer[20]; ++ char *fmt[] = { ++ "%u", "%uK", "%uM", "%uG", "%uT", "%uT"}; ++ for (ix = 0; (ix < 5) && !(size & 0x3FF) && size; ix++) { ++ size = (size >> 10); ++ } ++ sprintf(buffer, fmt[ix], size); ++ return buffer; ++} +\ No newline at end of file +diff --git a/net/net.c b/net/net.c +index 5199d67..8fcfc68 100644 +--- a/net/net.c ++++ b/net/net.c +@@ -911,6 +911,9 @@ static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp) + int offset8, start, len, done = 0; + u16 ip_off = ntohs(ip->ip_off); + ++ if (ip->ip_len < IP_MIN_FRAG_DATAGRAM_SIZE) ++ return NULL; ++ + /* payload starts after IP header, this fragment is in there */ + payload = (struct hole *)(pkt_buff + IP_HDR_SIZE); + offset8 = (ip_off & IP_OFFS); +diff --git a/net/nfs.c b/net/nfs.c +index 97e62f1..d119f16 100644 +--- a/net/nfs.c ++++ b/net/nfs.c +@@ -54,7 +54,7 @@ static ulong nfs_timeout = NFS_TIMEOUT; + + static char dirfh[NFS_FHSIZE]; /* NFSv2 / NFSv3 file handle of directory */ + static char filefh[NFS3_FHSIZE]; /* NFSv2 / NFSv3 file handle */ +-static int filefh3_length; /* (variable) length of filefh when NFSv3 */ ++static unsigned int filefh3_length; /* (variable) length of filefh when NFSv3 */ + + static enum net_loop_state nfs_download_state; + static struct in_addr nfs_server_ip; +@@ -574,8 +574,6 @@ static int nfs_lookup_reply(uchar *pkt, unsigned len) + filefh3_length = ntohl(rpc_pkt.u.reply.data[1]); + if (filefh3_length > NFS3_FHSIZE) + filefh3_length = NFS3_FHSIZE; +- if (((uchar *)&(rpc_pkt.u.reply.data[0]) - (uchar *)(&rpc_pkt) + filefh3_length) > len) +- return -NFS_RPC_DROP; + memcpy(filefh, rpc_pkt.u.reply.data + 2, filefh3_length); + } + +diff --git a/product/Kconfig b/product/Kconfig +new file mode 100644 +index 0000000..2fc0448 +--- /dev/null ++++ b/product/Kconfig +@@ -0,0 +1,7 @@ ++menu "Product" ++ ++source "product/ot_osd/Kconfig" ++source "product/update/Kconfig" ++source "product/security_subsys/Kconfig" ++ ++endmenu +diff --git a/product/audio/acodec/v750/Makefile b/product/audio/acodec/v750/Makefile +new file mode 100644 +index 0000000..420b108 +--- /dev/null ++++ b/product/audio/acodec/v750/Makefile +@@ -0,0 +1,3 @@ ++sinclude $(TOPDIR)/config.mk ++ ++obj-y += acodec.o +\ No newline at end of file +diff --git a/product/audio/acodec/v750/acodec.c b/product/audio/acodec/v750/acodec.c +new file mode 100644 +index 0000000..0103a7f +--- /dev/null ++++ b/product/audio/acodec/v750/acodec.c +@@ -0,0 +1,609 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "audio_ao.h" ++#include "acodec_def.h" ++#include "acodec.h" ++ ++#define io_address(x) (x) ++ ++void *g_acodec_crg_reg = NULL; ++ ++static unsigned long g_acodec_base; ++ ++unsigned int acodec_hal_read_reg(unsigned int offset) ++{ ++ return (*(volatile unsigned int *)((unsigned long)g_acodec_base + (unsigned int)offset)); ++} ++ ++void acodec_hal_write_reg(unsigned int offset, unsigned int value) ++{ ++ *(volatile unsigned int *)((unsigned long)(g_acodec_base) + (unsigned int)(offset)) = value; ++} ++ ++void acodec_hal_dump(unsigned int offset) ++{ ++ printf("addr: %8lx value: %x\n", ((unsigned long)g_acodec_base + (unsigned int)offset), ++ acodec_hal_read_reg(offset)); ++} ++ ++static inline void acodec_reg_write32(unsigned long value, unsigned long mask, unsigned long addr) ++{ ++ unsigned long t; ++ ++ t = readl((const volatile void *)addr); ++ t &= ~mask; ++ t |= value & mask; ++ writel(t, (volatile void *)addr); ++} ++ ++static inline void acodec_regsetbit(unsigned long value, unsigned long offset, unsigned long addr) ++{ ++ unsigned long t, mask; ++ ++ mask = 1 << offset; ++ t = readl(addr); ++ t &= ~mask; ++ t |= (value << offset) & mask; ++ writel(t, addr); ++} ++ ++static inline void reg_read(unsigned long *pvalue, unsigned long addr) ++{ ++ *pvalue = readl((const volatile void *)addr); ++} ++ ++static void acodec_ana_power_on_step_0(void) ++{ ++ acodec_anareg0 acodec_anareg0; ++ acodec_anareg1 acodec_anareg1; ++ acodec_anareg2 acodec_anareg2; ++ acodec_anareg3 acodec_anareg3; ++ acodec_anareg4 acodec_anareg4; ++ acodec_anareg5 acodec_anareg5; ++ ++ acodec_anareg0.ul32 = ACODEC_ANAREG0_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ acodec_anareg1.ul32 = ACODEC_ANAREG1_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, acodec_anareg1.ul32); ++ acodec_anareg2.ul32 = ACODEC_ANAREG2_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG2_ADDR, acodec_anareg2.ul32); ++ acodec_anareg3.ul32 = ACODEC_ANAREG3_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ acodec_anareg4.ul32 = ACODEC_ANAREG4_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG4_ADDR, acodec_anareg4.ul32); ++ acodec_anareg5.ul32 = ACODEC_ANAREG5_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG5_ADDR, acodec_anareg5.ul32); ++} ++ ++static void acodec_ana_power_on_step_1_6(void) ++{ ++ acodec_anareg0 acodec_anareg0; ++ acodec_anareg3 acodec_anareg3; ++ ++ /* ************ depop ************ */ ++ /* 2. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.rstn = 0x1; ++ acodec_anareg3.bits.pd_dac_clk = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 3. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.pop_rst = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 5. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_ctcm_rx = 0x0; ++ acodec_anareg0.bits.pd_ctcm_tx = 0x0; ++ acodec_anareg0.bits.pd_dac_vref = 0x0; ++ acodec_anareg0.bits.pd_ibias = 0x0; ++ acodec_anareg0.bits.pd_ldo = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ /* 6. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.pd_dacl_dff = 0x0; ++ acodec_anareg3.bits.pd_dacr_dff = 0x0; ++ acodec_anareg3.bits.mute_dacl = 0x0; ++ acodec_anareg3.bits.mute_dacr = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++} ++ ++static void acodec_ana_power_on_step_7_13(void) ++{ ++ acodec_anareg0 acodec_anareg0; ++ acodec_anareg1 acodec_anareg1; ++ acodec_anareg2 acodec_anareg2; ++ acodec_anareg3 acodec_anareg3; ++ ++ /* 7. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.pu_pop_pullb_reg = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 8. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_vref = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ acodec_anareg2.ul32 = acodec_hal_read_reg(ACODEC_ANAREG2_ADDR); ++ acodec_anareg2.bits.vref_pu_pdb = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG2_ADDR, acodec_anareg2.ul32); ++ ++ udelay(20 * 1000); /* 20 * 1000 us */ ++ ++ /* 9. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.pop_rst = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 10. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_lineoutl = 0x0; ++ acodec_anareg0.bits.pd_lineoutr = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ udelay(10 * 1000); /* 10 * 1000 us */ ++ ++ /* 11. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.pop_lineout_pull_en = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 12. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_micbias1 = 0x0; ++ acodec_anareg0.bits.pd_micbias2 = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ /* 13. */ ++ acodec_anareg1.ul32 = acodec_hal_read_reg(ACODEC_ANAREG1_ADDR); ++ acodec_anareg1.bits.pd_rctune = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, acodec_anareg1.ul32); ++} ++ ++static void acodec_ana_power_on(void) ++{ ++ acodec_anareg0 acodec_anareg0; ++ acodec_anareg1 acodec_anareg1; ++ acodec_anareg3 acodec_anareg3; ++ ++ acodec_ana_power_on_step_0(); ++ ++ acodec_ana_power_on_step_1_6(); ++ ++ acodec_ana_power_on_step_7_13(); ++ ++ /* 14. */ ++ acodec_anareg1.ul32 = acodec_hal_read_reg(ACODEC_ANAREG1_ADDR); ++ acodec_anareg1.bits.rctune = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, acodec_anareg1.ul32); ++ ++ udelay(1 * 1000); /* 1 * 1000 us */ ++ ++ /* 15. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_linein_l = 0x0; ++ acodec_anareg0.bits.pd_linein_r = 0x0; ++ acodec_anareg0.bits.pd_adcl = 0x0; ++ acodec_anareg0.bits.pd_adcr = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ /* 16. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.rstb_dac = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++} ++ ++static void acodec_ana_power_down_step_1_6(void) ++{ ++ acodec_anareg0 acodec_anareg0; ++ acodec_anareg3 acodec_anareg3; ++ ++ /* ************ depop ************ */ ++ /* 1. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_linein_l = 0x1; ++ acodec_anareg0.bits.pd_linein_r = 0x1; ++ acodec_anareg0.bits.pd_adcl = 0x1; ++ acodec_anareg0.bits.pd_adcr = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ /* 2. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_micbias1 = 0x1; ++ acodec_anareg0.bits.pd_micbias2 = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ /* 3. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.rstb_dac = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 4. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.pop_lineout_pull_en = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 5. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_lineoutl = 0x1; ++ acodec_anareg0.bits.pd_lineoutr = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ /* 6. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_dac_vref = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ /* 7. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_vref = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++} ++ ++static void acodec_ana_power_down_step_9_10(void) ++{ ++ acodec_anareg0 acodec_anareg0; ++ acodec_anareg1 acodec_anareg1; ++ acodec_anareg2 acodec_anareg2; ++ acodec_anareg3 acodec_anareg3; ++ acodec_anareg4 acodec_anareg4; ++ acodec_anareg5 acodec_anareg5; ++ ++ /* 9. */ ++ acodec_anareg3.ul32 = acodec_hal_read_reg(ACODEC_ANAREG3_ADDR); ++ acodec_anareg3.bits.pu_pop_pullb_reg = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ /* 10. */ ++ acodec_anareg0.ul32 = acodec_hal_read_reg(ACODEC_ANAREG0_ADDR); ++ acodec_anareg0.bits.pd_ctcm_rx = 0x1; ++ acodec_anareg0.bits.pd_ctcm_tx = 0x1; ++ acodec_anareg0.bits.pd_ibias = 0x1; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ acodec_anareg0.ul32 = ACODEC_ANAREG0_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ acodec_anareg1.ul32 = ACODEC_ANAREG1_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, acodec_anareg1.ul32); ++ acodec_anareg2.ul32 = ACODEC_ANAREG2_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG2_ADDR, acodec_anareg2.ul32); ++ acodec_anareg3.ul32 = ACODEC_ANAREG3_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ acodec_anareg4.ul32 = ACODEC_ANAREG4_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG4_ADDR, acodec_anareg4.ul32); ++ acodec_anareg5.ul32 = ACODEC_ANAREG5_DEFAULT; ++ acodec_hal_write_reg(ACODEC_ANAREG5_ADDR, acodec_anareg5.ul32); ++} ++ ++static void acodec_ana_power_down(void) ++{ ++ int i; ++ unsigned int value; ++ acodec_anareg2 acodec_anareg2; ++ acodec_anareg4 acodec_anareg4; ++ ++ acodec_ana_power_down_step_1_6(); ++ ++ value = 0x800; ++ for (i = 0; i < 12; i++) { /* 12:bit cnt */ ++ acodec_anareg4.ul32 = acodec_hal_read_reg(ACODEC_ANAREG4_ADDR); ++ acodec_anareg4.bits.vref_pd_res_sel = acodec_anareg4.bits.vref_pd_res_sel | value; ++ acodec_hal_write_reg(ACODEC_ANAREG4_ADDR, acodec_anareg4.ul32); ++ value = value >> 1; ++ udelay(1 * 1000); /* 1 * 1000 us */ ++ } ++ ++ acodec_anareg2.ul32 = acodec_hal_read_reg(ACODEC_ANAREG2_ADDR); ++ acodec_anareg2.bits.vref_pu_pdb = 0x0; ++ acodec_hal_write_reg(ACODEC_ANAREG2_ADDR, acodec_anareg2.ul32); ++ ++ /* 8. */ ++ value = 0x80; ++ for (i = 0; i < 8; i++) { /* 8:bit cnt */ ++ acodec_anareg4.ul32 = acodec_hal_read_reg(ACODEC_ANAREG4_ADDR); ++ acodec_anareg4.bits.pop_res_sel = acodec_anareg4.bits.pop_res_sel | value; ++ acodec_hal_write_reg(ACODEC_ANAREG4_ADDR, acodec_anareg4.ul32); ++ value = value >> 1; ++ udelay(1 * 1000); /* 1 * 1000 us */ ++ } ++ ++ acodec_ana_power_down_step_9_10(); ++} ++ ++static int acodec_soft_reset(void) ++{ ++ acodec_digctrl1 acodec_digctrl1; ++ acodec_digctrl2 acodec_digctrl2; ++ acodec_digctrl3 acodec_digctrl3; ++ acodec_digctrl4 acodec_digctrl4; ++ acodec_digctrl5 acodec_digctrl5; ++ ++ acodec_anareg0 acodec_anareg0; ++ acodec_anareg1 acodec_anareg1; ++ acodec_anareg2 acodec_anareg2; ++ acodec_anareg3 acodec_anareg3; ++ acodec_anareg4 acodec_anareg4; ++ acodec_anareg5 acodec_anareg5; ++ ++ acodec_anareg0.ul32 = 0x1C1C0000; ++ acodec_hal_write_reg(ACODEC_ANAREG0_ADDR, acodec_anareg0.ul32); ++ ++ acodec_anareg1.ul32 = 0xDF605E66; ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, acodec_anareg1.ul32); ++ ++ acodec_anareg2.ul32 = 0x00255544; ++ acodec_hal_write_reg(ACODEC_ANAREG2_ADDR, acodec_anareg2.ul32); ++ ++ acodec_anareg3.ul32 = 0x072D0451; ++ acodec_hal_write_reg(ACODEC_ANAREG3_ADDR, acodec_anareg3.ul32); ++ ++ acodec_anareg4.ul32 = 0x00000000; ++ acodec_hal_write_reg(ACODEC_ANAREG4_ADDR, acodec_anareg4.ul32); ++ ++ acodec_anareg5.ul32 = 0x00000000; ++ acodec_hal_write_reg(ACODEC_ANAREG5_ADDR, acodec_anareg5.ul32); ++ ++ acodec_digctrl1.ul32 = 0xff035a00; ++ acodec_hal_write_reg(ACODEC_DIGCTRL1_ADDR, acodec_digctrl1.ul32); ++ ++ acodec_digctrl2.ul32 = 0x08000001; ++ acodec_hal_write_reg(ACODEC_DIGCTRL2_ADDR, acodec_digctrl2.ul32); ++ ++ acodec_digctrl3.ul32 = 0x06062424; ++ acodec_hal_write_reg(ACODEC_DIGCTRL3_ADDR, acodec_digctrl3.ul32); ++ ++ acodec_digctrl4.ul32 = 0x1e1ec001; ++ acodec_hal_write_reg(ACODEC_DIGCTRL4_ADDR, acodec_digctrl4.ul32); ++ ++ acodec_digctrl5.ul32 = 0x24242424; ++ acodec_hal_write_reg(ACODEC_DIGCTRL5_ADDR, acodec_digctrl5.ul32); ++ ++ return 0; ++} ++ ++static int acodec_geti2sfs(acodec_fs acodec_fs) ++{ ++ switch (acodec_fs) { ++ case ACODEC_FS_8000: ++ case ACODEC_FS_11025: ++ case ACODEC_FS_12000: ++ return ACODEC_I2S_FS_8000; ++ case ACODEC_FS_16000: ++ case ACODEC_FS_22050: ++ case ACODEC_FS_24000: ++ return ACODEC_I2S_FS_16000; ++ case ACODEC_FS_32000: ++ case ACODEC_FS_44100: ++ case ACODEC_FS_48000: ++ return ACODEC_I2S_FS_32000; ++ case ACODEC_FS_64000: ++ case ACODEC_FS_96000: ++ return ACODEC_I2S_FS_64000; ++ default: ++ printf("Unsupport sample_rate %d.\n", acodec_fs); ++ return ACODEC_I2S_FS_BUTT; ++ } ++} ++ ++static int acodec_getadcmodesel(acodec_fs acodec_fs) ++{ ++ switch (acodec_fs) { ++ case ACODEC_FS_8000: ++ case ACODEC_FS_16000: ++ case ACODEC_FS_32000: ++ case ACODEC_FS_64000: ++ return ACODEC_ADC_MODESEL_4096; ++ case ACODEC_FS_11025: ++ case ACODEC_FS_12000: ++ case ACODEC_FS_22050: ++ case ACODEC_FS_24000: ++ case ACODEC_FS_44100: ++ case ACODEC_FS_48000: ++ case ACODEC_FS_96000: ++ return ACODEC_ADC_MODESEL_6144; ++ default: ++ printf("Unsupport sample_rate %d.\n", acodec_fs); ++ return ACODEC_I2S_FS_BUTT; ++ } ++} ++ ++static acodec_fs acodec_getacodecfs(audio_sample_rate sample_rate) ++{ ++ acodec_fs acodec_fs = ACODEC_FS_BUTT; ++ switch (sample_rate) { ++ case AUDIO_SAMPLE_RATE_8000: ++ acodec_fs = ACODEC_FS_8000; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_11025: ++ acodec_fs = ACODEC_FS_11025; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_12000: ++ acodec_fs = ACODEC_FS_12000; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_16000: ++ acodec_fs = ACODEC_FS_16000; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_22050: ++ acodec_fs = ACODEC_FS_22050; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_24000: ++ acodec_fs = ACODEC_FS_24000; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_32000: ++ acodec_fs = ACODEC_FS_32000; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_44100: ++ acodec_fs = ACODEC_FS_44100; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_48000: ++ acodec_fs = ACODEC_FS_48000; ++ break; ++ ++ default: ++ printf("Unsupport sample_rate %d.\n", sample_rate); ++ break; ++ } ++ ++ return acodec_fs; ++} ++ ++int acodec_i2s_set(audio_sample_rate sample_rate) ++{ ++ acodec_fs acodec_fs; ++ acodec_digctrl1 digctrl1; ++ acodec_anareg1 ana_reg1; ++ ++ acodec_fs = acodec_getacodecfs(sample_rate); ++ if (acodec_fs == ACODEC_FS_BUTT) { ++ printf("%s: not support enSample:%d.\n", __FUNCTION__, sample_rate); ++ return -1; ++ } ++ ++ digctrl1.ul32 = acodec_hal_read_reg(ACODEC_DIGCTRL1_ADDR); ++ digctrl1.bits.i2s1_fs_sel = acodec_geti2sfs(acodec_fs); ++ acodec_hal_write_reg(ACODEC_DIGCTRL1_ADDR, digctrl1.ul32); ++ ++ ana_reg1.ul32 = acodec_hal_read_reg(ACODEC_ANAREG1_ADDR); ++ ana_reg1.bits.mode_adcr = acodec_getadcmodesel(acodec_fs); ++ ana_reg1.bits.mode_adcl = acodec_getadcmodesel(acodec_fs); ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, ana_reg1.ul32); ++ ++ /* rctune */ ++ ana_reg1.ul32 = acodec_hal_read_reg(ACODEC_ANAREG1_ADDR); ++ ana_reg1.bits.rctune = 0; ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, ana_reg1.ul32); ++ ++ udelay(30); /* 30us */ ++ ana_reg1.ul32 = acodec_hal_read_reg(ACODEC_ANAREG1_ADDR); ++ ana_reg1.bits.rctune = 1; ++ acodec_hal_write_reg(ACODEC_ANAREG1_ADDR, ana_reg1.ul32); ++ ++ return 0; ++} ++ ++int acodec_device_init(void) ++{ ++ audio_reg_1 acodec_audio_reg; ++ acodec_digctrl1 acodec_digctrl1; ++ acodec_digctrl2 acodec_digctrl2; ++ acodec_digctrl3 acodec_digctrl3; ++ ++ unsigned int aiao_crg; ++ unsigned int aiao_cfg; ++ ++ g_acodec_base = (unsigned int)io_address(ACODEC_REGS_BASE); ++ if (g_acodec_base == 0) { ++ printf("could not ioremap acodec regs!"); ++ return -1; ++ } ++ ++ g_acodec_crg_reg = (void *)io_address(ACODEC_REGS_CRG); ++ if (g_acodec_crg_reg == NULL) { ++ printf("could not ioremap acodec regs!"); ++ return -1; ++ } ++ ++ udelay(1 * 1000); /* 1 * 1000 us */ ++ ++ /* audio crg */ ++ acodec_regsetbit(1, 1, (unsigned long)(uintptr_t)g_acodec_crg_reg); ++ acodec_regsetbit(1, 2, (unsigned long)(uintptr_t)g_acodec_crg_reg); /* 2: bit */ ++ ++ /* aiao clk */ ++ aiao_crg = (unsigned int)io_address(AIAO_CLK_TX0_CRG); ++ *(volatile unsigned int *)((unsigned long)(aiao_crg)) = 0x00152EF0; ++ aiao_cfg = (unsigned int)io_address(AIAO_CLK_TX0_CFG); ++ *(volatile unsigned int *)((unsigned long)(aiao_cfg)) = 0x00000115; ++ ++ udelay(50 * 1000); /* 50 * 1000 us */ ++ ++ /* acodec mux */ ++ acodec_audio_reg.ul32 = 0xf; ++ acodec_hal_write_reg(ACODEC_AUDIO_REG, acodec_audio_reg.ul32); ++ ++ /* digctrl */ ++ acodec_digctrl1.ul32 = 0xff035a00; ++ acodec_hal_write_reg(ACODEC_DIGCTRL1_ADDR, acodec_digctrl1.ul32); ++ ++ acodec_digctrl2.ul32 = 0x08000001; ++ acodec_hal_write_reg(ACODEC_DIGCTRL2_ADDR, acodec_digctrl2.ul32); ++ ++ acodec_digctrl3.ul32 = 0x7e7e2424; ++ acodec_hal_write_reg(ACODEC_DIGCTRL3_ADDR, acodec_digctrl3.ul32); ++ ++ udelay(1 * 1000); /* 1 * 1000 us */ ++ ++ acodec_ana_power_on(); ++ ++ udelay(1 * 1000); /* 1 * 1000 us */ ++ ++ acodec_soft_reset(); ++ ++ printf("acodec inited!\n"); ++ ++ return 0; ++} ++ ++int acodec_device_exit(void) ++{ ++ acodec_digctrl3 acodec_digctrl3; ++ ++ unsigned int aiao_crg; ++ unsigned int aiao_cfg; ++ ++ acodec_soft_reset(); ++ ++ udelay(1 * 1000); /* 1 * 1000 us */ ++ ++ acodec_ana_power_down(); ++ ++ /* digctrl */ ++ acodec_digctrl3.ul32 = 0x7f7f2424; ++ acodec_hal_write_reg(ACODEC_DIGCTRL3_ADDR, acodec_digctrl3.ul32); ++ /* audio crg */ ++ acodec_regsetbit(0, 1, (unsigned long)(uintptr_t)g_acodec_crg_reg); ++ acodec_regsetbit(0, 2, (unsigned long)(uintptr_t)g_acodec_crg_reg); /* 2: bit */ ++ ++ /* aiao clk */ ++ aiao_crg = (unsigned int)io_address(AIAO_CLK_TX0_CRG); ++ *(volatile unsigned int *)((unsigned long)(aiao_crg)) = 0x00152EF0; ++ aiao_cfg = (unsigned int)io_address(AIAO_CLK_TX0_CFG); ++ *(volatile unsigned int *)((unsigned long)(aiao_cfg)) = 0x00000115; ++ ++ printf("acodec exited!\n"); ++ return 0; ++} +diff --git a/product/audio/acodec/v750/acodec.h b/product/audio/acodec/v750/acodec.h +new file mode 100644 +index 0000000..6f96f2d +--- /dev/null ++++ b/product/audio/acodec/v750/acodec.h +@@ -0,0 +1,68 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __ACODEC_H__ ++#define __ACODEC_H__ ++ ++#include "audio_ao.h" ++ ++typedef enum { ++ ACODEC_FS_8000 = 0x1, ++ ACODEC_FS_11025 = 0x2, ++ ACODEC_FS_12000 = 0x3, ++ ACODEC_FS_16000 = 0x4, ++ ACODEC_FS_22050 = 0x5, ++ ACODEC_FS_24000 = 0x6, ++ ACODEC_FS_32000 = 0x7, ++ ACODEC_FS_44100 = 0x8, ++ ACODEC_FS_48000 = 0x9, ++ ACODEC_FS_64000 = 0xa, ++ ACODEC_FS_96000 = 0xb, ++ ++ ACODEC_FS_BUTT = 0x1c, ++} acodec_fs; ++ ++typedef enum { ++ ACODEC_I2S_FS_8000 = 0x18, ++ ACODEC_I2S_FS_11025 = 0x18, ++ ACODEC_I2S_FS_12000 = 0x18, ++ ACODEC_I2S_FS_16000 = 0x19, ++ ACODEC_I2S_FS_22050 = 0x19, ++ ACODEC_I2S_FS_24000 = 0x19, ++ ACODEC_I2S_FS_32000 = 0x1a, ++ ACODEC_I2S_FS_44100 = 0x1a, ++ ACODEC_I2S_FS_48000 = 0x1a, ++ ACODEC_I2S_FS_64000 = 0x1b, ++ ACODEC_I2S_FS_96000 = 0x1b, ++ ++ ACODEC_I2S_FS_BUTT = 0x1c, ++} acodec_i2s_fs; ++ ++typedef enum { ++ ACODEC_ADC_MODESEL_6144 = 0x0, ++ ACODEC_ADC_MODESEL_4096 = 0x1, ++ ++ ACODEC_ADC_MODESEL_BUTT = 0xff, ++} acodec_adc_mode_sel; ++ ++int acodec_i2s_set(audio_sample_rate sample_rate); ++int acodec_device_init(void); ++int acodec_device_exit(void); ++ ++#endif /* End of #ifndef __ACODEC_H__ */ +diff --git a/product/audio/acodec/v750/acodec_def.h b/product/audio/acodec/v750/acodec_def.h +new file mode 100644 +index 0000000..1393663 +--- /dev/null ++++ b/product/audio/acodec/v750/acodec_def.h +@@ -0,0 +1,322 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#ifndef __ACODEC_DEF_H__ ++#define __ACODEC_DEF_H__ ++ ++#define ACODEC_REGS_CRG 0x1201019C ++#define AIAO_CLK_TX0_CRG 0x100E0140 ++#define AIAO_CLK_TX0_CFG 0x100E0144 ++#define AIAO_REGS_BASE 0x100E0000 ++ ++#define AIAO_CLK_TX0_CRG_OFFSET 0x140 ++#define AIAO_CLK_TX0_CFG_OFFSET 0x144 ++ ++#define ACODEC_CRG_BASE 0x12010000 ++#define ACODEC_CRG_OFFSET 0x019C ++#define ACODEC_REGS_OFFSET 0x0 ++ ++#define ACODEC_REGS_BASE 0x100F0000 ++ ++/* acodec analog register */ ++#define ACODEC_ANAREG0_ADDR 0x00 ++#define ACODEC_ANAREG1_ADDR 0x04 ++#define ACODEC_ANAREG2_ADDR 0x08 ++#define ACODEC_ANAREG3_ADDR 0x0C ++#define ACODEC_ANAREG4_ADDR 0x10 ++#define ACODEC_ANAREG5_ADDR 0x14 ++ ++/* acodec dig control register */ ++#define ACODEC_DIGCTRL1_ADDR 0xCC ++#define ACODEC_DIGCTRL2_ADDR 0xD0 ++#define ACODEC_DIGCTRL3_ADDR 0xD4 ++#define ACODEC_DIGCTRL4_ADDR 0xD8 ++#define ACODEC_DIGCTRL5_ADDR 0xDC ++ ++/* default value of acodec analog register */ ++#define ACODEC_ANAREG0_DEFAULT 0x3434DFFF ++#define ACODEC_ANAREG1_DEFAULT 0xDF605E65 ++#define ACODEC_ANAREG2_DEFAULT 0x00255548 ++#define ACODEC_ANAREG3_DEFAULT 0x04293B50 ++#define ACODEC_ANAREG4_DEFAULT 0x00000000 ++#define ACODEC_ANAREG5_DEFAULT 0x00000000 ++ ++#define ACODEC_AUDIO_REG 0xE0 ++ ++/* Define the union ACODEC_ANAREG0_U */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int mute_linein_r : 1; /* [0] */ ++ unsigned int mute_linein_l : 1; /* [1] */ ++ unsigned int pd_linein_r : 1; /* [2] */ ++ unsigned int pd_linein_l : 1; /* [3] */ ++ unsigned int pd_adcr : 1; /* [4] */ ++ unsigned int pd_adcl : 1; /* [5] */ ++ unsigned int pd_lineoutr : 1; /* [6] */ ++ unsigned int pd_lineoutl : 1; /* [7] */ ++ unsigned int pd_vref : 1; /* [8] */ ++ unsigned int pd_ibias : 1; /* [9] */ ++ unsigned int pd_ctcm_rx : 1; /* [10] */ ++ unsigned int pd_ctcm_tx : 1; /* [11] */ ++ unsigned int pd_dac_vref : 1; /* [12] */ ++ unsigned int pd_ldo : 1; /* [13] */ ++ unsigned int pd_micbias2 : 1; /* [14] */ ++ unsigned int pd_micbias1 : 1; /* [15] */ ++ unsigned int linein_l_sel : 3; /* [18..16] */ ++ unsigned int linein_l_gain : 5; /* [23..19] */ ++ unsigned int linein_r_sel : 3; /* [26..24] */ ++ unsigned int linein_r_gain : 5; /* [31..27] */ ++ } bits; ++ unsigned int ul32; ++} acodec_anareg0; ++ ++/* Define the union ACODEC_ANAREG1_U */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int pd_rctune : 1; /* [0] */ ++ unsigned int rctune : 1; /* [1] */ ++ unsigned int adc_tune_sel : 1; /* [2] */ ++ unsigned int adc_tune_code : 5; /* [7..3] */ ++ unsigned int byp_adc_dwa : 1; /* [8] */ ++ unsigned int adc_the_clk_phsel : 1; /* [9] */ ++ unsigned int adc_bin_clk_phsel : 1; /* [10] */ ++ unsigned int adc_chop_clk_phsel : 1; /* [11] */ ++ unsigned int linein_chop_clk_sel : 2; /* [13..12] */ ++ unsigned int adc_chop_clk_sel : 2; /* [15..14] */ ++ unsigned int boost_adcr : 1; /* [16] */ ++ unsigned int boost_adcl : 1; /* [17] */ ++ unsigned int mode_adcr : 1; /* [18] */ ++ unsigned int mode_adcl : 1; /* [19] */ ++ unsigned int adc_dac_ib_sel : 2; /* [21..20] */ ++ unsigned int adc_fls_vref : 2; /* [23..22] */ ++ unsigned int adc_mis_selp : 4; /* [27..24] */ ++ unsigned int adc_mis_seln : 4; /* [31..28] */ ++ } bits; ++ unsigned int ul32; ++} acodec_anareg1; ++ ++/* Define the union ACODEC_ANAREG2_U */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int ana_loop : 1; /* [0] */ ++ unsigned int fs_vref : 1; /* [1] */ ++ unsigned int vref_pu_pdb : 1; /* [2] */ ++ unsigned int ldo_bk_en : 1; /* [3] */ ++ unsigned int ss_sel_vref : 4; /* [7..4] */ ++ unsigned int ibadj_linein : 2; /* [9..8] */ ++ unsigned int ibadj_adc : 2; /* [11..10] */ ++ unsigned int ibadj_ctcm : 2; /* [13..12] */ ++ unsigned int ibadj_micbias : 2; /* [15..14] */ ++ unsigned int ibadj_dac_vref : 2; /* [17..16] */ ++ unsigned int ibadj_lineout : 2; /* [19..18] */ ++ unsigned int ldo_sel : 2; /* [21..20] */ ++ unsigned int byp_chop_linein : 1; /* [22] */ ++ unsigned int byp_chop_adc : 1; /* [23] */ ++ unsigned int byp_chop_dac_vref : 1; /* [24] */ ++ unsigned int byp_chop_ctcm_tx : 1; /* [25] */ ++ unsigned int byp_chop_ctcm_rx : 1; /* [26] */ ++ unsigned int micbias_adj : 3; /* [29..27] */ ++ unsigned int reserved_0 : 2; /* [31..30] */ ++ } bits; ++ unsigned int ul32; ++} acodec_anareg2; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rstn : 1; /* [0] */ ++ unsigned int ctrl_mclk_ph : 1; /* [1] */ ++ unsigned int ctrl_clk_dac_ph : 1; /* [2] */ ++ unsigned int ctrl_clk_adc_ph : 1; /* [3] */ ++ unsigned int sel_clk_chop_dac_vref : 2; /* [5..4] */ ++ unsigned int sel_clk_chop_ctcm : 2; /* [7..6] */ ++ unsigned int mute_dacr : 1; /* [8] */ ++ unsigned int mute_dacl : 1; /* [9] */ ++ unsigned int rstb_dac : 1; /* [10] */ ++ unsigned int pd_dacr_dff : 1; /* [11] */ ++ unsigned int pd_dacl_dff : 1; /* [12] */ ++ unsigned int pd_dac_clk : 1; /* [13] */ ++ unsigned int reserved_0 : 2; /* [15..14] */ ++ unsigned int pop_sel_0h1s : 1; /* [16] */ ++ unsigned int pop_lineout_pull_en : 1; /* [17] */ ++ unsigned int pu_pop_pullb_reg : 1; /* [18] */ ++ unsigned int pop_r_track_l_en : 1; /* [19] */ ++ unsigned int pullout_weak : 1; /* [20] */ ++ unsigned int pop_spd_cfg : 2; /* [22..21] */ ++ unsigned int reserved_1 : 1; /* [23] */ ++ unsigned int pop_dis : 1; /* [24] */ ++ unsigned int pop_rst : 1; /* [25] */ ++ unsigned int pop_pdm_dly_cfg : 2; /* [27..26] */ ++ unsigned int reserved_2 : 4; /* [31..28] */ ++ } bits; ++ unsigned int ul32; ++} acodec_anareg3; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 8; /* [7..0] */ ++ unsigned int vref_pd_res_sel : 12; /* [19..8] */ ++ unsigned int reserved_1 : 4; /* [23..20] */ ++ unsigned int pop_res_sel : 8; /* [31..24] */ ++ } bits; ++ unsigned int ul32; ++} acodec_anareg4; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved : 8; /* [7..0] */ ++ } bits; ++ unsigned int ul32; ++} acodec_anareg5; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 8; /* [7..0] */ ++ unsigned int i2s2_fs_sel : 5; /* [12..8] */ ++ unsigned int i2s1_fs_sel : 5; /* [17..13] */ ++ unsigned int dig_loop : 1; /* [18] */ ++ unsigned int dig_bypass : 1; /* [19] */ ++ unsigned int i2s2_data_bits : 2; /* [21..20] */ ++ unsigned int i2s1_data_bits : 2; /* [23..22] */ ++ unsigned int adcr_en : 1; /* [24] */ ++ unsigned int adcl_en : 1; /* [25] */ ++ unsigned int dacr_en : 1; /* [26] */ ++ unsigned int dacl_en : 1; /* [27] */ ++ unsigned int adcr_rst_n : 1; /* [28] */ ++ unsigned int adcl_rst_n : 1; /* [29] */ ++ unsigned int dacr_rst_n : 1; /* [30] */ ++ unsigned int dacl_rst_n : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int ul32; ++} acodec_digctrl1; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int dacr_lrsel : 1; /* [0] */ ++ unsigned int dacr_i2ssel : 1; /* [1] */ ++ unsigned int dacl_lrsel : 1; /* [2] */ ++ unsigned int dal_i2ssel : 1; /* [3] */ ++ unsigned int reserved_0 : 15; /* [18.4] */ ++ unsigned int dacr_deemph : 2; /* [20..19] */ ++ unsigned int dacl_deemph : 2; /* [22..21] */ ++ unsigned int muter_rate : 2; /* [24..23] */ ++ unsigned int mutel_rate : 2; /* [26..25] */ ++ unsigned int dacvu : 1; /* [27] */ ++ unsigned int sunmuter : 1; /* [28] */ ++ unsigned int sunmutel : 1; /* [29] */ ++ unsigned int smuter : 1; /* [30] */ ++ unsigned int smutel : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int ul32; ++} acodec_digctrl2; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int dacl2dacr_vol : 7; /* [6..0] */ ++ unsigned int dacl2dacr_en : 1; /* [7] */ ++ unsigned int dacr2dacl_vol : 7; /* [14..8] */ ++ unsigned int dacr2dacl_en : 1; /* [15] */ ++ unsigned int dacr_vol : 7; /* [22..16] */ ++ unsigned int dacr_mute : 1; /* [23] */ ++ unsigned int dacl_vol : 7; /* [30..24] */ ++ unsigned int dacl_mute : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int ul32; ++} acodec_digctrl3; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int adcr_lrsel : 1; /* [0] */ ++ unsigned int adcr_i2ssel : 1; /* [1] */ ++ unsigned int adcl_lrsel : 1; /* [2] */ ++ unsigned int adcl_i2ssel : 1; /* [3] */ ++ unsigned int reserved_0 : 10; /* [13..4] */ ++ unsigned int adcr_hpf_en : 1; /* [14] */ ++ unsigned int adcl_hpf_en : 1; /* [15] */ ++ unsigned int adcr_vol : 7; /* [22..16] */ ++ unsigned int adcr_mute : 1; /* [23] */ ++ unsigned int adcl_vol : 7; /* [30..24] */ ++ unsigned int adcl_mute : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int ul32; ++} acodec_digctrl4; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int adcr2dacr_vol : 7; /* [6..0] */ ++ unsigned int adcr2dacr_en : 1; /* [7] */ ++ unsigned int adcl2dacr_vol : 7; /* [14..8] */ ++ unsigned int adcl2dacr_en : 1; /* [15] */ ++ unsigned int adcr2dacl_vol : 7; /* [22..16] */ ++ unsigned int adcr2dacl_en : 1; /* [23] */ ++ unsigned int adcl2dacl_vol : 7; /* [30..24] */ ++ unsigned int adcl2dacl_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int ul32; ++} acodec_digctrl5; ++ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int pad_tx_en : 1; /* [0] */ ++ unsigned int audio_rx_bclk_sel : 1; /* [1] */ ++ unsigned int rx_sd_sel : 1; /* [2] */ ++ unsigned int audio_mclk_sel : 1; /* [3] */ ++ unsigned int reserved_0 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int ul32; ++} audio_reg_1; ++ ++typedef struct { ++ volatile acodec_anareg0 audio_ana_ctrl_0; ++ volatile acodec_anareg1 audio_ana_ctrl_1; ++ volatile acodec_anareg2 audio_ana_ctrl_2; ++ volatile acodec_anareg3 audio_ana_ctrl_3; ++ volatile acodec_anareg4 audio_ana_ctrl_4; ++ volatile acodec_anareg5 audio_ana_ctrl_5; ++ volatile acodec_digctrl1 audio_ctrl_reg_1; ++ volatile acodec_digctrl2 audio_dac_reg_0; ++ volatile acodec_digctrl3 audio_dac_reg_1; ++ volatile acodec_digctrl4 audio_adc_reg_0; ++ volatile acodec_digctrl5 audio_adc_reg_1; ++ volatile audio_reg_1 audio_reg_1; ++} s_acodec_regs_type; ++ ++#endif /* End of #ifndef __ACODEC_DEF_H__ */ +diff --git a/product/audio/ao/ss101v200/Makefile b/product/audio/ao/ss101v200/Makefile +new file mode 100644 +index 0000000..279eceb +--- /dev/null ++++ b/product/audio/ao/ss101v200/Makefile +@@ -0,0 +1,19 @@ ++sinclude $(TOPDIR)/config.mk ++ ++ifeq ($(CONFIG_PRODUCTNAME), "ss101v200") ++ cflags-y += -DAO_PRODUCT_SS101V200 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss101v500") ++ cflags-y += -DAO_PRODUCT_SS101V500 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss101v300") ++ cflags-y += -DAO_PRODUCT_SS101V300 ++else ifeq ($(CONFIG_PRODUCTNAME), "ss101v600") ++ cflags-y += -DAO_PRODUCT_SS101V600 ++endif ++ ++ccflags-y += $(cflags-y) ++HOSTCFLAGS += $(cflags-y) ++CPPFLAGS += $(cflags-y) ++ ++obj-y += amp.o ++obj-y += aiao_hal.o ++obj-y += ao.o +\ No newline at end of file +diff --git a/product/audio/ao/ss101v200/aiao_ext.h b/product/audio/ao/ss101v200/aiao_ext.h +new file mode 100644 +index 0000000..83c3ae7 +--- /dev/null ++++ b/product/audio/ao/ss101v200/aiao_ext.h +@@ -0,0 +1,61 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __AIAO_EXT_H__ ++#define __AIAO_EXT_H__ ++ ++#include "aiao_hal.h" ++ ++typedef struct { ++ struct { ++ td_u32 buf_size; ++ td_u32 phy_addr; ++ td_u8 *vir_addr; ++ ++ td_u32 rptr_off_set; ++ td_u32 wptr_off_set; ++ } cir_buf; ++ ++ aio_attr aio_attr; ++ td_bool enable; ++ ++ td_u64 last_pts; ++ td_u32 frm_time; ++ td_u32 max_frm_time; ++ td_u32 isr_time; ++ td_u32 max_isr_time; ++ td_u32 aio_fifo_len; ++ td_u32 fifo_len_base; ++ td_u32 fifo_shift; ++ td_u32 trans_len; ++ ++ td_s32 chn_index[AIO_MAX_CHN_NUM]; ++ ++ td_u32 int_cnt; ++ td_u32 fifo_int_cnt; ++ td_u32 buff_int_cnt; ++ audio_track_mode track_mode; ++ td_bool mute; ++ audio_fade fade; ++ td_s32 volume; ++ td_bool mic_inl; ++ td_bool mic_inr; ++} aio_drv_dev_ctx; ++ ++#endif +diff --git a/product/audio/ao/ss101v200/aiao_hal.c b/product/audio/ao/ss101v200/aiao_hal.c +new file mode 100644 +index 0000000..cd625cc +--- /dev/null ++++ b/product/audio/ao/ss101v200/aiao_hal.c +@@ -0,0 +1,869 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "type.h" ++#include "mpp_board.h" ++#include "aiao_hal.h" ++#include "aiao_reg.h" ++#include "aiao_ext.h" ++ ++#define io_address(x) (x) ++ ++#define CHN_CNT_1 1 ++#define CHN_CNT_2 2 ++ ++static aio_state g_aio_state[AIO_MAX_NUM]; ++static td_ulong g_aio_base; ++static td_ulong g_crg_base; ++void *g_acodec_ctrl0_reg = NULL; ++void *g_acodec_fast_reg = NULL; ++ ++static td_u32 aiao_hal_read_reg(td_u32 offset) ++{ ++ return (*(volatile td_u32 *)(g_aio_base + (td_u32)offset)); ++} ++ ++static td_void aiao_hal_write_reg(td_u32 offset, td_u32 value) ++{ ++ *(volatile td_u32 *)(g_aio_base + (td_u32)(offset)) = value; ++} ++ ++static inline void aiao_reg_set_bit(unsigned long value, unsigned long offset, unsigned long addr) ++{ ++ unsigned long t, mask; ++ ++ mask = 1 << offset; ++ t = readl((td_u32 *)(td_uintptr_t)addr); ++ t &= ~mask; ++ t |= (value << offset) & mask; ++ writel(t, (td_u32 *)(td_uintptr_t)addr); ++} ++ ++td_s32 aiao_hal_sys_init(td_void) ++{ ++ g_aio_base = (td_u32)io_address(AIAO_REG_BASE); ++ if (g_aio_base == 0) { ++ return TD_FAILURE; ++ } ++ ++ g_crg_base = (td_u32)io_address(CRG_REGS_ADDR); ++ if (g_crg_base == 0) { ++ return TD_FAILURE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 aiao_hal_check_ai_ao_clk(const aio_attr *attr0, const aio_attr *attr1) ++{ ++ td_u32 chn_cnt0, chn_cnt1; ++ ++ if (attr0->chn_cnt == CHN_CNT_1) { ++ chn_cnt0 = CHN_CNT_2; ++ } else { ++ chn_cnt0 = attr0->chn_cnt; ++ } ++ ++ if (attr1->chn_cnt == CHN_CNT_1) { ++ chn_cnt1 = CHN_CNT_2; ++ } else { ++ chn_cnt1 = attr1->chn_cnt; ++ } ++ ++ if ((chn_cnt0 * aio_get_bit_cnt(attr0->bit_width)) != (chn_cnt1 * aio_get_bit_cnt(attr1->bit_width))) { ++ printf("(chn_cnt*bit_width)Fs of AI and AO should be same when u32ClkSel=1\n"); ++ return TD_FAILURE; ++ } ++ ++ if (attr0->sample_rate != attr1->sample_rate) { ++ printf("sample_rate of AI and AO should be same when u32ClkSel=1\n"); ++ return TD_FAILURE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 aiao_get_mclk_cfg(audio_dev audio_dev, const aio_attr *attr, ++ td_u32 *mclk_sel, td_bool *mclk_setted) ++{ ++ *mclk_setted = TD_FALSE; ++ ++ switch (attr->sample_rate) { ++ case AUDIO_SAMPLE_RATE_8000: ++ case AUDIO_SAMPLE_RATE_16000: ++ case AUDIO_SAMPLE_RATE_32000: ++ if ((attr->bit_width == AUDIO_BIT_WIDTH_24) || ++ ((attr->bit_width == AUDIO_BIT_WIDTH_16) && (attr->expand_flag == 2))) { /* 2: cut */ ++ *mclk_sel = AIO_MCLK_48K_1800; ++ } else { ++ *mclk_sel = AIO_MCLK_32K_1800; ++ } ++ break; ++ ++ case AUDIO_SAMPLE_RATE_12000: ++ case AUDIO_SAMPLE_RATE_24000: ++ case AUDIO_SAMPLE_RATE_48000: ++ *mclk_sel = AIO_MCLK_48K_1800; ++ break; ++ ++ case AUDIO_SAMPLE_RATE_11025: ++ case AUDIO_SAMPLE_RATE_22050: ++ case AUDIO_SAMPLE_RATE_44100: ++ *mclk_sel = AIO_MCLK_441K_1800; ++ break; ++ ++ default: ++ printf("not support this sample rate \n"); ++ return -1; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_void aiao_get_fs_bit(const aio_attr *attr, td_u32 *fs_bit) ++{ ++ if (attr->chn_cnt == CHN_CNT_1) { ++ if ((attr->bit_width == AUDIO_BIT_WIDTH_16) && (attr->expand_flag == 2)) { /* 2: cut */ ++ *fs_bit = CHN_CNT_2 * aio_get_bit_cnt(AUDIO_BIT_WIDTH_24); ++ } else { ++ *fs_bit = CHN_CNT_2 * aio_get_bit_cnt(attr->bit_width); ++ } ++ } else { ++ if ((attr->bit_width == AUDIO_BIT_WIDTH_16) && (attr->expand_flag == 2)) { /* 2: cut */ ++ *fs_bit = attr->chn_cnt * aio_get_bit_cnt(AUDIO_BIT_WIDTH_24); ++ } else { ++ *fs_bit = attr->chn_cnt * aio_get_bit_cnt(attr->bit_width); ++ } ++ } ++} ++ ++static td_s32 aiao_get_bclk_sel(td_u32 bclk_div, td_u32 *bclk_sel) ++{ ++ switch (bclk_div) { ++ case 1: /* mclk is bclk*1 */ ++ *bclk_sel = SYS_AIO_BS_CLK1; ++ break; ++ case 2: /* mclk is bclk*2 */ ++ *bclk_sel = SYS_AIO_BS_CLK2; ++ break; ++ case 3: /* mclk is bclk*3 */ ++ *bclk_sel = SYS_AIO_BS_CLK3; ++ break; ++ case 4: /* mclk is bclk*4 */ ++ *bclk_sel = SYS_AIO_BS_CLK4; ++ break; ++ case 6: /* mclk is bclk*6 */ ++ *bclk_sel = SYS_AIO_BS_CLK6; ++ break; ++ case 8: /* mclk is bclk*8 */ ++ *bclk_sel = SYS_AIO_BS_CLK8; ++ break; ++ case 12: /* mclk is bclk*12 */ ++ *bclk_sel = SYS_AIO_BS_CLK12; ++ break; ++ case 16: /* mclk is bclk*16 */ ++ *bclk_sel = SYS_AIO_BS_CLK16; ++ break; ++ case 24: /* mclk is bclk*24 */ ++ *bclk_sel = SYS_AIO_BS_CLK24; ++ break; ++ case 32: /* mclk is bclk*32 */ ++ *bclk_sel = SYS_AIO_BS_CLK32; ++ break; ++ case 48: /* mclk is bclk*48 */ ++ *bclk_sel = SYS_AIO_BS_CLK48; ++ break; ++ case 64: /* mclk is bclk*64 */ ++ *bclk_sel = SYS_AIO_BS_CLK64; ++ break; ++ default: ++ printf("not support this bclk_division ratio\n"); ++ return TD_FAILURE; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 aiao_get_lrclk_sel(td_u32 lrclk_div, td_u32 *lrclk_sel) ++{ ++ switch (lrclk_div) { ++ case 256: /* bclk is fsclk*256 */ ++ *lrclk_sel = SYS_AIO_SAMPLE_CLK256; ++ break; ++ case 128: /* bclk is fsclk*128 */ ++ *lrclk_sel = SYS_AIO_SAMPLE_CLK128; ++ break; ++ case 64: /* bclk is fsclk*64 */ ++ *lrclk_sel = SYS_AIO_SAMPLE_CLK64; ++ break; ++ case 32: /* bclk is fsclk*32 */ ++ *lrclk_sel = SYS_AIO_SAMPLE_CLK32; ++ break; ++ case 16: /* bclk is fsclk*16 */ ++ *lrclk_sel = SYS_AIO_SAMPLE_CLK16; ++ break; ++ default: ++ printf("not support this fsclk_division ratio\n"); ++ return TD_FAILURE; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 aiao_get_bclk_fsclk_div_cfg(const aio_attr *attr, td_u32 mclk_sel, td_u32 *bclk_sel, ++ td_u32 *lrclk_sel) ++{ ++ td_u32 mclk_rate_num; ++ td_u32 fs_bit; ++ td_s32 ret; ++ ++ aiao_get_fs_bit(attr, &fs_bit); ++ ++ if (mclk_sel == AIO_MCLK_48K_1800) { ++ mclk_rate_num = 48000 * 256; /* 48000Hz, 256bit */ ++ } else if (mclk_sel == AIO_MCLK_32K_1800) { ++ mclk_rate_num = 32000 * 256; /* 32000Hz, 256bit */ ++ } else if (mclk_sel == AIO_MCLK_441K_1800) { ++ mclk_rate_num = 44100 * 256; /* 44100Hz, 256bit */ ++ } else { ++ printf("not support this mclk\n"); ++ return TD_FAILURE; ++ } ++ ++ if ((mclk_rate_num % (fs_bit * attr->sample_rate)) != 0) { ++ printf("can not get the bclk_division ratio\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = aiao_get_bclk_sel(mclk_rate_num / (fs_bit * attr->sample_rate), bclk_sel); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ ret = aiao_get_lrclk_sel(fs_bit, lrclk_sel); ++ ++ return ret; ++} ++ ++static td_void aop_hal_int_en(td_u32 chn_id, td_bool en) ++{ ++ u_aiao_int_ena tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(AIAO_INT_ENA_REG); ++ ++ switch (chn_id) { ++ case 0: ++ tmp.bits.tx_ch0_int_ena = en; ++ break; ++ default: ++ break; ++ } ++ ++ aiao_hal_write_reg(AIAO_INT_ENA_REG, tmp.u32); ++} ++ ++td_void aop_hal_set_buffer_addr(td_u32 chn_id, td_u32 value) ++{ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ aiao_hal_write_reg(aop_buff_saddr_reg(chn_id), value); ++} ++ ++td_void aop_hal_set_buffer_size(td_u32 chn_id, td_u32 value) ++{ ++ u_tx_buff_size tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_buff_size_reg(chn_id)); ++ ++ tmp.bits.tx_buff_size = value; ++ ++ aiao_hal_write_reg(aop_buff_size_reg(chn_id), tmp.u32); ++} ++ ++td_void aop_hal_set_buff_wptr(td_u32 chn_id, td_u32 value) ++{ ++ u_tx_buff_wptr tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_buff_wptr_reg(chn_id)); ++ ++ tmp.bits.tx_buff_wptr = value; ++ ++ aiao_hal_write_reg(aop_buff_wptr_reg(chn_id), tmp.u32); ++} ++ ++td_void aop_hal_set_buff_rptr(td_u32 chn_id, td_u32 value) ++{ ++ u_tx_buff_rptr tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_buff_rptr_reg(chn_id)); ++ ++ tmp.bits.tx_buff_rptr = value; ++ ++ aiao_hal_write_reg(aop_buff_rptr_reg(chn_id), tmp.u32); ++} ++ ++td_void aop_hal_set_trans_size(td_u32 chn_id, td_u32 value) ++{ ++ u_tx_trans_size tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_trans_size_reg(chn_id)); ++ ++ tmp.bits.tx_trans_size = value; ++ ++ aiao_hal_write_reg(aop_trans_size_reg(chn_id), tmp.u32); ++} ++ ++static td_void aop_hal_set_tx_start(td_u32 chn_id, td_bool en) ++{ ++ u_tx_dsp_ctrl tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_ctrl_reg(chn_id)); ++ ++ tmp.bits.tx_enable = en; ++ ++ aiao_hal_write_reg(aop_ctrl_reg(chn_id), tmp.u32); ++} ++ ++static td_u32 aop_hal_get_dis_done(td_u32 chn_id) ++{ ++ u_tx_dsp_ctrl tmp; ++ ++ td_u32 status; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ return TD_FAILURE; ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_ctrl_reg(chn_id)); ++ ++ status = tmp.bits.tx_disable_done; ++ ++ return status; ++} ++ ++static td_void aop_hal_set_child_int_mask(td_u32 chn_id) ++{ ++ u_tx_int_ena tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_int_ena_reg(chn_id)); ++ ++ tmp.bits.tx_trans_int_ena = 1; ++ tmp.bits.tx_empty_int_ena = 1; ++ tmp.bits.tx_alempty_int_ena = 0; ++ tmp.bits.tx_bfifo_empty_int_ena = 1; ++ tmp.bits.tx_ififo_empty_int_ena = 1; ++ tmp.bits.tx_stop_int_ena = 0; ++ tmp.bits.tx_mfade_int_ena = 0; ++ tmp.bits.tx_dat_break_int_ena = 0; ++ tmp.bits.reserved_0 = 0; ++ aiao_hal_write_reg(aop_int_ena_reg(chn_id), tmp.u32); ++} ++ ++static td_void aop_hal_clr_child_int_all_status(td_u32 chn_id) ++{ ++ u_tx_int_clr int_mask; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ } ++ ++ int_mask.bits.tx_trans_int_clear = 1; ++ int_mask.bits.tx_empty_int_clear = 1; ++ int_mask.bits.tx_alempty_int_clear = 1; ++ int_mask.bits.tx_bfifo_empty_int_clear = 1; ++ int_mask.bits.tx_ififo_empty_int_clear = 1; ++ int_mask.bits.tx_stop_int_clear = 1; ++ int_mask.bits.tx_mfade_int_clear = 1; ++ int_mask.bits.tx_dat_break_int_clear = 1; ++ ++ aiao_hal_write_reg(aop_int_clr_reg(chn_id), int_mask.u32); ++} ++ ++td_s32 aop_hal_set_volume(td_u32 chn_id, td_s32 volume_db) ++{ ++ u_tx_dsp_ctrl tmp; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ return TD_FAILURE; ++ } ++ ++ /* 0x7F->6db 0x7E->5db 0x29->-80db 0~0x28 mute */ ++ if ((volume_db < -121) || (volume_db > 6)) { /* -121, 6: volume */ ++ printf("AoDev%u volume %d error\n", chn_id, volume_db); ++ return TD_FAILURE; ++ } ++ ++ tmp.u32 = aiao_hal_read_reg(aop_ctrl_reg(chn_id)); ++ ++ tmp.bits.volume = 0x7F - (6 - volume_db); /* 6: max volume */ ++ ++ aiao_hal_write_reg(aop_ctrl_reg(chn_id), tmp.u32); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 aop_set_switch_tx_bclk(audio_dev dev_id) ++{ ++ u_aiao_switch_tx_bclk switch_tx_bclk; ++ ++ /* internal bclk selection, for clock sharing */ ++ switch_tx_bclk.u32 = aiao_hal_read_reg(AOP_SWITCH_TX_BCLK); ++ switch (dev_id) { ++ case 0: { /* 0: AIO CHN ID */ ++ switch_tx_bclk.bits.inner_bclk_ws_sel_tx_00 = 0x8; ++ break; ++ } ++ default: { ++ printf("invalid ao dev:%d\n", dev_id); ++ return TD_FAILURE; ++ } ++ } ++ aiao_hal_write_reg(AOP_SWITCH_TX_BCLK, switch_tx_bclk.u32); ++ return TD_SUCCESS; ++} ++ ++static td_s32 aop_set_sys_ctl(audio_dev audio_dev_id, aio_type aio_type, const aio_attr *attr) ++{ ++ td_u32 mclk_sel = 0; ++ td_u32 lrclk_sel = 0; ++ td_u32 bclk_sel = 0; ++ td_bool mclk_setted; ++ td_s32 ret; ++ ++ u_i2s_crg_cfg0 i2s_crg_cfg0; ++ u_i2s_crg_cfg1 i2s_crg_cfg1; ++ ++ i2s_crg_cfg1.u32 = aiao_hal_read_reg(aop_i2s_reg_cfg1(audio_dev_id)); ++ ++ /* get clock value */ ++ ret = aiao_get_mclk_cfg(audio_dev_id, attr, &mclk_sel, &mclk_setted); ++ ret |= aiao_get_bclk_fsclk_div_cfg(attr, mclk_sel, &bclk_sel, &lrclk_sel); ++ if (ret != TD_SUCCESS) { ++ return TD_FAILURE; ++ } ++ ++ i2s_crg_cfg0.bits.aiao_mclk_div = mclk_sel; ++ i2s_crg_cfg0.bits.reserved_0 = 0; ++ aiao_hal_write_reg(aop_i2s_reg_cfg0(audio_dev_id), i2s_crg_cfg0.u32); ++ ++ i2s_crg_cfg1.bits.aiao_bclk_div = bclk_sel; ++ i2s_crg_cfg1.bits.aiao_fsclk_div = lrclk_sel; ++ ++ if (aio_is_master_mode(attr->work_mode)) { ++ i2s_crg_cfg1.bits.aiao_bclk_oen = 0; ++ i2s_crg_cfg1.bits.aiao_bclk_sel = 0; ++ } else if (aio_is_slave_mode(attr->work_mode)) { ++ i2s_crg_cfg1.bits.aiao_bclk_oen = 1; ++ i2s_crg_cfg1.bits.aiao_bclk_sel = 1; ++ } else { ++ printf("invalid aio work_mode:%d\n", attr->work_mode); ++ return TD_FAILURE; ++ } ++ ++ i2s_crg_cfg1.bits.aiao_cken = 1; ++ i2s_crg_cfg1.bits.aiao_srst_req = 0; ++ i2s_crg_cfg1.bits.aiao_bclkin_pctrl = 0; ++ i2s_crg_cfg1.bits.aiao_bclkout_pctrl = 0; ++ ++ aiao_hal_write_reg(aop_i2s_reg_cfg1(audio_dev_id), i2s_crg_cfg1.u32); ++ ++ ret = aop_set_switch_tx_bclk(audio_dev_id); ++ return ret; ++} ++ ++static td_bool check_attr_sample_rate(audio_sample_rate sample_rate) ++{ ++ if ((sample_rate != AUDIO_SAMPLE_RATE_8000) && (sample_rate != AUDIO_SAMPLE_RATE_12000) && ++ (sample_rate != AUDIO_SAMPLE_RATE_11025) && (sample_rate != AUDIO_SAMPLE_RATE_16000) && ++ (sample_rate != AUDIO_SAMPLE_RATE_22050) && (sample_rate != AUDIO_SAMPLE_RATE_24000) && ++ (sample_rate != AUDIO_SAMPLE_RATE_32000) && (sample_rate != AUDIO_SAMPLE_RATE_44100) && ++ (sample_rate != AUDIO_SAMPLE_RATE_48000)) { ++ return TD_FALSE; ++ } ++ return TD_TRUE; ++} ++ ++static td_s32 check_ao_attr_basic(const aio_attr *attr) ++{ ++ if (aio_is_master_mode(attr->work_mode) && (check_attr_sample_rate(attr->sample_rate) == TD_FALSE)) { ++ printf("invalid enSamplerate\n"); ++ return TD_FAILURE; ++ } ++ ++ if ((attr->chn_cnt != CHN_CNT_1) && (attr->chn_cnt != CHN_CNT_2)) { ++ printf("invalid u32ChnCnt:%u\n", attr->chn_cnt); ++ return TD_FAILURE; ++ } ++ ++ if ((attr->chn_cnt == CHN_CNT_1) && (attr->snd_mode == AUDIO_SOUND_MODE_STEREO)) { ++ printf("when chn=1,can't use STEREO mode\n"); ++ return TD_FAILURE; ++ } ++ ++ if (attr->bit_width != AUDIO_BIT_WIDTH_16) { ++ printf("invalid enBitwidth:%d\n", attr->bit_width); ++ return TD_FAILURE; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 check_ao_attr_other(const aio_attr *attr) ++{ ++ if (aio_is_i2s_mode(attr->work_mode)) { ++ if (attr->chn_cnt * aio_get_bit_cnt(attr->bit_width) > AIO_ONE_FIFO_BITWIDTH * 2) { /* 2 fifo */ ++ printf("I2S mode, not support chn_cnt:%u * bit_width:%dbit \n", attr->chn_cnt, ++ aio_get_bit_cnt(attr->bit_width)); ++ return TD_FAILURE; ++ } ++ } else if (aio_is_pcm_mode(attr->work_mode)) { ++ if (attr->chn_cnt * aio_get_bit_cnt(attr->bit_width) > 256) { /* 256 bit */ ++ printf("PCM mode, not support chn_cnt:%u * bit_width:%dbit \n", attr->chn_cnt, ++ aio_get_bit_cnt(attr->bit_width)); ++ return TD_FAILURE; ++ } ++ } ++ ++ if ((attr->work_mode < 0) || (attr->work_mode >= AIO_MODE_BUTT)) { ++ printf("invalid enWorkmode\n"); ++ return TD_FAILURE; ++ } ++ if ((attr->snd_mode < 0) || (attr->snd_mode >= AUDIO_SOUND_MODE_BUTT)) { ++ printf("invalid enSoundmode\n"); ++ return TD_FAILURE; ++ } ++ if ((attr->frame_num > MAX_AUDIO_FRAME_NUM) || (attr->frame_num < 2)) { /* 2 frame */ ++ printf("invalid u32FrmNum\n"); ++ return TD_FAILURE; ++ } ++ if ((attr->point_num_per_frame > MAX_AO_POINT_NUM) || (attr->point_num_per_frame < MIN_AUDIO_POINT_NUM)) { ++ printf("u32PtNumPerFrm %u is invalid!\n", attr->point_num_per_frame); ++ return TD_FAILURE; ++ } ++ if ((attr->clk_share != 0) && (attr->clk_share != 1)) { ++ printf("invalid u32ClkSel\n"); ++ return TD_FAILURE; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 check_ao_attr(const aio_attr *attr) ++{ ++ td_s32 ret; ++ ++ ret = check_ao_attr_basic(attr); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ ret = check_ao_attr_other(attr); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 aop_set_attr_reg_tx_i2s_precision(const aio_attr *attr, u_tx_if_attri *aop_attr_reg) ++{ ++ if (attr->bit_width == AUDIO_BIT_WIDTH_8) { ++ aop_attr_reg->bits.tx_i2s_precision = 0; /* 0: 8bit */ ++ } else if (attr->bit_width == AUDIO_BIT_WIDTH_16) { ++ aop_attr_reg->bits.tx_i2s_precision = 1; /* 1: 16bit */ ++ } else if (attr->bit_width == AUDIO_BIT_WIDTH_24) { ++ aop_attr_reg->bits.tx_i2s_precision = 2; /* 2: 24bit */ ++ } else { ++ return TD_FAILURE; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 aop_set_attr_reg(td_u32 chn_id, const aio_attr *attr) ++{ ++ td_s32 ret; ++ u_tx_if_attri aop_attr_reg; ++ ++ aop_attr_reg.u32 = aiao_hal_read_reg(aop_inf_attri_reg(chn_id)); ++ aop_attr_reg.bits.tx_underflow_ctrl = 1; /* send the last data */ ++ aop_attr_reg.bits.reserved_1 = 0; ++ aop_attr_reg.bits.reserved_0 = 0; ++ ++ if ((attr->work_mode == AIO_MODE_I2S_SLAVE) || (attr->work_mode == AIO_MODE_I2S_MASTER)) { ++ aop_attr_reg.bits.tx_mode = 0; /* I2S mode */ ++ aop_attr_reg.bits.tx_sd_offset = 0; ++ } else if ((attr->work_mode == AIO_MODE_PCM_SLAVE_STD) || (attr->work_mode == AIO_MODE_PCM_MASTER_STD)) { ++ aop_attr_reg.bits.tx_mode = 1; /* PCM standard mode */ ++ aop_attr_reg.bits.tx_sd_offset = 1; ++ } else if ((attr->work_mode == AIO_MODE_PCM_SLAVE_NSTD) || (attr->work_mode == AIO_MODE_PCM_MASTER_NSTD)) { ++ aop_attr_reg.bits.tx_mode = 1; /* PCM non standard mode */ ++ aop_attr_reg.bits.tx_sd_offset = 0; ++ } else { ++ return TD_FAILURE; ++ } ++ ++ if (attr->chn_cnt == CHN_CNT_1) { ++ aop_attr_reg.bits.tx_ch_num = 0; ++ } else if (attr->chn_cnt == CHN_CNT_2) { ++ aop_attr_reg.bits.tx_ch_num = 1; ++ } else { ++ return TD_FAILURE; ++ } ++ ++ ret = aop_set_attr_reg_tx_i2s_precision(attr, &aop_attr_reg); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ switch (chn_id) { ++ case 0: /* 0: AIO CHN ID */ ++ aop_attr_reg.bits.tx_sd_source_sel = 0; ++ break; ++ default: ++ return TD_FAILURE; ++ } ++ ++ aop_attr_reg.bits.tx_sd0_sel = 0; ++ aop_attr_reg.bits.tx_sd1_sel = 1; ++ aop_attr_reg.bits.tx_sd2_sel = 2; /* 2: sd3 */ ++ aop_attr_reg.bits.tx_sd3_sel = 3; /* 3: sd4 */ ++ ++ aop_attr_reg.bits.tx_trackmode = 0; ++ aiao_hal_write_reg(aop_inf_attri_reg(chn_id), aop_attr_reg.u32); ++ ++ return TD_SUCCESS; ++} ++ ++static td_void aop_set_ctrl_reg(td_u32 chn_id) ++{ ++ u_tx_dsp_ctrl aop_ctrl_reg; ++ aop_ctrl_reg.u32 = aiao_hal_read_reg(aop_ctrl_reg(chn_id)); ++ ++ aop_ctrl_reg.bits.mute_en = 0; ++ aop_ctrl_reg.bits.mute_fade_en = 0; ++ aop_ctrl_reg.bits.reserved_3 = 0; ++ aop_ctrl_reg.bits.volume = 0x79; /* 0db */ ++ aop_ctrl_reg.bits.reserved_2 = 0; ++ aop_ctrl_reg.bits.fade_in_rate = 0; ++ aop_ctrl_reg.bits.fade_out_rate = 0; ++ aop_ctrl_reg.bits.reserved_1 = 0; ++ aop_ctrl_reg.bits.bypass_en = 0; ++ aop_ctrl_reg.bits.tx_enable = 0; ++ aop_ctrl_reg.bits.reserved_0 = 0; ++ ++ aiao_hal_write_reg(aop_ctrl_reg(chn_id), aop_ctrl_reg.u32); ++} ++ ++td_s32 aop_hal_set_dev_attr(td_u32 chn_id, const aio_attr *attr) ++{ ++ td_s32 ret; ++ aio_attr *ai_attr = NULL; ++ ++ if (chn_id >= AO_DEV_MAX_NUM) { ++ printf("AoDev%u is invalid!\n", chn_id); ++ return TD_FAILURE; ++ } ++ ++ /* check AO attr */ ++ ret = check_ao_attr(attr); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ if ((attr->clk_share == 1) && (chn_id == 0)) { ++ ai_attr = &g_aio_state[chn_id].ai_attr; ++ ret = aiao_hal_check_ai_ao_clk(ai_attr, attr); ++ if (ret != TD_SUCCESS) { ++ return TD_FAILURE; ++ } ++ } ++ ++ ret = aop_set_sys_ctl(chn_id, AIO_TYPE_AO, attr); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ ret = aop_set_attr_reg(chn_id, attr); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ aop_set_ctrl_reg(chn_id); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 aop_hal_dev_enable(td_u32 chn_id) ++{ ++ u_i2s_crg_cfg1 i2s_crg_cfg1; ++ ++ aop_hal_set_child_int_mask(chn_id); ++ ++ aop_hal_int_en(chn_id, TD_TRUE); ++ ++ /* open blck and ws */ ++ i2s_crg_cfg1.u32 = aiao_hal_read_reg(aop_i2s_reg_cfg1(chn_id)); ++ i2s_crg_cfg1.bits.aiao_bclk_en = 1; ++ i2s_crg_cfg1.bits.aiao_ws_en = 1; ++ i2s_crg_cfg1.bits.aiao_srst_req = 0; ++ i2s_crg_cfg1.bits.aiao_cken = 1; ++ aiao_hal_write_reg(aop_i2s_reg_cfg1(chn_id), i2s_crg_cfg1.u32); ++ ++ aop_hal_set_tx_start(chn_id, TD_TRUE); ++ ++ return TD_SUCCESS; ++} ++ ++td_void aop_hal_dev_disable(td_u32 chn_id) ++{ ++ td_u32 cnt = 0; ++ u_i2s_crg_cfg1 i2s_crg_cfg1; ++ ++ aop_hal_set_tx_start(chn_id, TD_FALSE); ++ ++ aop_hal_int_en(chn_id, TD_FALSE); ++ ++ aop_hal_clr_child_int_all_status(chn_id); ++ ++ while (!aop_hal_get_dis_done(chn_id)) { ++ if (cnt > 100) { /* cnt is 100 */ ++ i2s_crg_cfg1.u32 = aiao_hal_read_reg(aop_i2s_reg_cfg1(chn_id)); ++ i2s_crg_cfg1.bits.aiao_srst_req = 1; ++ i2s_crg_cfg1.bits.aiao_cken = 0; ++ aiao_hal_write_reg(aop_i2s_reg_cfg1(chn_id), i2s_crg_cfg1.u32); ++ ++ udelay(10000); /* 10000us */ ++ ++ i2s_crg_cfg1.u32 = aiao_hal_read_reg(aop_i2s_reg_cfg1(chn_id)); ++ i2s_crg_cfg1.bits.aiao_cken = 1; ++ i2s_crg_cfg1.bits.aiao_srst_req = 0; ++ aiao_hal_write_reg(aop_i2s_reg_cfg1(chn_id), i2s_crg_cfg1.u32); ++ printf("Can't get ao stop flag. Ao clock is not config, please check your clock!\n"); ++ break; ++ } ++ udelay(10000); /* 10000us */ ++ cnt++; ++ } ++ ++ i2s_crg_cfg1.u32 = aiao_hal_read_reg(aop_i2s_reg_cfg1(chn_id)); ++ i2s_crg_cfg1.bits.aiao_bclk_en = 0; ++ i2s_crg_cfg1.bits.aiao_ws_en = 0; ++ i2s_crg_cfg1.bits.aiao_cken = 0; ++ i2s_crg_cfg1.bits.aiao_srst_req = 1; ++ aiao_hal_write_reg(aop_i2s_reg_cfg1(chn_id), i2s_crg_cfg1.u32); ++} ++ ++td_s32 sys_hal_apll_clk_en(td_bool clk_en) ++{ ++ if (clk_en == TD_TRUE) { ++ } ++ return 0; ++} ++ ++td_s32 sys_hal_aio_clk_sel(td_u32 clk_sel) ++{ ++ aiao_reg_set_bit(clk_sel << 5, 0x1 << 5, io_address(CRG_PERCTL103_ADDR)); /* 5:bit */ ++ ++ return 0; ++} ++ ++td_s32 sys_hal_aio_reset_sel(td_bool reset) ++{ ++ td_u32 tmp = (reset == TD_TRUE) ? 1 : 0; ++ ++ aiao_reg_set_bit(tmp, 0, io_address(CRG_PERCTL103_ADDR)); ++ ++ return 0; ++} ++ ++td_s32 sys_hal_aio_clk_en(td_bool clk_en) ++{ ++ td_u32 tmp = (clk_en == TD_TRUE) ? 1 : 0; ++ ++ aiao_reg_set_bit(tmp, 1, io_address(CRG_PERCTL103_ADDR)); ++ aiao_reg_set_bit(tmp, 2, io_address(CRG_PERCTL103_ADDR)); /* 2:bit */ ++ ++ return 0; ++} ++ ++td_s32 sys_hal_acodec_reset_sel(td_bool reset) ++{ ++ td_u32 tmp = (reset == TD_TRUE) ? 1 : 0; ++ ++ aiao_reg_set_bit(tmp, 3, io_address(CRG_PERCTL103_ADDR)); /* 3:bit */ ++ aiao_reg_set_bit(tmp, 4, io_address(CRG_PERCTL103_ADDR)); /* 4:bit */ ++ ++ return 0; ++} ++ ++td_void aiao_mod_init(td_void) ++{ ++ td_bool reset; ++ td_bool clk_en; ++ ++ reset = TD_FALSE; ++ clk_en = TD_TRUE; ++ ++ sys_hal_aio_clk_en(clk_en); ++ sys_hal_aio_reset_sel(reset); ++} ++ ++td_void aiao_mod_exit(td_void) ++{ ++ td_bool reset = TD_TRUE; ++ td_bool clk_en = TD_FALSE; ++ ++ sys_hal_aio_reset_sel(reset); ++ sys_hal_aio_clk_en(clk_en); ++} +diff --git a/product/audio/ao/ss101v200/aiao_hal.h b/product/audio/ao/ss101v200/aiao_hal.h +new file mode 100644 +index 0000000..9a498ac +--- /dev/null ++++ b/product/audio/ao/ss101v200/aiao_hal.h +@@ -0,0 +1,194 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __AIAO_HAL_H__ ++#define __AIAO_HAL_H__ ++ ++#include "type.h" ++#include "audio_ao.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif /* __cplusplus */ ++ ++#define AIO_DATA_WIDTH_8BIT 0x0 ++#define AIO_DATA_WIDTH_16BIT 0x1 ++#define AIO_DATA_WIDTH_18BIT 0x2 ++#define AIO_DATA_WIDTH_20BIT 0x3 ++#define AIO_DATA_WIDTH_24BIT 0x4 ++#define AIO_DATA_WIDTH_32BIT 0x5 ++ ++#define AIO_PCM_DATA_WIDTH_8BIT 0x0 ++#define AIO_PCM_DATA_WIDTH_16BIT 0x1 ++#define AIO_PCM_DATA_WIDTH_32BIT 0x2 ++#define AIO_PCM_DATA_WIDTH_64BIT 0x3 ++#define AIO_PCM_DATA_WIDTH_128BIT 0x4 ++ ++#define AIO_CHN_NUM_2CHN 0x0 ++#define AIO_CHN_NUM_4CHN 0x1 ++#define AIO_CHN_NUM_8CHN 0x2 ++#define AIO_CHN_NUM_16CHN 0x3 ++ ++#define AIO_ONE_FIFO_BITWIDTH 128 ++#define aio_get_bit_cnt(bit_width) (8 << (bit_width)) ++#define aio_time_diff_us(a, b) (((a).tv_sec - (b).tv_sec) * 1000000 + (a).tv_usec - (b).tv_usec) ++ ++#define aio_is_pcm_mode(mode) \ ++ ((mode) == AIO_MODE_PCM_SLAVE_STD || (mode) == AIO_MODE_PCM_SLAVE_NSTD || \ ++ (mode) == AIO_MODE_PCM_MASTER_STD || (mode) == AIO_MODE_PCM_MASTER_NSTD) ++#define aio_is_i2s_mode(mode) ((mode) == AIO_MODE_I2S_MASTER || (mode) == AIO_MODE_I2S_SLAVE) ++#define aio_is_master_mode(mode) \ ++ ((mode) == AIO_MODE_I2S_MASTER || (mode) == AIO_MODE_PCM_MASTER_STD || (mode) == AIO_MODE_PCM_MASTER_NSTD) ++#define aio_is_slave_mode(mode) \ ++ ((mode) == AIO_MODE_I2S_SLAVE || (mode) == AIO_MODE_PCM_SLAVE_STD || (mode) == AIO_MODE_PCM_SLAVE_NSTD) ++ ++#define AIP0_INTMASK 1 ++#define AOP0_INTMASK 1 ++#define AOP1_INTMASK 1 ++ ++#define AI_DEV_MAX_NUM 1 ++#define AO_DEV_MIN_NUM 0 ++#define AO_DEV_MAX_NUM 1 ++#define AIO_MAX_NUM 1 ++#define AIO_MAX_CHN_NUM 2 ++ ++#define MAX_AUDIO_FRAME_NUM 50 /* max count of audio frame in Buffer */ ++#define MAX_AUDIO_POINT_BYTES 4 /* max bytes of one sample point(now 32bit max) */ ++ ++#define MAX_VOICE_POINT_NUM 480 /* max sample per frame for voice encode */ ++ ++#define MAX_AUDIO_POINT_NUM 2048 /* max sample per frame for all encoder(aacplus:2048) */ ++#define MAX_AO_POINT_NUM 4096 /* from h3:support 4096 framelen */ ++#define MIN_AUDIO_POINT_NUM 80 /* min sample per frame */ ++#define MAX_AI_POINT_NUM 2048 /* max sample per frame for all encoder(aacplus:2048) */ ++ ++/* max length of audio frame by bytes, one frame contain many sample point */ ++#define MAX_AUDIO_FRAME_LEN (MAX_AUDIO_POINT_BYTES * MAX_AO_POINT_NUM) ++ ++/* 1800M (MCLK0/AIO Clock Source Frequency) x 2^27 */ ++#define AIO_MCLK_48K_1800 0x000DFB24 /* 48k * 256 */ ++#define AIO_MCLK_441K_1800 0x000CD856 /* 44.1k * 256 */ ++#define AIO_MCLK_32K_1800 0x00095218 /* 32k * 256 */ ++ ++typedef td_s32 audio_dev; ++ ++typedef enum { ++ AIO_TYPE_AI = 0, ++ AIO_TYPE_AO, ++ AIO_TYPE_BUTT ++} aio_type; ++ ++typedef enum { ++ AUDIO_SOUND_MODE_MONO = 0, /* mono */ ++ AUDIO_SOUND_MODE_STEREO = 1, /* stereo */ ++ AUDIO_SOUND_MODE_BUTT ++} audio_sound_mode; ++ ++typedef enum { ++ AIO_MODE_I2S_MASTER = 0, /* AIO I2S master mode */ ++ AIO_MODE_I2S_SLAVE, /* AIO I2S slave mode */ ++ AIO_MODE_PCM_SLAVE_STD, /* AIO PCM slave standard mode */ ++ AIO_MODE_PCM_SLAVE_NSTD, /* AIO PCM slave non-standard mode */ ++ AIO_MODE_PCM_MASTER_STD, /* AIO PCM master standard mode */ ++ AIO_MODE_PCM_MASTER_NSTD, /* AIO PCM master non-standard mode */ ++ AIO_MODE_BUTT ++} aio_mode; ++ ++typedef enum { ++ AUDIO_BIT_WIDTH_8 = 0, /* 8bit width */ ++ AUDIO_BIT_WIDTH_16 = 1, /* 16bit width */ ++ AUDIO_BIT_WIDTH_24 = 2, /* 24bit width */ ++ AUDIO_BIT_WIDTH_BUTT, ++} audio_bit_width; ++ ++typedef enum { ++ AUDIO_FADE_RATE_1 = 0, ++ AUDIO_FADE_RATE_2 = 1, ++ AUDIO_FADE_RATE_4 = 2, ++ AUDIO_FADE_RATE_8 = 3, ++ AUDIO_FADE_RATE_16 = 4, ++ AUDIO_FADE_RATE_32 = 5, ++ AUDIO_FADE_RATE_64 = 6, ++ AUDIO_FADE_RATE_128 = 7, ++ AUDIO_FADE_RATE_BUTT ++} audio_fade_rate; ++ ++typedef enum { ++ AUDIO_TRACK_NORMAL = 0, ++ AUDIO_TRACK_BOTH_LEFT = 1, ++ AUDIO_TRACK_BOTH_RIGHT = 2, ++ AUDIO_TRACK_EXCHANGE = 3, ++ AUDIO_TRACK_MIX = 4, ++ AUDIO_TRACK_LEFT_MUTE = 5, ++ AUDIO_TRACK_RIGHT_MUTE = 6, ++ AUDIO_TRACK_BOTH_MUTE = 7, ++ ++ AUDIO_TRACK_BUTT ++} audio_track_mode; ++ ++typedef struct { ++ td_bool fade; ++ audio_fade_rate fade_in_rate; ++ audio_fade_rate fade_out_rate; ++} audio_fade; ++ ++typedef struct { ++ audio_sample_rate sample_rate; /* sample rate */ ++ audio_bit_width bit_width; /* bit_width */ ++ aio_mode work_mode; /* master or slave mode */ ++ audio_sound_mode snd_mode; /* momo or steror */ ++ td_u32 expand_flag; /* expand 8bit to 16bit,use AI_EXPAND(only valid for AI 8bit) */ ++ td_u32 frame_num; /* frame num in buf[2,MAX_AUDIO_FRAME_NUM] */ ++ td_u32 point_num_per_frame; /* point num per frame (80/160/240/320/480/1024/2048) ++ (ADPCM IMA should add 1 point, AMR only support 160) */ ++ td_u32 chn_cnt; /* channle number on FS, valid value:1/2/4/8 */ ++ td_u32 clk_share; /* 0: AI and AO clock is separate; 1: AI and AO clock is inseparate, AI use AO's clock */ ++} aio_attr; ++ ++typedef struct { ++ td_bool initialized; /* initialed flag */ ++ td_bool ai_config; ++ td_bool ao_config; ++ aio_attr ai_attr; ++ aio_attr ao_attr; ++} aio_state; ++ ++/* Description : BSPAIO IP Driver API */ ++td_u32 aio_hal_read_reg(td_u32 offset); ++td_void aio_hal_write_reg(td_u32 offset, td_u32 value); ++ ++td_void aop_hal_set_buffer_addr(td_u32 chn_id, td_u32 value); ++td_void aop_hal_set_buffer_size(td_u32 chn_id, td_u32 value); ++td_void aop_hal_set_buff_wptr(td_u32 chn_id, td_u32 value); ++td_void aop_hal_set_buff_rptr(td_u32 chn_id, td_u32 value); ++td_void aop_hal_set_trans_size(td_u32 chn_id, td_u32 value); ++td_s32 aop_hal_set_dev_attr(td_u32 chn_id, const aio_attr *attr); ++td_s32 aop_hal_dev_enable(td_u32 chn_id); ++td_void aop_hal_dev_disable(td_u32 chn_id); ++td_s32 aop_hal_set_volume(td_u32 chn_id, td_s32 volume_db); ++ ++td_void aiao_mod_init(td_void); ++td_void aiao_mod_exit(td_void); ++td_s32 aiao_hal_sys_init(td_void); ++ ++#ifdef __cplusplus ++} ++#endif /* __cplusplus */ ++ ++#endif +diff --git a/product/audio/ao/ss101v200/aiao_reg.h b/product/audio/ao/ss101v200/aiao_reg.h +new file mode 100644 +index 0000000..f44323e +--- /dev/null ++++ b/product/audio/ao/ss101v200/aiao_reg.h +@@ -0,0 +1,689 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#ifndef __AIAO_REG_H__ ++#define __AIAO_REG_H__ ++ ++#include ++ ++#define BUFFER_ADDR_ALIGN 128 ++#define BUFFER_ADDR_SIZE_BASE 128 ++ ++#define AIAO_MAX_REG_SIZE (64 * 1024) ++#define AIAO_REG_BASE 0x100e0000 ++ ++#define AIAO_INT_ENA_REG 0x0 ++#define AIAO_INT_STATUS_REG 0x4 ++#define AIAO_INT_RAW_REG 0x8 ++ ++#define AIAO_I2S00_REG_CFG0 0x0100 ++#define AIAO_I2S00_REG_CFG1 0x0104 ++#define AIAO_I2S08_REG_CFG0 0x0140 ++#define AIAO_I2S08_REG_CFG1 0x0144 ++#define AIAO_I2S09_REG_CFG0 0x0148 ++#define AIAO_I2S09_REG_CFG1 0x014c ++ ++#define aip_i2s_reg_cfg0(n) (0x0100 + 8 * (n)) ++#define aip_i2s_reg_cfg1(n) (0x0104 + 8 * (n)) ++#define aop_i2s_reg_cfg0(n) (0x0140 + 8 * (n)) ++#define aop_i2s_reg_cfg1(n) (0x0144 + 8 * (n)) ++ ++#define AIP_SWITCH_RX_BCLK 0x0028 ++#define AOP_SWITCH_TX_BCLK 0x002c ++#define AIO_SOFT_RESET_STATUS 0x0030 ++ ++/* n = 0 */ ++#define aip_inf_attri_reg(n) (0x1000 + 0x100 * (n)) ++#define aip_ctrl_reg(n) (0x1004 + 0x100 * (n)) ++#define aip_buff_saddr_reg(n) (0x1080 + 0x100 * (n)) ++#define aip_buff_size_reg(n) (0x1084 + 0x100 * (n)) ++#define aip_buff_wptr_reg(n) (0x1088 + 0x100 * (n)) ++#define aip_buff_rptr_reg(n) (0x108c + 0x100 * (n)) ++#define aip_buff_alfull_th_reg(n) (0x1090 + 0x100 * (n)) ++#define aip_trans_size_reg(n) (0x1094 + 0x100 * (n)) ++#define aip_wptr_tmp_reg(n) (0x1098 + 0x100 * (n)) ++ ++#define aip_int_ena_reg(n) (0x10A0 + 0x100 * (n)) ++#define aip_int_raw_reg(n) (0x10A4 + 0x100 * (n)) ++#define aip_int_status_reg(n) (0x10A8 + 0x100 * (n)) ++#define aip_int_clr_reg(n) (0x10AC + 0x100 * (n)) ++ ++#define aop_inf_attri_reg(m) (0x2000 + 0x100 * (m)) ++#define aop_ctrl_reg(m) (0x2004 + 0x100 * (m)) ++ ++#define aop_buff_saddr_reg(m) (0x2080 + 0x100 * (m)) ++#define aop_buff_size_reg(m) (0x2084 + 0x100 * (m)) ++#define aop_buff_wptr_reg(m) (0x2088 + 0x100 * (m)) ++#define aop_buff_rptr_reg(m) (0x208C + 0x100 * (m)) ++#define aop_buff_alempty_th_reg(m) (0x2090 + 0x100 * (m)) ++#define aop_trans_size_reg(m) (0x2094 + 0x100 * (m)) ++#define aop_rptr_tmp_reg(m) (0x2098 + 0x100 * (m)) ++ ++#define aop_int_ena_reg(m) (0x20A0 + 0x100 * (m)) ++#define aop_int_raw_reg(m) (0x20A4 + 0x100 * (m)) ++#define aop_int_status_reg(m) (0x20A8 + 0x100 * (m)) ++#define aop_int_clr_reg(m) (0x20AC + 0x100 * (m)) ++ ++#define AIO_BUS_WRITE_REQUEST 0xFF00 ++#define AIO_BUS_READ_REQUEST 0xFF04 ++#define AIO_BUS_READ_DATA 0xFF08 ++#define AIO_BUS_WRITE_DATA 0xFF0C ++#define AIO_BUS_BVALID 0xFF10 ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_ch0_int_ena : 1; /* [0] */ ++ unsigned int rx_ch1_int_ena : 1; /* [1] */ ++ unsigned int rx_ch2_int_ena : 1; /* [2] */ ++ unsigned int rx_ch3_int_ena : 1; /* [3] */ ++ unsigned int rx_ch4_int_ena : 1; /* [4] */ ++ unsigned int rx_ch5_int_ena : 1; /* [5] */ ++ unsigned int rx_ch6_int_ena : 1; /* [6] */ ++ unsigned int rx_ch7_int_ena : 1; /* [7] */ ++ unsigned int reserved_1 : 8; /* [15..8] */ ++ unsigned int tx_ch0_int_ena : 1; /* [16] */ ++ unsigned int tx_ch1_int_ena : 1; /* [17] */ ++ unsigned int tx_ch2_int_ena : 1; /* [18] */ ++ unsigned int tx_ch3_int_ena : 1; /* [19] */ ++ unsigned int tx_ch4_int_ena : 1; /* [20] */ ++ unsigned int tx_ch5_int_ena : 1; /* [21] */ ++ unsigned int tx_ch6_int_ena : 1; /* [22] */ ++ unsigned int tx_ch7_int_ena : 1; /* [23] */ ++ unsigned int spdiftx_ch0_int_ena : 1; /* [24] */ ++ unsigned int spdiftx_ch1_int_ena : 1; /* [25] */ ++ unsigned int spdiftx_ch2_int_ena : 1; /* [26] */ ++ unsigned int spdiftx_ch3_int_ena : 1; /* [27] */ ++ unsigned int reserved_0 : 4; /* [31..28] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_aiao_int_ena; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_ch0_int_status : 1; /* [0] */ ++ unsigned int rx_ch1_int_status : 1; /* [1] */ ++ unsigned int rx_ch2_int_status : 1; /* [2] */ ++ unsigned int rx_ch3_int_status : 1; /* [3] */ ++ unsigned int rx_ch4_int_status : 1; /* [4] */ ++ unsigned int rx_ch5_int_status : 1; /* [5] */ ++ unsigned int rx_ch6_int_status : 1; /* [6] */ ++ unsigned int rx_ch7_int_status : 1; /* [7] */ ++ unsigned int reserved_1 : 8; /* [15..8] */ ++ unsigned int tx_ch0_int_status : 1; /* [16] */ ++ unsigned int tx_ch1_int_status : 1; /* [17] */ ++ unsigned int tx_ch2_int_status : 1; /* [18] */ ++ unsigned int tx_ch3_int_status : 1; /* [19] */ ++ unsigned int tx_ch4_int_status : 1; /* [20] */ ++ unsigned int tx_ch5_int_status : 1; /* [21] */ ++ unsigned int tx_ch6_int_status : 1; /* [22] */ ++ unsigned int tx_ch7_int_status : 1; /* [23] */ ++ unsigned int spdiftx_ch0_int_status : 1; /* [24] */ ++ unsigned int spdiftx_ch1_int_status : 1; /* [25] */ ++ unsigned int spdiftx_ch2_int_status : 1; /* [26] */ ++ unsigned int spdiftx_ch3_int_status : 1; /* [27] */ ++ unsigned int reserved_0 : 4; /* [31..28] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_aiao_int_status; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_ch0_int_raw : 1; /* [0] */ ++ unsigned int rx_ch1_int_raw : 1; /* [1] */ ++ unsigned int rx_ch2_int_raw : 1; /* [2] */ ++ unsigned int rx_ch3_int_raw : 1; /* [3] */ ++ unsigned int rx_ch4_int_raw : 1; /* [4] */ ++ unsigned int rx_ch5_int_raw : 1; /* [5] */ ++ unsigned int rx_ch6_int_raw : 1; /* [6] */ ++ unsigned int rx_ch7_int_raw : 1; /* [7] */ ++ unsigned int reserved_1 : 8; /* [15..8] */ ++ unsigned int tx_ch0_int_raw : 1; /* [16] */ ++ unsigned int tx_ch1_int_raw : 1; /* [17] */ ++ unsigned int tx_ch2_int_raw : 1; /* [18] */ ++ unsigned int tx_ch3_int_raw : 1; /* [19] */ ++ unsigned int tx_ch4_int_raw : 1; /* [20] */ ++ unsigned int tx_ch5_int_raw : 1; /* [21] */ ++ unsigned int tx_ch6_int_raw : 1; /* [22] */ ++ unsigned int tx_ch7_int_raw : 1; /* [23] */ ++ unsigned int spdiftx_ch0_int_raw : 1; /* [24] */ ++ unsigned int spdiftx_ch1_int_raw : 1; /* [25] */ ++ unsigned int spdiftx_ch2_int_raw : 1; /* [26] */ ++ unsigned int spdiftx_ch3_int_raw : 1; /* [27] */ ++ unsigned int reserved_0 : 4; /* [31..28] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_aiao_int_raw; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_ch0_cap : 1; /* [0] */ ++ unsigned int rx_ch1_cap : 1; /* [1] */ ++ unsigned int rx_ch2_cap : 1; /* [2] */ ++ unsigned int rx_ch3_cap : 1; /* [3] */ ++ unsigned int rx_ch4_cap : 1; /* [4] */ ++ unsigned int rx_ch5_cap : 1; /* [5] */ ++ unsigned int rx_ch6_cap : 1; /* [6] */ ++ unsigned int rx_ch7_cap : 1; /* [7] */ ++ unsigned int reserved_1 : 8; /* [15..8] */ ++ unsigned int tx_ch0_cap : 1; /* [16] */ ++ unsigned int tx_ch1_cap : 1; /* [17] */ ++ unsigned int tx_ch2_cap : 1; /* [18] */ ++ unsigned int tx_ch3_cap : 1; /* [19] */ ++ unsigned int tx_ch4_cap : 1; /* [20] */ ++ unsigned int tx_ch5_cap : 1; /* [21] */ ++ unsigned int tx_ch6_cap : 1; /* [22] */ ++ unsigned int tx_ch7_cap : 1; /* [23] */ ++ unsigned int spdiftx_ch0_cap : 1; /* [24] */ ++ unsigned int spdiftx_ch1_cap : 1; /* [25] */ ++ unsigned int spdiftx_ch2_cap : 1; /* [26] */ ++ unsigned int spdiftx_ch3_cap : 1; /* [27] */ ++ unsigned int reserved_0 : 4; /* [31..28] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_hw_capability; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int aiao_mclk_div : 27; // [26..0] ++ unsigned int reserved_0 : 5; // [31..27] ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_i2s_crg_cfg0; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int aiao_bclk_div : 4; /* [3..0] */ ++ unsigned int aiao_fsclk_div : 3; /* [6..4] */ ++ unsigned int reserved_1 : 1; /* [7] */ ++ unsigned int aiao_cken : 1; /* [8] */ ++ unsigned int aiao_srst_req : 1; /* [9] */ ++ unsigned int aiao_bclk_oen : 1; /* [10] */ ++ unsigned int aiao_bclk_sel : 1; /* [11] */ ++ unsigned int aiao_bclkin_pctrl : 1; /* [12] */ ++ unsigned int aiao_bclkout_pctrl : 1; /* [13] */ ++ unsigned int aiao_bclk_en : 1; /* [14] */ ++ unsigned int aiao_ws_en : 1; /* [15] */ ++ unsigned int reserved_0 : 16; /* [31..16] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_i2s_crg_cfg1; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int inner_bclk_ws_sel_rx_00 : 4; /* [3..0] */ ++ unsigned int inner_bclk_ws_sel_rx_01 : 4; /* [7..4] */ ++ unsigned int inner_bclk_ws_sel_rx_02 : 4; /* [11..8] */ ++ unsigned int inner_bclk_ws_sel_rx_03 : 4; /* [15..12] */ ++ unsigned int inner_bclk_ws_sel_rx_04 : 4; /* [19..16] */ ++ unsigned int inner_bclk_ws_sel_rx_05 : 4; /* [23..20] */ ++ unsigned int inner_bclk_ws_sel_rx_06 : 4; /* [27..24] */ ++ unsigned int inner_bclk_ws_sel_rx_07 : 4; /* [31..28] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_aiao_switch_rx_bclk; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int inner_bclk_ws_sel_tx_00 : 4; /* [3..0] */ ++ unsigned int inner_bclk_ws_sel_tx_01 : 4; /* [7..4] */ ++ unsigned int inner_bclk_ws_sel_tx_02 : 4; /* [11..8] */ ++ unsigned int inner_bclk_ws_sel_tx_03 : 4; /* [15..12] */ ++ unsigned int inner_bclk_ws_sel_tx_04 : 4; /* [19..16] */ ++ unsigned int inner_bclk_ws_sel_tx_05 : 4; /* [23..20] */ ++ unsigned int inner_bclk_ws_sel_tx_06 : 4; /* [27..24] */ ++ unsigned int inner_bclk_ws_sel_tx_07 : 4; /* [31..28] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_aiao_switch_tx_bclk; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_mode : 2; /* [1..0] */ ++ unsigned int rx_i2s_precision : 2; /* [3..2] */ ++ unsigned int rx_ch_num : 2; /* [5..4] */ ++ unsigned int reserved_1 : 1; /* [6] */ ++ unsigned int rx_multislot_en : 1; /* [7] */ ++ unsigned int rx_sd_offset : 8; /* [15..8] */ ++ unsigned int rx_trackmode : 3; /* [18..16] */ ++ unsigned int reserved_0 : 1; /* [19] */ ++ unsigned int rx_sd_source_sel : 4; /* [23..20] */ ++ unsigned int rx_sd0_sel : 2; /* [25..24] */ ++ unsigned int rx_sd1_sel : 2; /* [27..26] */ ++ unsigned int rx_sd2_sel : 2; /* [29..28] */ ++ unsigned int rx_sd3_sel : 2; /* [31..30] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_if_attri; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int mute_en : 1; /* [0] */ ++ unsigned int mute_fade_en : 1; /* [1] */ ++ unsigned int pause_en : 1; /* [2] */ ++ unsigned int pause_fade_en : 1; /* [3] */ ++ unsigned int reserved_3 : 4; /* [7..4] */ ++ unsigned int volume : 7; /* [14..8] */ ++ unsigned int reserved_2 : 1; /* [15] */ ++ unsigned int fade_in_rate : 4; /* [19..16] */ ++ unsigned int fade_out_rate : 4; /* [23..20] */ ++ unsigned int reserved_1 : 3; /* [26..24] */ ++ unsigned int bypass_en : 1; /* [27] */ ++ unsigned int rx_enable : 1; /* [28] */ ++ unsigned int rx_disable_done : 1; /* [29] */ ++ unsigned int reserved_0 : 2; /* [31..30] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_dsp_ctrl; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int ws_count : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_ws_cnt; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int bclk_count : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_bclk_cnt; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_buff_size : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_buff_size; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_buff_wptr : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_buff_wptr; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_buff_rptr : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_buff_rptr; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_buff_alfull_th : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_buff_alfull_th; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_trans_size : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_trans_size; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_wptr_tmp : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_wptr_tmp; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_trans_int_ena : 1; /* [0] */ ++ unsigned int rx_full_int_ena : 1; /* [1] */ ++ unsigned int rx_alfull_int_ena : 1; /* [2] */ ++ unsigned int rx_bfifo_full_int_ena : 1; /* [3] */ ++ unsigned int rx_ififo_full_int_ena : 1; /* [4] */ ++ unsigned int rx_stop_int_ena : 1; /* [5] */ ++ unsigned int reserved_0 : 26; /* [31..6] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_int_ena; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_trans_int_raw : 1; /* [0] */ ++ unsigned int rx_full_int_raw : 1; /* [1] */ ++ unsigned int rx_alfull_int_raw : 1; /* [2] */ ++ unsigned int rx_bfifo_full_int_raw : 1; /* [3] */ ++ unsigned int rx_ififo_full_int_raw : 1; /* [4] */ ++ unsigned int rx_stop_int_raw : 1; /* [5] */ ++ unsigned int reserved_0 : 26; /* [31..6] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_int_raw; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_trans_int_status : 1; /* [0] */ ++ unsigned int rx_full_int_status : 1; /* [1] */ ++ unsigned int rx_alfull_int_status : 1; /* [2] */ ++ unsigned int rx_bfifo_full_int_status : 1; /* [3] */ ++ unsigned int rx_ififo_full_int_status : 1; /* [4] */ ++ unsigned int rx_stop_int_status : 1; /* [5] */ ++ unsigned int reserved_0 : 26; /* [31..6] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_int_status; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int rx_trans_int_clear : 1; /* [0] */ ++ unsigned int rx_full_int_clear : 1; /* [1] */ ++ unsigned int rx_alfull_int_clear : 1; /* [2] */ ++ unsigned int rx_bfifo_full_int_clear : 1; /* [3] */ ++ unsigned int rx_ififo_full_int_clear : 1; /* [4] */ ++ unsigned int rx_stop_int_clear : 1; /* [5] */ ++ unsigned int reserved_0 : 26; /* [31..6] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_rx_int_clr; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_mode : 2; /* [1..0] */ ++ unsigned int tx_i2s_precision : 2; /* [3..2] */ ++ unsigned int tx_ch_num : 2; /* [5..4] */ ++ unsigned int tx_underflow_ctrl : 1; /* [6] */ ++ unsigned int reserved_1 : 1; /* [7] */ ++ unsigned int tx_sd_offset : 8; /* [15..8] */ ++ unsigned int tx_trackmode : 3; /* [18..16] */ ++ unsigned int reserved_0 : 1; /* [19] */ ++ unsigned int tx_sd_source_sel : 4; /* [23..20] */ ++ unsigned int tx_sd0_sel : 2; /* [25..24] */ ++ unsigned int tx_sd1_sel : 2; /* [27..26] */ ++ unsigned int tx_sd2_sel : 2; /* [29..28] */ ++ unsigned int tx_sd3_sel : 2; /* [31..30] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_if_attri; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int mute_en : 1; /* [0] */ ++ unsigned int mute_fade_en : 1; /* [1] */ ++ unsigned int reserved_3 : 6; /* [7..2] */ ++ unsigned int volume : 7; /* [14..8] */ ++ unsigned int reserved_2 : 1; /* [15] */ ++ unsigned int fade_in_rate : 4; /* [19..16] */ ++ unsigned int fade_out_rate : 4; /* [23..20] */ ++ unsigned int reserved_1 : 3; /* [26..24] */ ++ unsigned int bypass_en : 1; /* [27] */ ++ unsigned int tx_enable : 1; /* [28] */ ++ unsigned int tx_disable_done : 1; /* [29] */ ++ unsigned int reserved_0 : 2; /* [31..30] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_dsp_ctrl; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int mute_fade_vol : 7; /* [6..0] */ ++ unsigned int reserved_0 : 25; /* [31..7] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_dsp_status; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int ws_count : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_ws_cnt; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int bclk_count : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_bclk_cnt; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_buff_size : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_buff_size; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_buff_wptr : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_buff_wptr; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_buff_rptr : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_buff_rptr; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_buff_alempty_th : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_buff_alempty_th; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_trans_size : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_trans_size; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_rptr_tmp : 24; /* [23..0] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_rptr_tmp; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_trans_int_ena : 1; /* [0] */ ++ unsigned int tx_empty_int_ena : 1; /* [1] */ ++ unsigned int tx_alempty_int_ena : 1; /* [2] */ ++ unsigned int tx_bfifo_empty_int_ena : 1; /* [3] */ ++ unsigned int tx_ififo_empty_int_ena : 1; /* [4] */ ++ unsigned int tx_stop_int_ena : 1; /* [5] */ ++ unsigned int tx_mfade_int_ena : 1; /* [6] */ ++ unsigned int tx_dat_break_int_ena : 1; /* [7] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_int_ena; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_trans_int_raw : 1; /* [0] */ ++ unsigned int tx_empty_int_raw : 1; /* [1] */ ++ unsigned int tx_alempty_int_raw : 1; /* [2] */ ++ unsigned int tx_bfifo_empty_int_raw : 1; /* [3] */ ++ unsigned int tx_ififo_empty_int_raw : 1; /* [4] */ ++ unsigned int tx_stop_int_raw : 1; /* [5] */ ++ unsigned int tx_mfade_int_raw : 1; /* [6] */ ++ unsigned int tx_dat_break_int_raw : 1; /* [7] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_int_raw; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_trans_int_status : 1; /* [0] */ ++ unsigned int tx_empty_int_status : 1; /* [1] */ ++ unsigned int tx_alempty_int_status : 1; /* [2] */ ++ unsigned int tx_bfifo_empty_int_status : 1; /* [3] */ ++ unsigned int tx_ififo_empty_int_status : 1; /* [4] */ ++ unsigned int tx_stop_int_status : 1; /* [5] */ ++ unsigned int tx_mfade_int_status : 1; /* [6] */ ++ unsigned int tx_dat_break_int_status : 1; /* [7] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_int_status; ++ ++typedef union { ++ // Define the struct bits ++ struct { ++ unsigned int tx_trans_int_clear : 1; /* [0] */ ++ unsigned int tx_empty_int_clear : 1; /* [1] */ ++ unsigned int tx_alempty_int_clear : 1; /* [2] */ ++ unsigned int tx_bfifo_empty_int_clear : 1; /* [3] */ ++ unsigned int tx_ififo_empty_int_clear : 1; /* [4] */ ++ unsigned int tx_stop_int_clear : 1; /* [5] */ ++ unsigned int tx_mfade_int_clear : 1; /* [6] */ ++ unsigned int tx_dat_break_int_clear : 1; /* [7] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ // Define an unsigned member ++ unsigned int u32; ++} u_tx_int_clr; ++ ++#endif // __AIAO_REG_H__ +diff --git a/product/audio/ao/ss101v200/amp.c b/product/audio/ao/ss101v200/amp.c +new file mode 100644 +index 0000000..23ec3df +--- /dev/null ++++ b/product/audio/ao/ss101v200/amp.c +@@ -0,0 +1,164 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++ ++#include "amp.h" ++ ++/* if need dump, open DEBUG_AMP */ ++static inline void amp_reg_write32(unsigned long value, unsigned long mask, unsigned long addr) ++{ ++ unsigned long t; ++ ++ t = readl((const volatile void *)addr); ++ t &= ~mask; ++ t |= value & mask; ++ writel(t, (volatile void *)addr); ++ ++#ifdef DEBUG_AMP ++ printf("### fun:%s line:%d addr :%8lx mask:%8lx value: %8lx\n", __FUNCTION__, __LINE__, addr, mask, value); ++#endif ++} ++ ++#if defined(AO_PRODUCT_SS101V200) ++static void amp_unmute_mux_ss101v200_demo(void) ++{ ++ // ss101v200 demo ++ amp_reg_write32(0x1000, 0xffff, IOCFG_AHB_GPIO1_0_ADDR); ++ amp_reg_write32(0x1, 0x1, GPIO_REGS_ADDR + 0x1400); ++ amp_reg_write32(0x1, 0x1, GPIO_REGS_ADDR + 0x1004); ++#ifdef DEBUG_AMP ++ printf("ss101v200_demo amp unmute ok!\n"); ++#endif ++} ++ ++static void amp_mute_mux_ss101v200_demo(void) ++{ ++ // ss101v200 demo ++ amp_reg_write32(0x1000, 0xffff, IOCFG_AHB_GPIO1_0_ADDR); ++ amp_reg_write32(0x0, 0x1, GPIO_REGS_ADDR + 0x1400); ++ amp_reg_write32(0x0, 0x1, GPIO_REGS_ADDR + 0x1004); ++#ifdef DEBUG_AMP ++ printf("ss101v200_demo amp mute ok!\n"); ++#endif ++} ++#endif ++ ++#if defined(AO_PRODUCT_SS101V500) ++static void amp_unmute_mux_ss101v500_demo(void) ++{ ++ // ss101v500 demo ++ amp_reg_write32(0x1000, 0xffffffff, IOCFG_AHB_GPIO1_0_ADDR); ++ amp_reg_write32(0x1, 0x1, GPIO_REGS_ADDR + 0x1400); ++ amp_reg_write32(0x1, 0x1, GPIO_REGS_ADDR + 0x1004); ++#ifdef DEBUG_AMP ++ printf("ss101v500_demo amp unmute ok!\n"); ++#endif ++} ++ ++static void amp_mute_mux_ss101v500_demo(void) ++{ ++ // ss101v500 demo ++ amp_reg_write32(0x1000, 0xffffffff, IOCFG_AHB_GPIO1_0_ADDR); ++ amp_reg_write32(0x0, 0x1, GPIO_REGS_ADDR + 0x1400); ++ amp_reg_write32(0x0, 0x1, GPIO_REGS_ADDR + 0x1004); ++#ifdef DEBUG_AMP ++ printf("ss101v500_demo amp mute ok!\n"); ++#endif ++} ++#endif ++ ++#if defined(AO_PRODUCT_SS101V300) ++static void amp_unmute_mux_ss101v300_demo(void) ++{ ++ // ss101v300 demo ++ amp_reg_write32(0x1E02, 0xffffffff, IOCFG_AHB_GPIO1_5_ADDR); ++ amp_reg_write32(0x1 << 5, 0x1 << 5, GPIO_REGS_ADDR + 0x1400); /* 5 bit */ ++ amp_reg_write32(0x1 << 5, 0x1 << 5, GPIO_REGS_ADDR + 0x1080); /* 5 bit */ ++#ifdef DEBUG_AMP ++ printf("ss101v300_demo amp unmute ok!\n"); ++#endif ++} ++ ++static void amp_mute_mux_ss101v300_demo(void) ++{ ++ // ss101v300 demo ++ amp_reg_write32(0x1E02, 0xffffffff, IOCFG_AHB_GPIO1_5_ADDR); ++ amp_reg_write32(0x0 << 5, 0x1 << 5, GPIO_REGS_ADDR + 0x1400); /* 5 bit */ ++ amp_reg_write32(0x0 << 5, 0x1 << 5, GPIO_REGS_ADDR + 0x1080); /* 5 bit */ ++#ifdef DEBUG_AMP ++ printf("ss101v300_demo amp mute ok!\n"); ++#endif ++} ++#endif ++ ++#if defined(AO_PRODUCT_SS101V600) ++static void amp_unmute_mux_ss101v600_demo(void) ++{ ++ // ss101v600 demo ++ amp_reg_write32(0x1000, 0xffffffff, IOCFG_AHB_GPIO1_0_ADDR); ++ amp_reg_write32(0x1, 0x1, GPIO_REGS_ADDR + 0x1400); ++ amp_reg_write32(0x1, 0x1, GPIO_REGS_ADDR + 0x1004); ++#ifdef DEBUG_AMP ++ printf("ss101v600_demo amp unmute ok!\n"); ++#endif ++} ++ ++static void amp_mute_mux_ss101v600_demo(void) ++{ ++ // ss101v600 demo ++ amp_reg_write32(0x1000, 0xffffffff, IOCFG_AHB_GPIO1_0_ADDR); ++ amp_reg_write32(0x0, 0x1, GPIO_REGS_ADDR + 0x1400); ++ amp_reg_write32(0x0, 0x1, GPIO_REGS_ADDR + 0x1004); ++#ifdef DEBUG_AMP ++ printf("ss101v600_demo amp mute ok!\n"); ++#endif ++} ++#endif ++ ++void amp_unmute(void) ++{ ++#if defined(AO_PRODUCT_SS101V200) ++ amp_unmute_mux_ss101v200_demo(); ++#elif defined(AO_PRODUCT_SS101V500) ++ amp_unmute_mux_ss101v500_demo(); ++#elif defined(AO_PRODUCT_SS101V300) ++ amp_unmute_mux_ss101v300_demo(); ++#elif defined(AO_PRODUCT_SS101V600) ++ amp_unmute_mux_ss101v600_demo(); ++#else ++ printf("amp_unmute fail!\n"); ++#endif ++} ++ ++void amp_mute(void) ++{ ++#if defined(AO_PRODUCT_SS101V200) ++ amp_mute_mux_ss101v200_demo(); ++#elif defined(AO_PRODUCT_SS101V500) ++ amp_mute_mux_ss101v500_demo(); ++#elif defined(AO_PRODUCT_SS101V300) ++ amp_mute_mux_ss101v300_demo(); ++#elif defined(AO_PRODUCT_SS101V600) ++ amp_mute_mux_ss101v600_demo(); ++#else ++ printf("amp_mute fail!\n"); ++#endif ++} +diff --git a/product/audio/ao/ss101v200/amp.h b/product/audio/ao/ss101v200/amp.h +new file mode 100644 +index 0000000..dfa0902 +--- /dev/null ++++ b/product/audio/ao/ss101v200/amp.h +@@ -0,0 +1,35 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __AMP_H__ ++#define __AMP_H__ ++ ++#define GPIO_REGS_ADDR 0x120b0000 ++#define IOCFG_CORE_REGS_ADDR 0x120c0000 ++ ++// ss101v200 demo, ss101v500 demo ++#define IOCFG_AHB_GPIO1_0_ADDR (0x0000 + IOCFG_CORE_REGS_ADDR) ++ ++// ss101v300 demo ++#define IOCFG_AHB_GPIO1_5_ADDR (0x0014 + IOCFG_CORE_REGS_ADDR) ++ ++void amp_unmute(void); ++void amp_mute(void); ++ ++#endif +diff --git a/product/audio/ao/ss101v200/ao.c b/product/audio/ao/ss101v200/ao.c +new file mode 100644 +index 0000000..c154407 +--- /dev/null ++++ b/product/audio/ao/ss101v200/ao.c +@@ -0,0 +1,77 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "type.h" ++#include "mpp_board.h" ++#include "aiao_hal.h" ++ ++int start_ao(unsigned int addr, unsigned int size, audio_sample_rate sample_rate, ++ unsigned int chn_cnt, unsigned int vol) ++{ ++ td_u32 chn_id = 0; ++ td_u32 trans_size; ++ aio_attr attr; ++ ++ if (chn_cnt == 1) { ++ attr.snd_mode = AUDIO_SOUND_MODE_MONO; ++ } else if (chn_cnt == 2) { /* 2: chn count */ ++ attr.snd_mode = AUDIO_SOUND_MODE_STEREO; ++ } else { ++ printf("chn_cnt:%u is invalid!\n", chn_cnt); ++ return TD_FAILURE; ++ } ++ attr.bit_width = AUDIO_BIT_WIDTH_16; ++ attr.sample_rate = sample_rate; ++ attr.chn_cnt = chn_cnt; ++ attr.clk_share = 0; ++ attr.expand_flag = 0; ++ attr.frame_num = 30; /* 30: frame num */ ++ attr.point_num_per_frame = 320; /* 320: posint */ ++ attr.work_mode = AIO_MODE_I2S_MASTER; ++ trans_size = attr.point_num_per_frame << attr.bit_width; ++ ++ aiao_mod_init(); ++ aiao_hal_sys_init(); ++ aop_hal_set_dev_attr(chn_id, &attr); ++ aop_hal_set_buffer_addr(chn_id, addr); ++ aop_hal_set_buffer_size(chn_id, size); ++ aop_hal_set_buff_wptr(chn_id, size - 32); /* 32 byte */ ++ aop_hal_set_buff_rptr(chn_id, 0); ++ aop_hal_set_trans_size(chn_id, trans_size); ++ aop_hal_set_volume(chn_id, vol); ++ aop_hal_dev_enable(chn_id); ++ ++ return TD_SUCCESS; ++} ++ ++int stop_ao(void) ++{ ++ td_u32 chn_id = 0; ++ aop_hal_dev_disable(chn_id); ++ aiao_mod_exit(); ++ ++ return TD_SUCCESS; ++} +diff --git a/product/audio/ao/ss101v200/ao.h b/product/audio/ao/ss101v200/ao.h +new file mode 100644 +index 0000000..ceb7539 +--- /dev/null ++++ b/product/audio/ao/ss101v200/ao.h +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __AO_H__ ++#define __AO_H__ ++ ++int start_ao(unsigned int addr, unsigned int size, audio_sample_rate sample_rate, ++ unsigned int chn_cnt, unsigned int vol); ++int stop_ao(void); ++ ++#endif +diff --git a/product/audio/ao/ss101v200/mpp_board.h b/product/audio/ao/ss101v200/mpp_board.h +new file mode 100644 +index 0000000..f4a138c +--- /dev/null ++++ b/product/audio/ao/ss101v200/mpp_board.h +@@ -0,0 +1,414 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __BOARD_H__ ++#define __BOARD_H__ ++ ++/* ************************************* */ ++#define DDR_BUS_FR 310000000 /* ddr bus freq:310M */ ++ ++/* ************************************* */ ++#define DDRC0_REG_ADDR 0x120d0000 /* base addr of DDRCB */ ++#define DDRC_REGS_SIZE 0x10000 ++ ++#define DDRC0_REG_STAT_CFG_ADDR (DDRC0_REG_ADDR + 0x260) ++#define DDRC0_REG_STAT_WRITE_ADDR (DDRC0_REG_ADDR + 0x264) ++#define DDRC0_REG_STAT_READ_ADDR (DDRC0_REG_ADDR + 0x268) ++ ++/* ************************************* */ ++#define OTP_REGS_ADDR 0x100A0000 ++#define OTP_REGS_SIZE 0X10000 ++ ++/* ************************************* */ ++#define CRG_REGS_ADDR 0x12010000 ++#define CRG_REGS_SIZE 0X10000 ++ ++/* ************************************* */ ++#define SYS_REGS_ADDR 0x12020000 ++#define SYS_REGS_SIZE 0x8000 ++ ++/* ************************************* */ ++#define MISC_REGS_ADDR 0x12028000 ++#define MISC_REGS_SIZE 0x8000 ++ ++/* ************************************* */ ++#define VOU_REGS_ADDR 0x11280000 ++#define VOU_REGS_SIZE 0x40000 ++ ++/* ************************************* */ ++#define VGS0_REGS_ADDR 0x11300000 ++#define VGS_REGS_SIZE 0x10000 ++ ++/* ************************************* */ ++#define GDC0_BASE_ADDR 0x11110000 ++#define GDC_REGS_SIZE 0x10000 ++ ++/* ************************************* */ ++#define VPSS0_REGS_ADDR 0x11400000 ++#define VPSS_REGS_SIZE 0x10000 ++ ++/* ************************************* */ ++#define VI_CAP0_REGS_ADDR 0x11000000 ++#define VI_PROC0_REGS_ADDR 0x11200000 ++ ++/* ************************************* */ ++#define VEDU_0_REGS_ADDR 0x11410000 ++#define VEDU_REGS_SIZE 0x10000 ++ ++/* ************************************* */ ++#define JPEGU_REGS_ADDR 0x11420000 ++#define JPEGU_REGS_SIZE 0x10000 ++ ++/* ************************************** */ ++/* 0x11260000 ~ 0x1126FFFF */ ++#define JPEGD_REGS_ADDR 0x11260000 ++#define JPEGD_REGS_SIZE 0x10000 ++ ++/* ************************************* */ ++#define IVE_REGS_ADDR 0x11320000 ++#define IVE_REGS_SIZE 0x10000 ++#define IVE_CRG_RESET_REGS_ADDR 0x120100c0 ++ ++#define SVP_NNIE_0_REGS_ADDR 0x11100000 ++#define SVP_NNIE_0_REGS_SIZE 0x10000 ++ ++/* ************************************* */ ++/* Interrupt Request Number */ ++#define VOU_IRQ_NR (40 + 32) ++#define VOU1_IRQ_NR (41 + 32) ++#define VI_CAP0_IRQ_NR (43 + 32) ++#define VI_PROC0_IRQ_NR (44 + 32) ++#define VPSS0_IRQ_NR (46 + 32) ++#define VGS0_IRQ_NR (49 + 32) ++#define AIO_IRQ_NR (42 + 32) ++#define VEDU_0_IRQ_NR (47 + 32) ++#define JPEGU_IRQ_NR (48 + 32) ++#define IVE_IRQ_NR (51 + 32) ++ ++/* ************** */ ++#define SYS_PERCTL0_ADDR (0x0 + SYS_REGS_ADDR) ++#define SYS_PERCTL1_ADDR (0x4 + SYS_REGS_ADDR) ++#define SYS_PERCTL2_ADDR (0x8 + SYS_REGS_ADDR) ++#define SYS_PERCTL3_ADDR (0xc + SYS_REGS_ADDR) ++#define SYS_PERCTL4_ADDR (0x10 + SYS_REGS_ADDR) ++#define SYS_PERCTL5_ADDR (0x14 + SYS_REGS_ADDR) ++#define SYS_PERCTL6_ADDR (0x18 + SYS_REGS_ADDR) ++#define SYS_PERCTL7_ADDR (0x1c + SYS_REGS_ADDR) ++#define SYS_PERCTL8_ADDR (0x20 + SYS_REGS_ADDR) ++#define SYS_PERCTL9_ADDR (0x24 + SYS_REGS_ADDR) ++#define SYS_PERCTL10_ADDR (0x28 + SYS_REGS_ADDR) ++#define SYS_PERCTL11_ADDR (0x2C + SYS_REGS_ADDR) ++#define SYS_PERCTL12_ADDR (0x30 + SYS_REGS_ADDR) ++#define SYS_PERCTL13_ADDR (0x34 + SYS_REGS_ADDR) ++#define SYS_PERCTL14_ADDR (0x38 + SYS_REGS_ADDR) ++#define SYS_PERCTL15_ADDR (0x3C + SYS_REGS_ADDR) ++#define SYS_PERCTL16_ADDR (0x40 + SYS_REGS_ADDR) ++#define SYS_PERCTL17_ADDR (0x44 + SYS_REGS_ADDR) ++#define SYS_PERCTL18_ADDR (0x48 + SYS_REGS_ADDR) ++#define SYS_PERCTL19_ADDR (0x4c + SYS_REGS_ADDR) ++#define SYS_PERCTL20_ADDR (0x50 + SYS_REGS_ADDR) ++#define SYS_PERCTL21_ADDR (0x54 + SYS_REGS_ADDR) ++#define SYS_PERCTL22_ADDR (0x58 + SYS_REGS_ADDR) ++#define SYS_PERCTL23_ADDR (0x5c + SYS_REGS_ADDR) ++#define SYS_PERCTL24_ADDR (0x60 + SYS_REGS_ADDR) ++#define SYS_PERCTL25_ADDR (0x64 + SYS_REGS_ADDR) ++#define SYS_PERCTL26_ADDR (0x68 + SYS_REGS_ADDR) ++#define SYS_PERCTL27_ADDR (0x6C + SYS_REGS_ADDR) ++#define SYS_PERCTL28_ADDR (0x70 + SYS_REGS_ADDR) ++#define SYS_PERCTL29_ADDR (0x74 + SYS_REGS_ADDR) ++#define SYS_PERCTL30_ADDR (0x78 + SYS_REGS_ADDR) ++#define SYS_PERCTL31_ADDR (0x7C + SYS_REGS_ADDR) ++#define SYS_PERCTL32_ADDR (0x80 + SYS_REGS_ADDR) ++#define SYS_PERCTL33_ADDR (0x84 + SYS_REGS_ADDR) ++#define SYS_PERCTL34_ADDR (0x88 + SYS_REGS_ADDR) ++#define SYS_PERCTL35_ADDR (0x8C + SYS_REGS_ADDR) ++#define SYS_PERCTL36_ADDR (0x90 + SYS_REGS_ADDR) ++#define SYS_PERCTL37_ADDR (0x94 + SYS_REGS_ADDR) ++#define SYS_PERCTL38_ADDR (0x98 + SYS_REGS_ADDR) ++#define SYS_PERCTL39_ADDR (0x9C + SYS_REGS_ADDR) ++#define SYS_PERCTL40_ADDR (0xa0 + SYS_REGS_ADDR) ++#define SYS_PERCTL41_ADDR (0xa4 + SYS_REGS_ADDR) ++#define SYS_PERCTL42_ADDR (0xa8 + SYS_REGS_ADDR) ++#define SYS_PERCTL43_ADDR (0xaC + SYS_REGS_ADDR) ++#define SYS_PERCTL44_ADDR (0xb0 + SYS_REGS_ADDR) ++#define SYS_PERCTL45_ADDR (0xb4 + SYS_REGS_ADDR) ++#define SYS_PERCTL46_ADDR (0xb8 + SYS_REGS_ADDR) ++#define SYS_PERCTL47_ADDR (0xbC + SYS_REGS_ADDR) ++#define SYS_PERCTL48_ADDR (0xc0 + SYS_REGS_ADDR) ++#define SYS_PERCTL49_ADDR (0xc4 + SYS_REGS_ADDR) ++#define SYS_PERCTL50_ADDR (0xc8 + SYS_REGS_ADDR) ++#define SYS_PERCTL51_ADDR (0xcC + SYS_REGS_ADDR) ++#define SYS_PERCTL52_ADDR (0xd0 + SYS_REGS_ADDR) ++#define SYS_PERCTL53_ADDR (0xd4 + SYS_REGS_ADDR) ++#define SYS_PERCTL54_ADDR (0xd8 + SYS_REGS_ADDR) ++#define SYS_PERCTL55_ADDR (0xdC + SYS_REGS_ADDR) ++#define SYS_PERCTL56_ADDR (0xe0 + SYS_REGS_ADDR) ++#define SYS_PERCTL57_ADDR (0xe4 + SYS_REGS_ADDR) ++#define SYS_PERCTL58_ADDR (0xe8 + SYS_REGS_ADDR) ++#define SYS_PERCTL59_ADDR (0xeC + SYS_REGS_ADDR) ++#define SYS_PERCTL60_ADDR (0xf0 + SYS_REGS_ADDR) ++#define SYS_PERCTL61_ADDR (0xf4 + SYS_REGS_ADDR) ++#define SYS_PERCTL62_ADDR (0xf8 + SYS_REGS_ADDR) ++#define SYS_PERCTL63_ADDR (0xfC + SYS_REGS_ADDR) ++#define SYS_PERCTL64_ADDR (0x100 + SYS_REGS_ADDR) ++#define SYS_PERCTL65_ADDR (0x104 + SYS_REGS_ADDR) ++#define SYS_PERCTL66_ADDR (0x108 + SYS_REGS_ADDR) ++#define SYS_PERCTL67_ADDR (0x10c + SYS_REGS_ADDR) ++#define SYS_PERCTL68_ADDR (0x110 + SYS_REGS_ADDR) ++#define SYS_PERCTL69_ADDR (0x114 + SYS_REGS_ADDR) ++#define SYS_PERCTL70_ADDR (0x118 + SYS_REGS_ADDR) ++#define SYS_PERCTL71_ADDR (0x11c + SYS_REGS_ADDR) ++#define SYS_PERCTL72_ADDR (0x120 + SYS_REGS_ADDR) ++#define SYS_PERCTL73_ADDR (0x124 + SYS_REGS_ADDR) ++#define SYS_PERCTL74_ADDR (0x128 + SYS_REGS_ADDR) ++#define SYS_PERCTL75_ADDR (0x12C + SYS_REGS_ADDR) ++#define SYS_PERCTL76_ADDR (0x130 + SYS_REGS_ADDR) ++#define SYS_PERCTL77_ADDR (0x134 + SYS_REGS_ADDR) ++#define SYS_PERCTL78_ADDR (0x138 + SYS_REGS_ADDR) ++#define SYS_PERCTL79_ADDR (0x13C + SYS_REGS_ADDR) ++#define SYS_PERCTL80_ADDR (0x140 + SYS_REGS_ADDR) ++#define SYS_PERCTL81_ADDR (0x144 + SYS_REGS_ADDR) ++#define SYS_PERCTL82_ADDR (0x148 + SYS_REGS_ADDR) ++#define SYS_PERCTL83_ADDR (0x14c + SYS_REGS_ADDR) ++#define SYS_PERCTL84_ADDR (0x150 + SYS_REGS_ADDR) ++#define SYS_PERCTL85_ADDR (0x154 + SYS_REGS_ADDR) ++#define SYS_PERCTL86_ADDR (0x158 + SYS_REGS_ADDR) ++#define SYS_PERCTL87_ADDR (0x15c + SYS_REGS_ADDR) ++#define SYS_PERCTL88_ADDR (0x160 + SYS_REGS_ADDR) ++#define SYS_PERCTL89_ADDR (0x164 + SYS_REGS_ADDR) ++#define SYS_PERCTL90_ADDR (0x168 + SYS_REGS_ADDR) ++#define SYS_PERCTL91_ADDR (0x16C + SYS_REGS_ADDR) ++#define SYS_PERCTL92_ADDR (0x170 + SYS_REGS_ADDR) ++#define SYS_PERCTL93_ADDR (0x174 + SYS_REGS_ADDR) ++#define SYS_PERCTL94_ADDR (0x178 + SYS_REGS_ADDR) ++#define SYS_PERCTL95_ADDR (0x17C + SYS_REGS_ADDR) ++#define SYS_PERCTL96_ADDR (0x180 + SYS_REGS_ADDR) ++#define SYS_PERCTL97_ADDR (0x184 + SYS_REGS_ADDR) ++#define SYS_PERCTL98_ADDR (0x188 + SYS_REGS_ADDR) ++#define SYS_PERCTL99_ADDR (0x18C + SYS_REGS_ADDR) ++#define SYS_PERCTL100_ADDR (0x190 + SYS_REGS_ADDR) ++#define SYS_PERCTL101_ADDR (0x194 + SYS_REGS_ADDR) ++#define SYS_PERCTL102_ADDR (0x198 + SYS_REGS_ADDR) ++#define SYS_PERCTL103_ADDR (0x19C + SYS_REGS_ADDR) ++#define SYS_PERCTL104_ADDR (0x1a0 + SYS_REGS_ADDR) ++#define SYS_PERCTL105_ADDR (0x1a4 + SYS_REGS_ADDR) ++#define SYS_PERCTL106_ADDR (0x1a8 + SYS_REGS_ADDR) ++#define SYS_PERCTL107_ADDR (0x1aC + SYS_REGS_ADDR) ++#define SYS_PERCTL108_ADDR (0x1b0 + SYS_REGS_ADDR) ++#define SYS_PERCTL109_ADDR (0x1b4 + SYS_REGS_ADDR) ++#define SYS_PERCTL110_ADDR (0x1b8 + SYS_REGS_ADDR) ++#define SYS_PERCTL111_ADDR (0x1bC + SYS_REGS_ADDR) ++#define SYS_PERCTL112_ADDR (0x1c0 + SYS_REGS_ADDR) ++#define SYS_PERCTL113_ADDR (0x1c4 + SYS_REGS_ADDR) ++#define SYS_PERCTL114_ADDR (0x1c8 + SYS_REGS_ADDR) ++#define SYS_PERCTL115_ADDR (0x1cC + SYS_REGS_ADDR) ++#define SYS_PERCTL116_ADDR (0x1d0 + SYS_REGS_ADDR) ++#define SYS_PERCTL117_ADDR (0x1d4 + SYS_REGS_ADDR) ++#define SYS_PERCTL118_ADDR (0x1d8 + SYS_REGS_ADDR) ++#define SYS_PERCTL119_ADDR (0x1dC + SYS_REGS_ADDR) ++#define SYS_PERCTL120_ADDR (0x1e0 + SYS_REGS_ADDR) ++#define SYS_PERCTL121_ADDR (0x1e4 + SYS_REGS_ADDR) ++#define SYS_PERCTL122_ADDR (0x1e8 + SYS_REGS_ADDR) ++#define SYS_PERCTL123_ADDR (0x1eC + SYS_REGS_ADDR) ++#define SYS_PERCTL124_ADDR (0x1f0 + SYS_REGS_ADDR) ++#define SYS_PERCTL125_ADDR (0x1f4 + SYS_REGS_ADDR) ++#define SYS_PERCTL126_ADDR (0x1f8 + SYS_REGS_ADDR) ++#define SYS_PERCTL127_ADDR (0x1fC + SYS_REGS_ADDR) ++/* ************** */ ++ ++/* ************** */ ++#define CRG_PERCTL0_ADDR (0x0 + CRG_REGS_ADDR) ++#define CRG_PERCTL1_ADDR (0x4 + CRG_REGS_ADDR) ++#define CRG_PERCTL2_ADDR (0x8 + CRG_REGS_ADDR) ++#define CRG_PERCTL3_ADDR (0xc + CRG_REGS_ADDR) ++#define CRG_PERCTL4_ADDR (0x10 + CRG_REGS_ADDR) ++#define CRG_PERCTL5_ADDR (0x14 + CRG_REGS_ADDR) ++#define CRG_PERCTL6_ADDR (0x18 + CRG_REGS_ADDR) ++#define CRG_PERCTL7_ADDR (0x1c + CRG_REGS_ADDR) ++#define CRG_PERCTL8_ADDR (0x20 + CRG_REGS_ADDR) ++#define CRG_PERCTL9_ADDR (0x24 + CRG_REGS_ADDR) ++#define CRG_PERCTL10_ADDR (0x28 + CRG_REGS_ADDR) ++#define CRG_PERCTL11_ADDR (0x2C + CRG_REGS_ADDR) ++#define CRG_PERCTL12_ADDR (0x30 + CRG_REGS_ADDR) ++#define CRG_PERCTL13_ADDR (0x34 + CRG_REGS_ADDR) ++#define CRG_PERCTL14_ADDR (0x38 + CRG_REGS_ADDR) ++#define CRG_PERCTL15_ADDR (0x3C + CRG_REGS_ADDR) ++#define CRG_PERCTL16_ADDR (0x40 + CRG_REGS_ADDR) ++#define CRG_PERCTL17_ADDR (0x44 + CRG_REGS_ADDR) ++#define CRG_PERCTL18_ADDR (0x48 + CRG_REGS_ADDR) ++#define CRG_PERCTL19_ADDR (0x4c + CRG_REGS_ADDR) ++#define CRG_PERCTL20_ADDR (0x50 + CRG_REGS_ADDR) ++#define CRG_PERCTL21_ADDR (0x54 + CRG_REGS_ADDR) ++#define CRG_PERCTL22_ADDR (0x58 + CRG_REGS_ADDR) ++#define CRG_PERCTL23_ADDR (0x5c + CRG_REGS_ADDR) ++#define CRG_PERCTL24_ADDR (0x60 + CRG_REGS_ADDR) ++#define CRG_PERCTL25_ADDR (0x64 + CRG_REGS_ADDR) ++#define CRG_PERCTL26_ADDR (0x68 + CRG_REGS_ADDR) ++#define CRG_PERCTL27_ADDR (0x6C + CRG_REGS_ADDR) ++#define CRG_PERCTL28_ADDR (0x70 + CRG_REGS_ADDR) ++#define CRG_PERCTL29_ADDR (0x74 + CRG_REGS_ADDR) ++#define CRG_PERCTL30_ADDR (0x78 + CRG_REGS_ADDR) ++#define CRG_PERCTL31_ADDR (0x7C + CRG_REGS_ADDR) ++#define CRG_PERCTL32_ADDR (0x80 + CRG_REGS_ADDR) ++#define CRG_PERCTL33_ADDR (0x84 + CRG_REGS_ADDR) ++#define CRG_PERCTL34_ADDR (0x88 + CRG_REGS_ADDR) ++#define CRG_PERCTL35_ADDR (0x8C + CRG_REGS_ADDR) ++#define CRG_PERCTL36_ADDR (0x90 + CRG_REGS_ADDR) ++#define CRG_PERCTL37_ADDR (0x94 + CRG_REGS_ADDR) ++#define CRG_PERCTL38_ADDR (0x98 + CRG_REGS_ADDR) ++#define CRG_PERCTL39_ADDR (0x9C + CRG_REGS_ADDR) ++#define CRG_PERCTL40_ADDR (0xa0 + CRG_REGS_ADDR) ++#define CRG_PERCTL41_ADDR (0xa4 + CRG_REGS_ADDR) ++#define CRG_PERCTL42_ADDR (0xa8 + CRG_REGS_ADDR) ++#define CRG_PERCTL43_ADDR (0xaC + CRG_REGS_ADDR) ++#define CRG_PERCTL44_ADDR (0xb0 + CRG_REGS_ADDR) ++#define CRG_PERCTL45_ADDR (0xb4 + CRG_REGS_ADDR) ++#define CRG_PERCTL46_ADDR (0xb8 + CRG_REGS_ADDR) ++#define CRG_PERCTL47_ADDR (0xbC + CRG_REGS_ADDR) ++#define CRG_PERCTL48_ADDR (0xc0 + CRG_REGS_ADDR) ++#define CRG_PERCTL49_ADDR (0xc4 + CRG_REGS_ADDR) ++#define CRG_PERCTL50_ADDR (0xc8 + CRG_REGS_ADDR) ++#define CRG_PERCTL51_ADDR (0xcC + CRG_REGS_ADDR) ++#define CRG_PERCTL52_ADDR (0xd0 + CRG_REGS_ADDR) ++#define CRG_PERCTL53_ADDR (0xd4 + CRG_REGS_ADDR) ++#define CRG_PERCTL54_ADDR (0xd8 + CRG_REGS_ADDR) ++#define CRG_PERCTL55_ADDR (0xdC + CRG_REGS_ADDR) ++#define CRG_PERCTL56_ADDR (0xe0 + CRG_REGS_ADDR) ++#define CRG_PERCTL57_ADDR (0xe4 + CRG_REGS_ADDR) ++#define CRG_PERCTL58_ADDR (0xe8 + CRG_REGS_ADDR) ++#define CRG_PERCTL59_ADDR (0xeC + CRG_REGS_ADDR) ++#define CRG_PERCTL60_ADDR (0xf0 + CRG_REGS_ADDR) ++#define CRG_PERCTL61_ADDR (0xf4 + CRG_REGS_ADDR) ++#define CRG_PERCTL62_ADDR (0xf8 + CRG_REGS_ADDR) ++#define CRG_PERCTL63_ADDR (0xfC + CRG_REGS_ADDR) ++#define CRG_PERCTL64_ADDR (0x100 + CRG_REGS_ADDR) ++#define CRG_PERCTL65_ADDR (0x104 + CRG_REGS_ADDR) ++#define CRG_PERCTL66_ADDR (0x108 + CRG_REGS_ADDR) ++#define CRG_PERCTL67_ADDR (0x10c + CRG_REGS_ADDR) ++#define CRG_PERCTL68_ADDR (0x110 + CRG_REGS_ADDR) ++#define CRG_PERCTL69_ADDR (0x114 + CRG_REGS_ADDR) ++#define CRG_PERCTL70_ADDR (0x118 + CRG_REGS_ADDR) ++#define CRG_PERCTL71_ADDR (0x11c + CRG_REGS_ADDR) ++#define CRG_PERCTL72_ADDR (0x120 + CRG_REGS_ADDR) ++#define CRG_PERCTL73_ADDR (0x124 + CRG_REGS_ADDR) ++#define CRG_PERCTL74_ADDR (0x128 + CRG_REGS_ADDR) ++#define CRG_PERCTL75_ADDR (0x12C + CRG_REGS_ADDR) ++#define CRG_PERCTL76_ADDR (0x130 + CRG_REGS_ADDR) ++#define CRG_PERCTL77_ADDR (0x134 + CRG_REGS_ADDR) ++#define CRG_PERCTL78_ADDR (0x138 + CRG_REGS_ADDR) ++#define CRG_PERCTL79_ADDR (0x13C + CRG_REGS_ADDR) ++#define CRG_PERCTL80_ADDR (0x140 + CRG_REGS_ADDR) ++#define CRG_PERCTL81_ADDR (0x144 + CRG_REGS_ADDR) ++#define CRG_PERCTL82_ADDR (0x148 + CRG_REGS_ADDR) ++#define CRG_PERCTL83_ADDR (0x14c + CRG_REGS_ADDR) ++#define CRG_PERCTL84_ADDR (0x150 + CRG_REGS_ADDR) ++#define CRG_PERCTL85_ADDR (0x154 + CRG_REGS_ADDR) ++#define CRG_PERCTL86_ADDR (0x158 + CRG_REGS_ADDR) ++#define CRG_PERCTL87_ADDR (0x15c + CRG_REGS_ADDR) ++#define CRG_PERCTL88_ADDR (0x160 + CRG_REGS_ADDR) ++#define CRG_PERCTL89_ADDR (0x164 + CRG_REGS_ADDR) ++#define CRG_PERCTL90_ADDR (0x168 + CRG_REGS_ADDR) ++#define CRG_PERCTL91_ADDR (0x16C + CRG_REGS_ADDR) ++#define CRG_PERCTL92_ADDR (0x170 + CRG_REGS_ADDR) ++#define CRG_PERCTL93_ADDR (0x174 + CRG_REGS_ADDR) ++#define CRG_PERCTL94_ADDR (0x178 + CRG_REGS_ADDR) ++#define CRG_PERCTL95_ADDR (0x17C + CRG_REGS_ADDR) ++#define CRG_PERCTL96_ADDR (0x180 + CRG_REGS_ADDR) ++#define CRG_PERCTL97_ADDR (0x184 + CRG_REGS_ADDR) ++#define CRG_PERCTL98_ADDR (0x188 + CRG_REGS_ADDR) ++#define CRG_PERCTL99_ADDR (0x18C + CRG_REGS_ADDR) ++#define CRG_PERCTL100_ADDR (0x190 + CRG_REGS_ADDR) ++#define CRG_PERCTL101_ADDR (0x194 + CRG_REGS_ADDR) ++#define CRG_PERCTL102_ADDR (0x198 + CRG_REGS_ADDR) ++#define CRG_PERCTL103_ADDR (0x19C + CRG_REGS_ADDR) ++#define CRG_PERCTL104_ADDR (0x1a0 + CRG_REGS_ADDR) ++#define CRG_PERCTL105_ADDR (0x1a4 + CRG_REGS_ADDR) ++#define CRG_PERCTL106_ADDR (0x1a8 + CRG_REGS_ADDR) ++#define CRG_PERCTL107_ADDR (0x1aC + CRG_REGS_ADDR) ++#define CRG_PERCTL108_ADDR (0x1b0 + CRG_REGS_ADDR) ++#define CRG_PERCTL109_ADDR (0x1b4 + CRG_REGS_ADDR) ++#define CRG_PERCTL110_ADDR (0x1b8 + CRG_REGS_ADDR) ++#define CRG_PERCTL111_ADDR (0x1bC + CRG_REGS_ADDR) ++#define CRG_PERCTL112_ADDR (0x1c0 + CRG_REGS_ADDR) ++#define CRG_PERCTL113_ADDR (0x1c4 + CRG_REGS_ADDR) ++#define CRG_PERCTL114_ADDR (0x1c8 + CRG_REGS_ADDR) ++#define CRG_PERCTL115_ADDR (0x1cC + CRG_REGS_ADDR) ++#define CRG_PERCTL116_ADDR (0x1d0 + CRG_REGS_ADDR) ++#define CRG_PERCTL117_ADDR (0x1d4 + CRG_REGS_ADDR) ++#define CRG_PERCTL118_ADDR (0x1d8 + CRG_REGS_ADDR) ++#define CRG_PERCTL119_ADDR (0x1dC + CRG_REGS_ADDR) ++#define CRG_PERCTL120_ADDR (0x1e0 + CRG_REGS_ADDR) ++#define CRG_PERCTL121_ADDR (0x1e4 + CRG_REGS_ADDR) ++#define CRG_PERCTL122_ADDR (0x1e8 + CRG_REGS_ADDR) ++#define CRG_PERCTL123_ADDR (0x1eC + CRG_REGS_ADDR) ++#define CRG_PERCTL124_ADDR (0x1f0 + CRG_REGS_ADDR) ++#define CRG_PERCTL125_ADDR (0x1f4 + CRG_REGS_ADDR) ++#define CRG_PERCTL126_ADDR (0x1f8 + CRG_REGS_ADDR) ++#define CRG_PERCTL127_ADDR (0x1fC + CRG_REGS_ADDR) ++/* ************** */ ++ ++#define MISC_CTL18_ADDR (0x18 + MISC_REGS_ADDR) ++#define MISC_CTL12C_ADDR (0x12C + MISC_REGS_ADDR) ++#define MISC_CTL98_ADDR (0x98 + MISC_REGS_ADDR) ++#define MISC_CTL9C_ADDR (0x9C + MISC_REGS_ADDR) ++ ++/* ************** */ ++ ++/* For the following function ++ * * typedef TD_S32 FN_SYS_ViDivSel(TD_S32 s32ViDev, TD_U32 u32DivSel); ++ * * typedef TD_S32 FN_SYS_ViClkSel(TD_S32 s32ViDev, TD_BOOL bSelf); ++ */ ++#define SYS_VI_DIV_SEL2 0x00 /* 2 division */ ++#define SYS_VI_DIV_SEL4 0x01 /* 4 division */ ++#define SYS_VI_DIV_SEL1 0x02 /* no division */ ++ ++/* For the following function ++ * * typedef TD_S32 FN_SYS_VoDivSel(TD_S32 s32ViDev, TD_U32 u32DivSel); ++ */ ++#define SYS_VO_DIV_SEL1 0x00 /* 1 division */ ++#define SYS_VO_DIV_SEL2 0x01 /* 2 division */ ++#define SYS_VO_DIV_SEL4 0x02 /* 4 division */ ++ ++/* For the following function ++ * * typedef TD_S32 FN_SYS_AioSampleClkDivSel(TD_U32 u32DivSel); ++ */ ++#define SYS_AIO_SAMPLE_CLK16 0x0 /* 16 division */ ++#define SYS_AIO_SAMPLE_CLK32 0x01 /* 32 division */ ++#define SYS_AIO_SAMPLE_CLK48 0x02 /* 48 division */ ++#define SYS_AIO_SAMPLE_CLK64 0x03 /* 64 division */ ++#define SYS_AIO_SAMPLE_CLK128 0x04 /* 128 division */ ++#define SYS_AIO_SAMPLE_CLK256 0x05 /* 256 division */ ++ ++/* For the following function ++ * * typedef TD_S32 FN_SYS_AioBitStreamClkDivSel(TD_U32 u32DivSel); ++ */ ++#define SYS_AIO_BS_CLK1 0x00 /* 1 division */ ++#define SYS_AIO_BS_CLK2 0x02 /* 2 division */ ++#define SYS_AIO_BS_CLK3 0x01 /* 3 division */ ++#define SYS_AIO_BS_CLK4 0x03 /* 4 division */ ++#define SYS_AIO_BS_CLK6 0x04 /* 6 division */ ++#define SYS_AIO_BS_CLK8 0x05 /* 8 division */ ++#define SYS_AIO_BS_CLK12 0x06 /* 12 division */ ++#define SYS_AIO_BS_CLK16 0x07 /* 16 division */ ++#define SYS_AIO_BS_CLK24 0x08 /* 24 division */ ++#define SYS_AIO_BS_CLK32 0x09 /* 32 division */ ++#define SYS_AIO_BS_CLK48 0x0a /* 48 division */ ++#define SYS_AIO_BS_CLK64 0x0b /* 64 division */ ++ ++#endif /* __BOARD_H__ */ +diff --git a/product/audio/ao/ss101v200/type.h b/product/audio/ao/ss101v200/type.h +new file mode 100644 +index 0000000..112a434 +--- /dev/null ++++ b/product/audio/ao/ss101v200/type.h +@@ -0,0 +1,93 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __TYPE_H__ ++#define __TYPE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#else ++ ++#include ++#endif ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C"{ ++#endif ++#endif /* __cplusplus */ ++ ++#ifndef NULL ++ #define NULL 0L ++#endif ++ ++#define TD_NULL 0L ++#define TD_SUCCESS 0 ++#define TD_FAILURE (-1) ++ ++typedef unsigned char td_uchar; ++typedef unsigned char td_u8; ++typedef unsigned short td_u16; ++typedef unsigned int td_u32; ++typedef unsigned long td_ulong; ++ ++typedef char td_char; ++typedef signed char td_s8; ++typedef short td_s16; ++typedef int td_s32; ++typedef long td_slong; ++ ++typedef float td_float; ++typedef double td_double; ++ ++typedef void td_void; ++ ++#ifndef _M_IX86 ++ typedef unsigned long long td_u64; ++ typedef long long td_s64; ++#else ++ typedef unsigned __int64 td_u64; ++ typedef __int64 td_s64; ++#endif ++typedef unsigned long int uintptr_t; ++ ++typedef unsigned long td_size_t; ++typedef unsigned long td_length_t; ++typedef unsigned long int td_phys_addr_t; ++typedef td_u32 td_handle; ++typedef uintptr_t td_uintptr_t; ++typedef unsigned int td_fr32; ++ ++typedef enum { ++ TD_FALSE = 0, ++ TD_TRUE = 1, ++} td_bool; ++ ++ ++/** @} */ /** MGF ---> xor ++ +----------+ | ++ | | ++ +--+ V | ++ |00| xor <----- MGF <-----| ++ +--+ | | ++ | | | ++ V V V ++ +--+----------+----------------------------+ ++EM = |00|maskedSeed| maskedDB | ++ +--+----------+----------------------------+ ++ 1 hlen k - hlen- 1 ++ ++so: PS_LEN = k - hlen - 1 - (hlen + mlen + 1) = k - 2hlen - mlen - 2 > 0 ++so: mlen < k - 2hlen - 2 ++************************************************************ */ ++static td_s32 rsa_padding_add_pkcs1_oaep(rsa_padding_s *pad) ++{ ++ td_s32 ret; ++ td_u32 db_len; ++ td_u8 *ptr_db = TD_NULL; ++ td_u8 *ptr_seed = TD_NULL; ++ const td_s8 *l_hash = g_empty_l_sha1; ++ ++ /* In the v2.1 of PKCS #1, L is the empty string; */ ++ /* other uses outside the scope of rsa specifications */ ++ if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA224) { ++ l_hash = g_empty_l_sha224; ++ } else if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA256) { ++ l_hash = g_empty_l_sha256; ++ } else if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA384) { ++ l_hash = g_empty_l_sha384; ++ } else if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA512) { ++ l_hash = g_empty_l_sha512; ++ } ++ ++ chk_formula_fail_return(pad->in_len > pad->klen - 2 * pad->hlen - 2); /* 2 */ ++ ++ pad->out_data[0] = 0; ++ ptr_seed = pad->out_data + 1; ++ ptr_db = pad->out_data + pad->hlen + 1; ++ db_len = pad->klen - pad->hlen - 1; ++ ++ /* set lHash */ ++ ret = memcpy_s(ptr_db, db_len, l_hash, pad->hlen); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ /* set PS with 0x00 */ ++ ret = memset_s(&ptr_db[pad->hlen], db_len - pad->hlen, 0, db_len - pad->in_len - pad->hlen - 1); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ /* set 0x01 after PS */ ++ ptr_db[db_len - pad->in_len - 1] = 0x01; ++ ++ /* set M */ ++ ret = memcpy_s(&ptr_db[db_len - pad->in_len], pad->in_len, pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ /* set seed */ ++ ret = mbedtls_get_random_number(TD_NULL, ptr_seed, pad->hlen); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, mbedtls_get_random_number); ++ ++ ret = rsa_pkcs1_mgf1(pad->hash_type, ptr_seed, pad->hlen, ptr_db, pad->klen - pad->hlen - 1); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pkcs1_mgf1); ++ ++ /* compute maskedSeed */ ++ ret = rsa_pkcs1_mgf1(pad->hash_type, ptr_db, pad->klen - pad->hlen - 1, ptr_seed, pad->hlen); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pkcs1_mgf1); ++ ++ pad->out_len = pad->klen; ++ ++ return ret; ++} ++ ++/* PKCS #1: RSAES-PKCS1-V1_5-ENCRYPT */ ++/************************************************* ++formula: EM = 0x00 || 0x02 || PS || 0x00 || M ++ ++formula: PS_LEN > 8, mlen < klen - 11 ++*************************************************/ ++static td_s32 rsa_padding_add_pkcs1_v15(rsa_padding_s *pad) ++{ ++ td_u32 index = 0; ++ td_s32 ret; ++ ++ if (pad->in_len > pad->klen - 11) { /* 11 */ ++ ot_err_cipher("input_len is invalid.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ pad->out_data[index++] = 0x00; ++ pad->out_data[index++] = 0x02; ++ ret = mbedtls_get_random_number(TD_NULL, &pad->out_data[index], pad->klen - pad->in_len - 3); /* 3 */ ++ chk_func_fail_return(ret != TD_SUCCESS, ret, mbedtls_get_random_number); ++ ++ index += pad->klen - pad->in_len - 3; /* 3 */ ++ pad->out_data[index++] = 0x00; ++ ret = memcpy_s(&pad->out_data[index], pad->klen - index, pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ pad->out_len = pad->klen; ++ ++ return TD_SUCCESS; ++} ++ ++/* PKCS #1: block type 0,1,2 message padding */ ++/* ************************************************ ++formula: EB = 00 || BT || PS || 00 || D ++ ++formula: PS_LEN >= 8, mlen < klen - 11 ++************************************************ */ ++static td_s32 rsa_padding_add_pkcs1_type(rsa_padding_s *pad) ++{ ++ td_u32 pad_len; ++ td_u8 *key_eb = TD_NULL; ++ td_s32 ret; ++ ++ if (pad->in_len > pad->klen - 11) { /* 11 */ ++ ot_err_cipher("input_len is invalid.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ key_eb = pad->out_data; ++ ++ *(key_eb++) = 0; ++ *(key_eb++) = pad->key_bt; /* Private Key BT (Block Type) */ ++ ++ /* pad out with 0xff data */ ++ pad_len = pad->klen - 3 - pad->in_len; /* 3 */ ++ if (pad->key_bt == 0x00) { ++ ret = memset_s(key_eb, pad->klen - (td_u32)(key_eb - pad->out_data), 0x00, pad_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ } else if (pad->key_bt == 0x01) { ++ ret = memset_s(key_eb, pad->klen - (td_u32)(key_eb - pad->out_data), 0xFF, pad_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ } else if (pad->key_bt == 0x02) { ++ ret = mbedtls_get_random_number(TD_NULL, key_eb, pad_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, mbedtls_get_random_number); ++ } else { ++ ot_err_cipher("BT(0x%x) is invalid.\n", pad->key_bt); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ key_eb += pad_len; ++ *(key_eb++) = 0x00; ++ ret = memcpy_s(key_eb, pad->klen - (td_u32)(key_eb - pad->out_data), pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ pad->out_len = pad->klen; ++ ++ return TD_SUCCESS; ++} ++ ++/* PKCS #1: RSAES-PKCS1-V1_5-Signature */ ++/* ******************************************************** ++formula: EM = 0x00 || 0x01 || PS || 0x00 || T ++ ++T ::= SEQUENCE { ++ digestAlgorithm AlgorithmIdentifier, ++ digest OCTET STRING ++ } ++The first field identifies the hash function and the second ++contains the hash value ++********************************************************* */ ++static td_s32 rsa_padding_add_emsa_pkcs1_v15_sha1(const rsa_padding_s *pad, td_u8 *p) ++{ ++ td_s32 ret; ++ td_u32 pad_len, buf_len; ++ ++ pad_len = pad->klen - 3 - 35; /* 3, 35 */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memset_s(p, buf_len, 0xFF, pad_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ p += pad_len; ++ *p++ = 0; ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, ASN1_HASH_SHA1, 15); /* 15 copy size */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ p += 15; /* 15 p shift */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_add_emsa_pkcs1_v15_sha224(const rsa_padding_s *pad, td_u8 *p) ++{ ++ td_s32 ret; ++ td_u32 pad_len, buf_len; ++ ++ pad_len = pad->klen - 3 - 19 - pad->in_len; /* 3, 19 */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memset_s(p, buf_len, 0xFF, pad_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ p += pad_len; ++ *p++ = 0; ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, ASN1_HASH_SHA224, 19); /* 19 copy size */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ p += 19; /* 19 p shift */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_add_emsa_pkcs1_v15_sha256(const rsa_padding_s *pad, td_u8 *p) ++{ ++ td_s32 ret; ++ td_u32 pad_len, buf_len; ++ ++ pad_len = pad->klen - 3 - 19 - pad->in_len; /* 3, 19 */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memset_s(p, buf_len, 0xFF, pad_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ p += pad_len; ++ *p++ = 0; ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, ASN1_HASH_SHA256, 19); /* 19 copy size */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ p += 19; /* 19 p shift */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_add_emsa_pkcs1_v15_sha384(const rsa_padding_s *pad, td_u8 *p) ++{ ++ td_s32 ret; ++ td_u32 pad_len, buf_len; ++ ++ pad_len = pad->klen - 3 - 19 - pad->in_len; /* 3, 19 */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memset_s(p, buf_len, 0xFF, pad_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ p += pad_len; ++ *p++ = 0; ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, ASN1_HASH_SHA384, 19); /* 19 copy size */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ p += 19; /* 19 p shift */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_add_emsa_pkcs1_v15_sha512(const rsa_padding_s *pad, td_u8 *p) ++{ ++ td_s32 ret; ++ td_u32 pad_len, buf_len; ++ ++ pad_len = pad->klen - 3 - 19 - pad->in_len; /* 3, 19 */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memset_s(p, buf_len, 0xFF, pad_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ p += pad_len; ++ *p++ = 0; ++ ret = memcpy_s(p, pad->klen, ASN1_HASH_SHA512, 19); /* 19 copy size */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ p += 19; /* 19 p shift */ ++ buf_len = pad->klen - (td_u32)(p - pad->out_data); ++ ret = memcpy_s(p, buf_len, pad->in_data, pad->in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_add_emsa_pkcs1_v15(rsa_padding_s *pad) ++{ ++ td_u8 *p = pad->out_data; ++ ++ pad->out_len = pad->klen; ++ *p++ = 0; ++ *p++ = RSA_SIGN; ++ ++ switch (pad->hash_type) { ++ case OT_CIPHER_HASH_TYPE_SHA1: ++ return rsa_padding_add_emsa_pkcs1_v15_sha1(pad, p); ++ case OT_CIPHER_HASH_TYPE_SHA224: ++ return rsa_padding_add_emsa_pkcs1_v15_sha224(pad, p); ++ case OT_CIPHER_HASH_TYPE_SHA256: ++ return rsa_padding_add_emsa_pkcs1_v15_sha256(pad, p); ++ case OT_CIPHER_HASH_TYPE_SHA384: ++ return rsa_padding_add_emsa_pkcs1_v15_sha384(pad, p); ++ case OT_CIPHER_HASH_TYPE_SHA512: ++ return rsa_padding_add_emsa_pkcs1_v15_sha512(pad, p); ++ default: ++ ot_err_cipher("RSA unsuporrt hash type: 0x%x.\n", pad->hash_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++/* ***************************************************************** ++ +-----------+ ++ | M | ++ +-----------+ ++ | ++ V ++ Hash ++ | ++ V ++ +--------+----------+----------+ ++ M' = |Padding1| mHash | salt | ++ +--------+----------+----------+ ++ | ++ +--------+----------+ V ++ DB = |Padding2|maskedseed| Hash ++ +--------+----------+ | ++ | | ++ V | +--+ ++ xor <----- MGF <----| |bc| ++ | | +--+ ++ | | | ++ V V V ++ +-------------------+----- -------+--+ ++ EM = | maskedDB | maskedseed |bc| ++ +-------------------+-------------+--+ ++ ***************************************************************** */ ++static td_s32 rsa_padding_add_pkcs1_pss_hash(rsa_padding_s *pad, rsa_pkcs1_pss_s *pss) ++{ ++ td_s32 ret; ++ td_u32 mlen, index; ++ td_u8 *ptr_m = TD_NULL; ++ td_handle hash_handle = 0; ++ ot_cipher_hash_attr hash_attr; ++ ++ mlen = pss->slen + pad->hlen + 8; /* 8 */ ++ ptr_m = (td_u8 *)memalign(ARCH_DMA_MINALIGN, mlen); ++ chk_func_fail_return(ptr_m == TD_NULL, OT_ERR_CIPHER_INVALID_POINT, memalign); ++ ++ /* M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */ ++ if ((memset_s(ptr_m, mlen, 0x00, 8) != EOK) || /* 8 pad size */ ++ (memcpy_s(&ptr_m[8], mlen - 8, pad->in_data, pad->in_len) != EOK) || /* 8 pad size */ ++ (memcpy_s(&ptr_m[8 + pad->in_len], mlen - 8 - pad->in_len, pss->salt, pss->slen) != EOK)) { /* 8 pad size */ ++ ot_err_cipher("call sec func failed\n"); ++ cipher_free(ptr_m); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ ++ (td_void)memset_s(&hash_attr, sizeof(ot_cipher_hash_attr), 0, sizeof(ot_cipher_hash_attr)); ++ hash_attr.sha_type = pad->hash_type; ++ if ((ot_mpi_cipher_hash_init(&hash_attr, &hash_handle) != TD_SUCCESS) || ++ (ot_mpi_cipher_hash_update(hash_handle, ptr_m, mlen) != TD_SUCCESS) || ++ (ot_mpi_cipher_hash_final(hash_handle, pss->masked_seed) != TD_SUCCESS)) { ++ ot_err_cipher("call hash func failed\n"); ++ cipher_free(ptr_m); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ cipher_free(ptr_m); /* Must free ptr_m befort return */ ++ ++ /* formula: maskedDB = DB xor dbMask, DB = PS || 0x01 || salt */ ++ index = 0; ++ ret = memset_s(&pss->masked_db[index], pss->key_len - index, ++ 0x00, pss->key_len - pss->slen - pad->hlen - 2); /* 2 */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ index += pss->key_len - pss->slen - pad->hlen - 2; /* 2 */ ++ pss->masked_db[index++] = 0x01; ++ ret = memcpy_s(&pss->masked_db[index], pss->key_len - index, pss->salt, pss->slen); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ ret = rsa_pkcs1_mgf1(pad->hash_type, ++ pss->masked_seed, pad->hlen, pss->masked_db, pss->key_len - pad->hlen - 1); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pkcs1_mgf1); ++ ++ pad->out_data[pss->key_len - 1] = 0xBC; ++ ++ if (pss->msb_bits) { ++ pad->out_data[0] &= 0xFF >> (8 - pss->msb_bits); /* 8 */ ++ } ++ ++ return ret; ++} ++ ++static td_s32 rsa_padding_add_pkcs1_pss(rsa_padding_s *pad) ++{ ++ rsa_pkcs1_pss_s pss; ++ td_s32 ret; ++ ++ (td_void)memset_s(&pss, sizeof(rsa_pkcs1_pss_s), 0, sizeof(rsa_pkcs1_pss_s)); ++ pss.slen = pad->hlen; ++ pss.key_len = (pad->em_bit + 7) / 8; /* 7, 8 */ ++ pss.msb_bits = (pad->em_bit - 1) & 0x07; ++ ++ pad->out_len = pss.key_len; ++ ++ if (pss.key_len < (pad->hlen + pss.slen + 2)) { /* 2 */ ++ ot_err_cipher("message too long\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (pss.msb_bits == 0) { ++ *pad->out_data++ = 0; ++ pss.key_len--; ++ } ++ ++ pss.masked_db = pad->out_data; ++ pss.masked_seed = pad->out_data + pss.key_len - pad->hlen - 1; ++ ++ /* Generate a random octet string salt of length sLen */ ++ ret = mbedtls_get_random_number(TD_NULL, pss.salt, pss.slen); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, mbedtls_get_random_number); ++ ++ return rsa_padding_add_pkcs1_pss_hash(pad, &pss); ++} ++ ++static td_s32 rsa_padding_check_pkcs1_oaep(rsa_padding_s *pad) ++{ ++ td_s32 ret; ++ td_u32 i; ++ const td_s8 *l_hash = g_empty_l_sha1; ++ td_u8 *ptr_seed = TD_NULL; ++ td_u8 *ptr_db = TD_NULL; ++ td_u8 *masked_db = TD_NULL; ++ ++ if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA224) { ++ l_hash = g_empty_l_sha224; ++ } else if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA256) { ++ l_hash = g_empty_l_sha256; ++ } else if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA384) { ++ l_hash = g_empty_l_sha384; ++ } else if (pad->hash_type == OT_CIPHER_HASH_TYPE_SHA512) { ++ l_hash = g_empty_l_sha512; ++ } ++ ++ if (pad->klen < 2 * pad->hlen + 2) { /* 2 */ ++ ot_err_cipher("input_len is invalid.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (pad->in_data[0] != 0x00) { ++ ot_err_cipher("EM[0] != 0.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ pad->out_len = 0; ++ masked_db = pad->in_data + pad->hlen + 1; ++ ptr_seed = pad->in_data + 1; ++ ptr_db = pad->in_data + pad->hlen + 1; ++ ret = rsa_pkcs1_mgf1(pad->hash_type, masked_db, pad->klen - pad->hlen - 1, ptr_seed, pad->hlen); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pkcs1_mgf1); ++ ++ ret = rsa_pkcs1_mgf1(pad->hash_type, ptr_seed, pad->hlen, ptr_db, pad->klen - pad->hlen - 1); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pkcs1_mgf1); ++ ++ ret = memcmp(ptr_db, l_hash, pad->hlen); ++ chk_func_fail_return(ret != 0, OT_ERR_CIPHER_FAILED_DECRYPT, memcmp); ++ ++ for (i = pad->hlen; i < pad->klen - pad->hlen - 1; i++) { ++ if ((ptr_db[i] == 0x01)) { ++ ret = memcpy_s(pad->out_data, pad->klen, ptr_db + i + 1, pad->klen - pad->hlen - i - 2); /* 2 */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ pad->out_len = pad->klen - pad->hlen - i - 2; /* 2 */ ++ break; ++ } ++ } ++ if (i >= pad->klen - pad->hlen - 1) { ++ ot_err_cipher("PS error.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_check_pkcs1_v15(rsa_padding_s *pad) ++{ ++ td_u32 index = 0; ++ ++ if (pad->klen < 11) { /* 11 pad->klen max size */ ++ ot_err_cipher("input_len is invalid.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (pad->in_data[index] != 0x00) { ++ ot_err_cipher("EM[0] != 0x00.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ index++; ++ if (pad->in_data[index] != 0x02) { ++ ot_err_cipher("EM[1] != 0x02.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ index++; ++ for (; index < pad->klen; index++) { ++ /* The length of PS is large than 8 octets */ ++ if ((index >= 10) && (pad->in_data[index] == 0x00)) { /* 10 */ ++ if (memcpy_s(pad->out_data, pad->klen, &pad->in_data[index + 1], pad->klen - 1 - index) != EOK) { ++ ot_err_cipher("call failed memcpy_s\n"); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ pad->out_len = pad->klen - 1 - index; ++ break; ++ } ++ } ++ ++ if (index >= pad->klen) { ++ ot_err_cipher("PS error.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_check_pkcs1_type(rsa_padding_s *pad) ++{ ++ td_u8 *key_eb = pad->in_data; ++ ++ if (*key_eb != 0x00) { ++ ot_err_cipher("EB[0] != 0x00.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ key_eb++; ++ if (*key_eb != pad->key_bt) { ++ ot_err_cipher("EB[1] != BT(0x%x).\n", pad->key_bt); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ key_eb++; ++ if (pad->key_bt == 0x00) { ++ for (; key_eb < pad->in_data + pad->in_len - 1; key_eb++) { ++ if ((*key_eb == 0x00) && (*(key_eb + 1) != 0)) ++ break; ++ } ++ } else if (pad->key_bt == 0x01) { ++ for (; key_eb < pad->in_data + pad->in_len - 1; key_eb++) { ++ if (*key_eb == 0xFF) { ++ continue; ++ } else if (*key_eb == 0x00) { ++ break; ++ } else { ++ key_eb = pad->in_data + pad->in_len - 1; ++ break; ++ } ++ } ++ } else if (pad->key_bt == 0x02) { ++ for (; key_eb < pad->in_data + pad->in_len - 1; key_eb++) { ++ if (*key_eb == 0x00) ++ break; ++ } ++ } else { ++ ot_err_cipher("BT(0x%x) is invalid.\n", pad->key_bt); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (key_eb >= (pad->in_data + pad->in_len - 1)) { ++ ot_err_cipher("PS Error.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ key_eb++; ++ pad->out_len = pad->in_data + pad->klen - key_eb; ++ if (memcpy_s(pad->out_data, pad->klen, key_eb, pad->out_len) != EOK) { ++ ot_err_cipher("call failed memcpy_s\n"); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_check_emsa_pkcs1_v15_type(rsa_padding_s *pad, const td_u8 *p) ++{ ++ td_s32 ret; ++ td_u32 len; ++ ++ len = pad->klen - (td_u32)(p - pad->in_data); ++ ++ switch (pad->hash_type) { ++ case OT_CIPHER_HASH_TYPE_SHA1: ++ chk_u32_data_fail_return(len, 35); /* 35 len size */ ++ ret = memcmp(p, ASN1_HASH_SHA1, 15); /* 15 compare size */ ++ chk_func_fail_return(ret != 0, TD_FAILURE, memcmp); ++ ++ ret = memcpy_s(pad->out_data, pad->klen, p + 15, pad->hlen); /* 15 copy shift */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA224: ++ chk_u32_data_fail_return(len, (19 + pad->hlen)); /* len size: 19 + pad->hlen */ ++ ret = memcmp(p, ASN1_HASH_SHA224, 19); /* 19 compare size */ ++ chk_func_fail_return(ret != 0, TD_FAILURE, memcmp); ++ ++ ret = memcpy_s(pad->out_data, pad->klen, p + 19, pad->hlen); /* 19 copy shift */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA256: ++ chk_u32_data_fail_return(len, (19 + pad->hlen)); /* len size: 19 + pad->hlen */ ++ ret = memcmp(p, ASN1_HASH_SHA256, 19); /* 19 compare size */ ++ chk_func_fail_return(ret != 0, TD_FAILURE, memcmp); ++ ++ ret = memcpy_s(pad->out_data, pad->klen, p + 19, pad->hlen); /* 19 copy shift */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA384: ++ chk_u32_data_fail_return(len, (19 + pad->hlen)); /* len size: 19 + pad->hlen */ ++ ret = memcmp(p, ASN1_HASH_SHA384, 19); /* 19 compare size */ ++ chk_func_fail_return(ret != 0, TD_FAILURE, memcmp); ++ ++ ret = memcpy_s(pad->out_data, pad->klen, p + 19, pad->hlen); /* 19 copy shift */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA512: ++ chk_u32_data_fail_return(len, (19 + pad->hlen)); /* len size: 19 + pad->hlen */ ++ ret = memcmp(p, ASN1_HASH_SHA512, 19); /* 19 compare size */ ++ chk_func_fail_return(ret != 0, TD_FAILURE, memcmp); ++ ++ ret = memcpy_s(pad->out_data, pad->klen, p + 19, pad->hlen); /* 19 copy shift */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ default: ++ ot_err_cipher("RSA unsuporrt hash type: 0x%x.\n", pad->hash_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ return ret; ++} ++ ++static td_s32 rsa_padding_check_emsa_pkcs1_v15(rsa_padding_s *pad) ++{ ++ td_u8 *p = pad->in_data; ++ ++ pad->out_len = pad->hlen; ++ ++ /* formula: EM = 01 || PS || 00 || T */ ++ if (*p++ != 0) { ++ ot_err_cipher("RSA EM[0] must be 0\n"); ++ return TD_FAILURE; ++ } ++ ++ if (*p++ != RSA_SIGN) { ++ ot_err_cipher("RSA EM PS error\n"); ++ return TD_FAILURE; ++ } ++ ++ while (*p != 0) { ++ if (p >= pad->in_data + pad->klen - 1 || *p != 0xFF) { ++ ot_err_cipher("RSA PS error\n"); ++ return TD_FAILURE; ++ } ++ p++; ++ } ++ p++; // skip 0x00 ++ ++ return rsa_padding_check_emsa_pkcs1_v15_type(pad, p); ++} ++ ++static td_s32 rsa_padding_check_pkcs1_pss_hash(const rsa_padding_s *pad, ++ const td_u8 *mhash, const rsa_pkcs1_pss_s *pss) ++{ ++ td_u32 mlen; ++ td_u8 *ptr_m = TD_NULL; ++ td_handle hash_handle = 0; ++ ot_cipher_hash_attr hash_attr; ++ td_u8 arr_h[HASH_RESULT_MAX_LEN] = {0}; ++ ++ mlen = pss->slen + pad->hlen + 8; /* 8 */ ++ ptr_m = (td_u8 *)cipher_malloc(mlen); ++ chk_func_fail_return(ptr_m == TD_NULL, OT_ERR_CIPHER_INVALID_POINT, cipher_malloc); ++ (td_void)memset_s(ptr_m, mlen, 0, mlen); ++ ++ /* M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */ ++ if ((memset_s(ptr_m, mlen, 0x00, 8) != EOK) || /* 8 pad size */ ++ (memcpy_s(&ptr_m[8], mlen - 8, mhash, pad->hlen) != EOK) || /* 8 pad size */ ++ (memcpy_s(&ptr_m[8 + pad->hlen], mlen - 8 - pad->hlen, pss->salt, pss->slen) != EOK)) { /* 8 pad size */ ++ ot_err_cipher("call sec func failed\n"); ++ cipher_free(ptr_m); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ ++ (td_void)memset_s(&hash_attr, sizeof(ot_cipher_hash_attr), 0, sizeof(ot_cipher_hash_attr)); ++ hash_attr.sha_type = pad->hash_type; ++ if ((ot_mpi_cipher_hash_init(&hash_attr, &hash_handle) != TD_SUCCESS) || ++ (ot_mpi_cipher_hash_update(hash_handle, ptr_m, mlen) != TD_SUCCESS) || ++ (ot_mpi_cipher_hash_final(hash_handle, arr_h) != TD_SUCCESS)) { ++ ot_err_cipher("call hash func failed\n"); ++ cipher_free(ptr_m); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ cipher_free(ptr_m); /* Must free ptr_m befort return */ ++ ++ if (memcmp(arr_h, pss->masked_seed, pad->hlen) != 0) { ++ ot_err_cipher("compare failed\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 rsa_padding_check_pkcs1_pss(rsa_padding_s *pad, const td_u8 *mhash, td_u32 hash_len) ++{ ++ td_s32 ret; ++ td_u32 index, tmp_len; ++ rsa_pkcs1_pss_s pss; ++ ++ (td_void)memset_s(&pss, sizeof(rsa_pkcs1_pss_s), 0, sizeof(rsa_pkcs1_pss_s)); ++ pss.slen = pad->hlen; ++ pss.key_len = (pad->em_bit + 7) / 8; /* 7, 8 */ ++ pss.msb_bits = (pad->em_bit - 1) & 0x07; ++ ++ if (pss.key_len < (pad->hlen + pss.slen + 2)) { /* 2 */ ++ ot_err_cipher("message too long\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (pad->in_data[0] & (0xFF << pss.msb_bits)) { ++ ot_err_cipher("inconsistent, EM[0] invalid\n"); ++ return TD_FAILURE; ++ } ++ ++ if (pss.msb_bits == 0) { ++ pad->in_data++; ++ pss.key_len--; ++ } ++ ++ pss.masked_db = pad->in_data; ++ pss.masked_seed = pad->in_data + pss.key_len - pad->hlen - 1; ++ ++ if (pad->in_data[pss.key_len - 1] != 0xBC) { ++ ot_err_cipher("inconsistent, EM[key_len - 1] != 0xBC\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ /* formula: maskedDB = DB xor dbMask, DB = PS || 0x01 || salt */ ++ ret = rsa_pkcs1_mgf1(pad->hash_type, pss.masked_seed, ++ pad->hlen, pss.masked_db, pss.key_len - pad->hlen - 1); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pkcs1_mgf1); ++ ++ if (pss.msb_bits) ++ pss.masked_db[0] &= 0xFF >> (8 - pss.msb_bits); /* 8 */ ++ ++ tmp_len = pss.key_len - pss.slen - pad->hlen - 2; /* 2 */ ++ if (tmp_len >= CIPHER_MAX_RSA_KEY_LEN - 1) { /* -1 is for index++, avoid masked_db overflow */ ++ ot_err_cipher("operate masked_db maybe overflow %u\n", tmp_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ for (index = 0; index < tmp_len; index++) { ++ if (pss.masked_db[index] != 0x00) { ++ break; ++ } ++ } ++ pss.slen = pss.key_len - pad->hlen - index - 2; /* 2 */ ++ ++ chk_u32_data_fail_return(pss.masked_db[index], 0x01); ++ index++; ++ ret = memcpy_s(pss.salt, sizeof(pss.salt), &pss.masked_db[index], pss.slen); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return rsa_padding_check_pkcs1_pss_hash(pad, mhash, &pss); ++} ++ ++static td_s32 rsa_pub_enc_pad_init(rsa_padding_s *pad, const ot_cipher_rsa_pub_encrypt *rsa_enc, ++ const ot_cipher_rsa_crypt *rsa_crypt, td_u8 *arr_em, td_u32 em_len) ++{ ++ td_s32 ret; ++ ++ if (em_len < rsa_crypt->in_len) { ++ ot_err_cipher("buf len %u < in len %u\n", em_len, rsa_crypt->in_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ (td_void)memset_s(pad, sizeof(rsa_padding_s), 0, sizeof(rsa_padding_s)); ++ pad->in_data = (td_u8 *)rsa_crypt->in; ++ pad->in_len = rsa_crypt->in_len; ++ pad->out_data = arr_em; ++ ret = rsa_get_attr(rsa_enc->scheme, rsa_enc->pub_key.n_len, pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_get_attr); ++ chk_formula_fail_return(pad->in_len > pad->klen); ++ ++ return ret; ++} ++ ++static td_s32 rsa_pri_dec_pad_init(rsa_padding_s *pad, const ot_cipher_rsa_private_encrypt *rsa_decrypt, ++ ot_cipher_rsa_crypt *rsa_crypt, td_u8 *arr_em, td_u32 em_len) ++{ ++ td_s32 ret; ++ ++ if (em_len < rsa_crypt->in_len) { ++ ot_err_cipher("buf len %u < in len %u\n", em_len, rsa_crypt->in_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ (td_void)memset_s(pad, sizeof(rsa_padding_s), 0, sizeof(rsa_padding_s)); ++ pad->in_data = arr_em; ++ pad->in_len = rsa_crypt->in_len; ++ pad->out_data = rsa_crypt->out; ++ ret = rsa_get_attr(rsa_decrypt->scheme, rsa_decrypt->private_key.n_len, pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_get_attr); ++ chk_u32_data_fail_return(pad->in_len, pad->klen); ++ ++ return ret; ++} ++ ++static td_s32 rsa_pri_enc_pad_init(rsa_padding_s *pad, const ot_cipher_rsa_private_encrypt *rsa_encrypt, ++ const ot_cipher_rsa_crypt *rsa_crypt, td_u8 *arr_em, td_u32 em_len) ++{ ++ td_s32 ret; ++ ++ if (em_len < rsa_crypt->in_len) { ++ ot_err_cipher("buf len %u < in len %u\n", em_len, rsa_crypt->in_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ (td_void)memset_s(pad, sizeof(rsa_padding_s), 0, sizeof(rsa_padding_s)); ++ pad->in_data = (td_u8 *)rsa_crypt->in; ++ pad->in_len = rsa_crypt->in_len; ++ pad->out_data = arr_em; ++ ret = rsa_get_attr(rsa_encrypt->scheme, rsa_encrypt->private_key.n_len, pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_get_attr); ++ chk_formula_fail_return(pad->in_len > pad->klen); ++ ++ return ret; ++} ++ ++static td_s32 rsa_pub_dec_pad_init(rsa_padding_s *pad, const ot_cipher_rsa_pub_encrypt *rsa_decrypt, ++ ot_cipher_rsa_crypt *rsa_crypt, td_u8 *arr_em, td_u32 em_len) ++{ ++ td_s32 ret; ++ ++ if (em_len < rsa_crypt->in_len) { ++ ot_err_cipher("buf len %u < in len %u\n", em_len, rsa_crypt->in_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ (td_void)memset_s(pad, sizeof(rsa_padding_s), 0, sizeof(rsa_padding_s)); ++ pad->in_data = arr_em; ++ pad->in_len = rsa_crypt->in_len; ++ pad->out_data = rsa_crypt->out; ++ ret = rsa_get_attr(rsa_decrypt->scheme, rsa_decrypt->pub_key.n_len, pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_get_attr); ++ chk_u32_data_fail_return(pad->in_len, pad->klen); ++ ++ return ret; ++} ++ ++static td_s32 rsa_sign_pad_init(rsa_padding_s *pad, ++ const ot_cipher_rsa_sign *rsa_sign, const ot_cipher_sign_data *sign_data, rsa_sign_buf *sign_buf) ++{ ++ td_s32 ret; ++ td_handle hash_handle; ++ ++ (td_void)memset_s(pad, sizeof(rsa_padding_s), 0, sizeof(rsa_padding_s)); ++ ret = rsa_get_attr(rsa_sign->scheme, rsa_sign->private_key.n_len, pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_get_attr); ++ pad->out_data = sign_buf->arr_em; ++ pad->in_len = pad->hlen; ++ ++ /* hash is NULl, need to calc by self */ ++ if (sign_data->hash_data != TD_NULL) { ++ pad->in_data = (td_u8 *)sign_data->hash_data; ++ } else { ++ ot_cipher_hash_attr hash_attr = {0}; ++ hash_attr.sha_type = pad->hash_type; ++ ret = ot_mpi_cipher_hash_init(&hash_attr, &hash_handle); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, ot_mpi_cipher_hash_init); ++ ++ ret = ot_mpi_cipher_hash_update(hash_handle, (td_u8 *)sign_data->in, sign_data->in_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, ot_mpi_cipher_hash_update); ++ ++ ret = ot_mpi_cipher_hash_final(hash_handle, sign_buf->sign_hash); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, ot_mpi_cipher_hash_final); ++ ++ pad->in_data = sign_buf->sign_hash; ++ } ++ ++ return ret; ++} ++ ++static td_s32 rsa_verify_pad_init(rsa_padding_s *pad, const ot_cipher_rsa_verify *rsa_verify, ++ const ot_cipher_verify_data *verify_data, rsa_sign_buf *sign_buf) ++{ ++ td_s32 ret; ++ ++ (td_void)memset_s(pad, sizeof(rsa_padding_s), 0, sizeof(rsa_padding_s)); ++ ret = rsa_get_attr(rsa_verify->scheme, rsa_verify->pub_key.n_len, pad); ++ pad->in_data = sign_buf->arr_em; ++ pad->in_len = verify_data->sign_len; ++ pad->out_data = sign_buf->sign_hash; ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_get_attr); ++ chk_u32_data_fail_return(verify_data->sign_len, pad->klen); ++ ++ ret = rsa_public(&rsa_verify->pub_key, verify_data->sign, pad->in_data); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_public); ++ ++ return ret; ++} ++ ++static td_s32 rsa_verify_get_hash(const ot_cipher_verify_data *verify_data, ++ td_u8 *arr_hash, td_u32 hash_len, ot_cipher_hash_type hash_type) ++{ ++ td_s32 ret; ++ ot_cipher_hash_attr hash_attr; ++ td_handle hash_handle; ++ ++ if (verify_data->hash_data != TD_NULL) { ++ ret = memcpy_s(arr_hash, hash_len, verify_data->hash_data, verify_data->hash_data_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ } else { ++ (td_void)memset_s(&hash_attr, sizeof(ot_cipher_hash_attr), 0, sizeof(ot_cipher_hash_attr)); ++ hash_attr.sha_type = hash_type; ++ ret = ot_mpi_cipher_hash_init(&hash_attr, &hash_handle); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, ot_mpi_cipher_hash_init); ++ ++ ret = ot_mpi_cipher_hash_update(hash_handle, (td_u8 *)verify_data->in, verify_data->in_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, ot_mpi_cipher_hash_init); ++ ++ ret = ot_mpi_cipher_hash_final(hash_handle, arr_hash); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, ot_mpi_cipher_hash_init); ++ } ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_rsa_pub_encrypt( ++ const ot_cipher_rsa_pub_encrypt *rsa_enc, ot_cipher_rsa_crypt *rsa_crypt) ++{ ++ td_s32 ret; ++ rsa_padding_s pad; ++ td_u8 arr_em[CIPHER_MAX_RSA_KEY_LEN] = {0}; ++ ++ chk_dev_open_fail_return(); ++ chk_ptr_null_return(rsa_enc); ++ chk_ptr_null_return(rsa_crypt); ++ chk_ptr_null_return(rsa_crypt->in); ++ chk_ptr_null_return(rsa_crypt->out); ++ chk_ptr_null_return(rsa_crypt->out_len); ++ chk_formula_fail_return(rsa_crypt->in_len == 0); ++ ++ ret = rsa_pub_enc_pad_init(&pad, rsa_enc, rsa_crypt, arr_em, sizeof(arr_em)); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pub_enc_pad_init); ++ ++ switch (rsa_enc->scheme) { ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_NO_PADDING: ++ /* NO PADDING scheme inlen must be the same as klen */ ++ chk_formula_fail_return(pad.in_len != pad.klen); ++ ++ pad.out_len = pad.klen; ++ ret = memcpy_s(pad.out_data, pad.out_len, pad.in_data, pad.in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_1: ++ ot_err_cipher("RSA padding mode error, mode = 0x%x.\n", rsa_enc->scheme); ++ ot_err_cipher("For a public key encryption operation, the block type shall be 02.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_2: ++ pad.key_bt = (td_u8)(rsa_enc->scheme - OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0); ++ ret = rsa_padding_add_pkcs1_type(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_pkcs1_type); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA1: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA224: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA256: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA384: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA512: ++ ret = rsa_padding_add_pkcs1_oaep(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_pkcs1_oaep); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_PKCS1_V1_5: ++ ret = rsa_padding_add_pkcs1_v15(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_pkcs1_v15); ++ break; ++ default: ++ ot_err_cipher("RSA padding mode error, mode = 0x%x.\n", rsa_enc->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ *rsa_crypt->out_len = pad.out_len; ++ return rsa_public(&rsa_enc->pub_key, pad.out_data, rsa_crypt->out); ++} ++ ++td_s32 ot_mpi_cipher_rsa_private_decrypt( ++ const ot_cipher_rsa_private_encrypt *rsa_decrypt, ot_cipher_rsa_crypt *rsa_crypt) ++{ ++ td_s32 ret; ++ td_u8 arr_em[CIPHER_MAX_RSA_KEY_LEN] = {0}; ++ rsa_padding_s pad; ++ ++ chk_dev_open_fail_return(); ++ chk_ptr_null_return(rsa_decrypt); ++ chk_ptr_null_return(rsa_crypt); ++ chk_ptr_null_return(rsa_crypt->in); ++ chk_ptr_null_return(rsa_crypt->out); ++ chk_ptr_null_return(rsa_crypt->out_len); ++ chk_formula_fail_return(rsa_crypt->in_len == 0); ++ ++ ret = rsa_pri_dec_pad_init(&pad, rsa_decrypt, rsa_crypt, arr_em, sizeof(arr_em)); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pri_dec_pad_init); ++ ++ ret = rsa_private(&rsa_decrypt->private_key, rsa_decrypt->ca_type, rsa_crypt->in, pad.in_data); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_private); ++ ++ switch (rsa_decrypt->scheme) { ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_NO_PADDING: ++ pad.out_len = pad.in_len; ++ ret = memcpy_s(pad.out_data, pad.out_len, pad.in_data, pad.in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_1: ++ ot_err_cipher("RSA padding mode error, mode = 0x%x.\n", rsa_decrypt->scheme); ++ ot_err_cipher("For a private key decryption operation, the block type shall be 02.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_2: ++ pad.key_bt = (td_u8)(rsa_decrypt->scheme - OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0); ++ ret = rsa_padding_check_pkcs1_type(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_pkcs1_type); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA1: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA224: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA256: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA384: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA512: ++ ret = rsa_padding_check_pkcs1_oaep(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_pkcs1_oaep); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_PKCS1_V1_5: ++ ret = rsa_padding_check_pkcs1_v15(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_pkcs1_oaep); ++ break; ++ default: ++ ot_err_cipher("RSA scheme error, scheme = 0x%x.\n", rsa_decrypt->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ *rsa_crypt->out_len = pad.out_len; ++ return ret; ++} ++ ++td_s32 ot_mpi_cipher_rsa_private_encrypt( ++ const ot_cipher_rsa_private_encrypt *rsa_encrypt, ot_cipher_rsa_crypt *rsa_crypt) ++{ ++ td_s32 ret; ++ rsa_padding_s pad; ++ td_u8 arr_em[CIPHER_MAX_RSA_KEY_LEN] = {0}; ++ ++ chk_dev_open_fail_return(); ++ chk_ptr_null_return(rsa_encrypt); ++ chk_ptr_null_return(rsa_crypt); ++ chk_ptr_null_return(rsa_crypt->in); ++ chk_ptr_null_return(rsa_crypt->out); ++ chk_ptr_null_return(rsa_crypt->out_len); ++ chk_formula_fail_return(rsa_crypt->in_len == 0); ++ ++ ret = rsa_pri_enc_pad_init(&pad, rsa_encrypt, rsa_crypt, arr_em, sizeof(arr_em)); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pri_enc_pad_init); ++ ++ switch (rsa_encrypt->scheme) { ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_NO_PADDING: ++ /* NO PADDING scheme inlen must be the same as klen */ ++ chk_formula_fail_return(pad.in_len != pad.klen); ++ ++ pad.out_len = pad.klen; ++ ret = memcpy_s(pad.out_data, pad.out_len, pad.in_data, pad.in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_1: ++ pad.key_bt = (td_u8)(rsa_encrypt->scheme - OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0); ++ ret = rsa_padding_add_pkcs1_type(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_pkcs1_type); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_2: ++ ot_err_cipher("RSA padding mode error, mode = 0x%x.\n", rsa_encrypt->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA1: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA224: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA256: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA384: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA512: ++ ret = rsa_padding_add_pkcs1_oaep(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_pkcs1_oaep); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_PKCS1_V1_5: ++ pad.key_bt = 0x01; ++ ret = rsa_padding_add_pkcs1_type(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_pkcs1_type); ++ break; ++ default: ++ ot_err_cipher("RSA padding mode error, mode = 0x%x.\n", rsa_encrypt->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ chk_u32_data_fail_return(pad.out_len, pad.klen); ++ *rsa_crypt->out_len = pad.out_len; ++ ++ return rsa_private(&rsa_encrypt->private_key, rsa_encrypt->ca_type, pad.out_data, rsa_crypt->out); ++} ++ ++td_s32 ot_mpi_cipher_rsa_pub_decrypt( ++ const ot_cipher_rsa_pub_encrypt *rsa_decrypt, ot_cipher_rsa_crypt *rsa_crypt) ++{ ++ td_s32 ret; ++ td_u8 arr_em[CIPHER_MAX_RSA_KEY_LEN] = {0}; ++ rsa_padding_s pad; ++ ++ chk_dev_open_fail_return(); ++ chk_ptr_null_return(rsa_decrypt); ++ chk_ptr_null_return(rsa_crypt); ++ chk_ptr_null_return(rsa_crypt->in); ++ chk_ptr_null_return(rsa_crypt->out); ++ chk_ptr_null_return(rsa_crypt->out_len); ++ chk_formula_fail_return(rsa_crypt->in_len == 0); ++ ++ ret = rsa_pub_dec_pad_init(&pad, rsa_decrypt, rsa_crypt, arr_em, sizeof(arr_em)); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_pub_dec_pad_init); ++ ++ ret = rsa_public(&rsa_decrypt->pub_key, rsa_crypt->in, pad.in_data); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_public); ++ ++ switch (rsa_decrypt->scheme) { ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_NO_PADDING: ++ pad.out_len = pad.in_len; ++ ret = memcpy_s(pad.out_data, pad.out_len, pad.in_data, pad.in_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_1: ++ pad.key_bt = (td_u8)(rsa_decrypt->scheme - OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0); ++ ret = rsa_padding_check_pkcs1_type(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_pkcs1_type); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_2: ++ ot_err_cipher("RSA padding mode error, mode = 0x%x.\n", rsa_decrypt->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA1: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA224: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA256: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA384: ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA512: ++ ret = rsa_padding_check_pkcs1_oaep(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_pkcs1_oaep); ++ break; ++ case OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_PKCS1_V1_5: ++ pad.key_bt = 0x01; ++ ret = rsa_padding_check_pkcs1_type(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_pkcs1_type); ++ break; ++ default: ++ ot_err_cipher("RSA scheme error, scheme = 0x%x.\n", rsa_decrypt->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ *rsa_crypt->out_len = pad.out_len; ++ return ret; ++} ++ ++td_s32 ot_mpi_cipher_rsa_sign( ++ const ot_cipher_rsa_sign *rsa_sign, ot_cipher_sign_data *sign_data) ++{ ++ td_s32 ret; ++ rsa_padding_s pad; ++ rsa_sign_buf sign_buf; ++ ++ chk_dev_open_fail_return(); ++ chk_ptr_null_return(rsa_sign); ++ chk_ptr_null_return(sign_data); ++ chk_ptr_null_return(sign_data->sign); ++ chk_ptr_null_return(sign_data->sign_len); ++ chk_ptr_null_return(rsa_sign->private_key.n); ++ ++ chk_formula_fail_return(((sign_data->hash_data == TD_NULL) || (sign_data->hash_data_len == 0)) && ++ ((sign_data->in == TD_NULL) || (sign_data->in_len == 0))); ++ ++ (td_void)memset_s(&sign_buf, sizeof(rsa_sign_buf), 0, sizeof(rsa_sign_buf)); ++ ret = rsa_sign_pad_init(&pad, rsa_sign, sign_data, &sign_buf); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_sign_pad_init); ++ ++ switch (rsa_sign->scheme) { ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA1: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA224: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA256: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA384: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA512: ++ ret = rsa_padding_add_emsa_pkcs1_v15(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_emsa_pkcs1_v15); ++ break; ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA1: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA224: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA256: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA384: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA512: ++ pad.em_bit = rsa_get_bit_num(rsa_sign->private_key.n, pad.klen); ++ ret = rsa_padding_add_pkcs1_pss(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_add_pkcs1_pss); ++ break; ++ default: ++ ot_err_cipher("invalid scheme; 0x%x\n", rsa_sign->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ chk_u32_data_fail_return(pad.out_len, pad.klen); ++ *sign_data->sign_len = pad.out_len; ++ ++ return rsa_private(&rsa_sign->private_key, rsa_sign->ca_type, pad.out_data, sign_data->sign); ++} ++ ++td_s32 ot_mpi_cipher_rsa_verify( ++ const ot_cipher_rsa_verify *rsa_verify, const ot_cipher_verify_data *verify_data) ++{ ++ td_s32 ret; ++ rsa_padding_s pad; ++ rsa_sign_buf sign_buf; ++ td_u8 arr_hash[HASH_RESULT_MAX_LEN] = {0}; ++ ++ chk_dev_open_fail_return(); ++ chk_ptr_null_return(rsa_verify); ++ chk_ptr_null_return(verify_data); ++ chk_ptr_null_return(verify_data->sign); ++ ++ chk_formula_fail_return(((verify_data->hash_data == TD_NULL) || (verify_data->hash_data_len == 0)) && ++ ((verify_data->in == TD_NULL) || (verify_data->in_len == 0))); ++ ++ (td_void)memset_s(&sign_buf, sizeof(rsa_sign_buf), 0, sizeof(rsa_sign_buf)); ++ ret = rsa_verify_pad_init(&pad, rsa_verify, verify_data, &sign_buf); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_verify_pad_init); ++ ++ ret = rsa_verify_get_hash(verify_data, arr_hash, sizeof(arr_hash), pad.hash_type); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_verify_get_hash); ++ ++ switch (rsa_verify->scheme) { ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA1: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA224: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA256: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA384: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA512: ++ ret = rsa_padding_check_emsa_pkcs1_v15(&pad); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_emsa_pkcs1_v15); ++ ret = memcmp(arr_hash, pad.out_data, pad.hlen); ++ ++ chk_func_fail_return(ret != 0, OT_ERR_CIPHER_FAILED_DECRYPT, memcmp); ++ break; ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA1: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA224: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA256: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA384: ++ case OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA512: ++ pad.em_bit = rsa_get_bit_num(rsa_verify->pub_key.n, pad.klen); ++ ret = rsa_padding_check_pkcs1_pss(&pad, arr_hash, sizeof(arr_hash)); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, rsa_padding_check_pkcs1_pss); ++ break; ++ default: ++ ot_err_cipher("invalid scheme; 0x%x\n", rsa_verify->scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return ret; ++} +diff --git a/product/security_subsys/cipher/v2/build.mak b/product/security_subsys/cipher/v2/build.mak +new file mode 100644 +index 0000000..c8433a7 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/build.mak +@@ -0,0 +1,28 @@ ++# INTER_DRV defined before include arch/build.mak ++ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss919v100" "ss319v100" "ss015v100")) ++INTER_DRV := SS919V100 ++else ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss918v100" "ss318v100" "ss013v100")) ++INTER_DRV := SS918V100 ++else ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss812v100" "ss813v100" "ss815v100" "ss312v100" \ ++ "ss313v100" "ss011v100" "ss012v100")) ++INTER_DRV := SS812V100 ++else ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss101v200" "ss101v500" "ss101v600" "ss101v300")) ++INTER_DRV := SS101V200 ++else ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss528v100" "ss625v100")) ++INTER_DRV := SS528V100 ++else ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss524v100" "ss522v101" "ss522v100" "ss615v100")) ++INTER_DRV := SS524V100 ++endif ++ ++CIPHER_BASE_DIR := $(srctree)/product/security_subsys/cipher/v2 ++CIPHER_PREFIX := v2 ++ ++cflags-y += -DCHIP_TYPE_$(INTER_DRV) ++cflags-y += -I$(srctree)/product/security_subsys/ext_inc ++ ++include $(CIPHER_BASE_DIR)/api/build.mak ++include $(CIPHER_BASE_DIR)/drv/build.mak ++ ++ccflags-y += $(cflags-y) ++HOSTCFLAGS += $(cflags-y) ++CPPFLAGS += $(cflags-y) +diff --git a/product/security_subsys/cipher/v2/drv/build.mak b/product/security_subsys/cipher/v2/drv/build.mak +new file mode 100644 +index 0000000..4d70197 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/build.mak +@@ -0,0 +1,17 @@ ++cflags-y += -I$(CIPHER_BASE_DIR)/drv/platform ++cflags-y += -I$(CIPHER_BASE_DIR)/drv/rng ++cflags-y += -I$(CIPHER_BASE_DIR)/drv/rsa ++cflags-y += -I$(CIPHER_BASE_DIR)/drv/compat ++cflags-y += -I$(CIPHER_BASE_DIR)/drv/sm2 ++cflags-y += -I$(CIPHER_BASE_DIR)/drv/spacc ++cflags-y += -I$(CIPHER_BASE_DIR)/drv/include ++ ++obj-y += $(CIPHER_PREFIX)/drv/platform/cipher_adapt.o ++obj-y += $(CIPHER_PREFIX)/drv/drv_cipher_intf.o ++obj-y += $(CIPHER_PREFIX)/drv/rng/drv_rng.o ++obj-y += $(CIPHER_PREFIX)/drv/rsa/drv_rsa.o ++obj-y += $(CIPHER_PREFIX)/drv/spacc/spacc_body.o ++obj-y += $(CIPHER_PREFIX)/drv/spacc/spacc_intf.o ++obj-y += $(CIPHER_PREFIX)/drv/compat/hal_otp.o ++obj-y += $(CIPHER_PREFIX)/drv/compat/drv_klad.o ++obj-y += $(CIPHER_PREFIX)/drv/compat/ot_drv_compat.o +diff --git a/product/security_subsys/cipher/v2/drv/compat/drv_klad.c b/product/security_subsys/cipher/v2/drv/compat/drv_klad.c +new file mode 100644 +index 0000000..affaf54 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/compat/drv_klad.c +@@ -0,0 +1,273 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_klad.h" ++#include "cipher_adapt.h" ++#include "hal_otp.h" ++ ++/* Define the union klad_ctrl */ ++typedef union { ++ struct { ++ td_u32 start : 1; /* [0] */ ++ td_u32 decrypt : 1; /* [1] */ ++ td_u32 type : 2; /* [3..2] */ ++ td_u32 high_low_128bit_flag : 1; /* [4] */ ++ td_u32 resv1 : 11; /* [15..5] */ ++ td_u32 klad2ci_addr : 3; /* [18..16] */ ++ td_u32 resv2 : 13; /* [31..19] */ ++ } bits; ++ ++ td_u32 u32; ++} klad_ctrl; ++ ++#define KLAD_REG_BASE_ADDR g_klad_base ++#define KLAD_REG_KLAD_CTRL (KLAD_REG_BASE_ADDR + 0x00) ++#define KLAD_REG_DAT_IN (KLAD_REG_BASE_ADDR + 0x10) ++#define KLAD_REG_ENC_OUT (KLAD_REG_BASE_ADDR + 0x20) ++#define KLAD_KEY_LEN 4 ++#define CIPHER_WAIT_IDEL_TIMES 1000 ++ ++static td_void *g_klad_base = TD_NULL; ++ ++static td_s32 hal_cipher_klad_config(td_u32 chn_id, ++ td_u32 opt_id, ot_cipher_klad_target klad_target, td_bool is_decrypt) ++{ ++ td_s32 ret; ++ klad_ctrl ctrl; ++ ++ /* Load efuse or OTP key to KLAD */ ++ ret = hal_efuse_otp_load_cipher_key(chn_id, opt_id); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ ctrl.u32 = 0; ++ ctrl.bits.klad2ci_addr = chn_id; ++ ctrl.bits.type = klad_target; ++ ctrl.bits.decrypt = is_decrypt; ++ ctrl.bits.start = 0; ++ ++ (td_void)hal_cipher_write_reg(KLAD_REG_KLAD_CTRL, ctrl.u32); ++ ++ return TD_SUCCESS; ++} ++ ++static td_void hal_cipher_start_klad(td_u32 block_num) ++{ ++ klad_ctrl ctrl; ++ ++ ctrl.u32 = 0; ++ hal_cipher_read_reg(KLAD_REG_KLAD_CTRL, &ctrl.u32); ++ ++ /* High 128bits is just meaningful for loading cipher 256 bits key, and meaningless to rsa. */ ++ ctrl.bits.high_low_128bit_flag = block_num; ++ ++ /* start */ ++ ctrl.bits.start = 1; ++ hal_cipher_write_reg(KLAD_REG_KLAD_CTRL, ctrl.u32); ++} ++ ++static td_void hal_cipher_set_klad_data(const td_u32 *data_input, td_u32 data_len) ++{ ++ td_u32 i; ++ ++ for (i = 0; i < data_len; i++) { ++ hal_cipher_write_reg(KLAD_REG_DAT_IN + i * KLAD_KEY_LEN, data_input[i]); ++ } ++} ++ ++static td_void hal_cipher_get_klad_data(td_u32 *data_output) ++{ ++ td_u32 i; ++ ++ for (i = 0; i < KLAD_KEY_LEN; i++) { ++ hal_cipher_read_reg(KLAD_REG_ENC_OUT + i * KLAD_KEY_LEN, &data_output[i]); ++ } ++} ++ ++static td_s32 hal_cipher_wait_klad_done(void) ++{ ++ td_u32 try_count = 0; ++ td_u32 ctrl; ++ ++ do { ++ hal_cipher_read_reg(KLAD_REG_KLAD_CTRL, &ctrl); ++ if ((ctrl & 0x01) == 0x00) { ++ return TD_SUCCESS; ++ } ++ try_count++; ++ } while (try_count < CIPHER_WAIT_IDEL_TIMES); ++ ++ ot_err_cipher("Klad time out!\n"); ++ ++ return TD_FAILURE; ++} ++ ++static td_void hal_klad_init(td_void) ++{ ++ td_u32 crg_value; ++ td_u32 *sys_addr; ++ ++ sys_addr = cipher_ioremap_nocache(CIPHER_KLAD_CRG_ADDR_PHY, 0x100); ++ if (sys_addr == TD_NULL) { ++ ot_err_cipher("ERROR: sys_addr ioremap with nocache failed!!\n"); ++ return; ++ } ++ ++ hal_cipher_read_reg(sys_addr, &crg_value); ++ crg_value |= KLAD_CRG_RESET_BIT; /* reset */ ++ crg_value |= KLAD_CRG_CLOCK_BIT; /* set the bit 0, clock opened */ ++ hal_cipher_write_reg(sys_addr, crg_value); ++ ++ /* clock select and cancel reset 0x30100 */ ++ crg_value &= (~KLAD_CRG_RESET_BIT); /* cancel reset */ ++ crg_value |= KLAD_CRG_CLOCK_BIT; /* set the bit 0, clock opened */ ++ hal_cipher_write_reg(sys_addr, crg_value); ++ ++ cipher_iounmap(sys_addr); ++} ++ ++td_s32 drv_klad_init(td_void) ++{ ++ td_s32 ret; ++ ++ g_klad_base = cipher_ioremap_nocache(CIPHER_KLAD_REG_BASE_ADDR_PHY, 0x100); ++ if (g_klad_base == TD_NULL) { ++ ot_err_cipher("ERROR: osal_ioremap_nocache for KLAD failed!!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = hal_efuse_otp_init(); ++ if (ret != TD_SUCCESS) { ++ cipher_iounmap(g_klad_base); ++ return ret; ++ } ++ ++ hal_klad_init(); ++ ++ return TD_SUCCESS; ++} ++ ++td_void drv_klad_deinit(td_void) ++{ ++ if (g_klad_base != TD_NULL) { ++ cipher_iounmap(g_klad_base); ++ g_klad_base = TD_NULL; ++ } ++ if (g_efuse_otp_reg_base != TD_NULL) { ++ cipher_iounmap(g_efuse_otp_reg_base); ++ g_efuse_otp_reg_base = TD_NULL; ++ } ++ return; ++} ++ ++static td_void drv_cipher_invbuf(td_u8 *buf, td_u32 u32len) ++{ ++ td_u32 i; ++ td_u8 ch; ++ ++ for (i = 0; i < u32len / 2; i++) { /* 2 */ ++ ch = buf[i]; ++ buf[i] = buf[u32len - i - 1]; ++ buf[u32len - i - 1] = ch; ++ } ++} ++ ++td_s32 drv_cipher_klad_load_key(td_u32 chn_id, ot_cipher_ca_type root_key, ++ ot_cipher_klad_target klad_target, const td_u8 *data_input, td_u32 key_len) ++{ ++ td_s32 ret; ++ td_u32 i, opt_id; ++ td_u32 key[KLAD_KEY_LEN] = {0}; ++ ++ if ((root_key < OT_CIPHER_KEY_SRC_KLAD_1) || ++ (root_key > OT_CIPHER_KEY_SRC_KLAD_3)) { ++ ot_err_cipher("Error: Invalid Root Key src 0x%x!\n", root_key); ++ return TD_FAILURE; ++ } ++ ++ if (((key_len % 16) != 0) || (key_len == 0)) { /* key 16 align */ ++ ot_err_cipher("Error: Invalid key len 0x%x!\n", key_len); ++ return TD_FAILURE; ++ } ++ ++ opt_id = root_key - OT_CIPHER_KEY_SRC_KLAD_1 + 1; ++ ++ ret = hal_cipher_klad_config(chn_id, opt_id, klad_target, TD_TRUE); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("Error: cipher klad config failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ for (i = 0; i < key_len / 16; i++) { /* key 16 align */ ++ ret = memcpy_s(key, sizeof(key), data_input + i * 16, 16); /* key 16 align */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ hal_cipher_set_klad_data(key, KLAD_KEY_LEN); ++ hal_cipher_start_klad(i); ++ ret = hal_cipher_wait_klad_done(); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("Error: cipher klad wait done failed!\n"); ++ return TD_FAILURE; ++ } ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 drv_cipher_klad_encrypt_key(ot_cipher_ca_type root_key, ++ ot_cipher_klad_target klad_target, td_u32 *clean_key, td_u32 *encrypt_key) ++{ ++ td_s32 ret; ++ td_u32 opt_id; ++ ++ if ((root_key < OT_CIPHER_KEY_SRC_KLAD_1) || ++ (root_key >= OT_CIPHER_KEY_SRC_BUTT)) { ++ ot_err_cipher("Error: Invalid Root Key src 0x%x!\n", root_key); ++ return TD_FAILURE; ++ } ++ ++ if ((clean_key == TD_NULL) || (encrypt_key == TD_NULL)) { ++ ot_err_cipher("Clean key or encrypt key is null.\n"); ++ return TD_FAILURE; ++ } ++ ++ opt_id = root_key - OT_CIPHER_KEY_SRC_KLAD_1 + 1; ++ ++ ret = hal_cipher_klad_config(0, opt_id, OT_CIPHER_KLAD_TARGET_AES, TD_FALSE); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("Error: cipher klad config failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ if (klad_target == OT_CIPHER_KLAD_TARGET_RSA) ++ drv_cipher_invbuf((td_u8*)clean_key, 16); /* 16 clean key len */ ++ ++ hal_cipher_set_klad_data(clean_key, KLAD_KEY_LEN); ++ hal_cipher_start_klad(0); ++ ret = hal_cipher_wait_klad_done(); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("Error: cipher klad wait done failed!\n"); ++ return TD_FAILURE; ++ } ++ hal_cipher_get_klad_data(encrypt_key); ++ ++ return TD_SUCCESS; ++} ++ +diff --git a/product/security_subsys/cipher/v2/drv/compat/drv_klad.h b/product/security_subsys/cipher/v2/drv/compat/drv_klad.h +new file mode 100644 +index 0000000..7fa782a +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/compat/drv_klad.h +@@ -0,0 +1,38 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_KLAD_H ++#define DRV_KLAD_H ++ ++#include "ot_type.h" ++#include "common.h" ++#include "drv_cipher_ioctl.h" ++ ++extern td_void *g_efuse_otp_reg_base; ++ ++td_s32 drv_klad_init(td_void); ++td_void drv_klad_deinit(td_void); ++ ++td_s32 drv_cipher_klad_load_key(td_u32 chn_id, ot_cipher_ca_type root_key, ++ ot_cipher_klad_target klad_target, const td_u8 *data_input, td_u32 key_len); ++ ++td_s32 drv_cipher_klad_encrypt_key(ot_cipher_ca_type root_key, ++ ot_cipher_klad_target klad_target, td_u32 *clean_key, td_u32 *encrypt_key); ++ ++#endif /* DRV_KLAD_H */ +diff --git a/product/security_subsys/cipher/v2/drv/compat/hal_otp.c b/product/security_subsys/cipher/v2/drv/compat/hal_otp.c +new file mode 100644 +index 0000000..bf70b3f +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/compat/hal_otp.c +@@ -0,0 +1,185 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "hal_otp.h" ++#include "cipher_adapt.h" ++ ++#ifdef OTP_SUPPORT ++ ++typedef enum { ++ OTP_USER_LOCK_STA0_TYPE, ++ OTP_USER_LOCK_STA1_TYPE, ++ OTP_USER_LOCK_UNKNOWN_STA, ++} opt_lock_sta_type_e; ++ ++typedef enum { ++ OTP_READ_LOCK_STA_MODE, ++ OTP_LOCK_CIPHER_KEY_MODE, ++ OTP_WRITE_KEY_ID_OR_PASSWD_MODE, ++ OTP_KEY_ID_OR_PASSWD_CRC_MODE, ++ OTP_SET_FLAG_ENABLE_MODE, ++ OTP_WRITE_USER_ROOM_MODE, ++ OTP_READ_USER_ROOM_MODE, ++ OTP_UNKOOWN_MODE, ++} otp_user_work_mode_e; ++ ++typedef enum { ++ OTP_USER_KEY0, ++ OTP_USER_KEY1, ++ OTP_USER_KEY2, ++ OTP_USER_KEY3, ++ OTP_USER_KEY_JTAG_PW_ID, ++ OTP_USER_KEY_JTAG_PW, ++ OTP_USER_KEY_ROOTKEY, ++ OTP_USER_KEY_UNKNOWN, ++} otp_user_key_index_e; ++ ++typedef enum { ++ OTP_KEY_LENGTH_64BIT, ++ OTP_KEY_LENGTH_128BIT, ++ OTP_KEY_LENGTH_256BIT, ++ OTP_KEY_LENGTH_UNSUPPORT, ++} otp_user_key_length_e; ++ ++td_void *g_efuse_otp_reg_base = TD_NULL; ++ ++/* OTP init */ ++td_s32 hal_efuse_otp_init(td_void) ++{ ++ td_u32 crg_value = 0; ++ td_u32 *sys_addr = TD_NULL; ++ ++ sys_addr = cipher_ioremap_nocache(REG_SYS_OTP_CLK_ADDR_PHY, 0x100); ++ if (sys_addr == TD_NULL) { ++ ot_err_cipher("ERROR: sys_addr ioremap with nocache failed!!\n"); ++ return TD_FAILURE; ++ } ++ ++ hal_cipher_read_reg(sys_addr, &crg_value); ++#ifdef OTP_CRG_RESET_SUPPORT ++ crg_value |= OTP_CRG_RESET_BIT; /* reset */ ++ crg_value |= OTP_CRG_CLOCK_BIT; /* set the bit 0, clock opened */ ++ hal_cipher_write_reg(sys_addr, crg_value); ++ ++ /* clock select and cancel reset 0x30100 */ ++ crg_value &= (~OTP_CRG_RESET_BIT); /* cancel reset */ ++#endif ++ crg_value |= OTP_CRG_CLOCK_BIT; /* set the bit 0, clock opened */ ++ hal_cipher_write_reg(sys_addr, crg_value); ++ ++ cipher_iounmap(sys_addr); ++ sys_addr = TD_NULL; ++ ++ g_efuse_otp_reg_base = cipher_ioremap_nocache(CIPHER_OTP_REG_BASE_ADDR_PHY, 0x100); ++ if (g_efuse_otp_reg_base == TD_NULL) { ++ ot_err_cipher("ERROR: osal_ioremap_nocache for OTP failed!!\n"); ++ return TD_FAILURE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 hal_otp_wait_free(td_void) ++{ ++ td_u32 timeout_cnt = 0; ++ td_u32 reg_value = 0; ++ ++ while (1) { ++ hal_cipher_read_reg(OTP_USER_CTRL_STA, ®_value); ++ if ((reg_value & 0x1) == 0) /* bit0:otp_op_busy 0:idle, 1:busy */ ++ return TD_SUCCESS; ++ ++ timeout_cnt++; ++ if (timeout_cnt >= 10000) { /* 10000 count */ ++ ot_err_cipher("OTP_WaitFree TimeOut!\n"); ++ break; ++ } ++ } ++ return TD_FAILURE; ++} ++ ++static td_s32 hal_otp_set_mode(otp_user_work_mode_e otp_mode) ++{ ++ td_u32 reg_value = otp_mode; ++ ++ if (otp_mode >= OTP_UNKOOWN_MODE) { ++ ot_err_cipher("Mode Unknown!\n"); ++ return TD_FAILURE; ++ } ++ ++ (td_void)hal_cipher_write_reg(OTP_USER_WORK_MODE, reg_value); ++ return TD_SUCCESS; ++} ++ ++static td_void hal_otp_op_start(td_void) ++{ ++ td_u32 reg_value = 0x1acce551; ++ (td_void)hal_cipher_write_reg(OTP_USER_OP_START, reg_value); ++} ++ ++static td_s32 hal_otp_wait_op_done(td_void) ++{ ++ td_u32 timeout_cnt = 0; ++ td_u32 reg_value = 0; ++ ++ while (1) { ++ hal_cipher_read_reg(OTP_USER_CTRL_STA, ®_value); ++ if (reg_value & 0x2) { ++ return TD_SUCCESS; ++ } ++ ++ timeout_cnt++; ++ if (timeout_cnt >= 10000) { /* 10000 count */ ++ ot_err_cipher("OTP_Wait_OP_done TimeOut!\n"); ++ break; ++ } ++ } ++ return TD_FAILURE; ++} ++ ++static td_void hal_choose_otp_key(otp_user_key_index_e which_key) ++{ ++ td_u32 reg_value; ++ ++ reg_value = which_key; ++ (td_void)hal_cipher_write_reg(OTP_USER_KEY_INDEX, reg_value); ++} ++ ++/* set otp key to klad */ ++td_s32 hal_efuse_otp_load_cipher_key(td_u32 chn_id, td_u32 opt_id) ++{ ++ if (opt_id > OTP_USER_KEY3) ++ opt_id = OTP_USER_KEY0; ++ ++ if (TD_FAILURE == hal_otp_wait_free()) ++ return TD_FAILURE; ++ hal_choose_otp_key(opt_id); ++ ++ if (hal_otp_set_mode(OTP_LOCK_CIPHER_KEY_MODE)) ++ return TD_FAILURE; ++ ++ hal_otp_op_start(); ++ ++ if (TD_FAILURE == hal_otp_wait_op_done()) ++ return TD_FAILURE; ++ ++ return TD_SUCCESS; ++} ++#endif ++ +diff --git a/product/security_subsys/cipher/v2/drv/compat/hal_otp.h b/product/security_subsys/cipher/v2/drv/compat/hal_otp.h +new file mode 100644 +index 0000000..3019b7d +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/compat/hal_otp.h +@@ -0,0 +1,69 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HAL_OTP_H ++#define HAL_OTP_H ++ ++#include "ot_type.h" ++ ++#define OTP_USER_IF_BASE g_efuse_otp_reg_base ++#define OTP_USER_WORK_MODE (OTP_USER_IF_BASE + 0x0000) ++#define OTP_USER_OP_START (OTP_USER_IF_BASE + 0x0004) ++#define OTP_USER_KEY_INDEX (OTP_USER_IF_BASE + 0x0008) ++#define OTP_USER_KEY_DATA0 (OTP_USER_IF_BASE + 0x000c) ++#define OTP_USER_KEY_DATA1 (OTP_USER_IF_BASE + 0x0010) ++#define OTP_USER_KEY_DATA2 (OTP_USER_IF_BASE + 0x0014) ++#define OTP_USER_KEY_DATA3 (OTP_USER_IF_BASE + 0x0018) ++#define OTP_USER_KEY_DATA4 (OTP_USER_IF_BASE + 0x001c) ++#define OTP_USER_KEY_DATA5 (OTP_USER_IF_BASE + 0x0020) ++#define OTP_USER_KEY_DATA6 (OTP_USER_IF_BASE + 0x0024) ++#define OTP_USER_KEY_DATA7 (OTP_USER_IF_BASE + 0x0028) ++#define OTP_USER_KEY_DATA8 (OTP_USER_IF_BASE + 0x002c) ++#define OTP_USER_FLAG_VALUE (OTP_USER_IF_BASE + 0x0030) ++#define OTP_USER_FLAG_INDEX (OTP_USER_IF_BASE + 0x0034) ++#define OTP_USER_REV_ADDR (OTP_USER_IF_BASE + 0x0038) ++#define OTP_USER_REV_WDATA (OTP_USER_IF_BASE + 0x003c) ++#define OTP_USER_REV_RDATA (OTP_USER_IF_BASE + 0x0040) ++#define OTP_USER_LOCK_STA0 (OTP_USER_IF_BASE + 0x0044) ++#define OTP_USER_LOCK_STA1 (OTP_USER_IF_BASE + 0x0048) ++#define OTP_USER_CTRL_STA (OTP_USER_IF_BASE + 0x004c) ++ ++#if defined(CHIP_TYPE_SS919V100) ++#define REG_SYS_OTP_CLK_ADDR_PHY 0x12010194 ++#define OTP_CRG_CLOCK_BIT (0x01 << 7) ++#define OTP_CRG_RESET_BIT (0x01 << 6) ++#define OTP_CRG_RESET_SUPPORT ++#elif defined(CHIP_TYPE_SS918V100) ++#define REG_SYS_OTP_CLK_ADDR_PHY 0x04510194 ++#define OTP_CRG_CLOCK_BIT (0x01 << 7) ++#elif defined(CHIP_TYPE_SS812V100) ++#define REG_SYS_OTP_CLK_ADDR_PHY 0x120101BC ++#define OTP_CRG_CLOCK_BIT (0x01 << 1) ++#elif defined(CHIP_TYPE_SS101V200) ++#define REG_SYS_OTP_CLK_ADDR_PHY 0x120101BC ++#define OTP_CRG_CLOCK_BIT (0x01 << 1) ++#elif (defined(CHIP_TYPE_SS528V100) || defined(CHIP_TYPE_SS524V100)) ++#define REG_SYS_OTP_CLK_ADDR_PHY 0x11013240 ++#define OTP_CRG_CLOCK_BIT (0x01 << 4) ++#endif ++ ++td_s32 hal_efuse_otp_init(td_void); ++td_s32 hal_efuse_otp_load_cipher_key(td_u32 chn_id, td_u32 opt_id); ++ ++#endif /* HAL_OTP_H */ +diff --git a/product/security_subsys/cipher/v2/drv/compat/ot_drv_compat.c b/product/security_subsys/cipher/v2/drv/compat/ot_drv_compat.c +new file mode 100644 +index 0000000..bcb3b04 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/compat/ot_drv_compat.c +@@ -0,0 +1,41 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ot_drv_compat.h" ++#include "drv_klad.h" ++ ++td_s32 ot_drv_compat_init(void) ++{ ++ td_s32 ret; ++ ++ ret = drv_klad_init(); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_drv_compat_deinit(void) ++{ ++ drv_klad_deinit(); ++ ++ return TD_SUCCESS; ++} ++ +diff --git a/product/security_subsys/cipher/v2/drv/compat/ot_drv_compat.h b/product/security_subsys/cipher/v2/drv/compat/ot_drv_compat.h +new file mode 100644 +index 0000000..7cf5fb8 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/compat/ot_drv_compat.h +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_DRV_COMPAT_H ++#define OT_DRV_COMPAT_H ++ ++#include "ot_type.h" ++ ++td_s32 ot_drv_compat_init(void); ++td_s32 ot_drv_compat_deinit(void); ++ ++#endif /* OT_DRV_COMPAT_H */ ++ +diff --git a/product/security_subsys/cipher/v2/drv/drv_cipher_intf.c b/product/security_subsys/cipher/v2/drv/drv_cipher_intf.c +new file mode 100644 +index 0000000..805fea7 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/drv_cipher_intf.c +@@ -0,0 +1,224 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "spacc_intf.h" ++#include "drv_cipher_ioctl.h" ++#include "cipher_adapt.h" ++#include "ot_drv_compat.h" ++#include "drv_rsa.h" ++#include "drv_rng.h" ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C"{ ++#endif ++#endif /* End of #ifdef __cplusplus */ ++ ++td_s32 drv_cipher_ioctl(td_u32 cmd, td_void *argp, td_void *private_data) ++{ ++ td_s32 ret; ++ ++ if (argp == TD_NULL) { ++ ot_err_cipher("Error, argp is NULL!\n"); ++ return TD_FAILURE; ++ } ++ ++ switch (cmd) { ++ case CMD_CIPHER_CREATEHANDLE: { ++ cipher_handle_s *handle = (cipher_handle_s *)argp; ++ ret = ot_drv_cipher_create_handle(handle, private_data); ++ break; ++ } ++ case CMD_CIPHER_DESTROYHANDLE: { ++ td_handle *handle = (td_handle *)argp; ++ ret = ot_drv_cipher_destory_handle(*handle); ++ break; ++ } ++ case CMD_CIPHER_CONFIGHANDLE_EX: { ++ cipher_config_ctrl_ex_s *config_ex = (cipher_config_ctrl_ex_s *)argp; ++ ret = ot_drv_cipher_config_chn_ex(config_ex->ci_handle, config_ex); ++ break; ++ } ++ case CMD_CIPHER_ENCRYPT: { ++ cipher_data_s *data = (cipher_data_s *)argp; ++ ret = ot_drv_cipher_encrypt(data); ++ break; ++ } ++ case CMD_CIPHER_DECRYPT: { ++ cipher_data_s *data = (cipher_data_s *)argp; ++ ret = ot_drv_cipher_decrypt(data); ++ break; ++ } ++ case CMD_CIPHER_ENCRYPTMULTI: { ++ cipher_pkg_s *pkg = (cipher_pkg_s *)argp; ++ ret = ot_drv_cipher_encrypt_multi(pkg); ++ break; ++ } ++ case CMD_CIPHER_DECRYPTMULTI: { ++ cipher_pkg_s *pkg = (cipher_pkg_s *)argp; ++ ret = ot_drv_cipher_decrypt_multi(pkg); ++ break; ++ } ++#ifdef CIPHER_KLAD_SUPPORT ++ case CMD_CIPHER_KLAD_KEY: { ++ cipher_klad_key_s *klad_data = (cipher_klad_key_s *)argp; ++ ret = ot_drv_cipher_klad_encrypt_key(klad_data); ++ break; ++ } ++#endif ++ case CMD_CIPHER_GETTAG: { ++ cipher_tag_s *tag = (cipher_tag_s *)argp; ++ ret = ot_drv_cipher_get_tag(tag); ++ break; ++ } ++ case CMD_CIPHER_GETRANDOMNUMBER: { ++ cipher_rng_s *rng = (cipher_rng_s *)argp; ++ ret = ot_drv_cipher_get_random_number(rng); ++ break; ++ } ++ case CMD_CIPHER_GETHANDLECONFIG_EX: { ++ cipher_config_ctrl_ex_s *data = (cipher_config_ctrl_ex_s *)argp; ++ ret = ot_drv_cipher_get_handle_config_ex(data); ++ break; ++ } ++ case CMD_CIPHER_CALCHASH_INIT: { ++ cipher_hash_data_s *hash_data = (cipher_hash_data_s*)argp; ++ ret = ot_drv_cipher_calc_hash_init(hash_data); ++ break; ++ } ++ case CMD_CIPHER_CALCHASHUPDATE: { ++ cipher_hash_data_s *hash_data = (cipher_hash_data_s*)argp; ++ ret = ot_drv_cipher_calc_hash_update(hash_data); ++ break; ++ } ++ case CMD_CIPHER_CALCHASHFINAL: { ++ cipher_hash_data_s *hash_data = (cipher_hash_data_s*)argp; ++ ret = ot_drv_cipher_calc_hash_final(hash_data); ++ break; ++ } ++ case CMD_CIPHER_CALCRSA: { ++ cipher_rsa_data_s *rsa_data = (cipher_rsa_data_s*)argp; ++ ret = ot_drv_cipher_calc_rsa(rsa_data); ++ break; ++ } ++#ifdef CONFIG_COMPAT ++#ifdef CONFIG_RSA_HARDWARE_SUPPORT ++ case CMD_CIPHER_COMPAT_CALCRSA: { ++ cipher_compat_rsa_data_s *compat_rsa_data = (cipher_compat_rsa_data_s*)argp; ++ cipher_rsa_data_s rsa_data; ++ ++ rsa_data.input_data = u32_to_point(compat_rsa_data->input_via); ++ rsa_data.output_data = u32_to_point(compat_rsa_data->output_via); ++ rsa_data.rsa_k = u32_to_point(compat_rsa_data->rsa_k_via); ++ rsa_data.rsa_n = u32_to_point(compat_rsa_data->rsa_n_via); ++ rsa_data.rsa_k_len = compat_rsa_data->rsa_k_len; ++ rsa_data.rsa_n_len = compat_rsa_data->rsa_n_len; ++ rsa_data.data_len = compat_rsa_data->data_len; ++ ++ ret = ot_drv_cipher_calc_rsa(&rsa_data); ++ break; ++ } ++#endif ++ case CMD_CIPHER_COMPAT_ENCRYPTMULTI: { ++ cipher_compat_pkg_s *compat_pkg = (cipher_compat_pkg_s *)argp; ++ cipher_pkg_s pkg; ++ ++ pkg.ci_handle = compat_pkg->ci_handle; ++ pkg.pkg_num = compat_pkg->pkg_num; ++ pkg.cipher_data = u32_to_point(compat_pkg->pkg_via); ++ ++ ret = ot_drv_cipher_encrypt_multi(&pkg); ++ break; ++ } ++ case CMD_CIPHER_COMPAT_DECRYPTMULTI: { ++ cipher_compat_pkg_s *compat_pkg = (cipher_compat_pkg_s *)argp; ++ cipher_pkg_s pkg; ++ ++ pkg.ci_handle = compat_pkg->ci_handle; ++ pkg.pkg_num = compat_pkg->pkg_num; ++ pkg.cipher_data = u32_to_point(compat_pkg->pkg_via); ++ ++ ret = ot_drv_cipher_decrypt_multi(&pkg); ++ break; ++ } ++#endif ++ default: ++ ot_err_cipher("Unsupported cmd, MOD_ID=0x%02X, NR=0x%02x, SIZE=0x%02x!\n", ++ ree_cipher_ioc_type(cmd), ree_cipher_ioc_nr(cmd), ree_cipher_ioc_size(cmd)); ++ ret = TD_FAILURE; ++ break; ++ } ++ ++ return ret; ++} ++ ++td_s32 cipher_module_init(td_void) ++{ ++ td_s32 ret; ++ ++ ret = drv_cipher_init(); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ ret = drv_rng_init(); ++ if (ret != TD_SUCCESS) { ++ (td_void)drv_cipher_deinit(); ++ return ret; ++ } ++ ++#ifdef CIPHER_KLAD_SUPPORT ++ ret = ot_drv_compat_init(); ++ if (ret != TD_SUCCESS) { ++ (td_void)drv_cipher_deinit(); ++ (td_void)drv_rng_deinit(); ++ return ret; ++ } ++#endif ++ ++ ret = drv_rsa_init(); ++ if (ret != TD_SUCCESS) { ++ (td_void)drv_cipher_deinit(); ++ (td_void)drv_rng_deinit(); ++#ifdef CIPHER_KLAD_SUPPORT ++ (td_void)ot_drv_compat_deinit(); ++#endif ++ return ret; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_void cipher_module_exit(td_void) ++{ ++ (td_void)drv_cipher_deinit(); ++ (td_void)drv_rng_deinit(); ++ (td_void)drv_rsa_deinit(); ++ ++#ifdef CIPHER_KLAD_SUPPORT ++ (td_void)ot_drv_compat_deinit(); ++#endif ++ return; ++} ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* End of #ifdef __cplusplus */ +diff --git a/product/security_subsys/cipher/v2/drv/include/drv_cipher_ioctl.h b/product/security_subsys/cipher/v2/drv/include/drv_cipher_ioctl.h +new file mode 100644 +index 0000000..219fa93 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/include/drv_cipher_ioctl.h +@@ -0,0 +1,102 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_CIPHER_IOCTL_H ++#define DRV_CIPHER_IOCTL_H ++ ++#include "ot_type.h" ++#include "ot_mpi_cipher.h" ++ ++td_s32 cipher_module_init(td_void); ++td_void cipher_module_exit(td_void); ++td_s32 drv_cipher_ioctl(td_u32 cmd, td_void *argp, td_void *private_data); ++ ++typedef struct { ++ td_u32 chn_id; ++ td_char *open_status; ++ td_char *alg; ++ td_char *mode; ++ td_u32 key_len; ++ td_char *key_from; ++ td_bool is_decrypt; ++ td_u32 data_in_size; ++ td_u32 data_in_addr; ++ td_u32 data_out_size; ++ td_u32 data_out_addr; ++ td_bool in_int_all_en; ++ td_bool in_int_en; ++ td_bool in_int_raw; ++ td_bool out_int_en; ++ td_bool out_int_raw; ++ td_u32 out_int_count; /* CHANn_INT_OCNTCFG */ ++ char iv_string[33]; /* 33 iv string size */ ++} cipher_chn_status_s; ++ ++#define REE_CIPHER_IOC_NA 0U ++#define REE_CIPHER_IOC_W 1U ++#define REE_CIPHER_IOC_R 2U ++#define REE_CIPHER_IOC_RW 3U ++ ++#define ree_cipher_ioc(dir, type, nr, size) (((dir) << 30) | ((size) << 16) | ((type) << 8) | ((nr) << 0)) ++ ++#define ree_cipher_ior(type, nr, size) ree_cipher_ioc(REE_CIPHER_IOC_R, (type), (nr), sizeof(size)) ++#define ree_cipher_iow(type, nr, size) ree_cipher_ioc(REE_CIPHER_IOC_W, (type), (nr), sizeof(size)) ++#define ree_cipher_iowr(type, nr, size) ree_cipher_ioc(REE_CIPHER_IOC_RW, (type), (nr), sizeof(size)) ++ ++#define ree_cipher_ioc_dir(nr) (((nr) >> 30) & 0x03) ++#define ree_cipher_ioc_type(nr) (((nr) >> 8) & 0xFF) ++#define ree_cipher_ioc_nr(nr) (((nr) >> 0) & 0xFF) ++#define ree_cipher_ioc_size(nr) (((nr) >> 16) & 0x3FFF) ++ ++#define OT_ID_CIPHER 100 ++ ++#ifdef __cplusplus ++extern "C"{ ++#endif /* __cplusplus */ ++ ++ ++#define CMD_CIPHER_CREATEHANDLE ree_cipher_iowr(OT_ID_CIPHER, 0x1, cipher_handle_s) ++#define CMD_CIPHER_DESTROYHANDLE ree_cipher_iow(OT_ID_CIPHER, 0x2, td_u32) ++#define CMD_CIPHER_CONFIGHANDLE ree_cipher_iow(OT_ID_CIPHER, 0x3, cipher_config_ctrl_s) ++#define CMD_CIPHER_ENCRYPT ree_cipher_iow(OT_ID_CIPHER, 0x4, cipher_data_s) ++#define CMD_CIPHER_DECRYPT ree_cipher_iow(OT_ID_CIPHER, 0x5, cipher_data_s) ++#define CMD_CIPHER_DECRYPTMULTI ree_cipher_iow(OT_ID_CIPHER, 0x6, cipher_pkg_s) ++#define CMD_CIPHER_ENCRYPTMULTI ree_cipher_iow(OT_ID_CIPHER, 0x7, cipher_pkg_s) ++#define CMD_CIPHER_GETRANDOMNUMBER ree_cipher_iowr(OT_ID_CIPHER, 0x8, cipher_rng_s) ++#define CMD_CIPHER_GETHANDLECONFIG ree_cipher_iowr(OT_ID_CIPHER, 0x9, cipher_config_ctrl_s) ++#define CMD_CIPHER_CALCHASH_INIT ree_cipher_iowr(OT_ID_CIPHER, 0xa, cipher_hash_data_s) ++#define CMD_CIPHER_CALCHASHUPDATE ree_cipher_iowr(OT_ID_CIPHER, 0xb, cipher_hash_data_s) ++#define CMD_CIPHER_CALCHASHFINAL ree_cipher_iowr(OT_ID_CIPHER, 0xc, cipher_hash_data_s) ++#define CMD_CIPHER_CALCRSA ree_cipher_iowr(OT_ID_CIPHER, 0x10, cipher_rsa_data_s) ++#define CMD_CIPHER_GETTAG ree_cipher_iowr(OT_ID_CIPHER, 0x11, cipher_tag_s) ++#define CMD_CIPHER_CONFIGHANDLE_EX ree_cipher_iowr(OT_ID_CIPHER, 0x23, cipher_config_ctrl_ex_s) ++#define CMD_CIPHER_GETHANDLECONFIG_EX ree_cipher_iowr(OT_ID_CIPHER, 0x24, cipher_config_ctrl_ex_s) ++#define CMD_CIPHER_KLAD_KEY ree_cipher_iowr(OT_ID_CIPHER, 0x12, cipher_klad_key_s) ++ ++#ifdef CONFIG_COMPAT ++#define CMD_CIPHER_COMPAT_DECRYPTMULTI ree_cipher_iow(OT_ID_CIPHER, 0x6, cipher_compat_pkg_s) ++#define CMD_CIPHER_COMPAT_ENCRYPTMULTI ree_cipher_iow(OT_ID_CIPHER, 0x7, cipher_compat_pkg_s) ++#define CMD_CIPHER_COMPAT_CALCRSA ree_cipher_iowr(OT_ID_CIPHER, 0x10, cipher_compat_rsa_data_s) ++#endif ++ ++#ifdef __cplusplus ++} ++#endif /* __cplusplus */ ++ ++#endif /* DRV_CIPHER_IOCTL_H */ +diff --git a/product/security_subsys/cipher/v2/drv/include/ot_drv_cipher.h b/product/security_subsys/cipher/v2/drv/include/ot_drv_cipher.h +new file mode 100644 +index 0000000..9842c34 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/include/ot_drv_cipher.h +@@ -0,0 +1,264 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_DRV_CIPHER_H ++#define OT_DRV_CIPHER_H ++ ++#include "ot_type.h" ++#include "ot_mpi_cipher.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif /* __cplusplus */ ++ ++#define MAX_MULTI_PKG_NUM 128 ++#define CIPHER_SOFT_CHAN_NUM 8 ++#define CIPHER_INVALID_CHN 0xffffffff ++#define CIPHER_MAX_NODE_BUF_SIZE 0xFFFF0 /* 1M-16 */ ++#define CIPHER_MAX_RSA_KEY_LEN 512 ++#define HASH_OUTPUT_SIZE_WORD (1600 / 8 / 4) /* sha3 state */ ++ ++#define HDCP_KEY_RAM_SIZE 320 ++#define HDCP_KEY_PRIME_SIZE 320 ++#define HDCP_KEY_TOOL_FILE_SIZE 384 ++#define HDCP_KEY_CHIP_FILE_SIZE (HDCP_KEY_RAM_SIZE + 12) ++ ++#define HASH_ALG_SHA2 0x01 ++#define HASH_ALG_SHA3 0x02 ++#define HASH_ALG_SM3 0x03 ++ ++#define HASH_MODE_RAW 0x01 ++#define HASH_MODE_MAC 0x02 ++#define HASH_MODE_SHAKE 0x03 ++ ++#define CPU_BIT_WIDTH_32 32 ++#define CPU_BIT_WIDTH_64 64 ++#define MY_CPU_BIT_WIDTH sizeof(td_size_t) ++ ++typedef enum { ++ OT_DRV_HASH_STEP_UPDATE = 0x0, ++ OT_DRV_HASH_STEP_INIT = 0x01, ++ OT_DRV_HASH_STEP_FINAL = 0x02, ++} ot_drv_hash_step_e; ++ ++typedef struct { ++ td_handle ci_handle; ++ ot_cipher_attr cipher_atts; ++} cipher_handle_s; ++ ++typedef struct { ++ td_handle ci_handle; ++ td_u32 src_phy_addr; ++ td_u32 src_phy_addr_high; ++ td_u32 dest_phy_addr; ++ td_u32 dest_phy_addr_high; ++ td_u32 data_length; ++} cipher_data_s; ++ ++typedef struct { ++ td_handle ci_handle; ++ td_u32 pkg_num; ++ td_u32 user_bit_width; ++ ot_cipher_data *cipher_data; ++} cipher_pkg_s; ++ ++typedef struct { ++ td_handle ci_handle; ++ ot_cipher_ctrl cipher_ctrl; ++} cipher_config_ctrl_s; ++ ++typedef struct { ++ td_handle ci_handle; ++ td_u32 time_out; ++} cipher_wait_done_s; ++ ++typedef struct { ++ td_handle ci_handle; ++ td_u32 key[12]; /* Key input, EK||AK||SK for SM1, 12 */ ++ td_u32 odd_key[8]; /* Key input, Old Key, 8 */ ++ td_u32 iv[4]; /* Initialization vector (IV), 4 */ ++ /* Encryption using advanced conditional access (CA) or decryption using keys */ ++ td_bool key_by_ca; ++ ot_cipher_ca_type ca_type; /* Select keyladder type when using advanced CA */ ++ ot_cipher_alg ci_alg; /* Cipher algorithm */ ++ ot_cipher_bit_width bit_width; /* Bit width for encryption or decryption */ ++ ot_cipher_work_mode work_mode; /* Operating mode */ ++ ot_cipher_key_len key_len; /* Key length */ ++ /* control information exchange choices, ++ * we default all woulde be change except they have been in the choices */ ++ ot_cipher_ctrl_chg_flag change_flags; ++ ot_cipher_sm1_round sm1_round; /* SM1 round number, should be 8, 10, 12 or 14 */ ++ /* IV length for CCM/GCM, which is an element of {4, 6, 8, 10, 12, 14, 16} for CCM, ++ * and is an element of [1-16] for GCM */ ++ td_u32 iv_len; ++ /* Tag length for CCM which is an element of {4, 6, 8, 10, 12, 14, 16} */ ++ td_u32 tag_len; ++ td_u32 alen; /* Associated data for CCM and GCM */ ++ td_u32 aphy_addr; ++ td_u32 aphy_addr_high; /* Physical address of Associated data for CCM and GCM */ ++} cipher_config_ctrl_ex_s; ++ ++typedef struct { ++ ot_cipher_hash_type sha_type; ++ td_u32 hard_chn; ++ td_u32 sha_val[16]; /* 16 size */ ++ td_u32 data_phy; ++ td_u32 data_phy_high; ++ td_u32 data_len; ++} cipher_hash_data_s; ++ ++typedef struct { ++ ot_cipher_hash_attr hash_attr; ++ td_handle hash_handle; ++} cipher_hash_init_s; ++ ++typedef struct { ++ td_handle hash_handle; ++ td_u8 *input_data; ++ td_u32 input_data_len; ++} cipher_hash_update_s; ++ ++typedef struct { ++ td_handle hash_handle; ++ td_u8 *output_hash; ++} cipher_hash_finish_s; ++ ++typedef struct { ++ td_u32 time_out_us; ++ td_u32 ci_rng; ++} cipher_rng_s; ++ ++typedef struct { ++ td_handle ci_handle; ++ td_u32 tag_len; ++ td_u32 tag[4]; /* 4 tag size */ ++} cipher_tag_s; ++ ++typedef struct { ++ td_u8 *input_data; ++ td_u8 *output_data; ++ td_u32 data_len; ++ td_u8 *rsa_n; ++ td_u8 *rsa_k; ++ td_u16 rsa_n_len; ++ td_u16 rsa_k_len; ++ ot_cipher_ca_type ca_type; /* Select keyladder type when using advanced CA */ ++} cipher_rsa_data_s; ++ ++/** RSA private key struct */ ++typedef struct { ++ td_u8 *rsa_n; /*!< public modulus */ ++ td_u8 *rsa_e; /*!< public exponent */ ++ td_u8 *rsa_d; /*!< private exponent */ ++ td_u8 *rsa_p; /*!< 1st prime factor */ ++ td_u8 *rsa_q; /*!< 2nd prime factor */ ++ td_u8 *rsa_dp; /*!< D % (P - 1) */ ++ td_u8 *rsa_dq; /*!< D % (Q - 1) */ ++ td_u8 *rsa_qp; /*!< 1 / (Q % P) */ ++ td_u16 rsa_n_len; /* length of public modulus */ ++ td_u16 rsa_e_len; /* length of public exponent */ ++ td_u16 rsa_d_len; /* length of private exponent */ ++ td_u16 rsa_p_len; /* length of 1st prime factor */ ++ td_u16 rsa_q_len; /* length of 2nd prime factor */ ++ td_u16 rsa_dp_len; /* length of D % (P - 1) */ ++ td_u16 rsa_dq_len; /* length of D % (Q - 1) */ ++ td_u16 rsa_qp_len; /* length of 1 / (Q % P) */ ++} cipher_rsa_pri_key_s; ++ ++typedef struct { ++ cipher_rsa_pri_key_s pri_key; ++ td_u32 num_bits; ++ td_u32 exponent; ++} cipher_rsa_key_s; ++ ++typedef enum { ++ CIPHER_TEST_PRINT_PHY = 0x01, ++ CIPHER_TEST_PRINT_VIA, ++ CIPHER_TEST_MEMSET, ++ CIPHER_TEST_MEMCMP, ++ CIPHER_TEST_MEMCPY, ++ CIPHER_TEST_MEMCMP_PHY, ++ CIPHER_TEST_READ_REG, ++ CIPHER_TEST_WRITE_REG, ++ CIPHER_TEST_AES = 0x10, ++ CIPHER_TEST_HMAC, ++ CIPHER_TEST_RSA, ++ CIPHER_TEST_HASH, ++ CIPHER_TEST_DES, ++ CIPHER_TEST_RSA_PRIM, ++ CIPHER_TEST_RSA_KG, ++ CIPHER_TEST_RND, ++ CIPHER_TEST_BUTT, ++} cipher_test_e; ++ ++#ifdef CONFIG_COMPAT ++typedef struct { ++ td_handle ci_handle; ++ td_u32 pkg_num; ++ td_u32 pkg_via; ++} cipher_compat_pkg_s; ++ ++typedef struct { ++ td_u32 input_via; ++ td_u32 output_via; ++ td_u32 data_len; ++ td_u32 rsa_n_via; ++ td_u32 rsa_k_via; ++ td_u16 rsa_n_len; ++ td_u16 rsa_k_len; ++} cipher_compat_rsa_data_s; ++#endif ++ ++typedef struct { ++ ot_cipher_ca_type root_key; ++ ot_cipher_klad_target klad_target; ++ td_u32 clean_key[4]; /* 4 key size */ ++ td_u32 encrypt_key[4]; /* 4 key size */ ++ td_u32 key_len; ++} cipher_klad_key_s; ++ ++td_s32 ot_drv_cipher_create_handle(cipher_handle_s *ci_handle, const td_void *file); ++td_s32 ot_drv_cipher_config_chn_ex(td_handle ci_handle, const cipher_config_ctrl_ex_s *config); ++td_s32 ot_drv_cipher_get_handle_config(cipher_config_ctrl_s *cipher_config); ++td_s32 ot_drv_cipher_get_handle_config_ex(cipher_config_ctrl_ex_s *cipher_config); ++td_s32 ot_drv_cipher_destory_handle(td_handle cipher_chn); ++td_s32 ot_drv_cipher_encrypt(const cipher_data_s *ci_data); ++td_s32 ot_drv_cipher_decrypt(const cipher_data_s *ci_data); ++td_s32 ot_drv_cipher_encrypt_multi(const cipher_pkg_s *pkg); ++td_s32 ot_drv_cipher_decrypt_multi(const cipher_pkg_s *pkg); ++td_s32 ot_drv_cipher_wait_done(cipher_wait_done_s *wait_done); ++td_s32 ot_drv_cipher_hash_wait_done(td_handle handle); ++td_s32 ot_drv_cipher_get_random_number(cipher_rng_s *rng); ++td_s32 ot_drv_cipher_soft_reset(td_void); ++td_s32 ot_drv_cipher_calc_hash_init(const cipher_hash_data_s *cipher_hash_data); ++td_s32 ot_drv_cipher_calc_hash_update(cipher_hash_data_s *cipher_hash_data); ++td_s32 ot_drv_cipher_calc_hash_final(cipher_hash_data_s *cipher_hash_data); ++td_s32 ot_drv_cipher_get_tag(cipher_tag_s *tag); ++td_s32 ot_drv_cipher_calc_rsa(cipher_rsa_data_s *cipher_rsa_data); ++td_s32 ot_drv_cipher_klad_encrypt_key(cipher_klad_key_s *klad_key); ++ ++td_void ot_drv_cipher_suspend(td_void); ++td_s32 ot_drv_cipher_resume(td_void); ++ ++#ifdef __cplusplus ++} ++#endif /* __cplusplus */ ++ ++#endif /* OT_DRV_CIPHER_H */ ++ +diff --git a/product/security_subsys/cipher/v2/drv/platform/cipher_adapt.c b/product/security_subsys/cipher/v2/drv/platform/cipher_adapt.c +new file mode 100644 +index 0000000..c23d534 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/platform/cipher_adapt.c +@@ -0,0 +1,77 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "cipher_adapt.h" ++ ++td_s32 cipher_mmz_alloc_remap(td_char *name, cipher_mmz_buf_t *cipher_mmz) ++{ ++ if (cipher_mmz->mmz_size == 0) { ++ ot_err_cipher("Error: length of malloc is invalid!\n"); ++ return TD_FAILURE; ++ } ++ ++ cipher_mmz->start_phy_addr = (td_size_t)(uintptr_t)memalign(ARCH_DMA_MINALIGN, cipher_mmz->mmz_size); ++ ++ if (cipher_mmz->start_phy_addr == 0) { ++ ot_err_cipher("Error: Get phyaddr for cipher input failed!\n"); ++ return TD_FAILURE; ++ } ++ cipher_mmz->start_vir_addr = (td_u8 *)(uintptr_t)cipher_mmz->start_phy_addr; ++ ++ return TD_SUCCESS; ++} ++ ++td_void cipher_mmz_release_unmap(cipher_mmz_buf_t *cipher_mmz) ++{ ++ if (cipher_mmz->start_phy_addr > 0) { ++ free(cipher_mmz->start_vir_addr); ++ cipher_mmz->start_phy_addr = 0; ++ cipher_mmz->start_vir_addr = NULL; ++ } ++} ++ ++td_s32 cipher_mmz_map(cipher_mmz_buf_t *cipher_mmz) ++{ ++ cipher_mmz->start_vir_addr = (td_u8 *)(uintptr_t)cipher_mmz->start_phy_addr; ++ ++ return TD_SUCCESS; ++} ++ ++td_void cipher_mmz_unmap(const cipher_mmz_buf_t *cipher_mmz) ++{ ++} ++ ++/************************* SYSTEM API ************************/ ++void hex2str(char buf[2], td_u8 val) /* 2 buf size */ ++{ ++ td_u8 high, low; ++ ++ high = (val >> 4) & 0x0F; /* 4 */ ++ low = val & 0x0F; ++ ++ if (high <= 9) /* 9 */ ++ buf[0] = high + '0'; ++ else ++ buf[0] = (high - 0x0A) + 'A'; ++ ++ if (low <= 9) /* 9 */ ++ buf[1] = low + '0'; ++ else ++ buf[1] = (low - 0x0A) + 'A'; ++} +diff --git a/product/security_subsys/cipher/v2/drv/platform/cipher_adapt.h b/product/security_subsys/cipher/v2/drv/platform/cipher_adapt.h +new file mode 100644 +index 0000000..24db6f7 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/platform/cipher_adapt.h +@@ -0,0 +1,96 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef CIPHER_ADAPT_H ++#define CIPHER_ADAPT_H ++ ++#ifndef OT_MINIBOOT_SUPPORT ++#include ++#else ++#include "delay.h" ++#include "malloc.h" ++#include "string.h" ++#include "stdio.h" ++#endif ++#include "malloc.h" ++ ++#include ++#include "ot_type.h" ++#include "ot_drv_cipher.h" ++#include "cipher_config.h" ++#include "cipher_osal.h" ++ ++/**************************** M A C R O ****************************/ ++#define OT_ERR_CIPHER_NOT_INIT (td_s32)(0x804D0001) ++#define OT_ERR_CIPHER_INVALID_HANDLE (td_s32)(0x804D0002) ++#define OT_ERR_CIPHER_INVALID_POINT (td_s32)(0x804D0003) ++#define OT_ERR_CIPHER_INVALID_PARAM (td_s32)(0x804D0004) ++#define OT_ERR_CIPHER_FAILED_INIT (td_s32)(0x804D0005) ++#define OT_ERR_CIPHER_FAILED_GETHANDLE (td_s32)(0x804D0006) ++#define OT_ERR_CIPHER_FAILED_RELEASEHANDLE (td_s32)(0x804D0007) ++#define OT_ERR_CIPHER_FAILED_CONFIGAES (td_s32)(0x804D0008) ++#define OT_ERR_CIPHER_FAILED_CONFIGDES (td_s32)(0x804D0009) ++#define OT_ERR_CIPHER_FAILED_ENCRYPT (td_s32)(0x804D000A) ++#define OT_ERR_CIPHER_FAILED_DECRYPT (td_s32)(0x804D000B) ++#define OT_ERR_CIPHER_BUSY (td_s32)(0x804D000C) ++#define OT_ERR_CIPHER_NO_AVAILABLE_RNG (td_s32)(0x804D000D) ++ ++#define OT_ID_CIPHER 100 ++ ++#define CIPHER_IOR _IOWR ++#define CIPHER_IOW _IOW ++#define CIPHER_IOWR _IOWR ++ ++#define CIPHER_IOC_DIR _IOC_DIR ++#define CIPHER_IOC_TYPE _IOC_TYPE ++#define CIPHER_IOC_NR _IOC_NR ++#define CIPHER_IOC_SIZE _IOC_SIZE ++#define u32_to_point(addr) ((td_void*)((td_size_t)(addr))) ++#define point_to_u32(addr) ((td_u32)((td_size_t)(addr))) ++ ++#define hal_cipher_read_reg(addr, val) (*(val) = readl(addr)) ++#define hal_cipher_write_reg(addr, val) writel(val, addr) ++ ++#define hal_set_bit(src, bit) ((src) |= (1 << (bit))) ++#define hal_clear_bit(src, bit) ((src) &= ~(1 << (bit))) ++ ++/**************************** S T D L I B ****************************/ ++#define cipher_ioremap_nocache(addr, size) (td_void*)(addr) ++#define cipher_iounmap(x) ++ ++#define CIPHER_QUEUE_HEAD td_void * ++#define cipher_queue_init(x) ++#define cipher_queue_wait_up(x) ++#define cipher_queue_wait_timeout(head, con, time) ++ ++#define cipher_request_irq(irq, func, name) ++#define cipher_free_irq(irq, name) ++#define CRYPTO_IRQRETURN_T td_s32 ++#define CIPHER_IRQ_HANDLED 1 ++ ++#define cipher_udelay(usec) udelay(usec) ++ ++void hex2str(char buf[2], td_u8 val); /* 2 buf size */ ++ ++td_s32 cipher_mmz_alloc_remap(td_char *name, cipher_mmz_buf_t *cipher_mmz); ++td_void cipher_mmz_release_unmap(cipher_mmz_buf_t *cipher_mmz); ++td_s32 cipher_mmz_map(cipher_mmz_buf_t *cipher_mmz); ++td_void cipher_mmz_unmap(const cipher_mmz_buf_t *cipher_mmz); ++ ++#endif /* CIPHER_ADAPT_H */ +diff --git a/product/security_subsys/cipher/v2/drv/platform/cipher_config.h b/product/security_subsys/cipher/v2/drv/platform/cipher_config.h +new file mode 100644 +index 0000000..ffec835 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/platform/cipher_config.h +@@ -0,0 +1,139 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef CIPHER_CONFIG_H ++#define CIPHER_CONFIG_H ++ ++#define RSA_ENABLE ++#define OTP_SUPPORT ++#define CIPHER_HASH_SUPPORT ++#define CIPHER_EFUSE_SUPPORT ++#define CIPHER_KLAD_SUPPORT ++ ++#define CIPHER_IRQ_NUMBER 59 ++ ++#if defined(CHIP_TYPE_SS919V100) ++#define CIPHER_RNG_REG_BASE_ADDR_PHY 0x10230000 ++#define CIPHER_ENFUSE_REG_BASE_ADDR_PHY (0x10250000) ++#define CIPHER_CIPHER_REG_BASE_ADDR_PHY (0x10200000) ++#define CIPHER_RSA_REG_BASE_ADDR_PHY (0x10220000) ++ ++#define CIPHER_RSA_CRG_ADDR_PHY (0x12010194) ++#define RSA_CRG_CLOCK_BIT (0x01 << 15) ++#define RSA_CRG_RESET_BIT (0x01 << 14) ++#define CIPHER_SPACC_CRG_ADDR_PHY (0x1201016C) ++#define SPACC_CRG_CLOCK_BIT (0x01 << 1) ++#define SPACC_CRG_RESET_BIT (0x01 << 0) ++#define CIPHER_RNG_CRG_ADDR_PHY (0x12010194) ++#define RNG_CRG_CLOCK_BIT (0x01 << 13) ++#define RNG_CRG_RESET_BIT (0x01 << 12) ++ ++#define CIPHER_KLAD_REG_BASE_ADDR_PHY (0x10210000) ++#define CIPHER_OTP_REG_BASE_ADDR_PHY (0x10240000) ++#define CIPHER_KLAD_CRG_ADDR_PHY (0x12010194) ++ ++#define KLAD_CRG_CLOCK_BIT (0x01 << 11) ++#define KLAD_CRG_RESET_BIT (0x01 << 10) ++#elif defined(CHIP_TYPE_SS918V100) ++#define CIPHER_RNG_REG_BASE_ADDR_PHY (0x04090000) ++#define CIPHER_CIPHER_REG_BASE_ADDR_PHY (0x04060000) ++#define CIPHER_RSA_REG_BASE_ADDR_PHY (0x04088000) ++ ++#define CIPHER_RSA_CRG_ADDR_PHY (0x04510194) ++#define RSA_CRG_CLOCK_BIT (0x01 << 23) ++#define RSA_CRG_RESET_BIT (0x01 << 22) ++#define CIPHER_SPACC_CRG_ADDR_PHY (0x0451016c) ++#define SPACC_CRG_CLOCK_BIT (0x01 << 1) ++#define SPACC_CRG_RESET_BIT (0x01 << 0) ++#define CIPHER_RNG_CRG_ADDR_PHY (0x04510194) ++#define RNG_CRG_CLOCK_BIT (0x01 << 13) ++#define RNG_CRG_RESET_BIT (0x01 << 12) ++ ++#define CIPHER_KLAD_REG_BASE_ADDR_PHY (0x04070000) ++#define CIPHER_OTP_REG_BASE_ADDR_PHY (0x040A0000) ++#define CIPHER_KLAD_CRG_ADDR_PHY (0x04510194) ++ ++#define KLAD_CRG_CLOCK_BIT (0x01 << 11) ++#define KLAD_CRG_RESET_BIT (0x01 << 10) ++#elif defined(CHIP_TYPE_SS812V100) ++#define CIPHER_RNG_REG_BASE_ADDR_PHY (0x10090000) ++#define CIPHER_CIPHER_REG_BASE_ADDR_PHY (0x100C0000) ++#define CIPHER_RSA_REG_BASE_ADDR_PHY (0x100D0000) ++ ++#define CIPHER_RSA_CRG_ADDR_PHY (0x120101A0) ++#define RSA_CRG_CLOCK_BIT (0x01 << 7) ++#define RSA_CRG_RESET_BIT (0x01 << 6) ++#define CIPHER_SPACC_CRG_ADDR_PHY (0x120101A0) ++#define SPACC_CRG_CLOCK_BIT (0x01 << 9) ++#define SPACC_CRG_RESET_BIT (0x01 << 8) ++#define CIPHER_RNG_CRG_ADDR_PHY (0x120101A0) ++#define RNG_CRG_CLOCK_BIT (0x01 << 3) ++#define RNG_CRG_RESET_BIT (0x01 << 2) ++ ++#define CIPHER_KLAD_REG_BASE_ADDR_PHY (0x10070000) ++#define CIPHER_OTP_REG_BASE_ADDR_PHY (0x100B0000) ++#define CIPHER_KLAD_CRG_ADDR_PHY (0x120101A0) ++ ++#define KLAD_CRG_CLOCK_BIT (0x01 << 1) ++#define KLAD_CRG_RESET_BIT (0x01 << 0) ++#elif defined(CHIP_TYPE_SS101V200) ++#define CIPHER_RNG_REG_BASE_ADDR_PHY (0x10080000) ++#define CIPHER_CIPHER_REG_BASE_ADDR_PHY (0x10050000) ++#define CIPHER_RSA_REG_BASE_ADDR_PHY (0x10070000) ++ ++#define CIPHER_RSA_CRG_ADDR_PHY (0x120101A0) ++#define RSA_CRG_CLOCK_BIT (0x01 << 5) ++#define RSA_CRG_RESET_BIT (0x01 << 4) ++#define CIPHER_SPACC_CRG_ADDR_PHY (0x120101A0) ++#define SPACC_CRG_CLOCK_BIT (0x01 << 9) ++#define SPACC_CRG_RESET_BIT (0x01 << 8) ++#define CIPHER_RNG_CRG_ADDR_PHY (0x120101A0) ++#define RNG_CRG_CLOCK_BIT (0x01 << 3) ++#define RNG_CRG_RESET_BIT (0x01 << 2) ++ ++#define CIPHER_KLAD_REG_BASE_ADDR_PHY (0x10060000) ++#define CIPHER_OTP_REG_BASE_ADDR_PHY (0x10090000) ++#define CIPHER_KLAD_CRG_ADDR_PHY (0x120101A0) ++ ++#define KLAD_CRG_CLOCK_BIT (0x01 << 1) ++#define KLAD_CRG_RESET_BIT (0x01 << 0) ++#elif (defined(CHIP_TYPE_SS528V100) || defined(CHIP_TYPE_SS524V100)) ++#define CIPHER_RNG_REG_BASE_ADDR_PHY (0x10130000) ++#define CIPHER_CIPHER_REG_BASE_ADDR_PHY (0x10100000) ++#define CIPHER_RSA_REG_BASE_ADDR_PHY (0x10120000) ++ ++#define CIPHER_RSA_CRG_ADDR_PHY (0x11012D00) ++#define RSA_CRG_CLOCK_BIT (0x01 << 4) ++#define RSA_CRG_RESET_BIT (0x01 << 0) ++#define CIPHER_SPACC_CRG_ADDR_PHY (0x11012C80) ++#define SPACC_CRG_CLOCK_BIT (0x01 << 4) ++#define SPACC_CRG_RESET_BIT (0x01 << 0) ++#define CIPHER_RNG_CRG_ADDR_PHY (0x11012D80) ++#define RNG_CRG_CLOCK_BIT (0x01 << 4) ++#define RNG_CRG_RESET_BIT (0x01 << 0) ++ ++#define CIPHER_KLAD_REG_BASE_ADDR_PHY (0x10110000) ++#define CIPHER_OTP_REG_BASE_ADDR_PHY (0x10200000) ++#define CIPHER_KLAD_CRG_ADDR_PHY (0x11012CC0) ++ ++#define KLAD_CRG_CLOCK_BIT (0x01 << 4) ++#define KLAD_CRG_RESET_BIT (0x01 << 0) ++#endif ++ ++#endif /* CIPHER_CONFIG_H */ +diff --git a/product/security_subsys/cipher/v2/drv/rng/drv_rng.c b/product/security_subsys/cipher/v2/drv/rng/drv_rng.c +new file mode 100644 +index 0000000..e9879a2 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/rng/drv_rng.c +@@ -0,0 +1,107 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_rng.h" ++#include "cipher_adapt.h" ++ ++static td_void *g_rng_reg_base; ++ ++#define REG_RNG_BASE_ADDR g_rng_reg_base ++#define SEC_COM_TRNG_CTRL (REG_RNG_BASE_ADDR + 0x200) ++#define SEC_COM_TRNG_FIFO_DATA (REG_RNG_BASE_ADDR + 0x204) ++#define SEC_COM_TRNG_DATA_ST (REG_RNG_BASE_ADDR + 0x208) ++ ++td_s32 drv_rng_init(td_void) ++{ ++ td_u32 rng_stat = 0; ++ ++ g_rng_reg_base = cipher_ioremap_nocache(CIPHER_RNG_REG_BASE_ADDR_PHY, 0x1000); ++ if (g_rng_reg_base == TD_NULL) { ++ ot_err_cipher("ioremap_nocache sha2 Reg failed\n"); ++ return TD_FAILURE; ++ } ++ ++ hal_cipher_read_reg(CIPHER_RNG_CRG_ADDR_PHY, &rng_stat); ++ rng_stat |= RNG_CRG_CLOCK_BIT; ++ rng_stat &= ~RNG_CRG_RESET_BIT; ++ hal_cipher_write_reg(CIPHER_RNG_CRG_ADDR_PHY, rng_stat); ++ ++ return TD_SUCCESS; ++} ++ ++td_void drv_rng_deinit(td_void) ++{ ++ if (g_rng_reg_base != TD_NULL) { ++ cipher_iounmap(g_rng_reg_base); ++ g_rng_reg_base = TD_NULL; ++ } ++} ++ ++static td_s32 drv_cipher_get_random_number(cipher_rng_s *rng) ++{ ++ td_u32 rng_stat = 0; ++ td_u32 time_out = 0; ++ ++ if (rng->time_out_us == 0) { ++ /* low 3bit(RNG_data_count[2:0]), indicate how many RNGs in the fifo is available now */ ++ hal_cipher_read_reg(SEC_COM_TRNG_DATA_ST, &rng_stat); ++ if (((rng_stat >> 8) & 0x3F) <= 0) /* 8 right shift */ ++ return OT_ERR_CIPHER_NO_AVAILABLE_RNG; ++ } else { ++ while (time_out++ < rng->time_out_us) { ++ /* low 3bit(RNG_data_count[2:0]), indicate how many RNGs in the fifo is available now */ ++ hal_cipher_read_reg(SEC_COM_TRNG_DATA_ST, &rng_stat); ++ if (((rng_stat >> 8) & 0x3F) > 0) /* 8 right shift */ ++ break; ++ } ++ ++ if (time_out >= rng->time_out_us) ++ return OT_ERR_CIPHER_NO_AVAILABLE_RNG; ++ } ++ ++ hal_cipher_read_reg(SEC_COM_TRNG_FIFO_DATA, &rng->ci_rng); ++ ++ return TD_SUCCESS; ++} ++ ++td_u32 drv_cipher_rand(td_void) ++{ ++ cipher_rng_s rng; ++ ++ rng.time_out_us = -1; ++ if (drv_cipher_get_random_number(&rng) != TD_SUCCESS) ++ ot_err_cipher("Get random number failed!\n"); ++ ++ return rng.ci_rng; ++} ++ ++td_s32 ot_drv_cipher_get_random_number(cipher_rng_s *rng) ++{ ++ td_s32 ret; ++ ++ if (rng == NULL) { ++ ot_err_cipher("Invalid params!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = drv_cipher_get_random_number(rng); ++ ++ return ret; ++} ++ +diff --git a/product/security_subsys/cipher/v2/drv/rng/drv_rng.h b/product/security_subsys/cipher/v2/drv/rng/drv_rng.h +new file mode 100644 +index 0000000..a42ca01 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/rng/drv_rng.h +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_RNG_H ++#define DRV_RNG_H ++ ++/* add include here */ ++#include "ot_drv_cipher.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/***************************** Macro Definition ******************************/ ++td_u32 drv_cipher_rand(td_void); ++td_s32 drv_rng_init(td_void); ++td_void drv_rng_deinit(td_void); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* DRV_RNG_H */ +diff --git a/product/security_subsys/cipher/v2/drv/rsa/drv_rsa.c b/product/security_subsys/cipher/v2/drv/rsa/drv_rsa.c +new file mode 100644 +index 0000000..eaa7a74 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/rsa/drv_rsa.c +@@ -0,0 +1,626 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_klad.h" ++#include "cipher_adapt.h" ++#include "drv_rng.h" ++#include "drv_klad.h" ++ ++#ifdef RSA_ENABLE ++ ++CIPHER_MUTEX g_rsa_mutex_kernel; ++static td_void *g_rsa_reg_base = TD_NULL; ++static td_u32 g_rsa_done = TD_FALSE; ++ ++#define cipher_rsa_return_invalid_param(param) \ ++ do { \ ++ if (param) { \ ++ ot_err_cipher("Invalid params!\n"); \ ++ return OT_ERR_CIPHER_INVALID_PARAM; \ ++ } \ ++ } while (0) ++ ++#define RSA_INTERRUPT_ENABLE ++#define RSA_IRQ_NUMBER 136 ++ ++#define CIPHER_RSA_REG_BASE_RSA g_rsa_reg_base ++#define SEC_RSA_BUSY_REG (CIPHER_RSA_REG_BASE_RSA + 0x50) ++#define SEC_RSA_MOD_REG (CIPHER_RSA_REG_BASE_RSA + 0x54) ++#define SEC_RSA_WSEC_REG (CIPHER_RSA_REG_BASE_RSA + 0x58) ++#define SEC_RSA_WDAT_REG (CIPHER_RSA_REG_BASE_RSA + 0x5c) ++#define SEC_RSA_RPKT_REG (CIPHER_RSA_REG_BASE_RSA + 0x60) ++#define SEC_RSA_RRSLT_REG (CIPHER_RSA_REG_BASE_RSA + 0x64) ++#define SEC_RSA_START_REG (CIPHER_RSA_REG_BASE_RSA + 0x68) ++#define SEC_RSA_ADDR_REG (CIPHER_RSA_REG_BASE_RSA + 0x6C) ++#define SEC_RSA_ERROR_REG (CIPHER_RSA_REG_BASE_RSA + 0x70) ++#define SEC_RSA_CRC16_REG (CIPHER_RSA_REG_BASE_RSA + 0x74) ++#define SEC_RSA_KEY_RANDOM_1 (CIPHER_RSA_REG_BASE_RSA + 0x7c) ++#define SEC_RSA_INT_EN (CIPHER_RSA_REG_BASE_RSA + 0x80) ++#define SEC_RSA_INT_STATUS (CIPHER_RSA_REG_BASE_RSA + 0x84) ++#define SEC_RSA_INT_RAW (CIPHER_RSA_REG_BASE_RSA + 0x88) ++#define SEC_RSA_INT_ERR_CLR (CIPHER_RSA_REG_BASE_RSA + 0x8c) ++#define SEC_RSA_KEY_RANDOM_2 (CIPHER_RSA_REG_BASE_RSA + 0x94) ++#define SEC_RSA_VERSION (CIPHER_RSA_REG_BASE_RSA + 0x90) ++ ++#define RSA_DATA_CLR (7 << 4) ++#define RSA_DATA_CLR_KEY (1 << 4) ++#define RSA_DATA_CLR_INPUT (2 << 4) ++#define RSA_DATA_CLR_OUTPUT (4 << 4) ++#define RSA_MOD_SEL (3 << 0) ++#define RSA_MOD_SEL_OPT (0 << 0) ++#define RSA_MOD_SEL_KEY_UPDATA (1 << 0) ++#define RSA_MOD_SEL_RAM_CLAER (2 << 0) ++#define RSA_MOD_SEL_CRC16 (3 << 0) ++#define RSA_BUSY (1 << 0) ++#define RSA_START (1 << 0) ++ ++#define RSA_RTY_CNT 500000 ++#define RSA_TIME_OUT 1000 ++ ++#define RSA_RETRY_CNT 3 ++ ++#define CRC16_POLYNOMIAL 0x1021 ++ ++typedef enum { ++ CIPHER_RSA_DATA_TYPE_CONTEXT, ++ CIPHER_RSA_DATA_TYPE_MODULE, ++ CIPHER_RSA_DATA_TYPE_KEY, ++} cipher_rsa_data_type_e; ++ ++typedef enum { ++ CIPHER_RSA_KEY_WIDTH_1K = 0x00, ++ CIPHER_RSA_KEY_WIDTH_2K = 0x01, ++ CIPHER_RSA_KEY_WIDTH_4K = 0x02, ++ CIPHER_RSA_KEY_WIDTH_3K = 0x03, ++ CIPHER_RSA_KEY_WIDTH_BUTT = 0xff, ++} cipher_rsa_key_width_e; ++ ++static td_void hal_rsa_start(td_void) ++{ ++ hal_cipher_write_reg(SEC_RSA_START_REG, 0x05); ++} ++ ++static td_s32 hal_rsa_wait_free(td_void) ++{ ++ td_u32 value; ++ td_u32 try_count = 0; ++ ++ do { ++ hal_cipher_read_reg(SEC_RSA_BUSY_REG, &value); ++ if ((value & RSA_BUSY) == 0) ++ return TD_SUCCESS; ++ try_count++; ++ cipher_udelay(10); /* 10us */ ++ } while (try_count < RSA_RTY_CNT); ++ ++ return TD_FAILURE; ++} ++ ++static td_void hal_rsa_clear_ram(td_void) ++{ ++ td_u32 value; ++ ++ hal_cipher_read_reg(SEC_RSA_MOD_REG, &value); ++ value &= 0x0c; ++ value |= RSA_DATA_CLR_INPUT | RSA_DATA_CLR_OUTPUT | RSA_DATA_CLR_KEY | RSA_MOD_SEL_RAM_CLAER; ++ hal_cipher_write_reg(SEC_RSA_MOD_REG, value); ++} ++ ++static td_void hal_rsa_config_mode(cipher_rsa_key_width_e ken_width) ++{ ++ td_u32 value; ++ ++ value = ((td_u32)ken_width << 2) | RSA_MOD_SEL_OPT; /* 2 left shift */ ++ hal_cipher_write_reg(SEC_RSA_MOD_REG, value); ++} ++ ++static td_void hal_rsa_write_data(cipher_rsa_data_type_e data_type, ++ const td_u8 *data, td_u32 data_len, td_u32 length, const td_u32 random[2]) /* 2 random size */ ++{ ++ td_u32 *reg = TD_NULL; ++ const td_u8 *pos = TD_NULL; ++ td_u32 i, value; ++ td_bool id = 0; ++ ++ if (data_type == CIPHER_RSA_DATA_TYPE_CONTEXT) { ++ reg = SEC_RSA_WDAT_REG; ++ } else { ++ reg = SEC_RSA_WSEC_REG; ++ } ++ ++ pos = data; ++ for (i = 0; i < length; i += 4) { /* 4 groups */ ++ value = (td_u32)pos[0]; ++ value |= ((td_u32)pos[1]) << 8; /* 1 index, 8 left shift */ ++ value |= ((td_u32)pos[2]) << 16; /* 2 index, 16 left shift */ ++ value |= ((td_u32)pos[3]) << 24; /* 3 index, 24 left shift */ ++ if (data_type != CIPHER_RSA_DATA_TYPE_CONTEXT) { ++ value ^= random[id]; ++ } ++ ++ hal_cipher_write_reg(reg, value); ++ pos += 4; /* 4 groups */ ++ id = (td_u32)id ^ 0x01; ++ } ++} ++ ++static td_void hal_rsa_read_data(td_u8 *data, td_u32 data_len, td_u32 klen) ++{ ++ td_u32 value; ++ td_u8 *pos = TD_NULL; ++ td_u32 i; ++ ++ pos = data; ++ for (i = 0; i < klen; i += 4) { /* 4 groups */ ++ hal_cipher_read_reg(SEC_RSA_RRSLT_REG, &value); ++ pos[0] = (td_u8)(value & 0xFF); ++ pos[1] = (td_u8)((value >> 8) & 0xFF); /* 1 index, 8 right shift */ ++ pos[2] = (td_u8)((value >> 16) & 0xFF); /* 2 index, 16 right shift */ ++ pos[3] = (td_u8)((value >> 24) & 0xFF); /* 3 index, 24 right shift */ ++ pos += 4; /* 4 groups */ ++ } ++} ++ ++static td_u32 hal_rsa_get_error_code(td_void) ++{ ++ td_u32 value; ++ ++ hal_cipher_read_reg(SEC_RSA_ERROR_REG, &value); ++ ++ return value; ++} ++ ++static td_void hal_rsa_disable_int(td_void) ++{ ++ (td_void)hal_cipher_write_reg(SEC_RSA_INT_EN, 0x00); ++} ++ ++#ifdef RSA_RAND_MASK ++ ++static td_u16 g_crc_table[256]; /* 256 table size */ ++ ++static td_void drv_rsa_crc16_init(td_void) ++{ ++ td_u16 remainder; ++ td_u16 n, m; ++ td_u16 *table = g_crc_table; ++ ++ for (n = 0; n < 256; n++) { /* 256 */ ++ remainder = (td_u16)n << 8; /* 8 left shift */ ++ for (m = 8; m > 0; m--) { /* 8 */ ++ if (remainder & 0x8000) ++ remainder = (remainder << 1) ^ CRC16_POLYNOMIAL; ++ else ++ remainder = (remainder << 1); ++ } ++ *(table + n) = remainder; ++ } ++} ++ ++static td_u16 drv_rsa_crc16_block(td_u16 crc, td_u8 block[8], td_u8 random[8]) /* 8 */ ++{ ++ td_u8 i, j; ++ td_u8 val; ++ ++ for (i = 0; i < 2; i++) { /* 2 */ ++ for (j = 0; j < 4; j++) { /* 4 */ ++ val = block[i * 4 + 3 - j] ^ random[i * 4 + 3 - j]; /* 4, 3 */ ++ crc = (crc << 8) ^ g_crc_table[((crc >> 8) ^ val) & 0xFF]; /* 8 right shift */ ++ } ++ } ++ ++ return crc; ++} ++ ++static td_u16 drv_rsa_key_crc(const td_u8 *rsa_n, const td_u8 *rsa_k, td_u32 klen, td_u32 random[2]) /* 2 */ ++{ ++ td_u32 i; ++ td_u16 crc = 0; ++ ++ for (i = 0; i < klen; i += 8) /* 8 */ ++ crc = drv_rsa_crc16_block(crc, rsa_n + i, (td_u8*)random); ++ ++ for (i = 0; i < klen; i += 8) /* 8 */ ++ crc = drv_rsa_crc16_block(crc, rsa_k + i, (td_u8*)random); ++ ++ return crc; ++} ++#endif ++ ++CIPHER_QUEUE_HEAD g_rsa_wait_queue; ++#ifdef INT_ENABLE ++static CRYPTO_IRQRETURN_T drv_rsa_isr(td_s32 irq, td_void *dev_id) ++{ ++ td_u32 int_stat; ++ ++ int_stat = hal_rsa_get_int(); ++ ++ ot_info_cipher("RSA INT: 0x%x\n", int_stat); ++ ++ if (int_stat & 0x01) { ++ g_rsa_done = TD_TRUE; ++ ot_info_cipher("RSA Done\n"); ++ cipher_queue_wait_up(&g_rsa_wait_queue); ++ } ++ ++ hal_rsa_clr_int(); ++ ++ return CIPHER_IRQ_HANDLED; ++} ++#endif ++ ++td_s32 drv_rsa_init(td_void) ++{ ++ td_u32 rsa_stat = 0; ++ td_u32 rng_stat = 0; ++#ifdef INT_ENABLE ++ td_s32 ret; ++#endif ++ ++ cipher_mutex_init(&g_rsa_mutex_kernel); ++ cipher_queue_init(&g_rsa_wait_queue); ++ ++ /* rng reset and clock */ ++ hal_cipher_read_reg(CIPHER_RNG_CRG_ADDR_PHY, &rng_stat); ++ rng_stat |= RNG_CRG_CLOCK_BIT; ++ rng_stat &= ~RNG_CRG_RESET_BIT; ++ hal_cipher_write_reg(CIPHER_RNG_CRG_ADDR_PHY, rng_stat); ++ cipher_udelay(10); /* 10us */ ++ ++ /* rsa reset and clock */ ++ hal_cipher_read_reg(CIPHER_RSA_CRG_ADDR_PHY, &rsa_stat); ++ rsa_stat |= RSA_CRG_CLOCK_BIT; ++ rsa_stat |= RSA_CRG_RESET_BIT; ++ hal_cipher_write_reg(CIPHER_RSA_CRG_ADDR_PHY, rsa_stat); ++ cipher_udelay(10); /* 10us */ ++ ++ /* rsa cancel reset */ ++ rsa_stat &= ~RSA_CRG_RESET_BIT; ++ hal_cipher_write_reg(CIPHER_RSA_CRG_ADDR_PHY, rsa_stat); ++ ++ g_rsa_reg_base = cipher_ioremap_nocache(CIPHER_RSA_REG_BASE_ADDR_PHY, 0x1000); ++ if (g_rsa_reg_base == TD_NULL) { ++ ot_err_cipher("ioremap_nocache rsa Reg failed\n"); ++ return TD_FAILURE; ++ } ++ ++#ifdef INT_ENABLE ++ /* request irq */ ++ ret = cipher_request_irq(RSA_IRQ_NUMBER, drv_rsa_isr, "rsa"); ++ if (ret != TD_SUCCESS) { ++ hal_rsa_disable_int(); ++ ot_err_cipher("Irq request failure, ret=%#x.\n", ret); ++ return TD_FAILURE; ++ } ++ hal_rsa_enable_int(); ++#endif ++ ++#ifdef RSA_RAND_MASK ++ drv_rsa_crc16_init(); ++#endif ++ ++ return TD_SUCCESS; ++} ++ ++td_void drv_rsa_deinit(td_void) ++{ ++ hal_rsa_disable_int(); ++ ++#ifdef INT_ENABLE ++ cipher_free_irq(RSA_IRQ_NUMBER, "rsa"); ++#endif ++ ++ if (g_rsa_reg_base != TD_NULL) { ++ cipher_iounmap(g_rsa_reg_base); ++ g_rsa_reg_base = TD_NULL; ++ } ++} ++ ++static td_s32 drv_rsa_wait_done(td_void) ++{ ++#ifdef INT_ENABLE ++ if (cipher_queue_wait_timeout(&g_rsa_wait_queue, &g_rsa_done, RSA_TIME_OUT) == 0) { ++ ot_err_cipher("RSA time out! \n"); ++ return TD_FAILURE; ++ } ++ ++ return TD_SUCCESS; ++#else ++ return hal_rsa_wait_free(); ++#endif ++} ++ ++static td_s32 drv_cipher_check_rsa_data(const td_u8 *rsa_n, const td_u8 *rsa_e, const td_u8 *rsa_mc, td_u32 length) ++{ ++ td_u32 i; ++ ++ /* formula: rsa_mc > 0 */ ++ for (i = 0; i < length; i++) { ++ if (rsa_mc[i] > 0) ++ break; ++ } ++ if (i >= length) { ++ ot_err_cipher("RSA M/C is zero, error!\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ /* formula: rsa_mc < rsa_n */ ++ for (i = 0; i < length; i++) { ++ if (rsa_mc[i] < rsa_n[i]) ++ break; ++ } ++ if (i >= length) { ++ ot_err_cipher("RSA M/C is larger than rsa_n, error!\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ /* formula: rsa_e >= 1 */ ++ for (i = 0; i < length; i++) { ++ if (rsa_e[i] > 0) ++ break; ++ } ++ if (i >= length) { ++ ot_err_cipher("RSA D/rsa_e is zero, error!\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_clear_rsa_ram(td_void) ++{ ++ if (hal_rsa_wait_free() != TD_SUCCESS) { ++ ot_err_cipher("RSA is busy and timeout,error!\n"); ++ return TD_FAILURE; ++ } ++ ++ g_rsa_done = TD_FALSE; ++ ++ hal_rsa_clear_ram(); ++ hal_rsa_start(); ++ ++ if (drv_rsa_wait_done() != TD_SUCCESS) { ++ ot_err_cipher("RSA is busy and timeout,error!\n"); ++ return TD_FAILURE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_u8 g_rsa_n[CIPHER_MAX_RSA_KEY_LEN]; ++static td_u8 g_rsa_k[CIPHER_MAX_RSA_KEY_LEN]; ++static td_u8 g_rsa_m[CIPHER_MAX_RSA_KEY_LEN]; ++ ++static td_void drv_rsa_rand_mask(const cipher_rsa_data_s *rsa_data, ++ td_u32 key_len, ++ td_u32 *random) ++{ ++#ifdef RSA_RAND_MASK ++ td_u16 crc; ++ ++ random[0] = drv_cipher_rand(); ++ random[1] = drv_cipher_rand(); ++ crc = drv_rsa_key_crc(rsa_data->rsa_n, rsa_data->rsa_k, key_len, random); ++ ot_info_cipher("CRC16: 0x%x\n", crc); ++ hal_rsa_set_random(random); ++ hal_rsa_set_crc(crc); ++#endif ++} ++ ++static td_s32 drv_rsa_cipher_klad(const cipher_rsa_data_s *rsa_data, ++ td_u32 key_len, ++ const td_u32 *random) ++{ ++ td_s32 ret = TD_SUCCESS; ++#ifdef CIPHER_KLAD_SUPPORT ++ if (rsa_data->ca_type != OT_CIPHER_KEY_SRC_USER) { ++ drv_cipher_klad_load_key(0, rsa_data->ca_type, ++ OT_CIPHER_KLAD_TARGET_RSA, rsa_data->rsa_k, rsa_data->rsa_k_len); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("drv_cipher_klad_load_key, error!\n"); ++ return ret; ++ } ++ } else { ++ hal_rsa_write_data(CIPHER_RSA_DATA_TYPE_KEY, rsa_data->rsa_k, ++ rsa_data->rsa_n_len, key_len, random); ++ } ++#else ++ hal_rsa_write_data(CIPHER_RSA_DATA_TYPE_KEY, rsa_data->rsa_k, ++ rsa_data->rsa_n_len, key_len, random); ++#endif ++ return ret; ++} ++ ++static td_s32 drv_rsa_key_info(const cipher_rsa_data_s *rsa_data, ++ td_u32 *key_len, cipher_rsa_key_width_e *key_width) ++{ ++ td_s32 ret; ++ td_u8 *p = TD_NULL; ++ ++ /* Only support the key width of 1024, 2048 and 4096 */ ++ if (rsa_data->rsa_n_len <= 128) { /* key n size 128 */ ++ *key_len = 128; /* key n size 128 */ ++ *key_width = CIPHER_RSA_KEY_WIDTH_1K; ++ } else if (rsa_data->rsa_n_len <= 256) { /* key n size 256 */ ++ *key_len = 256; /* key n size 256 */ ++ *key_width = CIPHER_RSA_KEY_WIDTH_2K; ++ } else if (rsa_data->rsa_n_len <= 384) { /* key n size 384 */ ++ *key_len = 384; /* key n size 384 */ ++ *key_width = CIPHER_RSA_KEY_WIDTH_3K; ++ } else if (rsa_data->rsa_n_len <= 512) { /* key n size 512 */ ++ *key_len = 512; /* key n size 512 */ ++ *key_width = CIPHER_RSA_KEY_WIDTH_4K; ++ } else { ++ ot_err_cipher("rsa_n_len(0x%x) is invalid\n", rsa_data->rsa_n_len); ++ return OT_ERR_CIPHER_INVALID_POINT; ++ } ++ ++ /* if dataLen < key_len, padding 0 before data */ ++ p = g_rsa_n + (*key_len - rsa_data->rsa_n_len); ++ ret = memcpy_s(p, sizeof(g_rsa_n) - (*key_len - rsa_data->rsa_n_len), rsa_data->rsa_n, rsa_data->rsa_n_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ p = g_rsa_k + (*key_len - rsa_data->rsa_k_len); ++ ret = memcpy_s(p, sizeof(g_rsa_k) - (*key_len - rsa_data->rsa_k_len), rsa_data->rsa_k, rsa_data->rsa_k_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ p = g_rsa_m + (*key_len - rsa_data->data_len); ++ ret = memcpy_s(p, sizeof(g_rsa_m) - (*key_len - rsa_data->data_len), rsa_data->input_data, rsa_data->data_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_calc_rsa_ex(const cipher_rsa_data_s *rsa_data, ++ td_u32 key_len, cipher_rsa_key_width_e key_width) ++{ ++ td_u8 err_cnt; ++ td_s32 ret; ++ td_u32 err_code; ++ td_u64 random = 0; ++ ++ ret = drv_cipher_check_rsa_data(rsa_data->rsa_n, rsa_data->rsa_k, rsa_data->input_data, key_len); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("RSA data invalid!\n"); ++ return ret; ++ } ++ ++ g_rsa_done = TD_FALSE; ++ ++ for (err_cnt = 0; err_cnt < RSA_RETRY_CNT; err_cnt++) { ++ ret = hal_rsa_wait_free(); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("RSA is busy!\n"); ++ return ret; ++ } ++ ++ /* Config Mode */ ++ hal_rsa_config_mode(key_width); ++ ++ drv_rsa_rand_mask(rsa_data, key_len, (td_u32 *)&random); ++ ++ /* Write rsa_n, rsa_e, rsa_m */ ++ hal_rsa_write_data(CIPHER_RSA_DATA_TYPE_MODULE, ++ rsa_data->rsa_n, rsa_data->rsa_n_len, key_len, (td_u32 *)&random); ++ ++ ret = drv_rsa_cipher_klad(rsa_data, key_len, (td_u32 *)&random); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ hal_rsa_write_data(CIPHER_RSA_DATA_TYPE_CONTEXT, ++ rsa_data->input_data, rsa_data->rsa_n_len, key_len, (td_u32 *)&random); ++ ++ /* Sart */ ++ hal_rsa_start(); ++ ++ ret = drv_rsa_wait_done(); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("RSA is busy and timeout,error!\n"); ++ return ret; ++ } ++ ++ /* Get result */ ++ hal_rsa_read_data(rsa_data->output_data, rsa_data->rsa_n_len, key_len); ++ ++ ret = drv_cipher_clear_rsa_ram(); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ err_code = hal_rsa_get_error_code(); ++ if (err_code == 0) { ++ return TD_SUCCESS; ++ } else { ++ continue; ++ } ++ } ++ ++ ot_err_cipher("RSA is err: chipset error code: 0x%x!\n", err_code); ++ return TD_FAILURE; ++} ++#endif ++ ++static td_s32 drv_cipher_calc_rsa(cipher_rsa_data_s *rsa_data) ++{ ++ td_s32 ret; ++ td_u32 key_len = 0; ++ cipher_rsa_data_s cipher_rsa_data; ++ cipher_rsa_key_width_e key_width = CIPHER_RSA_KEY_WIDTH_BUTT; ++ ++ cipher_rsa_return_invalid_param(rsa_data == TD_NULL); ++ cipher_rsa_return_invalid_param(rsa_data->input_data == TD_NULL); ++ cipher_rsa_return_invalid_param(rsa_data->output_data == TD_NULL); ++ cipher_rsa_return_invalid_param(rsa_data->rsa_n == TD_NULL); ++ cipher_rsa_return_invalid_param(rsa_data->rsa_k == TD_NULL); ++ cipher_rsa_return_invalid_param(rsa_data->data_len != rsa_data->rsa_n_len); ++ cipher_rsa_return_invalid_param(rsa_data->rsa_k_len > rsa_data->rsa_n_len); ++ ++ (td_void)memset_s(g_rsa_n, sizeof(g_rsa_n), 0, sizeof(g_rsa_n)); ++ (td_void)memset_s(g_rsa_k, sizeof(g_rsa_k), 0, sizeof(g_rsa_k)); ++ (td_void)memset_s(g_rsa_m, sizeof(g_rsa_m), 0, sizeof(g_rsa_m)); ++ ++ ret = drv_rsa_key_info(rsa_data, &key_len, &key_width); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ (td_void)memset_s(&cipher_rsa_data, sizeof(cipher_rsa_data), 0, sizeof(cipher_rsa_data_s)); ++ cipher_rsa_data.rsa_n = g_rsa_n; ++ cipher_rsa_data.rsa_k = g_rsa_k; ++ cipher_rsa_data.rsa_n_len = key_len; ++ cipher_rsa_data.rsa_k_len = key_len; ++ cipher_rsa_data.input_data = g_rsa_m; ++ cipher_rsa_data.data_len = key_len; ++ cipher_rsa_data.output_data = g_rsa_m; ++ cipher_rsa_data.ca_type = rsa_data->ca_type; ++ ++ ret = drv_cipher_calc_rsa_ex(&cipher_rsa_data, key_len, key_width); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ ++ ret = memcpy_s(rsa_data->output_data, sizeof(g_rsa_m), ++ g_rsa_m + (key_len - rsa_data->rsa_n_len), rsa_data->rsa_n_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ return ret; ++} ++ ++td_s32 ot_drv_cipher_calc_rsa(cipher_rsa_data_s *rsa_data) ++{ ++ td_s32 ret; ++ ++ if (rsa_data == TD_NULL) { ++ ot_err_cipher("Invalid params!\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (cipher_mutex_lock(&g_rsa_mutex_kernel)) { ++ ot_err_cipher("down_interruptible failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = drv_cipher_calc_rsa(rsa_data); ++ ++ cipher_mutex_unlock(&g_rsa_mutex_kernel); ++ ++ return ret; ++} ++ +diff --git a/product/security_subsys/cipher/v2/drv/rsa/drv_rsa.h b/product/security_subsys/cipher/v2/drv/rsa/drv_rsa.h +new file mode 100644 +index 0000000..2262919 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/rsa/drv_rsa.h +@@ -0,0 +1,36 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_RSA_H ++#define DRV_RSA_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/***************************** Macro Definition ******************************/ ++ ++td_s32 drv_rsa_init(td_void); ++td_void drv_rsa_deinit(td_void); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* DRV_RSA_H */ +diff --git a/product/security_subsys/cipher/v2/drv/spacc/spacc_body.c b/product/security_subsys/cipher/v2/drv/spacc/spacc_body.c +new file mode 100644 +index 0000000..2ad0b60 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/spacc/spacc_body.c +@@ -0,0 +1,1119 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include "cipher_adapt.h" ++#include "spacc_union_define.h" ++#include "spacc_body.h" ++#include "spacc_reg.h" ++ ++#define spacc_point_return_if_null(p) \ ++ do { \ ++ if (!(p)) \ ++ return SPACC_ERR_NULL_POINT; \ ++ } while (0) ++ ++#define spacc_value_return_if_max(val, max) \ ++ do { \ ++ if ((val) >= (max)) \ ++ return SPACC_ERR_INVALID_PARAM; \ ++ } while (0) ++ ++#define SAPCC_SYMC_IN_ENTRY_TOTAL_SIZE (SPACC_PAGE_SIZE) ++#define SAPCC_SYMC_OUT_ENTRY_TOTAL_SIZE (SPACC_PAGE_SIZE) ++#define SAPCC_DIGEST_IN_ENTRY_TOTAL_SIZE (SPACC_PAGE_SIZE) ++ ++#define spacc_err(fmt, ...) OT_PRINT("ERR-%s-%03d: "fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__) ++#define spacc_dbg(fmt...) // OT_PRINT(fmt) ++ ++/* spacc symc int entry struct which is defined by hardware, you can't change it */ ++struct spacc_symc_in_entry_t { ++ unsigned int spacc_cmd : 2; ++ unsigned int rev1 : 6; ++ unsigned int sym_ctrl : 7; ++ unsigned int rev2 : 1; ++ unsigned int gcm_iv_len : 4; ++ unsigned int rev3 : 12; ++ unsigned int sym_start_addr_high; ++ unsigned int sym_start_addr; ++ unsigned int sym_alg_length; ++ unsigned int symc_iv[4]; /* 4 iv len */ ++}; ++ ++/* spacc digest in entry struct which is defined by hardware, you can't change it */ ++struct spacc_digest_in_entry_t { ++ unsigned int spacc_cmd : 2; ++ unsigned int rev1 : 6; ++ unsigned int hash_ctrl : 6; ++ unsigned int rev2 : 18; ++ unsigned int hash_start_addr; ++ unsigned int hash_alg_length; ++ unsigned int hash_start_addr_high; ++}; ++ ++/* spacc symc out entry struct which is defined by hardware, you can't change it */ ++struct spacc_symc_out_entry_t { ++ unsigned int rev1 : 8; ++ unsigned int aes_ctrl : 4; ++ unsigned int rev2 : 20; ++ unsigned int sym_start_addr; ++ unsigned int sym_alg_length; ++ unsigned int hash_rslt_start_addr; ++ unsigned int tag[4]; /* 4 tag len */ ++}; ++ ++struct spacc_symc_context { ++ symc_alg_en symc_alg; ++ symc_mode_en symc_mode; ++ unsigned int symc_iv[4]; /* 4 iv len */ ++ unsigned int symc_ivlen; ++ ++ unsigned int *pad_vir_addr; ++ unsigned int pad_phy_addr; ++ ++ struct spacc_symc_in_entry_t* entry_symc_in; ++ struct spacc_symc_out_entry_t* entry_symc_out; ++ struct spacc_digest_in_entry_t* entry_digest_in; ++ ++ unsigned int entry_symc_in_depth; ++ unsigned int entry_symc_out_depth; ++ unsigned int symc_cur_in_nodes; ++ unsigned int symc_cur_out_nodes; ++}; ++ ++struct spacc_digest_context { ++ digest_alg_en digest_alg; ++ digest_mode_en digest_mode; ++ unsigned int digest_key[SPACC_MAX_HMAC_KEY_LEN / 4]; /* 4 */ ++ unsigned int digest_klen; ++ unsigned int digest_len; ++ unsigned int digest_blen; ++ ++ struct spacc_digest_in_entry_t* entry_digest_in; ++ unsigned int entry_digest_in_depth; ++ unsigned int digest_cur_in_nodes; ++ ++ unsigned int hard_key; ++}; ++ ++const td_void *g_spacc_reg_base = 0; ++static struct spacc_symc_context g_symc_context[SPACC_LOGIC_MAX_CHN]; ++static struct spacc_digest_context g_digest_context[SPACC_LOGIC_MAX_CHN]; ++ ++static td_void spacc_secure_chn_enable(td_void) ++{ ++ u_sec_chn_cfg sec_chn_cfg; ++ ++ sec_chn_cfg.u32 = spacc_read(SEC_CHN_CFG); ++ sec_chn_cfg.bits.cipher_sec_chn_cfg |= SPACC_CHN_MASK; ++ sec_chn_cfg.bits.hash_sec_chn_cfg |= SPACC_CHN_MASK; ++ spacc_write(SEC_CHN_CFG, sec_chn_cfg.u32); ++ spacc_dbg("SEC_CHN_CFG[%p]: 0x%x\n", SEC_CHN_CFG, sec_chn_cfg.u32); ++ hal_cipher_read_reg(SEC_CHN_CFG, &sec_chn_cfg.u32); ++} ++ ++static td_void spacc_smmu_enable(unsigned int mmu_table_addr) ++{ ++#ifdef SMMU_ENABLE ++ u_cipher_in_smmu_en cipher_in_smmu_en; ++ u_out_smmu_en out_smmu_en; ++ u_hash_in_smmu_en hash_in_smmu_en; ++ ++ cipher_in_smmu_en.u32 = spacc_read(CIPHER_IN_SMMU_EN); ++ out_smmu_en.u32 = spacc_read(OUT_SMMU_EN); ++ hash_in_smmu_en.u32 = spacc_read(HASH_IN_SMMU_EN); ++ ++ cipher_in_smmu_en.bits.cipher_in_chan_rd_dat_smmu_en |= SPACC_CHN_MASK >> 1; ++ cipher_in_smmu_en.bits.cipher_in_chan_rd_node_smmu_en |= SPACC_CHN_MASK >> 1; ++ ++ out_smmu_en.bits.out_chan_wr_dat_smmu_en |= SPACC_CHN_MASK >> 1; ++ out_smmu_en.bits.out_chan_rd_node_smmu_en |= SPACC_CHN_MASK >> 1; ++ ++ hash_in_smmu_en.bits.hash_in_chan_rd_dat_smmu_en |= SPACC_CHN_MASK >> 1; ++ hash_in_smmu_en.bits.hash_in_chan_rd_node_smmu_en |= SPACC_CHN_MASK >> 1; ++ ++ spacc_write(CIPHER_IN_SMMU_EN, cipher_in_smmu_en.u32); ++ spacc_write(OUT_SMMU_EN, out_smmu_en.u32); ++ spacc_write(HASH_IN_SMMU_EN, hash_in_smmu_en.u32); ++ ++ spacc_dbg("CIPHER_IN_SMMU_EN[%p]: 0x%x\n", CIPHER_IN_SMMU_EN, cipher_in_smmu_en.u32); ++ spacc_dbg("OUT_SMMU_EN[%p] : 0x%x\n", OUT_SMMU_EN, out_smmu_en.u32); ++ spacc_dbg("HASH_IN_SMMU_EN[%p] : 0x%x\n", HASH_IN_SMMU_EN, hash_in_smmu_en.u32); ++#ifdef REE_NONSECURE_ENABLE ++ spacc_write(NORM_SMMU_START_ADDR, mmu_table_addr); ++#else ++ spacc_write(SEC_SMMU_START_ADDR, mmu_table_addr); ++#endif ++#endif // SMMU_ENABLE ++} ++ ++static td_void spacc_int_enable(td_void) ++{ ++#ifdef INT_ENABLE ++ u_cipher_int_en cipher_int_en; ++ u_hash_int_en hash_int_en; ++ ++ cipher_int_en.u32 = spacc_read(CIPHER_INT_EN); ++ hash_int_en.u32 = spacc_read(HASH_INT_EN); ++ ++ cipher_int_en.bits.cipher_chn_obuf_en |= SPACC_CHN_MASK; ++ hash_int_en.bits.hash_chn_oram_en |= SPACC_CHN_MASK; ++ ++ cipher_int_en.bits.cipher_nsec_int_en = 1; ++ hash_int_en.bits.hash_int_en = 1; ++ ++ spacc_write(CIPHER_INT_EN, cipher_int_en.u32); ++ spacc_write(HASH_INT_EN, hash_int_en.u32); ++ spacc_dbg("CIPHER_INT_EN: 0x%x\n", cipher_int_en.u32); ++ spacc_dbg("HASH_INT_EN: 0x%x\n", hash_int_en.u32); ++#endif ++} ++ ++static td_void spacc_config_start_addr(unsigned long entry_phy_addr, td_void *entry_via_addr) ++{ ++ unsigned int i; ++ td_size_t page_phy; ++ td_void *page_via = TD_NULL; ++ ++ page_phy = entry_phy_addr; ++ page_via = entry_via_addr; ++ ++ for (i = CIPHER_PKG_N_CHN_MIN; i <= CIPHER_PKG_N_CHN_MAX; i++) { ++ u_chann_cipher_in_node_cfg cipher_in_cfg; ++ u_chann_cipher_out_node_cfg cipher_out_cfg; ++ u_chann_hash_in_node_cfg hash_in_cfg; ++ ++ /* set total num and start addr for cipher in node */ ++ cipher_in_cfg.u32 = spacc_read(chn_n_cipher_in_node_cfg(i)); ++ cipher_in_cfg.bits.cipher_in_node_total_num = SPACC_MAX_DEPTH; ++ spacc_write(chn_n_cipher_in_node_cfg(i), cipher_in_cfg.u32); ++ spacc_write(chn_n_cipher_in_node_start_addr(i), get_ulong_low(page_phy)); ++ spacc_write(chn_n_cipher_in_node_start_addr_high(i), get_ulong_high(page_phy)); ++ spacc_dbg("chn_n_cipher_in_node_cfg[%p]: \t0x%x, PHY: 0x%zx, VIA %p\n", ++ chn_n_cipher_in_node_cfg(i), cipher_in_cfg.u32, page_phy, page_via); ++ g_symc_context[i].entry_symc_in = (struct spacc_symc_in_entry_t*)page_via; ++ g_symc_context[i].symc_cur_in_nodes = cipher_in_cfg.bits.cipher_in_node_wptr; ++ g_symc_context[i].entry_symc_in_depth = 0; ++ page_via += SAPCC_SYMC_IN_ENTRY_TOTAL_SIZE; ++ page_phy += SAPCC_SYMC_IN_ENTRY_TOTAL_SIZE; ++ ++ /* set total num and start addr for cipher out node */ ++ cipher_out_cfg.u32 = spacc_read(chn_n_cipher_out_node_cfg(i)); ++ cipher_out_cfg.bits.cipher_out_node_total_num = SPACC_MAX_DEPTH; ++ spacc_write(chn_n_cipher_out_node_cfg(i), cipher_out_cfg.u32); ++ spacc_write(chn_n_cipher_out_node_start_addr(i), get_ulong_low(page_phy)); ++ spacc_write(chn_n_cipher_out_node_start_addr_high(i), get_ulong_high(page_phy)); ++ spacc_dbg("chn_n_cipher_out_node_cfg[%p]: \t0x%x, PHY: 0x%zx, VIA %p\n", ++ chn_n_cipher_out_node_cfg(i), cipher_out_cfg.u32, page_phy, page_via); ++ g_symc_context[i].entry_symc_out = (struct spacc_symc_out_entry_t*)page_via; ++ g_symc_context[i].symc_cur_out_nodes = cipher_out_cfg.bits.cipher_out_node_wptr; ++ g_symc_context[i].entry_symc_out_depth = 0; ++ page_via += SAPCC_SYMC_OUT_ENTRY_TOTAL_SIZE; ++ page_phy += SAPCC_SYMC_OUT_ENTRY_TOTAL_SIZE; ++ ++ /* set total num and start addr for hash in node */ ++ hash_in_cfg.u32 = spacc_read(chn_n_hash_in_node_cfg(i)); ++ hash_in_cfg.bits.hash_in_node_total_num = SPACC_MAX_DEPTH; ++ spacc_write(chn_n_hash_in_node_cfg(i), hash_in_cfg.u32); ++ spacc_write(chn_n_hash_in_node_start_addr(i), get_ulong_low(page_phy)); ++ spacc_write(chn_n_hash_in_node_start_addr_high(i), get_ulong_high(page_phy)); ++ spacc_dbg("chn_n_hash_in_node_cfg[%p]: \t0x%x, PHY: 0x%zx, VIA %p\n", ++ chn_n_hash_in_node_cfg(i), hash_in_cfg.u32, page_phy, page_via); ++ g_digest_context[i].entry_digest_in = (struct spacc_digest_in_entry_t*)page_via; ++ g_digest_context[i].digest_cur_in_nodes = hash_in_cfg.bits.hash_in_node_wptr; ++ g_digest_context[i].entry_digest_in_depth = 0; ++ page_via += SAPCC_DIGEST_IN_ENTRY_TOTAL_SIZE; ++ page_phy += SAPCC_DIGEST_IN_ENTRY_TOTAL_SIZE; ++ } ++} ++ ++/** ++ * spacc_init - spacc hardware initialization. ++ * @reg_base: virtual address of spacc module which be accessed by CPU ++ * @mmu_table_addr: mmu base table physical addr, if disable mmu, set it to 0 ++ * @entry_phy_addr: a consecutive physical memory, used for nodes ++ * list of symc-in, symc-out and hash-in, the size ++ * must large than spacc_get_node_list_size(). ++ * @entry_via_addr: virtual address of entry_phy_addr. ++ * ++ * Description: ++ * spacc hardware initialization as follows: ++ * - reset global var. ++ * - enable interrupt ++ * - set nodes list addr ++ * - set mmu table addr ++ * - configureure hardware register ++ * ++ * Context: ++ * this function must be called one time in the beginning. ++ */ ++int spacc_init(const td_void *reg_base, unsigned int mmu_table_addr, ++ unsigned long entry_phy_addr, td_void *entry_via_addr) ++{ ++ spacc_point_return_if_null(reg_base); ++ spacc_point_return_if_null(entry_via_addr); ++ ++ (td_void)memset_s(&g_symc_context, sizeof(g_symc_context), 0, sizeof(g_symc_context)); ++ (td_void)memset_s(&g_digest_context, sizeof(g_digest_context), 0, sizeof(g_digest_context)); ++ (td_void)memset_s(entry_via_addr, spacc_get_node_list_size(), 0, spacc_get_node_list_size()); ++ ++ g_spacc_reg_base = reg_base; ++ ++ spacc_secure_chn_enable(); ++ spacc_smmu_enable(mmu_table_addr); ++ spacc_int_enable(); ++ ++ /* configure start addr for in-node and out-node */ ++ spacc_config_start_addr(entry_phy_addr, entry_via_addr); ++ ++ return SPACC_OK; ++} ++ ++/** ++ * spacc_deinit - spacc hardware deinit. ++ */ ++int spacc_deinit(void) ++{ ++ return SPACC_OK; ++} ++ ++/** ++ * spacc_get_node_list_size - return the total size of nodes lists memory required by the drive. ++ */ ++unsigned int spacc_get_node_list_size(void) ++{ ++ /********************************************************************* ++ symc in node page x1| symc out node page x0.5 | hash in node page x0.5 ++ *********************************************************************/ ++ return (SPACC_PAGE_SIZE * 3) * (CIPHER_PKG_N_CHN_MAX - CIPHER_PKG_N_CHN_MIN + 1); /* 3 */ ++} ++ ++int spacc_symc_getiv(unsigned int chn_num, unsigned int *iv, unsigned int ivsize) ++{ ++ unsigned int i; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ for (i = 0; i < 4; i++) /* 4 iv len */ ++ iv[i] = spacc_read(chn_n_cipher_iv_out(chn_num)); ++ ++ return SPACC_OK; ++} ++ ++/* ++ * spacc_symc_gettag - get the tag for CCM/GCM. ++ */ ++int spacc_symc_gettag(unsigned int chn_num, unsigned char *tag, unsigned int tag_len) ++{ ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ u_chann_cipher_out_node_cfg out_node_cfg; ++ unsigned int last; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ out_node_cfg.u32 = spacc_read(chn_n_cipher_out_node_cfg(chn_num)); ++ last = out_node_cfg.bits.cipher_out_node_wptr; ++ last = (last == 0) ? (SPACC_MAX_DEPTH - 1) : (last - 1); ++ ++ flush_cache(cipher_align_down((td_size_t)(uintptr_t)info->entry_symc_out), ++ cipher_align_size((td_size_t)(uintptr_t)info->entry_symc_out, SPACC_PAGE_SIZE)); ++ ++ if (memcpy_s(tag, tag_len, info->entry_symc_out[last].tag, sizeof(info->entry_symc_out[last].tag)) != EOK) { ++ ot_err_cipher("call failed memcpy_s\n"); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ ++ return SPACC_OK; ++} ++ ++/** ++ * spacc_symc_setkey - set even key and odd key for symc. ++ * @chn_num: the logic channel number, must 1~7. ++ * @even_key: even key ++ * @odd_key: odd key ++ * @klen: length of key. ++ * ++ * Description: ++ * the odd key only valid for aes ecb/cbc/ofb/cfb/ctr, and the data to encrypt/decprypt ++ * must be aligned with 64. ++ * ++ */ ++int spacc_symc_setkey(unsigned int chn_num, ++ const unsigned int *even_key, const unsigned int *odd_key, unsigned int klen) ++{ ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ unsigned int i; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ spacc_point_return_if_null(even_key); ++ spacc_point_return_if_null(odd_key); ++ spacc_value_return_if_max(klen, 49); /* 49 klen max */ ++ /* Set even key */ ++ spacc_write(ODD_EVEN_KEY_SEL, 0x00); ++ for (i = 0; (i < klen / 4) && (i < 8); i++) { /* 4, 8 */ ++ spacc_write(cipher_key(chn_num) + i * 4, even_key[i]); /* 4 */ ++ spacc_dbg("EVEN key[%p]: 0x%x\n", cipher_key(chn_num) + i * 4, even_key[i]); /* 4 */ ++ } ++ ++ if (info->symc_alg == SYMC_ALG_SM1) { ++ for (i = 0; i < 4; i++) { /* 4 */ ++ spacc_write(sm1_sk(chn_num) + i * 4, even_key[i + 8]); /* 4, 8 */ ++ spacc_dbg("SK[%p]: 0x%x\n", sm1_sk(chn_num) + i * 4, even_key[i + 8]); /* 4, 8 */ ++ } ++ } ++ ++ return SPACC_OK; ++} ++ ++/** ++ * spacc_symc_setiv - set iv for symc. ++ * @chn_num: the logic channel number, must 1~7. ++ * @iv: the initialization vector ++ * @ivlen: length of iv. ++ * ++ * Description: ++ * here store the iv to global structure of channel, don't set to logic, ++ * because the IV must be set in the nodes list. ++ * ++ */ ++int spacc_symc_setiv(unsigned int chn_num, const unsigned char *iv, unsigned int ivlen) ++{ ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ spacc_point_return_if_null(iv); ++ spacc_value_return_if_max(ivlen, 33); /* 33 iv max size */ ++ ++ (td_void)memset_s(info->symc_iv, sizeof(info->symc_iv), 0, sizeof(info->symc_iv)); ++ if (memcpy_s(info->symc_iv, sizeof(info->symc_iv), iv, ivlen) != EOK) { ++ ot_err_cipher("call failed memcpy_s\n"); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ info->symc_ivlen = ivlen; ++ ++ return SPACC_OK; ++} ++ ++/* ++ * spacc_symc_addbuf - filling the buf addr and length of ++ * encrypt/decrypt data into nodes list. ++ */ ++int spacc_symc_addbuf(unsigned int chn_num, unsigned long buf_phy, ++ unsigned int buf_size, spacc_buf_type_en type, unsigned int ctrl) ++{ ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ unsigned int id, size; ++ td_void *addr = TD_NULL; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ switch (type) { ++ case SPACC_BUF_TYPE_SYMC_IN: ++ id = info->symc_cur_in_nodes++; ++ addr = &info->entry_symc_in[id]; ++ size = sizeof(struct spacc_symc_in_entry_t); ++ (td_void)memset_s(addr, size, 0, size); ++ info->entry_symc_in[id].spacc_cmd = 0x00; ++ info->entry_symc_in[id].sym_start_addr = get_ulong_low(buf_phy); ++ info->entry_symc_in[id].sym_start_addr_high = get_ulong_high(buf_phy); ++ info->entry_symc_in[id].sym_alg_length = buf_size; ++ info->entry_symc_in[id].sym_ctrl = ctrl; ++ info->entry_symc_in_depth++; ++ info->symc_cur_in_nodes %= SPACC_MAX_DEPTH; ++ spacc_dbg("chn %d, add symc in buf: id %d, addr 0x%zx, len 0x%x, ctrl 0x%x\n", ++ chn_num, id, buf_phy, buf_size, ctrl); ++ break; ++ case SPACC_BUF_TYPE_SYMC_OUT: ++ id = info->symc_cur_out_nodes++; ++ addr = &info->entry_symc_out[id]; ++ size = sizeof(struct spacc_symc_out_entry_t); ++ (td_void)memset_s(addr, size, 0, size); ++ info->entry_symc_out[id].sym_start_addr = get_ulong_low(buf_phy); ++ info->entry_symc_out[id].tag[0] = get_ulong_high(buf_phy); ++ info->entry_symc_out[id].sym_alg_length = buf_size; ++ info->entry_symc_out[id].aes_ctrl = ctrl; ++ info->entry_symc_out_depth++; ++ info->symc_cur_out_nodes %= SPACC_MAX_DEPTH; ++ spacc_dbg("chn %d, add symc out buf: id %d, addr 0x%zx, len 0x%x\n", chn_num, id, buf_phy, buf_size); ++ break; ++ default: ++ return SPACC_ERR_INVALID_PARAM; ++ } ++ ++ flush_cache(cipher_align_down(buf_phy), cipher_align_size(buf_phy, buf_size)); ++ ++ return SPACC_OK; ++} ++ ++/* ++ * spacc_symc_addbuf - add a flags to the last valid nodes. ++ */ ++int spacc_symc_addctrl(unsigned int chn_num, spacc_buf_type_en type, unsigned int ctrl) ++{ ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ unsigned int id; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ switch (type) { ++ case SPACC_BUF_TYPE_SYMC_IN: ++ id = (info->symc_cur_in_nodes == 0) ? SPACC_MAX_DEPTH - 1 : info->symc_cur_in_nodes - 1; ++ info->entry_symc_in[id].sym_ctrl |= ctrl; ++ break; ++ case SPACC_BUF_TYPE_SYMC_OUT: ++ id = (info->symc_cur_out_nodes == 0) ? SPACC_MAX_DEPTH - 1 : info->symc_cur_out_nodes - 1; ++ info->entry_symc_out[id].aes_ctrl |= ctrl; ++ break; ++ default: ++ return SPACC_ERR_INVALID_PARAM; ++ } ++ ++ return SPACC_OK; ++} ++ ++/* ++ * spacc_symc_configure - configure logic register, such as alg, mode, key len and so on. ++ */ ++int spacc_symc_config(unsigned int chn_num, const spacc_symc_config_s *symc_cfg, ++ unsigned char sm1_round_num, unsigned char hard_key) ++{ ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ u_chann_cipher_ctrl cipher_ctrl; ++ unsigned int symc_klen = 0; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ spacc_value_return_if_max(symc_cfg->symc_alg, SYMC_ALG_COUNT); ++ spacc_value_return_if_max(symc_cfg->symc_mode, SYMC_MODE_COUNT); ++ spacc_value_return_if_max(symc_cfg->symc_width, SYMC_DAT_WIDTH_COUNT); ++ ++ if (symc_cfg->symc_alg == SYMC_ALG_AES) ++ symc_klen = symc_cfg->key_len / 8 - 2; /* 8, 2 */ ++ else if (symc_cfg->symc_alg == SYMC_ALG_3DES) ++ symc_klen = (symc_cfg->key_len == 16 ? 3 : 2); /* 16, 3, 2 */ ++ ++ cipher_ctrl.u32 = spacc_read(chn_n_cipher_ctrl(chn_num)); ++ cipher_ctrl.bits.sym_chn_sm1_round_num = sm1_round_num; ++ cipher_ctrl.bits.sym_chn_key_sel = hard_key; ++ cipher_ctrl.bits.sym_chn_key_length = symc_klen; ++ cipher_ctrl.bits.sym_chn_dat_width = symc_cfg->symc_width; ++ cipher_ctrl.bits.sym_chn_decrypt = 0x00; ++ cipher_ctrl.bits.sym_chn_alg_sel = symc_cfg->symc_alg; ++ cipher_ctrl.bits.sym_chn_alg_mode = symc_cfg->symc_mode; ++ spacc_write(chn_n_cipher_ctrl(chn_num), cipher_ctrl.u32); ++ spacc_dbg("chn_n_cipher_ctrl(%d): 0x%x\n", chn_num, cipher_ctrl.u32); ++ ++ info->symc_alg = symc_cfg->symc_alg; ++ info->symc_mode = symc_cfg->symc_mode; ++ info->entry_symc_in_depth = 0; ++ info->entry_symc_out_depth = 0; ++ ++ return SPACC_OK; ++} ++ ++static int spacc_config_out_node(unsigned int chn_num, const struct spacc_symc_context *info) ++{ ++ u_chann_cipher_out_node_cfg out_node_cfg; ++ unsigned int ptr; ++ ++ out_node_cfg.u32 = spacc_read(chn_n_cipher_out_node_cfg(chn_num)); ++ if (out_node_cfg.bits.cipher_out_node_wptr != out_node_cfg.bits.cipher_out_node_rptr) { ++ spacc_err("Error, chn %d is busy.\n", chn_num); ++ return SPACC_ERR_BUSY; ++ } ++ ptr = out_node_cfg.bits.cipher_out_node_wptr + info->entry_symc_out_depth; ++ out_node_cfg.bits.cipher_out_node_wptr = ptr % SPACC_MAX_DEPTH; ++ out_node_cfg.bits.cipher_out_node_mpackage_int_level = info->entry_symc_out_depth; ++ spacc_write(chn_n_cipher_out_node_cfg(chn_num), out_node_cfg.u32); ++ spacc_dbg("chn_n_cipher_out_node_cfg: 0x%x\n", out_node_cfg.u32); ++ ++ return SPACC_OK; ++} ++ ++static void spacc_config_in_node(unsigned int chn_num, const struct spacc_symc_context *info) ++{ ++ u_chann_cipher_in_node_cfg in_node_cfg; ++ unsigned int ptr; ++ ++ in_node_cfg.u32 = spacc_read(chn_n_cipher_in_node_cfg(chn_num)); ++ ptr = in_node_cfg.bits.cipher_in_node_wptr + info->entry_symc_in_depth; ++ in_node_cfg.bits.cipher_in_node_wptr = ptr % SPACC_MAX_DEPTH; ++ in_node_cfg.bits.cipher_in_node_mpackage_int_level = info->entry_symc_in_depth; ++ ++ spacc_dbg("chn_n_cipher_in_node_cfg: 0x%x\n", in_node_cfg.u32); ++ spacc_dbg("chn %d, start 0x%x from 0x%x to 0x%x\n", chn_num, info->entry_symc_in_depth, ++ in_node_cfg.bits.cipher_in_node_rptr, in_node_cfg.bits.cipher_in_node_wptr); ++ ++ /* move forward the in-node ptr to action the symc working */ ++ spacc_write(chn_n_cipher_in_node_cfg(chn_num), in_node_cfg.u32); ++} ++ ++static void spacc_cipher_chn_crypt(unsigned int chn_num, unsigned int decrypt) ++{ ++ u_chann_cipher_ctrl cipher_ctrl; ++ ++ cipher_ctrl.u32 = spacc_read(chn_n_cipher_ctrl(chn_num)); ++ cipher_ctrl.bits.sym_chn_decrypt = decrypt; ++ spacc_write(chn_n_cipher_ctrl(chn_num), cipher_ctrl.u32); ++} ++ ++/* ++ * spacc_symc_start - action the symc start to processing the node list. ++ */ ++int spacc_symc_start(unsigned int chn_num, unsigned int decrypt, unsigned int iv_set_flag) ++{ ++ unsigned int cur, i, j, node; ++ u_chann_cipher_in_node_cfg in_node_cfg; ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ in_node_cfg.u32 = spacc_read(chn_n_cipher_in_node_cfg(chn_num)); ++ cur = in_node_cfg.bits.cipher_in_node_rptr; ++ spacc_dbg("cur %d, depth %d, symc_iv Len %d\n", cur, info->entry_symc_in_depth, info->symc_ivlen); ++ for (j = 0; j < info->entry_symc_in_depth; j++) { ++ if (info->symc_ivlen > 0) { ++ /* Write iv to all nodes */ ++ node = (cur + j) % SPACC_MAX_DEPTH; ++ for (i = 0; i < 4; i++) { /* 4 iv len */ ++ info->entry_symc_in[node].symc_iv[i] = info->symc_iv[i]; ++ spacc_dbg("symc_iv[%d]: 0x%x\n", i, info->symc_iv[i]); ++ } ++ ++ /* Set iv len for GCM */ ++ if (info->symc_mode == SYMC_MODE_GCM) ++ info->entry_symc_in[node].gcm_iv_len = info->symc_ivlen - 1; ++ else ++ info->entry_symc_in[node].gcm_iv_len = 0; ++ ++ /* IV only be set for first node. */ ++ if (iv_set_flag == OT_CIPHER_IV_CHG_ONE_PKG) { ++ info->entry_symc_in[node].sym_ctrl |= SPACC_CTRL_SYMC_IN_FIRST; ++ iv_set_flag = 0; ++ } else if ((iv_set_flag == OT_CIPHER_IV_CHG_ALL_PKG) && ++ (info->symc_mode != SYMC_MODE_CCM) && (info->symc_mode != SYMC_MODE_GCM)) { ++ /* IV will be set for each node. */ ++ info->entry_symc_in[node].sym_ctrl |= SPACC_CTRL_SYMC_IN_FIRST | SPACC_CTRL_SYMC_IN_LAST; ++ } ++ } ++ } ++ ++ if (spacc_config_out_node(chn_num, info) != SPACC_OK) ++ return SPACC_ERR_BUSY; ++ ++ /* encrypt or decrypt */ ++ spacc_cipher_chn_crypt(chn_num, decrypt); ++ ++ flush_cache(cipher_align_down((td_size_t)(uintptr_t)info->entry_symc_in), ++ cipher_align_size((td_size_t)(uintptr_t)info->entry_symc_in, SPACC_PAGE_SIZE)); ++ flush_cache(cipher_align_down((td_size_t)(uintptr_t)info->entry_symc_out), ++ cipher_align_size((td_size_t)(uintptr_t)info->entry_symc_out, SPACC_PAGE_SIZE)); ++ ++ /* spacc_config_in_node must be placed after flush_cache, otherwise it may be crypt timeout */ ++ spacc_config_in_node(chn_num, info); ++ ++ /* all the nodes are processing, reset the depth to 0 */ ++ info->entry_symc_in_depth = 0; ++ info->entry_symc_out_depth = 0; ++ ++ return SPACC_OK; ++} ++ ++/* ++ * spacc_symc_restart - continue to action the symc to processing the node list. ++ */ ++void spacc_symc_restart(unsigned int chn_num, unsigned int iv_set_flag) ++{ ++ unsigned int cur, i, j, node; ++ u_chann_cipher_in_node_cfg in_node_cfg; ++ struct spacc_symc_context *info = &g_symc_context[chn_num]; ++ ++ if (chn_num > SPACC_LOGIC_MAX_CHN) { ++ ot_err_cipher("chn_num is %d\n", chn_num); ++ return; ++ } ++ ++ if (info->entry_symc_in_depth == 0) ++ return; ++ ++ in_node_cfg.u32 = spacc_read(chn_n_cipher_in_node_cfg(chn_num)); ++ if ((iv_set_flag == OT_CIPHER_IV_CHG_ALL_PKG) && ++ (info->symc_mode != SYMC_MODE_CCM) && (info->symc_mode != SYMC_MODE_GCM)) { ++ cur = in_node_cfg.bits.cipher_in_node_wptr; ++ for (j = 0; j < info->entry_symc_in_depth; j++) { ++ if (info->symc_ivlen == 0) ++ continue; ++ ++ node = (cur + j) % SPACC_MAX_DEPTH; ++ for (i = 0; i < 4; i++) { /* 4 iv len */ ++ info->entry_symc_in[node].symc_iv[i] = info->symc_iv[i]; ++ spacc_dbg("symc_iv[%d]: 0x%x\n", i, info->symc_iv[i]); ++ } ++ info->entry_symc_in[node].sym_ctrl |= 0x01; ++ } ++ } ++ ++ if (spacc_config_out_node(chn_num, info) != SPACC_OK) ++ return; ++ spacc_config_in_node(chn_num, info); ++ ++ flush_cache(cipher_align_down((td_size_t)(uintptr_t)info->entry_symc_in), ++ cipher_align_size((td_size_t)(uintptr_t)info->entry_symc_in, SPACC_PAGE_SIZE)); ++ flush_cache(cipher_align_down((td_size_t)(uintptr_t)info->entry_symc_out), ++ cipher_align_size((td_size_t)(uintptr_t)info->entry_symc_out, SPACC_PAGE_SIZE)); ++ ++ info->entry_symc_in_depth = 0; ++ info->entry_symc_out_depth = 0; ++} ++ ++/* ++ * spacc_symc_done_try - get the int status of symc. ++ */ ++unsigned int spacc_symc_done_notify(void) ++{ ++ u_cipher_int_raw int_raw; ++ unsigned int chn_mask; ++ ++ int_raw.u32 = spacc_read(CIPHER_INT_RAW); ++ int_raw.bits.cipher_chn_obuf_raw &= SPACC_CHN_MASK; ++ chn_mask = int_raw.bits.cipher_chn_obuf_raw; ++ spacc_write(CIPHER_INT_RAW, int_raw.u32); ++ ++ return chn_mask; ++} ++ ++/* ++ * spacc_symc_done_try - test the int status of symc channel. ++ */ ++unsigned int spacc_symc_done_try(unsigned int chn_num) ++{ ++ u_cipher_int_raw int_raw; ++ unsigned int chn_mask; ++ ++ int_raw.u32 = spacc_read(CIPHER_INT_RAW); ++ int_raw.bits.cipher_chn_obuf_raw &= 0x01 << chn_num; ++ chn_mask = int_raw.bits.cipher_chn_obuf_raw; ++ ++ /* Clean raw int */ ++ int_raw.u32 = 0x00; ++ int_raw.bits.cipher_chn_obuf_raw = chn_mask; ++ spacc_write(CIPHER_INT_RAW, int_raw.u32); ++ ++ return chn_mask ? 1 : 0; ++} ++ ++/* ++ * spacc_symc_get_err_code - get the error code of symc. ++ */ ++unsigned int spacc_symc_get_err_code(unsigned int chn_num, ++ unsigned int *src_addr, ++ unsigned int *dst_addr) ++{ ++ *src_addr = spacc_read(chn_n_cipher_in_buf_rptr(chn_num)); ++ *dst_addr = spacc_read(chn_n_cipher_out_buf_rptr(chn_num)); ++ ++ return spacc_read(CALC_ERR); ++} ++ ++/* ++ * spacc_digest_configure - configure the hash ctrl register. ++ */ ++int spacc_digest_config(unsigned int chn_num, ++ digest_alg_en digest_alg, ++ digest_mode_en digest_mode, ++ unsigned char hard_key) ++{ ++ struct spacc_digest_context *info = &g_digest_context[chn_num]; ++ u_chann_hash_ctrl hash_ctrl; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ spacc_value_return_if_max(digest_alg, DIGEST_ALG_COUNT); ++ spacc_value_return_if_max(digest_mode, DIGEST_MODE_COUNT); ++ ++ info->digest_alg = digest_alg; ++ info->digest_mode = digest_mode; ++ ++ switch (digest_alg) { ++ case DIGEST_ALG_SHA1: ++ info->digest_len = 20; /* 20 sha1 digest len */ ++ info->digest_blen = 64; /* 64 sha1 digest blen */ ++ break; ++ case DIGEST_ALG_SHA224: ++ info->digest_len = 28; /* 28 sha1 digest len */ ++ info->digest_blen = 64; /* 64 sha1 digest blen */ ++ break; ++ case DIGEST_ALG_SM3: ++ case DIGEST_ALG_SHA256: ++ info->digest_len = 32; /* 32 sha1 digest len */ ++ info->digest_blen = 64; /* 64 sha1 digest blen */ ++ break; ++ case DIGEST_ALG_SHA384: ++ info->digest_len = 48; /* 48 sha1 digest len */ ++ info->digest_blen = 128; /* 128 sha1 digest blen */ ++ break; ++ case DIGEST_ALG_SHA512: ++ info->digest_len = 64; /* 64 sha1 digest len */ ++ info->digest_blen = 128; /* 128 sha1 digest blen */ ++ break; ++ default: ++ return SPACC_ERR_INVALID_PARAM; ++ } ++ ++ hash_ctrl.u32 = spacc_read(chn_n_hash_ctrl(chn_num)); ++ hash_ctrl.bits.hash_chn_mode = digest_mode; ++ hash_ctrl.bits.hash_chn_agl_sel = digest_alg; ++ spacc_write(chn_n_hash_ctrl(chn_num), hash_ctrl.u32); ++ spacc_dbg("CTRL: 0x%X\n", hash_ctrl.u32); ++ ++ info->entry_digest_in_depth = 0; ++ info->hard_key = hard_key; ++ ++ return SPACC_OK; ++} ++ ++/** ++ * spacc_digest_addbuf - filling the buf addr and length of ++ * data into nodes list. ++ * ++ */ ++int spacc_digest_addbuf(unsigned int chn_num, ++ unsigned long buf_phy, ++ unsigned int buf_size, ++ unsigned int ctrl) ++{ ++ struct spacc_digest_context *info = &g_digest_context[chn_num]; ++ unsigned int id, size; ++ td_void *addr = TD_NULL; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ id = info->digest_cur_in_nodes++; ++ addr = &info->entry_digest_in[id]; ++ size = sizeof(struct spacc_digest_in_entry_t); ++ (td_void)memset_s(addr, size, 0, size); ++ info->entry_digest_in[id].spacc_cmd = 0x00; ++ info->entry_digest_in[id].hash_start_addr = get_ulong_low(buf_phy); ++ info->entry_digest_in[id].hash_alg_length = buf_size; ++ info->entry_digest_in[id].hash_ctrl = ctrl; ++ info->entry_digest_in[id].hash_start_addr_high = get_ulong_high(buf_phy); ++ info->entry_digest_in_depth++; ++ info->digest_cur_in_nodes %= SPACC_MAX_DEPTH; ++ spacc_dbg("add digest in buf: id %d, addr 0x%zx, len 0x%x, ctrl 0x%x\n", id, buf_phy, buf_size, ctrl); ++ ++ flush_cache(cipher_align_down(buf_phy), cipher_align_size(buf_phy, buf_size)); ++ ++ return SPACC_OK; ++} ++ ++/** ++ * spacc_digest_addctrl - add a flags to the last valid nodes. ++ * ++ */ ++int spacc_digest_addctrl(unsigned int chn_num, unsigned int ctrl) ++{ ++ struct spacc_digest_context *info = &g_digest_context[chn_num]; ++ unsigned int id; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ id = (info->digest_cur_in_nodes == 0) ? SPACC_MAX_DEPTH - 1 : info->digest_cur_in_nodes - 1; ++ info->entry_digest_in[id].hash_ctrl |= ctrl; ++ ++ return SPACC_OK; ++} ++ ++/** ++ * spacc_digest_get - get hash result. ++ * ++ */ ++int spacc_digest_get(unsigned int chn_num, unsigned int *digest) ++{ ++ unsigned int i; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ for (i = 0; i < HASH_RESULT_MAX_SIZE_IN_WORD; i++) { ++ spacc_write(chn_n_hash_state_val_addr(chn_num), i); ++ digest[i] = spacc_read(chn_n_hash_state_val(chn_num)); ++ spacc_dbg("digest[%d]: 0x%x\n", i, digest[i]); ++ } ++ ++ return SPACC_OK; ++} ++ ++/* ++ * spacc_digest_start - action the hash start to processing the node list. ++ */ ++int spacc_digest_start(unsigned int chn_num, spacc_ctrl_en spacc_ctrl, const unsigned int *state) ++{ ++ unsigned int i; ++ u_chann_hash_in_node_cfg in_node_cfg; ++ struct spacc_digest_context *info = &g_digest_context[chn_num]; ++ unsigned int ptr; ++ ++ spacc_value_return_if_max(chn_num, SPACC_LOGIC_MAX_CHN); ++ ++ spacc_dbg("chn %d, digest_mode %d, ctrl %d\n", chn_num, info->digest_mode, spacc_ctrl); ++ ++ /* Write last state */ ++ for (i = 0; i < HASH_RESULT_MAX_SIZE_IN_WORD; i++) { ++ spacc_write(chn_n_hash_state_val_addr(chn_num), i); ++ spacc_write(chn_n_hash_state_val(chn_num), state[i]); ++ spacc_dbg("state: 0x%x\n", state[i]); ++ } ++ ++ if (info->entry_digest_in_depth == 0) ++ return SPACC_OK; ++ ++ /* configure in-node */ ++ in_node_cfg.u32 = spacc_read(chn_n_hash_in_node_cfg(chn_num)); ++ if (in_node_cfg.bits.hash_in_node_wptr != in_node_cfg.bits.hash_in_node_rptr) { ++ spacc_err("Error, chn %d is busy.\n", chn_num); ++ return SPACC_ERR_BUSY; ++ } ++ ptr = in_node_cfg.bits.hash_in_node_wptr + info->entry_digest_in_depth; ++ in_node_cfg.bits.hash_in_node_wptr = ptr % SPACC_MAX_DEPTH; ++ in_node_cfg.bits.hash_in_node_mpackage_int_level = 1; ++ ++ flush_cache(cipher_align_down((td_size_t)(uintptr_t)info->entry_digest_in), \ ++ cipher_align_size((td_size_t)(uintptr_t)info->entry_digest_in, SPACC_PAGE_SIZE)); ++ ++ /* Start */ ++ spacc_write(chn_n_hash_in_node_cfg(chn_num), in_node_cfg.u32); ++ spacc_dbg("chn_n_hash_in_node_cfg: 0x%x\n", in_node_cfg.u32); ++ ++ return SPACC_OK; ++} ++ ++/* ++ * spacc_digest_done_notify - get the int status of hash. ++ */ ++unsigned int spacc_digest_done_notify(void) ++{ ++ u_hash_int_raw int_raw; ++ unsigned int chn_mask; ++ ++ int_raw.u32 = spacc_read(HASH_INT_RAW); ++ int_raw.bits.hash_chn_oram_raw &= SPACC_CHN_MASK; ++ chn_mask = int_raw.bits.hash_chn_oram_raw; ++ ++ /* Clean raw int */ ++ spacc_write(HASH_INT_RAW, int_raw.u32); ++ ++ return chn_mask; ++} ++ ++/* ++ * spacc_digest_done_try - test the int status of hash channel. ++ */ ++unsigned int spacc_digest_done_try(unsigned int chn_num) ++{ ++ u_hash_int_raw int_raw; ++ unsigned int chn_mask; ++ ++ int_raw.u32 = spacc_read(HASH_INT_RAW); ++ int_raw.bits.hash_chn_oram_raw &= 0x01 << chn_num; ++ chn_mask = int_raw.bits.hash_chn_oram_raw; ++ ++ /* Clean raw int */ ++ spacc_write(HASH_INT_RAW, int_raw.u32); ++ ++ return chn_mask; ++} ++ ++/* ++ * spacc_digest_get_err_code - get the error code of hash. ++ */ ++unsigned int spacc_digest_get_err_code(unsigned int chn_num, unsigned int *src_addr) ++{ ++ *src_addr = spacc_read(chn_n_hash_in_buf_rptr(chn_num)); ++ ++ return spacc_read(CALC_ERR); ++} ++ ++/******************* proc function begin *******************/ ++#ifndef DISABLE_DEBUG_INFO ++static td_void spacc_symc_proc_alg_sel(td_u32 chn, ++ cipher_chn_status_s *cipher_status, ++ u_chann_cipher_ctrl cipher_ctrl) ++{ ++ switch (cipher_ctrl.bits.sym_chn_alg_sel) { ++ case OT_CIPHER_ALG_AES: ++ cipher_status[chn].alg = "AES "; ++ break; ++ case OT_CIPHER_ALG_SM1: ++ cipher_status[chn].alg = "SM1 "; ++ break; ++ case OT_CIPHER_ALG_SM4: ++ cipher_status[chn].alg = "SM4 "; ++ break; ++ case OT_CIPHER_ALG_DMA: ++ cipher_status[chn].alg = "DMA "; ++ break; ++ default: ++ cipher_status[chn].alg = "BUTT"; ++ break; ++ } ++} ++ ++static td_void spacc_symc_proc_alg_mode(td_u32 chn, ++ cipher_chn_status_s *cipher_status, ++ u_chann_cipher_ctrl cipher_ctrl) ++{ ++ switch (cipher_ctrl.bits.sym_chn_alg_mode) { ++ case OT_CIPHER_WORK_MODE_ECB: ++ cipher_status[chn].mode = "ECB "; ++ break; ++ case OT_CIPHER_WORK_MODE_CBC: ++ cipher_status[chn].mode = "CBC "; ++ break; ++ case OT_CIPHER_WORK_MODE_CFB: ++ cipher_status[chn].mode = "CFB "; ++ break; ++ case OT_CIPHER_WORK_MODE_OFB: ++ cipher_status[chn].mode = "OFB "; ++ break; ++ case OT_CIPHER_WORK_MODE_CTR: ++ cipher_status[chn].mode = "CTR "; ++ break; ++ case OT_CIPHER_WORK_MODE_CCM: ++ cipher_status[chn].mode = "CCM "; ++ break; ++ case OT_CIPHER_WORK_MODE_GCM: ++ cipher_status[chn].mode = "GCM "; ++ break; ++ default: ++ cipher_status[chn].mode = "BUTT"; ++ break; ++ } ++} ++ ++static td_void spacc_symc_proc_key_len(td_u32 chn, ++ cipher_chn_status_s *cipher_status, ++ u_chann_cipher_ctrl cipher_ctrl) ++{ ++ if (cipher_ctrl.bits.sym_chn_alg_sel == OT_CIPHER_ALG_AES) { ++ switch (cipher_ctrl.bits.sym_chn_key_length) { ++ case OT_CIPHER_KEY_AES_128BIT: ++ cipher_status[chn].key_len = 128; /* 128 key len */ ++ break; ++ case OT_CIPHER_KEY_AES_192BIT: ++ cipher_status[chn].key_len = 192; /* 192 key len */ ++ break; ++ case OT_CIPHER_KEY_AES_256BIT: ++ cipher_status[chn].key_len = 256; /* 256 key len */ ++ break; ++ default: ++ cipher_status[chn].key_len = 0; ++ break; ++ } ++ } else if (cipher_ctrl.bits.sym_chn_alg_sel == OT_CIPHER_ALG_SM1) { ++ cipher_status[chn].key_len = 384; /* 384 key len */ ++ } else if (cipher_ctrl.bits.sym_chn_alg_sel == OT_CIPHER_ALG_SM4) { ++ cipher_status[chn].key_len = 128; /* 128 key len */ ++ } else { ++ cipher_status[chn].key_len = 0; ++ } ++} ++ ++static td_void spacc_symc_proc_int_info(td_u32 chn, cipher_chn_status_s *cipher_status) ++{ ++ u_cipher_int_raw int_raw; ++ u_cipher_int_en int_en; ++ u_chann_cipher_in_node_cfg in_node; ++ const td_u32 *reg_addr = TD_NULL; ++ ++ /* get INT RAW status */ ++ int_raw.u32 = spacc_read(CIPHER_INT_RAW); ++ cipher_status[chn].in_int_raw = (int_raw.bits.cipher_chn_ibuf_raw >> chn) & 0x1; ++ cipher_status[chn].out_int_raw = (int_raw.bits.cipher_chn_obuf_raw >> chn) & 0x1; ++ ++ /* get INT EN status */ ++ int_en.u32 = spacc_read(CIPHER_INT_EN); ++ cipher_status[chn].in_int_en = int_en.bits.cipher_nsec_int_en; ++ cipher_status[chn].out_int_en = (int_en.bits.cipher_chn_ibuf_en >> chn) & 0x1; ++ cipher_status[chn].in_int_all_en = (int_en.bits.cipher_chn_obuf_en >> chn) & 0x1; ++ ++ /* get INT_OINTCFG */ ++ reg_addr = chn_n_cipher_in_node_cfg(chn); ++ in_node.u32 = spacc_read(reg_addr); ++ cipher_status[chn].out_int_count = in_node.bits.cipher_in_node_mpackage_int_level; ++} ++ ++td_s32 spacc_symc_proc_status(cipher_chn_status_s *cipher_status) ++{ ++ const td_u32 *reg_addr = 0; ++ u_chann_cipher_ctrl cipher_ctrl; ++ td_u32 value; ++ td_u32 i, j; ++ ++ if (cipher_status == NULL) { ++ ot_err_cipher("HAL_CIPHER_ProcGetStatus failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ for (i = 0; i < 8; i++) { /* 8 max channel number */ ++ if (i != 0) ++ reg_addr = chn_n_cipher_ctrl(i); ++ else ++ reg_addr = CHN_0_CIPHER_CTRL; ++ ++ /* get cipher ctrl */ ++ cipher_ctrl.u32 = spacc_read(reg_addr); ++ cipher_status[i].is_decrypt = cipher_ctrl.bits.sym_chn_decrypt; ++ ++ spacc_symc_proc_alg_sel(i, cipher_status, cipher_ctrl); /* get alg sel */ ++ spacc_symc_proc_alg_mode(i, cipher_status, cipher_ctrl); /* get alg mode */ ++ spacc_symc_proc_key_len(i, cipher_status, cipher_ctrl); /* get key len */ ++ ++ /* get key sel */ ++ if (cipher_ctrl.bits.sym_chn_key_sel) ++ cipher_status[i].key_from = "HW"; ++ else ++ cipher_status[i].key_from = "SW"; ++ ++ /* get data in */ ++ if (i != 0) { ++ reg_addr = chn_n_cipher_in_buf_rptr(i); ++ cipher_status[i].data_in_addr = spacc_read(reg_addr); ++ } else { ++ cipher_status[0].data_in_addr = CIPHER_CIPHER_REG_BASE_ADDR_PHY + 0x420; ++ } ++ ++ /* get data out */ ++ if (i != 0) { ++ reg_addr = chn_n_cipher_out_buf_rptr(i); ++ cipher_status[i].data_out_addr = spacc_read(reg_addr); ++ } else { ++ cipher_status[0].data_out_addr = CIPHER_CIPHER_REG_BASE_ADDR_PHY + 0x80; ++ } ++ ++ for (j = 0; j < 4; j++) { /* 4 */ ++ value = spacc_read(chn_n_cipher_iv_out(i) + j * 4); /* 4 */ ++ hex2str(cipher_status[i].iv_string + j * 8, (td_u8)(value & 0xFF)); /* 8 */ ++ hex2str(cipher_status[i].iv_string + j * 8 + 2, (td_u8)((value >> 8) & 0xFF)); /* 8, 2 */ ++ hex2str(cipher_status[i].iv_string + j * 8 + 4, (td_u8)((value >> 16) & 0xFF)); /* 8, 4, 16 */ ++ hex2str(cipher_status[i].iv_string + j * 8 + 6, (td_u8)((value >> 24) & 0xFF)); /* 8, 6, 24 */ ++ } ++ ++ spacc_symc_proc_int_info(i, cipher_status); ++ } ++ ++ return TD_SUCCESS; ++} ++#endif +diff --git a/product/security_subsys/cipher/v2/drv/spacc/spacc_body.h b/product/security_subsys/cipher/v2/drv/spacc/spacc_body.h +new file mode 100644 +index 0000000..3e152ec +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/spacc/spacc_body.h +@@ -0,0 +1,238 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef SPACC_BODY_H ++#define SPACC_BODY_H ++ ++#include "drv_cipher_ioctl.h" ++ ++#define SPACC_LOGIC_MAX_CHN 8 ++#define CIPHER_PKG_1_CHN 0 ++#define SPACC_MAX_DEPTH 127 ++#define SPACC_MAX_HMAC_KEY_LEN 512 ++#define SPACC_PAGE_SIZE 4096 ++ ++ ++#define CIPHER_PKG_N_CHN_MIN 1 ++#define CIPHER_PKG_N_CHN_MAX 7 ++#define SPACC_CHN_MASK 0xFE ++#define CIPHER_DMA_MINALIGN ARCH_DMA_MINALIGN ++ ++typedef enum { ++ SYMC_ALG_DES = 0, ++ SYMC_ALG_3DES, ++ SYMC_ALG_AES, ++ SYMC_ALG_SM4, ++ SYMC_ALG_SM1, ++ SYMC_ALG_NULL_CIPHER, ++ SYMC_ALG_COUNT, ++} symc_alg_en; ++ ++typedef enum { ++ SYMC_MODE_ECB = 0, ++ SYMC_MODE_CBC, ++ SYMC_MODE_CFB, ++ SYMC_MODE_OFB, ++ SYMC_MODE_CTR, ++ SYMC_MODE_CCM, ++ SYMC_MODE_GCM, ++ SYMC_MODE_COUNT, ++} symc_mode_en; ++ ++typedef enum { ++ SYMC_DAT_WIDTH_128 = 0, ++ SYMC_DAT_WIDTH_64 = 0, ++ SYMC_DAT_WIDTH_8, ++ SYMC_DAT_WIDTH_1, ++ SYMC_DAT_WIDTH_COUNT, ++} symc_dat_width_en; ++ ++typedef enum { ++ DIGEST_MODE_HASH, ++ DIGEST_MODE_HMAC, ++ DIGEST_MODE_COUNT, ++} digest_mode_en; ++ ++typedef enum { ++ DIGEST_ALG_SHA1, ++ DIGEST_ALG_SHA224, ++ DIGEST_ALG_SHA256, ++ DIGEST_ALG_SHA384, ++ DIGEST_ALG_SHA512, ++ DIGEST_ALG_SM3, ++ DIGEST_ALG_COUNT, ++} digest_alg_en; ++ ++typedef enum { ++ SPACC_ALLIED_SYMC = 0x00, ++ SPACC_ALLIED_DIGEST, ++ SPACC_ALLIED_SYMC_WITH_DIGEST, ++ SPACC_ALLIED_DIGEST_WITH_SYMC, ++ SPACC_ALLIED_COUNT, ++} spacc_allied_en; ++ ++typedef enum { ++ SPACC_BUF_TYPE_SYMC_IN, ++ SPACC_BUF_TYPE_SYMC_OUT, ++ SPACC_BUF_TYPE_DIGEST_IN, ++ SPACC_BUF_TYPE_COUNT, ++} spacc_buf_type_en; ++ ++typedef enum { ++ SPACC_CTRL_NONE = 0x00, ++ SPACC_CTRL_SYMC_IN_GCM_A = 0x00, ++ SPACC_CTRL_SYMC_IN_GCM_P = 0x08, ++ SPACC_CTRL_SYMC_IN_GCM_LEN = 0x10, ++ SPACC_CTRL_SYMC_IN_CCM_N = 0x00, ++ SPACC_CTRL_SYMC_IN_CCM_A = 0x08, ++ SPACC_CTRL_SYMC_IN_CCM_P = 0x10, ++ SPACC_CTRL_SYMC_IN_CBC_OUTPUT_DISABLE = 0x04, ++ SPACC_CTRL_SYMC_IN_FIRST = 0x01, ++ SPACC_CTRL_SYMC_IN_LAST = 0x02, ++ SPACC_CTRL_HASH_IN_PAD = 0x04, ++ SPACC_CTRL_HASH_IN_FIRST = 0x01, ++ SPACC_CTRL_HASH_IN_LAST = 0x02, ++ SPACC_CTRL_HASH_IN_AUTO_PADDING = 0x04, ++ SPACC_CTRL_HASH_IN_HMAC_END = 0x08, ++ SPACC_CTRL_SYMC_OUT_LAST = 0x02, ++ SPACC_CTRL_SYMC_CCM_LAST = 0x20, ++ SPACC_CTRL_SYMC_ODD_KEY = 0x40, ++ SPACC_CTRL_SYMC_EVEN_KEY = 0x00, ++ SPACC_CTRL_COUNT, ++} spacc_ctrl_en; ++ ++typedef enum { ++ SPACC_DATA_SEAT_FIRST, ++ SPACC_DATA_SEAT_MIDDLE, ++ SPACC_DATA_SEAT_LAST, ++ SPACC_DATA_SEAT_COUNT, ++} spacc_data_seat_en; ++ ++typedef struct { ++ symc_alg_en symc_alg; ++ symc_mode_en symc_mode; ++ symc_dat_width_en symc_width; ++ td_u32 key_len; ++} spacc_symc_config_s; ++ ++#define SPACC_OK 0x000 ++#define SPACC_DIGEST_DONE 0x001 ++#define SPACC_DIGEST_LEN_ERR 0x002 ++#define SPACC_SYMC_DONE 0x004 ++#define SPACC_AEAD_DONE 0x008 ++#define SPACC_SYMC_KEY_ERR 0x0100 ++#define SPACC_SYMC_LEN_ERR 0x0200 ++#define SPACC_SYMC_DFA_ATTACK 0x0400 ++ ++#define SPACC_ERR_NULL_POINT 0x400 ++#define SPACC_ERR_INVALID_PARAM 0x401 ++#define SPACC_ERR_BUSY 0x402 ++#define SPACC_ERR_TIMEOUT 0x403 ++ ++#define SPACC_CHN_SECURE_ENABLE 0x80000000 ++ ++#define get_ulong_low(dw) (unsigned int)(dw) ++#define get_ulong_high(dw) 0 ++#define make_ulong(low, high) (low) ++#define make_size(low) (((unsigned long)(low##High) << 32) | (low)) ++ ++#if (defined(CHIP_TYPE_SS101V200) || defined(CHIP_TYPE_SS528V100) || \ ++ defined(CHIP_TYPE_SS524V100)) ++#define HASH_RESULT_MAX_SIZE_IN_WORD 8 ++#else ++#define HASH_RESULT_MAX_SIZE_IN_WORD 16 ++#endif ++ ++#define CACHE_LINE_VALUE CONFIG_SYS_CACHELINE_SIZE ++#define cipher_align_down(addr) ((addr) & (~((CACHE_LINE_VALUE) - 1))) ++#define cipher_align_size(addr, size) ALIGN((size) + ((addr) & (CACHE_LINE_VALUE - 1)), CIPHER_DMA_MINALIGN) ++ ++int spacc_init(const td_void *reg_base, unsigned int mmu_table_addr, ++ unsigned long entry_phy_addr, td_void *entry_via_addr); ++ ++int spacc_deinit(void); ++ ++unsigned int spacc_get_node_list_size(void); ++ ++/**************************** SYMC API ********************************/ ++int spacc_symc_getiv(unsigned int chn_num, unsigned int *iv, unsigned int ivsize); ++ ++int spacc_symc_gettag(unsigned int chn_num, unsigned char *tag, unsigned int tag_len); ++ ++int spacc_symc_setkey(unsigned int chn_num, ++ const unsigned int *even_key, ++ const unsigned int *odd_key, ++ const unsigned int klen); ++ ++int spacc_symc_setiv(unsigned int chn_num, ++ const unsigned char *iv, unsigned int ivlen); ++ ++int spacc_symc_addbuf(unsigned int chn_num, ++ unsigned long buf_phy, ++ unsigned int buf_size, ++ spacc_buf_type_en type, ++ unsigned int ctrl); ++ ++int spacc_symc_addctrl(unsigned int chn_num, spacc_buf_type_en type, unsigned int ctrl); ++ ++int spacc_symc_config(unsigned int chn_num, const spacc_symc_config_s *symc_cfg, ++ unsigned char sm1_round_num, unsigned char hard_key); ++ ++int spacc_symc_start(unsigned int chn_num, unsigned int decrypt, unsigned int iv_set_flag); ++void spacc_symc_restart(unsigned int chn_num, unsigned int iv_set_flag); ++ ++unsigned int spacc_symc_is_free(unsigned int chn_num); ++ ++unsigned int spacc_symc_done_notify(void); ++ ++unsigned int spacc_symc_done_try(unsigned int chn_num); ++ ++unsigned int spacc_symc_get_err_code(unsigned int chn_num, unsigned int *src_addr, unsigned int *dst_addr); ++ ++/**************************** DIGEST API ********************************/ ++int spacc_digest_config(unsigned int chn_num, ++ digest_alg_en digest_alg, ++ digest_mode_en digest_mode, ++ unsigned char hard_key); ++ ++void spacc_digest_get_init_val(digest_alg_en digest_alg, unsigned int state[16]); /* 16 */ ++ ++int spacc_digest_addbuf(unsigned int chn_num, ++ unsigned long buf_phy, ++ unsigned int buf_size, ++ unsigned int ctrl); ++ ++int spacc_digest_addctrl(unsigned int chn_num, unsigned int ctrl); ++ ++int spacc_digest_start(unsigned int chn_num, spacc_ctrl_en enCtrl, const unsigned int *state); ++ ++unsigned int spacc_digest_done_notify(void); ++unsigned int spacc_digest_done_try(unsigned int chn_num); ++ ++int spacc_digest_get(unsigned int chn_num, unsigned int *digest); ++ ++unsigned int spacc_digest_get_err_code(unsigned int chn_num, unsigned int *src_addr); ++ ++/* proc function begin */ ++#ifndef DISABLE_DEBUG_INFO ++td_s32 spacc_symc_proc_status(cipher_chn_status_s *cipher_statue); ++/* proc function end */ ++#endif ++ ++#endif /* SPACC_BODY_H */ +diff --git a/product/security_subsys/cipher/v2/drv/spacc/spacc_intf.c b/product/security_subsys/cipher/v2/drv/spacc/spacc_intf.c +new file mode 100644 +index 0000000..9a6b109 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/spacc/spacc_intf.c +@@ -0,0 +1,2197 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "spacc_intf.h" ++#include ++#include "cipher_adapt.h" ++#include "spacc_body.h" ++#include "spacc_union_define.h" ++#include "drv_klad.h" ++#include "cipher_osal.h" ++#include "cipher_ext.h" ++ ++#define SPACC_MAX_CHN 8 ++#define AES_BLOCK_SIZE 16 ++#define SPACC_TIME_OUT 100000 ++#define SPACC_PAD_BUF_SIZE 128 ++#define HASH_RESULT_MAX_LEN 64 ++ ++#define CHN_0_CIPHER_IV (g_cipher_reg_base + 0x0000) ++#define CHN_0_CIPHER_DOUT (g_cipher_reg_base + 0x0080) ++#define CHN_0_CIPHER_KEY (g_cipher_reg_base + 0x0100) ++#define CHN_0_SM1_SK (g_cipher_reg_base + 0x0200) ++#define ODD_EVEN_KEY_SEL (g_cipher_reg_base + 0x0290) ++#define HDCP_MODE_CTRL (g_cipher_reg_base + 0x0300) ++#define SEC_CHN_CFG (g_cipher_reg_base + 0x0304) ++#define CALC_ST0 (g_cipher_reg_base + 0x0318) ++#define CALC_ERR (g_cipher_reg_base + 0x0320) ++#define CHN_0_CCM_GCM_TAG (g_cipher_reg_base + 0x0380) ++#define CHN_0_CIPHER_CTRL (g_cipher_reg_base + 0x0400) ++#define CIPHER_INT_RAW (g_cipher_reg_base + 0x040c) ++#define CHN_0_CIPHER_DIN (g_cipher_reg_base + 0x0420) ++ ++#define SYMC_INT_LEVEL 100 /* (SPACC_MAX_DEPTH / 2) */ ++ ++typedef td_void (*func_cipher_callback)(td_u32); ++ ++typedef struct { ++ cipher_mmz_buf_t mmz_buf; ++} spacc_env_s; ++ ++typedef struct { ++ td_u8 *src_vir; ++ td_u8 *dest_vir; ++ td_u8 *aad_vir; ++ ++ cipher_mmz_buf_t src_mmz_buf; ++ cipher_mmz_buf_t dest_mmz_buf; ++ cipher_mmz_buf_t aad_mmz_buf; ++} spacc_mmz_s; ++ ++typedef struct { ++ td_bool is_open; ++ td_u32 hard_num; ++ td_u32 block_size; ++ ++ td_bool symc_done; ++ CIPHER_QUEUE_HEAD queue; ++ ++ ot_cipher_data *node_list; ++ td_u32 node_num; ++ td_u32 node_cur; ++ td_u32 total_len; ++ ++ td_u8 *pad_vir_addr; ++ td_size_t pad_phy_addr; ++ ++ td_u32 data_size; ++ const td_void* which_file; ++ ++ cipher_config_ctrl_ex_s ctrl_ex; ++ ++ func_cipher_callback callback; ++} spacc_symc_chn_s; ++ ++typedef struct { ++ td_bool is_open; ++ td_u32 hard_num; ++ td_u32 node_num; ++ td_u32 node_cur; ++ td_u32 block_size; ++ ++ td_bool digest_done; ++ CIPHER_QUEUE_HEAD queue; ++ ++ func_cipher_callback callback; ++ ++ td_u32 data_size; ++ const td_void* which_file; ++} spacc_digest_chn_s; ++ ++typedef struct { ++ td_u32 src_phys_addr; ++ td_u32 dst_phys_addr; ++ td_u32 byte_length; ++ td_bool is_use_odd_key; ++} cipher_data_compat_s; ++ ++CIPHER_MUTEX g_symc_mutex; ++CIPHER_MUTEX g_digest_mutex; ++static spacc_env_s g_spacc_env; ++static spacc_symc_chn_s g_symc_chn[SPACC_MAX_CHN]; ++static spacc_digest_chn_s g_digest_chn[SPACC_MAX_CHN]; ++static td_void* g_cipher_reg_base; ++ ++static td_s32 spacc_check_handle(td_handle ci_handle) ++{ ++ if ((td_handle_get_modid(ci_handle) != OT_ID_CIPHER) || \ ++ (td_handle_get_private_data(ci_handle) != 0)) { ++ ot_err_cipher("invalid cipher handle 0x%x\n", ci_handle); ++ cipher_mutex_unlock(&g_symc_mutex); ++ return OT_ERR_CIPHER_INVALID_HANDLE; ++ } ++ if (td_handle_get_chnid(ci_handle) >= SPACC_MAX_CHN) { ++ ot_err_cipher("chan %d is too large, max: %d\n", \ ++ td_handle_get_chnid(ci_handle), SPACC_MAX_CHN); ++ cipher_mutex_unlock(&g_symc_mutex); ++ return OT_ERR_CIPHER_INVALID_HANDLE; ++ } ++ if (g_symc_chn[td_handle_get_chnid(ci_handle)].is_open == TD_FALSE) { ++ ot_err_cipher("chan %d is not open\n", td_handle_get_chnid(ci_handle)); ++ cipher_mutex_unlock(&g_symc_mutex); ++ return OT_ERR_CIPHER_INVALID_HANDLE; ++ } ++ return TD_SUCCESS; ++} ++ ++#ifdef INT_ENABLE ++CRYPTO_IRQRETURN_T drv_cipher_isr(td_s32 irq, td_void *dev_id) ++{ ++ td_u32 chn_mask, i; ++ ++ chn_mask = spacc_symc_done_notify(); ++ ot_info_cipher("SPACC ISR IRQ: %d, chn_mask 0x%x\n", irq, chn_mask); ++ ++ for (i = CIPHER_PKG_N_CHN_MIN; i <= CIPHER_PKG_N_CHN_MAX; i++) { ++ if ((chn_mask >> i) & 0x01) { ++ if (g_symc_chn[i].callback) { ++ g_symc_chn[i].callback(i); ++ } else { ++ g_symc_chn[i].symc_done = TD_TRUE; ++ ot_info_cipher("chn %d wake up\n", i); ++ cipher_queue_wait_up(&g_symc_chn[i].queue); ++ } ++ } ++ } ++ ++ chn_mask = spacc_digest_done_notify(); ++ for (i = CIPHER_PKG_N_CHN_MIN; i <= CIPHER_PKG_N_CHN_MAX; i++) { ++ if ((chn_mask >> i) & 0x01) { ++ if (g_digest_chn[i].callback) { ++ g_digest_chn[i].callback(i); ++ } else { ++ g_digest_chn[i].digest_done = TD_TRUE; ++ cipher_queue_wait_up(&g_digest_chn[i].queue); ++ } ++ } ++ } ++ ++ return CIPHER_IRQ_HANDLED; ++} ++#endif ++ ++static td_s32 drv_cipher_reset(td_void) ++{ ++ td_u32 *pvirt = TD_NULL; ++ td_u32 spacc_stat = 0; ++ ++ pvirt = cipher_ioremap_nocache(CIPHER_SPACC_CRG_ADDR_PHY, 16); /* 16 */ ++ if (pvirt == TD_NULL) { ++ ot_err_cipher("ioremap_nocache phy addr err:%x.\n", CIPHER_SPACC_CRG_ADDR_PHY); ++ return TD_FAILURE; ++ } ++ ++ /* open clock, reset */ ++ hal_cipher_read_reg(CIPHER_SPACC_CRG_ADDR_PHY, &spacc_stat); ++ spacc_stat |= SPACC_CRG_CLOCK_BIT; ++ spacc_stat |= SPACC_CRG_RESET_BIT; ++ hal_cipher_write_reg(CIPHER_SPACC_CRG_ADDR_PHY, spacc_stat); ++ cipher_udelay(10); /* 10us */ ++ ++ /* cancel reset */ ++ spacc_stat &= ~SPACC_CRG_RESET_BIT; ++ hal_cipher_write_reg(CIPHER_SPACC_CRG_ADDR_PHY, spacc_stat); ++ ++ cipher_iounmap(pvirt); ++ pvirt = TD_NULL; ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 drv_cipher_init(td_void) ++{ ++ td_s32 ret; ++ td_size_t size_addr = 0; ++ td_u32 i; ++ u_sec_chn_cfg sec_cfg; ++ ++ cipher_mutex_init(&g_symc_mutex); ++ ++ g_cipher_reg_base = cipher_ioremap_nocache(CIPHER_CIPHER_REG_BASE_ADDR_PHY, 0x2000); ++ chk_func_fail_return(g_cipher_reg_base == TD_NULL, TD_FAILURE, cipher_ioremap_nocache); ++ ++ ret = drv_cipher_reset(); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_reset); ++ ++ (td_void)memset_s(&g_spacc_env, sizeof(spacc_env_s), 0, sizeof(spacc_env_s)); ++ (td_void)memset_s(&g_symc_chn, sizeof(g_symc_chn), 0, sizeof(g_symc_chn)); ++ (td_void)memset_s(&g_digest_chn, sizeof(g_digest_chn), 0, sizeof(g_digest_chn)); ++ ++ g_spacc_env.mmz_buf.mmz_size = spacc_get_node_list_size() + SPACC_PAGE_SIZE; ++ ret = cipher_mmz_alloc_remap("CIPHER_ChnBuf", &g_spacc_env.mmz_buf); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, cipher_mmz_alloc_remap); ++ ++ for (i = CIPHER_PKG_N_CHN_MIN; i <= CIPHER_PKG_N_CHN_MAX; i++) { ++ cipher_queue_init(&g_symc_chn[i].queue); ++ cipher_queue_init(&g_digest_chn[i].queue); ++ g_symc_chn[i].pad_phy_addr = g_spacc_env.mmz_buf.start_phy_addr + SPACC_PAD_BUF_SIZE * i; ++ g_symc_chn[i].pad_vir_addr = g_spacc_env.mmz_buf.start_vir_addr + SPACC_PAD_BUF_SIZE * i; ++ g_symc_chn[i].hard_num = i; ++ g_digest_chn[i].hard_num = i; ++ } ++ ++ ret = spacc_init(g_cipher_reg_base, size_addr, ++ g_spacc_env.mmz_buf.start_phy_addr + SPACC_PAGE_SIZE, ++ g_spacc_env.mmz_buf.start_vir_addr + SPACC_PAGE_SIZE); ++ if (ret != TD_SUCCESS) ++ goto unmap_mmz; ++ ++ hal_cipher_read_reg(SEC_CHN_CFG, &sec_cfg.u32); ++ sec_cfg.bits.cipher_sec_chn_cfg |= 0x01; ++ sec_cfg.bits.hash_sec_chn_cfg |= 0x01; ++ hal_cipher_write_reg(SEC_CHN_CFG, sec_cfg.u32); ++ ++#ifdef INT_ENABLE ++ ret = cipher_request_irq(CIPHER_IRQ_NUMBER, drv_cipher_isr, "cipher"); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("Irq request failure, ret=%d, irq = %d", ret, CIPHER_IRQ_NUMBER); ++ goto unmap_mmz; ++ } ++#endif ++ return ret; ++ ++unmap_mmz: ++ cipher_mmz_release_unmap(&g_spacc_env.mmz_buf); ++ return ret; ++} ++ ++td_void drv_cipher_deinit(td_void) ++{ ++ if (spacc_deinit() != TD_SUCCESS) ++ ot_err_cipher("spacc deinit failed.\n"); ++ ++#ifdef INT_ENABLE ++ cipher_free_irq(CIPHER_IRQ_NUMBER, "cipher"); ++#endif ++ ++ cipher_mmz_release_unmap(&g_spacc_env.mmz_buf); ++ ++ if (g_cipher_reg_base != TD_NULL) { ++ cipher_iounmap(g_cipher_reg_base); ++ g_cipher_reg_base = TD_NULL; ++ } ++ ++ return; ++} ++ ++td_s32 ot_drv_cipher_create_handle(cipher_handle_s *ci_handle, const td_void *file) ++{ ++ td_u32 i; ++ td_s32 ret = TD_SUCCESS; ++ ++ if (ci_handle == TD_NULL) { ++ ot_err_cipher("Invalid params!\n"); ++ return TD_FAILURE; ++ } ++ ++ if (ci_handle->cipher_atts.cipher_type > OT_CIPHER_TYPE_COPY_AVOID) { ++ ot_err_cipher("Invalid cipher type!\n"); ++ return TD_FAILURE; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ if (ci_handle->cipher_atts.cipher_type == OT_CIPHER_TYPE_COPY_AVOID) { ++ if (g_symc_chn[0].is_open == TD_FALSE) ++ i = 0; ++ else ++ i = SPACC_MAX_CHN; ++ } else { ++ for (i = CIPHER_PKG_N_CHN_MIN; i <= CIPHER_PKG_N_CHN_MAX; i++) { ++ if (g_symc_chn[i].is_open == TD_FALSE) ++ break; ++ } ++ } ++ ++ if (i <= CIPHER_PKG_N_CHN_MAX) { ++ g_symc_chn[i].is_open = TD_TRUE; ++ g_symc_chn[i].which_file = file; ++ g_symc_chn[i].callback = TD_NULL; ++ g_symc_chn[i].node_list = TD_NULL; ++ g_symc_chn[i].node_num = 0; ++ ci_handle->ci_handle = td_handle_init(OT_ID_CIPHER, 0, i); ++ } else { ++ ot_err_cipher("No more cipher chan left.\n"); ++ ret = TD_FAILURE; ++ } ++ ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_param_check(symc_alg_en symc_alg, ++ symc_mode_en symc_mode, ++ symc_dat_width_en symc_width, ++ ot_cipher_sm1_round sm1_round) ++{ ++ /* the mode depend on alg, which limit to hardware ++ * des/3des support ecb/cbc/cfb/ofb ++ * aes support ecb/cbc/cfb/ofb/ctr/ccm/gcm ++ * sm1 support ecb/cbc/cfb/ofb ++ * sm4 support ecb/cbc/ctr ++ */ ++ if ((symc_alg == SYMC_ALG_DES) || (symc_alg == SYMC_ALG_3DES) || (symc_alg == SYMC_ALG_SM1)) { ++ if ((symc_mode != SYMC_MODE_ECB) && (symc_mode != SYMC_MODE_CBC) && ++ (symc_mode != SYMC_MODE_CFB) && (symc_mode != SYMC_MODE_OFB)) { ++ ot_err_cipher("Invalid alg %d and mode: %d\n", symc_alg, symc_mode); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else if (symc_alg == SYMC_ALG_SM4) { ++ if ((symc_mode != SYMC_MODE_ECB) && (symc_mode != SYMC_MODE_CBC) && (symc_mode != SYMC_MODE_CTR)) { ++ ot_err_cipher("Invalid alg %d and mode %d\n", symc_alg, symc_mode); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } ++ ++ /* the bit width depend on alg and mode, which limit to hardware ++ * des/3des with cfb/ofb support bit1, bit8, bit 64. ++ * aes with cfb/ofb only support bit128. ++ * sm1 with ofb only support bit128, cfb support bit1, bit8, bit 64. ++ */ ++ if ((symc_alg == SYMC_ALG_DES) || (symc_alg == SYMC_ALG_3DES)) { ++ if ((symc_mode == SYMC_MODE_CFB) || (symc_mode == SYMC_MODE_OFB)) { ++ if (symc_width != SYMC_DAT_WIDTH_64 && ++ symc_width != SYMC_DAT_WIDTH_8 && symc_width != SYMC_DAT_WIDTH_1) { ++ ot_err_cipher("Invalid mode %d and bit width %d\n", symc_mode, symc_width); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } ++ } ++ ++ if (symc_alg == SYMC_ALG_AES) { ++ if (((symc_mode == SYMC_MODE_CFB) && (symc_width >= SYMC_DAT_WIDTH_COUNT)) || ++ ((symc_mode == SYMC_MODE_OFB) && (symc_width != SYMC_DAT_WIDTH_128))) { ++ ot_err_cipher("Invalid alg %d mode %d and width %d\n", symc_alg, symc_mode, symc_width); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } ++ ++ if (symc_alg == SYMC_ALG_SM1) { ++ if (((symc_mode == SYMC_MODE_OFB) && (symc_width != SYMC_DAT_WIDTH_128)) || ++ ((symc_mode == SYMC_MODE_CFB) && (symc_width >= SYMC_DAT_WIDTH_COUNT))) { ++ ot_err_cipher("Invalid alg %d mode %d and width %d\n", symc_alg, symc_mode, symc_width); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (sm1_round >= OT_CIPHER_SM1_ROUND_BUTT) { ++ ot_err_cipher("Invalid alg %d and Sm1Round %d\n", symc_alg, sm1_round); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_match_alg(const cipher_config_ctrl_ex_s *config, symc_alg_en *symc_alg, td_u32 *block_size) ++{ ++ /* set alg and block size */ ++ switch (config->ci_alg) { ++ case OT_CIPHER_ALG_AES: ++ *symc_alg = SYMC_ALG_AES; ++ *block_size = 16; /* 16 block size */ ++ break; ++ case OT_CIPHER_ALG_DMA: ++ *symc_alg = SYMC_ALG_NULL_CIPHER; ++ *block_size = 16; /* 16 block size */ ++ break; ++ case OT_CIPHER_ALG_SM1: ++ *symc_alg = SYMC_ALG_SM1; ++ *block_size = 16; /* 16 block size */ ++ break; ++ case OT_CIPHER_ALG_SM4: ++ *symc_alg = SYMC_ALG_SM4; ++ *block_size = 16; /* 16 block size */ ++ break; ++ default: ++ ot_err_cipher("Invalid alg: 0x%x\n", config->ci_alg); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_match_work_mode(const cipher_config_ctrl_ex_s *config, symc_mode_en *symc_mode) ++{ ++ switch (config->work_mode) { ++ case OT_CIPHER_WORK_MODE_ECB: ++ *symc_mode = SYMC_MODE_ECB; ++ break; ++ case OT_CIPHER_WORK_MODE_CBC: ++ *symc_mode = SYMC_MODE_CBC; ++ break; ++ case OT_CIPHER_WORK_MODE_CFB: ++ *symc_mode = SYMC_MODE_CFB; ++ break; ++ case OT_CIPHER_WORK_MODE_OFB: ++ *symc_mode = SYMC_MODE_OFB; ++ break; ++ case OT_CIPHER_WORK_MODE_CTR: ++ *symc_mode = SYMC_MODE_CTR; ++ break; ++ case OT_CIPHER_WORK_MODE_CCM: ++ *symc_mode = SYMC_MODE_CCM; ++ break; ++ case OT_CIPHER_WORK_MODE_GCM: ++ *symc_mode = SYMC_MODE_GCM; ++ break; ++ default: ++ ot_err_cipher("Invalid mode: 0x%x\n", config->work_mode); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_match_bit_width(const cipher_config_ctrl_ex_s *config, symc_dat_width_en *symc_width) ++{ ++ /* set the bit width which depend on alg and mode */ ++ if ((config->work_mode == OT_CIPHER_WORK_MODE_CFB) || ++ (config->work_mode == OT_CIPHER_WORK_MODE_OFB)) { ++ switch (config->bit_width) { ++ case OT_CIPHER_BIT_WIDTH_64BIT: ++ *symc_width = SYMC_DAT_WIDTH_64; ++ break; ++ case OT_CIPHER_BIT_WIDTH_8BIT: ++ *symc_width = SYMC_DAT_WIDTH_8; ++ break; ++ case OT_CIPHER_BIT_WIDTH_1BIT: ++ *symc_width = SYMC_DAT_WIDTH_1; ++ break; ++ case OT_CIPHER_BIT_WIDTH_128BIT: ++ *symc_width = SYMC_DAT_WIDTH_128; ++ break; ++ default: ++ ot_err_cipher("Invalid width: 0x%x, mode 0x%x, alg 0x%x\n", ++ config->bit_width, config->work_mode, config->ci_alg); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else { ++ *symc_width = SYMC_DAT_WIDTH_128; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_match_key_len(const cipher_config_ctrl_ex_s *config, td_u32 *key_len) ++{ ++ if (config->ci_alg == OT_CIPHER_ALG_AES) { ++ switch (config->key_len) { ++ case OT_CIPHER_KEY_AES_128BIT: ++ *key_len = 16; /* 16 key len */ ++ break; ++ case OT_CIPHER_KEY_AES_192BIT: ++ *key_len = 24; /* 24 key len */ ++ break; ++ case OT_CIPHER_KEY_AES_256BIT: ++ *key_len = 32; /* 32 key len */ ++ break; ++ default: ++ ot_err_cipher("Invalid key len: 0x%x\n", config->key_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else if (config->ci_alg == OT_CIPHER_ALG_SM1) { ++ *key_len = 48; /* 48 key len */ ++ } else if (config->ci_alg == OT_CIPHER_ALG_SM4) { ++ *key_len = 16; /* 16 key len */ ++ } else if (config->ci_alg != OT_CIPHER_ALG_DMA) { ++ ot_err_cipher("Invalid cipher alg: %d\n", config->ci_alg); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ return TD_SUCCESS; ++} ++ ++/* change the unf params to drive params */ ++static td_s32 drv_cipher_param(const cipher_config_ctrl_ex_s *config, ++ spacc_symc_config_s *symc_cfg, td_u32 *block_size) ++{ ++ td_s32 ret; ++ ++ /* set alg and block size */ ++ ret = drv_cipher_match_alg(config, &symc_cfg->symc_alg, block_size); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_match_alg); ++ ++ /* set the mode which depend on alg */ ++ ret = drv_cipher_match_work_mode(config, &symc_cfg->symc_mode); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_match_work_mode); ++ ++ /* set the bit width which depend on alg and mode */ ++ ret = drv_cipher_match_bit_width(config, &symc_cfg->symc_width); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_match_bit_width); ++ ++ /* set the key length depend on alg ++ * des/3des support 2key and 3key ++ * aes support 128, 192, and 256 ++ * sm1 support ak/ek/sk ++ * sm4 support 128 ++ */ ++ ret = drv_cipher_match_key_len(config, &symc_cfg->key_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_match_key_len); ++ ++ if (config->change_flags.bits_iv > OT_CIPHER_IV_CHG_ALL_PKG) { ++ ot_err_cipher("Invalid IV Change Flags: 0x%x\n", config->change_flags.bits_iv); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if ((config->change_flags.bits_iv == OT_CIPHER_IV_CHG_ALL_PKG) && ++ ((config->work_mode == OT_CIPHER_WORK_MODE_CCM) || ++ (config->work_mode == OT_CIPHER_WORK_MODE_GCM))) { ++ ot_err_cipher("Invalid IV Change Flags: 0x%x\n", config->change_flags.bits_iv); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (config->key_by_ca && (config->ca_type >= OT_CIPHER_KEY_SRC_BUTT)) { ++ ot_err_cipher("Invalid CA Type: 0x%x\n", config->ca_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return drv_cipher_param_check(symc_cfg->symc_alg, ++ symc_cfg->symc_mode, symc_cfg->symc_width, config->sm1_round); ++} ++ ++static td_s32 drv_cipher_config_ccm(const cipher_config_ctrl_ex_s *config, ++ td_u32 *iv, td_u32 ilen, td_u32 *real_ilen) ++{ ++ td_u8 *buf = TD_NULL; ++ ++ /* The octet lengths of N are denoted n, ++ * The octet length of the binary represen tation of the ++ * octet length of the payload denoted q, ++ * n is an element of {7, 8, 9, 10, 11, 12, 13}, ++ * equation: n + q = 15 ++ * here the string of N is config->iv, and n is config->iv_len. ++ */ ++ if ((config->iv_len < 7) || (config->iv_len > 13)) { /* 7, 13 iv len range */ ++ ot_err_cipher("Invalid IV LEN: 0x%x\n", config->iv_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ /* the parameter t denotes the octet length of T(tag) ++ * t is an element of { 4, 6, 8, 10, 12, 14, 16} ++ * here t is config->tag_len ++ */ ++ if ((config->tag_len & 0x01) || (config->tag_len < 4) || (config->tag_len > 16)) { /* 4, 16 tag len value */ ++ ot_err_cipher("Invalid TAG LEN: 0x%x\n", config->tag_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ /* Formatting of the Counter Blocks(IV for CTR) ++ * ++ * According to the CCM spec, the counter is equivalent to ++ * a formatting of the counter index i into a complete data block. ++ * The counter blocks Ctri are formatted as shown below: ++ * | Octet number: 0 1 ... 15-q 16-q ... 15 ++ * | Contents: Flags N [i] ++ * Within each block Ctri, the N is get from config->iv, n + q = 15, ++ * so the q equal to 15 - config->iv_len. ++ * the [i] is the block conut start with 0, ++ * In the Flags field, Bits 0, 1, and 2 contain the encoding of q - 1, ++ * others bits shall be set to 0. ++ * so the first byte of IV shall be q -1, that is 15 - config->iv_len - 1 ++ */ ++ buf = (td_u8 *)iv; ++ (td_void)memset_s(buf, ilen, 0, ilen); ++ buf[0] = 14 - config->iv_len; /* equation: IV[0] = q - 1 = 15 - n - 1, 14 */ ++ if (memcpy_s(buf + 1, ilen - 1, config->iv, config->iv_len) != EOK) { ++ ot_err_cipher("call failed memcpy_s\n"); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ *real_ilen = config->iv_len + 1; ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_config_gcm(const cipher_config_ctrl_ex_s *config, ++ td_u32 *iv, td_u32 ilen, td_u32 *real_ilen) ++{ ++ /* According to the GCM spec, the IVLen >= 1, typical equal to 12, ++ * but limit to hard logic devising, the IVLen can't large than 16. ++ */ ++ if (config->iv_len > ilen) { ++ ot_err_cipher("Invalid IV LEN: 0x%x\n", config->iv_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ if (memcpy_s(iv, ilen, config->iv, config->iv_len) != EOK) { ++ ot_err_cipher("call failed memcpy_s\n"); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ *real_ilen = config->iv_len; ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_config_chn_0(const spacc_symc_chn_s *channel, const cipher_config_ctrl_ex_s *config, ++ const spacc_symc_config_s *symc_cfg, const td_u32 *iv, td_u32 iv_len) ++{ ++ u_chan0_cipher_ctrl chn0_ctrl; ++ td_u32 klen = 0; ++ td_u32 i; ++ td_s32 ret = TD_SUCCESS; ++ ++ hal_cipher_write_reg(ODD_EVEN_KEY_SEL, 0x00); ++ if (config->key_by_ca == TD_FALSE) { ++ for (i = 0; i < 8; i++) /* 8 */ ++ hal_cipher_write_reg(CHN_0_CIPHER_KEY + i * 4, config->key[i]); /* 4 */ ++ } else { ++ ret = drv_cipher_klad_load_key(0, channel->ctrl_ex.ca_type, ++ OT_CIPHER_KLAD_TARGET_AES, (td_u8 *)config->key, symc_cfg->key_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_klad_load_key); ++ } ++ ++ if (symc_cfg->symc_alg == SYMC_ALG_SM1) { ++ for (i = 0; i < 4; i++) /* 4 */ ++ hal_cipher_write_reg(CHN_0_SM1_SK + i * 4, config->key[i + 8]); /* 4, 8 */ ++ } ++ for (i = 0; i < 4; i++) /* 4 */ ++ hal_cipher_write_reg(CHN_0_CIPHER_IV + i * 4, iv[i]); /* 4 */ ++ ++ if (symc_cfg->symc_alg == SYMC_ALG_AES) ++ klen = symc_cfg->key_len / 8 - 2; /* 8, 2 */ ++ else if (symc_cfg->symc_alg == SYMC_ALG_3DES) ++ klen = (symc_cfg->key_len == 16 ? 3 : 2); /* 16, 3, 2 */ ++ ++ chn0_ctrl.u32 = 0x00; ++ chn0_ctrl.bits.sym_ch0_sm1_round_num = config->sm1_round; ++ chn0_ctrl.bits.sym_ch0_ivin_sel = 0x01; ++ chn0_ctrl.bits.sym_ch0_key_sel = config->key_by_ca; ++ chn0_ctrl.bits.sym_ch0_key_length = klen; ++ chn0_ctrl.bits.sym_ch0_dat_width = symc_cfg->symc_width; ++ chn0_ctrl.bits.sym_ch0_alg_sel = symc_cfg->symc_alg; ++ chn0_ctrl.bits.sym_ch0_alg_mode = symc_cfg->symc_mode; ++ if (config->work_mode == OT_CIPHER_WORK_MODE_GCM) ++ chn0_ctrl.bits.sym_ch0_gcm_iv_len = config->iv_len - 1; ++ hal_cipher_write_reg(CHN_0_CIPHER_CTRL, chn0_ctrl.u32); ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_config_chn_n(const spacc_symc_chn_s *channel, const cipher_config_ctrl_ex_s *config, ++ const spacc_symc_config_s *symc_cfg, const td_u32 *iv, td_u32 iv_len) ++{ ++ td_s32 ret; ++ ++ ret = spacc_symc_config(channel->hard_num, symc_cfg, config->sm1_round, config->key_by_ca); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_config); ++ ++ if (config->key_by_ca == TD_FALSE) { /* OT_CIPHER_KEY_SRC_USER, CPU config key */ ++ ret = spacc_symc_setkey(channel->hard_num, config->key, config->odd_key, symc_cfg->key_len); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_setkey); ++#ifdef CIPHER_KLAD_SUPPORT ++ } else { /* OT_CIPHER_KEY_SRC_KLAD, KLAD load key */ ++ ret = drv_cipher_klad_load_key(channel->hard_num, channel->ctrl_ex.ca_type, ++ OT_CIPHER_KLAD_TARGET_AES, (td_u8 *)config->key, symc_cfg->key_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_klad_load_key); ++#endif ++ } ++ ++ ret = spacc_symc_setiv(channel->hard_num, (td_u8 *)iv, iv_len); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_setiv); ++ return ret; ++} ++ ++static td_s32 drv_cipher_config_chn(td_u32 soft_chn_id, const cipher_config_ctrl_ex_s *config) ++{ ++ td_s32 ret; ++ td_u32 iv[4]; /* 4 iv arr size */ ++ td_u32 real_ilen = 0; ++ spacc_symc_config_s symc_cfg; ++ spacc_symc_chn_s *channel = &g_symc_chn[soft_chn_id]; ++ ++ (td_void)memset_s(iv, sizeof(iv), 0, sizeof(iv)); ++ (td_void)memset_s(&symc_cfg, sizeof(symc_cfg), 0, sizeof(symc_cfg)); ++ (td_void)memcpy_s(&channel->ctrl_ex, sizeof(cipher_config_ctrl_ex_s), config, sizeof(cipher_config_ctrl_ex_s)); ++ ++ ret = drv_cipher_param(config, &symc_cfg, &channel->block_size); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_param); ++ ++ if (config->work_mode == OT_CIPHER_WORK_MODE_CCM) { ++ ret = drv_cipher_config_ccm(config, iv, sizeof(iv), &real_ilen); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_config_ccm); ++ } else if (config->work_mode == OT_CIPHER_WORK_MODE_GCM) { ++ ret = drv_cipher_config_gcm(config, iv, sizeof(iv), &real_ilen); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_config_gcm); ++ } else if (config->work_mode <= OT_CIPHER_WORK_MODE_CTR) { ++ /* IV length, 16 for aes, 8 for des/3des */ ++ ret = memcpy_s(iv, sizeof(iv), config->iv, sizeof(config->iv)); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ real_ilen = sizeof(config->iv); ++ } else { ++ ot_err_cipher("Invalid mode: 0x%x\n", config->work_mode); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ /* chn 1~7, data transferred by DMA */ ++ if (soft_chn_id != 0) { ++ ret = drv_cipher_config_chn_n(channel, config, &symc_cfg, iv, real_ilen); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_config_chn_n); ++ } else { /* chn 0, data transferred by CPU, spacc_body do not drvie chn0 */ ++ ret = drv_cipher_config_chn_0(channel, config, &symc_cfg, iv, sizeof(iv)); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_config_chn_0); ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_drv_cipher_config_chn_ex(td_handle ci_handle, const cipher_config_ctrl_ex_s *config) ++{ ++ td_s32 ret; ++ td_u32 soft_chn_id; ++ ++ if (config == TD_NULL) { ++ ot_err_cipher("Invalid params!\n"); ++ return TD_FAILURE; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(ci_handle); ++ if (ret != TD_SUCCESS) ++ return ret; ++ ++ soft_chn_id = td_handle_get_chnid(ci_handle); ++ ret = drv_cipher_config_chn(soft_chn_id, config); ++ ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++td_s32 ot_drv_cipher_destory_handle(td_handle cipher_chn) ++{ ++ td_u32 soft_chn_id; ++ td_s32 ret; ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(cipher_chn); ++ if (ret != TD_SUCCESS) ++ return ret; ++ soft_chn_id = td_handle_get_chnid(cipher_chn); ++ ++ g_symc_chn[soft_chn_id].is_open = TD_FALSE; ++ ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++/* check error code ++ * bit0: klad_key_use_err ++ * bit1: alg_len_err ++ * bit2: smmu_page_unvlid ++ */ ++static td_s32 drv_cipher_check_error_code(td_u32 hard_num, td_u32 wait, td_u32 src_addr) ++{ ++ td_s32 ret = TD_SUCCESS; ++ ++ if (wait & 0x01) { ++ ot_err_cipher("hash error: klad_key_use_err, chn %d !!!\n", hard_num); ++ ret = TD_FAILURE; ++ } ++ if (wait & 0x02) { ++ ot_err_cipher("hash error: alg_len_err, chn %d !!!\n", hard_num); ++ ret = TD_FAILURE; ++ } ++ if (wait & 0x04) { ++ ot_err_cipher("hash error: smmu_page_unvlid, chn %d !!!\n", hard_num); ++ ot_err_cipher("SRC ADDR: 0x%x\n", src_addr); ++ ret = TD_FAILURE; ++ } ++ return ret; ++} ++ ++static td_s32 drv_cipher_symc_wait_done(const spacc_symc_chn_s *channel, td_u32 time_out) ++{ ++ td_s32 ret = TD_SUCCESS; ++ td_u32 wait; ++ td_u32 src_addr, dst_addr; ++ ++#ifdef INT_ENABLE ++ if (cipher_queue_wait_timeout(&channel->queue, &channel->symc_done, time_out) != TD_SUCCESS) { ++ ot_err_cipher("Encrypt time out! Chn %d, CIPHER_IRQ_NUMBER: %d\n", channel->hard_num, CIPHER_IRQ_NUMBER); ++ ret = TD_FAILURE; ++ } ++#else ++ for (; time_out > 0; time_out--) { ++ if (spacc_symc_done_try(channel->hard_num)) { ++ break; ++ } ++ cipher_udelay(10); /* 10us */ ++ } ++ if (time_out == 0) { ++ ot_err_cipher("symc time out!\n"); ++ ret = TD_FAILURE; ++ } ++#endif ++ ++ wait = spacc_symc_get_err_code(channel->hard_num, &src_addr, &dst_addr); ++ if (drv_cipher_check_error_code(channel->hard_num, wait, src_addr) != TD_SUCCESS) ++ ret = TD_FAILURE; ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_digest_wait_done(const spacc_digest_chn_s *channel) ++{ ++ td_s32 ret = TD_SUCCESS; ++ td_u32 wait; ++ td_u32 time_out = SPACC_TIME_OUT; ++ td_u32 src_addr; ++ ++#ifdef INT_ENABLE ++ if (channel->data_size > 100 * 1024) { /* 100, 1024 */ ++ ret = cipher_queue_wait_timeout(&channel->queue, &channel->digest_done, time_out); ++ if (ret <= 0) { ++ ot_err_cipher("hash time out! CIPHER_IRQ_NUMBER: %d\n", CIPHER_IRQ_NUMBER); ++ ret = TD_FAILURE; ++ } ++ } else { ++ time_out = 0; ++ while (time_out++ < SPACC_TIME_OUT) { ++ if (channel->digest_done != TD_FALSE) ++ break; ++ cipher_udelay(10); /* 10us */ ++ } ++ if (time_out >= SPACC_TIME_OUT) { ++ ot_err_cipher("hash time out!\n"); ++ ret = TD_FAILURE; ++ } ++ } ++#else ++ time_out = 0; ++ while (time_out++ < SPACC_TIME_OUT) { ++ if (spacc_digest_done_try(channel->hard_num)) ++ break; ++ cipher_udelay(10); /* 10us */ ++ } ++ if (time_out >= SPACC_TIME_OUT) { ++ ot_err_cipher("hash time out!\n"); ++ ret = TD_FAILURE; ++ } ++#endif ++ ++ /* check error code ++ * bit0: klad_key_use_err ++ * bit1: alg_len_err ++ * bit2: smmu_page_unvlid ++ */ ++ wait = spacc_digest_get_err_code(channel->hard_num, &src_addr); ++ if (drv_cipher_check_error_code(channel->hard_num, wait, src_addr) != TD_SUCCESS) ++ ret = TD_FAILURE; ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_ccm_head_format(const spacc_symc_chn_s *channel, ++ td_u32 enc_len, td_u8 *arr_b, td_u32 blen, td_u32 *b1_len) ++{ ++ td_s32 ret; ++ td_u8 *pbuf = TD_NULL; ++ td_u32 index = 0; ++ ++ /* Format B0 */ ++ /* The leading octet of the first block of the formatting, B0, ++ * contains four flags for control information: two single bits, ++ * called Reserved and Adata, and two strings of three bits, ++ * to encode the values t and q. The encoding of t is [(t -2)/2], ++ * and the encoding of q is [ q-1]. ++ * The ordering of the flags with in the octet is given: ++ * _____________________________________________________ ++ * |Bit number 7 | 6 | 5 4 3 | 2 1 0 | ++ * |Contents Reserved Adata [( t -2)/2] | [q-1] | ++ * ----------------------------------------------------- ++ * The remaining 15 octets of the first block of the formatting are ++ * devoted to the nonce and the binary representation of ++ * the message length in q octets, as given: ++ * _____________________________________________ ++ * |Octet number 0 | 1 ... 15-q | 16-q ... 15 | ++ * |Contents Flags | N | Q | ++ * --------------------------------------------- ++ */ ++ pbuf = arr_b; ++ ret = memset_s(pbuf, blen, 0, AES_BLOCK_SIZE); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ pbuf[index] = (channel->ctrl_ex.alen > 0 ? 1 : 0) << 6; /* Adata, 6 left shift */ ++ pbuf[index] |= ((channel->ctrl_ex.tag_len - 2) / 2) << 3; /* formula: (t - 2) / 2, 3 left shift */ ++ pbuf[index] |= ((15 - channel->ctrl_ex.iv_len) - 1); /* formula: q - 1, n + q = 15 */ ++ index++; ++ ret = memcpy_s(&pbuf[index], blen - index, channel->ctrl_ex.iv, channel->ctrl_ex.iv_len); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ index += channel->ctrl_ex.iv_len; ++ if (index <= 12) { /* 12 index */ ++ index = 12; /* 12 index */ ++ pbuf[index++] = (td_u8)(enc_len >> 24); /* 24 right shift */ ++ pbuf[index++] = (td_u8)(enc_len >> 16); /* 16 right shift */ ++ pbuf[index++] = (td_u8)(enc_len >> 8); /* 8 right shift */ ++ pbuf[index++] = (td_u8)(enc_len); ++ } else if ((index == 13) && (enc_len <= 0xFFFFFF)) { /* 13 index */ ++ pbuf[index++] = (td_u8)(enc_len >> 16); /* 16 right shift */ ++ pbuf[index++] = (td_u8)(enc_len >> 8); /* 8 right shift */ ++ pbuf[index++] = (td_u8)(enc_len); ++ } else if ((index == 14) && (enc_len <= 0xFFFF)) { /* 14 index */ ++ pbuf[index++] = (td_u8)(enc_len >> 8); /* 8 right shift */ ++ pbuf[index++] = (td_u8)(enc_len); ++ } else { ++ ot_err_cipher("Invalid Mlen: 0x%x, q: 0x%x!\n", enc_len, 16 - index); /* 16 */ ++ return TD_FAILURE; ++ } ++ ++ /* Formatting of the Associated Data in B1, the length of A denotes as a */ ++ /* The value a is encoded according to the following three cases: ++ * If 0 < a < 2^16 - 2^8, then a is encoded as a[0..15], i.e., two octets. ++ * If 2^16 - 2^8 <= a < 2^32, then a is encoded as 0xff || 0xfe || a[0..31], i.e., six octets. ++ * If 2^32 <= a < 2^64, then a is encoded as 0xff || 0xff || a[0..63], i.e., ten octets. ++ * For example, if a=2^16, the encoding of a is ++ * 11111111 11111110 00000000 00000001 00000000 00000000. ++ */ ++ pbuf = arr_b + 16; /* 16 offset */ ++ index = 0; ++ if (channel->ctrl_ex.alen > 0) { ++ if (channel->ctrl_ex.alen < (0x10000 - 0x100)) { ++ pbuf[index++] = (td_u8)(channel->ctrl_ex.alen >> 8); /* 8 right shift */ ++ pbuf[index++] = (td_u8)(channel->ctrl_ex.alen); ++ } else { ++ pbuf[index++] = 0xFF; ++ pbuf[index++] = 0xFE; ++ pbuf[index++] = (td_u8)(channel->ctrl_ex.alen >> 24); /* 24 right shift */ ++ pbuf[index++] = (td_u8)(channel->ctrl_ex.alen >> 16); /* 16 right shift */ ++ pbuf[index++] = (td_u8)(channel->ctrl_ex.alen >> 8); /* 8 right shift */ ++ pbuf[index++] = (td_u8)channel->ctrl_ex.alen; ++ } ++ } ++ *b1_len = index; ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_cpu_input(const td_u8 *input, td_u8 *output, td_u32 length, ++ td_u32 block_size, td_u32 ctrl, td_bool set_last) ++{ ++ td_s32 ret; ++ td_u32 buf[4]; /* 4 buf arr size */ ++ td_u32 size, offset; ++ u_chan0_cipher_ctrl chn0_ctrl; ++ u_cipher_int_raw int_raw; ++ ++ chn0_ctrl.u32 = ctrl; ++ ++ chk_formula_fail_return(block_size == 0); ++ ++ for (offset = 0; offset < length; offset += block_size) { ++ td_u32 i; ++ td_u32 time = 0; ++ ++ /* Compute one block, if less than one block, padding with 0 */ ++ size = (offset + block_size) < length ? block_size : length - offset; ++ (td_void)memset_s(buf, sizeof(buf), 0, sizeof(buf)); ++ ret = memcpy_s(buf, sizeof(buf), &input[offset], size); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ for (i = 0; i < block_size / 4; i++) { /* 4 */ ++ hal_cipher_write_reg(CHN_0_CIPHER_DIN + i * 4, buf[i]); /* 4 */ ++ } ++ ++ /* may be needs set last flag when compute last block */ ++ if (((offset + block_size) >= length) && set_last) { ++ chn0_ctrl.bits.sym_ch0_ccm_gcm_pc_last = 0x01; ++ } ++ hal_cipher_write_reg(CHN_0_CIPHER_CTRL, chn0_ctrl.u32); ++ ++ /* start working */ ++ hal_cipher_write_reg(CHN_0_CIPHER_CTRL, chn0_ctrl.u32 | 0x01); ++ ++ /* Waiting compute finished */ ++ hal_cipher_read_reg(CIPHER_INT_RAW, &int_raw.u32); ++ while (!(int_raw.bits.cipher_chn_obuf_raw & 0x01) && (time++ < SPACC_TIME_OUT)) { ++ hal_cipher_read_reg(CIPHER_INT_RAW, &int_raw.u32); ++ } ++ ++ if (time >= SPACC_TIME_OUT) { ++ ot_err_cipher("Chn 0 time out!\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ /* Clean raw interrupt */ ++ int_raw.u32 = 0x00; ++ int_raw.bits.cipher_chn_obuf_raw = 0x01; ++ hal_cipher_write_reg(CIPHER_INT_RAW, int_raw.u32); ++ ++ /* May be needs read output data */ ++ if (output != TD_NULL) { ++ for (i = 0; i < block_size / 4; i++) { /* 4 */ ++ hal_cipher_read_reg(CHN_0_CIPHER_DOUT + i * 4, &buf[i]); /* 4 */ ++ } ++ ret = memcpy_s(&output[offset], length - offset, (td_u8*)buf, size); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ } ++ ++ /* prepare compute next block */ ++ chn0_ctrl.u32 &= ~(0x01 << 15); /* 15 iv set */ ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_cpu_mmz_map(const spacc_symc_chn_s *channel, ++ const cipher_data_s *ci_data, spacc_mmz_s *spacc_mmz) ++{ ++ td_s32 ret; ++ ++ /* mapping phy address of SRC */ ++ spacc_mmz->src_mmz_buf.mmz_size = ci_data->data_length; ++ spacc_mmz->src_mmz_buf.start_phy_addr = ++ make_ulong(ci_data->src_phy_addr, ci_data->src_phy_addr_high); ++ ret = cipher_mmz_map(&spacc_mmz->src_mmz_buf); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("DRV SRC MMZ MAP ERROR!, addr = 0x%x!\n", ci_data->src_phy_addr); ++ return ret; ++ } ++ spacc_mmz->src_vir = spacc_mmz->src_mmz_buf.start_vir_addr; ++ ++ /* mapping phy address of DST */ ++ spacc_mmz->dest_mmz_buf.mmz_size = ci_data->data_length; ++ spacc_mmz->dest_mmz_buf.start_phy_addr = ++ make_ulong(ci_data->dest_phy_addr, ci_data->dest_phy_addr_high); ++ ret = cipher_mmz_map(&spacc_mmz->dest_mmz_buf); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("DRV DEST MMZ MAP ERROR! addr = 0x%x!\n", ci_data->dest_phy_addr); ++ cipher_mmz_unmap(&spacc_mmz->src_mmz_buf); ++ return ret; ++ } ++ spacc_mmz->dest_vir = spacc_mmz->dest_mmz_buf.start_vir_addr; ++ ++ /* mapping phy address of A for CCM/GCM */ ++ if ((channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CCM) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_GCM)) { ++ spacc_mmz->aad_mmz_buf.mmz_size = channel->ctrl_ex.alen; ++ spacc_mmz->aad_mmz_buf.start_phy_addr = ++ make_ulong(channel->ctrl_ex.aphy_addr, channel->ctrl_ex.aphy_addr_high); ++ ret = cipher_mmz_map(&spacc_mmz->aad_mmz_buf); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("DRV AD MMZ MAP ERROR!, addr = 0x%x!\n", channel->ctrl_ex.aphy_addr); ++ cipher_mmz_unmap(&spacc_mmz->src_mmz_buf); ++ cipher_mmz_unmap(&spacc_mmz->dest_mmz_buf); ++ return ret; ++ } ++ spacc_mmz->aad_vir = spacc_mmz->aad_mmz_buf.start_vir_addr; ++ } ++ return ret; ++} ++ ++static td_void drv_cipher_cpu_mmz_unmap(const spacc_symc_chn_s *channel, const spacc_mmz_s *spacc_mmz) ++{ ++ cipher_mmz_unmap(&spacc_mmz->src_mmz_buf); ++ cipher_mmz_unmap(&spacc_mmz->dest_mmz_buf); ++ ++ if ((channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CCM) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_GCM)) ++ cipher_mmz_unmap(&spacc_mmz->aad_mmz_buf); ++} ++ ++static td_s32 drv_cipher_cpu_enc_ccm(const spacc_symc_chn_s *channel, ++ const cipher_data_s *ci_data, u_chan0_cipher_ctrl *chn0_ctrl, const spacc_mmz_s *spacc_mmz) ++{ ++ td_u8 arr_b[32] = {0}; /* 32 arr size */ ++ td_s32 ret; ++ td_u32 value; ++ td_u32 offset = 0; ++ td_bool set_last; ++ ++ /* Format N and A */ ++ ret = drv_cipher_ccm_head_format(channel, ci_data->data_length, arr_b, sizeof(arr_b), &offset); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_ccm_head_format); ++ ++ /* Set last block size */ ++ chn0_ctrl->bits.sym_ccm_gcm_last_block = (ci_data->data_length + 15) % 16; /* 15, 16 */ ++ ++ /* Set flag N */ ++ chn0_ctrl->bits.sym_ch0_ccm_gcm_input_flag = 0x00; // N ++ ++ /* Compute B0, contains N */ ++ set_last = (channel->ctrl_ex.alen + ci_data->data_length == 0) ? TD_TRUE : TD_FALSE; ++ ret = drv_cipher_cpu_input(arr_b, TD_NULL, 16, 16, chn0_ctrl->u32, set_last); /* 16 */ ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_cpu_input); ++ ++ /* Don't update IV any more */ ++ chn0_ctrl->bits.sym_ch0_ivin_sel = 0x00; ++ ++ /* Compute A */ ++ value = channel->ctrl_ex.alen + offset; ++ if (value > 16) { /* 16, a can't puts in one block */ ++ /* Set flag A */ ++ chn0_ctrl->bits.sym_ch0_ccm_gcm_input_flag = 0x01; // A ++ ++ /* Fill head of A to B1 split joint 16 byets */ ++ ret = memcpy_s(arr_b + 16 + offset, sizeof(arr_b) - 16 - offset, /* 16 */ ++ spacc_mmz->aad_vir, 16 - offset); /* 16 */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ /* Compute B1, contains the coding of a and head of A */ ++ ret = drv_cipher_cpu_input(arr_b + 16, TD_NULL, 16, 16, chn0_ctrl->u32, TD_FALSE); /* 16 */ ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_cpu_input); ++ ++ /* Compute the left data of A */ ++ set_last = (ci_data->data_length == 0) ? TD_TRUE : TD_FALSE; ++ ret = drv_cipher_cpu_input(spacc_mmz->aad_vir + 16 - offset, TD_NULL, /* 16 */ ++ channel->ctrl_ex.alen - (16 - offset), 16, chn0_ctrl->u32, set_last); /* 16 */ ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_cpu_input); ++ } else if (value > 0) { /* A and a can puts in one block */ ++ /* Set flag A */ ++ chn0_ctrl->bits.sym_ch0_ccm_gcm_input_flag = 0x01; // A ++ ++ /* Fill A to B1 split joint 16 byets */ ++ ret = memcpy_s(arr_b + 16 + offset, sizeof(arr_b) - 16 - offset, /* 16 */ ++ spacc_mmz->aad_vir, channel->ctrl_ex.alen); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memcpy_s); ++ ++ /* Compute B1, contains the coding of a and A */ ++ set_last = (ci_data->data_length == 0) ? TD_TRUE : TD_FALSE; ++ ret = drv_cipher_cpu_input(arr_b + 16, TD_NULL, 16, 16, chn0_ctrl->u32, set_last); /* 16 */ ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_cpu_input); ++ } ++ ++ /* Next, compute the P */ ++ chn0_ctrl->bits.sym_ch0_ccm_gcm_input_flag = 0x02; // P ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_cpu_enc_gcm(const spacc_symc_chn_s *channel, const cipher_data_s *ci_data, ++ u_chan0_cipher_ctrl *chn0_ctrl, const spacc_mmz_s *spacc_mmz, td_u32 block_size) ++{ ++ td_s32 ret; ++ td_bool set_last; ++ ++ /* Set last block size */ ++ chn0_ctrl->bits.sym_ccm_gcm_last_block = (ci_data->data_length + 15) % 16; /* 15, 16 */ ++ ++ /* Compute A */ ++ if (channel->ctrl_ex.alen > 0) { ++ /* Set flag A */ ++ chn0_ctrl->bits.sym_ch0_ccm_gcm_input_flag = 0x00; // A ++ ++ /* Compute A */ ++ set_last = (ci_data->data_length == 0) ? TD_TRUE : TD_FALSE; ++ ret = drv_cipher_cpu_input(spacc_mmz->aad_vir, TD_NULL, ++ channel->ctrl_ex.alen, block_size, chn0_ctrl->u32, set_last); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_cpu_input); ++ ++ chn0_ctrl->bits.sym_ch0_ivin_sel = 0x00; ++ } ++ ++ /* Don't update IV any more */ ++ chn0_ctrl->bits.sym_ch0_ccm_gcm_input_flag = 0x01; // P ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_cpu_enc_phy(const cipher_data_s *ci_data, td_u32 block_size, td_bool is_decrypt) ++{ ++ td_s32 ret; ++ u_chan0_cipher_ctrl chn0_ctrl; ++ td_u8 arr_b[32]; /* 32 arr size */ ++ spacc_mmz_s spacc_mmz; ++ spacc_symc_chn_s *channel = &g_symc_chn[0]; ++ ++ ret = drv_cipher_cpu_mmz_map(channel, ci_data, &spacc_mmz); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_cpu_mmz_map); ++ ++ /* Decrypt or encrypt */ ++ hal_cipher_read_reg(CHN_0_CIPHER_CTRL, &chn0_ctrl.u32); ++ chn0_ctrl.bits.sym_ch0_decrypt = is_decrypt; ++ hal_cipher_write_reg(CHN_0_CIPHER_CTRL, chn0_ctrl.u32); ++ ++ if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CCM) { ++ ret = drv_cipher_cpu_enc_ccm(channel, ci_data, &chn0_ctrl, &spacc_mmz); ++ } else if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_GCM) { ++ ret = drv_cipher_cpu_enc_gcm(channel, ci_data, &chn0_ctrl, &spacc_mmz, block_size); ++ } ++ if (ret != TD_SUCCESS) { ++ drv_cipher_cpu_mmz_unmap(channel, &spacc_mmz); ++ return ret; ++ } ++ ++ /* Compute P */ ++ if (ci_data->data_length > 0) { ++ ret = drv_cipher_cpu_input(spacc_mmz.src_vir, spacc_mmz.dest_vir, ++ ci_data->data_length, block_size, chn0_ctrl.u32, TD_TRUE); ++ if (ret != TD_SUCCESS) { ++ drv_cipher_cpu_mmz_unmap(channel, &spacc_mmz); ++ return ret; ++ } ++ ++ /* Don't update IV any more */ ++ chn0_ctrl.bits.sym_ch0_ivin_sel = 0x00; ++ } ++ ++ /* Compute LEN(C) for GCM */ ++ if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_GCM) { ++ /* Set flag LEN(C) */ ++ (td_void)memset_s(arr_b, sizeof(arr_b), 0, sizeof(arr_b)); ++ chn0_ctrl.bits.sym_ch0_ccm_gcm_input_flag = 0x02; // LEN ++ ++ /* Format LEN(C) = LEN(A) || LEN(P), coding in bits */ ++ arr_b[3] = (td_u8)((channel->ctrl_ex.alen >> 29) & 0x07); /* 3 index, 29 shift bits */ ++ arr_b[4] = (td_u8)((channel->ctrl_ex.alen >> 21) & 0xff); /* 4 index, 21 shift bits */ ++ arr_b[5] = (td_u8)((channel->ctrl_ex.alen >> 13) & 0xff); /* 5 index, 13 shift bits */ ++ arr_b[6] = (td_u8)((channel->ctrl_ex.alen >> 5) & 0xff); /* 6 index, 5 shift bits */ ++ arr_b[7] = (td_u8)((channel->ctrl_ex.alen << 3) & 0xff); /* 7 index, 3 shift bits */ ++ ++ arr_b[11] = (td_u8)((ci_data->data_length >> 29) & 0x07); /* 11 index, 29 shift bits */ ++ arr_b[12] = (td_u8)((ci_data->data_length >> 21) & 0xff); /* 12 index, 21 shift bits */ ++ arr_b[13] = (td_u8)((ci_data->data_length >> 13) & 0xff); /* 13 index, 13 shift bits */ ++ arr_b[14] = (td_u8)((ci_data->data_length >> 5) & 0xff); /* 14 index, 5 shift bits */ ++ arr_b[15] = (td_u8)((ci_data->data_length << 3) & 0xff); /* 15 index, 3 shift bits */ ++ ++ /* Compute LEN(C) */ ++ ret = drv_cipher_cpu_input(arr_b, TD_NULL, 16, 16, chn0_ctrl.u32, TD_FALSE); /* 16 */ ++ } ++ ++ drv_cipher_cpu_mmz_unmap(channel, &spacc_mmz); ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_ccm_na(spacc_symc_chn_s *channel, td_u32 enc_len) ++{ ++ td_s32 ret; ++ td_u8 *pbuf = TD_NULL; ++ td_u32 index = 0; ++ td_u32 alen = 0; ++ td_u32 count, flag; ++ ++ pbuf = channel->pad_vir_addr; ++ ret = memset_s(pbuf, SPACC_PAD_BUF_SIZE, 0, 32); /* 32 clean size */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ /* Format B0 and head of B1 */ ++ ret = drv_cipher_ccm_head_format(channel, enc_len, pbuf, 32, &alen); /* 32 */ ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_ccm_head_format); ++ ++ /* B0 contains the N, set flag N, for N, A, P, must set the first and last flag. */ ++ flag = SPACC_CTRL_SYMC_IN_CCM_N | SPACC_CTRL_SYMC_IN_LAST | SPACC_CTRL_SYMC_IN_FIRST; ++ if ((channel->ctrl_ex.alen == 0) && (enc_len == 0)) ++ flag |= SPACC_CTRL_SYMC_CCM_LAST; ++ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->pad_phy_addr, 16, SPACC_BUF_TYPE_SYMC_IN, flag); /* 16 */ ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ index += 16; /* 16 */ ++ ++ /* a > 0, add the phy of A into node list */ ++ if (alen) { ++ /* 1st. add the phy of B1 into node list, which contains the coding of a */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->pad_phy_addr + 16, /* 16 */ ++ alen, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_CCM_A); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ index += alen; ++ ++ /* 2nd. add the phy of A into node list */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->ctrl_ex.aphy_addr, ++ channel->ctrl_ex.alen, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_CCM_A); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ } ++ ++ /* if alen + Alen do not aligned with 16, padding 0 to the tail */ ++ count = (channel->ctrl_ex.alen + alen) % AES_BLOCK_SIZE; ++ if (count != 0) { ++ /* Compute the padding length */ ++ count = AES_BLOCK_SIZE - count; ++ ++ /* Set zero */ ++ ret = memset_s(pbuf + index, SPACC_PAD_BUF_SIZE - index, 0, count); ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ /* add the padding phy of A into node list */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->pad_phy_addr + index, ++ count, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_CCM_A); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ } ++ ++ flag = SPACC_CTRL_SYMC_IN_LAST; ++ if (enc_len == 0) { /* if do not contains the P, set CCM last flag signal to hardware */ ++ flag |= SPACC_CTRL_SYMC_CCM_LAST; ++ } ++ spacc_symc_addctrl(channel->hard_num, SPACC_BUF_TYPE_SYMC_IN, flag); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_gcm_a(spacc_symc_chn_s *channel) ++{ ++ td_s32 ret; ++ td_u32 count; ++ td_u32 index = 0; ++ td_u8 *pbuf = TD_NULL; ++ ++ if (channel->ctrl_ex.alen == 0) ++ return TD_SUCCESS; ++ ++ pbuf = channel->pad_vir_addr; ++ ++ /* Add phy of A into node list */ ++ ret = spacc_symc_addbuf(channel->hard_num, ++ make_ulong(channel->ctrl_ex.aphy_addr, channel->ctrl_ex.aphy_addr_high), ++ channel->ctrl_ex.alen, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_GCM_A); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("spacc add A buf failed, ret = 0x%x.\n", ret); ++ return TD_FAILURE; ++ } ++ ++ /* if Alen do not aligned with 16, padding 0 to the tail */ ++ count = channel->ctrl_ex.alen % AES_BLOCK_SIZE; ++ if (count != 0) { ++ /* Compute the padding length */ ++ count = AES_BLOCK_SIZE - count; ++ ++ /* Set zero */ ++ ret = memset_s(pbuf + index, SPACC_PAD_BUF_SIZE, 0, 16); /* 16 clean size */ ++ chk_func_fail_return(ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC, memset_s); ++ ++ /* add the padding phy of A into node list */ ++ ret = spacc_symc_addbuf(channel->hard_num, ++ channel->pad_phy_addr + index, count, ++ SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_GCM_A); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("spacc add A PAD buf failed, ret = 0x%x.\n", ret); ++ return TD_FAILURE; ++ } ++ } ++ ++ /* Set A last flag */ ++ spacc_symc_addctrl(channel->hard_num, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_LAST); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_gcm_len(spacc_symc_chn_s *channel, td_u32 enc_len) ++{ ++ td_s32 ret; ++ td_u8 *pbuf = TD_NULL; ++ td_u32 index = 32; ++ ++ /* Format len(C), 16 byets, coding in bits. ++ * Byet0~7: bits number of Add ++ * Byet8~15: bits number of P ++ */ ++ pbuf = channel->pad_vir_addr; ++ ++ pbuf[index + 0] = 0x00; /* 0 arr index */ ++ pbuf[index + 1] = 0x00; /* 1 arr index */ ++ pbuf[index + 2] = 0x00; /* 2 arr index */ ++ pbuf[index + 3] = (td_u8)((channel->ctrl_ex.alen >> 29) & 0x07); /* 3 arr index, 29 shift bits */ ++ pbuf[index + 4] = (td_u8)((channel->ctrl_ex.alen >> 21) & 0xff); /* 4 arr index, 21 shift bits */ ++ pbuf[index + 5] = (td_u8)((channel->ctrl_ex.alen >> 13) & 0xff); /* 5 arr index, 13 shift bits */ ++ pbuf[index + 6] = (td_u8)((channel->ctrl_ex.alen >> 5) & 0xff); /* 6 arr index, 5 shift bits */ ++ pbuf[index + 7] = (td_u8)((channel->ctrl_ex.alen << 3) & 0xff); /* 7 arr index, 3 shift bits */ ++ pbuf[index + 8] = 0x00; /* 8 arr index */ ++ pbuf[index + 9] = 0x00; /* 9 arr index */ ++ pbuf[index + 10] = 0x00; /* 10 arr index */ ++ pbuf[index + 11] = (td_u8)((enc_len >> 29) & 0x07); /* 11 arr index, 29 shift bits */ ++ pbuf[index + 12] = (td_u8)((enc_len >> 21) & 0xff); /* 12 arr index, 21 shift bits */ ++ pbuf[index + 13] = (td_u8)((enc_len >> 13) & 0xff); /* 13 arr index, 13 shift bits */ ++ pbuf[index + 14] = (td_u8)((enc_len >> 5) & 0xff); /* 14 arr index, 5 shift bits */ ++ pbuf[index + 15] = (td_u8)((enc_len << 3) & 0xff); /* 15 arr index, 3 shift bits */ ++ ++ /* Add to nodes list */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->pad_phy_addr + index, ++ 16, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_GCM_LEN | SPACC_CTRL_SYMC_IN_LAST); /* 16 */ ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("spacc add P buf failed, ret = 0x%x.\n", ret); ++ return TD_FAILURE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 drv_cipher_enc_ccm(spacc_symc_chn_s *channel, ++ const cipher_data_s *ci_data, td_size_t src_phy_addr, td_size_t dst_phys_addr) ++{ ++ td_s32 ret; ++ ++ /* format N and A, add to node list */ ++ ret = drv_cipher_ccm_na(channel, ci_data->data_length); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_ccm_na); ++ ++ /* Add the phy of P into node list */ ++ if (ci_data->data_length > 0) { ++ /* Add in buffer */ ++ ret = spacc_symc_addbuf(channel->hard_num, src_phy_addr, ci_data->data_length, ++ SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_CCM_P | SPACC_CTRL_SYMC_IN_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Add out buffer */ ++ ret = spacc_symc_addbuf(channel->hard_num, dst_phys_addr, ci_data->data_length, ++ SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_SYMC_OUT_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ } else { ++ /* If P is null, must add a empty node into node list, limit to hardware devising */ ++ ret = spacc_symc_addbuf(channel->hard_num, 0x00, 0x00, ++ SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_SYMC_OUT_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ } ++ ++ /* Set CCM last flag */ ++ spacc_symc_addctrl(channel->hard_num, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_CCM_LAST); ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_enc_gcm(spacc_symc_chn_s *channel, ++ const cipher_data_s *ci_data, td_size_t src_phy_addr, td_size_t dest_phy_addr) ++{ ++ td_s32 ret; ++ ++ /* format N, add to node list */ ++ ret = drv_cipher_gcm_a(channel); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_gcm_a); ++ ++ /* Add the phy of P into node list */ ++ if (ci_data->data_length > 0) { ++ /* Add in buffer */ ++ ret = spacc_symc_addbuf(channel->hard_num, src_phy_addr, ci_data->data_length, ++ SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_GCM_P | SPACC_CTRL_SYMC_IN_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Add out buffer */ ++ ret = spacc_symc_addbuf(channel->hard_num, dest_phy_addr, ci_data->data_length, ++ SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_NONE); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ } ++ ++ /* At the and of GCM, must add a empty node to nodes list, limit to hardware devising */ ++ ret = spacc_symc_addbuf(channel->hard_num, 0x00, 0x00, ++ SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_SYMC_OUT_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Format the length fields of C and add to nodes list */ ++ ret = drv_cipher_gcm_len(channel, ci_data->data_length); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, spacc_symcdrv_cipher_gcm_len_addbuf); ++ ++ return ret; ++} ++ ++/* except ccm/gcm */ ++static td_s32 drv_cipher_enc_others(const spacc_symc_chn_s *channel, ++ const cipher_data_s *ci_data, td_size_t src_phy_addr, td_size_t dest_phy_addr) ++{ ++ td_s32 ret; ++ ++ /* except ccm/gcm, the data length must not zero */ ++ if (ci_data->data_length == 0) { ++ ot_err_cipher("Invalid data len 0x%x.\n", ci_data->data_length); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ /* The length of data depend on alg and mode, which limit to hardware ++ * for ecb/cbc/ofb/cfb, the data length must aligned with block size. ++ * for ctr/ccm/gcm, support any data length. ++ */ ++ if (((channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_ECB) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CBC) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_OFB) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CFB)) && ++ (channel->ctrl_ex.ci_alg != OT_CIPHER_ALG_DMA)) { ++ if (ci_data->data_length % channel->block_size != 0) { ++ ot_err_cipher("Invalid data len 0x%x.\n", ci_data->data_length); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } ++ ++ /* Add in buffer */ ++ ret = spacc_symc_addbuf(channel->hard_num, src_phy_addr, ci_data->data_length, ++ SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Add out buffer */ ++ ret = spacc_symc_addbuf(channel->hard_num, dest_phy_addr, ci_data->data_length, ++ SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_SYMC_OUT_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_crypto(const cipher_data_s *ci_data, td_bool is_decrypt) ++{ ++ td_s32 ret; ++ spacc_symc_chn_s *channel; ++ td_u32 soft_chn_id; ++ td_size_t src_phy_addr, dest_phy_addr; ++ ++ src_phy_addr = make_ulong(ci_data->src_phy_addr, ci_data->src_phy_addr_high); ++ dest_phy_addr = make_ulong(ci_data->dest_phy_addr, ci_data->dest_phy_addr_high); ++ ++ soft_chn_id = td_handle_get_chnid(ci_data->ci_handle); ++ channel = &g_symc_chn[soft_chn_id]; ++ ++ if (soft_chn_id != 0) { ++ if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CCM) { ++ ret = drv_cipher_enc_ccm(channel, ci_data, src_phy_addr, dest_phy_addr); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_enc_ccm); ++ } else if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_GCM) { ++ ret = drv_cipher_enc_gcm(channel, ci_data, src_phy_addr, dest_phy_addr); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_enc_gcm); ++ } else { /* ECB/CBC/CBF/OFB/CTR */ ++ ret = drv_cipher_enc_others(channel, ci_data, src_phy_addr, dest_phy_addr); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_enc_others); ++ } ++ ++ channel->symc_done = TD_FALSE; ++ ++ /* Start working */ ++ spacc_symc_start(channel->hard_num, is_decrypt, channel->ctrl_ex.change_flags.bits_iv); ++ ++ if (channel->ctrl_ex.change_flags.bits_iv == OT_CIPHER_IV_CHG_ONE_PKG) ++ channel->ctrl_ex.change_flags.bits_iv = 0; /* only update IV for first pkg */ ++ ++ /* Waiting hardware computing finished */ ++ ret = drv_cipher_symc_wait_done(channel, SPACC_TIME_OUT); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_symc_wait_done); ++ ++ flush_cache(cipher_align_down(dest_phy_addr), cipher_align_size(dest_phy_addr, ci_data->data_length)); ++ } else { ++ /* Chn 0, CPU mode */ ++ ret = drv_cipher_cpu_enc_phy(ci_data, channel->block_size, is_decrypt); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_cpu_enc_phy); ++ } ++ ++ /* Save IV */ ++ if (channel->ctrl_ex.change_flags.bits_iv == OT_CIPHER_IV_CHG_ONE_PKG) ++ spacc_symc_getiv(channel->hard_num, channel->ctrl_ex.iv, sizeof(channel->ctrl_ex.iv)); ++ ++ flush_cache(cipher_align_down(dest_phy_addr), cipher_align_size(dest_phy_addr, ci_data->data_length)); ++ ++ return ret; ++} ++ ++td_s32 ot_drv_cipher_get_tag(cipher_tag_s *tag) ++{ ++ td_u32 soft_chn_id; ++ spacc_symc_chn_s *channel = TD_NULL; ++ td_u32 i; ++ td_s32 ret; ++ ++ if (tag == TD_NULL) { ++ ot_err_cipher("Invalid params!\n"); ++ return TD_FAILURE; ++ } ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(tag->ci_handle); ++ if (ret != TD_SUCCESS) ++ return ret; ++ ++ soft_chn_id = td_handle_get_chnid(tag->ci_handle); ++ channel = &g_symc_chn[soft_chn_id]; ++ ++ if ((channel->ctrl_ex.work_mode != OT_CIPHER_WORK_MODE_CCM) && ++ (channel->ctrl_ex.work_mode != OT_CIPHER_WORK_MODE_GCM)) { ++ ot_err_cipher("Invalid mode %d!\n", channel->ctrl_ex.work_mode); ++ cipher_mutex_unlock(&g_symc_mutex); ++ return TD_FAILURE; ++ } ++ tag->tag_len = channel->ctrl_ex.tag_len; ++ ++ /* Read tag for CCM/GCM */ ++ if (soft_chn_id == 0) { ++ for (i = 0; i < 4; i++) /* 4 loop count */ ++ hal_cipher_read_reg(CHN_0_CCM_GCM_TAG + i * 4, &tag->tag[i]); /* 4 */ ++ } else { ++ spacc_symc_gettag(channel->hard_num, (td_u8 *)tag->tag, sizeof(tag->tag)); ++ } ++ ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_drv_cipher_encrypt(const cipher_data_s *ci_data) ++{ ++ td_s32 ret; ++ ++ if (ci_data == TD_NULL) { ++ ot_err_cipher("Invalid point!\n"); ++ return OT_ERR_CIPHER_INVALID_POINT; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(ci_data->ci_handle); ++ if (ret != TD_SUCCESS) ++ return ret; ++ ++ ret = drv_cipher_crypto(ci_data, TD_FALSE); ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++td_s32 ot_drv_cipher_decrypt(const cipher_data_s *ci_data) ++{ ++ td_s32 ret; ++ ++ if (ci_data == TD_NULL) { ++ ot_err_cipher("Invalid point!\n"); ++ return OT_ERR_CIPHER_INVALID_POINT; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(ci_data->ci_handle); ++ if (ret != TD_SUCCESS) ++ return ret; ++ ++ ret = drv_cipher_crypto(ci_data, TD_TRUE); ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++/* Get odd/even key flag and P flag */ ++static td_u32 drv_cipher_get_pay_load_ctrl(td_bool odd_key, ot_cipher_work_mode mode) ++{ ++ td_u32 ctrl; ++ ++ ctrl = odd_key ? SPACC_CTRL_SYMC_ODD_KEY : SPACC_CTRL_SYMC_EVEN_KEY; ++ ++ if (mode == OT_CIPHER_WORK_MODE_CCM) ++ ctrl |= SPACC_CTRL_SYMC_IN_CCM_P; ++ else if (mode == OT_CIPHER_WORK_MODE_GCM) ++ ctrl |= SPACC_CTRL_SYMC_IN_GCM_P; ++ ++ return ctrl; ++} ++ ++static td_s32 drv_cipher_block_align(spacc_symc_chn_s *channel, td_u32 total, td_u32 node_cur) ++{ ++ td_s32 ret = TD_SUCCESS; ++ td_u32 ctrl; ++ ++ /* Compute the tail length */ ++ total %= channel->block_size; ++ if (total > 0) ++ total = channel->block_size - total; ++ ++ /* if the total length don't aligned with block size, split joint the follow nodes */ ++ while ((total > 0) && (channel->node_cur < channel->node_num)) { ++ /* The next node large than tail size, just split it to 2 nodes */ ++ if (channel->node_list[node_cur].byte_len > total) { ++ /* Add P in */ ++ ctrl = drv_cipher_get_pay_load_ctrl(channel->node_list[node_cur].odd_key, channel->ctrl_ex.work_mode); ++ ret = spacc_symc_addbuf(channel->hard_num, channel->node_list[node_cur].src_phys_addr, ++ total, SPACC_BUF_TYPE_SYMC_IN, ctrl); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Add P out */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->node_list[node_cur].dst_phys_addr, ++ total, SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_NONE); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Let next node skip the tail size */ ++ channel->node_list[node_cur].src_phys_addr += total; ++ channel->node_list[node_cur].dst_phys_addr += total; ++ channel->node_list[node_cur].byte_len -= total; ++ total = 0; ++ } else { ++ /* The next node less than tail size, add it to nodes list */ ++ /* Add P in */ ++ ctrl = drv_cipher_get_pay_load_ctrl(channel->node_list[node_cur].odd_key, channel->ctrl_ex.work_mode); ++ ret = spacc_symc_addbuf(channel->hard_num, channel->node_list[node_cur].src_phys_addr, ++ channel->node_list[node_cur].byte_len, SPACC_BUF_TYPE_SYMC_IN, ctrl); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Add P out */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->node_list[node_cur].dst_phys_addr, ++ channel->node_list[node_cur].byte_len, SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_NONE); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* re-compute the tail size */ ++ total -= channel->node_list[node_cur].byte_len; ++ ++ /* Process next node */ ++ node_cur++; ++ channel->node_cur++; ++ } ++ } ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_add_nodes(spacc_symc_chn_s *channel, td_u32 int_level) ++{ ++ td_s32 ret; ++ td_u32 i, nodes, node_cur, ctrl, total; ++ ++ if (channel->node_cur < channel->node_num) { ++ nodes = cipher_min(int_level, channel->node_num - channel->node_cur); ++ node_cur = channel->node_cur; ++ total = 0; ++ for (i = 0; i < nodes; i++) { ++ /* Get odd/even key flag and P flag */ ++ ctrl = drv_cipher_get_pay_load_ctrl(channel->node_list[node_cur].odd_key, channel->ctrl_ex.work_mode); ++ ++ /* Add P in */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->node_list[node_cur].src_phys_addr, ++ channel->node_list[node_cur].byte_len, SPACC_BUF_TYPE_SYMC_IN, ctrl); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* Add P out */ ++ ret = spacc_symc_addbuf(channel->hard_num, channel->node_list[node_cur].dst_phys_addr, ++ channel->node_list[node_cur].byte_len, SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_NONE); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ total += channel->node_list[node_cur].byte_len; ++ channel->node_cur++; ++ node_cur++; ++ } ++ ++ /* For each compute, the total length of valid nodes list ++ * must aligned with block size, otherwise can't recv interrupt, ++ * which limit to hardware devising. ++ */ ++ ret = drv_cipher_block_align(channel, total, node_cur); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_block_align); ++ } ++ ++ if (channel->node_cur == channel->node_num) { /* Set last flag */ ++ if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CCM) { ++ /* Set CCM last flag */ ++ spacc_symc_addctrl(channel->hard_num, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_CCM_LAST); ++ } else if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_GCM) { ++ /* Set GCM last flag */ ++ spacc_symc_addctrl(channel->hard_num, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_LAST); ++ ++ /* At the and of GCM, must add a empty P node to nodes list, limit to hardware devising */ ++ ret = spacc_symc_addbuf(channel->hard_num, 0x00, 0x00, ++ SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_SYMC_OUT_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, spacc_symc_addbuf); ++ ++ /* After compute P, compute LEN(C) for GCM */ ++ ret = drv_cipher_gcm_len(channel, channel->total_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, spacc_symc_addbuf); ++ } ++ ++ /* Set symc last flag */ ++ spacc_symc_addctrl(channel->hard_num, SPACC_BUF_TYPE_SYMC_IN, SPACC_CTRL_SYMC_IN_LAST); ++ spacc_symc_addctrl(channel->hard_num, SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_SYMC_OUT_LAST); ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_void drv_cipher_callback(td_u32 chn_id) ++{ ++ td_s32 ret; ++ spacc_symc_chn_s *channel = TD_NULL; ++ ++ if (chn_id >= SPACC_MAX_CHN) { ++ ot_err_cipher("invalid chn_id %u\n", chn_id); ++ return; ++ } ++ channel = &g_symc_chn[chn_id]; ++ ++ /* Compute the follow nodes */ ++ if (channel->node_cur < channel->node_num) { ++ ret = drv_cipher_add_nodes(channel, SYMC_INT_LEVEL); ++ if (ret == TD_SUCCESS) ++ spacc_symc_restart(channel->hard_num, channel->ctrl_ex.change_flags.bits_iv); ++ } else { ++ /* All the nodes compute finished, wake up user */ ++ channel->symc_done = TD_TRUE; ++ ot_info_cipher("chn %d wake up\n", channel->hard_num); ++ cipher_queue_wait_up(&channel->queue); ++ } ++} ++ ++static td_s32 drv_cipher_enc_prepare(spacc_symc_chn_s *channel) ++{ ++ td_s32 ret = TD_SUCCESS; ++ ++ /* Before compute P, compute N and A for CCM firstly */ ++ if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CCM) { ++ /* Format N and A, add to node list */ ++ ret = drv_cipher_ccm_na(channel, channel->total_len); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_ccm_na); ++ ++ if (channel->total_len == 0) { ++ /* If P is null, must add a empty node into node list, limit to hardware devising */ ++ ret = spacc_symc_addbuf(channel->hard_num, 0x00, 0x00, ++ SPACC_BUF_TYPE_SYMC_OUT, SPACC_CTRL_SYMC_OUT_LAST); ++ chk_func_fail_return(ret != SPACC_OK, TD_FAILURE, drv_cipher_ccm_na); ++ } ++ } else if (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_GCM) { ++ /* Before compute P, compute A for GCM firstly */ ++ ret = drv_cipher_gcm_a(channel); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_gcm_a); ++ } else if ((channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_ECB) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CBC) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_CFB) || ++ (channel->ctrl_ex.work_mode == OT_CIPHER_WORK_MODE_OFB)) { ++ /* The length of data depend on alg and mode, which limit to hardware ++ * for ecb/cbc/ofb/cfb, the total data length must aligned with block size. ++ * for ctr/ccm/gcm, support any data length. ++ */ ++ if (channel->total_len % channel->block_size != 0) { ++ ot_err_cipher("PKG len must align with 16.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } ++ ++ return ret; ++} ++ ++static td_s32 drv_cipher_encrypt_multi(const cipher_pkg_s *pkg, td_bool is_decrypt) ++{ ++ td_s32 ret; ++ td_u32 i, soft_chn_id, remainder; ++ spacc_symc_chn_s *channel = TD_NULL; ++ cipher_data_compat_s *cipher_data = TD_NULL; ++ ++ chk_formula_fail_return((pkg->pkg_num == 0) || (pkg->pkg_num > MAX_MULTI_PKG_NUM)); ++ ++ soft_chn_id = td_handle_get_chnid(pkg->ci_handle); ++ channel = &g_symc_chn[soft_chn_id]; ++ ++ ot_info_cipher("pkg_num %d\n", pkg->pkg_num); ++ channel->node_list = cipher_malloc(pkg->pkg_num * sizeof(ot_cipher_data)); ++ chk_func_fail_return(channel->node_list == TD_NULL, OT_ERR_CIPHER_INVALID_POINT, cipher_malloc); ++ ++ cipher_data = (cipher_data_compat_s *)channel->node_list; ++ ++ /* copy node list from user space to kernel */ ++ (td_void)memcpy_s(channel->node_list, pkg->pkg_num * sizeof(ot_cipher_data), ++ pkg->cipher_data, pkg->pkg_num * sizeof(ot_cipher_data)); ++ ++ /* Compute and check the nodes length */ ++ channel->total_len = 0; ++ ++ for (i = pkg->pkg_num; i > 0; i--) { ++ if (pkg->user_bit_width != MY_CPU_BIT_WIDTH) { ++ channel->node_list[i - 1].byte_len = cipher_data[i - 1].byte_length; ++ channel->node_list[i - 1].dst_phys_addr = cipher_data[i - 1].dst_phys_addr; ++ channel->node_list[i - 1].src_phys_addr = cipher_data[i - 1].src_phys_addr; ++ } ++ ++ /* Can't used the odd key */ ++ if (channel->node_list[i - 1].odd_key) { ++ ot_err_cipher("Odd key unsupported.\n"); ++ return TD_FAILURE; ++ } ++ ++ /* each node length can't be zero */ ++ if (channel->node_list[i - 1].byte_len == 0) { ++ ot_err_cipher("PKG len must large than 0.\n"); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ channel->total_len += channel->node_list[i - 1].byte_len; ++ } ++ ++ ret = drv_cipher_enc_prepare(channel); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_enc_prepare); ++ ++ channel->node_num = pkg->pkg_num; ++ channel->node_cur = 0; ++ ++ /* For one time compute, the max nodes is 127, but 126 for first time, ++ * here we compute 100 nodes firstly, ++ * Because under each compute, the total length of valid nodes list ++ * must aligned with block size, otherwise can't recv interrupt. ++ * if the total length don't aligned with block size, we must ++ * split joint the follow nodes with current nodes to multiple block size. ++ * so it follows that, the nodes num for this time compute may be larger than 100, ++ * The worst is that we need add 15 nodes(each node only carry 1 bye data) ++ * within this time, that is the SYMC_INT_LEVEL must less than 127 - 16 - 1= 110. ++ */ ++ remainder = cipher_min(pkg->pkg_num, SYMC_INT_LEVEL); ++ ret = drv_cipher_add_nodes(channel, remainder); ++ chk_func_fail_return(ret != TD_SUCCESS, ret, drv_cipher_add_nodes); ++ ++ channel->callback = drv_cipher_callback; ++ channel->symc_done = TD_FALSE; ++ spacc_symc_start(channel->hard_num, is_decrypt, channel->ctrl_ex.change_flags.bits_iv); ++ return ret; ++} ++ ++td_s32 ot_drv_cipher_encrypt_multi(const cipher_pkg_s *pkg) ++{ ++ td_s32 ret; ++ td_u32 soft_chn_id; ++ spacc_symc_chn_s *channel = TD_NULL; ++ ++ if (pkg == TD_NULL) { ++ ot_err_cipher("Invalid pkg.\n"); ++ return OT_ERR_CIPHER_INVALID_POINT; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(pkg->ci_handle); ++ if (ret != TD_SUCCESS) ++ return ret; ++ soft_chn_id = td_handle_get_chnid(pkg->ci_handle); ++ channel = &g_symc_chn[soft_chn_id]; ++ ++ ret = drv_cipher_encrypt_multi(pkg, TD_FALSE); ++ if (ret != TD_SUCCESS) { ++ if (channel->node_list != TD_NULL) { ++ cipher_free(channel->node_list); ++ channel->node_list = TD_NULL; ++ } ++ cipher_mutex_unlock(&g_symc_mutex); ++ return ret; ++ } ++ ++ ret = drv_cipher_symc_wait_done(channel, SPACC_TIME_OUT); ++ if (ret != TD_SUCCESS) ++ ot_err_cipher("spacc symc active failed, ret = 0x%x.\n", ret); ++ ++ if (channel->node_list != TD_NULL) { ++ cipher_free(channel->node_list); ++ channel->node_list = TD_NULL; ++ } ++ ++ if (channel->ctrl_ex.change_flags.bits_iv == OT_CIPHER_IV_CHG_ONE_PKG) ++ spacc_symc_getiv(channel->hard_num, channel->ctrl_ex.iv, sizeof(channel->ctrl_ex.iv)); ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++td_s32 ot_drv_cipher_decrypt_multi(const cipher_pkg_s *pkg) ++{ ++ td_s32 ret; ++ td_u32 soft_chn_id; ++ spacc_symc_chn_s *channel = TD_NULL; ++ ++ if (pkg == TD_NULL) { ++ ot_err_cipher("Invalid pkg.\n"); ++ return OT_ERR_CIPHER_INVALID_POINT; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(pkg->ci_handle); ++ if (ret != TD_SUCCESS) ++ return ret; ++ soft_chn_id = td_handle_get_chnid(pkg->ci_handle); ++ channel = &g_symc_chn[soft_chn_id]; ++ ++ ret = drv_cipher_encrypt_multi(pkg, TD_TRUE); ++ if (ret != TD_SUCCESS) { ++ if (channel->node_list != TD_NULL) { ++ cipher_free(channel->node_list); ++ channel->node_list = TD_NULL; ++ } ++ cipher_mutex_unlock(&g_symc_mutex); ++ return ret; ++ } ++ ++ ret = drv_cipher_symc_wait_done(channel, SPACC_TIME_OUT); ++ if (ret != TD_SUCCESS) ++ ot_err_cipher("spacc symc active failed, ret = 0x%x.\n", ret); ++ ++ if (channel->node_list != TD_NULL) { ++ cipher_free(channel->node_list); ++ channel->node_list = TD_NULL; ++ } ++ ++ if (channel->ctrl_ex.change_flags.bits_iv == OT_CIPHER_IV_CHG_ONE_PKG) ++ spacc_symc_getiv(channel->hard_num, channel->ctrl_ex.iv, sizeof(channel->ctrl_ex.iv)); ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++static td_s32 drv_digest_config(const cipher_hash_data_s *cipher_hash_data, spacc_ctrl_en *spacc_ctrl) ++{ ++ td_s32 ret; ++ digest_alg_en digest_alg; ++ digest_mode_en digest_mode; ++ ++ *spacc_ctrl = SPACC_CTRL_NONE; ++ ++ switch (cipher_hash_data->sha_type) { ++ case OT_CIPHER_HASH_TYPE_SHA1: ++ case OT_CIPHER_HASH_TYPE_HMAC_SHA1: ++ digest_alg = DIGEST_ALG_SHA1; ++ digest_mode = DIGEST_MODE_HASH; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA224: ++ case OT_CIPHER_HASH_TYPE_HMAC_SHA224: ++ digest_alg = DIGEST_ALG_SHA224; ++ digest_mode = DIGEST_MODE_HASH; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA256: ++ case OT_CIPHER_HASH_TYPE_HMAC_SHA256: ++ digest_alg = DIGEST_ALG_SHA256; ++ digest_mode = DIGEST_MODE_HASH; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA384: ++ case OT_CIPHER_HASH_TYPE_HMAC_SHA384: ++ digest_alg = DIGEST_ALG_SHA384; ++ digest_mode = DIGEST_MODE_HASH; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA512: ++ case OT_CIPHER_HASH_TYPE_HMAC_SHA512: ++ digest_alg = DIGEST_ALG_SHA512; ++ digest_mode = DIGEST_MODE_HASH; ++ break; ++ case OT_CIPHER_HASH_TYPE_SM3: ++ digest_alg = DIGEST_ALG_SM3; ++ digest_mode = DIGEST_MODE_HASH; ++ break; ++ default: ++ ot_err_cipher("Invalid hash type: 0x%x\n", cipher_hash_data->sha_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ ret = spacc_digest_config(cipher_hash_data->hard_chn, digest_alg, digest_mode, TD_FALSE); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("spacc set digest mode failed, chn %d, alg %d, mode %d.\n", ++ cipher_hash_data->hard_chn, digest_alg, digest_mode); ++ return TD_FAILURE; ++ } ++ ++ *spacc_ctrl = ((td_u32)*spacc_ctrl) | SPACC_CTRL_HASH_IN_FIRST; ++ *spacc_ctrl = ((td_u32)*spacc_ctrl) | SPACC_CTRL_HASH_IN_LAST; ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_drv_cipher_calc_hash_init(const cipher_hash_data_s *cipher_hash_data) ++{ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_drv_cipher_calc_hash_update(cipher_hash_data_s *cipher_hash_data) ++{ ++ td_s32 ret; ++ spacc_ctrl_en spacc_ctrl; ++ spacc_digest_chn_s *channel = TD_NULL; ++ ++ chk_formula_fail_return(cipher_hash_data->hard_chn >= SPACC_MAX_CHN); ++ channel = &g_digest_chn[cipher_hash_data->hard_chn]; ++ ++ /* configure hash register */ ++ ret = drv_digest_config(cipher_hash_data, &spacc_ctrl); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("cipher config failed, ret = 0x%x.\n", ret); ++ return TD_FAILURE; ++ } ++ ++ /* Add the phy of data to nodes list */ ++ ret = spacc_digest_addbuf(cipher_hash_data->hard_chn, make_ulong(cipher_hash_data->data_phy, ++ cipher_hash_data->data_phy_high), cipher_hash_data->data_len, spacc_ctrl); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("spacc add in buf failed, ret = 0x%x.\n", ret); ++ return TD_FAILURE; ++ } ++ ++ channel->data_size = cipher_hash_data->data_len; ++ channel->digest_done = TD_FALSE; ++ ++ /* Start working */ ++ ret = spacc_digest_start(cipher_hash_data->hard_chn, spacc_ctrl, cipher_hash_data->sha_val); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("spacc add in buf failed, ret = 0x%x.\n", ret); ++ return TD_FAILURE; ++ } ++ ++ /* Waiting hardware computing finished */ ++ ret = drv_cipher_digest_wait_done(channel); ++ if (ret == TD_SUCCESS) /* Read hash result */ ++ spacc_digest_get(cipher_hash_data->hard_chn, cipher_hash_data->sha_val); ++ ++ return ret; ++} ++ ++td_s32 ot_drv_cipher_calc_hash_final(cipher_hash_data_s *cipher_hash_data) ++{ ++ return ot_drv_cipher_calc_hash_update(cipher_hash_data); ++} ++ ++td_s32 ot_drv_cipher_get_handle_config_ex(cipher_config_ctrl_ex_s *config_ctrl) ++{ ++ spacc_symc_chn_s *channel = TD_NULL; ++ td_u32 soft_chn_id; ++ td_s32 ret; ++ ++ if (config_ctrl == TD_NULL) { ++ ot_err_cipher("Invalid params!\n"); ++ return OT_ERR_CIPHER_INVALID_POINT; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("cipher_mutex_lock failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = spacc_check_handle(config_ctrl->ci_handle); ++ if (ret != TD_SUCCESS) ++ return ret; ++ soft_chn_id = td_handle_get_chnid(config_ctrl->ci_handle); ++ channel = &g_symc_chn[soft_chn_id]; ++ ++ (td_void)memcpy_s(config_ctrl, sizeof(cipher_config_ctrl_ex_s), &channel->ctrl_ex, sizeof(channel->ctrl_ex)); ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++ ++#ifdef CIPHER_KLAD_SUPPORT ++td_s32 ot_drv_cipher_klad_encrypt_key(cipher_klad_key_s *klad_key) ++{ ++ td_s32 ret; ++ ++ if (klad_key == TD_NULL) { ++ ot_err_cipher("Invalid params!\n"); ++ return TD_FAILURE; ++ } ++ ++ if (cipher_mutex_lock(&g_symc_mutex)) { ++ ot_err_cipher("down_interruptible failed!\n"); ++ return TD_FAILURE; ++ } ++ ++ ret = drv_cipher_klad_encrypt_key(klad_key->root_key, ++ klad_key->klad_target, klad_key->clean_key, klad_key->encrypt_key); ++ if (ret != TD_SUCCESS) { ++ ot_err_cipher("KladEncryptKey failed!\n"); ++ cipher_mutex_unlock(&g_symc_mutex); ++ return ret; ++ } ++ ++ cipher_mutex_unlock(&g_symc_mutex); ++ ++ return ret; ++} ++#endif +diff --git a/product/security_subsys/cipher/v2/drv/spacc/spacc_intf.h b/product/security_subsys/cipher/v2/drv/spacc/spacc_intf.h +new file mode 100644 +index 0000000..af25182 +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/spacc/spacc_intf.h +@@ -0,0 +1,29 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef SPACC_INTF_H ++#define SPACC_INTF_H ++ ++#include "ot_type.h" ++ ++td_s32 drv_cipher_init(td_void); ++td_void drv_cipher_deinit(td_void); ++ ++#endif /* SPACC_INTF_H */ ++ +diff --git a/product/security_subsys/cipher/v2/drv/spacc/spacc_reg.h b/product/security_subsys/cipher/v2/drv/spacc/spacc_reg.h +new file mode 100644 +index 0000000..ef8c13d +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/spacc/spacc_reg.h +@@ -0,0 +1,70 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef SPACC_REG_H ++#define SPACC_REG_H ++ ++#define CHN_0_CIPHER_IV (g_spacc_reg_base + 0x0000) ++#define chn_n_cipher_iv_out(id) (g_spacc_reg_base + 0x0000 + (id) * 0x10) ++#define CHN_0_CIPHER_DOUT (g_spacc_reg_base + 0x0080) ++#define cipher_key(id) (g_spacc_reg_base + 0x0100 + (id) * 0x20) ++#define sm1_sk(id) (g_spacc_reg_base + 0x0200 + (id) * 0x10) ++#define ODD_EVEN_KEY_SEL (g_spacc_reg_base + 0x0290) ++#define HDCP_MODE_CTRL (g_spacc_reg_base + 0x0300) ++#define SEC_CHN_CFG (g_spacc_reg_base + 0x0304) ++#define CALC_ERR (g_spacc_reg_base + 0x0320) ++#define CHN_0_CIPHER_CTRL (g_spacc_reg_base + 0x0400) ++#define CIPHER_INT_STATUS (g_spacc_reg_base + 0x0404) ++#define CIPHER_INT_EN (g_spacc_reg_base + 0x0408) ++#define CIPHER_INT_RAW (g_spacc_reg_base + 0x040c) ++#define CIPHER_IN_SMMU_EN (g_spacc_reg_base + 0x0410) ++#define OUT_SMMU_EN (g_spacc_reg_base + 0x0414) ++#define CHN_0_CIPHER_DIN (g_spacc_reg_base + 0x0420) ++#define NORM_SMMU_START_ADDR (g_spacc_reg_base + 0x0440) ++#define SEC_SMMU_START_ADDR (g_spacc_reg_base + 0x0444) ++#define chn_n_cipher_ctrl(id) (g_spacc_reg_base + 0x0400 + (id) * 0x80) ++#define chn_n_cipher_in_node_cfg(id) (g_spacc_reg_base + 0x0404 + (id) * 0x80) ++#define chn_n_cipher_in_node_start_addr(id) (g_spacc_reg_base + 0x0408 + (id) * 0x80) ++#define chn_n_cipher_in_buf_rptr(id) (g_spacc_reg_base + 0x040C + (id) * 0x80) ++#define chn_n_cipher_out_node_cfg(id) (g_spacc_reg_base + 0x0430 + (id) * 0x80) ++#define chn_n_cipher_out_node_start_addr(id) (g_spacc_reg_base + 0x0434 + (id) * 0x80) ++#define chn_n_cipher_out_buf_rptr(id) (g_spacc_reg_base + 0x0438 + (id) * 0x80) ++#define chn_n_cipher_in_node_start_addr_high(id) (g_spacc_reg_base + 0x0460 + (id) * 0x80) ++#define chn_n_cipher_out_node_start_addr_high(id) (g_spacc_reg_base + 0x0470 + (id) * 0x80) ++ ++#define CHN_0_HASH_CTRL (g_spacc_reg_base + 0x0800) ++#define HASH_INT_STATUS (g_spacc_reg_base + 0x0804) ++#define HASH_INT_EN (g_spacc_reg_base + 0x0808) ++#define HASH_INT_RAW (g_spacc_reg_base + 0x080C) ++#define HASH_IN_SMMU_EN (g_spacc_reg_base + 0x0810) ++#define CHN_0_HASH_DAT_IN (g_spacc_reg_base + 0x0818) ++#define CHN_0_HASH_TOTAL_DAT_LEN (g_spacc_reg_base + 0x081C) ++#define chn_n_hash_ctrl(id) (g_spacc_reg_base + 0x0800 + (id) * 0x80) ++#define chn_n_hash_in_node_cfg(id) (g_spacc_reg_base + 0x0804 + (id) * 0x80) ++#define chn_n_hash_in_node_start_addr(id) (g_spacc_reg_base + 0x0808 + (id) * 0x80) ++#define chn_n_hash_in_buf_rptr(id) (g_spacc_reg_base + 0x080C + (id) * 0x80) ++#define chn_n_hash_state_val(id) (g_spacc_reg_base + 0x0340 + (id) * 0x08) ++#define chn_n_hash_state_val_addr(id) (g_spacc_reg_base + 0x0344 + (id) * 0x08) ++#define chn_n_hash_in_node_start_addr_high(id) (g_spacc_reg_base + 0x820 + (id) * 0x80) ++ ++#define spacc_read(addr) *(volatile unsigned int *)(addr) ++#define spacc_write(addr, val) *(volatile unsigned int *)(addr) = (val) ++ ++#endif /* SPACC_REG_H */ ++ +diff --git a/product/security_subsys/cipher/v2/drv/spacc/spacc_union_define.h b/product/security_subsys/cipher/v2/drv/spacc/spacc_union_define.h +new file mode 100644 +index 0000000..e7a2afe +--- /dev/null ++++ b/product/security_subsys/cipher/v2/drv/spacc/spacc_union_define.h +@@ -0,0 +1,458 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef SPACC_UNION_DEFINE_H ++#define SPACC_UNION_DEFINE_H ++ ++/* Define the union u_hdcp_mode_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int hdcp_mode_en : 1; /* [0] */ ++ unsigned int hdcp_rootkey_sel : 2; /* [2..1] */ ++ unsigned int reserved_0 : 1; /* [3] */ ++ unsigned int hdmi_tx_hdcp14_wr_en : 1; /* [4] */ ++ unsigned int hdmi_rx_hdcp14_wr_en : 1; /* [5] */ ++ unsigned int hdmi_rx_hdcp22_wr_en : 1; /* [6] */ ++ unsigned int reserved_1 : 1; /* [7] */ ++ unsigned int hdcp_wr_sel : 2; /* [9..8] */ ++ unsigned int reserved_2 : 22; /* [31..10] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_hdcp_mode_ctrl; ++ ++/* Define the union u_sec_chn_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int cipher_sec_chn_cfg : 8; /* [7..0] */ ++ unsigned int cipher_sec_chn_cfg_lock : 1; /* [8] */ ++ unsigned int reserved_0 : 7; /* [15..9] */ ++ unsigned int hash_sec_chn_cfg : 8; /* [23..16] */ ++ unsigned int hash_sec_chn_cfg_lock : 1; /* [24] */ ++ unsigned int reserved_1 : 7; /* [31..25] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_sec_chn_cfg; ++ ++/* Define the union u_mem_ema_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rfs_ema : 3; /* [2..0] */ ++ unsigned int reserved_0 : 1; /* [3] */ ++ unsigned int rfs_emaw : 2; /* [5..4] */ ++ unsigned int reserved_1 : 2; /* [7..6] */ ++ unsigned int rft_emaa : 3; /* [10..8] */ ++ unsigned int rft_emab : 3; /* [13..11] */ ++ unsigned int rft_emasa : 1; /* [14] */ ++ unsigned int rft_colldisn : 1; /* [15] */ ++ unsigned int reserved_2 : 16; /* [31..16] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_mem_ema_cfg; ++ ++/* Define the union u_key_st */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int key_req_cur_st : 2; /* [1..0] */ ++ unsigned int reserved_0 : 30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_key_st; ++ ++/* Define the union u_calc_st0 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int cipher_calc_cur_st : 4; /* [3..0] */ ++ unsigned int reserved_0 : 4; /* [7..4] */ ++ unsigned int hash_calc_cur_st : 4; /* [11..8] */ ++ unsigned int hdcp_key_ksv_crc4 : 4; /* [15..12] */ ++ unsigned int reserved_1 : 16; /* [31..16] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_calc_st0; ++ ++/* Define the union u_calc_err */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int klad_key_use_err : 1; /* [0] */ ++ unsigned int alg_len_err : 1; /* [1] */ ++ unsigned int smmu_page_unvlid : 1; /* [2] */ ++ unsigned int reserved_0 : 29; /* [31..3] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_calc_err; ++ ++/* Define the union u_chann_hash_state_val_addr */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int hash_state_val_addr : 4; /* [3..0] */ ++ unsigned int reserved_0 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_hash_state_val_addr; ++ ++/* Define the union u_chan0_cipher_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int sym_ch0_start : 1; /* [0] */ ++ unsigned int sym_ch0_alg_mode : 3; /* [3..1] */ ++ unsigned int sym_ch0_alg_sel : 3; /* [6..4] */ ++ unsigned int sym_ch0_decrypt : 1; /* [7] */ ++ unsigned int sym_ch0_dat_width : 2; /* [9..8] */ ++ unsigned int sym_ch0_key_length : 2; /* [11..10] */ ++ unsigned int sym_ch0_ccm_gcm_input_flag : 2; /* [13..12] */ ++ unsigned int sym_ch0_key_sel : 1; /* [14] */ ++ unsigned int sym_ch0_ivin_sel : 1; /* [15] */ ++ unsigned int reserved_0 : 2; /* [17..16] */ ++ unsigned int sym_ch0_sm1_round_num : 2; /* [19..18] */ ++ unsigned int sym_ch0_gcm_iv_len : 4; /* [23..20] */ ++ unsigned int sym_ch0_ccm_gcm_pc_last : 1; /* [24] */ ++ unsigned int sym_ccm_gcm_last_block : 4; /* [28..25] */ ++ unsigned int reserved_1 : 3; /* [31..28] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chan0_cipher_ctrl; ++ ++/* Define the union u_cipher_int_status */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 1; /* [0] */ ++ unsigned int cipher_chn_ibuf_int : 7; /* [7..1] */ ++ unsigned int cipher_chn_obuf_int : 8; /* [15..8] */ ++ unsigned int reserved_1 : 16; /* [31..16] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_cipher_int_status; ++ ++/* Define the union u_cipher_int_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 1; /* [0] */ ++ unsigned int cipher_chn_ibuf_en : 7; /* [7..1] */ ++ unsigned int cipher_chn_obuf_en : 8; /* [15..8] */ ++ unsigned int reserved_1 : 14; /* [29..16] */ ++ unsigned int cipher_sec_int_en : 1; /* [30] */ ++ unsigned int cipher_nsec_int_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_cipher_int_en; ++ ++/* Define the union u_cipher_int_raw */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 1; /* [0] */ ++ unsigned int cipher_chn_ibuf_raw : 7; /* [7..1] */ ++ unsigned int cipher_chn_obuf_raw : 8; /* [15..8] */ ++ unsigned int reserved_1 : 16; /* [31..16] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_cipher_int_raw; ++ ++/* Define the union u_cipher_in_smmu_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int cipher_in_chan_rd_dat_smmu_en : 7; /* [6..0] */ ++ unsigned int reserved_0 : 9; /* [15..7] */ ++ unsigned int cipher_in_chan_rd_node_smmu_en : 7; /* [22..16] */ ++ unsigned int reserved_1 : 9; /* [31..23] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_cipher_in_smmu_en; ++ ++/* Define the union u_out_smmu_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int out_chan_wr_dat_smmu_en : 7; /* [6..0] */ ++ unsigned int reserved_0 : 9; /* [15..7] */ ++ unsigned int out_chan_rd_node_smmu_en : 7; /* [22..16] */ ++ unsigned int reserved_1 : 9; /* [31..23] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_out_smmu_en; ++ ++/* Define the union u_in_st */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int hash_in_ctrl_cur_st : 5; /* [4..0] */ ++ unsigned int sym_in_ctrl_cur_st : 3; /* [7..5] */ ++ unsigned int sym_hash_req_cur_st : 4; /* [11..8] */ ++ unsigned int reserved_0 : 20; /* [31..12] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_in_st; ++ ++/* Define the union u_out_st */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int out_cur_st : 5; /* [4..0] */ ++ unsigned int reserved_0 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_out_st; ++ ++/* Define the union u_chann_cipher_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 1; /* [0] */ ++ unsigned int sym_chn_alg_mode : 3; /* [3..1] */ ++ unsigned int sym_chn_alg_sel : 3; /* [6..4] */ ++ unsigned int sym_chn_decrypt : 1; /* [7] */ ++ unsigned int sym_chn_dat_width : 2; /* [9..8] */ ++ unsigned int sym_chn_key_length : 2; /* [11..10] */ ++ unsigned int reserved_1 : 2; /* [13..12] */ ++ unsigned int sym_chn_key_sel : 1; /* [14] */ ++ unsigned int reserved_2 : 1; /* [15] */ ++ unsigned int sym_chn_dout_byte_swap_en : 1; /* [16] */ ++ unsigned int sym_chn_din_byte_swap_en : 1; /* [17] */ ++ unsigned int sym_chn_sm1_round_num : 2; /* [19..18] */ ++ unsigned int reserved_3 : 2; /* [21..20] */ ++ unsigned int weight : 10; /* [31..22] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_cipher_ctrl; ++ ++/* Define the union u_chann_cipher_in_node_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int cipher_in_node_mpackage_int_level : 7; /* [6..0] */ ++ unsigned int reserved_0 : 1; /* [7] */ ++ unsigned int cipher_in_node_rptr : 7; /* [14..8] */ ++ unsigned int reserved_1 : 1; /* [15] */ ++ unsigned int cipher_in_node_wptr : 7; /* [22..16] */ ++ unsigned int reserved_2 : 1; /* [23] */ ++ unsigned int cipher_in_node_total_num : 7; /* [30..24] */ ++ unsigned int reserved_3 : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_cipher_in_node_cfg; ++ ++/* Define the union u_chann_cipher_in_left_byte */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int in_left_byte0 : 24; /* [23..0] */ ++ unsigned int in_byte_cnt : 2; /* [25..24] */ ++ unsigned int in_word_cnt : 2; /* [27..26] */ ++ unsigned int reserved_0 : 4; /* [31..28] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_cipher_in_left_byte; ++ ++/* Define the union u_chann_cipher_out_node_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int cipher_out_node_mpackage_int_level : 7; /* [6..0] */ ++ unsigned int reserved_0 : 1; /* [7] */ ++ unsigned int cipher_out_node_rptr : 7; /* [14..8] */ ++ unsigned int reserved_1 : 1; /* [15] */ ++ unsigned int cipher_out_node_wptr : 7; /* [22..16] */ ++ unsigned int reserved_2 : 1; /* [23] */ ++ unsigned int cipher_out_node_total_num : 7; /* [30..24] */ ++ unsigned int reserved_3 : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_cipher_out_node_cfg; ++ ++/* Define the union u_chann_cipher_out_left_byte */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int out_left_byte0 : 24; /* [23..0] */ ++ unsigned int out_byte_cnt : 2; /* [25..24] */ ++ unsigned int out_word_cnt : 2; /* [27..26] */ ++ unsigned int reserved_0 : 4; /* [31..28] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_cipher_out_left_byte; ++ ++/* Define the union u_chan0_hash_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int hash_ch0_start : 1; /* [0] */ ++ unsigned int hash_ch0_agl_sel : 3; /* [3..1] */ ++ unsigned int hash_ch0_hmac_calc_step : 1; /* [4] */ ++ unsigned int hash_ch0_mode : 1; /* [5] */ ++ unsigned int hash_ch0_key_sel : 1; /* [6] */ ++ unsigned int reserved_0 : 2; /* [8..7] */ ++ unsigned int hash_ch0_auto_padding_en : 1; /* [9] */ ++ unsigned int hash_ch0_hmac_key_addr : 3; /* [12..10] */ ++ unsigned int hash_ch0_used : 1; /* [13] */ ++ unsigned int hash_ch0_sec_alarm : 1; /* [13] */ ++ unsigned int reserved_1 : 17; /* [31..15] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chan0_hash_ctrl; ++ ++/* Define the union u_hash_int_status */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 18; /* [17..0] */ ++ unsigned int hash_ch0_oram_int : 1; /* [18] */ ++ unsigned int hash_ch1_oram_int : 1; /* [19] */ ++ unsigned int hash_ch2_oram_int : 1; /* [20] */ ++ unsigned int hash_ch3_oram_int : 1; /* [21] */ ++ unsigned int hash_ch4_oram_int : 1; /* [22] */ ++ unsigned int hash_ch5_oram_int : 1; /* [23] */ ++ unsigned int hash_ch6_oram_int : 1; /* [24] */ ++ unsigned int hash_ch7_oram_int : 1; /* [25] */ ++ unsigned int reserved_1 : 6; /* [31..26] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_hash_int_status; ++ ++/* Define the union u_hash_int_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 18; /* [17..0] */ ++ unsigned int hash_chn_oram_en : 8; /* [25..18] */ ++ unsigned int reserved_1 : 4; /* [29..26] */ ++ unsigned int hash_sec_int_en : 1; /* [30] */ ++ unsigned int hash_int_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_hash_int_en; ++ ++/* Define the union u_hash_int_raw */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 18; /* [17..0] */ ++ unsigned int hash_chn_oram_raw : 8; /* [25..18] */ ++ unsigned int reserved_1 : 6; /* [31..26] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_hash_int_raw; ++ ++/* Define the union u_hash_in_smmu_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int hash_in_chan_rd_dat_smmu_en : 7; /* [6..0] */ ++ unsigned int reserved_0 : 9; /* [15..7] */ ++ unsigned int hash_in_chan_rd_node_smmu_en : 7; /* [22..16] */ ++ unsigned int reserved_1 : 9; /* [31..23] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_hash_in_smmu_en; ++ ++/* Define the union u_chann_hash_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 1; /* [0] */ ++ unsigned int hash_chn_agl_sel : 3; /* [3..1] */ ++ unsigned int reserved_1 : 1; /* [4] */ ++ unsigned int hash_chn_mode : 1; /* [5] */ ++ unsigned int hash_chn_key_sel : 1; /* [6] */ ++ unsigned int hash_chn_dat_in_byte_swap_en : 1; /* [7] */ ++ unsigned int hash_chn_dat_in_bit_swap_en : 1; /* [8] */ ++ unsigned int hash_chn_auto_padding_en : 1; /* [9] */ ++ unsigned int hash_chn_hmac_key_addr : 3; /* [12..10] */ ++ unsigned int reserved_2 : 19; /* [31..13] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_hash_ctrl; ++ ++/* Define the union u_chann_hash_in_node_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int hash_in_node_mpackage_int_level : 8; /* [7..0] */ ++ unsigned int hash_in_node_rptr : 8; /* [15..8] */ ++ unsigned int hash_in_node_wptr : 8; /* [23..16] */ ++ unsigned int hash_in_node_total_num : 8; /* [31..24] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} u_chann_hash_in_node_cfg; ++ ++#endif /* SPACC_UNION_DEFINE_H */ +diff --git a/product/security_subsys/cipher/v3/include/ot_common_cipher.h b/product/security_subsys/cipher/v3/include/ot_common_cipher.h +new file mode 100644 +index 0000000..ec796f2 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/include/ot_common_cipher.h +@@ -0,0 +1,312 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_COMMON_CIPHER_H ++#define OT_COMMON_CIPHER_H ++ ++#include "ot_type.h" ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C" { ++#endif ++#endif /* __cplusplus */ ++ ++/* CIPHER error code value */ ++#define OT_ERR_CIPHER_NOT_INIT (td_s32)(0x804D0001) ++#define OT_ERR_CIPHER_INVALID_HANDLE (td_s32)(0x804D0002) ++#define OT_ERR_CIPHER_INVALID_POINT (td_s32)(0x804D0003) ++#define OT_ERR_CIPHER_INVALID_PARAM (td_s32)(0x804D0004) ++#define OT_ERR_CIPHER_FAILED_INIT (td_s32)(0x804D0005) ++#define OT_ERR_CIPHER_FAILED_GETHANDLE (td_s32)(0x804D0006) ++#define OT_ERR_CIPHER_FAILED_RELEASEHANDLE (td_s32)(0x804D0007) ++#define OT_ERR_CIPHER_FAILED_CFG_AES (td_s32)(0x804D0008) ++#define OT_ERR_CIPHER_FAILED_CFG_DES (td_s32)(0x804D0009) ++#define OT_ERR_CIPHER_FAILED_ENCRYPT (td_s32)(0x804D000A) ++#define OT_ERR_CIPHER_FAILED_DECRYPT (td_s32)(0x804D000B) ++#define OT_ERR_CIPHER_BUSY (td_s32)(0x804D000C) ++#define OT_ERR_CIPHER_NO_AVAILABLE_RNG (td_s32)(0x804D000D) ++#define OT_ERR_CIPHER_FAILED_MEM (td_s32)(0x804D000E) ++#define OT_ERR_CIPHER_UNAVAILABLE (td_s32)(0x804D000F) ++#define OT_ERR_CIPHER_OVERFLOW (td_s32)(0x804D0010) ++#define OT_ERR_CIPHER_HARD_STATUS (td_s32)(0x804D0011) ++#define OT_ERR_CIPHER_TIMEOUT (td_s32)(0x804D0012) ++#define OT_ERR_CIPHER_UNSUPPORTED (td_s32)(0x804D0013) ++#define OT_ERR_CIPHER_REGISTER_IRQ (td_s32)(0x804D0014) ++#define OT_ERR_CIPHER_ILLEGAL_UUID (td_s32)(0x804D0015) ++#define OT_ERR_CIPHER_ILLEGAL_KEY (td_s32)(0x804D0016) ++#define OT_ERR_CIPHER_INVALID_ADDR (td_s32)(0x804D0017) ++#define OT_ERR_CIPHER_INVALID_LEN (td_s32)(0x804D0018) ++#define OT_ERR_CIPHER_ILLEGAL_DATA (td_s32)(0x804D0019) ++#define OT_ERR_CIPHER_RSA_SIGN (td_s32)(0x804D001A) ++#define OT_ERR_CIPHER_RSA_VERIFY (td_s32)(0x804D001B) ++#define OT_ERR_CIPHER_FAILED_SEC_FUNC (td_s32)(0x804D001C) ++ ++#define OT_CIPHER_MAX_IV_SIZE_IN_WORD 4 ++#define OT_CIPHER_SM2_LEN_IN_WORD 8 ++ ++/* enum typedef */ ++/* Cipher work mode. */ ++typedef enum { ++ OT_CIPHER_WORK_MODE_ECB = 0x0, /* Electronic codebook (ECB) mode, ECB has been considered insecure and it is ++ recommended not to use it. */ ++ OT_CIPHER_WORK_MODE_CBC, /* Cipher block chaining (CBC) mode. */ ++ OT_CIPHER_WORK_MODE_CFB, /* Cipher feedback (CFB) mode. */ ++ OT_CIPHER_WORK_MODE_OFB, /* Output feedback (OFB) mode. */ ++ OT_CIPHER_WORK_MODE_CTR, /* Counter (CTR) mode. */ ++ OT_CIPHER_WORK_MODE_CCM, /* Counter (CCM) mode. */ ++ OT_CIPHER_WORK_MODE_GCM, /* Counter (GCM) mode. */ ++ OT_CIPHER_WORK_MODE_BUTT, ++} ot_cipher_work_mode; ++ ++/* Cipher algorithm. */ ++typedef enum { ++ OT_CIPHER_ALG_AES = 0x0, /* Advanced encryption standard (AES) algorithm */ ++ OT_CIPHER_ALG_SM1, /* SM1 algorithm. */ ++ OT_CIPHER_ALG_SM4, /* SM4 algorithm. */ ++ OT_CIPHER_ALG_DMA, /* DMA copy. */ ++ OT_CIPHER_ALG_BUTT, ++} ot_cipher_alg; ++ ++/* Key length. */ ++typedef enum { ++ OT_CIPHER_KEY_DEFAULT = 0x0, /* Default key length, AES-16, SM1-48, SM4-16 */ ++ OT_CIPHER_KEY_AES_128BIT = 0x0, /* 128-bit key for the AES algorithm */ ++ OT_CIPHER_KEY_AES_192BIT = 0x1, /* 192-bit key for the AES algorithm */ ++ OT_CIPHER_KEY_AES_256BIT = 0x2, /* 256-bit key for the AES algorithm */ ++ OT_CIPHER_KEY_LEN_BUTT = 0x3, ++} ot_cipher_key_len; ++ ++/* Cipher bit width. */ ++typedef enum { ++ OT_CIPHER_BIT_WIDTH_1BIT = 0x0, /* 1-bit width */ ++ OT_CIPHER_BIT_WIDTH_8BIT, /* 8-bit width */ ++ OT_CIPHER_BIT_WIDTH_64BIT, /* 64-bit width */ ++ OT_CIPHER_BIT_WIDTH_128BIT, /* 128-bit width */ ++ OT_CIPHER_BIT_WIDTH_BUTT, ++} ot_cipher_bit_width; ++ ++/* Encryption/Decryption type selecting. */ ++typedef enum { ++ OT_CIPHER_TYPE_NORMAL = 0x0, ++ OT_CIPHER_TYPE_BUTT, ++} ot_cipher_type; ++ ++/* Cipher control parameters. */ ++typedef enum { ++ OT_CIPHER_IV_CHG_NONE = 0x0, /* CIPHER don't set key and IV */ ++ OT_CIPHER_IV_CHG_ONE_PACK, /* CIPHER set key and IV for first package */ ++ OT_CIPHER_IV_CHG_ALL_PACK, /* CIPHER set key and IV for all package */ ++ OT_CIPHER_IV_CHG_BUTT, ++} ot_cipher_ctrl_chg_flag; ++ ++/* Structure of the cipher type */ ++typedef struct { ++ ot_cipher_type cipher_type; ++} ot_cipher_attr; ++ ++/* Structure of the cipher AES control information */ ++typedef struct { ++ td_u32 iv[OT_CIPHER_MAX_IV_SIZE_IN_WORD]; /* Initialization vector (IV) */ ++ ot_cipher_bit_width bit_width; /* Bit width for encryption or decryption */ ++ ot_cipher_key_len key_len; /* Key length */ ++ /* control information exchange choices, we default all woulde be change except they have been in the choices */ ++ ot_cipher_ctrl_chg_flag chg_flags; ++} ot_cipher_ctrl_aes; ++ ++/* Structure of the cipher AES CCM/GCM control information */ ++typedef struct { ++ td_u32 iv[OT_CIPHER_MAX_IV_SIZE_IN_WORD]; /* Initialization vector (IV) */ ++ ot_cipher_key_len key_len; /* Key length */ ++ td_u32 iv_len; /* IV length for CCM/GCM, which is an element of {7, 8, 9, 10, 11, 12, 13} ++ for CCM, and is an element of [1-16] for GCM. */ ++ td_u32 tag_len; /* Tag length for CCM which is an element of {4, 6, 8, 10, 12, 14, 16}. */ ++ td_u32 aad_len; /* Associated data for CCM and GCM. */ ++ td_phys_addr_t aad_phys_addr; /* Physical address of Associated data for CCM and GCM. */ ++} ot_cipher_ctrl_aes_ccm_gcm; ++ ++/* Structure of the cipher SM4 control information */ ++typedef struct { ++ td_u32 iv[OT_CIPHER_MAX_IV_SIZE_IN_WORD]; /* Initialization vector (IV) */ ++ /* control information exchange choices, we default all woulde be change except they have been in the choices */ ++ ot_cipher_ctrl_chg_flag chg_flags; ++} ot_cipher_ctrl_sm4; ++ ++/* Structure of the cipher control information */ ++typedef struct { ++ ot_cipher_alg alg; /* cipher algorithm */ ++ ot_cipher_work_mode work_mode; /* algorithm work mode */ ++ union { ++ /* AES ECB/CBC/CFB/OFB/CTR control ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_AES; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_ECB ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_AES; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_CBC ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_AES; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_CFB ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_AES; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_OFB ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_AES; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_CTR */ ++ ot_cipher_ctrl_aes aes_ctrl; ++ ++ /* AES CCM/GCM control ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_AES; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_CCM ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_AES; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_GCM */ ++ ot_cipher_ctrl_aes_ccm_gcm aes_ccm_gcm_ctrl; ++ ++ /* SM4 ECB/CBC/CFB/OFB/CTR control ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_SM4; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_ECB ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_SM4; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_CBC ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_SM4; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_CFB ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_SM4; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_OFB ++ AUTO: ot_cipher_alg: OT_CIPHER_ALG_SM4; ot_cipher_work_mode: OT_CIPHER_WORK_MODE_CTR */ ++ ot_cipher_ctrl_sm4 sm4_ctrl; ++ }; ++} ot_cipher_ctrl; ++ ++/* Cipher data */ ++typedef struct { ++ td_phys_addr_t src_phys_addr; /* phy address of the original data */ ++ td_phys_addr_t dst_phys_addr; /* phy address of the purpose data */ ++ td_u32 byte_len; /* cipher data length. */ ++} ot_cipher_data; ++ ++/* Hash algrithm type. */ ++typedef enum { ++ OT_CIPHER_HASH_TYPE_SHA1 = 0x00, ++ OT_CIPHER_HASH_TYPE_SHA224, ++ OT_CIPHER_HASH_TYPE_SHA256, ++ OT_CIPHER_HASH_TYPE_SHA384, ++ OT_CIPHER_HASH_TYPE_SHA512, ++ OT_CIPHER_HASH_TYPE_SM3 = 0x10, ++ OT_CIPHER_HASH_TYPE_HMAC_SHA1 = 0x20, ++ OT_CIPHER_HASH_TYPE_HMAC_SHA224, ++ OT_CIPHER_HASH_TYPE_HMAC_SHA256, ++ OT_CIPHER_HASH_TYPE_HMAC_SHA384, ++ OT_CIPHER_HASH_TYPE_HMAC_SHA512, ++ OT_CIPHER_HASH_TYPE_HMAC_SM3 = 0x30, ++ OT_CIPHER_HASH_TYPE_BUTT, ++} ot_cipher_hash_type; ++ ++/* Hash init struct input */ ++typedef struct { ++ td_u8 *hmac_key; ++ td_u32 hmac_key_len; ++ ot_cipher_hash_type sha_type; ++} ot_cipher_hash_attr; ++ ++typedef struct { ++ td_u8 *data; ++ td_u32 data_len; ++} ot_cipher_common_data; ++ ++typedef enum { ++ OT_CIPHER_RSA_SCHEME_PKCS1_V15 = 0x00, /* PKCS#1 V15 */ ++ OT_CIPHER_RSA_SCHEME_PKCS1_V21, /* PKCS#1 V21, PSS for signning, OAEP for encryption */ ++ OT_CIPHER_RSA_SCHEME_BUTT, ++} ot_cipher_rsa_scheme; ++ ++/* RSA public key struct */ ++typedef struct { ++ td_u8 *n; /* Point to public modulus N */ ++ td_u8 *e; /* Point to public exponent E */ ++ td_u16 n_len; /* Length of public modulus N, max value is 512Byte */ ++ td_u16 e_len; /* Length of public exponent E, max value is 512Byte */ ++} ot_cipher_rsa_public_key; ++ ++/* RSA private key struct */ ++typedef struct { ++ td_u8 *n; /* Public modulus N. */ ++ td_u8 *e; /* Public exponent E. */ ++ td_u8 *d; /* Private exponent D. */ ++ td_u8 *p; /* 1st prime factor P. */ ++ td_u8 *q; /* 2nd prime factor Q. */ ++ td_u8 *dp; /* descript:d % (p - 1) is DP. */ ++ td_u8 *dq; /* descript:d % (q - 1) is DQ. */ ++ td_u8 *qp; /* descript:1 / (q % p) is QP. */ ++ td_u16 n_len; /* Length of public modulus */ ++ td_u16 e_len; /* Length of public exponent */ ++ td_u16 d_len; /* Length of private exponent */ ++ td_u16 p_len; /* Length of 1st prime factor,should be half of n_len */ ++ td_u16 q_len; /* Length of 2nd prime factor,should be half of n_len */ ++ td_u16 dp_len; /* Length of D % (P - 1),should be half of n_len */ ++ td_u16 dq_len; /* Length of D % (Q - 1),should be half of n_len */ ++ td_u16 qp_len; /* Length of 1 / (Q % P),should be half of n_len */ ++} ot_cipher_rsa_private_key; ++ ++typedef enum { ++ OT_CIPHER_SIGN_TYPE_MSG = 0x00, ++ OT_CIPHER_SIGN_TYPE_HASH, ++ OT_CIPHER_SIGN_TYPE_BUTT, ++} ot_cipher_sign_type; ++ ++typedef struct { ++ ot_cipher_sign_type sign_type; ++ td_u8 *input; ++ td_u32 input_len; ++} ot_cipher_sign_in_data; ++ ++typedef struct { ++ td_u32 px[OT_CIPHER_SM2_LEN_IN_WORD]; ++ td_u32 py[OT_CIPHER_SM2_LEN_IN_WORD]; ++} ot_cipher_sm2_public_key; ++ ++typedef struct { ++ td_u32 d[OT_CIPHER_SM2_LEN_IN_WORD]; ++} ot_cipher_sm2_private_key; ++ ++typedef struct { ++ td_u32 d[OT_CIPHER_SM2_LEN_IN_WORD]; ++ td_u32 px[OT_CIPHER_SM2_LEN_IN_WORD]; ++ td_u32 py[OT_CIPHER_SM2_LEN_IN_WORD]; ++ td_u8 *id; ++ td_u16 id_len; ++} ot_cipher_sm2_sign; ++ ++typedef struct { ++ td_u32 px[OT_CIPHER_SM2_LEN_IN_WORD]; ++ td_u32 py[OT_CIPHER_SM2_LEN_IN_WORD]; ++ td_u8 *id; ++ td_u16 id_len; ++} ot_cipher_sm2_verify; ++ ++typedef struct { ++ td_u32 r[OT_CIPHER_SM2_LEN_IN_WORD]; ++ td_u32 s[OT_CIPHER_SM2_LEN_IN_WORD]; ++} ot_cipher_sm2_sign_data; ++ ++typedef enum { ++ OT_KEYSLOT_TYPE_MCIPHER, /* keyslot is used to mcipher. */ ++ OT_KEYSLOT_TYPE_BUTT, ++} ot_keyslot_type; ++ ++typedef enum { ++ OT_KEYSLOT_SECURE_MODE_NONE = 0x00, /* no secure. */ ++ OT_KEYSLOT_SECURE_MODE_TEE, /* tee. */ ++ OT_KEYSLOT_SECURE_MODE_BUTT, ++} ot_keyslot_secure_mode; ++ ++typedef struct { ++ ot_keyslot_type type; ++ ot_keyslot_secure_mode secure_mode; ++} ot_keyslot_attr; ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* __cplusplus */ ++ ++#endif /* OT_COMMON_CIPHER_H */ +diff --git a/product/security_subsys/cipher/v3/include/ot_mpi_cipher.h b/product/security_subsys/cipher/v3/include/ot_mpi_cipher.h +new file mode 100644 +index 0000000..1c5b728 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/include/ot_mpi_cipher.h +@@ -0,0 +1,117 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_MPI_CIPHER_H ++#define OT_MPI_CIPHER_H ++ ++#include "ot_common_cipher.h" ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C" { ++#endif ++#endif /* __cplusplus */ ++ ++td_s32 ot_mpi_cipher_init(td_void); ++ ++td_s32 ot_mpi_cipher_deinit(td_void); ++ ++td_s32 ot_mpi_cipher_create(td_handle *handle, const ot_cipher_attr *cipher_attr); ++ ++td_s32 ot_mpi_cipher_destroy(td_handle handle); ++ ++td_s32 ot_mpi_cipher_set_cfg(td_handle handle, const ot_cipher_ctrl *ctrl); ++ ++td_s32 ot_mpi_cipher_get_cfg(td_handle handle, ot_cipher_ctrl *ctrl); ++ ++td_s32 ot_mpi_cipher_encrypt(td_handle handle, ++ td_phys_addr_t src_phys_addr, td_phys_addr_t dst_phys_addr, td_u32 byte_len); ++ ++td_s32 ot_mpi_cipher_decrypt(td_handle handle, ++ td_phys_addr_t src_phys_addr, td_phys_addr_t dst_phys_addr, td_u32 byte_len); ++ ++td_s32 ot_mpi_cipher_encrypt_virt(td_handle handle, const td_u8 *src_data, td_u8 *dst_data, td_u32 byte_len); ++ ++td_s32 ot_mpi_cipher_decrypt_virt(td_handle handle, const td_u8 *src_data, td_u8 *dst_data, td_u32 byte_len); ++ ++td_s32 ot_mpi_cipher_encrypt_multi_pack(td_handle handle, const ot_cipher_data *data_pack, td_u32 data_pack_num); ++ ++td_s32 ot_mpi_cipher_decrypt_multi_pack(td_handle handle, const ot_cipher_data *data_pack, td_u32 data_pack_num); ++ ++td_s32 ot_mpi_cipher_get_tag(td_handle handle, td_u8 *tag, td_u32 tag_len); ++ ++td_s32 ot_mpi_cipher_hash_init(const ot_cipher_hash_attr *hash_attr, td_handle *handle); ++ ++td_s32 ot_mpi_cipher_hash_update(td_handle handle, const td_u8 *in_data, td_u32 in_data_len); ++ ++td_s32 ot_mpi_cipher_hash_final(td_handle handle, td_u8 *out_hash, td_u32 out_hash_len); ++ ++td_s32 ot_mpi_cipher_get_random_num(td_u32 *random_num); ++ ++td_s32 ot_mpi_cipher_rsa_public_encrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_public_key *rsa_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_common_data *cipher_txt); ++ ++td_s32 ot_mpi_cipher_rsa_private_decrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_private_key *rsa_key, ++ const ot_cipher_common_data *cipher_txt, ot_cipher_common_data *plain_txt); ++ ++td_s32 ot_mpi_cipher_rsa_private_encrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_private_key *rsa_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_common_data *cipher_txt); ++ ++td_s32 ot_mpi_cipher_rsa_public_decrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_public_key *rsa_key, ++ const ot_cipher_common_data *cipher_txt, ot_cipher_common_data *plain_txt); ++ ++td_s32 ot_mpi_cipher_rsa_sign(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_private_key *rsa_key, ++ const ot_cipher_sign_in_data *rsa_data, ot_cipher_common_data *sign_data); ++ ++td_s32 ot_mpi_cipher_rsa_verify(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_public_key *rsa_key, ++ const ot_cipher_sign_in_data *rsa_data, const ot_cipher_common_data *sign_data); ++ ++td_s32 ot_mpi_cipher_sm2_encrypt(const ot_cipher_sm2_public_key *sm2_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_common_data *cipher_txt); ++ ++td_s32 ot_mpi_cipher_sm2_decrypt(const ot_cipher_sm2_private_key *sm2_key, ++ const ot_cipher_common_data *cipher_txt, ot_cipher_common_data *plain_txt); ++ ++td_s32 ot_mpi_cipher_sm2_sign(const ot_cipher_sm2_sign *sm2_sign, ++ const ot_cipher_sign_in_data *sm2_data, ot_cipher_sm2_sign_data *sign_data); ++ ++td_s32 ot_mpi_cipher_sm2_verify(const ot_cipher_sm2_verify *sm2_verify, ++ const ot_cipher_sign_in_data *sm2_data, const ot_cipher_sm2_sign_data *sign_data); ++ ++td_s32 ot_mpi_keyslot_create(const ot_keyslot_attr *attr, td_handle *keyslot); ++ ++td_s32 ot_mpi_keyslot_destroy(td_handle keyslot); ++ ++td_s32 ot_mpi_cipher_attach(td_handle cipher, td_handle keyslot); ++ ++td_s32 ot_mpi_cipher_detach(td_handle cipher, td_handle keyslot); ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* __cplusplus */ ++ ++#endif /* OT_MPI_CIPHER_H */ +diff --git a/product/security_subsys/cipher/v3/src/api/build.mak b/product/security_subsys/cipher/v3/src/api/build.mak +new file mode 100644 +index 0000000..8b281e3 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/api/build.mak +@@ -0,0 +1 @@ ++API_OBJS += $(CIPHER_PREFIX)/api/mpi_cipher.o +diff --git a/product/security_subsys/cipher/v3/src/api/mpi_cipher.c b/product/security_subsys/cipher/v3/src/api/mpi_cipher.c +new file mode 100644 +index 0000000..e693c1d +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/api/mpi_cipher.c +@@ -0,0 +1,1334 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ot_mpi_cipher.h" ++#include "drv_osal_lib.h" ++#include "drv_cipher_kapi.h" ++ ++#define MAX_TAG_LEN 16 ++#define CIPHER_MAX_MULTIPAD_NUM 5000 ++static td_bool g_init_flag = TD_FALSE; ++ ++#define mpi_chk_init_err_return() do { \ ++ if (g_init_flag != TD_TRUE) { \ ++ return OT_ERR_CIPHER_NOT_INIT; \ ++ } \ ++} while (0) ++ ++/* ++ * Read E in public key from array to U32, ++ * so only use last byte0~byte3, others are zero ++ */ ++#define cipher_get_pub_exponent(pub_e, pub_rsa) \ ++ do { \ ++ td_u8 *_buf = (pub_rsa)->e; \ ++ td_u8 *_pub = (td_u8 *)(pub_e); \ ++ td_u32 _len = (pub_rsa)->e_len; \ ++ td_u32 _i; \ ++ for (_i = 0; _i < crypto_min(WORD_WIDTH, _len); _i++) { \ ++ _pub[WORD_WIDTH - _i - 1] = _buf[_len - _i - 1]; \ ++ } \ ++ } while (0) ++ ++/* ++ * brief Init the cipher device. ++ */ ++td_s32 ot_mpi_cipher_init(td_void) ++{ ++ td_s32 ret = TD_FAILURE; ++ if (g_init_flag == TD_TRUE) { ++ return TD_SUCCESS; ++ } ++ (td_void)crypto_get_cpu_secure_sta(); ++ ret = crypto_entry(); ++ if (ret != TD_SUCCESS) { ++ return ret; ++ } ++ g_init_flag = TD_TRUE; ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Deinit the cipher device. ++ */ ++td_s32 ot_mpi_cipher_deinit(td_void) ++{ ++ if (g_init_flag == TD_TRUE) { ++ g_init_flag = TD_FALSE; ++ return crypto_exit(); ++ } ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Obtain a cipher handle for encryption and decryption. ++ */ ++td_s32 ot_mpi_cipher_create(td_handle *handle, const ot_cipher_attr *cipher_attr) ++{ ++ td_s32 ret; ++ td_u32 id = 0; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(handle); ++ chk_ptr_err_return(cipher_attr); ++ chk_param_err_return(cipher_attr->cipher_type >= OT_CIPHER_TYPE_BUTT); ++ ++ ret = kapi_symc_create(&id, cipher_attr->cipher_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_create, ret); ++ return ret; ++ } ++ ++ *handle = (td_handle)id; ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Destroy the existing cipher handle. ++ */ ++td_s32 ot_mpi_cipher_destroy(td_handle handle) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ ret = kapi_symc_destroy(handle); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_destroy, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++static td_s32 mpi_symc_set_cfg(symc_cfg_t *config, td_u32 id, const ot_cipher_ctrl *ctrl) ++{ ++ const td_u32 *iv = TD_NULL; ++ ++ (td_void)memset_s(config, sizeof(symc_cfg_t), 0, sizeof(symc_cfg_t)); ++ config->id = id; ++ config->alg = ctrl->alg; ++ config->mode = ctrl->work_mode; ++ config->width = OT_CIPHER_BIT_WIDTH_128BIT; ++ config->ivlen = AES_IV_SIZE; ++ ++ if (ctrl->alg == OT_CIPHER_ALG_AES) { ++ if (ctrl->work_mode == OT_CIPHER_WORK_MODE_CCM || ++ ctrl->work_mode == OT_CIPHER_WORK_MODE_GCM) { ++ const ot_cipher_ctrl_aes_ccm_gcm *aes_ccm_gcm = &ctrl->aes_ccm_gcm_ctrl; ++ config->klen = aes_ccm_gcm->key_len; ++ config->ivlen = aes_ccm_gcm->iv_len; ++ config->iv_usage = OT_CIPHER_IV_CHG_ONE_PACK; ++ config->tlen = aes_ccm_gcm->tag_len; ++ config->alen = aes_ccm_gcm->aad_len; ++ addr_u64(config->aad) = aes_ccm_gcm->aad_phys_addr; ++ iv = aes_ccm_gcm->iv; ++ } else { ++ const ot_cipher_ctrl_aes *aes = &ctrl->aes_ctrl; ++ config->klen = aes->key_len; ++ config->iv_usage = aes->chg_flags; ++ config->width = aes->bit_width; ++ iv = aes->iv; ++ } ++ } else if (ctrl->alg == OT_CIPHER_ALG_SM4) { ++ const ot_cipher_ctrl_sm4 *sm4 = &ctrl->sm4_ctrl; ++ config->iv_usage = sm4->chg_flags; ++ iv = sm4->iv; ++ } else if (ctrl->alg != OT_CIPHER_ALG_DMA) { ++ log_error("Unsupported alg %d\n", ctrl->alg); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ if (iv != TD_NULL) { ++ if (memcpy_s(config->iv, sizeof(config->iv), iv, config->ivlen) != EOK) { ++ print_func_errno(memcpy_s, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ } ++ ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Configures the cipher control information. ++ */ ++td_s32 ot_mpi_cipher_set_cfg(td_handle handle, const ot_cipher_ctrl *ctrl) ++{ ++ td_s32 ret; ++ symc_cfg_t config; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(ctrl); ++ ++ ret = mpi_symc_set_cfg(&config, handle, ctrl); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_symc_set_cfg, ret); ++ return ret; ++ } ++ ++ ret = kapi_symc_cfg(&config); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_cfg, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_get_cfg(td_handle handle, ot_cipher_ctrl *ctrl) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(ctrl); ++ ++ ret = kapi_symc_get_cfg(handle, ctrl); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_get_cfg, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Performs encryption. ++ */ ++td_s32 ot_mpi_cipher_encrypt(td_handle handle, ++ td_phys_addr_t src_phys_addr, td_phys_addr_t dst_phys_addr, td_u32 byte_len) ++{ ++ td_s32 ret; ++ symc_encrypt_t crypto; ++ ++ chk_param_err_return(byte_len > 4 * 1024 * 1024); // 4 * 1024 * 1024 is the max size of malloc ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ crypto.id = handle; ++ crypto.operation = SYMC_OPERATION_ENCRYPT; ++ crypto.last = TD_TRUE; ++ crypto.length = byte_len; ++ addr_u64(crypto.input) = src_phys_addr; ++ addr_u64(crypto.output) = dst_phys_addr; ++ ret = kapi_symc_crypto(&crypto); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_crypto, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Performs description. ++ */ ++td_s32 ot_mpi_cipher_decrypt(td_handle handle, ++ td_phys_addr_t src_phys_addr, td_phys_addr_t dst_phys_addr, td_u32 byte_len) ++{ ++ td_s32 ret; ++ symc_encrypt_t crypto; ++ ++ chk_param_err_return(byte_len > 4 * 1024 * 1024); // 4 * 1024 * 1024 is the max size of malloc ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ crypto.id = handle; ++ crypto.operation = SYMC_OPERATION_DECRYPT; ++ crypto.last = TD_TRUE; ++ crypto.length = byte_len; ++ addr_u64(crypto.input) = src_phys_addr; ++ addr_u64(crypto.output) = dst_phys_addr; ++ ++ ret = kapi_symc_crypto(&crypto); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_crypto, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Performs encryption. ++ */ ++td_s32 ot_mpi_cipher_encrypt_virt(td_handle handle, const td_u8 *src_data, td_u8 *dst_data, td_u32 byte_len) ++{ ++ td_s32 ret; ++ symc_encrypt_t crypto; ++ ++ chk_param_err_return(byte_len > 4 * 1024 * 1024); // 4 * 1024 * 1024 is the max size of malloc ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(src_data); ++ chk_ptr_err_return(dst_data); ++ ++ crypto.id = handle; ++ crypto.operation = SYMC_OPERATION_ENCRYPT; ++ crypto.last = TD_TRUE; ++ crypto.length = byte_len; ++ addr_via_const(crypto.input) = src_data; ++ addr_via(crypto.output) = dst_data; ++ ++ ret = kapi_symc_crypto(&crypto); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_crypto, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Performs decryption. ++ */ ++td_s32 ot_mpi_cipher_decrypt_virt(td_handle handle, const td_u8 *src_data, td_u8 *dst_data, td_u32 byte_len) ++{ ++ td_s32 ret; ++ symc_encrypt_t crypto; ++ ++ chk_param_err_return(byte_len > 4 * 1024 * 1024); // 4 * 1024 * 1024 is the max size of malloc ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(src_data); ++ chk_ptr_err_return(dst_data); ++ ++ crypto.id = handle; ++ crypto.operation = SYMC_OPERATION_DECRYPT; ++ crypto.last = TD_TRUE; ++ crypto.length = byte_len; ++ addr_via_const(crypto.input) = src_data; ++ addr_via(crypto.output) = dst_data; ++ ++ ret = kapi_symc_crypto(&crypto); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_crypto, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++static td_s32 mpi_cipher_crypto_multi_pack(td_handle handle, td_u32 operation, const ot_cipher_data *data_pack, ++ td_u32 data_pack_num) ++{ ++ td_u32 i; ++ td_s32 ret; ++ symc_encrypt_t crypto; ++ symc_cfg_t config; ++ ot_cipher_ctrl ctrl = {0}; ++ ret = kapi_symc_get_cfg(handle, &ctrl); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_get_cfg, ret); ++ return ret; ++ } ++ for (i = 0; i < data_pack_num; i++) { ++ chk_param_err_return(data_pack[i].byte_len > 4 * 1024 * 1024); // 4 * 1024 * 1024 is the max size of malloc ++ ret = mpi_symc_set_cfg(&config, handle, &ctrl); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_symc_set_cfg, ret); ++ return ret; ++ } ++ ++ ret = kapi_symc_cfg(&config); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_cfg, ret); ++ return ret; ++ } ++ ++ crypto.id = handle; ++ crypto.operation = operation; ++ crypto.last = TD_TRUE; ++ crypto.length = data_pack[i].byte_len; ++ addr_u64(crypto.input) = data_pack[i].src_phys_addr; ++ addr_u64(crypto.output) = data_pack[i].dst_phys_addr; ++ ret = kapi_symc_crypto(&crypto); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_crypto, ret); ++ return ret; ++ } ++ } ++ return ret; ++} ++/* ++ * brief Encrypt multiple packaged data. ++ */ ++td_s32 ot_mpi_cipher_encrypt_multi_pack(td_handle handle, const ot_cipher_data *data_pack, td_u32 data_pack_num) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(data_pack); ++ chk_param_err_return(data_pack_num == 0x00); ++ chk_param_err_return(data_pack_num > CIPHER_MAX_MULTIPAD_NUM); ++ ++ ret = mpi_cipher_crypto_multi_pack(handle, SYMC_OPERATION_ENCRYPT, data_pack, data_pack_num); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_cipher_crypto_multi_pack, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++/* ++ * brief Encrypt multiple packaged data. ++ */ ++td_s32 ot_mpi_cipher_decrypt_multi_pack(td_handle handle, const ot_cipher_data *data_pack, td_u32 data_pack_num) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(data_pack); ++ chk_param_err_return(data_pack_num == 0x00); ++ chk_param_err_return(data_pack_num > CIPHER_MAX_MULTIPAD_NUM); ++ ++ ret = mpi_cipher_crypto_multi_pack(handle, SYMC_OPERATION_DECRYPT, data_pack, data_pack_num); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_cipher_crypto_multi_pack, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_get_tag(td_handle handle, td_u8 *tag, td_u32 tag_len) ++{ ++ td_s32 ret; ++ td_u8 real_tag[MAX_TAG_LEN] = {0}; ++ td_u32 real_tag_len = MAX_TAG_LEN; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(tag); ++ ++ ret = kapi_aead_get_tag(handle, (td_u32 *)real_tag, &real_tag_len); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_aead_get_tag, ret); ++ return ret; ++ } ++ ++ if (tag_len < real_tag_len) { ++ log_error("Error: tag_len is less than the output tag len\n"); ++ (td_void)memset_s(real_tag, sizeof(real_tag), 0, sizeof(real_tag)); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ ret = memcpy_s(tag, tag_len, real_tag, real_tag_len); ++ if (ret != EOK) { ++ log_error("Error: memcpy_s for tag failed\n"); ++ (td_void)memset_s(real_tag, sizeof(real_tag), 0, sizeof(real_tag)); ++ return OT_ERR_CIPHER_FAILED_SEC_FUNC; ++ } ++ ++ func_exit(); ++ ++ (td_void)memset_s(real_tag, sizeof(real_tag), 0, sizeof(real_tag)); ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_get_random_num(td_u32 *random_num) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(random_num); ++ ++ ret = kapi_trng_get_random(random_num, 0); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_trng_get_random, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_hash_init(const ot_cipher_hash_attr *hash_attr, td_handle *handle) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(hash_attr); ++ chk_ptr_err_return(handle); ++ ++ ret = kapi_hash_start(handle, hash_attr->sha_type, hash_attr->hmac_key, hash_attr->hmac_key_len); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_hash_start, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_hash_update(td_handle handle, const td_u8 *in_data, td_u32 in_data_len) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(in_data); ++ ++ ret = kapi_hash_update(handle, in_data, in_data_len, HASH_CHUNCK_SRC_LOCAL); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_hash_update, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_hash_final(td_handle handle, td_u8 *out_hash, td_u32 out_hash_len) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(out_hash); ++ chk_param_err_return(out_hash_len == 0); ++ chk_param_err_return(handle == (td_handle)OT_INVALID_HANDLE); ++ ++ ret = kapi_hash_finish(handle, out_hash, out_hash_len, &out_hash_len); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_hash_finish, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++} ++ ++static td_s32 mpi_rsa_chk_private_key(const ot_cipher_rsa_private_key *rsa_key) ++{ ++ chk_ptr_err_return(rsa_key); ++ chk_ptr_err_return(rsa_key->n); ++ chk_param_err_return(rsa_key->n_len < RSA_MIN_KEY_LEN); ++ chk_param_err_return(rsa_key->n_len > RSA_MAX_KEY_LEN); ++ ++ if (rsa_key->d == TD_NULL) { ++ chk_ptr_err_return(rsa_key->p); ++ chk_ptr_err_return(rsa_key->q); ++ chk_ptr_err_return(rsa_key->dp); ++ chk_ptr_err_return(rsa_key->dq); ++ chk_ptr_err_return(rsa_key->qp); ++ chk_param_err_return((rsa_key->n_len >> 1) != rsa_key->p_len); ++ chk_param_err_return((rsa_key->n_len >> 1) != rsa_key->q_len); ++ chk_param_err_return((rsa_key->n_len >> 1) != rsa_key->dp_len); ++ chk_param_err_return((rsa_key->n_len >> 1) != rsa_key->dq_len); ++ chk_param_err_return((rsa_key->n_len >> 1) != rsa_key->qp_len); ++ } else { ++ chk_param_err_return(rsa_key->n_len != rsa_key->d_len); ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_void mpi_rsa_init_private_key(cryp_rsa_key *key, const ot_cipher_rsa_private_key *rsa_key) ++{ ++ (td_void)memset_s(key, sizeof(cryp_rsa_key), 0, sizeof(cryp_rsa_key)); ++ key->public = TD_FALSE; ++ key->klen = rsa_key->n_len; ++ key->n = rsa_key->n; ++ key->d = rsa_key->d; ++ key->p = rsa_key->p; ++ key->q = rsa_key->q; ++ key->dp = rsa_key->dp; ++ key->dq = rsa_key->dq; ++ key->qp = rsa_key->qp; ++} ++ ++static td_s32 mpi_rsa_get_crypto_attr(ot_cipher_rsa_encrypt_scheme *rsa_scheme, ++ ot_cipher_rsa_scheme scheme, ot_cipher_hash_type sha_type) ++{ ++ if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V15) { ++ *rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_PKCS1_V1_5; ++ } else if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V21) { ++ switch (sha_type) { ++ case OT_CIPHER_HASH_TYPE_SHA256: ++ *rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA256; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA384: ++ *rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA384; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA512: ++ *rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA512; ++ break; ++ default: ++ log_error("Invalid sha type %d\n", sha_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else { ++ log_error("Invalid scheme %d\n", scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_void cipher_rsa_get_hashlen(td_u32 *hash_len, ot_cipher_hash_type sha_type) ++{ ++ switch (sha_type) { ++ case OT_CIPHER_HASH_TYPE_SHA256: ++ *hash_len = SHA256_RESULT_SIZE; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA384: ++ *hash_len = SHA384_RESULT_SIZE; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA512: ++ *hash_len = SHA512_RESULT_SIZE; ++ break; ++ default: ++ log_error("Invalid sha type %d\n", sha_type); ++ } ++} ++ ++static td_s32 cipher_rsa_pubkey_len_check(ot_cipher_rsa_scheme scheme, const ot_cipher_rsa_public_key *public_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_hash_type sha_type) ++{ ++ if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V15) { ++ chk_param_err_return(plain_txt->data_len > public_key->n_len - 11); ++ } else if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V21) { ++ td_u32 hash_len = 0; ++ cipher_rsa_get_hashlen(&hash_len, sha_type); ++ /* the max len of plain txt is key_len - 2 * hash_len - 1 */ ++ chk_param_err_return(plain_txt->data_len > public_key->n_len - 2 * hash_len - 1); ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 cipher_rsa_prikey_len_check(ot_cipher_rsa_scheme scheme, const ot_cipher_rsa_private_key *private_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_hash_type sha_type) ++{ ++ if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V15) { ++ chk_param_err_return(plain_txt->data_len > private_key->n_len - 11); ++ } else if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V21) { ++ td_u32 hash_len = 0; ++ cipher_rsa_get_hashlen(&hash_len, sha_type); ++ /* the max len of plain txt is key_len - 2 * hash_len - 1 */ ++ chk_param_err_return(plain_txt->data_len > private_key->n_len - 2 * hash_len - 1); ++ } ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_rsa_public_encrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_public_key *rsa_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_common_data *cipher_txt) ++{ ++ td_s32 ret; ++ cryp_rsa_key key; ++ cryp_rsa_crypto_data rsa_data = {0}; ++ ot_cipher_rsa_encrypt_scheme rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_BUTT; ++ ++ mpi_chk_init_err_return(); ++ chk_ptr_err_return(rsa_key); ++ chk_ptr_err_return(rsa_key->n); ++ chk_ptr_err_return(rsa_key->e); ++ chk_param_err_return((rsa_key->n_len < RSA_MIN_KEY_LEN) || (rsa_key->n_len > RSA_MAX_KEY_LEN)); ++ chk_param_err_return(rsa_key->n_len < rsa_key->e_len); ++ ++ chk_ptr_err_return(plain_txt); ++ chk_ptr_err_return(plain_txt->data); ++ chk_param_err_return(plain_txt->data_len == 0); ++ ++ chk_ptr_err_return(cipher_txt); ++ chk_ptr_err_return(cipher_txt->data); ++ chk_param_err_return((cipher_txt->data_len == 0) || (cipher_txt->data_len < rsa_key->n_len)); ++ ++ rsa_data.in = plain_txt->data; ++ rsa_data.in_len = plain_txt->data_len; ++ rsa_data.out = cipher_txt->data; ++ rsa_data.out_len = rsa_key->n_len; ++ ++ ret = mpi_rsa_get_crypto_attr(&rsa_scheme, scheme, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_get_crypto_attr, ret); ++ return ret; ++ } ++ ret = cipher_rsa_pubkey_len_check(scheme, rsa_key, plain_txt, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(cipher_rsa_keylen_check, ret); ++ return ret; ++ } ++ ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ key.public = TD_TRUE; ++ key.klen = rsa_key->n_len; ++ key.n = rsa_key->n; ++ ++ cipher_get_pub_exponent(&key.e, rsa_key); ++ ++ ret = kapi_rsa_encrypt(rsa_scheme, &key, &rsa_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_rsa_encrypt, ret); ++ } ++ cipher_txt->data_len = rsa_data.out_len; ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ return ret; ++} ++ ++td_s32 ot_mpi_cipher_rsa_private_decrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_private_key *rsa_key, ++ const ot_cipher_common_data *cipher_txt, ot_cipher_common_data *plain_txt) ++{ ++ td_s32 ret; ++ cryp_rsa_key key; ++ cryp_rsa_crypto_data rsa_data = {0}; ++ ot_cipher_rsa_encrypt_scheme rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_BUTT; ++ ++ mpi_chk_init_err_return(); ++ ret = mpi_rsa_chk_private_key(rsa_key); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_chk_private_key, ret); ++ return ret; ++ } ++ ++ chk_ptr_err_return(plain_txt); ++ chk_ptr_err_return(plain_txt->data); ++ chk_param_err_return(plain_txt->data_len == 0); ++ ++ chk_ptr_err_return(cipher_txt); ++ chk_ptr_err_return(cipher_txt->data); ++ chk_param_err_return((cipher_txt->data_len == 0) || (cipher_txt->data_len != rsa_key->n_len)); ++ ++ rsa_data.in = cipher_txt->data; ++ rsa_data.in_len = cipher_txt->data_len; ++ rsa_data.out = plain_txt->data; ++ rsa_data.out_len = rsa_key->n_len; ++ ++ ret = mpi_rsa_get_crypto_attr(&rsa_scheme, scheme, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_get_crypto_attr, ret); ++ return ret; ++ } ++ ++ mpi_rsa_init_private_key(&key, rsa_key); ++ ++ ret = kapi_rsa_decrypt(rsa_scheme, &key, &rsa_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_rsa_decrypt, ret); ++ } ++ plain_txt->data_len = rsa_data.out_len; ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ return ret; ++} ++ ++td_s32 ot_mpi_cipher_rsa_private_encrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_private_key *rsa_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_common_data *cipher_txt) ++{ ++ td_s32 ret; ++ cryp_rsa_key key; ++ cryp_rsa_crypto_data rsa_data = {0}; ++ ot_cipher_rsa_encrypt_scheme rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_BUTT; ++ ++ mpi_chk_init_err_return(); ++ ret = mpi_rsa_chk_private_key(rsa_key); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_chk_private_key, ret); ++ return ret; ++ } ++ ++ chk_ptr_err_return(plain_txt); ++ chk_ptr_err_return(plain_txt->data); ++ chk_param_err_return(plain_txt->data_len == 0); ++ ++ chk_ptr_err_return(cipher_txt); ++ chk_ptr_err_return(cipher_txt->data); ++ chk_param_err_return((cipher_txt->data_len == 0) || (cipher_txt->data_len < rsa_key->n_len)); ++ ++ rsa_data.in = plain_txt->data; ++ rsa_data.in_len = plain_txt->data_len; ++ rsa_data.out = cipher_txt->data; ++ rsa_data.out_len = rsa_key->n_len; ++ ++ ret = mpi_rsa_get_crypto_attr(&rsa_scheme, scheme, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_get_crypto_attr, ret); ++ return ret; ++ } ++ ret = cipher_rsa_prikey_len_check(scheme, rsa_key, plain_txt, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(cipher_rsa_keylen_check, ret); ++ return ret; ++ } ++ ++ mpi_rsa_init_private_key(&key, rsa_key); ++ ++ ret = kapi_rsa_encrypt(rsa_scheme, &key, &rsa_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_rsa_encrypt, ret); ++ } ++ cipher_txt->data_len = rsa_data.out_len; ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ return ret; ++} ++ ++td_s32 ot_mpi_cipher_rsa_public_decrypt(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_public_key *rsa_key, ++ const ot_cipher_common_data *cipher_txt, ot_cipher_common_data *plain_txt) ++{ ++ td_s32 ret; ++ cryp_rsa_key key; ++ cryp_rsa_crypto_data rsa_data = {0}; ++ ot_cipher_rsa_encrypt_scheme rsa_scheme = OT_CIPHER_RSA_ENCRYPT_SCHEME_BUTT; ++ ++ mpi_chk_init_err_return(); ++ chk_ptr_err_return(rsa_key); ++ chk_ptr_err_return(rsa_key->n); ++ chk_ptr_err_return(rsa_key->e); ++ chk_param_err_return((rsa_key->n_len < RSA_MIN_KEY_LEN) || (rsa_key->n_len > RSA_MAX_KEY_LEN)); ++ chk_param_err_return(rsa_key->n_len < rsa_key->e_len); ++ ++ chk_ptr_err_return(plain_txt); ++ chk_ptr_err_return(plain_txt->data); ++ chk_param_err_return(plain_txt->data_len == 0); ++ ++ chk_ptr_err_return(cipher_txt); ++ chk_ptr_err_return(cipher_txt->data); ++ chk_param_err_return((cipher_txt->data_len == 0) || (cipher_txt->data_len != rsa_key->n_len)); ++ ++ rsa_data.in = cipher_txt->data; ++ rsa_data.in_len = cipher_txt->data_len; ++ rsa_data.out = plain_txt->data; ++ rsa_data.out_len = rsa_key->n_len; ++ ++ ret = mpi_rsa_get_crypto_attr(&rsa_scheme, scheme, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_get_crypto_attr, ret); ++ return ret; ++ } ++ ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ key.public = TD_TRUE; ++ key.klen = rsa_key->n_len; ++ key.n = rsa_key->n; ++ cipher_get_pub_exponent(&key.e, rsa_key); ++ ++ ret = kapi_rsa_decrypt(rsa_scheme, &key, &rsa_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_rsa_decrypt, ret); ++ } ++ plain_txt->data_len = rsa_data.out_len; ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ return ret; ++} ++ ++static td_s32 mpi_rsa_chk_sign_param(const ot_cipher_rsa_private_key *rsa_key, ++ const ot_cipher_sign_in_data *rsa_data, const ot_cipher_common_data *sign_data) ++{ ++ td_s32 ret; ++ ret = mpi_rsa_chk_private_key(rsa_key); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_chk_private_key, ret); ++ return ret; ++ } ++ ++ /* check sign data */ ++ chk_ptr_err_return(rsa_data); ++ chk_ptr_err_return(rsa_data->input); ++ chk_param_err_return(rsa_data->sign_type >= OT_CIPHER_SIGN_TYPE_BUTT); ++ chk_param_err_return(rsa_data->input_len == 0); ++ ++ chk_ptr_err_return(sign_data); ++ chk_ptr_err_return(sign_data->data); ++ chk_param_err_return((sign_data->data_len == 0) || (sign_data->data_len < rsa_key->n_len)); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 mpi_rsa_get_sign_attr(ot_cipher_rsa_sign_scheme *rsa_scheme, ++ td_u32 *hash_len, ot_cipher_rsa_scheme scheme, ot_cipher_hash_type sha_type) ++{ ++ if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V15) { ++ switch (sha_type) { ++ case OT_CIPHER_HASH_TYPE_SHA256: ++ *rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA256; ++ *hash_len = SHA256_RESULT_SIZE; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA384: ++ *rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA384; ++ *hash_len = SHA384_RESULT_SIZE; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA512: ++ *rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA512; ++ *hash_len = SHA512_RESULT_SIZE; ++ break; ++ default: ++ log_error("Invalid sha type %d\n", sha_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else if (scheme == OT_CIPHER_RSA_SCHEME_PKCS1_V21) { ++ switch (sha_type) { ++ case OT_CIPHER_HASH_TYPE_SHA256: ++ *rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA256; ++ *hash_len = SHA256_RESULT_SIZE; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA384: ++ *rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA384; ++ *hash_len = SHA384_RESULT_SIZE; ++ break; ++ case OT_CIPHER_HASH_TYPE_SHA512: ++ *rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA512; ++ *hash_len = SHA512_RESULT_SIZE; ++ break; ++ default: ++ log_error("Invalid sha type %d\n", sha_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else { ++ log_error("Invalid scheme %d\n", scheme); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 mpi_cipher_hash(ot_cipher_hash_type sha_type, ++ const td_u8 *in_data, td_u32 in_len, td_u8 *hash_data, td_u32 hash_len) ++{ ++ td_s32 ret; ++ td_handle hash_id; ++ ++ ret = kapi_hash_start(&hash_id, sha_type, TD_NULL, 0); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_hash_init, ret); ++ return ret; ++ } ++ ++ ret = kapi_hash_update(hash_id, in_data, in_len, HASH_CHUNCK_SRC_LOCAL); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_hash_update, ret); ++ return ret; ++ } ++ ++ ret = kapi_hash_finish(hash_id, hash_data, hash_len, &hash_len); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_hash_finish, ret); ++ return ret; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_rsa_sign(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_private_key *rsa_key, ++ const ot_cipher_sign_in_data *rsa_data, ot_cipher_common_data *sign_data) ++{ ++ td_s32 ret; ++ cryp_rsa_key key; ++ td_u8 hash[HASH_RESULT_MAX_SIZE] = {0}; ++ cryp_rsa_sign_data sys_data = {0}; ++ ot_cipher_rsa_sign_scheme rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_BUTT; ++ ++ mpi_chk_init_err_return(); ++ ret = mpi_rsa_chk_sign_param(rsa_key, rsa_data, sign_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_chk_sign_param, ret); ++ return ret; ++ } ++ ++ (td_void)memset_s(&sys_data, sizeof(sys_data), 0, sizeof(sys_data)); ++ ret = mpi_rsa_get_sign_attr(&rsa_scheme, &sys_data.in_len, scheme, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_get_sign_attr, ret); ++ return ret; ++ } ++ ++ /* hash value of context, if OT_CIPHER_SIGN_TYPE_HASH, compute hash = Hash(in_data). */ ++ if (rsa_data->sign_type == OT_CIPHER_SIGN_TYPE_MSG) { ++ ret = mpi_cipher_hash(sha_type, rsa_data->input, rsa_data->input_len, hash, sizeof(hash)); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_cipher_hash, ret); ++ return ret; ++ } ++ sys_data.in = hash; ++ } else if (rsa_data->sign_type == OT_CIPHER_SIGN_TYPE_HASH) { ++ sys_data.in = rsa_data->input; ++ if (rsa_data->input_len != sys_data.in_len) { ++ log_error("Invalid input len %u\n", rsa_data->input_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else { ++ log_error("Invalid sign type %d\n", rsa_data->sign_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ sys_data.out = sign_data->data; ++ sys_data.out_len = rsa_key->n_len; ++ ++ mpi_rsa_init_private_key(&key, rsa_key); ++ ++ ret = kapi_rsa_sign_hash(rsa_scheme, &key, &sys_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_rsa_sign_hash, ret); ++ } ++ sign_data->data_len = sys_data.out_len; ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ return ret; ++} ++ ++static td_s32 mpi_rsa_chk_verify_param(const ot_cipher_rsa_public_key *rsa_key, ++ const ot_cipher_sign_in_data *rsa_data, const ot_cipher_common_data *sign_data) ++{ ++ /* check public key */ ++ chk_ptr_err_return(rsa_key); ++ chk_ptr_err_return(rsa_key->n); ++ chk_ptr_err_return(rsa_key->e); ++ chk_param_err_return((rsa_key->n_len < RSA_MIN_KEY_LEN) || (rsa_key->n_len > RSA_MAX_KEY_LEN)); ++ chk_param_err_return(rsa_key->n_len < rsa_key->e_len); ++ ++ /* check verify data */ ++ chk_ptr_err_return(rsa_data); ++ chk_ptr_err_return(rsa_data->input); ++ chk_param_err_return(rsa_data->sign_type >= OT_CIPHER_SIGN_TYPE_BUTT); ++ chk_param_err_return(rsa_data->input_len == 0); ++ ++ chk_ptr_err_return(sign_data); ++ chk_ptr_err_return(sign_data->data); ++ chk_param_err_return((sign_data->data_len == 0) || (sign_data->data_len != rsa_key->n_len)); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_rsa_verify(ot_cipher_rsa_scheme scheme, ++ ot_cipher_hash_type sha_type, const ot_cipher_rsa_public_key *rsa_key, ++ const ot_cipher_sign_in_data *rsa_data, const ot_cipher_common_data *sign_data) ++{ ++ td_s32 ret; ++ cryp_rsa_key key; ++ td_u8 hash[HASH_RESULT_MAX_SIZE] = {0}; ++ cryp_rsa_sign_data sys_data; ++ ot_cipher_rsa_sign_scheme rsa_scheme = OT_CIPHER_RSA_SIGN_SCHEME_BUTT; ++ ++ mpi_chk_init_err_return(); ++ ret = mpi_rsa_chk_verify_param(rsa_key, rsa_data, sign_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_chk_verify_param, ret); ++ return ret; ++ } ++ ++ (td_void)memset_s(&sys_data, sizeof(sys_data), 0, sizeof(sys_data)); ++ ret = mpi_rsa_get_sign_attr(&rsa_scheme, &sys_data.in_len, scheme, sha_type); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_rsa_get_sign_attr, ret); ++ return ret; ++ } ++ ++ /* hash value of context, if OT_CIPHER_SIGN_TYPE_HASH, compute hash = Hash(in_data). */ ++ if (rsa_data->sign_type == OT_CIPHER_SIGN_TYPE_MSG) { ++ ret = mpi_cipher_hash(sha_type, rsa_data->input, rsa_data->input_len, hash, sizeof(hash)); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(mpi_cipher_hash, ret); ++ return ret; ++ } ++ sys_data.in = hash; ++ } else if (rsa_data->sign_type == OT_CIPHER_SIGN_TYPE_HASH) { ++ sys_data.in = rsa_data->input; ++ if (rsa_data->input_len != sys_data.in_len) { ++ log_error("Invalid input len %u\n", rsa_data->input_len); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ } else { ++ log_error("Invalid sign type %d\n", rsa_data->sign_type); ++ return OT_ERR_CIPHER_INVALID_PARAM; ++ } ++ sys_data.out = sign_data->data; ++ sys_data.out_len = sign_data->data_len; ++ ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ key.public = TD_TRUE; ++ key.klen = rsa_key->n_len; ++ key.n = rsa_key->n; ++ cipher_get_pub_exponent(&key.e, rsa_key); ++ ++ ret = kapi_rsa_verify_hash(rsa_scheme, &key, &sys_data); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_rsa_verify_hash, ret); ++ } ++ ++ (td_void)memset_s(&key, sizeof(key), 0, sizeof(key)); ++ return ret; ++} ++ ++td_s32 ot_mpi_cipher_sm2_sign(const ot_cipher_sm2_sign *sm2_sign, ++ const ot_cipher_sign_in_data *sm2_data, ot_cipher_sm2_sign_data *sign_data) ++{ ++#ifdef CHIP_SM2_SUPPORT ++ td_s32 ret; ++ sm2_sign_t sign; ++ ++ func_enter(); ++ ++ chk_ptr_err_return(sm2_sign); ++ chk_ptr_err_return(sm2_sign->id); ++ chk_param_err_return(sm2_sign->id_len > SM2_ID_MAX_LEN); ++ ++ chk_ptr_err_return(sm2_data); ++ chk_ptr_err_return(sm2_data->input); ++ chk_param_err_return(sm2_data->sign_type != OT_CIPHER_SIGN_TYPE_MSG); ++ chk_param_err_return(sm2_data->input_len == 0); ++ ++ chk_ptr_err_return(sign_data); ++ ++ (td_void)memset_s(&sign, sizeof(sm2_sign_t), 0, sizeof(sm2_sign_t)); ++ (td_void)memcpy_s(sign.d, SM2_LEN_IN_BYTE, sm2_sign->d, SM2_LEN_IN_BYTE); ++ (td_void)memcpy_s(sign.px, SM2_LEN_IN_BYTE, sm2_sign->px, SM2_LEN_IN_BYTE); ++ (td_void)memcpy_s(sign.py, SM2_LEN_IN_BYTE, sm2_sign->py, SM2_LEN_IN_BYTE); ++ sign.msg = sm2_data->input; ++ sign.msglen = sm2_data->input_len; ++ ++ ret = kapi_sm2_sign(&sign, sm2_sign->id, sm2_sign->id_len, HASH_CHUNCK_SRC_LOCAL); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_sm2_sign, ret); ++ } ++ (td_void)memcpy_s(sign_data->r, SM2_LEN_IN_BYTE, sign.r, SM2_LEN_IN_BYTE); ++ (td_void)memcpy_s(sign_data->s, SM2_LEN_IN_BYTE, sign.s, SM2_LEN_IN_BYTE); ++ ++ func_exit(); ++ return ret; ++#else ++ crypto_unused(sm2_sign); ++ crypto_unused(sm2_data); ++ crypto_unused(sign_data); ++ log_error("Unsupported SM2\n"); ++ return OT_ERR_CIPHER_UNSUPPORTED; ++#endif ++} ++ ++td_s32 ot_mpi_cipher_sm2_verify(const ot_cipher_sm2_verify *sm2_verify, ++ const ot_cipher_sign_in_data *sm2_data, const ot_cipher_sm2_sign_data *sign_data) ++{ ++#ifdef CHIP_SM2_SUPPORT ++ td_s32 ret; ++ sm2_verify_t verify; ++ ++ func_enter(); ++ ++ chk_ptr_err_return(sm2_verify); ++ chk_ptr_err_return(sm2_verify->id); ++ chk_param_err_return(sm2_verify->id_len > SM2_ID_MAX_LEN); ++ ++ chk_ptr_err_return(sm2_data); ++ chk_ptr_err_return(sm2_data->input); ++ chk_param_err_return(sm2_data->sign_type != OT_CIPHER_SIGN_TYPE_MSG); ++ chk_param_err_return(sm2_data->input_len == 0); ++ ++ chk_ptr_err_return(sign_data); ++ ++ (td_void)memset_s(&verify, sizeof(sm2_verify_t), 0, sizeof(sm2_verify_t)); ++ (td_void)memcpy_s(verify.px, SM2_LEN_IN_BYTE, sm2_verify->px, SM2_LEN_IN_BYTE); ++ (td_void)memcpy_s(verify.py, SM2_LEN_IN_BYTE, sm2_verify->py, SM2_LEN_IN_BYTE); ++ verify.msg = sm2_data->input; ++ verify.msglen = sm2_data->input_len; ++ (td_void)memcpy_s(verify.r, SM2_LEN_IN_BYTE, sign_data->r, SM2_LEN_IN_BYTE); ++ (td_void)memcpy_s(verify.s, SM2_LEN_IN_BYTE, sign_data->s, SM2_LEN_IN_BYTE); ++ ret = kapi_sm2_verify(&verify, sm2_verify->id, sm2_verify->id_len, HASH_CHUNCK_SRC_LOCAL); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_sm2_verify, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++#else ++ crypto_unused(sm2_verify); ++ crypto_unused(sm2_data); ++ crypto_unused(sign_data); ++ log_error("Unsupported SM2\n"); ++ return OT_ERR_CIPHER_UNSUPPORTED; ++#endif ++} ++ ++td_s32 ot_mpi_cipher_sm2_encrypt(const ot_cipher_sm2_public_key *sm2_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_common_data *cipher_txt) ++{ ++#ifdef CHIP_SM2_SUPPORT ++ td_s32 ret; ++ ++ func_enter(); ++ ++ chk_ptr_err_return(sm2_key); ++ ++ chk_ptr_err_return(plain_txt); ++ chk_ptr_err_return(plain_txt->data); ++ chk_param_err_return(plain_txt->data_len == 0); ++ ++ chk_ptr_err_return(cipher_txt); ++ chk_ptr_err_return(cipher_txt->data); ++ chk_param_err_return(cipher_txt->data_len == 0); ++ ++ ret = kapi_sm2_encrypt(sm2_key, plain_txt, cipher_txt); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_sm2_encrypt, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++#else ++ crypto_unused(sm2_key); ++ crypto_unused(plain_txt); ++ crypto_unused(cipher_txt); ++ log_error("Unsupported SM2\n"); ++ return OT_ERR_CIPHER_UNSUPPORTED; ++#endif ++} ++ ++td_s32 ot_mpi_cipher_sm2_decrypt(const ot_cipher_sm2_private_key *sm2_key, ++ const ot_cipher_common_data *cipher_txt, ot_cipher_common_data *plain_txt) ++{ ++#ifdef CHIP_SM2_SUPPORT ++ td_s32 ret; ++ ++ func_enter(); ++ ++ chk_ptr_err_return(sm2_key); ++ ++ chk_ptr_err_return(plain_txt); ++ chk_ptr_err_return(plain_txt->data); ++ chk_param_err_return(plain_txt->data_len == 0); ++ ++ chk_ptr_err_return(cipher_txt); ++ chk_ptr_err_return(cipher_txt->data); ++ chk_param_err_return(cipher_txt->data_len == 0); ++ ++ ret = kapi_sm2_decrypt(sm2_key, cipher_txt, plain_txt); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_sm2_decrypt, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ return TD_SUCCESS; ++#else ++ crypto_unused(sm2_key); ++ crypto_unused(plain_txt); ++ crypto_unused(cipher_txt); ++ log_error("Unsupported SM2\n"); ++ return OT_ERR_CIPHER_UNSUPPORTED; ++#endif ++} ++ ++td_s32 ot_mpi_keyslot_create(const ot_keyslot_attr *attr, td_handle *keyslot) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ chk_ptr_err_return(attr); ++ chk_ptr_err_return(keyslot); ++ ++ ret = kapi_keyslot_create(attr, keyslot); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_keyslot_create, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_keyslot_destroy(td_handle keyslot) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ ret = kapi_keyslot_destroy(keyslot); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_keyslot_destroy, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_attach(td_handle cipher, td_handle keyslot) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ ret = kapi_symc_attach(cipher, keyslot); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_attach, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_cipher_detach(td_handle cipher, td_handle keyslot) ++{ ++ td_s32 ret; ++ ++ mpi_chk_init_err_return(); ++ func_enter(); ++ ++ ret = kapi_symc_detach(cipher, keyslot); ++ if (ret != TD_SUCCESS) { ++ print_func_errno(kapi_symc_detach, ret); ++ return ret; ++ } ++ ++ func_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++/** @} */ /** MGF ---> xor ++ +----------+ | ++ | | ++ +--+ V | ++ |00| xor <----- MGF <-----| ++ +--+ | | ++ | | | ++ V V V ++ +--+----------+----------------------------+ ++ EM = |00|maskedSeed| maskedDB | ++ +--+----------+----------------------------+ ++ 1 hlen k - hlen- 1 ++ ++so: PS_LEN = k - hlen - 1 - (hlen + mlen + 1) = k - 2hlen - mlen - 2 > 0 ++so: mlen < k - 2hlen - 2 ++ ************************************************************ */ ++td_s32 rsa_encrypt_pad_pkcs_oaep(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *plain_txt, ot_data_t *em_txt) ++{ ++ td_s32 ret; ++ td_u8 *ptr_db = TD_NULL; ++ td_u8 *ptr_seed = TD_NULL; ++ const td_u8 *l_hash = TD_NULL; ++ ot_cipher_hash_type hash_type = OT_CIPHER_HASH_TYPE_BUTT; ++ td_u32 hash_len = 0; ++ td_u32 em_idx = 0; ++ ++ ret = rsa_crypto_get_oaep_hash(scheme, &hash_type, &l_hash, &hash_len); ++ chk_func_return_err(rsa_crypto_get_oaep_hash, ret != TD_SUCCESS, ret); ++ ++ chk_param_err_return(plain_txt->data_len > key->klen - 2 * hash_len - 2); /* 2 */ ++ ++ ptr_seed = em_txt->data + 1; ++ ptr_db = em_txt->data + hash_len + 1; ++ ++ /* 1. set data[0] = 0 */ ++ em_txt->data[em_idx++] = 0; ++ ++ /* 2. set data[1, hash_len + 1] = random */ ++ ret = rsa_get_multi_random(&em_txt->data[em_idx], em_txt->data_len - em_idx, hash_len); ++ chk_func_return_err(rsa_get_multi_random, ret != TD_SUCCESS, ret); ++ em_idx += hash_len; ++ ++ /* 3. set data[hash_len + 1, 2hash_len + 1] = lHash */ ++ ret = memcpy_s(&em_txt->data[em_idx], em_txt->data_len - em_idx, l_hash, hash_len); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ em_idx += hash_len; ++ ++ /* 4. set PS with 0x00 */ ++ ret = memset_s(&em_txt->data[em_idx], em_txt->data_len - em_idx, ++ 0, key->klen - plain_txt->data_len - 2 * hash_len - 2); /* 2 */ ++ chk_func_return_err(memset_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ em_idx += key->klen - plain_txt->data_len - 2 * hash_len - 2; /* 2 */ ++ ++ /* 5. set 0x01 after PS */ ++ em_txt->data[em_idx++] = 0x01; ++ ++ /* 6. set M */ ++ ret = memcpy_s(&em_txt->data[em_idx], em_txt->data_len - em_idx, plain_txt->data, plain_txt->data_len); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ ++ /* 7. MGF: seed -> db */ ++ ret = mgf(hash_type, ptr_seed, hash_len, ptr_db, key->klen - hash_len - 1); ++ chk_func_return_err(mgf, ret != TD_SUCCESS, ret); ++ ++ /* 8. MGF: db -> seed */ ++ ret = mgf(hash_type, ptr_db, key->klen - hash_len - 1, ptr_seed, hash_len); ++ chk_func_return_err(mgf, ret != TD_SUCCESS, ret); ++ ++ return ret; ++} ++ ++td_s32 rsa_decrypt_pad_pkcs_oaep(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, ot_data_t *plain_txt, ot_data_t *em_txt, td_u32 *outlen) ++{ ++ td_s32 ret; ++ td_u8 *ptr_db = TD_NULL; ++ td_u8 *ptr_seed = TD_NULL; ++ const td_u8 *l_hash = TD_NULL; ++ ot_cipher_hash_type hash_type = OT_CIPHER_HASH_TYPE_BUTT; ++ td_u32 hash_len = 0; ++ td_s32 em_idx = 0; ++ ++ ret = rsa_crypto_get_oaep_hash(scheme, &hash_type, &l_hash, &hash_len); ++ chk_func_return_err(rsa_crypto_get_oaep_hash, ret != TD_SUCCESS, ret); ++ ++ ptr_seed = em_txt->data + 1; ++ ptr_db = em_txt->data + hash_len + 1; ++ ++ /* 1. check data[0] = 0 */ ++ if (em_txt->data[em_idx++] != 0x00) { ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ /* 2. MGF: db -> seed */ ++ ret = mgf(hash_type, ptr_db, key->klen - hash_len - 1, ptr_seed, hash_len); ++ chk_func_return_err(mgf, ret != TD_SUCCESS, ret); ++ ++ /* 3. MGF: seed -> db */ ++ ret = mgf(hash_type, ptr_seed, hash_len, ptr_db, key->klen - hash_len - 1); ++ chk_func_return_err(mgf, ret != TD_SUCCESS, ret); ++ ++ /* 4. check data[hash + 1, 2hash + 1] */ ++ em_idx += hash_len; ++ ret = memcmp(&em_txt->data[em_idx], l_hash, hash_len); ++ chk_func_return_err(memcmp, ret != 0, OT_ERR_CIPHER_FAILED_DECRYPT); ++ ++ /* 5. remove PS */ ++ em_idx += hash_len; ++ for (; em_idx < key->klen - 1; em_idx++) { ++ if (em_txt->data[em_idx] != 0x00) { ++ break; ++ } ++ } ++ ++ /* 6. check 0x01 */ ++ if (em_txt->data[em_idx] != 0x01) { ++ log_error("em_txt->data[%u] = %u isn't 0x01!\n", em_idx, em_txt->data[em_idx]); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ em_idx++; ++ ++ /* 7. check data length */ ++ *outlen = em_txt->data_len - em_idx; ++ if ((*outlen == 0) || (*outlen > key->klen - 2 * hash_len - 2)) { /* 2 */ ++ log_error("PS error.\n"); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ ++ ret = memcpy_s(plain_txt->data, plain_txt->data_len, &em_txt->data[em_idx], *outlen); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 rsa_sign_pad_pkcs_v15(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, ot_data_t *em_txt) ++{ ++ td_s32 ret; ++ td_u32 em_idx = 0; ++ td_u32 ps_len; ++ ot_data_t asn1 = {0}; ++ ot_cipher_hash_type hash_type = OT_CIPHER_HASH_TYPE_BUTT; ++ ++ chk_param_err_return(em_txt->data_len < key->klen); ++ ++ ret = rsa_sign_get_pkcs_v15_hash(scheme, &hash_type, &asn1); ++ chk_func_return_err(rsa_sign_get_pkcs_v15_hash, ret != TD_SUCCESS, ret); ++ ++ chk_param_err_return((hash_txt->data_len + asn1.data_len) > (key->klen - PKCS_V15_MIN_PAD_LEN)); ++ ++ /* 1. set data[0] = 0 */ ++ em_txt->data[em_idx++] = 0x00; ++ ++ /* 2. set data[1] = 1 */ ++ em_txt->data[em_idx++] = 0x01; ++ ++ /* 3. padding PS 0xFF */ ++ ps_len = key->klen - asn1.data_len - hash_txt->data_len - PKCS_V15_MIN_OTHER_LEN; ++ ret = memset_s(&em_txt->data[em_idx], em_txt->data_len - em_idx, 0xFF, ps_len); ++ chk_func_return_err(memset_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ em_idx += ps_len; ++ ++ /* 4. set data[2 + pslen] = 0 */ ++ em_txt->data[em_idx++] = 0x00; ++ ++ /* 5. combine asn1 */ ++ ret = memcpy_s(&em_txt->data[em_idx], em_txt->data_len - em_idx, asn1.data, asn1.data_len); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ em_idx += asn1.data_len; ++ ++ /* 6. combine hash */ ++ ret = memcpy_s(&em_txt->data[em_idx], em_txt->data_len - em_idx, hash_txt->data, hash_txt->data_len); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 rsa_verify_pad_pkcs_v15(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, const ot_data_t *em_txt) ++{ ++ td_s32 ret; ++ td_u32 em_idx = 0; ++ td_u32 ps_len; ++ ot_data_t asn1 = {0}; ++ ot_cipher_hash_type hash_type = OT_CIPHER_HASH_TYPE_BUTT; ++ ++ chk_param_err_return(em_txt->data_len < key->klen); ++ ++ ret = rsa_sign_get_pkcs_v15_hash(scheme, &hash_type, &asn1); ++ chk_func_return_err(rsa_sign_get_pkcs_v15_hash, ret != TD_SUCCESS, ret); ++ ++ chk_param_err_return((hash_txt->data_len + asn1.data_len) > (key->klen - PKCS_V15_MIN_PAD_LEN)); ++ ++ /* 1. check data[0] = 0 */ ++ chk_param_err_return(em_txt->data[em_idx++] != 0x00); ++ ++ /* 2. check data[1] = 0x01 */ ++ chk_param_err_return(em_txt->data[em_idx++] != 0x01); ++ ++ /* 3. padding PS */ ++ ps_len = key->klen - asn1.data_len - hash_txt->data_len - PKCS_V15_MIN_OTHER_LEN; ++ for (; em_idx < ps_len + 2; em_idx++) { /* cur em_idx offset 2 bytes */ ++ if (em_txt->data[em_idx] != 0xFF) { ++ log_error("em_txt[%u] %x isn't 0xFF\n", em_idx, em_txt->data[em_idx]); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ } ++ ++ /* 4. check data[2 + ps_len] = 0x00 */ ++ if (em_txt->data[em_idx] != 0x00) { ++ log_error("em_txt[%u] %x isn't 0xFF\n", em_idx, em_txt->data[em_idx]); ++ return OT_ERR_CIPHER_FAILED_DECRYPT; ++ } ++ em_idx++; ++ ++ /* 5. check asn1 */ ++ ret = memcmp(&em_txt->data[em_idx], asn1.data, asn1.data_len); ++ chk_func_return_err(memcmp, ret != 0, OT_ERR_CIPHER_FAILED_DECRYPT); ++ em_idx += asn1.data_len; ++ ++ /* 6. check hash */ ++ ret = memcmp(&em_txt->data[em_idx], hash_txt->data, hash_txt->data_len); ++ chk_func_return_err(memcmp, ret != 0, OT_ERR_CIPHER_FAILED_DECRYPT); ++ ++ return TD_SUCCESS; ++} ++ ++/* ***************************************************************** ++ +-----------+ ++ | M | ++ +-----------+ ++ | ++ V ++ Hash ++ | ++ V ++ +--------+----------+----------+ ++ M' = |Padding1| mHash | salt | ++ +--------+----------+----------+ ++ | ++ +--------+----------+ V ++ DB = |Padding2|maskedseed| Hash ++ +--------+----------+ | ++ | | ++ V | +--+ ++ xor <----- MGF <----| |bc| ++ | | +--+ ++ | | | ++ V V V ++ +-------------------+----- -------+--+ ++ EM = | maskedDB | maskedseed |bc| ++ +-------------------+-------------+--+ ++ ++Note: SIGN only support the length of salt that is equal to hash'length ++ ***************************************************************** */ ++td_s32 rsa_sign_pad_pkcs_pss(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, ot_data_t *em_txt, td_u32 *outlen) ++{ ++ td_s32 ret; ++ td_u32 ps_len; ++ rsa_pss_t pss = {0}; ++ td_u8 *ptr_db = TD_NULL; ++ td_u8 *ptr_seed = TD_NULL; ++ ot_cipher_hash_type hash_type = OT_CIPHER_HASH_TYPE_BUTT; ++ td_u8 salt[HASH_RESULT_MAX_LEN] = {0}; ++ ++ ret = rsa_sign_get_pss_hash_type(scheme, &hash_type); ++ chk_func_return_err(rsa_sign_get_pss_hash_type, ret != TD_SUCCESS, ret); ++ ++ pss.modbits = rsa_get_bit_num(key->n, key->klen); ++ pss.embits = pss.modbits - 1; ++ pss.emlen = (pss.modbits + 7) / 8; /* 7, 8, round up */ ++ ++ /* check emlen min length */ ++ chk_param_err_return(pss.emlen < (hash_txt->data_len * 2 + 2)); /* 2 */ ++ ++ ptr_db = em_txt->data; ++ ptr_seed = em_txt->data + pss.emlen - hash_txt->data_len - 1; ++ ++ ret = rsa_get_multi_random(salt, HASH_RESULT_MAX_LEN, hash_txt->data_len); ++ chk_func_return_err(rsa_get_multi_random, ret != TD_SUCCESS, ret); ++ pss.salt_txt.data = salt; ++ pss.salt_txt.data_len = hash_txt->data_len; ++ ++ /* 1. calculate H */ ++ ret = rsa_sign_calcu_pss_h(hash_type, hash_txt, &pss); ++ chk_func_return_err(rsa_sign_calcu_pss_h, ret != TD_SUCCESS, ret); ++ ++ ret = memcpy_s(ptr_seed, em_txt->data_len - (ptr_seed - em_txt->data), pss.hash, hash_txt->data_len); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ ++ /* 2. set DB PAD2 */ ++ ps_len = pss.emlen - 2 * hash_txt->data_len - 2; ++ ret = memset_s(ptr_db, em_txt->data_len, 0, ps_len); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ ++ /* 3. set DB data[ps_len] = 0x01 */ ++ em_txt->data[ps_len] = 0x01; ++ ++ /* 4. set DB salt */ ++ ret = memcpy_s(ptr_db + ps_len + 1, em_txt->data_len - (ps_len + 1), salt, hash_txt->data_len); ++ chk_func_return_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ ++ if ((pss.embits & 0x07) == 0) { ++ ptr_db++; ++ ps_len--; ++ } ++ ++ /* 5. mgf */ ++ ret = mgf(hash_type, ptr_seed, hash_txt->data_len, ptr_db, ps_len + hash_txt->data_len + 1); ++ chk_func_return_err(mgf, ret != TD_SUCCESS, ret); ++ ++ /* 6. Set the leftmost 8emLen - emBits bits of the leftmost octet in maskedDB to zero */ ++ em_txt->data[0] &= (0xFF >> (pss.emlen * 8 - pss.embits)); /* 8 BYTE BITS */ ++ ++ /* 7. Set data[emlen - 1] = 0xBC */ ++ em_txt->data[pss.emlen - 1] = 0xBC; ++ ++ *outlen = pss.emlen; ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 rsa_verify_pad_pkcs_pss(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, ot_data_t *em_txt) ++{ ++ td_s32 ret; ++ td_s32 sign_len; ++ rsa_pss_t pss = {0}; ++ td_u8 *ptr_db = TD_NULL; ++ td_u8 *ptr_seed = TD_NULL; ++ ot_cipher_hash_type hash_type = OT_CIPHER_HASH_TYPE_BUTT; ++ ++ ret = rsa_sign_get_pss_hash_type(scheme, &hash_type); ++ chk_func_return_err(rsa_sign_get_pss_hash_type, ret != TD_SUCCESS, ret); ++ ++ pss.modbits = rsa_get_bit_num(key->n, key->klen); ++ pss.embits = pss.modbits - 1; ++ pss.emlen = (pss.modbits + 7) / 8; /* 7, 8, round up */ ++ sign_len = pss.emlen; ++ ++ /* check emlen min length */ ++ chk_param_err_return(pss.emlen < (hash_txt->data_len * 2 + 2)); /* 2 */ ++ ++ /* 1. check em[0] */ ++ chk_param_err_return(em_txt->data[0] & ~(0xFF >> (pss.emlen * 8 - pss.embits))); /* 8 BYTE BITS */ ++ ++ /* 2. check em[emlen - 1] != 0xBC */ ++ chk_param_err_return(em_txt->data[pss.emlen - 1] != 0xBC); ++ ++ ptr_db = em_txt->data; ++ ptr_seed = em_txt->data + pss.emlen - hash_txt->data_len - 1; ++ ++ if ((pss.embits & 0x07) == 0) { ++ ptr_db++; ++ sign_len--; ++ } ++ ++ /* 3. calculate DB */ ++ ret = mgf(hash_type, ptr_seed, hash_txt->data_len, ptr_db, sign_len - hash_txt->data_len - 1); ++ chk_func_return_err(mgf, ret != TD_SUCCESS, ret); ++ ++ /* 4. Set the leftmost 8emLen - emBits bits of the leftmost octet in maskedDB to zero */ ++ em_txt->data[0] &= (0xFF >> (pss.emlen * 8 - pss.embits)); /* 8 BYTE BITS */ ++ ++ /* 5. Remove PS */ ++ while (ptr_db < ptr_seed - 1 && *ptr_db == 0) { ++ ptr_db++; ++ } ++ ++ /* 6. check DB[pslen] whether is 0x01 */ ++ chk_param_err_return(*ptr_db++ != 0x01); ++ ++ /* 7. get salt */ ++ pss.salt_txt.data_len = ptr_seed - ptr_db; ++ pss.salt_txt.data = (td_u8 *)crypto_calloc(pss.salt_txt.data_len, 1); ++ chk_func_return_err(crypto_calloc, pss.salt_txt.data == TD_NULL, OT_ERR_CIPHER_FAILED_MEM); ++ ++ ret = memcpy_s(pss.salt_txt.data, pss.salt_txt.data_len, ptr_db, pss.salt_txt.data_len); ++ chk_func_goto_err(memcpy_s, ret != EOK, OT_ERR_CIPHER_FAILED_SEC_FUNC); ++ ++ /* 8. calculate H */ ++ ret = rsa_sign_calcu_pss_h(hash_type, hash_txt, &pss); ++ chk_func_goto_err(rsa_sign_calcu_pss_h, ret != TD_SUCCESS, ret); ++ ++ /* 9. compare H */ ++ ret = memcmp(pss.hash, ptr_seed, hash_txt->data_len); ++ chk_func_goto_err(memcmp, ret != 0, OT_ERR_CIPHER_RSA_VERIFY); ++ ++goto_exit: ++ if (pss.salt_txt.data != TD_NULL) { ++ crypto_free(pss.salt_txt.data); ++ pss.salt_txt.data = TD_NULL; ++ } ++ return ret; ++} ++ +diff --git a/product/security_subsys/cipher/v3/src/drv/compat/rsa_padding.h b/product/security_subsys/cipher/v3/src/drv/compat/rsa_padding.h +new file mode 100644 +index 0000000..f087ec4 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/drv/compat/rsa_padding.h +@@ -0,0 +1,69 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef RSA_PADDING_H ++#define RSA_PADDING_H ++ ++#include "ot_type.h" ++#include "drv_cipher_ioctl.h" ++ ++typedef struct { ++ td_u8 *data; ++ td_u32 data_len; ++} ot_data_t; ++ ++#define PKCS_BLOCK_TYPE_0 0 ++#define PKCS_BLOCK_TYPE_1 1 ++#define PKCS_BLOCK_TYPE_2 2 ++ ++td_s32 rsa_encrypt_pad_none(const cryp_rsa_key *key, const ot_data_t *plain_txt, ot_data_t *em_txt); ++ ++td_s32 rsa_decrypt_pad_none(const cryp_rsa_key *key, ot_data_t *plain_txt, const ot_data_t *em_txt); ++ ++td_s32 rsa_encrypt_pad_pkcs_type(const cryp_rsa_key *key, ++ const ot_data_t *plain_txt, ot_data_t *em_txt, td_u32 block_type); ++ ++td_s32 rsa_decrypt_pad_pkcs_type(const cryp_rsa_key *key, ++ ot_data_t *plain_txt, const ot_data_t *em_txt, td_u32 block_type, td_u32 *outlen); ++ ++td_s32 rsa_encrypt_pad_pkcs_v15(const cryp_rsa_key *key, ++ const ot_data_t *plain_txt, ot_data_t *em_txt); ++ ++td_s32 rsa_decrypt_pad_pkcs_v15(const cryp_rsa_key *key, ++ ot_data_t *plain_txt, const ot_data_t *em_txt, td_u32 *outlen); ++ ++td_s32 rsa_encrypt_pad_pkcs_oaep(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *plain_txt, ot_data_t *em_txt); ++ ++td_s32 rsa_decrypt_pad_pkcs_oaep(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, ot_data_t *plain_txt, ot_data_t *em_txt, td_u32 *outlen); ++ ++td_s32 rsa_sign_pad_pkcs_v15(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, ot_data_t *em_txt); ++ ++td_s32 rsa_verify_pad_pkcs_v15(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, const ot_data_t *em_txt); ++ ++td_s32 rsa_sign_pad_pkcs_pss(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, ot_data_t *em_txt, td_u32 *outlen); ++ ++td_s32 rsa_verify_pad_pkcs_pss(const cryp_rsa_key *key, ++ ot_cipher_rsa_encrypt_scheme scheme, const ot_data_t *hash_txt, ot_data_t *em_txt); ++ ++#endif /* RSA_PADDING_H */ +diff --git a/product/security_subsys/cipher/v3/src/drv/drivers/build.mak b/product/security_subsys/cipher/v3/src/drv/drivers/build.mak +new file mode 100644 +index 0000000..20327ad +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/drv/drivers/build.mak +@@ -0,0 +1,25 @@ ++ ++CIPHER_CFLAGS += -I$(CIPHER_BASE_DIR)/drv/drivers/core/include ++CIPHER_CFLAGS += -I$(CIPHER_BASE_DIR)/drv/drivers/crypto/include ++CIPHER_CFLAGS += -I$(CIPHER_BASE_DIR)/drv/drivers/extend ++CIPHER_CFLAGS += -I$(CIPHER_BASE_DIR)/drv/drivers/extend/include ++ ++DRV_OBJS += $(CIPHER_PREFIX)/drv/drivers/core/drv_symc_v300.o \ ++ $(CIPHER_PREFIX)/drv/drivers/core/drv_hash_v300.o \ ++ $(CIPHER_PREFIX)/drv/drivers/core/drv_pke_v200.o \ ++ $(CIPHER_PREFIX)/drv/drivers/core/drv_trng_v200.o \ ++ $(CIPHER_PREFIX)/drv/drivers/core/drv_lib.o ++ ++DRV_OBJS += $(CIPHER_PREFIX)/drv/drivers/crypto/cryp_symc.o \ ++ $(CIPHER_PREFIX)/drv/drivers/crypto/cryp_hash.o \ ++ $(CIPHER_PREFIX)/drv/drivers/crypto/cryp_trng.o \ ++ $(CIPHER_PREFIX)/drv/drivers/crypto/cryp_rsa.o \ ++ $(CIPHER_PREFIX)/drv/drivers/crypto/cryp_sm2.o ++ ++DRV_OBJS += $(CIPHER_PREFIX)/drv/drivers/kapi_symc.o \ ++ $(CIPHER_PREFIX)/drv/drivers/kapi_hash.o \ ++ $(CIPHER_PREFIX)/drv/drivers/kapi_rsa.o \ ++ $(CIPHER_PREFIX)/drv/drivers/kapi_trng.o \ ++ $(CIPHER_PREFIX)/drv/drivers/kapi_sm2.o \ ++ $(CIPHER_PREFIX)/drv/drivers/kapi_dispatch.o ++ +diff --git a/product/security_subsys/cipher/v3/src/drv/drivers/core/drv_hash_v300.c b/product/security_subsys/cipher/v3/src/drv/drivers/core/drv_hash_v300.c +new file mode 100644 +index 0000000..e9f3026 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/drv/drivers/core/drv_hash_v300.c +@@ -0,0 +1,1015 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_hash_v300.h" ++#include "drv_hash.h" ++ ++#ifdef CHIP_HASH_VER_V300 ++ ++/*************************** Structure Definition ****************************/ ++/** \addtogroup hash */ ++/** @{ */ /** \n") ++#define func_exit() cipher_trace(CIPHER_LEVEL_ENTRY, "", TD_FALSE, "function exit ... ...\n\n") ++#else ++#define log_warn(fmt, ...) ++#define log_dbg(fmt, ...) ++#define func_enter() ++#define func_exit() ++#endif ++ ++/* Function trace log, strictly prohibited to expand */ ++#define print_func_errno(func, errno) log_error("Call %s return [0x%08X]\n", #func, (unsigned int)(errno)) ++#define print_errno(errno) log_error("Error Code: [0x%08X]\n", (unsigned int)(errno)) ++ ++#define chk_init_err_return(init_count) \ ++ do { \ ++ if ((init_count) == 0) { \ ++ print_errno(OT_ERR_CIPHER_NOT_INIT); \ ++ return OT_ERR_CIPHER_NOT_INIT; \ ++ } \ ++ } while (0) ++ ++#define chk_param_err_return(formula) \ ++ do { \ ++ if (formula) { \ ++ print_errno(OT_ERR_CIPHER_INVALID_PARAM); \ ++ return OT_ERR_CIPHER_INVALID_PARAM; \ ++ } \ ++ } while (0) ++ ++#define chk_ptr_err_return(ptr) \ ++ do { \ ++ if ((ptr) == TD_NULL) { \ ++ print_errno(OT_ERR_CIPHER_INVALID_POINT); \ ++ return OT_ERR_CIPHER_INVALID_POINT; \ ++ } \ ++ } while (0) ++ ++#define chk_func_err_goto(expr) \ ++ do { \ ++ ret = expr; \ ++ if (ret != TD_SUCCESS) { \ ++ print_func_errno((expr), ret); \ ++ goto exit__; \ ++ } \ ++ } while (0) ++ ++#define chk_func_return_err(func, formula, errno) \ ++ do { \ ++ if (formula) { \ ++ print_func_errno(# func, errno); \ ++ return errno; \ ++ } \ ++ } while (0) ++ ++#define chk_func_goto_err(func, formula, errno) \ ++ do { \ ++ if (formula) { \ ++ print_func_errno(# func, errno); \ ++ ret = errno; \ ++ goto goto_exit; \ ++ } \ ++ } while (0) ++ ++#define chk_handle_modid(handle, modid) \ ++ do { \ ++ if (td_handle_get_modid(handle) != (modid)) { \ ++ log_error("invalid handle 0x%x\n", handle); \ ++ return OT_ERR_CIPHER_INVALID_HANDLE; \ ++ } \ ++ } while (0) ++ ++#define chk_handle_private_data(handle, data) \ ++ do { \ ++ if (td_handle_get_private_data(handle) != (data)) { \ ++ log_error("invalid handle 0x%x\n", handle); \ ++ return OT_ERR_CIPHER_INVALID_HANDLE; \ ++ } \ ++ } while (0) ++ ++#define chk_handle_chnid(handle, threshold) \ ++ do { \ ++ if (td_handle_get_chnid(handle) >= (threshold)) { \ ++ log_error("invalid handle 0x%x\n", handle); \ ++ return OT_ERR_CIPHER_INVALID_HANDLE; \ ++ } \ ++ } while (0) ++ ++#endif /* DRV_CIPHER_DEBUG_H */ +diff --git a/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_define.h b/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_define.h +new file mode 100644 +index 0000000..c9090a9 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_define.h +@@ -0,0 +1,98 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_CIPHER_DEFINE_H ++#define DRV_CIPHER_DEFINE_H ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C"{ ++#endif ++#endif /* __cplusplus */ ++ ++/* word index number. */ ++#define WORD_IDX_0 0 ++#define WORD_IDX_1 1 ++#define WORD_IDX_2 2 ++#define WORD_IDX_3 3 ++#define WORD_IDX_4 4 ++#define WORD_IDX_5 5 ++#define WORD_IDX_6 6 ++#define WORD_IDX_7 7 ++ ++/* Boundary value 1. */ ++#define BOUND_VAL_1 1 ++ ++/* multiple value */ ++#define MUL_VAL_1 1 ++#define MUL_VAL_2 2 ++#define MUL_VAL_3 3 ++#define MUL_VAL_4 4 ++ ++#define SHIFT_1BITS 1 ++#define SHIFT_2BITS 2 ++#define SHIFT_3BITS 3 ++#define SHIFT_4BITS 4 ++#define SHIFT_5BITS 5 ++#define SHIFT_6BITS 6 ++#define SHIFT_8BITS 8 ++#define SHIFT_9BITS 9 ++#define SHIFT_13BITS 13 ++#define SHIFT_15BITS 15 ++#define SHIFT_16BITS 16 ++#define SHIFT_17BITS 17 ++#define SHIFT_21BITS 21 ++#define SHIFT_23BITS 23 ++#define SHIFT_24BITS 24 ++#define SHIFT_29BITS 29 ++#define SHIFT_32BITS 32 ++ ++#define MAX_LOW_2BITS 3 ++#define MAX_LOW_3BITS 7 ++#define MAX_LOW_4BITS 0xF ++#define MAX_LOW_8BITS 0xFF ++ ++#define WORD_WIDTH 4 ++ ++/* width of word. */ ++#define WORD_BIT_WIDTH 32 ++#define U32_MAX_SIZE 0xFFFFFFFF ++ ++/* width of double word. */ ++#define DOUBLE_WORD_WIDTH 8 ++#define DOUBLE_WORD 2 ++ ++#define BYTE_BITS 8 ++/* The offset in one byte. */ ++#define BYTE_2BIT 2 ++#define BYTE_4BIT 4 ++#define BYTE_6BIT 6 ++ ++#define CRYPTO_NUM_1 1 ++#define CRYPTO_NUM_2 2 ++#define CRYPTO_NUM_3 3 ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* __cplusplus */ ++ ++#endif /* DRV_CIPHER_DEFINE_H */ ++ +diff --git a/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_ioctl.h b/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_ioctl.h +new file mode 100644 +index 0000000..aae7de1 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_ioctl.h +@@ -0,0 +1,440 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_CIPHER_IOCTL_H ++#define DRV_CIPHER_IOCTL_H ++ ++#include ++#include "ot_type.h" ++#include "ot_common.h" ++#include "ot_common_cipher.h" ++#include "mkp_ioctl.h" ++#include "drv_cipher_define.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif /* __cplusplus */ ++ ++#ifndef SM2_LEN_IN_WROD ++#define SM2_LEN_IN_WROD 8 ++#define SM2_LEN_IN_BYTE 32 ++#endif ++ ++#define crypto_unused(x) ((x) = (x)) ++ ++#define crypto_max(a, b) ((a) > (b) ? (a) : (b)) ++ ++#define crypto_min(a, b) ((a) < (b) ? (a) : (b)) ++ ++#define CRYPTO_MAGIC_NUM 0xc0704d19 ++ ++/* AES KEY size */ ++#define SYMC_KEY_SIZE 32 ++ ++/* SM1 SK size */ ++#define SYMC_SM1_SK_SIZE 16 ++ ++/* AES IV size */ ++#define AES_IV_SIZE 16 ++ ++/* AES BLOCK size */ ++#define AES_BLOCK_SIZE 16 ++ ++/* DES IV size */ ++#define DES_IV_SIZE 8 ++ ++/* aead tag length */ ++#define AEAD_TAG_SIZE 16 ++#define AEAD_TAG_SIZE_IN_WORD 4 ++ ++/* bits in a byte */ ++#define BITS_IN_BYTE 8 ++ ++/* hash result max size */ ++#define HASH_RESULT_MAX_SIZE 64 ++ ++/* hash result max size in word, used for hash state value address. */ ++#define HASH_RESULT_MAX_SIZE_IN_WORD 16 ++ ++/*! capacity upport */ ++#define CRYPTO_CAPACITY_SUPPORT 1 ++#define CRYPTO_CAPACITY_NONSUPPORT 0 ++ ++/* max length of SM2 ID */ ++#define SM2_ID_MAX_LEN 0x1FFF ++ ++#define SM2_ENCRYPT_PAD_LEN (SM2_LEN_IN_BYTE * 3) ++ ++/* Define the time out */ ++#define CRYPTO_TIME_OUT 6000 ++#define MS_TO_US 1000 ++ ++/* result size */ ++#define SHA1_RESULT_SIZE 20 ++#define SHA224_RESULT_SIZE 28 ++#define SHA256_RESULT_SIZE 32 ++#define SHA384_RESULT_SIZE 48 ++#define SHA512_RESULT_SIZE 64 ++#define SM3_RESULT_SIZE 32 ++/* rsa key length */ ++#define RSA_MIN_KEY_LEN 256 ++#define RSA_MAX_KEY_LEN 512 ++#define RSA_KEY_BITWIDTH_1024 128 ++#define RSA_KEY_BITWIDTH_2048 256 ++#define RSA_KEY_BITWIDTH_3072 384 ++#define RSA_KEY_BITWIDTH_4096 512 ++#define RSA_KEY_EXPONENT_VALUE1 0X3 ++#define RSA_KEY_EXPONENT_VALUE2 0X10001 ++ ++/* the source of hash message */ ++typedef enum { ++ HASH_CHUNCK_SRC_LOCAL, /* Local buffer, e.g. Kernel */ ++ HASH_CHUNCK_SRC_USER, /* User buffer, use copy_from_user to read data */ ++} hash_chunk_src; ++ ++/* union of compat addr */ ++typedef union { ++ td_void *p; /* virtual address */ ++ const td_void *cp; /* const virtual address */ ++ td_phys_addr_t phy; /* physical address */ ++ unsigned int word[MUL_VAL_2]; /* double word of address */ ++} compat_addr; ++ ++#define addr_h32(addr) (addr).word[1] /* High 32 bit of td_u64 */ ++#define addr_l32(addr) (addr).word[0] /* Low 32 bit of td_u64 */ ++#define addr_u64(addr) (addr).phy /* 64 bit of td_u64 */ ++#define addr_via(addr) (addr).p /* buffer point */ ++#define addr_via_const(addr) (addr).cp /* const buffer point */ ++ ++/* struct of Symmetric cipher create */ ++typedef struct { ++ td_u32 id; /* to store the id of soft channel */ ++ ot_cipher_type type; /* symc channel type */ ++} symc_create_t; ++ ++/* struct of Symmetric cipher destroy */ ++typedef struct { ++ td_u32 id; /* id of soft channel */ ++ td_u32 reserve; /* reserve to make align at 64bit */ ++} symc_destroy_t; ++ ++/* struct of Symmetric cipher configure information */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ ot_cipher_alg alg; /* Symmetric cipher algorithm */ ++ ot_cipher_work_mode mode; /* Symmetric cipher algorithm */ ++ ot_cipher_bit_width width; /* Symmetric cipher bit width */ ++ ot_cipher_key_len klen; /* Symmetric cipher key length */ ++ td_u8 iv[AES_IV_SIZE]; /* IV buffer */ ++ td_u32 ivlen; /* IV length */ ++ td_u32 iv_usage; /* Usage of IV */ ++ td_u32 reserve; /* reserve to make align at 64bit */ ++ compat_addr aad; /* Associated Data */ ++ td_u32 alen; /* Associated Data Length */ ++ td_u32 tlen; /* Tag length */ ++} symc_cfg_t; ++ ++typedef enum { ++ SYMC_OPERATION_ENCRYPT = 0, ++ SYMC_OPERATION_DECRYPT = 1, ++ SYMC_OPERATION_ENCRYPT_VIA = 0x10, ++ SYMC_OPERATION_DECRYPT_VIA = 0x11, ++} symc_operation_type; ++ ++/* struct of Symmetric cipher encrypt/decrypt */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ td_u32 length; /* Length of the encrypted data */ ++ td_u32 operation; /* operation type */ ++ td_u32 last; /* last or not */ ++ compat_addr input; /* Physical address of the input data */ ++ compat_addr output; /* Physical address of the output data */ ++} symc_encrypt_t; ++ ++/* struct of Symmetric cipher multiple encrypt/decrypt */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ compat_addr pack; /* Buffer of package information */ ++ td_u32 pack_num; /* Number of package information */ ++ td_u32 operation; /* Decrypt or encrypt */ ++} symc_encrypt_multi_t; ++ ++/* struct of Symmetric cipher get tag */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ td_u32 tag[AEAD_TAG_SIZE_IN_WORD]; /* Buffer of tag */ ++ td_u32 taglen; /* Length of tag */ ++} aead_tag_t; ++ ++/* struct of Symmetric cipher get ctrl */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ ot_cipher_ctrl ctrl; /* control information */ ++} symc_get_cfg_t; ++ ++/* struct of Hash start */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ ot_cipher_hash_type type; /* HASH type */ ++ compat_addr key; /* HMAC key */ ++ td_u32 keylen; /* HMAC key */ ++ td_u32 reserve; /* reserve for align at 64bit */ ++} hash_start_t; ++ ++/* struct of Hash update */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ td_u32 length; /* Length of the message */ ++ compat_addr input; /* Message data buffer */ ++ hash_chunk_src src; /* source of hash message */ ++ td_u32 reserve; /* reserve for align at 64bit */ ++} hash_update_t; ++ ++/* struct of Hash update */ ++typedef struct { ++ td_u32 id; /* Id of soft channel */ ++ td_u32 hash[HASH_RESULT_MAX_SIZE_IN_WORD]; /* buffer holding the hash data */ ++ td_u32 hashlen; /* length of the hash data */ ++ td_u32 reserve; /* reserve for align at 64bit */ ++} hash_finish_t; ++ ++/* struct of rsa encrypt/decrypt */ ++typedef struct { ++ td_u32 scheme; /* RSA scheme */ ++ td_u16 public; /* Type of key, true-public or false-private */ ++ td_u16 reserve; ++ td_u32 klen; /* length of rsa key */ ++ td_u32 e; /* The public exponent */ ++ compat_addr d; /* The private exponent */ ++ compat_addr n; /* The modulus */ ++ compat_addr p; /* The p factor of N */ ++ compat_addr q; /* The q factor of N */ ++ compat_addr qp; /* The 1/q mod p CRT param */ ++ compat_addr dp; /* The d mod (p - 1) CRT param */ ++ compat_addr dq; /* The d mod (q - 1) CRT param */ ++ compat_addr in; /* input data to be encryption */ ++ compat_addr out; /* output data of encryption */ ++ td_u32 inlen; /* length of input data to be encryption */ ++ td_u32 outlen; /* length of output data */ ++} rsa_info_t; ++ ++/* RSA PKCS style key */ ++typedef struct { ++ td_u8 public; /* Type of key, true-public or false-private */ ++ td_u8 reserve; ++ td_u16 klen; /* The key length */ ++ td_u32 e; /* The public exponent */ ++ td_u8 *d; /* The private exponent */ ++ td_u8 *n; /* The modulus */ ++ td_u8 *p; /* The p factor of n */ ++ td_u8 *q; /* The q factor of n */ ++ td_u8 *qp; /* The 1/q mod p CRT param */ ++ td_u8 *dp; /* The d mod (p - 1) CRT param */ ++ td_u8 *dq; /* The d mod (q - 1) CRT param */ ++ td_u32 bufsize; /* The buffer size alloc for n */ ++} cryp_rsa_key; ++ ++/* struct of rsa crypt input and output data. */ ++typedef struct { ++ td_u8 *in; ++ td_u32 in_len; ++ td_u8 *out; ++ td_u32 out_len; ++} cryp_rsa_crypto_data; ++ ++/* Rsa encrypt and decrypt scheme. */ ++typedef enum { ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_NO_PADDING = 0x00, /* without padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_0, /* PKCS#1 block type 0 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_1, /* PKCS#1 block type 1padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_BLOCK_TYPE_2, /* PKCS#1 block type 2 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA1, /* PKCS#1 RSAES-OAEP-SHA1 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA224, /* PKCS#1 RSAES-OAEP-SHA224 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA256, /* PKCS#1 RSAES-OAEP-SHA256 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA384, /* PKCS#1 RSAES-OAEP-SHA384 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_OAEP_SHA512, /* PKCS#1 RSAES-OAEP-SHA512 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_RSAES_PKCS1_V1_5, /* PKCS#1 RSAES-PKCS1_V1_5 padding. */ ++ OT_CIPHER_RSA_ENCRYPT_SCHEME_BUTT, ++} ot_cipher_rsa_encrypt_scheme; ++ ++typedef enum { ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA1 = 0x100, /* PKCS#1 RSASSA_PKCS1_V15_SHA1 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA224, /* PKCS#1 RSASSA_PKCS1_V15_SHA224 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA256, /* PKCS#1 RSASSA_PKCS1_V15_SHA256 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA384, /* PKCS#1 RSASSA_PKCS1_V15_SHA384 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_V15_SHA512, /* PKCS#1 RSASSA_PKCS1_V15_SHA512 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA1, /* PKCS#1 RSASSA_PKCS1_PSS_SHA1 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA224, /* PKCS#1 RSASSA_PKCS1_PSS_SHA224 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA256, /* PKCS#1 RSASSA_PKCS1_PSS_SHA256 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA384, /* PKCS#1 RSASSA_PKCS1_PSS_SHA1 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_RSASSA_PKCS1_PSS_SHA512, /* PKCS#1 RSASSA_PKCS1_PSS_SHA256 signature. */ ++ OT_CIPHER_RSA_SIGN_SCHEME_BUTT, ++} ot_cipher_rsa_sign_scheme; ++ ++/* struct of rsa sign or verify data. */ ++typedef struct { ++ td_u8 *in; ++ td_u32 in_len; ++ td_u8 *out; ++ td_u32 out_len; ++} cryp_rsa_sign_data; ++ ++/* struct of trng */ ++typedef struct { ++ td_u32 randnum; /* randnum rand number */ ++ td_u32 timeout; /* time out */ ++} trng_t; ++ ++/*! \struct of Symmetric cipher get key slot handle */ ++typedef struct { ++ td_u32 cipher; /*!< cipher channel */ ++ td_u32 keyslot; /*!< keyslot channel */ ++} symc_keyslot_t; ++ ++typedef struct { ++ td_u8 *x; ++ td_u8 *y; ++ td_u32 len; ++} ecc_point_t; ++ ++/*! \struct of ecc */ ++typedef struct { ++ /*!< Finite field: equal to p in case of prime field curves or equal to 2^n in case of binary field curves. */ ++ td_u8 *p; ++ td_u8 *a; /*!< Curve parameter a (q-3 in Suite B). */ ++ td_u8 *b; /*!< Curve parameter b */ ++ td_u8 *n; /*!< Prime which is the order of G point. */ ++ /*!< Cofactor, which is the order of the elliptic curve divided by the order of the point G. ++ For the Suite B curves, h = 1. ++ */ ++ ecc_point_t g; /*!< X coordinates of G which is a base point on the curve. */ ++ td_u32 h; ++ td_u32 ksize; /*!< Key size in bytes. It corresponds to the size in bytes of the prime ptd_u8n */ ++} ecc_param_t; ++ ++/*! \struct of sm2 sign */ ++typedef struct { ++ td_u32 d[SM2_LEN_IN_WROD]; /* sm2 private key */ ++ td_u32 px[SM2_LEN_IN_WROD]; /* sm2 x public key */ ++ td_u32 py[SM2_LEN_IN_WROD]; /* sm2 y public key */ ++ const td_u8 *id; /* sm2 user id */ ++ td_u32 id_len; /* length of sm2 user id */ ++ const td_u8 *msg; /* message to be sign */ ++ td_u32 msglen; /* length of message to be sign */ ++ td_u32 r[SM2_LEN_IN_WROD]; /* sm2 sign result of r */ ++ td_u32 s[SM2_LEN_IN_WROD]; /* sm2 sign result of s */ ++} sm2_sign_t; ++ ++/*! \struct of sm2 verify */ ++typedef struct { ++ td_u32 px[SM2_LEN_IN_WROD]; /* sm2 x public key */ ++ td_u32 py[SM2_LEN_IN_WROD]; /* sm2 y public key */ ++ const td_u8 *id; /* sm2 user id */ ++ td_u32 id_len; /* length of sm2 user id */ ++ const td_u8 *msg; /* message to be sign */ ++ td_u32 msglen; /* length of message to be sign */ ++ td_u32 r[SM2_LEN_IN_WROD]; /* sm2 sign result of r */ ++ td_u32 s[SM2_LEN_IN_WROD]; /* sm2 sign result of s */ ++} sm2_verify_t; ++ ++/*! \struct of sm2 encrypt */ ++typedef struct { ++ ot_cipher_sm2_public_key pub_key; ++ const td_u8 *plain_data; ++ td_u32 plain_len; ++ td_u8 *cipher_data; ++ td_u32 cipher_len; ++} sm2_encrypt_t; ++ ++/*! \struct of sm2 decrypt */ ++typedef struct { ++ ot_cipher_sm2_private_key pri_key; ++ const td_u8 *cipher_data; ++ td_u32 cipher_len; ++ td_u8 *plain_data; ++ td_u32 plain_len; ++} sm2_decrypt_t; ++ ++typedef struct { ++ ot_keyslot_attr attr; ++ td_handle keyslot; ++} keyslot_create_t; ++ ++typedef struct { ++ td_handle keyslot; ++} keyslot_destroy_t; ++ ++typedef enum { ++ CIPHER_IOC_NR_SYMC_CREATE, ++ CIPHER_IOC_NR_SYMC_DESTROY, ++ CIPHER_IOC_NR_SYMC_SET_CFG, ++ CIPHER_IOC_NR_SYMC_GET_CFG, ++ CIPHER_IOC_NR_SYMC_CRYPTO, ++ CIPHER_IOC_NR_SYMC_CRYPTO_MULTI, ++ CIPHER_IOC_NR_SYMC_GET_TAG, ++ CIPHER_IOC_NR_HASH_START, ++ CIPHER_IOC_NR_HASH_UPDATE, ++ CIPHER_IOC_NR_HASH_FINISH, ++ CIPHER_IOC_NR_TRNG, ++ CIPHER_IOC_NR_RSA_ENCRYPT, ++ CIPHER_IOC_NR_RSA_DECRYPT, ++ CIPHER_IOC_NR_RSA_SIGN, ++ CIPHER_IOC_NR_RSA_VERIFY, ++ CIPHER_IOC_NR_SM2_ENCRYPT, ++ CIPHER_IOC_NR_SM2_DECRYPT, ++ CIPHER_IOC_NR_SM2_SIGN, ++ CIPHER_IOC_NR_SM2_VERIFY, ++ CIPHER_IOC_NR_KEYSLOT_CREATE, ++ CIPHER_IOC_NR_KEYSLOT_DESTROY, ++ CIPHER_IOC_NR_SYMC_ATTACH, ++ CIPHER_IOC_NR_SYMC_DETACH, ++ CIPHER_IOC_NR_BUTT, ++} cipher_ioc_nr; ++ ++/* Ioctl definitions */ ++#define CRYPTO_CMD_SYMC_CREATE _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_CREATE, symc_create_t) ++#define CRYPTO_CMD_SYMC_DESTROY _IOW (IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_DESTROY, symc_destroy_t) ++#define CRYPTO_CMD_SYMC_SET_CFG _IOW (IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_SET_CFG, symc_cfg_t) ++#define CRYPTO_CMD_SYMC_GET_CFG _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_GET_CFG, symc_get_cfg_t) ++#define CRYPTO_CMD_SYMC_ENCRYPT _IOW (IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_CRYPTO, symc_encrypt_t) ++#define CRYPTO_CMD_SYMC_ENCRYPT_MULTI _IOW (IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_CRYPTO_MULTI, symc_encrypt_multi_t) ++#define CRYPTO_CMD_SYMC_GET_TAG _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_GET_TAG, aead_tag_t) ++#define CRYPTO_CMD_HASH_START _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_HASH_START, hash_start_t) ++#define CRYPTO_CMD_HASH_UPDATE _IOW (IOC_TYPE_CIPHER, CIPHER_IOC_NR_HASH_UPDATE, hash_update_t) ++#define CRYPTO_CMD_HASH_FINISH _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_HASH_FINISH, hash_finish_t) ++#define CRYPTO_CMD_TRNG _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_TRNG, trng_t) ++#define CRYPTO_CMD_RSA_ENCRYPT _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_RSA_ENCRYPT, rsa_info_t) ++#define CRYPTO_CMD_RSA_DECRYPT _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_RSA_DECRYPT, rsa_info_t) ++#define CRYPTO_CMD_RSA_SIGN _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_RSA_SIGN, rsa_info_t) ++#define CRYPTO_CMD_RSA_VERIFY _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_RSA_VERIFY, rsa_info_t) ++#define CRYPTO_CMD_SM2_ENCRYPT _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SM2_ENCRYPT, sm2_encrypt_t) ++#define CRYPTO_CMD_SM2_DECRYPT _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SM2_DECRYPT, sm2_decrypt_t) ++#define CRYPTO_CMD_SM2_SIGN _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SM2_SIGN, sm2_sign_t) ++#define CRYPTO_CMD_SM2_VERIFY _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SM2_VERIFY, sm2_verify_t) ++#define CRYPTO_CMD_KEYSLOT_CREATE _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_KEYSLOT_CREATE, keyslot_create_t) ++#define CRYPTO_CMD_KEYSLOT_DESTROY _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_KEYSLOT_DESTROY, keyslot_destroy_t) ++#define CRYPTO_CMD_SYMC_ATTACH _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_ATTACH, symc_keyslot_t) ++#define CRYPTO_CMD_SYMC_DETACH _IOWR(IOC_TYPE_CIPHER, CIPHER_IOC_NR_SYMC_DETACH, symc_keyslot_t) ++#define CRYPTO_CMD_COUNT CIPHER_IOC_NR_BUTT ++ ++#ifdef __cplusplus ++} ++#endif /* __cplusplus */ ++ ++#endif /* DRV_CIPHER_IOCTL_H */ +diff --git a/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_kapi.h b/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_kapi.h +new file mode 100644 +index 0000000..ae7a1d1 +--- /dev/null ++++ b/product/security_subsys/cipher/v3/src/drv/include/drv_cipher_kapi.h +@@ -0,0 +1,353 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_CIPHER_KAPI_H ++#define DRV_CIPHER_KAPI_H ++ ++#include "drv_cipher_ioctl.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif /* __cplusplus */ ++ ++/******************************* API Code *****************************/ ++/* ++ * brief Kapi Init. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_symc_init(td_void); ++ ++/* ++ * brief Kapi Deinit. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_symc_deinit(td_void); ++ ++/* ++ * brief Kapi release. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_symc_release(td_void); ++ ++/* ++ * brief Create symc handle. ++ * param[in] id The channel number. ++ * param[in] uuid The user identification. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_symc_create(td_u32 *id, ot_cipher_type type); ++ ++/* ++ * brief Destroy symc handle. ++ * param[in] id The channel number. ++ * param[in] uuid The user identification. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_symc_destroy(td_u32 id); ++ ++/* ++ * brief set work params. ++ * param[in] cfg config information. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_symc_cfg(const symc_cfg_t *cfg); ++ ++/* ++ * brief get work params. ++ * param[in] id The channel number. ++ * param[out] ctrl information. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_symc_get_cfg(td_u32 id, ot_cipher_ctrl *ctrl); ++ ++/* ++ * brief SYMC buffer encryption/decryption. ++ * ++ * Note: Due to the nature of aes you should use the same key schedule for ++ * both encryption and decryption. ++ * ++ * param[in] crypt The symc info data. ++ * ++ * return 0 if successful ++ */ ++td_s32 kapi_symc_crypto(symc_encrypt_t *crypt); ++ ++/* ++ * brief SYMC via buffer encryption/decryption. ++ * ++ * Note: Due to the nature of aes you should use the same key schedule for ++ * both encryption and decryption. ++ * ++ * param[in] crypt The symc info data. ++ * param[in] is_from_user input and output virtual address is from user or kapi. ++ * ++ * return 0 if successful ++ */ ++td_s32 kapi_symc_crypto_via(symc_encrypt_t *crypt, td_u32 is_from_user); ++ ++/* ++ * brief SYMC multiple buffer encryption/decryption. ++ * ++ * Note: Due to the nature of aes you should use the same key schedule for ++ * both encryption and decryption. ++ * ++ * param[in] id The channel number. ++ * \param pkg Buffer of package information ++ * \param pkg_num Number of package information ++ * param operation decrypt or encrypt ++ * param last last or not ++ * ++ * return 0 if successful ++ */ ++td_s32 kapi_symc_crypto_multi(td_u32 id, const ot_cipher_data *pkg, td_u32 pkg_num, td_u32 operation, td_u32 last); ++ ++/* ++ * brief SYMC multiple buffer encryption/decryption. ++ * param[in] id The channel number. ++ * param[in] tag tag data of CCM/GCM ++ * ++ * return 0 if successful ++ */ ++td_s32 kapi_aead_get_tag(td_u32 id, td_u32 tag[AEAD_TAG_SIZE_IN_WORD], td_u32 *taglen); ++ ++/* ++ * brief Kapi Init. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_hash_init(td_void); ++ ++/* ++ * brief Kapi Deinit. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_hash_deinit(td_void); ++ ++/* ++ * brief HASH context setup. ++ * ++ * ++ * param[out] id The channel number. ++ * param[in] type Hash type ++ * param[in] key hmac key ++ * param[in] keylen hmac key length ++ * ++ * return 0 if successful ++ */ ++td_s32 kapi_hash_start(td_u32 *id, ot_cipher_hash_type type, const td_u8 *key, td_u32 keylen); ++ ++/* ++ * brief HASH process buffer. ++ * ++ * param[in] id The channel number. ++ * param[in] input buffer holding the input data ++ * param[in] length length of the input data ++ * param[in] src source of hash message ++ * ++ * return 0 if successful ++ */ ++td_s32 kapi_hash_update(td_u32 id, const td_u8 *input, td_u32 length, hash_chunk_src src); ++ ++/* ++ * brief HASH final digest. ++ * ++ * param[in] id The channel number. ++ * param[out] hash buffer holding the hash data_type_t ++ * param[in] hash_buf_len buffer length of holding the hash data ++ * param[out] hashlen length of the hash data ++ * ++ * return 0 if successful ++ */ ++td_s32 kapi_hash_finish(td_u32 id, td_u8 *hash, td_u32 hash_buf_len, td_u32 *hashlen); ++ ++/* ++ * brief hash release. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_hash_release(td_void); ++ ++/* ++ * brief Kapi Init. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_rsa_init(td_void); ++ ++/* ++ * brief Kapi Deinitialize. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_rsa_deinit(td_void); ++ ++/* ++ * brief RSA encryption a plaintext with a RSA private key. ++ * ++ * param[in] key: rsa key struct. ++ * param[in/out] rsa: rsa encrypt/decrypt data. ++ * retval ::TD_SUCCESS Call this API successful ++ * retval ::TD_FAILURE Call this API fails. ++ */ ++td_s32 kapi_rsa_encrypt(ot_cipher_rsa_encrypt_scheme scheme, ++ const cryp_rsa_key *key, cryp_rsa_crypto_data *rsa_data); ++ ++/* ++ * brief RSA decryption a ciphertext with a RSA public key. ++ * ++ * param[in] key: rsa key struct. ++ * param[in/out] rsa: rsa encrypt/decrypt data. ++ * retval ::TD_SUCCESS Call this API successful ++ * retval ::TD_FAILURE Call this API fails. ++ */ ++td_s32 kapi_rsa_decrypt(ot_cipher_rsa_encrypt_scheme scheme, ++ const cryp_rsa_key *key, cryp_rsa_crypto_data *rsa_data); ++ ++/* ++ * brief RSA signature a context with appendix, where a signer's RSA private key is used. ++ * ++ * param[in] key: rsa key struct. ++ * param[in/out] rsa: rsa signature data. ++ * retval ::TD_SUCCESS Call this API successful ++ * retval ::TD_FAILURE Call this API fails. ++ */ ++td_s32 kapi_rsa_sign_hash(ot_cipher_rsa_sign_scheme scheme, ++ const cryp_rsa_key *key, cryp_rsa_sign_data *rsa_data); ++ ++/* ++ * brief RSA verify a ciphertext with a RSA public key. ++ * ++ * param[in] key: rsa key struct. ++ * param[in/out] rsa: rsa verify data. ++ * retval ::TD_SUCCESS Call this API successful ++ * retval ::TD_FAILURE Call this API fails. ++ */ ++td_s32 kapi_rsa_verify_hash(ot_cipher_rsa_sign_scheme scheme, ++ const cryp_rsa_key *key, const cryp_rsa_sign_data *rsa_data); ++ ++/** ++\brief Kapi Init. ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_trng_init(td_void); ++ ++/** ++\brief Kapi Deinitialize. ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_trng_deinit(td_void); ++ ++/* ++ * brief get rand number. ++ * param[out] randnum rand number. ++ * param[in] timeout time out. ++ * retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++ */ ++td_s32 kapi_trng_get_random(td_u32 *randnum, td_u32 timeout); ++ ++/** ++\brief get rand bytes. ++\param[out] randnum rand bytes. ++\param[in] size size of rand byte. ++\param[in] timeout time out. ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_trng_get_rand_byte(td_u8 *randbyte, td_u32 size, td_u32 timeout); ++ ++/** ++\brief Kapi Init. ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_sm2_init(td_void); ++ ++/** ++\brief Kapi Deinitialize. ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_sm2_deinit(td_void); ++ ++#ifdef CHIP_SM2_SUPPORT ++/** ++\brief SM2 signature a context with appendix, where a signers SM2 private key is used. ++\param d[in] sm2 private key ++\param px[in] sm2 x public key ++\param py[in] sm2 y public key ++\param id[in] sm2 user id ++\param idlen[in] length of sm2 user id ++\param msg[in] message to be sign ++\param msglen[in] length of message to be sign ++\param src[in] source of hash message ++\param r[out] sm2 sign result of r ++\param s[out] sm2 sign result of s ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_sm2_sign(sm2_sign_t *sign, const td_u8 *id, td_u16 idlen, hash_chunk_src src); ++ ++/** ++\brief SM2 signature verification a context with appendix, where a signers SM2 public key is used. ++\param px[in] sm2 x public key ++\param py[in] sm2 y public key ++\param id[in] sm2 user id ++\param idlen[in] length of sm2 user id ++\param msg[in] message to be sign ++\param msglen[in] length of message to be sign ++\param src[in] source of hash message ++\param r[in] sm2 sign result of r ++\param s[in] sm2 sign result of s ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_sm2_verify(sm2_verify_t *verify, const td_u8 *id, td_u16 idlen, hash_chunk_src src); ++ ++/** ++\brief SM2 encryption a plaintext with a RSA public key. ++\param px[in] sm2 x public key ++\param py[in] sm2 y public key ++\param msg[in] message to be encryption ++\param msglen[in] length of message to be encryption ++\param enc[out] encryption message ++\param enclen[out] length of encryption message ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_sm2_encrypt(const ot_cipher_sm2_public_key *sm2_key, ++ const ot_cipher_common_data *plain_txt, ot_cipher_common_data *cipher_txt); ++ ++/** ++\brief SM2 decryption a plaintext with a RSA public key. ++\param d[in] sm2 private key ++\param enc[out] message to be decryption ++\param enclen[out] length of message to be decryption ++\param msg[in] decryption message ++\param msglen[in] length of decryption message ++\retval On success, TD_SUCCESS is returned. On error, TD_FAILURE is returned. ++*/ ++td_s32 kapi_sm2_decrypt(const ot_cipher_sm2_private_key *sm2_key, ++ const ot_cipher_common_data *cipher_txt, ot_cipher_common_data *plain_txt); ++#endif ++ ++td_s32 kapi_keyslot_create(const ot_keyslot_attr *attr, td_handle *keyslot); ++ ++td_s32 kapi_keyslot_destroy(td_handle keyslot); ++ ++td_s32 kapi_symc_attach(td_handle cipher, td_handle keyslot); ++ ++td_s32 kapi_symc_detach(td_handle cipher, td_handle keyslot); ++ ++/** |<----- 8bit ----->|<---------- 16bit ---------->| ++ * |-------------------------------------------------------------------| ++ * | ot_mod_id | mod defined data | chan_id | ++ * |-------------------------------------------------------------------| ++ * mod defined data: private data define by each module(for example: sub-mod id), usually, set to 0. ++ */ ++#define td_handle_init(mod, private_data, chnid) \ ++ (td_handle)((((mod) & 0xff) << 24) | ((((private_data) & 0xff) << 16)) | (((chnid) & 0xffff))) ++ ++#define td_handle_get_modid(handle) (((handle) >> 24) & 0xff) ++ ++#define td_handle_get_private_data(handle) (((handle) >> 16) & 0xff) ++ ++#define td_handle_get_chnid(handle) (((handle)) & 0xffff) ++ ++#endif +diff --git a/product/security_subsys/ext_inc/dev_ext.h b/product/security_subsys/ext_inc/dev_ext.h +new file mode 100644 +index 0000000..f28c8b2 +--- /dev/null ++++ b/product/security_subsys/ext_inc/dev_ext.h +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef _DEV_EXT_H_ ++#define _DEV_EXT_H_ ++ ++#include "ot_type.h" ++ ++#define UMAP_CIPHER_MINOR_BASE 53 ++#define UMAP_KLAD_MINOR_BASE 54 ++#define UMAP_OTP_MINOR_BASE 56 ++ ++#define UMAP_DEVNAME_CIPHER_BASE OT_MPP_MOD_CIPHER ++#define UMAP_DEVNAME_KLAD_BASE OT_MPP_MOD_KLAD ++#define UMAP_DEVNAME_OTP_BASE OT_MPP_MOD_OTP ++ ++#endif /* _DEV_EXT_H_ */ +diff --git a/product/security_subsys/ext_inc/mkp_ioctl.h b/product/security_subsys/ext_inc/mkp_ioctl.h +new file mode 100644 +index 0000000..c7ffb80 +--- /dev/null ++++ b/product/security_subsys/ext_inc/mkp_ioctl.h +@@ -0,0 +1,28 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __MKP_IOCTL_H__ ++#define __MKP_IOCTL_H__ ++ ++#define IOC_TYPE_CIPHER 'l' ++#define IOC_TYPE_KLAD 'm' ++#define IOC_TYPE_OTP 'n' ++ ++#endif /* __MKP_IOCTL_H__ */ ++ +diff --git a/product/security_subsys/ext_inc/ot_common.h b/product/security_subsys/ext_inc/ot_common.h +new file mode 100644 +index 0000000..8e4d047 +--- /dev/null ++++ b/product/security_subsys/ext_inc/ot_common.h +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __OT_COMMON_H__ ++#define __OT_COMMON_H__ ++ ++#include "ot_type.h" ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C" { ++#endif ++#endif /* end of #ifdef __cplusplus */ ++ ++#define ot_unused(x) ((td_void)(x)) ++ ++#define OT_MPP_MOD_CIPHER "cipher" ++#define OT_MPP_MOD_KLAD "klad" ++#define OT_MPP_MOD_OTP "otp" ++ ++#define OT_INVALID_HANDLE (-1) ++ ++typedef enum { ++ OT_ID_CIPHER = 71, ++ OT_ID_KLAD = 72, ++ OT_ID_KEYSLOT = 73, ++ OT_ID_OTP = 74, ++ OT_ID_BUTT, ++} ot_mod_id; ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* end of #ifdef __cplusplus */ ++ ++#endif /* __OT_COMMON_H__ */ +diff --git a/product/security_subsys/ext_inc/ot_debug.h b/product/security_subsys/ext_inc/ot_debug.h +new file mode 100644 +index 0000000..175e2b5 +--- /dev/null ++++ b/product/security_subsys/ext_inc/ot_debug.h +@@ -0,0 +1,55 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __OT_DEBUG_H__ ++#define __OT_DEBUG_H__ ++ ++#ifndef __KERNEL__ ++#include ++#include ++#else ++#include ++#endif ++ ++#include "ot_type.h" ++#include "ot_common.h" ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C" { ++#endif ++#endif /* __cplusplus */ ++ ++#ifndef __KERNEL__ ++ ++#define OT_PRINT printf ++ ++#else ++ ++#define OT_PRINT printk ++ ++#endif /* end of __KERNEL__ */ ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* __cplusplus */ ++ ++#endif /* __OT_DEBUG_H__ */ +diff --git a/product/security_subsys/ext_inc/ot_type.h b/product/security_subsys/ext_inc/ot_type.h +new file mode 100644 +index 0000000..ba12de8 +--- /dev/null ++++ b/product/security_subsys/ext_inc/ot_type.h +@@ -0,0 +1,92 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __OT_TYPE_H__ ++#define __OT_TYPE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#else ++ ++#include ++#endif ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C"{ ++#endif ++#endif /* __cplusplus */ ++ ++#ifndef NULL ++ #define NULL 0L ++#endif ++ ++#define TD_NULL 0L ++#define TD_SUCCESS 0 ++#define TD_FAILURE (-1) ++ ++typedef unsigned char td_uchar; ++typedef unsigned char td_u8; ++typedef unsigned short td_u16; ++typedef unsigned int td_u32; ++typedef unsigned long td_ulong; ++ ++typedef char td_char; ++typedef signed char td_s8; ++typedef short td_s16; ++typedef int td_s32; ++typedef long td_slong; ++ ++typedef float td_float; ++typedef double td_double; ++ ++typedef void td_void; ++ ++#ifndef _M_IX86 ++ typedef unsigned long long td_u64; ++ typedef long long td_s64; ++#else ++ typedef unsigned __int64 td_u64; ++ typedef __int64 td_s64; ++#endif ++ ++typedef unsigned long td_size_t; ++typedef unsigned long td_length_t; ++typedef unsigned long int td_phys_addr_t; ++typedef td_u32 td_handle; ++typedef uintptr_t td_uintptr_t; ++typedef unsigned int td_fr32; ++ ++typedef enum { ++ TD_FALSE = 0, ++ TD_TRUE = 1, ++} td_bool; ++ ++#define EOK 0 ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* __cplusplus */ ++ ++#endif /* __OT_TYPE_H__ */ ++ +diff --git a/product/security_subsys/klad/include/ot_common_klad.h b/product/security_subsys/klad/include/ot_common_klad.h +new file mode 100644 +index 0000000..8b91ec4 +--- /dev/null ++++ b/product/security_subsys/klad/include/ot_common_klad.h +@@ -0,0 +1,139 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_COMMON_KLAD_H ++#define OT_COMMON_KLAD_H ++ ++#include "ot_type.h" ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C" { ++#endif ++#endif /* end of #ifdef __cplusplus */ ++ ++/* klad error code value */ ++#define OT_ERR_KLAD_NOT_INIT 0x805D0000 ++#define OT_ERR_KLAD_FAILED_INIT 0x805D0001 ++#define OT_ERR_KLAD_NULL_PTR 0x805D0002 ++#define OT_ERR_KLAD_INVALID_PARAM 0x805D0003 ++#define OT_ERR_KLAD_FAILED_CREATE_DEV 0x805D0004 ++#define OT_ERR_KLAD_DEVICE_BUSY 0x805D0005 ++#define OT_ERR_KLAD_FAILED_SEC_FUNC 0x805D0006 ++#define OT_ERR_KLAD_TIMEOUT 0x805D0007 ++#define OT_ERR_KLAD_FAILED_MEM 0x805D0008 ++#define OT_ERR_KLAD_FAILED_OPERATE 0x805D0009 ++#define OT_ERR_KLAD_INVALID_OWNER 0x805D000A ++#define OT_ERR_KLAD_INVALID_HANDLE 0x805D000B ++ ++/* klad max key length */ ++#define OT_KLAD_MAX_KEY_LEN 32 ++ ++/* klad rootkey select */ ++typedef enum { ++ OT_KLAD_ROOTKEY_SEL_OEM0 = 0x00, ++ OT_KLAD_ROOTKEY_SEL_OEM1, ++ OT_KLAD_ROOTKEY_SEL_OEM2, ++ OT_KLAD_ROOTKEY_SEL_OEM3, ++ OT_KLAD_ROOTKEY_SEL_VENDOR, ++ OT_KLAD_ROOTKEY_SEL_BUTT, ++} ot_klad_rootkey_sel; ++ ++typedef enum { ++ OT_KLAD_ROOTKEY_SEC_REE = 0x00, /* REE key, TEE CPU can select ree key */ ++ OT_KLAD_ROOTKEY_SEC_TEE, /* TEE key, REE CPU can't select tee key */ ++ OT_KLAD_ROOTKEY_SEC_BUTT, ++} ot_klad_rootkey_secure; ++ ++/* only OT_KLAD_TYPE_COMMON is valid */ ++typedef struct { ++ td_u32 owner_id; /* Derivative material, used for mcipher */ ++ ot_klad_rootkey_sel key_sel; /* common klad route select rootkey */ ++ ot_klad_rootkey_secure key_secure; /* Static value select: for ree key or for tee key */ ++} ot_klad_rootkey_attr; ++ ++/* klad route select */ ++typedef enum { ++ OT_KLAD_TYPE_CLEARCW, /* Used for clear key */ ++ OT_KLAD_TYPE_COMMON, /* Used for root key */ ++ OT_KLAD_TYPE_BUTT, ++} ot_klad_type; ++ ++/* klad config */ ++typedef struct { ++ ot_klad_type klad_type; /* klad route select: common/clear */ ++ ot_klad_rootkey_attr rootkey_attr; /* rootkey attr, OT_KLAD_TYPE_COMMON is valid */ ++} ot_klad_cfg; ++ ++typedef enum { ++ OT_KLAD_CRYPTO_ALG_AES = 0, ++ OT_KLAD_CRYPTO_ALG_SM4, ++ OT_KLAD_CRYPTO_ALG_BUTT, ++} ot_klad_crypto_alg; /* The content key can be used for which algorithm of the crypto engine. */ ++ ++/* klad attribute */ ++typedef struct { ++ ot_klad_cfg klad_cfg; ++} ot_klad_attr; ++ ++/* klad algorithm */ ++typedef enum { ++ OT_KLAD_ALG_TYPE_AES = 0, ++ OT_KLAD_ALG_TYPE_SM4, ++ OT_KLAD_ALG_TYPE_BUTT, ++} ot_klad_alg_type; ++ ++/* klad level */ ++typedef enum { ++ OT_KLAD_LEVEL1 = 0, ++ OT_KLAD_LEVEL2, ++ OT_KLAD_LEVEL3, ++ OT_KLAD_LEVEL_BUTT, ++} ot_klad_level; ++ ++/* session key: set 1~n-1 stage common route klad */ ++typedef struct { ++ ot_klad_level level; /* klad level */ ++ ot_klad_alg_type alg; /* klad algorithm */ ++ td_u32 key_size; /* klad key size */ ++ td_u8 key[OT_KLAD_MAX_KEY_LEN]; /* klad key */ ++} ot_klad_session_key; ++ ++/* content key: set n stage common route klad */ ++typedef struct { ++ ot_klad_alg_type alg; /* klad algorithm */ ++ ot_klad_crypto_alg crypto_alg; /* allowed target engine algorithm. */ ++ td_u32 key_size; /* klad key size */ ++ td_u8 key[OT_KLAD_MAX_KEY_LEN]; /* klad key */ ++} ot_klad_content_key; ++ ++/* clear key: set clear route klad */ ++typedef struct { ++ ot_klad_crypto_alg crypto_alg; /* allowed target engine algorithm. */ ++ td_u32 key_size; /* klad key size */ ++ td_u8 key[OT_KLAD_MAX_KEY_LEN]; /* klad key */ ++} ot_klad_clear_key; ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* end of #ifdef __cplusplus */ ++ ++#endif /* OT_COMMON_KLAD_H */ +diff --git a/product/security_subsys/klad/include/ot_mpi_klad.h b/product/security_subsys/klad/include/ot_mpi_klad.h +new file mode 100644 +index 0000000..bf2e4e6 +--- /dev/null ++++ b/product/security_subsys/klad/include/ot_mpi_klad.h +@@ -0,0 +1,133 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_MPI_KLAD_H ++#define OT_MPI_KLAD_H ++ ++#include "ot_common_klad.h" ++ ++#ifdef __cplusplus ++#if __cplusplus ++extern "C" { ++#endif ++#endif /* end of #ifdef __cplusplus */ ++ ++/* ++ * brief This API is used to init the klad device. ++ * param N/A ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad device fails to be initialized, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_init(td_void); ++ ++/* ++ * brief This API is used to deinit the klad device. ++ * param N/A ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad device fails to be Deinitialized, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_deinit(td_void); ++ ++/* ++ * brief This API is used to create the klad handle. ++ * param[in] klad Handle of key ladder. ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad handle fails to be created, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_create(td_handle *klad); ++ ++/* ++ * brief This API is used to destroy the klad handle. ++ * param[in] klad Handle of key ladder. ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad handle fails to be destroyed, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_destroy(td_handle klad); ++ ++/* ++ * brief This API is used to attach the klad handle & the keyslot handle ++ * param[in] klad Handle of key ladder. ++ * param[in] target Handle of keyslot. ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad handle and the keyslot handle fail to be attached, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_attach(td_handle klad, td_handle target); ++ ++/* ++ * brief This API is used to detach the klad handle & the keyslot handle ++ * param[in] klad Handle of key ladder. ++ * param[in] target Handle of keyslot. ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad handle and the keyslot handle fail to be detached, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_detach(td_handle klad, td_handle target); ++ ++/* ++ * brief This API is used to set the attributes of a key ladder. ++ * param[in] klad Handle of key ladder. ++ * param[in] attr Pointer to the attributes of a key ladder. ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad attributes fails to be set, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_set_attr(td_handle klad, const ot_klad_attr *attr); ++ ++/* ++ * brief This API is used to get the attributes of a key ladder. ++ * param[in] klad Handle of key ladder. ++ * param[out] attr Pointer to the attributes of a key ladder. ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The klad attributes fails to be obtained, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_get_attr(td_handle klad, ot_klad_attr *attr); ++ ++/* ++ * brief This API is used to set 1~n-1 stage common route klad ++ * param[in] klad Handle of key ladder. ++ * param[in] key Pointer to the session key ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The session key fails to be set, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_set_session_key(td_handle klad, const ot_klad_session_key *key); ++ ++/* ++ * brief This API is used to set n stage common route klad ++ * param[in] klad Handle of key ladder. ++ * param[in] key Pointer to the content key ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The content key fails to be set, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_set_content_key(td_handle klad, const ot_klad_content_key *key); ++ ++/* ++ * brief This API is used to set clear route klad ++ * param[in] klad Handle of key ladder. ++ * param[in] key Pointer to the clear key ++ * retval ::TD_SUCCESS Call this API successful. ++ * retval ::Others The clear key fails to be set, See the klad errno. ++ */ ++td_s32 ot_mpi_klad_set_clear_key(td_handle klad, const ot_klad_clear_key *key); ++ ++#ifdef __cplusplus ++#if __cplusplus ++} ++#endif ++#endif /* end of #ifdef __cplusplus */ ++ ++#endif /* OT_MPI_KLAD_H */ ++ +diff --git a/product/security_subsys/klad/src/Makefile b/product/security_subsys/klad/src/Makefile +new file mode 100644 +index 0000000..47927d2 +--- /dev/null ++++ b/product/security_subsys/klad/src/Makefile +@@ -0,0 +1,22 @@ ++# INTER_DRV defined before include arch/build.mak ++ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss928v100" "ss927v100")) ++INTER_DRV := ss928v100 ++endif ++ ++KLAD_BASE_DIR := $(srctree)/product/security_subsys/klad/src ++ ++# Add objs ++include $(KLAD_BASE_DIR)/mpi/build.mak ++include $(KLAD_BASE_DIR)/mkp/build.mak ++include $(KLAD_BASE_DIR)/arch/build.mak ++include $(KLAD_BASE_DIR)/osal/build.mak ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/include ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/../include ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/../../ext_inc ++ ++cflags-y += $(KLAD_CFLAGS) ++cflags-y += -DOT_KLAD_DEBUG=0 ++ ++ccflags-y += $(cflags-y) ++ ++obj-y += $(KLAD_API_OBJS) $(KLAD_DRV_OBJS) +diff --git a/product/security_subsys/klad/src/arch/build.mak b/product/security_subsys/klad/src/arch/build.mak +new file mode 100644 +index 0000000..b574b09 +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/build.mak +@@ -0,0 +1,12 @@ ++ ++include $(KLAD_BASE_DIR)/arch/$(INTER_DRV)/build.mak ++ ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/arch/$(INTER_DRV)/ ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/arch/hal/$(OT_KLAD_VERSION) ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/arch/hal/$(OT_KLAD_VERSION)/rkp/ ++ ++KLAD_DRV_OBJS += arch/hal/$(OT_KLAD_VERSION)/rkp/hal_rkp.o ++KLAD_DRV_OBJS += arch/hal/$(OT_KLAD_VERSION)/hal_klad.o ++ ++# lower to upper ++KLAD_CFLAGS += -DOT_KLAD_$(shell echo $(OT_KLAD_VERSION) | tr a-z A-Z) +diff --git a/product/security_subsys/klad/src/arch/hal/v100/hal_klad.c b/product/security_subsys/klad/src/arch/hal/v100/hal_klad.c +new file mode 100644 +index 0000000..abd4a7c +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/hal/v100/hal_klad.c +@@ -0,0 +1,872 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "hal_klad.h" ++#include "klad_cfg.h" ++#include "klad_reg_base.h" ++#include "klad_reg_define.h" ++#include "ot_debug_klad.h" ++#include "hal_rkp.h" ++#include "drv_osal_init.h" ++#include "drv_lib.h" ++#include "securec.h" ++ ++#define HKL_OP_TIMEOUT 6000 ++#define WORD_WIDTH 4 ++#define HKL_DATA_IN_8BYTES 8 ++#define HKL_DATA_IN_16BYTES 16 ++#define HKL_DATA_IN_24BYTES 24 ++#define HKL_DATA_IN_32BYTES 32 ++ ++typedef enum { ++ HKL_COM_LOCK_STAT_UNLOCK = 0x00, ++ HKL_COM_LOCK_STAT_TEE = 0xa5, ++ HKL_COM_LOCK_STAT_REE = 0xaa, ++} hkl_com_lock_stat; ++ ++typedef enum { ++ HKL_COM_ALG_AES = 0x01, ++ HKL_COM_ALG_SM4 = 0x02, ++} hkl_com_alg; ++ ++typedef enum { ++ HKL_COM_KEY_SIZE_128BIT = 0x00, ++ HKL_COM_KEY_SIZE_256BIT = 0x01, ++} hkl_com_key_size; ++ ++typedef struct { ++} hkl_com_key; ++ ++typedef enum { ++ HKL_KEY_TYPE_EVEN = 0x00, ++ HKL_KEY_TYPE_ODD = 0x01, ++} hkl_key_type; ++ ++typedef enum { ++ HKL_DSC_CODE_AES = 0x20, ++ HKL_DSC_CODE_SM4 = 0x50, ++} hkl_dsc_code; ++ ++typedef enum { ++ HKL_CLR_KEY_SIZE_64BIT = 0x00, ++ HKL_CLR_KEY_SIZE_128BIT = 0x01, ++ HKL_CLR_KEY_SIZE_192BIT = 0x02, ++ HKL_CLR_KEY_SIZE_256BIT = 0x03, ++} hkl_clr_key_size; ++ ++typedef struct { ++ td_u32 slot_num; ++ hkl_key_type type; ++ hkl_dsc_code dsc_code; ++ hkl_clr_key_size clr_size; ++ td_u8 key[HKL_DATA_IN_16BYTES]; ++} hkl_clr_route; ++ ++typedef struct { ++ td_u32 slot_num; ++ td_u32 level; ++ hkl_key_type type; ++ hkl_dsc_code dsc_code; ++ hkl_com_alg alg; /* klad level */ ++ hkl_com_key_size com_size; /* com key size */ ++ td_u8 key[HKL_DATA_IN_16BYTES]; ++} hkl_com_route; ++ ++static td_u8 *g_klad_reg_base = TD_NULL; ++ ++#ifdef KLAD_INT_SUPPORT ++typedef struct { ++ td_bool done; ++ KLAD_QUEUE_HEAD queue; ++} klad_hard_context; ++ ++/* 0 - clear route, 1 - common route */ ++#define KLAD_CLR_IDX 0 ++#define KLAD_COM_IDX 1 ++static klad_hard_context g_klad_ctx[KLAD_ROUTE_NUM]; ++#endif ++ ++#define check_hkl_reg_base_fail_return() \ ++ do { \ ++ if (g_klad_reg_base == TD_NULL) { \ ++ ot_klad_error("klad isn't init\n"); \ ++ return OT_ERR_KLAD_NOT_INIT; \ ++ } \ ++ } while (0) ++ ++static inline td_u32 _hkl_read(td_u32 offset) ++{ ++ td_u32 val; ++ val = klad_read(g_klad_reg_base + (offset)); ++ ot_klad_info(" offset %04x, read %08x\n", offset, val); ++ return val; ++} ++ ++static inline td_void _hkl_write(td_u32 offset, td_u32 data) ++{ ++ ot_klad_info("offset %04x, write %08x\n", offset, data); ++ klad_write(g_klad_reg_base + (offset), data); ++} ++ ++#ifdef KLAD_INT_SUPPORT ++static td_s32 _hkl_queue_callback_func(const td_void *param) ++{ ++ return *(td_bool *)param; ++} ++ ++static td_void _hkl_print_int_status(td_void) ++{ ++ kl_int_en int_en; ++ kl_int_raw int_raw; ++ kl_int int_sta; ++ ++ int_en.u32 = _hkl_read(KL_INT_EN); ++ ot_klad_error("klad int en %08x\n", int_en.u32); ++ ++ int_raw.u32 = _hkl_read(KL_INT_RAW); ++ ot_klad_error("klad int raw %08x\n", int_raw.u32); ++ ++ int_sta.u32 = _hkl_read(KL_INT); ++ ot_klad_error("klad int sta %08x\n", int_sta.u32); ++} ++ ++static td_void _hkl_enable_int(td_bool is_int) ++{ ++ kl_int_en int_en; ++ int_en.u32 = 0; ++ int_en.bits.kl_int_en = is_int; ++ int_en.bits.kl_lock_int_en = is_int; ++ _hkl_write(KL_INT_EN, int_en.u32); ++} ++ ++static td_void _hkl_clean_interrupt(td_void) ++{ ++ kl_int_raw int_raw; ++ ++ int_raw.u32 = 0; ++ int_raw.bits.kl_int_raw = 1; ++ int_raw.bits.kl_com_lock_int_raw = 1; ++ _hkl_write(KL_INT_RAW, int_raw.u32); ++} ++ ++static td_s32 _hkl_interrupt_isr(td_s32 irq, td_void *dev_id) ++{ ++ kl_int_raw int_raw; ++ td_u32 idx; ++ ++ ot_unused(dev_id); ++ ++ _hkl_clean_interrupt(); ++ ++ int_raw.u32 = _hkl_read(KL_INT_RAW); ++ if (int_raw.bits.kl_int_num == KLAD_CLR_ROUTE) { ++ idx = KLAD_CLR_IDX; ++ } else { ++ idx = KLAD_COM_IDX; ++ } ++ ++ if (g_klad_ctx[idx].done == TD_FALSE) { ++ g_klad_ctx[idx].done = TD_TRUE; ++ klad_queue_wait_up(&g_klad_ctx[idx].queue); ++ } ++ ++ return KLAD_IRQ_HANDLED; ++} ++ ++static td_s32 _hkl_interrupt_init(td_void) ++{ ++ td_s32 ret; ++ td_u32 i; ++ ++ (td_void)memset_s(g_klad_ctx, sizeof(g_klad_ctx), 0, sizeof(g_klad_ctx)); ++ ++ /* enable interrupt */ ++ _hkl_enable_int(TD_TRUE); ++ ++ /* the history of interception may trigger the system to ++ * call the irq function before initialization ++ * when register interrupt, this will cause a system abort. */ ++ _hkl_clean_interrupt(); ++ ++ /* register interrupt */ ++ ret = klad_request_irq(ot_klad_get_klad_irq(), _hkl_interrupt_isr, KLAD_INT_NAME); ++ ot_klad_func_fail_return(klad_request_irq, ret != TD_SUCCESS, OT_ERR_KLAD_FAILED_OPERATE); ++ ++ /* init queue */ ++ for (i = 0; i < KLAD_ROUTE_NUM; i++) { ++ klad_queue_init(&g_klad_ctx[i].queue); ++ } ++ ++ return ret; ++} ++ ++static td_void _hkl_interrupt_deinit(td_void) ++{ ++ td_u32 i; ++ ++ for (i = 0; i < KLAD_ROUTE_NUM; i++) { ++ klad_queue_destroy(&g_klad_ctx[i].queue); ++ } ++ ++ /* free irq */ ++ klad_free_irq(ot_klad_get_klad_irq(), KLAD_INT_NAME); ++ ++ _hkl_enable_int(TD_FALSE); ++} ++#endif ++ ++static td_s32 _hkl_com_lock_idle(td_void) ++{ ++ kl_com_lock_info lock_info; ++ td_u32 time_out = HKL_OP_TIMEOUT; ++ ++ while (time_out--) { ++ lock_info.u32 = _hkl_read(KL_COM_LOCK_INFO); ++ if (lock_info.bits.kl_com_lock_busy == 0x02) { /* 1 - busy, 2 - idle */ ++ break; ++ } ++ } ++ ++ if (time_out == 0) { ++ ot_klad_error("common klad op timeout!"); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++ return TD_SUCCESS; ++} ++ ++static hkl_com_lock_stat _hkl_com_lock_stat(td_void) ++{ ++ kl_com_lock_status status; ++ ++ status.u32 = _hkl_read(KL_COM_LOCK_STATUS); ++ return status.bits.kl_com_lock_stat; ++} ++ ++static td_void _hkl_lock_ctrl(td_void) ++{ ++ kl_lock_ctrl lock_ctl; ++ ++ lock_ctl.u32 = _hkl_read(KL_LOCK_CTRL); ++ lock_ctl.bits.kl_lock = 1; ++ _hkl_write(KL_LOCK_CTRL, lock_ctl.u32); ++} ++ ++static td_void _hkl_unlock_ctrl(td_void) ++{ ++ kl_unlock_ctrl unlock_ctl; ++ ++ unlock_ctl.u32 = _hkl_read(KL_UNLOCK_CTRL); ++ unlock_ctl.bits.kl_unlock = 1; ++ _hkl_write(KL_UNLOCK_CTRL, unlock_ctl.u32); ++} ++ ++static td_s32 _hkl_com_wait_idle(void) ++{ ++ kl_state state; ++ td_u32 time_out = HKL_OP_TIMEOUT; ++ ++ while (time_out--) { ++ state.u32 = _hkl_read(KL_STATE); ++ if (state.bits.kl_busy == 0x02) { ++ break; ++ } ++ klad_udelay(1); ++ } ++ ++ if (time_out == 0) { ++ ot_klad_error("hkl op timeout!"); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 _hkl_com_route_op_finish(void) ++{ ++ td_s32 ret; ++ ++#ifdef KLAD_INT_SUPPORT ++ ret = klad_queue_wait_timeout(&g_klad_ctx[KLAD_COM_IDX].queue, ++ _hkl_queue_callback_func, &g_klad_ctx[KLAD_COM_IDX].done, HKL_OP_TIMEOUT); ++ if (ret == 0) { ++ ot_klad_error("Failed call klad_queue_wait_timeout\n"); ++ _hkl_print_int_status(); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++#else ++ ret = _hkl_com_wait_idle(); ++ ot_klad_func_fail_return(_hkl_com_wait_idle, ret != TD_SUCCESS, ret); ++#endif ++ ++ return TD_SUCCESS; ++} ++ ++static td_void _hkl_set_sec_cfg(td_void) ++{ ++ kl_key_sec_cfg reg; ++ ++ reg.u32 = _hkl_read(KL_KEY_SEC_CFG); ++ reg.bits.dest_sec = 1; ++ reg.bits.dest_nsec = 1; ++ reg.bits.src_sec = 1; ++ reg.bits.src_nsec = 1; ++ reg.bits.key_sec = 1; ++ _hkl_write(KL_KEY_SEC_CFG, reg.u32); ++} ++ ++static td_void _hkl_set_com_ctrl(td_u32 level, td_u32 alg, td_u32 key_size) ++{ ++ kl_com_ctrl reg; ++ ++ reg.u32 = _hkl_read(KL_COM_CTRL); ++ reg.bits.kl_com_start = 1; ++ reg.bits.kl_com_level_sel = level; ++ reg.bits.kl_com_alg_sel = alg; ++ reg.bits.kl_com_key_size = key_size; ++ _hkl_write(KL_COM_CTRL, reg.u32); ++} ++ ++static td_s32 _hkl_check_error(td_void) ++{ ++ kl_error kl_err; ++ kc_error kc_err; ++ ++ kl_err.u32 = _hkl_read(KL_ERROR); ++ if (kl_err.u32 != 0) { ++ ot_klad_error("read KL_ERROR failed %x\n", kl_err.u32); ++ return OT_ERR_KLAD_FAILED_OPERATE; ++ } ++ ++ kc_err.u32 = _hkl_read(KC_ERROR); ++ if (kc_err.u32 != 0) { ++ ot_klad_error("read KC_ERROR failed %x\n", kc_err.u32); ++ return OT_ERR_KLAD_FAILED_OPERATE; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_void _hkl_set_data_in(const td_u8 *data, td_u32 data_len) ++{ ++ td_u32 i, u32_data; ++ ++ if (data_len % WORD_WIDTH != 0) { ++ ot_klad_error("Invalid date length %d\n", data_len); ++ return; ++ } ++ ++ for (i = 0; i < data_len; i += WORD_WIDTH) { ++ u32_data = ((td_u32)data[i + 3] << 24) | /* 3 arr index, 24 bits shift */ ++ ((td_u32)data[i + 2] << 16) | /* 2 arr index, 16 bits shift */ ++ ((td_u32)data[i + 1] << 8) | /* 1 arr index, 8 bits shift */ ++ ((td_u32)data[i]); ++ _hkl_write(KL_DATA_IN_0 + i, u32_data); ++ } ++} ++ ++static td_void _hkl_set_key_addr(td_u32 slot_num, hkl_key_type type) ++{ ++ kl_key_addr addr; ++ addr.u32 = ((slot_num << 1) | (td_u32)type); /* addr.key_addr is 10bits, can't assign */ ++ ++ _hkl_write(KL_KEY_ADDR, addr.u32); ++} ++ ++static td_void _hkl_set_key_cfg(hkl_dsc_code dsc) ++{ ++ kl_key_cfg cfg; ++ cfg.u32 = 0; ++ cfg.bits.dsc_code = dsc; ++ _hkl_write(KL_KEY_CFG, cfg.u32); ++} ++ ++/* wait clear klad route idle */ ++static td_s32 _hkl_clr_route_idle(td_void) ++{ ++ kl_clr_ctrl ctrl; ++ td_u32 timeout_cnt = HKL_OP_TIMEOUT; ++ ++ while (timeout_cnt--) { ++ ctrl.u32 = _hkl_read(KL_CLR_CTRL); ++ if (ctrl.bits.kl_clr_start == 0) { ++ break; ++ } ++ klad_udelay(1); ++ } ++ ++ if (timeout_cnt == 0) { ++ ot_klad_error("hkl wait op timeout!\n"); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++/* wait clear klad route operate finish & check operate status */ ++static td_s32 _hkl_clr_route_op_finish(td_void) ++{ ++ td_s32 ret; ++ ++#ifdef KLAD_INT_SUPPORT ++ ret = klad_queue_wait_timeout(&g_klad_ctx[KLAD_CLR_IDX].queue, ++ _hkl_queue_callback_func, &g_klad_ctx[KLAD_CLR_IDX].done, HKL_OP_TIMEOUT); ++ if (ret == 0) { ++ ot_klad_error("Failed call klad_queue_wait_timeout\n"); ++ _hkl_print_int_status(); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++#else ++ ret = _hkl_clr_route_idle(); ++ ot_klad_func_fail_return(_hkl_clr_route_idle, ret != TD_SUCCESS, ret); ++#endif ++ ++ ret = _hkl_check_error(); ++ ot_klad_func_fail_return(_hkl_check_error, ret != TD_SUCCESS, ret); ++ ++ return ret; ++} ++ ++static td_void _hkl_startup_clr_route(hkl_clr_key_size key_size) ++{ ++ kl_clr_ctrl ctrl; ++ ctrl.u32 = 0; ++ ctrl.bits.kl_clr_key_size = key_size; ++ ctrl.bits.kl_clr_start = 1; ++ _hkl_write(KL_CLR_CTRL, ctrl.u32); ++} ++ ++static td_s32 _hal_com_lock(td_void) ++{ ++ td_s32 ret; ++ td_u32 timeout = HKL_OP_TIMEOUT; ++ hkl_com_lock_stat stat; ++ hkl_com_lock_stat target_stat = klad_is_secure_cpu() ? HKL_COM_LOCK_STAT_TEE : HKL_COM_LOCK_STAT_REE; ++ ++ stat = _hkl_com_lock_stat(); ++ if (stat == target_stat) { ++ return TD_SUCCESS; ++ } ++ ++ while (timeout--) { ++ ret = _hkl_com_lock_idle(); ++ ot_klad_func_fail_return(_hkl_com_lock_idle, ret != TD_SUCCESS, ret); ++ ++ _hkl_lock_ctrl(); ++ ++ ret = _hkl_com_lock_idle(); ++ ot_klad_func_fail_return(_hkl_com_lock_idle, ret != TD_SUCCESS, ret); ++ ++ stat = _hkl_com_lock_stat(); ++ if (stat == target_stat) { ++ break; ++ } ++ } ++ ++ if (timeout == 0) { ++ ot_klad_error("common klad op timeout!"); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++ ++ return ret; ++} ++ ++static td_s32 _hal_com_unlock(td_void) ++{ ++ td_s32 ret; ++ td_u32 timeout = HKL_OP_TIMEOUT; ++ hkl_com_lock_stat stat; ++ hkl_com_lock_stat cur_stat = klad_is_secure_cpu() ? HKL_COM_LOCK_STAT_TEE : HKL_COM_LOCK_STAT_REE; ++ ++ stat = _hkl_com_lock_stat(); ++ if (stat == HKL_COM_LOCK_STAT_UNLOCK) { ++ return TD_SUCCESS; ++ } else if (stat != cur_stat) { ++ ot_klad_error("klad owner don't match %s\n", (stat == HKL_COM_LOCK_STAT_REE) ? "REE" : "TEE"); ++ return OT_ERR_KLAD_INVALID_OWNER; ++ } ++ ++ /* unlock when stat = cur_stat */ ++ while (timeout--) { ++ ret = _hkl_com_lock_idle(); ++ ot_klad_func_fail_return(_hkl_com_lock_idle, ret != TD_SUCCESS, ret); ++ ++ _hkl_unlock_ctrl(); ++ ++ ret = _hkl_com_lock_idle(); ++ ot_klad_func_fail_return(_hkl_com_lock_idle, ret != TD_SUCCESS, ret); ++ ++ stat = _hkl_com_lock_stat(); ++ if (stat == HKL_COM_LOCK_STAT_UNLOCK) { ++ break; ++ } ++ } ++ ++ if (timeout == 0) { ++ ot_klad_error("common klad op timeout!"); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++ ++ return ret; ++} ++ ++static td_s32 _hal_klad_cfg_com_route(hkl_com_route *com_route, ++ td_u32 level, td_u32 slot_num, hkl_key_type type, const klad_common_slot *common_slot) ++{ ++ td_s32 ret; ++ const klad_common_key *com_key = &common_slot->klad_key[level]; ++ ++ (td_void)memset_s(com_route, sizeof(hkl_com_route), 0, sizeof(hkl_com_route)); ++ com_route->level = level; ++ com_route->slot_num = slot_num; ++ com_route->type = type; ++ ++ if (common_slot->crypto_alg == OT_KLAD_CRYPTO_ALG_AES) { ++ com_route->dsc_code = HKL_DSC_CODE_AES; ++#ifdef KLAD_SM4_SUPPORT ++ } else if (common_slot->crypto_alg == OT_KLAD_CRYPTO_ALG_SM4) { ++ com_route->dsc_code = HKL_DSC_CODE_SM4; ++#endif ++ } else { ++ ot_klad_error("Invalid crypto alg %d\n", common_slot->crypto_alg); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ ++ if (com_key->alg == OT_KLAD_ALG_TYPE_AES) { ++ com_route->alg = HKL_COM_ALG_AES; ++#ifdef KLAD_SM4_SUPPORT ++ } else if (com_key->alg == OT_KLAD_ALG_TYPE_SM4) { ++ com_route->alg = HKL_COM_ALG_SM4; ++#endif ++ } else { ++ ot_klad_error("Invalid klad alg %d\n", com_key->alg); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ ++ if (com_key->key_size == HKL_DATA_IN_16BYTES) { ++ com_route->com_size = HKL_COM_KEY_SIZE_128BIT; ++ } else if (com_key->key_size == HKL_DATA_IN_32BYTES) { ++ com_route->com_size = HKL_COM_KEY_SIZE_256BIT; ++ } else { ++ ot_klad_error("Invalid key size %d\n", com_key->key_size); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ ++ if (type == HKL_KEY_TYPE_EVEN) { ++ ret = memcpy_s(com_route->key, sizeof(com_route->key), ++ com_key->key, HKL_DATA_IN_16BYTES); ++ } else { ++ ret = memcpy_s(com_route->key, sizeof(com_route->key), ++ &com_key->key[HKL_DATA_IN_16BYTES], OT_KLAD_MAX_KEY_LEN - HKL_DATA_IN_16BYTES); ++ } ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 _hal_klad_startup_com_route(const hkl_com_route *com_route) ++{ ++ td_s32 ret; ++ ++#ifdef KLAD_INT_SUPPORT ++ g_klad_ctx[KLAD_COM_IDX].done = TD_FALSE; ++#endif ++ ++ /* step 1. wait klad idle */ ++ ret = _hkl_com_wait_idle(); ++ ot_klad_func_fail_return(_hkl_com_wait_idle, ret != TD_SUCCESS, ret); ++ ++ /* step 2. set key data */ ++ _hkl_set_data_in(com_route->key, HKL_DATA_IN_16BYTES); ++ ++ /* step 3. bind key slot, set even&odd key */ ++ _hkl_set_key_addr(com_route->slot_num, com_route->type); ++ ++ /* step 4. set dsc alg */ ++ _hkl_set_key_cfg(com_route->dsc_code); ++ ++ /* step 5. set secure attribute */ ++ _hkl_set_sec_cfg(); ++ ++ /* step 6. set klad level, alg, key size & startup clear route */ ++ _hkl_set_com_ctrl(com_route->level, com_route->alg, com_route->com_size); ++ ++ /* step 7. wait klad operation finish */ ++ ret = _hkl_com_route_op_finish(); ++ ot_klad_func_fail_return(_hkl_com_route_op_finish, ret != TD_SUCCESS, ret); ++ ++ /* step 8. wait klad idle */ ++ ret = _hkl_check_error(); ++ ot_klad_func_fail_return(_hkl_check_error, ret != TD_SUCCESS, ret); ++ ++ return ret; ++} ++ ++static td_s32 _hal_klad_run_com_route(td_u32 slot_num, const klad_common_slot *common_slot) ++{ ++ td_u32 i; ++ td_s32 ret; ++ hkl_com_route com_route; ++ ++ for (i = 0; i < common_slot->klad_level; i++) { ++ if (common_slot->klad_key[i].key_size == HKL_DATA_IN_16BYTES) { ++ /* mcipher default used even key */ ++ ret = _hal_klad_cfg_com_route(&com_route, i, slot_num, HKL_KEY_TYPE_EVEN, common_slot); ++ ot_klad_func_fail_return(_hal_klad_cfg_com_route, ret != TD_SUCCESS, ret); ++ ++ ret = _hal_klad_startup_com_route(&com_route); ++ ot_klad_func_fail_return(_hal_klad_startup_com_route, ret != TD_SUCCESS, ret); ++ } else if (common_slot->klad_key[i].key_size == HKL_DATA_IN_32BYTES) { ++ /* First step, mcipher used even key */ ++ ret = _hal_klad_cfg_com_route(&com_route, i, slot_num, HKL_KEY_TYPE_EVEN, common_slot); ++ ot_klad_func_fail_return(_hal_klad_cfg_com_route, ret != TD_SUCCESS, ret); ++ ++ ret = _hal_klad_startup_com_route(&com_route); ++ ot_klad_func_fail_return(_hal_klad_startup_com_route, ret != TD_SUCCESS, ret); ++ ++ /* Second step, mcipher used odd key */ ++ ret = _hal_klad_cfg_com_route(&com_route, i, slot_num, HKL_KEY_TYPE_ODD, common_slot); ++ ot_klad_func_fail_return(_hal_klad_cfg_com_route, ret != TD_SUCCESS, ret); ++ ++ ret = _hal_klad_startup_com_route(&com_route); ++ ot_klad_func_fail_return(_hal_klad_startup_com_route, ret != TD_SUCCESS, ret); ++ } else { ++ ot_klad_error("Invalid key size %d\n", common_slot->klad_key[i].key_size); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ } ++ /* clean key */ ++ (td_void)memset_s(com_route.key, HKL_DATA_IN_16BYTES, 0, HKL_DATA_IN_16BYTES); ++ ++ return ret; ++} ++ ++static td_s32 _hal_klad_cfg_clr_route(hkl_clr_route *clr_route, ++ td_u32 slot_num, hkl_key_type type, const klad_clear_slot *clear_slot) ++{ ++ td_s32 ret; ++ const ot_klad_clear_key *clear_key = &clear_slot->clear_key; ++ ++ (td_void)memset_s(clr_route, sizeof(hkl_clr_route), 0, sizeof(hkl_clr_route)); ++ clr_route->slot_num = slot_num; ++ clr_route->type = type; ++ ++ if (clear_key->crypto_alg == OT_KLAD_CRYPTO_ALG_AES) { ++ clr_route->dsc_code = HKL_DSC_CODE_AES; ++#ifdef KLAD_SM4_SUPPORT ++ } else if (clear_key->crypto_alg == OT_KLAD_CRYPTO_ALG_SM4) { ++ clr_route->dsc_code = HKL_DSC_CODE_SM4; ++#endif ++ } else { ++ ot_klad_error("Invalid crypto alg %d\n", clear_key->crypto_alg); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ ++ switch (clear_key->key_size) { ++ case HKL_DATA_IN_16BYTES: ++ clr_route->clr_size = HKL_CLR_KEY_SIZE_128BIT; ++ ++ /* for 128bits key format: even key: 8bytes 8bytes */ ++ ret = memcpy_s(clr_route->key, sizeof(clr_route->key), clear_key->key, HKL_DATA_IN_16BYTES); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ break; ++ case HKL_DATA_IN_24BYTES: ++ clr_route->clr_size = HKL_CLR_KEY_SIZE_192BIT; ++ ++ /* for 192bits key format: even key: 0 8bytes, odd key: 8bytes 8bytes */ ++ if (type == HKL_KEY_TYPE_EVEN) { ++ ret = memcpy_s(&clr_route->key[HKL_DATA_IN_8BYTES], ++ sizeof(clr_route->key) - HKL_DATA_IN_8BYTES, clear_key->key, HKL_DATA_IN_8BYTES); ++ } else { ++ ret = memcpy_s(clr_route->key, sizeof(clr_route->key), ++ &clear_key->key[HKL_DATA_IN_8BYTES], HKL_DATA_IN_16BYTES); ++ } ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ break; ++ case HKL_DATA_IN_32BYTES: ++ clr_route->clr_size = HKL_CLR_KEY_SIZE_256BIT; ++ ++ /* for 256bits key format: even key: 8bytes 8bytes, odd key: 8bytes 8bytes */ ++ if (type == HKL_KEY_TYPE_EVEN) { ++ ret = memcpy_s(clr_route->key, sizeof(clr_route->key), clear_key->key, HKL_DATA_IN_16BYTES); ++ } else { ++ ret = memcpy_s(clr_route->key, sizeof(clr_route->key), ++ &clear_key->key[HKL_DATA_IN_16BYTES], HKL_DATA_IN_16BYTES); ++ } ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ break; ++ default: ++ ot_klad_error("Invalid key size %d\n", clear_key->key_size); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_s32 _hal_klad_startup_clr_route(const hkl_clr_route *clr_route) ++{ ++ td_s32 ret; ++ ++#ifdef KLAD_INT_SUPPORT ++ g_klad_ctx[KLAD_CLR_IDX].done = TD_FALSE; ++#endif ++ ++ /* step 1. wait clear route klad idle */ ++ ret = _hkl_clr_route_idle(); ++ ot_klad_func_fail_return(_hkl_clr_route_idle, ret != TD_SUCCESS, ret); ++ ++ /* step 2. set key data */ ++ _hkl_set_data_in(clr_route->key, HKL_DATA_IN_16BYTES); ++ ++ /* step 3. bind key slot, set even&odd key */ ++ _hkl_set_key_addr(clr_route->slot_num, clr_route->type); ++ ++ /* step 4. set dsc alg */ ++ _hkl_set_key_cfg(clr_route->dsc_code); ++ ++ /* step 5. set secure attribute */ ++ _hkl_set_sec_cfg(); ++ ++ /* step 6. set key size & startup clear route */ ++ _hkl_startup_clr_route(clr_route->clr_size); ++ ++ /* step 7. wait clear route klad idle & check error */ ++ ret = _hkl_clr_route_op_finish(); ++ ot_klad_func_fail_return(_hkl_clr_route_idle, ret != TD_SUCCESS, ret); ++ ++ return ret; ++} ++ ++ ++td_s32 hal_klad_init(td_void) ++{ ++ td_s32 ret; ++ ++ g_klad_reg_base = klad_ioremap_nocache(HKL_REG_BASE_ADDR_PHY, HKL_REG_BASE_ADDR_SIZE); ++ if (g_klad_reg_base == TD_NULL) { ++ ot_klad_error("hkl ioremap with nocache failed!!\n"); ++ return OT_ERR_KLAD_FAILED_MEM; ++ } ++ ++ ret = hal_rkp_init(); ++ if (ret != TD_SUCCESS) { ++ klad_iounmap(g_klad_reg_base, HKL_REG_BASE_ADDR_SIZE); ++ g_klad_reg_base = TD_NULL; ++ return ret; ++ } ++ ++#ifdef KLAD_INT_SUPPORT ++ ret = _hkl_interrupt_init(); ++ if (ret != TD_SUCCESS) { ++ ot_klad_error("Failed call _hkl_interrupt_init\n"); ++ klad_iounmap(g_klad_reg_base, HKL_REG_BASE_ADDR_SIZE); ++ g_klad_reg_base = TD_NULL; ++ hal_rkp_deinit(); ++ } ++#endif ++ ++ return TD_SUCCESS; ++} ++ ++td_void hal_klad_deinit(td_void) ++{ ++#ifdef KLAD_INT_SUPPORT ++ _hkl_interrupt_deinit(); ++#endif ++ ++ if (g_klad_reg_base != TD_NULL) { ++ klad_iounmap(g_klad_reg_base, HKL_REG_BASE_ADDR_SIZE); ++ g_klad_reg_base = TD_NULL; ++ } ++ ++ hal_rkp_deinit(); ++} ++ ++td_s32 hal_klad_common_route_startup(td_u32 slot_num, const klad_common_slot *common_slot) ++{ ++ td_s32 ret, ret1; ++ ++ ot_klad_enter(); ++ ++ check_hkl_reg_base_fail_return(); ++ ++ ret = _hal_com_lock(); ++ ot_klad_func_fail_return(_hal_com_lock, ret != TD_SUCCESS, ret); ++ ++ ret = hal_rkp_startup(&common_slot->key_attr); ++ if (ret != TD_SUCCESS) { ++ ot_klad_error("rkp failed, ret = %x\n", ret); ++ goto klad_exit; ++ } ++ ++ ret = _hal_klad_run_com_route(slot_num, common_slot); ++ if (ret != TD_SUCCESS) { ++ ot_klad_error("common route failed, ret = %x\n", ret); ++ goto klad_exit; ++ } ++ ++klad_exit: ++ /* _hal_com_unlock can't use 'ret' as return value */ ++ ret1 = _hal_com_unlock(); ++ ot_klad_func_fail_return(_hal_com_unlock, ret1 != TD_SUCCESS, ret1); ++ ++ ot_klad_exit(); ++ return ret; ++} ++ ++td_s32 hal_klad_clear_route_startup(td_u32 slot_num, const klad_clear_slot *clear_slot) ++{ ++ td_s32 ret; ++ hkl_clr_route clr_route; ++ ++ ot_klad_enter(); ++ ++ check_hkl_reg_base_fail_return(); ++ ++ if (clear_slot->clear_key.key_size == HKL_DATA_IN_16BYTES) { ++ /* mcipher default used even key */ ++ ret = _hal_klad_cfg_clr_route(&clr_route, slot_num, HKL_KEY_TYPE_EVEN, clear_slot); ++ ot_klad_func_fail_return(_hal_klad_cfg_clr_route, ret != TD_SUCCESS, ret); ++ ++ ret = _hal_klad_startup_clr_route(&clr_route); ++ ot_klad_func_fail_return(_hal_klad_startup_clr_route, ret != TD_SUCCESS, ret); ++ } else if ((clear_slot->clear_key.key_size == HKL_DATA_IN_24BYTES) || ++ (clear_slot->clear_key.key_size == HKL_DATA_IN_32BYTES)) { ++ /* First step, mcipher used even key */ ++ ret = _hal_klad_cfg_clr_route(&clr_route, slot_num, HKL_KEY_TYPE_EVEN, clear_slot); ++ ot_klad_func_fail_return(_hal_klad_cfg_clr_route, ret != TD_SUCCESS, ret); ++ ++ ret = _hal_klad_startup_clr_route(&clr_route); ++ ot_klad_func_fail_return(_hal_klad_startup_clr_route, ret != TD_SUCCESS, ret); ++ ++ /* Second step, mcipher used odd key */ ++ ret = _hal_klad_cfg_clr_route(&clr_route, slot_num, HKL_KEY_TYPE_ODD, clear_slot); ++ ot_klad_func_fail_return(_hal_klad_cfg_clr_route, ret != TD_SUCCESS, ret); ++ ++ ret = _hal_klad_startup_clr_route(&clr_route); ++ ot_klad_func_fail_return(_hal_klad_startup_clr_route, ret != TD_SUCCESS, ret); ++ } else { ++ ot_klad_error("Invalid key size %d\n", clear_slot->clear_key.key_size); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ /* clean key */ ++ (td_void)memset_s(clr_route.key, HKL_DATA_IN_16BYTES, 0, HKL_DATA_IN_16BYTES); ++ ++ ot_klad_exit(); ++ ++ return ret; ++} ++ +diff --git a/product/security_subsys/klad/src/arch/hal/v100/hal_klad.h b/product/security_subsys/klad/src/arch/hal/v100/hal_klad.h +new file mode 100644 +index 0000000..10d95bc +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/hal/v100/hal_klad.h +@@ -0,0 +1,34 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HAL_KLAD_H ++#define HAL_KLAD_H ++ ++#include "drv_klad_sw_utils.h" ++ ++td_s32 hal_klad_init(td_void); ++ ++td_void hal_klad_deinit(td_void); ++ ++td_s32 hal_klad_common_route_startup(td_u32 slot_num, const klad_common_slot *common_slot); ++ ++td_s32 hal_klad_clear_route_startup(td_u32 slot_num, const klad_clear_slot *key); ++ ++#endif /* HAL_KLAD_H */ ++ +diff --git a/product/security_subsys/klad/src/arch/hal/v100/klad_reg_define.h b/product/security_subsys/klad/src/arch/hal/v100/klad_reg_define.h +new file mode 100644 +index 0000000..27057d0 +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/hal/v100/klad_reg_define.h +@@ -0,0 +1,333 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef KLAD_REG_DEFINE_H ++#define KLAD_REG_DEFINE_H ++ ++/* Define the union kl_key_addr */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int key_addr : 10; /* [9..0] */ ++ unsigned int reserved_0 : 22; /* [31..10] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_key_addr; ++ ++/* Define the union kl_key_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_cfg_rsv_0 : 2; /* [1..0] */ ++ unsigned int reserved_0 : 2; /* [3..2] */ ++ unsigned int dsc_code : 8; /* [11..4] */ ++ unsigned int reserved_1 : 4; /* [15..12] */ ++ unsigned int kl_cfg_rsv_1 : 1; /* [16] */ ++ unsigned int kl_cfg_rsv_2 : 1; /* [17] */ ++ unsigned int reserved_2 : 14; /* [31..18] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_key_cfg; ++ ++/* Define the union kl_key_sec_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int key_sec : 1; /* [0] */ ++ unsigned int src_nsec : 1; /* [1] */ ++ unsigned int src_sec : 1; /* [2] */ ++ unsigned int dest_nsec : 1; /* [3] */ ++ unsigned int dest_sec : 1; /* [4] */ ++ unsigned int reserved_0 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_key_sec_cfg; ++ ++/* Define the union kl_state */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_busy : 2; /* [1..0] */ ++ unsigned int reserved_0 : 30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_state; ++ ++/* Define the union kl_crc */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_crc : 8; /* [7..0] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_crc; ++ ++/* Define the union kl_error */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int level_sel_err : 1; /* [0] */ ++ unsigned int algo_sel_err : 1; /* [1] */ ++ unsigned int reserved_0 : 1; /* [2] */ ++ unsigned int dsc_code_err : 1; /* [3] */ ++ unsigned int reserved_1 : 2; /* [5..4] */ ++ unsigned int key_size_err : 1; /* [6] */ ++ unsigned int rk_busy_err : 1; /* [7] */ ++ unsigned int reserved_2 : 4; /* [11..8] */ ++ unsigned int rk_rdy_err : 1; /* [12] */ ++ unsigned int lv1_rdy_err : 1; /* [13] */ ++ unsigned int lv2_rdy_err : 1; /* [14] */ ++ unsigned int reserved_3 : 17; /* [31..15] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_error; ++ ++/* Define the union kc_error */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int reserved_0 : 2; /* [1..0] */ ++ unsigned int tee_access_err : 1; /* [2] */ ++ unsigned int ree_access_err : 1; /* [3] */ ++ unsigned int reserved_1 : 10; /* [13..4] */ ++ unsigned int sm4_dis_err : 1; /* [14] */ ++ unsigned int reserved_2 : 6; /* [20..15] */ ++ unsigned int send_time_out : 1; /* [21] */ ++ unsigned int reserved_3 : 10; /* [31..22] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kc_error; ++ ++/* Define the union kl_int_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_int_en : 1; /* [0] */ ++ unsigned int kl_lock_int_en : 1; /* [1] */ ++ unsigned int reserved_0 : 30; /* [31..2] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_int_en; ++ ++/* Define the union kl_int_raw */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_int_raw : 1; /* [0] */ ++ unsigned int reserved_0 : 3; /* [3..1] */ ++ unsigned int kl_int_num : 5; /* [8..4] */ ++ unsigned int reserved_1 : 3; /* [11..9] */ ++ unsigned int kl_com_lock_int_raw : 1; /* [12] */ ++ unsigned int reserved_2 : 19; /* [31..13] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_int_raw; ++ ++/* Define the union kl_int */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_int : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_int; ++ ++/* Define the union kl_power_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_power_en : 4; /* [3..0] */ ++ unsigned int reserved_0 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_power_en; ++ ++/* Define the union kl_power_en_lock */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_power_en_lock : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_power_en_lock; ++ ++/* Define the union kl_rk_gen_status */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_rk_gen_busy : 9; /* [8..0] */ ++ unsigned int reserved_0 : 23; /* [31..9] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_rk_gen_status; ++ ++/* Define the union kl_lock_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_lock : 1; /* [0] */ ++ unsigned int reserved_0 : 3; /* [3..1] */ ++ unsigned int kl_lock_rsv_0 : 3; /* [6..4] */ ++ unsigned int reserved_1 : 25; /* [31..7] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_lock_ctrl; ++ ++/* Define the union kl_unlock_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_unlock : 1; /* [0] */ ++ unsigned int reserved_0 : 3; /* [3..1] */ ++ unsigned int kl_unlock_rsv_0 : 3; /* [6..4] */ ++ unsigned int reserved_1 : 1; /* [7] */ ++ unsigned int kl_unlock_rsv_1 : 3; /* [10..8] */ ++ unsigned int reserved_2 : 21; /* [31..11] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_unlock_ctrl; ++ ++/* Define the union kl_com_lock_info */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_com_lock_busy : 2; /* [1..0] */ ++ unsigned int kl_com_lock_fail : 2; /* [3..2] */ ++ unsigned int kl_com_unlock_fail : 2; /* [5..4] */ ++ unsigned int reserved_0 : 2; /* [7..6] */ ++ unsigned int reserved_1 : 3; /* [10..8] */ ++ unsigned int reserved_2 : 21; /* [31..11] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_com_lock_info; ++ ++/* Define the union kl_com_lock_status */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_com_lock_stat : 8; /* [7..0] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_com_lock_status; ++ ++/* Define the union kl_com_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_com_start : 1; /* [0] */ ++ unsigned int kl_com_level_sel : 3; /* [3..1] */ ++ unsigned int kl_com_alg_sel : 2; /* [5..4] */ ++ unsigned int reserved_0 : 2; /* [7..6] */ ++ unsigned int kl_com_key_size : 1; /* [8] */ ++ unsigned int kl_com_rsv_1 : 1; /* [9] */ ++ unsigned int reserved_1 : 22; /* [31..10] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_com_ctrl; ++ ++/* Define the union kl_com_status */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_com_rk_rdy : 1; /* [0] */ ++ unsigned int kl_com_lv1_rdy : 1; /* [1] */ ++ unsigned int kl_com_lv2_rdy : 1; /* [2] */ ++ unsigned int kl_com_lv3_rdy : 1; /* [3] */ ++ unsigned int reserved_0 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_com_status; ++ ++/* Define the union kl_clr_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int kl_clr_start : 1; /* [0] */ ++ unsigned int reserved_0 : 1; /* [1] */ ++ unsigned int kl_clr_key_size : 2; /* [3..2] */ ++ unsigned int reserved_1 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_clr_ctrl; ++ ++/* Define the union kl_alarm_info */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rng_crc4_alarm : 1; /* [0] */ ++ unsigned int kl_cfg_sig_alarm : 1; /* [1] */ ++ unsigned int kl_rk_tag_sig_alarm : 1; /* [2] */ ++ unsigned int kl_rk_tag_crc16_alarm : 1; /* [3] */ ++ unsigned int kl_rk_info_crc4_alarm : 1; /* [4] */ ++ unsigned int kl_sel_sig_alarm : 1; /* [5] */ ++ unsigned int kl_com_crc16_alarm : 1; /* [6] */ ++ unsigned int reserved_0 : 17; /* [23..7] */ ++ unsigned int cm_core_alarm : 1; /* [24] */ ++ unsigned int reserved_1 : 7; /* [31..25] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} kl_alarm_info; ++ ++#endif /* KLAD_REG_DEFINE_H */ +diff --git a/product/security_subsys/klad/src/arch/hal/v100/rkp/hal_rkp.c b/product/security_subsys/klad/src/arch/hal/v100/rkp/hal_rkp.c +new file mode 100644 +index 0000000..a56625a +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/hal/v100/rkp/hal_rkp.c +@@ -0,0 +1,357 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "hal_rkp.h" ++#include "klad_cfg.h" ++#include "rkp_reg_define.h" ++#include "ot_debug_klad.h" ++#include "ot_common_klad.h" ++#include "drv_lib.h" ++#include "klad_reg_base.h" ++#include "drv_osal_init.h" ++#include "securec.h" ++ ++#define RKP_OP_TIMEOUT 6000 ++ ++typedef enum { ++ RKP_STATIC_SEL_REE = 0x0, ++ RKP_STATIC_SEL_TEE, ++} rkp_static_sel; ++ ++typedef enum { ++ RKP_KEY_SEL_OEM0 = 0x0, ++ RKP_KEY_SEL_OEM1, ++ RKP_KEY_SEL_OEM2, ++ RKP_KEY_SEL_OEM3, ++ RKP_KEY_SEL_VENDOR, ++} rkp_key_sel; ++ ++typedef struct { ++ td_u32 owner_id; ++ rkp_static_sel static_sel; ++ rkp_key_sel key_sel; ++} rkp_materials; ++ ++#ifdef RKP_INT_SUPPORT ++typedef struct { ++ td_bool done; ++ KLAD_QUEUE_HEAD queue; ++} rkp_hard_context; ++ ++static rkp_hard_context g_rkp_ctx; ++#endif ++ ++ ++static td_u8 *g_rkp_reg_base = TD_NULL; ++ ++#define check_rkp_reg_base_fail_return() \ ++ do { \ ++ if (g_rkp_reg_base == TD_NULL) { \ ++ ot_klad_error("rkp isn't init\n"); \ ++ return OT_ERR_KLAD_NOT_INIT; \ ++ } \ ++ } while (0) ++ ++static inline td_u32 _rkp_read(td_u32 offset) ++{ ++ td_u32 val; ++ val = klad_read(g_rkp_reg_base + (offset)); ++ ot_klad_info(" offset %04x, read %08x\n", offset, val); ++ return val; ++} ++ ++static inline td_void _rkp_write(td_u32 offset, td_u32 data) ++{ ++ ot_klad_info("offset %04x, write %08x\n", offset, data); ++ klad_write(g_rkp_reg_base + (offset), data); ++} ++ ++#ifdef RKP_INT_SUPPORT ++static td_s32 _rkp_queue_callback_func(const td_void *param) ++{ ++ return (td_s32) *(td_bool *)param; ++} ++ ++static td_void _rkp_print_int_status(td_void) ++{ ++ rkp_int_en int_en; ++ rkp_int_raw int_raw; ++ rkp_int int_sta; ++ ++ int_en.u32 = _rkp_read(RKP_INT_EN); ++ ot_klad_error("klad int enable %08x\n", int_en.u32); ++ ++ int_raw.u32 = _rkp_read(RKP_INT_RAW); ++ ot_klad_error("klad int raw %08x\n", int_raw.u32); ++ ++ int_sta.u32 = _rkp_read(RKP_INT); ++ ot_klad_error("klad int status %08x\n", int_sta.u32); ++} ++ ++static td_void _rkp_enable_int(td_bool is_int) ++{ ++ rkp_int_en int_en; ++ int_en.u32 = 0; ++ int_en.bits.rkp_int_en = is_int; ++ _rkp_write(RKP_INT_EN, int_en.u32); ++} ++ ++static td_void _rkp_clean_interrupt(td_void) ++{ ++ rkp_int_raw int_raw; ++ ++ int_raw.u32 = 0; ++ int_raw.bits.rkp_int_raw = 1; ++ _rkp_write(RKP_INT_RAW, int_raw.u32); ++} ++ ++static td_s32 _rkp_interrupt_isr(td_s32 irq, td_void *dev_id) ++{ ++ ot_unused(dev_id); ++ ++ _rkp_clean_interrupt(); ++ ++ if (g_rkp_ctx.done == TD_FALSE) { ++ g_rkp_ctx.done = TD_TRUE; ++ klad_queue_wait_up(&g_rkp_ctx.queue); ++ } ++ ++ return KLAD_IRQ_HANDLED; ++} ++ ++static td_s32 _rkp_interrupt_init(td_void) ++{ ++ td_s32 ret; ++ ++ (td_void)memset_s(&g_rkp_ctx, sizeof(g_rkp_ctx), 0, sizeof(g_rkp_ctx)); ++ ++ _rkp_enable_int(TD_TRUE); ++ ++ /* the history of interception may trigger the system to ++ * call the irq function before initialization ++ * when register interrupt, this will cause a system abort. */ ++ _rkp_clean_interrupt(); ++ ++ ret = klad_request_irq(ot_klad_get_rkp_irq(), _rkp_interrupt_isr, RKP_INT_NAME); ++ ot_klad_func_fail_return(klad_request_irq, ret != TD_SUCCESS, OT_ERR_KLAD_FAILED_OPERATE); ++ ++ klad_queue_init(&g_rkp_ctx.queue); ++ ++ return ret; ++} ++ ++static td_void _rkp_interrupt_deinit(td_void) ++{ ++ klad_queue_destroy(&g_rkp_ctx.queue); ++ ++ klad_free_irq(ot_klad_get_rkp_irq(), RKP_INT_NAME); ++ ++ _rkp_enable_int(TD_FALSE); ++} ++#endif ++ ++static td_s32 _rkp_check_error(td_void) ++{ ++ rkp_error error; ++ ++ error.u32 = _rkp_read(RKP_ERROR); ++ if (error.u32 != 0) { ++ ot_klad_error("read RKP_ERROR failed 0x%08X\n", error.u32); ++ ot_klad_error("read RKP_DBG_STAT %08x\n", _rkp_read(RKP_DBG_STAT)); ++ return OT_ERR_KLAD_FAILED_OPERATE; ++ } ++ return TD_SUCCESS; ++} ++ ++static td_void _rkp_set_sw_reg(td_u32 sw_reg) ++{ ++ _rkp_write(RKP_SW_REG, sw_reg); ++} ++ ++static td_void _rkp_set_sec_cfg(rkp_static_sel static_sel) ++{ ++ rkp_sec_cfg cfg; ++ ++ cfg.u32 = _rkp_read(RKP_SEC_CFG); ++ cfg.bits.rkp_calc_type = 0; /* fixed to 0, 0 - generate rootkey, 1 - generate tee_user_password */ ++ cfg.bits.rkp_static_sel = static_sel; ++ _rkp_write(RKP_SEC_CFG, cfg.u32); ++} ++ ++static td_void _rkp_startup(rkp_key_sel key_sel) ++{ ++ rkp_calc_ctrl ctrl; ++ ++ ctrl.u32 = _rkp_read(RKP_CALC_CTRL); ++ ctrl.bits.rkp_calc_start = 1; ++ ctrl.bits.rkp_rk_sel = key_sel; ++ ctrl.bits.rkp_remap = 0; /* uboot/kernel default value 0 */ ++ _rkp_write(RKP_CALC_CTRL, ctrl.u32); ++} ++ ++static td_s32 _rkp_wait_idle(void) ++{ ++ td_u32 time_out = RKP_OP_TIMEOUT; ++ rkp_calc_ctrl ctrl; ++ ++ while (--time_out) { ++ ctrl.u32 = _rkp_read(RKP_CALC_CTRL); ++ if (ctrl.bits.rkp_calc_start == 0x0) { ++ break; ++ } ++ klad_udelay(1); ++ } ++ ++ if (time_out == 0) { ++ ot_klad_error("rkp wait op timeout!"); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 _rkp_wait_op_finish(void) ++{ ++ td_s32 ret; ++#ifdef RKP_INT_SUPPORT ++ ret = klad_queue_wait_timeout(&g_rkp_ctx.queue, ++ _rkp_queue_callback_func, &g_rkp_ctx.done, RKP_OP_TIMEOUT); ++ if (ret == 0) { ++ ot_klad_error("Failed call klad_queue_wait_timeout\n"); ++ _rkp_print_int_status(); ++ return OT_ERR_KLAD_TIMEOUT; ++ } ++#else ++ ret = _rkp_wait_idle(); ++ ot_klad_func_fail_return(_rkp_wait_idle, ret != TD_SUCCESS, ret); ++#endif ++ ++ ret = _rkp_check_error(); ++ ot_klad_func_fail_return(_rkp_check_error, ret != TD_SUCCESS, ret); ++ ++ return TD_SUCCESS; ++} ++ ++/* config rkp derived material */ ++static td_s32 drv_rkp_cfg_materials(rkp_materials *materials, const ot_klad_rootkey_attr *key_attr) ++{ ++ (td_void)memset_s(materials, sizeof(rkp_materials), 0, sizeof(rkp_materials)); ++ ++ materials->owner_id = key_attr->owner_id; ++ ++ switch (key_attr->key_sel) { ++ case OT_KLAD_ROOTKEY_SEL_OEM0: ++ materials->key_sel = RKP_KEY_SEL_OEM0; ++ break; ++ case OT_KLAD_ROOTKEY_SEL_OEM1: ++ materials->key_sel = RKP_KEY_SEL_OEM1; ++ break; ++ case OT_KLAD_ROOTKEY_SEL_OEM2: ++ materials->key_sel = RKP_KEY_SEL_OEM2; ++ break; ++ case OT_KLAD_ROOTKEY_SEL_OEM3: ++ materials->key_sel = RKP_KEY_SEL_OEM3; ++ break; ++ case OT_KLAD_ROOTKEY_SEL_VENDOR: ++ materials->key_sel = RKP_KEY_SEL_VENDOR; ++ break; ++ default: ++ ot_klad_error("Invalid rootkey sel %d\n", key_attr->key_sel); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ ++ if (key_attr->key_secure == OT_KLAD_ROOTKEY_SEC_REE) { ++ materials->static_sel = RKP_STATIC_SEL_REE; ++ } else if (klad_is_secure_cpu() && key_attr->key_secure == OT_KLAD_ROOTKEY_SEC_TEE) { ++ materials->static_sel = RKP_STATIC_SEL_TEE; ++ } else { ++ ot_klad_error("Invalid rootkey secure %d\n", key_attr->key_secure); ++ return OT_ERR_KLAD_INVALID_PARAM; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_rkp_init(td_void) ++{ ++ g_rkp_reg_base = klad_ioremap_nocache(RKP_REG_BASE_ADDR_PHY, RKP_REG_BASE_ADDR_SIZE); ++ if (g_rkp_reg_base == TD_NULL) { ++ ot_klad_error("ERROR: rkp ioremap with nocache failed!!\n"); ++ return OT_ERR_KLAD_FAILED_MEM; ++ } ++ ++#ifdef RKP_INT_SUPPORT ++ if (_rkp_interrupt_init() != TD_SUCCESS) { ++ ot_klad_error("Failed call _rkp_interrupt_init\n"); ++ klad_iounmap(g_rkp_reg_base, HKL_REG_BASE_ADDR_SIZE); ++ g_rkp_reg_base = TD_NULL; ++ return OT_ERR_KLAD_FAILED_INIT; ++ } ++#endif ++ ++ return TD_SUCCESS; ++} ++ ++td_void hal_rkp_deinit(td_void) ++{ ++#ifdef RKP_INT_SUPPORT ++ _rkp_interrupt_deinit(); ++#endif ++ ++ if (g_rkp_reg_base != TD_NULL) { ++ klad_iounmap(g_rkp_reg_base, RKP_REG_BASE_ADDR_SIZE); ++ g_rkp_reg_base = TD_NULL; ++ } ++} ++ ++/* param: sw_reg, key_sel, static_sel, rootkey or user password */ ++td_s32 hal_rkp_startup(const ot_klad_rootkey_attr *key_attr) ++{ ++ td_s32 ret; ++ rkp_materials materials; ++ ++ ot_klad_enter(); ++ ++ check_rkp_reg_base_fail_return(); ++ ++ /* config rkp derived material */ ++ ret = drv_rkp_cfg_materials(&materials, key_attr); ++ ot_klad_func_fail_return(drv_rkp_cfg_materials, ret != TD_SUCCESS, ret); ++ ++ /* step 1. wait rkp control idle */ ++ ret = _rkp_wait_idle(); ++ ot_klad_func_fail_return(_rkp_wait_idle, ret != TD_SUCCESS, ret); ++ ++ /* step 2. select static value */ ++ _rkp_set_sec_cfg(materials.static_sel); ++ ++ /* step 3. set derived material: sw_reg */ ++ _rkp_set_sw_reg(materials.owner_id); ++ ++ /* step 4. select otp key & startup rkp control */ ++ _rkp_startup(materials.key_sel); ++ ++ /* step 5. wait rkp control operate finish */ ++ ret = _rkp_wait_op_finish(); ++ ot_klad_func_fail_return(_rkp_wait_op_finish, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return ret; ++} +diff --git a/product/security_subsys/klad/src/arch/hal/v100/rkp/hal_rkp.h b/product/security_subsys/klad/src/arch/hal/v100/rkp/hal_rkp.h +new file mode 100644 +index 0000000..fb5d829 +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/hal/v100/rkp/hal_rkp.h +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HAL_RKP_H ++#define HAL_RKP_H ++ ++#include "ot_common_klad.h" ++ ++td_s32 hal_rkp_init(td_void); ++ ++td_void hal_rkp_deinit(td_void); ++ ++td_s32 hal_rkp_startup(const ot_klad_rootkey_attr *key_attr); ++ ++#endif /* HAL_RKP_H */ +diff --git a/product/security_subsys/klad/src/arch/hal/v100/rkp/rkp_reg_define.h b/product/security_subsys/klad/src/arch/hal/v100/rkp/rkp_reg_define.h +new file mode 100644 +index 0000000..4c60ddf +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/hal/v100/rkp/rkp_reg_define.h +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef RKP_REG_DEFINE_H ++#define RKP_REG_DEFINE_H ++ ++/* Define the union rkp_init_stat */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int oem_jtag_deob_done : 1; /* [0] */ ++ unsigned int ot_rk_deob_done : 1; /* [1] */ ++ unsigned int func_jtag_kdf_done : 1; /* [2] */ ++ unsigned int soc_jtag_kdf_done : 1; /* [3] */ ++ unsigned int npu_jtag_kdf_done : 1; /* [4] */ ++ unsigned int tee_prv_kdf_done : 1; /* [5] */ ++ unsigned int reserved_0 : 26; /* [31..6] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_init_stat; ++ ++/* Define the union rkp_dbg_stat */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int oem_rk_vld : 4; /* [3..0] */ ++ unsigned int reserved_0 : 4; /* [7..4] */ ++ unsigned int ot_rk_vld : 1; /* [8] */ ++ unsigned int reserved_1 : 7; /* [15..9] */ ++ unsigned int vmask_busy : 1; /* [16] */ ++ unsigned int deob_busy : 1; /* [17] */ ++ unsigned int kdf_busy : 1; /* [18] */ ++ unsigned int reserved_2 : 13; /* [31..19] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_dbg_stat; ++ ++/* Define the union rkp_state */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_init_rdy : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_state; ++ ++/* Define the union rkp_power_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_power_en : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_power_en; ++ ++/* Define the union rkp_alarm_info */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_crc4_alarm : 1; /* [0] */ ++ unsigned int rkp_fsm_alarm : 1; /* [1] */ ++ unsigned int rkp_core_alarm : 1; /* [2] */ ++ unsigned int reserved_0 : 29; /* [31..3] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_alarm_info; ++ ++/* Define the union rkp_int_en */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_int_en : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_int_en; ++ ++/* Define the union rkp_int_raw */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_int_raw : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_int_raw; ++ ++/* Define the union rkp_int */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_int : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_int; ++ ++/* Define the union rkp_sec_cfg */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_calc_type : 1; /* [0] */ ++ unsigned int reserved_0 : 3; /* [3..1] */ ++ unsigned int rkp_static_sel : 1; /* [4] */ ++ unsigned int reserved_1 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_sec_cfg; ++ ++/* Define the union rkp_calc_ctrl */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_calc_start : 1; /* [0] */ ++ unsigned int reserved_0 : 3; /* [3..1] */ ++ unsigned int rkp_rk_sel : 3; /* [6..4] */ ++ unsigned int reserved_1 : 1; /* [7] */ ++ unsigned int rkp_remap : 1; /* [8] */ ++ unsigned int reserved_2 : 23; /* [31..9] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_calc_ctrl; ++ ++/* Define the union rkp_error */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rk_invld_err : 1; /* [0] */ ++ unsigned int kl_busy_err : 1; /* [1] */ ++ unsigned int kl_access_err : 1; /* [2] */ ++ unsigned int reserved_0 : 29; /* [31..3] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_error; ++ ++/* Define the union rkp_crc */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int rkp_vmask_crc : 8; /* [7..0] */ ++ unsigned int rkp_deob_crc : 8; /* [15..8] */ ++ unsigned int rkp_kdf_crc : 8; /* [23..16] */ ++ unsigned int reserved_0 : 8; /* [31..24] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} rkp_crc; ++ ++#endif /* RKP_REG_DEFINE_H */ ++ +diff --git a/product/security_subsys/klad/src/arch/ss928v100/build.mak b/product/security_subsys/klad/src/arch/ss928v100/build.mak +new file mode 100644 +index 0000000..5845259 +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/ss928v100/build.mak +@@ -0,0 +1,7 @@ ++ ++OT_KLAD_VERSION := v100 ++ ++# KLAD_SECURE_CPU: force cpu to tee ++# KLAD_SWITCH_CPU: switch ree or tee cpu ++# else default ree cpu ++KLAD_CFLAGS += -DKLAD_SWITCH_CPU +diff --git a/product/security_subsys/klad/src/arch/ss928v100/klad_cfg.h b/product/security_subsys/klad/src/arch/ss928v100/klad_cfg.h +new file mode 100644 +index 0000000..de7f199 +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/ss928v100/klad_cfg.h +@@ -0,0 +1,48 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef KLAD_CFG_H ++#define KLAD_CFG_H ++ ++/* klad support sm4 ++ * #define KLAD_SM4_SUPPORT ++ */ ++/* klad software handle number */ ++#define KLAD_MAX_SW_HANDLE 16 ++ ++/* klad session level */ ++#define KLAD_MAX_SESSION_LEVEL 1 ++ ++/* keyslot hardware handle number */ ++#define KEYSLOT_MAX_HANDLE 16 ++ ++/* logical module klad has klad route numbers */ ++#define KLAD_ROUTE_NUM 2 ++ ++/* logical module klad support interrupt ++ * #define KLAD_INT_SUPPORT ++ */ ++/* logical module rkp support interrupt ++ * #define RKP_INT_SUPPORT ++ */ ++/* interrupt klad number, designated by RKP_INT_RAW.kl_int_num */ ++#define KLAD_CLR_ROUTE 0x02 ++#define KLAD_COM_ROUTE 0x10 ++ ++#endif /* KLAD_CFG_H */ +diff --git a/product/security_subsys/klad/src/arch/ss928v100/klad_reg_base.h b/product/security_subsys/klad/src/arch/ss928v100/klad_reg_base.h +new file mode 100644 +index 0000000..e52cf94 +--- /dev/null ++++ b/product/security_subsys/klad/src/arch/ss928v100/klad_reg_base.h +@@ -0,0 +1,58 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef KLAD_REG_BASE_H ++#define KLAD_REG_BASE_H ++ ++/* rkp register base addr and offset */ ++#define RKP_REG_BASE_ADDR_PHY 0x10111000 ++#define RKP_REG_BASE_ADDR_SIZE 0x1000 ++#define RKP_DBG_STAT 0x4 ++#define RKP_INT_EN 0x50 ++#define RKP_INT_RAW 0x54 ++#define RKP_INT 0x58 ++#define RKP_SEC_CFG 0x74 ++#define RKP_SW_REG 0x78 ++#define RKP_CALC_CTRL 0x7C ++#define RKP_ERROR 0x80 ++ ++/* klad register base addr and offset */ ++#define HKL_REG_BASE_ADDR_PHY 0x10110000 ++#define HKL_REG_BASE_ADDR_SIZE 0x1000 ++#define KL_DATA_IN_0 0x00 ++#define KL_DATA_IN_1 0x04 ++#define KL_DATA_IN_2 0x08 ++#define KL_DATA_IN_3 0x0C ++#define KL_KEY_ADDR 0x10 ++#define KL_KEY_CFG 0x14 ++#define KL_KEY_SEC_CFG 0x18 ++#define KL_STATE 0x30 ++#define KL_ERROR 0x38 ++#define KC_ERROR 0x3C ++#define KL_INT_EN 0x40 ++#define KL_INT_RAW 0x44 ++#define KL_INT 0x48 ++#define KL_LOCK_CTRL 0x74 ++#define KL_UNLOCK_CTRL 0x78 ++#define KL_COM_LOCK_INFO 0x7C ++#define KL_COM_LOCK_STATUS 0x80 ++#define KL_COM_CTRL 0x84 ++#define KL_CLR_CTRL 0x438 ++ ++#endif /* KLAD_REG_BASE_H */ +diff --git a/product/security_subsys/klad/src/include/drv_ioctl_klad.h b/product/security_subsys/klad/src/include/drv_ioctl_klad.h +new file mode 100644 +index 0000000..9beebf8 +--- /dev/null ++++ b/product/security_subsys/klad/src/include/drv_ioctl_klad.h +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_IOCTL_KLAD_H ++#define DRV_IOCTL_KLAD_H ++ ++#include ++#include "ot_common_klad.h" ++#include "ot_common.h" ++#include "mkp_ioctl.h" ++#include "dev_ext.h" ++ ++typedef struct { ++ td_handle klad; ++} klad_ctl_handle; ++ ++typedef struct { ++ td_handle klad; ++ td_handle target; ++} klad_ctl_target; ++ ++typedef struct { ++ td_handle klad; ++ ot_klad_attr attr; ++} klad_ctl_attr; ++ ++typedef struct { ++ td_handle klad; ++ ot_klad_session_key key; ++} klad_ctl_session_key; ++ ++typedef struct { ++ td_handle klad; ++ ot_klad_content_key key; ++} klad_ctl_content_key; ++ ++typedef struct { ++ td_handle klad; ++ ot_klad_clear_key key; ++} klad_ctl_clear_key; ++ ++typedef enum { ++ KLAD_IOC_NR_CREATE = 0, ++ KLAD_IOC_NR_DESTROY, ++ KLAD_IOC_NR_ATTACH, ++ KLAD_IOC_NR_DETACH, ++ KLAD_IOC_NR_SET_ATTR, ++ KLAD_IOC_NR_GET_ATTR, ++ KLAD_IOC_NR_SESSION_KEY, ++ KLAD_IOC_NR_CONTENT_KEY, ++ KLAD_IOC_NR_CLEAR_KEY, ++ KLAD_IOC_NR_BUTT, ++} klad_ioc_nr; ++ ++#define CMD_KLAD_CREATE_HANDLE _IOWR(IOC_TYPE_KLAD, KLAD_IOC_NR_CREATE, klad_ctl_handle) ++#define CMD_KLAD_DESTROY_HANDLE _IOW (IOC_TYPE_KLAD, KLAD_IOC_NR_DESTROY, klad_ctl_handle) ++#define CMD_KLAD_ATTACH_TARGET _IOW (IOC_TYPE_KLAD, KLAD_IOC_NR_ATTACH, klad_ctl_target) ++#define CMD_KLAD_DETACH_TARGET _IOW (IOC_TYPE_KLAD, KLAD_IOC_NR_DETACH, klad_ctl_target) ++#define CMD_KLAD_SET_ATTR _IOW (IOC_TYPE_KLAD, KLAD_IOC_NR_SET_ATTR, klad_ctl_attr) ++#define CMD_KLAD_GET_ATTR _IOWR(IOC_TYPE_KLAD, KLAD_IOC_NR_GET_ATTR, klad_ctl_attr) ++#define CMD_KLAD_SESSION_KEY _IOW (IOC_TYPE_KLAD, KLAD_IOC_NR_SESSION_KEY, klad_ctl_session_key) ++#define CMD_KLAD_CONTENT_KEY _IOW (IOC_TYPE_KLAD, KLAD_IOC_NR_CONTENT_KEY, klad_ctl_content_key) ++#define CMD_KLAD_CLEAR_KEY _IOW (IOC_TYPE_KLAD, KLAD_IOC_NR_CLEAR_KEY, klad_ctl_clear_key) ++#define CMD_KLAD_MAX_NUM KLAD_IOC_NR_BUTT ++ ++#endif /* DRV_IOCTL_KLAD_H */ +diff --git a/product/security_subsys/klad/src/include/ot_debug_klad.h b/product/security_subsys/klad/src/include/ot_debug_klad.h +new file mode 100644 +index 0000000..11e64cc +--- /dev/null ++++ b/product/security_subsys/klad/src/include/ot_debug_klad.h +@@ -0,0 +1,91 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_DEBUG_KLAD_H ++#define OT_DEBUG_KLAD_H ++ ++#include "ot_type.h" ++#include "ot_debug.h" ++ ++#define OT_KLAD_LVL_ERR 0 ++#define OT_KLAD_LVL_WARN 1 ++#define OT_KLAD_LVL_INFO 2 ++#define OT_KLAD_LVL_LOG 3 ++ ++#define klad_trace(level, msg, fmt, ...) \ ++ do { \ ++ if (OT_KLAD_DEBUG >= level) { \ ++ OT_PRINT("[%4s Line:%04d Func:%s] "fmt, msg, __LINE__, __FUNCTION__, ##__VA_ARGS__); \ ++ } \ ++ } while (0) ++ ++#define ot_klad_error(fmt, ...) klad_trace(OT_KLAD_LVL_ERR, "ERR", fmt, ##__VA_ARGS__) ++#if OT_KLAD_DEBUG ++#define ot_klad_warn(fmt, ...) klad_trace(OT_KLAD_LVL_WARN, "WARN", fmt, ##__VA_ARGS__) ++#define ot_klad_info(fmt, ...) klad_trace(OT_KLAD_LVL_INFO, "INFO", fmt, ##__VA_ARGS__) ++#define ot_klad_enter() klad_trace(OT_KLAD_LVL_LOG, "LOG", "enter--->\n") ++#define ot_klad_exit() klad_trace(OT_KLAD_LVL_LOG, "LOG", "exit <---\n") ++#else ++#define ot_klad_warn(fmt, ...) ++#define ot_klad_info(fmt, ...) ++#define ot_klad_enter() ++#define ot_klad_exit() ++#endif ++ ++#define ot_klad_func_fail_return(_func, _formula, _errno) \ ++ do { \ ++ if (_formula) { \ ++ ot_klad_error("call %s failed, ret = 0x%08X\n", # _func, _errno); \ ++ return _errno; \ ++ } \ ++ } while (0) ++ ++#define ot_klad_formula_fail_return(_formula, _errno) \ ++ do { \ ++ if (_formula) { \ ++ ot_klad_error("%s is invalid, ret = 0x%08X\n", # _formula, _errno); \ ++ return _errno; \ ++ } \ ++ } while (0) ++ ++#define chk_handle_modid(handle, modid) \ ++ do { \ ++ if (td_handle_get_modid(handle) != (modid)) { \ ++ ot_klad_error("invalid handle 0x%x\n", handle); \ ++ return OT_ERR_KLAD_INVALID_HANDLE; \ ++ } \ ++ } while (0) ++ ++#define chk_handle_private_data(handle, data) \ ++ do { \ ++ if (td_handle_get_private_data(handle) != (data)) { \ ++ ot_klad_error("invalid handle 0x%x\n", handle); \ ++ return OT_ERR_KLAD_INVALID_HANDLE; \ ++ } \ ++ } while (0) ++ ++#define chk_handle_chnid(handle, threshold) \ ++ do { \ ++ if (td_handle_get_chnid(handle) >= (threshold)) { \ ++ ot_klad_error("invalid handle 0x%x\n", handle); \ ++ return OT_ERR_KLAD_INVALID_HANDLE; \ ++ } \ ++ } while (0) ++ ++#endif /* OT_DEBUG_KLAD_H */ +diff --git a/product/security_subsys/klad/src/mkp/build.mak b/product/security_subsys/klad/src/mkp/build.mak +new file mode 100644 +index 0000000..c109062 +--- /dev/null ++++ b/product/security_subsys/klad/src/mkp/build.mak +@@ -0,0 +1,6 @@ ++ ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/mkp/ ++ ++KLAD_DRV_OBJS += mkp/drv_klad_intf.o ++KLAD_DRV_OBJS += mkp/kapi_klad.o ++KLAD_DRV_OBJS += mkp/drv_klad_sw_utils.o +diff --git a/product/security_subsys/klad/src/mkp/drv_klad_intf.c b/product/security_subsys/klad/src/mkp/drv_klad_intf.c +new file mode 100644 +index 0000000..effcf7a +--- /dev/null ++++ b/product/security_subsys/klad/src/mkp/drv_klad_intf.c +@@ -0,0 +1,246 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_klad_intf.h" ++#include "ot_debug_klad.h" ++#include "drv_ioctl_klad.h" ++#include "kapi_klad.h" ++#include "securec.h" ++ ++typedef struct { ++ const char *name; ++ td_s32 (*func)(td_void *); ++ td_u32 cmd; ++} intf_klad_func; ++ ++static td_s32 intf_klad_create(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_handle *handle = (klad_ctl_handle *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(handle == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_create(&handle->klad); ++ ot_klad_func_fail_return(kapi_klad_create, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_destroy(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_handle *handle = (klad_ctl_handle *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(handle == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_destroy(handle->klad); ++ ot_klad_func_fail_return(kapi_klad_destroy, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_attach(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_target *ctl_target = (klad_ctl_target *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(ctl_target == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_attach(ctl_target->klad, ctl_target->target); ++ ot_klad_func_fail_return(kapi_klad_attach, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_detach(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_target *ctl_target = (klad_ctl_target *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(ctl_target == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_detach(ctl_target->klad, ctl_target->target); ++ ot_klad_func_fail_return(kapi_klad_detach, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_set_attr(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_attr *ctl_attr = (klad_ctl_attr *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(ctl_attr == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_set_attr(ctl_attr->klad, &ctl_attr->attr); ++ ot_klad_func_fail_return(kapi_klad_set_attr, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_get_attr(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_attr *ctl_attr = (klad_ctl_attr *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(ctl_attr == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_get_attr(ctl_attr->klad, &ctl_attr->attr); ++ ot_klad_func_fail_return(kapi_klad_get_attr, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_set_session_key(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_session_key *session_key = (klad_ctl_session_key *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(session_key == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_set_session_key(session_key->klad, &session_key->key); ++ ot_klad_func_fail_return(kapi_klad_set_session_key, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(&session_key->key, sizeof(ot_klad_session_key), 0, sizeof(ot_klad_session_key)); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_set_content_key(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_content_key *content_key = (klad_ctl_content_key *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(content_key == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_set_content_key(content_key->klad, &content_key->key); ++ ot_klad_func_fail_return(kapi_klad_set_content_key, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(&content_key->key, sizeof(ot_klad_content_key), 0, sizeof(ot_klad_content_key)); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 intf_klad_set_clear_key(td_void *argp) ++{ ++ td_s32 ret; ++ klad_ctl_clear_key *clear_key = (klad_ctl_clear_key *)argp; ++ ++ ot_klad_enter(); ++ ++ ot_klad_formula_fail_return(clear_key == TD_NULL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ ret = kapi_klad_set_clear_key(clear_key->klad, &clear_key->key); ++ ot_klad_func_fail_return(kapi_klad_set_clear_key, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(&clear_key->key, sizeof(ot_klad_clear_key), 0, sizeof(ot_klad_clear_key)); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++static intf_klad_func g_klad_func[] = { ++ {"KladCreate", intf_klad_create, CMD_KLAD_CREATE_HANDLE}, ++ {"KladDestroy", intf_klad_destroy, CMD_KLAD_DESTROY_HANDLE}, ++ {"KladAttach", intf_klad_attach, CMD_KLAD_ATTACH_TARGET}, ++ {"KladDetach", intf_klad_detach, CMD_KLAD_DETACH_TARGET}, ++ {"KladSetAttr", intf_klad_set_attr, CMD_KLAD_SET_ATTR}, ++ {"KladGetAttr", intf_klad_get_attr, CMD_KLAD_GET_ATTR}, ++ {"KladSetSessionKey", intf_klad_set_session_key, CMD_KLAD_SESSION_KEY}, ++ {"KladSetContentKey", intf_klad_set_content_key, CMD_KLAD_CONTENT_KEY}, ++ {"KladSetClearKey", intf_klad_set_clear_key, CMD_KLAD_CLEAR_KEY}, ++}; ++ ++td_s32 intf_klad_ioctl(td_u32 cmd, td_void *param) ++{ ++ td_s32 ret; ++ td_u32 nr; ++ ++ ot_klad_enter(); ++ ++ nr = _IOC_NR(cmd); ++ ++ ot_klad_formula_fail_return(param == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ot_klad_formula_fail_return(nr >= CMD_KLAD_MAX_NUM, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(cmd != g_klad_func[nr].cmd, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(g_klad_func[nr].func == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ ot_klad_info("cmd %x, nr %d, dir %x\n", cmd, nr, _IOC_DIR(cmd)); ++ ++ ret = g_klad_func[nr].func(param); ++ if (ret != TD_SUCCESS) { ++ ot_klad_error("call intf func failed, cmd %x, nr %d\n", cmd, nr); ++ return ret; ++ } ++ ++ ot_klad_exit(); ++ ++ return ret; ++} ++ ++td_s32 klad_entry(td_void) ++{ ++ td_s32 ret; ++ ++ ret = kapi_klad_init(); ++ ot_klad_func_fail_return(kapi_klad_init, ret != TD_SUCCESS, ret); ++ ++ return TD_SUCCESS; ++} ++ ++td_void klad_exit(td_void) ++{ ++ kapi_klad_deinit(); ++} +diff --git a/product/security_subsys/klad/src/mkp/drv_klad_intf.h b/product/security_subsys/klad/src/mkp/drv_klad_intf.h +new file mode 100644 +index 0000000..761d860 +--- /dev/null ++++ b/product/security_subsys/klad/src/mkp/drv_klad_intf.h +@@ -0,0 +1,31 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_KLAD_INTF_H ++#define DRV_KLAD_INTF_H ++ ++#include "ot_type.h" ++ ++td_s32 intf_klad_ioctl(td_u32 cmd, td_void *param); ++ ++td_s32 klad_entry(td_void); ++ ++td_void klad_exit(td_void); ++ ++#endif /* DRV_KLAD_INTF_H */ +diff --git a/product/security_subsys/klad/src/mkp/drv_klad_sw_utils.c b/product/security_subsys/klad/src/mkp/drv_klad_sw_utils.c +new file mode 100644 +index 0000000..83fafba +--- /dev/null ++++ b/product/security_subsys/klad/src/mkp/drv_klad_sw_utils.c +@@ -0,0 +1,409 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_klad_sw_utils.h" ++#include "hal_klad.h" ++#include "ot_debug_klad.h" ++#include "klad_cfg.h" ++#include "klad_reg_base.h" ++#include "securec.h" ++#include "drv_osal_init.h" ++#include "cipher_ext.h" ++ ++#define KLAD_INVALID_VAL 0xffffffff ++ ++static klad_sw_mgmt g_klad_sw_mgmt[KLAD_MAX_SW_HANDLE]; ++ ++static klad_sw_mgmt *_get_klad_sw_mgmt(td_u32 id) ++{ ++ return &g_klad_sw_mgmt[id]; ++} ++ ++static td_s32 klad_get_idle_handle(td_handle *klad) ++{ ++ td_u32 i; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ for (i = 0; i < KLAD_MAX_SW_HANDLE; i++) { ++ mgmt = _get_klad_sw_mgmt(i); ++ if (mgmt->open == TD_FALSE) { ++ *klad = i; ++ return TD_SUCCESS; ++ } ++ } ++ ++ ot_klad_error("klad is busy!\n"); ++ return OT_ERR_KLAD_DEVICE_BUSY; ++} ++ ++#define klad_chk_handle(handle) \ ++ do { \ ++ chk_handle_modid(handle, OT_ID_KLAD); \ ++ chk_handle_private_data(handle, OT_PRIVATE_ID_KLAD); \ ++ chk_handle_chnid(handle, KLAD_MAX_SW_HANDLE); \ ++ } while (0) ++ ++td_s32 klad_sw_init(td_void) ++{ ++ td_s32 ret; ++ td_u32 i; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ ret = hal_klad_init(); ++ ot_klad_func_fail_return(hal_klad_init, ret != TD_SUCCESS, ret); ++ ++ (td_void)memset_s(g_klad_sw_mgmt, sizeof(g_klad_sw_mgmt), 0, sizeof(g_klad_sw_mgmt)); ++ for (i = 0; i < KLAD_MAX_SW_HANDLE; i++) { ++ mgmt = _get_klad_sw_mgmt(i); ++ mgmt->klad = KLAD_INVALID_VAL; ++ mgmt->target = KLAD_INVALID_VAL; ++ mgmt->open = TD_FALSE; ++ mgmt->attach = TD_FALSE; ++ mgmt->config = TD_FALSE; ++ } ++ ++ return ret; ++} ++ ++td_void klad_sw_deinit(td_void) ++{ ++ td_u32 i; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ hal_klad_deinit(); ++ ++ (td_void)memset_s(g_klad_sw_mgmt, sizeof(g_klad_sw_mgmt), 0, sizeof(g_klad_sw_mgmt)); ++ for (i = 0; i < KLAD_MAX_SW_HANDLE; i++) { ++ mgmt = _get_klad_sw_mgmt(i); ++ mgmt->klad = KLAD_INVALID_VAL; ++ mgmt->target = KLAD_INVALID_VAL; ++ mgmt->open = TD_FALSE; ++ mgmt->attach = TD_FALSE; ++ mgmt->config = TD_FALSE; ++ } ++} ++ ++td_s32 klad_sw_create(td_handle *klad) ++{ ++ td_s32 ret; ++ td_handle id; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ ret = klad_get_idle_handle(&id); ++ ot_klad_func_fail_return(klad_get_idle_handle, ret != TD_SUCCESS, ret); ++ ++ /* open *klad handle */ ++ mgmt = _get_klad_sw_mgmt(id); ++ (td_void)memset_s(mgmt, sizeof(klad_sw_mgmt), 0, sizeof(klad_sw_mgmt)); ++ mgmt->klad = id; ++ mgmt->target = KLAD_INVALID_VAL; ++ mgmt->open = TD_TRUE; ++ mgmt->attach = TD_FALSE; ++ mgmt->config = TD_FALSE; ++ ++ *klad = td_handle_init(OT_ID_KLAD, OT_PRIVATE_ID_KLAD, id); ++ ++ ot_klad_exit(); ++ ++ return ret; ++} ++ ++td_s32 klad_sw_destroy(td_handle klad) ++{ ++ td_u32 id; ++ td_s32 ret = TD_SUCCESS; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(id); ++ ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != id, OT_ERR_KLAD_INVALID_HANDLE); ++ ot_klad_formula_fail_return(mgmt->attach != TD_FALSE, OT_ERR_KLAD_INVALID_PARAM); ++ ++ /* close *klad handle */ ++ (td_void)memset_s(mgmt, sizeof(klad_sw_mgmt), 0, sizeof(klad_sw_mgmt)); ++ mgmt->klad = KLAD_INVALID_VAL; ++ mgmt->target = KLAD_INVALID_VAL; ++ mgmt->open = TD_FALSE; ++ mgmt->attach = TD_FALSE; ++ mgmt->config = TD_FALSE; ++ ++ ot_klad_exit(); ++ ++ return ret; ++} ++ ++td_s32 klad_sw_attach(td_handle klad, td_handle target) ++{ ++ td_u32 klad_id, target_id; ++ td_s32 ret = TD_SUCCESS; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ klad_id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(klad_id); ++ ++ target_id = td_handle_get_chnid(target); ++ if (td_handle_get_modid(target) == OT_ID_KEYSLOT) { ++ ot_klad_formula_fail_return(td_handle_get_private_data(target) != OT_PRIVATE_ID_KEYSLOT, ++ OT_ERR_KLAD_INVALID_HANDLE); ++ ot_klad_formula_fail_return(target_id >= KEYSLOT_MAX_HANDLE, OT_ERR_KLAD_INVALID_HANDLE); ++ } else { ++ ot_klad_error("invalid target 0x%x\n", target); ++ return OT_ERR_KLAD_INVALID_HANDLE; ++ } ++ ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != klad_id, OT_ERR_KLAD_INVALID_PARAM); ++ ++ mgmt->target = target_id; ++ mgmt->attach = TD_TRUE; ++ ++ ot_klad_exit(); ++ return ret; ++} ++ ++td_s32 klad_sw_detach(td_handle klad, td_handle target) ++{ ++ td_u32 klad_id, target_id; ++ td_s32 ret = TD_SUCCESS; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ klad_id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(klad_id); ++ ++ target_id = td_handle_get_chnid(target); ++ if (td_handle_get_modid(target) == OT_ID_KEYSLOT) { ++ ot_klad_formula_fail_return(td_handle_get_private_data(target) != OT_PRIVATE_ID_KEYSLOT, ++ OT_ERR_KLAD_INVALID_HANDLE); ++ ot_klad_formula_fail_return(target_id >= KEYSLOT_MAX_HANDLE, OT_ERR_KLAD_INVALID_HANDLE); ++ } else { ++ ot_klad_error("invalid target 0x%x\n", target); ++ return OT_ERR_KLAD_INVALID_HANDLE; ++ } ++ ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->attach != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != klad_id, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->target != target_id, OT_ERR_KLAD_INVALID_PARAM); ++ ++ mgmt->target = KLAD_INVALID_VAL; ++ mgmt->attach = TD_FALSE; ++ ++ ot_klad_exit(); ++ return ret; ++} ++ ++td_s32 klad_sw_set_attr(td_handle klad, const ot_klad_attr *attr) ++{ ++ td_u32 id; ++ td_s32 ret = TD_SUCCESS; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ klad_common_slot *com_slot = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(id); ++ ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != id, OT_ERR_KLAD_INVALID_PARAM); ++ ++ mgmt->type = attr->klad_cfg.klad_type; ++ if (mgmt->type == OT_KLAD_TYPE_COMMON) { ++ com_slot = &mgmt->klad_slot.common_slot; ++ ret = memcpy_s(&com_slot->key_attr, sizeof(ot_klad_rootkey_attr), ++ &attr->klad_cfg.rootkey_attr, sizeof(ot_klad_rootkey_attr)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ } ++ ++ mgmt->config = TD_TRUE; ++ ++ ot_klad_exit(); ++ return ret; ++} ++ ++td_s32 klad_sw_get_attr(td_handle klad, ot_klad_attr *attr) ++{ ++ td_u32 id; ++ td_s32 ret = TD_SUCCESS; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(id); ++ ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->config != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != id, OT_ERR_KLAD_INVALID_PARAM); ++ ++ attr->klad_cfg.klad_type = mgmt->type; ++ if (mgmt->type == OT_KLAD_TYPE_COMMON) { ++ klad_common_slot *com_slot = TD_NULL; ++ com_slot = &mgmt->klad_slot.common_slot; ++ ret = memcpy_s(&attr->klad_cfg.rootkey_attr, sizeof(ot_klad_rootkey_attr), ++ &com_slot->key_attr, sizeof(ot_klad_rootkey_attr)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ } ++ ++ ot_klad_exit(); ++ return ret; ++} ++ ++td_s32 klad_sw_set_session_key(td_handle klad, const ot_klad_session_key *session_key) ++{ ++ td_u32 id; ++ td_s32 ret; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ klad_common_slot *com_slot = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(id); ++ ++ ot_klad_formula_fail_return(session_key->level >= OT_KLAD_LEVEL_BUTT, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(session_key->alg >= OT_KLAD_ALG_TYPE_BUTT, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(session_key->key_size > OT_KLAD_MAX_KEY_LEN, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->attach != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->config != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != id, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->type != OT_KLAD_TYPE_COMMON, OT_ERR_KLAD_INVALID_PARAM); ++ ++ com_slot = &mgmt->klad_slot.common_slot; ++ ++ /* session level must add 1 level by level */ ++ ot_klad_formula_fail_return(session_key->level != com_slot->klad_level, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(session_key->level + 1 > KLAD_MAX_SESSION_LEVEL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ com_slot->klad_key[com_slot->klad_level].alg = session_key->alg; ++ com_slot->klad_key[com_slot->klad_level].key_size = session_key->key_size; ++ ret = memcpy_s(com_slot->klad_key[com_slot->klad_level].key, OT_KLAD_MAX_KEY_LEN, ++ session_key->key, session_key->key_size); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ /* klad level must add 1 level by level */ ++ com_slot->klad_level++; ++ ++ ot_klad_exit(); ++ return ret; ++} ++ ++td_s32 klad_sw_set_content_key(td_handle klad, const ot_klad_content_key *content_key) ++{ ++ td_u32 id; ++ td_s32 ret; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ klad_common_slot *com_slot = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(id); ++ ++ ot_klad_formula_fail_return(content_key->alg >= OT_KLAD_ALG_TYPE_BUTT, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(content_key->key_size > OT_KLAD_MAX_KEY_LEN, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->attach != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->config != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != id, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->type != OT_KLAD_TYPE_COMMON, OT_ERR_KLAD_INVALID_PARAM); ++ ++ com_slot = &mgmt->klad_slot.common_slot; ++ ot_klad_formula_fail_return(com_slot->klad_level != KLAD_MAX_SESSION_LEVEL, OT_ERR_KLAD_INVALID_PARAM); ++ ++ com_slot->crypto_alg = content_key->crypto_alg; ++ com_slot->klad_key[com_slot->klad_level].alg = content_key->alg; ++ com_slot->klad_key[com_slot->klad_level].key_size = content_key->key_size; ++ ret = memcpy_s(com_slot->klad_key[com_slot->klad_level].key, OT_KLAD_MAX_KEY_LEN, ++ content_key->key, content_key->key_size); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ /* klad level must add content level */ ++ com_slot->klad_level++; ++ ++ ret = hal_klad_common_route_startup(mgmt->target, com_slot); ++ ot_klad_func_fail_return(hal_klad_common_route_startup, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(com_slot->klad_key, sizeof(com_slot->klad_key), 0, sizeof(com_slot->klad_key)); ++ ++ ot_klad_exit(); ++ return ret; ++} ++ ++td_s32 klad_sw_set_clear_key(td_handle klad, const ot_klad_clear_key *key) ++{ ++ td_u32 id; ++ td_s32 ret; ++ klad_sw_mgmt *mgmt = TD_NULL; ++ klad_clear_slot *clr_slot = TD_NULL; ++ ++ ot_klad_enter(); ++ ++ klad_chk_handle(klad); ++ ++ id = td_handle_get_chnid(klad); ++ mgmt = _get_klad_sw_mgmt(id); ++ ++ ot_klad_formula_fail_return(mgmt->open != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->attach != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->config != TD_TRUE, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->klad != id, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(mgmt->type != OT_KLAD_TYPE_CLEARCW, OT_ERR_KLAD_INVALID_PARAM); ++ ++ clr_slot = &mgmt->klad_slot.clear_slot; ++ ret = memcpy_s(&clr_slot->clear_key, sizeof(ot_klad_clear_key), ++ key, sizeof(ot_klad_clear_key)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ ret = hal_klad_clear_route_startup(mgmt->target, clr_slot); ++ ot_klad_func_fail_return(hal_klad_clear_route_startup, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(&clr_slot->clear_key, sizeof(ot_klad_clear_key), 0, sizeof(ot_klad_clear_key)); ++ ++ ot_klad_exit(); ++ return ret; ++} ++ +diff --git a/product/security_subsys/klad/src/mkp/drv_klad_sw_utils.h b/product/security_subsys/klad/src/mkp/drv_klad_sw_utils.h +new file mode 100644 +index 0000000..2297b5f +--- /dev/null ++++ b/product/security_subsys/klad/src/mkp/drv_klad_sw_utils.h +@@ -0,0 +1,68 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_KLAD_SW_UTILS_H ++#define DRV_KLAD_SW_UTILS_H ++ ++#include "ot_common_klad.h" ++ ++/* content key: set n stage common route klad */ ++typedef struct { ++ ot_klad_alg_type alg; /* klad algorithm */ ++ td_u32 key_size; /* klad key size */ ++ td_u8 key[OT_KLAD_MAX_KEY_LEN]; /* klad key */ ++} klad_common_key; ++ ++typedef struct { ++ td_u32 klad_level; ++ ot_klad_rootkey_attr key_attr; ++ ot_klad_crypto_alg crypto_alg; ++ klad_common_key klad_key[OT_KLAD_LEVEL_BUTT]; ++} klad_common_slot; ++ ++typedef struct { ++ ot_klad_clear_key clear_key; ++} klad_clear_slot; ++ ++typedef struct { ++ td_handle klad; ++ td_handle target; ++ td_bool open; ++ td_bool attach; ++ td_bool config; ++ ot_klad_type type; ++ union { ++ klad_common_slot common_slot; ++ klad_clear_slot clear_slot; ++ } klad_slot; ++} klad_sw_mgmt; ++ ++td_s32 klad_sw_init(td_void); ++td_void klad_sw_deinit(td_void); ++td_s32 klad_sw_create(td_handle *klad); ++td_s32 klad_sw_destroy(td_handle klad); ++td_s32 klad_sw_attach(td_handle klad, td_handle target); ++td_s32 klad_sw_detach(td_handle klad, td_handle target); ++td_s32 klad_sw_set_attr(td_handle klad, const ot_klad_attr *attr); ++td_s32 klad_sw_get_attr(td_handle klad, ot_klad_attr *attr); ++td_s32 klad_sw_set_session_key(td_handle klad, const ot_klad_session_key *key); ++td_s32 klad_sw_set_content_key(td_handle klad, const ot_klad_content_key *key); ++td_s32 klad_sw_set_clear_key(td_handle klad, const ot_klad_clear_key *key); ++ ++#endif /* DRV_KLAD_SW_UTILS_H */ +diff --git a/product/security_subsys/klad/src/mkp/kapi_klad.c b/product/security_subsys/klad/src/mkp/kapi_klad.c +new file mode 100644 +index 0000000..5895917 +--- /dev/null ++++ b/product/security_subsys/klad/src/mkp/kapi_klad.c +@@ -0,0 +1,227 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "kapi_klad.h" ++#include "drv_osal_init.h" ++#include "drv_klad_sw_utils.h" ++#include "ot_debug_klad.h" ++#include "klad_reg_base.h" ++ ++#define kapi_klad_not_init_return(init) \ ++ do { \ ++ if ((init) == TD_FALSE) { \ ++ ot_klad_error("KLAD isn't init\n"); \ ++ return OT_ERR_KLAD_NOT_INIT; \ ++ } \ ++ } while (0) ++ ++typedef struct { ++ td_bool init; ++ KLAD_MUTEX_T lock; ++} kapi_klad_mgmt; ++static kapi_klad_mgmt g_kapi_klad_mgmt; ++ ++static kapi_klad_mgmt *_get_kapi_klad_mgmt(td_void) ++{ ++ return &g_kapi_klad_mgmt; ++} ++ ++td_s32 kapi_klad_init(td_void) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = TD_NULL; ++ ++ ret = klad_sw_init(); ++ ot_klad_func_fail_return(klad_sw_init, ret != TD_SUCCESS, ret); ++ ++ mgmt = _get_kapi_klad_mgmt(); ++ mgmt->init = TD_TRUE; ++ klad_mutex_init(&mgmt->lock); ++ ++ return TD_SUCCESS; ++} ++ ++td_void kapi_klad_deinit(td_void) ++{ ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ klad_sw_deinit(); ++ ++ mgmt->init = TD_FALSE; ++ klad_mutex_destroy(&mgmt->lock); ++} ++ ++td_s32 kapi_klad_create(td_handle *klad) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ ot_klad_formula_fail_return(klad == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_create(klad); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_destroy(td_handle klad) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_destroy(klad); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_attach(td_handle klad, td_handle target) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_attach(klad, target); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_detach(td_handle klad, td_handle target) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_detach(klad, target); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_set_attr(td_handle klad, const ot_klad_attr *attr) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ ot_klad_formula_fail_return(attr == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_set_attr(klad, attr); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_get_attr(td_handle klad, ot_klad_attr *attr) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ ot_klad_formula_fail_return(attr == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_get_attr(klad, attr); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_set_session_key(td_handle klad, const ot_klad_session_key *key) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ ot_klad_formula_fail_return(key == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ /* 16 byte:logic only supports 128bit session key */ ++ ot_klad_formula_fail_return(key->key_size != 16, OT_ERR_KLAD_INVALID_PARAM); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_set_session_key(klad, key); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_set_content_key(td_handle klad, const ot_klad_content_key *key) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ ot_klad_formula_fail_return(key == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_set_content_key(klad, key); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ ++td_s32 kapi_klad_set_clear_key(td_handle klad, const ot_klad_clear_key *key) ++{ ++ td_s32 ret; ++ kapi_klad_mgmt *mgmt = _get_kapi_klad_mgmt(); ++ ++ kapi_klad_not_init_return(mgmt->init); ++ ++ ot_klad_formula_fail_return(key == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ ret = klad_sw_set_clear_key(klad, key); ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ return ret; ++} ++ +diff --git a/product/security_subsys/klad/src/mkp/kapi_klad.h b/product/security_subsys/klad/src/mkp/kapi_klad.h +new file mode 100644 +index 0000000..bdfb0bd +--- /dev/null ++++ b/product/security_subsys/klad/src/mkp/kapi_klad.h +@@ -0,0 +1,47 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef KAPI_KLAD_H ++#define KAPI_KLAD_H ++ ++#include "ot_common_klad.h" ++ ++td_s32 kapi_klad_init(td_void); ++ ++td_void kapi_klad_deinit(td_void); ++ ++td_s32 kapi_klad_create(td_handle *klad); ++ ++td_s32 kapi_klad_destroy(td_handle klad); ++ ++td_s32 kapi_klad_attach(td_handle klad, td_handle target); ++ ++td_s32 kapi_klad_detach(td_handle klad, td_handle target); ++ ++td_s32 kapi_klad_set_attr(td_handle klad, const ot_klad_attr *attr); ++ ++td_s32 kapi_klad_get_attr(td_handle klad, ot_klad_attr *attr); ++ ++td_s32 kapi_klad_set_session_key(td_handle klad, const ot_klad_session_key *key); ++ ++td_s32 kapi_klad_set_content_key(td_handle klad, const ot_klad_content_key *key); ++ ++td_s32 kapi_klad_set_clear_key(td_handle klad, const ot_klad_clear_key *key); ++ ++#endif /* KAPI_KLAD_H */ +diff --git a/product/security_subsys/klad/src/mpi/build.mak b/product/security_subsys/klad/src/mpi/build.mak +new file mode 100644 +index 0000000..64725f6 +--- /dev/null ++++ b/product/security_subsys/klad/src/mpi/build.mak +@@ -0,0 +1,3 @@ ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/mpi/ ++ ++KLAD_API_OBJS += mpi/mpi_klad.o +diff --git a/product/security_subsys/klad/src/mpi/mpi_klad.c b/product/security_subsys/klad/src/mpi/mpi_klad.c +new file mode 100644 +index 0000000..2409ccd +--- /dev/null ++++ b/product/security_subsys/klad/src/mpi/mpi_klad.c +@@ -0,0 +1,359 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "ot_mpi_klad.h" ++#include "ot_debug_klad.h" ++#include "drv_ioctl_klad.h" ++#include "securec.h" ++#include "drv_klad_intf.h" ++#include "drv_lib.h" ++ ++#define KLAD_MUTEX_T td_s32 ++#define klad_mutex_lock(x) ++#define klad_mutex_unlock(x) ++#define klad_open(name, flags, mode) ((td_void)klad_get_cpu_secure_sta(), klad_entry()) ++#define klad_close(fd) (klad_exit(), 1) ++#define klad_ioctl(fd, request, var) intf_klad_ioctl(request, var) ++ ++#define KLAD_INIT_MAX_NUM 0x7FFFFFFF ++ ++typedef struct { ++ KLAD_MUTEX_T lock; ++ td_s32 ref_count; ++ td_s32 klad_fd; ++} mpi_klad_mgmt; ++ ++static mpi_klad_mgmt g_mpi_klad_mgmt = { ++ .lock = 1, ++ .ref_count = -1, ++ .klad_fd = -1, ++}; ++ ++#define _mpi_klad_not_init_return(ref_count) \ ++ do { \ ++ if ((ref_count) < 0) { \ ++ ot_klad_error("klad init counter %d\n", ref_count); \ ++ return OT_ERR_KLAD_NOT_INIT; \ ++ } \ ++ } while (0) ++ ++static mpi_klad_mgmt *_mpi_klad_get_mgmt(td_void) ++{ ++ return &g_mpi_klad_mgmt; ++} ++ ++td_s32 ot_mpi_klad_init(td_void) ++{ ++ td_s32 ret = TD_SUCCESS; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ if (mgmt->ref_count >= 0) { ++ if (mgmt->ref_count < KLAD_INIT_MAX_NUM) { ++ mgmt->ref_count++; ++ } ++ klad_mutex_unlock(&mgmt->lock); ++ return TD_SUCCESS; ++ } ++ ++ mgmt->klad_fd = klad_open("/dev/"UMAP_DEVNAME_KLAD_BASE, O_RDWR, 0); ++ if (mgmt->klad_fd < 0) { ++ klad_mutex_unlock(&mgmt->lock); ++ ot_klad_error("open /dev/%s failed. ret %x\n", UMAP_DEVNAME_KLAD_BASE, OT_ERR_KLAD_FAILED_INIT); ++ return OT_ERR_KLAD_FAILED_INIT; ++ } ++ mgmt->ref_count = 0; ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ ot_klad_exit(); ++ ++ return ret; ++} ++ ++td_s32 ot_mpi_klad_deinit(td_void) ++{ ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ot_klad_enter(); ++ ++ klad_mutex_lock(&mgmt->lock); ++ ++ if (mgmt->ref_count != 0) { ++ if (mgmt->ref_count > 0) { ++ mgmt->ref_count--; ++ } ++ klad_mutex_unlock(&mgmt->lock); ++ return TD_SUCCESS; ++ } ++ ++ if (klad_close(mgmt->klad_fd) < 0) { ++ mgmt->klad_fd = -1; ++ mgmt->ref_count = -1; ++ klad_mutex_unlock(&mgmt->lock); ++ ot_klad_error("close /dev/%s failed.\n", UMAP_DEVNAME_KLAD_BASE); ++ return OT_ERR_KLAD_FAILED_INIT; ++ } ++ mgmt->klad_fd = -1; ++ mgmt->ref_count = -1; ++ ++ klad_mutex_unlock(&mgmt->lock); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_create(td_handle *klad) ++{ ++ td_s32 ret; ++ klad_ctl_handle handle; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ ot_klad_formula_fail_return(klad == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ (td_void)memset_s(&handle, sizeof(klad_ctl_handle), 0, sizeof(klad_ctl_handle)); ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_CREATE_HANDLE, &handle); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ *klad = handle.klad; ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_destroy(td_handle klad) ++{ ++ td_s32 ret; ++ klad_ctl_handle handle; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ (td_void)memset_s(&handle, sizeof(klad_ctl_handle), 0, sizeof(klad_ctl_handle)); ++ handle.klad = klad; ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_DESTROY_HANDLE, &handle); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_attach(td_handle klad, td_handle target) ++{ ++ td_s32 ret; ++ klad_ctl_target ctl_target; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ (td_void)memset_s(&ctl_target, sizeof(klad_ctl_target), 0, sizeof(klad_ctl_target)); ++ ctl_target.klad = klad; ++ ctl_target.target = target; ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_ATTACH_TARGET, &ctl_target); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_detach(td_handle klad, td_handle target) ++{ ++ td_s32 ret; ++ klad_ctl_target ctl_target; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ (td_void)memset_s(&ctl_target, sizeof(klad_ctl_target), 0, sizeof(klad_ctl_target)); ++ ctl_target.klad = klad; ++ ctl_target.target = target; ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_DETACH_TARGET, &ctl_target); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_set_attr(td_handle klad, const ot_klad_attr *attr) ++{ ++ td_s32 ret; ++ klad_ctl_attr ctl_attr; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ ot_klad_formula_fail_return(attr == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ot_klad_formula_fail_return(attr->klad_cfg.klad_type != OT_KLAD_TYPE_COMMON && ++ attr->klad_cfg.klad_type != OT_KLAD_TYPE_CLEARCW, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(attr->klad_cfg.rootkey_attr.key_sel < 0 || ++ attr->klad_cfg.rootkey_attr.key_sel > OT_KLAD_ROOTKEY_SEL_VENDOR, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(attr->klad_cfg.rootkey_attr.key_secure != OT_KLAD_ROOTKEY_SEC_REE && ++ attr->klad_cfg.rootkey_attr.key_secure != OT_KLAD_ROOTKEY_SEC_TEE, OT_ERR_KLAD_INVALID_PARAM); ++ ++ (td_void)memset_s(&ctl_attr, sizeof(klad_ctl_attr), 0, sizeof(klad_ctl_attr)); ++ ctl_attr.klad = klad; ++ ret = memcpy_s(&(ctl_attr.attr), sizeof(ot_klad_attr), attr, sizeof(ot_klad_attr)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_SET_ATTR, &ctl_attr); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_get_attr(td_handle klad, ot_klad_attr *attr) ++{ ++ td_s32 ret; ++ klad_ctl_attr ctl_attr; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ ot_klad_formula_fail_return(attr == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ (td_void)memset_s(&ctl_attr, sizeof(klad_ctl_attr), 0, sizeof(klad_ctl_attr)); ++ ctl_attr.klad = klad; ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_GET_ATTR, &ctl_attr); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ ret = memcpy_s(attr, sizeof(ot_klad_attr), &(ctl_attr.attr), sizeof(ot_klad_attr)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_set_session_key(td_handle klad, const ot_klad_session_key *key) ++{ ++ td_s32 ret; ++ klad_ctl_session_key session_key; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ ot_klad_formula_fail_return(key == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ /* 16 byte:logic only supports 128bit session key */ ++ ot_klad_formula_fail_return(key->key_size != 16, OT_ERR_KLAD_INVALID_PARAM); ++ ++ (td_void)memset_s(&session_key, sizeof(klad_ctl_session_key), 0, sizeof(klad_ctl_session_key)); ++ session_key.klad = klad; ++ ret = memcpy_s(&session_key.key, sizeof(ot_klad_session_key), key, sizeof(ot_klad_session_key)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_SESSION_KEY, &session_key); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(&session_key.key, sizeof(ot_klad_session_key), 0, sizeof(ot_klad_session_key)); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_set_content_key(td_handle klad, const ot_klad_content_key *key) ++{ ++ td_s32 ret; ++ klad_ctl_content_key content_key; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ ot_klad_formula_fail_return(key == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ot_klad_formula_fail_return(key->alg != OT_KLAD_ALG_TYPE_AES && ++ key->alg != OT_KLAD_ALG_TYPE_SM4, OT_ERR_KLAD_INVALID_PARAM); ++ ot_klad_formula_fail_return(key->crypto_alg != OT_KLAD_CRYPTO_ALG_AES && ++ key->crypto_alg != OT_KLAD_CRYPTO_ALG_SM4, OT_ERR_KLAD_INVALID_PARAM); ++ ++ (td_void)memset_s(&content_key, sizeof(klad_ctl_content_key), 0, sizeof(klad_ctl_content_key)); ++ content_key.klad = klad; ++ ret = memcpy_s(&content_key.key, sizeof(ot_klad_content_key), key, sizeof(ot_klad_content_key)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_CONTENT_KEY, &content_key); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(&content_key.key, sizeof(ot_klad_content_key), 0, sizeof(ot_klad_content_key)); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_klad_set_clear_key(td_handle klad, const ot_klad_clear_key *key) ++{ ++ td_s32 ret; ++ klad_ctl_clear_key clear_key; ++ mpi_klad_mgmt *mgmt = _mpi_klad_get_mgmt(); ++ ++ ot_klad_enter(); ++ ++ _mpi_klad_not_init_return(mgmt->ref_count); ++ ++ ot_klad_formula_fail_return(key == TD_NULL, OT_ERR_KLAD_NULL_PTR); ++ ++ (td_void)memset_s(&clear_key, sizeof(klad_ctl_clear_key), 0, sizeof(klad_ctl_clear_key)); ++ clear_key.klad = klad; ++ ret = memcpy_s(&clear_key.key, sizeof(ot_klad_clear_key), key, sizeof(ot_klad_clear_key)); ++ ot_klad_func_fail_return(memcpy_s, ret != EOK, OT_ERR_KLAD_FAILED_SEC_FUNC); ++ ++ ret = klad_ioctl(mgmt->klad_fd, CMD_KLAD_CLEAR_KEY, &clear_key); ++ ot_klad_func_fail_return(klad_ioctl, ret != TD_SUCCESS, ret); ++ ++ /* clean key */ ++ (td_void)memset_s(&clear_key.key, sizeof(ot_klad_clear_key), 0, sizeof(ot_klad_clear_key)); ++ ++ ot_klad_exit(); ++ ++ return TD_SUCCESS; ++} +diff --git a/product/security_subsys/klad/src/osal/build.mak b/product/security_subsys/klad/src/osal/build.mak +new file mode 100644 +index 0000000..081e91c +--- /dev/null ++++ b/product/security_subsys/klad/src/osal/build.mak +@@ -0,0 +1,3 @@ ++KLAD_CFLAGS += -I$(KLAD_BASE_DIR)/osal/ ++ ++KLAD_DRV_OBJS += osal/drv_lib.o +diff --git a/product/security_subsys/klad/src/osal/drv_lib.c b/product/security_subsys/klad/src/osal/drv_lib.c +new file mode 100644 +index 0000000..56574b6 +--- /dev/null ++++ b/product/security_subsys/klad/src/osal/drv_lib.c +@@ -0,0 +1,54 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_lib.h" ++#ifdef KLAD_SWITCH_CPU ++#include "ot_debug_klad.h" ++#include "ot_common_klad.h" ++ ++static td_bool s_klad_secure_cpu = TD_FALSE; ++#endif ++ ++td_s32 klad_get_cpu_secure_sta(td_void) ++{ ++#ifdef KLAD_SWITCH_CPU ++ if (check_otp_cmd_mode()) { ++ s_klad_secure_cpu = TD_TRUE; ++ } else { ++ s_klad_secure_cpu = TD_FALSE; ++ } ++#endif ++ return TD_SUCCESS; ++} ++ ++/* KLAD_SECURE_CPU force to tee cpu ++ * KLAD_SWITCH_CPU can switch to ree or tee cpu ++ * else default ree cpu ++ */ ++td_bool klad_is_secure_cpu(td_void) ++{ ++#if defined(KLAD_SECURE_CPU) ++ return TD_TRUE; ++#elif defined(KLAD_SWITCH_CPU) ++ return s_klad_secure_cpu; ++#else ++ return TD_FALSE; ++#endif ++} ++ +diff --git a/product/security_subsys/klad/src/osal/drv_lib.h b/product/security_subsys/klad/src/osal/drv_lib.h +new file mode 100644 +index 0000000..5a845c6 +--- /dev/null ++++ b/product/security_subsys/klad/src/osal/drv_lib.h +@@ -0,0 +1,40 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_LIB_H ++#define DRV_LIB_H ++ ++#include "ot_type.h" ++ ++td_s32 klad_get_cpu_secure_sta(td_void); ++ ++td_bool klad_is_secure_cpu(td_void); ++ ++td_u32 ot_klad_get_klad_irq(td_void); ++ ++td_u32 ot_klad_get_rkp_irq(td_void); ++ ++extern int check_otp_cmd_mode(void); ++ ++/* must match kernel dts */ ++#define RKP_INT_NAME (klad_is_secure_cpu() ? "sec_rkp" : "nsec_rkp") ++ ++#define KLAD_INT_NAME (klad_is_secure_cpu() ? "sec_klad" : "nsec_klad") ++ ++#endif /* DRV_LIB_H */ +diff --git a/product/security_subsys/klad/src/osal/drv_osal_init.h b/product/security_subsys/klad/src/osal/drv_osal_init.h +new file mode 100644 +index 0000000..93eb40e +--- /dev/null ++++ b/product/security_subsys/klad/src/osal/drv_osal_init.h +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_OSAL_INIT_H ++#define DRV_OSAL_INIT_H ++ ++#include ++#include ++#include ++ ++#define KLAD_MUTEX_T td_s32 ++#define klad_mutex_init(x) (*(x) = *(x)) ++#define klad_mutex_lock(x) ++#define klad_mutex_unlock(x) ++#define klad_mutex_destroy(x) ++ ++#define klad_ioremap_nocache(addr, size) (td_void*)(addr) ++#define klad_iounmap(addr, size) ++ ++#define klad_malloc(x) malloc(x) ++#define klad_free(x) free(x) ++ ++#define klad_write(addr, data) writel(data, addr) ++#define klad_read(addr) readl(addr) ++ ++#define klad_udelay(x) udelay(x) ++ ++#define KLAD_QUEUE_HEAD td_s32 ++#define klad_queue_init(x) ++#define klad_queue_wait_up(x) ++#define klad_queue_destroy(x) ++ ++#define klad_queue_wait_timeout(head, func, param, time) ++ ++#define klad_request_irq(irq, func, name) ++ ++#define klad_free_irq(irq, name) ++ ++#endif /* DRV_OSAL_INIT_H */ ++ +diff --git a/product/security_subsys/otp/include/ot_common_otp.h b/product/security_subsys/otp/include/ot_common_otp.h +new file mode 100644 +index 0000000..977da70 +--- /dev/null ++++ b/product/security_subsys/otp/include/ot_common_otp.h +@@ -0,0 +1,68 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_COMMON_OTP_H ++#define OT_COMMON_OTP_H ++ ++#include "ot_type.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif /* __cplusplus */ ++ ++/* OTP error code value */ ++#define OT_ERR_OTP_NOT_INIT 0x804E0001 ++#define OT_ERR_OTP_NULL_PTR 0x804E0002 ++#define OT_ERR_OTP_BUSY 0x804E0003 ++#define OT_ERR_OTP_FAILED_INIT 0x804E0004 ++#define OT_ERR_OTP_FAILED_MEM 0x804E0005 ++#define OT_ERR_OTP_FAILED_SEC_FUNC 0x804E0006 ++#define OT_ERR_OTP_INVALID_PARAM 0x804E0007 ++#define OT_ERR_OTP_INVALID_FIELD_NAME 0x804E0008 ++#define OT_ERR_OTP_ZONE_ALREADY_SET 0x804E0009 ++#define OT_ERR_OTP_ZONE_LOCKED 0x804E000A ++#define OT_ERR_OTP_ZONE_NO_PERMIT 0x804E000B ++#define OT_ERR_OTP_WAIT_TIMEOUT 0x804E000C ++#define OT_ERR_OTP_FUNC_UNSUPPORT 0x804E000D ++ ++#define OT_OTP_PV_NAME_MAX_LEN 32 ++#define OT_OTP_PV_VALUE_MAX_LEN 32 ++ ++/* persistent value struct */ ++typedef struct { ++ td_bool burn; /* Burn OTP fuse or read OTP fuse. */ ++ td_char field_name[OT_OTP_PV_NAME_MAX_LEN]; /* OTP fuse name. */ ++ td_u32 value_len; /* OTP fuse value bit length. */ ++ td_u8 value[OT_OTP_PV_VALUE_MAX_LEN]; /* OTP fuse value. */ ++ td_bool lock; /* Burn OTP fuse lock or not, ++ If this item not support lock. ignore it */ ++} ot_otp_burn_pv_item; ++ ++typedef enum { ++ OT_OTP_STA_ALL_UNLOCKED = 0, /* user data area is all unlock. */ ++ OT_OTP_STA_PARTIAL_LOCKED, /* user data area is partial unlock. */ ++ OT_OTP_STA_ALL_LOCKED, /* user data area is all lock. */ ++ OT_OTP_STA_BUTT, /* invalid param. */ ++} ot_otp_lock_status; ++ ++#ifdef __cplusplus ++} ++#endif /* __cplusplus */ ++ ++#endif /* OT_COMMON_OTP_H */ +diff --git a/product/security_subsys/otp/include/ot_mpi_otp.h b/product/security_subsys/otp/include/ot_mpi_otp.h +new file mode 100644 +index 0000000..da76228 +--- /dev/null ++++ b/product/security_subsys/otp/include/ot_mpi_otp.h +@@ -0,0 +1,55 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_MPI_OTP_H ++#define OT_MPI_OTP_H ++ ++#include "ot_common_otp.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++td_s32 ot_mpi_otp_init(td_void); ++ ++td_s32 ot_mpi_otp_deinit(td_void); ++ ++td_s32 ot_mpi_otp_set_user_data(const td_char *field_name, ++ td_u32 offset, const td_u8 *value, td_u32 value_len); ++ ++td_s32 ot_mpi_otp_get_user_data(const td_char *field_name, ++ td_u32 offset, td_u8 *value, td_u32 value_len); ++ ++td_s32 ot_mpi_otp_burn_product_pv(const ot_otp_burn_pv_item *pv, td_u32 num); ++ ++td_s32 ot_mpi_otp_read_product_pv(ot_otp_burn_pv_item *pv, td_u32 num); ++ ++td_s32 ot_mpi_otp_get_key_verify_status(const td_char *key_name, td_bool *status); ++ ++td_s32 ot_mpi_otp_set_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len); ++ ++td_s32 ot_mpi_otp_get_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len, ot_otp_lock_status *lock); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* OT_MPI_OTP_H */ +diff --git a/product/security_subsys/otp/src/Makefile b/product/security_subsys/otp/src/Makefile +new file mode 100644 +index 0000000..0df6136 +--- /dev/null ++++ b/product/security_subsys/otp/src/Makefile +@@ -0,0 +1,26 @@ ++# INTER_DRV defined before include arch/build.mak ++ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss524v100" "ss522v101" "ss522v100" "ss615v100")) ++INTER_DRV := ss524v100 ++else ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss528v100" "ss625v100")) ++INTER_DRV := ss528v100 ++else ifeq ($(CONFIG_PRODUCTNAME), $(filter $(CONFIG_PRODUCTNAME), "ss928v100" "ss927v100")) ++INTER_DRV := ss928v100 ++endif ++ ++OTP_BASE_DIR := $(srctree)/product/security_subsys/otp/src ++ ++# Add objs ++include $(OTP_BASE_DIR)/mpi/build.mak ++include $(OTP_BASE_DIR)/mkp/build.mak ++include $(OTP_BASE_DIR)/arch/build.mak ++include $(OTP_BASE_DIR)/osal/build.mak ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/include ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/../include ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/../../ext_inc ++ ++cflags-y += $(OTP_CFLAGS) ++cflags-y += -DOT_OTP_DEBUG=0 ++ ++ccflags-y += $(cflags-y) ++ ++obj-y += $(MPI_OBJS) $(DRV_OBJS) +diff --git a/product/security_subsys/otp/src/arch/build.mak b/product/security_subsys/otp/src/arch/build.mak +new file mode 100644 +index 0000000..c648da7 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/build.mak +@@ -0,0 +1,14 @@ ++ ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/arch/include ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/arch/hal/ ++ ++# INTER_DRV select chip ++include $(OTP_BASE_DIR)/arch/$(INTER_DRV)/build.mak ++ ++DRV_OBJS += arch/hal/hal_otp_comm.o ++ ++DRV_OBJS += arch/hal/$(OT_OTP_VERSION)/hal_otp.o ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/arch/hal/$(OT_OTP_VERSION) ++ ++# lower to upper ++OTP_CFLAGS += -DOT_OTP_$(shell echo $(OT_OTP_VERSION) | tr a-z A-Z) +diff --git a/product/security_subsys/otp/src/arch/hal/hal_otp_comm.c b/product/security_subsys/otp/src/arch/hal/hal_otp_comm.c +new file mode 100644 +index 0000000..1ba431e +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/hal/hal_otp_comm.c +@@ -0,0 +1,79 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "hal_otp_comm.h" ++#include "drv_osal_init.h" ++#include "otp_reg_base.h" ++#include "ot_debug_otp.h" ++#include "ot_common_otp.h" ++ ++static td_u8 *g_otp_base_vir_addr = TD_NULL; ++ ++td_s32 hal_otp_init(td_void) ++{ ++#ifdef OT_OTP_V100 ++ td_u32 crg_value; ++ td_u32 *sys_addr = TD_NULL; ++ ++ /* otp clock configure */ ++ sys_addr = otp_ioremap_nocache(REG_SYS_OTP_CLK_ADDR_PHY, 0x100); ++ ot_otp_func_fail_return(otp_ioremap_nocache, sys_addr == TD_NULL, OT_ERR_OTP_FAILED_MEM); ++ ++ crg_value = otp_read(sys_addr); ++ crg_value |= OTP_CRG_CLOCK_BIT; /* set the bit 0, clock opened */ ++ otp_write(sys_addr, crg_value); ++ ++ otp_iounmap(sys_addr, 0x100); ++#endif ++ ++ /* otp reg addr map */ ++ g_otp_base_vir_addr = otp_ioremap_nocache(OTP_REG_BASE_ADDR_PHY, OTP_REG_BASE_ADDR_SIZE); ++ ot_otp_func_fail_return(otp_ioremap_nocache, g_otp_base_vir_addr == TD_NULL, OT_ERR_OTP_FAILED_MEM); ++ ++ return TD_SUCCESS; ++} ++ ++td_void hal_otp_deinit(td_void) ++{ ++ if (g_otp_base_vir_addr != TD_NULL) { ++ otp_iounmap(g_otp_base_vir_addr, OTP_REG_BASE_ADDR_SIZE); ++ g_otp_base_vir_addr = TD_NULL; ++ } ++} ++ ++td_s32 hal_otp_read_word(td_u32 offset, td_u32 *value) ++{ ++ ot_otp_formula_fail_return(value == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return(g_otp_base_vir_addr == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ++ *value = otp_read((volatile td_u8 *)(g_otp_base_vir_addr + (offset))); ++ ++ ot_otp_info(" read offset %04X value %x\n", offset, *value); ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_otp_write_word(td_u32 offset, td_u32 value) ++{ ++ ot_otp_formula_fail_return(g_otp_base_vir_addr == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ++ otp_write((volatile td_u8 *)(g_otp_base_vir_addr + (offset)), value); ++ ++ ot_otp_info("write offset %04X value %x\n", offset, value); ++ return TD_SUCCESS; ++} +diff --git a/product/security_subsys/otp/src/arch/hal/hal_otp_comm.h b/product/security_subsys/otp/src/arch/hal/hal_otp_comm.h +new file mode 100644 +index 0000000..3d825aa +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/hal/hal_otp_comm.h +@@ -0,0 +1,33 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HAL_OTP_COMM_H ++#define HAL_OTP_COMM_H ++ ++#include "ot_type.h" ++ ++td_s32 hal_otp_init(td_void); ++ ++td_void hal_otp_deinit(td_void); ++ ++td_s32 hal_otp_read_word(td_u32 offset, td_u32 *value); ++ ++td_s32 hal_otp_write_word(td_u32 offset, td_u32 value); ++ ++#endif /* HAL_OTP_COMM_H */ +diff --git a/product/security_subsys/otp/src/arch/hal/v100/hal_otp.c b/product/security_subsys/otp/src/arch/hal/v100/hal_otp.c +new file mode 100644 +index 0000000..354a1e3 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/hal/v100/hal_otp.c +@@ -0,0 +1,458 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "hal_otp.h" ++#include "drv_lib.h" ++#include "drv_osal_init.h" ++#include "securec.h" ++#include "ot_debug_otp.h" ++#include "otp_reg_base.h" ++#include "drv_ioctl_otp.h" ++#include "hal_otp_define.h" ++ ++#ifdef OT_OTP_V100 ++ ++typedef enum { ++ OTP_WORK_MODE_READ_LOCK_STA = 0, ++ OTP_WORK_MODE_LOAD_CIPHER_KEY, ++ OTP_WORK_MODE_BURN_KEY, ++ OTP_WORK_MODE_VERIFY_KEY_CRC, ++ OTP_WORK_MODE_ENABLE_FLAG_CFG, ++ OTP_WORK_MODE_WRITE_USER_ROOM, ++ OTP_WORK_MODE_READ_USER_ROOM, ++ OTP_WORK_MODE_BUTT, ++} hal_otp_work_mode; ++ ++typedef enum { ++ OTP_USER_KEY0 = 0, ++ OTP_USER_KEY1, ++ OTP_USER_KEY2, ++ OTP_USER_KEY3, ++ OTP_USER_KEY_JTAG_PW_ID, ++ OTP_USER_KEY_JTAG_PW, ++ OTP_USER_KEY_ROOTKEY, ++ OTP_USER_KEY_HMACKEY, ++ OTP_USER_KEY_BUTT, ++} hal_otp_key_index; ++ ++typedef enum { ++ OTP_USER_FLAG_SECURE_BOOT_EN = 0, ++ OTP_USER_FLAG_DDR_CA_EN, ++ OTP_USER_FLAG_JTAG_CA_EN, ++ OTP_USER_FLAG_JTAG_PRT_MODE, ++ OTP_USER_FLAG_SECURE_ISO_EN, ++ OTP_USER_FLAG_UBOOT_REDUNDANCE, ++ OTP_USER_FLAG_RESERVED, ++ OTP_USER_FLAG_BUTT, ++} hal_otp_flag_index; ++ ++#define _hal_otp_read_sta0(sta) hal_otp_read_word(OTP_USER_LOCK_STA0, sta) ++#define _hal_otp_read_rev_data(data) hal_otp_read_word(OTP_USER_REV_RDATA, data) ++#define _hal_otp_read_ctrl_sta(sta) hal_otp_read_word(OTP_USER_CTRL_STA, sta) ++ ++#define _hal_otp_set_rev_addr(offset) hal_otp_write_word(OTP_USER_REV_ADDR, offset) ++#define _hal_otp_set_rev_data(data) hal_otp_write_word(OTP_USER_REV_WDATA, data) ++#define _hal_otp_set_work_mode(mode) hal_otp_write_word(OTP_USER_WORK_MODE, mode) ++#define _hal_otp_set_key_index(idx) hal_otp_write_word(OTP_USER_KEY_INDEX, idx) ++#define _hal_otp_set_flag_index(idx) hal_otp_write_word(OTP_USER_FLAG_INDEX, idx) ++#define _hal_otp_set_flag_value(value) hal_otp_write_word(OTP_USER_FLAG_VALUE, value) ++#define _hal_otp_op_start() hal_otp_write_word(OTP_USER_OP_START, OTP_OP_START_VAL) ++ ++static td_s32 _hal_otp_wait_op_free(td_void) ++{ ++ otp_user_ctrl_sta ctrl_sta; ++ td_u32 time_out_cnt = OTP_WAIT_TIME_OUT; ++ ++ while (time_out_cnt--) { ++ _hal_otp_read_ctrl_sta(&ctrl_sta.u32); ++ ++ if (ctrl_sta.bits.otp_op_busy == 0) { ++ return TD_SUCCESS; ++ } ++ otp_udelay(1); ++ } ++ ++ ot_otp_error("otp wait op timeout!\n"); ++ return OT_ERR_OTP_WAIT_TIMEOUT; ++} ++ ++static td_s32 _hal_otp_wait_cmd_finish(td_void) ++{ ++ return _hal_otp_wait_op_free(); ++} ++ ++static td_s32 _hal_otp_get_user_lock_sta(td_u32 *sta0) ++{ ++ td_s32 ret; ++ ++ ot_otp_enter(); ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set work mode to read lock sta */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_READ_LOCK_STA); ++ ++ /* 3. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 4. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ /* 5. read lock sta */ ++ _hal_otp_read_sta0(sta0); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++/* ++ * write key: Writes key from a high address to a low address by word ++ * calculate crc: calculate crc according to the key write sequence by byte ++ */ ++static td_s32 _hal_otp_write_key_data(const td_u8 *u8_key, td_u32 u8_klen) ++{ ++ td_s32 ret; ++ td_u32 u32_key[OTP_USER_KEY_MAX_WORDS]; ++ td_u8 u8_tmp[OTP_USER_KEY_MAX_BYTES]; ++ td_u32 crc, i, u32_klen; ++ ++ ot_otp_formula_fail_return(!align_word(u8_klen), OT_ERR_OTP_INVALID_PARAM); ++ ++ (td_void)memset_s(u32_key, sizeof(u32_key), 0, sizeof(u32_key)); ++ (td_void)memset_s(u8_tmp, sizeof(u8_tmp), 0, sizeof(u8_tmp)); ++ ++ u32_klen = u8_klen / WORD_BYTE_WIDTH; ++ ++ ret = memcpy_s((td_u8 *)u32_key, sizeof(u32_key), u8_key, u8_klen); ++ ot_otp_func_fail_return(memcpy_s, ret != EOK, OT_ERR_OTP_FAILED_SEC_FUNC); ++ ++ for (i = 0; i < u32_klen; i++) { ++ hal_otp_write_word(OTP_USER_KEY_DATA0 + i * WORD_BYTE_WIDTH, u32_key[i]); ++ ++ ret = ot_otp_word_big_endian(u32_key[i], &u8_tmp[i * WORD_BYTE_WIDTH], WORD_BYTE_WIDTH); ++ if (ret != TD_SUCCESS) { ++ (td_void)memset_s(u32_key, sizeof(u32_key), 0, sizeof(u32_key)); ++ ot_otp_error("call %s failed, ret = 0x%08X\n", "ot_otp_word_big_endian", ret); ++ return ret; ++ } ++ } ++ ++ crc = (0x0000ffff & ot_otp_crc16_modbus(u8_tmp, OTP_USER_KEY_MAX_BYTES, u8_klen)); ++ hal_otp_write_word(OTP_USER_KEY_DATA0 + i * WORD_BYTE_WIDTH, crc); ++ ++ (td_void)memset_s(u32_key, sizeof(u32_key), 0, sizeof(u32_key)); ++ return TD_SUCCESS; ++} ++ ++static td_s32 _hal_otp_write_flag_value(hal_otp_flag_index flag_index, td_u32 value, td_u32 lock) ++{ ++ otp_user_flag_value flag_value; ++ ++ flag_value.u32 = 0; ++ ++ if (flag_index == OTP_USER_FLAG_JTAG_PRT_MODE) { ++ ot_otp_formula_fail_return(value > 0x03, OT_ERR_OTP_INVALID_PARAM); ++ ++ flag_value.bits.jtag_prt_mode = value; ++ if (lock == TD_TRUE) { ++ flag_value.bits.jtag_prt_mode_lock_en = 1; ++ } ++ } else { ++ ot_otp_formula_fail_return(value > 0x01, OT_ERR_OTP_INVALID_PARAM); ++ switch (flag_index) { ++ case OTP_USER_FLAG_SECURE_BOOT_EN: ++ flag_value.bits.secure_boot_en = value; ++ break; ++ case OTP_USER_FLAG_DDR_CA_EN: ++ flag_value.bits.ddr_ca_en = value; ++ break; ++ case OTP_USER_FLAG_JTAG_CA_EN: ++ flag_value.bits.jtag_ca_en = value; ++ break; ++ case OTP_USER_FLAG_UBOOT_REDUNDANCE: ++ flag_value.bits.uboot_redundance = value; ++ break; ++ default: ++ ot_otp_error("flag index %d is invalid\n", flag_index); ++ return OT_ERR_OTP_INVALID_PARAM; ++ } ++ } ++ _hal_otp_set_flag_value(flag_value.u32); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_otp_get_key_lock_sta(td_u32 offset, td_bool *lock_sta) ++{ ++ td_s32 ret; ++ otp_user_lock_sta0 sta0; ++ ++ ret = _hal_otp_get_user_lock_sta(&sta0.u32); ++ ot_otp_func_fail_return(_hal_otp_get_user_lock_sta, ret != TD_SUCCESS, ret); ++ ++ if ((offset == OTP_USER_KEY0 && sta0.bits.key0_lock == 1) || ++ (offset == OTP_USER_KEY1 && sta0.bits.key1_lock == 1) || ++ (offset == OTP_USER_KEY2 && sta0.bits.key2_lock == 1) || ++ (offset == OTP_USER_KEY3 && sta0.bits.key3_lock == 1) || ++ (offset == OTP_USER_KEY_JTAG_PW_ID && sta0.bits.jtag_pw_id_lock == 1) || ++ (offset == OTP_USER_KEY_JTAG_PW && sta0.bits.jtag_pw_lock == 1) || ++ (offset == OTP_USER_KEY_ROOTKEY && sta0.bits.root_key_lock == 1) || ++ (offset == OTP_USER_KEY_HMACKEY && sta0.bits.hmac_key_lock == 1)) { ++ *lock_sta = TD_TRUE; ++ } else { ++ *lock_sta = TD_FALSE; ++ } ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_otp_get_flag_lock_sta(td_u32 offset, td_bool *lock_sta) ++{ ++ td_s32 ret; ++ otp_user_lock_sta0 sta0; ++ ++ ret = _hal_otp_get_user_lock_sta(&sta0.u32); ++ ot_otp_func_fail_return(_hal_otp_get_user_lock_sta, ret != TD_SUCCESS, ret); ++ ++ if ((offset == OTP_USER_FLAG_SECURE_BOOT_EN && sta0.bits.secure_boot_en_lock == 1) || ++ (offset == OTP_USER_FLAG_DDR_CA_EN && sta0.bits.ddr_ca_en_lock == 1) || ++ (offset == OTP_USER_FLAG_JTAG_CA_EN && sta0.bits.jtag_ca_en_lock == 1) || ++ (offset == OTP_USER_FLAG_JTAG_PRT_MODE && sta0.bits.jtag_prt_mode_lock == 1) || ++ (offset == OTP_USER_FLAG_UBOOT_REDUNDANCE && sta0.bits.uboot_redundance_lock == 1)) { ++ *lock_sta = TD_TRUE; ++ } else { ++ *lock_sta = TD_FALSE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_otp_set_user_data_word(td_u32 index, td_u32 value, td_bool lock) ++{ ++ td_s32 ret; ++ otp_user_ctrl_sta ctrl_sta; ++ ++ ot_unused(lock); ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set rev addr */ ++ _hal_otp_set_rev_addr(index); ++ ++ /* 3. write rev data */ ++ _hal_otp_set_rev_data(value); ++ ++ /* 4. set work mode to write user room */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_WRITE_USER_ROOM); ++ ++ /* 5. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 6. wait user cmd finish & check lock status */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ _hal_otp_read_ctrl_sta(&ctrl_sta.u32); ++ if (ctrl_sta.bits.otp_user_lock_err == 1) { ++ ot_otp_error("index %u has locked\n", index); ++ return OT_ERR_OTP_ZONE_LOCKED; ++ } ++ return ret; ++} ++ ++td_s32 hal_otp_get_user_data_word(td_u32 index, td_u32 *value) ++{ ++ td_s32 ret; ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set rev addr */ ++ _hal_otp_set_rev_addr(index); ++ ++ /* 3. set work mode to read user room */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_READ_USER_ROOM); ++ ++ /* 4. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 5. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ /* 6. read rev data */ ++ _hal_otp_read_rev_data(value); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_load_key_to_klad(td_u32 key_index) ++{ ++ td_s32 ret; ++ ++ ot_otp_enter(); ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set key index */ ++ _hal_otp_set_key_index(key_index); ++ ++ /* 3. set work mode to load key */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_LOAD_CIPHER_KEY); ++ ++ /* 4. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 5. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_verify_key(td_u32 key_index, td_bool *flag) ++{ ++ td_s32 ret; ++ otp_user_ctrl_sta ctrl_sta; ++ ++ ot_otp_enter(); ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set key index */ ++ _hal_otp_set_key_index(key_index); ++ ++ /* 3. set work mode to verify key crc */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_VERIFY_KEY_CRC); ++ ++ /* 4. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 5. wait user cmd finish & check key crc flag */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ _hal_otp_read_ctrl_sta(&ctrl_sta.u32); ++ if (ctrl_sta.bits.key_crc_check_ok_flag == 1) { ++ *flag = TD_TRUE; ++ ot_otp_info("verify key crc %d ok\n", key_index); ++ } else { ++ *flag = TD_FALSE; ++ ot_otp_info("verify key crc %d failed\n", key_index); ++ } ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_burn_key(const otp_data_item *data_item, const td_u8 *key, td_u32 klen) ++{ ++ td_s32 ret; ++ td_bool lock_sta; ++ ++ ot_otp_enter(); ++ ++ /* 1. check user key status */ ++ ret = hal_otp_get_key_lock_sta(data_item->offset, &lock_sta); ++ ot_otp_func_fail_return(hal_otp_get_key_lock_sta, ret != TD_SUCCESS, ret); ++ if (lock_sta == TD_TRUE) { ++ ot_otp_error("key slot %s had locked\n", data_item->field_name); ++ return OT_ERR_OTP_ZONE_ALREADY_SET; ++ } ++ ++ /* 2. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 3. set user key index */ ++ _hal_otp_set_key_index(data_item->offset); ++ ++ /* 4. write key data */ ++ ret = _hal_otp_write_key_data(key, klen); ++ ot_otp_func_fail_return(_hal_otp_write_key_data, ret != TD_SUCCESS, ret); ++ ++ /* 5. set work mode */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_BURN_KEY); ++ ++ /* 6. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 7. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_enable_flag(const otp_data_item *data_item, td_u32 flag, td_bool lock) ++{ ++ td_s32 ret; ++ td_bool lock_sta; ++ ++ ot_otp_enter(); ++ ++ /* 1. check flag status */ ++ ret = hal_otp_get_flag_lock_sta(data_item->offset, &lock_sta); ++ ot_otp_func_fail_return(hal_otp_get_flag_lock_sta, ret != TD_SUCCESS, ret); ++ if (lock_sta == TD_TRUE) { ++ ot_otp_error("flag %s had locked!!!\n", data_item->field_name); ++ return OT_ERR_OTP_ZONE_ALREADY_SET; ++ } ++ ++ /* 2. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 3. set flag index */ ++ _hal_otp_set_flag_index(data_item->offset); ++ ++ /* 4. set flag value */ ++ ret = _hal_otp_write_flag_value(data_item->offset, flag, lock); ++ ot_otp_func_fail_return(_hal_otp_write_flag_value, ret != TD_SUCCESS, ret); ++ ++ /* 5. set work mode to enable flag config */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_ENABLE_FLAG_CFG); ++ ++ /* 6. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 7. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++#endif +diff --git a/product/security_subsys/otp/src/arch/hal/v100/hal_otp_define.h b/product/security_subsys/otp/src/arch/hal/v100/hal_otp_define.h +new file mode 100644 +index 0000000..cbe4c85 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/hal/v100/hal_otp_define.h +@@ -0,0 +1,162 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HAL_OTP_DEFINE_H ++#define HAL_OTP_DEFINE_H ++ ++/* Define the union otp_user_work_mode */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_user_work_mode : 3; /* [2..0] */ ++ unsigned int reserved_0 : 29; /* [31..3] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_work_mode; ++ ++/* Define the union otp_user_key_index */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_index : 3; /* [2..0] */ ++ unsigned int reserved_0 : 29; /* [31..3] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_key_index; ++ ++/* Define the union otp_user_flag_value */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int secure_boot_en : 1; /* [0] */ ++ unsigned int ddr_ca_en : 1; /* [1] */ ++ unsigned int jtag_ca_en : 1; /* [2] */ ++ unsigned int reserved_0 : 1; /* [3] */ ++ unsigned int jtag_prt_mode : 2; /* [5..4] */ ++ unsigned int jtag_prt_mode_lock_en : 1; /* [6] */ ++ unsigned int secure_iso_en : 1; /* [7] */ ++ unsigned int uboot_redundance : 1; /* [8] */ ++ unsigned int reserved_flag1 : 1; /* [9] */ ++ unsigned int reserved_flag2 : 21; /* [30..10] */ ++ unsigned int reserved_flag2_lock_en : 1; /* [31] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_flag_value; ++ ++/* Define the union otp_user_flag_index */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int flag_index : 3; /* [2..0] */ ++ unsigned int reserved_0 : 29; /* [31..3] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_flag_index; ++ ++/* Define the union otp_user_rev_addr */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_user_reserved_addr : 10; /* [9..0] */ ++ unsigned int reserved_0 : 22; /* [31..10] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_rev_addr; ++ ++/* Define the union otp_user_lock_sta0 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int secure_boot_en_lock : 1; /* [0] */ ++ unsigned int ddr_ca_en_lock : 1; /* [1] */ ++ unsigned int jtag_pw_id_lock : 1; /* [2] */ ++ unsigned int jtag_pw_lock : 1; /* [3] */ ++ unsigned int root_key_lock : 1; /* [4] */ ++ unsigned int key0_lock : 1; /* [5] */ ++ unsigned int key1_lock : 1; /* [6] */ ++ unsigned int key2_lock : 1; /* [7] */ ++ unsigned int key3_lock : 1; /* [8] */ ++ unsigned int jtag_ca_en_lock : 1; /* [9] */ ++ unsigned int jtag_prt_mode_lock : 1; /* [10] */ ++ unsigned int reserved_2 : 1; /* [11] */ ++ unsigned int uboot_redundance_lock : 1; /* [12] */ ++ unsigned int reserved_flag1_lock : 1; /* [13] */ ++ unsigned int reserved_flag2_lock : 1; /* [14] */ ++ unsigned int hmac_key_lock : 1; /* [15] */ ++ unsigned int reserved_3 : 16; /* [31..16] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lock_sta0; ++ ++/* Define the union otp_user_ctrl_sta */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_op_busy : 1; /* [0] */ ++ unsigned int otp_user_cmd_finish : 1; /* [1] */ ++ unsigned int otp_user_lock_err : 1; /* [2] */ ++ unsigned int key_rd_back_check_err : 1; /* [3] */ ++ unsigned int key_crc_check_ok_flag : 1; /* [4] */ ++ unsigned int user_rev_rd_finish : 1; /* [5] */ ++ unsigned int user_rev_pgm_finish : 1; /* [6] */ ++ unsigned int flag_pgm_finish : 1; /* [7] */ ++ unsigned int key_crc_check_finish : 1; /* [8] */ ++ unsigned int key_pgm_finish : 1; /* [9] */ ++ unsigned int key_load_finish : 1; /* [10] */ ++ unsigned int rd_lock_sta_finish : 1; /* [11] */ ++ unsigned int key_index_finish : 4; /* [15..12] */ ++ unsigned int user_rev_addr_finish : 8; /* [23..16] */ ++ unsigned int flag_index_finish : 2; /* [25..24] */ ++ unsigned int reserved_0 : 6; /* [31..26] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_ctrl_sta; ++ ++/* Define the union otp_flag_sta */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int uboot_redundance : 1; /* [0] */ ++ unsigned int reserved_flag1 : 1; /* [1] */ ++ unsigned int reserved_flag2 : 21; /* [22..2] */ ++ unsigned int reserved_0 : 1; /* [23] */ ++ unsigned int jtag_prt_mode : 2; /* [25..24] */ ++ unsigned int jtag_ca_en : 1; /* [26] */ ++ unsigned int reserved_1 : 5; /* [31..27] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_flag_sta; ++ ++#endif /* HAL_OTP_DEFINE_H */ +diff --git a/product/security_subsys/otp/src/arch/hal/v200/hal_otp.c b/product/security_subsys/otp/src/arch/hal/v200/hal_otp.c +new file mode 100644 +index 0000000..baf8554 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/hal/v200/hal_otp.c +@@ -0,0 +1,418 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "hal_otp.h" ++#include "drv_lib.h" ++#include "drv_osal_init.h" ++#include "securec.h" ++#include "ot_debug_otp.h" ++#include "otp_reg_base.h" ++#include "drv_ioctl_otp.h" ++#include "hal_otp_define.h" ++ ++#ifdef OT_OTP_V200 ++ ++typedef enum { ++ OTP_WORK_MODE_READ_LOCK_STA = 0, ++ OTP_WORK_MODE_LOAD_CIPHER_KEY, ++ OTP_WORK_MODE_BURN_KEY, ++ OTP_WORK_MODE_VERIFY_KEY_CRC, ++ OTP_WORK_MODE_ENABLE_FLAG_CFG, ++ OTP_WORK_MODE_WRITE_USER_ROOM, ++ OTP_WORK_MODE_READ_USER_ROOM, ++ OTP_WORK_MODE_BUTT, ++} hal_otp_work_mode; ++ ++#define _hal_otp_read_sta_data(sta) hal_otp_read_word(OTP_USER_LOCK_STA_DATA, sta) ++#define _hal_otp_read_rev_data(data) hal_otp_read_word(OTP_USER_REV_RDATA, data) ++#define _hal_otp_read_ctrl_sta(sta) hal_otp_read_word(OTP_USER_CTRL_STA, sta) ++ ++#define _hal_otp_set_rev_addr(offset) hal_otp_write_word(OTP_USER_REV_ADDR, offset) ++#define _hal_otp_set_rev_data(data) hal_otp_write_word(OTP_USER_REV_WDATA, data) ++#define _hal_otp_set_work_mode(mode) hal_otp_write_word(OTP_USER_WORK_MODE, mode) ++#define _hal_otp_set_key_index(idx) hal_otp_write_word(OTP_USER_KEY_INDEX, idx) ++#define _hal_otp_set_flag_index(idx) hal_otp_write_word(OTP_USER_FLAG_INDEX, idx) ++#define _hal_otp_set_flag_value(value) hal_otp_write_word(OTP_USER_FLAG_VALUE, value) ++#define _hal_otp_set_sta_addr(addr) hal_otp_write_word(OTP_USER_LOCK_STA_ADDR, addr) ++#define _hal_otp_op_start() hal_otp_write_word(OTP_USER_OP_START, OTP_OP_START_VAL) ++ ++static td_s32 _hal_otp_wait_op_free(td_void) ++{ ++ otp_user_ctrl_sta ctrl_sta; ++ td_u32 time_out_cnt = OTP_WAIT_TIME_OUT; ++ ++ while (time_out_cnt--) { ++ _hal_otp_read_ctrl_sta(&ctrl_sta.u32); ++ ++ if (ctrl_sta.bits.otp_op_busy == 0) { ++ return TD_SUCCESS; ++ } ++ otp_udelay(1); ++ } ++ ++ ot_otp_error("otp wait op timeout!\n"); ++ return OT_ERR_OTP_WAIT_TIMEOUT; ++} ++ ++static td_s32 _hal_otp_wait_cmd_finish(td_void) ++{ ++ return _hal_otp_wait_op_free(); ++} ++ ++static td_s32 _hal_otp_get_user_lock_sta(td_u32 addr, td_u32 *sta) ++{ ++ td_s32 ret; ++ ++ ot_otp_enter(); ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set work mode to read lock sta */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_READ_LOCK_STA); ++ ++ /* 3. set sta address */ ++ _hal_otp_set_sta_addr(addr); ++ ++ /* 4. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 5. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ /* 6. read lock sta data */ ++ _hal_otp_read_sta_data(sta); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++/* ++ * write key: Writes key from a high address to a low address by word ++ * calculate crc: calculate crc according to the key write sequence by byte ++ */ ++static td_s32 _hal_otp_write_key_data(const td_u8 *u8_key, td_u32 u8_klen, td_u32 key_index) ++{ ++ td_s32 ret; ++ td_u32 u32_key[OTP_USER_KEY_MAX_WORDS]; ++ td_u8 u8_tmp[OTP_USER_KEY_MAX_BYTES]; ++ td_u32 crc, i, u32_klen; ++ ++ ot_otp_formula_fail_return(!align_word(u8_klen), OT_ERR_OTP_INVALID_PARAM); ++ ++ (td_void)memset_s(u32_key, sizeof(u32_key), 0, sizeof(u32_key)); ++ (td_void)memset_s(u8_tmp, sizeof(u8_tmp), 0, sizeof(u8_tmp)); ++ ++ u32_klen = u8_klen / WORD_BYTE_WIDTH; ++ ++ /* v200 OTP SYMC KEY load RKP module, ++ RKP module use otp symc key isn't different from v100 OTP KEY */ ++ if (key_index >= 6 && key_index <= 10) { /* 6,10 -> OEM SYMC KEY[0~3] + VENDOR SYMC KEY */ ++ for (i = 0; i < u32_klen; i++) { ++ u32_key[u32_klen - i - 1] = ((td_u32)u8_key[i * WORD_BYTE_WIDTH + 0] << 24) | /* 0, 24 */ ++ ((td_u32)u8_key[i * WORD_BYTE_WIDTH + 1] << 16) | /* 1, 16 */ ++ ((td_u32)u8_key[i * WORD_BYTE_WIDTH + 2] << 8) | /* 2, 8 */ ++ ((td_u32)u8_key[i * WORD_BYTE_WIDTH + 3] << 0); /* 3, 0 */ ++ } ++ } else { ++ ret = memcpy_s((td_u8 *)u32_key, sizeof(u32_key), u8_key, u8_klen); ++ ot_otp_func_fail_return(memcpy_s, ret != EOK, OT_ERR_OTP_FAILED_SEC_FUNC); ++ } ++ ++ for (i = 0; i < u32_klen; i++) { ++ hal_otp_write_word(OTP_USER_KEY_DATA0 + i * WORD_BYTE_WIDTH, u32_key[i]); ++ ++ ret = ot_otp_word_big_endian(u32_key[i], &u8_tmp[i * WORD_BYTE_WIDTH], WORD_BYTE_WIDTH); ++ if (ret != TD_SUCCESS) { ++ (td_void)memset_s(u32_key, sizeof(u32_key), 0, sizeof(u32_key)); ++ ot_otp_error("call %s failed, ret = 0x%08X\n", "ot_otp_word_big_endian", ret); ++ return ret; ++ } ++ } ++ ++ crc = (0x0000ffff & ot_otp_crc16_modbus(u8_tmp, OTP_USER_KEY_MAX_BYTES, u8_klen)); ++ hal_otp_write_word(OTP_USER_KEY_DATA0 + i * WORD_BYTE_WIDTH, crc); ++ ++ (td_void)memset_s(u32_key, sizeof(u32_key), 0, sizeof(u32_key)); ++ return TD_SUCCESS; ++} ++ ++static td_void _hal_otp_write_flag_value(td_u32 value) ++{ ++ otp_user_flag_value flag_value; ++ ++ flag_value.u32 = 0; ++ flag_value.bits.otp_flag_region_pgm_data = (value & 0x01); ++ ++ _hal_otp_set_flag_value(flag_value.u32); ++} ++ ++td_s32 hal_otp_get_key_lock_sta(td_u32 offset, td_bool *lock_sta) ++{ ++ td_s32 ret; ++ td_u32 lock_data = 0; ++ ++ /* 0 - key lock sta, 1 - flag lock sta, 2~1C - user data lock sta */ ++ ret = _hal_otp_get_user_lock_sta(0, &lock_data); ++ ot_otp_func_fail_return(_hal_otp_get_user_lock_sta, ret != TD_SUCCESS, ret); ++ ++ if (lock_data & (1 << (offset % WORD_BIT_WIDTH))) { ++ *lock_sta = TD_TRUE; ++ } else { ++ *lock_sta = TD_FALSE; ++ } ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_otp_get_flag_lock_sta(td_u32 offset, td_bool *lock_sta) ++{ ++ td_s32 ret; ++ td_u32 lock_data = 0; ++ ++ /* 0 - key lock sta, 1 - flag lock sta, 2~1C - user data lock sta */ ++ ret = _hal_otp_get_user_lock_sta(1, &lock_data); ++ ot_otp_func_fail_return(_hal_otp_get_user_lock_sta, ret != TD_SUCCESS, ret); ++ ++ if (lock_data & (1 << (offset % WORD_BIT_WIDTH))) { ++ *lock_sta = TD_TRUE; ++ } else { ++ *lock_sta = TD_FALSE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_otp_get_user_data_lock_sta(td_u32 index, td_bool *lock_sta) ++{ ++ td_s32 ret; ++ td_u32 lock_data = 0; ++ ++ /* 0 - key lock sta, 1 - flag lock sta, 2~1C - user data lock sta */ ++ ret = _hal_otp_get_user_lock_sta(index / WORD_BIT_WIDTH + 2, &lock_data); ++ ot_otp_func_fail_return(_hal_otp_get_user_lock_sta, ret != TD_SUCCESS, ret); ++ ++ ot_otp_info("index %d, lock_data %08x\n", index, lock_data); ++ if (lock_data & (1 << (index % WORD_BIT_WIDTH))) { ++ *lock_sta = TD_TRUE; ++ } else { ++ *lock_sta = TD_FALSE; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 hal_otp_set_user_data_word(td_u32 index, td_u32 value, td_bool lock) ++{ ++ td_s32 ret; ++ td_bool lock_sta; ++ otp_user_work_mode mode; ++ ++ /* 1. check user data lock status */ ++ ret = hal_otp_get_user_data_lock_sta(index, &lock_sta); ++ ot_otp_func_fail_return(hal_otp_get_user_data_lock_sta, ret != TD_SUCCESS, ret); ++ if (lock_sta == TD_TRUE) { ++ ot_otp_error("user data offset %u had locked!!!\n", index); ++ return OT_ERR_OTP_ZONE_ALREADY_SET; ++ } ++ ++ /* 2. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 3. set rev addr */ ++ _hal_otp_set_rev_addr(index); ++ ++ /* 4. write rev data */ ++ _hal_otp_set_rev_data(value); ++ ++ /* 5. set work mode to write user room */ ++ mode.u32 = 0; ++ mode.bits.user_reserved_lock_en = (td_u32)lock & 0x01; ++ mode.bits.otp_user_work_mode = OTP_WORK_MODE_WRITE_USER_ROOM; ++ _hal_otp_set_work_mode(mode.u32); ++ ++ /* 6. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 7. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_get_user_data_word(td_u32 index, td_u32 *value) ++{ ++ td_s32 ret; ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set rev addr */ ++ _hal_otp_set_rev_addr(index); ++ ++ /* 3. set work mode to read user room */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_READ_USER_ROOM); ++ ++ /* 4. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 5. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ /* 6. read rev data */ ++ _hal_otp_read_rev_data(value); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_verify_key(td_u32 key_index, td_bool *flag) ++{ ++ td_s32 ret; ++ otp_user_ctrl_sta ctrl_sta; ++ ++ ot_otp_enter(); ++ ++ /* 1. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 2. set key index */ ++ _hal_otp_set_key_index(key_index); ++ ++ /* 3. set work mode to verify key crc */ ++ _hal_otp_set_work_mode(OTP_WORK_MODE_VERIFY_KEY_CRC); ++ ++ /* 4. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 5. wait user cmd finish & check key crc flag */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ _hal_otp_read_ctrl_sta(&ctrl_sta.u32); ++ if (ctrl_sta.bits.key_crc_check_ok_flag == 1) { ++ *flag = TD_TRUE; ++ ot_otp_info("verify key crc %d ok\n", key_index); ++ } else { ++ *flag = TD_FALSE; ++ ot_otp_info("verify key crc %d failed\n", key_index); ++ } ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_burn_key(const otp_data_item *data_item, const td_u8 *key, td_u32 klen) ++{ ++ td_s32 ret; ++ otp_user_work_mode mode; ++ td_bool lock_sta; ++ ++ ot_otp_enter(); ++ ++ /* 1. check user key status */ ++ ret = hal_otp_get_key_lock_sta(data_item->offset, &lock_sta); ++ ot_otp_func_fail_return(hal_otp_get_key_lock_sta, ret != TD_SUCCESS, ret); ++ if (lock_sta == TD_TRUE) { ++ ot_otp_error("key slot %s had locked!!!\n", data_item->field_name); ++ return OT_ERR_OTP_ZONE_ALREADY_SET; ++ } ++ ++ /* 2. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 3. set user key index */ ++ _hal_otp_set_key_index(data_item->offset); ++ ++ /* 4. write key data */ ++ ret = _hal_otp_write_key_data(key, klen, data_item->offset); ++ ot_otp_func_fail_return(_hal_otp_write_key_data, ret != TD_SUCCESS, ret); ++ ++ /* 5. set work mode */ ++ mode.u32 = 0; ++ mode.bits.otp_user_work_mode = OTP_WORK_MODE_BURN_KEY; ++ mode.bits.user_reserved_lock_en = 1; ++ _hal_otp_set_work_mode(mode.u32); ++ ++ /* 6. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 7. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++td_s32 hal_otp_enable_flag(const otp_data_item *data_item, td_u32 flag, td_bool lock) ++{ ++ td_s32 ret; ++ otp_user_work_mode mode; ++ td_bool lock_sta; ++ ++ ot_otp_enter(); ++ ++ /* 1. check flag status */ ++ ret = hal_otp_get_flag_lock_sta(data_item->offset, &lock_sta); ++ ot_otp_func_fail_return(hal_otp_get_flag_lock_sta, ret != TD_SUCCESS, ret); ++ if (lock_sta == TD_TRUE) { ++ ot_otp_error("flag %s had locked!!!\n", data_item->field_name); ++ return OT_ERR_OTP_ZONE_ALREADY_SET; ++ } ++ ++ /* 2. wait otp ctrl free */ ++ ret = _hal_otp_wait_op_free(); ++ ot_otp_func_fail_return(_hal_otp_wait_op_free, ret != TD_SUCCESS, ret); ++ ++ /* 3. set flag index */ ++ _hal_otp_set_flag_index(data_item->offset); ++ ++ /* 4. set flag value */ ++ _hal_otp_write_flag_value(flag); ++ ot_otp_func_fail_return(_hal_otp_write_flag_value, ret != TD_SUCCESS, ret); ++ ++ /* 5. set work mode to enable flag config */ ++ mode.u32 = 0; ++ mode.bits.otp_user_work_mode = OTP_WORK_MODE_ENABLE_FLAG_CFG; ++ mode.bits.user_reserved_lock_en = lock; ++ _hal_otp_set_work_mode(mode.u32); ++ ++ /* 6. start otp ctrl */ ++ _hal_otp_op_start(); ++ ++ /* 7. wait user cmd finish */ ++ ret = _hal_otp_wait_cmd_finish(); ++ ot_otp_func_fail_return(_hal_otp_wait_cmd_finish, ret != TD_SUCCESS, ret); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++#endif +diff --git a/product/security_subsys/otp/src/arch/hal/v200/hal_otp_define.h b/product/security_subsys/otp/src/arch/hal/v200/hal_otp_define.h +new file mode 100644 +index 0000000..742e76e +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/hal/v200/hal_otp_define.h +@@ -0,0 +1,326 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HAL_OTP_DEFINE_H ++#define HAL_OTP_DEFINE_H ++ ++/* Define the union otp_user_work_mode */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_user_work_mode : 3; /* [2..0] */ ++ unsigned int reserved_0 : 1; /* [3] */ ++ unsigned int user_reserved_lock_en : 1; /* [4] */ ++ unsigned int reserved_1 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_work_mode; ++ ++/* Define the union otp_user_key_index */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_user_key_index : 4; /* [3..0] */ ++ unsigned int reserved_0 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_key_index; ++ ++/* Define the union otp_user_flag_value */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_flag_region_pgm_data : 1; /* [0] */ ++ unsigned int reserved_0 : 31; /* [31..1] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_flag_value; ++ ++/* Define the union otp_user_flag_index */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_flag_region_index : 5; /* [4..0] */ ++ unsigned int reserved_0 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_flag_index; ++ ++/* Define the union otp_user_rev_addr */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_user_reserved_addr : 8; /* [7..0] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_rev_addr; ++ ++/* Define the union otp_user_lock_sta_addr */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_lock_st_addr : 5; /* [4..0] */ ++ unsigned int reserved_0 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lock_sta_addr; ++ ++/* Define the union otp_user_ctrl_sta */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_op_busy : 1; /* [0] */ ++ unsigned int ree_addr_check_err : 1; /* [1] */ ++ unsigned int otp_user_lock_err : 1; /* [2] */ ++ unsigned int key_rd_back_check_err : 1; /* [3] */ ++ unsigned int key_crc_check_ok_flag : 1; /* [4] */ ++ unsigned int user_rev_rd_finish : 1; /* [5] */ ++ unsigned int user_rev_pgm_finish : 1; /* [6] */ ++ unsigned int flag_pgm_finish : 1; /* [7] */ ++ unsigned int key_crc_check_finish : 1; /* [8] */ ++ unsigned int key_pgm_finish : 1; /* [9] */ ++ unsigned int key_load_finish : 1; /* [10] */ ++ unsigned int rd_lock_sta_finish : 1; /* [11] */ ++ unsigned int key_index_finish : 4; /* [15..12] */ ++ unsigned int user_rev_addr_finish : 10; /* [25..16] */ ++ unsigned int fms_state_error : 1; /* [26] */ ++ unsigned int flag_index_finish : 5; /* [31..27] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_ctrl_sta; ++ ++/* Define the union otp_user_oneway0 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int update_from_uart_disable : 1; /* [0] */ ++ unsigned int update_from_sdio_disable : 1; /* [1] */ ++ unsigned int update_from_usbdev_disable : 1; /* [2] */ ++ unsigned int scs_dbg_disable : 1; /* [3] */ ++ unsigned int reserveda0_0 : 4; /* [7..4] */ ++ unsigned int oem_cw_crc_rd_disable : 8; /* [15..8] */ ++ unsigned int func_jtag_prt_mode : 8; /* [23..16] */ ++ unsigned int soc_jtag_prt_mode : 8; /* [31..24] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_oneway0; ++ ++/* Define the union otp_user_oneway1 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int uart_disable : 6; /* [5..0] */ ++ unsigned int reserveda1_0 : 26; /* [31..6] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_oneway1; ++ ++/* Define the union otp_user_lockable0 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int soc_tee_enable : 8; /* [7..0] */ ++ unsigned int reservedlk0 : 24; /* [31..8] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable0; ++ ++/* Define the union otp_user_lockable5 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int sm4_disable : 1; /* [0] */ ++ unsigned int sm3_disable : 1; /* [1] */ ++ unsigned int sw_sm2_disable : 1; /* [2] */ ++ unsigned int trng_drbg_sm3_disable : 1; /* [3] */ ++ unsigned int tee_owner_sel : 1; /* [4] */ ++ unsigned int oem_rk_deob_en : 1; /* [5] */ ++ unsigned int jtag_key_sel : 2; /* [7..6] */ ++ unsigned int sec_ds_enable : 1; /* [8] */ ++ unsigned int acpu_ds_enable : 1; /* [9] */ ++ unsigned int rkp_aes_sm4_sel : 1; /* [10] */ ++ unsigned int uboot_redundance : 1; /* [11] */ ++ unsigned int otp_pcie_disable : 1; /* [12] */ ++ unsigned int otp_pcie_ep_boot_disable : 1; /* [13] */ ++ unsigned int bload_dec_en : 1; /* [14] */ ++ unsigned int reserved_flag : 17; /* [31..15] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable5; ++ ++/* Define the union otp_user_lockable6 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_init_rdy : 8; /* [7..0] */ ++ unsigned int reserved_0 : 24; /* [31..8] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable6; ++ ++/* Define the union otp_user_lockable8 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int secure_boot_en : 8; /* [7..0] */ ++ unsigned int reservedlk5 : 24; /* [31..8] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable8; ++ ++/* Define the union otp_user_lockable9 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int double_sign_en : 4; /* [3..0] */ ++ unsigned int reservedlk6 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable9; ++ ++/* Define the union otp_user_lockable12 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int scs_alg_sel : 4; /* [3..0] */ ++ unsigned int reservedlk7 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable12; ++ ++/* Define the union otp_user_lockable13 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int hsl_dec_en : 4; /* [3..0] */ ++ unsigned int reservedlk8 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable13; ++ ++/* Define the union otp_user_lockable14 */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int quick_boot : 4; /* [3..0] */ ++ unsigned int reservedlk9 : 28; /* [31..4] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lockable14; ++ ++/* Define the union otp_user_work_mode_tee */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int tee_otp_user_work_mode : 3; /* [2..0] */ ++ unsigned int reserved_0 : 1; /* [3] */ ++ unsigned int tee_user_reserved_lock_en : 1; /* [4] */ ++ unsigned int reserved_1 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_work_mode_tee; ++ ++/* Define the union otp_user_rev_addr_tee */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_user_reserved_addr_tee : 10; /* [9..0] */ ++ unsigned int reserved_0 : 22; /* [31..10] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_rev_addr_tee; ++ ++/* Define the union otp_user_lock_sta_addr_tee */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_lock_st_addr_tee : 5; /* [4..0] */ ++ unsigned int reserved_0 : 27; /* [31..5] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_lock_sta_addr_tee; ++ ++/* Define the union otp_user_ctrl_sta_tee */ ++typedef union { ++ /* Define the struct bits */ ++ struct { ++ unsigned int otp_op_busy_tee : 1; /* [0] */ ++ unsigned int reserved_0 : 1; /* [1] */ ++ unsigned int otp_user_lock_err_tee : 1; /* [2] */ ++ unsigned int key_rd_back_check_err_tee : 1; /* [3] */ ++ unsigned int key_crc_check_ok_flag_tee : 1; /* [4] */ ++ unsigned int user_rev_rd_finish_tee : 1; /* [5] */ ++ unsigned int user_rev_pgm_finish_tee : 1; /* [6] */ ++ unsigned int flag_pgm_finish_tee : 1; /* [7] */ ++ unsigned int key_crc_check_finish_tee : 1; /* [8] */ ++ unsigned int key_pgm_finish_tee : 1; /* [9] */ ++ unsigned int key_load_finish_tee : 1; /* [10] */ ++ unsigned int rd_lock_sta_finish_tee : 1; /* [11] */ ++ unsigned int key_index_finish_tee : 4; /* [15..12] */ ++ unsigned int user_rev_addr_finish_tee : 10; /* [25..16] */ ++ unsigned int fms_state_error : 1; /* [26] */ ++ unsigned int flag_index_finish_tee : 5; /* [31..27] */ ++ } bits; ++ ++ /* Define an unsigned member */ ++ unsigned int u32; ++} otp_user_ctrl_sta_tee; ++ ++#endif /* HAL_OTP_DEFINE_H */ +diff --git a/product/security_subsys/otp/src/arch/include/hal_otp.h b/product/security_subsys/otp/src/arch/include/hal_otp.h +new file mode 100644 +index 0000000..fb670ea +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/include/hal_otp.h +@@ -0,0 +1,49 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef HAL_OTP_H ++#define HAL_OTP_H ++ ++#include "ot_common_otp.h" ++#include "otp_data.h" ++#include "hal_otp_comm.h" ++ ++#define OTP_WAIT_TIME_OUT 10000 ++ ++#define OTP_USER_KEY_MAX_BYTES 32 ++#define OTP_USER_KEY_MAX_WORDS (OTP_USER_KEY_MAX_BYTES / 4) ++ ++/* get lock status */ ++td_s32 hal_otp_get_key_lock_sta(td_u32 offset, td_bool *lock_sta); ++td_s32 hal_otp_get_flag_lock_sta(td_u32 offset, td_bool *lock_sta); ++td_s32 hal_otp_get_user_data_lock_sta(td_u32 index, td_bool *lock_sta); ++ ++/* operate user data */ ++td_s32 hal_otp_set_user_data_word(td_u32 index, td_u32 value, td_bool lock); ++td_s32 hal_otp_get_user_data_word(td_u32 index, td_u32 *value); ++ ++/* operate key */ ++td_s32 hal_otp_load_key_to_klad(td_u32 key_index); ++td_s32 hal_otp_verify_key(td_u32 key_index, td_bool *flag); ++td_s32 hal_otp_burn_key(const otp_data_item *data_item, const td_u8 *key, td_u32 klen); ++ ++/* enable flag */ ++td_s32 hal_otp_enable_flag(const otp_data_item *data_item, td_u32 flag, td_bool lock); ++ ++#endif /* HAL_OTP_H */ +diff --git a/product/security_subsys/otp/src/arch/include/otp_data.h b/product/security_subsys/otp/src/arch/include/otp_data.h +new file mode 100644 +index 0000000..dc9ba7d +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/include/otp_data.h +@@ -0,0 +1,52 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OTP_DATA_H ++#define OTP_DATA_H ++ ++#include "ot_type.h" ++#include "ot_common_otp.h" ++ ++/* otp attr */ ++#define OTP_ATTR_BURN_KEY 0x00000001 /* burn key */ ++#define OTP_ATTR_VERIFY_KEY 0x00000002 /* verify key */ ++#define OTP_ATTR_LOAD_KEY 0x00000004 /* load key */ ++ ++#define OTP_ATTR_SPECIFY_FLAG 0x00000100 /* otp specify flag */ ++#define OTP_ATTR_DATA_FLAG_LOCKABLE 0x00000200 /* otp user data flag lockable */ ++#define OTP_ATTR_DATA_FLAG_ONEWARY 0x00000400 /* otp user data flag oneway */ ++ ++#define OTP_ATTR_REE_USER_DATA 0x00010000 /* ree user self-define data */ ++#define OTP_ATTR_TEE_DATA_REE_READ 0x00020000 /* tee user data, ree can read, not write or lock */ ++#define OTP_ATTR_TEE_USER_DATA 0x00040000 /* tee user self-define data, ree can't access */ ++ ++typedef struct { ++ td_char field_name[OT_OTP_PV_NAME_MAX_LEN]; /* OTP fuse name */ ++ /* offset point to bit offset for the user data or point to index for the key and specify flag */ ++ td_u32 offset; ++ td_u32 bit_width; /* OTP bit width */ ++ td_bool small_endian; /* OTP data is stored in big-endian or small-endian mode */ ++ td_u32 attr; /* OTP fuse name attribution */ ++} otp_data_item; ++ ++otp_data_item *otp_get_data_item(td_void); ++ ++td_u32 otp_get_data_item_num(td_void); ++ ++#endif /* OTP_DATA_H */ +diff --git a/product/security_subsys/otp/src/arch/ss524v100/build.mak b/product/security_subsys/otp/src/arch/ss524v100/build.mak +new file mode 100644 +index 0000000..6716ff1 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss524v100/build.mak +@@ -0,0 +1,9 @@ ++ ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/arch/$(INTER_DRV)/ ++ ++DRV_OBJS += arch/$(INTER_DRV)/otp_data.o ++ ++OT_OTP_VERSION := v100 ++ ++# cfg cpu type, ree cpu maybe read tee data, tee cpu can operate ree data ++OTP_CFLAGS += -DOT_OTP_REE_CPU +diff --git a/product/security_subsys/otp/src/arch/ss524v100/otp_data.c b/product/security_subsys/otp/src/arch/ss524v100/otp_data.c +new file mode 100644 +index 0000000..79c070d +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss524v100/otp_data.c +@@ -0,0 +1,52 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "otp_data.h" ++ ++otp_data_item g_otp_data_item[] = { ++ /* Key operation, support burn, verify, load */ ++ {"aes_key0", 0, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"aes_key1", 1, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"aes_key2", 2, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"aes_key3", 3, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"jtag_pw_id", 4, 64, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"jtag_pw", 5, 128, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"root_key_sha256", 6, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ ++ /* Specify flag operation, support write, not read */ ++ {"secure_boot_en", 0, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"ddr_ca_en", 1, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"jtag_ca_en", 2, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"jtag_prt_mode", 3, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"uboot_redundance", 5, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ ++ /* offset: 0b, 0word; bit_width: 28672b, 896word */ ++ {"user_reserved_data", 0, 28672, TD_TRUE, OTP_ATTR_REE_USER_DATA}, ++}; ++ ++otp_data_item *otp_get_data_item(td_void) ++{ ++ return g_otp_data_item; ++} ++ ++td_u32 otp_get_data_item_num(td_void) ++{ ++ return sizeof(g_otp_data_item) / sizeof(g_otp_data_item[0]); ++} ++ +diff --git a/product/security_subsys/otp/src/arch/ss524v100/otp_reg_base.h b/product/security_subsys/otp/src/arch/ss524v100/otp_reg_base.h +new file mode 100644 +index 0000000..edb5376 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss524v100/otp_reg_base.h +@@ -0,0 +1,51 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OTP_REG_BASE_H ++#define OTP_REG_BASE_H ++ ++#define OTP_REG_BASE_ADDR_PHY 0x10200000 ++#define OTP_REG_BASE_ADDR_SIZE 0x1000 ++ ++#define REG_SYS_OTP_CLK_ADDR_PHY 0x11013240 ++#define OTP_CRG_CLOCK_BIT (0x01 << 4) ++ ++#define OTP_USER_WORK_MODE 0x0000 ++#define OTP_USER_OP_START 0x0004 ++#define OTP_USER_KEY_INDEX 0x0008 ++#define OTP_USER_KEY_DATA0 0x000c ++#define OTP_USER_KEY_DATA1 0x0010 ++#define OTP_USER_KEY_DATA2 0x0014 ++#define OTP_USER_KEY_DATA3 0x0018 ++#define OTP_USER_KEY_DATA4 0x001c ++#define OTP_USER_KEY_DATA5 0x0020 ++#define OTP_USER_KEY_DATA6 0x0024 ++#define OTP_USER_KEY_DATA7 0x0028 ++#define OTP_USER_KEY_DATA8 0x002c ++#define OTP_USER_FLAG_VALUE 0x0030 ++#define OTP_USER_FLAG_INDEX 0x0034 ++#define OTP_USER_REV_ADDR 0x0038 ++#define OTP_USER_REV_WDATA 0x003c ++#define OTP_USER_REV_RDATA 0x0040 ++#define OTP_USER_LOCK_STA0 0x0044 ++#define OTP_USER_LOCK_STA1 0x0048 ++#define OTP_USER_CTRL_STA 0x004c ++#define OTP_OP_START_VAL 0x1acce551 ++ ++#endif /* OTP_REG_BASE_H */ +diff --git a/product/security_subsys/otp/src/arch/ss528v100/build.mak b/product/security_subsys/otp/src/arch/ss528v100/build.mak +new file mode 100644 +index 0000000..6716ff1 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss528v100/build.mak +@@ -0,0 +1,9 @@ ++ ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/arch/$(INTER_DRV)/ ++ ++DRV_OBJS += arch/$(INTER_DRV)/otp_data.o ++ ++OT_OTP_VERSION := v100 ++ ++# cfg cpu type, ree cpu maybe read tee data, tee cpu can operate ree data ++OTP_CFLAGS += -DOT_OTP_REE_CPU +diff --git a/product/security_subsys/otp/src/arch/ss528v100/otp_data.c b/product/security_subsys/otp/src/arch/ss528v100/otp_data.c +new file mode 100644 +index 0000000..79c070d +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss528v100/otp_data.c +@@ -0,0 +1,52 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "otp_data.h" ++ ++otp_data_item g_otp_data_item[] = { ++ /* Key operation, support burn, verify, load */ ++ {"aes_key0", 0, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"aes_key1", 1, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"aes_key2", 2, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"aes_key3", 3, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY | OTP_ATTR_LOAD_KEY}, ++ {"jtag_pw_id", 4, 64, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"jtag_pw", 5, 128, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"root_key_sha256", 6, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ ++ /* Specify flag operation, support write, not read */ ++ {"secure_boot_en", 0, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"ddr_ca_en", 1, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"jtag_ca_en", 2, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"jtag_prt_mode", 3, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"uboot_redundance", 5, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ ++ /* offset: 0b, 0word; bit_width: 28672b, 896word */ ++ {"user_reserved_data", 0, 28672, TD_TRUE, OTP_ATTR_REE_USER_DATA}, ++}; ++ ++otp_data_item *otp_get_data_item(td_void) ++{ ++ return g_otp_data_item; ++} ++ ++td_u32 otp_get_data_item_num(td_void) ++{ ++ return sizeof(g_otp_data_item) / sizeof(g_otp_data_item[0]); ++} ++ +diff --git a/product/security_subsys/otp/src/arch/ss528v100/otp_reg_base.h b/product/security_subsys/otp/src/arch/ss528v100/otp_reg_base.h +new file mode 100644 +index 0000000..edb5376 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss528v100/otp_reg_base.h +@@ -0,0 +1,51 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OTP_REG_BASE_H ++#define OTP_REG_BASE_H ++ ++#define OTP_REG_BASE_ADDR_PHY 0x10200000 ++#define OTP_REG_BASE_ADDR_SIZE 0x1000 ++ ++#define REG_SYS_OTP_CLK_ADDR_PHY 0x11013240 ++#define OTP_CRG_CLOCK_BIT (0x01 << 4) ++ ++#define OTP_USER_WORK_MODE 0x0000 ++#define OTP_USER_OP_START 0x0004 ++#define OTP_USER_KEY_INDEX 0x0008 ++#define OTP_USER_KEY_DATA0 0x000c ++#define OTP_USER_KEY_DATA1 0x0010 ++#define OTP_USER_KEY_DATA2 0x0014 ++#define OTP_USER_KEY_DATA3 0x0018 ++#define OTP_USER_KEY_DATA4 0x001c ++#define OTP_USER_KEY_DATA5 0x0020 ++#define OTP_USER_KEY_DATA6 0x0024 ++#define OTP_USER_KEY_DATA7 0x0028 ++#define OTP_USER_KEY_DATA8 0x002c ++#define OTP_USER_FLAG_VALUE 0x0030 ++#define OTP_USER_FLAG_INDEX 0x0034 ++#define OTP_USER_REV_ADDR 0x0038 ++#define OTP_USER_REV_WDATA 0x003c ++#define OTP_USER_REV_RDATA 0x0040 ++#define OTP_USER_LOCK_STA0 0x0044 ++#define OTP_USER_LOCK_STA1 0x0048 ++#define OTP_USER_CTRL_STA 0x004c ++#define OTP_OP_START_VAL 0x1acce551 ++ ++#endif /* OTP_REG_BASE_H */ +diff --git a/product/security_subsys/otp/src/arch/ss928v100/build.mak b/product/security_subsys/otp/src/arch/ss928v100/build.mak +new file mode 100644 +index 0000000..2a6f429 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss928v100/build.mak +@@ -0,0 +1,11 @@ ++ ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/arch/$(INTER_DRV)/ ++ ++DRV_OBJS += arch/$(INTER_DRV)/otp_data.o ++ ++OT_OTP_VERSION := v200 ++ ++# OTP_SECURE_CPU: force cpu to tee ++# OTP_SWITCH_CPU: switch ree or tee cpu ++# else default ree cpu ++OTP_CFLAGS += -DOTP_SWITCH_CPU +diff --git a/product/security_subsys/otp/src/arch/ss928v100/otp_data.c b/product/security_subsys/otp/src/arch/ss928v100/otp_data.c +new file mode 100644 +index 0000000..1ca781a +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss928v100/otp_data.c +@@ -0,0 +1,93 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "otp_data.h" ++ ++otp_data_item g_otp_data_item[] = { ++ /* Key operation, support burn, verify */ ++ {"oem_root_public_key_sha256", 2, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"tp_root_public_key_sha256", 4, 256, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"oem_root_symc_key0", 6, 128, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"oem_root_symc_key1", 7, 128, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"oem_root_symc_key2", 8, 128, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ {"oem_root_symc_key3", 9, 128, TD_TRUE, OTP_ATTR_BURN_KEY | OTP_ATTR_VERIFY_KEY}, ++ ++ /* Specify flag operation, support write, not read */ ++ {"tee_owner_sel", 4, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"oem_rk_deob_en", 5, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"jtag_key_sel0", 6, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"jtag_key_sel1", 7, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"sec_ds_enable", 8, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"acpu_ds_enable", 9, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"uboot_redundance", 11, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"otp_pcie_disable", 12, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"otp_pcie_ep_boot_disable", 13, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ {"bload_dec_en", 14, 1, TD_TRUE, OTP_ATTR_SPECIFY_FLAG}, ++ ++ /* User data flag operation, support write & read, can't lock */ ++ {"update_from_uart_disable", 0, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"update_from_sdio_disable", 1, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"update_from_usbdev_disable", 2, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"scs_dbg_disable", 3, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"oem_cw_crc_rd_disable", 8, 8, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"func_jtag_prt_mode", 16, 8, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"soc_jtag_prt_mode", 24, 8, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"uart0_disable", 32, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"uart1_disable", 33, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"uart2_disable", 34, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"uart3_disable", 35, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"uart4_disable", 36, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"uart5_disable", 37, 1, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"oem_version", 64, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ {"third_party_version", 96, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_ONEWARY}, ++ ++ /* User data flag operation, support write & read, can lock */ ++ {"soc_tee_enable", 512, 8, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"oem_root_symc_key0_flag", 544, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"oem_root_symc_key1_flag", 576, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"oem_root_symc_key2_flag", 608, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"oem_root_symc_key3_flag", 640, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"secure_boot_en", 672, 8, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"double_sign_en", 704, 4, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"gsl_dec_en", 768, 4, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"quick_boot", 800, 4, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"oem_msid", 832, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"third_party_msid", 864, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ {"tee_msid", 896, 32, TD_TRUE, OTP_ATTR_DATA_FLAG_LOCKABLE}, ++ ++ /* offset: 1536b, 48word; bit_width: 16384b, 512word */ ++ {"ree_user_data", 1536, 16384, TD_TRUE, OTP_ATTR_REE_USER_DATA}, ++ ++ /* offset: 17920b, 560word; bit_width: 128b, 4word, can't be locked */ ++ {"tee_sec_version", 17920, 128, TD_TRUE, OTP_ATTR_TEE_DATA_REE_READ}, ++ ++ /* offset: 18048b, 564word; bit_width: 9088b, 284word */ ++ {"tee_user_data", 18048, 9088, TD_TRUE, OTP_ATTR_TEE_USER_DATA}, ++}; ++ ++otp_data_item *otp_get_data_item(td_void) ++{ ++ return g_otp_data_item; ++} ++ ++td_u32 otp_get_data_item_num(td_void) ++{ ++ return sizeof(g_otp_data_item) / sizeof(g_otp_data_item[0]); ++} ++ +diff --git a/product/security_subsys/otp/src/arch/ss928v100/otp_reg_base.h b/product/security_subsys/otp/src/arch/ss928v100/otp_reg_base.h +new file mode 100644 +index 0000000..bd002c4 +--- /dev/null ++++ b/product/security_subsys/otp/src/arch/ss928v100/otp_reg_base.h +@@ -0,0 +1,53 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OTP_REG_BASE_H ++#define OTP_REG_BASE_H ++ ++#define OTP_REG_BASE_ADDR_PHY 0x10122000 ++#define OTP_REG_BASE_ADDR_SIZE 0x2000 ++#define OTP_TEE_REG_OFFSET 0x1000 ++ ++extern td_u32 g_tee_reg_offset; ++ ++#define OTP_USER_WORK_MODE (g_tee_reg_offset + 0x0000) ++#define OTP_USER_REV_ADDR (g_tee_reg_offset + 0x0038) ++#define OTP_USER_REV_WDATA (g_tee_reg_offset + 0x003c) ++#define OTP_USER_REV_RDATA (g_tee_reg_offset + 0x0040) ++#define OTP_USER_LOCK_STA_ADDR (g_tee_reg_offset + 0x0044) ++#define OTP_USER_LOCK_STA_DATA (g_tee_reg_offset + 0x0048) ++#define OTP_USER_CTRL_STA (g_tee_reg_offset + 0x004c) ++ ++#define OTP_USER_OP_START 0x0004 ++#define OTP_USER_KEY_INDEX 0x0008 ++#define OTP_USER_KEY_DATA0 0x000c ++#define OTP_USER_KEY_DATA1 0x0010 ++#define OTP_USER_KEY_DATA2 0x0014 ++#define OTP_USER_KEY_DATA3 0x0018 ++#define OTP_USER_KEY_DATA4 0x001c ++#define OTP_USER_KEY_DATA5 0x0020 ++#define OTP_USER_KEY_DATA6 0x0024 ++#define OTP_USER_KEY_DATA7 0x0028 ++#define OTP_USER_KEY_DATA8 0x002c ++#define OTP_USER_FLAG_VALUE 0x0030 ++#define OTP_USER_FLAG_INDEX 0x0034 ++ ++#define OTP_OP_START_VAL 0x1acce551 ++ ++#endif /* OTP_REG_BASE_H */ +diff --git a/product/security_subsys/otp/src/include/drv_ioctl_otp.h b/product/security_subsys/otp/src/include/drv_ioctl_otp.h +new file mode 100644 +index 0000000..fafebe7 +--- /dev/null ++++ b/product/security_subsys/otp/src/include/drv_ioctl_otp.h +@@ -0,0 +1,143 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_IOCTL_OTP_H ++#define DRV_IOCTL_OTP_H ++ ++#include ++#include "ot_common_otp.h" ++#include "ot_common.h" ++#include "mkp_ioctl.h" ++#include "dev_ext.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* word index number. */ ++#define WORD_IDX_0 0 ++#define WORD_IDX_1 1 ++#define WORD_IDX_2 2 ++#define WORD_IDX_3 3 ++#define WORD_IDX_4 4 ++#define WORD_IDX_5 5 ++#define WORD_IDX_6 6 ++#define WORD_IDX_7 7 ++ ++/* byte bit width */ ++#define BYTE_BIT_WIDTH 8 ++ ++/* word byte width */ ++#define WORD_BYTE_WIDTH 4 ++ ++#define WORD_BIT_WIDTH 32 ++ ++/* byte offset */ ++#define OFFSET_0_BYTE 0 ++#define OFFSET_1_BYTE 8 ++#define OFFSET_2_BYTE 16 ++#define OFFSET_3_BYTE 24 ++ ++/* Boundary value 1. */ ++#define BOUND_VAL_1 1 ++ ++/* multiple value */ ++#define MUL_VAL_1 1 ++#define MUL_VAL_2 2 ++#define MUL_VAL_3 3 ++#define MUL_VAL_4 4 ++ ++#define OTP_PRODUCT_PV_MAX_NUM 500 ++ ++/* otp user data max 16k bytes */ ++#define OTP_USER_DATA_MAX_SIZE 0x4000 ++ ++#define rem(value, base) ((value) % (base)) ++#define round(value, base) ((value) / (base)) ++ ++#define bit_mask(width) \ ++ (((width) == WORD_BIT_WIDTH) ? 0xFFFFFFFFU : ~(0xFFFFFFFFU << (width))) ++ ++#define data_bit_mask(value, offset, width) \ ++ (((value) & bit_mask(width)) << (rem(offset, WORD_BIT_WIDTH))) ++ ++#define data_bit_restore(value, offset, width) \ ++ (((value) >> rem(offset, WORD_BIT_WIDTH)) & bit_mask(width)) ++ ++#define align_word(offset) (((offset) & 0x03) == 0) ++#define align_16_bytes(offset) (((offset) & 0x0f) == 0) ++#define word_number(byte) \ ++ ((rem(byte, WORD_BYTE_WIDTH) == 0) ? round(byte, WORD_BYTE_WIDTH) : (round(byte, WORD_BYTE_WIDTH) + 1)) ++ ++#define byte_number(bit) \ ++ ((rem(bit, BYTE_BIT_WIDTH) == 0) ? round(bit, BYTE_BIT_WIDTH) : (round(bit, BYTE_BIT_WIDTH) + 1)) ++ ++#define otp_array_size(x) (sizeof(x) / sizeof((x)[0])) ++ ++/* ----------- IOCTL STRUCT CONFIG ----------- */ ++typedef struct { ++ td_char field_name[OT_OTP_PV_NAME_MAX_LEN]; ++ td_u32 offset; ++ td_u8 *value; ++ td_u32 value_len; ++} otp_user_data; ++ ++typedef struct { ++ ot_otp_burn_pv_item *pv; ++ td_u32 num; ++} otp_product_pv; ++ ++typedef struct { ++ td_char field_name[OT_OTP_PV_NAME_MAX_LEN]; ++ td_bool status; ++} otp_key_verify_status; ++ ++typedef struct { ++ td_char field_name[OT_OTP_PV_NAME_MAX_LEN]; ++ td_u32 offset; ++ td_u32 value_len; ++ ot_otp_lock_status lock; ++} otp_user_data_lock; ++ ++/* ----------- IOCTL CMD CONFIG ----------- */ ++typedef enum { ++ OTP_IOC_NR_SET_USER_DATA, ++ OTP_IOC_NR_GET_USER_DATA, ++ OTP_IOC_NR_SET_USER_DATA_LOCK, ++ OTP_IOC_NR_GET_USER_DATA_LOCK, ++ OTP_IOC_NR_BURN_PRODUCT_PV, ++ OTP_IOC_NR_READ_PRODUCT_PV, ++ OTP_IOC_NR_KEY_VERIFY, ++ OTP_IOC_NR_BUTT, ++} otp_ioc_nr; ++ ++#define CMD_OTP_SET_USER_DATA _IOW (IOC_TYPE_OTP, OTP_IOC_NR_SET_USER_DATA, otp_user_data) ++#define CMD_OTP_GET_USER_DATA _IOWR(IOC_TYPE_OTP, OTP_IOC_NR_GET_USER_DATA, otp_user_data) ++#define CMD_OTP_SET_USER_DATA_LOCK _IOW (IOC_TYPE_OTP, OTP_IOC_NR_SET_USER_DATA_LOCK, otp_user_data_lock) ++#define CMD_OTP_GET_USER_DATA_LOCK _IOWR(IOC_TYPE_OTP, OTP_IOC_NR_GET_USER_DATA_LOCK, otp_user_data_lock) ++#define CMD_OTP_BURN_PRODUCT_PV _IOW (IOC_TYPE_OTP, OTP_IOC_NR_BURN_PRODUCT_PV, otp_product_pv) ++#define CMD_OTP_READ_PRODUCT_PV _IOWR(IOC_TYPE_OTP, OTP_IOC_NR_READ_PRODUCT_PV, otp_product_pv) ++#define CMD_OTP_KEY_VERIFY _IOWR(IOC_TYPE_OTP, OTP_IOC_NR_KEY_VERIFY, otp_key_verify_status) ++#define CMD_OTP_COUNT OTP_IOC_NR_BUTT ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* DRV_IOCTL_OTP_H */ +diff --git a/product/security_subsys/otp/src/include/drv_lib.h b/product/security_subsys/otp/src/include/drv_lib.h +new file mode 100644 +index 0000000..76a9314 +--- /dev/null ++++ b/product/security_subsys/otp/src/include/drv_lib.h +@@ -0,0 +1,43 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_LIB_H ++#define DRV_LIB_H ++ ++#include "ot_type.h" ++ ++td_void ot_otp_print_arr_u8(const td_char *name, const td_u8 *msg, td_u32 msg_len); ++ ++td_void ot_otp_print_arr_u32(const td_char *name, const td_u32 *msg, td_u32 msg_len); ++ ++td_s32 ot_otp_word_big_endian(const td_u32 word_val, td_u8 *byte_buf, td_u32 len); ++ ++td_u16 ot_otp_crc16_modbus(const td_u8 *msg, td_u32 msg_len, td_u32 klen); ++ ++td_s32 otp_copy_from_user(td_void *to, unsigned long to_len, const td_void *from, unsigned long from_len); ++ ++td_s32 otp_copy_to_user(td_void *to, unsigned long to_len, const td_void *from, unsigned long from_len); ++ ++extern int check_otp_cmd_mode(void); ++ ++td_s32 otp_get_cpu_secure_sta(td_void); ++ ++td_bool otp_is_secure_cpu(td_void); ++ ++#endif /* DRV_LIB_H */ +diff --git a/product/security_subsys/otp/src/include/ot_debug_otp.h b/product/security_subsys/otp/src/include/ot_debug_otp.h +new file mode 100644 +index 0000000..83ce08c +--- /dev/null ++++ b/product/security_subsys/otp/src/include/ot_debug_otp.h +@@ -0,0 +1,67 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef OT_DEBUG_OTP_H ++#define OT_DEBUG_OTP_H ++ ++#include "ot_type.h" ++#include "ot_debug.h" ++ ++#define OT_OTP_LVL_ERR 0 ++#define OT_OTP_LVL_WARN 1 ++#define OT_OTP_LVL_INFO 2 ++#define OT_OTP_LVL_LOG 3 ++ ++#define otp_trace(level, msg, fmt, ...) \ ++ do { \ ++ if (OT_OTP_DEBUG >= level) { \ ++ OT_PRINT("[%4s Line:%04d Func:%s] "fmt, msg, __LINE__, __FUNCTION__, ##__VA_ARGS__); \ ++ } \ ++ } while (0) ++ ++#define ot_otp_error(fmt, ...) otp_trace(OT_OTP_LVL_ERR, "ERR", fmt, ##__VA_ARGS__) ++#if OT_OTP_DEBUG ++#define ot_otp_warn(fmt, ...) otp_trace(OT_OTP_LVL_WARN, "WARN", fmt, ##__VA_ARGS__) ++#define ot_otp_info(fmt, ...) otp_trace(OT_OTP_LVL_INFO, "INFO", fmt, ##__VA_ARGS__) ++#define ot_otp_enter() otp_trace(OT_OTP_LVL_LOG, "LOG", "enter--->\n") ++#define ot_otp_exit() otp_trace(OT_OTP_LVL_LOG, "LOG", "exit <---\n") ++#else ++#define ot_otp_warn(fmt, ...) ++#define ot_otp_info(fmt, ...) ++#define ot_otp_enter() ++#define ot_otp_exit() ++#endif ++ ++#define ot_otp_func_fail_return(_func, _formula, _errno) \ ++ do { \ ++ if (_formula) { \ ++ ot_otp_error("call %s failed, ret = 0x%08X\n", # _func, _errno); \ ++ return _errno; \ ++ } \ ++ } while (0) ++ ++#define ot_otp_formula_fail_return(_formula, _errno) \ ++ do { \ ++ if (_formula) { \ ++ ot_otp_error("%s is invalid, ret = 0x%08X\n", # _formula, _errno); \ ++ return _errno; \ ++ } \ ++ } while (0) ++ ++#endif /* OT_DEBUG_OTP_H */ +diff --git a/product/security_subsys/otp/src/mkp/build.mak b/product/security_subsys/otp/src/mkp/build.mak +new file mode 100644 +index 0000000..d865e9e +--- /dev/null ++++ b/product/security_subsys/otp/src/mkp/build.mak +@@ -0,0 +1,3 @@ ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/mkp ++ ++DRV_OBJS += mkp/drv_otp.o +diff --git a/product/security_subsys/otp/src/mkp/drv_otp.c b/product/security_subsys/otp/src/mkp/drv_otp.c +new file mode 100644 +index 0000000..a4b2efc +--- /dev/null ++++ b/product/security_subsys/otp/src/mkp/drv_otp.c +@@ -0,0 +1,542 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_otp.h" ++#include "drv_lib.h" ++#include "drv_osal_init.h" ++#include "securec.h" ++#include "hal_otp.h" ++#include "ot_debug_otp.h" ++#include "drv_ioctl_otp.h" ++ ++#define OEM_ROOT_SYMC_KEY_NAME_LEN 18 ++#define OEM_ROOT_SYMC_KEY0 "oem_root_symc_key0" ++#define OEM_ROOT_SYMC_KEY1 "oem_root_symc_key1" ++#define OEM_ROOT_SYMC_KEY2 "oem_root_symc_key2" ++#define OEM_ROOT_SYMC_KEY3 "oem_root_symc_key3" ++ ++typedef struct { ++ td_u32 offset; ++ td_u8 *value; ++ td_u32 value_len; ++ td_u32 *word_value; ++ td_u32 word_len; ++ td_u32 byte_len; ++} drv_data_param_s; ++ ++typedef enum { ++ DRV_DATA_TYPE_ONEWAY, ++ DRV_DATA_TYPE_LOCKABLE, ++} drv_data_type_e; ++ ++static otp_data_item *_drv_otp_match_field_name(const td_char *field_name) ++{ ++ td_u32 i; ++ otp_data_item *item = otp_get_data_item(); ++ ++ for (i = 0; i < otp_get_data_item_num(); i++) { ++ if (strncmp(field_name, item[i].field_name, strlen(item[i].field_name) + 1) != 0) { ++ continue; ++ } ++ ++ ot_otp_info("match %d field name %s\n", i, field_name); ++ return &item[i]; ++ } ++ ot_otp_error("field name %s don't match\n", field_name); ++ return TD_NULL; ++} ++ ++static td_s32 _drv_otp_chk_data_param(const otp_data_item *item, td_u32 offset, td_u32 value_len) ++{ ++ /* offset must word align */ ++ ot_otp_formula_fail_return(!align_word(offset), OT_ERR_OTP_INVALID_PARAM); ++ ++ /* avoid a + b overflow */ ++ ot_otp_formula_fail_return(offset + value_len < value_len, OT_ERR_OTP_INVALID_PARAM); ++ ++ /* check user name real max size: offset + value_len can't longer than max size */ ++ ot_otp_formula_fail_return(offset + value_len > byte_number(item->bit_width), OT_ERR_OTP_INVALID_PARAM); ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 _drv_otp_init_data_param(drv_data_param_s *user_data, ++ const otp_data_item *item, td_u32 offset, td_u8 *value, td_u32 value_len) ++{ ++ td_s32 ret; ++ ++ ret = _drv_otp_chk_data_param(item, offset, value_len); ++ ot_otp_func_fail_return(_drv_otp_chk_data_param, ret != TD_SUCCESS, ret); ++ ++ (td_void)memset_s(user_data, sizeof(drv_data_param_s), 0, sizeof(drv_data_param_s)); ++ user_data->offset = offset; ++ user_data->value = value; ++ user_data->value_len = value_len; ++ user_data->word_len = word_number(value_len); ++ user_data->byte_len = user_data->word_len * WORD_BYTE_WIDTH; ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 _drv_otp_set_user_data(drv_data_param_s *user_data, const otp_data_item *item) ++{ ++ td_u32 i, word_offset; ++ td_s32 ret; ++ ++ ret = memcpy_s((td_u8 *)user_data->word_value, user_data->byte_len, ++ user_data->value, user_data->value_len); ++ ot_otp_func_fail_return(memcpy_s, ret != EOK, OT_ERR_OTP_FAILED_SEC_FUNC); ++ ++ ot_otp_print_arr_u32("set user data", user_data->word_value, user_data->word_len); ++ ++ /* tee cpu can set ree data, ree cpu can't set tee data */ ++ if ((item->attr & OTP_ATTR_REE_USER_DATA) || (otp_is_secure_cpu() && ++ ((item->attr & OTP_ATTR_TEE_DATA_REE_READ) || (item->attr & OTP_ATTR_TEE_USER_DATA)))) { ++ for (i = 0; i < user_data->word_len; i++) { ++ /* user data word offset is sum of the input offset address plus the data offset address */ ++ word_offset = (user_data->offset + byte_number(item->offset)) / WORD_BYTE_WIDTH + i; ++ ++ ret = hal_otp_set_user_data_word(word_offset, user_data->word_value[i], TD_FALSE); ++ ot_otp_func_fail_return(hal_otp_set_user_data_word, ret != TD_SUCCESS, ret); ++ } ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 _drv_otp_get_user_data(drv_data_param_s *user_data, const otp_data_item *item) ++{ ++ td_u32 i, word_offset; ++ td_s32 ret; ++ ++ /* tee cpu can get ree data, ree cpu only get tee data which allowed */ ++ if ((item->attr & OTP_ATTR_REE_USER_DATA) || (item->attr & OTP_ATTR_TEE_DATA_REE_READ) || ++ (otp_is_secure_cpu() && (item->attr & OTP_ATTR_TEE_USER_DATA))) { ++ for (i = 0; i < user_data->word_len; i++) { ++ /* user data word offset is sum of the input offset address plus the data offset address */ ++ word_offset = (user_data->offset + byte_number(item->offset)) / WORD_BYTE_WIDTH + i; ++ ++ ret = hal_otp_get_user_data_word(word_offset, &user_data->word_value[i]); ++ ot_otp_func_fail_return(hal_otp_get_user_data_word, ret != TD_SUCCESS, ret); ++ } ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ ++ ret = memcpy_s(user_data->value, user_data->value_len, (td_u8 *)user_data->word_value, user_data->value_len); ++ ot_otp_func_fail_return(memcpy_s, ret != EOK, OT_ERR_OTP_FAILED_SEC_FUNC); ++ ++ return TD_SUCCESS; ++} ++ ++#ifdef OT_OTP_V200 ++static td_s32 _drv_otp_burn_data_flag(const ot_otp_burn_pv_item *pv_item, ++ const otp_data_item *data_item, drv_data_type_e type) ++{ ++ td_s32 ret; ++ td_u32 value = 0; ++ ++ /* data flag length can't be larger than 4bytes */ ++ ot_otp_formula_fail_return(pv_item->value_len > WORD_BIT_WIDTH, OT_ERR_OTP_INVALID_PARAM); ++ ot_otp_formula_fail_return(data_item->bit_width > WORD_BIT_WIDTH, OT_ERR_OTP_INVALID_PARAM); ++ ++ ret = memcpy_s((td_u8 *)&value, sizeof(td_u32), pv_item->value, byte_number(pv_item->value_len)); ++ ot_otp_func_fail_return(memcpy_s, ret != EOK, OT_ERR_OTP_FAILED_SEC_FUNC); ++ ++ value = data_bit_mask(value, data_item->offset, data_item->bit_width); ++ ot_otp_info("data flag offset %u, data %08x\n", data_item->offset, value); ++ ++ if (type == DRV_DATA_TYPE_ONEWAY) { ++ ret = hal_otp_set_user_data_word(data_item->offset / WORD_BIT_WIDTH, value, TD_FALSE); ++ ot_otp_func_fail_return(hal_otp_set_user_data_word, ret != TD_SUCCESS, ret); ++ } else { /* DRV_DATA_TYPE_LOCKABLE */ ++ td_bool lock; ++ ret = hal_otp_get_user_data_lock_sta(data_item->offset / WORD_BIT_WIDTH, &lock); ++ ot_otp_func_fail_return(hal_otp_get_user_data_lock_sta, ret != TD_SUCCESS, ret); ++ if (lock == TD_TRUE) { ++ ot_otp_error("flag %s had locked\n", pv_item->field_name); ++ return OT_ERR_OTP_ZONE_ALREADY_SET; ++ } ++ ++ ret = hal_otp_set_user_data_word(data_item->offset / WORD_BIT_WIDTH, value, pv_item->lock); ++ ot_otp_func_fail_return(hal_otp_set_user_data_word, ret != TD_SUCCESS, ret); ++ } ++ ++ return TD_SUCCESS; ++} ++ ++static td_s32 _drv_otp_read_data_flag(ot_otp_burn_pv_item *pv_item, ++ const otp_data_item *data_item, drv_data_type_e type) ++{ ++ td_s32 ret; ++ td_u32 value; ++ ++ /* data flag length can't be larger than 4bytes */ ++ ot_otp_formula_fail_return(pv_item->value_len > WORD_BIT_WIDTH, OT_ERR_OTP_INVALID_PARAM); ++ ot_otp_formula_fail_return(data_item->bit_width > WORD_BIT_WIDTH, OT_ERR_OTP_INVALID_PARAM); ++ ++ if (type == DRV_DATA_TYPE_LOCKABLE) { ++ ret = hal_otp_get_user_data_lock_sta(data_item->offset / WORD_BIT_WIDTH, &pv_item->lock); ++ ot_otp_func_fail_return(hal_otp_get_user_data_lock_sta, ret != TD_SUCCESS, ret); ++ } ++ ++ ret = hal_otp_get_user_data_word(data_item->offset / WORD_BIT_WIDTH, &value); ++ ot_otp_func_fail_return(hal_otp_get_user_data_word, ret != TD_SUCCESS, ret); ++ ++ value = data_bit_restore(value, data_item->offset, data_item->bit_width); ++ ot_otp_info("data flag offset %u, data %08x, lock_sta %d\n", data_item->offset, value, pv_item->lock); ++ ++ ret = memcpy_s(pv_item->value, byte_number(pv_item->value_len), ++ (td_u8 *)&value, byte_number(data_item->bit_width)); ++ ot_otp_func_fail_return(memcpy_s, ret != EOK, OT_ERR_OTP_FAILED_SEC_FUNC); ++ ++ return TD_SUCCESS; ++} ++ ++static td_void _drv_otp_cfg_user_data_lock(td_bool lock_sta, td_u32 index, ot_otp_lock_status *lock) ++{ ++ /* init lock value when index == 0, index = 0 must be called */ ++ if (index == 0) { ++ if (lock_sta == TD_TRUE) { ++ *lock = OT_OTP_STA_ALL_LOCKED; ++ } else { ++ *lock = OT_OTP_STA_ALL_UNLOCKED; ++ } ++ } else { ++ if ((lock_sta == TD_TRUE && *lock == OT_OTP_STA_ALL_UNLOCKED) || ++ (lock_sta == TD_FALSE && *lock == OT_OTP_STA_ALL_LOCKED)) { ++ *lock = OT_OTP_STA_PARTIAL_LOCKED; ++ } ++ } ++} ++#endif ++ ++td_s32 drv_otp_init(void) ++{ ++ td_s32 ret; ++ ++ ret = hal_otp_init(); ++ ot_otp_func_fail_return(hal_otp_init, ret != TD_SUCCESS, ret); ++ ++ return TD_SUCCESS; ++} ++ ++td_void drv_otp_deinit(void) ++{ ++ hal_otp_deinit(); ++} ++ ++td_s32 drv_otp_set_user_data(const td_char *field_name, ++ td_u32 offset, const td_u8 *value, td_u32 value_len) ++{ ++ td_s32 ret; ++ const otp_data_item *item = TD_NULL; ++ drv_data_param_s user_data; ++ ++ ot_otp_enter(); ++ ++ /* match field name */ ++ item = _drv_otp_match_field_name(field_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ++ ret = _drv_otp_init_data_param(&user_data, item, offset, (td_u8 *)value, value_len); ++ ot_otp_func_fail_return(_drv_otp_init_data_param, ret != TD_SUCCESS, ret); ++ ++ user_data.word_value = otp_malloc(user_data.byte_len); ++ ot_otp_func_fail_return(otp_malloc, user_data.word_value == TD_NULL, OT_ERR_OTP_FAILED_MEM); ++ (td_void)memset_s(user_data.word_value, user_data.byte_len, 0, user_data.byte_len); ++ ++ ret = _drv_otp_set_user_data(&user_data, item); ++ ++ otp_free(user_data.word_value); ++ ++ ot_otp_exit(); ++ ++ return ret; ++} ++ ++td_s32 drv_otp_get_user_data(const td_char *field_name, ++ td_u32 offset, td_u8 *value, td_u32 value_len) ++{ ++ td_s32 ret; ++ const otp_data_item *item = TD_NULL; ++ drv_data_param_s user_data; ++ ++ ot_otp_enter(); ++ ++ /* match field name */ ++ item = _drv_otp_match_field_name(field_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ++ ret = _drv_otp_init_data_param(&user_data, item, offset, value, value_len); ++ ot_otp_func_fail_return(_drv_otp_init_data_param, ret != TD_SUCCESS, ret); ++ ++ user_data.word_value = otp_malloc(user_data.byte_len); ++ ot_otp_func_fail_return(otp_malloc, user_data.word_value == TD_NULL, OT_ERR_OTP_FAILED_MEM); ++ (td_void)memset_s(user_data.word_value, user_data.byte_len, 0, user_data.byte_len); ++ ++ ret = _drv_otp_get_user_data(&user_data, item); ++ ++ otp_free(user_data.word_value); ++ ++ ot_otp_exit(); ++ return ret; ++} ++ ++td_s32 drv_otp_set_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len) ++{ ++#ifdef OT_OTP_V200 ++ td_s32 ret; ++ td_u32 i, word_offset, value; ++ td_bool lock_sta; ++ const otp_data_item *item = TD_NULL; ++ ++ item = _drv_otp_match_field_name(field_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ++ ret = _drv_otp_chk_data_param(item, offset, value_len); ++ ot_otp_func_fail_return(_drv_otp_chk_data_param, ret != TD_SUCCESS, ret); ++ ++ /* tee cpu can set ree data lock, ree cpu can't set tee data lock */ ++ if ((item->attr == OTP_ATTR_REE_USER_DATA) || ++ (otp_is_secure_cpu() && (item->attr == OTP_ATTR_TEE_USER_DATA))) { ++ for (i = 0; i < word_number(value_len); i++) { ++ /* user data word offset is sum of the input offset address plus the data offset address */ ++ word_offset = round(offset + byte_number(item->offset), WORD_BYTE_WIDTH) + i; ++ ++ /* get word offset lock status */ ++ ret = hal_otp_get_user_data_lock_sta(word_offset, &lock_sta); ++ ot_otp_func_fail_return(hal_otp_get_user_data_lock_sta, ret != TD_SUCCESS, ret); ++ if (lock_sta == TD_TRUE) { ++ ot_otp_warn("user data %u had locked\n", word_offset); ++ continue; ++ } ++ ++ /* get word offset value */ ++ ret = hal_otp_get_user_data_word(word_offset, &value); ++ ot_otp_func_fail_return(hal_otp_set_user_data_word, ret != TD_SUCCESS, ret); ++ ++ /* lock word offset value */ ++ ret = hal_otp_set_user_data_word(word_offset, value, TD_TRUE); ++ ot_otp_func_fail_return(hal_otp_set_user_data_word, ret != TD_SUCCESS, ret); ++ } ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ return TD_SUCCESS; ++#else ++ ot_unused(field_name); ++ ot_unused(offset); ++ ot_unused(value_len); ++ ot_otp_error("Unsupported set user data lock!\n"); ++ return OT_ERR_OTP_FUNC_UNSUPPORT; ++#endif ++} ++ ++td_s32 drv_otp_get_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len, ot_otp_lock_status *lock) ++{ ++#ifdef OT_OTP_V200 ++ td_s32 ret; ++ td_u32 i, word_offset; ++ td_bool lock_sta; ++ const otp_data_item *item = TD_NULL; ++ ot_otp_lock_status tmp_lock = OT_OTP_STA_ALL_UNLOCKED; ++ ++ item = _drv_otp_match_field_name(field_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ++ ret = _drv_otp_chk_data_param(item, offset, value_len); ++ ot_otp_func_fail_return(_drv_otp_chk_data_param, ret != TD_SUCCESS, ret); ++ ++ /* tee cpu can set gee data lock, ree cpu can't get tee data lock */ ++ if ((item->attr == OTP_ATTR_REE_USER_DATA) || ++ (otp_is_secure_cpu() && (item->attr == OTP_ATTR_TEE_USER_DATA))) { ++ for (i = 0; i < word_number(value_len); i++) { ++ /* user data word offset is sum of the input offset address plus the data offset address */ ++ word_offset = round(offset + byte_number(item->offset), WORD_BYTE_WIDTH) + i; ++ ++ /* get word offset lock status */ ++ ret = hal_otp_get_user_data_lock_sta(word_offset, &lock_sta); ++ ot_otp_func_fail_return(hal_otp_get_user_data_lock_sta, ret != TD_SUCCESS, ret); ++ ++ /* get lock status */ ++ _drv_otp_cfg_user_data_lock(lock_sta, i, &tmp_lock); ++ } ++ *lock = tmp_lock; ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ ++ return ret; ++#else ++ ot_unused(field_name); ++ ot_unused(offset); ++ ot_unused(value_len); ++ ot_unused(lock); ++ ot_otp_error("Unsupported set user data lock!\n"); ++ return OT_ERR_OTP_FUNC_UNSUPPORT; ++#endif ++} ++ ++static td_bool drv_otp_check_field_name(const ot_otp_burn_pv_item *pv) ++{ ++ td_s32 value_len = sizeof(pv->value) / sizeof(pv->value[0]); ++ td_s32 i; ++ if (strcmp(pv->field_name, OEM_ROOT_SYMC_KEY0) == 0 || ++ strcmp(pv->field_name, OEM_ROOT_SYMC_KEY1) == 0 || ++ strcmp(pv->field_name, OEM_ROOT_SYMC_KEY2) == 0 || ++ strcmp(pv->field_name, OEM_ROOT_SYMC_KEY3) == 0) { ++ for (i = 0; i < value_len; i++) { ++ if (pv->value[i] != 0x00) { ++ return TD_TRUE; ++ } ++ } ++ return TD_FALSE; ++ } else { ++ return TD_TRUE; ++ } ++} ++ ++td_s32 drv_otp_burn_product_pv(const ot_otp_burn_pv_item *pv, td_u32 num) ++{ ++ td_s32 ret; ++ td_u32 i; ++ const otp_data_item *item = TD_NULL; ++ ++ for (i = 0; i < num; i++) { ++ item = _drv_otp_match_field_name(pv[i].field_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ /* check k0-k3 is all 0 */ ++ ot_otp_formula_fail_return(drv_otp_check_field_name(&pv[i]) == TD_FALSE, OT_ERR_OTP_INVALID_PARAM); ++ ++ /* value_len must is equal to byte len */ ++ ot_otp_formula_fail_return(pv[i].value_len != item->bit_width, OT_ERR_OTP_INVALID_PARAM); ++ ++ if (item->attr & OTP_ATTR_BURN_KEY) { ++ ret = hal_otp_burn_key(item, pv[i].value, byte_number(pv[i].value_len)); ++ ot_otp_func_fail_return(hal_otp_burn_key, ret != TD_SUCCESS, ret); ++ } else if (item->attr & OTP_ATTR_SPECIFY_FLAG) { ++ ret = hal_otp_enable_flag(item, pv[i].value[0], pv[i].lock); ++ ot_otp_func_fail_return(hal_otp_enable_flag, ret != TD_SUCCESS, ret); ++#ifdef OT_OTP_V200 ++ } else if (item->attr & OTP_ATTR_DATA_FLAG_ONEWARY) { ++ ret = _drv_otp_burn_data_flag(&pv[i], item, DRV_DATA_TYPE_ONEWAY); ++ ot_otp_func_fail_return(_drv_otp_burn_data_flag, ret != TD_SUCCESS, ret); ++ } else if (item->attr & OTP_ATTR_DATA_FLAG_LOCKABLE) { ++ ret = _drv_otp_burn_data_flag(&pv[i], item, DRV_DATA_TYPE_LOCKABLE); ++ ot_otp_func_fail_return(_drv_otp_burn_data_flag, ret != TD_SUCCESS, ret); ++#endif ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ } ++ return TD_SUCCESS; ++} ++ ++td_s32 drv_otp_read_product_pv(ot_otp_burn_pv_item *pv, td_u32 num) ++{ ++ td_u32 i; ++ td_s32 ret; ++ const otp_data_item *item = TD_NULL; ++ ++ for (i = 0; i < num; i++) { ++ item = _drv_otp_match_field_name(pv[i].field_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ++ /* clean pv[i].value data */ ++ (td_void)memset_s(pv[i].value, sizeof(pv[i].value), 0, sizeof(pv[i].value)); ++ ++ if (item->attr & OTP_ATTR_BURN_KEY) { ++ /* read burn key only get lock flag, value_len can be 0 */ ++ ret = hal_otp_get_key_lock_sta(item->offset, &pv[i].lock); ++ ot_otp_func_fail_return(hal_otp_get_key_lock_sta, ret != TD_SUCCESS, ret); ++ } else if (item->attr & OTP_ATTR_SPECIFY_FLAG) { ++ /* read specify flag only get lock flag, value_len can be 0 */ ++ ret = hal_otp_get_flag_lock_sta(item->offset, &pv[i].lock); ++ ot_otp_func_fail_return(hal_otp_get_flag_lock_sta, ret != TD_SUCCESS, ret); ++#ifdef OT_OTP_V200 ++ } else if (item->attr & OTP_ATTR_DATA_FLAG_ONEWARY) { ++ /* get user data flag, value_len must is equal to byte len */ ++ ot_otp_formula_fail_return(pv[i].value_len != item->bit_width, OT_ERR_OTP_INVALID_PARAM); ++ ++ ret = _drv_otp_read_data_flag(&pv[i], item, DRV_DATA_TYPE_ONEWAY); ++ ot_otp_func_fail_return(_drv_otp_read_data_flag, ret != TD_SUCCESS, ret); ++ } else if (item->attr & OTP_ATTR_DATA_FLAG_LOCKABLE) { ++ /* get user data flag, value_len must is equal to byte len */ ++ ot_otp_formula_fail_return(pv[i].value_len != item->bit_width, OT_ERR_OTP_INVALID_PARAM); ++ ++ ret = _drv_otp_read_data_flag(&pv[i], item, DRV_DATA_TYPE_LOCKABLE); ++ ot_otp_func_fail_return(_drv_otp_read_data_flag, ret != TD_SUCCESS, ret); ++#endif ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ } ++ return TD_SUCCESS; ++} ++ ++td_s32 drv_otp_get_key_verify_status(const td_char *key_name, td_bool *status) ++{ ++ td_s32 ret; ++ const otp_data_item *item = TD_NULL; ++ ++ item = _drv_otp_match_field_name(key_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ++ if (item->attr & OTP_ATTR_VERIFY_KEY) { ++ ret = hal_otp_verify_key(item->offset, status); ++ ot_otp_func_fail_return(hal_otp_verify_key, ret != TD_SUCCESS, ret); ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ ++ return TD_SUCCESS; ++} ++ ++#ifdef OT_OTP_V100 ++td_s32 drv_otp_load_key_to_klad(const td_char *key_name) ++{ ++ td_s32 ret; ++ const otp_data_item *item = TD_NULL; ++ ++ item = _drv_otp_match_field_name(key_name); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, item == TD_NULL, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ++ if (item->attr & OTP_ATTR_LOAD_KEY) { ++ ret = hal_otp_load_key_to_klad(item->offset); ++ ot_otp_func_fail_return(_drv_otp_match_field_name, ret != TD_SUCCESS, ret); ++ } else { ++ ot_otp_error("%s don't match attribution %x\n", item->field_name, item->attr); ++ return OT_ERR_OTP_INVALID_FIELD_NAME; ++ } ++ ++ return ret; ++} ++#endif +diff --git a/product/security_subsys/otp/src/mkp/drv_otp.h b/product/security_subsys/otp/src/mkp/drv_otp.h +new file mode 100644 +index 0000000..976e4cd +--- /dev/null ++++ b/product/security_subsys/otp/src/mkp/drv_otp.h +@@ -0,0 +1,49 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_OTP_H ++#define DRV_OTP_H ++ ++#include "ot_common_otp.h" ++ ++td_s32 drv_otp_init(void); ++ ++td_void drv_otp_deinit(void); ++ ++td_s32 drv_otp_set_user_data(const td_char *field_name, ++ td_u32 offset, const td_u8 *value, td_u32 value_len); ++ ++td_s32 drv_otp_get_user_data(const td_char *field_name, ++ td_u32 offset, td_u8 *value, td_u32 value_len); ++ ++td_s32 drv_otp_burn_product_pv(const ot_otp_burn_pv_item *pv, td_u32 num); ++ ++td_s32 drv_otp_read_product_pv(ot_otp_burn_pv_item *pv, td_u32 num); ++ ++td_s32 drv_otp_get_key_verify_status(const td_char *key_name, td_bool *status); ++ ++td_s32 drv_otp_load_key_to_klad(const td_char *key_name); ++ ++td_s32 drv_otp_set_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len); ++ ++td_s32 drv_otp_get_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len, ot_otp_lock_status *lock); ++ ++#endif /* DRV_OTP_H */ +diff --git a/product/security_subsys/otp/src/mpi/build.mak b/product/security_subsys/otp/src/mpi/build.mak +new file mode 100644 +index 0000000..0526986 +--- /dev/null ++++ b/product/security_subsys/otp/src/mpi/build.mak +@@ -0,0 +1,3 @@ ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/mpi ++ ++MPI_OBJS += mpi/mpi_otp.o +diff --git a/product/security_subsys/otp/src/mpi/mpi_otp.c b/product/security_subsys/otp/src/mpi/mpi_otp.c +new file mode 100644 +index 0000000..70ba5b1 +--- /dev/null ++++ b/product/security_subsys/otp/src/mpi/mpi_otp.c +@@ -0,0 +1,176 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include "ot_mpi_otp.h" ++ ++#include ++#include "securec.h" ++#include "ot_type.h" ++#include "drv_ioctl_otp.h" ++#include "ot_debug_otp.h" ++#include "drv_otp.h" ++#include "drv_lib.h" ++ ++static td_bool g_otp_init_flag = TD_FALSE; ++ ++ ++#define LOG printf ++ ++#define crypto_chk_return(cond, err_ret, fmt, ...) do { \ ++ if (cond) { \ ++ LOG(fmt, ##__VA_ARGS__); \ ++ return err_ret; \ ++ } \ ++} while (0) ++ ++#define crypto_otp_not_init_return() do { \ ++ if (g_otp_init_flag != TD_TRUE) { \ ++ LOG("Error: otp not init\n"); \ ++ return OT_ERR_OTP_NOT_INIT; \ ++ } \ ++} while (0) ++ ++td_s32 ot_mpi_otp_init(td_void) ++{ ++ td_s32 ret = TD_SUCCESS; ++ if (g_otp_init_flag == TD_TRUE) { ++ return TD_SUCCESS; ++ } ++ (td_void)otp_get_cpu_secure_sta(); ++ ret = drv_otp_init(); ++ crypto_chk_return(ret != TD_SUCCESS, ret, "drv_otp_init failed, ret is 0x%x\n", ret); ++ ++ g_otp_init_flag = TD_TRUE; ++ return ret; ++} ++ ++td_s32 ot_mpi_otp_deinit(td_void) ++{ ++ if (g_otp_init_flag == TD_FALSE) { ++ return TD_FAILURE; ++ } ++ drv_otp_deinit(); ++ g_otp_init_flag = TD_FALSE; ++ return TD_SUCCESS; ++} ++ ++td_s32 ot_mpi_otp_set_user_data(const td_char *field_name, ++ td_u32 offset, const td_u8 *value, td_u32 value_len) ++{ ++ crypto_otp_not_init_return(); ++ ++ ot_otp_formula_fail_return(field_name == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return( ++ strlen(field_name) >= OT_OTP_PV_NAME_MAX_LEN, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ot_otp_formula_fail_return(value == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return(value_len == 0, OT_ERR_OTP_INVALID_PARAM); ++ ++ return drv_otp_set_user_data(field_name, offset, value, value_len); ++} ++ ++td_s32 ot_mpi_otp_get_user_data(const td_char *field_name, ++ td_u32 offset, td_u8 *value, td_u32 value_len) ++{ ++ crypto_otp_not_init_return(); ++ ++ ot_otp_formula_fail_return(field_name == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return( ++ strlen(field_name) >= OT_OTP_PV_NAME_MAX_LEN, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ot_otp_formula_fail_return(value == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return(value_len == 0, OT_ERR_OTP_INVALID_PARAM); ++ ++ return drv_otp_get_user_data(field_name, offset, value, value_len); ++} ++ ++td_s32 ot_mpi_otp_set_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len) ++{ ++ crypto_otp_not_init_return(); ++ ++ ot_otp_formula_fail_return(field_name == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return( ++ strlen(field_name) >= OT_OTP_PV_NAME_MAX_LEN, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ot_otp_formula_fail_return(value_len == 0, OT_ERR_OTP_INVALID_PARAM); ++ ++ return drv_otp_set_user_data_lock(field_name, offset, value_len); ++} ++ ++td_s32 ot_mpi_otp_get_user_data_lock(const td_char *field_name, ++ td_u32 offset, td_u32 value_len, ot_otp_lock_status *lock) ++{ ++ crypto_otp_not_init_return(); ++ ++ ot_otp_formula_fail_return(field_name == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return( ++ strlen(field_name) >= OT_OTP_PV_NAME_MAX_LEN, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ot_otp_formula_fail_return(value_len == 0, OT_ERR_OTP_INVALID_PARAM); ++ ot_otp_formula_fail_return(lock == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ++ return drv_otp_get_user_data_lock(field_name, offset, value_len, lock); ++} ++ ++td_s32 ot_mpi_otp_burn_product_pv(const ot_otp_burn_pv_item *pv, td_u32 num) ++{ ++ td_s32 i; ++ crypto_otp_not_init_return(); ++ ++ ot_otp_formula_fail_return(pv == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return((num == 0) || (num > OTP_PRODUCT_PV_MAX_NUM), OT_ERR_OTP_INVALID_PARAM); ++ ++ for (i = 0; i < num; i++) { ++ ot_otp_formula_fail_return(pv[i].burn != TD_TRUE, OT_ERR_OTP_INVALID_PARAM); ++ /* burn pv value_len couldn't be 0 */ ++ ot_otp_formula_fail_return(pv[i].value_len == 0, OT_ERR_OTP_INVALID_PARAM); ++ ot_otp_formula_fail_return( ++ pv[i].value_len > (OT_OTP_PV_VALUE_MAX_LEN * BYTE_BIT_WIDTH), OT_ERR_OTP_INVALID_PARAM); ++ ot_otp_formula_fail_return(pv[i].lock != TD_TRUE && pv[i].lock != TD_FALSE, OT_ERR_OTP_INVALID_PARAM); ++ } ++ ++ return drv_otp_burn_product_pv(pv, num); ++} ++ ++td_s32 ot_mpi_otp_read_product_pv(ot_otp_burn_pv_item *pv, td_u32 num) ++{ ++ td_s32 i; ++ ++ crypto_otp_not_init_return(); ++ ++ ot_otp_formula_fail_return(pv == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return((num == 0) || (num > OTP_PRODUCT_PV_MAX_NUM), OT_ERR_OTP_INVALID_PARAM); ++ ++ for (i = 0; i < num; i++) { ++ ot_otp_formula_fail_return(pv[i].burn == TD_TRUE, OT_ERR_OTP_INVALID_PARAM); ++ /* read pv value_len could be 0 */ ++ ot_otp_formula_fail_return( ++ pv[i].value_len > (OT_OTP_PV_VALUE_MAX_LEN * BYTE_BIT_WIDTH), OT_ERR_OTP_INVALID_PARAM); ++ } ++ ++ return drv_otp_read_product_pv(pv, num); ++} ++ ++td_s32 ot_mpi_otp_get_key_verify_status(const td_char *key_name, td_bool *status) ++{ ++ crypto_otp_not_init_return(); ++ ++ ot_otp_formula_fail_return(key_name == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return( ++ strlen(key_name) >= OT_OTP_PV_NAME_MAX_LEN, OT_ERR_OTP_INVALID_FIELD_NAME); ++ ot_otp_formula_fail_return(status == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ++ return drv_otp_get_key_verify_status(key_name, status); ++} +diff --git a/product/security_subsys/otp/src/osal/build.mak b/product/security_subsys/otp/src/osal/build.mak +new file mode 100644 +index 0000000..bcda27d +--- /dev/null ++++ b/product/security_subsys/otp/src/osal/build.mak +@@ -0,0 +1,3 @@ ++OTP_CFLAGS += -I$(OTP_BASE_DIR)/osal/ ++ ++DRV_OBJS += osal/drv_lib.o +diff --git a/product/security_subsys/otp/src/osal/drv_lib.c b/product/security_subsys/otp/src/osal/drv_lib.c +new file mode 100644 +index 0000000..7f54867 +--- /dev/null ++++ b/product/security_subsys/otp/src/osal/drv_lib.c +@@ -0,0 +1,185 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include "drv_lib.h" ++#include "drv_ioctl_otp.h" ++#include "linux/compat.h" ++#include "ot_debug_otp.h" ++#if defined(OTP_SWITCH_CPU) || defined(OTP_SECURE_CPU) ++#include "otp_reg_base.h" ++#endif ++ ++#ifdef OTP_SWITCH_CPU ++static td_bool s_otp_secure_cpu = TD_FALSE; ++#endif ++ ++#ifdef OTP_TEE_REG_OFFSET ++td_u32 g_tee_reg_offset = 0; ++#endif ++ ++td_void ot_otp_print_arr_u8(const td_char *name, const td_u8 *msg, td_u32 msg_len) ++{ ++#if OT_OTP_DEBUG ++ td_u32 i; ++ ++ if (name != TD_NULL) { ++ OT_PRINT("%s: \n", name); ++ } ++ ++ for (i = 0; i < msg_len; i++) { ++ if (i != 0 && align_16_bytes(i)) { ++ OT_PRINT("\n"); ++ } ++ OT_PRINT("%02X ", msg[i]); ++ } ++ OT_PRINT("\n"); ++#else ++ ot_unused(name); ++ ot_unused(msg); ++ ot_unused(msg_len); ++#endif ++} ++ ++td_void ot_otp_print_arr_u32(const td_char *name, const td_u32 *msg, td_u32 msg_len) ++{ ++#if OT_OTP_DEBUG ++ td_u32 i; ++ ++ if (name != TD_NULL) { ++ OT_PRINT("%s: \n", name); ++ } ++ ++ for (i = 0; i < msg_len; i++) { ++ if (i != 0 && align_16_bytes(i)) { ++ OT_PRINT("\n"); ++ } ++ OT_PRINT("%08X ", msg[i]); ++ } ++ OT_PRINT("\n"); ++#else ++ ot_unused(name); ++ ot_unused(msg); ++ ot_unused(msg_len); ++#endif ++} ++ ++td_s32 ot_otp_word_big_endian(const td_u32 word_val, td_u8 *byte_buf, td_u32 len) ++{ ++ ot_otp_formula_fail_return(len != WORD_BYTE_WIDTH, OT_ERR_OTP_INVALID_PARAM); ++ ++ byte_buf[WORD_IDX_0] = (word_val >> OFFSET_3_BYTE) & 0xff; ++ byte_buf[WORD_IDX_1] = (word_val >> OFFSET_2_BYTE) & 0xff; ++ byte_buf[WORD_IDX_2] = (word_val >> OFFSET_1_BYTE) & 0xff; ++ byte_buf[WORD_IDX_3] = (word_val >> OFFSET_0_BYTE) & 0xff; ++ ++ return TD_SUCCESS; ++} ++ ++td_u16 ot_otp_crc16_modbus(const td_u8 *msg, td_u32 msg_len, td_u32 klen) ++{ ++ td_u32 i; ++ td_u16 crc_in = 0xFFFF; ++ const td_u16 crc_poly = 0x8005; ++ td_u8 crc_char; ++ ++ while (klen-- != 0) { ++ crc_char = *(msg++); ++ crc_in ^= (crc_char << OFFSET_1_BYTE); ++ for (i = 0; i < BYTE_BIT_WIDTH; i++) { ++ if (crc_in & 0x8000) { ++ crc_in = (crc_in << 1) ^ crc_poly; ++ } else { ++ crc_in = crc_in << 1; ++ } ++ } ++ } ++ ot_unused(msg_len); ++ return crc_in; ++} ++ ++td_s32 otp_copy_from_user(td_void *to, unsigned long to_len, ++ const td_void *from, unsigned long from_len) ++{ ++ td_s32 ret; ++ if (from_len == 0) { ++ return TD_SUCCESS; ++ } ++ ++ ot_otp_formula_fail_return(to == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return(from == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return(from_len > to_len, OT_ERR_OTP_INVALID_PARAM); ++ ++ ret = copy_from_user(to, from, from_len); ++ ot_otp_func_fail_return(copy_from_user, ret != TD_SUCCESS, OT_ERR_OTP_INVALID_PARAM); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 otp_copy_to_user(td_void *to, unsigned long to_len, ++ const td_void *from, unsigned long from_len) ++{ ++ td_s32 ret; ++ if (from_len == 0) { ++ return TD_SUCCESS; ++ } ++ ++ ot_otp_formula_fail_return(to == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return(from == TD_NULL, OT_ERR_OTP_NULL_PTR); ++ ot_otp_formula_fail_return(from_len > to_len, OT_ERR_OTP_INVALID_PARAM); ++ ++ ret = copy_from_user(to, from, from_len); ++ ot_otp_func_fail_return(copy_from_user, ret != TD_SUCCESS, OT_ERR_OTP_INVALID_PARAM); ++ ++ return TD_SUCCESS; ++} ++ ++td_s32 otp_get_cpu_secure_sta(td_void) ++{ ++#if defined(OTP_SECURE_CPU) ++#ifdef OTP_TEE_REG_OFFSET ++ g_tee_reg_offset = OTP_TEE_REG_OFFSET; ++#endif ++#elif defined(OTP_SWITCH_CPU) ++ if (check_otp_cmd_mode()) { ++ s_otp_secure_cpu = TD_TRUE; ++#ifdef OTP_TEE_REG_OFFSET ++ g_tee_reg_offset = OTP_TEE_REG_OFFSET; ++#endif ++ } else { ++ s_otp_secure_cpu = TD_FALSE; ++ } ++#endif ++ return TD_SUCCESS; ++} ++ ++/* OTP_SECURE_CPU force to tee cpu ++ * OTP_SWITCH_CPU can switch to ree or tee cpu ++ * else default ree cpu ++ */ ++td_bool otp_is_secure_cpu(td_void) ++{ ++#if defined(OTP_SECURE_CPU) ++ return TD_TRUE; ++#elif defined(OTP_SWITCH_CPU) ++ return s_otp_secure_cpu; ++#else ++ return TD_FALSE; ++#endif ++} ++ +diff --git a/product/security_subsys/otp/src/osal/drv_osal_init.h b/product/security_subsys/otp/src/osal/drv_osal_init.h +new file mode 100644 +index 0000000..6cab676 +--- /dev/null ++++ b/product/security_subsys/otp/src/osal/drv_osal_init.h +@@ -0,0 +1,51 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef DRV_OSAL_INIT_H ++#define DRV_OSAL_INIT_H ++ ++#include ++#include ++#include ++#include ++ ++#define OTP_MUTEX_T td_s32 ++#define otp_mutex_init(x) (*(x) = *(x)) ++#define otp_mutex_lock(x) ++#define otp_mutex_unlock(x) ++#define otp_mutex_destroy(x) ++ ++#define otp_ioremap_nocache(addr, size) (td_void*)(addr) ++#define otp_iounmap(addr, size) ++ ++#define otp_malloc(x) malloc(x) ++#define otp_free(x) \ ++ do { \ ++ if (x) { \ ++ free(x); \ ++ x = TD_NULL; \ ++ } \ ++ } while (0) ++ ++#define otp_write(addr, data) writel(data, addr) ++#define otp_read(addr) readl(addr) ++ ++#define otp_udelay(x) udelay(x) ++ ++#endif /* DRV_OSAL_INIT_H */ +diff --git a/product/update/Kconfig b/product/update/Kconfig +new file mode 100644 +index 0000000..4ad377f +--- /dev/null ++++ b/product/update/Kconfig +@@ -0,0 +1,17 @@ ++config AUTO_UPDATE ++ bool "auto update" ++ select FS_FAT ++ ++config AUTO_SD_UPDATE ++ bool "auto sd update" ++ depends on AUTO_UPDATE ++ ++config AUTO_USB_UPDATE ++ bool "auto usb update" ++ select USB ++ select USB_GADGET ++ depends on AUTO_UPDATE ++ ++config AUTO_UPDATE_ADAPTATION ++ bool "auto update adaptation" ++ depends on AUTO_UPDATE +diff --git a/product/update/Makefile b/product/update/Makefile +new file mode 100644 +index 0000000..78cf629 +--- /dev/null ++++ b/product/update/Makefile +@@ -0,0 +1,8 @@ ++sinclude $(TOPDIR)/config.mk ++ ++ifdef CONFIG_AUTO_UPDATE_ADAPTATION ++obj-y += auto_update_adaptation.o ++else ++obj-y += auto_update.o ++endif ++ +diff --git a/product/update/auto_update.c b/product/update/auto_update.c +new file mode 100644 +index 0000000..4f7ea45 +--- /dev/null ++++ b/product/update/auto_update.c +@@ -0,0 +1,577 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if (CONFIG_AUTO_UPDATE == 1) /* cover the whole file */ ++ ++#ifdef CONFIG_AUTO_SD_UPDATE ++#ifndef CONFIG_MMC ++#error "should have defined CONFIG_MMC" ++#endif ++#include ++#include "mmc_init.c" ++#endif ++ ++#if defined CONFIG_AUTO_USB_UPDATE ++#if !defined CONFIG_USB_OHCI && !defined CONFIG_USB_XHCI ++#error "should have defined CONFIG_USB_OHCI or CONFIG_USB_XHCI" ++#endif ++#ifndef CONFIG_USB_STORAGE ++#error "should have defined CONFIG_USB_STORAGE" ++#endif ++#include ++#include "usb_init.c" ++#endif ++ ++#undef AU_DEBUG ++#undef debug ++#ifdef AU_DEBUG ++#define debug(fmt, args...) printf(fmt, ##args) ++#else ++#define debug(fmt, args...) ++#endif /* AU_DEBUG */ ++ ++/* possible names of files on the medium. */ ++#define AU_FIRMWARE "u-boot" ++#define AU_KERNEL "kernel" ++#define AU_ROOTFS "rootfs" ++ ++#define NAME_LEN 20 ++#define ENV_LEN 20 ++#define PERCENT 100 ++#define HEX 16 ++#define COUNT 3 ++#define MMC2 2 ++#define MAX_HZ 1000000UL ++#define SPI_MODE3 3 ++ ++struct flash_layout { ++ long start; ++ long end; ++}; ++static struct spi_flash *flash; ++ ++struct medium_interface { ++ char name[NAME_LEN]; ++ int (*init)(void); ++ void (*exit)(void); ++}; ++ ++#define MAX_UPDATE_INTF 3 ++static struct medium_interface s_intf[MAX_UPDATE_INTF] = { ++#ifdef CONFIG_AUTO_SD_UPDATE ++ {.name = "mmc", .init = mmc_stor_init, .exit = mmc_stor_exit, }, ++#endif ++#ifdef CONFIG_AUTO_USB_UPDATE ++ {.name = "usb", .init = usb_stor_init, .exit = usb_stor_exit, }, ++#endif ++}; ++ ++/* layout of the FLASH. ST = start address, ND = end address. */ ++#define AU_FL_FIRMWARE_ST 0x0 ++#define AU_FL_FIRMWARE_ND 0x7FFFF ++#define AU_FL_KERNEL_ST 0x100000 ++#define AU_FL_KERNEL_ND 0x5FFFFF ++#define AU_FL_ROOTFS_ST 0x600000 ++#define AU_FL_ROOTFS_ND 0xbFFFFF ++ ++static int au_stor_curr_dev; /* current device */ ++ ++/* index of each file in the following arrays */ ++#define IDX_FIRMWARE 0 ++#define IDX_KERNEL 1 ++#define IDX_ROOTFS 2 ++ ++/* max. number of files which could interest us */ ++#define AU_MAXFILES 3 ++ ++/* pointers to file names */ ++char *aufile[AU_MAXFILES] = { ++ AU_FIRMWARE, ++ AU_KERNEL, ++ AU_ROOTFS ++}; ++ ++/* sizes of flash areas for each file */ ++long ausize[AU_MAXFILES] = { ++ (AU_FL_FIRMWARE_ND + 1) - AU_FL_FIRMWARE_ST, ++ (AU_FL_KERNEL_ND + 1) - AU_FL_KERNEL_ST, ++ (AU_FL_ROOTFS_ND + 1) - AU_FL_ROOTFS_ST, ++}; ++ ++/* array of flash areas start and end addresses */ ++struct flash_layout aufl_layout[AU_MAXFILES] = { ++ { AU_FL_FIRMWARE_ST, AU_FL_FIRMWARE_ND, }, ++ { AU_FL_KERNEL_ST, AU_FL_KERNEL_ND, }, ++ { AU_FL_ROOTFS_ST, AU_FL_ROOTFS_ND, }, ++}; ++ ++/* where to load files into memory */ ++#define LOAD_ADDR ((unsigned char *)0x82000000) ++ ++/* the app is the largest image */ ++#define MAX_LOADSZ ausize[IDX_ROOTFS] ++ ++static int au_check_cksum_valid(int idx, long nbytes) ++{ ++ image_header_t *hdr; ++ unsigned long checksum; ++ ++ hdr = (image_header_t *)LOAD_ADDR; ++ ++ if (nbytes != (sizeof(*hdr) + ntohl(hdr->ih_size))) { ++ printf("Image %s bad total SIZE\n", aufile[idx]); ++ return -1; ++ } ++ /* check the data CRC */ ++ checksum = ntohl(hdr->ih_dcrc); ++ if (crc32(0, (unsigned char const *)(LOAD_ADDR + sizeof(*hdr)), ++ ntohl(hdr->ih_size)) != checksum) { ++ printf("Image %s bad data checksum\n", aufile[idx]); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++void check_valid_debug(image_header_t *hdr, long nbyte) ++{ ++#undef CHECK_VALID_DEBUG ++#ifdef CHECK_VALID_DEBUG ++ printf("\nmagic %#x %#x\n", ntohl(hdr->ih_magic), IH_MAGIC); ++ printf("arch %#x %#x\n", hdr->ih_arch, IH_ARCH_ARM); ++ printf("size %#x %#lx\n", ntohl(hdr->ih_size), nbytes); ++ printf("type %#x %#x\n", hdr->ih_type, IH_TYPE_KERNEL); ++#endif ++} ++ ++static int au_check_header_valid(int idx, long nbytes) ++{ ++ image_header_t *hdr; ++ unsigned long checksum; ++ ++ char env[ENV_LEN]; ++ char auversion[ENV_LEN]; ++ ++ hdr = (image_header_t *)LOAD_ADDR; ++ /* check the easy ones first */ ++ check_valid_debug(hdr, nbytes); ++ if (nbytes < sizeof(*hdr)) { ++ printf("Image %s bad header SIZE\n", aufile[idx]); ++ return -1; ++ } ++ if (ntohl(hdr->ih_magic) != IH_MAGIC || hdr->ih_arch != IH_ARCH_ARM) { ++ printf("Image %s bad MAGIC or ARCH\n", aufile[idx]); ++ return -1; ++ } ++ /* check the hdr CRC */ ++ checksum = ntohl(hdr->ih_hcrc); ++ hdr->ih_hcrc = 0; ++ ++ if (crc32(0, (unsigned char const *)hdr, sizeof(*hdr)) != checksum) { ++ printf("Image %s bad header checksum\n", aufile[idx]); ++ return -1; ++ } ++ hdr->ih_hcrc = htonl(checksum); ++ /* check the type - could do this all in one gigantic if() */ ++ if ((idx == IDX_FIRMWARE) && (hdr->ih_type != IH_TYPE_FIRMWARE)) { ++ printf("Image %s wrong type\n", aufile[idx]); ++ return -1; ++ } ++ if ((idx == IDX_KERNEL) && (hdr->ih_type != IH_TYPE_KERNEL)) { ++ printf("Image %s wrong type\n", aufile[idx]); ++ return -1; ++ } ++ if ((idx == IDX_ROOTFS) && ++ (hdr->ih_type != IH_TYPE_RAMDISK) && ++ (hdr->ih_type != IH_TYPE_FILESYSTEM)) { ++ printf("Image %s wrong type\n", aufile[idx]); ++ ausize[idx] = 0; ++ return -1; ++ } ++ ++ /* recycle checksum */ ++ checksum = ntohl(hdr->ih_size); ++ /* for kernel and app the image header must also fit into flash */ ++ if ((idx == IDX_KERNEL) || (hdr->ih_type == IH_TYPE_RAMDISK)) ++ checksum += sizeof(*hdr); ++ ++ /* check the size does not exceed space in flash. HUSH scripts */ ++ /* all have ausize[] set to 0 */ ++ if ((ausize[idx] != 0) && (ausize[idx] < checksum)) { ++ printf("Image %s is bigger than FLASH\n", aufile[idx]); ++ return -1; ++ } ++ ++ if (!sprintf_s(env, ENV_LEN, "%lx", (unsigned long)ntohl(hdr->ih_time))) { ++ printf("sprintf_s env err!\n"); ++ return -1; ++ } ++ setenv(auversion, env); ++ ++ return 0; ++} ++ ++static void schedule_notify(unsigned long offset, unsigned long len, ++ unsigned long off_start) ++{ ++ int percent_complete = -1; ++ ++ do { ++ unsigned long long n = ++ (unsigned long long)(offset - off_start) * PERCENT; ++ int percent; ++ ++ do_div(n, len); ++ percent = (int)n; ++ ++ /* output progress message only at whole percent ++ * steps to reduce the number of messages ++ * printed on (slow) serial consoles ++ */ ++ if (percent != percent_complete) { ++ percent_complete = percent; ++ ++ printf("\rOperation at 0x%lx -- %3d%% complete", ++ offset, percent); ++ } ++ } while (0); ++} ++ ++static int spi_flash_erase_op(struct spi_flash *flash, unsigned long offset, ++ unsigned long len) ++{ ++ int ret; ++ struct mtd_info_ex *spiflash_info = get_spiflash_info(); ++ unsigned long erase_start, erase_len, erase_step; ++ ++ erase_start = offset; ++ erase_len = len; ++ erase_step = spiflash_info->erasesize; ++ ++ while (len > 0) { ++ if (len < erase_step) ++ erase_step = len; ++ ++ ret = flash->erase(flash, (u32)offset, erase_step); ++ if (ret) ++ return 1; ++ ++ len -= erase_step; ++ offset += erase_step; ++ /* notify real time schedule */ ++ schedule_notify(offset, erase_len, erase_start); ++ } ++ return ret; ++} ++ ++static int spi_flash_write_op(struct spi_flash *flash, unsigned long offset, ++ unsigned long len, char *buf) ++{ ++ int ret; ++ unsigned long write_start, write_len, write_step; ++ char *pbuf = buf; ++ struct mtd_info_ex *spiflash_info = get_spiflash_info(); ++ ++ write_start = offset; ++ write_len = len; ++ write_step = spiflash_info->erasesize; ++ ++ while (len > 0) { ++ if (len < write_step) ++ write_step = len; ++ ++ ret = flash->write(flash, offset, write_step, pbuf); ++ if (ret) ++ break; ++ ++ offset += write_step; ++ pbuf += write_step; ++ len -= write_step; ++ /* notify real time schedule */ ++ schedule_notify(offset, write_len, write_start); ++ } ++ ++ return ret; ++} ++ ++static int au_do_update(int idx, long sz) ++{ ++ image_header_t *hdr; ++ unsigned long start, len; ++ unsigned long write_len; ++ int rc; ++ void *buf; ++ char *pbuf; ++ ++ hdr = (image_header_t *)LOAD_ADDR; ++ ++ start = aufl_layout[idx].start; ++ len = aufl_layout[idx].end - aufl_layout[idx].start + 1; ++ ++ /* ++ * erase the address range. ++ */ ++ printf("flash erase...\n"); ++ rc = spi_flash_erase_op(flash, start, len); ++ if (rc) { ++ printf("SPI flash sector erase failed\n"); ++ return 1; ++ } ++ ++ buf = map_physmem((unsigned long)LOAD_ADDR, len, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ /* strip the header - except for the kernel and ramdisk */ ++ if (hdr->ih_type == IH_TYPE_KERNEL || hdr->ih_type == IH_TYPE_RAMDISK) { ++ pbuf = buf; ++ write_len = sizeof(*hdr) + ntohl(hdr->ih_size); ++ } else { ++ pbuf = (buf + sizeof(*hdr)); ++ write_len = ntohl(hdr->ih_size); ++ } ++ ++ /* copy the data from RAM to FLASH */ ++ printf("\nflash write...\n"); ++ rc = spi_flash_write_op(flash, start, write_len, pbuf); ++ if (rc) { ++ printf("SPI flash write failed, return %d\n", rc); ++ return 1; ++ } ++ ++ /* check the dcrc of the copy */ ++ if (crc32(0, (unsigned char const *)(buf + sizeof(*hdr)), ++ ntohl(hdr->ih_size)) != ntohl(hdr->ih_dcrc)) { ++ printf("Image %s Bad Data Checksum After COPY\n", aufile[idx]); ++ return -1; ++ } ++ ++ unmap_physmem(buf, len); ++ ++ return 0; ++} ++ ++static void get_update_env(char *img_start, char *img_end) ++{ ++ long start = -1; ++ long end = 0; ++ char *env; ++ ++ /* ++ * check whether start and end are defined in environment ++ * variables. ++ */ ++ env = env_get(img_start); ++ if (env != NULL) ++ start = simple_strtoul(env, NULL, HEX); ++ ++ env = env_get(img_end); ++ if (env != NULL) ++ end = simple_strtoul(env, NULL, HEX); ++ ++ if (start >= 0 && end && end > start) { ++ ausize[IDX_FIRMWARE] = (end + 1) - start; ++ aufl_layout[0].start = start; ++ aufl_layout[0].end = end; ++ } ++} ++ ++/* ++ * If none of the update file(u-boot, kernel or rootfs) was found ++ * in the medium, return -1; ++ * If u-boot has been updated, return 1; ++ * Others, return 0; ++ */ ++static int update_to_flash(void) ++{ ++ int i; ++ long sz; ++ int res, cnt; ++ int uboot_updated; ++ int image_found; ++ ++ /* just loop thru all the possible files */ ++ for (i = 0; i < AU_MAXFILES; i++) { ++ /* just read the header */ ++ sz = file_fat_read(aufile[i], LOAD_ADDR, sizeof(image_header_t)); ++ debug("read %s sz %ld hdr %d\n", aufile[i], sz, sizeof(image_header_t)); ++ if (sz <= 0 || sz < sizeof(image_header_t)) { ++ debug("%s not found\n", aufile[i]); ++ continue; ++ } ++ ++ image_found = 1; ++ ++ if (au_check_header_valid(i, sz) < 0) { ++ debug("%s header not valid\n", aufile[i]); ++ continue; ++ } ++ ++ sz = file_fat_read(aufile[i], LOAD_ADDR, MAX_LOADSZ); ++ debug("read %s sz %ld hdr %d\n", aufile[i], sz, sizeof(image_header_t)); ++ if (sz <= 0 || sz <= sizeof(image_header_t)) { ++ debug("%s not found\n", aufile[i]); ++ continue; ++ } ++ ++ if (au_check_cksum_valid(i, sz) < 0) { ++ debug("%s checksum not valid\n", aufile[i]); ++ continue; ++ } ++ ++ /* If u-boot had been updated, we need to ++ * save current env to flash ++ */ ++ if (strcmp((char *)AU_FIRMWARE, aufile[i]) == 0) ++ uboot_updated = 1; ++ ++ /* this is really not a good idea, but it's what the customer wants. */ ++ cnt = 0; ++ do { ++ res = au_do_update(i, sz); ++ /* let the user break out of the loop */ ++ if (ctrlc() || had_ctrlc()) { ++ clear_ctrlc(); ++ ++ break; ++ } ++ cnt++; ++ } while (res < 0); ++ } ++ ++ if (uboot_updated == 1) ++ return 1; ++ ++ if (image_found == 1) ++ return 0; ++ ++ return -1; ++} ++ ++/* ++ * This is called by board_init() after the hardware has been set up ++ * and is usable. Only if SPI flash initialization failed will this function ++ * return -1, otherwise it will return 0; ++ */ ++int do_auto_update(void) ++{ ++ struct blk_desc *stor_dev; ++ int old_ctrlc; ++ int j; ++ int state = -1; ++ int dev; ++ ++ au_stor_curr_dev = -1; ++ for (j = 0; j < MAX_UPDATE_INTF; j++) { ++ if ((unsigned long)s_intf[j].name[0] != 0) { ++ au_stor_curr_dev = s_intf[j].init(); ++ if (au_stor_curr_dev == -1) { ++ debug("No %s storage device found!\n", ++ s_intf[j].name); ++ continue; ++ } ++ ++ dev = 0; ++ ++ debug("device name %s!\n", s_intf[j].name); ++ stor_dev = blk_get_dev(s_intf[j].name, dev); ++ if (stor_dev == NULL) { ++ debug("Unknow device type!\n"); ++ continue; ++ } ++ ++ if (fat_register_device(stor_dev, 1) != 0) { ++ debug("Unable to use %s %d:%d for fatls\n", ++ s_intf[j].name, au_stor_curr_dev, 1); ++ continue; ++ } ++ ++ if (file_fat_detectfs() != 0) { ++ debug("file_fat_detectfs failed\n"); ++ continue; ++ } ++ ++ /* ++ * Get image layout from environment. ++ * If the start address and the end address ++ * were not definedin environment virables, ++ * use the default value ++ */ ++ get_update_env("firmware_st", "firmware_nd"); ++ get_update_env("kernel_st", "kernel_nd"); ++ get_update_env("rootfs_st", "rootfs_nd"); ++ ++ /* ++ * make sure that we see CTRL-C ++ * and save the old state ++ */ ++ old_ctrlc = disable_ctrlc(0); ++ ++ /* ++ * CONFIG_SF_DEFAULT_SPEED equals 1000000, ++ * CONFIG_SF_DEFAULT_MODE equals 0x3 ++ */ ++ flash = spi_flash_probe(0, 0, MAX_HZ, SPI_MODE3); ++ if (!flash) { ++ printf("Failed to initialize SPI flash\n"); ++ return -1; ++ } ++ ++ state = update_to_flash(); ++ ++ /* restore the old state */ ++ disable_ctrlc(old_ctrlc); ++ ++ s_intf[j].exit(); ++ ++ /* ++ * no update file found ++ */ ++ if (state == -1) ++ continue; ++ /* ++ * update files have been found on current medium, ++ * so just break here ++ */ ++ break; ++ } ++ } ++ ++ /* ++ * If u-boot has been updated, it's better to save environment to flash ++ */ ++ if (state == 1) ++ saveenv(); ++ return 0; ++} ++#endif /* CONFIG_AUTO_UPDATE */ +diff --git a/product/update/auto_update.h b/product/update/auto_update.h +new file mode 100644 +index 0000000..3df3b9c +--- /dev/null ++++ b/product/update/auto_update.h +@@ -0,0 +1,28 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#ifndef __AUTO_UPDATE__H ++#define __AUTO_UPDATE__H ++ ++int do_auto_update(void); ++void *get_target_dev_value(void); ++void *get_target_paratition_value(void); ++void select_upgrade_media(void); ++void *get_dos_start_lba_value(void); ++#endif +diff --git a/product/update/auto_update_adaptation.c b/product/update/auto_update_adaptation.c +new file mode 100644 +index 0000000..25b4a80 +--- /dev/null ++++ b/product/update/auto_update_adaptation.c +@@ -0,0 +1,1848 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "image-sparse.h" ++#include ++ ++#if (CONFIG_AUTO_UPDATE == 1) /* cover the whole file */ ++ ++#if (CONFIG_AUTO_SD_UPDATE == 1) ++#ifndef CONFIG_MMC ++#error "should have defined CONFIG_MMC" ++#endif ++#include ++#include "mmc_init.c" ++#endif ++ ++#if defined CONFIG_AUTO_USB_UPDATE ++#ifndef CONFIG_VENDOR_MC ++#if !defined CONFIG_USB_OHCI && !defined CONFIG_USB_XHCI_HCD ++#error "should have defined CONFIG_USB_OHCI or CONFIG_USB_XHCI" ++#endif ++#endif ++#ifndef CONFIG_USB_STORAGE ++#error "should have defined CONFIG_USB_STORAGE" ++#endif ++#include ++#include "usb_init.c" ++#endif ++#include "../../fs/ext4/unsparse.h" ++ ++#undef AU_DEBUG ++#undef debug_print ++#ifdef AU_DEBUG ++#define debug_print(fmt, args...) printf(fmt, ##args) ++#else ++#define debug_print(fmt, args...) ++#endif /* AU_DEBUG */ ++ ++/* possible names of files on the medium. */ ++#define AU_CONFIG "config" ++/* config file's size < 1K */ ++#define CONFIG_MAX_SIZE 2048 ++#define BLOCK_SIZE 512 ++#define STR_LEN 80 ++#define LINE 16 ++#define EMMC_BLOCK_SHIFT 9 ++#define NAME_LEN 20 ++#define SIZE_M (1024 * 1024) ++#define SIZE_K 1024 ++#define DECI_VALUE 10 ++#define STR_STEPS 2 ++#define PERCENT_VALUE 100 ++#define STOR_DEV_OFFSET 16 ++#define STOR_DEV_MASK 0x03 ++#define STOR_PART 18 ++#define STOR_PART_MASK 0x1f ++ ++#ifdef CONFIG_VENDOR_UPGRADE_BY_SEGMENT ++#define SECTION_SIZE 0x1000000ULL /* 16M */ ++#define BLOCK_SIZE 512 ++#define DEFAULT_BLOCK_SIZE 4096 ++#define CHUNK_HEAD_SIZE 12 ++#define BOUNDARY (DEFAULT_BLOCK_SIZE + CHUNK_HEAD_SIZE) ++#define HEX_DUMP_LEN 128 ++unsigned long long map_size; ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++char is_sparse = 0; ++#endif ++#endif ++ ++#define AU_FIRMWARE "u-boot.bin" ++#define AU_KERNEL "kernel" ++ ++int boot_medium_type = 0xff; ++ ++#include "nand.h" ++struct flash_layout { ++ long start; ++ long end; ++}; ++ ++struct seg_update_parm { ++ int extra; ++ int nsect; ++ int uboot_updated; ++ int updatefile_found; ++ unsigned int section_size; ++ loff_t sz; ++ loff_t actread; ++ loff_t pos; ++ loff_t remain; ++}; ++struct update_medium_interface { ++ char name[NAME_LEN]; ++ int (*init)(void); ++ int (*erase)(unsigned long offset, unsigned long len); ++ int (*write)(unsigned long offset, unsigned long len, ++ unsigned char *buf); ++ int (*write_yaffs)(unsigned long offset, unsigned long len, ++ unsigned char *buf); ++ int (*write_ext4)(unsigned long offset, unsigned long len, ++ unsigned char *buf, char is_sparse); ++}; ++ ++#if defined(CONFIG_CMD_SF) || defined(CONFIG_CMD_NAND) ++static void schedule_notify(unsigned long offset, unsigned long len, ++ unsigned long off_start) ++{ ++ int percent_complete = -1; ++ unsigned long long n; ++ ++ do { ++ n = (unsigned long long)(offset - off_start) * PERCENT_VALUE; ++ int percent; ++ ++ do_div(n, len); ++ percent = (int)n; ++ ++ /* output progress message only at whole percent ++ * steps to reduce the number of messages ++ * printed on (slow) serial consoles ++ */ ++ if (percent != percent_complete) ++ /* do not to add '\n' to this message. */ ++ printf("\rOperation at 0x%lx -- %3d%% complete", ++ offset, percent); ++ } while (0); ++} ++#endif ++ ++#ifdef CONFIG_CMD_SF ++static struct spi_flash *spinor_flash; ++static int spinor_flash_init(void) ++{ ++ spinor_flash = spi_flash_probe(0, 0, 0, 0); ++ return 0; ++} ++ ++static int spi_flash_erase_op(struct spi_flash *flash, unsigned long offset, ++ unsigned long len) ++{ ++ int ret; ++ struct mtd_info_ex *spiflash_info = get_spiflash_info(); ++ unsigned long erase_start, erase_len, erase_step; ++ ++ erase_start = offset; ++ erase_len = len; ++ erase_step = spiflash_info->erasesize; ++ ++ while (len > 0) { ++ if (len < erase_step) ++ erase_step = len; ++ ++ ret = spi_flash_erase(flash, (u32)offset, erase_step); ++ if (ret) ++ return 1; ++ ++ len -= erase_step; ++ offset += erase_step; ++ /* notify real time schedule */ ++ schedule_notify(offset, erase_len, erase_start); ++ } ++ ++ return ret; ++} ++ ++static int spinor_flash_erase(unsigned long offset, unsigned long len) ++{ ++ return spi_flash_erase_op(spinor_flash, offset, len); ++} ++ ++static int spi_flash_write_op(struct spi_flash *flash, unsigned long offset, ++ unsigned long len, unsigned char *buf) ++{ ++ int ret; ++ unsigned long write_start, write_len, write_step; ++ unsigned char *pbuf = buf; ++ struct mtd_info_ex *spiflash_info = get_spiflash_info(); ++ ++ write_start = offset; ++ write_len = len; ++ write_step = spiflash_info->erasesize; ++ ++ while (len > 0) { ++ if (len < write_step) ++ write_step = len; ++ ++ ret = flash->write(flash, offset, write_step, pbuf); ++ if (ret) ++ break; ++ ++ offset += write_step; ++ pbuf += write_step; ++ len -= write_step; ++ /* notify real time schedule */ ++ schedule_notify(offset, write_len, write_start); ++ } ++ ++ return ret; ++} ++ ++static int spinor_flash_write(unsigned long offset, unsigned long len, unsigned char *buf) ++{ ++ return spi_flash_write_op(spinor_flash, offset, len, (unsigned char *)buf); ++} ++#endif ++ ++#ifdef CONFIG_CMD_NAND ++struct mtd_info *nand_flash; ++ ++static int nand_flash_init(void) ++{ ++ nand_flash = nand_info[0]; ++ return 0; ++} ++ ++static int nand_flash_erase(unsigned long offset, unsigned long len) ++{ ++ int ret; ++ unsigned long erase_len; ++ unsigned long erase_step; ++ unsigned long length; ++ nand_erase_options_t opts; ++ ++ (void)memset_s(&opts, sizeof(opts), 0, sizeof(opts)); ++ ++ length = len; ++ erase_step = nand_flash->erasesize; ++ erase_len = length; ++ opts.length = erase_step; ++ opts.offset = offset; ++ opts.quiet = 1; ++ ++ while (length > 0) { ++ if (length < erase_step) ++ erase_step = length; ++ ++ ret = nand_erase_opts(nand_flash, &opts); ++ if (ret) ++ return 1; ++ ++ length -= erase_step; ++ opts.offset += erase_step; ++ /* notify real time schedule */ ++ schedule_notify(opts.offset, erase_len, offset); ++ } ++ ++ return ret; ++} ++ ++static int nand_flash_write(unsigned long offset, unsigned long len, ++ unsigned char *buf) ++{ ++ int ret; ++ unsigned long offset_notify; ++ unsigned long write_start; ++ unsigned long write_len; ++ unsigned long write_step; ++ size_t length; ++ unsigned char *pbuf = buf; ++ ++ if (offset == 0) { ++ /* Make sure the length is block size algin */ ++ length = len & (nand_flash->erasesize - 1) ? (size_t)(len + ++ (nand_flash->erasesize - len % ++ nand_flash->erasesize)) : len; ++ write_step = nand_flash->erasesize; ++ } else { ++ /* Make sure the length is writesize algin */ ++ length = len & (nand_flash->erasesize - 1) ? (size_t)(len + ++ (nand_flash->writesize - len % ++ nand_flash->writesize)) : len; ++ write_step = nand_flash->writesize; ++ } ++ ++ write_start = offset; ++ offset_notify = offset; ++ write_len = length; ++ ++ while (length > 0) { ++ size_t block_offset = offset & (nand_flash->erasesize - 1); ++ size_t *rw_size; ++ ++ if (nand_flash->_block_isbad(nand_flash, offset & ~ ++ (nand_flash->erasesize - 1))) { ++ printf("Skip bad block 0x%08llx\n", ++ offset & ~(loff_t)(nand_flash->erasesize - 1)); ++ offset += nand_flash->erasesize - block_offset; ++ continue; ++ } ++ ++ rw_size = (size_t *)&write_step; ++ ++ struct mtd_info *mtd; ++ mtd = get_nand_dev_by_index(0); ++ ret = nand_write(mtd, offset, rw_size, pbuf); ++ if (ret) { ++ printf("NAND write to offset %lx failed %d\n", ++ offset, ret); ++ break; ++ } ++ offset += write_step; ++ pbuf += write_step; ++ length -= write_step; ++ offset_notify += write_step; ++ /* notify real time schedule */ ++ schedule_notify(offset_notify, write_len, write_start); ++ } ++ ++ return ret; ++} ++ ++static int nand_flash_yaffs_write(unsigned long offset, unsigned long len, ++ unsigned char *buf) ++{ ++ int ret; ++ size_t rw_size = len; ++ ++ ret = nand_write_yaffs_skip_bad(nand_flash, offset, &rw_size, ++ (u_char *)buf); ++ if (ret) ++ printk("Write yaffs fail !!\n"); ++ printk("Write yaffs done\n"); ++ ++ return ret; ++} ++ ++/* get count of area's bad block for nand flash */ ++int get_bad_block_count(unsigned long offset, unsigned long len) ++{ ++ int count = 0; ++ unsigned long block_offset = 0; ++ if (offset & (nand_flash->erasesize - 1)) ++ block_offset = offset & (nand_flash->erasesize - 1); ++ ++ if (len & (nand_flash->erasesize - 1)) ++ len = ALIGN(len, nand_flash->erasesize); ++ ++ for (int i = 0; i < len / nand_flash->erasesize; i++) { ++ if (nand_block_isbad(nand_flash, offset)) ++ count++; ++ offset += nand_flash->erasesize - block_offset; ++ } ++ return count; ++} ++#endif ++ ++void hex_dump(const unsigned char *buf, int len, int addr) ++{ ++ int i; ++ int j; ++ int k; ++ char binstr[STR_LEN]; ++ if (buf == NULL) ++ return; ++ if (len > strlen((char *)buf)) ++ return; ++ ++ for (i = 0; i < len; i++) { ++ if ((i % LINE) == 0) { ++ (void)sprintf_s(binstr, STR_LEN, "%08x -", i + addr); ++ (void)sprintf_s(binstr, STR_LEN, "%s %02x", binstr, (unsigned char)buf[i]); ++ } else if ((i % LINE) == (LINE - 1)) { ++ (void)sprintf_s(binstr, STR_LEN, "%s %02x", binstr, ++ (unsigned char)buf[i]); ++ (void)sprintf_s(binstr, STR_LEN, "%s ", binstr); ++ for (j = i - (LINE - 1); j <= i; j++) ++ (void)sprintf_s(binstr, STR_LEN, "%s%c", binstr, ++ (buf[j] > '!' && ++ buf[j] <= '~') ? buf[j] : '.'); ++ printf("%s\n", binstr); ++ } else { ++ (void)sprintf_s(binstr, STR_LEN, "%s %02x", binstr, ++ (unsigned char)buf[i]); ++ } ++ } ++ ++ if ((i % LINE) != 0) { ++ k = LINE - (i % LINE); ++ for (j = 0; j < k; j++) ++ (void)sprintf_s(binstr, STR_LEN, "%s ", binstr); ++ (void)sprintf_s(binstr, STR_LEN, "%s ", binstr); ++ k = LINE - k; ++ for (j = i - k; j < i; j++) ++ (void)sprintf_s(binstr, STR_LEN, "%s%c", binstr, ++ (buf[j] > '!' && buf[j] <= '~') ? buf[j] : '.'); ++ printf("%s\n", binstr); ++ } ++} ++ ++#ifdef CONFIG_SUPPORT_EMMC_BOOT ++static int mmc_save_init(void) ++{ ++ struct mmc *mmc = find_mmc_device(0); ++ ++ if (!mmc) { ++ printf("%s:find mmc device failed\n", __func__); ++ return -1; ++ } ++ ++ (void)mmc_init(mmc); ++ ++ return 0; ++} ++ ++static int mmc_save_write(unsigned long offset, unsigned long len, ++ unsigned char *buf) ++{ ++ struct mmc *mmc = find_mmc_device(0); ++ ++ if (!mmc) { ++ printf("%s:find mmc device failed\n", __func__); ++ return -1; ++ } ++ if (len % MMC_MAX_BLOCK_LEN) ++ blk_dwrite(mmc_get_blk_desc(mmc), (offset >> EMMC_BLOCK_SHIFT), ++ (len >> EMMC_BLOCK_SHIFT) + 1, buf); ++ else ++ blk_dwrite(mmc_get_blk_desc(mmc), (offset >> EMMC_BLOCK_SHIFT), ++ (len >> EMMC_BLOCK_SHIFT), buf); ++ ++ return 0; ++} ++ ++static int mmc_save_write_ext4(unsigned long offset, unsigned long len, ++ unsigned char *buf, char is_sparse) ++{ ++ struct mmc *mmc = find_mmc_device(0); ++ int retlen; ++ ++ if (!mmc) { ++ printf("%s:find mmc device failed\n", __func__); ++ return -1; ++ } ++ if (is_sparse) { ++ if (len % MMC_MAX_BLOCK_LEN) ++ retlen = ext4_unsparse(mmc, 0, (unsigned char *)buf, ++ (offset >> EMMC_BLOCK_SHIFT), ++ (len >> EMMC_BLOCK_SHIFT) + 1); ++ else ++ retlen = ext4_unsparse(mmc, 0, (unsigned char *)buf, ++ (offset >> EMMC_BLOCK_SHIFT), ++ (len >> EMMC_BLOCK_SHIFT)); ++ } else { ++ retlen = mmc_save_write(offset, (unsigned long)len, buf); ++ } ++ return retlen; ++} ++#endif ++ ++#define UPDATE_MEDIUM_SPINOR 0 ++#define UPDATE_MEDIUM_NAND 1 ++#define UPDATE_MEDIUM_EMMC 2 ++ ++static struct update_medium_interface update_intf[3] = { ++#ifdef CONFIG_CMD_SF ++ {"spinor", spinor_flash_init, spinor_flash_erase, spinor_flash_write, ++ NULL, NULL}, ++#else ++ {"none", NULL, NULL, NULL, NULL, NULL}, ++#endif ++ ++#ifdef CONFIG_CMD_NAND ++ {"nand", nand_flash_init, nand_flash_erase, nand_flash_write, ++ nand_flash_yaffs_write, NULL}, ++#else ++ {"none", NULL, NULL, NULL, NULL, NULL}, ++#endif ++#ifdef CONFIG_SUPPORT_EMMC_BOOT ++ {"emmc", mmc_save_init, NULL, mmc_save_write, NULL, mmc_save_write_ext4} ++#endif ++}; ++ ++static struct update_medium_interface *update_intf_p; ++ ++struct medium_interface { ++ char name[NAME_LEN]; ++ int (*init)(void); ++ void (*exit)(void); ++}; ++ ++#define MAX_UPDATE_INTF 2 ++static struct medium_interface s_intf[MAX_UPDATE_INTF] = { ++#if (CONFIG_AUTO_SD_UPDATE == 1) ++ { .name = "mmc", .init = mmc_stor_init, .exit = mmc_stor_exit, }, ++#endif ++#if (CONFIG_AUTO_USB_UPDATE == 1) ++ { .name = "usb", .init = usb_stor_init, .exit = usb_stor_exit, }, ++#endif ++}; ++ ++static int au_stor_curr_dev; /* current device */ ++ ++/* index of each file in the following arrays */ ++#define IDX_FIRMWARE 0 ++#define IDX_KERNEL 1 ++#define IDX_ROOTFS 2 ++ ++/* max. number of files which could interest us */ ++#define AU_MAXFILES 32 ++ ++/* pointers to file names */ ++char *aufile[AU_MAXFILES] = { ++ 0 ++}; ++ ++#define NAME_MAX_LEN 0x20 ++char aufile_table[AU_MAXFILES][NAME_MAX_LEN] = {{0, }, }; ++unsigned long aufile_size[AU_MAXFILES] = {0}; ++ ++/* sizes of flash areas for each file */ ++long ausize[AU_MAXFILES] = {0}; ++ ++/* array of flash areas start and end addresses */ ++struct flash_layout aufl_layout[AU_MAXFILES] = { ++ { 0, 0, } ++}; ++ ++#ifdef CONFIG_VENDOR_UPGRADE_BY_SEGMENT ++ ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++sparse_header_t sparse_header; /* Sparse file header information */ ++chunk_header_t chunk_header; ++int is_chunk_header(void *pbuf) ++{ ++ chunk_header_t *s_chunk_header = NULL; ++ ++ if (pbuf == NULL) ++ return 0; ++ s_chunk_header = pbuf; ++ if (s_chunk_header->chunk_type == CHUNK_TYPE_RAW || \ ++ s_chunk_header->chunk_type == CHUNK_TYPE_DONT_CARE || \ ++ s_chunk_header->chunk_type == CHUNK_TYPE_FILL) ++ return 1; ++ return 0; ++} ++ ++typedef struct buf_info { ++ int one_block; ++ unsigned int chunk_head_size; ++ bool had_sparse_header; ++ bool set_chunk_header; ++ bool had_chunk_header; ++ bool large_chunk_header; ++ void *buf; ++ unsigned long off; ++}buf_info_t; ++ ++void assign_data(buf_info_t *info, void *pbuf) ++{ ++ if (info == NULL || pbuf == NULL) ++ return; ++ /* clear info data struct to 0 */ ++ info->one_block = 0; ++ info->buf = pbuf; ++ info->chunk_head_size = 0; ++ info->had_sparse_header = false; ++ info->set_chunk_header = false; ++ info->had_chunk_header = false; ++ info->off = 0; ++ ++ sparse_header.total_blks = 0; ++ sparse_header.total_chunks = 0; ++ sparse_header.image_checksum = 0; ++} ++ ++chunk_header_t *check_buf_for_head(buf_info_t *info, void *pbuf) ++{ ++ chunk_header_t *chunk = NULL; ++ ++ if (info == NULL || info->buf == NULL) ++ return NULL; ++ ++ if (is_sparse_image(info->buf)) { ++ info->off += sparse_header.file_hdr_sz; ++ info->buf += sparse_header.file_hdr_sz; ++ info->had_sparse_header = true; ++ } ++ ++ if (is_chunk_header(info->buf)) { ++ info->had_chunk_header = true; ++ chunk = (chunk_header_t *)info->buf; ++ if (chunk->total_sz < SECTION_SIZE) ++ info->off += chunk->total_sz; ++ else ++ info->large_chunk_header = true; ++ } ++ ++ if (!is_chunk_header(info->buf) && is_chunk_header(&chunk_header)) { ++ /* Process the remaining data in large chunks */ ++ (void)memcpy_s(info->buf - sparse_header.chunk_hdr_sz, sizeof(chunk_header_t), ++ &chunk_header, sizeof(chunk_header_t)); ++ info->set_chunk_header = true; ++ info->buf -= sparse_header.chunk_hdr_sz; ++ chunk = (chunk_header_t *)info->buf; ++ if (chunk->total_sz < SECTION_SIZE) ++ info->off += chunk->total_sz - sparse_header.chunk_hdr_sz; ++ } ++ return chunk; ++} ++ ++void *process_large_chunks(buf_info_t *info, chunk_header_t *chunk, ++ unsigned long *sparse_len, loff_t *pos, void *pbuf) ++{ ++ unsigned char skip_chunk_head = 0; ++ ++ if (info == NULL || chunk == NULL || sparse_len == NULL || pos == NULL) ++ return NULL; ++ /* large chunk: head + len > SECTION_SIZE, so it has 1 chunk */ ++ sparse_header.total_chunks = 1; ++ if (info->large_chunk_header) { ++ /* head takes up 1 block */ ++ info->one_block = 1; ++ info->chunk_head_size = sparse_header.chunk_hdr_sz; ++ } else { ++ info->chunk_head_size = 0; ++ info->one_block = 0; ++ } ++ ++ if (info->set_chunk_header) ++ skip_chunk_head = sparse_header.chunk_hdr_sz; ++ sparse_header.total_blks += SECTION_SIZE / sparse_header.blk_sz - ++ info->one_block; ++ /*(16M - 12byte) / 4096byte ---> 16M/4096byte - 1 */ ++ *sparse_len += (SECTION_SIZE / sparse_header.blk_sz - info->one_block) * ++ sparse_header.blk_sz; ++ /* here can not set next chunk header,so i save it to chunk_header */ ++ chunk_header.chunk_type = chunk->chunk_type; ++ chunk_header.total_sz = chunk->total_sz - *sparse_len; ++ chunk_header.chunk_sz = chunk->chunk_sz - (SECTION_SIZE / ++ sparse_header.blk_sz - info->one_block); ++ /* update cur header */ ++ chunk->chunk_sz = SECTION_SIZE / sparse_header.blk_sz - info->one_block; ++ chunk->total_sz = *sparse_len + sparse_header.chunk_hdr_sz; ++ info->off = *sparse_len + info->chunk_head_size; ++ /* set cur pbuf sparse header */ ++ (void)memcpy_s(pbuf - sparse_header.file_hdr_sz - skip_chunk_head, sizeof(sparse_header_t), ++ &sparse_header, sizeof(sparse_header_t)); ++ *pos += info->off; ++ return pbuf - sparse_header.file_hdr_sz - skip_chunk_head; ++} ++ ++void *process_small_chunks(buf_info_t *info, chunk_header_t *chunk, unsigned long *sparse_len, ++ loff_t *pos, loff_t file_size, void *pbuf) ++{ ++ unsigned char skip_chunk_head; ++ ++ if (info == NULL || chunk == NULL || sparse_len == NULL || pos == NULL) ++ return NULL; ++ *sparse_len += chunk->chunk_sz * sparse_header.blk_sz; ++ /* skip first chunk */ ++ info->buf += chunk->total_sz; ++ sparse_header.total_chunks++; ++ sparse_header.total_blks += chunk->chunk_sz; ++ debug_print("total_blks:%u, total_chunks:%u,off:%lu, line:%d\n", sparse_header.total_blks, ++ sparse_header.total_chunks, info->off, __LINE__); ++ do { ++ if (!is_chunk_header(info->buf)) { ++ printf("this chunk header is not correct,please check off\n"); ++ return NULL; ++ } ++ chunk = (chunk_header_t *)info->buf; ++ print_chunk_info(chunk); ++ sparse_header.total_blks += chunk->chunk_sz; ++ sparse_header.total_chunks++; ++ info->buf += chunk->total_sz; ++ info->off += chunk->total_sz; ++ *sparse_len += chunk->chunk_sz * sparse_header.blk_sz; ++ debug_print("buf:%p,off:%lu,total_blks:%u\n", info->buf, info->off, ++ sparse_header.total_blks); ++ } while (info->off < SECTION_SIZE && (*pos + info->off) != file_size); ++ ++ if ((*pos + info->off) == file_size) { ++ skip_chunk_head = info->set_chunk_header ? sparse_header.chunk_hdr_sz : 0; ++ (void)memcpy_s(pbuf - sparse_header.file_hdr_sz - skip_chunk_head, sizeof(sparse_header_t), ++ &sparse_header, sizeof(sparse_header_t)); ++ pbuf -= (sparse_header.file_hdr_sz + skip_chunk_head); ++ *pos += info->off; ++ return pbuf; ++ } else if (info->off > SECTION_SIZE) { ++ /* discarding incomplete chunk */ ++ sparse_header.total_blks -= chunk->chunk_sz; ++ sparse_header.total_chunks--; ++ info->off -= chunk->total_sz; ++ info->buf -= chunk->total_sz; ++ *sparse_len -= chunk->chunk_sz * sparse_header.blk_sz; ++ debug_print("buf:%p, off:%lu\n", info->buf, info->off); ++ } ++ /* updating the sparse header */ ++ if (info->had_sparse_header) ++ (void)memcpy_s(pbuf, sizeof(sparse_header_t), &sparse_header, sizeof(sparse_header_t)); ++ if (!info->had_sparse_header) { ++ (void)memcpy_s(pbuf - sparse_header.file_hdr_sz, sizeof(sparse_header_t), ++ &sparse_header, sizeof(sparse_header_t)); ++ pbuf -= sparse_header.file_hdr_sz; ++ } ++ debug_print("total_blks:%u, total_chunks:%u,off:%lu\n", sparse_header.total_blks, ++ sparse_header.total_chunks, info->off); ++ *pos += info->off; ++ return pbuf; ++} ++ ++char* get_buffer_chunk_layout(char *pbuf, loff_t *pos, ++ unsigned long *sparse_len, loff_t file_size) ++{ ++ chunk_header_t *chunk = NULL; ++ buf_info_t info; ++ ++ assign_data(&info, pbuf); ++ chunk = check_buf_for_head(&info, pbuf); ++ if (chunk == NULL) { ++ printf("there is no chunk header or sparse header in buff\n"); ++ return NULL; ++ } ++ ++ if (chunk->total_sz >= SECTION_SIZE) ++ return process_large_chunks(&info, chunk, sparse_len, pos, pbuf); ++ else ++ return process_small_chunks(&info, chunk, sparse_len, pos, ++ file_size, pbuf); ++} ++#endif ++#endif ++/* where to load files into memory */ ++#define LOAD_ADDR (unsigned char *)CONFIG_SYS_LOAD_ADDR ++/* the app is the largest image */ ++#define MAX_LOADSZ ausize[IDX_ROOTFS] ++ ++#ifdef CONFIG_VENDOR_UPGRADE_BY_SEGMENT ++int write_yaffs2_fs(int idx, unsigned int sz, unsigned char *pbuf) ++{ ++#ifdef CONFIG_CMD_NAND ++ int bad_block_count; ++ unsigned long pages_len; ++ int ret; ++ unsigned long tmp_start_addr = aufl_layout[idx].start; ++ unsigned int sec_yaffs = nand_flash->writesize + nand_flash->oobsize; ++ ++ if (update_intf_p->write_yaffs) { ++ pages_len = (sz / sec_yaffs) * nand_flash->writesize; ++ bad_block_count = get_bad_block_count(tmp_start_addr, pages_len); ++ debug_print("write offset:%ld, bad block count:%d\n", ++ aufl_layout[idx].start, bad_block_count); ++ ++ tmp_start_addr += pages_len + bad_block_count * nand_flash->erasesize; ++ ++ if (tmp_start_addr > aufl_layout[idx].end) { ++ printf("The actual length of the partition is greater \ ++ than the specified value due to the \ ++ address offset caused by bad blocks.\n"); ++ return 1; ++ } ++ ret = update_intf_p->write_yaffs(aufl_layout[idx].start, sz, ++ pbuf); ++ if (ret) { ++ printf("write yaffs failed\n"); ++ return ret; ++ } ++ aufl_layout[idx].start += pages_len + bad_block_count * ++ nand_flash->erasesize; ++ } ++#endif ++ return 0; ++} ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++int write_ext4_fs(int idx, unsigned char *pbuf, unsigned long long sz) ++{ ++ int ret; ++ ++ ret = update_intf_p->write_ext4( ++ aufl_layout[idx].start, sz, pbuf, is_sparse); ++ if (ret) { ++ printf("write ext4 fs error\n"); ++ return 1; ++ } ++ aufl_layout[idx].start += sz; ++ return ret; ++} ++#endif ++int write_ubi_fs(int idx, unsigned int sz, unsigned char *pbuf) ++{ ++#ifdef CONFIG_CMD_NAND ++ int bad_block_count; ++ unsigned long tmp_start_addr; ++ int ret; ++#endif ++ if (boot_medium_type == BOOT_FROM_NAND) { ++#ifdef CONFIG_CMD_NAND ++ tmp_start_addr = aufl_layout[idx].start; ++ while (sz & (nand_flash->writesize - 1)) ++ sz = ALIGN(sz, nand_flash->writesize); ++ bad_block_count = get_bad_block_count(tmp_start_addr, sz); ++ debug_print("write offset: start:%ld, bad block count:%d\n", ++ tmp_start_addr, bad_block_count); ++ tmp_start_addr += sz + bad_block_count * nand_flash->erasesize; ++ if (tmp_start_addr > aufl_layout[idx].end) { ++ printf("The actual length of the partition is greater\ ++ than the specified value due to the address\ ++ offset caused by bad blocks.\n"); ++ return 1; ++ } ++ ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf); ++ if (ret) { ++ printf("spi nand write ubifs error.\n"); ++ return 1; ++ } ++ aufl_layout[idx].start += sz + bad_block_count * ++ nand_flash->erasesize; ++#endif ++ } else { ++ /* ubifs for spi nor */ ++ update_intf_p->write((aufl_layout[idx].start), sz, pbuf); ++ aufl_layout[idx].start += sz; ++ } ++ ++ return 0; ++} ++ ++int write_other_files(int idx, unsigned int sz, unsigned char *pbuf) ++{ ++ int ret = 0; ++#ifdef CONFIG_CMD_NAND ++ int bad_block_count; ++ unsigned long tmp_start_addr; ++#endif ++ if (boot_medium_type == BOOT_FROM_NAND) { ++#ifdef CONFIG_CMD_NAND ++ tmp_start_addr = aufl_layout[idx].start; ++ while (sz & (nand_flash->writesize - 1)) ++ sz = ALIGN(sz, nand_flash->writesize); ++ debug_print("write start:%ld, len:%u,pagesize:%u\n", ++ aufl_layout[idx].start, sz, nand_flash->writesize); ++ bad_block_count = get_bad_block_count(tmp_start_addr, sz); ++ tmp_start_addr += sz + bad_block_count * nand_flash->erasesize; ++ /* u-boot */ ++ if (strcmp((char *)AU_FIRMWARE, aufile[idx]) == 0) { ++ if (tmp_start_addr > aufl_layout[idx].end) { ++ printf("The address offset caused by bad\ ++ blocks causes the u-boot.bin file to overwrite the environment variable.\n"); ++ return 1; ++ } ++ } else if (strcmp((char *)AU_KERNEL, aufile[idx]) == 0) { ++ if (tmp_start_addr > aufl_layout[idx].end) { ++ printf("The address offset caused by bad blocks causes the kernel file to\ ++ overwrite the File system.\n"); ++ return 1; ++ } ++ } ++ ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf); ++ aufl_layout[idx].start += sz + bad_block_count * ++ nand_flash->erasesize; ++ ++#endif ++ } else if (boot_medium_type == BOOT_FROM_EMMC) { ++ if (sz & (BLOCK_SIZE - 1)) ++ sz = ALIGN(sz, BLOCK_SIZE); ++ debug_print("write start:%ld, len:%#x\n", aufl_layout[idx].start, sz); ++ ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf); ++ aufl_layout[idx].start += sz; ++ } else if (boot_medium_type == BOOT_FROM_SPI) { ++ debug_print("write start:%ld, len:%#x\n", aufl_layout[idx].start, sz); ++ ret = update_intf_p->write((aufl_layout[idx].start), sz, pbuf); ++ aufl_layout[idx].start += sz; ++ } ++ ++ if (ret) { ++ printf("write file %s failed\n", aufile[idx]); ++ return ret; ++ } ++ return 0; ++} ++#endif ++#ifndef CONFIG_VENDOR_UPGRADE_BY_SEGMENT ++static int au_do_update(int idx, long sz) ++{ ++ unsigned long start; ++ unsigned long len; ++ unsigned long write_len; ++ int rc = 0; ++ char *buf; ++ void *pbuf; ++ char sparse_flag = 0; ++ ++ start = aufl_layout[idx].start; ++ len = aufl_layout[idx].end - aufl_layout[idx].start + 1; ++ ++ /* erase the address range. */ ++ if (boot_medium_type == BOOT_FROM_SPI || boot_medium_type == BOOT_FROM_NAND) { ++ printf("%s erase...\n", update_intf_p->name); ++ rc = update_intf_p->erase(start, len); ++ if (rc) { ++ printf("sector erase failed\n"); ++ return 1; ++ } ++ } ++ ++ buf = map_physmem((unsigned long)LOAD_ADDR, len, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ /* write the whole file to flash;uboot and rootfs image have head ++ * kernel has head,it's head also be writed to flash ++ */ ++ pbuf = buf; ++ write_len = aufile_size[idx]; ++ ++ /* copy the data from RAM to FLASH */ ++ printf("\n%s write...\n", update_intf_p->name); ++ ++ if (strstr(aufile[idx], "yaffs")) { ++ if (update_intf_p->write_yaffs) ++ rc = update_intf_p->write_yaffs(start, write_len, pbuf); ++ } else if (strstr(aufile[idx], "ext4")) { ++ if (update_intf_p->write_ext4) { ++#ifdef CONFIG_EXT4_SPARSE ++ sparse_flag = is_sparse_image((sparse_header_t *)pbuf); ++#endif ++ rc = update_intf_p->write_ext4(start, len, pbuf, sparse_flag); ++ } ++ } else { ++ rc = update_intf_p->write(start, write_len, pbuf); ++ } ++ ++ if (rc) { ++ printf("write failed, return %d\n", rc); ++ return 1; ++ } ++ ++ unmap_physmem(buf, len); ++ return 0; ++} ++#else ++static int au_do_update(int idx, unsigned int sz, int segment, ++ void *new_pos, unsigned long sparse_len) ++{ ++ int rc; ++ void *buf; ++ unsigned char *pbuf; ++ unsigned long erase_len; ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++ unsigned long op_sz; ++#endif ++ if (segment > 0) ++ goto write_op; ++ ++ if (boot_medium_type == BOOT_FROM_SPI || boot_medium_type == BOOT_FROM_NAND) { ++ printf("%s erase...\n", update_intf_p->name); ++ erase_len = aufl_layout[idx].end - aufl_layout[idx].start + 1; ++ rc = update_intf_p->erase(aufl_layout[idx].start, erase_len); ++ debug_print("erase start:%lx erase end:%lx erase len:%lx\n", ++ aufl_layout[idx].start, aufl_layout[idx].end, erase_len); ++ if (rc) { ++ printf("sector erase failed\n"); ++ return 1; ++ } ++ } ++ ++write_op: ++ buf = map_physmem((unsigned long)LOAD_ADDR, map_size, MAP_WRBACK); ++ if (!buf) { ++ puts("Failed to map physical memory\n"); ++ return 1; ++ } ++ ++ /* ++ * write the whole file to flash;uboot and rootfs image have head ++ * kernel has head,it's head also be writed to flash ++ */ ++ pbuf = buf; ++ ++ /* copy the data from RAM to FLASH */ ++ printf("%s write...\n", update_intf_p->name); ++ ++ if (strstr(aufile[idx], "yaffs")) { ++ rc = write_yaffs2_fs(idx, sz, pbuf); ++ if (rc) { ++ printf("write yaffs fs failed\n"); ++ return rc; ++ } ++ } else if (strstr(aufile[idx], "ext4")) { ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++ pbuf = is_sparse ? new_pos : (pbuf + BLOCK_SIZE); ++ op_sz = is_sparse ? sparse_len : sz; ++ rc = write_ext4_fs(idx, pbuf, op_sz); ++ if (rc) { ++ printf("write ext4 fs failed\n"); ++ return rc; ++ } ++#endif ++ } else if (strstr(aufile[idx], "ubifs")) { ++ rc = write_ubi_fs(idx, sz, pbuf); ++ if (rc) { ++ printf("write ubi fs failed\n"); ++ return rc; ++ } ++ } else { ++ rc = write_other_files(idx, sz, pbuf); ++ if (rc) { ++ printf("write file operation failed\n"); ++ return rc; ++ } ++ } ++ unmap_physmem(buf, map_size); ++ return 0; ++} ++#endif ++ ++/* ++ * If none of the update file(u-boot, kernel or rootfs) was found ++ * in the medium, return -1; ++ * If u-boot has been updated, return 1; ++ * Others, return 0; ++ */ ++#ifndef CONFIG_VENDOR_UPGRADE_BY_SEGMENT ++static int update_to_flash(void) ++{ ++ int i; ++ loff_t sz; ++ int res; ++ int cnt; ++ int uboot_updated = 0; ++ ++ /* just loop thru all the possible files */ ++ for (i = 0; i < AU_MAXFILES && aufile[i] != NULL; i++) { ++ printf("\n"); ++ ++ if (!fat_exists(aufile[i])) { ++ printf("%s not found!\n", aufile[i]); ++ continue; ++ } ++ ++ /* get file's real size */ ++ if (!fat_size(aufile[i], &sz)) { ++ aufile_size[i] = ALIGN((unsigned long)sz, CONFIG_SYS_CACHELINE_SIZE); ++ } else { ++ printf("get size of %s failed!\n", aufile[i]); ++ continue; ++ } ++ printf("aligned size=0x%08lx\n", aufile_size[i]); ++ ++ (void)memset_s(LOAD_ADDR, aufile_size[i], 0xff, aufile_size[i]); ++ sz = file_fat_read(aufile[i], LOAD_ADDR, (unsigned long)sz); ++ debug_print("read %s sz %ld hdr %lu\n", ++ aufile[i], (unsigned long)sz, (unsigned long)sizeof(image_header_t)); ++ if (sz <= 0) { ++ printf("read %s failed!\n", aufile[i]); ++ continue; ++ } ++ /* get file's real size */ ++ aufile_size[i] = (unsigned long)sz; ++ printf("size=0x%08lx\n", aufile_size[i]); ++ ++ /* If u-boot had been updated, we need to ++ * save current env to flash */ ++ if (strcmp((char *)AU_FIRMWARE, aufile[i]) == 0) ++ uboot_updated = 1; ++ ++ /* this is really not a good idea, but it's what the */ ++ /* customer wants. */ ++ cnt = 0; ++ do { ++ res = au_do_update(i, (unsigned long)sz); ++ /* let the user break out of the loop */ ++ if (ctrlc() || had_ctrlc()) { ++ clear_ctrlc(); ++ ++ break; ++ } ++ cnt++; ++ } while (res < 0); ++ } ++ ++ if (uboot_updated == 1) ++ return 1; ++ else ++ return -1; ++} ++#else ++void get_sparse_header(int file_idx, struct seg_update_parm *update_parm) ++{ ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++ int ret; ++ if (strstr(aufile[file_idx], "ext4") || strstr(aufile[file_idx], "ufs")) { ++ ret = file_fat_read_at(aufile[file_idx], ++ update_parm->pos, LOAD_ADDR, BLOCK_SIZE, &update_parm->actread); ++ if (ret) ++ printf("read %s failed, actread:%lld\n", ++ aufile[file_idx], update_parm->actread); ++ (void)memset_s(&sparse_header, sizeof(sparse_header_t), 0, sizeof(sparse_header_t)); ++ get_unspare_header_info(LOAD_ADDR, &sparse_header, &is_sparse); ++ } ++#endif ++ ++ return; ++} ++ ++void get_section_size(int file_idx, struct seg_update_parm *update_parm) ++{ ++#ifdef CONFIG_CMD_NAND ++ if (strstr(aufile[file_idx], "yaffs2")) { ++ if (update_parm->sz > SECTION_SIZE) ++ update_parm->section_size = (SECTION_SIZE / (nand_flash->writesize + ++ nand_flash->oobsize)) * (nand_flash->writesize + nand_flash->oobsize); ++ else ++ update_parm->section_size = update_parm->sz; ++ debug_print("pagesize:%u, oobsize:%u, section_size:%u\n", nand_flash->writesize, ++ nand_flash->oobsize, update_parm->section_size); ++ } else { ++ update_parm->section_size = SECTION_SIZE; ++ } ++#endif ++ ++#ifndef CONFIG_CMD_NAND ++ update_parm->section_size = SECTION_SIZE; ++#endif ++ ++ return; ++} ++ ++void loop_au_do_update(int file_idx, unsigned int opsz, int segment, ++ void *buf, unsigned long sparse_len) ++{ ++ int cnt; ++ int res; ++ ++ do { ++ res = au_do_update(file_idx, opsz, segment, buf, sparse_len); ++ /* let the user break out of the loop */ ++ if (ctrlc() || had_ctrlc()) { ++ clear_ctrlc(); ++ ++ break; ++ } ++ cnt++; ++ } while (res < 0); ++ ++ return; ++} ++ ++void *handle_sparse_file(int file_idx, loff_t *pos, ++ unsigned long *sparse_len, void *buff) ++{ ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++ void *new_buf = NULL; ++ if (buff == NULL) ++ return NULL; ++ if (strstr(aufile[file_idx], "ext4") || strstr(aufile[file_idx], "ufs")) { ++ if (!is_sparse) ++ return buff; ++ ++ if (aufile_size[file_idx] <= SECTION_SIZE) ++ return buff; ++ ++ *sparse_len = 0; ++ if (aufile_size[file_idx] > SECTION_SIZE) { ++ new_buf = get_buffer_chunk_layout(buff, pos, sparse_len, ++ aufile_size[file_idx]); ++ if (new_buf == NULL) { ++ printf("Failed to parse the buffer.\n"); ++ return NULL; ++ } ++ return new_buf; ++ } ++ } ++#endif ++ return buff; ++} ++void segment_data_save(int file_idx, struct seg_update_parm *update_parm) ++{ ++ unsigned int opsz; ++ int ret; ++ void *new_pos = NULL; ++ void *buff = NULL; ++ unsigned long sparse_len = 0; ++ int segment; ++ ++ if (strstr(aufile[file_idx], "ext4") || strstr(aufile[file_idx], "ufs")) ++ buff = LOAD_ADDR + BLOCK_SIZE; ++ else ++ buff = LOAD_ADDR; ++ ++ for (segment = 0; update_parm->pos != aufile_size[file_idx]; segment++) { ++ if ((segment == update_parm->nsect) && update_parm->remain) ++ opsz = update_parm->remain; ++ else ++ opsz = update_parm->section_size; ++ ++ printf("\nnsect:%d, remain:%lld, section_size:%u, pos:%llu, opsz:%u, map_size:%llu\n", ++ update_parm->nsect, update_parm->remain, ++ update_parm->section_size, update_parm->pos, opsz, map_size); ++ (void)memset_s(LOAD_ADDR, map_size, 0xff, map_size); ++ ret = file_fat_read_at(aufile[file_idx], update_parm->pos, buff, opsz, ++ &update_parm->actread); ++ if (ret) ++ printf("read %s failed,opsz:%d,actread:%lld,pos:%lld\n", ++ aufile[file_idx], opsz, update_parm->actread, update_parm->pos); ++ new_pos = handle_sparse_file(file_idx, &update_parm->pos, &sparse_len, buff); ++ if (new_pos == NULL) ++ return; ++ printf("reading %d part of %s ,size:%u, start:%lld\n", segment, ++ aufile[file_idx], opsz, update_parm->pos); ++ update_parm->updatefile_found = 1; ++ ++ /* If u-boot had been updated, we need to ++ * save current env to flash */ ++ if (strcmp((char *)AU_FIRMWARE, aufile[file_idx]) == 0) ++ update_parm->uboot_updated = 1; ++ ++ loop_au_do_update(file_idx, opsz, segment, new_pos, sparse_len); ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++ if (strstr(aufile[file_idx], "ext4") || strstr(aufile[file_idx], "ufs")) { ++ if (!is_sparse) ++ update_parm->pos += opsz; ++ } else { ++ update_parm->pos += opsz; ++ } ++#else ++ update_parm->pos += opsz; ++#endif ++ } ++ return; ++} ++ ++static int update_to_flash(void) ++{ ++ int i; ++ struct seg_update_parm update_parm = {0}; ++#if (defined CONFIG_EMMC) || (defined CONFIG_CMD_UFS) ++ (void)memset_s(&chunk_header, sizeof(sparse_header_t), 0, sizeof(sparse_header_t)); ++#endif ++ /* just loop thru all the possible files */ ++ for (i = 0; i < AU_MAXFILES && aufile[i] != NULL; i++) { ++ printf("\n\n"); ++ ++ update_parm.pos = 0; ++ update_parm.extra = 0; ++ if (!fat_exists(aufile[i])) { ++ printf("%s not found!\n", aufile[i]); ++ continue; ++ } ++ ++ /* get file's real size */ ++ if (!fat_size(aufile[i], (loff_t *)&update_parm.sz)) { ++ aufile_size[i] = update_parm.sz; ++ } else { ++ printf("get size of %s failed!\n", aufile[i]); ++ continue; ++ } ++ printf("filesize size= %ld\n", aufile_size[i]); ++ if (strstr(aufile[i], "ext4") || strstr(aufile[i], "ufs")) ++ map_size = SECTION_SIZE + BLOCK_SIZE; ++ else ++ map_size = SECTION_SIZE; ++ (void)memset_s(LOAD_ADDR, map_size, 0xff, map_size); ++ ++ get_sparse_header(i, &update_parm); ++ get_section_size(i, &update_parm); ++ ++ update_parm.nsect = aufile_size[i] / update_parm.section_size; ++ update_parm.remain = aufile_size[i] % update_parm.section_size; ++ ++ if (update_parm.remain) ++ update_parm.extra = 1; ++ segment_data_save(i, &update_parm); ++ } ++ ++ if (update_parm.uboot_updated == 1) ++ return 1; ++ else if (update_parm.updatefile_found == 1) ++ return 0; ++ else ++ return -1; ++} ++#endif ++ ++#define ENV_MAX_LEN (CONFIG_MAX_SIZE / 2) ++ ++/* ++ * pick up env form config file and set env ++ * fail:return -1; ++ * ok: return 0; ++ */ ++static int env_pick_up(const char *envname, const char *buffer) ++{ ++ char *str, *str_s, *str_e; ++ char env[ENV_MAX_LEN]; ++ ++ str = strstr(buffer, envname); ++ if (!str) ++ goto env_err; ++ ++ str_s = strchr(str, '\''); ++ if (!str_s) ++ goto env_err; ++ ++ str_e = strchr(++str_s, '\''); ++ if (!str_e) ++ goto env_err; ++ ++ if ((unsigned long)(str_e - str_s) > ENV_MAX_LEN) { ++ printf("ERROR:%s too long!\n", envname); ++ goto err; ++ } ++ ++ if (strncpy_s(env, ENV_MAX_LEN, str_s, (size_t)(str_e - str_s))) { ++ printf("strncpy_s error!\n"); ++ goto err; ++ } ++ ++ env[(size_t)(str_e - str_s)] = '\0'; ++ env_set((char *)envname, env); ++ ++ return 0; ++ ++env_err: ++ printf("ERROR:%s not found!\n", envname); ++err: ++ return -1; ++} ++ ++/* ++ * pick up bootargs and bootcmd from config file and save env ++ * fail:return -1; ++ * ok: return 0; ++ */ ++static int get_env_from_config(void) ++{ ++ char *aufile = AU_CONFIG; ++ int ret; ++ long sz = file_fat_read(aufile, (void *)LOAD_ADDR, CONFIG_MAX_SIZE); ++ if (sz <= 0) { ++ printf("ERROR:%s not found!\n", aufile); ++ goto err; ++ } ++ ++ ret = env_pick_up("bootargs", (char *)LOAD_ADDR); ++ if (ret < 0) ++ goto err; ++ ++ ret = env_pick_up("bootcmd", (char *)LOAD_ADDR); ++ if (ret < 0) ++ goto err; ++ ++ return 0; ++err: ++ return -1; ++} ++ ++struct mtd_part_s { ++ char *str; ++ int unit; ++ struct mtd_part_s *next; ++}; ++ ++/* ++ * insert node to list ++ */ ++struct mtd_part_s *list_insert(struct mtd_part_s *head, struct mtd_part_s *node) ++{ ++ struct mtd_part_s *p = head; ++ ++ if (!head) ++ head = node; ++ ++ while (p) { ++ if (p->next == NULL) { ++ p->next = node; ++ break; ++ } ++ p = p->next; ++ } ++ ++ return head; ++} ++ ++/* ++ * sort list by str ++ */ ++struct mtd_part_s *mtd_list_sort(struct mtd_part_s *head) ++{ ++ int flag; ++ struct mtd_part_s *p, *pt1, *pt2; ++ ++ if (!head) ++ return NULL; ++ ++ do { ++ flag = 0; ++ if (head->next == NULL) ++ return head; ++ ++ if ((uintptr_t)(head->str) >= (uintptr_t) ++ (head->next->str)) { ++ pt1 = head->next->next; ++ pt2 = head->next; ++ pt2->next = head; ++ head->next = pt1; ++ head = pt2; ++ } ++ ++ for (p = head; p->next->next != NULL; p = p->next) { ++ if ((uintptr_t)(p->next->str) >= (uintptr_t) ++ (p->next->next->str)) { ++ pt1 = p->next->next->next; ++ pt2 = p->next->next; ++ pt2->next = p->next; ++ p->next->next = pt1; ++ p->next = pt2; ++ flag = 1; ++ } ++ } ++ } while (flag); ++ ++ return head; ++} ++ ++/* ++ * get mtd parttion info list from env string ++ */ ++struct mtd_part_s *get_mtd_parts(char *env) ++{ ++ char *str = NULL; ++ struct mtd_part_s *part_p = NULL; ++ struct mtd_part_s *head = NULL; ++ ++ if (env == NULL) { ++ printf("env is null!\n"); ++ return NULL; ++ } ++ ++ str = env; ++ while ((str = strstr(str, "M("))) { ++ part_p = malloc(sizeof(struct mtd_part_s)); ++ if (part_p == NULL) ++ return NULL; ++ ++ part_p->str = str; ++ part_p->unit = SIZE_M; ++ part_p->next = NULL; ++ head = list_insert(head, part_p); ++ str++; ++ } ++ ++ str = env; ++ while ((str = strstr(str, "K("))) { ++ part_p = malloc(sizeof(struct mtd_part_s)); ++ if (part_p == NULL) ++ return NULL; ++ ++ part_p->str = str; ++ part_p->unit = SIZE_K; ++ part_p->next = NULL; ++ head = list_insert(head, part_p); ++ str++; ++ } ++ ++ head = mtd_list_sort(head); ++ return head; ++} ++ ++unsigned long get_mtd_part_size(const char *str_num) ++{ ++ unsigned long size = 0; ++ int k; ++ int j = 0; ++ int num; ++ if (str_num == NULL) ++ return -1; ++ ++ while (*str_num >= '0' && *str_num <= '9') { ++ k = j; ++ num = *str_num - '0'; ++ while (k) { ++ num *= DECI_VALUE; ++ k--; ++ } ++ size += num; ++ j++; ++ str_num--; ++ } ++ ++ return size; ++} ++ ++int bootargs_analyze(void) ++{ ++ char *str; ++ char *str_0; ++ int i; ++ long start = 0; ++ struct mtd_part_s *part_p = NULL; ++ char env[ENV_MAX_LEN]; ++ char *envp = env_get("bootargs"); ++ if (envp == NULL) { ++ printf("ERROR:bootargs is NULL!\n"); ++ return -1; ++ } ++ ++ if (strlen(envp) > ENV_MAX_LEN - 1) { ++ printf("ERROR:bootargs is too long!\n"); ++ return -1; ++ } ++ if (strcpy_s(env, ENV_MAX_LEN, envp)) { ++ printf("%s %d ERR:strcpy_s fail !\n", __func__, __LINE__); ++ return -1; ++ } ++ str = env; ++ str_0 = env; ++ i = 0; ++ part_p = get_mtd_parts(env); ++ ++ while (part_p) { ++ if (i >= AU_MAXFILES) { ++ printf("ERROR:Num of partition is more than %0d!\n", ++ AU_MAXFILES); ++ break; ++ } ++ ++ if (part_p->str == NULL) ++ return -1; ++ str = part_p->str; ++ if ((get_mtd_part_size(str - 1) * part_p->unit) <= ULONG_MAX) ++ ausize[i] = get_mtd_part_size(str - 1) * part_p->unit; ++ else ++ return -1; ++ aufl_layout[i].start = start; ++ aufl_layout[i].end = start + ausize[i] - 1; ++ start += ausize[i]; ++ ++ str += STR_STEPS; ++ str_0 = strstr(str, ")"); ++ if ((unsigned long)(str_0 - str) > NAME_MAX_LEN) { ++ printf("file name len is too long\n"); ++ return -1; ++ } ++ if (strncpy_s(aufile_table[i], NAME_MAX_LEN, str, (unsigned long)(str_0 - str))) { ++ printf("strncpy_s error!\n"); ++ return -1; ++ } ++ aufile_table[i][str_0 - str] = '\0'; ++ aufile[i] = &(aufile_table[i][0]); ++ printf("\n[%0d]=%-16s start=0x%08lx end=0x%08lx size=0x%08lx", i, ++ aufile_table[i], (unsigned long)(aufl_layout[i].start), ++ (unsigned long)(aufl_layout[i].end), (unsigned long)ausize[i]); ++ i++; ++ part_p = part_p->next; ++ } ++ ++ if (i == 0) { ++ printf("ERROR:Can't find valid partition info!\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_EMMC ++unsigned int dos_start_lba = 0; ++/* 0:emmc 1:sd */ ++int target_dev = 1; ++/* the user's upgrade image stores the partition number in the eMMC medium */ ++int target_paratition; ++ ++void *get_target_dev_value(void) ++{ ++ return &target_dev; ++} ++ ++void *get_target_paratition_dev(void) ++{ ++ return &target_paratition; ++} ++ ++void *get_dos_start_lba_value(void) ++{ ++ return &dos_start_lba; ++} ++ ++/* ++ * get mtd parttion info list from env string in order ++ */ ++struct mtd_part_s *get_mtd_parts_inorder(char *env, int *total_paratition) ++{ ++ char *str = env; ++ struct mtd_part_s *part_p; ++ struct mtd_part_s *head = NULL; ++ int num = 0; ++ ++ str = env; ++ ++ if (str == NULL) ++ return NULL; ++ ++ str = strstr(str, "("); ++ while (str != NULL) { ++ printf("get_mtd_parts_inorder str=%s\n", str); ++ part_p = malloc(sizeof(struct mtd_part_s)); ++ if (part_p == NULL) ++ return NULL; ++ if ((*(str - 1) == 'M') || (*(str - 1) == 'm')) ++ part_p->unit = SIZE_M; ++ else if ((*(str - 1) == 'K') || (*(str - 1) == 'k')) ++ part_p->unit = SIZE_K; ++ else ++ part_p->unit = 1; ++ ++ part_p->str = str; ++ part_p->next = NULL; ++ head = list_insert(head, part_p); ++ ++ str++; ++ str = strstr(str, "("); ++ num++; ++ } ++ ++ head = mtd_list_sort(head); ++ ++ if (total_paratition != NULL) ++ *total_paratition = num; ++ ++ return head; ++} ++ ++int bootargs_getend(void) ++{ ++ char *str; ++ int i; ++ unsigned long start = 0; ++ char filename[NAME_MAX_LEN] = {0}; ++ int cur_target_paratition; ++ struct mtd_part_s *part_p = NULL; ++ char env[ENV_MAX_LEN]; ++ char *envp = env_get("bootargs"); ++ ++ if (envp == NULL) { ++ printf("ERROR:bootargs is NULL!\n"); ++ return -1; ++ } ++ ++ if (strlen(envp) > ENV_MAX_LEN - 1) { ++ printf("ERROR:bootargs is too long!\n"); ++ return -1; ++ } ++ ++ if (strcpy_s(env, ENV_MAX_LEN, envp)) { ++ printf("%s %d ERR:strcpy_s fail !\n", __func__, __LINE__); ; ++ return -1; ++ } ++ str = env; ++ i = 0; ++ part_p = get_mtd_parts_inorder(env, NULL); ++ cur_target_paratition = target_paratition; ++ ++ while (part_p) { ++ if (i >= AU_MAXFILES) { ++ printf("ERROR:Num of partition is more than %0d!\n", AU_MAXFILES); ++ break; ++ } ++ str = part_p->str; ++ start += get_mtd_part_size(str - 1) * part_p->unit; ++ str += 1; ++ if (strstr(str, ")") == NULL || (unsigned long)(strstr(str, ")") - str) > ++ (NAME_MAX_LEN - 1)) { ++ printf("file name is more than NAME_MAX_LEN\n"); ++ return -1; ++ } ++ if (strncpy_s(filename, NAME_MAX_LEN, str, (unsigned long)(strstr(str, ")") - str))) { ++ printf("strncpy_s error!\n"); ++ return -1; ++ } ++ filename[strstr(str, ")") - str] = '\0'; ++ ++ printf("\n[%0d]=%-16s start=0x%08lx", i, filename, start); ++ i++; ++ part_p = part_p->next; ++ if (i >= cur_target_paratition) ++ break; ++ } ++ ++ if (i == 0) { ++ printf("ERROR:Can't find valid partition info!\n"); ++ dos_start_lba = 0; ++ return -1; ++ } ++ ++ if (target_dev == 0) ++ /* emmc device */ ++ dos_start_lba = start >> EMMC_BLOCK_SHIFT; ++ else ++ /* sd device */ ++ dos_start_lba = 0; ++ ++ printf("\n dos_start_lba = %d\n", dos_start_lba); ++ return 0; ++} ++#endif ++ ++void get_boot_info(void) ++{ ++ unsigned int reg; ++ reg = readl((void *)(SYS_CTRL_REG_BASE + REG_SYSSTAT)); ++ if (get_sys_boot_mode(reg) == BOOT_FROM_SPI_NAND) { ++ boot_medium_type = BOOT_FROM_NAND; ++ update_intf_p = &update_intf[UPDATE_MEDIUM_NAND]; ++ } else if (get_sys_boot_mode(reg) == BOOT_FROM_SPI) { ++ boot_medium_type = BOOT_FROM_SPI; ++ update_intf_p = &update_intf[UPDATE_MEDIUM_SPINOR]; ++ } else if (get_sys_boot_mode(reg) == BOOT_FROM_NAND) { ++ boot_medium_type = BOOT_FROM_NAND; ++ update_intf_p = &update_intf[UPDATE_MEDIUM_NAND]; ++ } else if (get_sys_boot_mode(reg) == BOOT_FROM_EMMC) { ++ boot_medium_type = BOOT_FROM_EMMC; ++ update_intf_p = &update_intf[UPDATE_MEDIUM_EMMC]; ++ } ++} ++ ++struct blk_desc *detect_external_storage(int dev_index) ++{ ++ struct blk_desc *stor_dev; ++ int dev = 0; ++ ++ if (s_intf[dev_index].init == NULL) ++ return NULL; ++ ++ au_stor_curr_dev = -1; ++ au_stor_curr_dev = s_intf[dev_index].init(); ++ if (au_stor_curr_dev == -1) { ++ printf("%s storage device not found!\n", s_intf[dev_index].name); ++ return NULL; ++ } ++ printf("%s storage device found!\n", s_intf[dev_index].name); ++ if (strncmp("mmc", s_intf[dev_index].name, sizeof("mmc")) == 0) { ++#ifdef CONFIG_EMMC ++ dev = target_dev; ++#else ++ dev = 0; ++#endif ++ } ++ stor_dev = blk_get_dev(s_intf[dev_index].name, dev); ++ if (stor_dev == NULL) { ++ printf("Unknow device type!\n"); ++ return NULL; ++ } ++ ++ return stor_dev; ++} ++/* ++ * This is called by board_init() after the hardware has been set up ++ * and is usable. ++ * return -1, otherwise it will return 0; ++ */ ++int do_auto_update(void) ++{ ++ struct blk_desc *stor_dev = NULL; ++ int old_ctrlc; ++ int j; ++ int state; ++ ++ for (j = 0; j < MAX_UPDATE_INTF; j++) { ++ stor_dev = detect_external_storage(j); ++ if (stor_dev == NULL) ++ continue; ++ ++#ifdef CONFIG_EMMC ++ if (target_dev == 0) { ++ /* emmc upgrade */ ++ bootargs_getend(); ++ /* re-do the entry->test(dev_desc) to set the ++ * dev_desc->part_type to PART_TYPE_DOS ++ */ ++ part_init(stor_dev); ++ } ++#endif ++ ++ if (fat_register_device(stor_dev, 1) != 0) { ++ debug_print("Unable to use %s %d:%d for fatls\n", s_intf[j].name, ++ au_stor_curr_dev, 1); ++ continue; ++ } ++ ++ if (file_fat_detectfs() != 0) { ++ debug_print("file_fat_detectfs failed\n"); ++ continue; ++ } ++ get_boot_info(); ++ update_intf_p->init(); ++ ++ if (get_env_from_config()) { ++ printf("Warning:no config! Try to use old env!\n"); ++ }; ++ ++ if (bootargs_analyze()) { ++ printf("ERROR:bootargs analyze fail!\n"); ++ continue; ++ } ++ ++ /* make sure that we see CTRL-C and save the old state */ ++ old_ctrlc = disable_ctrlc(0); ++ state = update_to_flash(); ++ /* restore the old state */ ++ disable_ctrlc(old_ctrlc); ++ ++ s_intf[j].exit(); ++ if (state == -1) ++ continue; ++ /* update files have been found on current medium, so just break here */ ++ break; ++ } ++ ++ /* no update file found */ ++ if (state == -1) ++ return -1; ++ env_save(); ++ return 0; ++} ++ ++#endif /* CONFIG_AUTO_UPDATE */ +diff --git a/product/update/mmc_init.c b/product/update/mmc_init.c +new file mode 100644 +index 0000000..6d299f2 +--- /dev/null ++++ b/product/update/mmc_init.c +@@ -0,0 +1,49 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++ ++#include ++#include ++#include "auto_update.h" ++static int mmc_stor_init(void) ++{ ++ struct mmc *mmc; ++ int dev_num; ++#ifdef CONFIG_EMMC ++ int *target_dev = get_target_dev_value(); ++ dev_num = *target_dev; ++#else ++ dev_num = 0; ++#endif ++ mmc = find_mmc_device(dev_num); ++ if (mmc == NULL) { ++ printf("No mmc driver found!\n"); ++ return -1; ++ } ++ ++ if (((unsigned long)mmc->block_dev.vendor[0] == 0) || ++ ((unsigned long)mmc->block_dev.product[0] == 0)) { ++ printf("*No SD card found!\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++static void mmc_stor_exit(void) ++{ ++} +diff --git a/product/update/usb_init.c b/product/update/usb_init.c +new file mode 100644 +index 0000000..4fe99bc +--- /dev/null ++++ b/product/update/usb_init.c +@@ -0,0 +1,60 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2025 HiSilicon (Shanghai) Technologies Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see ++ * . ++ */ ++#include ++#include ++#include ++ ++static int usb_stor_init(void) ++{ ++ int ret = -1; ++#ifndef CONFIG_VENDOR_MC ++try_again: ++ if (usb_stop() < 0) { ++ debug("usb_stop failed\n"); ++ return ret; ++ } ++ /* delay for 1000 ms */ ++ mdelay(1000); ++ ret = usb_init(); ++ if (ret == -ESRCH) ++ goto try_again; ++ ++ if (ret < 0) { ++ debug("usb_init failed!\n"); ++ return ret; ++ } ++ ++ /* ++ * check whether a storage device is attached (assume that it's ++ * a USB memory stick, since nothing else should be attached). ++ */ ++ ret = usb_stor_scan(0); ++ if (ret == -1) ++ debug("No USB device found. Not initialized!\n"); ++#endif ++ return ret; ++} ++ ++static void usb_stor_exit(void) ++{ ++#ifndef CONFIG_VENDOR_MC ++ usb_stop(); ++#endif ++} ++ +diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt +index cf1808e..c2beed7 100644 +--- a/scripts/config_whitelist.txt ++++ b/scripts/config_whitelist.txt +@@ -45,6 +45,7 @@ CONFIG_ARCH_RMOBILE_EXTRAM_BOOT + CONFIG_ARCH_TEGRA + CONFIG_ARCH_USE_BUILTIN_BSWAP + CONFIG_ARC_MMU_VER ++CONFIG_ARM64_SUPPORT_LOAD_FIP + CONFIG_ARMADA100 + CONFIG_ARMADA100_FEC + CONFIG_ARMADA168 +@@ -95,6 +96,11 @@ CONFIG_ATMEL_LEGACY + CONFIG_ATMEL_MCI_8BIT + CONFIG_ATMEL_SPI0 + CONFIG_AT_TRANS ++CONFIG_AUDIO_ENABLE ++CONFIG_AUTO_SD_UPDATE ++CONFIG_AUTO_UPDATE ++CONFIG_AUTO_UPDATE_ADAPTATION ++CONFIG_AUTO_USB_UPDATE + CONFIG_AUTO_ZRELADDR + CONFIG_BACKSIDE_L2_CACHE + CONFIG_BAT_PAIR +@@ -153,6 +159,8 @@ CONFIG_BOOT_PARAMS_ADDR + CONFIG_BOOT_RETRY_MIN + CONFIG_BOOT_RETRY_TIME + CONFIG_BPTR_VIRT_ADDR ++CONFIG_BSP_SDHCI ++CONFIG_BSP_SDHCI_MAX_FREQ + CONFIG_BS_ADDR_DEVICE + CONFIG_BS_ADDR_RAM + CONFIG_BS_COPY_CMD +@@ -188,6 +196,7 @@ CONFIG_CHAIN_BOOT_CMD + CONFIG_CHIP_SELECTS_PER_CTRL + CONFIG_CHIP_SELECT_QUAD_CAPABLE + CONFIG_CHROMEOS_EXTRA_ENV_SETTINGS ++CONFIG_CIPHER_ENABLE + CONFIG_CI_UDC_HAS_HOSTPC + CONFIG_CLK0_DIV + CONFIG_CLK0_EN +@@ -202,6 +211,8 @@ CONFIG_CLOCK_SYNTHESIZER + CONFIG_CM922T_XA10 + CONFIG_CMDLINE_PS_SUPPORT + CONFIG_CMDLINE_TAG ++CONFIG_CMD_ENV ++CONFIG_CMD_UGZIP + CONFIG_CM_INIT + CONFIG_CM_MULTIPLE_SSRAM + CONFIG_CM_REMAP +@@ -214,6 +225,7 @@ CONFIG_CM_TCRAM + CONFIG_CNTL + CONFIG_COLDFIRE + CONFIG_COMMANDS ++CONFIG_COMMAND_HISTORY + CONFIG_COMMON_BOOT + CONFIG_COMMON_ENV_MISC + CONFIG_COMMON_ENV_SETTINGS +@@ -268,6 +280,7 @@ CONFIG_CS8900_BUS16 + CONFIG_CS8900_BUS32 + CONFIG_CTL_JTAG + CONFIG_CTL_TBE ++CONFIG_CUR_UART_BASE + CONFIG_CUSTOMER_BOARD_SUPPORT + CONFIG_D2NET_V2 + CONFIG_DA850_EVM_MAX_CPU_CLK +@@ -298,6 +311,7 @@ CONFIG_DDR_MT47H32M16 + CONFIG_DDR_MT47H64M16 + CONFIG_DDR_PLL2 + CONFIG_DDR_SPD ++CONFIG_DDR_TRAINING_V2 + CONFIG_DEBUG + CONFIG_DEBUG_FS + CONFIG_DEBUG_LED +@@ -419,6 +433,7 @@ CONFIG_EHCI_MMIO_BIG_ENDIAN + CONFIG_EHCI_MXS_PORT0 + CONFIG_EHCI_MXS_PORT1 + CONFIG_ELBC_NAND_SPL_STATIC_PGSIZE ++CONFIG_EMMC + CONFIG_EMU + CONFIG_ENABLE_36BIT_PHYS + CONFIG_ENABLE_MMU +@@ -481,6 +496,7 @@ CONFIG_ETHER_ON_FCC3 + CONFIG_ETHPRIME + CONFIG_ETH_BUFSIZE + CONFIG_ETH_RXSIZE ++CONFIG_EXT4_SPARSE + CONFIG_EXTRA_BOOTARGS + CONFIG_EXTRA_CLOCK + CONFIG_EXTRA_ENV +@@ -551,6 +567,9 @@ CONFIG_FLASH_SECTOR_SIZE + CONFIG_FLASH_SHOW_PROGRESS + CONFIG_FLASH_SPANSION_S29WS_N + CONFIG_FLASH_VERIFY ++CONFIG_FMC_BUFFER_BASE ++CONFIG_FMC_MAX_CS_NUM ++CONFIG_FMC_REG_BASE + CONFIG_FM_PLAT_CLK_DIV + CONFIG_FORCE_DDR_DATA_BUS_WIDTH_32 + CONFIG_FORMIKE +@@ -644,9 +663,17 @@ CONFIG_FTWDT010_BASE + CONFIG_FTWDT010_WATCHDOG + CONFIG_FZOTG266HD0A_BASE + CONFIG_GATEWAYIP ++CONFIG_GENERIC_MMC ++CONFIG_GENERIC_UFS + CONFIG_GICV2 + CONFIG_GLOBAL_DATA_NOT_REG10 + CONFIG_GLOBAL_TIMER ++CONFIG_GMAC_DESC_4_WORD ++CONFIG_GMAC_NUMS ++CONFIG_GMAC_PHY0_ADDR ++CONFIG_GMAC_PHY0_INTERFACE_MODE ++CONFIG_GMAC_PHY1_ADDR ++CONFIG_GMAC_PHY1_INTERFACE_MODE + CONFIG_GMII + CONFIG_GPCNTRL + CONFIG_GPIO_ENABLE_SPI_FLASH +@@ -833,6 +860,7 @@ CONFIG_HVBOOT + CONFIG_HWCONFIG + CONFIG_HW_ENV_SETTINGS + CONFIG_I2C ++CONFIG_I2C_BSP + CONFIG_I2C_CHIPADDRESS + CONFIG_I2C_CMD_TREE + CONFIG_I2C_ENV_EEPROM_BUS +@@ -919,6 +947,7 @@ CONFIG_KIRKWOOD_GPIO + CONFIG_KIRKWOOD_PCIE_INIT + CONFIG_KIRKWOOD_RGMII_PAD_1V8 + CONFIG_KIRQ_EN ++CONFIG_KLAD_ENABLE + CONFIG_KM8321 + CONFIG_KMCOGE4 + CONFIG_KMP204X +@@ -1089,6 +1118,7 @@ CONFIG_MCFFEC + CONFIG_MCFPIT + CONFIG_MCFRTC + CONFIG_MCFTMR ++CONFIG_MCI_MAX_FREQ + CONFIG_MCLK_DIS + CONFIG_MDIO_TIMEOUT + CONFIG_MEMSIZE +@@ -1116,7 +1146,17 @@ CONFIG_MK_edb9315a + CONFIG_MMCBOOTCOMMAND + CONFIG_MMCROOT + CONFIG_MMC_DEFAULT_DEV ++CONFIG_MMC_HS200_SUPPORT ++CONFIG_MMC_HS400_SUPPORT ++CONFIG_MMC_PHY ++CONFIG_MMC_POWER_OFF_TIMEOUT ++CONFIG_MMC_POWER_ON_TIMEROUT ++CONFIG_MMC_RESET_HIGH_TIMEROUT ++CONFIG_MMC_RESET_LOW_TIMEOUT + CONFIG_MMC_RPMB_TRACE ++CONFIG_MMC_SDHCI ++CONFIG_MMC_SDHCI_ADMA ++CONFIG_MMC_SDHCI_SDMA + CONFIG_MMC_SUNXI_SLOT + CONFIG_MMU + CONFIG_MONITOR_IS_IN_RAM +@@ -1258,7 +1298,9 @@ CONFIG_OMAP_USB3PHY1_HOST + CONFIG_ORIGEN + CONFIG_OS1_ENV_ADDR + CONFIG_OS2_ENV_ADDR ++CONFIG_OSD_ENABLE + CONFIG_OTHBOOTARGS ++CONFIG_OTP_ENABLE + CONFIG_OVERWRITE_ETHADDR_ONCE + CONFIG_PAGE_CNT_MASK + CONFIG_PAGE_CNT_SHIFT +@@ -1394,6 +1436,7 @@ CONFIG_PQ_MDS_PIB_ATM + CONFIG_PRAM + CONFIG_PRINTK + CONFIG_PROC_FS ++CONFIG_PRODUCTNAME + CONFIG_PROFILE_ALL_BRANCHES + CONFIG_PROFILING + CONFIG_PROG_FDT1 +@@ -1526,6 +1569,9 @@ CONFIG_SCSI_AHCI_PLAT + CONFIG_SCSI_DEV_LIST + CONFIG_SC_TIMER_CLK + CONFIG_SDCARD ++CONFIG_SDHCI ++CONFIG_SDHCI_ADMA ++CONFIG_SDIO0_FREQ + CONFIG_SDRAM_OFFSET_FOR_RT + CONFIG_SD_BOOT_QSPI + CONFIG_SECBOOT +@@ -1666,6 +1712,8 @@ CONFIG_SPI_HALF_DUPLEX + CONFIG_SPI_IDLE_VAL + CONFIG_SPI_LENGTH + CONFIG_SPI_N25Q256A_RESET ++CONFIG_SPI_NOR_MAX_CHIP_NUM ++CONFIG_SPI_NOR_QUIET_TEST + CONFIG_SPLASHIMAGE_GUARD + CONFIG_SPLASH_SCREEN + CONFIG_SPLASH_SCREEN_ALIGN +@@ -3322,6 +3370,7 @@ CONFIG_SYS_NOR_FTIM1 + CONFIG_SYS_NOR_FTIM2 + CONFIG_SYS_NOR_FTIM3 + CONFIG_SYS_NO_DCACHE ++CONFIG_SYS_NO_FLASH + CONFIG_SYS_NS16550_CLK + CONFIG_SYS_NS16550_CLK_DIV + CONFIG_SYS_NS16550_COM1 +@@ -3938,6 +3987,7 @@ CONFIG_SYS_SXCNFG_VAL + CONFIG_SYS_TBIPA_VALUE + CONFIG_SYS_TCLK + CONFIG_SYS_TEXT_BASE_NOR ++CONFIG_SYS_TEXT_BASE_ORI + CONFIG_SYS_TEXT_BASE_SPL + CONFIG_SYS_TIMERBASE + CONFIG_SYS_TIMER_BASE +@@ -4039,6 +4089,7 @@ CONFIG_SYS_USB_OHCI_CPU_INIT + CONFIG_SYS_USB_OHCI_MAX_ROOT_PORTS + CONFIG_SYS_USB_OHCI_REGS_BASE + CONFIG_SYS_USB_OHCI_SLOT_NAME ++CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS + CONFIG_SYS_USER_SWITCHES_BASE + CONFIG_SYS_USE_BOOT_NORFLASH + CONFIG_SYS_USE_DATAFLASH +diff --git a/securec/LICENSE b/securec/LICENSE +new file mode 100644 +index 0000000..42f2a83 +--- /dev/null ++++ b/securec/LICENSE +@@ -0,0 +1,124 @@ ++木兰宽松许可证, 第2版 ++ ++2020年1月 http://license.coscl.org.cn/MulanPSL2 ++ ++您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: ++ ++0. 定义 ++ ++“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 ++ ++“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 ++ ++“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 ++ ++“法人实体” 是指提交贡献的机构及其“关联实体”。 ++ ++“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 ++ ++1. 授予版权许可 ++ ++每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 ++ ++2. 授予专利许可 ++ ++每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 ++ ++3. 无商标许可 ++ ++“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 ++ ++4. 分发限制 ++ ++您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 ++ ++5. 免责声明与责任限制 ++ ++“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 ++ ++6. 语言 ++ ++“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 ++ ++条款结束 ++ ++如何将木兰宽松许可证,第2版,应用到您的软件 ++ ++如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: ++ ++1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; ++ ++2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; ++ ++3, 请将如下声明文本放入每个源文件的头部注释中。 ++ ++Copyright (c) [Year] [name of copyright holder] ++[Software Name] is licensed under Mulan PSL v2. ++You can use this software according to the terms and conditions of the Mulan PSL v2. ++You may obtain a copy of Mulan PSL v2 at: ++ http://license.coscl.org.cn/MulanPSL2 ++THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++See the Mulan PSL v2 for more details. ++Mulan Permissive Software License,Version 2 ++Mulan Permissive Software License,Version 2 (Mulan PSL v2) ++ ++January 2020 http://license.coscl.org.cn/MulanPSL2 ++ ++Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: ++ ++0. Definition ++ ++Software means the program and related documents which are licensed under this License and comprise all Contribution(s). ++ ++Contribution means the copyrightable work licensed by a particular Contributor under this License. ++ ++Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. ++ ++Legal Entity means the entity making a Contribution and all its Affiliates. ++ ++Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, 'control' means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. ++ ++1. Grant of Copyright License ++ ++Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. ++ ++2. Grant of Patent License ++ ++Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. ++ ++3. No Trademark License ++ ++No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4. ++ ++4. Distribution Restriction ++ ++You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. ++ ++5. Disclaimer of Warranty and Limitation of Liability ++ ++THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT'S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ++ ++6. Language ++ ++THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. ++ ++END OF THE TERMS AND CONDITIONS ++ ++How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software ++ ++To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: ++ ++Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; ++Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package; ++Attach the statement to the appropriate annotated syntax at the beginning of each source file. ++Copyright (c) [Year] [name of copyright holder] ++[Software Name] is licensed under Mulan PSL v2. ++You can use this software according to the terms and conditions of the Mulan PSL v2. ++You may obtain a copy of Mulan PSL v2 at: ++ http://license.coscl.org.cn/MulanPSL2 ++THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++See the Mulan PSL v2 for more details. +\ No newline at end of file +diff --git a/securec/Makefile b/securec/Makefile +new file mode 100644 +index 0000000..9d80433 +--- /dev/null ++++ b/securec/Makefile +@@ -0,0 +1 @@ ++obj-y += src/ +diff --git a/securec/README.en.md b/securec/README.en.md +new file mode 100644 +index 0000000..60c477f +--- /dev/null ++++ b/securec/README.en.md +@@ -0,0 +1,59 @@ ++# libboundscheck ++ ++#### Description ++ ++- following the standard of C11 Annex K (bound-checking interfaces), functions of the common memory/string operation classes, such as memcpy_s, strcpy_s, are selected and implemented. ++ ++- other standard functions in C11 Annex K will be analyzed in the future and implemented in this organization if necessary. ++ ++- handles the release, update, and maintenance of bounds_checking_function. ++ ++#### Function List ++ ++- memcpy_s ++- wmemcpy_s ++- memmove_s ++- wmemmove_s ++- memset_s ++- strcpy_s ++- wcscpy_s ++- strncpy_s ++- wcsncpy_s ++- strcat_s ++- wcscat_s ++- strncat_s ++- wcsncat_s ++- strtok_s ++- wcstok_s ++- sprintf_s ++- swprintf_s ++- vsprintf_s ++- vswprintf_s ++- snprintf_s ++- vsnprintf_s ++- scanf_s ++- wscanf_s ++- vscanf_s ++- vwscanf_s ++- fscanf_s ++- fwscanf_s ++- vfscanf_s ++- vfwscanf_s ++- sscanf_s ++- swscanf_s ++- vsscanf_s ++- vswscanf_s ++- gets_s ++ ++ ++#### Build ++ ++``` ++CC=gcc make ++``` ++The generated Dynamic library libboundscheck.so is stored in the newly created directory lib. ++ ++#### How to use ++1. Copy the libboundscheck.so to the library file directory, for example: "/usr/local/lib/". ++ ++2. To use the libboundscheck, add the “-lboundscheck” parameters to the compiler, for example: “gcc -g -o test test.c -lboundscheck”. +\ No newline at end of file +diff --git a/securec/README.md b/securec/README.md +new file mode 100644 +index 0000000..c16cbb1 +--- /dev/null ++++ b/securec/README.md +@@ -0,0 +1,56 @@ ++# libboundscheck ++ ++#### 介绍 ++- 遵循C11 Annex K (Bounds-checking interfaces)的标准,选取并实现了常见的内存/字符串操作类的函数,如memcpy_s、strcpy_s等函数。 ++- 未来将分析C11 Annex K中的其他标准函数,如果有必要,将在该组织中实现。 ++- 处理边界检查函数的版本发布、更新以及维护。 ++ ++#### 函数清单 ++ ++- memcpy_s ++- wmemcpy_s ++- memmove_s ++- wmemmove_s ++- memset_s ++- strcpy_s ++- wcscpy_s ++- strncpy_s ++- wcsncpy_s ++- strcat_s ++- wcscat_s ++- strncat_s ++- wcsncat_s ++- strtok_s ++- wcstok_s ++- sprintf_s ++- swprintf_s ++- vsprintf_s ++- vswprintf_s ++- snprintf_s ++- vsnprintf_s ++- scanf_s ++- wscanf_s ++- vscanf_s ++- vwscanf_s ++- fscanf_s ++- fwscanf_s ++- vfscanf_s ++- vfwscanf_s ++- sscanf_s ++- swscanf_s ++- vsscanf_s ++- vswscanf_s ++- gets_s ++ ++#### 构建方法 ++ ++运行命令 ++``` ++make CC=gcc ++``` ++生成的动态库libboundscheck.so存放在新创建的lib目录下。 ++ ++#### 使用方法 ++1. 将构建生成的动态库libboundscheck.so放到库文件目录下,例如:"/usr/local/lib/"。 ++ ++2. 为使用libboundscheck,编译程序时需增加编译参数"-lboundscheck",例如:"gcc -g -o test test.c -lboundscheck"。 +\ No newline at end of file +diff --git a/securec/src/Makefile b/securec/src/Makefile +new file mode 100644 +index 0000000..2d013e9 +--- /dev/null ++++ b/securec/src/Makefile +@@ -0,0 +1,39 @@ ++obj-y += securecutil.o ++#obj-y += gets_s.o ++#obj-y += vfwscanf_s.o ++obj-y += secureprintoutput_w.o ++#obj-y += fwscanf_s.o ++#obj-y += scanf_s.o ++#obj-y += vwscanf_s.o ++obj-y += strncpy_s.o ++obj-y += vsprintf_s.o ++obj-y += secureinput_w.o ++obj-y += wcscpy_s.o ++#obj-y += vswscanf_s.o ++obj-y += snprintf_s.o ++#obj-y += wscanf_s.o ++#obj-y += vfscanf_s.o ++#obj-y += vsscanf_s.o ++obj-y += memcpy_s.o ++obj-y += secureinput_a.o ++obj-y += memmove_s.o ++obj-y += wcscat_s.o ++obj-y += wmemcpy_s.o ++#obj-y += sscanf_s.o ++#obj-y += fscanf_s.o ++#obj-y += vscanf_s.o ++obj-y += strcat_s.o ++obj-y += wcstok_s.o ++obj-y += swprintf_s.o ++obj-y += wcsncpy_s.o ++obj-y += wmemmove_s.o ++obj-y += secureprintoutput_a.o ++obj-y += memset_s.o ++obj-y += strtok_s.o ++obj-y += wcsncat_s.o ++obj-y += vswprintf_s.o ++obj-y += sprintf_s.o ++obj-y += strncat_s.o ++obj-y += strcpy_s.o ++#obj-y += swscanf_s.o ++obj-y += vsnprintf_s.o +\ No newline at end of file +diff --git a/securec/src/fscanf_s.c b/securec/src/fscanf_s.c +new file mode 100644 +index 0000000..d3c7f06 +--- /dev/null ++++ b/securec/src/fscanf_s.c +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: fscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * The fscanf_s function is equivalent to fscanf except that the c, s, ++ * and [ conversion specifiers apply to a pair of arguments (unless assignment suppression is indicated by a*) ++ * The fscanf function reads data from the current position of stream into ++ * the locations given by argument (if any). Each argument must be a pointer ++ * to a variable of a type that corresponds to a type specifier in format. ++ * format controls the interpretation of the input fields and has the same ++ * form and function as the format argument for scanf. ++ * ++ * ++ * stream Pointer to FILE structure. ++ * format Format control string, see Format Specifications. ++ * ... Optional arguments. ++ * ++ * ++ * ... The converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; the return value does not include fields that were read but ++ * not assigned. A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int fscanf_s(FILE *stream, const char *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vfscanf_s(stream, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++ +diff --git a/securec/src/fwscanf_s.c b/securec/src/fwscanf_s.c +new file mode 100644 +index 0000000..bd0f12a +--- /dev/null ++++ b/securec/src/fwscanf_s.c +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: fwscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * The fwscanf_s function is the wide-character equivalent of the fscanf_s function ++ * The fwscanf_s function reads data from the current position of stream into ++ * the locations given by argument (if any). Each argument must be a pointer ++ * to a variable of a type that corresponds to a type specifier in format. ++ * format controls the interpretation of the input fields and has the same ++ * form and function as the format argument for scanf. ++ * ++ * ++ * stream Pointer to FILE structure. ++ * format Format control string, see Format Specifications. ++ * ... Optional arguments. ++ * ++ * ++ * ... The converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; the return value does not include fields that were read but ++ * not assigned. A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int fwscanf_s(FILE *stream, const wchar_t *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vfwscanf_s(stream, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++ +diff --git a/securec/src/gets_s.c b/securec/src/gets_s.c +new file mode 100644 +index 0000000..d12495a +--- /dev/null ++++ b/securec/src/gets_s.c +@@ -0,0 +1,71 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: gets_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++/* ++ * The parameter size is buffer size in byte ++ */ ++SECUREC_INLINE void SecTrimCRLF(char *buffer, size_t size) ++{ ++ size_t len = strlen(buffer); ++ --len; /* Unsigned integer wrapping is accepted and is checked afterwards */ ++ while (len < size && (buffer[len] == '\r' || buffer[len] == '\n')) { ++ buffer[len] = '\0'; ++ --len; /* Unsigned integer wrapping is accepted and is checked next loop */ ++ } ++} ++ ++/* ++ * ++ * The gets_s function reads at most one less than the number of characters ++ * specified by destMax from the std input stream, into the array pointed to by buffer ++ * The line consists of all characters up to and including ++ * the first newline character ('\n'). gets_s then replaces the newline ++ * character with a null character ('\0') before returning the line. ++ * If the first character read is the end-of-file character, a null character ++ * is stored at the beginning of buffer and NULL is returned. ++ * ++ * ++ * buffer Storage location for input string. ++ * destMax The size of the buffer. ++ * ++ * ++ * buffer is updated ++ * ++ * ++ * buffer Successful operation ++ * NULL Improper parameter or read fail ++ */ ++char *gets_s(char *buffer, size_t destMax) ++{ ++#ifdef SECUREC_COMPATIBLE_WIN_FORMAT ++ size_t bufferSize = ((destMax == (size_t)(-1)) ? SECUREC_STRING_MAX_LEN : destMax); ++#else ++ size_t bufferSize = destMax; ++#endif ++ ++ if (buffer == NULL || bufferSize == 0 || bufferSize > SECUREC_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_PARAMTER("gets_s"); ++ return NULL; ++ } ++ ++ if (fgets(buffer, (int)bufferSize, SECUREC_STREAM_STDIN) != NULL) { ++ SecTrimCRLF(buffer, bufferSize); ++ return buffer; ++ } ++ ++ return NULL; ++} ++ +diff --git a/securec/src/input.inl b/securec/src/input.inl +new file mode 100644 +index 0000000..5880d45 +--- /dev/null ++++ b/securec/src/input.inl +@@ -0,0 +1,2229 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: Used by secureinput_a.c and secureinput_w.c to include. ++ * This file provides a template function for ANSI and UNICODE compiling by ++ * different type definition. The functions of SecInputS or ++ * SecInputSW provides internal implementation for scanf family API, such as sscanf_s, fscanf_s. ++ * Create: 2014-02-25 ++ * Notes: The formatted input processing results of integers on different platforms are different. ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Performance-sensitive ++ * [reason] Always used in the performance critical path, ++ * and sufficient input validation is performed before calling ++ */ ++#ifndef INPUT_INL_5D13A042_DC3F_4ED9_A8D1_882811274C27 ++#define INPUT_INL_5D13A042_DC3F_4ED9_A8D1_882811274C27 ++ ++#if SECUREC_IN_KERNEL ++#if !defined(SECUREC_CTYPE_MACRO_ADAPT) ++#include ++#endif ++#else ++#if !defined(SECUREC_SYSAPI4VXWORKS) && !defined(SECUREC_CTYPE_MACRO_ADAPT) ++//#include ++#ifdef SECUREC_FOR_WCHAR ++//#include /* For iswspace */ ++#endif ++#endif ++#endif ++ ++#ifndef EOF ++#define EOF (-1) ++#endif ++ ++#define SECUREC_NUM_WIDTH_SHORT 0 ++#define SECUREC_NUM_WIDTH_INT 1 ++#define SECUREC_NUM_WIDTH_LONG 2 ++#define SECUREC_NUM_WIDTH_LONG_LONG 3 /* Also long double */ ++ ++#define SECUREC_BUFFERED_BLOK_SIZE 1024U ++ ++#if defined(SECUREC_VXWORKS_PLATFORM) && !defined(va_copy) && !defined(__va_copy) ++/* The name is the same as system macro. */ ++#define __va_copy(dest, src) do { \ ++ size_t destSize_ = (size_t)sizeof(dest); \ ++ size_t srcSize_ = (size_t)sizeof(src); \ ++ if (destSize_ != srcSize_) { \ ++ SECUREC_MEMCPY_WARP_OPT((dest), (src), sizeof(va_list)); \ ++ } else { \ ++ SECUREC_MEMCPY_WARP_OPT(&(dest), &(src), sizeof(va_list)); \ ++ } \ ++} SECUREC_WHILE_ZERO ++#endif ++ ++#define SECUREC_MULTI_BYTE_MAX_LEN 6 ++ ++/* Compatibility macro name cannot be modifie */ ++#ifndef UNALIGNED ++#if !(defined(_M_IA64)) && !(defined(_M_AMD64)) ++#define UNALIGNED ++#else ++#define UNALIGNED __unaligned ++#endif ++#endif ++ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++/* Max 64bit value is 0xffffffffffffffff */ ++#define SECUREC_MAX_64BITS_VALUE 18446744073709551615ULL ++#define SECUREC_MAX_64BITS_VALUE_DIV_TEN 1844674407370955161ULL ++#define SECUREC_MAX_64BITS_VALUE_CUT_LAST_DIGIT 18446744073709551610ULL ++#define SECUREC_MIN_64BITS_NEG_VALUE 9223372036854775808ULL ++#define SECUREC_MAX_64BITS_POS_VALUE 9223372036854775807ULL ++#define SECUREC_MIN_32BITS_NEG_VALUE 2147483648UL ++#define SECUREC_MAX_32BITS_POS_VALUE 2147483647UL ++#define SECUREC_MAX_32BITS_VALUE 4294967295UL ++#define SECUREC_MAX_32BITS_VALUE_INC 4294967296UL ++#define SECUREC_MAX_32BITS_VALUE_DIV_TEN 429496729UL ++#define SECUREC_LONG_BIT_NUM ((unsigned int)(sizeof(long) << 3U)) ++/* Use ULL to clean up cl6x compilation alerts */ ++#define SECUREC_MAX_LONG_POS_VALUE ((unsigned long)(1ULL << (SECUREC_LONG_BIT_NUM - 1)) - 1) ++#define SECUREC_MIN_LONG_NEG_VALUE ((unsigned long)(1ULL << (SECUREC_LONG_BIT_NUM - 1))) ++ ++/* Covert to long long to clean up cl6x compilation alerts */ ++#define SECUREC_LONG_HEX_BEYOND_MAX(number) (((unsigned long long)(number) >> (SECUREC_LONG_BIT_NUM - 4U)) > 0) ++#define SECUREC_LONG_OCTAL_BEYOND_MAX(number) (((unsigned long long)(number) >> (SECUREC_LONG_BIT_NUM - 3U)) > 0) ++ ++#define SECUREC_QWORD_HEX_BEYOND_MAX(number) (((number) >> (64U - 4U)) > 0) ++#define SECUREC_QWORD_OCTAL_BEYOND_MAX(number) (((number) >> (64U - 3U)) > 0) ++ ++#define SECUREC_LP64_BIT_WIDTH 64 ++#define SECUREC_LP32_BIT_WIDTH 32 ++ ++#define SECUREC_CONVERT_IS_SIGNED(conv) ((conv) == 'd' || (conv) == 'i') ++#endif ++ ++#define SECUREC_BRACE '{' /* [ to { */ ++#define SECUREC_FILED_WIDTH_ENOUGH(spec) ((spec)->widthSet == 0 || (spec)->width > 0) ++#define SECUREC_FILED_WIDTH_DEC(spec) do { \ ++ if ((spec)->widthSet != 0) { \ ++ --(spec)->width; \ ++ } \ ++} SECUREC_WHILE_ZERO ++ ++#ifdef SECUREC_FOR_WCHAR ++/* Bits for all wchar, size is 65536/8, only supports wide characters with a maximum length of two bytes */ ++#define SECUREC_BRACKET_TABLE_SIZE 8192 ++#define SECUREC_EOF WEOF ++#define SECUREC_MB_LEN 16 /* Max. # bytes in multibyte char ,see MB_LEN_MAX */ ++#else ++/* Bits for all char, size is 256/8 */ ++#define SECUREC_BRACKET_TABLE_SIZE 32 ++#define SECUREC_EOF EOF ++#endif ++ ++#if SECUREC_HAVE_WCHART ++#define SECUREC_ARRAY_WIDTH_IS_WRONG(spec) ((spec).arrayWidth == 0 || \ ++ ((spec).isWCharOrLong <= 0 && (spec).arrayWidth > SECUREC_STRING_MAX_LEN) || \ ++ ((spec).isWCharOrLong > 0 && (spec).arrayWidth > SECUREC_WCHAR_STRING_MAX_LEN)) ++#else ++#define SECUREC_ARRAY_WIDTH_IS_WRONG(spec) ((spec).arrayWidth == 0 || (spec).arrayWidth > SECUREC_STRING_MAX_LEN) ++#endif ++ ++#ifdef SECUREC_ON_64BITS ++/* Use 0xffffffffUL mask to pass integer as array length */ ++#define SECUREC_GET_ARRAYWIDTH(argList) (((size_t)va_arg((argList), size_t)) & 0xffffffffUL) ++#else /* !SECUREC_ON_64BITS */ ++#define SECUREC_GET_ARRAYWIDTH(argList) ((size_t)va_arg((argList), size_t)) ++#endif ++ ++typedef struct { ++#ifdef SECUREC_FOR_WCHAR ++ unsigned char *table; /* Default NULL */ ++#else ++ unsigned char table[SECUREC_BRACKET_TABLE_SIZE]; /* Array length is large enough in application scenarios */ ++#endif ++ unsigned char mask; /* Default 0 */ ++} SecBracketTable; ++ ++#ifdef SECUREC_FOR_WCHAR ++#define SECUREC_INIT_BRACKET_TABLE { NULL, 0 } ++#else ++#define SECUREC_INIT_BRACKET_TABLE { {0}, 0 } ++#endif ++ ++#if SECUREC_ENABLE_SCANF_FLOAT ++typedef struct { ++ size_t floatStrTotalLen; /* Initialization must be length of buffer in charater */ ++ size_t floatStrUsedLen; /* Store float string len */ ++ SecChar *floatStr; /* Initialization must point to buffer */ ++ SecChar *allocatedFloatStr; /* Initialization must be NULL to store alloced point */ ++ SecChar buffer[SECUREC_FLOAT_BUFSIZE + 1]; ++} SecFloatSpec; ++#endif ++ ++#define SECUREC_NUMBER_STATE_DEFAULT 0U ++#define SECUREC_NUMBER_STATE_STARTED 1U ++ ++typedef struct { ++ SecInt ch; /* Char read from input */ ++ int charCount; /* Number of characters processed */ ++ void *argPtr; /* Variable parameter pointer, point to the end of the string */ ++ size_t arrayWidth; /* Length of pointer Variable parameter, in charaters */ ++ SecUnsignedInt64 number64; /* Store input number64 value */ ++ unsigned long number; /* Store input number32 value */ ++ int numberWidth; /* 0 = SHORT, 1 = int, > 1 long or L_DOUBLE */ ++ int numberArgType; /* 1 for 64-bit integer, 0 otherwise. use it as decode function index */ ++ unsigned int negative; /* 0 is positive */ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ unsigned int beyondMax; /* Non-zero means beyond */ ++#endif ++ unsigned int numberState; /* Identifies whether to start processing numbers, 1 is can input number */ ++ int width; /* Width number in format */ ++ int widthSet; /* 0 is not set width in format */ ++ int convChr; /* Lowercase format conversion characters */ ++ int oriConvChr; /* Store original format conversion, convChr may change when parsing integers */ ++ signed char isWCharOrLong; /* -1/0 not wchar or long, 1 for wchar or long */ ++ unsigned char suppress; /* 0 is not have %* in format */ ++} SecScanSpec; ++ ++#ifdef SECUREC_FOR_WCHAR ++#define SECUREC_GETC fgetwc ++#define SECUREC_UN_GETC ungetwc ++/* Only supports wide characters with a maximum length of two bytes in format string */ ++#define SECUREC_BRACKET_CHAR_MASK 0xffffU ++#else ++#define SECUREC_GETC fgetc ++#define SECUREC_UN_GETC ungetc ++#define SECUREC_BRACKET_CHAR_MASK 0xffU ++#endif ++ ++#define SECUREC_CHAR_SIZE ((unsigned int)(sizeof(SecChar))) ++/* To avoid 648, mask high bit: 0x00ffffff 0x0000ffff or 0x00000000 */ ++#define SECUREC_CHAR_MASK_HIGH (((((((((unsigned int)(-1) >> SECUREC_CHAR_SIZE) >> SECUREC_CHAR_SIZE) >> \ ++ SECUREC_CHAR_SIZE) >> SECUREC_CHAR_SIZE) >> \ ++ SECUREC_CHAR_SIZE) >> SECUREC_CHAR_SIZE) >> \ ++ SECUREC_CHAR_SIZE) >> SECUREC_CHAR_SIZE) ++ ++/* For char is 0xff, wcahr_t is 0xffff or 0xffffffff. */ ++#define SECUREC_CHAR_MASK (~((((((((((unsigned int)(-1) & SECUREC_CHAR_MASK_HIGH) << \ ++ SECUREC_CHAR_SIZE) << SECUREC_CHAR_SIZE) << \ ++ SECUREC_CHAR_SIZE) << SECUREC_CHAR_SIZE) << \ ++ SECUREC_CHAR_SIZE) << SECUREC_CHAR_SIZE) << \ ++ SECUREC_CHAR_SIZE) << SECUREC_CHAR_SIZE)) ++ ++/* According wchar_t has multiple bytes, so use sizeof */ ++#define SECUREC_GET_CHAR(stream, outCh) do { \ ++ if ((stream)->count >= sizeof(SecChar)) { \ ++ *(outCh) = (SecInt)(SECUREC_CHAR_MASK & \ ++ (unsigned int)(int)(*((const SecChar *)(const void *)(stream)->cur))); \ ++ (stream)->cur += sizeof(SecChar); \ ++ (stream)->count -= sizeof(SecChar); \ ++ } else { \ ++ *(outCh) = SECUREC_EOF; \ ++ } \ ++} SECUREC_WHILE_ZERO ++ ++#define SECUREC_UN_GET_CHAR(stream) do { \ ++ if ((stream)->cur > (stream)->base) { \ ++ (stream)->cur -= sizeof(SecChar); \ ++ (stream)->count += sizeof(SecChar); \ ++ } \ ++} SECUREC_WHILE_ZERO ++ ++/* Convert wchar_t to int and then to unsigned int to keep data clearing warning */ ++#define SECUREC_TO_LOWERCASE(chr) ((int)((unsigned int)(int)(chr) | (unsigned int)('a' - 'A'))) ++ ++/* Record a flag for each bit */ ++#define SECUREC_BRACKET_INDEX(x) ((unsigned int)(x) >> 3U) ++#define SECUREC_BRACKET_VALUE(x) ((unsigned char)(1U << ((unsigned int)(x) & 7U))) ++#if SECUREC_IN_KERNEL ++#define SECUREC_CONVERT_IS_UNSIGNED(conv) ((conv) == 'x' || (conv) == 'o' || (conv) == 'u') ++#endif ++ ++/* ++ * Set char in %[xxx] into table, only supports wide characters with a maximum length of two bytes ++ */ ++SECUREC_INLINE void SecBracketSetBit(unsigned char *table, SecUnsignedChar ch) ++{ ++ unsigned int tableIndex = SECUREC_BRACKET_INDEX(((unsigned int)(int)ch & SECUREC_BRACKET_CHAR_MASK)); ++ unsigned int tableValue = SECUREC_BRACKET_VALUE(((unsigned int)(int)ch & SECUREC_BRACKET_CHAR_MASK)); ++ /* Do not use |= optimize this code, it will cause compiling warning */ ++ table[tableIndex] = (unsigned char)(table[tableIndex] | tableValue); ++} ++ ++SECUREC_INLINE void SecBracketSetBitRange(unsigned char *table, SecUnsignedChar startCh, SecUnsignedChar endCh) ++{ ++ SecUnsignedChar expCh; ++ /* %[a-z] %[a-a] Format %[a-\xff] end is 0xFF, condition (expCh <= endChar) cause dead loop */ ++ for (expCh = startCh; expCh < endCh; ++expCh) { ++ SecBracketSetBit(table, expCh); ++ } ++ SecBracketSetBit(table, endCh); ++} ++/* ++ * Determine whether the expression can be satisfied ++ */ ++SECUREC_INLINE int SecCanInputForBracket(int convChr, SecInt ch, const SecBracketTable *bracketTable) ++{ ++ unsigned int tableIndex = SECUREC_BRACKET_INDEX(((unsigned int)(int)ch & SECUREC_BRACKET_CHAR_MASK)); ++ unsigned int tableValue = SECUREC_BRACKET_VALUE(((unsigned int)(int)ch & SECUREC_BRACKET_CHAR_MASK)); ++#ifdef SECUREC_FOR_WCHAR ++ if (((unsigned int)(int)ch & (~(SECUREC_BRACKET_CHAR_MASK))) != 0) { ++ /* The value of the wide character exceeds the size of two bytes */ ++ return 0; ++ } ++ return (int)(convChr == SECUREC_BRACE && ++ (((unsigned int)bracketTable->table[tableIndex] ^ (unsigned int)bracketTable->mask) & tableValue) != 0); ++#else ++ return (int)(convChr == SECUREC_BRACE && ++ (((unsigned int)bracketTable->table[tableIndex] ^ (unsigned int)bracketTable->mask) & tableValue) != 0); ++#endif ++} ++ ++/* ++ * String input ends when blank character is encountered ++ */ ++SECUREC_INLINE int SecCanInputString(int convChr, SecInt ch) ++{ ++ return (int)(convChr == 's' && ++ (!(ch >= SECUREC_CHAR('\t') && ch <= SECUREC_CHAR('\r')) && ch != SECUREC_CHAR(' '))); ++} ++ ++/* ++ * Can input a character when format is %c ++ */ ++SECUREC_INLINE int SecCanInputCharacter(int convChr) ++{ ++ return (int)(convChr == 'c'); ++} ++ ++/* ++ * Determine if it is a 64-bit pointer function ++ * Return 0 is not ,1 is 64bit pointer ++ */ ++SECUREC_INLINE int SecNumberArgType(size_t sizeOfVoidStar) ++{ ++ /* Point size is 4 or 8 , Under the 64 bit system, the value not 0 */ ++ /* To clear e778 */ ++ if ((sizeOfVoidStar & sizeof(SecInt64)) != 0) { ++ return 1; ++ } ++ return 0; ++} ++SECUREC_INLINE int SecIsDigit(SecInt ch); ++SECUREC_INLINE int SecIsXdigit(SecInt ch); ++SECUREC_INLINE int SecIsSpace(SecInt ch); ++SECUREC_INLINE SecInt SecSkipSpaceChar(SecFileStream *stream, int *counter); ++SECUREC_INLINE SecInt SecGetChar(SecFileStream *stream, int *counter); ++SECUREC_INLINE void SecUnGetChar(SecInt ch, SecFileStream *stream, int *counter); ++ ++#if SECUREC_ENABLE_SCANF_FLOAT ++ ++/* ++ * Convert a floating point string to a floating point number ++ */ ++SECUREC_INLINE int SecAssignNarrowFloat(const char *floatStr, const SecScanSpec *spec) ++{ ++ char *endPtr = NULL; ++ double d; ++#if SECUREC_SUPPORT_STRTOLD ++ if (spec->numberWidth == SECUREC_NUM_WIDTH_LONG_LONG) { ++ long double d2 = strtold(floatStr, &endPtr); ++ if (endPtr == floatStr) { ++ return -1; ++ } ++ *(long double UNALIGNED *)(spec->argPtr) = d2; ++ return 0; ++ } ++#endif ++ d = strtod(floatStr, &endPtr); ++ /* cannot detect if endPtr points to the end of floatStr,because strtod handles only two characters for 1.E */ ++ if (endPtr == floatStr) { ++ return -1; ++ } ++ if (spec->numberWidth > SECUREC_NUM_WIDTH_INT) { ++ *(double UNALIGNED *)(spec->argPtr) = (double)d; ++ } else { ++ *(float UNALIGNED *)(spec->argPtr) = (float)d; ++ } ++ return 0; ++} ++ ++#ifdef SECUREC_FOR_WCHAR ++/* ++ * Convert a floating point wchar string to a floating point number ++ * Success ret 0 ++ */ ++SECUREC_INLINE int SecAssignWideFloat(const SecFloatSpec *floatSpec, const SecScanSpec *spec) ++{ ++ int retVal; ++ /* Convert float string */ ++ size_t mbsLen; ++ size_t tempFloatStrLen = (size_t)(floatSpec->floatStrUsedLen + 1) * sizeof(wchar_t); ++ char *tempFloatStr = (char *)SECUREC_MALLOC(tempFloatStrLen); ++ if (tempFloatStr == NULL) { ++ return -1; ++ } ++ tempFloatStr[0] = '\0'; ++ SECUREC_MASK_MSVC_CRT_WARNING ++ mbsLen = wcstombs(tempFloatStr, floatSpec->floatStr, tempFloatStrLen - 1); ++ SECUREC_END_MASK_MSVC_CRT_WARNING ++ /* This condition must satisfy mbsLen is not -1 */ ++ if (mbsLen >= tempFloatStrLen) { ++ SECUREC_FREE(tempFloatStr); ++ return -1; ++ } ++ tempFloatStr[mbsLen] = '\0'; ++ retVal = SecAssignNarrowFloat(tempFloatStr, spec); ++ SECUREC_FREE(tempFloatStr); ++ return retVal; ++} ++#endif ++ ++SECUREC_INLINE int SecAssignFloat(const SecFloatSpec *floatSpec, const SecScanSpec *spec) ++{ ++#ifdef SECUREC_FOR_WCHAR ++ return SecAssignWideFloat(floatSpec, spec); ++#else ++ return SecAssignNarrowFloat(floatSpec->floatStr, spec); ++#endif ++} ++ ++/* ++ * Init SecFloatSpec before parse format ++ */ ++SECUREC_INLINE void SecInitFloatSpec(SecFloatSpec *floatSpec) ++{ ++ floatSpec->floatStr = floatSpec->buffer; ++ floatSpec->allocatedFloatStr = NULL; ++ floatSpec->floatStrTotalLen = sizeof(floatSpec->buffer) / sizeof(floatSpec->buffer[0]); ++ floatSpec->floatStrUsedLen = 0; ++} ++ ++SECUREC_INLINE void SecFreeFloatSpec(SecFloatSpec *floatSpec, int *doneCount) ++{ ++ /* 2014.3.6 add, clear the stack data */ ++ if (memset_s(floatSpec->buffer, sizeof(floatSpec->buffer), 0, sizeof(floatSpec->buffer)) != EOK) { ++ *doneCount = 0; /* This code just to meet the coding requirements */ ++ } ++ /* The pFloatStr can be alloced in SecExtendFloatLen function, clear and free it */ ++ if (floatSpec->allocatedFloatStr != NULL) { ++ size_t bufferSize = floatSpec->floatStrTotalLen * sizeof(SecChar); ++ if (memset_s(floatSpec->allocatedFloatStr, bufferSize, 0, bufferSize) != EOK) { ++ *doneCount = 0; /* This code just to meet the coding requirements */ ++ } ++ SECUREC_FREE(floatSpec->allocatedFloatStr); ++ floatSpec->allocatedFloatStr = NULL; ++ floatSpec->floatStr = NULL; ++ } ++} ++ ++/* ++ * Splice floating point string ++ * Return 0 OK ++ */ ++SECUREC_INLINE int SecExtendFloatLen(SecFloatSpec *floatSpec) ++{ ++ if (floatSpec->floatStrUsedLen >= floatSpec->floatStrTotalLen) { ++ /* Buffer size is len x sizeof(SecChar) */ ++ size_t oriSize = floatSpec->floatStrTotalLen * sizeof(SecChar); ++ /* Add one character to clear tool warning */ ++ size_t nextSize = (oriSize * 2) + sizeof(SecChar); /* Multiply 2 to extend buffer size */ ++ ++ /* Prevents integer overflow, the maximum length of SECUREC_MAX_WIDTH_LEN is enough */ ++ if (nextSize <= (size_t)SECUREC_MAX_WIDTH_LEN) { ++ void *nextBuffer = (void *)SECUREC_MALLOC(nextSize); ++ if (nextBuffer == NULL) { ++ return -1; ++ } ++ if (memcpy_s(nextBuffer, nextSize, floatSpec->floatStr, oriSize) != EOK) { ++ SECUREC_FREE(nextBuffer); /* This is a dead code, just to meet the coding requirements */ ++ return -1; ++ } ++ /* Clear old buffer memory */ ++ if (memset_s(floatSpec->floatStr, oriSize, 0, oriSize) != EOK) { ++ SECUREC_FREE(nextBuffer); /* This is a dead code, just to meet the coding requirements */ ++ return -1; ++ } ++ /* Free old allocated buffer */ ++ if (floatSpec->allocatedFloatStr != NULL) { ++ SECUREC_FREE(floatSpec->allocatedFloatStr); ++ } ++ floatSpec->allocatedFloatStr = (SecChar *)(nextBuffer); /* Use to clear free on stack warning */ ++ floatSpec->floatStr = (SecChar *)(nextBuffer); ++ floatSpec->floatStrTotalLen = nextSize / sizeof(SecChar); /* Get buffer total len in character */ ++ return 0; ++ } ++ return -1; /* Next size is beyond max */ ++ } ++ return 0; ++} ++ ++/* Do not use localeconv()->decimal_pointif onlay support '.' */ ++SECUREC_INLINE int SecIsFloatDecimal(SecChar ch) ++{ ++ return (int)(ch == SECUREC_CHAR('.')); ++} ++ ++SECUREC_INLINE int SecInputFloatSign(SecFileStream *stream, SecScanSpec *spec, SecFloatSpec *floatSpec) ++{ ++ if (!SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ return 0; ++ } ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ if (spec->ch == SECUREC_CHAR('+') || spec->ch == SECUREC_CHAR('-')) { ++ SECUREC_FILED_WIDTH_DEC(spec); /* Make sure the count after un get char is correct */ ++ if (spec->ch == SECUREC_CHAR('-')) { ++ floatSpec->floatStr[floatSpec->floatStrUsedLen] = SECUREC_CHAR('-'); ++ ++floatSpec->floatStrUsedLen; ++ if (SecExtendFloatLen(floatSpec) != 0) { ++ return -1; ++ } ++ } ++ } else { ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ } ++ return 0; ++} ++ ++SECUREC_INLINE int SecInputFloatDigit(SecFileStream *stream, SecScanSpec *spec, SecFloatSpec *floatSpec) ++{ ++ /* Now get integral part */ ++ while (SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ if (SecIsDigit(spec->ch) == 0) { ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ return 0; ++ } ++ SECUREC_FILED_WIDTH_DEC(spec); /* Must be behind un get char, otherwise the logic is incorrect */ ++ spec->numberState = SECUREC_NUMBER_STATE_STARTED; ++ floatSpec->floatStr[floatSpec->floatStrUsedLen] = (SecChar)spec->ch; ++ ++floatSpec->floatStrUsedLen; ++ if (SecExtendFloatLen(floatSpec) != 0) { ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++/* ++* Scan value of exponent. ++* Return 0 OK ++*/ ++SECUREC_INLINE int SecInputFloatE(SecFileStream *stream, SecScanSpec *spec, SecFloatSpec *floatSpec) ++{ ++ if (SecInputFloatSign(stream, spec, floatSpec) == -1) { ++ return -1; ++ } ++ if (SecInputFloatDigit(stream, spec, floatSpec) != 0) { ++ return -1; ++ } ++ return 0; ++} ++ ++SECUREC_INLINE int SecInputFloatFractional(SecFileStream *stream, SecScanSpec *spec, SecFloatSpec *floatSpec) ++{ ++ if (SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ if (SecIsFloatDecimal((SecChar)spec->ch) == 0) { ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ return 0; ++ } ++ SECUREC_FILED_WIDTH_DEC(spec); /* Must be behind un get char, otherwise the logic is incorrect */ ++ /* Now check for decimal */ ++ floatSpec->floatStr[floatSpec->floatStrUsedLen] = (SecChar)spec->ch; ++ ++floatSpec->floatStrUsedLen; ++ if (SecExtendFloatLen(floatSpec) != 0) { ++ return -1; ++ } ++ if (SecInputFloatDigit(stream, spec, floatSpec) != 0) { ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++SECUREC_INLINE int SecInputFloatExponent(SecFileStream *stream, SecScanSpec *spec, SecFloatSpec *floatSpec) ++{ ++ /* Now get exponent part */ ++ if (spec->numberState == SECUREC_NUMBER_STATE_STARTED && SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ if (spec->ch != SECUREC_CHAR('e') && spec->ch != SECUREC_CHAR('E')) { ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ return 0; ++ } ++ SECUREC_FILED_WIDTH_DEC(spec); /* Must be behind un get char, otherwise the logic is incorrect */ ++ floatSpec->floatStr[floatSpec->floatStrUsedLen] = SECUREC_CHAR('e'); ++ ++floatSpec->floatStrUsedLen; ++ if (SecExtendFloatLen(floatSpec) != 0) { ++ return -1; ++ } ++ if (SecInputFloatE(stream, spec, floatSpec) != 0) { ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++/* ++* Scan %f. ++* Return 0 OK ++*/ ++SECUREC_INLINE int SecInputFloat(SecFileStream *stream, SecScanSpec *spec, SecFloatSpec *floatSpec) ++{ ++ floatSpec->floatStrUsedLen = 0; ++ ++ /* The following code sequence is strict */ ++ if (SecInputFloatSign(stream, spec, floatSpec) != 0) { ++ return -1; ++ } ++ if (SecInputFloatDigit(stream, spec, floatSpec) != 0) { ++ return -1; ++ } ++ if (SecInputFloatFractional(stream, spec, floatSpec) != 0) { ++ return -1; ++ } ++ if (SecInputFloatExponent(stream, spec, floatSpec) != 0) { ++ return -1; ++ } ++ ++ /* Make sure have a string terminator, buffer is large enough */ ++ floatSpec->floatStr[floatSpec->floatStrUsedLen] = SECUREC_CHAR('\0'); ++ if (spec->numberState == SECUREC_NUMBER_STATE_STARTED) { ++ return 0; ++ } ++ return -1; ++} ++#endif ++ ++#if (!defined(SECUREC_FOR_WCHAR) && SECUREC_HAVE_WCHART && SECUREC_HAVE_MBTOWC) || \ ++ (!defined(SECUREC_FOR_WCHAR) && defined(SECUREC_COMPATIBLE_VERSION)) ++/* only multi-bytes string need isleadbyte() function */ ++SECUREC_INLINE int SecIsLeadByte(SecInt ch) ++{ ++ unsigned int c = (unsigned int)ch; ++#if !(defined(_MSC_VER) || defined(_INC_WCTYPE)) ++ return (int)(c & 0x80U); /* Use bitwise operation to check if the most significant bit is 1 */ ++#else ++ return (int)isleadbyte((int)(c & 0xffU)); /* Use bitwise operations to limit character values to valid ranges */ ++#endif ++} ++#endif ++ ++/* ++ * Parsing whether it is a wide character ++ */ ++SECUREC_INLINE void SecUpdateWcharFlagByType(SecUnsignedChar ch, SecScanSpec *spec) ++{ ++ if (spec->isWCharOrLong != 0) { ++ /* Wide character identifiers have been explicitly set by l or h flag */ ++ return; ++ } ++ ++ /* Set default flag */ ++#if defined(SECUREC_FOR_WCHAR) && defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ spec->isWCharOrLong = 1; /* On windows wide char version %c %s %[ is wide char */ ++#else ++ spec->isWCharOrLong = -1; /* On linux all version %c %s %[ is multi char */ ++#endif ++ ++ if (ch == SECUREC_CHAR('C') || ch == SECUREC_CHAR('S')) { ++#if defined(SECUREC_FOR_WCHAR) && defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ spec->isWCharOrLong = -1; /* On windows wide char version %C %S is multi char */ ++#else ++ spec->isWCharOrLong = 1; /* On linux all version %C %S is wide char */ ++#endif ++ } ++ ++ return; ++} ++/* ++ * Decode %l %ll ++ */ ++SECUREC_INLINE void SecDecodeScanQualifierL(const SecUnsignedChar **format, SecScanSpec *spec) ++{ ++ const SecUnsignedChar *fmt = *format; ++ if (*(fmt + 1) == SECUREC_CHAR('l')) { ++ spec->numberArgType = 1; ++ spec->numberWidth = SECUREC_NUM_WIDTH_LONG_LONG; ++ ++fmt; ++ } else { ++ spec->numberWidth = SECUREC_NUM_WIDTH_LONG; ++#if defined(SECUREC_ON_64BITS) && !(defined(SECUREC_COMPATIBLE_WIN_FORMAT)) ++ /* On window 64 system sizeof long is 32bit */ ++ spec->numberArgType = 1; ++#endif ++ spec->isWCharOrLong = 1; ++ } ++ *format = fmt; ++} ++ ++/* ++ * Decode %I %I43 %I64 %Id %Ii %Io ... ++ * Set finishFlag to 1 finish Flag ++ */ ++SECUREC_INLINE void SecDecodeScanQualifierI(const SecUnsignedChar **format, SecScanSpec *spec, int *finishFlag) ++{ ++ const SecUnsignedChar *fmt = *format; ++ if ((*(fmt + 1) == SECUREC_CHAR('6')) && ++ (*(fmt + 2) == SECUREC_CHAR('4'))) { /* Offset 2 for I64 */ ++ spec->numberArgType = 1; ++ *format = *format + 2; /* Add 2 to skip I64 point to '4' next loop will inc */ ++ } else if ((*(fmt + 1) == SECUREC_CHAR('3')) && ++ (*(fmt + 2) == SECUREC_CHAR('2'))) { /* Offset 2 for I32 */ ++ *format = *format + 2; /* Add 2 to skip I32 point to '2' next loop will inc */ ++ } else if ((*(fmt + 1) == SECUREC_CHAR('d')) || ++ (*(fmt + 1) == SECUREC_CHAR('i')) || ++ (*(fmt + 1) == SECUREC_CHAR('o')) || ++ (*(fmt + 1) == SECUREC_CHAR('x')) || ++ (*(fmt + 1) == SECUREC_CHAR('X'))) { ++ spec->numberArgType = SecNumberArgType(sizeof(void *)); ++ } else { ++ /* For %I */ ++ spec->numberArgType = SecNumberArgType(sizeof(void *)); ++ *finishFlag = 1; ++ } ++} ++ ++SECUREC_INLINE int SecDecodeScanWidth(const SecUnsignedChar **format, SecScanSpec *spec) ++{ ++ const SecUnsignedChar *fmt = *format; ++ while (SecIsDigit((SecInt)(int)(*fmt)) != 0) { ++ spec->widthSet = 1; ++ if (SECUREC_MUL_TEN_ADD_BEYOND_MAX(spec->width)) { ++ return -1; ++ } ++ spec->width = (int)SECUREC_MUL_TEN((unsigned int)spec->width) + (unsigned char)(*fmt - SECUREC_CHAR('0')); ++ ++fmt; ++ } ++ *format = fmt; ++ return 0; ++} ++ ++/* ++ * Init default flags for each format. do not init ch this variable is context-dependent ++ */ ++SECUREC_INLINE void SecSetDefaultScanSpec(SecScanSpec *spec) ++{ ++ /* The ch and charCount member variables cannot be initialized here */ ++ spec->argPtr = NULL; ++ spec->arrayWidth = 0; ++ spec->number64 = 0; ++ spec->number = 0; ++ spec->numberWidth = SECUREC_NUM_WIDTH_INT; /* 0 = SHORT, 1 = int, > 1 long or L_DOUBLE */ ++ spec->numberArgType = 0; /* 1 for 64-bit integer, 0 otherwise */ ++ spec->width = 0; ++ spec->widthSet = 0; ++ spec->convChr = 0; ++ spec->oriConvChr = 0; ++ spec->isWCharOrLong = 0; ++ spec->suppress = 0; ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ spec->beyondMax = 0; ++#endif ++ spec->negative = 0; ++ spec->numberState = SECUREC_NUMBER_STATE_DEFAULT; ++} ++ ++/* ++ * Decode qualifier %I %L %h ... ++ * Set finishFlag to 1 finish Flag ++ */ ++SECUREC_INLINE void SecDecodeScanQualifier(const SecUnsignedChar **format, SecScanSpec *spec, int *finishFlag) ++{ ++ switch (**format) { ++ case SECUREC_CHAR('F'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('N'): ++ break; ++ case SECUREC_CHAR('h'): ++ --spec->numberWidth; /* The h for SHORT , hh for CHAR */ ++ spec->isWCharOrLong = -1; ++ break; ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++ case SECUREC_CHAR('j'): ++ spec->numberWidth = SECUREC_NUM_WIDTH_LONG_LONG; /* For intmax_t or uintmax_t */ ++ spec->numberArgType = 1; ++ break; ++ case SECUREC_CHAR('t'): /* fall-through */ /* FALLTHRU */ ++#endif ++#if SECUREC_IN_KERNEL ++ case SECUREC_CHAR('Z'): /* fall-through */ /* FALLTHRU */ ++#endif ++ case SECUREC_CHAR('z'): ++#ifdef SECUREC_ON_64BITS ++ spec->numberWidth = SECUREC_NUM_WIDTH_LONG_LONG; ++ spec->numberArgType = 1; ++#else ++ spec->numberWidth = SECUREC_NUM_WIDTH_LONG; ++#endif ++ break; ++ case SECUREC_CHAR('L'): /* For long double */ /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('q'): ++ spec->numberWidth = SECUREC_NUM_WIDTH_LONG_LONG; ++ spec->numberArgType = 1; ++ break; ++ case SECUREC_CHAR('l'): ++ SecDecodeScanQualifierL(format, spec); ++ break; ++ case SECUREC_CHAR('w'): ++ spec->isWCharOrLong = 1; ++ break; ++ case SECUREC_CHAR('*'): ++ spec->suppress = 1; ++ break; ++ case SECUREC_CHAR('I'): ++ SecDecodeScanQualifierI(format, spec, finishFlag); ++ break; ++ default: ++ *finishFlag = 1; ++ break; ++ } ++} ++/* ++ * Decode width and qualifier in format ++ */ ++SECUREC_INLINE int SecDecodeScanFlag(const SecUnsignedChar **format, SecScanSpec *spec) ++{ ++ const SecUnsignedChar *fmt = *format; ++ int finishFlag = 0; ++ ++ do { ++ ++fmt; /* First skip % , next seek fmt */ ++ /* May %*6d , so put it inside the loop */ ++ if (SecDecodeScanWidth(&fmt, spec) != 0) { ++ return -1; ++ } ++ SecDecodeScanQualifier(&fmt, spec, &finishFlag); ++ } while (finishFlag == 0); ++ *format = fmt; ++ return 0; ++} ++ ++/* ++ * Judging whether a zeroing buffer is needed according to different formats ++ */ ++SECUREC_INLINE int SecDecodeClearFormat(const SecUnsignedChar *format, int *convChr) ++{ ++ const SecUnsignedChar *fmt = format; ++ /* To lowercase */ ++ int ch = SECUREC_TO_LOWERCASE(*fmt); ++ if (!(ch == 'c' || ch == 's' || ch == SECUREC_BRACE)) { ++ return -1; /* First argument is not a string type */ ++ } ++ if (ch == SECUREC_BRACE) { ++#if !(defined(SECUREC_COMPATIBLE_WIN_FORMAT)) ++ if (*fmt == SECUREC_CHAR('{')) { ++ return -1; ++ } ++#endif ++ ++fmt; ++ if (*fmt == SECUREC_CHAR('^')) { ++ ++fmt; ++ } ++ if (*fmt == SECUREC_CHAR(']')) { ++ ++fmt; ++ } ++ while (*fmt != SECUREC_CHAR('\0') && *fmt != SECUREC_CHAR(']')) { ++ ++fmt; ++ } ++ if (*fmt == SECUREC_CHAR('\0')) { ++ return -1; /* Trunc'd format string */ ++ } ++ } ++ *convChr = ch; ++ return 0; ++} ++ ++/* ++ * Add L'\0' for wchar string , add '\0' for char string ++ */ ++SECUREC_INLINE void SecAddEndingZero(void *ptr, const SecScanSpec *spec) ++{ ++ if (spec->suppress == 0) { ++ *(char *)ptr = '\0'; ++#if SECUREC_HAVE_WCHART ++ if (spec->isWCharOrLong > 0) { ++ *(wchar_t UNALIGNED *)ptr = L'\0'; ++ } ++#endif ++ } ++} ++ ++SECUREC_INLINE void SecDecodeClearArg(SecScanSpec *spec, va_list argList) ++{ ++ va_list argListSave; /* Backup for argList value, this variable don't need initialized */ ++ (void)SECUREC_MEMSET_FUNC_OPT(&argListSave, 0, sizeof(va_list)); /* To clear e530 argListSave not initialized */ ++#if defined(va_copy) ++ va_copy(argListSave, argList); ++#elif defined(__va_copy) /* For vxworks */ ++ __va_copy(argListSave, argList); ++#else ++ argListSave = argList; ++#endif ++ spec->argPtr = (void *)va_arg(argListSave, void *); ++ /* Get the next argument, size of the array in characters */ ++ /* Use 0xffffffffUL mask to Support pass integer as array length */ ++ spec->arrayWidth = ((size_t)(va_arg(argListSave, size_t))) & 0xffffffffUL; ++ va_end(argListSave); ++ /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ (void)argListSave; ++} ++ ++#ifdef SECUREC_FOR_WCHAR ++/* ++ * Clean up the first %s %c buffer to zero for wchar version ++ */ ++void SecClearDestBufW(const wchar_t *buffer, const wchar_t *format, va_list argList) ++#else ++/* ++ * Clean up the first %s %c buffer to zero for char version ++ */ ++void SecClearDestBuf(const char *buffer, const char *format, va_list argList) ++#endif ++{ ++ SecScanSpec spec; ++ int convChr = 0; ++ const SecUnsignedChar *fmt = (const SecUnsignedChar *)format; ++ ++ /* Find first % */ ++ while (*fmt != SECUREC_CHAR('\0') && *fmt != SECUREC_CHAR('%')) { ++ ++fmt; ++ } ++ if (*fmt == SECUREC_CHAR('\0')) { ++ return; ++ } ++ ++ SecSetDefaultScanSpec(&spec); ++ if (SecDecodeScanFlag(&fmt, &spec) != 0) { ++ return; ++ } ++ ++ /* Update wchar flag for %S %C */ ++ SecUpdateWcharFlagByType(*fmt, &spec); ++ if (spec.suppress != 0) { ++ return; ++ } ++ ++ if (SecDecodeClearFormat(fmt, &convChr) != 0) { ++ return; ++ } ++ ++ if (*buffer != SECUREC_CHAR('\0') && convChr != 's') { ++ /* ++ * When buffer not empty just clear %s. ++ * Example call sscanf by argment of (" \n", "%s", s, sizeof(s)) ++ */ ++ return; ++ } ++ ++ SecDecodeClearArg(&spec, argList); ++ /* There is no need to judge the upper limit */ ++ if (spec.arrayWidth == 0 || spec.argPtr == NULL) { ++ return; ++ } ++ /* Clear one char */ ++ SecAddEndingZero(spec.argPtr, &spec); ++ return; ++} ++ ++/* ++ * Assign number to output buffer ++ */ ++SECUREC_INLINE void SecAssignNumber(const SecScanSpec *spec) ++{ ++ void *argPtr = spec->argPtr; ++ if (spec->numberArgType != 0) { ++#if defined(SECUREC_VXWORKS_PLATFORM) ++#if defined(SECUREC_VXWORKS_PLATFORM_COMP) ++ *(SecInt64 UNALIGNED *)argPtr = (SecInt64)(spec->number64); ++#else ++ /* Take number64 as unsigned number unsigned to int clear Compile warning */ ++ *(SecInt64 UNALIGNED *)argPtr = *(SecUnsignedInt64 *)(&(spec->number64)); ++#endif ++#else ++ /* Take number64 as unsigned number */ ++ *(SecInt64 UNALIGNED *)argPtr = (SecInt64)(spec->number64); ++#endif ++ return; ++ } ++ if (spec->numberWidth > SECUREC_NUM_WIDTH_INT) { ++ /* Take number as unsigned number */ ++ *(long UNALIGNED *)argPtr = (long)(spec->number); ++ } else if (spec->numberWidth == SECUREC_NUM_WIDTH_INT) { ++ *(int UNALIGNED *)argPtr = (int)(spec->number); ++ } else if (spec->numberWidth == SECUREC_NUM_WIDTH_SHORT) { ++ /* Take number as unsigned number */ ++ *(short UNALIGNED *)argPtr = (short)(spec->number); ++ } else { /* < 0 for hh format modifier */ ++ /* Take number as unsigned number */ ++ *(char UNALIGNED *)argPtr = (char)(spec->number); ++ } ++} ++ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++/* ++ * Judge the long bit width ++ */ ++SECUREC_INLINE int SecIsLongBitEqual(int bitNum) ++{ ++ return (int)((unsigned int)bitNum == SECUREC_LONG_BIT_NUM); ++} ++#endif ++ ++/* ++ * Convert hexadecimal characters to decimal value ++ */ ++SECUREC_INLINE int SecHexValueOfChar(SecInt ch) ++{ ++ /* Use isdigt Causing tool false alarms */ ++ return (int)((ch >= '0' && ch <= '9') ? ((unsigned char)ch - '0') : ++ ((((unsigned char)ch | (unsigned char)('a' - 'A')) - ('a')) + 10)); /* Adding 10 is to hex value */ ++} ++ ++/* ++ * Parse decimal character to integer for 32bit . ++ */ ++static void SecDecodeNumberDecimal(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ unsigned long decimalEdge = SECUREC_MAX_32BITS_VALUE_DIV_TEN; ++#ifdef SECUREC_ON_64BITS ++ if (SecIsLongBitEqual(SECUREC_LP64_BIT_WIDTH) != 0) { ++ decimalEdge = (unsigned long)SECUREC_MAX_64BITS_VALUE_DIV_TEN; ++ } ++#endif ++ if (spec->number > decimalEdge) { ++ spec->beyondMax = 1; ++ } ++#endif ++ spec->number = SECUREC_MUL_TEN(spec->number); ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (spec->number == SECUREC_MUL_TEN(decimalEdge)) { ++ /* This code is specially converted to unsigned long type for compatibility */ ++ SecUnsignedInt64 number64As = (unsigned long)SECUREC_MAX_64BITS_VALUE - spec->number; ++ if (number64As < (SecUnsignedInt64)(SecUnsignedInt)spec->ch - (SecUnsignedInt)SECUREC_CHAR('0')) { ++ spec->beyondMax = 1; ++ } ++ } ++#endif ++ spec->number += ((unsigned long)(SecUnsignedInt)spec->ch - (SecUnsignedInt)SECUREC_CHAR('0')); ++} ++ ++/* ++ * Parse Hex character to integer for 32bit . ++ */ ++static void SecDecodeNumberHex(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (SECUREC_LONG_HEX_BEYOND_MAX(spec->number)) { ++ spec->beyondMax = 1; ++ } ++#endif ++ spec->number = SECUREC_MUL_SIXTEEN(spec->number); ++ spec->number += (unsigned long)(unsigned int)SecHexValueOfChar(spec->ch); ++} ++ ++/* ++ * Parse Octal character to integer for 32bit . ++ */ ++static void SecDecodeNumberOctal(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (SECUREC_LONG_OCTAL_BEYOND_MAX(spec->number)) { ++ spec->beyondMax = 1; ++ } ++#endif ++ spec->number = SECUREC_MUL_EIGHT(spec->number); ++ spec->number += ((unsigned long)(SecUnsignedInt)spec->ch - (SecUnsignedInt)SECUREC_CHAR('0')); ++} ++ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++/* Compatible with integer negative values other than int */ ++SECUREC_INLINE void SecFinishNumberNegativeOther(SecScanSpec *spec) ++{ ++ if (SECUREC_CONVERT_IS_SIGNED(spec->oriConvChr)) { ++ if (spec->number > SECUREC_MIN_LONG_NEG_VALUE) { ++ spec->number = SECUREC_MIN_LONG_NEG_VALUE; ++ } else { ++ spec->number = (unsigned long)(0U - spec->number); /* Wrap with unsigned long numbers */ ++ } ++ if (spec->beyondMax != 0) { ++ if (spec->numberWidth < SECUREC_NUM_WIDTH_INT) { ++ spec->number = 0; ++ } ++ if (spec->numberWidth == SECUREC_NUM_WIDTH_LONG) { ++ spec->number = SECUREC_MIN_LONG_NEG_VALUE; ++ } ++ } ++ } else { /* For o, u, x, X, p */ ++ spec->number = (unsigned long)(0U - spec->number); /* Wrap with unsigned long numbers */ ++ if (spec->beyondMax != 0) { ++ spec->number = (unsigned long)SECUREC_MAX_64BITS_VALUE; ++ } ++ } ++} ++/* Compatible processing of integer negative numbers */ ++SECUREC_INLINE void SecFinishNumberNegativeInt(SecScanSpec *spec) ++{ ++ if (SECUREC_CONVERT_IS_SIGNED(spec->oriConvChr)) { ++#ifdef SECUREC_ON_64BITS ++ if (SecIsLongBitEqual(SECUREC_LP64_BIT_WIDTH) != 0) { ++ if ((spec->number > SECUREC_MIN_64BITS_NEG_VALUE)) { ++ spec->number = 0; ++ } else { ++ spec->number = (unsigned int)(0U - (unsigned int)spec->number); /* Wrap with unsigned int numbers */ ++ } ++ } ++#else ++ if (SecIsLongBitEqual(SECUREC_LP32_BIT_WIDTH) != 0) { ++ if ((spec->number > SECUREC_MIN_32BITS_NEG_VALUE)) { ++ spec->number = SECUREC_MIN_32BITS_NEG_VALUE; ++ } else { ++ spec->number = (unsigned int)(0U - (unsigned int)spec->number); /* Wrap with unsigned int numbers */ ++ } ++ } ++#endif ++ if (spec->beyondMax != 0) { ++#ifdef SECUREC_ON_64BITS ++ if (SecIsLongBitEqual(SECUREC_LP64_BIT_WIDTH) != 0) { ++ spec->number = 0; ++ } ++#else ++ if (SecIsLongBitEqual(SECUREC_LP32_BIT_WIDTH) != 0) { ++ spec->number = SECUREC_MIN_32BITS_NEG_VALUE; ++ } ++#endif ++ } ++ } else { /* For o, u, x, X ,p */ ++#ifdef SECUREC_ON_64BITS ++ if (spec->number > SECUREC_MAX_32BITS_VALUE_INC) { ++ spec->number = SECUREC_MAX_32BITS_VALUE; ++ } else { ++ spec->number = (unsigned int)(0U - (unsigned int)spec->number); /* Wrap with unsigned int numbers */ ++ } ++#else ++ spec->number = (unsigned int)(0U - (unsigned int)spec->number); /* Wrap with unsigned int numbers */ ++#endif ++ if (spec->beyondMax != 0) { ++ spec->number = (unsigned long)SECUREC_MAX_64BITS_VALUE; ++ } ++ } ++} ++ ++/* Compatible with integer positive values other than int */ ++SECUREC_INLINE void SecFinishNumberPositiveOther(SecScanSpec *spec) ++{ ++ if (SECUREC_CONVERT_IS_SIGNED(spec->oriConvChr)) { ++ if (spec->number > SECUREC_MAX_LONG_POS_VALUE) { ++ spec->number = SECUREC_MAX_LONG_POS_VALUE; ++ } ++ if ((spec->beyondMax != 0 && spec->numberWidth < SECUREC_NUM_WIDTH_INT)) { ++ spec->number = (unsigned long)SECUREC_MAX_64BITS_VALUE; ++ } ++ if (spec->beyondMax != 0 && spec->numberWidth == SECUREC_NUM_WIDTH_LONG) { ++ spec->number = SECUREC_MAX_LONG_POS_VALUE; ++ } ++ } else { ++ if (spec->beyondMax != 0) { ++ spec->number = (unsigned long)SECUREC_MAX_64BITS_VALUE; ++ } ++ } ++} ++ ++/* Compatible processing of integer positive numbers */ ++SECUREC_INLINE void SecFinishNumberPositiveInt(SecScanSpec *spec) ++{ ++ if (SECUREC_CONVERT_IS_SIGNED(spec->oriConvChr)) { ++#ifdef SECUREC_ON_64BITS ++ if (SecIsLongBitEqual(SECUREC_LP64_BIT_WIDTH) != 0) { ++ if (spec->number > SECUREC_MAX_64BITS_POS_VALUE) { ++ spec->number = (unsigned long)SECUREC_MAX_64BITS_VALUE; ++ } ++ } ++ if (spec->beyondMax != 0 && SecIsLongBitEqual(SECUREC_LP64_BIT_WIDTH) != 0) { ++ spec->number = (unsigned long)SECUREC_MAX_64BITS_VALUE; ++ } ++#else ++ if (SecIsLongBitEqual(SECUREC_LP32_BIT_WIDTH) != 0) { ++ if (spec->number > SECUREC_MAX_32BITS_POS_VALUE) { ++ spec->number = SECUREC_MAX_32BITS_POS_VALUE; ++ } ++ } ++ if (spec->beyondMax != 0 && SecIsLongBitEqual(SECUREC_LP32_BIT_WIDTH) != 0) { ++ spec->number = SECUREC_MAX_32BITS_POS_VALUE; ++ } ++#endif ++ } else { /* For o,u,x,X,p */ ++ if (spec->beyondMax != 0) { ++ spec->number = SECUREC_MAX_32BITS_VALUE; ++ } ++ } ++} ++ ++#endif ++ ++/* ++ * Parse decimal character to integer for 64bit . ++ */ ++static void SecDecodeNumber64Decimal(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (spec->number64 > SECUREC_MAX_64BITS_VALUE_DIV_TEN) { ++ spec->beyondMax = 1; ++ } ++#endif ++ spec->number64 = SECUREC_MUL_TEN(spec->number64); ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (spec->number64 == SECUREC_MAX_64BITS_VALUE_CUT_LAST_DIGIT) { ++ SecUnsignedInt64 number64As = (SecUnsignedInt64)SECUREC_MAX_64BITS_VALUE - spec->number64; ++ if (number64As < (SecUnsignedInt64)(SecUnsignedInt)spec->ch - (SecUnsignedInt)SECUREC_CHAR('0')) { ++ spec->beyondMax = 1; ++ } ++ } ++#endif ++ spec->number64 += ((SecUnsignedInt64)(SecUnsignedInt)spec->ch - (SecUnsignedInt)SECUREC_CHAR('0')); ++} ++ ++/* ++ * Parse Hex character to integer for 64bit . ++ */ ++static void SecDecodeNumber64Hex(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (SECUREC_QWORD_HEX_BEYOND_MAX(spec->number64)) { ++ spec->beyondMax = 1; ++ } ++#endif ++ spec->number64 = SECUREC_MUL_SIXTEEN(spec->number64); ++ spec->number64 += (SecUnsignedInt64)(unsigned int)SecHexValueOfChar(spec->ch); ++} ++ ++/* ++ * Parse Octal character to integer for 64bit . ++ */ ++static void SecDecodeNumber64Octal(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (SECUREC_QWORD_OCTAL_BEYOND_MAX(spec->number64)) { ++ spec->beyondMax = 1; ++ } ++#endif ++ spec->number64 = SECUREC_MUL_EIGHT(spec->number64); ++ spec->number64 += ((SecUnsignedInt64)(SecUnsignedInt)spec->ch - (SecUnsignedInt)SECUREC_CHAR('0')); ++} ++ ++#define SECUREC_DECODE_NUMBER_FUNC_NUM 2 ++ ++/* ++ * Parse 64-bit integer formatted input, return 0 when ch is a number. ++ */ ++SECUREC_INLINE int SecDecodeNumber(SecScanSpec *spec) ++{ ++ /* Function name cannot add address symbol, causing 546 alarm */ ++ static void (* const secDecodeNumberHex[SECUREC_DECODE_NUMBER_FUNC_NUM])(SecScanSpec *spec) = { ++ SecDecodeNumberHex, SecDecodeNumber64Hex ++ }; ++ static void (* const secDecodeNumberOctal[SECUREC_DECODE_NUMBER_FUNC_NUM])(SecScanSpec *spec) = { ++ SecDecodeNumberOctal, SecDecodeNumber64Octal ++ }; ++ static void (* const secDecodeNumberDecimal[SECUREC_DECODE_NUMBER_FUNC_NUM])(SecScanSpec *spec) = { ++ SecDecodeNumberDecimal, SecDecodeNumber64Decimal ++ }; ++ if (spec->convChr == 'x' || spec->convChr == 'p') { ++ if (SecIsXdigit(spec->ch) != 0) { ++ (*secDecodeNumberHex[spec->numberArgType])(spec); ++ } else { ++ return -1; ++ } ++ return 0; ++ } ++ if (SecIsDigit(spec->ch) == 0) { ++ return -1; ++ } ++ if (spec->convChr == 'o') { ++ if (spec->ch < SECUREC_CHAR('8')) { /* Octal maximum limit '8' */ ++ (*secDecodeNumberOctal[spec->numberArgType])(spec); ++ } else { ++ return -1; ++ } ++ } else { /* The convChr is 'd' */ ++ (*secDecodeNumberDecimal[spec->numberArgType])(spec); ++ } ++ return 0; ++} ++ ++/* ++ * Complete the final 32-bit integer formatted input ++ */ ++static void SecFinishNumber(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (spec->negative != 0) { ++ if (spec->numberWidth == SECUREC_NUM_WIDTH_INT) { ++ SecFinishNumberNegativeInt(spec); ++ } else { ++ SecFinishNumberNegativeOther(spec); ++ } ++ } else { ++ if (spec->numberWidth == SECUREC_NUM_WIDTH_INT) { ++ SecFinishNumberPositiveInt(spec); ++ } else { ++ SecFinishNumberPositiveOther(spec); ++ } ++ } ++#else ++ if (spec->negative != 0) { ++#if defined(__hpux) ++ if (spec->oriConvChr != 'p') { ++ spec->number = (unsigned long)(0U - spec->number); /* Wrap with unsigned long numbers */ ++ } ++#else ++ spec->number = (unsigned long)(0U - spec->number); /* Wrap with unsigned long numbers */ ++#endif ++ } ++#endif ++ return; ++} ++ ++/* ++ * Complete the final 64-bit integer formatted input ++ */ ++static void SecFinishNumber64(SecScanSpec *spec) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && !(defined(SECUREC_ON_UNIX))) ++ if (spec->negative != 0) { ++ if (SECUREC_CONVERT_IS_SIGNED(spec->oriConvChr)) { ++ if (spec->number64 > SECUREC_MIN_64BITS_NEG_VALUE) { ++ spec->number64 = SECUREC_MIN_64BITS_NEG_VALUE; ++ } else { ++ spec->number64 = (SecUnsignedInt64)(0U - spec->number64); /* Wrap with unsigned int64 numbers */ ++ } ++ if (spec->beyondMax != 0) { ++ spec->number64 = SECUREC_MIN_64BITS_NEG_VALUE; ++ } ++ } else { /* For o, u, x, X, p */ ++ spec->number64 = (SecUnsignedInt64)(0U - spec->number64); /* Wrap with unsigned int64 numbers */ ++ if (spec->beyondMax != 0) { ++ spec->number64 = SECUREC_MAX_64BITS_VALUE; ++ } ++ } ++ } else { ++ if (SECUREC_CONVERT_IS_SIGNED(spec->oriConvChr)) { ++ if (spec->number64 > SECUREC_MAX_64BITS_POS_VALUE) { ++ spec->number64 = SECUREC_MAX_64BITS_POS_VALUE; ++ } ++ if (spec->beyondMax != 0) { ++ spec->number64 = SECUREC_MAX_64BITS_POS_VALUE; ++ } ++ } else { ++ if (spec->beyondMax != 0) { ++ spec->number64 = SECUREC_MAX_64BITS_VALUE; ++ } ++ } ++ } ++#else ++ if (spec->negative != 0) { ++#if defined(__hpux) ++ if (spec->oriConvChr != 'p') { ++ spec->number64 = (SecUnsignedInt64)(0U - spec->number64); /* Wrap with unsigned int64 numbers */ ++ } ++#else ++ spec->number64 = (SecUnsignedInt64)(0U - spec->number64); /* Wrap with unsigned int64 numbers */ ++#endif ++ } ++#endif ++ return; ++} ++ ++#if SECUREC_ENABLE_SCANF_FILE ++ ++/* ++ * Adjust the pointer position of the file stream ++ */ ++SECUREC_INLINE void SecSeekStream(SecFileStream *stream) ++{ ++ if (stream->count == 0) { ++ if (feof(stream->pf) != 0) { ++ /* File pointer at the end of file, don't need to seek back */ ++ stream->base[0] = '\0'; ++ return; ++ } ++ } ++ /* Seek to original position, for file read, but nothing to input */ ++ if (fseek(stream->pf, stream->oriFilePos, SEEK_SET) != 0) { ++ /* Seek failed, ignore it */ ++ stream->oriFilePos = 0; ++ return; ++ } ++ ++ if (stream->fileRealRead > 0) { /* Do not seek without input data */ ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ size_t residue = stream->fileRealRead % SECUREC_BUFFERED_BLOK_SIZE; ++ size_t loops; ++ for (loops = 0; loops < (stream->fileRealRead / SECUREC_BUFFERED_BLOK_SIZE); ++loops) { ++ if (fread(stream->base, (size_t)SECUREC_BUFFERED_BLOK_SIZE, (size_t)1, stream->pf) != (size_t)1) { ++ break; ++ } ++ } ++ if (residue != 0) { ++ long curFilePos; ++ if (fread(stream->base, residue, (size_t)1, stream->pf) != (size_t)1) { ++ return; ++ } ++ curFilePos = ftell(stream->pf); ++ if (curFilePos < stream->oriFilePos || ++ (size_t)(unsigned long)(curFilePos - stream->oriFilePos) < stream->fileRealRead) { ++ /* Try to remedy the problem */ ++ long adjustNum = (long)(stream->fileRealRead - (size_t)(unsigned long)(curFilePos - stream->oriFilePos)); ++ (void)fseek(stream->pf, adjustNum, SEEK_CUR); ++ } ++ } ++#else ++ /* Seek from oriFilePos. Regardless of the integer sign problem, call scanf will not read very large data */ ++ if (fseek(stream->pf, (long)stream->fileRealRead, SEEK_CUR) != 0) { ++ /* Seek failed, ignore it */ ++ stream->oriFilePos = 0; ++ return; ++ } ++#endif ++ } ++ return; ++} ++ ++/* ++ * Adjust the pointer position of the file stream and free memory ++ */ ++SECUREC_INLINE void SecAdjustStream(SecFileStream *stream) ++{ ++ if ((stream->flag & SECUREC_FILE_STREAM_FLAG) != 0 && stream->base != NULL) { ++ SecSeekStream(stream); ++ SECUREC_FREE(stream->base); ++ stream->base = NULL; ++ } ++ return; ++} ++#endif ++ ++SECUREC_INLINE void SecSkipSpaceFormat(const SecUnsignedChar **format) ++{ ++ const SecUnsignedChar *fmt = *format; ++ while (SecIsSpace((SecInt)(int)(*fmt)) != 0) { ++ ++fmt; ++ } ++ *format = fmt; ++} ++ ++#if !defined(SECUREC_FOR_WCHAR) && defined(SECUREC_COMPATIBLE_VERSION) ++/* ++ * Handling multi-character characters ++ */ ++SECUREC_INLINE int SecDecodeLeadByte(SecScanSpec *spec, const SecUnsignedChar **format, SecFileStream *stream) ++{ ++#if SECUREC_HAVE_MBTOWC ++ const SecUnsignedChar *fmt = *format; ++ int ch1 = (int)spec->ch; ++ int ch2 = SecGetChar(stream, &(spec->charCount)); ++ spec->ch = (SecInt)ch2; ++ if (*fmt == SECUREC_CHAR('\0') || (int)(*fmt) != ch2) { ++ /* in console mode, ungetc twice may cause problem */ ++ SecUnGetChar(ch2, stream, &(spec->charCount)); ++ SecUnGetChar(ch1, stream, &(spec->charCount)); ++ return -1; ++ } ++ ++fmt; ++ if ((unsigned int)MB_CUR_MAX >= SECUREC_UTF8_BOM_HEADER_SIZE && ++ (((unsigned char)ch1 & SECUREC_UTF8_LEAD_1ST) == SECUREC_UTF8_LEAD_1ST) && ++ (((unsigned char)ch2 & SECUREC_UTF8_LEAD_2ND) == SECUREC_UTF8_LEAD_2ND)) { ++ /* This char is very likely to be a UTF-8 char */ ++ wchar_t tempWChar; ++ char temp[SECUREC_MULTI_BYTE_MAX_LEN]; ++ int ch3 = (int)SecGetChar(stream, &(spec->charCount)); ++ spec->ch = (SecInt)ch3; ++ if (*fmt == SECUREC_CHAR('\0') || (int)(*fmt) != ch3) { ++ SecUnGetChar(ch3, stream, &(spec->charCount)); ++ return -1; ++ } ++ temp[0] = (char)ch1; ++ temp[1] = (char)ch2; /* 1 index of second character */ ++ temp[2] = (char)ch3; /* 2 index of third character */ ++ temp[3] = '\0'; /* 3 of string terminator position */ ++ if (mbtowc(&tempWChar, temp, sizeof(temp)) > 0) { ++ /* Succeed */ ++ ++fmt; ++ --spec->charCount; ++ } else { ++ SecUnGetChar(ch3, stream, &(spec->charCount)); ++ } ++ } ++ --spec->charCount; /* Only count as one character read */ ++ *format = fmt; ++ return 0; ++#else ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ (void)format; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ return -1; ++#endif ++} ++ ++SECUREC_INLINE int SecFilterWcharInFormat(SecScanSpec *spec, const SecUnsignedChar **format, SecFileStream *stream) ++{ ++ if (SecIsLeadByte(spec->ch) != 0) { ++ if (SecDecodeLeadByte(spec, format, stream) != 0) { ++ return -1; ++ } ++ } ++ return 0; ++} ++#endif ++ ++/* ++ * Resolving sequence of characters from %[ format, format wile point to ']' ++ */ ++SECUREC_INLINE int SecSetupBracketTable(const SecUnsignedChar **format, SecBracketTable *bracketTable) ++{ ++ const SecUnsignedChar *fmt = *format; ++ SecUnsignedChar prevChar = 0; ++#if !(defined(SECUREC_COMPATIBLE_WIN_FORMAT)) ++ if (*fmt == SECUREC_CHAR('{')) { ++ return -1; ++ } ++#endif ++ /* For building "table" data */ ++ ++fmt; /* Skip [ */ ++ bracketTable->mask = 0; /* Set all bits to 0 */ ++ if (*fmt == SECUREC_CHAR('^')) { ++ ++fmt; ++ bracketTable->mask = (unsigned char)0xffU; /* Use 0xffU to set all bits to 1 */ ++ } ++ if (*fmt == SECUREC_CHAR(']')) { ++ prevChar = SECUREC_CHAR(']'); ++ ++fmt; ++ SecBracketSetBit(bracketTable->table, SECUREC_CHAR(']')); ++ } ++ while (*fmt != SECUREC_CHAR('\0') && *fmt != SECUREC_CHAR(']')) { ++ SecUnsignedChar expCh = *fmt; ++ ++fmt; ++ if (expCh != SECUREC_CHAR('-') || prevChar == 0 || *fmt == SECUREC_CHAR(']')) { ++ /* Normal character */ ++ prevChar = expCh; ++ SecBracketSetBit(bracketTable->table, expCh); ++ } else { ++ /* For %[a-z] */ ++ expCh = *fmt; /* Get end of range */ ++ ++fmt; ++ if (prevChar <= expCh) { /* %[a-z] %[a-a] */ ++ SecBracketSetBitRange(bracketTable->table, prevChar, expCh); ++ } else { ++ /* For %[z-a] */ ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ /* Swap start and end characters */ ++ SecBracketSetBitRange(bracketTable->table, expCh, prevChar); ++#else ++ SecBracketSetBit(bracketTable->table, SECUREC_CHAR('-')); ++ SecBracketSetBit(bracketTable->table, expCh); ++#endif ++ } ++ prevChar = 0; ++ } ++ } ++ *format = fmt; ++ return 0; ++} ++ ++#ifdef SECUREC_FOR_WCHAR ++SECUREC_INLINE int SecInputForWchar(SecScanSpec *spec) ++{ ++ void *endPtr = spec->argPtr; ++ if (spec->isWCharOrLong > 0) { ++ *(wchar_t UNALIGNED *)endPtr = (wchar_t)spec->ch; ++ endPtr = (wchar_t *)endPtr + 1; ++ --spec->arrayWidth; ++ } else { ++#if SECUREC_HAVE_WCTOMB ++ int temp; ++ char tmpBuf[SECUREC_MB_LEN + 1]; ++ SECUREC_MASK_MSVC_CRT_WARNING temp = wctomb(tmpBuf, (wchar_t)spec->ch); ++ SECUREC_END_MASK_MSVC_CRT_WARNING ++ if (temp <= 0 || (size_t)(unsigned int)temp > sizeof(tmpBuf)) { ++ /* If wctomb error, then ignore character */ ++ return 0; ++ } ++ if (((size_t)(unsigned int)temp) > spec->arrayWidth) { ++ return -1; ++ } ++ if (memcpy_s(endPtr, spec->arrayWidth, tmpBuf, (size_t)(unsigned int)temp) != EOK) { ++ return -1; ++ } ++ endPtr = (char *)endPtr + temp; ++ spec->arrayWidth -= (size_t)(unsigned int)temp; ++#else ++ return -1; ++#endif ++ } ++ spec->argPtr = endPtr; ++ return 0; ++} ++#endif ++ ++#ifndef SECUREC_FOR_WCHAR ++#if SECUREC_HAVE_WCHART ++SECUREC_INLINE wchar_t SecConvertInputCharToWchar(SecScanSpec *spec, SecFileStream *stream) ++{ ++ wchar_t tempWChar = L'?'; /* Set default char is ? */ ++#if SECUREC_HAVE_MBTOWC ++ char temp[SECUREC_MULTI_BYTE_MAX_LEN + 1]; ++ temp[0] = (char)spec->ch; ++ temp[1] = '\0'; ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ if (SecIsLeadByte(spec->ch) != 0) { ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ temp[1] = (char)spec->ch; ++ temp[2] = '\0'; /* 2 of string terminator position */ ++ } ++ if (mbtowc(&tempWChar, temp, sizeof(temp)) <= 0) { ++ /* No string termination error for tool */ ++ tempWChar = L'?'; ++ } ++#else ++ if (SecIsLeadByte(spec->ch) != 0) { ++ int convRes = 0; ++ int di = 1; ++ /* On Linux like system, the string is encoded in UTF-8 */ ++ while (convRes <= 0 && di < (int)MB_CUR_MAX && di < SECUREC_MULTI_BYTE_MAX_LEN) { ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ temp[di] = (char)spec->ch; ++ ++di; ++ temp[di] = '\0'; ++ convRes = mbtowc(&tempWChar, temp, sizeof(temp)); ++ } ++ if (convRes <= 0) { ++ tempWChar = L'?'; ++ } ++ } else { ++ if (mbtowc(&tempWChar, temp, sizeof(temp)) <= 0) { ++ tempWChar = L'?'; ++ } ++ } ++#endif ++#else ++ (void)spec; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ (void)stream; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++#endif /* SECUREC_HAVE_MBTOWC */ ++ ++ return tempWChar; ++} ++#endif /* SECUREC_HAVE_WCHART */ ++ ++SECUREC_INLINE int SecInputForChar(SecScanSpec *spec, SecFileStream *stream) ++{ ++ void *endPtr = spec->argPtr; ++ if (spec->isWCharOrLong > 0) { ++#if SECUREC_HAVE_WCHART ++ *(wchar_t UNALIGNED *)endPtr = SecConvertInputCharToWchar(spec, stream); ++ endPtr = (wchar_t *)endPtr + 1; ++ --spec->arrayWidth; ++#else ++ (void)stream; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ return -1; ++#endif ++ } else { ++ *(char *)endPtr = (char)spec->ch; ++ endPtr = (char *)endPtr + 1; ++ --spec->arrayWidth; ++ } ++ spec->argPtr = endPtr; ++ return 0; ++} ++#endif ++ ++/* ++ * Scan digital part of %d %i %o %u %x %p. ++ * Return 0 OK ++ */ ++SECUREC_INLINE int SecInputNumberDigital(SecFileStream *stream, SecScanSpec *spec) ++{ ++ static void (* const secFinishNumber[SECUREC_DECODE_NUMBER_FUNC_NUM])(SecScanSpec *spec) = { ++ SecFinishNumber, SecFinishNumber64 ++ }; ++ while (SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ /* Decode ch to number */ ++ if (SecDecodeNumber(spec) != 0) { ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ break; ++ } ++ SECUREC_FILED_WIDTH_DEC(spec); /* Must be behind un get char, otherwise the logic is incorrect */ ++ spec->numberState = SECUREC_NUMBER_STATE_STARTED; ++ } ++ /* Handling integer negative numbers and beyond max */ ++ (*secFinishNumber[spec->numberArgType])(spec); ++ if (spec->numberState == SECUREC_NUMBER_STATE_STARTED) { ++ return 0; ++ } ++ return -1; ++} ++ ++/* ++ * Scan %d %i %o %u %x %p. ++ * Return 0 OK ++ */ ++SECUREC_INLINE int SecInputNumber(SecFileStream *stream, SecScanSpec *spec) ++{ ++ /* Character already read */ ++ if (spec->ch == SECUREC_CHAR('+') || spec->ch == SECUREC_CHAR('-')) { ++ if (spec->ch == SECUREC_CHAR('-')) { ++ spec->negative = 1; ++#if SECUREC_IN_KERNEL ++ /* In kernel Refuse to enter negative number */ ++ if (SECUREC_CONVERT_IS_UNSIGNED(spec->oriConvChr)) { ++ return -1; ++ } ++#endif ++ } ++ SECUREC_FILED_WIDTH_DEC(spec); /* Do not need to check width here, must be greater than 0 */ ++ spec->ch = SecGetChar(stream, &(spec->charCount)); /* Eat + or - */ ++ spec->ch = SecGetChar(stream, &(spec->charCount)); /* Get next character, used for the '0' judgments */ ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); /* Not sure if it was actually read, so push back */ ++ } ++ ++ if (spec->oriConvChr == 'i') { ++ spec->convChr = 'd'; /* The i could be d, o, or x, use d as default */ ++ } ++ ++ if (spec->ch == SECUREC_CHAR('0') && (spec->oriConvChr == 'x' || spec->oriConvChr == 'i') && ++ SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ /* Input string begin with 0, may be 0x123 0X123 0123 0x 01 0yy 09 0 0ab 00 */ ++ SECUREC_FILED_WIDTH_DEC(spec); ++ spec->ch = SecGetChar(stream, &(spec->charCount)); /* ch is '0' */ ++ ++ /* Read only '0' due to width limitation */ ++ if (!SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ /* The number or number64 in spec has been set 0 */ ++ return 0; ++ } ++ ++ spec->ch = SecGetChar(stream, &(spec->charCount)); /* Get next char to check x or X, do not dec width */ ++ if ((SecChar)spec->ch == SECUREC_CHAR('x') || (SecChar)spec->ch == SECUREC_CHAR('X')) { ++ spec->convChr = 'x'; ++ SECUREC_FILED_WIDTH_DEC(spec); /* Make incorrect width for x or X */ ++ } else { ++ if (spec->oriConvChr == 'i') { ++ spec->convChr = 'o'; ++ } ++ /* For "0y" "08" "01" "0a" ... ,push the 'y' '8' '1' 'a' back */ ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ /* Since 0 has been read, it indicates that a valid character has been read */ ++ spec->numberState = SECUREC_NUMBER_STATE_STARTED; ++ } ++ } ++ return SecInputNumberDigital(stream, spec); ++} ++ ++/* ++ * Scan %c %s %[ ++ * Return 0 OK ++ */ ++SECUREC_INLINE int SecInputString(SecFileStream *stream, SecScanSpec *spec, ++ const SecBracketTable *bracketTable, int *doneCount) ++{ ++ void *startPtr = spec->argPtr; ++ int suppressed = 0; ++ int errNoMem = 0; ++ ++ while (SECUREC_FILED_WIDTH_ENOUGH(spec)) { ++ SECUREC_FILED_WIDTH_DEC(spec); ++ spec->ch = SecGetChar(stream, &(spec->charCount)); ++ /* ++ * The char condition or string condition and bracket condition. ++ * Only supports wide characters with a maximum length of two bytes ++ */ ++ if (spec->ch != SECUREC_EOF && (SecCanInputCharacter(spec->convChr) != 0 || ++ SecCanInputString(spec->convChr, spec->ch) != 0 || ++ SecCanInputForBracket(spec->convChr, spec->ch, bracketTable) != 0)) { ++ if (spec->suppress != 0) { ++ /* Used to identify processed data for %*, use argPtr to identify will cause 613, so use suppressed */ ++ suppressed = 1; ++ continue; ++ } ++ /* Now suppress is not set */ ++ if (spec->arrayWidth == 0) { ++ errNoMem = 1; /* We have exhausted the user's buffer */ ++ break; ++ } ++#ifdef SECUREC_FOR_WCHAR ++ errNoMem = SecInputForWchar(spec); ++#else ++ errNoMem = SecInputForChar(spec, stream); ++#endif ++ if (errNoMem != 0) { ++ break; ++ } ++ } else { ++ SecUnGetChar(spec->ch, stream, &(spec->charCount)); ++ break; ++ } ++ } ++ ++ if (errNoMem != 0) { ++ /* In case of error, blank out the input buffer */ ++ SecAddEndingZero(startPtr, spec); ++ return -1; ++ } ++ if ((spec->suppress != 0 && suppressed == 0) || ++ (spec->suppress == 0 && startPtr == spec->argPtr)) { ++ /* No input was scanned */ ++ return -1; ++ } ++ if (spec->convChr != 'c') { ++ /* Add null-terminate for strings */ ++ SecAddEndingZero(spec->argPtr, spec); ++ } ++ if (spec->suppress == 0) { ++ *doneCount = *doneCount + 1; ++ } ++ return 0; ++} ++ ++#ifdef SECUREC_FOR_WCHAR ++/* ++ * Alloce buffer for wchar version of %[. ++ * Return 0 OK ++ */ ++SECUREC_INLINE int SecAllocBracketTable(SecBracketTable *bracketTable) ++{ ++ if (bracketTable->table == NULL) { ++ /* Table should be freed after use */ ++ bracketTable->table = (unsigned char *)SECUREC_MALLOC(SECUREC_BRACKET_TABLE_SIZE); ++ if (bracketTable->table == NULL) { ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * Free buffer for wchar version of %[ ++ */ ++SECUREC_INLINE void SecFreeBracketTable(SecBracketTable *bracketTable) ++{ ++ if (bracketTable->table != NULL) { ++ SECUREC_FREE(bracketTable->table); ++ bracketTable->table = NULL; ++ } ++} ++#endif ++ ++#ifdef SECUREC_FOR_WCHAR ++/* ++ * Formatting input core functions for wchar version.Called by a function such as vswscanf_s ++ */ ++int SecInputSW(SecFileStream *stream, const wchar_t *cFormat, va_list argList) ++#else ++/* ++ * Formatting input core functions for char version.Called by a function such as vsscanf_s ++ */ ++int SecInputS(SecFileStream *stream, const char *cFormat, va_list argList) ++#endif ++{ ++ const SecUnsignedChar *format = (const SecUnsignedChar *)cFormat; ++ SecBracketTable bracketTable = SECUREC_INIT_BRACKET_TABLE; ++ SecScanSpec spec; ++ int doneCount = 0; ++ int formatError = 0; ++ int paraIsNull = 0; ++ int match = 0; /* When % is found , inc this value */ ++ int errRet = 0; ++#if SECUREC_ENABLE_SCANF_FLOAT ++ SecFloatSpec floatSpec; ++ SecInitFloatSpec(&floatSpec); ++#endif ++ spec.ch = 0; /* Need to initialize to 0 */ ++ spec.charCount = 0; /* Need to initialize to 0 */ ++ ++ /* Format must not NULL, use err < 1 to claer 845 */ ++ while (errRet < 1 && *format != SECUREC_CHAR('\0')) { ++ /* Skip space in format and space in input */ ++ if (SecIsSpace((SecInt)(int)(*format)) != 0) { ++ /* Read first no space char */ ++ spec.ch = SecSkipSpaceChar(stream, &(spec.charCount)); ++ /* Read the EOF cannot be returned directly here, because the case of " %n" needs to be handled */ ++ /* Put fist no space char backup. put EOF back is also OK, and to modify the character count */ ++ SecUnGetChar(spec.ch, stream, &(spec.charCount)); ++ SecSkipSpaceFormat(&format); ++ continue; ++ } ++ ++ if (*format != SECUREC_CHAR('%')) { ++ spec.ch = SecGetChar(stream, &(spec.charCount)); ++ if ((int)(*format) != (int)(spec.ch)) { ++ SecUnGetChar(spec.ch, stream, &(spec.charCount)); ++ break; ++ } ++ ++format; ++#if !defined(SECUREC_FOR_WCHAR) && defined(SECUREC_COMPATIBLE_VERSION) ++ if (SecFilterWcharInFormat(&spec, &format, stream) != 0) { ++ break; ++ } ++#endif ++ continue; ++ } ++ ++ /* Now *format is % */ ++ /* Set default value for each % */ ++ SecSetDefaultScanSpec(&spec); ++ if (SecDecodeScanFlag(&format, &spec) != 0) { ++ formatError = 1; ++ ++errRet; ++ continue; ++ } ++ if (!SECUREC_FILED_WIDTH_ENOUGH(&spec)) { ++ /* 0 width in format */ ++ ++errRet; ++ continue; ++ } ++ ++ /* Update wchar flag for %S %C */ ++ SecUpdateWcharFlagByType(*format, &spec); ++ ++ spec.convChr = SECUREC_TO_LOWERCASE(*format); ++ spec.oriConvChr = spec.convChr; /* convChr may be modified to handle integer logic */ ++ if (spec.convChr != 'n') { ++ if (spec.convChr != 'c' && spec.convChr != SECUREC_BRACE) { ++ spec.ch = SecSkipSpaceChar(stream, &(spec.charCount)); ++ } else { ++ spec.ch = SecGetChar(stream, &(spec.charCount)); ++ } ++ if (spec.ch == SECUREC_EOF) { ++ ++errRet; ++ continue; ++ } ++ } ++ ++ /* Now no 0 width in format and get one char from input */ ++ switch (spec.oriConvChr) { ++ case 'c': /* Also 'C' */ ++ if (spec.widthSet == 0) { ++ spec.widthSet = 1; ++ spec.width = 1; ++ } ++ /* fall-through */ /* FALLTHRU */ ++ case 's': /* Also 'S': */ ++ /* fall-through */ /* FALLTHRU */ ++ case SECUREC_BRACE: ++ /* Unset last char to stream */ ++ SecUnGetChar(spec.ch, stream, &(spec.charCount)); ++ /* Check dest buffer and size */ ++ if (spec.suppress == 0) { ++ spec.argPtr = (void *)va_arg(argList, void *); ++ if (spec.argPtr == NULL) { ++ paraIsNull = 1; ++ ++errRet; ++ continue; ++ } ++ /* Get the next argument, size of the array in characters */ ++ spec.arrayWidth = SECUREC_GET_ARRAYWIDTH(argList); ++ if (SECUREC_ARRAY_WIDTH_IS_WRONG(spec)) { ++ /* Do not clear buffer just go error */ ++ ++errRet; ++ continue; ++ } ++ /* One element is needed for '\0' for %s and %[ */ ++ if (spec.convChr != 'c') { ++ --spec.arrayWidth; ++ } ++ } else { ++ /* Set argPtr to NULL is necessary, in supress mode we don't use argPtr to store data */ ++ spec.argPtr = NULL; ++ } ++ ++ if (spec.convChr == SECUREC_BRACE) { ++ /* Malloc when first %[ is meet for wchar version */ ++#ifdef SECUREC_FOR_WCHAR ++ if (SecAllocBracketTable(&bracketTable) != 0) { ++ ++errRet; ++ continue; ++ } ++#endif ++ (void)SECUREC_MEMSET_FUNC_OPT(bracketTable.table, 0, (size_t)SECUREC_BRACKET_TABLE_SIZE); ++ if (SecSetupBracketTable(&format, &bracketTable) != 0) { ++ ++errRet; ++ continue; ++ } ++ ++ if (*format == SECUREC_CHAR('\0')) { ++ /* Default add string terminator */ ++ SecAddEndingZero(spec.argPtr, &spec); ++ ++errRet; ++ /* Truncated format */ ++ continue; ++ } ++ } ++ ++ /* Set completed. Now read string or character */ ++ if (SecInputString(stream, &spec, &bracketTable, &doneCount) != 0) { ++ ++errRet; ++ continue; ++ } ++ break; ++ case 'p': ++ /* Make %hp same as %p */ ++ spec.numberWidth = SECUREC_NUM_WIDTH_INT; ++#ifdef SECUREC_ON_64BITS ++ spec.numberArgType = 1; ++#endif ++ /* fall-through */ /* FALLTHRU */ ++ case 'o': /* fall-through */ /* FALLTHRU */ ++ case 'u': /* fall-through */ /* FALLTHRU */ ++ case 'd': /* fall-through */ /* FALLTHRU */ ++ case 'i': /* fall-through */ /* FALLTHRU */ ++ case 'x': ++ /* Unset last char to stream */ ++ SecUnGetChar(spec.ch, stream, &(spec.charCount)); ++ if (SecInputNumber(stream, &spec) != 0) { ++ ++errRet; ++ continue; ++ } ++ if (spec.suppress == 0) { ++ spec.argPtr = (void *)va_arg(argList, void *); ++ if (spec.argPtr == NULL) { ++ paraIsNull = 1; ++ ++errRet; ++ continue; ++ } ++ SecAssignNumber(&spec); ++ ++doneCount; ++ } ++ break; ++ case 'n': /* Char count */ ++ if (spec.suppress == 0) { ++ spec.argPtr = (void *)va_arg(argList, void *); ++ if (spec.argPtr == NULL) { ++ paraIsNull = 1; ++ ++errRet; ++ continue; ++ } ++ spec.number = (unsigned long)(unsigned int)(spec.charCount); ++ spec.numberArgType = 0; ++ SecAssignNumber(&spec); ++ } ++ break; ++ case 'e': /* fall-through */ /* FALLTHRU */ ++ case 'f': /* fall-through */ /* FALLTHRU */ ++ case 'g': /* Scan a float */ ++ /* Unset last char to stream */ ++ SecUnGetChar(spec.ch, stream, &(spec.charCount)); ++#if SECUREC_ENABLE_SCANF_FLOAT ++ if (SecInputFloat(stream, &spec, &floatSpec) != 0) { ++ ++errRet; ++ continue; ++ } ++ if (spec.suppress == 0) { ++ spec.argPtr = (void *)va_arg(argList, void *); ++ if (spec.argPtr == NULL) { ++ ++errRet; ++ paraIsNull = 1; ++ continue; ++ } ++ if (SecAssignFloat(&floatSpec, &spec) != 0) { ++ ++errRet; ++ continue; ++ } ++ ++doneCount; ++ } ++ break; ++#else /* SECUREC_ENABLE_SCANF_FLOAT */ ++ ++errRet; ++ continue; ++#endif ++ default: ++ if ((int)(*format) != (int)spec.ch) { ++ SecUnGetChar(spec.ch, stream, &(spec.charCount)); ++ formatError = 1; ++ ++errRet; ++ continue; ++ } else { ++ --match; /* Compensate for the self-increment of the following code */ ++ } ++ break; ++ } ++ ++match; ++ ++format; ++ } ++ ++#ifdef SECUREC_FOR_WCHAR ++ SecFreeBracketTable(&bracketTable); ++#endif ++ ++#if SECUREC_ENABLE_SCANF_FLOAT ++ SecFreeFloatSpec(&floatSpec, &doneCount); ++#endif ++ ++#if SECUREC_ENABLE_SCANF_FILE ++ SecAdjustStream(stream); ++#endif ++ ++ if (spec.ch == SECUREC_EOF) { ++ return ((doneCount != 0 || match != 0) ? doneCount : SECUREC_SCANF_EINVAL); ++ } ++ if (formatError != 0 || paraIsNull != 0) { ++ /* Invalid Input Format or parameter, but not meet EOF */ ++ return SECUREC_SCANF_ERROR_PARA; ++ } ++ return doneCount; ++} ++ ++#if SECUREC_ENABLE_SCANF_FILE ++/* ++ * Get char from stream use std function ++ */ ++SECUREC_INLINE SecInt SecGetCharFromStream(const SecFileStream *stream) ++{ ++ SecInt ch; ++ ch = SECUREC_GETC(stream->pf); ++ return ch; ++} ++ ++/* ++ * Try to read the BOM header, when meet a BOM head, discard it, then data is Aligned to base ++ */ ++SECUREC_INLINE void SecReadAndSkipBomHeader(SecFileStream *stream) ++{ ++ /* Use size_t type conversion to clean e747 */ ++ stream->count = fread(stream->base, (size_t)1, (size_t)SECUREC_BOM_HEADER_SIZE, stream->pf); ++ if (stream->count > SECUREC_BOM_HEADER_SIZE) { ++ stream->count = 0; ++ } ++ if (SECUREC_BEGIN_WITH_BOM(stream->base, stream->count)) { ++ /* It's BOM header, discard it */ ++ stream->count = 0; ++ } ++} ++ ++/* ++ * Get char from file stream or buffer ++ */ ++SECUREC_INLINE SecInt SecGetCharFromFile(SecFileStream *stream) ++{ ++ SecInt ch; ++ if (stream->count < sizeof(SecChar)) { ++ /* Load file to buffer */ ++ size_t len; ++ if (stream->base != NULL) { ++ /* Put the last unread data in the buffer head */ ++ for (len = 0; len < stream->count; ++len) { ++ stream->base[len] = stream->cur[len]; ++ } ++ } else { ++ stream->oriFilePos = ftell(stream->pf); /* Save original file read position */ ++ if (stream->oriFilePos == -1) { ++ /* It may be a pipe stream */ ++ stream->flag = SECUREC_PIPE_STREAM_FLAG; ++ return SecGetCharFromStream(stream); ++ } ++ /* Reserve the length of BOM head */ ++ stream->base = (char *)SECUREC_MALLOC(SECUREC_BUFFERED_BLOK_SIZE + ++ SECUREC_BOM_HEADER_SIZE + sizeof(SecChar)); /* To store '\0' and aligned to wide char */ ++ if (stream->base == NULL) { ++ return SECUREC_EOF; ++ } ++ /* First read file */ ++ if (stream->oriFilePos == 0) { ++ /* Make sure the data is aligned to base */ ++ SecReadAndSkipBomHeader(stream); ++ } ++ } ++ ++ /* Skip existing data and read data */ ++ len = fread(stream->base + stream->count, (size_t)1, (size_t)SECUREC_BUFFERED_BLOK_SIZE, stream->pf); ++ if (len > SECUREC_BUFFERED_BLOK_SIZE) { /* It won't happen, */ ++ len = 0; ++ } ++ stream->count += len; ++ stream->cur = stream->base; ++ stream->flag |= SECUREC_LOAD_FILE_TO_MEM_FLAG; ++ stream->base[stream->count] = '\0'; /* For tool Warning string null */ ++ } ++ ++ SECUREC_GET_CHAR(stream, &ch); ++ if (ch != SECUREC_EOF) { ++ stream->fileRealRead += sizeof(SecChar); ++ } ++ return ch; ++} ++#endif ++ ++/* ++ * Get char for wchar version ++ */ ++SECUREC_INLINE SecInt SecGetChar(SecFileStream *stream, int *counter) ++{ ++ *counter = *counter + 1; /* Always plus 1 */ ++ /* The main scenario is scanf str */ ++ if ((stream->flag & SECUREC_MEM_STR_FLAG) != 0) { ++ SecInt ch; ++ SECUREC_GET_CHAR(stream, &ch); ++ return ch; ++ } ++#if SECUREC_ENABLE_SCANF_FILE ++ if ((stream->flag & SECUREC_FILE_STREAM_FLAG) != 0) { ++ return SecGetCharFromFile(stream); ++ } ++ if ((stream->flag & SECUREC_PIPE_STREAM_FLAG) != 0) { ++ return SecGetCharFromStream(stream); ++ } ++#endif ++ return SECUREC_EOF; ++} ++ ++/* ++ * Unget Public realizatio char for wchar and char version ++ */ ++SECUREC_INLINE void SecUnGetCharImpl(SecInt ch, SecFileStream *stream) ++{ ++ if ((stream->flag & SECUREC_MEM_STR_FLAG) != 0) { ++ SECUREC_UN_GET_CHAR(stream); ++ return; ++ } ++#if SECUREC_ENABLE_SCANF_FILE ++ if ((stream->flag & SECUREC_LOAD_FILE_TO_MEM_FLAG) != 0) { ++ SECUREC_UN_GET_CHAR(stream); ++ if (stream->fileRealRead > 0) { ++ stream->fileRealRead -= sizeof(SecChar); ++ } ++ return; ++ } ++ if ((stream->flag & SECUREC_PIPE_STREAM_FLAG) != 0) { ++ (void)SECUREC_UN_GETC(ch, stream->pf); ++ return; ++ } ++#else ++ (void)ch; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++#endif ++} ++ ++/* ++ * Unget char for char version ++ */ ++SECUREC_INLINE void SecUnGetChar(SecInt ch, SecFileStream *stream, int *counter) ++{ ++ *counter = *counter - 1; /* Always mius 1 */ ++ if (ch != SECUREC_EOF) { ++ SecUnGetCharImpl(ch, stream); ++ } ++} ++ ++/* ++ * Skip space char by isspace ++ */ ++SECUREC_INLINE SecInt SecSkipSpaceChar(SecFileStream *stream, int *counter) ++{ ++ SecInt ch; ++ do { ++ ch = SecGetChar(stream, counter); ++ if (ch == SECUREC_EOF) { ++ break; ++ } ++ } while (SecIsSpace(ch) != 0); ++ return ch; ++} ++#endif /* INPUT_INL_5D13A042_DC3F_4ED9_A8D1_882811274C27 */ ++ +diff --git a/securec/src/memcpy_s.c b/securec/src/memcpy_s.c +new file mode 100644 +index 0000000..a7fd487 +--- /dev/null ++++ b/securec/src/memcpy_s.c +@@ -0,0 +1,555 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: memcpy_s function ++ * Create: 2014-02-25 ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Portability ++ * [reason] Use unsafe function to implement security function to maintain platform compatibility. ++ * And sufficient input validation is performed before calling ++ */ ++ ++#include "securecutil.h" ++ ++#if SECUREC_WITH_PERFORMANCE_ADDONS ++#ifndef SECUREC_MEMCOPY_THRESHOLD_SIZE ++#define SECUREC_MEMCOPY_THRESHOLD_SIZE 64UL ++#endif ++ ++#define SECUREC_SMALL_MEM_COPY(dest, src, count) do { \ ++ if (SECUREC_ADDR_ALIGNED_8(dest) && SECUREC_ADDR_ALIGNED_8(src)) { \ ++ /* Use struct assignment */ \ ++ switch (count) { \ ++ case 1: \ ++ *(unsigned char *)(dest) = *(const unsigned char *)(src); \ ++ break; \ ++ case 2: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 2); \ ++ break; \ ++ case 3: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 3); \ ++ break; \ ++ case 4: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 4); \ ++ break; \ ++ case 5: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 5); \ ++ break; \ ++ case 6: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 6); \ ++ break; \ ++ case 7: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 7); \ ++ break; \ ++ case 8: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 8); \ ++ break; \ ++ case 9: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 9); \ ++ break; \ ++ case 10: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 10); \ ++ break; \ ++ case 11: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 11); \ ++ break; \ ++ case 12: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 12); \ ++ break; \ ++ case 13: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 13); \ ++ break; \ ++ case 14: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 14); \ ++ break; \ ++ case 15: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 15); \ ++ break; \ ++ case 16: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 16); \ ++ break; \ ++ case 17: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 17); \ ++ break; \ ++ case 18: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 18); \ ++ break; \ ++ case 19: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 19); \ ++ break; \ ++ case 20: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 20); \ ++ break; \ ++ case 21: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 21); \ ++ break; \ ++ case 22: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 22); \ ++ break; \ ++ case 23: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 23); \ ++ break; \ ++ case 24: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 24); \ ++ break; \ ++ case 25: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 25); \ ++ break; \ ++ case 26: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 26); \ ++ break; \ ++ case 27: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 27); \ ++ break; \ ++ case 28: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 28); \ ++ break; \ ++ case 29: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 29); \ ++ break; \ ++ case 30: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 30); \ ++ break; \ ++ case 31: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 31); \ ++ break; \ ++ case 32: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 32); \ ++ break; \ ++ case 33: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 33); \ ++ break; \ ++ case 34: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 34); \ ++ break; \ ++ case 35: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 35); \ ++ break; \ ++ case 36: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 36); \ ++ break; \ ++ case 37: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 37); \ ++ break; \ ++ case 38: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 38); \ ++ break; \ ++ case 39: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 39); \ ++ break; \ ++ case 40: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 40); \ ++ break; \ ++ case 41: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 41); \ ++ break; \ ++ case 42: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 42); \ ++ break; \ ++ case 43: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 43); \ ++ break; \ ++ case 44: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 44); \ ++ break; \ ++ case 45: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 45); \ ++ break; \ ++ case 46: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 46); \ ++ break; \ ++ case 47: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 47); \ ++ break; \ ++ case 48: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 48); \ ++ break; \ ++ case 49: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 49); \ ++ break; \ ++ case 50: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 50); \ ++ break; \ ++ case 51: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 51); \ ++ break; \ ++ case 52: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 52); \ ++ break; \ ++ case 53: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 53); \ ++ break; \ ++ case 54: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 54); \ ++ break; \ ++ case 55: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 55); \ ++ break; \ ++ case 56: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 56); \ ++ break; \ ++ case 57: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 57); \ ++ break; \ ++ case 58: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 58); \ ++ break; \ ++ case 59: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 59); \ ++ break; \ ++ case 60: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 60); \ ++ break; \ ++ case 61: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 61); \ ++ break; \ ++ case 62: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 62); \ ++ break; \ ++ case 63: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 63); \ ++ break; \ ++ case 64: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((dest), (src), 64); \ ++ break; \ ++ default: \ ++ /* Do nothing */ \ ++ break; \ ++ } /* END switch */ \ ++ } else { \ ++ unsigned char *tmpDest_ = (unsigned char *)(dest); \ ++ const unsigned char *tmpSrc_ = (const unsigned char *)(src); \ ++ switch (count) { \ ++ case 64: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 63: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 62: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 61: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 60: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 59: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 58: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 57: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 56: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 55: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 54: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 53: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 52: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 51: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 50: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 49: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 48: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 47: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 46: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 45: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 44: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 43: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 42: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 41: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 40: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 39: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 38: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 37: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 36: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 35: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 34: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 33: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 32: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 31: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 30: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 29: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 28: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 27: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 26: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 25: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 24: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 23: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 22: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 21: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 20: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 19: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 18: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 17: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 16: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 15: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 14: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 13: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 12: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 11: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 10: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 9: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 8: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 7: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 6: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 5: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 4: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 3: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 2: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 1: \ ++ *(tmpDest_++) = *(tmpSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ default: \ ++ /* Do nothing */ \ ++ break; \ ++ } \ ++ } \ ++} SECUREC_WHILE_ZERO ++ ++/* ++ * Performance optimization ++ */ ++#define SECUREC_MEMCPY_OPT(dest, src, count) do { \ ++ if ((count) > SECUREC_MEMCOPY_THRESHOLD_SIZE) { \ ++ SECUREC_MEMCPY_WARP_OPT((dest), (src), (count)); \ ++ } else { \ ++ SECUREC_SMALL_MEM_COPY((dest), (src), (count)); \ ++ } \ ++} SECUREC_WHILE_ZERO ++#endif ++ ++/* ++ * Handling errors ++ */ ++SECUREC_INLINE errno_t SecMemcpyError(void *dest, size_t destMax, const void *src, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_MEM_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("memcpy_s"); ++ return ERANGE; ++ } ++ if (dest == NULL || src == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("memcpy_s"); ++ if (dest != NULL) { ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, 0, destMax); ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ if (count > destMax) { ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, 0, destMax); ++ SECUREC_ERROR_INVALID_RANGE("memcpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ if (SECUREC_MEMORY_IS_OVERLAP(dest, src, count)) { ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, 0, destMax); ++ SECUREC_ERROR_BUFFER_OVERLAP("memcpy_s"); ++ return EOVERLAP_AND_RESET; ++ } ++ /* Count is 0 or dest equal src also ret EOK */ ++ return EOK; ++} ++ ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ /* ++ * The fread API in windows will call memcpy_s and pass 0xffffffff to destMax. ++ * To avoid the failure of fread, we don't check desMax limit. ++ */ ++#define SECUREC_MEMCPY_PARAM_OK(dest, destMax, src, count) (SECUREC_LIKELY((count) <= (destMax) && \ ++ (dest) != NULL && (src) != NULL && \ ++ (count) > 0 && SECUREC_MEMORY_NO_OVERLAP((dest), (src), (count)))) ++#else ++#define SECUREC_MEMCPY_PARAM_OK(dest, destMax, src, count) (SECUREC_LIKELY((count) <= (destMax) && \ ++ (dest) != NULL && (src) != NULL && (destMax) <= SECUREC_MEM_MAX_LEN && \ ++ (count) > 0 && SECUREC_MEMORY_NO_OVERLAP((dest), (src), (count)))) ++#endif ++ ++/* ++ * ++ * The memcpy_s function copies n characters from the object pointed to by src into the object pointed to by dest ++ * ++ * ++ * dest Destination buffer. ++ * destMax Size of the destination buffer. ++ * src Buffer to copy from. ++ * count Number of characters to copy ++ * ++ * ++ * dest buffer is updated. ++ * ++ * ++ * EOK Success ++ * EINVAL dest is NULL and destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN ++ * EINVAL_AND_RESET dest != NULL and src is NULL and destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN ++ * ERANGE destMax > SECUREC_MEM_MAX_LEN or destMax is 0 ++ * ERANGE_AND_RESET count > destMax and destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN ++ * and dest != NULL and src != NULL ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and ++ * count <= destMax destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN and dest != NULL ++ * and src != NULL and dest != src ++ * ++ * if an error occurred, dest will be filled with 0. ++ * If the source and destination overlap, the behavior of memcpy_s is undefined. ++ * Use memmove_s to handle overlapping regions. ++ */ ++errno_t memcpy_s(void *dest, size_t destMax, const void *src, size_t count) ++{ ++ if (SECUREC_MEMCPY_PARAM_OK(dest, destMax, src, count)) { ++ SECUREC_MEMCPY_WARP_OPT(dest, src, count); ++ return EOK; ++ } ++ /* Meet some runtime violation, return error code */ ++ return SecMemcpyError(dest, destMax, src, count); ++} ++ ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(memcpy_s); ++#endif ++ ++#if SECUREC_WITH_PERFORMANCE_ADDONS ++/* ++ * Performance optimization ++ */ ++errno_t memcpy_sOptAsm(void *dest, size_t destMax, const void *src, size_t count) ++{ ++ if (SECUREC_MEMCPY_PARAM_OK(dest, destMax, src, count)) { ++ SECUREC_MEMCPY_OPT(dest, src, count); ++ return EOK; ++ } ++ /* Meet some runtime violation, return error code */ ++ return SecMemcpyError(dest, destMax, src, count); ++} ++ ++/* Trim judgement on "destMax <= SECUREC_MEM_MAX_LEN" */ ++errno_t memcpy_sOptTc(void *dest, size_t destMax, const void *src, size_t count) ++{ ++ if (SECUREC_LIKELY(count <= destMax && dest != NULL && src != NULL && \ ++ count > 0 && SECUREC_MEMORY_NO_OVERLAP((dest), (src), (count)))) { ++ SECUREC_MEMCPY_OPT(dest, src, count); ++ return EOK; ++ } ++ /* Meet some runtime violation, return error code */ ++ return SecMemcpyError(dest, destMax, src, count); ++} ++#endif ++ +diff --git a/securec/src/memmove_s.c b/securec/src/memmove_s.c +new file mode 100644 +index 0000000..f231f05 +--- /dev/null ++++ b/securec/src/memmove_s.c +@@ -0,0 +1,123 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: memmove_s function ++ * Create: 2014-02-25 ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Portability ++ * [reason] Use unsafe function to implement security function to maintain platform compatibility. ++ * And sufficient input validation is performed before calling ++ */ ++ ++#include "securecutil.h" ++ ++#ifdef SECUREC_NOT_CALL_LIBC_CORE_API ++/* ++ * Implementing memory data movement ++ */ ++SECUREC_INLINE void SecUtilMemmove(void *dst, const void *src, size_t count) ++{ ++ unsigned char *pDest = (unsigned char *)dst; ++ const unsigned char *pSrc = (const unsigned char *)src; ++ size_t maxCount = count; ++ ++ if (dst <= src || pDest >= (pSrc + maxCount)) { ++ /* ++ * Non-Overlapping Buffers ++ * Copy from lower addresses to higher addresses ++ */ ++ while (maxCount > 0) { ++ --maxCount; ++ *pDest = *pSrc; ++ ++pDest; ++ ++pSrc; ++ } ++ } else { ++ /* ++ * Overlapping Buffers ++ * Copy from higher addresses to lower addresses ++ */ ++ pDest = pDest + maxCount - 1; ++ pSrc = pSrc + maxCount - 1; ++ while (maxCount > 0) { ++ --maxCount; ++ *pDest = *pSrc; ++ --pDest; ++ --pSrc; ++ } ++ } ++} ++#endif ++ ++/* ++ * ++ * The memmove_s function copies count bytes of characters from src to dest. ++ * This function can be assigned correctly when memory overlaps. ++ * ++ * dest Destination object. ++ * destMax Size of the destination buffer. ++ * src Source object. ++ * count Number of characters to copy. ++ * ++ * ++ * dest buffer is updated. ++ * ++ * ++ * EOK Success ++ * EINVAL dest is NULL and destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN ++ * EINVAL_AND_RESET dest != NULL and src is NULL and destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN ++ * ERANGE destMax > SECUREC_MEM_MAX_LEN or destMax is 0 ++ * ERANGE_AND_RESET count > destMax and dest != NULL and src != NULL and destMax != 0 ++ * and destMax <= SECUREC_MEM_MAX_LEN ++ * ++ * If an error occurred, dest will be filled with 0 when dest and destMax valid. ++ * If some regions of the source area and the destination overlap, memmove_s ++ * ensures that the original source bytes in the overlapping region are copied ++ * before being overwritten. ++ */ ++errno_t memmove_s(void *dest, size_t destMax, const void *src, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_MEM_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("memmove_s"); ++ return ERANGE; ++ } ++ if (dest == NULL || src == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("memmove_s"); ++ if (dest != NULL) { ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, 0, destMax); ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ if (count > destMax) { ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, 0, destMax); ++ SECUREC_ERROR_INVALID_RANGE("memmove_s"); ++ return ERANGE_AND_RESET; ++ } ++ if (dest == src) { ++ return EOK; ++ } ++ ++ if (count > 0) { ++#ifdef SECUREC_NOT_CALL_LIBC_CORE_API ++ SecUtilMemmove(dest, src, count); ++#else ++ /* Use underlying memmove for performance consideration */ ++ (void)memmove(dest, src, count); ++#endif ++ } ++ return EOK; ++} ++ ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(memmove_s); ++#endif ++ +diff --git a/securec/src/memset_s.c b/securec/src/memset_s.c +new file mode 100644 +index 0000000..d9a657f +--- /dev/null ++++ b/securec/src/memset_s.c +@@ -0,0 +1,510 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: memset_s function ++ * Create: 2014-02-25 ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Portability ++ * [reason] Use unsafe function to implement security function to maintain platform compatibility. ++ * And sufficient input validation is performed before calling ++ */ ++ ++#include "securecutil.h" ++ ++#define SECUREC_MEMSET_PARAM_OK(dest, destMax, count) (SECUREC_LIKELY((destMax) <= SECUREC_MEM_MAX_LEN && \ ++ (dest) != NULL && (count) <= (destMax))) ++ ++#if SECUREC_WITH_PERFORMANCE_ADDONS ++ ++/* Use union to clear strict-aliasing warning */ ++typedef union { ++ SecStrBuf32 buf32; ++ SecStrBuf31 buf31; ++ SecStrBuf30 buf30; ++ SecStrBuf29 buf29; ++ SecStrBuf28 buf28; ++ SecStrBuf27 buf27; ++ SecStrBuf26 buf26; ++ SecStrBuf25 buf25; ++ SecStrBuf24 buf24; ++ SecStrBuf23 buf23; ++ SecStrBuf22 buf22; ++ SecStrBuf21 buf21; ++ SecStrBuf20 buf20; ++ SecStrBuf19 buf19; ++ SecStrBuf18 buf18; ++ SecStrBuf17 buf17; ++ SecStrBuf16 buf16; ++ SecStrBuf15 buf15; ++ SecStrBuf14 buf14; ++ SecStrBuf13 buf13; ++ SecStrBuf12 buf12; ++ SecStrBuf11 buf11; ++ SecStrBuf10 buf10; ++ SecStrBuf9 buf9; ++ SecStrBuf8 buf8; ++ SecStrBuf7 buf7; ++ SecStrBuf6 buf6; ++ SecStrBuf5 buf5; ++ SecStrBuf4 buf4; ++ SecStrBuf3 buf3; ++ SecStrBuf2 buf2; ++} SecStrBuf32Union; ++/* C standard initializes the first member of the consortium. */ ++static const SecStrBuf32 g_allZero = {{ ++ 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, ++ 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, ++ 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, ++ 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U ++}}; ++static const SecStrBuf32 g_allFF = {{ ++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ++}}; ++ ++/* Clear conversion warning strict aliasing" */ ++SECUREC_INLINE const SecStrBuf32Union *SecStrictAliasingCast(const SecStrBuf32 *buf) ++{ ++ return (const SecStrBuf32Union *)buf; ++} ++ ++#ifndef SECUREC_MEMSET_THRESHOLD_SIZE ++#define SECUREC_MEMSET_THRESHOLD_SIZE 32UL ++#endif ++ ++#define SECUREC_UNALIGNED_SET(dest, c, count) do { \ ++ unsigned char *pDest_ = (unsigned char *)(dest); \ ++ switch (count) { \ ++ case 32: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 31: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 30: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 29: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 28: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 27: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 26: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 25: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 24: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 23: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 22: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 21: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 20: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 19: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 18: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 17: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 16: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 15: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 14: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 13: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 12: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 11: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 10: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 9: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 8: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 7: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 6: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 5: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 4: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 3: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 2: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 1: \ ++ *(pDest_++) = (unsigned char)(c); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ default: \ ++ /* Do nothing */ \ ++ break; \ ++ } \ ++} SECUREC_WHILE_ZERO ++ ++#define SECUREC_SET_VALUE_BY_STRUCT(dest, dataName, n) do { \ ++ *(SecStrBuf##n *)(dest) = *(const SecStrBuf##n *)(&((SecStrictAliasingCast(&(dataName)))->buf##n)); \ ++} SECUREC_WHILE_ZERO ++ ++#define SECUREC_ALIGNED_SET_OPT_ZERO_FF(dest, c, count) do { \ ++ switch (c) { \ ++ case 0: \ ++ switch (count) { \ ++ case 1: \ ++ *(unsigned char *)(dest) = (unsigned char)0; \ ++ break; \ ++ case 2: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 2); \ ++ break; \ ++ case 3: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 3); \ ++ break; \ ++ case 4: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 4); \ ++ break; \ ++ case 5: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 5); \ ++ break; \ ++ case 6: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 6); \ ++ break; \ ++ case 7: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 7); \ ++ break; \ ++ case 8: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 8); \ ++ break; \ ++ case 9: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 9); \ ++ break; \ ++ case 10: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 10); \ ++ break; \ ++ case 11: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 11); \ ++ break; \ ++ case 12: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 12); \ ++ break; \ ++ case 13: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 13); \ ++ break; \ ++ case 14: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 14); \ ++ break; \ ++ case 15: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 15); \ ++ break; \ ++ case 16: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 16); \ ++ break; \ ++ case 17: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 17); \ ++ break; \ ++ case 18: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 18); \ ++ break; \ ++ case 19: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 19); \ ++ break; \ ++ case 20: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 20); \ ++ break; \ ++ case 21: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 21); \ ++ break; \ ++ case 22: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 22); \ ++ break; \ ++ case 23: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 23); \ ++ break; \ ++ case 24: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 24); \ ++ break; \ ++ case 25: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 25); \ ++ break; \ ++ case 26: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 26); \ ++ break; \ ++ case 27: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 27); \ ++ break; \ ++ case 28: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 28); \ ++ break; \ ++ case 29: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 29); \ ++ break; \ ++ case 30: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 30); \ ++ break; \ ++ case 31: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 31); \ ++ break; \ ++ case 32: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allZero, 32); \ ++ break; \ ++ default: \ ++ /* Do nothing */ \ ++ break; \ ++ } \ ++ break; \ ++ case 0xFF: \ ++ switch (count) { \ ++ case 1: \ ++ *(unsigned char *)(dest) = (unsigned char)0xffU; \ ++ break; \ ++ case 2: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 2); \ ++ break; \ ++ case 3: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 3); \ ++ break; \ ++ case 4: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 4); \ ++ break; \ ++ case 5: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 5); \ ++ break; \ ++ case 6: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 6); \ ++ break; \ ++ case 7: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 7); \ ++ break; \ ++ case 8: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 8); \ ++ break; \ ++ case 9: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 9); \ ++ break; \ ++ case 10: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 10); \ ++ break; \ ++ case 11: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 11); \ ++ break; \ ++ case 12: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 12); \ ++ break; \ ++ case 13: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 13); \ ++ break; \ ++ case 14: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 14); \ ++ break; \ ++ case 15: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 15); \ ++ break; \ ++ case 16: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 16); \ ++ break; \ ++ case 17: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 17); \ ++ break; \ ++ case 18: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 18); \ ++ break; \ ++ case 19: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 19); \ ++ break; \ ++ case 20: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 20); \ ++ break; \ ++ case 21: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 21); \ ++ break; \ ++ case 22: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 22); \ ++ break; \ ++ case 23: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 23); \ ++ break; \ ++ case 24: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 24); \ ++ break; \ ++ case 25: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 25); \ ++ break; \ ++ case 26: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 26); \ ++ break; \ ++ case 27: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 27); \ ++ break; \ ++ case 28: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 28); \ ++ break; \ ++ case 29: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 29); \ ++ break; \ ++ case 30: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 30); \ ++ break; \ ++ case 31: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 31); \ ++ break; \ ++ case 32: \ ++ SECUREC_SET_VALUE_BY_STRUCT((dest), g_allFF, 32); \ ++ break; \ ++ default: \ ++ /* Do nothing */ \ ++ break; \ ++ } \ ++ break; \ ++ default: \ ++ SECUREC_UNALIGNED_SET((dest), (c), (count)); \ ++ break; \ ++ } /* END switch */ \ ++} SECUREC_WHILE_ZERO ++ ++#define SECUREC_SMALL_MEM_SET(dest, c, count) do { \ ++ if (SECUREC_ADDR_ALIGNED_8((dest))) { \ ++ SECUREC_ALIGNED_SET_OPT_ZERO_FF((dest), (c), (count)); \ ++ } else { \ ++ SECUREC_UNALIGNED_SET((dest), (c), (count)); \ ++ } \ ++} SECUREC_WHILE_ZERO ++ ++/* ++ * Performance optimization ++ */ ++#define SECUREC_MEMSET_OPT(dest, c, count) do { \ ++ if ((count) > SECUREC_MEMSET_THRESHOLD_SIZE) { \ ++ SECUREC_MEMSET_PREVENT_DSE((dest), (c), (count)); \ ++ } else { \ ++ SECUREC_SMALL_MEM_SET((dest), (c), (count)); \ ++ } \ ++} SECUREC_WHILE_ZERO ++#endif ++ ++/* ++ * Handling errors ++ */ ++SECUREC_INLINE errno_t SecMemsetError(void *dest, size_t destMax, int c) ++{ ++ /* Check destMax is 0 compatible with _sp macro */ ++ if (destMax == 0 || destMax > SECUREC_MEM_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("memset_s"); ++ return ERANGE; ++ } ++ if (dest == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("memset_s"); ++ return EINVAL; ++ } ++ SECUREC_MEMSET_PREVENT_DSE(dest, c, destMax); /* Set entire buffer to value c */ ++ SECUREC_ERROR_INVALID_RANGE("memset_s"); ++ return ERANGE_AND_RESET; ++} ++ ++/* ++ * ++ * The memset_s function copies the value of c (converted to an unsigned char) ++ * into each of the first count characters of the object pointed to by dest. ++ * ++ * ++ * dest Pointer to destination. ++ * destMax The size of the buffer. ++ * c Character to set. ++ * count Number of characters. ++ * ++ * ++ * dest buffer is updated. ++ * ++ * ++ * EOK Success ++ * EINVAL dest == NULL and destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN ++ * ERANGE destMax > SECUREC_MEM_MAX_LEN or (destMax is 0 and count > destMax) ++ * ERANGE_AND_RESET count > destMax and destMax != 0 and destMax <= SECUREC_MEM_MAX_LEN and dest != NULL ++ * ++ * if return ERANGE_AND_RESET then fill dest to c ,fill length is destMax ++ */ ++errno_t memset_s(void *dest, size_t destMax, int c, size_t count) ++{ ++ if (SECUREC_MEMSET_PARAM_OK(dest, destMax, count)) { ++ SECUREC_MEMSET_PREVENT_DSE(dest, c, count); ++ return EOK; ++ } ++ /* Meet some runtime violation, return error code */ ++ return SecMemsetError(dest, destMax, c); ++} ++ ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(memset_s); ++#endif ++ ++#if SECUREC_WITH_PERFORMANCE_ADDONS ++/* ++ * Performance optimization ++ */ ++errno_t memset_sOptAsm(void *dest, size_t destMax, int c, size_t count) ++{ ++ if (SECUREC_MEMSET_PARAM_OK(dest, destMax, count)) { ++ SECUREC_MEMSET_OPT(dest, c, count); ++ return EOK; ++ } ++ /* Meet some runtime violation, return error code */ ++ return SecMemsetError(dest, destMax, c); ++} ++ ++/* ++ * Performance optimization, trim judgement on "destMax <= SECUREC_MEM_MAX_LEN" ++ */ ++errno_t memset_sOptTc(void *dest, size_t destMax, int c, size_t count) ++{ ++ if (SECUREC_LIKELY(count <= destMax && dest != NULL)) { ++ SECUREC_MEMSET_OPT(dest, c, count); ++ return EOK; ++ } ++ /* Meet some runtime violation, return error code */ ++ return SecMemsetError(dest, destMax, c); ++} ++#endif ++ +diff --git a/securec/src/output.inl b/securec/src/output.inl +new file mode 100644 +index 0000000..d2168ab +--- /dev/null ++++ b/securec/src/output.inl +@@ -0,0 +1,1722 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: Used by secureprintoutput_a.c and secureprintoutput_w.c to include. ++ * This file provides a template function for ANSI and UNICODE compiling ++ * by different type definition. The functions of SecOutputS or ++ * SecOutputSW provides internal implementation for printf family API, such as sprintf, swprintf_s. ++ * Create: 2014-02-25 ++ * Notes: see www.cplusplus.com/reference/cstdio/printf/ ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Portability ++ * [reason] Use unsafe function to implement security function to maintain platform compatibility. ++ * And sufficient input validation is performed before calling ++ */ ++#ifndef OUTPUT_INL_2B263E9C_43D8_44BB_B17A_6D2033DECEE5 ++#define OUTPUT_INL_2B263E9C_43D8_44BB_B17A_6D2033DECEE5 ++ ++#include ++ ++#ifndef SECUREC_ENABLE_SPRINTF_LONG_DOUBLE ++/* Some compilers do not support long double */ ++#define SECUREC_ENABLE_SPRINTF_LONG_DOUBLE 1 ++#endif ++ ++#define SECUREC_NULL_STRING_SIZE 8 ++#define SECUREC_STATE_TABLE_SIZE 337 ++ ++#if defined(SECUREC_VXWORKS_VERSION_5_4) && !defined(SECUREC_ON_64BITS) ++#define SECUREC_DIV_QUOTIENT_OCTAL(val64) ((val64) >> 3ULL) ++#define SECUREC_DIV_RESIDUE_OCTAL(val64) ((val64) & 7ULL) ++ ++#define SECUREC_DIV_QUOTIENT_HEX(val64) ((val64) >> 4ULL) ++#define SECUREC_DIV_RESIDUE_HEX(val64) ((val64) & 0xfULL) ++#endif ++ ++#define SECUREC_RADIX_OCTAL 8U ++#define SECUREC_RADIX_DECIMAL 10U ++#define SECUREC_RADIX_HEX 16U ++#define SECUREC_PREFIX_LEN 2 ++/* Size include '+' and '\0' */ ++#define SECUREC_FLOAT_BUF_EXT 2 ++ ++/* Sign extend or Zero-extend */ ++#define SECUREC_GET_LONG_FROM_ARG(attr) ((((attr).flags & SECUREC_FLAG_SIGNED) != 0) ? \ ++ (SecInt64)(long)va_arg(argList, long) : \ ++ (SecInt64)(unsigned long)va_arg(argList, long)) ++ ++/* Sign extend or Zero-extend */ ++#define SECUREC_GET_CHAR_FROM_ARG(attr) ((((attr).flags & SECUREC_FLAG_SIGNED) != 0) ? \ ++ SecUpdateNegativeChar(&(attr), ((char)va_arg(argList, int))) : \ ++ (SecInt64)(unsigned char)va_arg(argList, int)) ++ ++/* Sign extend or Zero-extend */ ++#define SECUREC_GET_SHORT_FROM_ARG(attr) ((((attr).flags & SECUREC_FLAG_SIGNED) != 0) ? \ ++ (SecInt64)(short)va_arg(argList, int) : \ ++ (SecInt64)(unsigned short)va_arg(argList, int)) ++ ++/* Sign extend or Zero-extend */ ++#define SECUREC_GET_INT_FROM_ARG(attr) ((((attr).flags & SECUREC_FLAG_SIGNED) != 0) ? \ ++ (SecInt64)(int)va_arg(argList, int) : \ ++ (SecInt64)(unsigned int)va_arg(argList, int)) ++ ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++/* Sign extend or Zero-extend. No suitable macros were found to handle the branch */ ++#define SECUREC_GET_SIZE_FROM_ARG(attr) ((((attr).flags & SECUREC_FLAG_SIGNED) != 0) ? \ ++ ((SecIsSameSize(sizeof(size_t), sizeof(long)) != 0) ? (SecInt64)(long)va_arg(argList, long) : \ ++ ((SecIsSameSize(sizeof(size_t), sizeof(long long)) != 0) ? (SecInt64)(long long)va_arg(argList, long long) : \ ++ (SecInt64)(int)va_arg(argList, int))) : \ ++ (SecInt64)(size_t)va_arg(argList, size_t)) ++#endif ++ ++/* Format output buffer pointer and available size */ ++typedef struct { ++ int count; ++ SecChar *cur; ++} SecPrintfStream; ++ ++typedef union { ++ /* Integer formatting refers to the end of the buffer, plus 1 to prevent tool alarms */ ++ char str[SECUREC_BUFFER_SIZE + 1]; ++#if SECUREC_HAVE_WCHART ++ wchar_t wStr[SECUREC_WCHAR_BUFFER_SIZE]; /* Just for %lc */ ++#endif ++} SecBuffer; ++ ++typedef union { ++ char *str; /* Not a null terminated string */ ++#if SECUREC_HAVE_WCHART ++ wchar_t *wStr; ++#endif ++} SecFormatBuf; ++ ++typedef struct { ++ const char *digits; /* Point to the hexadecimal subset */ ++ SecFormatBuf text; /* Point to formatted string */ ++ int textLen; /* Length of the text */ ++ int textIsWide; /* Flag for text is wide chars ; 0 is not wide char */ ++ unsigned int radix; /* Use for output number , default set to 10 */ ++ unsigned int flags; ++ int fldWidth; ++ int precision; ++ int dynWidth; /* %* 1 width from variable parameter ;0 not */ ++ int dynPrecision; /* %.* 1 precision from variable parameter ;0 not */ ++ int padding; /* Padding len */ ++ int prefixLen; /* Length of prefix, 0 or 1 or 2 */ ++ SecChar prefix[SECUREC_PREFIX_LEN]; /* Prefix is 0 or 0x */ ++ SecBuffer buffer; ++} SecFormatAttr; ++ ++#if SECUREC_ENABLE_SPRINTF_FLOAT ++#ifdef SECUREC_STACK_SIZE_LESS_THAN_1K ++#define SECUREC_FMT_STR_LEN 8 ++#else ++#define SECUREC_FMT_STR_LEN 16 ++#endif ++typedef struct { ++ char buffer[SECUREC_FMT_STR_LEN]; ++ char *fmtStr; /* Initialization must point to buffer */ ++ char *allocatedFmtStr; /* Initialization must be NULL to store allocated point */ ++ char *floatBuffer; /* Use heap memory if the SecFormatAttr.buffer is not enough */ ++ int bufferSize; /* The size of floatBuffer */ ++} SecFloatAdapt; ++#endif ++ ++/* Use 20 to Align the data */ ++#define SECUREC_DIGITS_BUF_SIZE 20 ++/* The serial number of 'x' or 'X' is 16 */ ++#define SECUREC_NUMBER_OF_X 16 ++/* Some systems can not use pointers to point to string literals, but can use string arrays. */ ++/* For example, when handling code under uboot, there is a problem with the pointer */ ++static const char g_itoaUpperDigits[SECUREC_DIGITS_BUF_SIZE] = "0123456789ABCDEFX"; ++static const char g_itoaLowerDigits[SECUREC_DIGITS_BUF_SIZE] = "0123456789abcdefx"; ++ ++#if SECUREC_ENABLE_SPRINTF_FLOAT ++/* Call system sprintf to format float value */ ++SECUREC_INLINE int SecFormatFloat(char *strDest, const char *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ SECUREC_MASK_VSPRINTF_WARNING ++ ret = vsprintf(strDest, format, argList); ++ SECUREC_END_MASK_VSPRINTF_WARNING ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++ ++#if defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && SECUREC_ENABLE_SPRINTF_LONG_DOUBLE ++/* Out put long double value to dest */ ++SECUREC_INLINE void SecFormatLongDouble(SecFormatAttr *attr, const SecFloatAdapt *floatAdapt, long double ldValue) ++{ ++ int fldWidth = (((attr->flags & SECUREC_FLAG_LEFT) != 0) ? (-attr->fldWidth) : attr->fldWidth); ++ if (attr->dynWidth != 0 && attr->dynPrecision != 0) { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, fldWidth, attr->precision, ldValue); ++ } else if (attr->dynWidth != 0) { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, fldWidth, ldValue); ++ } else if (attr->dynPrecision != 0) { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, attr->precision, ldValue); ++ } else { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, ldValue); ++ } ++ if (attr->textLen < 0 || attr->textLen >= floatAdapt->bufferSize) { ++ attr->textLen = 0; ++ } ++} ++#endif ++ ++/* Out put double value to dest */ ++SECUREC_INLINE void SecFormatDouble(SecFormatAttr *attr, const SecFloatAdapt *floatAdapt, double dValue) ++{ ++ int fldWidth = (((attr->flags & SECUREC_FLAG_LEFT) != 0) ? (-attr->fldWidth) : attr->fldWidth); ++ if (attr->dynWidth != 0 && attr->dynPrecision != 0) { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, fldWidth, attr->precision, dValue); ++ } else if (attr->dynWidth != 0) { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, fldWidth, dValue); ++ } else if (attr->dynPrecision != 0) { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, attr->precision, dValue); ++ } else { ++ attr->textLen = SecFormatFloat(attr->text.str, floatAdapt->fmtStr, dValue); ++ } ++ if (attr->textLen < 0 || attr->textLen >= floatAdapt->bufferSize) { ++ attr->textLen = 0; ++ } ++} ++#endif ++ ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++/* To clear e506 warning */ ++SECUREC_INLINE int SecIsSameSize(size_t sizeA, size_t sizeB) ++{ ++ return (int)(sizeA == sizeB); ++} ++#endif ++ ++#ifndef SECUREC_ON_64BITS ++/* ++ * Compiler Optimized Division 8. ++ * The text.str point to buffer end, must be Large enough ++ */ ++SECUREC_INLINE void SecNumber32ToOctalString(SecUnsignedInt32 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt32 val32 = number; ++ do { ++ --attr->text.str; ++ /* Just use lowerDigits for 0 - 9 */ ++ *(attr->text.str) = g_itoaLowerDigits[val32 % SECUREC_RADIX_OCTAL]; ++ val32 /= SECUREC_RADIX_OCTAL; ++ } while (val32 != 0); ++} ++ ++#ifdef _AIX ++/* ++ * Compiler Optimized Division 10. ++ * The text.str point to buffer end, must be Large enough ++ */ ++SECUREC_INLINE void SecNumber32ToDecString(SecUnsignedInt32 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt32 val32 = number; ++ do { ++ --attr->text.str; ++ /* Just use lowerDigits for 0 - 9 */ ++ *(attr->text.str) = g_itoaLowerDigits[val32 % SECUREC_RADIX_DECIMAL]; ++ val32 /= SECUREC_RADIX_DECIMAL; ++ } while (val32 != 0); ++} ++#endif ++/* ++ * Compiler Optimized Division 16. ++ * The text.str point to buffer end, must be Large enough ++ */ ++SECUREC_INLINE void SecNumber32ToHexString(SecUnsignedInt32 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt32 val32 = number; ++ do { ++ --attr->text.str; ++ *(attr->text.str) = attr->digits[val32 % SECUREC_RADIX_HEX]; ++ val32 /= SECUREC_RADIX_HEX; ++ } while (val32 != 0); ++} ++ ++#ifndef _AIX ++/* Use fast div 10 */ ++SECUREC_INLINE void SecNumber32ToDecStringFast(SecUnsignedInt32 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt32 val32 = number; ++ do { ++ SecUnsignedInt32 quotient; ++ SecUnsignedInt32 remain; ++ --attr->text.str; ++ *(attr->text.str) = g_itoaLowerDigits[val32 % SECUREC_RADIX_DECIMAL]; ++ quotient = (val32 >> 1U) + (val32 >> 2U); /* Fast div magic 2 */ ++ quotient = quotient + (quotient >> 4U); /* Fast div magic 4 */ ++ quotient = quotient + (quotient >> 8U); /* Fast div magic 8 */ ++ quotient = quotient + (quotient >> 16U); /* Fast div magic 16 */ ++ quotient = quotient >> 3U; /* Fast div magic 3 */ ++ remain = val32 - SECUREC_MUL_TEN(quotient); ++ val32 = (remain > 9U) ? (quotient + 1U) : quotient; /* Fast div magic 9 */ ++ } while (val32 != 0); ++} ++#endif ++ ++SECUREC_INLINE void SecNumber32ToString(SecUnsignedInt32 number, SecFormatAttr *attr) ++{ ++ switch (attr->radix) { ++ case SECUREC_RADIX_HEX: ++ SecNumber32ToHexString(number, attr); ++ break; ++ case SECUREC_RADIX_OCTAL: ++ SecNumber32ToOctalString(number, attr); ++ break; ++ case SECUREC_RADIX_DECIMAL: ++#ifdef _AIX ++ /* The compiler will optimize div 10 */ ++ SecNumber32ToDecString(number, attr); ++#else ++ SecNumber32ToDecStringFast(number, attr); ++#endif ++ break; ++ default: ++ /* Do nothing */ ++ break; ++ } ++} ++#endif ++ ++#if defined(SECUREC_USE_SPECIAL_DIV64) || (defined(SECUREC_VXWORKS_VERSION_5_4) && !defined(SECUREC_ON_64BITS)) ++/* ++ * This function just to clear warning, on sume vxworks compiler shift 32 bit make warnings ++ */ ++SECUREC_INLINE SecUnsignedInt64 SecU64Shr32(SecUnsignedInt64 number) ++{ ++ return (((number) >> 16U) >> 16U); /* Two shifts of 16 bits to realize shifts of 32 bits */ ++} ++/* ++ * Fast divide by 10 algorithm. ++ * Calculation divisor multiply 0xcccccccccccccccdULL, resultHi64 >> 3 as quotient ++ */ ++SECUREC_INLINE void SecU64Div10(SecUnsignedInt64 divisor, SecUnsignedInt64 *quotient, SecUnsignedInt32 *residue) ++{ ++ SecUnsignedInt64 mask = 0xffffffffULL; /* Use 0xffffffffULL as 32 bit mask */ ++ SecUnsignedInt64 magicHi = 0xccccccccULL; /* Fast divide 10 magic numbers high 32bit 0xccccccccULL */ ++ SecUnsignedInt64 magicLow = 0xcccccccdULL; /* Fast divide 10 magic numbers low 32bit 0xcccccccdULL */ ++ SecUnsignedInt64 divisorHi = (SecUnsignedInt64)(SecU64Shr32(divisor)); /* High 32 bit use */ ++ SecUnsignedInt64 divisorLow = (SecUnsignedInt64)(divisor & mask); /* Low 32 bit mask */ ++ SecUnsignedInt64 factorHi = divisorHi * magicHi; ++ SecUnsignedInt64 factorLow1 = divisorHi * magicLow; ++ SecUnsignedInt64 factorLow2 = divisorLow * magicHi; ++ SecUnsignedInt64 factorLow3 = divisorLow * magicLow; ++ SecUnsignedInt64 carry = (factorLow1 & mask) + (factorLow2 & mask) + SecU64Shr32(factorLow3); ++ SecUnsignedInt64 resultHi64 = factorHi + SecU64Shr32(factorLow1) + SecU64Shr32(factorLow2) + SecU64Shr32(carry); ++ ++ *quotient = resultHi64 >> 3U; /* Fast divide 10 magic numbers 3 */ ++ *residue = (SecUnsignedInt32)(divisor - ((*quotient) * 10)); /* Quotient mul 10 */ ++ return; ++} ++#if defined(SECUREC_VXWORKS_VERSION_5_4) && !defined(SECUREC_ON_64BITS) ++/* ++ * Divide function for VXWORKS ++ */ ++SECUREC_INLINE int SecU64Div32(SecUnsignedInt64 divisor, SecUnsignedInt32 radix, ++ SecUnsignedInt64 *quotient, SecUnsignedInt32 *residue) ++{ ++ switch (radix) { ++ case SECUREC_RADIX_DECIMAL: ++ SecU64Div10(divisor, quotient, residue); ++ break; ++ case SECUREC_RADIX_HEX: ++ *quotient = SECUREC_DIV_QUOTIENT_HEX(divisor); ++ *residue = (SecUnsignedInt32)SECUREC_DIV_RESIDUE_HEX(divisor); ++ break; ++ case SECUREC_RADIX_OCTAL: ++ *quotient = SECUREC_DIV_QUOTIENT_OCTAL(divisor); ++ *residue = (SecUnsignedInt32)SECUREC_DIV_RESIDUE_OCTAL(divisor); ++ break; ++ default: ++ return -1; /* This does not happen in the current file */ ++ } ++ return 0; ++} ++SECUREC_INLINE void SecNumber64ToStringSpecial(SecUnsignedInt64 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt64 val64 = number; ++ do { ++ SecUnsignedInt32 digit = 0; /* Ascii value of digit */ ++ SecUnsignedInt64 quotient = 0; ++ if (SecU64Div32(val64, (SecUnsignedInt32)attr->radix, "ient, &digit) != 0) { ++ /* Just break, when enter this function, no error is returned */ ++ break; ++ } ++ --attr->text.str; ++ *(attr->text.str) = attr->digits[digit]; ++ val64 = quotient; ++ } while (val64 != 0); ++} ++#endif ++#endif ++ ++#if defined(SECUREC_ON_64BITS) || !defined(SECUREC_VXWORKS_VERSION_5_4) ++#if defined(SECUREC_USE_SPECIAL_DIV64) ++/* The compiler does not provide 64 bit division problems */ ++SECUREC_INLINE void SecNumber64ToDecString(SecUnsignedInt64 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt64 val64 = number; ++ do { ++ SecUnsignedInt64 quotient = 0; ++ SecUnsignedInt32 digit = 0; ++ SecU64Div10(val64, "ient, &digit); ++ --attr->text.str; ++ /* Just use lowerDigits for 0 - 9 */ ++ *(attr->text.str) = g_itoaLowerDigits[digit]; ++ val64 = quotient; ++ } while (val64 != 0); ++} ++#else ++/* ++ * Compiler Optimized Division 10. ++ * The text.str point to buffer end, must be Large enough ++ */ ++SECUREC_INLINE void SecNumber64ToDecString(SecUnsignedInt64 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt64 val64 = number; ++ do { ++ --attr->text.str; ++ /* Just use lowerDigits for 0 - 9 */ ++ *(attr->text.str) = g_itoaLowerDigits[val64 % SECUREC_RADIX_DECIMAL]; ++ val64 /= SECUREC_RADIX_DECIMAL; ++ } while (val64 != 0); ++} ++#endif ++ ++/* ++ * Compiler Optimized Division 8. ++ * The text.str point to buffer end, must be Large enough ++ */ ++SECUREC_INLINE void SecNumber64ToOctalString(SecUnsignedInt64 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt64 val64 = number; ++ do { ++ --attr->text.str; ++ /* Just use lowerDigits for 0 - 9 */ ++ *(attr->text.str) = g_itoaLowerDigits[val64 % SECUREC_RADIX_OCTAL]; ++ val64 /= SECUREC_RADIX_OCTAL; ++ } while (val64 != 0); ++} ++/* ++ * Compiler Optimized Division 16. ++ * The text.str point to buffer end, must be Large enough ++ */ ++SECUREC_INLINE void SecNumber64ToHexString(SecUnsignedInt64 number, SecFormatAttr *attr) ++{ ++ SecUnsignedInt64 val64 = number; ++ do { ++ --attr->text.str; ++ *(attr->text.str) = attr->digits[val64 % SECUREC_RADIX_HEX]; ++ val64 /= SECUREC_RADIX_HEX; ++ } while (val64 != 0); ++} ++ ++SECUREC_INLINE void SecNumber64ToString(SecUnsignedInt64 number, SecFormatAttr *attr) ++{ ++ switch (attr->radix) { ++ /* The compiler will optimize div 10 */ ++ case SECUREC_RADIX_DECIMAL: ++ SecNumber64ToDecString(number, attr); ++ break; ++ case SECUREC_RADIX_OCTAL: ++ SecNumber64ToOctalString(number, attr); ++ break; ++ case SECUREC_RADIX_HEX: ++ SecNumber64ToHexString(number, attr); ++ break; ++ default: ++ /* Do nothing */ ++ break; ++ } ++} ++#endif ++ ++/* ++ * Converting integers to string ++ */ ++SECUREC_INLINE void SecNumberToString(SecUnsignedInt64 number, SecFormatAttr *attr) ++{ ++#ifdef SECUREC_ON_64BITS ++ SecNumber64ToString(number, attr); ++#else /* For 32 bits system */ ++ if (number <= 0xffffffffUL) { /* Use 0xffffffffUL to check if the value is in the 32-bit range */ ++ /* In most case, the value to be converted is small value */ ++ SecUnsignedInt32 n32Tmp = (SecUnsignedInt32)number; ++ SecNumber32ToString(n32Tmp, attr); ++ } else { ++ /* The value to be converted is greater than 4G */ ++#if defined(SECUREC_VXWORKS_VERSION_5_4) ++ SecNumber64ToStringSpecial(number, attr); ++#else ++ SecNumber64ToString(number, attr); ++#endif ++ } ++#endif ++} ++ ++SECUREC_INLINE int SecIsNumberNeedTo32Bit(const SecFormatAttr *attr) ++{ ++ return (int)(((attr->flags & SECUREC_FLAG_I64) == 0) && ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++ ((attr->flags & SECUREC_FLAG_INTMAX) == 0) && ++#endif ++#ifdef SECUREC_ON_64BITS ++ ((attr->flags & SECUREC_FLAG_PTRDIFF) == 0) && ++ ((attr->flags & SECUREC_FLAG_SIZE) == 0) && ++#if !defined(SECUREC_COMPATIBLE_WIN_FORMAT) /* on window 64 system sizeof long is 32bit */ ++ ((attr->flags & SECUREC_FLAG_LONG) == 0) && ++#endif ++#endif ++ ((attr->flags & SECUREC_FLAG_LONGLONG) == 0)); ++} ++ ++SECUREC_INLINE void SecNumberToBuffer(SecFormatAttr *attr, SecInt64 num64) ++{ ++ SecUnsignedInt64 number; ++ /* Check for negative; copy into number */ ++ if ((attr->flags & SECUREC_FLAG_SIGNED) != 0 && num64 < 0) { ++ number = (SecUnsignedInt64)(0 - (SecUnsignedInt64)num64); /* Wrap with unsigned int64 numbers */ ++ attr->flags |= SECUREC_FLAG_NEGATIVE; ++ } else { ++ number = (SecUnsignedInt64)num64; ++ } ++ if (SecIsNumberNeedTo32Bit(attr) != 0) { ++ number = (number & (SecUnsignedInt64)0xffffffffUL); /* Use 0xffffffff as 32 bit mask */ ++ } ++ ++ /* The text.str must be point to buffer.str, this pointer is used outside the function */ ++ attr->text.str = &attr->buffer.str[SECUREC_BUFFER_SIZE]; ++ ++ if (number == 0) { ++ /* Turn off hex prefix default, and textLen is zero */ ++ attr->prefixLen = 0; ++ attr->textLen = 0; ++ return; ++ } ++ ++ /* Convert integer to string. It must be invoked when number > 0, otherwise the following logic is incorrect */ ++ SecNumberToString(number, attr); ++ /* Compute length of number, text.str must be in buffer.str */ ++ attr->textLen = (int)(size_t)((char *)&attr->buffer.str[SECUREC_BUFFER_SIZE] - attr->text.str); ++} ++ ++/* ++ * Write one character to dest buffer ++ */ ++SECUREC_INLINE void SecWriteChar(SecPrintfStream *stream, SecChar ch, int *charsOut) ++{ ++ /* Count must be reduced first, In order to identify insufficient length */ ++ --stream->count; ++ if (stream->count >= 0) { ++ *(stream->cur) = ch; ++ ++stream->cur; ++ *charsOut = *charsOut + 1; ++ return; ++ } ++ /* No enough length */ ++ *charsOut = -1; ++} ++ ++/* ++* Write multiple identical characters. ++*/ ++SECUREC_INLINE void SecWriteMultiChar(SecPrintfStream *stream, SecChar ch, int num, int *charsOut) ++{ ++ int count; ++ for (count = num; count > 0; --count) { ++ --stream->count; /* count may be negative,indicating insufficient space */ ++ if (stream->count < 0) { ++ *charsOut = -1; ++ return; ++ } ++ *(stream->cur) = ch; ++ ++stream->cur; ++ } ++ *charsOut = *charsOut + num; ++} ++ ++/* ++* Write string function, where this function is called, make sure that len is greater than 0 ++*/ ++SECUREC_INLINE void SecWriteString(SecPrintfStream *stream, const SecChar *str, int len, int *charsOut) ++{ ++ const SecChar *tmp = str; ++ int count; ++ for (count = len; count > 0; --count) { ++ --stream->count; /* count may be negative,indicating insufficient space */ ++ if (stream->count < 0) { ++ *charsOut = -1; ++ return; ++ } ++ *(stream->cur) = *tmp; ++ ++stream->cur; ++ ++tmp; ++ } ++ *charsOut = *charsOut + len; ++} ++ ++/* Use loop copy char or wchar_t string */ ++SECUREC_INLINE void SecWriteStringByLoop(SecPrintfStream *stream, const SecChar *str, int len) ++{ ++ int i; ++ const SecChar *tmp = str; ++ for (i = 0; i < len; ++i) { ++ *stream->cur = *tmp; ++ ++stream->cur; ++ ++tmp; ++ } ++ stream->count -= len; ++} ++ ++SECUREC_INLINE void SecWriteStringOpt(SecPrintfStream *stream, const SecChar *str, int len) ++{ ++ if (len < 12) { /* Performance optimization for mobile number length 12 */ ++ SecWriteStringByLoop(stream, str, len); ++ } else { ++ size_t count = (size_t)(unsigned int)len * sizeof(SecChar); ++ SECUREC_MEMCPY_WARP_OPT(stream->cur, str, count); ++ stream->cur += len; ++ stream->count -= len; ++ } ++} ++ ++/* ++ * Return if buffer length is enough ++ * The count variable can be reduced to 0, and the external function complements the \0 terminator. ++ */ ++SECUREC_INLINE int SecIsStreamBufEnough(const SecPrintfStream *stream, int needLen) ++{ ++ return (int)(stream->count >= needLen); ++} ++ ++/* Write text string */ ++SECUREC_INLINE void SecWriteTextOpt(SecPrintfStream *stream, const SecChar *str, int len, int *charsOut) ++{ ++ if (SecIsStreamBufEnough(stream, len) != 0) { ++ SecWriteStringOpt(stream, str, len); ++ *charsOut += len; ++ } else { ++ SecWriteString(stream, str, len, charsOut); ++ } ++} ++ ++/* Write left padding */ ++SECUREC_INLINE void SecWriteLeftPadding(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++ if ((attr->flags & (SECUREC_FLAG_LEFT | SECUREC_FLAG_LEADZERO)) == 0 && attr->padding > 0) { ++ /* Pad on left with blanks */ ++ SecWriteMultiChar(stream, SECUREC_CHAR(' '), attr->padding, charsOut); ++ } ++} ++ ++/* Write prefix */ ++SECUREC_INLINE void SecWritePrefix(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++ if (attr->prefixLen > 0) { ++ SecWriteString(stream, attr->prefix, attr->prefixLen, charsOut); ++ } ++} ++ ++/* Write leading zeros */ ++SECUREC_INLINE void SecWriteLeadingZero(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++ if ((attr->flags & SECUREC_FLAG_LEADZERO) != 0 && (attr->flags & SECUREC_FLAG_LEFT) == 0 && ++ attr->padding > 0) { ++ SecWriteMultiChar(stream, SECUREC_CHAR('0'), attr->padding, charsOut); ++ } ++} ++ ++/* Write right padding */ ++SECUREC_INLINE void SecWriteRightPadding(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++ if (*charsOut >= 0 && (attr->flags & SECUREC_FLAG_LEFT) != 0 && attr->padding > 0) { ++ /* Pad on right with blanks */ ++ SecWriteMultiChar(stream, SECUREC_CHAR(' '), attr->padding, charsOut); ++ } ++} ++ ++#ifdef SECUREC_FOR_WCHAR ++#define SECUREC_TEXT_CHAR_PTR(text) ((text).wStr) ++#define SECUREC_NEED_CONVERT_TEXT(attr) ((attr)->textIsWide == 0) ++#if SECUREC_HAVE_MBTOWC ++#define SECUREC_WRITE_TEXT_AFTER_CONVERT(stream, attr, charsOut) SecWriteTextAfterMbtowc((stream), (attr), (charsOut)) ++#else ++#define SECUREC_WRITE_TEXT_AFTER_CONVERT(stream, attr, charsOut) (*(charsOut) = -1) ++#endif ++#else ++#define SECUREC_TEXT_CHAR_PTR(text) ((text).str) ++#define SECUREC_NEED_CONVERT_TEXT(attr) ((attr)->textIsWide != 0) ++#if SECUREC_HAVE_WCTOMB ++#define SECUREC_WRITE_TEXT_AFTER_CONVERT(stream, attr, charsOut) SecWriteTextAfterWctomb((stream), (attr), (charsOut)) ++#else ++#define SECUREC_WRITE_TEXT_AFTER_CONVERT(stream, attr, charsOut) (*(charsOut) = -1) ++#endif ++#endif ++ ++#ifdef SECUREC_FOR_WCHAR ++#if SECUREC_HAVE_MBTOWC ++SECUREC_INLINE void SecWriteTextAfterMbtowc(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++ const char *p = attr->text.str; ++ int count = attr->textLen; ++ while (count > 0) { ++ wchar_t wChar = L'\0'; ++ int retVal = mbtowc(&wChar, p, (size_t)MB_CUR_MAX); ++ if (retVal <= 0) { ++ *charsOut = -1; ++ break; ++ } ++ SecWriteChar(stream, wChar, charsOut); ++ if (*charsOut == -1) { ++ break; ++ } ++ p += retVal; ++ count -= retVal; ++ } ++} ++#endif ++#else /* Not SECUREC_FOR_WCHAR */ ++#if SECUREC_HAVE_WCTOMB ++SECUREC_INLINE void SecWriteTextAfterWctomb(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++ const wchar_t *p = attr->text.wStr; ++ int count = attr->textLen; ++ while (count > 0) { ++ char tmpBuf[SECUREC_MB_LEN + 1]; ++ SECUREC_MASK_MSVC_CRT_WARNING ++ int retVal = wctomb(tmpBuf, *p); ++ SECUREC_END_MASK_MSVC_CRT_WARNING ++ if (retVal <= 0) { ++ *charsOut = -1; ++ break; ++ } ++ SecWriteString(stream, tmpBuf, retVal, charsOut); ++ if (*charsOut == -1) { ++ break; ++ } ++ --count; ++ ++p; ++ } ++} ++#endif ++#endif ++ ++#if SECUREC_ENABLE_SPRINTF_FLOAT ++/* ++ * Write text of float ++ * Using independent functions to optimize the expansion of inline functions by the compiler ++ */ ++SECUREC_INLINE void SecWriteFloatText(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++#ifdef SECUREC_FOR_WCHAR ++#if SECUREC_HAVE_MBTOWC ++ SecWriteTextAfterMbtowc(stream, attr, charsOut); ++#else ++ *charsOut = -1; ++ (void)stream; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ (void)attr; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++#endif ++#else /* Not SECUREC_FOR_WCHAR */ ++ SecWriteString(stream, attr->text.str, attr->textLen, charsOut); ++#endif ++} ++#endif ++ ++/* Write text of integer or string ... */ ++SECUREC_INLINE void SecWriteText(SecPrintfStream *stream, const SecFormatAttr *attr, int *charsOut) ++{ ++ if (SECUREC_NEED_CONVERT_TEXT(attr)) { ++ SECUREC_WRITE_TEXT_AFTER_CONVERT(stream, attr, charsOut); ++ } else { ++ SecWriteTextOpt(stream, SECUREC_TEXT_CHAR_PTR(attr->text), attr->textLen, charsOut); ++ } ++} ++ ++#define SECUREC_FMT_STATE_OFFSET 256 ++ ++SECUREC_INLINE SecFmtState SecDecodeState(SecChar ch, SecFmtState lastState) ++{ ++ static const unsigned char stateTable[SECUREC_STATE_TABLE_SIZE] = { ++ /* ++ * Type ++ * 0: nospecial meaning; ++ * 1: '%' ++ * 2: '.' ++ * 3: '*' ++ * 4: '0' ++ * 5: '1' ... '9' ++ * 6: ' ', '+', '-', '#' ++ * 7: 'h', 'l', 'L', 'w' , 'N', 'z', 'q', 't', 'j' ++ * 8: 'd', 'o', 'u', 'i', 'x', 'X', 'e', 'f', 'g', 'E', 'F', 'G', 's', 'c', '[', 'p' ++ */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x06, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x06, 0x02, 0x00, ++ 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x08, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, ++ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x08, 0x07, 0x00, 0x07, 0x00, 0x00, 0x08, ++ 0x08, 0x07, 0x00, 0x08, 0x07, 0x08, 0x00, 0x07, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, ++ /* Fill zero for normal char 128 byte for 0x80 - 0xff */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ /* ++ * State ++ * 0: normal ++ * 1: percent ++ * 2: flag ++ * 3: width ++ * 4: dot ++ * 5: precis ++ * 6: size ++ * 7: type ++ * 8: invalid ++ */ ++ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x01, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, ++ 0x01, 0x00, 0x00, 0x04, 0x04, 0x04, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x03, 0x03, 0x08, 0x05, ++ 0x08, 0x08, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x05, 0x05, 0x08, 0x00, 0x00, 0x00, 0x03, 0x03, ++ 0x03, 0x05, 0x05, 0x08, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, ++ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, ++ 0x00 ++ }; ++ ++#ifdef SECUREC_FOR_WCHAR ++ /* Convert to unsigned char to clear gcc 4.3.4 warning */ ++ unsigned char fmtType = (unsigned char)((((unsigned int)(int)(ch)) <= (unsigned int)(int)(L'~')) ? \ ++ (stateTable[(unsigned char)(ch)]) : 0); ++ return (SecFmtState)(stateTable[fmtType * ((unsigned char)STAT_INVALID + 1) + ++ (unsigned char)(lastState) + SECUREC_FMT_STATE_OFFSET]); ++#else ++ unsigned char fmtType = stateTable[(unsigned char)(ch)]; ++ return (SecFmtState)(stateTable[fmtType * ((unsigned char)STAT_INVALID + 1) + ++ (unsigned char)(lastState) + SECUREC_FMT_STATE_OFFSET]); ++#endif ++} ++ ++SECUREC_INLINE void SecDecodeFlags(SecChar ch, SecFormatAttr *attr) ++{ ++ switch (ch) { ++ case SECUREC_CHAR(' '): ++ attr->flags |= SECUREC_FLAG_SIGN_SPACE; ++ break; ++ case SECUREC_CHAR('+'): ++ attr->flags |= SECUREC_FLAG_SIGN; ++ break; ++ case SECUREC_CHAR('-'): ++ attr->flags |= SECUREC_FLAG_LEFT; ++ break; ++ case SECUREC_CHAR('0'): ++ attr->flags |= SECUREC_FLAG_LEADZERO; /* Add zero th the front */ ++ break; ++ case SECUREC_CHAR('#'): ++ attr->flags |= SECUREC_FLAG_ALTERNATE; /* Output %x with 0x */ ++ break; ++ default: ++ /* Do nothing */ ++ break; ++ } ++ return; ++} ++ ++/* ++ * Decoded size identifier in format string to Reduce the number of lines of function code ++ */ ++SECUREC_INLINE int SecDecodeSizeI(SecFormatAttr *attr, const SecChar **format) ++{ ++#ifdef SECUREC_ON_64BITS ++ attr->flags |= SECUREC_FLAG_I64; /* %I to INT64 */ ++#endif ++ if ((**format == SECUREC_CHAR('6')) && (*((*format) + 1) == SECUREC_CHAR('4'))) { ++ (*format) += 2; /* Add 2 to skip I64 */ ++ attr->flags |= SECUREC_FLAG_I64; /* %I64 to INT64 */ ++ } else if ((**format == SECUREC_CHAR('3')) && (*((*format) + 1) == SECUREC_CHAR('2'))) { ++ (*format) += 2; /* Add 2 to skip I32 */ ++ attr->flags &= ~SECUREC_FLAG_I64; /* %I64 to INT32 */ ++ } else if ((**format == SECUREC_CHAR('d')) || (**format == SECUREC_CHAR('i')) || ++ (**format == SECUREC_CHAR('o')) || (**format == SECUREC_CHAR('u')) || ++ (**format == SECUREC_CHAR('x')) || (**format == SECUREC_CHAR('X'))) { ++ /* Do nothing */ ++ } else { ++ /* Compatibility code for "%I" just print I */ ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * Decoded size identifier in format string, and skip format to next charater ++ */ ++SECUREC_INLINE int SecDecodeSize(SecChar ch, SecFormatAttr *attr, const SecChar **format) ++{ ++ switch (ch) { ++ case SECUREC_CHAR('l'): ++ if (**format == SECUREC_CHAR('l')) { ++ *format = *format + 1; ++ attr->flags |= SECUREC_FLAG_LONGLONG; /* For long long */ ++ } else { ++ attr->flags |= SECUREC_FLAG_LONG; /* For long int or wchar_t */ ++ } ++ break; ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++ case SECUREC_CHAR('z'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('Z'): ++ attr->flags |= SECUREC_FLAG_SIZE; ++ break; ++ case SECUREC_CHAR('j'): ++ attr->flags |= SECUREC_FLAG_INTMAX; ++ break; ++#endif ++ case SECUREC_CHAR('t'): ++ attr->flags |= SECUREC_FLAG_PTRDIFF; ++ break; ++ case SECUREC_CHAR('q'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('L'): ++ attr->flags |= (SECUREC_FLAG_LONGLONG | SECUREC_FLAG_LONG_DOUBLE); ++ break; ++ case SECUREC_CHAR('I'): ++ if (SecDecodeSizeI(attr, format) != 0) { ++ /* Compatibility code for "%I" just print I */ ++ return -1; ++ } ++ break; ++ case SECUREC_CHAR('h'): ++ if (**format == SECUREC_CHAR('h')) { ++ *format = *format + 1; ++ attr->flags |= SECUREC_FLAG_CHAR; /* For char */ ++ } else { ++ attr->flags |= SECUREC_FLAG_SHORT; /* For short int */ ++ } ++ break; ++ case SECUREC_CHAR('w'): ++ attr->flags |= SECUREC_FLAG_WIDECHAR; /* For wide char */ ++ break; ++ default: ++ /* Do nothing */ ++ break; ++ } ++ return 0; ++} ++ ++/* ++ * Decoded char type identifier ++ */ ++SECUREC_INLINE void SecDecodeTypeC(SecFormatAttr *attr, unsigned int c) ++{ ++ attr->textLen = 1; /* Only 1 wide character */ ++ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT)) && !(defined(__hpux)) && !(defined(SECUREC_ON_SOLARIS)) ++ attr->flags &= ~SECUREC_FLAG_LEADZERO; ++#endif ++ ++#ifdef SECUREC_FOR_WCHAR ++ if ((attr->flags & SECUREC_FLAG_SHORT) != 0) { ++ /* Get multibyte character from argument */ ++ attr->buffer.str[0] = (char)c; ++ attr->text.str = attr->buffer.str; ++ attr->textIsWide = 0; ++ } else { ++ attr->buffer.wStr[0] = (wchar_t)c; ++ attr->text.wStr = attr->buffer.wStr; ++ attr->textIsWide = 1; ++ } ++#else /* Not SECUREC_FOR_WCHAR */ ++ if ((attr->flags & (SECUREC_FLAG_LONG | SECUREC_FLAG_WIDECHAR)) != 0) { ++#if SECUREC_HAVE_WCHART ++ attr->buffer.wStr[0] = (wchar_t)c; ++ attr->text.wStr = attr->buffer.wStr; ++ attr->textIsWide = 1; ++#else ++ attr->textLen = 0; /* Ignore unsupported characters */ ++ attr->fldWidth = 0; /* No paddings */ ++#endif ++ } else { ++ /* Get multibyte character from argument */ ++ attr->buffer.str[0] = (char)c; ++ attr->text.str = attr->buffer.str; ++ attr->textIsWide = 0; ++ } ++#endif ++} ++ ++#ifdef SECUREC_FOR_WCHAR ++#define SECUREC_IS_NARROW_STRING(attr) (((attr)->flags & SECUREC_FLAG_SHORT) != 0) ++#else ++#define SECUREC_IS_NARROW_STRING(attr) (((attr)->flags & (SECUREC_FLAG_LONG | SECUREC_FLAG_WIDECHAR)) == 0) ++#endif ++ ++SECUREC_INLINE void SecDecodeTypeSchar(SecFormatAttr *attr) ++{ ++ size_t textLen; ++ if (attr->text.str == NULL) { ++ /* ++ * Literal string to print null ptr, define it as array rather than const text area ++ * To avoid gcc warning with pointing const text with variable ++ */ ++ static char strNullString[SECUREC_NULL_STRING_SIZE] = "(null)"; ++ attr->text.str = strNullString; ++ } ++ if (attr->precision == -1) { ++ /* Precision NOT assigned */ ++ /* The strlen performance is high when the string length is greater than 32 */ ++ textLen = strlen(attr->text.str); ++ if (textLen > SECUREC_STRING_MAX_LEN) { ++ textLen = 0; ++ } ++ } else { ++ /* Precision assigned */ ++ SECUREC_CALC_STR_LEN(attr->text.str, (size_t)(unsigned int)attr->precision, &textLen); ++ } ++ attr->textLen = (int)textLen; ++} ++ ++SECUREC_INLINE void SecDecodeTypeSwchar(SecFormatAttr *attr) ++{ ++#if SECUREC_HAVE_WCHART ++ size_t textLen; ++ attr->textIsWide = 1; ++ if (attr->text.wStr == NULL) { ++ /* ++ * Literal string to print null ptr, define it as array rather than const text area ++ * To avoid gcc warning with pointing const text with variable ++ */ ++ static wchar_t wStrNullString[SECUREC_NULL_STRING_SIZE] = { L'(', L'n', L'u', L'l', L'l', L')', L'\0', L'\0' }; ++ attr->text.wStr = wStrNullString; ++ } ++ /* The textLen in wchar_t,when precision is -1, it is unlimited */ ++ SECUREC_CALC_WSTR_LEN(attr->text.wStr, (size_t)(unsigned int)attr->precision, &textLen); ++ if (textLen > SECUREC_WCHAR_STRING_MAX_LEN) { ++ textLen = 0; ++ } ++ attr->textLen = (int)textLen; ++#else ++ attr->textLen = 0; ++#endif ++} ++ ++/* ++ * Decoded string identifier ++ */ ++SECUREC_INLINE void SecDecodeTypeS(SecFormatAttr *attr, char *argPtr) ++{ ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT)) ++#if (!defined(SECUREC_ON_UNIX)) ++ attr->flags &= ~SECUREC_FLAG_LEADZERO; ++#endif ++#if (defined(SECUREC_FOR_WCHAR)) ++ if ((attr->flags & SECUREC_FLAG_LONG) == 0) { ++ attr->flags |= SECUREC_FLAG_SHORT; ++ } ++#endif ++#endif ++ attr->text.str = argPtr; ++ if (SECUREC_IS_NARROW_STRING(attr)) { ++ /* The textLen now contains length in multibyte chars */ ++ SecDecodeTypeSchar(attr); ++ } else { ++ /* The textLen now contains length in wide chars */ ++ SecDecodeTypeSwchar(attr); ++ } ++} ++ ++/* ++ * Check precision in format ++ */ ++SECUREC_INLINE int SecDecodePrecision(SecChar ch, SecFormatAttr *attr) ++{ ++ if (attr->dynPrecision == 0) { ++ /* Add digit to current precision */ ++ if (SECUREC_MUL_TEN_ADD_BEYOND_MAX(attr->precision)) { ++ return -1; ++ } ++ attr->precision = (int)SECUREC_MUL_TEN((unsigned int)attr->precision) + ++ (unsigned char)(ch - SECUREC_CHAR('0')); ++ } else { ++ if (attr->precision < 0) { ++ attr->precision = -1; ++ } ++ if (attr->precision > SECUREC_MAX_WIDTH_LEN) { ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * Check width in format ++ */ ++SECUREC_INLINE int SecDecodeWidth(SecChar ch, SecFormatAttr *attr, SecFmtState lastState) ++{ ++ if (attr->dynWidth == 0) { ++ if (lastState != STAT_WIDTH) { ++ attr->fldWidth = 0; ++ } ++ if (SECUREC_MUL_TEN_ADD_BEYOND_MAX(attr->fldWidth)) { ++ return -1; ++ } ++ attr->fldWidth = (int)SECUREC_MUL_TEN((unsigned int)attr->fldWidth) + ++ (unsigned char)(ch - SECUREC_CHAR('0')); ++ } else { ++ if (attr->fldWidth < 0) { ++ attr->flags |= SECUREC_FLAG_LEFT; ++ attr->fldWidth = (-attr->fldWidth); ++ } ++ if (attr->fldWidth > SECUREC_MAX_WIDTH_LEN) { ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * The sprintf_s function processes the wide character as a parameter for %C ++ * The swprintf_s function processes the multiple character as a parameter for %C ++ */ ++SECUREC_INLINE void SecUpdateWcharFlags(SecFormatAttr *attr) ++{ ++ if ((attr->flags & (SECUREC_FLAG_SHORT | SECUREC_FLAG_LONG | SECUREC_FLAG_WIDECHAR)) == 0) { ++#ifdef SECUREC_FOR_WCHAR ++ attr->flags |= SECUREC_FLAG_SHORT; ++#else ++ attr->flags |= SECUREC_FLAG_WIDECHAR; ++#endif ++ } ++} ++/* ++ * When encountering %S, current just same as %C ++ */ ++SECUREC_INLINE void SecUpdateWstringFlags(SecFormatAttr *attr) ++{ ++ SecUpdateWcharFlags(attr); ++} ++ ++#if SECUREC_IN_KERNEL ++SECUREC_INLINE void SecUpdatePointFlagsForKernel(SecFormatAttr *attr) ++{ ++ /* Width is not set */ ++ if (attr->fldWidth <= 0) { ++ attr->flags |= SECUREC_FLAG_LEADZERO; ++ attr->fldWidth = 2 * sizeof(void *); /* 2 x byte number is the length of hex */ ++ } ++ if ((attr->flags & SECUREC_FLAG_ALTERNATE) != 0) { ++ /* Alternate form means '0x' prefix */ ++ attr->prefix[0] = SECUREC_CHAR('0'); ++ attr->prefix[1] = SECUREC_CHAR('x'); ++ attr->prefixLen = SECUREC_PREFIX_LEN; ++ } ++ attr->flags |= SECUREC_FLAG_LONG; /* Converting a long */ ++} ++#endif ++ ++SECUREC_INLINE void SecUpdatePointFlags(SecFormatAttr *attr) ++{ ++ attr->flags |= SECUREC_FLAG_POINTER; ++#if SECUREC_IN_KERNEL ++ SecUpdatePointFlagsForKernel(attr); ++#else ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) || defined(SECUREC_VXWORKS_PLATFORM)) && (!defined(SECUREC_ON_UNIX)) ++#if defined(SECUREC_VXWORKS_PLATFORM) ++ attr->precision = 1; ++#else ++ attr->precision = 0; ++#endif ++ attr->flags |= SECUREC_FLAG_ALTERNATE; /* "0x" is not default prefix in UNIX */ ++ attr->digits = g_itoaLowerDigits; ++#else /* On unix or win */ ++#if defined(_AIX) || defined(SECUREC_ON_SOLARIS) ++ attr->precision = 1; ++#else ++ attr->precision = 2 * sizeof(void *); /* 2 x byte number is the length of hex */ ++#endif ++#if defined(SECUREC_ON_UNIX) ++ attr->digits = g_itoaLowerDigits; ++#else ++ attr->digits = g_itoaUpperDigits; ++#endif ++#endif ++ ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ attr->flags &= ~SECUREC_FLAG_LEADZERO; ++#endif ++ ++#ifdef SECUREC_ON_64BITS ++ attr->flags |= SECUREC_FLAG_I64; /* Converting an int64 */ ++#else ++ attr->flags |= SECUREC_FLAG_LONG; /* Converting a long */ ++#endif ++ /* Set up for %#p on different system */ ++ if ((attr->flags & SECUREC_FLAG_ALTERNATE) != 0) { ++ /* Alternate form means '0x' prefix */ ++ attr->prefix[0] = SECUREC_CHAR('0'); ++#if (defined(SECUREC_COMPATIBLE_LINUX_FORMAT) || defined(SECUREC_VXWORKS_PLATFORM)) ++ attr->prefix[1] = SECUREC_CHAR('x'); ++#else ++ attr->prefix[1] = (SecChar)(attr->digits[SECUREC_NUMBER_OF_X]); ++#endif ++#if defined(_AIX) || defined(SECUREC_ON_SOLARIS) ++ attr->prefixLen = 0; ++#else ++ attr->prefixLen = SECUREC_PREFIX_LEN; ++#endif ++ } ++#endif ++} ++ ++SECUREC_INLINE void SecUpdateXpxFlags(SecFormatAttr *attr, SecChar ch) ++{ ++ /* Use unsigned lower hex output for 'x' */ ++ attr->digits = g_itoaLowerDigits; ++ attr->radix = SECUREC_RADIX_HEX; ++ switch (ch) { ++ case SECUREC_CHAR('p'): ++ /* Print a pointer */ ++ SecUpdatePointFlags(attr); ++ break; ++ case SECUREC_CHAR('X'): /* fall-through */ /* FALLTHRU */ ++ /* Unsigned upper hex output */ ++ attr->digits = g_itoaUpperDigits; ++ /* fall-through */ /* FALLTHRU */ ++ default: ++ /* For %#x or %#X */ ++ if ((attr->flags & SECUREC_FLAG_ALTERNATE) != 0) { ++ /* Alternate form means '0x' prefix */ ++ attr->prefix[0] = SECUREC_CHAR('0'); ++ attr->prefix[1] = (SecChar)(attr->digits[SECUREC_NUMBER_OF_X]); ++ attr->prefixLen = SECUREC_PREFIX_LEN; ++ } ++ break; ++ } ++} ++ ++SECUREC_INLINE void SecUpdateOudiFlags(SecFormatAttr *attr, SecChar ch) ++{ ++ /* Do not set digits here */ ++ switch (ch) { ++ case SECUREC_CHAR('i'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('d'): /* fall-through */ /* FALLTHRU */ ++ /* For signed decimal output */ ++ attr->flags |= SECUREC_FLAG_SIGNED; ++ /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('u'): ++ attr->radix = SECUREC_RADIX_DECIMAL; ++ attr->digits = g_itoaLowerDigits; ++ break; ++ case SECUREC_CHAR('o'): ++ /* For unsigned octal output */ ++ attr->radix = SECUREC_RADIX_OCTAL; ++ attr->digits = g_itoaLowerDigits; ++ if ((attr->flags & SECUREC_FLAG_ALTERNATE) != 0) { ++ /* Alternate form means force a leading 0 */ ++ attr->flags |= SECUREC_FLAG_FORCE_OCTAL; ++ } ++ break; ++ default: ++ /* Do nothing */ ++ break; ++ } ++} ++ ++#if SECUREC_ENABLE_SPRINTF_FLOAT ++SECUREC_INLINE void SecFreeFloatBuffer(SecFloatAdapt *floatAdapt) ++{ ++ if (floatAdapt->floatBuffer != NULL) { ++ SECUREC_FREE(floatAdapt->floatBuffer); ++ } ++ if (floatAdapt->allocatedFmtStr != NULL) { ++ SECUREC_FREE(floatAdapt->allocatedFmtStr); ++ } ++ floatAdapt->floatBuffer = NULL; ++ floatAdapt->allocatedFmtStr = NULL; ++ floatAdapt->fmtStr = NULL; ++ floatAdapt->bufferSize = 0; ++} ++ ++SECUREC_INLINE void SecSeekToFrontPercent(const SecChar **format) ++{ ++ const SecChar *fmt = *format; ++ while (*fmt != SECUREC_CHAR('%')) { /* Must meet '%' */ ++ --fmt; ++ } ++ *format = fmt; ++} ++ ++/* Init float format, return 0 is OK */ ++SECUREC_INLINE int SecInitFloatFmt(SecFloatAdapt *floatFmt, const SecChar *format) ++{ ++ const SecChar *fmt = format - 2; /* Sub 2 to the position before 'f' or 'g' */ ++ int fmtStrLen; ++ int i; ++ ++ SecSeekToFrontPercent(&fmt); ++ /* Now fmt point to '%' */ ++ fmtStrLen = (int)(size_t)(format - fmt) + 1; /* With ending terminator */ ++ if (fmtStrLen > (int)sizeof(floatFmt->buffer)) { ++ /* When buffer is NOT enough, alloc a new buffer */ ++ floatFmt->allocatedFmtStr = (char *)SECUREC_MALLOC((size_t)((unsigned int)fmtStrLen)); ++ if (floatFmt->allocatedFmtStr == NULL) { ++ return -1; ++ } ++ floatFmt->fmtStr = floatFmt->allocatedFmtStr; ++ } else { ++ floatFmt->fmtStr = floatFmt->buffer; ++ floatFmt->allocatedFmtStr = NULL; /* Must set to NULL, later code free memory based on this identity */ ++ } ++ ++ for (i = 0; i < fmtStrLen - 1; ++i) { ++ /* Convert wchar to char */ ++ floatFmt->fmtStr[i] = (char)(fmt[i]); /* Copy the format string */ ++ } ++ floatFmt->fmtStr[fmtStrLen - 1] = '\0'; ++ ++ return 0; ++} ++ ++/* Init float buffer and format, return 0 is OK */ ++SECUREC_INLINE int SecInitFloatBuffer(SecFloatAdapt *floatAdapt, const SecChar *format, SecFormatAttr *attr) ++{ ++ floatAdapt->allocatedFmtStr = NULL; ++ floatAdapt->fmtStr = NULL; ++ floatAdapt->floatBuffer = NULL; ++ /* Compute the precision value */ ++ if (attr->precision < 0) { ++ attr->precision = SECUREC_FLOAT_DEFAULT_PRECISION; ++ } ++ /* ++ * Calc buffer size to store double value ++ * The maximum length of SECUREC_MAX_WIDTH_LEN is enough ++ */ ++ if ((attr->flags & SECUREC_FLAG_LONG_DOUBLE) != 0) { ++ if (attr->precision > (SECUREC_MAX_WIDTH_LEN - SECUREC_FLOAT_BUFSIZE_LB)) { ++ return -1; ++ } ++ /* Long double needs to meet the basic print length */ ++ floatAdapt->bufferSize = SECUREC_FLOAT_BUFSIZE_LB + attr->precision + SECUREC_FLOAT_BUF_EXT; ++ } else { ++ if (attr->precision > (SECUREC_MAX_WIDTH_LEN - SECUREC_FLOAT_BUFSIZE)) { ++ return -1; ++ } ++ /* Double needs to meet the basic print length */ ++ floatAdapt->bufferSize = SECUREC_FLOAT_BUFSIZE + attr->precision + SECUREC_FLOAT_BUF_EXT; ++ } ++ if (attr->fldWidth > floatAdapt->bufferSize) { ++ floatAdapt->bufferSize = attr->fldWidth + SECUREC_FLOAT_BUF_EXT; ++ } ++ ++ if (floatAdapt->bufferSize > SECUREC_BUFFER_SIZE) { ++ /* The current value of SECUREC_BUFFER_SIZE could not store the formatted float string */ ++ floatAdapt->floatBuffer = (char *)SECUREC_MALLOC(((size_t)(unsigned int)floatAdapt->bufferSize)); ++ if (floatAdapt->floatBuffer == NULL) { ++ return -1; ++ } ++ attr->text.str = floatAdapt->floatBuffer; ++ } else { ++ attr->text.str = attr->buffer.str; /* Output buffer for float string with default size */ ++ } ++ ++ if (SecInitFloatFmt(floatAdapt, format) != 0) { ++ if (floatAdapt->floatBuffer != NULL) { ++ SECUREC_FREE(floatAdapt->floatBuffer); ++ floatAdapt->floatBuffer = NULL; ++ } ++ return -1; ++ } ++ return 0; ++} ++#endif ++ ++SECUREC_INLINE SecInt64 SecUpdateNegativeChar(SecFormatAttr *attr, char ch) ++{ ++ SecInt64 num64 = ch; /* Sign extend */ ++ if (num64 >= 128) { /* 128 on some platform, char is always unsigned */ ++ unsigned char tmp = (unsigned char)(~((unsigned char)ch)); ++ num64 = tmp + 1; ++ attr->flags |= SECUREC_FLAG_NEGATIVE; ++ } ++ return num64; ++} ++ ++/* ++ * If the precision is not satisfied, zero is added before the string ++ */ ++SECUREC_INLINE void SecNumberSatisfyPrecision(SecFormatAttr *attr) ++{ ++ int precision; ++ if (attr->precision < 0) { ++ precision = 1; /* Default precision 1 */ ++ } else { ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++ attr->flags &= ~SECUREC_FLAG_LEADZERO; ++#else ++ if ((attr->flags & SECUREC_FLAG_POINTER) == 0) { ++ attr->flags &= ~SECUREC_FLAG_LEADZERO; ++ } ++#endif ++ if (attr->precision > SECUREC_MAX_PRECISION) { ++ attr->precision = SECUREC_MAX_PRECISION; ++ } ++ precision = attr->precision; ++ } ++ while (attr->textLen < precision) { ++ --attr->text.str; ++ *(attr->text.str) = '0'; ++ ++attr->textLen; ++ } ++} ++ ++/* ++ * Add leading zero for %#o ++ */ ++SECUREC_INLINE void SecNumberForceOctal(SecFormatAttr *attr) ++{ ++ /* Force a leading zero if FORCEOCTAL flag set */ ++ if ((attr->flags & SECUREC_FLAG_FORCE_OCTAL) != 0 && ++ (attr->textLen == 0 || attr->text.str[0] != '0')) { ++ --attr->text.str; ++ *(attr->text.str) = '0'; ++ ++attr->textLen; ++ } ++} ++ ++SECUREC_INLINE void SecUpdateSignedNumberPrefix(SecFormatAttr *attr) ++{ ++ if ((attr->flags & SECUREC_FLAG_SIGNED) == 0) { ++ return; ++ } ++ if ((attr->flags & SECUREC_FLAG_NEGATIVE) != 0) { ++ /* Prefix is '-' */ ++ attr->prefix[0] = SECUREC_CHAR('-'); ++ attr->prefixLen = 1; ++ return; ++ } ++ if ((attr->flags & SECUREC_FLAG_SIGN) != 0) { ++ /* Prefix is '+' */ ++ attr->prefix[0] = SECUREC_CHAR('+'); ++ attr->prefixLen = 1; ++ return; ++ } ++ if ((attr->flags & SECUREC_FLAG_SIGN_SPACE) != 0) { ++ /* Prefix is ' ' */ ++ attr->prefix[0] = SECUREC_CHAR(' '); ++ attr->prefixLen = 1; ++ return; ++ } ++ return; ++} ++ ++SECUREC_INLINE void SecNumberCompatZero(SecFormatAttr *attr) ++{ ++#if SECUREC_IN_KERNEL ++ if ((attr->flags & SECUREC_FLAG_POINTER) != 0) { ++ static char strNullPointer[SECUREC_NULL_STRING_SIZE] = "(null)"; ++ attr->text.str = strNullPointer; ++ attr->textLen = 6; /* Length of (null) is 6 */ ++ attr->flags &= ~SECUREC_FLAG_LEADZERO; ++ attr->prefixLen = 0; ++ if (attr->precision >= 0 && attr->precision < attr->textLen) { ++ attr->textLen = attr->precision; ++ } ++ } ++ if ((attr->flags & SECUREC_FLAG_POINTER) == 0 && attr->radix == SECUREC_RADIX_HEX && ++ (attr->flags & SECUREC_FLAG_ALTERNATE) != 0) { ++ /* Add 0x prefix for %x or %X, the prefix string has been set before */ ++ attr->prefixLen = SECUREC_PREFIX_LEN; ++ } ++#elif defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && (!defined(SECUREC_ON_UNIX)) ++ if ((attr->flags & SECUREC_FLAG_POINTER) != 0) { ++ static char strNullPointer[SECUREC_NULL_STRING_SIZE] = "(nil)"; ++ attr->text.str = strNullPointer; ++ attr->textLen = 5; /* Length of (nil) is 5 */ ++ attr->flags &= ~SECUREC_FLAG_LEADZERO; ++ } ++#elif defined(SECUREC_VXWORKS_PLATFORM) || defined(__hpux) ++ if ((attr->flags & SECUREC_FLAG_POINTER) != 0 && (attr->flags & SECUREC_FLAG_ALTERNATE) != 0) { ++ /* Add 0x prefix for %p, the prefix string has been set before */ ++ attr->prefixLen = SECUREC_PREFIX_LEN; ++ } ++#endif ++ (void)attr; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++} ++ ++/* ++ * Formatting output core function ++ */ ++SECUREC_INLINE int SecOutput(SecPrintfStream *stream, const SecChar *cFormat, va_list argList) ++{ ++ const SecChar *format = cFormat; ++ int charsOut; /* Characters written */ ++ int noOutput = 0; /* Must be initialized or compiler alerts */ ++ SecFmtState state; ++ SecFormatAttr formatAttr; ++ ++ formatAttr.flags = 0; ++ formatAttr.textIsWide = 0; /* Flag for buffer contains wide chars */ ++ formatAttr.fldWidth = 0; ++ formatAttr.precision = 0; ++ formatAttr.dynWidth = 0; ++ formatAttr.dynPrecision = 0; ++ formatAttr.digits = g_itoaUpperDigits; ++ formatAttr.radix = SECUREC_RADIX_DECIMAL; ++ formatAttr.padding = 0; ++ formatAttr.textLen = 0; ++ formatAttr.text.str = NULL; ++ formatAttr.prefixLen = 0; ++ formatAttr.prefix[0] = SECUREC_CHAR('\0'); ++ formatAttr.prefix[1] = SECUREC_CHAR('\0'); ++ charsOut = 0; ++ state = STAT_NORMAL; /* Starting state */ ++ ++ /* Loop each format character */ ++ while (*format != SECUREC_CHAR('\0') && charsOut >= 0) { ++ SecFmtState lastState = state; ++ SecChar ch = *format; /* Currently read character */ ++ ++format; ++ state = SecDecodeState(ch, lastState); ++ switch (state) { ++ case STAT_NORMAL: ++ SecWriteChar(stream, ch, &charsOut); ++ continue; ++ case STAT_PERCENT: ++ /* Set default values */ ++ noOutput = 0; ++ formatAttr.prefixLen = 0; ++ formatAttr.textLen = 0; ++ formatAttr.flags = 0; ++ formatAttr.fldWidth = 0; ++ formatAttr.precision = -1; ++ formatAttr.textIsWide = 0; ++ formatAttr.dynWidth = 0; ++ formatAttr.dynPrecision = 0; ++ break; ++ case STAT_FLAG: ++ /* Set flag based on which flag character */ ++ SecDecodeFlags(ch, &formatAttr); ++ break; ++ case STAT_WIDTH: ++ /* Update width value */ ++ if (ch == SECUREC_CHAR('*')) { ++ /* get width from arg list */ ++ formatAttr.fldWidth = (int)va_arg(argList, int); ++ formatAttr.dynWidth = 1; ++ } ++ if (SecDecodeWidth(ch, &formatAttr, lastState) != 0) { ++ return -1; ++ } ++ break; ++ case STAT_DOT: ++ formatAttr.precision = 0; ++ break; ++ case STAT_PRECIS: ++ /* Update precision value */ ++ if (ch == SECUREC_CHAR('*')) { ++ /* Get precision from arg list */ ++ formatAttr.precision = (int)va_arg(argList, int); ++ formatAttr.dynPrecision = 1; ++ } ++ if (SecDecodePrecision(ch, &formatAttr) != 0) { ++ return -1; ++ } ++ break; ++ case STAT_SIZE: ++ /* Read a size specifier, set the formatAttr.flags based on it, and skip format to next character */ ++ if (SecDecodeSize(ch, &formatAttr, &format) != 0) { ++ /* Compatibility code for "%I" just print I */ ++ SecWriteChar(stream, ch, &charsOut); ++ state = STAT_NORMAL; ++ continue; ++ } ++ break; ++ case STAT_TYPE: ++ switch (ch) { ++ case SECUREC_CHAR('C'): /* Wide char */ ++ SecUpdateWcharFlags(&formatAttr); ++ /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('c'): { ++ unsigned int cValue = (unsigned int)va_arg(argList, int); ++ SecDecodeTypeC(&formatAttr, cValue); ++ break; ++ } ++ case SECUREC_CHAR('S'): /* Wide char string */ ++ SecUpdateWstringFlags(&formatAttr); ++ /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('s'): { ++ char *argPtr = (char *)va_arg(argList, char *); ++ SecDecodeTypeS(&formatAttr, argPtr); ++ break; ++ } ++ case SECUREC_CHAR('G'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('g'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('E'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('F'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('e'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('f'): { ++#if SECUREC_ENABLE_SPRINTF_FLOAT ++ /* Add following code to call system sprintf API for float number */ ++ SecFloatAdapt floatAdapt; ++ noOutput = 1; /* It's no more data needs to be written */ ++ ++ /* Now format is pointer to the next character of 'f' */ ++ if (SecInitFloatBuffer(&floatAdapt, format, &formatAttr) != 0) { ++ break; ++ } ++ ++ if ((formatAttr.flags & SECUREC_FLAG_LONG_DOUBLE) != 0) { ++#if defined(SECUREC_COMPATIBLE_LINUX_FORMAT) && SECUREC_ENABLE_SPRINTF_LONG_DOUBLE ++ long double tmp = (long double)va_arg(argList, long double); ++ SecFormatLongDouble(&formatAttr, &floatAdapt, tmp); ++#else ++ double tmp = (double)va_arg(argList, double); ++ SecFormatDouble(&formatAttr, &floatAdapt, tmp); ++#endif ++ } else { ++ double tmp = (double)va_arg(argList, double); ++ SecFormatDouble(&formatAttr, &floatAdapt, tmp); ++ } ++ ++ /* Only need write formatted float string */ ++ SecWriteFloatText(stream, &formatAttr, &charsOut); ++ SecFreeFloatBuffer(&floatAdapt); ++ break; ++#else ++ return -1; ++#endif ++ } ++ case SECUREC_CHAR('X'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('p'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('x'): /* fall-through */ /* FALLTHRU */ ++ SecUpdateXpxFlags(&formatAttr, ch); ++ /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('i'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('d'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('u'): /* fall-through */ /* FALLTHRU */ ++ case SECUREC_CHAR('o'): { ++ SecInt64 num64; ++ SecUpdateOudiFlags(&formatAttr, ch); ++ /* Read argument into variable num64. Be careful, depend on the order of judgment */ ++ if ((formatAttr.flags & SECUREC_FLAG_I64) != 0 || ++ (formatAttr.flags & SECUREC_FLAG_LONGLONG) != 0) { ++ num64 = (SecInt64)va_arg(argList, SecInt64); /* Maximum Bit Width sign bit unchanged */ ++ } else if ((formatAttr.flags & SECUREC_FLAG_LONG) != 0) { ++ num64 = SECUREC_GET_LONG_FROM_ARG(formatAttr); ++ } else if ((formatAttr.flags & SECUREC_FLAG_CHAR) != 0) { ++ num64 = SECUREC_GET_CHAR_FROM_ARG(formatAttr); ++ } else if ((formatAttr.flags & SECUREC_FLAG_SHORT) != 0) { ++ num64 = SECUREC_GET_SHORT_FROM_ARG(formatAttr); ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++ } else if ((formatAttr.flags & SECUREC_FLAG_PTRDIFF) != 0) { ++ num64 = (ptrdiff_t)va_arg(argList, ptrdiff_t); /* Sign extend */ ++ } else if ((formatAttr.flags & SECUREC_FLAG_SIZE) != 0) { ++ num64 = SECUREC_GET_SIZE_FROM_ARG(formatAttr); ++ } else if ((formatAttr.flags & SECUREC_FLAG_INTMAX) != 0) { ++ num64 = (SecInt64)va_arg(argList, SecInt64); ++#endif ++ } else { ++ num64 = SECUREC_GET_INT_FROM_ARG(formatAttr); ++ } ++ ++ /* The order of the following calls must be correct */ ++ SecNumberToBuffer(&formatAttr, num64); ++ SecNumberSatisfyPrecision(&formatAttr); ++ SecNumberForceOctal(&formatAttr); ++ SecUpdateSignedNumberPrefix(&formatAttr); ++ if (num64 == 0) { ++ SecNumberCompatZero(&formatAttr); ++ } ++ break; ++ } ++ default: ++ /* Do nothing */ ++ break; ++ } ++ ++ if (noOutput == 0) { ++ /* Calculate amount of padding */ ++ formatAttr.padding = (formatAttr.fldWidth - formatAttr.textLen) - formatAttr.prefixLen; ++ ++ /* Put out the padding, prefix, and text, in the correct order */ ++ SecWriteLeftPadding(stream, &formatAttr, &charsOut); ++ SecWritePrefix(stream, &formatAttr, &charsOut); ++ SecWriteLeadingZero(stream, &formatAttr, &charsOut); ++ SecWriteText(stream, &formatAttr, &charsOut); ++ SecWriteRightPadding(stream, &formatAttr, &charsOut); ++ } ++ break; ++ case STAT_INVALID: /* fall-through */ /* FALLTHRU */ ++ default: ++ return -1; /* Input format is wrong(STAT_INVALID), directly return */ ++ } ++ } ++ ++ if (state != STAT_NORMAL && state != STAT_TYPE) { ++ return -1; ++ } ++ ++ return charsOut; /* The number of characters written */ ++} ++ ++/* ++ * Output one zero character zero into the SecPrintfStream structure ++ * If there is not enough space, make sure f->count is less than 0 ++ */ ++SECUREC_INLINE int SecPutZeroChar(SecPrintfStream *stream) ++{ ++ --stream->count; ++ if (stream->count >= 0) { ++ *(stream->cur) = SECUREC_CHAR('\0'); ++ ++stream->cur; ++ return 0; ++ } ++ return -1; ++} ++ ++/* ++ * Multi character formatted output implementation ++ */ ++#ifdef SECUREC_FOR_WCHAR ++int SecVswprintfImpl(wchar_t *string, size_t count, const wchar_t *format, va_list argList) ++#else ++int SecVsnprintfImpl(char *string, size_t count, const char *format, va_list argList) ++#endif ++{ ++ SecPrintfStream stream; ++ int retVal; ++ ++ stream.count = (int)count; /* The count include \0 character, must be greater than zero */ ++ stream.cur = string; ++ ++ retVal = SecOutput(&stream, format, argList); ++ if (retVal >= 0) { ++ if (SecPutZeroChar(&stream) == 0) { ++ return retVal; ++ } ++ } ++ if (stream.count < 0) { ++ /* The buffer was too small, then truncate */ ++ string[count - 1] = SECUREC_CHAR('\0'); ++ return SECUREC_PRINTF_TRUNCATE; ++ } ++ string[0] = SECUREC_CHAR('\0'); /* Empty the dest string */ ++ return -1; ++} ++#endif /* OUTPUT_INL_2B263E9C_43D8_44BB_B17A_6D2033DECEE5 */ ++ +diff --git a/securec/src/scanf_s.c b/securec/src/scanf_s.c +new file mode 100644 +index 0000000..fa5470b +--- /dev/null ++++ b/securec/src/scanf_s.c +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: scanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * The scanf_s function is equivalent to fscanf_s with the argument stdin interposed before the arguments to scanf_s ++ * The scanf_s function reads data from the standard input stream stdin and ++ * writes the data into the location that's given by argument. Each argument ++ * must be a pointer to a variable of a type that corresponds to a type specifier ++ * in format. If copying occurs between strings that overlap, the behavior is ++ * undefined. ++ * ++ * ++ * format Format control string. ++ * ... Optional arguments. ++ * ++ * ++ * ... The converted value stored in user assigned address ++ * ++ * ++ * Returns the number of fields successfully converted and assigned; ++ * the return value does not include fields that were read but not assigned. ++ * A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int scanf_s(const char *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vscanf_s(format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++ +diff --git a/securec/src/secinput.h b/securec/src/secinput.h +new file mode 100644 +index 0000000..0782456 +--- /dev/null ++++ b/securec/src/secinput.h +@@ -0,0 +1,181 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: Define macro, data struct, and declare function prototype, ++ * which is used by input.inl, secureinput_a.c and secureinput_w.c. ++ * Create: 2014-02-25 ++ */ ++ ++#ifndef SEC_INPUT_H_E950DA2C_902F_4B15_BECD_948E99090D9C ++#define SEC_INPUT_H_E950DA2C_902F_4B15_BECD_948E99090D9C ++#include "securecutil.h" ++ ++#define SECUREC_SCANF_EINVAL (-1) ++#define SECUREC_SCANF_ERROR_PARA (-2) ++ ++/* For internal stream flag */ ++#define SECUREC_MEM_STR_FLAG 0x01U ++#define SECUREC_FILE_STREAM_FLAG 0x02U ++#define SECUREC_PIPE_STREAM_FLAG 0x04U ++#define SECUREC_LOAD_FILE_TO_MEM_FLAG 0x08U ++ ++#define SECUREC_UCS_BOM_HEADER_SIZE 2U ++#define SECUREC_UCS_BOM_HEADER_BE_1ST 0xfeU ++#define SECUREC_UCS_BOM_HEADER_BE_2ST 0xffU ++#define SECUREC_UCS_BOM_HEADER_LE_1ST 0xffU ++#define SECUREC_UCS_BOM_HEADER_LE_2ST 0xfeU ++#define SECUREC_UTF8_BOM_HEADER_SIZE 3U ++#define SECUREC_UTF8_BOM_HEADER_1ST 0xefU ++#define SECUREC_UTF8_BOM_HEADER_2ND 0xbbU ++#define SECUREC_UTF8_BOM_HEADER_3RD 0xbfU ++#define SECUREC_UTF8_LEAD_1ST 0xe0U ++#define SECUREC_UTF8_LEAD_2ND 0x80U ++ ++#define SECUREC_BEGIN_WITH_UCS_BOM(s, len) ((len) == SECUREC_UCS_BOM_HEADER_SIZE && \ ++ (((unsigned char)((s)[0]) == SECUREC_UCS_BOM_HEADER_LE_1ST && \ ++ (unsigned char)((s)[1]) == SECUREC_UCS_BOM_HEADER_LE_2ST) || \ ++ ((unsigned char)((s)[0]) == SECUREC_UCS_BOM_HEADER_BE_1ST && \ ++ (unsigned char)((s)[1]) == SECUREC_UCS_BOM_HEADER_BE_2ST))) ++ ++#define SECUREC_BEGIN_WITH_UTF8_BOM(s, len) ((len) == SECUREC_UTF8_BOM_HEADER_SIZE && \ ++ (unsigned char)((s)[0]) == SECUREC_UTF8_BOM_HEADER_1ST && \ ++ (unsigned char)((s)[1]) == SECUREC_UTF8_BOM_HEADER_2ND && \ ++ (unsigned char)((s)[2]) == SECUREC_UTF8_BOM_HEADER_3RD) ++ ++#ifdef SECUREC_FOR_WCHAR ++#define SECUREC_BOM_HEADER_SIZE SECUREC_UCS_BOM_HEADER_SIZE ++#define SECUREC_BEGIN_WITH_BOM(s, len) SECUREC_BEGIN_WITH_UCS_BOM((s), (len)) ++#else ++#define SECUREC_BOM_HEADER_SIZE SECUREC_UTF8_BOM_HEADER_SIZE ++#define SECUREC_BEGIN_WITH_BOM(s, len) SECUREC_BEGIN_WITH_UTF8_BOM((s), (len)) ++#endif ++ ++typedef struct { ++ unsigned int flag; /* Mark the properties of input stream */ ++ char *base; /* The pointer to the header of buffered string */ ++ const char *cur; /* The pointer to next read position */ ++ size_t count; /* The size of buffered string in bytes */ ++#if SECUREC_ENABLE_SCANF_FILE ++ FILE *pf; /* The file pointer */ ++ size_t fileRealRead; ++ long oriFilePos; /* The original position of file offset when fscanf is called */ ++#endif ++} SecFileStream; ++ ++#if SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_FILE_STREAM_INIT_FILE(stream, fp) do { \ ++ (stream)->pf = (fp); \ ++ (stream)->fileRealRead = 0; \ ++ (stream)->oriFilePos = 0; \ ++} SECUREC_WHILE_ZERO ++#else ++/* Disable file */ ++#define SECUREC_FILE_STREAM_INIT_FILE(stream, fp) ++#endif ++ ++/* This initialization for eliminating redundant initialization. */ ++#define SECUREC_FILE_STREAM_FROM_STRING(stream, buf, cnt) do { \ ++ (stream)->flag = SECUREC_MEM_STR_FLAG; \ ++ (stream)->base = NULL; \ ++ (stream)->cur = (buf); \ ++ (stream)->count = (cnt); \ ++ SECUREC_FILE_STREAM_INIT_FILE((stream), NULL); \ ++} SECUREC_WHILE_ZERO ++ ++/* This initialization for eliminating redundant initialization. */ ++#define SECUREC_FILE_STREAM_FROM_FILE(stream, fp) do { \ ++ (stream)->flag = SECUREC_FILE_STREAM_FLAG; \ ++ (stream)->base = NULL; \ ++ (stream)->cur = NULL; \ ++ (stream)->count = 0; \ ++ SECUREC_FILE_STREAM_INIT_FILE((stream), (fp)); \ ++} SECUREC_WHILE_ZERO ++ ++/* This initialization for eliminating redundant initialization. */ ++#define SECUREC_FILE_STREAM_FROM_STDIN(stream) do { \ ++ (stream)->flag = SECUREC_PIPE_STREAM_FLAG; \ ++ (stream)->base = NULL; \ ++ (stream)->cur = NULL; \ ++ (stream)->count = 0; \ ++ SECUREC_FILE_STREAM_INIT_FILE((stream), SECUREC_STREAM_STDIN); \ ++} SECUREC_WHILE_ZERO ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++int SecInputS(SecFileStream *stream, const char *cFormat, va_list argList); ++void SecClearDestBuf(const char *buffer, const char *format, va_list argList); ++#ifdef SECUREC_FOR_WCHAR ++int SecInputSW(SecFileStream *stream, const wchar_t *cFormat, va_list argList); ++void SecClearDestBufW(const wchar_t *buffer, const wchar_t *format, va_list argList); ++#endif ++ ++/* 20150105 For software and hardware decoupling,such as UMG */ ++#ifdef SECUREC_SYSAPI4VXWORKS ++#ifdef feof ++#undef feof ++#endif ++extern int feof(FILE *stream); ++#endif ++ ++#if 1//defined(SECUREC_SYSAPI4VXWORKS) || defined(SECUREC_CTYPE_MACRO_ADAPT) ++#ifndef isspace ++#define isspace(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\r') || ((c) == '\n')) ++#endif ++#ifndef iswspace ++#define iswspace(c) (((c) == L' ') || ((c) == L'\t') || ((c) == L'\r') || ((c) == L'\n')) ++#endif ++#ifndef isascii ++#define isascii(c) (((unsigned char)(c)) <= 0x7f) ++#endif ++#ifndef isupper ++#define isupper(c) ((c) >= 'A' && (c) <= 'Z') ++#endif ++#ifndef islower ++#define islower(c) ((c) >= 'a' && (c) <= 'z') ++#endif ++#ifndef isalpha ++#define isalpha(c) (isupper(c) || (islower(c))) ++#endif ++#ifndef isdigit ++#define isdigit(c) ((c) >= '0' && (c) <= '9') ++#endif ++#ifndef isxupper ++#define isxupper(c) ((c) >= 'A' && (c) <= 'F') ++#endif ++#ifndef isxlower ++#define isxlower(c) ((c) >= 'a' && (c) <= 'f') ++#endif ++#ifndef isxdigit ++#define isxdigit(c) (isdigit(c) || isxupper(c) || isxlower(c)) ++#endif ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++/* Reserved file operation macro interface, s is FILE *, i is fileno zero. */ ++#ifndef SECUREC_LOCK_FILE ++#define SECUREC_LOCK_FILE(s) ++#endif ++ ++#ifndef SECUREC_UNLOCK_FILE ++#define SECUREC_UNLOCK_FILE(s) ++#endif ++ ++#ifndef SECUREC_LOCK_STDIN ++#define SECUREC_LOCK_STDIN(i, s) ++#endif ++ ++#ifndef SECUREC_UNLOCK_STDIN ++#define SECUREC_UNLOCK_STDIN(i, s) ++#endif ++#endif ++ +diff --git a/securec/src/securecutil.c b/securec/src/securecutil.c +new file mode 100644 +index 0000000..7518eb3 +--- /dev/null ++++ b/securec/src/securecutil.c +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: Provides internal functions used by this library, such as memory ++ * copy and memory move. Besides, include some helper function for ++ * printf family API, such as SecVsnprintfImpl ++ * Create: 2014-02-25 ++ */ ++ ++/* Avoid duplicate header files,not include securecutil.h */ ++#include "securecutil.h" ++ ++#if defined(ANDROID) && !defined(SECUREC_CLOSE_ANDROID_HANDLE) && (SECUREC_HAVE_WCTOMB || SECUREC_HAVE_MBTOWC) ++#include ++#if SECUREC_HAVE_WCTOMB ++/* ++ * Convert wide characters to narrow multi-bytes ++ */ ++int wctomb(char *s, wchar_t wc) ++{ ++ return (int)wcrtomb(s, wc, NULL); ++} ++#endif ++ ++#if SECUREC_HAVE_MBTOWC ++/* ++ * Converting narrow multi-byte characters to wide characters ++ * mbrtowc returns -1 or -2 upon failure, unlike mbtowc, which only returns -1 ++ * When the return value is less than zero, we treat it as a failure ++ */ ++int mbtowc(wchar_t *pwc, const char *s, size_t n) ++{ ++ return (int)mbrtowc(pwc, s, n, NULL); ++} ++#endif ++#endif ++ ++/* The V100R001C01 version num is 0x5 (High 8 bits) */ ++#define SECUREC_C_VERSION 0x500U ++#define SECUREC_SPC_VERSION 0xbU ++#define SECUREC_VERSION_STR "V100R001C01SPC011B003" ++ ++/* ++ * Get version string and version number. ++ * The rules for version number are as follows: ++ * 1) SPC verNumber<->verStr like: ++ * 0x201<->C01 ++ * 0x202<->C01SPC001 Redefine numbers after this version ++ * 0x502<->C01SPC002 ++ * 0x503<->C01SPC003 ++ * ... ++ * 0X50a<->SPC010 ++ * 0X50b<->SPC011 ++ * ... ++ * 0x700<->C02 ++ * 0x701<->C01SPC001 ++ * 0x702<->C02SPC002 ++ * ... ++ * 2) CP verNumber<->verStr like: ++ * 0X601<->CP0001 ++ * 0X602<->CP0002 ++ * ... ++ */ ++const char *GetHwSecureCVersion(unsigned short *verNumber) ++{ ++ if (verNumber != NULL) { ++ *verNumber = (unsigned short)(SECUREC_C_VERSION | SECUREC_SPC_VERSION); ++ } ++ return SECUREC_VERSION_STR; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(GetHwSecureCVersion); ++#endif ++ +diff --git a/securec/src/securecutil.h b/securec/src/securecutil.h +new file mode 100644 +index 0000000..2626a45 +--- /dev/null ++++ b/securec/src/securecutil.h +@@ -0,0 +1,574 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: Define macro, data struct, and declare internal used function prototype, ++ * which is used by secure functions. ++ * Create: 2014-02-25 ++ */ ++ ++#ifndef SECURECUTIL_H_46C86578_F8FF_4E49_8E64_9B175241761F ++#define SECURECUTIL_H_46C86578_F8FF_4E49_8E64_9B175241761F ++#include "securec.h" ++ ++#if (defined(_MSC_VER)) && (_MSC_VER >= 1400) ++/* Shield compilation alerts using discarded functions and Constant expression to maximize code compatibility */ ++#define SECUREC_MASK_MSVC_CRT_WARNING __pragma(warning(push)) \ ++ __pragma(warning(disable : 4996 4127)) ++#define SECUREC_END_MASK_MSVC_CRT_WARNING __pragma(warning(pop)) ++#else ++#define SECUREC_MASK_MSVC_CRT_WARNING ++#define SECUREC_END_MASK_MSVC_CRT_WARNING ++#endif ++#define SECUREC_WHILE_ZERO SECUREC_MASK_MSVC_CRT_WARNING while (0) SECUREC_END_MASK_MSVC_CRT_WARNING ++ ++/* Automatically identify the platform that supports strnlen function, and use this function to improve performance */ ++#ifndef SECUREC_HAVE_STRNLEN ++#if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) ++#if SECUREC_IN_KERNEL ++#define SECUREC_HAVE_STRNLEN 0 ++#else ++#if defined(__GLIBC__) && __GLIBC__ >= 2 && defined(__GLIBC_MINOR__) && __GLIBC_MINOR__ >= 10 ++#define SECUREC_HAVE_STRNLEN 1 ++#else ++#define SECUREC_HAVE_STRNLEN 0 ++#endif ++#endif ++#else ++#define SECUREC_HAVE_STRNLEN 0 ++#endif ++#endif ++ ++#if SECUREC_IN_KERNEL ++/* In kernel disable functions */ ++#ifndef SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_SCANF_FILE 0 ++#endif ++#ifndef SECUREC_ENABLE_SCANF_FLOAT ++#define SECUREC_ENABLE_SCANF_FLOAT 0 ++#endif ++#ifndef SECUREC_ENABLE_SPRINTF_FLOAT ++#define SECUREC_ENABLE_SPRINTF_FLOAT 0 ++#endif ++#ifndef SECUREC_HAVE_MBTOWC ++#define SECUREC_HAVE_MBTOWC 0 ++#endif ++#ifndef SECUREC_HAVE_WCTOMB ++#define SECUREC_HAVE_WCTOMB 0 ++#endif ++#ifndef SECUREC_HAVE_WCHART ++#define SECUREC_HAVE_WCHART 0 ++#endif ++#else /* Not in kernel */ ++/* Systems that do not support file, can define this macro to 0. */ ++#ifndef SECUREC_ENABLE_SCANF_FILE ++#define SECUREC_ENABLE_SCANF_FILE 1 ++#endif ++#ifndef SECUREC_ENABLE_SCANF_FLOAT ++#define SECUREC_ENABLE_SCANF_FLOAT 0 ++#endif ++/* Systems that do not support float, can define this macro to 0. */ ++#ifndef SECUREC_ENABLE_SPRINTF_FLOAT ++#define SECUREC_ENABLE_SPRINTF_FLOAT 1 ++#endif ++#ifndef SECUREC_HAVE_MBTOWC ++#define SECUREC_HAVE_MBTOWC 0 ++#endif ++#ifndef SECUREC_HAVE_WCTOMB ++#define SECUREC_HAVE_WCTOMB 0 ++#endif ++#ifndef SECUREC_HAVE_WCHART ++#define SECUREC_HAVE_WCHART 1 ++#endif ++#endif ++ ++#ifndef SECUREC_ENABLE_INLINE ++#define SECUREC_ENABLE_INLINE 0 ++#endif ++ ++#ifndef SECUREC_INLINE ++#if SECUREC_ENABLE_INLINE ++#define SECUREC_INLINE static inline ++#else ++#define SECUREC_INLINE static ++#endif ++#endif ++ ++#ifndef SECUREC_WARP_OUTPUT ++#if SECUREC_IN_KERNEL ++#define SECUREC_WARP_OUTPUT 1 ++#else ++#define SECUREC_WARP_OUTPUT 0 ++#endif ++#endif ++ ++#ifndef SECUREC_STREAM_STDIN ++#define SECUREC_STREAM_STDIN stdin ++#endif ++ ++#define SECUREC_MUL_SIXTEEN(x) ((x) << 4U) ++#define SECUREC_MUL_EIGHT(x) ((x) << 3U) ++#define SECUREC_MUL_TEN(x) ((((x) << 2U) + (x)) << 1U) ++/* Limited format input and output width, use signed integer */ ++#define SECUREC_MAX_WIDTH_LEN_DIV_TEN 21474836 ++#define SECUREC_MAX_WIDTH_LEN (SECUREC_MAX_WIDTH_LEN_DIV_TEN * 10) ++/* Is the x multiplied by 10 greater than */ ++#define SECUREC_MUL_TEN_ADD_BEYOND_MAX(x) (((x) > SECUREC_MAX_WIDTH_LEN_DIV_TEN)) ++ ++#define SECUREC_FLOAT_BUFSIZE (309 + 40) /* Max length of double value */ ++#define SECUREC_FLOAT_BUFSIZE_LB (4932 + 40) /* Max length of long double value */ ++#define SECUREC_FLOAT_DEFAULT_PRECISION 6 ++ ++/* This macro does not handle pointer equality or integer overflow */ ++#define SECUREC_MEMORY_NO_OVERLAP(dest, src, count) \ ++ (((src) < (dest) && ((const char *)(src) + (count)) <= (char *)(dest)) || \ ++ ((dest) < (src) && ((char *)(dest) + (count)) <= (const char *)(src))) ++ ++#define SECUREC_MEMORY_IS_OVERLAP(dest, src, count) \ ++ (((src) < (dest) && ((const char *)(src) + (count)) > (char *)(dest)) || \ ++ ((dest) < (src) && ((char *)(dest) + (count)) > (const char *)(src))) ++ ++/* ++ * Check whether the strings overlap, len is the length of the string not include terminator ++ * Length is related to data type char or wchar , do not force conversion of types ++ */ ++#define SECUREC_STRING_NO_OVERLAP(dest, src, len) \ ++ (((src) < (dest) && ((src) + (len)) < (dest)) || \ ++ ((dest) < (src) && ((dest) + (len)) < (src))) ++ ++/* ++ * Check whether the strings overlap for strcpy wcscpy function, dest len and src Len are not include terminator ++ * Length is related to data type char or wchar , do not force conversion of types ++ */ ++#define SECUREC_STRING_IS_OVERLAP(dest, src, len) \ ++ (((src) < (dest) && ((src) + (len)) >= (dest)) || \ ++ ((dest) < (src) && ((dest) + (len)) >= (src))) ++ ++/* ++ * Check whether the strings overlap for strcat wcscat function, dest len and src Len are not include terminator ++ * Length is related to data type char or wchar , do not force conversion of types ++ */ ++#define SECUREC_CAT_STRING_IS_OVERLAP(dest, destLen, src, srcLen) \ ++ (((dest) < (src) && ((dest) + (destLen) + (srcLen)) >= (src)) || \ ++ ((src) < (dest) && ((src) + (srcLen)) >= (dest))) ++ ++#if SECUREC_HAVE_STRNLEN ++#define SECUREC_CALC_STR_LEN(str, maxLen, outLen) do { \ ++ *(outLen) = strnlen((str), (maxLen)); \ ++} SECUREC_WHILE_ZERO ++#define SECUREC_CALC_STR_LEN_OPT(str, maxLen, outLen) do { \ ++ if ((maxLen) > 8) { \ ++ /* Optimization or len less then 8 */ \ ++ if (*((str) + 0) == '\0') { \ ++ *(outLen) = 0; \ ++ } else if (*((str) + 1) == '\0') { \ ++ *(outLen) = 1; \ ++ } else if (*((str) + 2) == '\0') { \ ++ *(outLen) = 2; \ ++ } else if (*((str) + 3) == '\0') { \ ++ *(outLen) = 3; \ ++ } else if (*((str) + 4) == '\0') { \ ++ *(outLen) = 4; \ ++ } else if (*((str) + 5) == '\0') { \ ++ *(outLen) = 5; \ ++ } else if (*((str) + 6) == '\0') { \ ++ *(outLen) = 6; \ ++ } else if (*((str) + 7) == '\0') { \ ++ *(outLen) = 7; \ ++ } else if (*((str) + 8) == '\0') { \ ++ /* Optimization with a length of 8 */ \ ++ *(outLen) = 8; \ ++ } else { \ ++ /* The offset is 8 because the performance of 8 byte alignment is high */ \ ++ *(outLen) = 8 + strnlen((str) + 8, (maxLen) - 8); \ ++ } \ ++ } else { \ ++ SECUREC_CALC_STR_LEN((str), (maxLen), (outLen)); \ ++ } \ ++} SECUREC_WHILE_ZERO ++#else ++#define SECUREC_CALC_STR_LEN(str, maxLen, outLen) do { \ ++ const char *strEnd_ = (const char *)(str); \ ++ size_t availableSize_ = (size_t)(maxLen); \ ++ while (availableSize_ > 0 && *strEnd_ != '\0') { \ ++ --availableSize_; \ ++ ++strEnd_; \ ++ } \ ++ *(outLen) = (size_t)(strEnd_ - (str)); \ ++} SECUREC_WHILE_ZERO ++#define SECUREC_CALC_STR_LEN_OPT SECUREC_CALC_STR_LEN ++#endif ++ ++#define SECUREC_CALC_WSTR_LEN(str, maxLen, outLen) do { \ ++ const wchar_t *strEnd_ = (const wchar_t *)(str); \ ++ size_t len_ = 0; \ ++ while (len_ < (maxLen) && *strEnd_ != L'\0') { \ ++ ++len_; \ ++ ++strEnd_; \ ++ } \ ++ *(outLen) = len_; \ ++} SECUREC_WHILE_ZERO ++ ++/* ++ * Performance optimization, product may disable inline function. ++ * Using function pointer for MEMSET to prevent compiler optimization when cleaning up memory. ++ */ ++#ifdef SECUREC_USE_ASM ++#define SECUREC_MEMSET_FUNC_OPT memset_opt ++#define SECUREC_MEMCPY_FUNC_OPT memcpy_opt ++#else ++#define SECUREC_MEMSET_FUNC_OPT memset ++#define SECUREC_MEMCPY_FUNC_OPT memcpy ++#endif ++ ++#define SECUREC_MEMCPY_WARP_OPT(dest, src, count) (void)SECUREC_MEMCPY_FUNC_OPT((dest), (src), (count)) ++ ++#ifndef SECUREC_MEMSET_BARRIER ++#if defined(__GNUC__) ++/* Can be turned off for scenarios that do not use memory barrier */ ++#define SECUREC_MEMSET_BARRIER 1 ++#else ++#define SECUREC_MEMSET_BARRIER 0 ++#endif ++#endif ++ ++#ifndef SECUREC_MEMSET_INDIRECT_USE ++/* Can be turned off for scenarios that do not allow pointer calls */ ++#define SECUREC_MEMSET_INDIRECT_USE 1 ++#endif ++ ++#if SECUREC_MEMSET_BARRIER ++#define SECUREC_MEMORY_BARRIER(dest) __asm__ __volatile__("": : "r"(dest) : "memory") ++#else ++#define SECUREC_MEMORY_BARRIER(dest) ++#endif ++ ++#if SECUREC_MEMSET_BARRIER ++#define SECUREC_MEMSET_PREVENT_DSE(dest, value, count) do { \ ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, value, count); \ ++ SECUREC_MEMORY_BARRIER(dest); \ ++} SECUREC_WHILE_ZERO ++#elif SECUREC_MEMSET_INDIRECT_USE ++#define SECUREC_MEMSET_PREVENT_DSE(dest, value, count) do { \ ++ void *(* const volatile fn_)(void *s_, int c_, size_t n_) = SECUREC_MEMSET_FUNC_OPT; \ ++ (void)(*fn_)((dest), (value), (count)); \ ++} SECUREC_WHILE_ZERO ++#else ++#define SECUREC_MEMSET_PREVENT_DSE(dest, value, count) (void)SECUREC_MEMSET_FUNC_OPT((dest), (value), (count)) ++#endif ++ ++#ifdef SECUREC_FORMAT_OUTPUT_INPUT ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) || defined(__ARMCC_VERSION) ++typedef __int64 SecInt64; ++typedef unsigned __int64 SecUnsignedInt64; ++#if defined(__ARMCC_VERSION) ++typedef unsigned int SecUnsignedInt32; ++#else ++typedef unsigned __int32 SecUnsignedInt32; ++#endif ++#else ++typedef unsigned int SecUnsignedInt32; ++typedef long long SecInt64; ++typedef unsigned long long SecUnsignedInt64; ++#endif ++ ++#ifdef SECUREC_FOR_WCHAR ++#if 1//defined(SECUREC_VXWORKS_PLATFORM) && !defined(__WINT_TYPE__) ++typedef wchar_t wint_t; ++#endif ++#ifndef WEOF ++#define WEOF ((wchar_t)(-1)) ++#endif ++#define SECUREC_CHAR(x) L ## x ++typedef wchar_t SecChar; ++typedef wchar_t SecUnsignedChar; ++typedef wint_t SecInt; ++typedef wint_t SecUnsignedInt; ++#else /* no SECUREC_FOR_WCHAR */ ++#define SECUREC_CHAR(x) (x) ++typedef char SecChar; ++typedef unsigned char SecUnsignedChar; ++typedef int SecInt; ++typedef unsigned int SecUnsignedInt; ++#endif ++#endif ++ ++/* ++ * Determine whether the address is 8-byte aligned ++ * Some systems do not have uintptr_t type, so use NULL to clear tool alarm 507 ++ */ ++#define SECUREC_ADDR_ALIGNED_8(addr) ((((size_t)(addr)) & 7U) == 0) /* Use 7 to check aligned 8 */ ++ ++/* ++ * If you define the memory allocation function, you need to define the function prototype. ++ * You can define this macro as a header file. ++ */ ++#if defined(SECUREC_MALLOC_PROTOTYPE) ++SECUREC_MALLOC_PROTOTYPE ++#endif ++ ++#ifndef SECUREC_MALLOC ++#define SECUREC_MALLOC(x) malloc((size_t)(x)) ++#endif ++ ++#ifndef SECUREC_FREE ++#define SECUREC_FREE(x) free((void *)(x)) ++#endif ++ ++/* Improve performance with struct assignment, buf1 is not defined to avoid tool false positive */ ++#define SECUREC_COPY_VALUE_BY_STRUCT(dest, src, n) do { \ ++ *(SecStrBuf##n *)(void *)(dest) = *(const SecStrBuf##n *)(const void *)(src); \ ++} SECUREC_WHILE_ZERO ++ ++typedef struct { ++ unsigned char buf[2]; /* Performance optimization code structure assignment length 2 bytes */ ++} SecStrBuf2; ++typedef struct { ++ unsigned char buf[3]; /* Performance optimization code structure assignment length 3 bytes */ ++} SecStrBuf3; ++typedef struct { ++ unsigned char buf[4]; /* Performance optimization code structure assignment length 4 bytes */ ++} SecStrBuf4; ++typedef struct { ++ unsigned char buf[5]; /* Performance optimization code structure assignment length 5 bytes */ ++} SecStrBuf5; ++typedef struct { ++ unsigned char buf[6]; /* Performance optimization code structure assignment length 6 bytes */ ++} SecStrBuf6; ++typedef struct { ++ unsigned char buf[7]; /* Performance optimization code structure assignment length 7 bytes */ ++} SecStrBuf7; ++typedef struct { ++ unsigned char buf[8]; /* Performance optimization code structure assignment length 8 bytes */ ++} SecStrBuf8; ++typedef struct { ++ unsigned char buf[9]; /* Performance optimization code structure assignment length 9 bytes */ ++} SecStrBuf9; ++typedef struct { ++ unsigned char buf[10]; /* Performance optimization code structure assignment length 10 bytes */ ++} SecStrBuf10; ++typedef struct { ++ unsigned char buf[11]; /* Performance optimization code structure assignment length 11 bytes */ ++} SecStrBuf11; ++typedef struct { ++ unsigned char buf[12]; /* Performance optimization code structure assignment length 12 bytes */ ++} SecStrBuf12; ++typedef struct { ++ unsigned char buf[13]; /* Performance optimization code structure assignment length 13 bytes */ ++} SecStrBuf13; ++typedef struct { ++ unsigned char buf[14]; /* Performance optimization code structure assignment length 14 bytes */ ++} SecStrBuf14; ++typedef struct { ++ unsigned char buf[15]; /* Performance optimization code structure assignment length 15 bytes */ ++} SecStrBuf15; ++typedef struct { ++ unsigned char buf[16]; /* Performance optimization code structure assignment length 16 bytes */ ++} SecStrBuf16; ++typedef struct { ++ unsigned char buf[17]; /* Performance optimization code structure assignment length 17 bytes */ ++} SecStrBuf17; ++typedef struct { ++ unsigned char buf[18]; /* Performance optimization code structure assignment length 18 bytes */ ++} SecStrBuf18; ++typedef struct { ++ unsigned char buf[19]; /* Performance optimization code structure assignment length 19 bytes */ ++} SecStrBuf19; ++typedef struct { ++ unsigned char buf[20]; /* Performance optimization code structure assignment length 20 bytes */ ++} SecStrBuf20; ++typedef struct { ++ unsigned char buf[21]; /* Performance optimization code structure assignment length 21 bytes */ ++} SecStrBuf21; ++typedef struct { ++ unsigned char buf[22]; /* Performance optimization code structure assignment length 22 bytes */ ++} SecStrBuf22; ++typedef struct { ++ unsigned char buf[23]; /* Performance optimization code structure assignment length 23 bytes */ ++} SecStrBuf23; ++typedef struct { ++ unsigned char buf[24]; /* Performance optimization code structure assignment length 24 bytes */ ++} SecStrBuf24; ++typedef struct { ++ unsigned char buf[25]; /* Performance optimization code structure assignment length 25 bytes */ ++} SecStrBuf25; ++typedef struct { ++ unsigned char buf[26]; /* Performance optimization code structure assignment length 26 bytes */ ++} SecStrBuf26; ++typedef struct { ++ unsigned char buf[27]; /* Performance optimization code structure assignment length 27 bytes */ ++} SecStrBuf27; ++typedef struct { ++ unsigned char buf[28]; /* Performance optimization code structure assignment length 28 bytes */ ++} SecStrBuf28; ++typedef struct { ++ unsigned char buf[29]; /* Performance optimization code structure assignment length 29 bytes */ ++} SecStrBuf29; ++typedef struct { ++ unsigned char buf[30]; /* Performance optimization code structure assignment length 30 bytes */ ++} SecStrBuf30; ++typedef struct { ++ unsigned char buf[31]; /* Performance optimization code structure assignment length 31 bytes */ ++} SecStrBuf31; ++typedef struct { ++ unsigned char buf[32]; /* Performance optimization code structure assignment length 32 bytes */ ++} SecStrBuf32; ++typedef struct { ++ unsigned char buf[33]; /* Performance optimization code structure assignment length 33 bytes */ ++} SecStrBuf33; ++typedef struct { ++ unsigned char buf[34]; /* Performance optimization code structure assignment length 34 bytes */ ++} SecStrBuf34; ++typedef struct { ++ unsigned char buf[35]; /* Performance optimization code structure assignment length 35 bytes */ ++} SecStrBuf35; ++typedef struct { ++ unsigned char buf[36]; /* Performance optimization code structure assignment length 36 bytes */ ++} SecStrBuf36; ++typedef struct { ++ unsigned char buf[37]; /* Performance optimization code structure assignment length 37 bytes */ ++} SecStrBuf37; ++typedef struct { ++ unsigned char buf[38]; /* Performance optimization code structure assignment length 38 bytes */ ++} SecStrBuf38; ++typedef struct { ++ unsigned char buf[39]; /* Performance optimization code structure assignment length 39 bytes */ ++} SecStrBuf39; ++typedef struct { ++ unsigned char buf[40]; /* Performance optimization code structure assignment length 40 bytes */ ++} SecStrBuf40; ++typedef struct { ++ unsigned char buf[41]; /* Performance optimization code structure assignment length 41 bytes */ ++} SecStrBuf41; ++typedef struct { ++ unsigned char buf[42]; /* Performance optimization code structure assignment length 42 bytes */ ++} SecStrBuf42; ++typedef struct { ++ unsigned char buf[43]; /* Performance optimization code structure assignment length 43 bytes */ ++} SecStrBuf43; ++typedef struct { ++ unsigned char buf[44]; /* Performance optimization code structure assignment length 44 bytes */ ++} SecStrBuf44; ++typedef struct { ++ unsigned char buf[45]; /* Performance optimization code structure assignment length 45 bytes */ ++} SecStrBuf45; ++typedef struct { ++ unsigned char buf[46]; /* Performance optimization code structure assignment length 46 bytes */ ++} SecStrBuf46; ++typedef struct { ++ unsigned char buf[47]; /* Performance optimization code structure assignment length 47 bytes */ ++} SecStrBuf47; ++typedef struct { ++ unsigned char buf[48]; /* Performance optimization code structure assignment length 48 bytes */ ++} SecStrBuf48; ++typedef struct { ++ unsigned char buf[49]; /* Performance optimization code structure assignment length 49 bytes */ ++} SecStrBuf49; ++typedef struct { ++ unsigned char buf[50]; /* Performance optimization code structure assignment length 50 bytes */ ++} SecStrBuf50; ++typedef struct { ++ unsigned char buf[51]; /* Performance optimization code structure assignment length 51 bytes */ ++} SecStrBuf51; ++typedef struct { ++ unsigned char buf[52]; /* Performance optimization code structure assignment length 52 bytes */ ++} SecStrBuf52; ++typedef struct { ++ unsigned char buf[53]; /* Performance optimization code structure assignment length 53 bytes */ ++} SecStrBuf53; ++typedef struct { ++ unsigned char buf[54]; /* Performance optimization code structure assignment length 54 bytes */ ++} SecStrBuf54; ++typedef struct { ++ unsigned char buf[55]; /* Performance optimization code structure assignment length 55 bytes */ ++} SecStrBuf55; ++typedef struct { ++ unsigned char buf[56]; /* Performance optimization code structure assignment length 56 bytes */ ++} SecStrBuf56; ++typedef struct { ++ unsigned char buf[57]; /* Performance optimization code structure assignment length 57 bytes */ ++} SecStrBuf57; ++typedef struct { ++ unsigned char buf[58]; /* Performance optimization code structure assignment length 58 bytes */ ++} SecStrBuf58; ++typedef struct { ++ unsigned char buf[59]; /* Performance optimization code structure assignment length 59 bytes */ ++} SecStrBuf59; ++typedef struct { ++ unsigned char buf[60]; /* Performance optimization code structure assignment length 60 bytes */ ++} SecStrBuf60; ++typedef struct { ++ unsigned char buf[61]; /* Performance optimization code structure assignment length 61 bytes */ ++} SecStrBuf61; ++typedef struct { ++ unsigned char buf[62]; /* Performance optimization code structure assignment length 62 bytes */ ++} SecStrBuf62; ++typedef struct { ++ unsigned char buf[63]; /* Performance optimization code structure assignment length 63 bytes */ ++} SecStrBuf63; ++typedef struct { ++ unsigned char buf[64]; /* Performance optimization code structure assignment length 64 bytes */ ++} SecStrBuf64; ++ ++/* ++ * User can change the error handler by modify the following definition, ++ * such as logging the detail error in file. ++ */ ++#if defined(_DEBUG) || defined(DEBUG) ++#if defined(SECUREC_ERROR_HANDLER_BY_ASSERT) ++#define SECUREC_ERROR_INVALID_PARAMTER(msg) assert(msg "invalid argument" == NULL) ++#define SECUREC_ERROR_INVALID_RANGE(msg) assert(msg "invalid dest buffer size" == NULL) ++#define SECUREC_ERROR_BUFFER_OVERLAP(msg) assert(msg "buffer overlap" == NULL) ++#elif defined(SECUREC_ERROR_HANDLER_BY_PRINTF) ++#if SECUREC_IN_KERNEL ++#define SECUREC_ERROR_INVALID_PARAMTER(msg) printk("%s invalid argument\n", msg) ++#define SECUREC_ERROR_INVALID_RANGE(msg) printk("%s invalid dest buffer size\n", msg) ++#define SECUREC_ERROR_BUFFER_OVERLAP(msg) printk("%s buffer overlap\n", msg) ++#else ++#define SECUREC_ERROR_INVALID_PARAMTER(msg) printf("%s invalid argument\n", msg) ++#define SECUREC_ERROR_INVALID_RANGE(msg) printf("%s invalid dest buffer size\n", msg) ++#define SECUREC_ERROR_BUFFER_OVERLAP(msg) printf("%s buffer overlap\n", msg) ++#endif ++#elif defined(SECUREC_ERROR_HANDLER_BY_FILE_LOG) ++#define SECUREC_ERROR_INVALID_PARAMTER(msg) LogSecureCRuntimeError(msg " EINVAL\n") ++#define SECUREC_ERROR_INVALID_RANGE(msg) LogSecureCRuntimeError(msg " ERANGE\n") ++#define SECUREC_ERROR_BUFFER_OVERLAP(msg) LogSecureCRuntimeError(msg " EOVERLAP\n") ++#endif ++#endif ++ ++/* Default handler is none */ ++#ifndef SECUREC_ERROR_INVALID_PARAMTER ++#define SECUREC_ERROR_INVALID_PARAMTER(msg) ++#endif ++#ifndef SECUREC_ERROR_INVALID_RANGE ++#define SECUREC_ERROR_INVALID_RANGE(msg) ++#endif ++#ifndef SECUREC_ERROR_BUFFER_OVERLAP ++#define SECUREC_ERROR_BUFFER_OVERLAP(msg) ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* Assembly language memory copy and memory set for X86 or MIPS ... */ ++#ifdef SECUREC_USE_ASM ++void *memcpy_opt(void *dest, const void *src, size_t n); ++void *memset_opt(void *s, int c, size_t n); ++#endif ++ ++#if defined(SECUREC_ERROR_HANDLER_BY_FILE_LOG) ++void LogSecureCRuntimeError(const char *errDetail); ++#endif ++ ++#ifdef __cplusplus ++} ++#endif /* __cplusplus */ ++#endif ++ +diff --git a/securec/src/secureinput_a.c b/securec/src/secureinput_a.c +new file mode 100644 +index 0000000..e79868f +--- /dev/null ++++ b/securec/src/secureinput_a.c +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: By defining data type for ANSI string and including "input.inl", ++ * this file generates real underlying function used by scanf family API. ++ * Create: 2014-02-25 ++ */ ++ ++#define SECUREC_FORMAT_OUTPUT_INPUT 1 ++#ifdef SECUREC_FOR_WCHAR ++#undef SECUREC_FOR_WCHAR ++#endif ++ ++#include "secinput.h" ++ ++#include "input.inl" ++ ++SECUREC_INLINE int SecIsDigit(SecInt ch) ++{ ++ /* SecInt to unsigned char clear 571, use bit mask to clear negative return of ch */ ++ return isdigit((int)((unsigned int)(unsigned char)(ch) & 0xffU)); ++} ++SECUREC_INLINE int SecIsXdigit(SecInt ch) ++{ ++ return isxdigit((int)((unsigned int)(unsigned char)(ch) & 0xffU)); ++} ++SECUREC_INLINE int SecIsSpace(SecInt ch) ++{ ++ return isspace((int)((unsigned int)(unsigned char)(ch) & 0xffU)); ++} ++ +diff --git a/securec/src/secureinput_w.c b/securec/src/secureinput_w.c +new file mode 100644 +index 0000000..04df34c +--- /dev/null ++++ b/securec/src/secureinput_w.c +@@ -0,0 +1,75 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: By defining data type for UNICODE string and including "input.inl", ++ * this file generates real underlying function used by scanf family API. ++ * Create: 2014-02-25 ++ */ ++ ++/* If some platforms don't have wchar.h, don't include it */ ++#if !(defined(SECUREC_VXWORKS_PLATFORM)) ++/* If there is no macro below, it will cause vs2010 compiling alarm */ ++#if defined(_MSC_VER) && (_MSC_VER >= 1400) ++#ifndef __STDC_WANT_SECURE_LIB__ ++/* The order of adjustment is to eliminate alarm of Duplicate Block */ ++#define __STDC_WANT_SECURE_LIB__ 0 ++#endif ++#ifndef _CRTIMP_ALTERNATIVE ++#define _CRTIMP_ALTERNATIVE /* Comment microsoft *_s function */ ++#endif ++#endif ++//#include ++#endif ++ ++/* Disable wchar func to clear vs warning */ ++#define SECUREC_ENABLE_WCHAR_FUNC 0 ++#define SECUREC_FORMAT_OUTPUT_INPUT 1 ++ ++#ifndef SECUREC_FOR_WCHAR ++#define SECUREC_FOR_WCHAR ++#endif ++ ++#include "secinput.h" ++ ++#include "input.inl" ++ ++SECUREC_INLINE unsigned int SecWcharHighBits(SecInt ch) ++{ ++ /* Convert int to unsigned int clear 571 */ ++ return ((unsigned int)(int)ch & (~0xffU)); ++} ++ ++SECUREC_INLINE unsigned char SecWcharLowByte(SecInt ch) ++{ ++ /* Convert int to unsigned int clear 571 */ ++ return (unsigned char)((unsigned int)(int)ch & 0xffU); ++} ++ ++SECUREC_INLINE int SecIsDigit(SecInt ch) ++{ ++ if (SecWcharHighBits(ch) != 0) { ++ return 0; /* Same as isdigit */ ++ } ++ return isdigit((int)SecWcharLowByte(ch)); ++} ++ ++SECUREC_INLINE int SecIsXdigit(SecInt ch) ++{ ++ if (SecWcharHighBits(ch) != 0) { ++ return 0; /* Same as isxdigit */ ++ } ++ return isxdigit((int)SecWcharLowByte(ch)); ++} ++ ++SECUREC_INLINE int SecIsSpace(SecInt ch) ++{ ++ return iswspace((wint_t)(int)(ch)); ++} ++ +diff --git a/securec/src/secureprintoutput.h b/securec/src/secureprintoutput.h +new file mode 100644 +index 0000000..a00b10d +--- /dev/null ++++ b/securec/src/secureprintoutput.h +@@ -0,0 +1,146 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: Define macro, enum, data struct, and declare internal used function ++ * prototype, which is used by output.inl, secureprintoutput_w.c and ++ * secureprintoutput_a.c. ++ * Create: 2014-02-25 ++ */ ++ ++#ifndef SECUREPRINTOUTPUT_H_E950DA2C_902F_4B15_BECD_948E99090D9C ++#define SECUREPRINTOUTPUT_H_E950DA2C_902F_4B15_BECD_948E99090D9C ++#include "securecutil.h" ++ ++/* Shield compilation alerts about using sprintf without format attribute to format float value. */ ++#ifndef SECUREC_HANDLE_WFORMAT ++#define SECUREC_HANDLE_WFORMAT 1 ++#endif ++ ++#if SECUREC_HANDLE_WFORMAT && defined(__GNUC__) && ((__GNUC__ >= 5) || \ ++ (defined(__GNUC_MINOR__) && (__GNUC__ == 4 && __GNUC_MINOR__ > 7))) ++#if defined(__clang__) ++#define SECUREC_MASK_WFORMAT_WARNING _Pragma("GCC diagnostic push") \ ++ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") ++#else ++#define SECUREC_MASK_WFORMAT_WARNING _Pragma("GCC diagnostic push") \ ++ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") \ ++ _Pragma("GCC diagnostic ignored \"-Wmissing-format-attribute\"") \ ++ _Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=format\"") ++#endif ++#define SECUREC_END_MASK_WFORMAT_WARNING _Pragma("GCC diagnostic pop") ++#else ++#define SECUREC_MASK_WFORMAT_WARNING ++#define SECUREC_END_MASK_WFORMAT_WARNING ++#endif ++ ++#define SECUREC_MASK_VSPRINTF_WARNING SECUREC_MASK_WFORMAT_WARNING \ ++ SECUREC_MASK_MSVC_CRT_WARNING ++ ++#define SECUREC_END_MASK_VSPRINTF_WARNING SECUREC_END_MASK_WFORMAT_WARNING \ ++ SECUREC_END_MASK_MSVC_CRT_WARNING ++ ++/* ++ * Flag definitions. ++ * Using macros instead of enumerations is because some of the enumerated types under the compiler are 16bit. ++ */ ++#define SECUREC_FLAG_SIGN 0x00001U ++#define SECUREC_FLAG_SIGN_SPACE 0x00002U ++#define SECUREC_FLAG_LEFT 0x00004U ++#define SECUREC_FLAG_LEADZERO 0x00008U ++#define SECUREC_FLAG_LONG 0x00010U ++#define SECUREC_FLAG_SHORT 0x00020U ++#define SECUREC_FLAG_SIGNED 0x00040U ++#define SECUREC_FLAG_ALTERNATE 0x00080U ++#define SECUREC_FLAG_NEGATIVE 0x00100U ++#define SECUREC_FLAG_FORCE_OCTAL 0x00200U ++#define SECUREC_FLAG_LONG_DOUBLE 0x00400U ++#define SECUREC_FLAG_WIDECHAR 0x00800U ++#define SECUREC_FLAG_LONGLONG 0x01000U ++#define SECUREC_FLAG_CHAR 0x02000U ++#define SECUREC_FLAG_POINTER 0x04000U ++#define SECUREC_FLAG_I64 0x08000U ++#define SECUREC_FLAG_PTRDIFF 0x10000U ++#define SECUREC_FLAG_SIZE 0x20000U ++#ifdef SECUREC_COMPATIBLE_LINUX_FORMAT ++#define SECUREC_FLAG_INTMAX 0x40000U ++#endif ++ ++/* State definitions. Identify the status of the current format */ ++typedef enum { ++ STAT_NORMAL, ++ STAT_PERCENT, ++ STAT_FLAG, ++ STAT_WIDTH, ++ STAT_DOT, ++ STAT_PRECIS, ++ STAT_SIZE, ++ STAT_TYPE, ++ STAT_INVALID ++} SecFmtState; ++ ++#ifndef SECUREC_BUFFER_SIZE ++#if SECUREC_IN_KERNEL ++#define SECUREC_BUFFER_SIZE 32 ++#elif defined(SECUREC_STACK_SIZE_LESS_THAN_1K) ++/* ++ * SECUREC BUFFER SIZE Can not be less than 23 ++ * The length of the octal representation of 64-bit integers with zero lead ++ */ ++#define SECUREC_BUFFER_SIZE 256 ++#else ++#define SECUREC_BUFFER_SIZE 512 ++#endif ++#endif ++#if SECUREC_BUFFER_SIZE < 23 ++#error SECUREC_BUFFER_SIZE Can not be less than 23 ++#endif ++/* Buffer size for wchar, use 4 to make the compiler aligns as 8 bytes as possible */ ++#define SECUREC_WCHAR_BUFFER_SIZE 4 ++ ++#define SECUREC_MAX_PRECISION SECUREC_BUFFER_SIZE ++/* Max. # bytes in multibyte char,see MB_LEN_MAX */ ++#define SECUREC_MB_LEN 16 ++/* The return value of the internal function, which is returned when truncated */ ++#define SECUREC_PRINTF_TRUNCATE (-2) ++ ++#define SECUREC_VSPRINTF_PARAM_ERROR(format, strDest, destMax, maxLimit) \ ++ ((format) == NULL || (strDest) == NULL || (destMax) == 0 || (destMax) > (maxLimit)) ++ ++#define SECUREC_VSPRINTF_CLEAR_DEST(strDest, destMax, maxLimit) do { \ ++ if ((strDest) != NULL && (destMax) > 0 && (destMax) <= (maxLimit)) { \ ++ *(strDest) = '\0'; \ ++ } \ ++} SECUREC_WHILE_ZERO ++ ++#ifdef SECUREC_COMPATIBLE_WIN_FORMAT ++#define SECUREC_VSNPRINTF_PARAM_ERROR(format, strDest, destMax, count, maxLimit) \ ++ (((format) == NULL || (strDest) == NULL || (destMax) == 0 || (destMax) > (maxLimit)) || \ ++ ((count) > (SECUREC_STRING_MAX_LEN - 1) && (count) != (size_t)(-1))) ++ ++#else ++#define SECUREC_VSNPRINTF_PARAM_ERROR(format, strDest, destMax, count, maxLimit) \ ++ (((format) == NULL || (strDest) == NULL || (destMax) == 0 || (destMax) > (maxLimit)) || \ ++ ((count) > (SECUREC_STRING_MAX_LEN - 1))) ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++#ifdef SECUREC_FOR_WCHAR ++int SecVswprintfImpl(wchar_t *string, size_t count, const wchar_t *format, va_list argList); ++#else ++int SecVsnprintfImpl(char *string, size_t count, const char *format, va_list argList); ++#endif ++#ifdef __cplusplus ++} ++#endif ++ ++#endif ++ +diff --git a/securec/src/secureprintoutput_a.c b/securec/src/secureprintoutput_a.c +new file mode 100644 +index 0000000..8094494 +--- /dev/null ++++ b/securec/src/secureprintoutput_a.c +@@ -0,0 +1,113 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: By defining corresponding macro for ANSI string and including "output.inl", ++ * this file generates real underlying function used by printf family API. ++ * Create: 2014-02-25 ++ */ ++ ++#define SECUREC_FORMAT_OUTPUT_INPUT 1 ++ ++#ifdef SECUREC_FOR_WCHAR ++#undef SECUREC_FOR_WCHAR ++#endif ++ ++#include ++#include "secureprintoutput.h" ++#if SECUREC_WARP_OUTPUT ++#define SECUREC_FORMAT_FLAG_TABLE_SIZE 128 ++SECUREC_INLINE const char *SecSkipKnownFlags(const char *format) ++{ ++ static const unsigned char flagTable[SECUREC_FORMAT_FLAG_TABLE_SIZE] = { ++ /* ++ * Known flag is "0123456789 +-#hlLwZzjqt*I$" ++ */ ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, ++ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, ++ 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 ++ }; ++ const char *fmt = format; ++ while (*fmt != '\0') { ++ char fmtChar = *fmt; ++ if ((unsigned char)fmtChar > 0x7f) { /* 0x7f is upper limit of format char value */ ++ break; ++ } ++ if (flagTable[(unsigned char)fmtChar] == 0) { ++ break; ++ } ++ ++fmt; ++ } ++ return fmt; ++} ++ ++SECUREC_INLINE int SecFormatContainN(const char *format) ++{ ++ const char *fmt = format; ++ while (*fmt != '\0') { ++ ++fmt; ++ /* Skip normal char */ ++ if (*(fmt - 1) != '%') { ++ continue; ++ } ++ /* Meet %% */ ++ if (*fmt == '%') { ++ ++fmt; /* Point to the character after the %. Correct handling %%xx */ ++ continue; ++ } ++ /* Now parse %..., fmt point to the character after the % */ ++ fmt = SecSkipKnownFlags(fmt); ++ if (*fmt == 'n') { ++ return 1; ++ } ++ } ++ return 0; ++} ++/* ++ * Multi character formatted output implementation, the count include \0 character, must be greater than zero ++ */ ++int SecVsnprintfImpl(char *string, size_t count, const char *format, va_list argList) ++{ ++ int retVal; ++ if (SecFormatContainN(format) != 0) { ++ string[0] = '\0'; ++ return -1; ++ } ++ SECUREC_MASK_VSPRINTF_WARNING ++ retVal = vsnprintf(string, count, format, argList); ++ SECUREC_END_MASK_VSPRINTF_WARNING ++ if (retVal >= (int)count) { /* The size_t to int is ok, count max is SECUREC_STRING_MAX_LEN */ ++ /* The buffer was too small; we return truncation */ ++ string[count - 1] = '\0'; ++ return SECUREC_PRINTF_TRUNCATE; ++ } ++ if (retVal < 0) { ++ string[0] = '\0'; /* Empty the dest strDest */ ++ return -1; ++ } ++ return retVal; ++} ++#else ++#if SECUREC_IN_KERNEL ++#include ++#endif ++ ++#ifndef EOF ++#define EOF (-1) ++#endif ++ ++#include "output.inl" ++ ++#endif ++ +diff --git a/securec/src/secureprintoutput_w.c b/securec/src/secureprintoutput_w.c +new file mode 100644 +index 0000000..7a3fb91 +--- /dev/null ++++ b/securec/src/secureprintoutput_w.c +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: By defining corresponding macro for UNICODE string and including "output.inl", ++ * this file generates real underlying function used by printf family API. ++ * Create: 2014-02-25 ++ */ ++ ++/* If some platforms don't have wchar.h, don't include it */ ++#if !(defined(SECUREC_VXWORKS_PLATFORM)) ++/* If there is no macro above, it will cause compiling alarm */ ++#if defined(_MSC_VER) && (_MSC_VER >= 1400) ++#ifndef _CRTIMP_ALTERNATIVE ++#define _CRTIMP_ALTERNATIVE /* Comment microsoft *_s function */ ++#endif ++#ifndef __STDC_WANT_SECURE_LIB__ ++#define __STDC_WANT_SECURE_LIB__ 0 ++#endif ++#endif ++//#include ++#endif ++ ++/* Disable wchar func to clear vs warning */ ++#define SECUREC_ENABLE_WCHAR_FUNC 0 ++#define SECUREC_FORMAT_OUTPUT_INPUT 1 ++ ++#ifndef SECUREC_FOR_WCHAR ++#define SECUREC_FOR_WCHAR ++#endif ++ ++#include "secureprintoutput.h" ++ ++#include "output.inl" ++ +diff --git a/securec/src/snprintf_s.c b/securec/src/snprintf_s.c +new file mode 100644 +index 0000000..e9b94f3 +--- /dev/null ++++ b/securec/src/snprintf_s.c +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: snprintf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++#if SECUREC_ENABLE_SNPRINTF ++/* ++ * ++ * The snprintf_s function is equivalent to the snprintf function ++ * except for the parameter destMax/count and the explicit runtime-constraints violation ++ * The snprintf_s function formats and stores count or fewer characters in ++ * strDest and appends a terminating null. Each argument (if any) is converted ++ * and output according to the corresponding format specification in format. ++ * The formatting is consistent with the printf family of functions; If copying ++ * occurs between strings that overlap, the behavior is undefined. ++ * ++ * ++ * strDest Storage location for the output. ++ * destMax The size of the storage location for output. Size ++ * in bytes for snprintf_s or size in words for snwprintf_s. ++ * count Maximum number of character to store. ++ * format Format-control string. ++ * ... Optional arguments. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of characters written, not including the terminating null ++ * return -1 if an error occurs. ++ * return -1 if count < destMax and the output string has been truncated ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ * ++ */ ++int snprintf_s(char *strDest, size_t destMax, size_t count, const char *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vsnprintf_s(strDest, destMax, count, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(snprintf_s); ++#endif ++#endif ++ ++#if SECUREC_SNPRINTF_TRUNCATED ++/* ++ * ++ * The snprintf_truncated_s function is equivalent to the snprintf function ++ * except for the parameter destMax/count and the explicit runtime-constraints violation ++ * The snprintf_truncated_s function formats and stores count or fewer characters in ++ * strDest and appends a terminating null. Each argument (if any) is converted ++ * and output according to the corresponding format specification in format. ++ * The formatting is consistent with the printf family of functions; If copying ++ * occurs between strings that overlap, the behavior is undefined. ++ * ++ * ++ * strDest Storage location for the output. ++ * destMax The size of the storage location for output. Size ++ * in bytes for snprintf_truncated_s or size in words for snwprintf_s. ++ * format Format-control string. ++ * ... Optional arguments. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of characters written, not including the terminating null ++ * return -1 if an error occurs. ++ * return destMax-1 if output string has been truncated ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ * ++ */ ++int snprintf_truncated_s(char *strDest, size_t destMax, const char *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vsnprintf_truncated_s(strDest, destMax, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(snprintf_truncated_s); ++#endif ++ ++#endif ++ +diff --git a/securec/src/sprintf_s.c b/securec/src/sprintf_s.c +new file mode 100644 +index 0000000..0cf3fca +--- /dev/null ++++ b/securec/src/sprintf_s.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: sprintf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * The sprintf_s function is equivalent to the sprintf function ++ * except for the parameter destMax and the explicit runtime-constraints violation ++ * The sprintf_s function formats and stores a series of characters and values ++ * in strDest. Each argument (if any) is converted and output according to ++ * the corresponding format specification in format. The format consists of ++ * ordinary characters and has the same form and function as the format argument ++ * for printf. A null character is appended after the last character written. ++ * If copying occurs between strings that overlap, the behavior is undefined. ++ * ++ * ++ * strDest Storage location for output. ++ * destMax Maximum number of characters to store. ++ * format Format-control string. ++ * ... Optional arguments ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of bytes stored in strDest, not counting the terminating null character. ++ * return -1 if an error occurred. ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++int sprintf_s(char *strDest, size_t destMax, const char *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vsprintf_s(strDest, destMax, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(sprintf_s); ++#endif ++ +diff --git a/securec/src/sscanf_s.c b/securec/src/sscanf_s.c +new file mode 100644 +index 0000000..b441329 +--- /dev/null ++++ b/securec/src/sscanf_s.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: sscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * The sscanf_s function is equivalent to fscanf_s, ++ * except that input is obtained from a string (specified by the argument buffer) rather than from a stream ++ * The sscanf function reads data from buffer into the location given by each ++ * argument. Every argument must be a pointer to a variable with a type that ++ * corresponds to a type specifier in format. The format argument controls the ++ * interpretation of the input fields and has the same form and function as ++ * the format argument for the scanf function. ++ * If copying takes place between strings that overlap, the behavior is undefined. ++ * ++ * ++ * buffer Stored data. ++ * format Format control string, see Format Specifications. ++ * ... Optional arguments. ++ * ++ * ++ * ... The converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; the return value does not include fields that were read but ++ * not assigned. ++ * A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int sscanf_s(const char *buffer, const char *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vsscanf_s(buffer, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(sscanf_s); ++#endif ++ +diff --git a/securec/src/strcat_s.c b/securec/src/strcat_s.c +new file mode 100644 +index 0000000..f835e7b +--- /dev/null ++++ b/securec/src/strcat_s.c +@@ -0,0 +1,101 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: strcat_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++/* ++ * Befor this function, the basic parameter checking has been done ++ */ ++SECUREC_INLINE errno_t SecDoCat(char *strDest, size_t destMax, const char *strSrc) ++{ ++ size_t destLen; ++ size_t srcLen; ++ size_t maxSrcLen; ++ SECUREC_CALC_STR_LEN(strDest, destMax, &destLen); ++ /* Only optimize strSrc, do not apply this function to strDest */ ++ maxSrcLen = destMax - destLen; ++ SECUREC_CALC_STR_LEN_OPT(strSrc, maxSrcLen, &srcLen); ++ ++ if (SECUREC_CAT_STRING_IS_OVERLAP(strDest, destLen, strSrc, srcLen)) { ++ strDest[0] = '\0'; ++ if (strDest + destLen <= strSrc && destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("strcat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_BUFFER_OVERLAP("strcat_s"); ++ return EOVERLAP_AND_RESET; ++ } ++ if (srcLen + destLen >= destMax || strDest == strSrc) { ++ strDest[0] = '\0'; ++ if (destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("strcat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_INVALID_RANGE("strcat_s"); ++ return ERANGE_AND_RESET; ++ } ++ SECUREC_MEMCPY_WARP_OPT(strDest + destLen, strSrc, srcLen + 1); /* Single character length include \0 */ ++ return EOK; ++} ++ ++/* ++ * ++ * The strcat_s function appends a copy of the string pointed to by strSrc (including the terminating null character) ++ * to the end of the string pointed to by strDest. ++ * The initial character of strSrc overwrites the terminating null character of strDest. ++ * strcat_s will return EOVERLAP_AND_RESET if the source and destination strings overlap. ++ * ++ * Note that the second parameter is the total size of the buffer, not the ++ * remaining size. ++ * ++ * ++ * strDest Null-terminated destination string buffer. ++ * destMax Size of the destination string buffer. ++ * strSrc Null-terminated source string buffer. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN ++ * EINVAL_AND_RESET (strDest unterminated and all other parameters are valid) or ++ * (strDest != NULL and strSrc is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN) ++ * ERANGE destMax is 0 and destMax > SECUREC_STRING_MAX_LEN ++ * ERANGE_AND_RESET strDest have not enough space and all other parameters are valid and not overlap ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t strcat_s(char *strDest, size_t destMax, const char *strSrc) ++{ ++ if (destMax == 0 || destMax > SECUREC_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("strcat_s"); ++ return ERANGE; ++ } ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("strcat_s"); ++ if (strDest != NULL) { ++ strDest[0] = '\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ return SecDoCat(strDest, destMax, strSrc); ++} ++ ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(strcat_s); ++#endif ++ +diff --git a/securec/src/strcpy_s.c b/securec/src/strcpy_s.c +new file mode 100644 +index 0000000..ca1b2dd +--- /dev/null ++++ b/securec/src/strcpy_s.c +@@ -0,0 +1,353 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: strcpy_s function ++ * Create: 2014-02-25 ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Performance-sensitive ++ * [reason] Always used in the performance critical path, ++ * and sufficient input validation is performed before calling ++ */ ++ ++#include "securecutil.h" ++ ++#ifndef SECUREC_STRCPY_WITH_PERFORMANCE ++#define SECUREC_STRCPY_WITH_PERFORMANCE 1 ++#endif ++ ++#define SECUREC_STRCPY_PARAM_OK(strDest, destMax, strSrc) ((destMax) > 0 && \ ++ (destMax) <= SECUREC_STRING_MAX_LEN && (strDest) != NULL && (strSrc) != NULL && (strDest) != (strSrc)) ++ ++#if (!SECUREC_IN_KERNEL) && SECUREC_STRCPY_WITH_PERFORMANCE ++#ifndef SECUREC_STRCOPY_THRESHOLD_SIZE ++#define SECUREC_STRCOPY_THRESHOLD_SIZE 32UL ++#endif ++/* The purpose of converting to void is to clean up the alarm */ ++#define SECUREC_SMALL_STR_COPY(strDest, strSrc, lenWithTerm) do { \ ++ if (SECUREC_ADDR_ALIGNED_8(strDest) && SECUREC_ADDR_ALIGNED_8(strSrc)) { \ ++ /* Use struct assignment */ \ ++ switch (lenWithTerm) { \ ++ case 1: \ ++ *(strDest) = *(strSrc); \ ++ break; \ ++ case 2: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 2); \ ++ break; \ ++ case 3: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 3); \ ++ break; \ ++ case 4: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 4); \ ++ break; \ ++ case 5: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 5); \ ++ break; \ ++ case 6: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 6); \ ++ break; \ ++ case 7: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 7); \ ++ break; \ ++ case 8: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 8); \ ++ break; \ ++ case 9: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 9); \ ++ break; \ ++ case 10: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 10); \ ++ break; \ ++ case 11: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 11); \ ++ break; \ ++ case 12: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 12); \ ++ break; \ ++ case 13: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 13); \ ++ break; \ ++ case 14: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 14); \ ++ break; \ ++ case 15: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 15); \ ++ break; \ ++ case 16: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 16); \ ++ break; \ ++ case 17: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 17); \ ++ break; \ ++ case 18: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 18); \ ++ break; \ ++ case 19: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 19); \ ++ break; \ ++ case 20: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 20); \ ++ break; \ ++ case 21: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 21); \ ++ break; \ ++ case 22: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 22); \ ++ break; \ ++ case 23: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 23); \ ++ break; \ ++ case 24: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 24); \ ++ break; \ ++ case 25: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 25); \ ++ break; \ ++ case 26: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 26); \ ++ break; \ ++ case 27: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 27); \ ++ break; \ ++ case 28: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 28); \ ++ break; \ ++ case 29: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 29); \ ++ break; \ ++ case 30: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 30); \ ++ break; \ ++ case 31: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 31); \ ++ break; \ ++ case 32: \ ++ SECUREC_COPY_VALUE_BY_STRUCT((strDest), (strSrc), 32); \ ++ break; \ ++ default: \ ++ /* Do nothing */ \ ++ break; \ ++ } /* END switch */ \ ++ } else { \ ++ char *tmpStrDest_ = (char *)(strDest); \ ++ const char *tmpStrSrc_ = (const char *)(strSrc); \ ++ switch (lenWithTerm) { \ ++ case 32: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 31: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 30: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 29: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 28: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 27: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 26: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 25: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 24: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 23: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 22: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 21: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 20: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 19: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 18: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 17: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 16: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 15: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 14: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 13: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 12: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 11: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 10: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 9: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 8: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 7: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 6: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 5: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 4: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 3: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 2: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ case 1: \ ++ *(tmpStrDest_++) = *(tmpStrSrc_++); \ ++ /* fall-through */ /* FALLTHRU */ \ ++ default: \ ++ /* Do nothing */ \ ++ break; \ ++ } \ ++ } \ ++} SECUREC_WHILE_ZERO ++#endif ++ ++#if SECUREC_IN_KERNEL || (!SECUREC_STRCPY_WITH_PERFORMANCE) ++#define SECUREC_STRCPY_OPT(dest, src, lenWithTerm) SECUREC_MEMCPY_WARP_OPT((dest), (src), (lenWithTerm)) ++#else ++/* ++ * Performance optimization. lenWithTerm include '\0' ++ */ ++#define SECUREC_STRCPY_OPT(dest, src, lenWithTerm) do { \ ++ if ((lenWithTerm) > SECUREC_STRCOPY_THRESHOLD_SIZE) { \ ++ SECUREC_MEMCPY_WARP_OPT((dest), (src), (lenWithTerm)); \ ++ } else { \ ++ SECUREC_SMALL_STR_COPY((dest), (src), (lenWithTerm)); \ ++ } \ ++} SECUREC_WHILE_ZERO ++#endif ++ ++/* ++ * Check Src Range ++ */ ++SECUREC_INLINE errno_t CheckSrcRange(char *strDest, size_t destMax, const char *strSrc) ++{ ++ size_t tmpDestMax = destMax; ++ const char *tmpSrc = strSrc; ++ /* Use destMax as boundary checker and destMax must be greater than zero */ ++ while (*tmpSrc != '\0' && tmpDestMax > 0) { ++ ++tmpSrc; ++ --tmpDestMax; ++ } ++ if (tmpDestMax == 0) { ++ strDest[0] = '\0'; ++ SECUREC_ERROR_INVALID_RANGE("strcpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ return EOK; ++} ++ ++/* ++ * Handling errors ++ */ ++errno_t strcpy_error(char *strDest, size_t destMax, const char *strSrc) ++{ ++ if (destMax == 0 || destMax > SECUREC_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("strcpy_s"); ++ return ERANGE; ++ } ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("strcpy_s"); ++ if (strDest != NULL) { ++ strDest[0] = '\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ return CheckSrcRange(strDest, destMax, strSrc); ++} ++ ++/* ++ * ++ * The strcpy_s function copies the string pointed to strSrc ++ * (including the terminating null character) into the array pointed to by strDest ++ * The destination string must be large enough to hold the source string, ++ * including the terminating null character. strcpy_s will return EOVERLAP_AND_RESET ++ * if the source and destination strings overlap. ++ * ++ * ++ * strDest Location of destination string buffer ++ * destMax Size of the destination string buffer. ++ * strSrc Null-terminated source string buffer. ++ * ++ * ++ * strDest is updated. ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN ++ * EINVAL_AND_RESET strDest != NULL and strSrc is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN ++ * ERANGE destMax is 0 and destMax > SECUREC_STRING_MAX_LEN ++ * ERANGE_AND_RESET strDest have not enough space and all other parameters are valid and not overlap ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t strcpy_s(char *strDest, size_t destMax, const char *strSrc) ++{ ++ if (SECUREC_STRCPY_PARAM_OK(strDest, destMax, strSrc)) { ++ size_t srcStrLen; ++ SECUREC_CALC_STR_LEN(strSrc, destMax, &srcStrLen); ++ ++srcStrLen; /* The length include '\0' */ ++ ++ if (srcStrLen <= destMax) { ++ /* Use mem overlap check include '\0' */ ++ if (SECUREC_MEMORY_NO_OVERLAP(strDest, strSrc, srcStrLen)) { ++ /* Performance optimization srcStrLen include '\0' */ ++ SECUREC_STRCPY_OPT(strDest, strSrc, srcStrLen); ++ return EOK; ++ } else { ++ strDest[0] = '\0'; ++ SECUREC_ERROR_BUFFER_OVERLAP("strcpy_s"); ++ return EOVERLAP_AND_RESET; ++ } ++ } ++ } ++ return strcpy_error(strDest, destMax, strSrc); ++} ++ ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(strcpy_s); ++#endif ++ +diff --git a/securec/src/strncat_s.c b/securec/src/strncat_s.c +new file mode 100644 +index 0000000..6686d29 +--- /dev/null ++++ b/securec/src/strncat_s.c +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: strncat_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++/* ++ * Befor this function, the basic parameter checking has been done ++ */ ++SECUREC_INLINE errno_t SecDoCatLimit(char *strDest, size_t destMax, const char *strSrc, size_t count) ++{ ++ size_t destLen; ++ size_t srcLen; ++ SECUREC_CALC_STR_LEN(strDest, destMax, &destLen); ++ /* ++ * The strSrc is no longer optimized. The reason is that when count is small, ++ * the efficiency of strnlen is higher than that of self realization. ++ */ ++ SECUREC_CALC_STR_LEN(strSrc, count, &srcLen); ++ ++ if (SECUREC_CAT_STRING_IS_OVERLAP(strDest, destLen, strSrc, srcLen)) { ++ strDest[0] = '\0'; ++ if (strDest + destLen <= strSrc && destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("strncat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_BUFFER_OVERLAP("strncat_s"); ++ return EOVERLAP_AND_RESET; ++ } ++ if (srcLen + destLen >= destMax || strDest == strSrc) { ++ strDest[0] = '\0'; ++ if (destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("strncat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_INVALID_RANGE("strncat_s"); ++ return ERANGE_AND_RESET; ++ } ++ SECUREC_MEMCPY_WARP_OPT(strDest + destLen, strSrc, srcLen); /* No terminator */ ++ *(strDest + destLen + srcLen) = '\0'; ++ return EOK; ++} ++ ++/* ++ * ++ * The strncat_s function appends not more than n successive characters ++ * (not including the terminating null character) ++ * from the array pointed to by strSrc to the end of the string pointed to by strDest ++ * The strncat_s function try to append the first D characters of strSrc to ++ * the end of strDest, where D is the lesser of count and the length of strSrc. ++ * If appending those D characters will fit within strDest (whose size is given ++ * as destMax) and still leave room for a null terminator, then those characters ++ * are appended, starting at the original terminating null of strDest, and a ++ * new terminating null is appended; otherwise, strDest[0] is set to the null ++ * character. ++ * ++ * ++ * strDest Null-terminated destination string. ++ * destMax Size of the destination buffer. ++ * strSrc Null-terminated source string. ++ * count Number of character to append, or truncate. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN ++ * EINVAL_AND_RESET (strDest unterminated and all other parameters are valid)or ++ * (strDest != NULL and strSrc is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN) ++ * ERANGE destMax is 0 and destMax > SECUREC_STRING_MAX_LEN ++ * ERANGE_AND_RESET strDest have not enough space and all other parameters are valid and not overlap ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t strncat_s(char *strDest, size_t destMax, const char *strSrc, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("strncat_s"); ++ return ERANGE; ++ } ++ ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("strncat_s"); ++ if (strDest != NULL) { ++ strDest[0] = '\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ if (count > SECUREC_STRING_MAX_LEN) { ++#ifdef SECUREC_COMPATIBLE_WIN_FORMAT ++ if (count == (size_t)(-1)) { ++ /* Windows internal functions may pass in -1 when calling this function */ ++ return SecDoCatLimit(strDest, destMax, strSrc, destMax); ++ } ++#endif ++ strDest[0] = '\0'; ++ SECUREC_ERROR_INVALID_RANGE("strncat_s"); ++ return ERANGE_AND_RESET; ++ } ++ return SecDoCatLimit(strDest, destMax, strSrc, count); ++} ++ ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(strncat_s); ++#endif ++ +diff --git a/securec/src/strncpy_s.c b/securec/src/strncpy_s.c +new file mode 100644 +index 0000000..5f4c5b7 +--- /dev/null ++++ b/securec/src/strncpy_s.c +@@ -0,0 +1,145 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: strncpy_s function ++ * Create: 2014-02-25 ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Performance-sensitive ++ * [reason] Always used in the performance critical path, ++ * and sufficient input validation is performed before calling ++ */ ++ ++#include "securecutil.h" ++ ++#if defined(SECUREC_COMPATIBLE_WIN_FORMAT) ++#define SECUREC_STRNCPY_PARAM_OK(strDest, destMax, strSrc, count) \ ++ (((destMax) > 0 && (destMax) <= SECUREC_STRING_MAX_LEN && (strDest) != NULL && (strSrc) != NULL && \ ++ ((count) <= SECUREC_STRING_MAX_LEN || (count) == ((size_t)(-1))) && (count) > 0)) ++#else ++#define SECUREC_STRNCPY_PARAM_OK(strDest, destMax, strSrc, count) \ ++ (((destMax) > 0 && (destMax) <= SECUREC_STRING_MAX_LEN && (strDest) != NULL && (strSrc) != NULL && \ ++ (count) <= SECUREC_STRING_MAX_LEN && (count) > 0)) ++#endif ++ ++/* ++ * Check Src Count Range ++ */ ++SECUREC_INLINE errno_t CheckSrcCountRange(char *strDest, size_t destMax, const char *strSrc, size_t count) ++{ ++ size_t tmpDestMax = destMax; ++ size_t tmpCount = count; ++ const char *endPos = strSrc; ++ ++ /* Use destMax and count as boundary checker and destMax must be greater than zero */ ++ while (*(endPos) != '\0' && tmpDestMax > 0 && tmpCount > 0) { ++ ++endPos; ++ --tmpCount; ++ --tmpDestMax; ++ } ++ if (tmpDestMax == 0) { ++ strDest[0] = '\0'; ++ SECUREC_ERROR_INVALID_RANGE("strncpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ return EOK; ++} ++ ++/* ++ * Handling errors, when dest equal src return EOK ++ */ ++errno_t strncpy_error(char *strDest, size_t destMax, const char *strSrc, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("strncpy_s"); ++ return ERANGE; ++ } ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("strncpy_s"); ++ if (strDest != NULL) { ++ strDest[0] = '\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ if (count > SECUREC_STRING_MAX_LEN) { ++ strDest[0] = '\0'; /* Clear dest string */ ++ SECUREC_ERROR_INVALID_RANGE("strncpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ if (count == 0) { ++ strDest[0] = '\0'; ++ return EOK; ++ } ++ return CheckSrcCountRange(strDest, destMax, strSrc, count); ++} ++ ++/* ++ * ++ * The strncpy_s function copies not more than n successive characters (not including the terminating null character) ++ * from the array pointed to by strSrc to the array pointed to by strDest. ++ * ++ * ++ * strDest Destination string. ++ * destMax The size of the destination string, in characters. ++ * strSrc Source string. ++ * count Number of characters to be copied. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN ++ * EINVAL_AND_RESET strDest != NULL and strSrc is NULL and destMax != 0 and destMax <= SECUREC_STRING_MAX_LEN ++ * ERANGE destMax is 0 and destMax > SECUREC_STRING_MAX_LEN ++ * ERANGE_AND_RESET strDest have not enough space and all other parameters are valid and not overlap ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t strncpy_s(char *strDest, size_t destMax, const char *strSrc, size_t count) ++{ ++ if (SECUREC_STRNCPY_PARAM_OK(strDest, destMax, strSrc, count)) { ++ size_t minCpLen; /* Use it to store the maxi length limit */ ++ if (count < destMax) { ++ SECUREC_CALC_STR_LEN(strSrc, count, &minCpLen); /* No ending terminator */ ++ } else { ++ size_t tmpCount = destMax; ++#ifdef SECUREC_COMPATIBLE_WIN_FORMAT ++ if (count == ((size_t)(-1))) { ++ tmpCount = destMax - 1; ++ } ++#endif ++ SECUREC_CALC_STR_LEN(strSrc, tmpCount, &minCpLen); /* No ending terminator */ ++ if (minCpLen == destMax) { ++ strDest[0] = '\0'; ++ SECUREC_ERROR_INVALID_RANGE("strncpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ } ++ if (SECUREC_STRING_NO_OVERLAP(strDest, strSrc, minCpLen) || strDest == strSrc) { ++ /* Not overlap */ ++ SECUREC_MEMCPY_WARP_OPT(strDest, strSrc, minCpLen); /* Copy string without terminator */ ++ strDest[minCpLen] = '\0'; ++ return EOK; ++ } else { ++ strDest[0] = '\0'; ++ SECUREC_ERROR_BUFFER_OVERLAP("strncpy_s"); ++ return EOVERLAP_AND_RESET; ++ } ++ } ++ return strncpy_error(strDest, destMax, strSrc, count); ++} ++ ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(strncpy_s); ++#endif ++ +diff --git a/securec/src/strtok_s.c b/securec/src/strtok_s.c +new file mode 100644 +index 0000000..cd5dcd2 +--- /dev/null ++++ b/securec/src/strtok_s.c +@@ -0,0 +1,116 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: strtok_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++SECUREC_INLINE int SecIsInDelimit(char ch, const char *strDelimit) ++{ ++ const char *ctl = strDelimit; ++ while (*ctl != '\0' && *ctl != ch) { ++ ++ctl; ++ } ++ return (int)(*ctl != '\0'); ++} ++ ++/* ++ * Find beginning of token (skip over leading delimiters). ++ * Note that there is no token if this loop sets string to point to the terminal null. ++ */ ++SECUREC_INLINE char *SecFindBegin(char *strToken, const char *strDelimit) ++{ ++ char *token = strToken; ++ while (*token != '\0') { ++ if (SecIsInDelimit(*token, strDelimit) != 0) { ++ ++token; ++ continue; ++ } ++ /* Don't find any delimiter in string header, break the loop */ ++ break; ++ } ++ return token; ++} ++ ++/* ++ * Find rest of token ++ */ ++SECUREC_INLINE char *SecFindRest(char *strToken, const char *strDelimit) ++{ ++ /* Find the rest of the token. If it is not the end of the string, put a null there */ ++ char *token = strToken; ++ while (*token != '\0') { ++ if (SecIsInDelimit(*token, strDelimit) != 0) { ++ /* Find a delimiter, set string terminator */ ++ *token = '\0'; ++ ++token; ++ break; ++ } ++ ++token; ++ } ++ return token; ++} ++ ++/* ++ * Find the final position pointer ++ */ ++SECUREC_INLINE char *SecUpdateToken(char *strToken, const char *strDelimit, char **context) ++{ ++ /* Point to updated position. Record string position for next search in the context */ ++ *context = SecFindRest(strToken, strDelimit); ++ /* Determine if a token has been found. */ ++ if (*context == strToken) { ++ return NULL; ++ } ++ return strToken; ++} ++ ++/* ++ * ++ * The strtok_s function parses a string into a sequence of strToken, ++ * replace all characters in strToken string that match to strDelimit set with 0. ++ * On the first call to strtok_s the string to be parsed should be specified in strToken. ++ * In each subsequent call that should parse the same string, strToken should be NULL ++ * ++ * strToken String containing token or tokens. ++ * strDelimit Set of delimiter characters. ++ * context Used to store position information between calls ++ * to strtok_s ++ * ++ * context is updated ++ * ++ * On the first call returns the address of the first non \0 character, otherwise NULL is returned. ++ * In subsequent calls, the strtoken is set to NULL, and the context set is the same as the previous call, ++ * return NULL if the *context string length is equal 0, otherwise return *context. ++ */ ++char *strtok_s(char *strToken, const char *strDelimit, char **context) ++{ ++ char *orgToken = strToken; ++ /* Validate delimiter and string context */ ++ if (context == NULL || strDelimit == NULL) { ++ return NULL; ++ } ++ /* Valid input string and string pointer from where to search */ ++ if (orgToken == NULL && *context == NULL) { ++ return NULL; ++ } ++ /* If string is null, continue searching from previous string position stored in context */ ++ if (orgToken == NULL) { ++ orgToken = *context; ++ } ++ orgToken = SecFindBegin(orgToken, strDelimit); ++ return SecUpdateToken(orgToken, strDelimit, context); ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(strtok_s); ++#endif ++ +diff --git a/securec/src/swprintf_s.c b/securec/src/swprintf_s.c +new file mode 100644 +index 0000000..09d77a2 +--- /dev/null ++++ b/securec/src/swprintf_s.c +@@ -0,0 +1,48 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: swprintf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * The swprintf_s function is the wide-character equivalent of the sprintf_s function ++ * ++ * ++ * strDest Storage location for the output. ++ * destMax Maximum number of characters to store. ++ * format Format-control string. ++ * ... Optional arguments ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of wide characters stored in strDest, not counting the terminating null wide character. ++ * return -1 if an error occurred. ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++int swprintf_s(wchar_t *strDest, size_t destMax, const wchar_t *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vswprintf_s(strDest, destMax, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++ +diff --git a/securec/src/swscanf_s.c b/securec/src/swscanf_s.c +new file mode 100644 +index 0000000..e5b8bbf +--- /dev/null ++++ b/securec/src/swscanf_s.c +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: swscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * The swscanf_s function is the wide-character equivalent of the sscanf_s function ++ * The swscanf_s function reads data from buffer into the location given by ++ * each argument. Every argument must be a pointer to a variable with a type ++ * that corresponds to a type specifier in format. The format argument controls ++ * the interpretation of the input fields and has the same form and function ++ * as the format argument for the scanf function. If copying takes place between ++ * strings that overlap, the behavior is undefined. ++ * ++ * ++ * buffer Stored data. ++ * format Format control string, see Format Specifications. ++ * ... Optional arguments. ++ * ++ * ++ * ... the converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; The return value does not include fields that were read but not ++ * assigned. ++ * A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int swscanf_s(const wchar_t *buffer, const wchar_t *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vswscanf_s(buffer, format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++ +diff --git a/securec/src/vfscanf_s.c b/securec/src/vfscanf_s.c +new file mode 100644 +index 0000000..214ee6a +--- /dev/null ++++ b/securec/src/vfscanf_s.c +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vfscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "secinput.h" ++ ++/* ++ * ++ * The vfscanf_s function is equivalent to fscanf_s, with the variable argument list replaced by argList ++ * The vfscanf_s function reads data from the current position of stream into ++ * the locations given by argument (if any). Each argument must be a pointer ++ * to a variable of a type that corresponds to a type specifier in format. ++ * format controls the interpretation of the input fields and has the same ++ * form and function as the format argument for scanf. ++ * ++ * ++ * stream Pointer to FILE structure. ++ * format Format control string, see Format Specifications. ++ * argList pointer to list of arguments ++ * ++ * ++ * argList the converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; the return value does not include fields that were read but ++ * not assigned. A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int vfscanf_s(FILE *stream, const char *format, va_list argList) ++{ ++ int retVal; /* If initialization causes e838 */ ++ SecFileStream fStr; ++ ++ if (stream == NULL || format == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("vfscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ if (stream == SECUREC_STREAM_STDIN) { ++ return vscanf_s(format, argList); ++ } ++ ++ SECUREC_LOCK_FILE(stream); ++ SECUREC_FILE_STREAM_FROM_FILE(&fStr, stream); ++ retVal = SecInputS(&fStr, format, argList); ++ SECUREC_UNLOCK_FILE(stream); ++ if (retVal < 0) { ++ SECUREC_ERROR_INVALID_PARAMTER("vfscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ ++ return retVal; ++} ++ +diff --git a/securec/src/vfwscanf_s.c b/securec/src/vfwscanf_s.c +new file mode 100644 +index 0000000..1ab9c3c +--- /dev/null ++++ b/securec/src/vfwscanf_s.c +@@ -0,0 +1,67 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vfwscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#ifndef SECUREC_FOR_WCHAR ++#define SECUREC_FOR_WCHAR ++#endif ++ ++#include "secinput.h" ++ ++/* ++ * ++ * The vfwscanf_s function is the wide-character equivalent of the vfscanf_s function ++ * The vfwscanf_s function reads data from the current position of stream into ++ * the locations given by argument (if any). Each argument must be a pointer ++ * to a variable of a type that corresponds to a type specifier in format. ++ * format controls the interpretation of the input fields and has the same form ++ * and function as the format argument for scanf. ++ * ++ * ++ * stream Pointer to FILE structure. ++ * format Format control string, see Format Specifications. ++ * argList pointer to list of arguments ++ * ++ * ++ * argList the converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; the return value does not include fields that were read but ++ * not assigned. A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int vfwscanf_s(FILE *stream, const wchar_t *format, va_list argList) ++{ ++ int retVal; /* If initialization causes e838 */ ++ SecFileStream fStr; ++ ++ if (stream == NULL || format == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("vfwscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ if (stream == SECUREC_STREAM_STDIN) { ++ return vwscanf_s(format, argList); ++ } ++ ++ SECUREC_LOCK_FILE(stream); ++ SECUREC_FILE_STREAM_FROM_FILE(&fStr, stream); ++ retVal = SecInputSW(&fStr, format, argList); ++ SECUREC_UNLOCK_FILE(stream); ++ if (retVal < 0) { ++ SECUREC_ERROR_INVALID_PARAMTER("vfwscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ return retVal; ++} ++ +diff --git a/securec/src/vscanf_s.c b/securec/src/vscanf_s.c +new file mode 100644 +index 0000000..61480a6 +--- /dev/null ++++ b/securec/src/vscanf_s.c +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "secinput.h" ++ ++/* ++ * ++ * The vscanf_s function is equivalent to scanf_s, with the variable argument list replaced by argList, ++ * The vscanf_s function reads data from the standard input stream stdin and ++ * writes the data into the location that's given by argument. Each argument ++ * must be a pointer to a variable of a type that corresponds to a type specifier ++ * in format. If copying occurs between strings that overlap, the behavior is ++ * undefined. ++ * ++ * ++ * format Format control string. ++ * argList pointer to list of arguments ++ * ++ * ++ * argList the converted value stored in user assigned address ++ * ++ * ++ * Returns the number of fields successfully converted and assigned; ++ * the return value does not include fields that were read but not assigned. ++ * A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int vscanf_s(const char *format, va_list argList) ++{ ++ int retVal; /* If initialization causes e838 */ ++ SecFileStream fStr; ++ SECUREC_FILE_STREAM_FROM_STDIN(&fStr); ++ /* ++ * The "va_list" has different definition on different platform, so we can't use argList == NULL ++ * To determine it's invalid. If you has fixed platform, you can check some fields to validate it, ++ * such as "argList == NULL" or argList.xxx != NULL or *(size_t *)&argList != 0. ++ */ ++ if (format == NULL || fStr.pf == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("vscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ ++ SECUREC_LOCK_STDIN(0, fStr.pf); ++ retVal = SecInputS(&fStr, format, argList); ++ SECUREC_UNLOCK_STDIN(0, fStr.pf); ++ if (retVal < 0) { ++ SECUREC_ERROR_INVALID_PARAMTER("vscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ return retVal; ++} ++ +diff --git a/securec/src/vsnprintf_s.c b/securec/src/vsnprintf_s.c +new file mode 100644 +index 0000000..35caaa2 +--- /dev/null ++++ b/securec/src/vsnprintf_s.c +@@ -0,0 +1,138 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vsnprintf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "secureprintoutput.h" ++ ++#if SECUREC_ENABLE_VSNPRINTF ++/* ++ * ++ * The vsnprintf_s function is equivalent to the vsnprintf function ++ * except for the parameter destMax/count and the explicit runtime-constraints violation ++ * The vsnprintf_s function takes a pointer to an argument list, then formats ++ * and writes up to count characters of the given data to the memory pointed ++ * to by strDest and appends a terminating null. ++ * ++ * ++ * strDest Storage location for the output. ++ * destMax The size of the strDest for output. ++ * count Maximum number of character to write(not including ++ * the terminating NULL) ++ * format Format-control string. ++ * argList pointer to list of arguments. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of characters written, not including the terminating null ++ * return -1 if an error occurs. ++ * return -1 if count < destMax and the output string has been truncated ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++int vsnprintf_s(char *strDest, size_t destMax, size_t count, const char *format, va_list argList) ++{ ++ int retVal; ++ ++ if (SECUREC_VSNPRINTF_PARAM_ERROR(format, strDest, destMax, count, SECUREC_STRING_MAX_LEN)) { ++ SECUREC_VSPRINTF_CLEAR_DEST(strDest, destMax, SECUREC_STRING_MAX_LEN); ++ SECUREC_ERROR_INVALID_PARAMTER("vsnprintf_s"); ++ return -1; ++ } ++ ++ if (destMax > count) { ++ retVal = SecVsnprintfImpl(strDest, count + 1, format, argList); ++ if (retVal == SECUREC_PRINTF_TRUNCATE) { /* To keep dest buffer not destroyed 2014.2.18 */ ++ /* The string has been truncated, return -1 */ ++ return -1; /* To skip error handler, return strlen(strDest) or -1 */ ++ } ++ } else { ++ retVal = SecVsnprintfImpl(strDest, destMax, format, argList); ++#ifdef SECUREC_COMPATIBLE_WIN_FORMAT ++ if (retVal == SECUREC_PRINTF_TRUNCATE && count == (size_t)(-1)) { ++ return -1; ++ } ++#endif ++ } ++ ++ if (retVal < 0) { ++ strDest[0] = '\0'; /* Empty the dest strDest */ ++ if (retVal == SECUREC_PRINTF_TRUNCATE) { ++ /* Buffer too small */ ++ SECUREC_ERROR_INVALID_RANGE("vsnprintf_s"); ++ } ++ SECUREC_ERROR_INVALID_PARAMTER("vsnprintf_s"); ++ return -1; ++ } ++ ++ return retVal; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(vsnprintf_s); ++#endif ++#endif ++ ++#if SECUREC_SNPRINTF_TRUNCATED ++/* ++ * ++ * The vsnprintf_truncated_s function is equivalent to the vsnprintf function ++ * except for the parameter destMax/count and the explicit runtime-constraints violation ++ * The vsnprintf_truncated_s function takes a pointer to an argument list, then formats ++ * and writes up to count characters of the given data to the memory pointed ++ * to by strDest and appends a terminating null. ++ * ++ * ++ * strDest Storage location for the output. ++ * destMax The size of the strDest for output. ++ * the terminating NULL) ++ * format Format-control string. ++ * argList pointer to list of arguments. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of characters written, not including the terminating null ++ * return -1 if an error occurs. ++ * return destMax-1 if output string has been truncated ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++int vsnprintf_truncated_s(char *strDest, size_t destMax, const char *format, va_list argList) ++{ ++ int retVal; ++ ++ if (SECUREC_VSPRINTF_PARAM_ERROR(format, strDest, destMax, SECUREC_STRING_MAX_LEN)) { ++ SECUREC_VSPRINTF_CLEAR_DEST(strDest, destMax, SECUREC_STRING_MAX_LEN); ++ SECUREC_ERROR_INVALID_PARAMTER("vsnprintf_truncated_s"); ++ return -1; ++ } ++ ++ retVal = SecVsnprintfImpl(strDest, destMax, format, argList); ++ if (retVal < 0) { ++ if (retVal == SECUREC_PRINTF_TRUNCATE) { ++ return (int)(destMax - 1); /* To skip error handler, return strlen(strDest) */ ++ } ++ strDest[0] = '\0'; /* Empty the dest strDest */ ++ SECUREC_ERROR_INVALID_PARAMTER("vsnprintf_truncated_s"); ++ return -1; ++ } ++ ++ return retVal; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(vsnprintf_truncated_s); ++#endif ++#endif ++ +diff --git a/securec/src/vsprintf_s.c b/securec/src/vsprintf_s.c +new file mode 100644 +index 0000000..f50fa4a +--- /dev/null ++++ b/securec/src/vsprintf_s.c +@@ -0,0 +1,67 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vsprintf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "secureprintoutput.h" ++ ++/* ++ * ++ * The vsprintf_s function is equivalent to the vsprintf function ++ * except for the parameter destMax and the explicit runtime-constraints violation ++ * The vsprintf_s function takes a pointer to an argument list, and then formats ++ * and writes the given data to the memory pointed to by strDest. ++ * The function differ from the non-secure versions only in that the secure ++ * versions support positional parameters. ++ * ++ * ++ * strDest Storage location for the output. ++ * destMax Size of strDest ++ * format Format specification. ++ * argList pointer to list of arguments ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of characters written, not including the terminating null character, ++ * return -1 if an error occurs. ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++int vsprintf_s(char *strDest, size_t destMax, const char *format, va_list argList) ++{ ++ int retVal; /* If initialization causes e838 */ ++ ++ if (SECUREC_VSPRINTF_PARAM_ERROR(format, strDest, destMax, SECUREC_STRING_MAX_LEN)) { ++ SECUREC_VSPRINTF_CLEAR_DEST(strDest, destMax, SECUREC_STRING_MAX_LEN); ++ SECUREC_ERROR_INVALID_PARAMTER("vsprintf_s"); ++ return -1; ++ } ++ ++ retVal = SecVsnprintfImpl(strDest, destMax, format, argList); ++ if (retVal < 0) { ++ strDest[0] = '\0'; ++ if (retVal == SECUREC_PRINTF_TRUNCATE) { ++ /* Buffer is too small */ ++ SECUREC_ERROR_INVALID_RANGE("vsprintf_s"); ++ } ++ SECUREC_ERROR_INVALID_PARAMTER("vsprintf_s"); ++ return -1; ++ } ++ ++ return retVal; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(vsprintf_s); ++#endif ++ +diff --git a/securec/src/vsscanf_s.c b/securec/src/vsscanf_s.c +new file mode 100644 +index 0000000..a19abe2 +--- /dev/null ++++ b/securec/src/vsscanf_s.c +@@ -0,0 +1,87 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vsscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "secinput.h" ++#if defined(SECUREC_VXWORKS_PLATFORM) && !SECUREC_IN_KERNEL && \ ++ (!defined(SECUREC_SYSAPI4VXWORKS) && !defined(SECUREC_CTYPE_MACRO_ADAPT)) ++#include ++#endif ++ ++/* ++ * ++ * vsscanf_s ++ * ++ * ++ * ++ * The vsscanf_s function is equivalent to sscanf_s, with the variable argument list replaced by argList ++ * The vsscanf_s function reads data from buffer into the location given by ++ * each argument. Every argument must be a pointer to a variable with a type ++ * that corresponds to a type specifier in format. The format argument controls ++ * the interpretation of the input fields and has the same form and function ++ * as the format argument for the scanf function. ++ * If copying takes place between strings that overlap, the behavior is undefined. ++ * ++ * ++ * buffer Stored data ++ * format Format control string, see Format Specifications. ++ * argList pointer to list of arguments ++ * ++ * ++ * argList the converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; the return value does not include fields that were read but ++ * not assigned. A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int vsscanf_s(const char *buffer, const char *format, va_list argList) ++{ ++ size_t count; /* If initialization causes e838 */ ++ int retVal; ++ SecFileStream fStr; ++ ++ /* Validation section */ ++ if (buffer == NULL || format == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("vsscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ count = strlen(buffer); ++ if (count == 0 || count > SECUREC_STRING_MAX_LEN) { ++ SecClearDestBuf(buffer, format, argList); ++ SECUREC_ERROR_INVALID_PARAMTER("vsscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++#if defined(SECUREC_VXWORKS_PLATFORM) && !SECUREC_IN_KERNEL ++ /* ++ * On vxworks platform when buffer is white string, will set first %s argument to zero.Like following usage: ++ * " \v\f\t\r\n", "%s", str, strSize ++ * Do not check all character, just first and last character then consider it is white string ++ */ ++ if (isspace((int)(unsigned char)buffer[0]) != 0 && isspace((int)(unsigned char)buffer[count - 1]) != 0) { ++ SecClearDestBuf(buffer, format, argList); ++ } ++#endif ++ SECUREC_FILE_STREAM_FROM_STRING(&fStr, buffer, count); ++ retVal = SecInputS(&fStr, format, argList); ++ if (retVal < 0) { ++ SECUREC_ERROR_INVALID_PARAMTER("vsscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ return retVal; ++} ++#if SECUREC_EXPORT_KERNEL_SYMBOL ++EXPORT_SYMBOL(vsscanf_s); ++#endif ++ +diff --git a/securec/src/vswprintf_s.c b/securec/src/vswprintf_s.c +new file mode 100644 +index 0000000..29715fc +--- /dev/null ++++ b/securec/src/vswprintf_s.c +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vswprintf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#ifndef SECUREC_FOR_WCHAR ++#define SECUREC_FOR_WCHAR ++#endif ++ ++#include "secureprintoutput.h" ++ ++/* ++ * ++ * The vswprintf_s function is the wide-character equivalent of the vsprintf_s function ++ * ++ * ++ * strDest Storage location for the output. ++ * destMax Maximum number of characters to store ++ * format Format specification. ++ * argList pointer to list of arguments ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * return the number of wide characters stored in strDest, not counting the terminating null wide character. ++ * return -1 if an error occurred. ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++int vswprintf_s(wchar_t *strDest, size_t destMax, const wchar_t *format, va_list argList) ++{ ++ int retVal; /* If initialization causes e838 */ ++ if (SECUREC_VSPRINTF_PARAM_ERROR(format, strDest, destMax, SECUREC_WCHAR_STRING_MAX_LEN)) { ++ SECUREC_VSPRINTF_CLEAR_DEST(strDest, destMax, SECUREC_WCHAR_STRING_MAX_LEN); ++ SECUREC_ERROR_INVALID_PARAMTER("vswprintf_s"); ++ return -1; ++ } ++ ++ retVal = SecVswprintfImpl(strDest, destMax, format, argList); ++ if (retVal < 0) { ++ strDest[0] = L'\0'; ++ if (retVal == SECUREC_PRINTF_TRUNCATE) { ++ /* Buffer too small */ ++ SECUREC_ERROR_INVALID_RANGE("vswprintf_s"); ++ } ++ SECUREC_ERROR_INVALID_PARAMTER("vswprintf_s"); ++ return -1; ++ } ++ ++ return retVal; ++} ++ +diff --git a/securec/src/vswscanf_s.c b/securec/src/vswscanf_s.c +new file mode 100644 +index 0000000..bab53a3 +--- /dev/null ++++ b/securec/src/vswscanf_s.c +@@ -0,0 +1,79 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vswscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#ifndef SECUREC_FOR_WCHAR ++#define SECUREC_FOR_WCHAR ++#endif ++ ++#include "secinput.h" ++ ++SECUREC_INLINE size_t SecWcslen(const wchar_t *s) ++{ ++ const wchar_t *end = s; ++ while (*end != L'\0') { ++ ++end; ++ } ++ return ((size_t)((end - s))); ++} ++ ++/* ++ * ++ * The vswscanf_s function is the wide-character equivalent of the vsscanf_s function ++ * The vsscanf_s function reads data from buffer into the location given by ++ * each argument. Every argument must be a pointer to a variable with a type ++ * that corresponds to a type specifier in format. ++ * The format argument controls the interpretation of the input fields and ++ * has the same form and function as the format argument for the scanf function. ++ * If copying takes place between strings that overlap, the behavior is undefined. ++ * ++ * ++ * buffer Stored data ++ * format Format control string, see Format Specifications. ++ * argList pointer to list of arguments ++ * ++ * ++ * argList the converted value stored in user assigned address ++ * ++ * ++ * Each of these functions returns the number of fields successfully converted ++ * and assigned; the return value does not include fields that were read but ++ * not assigned. A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int vswscanf_s(const wchar_t *buffer, const wchar_t *format, va_list argList) ++{ ++ size_t count; /* If initialization causes e838 */ ++ SecFileStream fStr; ++ int retVal; ++ ++ /* Validation section */ ++ if (buffer == NULL || format == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("vswscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ count = SecWcslen(buffer); ++ if (count == 0 || count > SECUREC_WCHAR_STRING_MAX_LEN) { ++ SecClearDestBufW(buffer, format, argList); ++ SECUREC_ERROR_INVALID_PARAMTER("vswscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ SECUREC_FILE_STREAM_FROM_STRING(&fStr, (const char *)buffer, count * sizeof(wchar_t)); ++ retVal = SecInputSW(&fStr, format, argList); ++ if (retVal < 0) { ++ SECUREC_ERROR_INVALID_PARAMTER("vswscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ return retVal; ++} ++ +diff --git a/securec/src/vwscanf_s.c b/securec/src/vwscanf_s.c +new file mode 100644 +index 0000000..b39f9bc +--- /dev/null ++++ b/securec/src/vwscanf_s.c +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: vwscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#ifndef SECUREC_FOR_WCHAR ++#define SECUREC_FOR_WCHAR ++#endif ++ ++#include "secinput.h" ++ ++/* ++ * ++ * The vwscanf_s function is the wide-character equivalent of the vscanf_s function ++ * The vwscanf_s function is the wide-character version of vscanf_s. The ++ * function reads data from the standard input stream stdin and writes the ++ * data into the location that's given by argument. Each argument must be a ++ * pointer to a variable of a type that corresponds to a type specifier in ++ * format. If copying occurs between strings that overlap, the behavior is ++ * undefined. ++ * ++ * ++ * format Format control string. ++ * argList pointer to list of arguments ++ * ++ * ++ * argList the converted value stored in user assigned address ++ * ++ * ++ * Returns the number of fields successfully converted and assigned; ++ * the return value does not include fields that were read but not assigned. ++ * A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int vwscanf_s(const wchar_t *format, va_list argList) ++{ ++ int retVal; /* If initialization causes e838 */ ++ SecFileStream fStr; ++ SECUREC_FILE_STREAM_FROM_STDIN(&fStr); ++ if (format == NULL || fStr.pf == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("vwscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ ++ SECUREC_LOCK_STDIN(0, fStr.pf); ++ retVal = SecInputSW(&fStr, format, argList); ++ SECUREC_UNLOCK_STDIN(0, fStr.pf); ++ if (retVal < 0) { ++ SECUREC_ERROR_INVALID_PARAMTER("vwscanf_s"); ++ return SECUREC_SCANF_EINVAL; ++ } ++ ++ return retVal; ++} ++ +diff --git a/securec/src/wcscat_s.c b/securec/src/wcscat_s.c +new file mode 100644 +index 0000000..fa7d847 +--- /dev/null ++++ b/securec/src/wcscat_s.c +@@ -0,0 +1,107 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wcscat_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++/* ++ * Befor this function, the basic parameter checking has been done ++ */ ++SECUREC_INLINE errno_t SecDoCatW(wchar_t *strDest, size_t destMax, const wchar_t *strSrc) ++{ ++ size_t destLen; ++ size_t srcLen; ++ size_t maxCount; /* Store the maximum available count */ ++ ++ /* To calculate the length of a wide character, the parameter must be a wide character */ ++ SECUREC_CALC_WSTR_LEN(strDest, destMax, &destLen); ++ maxCount = destMax - destLen; ++ SECUREC_CALC_WSTR_LEN(strSrc, maxCount, &srcLen); ++ ++ if (SECUREC_CAT_STRING_IS_OVERLAP(strDest, destLen, strSrc, srcLen)) { ++ strDest[0] = L'\0'; ++ if (strDest + destLen <= strSrc && destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcscat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_BUFFER_OVERLAP("wcscat_s"); ++ return EOVERLAP_AND_RESET; ++ } ++ if (srcLen + destLen >= destMax || strDest == strSrc) { ++ strDest[0] = L'\0'; ++ if (destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcscat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_INVALID_RANGE("wcscat_s"); ++ return ERANGE_AND_RESET; ++ } ++ /* Copy single character length include \0 */ ++ SECUREC_MEMCPY_WARP_OPT(strDest + destLen, strSrc, (srcLen + 1) * sizeof(wchar_t)); ++ return EOK; ++} ++ ++/* ++ * ++ * The wcscat_s function appends a copy of the wide string pointed to by strSrc ++* (including the terminating null wide character) ++ * to the end of the wide string pointed to by strDest. ++ * The arguments and return value of wcscat_s are wide-character strings. ++ * ++ * The wcscat_s function appends strSrc to strDest and terminates the resulting ++ * string with a null character. The initial character of strSrc overwrites the ++ * terminating null character of strDest. wcscat_s will return EOVERLAP_AND_RESET if the ++ * source and destination strings overlap. ++ * ++ * Note that the second parameter is the total size of the buffer, not the ++ * remaining size. ++ * ++ * ++ * strDest Null-terminated destination string buffer. ++ * destMax Size of the destination string buffer. ++ * strSrc Null-terminated source string buffer. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_WCHAR_STRING_MAX_LEN ++ * EINVAL_AND_RESET (strDest unterminated and all other parameters are valid) or ++ * (strDest != NULL and strSrc is NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_STRING_MAX_LEN) ++ * ERANGE destMax > SECUREC_WCHAR_STRING_MAX_LEN or destMax is 0 ++ * ERANGE_AND_RESET strDest have not enough space and all other parameters are valid and not overlap ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t wcscat_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc) ++{ ++ if (destMax == 0 || destMax > SECUREC_WCHAR_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("wcscat_s"); ++ return ERANGE; ++ } ++ ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcscat_s"); ++ if (strDest != NULL) { ++ strDest[0] = L'\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ ++ return SecDoCatW(strDest, destMax, strSrc); ++} ++ +diff --git a/securec/src/wcscpy_s.c b/securec/src/wcscpy_s.c +new file mode 100644 +index 0000000..8c4a4af +--- /dev/null ++++ b/securec/src/wcscpy_s.c +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wcscpy_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++SECUREC_INLINE errno_t SecDoCpyW(wchar_t *strDest, size_t destMax, const wchar_t *strSrc) ++{ ++ size_t srcStrLen; ++ SECUREC_CALC_WSTR_LEN(strSrc, destMax, &srcStrLen); ++ ++ if (srcStrLen == destMax) { ++ strDest[0] = L'\0'; ++ SECUREC_ERROR_INVALID_RANGE("wcscpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ if (strDest == strSrc) { ++ return EOK; ++ } ++ ++ if (SECUREC_STRING_NO_OVERLAP(strDest, strSrc, srcStrLen)) { ++ /* Performance optimization, srcStrLen is single character length include '\0' */ ++ SECUREC_MEMCPY_WARP_OPT(strDest, strSrc, (srcStrLen + 1) * sizeof(wchar_t)); ++ return EOK; ++ } else { ++ strDest[0] = L'\0'; ++ SECUREC_ERROR_BUFFER_OVERLAP("wcscpy_s"); ++ return EOVERLAP_AND_RESET; ++ } ++} ++ ++/* ++ * ++ * The wcscpy_s function copies the wide string pointed to by strSrc ++ * (including the terminating null wide character) into the array pointed to by strDest ++ ++ * ++ * strDest Destination string buffer ++ * destMax Size of the destination string buffer. ++ * strSrc Null-terminated source string buffer. ++ * ++ * ++ * strDest is updated. ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_WCHAR_STRING_MAX_LEN ++ * EINVAL_AND_RESET strDest != NULL and strSrc is NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_STRING_MAX_LEN ++ * ERANGE destMax > SECUREC_WCHAR_STRING_MAX_LEN or destMax is 0 ++ * ERANGE_AND_RESET destMax <= length of strSrc and strDest != strSrc ++ * and strDest != NULL and strSrc != NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_STRING_MAX_LEN and not overlap ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_STRING_MAX_LEN ++ * and strDest != NULL and strSrc !=NULL and strDest != strSrc ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t wcscpy_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc) ++{ ++ if (destMax == 0 || destMax > SECUREC_WCHAR_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("wcscpy_s"); ++ return ERANGE; ++ } ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcscpy_s"); ++ if (strDest != NULL) { ++ strDest[0] = L'\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ return SecDoCpyW(strDest, destMax, strSrc); ++} ++ +diff --git a/securec/src/wcsncat_s.c b/securec/src/wcsncat_s.c +new file mode 100644 +index 0000000..33e53a3 +--- /dev/null ++++ b/securec/src/wcsncat_s.c +@@ -0,0 +1,113 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wcsncat_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++/* ++ * Befor this function, the basic parameter checking has been done ++ */ ++SECUREC_INLINE errno_t SecDoCatLimitW(wchar_t *strDest, size_t destMax, const wchar_t *strSrc, size_t count) ++{ ++ /* To calculate the length of a wide character, the parameter must be a wide character */ ++ size_t destLen; ++ size_t srcLen; ++ SECUREC_CALC_WSTR_LEN(strDest, destMax, &destLen); ++ SECUREC_CALC_WSTR_LEN(strSrc, count, &srcLen); ++ ++ if (SECUREC_CAT_STRING_IS_OVERLAP(strDest, destLen, strSrc, srcLen)) { ++ strDest[0] = L'\0'; ++ if (strDest + destLen <= strSrc && destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcsncat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_BUFFER_OVERLAP("wcsncat_s"); ++ return EOVERLAP_AND_RESET; ++ } ++ if (srcLen + destLen >= destMax || strDest == strSrc) { ++ strDest[0] = L'\0'; ++ if (destLen == destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcsncat_s"); ++ return EINVAL_AND_RESET; ++ } ++ SECUREC_ERROR_INVALID_RANGE("wcsncat_s"); ++ return ERANGE_AND_RESET; ++ } ++ SECUREC_MEMCPY_WARP_OPT(strDest + destLen, strSrc, srcLen * sizeof(wchar_t)); /* no terminator */ ++ *(strDest + destLen + srcLen) = L'\0'; ++ return EOK; ++} ++ ++/* ++ * ++ * The wcsncat_s function appends not more than n successive wide characters ++ * (not including the terminating null wide character) ++ * from the array pointed to by strSrc to the end of the wide string pointed to by strDest. ++ * ++ * The wcsncat_s function try to append the first D characters of strSrc to ++ * the end of strDest, where D is the lesser of count and the length of strSrc. ++ * If appending those D characters will fit within strDest (whose size is ++ * given as destMax) and still leave room for a null terminator, then those ++ * characters are appended, starting at the original terminating null of ++ * strDest, and a new terminating null is appended; otherwise, strDest[0] is ++ * set to the null character. ++ * ++ * ++ * strDest Null-terminated destination string. ++ * destMax Size of the destination buffer. ++ * strSrc Null-terminated source string. ++ * count Number of character to append, or truncate. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_WCHAR_STRING_MAX_LEN ++ * EINVAL_AND_RESET (strDest unterminated and all other parameters are valid) or ++ * (strDest != NULL and strSrc is NULL and destMax != 0 and ++ * destMax <= SECUREC_WCHAR_STRING_MAX_LEN) ++ * ERANGE destMax > SECUREC_WCHAR_STRING_MAX_LEN or destMax is 0 ++ * ERANGE_AND_RESET strDest have not enough space and all other parameters are valid and not overlap ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t wcsncat_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_WCHAR_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("wcsncat_s"); ++ return ERANGE; ++ } ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcsncat_s"); ++ if (strDest != NULL) { ++ strDest[0] = L'\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ if (count > SECUREC_WCHAR_STRING_MAX_LEN) { ++#ifdef SECUREC_COMPATIBLE_WIN_FORMAT ++ if (count == ((size_t)(-1))) { ++ /* Windows internal functions may pass in -1 when calling this function */ ++ return SecDoCatLimitW(strDest, destMax, strSrc, destMax); ++ } ++#endif ++ strDest[0] = L'\0'; ++ SECUREC_ERROR_INVALID_RANGE("wcsncat_s"); ++ return ERANGE_AND_RESET; ++ } ++ return SecDoCatLimitW(strDest, destMax, strSrc, count); ++} ++ +diff --git a/securec/src/wcsncpy_s.c b/securec/src/wcsncpy_s.c +new file mode 100644 +index 0000000..463f90e +--- /dev/null ++++ b/securec/src/wcsncpy_s.c +@@ -0,0 +1,107 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wcsncpy_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++SECUREC_INLINE errno_t SecDoCpyLimitW(wchar_t *strDest, size_t destMax, const wchar_t *strSrc, size_t count) ++{ ++ size_t srcStrLen; ++ if (count < destMax) { ++ SECUREC_CALC_WSTR_LEN(strSrc, count, &srcStrLen); ++ } else { ++ SECUREC_CALC_WSTR_LEN(strSrc, destMax, &srcStrLen); ++ } ++ if (srcStrLen == destMax) { ++ strDest[0] = L'\0'; ++ SECUREC_ERROR_INVALID_RANGE("wcsncpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ if (strDest == strSrc) { ++ return EOK; ++ } ++ if (SECUREC_STRING_NO_OVERLAP(strDest, strSrc, srcStrLen)) { ++ /* Performance optimization srcStrLen not include '\0' */ ++ SECUREC_MEMCPY_WARP_OPT(strDest, strSrc, srcStrLen * sizeof(wchar_t)); ++ *(strDest + srcStrLen) = L'\0'; ++ return EOK; ++ } else { ++ strDest[0] = L'\0'; ++ SECUREC_ERROR_BUFFER_OVERLAP("wcsncpy_s"); ++ return EOVERLAP_AND_RESET; ++ } ++} ++ ++/* ++ * ++ * The wcsncpy_s function copies not more than n successive wide characters ++ * (not including the terminating null wide character) ++ * from the array pointed to by strSrc to the array pointed to by strDest ++ * ++ * ++ * strDest Destination string. ++ * destMax The size of the destination string, in characters. ++ * strSrc Source string. ++ * count Number of characters to be copied. ++ * ++ * ++ * strDest is updated ++ * ++ * ++ * EOK Success ++ * EINVAL strDest is NULL and destMax != 0 and destMax <= SECUREC_WCHAR_STRING_MAX_LEN ++ * EINVAL_AND_RESET strDest != NULL and strSrc is NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_STRING_MAX_LEN ++ * ERANGE destMax > SECUREC_WCHAR_STRING_MAX_LEN or destMax is 0 ++ * ERANGE_AND_RESET count > SECUREC_WCHAR_STRING_MAX_LEN or ++ * (destMax <= length of strSrc and destMax <= count and strDest != strSrc ++ * and strDest != NULL and strSrc != NULL and destMax != 0 and ++ * destMax <= SECUREC_WCHAR_STRING_MAX_LEN and not overlap) ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and all parameters are valid ++ * ++ * ++ * If there is a runtime-constraint violation, strDest[0] will be set to the '\0' when strDest and destMax valid ++ */ ++errno_t wcsncpy_s(wchar_t *strDest, size_t destMax, const wchar_t *strSrc, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_WCHAR_STRING_MAX_LEN) { ++ SECUREC_ERROR_INVALID_RANGE("wcsncpy_s"); ++ return ERANGE; ++ } ++ if (strDest == NULL || strSrc == NULL) { ++ SECUREC_ERROR_INVALID_PARAMTER("wcsncpy_s"); ++ if (strDest != NULL) { ++ strDest[0] = L'\0'; ++ return EINVAL_AND_RESET; ++ } ++ return EINVAL; ++ } ++ if (count > SECUREC_WCHAR_STRING_MAX_LEN) { ++#ifdef SECUREC_COMPATIBLE_WIN_FORMAT ++ if (count == (size_t)(-1)) { ++ return SecDoCpyLimitW(strDest, destMax, strSrc, destMax - 1); ++ } ++#endif ++ strDest[0] = L'\0'; /* Clear dest string */ ++ SECUREC_ERROR_INVALID_RANGE("wcsncpy_s"); ++ return ERANGE_AND_RESET; ++ } ++ ++ if (count == 0) { ++ strDest[0] = L'\0'; ++ return EOK; ++ } ++ ++ return SecDoCpyLimitW(strDest, destMax, strSrc, count); ++} ++ +diff --git a/securec/src/wcstok_s.c b/securec/src/wcstok_s.c +new file mode 100644 +index 0000000..063ca69 +--- /dev/null ++++ b/securec/src/wcstok_s.c +@@ -0,0 +1,112 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wcstok_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securecutil.h" ++ ++SECUREC_INLINE int SecIsInDelimitW(wchar_t ch, const wchar_t *strDelimit) ++{ ++ const wchar_t *ctl = strDelimit; ++ while (*ctl != L'\0' && *ctl != ch) { ++ ++ctl; ++ } ++ return (int)(*ctl != L'\0'); ++} ++ ++/* ++ * Find beginning of token (skip over leading delimiters). ++ * Note that there is no token if this loop sets string to point to the terminal null. ++ */ ++SECUREC_INLINE wchar_t *SecFindBeginW(wchar_t *strToken, const wchar_t *strDelimit) ++{ ++ wchar_t *token = strToken; ++ while (*token != L'\0') { ++ if (SecIsInDelimitW(*token, strDelimit) != 0) { ++ ++token; ++ continue; ++ } ++ /* Don't find any delimiter in string header, break the loop */ ++ break; ++ } ++ return token; ++} ++ ++/* ++ * Find the end of the token. If it is not the end of the string, put a null there. ++ */ ++SECUREC_INLINE wchar_t *SecFindRestW(wchar_t *strToken, const wchar_t *strDelimit) ++{ ++ wchar_t *token = strToken; ++ while (*token != L'\0') { ++ if (SecIsInDelimitW(*token, strDelimit) != 0) { ++ /* Find a delimiter, set string terminator */ ++ *token = L'\0'; ++ ++token; ++ break; ++ } ++ ++token; ++ } ++ return token; ++} ++ ++/* ++ * Update Token wide character function ++ */ ++SECUREC_INLINE wchar_t *SecUpdateTokenW(wchar_t *strToken, const wchar_t *strDelimit, wchar_t **context) ++{ ++ /* Point to updated position. Record string position for next search in the context */ ++ *context = SecFindRestW(strToken, strDelimit); ++ /* Determine if a token has been found */ ++ if (*context == strToken) { ++ return NULL; ++ } ++ return strToken; ++} ++ ++/* ++ * ++ * wcstok_s ++ * ++ * ++ * ++ * The wcstok_s function is the wide-character equivalent of the strtok_s function ++ * ++ * ++ * strToken String containing token or tokens. ++ * strDelimit Set of delimiter characters. ++ * context Used to store position information between calls to ++ * wcstok_s. ++ * ++ * ++ * context is updated ++ * ++ * The wcstok_s function is the wide-character equivalent of the strtok_s function ++ */ ++wchar_t *wcstok_s(wchar_t *strToken, const wchar_t *strDelimit, wchar_t **context) ++{ ++ wchar_t *orgToken = strToken; ++ /* Validation section */ ++ if (context == NULL || strDelimit == NULL) { ++ return NULL; ++ } ++ if (orgToken == NULL && *context == NULL) { ++ return NULL; ++ } ++ /* If string==NULL, continue with previous string */ ++ if (orgToken == NULL) { ++ orgToken = *context; ++ } ++ orgToken = SecFindBeginW(orgToken, strDelimit); ++ return SecUpdateTokenW(orgToken, strDelimit, context); ++} ++ +diff --git a/securec/src/wmemcpy_s.c b/securec/src/wmemcpy_s.c +new file mode 100644 +index 0000000..2f2b4a3 +--- /dev/null ++++ b/securec/src/wmemcpy_s.c +@@ -0,0 +1,71 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wmemcpy_s function ++ * Create: 2014-02-25 ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Portability ++ * [reason] Use unsafe function to implement security function to maintain platform compatibility. ++ * And sufficient input validation is performed before calling ++ */ ++ ++#include "securecutil.h" ++ ++/* ++ * ++ * The wmemcpy_s function copies n successive wide characters ++ * from the object pointed to by src into the object pointed to by dest.t. ++ * ++ * ++ * dest Destination buffer. ++ * destMax Size of the destination buffer. ++ * src Buffer to copy from. ++ * count Number of characters to copy. ++ * ++ * ++ * dest buffer is updated. ++ * ++ * ++ * EOK Success ++ * EINVAL dest is NULL and destMax != 0 and count <= destMax ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN ++ * EINVAL_AND_RESET dest != NULL and src is NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN and count <= destMax ++ * ERANGE destMax > SECUREC_WCHAR_MEM_MAX_LEN or destMax is 0 or ++ * (count > destMax and dest is NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN) ++ * ERANGE_AND_RESET count > destMax and dest != NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN ++ * EOVERLAP_AND_RESET dest buffer and source buffer are overlapped and ++ * count <= destMax destMax != 0 and destMax <= SECUREC_WCHAR_MEM_MAX_LEN ++ * and dest != NULL and src != NULL and dest != src ++ * ++ * if an error occurred, dest will be filled with 0 when dest and destMax valid . ++ * If the source and destination overlap, the behavior of wmemcpy_s is undefined. ++ * Use wmemmove_s to handle overlapping regions. ++ */ ++errno_t wmemcpy_s(wchar_t *dest, size_t destMax, const wchar_t *src, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_WCHAR_MEM_MAX_LEN) { ++ SECUREC_ERROR_INVALID_PARAMTER("wmemcpy_s"); ++ return ERANGE; ++ } ++ if (count > destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("wmemcpy_s"); ++ if (dest != NULL) { ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, 0, destMax * sizeof(wchar_t)); ++ return ERANGE_AND_RESET; ++ } ++ return ERANGE; ++ } ++ return memcpy_s(dest, destMax * sizeof(wchar_t), src, count * sizeof(wchar_t)); ++} ++ +diff --git a/securec/src/wmemmove_s.c b/securec/src/wmemmove_s.c +new file mode 100644 +index 0000000..88bb97b +--- /dev/null ++++ b/securec/src/wmemmove_s.c +@@ -0,0 +1,70 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wmemmove_s function ++ * Create: 2014-02-25 ++ */ ++/* ++ * [Standardize-exceptions] Use unsafe function: Portability ++ * [reason] Use unsafe function to implement security function to maintain platform compatibility. ++ * And sufficient input validation is performed before calling ++ */ ++ ++#include "securecutil.h" ++ ++/* ++ * ++ * The wmemmove_s function copies n successive wide characters from the object pointed ++ * to by src into the object pointed to by dest. ++ * ++ * ++ * dest Destination buffer. ++ * destMax Size of the destination buffer. ++ * src Source object. ++ * count Number of bytes or character to copy. ++ * ++ * ++ * dest is updated. ++ * ++ * ++ * EOK Success ++ * EINVAL dest is NULL and destMax != 0 and count <= destMax ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN ++ * EINVAL_AND_RESET dest != NULL and src is NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN and count <= destMax ++ * ERANGE destMax > SECUREC_WCHAR_MEM_MAX_LEN or destMax is 0 or ++ * (count > destMax and dest is NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN) ++ * ERANGE_AND_RESET count > destMax and dest != NULL and destMax != 0 ++ * and destMax <= SECUREC_WCHAR_MEM_MAX_LEN ++ * ++ * ++ * If an error occurred, dest will be filled with 0 when dest and destMax valid. ++ * If some regions of the source area and the destination overlap, wmemmove_s ++ * ensures that the original source bytes in the overlapping region are copied ++ * before being overwritten ++ */ ++errno_t wmemmove_s(wchar_t *dest, size_t destMax, const wchar_t *src, size_t count) ++{ ++ if (destMax == 0 || destMax > SECUREC_WCHAR_MEM_MAX_LEN) { ++ SECUREC_ERROR_INVALID_PARAMTER("wmemmove_s"); ++ return ERANGE; ++ } ++ if (count > destMax) { ++ SECUREC_ERROR_INVALID_PARAMTER("wmemmove_s"); ++ if (dest != NULL) { ++ (void)SECUREC_MEMSET_FUNC_OPT(dest, 0, destMax * sizeof(wchar_t)); ++ return ERANGE_AND_RESET; ++ } ++ return ERANGE; ++ } ++ return memmove_s(dest, destMax * sizeof(wchar_t), src, count * sizeof(wchar_t)); ++} ++ +diff --git a/securec/src/wscanf_s.c b/securec/src/wscanf_s.c +new file mode 100644 +index 0000000..badb04e +--- /dev/null ++++ b/securec/src/wscanf_s.c +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2014-2021. All rights reserved. ++ * Licensed under Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, ++ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, ++ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ * Description: wscanf_s function ++ * Create: 2014-02-25 ++ */ ++ ++#include "securec.h" ++ ++/* ++ * ++ * ++ * The wscanf_s function is the wide-character equivalent of the scanf_s function ++ * The wscanf_s function reads data from the standard input stream stdin and ++ * writes the data into the location that's given by argument. Each argument ++ * must be a pointer to a variable of a type that corresponds to a type specifier ++ * in format. If copying occurs between strings that overlap, the behavior is ++ * undefined. ++ * ++ * ++ * format Format control string. ++ * ... Optional arguments. ++ * ++ * ++ * ... the converted value stored in user assigned address ++ * ++ * ++ * Returns the number of fields successfully converted and assigned; ++ * the return value does not include fields that were read but not assigned. ++ * A return value of 0 indicates that no fields were assigned. ++ * return -1 if an error occurs. ++ */ ++int wscanf_s(const wchar_t *format, ...) ++{ ++ int ret; /* If initialization causes e838 */ ++ va_list argList; ++ ++ va_start(argList, format); ++ ret = vwscanf_s(format, argList); ++ va_end(argList); ++ (void)argList; /* To clear e438 last value assigned not used , the compiler will optimize this code */ ++ ++ return ret; ++} ++ +diff --git a/test/py/tests/test_fit.py b/test/py/tests/test_fit.py +old mode 100755 +new mode 100644 +index 356d9a2..4477c44 +--- a/test/py/tests/test_fit.py ++++ b/test/py/tests/test_fit.py +@@ -17,7 +17,7 @@ base_its = ''' + #address-cells = <1>; + + images { +- kernel@1 { ++ kernel-1 { + data = /incbin/("%(kernel)s"); + type = "kernel"; + arch = "sandbox"; +@@ -26,7 +26,7 @@ base_its = ''' + load = <0x40000>; + entry = <0x8>; + }; +- kernel@2 { ++ kernel-2 { + data = /incbin/("%(loadables1)s"); + type = "kernel"; + arch = "sandbox"; +@@ -35,19 +35,19 @@ base_its = ''' + %(loadables1_load)s + entry = <0x0>; + }; +- fdt@1 { ++ fdt-1 { + description = "snow"; + data = /incbin/("%(fdt)s"); + type = "flat_dt"; + arch = "sandbox"; + %(fdt_load)s + compression = "%(compression)s"; +- signature@1 { ++ signature-1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; +- ramdisk@1 { ++ ramdisk-1 { + description = "snow"; + data = /incbin/("%(ramdisk)s"); + type = "ramdisk"; +@@ -56,7 +56,7 @@ base_its = ''' + %(ramdisk_load)s + compression = "%(compression)s"; + }; +- ramdisk@2 { ++ ramdisk-2 { + description = "snow"; + data = /incbin/("%(loadables2)s"); + type = "ramdisk"; +@@ -67,10 +67,10 @@ base_its = ''' + }; + }; + configurations { +- default = "conf@1"; +- conf@1 { +- kernel = "kernel@1"; +- fdt = "fdt@1"; ++ default = "conf-1"; ++ conf-1 { ++ kernel = "kernel-1"; ++ fdt = "fdt-1"; + %(ramdisk_config)s + %(loadables_config)s + }; +@@ -407,7 +407,7 @@ def test_fit(u_boot_console): + + # Try a ramdisk + with cons.log.section('Kernel + FDT + Ramdisk load'): +- params['ramdisk_config'] = 'ramdisk = "ramdisk@1";' ++ params['ramdisk_config'] = 'ramdisk = "ramdisk-1";' + params['ramdisk_load'] = 'load = <%#x>;' % params['ramdisk_addr'] + fit = make_fit(mkimage, params) + cons.restart_uboot() +@@ -416,7 +416,7 @@ def test_fit(u_boot_console): + + # Configuration with some Loadables + with cons.log.section('Kernel + FDT + Ramdisk load + Loadables'): +- params['loadables_config'] = 'loadables = "kernel@2", "ramdisk@2";' ++ params['loadables_config'] = 'loadables = "kernel-2", "ramdisk-2";' + params['loadables1_load'] = ('load = <%#x>;' % + params['loadables1_addr']) + params['loadables2_load'] = ('load = <%#x>;' % +diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py +index 9c41ee5..150b98e 100644 +--- a/test/py/tests/test_vboot.py ++++ b/test/py/tests/test_vboot.py +@@ -24,10 +24,18 @@ For configuration verification: + Tests run with both SHA1 and SHA256 hashing. + """ + +-import pytest +-import sys + import struct ++import pytest + import u_boot_utils as util ++import vboot_forge ++ ++TESTDATA = [ ++ ['sha1', '', False], ++ ['sha1', '-pss', False], ++ ['sha256', '', False], ++ ['sha256', '-pss', False], ++ ['sha256', '-pss', True], ++] + + @pytest.mark.boardspec('sandbox') + @pytest.mark.buildconfigspec('fit_signature') +@@ -35,7 +43,8 @@ import u_boot_utils as util + @pytest.mark.requiredtool('fdtget') + @pytest.mark.requiredtool('fdtput') + @pytest.mark.requiredtool('openssl') +-def test_vboot(u_boot_console): ++@pytest.mark.parametrize("sha_algo,padding,required", TESTDATA) ++def test_vboot(u_boot_console, sha_algo, padding, required): + """Test verified boot signing with mkimage and verification with 'bootm'. + + This works using sandbox only as it needs to update the device tree used +@@ -75,13 +84,14 @@ def test_vboot(u_boot_console): + with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)): + output = cons.run_command_list( + ['host load hostfs - 100 %stest.fit' % tmpdir, +- 'fdt addr 100', +- 'bootm 100']) +- assert(expect_string in ''.join(output)) ++ 'fdt addr 100', ++ 'bootm 100']) ++ assert expect_string in ''.join(output) + if boots: +- assert('sandbox: continuing, as we cannot run' in ''.join(output)) ++ assert 'sandbox: continuing, as we cannot run' in ''.join(output) + else: +- assert('sandbox: continuing, as we cannot run' not in ''.join(output)) ++ assert('sandbox: continuing, as we cannot run' ++ not in ''.join(output)) + + def make_fit(its): + """Make a new FIT from the .its source file. +@@ -108,20 +118,6 @@ def test_vboot(u_boot_console): + util.run_and_log(cons, [mkimage, '-F', '-k', tmpdir, '-K', dtb, + '-r', fit]) + +- def sign_fit_norequire(sha_algo): +- """Sign the FIT +- +- Signs the FIT and writes the signature into it. It also writes the +- public key into the dtb. +- +- Args: +- sha_algo: Either 'sha1' or 'sha256', to select the algorithm to +- use. +- """ +- cons.log.action('%s: Sign images' % sha_algo) +- util.run_and_log(cons, [mkimage, '-F', '-k', tmpdir, '-K', dtb, +- fit]) +- + def replace_fit_totalsize(size): + """Replace FIT header's totalsize with something greater. + +@@ -142,6 +138,22 @@ def test_vboot(u_boot_console): + handle.write(struct.pack(">I", size)) + return struct.unpack(">I", total_size)[0] + ++ def create_rsa_pair(name): ++ """Generate a new RSA key paid and certificate ++ ++ Args: ++ name: Name of of the key (e.g. 'dev') ++ """ ++ public_exponent = 65537 ++ util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %s%s.key ' ++ '-pkeyopt rsa_keygen_bits:2048 ' ++ '-pkeyopt rsa_keygen_pubexp:%d' % ++ (tmpdir, name, public_exponent)) ++ ++ # Create a certificate containing the public key ++ util.run_and_log(cons, 'openssl req -batch -new -x509 -key %s%s.key ' ++ '-out %s%s.crt' % (tmpdir, name, tmpdir, name)) ++ + def test_with_algo(sha_algo, padding): + """Test verified boot with the given hash algorithm. + +@@ -160,7 +172,7 @@ def test_vboot(u_boot_console): + + # Build the FIT, but don't sign anything yet + cons.log.action('%s: Test FIT with signed images' % sha_algo) +- make_fit('sign-images-%s%s.its' % (sha_algo , padding)) ++ make_fit('sign-images-%s%s.its' % (sha_algo, padding)) + run_bootm(sha_algo, 'unsigned images', 'dev-', True) + + # Sign images with our dev keys +@@ -171,7 +183,7 @@ def test_vboot(u_boot_console): + dtc('sandbox-u-boot.dts') + + cons.log.action('%s: Test FIT with signed configuration' % sha_algo) +- make_fit('sign-configs-%s%s.its' % (sha_algo , padding)) ++ make_fit('sign-configs-%s%s.its' % (sha_algo, padding)) + run_bootm(sha_algo, 'unsigned config', '%s+ OK' % sha_algo, True) + + # Sign images with our dev keys +@@ -180,14 +192,52 @@ def test_vboot(u_boot_console): + + cons.log.action('%s: Check signed config on the host' % sha_algo) + +- util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', tmpdir, +- '-k', dtb]) +- ++ util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb]) ++ ++ if full_test: ++ # Make sure that U-Boot checks that the config is in the list of ++ # hashed nodes. If it isn't, a security bypass is possible. ++ ffit = '%stest.forged.fit' % tmpdir ++ shutil.copyfile(fit, ffit) ++ with open(ffit, 'rb') as fd: ++ root, strblock = vboot_forge.read_fdt(fd) ++ root, strblock = vboot_forge.manipulate(root, strblock) ++ with open(ffit, 'w+b') as fd: ++ vboot_forge.write_fdt(root, strblock, fd) ++ util.run_and_log_expect_exception( ++ cons, [fit_check_sign, '-f', ffit, '-k', dtb], ++ 1, 'Failed to verify required signature') ++ ++ run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False, ffit) ++ ++ # Try adding an evil root node. This should be detected. ++ efit = '%stest.evilf.fit' % tmpdir ++ shutil.copyfile(fit, efit) ++ vboot_evil.add_evil_node(fit, efit, evil_kernel, 'fakeroot') ++ ++ util.run_and_log_expect_exception( ++ cons, [fit_check_sign, '-f', efit, '-k', dtb], ++ 1, 'Failed to verify required signature') ++ run_bootm(sha_algo, 'evil fakeroot', 'Bad FIT kernel image format', ++ False, efit) ++ ++ # Try adding an @ to the kernel node name. This should be detected. ++ efit = '%stest.evilk.fit' % tmpdir ++ shutil.copyfile(fit, efit) ++ vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@') ++ ++ msg = 'Signature checking prevents use of unit addresses (@) in nodes' ++ util.run_and_log_expect_exception( ++ cons, [fit_check_sign, '-f', efit, '-k', dtb], ++ 1, msg) ++ run_bootm(sha_algo, 'evil kernel@', msg, False, efit) ++ + # Replace header bytes + bcfg = u_boot_console.config.buildconfig + max_size = int(bcfg.get('config_fit_signature_max_size', 0x10000000), 0) + existing_size = replace_fit_totalsize(max_size + 1) +- run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', False) ++ run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', ++ False) + cons.log.action('%s: Check overflowed FIT header totalsize' % sha_algo) + + # Replace with existing header bytes +@@ -205,21 +255,22 @@ def test_vboot(u_boot_console): + util.run_and_log(cons, 'fdtput -t bx %s %s value %s' % + (fit, sig_node, sig)) + +- run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', False) ++ run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', ++ False) + + cons.log.action('%s: Check bad config on the host' % sha_algo) +- util.run_and_log_expect_exception(cons, [fit_check_sign, '-f', fit, +- '-k', dtb], 1, 'Failed to verify required signature') ++ util.run_and_log_expect_exception( ++ cons, [fit_check_sign, '-f', fit, '-k', dtb], ++ 1, 'Failed to verify required signature') + + def test_required_key(sha_algo, padding): + """Test verified boot with the given hash algorithm. + +- This function test if u-boot reject an image when a required +- key isn't used to sign a FIT. ++ This function tests if U-Boot rejects an image when a required key isn't ++ used to sign a FIT. + + Args: +- sha_algo: Either 'sha1' or 'sha256', to select the algorithm to +- use. ++ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use + """ + # Compile our device tree files for kernel and U-Boot. These are + # regenerated here since mkimage will modify them (by adding a +@@ -227,22 +278,27 @@ def test_vboot(u_boot_console): + dtc('sandbox-kernel.dts') + dtc('sandbox-u-boot.dts') + +- # Build the FIT with prod key (keys required) +- # Build the FIT with dev key (keys NOT required) +- # The dtb contain the key prod and dev and the key prod are set as required. +- # Then try to boot the FIT with dev key +- # This FIT should not be accepted by u-boot because the key prod is required + cons.log.action('%s: Test FIT with configs images' % sha_algo) +- make_fit('sign-configs-%s%s-prod.its' % (sha_algo , padding)) ++ ++ # Build the FIT with prod key (keys required) and sign it. This puts the ++ # signature into sandbox-u-boot.dtb, marked 'required' ++ make_fit('sign-configs-%s%s-prod.its' % (sha_algo, padding)) + sign_fit(sha_algo) +- make_fit('sign-configs-%s%s.its' % (sha_algo , padding)) ++ ++ # Build the FIT with dev key (keys NOT required). This adds the ++ # signature into sandbox-u-boot.dtb, NOT marked 'required'. ++ make_fit('sign-configs-%s%s.its' % (sha_algo, padding)) + sign_fit(sha_algo) + +- run_bootm(sha_algo, 'signed configs', '', False) ++ # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys. ++ # Only the prod key is set as 'required'. But FIT we just built has ++ # a dev signature only (sign_fit() overwrites the FIT). ++ # Try to boot the FIT with dev key. This FIT should not be accepted by ++ # U-Boot because the prod key is required. ++ run_bootm(sha_algo, 'required key', '', False) + + cons = u_boot_console + tmpdir = cons.config.result_dir + '/' +- tmp = tmpdir + 'vboot.tmp' + datadir = cons.config.source_dir + '/test/py/tests/vboot/' + fit = '%stest.fit' % tmpdir + mkimage = cons.config.build_dir + '/tools/mkimage' +@@ -251,42 +307,22 @@ def test_vboot(u_boot_console): + dtb = '%ssandbox-u-boot.dtb' % tmpdir + sig_node = '/configurations/conf-1/signature' + +- # Create an RSA key pair +- public_exponent = 65537 +- util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %sdev.key ' +- '-pkeyopt rsa_keygen_bits:2048 ' +- '-pkeyopt rsa_keygen_pubexp:%d' % +- (tmpdir, public_exponent)) +- +- # Create a certificate containing the public key +- util.run_and_log(cons, 'openssl req -batch -new -x509 -key %sdev.key -out ' +- '%sdev.crt' % (tmpdir, tmpdir)) +- +- # Create an RSA key pair (prod) +- public_exponent = 65537 +- util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %sprod.key ' +- '-pkeyopt rsa_keygen_bits:2048 ' +- '-pkeyopt rsa_keygen_pubexp:%d' % +- (tmpdir, public_exponent)) +- +- # Create a certificate containing the public key (prod) +- util.run_and_log(cons, 'openssl req -batch -new -x509 -key %sprod.key -out ' +- '%sprod.crt' % (tmpdir, tmpdir)) ++ create_rsa_pair('dev') ++ create_rsa_pair('prod') + + # Create a number kernel image with zeroes + with open('%stest-kernel.bin' % tmpdir, 'w') as fd: +- fd.write(5000 * chr(0)) ++ fd.write(500 * chr(0)) + + try: + # We need to use our own device tree file. Remember to restore it + # afterwards. + old_dtb = cons.config.dtb + cons.config.dtb = dtb +- test_with_algo('sha1','') +- test_with_algo('sha1','-pss') +- test_with_algo('sha256','') +- test_with_algo('sha256','-pss') +- test_required_key('sha256','-pss') ++ if required: ++ test_required_key(sha_algo, padding) ++ else: ++ test_with_algo(sha_algo, padding) + finally: + # Go back to the original U-Boot with the correct dtb. + cons.config.dtb = old_dtb +diff --git a/test/py/tests/vboot_forge.py b/test/py/tests/vboot_forge.py +new file mode 100644 +index 0000000..0fb7ef4 +--- /dev/null ++++ b/test/py/tests/vboot_forge.py +@@ -0,0 +1,423 @@ ++#!/usr/bin/python3 ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (c) 2020, F-Secure Corporation, https://foundry.f-secure.com ++# ++# pylint: disable=E1101,W0201,C0103 ++ ++""" ++Verified boot image forgery tools and utilities ++ ++This module provides services to both take apart and regenerate FIT images ++in a way that preserves all existing verified boot signatures, unless you ++manipulate nodes in the process. ++""" ++ ++import struct ++import binascii ++from io import BytesIO ++ ++# ++# struct parsing helpers ++# ++ ++class BetterStructMeta(type): ++ """ ++ Preprocesses field definitions and creates a struct.Struct instance from them ++ """ ++ def __new__(cls, clsname, superclasses, attributedict): ++ if clsname != 'BetterStruct': ++ fields = attributedict['__fields__'] ++ field_types = [_[0] for _ in fields] ++ field_names = [_[1] for _ in fields if _[1] is not None] ++ attributedict['__names__'] = field_names ++ s = struct.Struct(attributedict.get('__endian__', '') + ''.join(field_types)) ++ attributedict['__struct__'] = s ++ attributedict['size'] = s.size ++ return type.__new__(cls, clsname, superclasses, attributedict) ++ ++class BetterStruct(metaclass=BetterStructMeta): ++ """ ++ Base class for better structures ++ """ ++ def __init__(self): ++ for t, n in self.__fields__: ++ if 's' in t: ++ setattr(self, n, '') ++ elif t in ('Q', 'I', 'H', 'B'): ++ setattr(self, n, 0) ++ ++ @classmethod ++ def unpack_from(cls, buffer, offset=0): ++ """ ++ Unpack structure instance from a buffer ++ """ ++ fields = cls.__struct__.unpack_from(buffer, offset) ++ instance = cls() ++ for n, v in zip(cls.__names__, fields): ++ setattr(instance, n, v) ++ return instance ++ ++ def pack(self): ++ """ ++ Pack structure instance into bytes ++ """ ++ return self.__struct__.pack(*[getattr(self, n) for n in self.__names__]) ++ ++ def __str__(self): ++ items = ["'%s': %s" % (n, repr(getattr(self, n))) for n in self.__names__ if n is not None] ++ return '(' + ', '.join(items) + ')' ++ ++# ++# some defs for flat DT data ++# ++ ++class HeaderV17(BetterStruct): ++ __endian__ = '>' ++ __fields__ = [ ++ ('I', 'magic'), ++ ('I', 'totalsize'), ++ ('I', 'off_dt_struct'), ++ ('I', 'off_dt_strings'), ++ ('I', 'off_mem_rsvmap'), ++ ('I', 'version'), ++ ('I', 'last_comp_version'), ++ ('I', 'boot_cpuid_phys'), ++ ('I', 'size_dt_strings'), ++ ('I', 'size_dt_struct'), ++ ] ++ ++class RRHeader(BetterStruct): ++ __endian__ = '>' ++ __fields__ = [ ++ ('Q', 'address'), ++ ('Q', 'size'), ++ ] ++ ++class PropHeader(BetterStruct): ++ __endian__ = '>' ++ __fields__ = [ ++ ('I', 'value_size'), ++ ('I', 'name_offset'), ++ ] ++ ++# magical constants for DTB format ++OF_DT_HEADER = 0xd00dfeed ++OF_DT_BEGIN_NODE = 1 ++OF_DT_END_NODE = 2 ++OF_DT_PROP = 3 ++OF_DT_END = 9 ++ ++class StringsBlock: ++ """ ++ Represents a parsed device tree string block ++ """ ++ def __init__(self, values=None): ++ if values is None: ++ self.values = [] ++ else: ++ self.values = values ++ ++ def __getitem__(self, at): ++ if isinstance(at, str): ++ offset = 0 ++ for value in self.values: ++ if value == at: ++ break ++ offset += len(value) + 1 ++ else: ++ self.values.append(at) ++ return offset ++ ++ if isinstance(at, int): ++ offset = 0 ++ for value in self.values: ++ if offset == at: ++ return value ++ offset += len(value) + 1 ++ raise IndexError('no string found corresponding to the given offset') ++ ++ raise TypeError('only strings and integers are accepted') ++ ++class Prop: ++ """ ++ Represents a parsed device tree property ++ """ ++ def __init__(self, name=None, value=None): ++ self.name = name ++ self.value = value ++ ++ def clone(self): ++ return Prop(self.name, self.value) ++ ++ def __repr__(self): ++ return "" % (self.name, repr(self.value)) ++ ++class Node: ++ """ ++ Represents a parsed device tree node ++ """ ++ def __init__(self, name=None): ++ self.name = name ++ self.props = [] ++ self.children = [] ++ ++ def clone(self): ++ o = Node(self.name) ++ o.props = [x.clone() for x in self.props] ++ o.children = [x.clone() for x in self.children] ++ return o ++ ++ def __getitem__(self, index): ++ return self.children[index] ++ ++ def __repr__(self): ++ return "" % (self.name, repr(self.props), repr(self.children)) ++ ++# ++# flat DT to memory ++# ++ ++def parse_strings(strings): ++ """ ++ Converts the bytes into a StringsBlock instance so it is convenient to work with ++ """ ++ strings = strings.split(b'\x00') ++ return StringsBlock(strings) ++ ++def parse_struct(stream): ++ """ ++ Parses DTB structure(s) into a Node or Prop instance ++ """ ++ tag = bytearray(stream.read(4))[3] ++ if tag == OF_DT_BEGIN_NODE: ++ name = b'' ++ while b'\x00' not in name: ++ name += stream.read(4) ++ name = name.rstrip(b'\x00') ++ node = Node(name) ++ ++ item = parse_struct(stream) ++ while item is not None: ++ if isinstance(item, Node): ++ node.children.append(item) ++ elif isinstance(item, Prop): ++ node.props.append(item) ++ item = parse_struct(stream) ++ ++ return node ++ ++ if tag == OF_DT_PROP: ++ h = PropHeader.unpack_from(stream.read(PropHeader.size)) ++ length = (h.value_size + 3) & (~3) ++ value = stream.read(length)[:h.value_size] ++ prop = Prop(h.name_offset, value) ++ return prop ++ ++ if tag in (OF_DT_END_NODE, OF_DT_END): ++ return None ++ ++ raise ValueError('unexpected tag value') ++ ++def read_fdt(fp): ++ """ ++ Reads and parses the flattened device tree (or derivatives like FIT) ++ """ ++ header = HeaderV17.unpack_from(fp.read(HeaderV17.size)) ++ if header.magic != OF_DT_HEADER: ++ raise ValueError('invalid magic value %08x; expected %08x' % (header.magic, OF_DT_HEADER)) ++ # TODO: read/parse reserved regions ++ fp.seek(header.off_dt_struct) ++ structs = fp.read(header.size_dt_struct) ++ fp.seek(header.off_dt_strings) ++ strings = fp.read(header.size_dt_strings) ++ strblock = parse_strings(strings) ++ root = parse_struct(BytesIO(structs)) ++ ++ return root, strblock ++ ++# ++# memory to flat DT ++# ++ ++def compose_structs_r(item): ++ """ ++ Recursive part of composing Nodes and Props into a bytearray ++ """ ++ t = bytearray() ++ ++ if isinstance(item, Node): ++ t.extend(struct.pack('>I', OF_DT_BEGIN_NODE)) ++ if isinstance(item.name, str): ++ item.name = bytes(item.name, 'utf-8') ++ name = item.name + b'\x00' ++ if len(name) & 3: ++ name += b'\x00' * (4 - (len(name) & 3)) ++ t.extend(name) ++ for p in item.props: ++ t.extend(compose_structs_r(p)) ++ for c in item.children: ++ t.extend(compose_structs_r(c)) ++ t.extend(struct.pack('>I', OF_DT_END_NODE)) ++ ++ elif isinstance(item, Prop): ++ t.extend(struct.pack('>I', OF_DT_PROP)) ++ value = item.value ++ h = PropHeader() ++ h.name_offset = item.name ++ if value: ++ h.value_size = len(value) ++ t.extend(h.pack()) ++ if len(value) & 3: ++ value += b'\x00' * (4 - (len(value) & 3)) ++ t.extend(value) ++ else: ++ h.value_size = 0 ++ t.extend(h.pack()) ++ ++ return t ++ ++def compose_structs(root): ++ """ ++ Composes the parsed Nodes into a flat bytearray instance ++ """ ++ t = compose_structs_r(root) ++ t.extend(struct.pack('>I', OF_DT_END)) ++ return t ++ ++def compose_strings(strblock): ++ """ ++ Composes the StringsBlock instance back into a bytearray instance ++ """ ++ b = bytearray() ++ for s in strblock.values: ++ b.extend(s) ++ b.append(0) ++ return bytes(b) ++ ++def write_fdt(root, strblock, fp): ++ """ ++ Writes out a complete flattened device tree (or FIT) ++ """ ++ header = HeaderV17() ++ header.magic = OF_DT_HEADER ++ header.version = 17 ++ header.last_comp_version = 16 ++ fp.write(header.pack()) ++ ++ header.off_mem_rsvmap = fp.tell() ++ fp.write(RRHeader().pack()) ++ ++ structs = compose_structs(root) ++ header.off_dt_struct = fp.tell() ++ header.size_dt_struct = len(structs) ++ fp.write(structs) ++ ++ strings = compose_strings(strblock) ++ header.off_dt_strings = fp.tell() ++ header.size_dt_strings = len(strings) ++ fp.write(strings) ++ ++ header.totalsize = fp.tell() ++ ++ fp.seek(0) ++ fp.write(header.pack()) ++ ++# ++# pretty printing / converting to DT source ++# ++ ++def as_bytes(value): ++ return ' '.join(["%02X" % x for x in value]) ++ ++def prety_print_value(value): ++ """ ++ Formats a property value as appropriate depending on the guessed data type ++ """ ++ if not value: ++ return '""' ++ if value[-1] == b'\x00': ++ printable = True ++ for x in value[:-1]: ++ x = ord(x) ++ if x != 0 and (x < 0x20 or x > 0x7F): ++ printable = False ++ break ++ if printable: ++ value = value[:-1] ++ return ', '.join('"' + x + '"' for x in value.split(b'\x00')) ++ if len(value) > 0x80: ++ return '[' + as_bytes(value[:0x80]) + ' ... ]' ++ return '[' + as_bytes(value) + ']' ++ ++def pretty_print_r(node, strblock, indent=0): ++ """ ++ Prints out a single node, recursing further for each of its children ++ """ ++ spaces = ' ' * indent ++ print((spaces + '%s {' % (node.name.decode('utf-8') if node.name else '/'))) ++ for p in node.props: ++ print((spaces + ' %s = %s;' % (strblock[p.name].decode('utf-8'), prety_print_value(p.value)))) ++ for c in node.children: ++ pretty_print_r(c, strblock, indent+1) ++ print((spaces + '};')) ++ ++def pretty_print(node, strblock): ++ """ ++ Generates an almost-DTS formatted printout of the parsed device tree ++ """ ++ print('/dts-v1/;') ++ pretty_print_r(node, strblock, 0) ++ ++# ++# manipulating the DT structure ++# ++ ++def manipulate(root, strblock): ++ """ ++ Maliciously manipulates the structure to create a crafted FIT file ++ """ ++ # locate /images/kernel@1 (frankly, it just expects it to be the first one) ++ kernel_node = root[0][0] ++ # clone it to save time filling all the properties ++ fake_kernel = kernel_node.clone() ++ # rename the node ++ fake_kernel.name = b'kernel@2' ++ # get rid of signatures/hashes ++ fake_kernel.children = [] ++ # NOTE: this simply replaces the first prop... either description or data ++ # should be good for testing purposes ++ fake_kernel.props[0].value = b'Super 1337 kernel\x00' ++ # insert the new kernel node under /images ++ root[0].children.append(fake_kernel) ++ ++ # modify the default configuration ++ root[1].props[0].value = b'conf@2\x00' ++ # clone the first (only?) configuration ++ fake_conf = root[1][0].clone() ++ # rename and change kernel and fdt properties to select the crafted kernel ++ fake_conf.name = b'conf@2' ++ fake_conf.props[0].value = b'kernel@2\x00' ++ fake_conf.props[1].value = b'fdt@1\x00' ++ # insert the new configuration under /configurations ++ root[1].children.append(fake_conf) ++ ++ return root, strblock ++ ++def main(argv): ++ with open(argv[1], 'rb') as fp: ++ root, strblock = read_fdt(fp) ++ ++ print("Before:") ++ pretty_print(root, strblock) ++ ++ root, strblock = manipulate(root, strblock) ++ print("After:") ++ pretty_print(root, strblock) ++ ++ with open('blah', 'w+b') as fp: ++ write_fdt(root, strblock, fp) ++ ++if __name__ == '__main__': ++ import sys ++ main(sys.argv) ++# EOF +diff --git a/tools/Makefile b/tools/Makefile +index 345bc84..99be724 100644 +--- a/tools/Makefile ++++ b/tools/Makefile +@@ -59,6 +59,7 @@ hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include + + FIT_OBJS-$(CONFIG_FIT) := fit_common.o fit_image.o image-host.o common/image-fit.o + FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o ++FIT_CIPHER_OBJS-$(CONFIG_FIT_CIPHER) := common/image-cipher.o + + # The following files are synced with upstream DTC. + # Use synced versions from scripts/dtc/libfdt/. +@@ -75,6 +76,9 @@ RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ + rsa-sign.o rsa-verify.o rsa-checksum.o \ + rsa-mod-exp.o) + ++AES_OBJS-$(CONFIG_FIT_CIPHER) := $(addprefix lib/aes/, \ ++ aes-encrypt.o aes-decrypt.o) ++ + ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o + + # common objs for dumpimage and mkimage +@@ -82,6 +86,7 @@ dumpimage-mkimage-objs := aisimage.o \ + atmelimage.o \ + $(FIT_OBJS-y) \ + $(FIT_SIG_OBJS-y) \ ++ $(FIT_CIPHER_OBJS-y) \ + common/bootm.o \ + lib/crc32.o \ + default_image.o \ +@@ -116,7 +121,8 @@ dumpimage-mkimage-objs := aisimage.o \ + gpimage.o \ + gpimage-common.o \ + mtk_image.o \ +- $(RSA_OBJS-y) ++ $(RSA_OBJS-y) \ ++ $(AES_OBJS-y) + + dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o + mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o +@@ -137,6 +143,12 @@ HOST_EXTRACFLAGS += -DCONFIG_FIT_SIGNATURE + HOST_EXTRACFLAGS += -DCONFIG_FIT_SIGNATURE_MAX_SIZE=$(CONFIG_FIT_SIGNATURE_MAX_SIZE) + endif + ++ifdef CONFIG_FIT_CIPHER ++# This affects include/image.h, but including the board config file ++# is tricky, so manually define this options here. ++HOST_EXTRACFLAGS += -DCONFIG_FIT_CIPHER ++endif ++ + ifdef CONFIG_SYS_U_BOOT_OFFS + HOSTCFLAGS_kwbimage.o += -DCONFIG_SYS_U_BOOT_OFFS=$(CONFIG_SYS_U_BOOT_OFFS) + endif +diff --git a/tools/fdt_host.h b/tools/fdt_host.h +index 99b009b..15c07c7 100644 +--- a/tools/fdt_host.h ++++ b/tools/fdt_host.h +@@ -27,6 +27,7 @@ + */ + int fdt_remove_unused_strings(const void *old, void *new); + +-int fit_check_sign(const void *working_fdt, const void *key); ++int fit_check_sign(const void *fit, const void *key, ++ const char *fit_uname_config); + + #endif /* __FDT_HOST_H__ */ +diff --git a/tools/fit_check_sign.c b/tools/fit_check_sign.c +index 4528743..9375d5c 100644 +--- a/tools/fit_check_sign.c ++++ b/tools/fit_check_sign.c +@@ -41,6 +41,7 @@ int main(int argc, char **argv) + void *fit_blob; + char *fdtfile = NULL; + char *keyfile = NULL; ++ char *config_name = NULL; + char cmdname[256]; + int ret; + void *key_blob; +@@ -48,7 +49,7 @@ int main(int argc, char **argv) + + strncpy(cmdname, *argv, sizeof(cmdname) - 1); + cmdname[sizeof(cmdname) - 1] = '\0'; +- while ((c = getopt(argc, argv, "f:k:")) != -1) ++ while ((c = getopt(argc, argv, "f:k:c:")) != -1) + switch (c) { + case 'f': + fdtfile = optarg; +@@ -56,6 +57,9 @@ int main(int argc, char **argv) + case 'k': + keyfile = optarg; + break; ++ case 'c': ++ config_name = optarg; ++ break; + default: + usage(cmdname); + break; +@@ -78,7 +82,7 @@ int main(int argc, char **argv) + return EXIT_FAILURE; + + image_set_host_blob(key_blob); +- ret = fit_check_sign(fit_blob, key_blob); ++ ret = fit_check_sign(fit_blob, key_blob, config_name); + if (!ret) { + ret = EXIT_SUCCESS; + fprintf(stderr, "Signature check OK\n"); +diff --git a/tools/fit_image.c b/tools/fit_image.c +index 0201cc4..e6a5ae4 100644 +--- a/tools/fit_image.c ++++ b/tools/fit_image.c +@@ -58,6 +58,14 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, + ret = fit_set_timestamp(ptr, 0, time); + } + ++ if (!ret) { ++ ret = fit_cipher_data(params->keydir, dest_blob, ptr, ++ params->comment, ++ params->require_keys, ++ params->engine_id, ++ params->cmdname); ++ } ++ + if (!ret) { + ret = fit_add_verification_data(params->keydir, dest_blob, ptr, + params->comment, +@@ -74,7 +82,6 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, + err_keydest: + munmap(ptr, sbuf.st_size); + close(tfd); +- + return ret; + } + +@@ -621,6 +628,62 @@ err_no_fd: + return ret; + } + ++static int copyfile(const char *src, const char *dst) ++{ ++ int fd_src = -1, fd_dst = -1; ++ void *buf = NULL; ++ ssize_t size; ++ size_t count; ++ int ret = -1; ++ ++ fd_src = open(src, O_RDONLY); ++ if (fd_src < 0) { ++ printf("Can't open file %s (%s)\n", src, strerror(errno)); ++ goto out; ++ } ++ ++ fd_dst = open(dst, O_WRONLY | O_CREAT, 0700); ++ if (fd_dst < 0) { ++ printf("Can't open file %s (%s)\n", dst, strerror(errno)); ++ goto out; ++ } ++ ++ buf = malloc(512); ++ if (!buf) { ++ printf("Can't allocate buffer to copy file\n"); ++ goto out; ++ } ++ ++ while (1) { ++ size = read(fd_src, buf, 512); ++ if (size < 0) { ++ printf("Can't read file %s\n", src); ++ goto out; ++ } ++ if (!size) ++ break; ++ ++ count = size; ++ size = write(fd_dst, buf, count); ++ if (size < 0) { ++ printf("Can't write file %s\n", dst); ++ goto out; ++ } ++ } ++ ++ ret = 0; ++ ++ out: ++ if (fd_src >= 0) ++ close(fd_src); ++ if (fd_dst >= 0) ++ close(fd_dst); ++ if (buf) ++ free(buf); ++ ++ return ret; ++} ++ + /** + * fit_handle_file - main FIT file processing function + * +@@ -636,6 +699,7 @@ err_no_fd: + static int fit_handle_file(struct image_tool_params *params) + { + char tmpfile[MKIMAGE_MAX_TMPFILE_LEN]; ++ char bakfile[MKIMAGE_MAX_TMPFILE_LEN + 4] = {0}; + char cmd[MKIMAGE_MAX_DTC_CMDLINE_LEN]; + size_t size_inc; + int ret; +@@ -670,6 +734,7 @@ static int fit_handle_file(struct image_tool_params *params) + snprintf(cmd, sizeof(cmd), "cp \"%s\" \"%s\"", + params->imagefile, tmpfile); + } ++ + if (*cmd && system(cmd) == -1) { + fprintf (stderr, "%s: system(%s) failed: %s\n", + params->cmdname, cmd, strerror(errno)); +@@ -681,6 +746,14 @@ static int fit_handle_file(struct image_tool_params *params) + if (ret) + goto err_system; + ++ /* ++ * Copy the tmpfile to bakfile, then in the following loop ++ * we copy bakfile to tmpfile. So we always start from the ++ * beginning. ++ */ ++ sprintf(bakfile, "%s%s", tmpfile, ".bak"); ++ rename(tmpfile, bakfile); ++ + /* + * Set hashes for images in the blob. Unfortunately we may need more + * space in either FDT, so keep trying until we succeed. +@@ -692,6 +765,11 @@ static int fit_handle_file(struct image_tool_params *params) + * steps of this loop is enough to sign with several keys. + */ + for (size_inc = 0; size_inc < 64 * 1024; size_inc += 1024) { ++ if (copyfile(bakfile, tmpfile) < 0) { ++ printf("Can't copy %s to %s\n", bakfile, tmpfile); ++ ret = -EIO; ++ break; ++ } + ret = fit_add_file_data(params, size_inc, tmpfile); + if (!ret || ret != -ENOSPC) + break; +@@ -715,13 +793,16 @@ static int fit_handle_file(struct image_tool_params *params) + params->cmdname, tmpfile, params->imagefile, + strerror (errno)); + unlink (tmpfile); ++ unlink(bakfile); + unlink (params->imagefile); + return EXIT_FAILURE; + } ++ unlink(bakfile); + return EXIT_SUCCESS; + + err_system: + unlink(tmpfile); ++ unlink(bakfile); + return -1; + } + +diff --git a/tools/image-host.c b/tools/image-host.c +index 88b3295..8da39ac 100644 +--- a/tools/image-host.c ++++ b/tools/image-host.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + /** + * fit_set_hash_value - set hash value in requested has node +@@ -170,7 +171,7 @@ static int fit_image_setup_sig(struct image_sign_info *info, + + memset(info, '\0', sizeof(*info)); + info->keydir = keydir; +- info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); ++ info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + info->fit = fit; + info->node_offset = noffset; + info->name = strdup(algo_name); +@@ -249,7 +250,7 @@ static int fit_image_process_sig(const char *keydir, void *keydest, + free(value); + + /* Get keyname again, as FDT has changed and invalidated our pointer */ +- info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); ++ info.keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + + /* + * Write the public key into the supplied FDT file; this might fail +@@ -268,6 +269,262 @@ static int fit_image_process_sig(const char *keydir, void *keydest, + return 0; + } + ++static int fit_image_read_data(char *filename, unsigned char *data, ++ int expected_size) ++{ ++ struct stat sbuf; ++ int fd, ret = -1; ++ ssize_t n; ++ ++ /* Open file */ ++ fd = open(filename, O_RDONLY | O_BINARY); ++ if (fd < 0) { ++ printf("Can't open file %s (err=%d => %s)\n", ++ filename, errno, strerror(errno)); ++ return -1; ++ } ++ ++ /* Compute file size */ ++ if (fstat(fd, &sbuf) < 0) { ++ printf("Can't fstat file %s (err=%d => %s)\n", ++ filename, errno, strerror(errno)); ++ goto err; ++ } ++ ++ /* Check file size */ ++ if (sbuf.st_size != expected_size) { ++ printf("File %s don't have the expected size (size=%ld, expected=%d)\n", ++ filename, sbuf.st_size, expected_size); ++ goto err; ++ } ++ ++ /* Read data */ ++ n = read(fd, data, sbuf.st_size); ++ if (n < 0) { ++ printf("Can't read file %s (err=%d => %s)\n", ++ filename, errno, strerror(errno)); ++ goto err; ++ } ++ ++ /* Check that we have read all the file */ ++ if (n != sbuf.st_size) { ++ printf("Can't read all file %s (read %ld bytes, expexted %ld)\n", ++ filename, n, sbuf.st_size); ++ goto err; ++ } ++ ++ ret = 0; ++ ++err: ++ close(fd); ++ return ret; ++} ++ ++static int fit_image_setup_cipher(struct image_cipher_info *info, ++ const char *keydir, void *fit, ++ const char *image_name, int image_noffset, ++ const char *node_name, int noffset) ++{ ++ char *algo_name; ++ char filename[128]; ++ int ret = -1; ++ ++ if (fit_image_cipher_get_algo(fit, noffset, &algo_name)) { ++ printf("Can't get algo name for cipher '%s' in image '%s'\n", ++ node_name, image_name); ++ goto out; ++ } ++ ++ info->keydir = keydir; ++ ++ /* Read the key name */ ++ info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); ++ if (!info->keyname) { ++ printf("Can't get key name for cipher '%s' in image '%s'\n", ++ node_name, image_name); ++ goto out; ++ } ++ ++ /* Read the IV name */ ++ info->ivname = fdt_getprop(fit, noffset, "iv-name-hint", NULL); ++ if (!info->ivname) { ++ printf("Can't get iv name for cipher '%s' in image '%s'\n", ++ node_name, image_name); ++ goto out; ++ } ++ ++ info->fit = fit; ++ info->node_noffset = noffset; ++ info->name = algo_name; ++ ++ info->cipher = image_get_cipher_algo(algo_name); ++ if (!info->cipher) { ++ printf("Can't get algo for cipher '%s'\n", image_name); ++ goto out; ++ } ++ ++ /* Read the key in the file */ ++ snprintf(filename, sizeof(filename), "%s/%s%s", ++ info->keydir, info->keyname, ".bin"); ++ info->key = malloc(info->cipher->key_len); ++ if (!info->key) { ++ printf("Can't allocate memory for key\n"); ++ ret = -1; ++ goto out; ++ } ++ ret = fit_image_read_data(filename, (unsigned char *)info->key, ++ info->cipher->key_len); ++ if (ret < 0) ++ goto out; ++ ++ /* Read the IV in the file */ ++ snprintf(filename, sizeof(filename), "%s/%s%s", ++ info->keydir, info->ivname, ".bin"); ++ info->iv = malloc(info->cipher->iv_len); ++ if (!info->iv) { ++ printf("Can't allocate memory for iv\n"); ++ ret = -1; ++ goto out; ++ } ++ ret = fit_image_read_data(filename, (unsigned char *)info->iv, ++ info->cipher->iv_len); ++ ++ out: ++ return ret; ++} ++ ++int fit_image_write_cipher(void *fit, int image_noffset, int noffset, ++ const void *data, size_t size, ++ unsigned char *data_ciphered, int data_ciphered_len) ++{ ++ int ret = -1; ++ ++ /* Remove unciphered data */ ++ ret = fdt_delprop(fit, image_noffset, FIT_DATA_PROP); ++ if (ret) { ++ printf("Can't remove data (err = %d)\n", ret); ++ goto out; ++ } ++ ++ /* Add ciphered data */ ++ ret = fdt_setprop(fit, image_noffset, FIT_DATA_PROP, ++ data_ciphered, data_ciphered_len); ++ if (ret) { ++ printf("Can't add ciphered data (err = %d)\n", ret); ++ goto out; ++ } ++ ++ /* add non ciphered data size */ ++ ret = fdt_setprop_u32(fit, image_noffset, "data-size-unciphered", size); ++ if (ret) { ++ printf("Can't add unciphered data size (err = %d)\n", ret); ++ goto out; ++ } ++ ++ out: ++ return ret; ++} ++ ++static int ++fit_image_process_cipher(const char *keydir, void *keydest, void *fit, ++ const char *image_name, int image_noffset, ++ const char *node_name, int node_noffset, ++ const void *data, size_t size, ++ const char *cmdname) ++{ ++ struct image_cipher_info info; ++ unsigned char *data_ciphered = NULL; ++ int data_ciphered_len; ++ int ret; ++ ++ memset(&info, 0, sizeof(info)); ++ ++ ret = fit_image_setup_cipher(&info, keydir, fit, image_name, ++ image_noffset, node_name, node_noffset); ++ if (ret) ++ goto out; ++ ++ ret = info.cipher->encrypt(&info, data, size, ++ &data_ciphered, &data_ciphered_len); ++ if (ret) ++ goto out; ++ ++ /* ++ * Write the public key into the supplied FDT file; this might fail ++ * several times, since we try signing with successively increasing ++ * size values ++ */ ++ if (keydest) { ++ ret = info.cipher->add_cipher_data(&info, keydest); ++ if (ret) { ++ printf("Failed to add verification data for cipher '%s' in image '%s'\n", ++ info.keyname, image_name); ++ goto out; ++ } ++ } ++ ++ ret = fit_image_write_cipher(fit, image_noffset, node_noffset, ++ data, size, ++ data_ciphered, data_ciphered_len); ++ ++ out: ++ free(data_ciphered); ++ free((void *)info.key); ++ free((void *)info.iv); ++ return ret; ++} ++ ++int fit_image_cipher_data(const char *keydir, void *keydest, ++ void *fit, int image_noffset, const char *comment, ++ int require_keys, const char *engine_id, ++ const char *cmdname) ++{ ++ const char *image_name; ++ const void *data; ++ size_t size; ++ int node_noffset; ++ ++ /* Get image name */ ++ image_name = fit_get_name(fit, image_noffset, NULL); ++ if (!image_name) { ++ printf("Can't get image name\n"); ++ return -1; ++ } ++ ++ /* Get image data and data length */ ++ if (fit_image_get_data(fit, image_noffset, &data, &size)) { ++ printf("Can't get image data/size\n"); ++ return -1; ++ } ++ ++ /* Process all hash subnodes of the component image node */ ++ for (node_noffset = fdt_first_subnode(fit, image_noffset); ++ node_noffset >= 0; ++ node_noffset = fdt_next_subnode(fit, node_noffset)) { ++ const char *node_name; ++ int ret = 0; ++ ++ node_name = fit_get_name(fit, node_noffset, NULL); ++ if (!node_name) { ++ printf("Can't get node name\n"); ++ return -1; ++ } ++ ++ if (IMAGE_ENABLE_ENCRYPT && keydir && ++ !strncmp(node_name, FIT_CIPHER_NODENAME, ++ strlen(FIT_CIPHER_NODENAME))) ++ ret = fit_image_process_cipher(keydir, keydest, ++ fit, image_name, ++ image_noffset, ++ node_name, node_noffset, ++ data, size, cmdname); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + /** + * fit_image_add_verification_data() - calculate/set verig. data for image node + * +@@ -630,7 +887,7 @@ static int fit_config_process_sig(const char *keydir, void *keydest, + free(region_prop); + + /* Get keyname again, as FDT has changed and invalidated our pointer */ +- info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); ++ info.keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL); + + /* Write the public key into the supplied FDT file */ + if (keydest) { +@@ -675,6 +932,41 @@ static int fit_config_add_verification_data(const char *keydir, void *keydest, + return 0; + } + ++int fit_cipher_data(const char *keydir, void *keydest, void *fit, ++ const char *comment, int require_keys, ++ const char *engine_id, const char *cmdname) ++{ ++ int images_noffset; ++ int noffset; ++ int ret; ++ ++ /* Find images parent node offset */ ++ images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH); ++ if (images_noffset < 0) { ++ printf("Can't find images parent node '%s' (%s)\n", ++ FIT_IMAGES_PATH, fdt_strerror(images_noffset)); ++ return images_noffset; ++ } ++ ++ /* Process its subnodes, print out component images details */ ++ for (noffset = fdt_first_subnode(fit, images_noffset); ++ noffset >= 0; ++ noffset = fdt_next_subnode(fit, noffset)) { ++ /* ++ * Direct child node of the images parent node, ++ * i.e. component image node. ++ */ ++ ret = fit_image_cipher_data(keydir, keydest, ++ fit, noffset, comment, ++ require_keys, engine_id, ++ cmdname); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ + int fit_add_verification_data(const char *keydir, void *keydest, void *fit, + const char *comment, int require_keys, + const char *engine_id, const char *cmdname) +@@ -734,19 +1026,22 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit, + } + + #ifdef CONFIG_FIT_SIGNATURE +-int fit_check_sign(const void *fit, const void *key) ++int fit_check_sign(const void *fit, const void *key, ++ const char *fit_uname_config) + { + int cfg_noffset; + int ret; + +- cfg_noffset = fit_conf_get_node(fit, NULL); ++ cfg_noffset = fit_conf_get_node(fit, fit_uname_config); + if (!cfg_noffset) + return -1; + +- printf("Verifying Hash Integrity ... "); ++ printf("Verifying Hash Integrity for node '%s'... ", ++ fdt_get_name(fit, cfg_noffset, NULL)); + ret = fit_config_verify(fit, cfg_noffset); + if (ret) + return ret; ++ printf("Verified OK, loading images\n"); + ret = bootm_host_load_images(fit, cfg_noffset); + + return ret;