diff --git a/Makefile b/Makefile index 2c4cc7609..0b8f3fce3 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ SHELL = /bin/bash .SHELLFLAGS += -ex -KERNEL_VERSION ?= 6.12.41 +KERNEL_VERSION ?= 6.12.94 KERNEL_ABISUFFIX ?= +deb13 KERNEL_SUBVERSION ?= 1 KERNEL_FEATURESET ?= sonic diff --git a/patch/kconfig-exclusions b/patch/kconfig-exclusions deleted file mode 100644 index 756b4ab08..000000000 --- a/patch/kconfig-exclusions +++ /dev/null @@ -1,70 +0,0 @@ -[common] -# Unset STRICT_DEVMEM according to Broadcom's requirement and for CENTEC arm64 arch -CONFIG_STRICT_DEVMEM -# Unset classid and priority network cgroups due to conflict between cgroups v1 and v2 -CONFIG_CGROUP_NET_CLASSID -CONFIG_NET_CLS_CGROUP -CONFIG_NETFILTER_XT_MATCH_CGROUP -CONFIG_CGROUP_NET_PRIO -# Unset to enable UBIFS on flash -CONFIG_MTD_SPI_NOR_USE_4K_SECTORS -###-> mellanox_common-start -###-> mellanox_common-end -CONFIG_DRM -CONFIG_VIDEO_DEV -CONFIG_MEDIA_SUPPORT -CONFIG_MEDIA_CONTROLLER -CONFIG_DVB_CORE -CONFIG_MEDIA_TUNER -CONFIG_MEDIA_ATTACH - -[amd64] -CONFIG_AMD_MEM_ENCRYPT -# Unset X86_PAT according to Broadcom's requirement -CONFIG_X86_PAT -CONFIG_MLXSW_PCI -###-> mellanox_amd64-start -###-> mellanox_amd64-end - -[arm64] -CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU - -[marvell-prestera-arm64] -CONFIG_UBIFS_FS_ZSTD -CONFIG_CRYPTO_DEV_SAFEXCEL - -[pensando] -CONFIG_ARM64_VA_BITS_48 - -[armhf] - -[marvell-prestera-armhf] -CONFIG_ARCH_VIRT -CONFIG_ARCH_BCM -CONFIG_ARCH_EXYNOS -CONFIG_ARCH_HIGHBANK -CONFIG_ARCH_MXC -CONFIG_ARCH_MESON -CONFIG_ARCH_OMAP3 -CONFIG_ARCH_OMAP4 -CONFIG_SOC_OMAP5 -CONFIG_SOC_AM33XX -CONFIG_SOC_DRA7XX -CONFIG_ARCH_ROCKCHIP -CONFIG_ARCH_SOCFPGA -CONFIG_ARCH_SUNXI -CONFIG_ARCH_TEGRA -CONFIG_ARCH_VEXPRESS -CONFIG_ARCH_WM8850 -CONFIG_FW_CFG_SYSFS -CONFIG_VORTEX -CONFIG_NTP_PPS -CONFIG_SENSORS_MAX6620 -CONFIG_SND_LX6464ES -CONFIG_EFI -CONFIG_LOCK_DOWN_KERNEL -CONFIG_SECONDARY_TRUSTED_KEYRING -CONFIG_SYSTEM_BLACKLIST_KEYRING - -[mellanox-arm64] - diff --git a/patch/kconfig-force-inclusions b/patch/kconfig-force-inclusions deleted file mode 100644 index e14876637..000000000 --- a/patch/kconfig-force-inclusions +++ /dev/null @@ -1,3 +0,0 @@ -[arm64] -CONFIG_COMPAT_VDSO=y -CONFIG_THUMB2_COMPAT_VDSO=y diff --git a/patch/kconfig-inclusions b/patch/kconfig-inclusions deleted file mode 100644 index 11b64f57d..000000000 --- a/patch/kconfig-inclusions +++ /dev/null @@ -1,254 +0,0 @@ -[common] -CONFIG_LOG_BUF_SHIFT=20 -CONFIG_RTC_INTF_DEV_UIE_EMUL=y -###-> mellanox_common-start -###-> mellanox_common-end - -[amd64] -CONFIG_SATA_MOBILE_LPM_POLICY=1 -# For Arista -CONFIG_I2C_MUX_PCA9541=m -CONFIG_SENSORS_MAX6697=m -#CONFIG_SENSORS_DPS1900=m # TODO: enable once the DPS1900 patch is re-enabled -CONFIG_PCI_REASSIGN_PREF_MEM=y -# For cig-cs6436 -CONFIG_SERIAL_8250_LPSS=m -# SFF_8436 -CONFIG_EEPROM_SFF_8436=m -# For Dell S6000 -CONFIG_SENSORS_MAX6620=m -CONFIG_PMBUS=m -CONFIG_SENSORS_PMBUS=m -#CONFIG_SENSORS_DNI_DPS460=m # TODO: enable once the DPS460 patch is re-enabled -CONFIG_I2C_MUX_GPIO=m -CONFIG_GPIO_SYSFS=y -CONFIG_GPIO_SCH=m -# For Dell Z9100 -CONFIG_I2C_MUX_PCA954x=m -# For Ingrasys S9100 -CONFIG_I2C_GPIO=m -CONFIG_GPIO_PCA953X=m -# For Inventec d7032 -CONFIG_GPIO_ICH=m -# For mitac ly1200 -CONFIG_SENSORS_MAX31790=m -# For optoe -CONFIG_EEPROM_OPTOE=m -###-> mellanox_amd64-start -CONFIG_X86_AMD_PLATFORM_DEVICE=y -CONFIG_MCTP=y -CONFIG_DMI_SYSFS=y -CONFIG_OF=y -CONFIG_EEPROM_AT24=m -CONFIG_AMD_XGBE=m -CONFIG_IGB=m -CONFIG_MLXSW_CORE=m -CONFIG_MARVELL_PHY=m -CONFIG_MARVELL_10G_PHY=m -CONFIG_SERIAL_8250_DW=y -CONFIG_HW_RANDOM_AMD=m -CONFIG_I2C_CHARDEV=m -CONFIG_I2C_MUX=m -CONFIG_I2C_SMBUS=m -CONFIG_I2C_I801=m -CONFIG_I2C_DESIGNWARE_PCI=m -CONFIG_I2C_MLXCPLD=m -CONFIG_I2C_SLAVE=y -CONFIG_PINCTRL=y -CONFIG_GPIOLIB=y -CONFIG_SENSORS_K10TEMP=m -CONFIG_SENSORS_DRIVETEMP=m -CONFIG_SENSORS_CORETEMP=m -CONFIG_SENSORS_JC42=m -CONFIG_SENSORS_POWR1220=m -CONFIG_SENSORS_LM75=m -CONFIG_SENSORS_EMC1403=m -CONFIG_SENSORS_EMC2305=m -CONFIG_SENSORS_STTS751=m -CONFIG_SENSORS_TMP102=m -CONFIG_SENSORS_TMP421=m -CONFIG_THERMAL_NETLINK=y -CONFIG_INTEL_PCH_THERMAL=m -CONFIG_WATCHDOG_SYSFS=y -CONFIG_LPC_ICH=m -CONFIG_MFD_INTEL_LPSS_PCI=y -CONFIG_LEDS_MLXREG=m -CONFIG_LEDS_TRIGGER_TIMER=m -CONFIG_DW_DMAC_PCI=y -CONFIG_MELLANOX_PLATFORM=y -CONFIG_MLX_PLATFORM=m -CONFIG_NET_DEVLINK=y -CONFIG_I2C_MUX_REG=m -CONFIG_I2C_MUX_MLXCPLD=m -CONFIG_REGMAP_I2C=m -CONFIG_IIO_SYSFS_TRIGGER=m -CONFIG_NVME_HWMON=y -CONFIG_MLXSW_CORE_HWMON=y -CONFIG_MLXSW_I2C=m -CONFIG_MLXSW_MINIMAL=m -CONFIG_MLXSW_CORE_THERMAL=n -CONFIG_MLXREG_HOTPLUG=m -CONFIG_MLXREG_IO=m -CONFIG_MLX_WDT=m -CONFIG_MLXREG_LC=m -#CONFIG_MLXREG_DPU=m -CONFIG_SENSORS_MLXREG_FAN=m -CONFIG_GPIO_GENERIC=m -CONFIG_MAX1363=m -CONFIG_SENSORS_TPS53679=m -CONFIG_SENSORS_XDPE122=m -CONFIG_SENSORS_MP2975=m -#CONFIG_SENSORS_MP2855=m -CONFIG_SENSORS_MP2888=m -CONFIG_SENSORS_MP2891=m -CONFIG_CPU_THERMAL=y -CONFIG_IGB_HWMON=y -CONFIG_MFD_CORE=y -CONFIG_MFD_INTEL_LPSS=y -CONFIG_TI_ADS1015=m -CONFIG_PINCTRL_INTEL=y -CONFIG_PINCTRL_CANNONLAKE=m -CONFIG_PINCTRL_DENVERTON=m -CONFIG_NVSW_SN2201=m -CONFIG_I2C_DESIGNWARE_PLATFORM=m -CONFIG_I2C_DESIGNWARE_BAYTRAIL=y -CONFIG_I2C_DESIGNWARE_CORE=m -CONFIG_SPI_PXA2XX=m -CONFIG_SENSORS_XDPE152=m -CONFIG_SENSORS_IIO_HWMON=m -CONFIG_SENSORS_LM25066=m -CONFIG_SENSORS_UCD9000=m -CONFIG_SENSORS_UCD9200=m -CONFIG_THERMAL_OF=y -CONFIG_MCTP_FLOWS=y -CONFIG_MCTP_TRANSPORT_I2C=m -CONFIG_I2C_SLAVE_EEPROM=m -#CONFIG_I2C_ASF=m -CONFIG_SPI_AMD=m -CONFIG_PINCTRL_AMD=y -CONFIG_EDAC_AMD64=m -CONFIG_AMD_XGBE_DCB=y -CONFIG_SENSORS_ISL68137=m -###-> mellanox_amd64-end -# For Cisco 8000 -CONFIG_PHYLIB=m -CONFIG_OF_GPIO=y -CONFIG_OF_MDIO=m -CONFIG_MDIO_BUS_MUX_GPIO=m -CONFIG_MDIO_BUS_MUX=m -CONFIG_REGMAP_MMIO=y -CONFIG_TI_TLC4541=m -CONFIG_SENSORS_LTC2978=m -CONFIG_SENSORS_LTC2945=m -CONFIG_SENSORS_TPS40422=m -CONFIG_SENSORS_ADM1275=m -CONFIG_SPI_INTEL_PLATFORM=m -CONFIG_SPI_INTEL=m -# For pensando -CONFIG_IONIC=m -# For AMD Ryzen Embedded processors -CONFIG_PCI_ENDPOINT=y -CONFIG_PCIE_DW=y -CONFIG_PCIE_DW_HOST=y -CONFIG_PCIE_DW_EP=y -CONFIG_PCIE_DW_PLAT=y -CONFIG_PCIE_DW_PLAT_HOST=y -CONFIG_PCIE_DW_PLAT_EP=y -# For Micas -CONFIG_MDIO_BITBANG=m -CONFIG_MDIO_GPIO=m -CONFIG_SPI_GPIO=m -CONFIG_SENSORS_INA3221=m -# For Nokia -CONFIG_RTC_DRV_DS1307=m -CONFIG_SENSORS_TMP464=m -CONFIG_RTC_DRV_PCF85363=m -# For Nexthop -CONFIG_PCI_STUB=y -CONFIG_SENSORS_ADM1266=m - -[arm64] -# For enabling SDEI -CONFIG_ARM_SDE_INTERFACE=y -# For Marvell -CONFIG_EEPROM_SFF_8436=m -CONFIG_EEPROM_OPTOE=m -CONFIG_I2C_MUX_GPIO=m -CONFIG_MTD_RAW_NAND=m -CONFIG_MTD_NAND_ECC_SW_BCH=y -CONFIG_MTD_NAND_MARVELL=m -CONFIG_SENSORS_EMC2305=m -CONFIG_PHY_MVEBU_CP110_COMPHY=y -CONFIG_SPI_ORION=m -CONFIG_ARM_SMC_WATCHDOG=m -# For pensando -CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y -CONFIG_DEVTMPFS_MOUNT=y -CONFIG_EMBEDDED=y -CONFIG_GENERIC_IRQ_DEBUGFS=y -CONFIG_GPIO_DWAPB=m -CONFIG_IP_PNP=y -CONFIG_IP_PNP_BOOTP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IRQSOFF_TRACER=y -CONFIG_KALLSYMS_ALL=y -CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y -CONFIG_SCHED_TRACER=y -CONFIG_MEMTEST=y -CONFIG_MMC_DEBUG=y -CONFIG_MMC_SDHCI_CADENCE=m -CONFIG_PMBUS=m -CONFIG_SENSORS_PMBUS=m -CONFIG_SENSORS_LTC2978=m -CONFIG_SENSORS_TPS53679=m -CONFIG_RTC_DRV_PCF85363=m -CONFIG_RTC_CLASS=y -CONFIG_SPI_CADENCE_QUADSPI=m -CONFIG_SPI_DESIGNWARE=m -CONFIG_SPI_DW_MMIO=m -CONFIG_SQUASHFS_DECOMP_MULTI=y -CONFIG_PREEMPTIRQ_TRACEPOINTS=y -CONFIG_RING_BUFFER_ALLOW_SWAP=y -CONFIG_KEXEC_FILE=y - -[marvell-prestera-arm64] - -[pensando] -CONFIG_ARCH_PENSANDO=y -CONFIG_ARCH_PENSANDO_ELBA_SOC=y -CONFIG_PENSANDO_SOC_PENFW=y -CONFIG_EDAC_ELBA=y -CONFIG_IRQ_PENSANDO=y -CONFIG_MFD_PENSANDO_ELBASR=y -CONFIG_RESET_ELBASR=y -CONFIG_UIO_PCIEMAC=m -CONFIG_UIO_PENMSI1=m -CONFIG_UIO_PENGIC=m -CONFIG_UIO_PENMSI=m -CONFIG_ARM64_VA_BITS_39=y -CONFIG_COMMON_CLK_CS2000_CP=y -CONFIG_I2C_RD1173=y -CONFIG_CONTEXT_TRACKING=y -CONFIG_NO_HZ_FULL=y -CONFIG_RCU_NOCB_CPU=y -CONFIG_VIRT_CPU_ACCOUNTING=y -CONFIG_VIRT_CPU_ACCOUNTING_GEN=y -CONFIG_RTC_DRV_ELBA_CPLD=y - -[armhf] -CONFIG_EEPROM_SFF_8436=m -CONFIG_EEPROM_OPTOE=m -CONFIG_I2C_MUX_GPIO=m - -[marvell-prestera-armhf] -CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_OF_PARTS=y - -[nvidia-bluefield] -CONFIG_MMC_DW=y -CONFIG_MMC_DW_PLTFM=y -CONFIG_MMC_DW_BLUEFIELD=y -CONFIG_EFIVAR_FS=y - -[mellanox-arm64] - diff --git a/patch/kconfig-secure-boot-exclusions b/patch/kconfig-secure-boot-exclusions deleted file mode 100644 index 68fb8c58f..000000000 --- a/patch/kconfig-secure-boot-exclusions +++ /dev/null @@ -1,20 +0,0 @@ -[common] - -[amd64] -CONFIG_MODULE_SIG_SHA256 -# For mellanox -CONFIG_SECURITY_LOCKDOWN_LSM -CONFIG_SECURITY_LOCKDOWN_LSM_EARLY -CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE -CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT - -[arm64] -CONFIG_MODULE_SIG_SHA256 -# For mellanox -CONFIG_SECURITY_LOCKDOWN_LSM -CONFIG_SECURITY_LOCKDOWN_LSM_EARLY -CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE -CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT - -[armhf] - diff --git a/patch/kconfig-secure-boot-inclusions b/patch/kconfig-secure-boot-inclusions deleted file mode 100644 index cfd9e900c..000000000 --- a/patch/kconfig-secure-boot-inclusions +++ /dev/null @@ -1,18 +0,0 @@ -[common] - -[amd64] -CONFIG_SYSTEM_TRUSTED_KEYS="debian/certs/debian-uefi-certs.pem" -CONFIG_MODULE_SIG_HASH="sha512" -CONFIG_MODULE_SIG_SHA512=y -CONFIG_KEXEC_SIG_FORCE=y -CONFIG_MODULE_SIG_FORCE=y - -[arm64] -CONFIG_SYSTEM_TRUSTED_KEYS="debian/certs/debian-uefi-certs.pem" -CONFIG_MODULE_SIG_HASH="sha512" -CONFIG_MODULE_SIG_SHA512=y -CONFIG_KEXEC_SIG=y -CONFIG_MODULE_SIG_FORCE=y - -[armhf] - diff --git a/patches-sonic/0001-Add-device-tree-for-Nokia-BMC-H6-128-platform.patch b/patches-sonic/0001-Add-device-tree-for-Nokia-BMC-H6-128-platform.patch index 09d450a3a..eb6b4de74 100644 --- a/patches-sonic/0001-Add-device-tree-for-Nokia-BMC-H6-128-platform.patch +++ b/patches-sonic/0001-Add-device-tree-for-Nokia-BMC-H6-128-platform.patch @@ -28,9 +28,9 @@ index 3e904b273f1f..5caf80a7ff69 100644 ast2700-evb.dtb \ nexthop-b27-r0.dtb \ + nokia-ast2700-h6-128-r0.dtb \ + ast2700-evb_ast1700-evb.dtb \ + ast2700-evb_ast1700-evb-dual.dtb \ ast2700-raw.dtb \ - ast2700-evb-s0.dtb \ - ast2700-evb-s1.dtb \ diff --git a/arch/arm64/boot/dts/aspeed/nokia-ast2700-h6-128-r0.dts b/arch/arm64/boot/dts/aspeed/nokia-ast2700-h6-128-r0.dts new file mode 100644 index 000000000000..79b5bd7fb94a diff --git a/patches-sonic/0001-mmc-sdhci-cadence-Add-CN10K-eMMC-support.patch b/patches-sonic/0001-mmc-sdhci-cadence-Add-CN10K-eMMC-support.patch index aa878442f..9776e4650 100644 --- a/patches-sonic/0001-mmc-sdhci-cadence-Add-CN10K-eMMC-support.patch +++ b/patches-sonic/0001-mmc-sdhci-cadence-Add-CN10K-eMMC-support.patch @@ -2470,7 +2470,7 @@ index be1505e8c536..87022bc6d941 100644 }; static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { -@@ -431,12 +2281,32 @@ static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { +@@ -431,19 +2281,40 @@ static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { .pltfm_data = { .ops = &sdhci_elba_ops, }, @@ -2494,6 +2494,15 @@ index be1505e8c536..87022bc6d941 100644 + .cdns_data = &sdhci_cdns_sd4_data, }; + static const struct sdhci_cdns_drv_data sdhci_eyeq_drv_data = { + .pltfm_data = { +- .ops = &sdhci_cdns_ops, ++ .ops = &sdhci_cdns_sd4_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, ++ .cdns_data = NULL, + }; + -static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = { +static const struct sdhci_cdns_drv_data sdhci_cdns_sd6_drv_data = { + .init = sdhci_cdns_sd6_phy_probe, @@ -2698,9 +2707,9 @@ index be1505e8c536..87022bc6d941 100644 return ret; } -@@ -595,7 +2549,14 @@ static const struct of_device_id sdhci_cdns_match[] = { - .compatible = "amd,pensando-elba-sd4hc", - .data = &sdhci_elba_drv_data, +@@ -606,7 +2549,14 @@ static const struct of_device_id sdhci_cdns_match[] = { + .compatible = "mobileye,eyeq-sd4hc", + .data = &sdhci_eyeq_drv_data, }, - { .compatible = "cdns,sd4hc" }, + { diff --git a/patches-sonic/0002-arm64-dts-Update-cache-properties-for-marvell.patch b/patches-sonic/0002-arm64-dts-Update-cache-properties-for-marvell.patch deleted file mode 100644 index 4d6c1bb20..000000000 --- a/patches-sonic/0002-arm64-dts-Update-cache-properties-for-marvell.patch +++ /dev/null @@ -1,88 +0,0 @@ -From: Pierre Gondois -Date: Mon, 31 Oct 2022 10:20:16 +0100 -arm64: dts: Update cache properties for marvell - -The DeviceTree Specification v0.3 specifies that the cache node -'compatible' and 'cache-level' properties are 'required'. Cf. -s3.8 Multi-level and Shared Cache Nodes - -The recently added init_of_cache_level() function checks -these properties. Add them if missing. - -Signed-off-by: Pierre Gondois -Reviewed-by: Chris Packham -Signed-off-by: Gregory CLEMENT ---- - arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi | 1 + - arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi | 1 + - arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi | 2 ++ - arch/arm64/boot/dts/marvell/armada-ap807-quad.dtsi | 2 ++ - 4 files changed, 6 insertions(+) - -diff --git a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -index 44ed6f963..7308f7b6b 100644 ---- a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -+++ b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -@@ -49,6 +49,7 @@ cpu1: cpu@1 { - - l2: l2-cache { - compatible = "cache"; -+ cache-level = <2>; - }; - }; - -diff --git a/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi -index fcab5173f..990f70303 100644 ---- a/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi -+++ b/arch/arm64/boot/dts/marvell/armada-ap806-dual.dtsi -@@ -51,6 +51,7 @@ l2: l2-cache { - cache-size = <0x80000>; - cache-line-size = <64>; - cache-sets = <512>; -+ cache-level = <2>; - }; - }; - -diff --git a/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi -index 3db427122..a7b8e001c 100644 ---- a/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi -+++ b/arch/arm64/boot/dts/marvell/armada-ap806-quad.dtsi -@@ -81,6 +81,7 @@ l2_0: l2-cache0 { - cache-size = <0x80000>; - cache-line-size = <64>; - cache-sets = <512>; -+ cache-level = <2>; - }; - - l2_1: l2-cache1 { -@@ -88,6 +89,7 @@ l2_1: l2-cache1 { - cache-size = <0x80000>; - cache-line-size = <64>; - cache-sets = <512>; -+ cache-level = <2>; - }; - }; - }; -diff --git a/arch/arm64/boot/dts/marvell/armada-ap807-quad.dtsi b/arch/arm64/boot/dts/marvell/armada-ap807-quad.dtsi -index 68782f161..7740098fd 100644 ---- a/arch/arm64/boot/dts/marvell/armada-ap807-quad.dtsi -+++ b/arch/arm64/boot/dts/marvell/armada-ap807-quad.dtsi -@@ -81,6 +81,7 @@ l2_0: l2-cache0 { - cache-size = <0x80000>; - cache-line-size = <64>; - cache-sets = <512>; -+ cache-level = <2>; - }; - - l2_1: l2-cache1 { -@@ -88,6 +89,7 @@ l2_1: l2-cache1 { - cache-size = <0x80000>; - cache-line-size = <64>; - cache-sets = <512>; -+ cache-level = <2>; - }; - }; - }; --- -2.25.1 - diff --git a/patches-sonic/0002-net-bridge-vxlan-Protocol-field-in-bridge-fdb.patch b/patches-sonic/0002-net-bridge-vxlan-Protocol-field-in-bridge-fdb.patch index 22bfe9744..f49434b8c 100644 --- a/patches-sonic/0002-net-bridge-vxlan-Protocol-field-in-bridge-fdb.patch +++ b/patches-sonic/0002-net-bridge-vxlan-Protocol-field-in-bridge-fdb.patch @@ -217,7 +217,7 @@ index ffb0b61..7e08ae3 100644 hash_index = fdb_head_index(vxlan, addr, src_vni); spin_lock_bh(&vxlan->hash_lock[hash_index]); + if (protocol != RTPROT_UNSPEC) { -+ f = vxlan_find_mac(vxlan, addr, src_vni); ++ f = vxlan_find_mac_tx(vxlan, addr, src_vni); + if (!f || f->protocol != protocol) { + err = -ENOENT; + goto out; diff --git a/patches-sonic/0003-arm64-dts-marvell-Add-NAND-flash-controller-to-AC5.patch b/patches-sonic/0003-arm64-dts-marvell-Add-NAND-flash-controller-to-AC5.patch deleted file mode 100644 index b19946b94..000000000 --- a/patches-sonic/0003-arm64-dts-marvell-Add-NAND-flash-controller-to-AC5.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 58fe732052196777112fdba536fb9db5cdd42ec6 Mon Sep 17 00:00:00 2001 -From: Chris Packham -Date: Mon, 3 Jul 2023 15:50:43 +1200 -Subject: arm64: dts: marvell: Add NAND flash controller to AC5 - -The AC5/AC5X SoC has a NAND flash controller (NFC). Add this to -the base SoC dtsi file as a disabled node. The NFC integration -on the AC5/AC5X only supports SDR timing modes up to 3 so requires a -dedicated compatible property so this limitation can be enforced. - -Signed-off-by: Chris Packham -Signed-off-by: Gregory CLEMENT ---- - arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -diff --git a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -index 67c4688546b8..62d03ffa9485 100644 ---- a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -+++ b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -@@ -297,6 +297,16 @@ spi1: spi@805a8000 { - status = "disabled"; - }; - -+ nand: nand-controller@805b0000 { -+ compatible = "marvell,ac5-nand-controller"; -+ reg = <0x0 0x805b0000 0x0 0x00000054>; -+ #address-cells = <0x1>; -+ #size-cells = <0x0>; -+ interrupts = ; -+ clocks = <&nand_clock>; -+ status = "disabled"; -+ }; -+ - gic: interrupt-controller@80600000 { - compatible = "arm,gic-v3"; - #interrupt-cells = <3>; -@@ -319,5 +329,11 @@ spi_clock: spi-clock { - #clock-cells = <0>; - clock-frequency = <200000000>; - }; -+ -+ nand_clock: nand-clock { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <400000000>; -+ }; - }; - }; --- -2.25.1 - diff --git a/patches-sonic/0003-i2c-designware-Support-stuck-SDA-line-recovery.patch b/patches-sonic/0003-i2c-designware-Support-stuck-SDA-line-recovery.patch index 550f39abc..dc71eec8a 100644 --- a/patches-sonic/0003-i2c-designware-Support-stuck-SDA-line-recovery.patch +++ b/patches-sonic/0003-i2c-designware-Support-stuck-SDA-line-recovery.patch @@ -42,9 +42,9 @@ index 2d32896..59d1eba 100644 #define DW_IC_CLR_RESTART_DET 0xa8 +#define DW_IC_SCL_STUCK_AT_LOW 0xac +#define DW_IC_SDA_STUCK_AT_LOW 0xb0 + #define DW_IC_SMBUS_INTR_MASK 0xcc #define DW_IC_COMP_PARAM_1 0xf4 #define DW_IC_COMP_VERSION 0xf8 - #define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ @@ -98,6 +100,7 @@ #define DW_IC_INTR_GEN_CALL BIT(11) #define DW_IC_INTR_RESTART_DET BIT(12) diff --git a/patches-sonic/0004-arm64-dts-ac5-add-mmc-node-and-clock.patch b/patches-sonic/0004-arm64-dts-ac5-add-mmc-node-and-clock.patch deleted file mode 100644 index dddb78a76..000000000 --- a/patches-sonic/0004-arm64-dts-ac5-add-mmc-node-and-clock.patch +++ /dev/null @@ -1,84 +0,0 @@ -From cd40be983803830da19cb492b250abccf71ace7d Mon Sep 17 00:00:00 2001 -From: Elad Nachman -Date: Wed, 3 Jan 2024 19:28:03 +0200 -Subject: arm64: dts: ac5: add mmc node and clock - -Add mmc and mmc clock nodes to ac5 and ac5x device tree files - -Signed-off-by: Elad Nachman -Signed-off-by: Gregory CLEMENT ---- - arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi | 31 ++++++++++++++++++- - .../boot/dts/marvell/ac5-98dx35xx-rd.dts | 4 +++ - 2 files changed, 34 insertions(+), 1 deletion(-) - -diff --git a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -index b5e042b8e929..5591939e057b 100644 ---- a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -+++ b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi -@@ -77,7 +77,6 @@ soc { - #address-cells = <2>; - #size-cells = <2>; - ranges; -- dma-ranges; - - internal-regs@7f000000 { - #address-cells = <1>; -@@ -204,6 +203,30 @@ gpio1: gpio@18140 { - }; - }; - -+ mmc_dma: bus@80500000 { -+ compatible = "simple-bus"; -+ ranges; -+ #address-cells = <0x2>; -+ #size-cells = <0x2>; -+ reg = <0x0 0x80500000 0x0 0x100000>; -+ dma-ranges = <0x0 0x0 0x2 0x0 0x0 0x80000000>; -+ dma-coherent; -+ -+ sdhci: mmc@805c0000 { -+ compatible = "marvell,ac5-sdhci", -+ "marvell,armada-ap806-sdhci"; -+ reg = <0x0 0x805c0000 0x0 0x1000>; -+ interrupts = ; -+ clocks = <&emmc_clock>, <&cnm_clock>; -+ clock-names = "core", "axi"; -+ bus-width = <8>; -+ non-removable; -+ mmc-ddr-1_8v; -+ mmc-hs200-1_8v; -+ mmc-hs400-1_8v; -+ }; -+ }; -+ - /* - * Dedicated section for devices behind 32bit controllers so we - * can configure specific DMA mapping for them -@@ -335,5 +358,11 @@ nand_clock: nand-clock { - #clock-cells = <0>; - clock-frequency = <400000000>; - }; -+ -+ emmc_clock: emmc-clock { -+ compatible = "fixed-clock"; -+ #clock-cells = <0>; -+ clock-frequency = <400000000>; -+ }; - }; - }; -diff --git a/arch/arm64/boot/dts/marvell/ac5-98dx35xx-rd.dts b/arch/arm64/boot/dts/marvell/ac5-98dx35xx-rd.dts -index f0ebdb84eec9..0c973d7a215a 100644 ---- a/arch/arm64/boot/dts/marvell/ac5-98dx35xx-rd.dts -+++ b/arch/arm64/boot/dts/marvell/ac5-98dx35xx-rd.dts -@@ -99,3 +99,7 @@ parition@2 { - }; - }; - }; -+ -+&sdhci { -+ status = "okay"; -+}; --- -2.25.1 - diff --git a/patches-sonic/0005-arm64-traps-Call-platform-handler-for-do_serror.patch b/patches-sonic/0005-arm64-traps-Call-platform-handler-for-do_serror.patch index 9dbe58b22..77d9790c5 100644 --- a/patches-sonic/0005-arm64-traps-Call-platform-handler-for-do_serror.patch +++ b/patches-sonic/0005-arm64-traps-Call-platform-handler-for-do_serror.patch @@ -18,9 +18,9 @@ index 82cf1f8..1b22756 100644 --- a/arch/arm64/include/asm/traps.h +++ b/arch/arm64/include/asm/traps.h @@ -28,7 +28,7 @@ - void arm64_force_sig_fault_pkey(unsigned long far, const char *str, int pkey); - void arm64_force_sig_mceerr(int code, unsigned long far, short lsb, const char *str); - void arm64_force_sig_ptrace_errno_trap(int errno, unsigned long far, const char *str); + int reserved_fault_brk_handler(struct pt_regs *regs, unsigned long esr); + int kasan_brk_handler(struct pt_regs *regs, unsigned long esr); + int ubsan_brk_handler(struct pt_regs *regs, unsigned long esr); - +int platform_serror(struct pt_regs *regs, unsigned int esr); int early_brk64(unsigned long addr, unsigned long esr, struct pt_regs *regs); diff --git a/patches-sonic/0006-mmc-xenon-Add-ac5-support-via-bounce-buffer.patch b/patches-sonic/0006-mmc-xenon-Add-ac5-support-via-bounce-buffer.patch deleted file mode 100644 index 83e4e7793..000000000 --- a/patches-sonic/0006-mmc-xenon-Add-ac5-support-via-bounce-buffer.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 5d40213347480e3ab903d5438dbd0d6b0110e6b8 Mon Sep 17 00:00:00 2001 -From: Elad Nachman -Date: Thu, 4 Jan 2024 19:30:33 +0200 -Subject: mmc: xenon: Add ac5 support via bounce buffer - -AC5/X/IM SOCs has a variant of the Xenon eMMC controller, -in which only 31-bit of addressing pass from the controller -on the AXI bus. -Since we cannot guarantee that only buffers from the first 2GB -of memory will reach the driver, the driver is configured for -SDMA mode, without 64-bit mode, overriding the DMA mask to 34-bit -to support the DDR memory mapping, which starts at offset 8GB. - -Signed-off-by: Elad Nachman -Acked-by: Adrian Hunter -Link: https://lore.kernel.org/r/20240104173033.2836110-1-enachman@marvell.com -Signed-off-by: Ulf Hansson ---- - drivers/mmc/host/sdhci-xenon.c | 31 +++++++++++++++++++++++++++++++ - drivers/mmc/host/sdhci-xenon.h | 3 ++- - 2 files changed, 33 insertions(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c -index 25ba7aecc3be..0e52867f6e91 100644 ---- a/drivers/mmc/host/sdhci-xenon.c -+++ b/drivers/mmc/host/sdhci-xenon.c -@@ -18,6 +18,8 @@ - #include - #include - #include -+#include -+#include - - #include "sdhci-pltfm.h" - #include "sdhci-xenon.h" -@@ -422,6 +424,7 @@ static int xenon_probe_params(struct platform_device *pdev) - struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); - u32 sdhc_id, nr_sdhc; - u32 tuning_count; -+ struct sysinfo si; - - /* Disable HS200 on Armada AP806 */ - if (priv->hw_version == XENON_AP806) -@@ -450,6 +453,23 @@ static int xenon_probe_params(struct platform_device *pdev) - } - priv->tuning_count = tuning_count; - -+ /* -+ * AC5/X/IM HW has only 31-bits passed in the crossbar switch. -+ * If we have more than 2GB of memory, this means we might pass -+ * memory pointers which are above 2GB and which cannot be properly -+ * represented. In this case, disable ADMA, 64-bit DMA and allow only SDMA. -+ * This effectively will enable bounce buffer quirk in the -+ * generic SDHCI driver, which will make sure DMA is only done -+ * from supported memory regions: -+ */ -+ if (priv->hw_version == XENON_AC5) { -+ si_meminfo(&si); -+ if (si.totalram * si.mem_unit > SZ_2G) { -+ host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; -+ host->quirks2 |= SDHCI_QUIRK2_BROKEN_64_BIT_DMA; -+ } -+ } -+ - return xenon_phy_parse_params(dev, host); - } - -@@ -562,6 +582,16 @@ static int xenon_probe(struct platform_device *pdev) - goto remove_sdhc; - - pm_runtime_put_autosuspend(&pdev->dev); -+ /* -+ * If we previously detected AC5 with over 2GB of memory, -+ * then we disable ADMA and 64-bit DMA. -+ * This means generic SDHCI driver has set the DMA mask to -+ * 32-bit. Since DDR starts at 0x2_0000_0000, we must use -+ * 34-bit DMA mask to access this DDR memory: -+ */ -+ if (priv->hw_version == XENON_AC5 && -+ host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) -+ dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); - - return 0; - -@@ -680,6 +710,7 @@ static const struct of_device_id sdhci_xenon_dt_ids[] = { - { .compatible = "marvell,armada-ap807-sdhci", .data = (void *)XENON_AP807}, - { .compatible = "marvell,armada-cp110-sdhci", .data = (void *)XENON_CP110}, - { .compatible = "marvell,armada-3700-sdhci", .data = (void *)XENON_A3700}, -+ { .compatible = "marvell,ac5-sdhci", .data = (void *)XENON_AC5}, - {} - }; - MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids); -diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h -index 3e9c6c908a79..0460d97aad26 100644 ---- a/drivers/mmc/host/sdhci-xenon.h -+++ b/drivers/mmc/host/sdhci-xenon.h -@@ -57,7 +57,8 @@ enum xenon_variant { - XENON_A3700, - XENON_AP806, - XENON_AP807, -- XENON_CP110 -+ XENON_CP110, -+ XENON_AC5 - }; - - struct xenon_priv { --- -2.25.1 - diff --git a/patches-sonic/0007-usb-ehci-Add-support-for-ac5.patch b/patches-sonic/0007-usb-ehci-Add-support-for-ac5.patch deleted file mode 100644 index 48dfaf832..000000000 --- a/patches-sonic/0007-usb-ehci-Add-support-for-ac5.patch +++ /dev/null @@ -1,38 +0,0 @@ -From: Pavan Naregundi -Date: Mon, 13 Mar 2023 10:01:51 +0000 -usb: ehci: Add support for ac5 - -Add a new compatible string for 98DX25xx SoCs. -Change DMA bit mask to 64-bit addressing. - -Signed-off-by: Yuval Shaia -Tested-by: Raz Adashi -Reviewed-by: Raz Adashi ---- - drivers/usb/host/ehci-orion.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c -index 3626758b3..488a2ba86 100644 ---- a/drivers/usb/host/ehci-orion.c -+++ b/drivers/usb/host/ehci-orion.c -@@ -232,7 +232,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) - * set. Since shared usb code relies on it, set it here for - * now. Once we have dma capability bindings this can go away. - */ -- err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); -+ err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - if (err) - goto err; - -@@ -341,6 +341,7 @@ static int ehci_orion_drv_remove(struct platform_device *pdev) - static const struct of_device_id ehci_orion_dt_ids[] = { - { .compatible = "marvell,orion-ehci", }, - { .compatible = "marvell,armada-3700-ehci", }, -+ { .compatible = "marvell,ac5-ehci", }, - {}, - }; - MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); --- -2.25.1 - diff --git a/patches-sonic/PCI-ASPM-Fix-link-state-exit-during-switch-upstream.patch b/patches-sonic/PCI-ASPM-Fix-link-state-exit-during-switch-upstream.patch deleted file mode 100644 index 1b2cb4d8c..000000000 --- a/patches-sonic/PCI-ASPM-Fix-link-state-exit-during-switch-upstream.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Daniel Stodden -Date: Sun, 22 Dec 2024 19:39:08 -0800 -Subject: [PATCH] PCI/ASPM: Fix link state exit during switch upstream function - removal -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -[ Upstream commit cbf937dcadfd571a434f8074d057b32cd14fbea5 ] - -Before 456d8aa37d0f ("PCI/ASPM: Disable ASPM on MFD function removal to -avoid use-after-free"), we would free the ASPM link only after the last -function on the bus pertaining to the given link was removed. - -That was too late. If function 0 is removed before sibling function, -link->downstream would point to free'd memory after. - -After above change, we freed the ASPM parent link state upon any function -removal on the bus pertaining to a given link. - -That is too early. If the link is to a PCIe switch with MFD on the upstream -port, then removing functions other than 0 first would free a link which -still remains parent_link to the remaining downstream ports. - -The resulting GPFs are especially frequent during hot-unplug, because -pciehp removes devices on the link bus in reverse order. - -On that switch, function 0 is the virtual P2P bridge to the internal bus. -Free exactly when function 0 is removed -- before the parent link is -obsolete, but after all subordinate links are gone. - -Link: https://lore.kernel.org/r/e12898835f25234561c9d7de4435590d957b85d9.1734924854.git.dns@arista.com -Fixes: 456d8aa37d0f ("PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free") -Signed-off-by: Daniel Stodden -Signed-off-by: Bjorn Helgaas -[kwilczynski: commit log] -Signed-off-by: Krzysztof Wilczyński ---- - drivers/pci/pcie/aspm.c | 17 +++++++++-------- - 1 file changed, 9 insertions(+), 8 deletions(-) - -diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c -index cf4acea6610d..20ec2a4aae65 100644 ---- a/drivers/pci/pcie/aspm.c -+++ b/drivers/pci/pcie/aspm.c -@@ -1031,16 +1031,16 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) - parent_link = link->parent; - - /* -- * link->downstream is a pointer to the pci_dev of function 0. If -- * we remove that function, the pci_dev is about to be deallocated, -- * so we can't use link->downstream again. Free the link state to -- * avoid this. -+ * Free the parent link state, no later than function 0 (i.e. -+ * link->downstream) being removed. - * -- * If we're removing a non-0 function, it's possible we could -- * retain the link state, but PCIe r6.0, sec 7.5.3.7, recommends -- * programming the same ASPM Control value for all functions of -- * multi-function devices, so disable ASPM for all of them. -+ * Do not free the link state any earlier. If function 0 is a -+ * switch upstream port, this link state is parent_link to all -+ * subordinate ones. - */ -+ if (pdev != link->downstream) -+ goto out; -+ - pcie_config_aspm_link(link, 0); - list_del(&link->sibling); - free_link_state(link); -@@ -1051,6 +1051,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) - pcie_config_aspm_path(parent_link); - } - -+ out: - mutex_unlock(&aspm_lock); - up_read(&pci_bus_sem); - } diff --git a/patches-sonic/arista_goldfinch-dts.patch b/patches-sonic/arista_goldfinch-dts.patch index f7dce73e1..63093226a 100644 --- a/patches-sonic/arista_goldfinch-dts.patch +++ b/patches-sonic/arista_goldfinch-dts.patch @@ -24,9 +24,9 @@ index 5caf80a7f..496ae839d 100644 nexthop-b27-r0.dtb \ nokia-ast2700-h6-128-r0.dtb \ + arista_goldfinch-r0.dtb \ + ast2700-evb_ast1700-evb.dtb \ + ast2700-evb_ast1700-evb-dual.dtb \ ast2700-raw.dtb \ - ast2700-evb-s0.dtb \ - ast2700-evb-s1.dtb \ diff --git a/arch/arm64/boot/dts/aspeed/arista_goldfinch-r0.dts b/arch/arm64/boot/dts/aspeed/arista_goldfinch-r0.dts new file mode 100644 index 000000000..52536fc16 diff --git a/patches-sonic/aspeed-ast2700-support.patch b/patches-sonic/aspeed-ast2700-support.patch index edc5a1c55..988a3bb2b 100644 --- a/patches-sonic/aspeed-ast2700-support.patch +++ b/patches-sonic/aspeed-ast2700-support.patch @@ -1,5 +1,5 @@ From: Chander -Date: Wed, 08 Apr 2026 18:03:52 +0000 +Date: Fri, 17 Jul 2026 08:44:59 +0000 Subject: [PATCH] Add Aspeed AST2700 support This patch adds support for Aspeed AST2700 ARM64 SoC including: @@ -11,8 +11,8 @@ This patch adds support for Aspeed AST2700 ARM64 SoC including: Signed-off-by: Chander --- diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms ---- a/arch/arm64/Kconfig.platforms 2025-08-01 08:48:47.000000000 +0000 -+++ b/arch/arm64/Kconfig.platforms 2026-04-08 18:03:48.534701227 +0000 +--- a/arch/arm64/Kconfig.platforms 2026-06-19 11:42:39.000000000 +0000 ++++ b/arch/arm64/Kconfig.platforms 2026-07-17 08:44:55.514303098 +0000 @@ -40,6 +40,13 @@ This enables support for Apple's in-house ARM SoC family, starting with the Apple M1. @@ -28,8 +28,8 @@ diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms bool "Broadcom SoC Support" diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile ---- a/arch/arm64/boot/dts/Makefile 2026-04-08 18:03:23.250162666 +0000 -+++ b/arch/arm64/boot/dts/Makefile 2026-04-08 18:03:48.565700661 +0000 +--- a/arch/arm64/boot/dts/Makefile 2026-07-17 08:44:32.477658867 +0000 ++++ b/arch/arm64/boot/dts/Makefile 2026-07-17 08:44:55.554302482 +0000 @@ -9,6 +9,7 @@ subdir-y += apm subdir-y += apple @@ -40,12 +40,14 @@ diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile subdir-y += cavium diff --git a/arch/arm64/boot/dts/aspeed/Makefile b/arch/arm64/boot/dts/aspeed/Makefile --- a/arch/arm64/boot/dts/aspeed/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/Makefile 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,27 @@ ++++ b/arch/arm64/boot/dts/aspeed/Makefile 2026-07-17 08:44:38.761561479 +0000 +@@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0 + +dtb-$(CONFIG_ARCH_ASPEED) += \ + ast2700-evb.dtb \ ++ ast2700-evb_ast1700-evb.dtb \ ++ ast2700-evb_ast1700-evb-dual.dtb \ + ast2700-raw.dtb \ + ast2700-evb-s0.dtb \ + ast2700-evb-s1.dtb \ @@ -71,7 +73,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/Makefile b/arch/arm64/boot/dts/aspeed/Ma + ast2700a1-ci-host.dtb diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi 2026-07-17 08:44:38.761561479 +0000 @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ + @@ -107,7 +109,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-128.dtsi b/arch/ +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi 2026-07-17 08:44:38.761561479 +0000 @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ + @@ -168,8 +170,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-evb-flash-layout-256-abr.dtsi b/a +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,1425 @@ ++++ b/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi 2026-07-17 08:44:38.761561479 +0000 +@@ -0,0 +1,1435 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2025 ASPEED Corp. + @@ -696,6 +698,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi b/arch/arm64/boot + groups = "JTAGM1"; + }; + ++ pinctrl_jtagm1trst_default: jtagm1trst-default { ++ function = "JTAGM1"; ++ groups = "JTAGM1TRST"; ++ }; ++ + pinctrl_mdio0_default: mdio0-default { + function = "MDIO0"; + groups = "MDIO0"; @@ -1231,6 +1238,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi b/arch/arm64/boot + groups = "CANBUS"; + }; + ++ pinctrl_canstby_default: canstby-default { ++ function = "CANBUS"; ++ groups = "CANBUSSTBY"; ++ }; ++ + pinctrl_di2c0_default: di2c0-default { + function = "I2C0"; + groups = "DI2C0"; @@ -1597,8 +1609,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7-pinctrl.dtsi b/arch/arm64/boot +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,2923 @@ ++++ b/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,2867 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include @@ -1829,8 +1841,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + uphy3a: usb-phy3@12010000 { -+ compatible = "aspeed,ast2700-uphy3a"; -+ reg = <0x0 0x12010000 0x0 0xBC>; ++ compatible = "aspeed,ast2700-usb3-phy"; ++ reg = <0x0 0x12010000 0x0 0xC0>; + clocks = <&syscon0 SCU0_CLK_GATE_PORTAUSB2CLK>; + resets = <&syscon0 SCU0_RESET_PORTA_PHY3>; + aspeed,scu = <&syscon0>; @@ -1839,7 +1851,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + vhuba1: usb-vhub@12011000 { -+ compatible = "aspeed,ast2700-usb-vhuba1"; ++ compatible = "aspeed,ast2700-usb-vhub"; + reg = <0x0 0x12011000 0x0 0x820>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_GATE_PORTAUSB2CLK>; @@ -1848,8 +1860,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + aspeed,vhub-generic-endpoints = <21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb2axhpd1_default>; -+ aspeed,device = <&pcie_cfg0>; -+ aspeed,scu = <&syscon0>; + aspeed,enlarge-fifo; + status = "disabled"; + }; @@ -1863,8 +1873,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + uphy3b: usb-phy3@12020000 { -+ compatible = "aspeed,ast2700-uphy3b"; -+ reg = <0x0 0x12020000 0x0 0xBC>; ++ compatible = "aspeed,ast2700-usb3-phy"; ++ reg = <0x0 0x12020000 0x0 0xC0>; + clocks = <&syscon0 SCU0_CLK_GATE_PORTBUSB2CLK>; + resets = <&syscon0 SCU0_RESET_PORTB_PHY3>; + aspeed,scu = <&syscon0>; @@ -1873,7 +1883,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + vhubb1: usb-vhub@12021000 { -+ compatible = "aspeed,ast2700-usb-vhubb1"; ++ compatible = "aspeed,ast2700-usb-vhub"; + reg = <0x0 0x12021000 0x0 0x820>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_GATE_PORTBUSB2CLK>; @@ -1882,8 +1892,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + aspeed,vhub-generic-endpoints = <21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb2bxhpd1_default>; -+ aspeed,device = <&pcie_cfg1>; -+ aspeed,scu = <&syscon0>; + aspeed,enlarge-fifo; + status = "disabled"; + }; @@ -1914,7 +1922,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + usb3ahp: usb3ahp { -+ compatible = "aspeed,ast2700-usb3ahp"; ++ compatible = "aspeed,ast2700-usb3hp"; + clocks = <&syscon0 SCU0_CLK_GATE_PORTAUSB2CLK>; + resets = <&syscon0 SCU0_RESET_PORTA_XHCI>; + pinctrl-names = "default"; @@ -1953,7 +1961,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + usb3bhp: usb3bhp { -+ compatible = "aspeed,ast2700-usb3bhp"; ++ compatible = "aspeed,ast2700-usb3hp"; + clocks = <&syscon0 SCU0_CLK_GATE_PORTBUSB2CLK>; + resets = <&syscon0 SCU0_RESET_PORTB_XHCI>; + pinctrl-names = "default"; @@ -1965,7 +1973,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + vhuba0: usb-vhub@12060000 { -+ compatible = "aspeed,ast2700-usb-vhuba0"; ++ compatible = "aspeed,ast2700-usb-vhub"; + reg = <0x0 0x12060000 0x0 0x820>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_GATE_PORTAUSB2CLK>; @@ -1974,8 +1982,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + aspeed,vhub-generic-endpoints = <21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb2ad0_default>; -+ aspeed,device = <&pcie_cfg0>; -+ aspeed,scu = <&syscon0>; ++ aspeed,txfifo-retry-quirk; + status = "disabled"; + }; + @@ -1991,18 +1998,16 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + usb2ahp: usb2ahp { -+ compatible = "aspeed,ast2700-usb2ahp"; ++ compatible = "aspeed,ast2700-usb2hp"; + reg = <0x0 0x12061800 0x0 0x90>; + clocks = <&syscon0 SCU0_CLK_GATE_PORTAUSB2CLK>; + resets = <&syscon0 SCU0_RESET_PORTA_VHUB_EHCI>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_usb2ahp_default>; + aspeed,device = <&pcie_cfg0>; + status = "disabled"; + }; + + vhubb0: usb-vhub@12062000 { -+ compatible = "aspeed,ast2700-usb-vhubb0"; ++ compatible = "aspeed,ast2700-usb-vhub"; + reg = <0x0 0x12062000 0x0 0x820>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_GATE_PORTBUSB2CLK>; @@ -2011,8 +2016,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + aspeed,vhub-generic-endpoints = <21>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb2bd0_default>; -+ aspeed,device = <&pcie_cfg1>; -+ aspeed,scu = <&syscon0>; ++ aspeed,txfifo-retry-quirk; + status = "disabled"; + }; + @@ -2028,19 +2032,17 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + usb2bhp: usb2bhp { -+ compatible = "aspeed,ast2700-usb2bhp"; ++ compatible = "aspeed,ast2700-usb2hp"; + reg = <0x0 0x12063800 0x0 0x90>; + clocks = <&syscon0 SCU0_CLK_GATE_PORTBUSB2CLK>; + resets = <&syscon0 SCU0_RESET_PORTB_VHUB_EHCI>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_usb2bhp_default>; + aspeed,device = <&pcie_cfg1>; + status = "disabled"; + }; + + hace: crypto@12070000 { + compatible = "aspeed,ast2700-hace"; -+ reg = <0x0 0x12070000 0x0 0x200>; ++ reg = <0x0 0x12070000 0x0 0x200>, <0x0 0x12c1f000 0x0 0x10>; + interrupts = ; + clocks = <&syscon0 SCU0_CLK_GATE_HACCLK>; + resets = <&syscon0 SCU0_RESET_HACE>; @@ -2121,9 +2123,9 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + status = "disabled"; + }; + -+ rvas1: rvas@120bc000 { ++ rvas1: rvas@121b8000 { + compatible = "aspeed,ast2700-rvas"; -+ reg = <0x0 0x120bc000 0x0 0x800>, ++ reg = <0x0 0x121b8000 0x0 0x800>, + <0x0 0x12c14800 0x0 0x800>, + <0x0 0x120a1000 0x0 0x1000>; + clocks = <&syscon0 SCU0_CLK_GATE_RVAS1CLK>, @@ -2653,18 +2655,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + #size-cells = <2>; + aspeed,device = <&pcie_cfg0>; + -+ e2m_ic0: interrupt-controller@14 { -+ #interrupt-cells = <1>; -+ compatible = "aspeed,ast2700-e2m-ic"; -+ reg = <0 0x14 0 0x8>; -+ interrupts-extended = <&gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; -+ interrupt-names = "e2m"; -+ interrupt-controller; -+ }; -+ + pcie0_mmbi0: pcie0-mmbi@0 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic0 ASPEED_AST2700_E2M_MMBI_H2B_INT0>; + index = <0>; + pid = <3>; + bar = <0x3c>; @@ -2674,7 +2666,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + pcie0_mmbi1: pcie0-mmbi@1 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic0 ASPEED_AST2700_E2M_MMBI_H2B_INT1>; + index = <1>; + pid = <4>; + bar = <0x4c>; @@ -2684,7 +2675,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + pcie0_mmbi2: pcie0-mmbi@2 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic0 ASPEED_AST2700_E2M_MMBI_H2B_INT2>; + index = <2>; + pid = <5>; + bar = <0x5c>; @@ -2694,7 +2684,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + pcie0_mmbi3: pcie0-mmbi@3 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic0 ASPEED_AST2700_E2M_MMBI_H2B_INT3>; + index = <3>; + pid = <6>; + bar = <0x6c>; @@ -2711,17 +2700,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + #size-cells = <2>; + aspeed,device = <&pcie_cfg1>; + -+ e2m_ic1: interrupt-controller@14 { -+ #interrupt-cells = <1>; -+ compatible = "aspeed,ast2700-e2m-ic"; -+ reg = <0 0x14 0 0x8>; -+ interrupts-extended = <&gic GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>; -+ interrupt-controller; -+ }; -+ + pcie1_mmbi4: pcie1-mmbi@4 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic1 ASPEED_AST2700_E2M_MMBI_H2B_INT4>; + index = <4>; + pid = <11>; + bar = <0x3c>; @@ -2731,7 +2711,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + pcie1_mmbi5: pcie1-mmbi@5 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic1 ASPEED_AST2700_E2M_MMBI_H2B_INT5>; + index = <5>; + pid = <12>; + bar = <0x4c>; @@ -2741,7 +2720,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + pcie1_mmbi6: pcie1-mmbi@6 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic1 ASPEED_AST2700_E2M_MMBI_H2B_INT6>; + index = <6>; + pid = <13>; + bar = <0x5c>; @@ -2751,7 +2729,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + pcie1_mmbi7: pcie1-mmbi@7 { + compatible = "aspeed,ast2700-pcie-mmbi"; -+ interrupts-extended = <&e2m_ic1 ASPEED_AST2700_E2M_MMBI_H2B_INT7>; + index = <7>; + pid = <14>; + bar = <0x6c>; @@ -2913,8 +2890,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon1 SCU1_CLK_GATE_MAC0CLK>; + resets = <&syscon1 SCU1_RESET_MAC0>; + status = "disabled"; -+ -+ aspeed,scu = <&syscon1>; + }; + + mac1: ethernet@14060000 { @@ -2925,8 +2900,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon1 SCU1_CLK_GATE_MAC1CLK>; + resets = <&syscon1 SCU1_RESET_MAC1>; + status = "disabled"; -+ -+ aspeed,scu = <&syscon1>; + }; + + mac2: ethernet@14070000 { @@ -2940,8 +2913,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon1 SCU1_CLK_GATE_MAC2CLK>; + resets = <&syscon1 SCU1_RESET_MAC2>; + status = "disabled"; -+ -+ aspeed,scu = <&syscon1>; + }; + + sdio_controller: sdc@14080000 { @@ -3023,7 +2994,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + vhubc: usb-vhub@14120000 { -+ compatible = "aspeed,ast2700-usb-vhubc"; ++ compatible = "aspeed,ast2700-usb-vhub"; + reg = <0x0 0x14120000 0x0 0x820>; + interrupts-extended = <&intc1_4 28>; + clocks = <&syscon1 SCU1_CLK_GATE_PORTCUSB2CLK>; @@ -3047,7 +3018,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + }; + + vhubd: usb-vhub@14122000 { -+ compatible = "aspeed,ast2700-usb-vhubd"; ++ compatible = "aspeed,ast2700-usb-vhub"; + reg = <0x0 0x14122000 0x0 0x820>; + interrupts-extended = <&intc1_4 29>; + clocks = <&syscon1 SCU1_CLK_GATE_PORTDUSB2CLK>; @@ -3221,7 +3192,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + interrupts-extended = <&intc1_0 10>, <&intc1_0 16>; + clocks = <&syscon1 SCU1_CLK_GATE_ESPI0CLK>; + resets = <&syscon1 SCU1_RESET_ESPI0>; -+ syscon = <&syscon1>; ++ aspeed,syscon = <&syscon1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_espi0_default>; + status = "disabled"; @@ -3233,7 +3204,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + interrupts-extended = <&intc1_1 10>, <&intc1_1 16>; + clocks = <&syscon1 SCU1_CLK_GATE_ESPI1CLK>; + resets = <&syscon1 SCU1_RESET_ESPI1>; -+ syscon = <&syscon1>; ++ aspeed,syscon = <&syscon1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_espi1_default>; + status = "disabled"; @@ -3255,7 +3226,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + clocks = <&syscon1 SCU1_CLK_AHB>; + resets = <&syscon1 SCU1_RESET_JTAG1>; + pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_jtagm1_default>; ++ pinctrl-0 = <&pinctrl_jtagm1_default &pinctrl_jtagm1trst_default>; + status = "disabled"; + }; + @@ -3486,7 +3457,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + compatible = "aspeed,ast2600-peci"; + reg = <0x0 0x14c1f000 0x0 0x100>; + interrupts-extended = <&intc1_5 4>; -+ clocks = <&syscon1 SCU1_CLK_GATE_IPEREFCLK>; ++ clocks = <&syscon1 SCU1_CLK_PECI>; + resets = <&syscon1 SCU1_RESET_PECI>; + cmd-timeout-ms = <1000>; + clock-frequency = <1000000>; @@ -4106,7 +4077,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + ltpi0: ltpi@14c34000 { + compatible = "aspeed-ltpi"; -+ reg = <0x0 0x14c34000 0x0 0x100>, ++ reg = <0x0 0x14c34000 0x0 0x200>, + <0x0 0x14c34200 0x0 0x100>, + <0x0 0x14c34800 0x0 0x100>; + reg-names = "base", "phy", "top"; @@ -4133,7 +4104,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + + ltpi1: ltpi@14c35000 { + compatible = "aspeed-ltpi"; -+ reg = <0x0 0x14c35000 0x0 0x100>, ++ reg = <0x0 0x14c35000 0x0 0x200>, + <0x0 0x14c35200 0x0 0x100>, + <0x0 0x14c35800 0x0 0x100>; + reg-names = "base", "phy", "top"; @@ -4257,7 +4228,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x100 0xA0>, <0x1C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 0>; @@ -4274,7 +4244,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x200 0xA0>, <0x2C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 1>; @@ -4291,7 +4260,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x300 0xA0>, <0x3C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 2>; @@ -4324,7 +4292,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x500 0xA0>, <0x5C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + clock-frequency = <100000>; @@ -4341,7 +4308,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x600 0xA0>, <0x6C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 5>; @@ -4358,7 +4324,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x700 0xA0>, <0x7C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 6>; @@ -4375,7 +4340,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x800 0xA0>, <0x8C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 7>; @@ -4392,7 +4356,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x900 0xA0>, <0x9C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 8>; @@ -4409,7 +4372,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0xA00 0xA0>, <0xAC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 9>; @@ -4426,7 +4388,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0xB00 0xA0>, <0xBC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 10>; @@ -4443,7 +4404,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0xC00 0xA0>, <0xCC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 11>; @@ -4460,7 +4420,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0xD00 0xA0>, <0xDC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 12>; @@ -4477,7 +4436,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0xE00 0xA0>, <0xEC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 13>; @@ -4494,7 +4452,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0xF00 0xA0>, <0xFC0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 14>; @@ -4511,7 +4468,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp + reg = <0x1000 0xA0>, <0x10C0 0x40>; + compatible = "aspeed,ast2700-i2c"; + aspeed,global-regs = <&i2c_global>; -+ aspeed,enable-dma; + clocks = <&syscon1 SCU1_CLK_APB>; + resets = <&syscon1 SCU1_RESET_I2C>; + interrupts-extended = <&intc1_2 15>; @@ -4524,7 +4480,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-g7.dtsi b/arch/arm64/boot/dts/asp +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi 2026-07-17 08:44:38.762561463 +0000 @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -4937,8 +4893,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0-pinctrl.dtsi b/arch/arm64/b +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,659 @@ ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,660 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include @@ -5039,6 +4995,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ + reg= <0x0 0x30c09000 0x0 0x40>; + clocks = <<pi0_syscon AST1700_CLK_AHB>; + resets = <<pi0_syscon AST1700_RESET_JTAG1>; ++ interrupts-extended = <<pi0_intc1_0 23>; + status = "disabled"; + }; + @@ -5600,7 +5557,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi0.dtsi b/arch/arm64/boot/dts/ +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi 2026-07-17 08:44:38.762561463 +0000 @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -6013,8 +5970,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1-pinctrl.dtsi b/arch/arm64/b +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,659 @@ ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,660 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include @@ -6115,6 +6072,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ + reg= <0x0 0x50c09000 0x0 0x40>; + clocks = <<pi1_syscon AST1700_CLK_AHB>; + resets = <<pi1_syscon AST1700_RESET_JTAG1>; ++ interrupts-extended = <<pi1_intc1_0 23>; + status = "disabled"; + }; + @@ -6676,7 +6634,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1.dtsi b/arch/arm64/boot/dts/ +}; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi --- a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi 2026-07-17 08:44:38.762561463 +0000 @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include @@ -7327,8 +7285,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-ltpi1800.dtsi b/arch/arm64/boot/d +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts b/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,127 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -7456,10 +7414,18 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ci-host.dts b/arch/arm64/boot/dt + phy-mode = "sgmii"; + phy-handle = <ðphy2>; +}; ++ ++&mctp0 { ++ pcie_rc; ++}; ++ ++&mctp1 { ++ pcie_rc; ++}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,973 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,991 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -8175,6 +8141,22 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/ +}; + +&hace { ++ dummy-key0 = < ++ 0x89878915 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key1 = < ++ 0x89878902 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key2 = < ++ 0x2C914FA3 0x1056D87B 0xC13D6AE2 0x5E44AF98 ++ 0x0DB97321 0xCA378CF6 0x62EE115D 0x3FA74B90 ++ >; ++ dummy-key3 = < ++ 0x9E18D25C 0x6FB17A43 0x348DE520 0x1269FAC7 ++ 0xAC5503DE 0x2DB61F88 0xEF9AC074 0xB84E6731 ++ >; + status = "okay"; +}; + @@ -8380,34 +8362,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/ + status = "okay"; +}; + -+&uphy3a { -+ status = "okay"; -+}; -+ -+&uphy3b { -+ status = "okay"; -+}; -+ +&vhuba0 { + status = "okay"; + pinctrl-0 = <&pinctrl_usb2ahpd0_default>; +}; + -+&usb3ahp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; -+}; -+ -+&usb3bhp { -+ status = "okay"; -+}; -+ -+&uphy2b { -+ status = "okay"; -+}; -+ -+&vhubb1 { ++&vhubb0 { + status = "okay"; ++ pinctrl-0 = <&pinctrl_usb2bhpd0_default>; +}; + +&vhubc { @@ -8433,9 +8395,31 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm.dts b/arch/arm64/boot/dts/ +&otp { + status = "okay"; +}; ++ ++#if 0 ++&soc0 { ++ mbox-ssp-0 { ++ compatible = "aspeed,aspeed-mbox"; ++ reg = <0x4 0x31480000 0x0 0x200000>, <0x0 0x10001000 0x0 0x1000>; ++ mboxes = <&mbox0 0>; ++ aspeed,tx-timeout = <100>; ++ }; ++}; ++#endif ++ ++#if 1 ++&soc0 { ++ mbox-bootmcu-1 { ++ compatible = "aspeed,aspeed-mbox"; ++ reg = <0x4 0x31880000 0x0 0x100000>, <0x4 0x31980000 0x0 0x100000>; ++ mboxes = <&mbox2 1>; ++ aspeed,tx-timeout = <10000>; ++ }; ++}; ++#endif diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts 2026-07-17 08:44:38.762561463 +0000 @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -8657,7 +8641,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb-dual.dts b/arc +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts 2026-07-17 08:44:38.762561463 +0000 @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -9117,7 +9101,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1700-evb.dts b/arch/arm +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts 2026-07-17 08:44:38.762561463 +0000 @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -9557,7 +9541,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-dcscm_ast1800-evb.dts b/arch/arm +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts 2026-04-08 18:03:31.587010606 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts 2026-07-17 08:44:38.762561463 +0000 @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -9601,8 +9585,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-256-abr.dts b/arch/arm64/boo + diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts 2026-04-08 18:03:31.587010606 +0000 -@@ -0,0 +1,367 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ast2700-evb.dts" @@ -9673,6 +9657,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9685,6 +9670,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9697,6 +9683,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9709,6 +9696,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9721,6 +9709,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9733,6 +9722,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9745,6 +9735,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9753,10 +9744,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + +&i2c7 { + clock-frequency = <1000000>; -+ debounce-level = <4>; ++ debounce-level = <8>; + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9769,6 +9761,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9781,6 +9774,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9789,10 +9783,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + +&i2c10 { + clock-frequency = <1000000>; -+ debounce-level = <4>; ++ debounce-level = <8>; + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9805,6 +9800,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9817,6 +9813,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9829,6 +9826,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9841,6 +9839,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9849,10 +9848,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts + +&i2c15 { + clock-frequency = <1000000>; -+ debounce-level = <4>; ++ debounce-level = <8>; + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -9972,8 +9972,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s0.dts b/arch/arm64/boot/dts +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,481 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ast2700-evb.dts" @@ -10106,6 +10106,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10118,6 +10119,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10130,6 +10132,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10142,6 +10145,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10154,6 +10158,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10166,6 +10171,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10178,6 +10184,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10186,10 +10193,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + +&i2c7 { + clock-frequency = <1000000>; -+ debounce-level = <4>; ++ debounce-level = <8>; + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10202,6 +10210,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10214,6 +10223,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10222,10 +10232,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + +&i2c10 { + clock-frequency = <1000000>; -+ debounce-level = <4>; ++ debounce-level = <8>; + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10238,6 +10249,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10250,6 +10262,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10262,6 +10275,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10274,6 +10288,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10282,10 +10297,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + +&i2c15 { + clock-frequency = <1000000>; -+ debounce-level = <4>; ++ debounce-level = <8>; + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -10416,20 +10432,9 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts + pinctrl-0 = <&pinctrl_usb2ad0_default>; +}; + -+&uphy2a { -+ status = "okay"; -+}; -+ -+&usb3ahp { -+ status = "disabled"; -+}; -+ -+&usb3bhp { -+ status = "disabled"; -+}; -+ +&vhubb0 { + status = "okay"; ++ pinctrl-0 = <&pinctrl_usb2bd0_default>; +}; + +&vhubb1 { @@ -10457,8 +10462,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb-s1.dts b/arch/arm64/boot/dts +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evb.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,1271 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,1307 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -10505,6 +10510,20 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + reusable; + }; + ++ rvas_memory0: rvas0{ ++ size = <0x0 0x04000000>; ++ alignment = <0x0 0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ ++ rvas_memory1: rvas1{ ++ size = <0x0 0x04000000>; ++ alignment = <0x0 0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ +#if 0 + gfx_memory: framebuffer { + size = <0x0 0x01000000>; @@ -11147,9 +11166,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii0_default &pinctrl_rgmii0_driving>; -+ -+ rx-internal-delay-ps = <0>; -+ tx-internal-delay-ps = <0>; +}; + +&mac1 { @@ -11160,9 +11176,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii1_default &pinctrl_rgmii1_driving>; -+ -+ rx-internal-delay-ps = <0>; -+ tx-internal-delay-ps = <0>; +}; + +#if 0 // Default to disable RC & SGMII @@ -11369,6 +11382,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + status = "okay"; +}; + ++#if 1 +&video0 { + status = "okay"; + memory-region = <&video_engine_memory0>; @@ -11378,6 +11392,17 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + status = "okay"; + memory-region = <&video_engine_memory1>; +}; ++#else ++&rvas0{ ++ status = "okay"; ++ memory-region = <&rvas_memory0>; ++}; ++ ++&rvas1{ ++ status = "okay"; ++ memory-region = <&rvas_memory1>; ++}; ++#endif + +&disp_intf { + status = "okay"; @@ -11396,6 +11421,22 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as +}; + +&hace { ++ dummy-key0 = < ++ 0x89878915 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key1 = < ++ 0x89878902 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key2 = < ++ 0x2C914FA3 0x1056D87B 0xC13D6AE2 0x5E44AF98 ++ 0x0DB97321 0xCA378CF6 0x62EE115D 0x3FA74B90 ++ >; ++ dummy-key3 = < ++ 0x9E18D25C 0x6FB17A43 0x348DE520 0x1269FAC7 ++ 0xAC5503DE 0x2DB61F88 0xEF9AC074 0xB84E6731 ++ >; + status = "okay"; +}; + @@ -11459,8 +11500,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + status = "okay"; + memory-region = <&pcie0_mmbi0_memory>; + -+ bmc-int-value = /bits/ 8 <0x00>; -+ bmc-int-location = <0>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else /* !PCIE0_EP */ +&pcie0 { @@ -11528,8 +11575,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + status = "okay"; + memory-region = <&pcie1_mmbi4_memory>; + -+ bmc-int-value = /bits/ 8 <0x00>; -+ bmc-int-location = <0>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else /* !PCIE1_EP */ +&pcie1 { @@ -11641,7 +11694,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + +#endif + -+#if 1 ++#if 0 +&uphy3a { + status = "okay"; +}; @@ -11649,9 +11702,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as +&uphy3b { + status = "okay"; +}; -+#endif + -+#if 0 +&xhci0 { + status = "okay"; +}; @@ -11666,21 +11717,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + pinctrl-0 = <&pinctrl_usb2ahpd0_default>; +}; + -+&usb3ahp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; -+}; -+ +&vhubb0 { + status = "okay"; + pinctrl-0 = <&pinctrl_usb2bhpd0_default>; +}; + -+&usb3bhp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3bxhp_default &pinctrl_usb2bxhp_default>; -+}; -+ +&vhubc { + status = "okay"; +#if 0 @@ -11730,1198 +11771,701 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb.dts b/arch/arm64/boot/dts/as + }; +}; +#endif -diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts b/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts ---- a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,1175 @@ +diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb-dual.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb-dual.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb-dual.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb-dual.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; + -+#include "aspeed-g7.dtsi" -+#include -+#include -+#define PCIE0_EP 1 // 1: EP, 0: RC -+#define PCIE1_EP 1 // 1: EP, 0: RC ++#include "ast2700-evb_ast1700-evb.dts" ++#include "aspeed-ltpi1.dtsi" + +/ { -+ model = "AST2700-EVB"; -+ compatible = "aspeed,ast2700-evb", "aspeed,ast2700"; ++ model = "AST2700-EVB_AST1700-EVB-DUAL"; + -+ chosen { -+ stdout-path = "serial12:115200n8"; ++ ltpi1-iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <<pi1_adc0 0>, <<pi1_adc0 1>, <<pi1_adc0 2>, <<pi1_adc0 3>, ++ <<pi1_adc0 4>, <<pi1_adc0 5>, <<pi1_adc0 6>, <<pi1_adc0 7>, ++ <<pi1_adc1 0>, <<pi1_adc1 1>, <<pi1_adc1 2>, <<pi1_adc1 3>, ++ <<pi1_adc1 4>, <<pi1_adc1 5>, <<pi1_adc1 6>, <<pi1_adc1 7>; + }; ++}; + -+ memory@400000000 { -+ device_type = "memory"; -+ reg = <0x4 0x00000000 0x0 0x40000000>; -+ }; ++<pi1_adc0 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++}; + -+ reserved-memory { -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; ++<pi1_adc1 { ++ aspeed,int-vref-microvolt = <2500000>; ++ status = "okay"; ++}; + -+ #include "ast2700-reserved-mem.dtsi" ++<pi1 { ++ i2c-tunneling = <0x0>; ++ status = "okay"; ++}; + -+ video_engine_memory0: video0 { -+ size = <0x0 0x02c00000>; -+ alignment = <0x0 0x00100000>; -+ compatible = "shared-dma-pool"; -+ reusable; -+ }; ++<pi1_gpio { ++ status = "okay"; ++}; + -+ video_engine_memory1: video1{ -+ size = <0x0 0x02c00000>; -+ alignment = <0x0 0x00100000>; -+ compatible = "shared-dma-pool"; -+ reusable; -+ }; ++<pi1_i3c0 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06010000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ gfx_memory: framebuffer { -+ size = <0x0 0x01000000>; -+ alignment = <0x0 0x01000000>; -+ compatible = "shared-dma-pool"; -+ reusable; -+ }; ++<pi1_i3c1 { ++ initial-role = "primary"; ++ status = "okay"; ++}; + -+ xdma_memory0: xdma0 { -+ size = <0x0 0x01000000>; -+ alignment = <0x0 0x01000000>; -+ compatible = "shared-dma-pool"; -+ no-map; -+ }; ++<pi1_i3c2 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06012000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; + -+ xdma_memory1: xdma1 { -+ size = <0x0 0x01000000>; -+ alignment = <0x0 0x01000000>; -+ compatible = "shared-dma-pool"; -+ no-map; -+ }; ++<pi1_i3c3 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++<pi1_i3c4 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06014000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; ++ ++<pi1_i3c5 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++<pi1_i3c6 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06016000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; ++ ++<pi1_i3c7 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++<pi1_i3c8 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x06018000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; ++ ++<pi1_i3c9 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++<pi1_i3c10 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601A000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; ++ ++<pi1_i3c11 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++<pi1_i3c12 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601C000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; ++ ++<pi1_i3c13 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++<pi1_i3c14 { ++ initial-role = "target"; ++ pid = <0x000007ec 0x0601E000>; ++ dcr = /bits/ 8 <0xcc>; ++ status = "okay"; ++}; ++ ++<pi1_i3c15 { ++ initial-role = "primary"; ++ status = "okay"; ++}; ++ ++&i2c6 { ++ status = "disabled"; ++}; ++ ++&i2c7 { ++ status = "disabled"; ++}; ++ ++// The following I2C buses require enabling according to the ast2700-dcscm.dts. ++// i2c8 / i2c9 / i2c10 ++ ++&i2c11 { ++ status = "disabled"; ++}; ++ ++<pi1_i2c0 { ++ status = "okay"; ++}; ++ ++<pi1_i2c1 { ++ status = "okay"; ++}; ++ ++<pi1_i2c2 { ++ status = "okay"; ++}; ++ ++<pi1_i2c3 { ++ status = "okay"; ++}; ++ ++<pi1_i2c4 { ++ status = "okay"; ++}; ++ ++<pi1_i2c5 { ++ status = "okay"; ++}; ++ ++<pi1_i2c6 { ++ status = "okay"; ++}; ++ ++<pi1_i2c7 { ++ status = "okay"; ++}; ++ ++<pi1_i2c8 { ++ status = "okay"; ++}; ++ ++<pi1_i2c9 { ++ status = "okay"; ++}; ++ ++<pi1_i2c10 { ++ status = "okay"; ++}; ++ ++<pi1_i2c11 { ++ status = "okay"; ++}; ++ ++<pi1_i2c12 { ++ status = "okay"; ++}; ++ ++<pi1_i2c13 { ++ status = "okay"; ++}; ++ ++<pi1_i2c14 { ++ status = "okay"; ++}; ++ ++<pi1_i2c15 { ++ status = "okay"; ++}; ++ ++&uart8 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; ++ status = "okay"; ++}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evb_ast1700-evb.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,456 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++ ++/dts-v1/; ++ ++#include "ast2700-evb.dts" ++#include "aspeed-ltpi0.dtsi" ++ ++/ { ++ model = "AST2700-EVB_AST1700-EVB"; ++ ++ ltpi_fan0: ltpi-pwm-fan0 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 0 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; + }; + -+ fan0: pwm-fan0 { ++ ltpi_fan1: ltpi-pwm-fan1 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 0 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 1 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan1: pwm-fan1 { ++ ltpi_fan2: ltpi-pwm-fan2 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 1 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 2 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan2: pwm-fan2 { ++ ltpi_fan3: ltpi-pwm-fan3 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 2 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 3 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan3: pwm-fan3 { ++ ltpi_fan4: ltpi-pwm-fan4 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 3 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 4 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan4: pwm-fan4 { ++ ltpi_fan5: ltpi-pwm-fan5 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 4 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 5 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan5: pwm-fan5 { ++ ltpi_fan6: ltpi-pwm-fan6 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 5 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 6 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan6: pwm-fan6 { ++ ltpi_fan7: ltpi-pwm-fan7 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 6 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 7 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan7: pwm-fan7 { ++ ltpi_fan8: ltpi-pwm-fan8 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 7 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 8 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ fan8: pwm-fan8 { ++ ltpi_fan9: ltpi-pwm-fan9 { + compatible = "pwm-fan"; -+ pwms = <&pwm_tach 8 40000 0>; /* Target freq:25 kHz */ ++ pwms = <<pi0_pwm_tach 9 40000 0>; /* Target freq:25 kHz */ + cooling-min-state = <0>; + cooling-max-state = <3>; + #cooling-cells = <2>; + cooling-levels = <0 15 128 255>; + }; + -+ iio-hwmon { -+ compatible = "iio-hwmon"; -+ status = "okay"; -+ io-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>, -+ <&adc0 4>, <&adc0 5>, <&adc0 6>, <&adc0 7>, -+ <&adc1 0>, <&adc1 1>, <&adc1 2>, <&adc1 3>, -+ <&adc1 4>, <&adc1 5>, <&adc1 6>, <&adc1 7>; ++ ltpi_fan10: ltpi-pwm-fan10 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 10 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; + }; -+}; + -+&pwm_tach { -+ status = "okay"; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default -+ &pinctrl_pwm2_default &pinctrl_pwm3_default -+ &pinctrl_pwm4_default &pinctrl_pwm5_default -+ &pinctrl_pwm6_default &pinctrl_pwm7_default -+ &pinctrl_pwm8_default -+ &pinctrl_tach0_default &pinctrl_tach1_default -+ &pinctrl_tach2_default &pinctrl_tach3_default -+ &pinctrl_tach4_default &pinctrl_tach5_default -+ &pinctrl_tach6_default &pinctrl_tach7_default -+ &pinctrl_tach8_default &pinctrl_tach9_default -+ &pinctrl_tach10_default &pinctrl_tach11_default -+ &pinctrl_tach12_default &pinctrl_tach13_default -+ &pinctrl_tach14_default &pinctrl_tach15_default>; -+ fan-0 { -+ tach-ch = /bits/ 8 <0x0>; ++ ltpi_fan11: ltpi-pwm-fan11 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 11 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; + }; -+ fan-1 { -+ tach-ch = /bits/ 8 <0x1>; ++ ++ ltpi_fan12: ltpi-pwm-fan12 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 12 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; + }; -+ fan-2 { -+ tach-ch = /bits/ 8 <0x2>; ++ ++ ltpi_fan13: ltpi-pwm-fan13 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 13 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; + }; -+ fan-3 { ++ ++ ltpi_fan14: ltpi-pwm-fan14 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 14 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; ++ ++ ltpi_fan15: ltpi-pwm-fan15 { ++ compatible = "pwm-fan"; ++ pwms = <<pi0_pwm_tach 15 40000 0>; /* Target freq:25 kHz */ ++ cooling-min-state = <0>; ++ cooling-max-state = <3>; ++ #cooling-cells = <2>; ++ cooling-levels = <0 15 128 255>; ++ }; ++ ++ ltpi0-iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <<pi0_adc0 0>, <<pi0_adc0 1>, <<pi0_adc0 2>, <<pi0_adc0 3>, ++ <<pi0_adc0 4>, <<pi0_adc0 5>, <<pi0_adc0 6>, <<pi0_adc0 7>, ++ <<pi0_adc1 0>, <<pi0_adc1 1>, <<pi0_adc1 2>, <<pi0_adc1 3>, ++ <<pi0_adc1 4>, <<pi0_adc1 5>, <<pi0_adc1 6>, <<pi0_adc1 7>; ++ }; ++}; ++ ++<pi0_pwm_tach { ++ status = "okay"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_ltpi0_pwm0_default &pinctrl_ltpi0_pwm1_default ++ &pinctrl_ltpi0_pwm2_default &pinctrl_ltpi0_pwm3_default ++ &pinctrl_ltpi0_pwm4_default &pinctrl_ltpi0_pwm5_default ++ &pinctrl_ltpi0_pwm6_default &pinctrl_ltpi0_pwm7_default ++ &pinctrl_ltpi0_pwm8_default ++ &pinctrl_ltpi0_tach0_default &pinctrl_ltpi0_tach1_default ++ &pinctrl_ltpi0_tach2_default &pinctrl_ltpi0_tach3_default ++ &pinctrl_ltpi0_tach4_default &pinctrl_ltpi0_tach5_default ++ &pinctrl_ltpi0_tach6_default &pinctrl_ltpi0_tach7_default ++ &pinctrl_ltpi0_tach8_default &pinctrl_ltpi0_tach9_default ++ &pinctrl_ltpi0_tach10_default &pinctrl_ltpi0_tach11_default ++ &pinctrl_ltpi0_tach12_default &pinctrl_ltpi0_tach13_default ++ &pinctrl_ltpi0_tach14_default &pinctrl_ltpi0_tach15_default>; ++ ltpi_fan0 { ++ tach-ch = /bits/ 8 <0x0>; ++ }; ++ ltpi_fan1 { ++ tach-ch = /bits/ 8 <0x1>; ++ }; ++ ltpi_fan2 { ++ tach-ch = /bits/ 8 <0x2>; ++ }; ++ ltpi_fan3 { + tach-ch = /bits/ 8 <0x3>; + }; -+ fan-4 { ++ ltpi_fan4 { + tach-ch = /bits/ 8 <0x4>; + }; -+ fan-5 { ++ ltpi_fan5 { + tach-ch = /bits/ 8 <0x5>; + }; -+ fan-6 { ++ ltpi_fan6 { + tach-ch = /bits/ 8 <0x6>; + }; -+ fan-7 { ++ ltpi_fan7 { + tach-ch = /bits/ 8 <0x7>; + }; -+ fan-8 { ++ ltpi_fan8 { + tach-ch = /bits/ 8 <0x8>; + }; -+ fan-9 { ++ ltpi_fan9 { + tach-ch = /bits/ 8 <0x9>; + }; -+ fan-10 { ++ ltpi_fan10 { + tach-ch = /bits/ 8 <0xA>; + }; -+ fan-11 { ++ ltpi_fan11 { + tach-ch = /bits/ 8 <0xB>; + }; -+ fan-12 { ++ ltpi_fan12 { + tach-ch = /bits/ 8 <0xC>; + }; -+ fan-13 { ++ ltpi_fan13 { + tach-ch = /bits/ 8 <0xD>; + }; -+ fan-14 { ++ ltpi_fan14 { + tach-ch = /bits/ 8 <0xE>; + }; -+ fan-15 { ++ ltpi_fan15 { + tach-ch = /bits/ 8 <0xF>; + }; +}; + -+&mctp0 { -+ status = "okay"; -+ memory-region = <&mctp0_reserved>; -+}; -+ -+&mctp1 { -+ status = "okay"; -+ memory-region = <&mctp1_reserved>; -+}; -+ -+&mctp2 { -+ status = "okay"; -+ memory-region = <&mctp2_reserved>; -+}; -+ -+&sgpiom0 { -+ status = "okay"; -+}; -+ -+&sgpiom1 { -+ status = "okay"; ++&sgpios { ++ /delete-property/ gpio-line-names; +}; + -+&jtag1 { -+ status = "okay"; ++<pi0_gpio { ++ /delete-property/ gpio-line-names; ++ /delete-node/ gpio_17; ++ /delete-node/ gpio_19; ++ /delete-node/ gpio_25; ++ /delete-node/ gpio_53; ++ /delete-node/ gpio_61; ++ /delete-node/ gpio_63; +}; + -+&adc0 { ++<pi0_adc0 { + aspeed,int-vref-microvolt = <2500000>; + status = "okay"; -+ + pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_adc0_default &pinctrl_adc1_default -+ &pinctrl_adc2_default &pinctrl_adc3_default -+ &pinctrl_adc4_default &pinctrl_adc5_default -+ &pinctrl_adc6_default &pinctrl_adc7_default>; ++ pinctrl-0 = <&pinctrl_ltpi0_adc0_default &pinctrl_ltpi0_adc1_default ++ &pinctrl_ltpi0_adc2_default &pinctrl_ltpi0_adc3_default ++ &pinctrl_ltpi0_adc4_default &pinctrl_ltpi0_adc5_default ++ &pinctrl_ltpi0_adc6_default &pinctrl_ltpi0_adc7_default>; +}; + -+&adc1 { ++<pi0_adc1 { + aspeed,int-vref-microvolt = <2500000>; + status = "okay"; -+ -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_adc8_default &pinctrl_adc9_default -+ &pinctrl_adc10_default &pinctrl_adc11_default -+ &pinctrl_adc12_default &pinctrl_adc13_default -+ &pinctrl_adc14_default &pinctrl_adc15_default>; -+}; -+ -+&pinctrl1 { -+ pinctrl_i3c0_3_hv_voltage: i3chv-voltage { -+ pins = "U25"; -+ power-source = <1800>; -+ }; -+ -+ pinctrl_i3c0_driving: i3c0-driving { -+ pins = "U25", "U26"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_i3c1_driving: i3c1-driving { -+ pins = "Y26", "AA24"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_i3c2_driving: i3c2-driving { -+ pins = "R25", "AA26"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_i3c3_driving: i3c3-driving { -+ pins = "R26", "Y25"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_i3c12_15_hv_voltage: i3chv-voltage { -+ pins = "W25"; -+ power-source = <1800>; -+ }; -+ -+ pinctrl_i3c12_driving: i3c12-driving { -+ pins = "W25", "Y23"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_i3c13_driving: i3c13-driving { -+ pins = "Y24", "W21"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_i3c14_driving: i3c14-driving { -+ pins = "AA23", "AC22"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_i3c15_driving: i3c15-driving { -+ pins = "AB22", "Y21"; -+ drive-strength = <2>; -+ }; -+ -+ pinctrl_rgmii0_driving: rgmii0-driving { -+ pins = "C20", "C19", "A8", "R14", "A7", "P14", -+ "D20", "A6", "B6", "N14", "B7", "B8"; -+ drive-strength = <1>; -+ }; -+ -+ pinctrl_rgmii1_driving: rgmii1-driving { -+ pins = "D19", "C19", "D15", "B12", "B10", "P13", -+ "C18", "C6", "C7", "D7", "N13", "C8"; -+ drive-strength = <1>; -+ }; ++ pinctrl-0 = <&pinctrl_ltpi0_adc8_default &pinctrl_ltpi0_adc9_default ++ &pinctrl_ltpi0_adc10_default &pinctrl_ltpi0_adc11_default ++ &pinctrl_ltpi0_adc12_default &pinctrl_ltpi0_adc13_default ++ &pinctrl_ltpi0_adc14_default &pinctrl_ltpi0_adc15_default>; +}; + -+&gpio0 { -+ pinctrl-0 = <&pinctrl_i3c0_3_hv_voltage &pinctrl_i3c12_15_hv_voltage -+ &pinctrl_i3c0_driving &pinctrl_i3c1_driving -+ &pinctrl_i3c2_driving &pinctrl_i3c3_driving -+ &pinctrl_i3c12_driving &pinctrl_i3c13_driving -+ &pinctrl_i3c14_driving &pinctrl_i3c15_driving>; -+ pinctrl-names = "default"; ++<pi0 { ++ i2c-tunneling = <0x0>; ++ status = "okay"; +}; + -+&i3c0 { ++<pi0_i3c0 { + initial-role = "target"; + pid = <0x000007ec 0x06010000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c1 { ++<pi0_i3c1 { + initial-role = "primary"; + status = "okay"; +}; + -+&i3c2 { ++<pi0_i3c2 { + initial-role = "target"; + pid = <0x000007ec 0x06012000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c3 { ++<pi0_i3c3 { + initial-role = "primary"; + status = "okay"; +}; + -+&i3c4 { ++<pi0_i3c4 { + initial-role = "target"; + pid = <0x000007ec 0x06014000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c5 { ++<pi0_i3c5 { + initial-role = "primary"; + status = "okay"; +}; + -+&i3c6 { ++<pi0_i3c6 { + initial-role = "target"; + pid = <0x000007ec 0x06016000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c7 { ++<pi0_i3c7 { + initial-role = "primary"; + status = "okay"; +}; + -+&i3c8 { ++<pi0_i3c8 { + initial-role = "target"; + pid = <0x000007ec 0x06018000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c9 { ++<pi0_i3c9 { + initial-role = "primary"; + status = "okay"; +}; + -+&i3c10 { ++<pi0_i3c10 { + initial-role = "target"; + pid = <0x000007ec 0x0601A000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c11 { ++<pi0_i3c11 { + initial-role = "primary"; + status = "okay"; +}; + -+&i3c12 { ++<pi0_i3c12 { + initial-role = "target"; + pid = <0x000007ec 0x0601C000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c13 { ++<pi0_i3c13 { + initial-role = "primary"; + status = "okay"; +}; + -+&i3c14 { ++<pi0_i3c14 { + initial-role = "target"; + pid = <0x000007ec 0x0601E000>; + dcr = /bits/ 8 <0xcc>; + status = "okay"; +}; + -+&i3c15 { ++<pi0_i3c15 { + initial-role = "primary"; + status = "okay"; +}; + -+&uart12 { -+ status = "okay"; ++&i2c0 { ++ status = "disabled"; +}; + -+&fmc { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_fwspi_quad_default>; -+ pinctrl-names = "default"; -+ -+ flash@0 { -+ status = "okay"; -+ m25p,fast-read; -+ label = "bmc"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <4>; -+ spi-rx-bus-width = <4>; -+#include "aspeed-evb-flash-layout-128.dtsi" -+ }; -+ -+ flash@1 { -+ status = "okay"; -+ m25p,fast-read; -+ label = "fmc0:1"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <4>; -+ spi-rx-bus-width = <4>; -+ }; -+ -+ flash@2 { -+ status = "disabled"; -+ m25p,fast-read; -+ label = "fmc0:2"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <4>; -+ spi-rx-bus-width = <4>; -+ }; ++&i2c1 { ++ status = "disabled"; +}; + -+&spi0 { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_spi0_default &pinctrl_spi0_cs1_default>; -+ pinctrl-names = "default"; -+ -+ flash@0 { -+ status = "okay"; -+ m25p,fast-read; -+ label = "spi0:0"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <2>; -+ spi-rx-bus-width = <2>; -+ }; -+ -+ flash@1 { -+ status = "disabled"; -+ m25p,fast-read; -+ label = "spi0:1"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <2>; -+ spi-rx-bus-width = <2>; -+ }; ++&i2c2 { ++ status = "disabled"; +}; + -+&spi1 { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_spi1_default &pinctrl_spi1_cs1_default>; -+ pinctrl-names = "default"; -+ -+ flash@0 { -+ status = "okay"; -+ m25p,fast-read; -+ label = "spi1:0"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <2>; -+ spi-rx-bus-width = <2>; -+ }; -+ -+ flash@1 { -+ status = "disabled"; -+ m25p,fast-read; -+ label = "spi1:1"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <2>; -+ spi-rx-bus-width = <2>; -+ }; ++&i2c3 { ++ status = "disabled"; +}; + -+#if 1 -+&spi2 { -+ compatible = "aspeed,ast2700-spi-txrx"; -+ pinctrl-0 = <&pinctrl_spi2_default>; -+ pinctrl-names = "default"; -+ status = "okay"; -+ -+ spi-aspeed-full-duplex; -+ -+ tpm0: tpmdev@0 { -+ compatible = "tcg,tpm_tis-spi"; -+ spi-max-frequency = <34000000>; -+ reg = <0>; -+ status = "okay"; -+ }; ++&i2c4 { ++ status = "disabled"; +}; -+#else -+&spi2 { -+ compatible = "aspeed,ast2700-spi"; -+ pinctrl-0 = <&pinctrl_spi2_default &pinctrl_spi2_cs1_default>; -+ pinctrl-names = "default"; -+ status = "okay"; -+ -+ flash@0 { -+ status = "okay"; -+ reg = < 0 >; -+ compatible = "jedec,spi-nor"; -+ m25p,fast-read; -+ label = "spi2:0"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <2>; -+ spi-rx-bus-width = <2>; -+ }; + -+ flash@1 { -+ status = "okay"; -+ reg = < 1 >; -+ compatible = "jedec,spi-nor"; -+ m25p,fast-read; -+ label = "spi2:1"; -+ spi-max-frequency = <50000000>; -+ spi-tx-bus-width = <2>; -+ spi-rx-bus-width = <2>; -+ }; ++&i2c5 { ++ status = "disabled"; +}; -+#endif + -+&emmc_controller { ++<pi0_i2c0 { + status = "okay"; -+ mmc-hs200-1_8v; +}; + -+&emmc { ++<pi0_i2c1 { + status = "okay"; -+#if 1 -+ bus-width = <4>; -+#else -+ bus-width = <8>; -+ pinctrl-0 = <&pinctrl_emmc_default -+ &pinctrl_emmcg8_default>; -+#endif -+ non-removable; -+ max-frequency = <200000000>; +}; + -+&ufs_controller { ++<pi0_i2c2 { + status = "okay"; +}; + -+&ufs { ++<pi0_i2c3 { + status = "okay"; -+ lanes-per-direction = <2>; -+ ref-clk-freq = <26000000>; +}; + -+&chassis { ++<pi0_i2c4 { + status = "okay"; +}; + -+&mdio0 { ++<pi0_i2c5 { + status = "okay"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ ethphy0: ethernet-phy@0 { -+ compatible = "ethernet-phy-ieee802.3-c22"; -+ reg = <0>; -+ }; +}; + -+&mdio1 { ++<pi0_i2c6 { + status = "okay"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ ethphy1: ethernet-phy@0 { -+ compatible = "ethernet-phy-ieee802.3-c22"; -+ reg = <0>; -+ }; +}; + -+&mac0 { ++<pi0_i2c7 { + status = "okay"; -+ -+ phy-mode = "rgmii"; -+ phy-handle = <ðphy0>; -+ -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_rgmii0_default &pinctrl_rgmii0_driving>; +}; + -+&mac1 { ++<pi0_i2c8 { + status = "okay"; -+ -+ phy-mode = "rgmii"; -+ phy-handle = <ðphy1>; -+ -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_rgmii1_default &pinctrl_rgmii1_driving>; +}; + -+#if 0 -+&mdio2 { ++<pi0_i2c9 { + status = "okay"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ ethphy2: ethernet-phy@0 { -+ compatible = "ethernet-phy-ieee802.3-c22"; -+ reg = <0>; -+ }; +}; + -+&sgmii { ++<pi0_i2c10 { + status = "okay"; +}; + -+&mac2 { ++<pi0_i2c11 { + status = "okay"; -+ -+ phy-mode = "sgmii"; -+ phy-handle = <ðphy2>; +}; + -+&pcie2 { ++<pi0_i2c12 { + status = "okay"; +}; -+#endif -+ -+&syscon1 { -+ mac0-clk-delay = <0x33 0x30 -+ 0x10 0x10 -+ 0x10 0x10>; -+ mac1-clk-delay = <0x31 0x31 -+ 0x10 0x10 -+ 0x10 0x10>; -+}; + -+&espi0 { ++<pi0_i2c13 { + status = "okay"; -+ perif-dma-mode; -+ perif-mmbi-enable; -+ perif-mmbi-src-addr = <0x0 0xa8000000>; -+ perif-mmbi-tgt-memory = <&espi0_mmbi_memory>; -+ perif-mmbi-instance-num = <0x1>; -+ perif-mcyc-enable; -+ perif-mcyc-src-addr = <0x0 0x98000000>; -+ perif-mcyc-size = <0x0 0x10000>; -+ oob-dma-mode; -+ flash-dma-mode; +}; + -+&lpc0_kcs0 { ++<pi0_i2c14 { + status = "okay"; -+ kcs-io-addr = <0xca0>; -+ kcs-channel = <0>; +}; + -+&lpc0_kcs1 { ++<pi0_i2c15 { + status = "okay"; -+ kcs-io-addr = <0xca8>; -+ kcs-channel = <1>; +}; + -+&lpc0_kcs2 { ++&uart3 { ++ /delete-property/ pinctrl-names; ++ /delete-property/ pinctrl-0; + status = "okay"; -+ kcs-io-addr = <0xca2>; -+ kcs-channel = <2>; +}; +diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts b/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts +--- a/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts 1970-01-01 00:00:00.000000000 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-evbeeprom.dts 2026-07-17 08:44:38.762561463 +0000 +@@ -0,0 +1,1174 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later + -+&lpc0_kcs3 { -+ status = "okay"; -+ kcs-io-addr = <0xca4>; -+ kcs-channel = <3>; -+}; ++/dts-v1/; + -+&lpc0_ibt { -+ status = "okay"; -+}; -+ -+&lpc0_mbox { -+ status = "okay"; -+}; -+ -+&lpc0_snoop { -+ status = "okay"; -+ snoop-ports = <0x80>, <0x81>; -+}; -+ -+&lpc0_uart_routing { -+ status = "okay"; -+}; -+ -+&lpc1_kcs0 { -+ status = "okay"; -+ kcs-io-addr = <0xca0>; -+ kcs-channel = <4>; -+}; -+ -+&lpc1_kcs1 { -+ status = "okay"; -+ kcs-io-addr = <0xca8>; -+ kcs-channel = <5>; -+}; -+ -+&lpc1_kcs2 { -+ status = "okay"; -+ kcs-io-addr = <0xca2>; -+ kcs-channel = <6>; -+}; -+ -+&lpc1_kcs3 { -+ status = "okay"; -+ kcs-io-addr = <0xca4>; -+ kcs-channel = <7>; -+}; -+ -+&lpc1_ibt { -+ status = "okay"; -+}; -+ -+&lpc1_mbox { -+ status = "okay"; -+}; -+ -+&lpc1_snoop { -+ status = "okay"; -+ snoop-ports = <0x80>, <0x81>; -+}; -+ -+&lpc1_uart_routing { -+ status = "okay"; -+}; -+ -+&video0 { -+ status = "okay"; -+ memory-region = <&video_engine_memory0>; -+}; -+ -+&video1 { -+ status = "okay"; -+ memory-region = <&video_engine_memory1>; -+}; -+ -+&disp_intf { -+ status = "okay"; -+}; -+ -+&rtc { -+ status = "okay"; -+}; -+ -+&rsss { -+ status = "okay"; -+}; -+ -+&ecdsa { -+ status = "okay"; -+}; -+ -+&hace { -+ status = "okay"; -+}; -+ -+#if PCIE0_EP -+&bmc_dev0 { -+ status = "okay"; -+ memory-region = <&bmc_dev0_memory>; -+}; -+ -+&xdma0 { -+ status = "okay"; -+ memory-region = <&xdma_memory0>; -+}; -+ -+&pcie_vuart0 { -+ port = <0x3f8>; -+ sirq = <4>; -+ sirq-polarity = <0>; -+ -+ status = "okay"; -+}; -+ -+&pcie_vuart1 { -+ port = <0x2f8>; -+ sirq = <3>; -+ sirq-polarity = <0>; -+ -+ status = "okay"; -+}; -+ -+&pcie_lpc0_kcs0 { -+ status = "okay"; -+ kcs-io-addr = <0x3a0>; -+ kcs-channel = <8>; -+}; -+ -+&pcie_lpc0_kcs1 { -+ status = "okay"; -+ kcs-io-addr = <0x3a8>; -+ kcs-channel = <9>; -+}; -+ -+&pcie_lpc0_kcs2 { -+ status = "okay"; -+ kcs-io-addr = <0x3a2>; -+ kcs-channel = <10>; -+}; -+ -+&pcie_lpc0_kcs3 { -+ status = "okay"; -+ kcs-io-addr = <0x3a4>; -+ kcs-channel = <11>; -+}; -+ -+&pcie_lpc0_ibt { -+ status = "okay"; -+ bt-channel = <2>; -+}; -+ -+&pcie0_mmbi0 { -+ status = "okay"; -+ memory-region = <&pcie0_mmbi0_memory>; -+ -+ mmbi-bmc-int-value = /bits/ 8 <0x00>; -+ mmbi-bmc-int-offset = <0x100000>; -+}; -+#else -+&pcie0 { -+ status = "okay"; -+}; -+#endif -+ -+#if PCIE1_EP -+&bmc_dev1 { -+ status = "okay"; -+ memory-region = <&bmc_dev1_memory>; -+}; -+ -+&xdma1 { -+ status = "okay"; -+ memory-region = <&xdma_memory1>; -+}; -+ -+&pcie_vuart2 { -+ port = <0x3f8>; -+ sirq = <4>; -+ sirq-polarity = <0>; -+ -+ status = "okay"; -+}; -+ -+&pcie_vuart3 { -+ port = <0x2f8>; -+ sirq = <3>; -+ sirq-polarity = <0>; -+ -+ status = "okay"; -+}; -+ -+&pcie_lpc1_kcs0 { -+ status = "okay"; -+ kcs-io-addr = <0x3a0>; -+ kcs-channel = <12>; -+}; -+ -+&pcie_lpc1_kcs1 { -+ status = "okay"; -+ kcs-io-addr = <0x3a8>; -+ kcs-channel = <13>; -+}; -+ -+&pcie_lpc1_kcs2 { -+ status = "okay"; -+ kcs-io-addr = <0x3a2>; -+ kcs-channel = <14>; -+}; -+ -+&pcie_lpc1_kcs3 { -+ status = "okay"; -+ kcs-io-addr = <0x3a4>; -+ kcs-channel = <15>; -+}; -+ -+&pcie_lpc1_ibt { -+ status = "okay"; -+ bt-channel = <3>; -+}; -+ -+&pcie1_mmbi4 { -+ status = "okay"; -+ memory-region = <&pcie1_mmbi4_memory>; -+ -+ mmbi-bmc-int-value = /bits/ 8 <0x44>; -+ mmbi-bmc-int-offset = <0x100400>; -+}; -+#else -+&pcie1 { -+ status = "okay"; -+}; -+#endif -+ -+#if 0 -+&i3c0 { -+ status = "okay"; -+}; -+ -+&jtag0 { -+ status = "okay"; -+}; -+#endif -+ -+&sdio_controller { -+ status = "okay"; -+ mmc-hs200-1_8v; -+ -+ vcc_sdhci0: regulator-vcc-sdhci0 { -+ compatible = "regulator-fixed"; -+ regulator-name = "SDHCI0 Vcc"; -+ regulator-min-microvolt = <3300000>; -+ regulator-max-microvolt = <3300000>; -+ gpios = <&gpio0 ASPEED_GPIO(G, 6) GPIO_ACTIVE_HIGH>; -+ enable-active-high; -+ }; -+ -+ vccq_sdhci0: regulator-vccq-sdhci0 { -+ compatible = "regulator-gpio"; -+ regulator-name = "SDHCI0 VccQ"; -+ regulator-min-microvolt = <1800000>; -+ regulator-max-microvolt = <3300000>; -+ gpios = <&gpio0 ASPEED_GPIO(G, 7) GPIO_ACTIVE_HIGH>; -+ gpios-states = <1>; -+ states = <3300000 1>, -+ <1800000 0>; -+ }; -+ -+}; -+ -+&sdhci { -+ status = "okay"; -+ bus-width = <4>; -+ max-frequency = <100000000>; -+ /* DDR50 bits in CAPA2 are not supported */ -+ sdhci-caps-mask = <0x6 0x0>; -+ sdhci-drive-type = /bits/ 8 <3>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&pinctrl_sd_default>; -+ vmmc-supply = <&vcc_sdhci0>; -+ vqmmc-supply = <&vccq_sdhci0>; -+ sd-uhs-sdr104; /* enable sdr104 to execute tuning */ -+}; -+ -+&i2c0 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c1 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c2 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c3 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c4 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c5 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c6 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c7 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c8 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c9 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c10 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c11 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c12 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c13 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c14 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+&i2c15 { -+ status = "okay"; -+ clock-frequency = <1000000>; -+ multi-master; -+}; -+ -+#if 0 -+&ehci0 { -+ status = "okay"; -+}; -+ -+&ehci1 { -+ status = "okay"; -+}; -+ -+&uhci0 { -+ status = "okay"; -+}; -+ -+#endif -+ -+#if 1 -+&uphy3a { -+ status = "okay"; -+}; -+ -+&uphy3b { -+ status = "okay"; -+}; -+#endif -+ -+#if 0 -+&xhci0 { -+ status = "okay"; -+}; -+ -+&xhci1 { -+ status = "okay"; -+}; -+#endif -+ -+&vhuba0 { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb2ahpd0_default>; -+}; -+ -+&usb3ahp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; -+}; -+ -+&usb3bhp { -+ status = "okay"; -+}; -+ -+&uphy2b { -+ status = "okay"; -+}; -+ -+&vhubb1 { -+ status = "okay"; -+}; -+ -+&vhubc { -+ status = "okay"; -+}; -+ -+&ehci3 { -+ status = "okay"; -+}; -+ -+&uhci1 { -+ status = "okay"; -+}; -+ -+&wdt0 { -+ status = "okay"; -+}; -+ -+&wdt1 { -+ status = "okay"; -+}; -+ -+&otp { -+ status = "okay"; -+}; -diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts ---- a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,1175 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+ -+/dts-v1/; -+ -+#include "aspeed-g7.dtsi" -+#include -+#include -+#define PCIE0_EP 1 // 1: EP, 0: RC -+#define PCIE1_EP 1 // 1: EP, 0: RC ++#include "aspeed-g7.dtsi" ++#include ++#include ++#define PCIE0_EP 1 // 1: EP, 0: RC ++#define PCIE1_EP 1 // 1: EP, 0: RC + +/ { + model = "AST2700-EVB"; @@ -13784,8 +13328,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts + status = "okay"; + memory-region = <&pcie0_mmbi0_memory>; + -+ mmbi-bmc-int-value = /bits/ 8 <0x00>; -+ mmbi-bmc-int-offset = <0x100000>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else +&pcie0 { @@ -13853,8 +13403,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts + status = "okay"; + memory-region = <&pcie1_mmbi4_memory>; + -+ mmbi-bmc-int-value = /bits/ 8 <0x44>; -+ mmbi-bmc-int-offset = <0x100400>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else +&pcie1 { @@ -14023,7 +13579,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts + +#endif + -+#if 1 ++#if 0 +&uphy3a { + status = "okay"; +}; @@ -14048,19 +13604,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts + pinctrl-0 = <&pinctrl_usb2ahpd0_default>; +}; + -+&usb3ahp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; -+}; -+ -+&usb3bhp { -+ status = "okay"; -+}; -+ -+&uphy2b { -+ status = "okay"; -+}; -+ +&vhubb1 { + status = "okay"; +}; @@ -14090,8 +13633,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-evbi2c.dts b/arch/arm64/boot/dts +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,1128 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts 2026-07-17 08:44:38.763561448 +0000 +@@ -0,0 +1,1127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -14958,8 +14501,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/a + status = "okay"; + memory-region = <&pcie0_mmbi0_memory>; + -+ bmc-int-value = /bits/ 8 <0x00>; -+ bmc-int-location = <0>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else +&pcie0 { @@ -15027,8 +14576,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/a + status = "okay"; + memory-region = <&pcie1_mmbi4_memory>; + -+ bmc-int-value = /bits/ 8 <0x00>; -+ bmc-int-location = <0>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else +&pcie1 { @@ -15152,7 +14707,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/a + +#endif + -+#if 1 ++#if 0 +&uphy3a { + status = "okay"; +}; @@ -15177,19 +14732,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/a + pinctrl-0 = <&pinctrl_usb2ahpd0_default>; +}; + -+&usb3ahp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; -+}; -+ -+&usb3bhp { -+ status = "okay"; -+}; -+ -+&uphy2b { -+ status = "okay"; -+}; -+ +&vhubb1 { + status = "okay"; +}; @@ -15222,7 +14764,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-fpga.dts b/arch/arm64/boot/dts/a +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts b/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -15274,7 +14816,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-ncsi.dts b/arch/arm64/boot/dts/a +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-raw.dts b/arch/arm64/boot/dts/aspeed/ast2700-raw.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-raw.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-raw.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-raw.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -15498,7 +15040,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-raw.dts b/arch/arm64/boot/dts/as + diff --git a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi b/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi --- a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -15630,8 +15172,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-reserved-mem.dtsi b/arch/arm64/b +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts b/arch/arm64/boot/dts/aspeed/ast2700-slt.dts --- a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700-slt.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,844 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700-slt.dts 2026-07-17 08:44:38.763561448 +0000 +@@ -0,0 +1,850 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -16326,8 +15868,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts b/arch/arm64/boot/dts/as + status = "okay"; + memory-region = <&pcie0_mmbi0_memory>; + -+ mmbi-bmc-int-value = /bits/ 8 <0x00>; -+ mmbi-bmc-int-offset = <0x100000>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#endif + @@ -16478,7 +16026,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700-slt.dts b/arch/arm64/boot/dts/as +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -16609,8 +16157,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-ci-host.dts b/arch/arm64/boot/ +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,974 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts 2026-07-17 08:44:38.763561448 +0000 +@@ -0,0 +1,991 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -17326,6 +16874,22 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts b/arch/arm64/boot/dt +}; + +&hace { ++ dummy-key0 = < ++ 0x89878915 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key1 = < ++ 0x89878902 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key2 = < ++ 0x2C914FA3 0x1056D87B 0xC13D6AE2 0x5E44AF98 ++ 0x0DB97321 0xCA378CF6 0x62EE115D 0x3FA74B90 ++ >; ++ dummy-key3 = < ++ 0x9E18D25C 0x6FB17A43 0x348DE520 0x1269FAC7 ++ 0xAC5503DE 0x2DB61F88 0xEF9AC074 0xB84E6731 ++ >; + status = "okay"; +}; + @@ -17531,32 +17095,11 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts b/arch/arm64/boot/dt + status = "okay"; +}; + -+&uphy3a { -+ status = "okay"; -+}; -+ -+&uphy3b { -+ status = "okay"; -+}; -+ +&vhuba0 { + status = "okay"; + pinctrl-0 = <&pinctrl_usb2ahpd0_default>; +}; + -+&usb3ahp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; -+}; -+ -+&usb3bhp { -+ status = "okay"; -+}; -+ -+&uphy2b { -+ status = "okay"; -+}; -+ +&vhubb1 { + status = "okay"; +}; @@ -17585,9 +17128,31 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm.dts b/arch/arm64/boot/dt +&otp { + status = "okay"; +}; ++ ++#if 0 ++&soc0 { ++ mbox-ssp-0 { ++ compatible = "aspeed,aspeed-mbox"; ++ reg = <0x4 0x31480000 0x0 0x200000>, <0x0 0x10001000 0x0 0x1000>; ++ mboxes = <&mbox0 0>; ++ aspeed,tx-timeout = <100>; ++ }; ++}; ++#endif ++ ++#if 1 ++&soc0 { ++ mbox-bootmcu-1 { ++ compatible = "aspeed,aspeed-mbox"; ++ reg = <0x4 0x31880000 0x0 0x100000>, <0x4 0x31980000 0x0 0x100000>; ++ mboxes = <&mbox2 1>; ++ aspeed,tx-timeout = <10000>; ++ }; ++}; ++#endif diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -17809,7 +17374,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb-dual.dts b +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -18269,7 +17834,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1700a1-evb.dts b/arch +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -18709,7 +18274,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-dcscm_ast1800-evb.dts b/arch/a +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -18753,8 +18318,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-256-abr.dts b/arch/arm64/b + diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,367 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts 2026-07-17 08:44:38.763561448 +0000 +@@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ast2700a1-evb.dts" @@ -18825,6 +18390,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18837,6 +18403,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18849,6 +18416,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18861,6 +18429,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18873,6 +18442,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18885,6 +18455,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18897,6 +18468,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18909,6 +18481,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18921,6 +18494,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18933,6 +18507,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18945,6 +18520,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18957,6 +18533,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18969,6 +18546,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18981,6 +18559,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -18993,6 +18572,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19005,6 +18585,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@10 { + compatible = "mctp-i2c-controller"; + reg = <(0x10 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19124,8 +18705,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s0.dts b/arch/arm64/boot/d +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,481 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts 2026-07-17 08:44:38.763561448 +0000 +@@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ast2700a1-evb.dts" @@ -19258,6 +18839,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19270,6 +18852,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19282,6 +18865,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19294,6 +18878,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19306,6 +18891,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19318,6 +18904,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19330,6 +18917,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19342,6 +18930,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19354,6 +18943,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19366,6 +18956,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19378,6 +18969,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19390,6 +18982,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19402,6 +18995,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19414,6 +19008,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19426,6 +19021,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "buff"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19438,6 +19034,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + status = "okay"; + multi-master; + mctp-controller; ++ aspeed,transfer-mode = "dma"; + mctp@12 { + compatible = "mctp-i2c-controller"; + reg = <(0x12 | I2C_OWN_SLAVE_ADDRESS)>; @@ -19568,18 +19165,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d + pinctrl-0 = <&pinctrl_usb2ad0_default>; +}; + -+&uphy2a { -+ status = "okay"; -+}; -+ -+&usb3ahp { -+ status = "disabled"; -+}; -+ -+&usb3bhp { -+ status = "disabled"; -+}; -+ +&vhubb0 { + status = "okay"; +}; @@ -19609,8 +19194,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb-s1.dts b/arch/arm64/boot/d +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts 2026-04-08 18:03:31.588010588 +0000 -@@ -0,0 +1,1265 @@ ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts 2026-07-17 08:44:38.763561448 +0000 +@@ -0,0 +1,1306 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/dts-v1/; @@ -19658,6 +19243,20 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ + reusable; + }; + ++ rvas_memory0: rvas0{ ++ size = <0x0 0x04000000>; ++ alignment = <0x0 0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ ++ rvas_memory1: rvas1{ ++ size = <0x0 0x04000000>; ++ alignment = <0x0 0x01000000>; ++ compatible = "shared-dma-pool"; ++ reusable; ++ }; ++ +#if 0 + gfx_memory: framebuffer { + size = <0x0 0x01000000>; @@ -20511,6 +20110,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ + status = "okay"; +}; + ++#if 1 +&video0 { + status = "okay"; + memory-region = <&video_engine_memory0>; @@ -20520,6 +20120,17 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ + status = "okay"; + memory-region = <&video_engine_memory1>; +}; ++#else ++&rvas0{ ++ status = "okay"; ++ memory-region = <&rvas_memory0>; ++}; ++ ++&rvas1{ ++ status = "okay"; ++ memory-region = <&rvas_memory1>; ++}; ++#endif + +&disp_intf { + status = "okay"; @@ -20538,6 +20149,22 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ +}; + +&hace { ++ dummy-key0 = < ++ 0x89878915 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key1 = < ++ 0x89878902 0x1DA242FA 0x1A7885CE 0x5BD7FA39 ++ 0xC53265FD 0xCC346B25 0x230370EE 0xD0570629 ++ >; ++ dummy-key2 = < ++ 0x2C914FA3 0x1056D87B 0xC13D6AE2 0x5E44AF98 ++ 0x0DB97321 0xCA378CF6 0x62EE115D 0x3FA74B90 ++ >; ++ dummy-key3 = < ++ 0x9E18D25C 0x6FB17A43 0x348DE520 0x1269FAC7 ++ 0xAC5503DE 0x2DB61F88 0xEF9AC074 0xB84E6731 ++ >; + status = "okay"; +}; + @@ -20601,8 +20228,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ + status = "okay"; + memory-region = <&pcie0_mmbi0_memory>; + -+ bmc-int-value = /bits/ 8 <0x00>; -+ bmc-int-location = <0>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else /* !PCIE0_EP */ +&pcie0 { @@ -20670,8 +20303,14 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ + status = "okay"; + memory-region = <&pcie1_mmbi4_memory>; + -+ bmc-int-value = /bits/ 8 <0x00>; -+ bmc-int-location = <0>; ++ noi = <7>; ++ aspeed,mmbi-interface = "mctp-netdev", /* ch0 */ ++ "ioctl", /* ch1 */ ++ "ioctl", /* ch2 */ ++ "ioctl", /* ch3 */ ++ "ioctl", /* ch4 */ ++ "ioctl", /* ch5 */ ++ "ioctl"; /* ch6 */ +}; +#else /* !PCIE1_EP */ +&pcie1 { @@ -20784,7 +20423,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ + +#endif + -+#if 1 ++#if 0 +&uphy3a { + status = "okay"; +}; @@ -20809,19 +20448,6 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ + pinctrl-0 = <&pinctrl_usb2ahpd0_default>; +}; + -+&usb3ahp { -+ status = "okay"; -+ pinctrl-0 = <&pinctrl_usb3axhp_default &pinctrl_usb2axhp_default>; -+}; -+ -+&usb3bhp { -+ status = "okay"; -+}; -+ -+&uphy2b { -+ status = "okay"; -+}; -+ +&vhubb1 { + status = "okay"; +}; @@ -20878,7 +20504,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-evb.dts b/arch/arm64/boot/dts/ +#endif diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -20930,7 +20556,7 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-ncsi.dts b/arch/arm64/boot/dts +}; diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts b/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts --- a/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts 1970-01-01 00:00:00.000000000 +0000 -+++ b/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts 2026-04-08 18:03:31.588010588 +0000 ++++ b/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts 2026-07-17 08:44:38.763561448 +0000 @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + @@ -21153,8 +20779,8 @@ diff --git a/arch/arm64/boot/dts/aspeed/ast2700a1-raw.dts b/arch/arm64/boot/dts/ +}; + diff --git a/crypto/Makefile b/crypto/Makefile ---- a/crypto/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/crypto/Makefile 2026-04-08 18:03:36.190926635 +0000 +--- a/crypto/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/crypto/Makefile 2026-07-17 08:44:43.223492542 +0000 @@ -181,6 +181,7 @@ obj-$(CONFIG_CRYPTO_ECC) += ecc.o obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o @@ -21165,7 +20791,7 @@ diff --git a/crypto/Makefile b/crypto/Makefile ecdh_generic-y += ecdh_helper.o diff --git a/crypto/aspeed_crypt.c b/crypto/aspeed_crypt.c --- a/crypto/aspeed_crypt.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/crypto/aspeed_crypt.c 2026-04-08 18:03:48.306705393 +0000 ++++ b/crypto/aspeed_crypt.c 2026-07-17 08:44:55.285306628 +0000 @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -21432,16 +21058,16 @@ diff --git a/crypto/aspeed_crypt.c b/crypto/aspeed_crypt.c +subsys_initcall(acrypt_mod_init); +module_exit(acrypt_mod_fini); diff --git a/drivers/Makefile b/drivers/Makefile ---- a/drivers/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/Makefile 2026-04-08 18:03:39.533865664 +0000 +--- a/drivers/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/Makefile 2026-07-17 08:44:46.786437624 +0000 @@ -195,3 +195,4 @@ obj-$(CONFIG_DPLL) += dpll/ obj-$(CONFIG_S390) += s390/ +obj-$(CONFIG_JTAG_ASPEED) += jtag/ diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig ---- a/drivers/bus/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/bus/Kconfig 2026-04-08 18:03:34.258961872 +0000 +--- a/drivers/bus/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/bus/Kconfig 2026-07-17 08:44:41.233523214 +0000 @@ -261,6 +261,12 @@ configuration. Allows to adjust the priorities of all master peripherals. @@ -21456,8 +21082,8 @@ diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig source "drivers/bus/mhi/Kconfig" diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile ---- a/drivers/bus/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/bus/Makefile 2026-04-08 18:03:38.952876260 +0000 +--- a/drivers/bus/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/bus/Makefile 2026-07-17 08:44:46.220446348 +0000 @@ -39,6 +39,7 @@ obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o @@ -21468,8 +21094,8 @@ diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile obj-y += mhi/ diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c --- a/drivers/bus/aspeed-ltpi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/bus/aspeed-ltpi.c 2026-04-08 18:03:48.139708444 +0000 -@@ -0,0 +1,1173 @@ ++++ b/drivers/bus/aspeed-ltpi.c 2026-07-17 08:44:55.116309232 +0000 +@@ -0,0 +1,1195 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright ASPEED Technology + @@ -21668,6 +21294,26 @@ diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c + return FIELD_GET(REG_LTPI_LINK_PARTNER_FLAG, reg); +} + ++static void ltpi_dump(struct aspeed_ltpi_priv *ltpi) ++{ ++ int i; ++ ++ dev_warn(ltpi->dev, "\nDumping LTPI registers:\n"); ++ for (i = 0; i < 0x250; i += 0x10) { ++ dev_warn(ltpi->dev, "control[%08x]: %08x %08x %08x %08x\n", i, ++ readl(ltpi->regs + i), readl(ltpi->regs + i + 4), ++ readl(ltpi->regs + i + 8), readl(ltpi->regs + i + 12)); ++ } ++ ++ for (i = 0; i < 0x30; i += 0x10) { ++ dev_warn(ltpi->dev, "PHY[%08x]: %08x %08x %08x %08x\n", i, ++ readl(ltpi->phy_regs + i), ++ readl(ltpi->phy_regs + i + 4), ++ readl(ltpi->phy_regs + i + 8), ++ readl(ltpi->phy_regs + i + 12)); ++ } ++} ++ +static irqreturn_t aspeed_ltpi_irq_handler(int irq, void *dev_id) +{ + struct aspeed_ltpi_priv *priv = dev_id; @@ -22159,6 +21805,7 @@ diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c + + dev_warn(ltpi->dev, + "Failed to enter operational state, restarting link training\n"); ++ ltpi_dump(ltpi); + } while (1); + + dev_info(ltpi->dev, "LTPI Link trained successfully\n"); @@ -22166,6 +21813,7 @@ diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c + +ltpi_scm_exit: + dev_err(ltpi->dev, "Exiting initialization\n"); ++ ltpi_dump(ltpi); + ltpi_reset(ltpi); + return ret; +} @@ -22575,7 +22223,7 @@ diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c + priv->ad_timeout = ADVERTISE_TIMEOUT_US; + priv->io_driving = 0x2; + priv->clk_inverse = 0x0; -+ priv->link_speed_frm_rx_cnt = 4; ++ priv->link_speed_frm_rx_cnt = 0; + priv->disable_auto_downshift = 0; + priv->crc_format = 0; + priv->otp_ddr_dis = false; @@ -22645,8 +22293,8 @@ diff --git a/drivers/bus/aspeed-ltpi.c b/drivers/bus/aspeed-ltpi.c +MODULE_LICENSE("GPL"); diff --git a/drivers/bus/aspeed-ltpi.h b/drivers/bus/aspeed-ltpi.h --- a/drivers/bus/aspeed-ltpi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/bus/aspeed-ltpi.h 2026-04-08 18:03:48.134708536 +0000 -@@ -0,0 +1,276 @@ ++++ b/drivers/bus/aspeed-ltpi.h 2026-07-17 08:44:55.111309310 +0000 +@@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2024 ASPEED Technology Inc. @@ -22918,15 +22566,14 @@ diff --git a/drivers/bus/aspeed-ltpi.h b/drivers/bus/aspeed-ltpi.h +#define REG_LTPI_STRAP_1700_EN BIT(0) +#define LTPI_SW_FORCE_EN 0x010 +#define LTPI_SW_FORCE_VAL 0x014 -+#define REG_LTPI_SW_FORCE_LVDS_TX_DS_EN BIT(2) -+#define REG_LTPI_SW_FORCE_2LTPI_EN BIT(1) ++#define REG_LTPI_SW_FORCE_LVDS_TX_DS_EN BIT(1) +#define REG_LTPI_SW_FORCE_1700_EN BIT(0) + +#endif /* __ASPEED_LTPI_H__ */ diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig ---- a/drivers/char/hw_random/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/char/hw_random/Kconfig 2026-04-08 18:03:35.280943232 +0000 -@@ -587,6 +587,21 @@ +--- a/drivers/char/hw_random/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/char/hw_random/Kconfig 2026-07-17 08:44:42.299506783 +0000 +@@ -588,6 +588,21 @@ If unsure, say Y. @@ -22949,8 +22596,8 @@ diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig config UML_RANDOM diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile ---- a/drivers/char/hw_random/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/char/hw_random/Makefile 2026-04-08 18:03:40.212853274 +0000 +--- a/drivers/char/hw_random/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/char/hw_random/Makefile 2026-07-17 08:44:47.446427452 +0000 @@ -50,3 +50,5 @@ obj-$(CONFIG_HW_RANDOM_POLARFIRE_SOC) += mpfs-rng.o obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o @@ -22959,7 +22606,7 @@ diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile +obj-$(CONFIG_HW_RANDOM_DWC) += dwc/ diff --git a/drivers/char/hw_random/aspeed-rng.c b/drivers/char/hw_random/aspeed-rng.c --- a/drivers/char/hw_random/aspeed-rng.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/aspeed-rng.c 2026-04-08 18:03:48.278705905 +0000 ++++ b/drivers/char/hw_random/aspeed-rng.c 2026-07-17 08:44:55.258307044 +0000 @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -23097,7 +22744,7 @@ diff --git a/drivers/char/hw_random/aspeed-rng.c b/drivers/char/hw_random/aspeed +MODULE_DESCRIPTION("Aspeed true random number generator driver"); diff --git a/drivers/char/hw_random/dwc/Kconfig b/drivers/char/hw_random/dwc/Kconfig --- a/drivers/char/hw_random/dwc/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/Kconfig 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/Kconfig 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +# @@ -23119,7 +22766,7 @@ diff --git a/drivers/char/hw_random/dwc/Kconfig b/drivers/char/hw_random/dwc/Kco + diff --git a/drivers/char/hw_random/dwc/Makefile b/drivers/char/hw_random/dwc/Makefile --- a/drivers/char/hw_random/dwc/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/Makefile 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/Makefile 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 + @@ -23145,7 +22792,7 @@ diff --git a/drivers/char/hw_random/dwc/Makefile b/drivers/char/hw_random/dwc/Ma + \) -type f -print | xargs rm -rvf diff --git a/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h b/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h --- a/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -23245,7 +22892,7 @@ diff --git a/drivers/char/hw_random/dwc/src/pdu/common/include/elppdu_error.h b/ +#endif diff --git a/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c b/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c --- a/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -23414,7 +23061,7 @@ diff --git a/drivers/char/hw_random/dwc/src/pdu/common/pdu/pdu_dev32.c b/drivers +EXPORT_SYMBOL(pdu_from_dev32_s); diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h b/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h --- a/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -23543,7 +23190,7 @@ diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/include/elppdu.h b/drivers + diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c --- a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -23735,7 +23382,7 @@ diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/pdu.c b/drivers/cha +module_exit(pdu_mod_exit); diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c --- a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -23930,7 +23577,7 @@ diff --git a/drivers/char/hw_random/dwc/src/pdu/linux/kernel/spacc_mem.c b/drive +MODULE_AUTHOR("Synopsys, Inc."); diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h --- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -23997,7 +23644,7 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng.h b/drivers/ch +#endif diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h --- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h 2026-07-17 08:44:53.513333940 +0000 @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// ------------------------------------------------------------------------ @@ -24145,7 +23792,7 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_common.h b/dri +#endif diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h --- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h 2026-07-17 08:44:53.514333924 +0000 @@ -0,0 +1,457 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -24606,7 +24253,7 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_hw.h b/drivers +#endif diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h --- a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h 2026-07-17 08:44:53.514333924 +0000 @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -24699,7 +24346,7 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/include/nisttrng_private.h b/dr +#endif diff --git a/drivers/char/hw_random/dwc/src/trng/include/synversion.h b/drivers/char/hw_random/dwc/src/trng/include/synversion.h --- a/drivers/char/hw_random/dwc/src/trng/include/synversion.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/include/synversion.h 2026-04-08 18:03:46.629736033 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/include/synversion.h 2026-07-17 08:44:53.514333924 +0000 @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -24755,7 +24402,7 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/include/synversion.h b/drivers/ +#endif diff --git a/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c b/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c --- a/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c 2026-04-08 18:03:46.630736014 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c 2026-07-17 08:44:53.514333924 +0000 @@ -0,0 +1,2170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -26929,7 +26576,7 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/kernel/nist_trng.c b/drivers/ch +MODULE_AUTHOR("Synopsys, Inc."); diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c --- a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c 2026-04-08 18:03:46.630736014 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c 2026-07-17 08:44:53.514333924 +0000 @@ -0,0 +1,956 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -27889,7 +27536,7 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng.c b/drivers/char +EXPORT_SYMBOL(nisttrng_generate); diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c --- a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c 2026-04-08 18:03:46.630736014 +0000 ++++ b/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c 2026-07-17 08:44:53.514333924 +0000 @@ -0,0 +1,1022 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -28914,8 +28561,8 @@ diff --git a/drivers/char/hw_random/dwc/src/trng/trng/nist_trng_private.c b/driv + return err; +} diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspeed.c ---- a/drivers/char/ipmi/kcs_bmc_aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/char/ipmi/kcs_bmc_aspeed.c 2026-04-08 18:03:48.285705777 +0000 +--- a/drivers/char/ipmi/kcs_bmc_aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/char/ipmi/kcs_bmc_aspeed.c 2026-07-17 08:44:55.264306951 +0000 @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* @@ -29938,8 +29585,8 @@ diff --git a/drivers/char/ipmi/kcs_bmc_aspeed.c b/drivers/char/ipmi/kcs_bmc_aspe +MODULE_AUTHOR("Chia-Wei Wang "); MODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device"); diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig ---- a/drivers/clk/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/Kconfig 2026-04-08 18:03:32.729989759 +0000 +--- a/drivers/clk/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/clk/Kconfig 2026-07-17 08:44:39.671547376 +0000 @@ -277,6 +277,20 @@ The G4 and G5 series, including the ast2400 and ast2500, are supported by this driver. @@ -29962,8 +29609,8 @@ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig tristate "Clock driver for S2MPS1X/S5M8767 MFD" depends on MFD_SEC_CORE || COMPILE_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile ---- a/drivers/clk/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/Makefile 2026-04-08 18:03:37.201908196 +0000 +--- a/drivers/clk/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/clk/Makefile 2026-07-17 08:44:44.241476851 +0000 @@ -48,6 +48,8 @@ obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o @@ -29974,8 +29621,8 @@ diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_K210) += clk-k210.o diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c ---- a/drivers/clk/clk-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/clk-aspeed.c 2026-04-08 18:03:47.783714949 +0000 +--- a/drivers/clk/clk-aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/clk/clk-aspeed.c 2026-07-17 08:44:54.689315814 +0000 @@ -54,15 +54,15 @@ [ASPEED_CLK_GATE_DCLK] = { 5, -1, "dclk-gate", NULL, CLK_IS_CRITICAL }, /* DAC */ [ASPEED_CLK_GATE_REFCLK] = { 6, -1, "refclk-gate", "clkin", CLK_IS_CRITICAL }, @@ -30006,7 +29653,7 @@ diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c * SCUD4 resets start at an offset to separate them from diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c --- a/drivers/clk/clk-ast1700.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/clk/clk-ast1700.c 2026-04-08 18:03:47.788714857 +0000 ++++ b/drivers/clk/clk-ast1700.c 2026-07-17 08:44:54.694315737 +0000 @@ -0,0 +1,809 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright ASPEED Technology @@ -30818,8 +30465,8 @@ diff --git a/drivers/clk/clk-ast1700.c b/drivers/clk/clk-ast1700.c +CLK_OF_DECLARE_DRIVER(ast1700, "aspeed,ast1700-scu", AST1700_clk_init); + diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c ---- a/drivers/clk/clk-ast2600.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/clk/clk-ast2600.c 2026-04-08 18:03:47.794714748 +0000 +--- a/drivers/clk/clk-ast2600.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/clk/clk-ast2600.c 2026-07-17 08:44:54.700315644 +0000 @@ -19,7 +19,7 @@ * This includes the gates (configured from aspeed_g6_gates), plus the * explicitly-configured clocks (ASPEED_CLK_HPLL and up). @@ -30829,32 +30476,21 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c #define ASPEED_G6_SILICON_REV 0x014 #define CHIP_REVISION_ID GENMASK(23, 16) -@@ -59,8 +59,24 @@ +@@ -59,9 +59,13 @@ #define ASPEED_G6_STRAP1 0x500 +#define ASPEED_UARTCLK_FROM_UXCLK 0x338 + #define ASPEED_MAC12_CLK_DLY 0x340 -+#define ASPEED_MAC12_CLK_DLY_100M 0x348 -+#define ASPEED_MAC12_CLK_DLY_10M 0x34C -+ #define ASPEED_MAC34_CLK_DLY 0x350 -+#define ASPEED_MAC34_CLK_DLY_100M 0x358 -+#define ASPEED_MAC34_CLK_DLY_10M 0x35C -+ + +#define ASPEED_G6_MAC34_DRIVING_CTRL 0x458 + -+#define ASPEED_G6_DEF_MAC12_DELAY_1G 0x0028a410 -+#define ASPEED_G6_DEF_MAC12_DELAY_100M 0x00410410 -+#define ASPEED_G6_DEF_MAC12_DELAY_10M 0x00410410 -+#define ASPEED_G6_DEF_MAC34_DELAY_1G 0x00104208 -+#define ASPEED_G6_DEF_MAC34_DELAY_100M 0x00104208 -+#define ASPEED_G6_DEF_MAC34_DELAY_10M 0x00104208 - /* Globally visible clocks */ static DEFINE_SPINLOCK(aspeed_g6_clk_lock); -@@ -72,6 +88,45 @@ + +@@ -72,6 +76,45 @@ /* AST2600 revision: A0, A1, A2, etc */ static u8 soc_rev; @@ -30900,7 +30536,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c /* * The majority of the clocks in the system are gates paired with a reset * controller that holds the IP in reset; this is represented by the @reset_idx -@@ -99,14 +154,14 @@ +@@ -99,14 +142,14 @@ * ref0 and ref1 are essential for the SoC to operate * mpll is required if SDRAM is used */ @@ -30917,7 +30553,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c /* From dpll */ [ASPEED_CLK_GATE_DCLK] = { 5, -1, "dclk-gate", NULL, CLK_IS_CRITICAL }, /* DAC */ [ASPEED_CLK_GATE_REF0CLK] = { 6, -1, "ref0clk-gate", "clkin", CLK_IS_CRITICAL }, -@@ -128,8 +183,8 @@ +@@ -128,8 +171,8 @@ /* Reserved 26 */ [ASPEED_CLK_GATE_EMMCCLK] = { 27, 16, "emmcclk-gate", NULL, 0 }, /* For card clk */ /* Reserved 28/29/30 */ @@ -30928,7 +30564,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c [ASPEED_CLK_GATE_REF1CLK] = { 34, -1, "ref1clk-gate", "clkin", CLK_IS_CRITICAL }, /* Reserved 35 */ [ASPEED_CLK_GATE_SDCLK] = { 36, 56, "sdclk-gate", NULL, 0 }, /* SDIO/SD */ -@@ -143,20 +198,20 @@ +@@ -143,20 +186,20 @@ [ASPEED_CLK_GATE_I3C4CLK] = { 44, 44, "i3c4clk-gate", "i3cclk", 0 }, /* I3C4 */ [ASPEED_CLK_GATE_I3C5CLK] = { 45, 45, "i3c5clk-gate", "i3cclk", 0 }, /* I3C5 */ /* Reserved: 46 & 47 */ @@ -30961,7 +30597,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c [ASPEED_CLK_GATE_FSICLK] = { 62, 59, "fsiclk-gate", "fsiclk", 0 }, /* FSI */ }; -@@ -184,6 +239,18 @@ +@@ -184,6 +227,18 @@ { 0 } }; @@ -30980,7 +30616,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c static const struct clk_div_table ast2600_mac_div_table[] = { { 0x0, 4 }, { 0x1, 4 }, -@@ -384,9 +451,14 @@ +@@ -384,9 +439,14 @@ struct aspeed_reset *ar = to_aspeed_reset(rcdev); u32 rst = get_bit(id); u32 reg = id >= 32 ? ASPEED_G6_RESET_CTRL2 : ASPEED_G6_RESET_CTRL; @@ -30996,7 +30632,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c } static int aspeed_g6_reset_assert(struct reset_controller_dev *rcdev, -@@ -458,11 +530,6 @@ +@@ -458,11 +518,6 @@ return hw; } @@ -31008,7 +30644,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c static const char * const vclk_parent_names[] = { "dpll", "d1pll", -@@ -484,7 +551,7 @@ +@@ -484,7 +539,7 @@ struct aspeed_reset *ar; struct regmap *map; struct clk_hw *hw; @@ -31017,7 +30653,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c int i, ret; map = syscon_node_to_regmap(dev->of_node); -@@ -510,70 +577,50 @@ +@@ -510,70 +565,50 @@ return ret; } @@ -31042,13 +30678,18 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c - if (IS_ERR(hw)) - return PTR_ERR(hw); - aspeed_g6_clk_data->hws[ASPEED_CLK_UARTX] = hw; -- ++ regmap_update_bits(map, ASPEED_G6_CLK_SELECTION1, GENMASK(14, 11), BIT(11)); + - /* EMMC ext clock */ - hw = clk_hw_register_fixed_factor(dev, "emmc_extclk_hpll_in", "hpll", - 0, 1, 2); -- if (IS_ERR(hw)) -- return PTR_ERR(hw); -- ++ /* EMMC ext clock divider */ ++ hw = clk_hw_register_gate(dev, "emmc_extclk_gate", "mpll", 0, ++ scu_g6_base + ASPEED_G6_CLK_SELECTION1, 15, 0, ++ &aspeed_g6_clk_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + - hw = clk_hw_register_mux(dev, "emmc_extclk_mux", - emmc_extclk_parent_names, - ARRAY_SIZE(emmc_extclk_parent_names), 0, @@ -31056,18 +30697,13 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c - 0, &aspeed_g6_clk_lock); - if (IS_ERR(hw)) - return PTR_ERR(hw); -+ regmap_update_bits(map, ASPEED_G6_CLK_SELECTION1, GENMASK(14, 11), BIT(11)); - +- - hw = clk_hw_register_gate(dev, "emmc_extclk_gate", "emmc_extclk_mux", - 0, scu_g6_base + ASPEED_G6_CLK_SELECTION1, - 15, 0, &aspeed_g6_clk_lock); -+ /* EMMC ext clock divider */ -+ hw = clk_hw_register_gate(dev, "emmc_extclk_gate", "mpll", 0, -+ scu_g6_base + ASPEED_G6_CLK_SELECTION1, 15, 0, -+ &aspeed_g6_clk_lock); - if (IS_ERR(hw)) - return PTR_ERR(hw); - +- if (IS_ERR(hw)) +- return PTR_ERR(hw); +- - hw = clk_hw_register_divider_table(dev, "emmc_extclk", - "emmc_extclk_gate", 0, - scu_g6_base + @@ -31120,7 +30756,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c aspeed_g6_clk_data->hws[ASPEED_CLK_SDIO] = hw; /* MAC1/2 RMII 50MHz RCLK */ -@@ -645,8 +692,8 @@ +@@ -645,8 +680,8 @@ return PTR_ERR(hw); aspeed_g6_clk_data->hws[ASPEED_CLK_LHCLK] = hw; @@ -31131,7 +30767,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c /* SoC Display clock selection */ hw = clk_hw_register_mux(dev, "d1clk", d1clk_parent_names, ARRAY_SIZE(d1clk_parent_names), 0, -@@ -677,6 +724,8 @@ +@@ -677,6 +712,8 @@ return PTR_ERR(hw); aspeed_g6_clk_data->hws[ASPEED_CLK_VCLK] = hw; @@ -31140,7 +30776,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c /* Video Engine clock divider */ hw = clk_hw_register_divider_table(dev, "eclk", NULL, 0, scu_g6_base + ASPEED_G6_CLK_SELECTION1, 28, 3, 0, -@@ -686,6 +735,26 @@ +@@ -686,6 +723,26 @@ return PTR_ERR(hw); aspeed_g6_clk_data->hws[ASPEED_CLK_ECLK] = hw; @@ -31167,7 +30803,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c for (i = 0; i < ARRAY_SIZE(aspeed_g6_gates); i++) { const struct aspeed_gate_data *gd = &aspeed_g6_gates[i]; u32 gate_flags; -@@ -749,7 +818,8 @@ +@@ -749,7 +806,8 @@ static void __init aspeed_g6_cc(struct regmap *map) { struct clk_hw *hw; @@ -31177,7 +30813,7 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c clk_hw_register_fixed_rate(NULL, "clkin", NULL, 0, 25000000); -@@ -814,6 +884,55 @@ +@@ -814,6 +872,55 @@ hw = clk_hw_register_fixed_rate(NULL, "usb-phy-40m", NULL, 0, 40000000); aspeed_g6_clk_data->hws[ASPEED_CLK_USBPHY_40M] = hw; @@ -31233,18 +30869,15 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c /* i3c clock: source from apll, divide by 8 */ regmap_update_bits(map, ASPEED_G6_CLK_SELECTION5, I3C_CLK_SELECTION | APLL_DIV_SELECTION, -@@ -829,6 +948,10 @@ +@@ -829,6 +936,7 @@ static void __init aspeed_g6_cc_init(struct device_node *np) { struct regmap *map; -+ struct mac_delay_config mac_cfg; -+ union mac_delay_1g reg_1g; -+ union mac_delay_100_10 reg_100, reg_10; + u32 uart_clk_source = 0; int ret; int i; -@@ -863,6 +986,100 @@ +@@ -863,6 +971,32 @@ return; } @@ -31267,77 +30900,9 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c + regmap_update_bits(map, ASPEED_G6_CLK_SELECTION4, GENMASK(18, 16), + (0x3 << 16)); + -+ /* BIT[31]: MAC1/2 RGMII 125M source = internal PLL -+ * BIT[28]: RGMIICK pad direction = output -+ */ -+ regmap_write(map, ASPEED_MAC12_CLK_DLY, -+ BIT(31) | BIT(28) | ASPEED_G6_DEF_MAC12_DELAY_1G); -+ regmap_write(map, ASPEED_MAC12_CLK_DLY_100M, -+ ASPEED_G6_DEF_MAC12_DELAY_100M); -+ regmap_write(map, ASPEED_MAC12_CLK_DLY_10M, -+ ASPEED_G6_DEF_MAC12_DELAY_10M); -+ -+ /* MAC3/4 RGMII 125M source = RGMIICK pad */ -+ regmap_write(map, ASPEED_MAC34_CLK_DLY, -+ ASPEED_G6_DEF_MAC34_DELAY_1G); -+ regmap_write(map, ASPEED_MAC34_CLK_DLY_100M, -+ ASPEED_G6_DEF_MAC34_DELAY_100M); -+ regmap_write(map, ASPEED_MAC34_CLK_DLY_10M, -+ ASPEED_G6_DEF_MAC34_DELAY_10M); -+ + /* MAC3/4 default pad driving strength */ + regmap_write(map, ASPEED_G6_MAC34_DRIVING_CTRL, 0x0000000f); + -+ regmap_read(map, ASPEED_MAC12_CLK_DLY, ®_1g.w); -+ regmap_read(map, ASPEED_MAC12_CLK_DLY_100M, ®_100.w); -+ regmap_read(map, ASPEED_MAC12_CLK_DLY_10M, ®_10.w); -+ ret = of_property_read_u32_array(np, "mac0-clk-delay", (u32 *)&mac_cfg, 6); -+ if (!ret) { -+ reg_1g.b.tx_delay_1 = mac_cfg.tx_delay_1000; -+ reg_1g.b.rx_delay_1 = mac_cfg.rx_delay_1000; -+ reg_100.b.tx_delay_1 = mac_cfg.tx_delay_100; -+ reg_100.b.rx_delay_1 = mac_cfg.rx_delay_100; -+ reg_10.b.tx_delay_1 = mac_cfg.tx_delay_10; -+ reg_10.b.rx_delay_1 = mac_cfg.rx_delay_10; -+ } -+ ret = of_property_read_u32_array(np, "mac1-clk-delay", (u32 *)&mac_cfg, 6); -+ if (!ret) { -+ reg_1g.b.tx_delay_2 = mac_cfg.tx_delay_1000; -+ reg_1g.b.rx_delay_2 = mac_cfg.rx_delay_1000; -+ reg_100.b.tx_delay_2 = mac_cfg.tx_delay_100; -+ reg_100.b.rx_delay_2 = mac_cfg.rx_delay_100; -+ reg_10.b.tx_delay_2 = mac_cfg.tx_delay_10; -+ reg_10.b.rx_delay_2 = mac_cfg.rx_delay_10; -+ } -+ regmap_write(map, ASPEED_MAC12_CLK_DLY, reg_1g.w); -+ regmap_write(map, ASPEED_MAC12_CLK_DLY_100M, reg_100.w); -+ regmap_write(map, ASPEED_MAC12_CLK_DLY_10M, reg_10.w); -+ -+ regmap_read(map, ASPEED_MAC34_CLK_DLY, ®_1g.w); -+ regmap_read(map, ASPEED_MAC34_CLK_DLY_100M, ®_100.w); -+ regmap_read(map, ASPEED_MAC34_CLK_DLY_10M, ®_10.w); -+ ret = of_property_read_u32_array(np, "mac2-clk-delay", (u32 *)&mac_cfg, 6); -+ if (!ret) { -+ reg_1g.b.tx_delay_1 = mac_cfg.tx_delay_1000; -+ reg_1g.b.rx_delay_1 = mac_cfg.rx_delay_1000; -+ reg_100.b.tx_delay_1 = mac_cfg.tx_delay_100; -+ reg_100.b.rx_delay_1 = mac_cfg.rx_delay_100; -+ reg_10.b.tx_delay_1 = mac_cfg.tx_delay_10; -+ reg_10.b.rx_delay_1 = mac_cfg.rx_delay_10; -+ } -+ ret = of_property_read_u32_array(np, "mac3-clk-delay", (u32 *)&mac_cfg, 6); -+ if (!ret) { -+ reg_1g.b.tx_delay_2 = mac_cfg.tx_delay_1000; -+ reg_1g.b.rx_delay_2 = mac_cfg.rx_delay_1000; -+ reg_100.b.tx_delay_2 = mac_cfg.tx_delay_100; -+ reg_100.b.rx_delay_2 = mac_cfg.rx_delay_100; -+ reg_10.b.tx_delay_2 = mac_cfg.tx_delay_10; -+ reg_10.b.rx_delay_2 = mac_cfg.rx_delay_10; -+ } -+ regmap_write(map, ASPEED_MAC34_CLK_DLY, reg_1g.w); -+ regmap_write(map, ASPEED_MAC34_CLK_DLY_100M, reg_100.w); -+ regmap_write(map, ASPEED_MAC34_CLK_DLY_10M, reg_10.w); -+ + /* A0/A1 need change to RSA clock = HPLL/3, A2/A3 have been set at Rom Code */ + regmap_update_bits(map, ASPEED_G6_CLK_SELECTION1, BIT(19), BIT(19)); + regmap_update_bits(map, ASPEED_G6_CLK_SELECTION1, GENMASK(27, 26), (2 << 26)); @@ -31347,8 +30912,8 @@ diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c if (ret) diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c --- a/drivers/clk/clk-ast2700.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/clk/clk-ast2700.c 2026-04-08 18:03:47.777715058 +0000 -@@ -0,0 +1,1244 @@ ++++ b/drivers/clk/clk-ast2700.c 2026-07-17 08:44:54.683315906 +0000 +@@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2024 ASPEED Technology Inc. @@ -31631,6 +31196,13 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c + +static const struct clk_hw *sdclk_parent_hws[ARRAY_SIZE(sdclk_parent_ids)]; + ++static const unsigned int peciclk_parent_ids[] = { ++ SCU1_CLKIN, ++ SCU1_CLK_HPLL_DIV4 ++}; ++ ++static const struct clk_hw *peciclk_parent_hws[ARRAY_SIZE(peciclk_parent_ids)]; ++ +#define FIXED_CLK(_id, _name, _rate) \ + { \ + .id = _id, \ @@ -31816,6 +31388,7 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c + PLL_CLK(SCU1_CLK_HPLL, CLK_PLL, "soc1-hpll", SCU1_CLKIN, SCU1_HPLL_PARAM), + PLL_CLK(SCU1_CLK_APLL, CLK_PLL, "soc1-apll", SCU1_CLKIN, SCU1_APLL_PARAM), + PLL_CLK(SCU1_CLK_DPLL, CLK_PLL, "soc1-dpll", SCU1_CLKIN, SCU1_DPLL_PARAM), ++ FIXED_FACTOR_CLK(SCU1_CLK_HPLL_DIV4, "soc1-hpll_div4", SCU1_CLK_HPLL, 1, 4), + FIXED_FACTOR_CLK(SCU1_CLK_APLL_DIV2, "soc1-apll_div2", SCU1_CLK_APLL, 1, 2), + FIXED_FACTOR_CLK(SCU1_CLK_APLL_DIV4, "soc1-apll_div4", SCU1_CLK_APLL, 1, 4), + FIXED_FACTOR_CLK(SCU1_CLK_CAN, "canclk", SCU1_CLK_APLL, 1, 10), @@ -31839,6 +31412,8 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c + uxclk_parent_hws, SCU1_CLK_SEL2, 0, 2), + MUX_CLK(SCU1_CLK_HUXCLK, "huxclk", uxclk_parent_ids, ARRAY_SIZE(uxclk_parent_ids), + uxclk_parent_hws, SCU1_CLK_SEL2, 3, 2), ++ MUX_CLK(SCU1_CLK_PECI, "peciclk", peciclk_parent_ids, ARRAY_SIZE(peciclk_parent_ids), ++ peciclk_parent_hws, SCU1_CLK_SEL2, 16, 1), + DIVIDER_CLK(SCU1_CLK_SDCLK, "sdclk", SCU1_CLK_SDMUX, + SCU1_CLK_SEL1, 14, 3, ast2700_clk_div_table), + PLL_CLK(SCU1_CLK_UARTX, CLK_UART_PLL, "uartxclk", SCU1_CLK_UXCLK, SCU1_UXCLK_CTRL), @@ -32594,8 +32169,8 @@ diff --git a/drivers/clk/clk-ast2700.c b/drivers/clk/clk-ast2700.c +} +arch_initcall(clk_ast2700_init); diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig ---- a/drivers/crypto/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/Kconfig 2026-04-08 18:03:48.327705009 +0000 +--- a/drivers/crypto/aspeed/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/crypto/aspeed/Kconfig 2026-07-17 08:44:55.307306289 +0000 @@ -1,26 +1,32 @@ config CRYPTO_DEV_ASPEED - tristate "Support for Aspeed cryptographic engine driver" @@ -32636,7 +32211,7 @@ diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig select CRYPTO_SHA1 select CRYPTO_SHA256 select CRYPTO_SHA512 -@@ -33,25 +39,56 @@ +@@ -33,25 +39,55 @@ config CRYPTO_DEV_ASPEED_HACE_CRYPTO bool "Enable Aspeed Hash & Crypto Engine (HACE) crypto" @@ -32646,8 +32221,7 @@ diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig select CRYPTO_DES select CRYPTO_ECB select CRYPTO_CBC -+ select CRYPTO_CFB -+ select CRYPTO_OFB ++ select CRYPTO_GCM select CRYPTO_CTR help Select here to enable Aspeed Hash & Crypto Engine (HACE) @@ -32699,11 +32273,12 @@ diff --git a/drivers/crypto/aspeed/Kconfig b/drivers/crypto/aspeed/Kconfig + +endif #CRYPTO_DEV_ASPEED diff --git a/drivers/crypto/aspeed/Makefile b/drivers/crypto/aspeed/Makefile ---- a/drivers/crypto/aspeed/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/Makefile 2026-04-08 18:03:48.327705009 +0000 +--- a/drivers/crypto/aspeed/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/crypto/aspeed/Makefile 2026-07-17 08:44:55.307306289 +0000 @@ -1,11 +1,14 @@ hace-hash-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH) := aspeed-hace-hash.o - hace-crypto-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO) := aspeed-hace-crypto.o +-hace-crypto-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO) := aspeed-hace-crypto.o ++hace-crypto-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO) := aspeed-hace-crypto.o aspeed-hace-gcm.o -obj-$(CONFIG_CRYPTO_DEV_ASPEED) += aspeed_crypto.o +obj-$(CONFIG_CRYPTO_DEV_ASPEED_HACE) += aspeed_crypto.o @@ -32720,9 +32295,24 @@ diff --git a/drivers/crypto/aspeed/Makefile b/drivers/crypto/aspeed/Makefile + +obj-$(CONFIG_CRYPTO_DEV_ASPEED_ECDSA) += aspeed-ecdsa.o diff --git a/drivers/crypto/aspeed/aspeed-acry.c b/drivers/crypto/aspeed/aspeed-acry.c ---- a/drivers/crypto/aspeed/aspeed-acry.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-acry.c 2026-04-08 18:03:48.327705009 +0000 -@@ -808,7 +808,7 @@ +--- a/drivers/crypto/aspeed/aspeed-acry.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/crypto/aspeed/aspeed-acry.c 2026-07-17 08:44:55.307306289 +0000 +@@ -789,6 +789,7 @@ + err_engine_rsa_start: + crypto_engine_exit(acry_dev->crypt_engine_rsa); + clk_exit: ++ clk_disable_unprepare(acry_dev->clk); + + return rc; + } +@@ -800,13 +801,14 @@ + aspeed_acry_unregister(acry_dev); + crypto_engine_exit(acry_dev->crypt_engine_rsa); + tasklet_kill(&acry_dev->done_task); ++ clk_disable_unprepare(acry_dev->clk); + } + + MODULE_DEVICE_TABLE(of, aspeed_acry_of_matches); static struct platform_driver aspeed_acry_driver = { .probe = aspeed_acry_probe, @@ -32733,7 +32323,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-acry.c b/drivers/crypto/aspeed/aspeed- .of_match_table = aspeed_acry_of_matches, diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.c b/drivers/crypto/aspeed/aspeed-ecdsa.c --- a/drivers/crypto/aspeed/aspeed-ecdsa.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-ecdsa.c 2026-04-08 18:03:48.327705009 +0000 ++++ b/drivers/crypto/aspeed/aspeed-ecdsa.c 2026-07-17 08:44:55.308306273 +0000 @@ -0,0 +1,779 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -33516,7 +33106,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.c b/drivers/crypto/aspeed/aspeed +MODULE_LICENSE("GPL"); diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.h b/drivers/crypto/aspeed/aspeed-ecdsa.h --- a/drivers/crypto/aspeed/aspeed-ecdsa.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-ecdsa.h 2026-04-08 18:03:48.327705009 +0000 ++++ b/drivers/crypto/aspeed/aspeed-ecdsa.h 2026-07-17 08:44:55.308306273 +0000 @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + @@ -33638,21 +33228,43 @@ diff --git a/drivers/crypto/aspeed/aspeed-ecdsa.h b/drivers/crypto/aspeed/aspeed + +#endif diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/aspeed-hace-crypto.c ---- a/drivers/crypto/aspeed/aspeed-hace-crypto.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace-crypto.c 2026-04-08 18:03:48.327705009 +0000 -@@ -24,6 +24,11 @@ +--- a/drivers/crypto/aspeed/aspeed-hace-crypto.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace-crypto.c 2026-07-17 08:44:55.308306273 +0000 +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -24,6 +25,25 @@ dev_dbg((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__) #endif -+#define ASPEED_SEC_PROTECTION 0x0 -+#define SEC_UNLOCK_PASSWORD 0x349fe38a -+#define ASPEED_VAULT_KEY_CTRL 0x80C -+#define SEC_VK_CTRL_VK_SELECTION BIT(0) ++static int aspeed_hace_find_vault_key(struct aspeed_hace_dev *hace_dev, ++ const u8 *key, unsigned int keylen) ++{ ++ struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine; ++ int i = 0; ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ for (i = 0; i < crypto_engine->vault_key_num; i++) { ++ if (!crypto_engine->dummy_key[i]) ++ continue; ++ ++ if (memcmp(crypto_engine->dummy_key[i], key, keylen) == 0) ++ return i; ++ } ++ ++ return ASPEED_VAULT_KEY_NOT_FOUND; ++} + static int aspeed_crypto_do_fallback(struct skcipher_request *areq) { struct aspeed_cipher_reqctx *rctx = skcipher_request_ctx(areq); -@@ -143,6 +148,7 @@ +@@ -143,6 +163,7 @@ dma_unmap_sg(dev, req->src, rctx->src_nents, DMA_TO_DEVICE); dma_unmap_sg(dev, req->dst, rctx->dst_nents, DMA_FROM_DEVICE); } @@ -33660,7 +33272,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ return aspeed_sk_complete(hace_dev, 0); } -@@ -206,10 +212,21 @@ +@@ -206,10 +227,21 @@ crypto_engine->resume = aspeed_sk_transfer; /* Trigger engines */ @@ -33682,7 +33294,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ ast_hace_write(hace_dev, req->cryptlen, ASPEED_HACE_DATA_LEN); ast_hace_write(hace_dev, rctx->enc_cmd, ASPEED_HACE_CMD); -@@ -222,21 +239,65 @@ +@@ -222,6 +254,8 @@ struct aspeed_sg_list *src_list, *dst_list; dma_addr_t src_dma_addr, dst_dma_addr; struct aspeed_cipher_reqctx *rctx; @@ -33690,13 +33302,8 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ + struct aspeed_cipher_ctx *ctx; struct skcipher_request *req; struct scatterlist *s; -+ int use_vault_key = 0; int src_sg_len; - int dst_sg_len; - int total, i; - int rc; -+ u32 val; - +@@ -232,6 +266,8 @@ CIPHER_DBG(hace_dev, "\n"); req = crypto_engine->req; @@ -33705,52 +33312,22 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ rctx = skcipher_request_ctx(req); rctx->enc_cmd |= HACE_CMD_DES_SG_CTRL | HACE_CMD_SRC_SG_CTRL | - HACE_CMD_AES_KEY_HW_EXP | HACE_CMD_MBUS_REQ_SYNC_EN; - -+ if (crypto_engine->load_vault_key) { -+ writel(SEC_UNLOCK_PASSWORD, hace_dev->sec_regs + ASPEED_SEC_PROTECTION); -+ CIPHER_DBG(hace_dev, "unlock SB, SEC000=0x%x\n", readl(hace_dev->sec_regs + ASPEED_SEC_PROTECTION)); -+ val = readl(hace_dev->sec_regs + ASPEED_VAULT_KEY_CTRL); -+ if (val & BIT(2)) { -+ if (ctx->dummy_key == 1 && !(val & BIT(0))) { -+ use_vault_key = 1; -+ CIPHER_DBG(hace_dev, "Use Vault key 1\n"); -+ } else if (ctx->dummy_key == 2 && (val & BIT(0))) { -+ use_vault_key = 1; -+ CIPHER_DBG(hace_dev, "Use Vault key 2\n"); -+ } else { -+ use_vault_key = 0; -+ } -+ } else { -+ if (ctx->dummy_key == 1) { -+ use_vault_key = 1; -+ val &= ~SEC_VK_CTRL_VK_SELECTION; -+ writel(val, hace_dev->sec_regs + ASPEED_VAULT_KEY_CTRL); -+ CIPHER_DBG(hace_dev, "Set Vault key 1\n"); -+ } else if (ctx->dummy_key == 2) { -+ use_vault_key = 1; -+ val |= SEC_VK_CTRL_VK_SELECTION; -+ writel(val, hace_dev->sec_regs + ASPEED_VAULT_KEY_CTRL); -+ CIPHER_DBG(hace_dev, "Set Vault key 2\n"); -+ } else { -+ use_vault_key = 0; -+ } -+ } -+ writel(0x0, hace_dev->sec_regs + ASPEED_SEC_PROTECTION); -+ CIPHER_DBG(hace_dev, "lock SB, SEC000=0x%x\n", readl(hace_dev->sec_regs + ASPEED_SEC_PROTECTION)); -+ -+ if (use_vault_key) -+ rctx->enc_cmd |= HACE_CMD_AES_KEY_FROM_OTP; -+ else -+ rctx->enc_cmd &= ~HACE_CMD_AES_KEY_FROM_OTP; -+ } -+ - /* BIDIRECTIONAL */ - if (req->dst == req->src) { +@@ -246,7 +282,6 @@ + dev_warn(hace_dev->dev, "dma_map_sg() src error\n"); + return -EINVAL; + } +- + } else { src_sg_len = dma_map_sg(hace_dev->dev, req->src, -@@ -332,8 +393,19 @@ + rctx->src_nents, DMA_TO_DEVICE); +@@ -331,9 +366,24 @@ + /* Memory barrier to ensure all data setup before engine starts */ mb(); ++ if (crypto_engine->vault_key_num && ++ ctx->key_idx < crypto_engine->vault_key_num) ++ ast_sbc_write(hace_dev, ctx->key_idx, ASPEED_VAULT_KEY_CTRL); ++ /* Trigger engines */ + down(&hace_dev->lock); + ast_hace_write(hace_dev, crypto_engine->cipher_ctx_dma, @@ -33768,16 +33345,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ ast_hace_write(hace_dev, req->cryptlen, ASPEED_HACE_DATA_LEN); ast_hace_write(hace_dev, rctx->enc_cmd, ASPEED_HACE_CMD); -@@ -346,7 +418,7 @@ - - } else { - dma_unmap_sg(hace_dev->dev, req->dst, rctx->dst_nents, -- DMA_TO_DEVICE); -+ DMA_FROM_DEVICE); - dma_unmap_sg(hace_dev->dev, req->src, rctx->src_nents, - DMA_TO_DEVICE); - } -@@ -380,9 +452,6 @@ +@@ -380,9 +430,6 @@ rctx->dst_nents = sg_nents(req->dst); rctx->src_nents = sg_nents(req->src); @@ -33787,7 +33355,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ if (rctx->enc_cmd & HACE_CMD_IV_REQUIRE) { if (rctx->enc_cmd & HACE_CMD_DES_SELECT) memcpy(crypto_engine->cipher_ctx + DES_BLOCK_SIZE, -@@ -392,7 +461,8 @@ +@@ -392,7 +439,8 @@ AES_BLOCK_SIZE); } @@ -33797,67 +33365,110 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ memcpy(crypto_engine->cipher_ctx + 16, ctx->key, ctx->key_len); return aspeed_sk_start_sg(hace_dev); -@@ -580,6 +650,8 @@ +@@ -435,6 +483,9 @@ + + CIPHER_DBG(hace_dev, "keylen: %d bits\n", keylen); + ++ /* DES does not support vault key */ ++ ctx->key_idx = ASPEED_VAULT_KEY_NOT_FOUND; ++ + if (keylen != DES_KEY_SIZE && keylen != DES3_EDE_KEY_SIZE) { + dev_warn(hace_dev->dev, "invalid keylen: %d bits\n", keylen); + return -EINVAL; +@@ -539,6 +590,7 @@ + struct crypto_skcipher *cipher = crypto_skcipher_reqtfm(req); + struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(cipher); + struct aspeed_hace_dev *hace_dev = ctx->hace_dev; ++ struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine; + u32 crypto_alg = cmd & HACE_CMD_OP_MODE_MASK; + + if (crypto_alg == HACE_CMD_CBC || crypto_alg == HACE_CMD_ECB) { +@@ -552,6 +604,9 @@ + cmd |= HACE_CMD_AES_SELECT | HACE_CMD_RI_WO_DATA_ENABLE | + HACE_CMD_CONTEXT_LOAD_ENABLE | HACE_CMD_CONTEXT_SAVE_ENABLE; + ++ if (ctx->key_idx < crypto_engine->vault_key_num) ++ cmd |= HACE_CMD_AES_KEY_FROM_OTP; ++ + switch (ctx->key_len) { + case AES_KEYSIZE_128: + cmd |= HACE_CMD_AES128; +@@ -580,6 +635,8 @@ CIPHER_DBG(hace_dev, "keylen: %d bits\n", (keylen * 8)); -+ ctx->dummy_key = find_dummy_key(key, keylen); ++ ctx->key_idx = aspeed_hace_find_vault_key(hace_dev, key, keylen); + if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 && keylen != AES_KEYSIZE_256) return -EINVAL; -@@ -919,19 +991,49 @@ - for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs); i++) - crypto_engine_unregister_skcipher(&aspeed_crypto_algs[i].alg.skcipher); +@@ -635,9 +692,8 @@ + { + struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(tfm); + struct skcipher_alg *alg = crypto_skcipher_alg(tfm); +- const char *name = crypto_tfm_alg_name(&tfm->base); + struct aspeed_hace_alg *crypto_alg; +- ++ const char *name = crypto_tfm_alg_name(&tfm->base); -- if (hace_dev->version != AST2600_VERSION) -+ if (hace_dev->version == AST2500_VERSION) - return; + crypto_alg = container_of(alg, struct aspeed_hace_alg, alg.skcipher.base); + ctx->hace_dev = crypto_alg->hace_dev; +@@ -912,6 +968,45 @@ - for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs_g6); i++) - crypto_engine_unregister_skcipher(&aspeed_crypto_algs_g6[i].alg.skcipher); - } + }; -+#ifdef CONFIG_AST2600_OTP -+static void find_vault_key(struct aspeed_hace_dev *hace_dev) ++void aspeed_register_hace_vault_key(struct aspeed_hace_dev *hace_dev) +{ ++ struct device *dev = hace_dev->dev; + struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine; -+ u32 otp_data[16]; -+ int i; ++ static const char * const key[] = { ASPEED_VAULT_KEY_DTS }; ++ int i = 0; ++ int err = 0; ++ int key_num = hace_dev->version == AST2700_VERSION ? ++ ASPEED_G7_VAULT_KEY_MAX_NUM : ++ ASPEED_G6_VAULT_KEY_MAX_NUM; + -+ crypto_engine->load_vault_key = 0; ++ crypto_engine->vault_key_num = 0; + -+ otp_read_data_buf(0, otp_data, 16); -+ for (i = 0; i < 16; i++) { -+ CIPHER_DBG(hace_dev, "OTPDATA%d=%x\n", i, otp_data[i]); -+ if (((otp_data[i] >> 14) & 0xf) == 1) { -+ CIPHER_DBG(hace_dev, "Found vault key in OTP\n"); -+ crypto_engine->load_vault_key = 1; -+ return; ++ if (hace_dev->version == AST2500_VERSION) ++ return; ++ ++ /* Check all vault key from dts and store in dummy_key array */ ++ for (i = 0; i < key_num; i++) { ++ crypto_engine->dummy_key[i] = ++ devm_kzalloc(dev, DUMMY_KEY_SIZE, GFP_KERNEL); ++ if (!crypto_engine->dummy_key[i]) { ++ dev_err(dev, "Failed to allocate memory for %s\n", key[i]); ++ continue; + } -+ if (otp_data[i] & BIT(13)) -+ break; ++ ++ err = of_property_read_u32_array(dev->of_node, key[i], ++ crypto_engine->dummy_key[i], ++ DUMMY_KEY_SIZE / sizeof(u32)); ++ if (err) { ++ dev_err(dev, "Cannot find %s, skip key register\n", key[i]); ++ devm_kfree(dev, crypto_engine->dummy_key[i]); ++ crypto_engine->dummy_key[i] = NULL; ++ continue; ++ } ++ ++ crypto_engine->vault_key_num = key_num; + } -+ CIPHER_DBG(hace_dev, "Not found vault key in OTP\n"); +} -+#endif + - void aspeed_register_hace_crypto_algs(struct aspeed_hace_dev *hace_dev) + void aspeed_unregister_hace_crypto_algs(struct aspeed_hace_dev *hace_dev) { - int rc, i; + int i; +@@ -919,7 +1014,7 @@ + for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs); i++) + crypto_engine_unregister_skcipher(&aspeed_crypto_algs[i].alg.skcipher); - CIPHER_DBG(hace_dev, "\n"); +- if (hace_dev->version != AST2600_VERSION) ++ if (hace_dev->version == AST2500_VERSION) + return; -+#ifdef CONFIG_AST2600_OTP -+ find_vault_key(hace_dev); -+#else -+ hace_dev->crypto_engine.load_vault_key = 0; -+#endif -+ - for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs); i++) { - aspeed_crypto_algs[i].hace_dev = hace_dev; - rc = crypto_engine_register_skcipher(&aspeed_crypto_algs[i].alg.skcipher); -@@ -941,7 +1043,7 @@ + for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs_g6); i++) +@@ -941,7 +1036,7 @@ } } @@ -33866,7 +33477,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ return; for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs_g6); i++) { -@@ -953,3 +1055,88 @@ +@@ -953,3 +1048,94 @@ } } } @@ -33940,6 +33551,12 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ + } + } + ++ /* Re-use destination buffer for aes-gcm tag output */ ++ crypto_engine->tag_addr = crypto_engine->dst_sg_addr + ++ ASPEED_CRYPTO_DST_DMA_BUF_LEN - AES_BLOCK_SIZE; ++ crypto_engine->tag_dma_addr = crypto_engine->dst_sg_dma_addr + ++ ASPEED_CRYPTO_DST_DMA_BUF_LEN - AES_BLOCK_SIZE; ++ + return 0; + +err_engine_crypto_start: @@ -33955,9 +33572,505 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-crypto.c b/drivers/crypto/aspeed/ + crypto_engine_exit(hace_dev->crypt_engine_crypto); + tasklet_kill(&crypto_engine->done_task); +} +diff --git a/drivers/crypto/aspeed/aspeed-hace-gcm.c b/drivers/crypto/aspeed/aspeed-hace-gcm.c +--- a/drivers/crypto/aspeed/aspeed-hace-gcm.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace-gcm.c 2026-07-17 08:44:55.308306273 +0000 +@@ -0,0 +1,492 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (c) 2026 Aspeed Technology Inc. ++ */ ++ ++#include "aspeed-hace.h" ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO_DEBUG ++#define CIPHER_DBG(h, fmt, ...) \ ++ dev_info((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__) ++#else ++#define CIPHER_DBG(h, fmt, ...) \ ++ dev_dbg((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__) ++#endif ++ ++static bool aspeed_crypto_aead_need_fallback(struct aead_request *areq) ++{ ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(areq); ++ ++ if (areq->assoclen != 0) ++ return true; ++ ++ if (rctx->enc_cmd & HACE_CMD_ENCRYPT && areq->cryptlen == 0) ++ return true; ++ ++ if (!(rctx->enc_cmd & HACE_CMD_ENCRYPT) && ++ areq->cryptlen == AES_GCM_TAG_SIZE) ++ return true; ++ ++ return false; ++} ++ ++static int aspeed_crypto_aead_do_fallback(struct aead_request *areq) ++{ ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(areq); ++ struct crypto_aead *tfm = crypto_aead_reqtfm(areq); ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(tfm); ++ int err; ++ ++ aead_request_set_tfm(&rctx->fallback_aead_req, ctx->fallback_aead_tfm); ++ ++ aead_request_set_callback(&rctx->fallback_aead_req, areq->base.flags, ++ NULL, NULL); ++ aead_request_set_crypt(&rctx->fallback_aead_req, areq->src, areq->dst, ++ areq->cryptlen, areq->iv); ++ aead_request_set_ad(&rctx->fallback_aead_req, areq->assoclen); ++ ++ if (rctx->enc_cmd & HACE_CMD_ENCRYPT) ++ err = crypto_aead_encrypt(&rctx->fallback_aead_req); ++ else ++ err = crypto_aead_decrypt(&rctx->fallback_aead_req); ++ ++ return err; ++} ++ ++static int ++aspeed_hace_crypto_aead_handle_queue(struct aspeed_hace_dev *hace_dev, ++ struct aead_request *req) ++{ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ if (aspeed_crypto_aead_need_fallback(req)) ++ return aspeed_crypto_aead_do_fallback(req); ++ ++ return crypto_transfer_aead_request_to_engine(hace_dev->crypt_engine_crypto, req); ++} ++ ++static void aspeed_hace_aead_sg_unmap(struct aspeed_hace_dev *hace_dev, ++ struct aead_request *req) ++{ ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ if (rctx->dst_sg_len) { ++ dma_unmap_sg(hace_dev->dev, req->dst, rctx->dst_nents, ++ DMA_FROM_DEVICE); ++ } ++ ++ if (rctx->src_sg_len) { ++ dma_unmap_sg(hace_dev->dev, req->src, rctx->src_nents, ++ DMA_TO_DEVICE); ++ } ++} ++ ++static int aspeed_hace_aead_sg_map(struct aspeed_hace_dev *hace_dev, ++ struct aead_request *req) ++{ ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ rctx->src_sg_len = dma_map_sg(hace_dev->dev, req->src, rctx->src_nents, ++ DMA_TO_DEVICE); ++ if (!rctx->src_sg_len) { ++ dev_warn(hace_dev->dev, "dma_map_sg() src error\n"); ++ return -EINVAL; ++ } ++ ++ rctx->dst_sg_len = dma_map_sg(hace_dev->dev, req->dst, rctx->dst_nents, ++ DMA_FROM_DEVICE); ++ if (!rctx->dst_sg_len) { ++ dev_warn(hace_dev->dev, "dma_map_sg() dst error\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aspeed_hace_aead_build_sg(struct aspeed_sg_list *sg_list, ++ struct scatterlist *src_sg, int src_sg_len, ++ int total) ++{ ++ struct scatterlist *s; ++ int i; ++ ++ for_each_sg(src_sg, s, src_sg_len, i) { ++ u32 phy_addr = sg_dma_address(s); ++ u32 len = sg_dma_len(s); ++ ++ len = total > len ? len : total | BIT(31); ++ total = total > len ? total - len : 0; ++ ++ sg_list[i].phy_addr = cpu_to_le32(phy_addr); ++ sg_list[i].len = cpu_to_le32(len); ++ } ++ ++ sg_list[src_sg_len].phy_addr = 0; ++ sg_list[src_sg_len].len = 0; ++ ++ if (total != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int aspeed_aead_complete(struct aspeed_hace_dev *hace_dev, int err) ++{ ++ struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine; ++ struct aead_request *req = crypto_engine->aead_req; ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ if (rctx->enc_cmd & HACE_CMD_ENCRYPT) ++ scatterwalk_map_and_copy(crypto_engine->tag_addr, req->dst, ++ req->cryptlen, AES_GCM_TAG_SIZE, 1); ++ ++ if (!(rctx->enc_cmd & HACE_CMD_ENCRYPT)) ++ req->cryptlen += AES_GCM_TAG_SIZE; ++ ++ crypto_engine->flags &= ~CRYPTO_FLAGS_BUSY; ++ ++ crypto_finalize_aead_request(hace_dev->crypt_engine_crypto, req, err); ++ ++ return err; ++} ++ ++static int aspeed_aead_transfer_sg(struct aspeed_hace_dev *hace_dev) ++{ ++ struct aead_request *req = hace_dev->crypto_engine.aead_req; ++ ++ aspeed_hace_aead_sg_unmap(hace_dev, req); ++ up(&hace_dev->lock); ++ ++ return aspeed_aead_complete(hace_dev, 0); ++} ++ ++static void aspeed_hace_trigger(struct aspeed_hace_dev *hace_dev) ++{ ++ struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine; ++ struct aead_request *req = crypto_engine->aead_req; ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(req); ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ down(&hace_dev->lock); ++ ast_hace_write(hace_dev, crypto_engine->cipher_ctx_dma, ++ ASPEED_HACE_CONTEXT); ++ ast_hace_write(hace_dev, crypto_engine->tag_dma_addr, ++ ASPEED_HACE_GCM_TAG_BASE_ADDR); ++ ast_hace_write(hace_dev, crypto_engine->cipher_dma_addr, ++ ASPEED_HACE_SRC); ++ ast_hace_write(hace_dev, crypto_engine->dst_sg_dma_addr, ++ ASPEED_HACE_DEST); ++ ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ ast_hace_write(hace_dev, crypto_engine->tag_dma_addr >> 32, ++ ASPEED_HACE_TAG_H); ++ ast_hace_write(hace_dev, crypto_engine->cipher_ctx_dma >> 32, ++ ASPEED_HACE_CONTEXT_H); ++ ast_hace_write(hace_dev, crypto_engine->cipher_dma_addr >> 32, ++ ASPEED_HACE_SRC_H); ++ ast_hace_write(hace_dev, crypto_engine->dst_sg_dma_addr >> 32, ++ ASPEED_HACE_DEST_H); ++#endif ++ ++ ast_hace_write(hace_dev, req->cryptlen, ASPEED_HACE_DATA_LEN); ++ ast_hace_write(hace_dev, req->assoclen, ASPEED_HACE_GCM_ADD_LEN); ++ ast_hace_write(hace_dev, rctx->enc_cmd, ASPEED_HACE_CMD); ++} ++ ++static int aspeed_aead_start_sg(struct aspeed_hace_dev *hace_dev) ++{ ++ struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine; ++ struct aead_request *req = crypto_engine->aead_req; ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(req); ++ struct aspeed_sg_list *src_list, *dst_list; ++ int ret = 0; ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ /* Scatter list mapping */ ++ ret = aspeed_hace_aead_sg_map(hace_dev, req); ++ if (ret) ++ goto fail; ++ ++ /* Build scatter-gather source list */ ++ src_list = (struct aspeed_sg_list *)crypto_engine->cipher_addr; ++ ret = aspeed_hace_aead_build_sg(src_list, req->src, rctx->src_sg_len, ++ req->cryptlen); ++ if (ret) ++ goto fail; ++ ++ /* Build scatter-gather destination list */ ++ dst_list = (struct aspeed_sg_list *)crypto_engine->dst_sg_addr; ++ ret = aspeed_hace_aead_build_sg(dst_list, req->dst, rctx->dst_sg_len, ++ req->cryptlen); ++ if (ret) ++ goto fail; ++ ++ crypto_engine->resume = aspeed_aead_transfer_sg; ++ ++ /* Memory barrier to ensure all data setup before engine starts */ ++ mb(); ++ aspeed_hace_trigger(hace_dev); ++ ++ return -EINPROGRESS; ++fail: ++ aspeed_hace_aead_sg_unmap(hace_dev, req); ++ return ret; ++} ++ ++static int aspeed_hace_aead_subkey(u8 *subkey, u8 *key, u32 keylen) ++{ ++ struct crypto_cipher *tfm = NULL; ++ int ret; ++ u8 src[AES_BLOCK_SIZE] = { 0 }; ++ ++ tfm = crypto_alloc_cipher("aes", 0, 0); ++ if (IS_ERR(tfm)) ++ return PTR_ERR(tfm); ++ ++ ret = crypto_cipher_setkey(tfm, key, keylen); ++ if (ret) ++ goto out; ++ ++ /* Encrypt one 16-byte block */ ++ crypto_cipher_encrypt_one(tfm, subkey, src); ++ ++out: ++ crypto_free_cipher(tfm); ++ return ret; ++} ++ ++static int aspeed_hace_aead_trigger(struct aspeed_hace_dev *hace_dev) ++{ ++ struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine; ++ struct aead_request *req = crypto_engine->aead_req; ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(req); ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(aead); ++ u8 iv_count[] = { 0x00, 0x00, 0x00, 0x01 }; ++ u8 subkey_offset = 0; ++ u8 subkey[AES_BLOCK_SIZE] = { 0 }; ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ if (!(rctx->enc_cmd & HACE_CMD_ENCRYPT)) ++ req->cryptlen = req->cryptlen - AES_GCM_TAG_SIZE; ++ ++ aspeed_hace_aead_subkey(subkey, ctx->key, ctx->key_len); ++ if (ctx->key_len == AES_KEYSIZE_128) ++ subkey_offset = ASPEED_HACE_CTX_128_SUBKEY_OFFSET; ++ else ++ subkey_offset = ASPEED_HACE_CTX_256_SUBKEY_OFFSET; ++ ++ rctx->dst_nents = sg_nents(req->dst); ++ rctx->src_nents = sg_nents(req->src); ++ ++ memcpy(crypto_engine->cipher_ctx, req->iv, GCM_AES_IV_SIZE); ++ memcpy(crypto_engine->cipher_ctx + GCM_AES_IV_SIZE, iv_count, ++ sizeof(iv_count)); ++ memcpy(crypto_engine->cipher_ctx + ASPEED_HACE_CTX_KEY_OFFSET, ctx->key, ++ ctx->key_len); ++ memcpy(crypto_engine->cipher_ctx + subkey_offset, subkey, ++ AES_BLOCK_SIZE); ++ ++ return aspeed_aead_start_sg(hace_dev); ++} ++ ++static int aspeed_aes_aead_setkey(struct crypto_aead *aead, const u8 *key, ++ unsigned int keylen) ++{ ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(aead); ++ struct aspeed_hace_dev *hace_dev = ctx->hace_dev; ++ ++ CIPHER_DBG(hace_dev, "keylen: %d bits\n", (keylen * 8)); ++ ++ ctx->key_idx = ASPEED_VAULT_KEY_NOT_FOUND; ++ ++ if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 && ++ keylen != AES_KEYSIZE_256) ++ return -EINVAL; ++ ++ memcpy(ctx->key, key, keylen); ++ ctx->key_len = keylen; ++ ++ crypto_aead_clear_flags(ctx->fallback_aead_tfm, CRYPTO_TFM_REQ_MASK); ++ crypto_aead_set_flags(ctx->fallback_aead_tfm, ++ aead->base.crt_flags & CRYPTO_TFM_REQ_MASK); ++ ++ return crypto_aead_setkey(ctx->fallback_aead_tfm, key, keylen); ++} ++ ++static int aspeed_aes_aead_setauthsize(struct crypto_aead *aead, ++ unsigned int authsize) ++{ ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(aead); ++ struct aspeed_hace_dev *hace_dev = ctx->hace_dev; ++ ++ CIPHER_DBG(hace_dev, "authsize: %d bits\n", (authsize * 8)); ++ ++ return crypto_aead_setauthsize(ctx->fallback_aead_tfm, authsize); ++} ++ ++static int aspeed_aes_gcm_crypt(struct aead_request *req, u32 cmd) ++{ ++ struct aspeed_cipher_reqctx *rctx = aead_request_ctx(req); ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(aead); ++ struct aspeed_hace_dev *hace_dev = ctx->hace_dev; ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ cmd = cmd | HACE_CMD_GCM | HACE_CMD_AES_SELECT | ++ HACE_CMD_RI_WO_DATA_ENABLE | HACE_CMD_CONTEXT_LOAD_ENABLE | ++ HACE_CMD_CONTEXT_SAVE_ENABLE | HACE_CMD_ISR_EN | ++ HACE_CMD_DES_SG_CTRL | HACE_CMD_SRC_SG_CTRL | ++ HACE_CMD_AES_KEY_HW_EXP | HACE_CMD_MBUS_REQ_SYNC_EN | ++ HACE_CMD_CTR_IV_AES_96 | HACE_CMD_GCM_TAG_ADDR_SEL; ++ ++ switch (ctx->key_len) { ++ case AES_KEYSIZE_128: ++ cmd |= HACE_CMD_AES128; ++ break; ++ case AES_KEYSIZE_192: ++ cmd |= HACE_CMD_AES192; ++ break; ++ case AES_KEYSIZE_256: ++ cmd |= HACE_CMD_AES256; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ rctx->enc_cmd = cmd; ++ ++ return aspeed_hace_crypto_aead_handle_queue(ctx->hace_dev, req); ++} ++ ++static int aspeed_aes_gcm_encrypt(struct aead_request *req) ++{ ++ return aspeed_aes_gcm_crypt(req, HACE_CMD_ENCRYPT); ++} ++ ++static int aspeed_aes_gcm_decrypt(struct aead_request *req) ++{ ++ return aspeed_aes_gcm_crypt(req, HACE_CMD_DECRYPT); ++} ++ ++static int aspeed_aead_do_request(struct crypto_engine *engine, void *areq) ++{ ++ struct aead_request *req = aead_request_cast(areq); ++ struct crypto_aead *aead = crypto_aead_reqtfm(req); ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(aead); ++ struct aspeed_hace_dev *hace_dev = ctx->hace_dev; ++ struct aspeed_engine_crypto *crypto_engine; ++ int rc; ++ ++ crypto_engine = &hace_dev->crypto_engine; ++ crypto_engine->aead_req = (struct aead_request *)req; ++ crypto_engine->flags |= CRYPTO_FLAGS_BUSY; ++ ++ rc = aspeed_hace_aead_trigger(hace_dev); ++ if (rc != -EINPROGRESS) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int aspeed_crypto_aead_init(struct crypto_aead *aead) ++{ ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(aead); ++ struct aead_alg *alg = crypto_aead_alg(aead); ++ struct aspeed_hace_alg *crypto_alg = NULL; ++ const char *name = crypto_tfm_alg_name(&aead->base); ++ ++ crypto_alg = container_of(alg, struct aspeed_hace_alg, alg.aead.base); ++ ctx->hace_dev = crypto_alg->hace_dev; ++ ++ CIPHER_DBG(ctx->hace_dev, "\n"); ++ ++ ctx->fallback_aead_tfm = ++ crypto_alloc_aead(name, 0, CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); ++ if (IS_ERR(ctx->fallback_aead_tfm)) { ++ dev_err(ctx->hace_dev->dev, "Failed to allocate %s %ld\n", name, ++ PTR_ERR(ctx->fallback_aead_tfm)); ++ return PTR_ERR(ctx->fallback_aead_tfm); ++ } ++ ++ crypto_aead_set_reqsize(aead, sizeof(struct aead_request) + ++ crypto_aead_reqsize(ctx->fallback_aead_tfm)); ++ ++ return 0; ++} ++ ++static void aspeed_crypto_aead_exit(struct crypto_aead *aead) ++{ ++ struct aspeed_cipher_ctx *ctx = crypto_aead_ctx(aead); ++ struct aspeed_hace_dev *hace_dev = ctx->hace_dev; ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ if (ctx->fallback_aead_tfm) { ++ crypto_free_aead(ctx->fallback_aead_tfm); ++ ctx->fallback_aead_tfm = NULL; ++ } ++ ++ memzero_explicit(ctx, sizeof(struct aspeed_cipher_ctx)); ++} ++ ++static struct aspeed_hace_alg aspeed_aead_algs[] = { ++ { ++ .alg.aead.base = { ++ .ivsize = GCM_AES_IV_SIZE, ++ .maxauthsize = AES_BLOCK_SIZE, ++ .setkey = aspeed_aes_aead_setkey, ++ .setauthsize = aspeed_aes_aead_setauthsize, ++ .encrypt = aspeed_aes_gcm_encrypt, ++ .decrypt = aspeed_aes_gcm_decrypt, ++ .init = aspeed_crypto_aead_init, ++ .exit = aspeed_crypto_aead_exit, ++ .base = { ++ .cra_name = "gcm(aes)", ++ .cra_driver_name = "aspeed-gcm-aes", ++ .cra_priority = 300, ++ .cra_flags = CRYPTO_ALG_TYPE_AEAD | ++ CRYPTO_ALG_ASYNC | ++ CRYPTO_ALG_NEED_FALLBACK, ++ .cra_blocksize = AES_BLOCK_SIZE, ++ .cra_ctxsize = sizeof(struct aspeed_cipher_ctx), ++ .cra_alignmask = 0x0f, ++ .cra_module = THIS_MODULE, ++ } ++ }, ++ .alg.aead.op = { ++ .do_one_request = aspeed_aead_do_request, ++ }, ++ }, ++}; ++ ++void aspeed_register_hace_aead_algs(struct aspeed_hace_dev *hace_dev) ++{ ++ int rc, i; ++ ++ CIPHER_DBG(hace_dev, "\n"); ++ ++ if (hace_dev->version == AST2500_VERSION || ++ hace_dev->version == AST2600_VERSION) ++ return; ++ ++ for (i = 0; i < ARRAY_SIZE(aspeed_aead_algs); i++) { ++ aspeed_aead_algs[i].hace_dev = hace_dev; ++ rc = crypto_engine_register_aead(&aspeed_aead_algs[i].alg.aead); ++ if (rc) { ++ CIPHER_DBG(hace_dev, "Failed to register %s\n", ++ aspeed_aead_algs[i].alg.aead.base.base.cra_name); ++ } ++ } ++} diff --git a/drivers/crypto/aspeed/aspeed-hace-hash.c b/drivers/crypto/aspeed/aspeed-hace-hash.c ---- a/drivers/crypto/aspeed/aspeed-hace-hash.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace-hash.c 2026-04-08 18:03:48.327705009 +0000 +--- a/drivers/crypto/aspeed/aspeed-hace-hash.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace-hash.c 2026-07-17 08:44:55.308306273 +0000 @@ -138,21 +138,13 @@ return -EINVAL; } @@ -34431,9 +34544,9 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace-hash.c b/drivers/crypto/aspeed/as + tasklet_kill(&hash_engine->done_task); +} diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed-hace.c ---- a/drivers/crypto/aspeed/aspeed-hace.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace.c 2026-04-08 18:03:48.327705009 +0000 -@@ -6,15 +6,18 @@ +--- a/drivers/crypto/aspeed/aspeed-hace.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace.c 2026-07-17 08:44:55.308306273 +0000 +@@ -6,15 +6,17 @@ #include "aspeed-hace.h" #include #include @@ -34444,7 +34557,6 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- #include #include #include -+#include +#include +#include #include @@ -34453,25 +34565,10 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- #ifdef CONFIG_CRYPTO_DEV_ASPEED_DEBUG #define HACE_DBG(d, fmt, ...) \ -@@ -24,6 +27,45 @@ +@@ -24,6 +26,30 @@ dev_dbg((d)->dev, "%s() " fmt, __func__, ##__VA_ARGS__) #endif -+static unsigned char *dummy_key1; -+static unsigned char *dummy_key2; -+ -+int find_dummy_key(const char *key, int keylen) -+{ -+ int ret = 0; -+ -+ if (dummy_key1 && memcmp(key, dummy_key1, keylen) == 0) -+ ret = 1; -+ else if (dummy_key2 && memcmp(key, dummy_key2, keylen) == 0) -+ ret = 2; -+ -+ return ret; -+} -+ +int aspeed_hace_reset(struct aspeed_hace_dev *hace_dev) +{ + int rc; @@ -34499,7 +34596,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- /* HACE interrupt service routine */ static irqreturn_t aspeed_hace_irq(int irq, void *dev) { -@@ -51,23 +93,9 @@ +@@ -51,23 +77,9 @@ dev_warn(hace_dev->dev, "CRYPTO no active requests.\n"); } @@ -34525,7 +34622,16 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- } static void aspeed_hace_register(struct aspeed_hace_dev *hace_dev) -@@ -93,30 +121,35 @@ +@@ -77,6 +89,8 @@ + #endif + #ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO + aspeed_register_hace_crypto_algs(hace_dev); ++ aspeed_register_hace_aead_algs(hace_dev); ++ aspeed_register_hace_vault_key(hace_dev); + #endif + } + +@@ -93,30 +107,31 @@ static const struct of_device_id aspeed_hace_of_matches[] = { { .compatible = "aspeed,ast2500-hace", .data = (void *)5, }, { .compatible = "aspeed,ast2600-hace", .data = (void *)6, }, @@ -34541,10 +34647,6 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- struct aspeed_hace_dev *hace_dev; + struct device *dev = &pdev->dev; int rc; -+#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO -+ struct device_node *sec_node; -+ int err; -+#endif + /* Allocate and register hace driver in linux kernel */ hace_dev = devm_kzalloc(&pdev->dev, sizeof(struct aspeed_hace_dev), @@ -34567,7 +34669,18 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- platform_set_drvdata(pdev, hace_dev); -@@ -149,115 +182,93 @@ +@@ -124,6 +139,10 @@ + if (IS_ERR(hace_dev->regs)) + return PTR_ERR(hace_dev->regs); + ++ hace_dev->sbc = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); ++ if (IS_ERR(hace_dev->sbc)) ++ return PTR_ERR(hace_dev->sbc); ++ + /* Get irq number and register it */ + hace_dev->irq = platform_get_irq(pdev, 0); + if (hace_dev->irq < 0) +@@ -149,115 +168,68 @@ return rc; } @@ -34648,19 +34761,26 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- - dev_err(&pdev->dev, "Failed to allocate dst_sg dma\n"); - rc = -ENOMEM; - goto err_engine_crypto_start; +- } + /* Get rst and de-assert reset */ + hace_dev->rst = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(hace_dev->rst)) { + dev_err(&pdev->dev, "Failed to get hace reset\n"); + return PTR_ERR(hace_dev->rst); -+ } -+ + } + + rc = reset_control_deassert(hace_dev->rst); + if (rc) { + dev_err(&pdev->dev, "Deassert hace reset failed\n"); + return rc; + } + ++ rc = aspeed_hace_reset(hace_dev); ++ if (rc) { ++ dev_err(&pdev->dev, "Hace reset failed\n"); ++ return rc; ++ } ++ + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (rc) { + dev_warn(&pdev->dev, "No suitable DMA available\n"); @@ -34683,37 +34803,6 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- + dev_err(&pdev->dev, "Crypto init failed\n"); + return rc; + } -+ -+ if (of_find_property(dev->of_node, "dummy-key1", NULL)) { -+ dummy_key1 = kzalloc(DUMMY_KEY_SIZE, GFP_KERNEL); -+ if (dummy_key1) { -+ err = of_property_read_u8_array(dev->of_node, "dummy-key1", dummy_key1, DUMMY_KEY_SIZE); -+ if (err) -+ dev_err(dev, "error of reading dummy_key 1\n"); -+ } else { -+ dev_err(dev, "error dummy_key1 allocation\n"); - } - } - -+ if (of_find_property(dev->of_node, "dummy-key2", NULL)) { -+ dummy_key2 = kzalloc(DUMMY_KEY_SIZE, GFP_KERNEL); -+ if (dummy_key2) { -+ err = of_property_read_u8_array(dev->of_node, "dummy-key2", dummy_key2, DUMMY_KEY_SIZE); -+ if (err) -+ dev_err(dev, "error of reading dummy_key 2\n"); -+ } else { -+ dev_err(dev, "error dummy_key2 allocation\n"); -+ } -+ } -+ -+ sec_node = of_find_compatible_node(NULL, NULL, "aspeed,ast2600-sbc"); -+ if (!sec_node) { -+ dev_err(dev, "cannot find sbc node\n"); -+ } else { -+ hace_dev->sec_regs = of_iomap(sec_node, 0); -+ if (!hace_dev->sec_regs) -+ dev_err(dev, "failed to map SBC registers\n"); -+ } +#endif aspeed_hace_register(hace_dev); @@ -34753,7 +34842,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- clk_disable_unprepare(hace_dev->clk); } -@@ -266,7 +277,7 @@ +@@ -266,7 +238,7 @@ static struct platform_driver aspeed_hace_driver = { .probe = aspeed_hace_probe, @@ -34763,8 +34852,8 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.c b/drivers/crypto/aspeed/aspeed- .name = KBUILD_MODNAME, .of_match_table = aspeed_hace_of_matches, diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed-hace.h ---- a/drivers/crypto/aspeed/aspeed-hace.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-hace.h 2026-04-08 18:03:48.327705009 +0000 +--- a/drivers/crypto/aspeed/aspeed-hace.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/crypto/aspeed/aspeed-hace.h 2026-07-17 08:44:55.308306273 +0000 @@ -10,6 +10,8 @@ #include #include @@ -34783,7 +34872,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- /* G6 */ #define ASPEED_HACE_GCM_ADD_LEN 0x14 /* Crypto AES-GCM Additional Data Length Register */ #define ASPEED_HACE_GCM_TAG_BASE_ADDR 0x18 /* Crypto AES-GCM Tag Write Buff Base Address Reg */ -@@ -36,6 +38,15 @@ +@@ -36,6 +38,22 @@ #define ASPEED_HACE_HASH_DATA_LEN 0x2C /* Hash Data Length Register */ #define ASPEED_HACE_HASH_CMD 0x30 /* Hash Engine Command Register */ @@ -34795,11 +34884,18 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- +#define ASPEED_HACE_HASH_SRC_H 0x90 /* Hash Data Source Base High Address Register */ +#define ASPEED_HACE_HASH_DIGEST_BUFF_H 0x94 /* Hash Digest Write Buffer Base High Address Register */ +#define ASPEED_HACE_HASH_KEY_BUFF_H 0x98 /* Hash HMAC Key Buffer Base High Address Register */ ++ ++/***************************** ++ * * ++ * SBC register definitions * ++ * * ++ * ***************************/ ++#define ASPEED_VAULT_KEY_CTRL 0x00C + /* crypto cmd */ #define HACE_CMD_SINGLE_DES 0 #define HACE_CMD_TRIPLE_DES BIT(17) -@@ -109,8 +120,9 @@ +@@ -109,8 +127,9 @@ #define CRYPTO_FLAGS_BUSY BIT(1) @@ -34811,16 +34907,29 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- #define SHA_FLAGS_SHA1 BIT(0) #define SHA_FLAGS_SHA224 BIT(1) -@@ -132,6 +144,8 @@ - #define HACE_CMD_IV_REQUIRE (HACE_CMD_CBC | HACE_CMD_CFB | \ - HACE_CMD_OFB | HACE_CMD_CTR) - +@@ -129,8 +148,19 @@ + #define ASPEED_HASH_SRC_DMA_BUF_LEN 0xa000 + #define ASPEED_HASH_QUEUE_LENGTH 50 + +-#define HACE_CMD_IV_REQUIRE (HACE_CMD_CBC | HACE_CMD_CFB | \ +- HACE_CMD_OFB | HACE_CMD_CTR) ++#define HACE_CMD_IV_REQUIRE \ ++ (HACE_CMD_CBC | HACE_CMD_CFB | HACE_CMD_OFB | HACE_CMD_CTR) ++#define ASPEED_HACE_CTX_KEY_OFFSET (0x10) ++#define ASPEED_HACE_CTX_128_SUBKEY_OFFSET (0x20) ++#define ASPEED_HACE_CTX_256_SUBKEY_OFFSET (0x30) ++#define AES_GCM_TAG_SIZE (16) ++ ++#define ASPEED_VAULT_KEY_NOT_FOUND (0xffffffff) ++#define ASPEED_G6_VAULT_KEY_MAX_NUM 2 ++#define ASPEED_G7_VAULT_KEY_MAX_NUM 4 +#define DUMMY_KEY_SIZE 32 -+ ++#define ASPEED_VAULT_KEY_DTS \ ++ "dummy-key0", "dummy-key1", "dummy-key2", "dummy-key3" + struct aspeed_hace_dev; struct scatterlist; - -@@ -147,10 +161,21 @@ +@@ -147,10 +177,21 @@ unsigned long flags; struct ahash_request *req; @@ -34842,7 +34951,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- dma_addr_t src_dma; dma_addr_t digest_dma; -@@ -192,12 +217,10 @@ +@@ -192,12 +233,10 @@ /* remain data buffer */ u8 buffer[SHA512_BLOCK_SIZE * 2]; @@ -34856,26 +34965,60 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- u64 digcnt[2]; }; -@@ -220,12 +243,14 @@ +@@ -205,6 +244,7 @@ + struct tasklet_struct done_task; + unsigned long flags; + struct skcipher_request *req; ++ struct aead_request *aead_req; + + /* context buffer */ + void *cipher_ctx; +@@ -218,36 +258,54 @@ + void *dst_sg_addr; + dma_addr_t dst_sg_dma_addr; ++ /* tag buffer, only used in scatter-gather lists */ ++ void *tag_addr; ++ dma_addr_t tag_dma_addr; ++ ++ /* Vault key */ ++ u32 *dummy_key[4]; ++ int vault_key_num; ++ /* callback func */ - aspeed_hace_fn_t resume; -+ int load_vault_key; +- aspeed_hace_fn_t resume; ++ aspeed_hace_fn_t resume; }; struct aspeed_cipher_ctx { struct aspeed_hace_dev *hace_dev; int key_len; u8 key[AES_MAX_KEYLENGTH]; -+ int dummy_key; ++ u32 key_idx; /* callback func */ aspeed_hace_fn_t start; -@@ -243,11 +268,16 @@ + +- struct crypto_skcipher *fallback_tfm; ++ struct crypto_aead *fallback_aead_tfm; ++ struct crypto_skcipher *fallback_tfm; + }; + + struct aspeed_cipher_reqctx { + int enc_cmd; + int src_nents; ++ int src_sg_len; + int dst_nents; ++ int dst_sg_len; + +- struct skcipher_request fallback_req; /* keep at the end */ ++ struct aead_request fallback_aead_req; ++ struct skcipher_request fallback_req; /* keep at the end */ + }; struct aspeed_hace_dev { void __iomem *regs; -+ void __iomem *sec_regs; ++ void __iomem *sbc; struct device *dev; int irq; struct clk *clk; @@ -34888,7 +35031,14 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- struct crypto_engine *crypt_engine_hash; struct crypto_engine *crypt_engine_crypto; -@@ -268,7 +298,8 @@ +@@ -262,13 +320,15 @@ + + union { + struct skcipher_engine_alg skcipher; ++ struct aead_engine_alg aead; + struct ahash_engine_alg ahash; + } alg; + }; enum aspeed_version { AST2500_VERSION = 5, @@ -34898,21 +35048,31 @@ diff --git a/drivers/crypto/aspeed/aspeed-hace.h b/drivers/crypto/aspeed/aspeed- }; #define ast_hace_write(hace, val, offset) \ -@@ -280,5 +311,11 @@ +@@ -276,9 +336,21 @@ + #define ast_hace_read(hace, offset) \ + readl((hace)->regs + (offset)) + ++#define ast_sbc_write(hace, val, offset) \ ++ writel((val), (hace)->sbc + (offset)) ++#define ast_sbc_read(hace, offset) \ ++ readl((hace)->sbc + (offset)) ++ + void aspeed_register_hace_hash_algs(struct aspeed_hace_dev *hace_dev); void aspeed_unregister_hace_hash_algs(struct aspeed_hace_dev *hace_dev); void aspeed_register_hace_crypto_algs(struct aspeed_hace_dev *hace_dev); ++void aspeed_register_hace_aead_algs(struct aspeed_hace_dev *hace_dev); ++void aspeed_register_hace_vault_key(struct aspeed_hace_dev *hace_dev); void aspeed_unregister_hace_crypto_algs(struct aspeed_hace_dev *hace_dev); +int aspeed_hace_hash_init(struct aspeed_hace_dev *hace_dev); +void aspeed_hace_hash_remove(struct aspeed_hace_dev *hace_dev); +int aspeed_hace_crypto_init(struct aspeed_hace_dev *hace_dev); +void aspeed_hace_crypto_remove(struct aspeed_hace_dev *hace_dev); -+int find_dummy_key(const char *key, int keylen); +int aspeed_hace_reset(struct aspeed_hace_dev *dev); #endif diff --git a/drivers/crypto/aspeed/aspeed-rsss-hash.c b/drivers/crypto/aspeed/aspeed-rsss-hash.c --- a/drivers/crypto/aspeed/aspeed-rsss-hash.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss-hash.c 2026-04-08 18:03:48.327705009 +0000 ++++ b/drivers/crypto/aspeed/aspeed-rsss-hash.c 2026-07-17 08:44:55.308306273 +0000 @@ -0,0 +1,901 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -35817,7 +35977,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-hash.c b/drivers/crypto/aspeed/as +} diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/aspeed-rsss-rsa.c --- a/drivers/crypto/aspeed/aspeed-rsss-rsa.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss-rsa.c 2026-04-08 18:03:48.328704991 +0000 ++++ b/drivers/crypto/aspeed/aspeed-rsss-rsa.c 2026-07-17 08:44:55.308306273 +0000 @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -36396,7 +36556,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss-rsa.c b/drivers/crypto/aspeed/asp +} diff --git a/drivers/crypto/aspeed/aspeed-rsss.c b/drivers/crypto/aspeed/aspeed-rsss.c --- a/drivers/crypto/aspeed/aspeed-rsss.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss.c 2026-04-08 18:03:48.328704991 +0000 ++++ b/drivers/crypto/aspeed/aspeed-rsss.c 2026-07-17 08:44:55.308306273 +0000 @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -36588,7 +36748,7 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss.c b/drivers/crypto/aspeed/aspeed- +MODULE_LICENSE("GPL"); diff --git a/drivers/crypto/aspeed/aspeed-rsss.h b/drivers/crypto/aspeed/aspeed-rsss.h --- a/drivers/crypto/aspeed/aspeed-rsss.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/crypto/aspeed/aspeed-rsss.h 2026-04-08 18:03:48.328704991 +0000 ++++ b/drivers/crypto/aspeed/aspeed-rsss.h 2026-07-17 08:44:55.309306258 +0000 @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + @@ -36866,8 +37026,8 @@ diff --git a/drivers/crypto/aspeed/aspeed-rsss.h b/drivers/crypto/aspeed/aspeed- + +#endif /* __ASPEED_RSSS_H__ */ diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig ---- a/drivers/edac/Kconfig 2026-04-08 18:03:23.257162538 +0000 -+++ b/drivers/edac/Kconfig 2026-04-08 18:03:33.347978488 +0000 +--- a/drivers/edac/Kconfig 2026-07-17 08:44:32.485658743 +0000 ++++ b/drivers/edac/Kconfig 2026-07-17 08:44:40.316537380 +0000 @@ -520,6 +520,15 @@ First, ECC must be configured in the bootloader. Then, this driver will expose error counters via the EDAC kernel framework. @@ -36884,9 +37044,378 @@ diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig config EDAC_BLUEFIELD tristate "Mellanox BlueField Memory ECC" depends on ARM64 && ((MELLANOX_PLATFORM && ACPI) || COMPILE_TEST) +diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile +--- a/drivers/edac/Makefile 2026-07-17 08:44:32.485658743 +0000 ++++ b/drivers/edac/Makefile 2026-07-17 08:44:44.918466416 +0000 +@@ -83,6 +83,7 @@ + obj-$(CONFIG_EDAC_TI) += ti_edac.o + obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o + obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o ++obj-$(CONFIG_EDAC_AST2700) += edac_ast2700.o + obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o + obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o + obj-$(CONFIG_EDAC_ELBA) += elba_edac.o +diff --git a/drivers/edac/edac_ast2700.c b/drivers/edac/edac_ast2700.c +--- a/drivers/edac/edac_ast2700.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/edac/edac_ast2700.c 2026-07-17 08:44:54.945311868 +0000 +@@ -0,0 +1,354 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2018, 2019 Cisco Systems ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "edac_module.h" ++ ++#define DRV_NAME "aspeed-edac" ++ ++#define DRAMC_PROT 0x00 /* protection key register */ ++#define DRAMC_INT_STS 0x04 /* interrupt status register */ ++#define INT_STS_ECC_REC BIT(5) /* ECC recoverable error interrupt */ ++#define INT_STS_ECC_ERR BIT(4) /* ECC error interrupt */ ++#define DRAMC_INT_CLR 0x08 /* interrupt clear register */ ++#define DRAMC_INT_MASK 0x0c /* interrupt mask register */ ++#define DRAMC_MCFG 0x10 /* main configuration register */ ++#define MCFG_ECC_ENABLE BIT(6) ++#define MCFG_DRAM_SIZE_MASK GENMASK(5, 4) /* 2=1GB, 3=2GB, 4=4GB, 5=8GB */ ++#define MCFG_DRAM_SIZE_SHIFT (2) ++#define MCFG_DRAM_TYPE BIT(0) /* 0=DDR4, 1=DDR5 */ ++ ++#define DRAMC_ECC_STS 0x78 /* ECC status register */ ++#define ECC_STS_REC_CNT_MASK GENMASK(15, 8) /* recoverable error count */ ++#define ECC_STS_REC_CNT_SHIFT (8) ++#define ECC_STS_UNREC_CNT_MASK GENMASK(7, 0) /* unrecoverable error count */ ++#define ECC_STS_UNREC_CNT_SHIFT (0) ++#define DRAMC_ECC_FAIL_ADDR 0x7c /* ECC fail address register */ ++ ++static struct regmap *aspeed_regmap; ++ ++static int regmap_reg_write(void *context, unsigned int reg, unsigned int val) ++{ ++ void __iomem *regs = (void __iomem *)context; ++ ++ writel(val, regs + reg); ++ ++ return 0; ++} ++ ++static int regmap_reg_read(void *context, unsigned int reg, unsigned int *val) ++{ ++ void __iomem *regs = (void __iomem *)context; ++ ++ *val = readl(regs + reg); ++ ++ return 0; ++} ++ ++static bool regmap_is_volatile(struct device *dev, unsigned int reg) ++{ ++ return 1; ++} ++ ++static const struct regmap_config aspeed_regmap_config = { ++ .reg_bits = 32, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = DRAMC_ECC_FAIL_ADDR, ++ .reg_write = regmap_reg_write, ++ .reg_read = regmap_reg_read, ++ .volatile_reg = regmap_is_volatile, ++ .fast_io = true, ++}; ++ ++static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) ++{ ++ struct csrow_info *csrow = mci->csrows[0]; ++ u32 page, offset, syndrome; ++ ++ if (!rec_cnt) ++ return; ++ ++ /* report first few errors (if there are) */ ++ /* note: no addresses are recorded */ ++ if (rec_cnt > 1) { ++ /* page, offset and syndrome are not available */ ++ page = 0; ++ offset = 0; ++ syndrome = 0; ++ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, rec_cnt - 1, ++ page, offset, syndrome, 0, 0, -1, ++ "address(es) not available", ""); ++ } ++ ++ /* report last error */ ++ /* note: rec_addr is the last recoverable error addr */ ++ page = rec_addr >> PAGE_SHIFT; ++ offset = rec_addr & ~PAGE_MASK; ++ /* syndrome is not available */ ++ syndrome = 0; ++ edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, ++ csrow->first_page + page, offset, syndrome, ++ 0, 0, -1, "", ""); ++} ++ ++static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt, ++ u32 un_rec_addr) ++{ ++ struct csrow_info *csrow = mci->csrows[0]; ++ u32 page, offset, syndrome; ++ ++ if (!un_rec_cnt) ++ return; ++ ++ /* report 1. error */ ++ /* note: un_rec_addr is the first unrecoverable error addr */ ++ page = un_rec_addr >> PAGE_SHIFT; ++ offset = un_rec_addr & ~PAGE_MASK; ++ /* syndrome is not available */ ++ syndrome = 0; ++ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, ++ csrow->first_page + page, offset, syndrome, ++ 0, 0, -1, "", ""); ++ ++ /* report further errors (if there are) */ ++ /* note: no addresses are recorded */ ++ if (un_rec_cnt > 1) { ++ /* page, offset and syndrome are not available */ ++ page = 0; ++ offset = 0; ++ syndrome = 0; ++ edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, un_rec_cnt - 1, ++ page, offset, syndrome, 0, 0, -1, ++ "address(es) not available", ""); ++ } ++} ++ ++static irqreturn_t mcr_isr(int irq, void *arg) ++{ ++ struct mem_ctl_info *mci = arg; ++ u32 reg, ecc_sts, ecc_fail_addr; ++ u16 rec_cnt, un_rec_cnt; ++ ++ regmap_read(aspeed_regmap, DRAMC_INT_STS, ®); ++ ++ regmap_read(aspeed_regmap, DRAMC_ECC_STS, &ecc_sts); ++ ++ /* collect data about recoverable and unrecoverable errors */ ++ rec_cnt = (ecc_sts & ECC_STS_REC_CNT_MASK) >> ECC_STS_REC_CNT_SHIFT; ++ un_rec_cnt = (ecc_sts & ECC_STS_UNREC_CNT_MASK) >> ECC_STS_UNREC_CNT_SHIFT; ++ ++ regmap_read(aspeed_regmap, DRAMC_ECC_FAIL_ADDR, &ecc_fail_addr); ++ ++ /* clear interrupt flags */ ++ regmap_update_bits(aspeed_regmap, DRAMC_INT_CLR, ++ reg, reg); ++ ++ /* process recoverable and unrecoverable errors */ ++ count_rec(mci, rec_cnt, ecc_fail_addr); ++ count_un_rec(mci, un_rec_cnt, ecc_fail_addr); ++ ++ return IRQ_HANDLED; ++} ++ ++static int config_irq(void *ctx, struct platform_device *pdev) ++{ ++ int irq; ++ int rc; ++ ++ /* register interrupt handler */ ++ irq = platform_get_irq(pdev, 0); ++ dev_dbg(&pdev->dev, "got irq %d\n", irq); ++ if (irq < 0) ++ return irq; ++ ++ rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH, ++ DRV_NAME, ctx); ++ if (rc) { ++ dev_err(&pdev->dev, "unable to request irq %d\n", irq); ++ return rc; ++ } ++ ++ /* enable interrupts */ ++ regmap_clear_bits(aspeed_regmap, DRAMC_INT_MASK, ++ INT_STS_ECC_REC | INT_STS_ECC_ERR); ++ ++ return 0; ++} ++ ++static int init_csrows(struct mem_ctl_info *mci) ++{ ++ struct csrow_info *csrow = mci->csrows[0]; ++ u32 nr_pages, dram_type; ++ struct dimm_info *dimm; ++ struct device_node *np; ++ struct resource r; ++ u32 reg04; ++ int rc; ++ ++ pr_info("%s: initializing csrows\n", DRV_NAME); ++ ++ /* retrieve info about physical memory from device tree */ ++ np = of_find_node_by_name(NULL, "memory"); ++ if (!np) { ++ dev_err(mci->pdev, "dt: missing /memory node\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_address_to_resource(np, 0, &r); ++ ++ of_node_put(np); ++ ++ if (rc) { ++ dev_err(mci->pdev, "dt: failed requesting resource for /memory node\n"); ++ return rc; ++ } ++ ++ dev_dbg(mci->pdev, "dt: /memory node resources: first page %pR, PAGE_SHIFT macro=0x%x\n", ++ &r, PAGE_SHIFT); ++ ++ csrow->first_page = r.start >> PAGE_SHIFT; ++ nr_pages = resource_size(&r) >> PAGE_SHIFT; ++ csrow->last_page = csrow->first_page + nr_pages - 1; ++ ++ regmap_read(aspeed_regmap, DRAMC_MCFG, ®04); ++ dram_type = (reg04 & MCFG_DRAM_TYPE) ? MEM_DDR5 : MEM_DDR4; ++ ++ dimm = csrow->channels[0]->dimm; ++ dimm->mtype = dram_type; ++ dimm->edac_mode = EDAC_SECDED; ++ dimm->nr_pages = nr_pages / csrow->nr_channels; ++ ++ dev_dbg(mci->pdev, "initialized dimm with first_page=0x%lx and nr_pages=0x%x\n", ++ csrow->first_page, nr_pages); ++ ++ return 0; ++} ++ ++static int aspeed_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct edac_mc_layer layers[2]; ++ struct mem_ctl_info *mci; ++ void __iomem *regs; ++ //u32 reg04; ++ u32 reg; ++ int rc; ++ ++ regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(regs)) ++ return PTR_ERR(regs); ++ ++ aspeed_regmap = devm_regmap_init(dev, NULL, (__force void *)regs, ++ &aspeed_regmap_config); ++ if (IS_ERR(aspeed_regmap)) ++ return PTR_ERR(aspeed_regmap); ++ ++ /* bail out if ECC mode is not configured */ ++ regmap_read(aspeed_regmap, DRAMC_MCFG, ®); ++ if (!(reg & MCFG_ECC_ENABLE)) { ++ dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n"); ++ return -EPERM; ++ } ++ ++ edac_op_state = EDAC_OPSTATE_INT; ++ ++ /* allocate & init EDAC MC data structure */ ++ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; ++ layers[0].size = 1; ++ layers[0].is_virt_csrow = true; ++ layers[1].type = EDAC_MC_LAYER_CHANNEL; ++ layers[1].size = 1; ++ layers[1].is_virt_csrow = false; ++ ++ mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0); ++ if (!mci) ++ return -ENOMEM; ++ ++ mci->pdev = &pdev->dev; ++ mci->mtype_cap = MEM_FLAG_DDR4 | MEM_FLAG_DDR5; ++ mci->edac_ctl_cap = EDAC_FLAG_SECDED; ++ mci->edac_cap = EDAC_FLAG_SECDED; ++ mci->scrub_cap = SCRUB_FLAG_HW_SRC; ++ mci->scrub_mode = SCRUB_HW_SRC; ++ mci->mod_name = DRV_NAME; ++ mci->ctl_name = "MIC"; ++ mci->dev_name = dev_name(&pdev->dev); ++ ++ rc = init_csrows(mci); ++ if (rc) { ++ dev_err(&pdev->dev, "failed to init csrows\n"); ++ goto probe_exit02; ++ } ++ ++ platform_set_drvdata(pdev, mci); ++ ++ /* register with edac core */ ++ rc = edac_mc_add_mc(mci); ++ if (rc) { ++ dev_err(&pdev->dev, "failed to register with EDAC core\n"); ++ goto probe_exit02; ++ } ++ ++ /* register interrupt handler and enable interrupts */ ++ rc = config_irq(mci, pdev); ++ if (rc) { ++ dev_err(&pdev->dev, "failed setting up irq\n"); ++ goto probe_exit01; ++ } ++ ++ return 0; ++ ++probe_exit01: ++ edac_mc_del_mc(&pdev->dev); ++probe_exit02: ++ edac_mc_free(mci); ++ return rc; ++} ++ ++static void aspeed_remove(struct platform_device *pdev) ++{ ++ struct mem_ctl_info *mci; ++ ++ /* disable interrupts */ ++ regmap_update_bits(aspeed_regmap, DRAMC_INT_MASK, ++ INT_STS_ECC_REC | INT_STS_ECC_ERR, ++ INT_STS_ECC_REC | INT_STS_ECC_ERR); ++ ++ /* free resources */ ++ mci = edac_mc_del_mc(&pdev->dev); ++ if (mci) ++ edac_mc_free(mci); ++} ++ ++static const struct of_device_id aspeed_of_match[] = { ++ { .compatible = "aspeed,ast2700-sdram-edac" }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, aspeed_of_match); ++ ++static struct platform_driver aspeed_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ .of_match_table = aspeed_of_match ++ }, ++ .probe = aspeed_probe, ++ .remove = aspeed_remove ++}; ++module_platform_driver(aspeed_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Stefan Schaeckeler "); ++MODULE_DESCRIPTION("Aspeed BMC SoC EDAC driver"); ++MODULE_VERSION("1.0"); diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c ---- a/drivers/fsi/fsi-master-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/fsi/fsi-master-aspeed.c 2026-04-08 18:03:48.122708755 +0000 +--- a/drivers/fsi/fsi-master-aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/fsi/fsi-master-aspeed.c 2026-07-17 08:44:55.098309510 +0000 @@ -3,6 +3,7 @@ // FSI master driver for AST2600 @@ -37000,8 +37529,8 @@ diff --git a/drivers/fsi/fsi-master-aspeed.c b/drivers/fsi/fsi-master-aspeed.c }; MODULE_DEVICE_TABLE(of, fsi_master_aspeed_match); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig ---- a/drivers/gpio/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/Kconfig 2026-04-08 18:03:35.024947902 +0000 +--- a/drivers/gpio/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpio/Kconfig 2026-07-17 08:44:41.948512194 +0000 @@ -180,6 +180,14 @@ help Say Y here to support Aspeed AST2500 SGPIO functionality. @@ -37018,9 +37547,9 @@ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig tristate "Atheros AR71XX/AR724X/AR913X GPIO support" default y if ATH79 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile ---- a/drivers/gpio/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/Makefile 2026-04-08 18:03:39.921858587 +0000 -@@ -36,6 +36,7 @@ +--- a/drivers/gpio/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpio/Makefile 2026-07-17 08:44:47.182431521 +0000 +@@ -37,6 +37,7 @@ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o @@ -37030,7 +37559,7 @@ diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o diff --git a/drivers/gpio/gpio-aspeed-ltpi.c b/drivers/gpio/gpio-aspeed-ltpi.c --- a/drivers/gpio/gpio-aspeed-ltpi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/gpio/gpio-aspeed-ltpi.c 2026-04-08 18:03:48.238706636 +0000 ++++ b/drivers/gpio/gpio-aspeed-ltpi.c 2026-07-17 08:44:55.218307660 +0000 @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -37502,8 +38031,8 @@ diff --git a/drivers/gpio/gpio-aspeed-ltpi.c b/drivers/gpio/gpio-aspeed-ltpi.c +module_platform_driver_probe(aspeed_ltpi_gpio_driver, aspeed_ltpi_gpio_probe); +MODULE_DESCRIPTION("Aspeed LTPI GPIO Driver"); diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c ---- a/drivers/gpio/gpio-aspeed-sgpio.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/gpio-aspeed-sgpio.c 2026-04-08 18:03:48.233706727 +0000 +--- a/drivers/gpio/gpio-aspeed-sgpio.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpio/gpio-aspeed-sgpio.c 2026-07-17 08:44:55.212307753 +0000 @@ -6,6 +6,7 @@ */ @@ -37512,7 +38041,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c #include #include #include -@@ -18,7 +19,35 @@ +@@ -18,14 +19,50 @@ #include #include @@ -37544,21 +38073,27 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c +#define SELECT_FROM_PARALLEL_IN 1 +#define SELECT_FROM_SERIAL_IN 2 + ++#define BMC_CONTROL_START_INDEX 128 ++#define BMC_CONTROL_END_INDEX 143 ++ +#define ASPEED_SGPIO_G4_CFG_OFFSET 0x54 +#define ASPEED_SGPIO_G7_CFG_OFFSET 0x0 #define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16) #define ASPEED_SGPIO_ENABLE BIT(0) -@@ -26,6 +55,8 @@ + #define ASPEED_SGPIO_PINS_SHIFT 6 ++#define ASPEED_SGPIO_G7_SERIAL_OUT_LOCK BIT(12) ++ struct aspeed_sgpio_pdata { const u32 pin_mask; + const struct aspeed_sgpio_llops *llops; + const u32 cfg_offset; ++ const bool slave; }; struct aspeed_sgpio { -@@ -35,6 +66,7 @@ +@@ -35,6 +72,7 @@ raw_spinlock_t lock; void __iomem *base; int irq; @@ -37566,7 +38101,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c }; struct aspeed_sgpio_bank { -@@ -42,7 +74,6 @@ +@@ -42,7 +80,6 @@ u16 rdata_reg; u16 irq_regs; u16 tolerance_regs; @@ -37574,7 +38109,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c }; /* -@@ -58,28 +89,24 @@ +@@ -58,28 +95,24 @@ .rdata_reg = 0x0070, .irq_regs = 0x0004, .tolerance_regs = 0x0018, @@ -37603,23 +38138,27 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c }, }; -@@ -94,6 +121,15 @@ +@@ -92,6 +125,19 @@ + reg_irq_type2, + reg_irq_status, reg_tolerance, - }; - ++ reg_serial_out_sel, ++ reg_parallel_out_sel, ++}; ++ +struct aspeed_sgpio_llops { + void (*reg_bit_set)(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg, bool val); + bool (*reg_bit_get)(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg); ++ void (*reg_bank_set)(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg, u32 val); + int (*reg_bank_get)(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg); -+}; -+ + }; + #define GPIO_VAL_VALUE 0x00 - #define GPIO_IRQ_ENABLE 0x00 - #define GPIO_IRQ_TYPE0 0x04 -@@ -101,9 +137,9 @@ +@@ -101,9 +147,9 @@ #define GPIO_IRQ_TYPE2 0x0C #define GPIO_IRQ_STATUS 0x10 @@ -37632,7 +38171,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c { switch (reg) { case reg_val: -@@ -128,6 +164,30 @@ +@@ -128,6 +174,34 @@ } } @@ -37654,6 +38193,10 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c + return SGPIO_G7_IRQ_STS; + case reg_tolerance: + return SGPIO_G7_RST_TOLERANCE; ++ case reg_serial_out_sel: ++ return SGPIO_G7_SERIAL_OUT_SEL; ++ case reg_parallel_out_sel: ++ return SGPIO_G7_PARALLEL_OUT_SEL; + default: + WARN_ON_ONCE(1); + return 0; @@ -37663,7 +38206,18 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c #define GPIO_BANK(x) ((x) >> 6) #define GPIO_OFFSET(x) ((x) & GENMASK(5, 0)) #define GPIO_BIT(x) BIT(GPIO_OFFSET(x) >> 1) -@@ -169,17 +229,13 @@ +@@ -166,20 +240,24 @@ + return !(offset % 2); + } + ++static bool aspeed_sgpios_ctrl_by_csr(unsigned int offset) ++{ ++ if (offset >= BMC_CONTROL_START_INDEX && ++ offset <= BMC_CONTROL_END_INDEX) ++ return true; ++ return false; ++} ++ static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); @@ -37683,7 +38237,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c return rc; } -@@ -187,26 +243,11 @@ +@@ -187,26 +265,11 @@ static int sgpio_set_value(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); @@ -37711,7 +38265,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c return 0; } -@@ -214,13 +255,11 @@ +@@ -214,13 +277,11 @@ static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); @@ -37727,7 +38281,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c } static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset) -@@ -231,15 +270,14 @@ +@@ -231,15 +292,14 @@ static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val) { struct aspeed_sgpio *gpio = gpiochip_get_data(gc); @@ -37745,7 +38299,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c return rc; } -@@ -249,75 +287,34 @@ +@@ -249,75 +309,33 @@ return !!aspeed_sgpio_is_input(offset); } @@ -37764,7 +38318,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c - *bank = to_bank(*offset); - *bit = GPIO_BIT(*offset); -} - +- static void aspeed_sgpio_irq_ack(struct irq_data *d) { - const struct aspeed_sgpio_bank *bank; @@ -37777,14 +38331,14 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c - irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset); - - status_addr = bank_reg(gpio, bank, reg_irq_status); -- -- raw_spin_lock_irqsave(&gpio->lock, flags); + struct aspeed_sgpio *gpio = irq_data_get_irq_chip_data(d); + int offset = irqd_to_hwirq(d); -- iowrite32(bit, status_addr); +- raw_spin_lock_irqsave(&gpio->lock, flags); + guard(raw_spinlock_irqsave)(&gpio->lock); +- iowrite32(bit, status_addr); +- - raw_spin_unlock_irqrestore(&gpio->lock, flags); + gpio->pdata->llops->reg_bit_set(gpio, offset, reg_irq_status, 1); } @@ -37834,7 +38388,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c } static void aspeed_sgpio_irq_mask(struct irq_data *d) -@@ -335,55 +332,36 @@ +@@ -335,55 +353,36 @@ u32 type0 = 0; u32 type1 = 0; u32 type2 = 0; @@ -37901,7 +38455,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c irq_set_handler_locked(d, handler); -@@ -395,15 +373,14 @@ +@@ -395,15 +394,14 @@ struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct irq_chip *ic = irq_desc_get_chip(desc); struct aspeed_sgpio *data = gpiochip_get_data(gc); @@ -37921,7 +38475,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c for_each_set_bit(p, ®, 32) generic_handle_domain_irq(gc->irq.domain, (i * 32 + p) * 2); -@@ -414,13 +391,9 @@ +@@ -414,13 +412,9 @@ static void aspeed_sgpio_irq_print_chip(struct irq_data *d, struct seq_file *p) { @@ -37937,7 +38491,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c } static const struct irq_chip aspeed_sgpio_irq_chip = { -@@ -437,7 +410,6 @@ +@@ -437,7 +431,6 @@ struct platform_device *pdev) { int rc, i; @@ -37945,7 +38499,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c struct gpio_irq_chip *irq; rc = platform_get_irq(pdev, 0); -@@ -447,12 +419,11 @@ +@@ -447,12 +440,11 @@ gpio->irq = rc; /* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */ @@ -37961,7 +38515,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c } irq = &gpio->chip.irq; -@@ -466,45 +437,91 @@ +@@ -466,45 +458,98 @@ irq->num_parents = 1; /* Apply default IRQ settings */ @@ -38035,9 +38589,16 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c + return -EOPNOTSUPP; +} + ++static void aspeed_sgpio_g4_reg_bank_set(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg, u32 val) ++{ ++ /* G4 doesn't support bank set for now */ ++} ++ +static const struct aspeed_sgpio_llops aspeed_sgpio_g4_llops = { + .reg_bit_set = aspeed_sgpio_g4_reg_bit_set, + .reg_bit_get = aspeed_sgpio_g4_reg_bit_get, ++ .reg_bank_set = aspeed_sgpio_g4_reg_bank_set, + .reg_bank_get = aspeed_sgpio_g4_reg_bank_get, +}; + @@ -38060,12 +38621,12 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c - raw_spin_lock_irqsave(&gpio->lock, flags); - - val = readl(reg); - +- - if (enable) - val |= GPIO_BIT(offset); - else - val &= ~GPIO_BIT(offset); -- + - writel(val, reg); + guard(raw_spinlock_irqsave)(&gpio->lock); @@ -38074,7 +38635,7 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c return 0; } -@@ -523,21 +540,77 @@ +@@ -523,12 +568,151 @@ static const struct aspeed_sgpio_pdata ast2600_sgpiom_pdata = { .pin_mask = GENMASK(10, 6), @@ -38082,16 +38643,53 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c + .cfg_offset = ASPEED_SGPIO_G4_CFG_OFFSET, +}; + ++static void aspeed_sgpio_g7_serial_out_lock(struct aspeed_sgpio *gpio, bool lock) ++{ ++ void __iomem *cfg = gpio->base + gpio->pdata->cfg_offset; ++ u32 val = ioread32(cfg); ++ ++ if (lock) ++ val |= ASPEED_SGPIO_G7_SERIAL_OUT_LOCK; ++ else ++ val &= ~ASPEED_SGPIO_G7_SERIAL_OUT_LOCK; ++ iowrite32(val, cfg); ++} ++ +static void aspeed_sgpio_g7_reg_bit_set(struct aspeed_sgpio *gpio, unsigned int offset, + const enum aspeed_sgpio_reg reg, bool val) +{ + u32 mask = aspeed_sgpio_g7_reg_mask(reg); + void __iomem *addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); ++ bool serial_out = false; + u32 write_val; + ++ if (reg == reg_val || reg == reg_rdata) { ++ if (gpio->pdata->slave && !aspeed_sgpios_ctrl_by_csr(offset)) { ++ // Ensure the parallel out value control by the software. ++ gpio->pdata->llops->reg_bank_set(gpio, offset, reg_parallel_out_sel, ++ SELECT_FROM_CSR); ++ mask = SGPIO_G7_PARALLEL_OUT_DATA; ++ } else { ++ // Ensure the serial out value control by the software. ++ gpio->pdata->llops->reg_bank_set(gpio, offset, reg_serial_out_sel, ++ SELECT_FROM_CSR); ++ mask = SGPIO_G7_OUT_DATA; ++ serial_out = (reg == reg_val); ++ } ++ } ++ + if (mask) { ++ /* ++ * Freeze the serial-out shifter across the value update so a ++ * half-written level can never be clocked out mid-waveform, ++ * then release it once the new value is in place. ++ */ ++ if (serial_out) ++ aspeed_sgpio_g7_serial_out_lock(gpio, true); + write_val = (ioread32(addr) & ~(mask)) | field_prep(mask, val); + iowrite32(write_val, addr); ++ if (serial_out) ++ aspeed_sgpio_g7_serial_out_lock(gpio, false); + } +} + @@ -38102,8 +38700,31 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c + void __iomem *addr; + + addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); -+ if (reg == reg_val) -+ mask = SGPIO_G7_IN_DATA; ++ if (reg == reg_val) { ++ if (gpio->pdata->slave && !aspeed_sgpios_ctrl_by_csr(offset)) { ++ unsigned int pin = offset >> 1; ++ ++ /* ++ * Slave pins 15:0 split their input source: data[3:0] ++ * (pins 0..3) come from the parallel-in pads, while ++ * data[15:4] (pins 4..15) are sampled from the serial ++ * input stream driven by the master. ++ */ ++ if (pin >= 4 && pin <= 15) ++ mask = SGPIO_G7_IN_DATA; ++ else ++ mask = SGPIO_G7_PARALLEL_IN_DATA; ++ } else { ++ mask = SGPIO_G7_IN_DATA; ++ } ++ } ++ ++ if (reg == reg_rdata) { ++ if (gpio->pdata->slave && !aspeed_sgpios_ctrl_by_csr(offset)) ++ mask = SGPIO_G7_PARALLEL_OUT_DATA; ++ else ++ mask = SGPIO_G7_OUT_DATA; ++ } + + if (mask) + return field_get(mask, ioread32(addr)); @@ -38124,9 +38745,23 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c + } +} + ++static void aspeed_sgpio_g7_reg_bank_set(struct aspeed_sgpio *gpio, unsigned int offset, ++ const enum aspeed_sgpio_reg reg, u32 val) ++{ ++ void __iomem *addr = gpio->base + SGPIO_G7_CTRL_REG_OFFSET(offset >> 1); ++ u32 mask, write_val; ++ ++ if (reg == reg_serial_out_sel || reg == reg_parallel_out_sel) { ++ mask = aspeed_sgpio_g7_reg_mask(reg); ++ write_val = (ioread32(addr) & ~(mask)) | field_prep(mask, val); ++ iowrite32(write_val, addr); ++ } ++} ++ +static const struct aspeed_sgpio_llops aspeed_sgpio_g7_llops = { + .reg_bit_set = aspeed_sgpio_g7_reg_bit_set, + .reg_bit_get = aspeed_sgpio_g7_reg_bit_get, ++ .reg_bank_set = aspeed_sgpio_g7_reg_bank_set, + .reg_bank_get = aspeed_sgpio_g7_reg_bank_get, +}; + @@ -38134,6 +38769,13 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c + .pin_mask = GENMASK(11, 6), + .llops = &aspeed_sgpio_g7_llops, + .cfg_offset = ASPEED_SGPIO_G7_CFG_OFFSET, ++}; ++ ++static const struct aspeed_sgpio_pdata ast2700_sgpios_pdata = { ++ .pin_mask = GENMASK(11, 6), ++ .llops = &aspeed_sgpio_g7_llops, ++ .cfg_offset = ASPEED_SGPIO_G7_CFG_OFFSET, ++ .slave = true, }; static const struct of_device_id aspeed_sgpio_of_table[] = { @@ -38141,20 +38783,19 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c { .compatible = "aspeed,ast2500-sgpio", .data = &ast2400_sgpio_pdata, }, { .compatible = "aspeed,ast2600-sgpiom", .data = &ast2600_sgpiom_pdata, }, + { .compatible = "aspeed,ast2700-sgpiom", .data = &ast2700_sgpiom_pdata, }, ++ { .compatible = "aspeed,ast2700-sgpios", .data = &ast2700_sgpios_pdata, }, {} }; - MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table); - --static int __init aspeed_sgpio_probe(struct platform_device *pdev) -+static int aspeed_sgpio_probe(struct platform_device *pdev) +@@ -537,7 +721,6 @@ + static int aspeed_sgpio_probe(struct platform_device *pdev) { u32 nr_gpios, sgpio_freq, sgpio_clk_div, gpio_cnt_regval, pin_mask; - const struct aspeed_sgpio_pdata *pdata; struct aspeed_sgpio *gpio; unsigned long apb_freq; int rc; -@@ -552,12 +625,11 @@ +@@ -552,12 +735,11 @@ gpio->dev = &pdev->dev; @@ -38170,32 +38811,93 @@ diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c rc = device_property_read_u32(&pdev->dev, "ngpios", &nr_gpios); if (rc < 0) { dev_err(&pdev->dev, "Could not read ngpios property\n"); -@@ -601,7 +673,7 @@ +@@ -568,40 +750,50 @@ + return -EINVAL; + } - gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask; - iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval | +- rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq); +- if (rc < 0) { +- dev_err(&pdev->dev, "Could not read bus-frequency property\n"); +- return -EINVAL; +- } +- +- gpio->pclk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(gpio->pclk)) { +- dev_err(&pdev->dev, "devm_clk_get failed\n"); +- return PTR_ERR(gpio->pclk); +- } +- +- apb_freq = clk_get_rate(gpio->pclk); +- +- /* +- * From the datasheet, +- * SGPIO period = 1/PCLK * 2 * (GPIO254[31:16] + 1) +- * period = 2 * (GPIO254[31:16] + 1) / PCLK +- * frequency = 1 / (2 * (GPIO254[31:16] + 1) / PCLK) +- * frequency = PCLK / (2 * (GPIO254[31:16] + 1)) +- * frequency * 2 * (GPIO254[31:16] + 1) = PCLK +- * GPIO254[31:16] = PCLK / (frequency * 2) - 1 +- */ +- if (sgpio_freq == 0) +- return -EINVAL; +- +- sgpio_clk_div = (apb_freq / (sgpio_freq * 2)) - 1; +- +- if (sgpio_clk_div > (1 << 16) - 1) +- return -EINVAL; ++ if (!gpio->pdata->slave) { ++ int i; + +- gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask; +- iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval | - ASPEED_SGPIO_ENABLE, gpio->base + ASPEED_SGPIO_CTRL); -+ ASPEED_SGPIO_ENABLE, gpio->base + gpio->pdata->cfg_offset); ++ for (i = 0; i < nr_gpios; i++) ++ gpio->pdata->llops->reg_bank_set(gpio, i << 1, reg_serial_out_sel, ++ SELECT_FROM_CSR); ++ ++ rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq); ++ if (rc < 0) { ++ dev_err(&pdev->dev, "Could not read bus-frequency property\n"); ++ return -EINVAL; ++ } ++ ++ gpio->pclk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(gpio->pclk)) { ++ dev_err(&pdev->dev, "devm_clk_get failed\n"); ++ return PTR_ERR(gpio->pclk); ++ } ++ ++ apb_freq = clk_get_rate(gpio->pclk); ++ ++ /* ++ * From the datasheet, ++ * SGPIO period = 1/PCLK * 2 * (GPIO254[31:16] + 1) ++ * period = 2 * (GPIO254[31:16] + 1) / PCLK ++ * frequency = 1 / (2 * (GPIO254[31:16] + 1) / PCLK) ++ * frequency = PCLK / (2 * (GPIO254[31:16] + 1)) ++ * frequency * 2 * (GPIO254[31:16] + 1) = PCLK ++ * GPIO254[31:16] = PCLK / (frequency * 2) - 1 ++ */ ++ if (sgpio_freq == 0) ++ return -EINVAL; ++ ++ sgpio_clk_div = (apb_freq / (sgpio_freq * 2)) - 1; ++ ++ if (sgpio_clk_div > (1 << 16) - 1) ++ return -EINVAL; ++ ++ gpio_cnt_regval = ((nr_gpios / 8) << ASPEED_SGPIO_PINS_SHIFT) & pin_mask; ++ iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, sgpio_clk_div) | gpio_cnt_regval | ++ ASPEED_SGPIO_ENABLE, gpio->base + gpio->pdata->cfg_offset); ++ } else { ++ iowrite32(ASPEED_SGPIO_ENABLE, gpio->base + gpio->pdata->cfg_offset); ++ } raw_spin_lock_init(&gpio->lock); -@@ -629,11 +701,12 @@ - } - - static struct platform_driver aspeed_sgpio_driver = { -+ .probe = aspeed_sgpio_probe, - .driver = { - .name = KBUILD_MODNAME, - .of_match_table = aspeed_sgpio_of_table, - }, - }; - --module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe); -+module_platform_driver(aspeed_sgpio_driver); - MODULE_DESCRIPTION("Aspeed Serial GPIO Driver"); diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c ---- a/drivers/gpio/gpio-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpio/gpio-aspeed.c 2026-04-08 18:03:48.226706855 +0000 +--- a/drivers/gpio/gpio-aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpio/gpio-aspeed.c 2026-07-17 08:44:55.206307845 +0000 @@ -30,6 +30,27 @@ #include #include "gpiolib.h" @@ -38215,8 +38917,8 @@ diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c +#define GPIO_G7_CTRL_IRQ_TYPE1 BIT(4) +#define GPIO_G7_CTRL_IRQ_TYPE2 BIT(5) +#define GPIO_G7_CTRL_RST_TOLERANCE BIT(6) -+#define GPIO_G7_CTRL_DEBOUNCE_SEL1 BIT(7) -+#define GPIO_G7_CTRL_DEBOUNCE_SEL2 BIT(8) ++#define GPIO_G7_CTRL_DEBOUNCE_SEL2 BIT(7) ++#define GPIO_G7_CTRL_DEBOUNCE_SEL1 BIT(8) +#define GPIO_G7_CTRL_INPUT_MASK BIT(9) +#define GPIO_G7_CTRL_IRQ_STS BIT(12) +#define GPIO_G7_CTRL_IN_DATA BIT(13) @@ -39271,8 +39973,8 @@ diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c MODULE_DESCRIPTION("Aspeed GPIO Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig ---- a/drivers/gpu/drm/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/Kconfig 2026-04-08 18:03:34.489957659 +0000 +--- a/drivers/gpu/drm/aspeed/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpu/drm/aspeed/Kconfig 2026-07-17 08:44:41.440520023 +0000 @@ -4,6 +4,7 @@ depends on DRM && OF depends on (COMPILE_TEST || ARCH_ASPEED) @@ -39282,8 +39984,8 @@ diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig select DRM_GEM_DMA_HELPER select DMA_CMA if HAVE_DMA_CONTIGUOUS diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed_gfx.h ---- a/drivers/gpu/drm/aspeed/aspeed_gfx.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h 2026-04-08 18:03:48.158708097 +0000 +--- a/drivers/gpu/drm/aspeed/aspeed_gfx.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx.h 2026-07-17 08:44:55.135308940 +0000 @@ -8,14 +8,32 @@ struct drm_device drm; void __iomem *base; @@ -39358,8 +40060,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed +/* PCIE interrupt */ +#define PCIE_PERST_L_T_H_G5 BIT(18) +#define PCIE_PERST_H_T_L_G5 BIT(19) -+#define PCIE_PERST_L_T_H_G7 BIT(2) -+#define PCIE_PERST_H_T_L_G7 BIT(3) ++#define PCIE_PERST_L_T_H_G7 BIT(3) ++#define PCIE_PERST_H_T_L_G7 BIT(2) + +/* PCIE end pointer define */ +#define PCIE_LINK_REG_G5 0xC0 @@ -39389,9 +40091,9 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx.h b/drivers/gpu/drm/aspeed/aspeed +#define DP_800 0x01050020 /* 800 x 600 60Hz */ +#define DP_1024 0x010a0020 /* 1024 x 768 70Hz */ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c ---- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c 2026-04-08 18:03:48.152708207 +0000 -@@ -23,6 +23,44 @@ +--- a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c 2026-07-17 08:44:55.128309048 +0000 +@@ -23,6 +23,45 @@ return container_of(pipe, struct aspeed_gfx, pipe); } @@ -39429,14 +40131,15 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a + regmap_write(priv->scu, 0x340, 0x00190002); + + /* apply 800 x 600 @ 60 on ast2700 DAC */ -+ regmap_update_bits(priv->scu1, 0xd0, BIT(10), BIT(10)); ++ /* Move the scu1.d0 to the aspeed_gfx_enable_controller first check */ ++ regmap_write(priv->scu1, 0x324, 0x00000007); + regmap_write(priv->scu1, 0x320, 0x1048000F); +} + static int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp) { struct drm_crtc *crtc = &priv->pipe.crtc; -@@ -59,11 +97,22 @@ +@@ -59,11 +98,24 @@ u32 ctrl1 = readl(priv->base + CRT_CTRL1); u32 ctrl2 = readl(priv->base + CRT_CTRL2); @@ -39447,6 +40150,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a + regmap_update_bits(priv->scu, priv->dac_reg, priv->soc_crt_bit, priv->soc_crt_bit); + if (priv->dp_support) + regmap_update_bits(priv->scu, priv->dac_reg, priv->soc_dp_bit, priv->soc_dp_bit); ++ if ((priv->flags & CLK_MASK) == CLK_G7) ++ regmap_update_bits(priv->scu1, 0xd0, BIT(10), BIT(10)); + } + + /* remove the cursor and osd usage */ @@ -39461,7 +40166,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a } static void aspeed_gfx_disable_controller(struct aspeed_gfx *priv) -@@ -74,7 +123,42 @@ +@@ -74,7 +126,42 @@ writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1); writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2); @@ -39505,7 +40210,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a } static void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv) -@@ -87,6 +171,8 @@ +@@ -87,6 +174,8 @@ if (err) return; @@ -39514,7 +40219,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a #if 0 /* TODO: we have only been able to test with the 40MHz USB clock. The * clock is fixed, so we cannot adjust it here. */ -@@ -137,6 +223,10 @@ +@@ -137,6 +226,10 @@ * per line, rounded up) */ writel(priv->throd_val, priv->base + CRT_THROD); @@ -39525,7 +40230,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a } static void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe, -@@ -187,12 +277,17 @@ +@@ -187,12 +280,17 @@ gem = drm_fb_dma_get_gem_obj(fb, 0); if (!gem) return; @@ -39544,7 +40249,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a u32 reg = readl(priv->base + CRT_CTRL1); /* Clear pending VBLANK IRQ */ -@@ -207,6 +302,7 @@ +@@ -207,6 +305,7 @@ static void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe) { struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe); @@ -39553,8 +40258,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c b/drivers/gpu/drm/aspeed/a reg &= ~CRT_CTRL_VERTICAL_INTR_EN; diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c ---- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c 2026-04-08 18:03:48.165707969 +0000 +--- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c 2026-07-17 08:44:55.142308832 +0000 @@ -63,6 +63,15 @@ u32 vga_scratch_reg; /* VGA scratch register in SCU */ u32 throd_val; /* Default Threshold Seting */ @@ -39625,7 +40330,7 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as + .throd_val = CRT_THROD_LOW(0x50) | CRT_THROD_HIGH(0x70), + .scan_line_max = 128, + .gfx_flags = CLK_G7 | ADDR_64, -+ .pcie_int_reg = 0x1D0, ++ .pcie_int_reg = 0x1D4, + .pcie_int_mask = (PCIE_PERST_L_T_H_G7 | PCIE_PERST_H_T_L_G7), + .pcie_int_l_to_h = PCIE_PERST_L_T_H_G7, + .pcie_int_h_to_l = PCIE_PERST_H_T_L_G7, @@ -40017,8 +40722,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/as } diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c ---- a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c 2026-04-08 18:03:48.145708335 +0000 +--- a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/gpu/drm/aspeed/aspeed_gfx_out.c 2026-07-17 08:44:55.122309140 +0000 @@ -10,7 +10,19 @@ static int aspeed_gfx_get_modes(struct drm_connector *connector) @@ -40041,8 +40746,8 @@ diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_out.c b/drivers/gpu/drm/aspeed/as static const struct diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig ---- a/drivers/hwmon/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/hwmon/Kconfig 2026-04-08 18:03:33.210980986 +0000 +--- a/drivers/hwmon/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/hwmon/Kconfig 2026-07-17 08:44:40.182539457 +0000 @@ -423,6 +423,16 @@ This driver can also be built as a module. If so, the module will be called aspeed_g6_pwm_tach. @@ -40061,8 +40766,8 @@ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig tristate "Attansic ATXP1 VID controller" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile ---- a/drivers/hwmon/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/hwmon/Makefile 2026-04-08 18:03:37.718898767 +0000 +--- a/drivers/hwmon/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/hwmon/Makefile 2026-07-17 08:44:44.768468728 +0000 @@ -55,6 +55,7 @@ obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o @@ -40073,7 +40778,7 @@ diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o diff --git a/drivers/hwmon/aspeed-chassis.c b/drivers/hwmon/aspeed-chassis.c --- a/drivers/hwmon/aspeed-chassis.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/hwmon/aspeed-chassis.c 2026-04-08 18:03:47.815714364 +0000 ++++ b/drivers/hwmon/aspeed-chassis.c 2026-07-17 08:44:54.721315321 +0000 @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -40297,8 +41002,8 @@ diff --git a/drivers/hwmon/aspeed-chassis.c b/drivers/hwmon/aspeed-chassis.c +MODULE_DESCRIPTION("ASPEED CHASSIS Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c ---- a/drivers/hwmon/aspeed-g6-pwm-tach.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/hwmon/aspeed-g6-pwm-tach.c 2026-04-08 18:03:47.821714254 +0000 +--- a/drivers/hwmon/aspeed-g6-pwm-tach.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/hwmon/aspeed-g6-pwm-tach.c 2026-07-17 08:44:54.728315213 +0000 @@ -56,6 +56,7 @@ #include #include @@ -40434,7 +41139,7 @@ diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-ta ret = devm_pwmchip_add(dev, chip); if (ret) return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); -@@ -528,6 +577,9 @@ +@@ -521,6 +570,9 @@ { .compatible = "aspeed,ast2600-pwm-tach", }, @@ -40445,8 +41150,8 @@ diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-ta }; MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig ---- a/drivers/i2c/busses/Kconfig 2026-04-08 18:03:23.246162739 +0000 -+++ b/drivers/i2c/busses/Kconfig 2026-04-08 18:03:32.670990835 +0000 +--- a/drivers/i2c/busses/Kconfig 2026-07-17 08:44:32.473658929 +0000 ++++ b/drivers/i2c/busses/Kconfig 2026-07-17 08:44:39.604548414 +0000 @@ -421,6 +421,17 @@ This driver can also be built as a module. If so, the module will be called i2c-altera. @@ -40466,8 +41171,8 @@ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig tristate "Aspeed I2C Controller" depends on ARCH_ASPEED || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile ---- a/drivers/i2c/busses/Makefile 2026-04-08 18:03:23.246162739 +0000 -+++ b/drivers/i2c/busses/Makefile 2026-04-08 18:03:37.112909819 +0000 +--- a/drivers/i2c/busses/Makefile 2026-07-17 08:44:32.473658929 +0000 ++++ b/drivers/i2c/busses/Makefile 2026-07-17 08:44:44.149478269 +0000 @@ -39,6 +39,7 @@ obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o @@ -40478,13 +41183,13 @@ diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y) diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c --- a/drivers/i2c/busses/i2c-ast2600.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i2c/busses/i2c-ast2600.c 2026-04-08 18:03:47.772715150 +0000 -@@ -0,0 +1,2449 @@ ++++ b/drivers/i2c/busses/i2c-ast2600.c 2026-07-17 08:44:54.678315983 +0000 +@@ -0,0 +1,3134 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ASPEED AST2600 new register set I2C controller driver + * -+ * Copyright (C) ASPEED Technology Inc. ++ * Copyright (C) 2023 ASPEED Technology Inc. + */ +#include +#include @@ -40605,7 +41310,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +#define AST2600_I2CM_BUS_RECOVER_FAIL BIT(15) +#define AST2600_I2CM_SDA_DL_TO BIT(14) +#define AST2600_I2CM_BUS_RECOVER BIT(13) -+#define AST2600_I2CM_SMBUS_ALT BIT(12) ++#define AST2600_I2CM_SMBUS_ALERT BIT(12) +#define AST2700_I2CM_ABNORMAL_ACTION BIT(8) + +#define AST2600_I2CM_SCL_LOW_TO BIT(6) @@ -40758,10 +41463,13 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +#define STX_DONE BIT(1) +#define SLAVE_STOP BIT(0) + ++/* 0x94 : I2CC_VERSION_CTRL */ ++#define I2CC_VERSION_CTRL 0x94 ++#define FUNC_CFG_DMA_EN BIT(2) +/* 0x9c : Misc_2 Debounce Setting */ +#define MSIC2_CONFIG 0x9C +#define AST2700_DEBOUNCE_MASK GENMASK(7, 0) -+#define AST2700_DEBOUNCE_LEVEL_MAX 0x20 ++#define AST2700_DEBOUNCE_LEVEL_MAX 0xFF +#define AST2700_DEBOUNCE_LEVEL_MIN 0x2 + +#define I2C_TARGET_MSG_BUF_SIZE 4096 @@ -40805,7 +41513,6 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + struct device *dev; + void __iomem *reg_base; + struct regmap *global_regs; -+ struct reset_control *rst; + struct clk *clk; + struct i2c_timings timing_info; + struct completion cmd_complete; @@ -40830,6 +41537,11 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + u32 manual_data_hold; + /* Buffer mode */ + void __iomem *buf_base; ++ int (*setup_tx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus); ++ int (*setup_rx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus); ++ void (*ac_timing_config)(struct ast2600_i2c_bus *i2c_bus); ++ int (*irq_err_to_errno)(u32 irq_status); ++ void (*irq_clear)(struct ast2600_i2c_bus *i2c_bus, u32 sts); + /* smbus alert */ + bool alert_enable; + struct i2c_smbus_alert_setup alert_data; @@ -40842,6 +41554,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + u8 target_attached; + struct i2c_client *multi_target[AST2600_I2C_TARGET_COUNT]; + struct i2c_client *target; ++ void (*target_packet_irq)(struct ast2600_i2c_bus *i2c_bus, u32 isr); ++ void (*target_byte_irq)(struct ast2600_i2c_bus *i2c_bus, u32 isr); +#endif +}; + @@ -41041,7 +41755,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + reinit_completion(&i2c_bus->cmd_complete); + i2c_bus->cmd_err = 0; + -+ /* Check 0x14's SDA and SCL status */ ++ /* Check SDA/SCL status in the status register. */ + state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF); + if (!(state & AST2600_I2CC_SDA_LINE_STS) && (state & AST2600_I2CC_SCL_LINE_STS)) { + writel(AST2600_I2CM_RECOVER_CMD_EN, i2c_bus->reg_base + AST2600_I2CM_CMD_STS); @@ -41101,7 +41815,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + + sts = isr & ~(AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_ADDR_NAK_MASK); + /* Handle i2c target timeout condition */ -+ if (AST2600_I2CS_INACTIVE_TO & sts) { ++ if (sts & AST2600_I2CS_INACTIVE_TO) { + dev_dbg(i2c_bus->dev, "The target timeout occurs isr: 0x%08x.\n", isr); + /* Reset timeout counter */ + u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) & @@ -41131,7 +41845,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + return; + } + -+ if (AST2600_I2CS_ABNOR_STOP & sts) { ++ if (sts & AST2600_I2CS_ABNOR_STOP) { + dev_err(i2c_bus->dev, "The target abnomal protocol occurs isr: 0x%08x.\n", isr); + cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; + writel(AST2600_I2CS_SET_RX_DMA_LEN(I2C_TARGET_MSG_BUF_SIZE), @@ -41526,6 +42240,512 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + readl(i2c_bus->reg_base + AST2600_I2CS_ISR); +} + ++static void ast2700_i2c_target_packet_buff_irq(struct ast2600_i2c_bus *i2c_bus, u32 isr) ++{ ++ int target_rx_len = 0; ++ u32 cmd = 0; ++ u8 value; ++ int i; ++ u32 sirq_log; ++ u32 sts; ++ ++ writel(AST2600_I2CS_SADDR_PENDING | AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_WAIT_RX_DMA, ++ i2c_bus->reg_base + AST2600_I2CS_ISR); ++ isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR); ++ ++ sts = isr & ~(AST2600_I2CS_SLAVE_PENDING | AST2600_I2CS_ADDR_NAK_MASK); ++ /* Handle i2c target timeout condition */ ++ if (sts & AST2600_I2CS_INACTIVE_TO) { ++ dev_dbg(i2c_bus->dev, "The target timeout occurs isr: 0x%08x.\n", isr); ++ /* Reset timeout counter */ ++ u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) & ++ AST2600_I2CC_AC_TIMING_MASK; ++ ++ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING); ++ ac_timing |= AST2700_I2CC_TTIMEOUT(i2c_bus->timeout); ++ writel(ac_timing, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING); ++ writel(TARGET_TRIGGER_CMD, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); ++ /* clear sirq log */ ++ while ((sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG))) { ++ /* assign the target client*/ ++ if (sirq_log & SADDR_HIT) { ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, ++ sirq_log >> SLAVE_ADDR_SHIFT); ++ } ++ }; ++ writel(isr, i2c_bus->reg_base + AST2600_I2CS_ISR); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ } ++ i2c_bus->target_operate = 0; ++ return; ++ } ++ ++ if (sts & AST2600_I2CS_ABNOR_STOP) { ++ dev_err(i2c_bus->dev, "The target abnomal protocol occurs isr: 0x%08x.\n", isr); ++ writel(TARGET_TRIGGER_CMD, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); ++ /* clear sirq log */ ++ while ((sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG))) { ++ /* assign the target client*/ ++ if (sirq_log & SADDR_HIT) { ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, ++ sirq_log >> SLAVE_ADDR_SHIFT); ++ } ++ }; ++ writel(isr, i2c_bus->reg_base + AST2600_I2CS_ISR); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ } ++ i2c_bus->target_operate = 0; ++ return; ++ } ++ ++ if (sts & AST2600_I2CS_SLAVE_MATCH) ++ i2c_bus->target_operate = 1; ++ ++ sts &= ~(AST2600_I2CS_PKT_DONE | AST2600_I2CS_PKT_ERROR); ++ dev_dbg(i2c_bus->dev, "sts %x\n", sts); ++ switch (sts) { ++ case AST2600_I2CS_SADDR_PENDING | AST2600_I2CS_WAIT_RX_DMA | ++ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP: ++ writel(AST2600_I2CS_SLAVE_MATCH, i2c_bus->reg_base + AST2600_I2CS_ISR); ++ isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR); ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ /* bug workaround */ ++ if (sirq_log & SADDR_HIT) { ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ } ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_SLAVE_MATCH: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_SADDR_PENDING | AST2600_I2CS_SLAVE_MATCH | ++ AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP: ++ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_STOP: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | ++ AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ /* workaround: false alarm target match check */ ++ if (sirq_log & SADDR_HIT) { ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ } ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ /* workaround new target match */ ++ if (sirq_log & SADDR_HIT) { ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, ++ &i2c_bus->target_dma_buf[0]); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ } ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP | AST2600_I2CS_SLAVE_MATCH: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (sirq_log & SADDR_HIT) { ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ /* workaround: not clear target match due to wait next isr check tx or rx */ ++ isr &= ~AST2600_I2CS_SLAVE_MATCH; ++ break; ++ case AST2600_I2CS_TX_NAK | AST2600_I2CS_STOP: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_TX_NAK | ++ AST2600_I2CS_STOP | AST2600_I2CS_SLAVE_MATCH: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_TX_DMA: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value); ++ writeb(value, i2c_bus->buf_base + 0x20); ++ } ++ writel(AST2600_I2CS_SET_TX_DMA_LEN(1), i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN; ++ break; ++ case AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_WAIT_RX_DMA: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_TX_ACK | AST2600_I2CS_WAIT_TX_DMA: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_PROCESSED, &value); ++ writeb(value, i2c_bus->buf_base + 0x20); ++ } ++ writel(AST2600_I2CS_SET_TX_DMA_LEN(1), i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN; ++ break; ++ case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_SLAVE_MATCH | ++ AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value); ++ writeb(value, i2c_bus->buf_base + 0x20); ++ } ++ writel(AST2600_I2CS_SET_TX_DMA_LEN(1), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN; ++ break; ++ case AST2600_I2CS_WAIT_TX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, ++ &i2c_bus->target_dma_buf[i]); ++ } ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value); ++ writeb(value, i2c_bus->buf_base + 0x20); ++ } ++ writel(AST2600_I2CS_SET_TX_DMA_LEN(1), i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN; ++ break; ++ case AST2600_I2CS_SADDR_PENDING | AST2600_I2CS_WAIT_TX_DMA | ++ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE: ++ writel(AST2600_I2CS_SLAVE_MATCH, i2c_bus->reg_base + AST2600_I2CS_ISR); ++ isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR); ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value); ++ writeb(value, i2c_bus->buf_base + 0x20); ++ } ++ writel(AST2600_I2CS_SET_TX_DMA_LEN(1), i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN; ++ break; ++ case AST2600_I2CS_SADDR_PENDING | AST2600_I2CS_WAIT_TX_DMA | ++ AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE | AST2600_I2CS_STOP: ++ writel(AST2600_I2CS_SLAVE_MATCH, i2c_bus->reg_base + AST2600_I2CS_ISR); ++ isr = readl(i2c_bus->reg_base + AST2600_I2CS_ISR); ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, ++ &i2c_bus->target_dma_buf[0]); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value); ++ writeb(value, i2c_bus->buf_base + 0x20); ++ } ++ writel(AST2600_I2CS_SET_TX_DMA_LEN(1), i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN; ++ break; ++ case AST2600_I2CS_WAIT_TX_DMA: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_READ_REQUESTED, &value); ++ writeb(value, i2c_bus->buf_base + 0x20); ++ } ++ writel(AST2600_I2CS_SET_TX_DMA_LEN(1), i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_TX_DMA_EN; ++ break; ++ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_SLAVE_MATCH | AST2600_I2CS_RX_DONE: ++ case AST2600_I2CS_WAIT_RX_DMA | AST2600_I2CS_RX_DONE: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ /* workaround: false alarm target match check */ ++ if (sirq_log & SADDR_HIT) { ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_REQUESTED, &value); ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ } ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ case AST2600_I2CS_STOP: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (i2c_bus->target) ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target = NULL; ++ break; ++ case AST2600_I2CS_WAIT_RX_DMA: ++ sirq_log = readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG); ++ if (!i2c_bus->target) ++ ast2700_i2c_get_target(i2c_bus, sirq_log >> SLAVE_ADDR_SHIFT); ++ target_rx_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CS_DMA_LEN_STS)); ++ for (i = 0; i < target_rx_len; i++) { ++ if (i2c_bus->target) { ++ value = readb(i2c_bus->buf_base + 0x30 + i); ++ i2c_slave_event(i2c_bus->target, I2C_SLAVE_WRITE_RECEIVED, &value); ++ } ++ } ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); ++ cmd = TARGET_TRIGGER_CMD | AST2600_I2CS_RX_DMA_EN; ++ break; ++ default: ++ dev_err(i2c_bus->dev, "unhandled target isr case %x, isr %x, sts %x\n", sts, ++ isr, ++ readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF)); ++ ++ /* clear sirq log */ ++ while (readl(i2c_bus->reg_base + AST2700_I2CC_SIRQ_LOG)) ++ ; ++ break; ++ } ++ ++ if (cmd) ++ writel(cmd, i2c_bus->reg_base + AST2600_I2CS_CMD_STS); ++ ++ writel(isr, i2c_bus->reg_base + AST2600_I2CS_ISR); ++ readl(i2c_bus->reg_base + AST2600_I2CS_ISR); ++ ++ if ((sts & AST2600_I2CS_STOP) && !(sts & AST2600_I2CS_SLAVE_PENDING)) ++ i2c_bus->target_operate = 0; ++ else ++ i2c_bus->previous_idx = AST2600_I2CS_GET_TARGET(sts); ++} ++ +static void ast2600_i2c_target_packet_dma_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts) +{ + int target_rx_len = 0; @@ -41539,7 +42759,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->target = i2c_bus->multi_target[AST2600_I2CS_GET_TARGET(sts)]; + + /* Handle i2c target timeout condition */ -+ if (AST2600_I2CS_INACTIVE_TO & sts) { ++ if (sts & AST2600_I2CS_INACTIVE_TO) { + /* Reset time out counter */ + u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) & + AST2600_I2CC_AC_TIMING_MASK; @@ -41673,7 +42893,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + u8 value; + int i; + -+ /* due to controller target share same buffer, so need force the master stop not issue */ ++ /* due to controller target is common buffer, need force the master stop not issue */ + if (readl(i2c_bus->reg_base + AST2600_I2CM_CMD_STS) & GENMASK(15, 0)) { + writel(0, i2c_bus->reg_base + AST2600_I2CM_CMD_STS); + i2c_bus->cmd_err = -EBUSY; @@ -41684,8 +42904,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->target = i2c_bus->multi_target[AST2600_I2CS_GET_TARGET(sts)]; + + /* Handle i2c target timeout condition */ -+ if (AST2600_I2CS_INACTIVE_TO & sts) { -+ /* Reset time out counter */ ++ if (sts & AST2600_I2CS_INACTIVE_TO) { ++ /* Reset timeout counter */ + u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) & + AST2600_I2CC_AC_TIMING_MASK; + @@ -41879,7 +43099,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->target = i2c_bus->multi_target[AST2600_I2CS_GET_TARGET(sts)]; + + /* Handle i2c target timeout condition */ -+ if (AST2600_I2CS_INACTIVE_TO & sts) { ++ if (sts & AST2600_I2CS_INACTIVE_TO) { + /* Reset time out counter */ + u32 ac_timing = readl(i2c_bus->reg_base + AST2600_I2CC_AC_TIMING) & + AST2600_I2CC_AC_TIMING_MASK; @@ -41892,6 +43112,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + writel(sts, i2c_bus->reg_base + AST2600_I2CS_ISR); + readl(i2c_bus->reg_base + AST2600_I2CS_ISR); + i2c_slave_event(i2c_bus->target, I2C_SLAVE_STOP, &value); ++ i2c_bus->target_operate = 0; + return; + } + @@ -41940,26 +43161,19 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + return 0; + + /* -+ * Target interrupt coming after Controller package done ++ * Target interrupt coming after Controller packet done + * So need handle controller first. ++ * avoid case: Mr Dr P -> S match + */ + if (readl(i2c_bus->reg_base + AST2600_I2CM_ISR) & AST2600_I2CM_PKT_DONE) + return 0; + + isr &= ~(AST2600_I2CS_ADDR_MASK); + -+ if (AST2600_I2CS_PKT_DONE & isr) { -+ if (i2c_bus->mode == DMA_MODE) { -+ if (i2c_bus->version == AST2700) -+ ast2700_i2c_target_packet_dma_irq(i2c_bus, isr); -+ else -+ ast2600_i2c_target_packet_dma_irq(i2c_bus, isr); -+ } else { -+ ast2600_i2c_target_packet_buff_irq(i2c_bus, isr); -+ } -+ } else { -+ ast2600_i2c_target_byte_irq(i2c_bus, isr); -+ } ++ if (AST2600_I2CS_PKT_DONE & isr) ++ i2c_bus->target_packet_irq(i2c_bus, isr); ++ else ++ i2c_bus->target_byte_irq(i2c_bus, isr); + + return 1; +} @@ -41992,12 +43206,64 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + return 0; +} + ++static int ast2700_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus) ++{ ++ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; ++ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt; ++ ++ cmd |= AST2600_I2CM_PKT_EN; ++ ++ if (xfer_len > i2c_bus->buf_size) ++ xfer_len = i2c_bus->buf_size; ++ else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) ++ cmd |= AST2600_I2CM_STOP_CMD; ++ ++ if (cmd & AST2600_I2CM_START_CMD) ++ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr); ++ ++ if (xfer_len) { ++ u32 wbuf_dword; ++ ++ cmd |= AST2600_I2CM_TX_DMA_EN | AST2600_I2CM_TX_CMD; ++ /* ++ * The controller's buffer register supports dword writes only. ++ * Therefore, write dwords to the buffer register in a 4-byte aligned, ++ * and write the remaining unaligned data at the end. ++ */ ++ for (int i = 0; i < xfer_len; i += 4) { ++ int xfer_cnt = i2c_bus->controller_xfer_cnt + i; ++ ++ switch (min(xfer_len - i, 4) % 4) { ++ case 1: ++ wbuf_dword = msg->buf[xfer_cnt]; ++ break; ++ case 2: ++ wbuf_dword = get_unaligned_le16(&msg->buf[xfer_cnt]); ++ break; ++ case 3: ++ wbuf_dword = get_unaligned_le24(&msg->buf[xfer_cnt]); ++ break; ++ default: ++ wbuf_dword = get_unaligned_le32(&msg->buf[xfer_cnt]); ++ break; ++ } ++ writel(wbuf_dword, i2c_bus->buf_base + i); ++ } ++ ++ dev_dbg(i2c_bus->dev, "len : %d\n", xfer_len); ++ writel(AST2600_I2CM_SET_TX_DMA_LEN(xfer_len - 1), ++ i2c_bus->reg_base + AST2600_I2CM_DMA_LEN); ++ } ++ ++ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS); ++ ++ return 0; ++} ++ +static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus) +{ + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; + int xfer_len = msg->len - i2c_bus->controller_xfer_cnt; -+ u32 wbuf_dword; -+ int i; + + cmd |= AST2600_I2CM_PKT_EN; + @@ -42010,6 +43276,8 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + cmd |= AST2600_I2CM_PKT_ADDR(msg->addr); + + if (xfer_len) { ++ u32 wbuf_dword; ++ + cmd |= AST2600_I2CM_TX_BUFF_EN | AST2600_I2CM_TX_CMD; + /* + * The controller's buffer register supports dword writes only. @@ -42018,7 +43286,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + */ + if (readl(i2c_bus->reg_base + AST2600_I2CS_ISR)) + return -EBUSY; -+ for (i = 0; i < xfer_len; i += 4) { ++ for (int i = 0; i < xfer_len; i += 4) { + int xfer_cnt = i2c_bus->controller_xfer_cnt + i; + + switch (min(xfer_len - i, 4) % 4) { @@ -42064,7 +43332,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + cmd |= AST2600_I2CM_PKT_ADDR(msg->addr); + + if ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) && -+ xfer_len == 1) ++ ((i2c_bus->controller_xfer_cnt + 1) == msg->len)) + cmd |= AST2600_I2CM_STOP_CMD; + + if (xfer_len) { @@ -42104,6 +43372,32 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + return 0; +} + ++static int ast2700_i2c_setup_buff_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus) ++{ ++ struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; ++ int xfer_len = msg->len - i2c_bus->controller_xfer_cnt; ++ ++ cmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_DMA_EN | AST2600_I2CM_RX_CMD; ++ ++ if (cmd & AST2600_I2CM_START_CMD) ++ cmd |= AST2600_I2CM_PKT_ADDR(msg->addr); ++ ++ if (msg->flags & I2C_M_RECV_LEN) { ++ dev_dbg(i2c_bus->dev, "smbus read\n"); ++ xfer_len = 1; ++ } else if (xfer_len > i2c_bus->buf_size) { ++ xfer_len = i2c_bus->buf_size; ++ } else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) { ++ cmd |= CONTROLLER_TRIGGER_LAST_STOP; ++ } ++ ++ writel(AST2600_I2CM_SET_RX_DMA_LEN(xfer_len - 1), ++ i2c_bus->reg_base + AST2600_I2CM_DMA_LEN); ++ writel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS); ++ ++ return 0; ++} ++ +static int ast2600_i2c_setup_buff_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus) +{ + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; @@ -42142,7 +43436,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + if (msg->flags & I2C_M_RECV_LEN) { + dev_dbg(i2c_bus->dev, "smbus read\n"); + } else if ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) && -+ ((i2c_bus->controller_xfer_cnt + 1) == msg->len)) { ++ ((i2c_bus->controller_xfer_cnt + 1) == msg->len)) { + cmd |= CONTROLLER_TRIGGER_LAST_STOP; + } + @@ -42156,29 +43450,21 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; + + /* send start */ -+ dev_dbg(i2c_bus->dev, "[%d] %sing %d byte%s %s 0x%02x\n", -+ i2c_bus->msgs_index, msg->flags & I2C_M_RD ? "read" : "write", -+ msg->len, msg->len > 1 ? "s" : "", ++ dev_dbg(i2c_bus->dev, "[%d] %s %d byte%s %s 0x%02x\n", ++ i2c_bus->msgs_index, str_read_write(msg->flags & I2C_M_RD), ++ msg->len, str_plural(msg->len), + msg->flags & I2C_M_RD ? "from" : "to", msg->addr); + ++ if (!i2c_bus->setup_rx || !i2c_bus->setup_tx) ++ return -EINVAL; ++ + i2c_bus->controller_xfer_cnt = 0; + i2c_bus->buf_index = 0; + -+ if (msg->flags & I2C_M_RD) { -+ if (i2c_bus->mode == DMA_MODE) -+ return ast2600_i2c_setup_dma_rx(AST2600_I2CM_START_CMD, i2c_bus); -+ else if (i2c_bus->mode == BUFF_MODE) -+ return ast2600_i2c_setup_buff_rx(AST2600_I2CM_START_CMD, i2c_bus); -+ else -+ return ast2600_i2c_setup_byte_rx(AST2600_I2CM_START_CMD, i2c_bus); -+ } else { -+ if (i2c_bus->mode == DMA_MODE) -+ return ast2600_i2c_setup_dma_tx(AST2600_I2CM_START_CMD, i2c_bus); -+ else if (i2c_bus->mode == BUFF_MODE) -+ return ast2600_i2c_setup_buff_tx(AST2600_I2CM_START_CMD, i2c_bus); -+ else -+ return ast2600_i2c_setup_byte_tx(AST2600_I2CM_START_CMD, i2c_bus); -+ } ++ if (msg->flags & I2C_M_RD) ++ return i2c_bus->setup_rx(AST2600_I2CM_START_CMD, i2c_bus); ++ ++ return i2c_bus->setup_tx(AST2600_I2CM_START_CMD, i2c_bus); +} + +static int ast2700_i2c_irq_err_to_errno(u32 irq_status) @@ -42193,28 +43479,35 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + return 0; +} + ++static void ast2700_i2c_irq_clear(struct ast2600_i2c_bus *i2c_bus, u32 sts) ++{ ++ writel(sts, i2c_bus->reg_base + AST2600_I2CM_ISR); ++} ++ +static int ast2600_i2c_irq_err_to_errno(u32 irq_status) +{ + if (irq_status & AST2600_I2CM_ARBIT_LOSS) + return -EAGAIN; + if (irq_status & (AST2600_I2CM_SDA_DL_TO | AST2600_I2CM_SCL_LOW_TO)) -+ return -EBUSY; ++ return -ETIMEDOUT; + if (irq_status & (AST2600_I2CM_ABNORMAL)) + return -EPROTO; + + return 0; +} + -+static void ast2600_i2c_controller_package_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts) ++static void ast2600_i2c_irq_clear(struct ast2600_i2c_bus *i2c_bus, u32 sts) ++{ ++ writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR); ++} ++ ++static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts) +{ + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index]; + int xfer_len; + int i; + -+ if (i2c_bus->version == AST2700) -+ writel(sts, i2c_bus->reg_base + AST2600_I2CM_ISR); -+ else -+ writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR); ++ i2c_bus->irq_clear(i2c_bus, sts); + + sts &= ~(AST2600_I2CM_PKT_DONE | AST2600_I2CM_SW_ISR_MASK); + @@ -42248,8 +43541,12 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + xfer_len = AST2600_I2C_GET_TX_DMA_LEN(readl(i2c_bus->reg_base + + AST2600_I2CM_DMA_LEN_STS)); + } else if (i2c_bus->mode == BUFF_MODE) { -+ xfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base + -+ AST2600_I2CC_BUFF_CTRL)); ++ if (i2c_bus->version == AST2600) ++ xfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CC_BUFF_CTRL)); ++ else ++ xfer_len = AST2600_I2C_GET_TX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CM_DMA_LEN_STS)); + } else { + xfer_len = 1; + } @@ -42268,18 +43565,13 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + } + } + } else { -+ if (i2c_bus->mode == DMA_MODE) -+ ast2600_i2c_setup_dma_tx(0, i2c_bus); -+ else if (i2c_bus->mode == BUFF_MODE) -+ ast2600_i2c_setup_buff_tx(0, i2c_bus); -+ else -+ ast2600_i2c_setup_byte_tx(0, i2c_bus); ++ i2c_bus->setup_tx(0, i2c_bus); + } + break; + case AST2600_I2CM_RX_DONE: +#if IS_ENABLED(CONFIG_I2C_SLAVE) + /* -+ * Workaround for controller/target package mode enable rx done stuck issue ++ * Workaround for controller/target packet mode enable rx done stuck issue + * When controller go for first read (RX_DONE), target mode will also effect + * Then controller will send nack, not operate anymore. + */ @@ -42299,8 +43591,13 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + memcpy(&msg->buf[i2c_bus->controller_xfer_cnt], + i2c_bus->controller_dma_buf, xfer_len); + } else if (i2c_bus->mode == BUFF_MODE) { -+ xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + -+ AST2600_I2CC_BUFF_CTRL)); ++ if (i2c_bus->version == AST2600) { ++ xfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CC_BUFF_CTRL)); ++ } else { ++ xfer_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base + ++ AST2600_I2CM_DMA_LEN_STS)); ++ } + for (i = 0; i < xfer_len; i++) + msg->buf[i2c_bus->controller_xfer_cnt + i] = + readb(i2c_bus->buf_base + 0x10 + i); @@ -42316,10 +43613,10 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + + if (i2c_bus->version == AST2700) { + recv_len = AST2700_I2CC_GET_BUFF(readl(i2c_bus->reg_base -+ + BYTE_DATA_LOG)); ++ + BYTE_DATA_LOG)); + } else { + recv_len = AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base -+ + AST2600_I2CC_STS_AND_BUFF)); ++ + AST2600_I2CC_STS_AND_BUFF)); + } + + msg->len = min_t(unsigned int, recv_len, I2C_SMBUS_BLOCK_MAX); @@ -42345,12 +43642,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + } + } + } else { -+ if (i2c_bus->mode == DMA_MODE) -+ ast2600_i2c_setup_dma_rx(0, i2c_bus); -+ else if (i2c_bus->mode == BUFF_MODE) -+ ast2600_i2c_setup_buff_rx(0, i2c_bus); -+ else -+ ast2600_i2c_setup_byte_rx(0, i2c_bus); ++ i2c_bus->setup_rx(0, i2c_bus); + } + break; + default: @@ -42369,9 +43661,9 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + sts &= ~AST2600_I2CM_ISR_MASK; + + if (!i2c_bus->alert_enable) -+ sts &= ~AST2600_I2CM_SMBUS_ALT; ++ sts &= ~AST2600_I2CM_SMBUS_ALERT; + -+ if (AST2600_I2CM_BUS_RECOVER_FAIL & sts) { ++ if (sts & AST2600_I2CM_BUS_RECOVER_FAIL) { + writel(AST2600_I2CM_BUS_RECOVER_FAIL, i2c_bus->reg_base + AST2600_I2CM_ISR); + ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); + writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); @@ -42381,19 +43673,19 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + return 1; + } + -+ if (AST2600_I2CM_BUS_RECOVER & sts) { ++ if (sts & AST2600_I2CM_BUS_RECOVER) { + writel(AST2600_I2CM_BUS_RECOVER, i2c_bus->reg_base + AST2600_I2CM_ISR); + i2c_bus->cmd_err = 0; + complete(&i2c_bus->cmd_complete); + return 1; + } + -+ if (AST2600_I2CM_SMBUS_ALT & sts) { -+ if (ier & AST2600_I2CM_SMBUS_ALT) { ++ if (sts & AST2600_I2CM_SMBUS_ALERT) { ++ if (ier & AST2600_I2CM_SMBUS_ALERT) { + /* Disable ALT INT */ -+ writel(ier & ~AST2600_I2CM_SMBUS_ALT, i2c_bus->reg_base + AST2600_I2CM_IER); ++ writel(ier & ~AST2600_I2CM_SMBUS_ALERT, i2c_bus->reg_base + AST2600_I2CM_IER); + i2c_handle_smbus_alert(i2c_bus->ara); -+ writel(AST2600_I2CM_SMBUS_ALT, i2c_bus->reg_base + AST2600_I2CM_ISR); ++ writel(AST2600_I2CM_SMBUS_ALERT, i2c_bus->reg_base + AST2600_I2CM_ISR); + dev_err(i2c_bus->dev, + "ast2600_controller_alert_recv bus id %d, Disable Alt, Please Imple\n", + i2c_bus->adap.nr); @@ -42402,24 +43694,17 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + } + + /* handle controller abnormal condition */ -+ if (i2c_bus->version == AST2700) { -+ i2c_bus->cmd_err = ast2700_i2c_irq_err_to_errno(sts); -+ if (i2c_bus->cmd_err) { -+ writel(sts, i2c_bus->reg_base + AST2600_I2CM_ISR); -+ complete(&i2c_bus->cmd_complete); -+ return 1; -+ } -+ } else { -+ i2c_bus->cmd_err = ast2600_i2c_irq_err_to_errno(sts); ++ if (i2c_bus->irq_err_to_errno && i2c_bus->irq_clear) { ++ i2c_bus->cmd_err = i2c_bus->irq_err_to_errno(sts); + if (i2c_bus->cmd_err) { -+ writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR); ++ i2c_bus->irq_clear(i2c_bus, sts); + complete(&i2c_bus->cmd_complete); + return 1; + } + } + -+ if (AST2600_I2CM_PKT_DONE & sts) { -+ ast2600_i2c_controller_package_irq(i2c_bus, sts); ++ if (sts & AST2600_I2CM_PKT_DONE) { ++ ast2600_i2c_controller_packet_irq(i2c_bus, sts); + return 1; + } + @@ -42487,6 +43772,12 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF)); + writel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); + writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); ++ ++ /* ++ * A slave holding SCL low can stall the transfer and trigger ++ * a master timeout. In multi-master mode, attempt bus recovery ++ * if the bus is still busy. ++ */ + if (i2c_bus->multi_master && !i2c_bus->target_operate && + (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) & + AST2600_I2CC_BUS_BUSY_STS)) @@ -42510,7 +43801,6 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + /* I2C Reset */ + writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); + -+ i2c_bus->multi_master = device_property_read_bool(&pdev->dev, "multi-master"); + if (!i2c_bus->multi_master) + fun_ctrl |= AST2600_I2CC_MULTI_MASTER_DIS; + @@ -42550,14 +43840,22 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + + /* Enable Controller Mode */ + writel(fun_ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); -+ /* Disable Target Address */ ++ /* disable target address */ + writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL); + ++ if (i2c_bus->version == AST2700 && i2c_bus->mode == BUFF_MODE) { ++ writel(0x00, i2c_bus->reg_base + AST2600_I2CM_TX_DMA); ++ writel(0x00, i2c_bus->reg_base + AST2600_I2CM_TX_DMA_H); ++ writel(0x10, i2c_bus->reg_base + AST2600_I2CM_RX_DMA); ++ writel(0x00, i2c_bus->reg_base + AST2600_I2CM_RX_DMA_H); ++ writel(0x20, i2c_bus->reg_base + AST2600_I2CS_TX_DMA); ++ writel(0x00, i2c_bus->reg_base + AST2600_I2CS_TX_DMA_H); ++ writel(0x30, i2c_bus->reg_base + AST2600_I2CS_RX_DMA); ++ writel(0x00, i2c_bus->reg_base + AST2600_I2CS_RX_DMA_H); ++ } ++ + /* Set AC Timing */ -+ if (i2c_bus->version == AST2700) -+ ast2700_i2c_ac_timing_config(i2c_bus); -+ else -+ ast2600_i2c_ac_timing_config(i2c_bus); ++ i2c_bus->ac_timing_config(i2c_bus); + + if (i2c_bus->mode == DMA_MODE) { + i2c_bus->controller_dma_buf = @@ -42683,8 +43981,11 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); + } else if (i2c_bus->mode == BUFF_MODE) { + cmd = TARGET_TRIGGER_CMD; -+ if (i2c_bus->version == AST2700) ++ if (i2c_bus->version == AST2700) { ++ writel(AST2600_I2CS_SET_RX_DMA_LEN(i2c_bus->buf_size), ++ i2c_bus->reg_base + AST2600_I2CS_DMA_LEN); + cmd |= AST2600_I2CS_RX_DMA_EN; ++ } + } else { + cmd &= ~AST2600_I2CS_PKT_MODE_EN; + } @@ -42751,30 +44052,38 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c +#endif +}; + ++static const struct i2c_adapter_quirks ast2600_i2c_quirks = { ++ .flags = I2C_AQ_NO_ZERO_LEN_READ, ++}; ++ +static int ast2600_i2c_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ast2600_i2c_bus *i2c_bus; ++ struct reset_control *rst; + const char *xfer_mode; + struct resource *res; + u32 global_ctrl; + int ret; + ++ if (!device_property_present(dev, "aspeed,global-regs")) ++ return -ENODEV; ++ + i2c_bus = devm_kzalloc(dev, sizeof(*i2c_bus), GFP_KERNEL); + if (!i2c_bus) + return -ENOMEM; + -+ i2c_bus->version = (enum i2c_version)device_get_match_data(dev); -+ + i2c_bus->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(i2c_bus->reg_base)) + return PTR_ERR(i2c_bus->reg_base); + -+ i2c_bus->rst = devm_reset_control_get_shared(dev, NULL); -+ if (IS_ERR(i2c_bus->rst)) -+ return dev_err_probe(dev, PTR_ERR(i2c_bus->rst), "Missing reset ctrl\n"); ++ rst = devm_reset_control_get_shared(dev, NULL); ++ if (IS_ERR(rst)) ++ return dev_err_probe(dev, PTR_ERR(rst), "Missing reset ctrl\n"); ++ ++ reset_control_deassert(rst); + -+ reset_control_deassert(i2c_bus->rst); ++ i2c_bus->version = (enum i2c_version)device_get_match_data(dev); + + i2c_bus->global_regs = + syscon_regmap_lookup_by_phandle(dev_of_node(dev), "aspeed,global-regs"); @@ -42792,6 +44101,29 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + AST2600_I2CG_CLK_DIV_CTRL, AST2700_I2CCG_DIV_CTRL); + } + ++ if (i2c_bus->version == AST2700) { ++ i2c_bus->ac_timing_config = ast2700_i2c_ac_timing_config; ++ i2c_bus->irq_err_to_errno = ast2700_i2c_irq_err_to_errno; ++ i2c_bus->irq_clear = ast2700_i2c_irq_clear; ++ } else { ++ i2c_bus->ac_timing_config = ast2600_i2c_ac_timing_config; ++ i2c_bus->irq_err_to_errno = ast2600_i2c_irq_err_to_errno; ++ i2c_bus->irq_clear = ast2600_i2c_irq_clear; ++ } ++ ++ /* Default BUFF mode, allow override */ ++ i2c_bus->mode = BUFF_MODE; ++ ++ /* override the transfer mode */ ++ if (!device_property_read_string(dev, "aspeed,transfer-mode", &xfer_mode)) { ++ if (!strcmp(xfer_mode, "dma")) ++ i2c_bus->mode = DMA_MODE; ++ else if (!strcmp(xfer_mode, "byte")) ++ i2c_bus->mode = BYTE_MODE; ++ else ++ i2c_bus->mode = BUFF_MODE; ++ } ++ +#if IS_ENABLED(CONFIG_I2C_SLAVE) + i2c_bus->target_operate = 0; + i2c_bus->target_attached = 0; @@ -42799,18 +44131,32 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->multi_target[i] = NULL; +#endif + i2c_bus->dev = dev; -+ if (i2c_bus->version == AST2600) { -+ i2c_bus->mode = BUFF_MODE; ++ i2c_bus->multi_master = device_property_read_bool(dev, "multi-master"); ++ ++ if (i2c_bus->version == AST2700) { ++ /* select the transfer method*/ ++ u32 xfer_type = readl(i2c_bus->reg_base + I2CC_VERSION_CTRL); + -+ if (!device_property_read_string(dev, "aspeed,transfer-mode", &xfer_mode)) { -+ if (!strcmp(xfer_mode, "dma")) ++ /* AST2700 would not allow byte mode */ ++ if (i2c_bus->mode == BYTE_MODE) ++ i2c_bus->mode = DMA_MODE; ++ ++ if (i2c_bus->mode == DMA_MODE) { ++ /* ast2700 cfg enable dma */ ++ xfer_type |= FUNC_CFG_DMA_EN; ++ } else if (i2c_bus->mode == BUFF_MODE) { ++ i2c_bus->buf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); ++ if (IS_ERR(i2c_bus->buf_base)) { + i2c_bus->mode = DMA_MODE; -+ else if (!strcmp(xfer_mode, "byte")) -+ i2c_bus->mode = BYTE_MODE; -+ else -+ i2c_bus->mode = BUFF_MODE; -+ } ++ } else { ++ i2c_bus->buf_size = resource_size(res) / 4; + ++ /* ast2700 cfg disable dma */ ++ xfer_type &= ~FUNC_CFG_DMA_EN; ++ } ++ } ++ writel(xfer_type, i2c_bus->reg_base + I2CC_VERSION_CTRL); ++ } else { + if (i2c_bus->mode == BUFF_MODE) { + i2c_bus->buf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); + if (IS_ERR(i2c_bus->buf_base)) @@ -42818,10 +44164,50 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + else + i2c_bus->buf_size = resource_size(res) / 2; + } -+ } else { -+ i2c_bus->mode = DMA_MODE; + } + ++ switch (i2c_bus->mode) { ++ case DMA_MODE: ++ i2c_bus->setup_tx = ast2600_i2c_setup_dma_tx; ++ i2c_bus->setup_rx = ast2600_i2c_setup_dma_rx; ++ break; ++ case BUFF_MODE: ++ default: ++ if (i2c_bus->version == AST2700) { ++ i2c_bus->setup_tx = ast2700_i2c_setup_buff_tx; ++ i2c_bus->setup_rx = ast2700_i2c_setup_buff_rx; ++ } else { ++ i2c_bus->setup_tx = ast2600_i2c_setup_buff_tx; ++ i2c_bus->setup_rx = ast2600_i2c_setup_buff_rx; ++ } ++ break; ++ case BYTE_MODE: ++ i2c_bus->setup_tx = ast2600_i2c_setup_byte_tx; ++ i2c_bus->setup_rx = ast2600_i2c_setup_byte_rx; ++ break; ++ } ++ ++#if IS_ENABLED(CONFIG_I2C_SLAVE) ++ switch (i2c_bus->mode) { ++ case DMA_MODE: ++ if (i2c_bus->version == AST2700) ++ i2c_bus->target_packet_irq = ast2700_i2c_target_packet_dma_irq; ++ else ++ i2c_bus->target_packet_irq = ast2600_i2c_target_packet_dma_irq; ++ break; ++ case BUFF_MODE: ++ default: ++ if (i2c_bus->version == AST2700) ++ i2c_bus->target_packet_irq = ast2700_i2c_target_packet_buff_irq; ++ else ++ i2c_bus->target_packet_irq = ast2600_i2c_target_packet_buff_irq; ++ break; ++ case BYTE_MODE: ++ i2c_bus->target_byte_irq = ast2600_i2c_target_byte_irq; ++ break; ++ } ++#endif ++ + /* + * i2c timeout counter: use base clk4 1Mhz, + * per unit: 1/(1000/1024) = 1024us @@ -42829,10 +44215,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + */ + ret = device_property_read_u32(dev, "i2c-scl-clk-low-timeout-us", &i2c_bus->timeout); + if (!ret) { -+ i2c_bus->timeout /= 1024; -+ -+ if (!i2c_bus->timeout) -+ i2c_bus->timeout = 1; ++ i2c_bus->timeout = DIV_ROUND_UP(i2c_bus->timeout, 1024); + } + + init_completion(&i2c_bus->cmd_complete); @@ -42854,11 +44237,12 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + /* Initialize the I2C adapter */ + i2c_bus->adap.owner = THIS_MODULE; + i2c_bus->adap.algo = &i2c_ast2600_algorithm; ++ i2c_bus->adap.quirks = &ast2600_i2c_quirks; + i2c_bus->adap.retries = 0; + i2c_bus->adap.dev.parent = i2c_bus->dev; + device_set_node(&i2c_bus->adap.dev, dev_fwnode(dev)); + i2c_bus->adap.algo_data = i2c_bus; -+ strscpy(i2c_bus->adap.name, pdev->name, sizeof(i2c_bus->adap.name)); ++ strscpy(i2c_bus->adap.name, pdev->name); + i2c_set_adapdata(&i2c_bus->adap, i2c_bus); + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + @@ -42868,16 +44252,18 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + + ret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq, 0, + dev_name(dev), i2c_bus); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "Unable to request irq %d\n", i2c_bus->irq); ++ if (ret < 0) { ++ ret = dev_err_probe(dev, ret, "Unable to request irq %d\n", ++ i2c_bus->irq); ++ goto err; ++ } + -+ /* Set interrupt generation of i2c controller */ + writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER, + i2c_bus->reg_base + AST2600_I2CM_IER); + + ret = devm_i2c_add_adapter(dev, &i2c_bus->adap); + if (ret) -+ return ret; ++ goto err; + + i2c_bus->alert_enable = device_property_read_bool(dev, "smbus-alert"); + if (i2c_bus->alert_enable) { @@ -42886,7 +44272,7 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + dev_warn(dev, "Failed to register ARA client\n"); + else + writel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER | -+ AST2600_I2CM_SMBUS_ALT, ++ AST2600_I2CM_SMBUS_ALERT, + i2c_bus->reg_base + AST2600_I2CM_IER); + } else { + i2c_bus->alert_enable = false; @@ -42897,6 +44283,11 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + i2c_bus->mode, i2c_bus->version); + + return 0; ++ ++err: ++ writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL); ++ writel(0, i2c_bus->reg_base + AST2600_I2CM_IER); ++ return ret; +} + +static void ast2600_i2c_remove(struct platform_device *pdev) @@ -42908,30 +44299,29 @@ diff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c + writel(0, i2c_bus->reg_base + AST2600_I2CM_IER); +} + -+static const struct of_device_id ast2600_i2c_bus_of_table[] = { ++static const struct of_device_id ast2600_i2c_of_match[] = { + { .compatible = "aspeed,ast2600-i2cv2", .data = (const void *)AST2600, }, + { .compatible = "aspeed,ast2700-i2c", .data = (const void *)AST2700, }, + {} +}; -+MODULE_DEVICE_TABLE(of, ast2600_i2c_bus_of_table); ++MODULE_DEVICE_TABLE(of, ast2600_i2c_of_match); + -+static struct platform_driver ast2600_i2c_bus_driver = { -+ .probe = ast2600_i2c_probe, -+ .remove_new = ast2600_i2c_remove, -+ .driver = { ++static struct platform_driver ast2600_i2c_driver = { ++ .probe = ast2600_i2c_probe, ++ .remove = ast2600_i2c_remove, ++ .driver = { + .name = KBUILD_MODNAME, -+ .of_match_table = ast2600_i2c_bus_of_table, ++ .of_match_table = ast2600_i2c_of_match, + }, +}; -+ -+module_platform_driver(ast2600_i2c_bus_driver); ++module_platform_driver(ast2600_i2c_driver); + +MODULE_AUTHOR("Ryan Chen "); +MODULE_DESCRIPTION("ASPEED AST2600 I2C Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast2600-i3c-master.c ---- a/drivers/i3c/master/ast2600-i3c-master.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/i3c/master/ast2600-i3c-master.c 2026-04-08 18:03:48.267706106 +0000 +--- a/drivers/i3c/master/ast2600-i3c-master.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/i3c/master/ast2600-i3c-master.c 2026-07-17 08:44:55.247307213 +0000 @@ -10,6 +10,8 @@ #include #include @@ -43612,8 +45002,8 @@ diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast260 diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h 2026-04-08 18:03:48.261706215 +0000 -@@ -0,0 +1,413 @@ ++++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h 2026-07-17 08:44:55.241307306 +0000 +@@ -0,0 +1,453 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef VENDOR_ASPEED_H +#define VENDOR_ASPEED_H @@ -43625,6 +45015,7 @@ diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/maste + +#define ASPEED_I3C_CTRL 0x0 +#define ASPEED_I3C_CTRL_STOP_QUEUE_PT BIT(31) //Stop the queue read pointer. ++#define ASPEED_I3C_CTRL_CLOCK_STALL_EN BIT(14) +#define ASPEED_I3C_CTRL_INIT BIT(4) +#define ASPEED_I3C_CTRL_INIT_MODE GENMASK(1, 0) +#define INIT_MST_MODE 0 @@ -43644,6 +45035,10 @@ diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/maste +#define ASPEED_I3C_STS_MODE_PRIMARY_SLV BIT(1) +#define ASPEED_I3C_STS_MODE_PRIMARY_MST BIT(0) + ++#define ASPEED_I3C_MST_MRL 0x8 ++#define ASPEED_I3C_IBI_TERMINATE_EN BIT(16) ++#define ASPEED_I3C_IBI_TERMINATE_LEN GENMASK(15, 0) ++ +#define ASPEED_I3C_DAA_INDEX0 0x10 +#define ASPEED_I3C_DAA_INDEX1 0x14 +#define ASPEED_I3C_DAA_INDEX2 0x18 @@ -43675,6 +45070,36 @@ diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/maste +#define ASPEED_I3C_AUTOCMD_SEL_112_119 0x78 +#define ASPEED_I3C_AUTOCMD_SEL_120_127 0x7C + ++#define ASPEED_I3C_WDMA_CTRL 0x80 ++#define ASPEED_I3C_WDMA_DBG_LO 0x84 ++#define ASPEED_I3C_WDMA_DBG_HI 0x88 ++#define ASPEED_I3C_RDMA_CTRL 0x90 ++#define ASPEED_I3C_RDMA_DBG_LO 0x94 ++#define ASPEED_I3C_RDMA_DBG_HI 0x98 ++#define I3C_DMA_DBG_LO_ABORT BIT(0) ++#define I3C_DMA_DBG_LO_BUSY BIT(1) ++#define I3C_DMA_DBG_LO_DONE BIT(2) ++#define I3C_DMA_DBG_LO_START BIT(3) ++#define I3C_DMA_DBG_LO_READY BIT(4) ++#define I3C_DMA_DBG_LO_VALID BIT(5) ++#define I3C_DMA_DBG_LO_EN BIT(10) ++ ++#define ASPEED_I3C_RING_STATUS 0x9C ++#define I3C_RING_IDLE 0 ++#define I3C_RING_GET_TRANSFER 1 ++#define I3C_RING_GET_TX_DATA 2 ++#define I3C_RING_CMD_PROCESS 3 ++#define I3C_RING_WRITE_RX_DATA 4 ++#define I3C_RING_WRITE_RESPONSE 5 ++#define I3C_RING_UPDATE_CR_PTR 6 ++#define I3C_RING_WRITE_IBI_STS 7 ++#define I3C_RING_WRITE_IBI_DAT 8 ++#define I3C_RING_UPDATE_IBI_PTR 9 ++#define I3C_RING_ABORT 10 ++#define I3C_RING_SLV_WRITE_DATA 11 ++#define I3C_RING_SLV_WRITE_RESPONSE 12 ++#define I3C_RING_SLV_UPDATE_PT 13 ++ +#define ASPEED_I3C_SLV_CHAR_CTRL 0xA0 +#define ASPEED_I3C_SLV_CHAR_CTRL_DCR GENMASK(23, 16) +#define ASPEED_I3C_SLV_CHAR_CTRL_BCR GENMASK(15, 8) @@ -43865,8 +45290,13 @@ diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/maste +#define PHY_I3C_OD_CTRL3_HD_DAT GENMASK(25, 16) +#define PHY_I3C_OD_CTRL3_AHD_DAT GENMASK(9, 0) + -+#define PHY_I3C_OD_DEFAULT_CAS_NS 40 -+#define PHY_I3C_OD_DEFAULT_CBP_NS 40 ++/* ++ * MIPI I3C minimum timing for the Open-Drain start/stop, expressed in tenths ++ * of a nanosecond to keep the fractional spec values (tCAS >= 38.4ns, ++ * tCBP >= 19.2ns) in integer arithmetic. ++ */ ++#define PHY_I3C_OD_MIN_CAS_NS_X10 384 ++#define PHY_I3C_OD_MIN_CBP_NS_X10 192 +#define PHY_I3C_OD_DEFAULT_SCL_H_NS 380 +#define PHY_I3C_OD_DEFAULT_SCL_L_NS 620 +#define PHY_I3C_OD_DEFAULT_HD_DAT 10 @@ -44029,7 +45459,7 @@ diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/maste +#endif diff --git a/drivers/i3c/mctp/Kconfig b/drivers/i3c/mctp/Kconfig --- a/drivers/i3c/mctp/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/Kconfig 2026-04-08 18:03:46.423739796 +0000 ++++ b/drivers/i3c/mctp/Kconfig 2026-07-17 08:44:53.311337053 +0000 @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only +config I3C_MCTP @@ -44056,14 +45486,14 @@ diff --git a/drivers/i3c/mctp/Kconfig b/drivers/i3c/mctp/Kconfig + configured as an I3C Target Device on the I3C Bus. diff --git a/drivers/i3c/mctp/Makefile b/drivers/i3c/mctp/Makefile --- a/drivers/i3c/mctp/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/Makefile 2026-04-08 18:03:46.423739796 +0000 ++++ b/drivers/i3c/mctp/Makefile 2026-07-17 08:44:53.311337053 +0000 @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_I3C_MCTP) += i3c-mctp.o +obj-$(CONFIG_I3C_TARGET_MCTP) += i3c-target-mctp.o diff --git a/drivers/i3c/mctp/i3c-mctp.c b/drivers/i3c/mctp/i3c-mctp.c --- a/drivers/i3c/mctp/i3c-mctp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/i3c-mctp.c 2026-04-08 18:03:46.423739796 +0000 ++++ b/drivers/i3c/mctp/i3c-mctp.c 2026-07-17 08:44:53.311337053 +0000 @@ -0,0 +1,697 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022 Intel Corporation.*/ @@ -44764,8 +46194,8 @@ diff --git a/drivers/i3c/mctp/i3c-mctp.c b/drivers/i3c/mctp/i3c-mctp.c +MODULE_LICENSE("GPL"); diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mctp.c --- a/drivers/i3c/mctp/i3c-target-mctp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/i3c/mctp/i3c-target-mctp.c 2026-04-08 18:03:46.423739796 +0000 -@@ -0,0 +1,485 @@ ++++ b/drivers/i3c/mctp/i3c-target-mctp.c 2026-07-17 08:44:53.311337053 +0000 +@@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2022 Intel Corporation.*/ + @@ -45160,17 +46590,15 @@ diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mc + + ret = cdev_add(&priv->cdev, + MKDEV(MAJOR(i3c_target_mctp_devt), priv->id), 1); -+ if (ret) { -+ ida_free(&i3c_target_mctp_ida, priv->id); -+ return ret; -+ } ++ if (ret) ++ goto err_ida; + + dev = device_create(i3c_target_mctp_class, parent, + MKDEV(MAJOR(i3c_target_mctp_devt), priv->id), i3cdev, + "i3c-mctp-target-%d", priv->id); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); -+ goto err; ++ goto err_cdev; + } + + /* @@ -45183,7 +46611,7 @@ diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mc + if (unlikely(ret)) { + dev_err(dev, "Failed creating device attrs\n"); + ret = -EINVAL; -+ goto err; ++ goto err_device; + } + + i3cdev_set_drvdata(i3cdev, priv); @@ -45193,8 +46621,13 @@ diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mc + crc8_populate_msb(i3c_crc8_table, I3C_CRC8_POLYNOMIAL); + + return 0; -+err: ++ ++err_device: ++ device_destroy(i3c_target_mctp_class, ++ MKDEV(MAJOR(i3c_target_mctp_devt), priv->id)); ++err_cdev: + cdev_del(&priv->cdev); ++err_ida: + ida_free(&i3c_target_mctp_ida, priv->id); + + return ret; @@ -45204,7 +46637,8 @@ diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mc +{ + struct i3c_target_mctp *priv = dev_get_drvdata(i3cdev_to_dev(i3cdev)); + -+ device_destroy(i3c_target_mctp_class, i3c_target_mctp_devt); ++ device_destroy(i3c_target_mctp_class, ++ MKDEV(MAJOR(i3c_target_mctp_devt), priv->id)); + cdev_del(&priv->cdev); + ida_free(&i3c_target_mctp_ida, priv->id); +} @@ -45252,8 +46686,8 @@ diff --git a/drivers/i3c/mctp/i3c-target-mctp.c b/drivers/i3c/mctp/i3c-target-mc +MODULE_DESCRIPTION("I3C Target MCTP driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c ---- a/drivers/iio/adc/aspeed_adc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/iio/adc/aspeed_adc.c 2026-04-08 18:03:48.116708865 +0000 +--- a/drivers/iio/adc/aspeed_adc.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/iio/adc/aspeed_adc.c 2026-07-17 08:44:55.091309618 +0000 @@ -72,6 +72,8 @@ #define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) #define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) @@ -45549,15 +46983,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c static void aspeed_adc_reset_assert(void *data) { struct reset_control *rst = data; -@@ -415,6 +572,7 @@ - } - adc_engine_control_reg_val = - readl(data->base + ASPEED_REG_ENGINE_CONTROL); -+ adc_engine_control_reg_val &= ~ASPEED_ADC_REF_VOLTAGE; - - ret = devm_regulator_get_enable_read_voltage(data->dev, "vref"); - if (ret < 0 && ret != -ENODEV) -@@ -474,6 +632,7 @@ +@@ -475,6 +632,7 @@ u32 adc_engine_control_reg_val; unsigned long scaler_flags = 0; char clk_name[32], clk_parent_name[32]; @@ -45565,7 +46991,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); if (!indio_dev) -@@ -488,12 +647,44 @@ +@@ -489,12 +647,44 @@ if (IS_ERR(data->base)) return PTR_ERR(data->base); @@ -45611,7 +47037,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c data->fixed_div_clk = clk_hw_register_fixed_factor( &pdev->dev, clk_name, clk_parent_name, 0, 1, 2); if (IS_ERR(data->fixed_div_clk)) -@@ -508,7 +699,7 @@ +@@ -509,7 +699,7 @@ if (data->model_data->need_prescaler) { snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", @@ -45620,7 +47046,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c data->clk_prescaler = devm_clk_hw_register_divider( &pdev->dev, clk_name, clk_parent_name, 0, data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, -@@ -524,7 +715,7 @@ +@@ -525,7 +715,7 @@ * setting to adjust the prescaler as well. */ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler", @@ -45629,7 +47055,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c data->clk_scaler = devm_clk_hw_register_divider( &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, -@@ -612,13 +803,25 @@ +@@ -613,13 +803,25 @@ aspeed_adc_compensation(indio_dev); /* Start all channels in normal mode. */ @@ -45660,7 +47086,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = data->battery_sensing ? -@@ -645,6 +848,16 @@ +@@ -646,6 +848,16 @@ .field = GENMASK(7, 4), }; @@ -45677,7 +47103,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", .vref_fixed_mv = 2500, -@@ -653,6 +866,7 @@ +@@ -654,6 +866,7 @@ .need_prescaler = true, .scaler_bit_width = 10, .num_channels = 16, @@ -45685,7 +47111,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c }; static const struct aspeed_adc_model_data ast2500_model_data = { -@@ -665,6 +879,7 @@ +@@ -666,6 +879,7 @@ .scaler_bit_width = 10, .num_channels = 16, .trim_locate = &ast2500_adc_trim, @@ -45693,7 +47119,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c }; static const struct aspeed_adc_model_data ast2600_adc0_model_data = { -@@ -676,6 +891,7 @@ +@@ -677,6 +891,7 @@ .scaler_bit_width = 16, .num_channels = 8, .trim_locate = &ast2600_adc0_trim, @@ -45701,7 +47127,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c }; static const struct aspeed_adc_model_data ast2600_adc1_model_data = { -@@ -687,6 +903,29 @@ +@@ -688,6 +903,29 @@ .scaler_bit_width = 16, .num_channels = 8, .trim_locate = &ast2600_adc1_trim, @@ -45731,7 +47157,7 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c }; static const struct of_device_id aspeed_adc_matches[] = { -@@ -694,6 +933,8 @@ +@@ -695,6 +933,8 @@ { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, @@ -45741,9 +47167,9 @@ diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c }; MODULE_DEVICE_TABLE(of, aspeed_adc_matches); diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig ---- a/drivers/irqchip/Kconfig 2026-04-08 18:03:23.262162447 +0000 -+++ b/drivers/irqchip/Kconfig 2026-04-08 18:03:34.708953665 +0000 -@@ -734,6 +734,9 @@ +--- a/drivers/irqchip/Kconfig 2026-07-17 08:44:32.490658666 +0000 ++++ b/drivers/irqchip/Kconfig 2026-07-17 08:44:41.644516879 +0000 +@@ -735,6 +735,9 @@ Support for the Apple Interrupt Controller found on Apple Silicon SoCs, such as the M1. @@ -45754,8 +47180,8 @@ diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig bool "Microchip External Interrupt Controller" depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile ---- a/drivers/irqchip/Makefile 2026-04-08 18:03:23.262162447 +0000 -+++ b/drivers/irqchip/Makefile 2026-04-08 18:03:39.587864679 +0000 +--- a/drivers/irqchip/Makefile 2026-07-17 08:44:32.490658666 +0000 ++++ b/drivers/irqchip/Makefile 2026-07-17 08:44:46.842436761 +0000 @@ -85,6 +85,8 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o @@ -45767,7 +47193,7 @@ diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile obj-$(CONFIG_IRQ_UNIPHIER_AIDET) += irq-uniphier-aidet.o diff --git a/drivers/irqchip/irq-aspeed-e2m-ic.c b/drivers/irqchip/irq-aspeed-e2m-ic.c --- a/drivers/irqchip/irq-aspeed-e2m-ic.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/irqchip/irq-aspeed-e2m-ic.c 2026-04-08 18:03:48.342704735 +0000 ++++ b/drivers/irqchip/irq-aspeed-e2m-ic.c 2026-07-17 08:44:55.322306057 +0000 @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -45949,8 +47375,8 @@ diff --git a/drivers/irqchip/irq-aspeed-e2m-ic.c b/drivers/irqchip/irq-aspeed-e2 + aspeed_ast2700_e2m_ic_of_init); diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc.c --- a/drivers/irqchip/irq-aspeed-intc.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/irqchip/irq-aspeed-intc.c 2026-04-08 18:03:48.349704608 +0000 -@@ -0,0 +1,188 @@ ++++ b/drivers/irqchip/irq-aspeed-intc.c 2026-07-17 08:44:55.329305949 +0000 +@@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Aspeed Interrupt Controller. @@ -45959,14 +47385,19 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + */ + +#include ++#include +#include +#include -+#include +#include ++#include ++#include ++#include +#include +#include +#include ++#include +#include ++#include + +#define INTC_INT_ENABLE_REG 0x00 +#define INTC_INT_STATUS_REG 0x04 @@ -45977,62 +47408,71 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + void __iomem *base; + raw_spinlock_t intc_lock; + struct irq_domain *irq_domain; ++ struct device_node *node; ++ unsigned int parent_irqs[INTC_IRQS_PER_WORD]; ++ unsigned int parent_irq_count; ++ struct kobject kobj; ++ struct list_head list; +}; + -+static void aspeed_intc0_ic_irq_handler(struct irq_desc *desc) ++static LIST_HEAD(aspeed_intc_instances); ++ ++/* ++ * INTC0 uses a 1:1 mapping between each parent GIC SPI line and one leaf ++ * interrupt bit. The leaf hwirq is derived directly from the parent GIC ++ * hwirq number, so no status register scan is needed (contrast with intc1 ++ * which aggregates multiple sources behind a single parent line). ++ */ ++static irqreturn_t aspeed_intc0_ic_irq_handler(int irq, void *dev_id) +{ -+ struct aspeed_intc_ic *intc_ic = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ struct irq_data *irq_data = irq_desc_get_irq_data(desc); ++ struct aspeed_intc_ic *intc_ic = dev_id; ++ struct irq_data *irq_data = irq_get_irq_data(irq); + unsigned long hwirq; + + if (!irq_data || !intc_ic) { + pr_err("Invalid irq_data or intc_ic\n"); -+ return; ++ return IRQ_NONE; + } + + if (irq_data->hwirq < INTC_IRQ_BASE + 32) { + pr_err("Invalid hwirq: %lu\n", irq_data->hwirq); -+ return; ++ return IRQ_NONE; + } + hwirq = irq_data->hwirq - INTC_IRQ_BASE - 32; /* 32 is SPI offset */ + -+ chained_irq_enter(chip, desc); -+ + generic_handle_domain_irq(intc_ic->irq_domain, hwirq); + + /* -+ * TODO: This a WA to prevnet potential race conditions when -+ * multiple interrupts are processed in multi-core environment. ++ * Serialize the status-clear to prevent potential race conditions ++ * when multiple interrupts are processed in a multi-core environment. + */ + raw_spin_lock(&intc_ic->intc_lock); + writel(BIT(hwirq), intc_ic->base + INTC_INT_STATUS_REG); + raw_spin_unlock(&intc_ic->intc_lock); + -+ chained_irq_exit(chip, desc); ++ return IRQ_HANDLED; +} + -+static void aspeed_intc1_ic_irq_handler(struct irq_desc *desc) ++static irqreturn_t aspeed_intc1_ic_irq_handler(int irq, void *dev_id) +{ -+ struct aspeed_intc_ic *intc_ic = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct aspeed_intc_ic *intc_ic = dev_id; + unsigned long bit, status; + + if (!intc_ic) { + pr_err("Invalid intc_ic\n"); -+ return; ++ return IRQ_NONE; + } + -+ chained_irq_enter(chip, desc); -+ + status = readl(intc_ic->base + INTC_INT_STATUS_REG); ++ if (!status) ++ return IRQ_NONE; + + for_each_set_bit(bit, &status, INTC_IRQS_PER_WORD) { + generic_handle_domain_irq(intc_ic->irq_domain, bit); + writel(BIT(bit), intc_ic->base + INTC_INT_STATUS_REG); + } + -+ chained_irq_exit(chip, desc); ++ return IRQ_HANDLED; +} + +static void aspeed_intc_irq_mask(struct irq_data *data) @@ -46055,10 +47495,21 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + writel(unmask, intc_ic->base + INTC_INT_ENABLE_REG); +} + ++static void aspeed_intc_irq_print_chip(struct irq_data *data, struct seq_file *p) ++{ ++ struct aspeed_intc_ic *intc_ic = irq_data_get_irq_chip_data(data); ++ ++ if (intc_ic && intc_ic->node) ++ seq_printf(p, " %pOF", intc_ic->node); ++ else ++ seq_puts(p, " ASPEED INTC"); ++} ++ +static struct irq_chip aspeed_intc_chip = { + .name = "ASPEED INTC", + .irq_mask = aspeed_intc_irq_mask, + .irq_unmask = aspeed_intc_irq_unmask, ++ .irq_print_chip = aspeed_intc_irq_print_chip, +}; + +static int aspeed_intc_ic_map_irq_domain(struct irq_domain *domain, unsigned int irq, @@ -46074,12 +47525,68 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + .map = aspeed_intc_ic_map_irq_domain, +}; + ++static ssize_t irq_groups_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ struct aspeed_intc_ic *intc = ++ container_of(kobj, struct aspeed_intc_ic, kobj); ++ ssize_t len = 0; ++ ++ if (intc->parent_irq_count == 1) { ++ /* Aggregated: one parent line serves all children. */ ++ len += sysfs_emit_at(buf, len, "group 0: parent_irq=%u members=", ++ intc->parent_irqs[0]); ++ for (int hwirq = 0; hwirq < INTC_IRQS_PER_WORD; hwirq++) { ++ unsigned int virq = irq_find_mapping(intc->irq_domain, hwirq); ++ ++ if (virq) ++ len += sysfs_emit_at(buf, len, "%u ", virq); ++ } ++ len += sysfs_emit_at(buf, len, "\n"); ++ } else { ++ /* 1:1: each parent serves one child (derived from GIC hwirq). */ ++ for (unsigned int i = 0; i < intc->parent_irq_count; i++) { ++ struct irq_data *pdata = irq_get_irq_data(intc->parent_irqs[i]); ++ unsigned int virq; ++ unsigned long phw; ++ ++ if (!pdata) ++ continue; ++ phw = pdata->hwirq; ++ if (phw < INTC_IRQ_BASE + 32) ++ continue; ++ virq = irq_find_mapping(intc->irq_domain, ++ phw - INTC_IRQ_BASE - 32); ++ if (!virq) ++ continue; ++ len += sysfs_emit_at(buf, len, ++ "group %u: parent_irq=%u members=%u\n", ++ i, intc->parent_irqs[i], virq); ++ } ++ } ++ return len; ++} ++ ++static struct kobj_attribute aspeed_intc_irq_groups_attr = __ATTR_RO(irq_groups); ++ ++static struct attribute *aspeed_intc_attrs[] = { ++ &aspeed_intc_irq_groups_attr.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(aspeed_intc); ++ ++static const struct kobj_type aspeed_intc_kobj_type = { ++ .sysfs_ops = &kobj_sysfs_ops, ++ .default_groups = aspeed_intc_groups, ++}; ++ +static int __init aspeed_intc_ic_of_init(struct device_node *node, + struct device_node *parent) +{ + struct aspeed_intc_ic *intc_ic; ++ irq_handler_t handler; + int ret = 0; -+ int irq, irq_count, i; ++ int irq, irq_count = 0, i; + + intc_ic = kzalloc(sizeof(*intc_ic), GFP_KERNEL); + if (!intc_ic) @@ -46091,11 +47598,28 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + ret = -ENOMEM; + goto err_free_ic; + } ++ intc_ic->node = node; + writel(0xffffffff, intc_ic->base + INTC_INT_STATUS_REG); + writel(0x0, intc_ic->base + INTC_INT_ENABLE_REG); + -+ intc_ic->irq_domain = irq_domain_add_linear(node, INTC_IRQS_PER_WORD, -+ &aspeed_intc_ic_irq_domain_ops, intc_ic); ++ irq_count = of_irq_count(node); ++ if (irq_count == 0) { ++ pr_err("Failed to get irq count\n"); ++ ret = -EINVAL; ++ goto err_iounmap; ++ } ++ ++ if (irq_count > INTC_IRQS_PER_WORD) { ++ pr_err("Too many parent IRQs: %d\n", irq_count); ++ ret = -EINVAL; ++ goto err_iounmap; ++ } ++ ++ intc_ic->irq_domain = ++ irq_domain_create_linear(of_fwnode_handle(node), ++ INTC_IRQS_PER_WORD, ++ &aspeed_intc_ic_irq_domain_ops, ++ intc_ic); + if (!intc_ic->irq_domain) { + ret = -ENOMEM; + goto err_iounmap; @@ -46103,12 +47627,10 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + + raw_spin_lock_init(&intc_ic->intc_lock); + -+ irq_count = of_irq_count(node); -+ if (irq_count == 0) { -+ pr_err("Failed to get irq count\n"); -+ ret = -EINVAL; -+ goto err_iounmap; -+ } ++ if (irq_count > 1) ++ handler = aspeed_intc0_ic_irq_handler; ++ else ++ handler = aspeed_intc1_ic_irq_handler; + + for (i = 0; i < irq_count; i++) { + irq = irq_of_parse_and_map(node, i); @@ -46117,31 +47639,74 @@ diff --git a/drivers/irqchip/irq-aspeed-intc.c b/drivers/irqchip/irq-aspeed-intc + ret = -EINVAL; + goto err_iounmap; + } else { -+ if (irq_count > 1) -+ irq_set_chained_handler_and_data(irq, aspeed_intc0_ic_irq_handler, intc_ic); -+ else -+ irq_set_chained_handler_and_data(irq, aspeed_intc1_ic_irq_handler, intc_ic); ++ ret = request_irq(irq, handler, IRQF_NO_THREAD, ++ "aspeed-intc", intc_ic); ++ if (ret) { ++ pr_err("Failed to request IRQ %d\n", irq); ++ irq_dispose_mapping(irq); ++ goto err_iounmap; ++ } ++ ++ intc_ic->parent_irqs[intc_ic->parent_irq_count++] = irq; + } + } + ++ list_add_tail(&intc_ic->list, &aspeed_intc_instances); ++ + return 0; + +err_iounmap: -+ for (i = 0; i < irq_count; i++) { -+ irq = irq_of_parse_and_map(node, i); -+ if (irq) -+ irq_dispose_mapping(irq); ++ for (i = 0; i < intc_ic->parent_irq_count; i++) { ++ free_irq(intc_ic->parent_irqs[i], intc_ic); ++ irq_dispose_mapping(intc_ic->parent_irqs[i]); + } ++ if (intc_ic->irq_domain) ++ irq_domain_remove(intc_ic->irq_domain); + iounmap(intc_ic->base); +err_free_ic: + kfree(intc_ic); + return ret; +} + ++/* ++ * IRQCHIP_DECLARE callbacks fire from init_IRQ(), which runs before ++ * kernel_kobj is created in core_initcall(ksysfs_init). Defer the sysfs ++ * setup until kernel_kobj is available so /sys/kernel/aspeed-intc-* can be ++ * created. The physical base address is appended to disambiguate multiple ++ * controllers that share a basename (e.g. nested LTPI controllers). ++ */ ++static int __init aspeed_intc_sysfs_init(void) ++{ ++ struct aspeed_intc_ic *intc; ++ struct resource res; ++ int ret; ++ ++ list_for_each_entry(intc, &aspeed_intc_instances, list) { ++ ret = of_address_to_resource(intc->node, 0, &res); ++ if (ret) { ++ pr_warn("Failed to get resource for %pOF: %d\n", ++ intc->node, ret); ++ continue; ++ } ++ ++ ret = kobject_init_and_add(&intc->kobj, &aspeed_intc_kobj_type, ++ kernel_kobj, "aspeed-intc-%pOFn@%llx", ++ intc->node, ++ (unsigned long long)res.start); ++ if (ret) { ++ pr_warn("Failed to create sysfs entry for %pOF: %d\n", ++ intc->node, ret); ++ kobject_put(&intc->kobj); ++ } ++ } ++ return 0; ++} ++late_initcall(aspeed_intc_sysfs_init); ++ +IRQCHIP_DECLARE(ast2700_intc_ic, "aspeed,ast2700-intc-ic", aspeed_intc_ic_of_init); diff --git a/drivers/irqchip/irq-aspeed-scu-ic.c b/drivers/irqchip/irq-aspeed-scu-ic.c ---- a/drivers/irqchip/irq-aspeed-scu-ic.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/irqchip/irq-aspeed-scu-ic.c 2026-04-08 18:03:48.352704553 +0000 +--- a/drivers/irqchip/irq-aspeed-scu-ic.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/irqchip/irq-aspeed-scu-ic.c 2026-07-17 08:44:55.333305888 +0000 @@ -1,61 +1,76 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* @@ -46512,7 +48077,7 @@ diff --git a/drivers/irqchip/irq-aspeed-scu-ic.c b/drivers/irqchip/irq-aspeed-sc +IRQCHIP_DECLARE(ast2700_scu_ic3, "aspeed,ast2700-scu-ic3", aspeed_scu_ic_of_init); diff --git a/drivers/irqchip/irq-ast2700-intc0.c b/drivers/irqchip/irq-ast2700-intc0.c --- a/drivers/irqchip/irq-ast2700-intc0.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/irqchip/irq-ast2700-intc0.c 2026-04-08 18:03:48.199707348 +0000 ++++ b/drivers/irqchip/irq-ast2700-intc0.c 2026-07-17 08:44:55.177308292 +0000 @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* @@ -47025,7 +48590,7 @@ diff --git a/drivers/irqchip/irq-ast2700-intc0.c b/drivers/irqchip/irq-ast2700-i +IRQCHIP_DECLARE(ast2700_intc0_ic, "aspeed,ast2700-intc0-ic", aspeed_intc0_ic_of_init); diff --git a/drivers/irqchip/irq-ast2700-intc1.c b/drivers/irqchip/irq-ast2700-intc1.c --- a/drivers/irqchip/irq-ast2700-intc1.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/irqchip/irq-ast2700-intc1.c 2026-04-08 18:03:48.193707458 +0000 ++++ b/drivers/irqchip/irq-ast2700-intc1.c 2026-07-17 08:44:55.170308400 +0000 @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* @@ -47283,7 +48848,7 @@ diff --git a/drivers/irqchip/irq-ast2700-intc1.c b/drivers/irqchip/irq-ast2700-i +IRQCHIP_DECLARE(ast2700_intc1_ic, "aspeed,ast2700-intc1-ic", aspeed_intc1_ic_of_init); diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig --- a/drivers/jtag/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/Kconfig 2026-04-08 18:03:44.029783536 +0000 ++++ b/drivers/jtag/Kconfig 2026-07-17 08:44:51.099371147 +0000 @@ -0,0 +1,31 @@ +menuconfig JTAG + tristate "JTAG support" @@ -47318,14 +48883,14 @@ diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig + be called jtag-aspeed. diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile --- a/drivers/jtag/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/Makefile 2026-04-08 18:03:44.029783536 +0000 ++++ b/drivers/jtag/Makefile 2026-07-17 08:44:51.099371147 +0000 @@ -0,0 +1,2 @@ +obj-$(CONFIG_JTAG) += jtag.o +obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c --- a/drivers/jtag/jtag-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/jtag-aspeed.c 2026-04-08 18:03:48.170707878 +0000 -@@ -0,0 +1,1657 @@ ++++ b/drivers/jtag/jtag-aspeed.c 2026-07-17 08:44:55.147308755 +0000 +@@ -0,0 +1,1692 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Mellanox Technologies. All rights reserved. +// Copyright (c) 2018 Oleksandr Shamray @@ -47334,6 +48899,7 @@ diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c +#include +#include +#include ++#include +#include +#include +#include @@ -47490,6 +49056,9 @@ diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c +/* Use this macro to set us delay for JTAG Master Controller to be programmed */ +#define AST26XX_JTAG_CTRL_UDELAY 2 + ++/* Timeout (us) for a GBLCTRL FIFO mode/reset state change to settle */ ++#define ASPEED_JTAG_FIFO_POLL_TIMEOUT_US 1000 ++ +#define DEBUG_JTAG + +static const char * const regnames[] = { @@ -48464,6 +50033,7 @@ diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c + u32 shift_bits; + u32 data_reg; + u32 reg_val; ++ u32 poll_val; + enum jtag_tapstate shift; + enum jtag_tapstate exit; + enum jtag_tapstate exitx; @@ -48548,14 +50118,44 @@ diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c + partial_xfer = partial_xfer_size; + + reg_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_GBLCTRL); ++ ++ /* ++ * The FIFO must only be reset while it is in CPU mode; ++ * asserting RESET_FIFO in controller mode corrupts the data ++ * of subsequent transfers. Whether the FIFO is in CPU or ++ * controller mode is decided by the hardware itself and ++ * FIFO_CTRL_MODE is read-only, so wait for the hardware to ++ * report CPU mode (0) before asserting RESET_FIFO. ++ */ ++ ret = read_poll_timeout(aspeed_jtag_read, poll_val, ++ !(poll_val & ASPEED_JTAG_GBLCTRL_FIFO_CTRL_MODE), ++ 0, ASPEED_JTAG_FIFO_POLL_TIMEOUT_US, false, ++ aspeed_jtag, ASPEED_JTAG_GBLCTRL); ++ if (ret) { ++ dev_err(aspeed_jtag->dev, ++ "timed out waiting for FIFO to return to CPU mode\n"); ++ return ret; ++ } ++ ++ /* CPU mode confirmed; it is now safe to reset the FIFO. */ + aspeed_jtag_write(aspeed_jtag, reg_val | + ASPEED_JTAG_GBLCTRL_RESET_FIFO, + ASPEED_JTAG_GBLCTRL); + -+ /* Switch internal FIFO into CPU mode */ -+ reg_val = reg_val & ~BIT(24); -+ aspeed_jtag_write(aspeed_jtag, reg_val, -+ ASPEED_JTAG_GBLCTRL); ++ /* ++ * RESET_FIFO is self-clearing; wait for it to read back as 0 ++ * so the FIFO is guaranteed flushed before we start loading ++ * data into it. ++ */ ++ ret = read_poll_timeout(aspeed_jtag_read, poll_val, ++ !(poll_val & ASPEED_JTAG_GBLCTRL_RESET_FIFO), ++ 0, ASPEED_JTAG_FIFO_POLL_TIMEOUT_US, false, ++ aspeed_jtag, ASPEED_JTAG_GBLCTRL); ++ if (ret) { ++ dev_err(aspeed_jtag->dev, ++ "timed out waiting for FIFO reset to clear\n"); ++ return ret; ++ } + + while (partial_xfer) { + if (partial_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) @@ -48985,7 +50585,7 @@ diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c +MODULE_LICENSE("GPL v2"); diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c --- a/drivers/jtag/jtag.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/jtag/jtag.c 2026-04-08 18:03:44.029783536 +0000 ++++ b/drivers/jtag/jtag.c 2026-07-17 08:44:51.100371132 +0000 @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018 Mellanox Technologies. All rights reserved. @@ -49375,8 +50975,8 @@ diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c +MODULE_DESCRIPTION("Generic jtag support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig ---- a/drivers/mailbox/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/mailbox/Kconfig 2026-04-08 18:03:34.959949087 +0000 +--- a/drivers/mailbox/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/mailbox/Kconfig 2026-07-17 08:44:41.884513180 +0000 @@ -295,4 +295,12 @@ acts as an interrupt controller for receiving interrupts from clients. Say Y here if you want to build this driver. @@ -49391,8 +50991,8 @@ diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig + endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile ---- a/drivers/mailbox/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/mailbox/Makefile 2026-04-08 18:03:39.858859736 +0000 +--- a/drivers/mailbox/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/mailbox/Makefile 2026-07-17 08:44:47.118432507 +0000 @@ -64,3 +64,5 @@ obj-$(CONFIG_QCOM_CPUCP_MBOX) += qcom-cpucp-mbox.o @@ -49401,7 +51001,7 @@ diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile +obj-$(CONFIG_AST2700_MBOX) += ast2700-mailbox.o diff --git a/drivers/mailbox/ast2700-mailbox.c b/drivers/mailbox/ast2700-mailbox.c --- a/drivers/mailbox/ast2700-mailbox.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/mailbox/ast2700-mailbox.c 2026-04-08 18:03:48.220706964 +0000 ++++ b/drivers/mailbox/ast2700-mailbox.c 2026-07-17 08:44:55.199307953 +0000 @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* @@ -49639,8 +51239,8 @@ diff --git a/drivers/mailbox/ast2700-mailbox.c b/drivers/mailbox/ast2700-mailbox +MODULE_DESCRIPTION("ASPEED AST2700 IPC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c ---- a/drivers/media/platform/aspeed/aspeed-video.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/media/platform/aspeed/aspeed-video.c 2026-04-08 18:03:48.318705174 +0000 +--- a/drivers/media/platform/aspeed/aspeed-video.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/media/platform/aspeed/aspeed-video.c 2026-07-17 08:44:55.298306427 +0000 @@ -4,6 +4,7 @@ #include @@ -50452,12 +52052,12 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + get_box = true; + } + } ++ ++ return get_box ? IRQ_WAKE_THREAD : IRQ_HANDLED; ++} - if (test_bit(VIDEO_STREAMING, &video->flags) && !empty) - aspeed_video_start_frame(video); -+ return get_box ? IRQ_WAKE_THREAD : IRQ_HANDLED; -+} -+ +static irqreturn_t aspeed_video_md_irq(int irq, void *arg) +{ + struct aspeed_video *video = arg; @@ -50620,15 +52220,16 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo v4l2_dbg(1, debug, &video->v4l2_dev, "Got resolution: %dx%d\n", det->width, det->height); -@@ -1130,6 +1620,7 @@ +@@ -1130,6 +1620,8 @@ { struct v4l2_bt_timings *act = &video->active_timings; unsigned int size = act->width * ALIGN(act->height, 8); -+ bool is_sync_mode_ok = (video->version != 7); ++ bool is_sync_mode_ok = (video->version != 7) || ++ (video->version == 7 && video->id == 0); /* Set capture/compression frame sizes */ aspeed_video_calc_compressed_size(video, size); -@@ -1156,7 +1647,8 @@ +@@ -1156,7 +1648,8 @@ aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ @@ -50638,7 +52239,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Sync Mode\n"); aspeed_video_write(video, VE_TGS_0, FIELD_PREP(VE_TGS_FIRST, -@@ -1170,41 +1662,50 @@ +@@ -1170,41 +1663,50 @@ aspeed_video_update(video, VE_CTRL, VE_CTRL_INT_DE | VE_CTRL_DIRECT_FETCH, VE_CTRL_INT_DE); @@ -50716,7 +52317,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static void aspeed_video_update_regs(struct aspeed_video *video) -@@ -1219,6 +1720,8 @@ +@@ -1219,6 +1721,8 @@ u32 ctrl = 0; u32 seq_ctrl = 0; @@ -50725,7 +52326,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo v4l2_dbg(1, debug, &video->v4l2_dev, "framerate(%d)\n", video->frame_rate); v4l2_dbg(1, debug, &video->v4l2_dev, "jpeg format(%s) subsample(%s)\n", -@@ -1234,14 +1737,26 @@ +@@ -1234,14 +1738,35 @@ else aspeed_video_update(video, VE_BCD_CTRL, VE_BCD_CTRL_EN_BCD, 0); @@ -50734,6 +52335,15 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + + if (video->input == VIDEO_INPUT_DVI) + ctrl |= VE_CTRL_SOURCE; ++ ++ if (video->version == 7) { ++ if (video->input == VIDEO_INPUT_DVI) ++ ctrl |= FIELD_PREP(VE_CTRL_CLK_DELAY, VIDEO_CLK_CRT2); ++ else if (video->input == VIDEO_INPUT_VGA && video->id == 0) ++ ctrl |= FIELD_PREP(VE_CTRL_CLK_DELAY, VIDEO_CLK_D1); ++ else ++ ctrl |= FIELD_PREP(VE_CTRL_CLK_DELAY, VIDEO_CLK_48MHz); ++ } + if (video->frame_rate) ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate); @@ -50752,18 +52362,18 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo if (video->yuv420) seq_ctrl |= VE_SEQ_CTRL_YUV420; -@@ -1252,7 +1767,9 @@ +@@ -1252,7 +1777,9 @@ aspeed_video_update(video, VE_SEQ_CTRL, video->jpeg_mode | VE_SEQ_CTRL_YUV420, seq_ctrl); - aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC, ctrl); + aspeed_video_update(video, VE_CTRL, + VE_CTRL_FRC | VE_CTRL_AUTO_OR_CURSOR | -+ VE_CTRL_SOURCE, ctrl); ++ VE_CTRL_SOURCE | VE_CTRL_CLK_DELAY, ctrl); aspeed_video_update(video, VE_COMP_CTRL, VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR | VE_COMP_CTRL_EN_HQ | VE_COMP_CTRL_HQ_DCT_LUM | -@@ -1278,8 +1795,16 @@ +@@ -1278,8 +1805,18 @@ aspeed_video_write(video, VE_COMP_OFFSET, 0); aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma); @@ -50774,13 +52384,24 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + if (video->version == 7) { + if (video->input == VIDEO_INPUT_DVI) + ctrl |= FIELD_PREP(VE_CTRL_CLK_DELAY, VIDEO_CLK_CRT2); ++ else if (video->input == VIDEO_INPUT_VGA && video->id == 0) ++ ctrl |= FIELD_PREP(VE_CTRL_CLK_DELAY, VIDEO_CLK_D1); + else + ctrl |= FIELD_PREP(VE_CTRL_CLK_DELAY, VIDEO_CLK_48MHz); + } aspeed_video_write(video, VE_CTRL, ctrl); aspeed_video_write(video, VE_COMP_CTRL, VE_COMP_CTRL_RSVD); -@@ -1311,12 +1836,7 @@ +@@ -1303,6 +1840,8 @@ + + static void aspeed_video_start(struct aspeed_video *video) + { ++ aspeed_video_set_vga_on(video, true); ++ + aspeed_video_on(video); + + aspeed_video_init_regs(video); +@@ -1311,30 +1850,18 @@ aspeed_video_get_resolution(video); /* Set timings since the device is being opened for the first time */ @@ -50794,7 +52415,11 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static void aspeed_video_stop(struct aspeed_video *video) -@@ -1326,15 +1846,6 @@ + { ++ aspeed_video_set_vga_on(video, false); ++ + set_bit(VIDEO_STOPPED, &video->flags); + cancel_delayed_work_sync(&video->res_work); aspeed_video_off(video); @@ -50810,7 +52435,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; video->flags = 0; } -@@ -1342,10 +1853,14 @@ +@@ -1342,10 +1869,14 @@ static int aspeed_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { @@ -50825,7 +52450,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo return 0; } -@@ -1383,7 +1898,8 @@ +@@ -1383,7 +1914,8 @@ switch (f->fmt.pix.pixelformat) { case V4L2_PIX_FMT_JPEG: @@ -50835,7 +52460,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo break; case V4L2_PIX_FMT_AJPG: video->format = VIDEO_FMT_ASPEED; -@@ -1401,10 +1917,10 @@ +@@ -1401,10 +1933,10 @@ { struct aspeed_video *video = video_drvdata(file); @@ -50848,7 +52473,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo inp->type = V4L2_INPUT_TYPE_CAMERA; inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; inp->status = video->v4l2_input_status; -@@ -1414,16 +1930,111 @@ +@@ -1414,15 +1946,110 @@ static int aspeed_video_get_input(struct file *file, void *fh, unsigned int *i) { @@ -50866,8 +52491,8 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + struct aspeed_video *video = video_drvdata(file); + + if (i >= VIDEO_INPUT_MAX) - return -EINVAL; - ++ return -EINVAL; ++ + if (IS_ERR(video->scu)) { + v4l2_dbg(1, debug, &video->v4l2_dev, "%s: scu isn't ready for input-control\n", __func__); + return -EINVAL; @@ -50875,7 +52500,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + + if (IS_ERR(video->gfx) && i == VIDEO_INPUT_GFX) { + v4l2_dbg(1, debug, &video->v4l2_dev, "%s: gfx isn't ready for GFX input\n", __func__); -+ return -EINVAL; + return -EINVAL; + } + + // prepare memory space for user to put test batch @@ -50958,11 +52583,10 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo + + if (video->input == VIDEO_INPUT_MEM) + aspeed_video_start_frame(video); -+ + return 0; } - -@@ -1520,6 +2131,12 @@ +@@ -1520,6 +2147,12 @@ { struct aspeed_video *video = video_drvdata(file); @@ -50975,7 +52599,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo if (timings->bt.width == video->active_timings.width && timings->bt.height == video->active_timings.height) return 0; -@@ -1527,13 +2144,7 @@ +@@ -1527,13 +2160,7 @@ if (vb2_is_busy(&video->queue)) return -EBUSY; @@ -50990,7 +52614,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo timings->type = V4L2_DV_BT_656_1120; -@@ -1589,6 +2200,37 @@ +@@ -1589,6 +2216,37 @@ NULL, NULL); } @@ -51028,7 +52652,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo static int aspeed_video_dv_timings_cap(struct file *file, void *fh, struct v4l2_dv_timings_cap *cap) { -@@ -1641,6 +2283,8 @@ +@@ -1641,6 +2299,8 @@ .vidioc_enum_dv_timings = aspeed_video_enum_dv_timings, .vidioc_dv_timings_cap = aspeed_video_dv_timings_cap, @@ -51037,7 +52661,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo .vidioc_subscribe_event = aspeed_video_sub_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -@@ -1710,6 +2354,9 @@ +@@ -1710,6 +2370,9 @@ struct delayed_work *dwork = to_delayed_work(work); struct aspeed_video *video = container_of(dwork, struct aspeed_video, res_work); @@ -51047,7 +52671,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_on(video); -@@ -1723,8 +2370,14 @@ +@@ -1723,8 +2386,14 @@ aspeed_video_get_resolution(video); @@ -51064,7 +52688,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo static const struct v4l2_event ev = { .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, -@@ -1740,6 +2393,32 @@ +@@ -1740,6 +2409,32 @@ done: clear_bit(VIDEO_RES_CHANGE, &video->flags); wake_up_interruptible_all(&video->wait); @@ -51097,7 +52721,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } static int aspeed_video_open(struct file *file) -@@ -1785,7 +2464,7 @@ +@@ -1785,7 +2480,7 @@ .read = vb2_fop_read, .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, @@ -51106,12 +52730,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo .open = aspeed_video_open, .release = aspeed_video_release, }; -@@ -1827,16 +2506,22 @@ - int rc; - struct aspeed_video *video = vb2_get_drv_priv(q); - -+ aspeed_video_set_vga_on(video, true); -+ +@@ -1830,13 +2525,17 @@ video->sequence = 0; video->perf.duration_max = 0; video->perf.duration_min = 0xffffffff; @@ -51133,16 +52752,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } set_bit(VIDEO_STREAMING, &video->flags); -@@ -1848,6 +2533,8 @@ - int rc; - struct aspeed_video *video = vb2_get_drv_priv(q); - -+ aspeed_video_set_vga_on(video, false); -+ - clear_bit(VIDEO_STREAMING, &video->flags); - - rc = wait_event_timeout(video->wait, -@@ -1860,8 +2547,7 @@ +@@ -1860,8 +2559,7 @@ * Need to force stop any DMA and try and get HW into a good * state for future calls to start streaming again. */ @@ -51152,7 +52762,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_init_regs(video); -@@ -1885,7 +2571,8 @@ +@@ -1885,7 +2583,8 @@ spin_unlock_irqrestore(&video->lock, flags); if (test_bit(VIDEO_STREAMING, &video->flags) && @@ -51162,7 +52772,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_start_frame(video); } -@@ -1911,6 +2598,7 @@ +@@ -1911,6 +2610,7 @@ val08 = aspeed_video_read(v, VE_CTRL); if (FIELD_GET(VE_CTRL_DIRECT_FETCH, val08)) { seq_printf(s, " %-20s:\tDirect fetch\n", "Mode"); @@ -51170,7 +52780,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo seq_printf(s, " %-20s:\t%s\n", "VGA bpp mode", FIELD_GET(VE_CTRL_INT_DE, val08) ? "16" : "32"); } else { -@@ -1962,19 +2650,19 @@ +@@ -1962,19 +2662,19 @@ } DEFINE_SHOW_ATTRIBUTE(aspeed_video_debugfs); @@ -51197,7 +52807,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo } #else static void aspeed_video_debugfs_remove(struct aspeed_video *video) { } -@@ -2028,6 +2716,8 @@ +@@ -2028,6 +2728,8 @@ vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vbq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; vbq->dev = v4l2_dev->dev; @@ -51206,7 +52816,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo vbq->lock = &video->video_lock; vbq->ops = &aspeed_video_vb2_ops; vbq->mem_ops = &vb2_dma_contig_memops; -@@ -2070,11 +2760,30 @@ +@@ -2070,11 +2772,30 @@ return 0; } @@ -51237,7 +52847,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo irq = irq_of_parse_and_map(dev->of_node, 0); if (!irq) { -@@ -2082,14 +2791,36 @@ +@@ -2082,14 +2803,36 @@ return -ENODEV; } @@ -51276,7 +52886,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->eclk = devm_clk_get(dev, "eclk"); if (IS_ERR(video->eclk)) { dev_err(dev, "Unable to get ECLK\n"); -@@ -2111,22 +2842,53 @@ +@@ -2111,22 +2854,53 @@ if (rc) goto err_unprepare_eclk; @@ -51336,7 +52946,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); -@@ -2134,6 +2896,9 @@ +@@ -2134,6 +2908,9 @@ err_release_reserved_mem: of_reserved_mem_device_release(dev); @@ -51346,7 +52956,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo clk_unprepare(video->vclk); err_unprepare_eclk: clk_unprepare(video->eclk); -@@ -2145,6 +2910,7 @@ +@@ -2145,6 +2922,7 @@ { .compatible = "aspeed,ast2400-video-engine", .data = &ast2400_config }, { .compatible = "aspeed,ast2500-video-engine", .data = &ast2500_config }, { .compatible = "aspeed,ast2600-video-engine", .data = &ast2600_config }, @@ -51354,7 +52964,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo {} }; MODULE_DEVICE_TABLE(of, aspeed_video_of_match); -@@ -2154,11 +2920,16 @@ +@@ -2154,11 +2932,16 @@ const struct aspeed_video_config *config; struct aspeed_video *video; int rc; @@ -51371,7 +52981,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(video->base)) return PTR_ERR(video->base); -@@ -2167,8 +2938,10 @@ +@@ -2167,8 +2950,10 @@ if (!config) return -ENODEV; @@ -51382,7 +52992,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo video->frame_rate = 30; video->jpeg_hq_quality = 1; -@@ -2178,14 +2951,31 @@ +@@ -2178,14 +2963,31 @@ init_waitqueue_head(&video->wait); INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); INIT_LIST_HEAD(&video->buffers); @@ -51415,7 +53025,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo clk_unprepare(video->vclk); clk_unprepare(video->eclk); return rc; -@@ -2193,6 +2983,9 @@ +@@ -2193,6 +2995,9 @@ aspeed_video_debugfs_create(video); @@ -51425,7 +53035,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo return 0; } -@@ -2204,8 +2997,12 @@ +@@ -2204,8 +3009,12 @@ aspeed_video_off(video); @@ -51438,7 +53048,7 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo clk_unprepare(video->vclk); clk_unprepare(video->eclk); -@@ -2215,7 +3012,7 @@ +@@ -2215,7 +3024,7 @@ v4l2_device_unregister(v4l2_dev); @@ -51448,8 +53058,8 @@ diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platfo of_reserved_mem_device_release(dev); } diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c ---- a/drivers/mmc/host/sdhci-of-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/mmc/host/sdhci-of-aspeed.c 2026-04-08 18:03:47.802714601 +0000 +--- a/drivers/mmc/host/sdhci-of-aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/mmc/host/sdhci-of-aspeed.c 2026-07-17 08:44:54.708315521 +0000 @@ -11,8 +11,10 @@ #include #include @@ -51470,12 +53080,13 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe #define ASPEED_SDC_S1_PHASE_OUT GENMASK(15, 11) #define ASPEED_SDC_S1_PHASE_IN_EN BIT(10) #define ASPEED_SDC_S1_PHASE_OUT_EN GENMASK(9, 8) -@@ -31,61 +35,52 @@ +@@ -31,61 +35,53 @@ #define ASPEED_SDC_S0_PHASE_OUT_EN GENMASK(1, 0) #define ASPEED_SDC_PHASE_MAX 31 +#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK BIT(4) +#define ASPEED_SDHCI_NR_TAPS 15 ++#define ASPEED_SDHCI_MAX_SWEEPS 10 + /* SDIO{10,20} */ -#define ASPEED_SDC_CAP1_1_8V (0 * 32 + 26) @@ -51551,7 +53162,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe * 1 | CAP2_SDR104 | SDIO244 | SDIO24 */ static void aspeed_sdc_set_slot_capability(struct sdhci_host *host, struct aspeed_sdc *sdc, -@@ -125,222 +120,217 @@ +@@ -125,222 +121,307 @@ spin_unlock(&sdc->lock); } @@ -51678,38 +53289,89 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe + pltfm_priv = sdhci_priv(host); + aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv); + aspeed_sdc = aspeed_sdhci->parent; - -- taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg); -- taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg); --} ++ + if (!IS_ERR(aspeed_sdc->rst)) { + for (i = 0; i < ARRAY_SIZE(reg_array); i++) + save_array[i] = sdhci_readl(host, reg_array[i]); ++ ++ tran_mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); ++ mmc8_mode = readl(aspeed_sdc->regs); ++ ++ reset_control_assert(aspeed_sdc->rst); ++ mdelay(1); ++ reset_control_deassert(aspeed_sdc->rst); ++ mdelay(1); ++ ++ for (i = 0; i < ARRAY_SIZE(reg_array); i++) ++ sdhci_writel(host, save_array[i], reg_array[i]); ++ ++ sdhci_writew(host, tran_mode, SDHCI_TRANSFER_MODE); ++ writel(mmc8_mode, aspeed_sdc->regs); + +- taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg); +- taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg); ++ sdhci_set_clock(host, host->clock); ++ } ++ ++ sdhci_reset(host, mask); + } -static void -aspeed_sdhci_configure_phase(struct sdhci_host *host, unsigned long rate) --{ ++/* ++ * aspeed_sdhci_find_center_tap - find the midpoint of the widest passing window ++ * @pass: boolean array [0..total-1], true = tuning passed at this position ++ * @total: number of phase positions (ASPEED_SDHCI_NR_TAPS * 2 = 30) ++ * ++ * Phase space layout (15 taps = half cycle at 200 MHz, ~167 ps/tap): ++ * pos [0..14] - rising edge, tap delay 0..14 ( 0° .. ~168°) ++ * pos [15..29] - falling edge, tap delay 0..14 (180° .. ~348°) ++ * ++ * The array is treated as circular so windows that wrap across the ++ * rising/falling boundary (around 0°/360°) are detected correctly. ++ * ++ * Returns a 5-bit phase_bits value: ++ * bits [3:0] = tap index ++ * bit [4] = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK (set for falling edge) ++ */ ++static u32 aspeed_sdhci_find_center_tap(const bool *pass, int total) + { - struct aspeed_sdhci_tap_param _taps = {0}, *taps = &_taps; - struct mmc_clk_phase *params; - struct aspeed_sdhci *sdhci; - struct device *dev; -+ tran_mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); -+ mmc8_mode = readl(aspeed_sdc->regs); ++ int pos, cur_start = -1, cur_len = 0, best_start = 0, best_len = 0; ++ int center_pos; - dev = mmc_dev(host->mmc); - sdhci = sdhci_pltfm_priv(sdhci_priv(host)); -+ reset_control_assert(aspeed_sdc->rst); -+ mdelay(1); -+ reset_control_deassert(aspeed_sdc->rst); -+ mdelay(1); ++ /* ++ * Double-scan the circular buffer: the second pass detects windows ++ * that straddle the index-0 wrap-around boundary. The window length ++ * is capped at 'total' so a full-circle window is counted only once. ++ */ ++ for (pos = 0; pos < total * 2; pos++) { ++ if (pass[pos % total]) { ++ if (cur_start < 0) ++ cur_start = pos; ++ if (++cur_len > total) ++ cur_len = total; ++ if (cur_len > best_len) { ++ best_len = cur_len; ++ best_start = cur_start; ++ } ++ } else { ++ cur_start = -1; ++ cur_len = 0; ++ } ++ } - if (!sdhci->phase_desc) - return; -+ for (i = 0; i < ARRAY_SIZE(reg_array); i++) -+ sdhci_writel(host, save_array[i], reg_array[i]); ++ if (!best_len) ++ return 0; + -+ sdhci_writew(host, tran_mode, SDHCI_TRANSFER_MODE); -+ writel(mmc8_mode, aspeed_sdc->regs); ++ center_pos = (best_start + best_len / 2) % total; - params = &sdhci->phase_map.phase[host->timing]; - aspeed_sdhci_phases_to_taps(dev, rate, params, taps); @@ -51719,10 +53381,10 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe - taps->in & ASPEED_SDHCI_NR_TAPS, - taps->out & ASPEED_SDHCI_NR_TAPS, - params->in_deg, params->out_deg, rate, host->timing); -+ sdhci_set_clock(host, host->clock); -+ } ++ if (center_pos < ASPEED_SDHCI_NR_TAPS) ++ return (u32)center_pos; + -+ sdhci_reset(host, mask); ++ return (u32)(center_pos - ASPEED_SDHCI_NR_TAPS) | ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; } -static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) @@ -51734,32 +53396,74 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe struct aspeed_sdhci *sdhci; - int div; - u16 clk; -- -- pltfm_host = sdhci_priv(host); -- sdhci = sdhci_pltfm_priv(pltfm_host); + struct aspeed_sdc *sdc; + struct device *dev; ++ /* ++ * 15 taps cover half a 200 MHz clock cycle (2.5 ns ≈ 167 ps/tap). ++ * Rising and falling edges together span a full cycle: 30 positions. ++ */ ++ const int total = ASPEED_SDHCI_NR_TAPS * 2; ++ bool pass[ASPEED_SDHCI_NR_TAPS * 2]; ++ u32 enable_mask, base, val, phase_bits; ++ int pos, pass_count, sweep; -- parent = clk_get_rate(pltfm_host->clk); -+ u32 val, left, right, edge; -+ u32 window, oldwindow = 0, center; -+ u32 in_phase, out_phase, enable_mask, inverted = 0; - -- sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); +- pltfm_host = sdhci_priv(host); +- sdhci = sdhci_pltfm_priv(pltfm_host); + dev = mmc_dev(host->mmc); + pltfm_priv = sdhci_priv(host); + sdhci = sdhci_pltfm_priv(pltfm_priv); + sdc = sdhci->parent; +- parent = clk_get_rate(pltfm_host->clk); ++ enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN | ASPEED_SDC_S0_PHASE_IN_EN; ++ base = readl(sdc->regs + ASPEED_SDC_PHASE) & ASPEED_SDC_S0_PHASE_OUT; + +- sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ /* ++ * For each phase direction (input then output), sweep all 30 ++ * positions. If every position passes, the result is ++ * untrustworthy — all phases are not really reliable but we ++ * cannot detect where the unreliable ones are. Re-run the ++ * sweep up to ASPEED_SDHCI_MAX_SWEEPS times, skipping ++ * already-failed taps, until some fail and reveal the true ++ * window. ++ */ + - if (clock == 0) - return; -+ out_phase = readl(sdc->regs + ASPEED_SDC_PHASE) & ASPEED_SDC_S0_PHASE_OUT; ++ /* -------- Step 1: scan all input-phase positions -------- */ ++ for (pos = 0; pos < total; pos++) ++ pass[pos] = true; ++ ++ for (sweep = 0; sweep < ASPEED_SDHCI_MAX_SWEEPS; sweep++) { ++ for (pos = 0; pos < total; pos++) { ++ if (!pass[pos]) ++ continue; ++ ++ phase_bits = (pos < ASPEED_SDHCI_NR_TAPS) ++ ? (u32)pos ++ : (u32)(pos - ASPEED_SDHCI_NR_TAPS) | ++ ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; ++ ++ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_IN_SHIFT); ++ writel(val, sdc->regs + ASPEED_SDC_PHASE); ++ ++ if (mmc_send_tuning(host->mmc, opcode, NULL)) ++ pass[pos] = false; ++ ++ dev_dbg(dev, "in pos=%2d (%s tap%2u) %s\n", pos, ++ pos < ASPEED_SDHCI_NR_TAPS ? "rise" : "fall", ++ pos % ASPEED_SDHCI_NR_TAPS, ++ pass[pos] ? "pass" : "fail"); ++ } - if (WARN_ON(clock > host->max_clk)) - clock = host->max_clk; -+ enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN | ASPEED_SDC_S0_PHASE_IN_EN; ++ for (pos = 0, pass_count = 0; pos < total; pos++) ++ if (pass[pos]) ++ pass_count++; - /* +- /* - * Regarding the AST2600: - * - * If (EMMC12C[7:6], EMMC12C[15:8] == 0) then @@ -51774,103 +53478,106 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe - * Keep the one-hot behaviour for backwards compatibility except for - * supporting the value 0 in (EMMC12C[7:6], EMMC12C[15:8]), and capture - * the 0-value capability in clk_div_start. -+ * There are two window upon clock rising and falling edge. -+ * Iterate each tap delay to find the valid window and choose the -+ * bigger one, set the tap delay at the middle of window. - */ +- */ - for (div = sdhci->pdata->clk_div_start; div < 256; div *= 2) { - bus = parent / div; - if (bus <= clock) -- break; -+ for (edge = 0; edge < 2; edge++) { -+ if (edge == 1) -+ inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; ++ dev_dbg(dev, "input phase: sweep %d, %d/%d passed\n", ++ sweep, pass_count, total); ++ ++ if (pass_count < total || !pass_count) + break; + } + +- div >>= 1; ++ if (!pass_count) ++ dev_warn(dev, "input phase: no positions passed tuning\n"); + +- clk = div << SDHCI_DIVIDER_SHIFT; ++ phase_bits = aspeed_sdhci_find_center_tap(pass, total); ++ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_IN_SHIFT); ++ writel(val, sdc->regs + ASPEED_SDC_PHASE); ++ dev_dbg(dev, "input center: phase_bits=0x%02x reg=0x%08x\n", phase_bits, val); + -+ val = (out_phase | enable_mask | (inverted << ASPEED_SDC_S0_PHASE_IN_SHIFT)); ++ /* -------- Step 2: scan all output-phase positions -------- */ ++ base = val & ~ASPEED_SDC_S0_PHASE_OUT; + -+ /* find the left boundary */ -+ for (left = 0; left < ASPEED_SDHCI_NR_TAPS + 1; left++) { -+ in_phase = val | (left << ASPEED_SDC_S0_PHASE_IN_SHIFT); -+ writel(in_phase, sdc->regs + ASPEED_SDC_PHASE); ++ for (pos = 0; pos < total; pos++) ++ pass[pos] = true; + -+ if (!mmc_send_tuning(host->mmc, opcode, NULL)) -+ break; -+ } ++ for (sweep = 0; sweep < ASPEED_SDHCI_MAX_SWEEPS; sweep++) { ++ for (pos = 0; pos < total; pos++) { ++ if (!pass[pos]) ++ continue; + -+ /* find the right boundary */ -+ for (right = left + 1; right < ASPEED_SDHCI_NR_TAPS + 1; right++) { -+ in_phase = val | (right << ASPEED_SDC_S0_PHASE_IN_SHIFT); -+ writel(in_phase, sdc->regs + ASPEED_SDC_PHASE); ++ phase_bits = (pos < ASPEED_SDHCI_NR_TAPS) ++ ? (u32)pos ++ : (u32)(pos - ASPEED_SDHCI_NR_TAPS) | ++ ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; + -+ if (mmc_send_tuning(host->mmc, opcode, NULL)) -+ break; -+ } ++ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_OUT_SHIFT); ++ writel(val, sdc->regs + ASPEED_SDC_PHASE); + -+ window = right - left; -+ pr_debug("tuning window[%d][%d~%d] = %d\n", edge, left, right, window); ++ if (mmc_send_tuning(host->mmc, opcode, NULL)) ++ pass[pos] = false; + -+ if (window > oldwindow) { -+ oldwindow = window; -+ center = (((right - 1) + left) / 2) | inverted; ++ dev_dbg(dev, "out pos=%2d (%s tap%2u) %s\n", pos, ++ pos < ASPEED_SDHCI_NR_TAPS ? "rise" : "fall", ++ pos % ASPEED_SDHCI_NR_TAPS, ++ pass[pos] ? "pass" : "fail"); + } - } - -- div >>= 1; -+ val = (out_phase | enable_mask | (center << ASPEED_SDC_S0_PHASE_IN_SHIFT)); -+ writel(val, sdc->regs + ASPEED_SDC_PHASE); - -- clk = div << SDHCI_DIVIDER_SHIFT; -+ pr_debug("input tuning result=%x\n", val); - aspeed_sdhci_configure_phase(host, bus); -+ inverted = 0; -+ out_phase = val & ~ASPEED_SDC_S0_PHASE_OUT; -+ in_phase = out_phase; -+ oldwindow = 0; ++ for (pos = 0, pass_count = 0; pos < total; pos++) ++ if (pass[pos]) ++ pass_count++; - sdhci_enable_clk(host, clk); -} -+ for (edge = 0; edge < 2; edge++) { -+ if (edge == 1) -+ inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; ++ dev_dbg(dev, "output phase: sweep %d, %d/%d passed\n", ++ sweep, pass_count, total); -static unsigned int aspeed_sdhci_get_max_clock(struct sdhci_host *host) -{ - if (host->mmc->f_max) - return host->mmc->f_max; -+ val = (in_phase | enable_mask | (inverted << ASPEED_SDC_S0_PHASE_OUT_SHIFT)); ++ if (pass_count < total || !pass_count) ++ break; ++ } ++ ++ if (!pass_count) ++ dev_warn(dev, "output phase: no positions passed tuning\n"); ++ ++ phase_bits = aspeed_sdhci_find_center_tap(pass, total); ++ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_OUT_SHIFT); ++ writel(val, sdc->regs + ASPEED_SDC_PHASE); ++ dev_dbg(dev, "output center: phase_bits=0x%02x reg=0x%08x\n", phase_bits, val); - return sdhci_pltfm_clk_get_max_clock(host); --} -+ /* find the left boundary */ -+ for (left = 0; left < ASPEED_SDHCI_NR_TAPS + 1; left++) { -+ out_phase = val | (left << ASPEED_SDC_S0_PHASE_OUT_SHIFT); -+ writel(out_phase, sdc->regs + ASPEED_SDC_PHASE); ++ return mmc_send_tuning(host->mmc, opcode, NULL); + } -static void aspeed_sdhci_set_bus_width(struct sdhci_host *host, int width) --{ -- struct sdhci_pltfm_host *pltfm_priv; ++static void aspeed_sdhci_voltage_switch(struct sdhci_host *host) + { + struct sdhci_pltfm_host *pltfm_priv; - struct aspeed_sdhci *aspeed_sdhci; - struct aspeed_sdc *aspeed_sdc; - u8 ctrl; -+ if (!mmc_send_tuning(host->mmc, opcode, NULL)) -+ break; -+ } ++ struct aspeed_sdhci *sdhci; ++ struct aspeed_sdc *sdc; ++ u32 out_phase, enable_mask, inverted = 1; -- pltfm_priv = sdhci_priv(host); + pltfm_priv = sdhci_priv(host); - aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv); - aspeed_sdc = aspeed_sdhci->parent; -+ /* find the right boundary */ -+ for (right = left + 1; right < ASPEED_SDHCI_NR_TAPS + 1; right++) { -+ out_phase = val | (right << ASPEED_SDC_S0_PHASE_OUT_SHIFT); -+ writel(out_phase, sdc->regs + ASPEED_SDC_PHASE); ++ sdhci = sdhci_pltfm_priv(pltfm_priv); ++ sdc = sdhci->parent; - /* Set/clear 8-bit mode */ - aspeed_sdc_configure_8bit_mode(aspeed_sdc, aspeed_sdhci, - width == MMC_BUS_WIDTH_8); -+ if (mmc_send_tuning(host->mmc, opcode, NULL)) -+ break; -+ } ++ enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN | ASPEED_SDC_S0_PHASE_IN_EN; - /* Set/clear 1 or 4 bit mode */ - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); @@ -51880,32 +53587,21 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe - ctrl &= ~SDHCI_CTRL_4BITBUS; - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); -} -+ window = right - left; -+ pr_debug("tuning window[%d][%d~%d] = %d\n", edge, left, right, window); ++ inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; ++ out_phase = readl(sdc->regs + ASPEED_SDC_PHASE) & ASPEED_SDC_S0_PHASE_OUT; ++ out_phase |= (inverted << ASPEED_SDC_S0_PHASE_OUT_SHIFT); -static u32 aspeed_sdhci_readl(struct sdhci_host *host, int reg) -{ - u32 val = readl(host->ioaddr + reg); -+ if (window > oldwindow) { -+ oldwindow = window; -+ center = (((right - 1) + left) / 2) | inverted; -+ } -+ } ++ /* output inverse */ ++ writel(out_phase | enable_mask, sdc->regs + ASPEED_SDC_PHASE); - if (unlikely(reg == SDHCI_PRESENT_STATE) && - (host->mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)) - val ^= SDHCI_CARD_PRESENT; -+ val = (in_phase | enable_mask | (center << ASPEED_SDC_S0_PHASE_OUT_SHIFT)); -+ writel(val, sdc->regs + ASPEED_SDC_PHASE); - +- - return val; -+ pr_debug("output tuning result=%x\n", val); -+ -+ return mmc_send_tuning(host->mmc, opcode, NULL); -+} -+ -+static void aspeed_sdhci_voltage_switch(struct sdhci_host *host) -+{ + mdelay(30); } @@ -51931,7 +53627,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe }; static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev, -@@ -372,12 +362,6 @@ +@@ -372,12 +453,6 @@ int slot; int ret; @@ -51944,7 +53640,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev)); if (IS_ERR(host)) return PTR_ERR(host); -@@ -395,14 +379,6 @@ +@@ -395,14 +470,6 @@ else if (slot >= 2) return -EINVAL; @@ -51959,7 +53655,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe dev->width_mask = !slot ? ASPEED_SDC_S0_MMC8 : ASPEED_SDC_S1_MMC8; dev_info(&pdev->dev, "Configured for slot %d\n", slot); -@@ -413,11 +389,13 @@ +@@ -413,11 +480,13 @@ of_property_read_bool(np, "sd-uhs-sdr104")) { aspeed_sdc_set_slot_capability(host, dev->parent, ASPEED_SDC_CAP1_1_8V, true, slot); @@ -51976,7 +53672,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe } pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); -@@ -434,9 +412,6 @@ +@@ -434,9 +503,6 @@ if (ret) goto err_sdhci_add; @@ -51986,7 +53682,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe ret = sdhci_add_host(host); if (ret) goto err_sdhci_add; -@@ -469,45 +444,11 @@ +@@ -469,45 +535,11 @@ .clk_div_start = 2, }; @@ -52035,7 +53731,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe { } }; MODULE_DEVICE_TABLE(of, aspeed_sdhci_of_match); -@@ -522,11 +463,22 @@ +@@ -522,11 +554,22 @@ .remove_new = aspeed_sdhci_remove, }; @@ -52058,7 +53754,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe int ret; sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL); -@@ -535,6 +487,23 @@ +@@ -535,6 +578,23 @@ spin_lock_init(&sdc->lock); @@ -52082,7 +53778,7 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe sdc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sdc->clk)) return PTR_ERR(sdc->clk); -@@ -579,15 +548,6 @@ +@@ -579,15 +639,6 @@ clk_disable_unprepare(sdc->clk); } @@ -52099,8 +53795,8 @@ diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspe .driver = { .name = "sd-controller-aspeed", diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig ---- a/drivers/net/can/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/can/Kconfig 2026-04-08 18:03:33.980966943 +0000 +--- a/drivers/net/can/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/net/can/Kconfig 2026-07-17 08:44:40.965527345 +0000 @@ -66,6 +66,12 @@ if CAN_NETLINK @@ -52115,8 +53811,8 @@ diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig bool "CAN bit-timing calculation" default y diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile ---- a/drivers/net/can/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/can/Makefile 2026-04-08 18:03:38.662881549 +0000 +--- a/drivers/net/can/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/net/can/Makefile 2026-07-17 08:44:45.917451019 +0000 @@ -15,6 +15,7 @@ obj-y += usb/ obj-y += softing/ @@ -52127,8 +53823,8 @@ diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile obj-$(CONFIG_CAN_CAN327) += can327.o diff --git a/drivers/net/can/aspeed_can.c b/drivers/net/can/aspeed_can.c --- a/drivers/net/can/aspeed_can.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/net/can/aspeed_can.c 2026-04-08 18:03:48.110708974 +0000 -@@ -0,0 +1,1720 @@ ++++ b/drivers/net/can/aspeed_can.c 2026-07-17 08:44:55.084309726 +0000 +@@ -0,0 +1,1728 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* ASPEED CAN device driver + * @@ -52738,6 +54434,14 @@ diff --git a/drivers/net/can/aspeed_can.c b/drivers/net/can/aspeed_can.c + + for (i = 0; i < 0x50; i += 4) + netdev_info(ndev, "REG(%03x): 0x%08x\n", i, readl(priv->reg_base + i)); ++ ++ netdev_info(ndev, "TX_MIRROR\n"); ++ for (i = 0; i < 0x30; i += 4) ++ netdev_info(ndev, "(0x%02x): 0x%08x\n", i, readl(priv->reg_base + CAN_TBUF_MIRROR + i)); ++ ++ netdev_info(ndev, "RX_BUF\n"); ++ for (i = 0; i < 0x30; i += 4) ++ netdev_info(ndev, "(0x%02x): 0x%08x\n", i, readl(priv->reg_base + CAN_RBUF + i)); +} + +static int aspeed_can_set_bittiming(struct net_device *ndev) @@ -53850,8 +55554,8 @@ diff --git a/drivers/net/can/aspeed_can.c b/drivers/net/can/aspeed_can.c +MODULE_AUTHOR("Chin-Ting Kuo "); +MODULE_DESCRIPTION("ASPEED CAN interface"); diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig ---- a/drivers/net/ethernet/faraday/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/ethernet/faraday/Kconfig 2026-04-08 18:03:48.332704918 +0000 +--- a/drivers/net/ethernet/faraday/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/net/ethernet/faraday/Kconfig 2026-07-17 08:44:55.312306211 +0000 @@ -6,7 +6,7 @@ config NET_VENDOR_FARADAY bool "Faraday devices" @@ -53876,8 +55580,8 @@ diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday help This driver supports the FTGMAC100 Gigabit Ethernet controller diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c ---- a/drivers/net/ethernet/faraday/ftgmac100.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/ethernet/faraday/ftgmac100.c 2026-04-08 18:03:48.332704918 +0000 +--- a/drivers/net/ethernet/faraday/ftgmac100.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/net/ethernet/faraday/ftgmac100.c 2026-07-17 08:44:55.313306196 +0000 @@ -9,6 +9,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -53921,14 +55625,20 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far }; static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr) -@@ -148,6 +157,23 @@ +@@ -148,6 +157,29 @@ { u32 maccr = 0; -+ /* RMII needs SCU reset to clear status */ -+ if (priv->netdev->phydev->interface == PHY_INTERFACE_MODE_RMII) { ++ /* Aspeed SoC needs SCU reset */ ++ if (priv->is_aspeed) { + int err; + ++ err = phy_reset(priv->sgmii); ++ if (err) { ++ dev_err(priv->dev, "Failed to reset sgmii\n"); ++ return err; ++ } ++ + err = reset_control_assert(priv->rst); + if (err) { + dev_err(priv->dev, "Failed to reset mac (%d)\n", err); @@ -53945,7 +55655,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far switch (priv->cur_speed) { case SPEED_10: case 0: /* no link */ -@@ -265,10 +291,12 @@ +@@ -265,10 +297,12 @@ iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR); /* Setup RX ring buffer base */ @@ -53960,7 +55670,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Configure RX buffer size */ iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE), -@@ -321,6 +349,7 @@ +@@ -321,6 +355,7 @@ static void ftgmac100_start_hw(struct ftgmac100 *priv) { u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); @@ -53968,7 +55678,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Keep the original GMAC and FAST bits */ maccr &= (FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_GIGA_MODE); -@@ -349,6 +378,10 @@ +@@ -349,12 +384,18 @@ if (priv->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) maccr |= FTGMAC100_MACCR_RM_VLAN; @@ -53979,7 +55689,15 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Hit the HW */ iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); } -@@ -425,7 +458,9 @@ + + static void ftgmac100_stop_hw(struct ftgmac100 *priv) + { ++ phy_exit(priv->sgmii); ++ + iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR); + } + +@@ -425,7 +466,9 @@ priv->rx_skbs[entry] = skb; /* Store DMA address into RX desc */ @@ -53990,7 +55708,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Ensure the above is ordered vs clearing the OWN bit */ dma_wmb(); -@@ -551,7 +586,8 @@ +@@ -551,7 +594,8 @@ csum_vlan & 0xffff); /* Tear down DMA mapping, do necessary cache management */ @@ -54000,7 +55718,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far #if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU) /* When we don't have an iommu, we can save cycles by not -@@ -628,9 +664,12 @@ +@@ -628,9 +672,12 @@ struct ftgmac100_txdes *txdes, u32 ctl_stat) { @@ -54014,7 +55732,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far if (ctl_stat & FTGMAC100_TXDES0_FTS) { len = skb_headlen(skb); dma_unmap_single(priv->dev, map, len, DMA_TO_DEVICE); -@@ -784,7 +823,9 @@ +@@ -784,7 +831,9 @@ f_ctl_stat |= FTGMAC100_TXDES0_FTS; if (nfrags == 0) f_ctl_stat |= FTGMAC100_TXDES0_LTS; @@ -54025,7 +55743,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far txdes->txdes1 = cpu_to_le32(csum_vlan); /* Next descriptor */ -@@ -812,7 +853,9 @@ +@@ -812,7 +861,9 @@ ctl_stat |= FTGMAC100_TXDES0_LTS; txdes->txdes0 = cpu_to_le32(ctl_stat); txdes->txdes1 = 0; @@ -54036,7 +55754,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far /* Next one */ pointer = ftgmac100_next_tx_pointer(priv, pointer); -@@ -887,7 +930,10 @@ +@@ -887,7 +938,10 @@ for (i = 0; i < priv->rx_q_entries; i++) { struct ftgmac100_rxdes *rxdes = &priv->rxdes[i]; struct sk_buff *skb = priv->rx_skbs[i]; @@ -54048,7 +55766,61 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far if (!skb) continue; -@@ -986,7 +1032,9 @@ +@@ -944,19 +998,19 @@ + priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *), + GFP_KERNEL); + if (!priv->tx_skbs) +- goto err_free_rx_skbs; ++ return -ENOMEM; + + /* Allocate descriptors */ + priv->rxdes = dma_alloc_coherent(priv->dev, + MAX_RX_QUEUE_ENTRIES * sizeof(struct ftgmac100_rxdes), + &priv->rxdes_dma, GFP_KERNEL); + if (!priv->rxdes) +- goto err_free_tx_skbs; ++ return -ENOMEM; + priv->txdes = dma_alloc_coherent(priv->dev, + MAX_TX_QUEUE_ENTRIES * sizeof(struct ftgmac100_txdes), + &priv->txdes_dma, GFP_KERNEL); + if (!priv->txdes) +- goto err_free_rxdes; ++ return -ENOMEM; + + /* Allocate scratch packet buffer */ + priv->rx_scratch = dma_alloc_coherent(priv->dev, +@@ -964,29 +1018,9 @@ + &priv->rx_scratch_dma, + GFP_KERNEL); + if (!priv->rx_scratch) +- goto err_free_txdes; ++ return -ENOMEM; + + return 0; +- +-err_free_txdes: +- dma_free_coherent(priv->dev, +- MAX_TX_QUEUE_ENTRIES * +- sizeof(struct ftgmac100_txdes), +- priv->txdes, priv->txdes_dma); +- priv->txdes = NULL; +-err_free_rxdes: +- dma_free_coherent(priv->dev, +- MAX_RX_QUEUE_ENTRIES * +- sizeof(struct ftgmac100_rxdes), +- priv->rxdes, priv->rxdes_dma); +- priv->rxdes = NULL; +-err_free_tx_skbs: +- kfree(priv->tx_skbs); +- priv->tx_skbs = NULL; +-err_free_rx_skbs: +- kfree(priv->rx_skbs); +- priv->rx_skbs = NULL; +- return -ENOMEM; + } + + static void ftgmac100_init_rings(struct ftgmac100 *priv) +@@ -1006,7 +1040,9 @@ for (i = 0; i < priv->rx_q_entries; i++) { rxdes = &priv->rxdes[i]; rxdes->rxdes0 = 0; @@ -54059,414 +55831,86 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far } /* Mark the end of the ring */ rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask); -@@ -1730,16 +1778,21 @@ - static void ftgmac100_phy_disconnect(struct net_device *netdev) - { - struct ftgmac100 *priv = netdev_priv(netdev); -+ struct phy_device *phydev = netdev->phydev; - -- if (!netdev->phydev) -- return; -+ if (priv->sgmii) { -+ phy_exit(priv->sgmii); -+ devm_phy_put(priv->dev, priv->sgmii); -+ } - -- phy_disconnect(netdev->phydev); -- if (of_phy_is_fixed_link(priv->dev->of_node)) -- of_phy_deregister_fixed_link(priv->dev->of_node); -+ if (phydev) { -+ phy_disconnect(phydev); -+ if (of_phy_is_fixed_link(priv->dev->of_node)) -+ of_phy_deregister_fixed_link(priv->dev->of_node); - -- if (priv->use_ncsi) -- fixed_phy_unregister(netdev->phydev); -+ if (priv->use_ncsi) -+ fixed_phy_unregister(phydev); -+ } - } - - static void ftgmac100_destroy_mdio(struct net_device *netdev) -@@ -1812,12 +1865,377 @@ - return ret; +@@ -1318,6 +1354,44 @@ + return work_done; } -+static int ftgmac100_get_ast2600_rgmii_flag(u32 delay) ++static int ftgmac100_init_sgmii(struct ftgmac100 *priv) +{ -+ if ((delay > 500 && delay < 1500) || -+ (delay > 2500 && delay < 7500)) -+ return AST2600_RGMII_KEEP_DELAY; -+ -+ return AST2600_RGMII_DIS_DELAY; -+} -+ -+static int ftgmac100_check_ast2600_rgmii_delay(struct regmap *scu, -+ u32 delay_unit, -+ int mac_id, int dly_reg) -+{ -+ u32 delay_value; -+ u32 tx_delay; -+ u32 rx_delay; -+ int tx_flag; -+ int rx_flag; -+ -+ regmap_read(scu, dly_reg, &delay_value); -+ if (mac_id == 0 || mac_id == 2) { -+ tx_delay = FIELD_GET(ASPEED_MAC0_2_TX_DLY, delay_value); -+ rx_delay = FIELD_GET(ASPEED_MAC0_2_RX_DLY, delay_value); -+ } else { -+ tx_delay = FIELD_GET(ASPEED_MAC1_3_TX_DLY, delay_value); -+ rx_delay = FIELD_GET(ASPEED_MAC1_3_RX_DLY, delay_value); -+ } -+ -+ /* Due to the hardware design reason, for MAC2/3 on AST2600, -+ * the zero delay ns on RX is configured by setting value 0x1a. -+ * List as below: -+ * 0x1a, 0x1b, ... , 0x1f, 0x00, 0x01, ... , 0x19 -+ * Covert for calculation purpose. -+ * 0x00, 0x01, ... , 0x19, 0x1a, 0x1b, ... , 0x1f -+ */ -+ if (mac_id == 2 || mac_id == 3) -+ rx_delay = (rx_delay + 0x06) & 0x1f; -+ -+ tx_delay *= delay_unit; -+ rx_delay *= delay_unit; -+ -+ tx_flag = ftgmac100_get_ast2600_rgmii_flag(tx_delay); -+ rx_flag = ftgmac100_get_ast2600_rgmii_flag(rx_delay); -+ -+ if (tx_flag == AST2600_RGMII_KEEP_DELAY || -+ rx_flag == AST2600_RGMII_KEEP_DELAY) { -+ return AST2600_RGMII_KEEP_DELAY; -+ } -+ -+ return AST2600_RGMII_DIS_DELAY; -+} -+ -+static int ftgmac100_set_ast2600_rgmii_delay(struct ftgmac100 *priv, -+ s32 rgmii_tx_delay, -+ s32 rgmii_rx_delay, -+ phy_interface_t *phy_intf) -+{ -+ struct device *dev = priv->dev; -+ struct device_node *np; -+ u32 rgmii_delay_unit; -+ u32 rx_delay_index; -+ u32 tx_delay_index; -+ struct regmap *scu; -+ int dly_mask; -+ int dly_reg; -+ int mac_id; ++ struct device_node *fixed_link_node = NULL; + int err; + -+ np = dev->of_node; -+ -+ err = of_get_phy_mode(np, phy_intf); ++ err = phy_init(priv->sgmii); + if (err) { -+ dev_err(priv->dev, "Failed to get phy mode: %d\n", err); ++ dev_err(priv->dev, "Failed to initialize sgmii\n"); + return err; + } + -+ scu = syscon_regmap_lookup_by_phandle(np, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(dev, "failed to get aspeed,scu"); -+ return PTR_ERR(scu); -+ } ++ /* If using fixed link in dts, sgmii need to be forced */ ++ fixed_link_node = of_get_child_by_name(priv->dev->of_node, "fixed-link"); ++ if (fixed_link_node) { ++ int speed; + -+ /* According to the register base address to specify the corresponding -+ * values. -+ */ -+ switch (priv->res->start) { -+ case AST2600_MAC0_BASE_ADDR: -+ mac_id = 0; -+ rgmii_delay_unit = AST2600_MAC01_CLK_DLY_UNIT; -+ dly_reg = AST2600_MAC01_CLK_DLY; -+ break; -+ case AST2600_MAC1_BASE_ADDR: -+ mac_id = 1; -+ rgmii_delay_unit = AST2600_MAC01_CLK_DLY_UNIT; -+ dly_reg = AST2600_MAC01_CLK_DLY; -+ break; -+ case AST2600_MAC2_BASE_ADDR: -+ mac_id = 2; -+ rgmii_delay_unit = AST2600_MAC23_CLK_DLY_UNIT; -+ dly_reg = AST2600_MAC23_CLK_DLY; -+ break; -+ case AST2600_MAC3_BASE_ADDR: -+ mac_id = 3; -+ rgmii_delay_unit = AST2600_MAC23_CLK_DLY_UNIT; -+ dly_reg = AST2600_MAC23_CLK_DLY; -+ break; -+ default: -+ dev_err(dev, "Invalid mac base address"); -+ return -EINVAL; -+ } -+ -+ if (of_phy_is_fixed_link(np)) { -+ if (rgmii_tx_delay < 0 || rgmii_rx_delay < 0) { -+ dev_err(dev, -+ "Add rx/tx-internal-delay-ps for fixed-link\n"); -+ /* Keep original RGMII delay value*/ -+ return 0; ++ if (of_property_read_u32(fixed_link_node, "speed", &speed)) { ++ dev_err(priv->dev, "Failed to read speed from fixed-link node\n"); ++ err = -EINVAL; ++ goto err_out; + } + -+ /* Must have both of rx/tx-internal-delay-ps for fixed-link */ -+ goto conf_delay; -+ } -+ -+ if (*phy_intf == PHY_INTERFACE_MODE_RGMII_RXID || -+ *phy_intf == PHY_INTERFACE_MODE_RGMII_TXID) -+ goto out_warn; -+ -+ if (*phy_intf != PHY_INTERFACE_MODE_RGMII && -+ *phy_intf != PHY_INTERFACE_MODE_RGMII_ID) -+ return 0; -+ -+ /* Both rx/tx-internal-delay-ps are not existed. */ -+ if (rgmii_tx_delay < 0 && rgmii_rx_delay < 0) { -+ int flag; -+ -+ flag = ftgmac100_check_ast2600_rgmii_delay(scu, -+ rgmii_delay_unit, -+ mac_id, -+ dly_reg); -+ if (flag == AST2600_RGMII_KEEP_DELAY) -+ goto out_warn; -+ -+ if (*phy_intf == PHY_INTERFACE_MODE_RGMII) { -+ dev_err(dev, "Update phy-mode to 'rgmii-id'\n"); -+ /* Forced phy interface to RGMII_ID and MAC will disable -+ * RGMII delay. -+ */ -+ *phy_intf = PHY_INTERFACE_MODE_RGMII_ID; -+ } -+ } else { -+ /* Please refer to ethernet-controller.yaml. */ -+ if (*phy_intf == PHY_INTERFACE_MODE_RGMII && -+ (rgmii_tx_delay == 2000 || rgmii_rx_delay == 2000)) { -+ dev_warn(dev, -+ "RX/TX delay cannot set to 2000 on 'rgmii'\n"); -+ return -EINVAL; -+ } -+ } -+ -+ /* The value is negative, which means the rx/tx-internal-delay-ps -+ * property is not existed in dts. Therefore, set to default 0. -+ */ -+ if (rgmii_tx_delay < 0) -+ rgmii_tx_delay = 0; -+ if (rgmii_rx_delay < 0) -+ rgmii_rx_delay = 0; -+ -+conf_delay: -+ tx_delay_index = DIV_ROUND_CLOSEST(rgmii_tx_delay, rgmii_delay_unit); -+ if (tx_delay_index >= 32) { -+ dev_err(dev, "The %u ps of TX delay is out of range\n", -+ rgmii_tx_delay); -+ return -EINVAL; -+ } -+ -+ rx_delay_index = DIV_ROUND_CLOSEST(rgmii_rx_delay, rgmii_delay_unit); -+ if (rx_delay_index >= 32) { -+ dev_err(dev, "The %u ps of RX delay is out of range\n", -+ rgmii_rx_delay); -+ return -EINVAL; -+ } -+ -+ /* Due to the hardware design reason, for MAC2/3 on AST2600, the zero -+ * delay ns on RX is configured by setting value 0x1a. -+ * List as below: -+ * 0x1a -> 0 ns, 0x1b -> 0.25 ns, ... , 0x1f -> 1.25 ns, -+ * 0x00 -> 1.5 ns, 0x01 -> 1.75 ns, ... , 0x19 -> 7.75 ns, 0x1a -> 0 ns -+ */ -+ if (mac_id == 2 || mac_id == 3) -+ rx_delay_index = (AST2600_MAC23_RX_DLY_0_NS + rx_delay_index) & -+ AST2600_MAC_TX_RX_DLY_MASK; -+ -+ if (mac_id == 0 || mac_id == 2) { -+ dly_mask = ASPEED_MAC0_2_TX_DLY | ASPEED_MAC0_2_RX_DLY; -+ tx_delay_index = FIELD_PREP(ASPEED_MAC0_2_TX_DLY, tx_delay_index); -+ rx_delay_index = FIELD_PREP(ASPEED_MAC0_2_RX_DLY, rx_delay_index); -+ } else { -+ dly_mask = ASPEED_MAC1_3_TX_DLY | ASPEED_MAC1_3_RX_DLY; -+ tx_delay_index = FIELD_PREP(ASPEED_MAC1_3_TX_DLY, tx_delay_index); -+ rx_delay_index = FIELD_PREP(ASPEED_MAC1_3_RX_DLY, rx_delay_index); -+ } -+ -+ regmap_update_bits(scu, dly_reg, dly_mask, tx_delay_index | rx_delay_index); -+ -+ return 0; -+ -+out_warn: -+ /* Print the warning message. Keep the phy-mode and the RGMII delay value. */ -+ dev_warn(dev, "Update phy-mode to 'rgmii-id' and add rx/tx-internal-delay-ps\n"); -+ -+ return 0; -+} -+ -+static int ftgmac100_set_ast2700_rgmii_delay(struct ftgmac100 *priv, -+ s32 rgmii_tx_delay, -+ s32 rgmii_rx_delay) -+{ -+ struct device *dev = priv->dev; -+ struct device_node *np; -+ u32 rgmii_delay_tx_unit, rgmii_delay_rx_unit; -+ u32 rgmii_delay_tx_dis, rgmii_delay_rx_dis; -+ u32 tx_delay_index, rx_delay_index; -+ u32 reg_unit, reg_val; -+ struct regmap *scu; -+ int dly_mask; -+ int mac_id; -+ -+ np = dev->of_node; -+ -+ scu = syscon_regmap_lookup_by_phandle(np, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(dev, "failed to get aspeed,scu"); -+ return PTR_ERR(scu); -+ } -+ -+ /* According to the register base address to specify the corresponding -+ * values. -+ */ -+ switch (priv->res->start) { -+ case AST2700_MAC0_BASE_ADDR: -+ mac_id = 0; -+ regmap_read(scu, AST2700_MAC0_DLY_UNIT, ®_unit); -+ regmap_read(scu, AST2700_MAC0_DLY_VAL, ®_val); -+ break; -+ case AST2700_MAC1_BASE_ADDR: -+ mac_id = 1; -+ regmap_read(scu, AST2700_MAC1_DLY_UNIT, ®_unit); -+ regmap_read(scu, AST2700_MAC1_DLY_VAL, ®_val); -+ break; -+ case AST2700_MAC2_BASE_ADDR: -+ /* Only support SGMII */ -+ return 0; -+ default: -+ dev_err(dev, "Invalid mac base address"); -+ return -EINVAL; -+ } -+ -+ rgmii_delay_tx_unit = AST2700_MAC_TX_DLY_UNIT(reg_unit); -+ rgmii_delay_rx_unit = AST2700_MAC_RX_DLY_UNIT(reg_unit); -+ rgmii_delay_tx_dis = AST2700_MAC_TX_DLY_DIS(reg_val); -+ rgmii_delay_rx_dis = AST2700_MAC_RX_DLY_DIS(reg_val); -+ -+ /* The value is negative, which means the rx/tx-internal-delay-ps -+ * property is not existed in dts. Therefore, set to default 0. -+ */ -+ if (rgmii_tx_delay < 0) -+ rgmii_tx_delay = 0; -+ if (rgmii_rx_delay < 0) -+ rgmii_rx_delay = 0; -+ -+ tx_delay_index = DIV_ROUND_CLOSEST(rgmii_tx_delay, rgmii_delay_tx_unit); -+ tx_delay_index += rgmii_delay_tx_dis; -+ if (tx_delay_index >= 32) { -+ dev_err(dev, "The %u ps of TX delay is out of range\n", -+ rgmii_tx_delay); -+ return -EINVAL; -+ } -+ -+ rx_delay_index = DIV_ROUND_CLOSEST(rgmii_rx_delay, rgmii_delay_rx_unit); -+ rx_delay_index += rgmii_delay_rx_dis; -+ if (rx_delay_index >= 32) { -+ dev_err(dev, "The %u ps of RX delay is out of range\n", -+ rgmii_rx_delay); -+ return -EINVAL; -+ } -+ -+ if (mac_id == 0) { -+ dly_mask = ASPEED_MAC0_2_TX_DLY | ASPEED_MAC0_2_RX_DLY; -+ tx_delay_index = FIELD_PREP(ASPEED_MAC0_2_TX_DLY, tx_delay_index); -+ rx_delay_index = FIELD_PREP(ASPEED_MAC0_2_RX_DLY, rx_delay_index); ++ err = phy_set_speed(priv->sgmii, speed); ++ if (err) ++ dev_err(priv->dev, "Failed to force sgmii speed\n"); + } else { -+ dly_mask = ASPEED_MAC1_3_TX_DLY | ASPEED_MAC1_3_RX_DLY; -+ tx_delay_index = FIELD_PREP(ASPEED_MAC1_3_TX_DLY, tx_delay_index); -+ rx_delay_index = FIELD_PREP(ASPEED_MAC1_3_RX_DLY, rx_delay_index); ++ err = phy_set_speed(priv->sgmii, 0); ++ if (err) ++ dev_err(priv->dev, "Failed to enable sgmii Nway\n"); + } + -+ regmap_update_bits(scu, AST2700_MAC01_CLK_DLY, dly_mask, -+ tx_delay_index | rx_delay_index); -+ -+ return 0; -+} -+ -+static int ftgmac100_set_internal_delay(struct ftgmac100 *priv, -+ phy_interface_t *phy_intf) -+{ -+ struct device_node *np = priv->dev->of_node; -+ s32 rgmii_tx_delay; -+ s32 rgmii_rx_delay; -+ int err = 0; -+ -+ /* NCSI mode is based on RMII, not neet to set delay for RMII */ -+ if (of_get_property(np, "use-ncsi", NULL)) -+ return 0; -+ -+ /* AST2600 needs to know if the "tx/rx-internal-delay-ps" properties -+ * are existed in dts. If not existed, set -1 and delay is equal to 0. -+ */ -+ if (of_property_read_u32(np, "tx-internal-delay-ps", &rgmii_tx_delay)) -+ rgmii_tx_delay = -1; -+ if (of_property_read_u32(np, "rx-internal-delay-ps", &rgmii_rx_delay)) -+ rgmii_rx_delay = -1; -+ -+ if ((of_device_is_compatible(np, "aspeed,ast2600-mac"))) -+ err = ftgmac100_set_ast2600_rgmii_delay(priv, -+ rgmii_tx_delay, -+ rgmii_rx_delay, -+ phy_intf); -+ else if ((of_device_is_compatible(np, "aspeed,ast2700-mac"))) -+ err = ftgmac100_set_ast2700_rgmii_delay(priv, -+ rgmii_tx_delay, -+ rgmii_rx_delay); ++err_out: ++ if (fixed_link_node) ++ of_node_put(fixed_link_node); + + return err; +} + -+static struct phy_device *ftgmac100_ast2600_phy_get(struct net_device *dev, -+ struct device_node *np, -+ void (*hndlr)(struct net_device *), -+ phy_interface_t phy_intf) -+{ -+ struct device_node *phy_np; -+ struct phy_device *phy; -+ int ret; -+ -+ if (of_phy_is_fixed_link(np)) { -+ ret = of_phy_register_fixed_link(np); -+ if (ret < 0) { -+ netdev_err(dev, "broken fixed-link specification\n"); -+ return NULL; -+ } -+ phy_np = of_node_get(np); -+ } else { -+ phy_np = of_parse_phandle(np, "phy-handle", 0); -+ if (!phy_np) -+ return NULL; -+ } -+ -+ phy = of_phy_connect(dev, phy_np, hndlr, 0, phy_intf); -+ -+ of_node_put(phy_np); -+ -+ return phy; -+} -+ - static int ftgmac100_probe(struct platform_device *pdev) + static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err) { - struct resource *res; - int irq; - struct net_device *netdev; - struct phy_device *phydev; -+ phy_interface_t phy_intf; - struct ftgmac100 *priv; - struct device_node *np; int err = 0; -@@ -1882,10 +2300,15 @@ +@@ -1342,6 +1416,9 @@ + /* Enable all interrupts */ + iowrite32(FTGMAC100_INT_ALL, priv->base + FTGMAC100_OFFSET_IER); + ++ /* Configure SGMII controller */ ++ ftgmac100_init_sgmii(priv); ++ + return err; + } + +@@ -1752,15 +1829,16 @@ + struct ftgmac100 *priv = netdev_priv(netdev); + struct phy_device *phydev = netdev->phydev; + +- if (!phydev) +- return; ++ devm_phy_put(priv->dev, priv->sgmii); + +- phy_disconnect(phydev); +- if (of_phy_is_fixed_link(priv->dev->of_node)) +- of_phy_deregister_fixed_link(priv->dev->of_node); ++ if (phydev) { ++ phy_disconnect(phydev); ++ if (of_phy_is_fixed_link(priv->dev->of_node)) ++ of_phy_deregister_fixed_link(priv->dev->of_node); + +- if (priv->use_ncsi) +- fixed_phy_unregister(phydev); ++ if (priv->use_ncsi) ++ fixed_phy_unregister(phydev); ++ } + } + + static void ftgmac100_destroy_mdio(struct net_device *netdev) +@@ -1903,7 +1981,8 @@ np = pdev->dev.of_node; if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac") || of_device_is_compatible(np, "aspeed,ast2500-mac") || @@ -54476,14 +55920,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far priv->rxdes0_edorr_mask = BIT(30); priv->txdes0_edotr_mask = BIT(30); priv->is_aspeed = true; -+ /* Configure RGMII delay if there are the corresponding compatibles */ -+ err = ftgmac100_set_internal_delay(priv, &phy_intf); -+ if (err) -+ goto err_phy_connect; - } else { - priv->rxdes0_edorr_mask = BIT(15); - priv->txdes0_edotr_mask = BIT(15); -@@ -1913,47 +2336,37 @@ +@@ -1934,40 +2013,22 @@ goto err_phy_connect; } err = phy_connect_direct(netdev, phydev, ftgmac100_adjust_link, @@ -54530,24 +55967,7 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far err = ftgmac100_setup_mdio(netdev); if (err) goto err_setup_mdio; - } - -- phy = of_phy_get_and_connect(priv->netdev, np, -- &ftgmac100_adjust_link); -+ /* Because AST2600 will use the RGMII delay to determine -+ * which phy interface to use. -+ */ -+ if (of_device_is_compatible(np, "aspeed,ast2600-mac")) -+ phy = ftgmac100_ast2600_phy_get(priv->netdev, np, -+ &ftgmac100_adjust_link, -+ phy_intf); -+ else -+ phy = of_phy_get_and_connect(priv->netdev, np, -+ &ftgmac100_adjust_link); - if (!phy) { - dev_err(&pdev->dev, "Failed to connect to phy\n"); - err = -EINVAL; -@@ -1995,6 +2408,54 @@ +@@ -2016,6 +2077,24 @@ if (of_device_is_compatible(np, "aspeed,ast2600-mac")) iowrite32(FTGMAC100_TM_DEFAULT, priv->base + FTGMAC100_OFFSET_TM); @@ -54569,40 +55989,10 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far + if (IS_ERR(priv->rst)) { + err = PTR_ERR(priv->rst); + goto err_register_netdev; -+ } -+ -+ err = reset_control_assert(priv->rst); -+ if (err) { -+ dev_err(priv->dev, "Failed to reset mac (%d)\n", err); -+ goto err_register_netdev; -+ } -+ usleep_range(10000, 20000); -+ err = reset_control_deassert(priv->rst); -+ if (err) { -+ dev_err(priv->dev, "Failed to deassert mac reset (%d)\n", err); -+ goto err_register_netdev; -+ } -+ -+ if (priv->sgmii) { -+ /* If using fixed link in dts, sgmii need to be forced */ -+ if (of_phy_is_fixed_link(np)) { -+ err = phy_set_speed(priv->sgmii, netdev->phydev->speed); -+ if (err) { -+ dev_err(priv->dev, "Failed to force sgmii speed\n"); -+ goto err_register_netdev; -+ } -+ } else { -+ /* The phy_init is used to configure Nway */ -+ err = phy_init(priv->sgmii); -+ if (err) { -+ dev_err(priv->dev, "Failed to configure sgmii Nway\n"); -+ goto err_register_netdev; -+ } -+ } } /* Default ring sizes */ -@@ -2021,6 +2482,12 @@ +@@ -2042,6 +2121,12 @@ netdev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); netdev->features |= netdev->hw_features; @@ -54616,8 +56006,8 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/far err = register_netdev(netdev); if (err) { diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h ---- a/drivers/net/ethernet/faraday/ftgmac100.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/ethernet/faraday/ftgmac100.h 2026-04-08 18:03:48.332704918 +0000 +--- a/drivers/net/ethernet/faraday/ftgmac100.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/net/ethernet/faraday/ftgmac100.h 2026-07-17 08:44:55.313306196 +0000 @@ -57,6 +57,13 @@ #define FTGMAC100_OFFSET_RX_RUNT 0xc0 #define FTGMAC100_OFFSET_RX_CRCER_FTL 0xc4 @@ -54648,55 +56038,17 @@ diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/far /* * Receive descriptor, aligned to 16 bytes */ -@@ -271,4 +280,44 @@ +@@ -271,4 +280,6 @@ #define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) #define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) +#define FTGMAC100_RXDES2_RXBUF_BADR_HI GENMASK(18, 16) -+ -+/* Aspeed SCU */ -+#define AST2600_MAC01_CLK_DLY 0x340 -+#define AST2600_MAC23_CLK_DLY 0x350 -+#define AST2600_MAC01_CLK_DLY_UNIT 45 /* ps */ -+#define AST2600_MAC01_TX_DLY_0_NS 0 -+#define AST2600_MAC01_RX_DLY_0_NS 0 -+#define AST2600_MAC23_CLK_DLY_UNIT 250 /* ps */ -+#define AST2600_MAC23_TX_DLY_0_NS 0 -+#define AST2600_MAC23_RX_DLY_0_NS 0x1a -+#define AST2600_MAC_TX_RX_DLY_MASK 0x1f -+#define ASPEED_MAC0_2_TX_DLY GENMASK(5, 0) -+#define ASPEED_MAC0_2_RX_DLY GENMASK(17, 12) -+#define ASPEED_MAC1_3_TX_DLY GENMASK(11, 6) -+#define ASPEED_MAC1_3_RX_DLY GENMASK(23, 18) -+ -+#define AST2600_MAC0_BASE_ADDR 0x1e660000 -+#define AST2600_MAC1_BASE_ADDR 0x1e680000 -+#define AST2600_MAC2_BASE_ADDR 0x1e670000 -+#define AST2600_MAC3_BASE_ADDR 0x1e690000 -+ -+/* Keep original delay */ -+#define AST2600_RGMII_KEEP_DELAY 0x01 -+/* Need to disable delay on MAC side */ -+#define AST2600_RGMII_DIS_DELAY 0x02 -+ -+#define AST2700_MAC0_DLY_UNIT 0x190 -+#define AST2700_MAC_TX_DLY_UNIT(x) FIELD_GET(GENMASK(15, 0), (x)) -+#define AST2700_MAC_RX_DLY_UNIT(x) FIELD_GET(GENMASK(31, 16), (x)) -+#define AST2700_MAC0_DLY_VAL 0x194 -+#define AST2700_MAC_TX_DLY_DIS(x) FIELD_GET(GENMASK(7, 0), (x)) -+#define AST2700_MAC_RX_DLY_DIS(x) FIELD_GET(GENMASK(23, 16), (x)) -+#define AST2700_MAC1_DLY_UNIT 0x198 -+#define AST2700_MAC1_DLY_VAL 0x19c -+#define AST2700_MAC01_CLK_DLY 0x390 -+#define AST2700_MAC0_BASE_ADDR 0x14050000 -+#define AST2700_MAC1_BASE_ADDR 0x14060000 -+#define AST2700_MAC2_BASE_ADDR 0x14070000 + #endif /* __FTGMAC100_H */ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c --- a/drivers/net/mctp/mctp-pcie-vdm.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/net/mctp/mctp-pcie-vdm.c 2026-04-08 18:03:49.259687981 +0000 -@@ -0,0 +1,361 @@ ++++ b/drivers/net/mctp/mctp-pcie-vdm.c 2026-07-17 08:44:56.338290398 +0000 +@@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mctp-pcie-vdm.c - MCTP-over-PCIe-VDM (DMTF DSP0238) transport binding driver. @@ -54776,14 +56128,6 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c + MCTP_PCIE_VDM_BROADCAST_FROM_RC = 3, +}; + -+enum mctp_ctrl_command_code { -+ MCTP_CTRL_CMD_SET_ENDPOINT_ID = 0x01, -+ MCTP_CTRL_CMD_GET_ENDPOINT_ID = 0x02, -+ MCTP_CTRL_CMD_PREPARE_ENDPOINT_DISCOVERY = 0x0B, -+ MCTP_CTRL_CMD_ENDPOINT_DISCOVERY = 0x0C, -+ MCTP_CTRL_CMD_DISCOVERY_NOTIFY = 0x0D -+}; -+ +struct mctp_pcie_vdm_hdr { + u32 length : 10, rsvd0 : 2, attr : 2, ep : 1, td : 1, rsvd1 : 4, tc : 3, + rsvd2 : 1, route_type : 5, fmt : 2, rsvd3 : 1; @@ -54860,6 +56204,8 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c + if (rc) { + pr_err("%s: failed to send packet, rc %d\n", __func__, rc); + stats->tx_errors++; ++ if (rc != -ENOSPC && rc != -EBUSY) ++ stats->tx_dropped++; + } else { + stats->tx_packets++; + stats->tx_bytes += (skb->len - sizeof(struct mctp_pcie_vdm_hdr)); @@ -54879,11 +56225,13 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c + rc = mctp_pcie_vdm_xmit(ndev, skb); + if (rc) { + pr_err("%s: failed to send packet, rc %d\n", __func__, rc); -+ ret = NETDEV_TX_BUSY; -+ } else { -+ ret = NETDEV_TX_OK; -+ kfree_skb(skb); ++ if (rc == -ENOSPC || rc == -EBUSY) { ++ ret = NETDEV_TX_BUSY; ++ return ret; ++ } + } ++ ret = NETDEV_TX_OK; ++ kfree_skb(skb); + } + return ret; +} @@ -54948,10 +56296,11 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c + ndev->header_ops = &mctp_pcie_vdm_net_hdr_ops; +} + -+static int mctp_pcie_vdm_add_net_dev(struct net_device **dev) ++static int mctp_pcie_vdm_add_net_dev(struct net_device **dev, const char *ifname) +{ + struct net_device *ndev = alloc_netdev(sizeof(struct mctp_pcie_vdm_dev), -+ "mctppci%d", NET_NAME_UNKNOWN, ++ ifname ? ifname : "mctppci%d", ++ NET_NAME_UNKNOWN, + mctp_pcie_vdm_net_setup); + + if (!ndev) { @@ -55028,13 +56377,14 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c +} + +struct net_device *mctp_pcie_vdm_add_dev(struct device *dev, -+ const struct mctp_pcie_vdm_ops *ops) ++ const struct mctp_pcie_vdm_ops *ops, ++ const char *ifname) +{ + struct net_device *ndev; + struct mctp_pcie_vdm_dev *vdm_dev; + int rc; + -+ rc = mctp_pcie_vdm_add_net_dev(&ndev); ++ rc = mctp_pcie_vdm_add_net_dev(&ndev, ifname); + if (rc) { + pr_err("%s: failed to add net device\n", __func__); + return ERR_PTR(rc); @@ -55058,25 +56408,55 @@ diff --git a/drivers/net/mctp/mctp-pcie-vdm.c b/drivers/net/mctp/mctp-pcie-vdm.c + } +} +EXPORT_SYMBOL_GPL(mctp_pcie_vdm_remove_dev); ++ ++void mctp_pcie_vdm_set_carrier(struct net_device *ndev, bool up) ++{ ++ if (!ndev) { ++ pr_err("%s: ndev is NULL\n", __func__); ++ return; ++ } ++ ++ if (up) ++ netif_carrier_on(ndev); ++ else ++ netif_carrier_off(ndev); ++} ++EXPORT_SYMBOL_GPL(mctp_pcie_vdm_set_carrier); diff --git a/drivers/net/mdio/mdio-aspeed.c b/drivers/net/mdio/mdio-aspeed.c ---- a/drivers/net/mdio/mdio-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/net/mdio/mdio-aspeed.c 2026-04-08 18:03:48.105709065 +0000 -@@ -62,6 +62,8 @@ +--- a/drivers/net/mdio/mdio-aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/net/mdio/mdio-aspeed.c 2026-07-17 08:44:55.079309803 +0000 +@@ -62,13 +62,8 @@ | FIELD_PREP(ASPEED_MDIO_DATA_MIIRDATA, data); iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL); +- +- /* Workaround for read-after-write issue. +- * The controller may return stale data if a read follows immediately +- * after a write. A dummy read forces the hardware to update its +- * internal state, ensuring that the next real read returns correct data. +- */ +- ioread32(ctx->base + ASPEED_MDIO_CTRL); + /* Add dummy read to ensure triggering mdio controller */ + (void)ioread32(ctx->base + ASPEED_MDIO_CTRL); return readl_poll_timeout(ctx->base + ASPEED_MDIO_CTRL, ctrl, !(ctrl & ASPEED_MDIO_CTRL_FIRE), diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig ---- a/drivers/pci/controller/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pci/controller/Kconfig 2026-04-08 18:03:35.347942010 +0000 -@@ -47,6 +47,22 @@ +--- a/drivers/pci/controller/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pci/controller/Kconfig 2026-07-17 08:44:42.375505612 +0000 +@@ -47,6 +47,31 @@ If unsure, say Y if you have an Apple Silicon system. ++config PHY_ASPEED_COMBINE_PERST ++ bool "ASPEED PCIe RC combine PERST with internal reset" ++ depends on ARCH_ASPEED ++ help ++ Configure PERST to connect with the internal PHY reset. When ++ this option is disabled, the driver selects the separate internal ++ PHY reset path by default. Enable it only on boards that require ++ PERST and the internal PHY reset to toggle at the same time. ++ +config PCIE_ASPEED + bool "ASPEED PCIe controller" + depends on ARCH_ASPEED || COMPILE_TEST @@ -55097,8 +56477,8 @@ diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE || COMPILE_TEST diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile ---- a/drivers/pci/controller/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pci/controller/Makefile 2026-04-08 18:03:40.287851903 +0000 +--- a/drivers/pci/controller/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pci/controller/Makefile 2026-07-17 08:44:47.516426373 +0000 @@ -39,6 +39,7 @@ obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o @@ -55109,8 +56489,8 @@ diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile obj-y += dwc/ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c --- a/drivers/pci/controller/pcie-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pci/controller/pcie-aspeed.c 2026-04-08 18:03:48.290705685 +0000 -@@ -0,0 +1,1182 @@ ++++ b/drivers/pci/controller/pcie-aspeed.c 2026-07-17 08:44:55.269306874 +0000 +@@ -0,0 +1,1208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for ASPEED PCIe Bridge @@ -55204,6 +56584,12 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- +#define H2X_CFGE_INT_STS 0x08 +#define CFGE_TX_IDLE BIT(0) +#define CFGE_RX_BUSY BIT(1) ++#define H2X_CTRL2 0x1c ++#define CPL_TIMEOUT_MASK GENMASK(9, 8) ++#define CPL_TIMEOUT_10US 0 ++#define CPL_TIMEOUT_20US 1 ++#define CPL_TIMEOUT_40US 2 ++#define CPL_TIMEOUT_160US 3 +#define H2X_CFGI_TLP 0x20 +#define H2X_CFGI_WR_DATA 0x24 +#define H2X_CFGI_CTRL 0x28 @@ -55213,6 +56599,7 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- +#define H2X_CFGE_TLP_NEXT 0x34 +#define H2X_CFGE_CTRL 0x38 +#define CFGE_TLP_FIRE BIT(0) ++#define CFGE_TLP_RET_BYPASS BIT(1) +#define H2X_CFGE_RET_DATA 0x3C +#define H2X_REMAP_PREF_ADDR 0x70 +#define H2X_REMAP_DIRECT_ADDR 0x78 @@ -55234,6 +56621,11 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- +#define PEHR_MISC_78 0x78 +#define PEHR_MISC_1B8 0x1B8 +#define SW_ATT_BTN BIT(0) ++#define PEHR_MISC_278 0x278 ++#define SET_TO_DOWNSTREAM BIT(22) ++#define ASSERT_INTERNAL_RESET BIT(31) ++#define PEHR_MISC_280 0x280 ++#define SEL_INTERNAL_RESET BIT(0) +#define PEHR_MISC_344 0x344 +#define LINK_STATUS_GEN2 BIT(18) +#define PEHR_MISC_358 0x358 @@ -55638,6 +57030,7 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + "[%X:%02X:%02X.%02X]CR rx timeoutsts: 0x%08x\n", + pcie->domain, bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), status); ++ writel(CFGE_TLP_RET_BYPASS, pcie->reg + H2X_CFGE_CTRL); + goto out; + } + *val = readl(pcie->reg + H2X_CFGE_RET_DATA); @@ -56103,6 +57496,9 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + mdelay(10); + reset_control_deassert(pcie->h2xrst); + ++ regmap_write(pcie->pciephy, PEHR_MISC_278, 0); ++ if (!IS_ENABLED(CONFIG_PHY_ASPEED_COMBINE_PERST)) ++ regmap_write(pcie->pciephy, PEHR_MISC_280, SEL_INTERNAL_RESET); + regmap_write(pcie->pciephy, PEHR_MISC_5C, 0x40000000); + regmap_read(pcie->pciephy, PEHR_MISC_60, &cfg_val); + regmap_write(pcie->pciephy, PEHR_MISC_60, @@ -56111,6 +57507,9 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + writel(0, pcie->reg + H2X_CTRL); + writel(H2X_BRIDGE_EN | H2X_BRIDGE_DIRECT_EN, pcie->reg + H2X_CTRL); + ++ writel(FIELD_PREP(CPL_TIMEOUT_MASK, CPL_TIMEOUT_160US), ++ pcie->reg + H2X_CTRL2); ++ + /* The BAR mapping: + * CPU Node0(domain 0): 0x60000000 + * CPU Node1(domain 1): 0x80000000 @@ -56121,6 +57520,12 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + /* Prepare for 64-bit BAR pref */ + writel(0x3, pcie->reg + H2X_REMAP_PREF_ADDR); + ++ if (IS_ENABLED(CONFIG_PHY_ASPEED_COMBINE_PERST)) ++ regmap_write(pcie->pciephy, PEHR_MISC_278, SET_TO_DOWNSTREAM); ++ else ++ regmap_write(pcie->pciephy, PEHR_MISC_278, ++ ASSERT_INTERNAL_RESET | SET_TO_DOWNSTREAM); ++ + reset_control_deassert(pcie->perst); + if (pcie->perst_rc_out) + gpiod_set_value(pcie->perst_rc_out, 1); @@ -56136,6 +57541,7 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + ATTENTION_BUTTON_ENABLE); + regmap_write_bits(pcie->pciephy, PEHR_MISC_38, + DATALINK_REPORT_CAP, DATALINK_REPORT_CAP); ++ regmap_write(pcie->pciephy, PEHR_MISC_278, SET_TO_DOWNSTREAM); + } + + if (!aspeed_ast2700_get_link(pcie)) @@ -56294,8 +57700,8 @@ diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie- + +module_platform_driver(aspeed_pcie_driver); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig ---- a/drivers/phy/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/phy/Kconfig 2026-04-08 18:03:34.885950437 +0000 +--- a/drivers/phy/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/phy/Kconfig 2026-07-17 08:44:41.811514305 +0000 @@ -84,6 +84,7 @@ source "drivers/phy/allwinner/Kconfig" @@ -56305,8 +57711,8 @@ diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig source "drivers/phy/cadence/Kconfig" source "drivers/phy/freescale/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile ---- a/drivers/phy/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/phy/Makefile 2026-04-08 18:03:39.794860903 +0000 +--- a/drivers/phy/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/phy/Makefile 2026-07-17 08:44:47.053433509 +0000 @@ -13,6 +13,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE) += phy-airoha-pcie.o obj-y += allwinner/ \ @@ -56317,8 +57723,8 @@ diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile freescale/ \ diff --git a/drivers/phy/aspeed/Kconfig b/drivers/phy/aspeed/Kconfig --- a/drivers/phy/aspeed/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/Kconfig 2026-04-08 18:03:46.226743396 +0000 -@@ -0,0 +1,23 @@ ++++ b/drivers/phy/aspeed/Kconfig 2026-07-17 08:44:53.112340121 +0000 +@@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# @@ -56338,284 +57744,21 @@ diff --git a/drivers/phy/aspeed/Kconfig b/drivers/phy/aspeed/Kconfig +config PHY_ASPEED_USB3 + tristate "ASPEED USB3 PHY driver" + select GENERIC_PHY -+ depends on ARCH_ASPEED -+ default n ++ depends on (ARCH_ASPEED || COMPILE_TEST) + help -+ Enable driver support for Aspeed AST2700 PHY USB3. ++ Enable driver support for Aspeed AST2700 USB3 PHY. diff --git a/drivers/phy/aspeed/Makefile b/drivers/phy/aspeed/Makefile --- a/drivers/phy/aspeed/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/Makefile 2026-04-08 18:03:46.226743396 +0000 ++++ b/drivers/phy/aspeed/Makefile 2026-07-17 08:44:53.112340121 +0000 @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PHY_ASPEED_SGMII) += phy-aspeed-sgmii.o -+obj-$(CONFIG_PHY_ASPEED_USB3) += aspeed-usb-phy3.o -\ No newline at end of file -diff --git a/drivers/phy/aspeed/aspeed-usb-phy3.c b/drivers/phy/aspeed/aspeed-usb-phy3.c ---- a/drivers/phy/aspeed/aspeed-usb-phy3.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/aspeed-usb-phy3.c 2026-04-08 18:03:48.216707037 +0000 -@@ -0,0 +1,257 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define PHY3P00_DEFAULT 0xCE70000F /* PHY PCS Protocol Setting #1 default value */ -+#define PHY3P04_DEFAULT 0x49C00014 /* PHY PCS Protocol Setting #2 default value */ -+#define PHY3P08_DEFAULT 0x5E406825 /* PHY PCS Protocol Setting #3 default value */ -+#define PHY3P0C_DEFAULT 0x00000001 /* PHY PCS Protocol Setting #4 default value */ -+ -+#define DWC_CRTL_NUM 3 -+ -+#define USB_PHY3_INIT_DONE BIT(15) /* BIT15: USB3.1 Phy internal SRAM initialization done */ -+#define USB_PHY3_SRAM_BYPASS BIT(7) /* USB3.1 Phy SRAM bypass */ -+#define USB_PHY3_SRAM_EXT_LOAD BIT(6) /* USB3.1 Phy SRAM external load done */ -+ -+struct aspeed_usb_phy3 { -+ struct device *dev; -+ void __iomem *regs; -+ const struct aspeed_usb_phy3_model *model; -+ bool phy_ext_load_quirk; -+}; -+ -+struct usb_dwc3_ctrl { -+ u32 offset; -+ u32 value; -+}; -+ -+struct aspeed_usb_phy3_model { -+ /* offsets to the PHY3 registers */ -+ unsigned int phy3s00; /* PHY SRAM Control/Status #1 */ -+ unsigned int phy3s04; /* PHY SRAM Control/Status #2 */ -+ unsigned int phy3c00; /* PHY PCS Control/Status #1 */ -+ unsigned int phy3c04; /* PHY PCS Control/Status #2 */ -+ unsigned int phy3p00; /* PHY PCS Protocol Setting #1 */ -+ unsigned int phy3p04; /* PHY PCS Protocol Setting #2 */ -+ unsigned int phy3p08; /* PHY PCS Protocol Setting #3 */ -+ unsigned int phy3p0c; /* PHY PCS Protocol Setting #4 */ -+ unsigned int dwc_cmd; /* DWC3 Commands base address offest */ -+}; -+ -+static struct usb_dwc3_ctrl ctrl_data[DWC_CRTL_NUM] = { -+ {0xc100, 0x00000006}, /* Set DWC3 GSBUSCFG0 for Bus Burst Type */ -+ {0xc12c, 0x0c854802}, /* Set DWC3 GUCTL for ref_clk */ -+ {0xc630, 0x0c800020}, /* Set DWC3 GLADJ for ref_clk */ -+}; -+ -+static const struct aspeed_usb_phy3_model ast2700a0_model = { -+ .phy3s00 = 0x800, -+ .phy3s04 = 0x804, -+ .phy3c00 = 0x808, -+ .phy3c04 = 0x80C, -+ .phy3p00 = 0x810, -+ .phy3p04 = 0x814, -+ .phy3p08 = 0x818, -+ .phy3p0c = 0x81C, -+ .dwc_cmd = 0xB80, -+}; -+ -+static const struct aspeed_usb_phy3_model ast2700_model = { -+ .phy3s00 = 0x00, -+ .phy3s04 = 0x04, -+ .phy3c00 = 0x08, -+ .phy3c04 = 0x0C, -+ .phy3p00 = 0x10, -+ .phy3p04 = 0x14, -+ .phy3p08 = 0x18, -+ .phy3p0c = 0x1C, -+ .dwc_cmd = 0x40, -+}; -+ -+static const struct of_device_id aspeed_usb_phy3_dt_ids[] = { -+ { -+ .compatible = "aspeed,ast2700-a0-uhy3a", -+ .data = &ast2700a0_model -+ }, -+ { -+ .compatible = "aspeed,ast2700-a0-uhy3b", -+ .data = &ast2700a0_model -+ }, -+ { -+ .compatible = "aspeed,ast2700-uphy3a", -+ .data = &ast2700_model -+ }, -+ { -+ .compatible = "aspeed,ast2700-uphy3b", -+ .data = &ast2700_model -+ }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, aspeed_usb_phy3_dt_ids); -+ -+static int aspeed_usb_phy3_init(struct phy *phy) -+{ -+ struct aspeed_usb_phy3 *phy3 = phy_get_drvdata(phy); -+ const struct aspeed_usb_phy3_model *model = phy3->model; -+ u32 val; -+ int timeout = 100; -+ int i, j; -+ -+ while ((readl(phy3->regs + model->phy3s00) & USB_PHY3_INIT_DONE) -+ != USB_PHY3_INIT_DONE) { -+ usleep_range(100, 110); -+ if (--timeout == 0) { -+ dev_err(phy3->dev, "Wait phy3 init timed out\n"); -+ return -ETIMEDOUT; -+ } -+ } -+ -+ val = readl(phy3->regs + model->phy3s00); -+ -+ if (phy3->phy_ext_load_quirk) -+ val |= USB_PHY3_SRAM_EXT_LOAD; -+ else -+ val |= USB_PHY3_SRAM_BYPASS; -+ writel(val, phy3->regs + model->phy3s00); -+ -+ /* Set protocol1_ext signals as default PHY3 settings based on SNPS documents. -+ * Including PCFGI[54]: protocol1_ext_rx_los_lfps_en for better compatibility -+ */ -+ writel(PHY3P00_DEFAULT, phy3->regs + model->phy3p00); -+ writel(PHY3P04_DEFAULT, phy3->regs + model->phy3p04); -+ writel(PHY3P08_DEFAULT, phy3->regs + model->phy3p08); -+ writel(PHY3P0C_DEFAULT, phy3->regs + model->phy3p0c); -+ -+ /* xHCI DWC specific command initially set when PCIe xHCI enable */ -+ for (i = 0, j = model->dwc_cmd; i < DWC_CRTL_NUM; i++) { -+ /* 48-bits Command: -+ * CMD1: Data -> DWC CMD [31:0], Address -> DWC CMD [47:32] -+ * CMD2: Data -> DWC CMD [79:48], Address -> DWC CMD [95:80] -+ * ... and etc. -+ */ -+ if (i % 2 == 0) { -+ writel(ctrl_data[i].value, phy3->regs + j); -+ j += 4; -+ -+ writel(ctrl_data[i].offset & 0xFFFF, phy3->regs + j); -+ } else { -+ val = readl(phy3->regs + j) & 0xFFFF; -+ val |= ((ctrl_data[i].value & 0xFFFF) << 16); -+ writel(val, phy3->regs + j); -+ j += 4; -+ -+ val = (ctrl_data[i].offset << 16) | (ctrl_data[i].value >> 16); -+ writel(val, phy3->regs + j); -+ j += 4; -+ } -+ } -+ -+ dev_info(phy3->dev, "Initialized USB PHY3\n"); -+ return 0; -+} -+ -+static const struct phy_ops aspeed_usb_phy3_phyops = { -+ .init = aspeed_usb_phy3_init, -+ .owner = THIS_MODULE, -+}; -+ -+static int aspeed_usb_phy3_probe(struct platform_device *pdev) -+{ -+ struct aspeed_usb_phy3 *phy3; -+ struct device *dev; -+ struct phy_provider *provider; -+ struct phy *phy; -+ struct device_node *node = pdev->dev.of_node; -+ struct clk *clk; -+ struct reset_control *rst; -+ int rc = 0; -+ -+ dev = &pdev->dev; -+ -+ phy3 = devm_kzalloc(dev, sizeof(*phy3), GFP_KERNEL); -+ if (!phy3) -+ return -ENOMEM; -+ -+ phy3->dev = dev; -+ -+ phy3->model = of_device_get_match_data(dev); -+ if (IS_ERR(phy3->model)) { -+ dev_err(dev, "Couldn't get model data\n"); -+ return -ENODEV; -+ } -+ -+ clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); -+ -+ rc = clk_prepare_enable(clk); -+ if (rc) { -+ dev_err(dev, "Unable to enable clock (%d)\n", rc); -+ return rc; -+ } -+ -+ rst = devm_reset_control_get_shared(dev, NULL); -+ if (IS_ERR(rst)) { -+ rc = PTR_ERR(rst); -+ goto err; -+ } -+ rc = reset_control_deassert(rst); -+ if (rc) -+ goto err; -+ -+ phy3->regs = of_iomap(node, 0); -+ -+ phy3->phy_ext_load_quirk = -+ device_property_read_bool(dev, "aspeed,phy_ext_load_quirk"); -+ -+ phy = devm_phy_create(dev, NULL, &aspeed_usb_phy3_phyops); -+ if (IS_ERR(phy)) { -+ dev_err(dev, "failed to create PHY\n"); -+ return PTR_ERR(phy); -+ } -+ -+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); -+ if (IS_ERR(provider)) -+ return PTR_ERR(provider); -+ -+ phy_set_drvdata(phy, phy3); -+ -+ dev_info(phy3->dev, "Probed USB PHY3\n"); -+ -+ return 0; -+ -+err: -+ if (clk) -+ clk_disable_unprepare(clk); -+ return rc; -+} -+ -+static void aspeed_usb_phy3_remove(struct platform_device *pdev) -+{ -+} -+ -+static struct platform_driver aspeed_usb_phy3_driver = { -+ .probe = aspeed_usb_phy3_probe, -+ .remove = aspeed_usb_phy3_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_usb_phy3_dt_ids, -+ }, -+}; -+module_platform_driver(aspeed_usb_phy3_driver); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Joe Wang "); ++obj-$(CONFIG_PHY_ASPEED_USB3) += phy-aspeed-usb3.o diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspeed-sgmii.c --- a/drivers/phy/aspeed/phy-aspeed-sgmii.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/phy/aspeed/phy-aspeed-sgmii.c 2026-04-08 18:03:48.211707129 +0000 -@@ -0,0 +1,234 @@ ++++ b/drivers/phy/aspeed/phy-aspeed-sgmii.c 2026-07-17 08:44:55.189308107 +0000 +@@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Aspeed Technology Inc. @@ -56667,7 +57810,7 @@ diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspe + u8 revision; +}; + -+static int aspeed_sgmii_conf(struct phy *phy, bool nway, int speed) ++static int aspeed_sgmii_conf(struct phy *phy, int speed) +{ + struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); + u32 cfg; @@ -56675,8 +57818,8 @@ diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspe + writel(0, sgmii->regs + SGMII_MODE); + + writel(0, sgmii->regs + SGMII_CFG); -+ writel(SGMII_CFG_SW_RESET | SGMII_CFG_PWR_DOWN, sgmii->regs + SGMII_CFG); -+ if (nway) { ++ if (speed == 0) { ++ /* Configure for auto-negotiation */ + if (sgmii->revision == 1) + writel(SGMII_CFG_AN_ENABLE, sgmii->regs + SGMII_CFG); + else @@ -56718,7 +57861,7 @@ diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspe + writel(0x1, sgmii->regs + SGMII_NWAY_ACK); + + cfg = SGMII_MODE_ENABLE; -+ if (!nway) ++ if (speed) + cfg |= SGMII_MODE_USE_LOCAL_CONFIG; + writel(cfg, sgmii->regs + SGMII_MODE); + @@ -56727,13 +57870,38 @@ diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspe + +static int aspeed_sgmii_phy_init(struct phy *phy) +{ -+ /* Default to enable Nway, not need configure speed */ -+ return aspeed_sgmii_conf(phy, true, 0); ++ struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); ++ u32 reg; ++ ++ /* ++ * The PLDA frequency multiplication is X xor 0x19. ++ * (X xor 0x19) * clock source = data rate. ++ * SGMII data rate is 1.25G, so (0x2b xor 0x19) * 25MHz is equal 1.25G. ++ */ ++ reg = PCIEPHY_CLK_SEL_INTERNAL_25M | PCIEPHY_CLK_FREQ_MULTI(0x2b); ++ regmap_write(sgmii->pcie_phy_regmap, PCIEPHY_CLK, reg); ++ if (sgmii->revision > 1) { ++ regmap_read(sgmii->pcie_phy_regmap, PEHR280, ®); ++ reg |= SGMII_INTERNAL_CLK_EN; ++ regmap_write(sgmii->pcie_phy_regmap, PEHR280, reg); ++ } ++ ++ return 0; ++} ++ ++static int aspeed_sgmii_phy_reset(struct phy *phy) ++{ ++ struct aspeed_sgmii *sgmii = phy_get_drvdata(phy); ++ ++ writel(SGMII_CFG_PWR_DOWN, sgmii->regs + SGMII_CFG); ++ writel(0, sgmii->regs + SGMII_CFG); ++ ++ return 0; +} + +static int aspeed_sgmii_phy_set_speed(struct phy *phy, int speed) +{ -+ return aspeed_sgmii_conf(phy, false, speed); ++ return aspeed_sgmii_conf(phy, speed); +} + +static int aspeed_sgmii_phy_exit(struct phy *phy) @@ -56748,6 +57916,7 @@ diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspe + +static const struct phy_ops aspeed_sgmii_phyops = { + .init = aspeed_sgmii_phy_init, ++ .reset = aspeed_sgmii_phy_reset, + .set_speed = aspeed_sgmii_phy_set_speed, + .exit = aspeed_sgmii_phy_exit, + .owner = THIS_MODULE, @@ -56814,19 +57983,6 @@ diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspe + + phy_set_drvdata(phy, sgmii); + -+ /* -+ * The PLDA frequency multiplication is X xor 0x19. -+ * (X xor 0x19) * clock source = data rate. -+ * SGMII data rate is 1.25G, so (0x2b xor 0x19) * 25MHz is equal 1.25G. -+ */ -+ reg = PCIEPHY_CLK_SEL_INTERNAL_25M | PCIEPHY_CLK_FREQ_MULTI(0x2b); -+ regmap_write(sgmii->pcie_phy_regmap, PCIEPHY_CLK, reg); -+ if (sgmii->revision > 1) { -+ regmap_read(sgmii->pcie_phy_regmap, PEHR280, ®); -+ reg |= SGMII_INTERNAL_CLK_EN; -+ regmap_write(sgmii->pcie_phy_regmap, PEHR280, reg); -+ } -+ + dev_info(dev, "module loaded\n"); + + return 0; @@ -56850,9 +58006,249 @@ diff --git a/drivers/phy/aspeed/phy-aspeed-sgmii.c b/drivers/phy/aspeed/phy-aspe +MODULE_AUTHOR("Jacky Chou "); +MODULE_DESCRIPTION("ASPEED SGMII Serdes/PHY controller"); +MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/aspeed/phy-aspeed-usb3.c b/drivers/phy/aspeed/phy-aspeed-usb3.c +--- a/drivers/phy/aspeed/phy-aspeed-usb3.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/phy/aspeed/phy-aspeed-usb3.c 2026-07-17 08:44:55.194308030 +0000 +@@ -0,0 +1,236 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PHY3S00 0x00 ++#define PHY3S00_INIT_DONE BIT(15) ++#define PHY3S00_SRAM_BYPASS BIT(7) ++#define PHY3S00_SRAM_EXT_LOAD BIT(6) ++#define PHY3S04 0x04 ++#define PHY3C00 0x08 ++#define PHY3C04 0x0C ++#define PHY3P00 0x10 ++#define PHY3P00_RX_ADAPT_AFE_EN_G1 BIT(0) ++#define PHY3P00_RX_ADAPT_AFE_EN_G2 BIT(1) ++#define PHY3P00_RX_ADAPT_DFE_EN_G1 BIT(2) ++#define PHY3P00_RX_ADAPT_DFE_EN_G2 BIT(3) ++#define PHY3P00_RX_CDR_VCO_LOWFREQ_G1 BIT(4) ++#define PHY3P00_RX_CDR_VCO_LOWFREQ_G2 BIT(5) ++#define PHY3P00_RX_EQ_AFE_GAIN_G1 GENMASK(9, 6) ++#define PHY3P00_RX_EQ_AFE_GAIN_G2 GENMASK(13, 10) ++#define PHY3P00_RX_EQ_ATT_LVL_G1 GENMASK(16, 14) ++#define PHY3P00_RX_EQ_ATT_LVL_G2 GENMASK(19, 17) ++#define PHY3P00_RX_EQ_CTLE_BOOST_G1 GENMASK(24, 20) ++#define PHY3P00_RX_EQ_CTLE_BOOST_G2 GENMASK(29, 25) ++#define PHY3P00_RX_EQ_DELTA_IQ_G1_LO GENMASK(31, 30) ++ ++#define PHY3P04 0x14 ++#define PHY3P04_RX_EQ_DELTA_IQ_G1_HI GENMASK(1, 0) ++#define PHY3P04_RX_EQ_DELTA_IQ_G2 GENMASK(5, 2) ++#define PHY3P04_RX_EQ_DFE_TAP1_G1 GENMASK(13, 6) ++#define PHY3P04_RX_EQ_DFE_TAP1_G2 GENMASK(21, 14) ++#define PHY3P04_RX_LOS_LFPS_EN BIT(22) ++#define PHY3P04_RX_LOS_THRESHOLD GENMASK(25, 23) ++#define PHY3P04_RX_TERM_CTRL GENMASK(28, 26) ++#define PHY3P04_TX_EQ_MAIN_G1_LO GENMASK(31, 29) ++ ++#define PHY3P08 0x18 ++#define PHY3P08_TX_EQ_MAIN_G1_HI GENMASK(1, 0) ++#define PHY3P08_TX_EQ_MAIN_G2 GENMASK(6, 2) ++#define PHY3P08_TX_EQ_OVRD BIT(7) ++#define PHY3P08_TX_EQ_POST_G1 GENMASK(12, 9) ++#define PHY3P08_TX_EQ_POST_G2 GENMASK(16, 13) ++#define PHY3P08_TX_EQ_PRE_G1 GENMASK(20, 17) ++#define PHY3P08_TX_EQ_PRE_G2 GENMASK(24, 21) ++#define PHY3P08_TX_IBOOST_LVL GENMASK(28, 25) ++#define PHY3P08_TX_TERM_CTRL GENMASK(31, 29) ++ ++#define PHY3P0C 0x1C ++#define PHY3P0C_TX_VBOOST_EN BIT(0) ++ ++#define PHY3CMD 0x40 ++ ++#define PHY3P_RX_EQ_CTLE_BOOST_G1_DEFAULT 0x7 ++#define PHY3P_RX_EQ_CTLE_BOOST_G2_DEFAULT 0x7 ++#define PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT 0x3 ++#define PHY3P_RX_EQ_DELTA_IQ_G2_DEFAULT 0x5 ++#define PHY3P_RX_LOS_THRESHOLD_DEFAULT 0x3 ++#define PHY3P_RX_TERM_CTRL_DEFAULT 0x2 ++#define PHY3P_TX_EQ_MAIN_G1_DEFAULT 0xa ++#define PHY3P_TX_EQ_MAIN_G2_DEFAULT 0x9 ++#define PHY3P_TX_EQ_POST_G1_DEFAULT 0x4 ++#define PHY3P_TX_EQ_POST_G2_DEFAULT 0x3 ++#define PHY3P_TX_EQ_PRE_G2_DEFAULT 0x2 ++#define PHY3P_TX_IBOOST_LVL_DEFAULT 0xf ++#define PHY3P_TX_TERM_CTRL_DEFAULT 0x2 ++ ++#define PHY3P00_DEFAULT ( \ ++ PHY3P00_RX_ADAPT_AFE_EN_G1 | \ ++ PHY3P00_RX_ADAPT_AFE_EN_G2 | \ ++ PHY3P00_RX_ADAPT_DFE_EN_G1 | \ ++ PHY3P00_RX_ADAPT_DFE_EN_G2 | \ ++ FIELD_PREP(PHY3P00_RX_EQ_CTLE_BOOST_G1, PHY3P_RX_EQ_CTLE_BOOST_G1_DEFAULT) | \ ++ FIELD_PREP(PHY3P00_RX_EQ_CTLE_BOOST_G2, PHY3P_RX_EQ_CTLE_BOOST_G2_DEFAULT) | \ ++ FIELD_PREP(PHY3P00_RX_EQ_DELTA_IQ_G1_LO, \ ++ PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT & 0x3) \ ++) ++ ++#define PHY3P04_DEFAULT ( \ ++ FIELD_PREP(PHY3P04_RX_EQ_DELTA_IQ_G1_HI, \ ++ PHY3P_RX_EQ_DELTA_IQ_G1_DEFAULT >> 2) | \ ++ FIELD_PREP(PHY3P04_RX_EQ_DELTA_IQ_G2, PHY3P_RX_EQ_DELTA_IQ_G2_DEFAULT) | \ ++ PHY3P04_RX_LOS_LFPS_EN | \ ++ FIELD_PREP(PHY3P04_RX_LOS_THRESHOLD, PHY3P_RX_LOS_THRESHOLD_DEFAULT) | \ ++ FIELD_PREP(PHY3P04_RX_TERM_CTRL, PHY3P_RX_TERM_CTRL_DEFAULT) | \ ++ FIELD_PREP(PHY3P04_TX_EQ_MAIN_G1_LO, \ ++ PHY3P_TX_EQ_MAIN_G1_DEFAULT & 0x7) \ ++) ++ ++#define PHY3P08_DEFAULT ( \ ++ FIELD_PREP(PHY3P08_TX_EQ_MAIN_G1_HI, PHY3P_TX_EQ_MAIN_G1_DEFAULT >> 3) | \ ++ FIELD_PREP(PHY3P08_TX_EQ_MAIN_G2, PHY3P_TX_EQ_MAIN_G2_DEFAULT) | \ ++ FIELD_PREP(PHY3P08_TX_EQ_POST_G1, PHY3P_TX_EQ_POST_G1_DEFAULT) | \ ++ FIELD_PREP(PHY3P08_TX_EQ_POST_G2, PHY3P_TX_EQ_POST_G2_DEFAULT) | \ ++ FIELD_PREP(PHY3P08_TX_EQ_PRE_G2, PHY3P_TX_EQ_PRE_G2_DEFAULT) | \ ++ FIELD_PREP(PHY3P08_TX_IBOOST_LVL, PHY3P_TX_IBOOST_LVL_DEFAULT) | \ ++ FIELD_PREP(PHY3P08_TX_TERM_CTRL, PHY3P_TX_TERM_CTRL_DEFAULT) \ ++) ++ ++#define PHY3P0C_DEFAULT \ ++ PHY3P0C_TX_VBOOST_EN ++ ++struct aspeed_usb3_phy { ++ void __iomem *regs; ++ struct reset_control *rst; ++ struct device *dev; ++ struct clk *clk; ++}; ++ ++static int aspeed_usb3_phy_init(struct phy *phy) ++{ ++ struct aspeed_usb3_phy *aspeed_phy = phy_get_drvdata(phy); ++ u32 val; ++ int ret; ++ ++ ret = clk_prepare_enable(aspeed_phy->clk); ++ if (ret) { ++ dev_err(aspeed_phy->dev, "Failed to enable clock %d\n", ret); ++ return ret; ++ } ++ ++ ret = reset_control_deassert(aspeed_phy->rst); ++ if (ret) { ++ clk_disable_unprepare(aspeed_phy->clk); ++ return ret; ++ } ++ ++ /* Wait for USB3 PHY internal SRAM initialization done */ ++ ret = readl_poll_timeout(aspeed_phy->regs + PHY3S00, val, ++ val & PHY3S00_INIT_DONE, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC); ++ if (ret) { ++ dev_err(aspeed_phy->dev, "SRAM init timeout\n"); ++ goto err_assert_reset; ++ } ++ ++ val = readl(aspeed_phy->regs + PHY3S00); ++ val |= PHY3S00_SRAM_BYPASS; ++ writel(val, aspeed_phy->regs + PHY3S00); ++ ++ /* Set protocol1_ext signals as default PHY3 settings based on SNPS documents. ++ * Including PCFGI[54]: protocol1_ext_rx_los_lfps_en for better compatibility ++ */ ++ writel(PHY3P00_DEFAULT, aspeed_phy->regs + PHY3P00); ++ writel(PHY3P04_DEFAULT, aspeed_phy->regs + PHY3P04); ++ writel(PHY3P08_DEFAULT, aspeed_phy->regs + PHY3P08); ++ writel(PHY3P0C_DEFAULT, aspeed_phy->regs + PHY3P0C); ++ ++ return 0; ++ ++err_assert_reset: ++ reset_control_assert(aspeed_phy->rst); ++ clk_disable_unprepare(aspeed_phy->clk); ++ return ret; ++} ++ ++static int aspeed_usb3_phy_exit(struct phy *phy) ++{ ++ struct aspeed_usb3_phy *aspeed_phy = phy_get_drvdata(phy); ++ ++ reset_control_assert(aspeed_phy->rst); ++ clk_disable_unprepare(aspeed_phy->clk); ++ ++ return 0; ++} ++ ++static const struct phy_ops aspeed_usb3_phy_ops = { ++ .init = aspeed_usb3_phy_init, ++ .exit = aspeed_usb3_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int aspeed_usb3_phy_probe(struct platform_device *pdev) ++{ ++ struct aspeed_usb3_phy *aspeed_phy; ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ ++ aspeed_phy = devm_kzalloc(dev, sizeof(*aspeed_phy), GFP_KERNEL); ++ if (!aspeed_phy) ++ return -ENOMEM; ++ ++ aspeed_phy->dev = dev; ++ ++ aspeed_phy->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(aspeed_phy->clk)) ++ return PTR_ERR(aspeed_phy->clk); ++ ++ aspeed_phy->rst = devm_reset_control_get_exclusive(dev, NULL); ++ if (IS_ERR(aspeed_phy->rst)) ++ return PTR_ERR(aspeed_phy->rst); ++ ++ aspeed_phy->regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(aspeed_phy->regs)) ++ return PTR_ERR(aspeed_phy->regs); ++ ++ phy = devm_phy_create(dev, NULL, &aspeed_usb3_phy_ops); ++ if (IS_ERR(phy)) ++ return PTR_ERR(phy); ++ ++ phy_set_drvdata(phy, aspeed_phy); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id aspeed_usb3_phy_match_table[] = { ++ { ++ .compatible = "aspeed,ast2700-usb3-phy", ++ }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_usb3_phy_match_table); ++ ++static struct platform_driver aspeed_usb3_phy_driver = { ++ .probe = aspeed_usb3_phy_probe, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_usb3_phy_match_table, ++ }, ++}; ++module_platform_driver(aspeed_usb3_phy_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ASPEED USB3.0 PHY Driver"); diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig ---- a/drivers/pinctrl/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/Kconfig 2026-04-08 18:03:48.322705101 +0000 +--- a/drivers/pinctrl/aspeed/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pinctrl/aspeed/Kconfig 2026-07-17 08:44:55.302306366 +0000 @@ -31,3 +31,11 @@ help Say Y here to enable pin controller support for Aspeed's 6th @@ -56866,8 +58262,8 @@ diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig + Say Y here to enable pin controller support for Aspeed's 7th + generation SoCs. GPIO is provided by a separate GPIO driver. diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile ---- a/drivers/pinctrl/aspeed/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/Makefile 2026-04-08 18:03:48.322705101 +0000 +--- a/drivers/pinctrl/aspeed/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pinctrl/aspeed/Makefile 2026-07-17 08:44:55.302306366 +0000 @@ -6,3 +6,4 @@ obj-$(CONFIG_PINCTRL_ASPEED_G4) += pinctrl-aspeed-g4.o obj-$(CONFIG_PINCTRL_ASPEED_G5) += pinctrl-aspeed-g5.o @@ -56875,8 +58271,8 @@ diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile +obj-$(CONFIG_PINCTRL_ASPEED_G7) += pinctrl-aspeed-g7-soc0.o pinctrl-aspeed-g7-soc1.o pinctrl-aspeed-g7-ltpi.o \ No newline at end of file diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c ---- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c 2026-04-08 18:03:48.322705101 +0000 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c 2026-07-17 08:44:55.303306350 +0000 @@ -17,6 +17,8 @@ #include "../pinctrl-utils.h" #include "pinctrl-aspeed.h" @@ -58014,8 +59410,8 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c b/drivers/pinctrl/aspeed ASPEED_SB_PINCONF(PIN_CONFIG_BIAS_DISABLE, Y1, Y4, SCU40C, 4), diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c 2026-04-08 18:03:48.322705101 +0000 -@@ -0,0 +1,1156 @@ ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c 2026-07-17 08:44:55.303306350 +0000 +@@ -0,0 +1,1528 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include @@ -58072,6 +59468,13 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a +#define SCU474 0x474 /* Multi-function Pin Control #30 */ +#define SCU478 0x478 /* Multi-function Pin Control #31 */ +#define SCU47C 0x47C ++#define SCU480 0x480 /* Disable Pull-Down Control #1 */ ++#define SCU484 0x484 /* Disable Pull-Down Control #2 */ ++#define SCU488 0x488 /* Disable Pull-Down Control #3 */ ++#define SCU48C 0x48C /* Disable Pull-Down Control #4 */ ++#define SCU490 0x490 /* Disable Pull-Down Control #5 */ ++#define SCU494 0x494 /* Disable Pull-Down Control #6 */ ++#define SCU498 0x498 /* Disable Pull-Down Control #7 */ +#define SCU4A0 0x4A0 /* Voltage Selection */ +#define SCU4C0 0x4C0 /* Driving Strength #0 A-I */ +#define SCU4C4 0x4C4 /* Driving Strength #1 J-K */ @@ -58221,16 +59624,24 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a + C1, + E5, + HOLE18, -+ C11, -+ D11, + HOLE19, + HOLE20, -+ C10, -+ D10, + HOLE21, + HOLE22, + HOLE23, + HOLE24, ++ HOLE25, ++ HOLE26, ++ C11, ++ D11, ++ HOLE27, ++ HOLE28, ++ C10, ++ D10, ++ HOLE29, ++ HOLE30, ++ HOLE31, ++ HOLE32, + D9, + E9, + C9, @@ -58241,10 +59652,10 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a + A12, + A11, + D7, -+ HOLE25, -+ HOLE26, -+ HOLE27, -+ HOLE28, ++ HOLE33, ++ HOLE34, ++ HOLE35, ++ HOLE36, + G2, + G1, + G4, @@ -58769,16 +60180,24 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a + PINCTRL_PIN(C1, "C1"), + PINCTRL_PIN(E5, "E5"), + PINCTRL_PIN(HOLE18, "HOLE18"), -+ PINCTRL_PIN(C11, "C11"), -+ PINCTRL_PIN(D11, "D11"), + PINCTRL_PIN(HOLE19, "HOLE19"), + PINCTRL_PIN(HOLE20, "HOLE20"), -+ PINCTRL_PIN(C10, "C10"), -+ PINCTRL_PIN(D10, "D10"), + PINCTRL_PIN(HOLE21, "HOLE21"), + PINCTRL_PIN(HOLE22, "HOLE22"), + PINCTRL_PIN(HOLE23, "HOLE23"), + PINCTRL_PIN(HOLE24, "HOLE24"), ++ PINCTRL_PIN(HOLE25, "HOLE25"), ++ PINCTRL_PIN(HOLE26, "HOLE26"), ++ PINCTRL_PIN(C11, "C11"), ++ PINCTRL_PIN(D11, "D11"), ++ PINCTRL_PIN(HOLE27, "HOLE27"), ++ PINCTRL_PIN(HOLE28, "HOLE28"), ++ PINCTRL_PIN(C10, "C10"), ++ PINCTRL_PIN(D10, "D10"), ++ PINCTRL_PIN(HOLE29, "HOLE29"), ++ PINCTRL_PIN(HOLE30, "HOLE30"), ++ PINCTRL_PIN(HOLE31, "HOLE31"), ++ PINCTRL_PIN(HOLE32, "HOLE32"), + PINCTRL_PIN(D9, "D9"), + PINCTRL_PIN(E9, "E9"), + PINCTRL_PIN(C9, "C9"), @@ -58789,10 +60208,10 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a + PINCTRL_PIN(A12, "A12"), + PINCTRL_PIN(A11, "A11"), + PINCTRL_PIN(D7, "D7"), -+ PINCTRL_PIN(HOLE25, "HOLE25"), -+ PINCTRL_PIN(HOLE26, "HOLE26"), -+ PINCTRL_PIN(HOLE27, "HOLE27"), -+ PINCTRL_PIN(HOLE28, "HOLE28"), ++ PINCTRL_PIN(HOLE33, "HOLE33"), ++ PINCTRL_PIN(HOLE34, "HOLE34"), ++ PINCTRL_PIN(HOLE35, "HOLE35"), ++ PINCTRL_PIN(HOLE36, "HOLE36"), + PINCTRL_PIN(G2, "G2"), + PINCTRL_PIN(G1, "G1"), + PINCTRL_PIN(G4, "G4"), @@ -59080,6 +60499,351 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a + PINCFG_PIN(E15), +}; + ++static struct aspeed_pin_config aspeed_g7_ltpi_configs[] = { ++ /* GPIOA */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { D6, D6 }, SCU4C0, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B7, B7 }, SCU4C0, GENMASK(3, 2) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A7, A7 }, SCU4C0, GENMASK(5, 4) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C6, C6 }, SCU4C0, GENMASK(7, 6) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B6, B6 }, SCU4C0, GENMASK(9, 8) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A6, A6 }, SCU4C0, GENMASK(11, 10) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C5, C5 }, SCU4C0, GENMASK(13, 12) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D5, D5 }, SCU4C0, GENMASK(15, 14) }, ++ { PIN_CONFIG_POWER_SOURCE, { D6, D5 }, SCU4A0, BIT_MASK(4) }, ++ /* GPIOI */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { U15, U15 }, SCU4C0, GENMASK(17, 16) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { U16, U16 }, SCU4C0, GENMASK(19, 18) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { T17, T17 }, SCU4C0, GENMASK(21, 20) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { P12, P12 }, SCU4C0, GENMASK(23, 22) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { R13, R13 }, SCU4C0, GENMASK(25, 24) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { T15, T15 }, SCU4C0, GENMASK(27, 26) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { T16, T16 }, SCU4C0, GENMASK(29, 28) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { R12, R12 }, SCU4C0, GENMASK(31, 30) }, ++ { PIN_CONFIG_POWER_SOURCE, { U15, R12 }, SCU4A0, BIT_MASK(12) }, ++ /* GPIOJ */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { R14, R14 }, SCU4C4, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { R17, R17 }, SCU4C4, GENMASK(3, 2) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { R15, R15 }, SCU4C4, GENMASK(5, 4) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { P13, P13 }, SCU4C4, GENMASK(7, 6) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { R16, R16 }, SCU4C4, GENMASK(9, 8) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { P14, P14 }, SCU4C4, GENMASK(11, 10) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { P17, P17 }, SCU4C4, GENMASK(13, 12) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { P16, P16 }, SCU4C4, GENMASK(15, 14) }, ++ /* GPIOK */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { P15, P15 }, SCU4C4, GENMASK(17, 16) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { N17, N17 }, SCU4C4, GENMASK(19, 18) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { N14, N14 }, SCU4C4, GENMASK(21, 20) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { M17, M17 }, SCU4C4, GENMASK(23, 22) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { N15, N15 }, SCU4C4, GENMASK(25, 24) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { N16, N16 }, SCU4C4, GENMASK(27, 26) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { L17, L17 }, SCU4C4, GENMASK(29, 28) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { K17, K17 }, SCU4C4, GENMASK(31, 30) }, ++ /* GPIOL */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { J17, J17 }, SCU4C8, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { M14, M14 }, SCU4C8, GENMASK(3, 2) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { M15, M15 }, SCU4C8, GENMASK(5, 4) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { M16, M16 }, SCU4C8, GENMASK(7, 6) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A4, A4 }, SCU4C8, GENMASK(9, 8) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B5, B5 }, SCU4C8, GENMASK(11, 10) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A5, A5 }, SCU4C8, GENMASK(13, 12) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B4, B4 }, SCU4C8, GENMASK(15, 14) }, ++ { PIN_CONFIG_POWER_SOURCE, { J17, B4 }, SCU4A0, BIT_MASK(15) }, ++ /* GPIOM */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { A10, A10 }, SCU4C8, GENMASK(17, 16) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B9, B9 }, SCU4C8, GENMASK(19, 18) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C7, C7 }, SCU4C8, GENMASK(21, 20) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { E8, E8 }, SCU4C8, GENMASK(23, 22) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { E7, E7 }, SCU4C8, GENMASK(25, 24) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B8, B8 }, SCU4C8, GENMASK(27, 26) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A9, A9 }, SCU4C8, GENMASK(29, 28) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A8, A8 }, SCU4C8, GENMASK(31, 30) }, ++ { PIN_CONFIG_POWER_SOURCE, { A10, A8 }, SCU4A0, BIT_MASK(16) }, ++ /* GPIOO */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { E13, E13 }, SCU4CC, GENMASK(17, 16) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C12, C12 }, SCU4CC, GENMASK(19, 18) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D12, D12 }, SCU4CC, GENMASK(21, 20) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { E12, E12 }, SCU4CC, GENMASK(23, 22) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D13, D13 }, SCU4CC, GENMASK(25, 24) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B12, B12 }, SCU4CC, GENMASK(29, 28) }, ++ { PIN_CONFIG_POWER_SOURCE, { E13, B12 }, SCU4A0, BIT_MASK(18) }, ++ /* GPIOP */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { D15, D15 }, SCU4D0, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B13, B13 }, SCU4D0, GENMASK(3, 2) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C14, C14 }, SCU4D0, GENMASK(5, 4) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C13, C13 }, SCU4D0, GENMASK(7, 6) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D14, D14 }, SCU4D0, GENMASK(9, 8) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { F13, F13 }, SCU4D0, GENMASK(11, 10) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { E14, E14 }, SCU4D0, GENMASK(13, 12) }, ++ { PIN_CONFIG_POWER_SOURCE, { D15, E14 }, SCU4A0, BIT_MASK(19) }, ++ /* GPIOQ */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { B1, B1 }, SCU4D0, GENMASK(21, 20) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C2, C2 }, SCU4D0, GENMASK(23, 22) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D3, D3 }, SCU4D0, GENMASK(25, 24) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C1, C1 }, SCU4D0, GENMASK(27, 26) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { E5, E5 }, SCU4D0, GENMASK(29, 28) }, ++ /* GPIOS */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { C11, C11 }, SCU4D4, GENMASK(17, 16) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D11, D11 }, SCU4D4, GENMASK(19, 18) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C10, C10 }, SCU4D4, GENMASK(25, 24) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D10, D10 }, SCU4D4, GENMASK(27, 26) }, ++ { PIN_CONFIG_POWER_SOURCE, { C11, D10 }, SCU4A0, BIT_MASK(22) }, ++ /* GPIOT */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { D9, D9 }, SCU4D8, GENMASK(5, 4) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { E9, E9 }, SCU4D8, GENMASK(7, 6) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C9, C9 }, SCU4D8, GENMASK(9, 8) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D8, D8 }, SCU4D8, GENMASK(11, 10) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C8, C8 }, SCU4D8, GENMASK(13, 12) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B11, B11 }, SCU4D8, GENMASK(15, 14) }, ++ { PIN_CONFIG_POWER_SOURCE, { D9, B11 }, SCU4A0, BIT_MASK(23) }, ++ /* GPIOU */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { B10, B10 }, SCU4D8, GENMASK(17, 16) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A12, A12 }, SCU4D8, GENMASK(19, 18) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A11, A11 }, SCU4D8, GENMASK(21, 20) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { D7, D7 }, SCU4D8, GENMASK(23, 22) }, ++ { PIN_CONFIG_POWER_SOURCE, { B10, D7 }, SCU4A0, BIT_MASK(24) }, ++ /* GPIOW */ ++ { PIN_CONFIG_DRIVE_STRENGTH, { C4, C4 }, SCU4DC, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A2, A2 }, SCU4DC, GENMASK(3, 2) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { C3, C3 }, SCU4DC, GENMASK(5, 4) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B2, B2 }, SCU4DC, GENMASK(7, 6) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { B3, B3 }, SCU4DC, GENMASK(9, 8) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { A3, A3 }, SCU4DC, GENMASK(11, 10) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { F2, F2 }, SCU4DC, GENMASK(13, 12) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, { F1, F1 }, SCU4DC, GENMASK(15, 14) }, ++ { PIN_CONFIG_POWER_SOURCE, { C4, F1 }, SCU4A0, BIT_MASK(26) }, ++ ++ ASPEED_PULL_DOWN_PINCONF(D6, SCU480, 0), ++ ASPEED_PULL_DOWN_PINCONF(B7, SCU480, 1), ++ ASPEED_PULL_DOWN_PINCONF(A7, SCU480, 2), ++ ASPEED_PULL_DOWN_PINCONF(C6, SCU480, 3), ++ ASPEED_PULL_DOWN_PINCONF(B6, SCU480, 4), ++ ASPEED_PULL_DOWN_PINCONF(A6, SCU480, 5), ++ ASPEED_PULL_DOWN_PINCONF(C5, SCU480, 6), ++ ASPEED_PULL_DOWN_PINCONF(D5, SCU480, 7), ++ ASPEED_PULL_DOWN_PINCONF(L15, SCU480, 8), ++ ASPEED_PULL_DOWN_PINCONF(H17, SCU480, 9), ++ ASPEED_PULL_DOWN_PINCONF(L16, SCU480, 10), ++ ASPEED_PULL_DOWN_PINCONF(K15, SCU480, 11), ++ ASPEED_PULL_DOWN_PINCONF(K16, SCU480, 12), ++ ASPEED_PULL_DOWN_PINCONF(K14, SCU480, 13), ++ ASPEED_PULL_DOWN_PINCONF(J14, SCU480, 14), ++ ASPEED_PULL_DOWN_PINCONF(J16, SCU480, 15), ++ ASPEED_PULL_DOWN_PINCONF(J15, SCU480, 16), ++ ASPEED_PULL_DOWN_PINCONF(G17, SCU480, 17), ++ ASPEED_PULL_DOWN_PINCONF(F17, SCU480, 18), ++ ASPEED_PULL_DOWN_PINCONF(D17, SCU480, 19), ++ ASPEED_PULL_DOWN_PINCONF(C17, SCU480, 20), ++ ASPEED_PULL_DOWN_PINCONF(H16, SCU480, 21), ++ ASPEED_PULL_DOWN_PINCONF(B17, SCU480, 22), ++ ASPEED_PULL_DOWN_PINCONF(E17, SCU480, 23), ++ ASPEED_PULL_DOWN_PINCONF(A16, SCU480, 24), ++ ASPEED_PULL_DOWN_PINCONF(B16, SCU480, 25), ++ ASPEED_PULL_DOWN_PINCONF(H14, SCU480, 26), ++ ASPEED_PULL_DOWN_PINCONF(A15, SCU480, 27), ++ ASPEED_PULL_DOWN_PINCONF(G16, SCU480, 28), ++ ASPEED_PULL_DOWN_PINCONF(F15, SCU480, 29), ++ ASPEED_PULL_DOWN_PINCONF(H15, SCU480, 30), ++ ASPEED_PULL_DOWN_PINCONF(D16, SCU480, 31), ++ ASPEED_PULL_DOWN_PINCONF(M2, SCU484, 0), ++ ASPEED_PULL_DOWN_PINCONF(M1, SCU484, 1), ++ ASPEED_PULL_DOWN_PINCONF(M3, SCU484, 2), ++ ASPEED_PULL_DOWN_PINCONF(M4, SCU484, 3), ++ ASPEED_PULL_DOWN_PINCONF(N1, SCU484, 4), ++ ASPEED_PULL_DOWN_PINCONF(N2, SCU484, 5), ++ ASPEED_PULL_DOWN_PINCONF(N3, SCU484, 6), ++ ASPEED_PULL_DOWN_PINCONF(L5, SCU484, 7), ++ ASPEED_PULL_DOWN_PINCONF(P2, SCU484, 8), ++ ASPEED_PULL_DOWN_PINCONF(P1, SCU484, 9), ++ ASPEED_PULL_DOWN_PINCONF(N4, SCU484, 10), ++ ASPEED_PULL_DOWN_PINCONF(N5, SCU484, 11), ++ ASPEED_PULL_DOWN_PINCONF(HOLE0, SCU484, 12), ++ ASPEED_PULL_DOWN_PINCONF(P3, SCU484, 13), ++ ASPEED_PULL_DOWN_PINCONF(R1, SCU484, 14), ++ ASPEED_PULL_DOWN_PINCONF(P4, SCU484, 15), ++ ASPEED_PULL_DOWN_PINCONF(J2, SCU484, 16), ++ ASPEED_PULL_DOWN_PINCONF(J1, SCU484, 17), ++ ASPEED_PULL_DOWN_PINCONF(K4, SCU484, 18), ++ ASPEED_PULL_DOWN_PINCONF(K2, SCU484, 19), ++ ASPEED_PULL_DOWN_PINCONF(K3, SCU484, 20), ++ ASPEED_PULL_DOWN_PINCONF(K1, SCU484, 21), ++ ASPEED_PULL_DOWN_PINCONF(L4, SCU484, 22), ++ ASPEED_PULL_DOWN_PINCONF(L2, SCU484, 23), ++ ASPEED_PULL_DOWN_PINCONF(L1, SCU484, 24), ++ ASPEED_PULL_DOWN_PINCONF(L3, SCU484, 25), ++ ASPEED_PULL_DOWN_PINCONF(A14, SCU484, 26), ++ ASPEED_PULL_DOWN_PINCONF(B14, SCU484, 27), ++ ASPEED_PULL_DOWN_PINCONF(HOLE1, SCU484, 28), ++ ASPEED_PULL_DOWN_PINCONF(HOLE2, SCU484, 29), ++ ASPEED_PULL_DOWN_PINCONF(HOLE3, SCU484, 30), ++ ASPEED_PULL_DOWN_PINCONF(HOLE4, SCU484, 31), ++ ASPEED_PULL_DOWN_PINCONF(U15, SCU488, 0), ++ ASPEED_PULL_DOWN_PINCONF(U16, SCU488, 1), ++ ASPEED_PULL_DOWN_PINCONF(T17, SCU488, 2), ++ ASPEED_PULL_DOWN_PINCONF(P12, SCU488, 3), ++ ASPEED_PULL_DOWN_PINCONF(R13, SCU488, 4), ++ ASPEED_PULL_DOWN_PINCONF(T15, SCU488, 5), ++ ASPEED_PULL_DOWN_PINCONF(T16, SCU488, 6), ++ ASPEED_PULL_DOWN_PINCONF(R12, SCU488, 7), ++ ASPEED_PULL_DOWN_PINCONF(R14, SCU488, 8), ++ ASPEED_PULL_DOWN_PINCONF(R17, SCU488, 9), ++ ASPEED_PULL_DOWN_PINCONF(R15, SCU488, 10), ++ ASPEED_PULL_DOWN_PINCONF(P13, SCU488, 11), ++ ASPEED_PULL_DOWN_PINCONF(R16, SCU488, 12), ++ ASPEED_PULL_DOWN_PINCONF(P14, SCU488, 13), ++ ASPEED_PULL_DOWN_PINCONF(P17, SCU488, 14), ++ ASPEED_PULL_DOWN_PINCONF(P16, SCU488, 15), ++ ASPEED_PULL_DOWN_PINCONF(P15, SCU488, 16), ++ ASPEED_PULL_DOWN_PINCONF(N17, SCU488, 17), ++ ASPEED_PULL_DOWN_PINCONF(N14, SCU488, 18), ++ ASPEED_PULL_DOWN_PINCONF(M17, SCU488, 19), ++ ASPEED_PULL_DOWN_PINCONF(N15, SCU488, 20), ++ ASPEED_PULL_DOWN_PINCONF(N16, SCU488, 21), ++ ASPEED_PULL_DOWN_PINCONF(L17, SCU488, 22), ++ ASPEED_PULL_DOWN_PINCONF(K17, SCU488, 23), ++ ASPEED_PULL_DOWN_PINCONF(J17, SCU488, 24), ++ ASPEED_PULL_DOWN_PINCONF(M14, SCU488, 25), ++ ASPEED_PULL_DOWN_PINCONF(M15, SCU488, 26), ++ ASPEED_PULL_DOWN_PINCONF(M16, SCU488, 27), ++ ASPEED_PULL_DOWN_PINCONF(A4, SCU488, 28), ++ ASPEED_PULL_DOWN_PINCONF(B5, SCU488, 29), ++ ASPEED_PULL_DOWN_PINCONF(A5, SCU488, 30), ++ ASPEED_PULL_DOWN_PINCONF(B4, SCU488, 31), ++ ASPEED_PULL_DOWN_PINCONF(A10, SCU48C, 0), ++ ASPEED_PULL_DOWN_PINCONF(B9, SCU48C, 1), ++ ASPEED_PULL_DOWN_PINCONF(C7, SCU48C, 2), ++ ASPEED_PULL_DOWN_PINCONF(E8, SCU48C, 3), ++ ASPEED_PULL_DOWN_PINCONF(E7, SCU48C, 4), ++ ASPEED_PULL_DOWN_PINCONF(B8, SCU48C, 5), ++ ASPEED_PULL_DOWN_PINCONF(A9, SCU48C, 6), ++ ASPEED_PULL_DOWN_PINCONF(A8, SCU48C, 7), ++ ASPEED_PULL_DOWN_PINCONF(HOLE5, SCU48C, 8), ++ ASPEED_PULL_DOWN_PINCONF(HOLE6, SCU48C, 9), ++ ASPEED_PULL_DOWN_PINCONF(HOLE7, SCU48C, 10), ++ ASPEED_PULL_DOWN_PINCONF(HOLE8, SCU48C, 11), ++ ASPEED_PULL_DOWN_PINCONF(HOLE9, SCU48C, 12), ++ ASPEED_PULL_DOWN_PINCONF(HOLE10, SCU48C, 13), ++ ASPEED_PULL_DOWN_PINCONF(HOLE11, SCU48C, 14), ++ ASPEED_PULL_DOWN_PINCONF(HOLE12, SCU48C, 15), ++ ASPEED_PULL_DOWN_PINCONF(E13, SCU48C, 16), ++ ASPEED_PULL_DOWN_PINCONF(C12, SCU48C, 17), ++ ASPEED_PULL_DOWN_PINCONF(D12, SCU48C, 18), ++ ASPEED_PULL_DOWN_PINCONF(E12, SCU48C, 19), ++ ASPEED_PULL_DOWN_PINCONF(D13, SCU48C, 20), ++ ASPEED_PULL_DOWN_PINCONF(HOLE13, SCU48C, 21), ++ ASPEED_PULL_DOWN_PINCONF(B12, SCU48C, 22), ++ ASPEED_PULL_DOWN_PINCONF(HOLE14, SCU48C, 23), ++ ASPEED_PULL_DOWN_PINCONF(D15, SCU48C, 24), ++ ASPEED_PULL_DOWN_PINCONF(B13, SCU48C, 25), ++ ASPEED_PULL_DOWN_PINCONF(C14, SCU48C, 26), ++ ASPEED_PULL_DOWN_PINCONF(C13, SCU48C, 27), ++ ASPEED_PULL_DOWN_PINCONF(D14, SCU48C, 28), ++ ASPEED_PULL_DOWN_PINCONF(F13, SCU48C, 29), ++ ASPEED_PULL_DOWN_PINCONF(E14, SCU48C, 30), ++ ASPEED_PULL_DOWN_PINCONF(HOLE15, SCU48C, 31), ++ ASPEED_PULL_DOWN_PINCONF(HOLE16, SCU490, 0), ++ ASPEED_PULL_DOWN_PINCONF(HOLE17, SCU490, 1), ++ ASPEED_PULL_DOWN_PINCONF(B1, SCU490, 2), ++ ASPEED_PULL_DOWN_PINCONF(C2, SCU490, 3), ++ ASPEED_PULL_DOWN_PINCONF(D3, SCU490, 4), ++ ASPEED_PULL_DOWN_PINCONF(C1, SCU490, 5), ++ ASPEED_PULL_DOWN_PINCONF(E5, SCU490, 6), ++ ASPEED_PULL_DOWN_PINCONF(HOLE18, SCU490, 7), ++ ASPEED_PULL_DOWN_PINCONF(HOLE19, SCU490, 8), ++ ASPEED_PULL_DOWN_PINCONF(HOLE20, SCU490, 9), ++ ASPEED_PULL_DOWN_PINCONF(HOLE21, SCU490, 10), ++ ASPEED_PULL_DOWN_PINCONF(HOLE22, SCU490, 11), ++ ASPEED_PULL_DOWN_PINCONF(HOLE23, SCU490, 12), ++ ASPEED_PULL_DOWN_PINCONF(HOLE24, SCU490, 13), ++ ASPEED_PULL_DOWN_PINCONF(HOLE25, SCU490, 14), ++ ASPEED_PULL_DOWN_PINCONF(HOLE26, SCU490, 15), ++ ASPEED_PULL_DOWN_PINCONF(C11, SCU490, 16), ++ ASPEED_PULL_DOWN_PINCONF(D11, SCU490, 17), ++ ASPEED_PULL_DOWN_PINCONF(HOLE27, SCU490, 18), ++ ASPEED_PULL_DOWN_PINCONF(HOLE28, SCU490, 19), ++ ASPEED_PULL_DOWN_PINCONF(C10, SCU490, 20), ++ ASPEED_PULL_DOWN_PINCONF(D10, SCU490, 21), ++ ASPEED_PULL_DOWN_PINCONF(HOLE29, SCU490, 22), ++ ASPEED_PULL_DOWN_PINCONF(HOLE30, SCU490, 23), ++ ASPEED_PULL_DOWN_PINCONF(HOLE31, SCU490, 24), ++ ASPEED_PULL_DOWN_PINCONF(HOLE32, SCU490, 25), ++ ASPEED_PULL_DOWN_PINCONF(D9, SCU490, 26), ++ ASPEED_PULL_DOWN_PINCONF(E9, SCU490, 27), ++ ASPEED_PULL_DOWN_PINCONF(C9, SCU490, 28), ++ ASPEED_PULL_DOWN_PINCONF(D8, SCU490, 29), ++ ASPEED_PULL_DOWN_PINCONF(C8, SCU490, 30), ++ ASPEED_PULL_DOWN_PINCONF(B11, SCU490, 31), ++ ASPEED_PULL_DOWN_PINCONF(B10, SCU494, 0), ++ ASPEED_PULL_DOWN_PINCONF(A12, SCU494, 1), ++ ASPEED_PULL_DOWN_PINCONF(A11, SCU494, 2), ++ ASPEED_PULL_DOWN_PINCONF(D7, SCU494, 3), ++ ASPEED_PULL_DOWN_PINCONF(HOLE33, SCU494, 4), ++ ASPEED_PULL_DOWN_PINCONF(HOLE34, SCU494, 5), ++ ASPEED_PULL_DOWN_PINCONF(HOLE35, SCU494, 6), ++ ASPEED_PULL_DOWN_PINCONF(HOLE36, SCU494, 7), ++ ASPEED_PULL_DOWN_PINCONF(G2, SCU494, 8), ++ ASPEED_PULL_DOWN_PINCONF(G1, SCU494, 9), ++ ASPEED_PULL_DOWN_PINCONF(G4, SCU494, 10), ++ ASPEED_PULL_DOWN_PINCONF(H3, SCU494, 11), ++ ASPEED_PULL_DOWN_PINCONF(H2, SCU494, 12), ++ ASPEED_PULL_DOWN_PINCONF(H1, SCU494, 13), ++ ASPEED_PULL_DOWN_PINCONF(J4, SCU494, 14), ++ ASPEED_PULL_DOWN_PINCONF(J3, SCU494, 15), ++ ASPEED_PULL_DOWN_PINCONF(C4, SCU494, 16), ++ ASPEED_PULL_DOWN_PINCONF(A2, SCU494, 17), ++ ASPEED_PULL_DOWN_PINCONF(C3, SCU494, 18), ++ ASPEED_PULL_DOWN_PINCONF(B2, SCU494, 19), ++ ASPEED_PULL_DOWN_PINCONF(B3, SCU494, 20), ++ ASPEED_PULL_DOWN_PINCONF(A3, SCU494, 21), ++ ASPEED_PULL_DOWN_PINCONF(F2, SCU494, 22), ++ ASPEED_PULL_DOWN_PINCONF(F1, SCU494, 23), ++ ASPEED_PULL_DOWN_PINCONF(D2, SCU494, 24), ++ ASPEED_PULL_DOWN_PINCONF(D1, SCU494, 25), ++ ASPEED_PULL_DOWN_PINCONF(F4, SCU494, 26), ++ ASPEED_PULL_DOWN_PINCONF(E4, SCU494, 27), ++ ASPEED_PULL_DOWN_PINCONF(E2, SCU494, 28), ++ ASPEED_PULL_DOWN_PINCONF(E1, SCU494, 29), ++ ASPEED_PULL_DOWN_PINCONF(F3, SCU494, 30), ++ ASPEED_PULL_DOWN_PINCONF(G5, SCU494, 31), ++ ASPEED_PULL_DOWN_PINCONF(T4, SCU498, 0), ++ ASPEED_PULL_DOWN_PINCONF(T3, SCU498, 1), ++ ASPEED_PULL_DOWN_PINCONF(R5, SCU498, 2), ++ ASPEED_PULL_DOWN_PINCONF(R6, SCU498, 3), ++ ASPEED_PULL_DOWN_PINCONF(T2, SCU498, 4), ++ ASPEED_PULL_DOWN_PINCONF(T5, SCU498, 5), ++ ASPEED_PULL_DOWN_PINCONF(U2, SCU498, 6), ++ ASPEED_PULL_DOWN_PINCONF(R7, SCU498, 7), ++ ASPEED_PULL_DOWN_PINCONF(P9, SCU498, 8), ++ ASPEED_PULL_DOWN_PINCONF(U4, SCU498, 9), ++ ASPEED_PULL_DOWN_PINCONF(U5, SCU498, 10), ++ ASPEED_PULL_DOWN_PINCONF(U6, SCU498, 11), ++ ASPEED_PULL_DOWN_PINCONF(T7, SCU498, 12), ++ ASPEED_PULL_DOWN_PINCONF(U7, SCU498, 13), ++ ASPEED_PULL_DOWN_PINCONF(R10, SCU498, 14), ++ ASPEED_PULL_DOWN_PINCONF(R11, SCU498, 15), ++ ASPEED_PULL_DOWN_PINCONF(C16, SCU498, 16), ++ ASPEED_PULL_DOWN_PINCONF(F16, SCU498, 17), ++ ASPEED_PULL_DOWN_PINCONF(B15, SCU498, 18), ++ ASPEED_PULL_DOWN_PINCONF(G15, SCU498, 19), ++ ASPEED_PULL_DOWN_PINCONF(G14, SCU498, 20), ++ ASPEED_PULL_DOWN_PINCONF(F14, SCU498, 21), ++ ASPEED_PULL_DOWN_PINCONF(E15, SCU498, 22), ++ ASPEED_PULL_DOWN_PINCONF(C15, SCU498, 23), ++}; ++ ++static const struct aspeed_pin_config_map aspeed_g7_ltpi_pin_config_map[] = { ++ { PIN_CONFIG_BIAS_PULL_DOWN, 0, 1, BIT_MASK(0) }, ++ { PIN_CONFIG_BIAS_PULL_DOWN, -1, 0, BIT_MASK(0) }, ++ { PIN_CONFIG_BIAS_PULL_UP, 0, 1, BIT_MASK(0) }, ++ { PIN_CONFIG_BIAS_PULL_UP, -1, 0, BIT_MASK(0) }, ++ { PIN_CONFIG_BIAS_DISABLE, -1, 1, BIT_MASK(0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, 0, 0, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, 1, 1, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, 2, 2, GENMASK(1, 0) }, ++ { PIN_CONFIG_DRIVE_STRENGTH, 3, 3, GENMASK(1, 0) }, ++ { PIN_CONFIG_POWER_SOURCE, 3300, 0, BIT_MASK(0) }, ++ { PIN_CONFIG_POWER_SOURCE, 1800, 1, BIT_MASK(0) }, ++}; ++ +static int aspeed_g7_ltpi_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, u32 *num_maps) @@ -59148,6 +60912,10 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a + ltpi_pinctrl_data->pinmux.nfunctions = ARRAY_SIZE(aspeed_g7_ltpi_funcs); + ltpi_pinctrl_data->pinmux.configs_g7 = pin_cfg; + ltpi_pinctrl_data->pinmux.nconfigs_g7 = ARRAY_SIZE(pin_cfg); ++ ltpi_pinctrl_data->configs = aspeed_g7_ltpi_configs; ++ ltpi_pinctrl_data->nconfigs = ARRAY_SIZE(aspeed_g7_ltpi_configs); ++ ltpi_pinctrl_data->confmaps = aspeed_g7_ltpi_pin_config_map; ++ ltpi_pinctrl_data->nconfmaps = ARRAY_SIZE(aspeed_g7_ltpi_pin_config_map); + + return aspeed_pinctrl_probe(pdev, &aspeed_g7_ltpi_pinctrl_desc, ltpi_pinctrl_data); +} @@ -59174,7 +60942,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-ltpi.c b/drivers/pinctrl/a +arch_initcall(aspeed_g7_ltpi_pinctrl_register); diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c 2026-04-08 18:03:48.322705101 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c 2026-07-17 08:44:55.303306350 +0000 @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 + @@ -59681,8 +61449,8 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c b/drivers/pinctrl/a +arch_initcall(aspeed_g7_soc0_pinctrl_register); diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c 2026-04-08 18:03:48.323705083 +0000 -@@ -0,0 +1,2533 @@ ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c 2026-07-17 08:44:55.303306350 +0000 +@@ -0,0 +1,2537 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include @@ -60202,7 +61970,8 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +GROUP_DECL(SPI2CS1, A26); +GROUP_DECL(THRU2, A19, A18); +GROUP_DECL(THRU3, B25, F26); -+GROUP_DECL(JTAGM1, D12, F10, E11, F11, F13); ++GROUP_DECL(JTAGM1, F10, E11, F11, F13); ++GROUP_DECL(JTAGM1TRST, D12); +GROUP_DECL(MDIO0, B9, M14); +GROUP_DECL(MDIO1, C9, C10); +GROUP_DECL(MDIO2, E10, E13); @@ -60221,7 +61990,8 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +GROUP_DECL(I2CF0, F12, E12, J12, G7); +GROUP_DECL(I2CF1, E9, F9, F8, M13); +GROUP_DECL(I2CF2, F7, D8, E8, L12); -+GROUP_DECL(CANBUS, G7, G8, G9); ++GROUP_DECL(CANBUS, G8, G9); ++GROUP_DECL(CANBUSSTBY, G7); +GROUP_DECL(USBUART, G10, K12); +GROUP_DECL(HBLED, V24); +GROUP_DECL(MACLINK0, U21); @@ -60479,6 +62249,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a + ASPEED_PINCTRL_GROUP(THRU2), + ASPEED_PINCTRL_GROUP(THRU3), + ASPEED_PINCTRL_GROUP(JTAGM1), ++ ASPEED_PINCTRL_GROUP(JTAGM1TRST), + ASPEED_PINCTRL_GROUP(MDIO0), + ASPEED_PINCTRL_GROUP(MDIO1), + ASPEED_PINCTRL_GROUP(MDIO2), @@ -60498,6 +62269,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a + ASPEED_PINCTRL_GROUP(I2CF1), + ASPEED_PINCTRL_GROUP(I2CF2), + ASPEED_PINCTRL_GROUP(CANBUS), ++ ASPEED_PINCTRL_GROUP(CANBUSSTBY), + ASPEED_PINCTRL_GROUP(USBUART), + ASPEED_PINCTRL_GROUP(HBLED), + ASPEED_PINCTRL_GROUP(MACLINK0), @@ -60695,7 +62467,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +FUNC_DECL_(SPI2CS1, "SPI2CS1"); +FUNC_DECL_(THRU2, "THRU2"); +FUNC_DECL_(THRU3, "THRU3"); -+FUNC_DECL_(JTAGM1, "JTAGM1"); ++FUNC_DECL_(JTAGM1, "JTAGM1", "JTAGM1TRST"); +FUNC_DECL_(MDIO0, "MDIO0"); +FUNC_DECL_(MDIO1, "MDIO1"); +FUNC_DECL_(MDIO2, "MDIO2"); @@ -60713,7 +62485,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +FUNC_DECL_(I2CF0, "I2CF0"); +FUNC_DECL_(I2CF1, "I2CF1"); +FUNC_DECL_(I2CF2, "I2CF2"); -+FUNC_DECL_(CANBUS, "CANBUS"); ++FUNC_DECL_(CANBUS, "CANBUS", "CANBUSSTBY"); +FUNC_DECL_(USBUART, "USBUART"); +FUNC_DECL_(HBLED, "HBLED"); +FUNC_DECL_(MACLINK0, "MACLINK0"); @@ -61471,7 +63243,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +FUNCFG_DESCL(E10, PIN_CFG(MDIO2, SCU440, GENMASK(2, 0), 1), + PIN_CFG(PE2SGRSTN, SCU440, GENMASK(2, 0), 2)); +FUNCFG_DESCL(E13, PIN_CFG(MDIO2, SCU440, GENMASK(6, 4), (1 << 4))); -+FUNCFG_DESCL(D12, PIN_CFG(JTAGM1, SCU440, GENMASK(10, 8), (1 << 8))); ++FUNCFG_DESCL(D12, PIN_CFG(JTAGM1TRST, SCU440, GENMASK(10, 8), (1 << 8))); +FUNCFG_DESCL(F10, PIN_CFG(JTAGM1, SCU440, GENMASK(14, 12), (1 << 12))); +FUNCFG_DESCL(E11, PIN_CFG(JTAGM1, SCU440, GENMASK(18, 16), (1 << 16))); +FUNCFG_DESCL(F11, PIN_CFG(JTAGM1, SCU440, GENMASK(22, 20), (1 << 20))); @@ -61576,7 +63348,7 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +FUNCFG_DESCL(J12, PIN_CFG(I2C9, SCU45C, GENMASK(10, 8), (1 << 8)), + PIN_CFG(I2CF0, SCU45C, GENMASK(10, 8), (5 << 8))); +FUNCFG_DESCL(G7, PIN_CFG(I2C9, SCU45C, GENMASK(14, 12), (1 << 12)), -+ PIN_CFG(CANBUS, SCU45C, GENMASK(14, 12), (2 << 12)), ++ PIN_CFG(CANBUSSTBY, SCU45C, GENMASK(14, 12), (2 << 12)), + PIN_CFG(I2CF0, SCU45C, GENMASK(14, 12), (5 << 12))); +FUNCFG_DESCL(G8, PIN_CFG(I2C10, SCU45C, GENMASK(18, 16), (1 << 16)), + PIN_CFG(CANBUS, SCU45C, GENMASK(18, 16), (2 << 16))); @@ -62217,8 +63989,8 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/a +} +arch_initcall(aspeed_g7_soc1_pinctrl_register); diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c ---- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c 2026-04-08 18:03:48.323705083 +0000 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c 2026-07-17 08:44:55.303306350 +0000 @@ -285,6 +285,32 @@ return 0; } @@ -62281,8 +64053,8 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pi struct pinctrl_desc *pdesc, struct aspeed_pinctrl_data *pdata) diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pinctrl-aspeed.h ---- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h 2026-04-08 18:03:48.323705083 +0000 +--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h 2026-07-17 08:44:55.303306350 +0000 @@ -70,15 +70,15 @@ struct regmap *scu; @@ -62315,8 +64087,8 @@ diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pi struct pinctrl_desc *pdesc, struct aspeed_pinctrl_data *pdata); diff --git a/drivers/pinctrl/aspeed/pinmux-aspeed.h b/drivers/pinctrl/aspeed/pinmux-aspeed.h ---- a/drivers/pinctrl/aspeed/pinmux-aspeed.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pinctrl/aspeed/pinmux-aspeed.h 2026-04-08 18:03:48.323705083 +0000 +--- a/drivers/pinctrl/aspeed/pinmux-aspeed.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pinctrl/aspeed/pinmux-aspeed.h 2026-07-17 08:44:55.304306335 +0000 @@ -792,6 +792,33 @@ const struct aspeed_sig_expr *expr, bool enabled); }; @@ -62369,8 +64141,8 @@ diff --git a/drivers/pinctrl/aspeed/pinmux-aspeed.h b/drivers/pinctrl/aspeed/pin int aspeed_sig_desc_eval(const struct aspeed_sig_desc *desc, bool enabled, diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c ---- a/drivers/pwm/core.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/pwm/core.c 2026-04-08 18:03:49.301687214 +0000 +--- a/drivers/pwm/core.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/pwm/core.c 2026-07-17 08:44:56.378289781 +0000 @@ -426,9 +426,8 @@ * chip. A negative error code is returned if the index is not valid for the * specified PWM chip or if the PWM device cannot be requested. @@ -62392,8 +64164,8 @@ diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c struct pwm_device * of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig ---- a/drivers/reset/Kconfig 2026-04-08 18:03:23.253162611 +0000 -+++ b/drivers/reset/Kconfig 2026-04-08 18:03:35.468939804 +0000 +--- a/drivers/reset/Kconfig 2026-07-17 08:44:32.481658805 +0000 ++++ b/drivers/reset/Kconfig 2026-07-17 08:44:42.494503778 +0000 @@ -22,6 +22,13 @@ This option enables support for the external reset functions for peripheral PHYs on the Altera Arria10 System Resource Chip. @@ -62409,8 +64181,8 @@ diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig bool "AR71xx Reset Driver" if COMPILE_TEST default ATH79 diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile ---- a/drivers/reset/Makefile 2026-04-08 18:03:23.253162611 +0000 -+++ b/drivers/reset/Makefile 2026-04-08 18:03:40.401849820 +0000 +--- a/drivers/reset/Makefile 2026-07-17 08:44:32.481658805 +0000 ++++ b/drivers/reset/Makefile 2026-07-17 08:44:47.633424569 +0000 @@ -5,6 +5,7 @@ obj-y += sti/ obj-y += tegra/ @@ -62421,7 +64193,7 @@ diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o diff --git a/drivers/reset/reset-aspeed.c b/drivers/reset/reset-aspeed.c --- a/drivers/reset/reset-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/reset/reset-aspeed.c 2026-04-08 18:03:48.301705484 +0000 ++++ b/drivers/reset/reset-aspeed.c 2026-07-17 08:44:55.280306705 +0000 @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -62734,8 +64506,8 @@ diff --git a/drivers/reset/reset-aspeed.c b/drivers/reset/reset-aspeed.c +MODULE_DESCRIPTION("ASPEED SoC Reset Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c ---- a/drivers/rtc/rtc-aspeed.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/rtc/rtc-aspeed.c 2026-04-08 18:03:48.273705996 +0000 +--- a/drivers/rtc/rtc-aspeed.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/rtc/rtc-aspeed.c 2026-07-17 08:44:55.253307121 +0000 @@ -10,14 +10,64 @@ struct aspeed_rtc { struct rtc_device *rtc_dev; @@ -63042,8 +64814,8 @@ diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c }; MODULE_DEVICE_TABLE(of, aspeed_rtc_match); diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig ---- a/drivers/soc/aspeed/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/Kconfig 2026-04-08 18:03:48.310705320 +0000 +--- a/drivers/soc/aspeed/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/soc/aspeed/Kconfig 2026-07-17 08:44:55.289306566 +0000 @@ -4,6 +4,12 @@ menu "ASPEED SoC drivers" @@ -63088,17 +64860,16 @@ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig config ASPEED_P2A_CTRL tristate "ASPEED P2A (VGA MMIO to BMC) bridge control" select REGMAP -@@ -52,6 +75,100 @@ +@@ -52,6 +75,98 @@ help Say yes to support decoding of ASPEED BMC information. +config ASPEED_SBC + bool "ASPEED Secure Boot Controller driver" -+ depends on ARM + default MACH_ASPEED_G6 + help + Say yes to provide information about the secure boot controller in -+ debugfs. This is only for AST2600 (ARM32). ++ debugfs. + +config ASPEED_XDMA + tristate "ASPEED XDMA Engine Driver" @@ -63158,9 +64929,6 @@ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig + SOCs. The UDMA engine can perform UART DMA operations between the memory + buffer and the UART/VUART devices. + -+config ASPEED_PCIE_MMBI -+ tristate "ASPEED PCIE MMBI" -+ +config ASPEED_OTP + tristate + help @@ -63185,13 +64953,15 @@ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig + +source "drivers/soc/aspeed/espi/Kconfig" +source "drivers/soc/aspeed/rvas/Kconfig" ++ ++source "drivers/soc/aspeed/mmbi/Kconfig" + endmenu endif diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile ---- a/drivers/soc/aspeed/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/Makefile 2026-04-08 18:03:48.360704407 +0000 +--- a/drivers/soc/aspeed/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/soc/aspeed/Makefile 2026-07-17 08:44:55.340305780 +0000 @@ -1,6 +1,23 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ASPEED_BMC_DEV) += aspeed-bmc-dev.o @@ -63211,15 +64981,15 @@ diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile +obj-$(CONFIG_ARCH_ASPEED) += aspeed-usb-hp.o +obj-$(CONFIG_ASPEED_MCTP) += aspeed-mctp.o +obj-$(CONFIG_ASPEED_DISP_INTF) += aspeed-disp-intf.o -+obj-$(CONFIG_ASPEED_PCIE_MMBI) += aspeed-pcie-mmbi.o +obj-$(CONFIG_ASPEED_MBOX) += aspeed-mbox.o +obj-$(CONFIG_AST2600_OTP) += ast2600-otp.o +obj-$(CONFIG_AST2700_OTP) += ast2700-otp.o +obj-y += espi/ ++obj-y += mmbi/ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc-dev.c --- a/drivers/soc/aspeed/aspeed-bmc-dev.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-bmc-dev.c 2026-04-08 18:03:48.310705320 +0000 -@@ -0,0 +1,703 @@ ++++ b/drivers/soc/aspeed/aspeed-bmc-dev.c 2026-07-17 08:44:55.289306566 +0000 +@@ -0,0 +1,701 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. + @@ -63904,8 +65674,6 @@ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc + devm_free_irq(&pdev->dev, bmc_device->irq, bmc_device); + devm_free_irq(&pdev->dev, bmc_device->pcie_irq, bmc_device); + -+ iounmap(bmc_device->reg_base); -+ + devm_kfree(&pdev->dev, bmc_device); +} + @@ -63925,7 +65693,7 @@ diff --git a/drivers/soc/aspeed/aspeed-bmc-dev.c b/drivers/soc/aspeed/aspeed-bmc +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/aspeed/aspeed-disp-intf.c b/drivers/soc/aspeed/aspeed-disp-intf.c --- a/drivers/soc/aspeed/aspeed-disp-intf.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-disp-intf.c 2026-04-08 18:03:48.310705320 +0000 ++++ b/drivers/soc/aspeed/aspeed-disp-intf.c 2026-07-17 08:44:55.289306566 +0000 @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. @@ -64184,8 +65952,8 @@ diff --git a/drivers/soc/aspeed/aspeed-disp-intf.c b/drivers/soc/aspeed/aspeed-d +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspeed-host-bmc-dev.c --- a/drivers/soc/aspeed/aspeed-host-bmc-dev.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-host-bmc-dev.c 2026-04-08 18:03:48.310705320 +0000 -@@ -0,0 +1,1435 @@ ++++ b/drivers/soc/aspeed/aspeed-host-bmc-dev.c 2026-07-17 08:44:55.289306566 +0000 +@@ -0,0 +1,828 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. + @@ -64212,8 +65980,9 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee +#include +#include +#include -+ -+#include "aspeed-pcie-mmbi.h" ++#if IS_ENABLED(CONFIG_ASPEED_MMBI) ++#include "mmbi/aspeed-mmbi.h" ++#endif + +#define PCI_BMC_HOST2BMC_Q1 0x30000 +#define PCI_BMC_HOST2BMC_Q2 0x30010 @@ -64247,16 +66016,10 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + +static DEFINE_IDA(bmc_device_ida); + -+#define MMBI_MAX_INST 6 +#define VUART_MAX_PARMS 2 +#define ASPEED_QUEUE_NUM 2 +#define MAX_MSI_NUM 8 + -+enum aspeed_platform_id { -+ ASPEED, -+ ASPEED_AST2700_SOC1, -+}; -+ +enum queue_index { + QUEUE1 = 0, + QUEUE2, @@ -64267,17 +66030,11 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + MBX_MSI, + VUART0_MSI, + VUART1_MSI, -+ MMBI0_MSI, -+ MMBI1_MSI, -+ MMBI2_MSI, -+ MMBI3_MSI, +}; + +/* Match msi_index */ +static int ast2600_msi_idx_table[MAX_MSI_NUM] = { 4, 21, 16, 15 }; -+static int ast2700_soc0_msi_idx_table[MAX_MSI_NUM] = { 0, 11, 6, 5, 28, 29, 30, 31 }; -+/* ARRAY = MMIB0_MSI, MMBI1_MSI, MMBI2_MSI, MMBI3_MSI, MMBI4_MSI, MMBI5_MSI */ -+static int ast2700_soc1_msi_idx_table[MAX_MSI_NUM] = { 1, 2, 3, 4, 5, 6 }; ++static int ast2700_soc0_msi_idx_table[MAX_MSI_NUM] = { 0, 11, 6, 5}; + +struct aspeed_platform { + int (*setup)(struct pci_dev *pdev); @@ -64293,17 +66050,47 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + struct aspeed_pci_bmc_dev *pci_bmc_device; +}; + -+struct aspeed_pcie_mmbi { -+ resource_size_t base; -+ resource_size_t mem_size; -+ void __iomem *mem; -+ u32 segment_size; ++#if IS_ENABLED(CONFIG_ASPEED_MMBI) ++struct aspeed_host_pcie_mmbi_ins { ++ struct mmbi_ins_desc mmbi_desc; + int irq; -+ int id; -+ struct aspeed_mmbi_channel chan; ++ int msi_index; ++ int instance_index; ++ int bar_index; + const char *dev_name; ++ bool valid; ++}; ++ ++struct aspeed_host_pcie_mmbi { ++ struct device *dev; ++ struct aspeed_host_pcie_mmbi_ins instances[PCI_STD_NUM_BARS]; ++ int msi_nums; ++}; ++ ++/* ++ * app_interface: per-channel application interface selection. ++ * 0 = ioctl (MMBI_APP_INTF_IOCTL) ++ * 1 = mctp-netdev (MMBI_APP_INTF_MCTP_NETDEV) ++ * ++ * One value applies to every channel. Multiple values map by channel index, ++ * and omitted channels keep their defaults. ++ * Examples: ++ * app_interface=1 -> all channels use mctp-netdev ++ * app_interface=1,0,0,0,1 -> ch0/ch4 use mctp-netdev ++ * Invalid values fall back to the default map. ++ */ ++static const uint app_interface_defaults[MMBI_MAX_CHANNELS] = { ++ [0] = MMBI_APP_INTF_MCTP_NETDEV, +}; + ++static uint app_interface[MMBI_MAX_CHANNELS] = { [0] = MMBI_APP_INTF_MCTP_NETDEV }; ++static int app_interface_count; ++module_param_array(app_interface, uint, &app_interface_count, 0444); ++MODULE_PARM_DESC(app_interface, ++ "App interface per channel: 0=ioctl, 1=mctp-netdev; one value applies to all channels"); ++ ++#endif ++ +struct aspeed_pci_bmc_dev { + struct device *dev; + struct miscdevice miscdev; @@ -64331,12 +66118,9 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + * The index of array is using to enum msi_index + */ + int *msi_idx_table; -+ -+ bool ast2700_soc1; -+ -+ /* AST2700 MMBI */ -+ struct aspeed_pcie_mmbi mmbi[MMBI_MAX_INST]; -+ int mmbi_start_msi; ++#if IS_ENABLED(CONFIG_ASPEED_MMBI) ++ struct aspeed_host_pcie_mmbi mmbi; ++#endif +}; + +#define PCIE_DEVICE_SIO_ADDR (0x2E * 4) @@ -64344,465 +66128,6 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + +#define DRIVER_NAME "aspeed-host-bmc-dev" + -+static int mmbi_desc_init(struct aspeed_mmbi_channel *chan); -+ -+static u8 mmbi_get_bmc_rdy(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ -+ return hros.b_rdy; -+} -+ -+static u8 mmbi_get_bmc_up(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ -+ return hros.b_up; -+} -+ -+static u8 mmbi_get_bmc_rst(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ -+ return hros.b_rst; -+} -+ -+static u8 mmbi_get_host_rst(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ -+ return hrws.h_rst; -+} -+ -+static u8 mmbi_get_host_up(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ -+ return hrws.h_up; -+} -+ -+static void mmbi_set_host_rst(struct aspeed_mmbi_channel *chan, bool set) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ hrws.h_rst = set; -+ memcpy_toio(chan->hrws_vmem, &hrws, sizeof(hrws)); -+} -+ -+static void mmbi_set_host_rdy(struct aspeed_mmbi_channel *chan, bool set) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ hrws.h_rdy = set; -+ memcpy_toio(chan->hrws_vmem, &hrws, sizeof(hrws)); -+} -+ -+static void mmbi_set_host_up(struct aspeed_mmbi_channel *chan, bool set) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ hrws.h_up = set; -+ memcpy_toio(chan->hrws_vmem, &hrws, sizeof(hrws)); -+} -+ -+static void get_h2b_avail_buf_len(struct aspeed_mmbi_channel *chan, ssize_t *avail_buf_len) -+{ -+ struct device *dev = chan->dev; -+ u32 h2b_rp, h2b_wp; -+ -+ h2b_rp = GET_H2B_READ_POINTER(chan); -+ h2b_wp = GET_H2B_WRITE_POINTER(chan); -+ dev_dbg(dev, "MMBI HRWS - h2b_rp: 0x%0x, h2b_wp: 0x%0x\n", h2b_rp, h2b_wp); -+ -+ if (h2b_wp >= h2b_rp) -+ *avail_buf_len = chan->h2b_cb_size - h2b_wp + h2b_rp; -+ else -+ *avail_buf_len = h2b_rp - h2b_wp; -+} -+ -+static u8 mmbi_get_state(struct aspeed_mmbi_channel *chan) -+{ -+ u8 state = 0; -+ -+ state = mmbi_get_bmc_up(chan) << 3; -+ state |= mmbi_get_bmc_rst(chan) << 2; -+ state |= mmbi_get_host_up(chan) << 1; -+ state |= mmbi_get_host_rst(chan); -+ -+ dev_dbg(chan->dev, "MMBI state: 0x%x\n", state); -+ -+ return state; -+} -+ -+static void raise_h2b_interrupt(struct aspeed_mmbi_channel *chan) -+{ -+ if (!chan->bmc_int_en) -+ return; -+ -+ writeb(chan->bmc_int_value, chan->desc_vmem + chan->bmc_int_location); -+} -+ -+static int mmbi_state_check(struct aspeed_mmbi_channel *chan) -+{ -+ enum mmbi_state current_state = mmbi_get_state(chan); -+ struct device *dev = chan->dev; -+ int ret; -+ -+ switch (current_state) { -+ case INIT_COMPLETED: -+ dev_dbg(dev, "Get INIT_COMPLETED state from BMC"); -+ -+ ret = mmbi_desc_init(chan); -+ if (ret) { -+ dev_warn(dev, "Check MMBI signature timeout\n"); -+ raise_h2b_interrupt(chan); -+ } -+ return 1; -+ case RESET_REQ_BY_BMC: -+ dev_dbg(dev, "Get RESET_REQ_BY_BMC state from BMC"); -+ -+ /* Change state to RESET_ACKED */ -+ mmbi_set_host_rst(chan, 1); -+ -+ dev_dbg(dev, "Change state to RESET_ACKED to BMC"); -+ raise_h2b_interrupt(chan); -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static int mmbi_bmc_up_check(struct aspeed_mmbi_channel *chan) -+{ -+ u64 __timeout_us = 1000; -+ ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); -+ -+ for (;;) { -+ enum mmbi_state current_state = mmbi_get_state(chan); -+ -+ if (current_state == INIT_COMPLETED) -+ break; -+ if (__timeout_us && ktime_compare(ktime_get(), __timeout) > 0) -+ return -EAGAIN; -+ } -+ -+ return 0; -+} -+ -+static void update_host_rws(struct aspeed_mmbi_channel *chan, unsigned int w_len, -+ unsigned int r_len) -+{ -+ struct device *dev = chan->dev; -+ struct host_rws hrws; -+ u32 h2b_wp, b2h_rp; -+ -+ h2b_wp = GET_H2B_WRITE_POINTER(chan); -+ b2h_rp = GET_B2H_READ_POINTER(chan); -+ -+ dev_dbg(dev, "MMBI HRWS - b2h_rp: 0x%0x, h2b_wp: 0x%0x\n", b2h_rp, h2b_wp); -+ -+ /* Advance the H2B CB offset for next write */ -+ if ((h2b_wp + w_len) <= chan->h2b_cb_size) -+ h2b_wp += w_len; -+ else -+ h2b_wp = h2b_wp + w_len - chan->h2b_cb_size; -+ -+ /* Advance the B2H CB offset till where BMC read data */ -+ if ((b2h_rp + r_len) <= chan->b2h_cb_size) -+ b2h_rp += r_len; -+ else -+ b2h_rp = b2h_rp + r_len - chan->b2h_cb_size; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ -+ hrws.h2b_wp = FIELD_GET(H2B_WRITE_POINTER_MASK, h2b_wp); -+ hrws.b2h_rp = FIELD_GET(B2H_READ_POINTER_MASK, b2h_rp); -+ memcpy_toio(chan->hrws_vmem, &hrws, sizeof(hrws)); -+ dev_dbg(dev, "Updating HRWS - b2h_rp: 0x%0x, h2b_wp: 0x%0x\n", b2h_rp, h2b_wp); -+ -+ if (w_len != 0) -+ raise_h2b_interrupt(chan); -+} -+ -+static int get_mmbi_header(struct aspeed_mmbi_channel *chan, u32 *data_length, u8 *type, -+ u32 *unread_data_len, u8 *padding) -+{ -+ u32 h2b_wp, h2b_rp, b2h_wp, b2h_rp; -+ struct mmbi_header header; -+ -+ h2b_wp = GET_H2B_WRITE_POINTER(chan); -+ h2b_rp = GET_H2B_READ_POINTER(chan); -+ b2h_wp = GET_B2H_WRITE_POINTER(chan); -+ b2h_rp = GET_B2H_READ_POINTER(chan); -+ dev_dbg(chan->dev, "MMBI HRWS - h2b_wp: 0x%0x, b2h_rp: 0x%0x\n", h2b_wp, b2h_rp); -+ dev_dbg(chan->dev, "MMBI HROS - b2h_wp: 0x%0x, h2b_rp: 0x%0x\n", b2h_wp, h2b_rp); -+ -+ if (b2h_wp >= b2h_rp) -+ *unread_data_len = b2h_wp - b2h_rp; -+ else -+ *unread_data_len = chan->b2h_cb_size - b2h_rp + b2h_wp; -+ -+ if (*unread_data_len < sizeof(struct mmbi_header)) { -+ dev_dbg(chan->dev, "No data to read(%d - %d)\n", b2h_wp, b2h_rp); -+ return -EAGAIN; -+ } -+ -+ dev_dbg(chan->dev, "READ MMBI header from: %p\n", chan->b2h_cb_vmem + b2h_rp); -+ -+ /* Extract MMBI protocol - protocol type and length */ -+ if ((b2h_rp + sizeof(header)) <= chan->b2h_cb_size) { -+ memcpy_fromio(&header, chan->b2h_cb_vmem + b2h_rp, sizeof(header)); -+ } else { -+ ssize_t chunk_len = chan->b2h_cb_size - b2h_rp; -+ -+ memcpy_fromio(&header, chan->b2h_cb_vmem + b2h_rp, chunk_len); -+ memcpy_fromio(((u8 *)&header) + chunk_len, chan->b2h_cb_vmem, -+ sizeof(header) - chunk_len); -+ } -+ -+ *data_length = (header.pkt_len << 2) - sizeof(header) - header.pkt_pad; -+ *padding = header.pkt_pad; -+ *type = header.pkt_type; -+ -+ return 0; -+} -+ -+static int aspeed_mmbi_write(struct aspeed_mmbi_channel *chan, const char *buffer, size_t len, -+ protocol_type type) -+{ -+ struct device *dev = chan->dev; -+ struct mmbi_header header = {0}; -+ ssize_t avail_buf_len; -+ ssize_t total_len; -+ ssize_t wt_offset; -+ ssize_t chunk_len; -+ ssize_t end_offset; -+ u8 padding = 0; -+ -+ /* If BMC READY bit is not set, Just discard the write. */ -+ if (!GET_BMC_READY_BIT(chan)) { -+ dev_dbg(dev, "Host not ready, discarding request...\n"); -+ return -EAGAIN; -+ } -+ -+ get_h2b_avail_buf_len(chan, &avail_buf_len); -+ -+ dev_dbg(dev, "H2B buffer empty space: %zd\n", avail_buf_len); -+ -+ /* Header size */ -+ total_len = len + 4; -+ -+ padding = total_len & 0x3; -+ if (padding) -+ padding = 4 - padding; -+ total_len += padding; -+ -+ /* Empty space should be more than write request data size */ -+ if (avail_buf_len <= sizeof(header) || (total_len > (avail_buf_len - sizeof(header)))) -+ return -ENOSPC; -+ -+ /* Fill multi-protocol header */ -+ header.pkt_type = type; -+ header.pkt_len = total_len >> 2; -+ header.pkt_pad = padding; -+ -+ wt_offset = GET_H2B_WRITE_POINTER(chan); -+ end_offset = chan->h2b_cb_size; -+ -+ /* Copy Header */ -+ if ((end_offset - wt_offset) >= sizeof(header)) { -+ memcpy_toio(chan->h2b_cb_vmem + wt_offset, &header, sizeof(header)); -+ wt_offset += sizeof(header); -+ } else { -+ chunk_len = end_offset - wt_offset; -+ dev_dbg(dev, "Write header chunk_len: %zd\n", chunk_len); -+ memcpy_toio(chan->h2b_cb_vmem + wt_offset, &header, chunk_len); -+ memcpy_toio(chan->h2b_cb_vmem, (u8 *)&header + chunk_len, -+ (sizeof(header) - chunk_len)); -+ wt_offset = (sizeof(header) - chunk_len); -+ } -+ -+ /* Write the data */ -+ if ((end_offset - wt_offset) >= len) { -+ memcpy_toio(&chan->h2b_cb_vmem[wt_offset], buffer, len); -+ wt_offset += len; -+ } else { -+ chunk_len = end_offset - wt_offset; -+ dev_dbg(dev, "Write data chunk_len: %zd\n", chunk_len); -+ memcpy_toio(&chan->h2b_cb_vmem[wt_offset], buffer, chunk_len); -+ wt_offset = 0; -+ memcpy_toio(&chan->h2b_cb_vmem[wt_offset], buffer + chunk_len, len - chunk_len); -+ wt_offset += len - chunk_len; -+ } -+ -+ update_host_rws(chan, total_len, 0); -+ -+ return 0; -+} -+ -+static void aspeed_mmbi_read(struct aspeed_mmbi_channel *chan, char *buffer, size_t len, u8 padding) -+{ -+ struct device *dev = chan->dev; -+ ssize_t rd_offset; -+ u32 b2h_rp; -+ -+ b2h_rp = GET_B2H_READ_POINTER(chan); -+ if ((b2h_rp + sizeof(struct mmbi_header)) <= chan->b2h_cb_size) -+ rd_offset = b2h_rp + sizeof(struct mmbi_header); -+ else -+ rd_offset = b2h_rp + sizeof(struct mmbi_header) - chan->b2h_cb_size; -+ -+ /* Extract data and copy to user space application */ -+ dev_dbg(dev, "READ MMBI Data from: %p and length: %zd\n", -+ chan->b2h_cb_vmem + rd_offset, len); -+ -+ if ((chan->b2h_cb_size - rd_offset) >= len) { -+ memcpy_fromio(buffer, chan->b2h_cb_vmem + rd_offset, len); -+ rd_offset += len; -+ } else { -+ ssize_t chunk_len; -+ -+ chunk_len = chan->b2h_cb_size - rd_offset; -+ dev_dbg(dev, "Read data chunk_len: %zd\n", chunk_len); -+ memcpy_fromio(buffer, chan->b2h_cb_vmem + rd_offset, chunk_len); -+ -+ rd_offset = 0; -+ memcpy_fromio(buffer + chunk_len, chan->b2h_cb_vmem + rd_offset, -+ len - chunk_len); -+ } -+ -+ update_host_rws(chan, 0, len + sizeof(struct mmbi_header) + padding); -+} -+ -+static void mctp_mmbi_rx(struct aspeed_mmbi_channel *chan) -+{ -+ struct net_device *ndev; -+ struct sk_buff *skb; -+ struct mctp_skb_cb *cb; -+ u32 req_data_len, unread_data_len; -+ u8 type, padding; -+ int status; -+ -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ return; -+ -+ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d, Unread data: %d\n", __func__, -+ req_data_len, type, unread_data_len); -+ -+ ndev = chan->ndev; -+ -+ skb = netdev_alloc_skb(ndev, req_data_len); -+ if (!skb) { -+ ndev->stats.rx_dropped++; -+ update_host_rws(chan, 0, req_data_len + sizeof(struct mmbi_header)); -+ return; -+ } -+ -+ skb->protocol = htons(ETH_P_MCTP); -+ aspeed_mmbi_read(chan, skb_put(skb, req_data_len), req_data_len, padding); -+ skb_reset_network_header(skb); -+ -+ cb = __mctp_cb(skb); -+ cb->halen = 0; -+ -+ status = netif_rx(skb); -+ if (status == NET_RX_SUCCESS) { -+ ndev->stats.rx_packets++; -+ ndev->stats.rx_bytes += req_data_len; -+ } else { -+ ndev->stats.rx_dropped++; -+ } -+} -+ -+static netdev_tx_t mctp_mmbi_tx(struct sk_buff *skb, struct net_device *ndev) -+{ -+ struct aspeed_mmbi_mctp *mctp = netdev_priv(ndev); -+ int ret; -+ -+ if (!mmbi_get_bmc_rdy(&mctp->mmbi->chan) || skb->len > MCTP_MMBI_MTU_MAX) { -+ ndev->stats.tx_dropped++; -+ goto out; -+ } -+ -+ ret = aspeed_mmbi_write(&mctp->mmbi->chan, skb->data, skb->len, MMBI_PROTOCOL_MCTP); -+ if (ret) { -+ netif_stop_queue(ndev); -+ return NETDEV_TX_BUSY; -+ } -+ -+ ndev->stats.tx_packets++; -+ ndev->stats.tx_bytes += skb->len; -+out: -+ kfree_skb(skb); -+ return NETDEV_TX_OK; -+} -+ -+static const struct net_device_ops mctp_mmbi_netdev_ops = { -+ .ndo_start_xmit = mctp_mmbi_tx, -+}; -+ -+static void aspeed_mctp_mmbi_setup(struct net_device *ndev) -+{ -+ ndev->type = ARPHRD_MCTP; -+ -+ /* we limit at the fixed MTU, which is also the MCTP-standard -+ * baseline MTU, so is also our minimum -+ */ -+ ndev->mtu = MCTP_MMBI_MTU; -+ ndev->max_mtu = MCTP_MMBI_MTU_MAX; -+ ndev->min_mtu = MCTP_MMBI_MTU_MIN; -+ -+ ndev->hard_header_len = 0; -+ ndev->addr_len = 0; -+ ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; -+ ndev->flags = IFF_NOARP; -+ ndev->netdev_ops = &mctp_mmbi_netdev_ops; -+ ndev->needs_free_netdev = true; -+} -+ -+static int aspeed_mmbi_mctp_init(struct aspeed_mmbi_channel *chan) -+{ -+ struct aspeed_mmbi_mctp *mctp; -+ struct net_device *ndev; -+ char name[32]; -+ int ret; -+ -+ snprintf(name, sizeof(name), "mctpmmbi%d", chan->mmbi->id); -+ ndev = alloc_netdev(sizeof(*mctp), name, NET_NAME_ENUM, aspeed_mctp_mmbi_setup); -+ if (!ndev) -+ return -ENOMEM; -+ mctp = netdev_priv(ndev); -+ mctp->ndev = ndev; -+ mctp->mmbi = chan->mmbi; -+ -+ chan->ndev = ndev; -+ -+ ret = register_netdev(ndev); -+ if (ret) -+ goto free_netdev; -+ -+ return 0; -+ -+free_netdev: -+ free_netdev(ndev); -+ -+ return ret; -+} -+ +static struct aspeed_pci_bmc_dev *file_aspeed_bmc_device(struct file *file) +{ + return container_of(file->private_data, struct aspeed_pci_bmc_dev, miscdev); @@ -64929,67 +66254,13 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + return IRQ_HANDLED; +} + -+static void aspeed_mmbi_work_func(struct work_struct *workq) -+{ -+ struct aspeed_mmbi_channel *chan = container_of(workq, struct aspeed_mmbi_channel, work); -+ u32 weight = 256, req_data_len, unread_data_len; -+ u8 type, padding; -+ int i; -+ -+ for (i = 0; i < weight; i++) { -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ return; -+ -+ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d\n", -+ __func__, req_data_len, type); -+ -+ if (type == MMBI_PROTOCOL_MCTP) -+ mctp_mmbi_rx(chan); -+ else -+ /* Discard data and advance the hrws */ -+ update_host_rws(chan, 0, req_data_len + sizeof(struct mmbi_header) + padding); -+ -+ raise_h2b_interrupt(chan); -+ } -+ -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ queue_work(system_unbound_wq, &chan->work); -+} -+ -+static irqreturn_t aspeed_pci_mmbi_isr(int irq, void *dev_id) -+{ -+ struct aspeed_pcie_mmbi *mmbi = dev_id; -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ ssize_t avail_buf_len; -+ -+ get_h2b_avail_buf_len(chan, &avail_buf_len); -+ if (avail_buf_len > MCTP_MMBI_MTU_MAX) { -+ if (netif_queue_stopped(chan->ndev)) { -+ dev_dbg(chan->dev, "Wake up mctp net device\n"); -+ netif_wake_queue(chan->ndev); -+ } -+ } -+ -+ if (mmbi_state_check(chan)) -+ return IRQ_HANDLED; -+ -+ queue_work(system_unbound_wq, &chan->work); -+ -+ return IRQ_HANDLED; -+} -+ +static void aspeed_pci_setup_irq_resource(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev = pci_get_drvdata(pdev); + + /* Assign static msi index table by platform */ + if (pdev->revision == 0x27) { -+ if (pci_bmc_dev->driver_data == ASPEED) { -+ pci_bmc_dev->msi_idx_table = ast2700_soc0_msi_idx_table; -+ } else { -+ pci_bmc_dev->msi_idx_table = ast2700_soc1_msi_idx_table; -+ pci_bmc_dev->ast2700_soc1 = true; -+ } ++ pci_bmc_dev->msi_idx_table = ast2700_soc0_msi_idx_table; + } else { + pci_bmc_dev->msi_idx_table = ast2600_msi_idx_table; + } @@ -65135,231 +66406,6 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + return 0; +} + -+static int mmbi_signature_check(struct aspeed_mmbi_channel *chan) -+{ -+ u8 signature[6]; -+ u64 __timeout_us = 1000; -+ ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); -+ -+ for (;;) { -+ memcpy_fromio(signature, chan->desc_vmem, 6); -+ if (!memcmp(MMBI_SIGNATURE, signature, 6)) -+ break; -+ if (__timeout_us && ktime_compare(ktime_get(), __timeout) > 0) -+ return -ETIMEDOUT; -+ } -+ return 0; -+} -+ -+static int mmbi_desc_init(struct aspeed_mmbi_channel *chan) -+{ -+ struct aspeed_pcie_mmbi *mmbi = chan->mmbi; -+ struct device *dev = chan->dev; -+ struct mmbi_cap_desc desc; -+ u8 __iomem *desc_base = chan->desc_vmem; -+ int ret; -+ -+ /* First, check mmbi signature "#MMBI$" */ -+ ret = mmbi_signature_check(chan); -+ if (ret) { -+ dev_warn(dev, "Check MMBI signature timeout\n"); -+ return ret; -+ } -+ -+ memcpy_fromio(&desc, chan->desc_vmem, sizeof(desc)); -+ -+ /* HROS */ -+ if (((desc.bt_desc.h_ros_p << 3) + sizeof(struct host_ros)) >= mmbi->mem_size) { -+ dev_warn(dev, "HROS is out of range"); -+ return -EINVAL; -+ } -+ chan->hros_vmem = desc_base + (desc.bt_desc.h_ros_p << 3); -+ -+ /* HRWS */ -+ if (((desc.bt_desc.h_rws_p << 3) + sizeof(struct host_rws)) >= mmbi->mem_size) { -+ dev_warn(dev, "HRWS is out of range"); -+ return -EINVAL; -+ } -+ chan->hrws_vmem = desc_base + (desc.bt_desc.h_rws_p << 3); -+ -+ ret = mmbi_bmc_up_check(chan); -+ if (ret) { -+ dev_warn(dev, "Check BMC up timeout\n"); -+ return ret; -+ } -+ -+ /* Implementations of MMBI described in this document shall indicate version 1 of MMBI */ -+ if (desc.version != 1) { -+ dev_warn(dev, "MMBI version must be 1"); -+ goto err_mismatch; -+ } -+ -+ /* This MMBI interface is intended for OS use */ -+ if (desc.os_use != 1) { -+ dev_warn(dev, "This MMBI does not provide for OS"); -+ goto err_mismatch; -+ } -+ -+ /* Current application is only MMBI Variable Packet Size Circular Buffers (VPSCB) v1 */ -+ if (desc.buffer_type != 1) { -+ dev_warn(dev, "The buffer type is not VPSCB: (%d)", desc.buffer_type); -+ goto err_mismatch; -+ } -+ -+ /* B2H Buffer */ -+ if (((desc.b2h_ba << 3) + desc.b2h_l) > mmbi->mem_size) { -+ dev_warn(dev, "B2H buffer is out of range"); -+ goto err_mismatch; -+ } -+ chan->b2h_cb_vmem = desc_base + (desc.b2h_ba << 3); -+ chan->b2h_cb_size = desc.b2h_l; -+ -+ /* H2B Buffer */ -+ if (((desc.h2b_ba << 3) + desc.h2b_l) > mmbi->mem_size) { -+ dev_warn(dev, "H2B buffer is out of range"); -+ goto err_mismatch; -+ } -+ chan->h2b_cb_vmem = desc_base + (desc.h2b_ba << 3); -+ chan->h2b_cb_size = desc.h2b_l; -+ -+ dev_dbg(dev, "B2H mapped addr - desc: %p, hros: %p, b2h_cb: %p\n", -+ chan->desc_vmem, chan->hros_vmem, chan->b2h_cb_vmem); -+ dev_dbg(dev, "H2B mapped addr - hrws: %p, h2b_cb: %p\n", chan->hrws_vmem, -+ chan->h2b_cb_vmem); -+ -+ dev_dbg(dev, "B2H buffer size: 0x%0x\n", chan->b2h_cb_size); -+ dev_dbg(dev, "H2B buffer size: 0x%0x\n", chan->h2b_cb_size); -+ -+ /* Host Interrupt */ -+ chan->host_int_en = !!desc.bt_desc.h_int_t; -+ if (chan->host_int_en) { -+ /* 1 for PCIe */ -+ if (desc.bt_desc.h_int_t != 1) -+ chan->host_int_en = 0; -+ else -+ chan->host_int_location = desc.bt_desc.h_int_l; -+ } -+ -+ /* BMC Interrupt */ -+ chan->bmc_int_en = !!desc.bt_desc.bmc_int_t; -+ if (chan->bmc_int_en) { -+ if (desc.bt_desc.bmc_int_t != 1) { -+ chan->bmc_int_en = 0; -+ } else { -+ chan->bmc_int_location = desc.bt_desc.bmc_int_l; -+ chan->bmc_int_vmem = desc_base + chan->bmc_int_location; -+ chan->bmc_int_value = desc.bt_desc.bmc_int_v; -+ } -+ } -+ -+ INIT_WORK(&chan->work, aspeed_mmbi_work_func); -+ -+ return 0; -+err_mismatch: -+ /* Change state to INIT_MISMATCH */ -+ mmbi_set_host_rst(chan, 1); -+ return -EINVAL; -+} -+ -+static int aspeed_pci_mmbi_init(struct aspeed_pcie_mmbi *mmbi) -+{ -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ struct device *dev = chan->dev; -+ int ret; -+ -+ chan->desc_vmem = mmbi->mem; -+ chan->mmbi = mmbi; -+ -+ ret = mmbi_desc_init(chan); -+ if (ret) { -+ dev_err(dev, "Unable to init mmbi desc\n"); -+ return ret; -+ } -+ -+ /* Initialize MTCP function */ -+ ret = aspeed_mmbi_mctp_init(chan); -+ if (ret) { -+ dev_err(dev, "Unable to init mctp\n"); -+ return ret; -+ } -+ -+ /* Change state to NORMAL_RUNTIME */ -+ mmbi_set_host_up(chan, 1); -+ mmbi_set_host_rdy(chan, 1); -+ /* Trigger BMC to finish normal runtime state */ -+ raise_h2b_interrupt(chan); -+ -+ return 0; -+} -+ -+/* AST2700 PCIe MMBI -+ * SoC : | 0 | 1 | -+ * BAR : | 2 3 4 5 | 0 1 2 3 4 5 | -+ * MMBI: | 0 1 2 3 | 0 1 2 3 4 5 | -+ */ -+static void aspeed_pci_bmc_device_setup_mmbi(struct pci_dev *pdev) -+{ -+ struct aspeed_pci_bmc_dev *pci_bmc_dev = pci_get_drvdata(pdev); -+ struct aspeed_pcie_mmbi *mmbi; -+ u32 start_bar = 2, mmbi_max_inst = 4, start_msi = MMBI0_MSI; /* AST2700 SoC0 */ -+ int i, rc = 0; -+ -+ /* AST2700 A1 supports MMBI */ -+ if (pdev->revision != 0x27) -+ return; -+ -+ if (pci_bmc_dev->ast2700_soc1) { -+ /* AST2700 SoC1 */ -+ start_bar = 0; -+ mmbi_max_inst = 6; -+ start_msi = 0; -+ } -+ -+ for (i = 0; i < mmbi_max_inst; i++) { -+ mmbi = &pci_bmc_dev->mmbi[i]; -+ -+ /* Get MMBI BAR resource */ -+ mmbi->base = pci_resource_start(pdev, start_bar + i); -+ mmbi->mem_size = pci_resource_len(pdev, start_bar + i); -+ -+ /* Check if there is bar */ -+ if (!mmbi->mem_size) -+ continue; -+ -+ mmbi->mem = pci_ioremap_bar(pdev, start_bar + i); -+ if (!mmbi->mem) { -+ mmbi->mem_size = 0; -+ continue; -+ } -+ -+ mmbi->chan.dev = &pdev->dev; -+ mmbi->dev_name = devm_kasprintf(mmbi->chan.dev, GFP_KERNEL, "pci-mmbi%d", i); -+ mmbi->id = i; -+ rc = aspeed_pci_mmbi_init(mmbi); -+ if (rc < 0) { -+ pr_err("Initialize MMBI device failed.\n"); -+ goto free_ioremap; -+ } -+ -+ if (mmbi->chan.host_int_en) { -+ mmbi->irq = pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[start_msi + i]); -+ rc = devm_request_irq(&pdev->dev, mmbi->irq, aspeed_pci_mmbi_isr, -+ IRQF_SHARED, mmbi->dev_name, mmbi); -+ if (rc) { -+ pr_err("MMBI device %s unable to get IRQ %d\n", mmbi->dev_name, rc); -+ mmbi->irq = 0; -+ goto free_ioremap; -+ } -+ } else { -+ mmbi->irq = 0; -+ } -+ continue; -+free_ioremap: -+ mmbi->mem_size = 0; -+ pci_iounmap(pdev, mmbi->mem); -+ } -+} -+ +static void aspeed_pci_host_bmc_device_release_queue(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev = pci_get_drvdata(pdev); @@ -65388,35 +66434,6 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + misc_deregister(&pci_bmc_dev->miscdev); +} + -+static void aspeed_pci_release_mmbi(struct pci_dev *pdev) -+{ -+ struct aspeed_pci_bmc_dev *pci_bmc_dev = pci_get_drvdata(pdev); -+ struct aspeed_pcie_mmbi *mmbi; -+ int i; -+ -+ if (pdev->revision != 0x27) -+ return; -+ -+ for (i = 0; i < MMBI_MAX_INST; i++) { -+ mmbi = &pci_bmc_dev->mmbi[i]; -+ -+ if (mmbi->mem_size == 0) -+ continue; -+ -+ cancel_work_sync(&mmbi->chan.work); -+ -+ mmbi_set_host_rdy(&mmbi->chan, 0); -+ mmbi_set_host_up(&mmbi->chan, 0); -+ -+ unregister_netdev(mmbi->chan.ndev); -+ -+ if (mmbi->mem) -+ pci_iounmap(pdev, mmbi->mem); -+ if (mmbi->irq != 0) -+ devm_free_irq(&pdev->dev, mmbi->irq, mmbi); -+ } -+} -+ +static int aspeed_pci_host_setup(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev = pci_get_drvdata(pdev); @@ -65474,9 +66491,6 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + goto out_free_uart; + } + -+ /* Setup AST2700 PCIe MMBI device */ -+ aspeed_pci_bmc_device_setup_mmbi(pdev); -+ + return 0; + +out_free_uart: @@ -65497,18 +66511,150 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + return rc; +} + -+static int aspeed_pci_host_mmbi_device_setup(struct pci_dev *pdev) -+{ -+ aspeed_pci_bmc_device_setup_mmbi(pdev); -+ return 0; -+} -+ +static struct aspeed_platform aspeed_pcie_host[] = { + { .setup = aspeed_pci_host_setup }, -+ { .setup = aspeed_pci_host_mmbi_device_setup }, + { 0 } +}; + ++#if IS_ENABLED(CONFIG_ASPEED_MMBI) ++static const char * ++aspeed_host_pcie_mmbi_app_interface_name(enum mmbi_app_interface intf) ++{ ++ switch (intf) { ++ case MMBI_APP_INTF_MCTP_NETDEV: ++ return "mctp-netdev"; ++ case MMBI_APP_INTF_IOCTL: ++ default: ++ return "ioctl"; ++ } ++} ++ ++static int aspeed_host_pcie_mmbi_validate_app_interface(struct device *dev) ++{ ++ int i; ++ ++ for (i = 0; i < app_interface_count; i++) { ++ if (app_interface[i] > MMBI_APP_INTF_MCTP_NETDEV) { ++ dev_warn(dev, ++ "invalid app_interface[%d]=%u, using defaults\n", ++ i, app_interface[i]); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static enum mmbi_app_interface ++aspeed_host_pcie_mmbi_get_app_interface(int channel, bool use_defaults) ++{ ++ if (use_defaults) ++ return app_interface_defaults[channel]; ++ ++ if (app_interface_count == 1) ++ return app_interface[0]; ++ ++ if (channel < app_interface_count) ++ return app_interface[channel]; ++ ++ return app_interface_defaults[channel]; ++} ++ ++static void ++aspeed_host_pcie_mmbi_init_app_interfaces(struct mmbi_ins_desc *mmbi_desc, ++ bool use_defaults) ++{ ++ int i; ++ ++ for (i = 0; i < MMBI_MAX_CHANNELS; i++) ++ mmbi_desc->chan_desc[i].app_interface = ++ aspeed_host_pcie_mmbi_get_app_interface(i, ++ use_defaults); ++} ++ ++static void aspeed_host_pcie_mmbi_log_app_interfaces(struct device *dev, ++ struct mmbi_ins_desc *mmbi_desc, ++ int bar) ++{ ++ struct mmbi_chan_desc *chan_desc; ++ const char *interface; ++ int i; ++ ++ for (i = 0; i < mmbi_desc->num_of_channels; i++) { ++ chan_desc = &mmbi_desc->chan_desc[i]; ++ interface = aspeed_host_pcie_mmbi_app_interface_name(chan_desc->app_interface); ++ dev_info(dev, "BAR %d channel %d application interface: %s\n", ++ bar, i, interface); ++ } ++} ++ ++static int aspeed_pci_host_mmbi_setup(struct pci_dev *pdev) ++{ ++ struct aspeed_pci_bmc_dev *pci_bmc_dev = pci_get_drvdata(pdev); ++ struct aspeed_host_pcie_mmbi *mmbi = &pci_bmc_dev->mmbi; ++ struct aspeed_host_pcie_mmbi_ins *ins; ++ struct device *dev = &pdev->dev; ++ struct mmbi_ins_desc *mmbi_desc; ++ int i, ret, instance_id; ++ int last_ret = -ENODEV; ++ bool use_default_app_interfaces; ++ resource_size_t start, size; ++ ++ ret = aspeed_host_pcie_mmbi_validate_app_interface(dev); ++ use_default_app_interfaces = ret != 0; ++ ++ instance_id = 0; ++ for (i = 2; i < PCI_STD_NUM_BARS; i++) { ++ ins = &mmbi->instances[i]; ++ mmbi_desc = &ins->mmbi_desc; ++ start = pci_resource_start(pdev, i); ++ size = pci_resource_len(pdev, i); ++ ++ if (!start || !size) { ++ dev_err(dev, ++ "Invalid BAR %d with start 0x%llx and size 0x%llx\n", ++ i, (unsigned long long)start, ++ (unsigned long long)size); ++ continue; ++ } ++ ++ mmbi_desc->desc_virt = pci_ioremap_bar(pdev, i); ++ if (!mmbi_desc->desc_virt) { ++ dev_err(dev, "Failed to ioremap BAR %d\n", i); ++ continue; ++ } ++ mmbi_desc->dev = dev; ++ mmbi_desc->role = MMBI_ROLE_HOST; ++ ++ aspeed_host_pcie_mmbi_init_app_interfaces(mmbi_desc, ++ use_default_app_interfaces); ++ ret = mmbi_instance_init(mmbi_desc); ++ if (ret) { ++ dev_info(dev, ++ "Failed to initialize MMBI at bar %d, error %d\n", ++ i, ret); ++ pci_iounmap(pdev, mmbi_desc->desc_virt); ++ last_ret = ret; ++ } else { ++ ins->instance_index = instance_id++; ++ ins->bar_index = i; ++ ins->valid = true; ++ ins->dev_name = devm_kasprintf(dev, GFP_KERNEL, ++ "mmbi_host_ins%d", ++ ins->instance_index); ++ aspeed_host_pcie_mmbi_log_app_interfaces(dev, mmbi_desc, i); ++ ++ ins->msi_index = mmbi_desc->host_int_location; ++ } ++ } ++ ++ if (!instance_id) ++ return last_ret; ++ ++ return 0; ++} ++#endif ++ +static int aspeed_pci_host_bmc_device_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev; @@ -65549,25 +66695,44 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + return rc; + } + ++#if IS_ENABLED(CONFIG_ASPEED_MMBI) ++ pci_bmc_dev->mmbi.dev = &pdev->dev; ++ rc = aspeed_pci_host_mmbi_setup(pdev); ++ if (rc) { ++ dev_err(&pdev->dev, "Failed to setup MMBI instances\n"); ++ return rc; ++ } ++#endif ++ + return 0; +} + +static void aspeed_pci_host_bmc_device_remove(struct pci_dev *pdev) +{ + struct aspeed_pci_bmc_dev *pci_bmc_dev = pci_get_drvdata(pdev); ++#if IS_ENABLED(CONFIG_ASPEED_MMBI) ++ struct aspeed_host_pcie_mmbi *mmbi = &pci_bmc_dev->mmbi; ++ struct aspeed_host_pcie_mmbi_ins *ins; ++ int i; + -+ if (pci_bmc_dev->driver_data == ASPEED) { -+ aspeed_pci_host_bmc_device_release_queue(pdev); -+ aspeed_pci_host_bmc_device_release_memory_mapping(pdev); -+ aspeed_pci_host_bmc_device_release_vuart(pdev); -+ -+ devm_free_irq(&pdev->dev, pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[BMC_MSI]), -+ pci_bmc_dev); -+ devm_free_irq(&pdev->dev, pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[MBX_MSI]), -+ pci_bmc_dev); ++ for (i = 2; i < PCI_STD_NUM_BARS; i++) { ++ ins = &mmbi->instances[i]; ++ if (ins->valid) { ++ mmbi_instance_remove(&ins->mmbi_desc); ++ pci_iounmap(pdev, ins->mmbi_desc.desc_virt); ++ } + } ++#endif ++ aspeed_pci_host_bmc_device_release_queue(pdev); ++ aspeed_pci_host_bmc_device_release_memory_mapping(pdev); ++ aspeed_pci_host_bmc_device_release_vuart(pdev); + -+ aspeed_pci_release_mmbi(pdev); ++ devm_free_irq(&pdev->dev, ++ pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[BMC_MSI]), ++ pci_bmc_dev); ++ devm_free_irq(&pdev->dev, ++ pci_irq_vector(pdev, pci_bmc_dev->msi_idx_table[MBX_MSI]), ++ pci_bmc_dev); + + ida_simple_remove(&bmc_device_ida, pci_bmc_dev->id); + @@ -65584,12 +66749,8 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee + * + */ +static struct pci_device_id aspeed_host_bmc_dev_pci_ids[] = { -+ /* ASPEED BMC Device */ -+ { PCI_DEVICE(0x1A03, 0x2402), .class = 0xFF0000, .class_mask = 0xFFFF00, -+ .driver_data = ASPEED }, -+ /* AST2700 SoC1 MMBI device */ -+ { PCI_DEVICE(0x1A03, 0x2402), .class = 0x0C0C00, .class_mask = (0xFFFF00), -+ .driver_data = ASPEED_AST2700_SOC1 }, ++ { PCI_DEVICE(0x1A03, 0x2402), .class = 0xFF0000, .class_mask = 0xFFFF00 }, ++ { PCI_DEVICE(0x1A03, 0x2402), .class = 0x0C0C00, .class_mask = 0xFFFF00 }, + { + 0, + } @@ -65621,21 +66782,9 @@ diff --git a/drivers/soc/aspeed/aspeed-host-bmc-dev.c b/drivers/soc/aspeed/aspee +MODULE_AUTHOR("Ryan Chen "); +MODULE_DESCRIPTION("ASPEED Host BMC DEVICE Driver"); +MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c ---- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c 2026-04-08 18:03:48.310705320 +0000 -@@ -353,7 +353,7 @@ - .of_match_table = aspeed_lpc_ctrl_match, - }, - .probe = aspeed_lpc_ctrl_probe, -- .remove_new = aspeed_lpc_ctrl_remove, -+ .remove = aspeed_lpc_ctrl_remove, - }; - - module_platform_driver(aspeed_lpc_ctrl_driver); diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c --- a/drivers/soc/aspeed/aspeed-lpc-mbox.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c 2026-04-08 18:03:48.310705320 +0000 ++++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c 2026-07-17 08:44:55.289306566 +0000 @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -66045,7 +67194,7 @@ diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lp +MODULE_DESCRIPTION("Aspeed mailbox device driver"); diff --git a/drivers/soc/aspeed/aspeed-lpc-pcc.c b/drivers/soc/aspeed/aspeed-lpc-pcc.c --- a/drivers/soc/aspeed/aspeed-lpc-pcc.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-lpc-pcc.c 2026-04-08 18:03:48.310705320 +0000 ++++ b/drivers/soc/aspeed/aspeed-lpc-pcc.c 2026-07-17 08:44:55.289306566 +0000 @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -66499,8 +67648,8 @@ diff --git a/drivers/soc/aspeed/aspeed-lpc-pcc.c b/drivers/soc/aspeed/aspeed-lpc +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Driver for Aspeed Post Code Capture"); diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c ---- a/drivers/soc/aspeed/aspeed-lpc-snoop.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c 2026-04-08 18:03:48.310705320 +0000 +--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c 2026-07-17 08:44:55.289306566 +0000 @@ -11,7 +11,7 @@ */ @@ -66864,18 +68013,9 @@ diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-l static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { .has_hicrb_ensnp = 0, }; -@@ -388,7 +375,7 @@ - .of_match_table = aspeed_lpc_snoop_match, - }, - .probe = aspeed_lpc_snoop_probe, -- .remove_new = aspeed_lpc_snoop_remove, -+ .remove = aspeed_lpc_snoop_remove, - }; - - module_platform_driver(aspeed_lpc_snoop_driver); diff --git a/drivers/soc/aspeed/aspeed-mbox.c b/drivers/soc/aspeed/aspeed-mbox.c --- a/drivers/soc/aspeed/aspeed-mbox.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-mbox.c 2026-04-08 18:03:48.310705320 +0000 ++++ b/drivers/soc/aspeed/aspeed-mbox.c 2026-07-17 08:44:55.289306566 +0000 @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* @@ -67191,8 +68331,8 @@ diff --git a/drivers/soc/aspeed/aspeed-mbox.c b/drivers/soc/aspeed/aspeed-mbox.c +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c --- a/drivers/soc/aspeed/aspeed-mctp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-mctp.c 2026-04-08 18:03:48.310705320 +0000 -@@ -0,0 +1,2658 @@ ++++ b/drivers/soc/aspeed/aspeed-mctp.c 2026-07-17 08:44:55.290306551 +0000 +@@ -0,0 +1,2693 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020, Intel Corporation. + @@ -67225,6 +68365,9 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c +#include + +#include ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) ++#include ++#endif + +/* AST2600 MCTP Controller registers */ +#define ASPEED_MCTP_CTRL 0x000 @@ -67510,6 +68653,8 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + int packet_counter; + } rx_runaway_wa; + bool rx_warmup; ++ /* Device ID for the device instance */ ++ u8 dev_id; + u8 eid; + struct platform_device *peci_mctp; + /* Use the flag to identify RC or EP */ @@ -67525,8 +68670,9 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + /* Delayed work for periodic detection of Rx packets */ + struct delayed_work rx_det_dwork; + u32 rx_det_period_us; -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) + struct net_device *ndev; ++ bool pcie_vdm_enabled; +#endif +}; + @@ -67630,6 +68776,9 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + bdf = PCI_DEVID(GET_PCI_BUS_NUM(reg), devfn); + } + ++ if (!priv->rc_f && bdf == 0) ++ return -ENODEV; ++ + return bdf; +} + @@ -67960,7 +69109,7 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + } else { + wake_up_all(&client->wait_queue); + } -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) + mctp_pcie_vdm_receive_packet(priv->ndev); +#endif + aspeed_mctp_client_put(client); @@ -69132,7 +70281,7 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + return ret; +} + -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) +static int aspeed_mctp_pcie_vdm_op_send_pkt(struct device *dev, + u8 *data, size_t size) +{ @@ -69176,7 +70325,7 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + + if (IS_ERR(rx_packet)) { + if (PTR_ERR(rx_packet) == -ETIME) { -+ dev_dbg(priv->dev, "no packet received\n"); ++ dev_dbg(priv->dev, "all packet received\n"); + } else { + dev_err(priv->dev, "failed to receive packet: %ld\n", + PTR_ERR(rx_packet)); @@ -69206,6 +70355,23 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + .uninit = aspeed_mctp_pcie_vdm_op_uninit, +}; + ++static void aspeed_mctp_pcie_vdm_register(struct aspeed_mctp *priv) ++{ ++ struct net_device *ndev; ++ struct mctp_client *client; ++ char ifname[IFNAMSIZ]; ++ ++ /** use priv's default client to send/receive mctp packets */ ++ client = aspeed_mctp_create_client(priv); ++ aspeed_mctp_register_default_handler(client); ++ ++ snprintf(ifname, IFNAMSIZ, "mctppci%d", priv->dev_id); ++ ndev = mctp_pcie_vdm_add_dev(priv->dev, &aspeed_mctp_pcie_vdm_ops, ifname); ++ if (IS_ERR(ndev)) ++ dev_err(priv->dev, "Failed to add mctp pcie vdm device Err %ld\n", PTR_ERR(ndev)); ++ priv->ndev = ndev; ++} ++ +#endif + +static const struct file_operations aspeed_mctp_fops = { @@ -69303,9 +70469,15 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + } + aspeed_mctp_rx_trigger(&priv->rx); + aspeed_mctp_send_pcie_uevent(kobj, true); ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) ++ mctp_pcie_vdm_set_carrier(priv->ndev, true); ++#endif + } else { + schedule_delayed_work(&priv->pcie.rst_dwork, + msecs_to_jiffies(1000)); ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) ++ mctp_pcie_vdm_set_carrier(priv->ndev, false); ++#endif + } +} + @@ -69451,11 +70623,15 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + return PTR_ERR(priv->reset); + } + -+ if (priv->rc_f) { -+ priv->reset_dma = devm_reset_control_get_shared_by_index(priv->dev, 1); -+ if (IS_ERR(priv->reset_dma)) { -+ dev_err(priv->dev, "Failed to get ep reset!\n"); -+ return PTR_ERR(priv->reset_dma); ++ if (priv->match_data->dma_need_64bits_width) { ++ priv->reset_dma = NULL; ++ } else { ++ if (priv->rc_f) { ++ priv->reset_dma = devm_reset_control_get_shared_by_index(priv->dev, 1); ++ if (IS_ERR(priv->reset_dma)) { ++ dev_err(priv->dev, "Failed to get ep reset!\n"); ++ return PTR_ERR(priv->reset_dma); ++ } + } + } + priv->pcie.map = @@ -69616,7 +70792,7 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + return ret; + } + -+ if (priv->rc_f) { ++ if (priv->rc_f && priv->reset_dma) { + ret = reset_control_deassert(priv->reset_dma); + if (ret) { + dev_warn(priv->dev, "Failed to deassert ep reset\n"); @@ -69682,22 +70858,6 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + goto out_drv; + } + -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM -+ struct net_device *ndev; -+ struct mctp_client *client; -+ -+ /** use priv's default client to send/receive mctp packets */ -+ client = aspeed_mctp_create_client(priv); -+ aspeed_mctp_register_default_handler(client); -+ -+ ndev = mctp_pcie_vdm_add_dev(priv->dev, &aspeed_mctp_pcie_vdm_ops); -+ if (IS_ERR(ndev)) { -+ dev_err(priv->dev, "Failed to add mctp pcie vdm device Err %ld\n", PTR_ERR(ndev)); -+ goto out_drv; -+ } -+ priv->ndev = ndev; -+#endif -+ + ret = aspeed_mctp_dma_init(priv); + if (ret) { + dev_err(priv->dev, "Failed to init DMA\n"); @@ -69706,13 +70866,17 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + + ret = aspeed_mctp_hw_reset(priv); + if (ret) -+ goto out_drv; ++ goto out_dma; + + aspeed_mctp_channels_init(priv); + + id = of_alias_get_id(priv->dev->of_node, "mctp"); -+ if (id < 0) -+ return id; ++ if (id < 0) { ++ ret = id; ++ goto out_dma; ++ } ++ ++ priv->dev_id = id; + priv->mctp_miscdev.parent = priv->dev; + priv->mctp_miscdev.minor = MISC_DYNAMIC_MINOR; + priv->mctp_miscdev.name = devm_kasprintf(priv->dev, GFP_KERNEL, "aspeed-mctp%d", id); @@ -69724,10 +70888,17 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + } + priv->mctp_miscdev.this_device->type = &aspeed_mctp_type; + ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) ++ if (!priv->pcie_vdm_enabled) { ++ aspeed_mctp_pcie_vdm_register(priv); ++ priv->pcie_vdm_enabled = true; ++ } ++#endif ++ + ret = aspeed_mctp_irq_init(priv); + if (ret) { + dev_err(priv->dev, "Failed to init IRQ!\n"); -+ goto out_dma; ++ goto out_irq; + } + aspeed_mctp_pcie_setup(priv); + @@ -69738,7 +70909,8 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c + dev_err(priv->dev, "Failed to register peci-mctp device\n"); + + return 0; -+ ++out_irq: ++ misc_deregister(&priv->mctp_miscdev); +out_dma: + aspeed_mctp_dma_fini(priv); +out_drv: @@ -69752,8 +70924,11 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c +{ + struct aspeed_mctp *priv = platform_get_drvdata(pdev); + -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM -+ mctp_pcie_vdm_remove_dev(priv->ndev); ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) ++ if (priv->pcie_vdm_enabled) { ++ mctp_pcie_vdm_remove_dev(priv->ndev); ++ priv->pcie_vdm_enabled = false; ++ } +#endif + + platform_device_unregister(priv->peci_mctp); @@ -69851,6017 +71026,6706 @@ diff --git a/drivers/soc/aspeed/aspeed-mctp.c b/drivers/soc/aspeed/aspeed-mctp.c +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Iwona Winiarska "); +MODULE_DESCRIPTION("Aspeed MCTP driver"); -diff --git a/drivers/soc/aspeed/aspeed-p2a-ctrl.c b/drivers/soc/aspeed/aspeed-p2a-ctrl.c ---- a/drivers/soc/aspeed/aspeed-p2a-ctrl.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-p2a-ctrl.c 2026-04-08 18:03:48.311705302 +0000 -@@ -431,7 +431,7 @@ - .of_match_table = aspeed_p2a_ctrl_match, - }, - .probe = aspeed_p2a_ctrl_probe, -- .remove_new = aspeed_p2a_ctrl_remove, -+ .remove = aspeed_p2a_ctrl_remove, - }; +diff --git a/drivers/soc/aspeed/aspeed-socinfo.c b/drivers/soc/aspeed/aspeed-socinfo.c +--- a/drivers/soc/aspeed/aspeed-socinfo.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-socinfo.c 2026-07-17 08:44:55.290306551 +0000 +@@ -39,7 +39,7 @@ + unsigned int i; + + for (i = 0 ; i < ARRAY_SIZE(rev_table) ; ++i) { +- if ((rev_table[i].id & 0xff00ffff) == id) ++ if (rev_table[i].id == id) + return rev_table[i].name; + } - module_platform_driver(aspeed_p2a_ctrl_driver); -diff --git a/drivers/soc/aspeed/aspeed-pcie-mmbi.c b/drivers/soc/aspeed/aspeed-pcie-mmbi.c ---- a/drivers/soc/aspeed/aspeed-pcie-mmbi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-pcie-mmbi.c 2026-04-08 18:03:48.311705302 +0000 -@@ -0,0 +1,963 @@ +diff --git a/drivers/soc/aspeed/aspeed-ssp.c b/drivers/soc/aspeed/aspeed-ssp.c +--- a/drivers/soc/aspeed/aspeed-ssp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-ssp.c 2026-07-17 08:44:55.290306551 +0000 +@@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) ASPEED Technology Inc. + -+#include ++#include ++#include ++#include +#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include ++#include ++#include ++#include ++#include +#include -+ -+#include -+#include -+ -+#include +#include ++#include ++#include ++#include ++#include +#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "aspeed-pcie-mmbi.h" ++#include ++#include + -+/* AST2700 E2M */ -+#define ASPEED_E2M_EVENT 0x0D0 -+#define ASPEED_E2M_EVENT_SET 0x0D4 -+#define ASPEED_E2M_EVENT_CLR 0x0D8 -+#define ASPEED_E2M_EVENT_EN 0x0DC -+#define ASPEED_E2M_ADRMAP00 0x100 -+#define ASPEED_E2M_WIRQA0 0x180 -+#define ASPEED_E2M_WIRQV0 0x1C0 -+#define ASPEED_E2M_SPROT_SIDG0 0x210 -+#define ASPEED_E2M_SPROT_CTL0 0x280 -+#define ASPEED_E2M_SPROT_ADR0 0x2C0 ++#define SSP_FILE_NAME "ast2600_ssp.bin" ++#define AST2600_CVIC_TRIGGER 0x28 ++#define AST2600_CVIC_PENDING_STATUS 0x18 ++#define AST2600_CVIC_PENDING_CLEAR 0x1C + -+/* AST2700 SCU */ -+#define ASPEED_SCU_DECODE_DEV BIT(18) -+#define ASPEED_SCU_INT_EN BIT(23) -+struct aspeed_platform { -+ int (*mmbi_init)(struct platform_device *pdev); -+}; ++#define SSP_CTRL_REG 0xa00 ++#define SSP_CTRL_RESET_ASSERT BIT(1) ++#define SSP_CTRL_EN BIT(0) + -+struct aspeed_pcie_mmbi { -+ struct device *dev; -+ struct regmap *device; -+ struct regmap *e2m; -+ int irq; -+ const struct aspeed_platform *platform; -+ /* E2M index */ -+ int id; -+ int pid; -+ int scu_bar_offset; -+ int e2m_index; -+ int e2m_h2b_int; ++#define SSP_MEM_BASE_REG 0xa04 ++#define SSP_IMEM_LIMIT_REG 0xa08 ++#define SSP_DMEM_LIMIT_REG 0xa0c ++#define SSP_CACHE_RANGE_REG 0xa40 ++#define SSP_CACHE_INVALID_REG 0xa44 ++#define SSP_CACHE_CTRL_REG 0xa48 ++#define SSP_CACHE_CLEAR_ICACHE BIT(2) ++#define SSP_CACHE_CLEAR_DCACHE BIT(1) ++#define SSP_CACHE_EN BIT(0) + -+ /* Memory Mapping */ -+ void __iomem *mem_virt; -+ dma_addr_t mem_phy; -+ phys_addr_t mem_size; ++#define SSP_TOTAL_MEM_SZ (32 * 1024 * 1024) ++#define SSP_CACHED_MEM_SZ (16 * 1024 * 1024) ++#define SSP_UNCACHED_MEM_SZ (SSP_TOTAL_MEM_SZ - SSP_CACHED_MEM_SZ) ++#define SSP_CACHE_1ST_16MB_ENABLE BIT(0) + -+ struct aspeed_mmbi_channel chan; ++struct ast2600_ssp { ++ struct device *dev; ++ struct regmap *scu; ++ dma_addr_t ssp_mem_phy_addr; ++ void __iomem *ssp_mem_vir_addr; ++ dma_addr_t ssp_shared_mem_phy_addr; ++ void __iomem *ssp_shared_mem_vir_addr; ++ int ssp_shared_mem_size; ++ void __iomem *cvic; ++ int irq[16]; ++ int n_irq; +}; + -+static void mmbi_desc_init(struct aspeed_mmbi_channel *chan); -+ -+static u8 mmbi_get_bmc_up(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ -+ return hros.b_up; -+} -+ -+static u8 mmbi_get_bmc_rdy(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ -+ return hros.b_rdy; -+} -+ -+static u8 mmbi_get_bmc_rst(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ -+ return hros.b_rst; -+} -+ -+static u8 mmbi_get_host_rst(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ -+ return hrws.h_rst; -+} -+ -+static u8 mmbi_get_host_rdy(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ -+ return hrws.h_rdy; -+} -+ -+static u8 mmbi_get_host_up(struct aspeed_mmbi_channel *chan) -+{ -+ struct host_rws hrws; -+ -+ memcpy_fromio(&hrws, chan->hrws_vmem, sizeof(hrws)); -+ -+ return hrws.h_up; -+} -+ -+static void mmbi_set_bmc_rst(struct aspeed_mmbi_channel *chan, bool set) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b_rst = set; -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); -+} -+ -+static void mmbi_set_bmc_rdy(struct aspeed_mmbi_channel *chan, bool set) -+{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b_rdy = set; -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); -+} -+ -+static void mmbi_set_bmc_up(struct aspeed_mmbi_channel *chan, bool set) ++static int ast_ssp_open(struct inode *inode, struct file *file) +{ -+ struct host_ros hros; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b_up = set; -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); ++ return 0; +} + -+static void raise_b2h_interrupt(struct aspeed_mmbi_channel *chan) ++static int ast_ssp_release(struct inode *inode, struct file *file) +{ -+ if (!chan->host_int_en) -+ return; -+ -+ regmap_write(chan->mmbi->e2m, ASPEED_E2M_EVENT_SET, BIT(chan->mmbi->e2m_h2b_int)); ++ return 0; +} + -+static void mmbi_clear_hros(struct aspeed_mmbi_channel *chan) -+{ -+ memset_io(chan->hros_vmem, 0, sizeof(struct host_ros)); -+} ++static const struct file_operations ast_ssp_fops = { ++ .owner = THIS_MODULE, ++ .open = ast_ssp_open, ++ .release = ast_ssp_release, ++ .llseek = no_llseek, ++}; + -+static void mmbi_clear_hrws(struct aspeed_mmbi_channel *chan) -+{ -+ memset_io(chan->hrws_vmem, 0, sizeof(struct host_rws)); -+} ++struct miscdevice ast_ssp_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "ast-ssp", ++ .fops = &ast_ssp_fops, ++}; + -+static void get_b2h_avail_buf_len(struct aspeed_mmbi_channel *chan, ssize_t *avail_buf_len) ++static irqreturn_t ast2600_ssp_interrupt(int irq, void *dev_id) +{ -+ struct device *dev = chan->dev; -+ u32 b2h_rp, b2h_wp; -+ -+ b2h_rp = GET_B2H_READ_POINTER(chan); -+ b2h_wp = GET_B2H_WRITE_POINTER(chan); -+ dev_dbg(dev, "MMBI B2H - b2h_rp: 0x%0x, b2h_wp: 0x%0x\n", b2h_rp, b2h_wp); ++ u32 i; ++ struct ast2600_ssp *priv = dev_id; ++ u32 isr = readl(priv->cvic + AST2600_CVIC_PENDING_STATUS); ++ u32 ssp_shared_rx_tx_size = priv->ssp_shared_mem_size / 2; ++ u32 *ssp_shared_mem_tx = priv->ssp_shared_mem_vir_addr; ++ u32 *ssp_shared_mem_rx = priv->ssp_shared_mem_vir_addr + ssp_shared_rx_tx_size; + -+ if (b2h_wp >= b2h_rp) -+ *avail_buf_len = chan->b2h_cb_size - b2h_wp + b2h_rp; -+ else -+ *avail_buf_len = b2h_rp - b2h_wp; -+} ++ dev_info(priv->dev, "isr %x\n", isr); ++ writel(isr, priv->cvic + AST2600_CVIC_PENDING_CLEAR); + -+static u8 mmbi_get_state(struct aspeed_mmbi_channel *chan) -+{ -+ u8 state = 0; ++ dev_info(priv->dev, "[CA7] rx addr:%08x, tx addr:%08x\n", ++ (u32)ssp_shared_mem_rx, (u32)ssp_shared_mem_tx); + -+ state = mmbi_get_bmc_up(chan) << 3; -+ state |= mmbi_get_bmc_rst(chan) << 2; -+ state |= mmbi_get_host_up(chan) << 1; -+ state |= mmbi_get_host_rst(chan); ++ /* Check the CA7 RX data from CM3 TX data. */ ++ dev_info(priv->dev, "CA7 RX data from CM3 TX data: "); ++ for (i = 0; i < ssp_shared_rx_tx_size / 4; i++) { ++ if (readl(ssp_shared_mem_rx + i) != 0) { ++ dev_info(priv->dev, "[%08x] %08x ", ++ (u32)(ssp_shared_mem_rx + i), readl(ssp_shared_mem_rx + i)); ++ } else { ++ break; ++ } ++ } + -+ return state; ++ return IRQ_HANDLED; +} + -+static int get_mmbi_header(struct aspeed_mmbi_channel *chan, u32 *data_length, -+ u8 *type, u32 *unread_data_len, u8 *padding) ++static int ast_ssp_probe(struct platform_device *pdev) +{ -+ u32 h2b_wp, h2b_rp, b2h_wp, b2h_rp; -+ struct device *dev = chan->dev; -+ struct mmbi_header header; -+ -+ h2b_wp = GET_H2B_WRITE_POINTER(chan); -+ h2b_rp = GET_H2B_READ_POINTER(chan); -+ b2h_wp = GET_B2H_WRITE_POINTER(chan); -+ b2h_rp = GET_B2H_READ_POINTER(chan); -+ dev_dbg(dev, "MMBI HRWS - h2b_wp: 0x%0x, b2h_rp: 0x%0x\n", h2b_wp, -+ b2h_rp); -+ dev_dbg(dev, "MMBI HROS - b2h_wp: 0x%0x, h2b_rp: 0x%0x\n", b2h_wp, -+ h2b_rp); -+ -+ if (h2b_wp >= h2b_rp) -+ *unread_data_len = h2b_wp - h2b_rp; -+ else -+ *unread_data_len = chan->h2b_cb_size - h2b_rp + h2b_wp; ++ struct device_node *np, *mnode = dev_of_node(&pdev->dev); ++ const struct firmware *firmware; ++ struct ast2600_ssp *priv; ++ struct reserved_mem *rmem; ++ int i, ret; + -+ if (*unread_data_len < sizeof(struct mmbi_header)) { -+ dev_dbg(dev, "No data to read(%d - %d)\n", h2b_wp, h2b_rp); -+ return -EAGAIN; ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ ret = -ENOMEM; ++ goto finish; + } + -+ dev_dbg(dev, "READ MMBI header from: 0x%lx\n", -+ (ssize_t)(chan->h2b_cb_vmem + h2b_rp)); -+ -+ /* Extract MMBI protocol - protocol type and length */ -+ if ((h2b_rp + sizeof(header)) <= chan->h2b_cb_size) { -+ memcpy_fromio(&header, chan->h2b_cb_vmem + h2b_rp, -+ sizeof(header)); -+ } else { -+ ssize_t chunk_len = chan->h2b_cb_size - h2b_rp; -+ -+ memcpy_fromio(&header, chan->h2b_cb_vmem + h2b_rp, chunk_len); -+ memcpy_fromio(((u8 *)&header) + chunk_len, chan->h2b_cb_vmem, -+ sizeof(header) - chunk_len); ++ priv->dev = &pdev->dev; ++ priv->scu = syscon_regmap_lookup_by_phandle(priv->dev->of_node, "aspeed,scu"); ++ if (IS_ERR(priv->scu)) { ++ dev_err(priv->dev, "failed to find SCU regmap\n"); ++ ret = -EINVAL; ++ goto finish; + } ++ platform_set_drvdata(pdev, priv); + -+ *data_length = (header.pkt_len << 2) - sizeof(header) - header.pkt_pad; -+ *padding = header.pkt_pad; -+ *type = header.pkt_type; -+ -+ return 0; -+} -+ -+static int mmbi_state_check(struct aspeed_mmbi_channel *chan) -+{ -+ enum mmbi_state current_state = mmbi_get_state(chan); -+ struct device *dev = chan->dev; -+ u32 req_data_len, unread_data_len; -+ u8 type, padding; -+ -+ switch (current_state) { -+ case INIT_MISMATCH: -+ dev_dbg(dev, "Get INIT_MISMATCH state from HOST"); -+ /* Reset MMBI data structure */ -+ mmbi_desc_init(chan); -+ /* Translat state to INIT_IN_PROGRESS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); -+ /* Translat state to INIT_COMPLETED*/ -+ mmbi_set_bmc_up(chan, 1); -+ -+ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); -+ raise_b2h_interrupt(chan); -+ return 1; -+ case NORMAL_RUNTIME: -+ if (mmbi_get_bmc_rdy(chan)) -+ return 0; -+ dev_dbg(dev, "Get NORMAL_RUNTIME state from HOST"); -+ mmbi_set_bmc_rdy(chan, 1); -+ return 1; -+ case RESET_REQ_BY_HOST: -+ dev_dbg(dev, "Get RESET_REQ_BY_HOST state from HOST"); -+ /* Stop operation */ -+ mmbi_set_bmc_rdy(chan, 0); -+ /* Change state to RESET_ACKED */ -+ mmbi_set_bmc_rst(chan, 1); -+ raise_b2h_interrupt(chan); -+ /* Change state to TRANS_TO_INIT */ -+ mmbi_set_bmc_up(chan, 0); -+ /* Reset MMBI data structure */ -+ mmbi_desc_init(chan); -+ /* Translat state to INIT_IN_PROGRESS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); -+ /* Translat state to INIT_COMPLETED*/ -+ mmbi_set_bmc_up(chan, 1); -+ -+ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); -+ raise_b2h_interrupt(chan); -+ return 1; -+ case RESET_ACKED: -+ /* Receive all packet from Host */ -+ while (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) == 0 && -+ mmbi_get_state(chan) == RESET_ACKED) -+ ; -+ /* Change state to TRANS_TO_INIT */ -+ mmbi_set_bmc_up(chan, 0); -+ /* Reset MMBI data structure */ -+ mmbi_desc_init(chan); -+ /* Translat state to INIT_IN_PROGRESS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); -+ /* Translat state to INIT_COMPLETED*/ -+ mmbi_set_bmc_up(chan, 1); -+ -+ dev_dbg(dev, "Change state to INIT_COMPLETED to HOST"); -+ raise_b2h_interrupt(chan); -+ default: -+ break; ++ ret = misc_register(&ast_ssp_misc); ++ if (ret) { ++ pr_err("can't misc_register :(\n"); ++ ret = -EIO; ++ goto finish; + } ++ dev_set_drvdata(ast_ssp_misc.this_device, pdev); + -+ return 0; -+} -+ -+static void update_host_ros(struct aspeed_mmbi_channel *chan, unsigned int w_len, -+ unsigned int r_len) -+{ -+ struct device *dev = chan->dev; -+ struct host_ros hros; -+ u32 h2b_rp, b2h_wp; -+ -+ b2h_wp = GET_B2H_WRITE_POINTER(chan); -+ h2b_rp = GET_H2B_READ_POINTER(chan); -+ -+ /* Advance the B2H CB offset for next write */ -+ if ((b2h_wp + w_len) <= chan->b2h_cb_size) -+ b2h_wp += w_len; -+ else -+ b2h_wp = b2h_wp + w_len - chan->b2h_cb_size; -+ -+ /* Advance the H2B CB offset till where BMC read data */ -+ if ((h2b_rp + r_len) <= chan->h2b_cb_size) -+ h2b_rp += r_len; -+ else -+ h2b_rp = h2b_rp + r_len - chan->h2b_cb_size; -+ -+ memcpy_fromio(&hros, chan->hros_vmem, sizeof(hros)); -+ hros.b2h_wp = FIELD_GET(B2H_WRITE_POINTER_MASK, b2h_wp); -+ hros.h2b_rp = FIELD_GET(H2B_READ_POINTER_MASK, h2b_rp); -+ memcpy_toio(chan->hros_vmem, &hros, sizeof(hros)); -+ dev_dbg(dev, "Updating HROS - h2b_rp: 0x%0x, b2h_wp: 0x%0x\n", h2b_rp, b2h_wp); -+ -+ if (w_len != 0) -+ raise_b2h_interrupt(chan); -+} -+ -+static int aspeed_mmbi_write(struct aspeed_mmbi_channel *chan, char *buffer, size_t len, -+ protocol_type type) -+{ -+ struct device *dev = chan->dev; -+ struct mmbi_header header = { 0 }; -+ ssize_t avail_buf_len; -+ ssize_t total_len; -+ ssize_t wt_offset; -+ ssize_t chunk_len; -+ ssize_t end_offset; -+ u8 padding = 0; -+ -+ /* If HOST READY bit is not set, Just discard the write. */ -+ if (!GET_HOST_READY_BIT(chan)) { -+ dev_dbg(dev, "Host not ready, discarding request...\n"); -+ return -EAGAIN; ++ ret = of_reserved_mem_device_init(&pdev->dev); ++ if (ret) { ++ dev_err(priv->dev, ++ "failed to initialize reserved mem: %d\n", ret); ++ ret = -ENOMEM; ++ goto finish; + } + -+ get_b2h_avail_buf_len(chan, &avail_buf_len); -+ -+ dev_dbg(dev, "B2H buffer empty space: %ld\n", avail_buf_len); -+ -+ /* Header size */ -+ total_len = len + 4; -+ -+ padding = total_len & 0x3; -+ if (padding) -+ padding = 4 - padding; -+ total_len += padding; -+ -+ /* Empty space should be more than write request data size */ -+ if (avail_buf_len <= sizeof(header) || (total_len > (avail_buf_len - sizeof(header)))) -+ return -ENOSPC; -+ -+ /* Fill multi-protocol header */ -+ header.pkt_type = type; -+ header.pkt_len = total_len >> 2; -+ header.pkt_pad = padding; -+ -+ wt_offset = GET_B2H_WRITE_POINTER(chan); -+ end_offset = chan->b2h_cb_size; -+ -+ /* Copy Header */ -+ if ((end_offset - wt_offset) >= sizeof(header)) { -+ memcpy_toio(chan->b2h_cb_vmem + wt_offset, &header, sizeof(header)); -+ wt_offset += sizeof(header); -+ } else { -+ chunk_len = end_offset - wt_offset; -+ memcpy_toio(chan->b2h_cb_vmem + wt_offset, &header, chunk_len); -+ memcpy_toio(chan->b2h_cb_vmem, &header + chunk_len, (sizeof(header) - chunk_len)); -+ wt_offset = (sizeof(header) - chunk_len); ++ np = of_parse_phandle(priv->dev->of_node, "memory-region", 0); ++ if (!np) { ++ dev_err(priv->dev, "can't find memory-region node\n"); ++ ret = -ENOMEM; ++ goto finish; + } + -+ /* Write the data */ -+ if ((end_offset - wt_offset) >= len) { -+ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer, len); -+ wt_offset += len; ++ rmem = of_reserved_mem_lookup(np); ++ of_node_put(np); ++ if (!rmem) { ++ dev_err(priv->dev, "can't find reserved memory.\n"); ++ ret = -ENOMEM; ++ goto finish; + } else { -+ chunk_len = end_offset - wt_offset; -+ dev_dbg(dev, "Write data chunk_len: %ld\n", chunk_len); -+ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer, chunk_len); -+ -+ wt_offset = 0; -+ memcpy_toio(&chan->b2h_cb_vmem[wt_offset], buffer + chunk_len, len - chunk_len); -+ wt_offset += len - chunk_len; ++ priv->ssp_mem_phy_addr = rmem->base; ++ priv->ssp_mem_vir_addr = devm_ioremap(priv->dev, priv->ssp_mem_phy_addr, SSP_TOTAL_MEM_SZ); ++ if (!priv->ssp_mem_vir_addr) { ++ dev_err(priv->dev, "can't create reserved memory.\n"); ++ ret = -ENOMEM; ++ goto finish; ++ } else { ++ dev_info(priv->dev, "\nSSP memory: virt(0x%08x), phys(0x%08x)\n", ++ (uint32_t)priv->ssp_mem_vir_addr, priv->ssp_mem_phy_addr); ++ } + } + -+ update_host_ros(chan, total_len, 0); -+ -+ return 0; -+} -+ -+static void aspeed_mmbi_read(struct aspeed_mmbi_channel *chan, char *buffer, size_t len, u8 padding) -+{ -+ struct device *dev = chan->dev; -+ ssize_t rd_offset; -+ u32 h2b_rp; -+ -+ h2b_rp = GET_H2B_READ_POINTER(chan); -+ if ((h2b_rp + sizeof(struct mmbi_header)) <= chan->h2b_cb_size) -+ rd_offset = h2b_rp + sizeof(struct mmbi_header); -+ else -+ rd_offset = h2b_rp + sizeof(struct mmbi_header) - chan->h2b_cb_size; -+ -+ /* Extract data and copy to user space application */ -+ dev_dbg(dev, "READ MMBI Data from: 0x%0lx and length: %ld\n", -+ (ssize_t)(chan->h2b_cb_vmem + rd_offset), len); -+ -+ if ((chan->h2b_cb_size - rd_offset) >= len) { -+ memcpy_fromio(buffer, chan->h2b_cb_vmem + rd_offset, len); -+ rd_offset += len; -+ } else { -+ ssize_t chunk_len; -+ -+ chunk_len = chan->h2b_cb_size - rd_offset; -+ dev_dbg(dev, "Read data chunk_len: %ld\n", chunk_len); -+ memcpy_fromio(buffer, chan->h2b_cb_vmem + rd_offset, chunk_len); -+ -+ rd_offset = 0; -+ memcpy_fromio(buffer + chunk_len, chan->h2b_cb_vmem + rd_offset, len - chunk_len); ++ if (of_property_read_u32(np, "shm-size", &priv->ssp_shared_mem_size)) { ++ dev_err(priv->dev, "can't find shm-size property\n"); ++ ret = -ENOMEM; ++ goto finish; + } + -+ update_host_ros(chan, 0, len + sizeof(struct mmbi_header) + padding); -+} -+ -+static void mctp_mmbi_rx(struct aspeed_mmbi_channel *chan) -+{ -+ struct net_device *ndev = chan->ndev; -+ struct sk_buff *skb; -+ struct mctp_skb_cb *cb; -+ u32 req_data_len, unread_data_len; -+ u8 type, padding; -+ int status; -+ -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ return; -+ -+ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d, Unread data: %d\n", __func__, -+ req_data_len, type, unread_data_len); ++ priv->ssp_shared_mem_vir_addr = priv->ssp_mem_vir_addr + SSP_TOTAL_MEM_SZ ++ - priv->ssp_shared_mem_size; ++ priv->ssp_shared_mem_phy_addr = priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ ++ - priv->ssp_shared_mem_size; ++ dev_info(priv->dev, "\nSSP shared memory: virt(0x%08x), phys(0x%08x), size(0x%08x)\n", ++ (uint32_t)priv->ssp_shared_mem_vir_addr, priv->ssp_shared_mem_phy_addr, ++ priv->ssp_shared_mem_size); + -+ skb = netdev_alloc_skb(ndev, req_data_len); -+ if (!skb) { -+ ndev->stats.rx_dropped++; -+ update_host_ros(chan, 0, req_data_len + sizeof(struct mmbi_header)); -+ return; ++ if (request_firmware(&firmware, SSP_FILE_NAME, priv->dev) < 0) { ++ dev_err(priv->dev, "don't have %s\n", SSP_FILE_NAME); ++ release_firmware(firmware); ++ ret = -EINVAL; ++ goto finish; + } + -+ skb->protocol = htons(ETH_P_MCTP); -+ aspeed_mmbi_read(chan, skb_put(skb, req_data_len), req_data_len, padding); -+ skb_reset_network_header(skb); -+ -+ cb = __mctp_cb(skb); -+ cb->halen = 0; ++ memcpy(priv->ssp_mem_vir_addr, (void *)firmware->data, firmware->size); ++ release_firmware(firmware); + -+ status = netif_rx(skb); -+ if (status == NET_RX_SUCCESS) { -+ ndev->stats.rx_packets++; -+ ndev->stats.rx_bytes += req_data_len; -+ } else { -+ ndev->stats.rx_dropped++; ++ np = of_parse_phandle(mnode, "aspeed,cvic", 0); ++ if (!np) { ++ dev_err(priv->dev, "can't find CVIC\n"); ++ ret = -EINVAL; ++ goto finish; + } -+} -+ -+static netdev_tx_t mctp_mmbi_tx(struct sk_buff *skb, struct net_device *ndev) -+{ -+ struct aspeed_mmbi_mctp *mctp = netdev_priv(ndev); -+ int ret; + -+ if (!mmbi_get_host_rdy(&mctp->mmbi->chan) || skb->len > MCTP_MMBI_MTU_MAX) { -+ ndev->stats.tx_dropped++; -+ goto out; ++ priv->cvic = devm_of_iomap(priv->dev, np, 0, NULL); ++ if (IS_ERR(priv->cvic)) { ++ dev_err(priv->dev, "can't map CVIC\n"); ++ ret = -EINVAL; ++ goto finish; + } + -+ ret = aspeed_mmbi_write(&mctp->mmbi->chan, skb->data, skb->len, MMBI_PROTOCOL_MCTP); -+ if (ret) { -+ netif_stop_queue(ndev); -+ return NETDEV_TX_BUSY; ++ i = 0; ++ while (0 != (priv->irq[i] = irq_of_parse_and_map(mnode, i))) { ++ ret = request_irq(priv->irq[i], ast2600_ssp_interrupt, 0, ++ "ssp-sw-irq", priv); ++ i++; + } ++ priv->n_irq = i; ++ dev_info(priv->dev, "%d ISRs registered\n", priv->n_irq); + -+ ndev->stats.tx_packets++; -+ ndev->stats.tx_bytes += skb->len; -+out: -+ kfree_skb(skb); -+ return NETDEV_TX_OK; -+} -+ -+static const struct net_device_ops mctp_mmbi_netdev_ops = { -+ .ndo_start_xmit = mctp_mmbi_tx, -+}; -+ -+static void aspeed_mctp_mmbi_setup(struct net_device *ndev) -+{ -+ ndev->type = ARPHRD_MCTP; -+ -+ /* we limit at the fixed MTU, which is also the MCTP-standard -+ * baseline MTU, so is also our minimum -+ */ -+ ndev->mtu = MCTP_MMBI_MTU; -+ ndev->max_mtu = MCTP_MMBI_MTU_MAX; -+ ndev->min_mtu = MCTP_MMBI_MTU_MIN; -+ -+ ndev->hard_header_len = 0; -+ ndev->addr_len = 0; -+ ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; -+ ndev->flags = IFF_NOARP; -+ ndev->netdev_ops = &mctp_mmbi_netdev_ops; -+ ndev->needs_free_netdev = true; -+} -+ -+static int aspeed_mmbi_mctp_init(struct aspeed_mmbi_channel *chan) -+{ -+ struct aspeed_mmbi_mctp *mctp; -+ struct net_device *ndev; -+ char name[32]; -+ int ret; -+ -+ snprintf(name, sizeof(name), "mctpmmbi%d%d", chan->mmbi->id, chan->mmbi->e2m_index); -+ ndev = alloc_netdev(sizeof(*mctp), name, NET_NAME_ENUM, aspeed_mctp_mmbi_setup); -+ if (!ndev) -+ return -ENOMEM; -+ -+ mctp = netdev_priv(ndev); -+ mctp->ndev = ndev; -+ mctp->mmbi = chan->mmbi; -+ -+ chan->ndev = ndev; -+ -+ ret = register_netdev(ndev); -+ if (ret) -+ goto free_netdev; ++ regmap_write(priv->scu, SSP_CTRL_REG, 0); ++ mdelay(1); ++ regmap_write(priv->scu, SSP_MEM_BASE_REG, priv->ssp_mem_phy_addr); ++ regmap_write(priv->scu, SSP_IMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_CACHED_MEM_SZ); ++ regmap_write(priv->scu, SSP_DMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ); + -+ return 0; ++ regmap_write(priv->scu, SSP_CACHE_RANGE_REG, SSP_CACHE_1ST_16MB_ENABLE); + -+free_netdev: -+ free_netdev(ndev); ++ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_RESET_ASSERT); ++ mdelay(1); ++ regmap_write(priv->scu, SSP_CTRL_REG, 0); ++ mdelay(1); ++ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_EN); + ++ dev_info(priv->dev, "Init successful\n"); ++ ret = 0; ++finish: + return ret; +} + -+static void aspeed_mmbi_work_func(struct work_struct *workq) ++static int ast_ssp_remove(struct platform_device *pdev) +{ -+ struct aspeed_mmbi_channel *chan = container_of(workq, struct aspeed_mmbi_channel, work); -+ u32 weight = 256, req_data_len, unread_data_len; -+ u8 type, padding; ++ struct ast2600_ssp *priv = platform_get_drvdata(pdev); + int i; + -+ for (i = 0; i < weight; i++) { -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ return; -+ -+ dev_dbg(chan->dev, "%s: Length: 0x%0x, Protocol Type: %d\n", -+ __func__, req_data_len, type); ++ dev_info(priv->dev, "SSP module removed\n"); ++ regmap_write(priv->scu, SSP_CTRL_REG, 0); ++ for (i = 0; i < priv->n_irq; i++) ++ free_irq(priv->irq[i], priv); + -+ if (type == MMBI_PROTOCOL_MCTP) -+ mctp_mmbi_rx(chan); -+ else -+ /* Discard data and advance the hrws */ -+ update_host_ros(chan, 0, req_data_len + sizeof(struct mmbi_header) + padding); ++ kfree(priv); + -+ raise_b2h_interrupt(chan); -+ } ++ misc_deregister((struct miscdevice *)&ast_ssp_misc); + -+ if (get_mmbi_header(chan, &req_data_len, &type, &unread_data_len, &padding) != 0) -+ queue_work(system_unbound_wq, &chan->work); ++ return 0; +} + -+static irqreturn_t aspeed_pcie_mmbi_isr(int irq, void *dev_id) -+{ -+ struct aspeed_pcie_mmbi *mmbi = dev_id; -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ ssize_t avail_buf_len; -+ -+ get_b2h_avail_buf_len(chan, &avail_buf_len); -+ if (avail_buf_len > MCTP_MMBI_MTU_MAX) { -+ if (netif_queue_stopped(chan->ndev)) { -+ dev_dbg(chan->dev, "Wake up mctp net device\n"); -+ netif_wake_queue(chan->ndev); -+ } -+ } -+ -+ if (mmbi_state_check(chan)) -+ return IRQ_HANDLED; ++static const struct of_device_id of_ast_ssp_match_table[] = { ++ { .compatible = "aspeed,ast2600-ssp", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, of_ast_ssp_match_table); + -+ queue_work(system_unbound_wq, &chan->work); ++static struct platform_driver ast_ssp_driver = { ++ .probe = ast_ssp_probe, ++ .remove = ast_ssp_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = of_ast_ssp_match_table, ++ }, ++}; + -+ return IRQ_HANDLED; -+} ++module_platform_driver(ast_ssp_driver); + -+static void mmbi_desc_init(struct aspeed_mmbi_channel *chan) -+{ -+ struct mmbi_cap_desc desc; ++MODULE_LICENSE("Dual BSD/GPL"); +diff --git a/drivers/soc/aspeed/aspeed-uart-routing.c b/drivers/soc/aspeed/aspeed-uart-routing.c +--- a/drivers/soc/aspeed/aspeed-uart-routing.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-uart-routing.c 2026-07-17 08:44:55.290306551 +0000 +@@ -15,20 +15,30 @@ + #define HICRA 0x9c + + /* attributes options */ ++#define UART_ROUTING_IO0 "io0" + #define UART_ROUTING_IO1 "io1" + #define UART_ROUTING_IO2 "io2" + #define UART_ROUTING_IO3 "io3" + #define UART_ROUTING_IO4 "io4" + #define UART_ROUTING_IO5 "io5" + #define UART_ROUTING_IO6 "io6" ++#define UART_ROUTING_IO7 "io7" ++#define UART_ROUTING_IO8 "io8" ++#define UART_ROUTING_IO9 "io9" + #define UART_ROUTING_IO10 "io10" ++#define UART_ROUTING_IO12 "io12" ++#define UART_ROUTING_UART0 "uart0" + #define UART_ROUTING_UART1 "uart1" + #define UART_ROUTING_UART2 "uart2" + #define UART_ROUTING_UART3 "uart3" + #define UART_ROUTING_UART4 "uart4" + #define UART_ROUTING_UART5 "uart5" + #define UART_ROUTING_UART6 "uart6" ++#define UART_ROUTING_UART7 "uart7" ++#define UART_ROUTING_UART8 "uart8" ++#define UART_ROUTING_UART9 "uart9" + #define UART_ROUTING_UART10 "uart10" ++#define UART_ROUTING_UART12 "uart12" + #define UART_ROUTING_RES "reserved" + + struct aspeed_uart_routing { +@@ -488,6 +498,416 @@ + .attrs = ast2600_uart_routing_attrs, + }; + ++/* routing selector for AST27xx node 0 */ ++static struct aspeed_uart_routing_selector ast2700n0_uart9_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART9), ++ .reg = HICR9, ++ .shift = 12, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_IO9, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART12, ++ NULL, ++ }, ++}; + -+ memset(&desc, 0, sizeof(struct mmbi_cap_desc)); -+ -+ desc.version = 1; -+ /* This MMBI interface is intended for OS use */ -+ desc.os_use = 1; -+ desc.b2h_ba = (chan->b2h_cb_vmem - chan->desc_vmem) >> 3; -+ desc.h2b_ba = (chan->h2b_cb_vmem - chan->desc_vmem) >> 3; -+ /* Make sure the buffer size is 4 byte aligmnent */ -+ desc.b2h_l = chan->b2h_cb_size & ~0x3; -+ desc.h2b_l = chan->h2b_cb_size & ~0x3; -+ /* Variable Packet Size Circular Buffers (VPSCB) v1 */ -+ desc.buffer_type = 0x01; -+ desc.bt_desc.h_ros_p = (chan->hros_vmem - chan->desc_vmem) >> 3; -+ desc.bt_desc.h_rws_p = (chan->hrws_vmem - chan->desc_vmem) >> 3; -+ /* PCIe Interrupt */ -+ desc.bt_desc.h_int_t = 0x01; -+ desc.bt_desc.h_int_l = chan->host_int_location; -+ desc.bt_desc.h_int_v = 0; /* Skip for PCIe Interrupt */ -+ desc.bt_desc.bmc_int_t = 0x01; /* relative memory space address */ -+ desc.bt_desc.bmc_int_l = chan->bmc_int_location; -+ desc.bt_desc.bmc_int_v = chan->bmc_int_value; -+ -+ /* Per MMBI protoco spec, Set it to "#MMBI$" */ -+ memcpy(desc.signature, MMBI_SIGNATURE, sizeof(desc.signature)); -+ -+ memcpy_toio(chan->desc_vmem, &desc, sizeof(desc)); -+} -+ -+static int aspeed_pcie_mmbi_init(struct aspeed_pcie_mmbi *mmbi) -+{ -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ struct device *dev = chan->dev; -+ u32 b2h_size = mmbi->mem_size >> 1; -+ u32 h2b_size = mmbi->mem_size >> 1; -+ u8 *h2b_vaddr, *b2h_vaddr; -+ int ret; -+ -+ b2h_vaddr = mmbi->mem_virt; -+ h2b_vaddr = b2h_vaddr + b2h_size; -+ -+ chan->dev = dev; -+ chan->desc_vmem = b2h_vaddr; -+ chan->hros_vmem = b2h_vaddr + sizeof(struct mmbi_cap_desc); -+ chan->b2h_cb_vmem = b2h_vaddr + sizeof(struct mmbi_cap_desc) + sizeof(struct host_ros); -+ chan->b2h_cb_size = b2h_size - sizeof(struct mmbi_cap_desc) - sizeof(struct host_ros); -+ -+ chan->hrws_vmem = h2b_vaddr; -+ chan->h2b_cb_vmem = h2b_vaddr + sizeof(struct host_rws); -+ chan->h2b_cb_size = h2b_size - sizeof(struct host_rws); ++static struct aspeed_uart_routing_selector ast2700n0_io9_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO9), ++ .reg = HICR9, ++ .shift = 8, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART12, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART9, ++ NULL, ++ }, ++}; + -+ dev_dbg(dev, "B2H mapped addr - desc: 0x%0lx, hros: 0x%0lx, b2h_cb: 0x%0lx\n", -+ (size_t)chan->desc_vmem, (size_t)chan->hros_vmem, (size_t)chan->b2h_cb_vmem); -+ dev_dbg(dev, "H2B mapped addr - hrws: 0x%0lx, h2b_cb: 0x%0lx\n", (size_t)chan->hrws_vmem, -+ (size_t)chan->h2b_cb_vmem); ++static struct aspeed_uart_routing_selector ast2700n0_uart3_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART3), ++ .reg = HICRA, ++ .shift = 25, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ dev_dbg(dev, "B2H buffer size: 0x%0lx\n", (size_t)chan->b2h_cb_size); -+ dev_dbg(dev, "H2B buffer size: 0x%0lx\n", (size_t)chan->h2b_cb_size); ++static struct aspeed_uart_routing_selector ast2700n0_uart2_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART2), ++ .reg = HICRA, ++ .shift = 22, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ /* Initialize the MMBI channel descriptor */ -+ mmbi_desc_init(chan); ++static struct aspeed_uart_routing_selector ast2700n0_uart1_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART1), ++ .reg = HICRA, ++ .shift = 19, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO0, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART0, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ /* Clear HRWS & HROS */ -+ mmbi_clear_hros(chan); -+ mmbi_clear_hrws(chan); ++static struct aspeed_uart_routing_selector ast2700n0_uart0_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART0), ++ .reg = HICRA, ++ .shift = 16, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ /* Initialize MTCP function */ -+ ret = aspeed_mmbi_mctp_init(chan); -+ if (ret) { -+ dev_err(dev, "Unable to init mctp\n"); -+ return ret; -+ } ++static struct aspeed_uart_routing_selector ast2700n0_io3_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO3), ++ .reg = HICRA, ++ .shift = 9, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ /* Set BMC UP bit */ -+ mmbi_set_bmc_up(chan, 1); ++static struct aspeed_uart_routing_selector ast2700n0_io2_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO2), ++ .reg = HICRA, ++ .shift = 6, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_IO0, ++ UART_ROUTING_IO1, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ return 0; -+} ++static struct aspeed_uart_routing_selector ast2700n0_io1_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO1), ++ .reg = HICRA, ++ .shift = 3, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_UART0, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+/* -+ * AST2700 PCIe MMBI (SCU & E2M) -+ * SoC | 0 | 1 | -+ * PCI class | MFD (0xFF_00_00) | MMBI (0x0C_0C_00) | -+ * Node | 0 1 | 0 | -+ * PID | 3 4 5 6 11 12 13 14 | 2 3 4 5 6 7 | -+ * E2M index | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 | -+ * BAR index | 2 3 4 5 2 3 4 5 | 0 1 2 3 4 5 | -+ * SCU BAR | 3c 4c 5c 6c 3c 4c 5c 6c | 1c 50 3c 4c 5c 6c | -+ * E2M H2B Int | 0 1 2 3 0 1 2 3 | 0 1 2 3 4 5 | (bit) -+ */ -+static int aspeed_ast2700_pcie_mmbi_init(struct platform_device *pdev) -+{ -+ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); -+ struct aspeed_mmbi_channel *chan = &mmbi->chan; -+ struct device *dev = &pdev->dev; -+ u32 value, sprot_size, e2m_index, pid; -+ struct resource res; -+ int ret, i; ++static struct aspeed_uart_routing_selector ast2700n0_io0_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO0), ++ .reg = HICRA, ++ .shift = 0, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART0, ++ UART_ROUTING_UART1, ++ UART_ROUTING_UART2, ++ UART_ROUTING_UART3, ++ UART_ROUTING_UART9, ++ UART_ROUTING_IO2, ++ UART_ROUTING_IO3, ++ UART_ROUTING_IO9, ++ NULL, ++ }, ++}; + -+ /* Get register map*/ -+ mmbi->e2m = syscon_node_to_regmap(dev->of_node->parent); -+ if (IS_ERR(mmbi->e2m)) { -+ dev_err(dev, "failed to find e2m regmap\n"); -+ return PTR_ERR(mmbi->e2m); -+ } -+ if (of_address_to_resource(dev->of_node->parent, 0, &res)) { -+ dev_err(dev, "Failed to get e2m resource\n"); -+ return -EINVAL; -+ } -+ if (res.start == 0x14c1d000) -+ mmbi->id = 2; -+ else if (res.start == 0x12c22000) -+ mmbi->id = 1; -+ else -+ mmbi->id = 0; /* 0x12c21000 */ ++static struct attribute *ast2700n0_uart_routing_attrs[] = { ++ &ast2700n0_uart9_sel.dev_attr.attr, ++ &ast2700n0_io9_sel.dev_attr.attr, ++ &ast2700n0_uart3_sel.dev_attr.attr, ++ &ast2700n0_uart2_sel.dev_attr.attr, ++ &ast2700n0_uart1_sel.dev_attr.attr, ++ &ast2700n0_uart0_sel.dev_attr.attr, ++ &ast2700n0_io3_sel.dev_attr.attr, ++ &ast2700n0_io2_sel.dev_attr.attr, ++ &ast2700n0_io1_sel.dev_attr.attr, ++ &ast2700n0_io0_sel.dev_attr.attr, ++ NULL, ++}; + -+ mmbi->device = syscon_regmap_lookup_by_phandle(dev->of_node->parent, "aspeed,device"); -+ if (IS_ERR(mmbi->device)) { -+ dev_err(dev, "failed to find device regmap\n"); -+ return PTR_ERR(mmbi->device); -+ } ++static const struct attribute_group ast2700n0_uart_routing_attr_group = { ++ .attrs = ast2700n0_uart_routing_attrs, ++}; + -+ ret = of_property_read_u32(dev->of_node, "index", &mmbi->e2m_index); -+ if (ret < 0) { -+ dev_err(dev, "cannot get mmbi index value\n"); -+ return ret; -+ } ++/* routing selector for AST27xx node 1 */ ++static struct aspeed_uart_routing_selector ast2700n1_uart10_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART10), ++ .reg = HICR9, ++ .shift = 12, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_IO10, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART12, ++ NULL, ++ }, ++}; + -+ ret = of_property_read_u32(dev->of_node, "pid", &mmbi->pid); -+ if (ret < 0) { -+ dev_err(dev, "cannot get mmbi pid value\n"); -+ return ret; -+ } ++static struct aspeed_uart_routing_selector ast2700n1_io10_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO10), ++ .reg = HICR9, ++ .shift = 8, ++ .mask = 0xf, ++ .options = { ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART12, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_RES, ++ UART_ROUTING_UART10, ++ NULL, ++ }, ++}; + -+ ret = of_property_read_u32(dev->of_node, "bar", &mmbi->scu_bar_offset); -+ if (ret < 0) { -+ dev_err(dev, "cannot get mmbi bar value\n"); -+ return ret; -+ } ++static struct aspeed_uart_routing_selector ast2700n1_uart8_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART8), ++ .reg = HICRA, ++ .shift = 25, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ e2m_index = mmbi->e2m_index; -+ pid = mmbi->pid; -+ mmbi->e2m_h2b_int += mmbi->e2m_index; -+ if (mmbi->id < 2) { -+ /* PCIe device class, sub-class, protocol and reversion */ -+ regmap_write(mmbi->device, 0x18, 0xFF000027); -+ } else { -+ regmap_write(mmbi->device, 0x18, 0x0C0C0027); -+ regmap_write(mmbi->device, 0x78, ASPEED_SCU_INT_EN | ASPEED_SCU_DECODE_DEV); -+ } ++static struct aspeed_uart_routing_selector ast2700n1_uart7_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART7), ++ .reg = HICRA, ++ .shift = 22, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ /* MSI */ -+ regmap_update_bits(mmbi->device, 0x74, GENMASK(7, 4), BIT(7) | (5 << 4)); ++static struct aspeed_uart_routing_selector ast2700n1_uart6_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART6), ++ .reg = HICRA, ++ .shift = 19, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO5, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART5, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ regmap_update_bits(mmbi->device, 0x70, BIT(25) | BIT(17) | BIT(9) | BIT(1), -+ BIT(25) | BIT(17) | BIT(9) | BIT(1)); ++static struct aspeed_uart_routing_selector ast2700n1_uart5_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART5), ++ .reg = HICRA, ++ .shift = 16, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ /* Calculate the BAR Size */ -+ for (i = 1; i < 16; i++) { -+ /* bar size check for 4k align */ -+ if ((mmbi->mem_size / 4096) == (1 << (i - 1))) -+ break; -+ } -+ if (i == 16) { -+ i = 0; -+ dev_warn(dev, "Bar size not align for 4K : %dK\n", (u32)mmbi->mem_size / 1024); -+ } -+ regmap_write(mmbi->device, mmbi->scu_bar_offset, (mmbi->mem_phy >> 4) | i); -+ regmap_write(mmbi->e2m, ASPEED_E2M_ADRMAP00 + (4 * pid), (mmbi->mem_phy >> 4) | i); ++static struct aspeed_uart_routing_selector ast2700n1_io8_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO8), ++ .reg = HICRA, ++ .shift = 9, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ /* BMC Interrupt */ -+ if (chan->bmc_int_en) { -+ value = mmbi->mem_phy + chan->bmc_int_location; -+ regmap_write(mmbi->e2m, ASPEED_E2M_WIRQA0 + (4 * e2m_index), value); -+ value = (BIT(16) << pid) | chan->bmc_int_value; -+ regmap_write(mmbi->e2m, ASPEED_E2M_WIRQV0 + (4 * e2m_index), value); -+ } -+ -+ /* HOST Interrupt: MSI */ -+ regmap_read(mmbi->e2m, ASPEED_E2M_EVENT_EN, &value); -+ value |= BIT(mmbi->e2m_h2b_int); -+ regmap_write(mmbi->e2m, ASPEED_E2M_EVENT_EN, value); -+ -+ /* B2H Write Protect */ -+ sprot_size = (mmbi->mem_size / 2) / SZ_1M; -+ value = (sprot_size << 16) | (mmbi->mem_phy >> 20); -+ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_ADR0 + (4 * e2m_index), value); -+ /* Enable read & disalbe write */ -+ value = 1 << (8 + e2m_index); -+ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_CTL0 + (4 * e2m_index), value); -+ /* Set PID */ -+ regmap_read(mmbi->e2m, ASPEED_E2M_SPROT_SIDG0 + (4 * (e2m_index / 4)), &value); -+ value |= pid << (8 * (e2m_index % 4)); -+ regmap_write(mmbi->e2m, ASPEED_E2M_SPROT_SIDG0 + (4 * (e2m_index / 4)), value); -+ -+ mmbi->chan.dev = dev; -+ mmbi->chan.mmbi = mmbi; -+ ret = aspeed_pcie_mmbi_init(mmbi); -+ if (ret < 0) { -+ dev_err(dev, "Initialize MMBI device failed.\n"); -+ return ret; -+ } ++static struct aspeed_uart_routing_selector ast2700n1_io7_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO7), ++ .reg = HICRA, ++ .shift = 6, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_IO5, ++ UART_ROUTING_IO6, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ INIT_WORK(&chan->work, aspeed_mmbi_work_func); ++static struct aspeed_uart_routing_selector ast2700n1_io6_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO6), ++ .reg = HICRA, ++ .shift = 3, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_UART5, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+ return 0; -+} ++static struct aspeed_uart_routing_selector ast2700n1_io5_sel = { ++ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO5), ++ .reg = HICRA, ++ .shift = 0, ++ .mask = 0x7, ++ .options = { ++ UART_ROUTING_UART5, ++ UART_ROUTING_UART6, ++ UART_ROUTING_UART7, ++ UART_ROUTING_UART8, ++ UART_ROUTING_UART10, ++ UART_ROUTING_IO7, ++ UART_ROUTING_IO8, ++ UART_ROUTING_IO10, ++ NULL, ++ }, ++}; + -+struct aspeed_platform ast2700_platform = { -+ .mmbi_init = aspeed_ast2700_pcie_mmbi_init, ++static struct attribute *ast2700n1_uart_routing_attrs[] = { ++ &ast2700n1_uart10_sel.dev_attr.attr, ++ &ast2700n1_io10_sel.dev_attr.attr, ++ &ast2700n1_uart8_sel.dev_attr.attr, ++ &ast2700n1_uart7_sel.dev_attr.attr, ++ &ast2700n1_uart6_sel.dev_attr.attr, ++ &ast2700n1_uart5_sel.dev_attr.attr, ++ &ast2700n1_io8_sel.dev_attr.attr, ++ &ast2700n1_io7_sel.dev_attr.attr, ++ &ast2700n1_io6_sel.dev_attr.attr, ++ &ast2700n1_io5_sel.dev_attr.attr, ++ NULL, +}; + -+static const struct of_device_id aspeed_pcie_mmbi_of_matches[] = { -+ { .compatible = "aspeed,ast2700-pcie-mmbi", .data = &ast2700_platform }, -+ {}, ++static const struct attribute_group ast2700n1_uart_routing_attr_group = { ++ .attrs = ast2700n1_uart_routing_attrs, +}; -+MODULE_DEVICE_TABLE(of, aspeed_pcie_mmbi_of_matches); + -+static int aspeed_pcie_mmbi_probe(struct platform_device *pdev) -+{ -+ struct aspeed_pcie_mmbi *mmbi; -+ struct aspeed_mmbi_channel *chan; -+ struct device *dev = &pdev->dev; -+ struct resource res; -+ struct device_node *np; -+ const void *md; -+ int ret = 0; + static ssize_t aspeed_uart_routing_show(struct device *dev, + struct device_attribute *attr, + char *buf) +@@ -580,6 +1000,10 @@ + .data = &ast2500_uart_routing_attr_group }, + { .compatible = "aspeed,ast2600-uart-routing", + .data = &ast2600_uart_routing_attr_group }, ++ { .compatible = "aspeed,ast2700n0-uart-routing", ++ .data = &ast2700n0_uart_routing_attr_group }, ++ { .compatible = "aspeed,ast2700n1-uart-routing", ++ .data = &ast2700n1_uart_routing_attr_group }, + { }, + }; + +diff --git a/drivers/soc/aspeed/aspeed-udma.c b/drivers/soc/aspeed/aspeed-udma.c +--- a/drivers/soc/aspeed/aspeed-udma.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-udma.c 2026-07-17 08:44:55.290306551 +0000 +@@ -0,0 +1,424 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright 2020 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ md = of_device_get_match_data(dev); -+ if (!md) -+ return -ENODEV; ++#define DEVICE_NAME "aspeed-udma" + -+ mmbi = devm_kzalloc(&pdev->dev, sizeof(struct aspeed_pcie_mmbi), GFP_KERNEL); -+ if (!mmbi) -+ return -ENOMEM; -+ dev_set_drvdata(dev, mmbi); ++/* UART DMA registers offset */ ++#define UDMA_TX_DMA_EN 0x000 ++#define UDMA_RX_DMA_EN 0x004 ++#define UDMA_MISC 0x008 ++#define UDMA_MISC_RX_BUFSZ GENMASK(3, 2) ++#define UDMA_MISC_TX_BUFSZ GENMASK(1, 0) ++#define UDMA_TMOUT_TIMER 0x00c ++#define UDMA_TX_DMA_RST 0x020 ++#define UDMA_RX_DMA_RST 0x024 ++#define UDMA_TX_DMA_INT_EN 0x030 ++#define UDMA_TX_DMA_INT_STS 0x034 ++#define UDMA_RX_DMA_INT_EN 0x038 ++#define UDMA_RX_DMA_INT_STS 0x03c + -+ mmbi->dev = dev; -+ mmbi->platform = md; ++#define UDMA_CHX_OFF(x) ((x) * 0x20) ++#define UDMA_CHX_TX_RD_PTR(x) (0x040 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_WR_PTR(x) (0x044 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_BUF_ADDR(x) (0x048 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_TX_CTRL(x) (0x04c + UDMA_CHX_OFF(x)) ++#define UDMA_TX_CTRL_BUF_ADDRH GENMASK(10, 8) ++#define UDMA_TX_CTRL_TMOUT_DIS BIT(4) ++#define UDMA_TX_CTRL_BUFSZ GENMASK(3, 0) ++#define UDMA_CHX_RX_RD_PTR(x) (0x050 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_WR_PTR(x) (0x054 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_BUF_ADDR(x) (0x058 + UDMA_CHX_OFF(x)) ++#define UDMA_CHX_RX_CTRL(x) (0x05c + UDMA_CHX_OFF(x)) ++#define UDMA_RX_CTRL_BUF_ADDRH GENMASK(10, 8) ++#define UDMA_RX_CTRL_TMOUT_DIS BIT(4) ++#define UDMA_RX_CTRL_BUFSZ GENMASK(1, 0) + -+ /* Get MMBI memory size */ -+ np = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (!np || of_address_to_resource(np, 0, &res)) { -+ dev_err(dev, "Failed to find memory-region.\n"); -+ ret = -ENOMEM; -+ goto out_region; -+ } ++#define UDMA_MAX_CHANNEL 16 ++#define UDMA_TMOUT 0x200 + -+ of_node_put(np); ++enum aspeed_udma_bufsz_code { ++ UDMA_BUFSZ_CODE_1KB, ++ UDMA_BUFSZ_CODE_4KB, ++ UDMA_BUFSZ_CODE_16KB, ++ UDMA_BUFSZ_CODE_64KB, ++}; + -+ mmbi->mem_phy = res.start; -+ mmbi->mem_size = resource_size(&res); -+ mmbi->mem_virt = ioremap(mmbi->mem_phy, mmbi->mem_size); -+ if (!mmbi->mem_virt) { -+ dev_err(dev, "cannot map mmbi memory region\n"); -+ ret = -ENOMEM; -+ goto out_region; -+ } ++struct aspeed_udma_chan { ++ dma_addr_t dma_addr; + -+ /* Get IRQ */ -+ mmbi->irq = platform_get_irq(pdev, 0); -+ if (mmbi->irq < 0) { -+ dev_err(&pdev->dev, "platform get of irq[=%d] failed!\n", mmbi->irq); -+ ret = mmbi->irq; -+ goto out_unmap; -+ } -+ ret = devm_request_irq(&pdev->dev, mmbi->irq, aspeed_pcie_mmbi_isr, 0, dev_name(&pdev->dev), -+ mmbi); -+ if (ret) { -+ dev_err(dev, "pcie mmbi unable to get IRQ"); -+ goto out_unmap; -+ } ++ u32 rb_sz; + -+ chan = &mmbi->chan; -+ memset(chan, 0, sizeof(struct aspeed_mmbi_channel)); ++ aspeed_udma_cb_t cb; ++ void *cb_arg; + -+ chan->bmc_int_en = true; -+ /* H2B Interrupt */ -+ ret = of_property_read_u8(dev->of_node, "bmc-int-value", &chan->bmc_int_value); -+ if (ret) { -+ dev_err(dev, "cannot get valid MMBI H2B interrupt value\n"); -+ chan->bmc_int_en = false; -+ } -+ ret = of_property_read_u32(dev->of_node, "bmc-int-location", &chan->bmc_int_location); -+ if (ret) { -+ dev_err(dev, "cannot get valid MMBI H2B interrupt location\n"); -+ chan->bmc_int_en = false; -+ } -+ /* B2H Interrupt */ -+ chan->host_int_en = true; -+ ret = of_property_read_u8(dev->of_node, "msi", &chan->host_int_value); -+ if (ret) { -+ dev_err(dev, "cannot get valid MMBI B2H interrupt location\n"); -+ chan->host_int_en = false; -+ } ++ bool dis_tmout; ++}; + -+ ret = mmbi->platform->mmbi_init(pdev); -+ if (ret) { -+ dev_err(dev, "Initialize pcie mmbi failed\n"); -+ goto out_irq; ++struct aspeed_udma { ++ struct device *dev; ++ u8 __iomem *regs; ++ int irq; ++ struct aspeed_udma_chan tx_chs[UDMA_MAX_CHANNEL]; ++ struct aspeed_udma_chan rx_chs[UDMA_MAX_CHANNEL]; ++ spinlock_t lock; ++}; ++ ++struct aspeed_udma udma[1]; ++ ++static int aspeed_udma_get_bufsz_code(u32 buf_sz) ++{ ++ switch (buf_sz) { ++ case SZ_1K: ++ return UDMA_BUFSZ_CODE_1KB; ++ case SZ_4K: ++ return UDMA_BUFSZ_CODE_4KB; ++ case SZ_16K: ++ return UDMA_BUFSZ_CODE_16KB; ++ case SZ_64K: ++ return UDMA_BUFSZ_CODE_64KB; ++ default: ++ break; + } + -+ dev_info(dev, "ASPEED PCIe MMBI Dev %d: driver successfully loaded.\n", mmbi->id); ++ return -1; ++} + -+ return 0; -+out_irq: -+ devm_free_irq(dev, mmbi->irq, mmbi); -+out_unmap: -+ iounmap(mmbi->mem_virt); -+out_region: -+ devm_kfree(dev, mmbi); -+ dev_warn(dev, "aspeed pcie mmbi: driver init failed (ret=%d)!\n", ret); -+ return ret; ++static u32 aspeed_udma_get_tx_rptr(u32 ch_no) ++{ ++ return readl(udma->regs + UDMA_CHX_TX_RD_PTR(ch_no)); +} + -+static void aspeed_pcie_mmbi_remove(struct platform_device *pdev) ++static u32 aspeed_udma_get_rx_wptr(u32 ch_no) +{ -+ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); ++ return readl(udma->regs + UDMA_CHX_RX_WR_PTR(ch_no)); ++} + -+ cancel_work_sync(&mmbi->chan.work); -+ unregister_netdev(mmbi->chan.ndev); -+ devm_free_irq(&pdev->dev, mmbi->irq, mmbi); -+ iounmap(mmbi->mem_virt); -+ devm_kfree(&pdev->dev, mmbi); ++static void aspeed_udma_set_ptr(u32 ch_no, u32 ptr, bool is_tx) ++{ ++ writel(ptr, udma->regs + ++ ((is_tx) ? UDMA_CHX_TX_WR_PTR(ch_no) : UDMA_CHX_RX_RD_PTR(ch_no))); +} + -+static struct platform_driver aspeed_pcie_mmbi_driver = { -+ .probe = aspeed_pcie_mmbi_probe, -+ .remove = aspeed_pcie_mmbi_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_pcie_mmbi_of_matches, -+ }, -+}; ++void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr) ++{ ++ aspeed_udma_set_ptr(ch_no, wptr, true); ++} ++EXPORT_SYMBOL(aspeed_udma_set_tx_wptr); + -+module_platform_driver(aspeed_pcie_mmbi_driver); ++void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr) ++{ ++ aspeed_udma_set_ptr(ch_no, rptr, false); ++} ++EXPORT_SYMBOL(aspeed_udma_set_rx_rptr); + -+MODULE_AUTHOR("Jacky Chou "); -+MODULE_DESCRIPTION("ASPEED PCI-E MMBI Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/aspeed-pcie-mmbi.h b/drivers/soc/aspeed/aspeed-pcie-mmbi.h ---- a/drivers/soc/aspeed/aspeed-pcie-mmbi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-pcie-mmbi.h 2026-04-08 18:03:48.311705302 +0000 -@@ -0,0 +1,141 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * Copyright 2024 Aspeed Technology Inc. -+ */ -+#ifndef __ASPEED_PCIE_MMBI_H__ -+#define __ASPEED_PCIE_MMBI_H__ ++static int aspeed_udma_free_chan(u32 ch_no, bool is_tx) ++{ ++ u32 reg; ++ unsigned long flags; + -+#define MMBI_SIGNATURE "#MMBI$" ++ if (ch_no > UDMA_MAX_CHANNEL) ++ return -EINVAL; + -+//This definitions are as per MMBI specification. -+#define MMBI_PROTOCOL_IPMI 1 -+#define MMBI_PROTOCOL_SEAMLESS 2 -+#define MMBI_PROTOCOL_RAS_OFFLOAD 3 -+#define MMBI_PROTOCOL_MCTP 4 -+#define MMBI_PROTOCOL_NODE_MANAGER 5 ++ spin_lock_irqsave(&udma->lock, flags); + -+#define MMBI_HRWS0(x) readl((x)->hrws_vmem) -+#define MMBI_HRWS1(x) readl((x)->hrws_vmem + 4) -+#define MMBI_HROS0(x) readl((x)->hros_vmem) -+#define MMBI_HROS1(x) readl((x)->hros_vmem + 4) ++ reg = readl(udma->regs + ++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); ++ reg &= ~(0x1 << ch_no); + -+#define H2B_WRITE_POINTER_MASK GENMASK(31, 2) -+#define H2B_READ_POINTER_MASK GENMASK(31, 2) -+#define B2H_WRITE_POINTER_MASK GENMASK(31, 2) -+#define B2H_READ_POINTER_MASK GENMASK(31, 2) ++ writel(reg, udma->regs + ++ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); + -+#define GET_H2B_WRITE_POINTER(x) (MMBI_HRWS0(x) & H2B_WRITE_POINTER_MASK) -+#define GET_H2B_READ_POINTER(x) (MMBI_HROS1(x) & H2B_READ_POINTER_MASK) -+#define GET_B2H_WRITE_POINTER(x) (MMBI_HROS0(x) & B2H_WRITE_POINTER_MASK) -+#define GET_B2H_READ_POINTER(x) (MMBI_HRWS1(x) & B2H_READ_POINTER_MASK) ++ spin_unlock_irqrestore(&udma->lock, flags); + -+#define GET_HOST_READY_BIT(x) (MMBI_HRWS1(x) & 0x01) -+#define GET_BMC_READY_BIT(x) (MMBI_HROS1(x) & 0x01) ++ return 0; ++} + -+typedef u8 protocol_type; ++int aspeed_udma_free_tx_chan(u32 ch_no) ++{ ++ return aspeed_udma_free_chan(ch_no, true); ++} ++EXPORT_SYMBOL(aspeed_udma_free_tx_chan); + -+enum mmbi_state { /* B_U B_R H_U H_R */ -+ INIT_IN_PROGRESS = 0x00, /* 0 0 0 0 */ -+ INIT_COMPLETED = 0x08, /* 1 0 0 0 */ -+ NORMAL_RUNTIME = 0x0A, /* 1 0 1 0 */ -+ RESET_REQ_BY_BMC = 0x0E, /* 1 1 1 0 */ -+ RESET_REQ_BY_HOST = 0x0B, /* 1 0 1 1 */ -+ RESET_ACKED = 0x0F, /* 1 1 1 1 */ -+ TRANS_TO_INIT = 0x07, /* 0 1 1 1 */ -+ INIT_MISMATCH = 0x09, /* 1 0 0 1 */ -+ POWER_UP_OR_ERROR = 0x80000000, -+}; ++int aspeed_udma_free_rx_chan(u32 ch_no) ++{ ++ return aspeed_udma_free_chan(ch_no, false); ++} ++EXPORT_SYMBOL(aspeed_udma_free_rx_chan); + -+struct mmbi_header { -+ u32 pkt_pad : 2; -+ u32 pkt_len : 22; -+ u32 pkt_type : 4; -+ u32 reserved : 4; -+}; -+ -+struct host_ros { -+ u32 b_rst : 1; /* BMC Reset Request */ -+ u32 b_up : 1; /* BMC Interface Up */ -+ u32 b2h_wp : 30; /* B2H Write Pointer */ -+ u32 b_rdy : 1; /* BMC Ready */ -+ u32 reserved1 : 1; -+ u32 h2b_rp : 30; /* H2B Read Pointer */ -+}; -+ -+struct host_rws { -+ u32 h_rst : 1; /* Host Reset Request */ -+ u32 h_up : 1; /* Host Interface Up */ -+ u32 h2b_wp : 30; /* H2B Write Pointer */ -+ u32 h_rdy : 1; /* Host Ready */ -+ u32 reserved1 : 1; -+ u32 b2h_rp : 30; /* B2H Read Pointer */ -+}; -+ -+struct buffer_type_desc { -+ u32 h_ros_p; /* Host Read-Only Structure Pointer */ -+ u32 h_rws_p; /* Host Read-Write Structure Pointer */ -+ u8 h_int_t; /* Host Interrupt Type */ -+ u8 h_int_l; /* Host Interrupt Location */ -+ u8 reserved1[3]; -+ u8 h_int_v; /* Host Interrupt Value */ -+ u8 bmc_int_t; /* BMC Interrupt Type */ -+ u32 bmc_int_l; /* BMC Interrupt Location */ -+ u8 reserved2[4]; -+ u8 bmc_int_v; /* BMC Interrupt Value */ -+} __packed; ++static int aspeed_udma_request_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, ++ bool dis_tmout, bool is_tx) ++{ ++ int retval = 0; ++ int rbsz_code; + -+struct mmbi_cap_desc { -+ u8 signature[6]; -+ u8 version; -+ u8 os_use; -+ u32 b2h_ba; /* B2H Buffer Base Address */ -+ u32 h2b_ba; /* H2B Buffer Base Address */ -+ u32 b2h_l; /* B2H Buffer Length */ -+ u32 h2b_l; /* H2B Buffer Length */ -+ u8 buffer_type; -+ u8 reserved1[7]; -+ struct buffer_type_desc bt_desc; -+ u8 reserved2[8]; -+} __packed; ++ u32 reg; ++ unsigned long flags; ++ struct aspeed_udma_chan *ch; + -+struct aspeed_pcie_mmbi; ++ if (ch_no > UDMA_MAX_CHANNEL) { ++ retval = -EINVAL; ++ goto out; ++ } + -+#define MCTP_MMBI_MTU 65536 -+#define MCTP_MMBI_MTU_MIN 68 /* base mtu (64) + mctp header */ -+#define MCTP_MMBI_MTU_MAX 65536 ++ rbsz_code = aspeed_udma_get_bufsz_code(rb_sz); ++ if (rbsz_code < 0) { ++ retval = -EINVAL; ++ goto out; ++ } + -+struct aspeed_mmbi_mctp { -+ struct aspeed_pcie_mmbi *mmbi; -+ struct net_device *ndev; -+}; ++ spin_lock_irqsave(&udma->lock, flags); + -+struct aspeed_mmbi_channel { -+ struct aspeed_pcie_mmbi *mmbi; -+ struct device *dev; ++ if (is_tx) { ++ reg = readl(udma->regs + UDMA_TX_DMA_INT_EN); ++ if (reg & (0x1 << ch_no)) { ++ retval = -EBUSY; ++ goto unlock_n_out; ++ } + -+ u32 b2h_cb_size; -+ u32 h2b_cb_size; -+ u8 __iomem *desc_vmem; -+ u8 __iomem *hros_vmem; -+ u8 __iomem *b2h_cb_vmem; -+ u8 __iomem *hrws_vmem; -+ u8 __iomem *h2b_cb_vmem; ++ reg |= (0x1 << ch_no); ++ writel(reg, udma->regs + UDMA_TX_DMA_INT_EN); + -+ bool bmc_int_en; -+ u8 bmc_int_value; -+ u32 bmc_int_location; -+ u8 __iomem *bmc_int_vmem; ++ reg = FIELD_PREP(UDMA_TX_CTRL_BUF_ADDRH, (u64)addr >> 32) | ++ ((dis_tmout) ? UDMA_TX_CTRL_TMOUT_DIS : 0) | ++ FIELD_PREP(UDMA_TX_CTRL_BUFSZ, rbsz_code); ++ writel(reg, udma->regs + UDMA_CHX_TX_CTRL(ch_no)); + -+ bool host_int_en; -+ u8 host_int_location; -+ u8 host_int_value; ++ writel(addr, udma->regs + UDMA_CHX_TX_BUF_ADDR(ch_no)); ++ } else { ++ reg = readl(udma->regs + UDMA_RX_DMA_INT_EN); ++ if (reg & (0x1 << ch_no)) { ++ retval = -EBUSY; ++ goto unlock_n_out; ++ } ++ ++ reg |= (0x1 << ch_no); ++ writel(reg, udma->regs + UDMA_RX_DMA_INT_EN); + -+ enum mmbi_state state; ++ reg = FIELD_PREP(UDMA_RX_CTRL_BUF_ADDRH, (u64)addr >> 32) | ++ ((dis_tmout) ? UDMA_RX_CTRL_TMOUT_DIS : 0) | ++ FIELD_PREP(UDMA_RX_CTRL_BUFSZ, rbsz_code); ++ writel(reg, udma->regs + UDMA_CHX_RX_CTRL(ch_no)); + -+ /* MCTP */ -+ struct net_device *ndev; ++ writel(addr, udma->regs + UDMA_CHX_RX_BUF_ADDR(ch_no)); ++ } + -+ struct work_struct work; -+}; ++ ch = (is_tx) ? &udma->tx_chs[ch_no] : &udma->rx_chs[ch_no]; ++ ch->rb_sz = rb_sz; ++ ch->cb = cb; ++ ch->cb_arg = id; ++ ch->dma_addr = addr; ++ ch->dis_tmout = dis_tmout; + -+#endif -diff --git a/drivers/soc/aspeed/aspeed-socinfo.c b/drivers/soc/aspeed/aspeed-socinfo.c ---- a/drivers/soc/aspeed/aspeed-socinfo.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-socinfo.c 2026-04-08 18:03:48.311705302 +0000 -@@ -27,6 +27,10 @@ - { "AST2620", 0x05010203 }, - { "AST2605", 0x05030103 }, - { "AST2625", 0x05030403 }, -+ /* AST2700 */ -+ { "AST2750", 0x06000003 }, -+ { "AST2700", 0x06000103 }, -+ { "AST2720", 0x06000203 }, - }; - - static const char *siliconid_to_name(u32 siliconid) -diff --git a/drivers/soc/aspeed/aspeed-ssp.c b/drivers/soc/aspeed/aspeed-ssp.c ---- a/drivers/soc/aspeed/aspeed-ssp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-ssp.c 2026-04-08 18:03:48.311705302 +0000 -@@ -0,0 +1,275 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+// Copyright (C) ASPEED Technology Inc. ++unlock_n_out: ++ spin_unlock_irqrestore(&udma->lock, flags); ++out: ++ return 0; ++} + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool dis_tmout) ++{ ++ return aspeed_udma_request_chan(ch_no, addr, rb_sz, cb, id, ++ dis_tmout, true); ++} ++EXPORT_SYMBOL(aspeed_udma_request_tx_chan); + -+#define SSP_FILE_NAME "ast2600_ssp.bin" -+#define AST2600_CVIC_TRIGGER 0x28 -+#define AST2600_CVIC_PENDING_STATUS 0x18 -+#define AST2600_CVIC_PENDING_CLEAR 0x1C ++int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, ++ aspeed_udma_cb_t cb, void *id, bool dis_tmout) ++{ ++ return aspeed_udma_request_chan(ch_no, addr, rb_sz, cb, id, ++ dis_tmout, false); ++} ++EXPORT_SYMBOL(aspeed_udma_request_rx_chan); + -+#define SSP_CTRL_REG 0xa00 -+#define SSP_CTRL_RESET_ASSERT BIT(1) -+#define SSP_CTRL_EN BIT(0) ++static void aspeed_udma_chan_ctrl(u32 ch_no, u32 op, bool is_tx) ++{ ++ unsigned long flags; ++ u32 reg_en, reg_rst; ++ u32 reg_en_off = (is_tx) ? UDMA_TX_DMA_EN : UDMA_RX_DMA_EN; ++ u32 reg_rst_off = (is_tx) ? UDMA_TX_DMA_RST : UDMA_TX_DMA_RST; + -+#define SSP_MEM_BASE_REG 0xa04 -+#define SSP_IMEM_LIMIT_REG 0xa08 -+#define SSP_DMEM_LIMIT_REG 0xa0c -+#define SSP_CACHE_RANGE_REG 0xa40 -+#define SSP_CACHE_INVALID_REG 0xa44 -+#define SSP_CACHE_CTRL_REG 0xa48 -+#define SSP_CACHE_CLEAR_ICACHE BIT(2) -+#define SSP_CACHE_CLEAR_DCACHE BIT(1) -+#define SSP_CACHE_EN BIT(0) ++ if (ch_no > UDMA_MAX_CHANNEL) ++ return; + -+#define SSP_TOTAL_MEM_SZ (32 * 1024 * 1024) -+#define SSP_CACHED_MEM_SZ (16 * 1024 * 1024) -+#define SSP_UNCACHED_MEM_SZ (SSP_TOTAL_MEM_SZ - SSP_CACHED_MEM_SZ) -+#define SSP_CACHE_1ST_16MB_ENABLE BIT(0) ++ spin_lock_irqsave(&udma->lock, flags); + -+struct ast2600_ssp { -+ struct device *dev; -+ struct regmap *scu; -+ dma_addr_t ssp_mem_phy_addr; -+ void __iomem *ssp_mem_vir_addr; -+ dma_addr_t ssp_shared_mem_phy_addr; -+ void __iomem *ssp_shared_mem_vir_addr; -+ int ssp_shared_mem_size; -+ void __iomem *cvic; -+ int irq[16]; -+ int n_irq; -+}; ++ reg_en = readl(udma->regs + reg_en_off); ++ reg_rst = readl(udma->regs + reg_rst_off); + -+static int ast_ssp_open(struct inode *inode, struct file *file) -+{ -+ return 0; ++ switch (op) { ++ case ASPEED_UDMA_OP_ENABLE: ++ reg_en |= (0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ break; ++ case ASPEED_UDMA_OP_DISABLE: ++ reg_en &= ~(0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ break; ++ case ASPEED_UDMA_OP_RESET: ++ reg_en &= ~(0x1 << ch_no); ++ writel(reg_en, udma->regs + reg_en_off); ++ ++ reg_rst |= (0x1 << ch_no); ++ writel(reg_rst, udma->regs + reg_rst_off); ++ ++ udelay(100); ++ ++ reg_rst &= ~(0x1 << ch_no); ++ writel(reg_rst, udma->regs + reg_rst_off); ++ break; ++ default: ++ break; ++ } ++ ++ spin_unlock_irqrestore(&udma->lock, flags); +} + -+static int ast_ssp_release(struct inode *inode, struct file *file) ++void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) +{ -+ return 0; ++ aspeed_udma_chan_ctrl(ch_no, op, true); +} ++EXPORT_SYMBOL(aspeed_udma_tx_chan_ctrl); + -+static const struct file_operations ast_ssp_fops = { -+ .owner = THIS_MODULE, -+ .open = ast_ssp_open, -+ .release = ast_ssp_release, -+ .llseek = noop_llseek, -+}; -+ -+struct miscdevice ast_ssp_misc = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "ast-ssp", -+ .fops = &ast_ssp_fops, -+}; ++void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++{ ++ aspeed_udma_chan_ctrl(ch_no, op, false); ++} ++EXPORT_SYMBOL(aspeed_udma_rx_chan_ctrl); + -+static irqreturn_t ast2600_ssp_interrupt(int irq, void *dev_id) ++static irqreturn_t aspeed_udma_isr(int irq, void *arg) +{ -+ u32 i; -+ struct ast2600_ssp *priv = dev_id; -+ u32 isr = readl(priv->cvic + AST2600_CVIC_PENDING_STATUS); -+ u32 ssp_shared_rx_tx_size = priv->ssp_shared_mem_size / 2; -+ u32 *ssp_shared_mem_tx = priv->ssp_shared_mem_vir_addr; -+ u32 *ssp_shared_mem_rx = priv->ssp_shared_mem_vir_addr + ssp_shared_rx_tx_size; ++ u32 bit; ++ unsigned long tx_sts = readl(udma->regs + UDMA_TX_DMA_INT_STS); ++ unsigned long rx_sts = readl(udma->regs + UDMA_RX_DMA_INT_STS); + -+ dev_info(priv->dev, "isr %x\n", isr); -+ writel(isr, priv->cvic + AST2600_CVIC_PENDING_CLEAR); ++ if (udma != (struct aspeed_udma *)arg) ++ return IRQ_NONE; + -+ dev_info(priv->dev, "[CA7] rx addr:%08x, tx addr:%08x\n", -+ (u32)ssp_shared_mem_rx, (u32)ssp_shared_mem_tx); ++ if (tx_sts == 0 && rx_sts == 0) ++ return IRQ_NONE; + -+ /* Check the CA7 RX data from CM3 TX data. */ -+ dev_info(priv->dev, "CA7 RX data from CM3 TX data: "); -+ for (i = 0; i < ssp_shared_rx_tx_size / 4; i++) { -+ if (readl(ssp_shared_mem_rx + i) != 0) { -+ dev_info(priv->dev, "[%08x] %08x ", -+ (u32)(ssp_shared_mem_rx + i), readl(ssp_shared_mem_rx + i)); -+ } else { -+ break; -+ } ++ for_each_set_bit(bit, &tx_sts, UDMA_MAX_CHANNEL) { ++ writel((0x1 << bit), udma->regs + UDMA_TX_DMA_INT_STS); ++ if (udma->tx_chs[bit].cb) ++ udma->tx_chs[bit].cb(aspeed_udma_get_tx_rptr(bit), ++ udma->tx_chs[bit].cb_arg); ++ } ++ ++ for_each_set_bit(bit, &rx_sts, UDMA_MAX_CHANNEL) { ++ writel((0x1 << bit), udma->regs + UDMA_RX_DMA_INT_STS); ++ if (udma->rx_chs[bit].cb) ++ udma->rx_chs[bit].cb(aspeed_udma_get_rx_wptr(bit), ++ udma->rx_chs[bit].cb_arg); + } + + return IRQ_HANDLED; +} + -+static int ast_ssp_probe(struct platform_device *pdev) ++static int aspeed_udma_probe(struct platform_device *pdev) +{ -+ struct device_node *np, *mnode = dev_of_node(&pdev->dev); -+ const struct firmware *firmware; -+ struct ast2600_ssp *priv; -+ struct reserved_mem *rmem; -+ int i, ret; ++ int i, rc; ++ uint32_t reg; ++ struct resource *res; ++ struct device *dev = &pdev->dev; + -+ priv = kzalloc(sizeof(*priv), GFP_KERNEL); -+ if (!priv) { -+ ret = -ENOMEM; -+ goto finish; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (IS_ERR_OR_NULL(res)) { ++ dev_err(dev, "failed to get register base\n"); ++ return -ENODEV; + } + -+ priv->dev = &pdev->dev; -+ priv->scu = syscon_regmap_lookup_by_phandle(priv->dev->of_node, "aspeed,scu"); -+ if (IS_ERR(priv->scu)) { -+ dev_err(priv->dev, "failed to find SCU regmap\n"); -+ ret = -EINVAL; -+ goto finish; ++ udma->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR_OR_NULL(udma->regs)) { ++ dev_err(dev, "failed to map registers\n"); ++ return PTR_ERR(udma->regs); + } -+ platform_set_drvdata(pdev, priv); + -+ ret = misc_register(&ast_ssp_misc); -+ if (ret) { -+ pr_err("can't misc_register :(\n"); -+ ret = -EIO; -+ goto finish; -+ } -+ dev_set_drvdata(ast_ssp_misc.this_device, pdev); ++ /* disable for safety */ ++ writel(0x0, udma->regs + UDMA_TX_DMA_EN); ++ writel(0x0, udma->regs + UDMA_RX_DMA_EN); + -+ ret = of_reserved_mem_device_init(&pdev->dev); -+ if (ret) { -+ dev_err(priv->dev, -+ "failed to initialize reserved mem: %d\n", ret); -+ ret = -ENOMEM; -+ goto finish; ++ udma->irq = platform_get_irq(pdev, 0); ++ if (udma->irq < 0) { ++ dev_err(dev, "failed to get IRQ number\n"); ++ return -ENODEV; + } + -+ np = of_parse_phandle(priv->dev->of_node, "memory-region", 0); -+ if (!np) { -+ dev_err(priv->dev, "can't find memory-region node\n"); -+ ret = -ENOMEM; -+ goto finish; ++ rc = devm_request_irq(dev, udma->irq, aspeed_udma_isr, ++ IRQF_SHARED, DEVICE_NAME, udma); ++ if (rc) { ++ dev_err(dev, "failed to request IRQ handler\n"); ++ return rc; + } + -+ rmem = of_reserved_mem_lookup(np); -+ of_node_put(np); -+ if (!rmem) { -+ dev_err(priv->dev, "can't find reserved memory.\n"); -+ ret = -ENOMEM; -+ goto finish; -+ } else { -+ priv->ssp_mem_phy_addr = rmem->base; -+ priv->ssp_mem_vir_addr = devm_ioremap(priv->dev, priv->ssp_mem_phy_addr, SSP_TOTAL_MEM_SZ); -+ if (!priv->ssp_mem_vir_addr) { -+ dev_err(priv->dev, "can't create reserved memory.\n"); -+ ret = -ENOMEM; -+ goto finish; -+ } else { -+ dev_info(priv->dev, "\nSSP memory: virt(0x%08x), phys(0x%08x)\n", -+ (uint32_t)priv->ssp_mem_vir_addr, priv->ssp_mem_phy_addr); -+ } -+ } ++ /* ++ * For legacy design. ++ * - TX ringbuffer size: 4KB ++ * - RX ringbuffer size: 64KB ++ * - Timeout timer disabled ++ */ ++ reg = FIELD_PREP(UDMA_MISC_TX_BUFSZ, UDMA_BUFSZ_CODE_4KB) | ++ FIELD_PREP(UDMA_MISC_RX_BUFSZ, UDMA_BUFSZ_CODE_64KB); ++ writel(reg, udma->regs + UDMA_MISC); + -+ if (of_property_read_u32(np, "shm-size", &priv->ssp_shared_mem_size)) { -+ dev_err(priv->dev, "can't find shm-size property\n"); -+ ret = -ENOMEM; -+ goto finish; ++ for (i = 0; i < UDMA_MAX_CHANNEL; ++i) { ++ writel(0, udma->regs + UDMA_CHX_TX_WR_PTR(i)); ++ writel(0, udma->regs + UDMA_CHX_RX_RD_PTR(i)); + } + -+ priv->ssp_shared_mem_vir_addr = priv->ssp_mem_vir_addr + SSP_TOTAL_MEM_SZ -+ - priv->ssp_shared_mem_size; -+ priv->ssp_shared_mem_phy_addr = priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ -+ - priv->ssp_shared_mem_size; -+ dev_info(priv->dev, "\nSSP shared memory: virt(0x%08x), phys(0x%08x), size(0x%08x)\n", -+ (uint32_t)priv->ssp_shared_mem_vir_addr, priv->ssp_shared_mem_phy_addr, -+ priv->ssp_shared_mem_size); ++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_RST); ++ writel(0x0, udma->regs + UDMA_TX_DMA_RST); + -+ if (request_firmware(&firmware, SSP_FILE_NAME, priv->dev) < 0) { -+ dev_err(priv->dev, "don't have %s\n", SSP_FILE_NAME); -+ release_firmware(firmware); -+ ret = -EINVAL; -+ goto finish; ++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_RST); ++ writel(0x0, udma->regs + UDMA_RX_DMA_RST); ++ ++ writel(0x0, udma->regs + UDMA_TX_DMA_INT_EN); ++ writel(0xffffffff, udma->regs + UDMA_TX_DMA_INT_STS); ++ writel(0x0, udma->regs + UDMA_RX_DMA_INT_EN); ++ writel(0xffffffff, udma->regs + UDMA_RX_DMA_INT_STS); ++ ++ writel(UDMA_TMOUT, udma->regs + UDMA_TMOUT_TIMER); ++ ++ spin_lock_init(&udma->lock); ++ ++ dev_set_drvdata(dev, udma); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_udma_match[] = { ++ { .compatible = "aspeed,ast2500-udma" }, ++ { .compatible = "aspeed,ast2600-udma" }, ++ { .compatible = "aspeed,ast2700-udma" }, ++ { }, ++}; ++ ++static struct platform_driver aspeed_udma_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_udma_match, ++ ++ }, ++ .probe = aspeed_udma_probe, ++}; ++ ++module_platform_driver(aspeed_udma_driver); ++ ++MODULE_AUTHOR("Chia-Wei Wang "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Aspeed UDMA Engine Driver"); +diff --git a/drivers/soc/aspeed/aspeed-usb-hp.c b/drivers/soc/aspeed/aspeed-usb-hp.c +--- a/drivers/soc/aspeed/aspeed-usb-hp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-usb-hp.c 2026-07-17 08:44:55.290306551 +0000 +@@ -0,0 +1,157 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2021 Aspeed Technology Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define USB_HP_BEHCI84 0x84 /* Controller Fine-tune Register */ ++#define USB_HP_BEHCI88 0x88 /* Frame Timing Adjustment */ ++ ++#define BEHCI_64BIT BIT(11) /* Enable support 64 bit address mode */ ++#define BEHCI_PRE_EOF1(x) ((x) << 12) ++#define BEHCI_PRE_EOF2(x) ((x) << 22) ++/* ++ * Set preEOF1 to 0x100 and preEOF2 to 0x140 ++ * (i.e. preEOF1 + MPS 0x40) as the workaround. ++ */ ++#define BEHCI_EOF1_EOF2_TIMING \ ++ (BEHCI_PRE_EOF1(0x100) | BEHCI_PRE_EOF2(0x140)) ++ ++static const struct of_device_id aspeed_usb_hp_dt_ids[] = { ++ { ++ .compatible = "aspeed,ast2600-usb2ahp", ++ }, ++ { ++ .compatible = "aspeed,ast2700-usb3hp", ++ }, ++ { ++ .compatible = "aspeed,ast2700-usb2hp", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, aspeed_usb_hp_dt_ids); ++ ++static int aspeed_usb_hp_probe(struct platform_device *pdev) ++{ ++ void __iomem *regs; ++ u32 val; ++ struct clk *clk; ++ struct reset_control *rst; ++ struct regmap *device; ++ struct phy *usb3_phy; ++ bool is_pcie_xhci; ++ int rc = 0; ++ ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2600-usb2ahp")) { ++ dev_info(&pdev->dev, "Initialized AST2600 USB2AHP\n"); ++ return 0; + } + -+ memcpy(priv->ssp_mem_vir_addr, (void *)firmware->data, firmware->size); -+ release_firmware(firmware); ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-usb3hp")) ++ is_pcie_xhci = true; ++ else if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-usb2hp")) ++ is_pcie_xhci = false; + -+ np = of_parse_phandle(mnode, "aspeed,cvic", 0); -+ if (!np) { -+ dev_err(priv->dev, "can't find CVIC\n"); -+ ret = -EINVAL; -+ goto finish; ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ rc = clk_prepare_enable(clk); ++ if (rc) { ++ dev_err(&pdev->dev, "Unable to enable clock (%d)\n", rc); ++ return rc; + } + -+ priv->cvic = devm_of_iomap(priv->dev, np, 0, NULL); -+ if (IS_ERR(priv->cvic)) { -+ dev_err(priv->dev, "can't map CVIC\n"); -+ ret = -EINVAL; -+ goto finish; ++ rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); ++ if (IS_ERR(rst)) { ++ rc = PTR_ERR(rst); ++ goto err; + } ++ rc = reset_control_deassert(rst); ++ if (rc) ++ goto err; + -+ i = 0; -+ while (0 != (priv->irq[i] = irq_of_parse_and_map(mnode, i))) { -+ ret = request_irq(priv->irq[i], ast2600_ssp_interrupt, 0, -+ "ssp-sw-irq", priv); -+ i++; ++ device = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,device"); ++ if (IS_ERR(device)) { ++ dev_err(&pdev->dev, "failed to find device regmap\n"); ++ goto err; + } -+ priv->n_irq = i; -+ dev_info(priv->dev, "%d ISRs registered\n", priv->n_irq); + -+ regmap_write(priv->scu, SSP_CTRL_REG, 0); -+ mdelay(1); -+ regmap_write(priv->scu, SSP_MEM_BASE_REG, priv->ssp_mem_phy_addr); -+ regmap_write(priv->scu, SSP_IMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_CACHED_MEM_SZ); -+ regmap_write(priv->scu, SSP_DMEM_LIMIT_REG, priv->ssp_mem_phy_addr + SSP_TOTAL_MEM_SZ); ++ if (is_pcie_xhci) { ++ usb3_phy = devm_phy_get(&pdev->dev, "usb3-phy"); ++ if (IS_ERR(usb3_phy)) { ++ rc = dev_err_probe(&pdev->dev, PTR_ERR(usb3_phy), ++ "failed to get usb3 phy\n"); ++ goto err; ++ } ++ rc = phy_init(usb3_phy); ++ if (rc < 0) { ++ dev_err(&pdev->dev, "failed to init usb3 phy\n"); ++ goto err; ++ } ++ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev ++ /* Turn on PCIe xHCI without MSI */ ++ regmap_update_bits(device, 0x70, ++ BIT(19) | BIT(11) | BIT(3), ++ BIT(19) | BIT(11) | BIT(3)); ++ } else { ++ regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(regs)) { ++ dev_err(&pdev->dev, "Failed to map resources\n"); ++ rc = PTR_ERR(regs); ++ goto err; ++ } + -+ regmap_write(priv->scu, SSP_CACHE_RANGE_REG, SSP_CACHE_1ST_16MB_ENABLE); ++ if (device_property_read_bool(&pdev->dev, "aspeed,ehci_32bits_cap")) { ++ val = readl(regs + USB_HP_BEHCI84) & ~BEHCI_64BIT; ++ writel(val, regs + USB_HP_BEHCI84); ++ } + -+ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_RESET_ASSERT); -+ mdelay(1); -+ regmap_write(priv->scu, SSP_CTRL_REG, 0); -+ mdelay(1); -+ regmap_write(priv->scu, SSP_CTRL_REG, SSP_CTRL_EN); ++ /* Adjust PCIe-EHCI EOF1/EOF2 timing to workaround the DMA termination bug. */ ++ writel(BEHCI_EOF1_EOF2_TIMING, regs + USB_HP_BEHCI88); + -+ dev_info(priv->dev, "Init successful\n"); -+ ret = 0; -+finish: -+ return ret; ++ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev ++ /* Turn on PCIe EHCI without MSI */ ++ regmap_update_bits(device, 0x70, ++ BIT(18) | BIT(10) | BIT(2), ++ BIT(18) | BIT(10) | BIT(2)); ++ } ++ dev_info(&pdev->dev, "Initialized AST2700 USB Host PCIe %s\n", is_pcie_xhci ? "xHCI" : "EHCI"); ++ return 0; ++err: ++ if (clk) ++ clk_disable_unprepare(clk); ++ return rc; +} + -+static void ast_ssp_remove(struct platform_device *pdev) ++static void aspeed_usb_hp_remove(struct platform_device *pdev) +{ -+ struct ast2600_ssp *priv = platform_get_drvdata(pdev); -+ int i; -+ -+ dev_info(priv->dev, "SSP module removed\n"); -+ regmap_write(priv->scu, SSP_CTRL_REG, 0); -+ for (i = 0; i < priv->n_irq; i++) -+ free_irq(priv->irq[i], priv); -+ -+ kfree(priv); -+ -+ misc_deregister((struct miscdevice *)&ast_ssp_misc); ++ dev_info(&pdev->dev, "Remove USB Host PCIe\n"); +} + -+static const struct of_device_id of_ast_ssp_match_table[] = { -+ { .compatible = "aspeed,ast2600-ssp", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, of_ast_ssp_match_table); -+ -+static struct platform_driver ast_ssp_driver = { -+ .probe = ast_ssp_probe, -+ .remove = ast_ssp_remove, ++static struct platform_driver aspeed_usb_hp_driver = { ++ .probe = aspeed_usb_hp_probe, ++ .remove = aspeed_usb_hp_remove, + .driver = { + .name = KBUILD_MODNAME, -+ .of_match_table = of_ast_ssp_match_table, ++ .of_match_table = aspeed_usb_hp_dt_ids, + }, +}; ++module_platform_driver(aspeed_usb_hp_driver); + -+module_platform_driver(ast_ssp_driver); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Neal Liu "); +diff --git a/drivers/soc/aspeed/aspeed-usb-phy.c b/drivers/soc/aspeed/aspeed-usb-phy.c +--- a/drivers/soc/aspeed/aspeed-usb-phy.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-usb-phy.c 2026-07-17 08:44:55.290306551 +0000 +@@ -0,0 +1,112 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2021 Aspeed Technology Inc. ++ */ + -+MODULE_LICENSE("Dual BSD/GPL"); -diff --git a/drivers/soc/aspeed/aspeed-uart-routing.c b/drivers/soc/aspeed/aspeed-uart-routing.c ---- a/drivers/soc/aspeed/aspeed-uart-routing.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-uart-routing.c 2026-04-08 18:03:48.311705302 +0000 -@@ -15,20 +15,30 @@ - #define HICRA 0x9c - - /* attributes options */ -+#define UART_ROUTING_IO0 "io0" - #define UART_ROUTING_IO1 "io1" - #define UART_ROUTING_IO2 "io2" - #define UART_ROUTING_IO3 "io3" - #define UART_ROUTING_IO4 "io4" - #define UART_ROUTING_IO5 "io5" - #define UART_ROUTING_IO6 "io6" -+#define UART_ROUTING_IO7 "io7" -+#define UART_ROUTING_IO8 "io8" -+#define UART_ROUTING_IO9 "io9" - #define UART_ROUTING_IO10 "io10" -+#define UART_ROUTING_IO12 "io12" -+#define UART_ROUTING_UART0 "uart0" - #define UART_ROUTING_UART1 "uart1" - #define UART_ROUTING_UART2 "uart2" - #define UART_ROUTING_UART3 "uart3" - #define UART_ROUTING_UART4 "uart4" - #define UART_ROUTING_UART5 "uart5" - #define UART_ROUTING_UART6 "uart6" -+#define UART_ROUTING_UART7 "uart7" -+#define UART_ROUTING_UART8 "uart8" -+#define UART_ROUTING_UART9 "uart9" - #define UART_ROUTING_UART10 "uart10" -+#define UART_ROUTING_UART12 "uart12" - #define UART_ROUTING_RES "reserved" - - struct aspeed_uart_routing { -@@ -488,6 +498,416 @@ - .attrs = ast2600_uart_routing_attrs, - }; - -+/* routing selector for AST27xx node 0 */ -+static struct aspeed_uart_routing_selector ast2700n0_uart9_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART9), -+ .reg = HICR9, -+ .shift = 12, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_IO9, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART12, -+ NULL, -+ }, -+}; ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+static struct aspeed_uart_routing_selector ast2700n0_io9_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO9), -+ .reg = HICR9, -+ .shift = 8, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART12, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART9, -+ NULL, -+ }, ++struct usb_phy_ctrl { ++ u32 offset; ++ u32 value; +}; + -+static struct aspeed_uart_routing_selector ast2700n0_uart3_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART3), -+ .reg = HICRA, -+ .shift = 25, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_IO9, -+ NULL, ++static const struct of_device_id aspeed_usb_phy_dt_ids[] = { ++ { ++ .compatible = "aspeed,ast2600-uphyb", ++ }, ++ { ++ .compatible = "aspeed,ast2700-uphy2a", + }, ++ { ++ .compatible = "aspeed,ast2700-uphy2b", ++ }, ++ { } +}; ++MODULE_DEVICE_TABLE(of, aspeed_usb_phy_dt_ids); + -+static struct aspeed_uart_routing_selector ast2700n0_uart2_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART2), -+ .reg = HICRA, -+ .shift = 22, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++static int aspeed_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct usb_phy_ctrl *ctrl_data; ++ void __iomem *base; ++ struct regmap *scu; ++ int ctrl_num = 1; ++ int ret, i; ++ u32 val; + -+static struct aspeed_uart_routing_selector ast2700n0_uart1_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART1), -+ .reg = HICRA, -+ .shift = 19, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO0, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART0, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ scu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(&pdev->dev, "cannot to find SCU regmap\n"); ++ return -ENODEV; ++ } + -+static struct aspeed_uart_routing_selector ast2700n0_uart0_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART0), -+ .reg = HICRA, -+ .shift = 16, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2600-uphyb")) { ++ /* Check SCU040[3] USB port B controller reset is deassert */ ++ regmap_read(scu, 0x40, &val); ++ if ((val & BIT(3))) ++ return -EPROBE_DEFER; ++ } + -+static struct aspeed_uart_routing_selector ast2700n0_io3_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO3), -+ .reg = HICRA, -+ .shift = 9, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-uphy2a")) { ++ /* Check SCU220[0] USB vHubA1 controller reset is deassert */ ++ regmap_read(scu, 0x220, &val); ++ if ((val & BIT(0))) ++ return -EPROBE_DEFER; ++ } + -+static struct aspeed_uart_routing_selector ast2700n0_io2_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO2), -+ .reg = HICRA, -+ .shift = 6, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_IO0, -+ UART_ROUTING_IO1, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ if (of_device_is_compatible(pdev->dev.of_node, ++ "aspeed,ast2700-uphy2b")) { ++ /* Check SCU220[3] USB vHubB1 controller reset is deassert */ ++ regmap_read(scu, 0x220, &val); ++ if ((val & BIT(3))) ++ return -EPROBE_DEFER; ++ } + -+static struct aspeed_uart_routing_selector ast2700n0_io1_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO1), -+ .reg = HICRA, -+ .shift = 3, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_UART0, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ ctrl_data = devm_kzalloc(&pdev->dev, ++ sizeof(struct usb_phy_ctrl) * ctrl_num, ++ GFP_KERNEL); ++ if (!ctrl_data) ++ return -ENOMEM; + -+static struct aspeed_uart_routing_selector ast2700n0_io0_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO0), -+ .reg = HICRA, -+ .shift = 0, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART0, -+ UART_ROUTING_UART1, -+ UART_ROUTING_UART2, -+ UART_ROUTING_UART3, -+ UART_ROUTING_UART9, -+ UART_ROUTING_IO2, -+ UART_ROUTING_IO3, -+ UART_ROUTING_IO9, -+ NULL, -+ }, -+}; ++ base = of_iomap(node, 0); + -+static struct attribute *ast2700n0_uart_routing_attrs[] = { -+ &ast2700n0_uart9_sel.dev_attr.attr, -+ &ast2700n0_io9_sel.dev_attr.attr, -+ &ast2700n0_uart3_sel.dev_attr.attr, -+ &ast2700n0_uart2_sel.dev_attr.attr, -+ &ast2700n0_uart1_sel.dev_attr.attr, -+ &ast2700n0_uart0_sel.dev_attr.attr, -+ &ast2700n0_io3_sel.dev_attr.attr, -+ &ast2700n0_io2_sel.dev_attr.attr, -+ &ast2700n0_io1_sel.dev_attr.attr, -+ &ast2700n0_io0_sel.dev_attr.attr, -+ NULL, -+}; ++ ret = of_property_read_u32_array(node, "ctrl", (u32 *)ctrl_data, ++ ctrl_num * 2); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Could not read ctrl property\n"); ++ return -EINVAL; ++ } + -+static const struct attribute_group ast2700n0_uart_routing_attr_group = { -+ .attrs = ast2700n0_uart_routing_attrs, -+}; ++ for (i = 0; i < ctrl_num; i++) ++ writel(ctrl_data[i].value, base + ctrl_data[i].offset); + -+/* routing selector for AST27xx node 1 */ -+static struct aspeed_uart_routing_selector ast2700n1_uart10_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART10), -+ .reg = HICR9, -+ .shift = 12, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_IO10, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART12, -+ NULL, -+ }, -+}; ++ dev_info(&pdev->dev, "Initialized USB PHY\n"); + -+static struct aspeed_uart_routing_selector ast2700n1_io10_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO10), -+ .reg = HICR9, -+ .shift = 8, -+ .mask = 0xf, -+ .options = { -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART12, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_RES, -+ UART_ROUTING_UART10, -+ NULL, -+ }, -+}; ++ return 0; ++} + -+static struct aspeed_uart_routing_selector ast2700n1_uart8_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART8), -+ .reg = HICRA, -+ .shift = 25, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_IO10, -+ NULL, ++static void aspeed_usb_phy_remove(struct platform_device *pdev) ++{ ++} ++ ++static struct platform_driver aspeed_usb_phy_driver = { ++ .probe = aspeed_usb_phy_probe, ++ .remove = aspeed_usb_phy_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_usb_phy_dt_ids, + }, +}; ++module_platform_driver(aspeed_usb_phy_driver); + -+static struct aspeed_uart_routing_selector ast2700n1_uart7_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART7), -+ .reg = HICRA, -+ .shift = 22, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_IO10, -+ NULL, -+ }, ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Neal Liu "); +diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c +--- a/drivers/soc/aspeed/aspeed-xdma.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/aspeed-xdma.c 2026-07-17 08:44:55.290306551 +0000 +@@ -0,0 +1,1379 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright IBM Corp 2019 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DEVICE_NAME "aspeed-xdma" ++ ++#define SCU_AST2600_MISC_CTRL 0x0c0 ++#define SCU_AST2600_MISC_CTRL_XDMA_BMC BIT(8) ++#define SCU_AST2700_MISC_CTRL_XDMA_CLIENT BIT(4) ++ ++#define SCU_AST2600_DEBUG_CTRL 0x0c8 ++#define DEBUG_CTRL_AST2600_XDMA_DISABLE BIT(2) ++#define DEBUG_CTRL_AST2700_XDMA_DISABLE BIT(8) ++ ++#define SCU_AST2500_PCIE_CONF 0x180 ++#define SCU_AST2600_PCIE_CONF 0xc20 ++#define SCU_AST2700_PCIE0_CONF 0x970 ++#define SCU_AST2700_PCIE1_CONF 0x9B0 ++#define SCU_PCIE_CONF_VGA_EN BIT(0) ++#define SCU_PCIE_CONF_VGA_EN_MMIO BIT(1) ++#define SCU_PCIE_CONF_VGA_EN_LPC BIT(2) ++#define SCU_PCIE_CONF_VGA_EN_MSI BIT(3) ++#define SCU_PCIE_CONF_VGA_EN_MCTP BIT(4) ++#define SCU_PCIE_CONF_VGA_EN_IRQ BIT(5) ++#define SCU_PCIE_CONF_VGA_EN_DMA BIT(6) ++#define SCU_PCIE_CONF_BMC_EN BIT(8) ++#define SCU_PCIE_CONF_BMC_EN_MMIO BIT(9) ++#define SCU_PCIE_CONF_BMC_EN_MSI BIT(11) ++#define SCU_PCIE_CONF_BMC_EN_MCTP BIT(12) ++#define SCU_PCIE_CONF_BMC_EN_IRQ BIT(13) ++#define SCU_PCIE_CONF_BMC_EN_DMA BIT(14) ++ ++#define SCU_AST2700_PCIE0_CTRL 0xa60 ++#define SCU_AST2700_PCIE1_CTRL 0xae0 ++#define SCU_AST2700_PCIE_CTRL_DMA_EN BIT(2) ++ ++#define SCU_AST2500_BMC_CLASS_REV 0x19c ++#define SCU_AST2600_BMC_CLASS_REV 0xc68 ++#define SCU_AST2700_PCIE0_BMC_CLASS_REV 0xa18 ++#define SCU_AST2700_PCIE1_BMC_CLASS_REV 0xa98 ++#define SCU_BMC_CLASS_REV_XDMA 0xff000001 ++#define SCU_BMC_CLASS_REV_MASK 0xffffff00 ++ ++#define XDMA_CMDQ_SIZE PAGE_SIZE ++#define XDMA_NUM_CMDS \ ++ (XDMA_CMDQ_SIZE / sizeof(struct aspeed_xdma_cmd)) ++ ++/* Aspeed specification requires 100us after disabling the reset */ ++#define XDMA_ENGINE_SETUP_TIME_MAX_US 1000 ++#define XDMA_ENGINE_SETUP_TIME_MIN_US 100 ++ ++#define XDMA_CMD_AST2500_PITCH_SHIFT 3 ++#define XDMA_CMD_AST2500_PITCH_BMC GENMASK_ULL(62, 51) ++#define XDMA_CMD_AST2500_PITCH_HOST GENMASK_ULL(46, 35) ++#define XDMA_CMD_AST2500_PITCH_UPSTREAM BIT_ULL(31) ++#define XDMA_CMD_AST2500_PITCH_ADDR GENMASK_ULL(29, 4) ++#define XDMA_CMD_AST2500_PITCH_ID BIT_ULL(0) ++#define XDMA_CMD_AST2500_CMD_IRQ_EN BIT_ULL(31) ++#define XDMA_CMD_AST2500_CMD_LINE_NO GENMASK_ULL(27, 16) ++#define XDMA_CMD_AST2500_CMD_IRQ_BMC BIT_ULL(15) ++#define XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT 4 ++#define XDMA_CMD_AST2500_CMD_LINE_SIZE \ ++ GENMASK_ULL(14, XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT) ++#define XDMA_CMD_AST2500_CMD_ID BIT_ULL(1) ++ ++#define XDMA_CMD_AST2600_PITCH_BMC GENMASK_ULL(62, 48) ++#define XDMA_CMD_AST2600_PITCH_HOST GENMASK_ULL(46, 32) ++#define XDMA_CMD_AST2600_PITCH_ADDR GENMASK_ULL(30, 0) ++#define XDMA_CMD_AST2600_CMD_64_EN BIT_ULL(40) ++#define XDMA_CMD_AST2600_CMD_IRQ_BMC BIT_ULL(37) ++#define XDMA_CMD_AST2600_CMD_IRQ_HOST BIT_ULL(36) ++#define XDMA_CMD_AST2600_CMD_UPSTREAM BIT_ULL(32) ++#define XDMA_CMD_AST2600_CMD_LINE_NO GENMASK_ULL(27, 16) ++#define XDMA_CMD_AST2600_CMD_LINE_SIZE GENMASK_ULL(14, 0) ++#define XDMA_CMD_AST2600_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) ++ ++#define XDMA_CMD_AST2700_PITCH_BMC GENMASK_ULL(62, 48) ++#define XDMA_CMD_AST2700_PITCH_HOST GENMASK_ULL(46, 32) ++#define XDMA_CMD_AST2700_CMD_64_EN BIT_ULL(40) ++#define XDMA_CMD_AST2700_CMD_IRQ_BMC BIT_ULL(37) ++#define XDMA_CMD_AST2700_CMD_UPSTREAM BIT_ULL(32) ++#define XDMA_CMD_AST2700_CMD_LINE_NO GENMASK_ULL(27, 16) ++#define XDMA_CMD_AST2700_CMD_LINE_SIZE GENMASK_ULL(14, 0) ++#define XDMA_CMD_AST2700_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) ++#define XDMA_CMD_AST2700_BMC_ADDR GENMASK_ULL(34, 0) ++ ++#define XDMA_AST2500_QUEUE_ENTRY_SIZE 4 ++#define XDMA_AST2500_HOST_CMDQ_ADDR0 0x00 ++#define XDMA_AST2500_HOST_CMDQ_ENDP 0x04 ++#define XDMA_AST2500_HOST_CMDQ_WRITEP 0x08 ++#define XDMA_AST2500_HOST_CMDQ_READP 0x0c ++#define XDMA_AST2500_BMC_CMDQ_ADDR 0x10 ++#define XDMA_AST2500_BMC_CMDQ_ENDP 0x14 ++#define XDMA_AST2500_BMC_CMDQ_WRITEP 0x18 ++#define XDMA_AST2500_BMC_CMDQ_READP 0x1c ++#define XDMA_BMC_CMDQ_READP_RESET 0xee882266 ++#define XDMA_AST2500_CTRL 0x20 ++#define XDMA_AST2500_CTRL_US_COMP BIT(4) ++#define XDMA_AST2500_CTRL_DS_COMP BIT(5) ++#define XDMA_AST2500_CTRL_DS_DIRTY BIT(6) ++#define XDMA_AST2500_CTRL_DS_SIZE_256 BIT(17) ++#define XDMA_AST2500_CTRL_DS_TIMEOUT BIT(28) ++#define XDMA_AST2500_CTRL_DS_CHECK_ID BIT(29) ++#define XDMA_AST2500_STATUS 0x24 ++#define XDMA_AST2500_STATUS_US_COMP BIT(4) ++#define XDMA_AST2500_STATUS_DS_COMP BIT(5) ++#define XDMA_AST2500_STATUS_DS_DIRTY BIT(6) ++#define XDMA_AST2500_INPRG_DS_CMD1 0x38 ++#define XDMA_AST2500_INPRG_DS_CMD2 0x3c ++#define XDMA_AST2500_INPRG_US_CMD00 0x40 ++#define XDMA_AST2500_INPRG_US_CMD01 0x44 ++#define XDMA_AST2500_INPRG_US_CMD10 0x48 ++#define XDMA_AST2500_INPRG_US_CMD11 0x4c ++#define XDMA_AST2500_INPRG_US_CMD20 0x50 ++#define XDMA_AST2500_INPRG_US_CMD21 0x54 ++#define XDMA_AST2500_HOST_CMDQ_ADDR1 0x60 ++#define XDMA_AST2500_VGA_CMDQ_ADDR0 0x64 ++#define XDMA_AST2500_VGA_CMDQ_ENDP 0x68 ++#define XDMA_AST2500_VGA_CMDQ_WRITEP 0x6c ++#define XDMA_AST2500_VGA_CMDQ_READP 0x70 ++#define XDMA_AST2500_VGA_CMD_STATUS 0x74 ++#define XDMA_AST2500_VGA_CMDQ_ADDR1 0x78 ++ ++#define XDMA_AST2600_QUEUE_ENTRY_SIZE 2 ++#define XDMA_AST2600_HOST_CMDQ_ADDR0 0x00 ++#define XDMA_AST2600_HOST_CMDQ_ADDR1 0x04 ++#define XDMA_AST2600_HOST_CMDQ_ENDP 0x08 ++#define XDMA_AST2600_HOST_CMDQ_WRITEP 0x0c ++#define XDMA_AST2600_HOST_CMDQ_READP 0x10 ++#define XDMA_AST2600_BMC_CMDQ_ADDR 0x14 ++#define XDMA_AST2600_BMC_CMDQ_ENDP 0x18 ++#define XDMA_AST2600_BMC_CMDQ_WRITEP 0x1c ++#define XDMA_AST2600_BMC_CMDQ_READP 0x20 ++#define XDMA_AST2600_VGA_CMDQ_ADDR0 0x24 ++#define XDMA_AST2600_VGA_CMDQ_ADDR1 0x28 ++#define XDMA_AST2600_VGA_CMDQ_ENDP 0x2c ++#define XDMA_AST2600_VGA_CMDQ_WRITEP 0x30 ++#define XDMA_AST2600_VGA_CMDQ_READP 0x34 ++#define XDMA_AST2600_CTRL 0x38 ++#define XDMA_AST2600_CTRL_US_COMP BIT(16) ++#define XDMA_AST2600_CTRL_DS_COMP BIT(17) ++#define XDMA_AST2600_CTRL_DS_DIRTY BIT(18) ++#define XDMA_AST2600_CTRL_DS_SIZE_256 BIT(20) ++#define XDMA_AST2600_STATUS 0x3c ++#define XDMA_AST2600_STATUS_US_COMP BIT(16) ++#define XDMA_AST2600_STATUS_DS_COMP BIT(17) ++#define XDMA_AST2600_STATUS_DS_DIRTY BIT(18) ++#define XDMA_AST2600_INPRG_DS_CMD00 0x40 ++#define XDMA_AST2600_INPRG_DS_CMD01 0x44 ++#define XDMA_AST2600_INPRG_DS_CMD10 0x48 ++#define XDMA_AST2600_INPRG_DS_CMD11 0x4c ++#define XDMA_AST2600_INPRG_DS_CMD20 0x50 ++#define XDMA_AST2600_INPRG_DS_CMD21 0x54 ++#define XDMA_AST2600_INPRG_US_CMD00 0x60 ++#define XDMA_AST2600_INPRG_US_CMD01 0x64 ++#define XDMA_AST2600_INPRG_US_CMD10 0x68 ++#define XDMA_AST2600_INPRG_US_CMD11 0x6c ++#define XDMA_AST2600_INPRG_US_CMD20 0x70 ++#define XDMA_AST2600_INPRG_US_CMD21 0x74 ++ ++#define XDMA_AST2700_QUEUE_ENTRY_SIZE 2 ++#define XDMA_AST2700_BMC_CMDQ_ADDR0 0x10 ++#define XDMA_AST2700_BMC_CMDQ_ADDR1 0x14 ++#define XDMA_AST2700_BMC_CMDQ_ENDP 0x18 ++#define XDMA_AST2700_BMC_CMDQ_WRITEP 0x1c ++#define XDMA_AST2700_BMC_CMDQ_READP 0x20 ++#define XDMA_AST2700_CTRL 0x38 ++#define XDMA_AST2700_CTRL_US_COMP BIT(16) ++#define XDMA_AST2700_CTRL_DS_COMP BIT(17) ++#define XDMA_AST2700_CTRL_DS_DIRTY BIT(18) ++#define XDMA_AST2700_CTRL_IDLE BIT(19) ++#define XDMA_AST2700_CTRL_DS_SIZE_256 BIT(20) ++#define XDMA_AST2700_STATUS 0x3c ++#define XDMA_AST2700_STATUS_US_COMP BIT(16) ++#define XDMA_AST2700_STATUS_DS_COMP BIT(17) ++#define XDMA_AST2700_STATUS_DS_DIRTY BIT(18) ++#define XDMA_AST2700_STATUS_IDLE BIT(19) ++#define XDMA_AST2700_INPRG_DS_CMD00 0x40 ++#define XDMA_AST2700_INPRG_DS_CMD01 0x44 ++#define XDMA_AST2700_INPRG_DS_CMD10 0x48 ++#define XDMA_AST2700_INPRG_DS_CMD11 0x4c ++#define XDMA_AST2700_INPRG_DS_CMD20 0x50 ++#define XDMA_AST2700_INPRG_DS_CMD21 0x54 ++#define XDMA_AST2700_INPRG_US_CMD00 0x60 ++#define XDMA_AST2700_INPRG_US_CMD01 0x64 ++#define XDMA_AST2700_INPRG_US_CMD10 0x68 ++#define XDMA_AST2700_INPRG_US_CMD11 0x6c ++#define XDMA_AST2700_INPRG_US_CMD20 0x70 ++#define XDMA_AST2700_INPRG_US_CMD21 0x74 ++ ++struct aspeed_xdma_cmd { ++ u64 host_addr; ++ u64 pitch; ++ u64 cmd; ++ u64 reserved; +}; + -+static struct aspeed_uart_routing_selector ast2700n1_uart6_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART6), -+ .reg = HICRA, -+ .shift = 19, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO5, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART5, -+ UART_ROUTING_IO10, -+ NULL, -+ }, ++struct aspeed_xdma_regs { ++ u8 bmc_cmdq_addr; ++ u8 bmc_cmdq_addr_ext; ++ u8 bmc_cmdq_endp; ++ u8 bmc_cmdq_writep; ++ u8 bmc_cmdq_readp; ++ u8 control; ++ u8 status; +}; + -+static struct aspeed_uart_routing_selector ast2700n1_uart5_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_UART5), -+ .reg = HICRA, -+ .shift = 16, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_IO10, -+ NULL, -+ }, ++struct aspeed_xdma_status_bits { ++ u32 us_comp; ++ u32 ds_comp; ++ u32 ds_dirty; +}; + -+static struct aspeed_uart_routing_selector ast2700n1_io8_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO8), -+ .reg = HICRA, -+ .shift = 9, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO10, -+ NULL, -+ }, ++struct aspeed_xdma; ++ ++struct aspeed_xdma_chip { ++ u32 control; ++ u32 scu_bmc_class; ++ u32 scu_misc_ctrl; ++ u32 scu_misc_mask; ++ u32 scu_disable_mask; ++ u32 scu_pcie_conf; ++ u32 scu_pcie_ctrl; ++ unsigned int queue_entry_size; ++ struct aspeed_xdma_regs regs; ++ struct aspeed_xdma_status_bits status_bits; ++ unsigned int (*set_cmd)(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, u64 bmc_addr); +}; + -+static struct aspeed_uart_routing_selector ast2700n1_io7_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO7), -+ .reg = HICRA, -+ .shift = 6, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_IO5, -+ UART_ROUTING_IO6, -+ UART_ROUTING_IO10, -+ NULL, -+ }, ++struct aspeed_xdma_client; ++ ++struct aspeed_xdma { ++ struct kobject kobj; ++ const struct aspeed_xdma_chip *chip; ++ ++ int irq; ++ int pcie_irq; ++ struct clk *clock; ++ struct device *dev; ++ void __iomem *base; ++ struct reset_control *reset; ++ struct reset_control *reset_rc; ++ ++ /* Protects current_client */ ++ spinlock_t client_lock; ++ struct aspeed_xdma_client *current_client; ++ ++ /* Protects engine configuration */ ++ spinlock_t engine_lock; ++ struct aspeed_xdma_cmd *cmdq; ++ unsigned int cmd_idx; ++ bool in_reset; ++ bool upstream; ++ ++ /* Queue waiters for idle engine */ ++ wait_queue_head_t wait; ++ ++ struct work_struct reset_work; ++ ++ phys_addr_t mem_phys; ++ phys_addr_t mem_size; ++ void *mem_virt; ++ dma_addr_t mem_coherent; ++ dma_addr_t cmdq_phys; ++ struct gen_pool *pool; ++ ++ struct miscdevice misc; +}; + -+static struct aspeed_uart_routing_selector ast2700n1_io6_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO6), -+ .reg = HICRA, -+ .shift = 3, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_UART5, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO10, -+ NULL, -+ }, ++struct aspeed_xdma_client { ++ struct aspeed_xdma *ctx; ++ ++ bool error; ++ bool in_progress; ++ void *virt; ++ dma_addr_t phys; ++ u32 size; ++}; ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++static u32 aspeed_xdma_readl(struct aspeed_xdma *ctx, u8 reg) ++{ ++ u32 v = readl(ctx->base + reg); ++ ++ dev_dbg(ctx->dev, "read %02x[%08x]\n", reg, v); ++ return v; ++} ++ ++static void aspeed_xdma_writel(struct aspeed_xdma *ctx, u8 reg, u32 val) ++{ ++ writel(val, ctx->base + reg); ++ dev_dbg(ctx->dev, "write %02x[%08x]\n", reg, val); ++} ++ ++static void aspeed_xdma_init_eng(struct aspeed_xdma *ctx) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_endp, ++ ctx->chip->queue_entry_size * XDMA_NUM_CMDS); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_readp, ++ XDMA_BMC_CMDQ_READP_RESET); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, 0); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.control, ctx->chip->control); ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr, ctx->cmdq_phys); ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ if (ctx->chip->regs.bmc_cmdq_addr_ext) ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr_ext, ctx->cmdq_phys >> 32); ++#endif ++ ++ ctx->cmd_idx = 0; ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++} ++ ++static unsigned int aspeed_xdma_ast2500_set_cmd(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, ++ u64 bmc_addr) ++{ ++ unsigned int rc = 1; ++ unsigned int pitch = 1; ++ unsigned int line_no = 1; ++ unsigned int line_size = op->len >> ++ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; ++ u64 cmd = XDMA_CMD_AST2500_CMD_IRQ_EN | XDMA_CMD_AST2500_CMD_IRQ_BMC | ++ XDMA_CMD_AST2500_CMD_ID; ++ u64 cmd_pitch = (op->direction ? XDMA_CMD_AST2500_PITCH_UPSTREAM : 0) | ++ XDMA_CMD_AST2500_PITCH_ID; ++ ++ dev_dbg(ctx->dev, "xdma %s ast2500: bmc[%08llx] len[%08x] host[%08x]\n", ++ op->direction ? "upstream" : "downstream", bmc_addr, op->len, ++ (u32)op->host_addr); ++ ++ if (op->len > XDMA_CMD_AST2500_CMD_LINE_SIZE) { ++ unsigned int rem; ++ unsigned int total; ++ ++ line_no = op->len / XDMA_CMD_AST2500_CMD_LINE_SIZE; ++ total = XDMA_CMD_AST2500_CMD_LINE_SIZE * line_no; ++ rem = (op->len - total) >> ++ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; ++ line_size = XDMA_CMD_AST2500_CMD_LINE_SIZE; ++ pitch = line_size >> XDMA_CMD_AST2500_PITCH_SHIFT; ++ line_size >>= XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; ++ ++ if (rem) { ++ u32 rbmc = bmc_addr + total; ++ ++ cmds[1].host_addr = op->host_addr + (u64)total; ++ cmds[1].pitch = cmd_pitch | ++ ((u64)rbmc & XDMA_CMD_AST2500_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, 1) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, 1); ++ cmds[1].cmd = cmd | ++ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, 1) | ++ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, ++ rem); ++ cmds[1].reserved = 0ULL; ++ ++ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, ++ 16, 1, &cmds[1], sizeof(*cmds), ++ true); ++ ++ cmd &= ~(XDMA_CMD_AST2500_CMD_IRQ_EN | ++ XDMA_CMD_AST2500_CMD_IRQ_BMC); ++ ++ rc++; ++ } ++ } ++ ++ cmds[0].host_addr = op->host_addr; ++ cmds[0].pitch = cmd_pitch | ++ (bmc_addr & XDMA_CMD_AST2500_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, pitch) | ++ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, pitch); ++ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, line_no) | ++ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, line_size); ++ cmds[0].reserved = 0ULL; ++ ++ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, ++ sizeof(*cmds), true); ++ ++ return rc; ++} ++ ++static unsigned int aspeed_xdma_ast2600_set_cmd(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, ++ u64 bmc_addr) ++{ ++ unsigned int rc = 1; ++ unsigned int pitch = 1; ++ unsigned int line_no = 1; ++ unsigned int line_size = op->len; ++ u64 cmd = XDMA_CMD_AST2600_CMD_IRQ_BMC | ++ (op->direction ? XDMA_CMD_AST2600_CMD_UPSTREAM : 0); ++ ++ if (op->host_addr & 0xffffffff00000000ULL || ++ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) ++ cmd |= XDMA_CMD_AST2600_CMD_64_EN; ++ ++ dev_dbg(ctx->dev, "xdma %s ast2600: bmc[%08llx] len[%08x] host[%016llx]\n", ++ op->direction ? "upstream" : "downstream", ++ bmc_addr, op->len, op->host_addr); ++ ++ if ((op->host_addr & 0xff) + op->len > XDMA_CMD_AST2600_CMD_LINE_SIZE) { ++ unsigned int rem; ++ unsigned int total; ++ ++ line_no = op->len / XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; ++ total = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE * line_no; ++ rem = op->len - total; ++ line_size = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; ++ pitch = line_size; ++ ++ if (rem) { ++ u32 rbmc = bmc_addr + total; ++ ++ cmds[1].host_addr = op->host_addr + (u64)total; ++ cmds[1].pitch = ++ ((u64)rbmc & XDMA_CMD_AST2600_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, 1) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, 1); ++ cmds[1].cmd = cmd | ++ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, 1) | ++ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, ++ rem); ++ cmds[1].reserved = 0ULL; ++ ++ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, ++ 16, 1, &cmds[1], sizeof(*cmds), ++ true); ++ ++ cmd &= ~XDMA_CMD_AST2600_CMD_IRQ_BMC; ++ ++ rc++; ++ } ++ } ++ ++ cmds[0].host_addr = op->host_addr; ++ cmds[0].pitch = (bmc_addr & XDMA_CMD_AST2600_PITCH_ADDR) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, pitch) | ++ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, pitch); ++ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, line_no) | ++ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, line_size); ++ cmds[0].reserved = 0ULL; ++ ++ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, ++ sizeof(*cmds), true); ++ ++ return rc; ++} ++ ++static unsigned int aspeed_xdma_ast2700_set_cmd(struct aspeed_xdma *ctx, ++ struct aspeed_xdma_cmd cmds[2], ++ struct aspeed_xdma_op *op, ++ u64 bmc_addr) ++{ ++ unsigned int rc = 1; ++ unsigned int pitch = 1; ++ unsigned int line_no = 1; ++ unsigned int line_size = op->len; ++ u64 cmd = XDMA_CMD_AST2700_CMD_IRQ_BMC | ++ (op->direction ? XDMA_CMD_AST2700_CMD_UPSTREAM : 0); ++ ++ if (op->host_addr & 0xffffffff00000000ULL || ++ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) ++ cmd |= XDMA_CMD_AST2700_CMD_64_EN; ++ ++ dev_dbg(ctx->dev, "xdma %s ast2700: bmc[%016llx] len[%08x] host[%016llx]\n", ++ op->direction ? "upstream" : "downstream", ++ bmc_addr, op->len, op->host_addr); ++ ++ if (op->len > XDMA_CMD_AST2700_CMD_LINE_SIZE) { ++ unsigned int rem; ++ unsigned int total; ++ ++ line_no = op->len / XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; ++ total = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE * line_no; ++ rem = op->len - total; ++ line_size = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; ++ pitch = line_size; ++ ++ if (rem) { ++ u64 rbmc = bmc_addr + total; ++ ++ cmds[1].host_addr = op->host_addr + (u64)total; ++ cmds[1].pitch = ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, 1) | ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, 1); ++ cmds[1].cmd = cmd | ++ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, 1) | ++ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, ++ rem); ++ cmds[1].reserved = rbmc & XDMA_CMD_AST2700_BMC_ADDR; ++ ++ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, ++ 16, 1, &cmds[1], sizeof(*cmds), ++ true); ++ ++ cmd &= ~XDMA_CMD_AST2700_CMD_IRQ_BMC; ++ ++ rc++; ++ } ++ } ++ cmds[0].host_addr = op->host_addr; ++ cmds[0].pitch = ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, pitch) | ++ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, pitch); ++ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, line_no) | ++ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, line_size); ++ cmds[0].reserved = bmc_addr & XDMA_CMD_AST2700_BMC_ADDR; ++ ++ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, ++ sizeof(*cmds), true); ++ ++ return rc; ++} ++ ++static int aspeed_xdma_start(struct aspeed_xdma *ctx, unsigned int num_cmds, ++ struct aspeed_xdma_cmd cmds[2], bool upstream, ++ struct aspeed_xdma_client *client) ++{ ++ unsigned int i; ++ int rc = -EBUSY; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ if (ctx->in_reset) ++ goto unlock; ++ ++ spin_lock(&ctx->client_lock); ++ if (ctx->current_client) { ++ spin_unlock(&ctx->client_lock); ++ goto unlock; ++ } ++ ++ client->error = false; ++ client->in_progress = true; ++ ctx->current_client = client; ++ spin_unlock(&ctx->client_lock); ++ ++ ctx->upstream = upstream; ++ for (i = 0; i < num_cmds; ++i) { ++ trace_xdma_start(ctx, &cmds[i]); ++ /* ++ * Use memcpy_toio here to get some barriers before starting ++ * the operation. The command(s) need to be in physical memory ++ * before the XDMA engine starts. ++ */ ++ memcpy_toio(&ctx->cmdq[ctx->cmd_idx], &cmds[i], ++ sizeof(struct aspeed_xdma_cmd)); ++ ctx->cmd_idx = (ctx->cmd_idx + 1) % XDMA_NUM_CMDS; ++ } ++ ++ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, ++ ctx->cmd_idx * ctx->chip->queue_entry_size); ++ rc = 0; ++ ++unlock: ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ return rc; ++} ++ ++static void aspeed_xdma_done(struct aspeed_xdma *ctx, bool error) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctx->client_lock, flags); ++ if (ctx->current_client) { ++ ctx->current_client->error = error; ++ ctx->current_client->in_progress = false; ++ ctx->current_client = NULL; ++ } ++ spin_unlock_irqrestore(&ctx->client_lock, flags); ++ ++ wake_up_interruptible_all(&ctx->wait); ++} ++ ++static irqreturn_t aspeed_xdma_irq(int irq, void *arg) ++{ ++ struct aspeed_xdma *ctx = arg; ++ u32 status; ++ ++ spin_lock(&ctx->engine_lock); ++ status = aspeed_xdma_readl(ctx, ctx->chip->regs.status); ++ ++ trace_xdma_irq(status); ++ ++ if (status & ctx->chip->status_bits.ds_dirty) { ++ aspeed_xdma_done(ctx, true); ++ } else { ++ if (status & ctx->chip->status_bits.us_comp) { ++ if (ctx->upstream) ++ aspeed_xdma_done(ctx, false); ++ } ++ ++ if (status & ctx->chip->status_bits.ds_comp) { ++ if (!ctx->upstream) ++ aspeed_xdma_done(ctx, false); ++ } ++ } ++ ++ aspeed_xdma_writel(ctx, ctx->chip->regs.status, status); ++ spin_unlock(&ctx->engine_lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static void aspeed_xdma_reset(struct aspeed_xdma *ctx) ++{ ++ unsigned long flags; ++ ++ trace_xdma_reset(ctx); ++ ++ reset_control_assert(ctx->reset); ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); ++ reset_control_deassert(ctx->reset); ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); ++ ++ aspeed_xdma_init_eng(ctx); ++ ++ aspeed_xdma_done(ctx, true); ++ ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ ctx->in_reset = false; ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ ++ wake_up_interruptible(&ctx->wait); ++} ++ ++static void aspeed_xdma_reset_work(struct work_struct *work) ++{ ++ struct aspeed_xdma *ctx = container_of(work, struct aspeed_xdma, ++ reset_work); ++ ++ aspeed_xdma_reset(ctx); ++} ++ ++static irqreturn_t aspeed_xdma_pcie_irq(int irq, void *arg) ++{ ++ struct aspeed_xdma *ctx = arg; ++ ++ trace_xdma_perst(ctx); ++ ++ spin_lock(&ctx->engine_lock); ++ if (ctx->in_reset) { ++ spin_unlock(&ctx->engine_lock); ++ return IRQ_HANDLED; ++ } ++ ++ ctx->in_reset = true; ++ spin_unlock(&ctx->engine_lock); ++ ++ schedule_work(&ctx->reset_work); ++ return IRQ_HANDLED; ++} ++ ++static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ int rc; ++ unsigned int num_cmds; ++ struct aspeed_xdma_op op; ++ struct aspeed_xdma_cmd cmds[2]; ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; ++ ++ if (len != sizeof(op)) ++ return -EINVAL; ++ ++ if (READ_ONCE(client->in_progress)) ++ return -EBUSY; ++ ++ if (copy_from_user(&op, buf, len)) ++ return -EFAULT; ++ ++ if (!op.len || op.len > client->size || ++ op.direction > ASPEED_XDMA_DIRECTION_UPSTREAM) ++ return -EINVAL; ++ ++ num_cmds = ctx->chip->set_cmd(ctx, cmds, &op, client->phys); ++ do { ++ rc = aspeed_xdma_start(ctx, num_cmds, cmds, !!op.direction, ++ client); ++ if (!rc) ++ break; ++ ++ if ((file->f_flags & O_NONBLOCK) || rc != -EBUSY) ++ return rc; ++ ++ rc = wait_event_interruptible(ctx->wait, ++ !(ctx->current_client || ++ ctx->in_reset)); ++ } while (!rc); ++ ++ if (rc) ++ return -EINTR; ++ ++ if (!(file->f_flags & O_NONBLOCK)) { ++ rc = wait_event_interruptible(ctx->wait, !client->in_progress); ++ if (rc) ++ return -EINTR; ++ ++ if (client->error) ++ return -EIO; ++ } ++ ++ return len; ++} ++ ++static __poll_t aspeed_xdma_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ __poll_t mask = 0; ++ __poll_t req = poll_requested_events(wait); ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; ++ ++ if (req & (EPOLLIN | EPOLLRDNORM)) { ++ if (READ_ONCE(client->in_progress)) ++ poll_wait(file, &ctx->wait, wait); ++ ++ if (!READ_ONCE(client->in_progress)) { ++ if (READ_ONCE(client->error)) ++ mask |= EPOLLERR; ++ else ++ mask |= EPOLLIN | EPOLLRDNORM; ++ } ++ } ++ ++ if (req & (EPOLLOUT | EPOLLWRNORM)) { ++ if (READ_ONCE(ctx->current_client)) ++ poll_wait(file, &ctx->wait, wait); ++ ++ if (!READ_ONCE(ctx->current_client)) ++ mask |= EPOLLOUT | EPOLLWRNORM; ++ } ++ ++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT ++ // 2600 and older IC do reset here will trigger unexpected irq. ++ // Only do it for 2700. ++ if (mask) ++ aspeed_xdma_reset(ctx); ++#endif ++ ++ return mask; ++} ++ ++static long aspeed_xdma_ioctl(struct file *file, unsigned int cmd, ++ unsigned long param) ++{ ++ unsigned long flags; ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; ++ ++ switch (cmd) { ++ case ASPEED_XDMA_IOCTL_RESET: ++ spin_lock_irqsave(&ctx->engine_lock, flags); ++ if (ctx->in_reset) { ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ return 0; ++ } ++ ++ ctx->in_reset = true; ++ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ ++ if (READ_ONCE(ctx->current_client)) ++ dev_warn(ctx->dev, ++ "User reset with transfer in progress.\n"); ++ ++ aspeed_xdma_reset(ctx); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void aspeed_xdma_vma_close(struct vm_area_struct *vma) ++{ ++ int rc; ++ struct aspeed_xdma_client *client = vma->vm_private_data; ++ ++ rc = wait_event_interruptible(client->ctx->wait, !client->in_progress); ++ if (rc) ++ return; ++ ++ gen_pool_free(client->ctx->pool, (unsigned long)client->virt, ++ client->size); ++ trace_xdma_unmap(client); ++ ++ client->virt = NULL; ++ client->phys = 0; ++ client->size = 0; ++} ++ ++static const struct vm_operations_struct aspeed_xdma_vm_ops = { ++ .close = aspeed_xdma_vma_close, ++}; ++ ++static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int rc; ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; ++ ++ /* restrict file to one mapping */ ++ if (client->size) ++ return -EBUSY; ++ ++ client->size = vma->vm_end - vma->vm_start; ++ client->virt = gen_pool_dma_alloc(ctx->pool, client->size, ++ &client->phys); ++ if (!client->virt) { ++ trace_xdma_mmap_error(client, 0UL); ++ client->phys = 0; ++ client->size = 0; ++ return -ENOMEM; ++ } ++ ++ vma->vm_pgoff = (client->phys - ctx->mem_phys) >> PAGE_SHIFT; ++ vma->vm_ops = &aspeed_xdma_vm_ops; ++ vma->vm_private_data = client; ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ rc = io_remap_pfn_range(vma, vma->vm_start, client->phys >> PAGE_SHIFT, ++ client->size, vma->vm_page_prot); ++ if (rc) { ++ dev_warn(ctx->dev, "mmap err: v[%08lx] to p[%pa], s[%08x]\n", ++ vma->vm_start, &client->phys, client->size); ++ ++ gen_pool_free(ctx->pool, (unsigned long)client->virt, ++ client->size); ++ ++ trace_xdma_mmap_error(client, vma->vm_start); ++ client->virt = NULL; ++ client->phys = 0; ++ client->size = 0; ++ return rc; ++ } ++ ++ trace_xdma_mmap(client); ++ dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%pa], s[%08x]\n", ++ vma->vm_start, &client->phys, client->size); ++ ++ return 0; ++} ++ ++static int aspeed_xdma_open(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *misc = file->private_data; ++ struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc); ++ struct aspeed_xdma_client *client = kzalloc(sizeof(*client), ++ GFP_KERNEL); ++ ++ if (!client) ++ return -ENOMEM; ++ ++ kobject_get(&ctx->kobj); ++ client->ctx = ctx; ++ file->private_data = client; ++ return 0; ++} ++ ++static int aspeed_xdma_release(struct inode *inode, struct file *file) ++{ ++ bool reset = false; ++ unsigned long flags; ++ struct aspeed_xdma_client *client = file->private_data; ++ struct aspeed_xdma *ctx = client->ctx; ++ ++ spin_lock_irqsave(&ctx->client_lock, flags); ++ if (client == ctx->current_client) { ++ spin_lock(&ctx->engine_lock); ++ if (ctx->in_reset) { ++ ctx->current_client = NULL; ++ } else { ++ ctx->in_reset = true; ++ reset = true; ++ } ++ spin_unlock(&ctx->engine_lock); ++ } ++ spin_unlock_irqrestore(&ctx->client_lock, flags); ++ ++ if (reset) ++ aspeed_xdma_reset(ctx); ++ ++ if (client->virt) { ++ gen_pool_free(ctx->pool, (unsigned long)client->virt, ++ client->size); ++ trace_xdma_unmap(client); ++ } ++ ++ kfree(client); ++ kobject_put(&ctx->kobj); ++ return 0; ++} ++ ++static const struct file_operations aspeed_xdma_fops = { ++ .owner = THIS_MODULE, ++ .write = aspeed_xdma_write, ++ .poll = aspeed_xdma_poll, ++ .unlocked_ioctl = aspeed_xdma_ioctl, ++ .mmap = aspeed_xdma_mmap, ++ .open = aspeed_xdma_open, ++ .release = aspeed_xdma_release, ++}; ++ ++static int aspeed_xdma_init_scu(struct aspeed_xdma *ctx, struct device *dev) ++{ ++ struct regmap *scu = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "aspeed,scu"); ++ ++ if (!IS_ERR(scu)) { ++ u32 selection; ++ bool pcie_device_bmc = true; ++ const u32 bmc = SCU_PCIE_CONF_BMC_EN | ++ SCU_PCIE_CONF_BMC_EN_MSI | SCU_PCIE_CONF_BMC_EN_IRQ | ++ SCU_PCIE_CONF_BMC_EN_DMA; ++ const u32 vga = SCU_PCIE_CONF_VGA_EN | ++ SCU_PCIE_CONF_VGA_EN_MSI | SCU_PCIE_CONF_VGA_EN_IRQ | ++ SCU_PCIE_CONF_VGA_EN_DMA; ++ const char *pcie = NULL; ++ ++ if (!of_property_read_string(dev->of_node, ++ "aspeed,pcie-device", &pcie)) { ++ if (!strcmp(pcie, "vga")) { ++ pcie_device_bmc = false; ++ } else if (strcmp(pcie, "bmc")) { ++ dev_err(dev, ++ "Invalid pcie-device property %s.\n", ++ pcie); ++ return -EINVAL; ++ } ++ } ++ ++ if (pcie_device_bmc) { ++ selection = bmc; ++ regmap_update_bits(scu, ctx->chip->scu_bmc_class, ++ SCU_BMC_CLASS_REV_MASK, ++ SCU_BMC_CLASS_REV_XDMA); ++ } else { ++ selection = vga; ++ } ++ ++ regmap_update_bits(scu, ctx->chip->scu_pcie_conf, bmc | vga, ++ selection); ++ ++ if (ctx->chip->scu_misc_ctrl) { ++ regmap_update_bits(scu, ctx->chip->scu_misc_ctrl, ++ ctx->chip->scu_misc_mask, ++ ctx->chip->scu_misc_mask); ++ ++ regmap_update_bits(scu, SCU_AST2600_DEBUG_CTRL, ++ ctx->chip->scu_disable_mask, 0); ++ } ++ ++ if (ctx->chip->scu_pcie_ctrl) { ++ regmap_update_bits(scu, ctx->chip->scu_pcie_ctrl, ++ SCU_AST2700_PCIE_CTRL_DMA_EN, ++ SCU_AST2700_PCIE_CTRL_DMA_EN); ++ } ++ } else { ++ dev_warn(dev, "Unable to configure PCIe: %ld; continuing.\n", ++ PTR_ERR(scu)); ++ } ++ ++ return 0; ++} ++ ++static void aspeed_xdma_kobject_release(struct kobject *kobj) ++{ ++ struct aspeed_xdma *ctx = container_of(kobj, struct aspeed_xdma, kobj); ++ ++ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); ++ ++ gen_pool_destroy(ctx->pool); ++ ++ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, ++ ctx->mem_coherent); ++} ++ ++static const struct kobj_type aspeed_xdma_kobject_type = { ++ .release = aspeed_xdma_kobject_release, ++}; ++ ++static int aspeed_xdma_probe(struct platform_device *pdev) ++{ ++ int rc, id; ++ struct aspeed_xdma *ctx; ++ struct reserved_mem *mem; ++ struct device *dev = &pdev->dev; ++ struct device_node *memory_region; ++ const void *md = of_device_get_match_data(dev); ++ bool rc_f; ++ ++ if (!md) ++ return -ENODEV; ++ ++ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ ctx->chip = md; ++ ctx->dev = dev; ++ platform_set_drvdata(pdev, ctx); ++ spin_lock_init(&ctx->client_lock); ++ spin_lock_init(&ctx->engine_lock); ++ INIT_WORK(&ctx->reset_work, aspeed_xdma_reset_work); ++ init_waitqueue_head(&ctx->wait); ++ ++ rc_f = of_find_property(dev->of_node, "pcie_rc", NULL) ? 1 : 0; ++ ++ ctx->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(ctx->base)) ++ return PTR_ERR(ctx->base); ++ ++ ctx->irq = platform_get_irq(pdev, 0); ++ if (ctx->irq < 0) { ++ dev_err(dev, "Failed to find IRQ.\n"); ++ return ctx->irq; ++ } ++ ++ ctx->pcie_irq = platform_get_irq(pdev, 1); ++ if (ctx->pcie_irq < 0) ++ return ctx->pcie_irq; ++ ++ rc = devm_request_irq(dev, ctx->irq, aspeed_xdma_irq, 0, ++ dev_name(dev), ctx); ++ if (rc < 0) { ++ dev_err(dev, "Failed to request IRQ %d.\n", ctx->irq); ++ return rc; ++ } ++ ++ ctx->clock = devm_clk_get(dev, NULL); ++ if (IS_ERR(ctx->clock)) { ++ dev_err(dev, "Failed to request clock.\n"); ++ return PTR_ERR(ctx->clock); ++ } ++ ++ ctx->reset = devm_reset_control_get_exclusive(dev, NULL); ++ if (IS_ERR(ctx->reset)) { ++ dev_err(dev, "Failed to request reset control.\n"); ++ return PTR_ERR(ctx->reset); ++ } ++ ++ if (rc_f) { ++ ctx->reset_rc = devm_reset_control_get_exclusive(dev, "root-complex"); ++ if (IS_ERR(ctx->reset_rc)) { ++ dev_dbg(dev, "Failed to request reset RC control.\n"); ++ ctx->reset_rc = NULL; ++ } ++ } ++ ++ memory_region = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (!memory_region) { ++ dev_err(dev, "Failed to find memory-region.\n"); ++ rc = -ENOMEM; ++ return rc; ++ } ++ ++ mem = of_reserved_mem_lookup(memory_region); ++ of_node_put(memory_region); ++ if (!mem) { ++ dev_err(dev, "Failed to find reserved memory.\n"); ++ rc = -ENOMEM; ++ return rc; ++ } ++ ++ ctx->mem_phys = mem->base; ++ ctx->mem_size = mem->size; ++ ++ rc = of_reserved_mem_device_init(dev); ++ if (rc) { ++ dev_err(dev, "Failed to init reserved memory.\n"); ++ return rc; ++ } ++ ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); ++ if (rc) { ++ dev_err(dev, "Failed to mask DMA.\n"); ++ return rc; ++ } ++ ++ ctx->mem_virt = dma_alloc_coherent(dev, ctx->mem_size, ++ &ctx->mem_coherent, __GFP_NOWARN); ++ if (!ctx->mem_virt) { ++ dev_err(dev, "Failed to allocate reserved memory.\n"); ++ rc = -ENOMEM; ++ return rc; ++ } ++ ++ ctx->pool = gen_pool_create(ilog2(PAGE_SIZE), -1); ++ if (!ctx->pool) { ++ dev_err(dev, "Failed to setup genalloc pool.\n"); ++ rc = -ENOMEM; ++ goto err_nopool; ++ } ++ ++ rc = gen_pool_add_virt(ctx->pool, (unsigned long)ctx->mem_virt, ++ ctx->mem_phys, ctx->mem_size, -1); ++ if (rc) { ++ dev_err(ctx->dev, "Failed to add memory to genalloc pool.\n"); ++ goto err_pool_scu_clk; ++ } ++ ++ rc = aspeed_xdma_init_scu(ctx, dev); ++ if (rc) ++ goto err_pool_scu_clk; ++ ++ rc = clk_prepare_enable(ctx->clock); ++ if (rc) { ++ dev_err(dev, "Failed to enable the clock.\n"); ++ goto err_pool_scu_clk; ++ } ++ ++ if (ctx->reset_rc) { ++ rc = reset_control_deassert(ctx->reset_rc); ++ if (rc) { ++ dev_err(dev, "Failed to clear the RC reset.\n"); ++ goto err_reset_rc; ++ } ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); ++ } ++ ++ rc = reset_control_deassert(ctx->reset); ++ if (rc) { ++ dev_err(dev, "Failed to clear the reset.\n"); ++ goto err_reset; ++ } ++ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, ++ XDMA_ENGINE_SETUP_TIME_MAX_US); ++ ++ ctx->cmdq = gen_pool_dma_alloc(ctx->pool, XDMA_CMDQ_SIZE, ++ &ctx->cmdq_phys); ++ if (!ctx->cmdq) { ++ dev_err(ctx->dev, "Failed to genalloc cmdq.\n"); ++ rc = -ENOMEM; ++ goto err_pool; ++ } ++ ++ aspeed_xdma_init_eng(ctx); ++ ++ id = of_alias_get_id(dev->of_node, "xdma"); ++ if (id < 0) ++ id = 0; ++ ++ ctx->misc.minor = MISC_DYNAMIC_MINOR; ++ ctx->misc.fops = &aspeed_xdma_fops; ++ ctx->misc.name = kasprintf(GFP_KERNEL, "%s%d", DEVICE_NAME, id); ++ ctx->misc.parent = dev; ++ rc = misc_register(&ctx->misc); ++ if (rc) { ++ dev_err(dev, "Failed to register xdma miscdevice.\n"); ++ goto err_misc; ++ } ++ ++ /* ++ * This interrupt could fire immediately so only request it once the ++ * engine and driver are initialized. ++ */ ++ rc = devm_request_irq(dev, ctx->pcie_irq, aspeed_xdma_pcie_irq, ++ IRQF_SHARED, dev_name(dev), ctx); ++ if (rc) ++ return rc; ++ ++ kobject_init(&ctx->kobj, &aspeed_xdma_kobject_type); ++ return 0; ++ ++err_misc: ++ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); ++err_pool: ++ reset_control_assert(ctx->reset); ++err_reset: ++ if (ctx->reset_rc) ++ reset_control_assert(ctx->reset_rc); ++err_reset_rc: ++ clk_disable_unprepare(ctx->clock); ++err_pool_scu_clk: ++ gen_pool_destroy(ctx->pool); ++err_nopool: ++ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, ++ ctx->mem_coherent); ++ return rc; ++} ++ ++static void aspeed_xdma_remove(struct platform_device *pdev) ++{ ++ struct aspeed_xdma *ctx = platform_get_drvdata(pdev); ++ ++ reset_control_assert(ctx->reset); ++ if (ctx->reset_rc) ++ reset_control_assert(ctx->reset_rc); ++ clk_disable_unprepare(ctx->clock); ++ ++ aspeed_xdma_done(ctx, true); ++ ++ misc_deregister(&ctx->misc); ++ kobject_put(&ctx->kobj); ++} ++ ++static const struct aspeed_xdma_chip aspeed_ast2500_xdma_chip = { ++ .control = XDMA_AST2500_CTRL_US_COMP | XDMA_AST2500_CTRL_DS_COMP | ++ XDMA_AST2500_CTRL_DS_DIRTY | XDMA_AST2500_CTRL_DS_SIZE_256 | ++ XDMA_AST2500_CTRL_DS_TIMEOUT | XDMA_AST2500_CTRL_DS_CHECK_ID, ++ .scu_bmc_class = SCU_AST2500_BMC_CLASS_REV, ++ .scu_misc_ctrl = 0, ++ .scu_pcie_conf = SCU_AST2500_PCIE_CONF, ++ .scu_pcie_ctrl = 0, ++ .queue_entry_size = XDMA_AST2500_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2500_BMC_CMDQ_ADDR, ++ .bmc_cmdq_addr_ext = 0, ++ .bmc_cmdq_endp = XDMA_AST2500_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2500_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2500_BMC_CMDQ_READP, ++ .control = XDMA_AST2500_CTRL, ++ .status = XDMA_AST2500_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2500_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2500_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2500_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2500_set_cmd, ++}; ++ ++static const struct aspeed_xdma_chip aspeed_ast2600_xdma_chip = { ++ .control = XDMA_AST2600_CTRL_US_COMP | XDMA_AST2600_CTRL_DS_COMP | ++ XDMA_AST2600_CTRL_DS_DIRTY | XDMA_AST2600_CTRL_DS_SIZE_256, ++ .scu_bmc_class = SCU_AST2600_BMC_CLASS_REV, ++ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, ++ .scu_misc_mask = SCU_AST2600_MISC_CTRL_XDMA_BMC, ++ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE, ++ .scu_pcie_conf = SCU_AST2600_PCIE_CONF, ++ .scu_pcie_ctrl = 0, ++ .queue_entry_size = XDMA_AST2600_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2600_BMC_CMDQ_ADDR, ++ .bmc_cmdq_addr_ext = 0, ++ .bmc_cmdq_endp = XDMA_AST2600_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2600_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2600_BMC_CMDQ_READP, ++ .control = XDMA_AST2600_CTRL, ++ .status = XDMA_AST2600_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2600_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2600_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2600_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2600_set_cmd, ++}; ++ ++static const struct aspeed_xdma_chip aspeed_ast2700_xdma0_chip = { ++ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | ++ XDMA_AST2700_CTRL_DS_DIRTY, ++ .scu_bmc_class = SCU_AST2700_PCIE0_BMC_CLASS_REV, ++ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, ++ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, ++ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, ++ .scu_pcie_conf = SCU_AST2700_PCIE0_CONF, ++ .scu_pcie_ctrl = SCU_AST2700_PCIE0_CTRL, ++ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, ++ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, ++ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, ++ .control = XDMA_AST2700_CTRL, ++ .status = XDMA_AST2700_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2700_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2700_set_cmd, ++}; ++ ++static const struct aspeed_xdma_chip aspeed_ast2700_xdma1_chip = { ++ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | ++ XDMA_AST2700_CTRL_DS_DIRTY, ++ .scu_bmc_class = SCU_AST2700_PCIE1_BMC_CLASS_REV, ++ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, ++ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, ++ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, ++ .scu_pcie_conf = SCU_AST2700_PCIE1_CONF, ++ .scu_pcie_ctrl = SCU_AST2700_PCIE1_CTRL, ++ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, ++ .regs = { ++ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, ++ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, ++ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, ++ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, ++ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, ++ .control = XDMA_AST2700_CTRL, ++ .status = XDMA_AST2700_STATUS, ++ }, ++ .status_bits = { ++ .us_comp = XDMA_AST2700_STATUS_US_COMP, ++ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, ++ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, ++ }, ++ .set_cmd = aspeed_xdma_ast2700_set_cmd, +}; + -+static struct aspeed_uart_routing_selector ast2700n1_io5_sel = { -+ .dev_attr = ROUTING_ATTR(UART_ROUTING_IO5), -+ .reg = HICRA, -+ .shift = 0, -+ .mask = 0x7, -+ .options = { -+ UART_ROUTING_UART5, -+ UART_ROUTING_UART6, -+ UART_ROUTING_UART7, -+ UART_ROUTING_UART8, -+ UART_ROUTING_UART10, -+ UART_ROUTING_IO7, -+ UART_ROUTING_IO8, -+ UART_ROUTING_IO10, -+ NULL, -+ }, ++static const struct of_device_id aspeed_xdma_match[] = { ++ { ++ .compatible = "aspeed,ast2500-xdma", ++ .data = &aspeed_ast2500_xdma_chip, ++ }, ++ { ++ .compatible = "aspeed,ast2600-xdma", ++ .data = &aspeed_ast2600_xdma_chip, ++ }, ++ { ++ .compatible = "aspeed,ast2700-xdma0", ++ .data = &aspeed_ast2700_xdma0_chip, ++ }, ++ { ++ .compatible = "aspeed,ast2700-xdma1", ++ .data = &aspeed_ast2700_xdma1_chip, ++ }, ++ { }, +}; + -+static struct attribute *ast2700n1_uart_routing_attrs[] = { -+ &ast2700n1_uart10_sel.dev_attr.attr, -+ &ast2700n1_io10_sel.dev_attr.attr, -+ &ast2700n1_uart8_sel.dev_attr.attr, -+ &ast2700n1_uart7_sel.dev_attr.attr, -+ &ast2700n1_uart6_sel.dev_attr.attr, -+ &ast2700n1_uart5_sel.dev_attr.attr, -+ &ast2700n1_io8_sel.dev_attr.attr, -+ &ast2700n1_io7_sel.dev_attr.attr, -+ &ast2700n1_io6_sel.dev_attr.attr, -+ &ast2700n1_io5_sel.dev_attr.attr, -+ NULL, ++static struct platform_driver aspeed_xdma_driver = { ++ .probe = aspeed_xdma_probe, ++ .remove = aspeed_xdma_remove, ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_xdma_match, ++ }, +}; + -+static const struct attribute_group ast2700n1_uart_routing_attr_group = { -+ .attrs = ast2700n1_uart_routing_attrs, -+}; ++module_platform_driver(aspeed_xdma_driver); + - static ssize_t aspeed_uart_routing_show(struct device *dev, - struct device_attribute *attr, - char *buf) -@@ -580,6 +1000,10 @@ - .data = &ast2500_uart_routing_attr_group }, - { .compatible = "aspeed,ast2600-uart-routing", - .data = &ast2600_uart_routing_attr_group }, -+ { .compatible = "aspeed,ast2700n0-uart-routing", -+ .data = &ast2700n0_uart_routing_attr_group }, -+ { .compatible = "aspeed,ast2700n1-uart-routing", -+ .data = &ast2700n1_uart_routing_attr_group }, - { }, - }; - -@@ -589,7 +1013,7 @@ - .of_match_table = aspeed_uart_routing_table, - }, - .probe = aspeed_uart_routing_probe, -- .remove_new = aspeed_uart_routing_remove, -+ .remove = aspeed_uart_routing_remove, - }; - - module_platform_driver(aspeed_uart_routing_driver); -diff --git a/drivers/soc/aspeed/aspeed-udma.c b/drivers/soc/aspeed/aspeed-udma.c ---- a/drivers/soc/aspeed/aspeed-udma.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-udma.c 2026-04-08 18:03:48.311705302 +0000 -@@ -0,0 +1,424 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later ++MODULE_AUTHOR("Eddie James"); ++MODULE_DESCRIPTION("ASPEED XDMA Engine Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/soc/aspeed/ast2600-otp.c b/drivers/soc/aspeed/ast2600-otp.c +--- a/drivers/soc/aspeed/ast2600-otp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/ast2600-otp.c 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,638 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright 2020 Aspeed Technology Inc. ++ * Copyright (C) ASPEED Technology Inc. + */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ ++#include ++#include ++#include ++#include +#include -+#include -+#include ++#include +#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+#define DEVICE_NAME "aspeed-udma" -+ -+/* UART DMA registers offset */ -+#define UDMA_TX_DMA_EN 0x000 -+#define UDMA_RX_DMA_EN 0x004 -+#define UDMA_MISC 0x008 -+#define UDMA_MISC_RX_BUFSZ GENMASK(3, 2) -+#define UDMA_MISC_TX_BUFSZ GENMASK(1, 0) -+#define UDMA_TMOUT_TIMER 0x00c -+#define UDMA_TX_DMA_RST 0x020 -+#define UDMA_RX_DMA_RST 0x024 -+#define UDMA_TX_DMA_INT_EN 0x030 -+#define UDMA_TX_DMA_INT_STS 0x034 -+#define UDMA_RX_DMA_INT_EN 0x038 -+#define UDMA_RX_DMA_INT_STS 0x03c -+ -+#define UDMA_CHX_OFF(x) ((x) * 0x20) -+#define UDMA_CHX_TX_RD_PTR(x) (0x040 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_TX_WR_PTR(x) (0x044 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_TX_BUF_ADDR(x) (0x048 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_TX_CTRL(x) (0x04c + UDMA_CHX_OFF(x)) -+#define UDMA_TX_CTRL_BUF_ADDRH GENMASK(10, 8) -+#define UDMA_TX_CTRL_TMOUT_DIS BIT(4) -+#define UDMA_TX_CTRL_BUFSZ GENMASK(3, 0) -+#define UDMA_CHX_RX_RD_PTR(x) (0x050 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_RX_WR_PTR(x) (0x054 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_RX_BUF_ADDR(x) (0x058 + UDMA_CHX_OFF(x)) -+#define UDMA_CHX_RX_CTRL(x) (0x05c + UDMA_CHX_OFF(x)) -+#define UDMA_RX_CTRL_BUF_ADDRH GENMASK(10, 8) -+#define UDMA_RX_CTRL_TMOUT_DIS BIT(4) -+#define UDMA_RX_CTRL_BUFSZ GENMASK(1, 0) -+ -+#define UDMA_MAX_CHANNEL 16 -+#define UDMA_TMOUT 0x200 -+ -+enum aspeed_udma_bufsz_code { -+ UDMA_BUFSZ_CODE_1KB, -+ UDMA_BUFSZ_CODE_4KB, -+ UDMA_BUFSZ_CODE_16KB, -+ UDMA_BUFSZ_CODE_64KB, -+}; -+ -+struct aspeed_udma_chan { -+ dma_addr_t dma_addr; -+ -+ u32 rb_sz; -+ -+ aspeed_udma_cb_t cb; -+ void *cb_arg; ++#define ASPEED_REVISION_ID0 0x04 ++#define ASPEED_REVISION_ID1 0x14 ++#define ID0_AST2600A0 0x05000303 ++#define ID1_AST2600A0 0x05000303 ++#define ID0_AST2600A1 0x05010303 ++#define ID1_AST2600A1 0x05010303 ++#define ID0_AST2600A2 0x05010303 ++#define ID1_AST2600A2 0x05020303 ++#define ID0_AST2600A3 0x05030303 ++#define ID1_AST2600A3 0x05030303 ++#define ID0_AST2620A1 0x05010203 ++#define ID1_AST2620A1 0x05010203 ++#define ID0_AST2620A2 0x05010203 ++#define ID1_AST2620A2 0x05020203 ++#define ID0_AST2620A3 0x05030203 ++#define ID1_AST2620A3 0x05030203 ++#define ID0_AST2605A2 0x05010103 ++#define ID1_AST2605A2 0x05020103 ++#define ID0_AST2605A3 0x05030103 ++#define ID1_AST2605A3 0x05030103 ++#define ID0_AST2625A3 0x05030403 ++#define ID1_AST2625A3 0x05030403 + -+ bool dis_tmout; -+}; ++#define OTP_PROTECT_KEY 0x0 ++#define OTP_PASSWD 0x349fe38a ++#define OTP_COMMAND 0x4 ++#define OTP_TIMING 0x8 ++#define OTP_ADDR 0x10 ++#define OTP_STATUS 0x14 ++#define OTP_COMPARE_1 0x20 ++#define OTP_COMPARE_2 0x24 ++#define OTP_COMPARE_3 0x28 ++#define OTP_COMPARE_4 0x2c ++#define SW_REV_ID0 0x68 ++#define SW_REV_ID1 0x6c ++#define SEC_KEY_NUM 0x78 ++#define RETRY 20 + -+struct aspeed_udma { -+ struct device *dev; -+ u8 __iomem *regs; -+ int irq; -+ struct aspeed_udma_chan tx_chs[UDMA_MAX_CHANNEL]; -+ struct aspeed_udma_chan rx_chs[UDMA_MAX_CHANNEL]; -+ spinlock_t lock; ++struct aspeed_otp { ++ struct miscdevice miscdev; ++ void __iomem *reg_base; ++ bool is_open; ++ u32 otp_ver; ++ u32 *data; +}; + -+struct aspeed_udma udma[1]; ++static DEFINE_SPINLOCK(otp_state_lock); + -+static int aspeed_udma_get_bufsz_code(u32 buf_sz) ++static inline u32 aspeed_otp_read(struct aspeed_otp *ctx, u32 reg) +{ -+ switch (buf_sz) { -+ case SZ_1K: -+ return UDMA_BUFSZ_CODE_1KB; -+ case SZ_4K: -+ return UDMA_BUFSZ_CODE_4KB; -+ case SZ_16K: -+ return UDMA_BUFSZ_CODE_16KB; -+ case SZ_64K: -+ return UDMA_BUFSZ_CODE_64KB; -+ default: -+ break; -+ } -+ -+ return -1; -+} ++ int val; + -+static u32 aspeed_udma_get_tx_rptr(u32 ch_no) -+{ -+ return readl(udma->regs + UDMA_CHX_TX_RD_PTR(ch_no)); ++ val = readl(ctx->reg_base + reg); ++ // printk("read:reg = 0x%08x, val = 0x%08x\n", reg, val); ++ return val; +} + -+static u32 aspeed_udma_get_rx_wptr(u32 ch_no) ++static inline void aspeed_otp_write(struct aspeed_otp *ctx, u32 val, u32 reg) +{ -+ return readl(udma->regs + UDMA_CHX_RX_WR_PTR(ch_no)); ++ // printk("write:reg = 0x%08x, val = 0x%08x\n", reg, val); ++ writel(val, ctx->reg_base + reg); +} + -+static void aspeed_udma_set_ptr(u32 ch_no, u32 ptr, bool is_tx) ++static uint32_t chip_version(u32 revid0, u32 revid1) +{ -+ writel(ptr, udma->regs + -+ ((is_tx) ? UDMA_CHX_TX_WR_PTR(ch_no) : UDMA_CHX_RX_RD_PTR(ch_no))); ++ if (revid0 == ID0_AST2600A0 && revid1 == ID1_AST2600A0) { ++ /* AST2600-A0 */ ++ return OTP_A0; ++ } else if (revid0 == ID0_AST2600A1 && revid1 == ID1_AST2600A1) { ++ /* AST2600-A1 */ ++ return OTP_A1; ++ } else if (revid0 == ID0_AST2600A2 && revid1 == ID1_AST2600A2) { ++ /* AST2600-A2 */ ++ return OTP_A2; ++ } else if (revid0 == ID0_AST2600A3 && revid1 == ID1_AST2600A3) { ++ /* AST2600-A3 */ ++ return OTP_A3; ++ } else if (revid0 == ID0_AST2620A1 && revid1 == ID1_AST2620A1) { ++ /* AST2620-A1 */ ++ return OTP_A1; ++ } else if (revid0 == ID0_AST2620A2 && revid1 == ID1_AST2620A2) { ++ /* AST2620-A2 */ ++ return OTP_A2; ++ } else if (revid0 == ID0_AST2620A3 && revid1 == ID1_AST2620A3) { ++ /* AST2620-A3 */ ++ return OTP_A3; ++ } else if (revid0 == ID0_AST2605A2 && revid1 == ID1_AST2605A2) { ++ /* AST2605-A2 */ ++ return OTP_A2; ++ } else if (revid0 == ID0_AST2605A3 && revid1 == ID1_AST2605A3) { ++ /* AST2605-A3 */ ++ return OTP_A3; ++ } else if (revid0 == ID0_AST2625A3 && revid1 == ID1_AST2625A3) { ++ /* AST2605-A3 */ ++ return OTP_A3; ++ } ++ return -1; +} + -+void aspeed_udma_set_tx_wptr(u32 ch_no, u32 wptr) ++static void wait_complete(struct aspeed_otp *ctx) +{ -+ aspeed_udma_set_ptr(ch_no, wptr, true); -+} -+EXPORT_SYMBOL(aspeed_udma_set_tx_wptr); ++ int reg; ++ int i = 0; + -+void aspeed_udma_set_rx_rptr(u32 ch_no, u32 rptr) -+{ -+ aspeed_udma_set_ptr(ch_no, rptr, false); ++ do { ++ reg = aspeed_otp_read(ctx, OTP_STATUS); ++ if ((reg & 0x6) == 0x6) ++ i++; ++ } while (i != 2); +} -+EXPORT_SYMBOL(aspeed_udma_set_rx_rptr); + -+static int aspeed_udma_free_chan(u32 ch_no, bool is_tx) ++static void otp_write(struct aspeed_otp *ctx, u32 otp_addr, u32 val) +{ -+ u32 reg; -+ unsigned long flags; -+ -+ if (ch_no > UDMA_MAX_CHANNEL) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&udma->lock, flags); -+ -+ reg = readl(udma->regs + -+ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); -+ reg &= ~(0x1 << ch_no); -+ -+ writel(reg, udma->regs + -+ ((is_tx) ? UDMA_TX_DMA_INT_EN : UDMA_RX_DMA_INT_EN)); -+ -+ spin_unlock_irqrestore(&udma->lock, flags); -+ -+ return 0; ++ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address ++ aspeed_otp_write(ctx, val, OTP_COMPARE_1); //write val ++ aspeed_otp_write(ctx, 0x23b1e362, OTP_COMMAND); //write command ++ wait_complete(ctx); +} + -+int aspeed_udma_free_tx_chan(u32 ch_no) ++static void otp_soak(struct aspeed_otp *ctx, int soak) +{ -+ return aspeed_udma_free_chan(ch_no, true); -+} -+EXPORT_SYMBOL(aspeed_udma_free_tx_chan); ++ if (ctx->otp_ver == OTP_A2 || ctx->otp_ver == OTP_A3) { ++ switch (soak) { ++ case 0: //default ++ otp_write(ctx, 0x3000, 0x0); // Write MRA ++ otp_write(ctx, 0x5000, 0x0); // Write MRB ++ otp_write(ctx, 0x1000, 0x0); // Write MR ++ break; ++ case 1: //normal program ++ otp_write(ctx, 0x3000, 0x1320); // Write MRA ++ otp_write(ctx, 0x5000, 0x1008); // Write MRB ++ otp_write(ctx, 0x1000, 0x0024); // Write MR ++ aspeed_otp_write(ctx, 0x04191388, OTP_TIMING); // 200us ++ break; ++ case 2: //soak program ++ otp_write(ctx, 0x3000, 0x1320); // Write MRA ++ otp_write(ctx, 0x5000, 0x0007); // Write MRB ++ otp_write(ctx, 0x1000, 0x0100); // Write MR ++ aspeed_otp_write(ctx, 0x04193a98, OTP_TIMING); // 600us ++ break; ++ } ++ } else { ++ switch (soak) { ++ case 0: //default ++ otp_write(ctx, 0x3000, 0x0); // Write MRA ++ otp_write(ctx, 0x5000, 0x0); // Write MRB ++ otp_write(ctx, 0x1000, 0x0); // Write MR ++ break; ++ case 1: //normal program ++ otp_write(ctx, 0x3000, 0x4021); // Write MRA ++ otp_write(ctx, 0x5000, 0x302f); // Write MRB ++ otp_write(ctx, 0x1000, 0x4020); // Write MR ++ aspeed_otp_write(ctx, 0x04190760, OTP_TIMING); // 75us ++ break; ++ case 2: //soak program ++ otp_write(ctx, 0x3000, 0x4021); // Write MRA ++ otp_write(ctx, 0x5000, 0x1027); // Write MRB ++ otp_write(ctx, 0x1000, 0x4820); // Write MR ++ aspeed_otp_write(ctx, 0x041930d4, OTP_TIMING); // 500us ++ break; ++ } ++ } + -+int aspeed_udma_free_rx_chan(u32 ch_no) -+{ -+ return aspeed_udma_free_chan(ch_no, false); ++ wait_complete(ctx); +} -+EXPORT_SYMBOL(aspeed_udma_free_rx_chan); + -+static int aspeed_udma_request_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, -+ aspeed_udma_cb_t cb, void *id, -+ bool dis_tmout, bool is_tx) ++static int verify_bit(struct aspeed_otp *ctx, u32 otp_addr, int bit_offset, int value) +{ -+ int retval = 0; -+ int rbsz_code; -+ -+ u32 reg; -+ unsigned long flags; -+ struct aspeed_udma_chan *ch; -+ -+ if (ch_no > UDMA_MAX_CHANNEL) { -+ retval = -EINVAL; -+ goto out; -+ } -+ -+ rbsz_code = aspeed_udma_get_bufsz_code(rb_sz); -+ if (rbsz_code < 0) { -+ retval = -EINVAL; -+ goto out; -+ } -+ -+ spin_lock_irqsave(&udma->lock, flags); -+ -+ if (is_tx) { -+ reg = readl(udma->regs + UDMA_TX_DMA_INT_EN); -+ if (reg & (0x1 << ch_no)) { -+ retval = -EBUSY; -+ goto unlock_n_out; -+ } ++ u32 ret[2]; + -+ reg |= (0x1 << ch_no); -+ writel(reg, udma->regs + UDMA_TX_DMA_INT_EN); ++ if (otp_addr % 2 == 0) ++ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //Read address ++ else ++ aspeed_otp_write(ctx, otp_addr - 1, OTP_ADDR); //Read address + -+ reg = FIELD_PREP(UDMA_TX_CTRL_BUF_ADDRH, (u64)addr >> 32) | -+ ((dis_tmout) ? UDMA_TX_CTRL_TMOUT_DIS : 0) | -+ FIELD_PREP(UDMA_TX_CTRL_BUFSZ, rbsz_code); -+ writel(reg, udma->regs + UDMA_CHX_TX_CTRL(ch_no)); ++ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read ++ wait_complete(ctx); ++ ret[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); ++ ret[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); + -+ writel(addr, udma->regs + UDMA_CHX_TX_BUF_ADDR(ch_no)); ++ if (otp_addr % 2 == 0) { ++ if (((ret[0] >> bit_offset) & 1) == value) ++ return 0; ++ else ++ return -1; + } else { -+ reg = readl(udma->regs + UDMA_RX_DMA_INT_EN); -+ if (reg & (0x1 << ch_no)) { -+ retval = -EBUSY; -+ goto unlock_n_out; -+ } -+ -+ reg |= (0x1 << ch_no); -+ writel(reg, udma->regs + UDMA_RX_DMA_INT_EN); -+ -+ reg = FIELD_PREP(UDMA_RX_CTRL_BUF_ADDRH, (u64)addr >> 32) | -+ ((dis_tmout) ? UDMA_RX_CTRL_TMOUT_DIS : 0) | -+ FIELD_PREP(UDMA_RX_CTRL_BUFSZ, rbsz_code); -+ writel(reg, udma->regs + UDMA_CHX_RX_CTRL(ch_no)); -+ -+ writel(addr, udma->regs + UDMA_CHX_RX_BUF_ADDR(ch_no)); ++ if (((ret[1] >> bit_offset) & 1) == value) ++ return 0; ++ else ++ return -1; + } -+ -+ ch = (is_tx) ? &udma->tx_chs[ch_no] : &udma->rx_chs[ch_no]; -+ ch->rb_sz = rb_sz; -+ ch->cb = cb; -+ ch->cb_arg = id; -+ ch->dma_addr = addr; -+ ch->dis_tmout = dis_tmout; -+ -+unlock_n_out: -+ spin_unlock_irqrestore(&udma->lock, flags); -+out: -+ return 0; +} + -+int aspeed_udma_request_tx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, -+ aspeed_udma_cb_t cb, void *id, bool dis_tmout) ++static void otp_prog(struct aspeed_otp *ctx, u32 otp_addr, u32 prog_bit) +{ -+ return aspeed_udma_request_chan(ch_no, addr, rb_sz, cb, id, -+ dis_tmout, true); ++ otp_write(ctx, 0x0, prog_bit); ++ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address ++ aspeed_otp_write(ctx, prog_bit, OTP_COMPARE_1); //write data ++ aspeed_otp_write(ctx, 0x23b1e364, OTP_COMMAND); //write command ++ wait_complete(ctx); +} -+EXPORT_SYMBOL(aspeed_udma_request_tx_chan); + -+int aspeed_udma_request_rx_chan(u32 ch_no, dma_addr_t addr, u32 rb_sz, -+ aspeed_udma_cb_t cb, void *id, bool dis_tmout) ++static void _otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) +{ -+ return aspeed_udma_request_chan(ch_no, addr, rb_sz, cb, id, -+ dis_tmout, false); ++ int prog_bit; ++ ++ if (prog_address % 2 == 0) { ++ if (value) ++ prog_bit = ~(0x1 << bit_offset); ++ else ++ return; ++ } else { ++ if (ctx->otp_ver != OTP_A3) ++ prog_address |= 1 << 15; ++ if (!value) ++ prog_bit = 0x1 << bit_offset; ++ else ++ return; ++ } ++ otp_prog(ctx, prog_address, prog_bit); +} -+EXPORT_SYMBOL(aspeed_udma_request_rx_chan); + -+static void aspeed_udma_chan_ctrl(u32 ch_no, u32 op, bool is_tx) ++static int otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) +{ -+ unsigned long flags; -+ u32 reg_en, reg_rst; -+ u32 reg_en_off = (is_tx) ? UDMA_TX_DMA_EN : UDMA_RX_DMA_EN; -+ u32 reg_rst_off = (is_tx) ? UDMA_TX_DMA_RST : UDMA_TX_DMA_RST; -+ -+ if (ch_no > UDMA_MAX_CHANNEL) -+ return; ++ int pass; ++ int i; + -+ spin_lock_irqsave(&udma->lock, flags); ++ otp_soak(ctx, 1); ++ _otp_prog_bit(ctx, value, prog_address, bit_offset); ++ pass = 0; + -+ reg_en = readl(udma->regs + reg_en_off); -+ reg_rst = readl(udma->regs + reg_rst_off); ++ for (i = 0; i < RETRY; i++) { ++ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { ++ otp_soak(ctx, 2); ++ _otp_prog_bit(ctx, value, prog_address, bit_offset); ++ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { ++ otp_soak(ctx, 1); ++ } else { ++ pass = 1; ++ break; ++ } ++ } else { ++ pass = 1; ++ break; ++ } ++ } ++ otp_soak(ctx, 0); ++ return pass; ++} + -+ switch (op) { -+ case ASPEED_UDMA_OP_ENABLE: -+ reg_en |= (0x1 << ch_no); -+ writel(reg_en, udma->regs + reg_en_off); -+ break; -+ case ASPEED_UDMA_OP_DISABLE: -+ reg_en &= ~(0x1 << ch_no); -+ writel(reg_en, udma->regs + reg_en_off); -+ break; -+ case ASPEED_UDMA_OP_RESET: -+ reg_en &= ~(0x1 << ch_no); -+ writel(reg_en, udma->regs + reg_en_off); ++static void otp_read_conf_dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) ++{ ++ u32 config_offset; + -+ reg_rst |= (0x1 << ch_no); -+ writel(reg_rst, udma->regs + reg_rst_off); ++ config_offset = 0x800; ++ config_offset |= (offset / 8) * 0x200; ++ config_offset |= (offset % 8) * 0x2; + -+ udelay(100); ++ aspeed_otp_write(ctx, config_offset, OTP_ADDR); //Read address ++ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read ++ wait_complete(ctx); ++ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); ++} + -+ reg_rst &= ~(0x1 << ch_no); -+ writel(reg_rst, udma->regs + reg_rst_off); -+ break; -+ default: -+ break; -+ } ++static void otp_read_conf(struct aspeed_otp *ctx, u32 offset, u32 len) ++{ ++ int i, j; + -+ spin_unlock_irqrestore(&udma->lock, flags); ++ otp_soak(ctx, 0); ++ for (i = offset, j = 0; j < len; i++, j++) ++ otp_read_conf_dw(ctx, i, &ctx->data[j]); +} + -+void aspeed_udma_tx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++static void otp_read_data_2dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) +{ -+ aspeed_udma_chan_ctrl(ch_no, op, true); ++ aspeed_otp_write(ctx, offset, OTP_ADDR); //Read address ++ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read ++ wait_complete(ctx); ++ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); ++ buf[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); +} -+EXPORT_SYMBOL(aspeed_udma_tx_chan_ctrl); + -+void aspeed_udma_rx_chan_ctrl(u32 ch_no, enum aspeed_udma_ops op) ++static void otp_read_data(struct aspeed_otp *ctx, u32 offset, u32 len) +{ -+ aspeed_udma_chan_ctrl(ch_no, op, false); ++ int i, j; ++ u32 ret[2]; ++ ++ otp_soak(ctx, 0); ++ ++ i = offset; ++ j = 0; ++ if (offset % 2) { ++ otp_read_data_2dw(ctx, i - 1, ret); ++ ctx->data[0] = ret[1]; ++ i++; ++ j++; ++ } ++ for (; j < len; i += 2, j += 2) ++ otp_read_data_2dw(ctx, i, &ctx->data[j]); +} -+EXPORT_SYMBOL(aspeed_udma_rx_chan_ctrl); + -+static irqreturn_t aspeed_udma_isr(int irq, void *arg) ++static int otp_prog_data(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) +{ -+ u32 bit; -+ unsigned long tx_sts = readl(udma->regs + UDMA_TX_DMA_INT_STS); -+ unsigned long rx_sts = readl(udma->regs + UDMA_RX_DMA_INT_STS); ++ u32 read[2]; ++ int otp_bit; + -+ if (udma != (struct aspeed_udma *)arg) -+ return IRQ_NONE; ++ if (dw_offset % 2 == 0) { ++ otp_read_data_2dw(ctx, dw_offset, read); ++ otp_bit = (read[0] >> bit_offset) & 0x1; + -+ if (tx_sts == 0 && rx_sts == 0) -+ return IRQ_NONE; ++ if (otp_bit == 1 && value == 0) { ++ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); ++ pr_err("OTP is programed, which can't be cleaned\n"); ++ return -EINVAL; ++ } ++ } else { ++ otp_read_data_2dw(ctx, dw_offset - 1, read); ++ otp_bit = (read[1] >> bit_offset) & 0x1; + -+ for_each_set_bit(bit, &tx_sts, UDMA_MAX_CHANNEL) { -+ writel((0x1 << bit), udma->regs + UDMA_TX_DMA_INT_STS); -+ if (udma->tx_chs[bit].cb) -+ udma->tx_chs[bit].cb(aspeed_udma_get_tx_rptr(bit), -+ udma->tx_chs[bit].cb_arg); ++ if (otp_bit == 0 && value == 1) { ++ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); ++ pr_err("OTP is programed, which can't be writen\n"); ++ return -EINVAL; ++ } + } -+ -+ for_each_set_bit(bit, &rx_sts, UDMA_MAX_CHANNEL) { -+ writel((0x1 << bit), udma->regs + UDMA_RX_DMA_INT_STS); -+ if (udma->rx_chs[bit].cb) -+ udma->rx_chs[bit].cb(aspeed_udma_get_rx_wptr(bit), -+ udma->rx_chs[bit].cb_arg); ++ if (otp_bit == value) { ++ pr_err("OTPDATA%X[%X] = %d\n", dw_offset, bit_offset, value); ++ pr_err("No need to program\n"); ++ return 0; + } + -+ return IRQ_HANDLED; ++ return otp_prog_bit(ctx, value, dw_offset, bit_offset); +} + -+static int aspeed_udma_probe(struct platform_device *pdev) ++static int otp_prog_conf(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) +{ -+ int i, rc; -+ uint32_t reg; -+ struct resource *res; -+ struct device *dev = &pdev->dev; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (IS_ERR_OR_NULL(res)) { -+ dev_err(dev, "failed to get register base\n"); -+ return -ENODEV; -+ } -+ -+ udma->regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR_OR_NULL(udma->regs)) { -+ dev_err(dev, "failed to map registers\n"); -+ return PTR_ERR(udma->regs); -+ } -+ -+ /* disable for safety */ -+ writel(0x0, udma->regs + UDMA_TX_DMA_EN); -+ writel(0x0, udma->regs + UDMA_RX_DMA_EN); ++ u32 read; ++ u32 prog_address = 0; ++ int otp_bit; + -+ udma->irq = platform_get_irq(pdev, 0); -+ if (udma->irq < 0) { -+ dev_err(dev, "failed to get IRQ number\n"); -+ return -ENODEV; -+ } ++ otp_read_conf_dw(ctx, dw_offset, &read); + -+ rc = devm_request_irq(dev, udma->irq, aspeed_udma_isr, -+ IRQF_SHARED, DEVICE_NAME, udma); -+ if (rc) { -+ dev_err(dev, "failed to request IRQ handler\n"); -+ return rc; ++ prog_address = 0x800; ++ prog_address |= (dw_offset / 8) * 0x200; ++ prog_address |= (dw_offset % 8) * 0x2; ++ otp_bit = (read >> bit_offset) & 0x1; ++ if (otp_bit == value) { ++ pr_err("OTPCFG%X[%X] = %d\n", dw_offset, bit_offset, value); ++ pr_err("No need to program\n"); ++ return 0; + } -+ -+ /* -+ * For legacy design. -+ * - TX ringbuffer size: 4KB -+ * - RX ringbuffer size: 64KB -+ * - Timeout timer disabled -+ */ -+ reg = FIELD_PREP(UDMA_MISC_TX_BUFSZ, UDMA_BUFSZ_CODE_4KB) | -+ FIELD_PREP(UDMA_MISC_RX_BUFSZ, UDMA_BUFSZ_CODE_64KB); -+ writel(reg, udma->regs + UDMA_MISC); -+ -+ for (i = 0; i < UDMA_MAX_CHANNEL; ++i) { -+ writel(0, udma->regs + UDMA_CHX_TX_WR_PTR(i)); -+ writel(0, udma->regs + UDMA_CHX_RX_RD_PTR(i)); ++ if (otp_bit == 1 && value == 0) { ++ pr_err("OTPCFG%X[%X] = 1\n", dw_offset, bit_offset); ++ pr_err("OTP is programed, which can't be clean\n"); ++ return -EINVAL; + } + -+ writel(0xffffffff, udma->regs + UDMA_TX_DMA_RST); -+ writel(0x0, udma->regs + UDMA_TX_DMA_RST); -+ -+ writel(0xffffffff, udma->regs + UDMA_RX_DMA_RST); -+ writel(0x0, udma->regs + UDMA_RX_DMA_RST); ++ return otp_prog_bit(ctx, value, prog_address, bit_offset); ++} + -+ writel(0x0, udma->regs + UDMA_TX_DMA_INT_EN); -+ writel(0xffffffff, udma->regs + UDMA_TX_DMA_INT_STS); -+ writel(0x0, udma->regs + UDMA_RX_DMA_INT_EN); -+ writel(0xffffffff, udma->regs + UDMA_RX_DMA_INT_STS); ++struct aspeed_otp *glob_ctx; + -+ writel(UDMA_TMOUT, udma->regs + UDMA_TMOUT_TIMER); ++void otp_read_data_buf(u32 offset, u32 *buf, u32 len) ++{ ++ int i, j; ++ u32 ret[2]; + -+ spin_lock_init(&udma->lock); ++ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); + -+ dev_set_drvdata(dev, udma); ++ otp_soak(glob_ctx, 0); + -+ return 0; ++ i = offset; ++ j = 0; ++ if (offset % 2) { ++ otp_read_data_2dw(glob_ctx, i - 1, ret); ++ buf[0] = ret[1]; ++ i++; ++ j++; ++ } ++ for (; j < len; i += 2, j += 2) ++ otp_read_data_2dw(glob_ctx, i, &buf[j]); ++ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); +} ++EXPORT_SYMBOL(otp_read_data_buf); + -+static const struct of_device_id aspeed_udma_match[] = { -+ { .compatible = "aspeed,ast2500-udma" }, -+ { .compatible = "aspeed,ast2600-udma" }, -+ { .compatible = "aspeed,ast2700-udma" }, -+ { }, -+}; -+ -+static struct platform_driver aspeed_udma_driver = { -+ .driver = { -+ .name = DEVICE_NAME, -+ .of_match_table = aspeed_udma_match, -+ -+ }, -+ .probe = aspeed_udma_probe, -+}; -+ -+module_platform_driver(aspeed_udma_driver); ++void otp_read_conf_buf(u32 offset, u32 *buf, u32 len) ++{ ++ int i, j; + -+MODULE_AUTHOR("Chia-Wei Wang "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Aspeed UDMA Engine Driver"); -diff --git a/drivers/soc/aspeed/aspeed-usb-hp.c b/drivers/soc/aspeed/aspeed-usb-hp.c ---- a/drivers/soc/aspeed/aspeed-usb-hp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-usb-hp.c 2026-04-08 18:03:48.311705302 +0000 -@@ -0,0 +1,152 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2021 Aspeed Technology Inc. -+ */ ++ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ otp_soak(glob_ctx, 0); ++ for (i = offset, j = 0; j < len; i++, j++) ++ otp_read_conf_dw(glob_ctx, i, &buf[j]); ++ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); ++} ++EXPORT_SYMBOL(otp_read_conf_buf); + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++static long otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ void __user *argp = (void __user *)arg; ++ struct otp_read xfer; ++ struct otp_prog prog; ++ u32 reg_read[2]; ++ int ret = 0; + -+#define USB_HP_BEHCI84 0x84 /* Controller Fine-tune Register */ ++ switch (cmd) { ++ case ASPEED_OTP_READ_DATA: ++ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) ++ return -EFAULT; ++ if ((xfer.offset + xfer.len) > 0x800) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } + -+static const struct of_device_id aspeed_usb_hp_dt_ids[] = { -+ { -+ .compatible = "aspeed,ast2600-usb2ahp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb3ahp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb3bhp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb2ahp", -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb2bhp", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, aspeed_usb_hp_dt_ids); ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ otp_read_data(ctx, xfer.offset, xfer.len); ++ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); + -+static int aspeed_usb_hp_probe(struct platform_device *pdev) -+{ -+ struct device_node *node = pdev->dev.of_node; -+ void __iomem *regs; -+ bool ehci_32bits_quirk; -+ u32 val; -+ struct clk *clk; -+ struct reset_control *rst; -+ struct regmap *device; -+ struct phy *usb3_phy; -+ bool is_pcie_xhci; -+ int rc = 0; ++ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) ++ return -EFAULT; ++ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) ++ return -EFAULT; ++ break; ++ case ASPEED_OTP_READ_CONF: ++ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) ++ return -EFAULT; ++ if ((xfer.offset + xfer.len) > 0x800) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2600-usb2ahp")) { -+ dev_info(&pdev->dev, "Initialized AST2600 USB2AHP\n"); -+ return 0; -+ } ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ otp_read_conf(ctx, xfer.offset, xfer.len); ++ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb3ahp") || -+ of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb3bhp")) { -+ is_pcie_xhci = true; -+ } else if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb2ahp") || -+ of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-usb2bhp")) { -+ is_pcie_xhci = false; ++ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) ++ return -EFAULT; ++ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) ++ return -EFAULT; ++ break; ++ case ASPEED_OTP_PROG_DATA: ++ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) ++ return -EFAULT; ++ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ if (prog.dw_offset >= 0x800) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ ret = otp_prog_data(ctx, prog.value, prog.dw_offset, prog.bit_offset); ++ break; ++ case ASPEED_OTP_PROG_CONF: ++ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) ++ return -EFAULT; ++ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ if (prog.dw_offset >= 0x20) { ++ pr_err("out of range"); ++ return -EINVAL; ++ } ++ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ ret = otp_prog_conf(ctx, prog.value, prog.dw_offset, prog.bit_offset); ++ break; ++ case ASPEED_OTP_VER: ++ if (copy_to_user(argp, &ctx->otp_ver, sizeof(u32))) ++ return -EFAULT; ++ break; ++ case ASPEED_OTP_SW_RID: ++ reg_read[0] = aspeed_otp_read(ctx, SW_REV_ID0); ++ reg_read[1] = aspeed_otp_read(ctx, SW_REV_ID1); ++ if (copy_to_user(argp, reg_read, sizeof(u32) * 2)) ++ return -EFAULT; ++ break; ++ case ASPEED_SEC_KEY_NUM: ++ reg_read[0] = aspeed_otp_read(ctx, SEC_KEY_NUM) & 7; ++ if (copy_to_user(argp, reg_read, sizeof(u32))) ++ return -EFAULT; ++ break; + } -+ clk = devm_clk_get(&pdev->dev, NULL); -+ if (IS_ERR(clk)) -+ return PTR_ERR(clk); ++ return ret; ++} + -+ rc = clk_prepare_enable(clk); -+ if (rc) { -+ dev_err(&pdev->dev, "Unable to enable clock (%d)\n", rc); -+ return rc; -+ } ++static int otp_open(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+ rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); -+ if (IS_ERR(rst)) { -+ rc = PTR_ERR(rst); -+ goto err; -+ } -+ rc = reset_control_deassert(rst); -+ if (rc) -+ goto err; ++ spin_lock(&otp_state_lock); + -+ device = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,device"); -+ if (IS_ERR(device)) { -+ dev_err(&pdev->dev, "failed to find device regmap\n"); -+ goto err; ++ if (ctx->is_open) { ++ spin_unlock(&otp_state_lock); ++ return -EBUSY; + } + -+ if (is_pcie_xhci) { -+ usb3_phy = devm_phy_get(&pdev->dev, "usb3-phy"); -+ if (IS_ERR(usb3_phy)) { -+ rc = dev_err_probe(&pdev->dev, PTR_ERR(usb3_phy), -+ "failed to get usb3 phy\n"); -+ goto err; -+ } -+ rc = phy_init(usb3_phy); -+ if (rc < 0) { -+ dev_err(&pdev->dev, "failed to init usb3 phy\n"); -+ goto err; -+ } -+ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev -+ /* Turn on PCIe xHCI without MSI */ -+ regmap_update_bits(device, 0x70, -+ BIT(19) | BIT(11) | BIT(3), -+ BIT(19) | BIT(11) | BIT(3)); -+ } else { -+ ehci_32bits_quirk = -+ device_property_read_bool(&pdev->dev, "aspeed,ehci_32bits_quirk"); ++ ctx->is_open = true; + -+ if (ehci_32bits_quirk) { -+ regs = of_iomap(node, 0); -+ val = readl(regs + USB_HP_BEHCI84) & ~BIT(11); -+ writel(val, regs + USB_HP_BEHCI84); -+ } ++ spin_unlock(&otp_state_lock); + -+ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev -+ /* Turn on PCIe EHCI without MSI */ -+ regmap_update_bits(device, 0x70, -+ BIT(18) | BIT(10) | BIT(2), -+ BIT(18) | BIT(10) | BIT(2)); -+ } -+ dev_info(&pdev->dev, "Initialized AST2700 USB Host PCIe\n"); + return 0; -+err: -+ if (clk) -+ clk_disable_unprepare(clk); -+ return rc; +} + -+static void aspeed_usb_hp_remove(struct platform_device *pdev) ++static int otp_release(struct inode *inode, struct file *file) +{ -+ dev_info(&pdev->dev, "Remove USB Host PCIe\n"); -+} ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+static struct platform_driver aspeed_usb_hp_driver = { -+ .probe = aspeed_usb_hp_probe, -+ .remove = aspeed_usb_hp_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_usb_hp_dt_ids, -+ }, -+}; -+module_platform_driver(aspeed_usb_hp_driver); ++ spin_lock(&otp_state_lock); + -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Neal Liu "); -diff --git a/drivers/soc/aspeed/aspeed-usb-phy.c b/drivers/soc/aspeed/aspeed-usb-phy.c ---- a/drivers/soc/aspeed/aspeed-usb-phy.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-usb-phy.c 2026-04-08 18:03:48.311705302 +0000 -@@ -0,0 +1,112 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2021 Aspeed Technology Inc. -+ */ ++ ctx->is_open = false; + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ spin_unlock(&otp_state_lock); + -+struct usb_phy_ctrl { -+ u32 offset; -+ u32 value; -+}; ++ return 0; ++} + -+static const struct of_device_id aspeed_usb_phy_dt_ids[] = { -+ { -+ .compatible = "aspeed,ast2600-uphyb", -+ }, -+ { -+ .compatible = "aspeed,ast2700-uphy2a", -+ }, -+ { -+ .compatible = "aspeed,ast2700-uphy2b", -+ }, ++static const struct file_operations otp_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = otp_ioctl, ++ .open = otp_open, ++ .release = otp_release, ++}; ++ ++static const struct of_device_id aspeed_otp_of_matches[] = { ++ { .compatible = "aspeed,ast2600-sbc" }, + { } +}; -+MODULE_DEVICE_TABLE(of, aspeed_usb_phy_dt_ids); ++MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); + -+static int aspeed_usb_phy_probe(struct platform_device *pdev) ++static int aspeed_otp_probe(struct platform_device *pdev) +{ -+ struct device_node *node = pdev->dev.of_node; -+ struct usb_phy_ctrl *ctrl_data; -+ void __iomem *base; ++ struct device *dev = &pdev->dev; + struct regmap *scu; -+ int ctrl_num = 1; -+ int ret, i; -+ u32 val; ++ struct aspeed_otp *priv; ++ struct resource *res; ++ u32 revid0, revid1; ++ int rc; + -+ scu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(&pdev->dev, "cannot to find SCU regmap\n"); -+ return -ENODEV; -+ } ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ glob_ctx = priv; ++ if (!priv) ++ return -ENOMEM; + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2600-uphyb")) { -+ /* Check SCU040[3] USB port B controller reset is deassert */ -+ regmap_read(scu, 0x40, &val); -+ if ((val & BIT(3))) -+ return -EPROBE_DEFER; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); ++ return -ENOENT; + } + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-uphy2a")) { -+ /* Check SCU220[0] USB vHubA1 controller reset is deassert */ -+ regmap_read(scu, 0x220, &val); -+ if ((val & BIT(0))) -+ return -EPROBE_DEFER; -+ } ++ priv->reg_base = devm_ioremap_resource(&pdev->dev, res); ++ if (!priv->reg_base) ++ return -EIO; + -+ if (of_device_is_compatible(pdev->dev.of_node, -+ "aspeed,ast2700-uphy2b")) { -+ /* Check SCU220[3] USB vHubB1 controller reset is deassert */ -+ regmap_read(scu, 0x220, &val); -+ if ((val & BIT(3))) -+ return -EPROBE_DEFER; ++ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); ++ if (IS_ERR(scu)) { ++ dev_err(dev, "failed to find 2600 SCU regmap\n"); ++ return PTR_ERR(scu); + } + -+ ctrl_data = devm_kzalloc(&pdev->dev, -+ sizeof(struct usb_phy_ctrl) * ctrl_num, -+ GFP_KERNEL); -+ if (!ctrl_data) -+ return -ENOMEM; ++ regmap_read(scu, ASPEED_REVISION_ID0, &revid0); ++ regmap_read(scu, ASPEED_REVISION_ID1, &revid1); + -+ base = of_iomap(node, 0); ++ priv->otp_ver = chip_version(revid0, revid1); + -+ ret = of_property_read_u32_array(node, "ctrl", (u32 *)ctrl_data, -+ ctrl_num * 2); -+ if (ret < 0) { -+ dev_err(&pdev->dev, "Could not read ctrl property\n"); ++ if (priv->otp_ver == -1) { ++ dev_err(dev, "invalid SCU\n"); + return -EINVAL; + } + -+ for (i = 0; i < ctrl_num; i++) -+ writel(ctrl_data[i].value, base + ctrl_data[i].offset); ++ priv->data = kmalloc(8192, GFP_KERNEL); ++ if (!priv->data) ++ return -ENOMEM; + -+ dev_info(&pdev->dev, "Initialized USB PHY\n"); ++ dev_set_drvdata(dev, priv); ++ ++ /* Set up the miscdevice */ ++ priv->miscdev.minor = MISC_DYNAMIC_MINOR; ++ priv->miscdev.name = "aspeed-otp"; ++ priv->miscdev.fops = &otp_fops; ++ ++ /* Register the device */ ++ rc = misc_register(&priv->miscdev); ++ if (rc) { ++ dev_err(dev, "Unable to register device\n"); ++ return rc; ++ } + + return 0; +} + -+static void aspeed_usb_phy_remove(struct platform_device *pdev) ++static void aspeed_otp_remove(struct platform_device *pdev) +{ ++ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); ++ ++ kfree(ctx->data); ++ misc_deregister(&ctx->miscdev); +} + -+static struct platform_driver aspeed_usb_phy_driver = { -+ .probe = aspeed_usb_phy_probe, -+ .remove = aspeed_usb_phy_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_usb_phy_dt_ids, ++static struct platform_driver aspeed_otp_driver = { ++ .probe = aspeed_otp_probe, ++ .remove = aspeed_otp_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_otp_of_matches, + }, +}; -+module_platform_driver(aspeed_usb_phy_driver); + -+MODULE_LICENSE("GPL"); ++module_platform_driver(aspeed_otp_driver); ++ +MODULE_AUTHOR("Neal Liu "); -diff --git a/drivers/soc/aspeed/aspeed-xdma.c b/drivers/soc/aspeed/aspeed-xdma.c ---- a/drivers/soc/aspeed/aspeed-xdma.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/aspeed-xdma.c 2026-04-08 18:03:48.311705302 +0000 -@@ -0,0 +1,1438 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+// Copyright IBM Corp 2019 ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ASPEED OTP Driver"); +diff --git a/drivers/soc/aspeed/ast2700-otp.c b/drivers/soc/aspeed/ast2700-otp.c +--- a/drivers/soc/aspeed/ast2700-otp.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/ast2700-otp.c 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,565 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2024 Aspeed Technology Inc. ++ */ + -+#include -+#include -+#include -+#include -+#include -+#include ++#include +#include -+#include -+#include -+#include -+#include -+#include +#include -+#include -+#include -+#include -+#include ++#include +#include -+#include +#include -+#include -+#include +#include -+#include +#include -+#include -+#include ++#include ++#include ++#include ++#include ++#include + -+#define DEVICE_NAME "aspeed-xdma" ++static DEFINE_SPINLOCK(otp_state_lock); + -+#define SCU_AST2600_MISC_CTRL 0x0c0 -+#define SCU_AST2600_MISC_CTRL_XDMA_BMC BIT(8) -+#define SCU_AST2700_MISC_CTRL_XDMA_CLIENT BIT(4) ++/*********************** ++ * * ++ * OTP regs definition * ++ * * ++ ***********************/ ++#define OTP_REG_SIZE 0x200 + -+#define SCU_AST2600_DEBUG_CTRL 0x0c8 -+#define DEBUG_CTRL_AST2600_XDMA_DISABLE BIT(2) -+#define DEBUG_CTRL_AST2700_XDMA_DISABLE BIT(8) ++#define OTP_PASSWD 0x349fe38a ++#define OTP_CMD_READ 0x23b1e361 ++#define OTP_CMD_PROG 0x23b1e364 ++#define OTP_CMD_PROG_MULTI 0x23b1e365 ++#define OTP_CMD_CMP 0x23b1e363 ++#define OTP_CMD_BIST 0x23b1e368 + -+#define SCU_AST2500_PCIE_CONF 0x180 -+#define SCU_AST2600_PCIE_CONF 0xc20 -+#define SCU_AST2700_PCIE0_CONF 0x970 -+#define SCU_AST2700_PCIE1_CONF 0x9B0 -+#define SCU_PCIE_CONF_VGA_EN BIT(0) -+#define SCU_PCIE_CONF_VGA_EN_MMIO BIT(1) -+#define SCU_PCIE_CONF_VGA_EN_LPC BIT(2) -+#define SCU_PCIE_CONF_VGA_EN_MSI BIT(3) -+#define SCU_PCIE_CONF_VGA_EN_MCTP BIT(4) -+#define SCU_PCIE_CONF_VGA_EN_IRQ BIT(5) -+#define SCU_PCIE_CONF_VGA_EN_DMA BIT(6) -+#define SCU_PCIE_CONF_BMC_EN BIT(8) -+#define SCU_PCIE_CONF_BMC_EN_MMIO BIT(9) -+#define SCU_PCIE_CONF_BMC_EN_MSI BIT(11) -+#define SCU_PCIE_CONF_BMC_EN_MCTP BIT(12) -+#define SCU_PCIE_CONF_BMC_EN_IRQ BIT(13) -+#define SCU_PCIE_CONF_BMC_EN_DMA BIT(14) ++#define OTP_CMD_OFFSET 0x20 ++#define OTP_MASTER OTP_M0 + -+#define SCU_AST2700_PCIE0_CTRL 0xa60 -+#define SCU_AST2700_PCIE1_CTRL 0xae0 -+#define SCU_AST2700_PCIE_CTRL_DMA_EN BIT(2) ++#define OTP_KEY 0x0 ++#define OTP_CMD (OTP_MASTER * OTP_CMD_OFFSET + 0x4) ++#define OTP_WDATA_0 (OTP_MASTER * OTP_CMD_OFFSET + 0x8) ++#define OTP_WDATA_1 (OTP_MASTER * OTP_CMD_OFFSET + 0xc) ++#define OTP_WDATA_2 (OTP_MASTER * OTP_CMD_OFFSET + 0x10) ++#define OTP_WDATA_3 (OTP_MASTER * OTP_CMD_OFFSET + 0x14) ++#define OTP_STATUS (OTP_MASTER * OTP_CMD_OFFSET + 0x18) ++#define OTP_ADDR (OTP_MASTER * OTP_CMD_OFFSET + 0x1c) ++#define OTP_RDATA (OTP_MASTER * OTP_CMD_OFFSET + 0x20) + -+#define SCU_AST2500_BMC_CLASS_REV 0x19c -+#define SCU_AST2600_BMC_CLASS_REV 0xc68 -+#define SCU_AST2700_PCIE0_BMC_CLASS_REV 0xa18 -+#define SCU_AST2700_PCIE1_BMC_CLASS_REV 0xa98 -+#define SCU_BMC_CLASS_REV_XDMA 0xff000001 -+#define SCU_BMC_CLASS_REV_MASK 0xffffff00 ++#define OTP_DBG00 0x0C4 ++#define OTP_DBG01 0x0C8 ++#define OTP_MASTER_PID 0x0D0 ++#define OTP_ECC_EN 0x0D4 ++#define OTP_CMD_LOCK 0x0D8 ++#define OTP_SW_RST 0x0DC ++#define OTP_SLV_ID 0x0E0 ++#define OTP_PMC_CQ 0x0E4 ++#define OTP_FPGA 0x0EC ++#define OTP_CLR_FPGA 0x0F0 ++#define OTP_REGION_ROM_PATCH 0x100 ++#define OTP_REGION_OTPCFG 0x104 ++#define OTP_REGION_OTPSTRAP 0x108 ++#define OTP_REGION_OTPSTRAP_EXT 0x10C ++#define OTP_REGION_SECURE0 0x120 ++#define OTP_REGION_SECURE0_RANGE 0x124 ++#define OTP_REGION_SECURE1 0x128 ++#define OTP_REGION_SECURE1_RANGE 0x12C ++#define OTP_REGION_SECURE2 0x130 ++#define OTP_REGION_SECURE2_RANGE 0x134 ++#define OTP_REGION_SECURE3 0x138 ++#define OTP_REGION_SECURE3_RANGE 0x13C ++#define OTP_REGION_USR0 0x140 ++#define OTP_REGION_USR0_RANGE 0x144 ++#define OTP_REGION_USR1 0x148 ++#define OTP_REGION_USR1_RANGE 0x14C ++#define OTP_REGION_USR2 0x150 ++#define OTP_REGION_USR2_RANGE 0x154 ++#define OTP_REGION_USR3 0x158 ++#define OTP_REGION_USR3_RANGE 0x15C ++#define OTP_REGION_CALIPTRA_0 0x160 ++#define OTP_REGION_CALIPTRA_0_RANGE 0x164 ++#define OTP_REGION_CALIPTRA_1 0x168 ++#define OTP_REGION_CALIPTRA_1_RANGE 0x16C ++#define OTP_REGION_CALIPTRA_2 0x170 ++#define OTP_REGION_CALIPTRA_2_RANGE 0x174 ++#define OTP_REGION_CALIPTRA_3 0x178 ++#define OTP_REGION_CALIPTRA_3_RANGE 0x17C ++#define OTP_RBP_SOC_SVN 0x180 ++#define OTP_RBP_SOC_KEYRETIRE 0x184 ++#define OTP_RBP_CALIP_SVN 0x188 ++#define OTP_RBP_CALIP_KEYRETIRE 0x18C ++#define OTP_PUF 0x1A0 ++#define OTP_MASTER_ID 0x1B0 ++#define OTP_MASTER_ID_EXT 0x1B4 ++#define OTP_R_MASTER_ID 0x1B8 ++#define OTP_R_MASTER_ID_EXT 0x1BC ++#define OTP_SOC_ECCKEY 0x1C0 ++#define OTP_SEC_BOOT_EN 0x1C4 ++#define OTP_SOC_KEY 0x1C8 ++#define OTP_CALPITRA_MANU_KEY 0x1CC ++#define OTP_CALPITRA_OWNER_KEY 0x1D0 ++#define OTP_FW_ID_LSB 0x1D4 ++#define OTP_FW_ID_MSB 0x1D8 ++#define OTP_CALIP_FMC_SVN 0x1DC ++#define OTP_CALIP_RUNTIME_SVN0 0x1E0 ++#define OTP_CALIP_RUNTIME_SVN1 0x1E4 ++#define OTP_CALIP_RUNTIME_SVN2 0x1E8 ++#define OTP_CALIP_RUNTIME_SVN3 0x1EC ++#define OTP_SVN_WLOCK 0x1F0 ++#define OTP_INTR_EN 0x200 ++#define OTP_INTR_STS 0x204 ++#define OTP_INTR_MID 0x208 ++#define OTP_INTR_FUNC_INFO 0x20C ++#define OTP_INTR_M_INFO 0x210 ++#define OTP_INTR_R_INFO 0x214 + -+#define XDMA_CMDQ_SIZE PAGE_SIZE -+#define XDMA_NUM_CMDS \ -+ (XDMA_CMDQ_SIZE / sizeof(struct aspeed_xdma_cmd)) ++#define OTP_PMC 0x400 ++#define OTP_DAP 0x500 + -+/* Aspeed specification requires 100us after disabling the reset */ -+#define XDMA_ENGINE_SETUP_TIME_MAX_US 1000 -+#define XDMA_ENGINE_SETUP_TIME_MIN_US 100 ++/* OTP status: [0] */ ++#define OTP_STS_IDLE 0x0 ++#define OTP_STS_BUSY 0x1 + -+#define XDMA_CMD_AST2500_PITCH_SHIFT 3 -+#define XDMA_CMD_AST2500_PITCH_BMC GENMASK_ULL(62, 51) -+#define XDMA_CMD_AST2500_PITCH_HOST GENMASK_ULL(46, 35) -+#define XDMA_CMD_AST2500_PITCH_UPSTREAM BIT_ULL(31) -+#define XDMA_CMD_AST2500_PITCH_ADDR GENMASK_ULL(29, 4) -+#define XDMA_CMD_AST2500_PITCH_ID BIT_ULL(0) -+#define XDMA_CMD_AST2500_CMD_IRQ_EN BIT_ULL(31) -+#define XDMA_CMD_AST2500_CMD_LINE_NO GENMASK_ULL(27, 16) -+#define XDMA_CMD_AST2500_CMD_IRQ_BMC BIT_ULL(15) -+#define XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT 4 -+#define XDMA_CMD_AST2500_CMD_LINE_SIZE \ -+ GENMASK_ULL(14, XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT) -+#define XDMA_CMD_AST2500_CMD_ID BIT_ULL(1) ++/* OTP cmd status: [7:4] */ ++#define OTP_GET_CMD_STS(x) (((x) & 0xF0) >> 4) ++#define OTP_STS_PASS 0x0 ++#define OTP_STS_FAIL 0x1 ++#define OTP_STS_CMP_FAIL 0x2 ++#define OTP_STS_REGION_FAIL 0x3 ++#define OTP_STS_MASTER_FAIL 0x4 + -+#define XDMA_CMD_AST2600_PITCH_BMC GENMASK_ULL(62, 48) -+#define XDMA_CMD_AST2600_PITCH_HOST GENMASK_ULL(46, 32) -+#define XDMA_CMD_AST2600_PITCH_ADDR GENMASK_ULL(30, 0) -+#define XDMA_CMD_AST2600_CMD_64_EN BIT_ULL(40) -+#define XDMA_CMD_AST2600_CMD_IRQ_BMC BIT_ULL(37) -+#define XDMA_CMD_AST2600_CMD_IRQ_HOST BIT_ULL(36) -+#define XDMA_CMD_AST2600_CMD_UPSTREAM BIT_ULL(32) -+#define XDMA_CMD_AST2600_CMD_LINE_NO GENMASK_ULL(27, 16) -+#define XDMA_CMD_AST2600_CMD_LINE_SIZE GENMASK_ULL(14, 0) -+#define XDMA_CMD_AST2600_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) ++/* OTP ECC EN */ ++#define ECC_ENABLE 0x1 ++#define ECC_DISABLE 0x0 ++#define ECCBRP_EN BIT(0) + -+#define XDMA_CMD_AST2700_PITCH_BMC GENMASK_ULL(62, 48) -+#define XDMA_CMD_AST2700_PITCH_HOST GENMASK_ULL(46, 32) -+#define XDMA_CMD_AST2700_CMD_64_EN BIT_ULL(40) -+#define XDMA_CMD_AST2700_CMD_IRQ_BMC BIT_ULL(37) -+#define XDMA_CMD_AST2700_CMD_UPSTREAM BIT_ULL(32) -+#define XDMA_CMD_AST2700_CMD_LINE_NO GENMASK_ULL(27, 16) -+#define XDMA_CMD_AST2700_CMD_LINE_SIZE GENMASK_ULL(14, 0) -+#define XDMA_CMD_AST2700_CMD_MULTILINE_SIZE GENMASK_ULL(14, 12) -+#define XDMA_CMD_AST2700_BMC_ADDR GENMASK_ULL(34, 0) ++#define ROM_REGION_START_ADDR 0x0 ++#define ROM_REGION_END_ADDR 0x3e0 ++#define RBP_REGION_START_ADDR ROM_REGION_END_ADDR ++#define RBP_REGION_END_ADDR 0x400 ++#define CONF_REGION_START_ADDR RBP_REGION_END_ADDR ++#define CONF_REGION_END_ADDR 0x420 ++#define STRAP_REGION_START_ADDR CONF_REGION_END_ADDR ++#define STRAP_REGION_END_ADDR 0x430 ++#define STRAPEXT_REGION_START_ADDR STRAP_REGION_END_ADDR ++#define STRAPEXT_REGION_END_ADDR 0x440 ++#define USER_REGION_START_ADDR STRAPEXT_REGION_END_ADDR ++#define USER_REGION_END_ADDR 0x1000 ++#define SEC_REGION_START_ADDR USER_REGION_END_ADDR ++#define SEC_REGION_END_ADDR 0x1c00 ++#define CAL_REGION_START_ADDR SEC_REGION_END_ADDR ++#define CAL_REGION_END_ADDR 0x1f80 ++#define SW_PUF_REGION_START_ADDR CAL_REGION_END_ADDR ++#define SW_PUF_REGION_END_ADDR 0x1fc0 ++#define HW_PUF_REGION_START_ADDR SW_PUF_REGION_END_ADDR ++#define HW_PUF_REGION_END_ADDR 0x2000 + -+#define XDMA_AST2500_QUEUE_ENTRY_SIZE 4 -+#define XDMA_AST2500_HOST_CMDQ_ADDR0 0x00 -+#define XDMA_AST2500_HOST_CMDQ_ENDP 0x04 -+#define XDMA_AST2500_HOST_CMDQ_WRITEP 0x08 -+#define XDMA_AST2500_HOST_CMDQ_READP 0x0c -+#define XDMA_AST2500_BMC_CMDQ_ADDR 0x10 -+#define XDMA_AST2500_BMC_CMDQ_ENDP 0x14 -+#define XDMA_AST2500_BMC_CMDQ_WRITEP 0x18 -+#define XDMA_AST2500_BMC_CMDQ_READP 0x1c -+#define XDMA_BMC_CMDQ_READP_RESET 0xee882266 -+#define XDMA_AST2500_CTRL 0x20 -+#define XDMA_AST2500_CTRL_US_COMP BIT(4) -+#define XDMA_AST2500_CTRL_DS_COMP BIT(5) -+#define XDMA_AST2500_CTRL_DS_DIRTY BIT(6) -+#define XDMA_AST2500_CTRL_DS_SIZE_256 BIT(17) -+#define XDMA_AST2500_CTRL_DS_TIMEOUT BIT(28) -+#define XDMA_AST2500_CTRL_DS_CHECK_ID BIT(29) -+#define XDMA_AST2500_STATUS 0x24 -+#define XDMA_AST2500_STATUS_US_COMP BIT(4) -+#define XDMA_AST2500_STATUS_DS_COMP BIT(5) -+#define XDMA_AST2500_STATUS_DS_DIRTY BIT(6) -+#define XDMA_AST2500_INPRG_DS_CMD1 0x38 -+#define XDMA_AST2500_INPRG_DS_CMD2 0x3c -+#define XDMA_AST2500_INPRG_US_CMD00 0x40 -+#define XDMA_AST2500_INPRG_US_CMD01 0x44 -+#define XDMA_AST2500_INPRG_US_CMD10 0x48 -+#define XDMA_AST2500_INPRG_US_CMD11 0x4c -+#define XDMA_AST2500_INPRG_US_CMD20 0x50 -+#define XDMA_AST2500_INPRG_US_CMD21 0x54 -+#define XDMA_AST2500_HOST_CMDQ_ADDR1 0x60 -+#define XDMA_AST2500_VGA_CMDQ_ADDR0 0x64 -+#define XDMA_AST2500_VGA_CMDQ_ENDP 0x68 -+#define XDMA_AST2500_VGA_CMDQ_WRITEP 0x6c -+#define XDMA_AST2500_VGA_CMDQ_READP 0x70 -+#define XDMA_AST2500_VGA_CMD_STATUS 0x74 -+#define XDMA_AST2500_VGA_CMDQ_ADDR1 0x78 ++#define OTP_MEMORY_SIZE (HW_PUF_REGION_END_ADDR * 2) + -+#define XDMA_AST2600_QUEUE_ENTRY_SIZE 2 -+#define XDMA_AST2600_HOST_CMDQ_ADDR0 0x00 -+#define XDMA_AST2600_HOST_CMDQ_ADDR1 0x04 -+#define XDMA_AST2600_HOST_CMDQ_ENDP 0x08 -+#define XDMA_AST2600_HOST_CMDQ_WRITEP 0x0c -+#define XDMA_AST2600_HOST_CMDQ_READP 0x10 -+#define XDMA_AST2600_BMC_CMDQ_ADDR 0x14 -+#define XDMA_AST2600_BMC_CMDQ_ENDP 0x18 -+#define XDMA_AST2600_BMC_CMDQ_WRITEP 0x1c -+#define XDMA_AST2600_BMC_CMDQ_READP 0x20 -+#define XDMA_AST2600_VGA_CMDQ_ADDR0 0x24 -+#define XDMA_AST2600_VGA_CMDQ_ADDR1 0x28 -+#define XDMA_AST2600_VGA_CMDQ_ENDP 0x2c -+#define XDMA_AST2600_VGA_CMDQ_WRITEP 0x30 -+#define XDMA_AST2600_VGA_CMDQ_READP 0x34 -+#define XDMA_AST2600_CTRL 0x38 -+#define XDMA_AST2600_CTRL_US_COMP BIT(16) -+#define XDMA_AST2600_CTRL_DS_COMP BIT(17) -+#define XDMA_AST2600_CTRL_DS_DIRTY BIT(18) -+#define XDMA_AST2600_CTRL_DS_SIZE_256 BIT(20) -+#define XDMA_AST2600_STATUS 0x3c -+#define XDMA_AST2600_STATUS_US_COMP BIT(16) -+#define XDMA_AST2600_STATUS_DS_COMP BIT(17) -+#define XDMA_AST2600_STATUS_DS_DIRTY BIT(18) -+#define XDMA_AST2600_INPRG_DS_CMD00 0x40 -+#define XDMA_AST2600_INPRG_DS_CMD01 0x44 -+#define XDMA_AST2600_INPRG_DS_CMD10 0x48 -+#define XDMA_AST2600_INPRG_DS_CMD11 0x4c -+#define XDMA_AST2600_INPRG_DS_CMD20 0x50 -+#define XDMA_AST2600_INPRG_DS_CMD21 0x54 -+#define XDMA_AST2600_INPRG_US_CMD00 0x60 -+#define XDMA_AST2600_INPRG_US_CMD01 0x64 -+#define XDMA_AST2600_INPRG_US_CMD10 0x68 -+#define XDMA_AST2600_INPRG_US_CMD11 0x6c -+#define XDMA_AST2600_INPRG_US_CMD20 0x70 -+#define XDMA_AST2600_INPRG_US_CMD21 0x74 ++#define OTP_TIMEOUT_US 10000 + -+#define XDMA_AST2700_QUEUE_ENTRY_SIZE 2 -+#define XDMA_AST2700_BMC_CMDQ_ADDR0 0x10 -+#define XDMA_AST2700_BMC_CMDQ_ADDR1 0x14 -+#define XDMA_AST2700_BMC_CMDQ_ENDP 0x18 -+#define XDMA_AST2700_BMC_CMDQ_WRITEP 0x1c -+#define XDMA_AST2700_BMC_CMDQ_READP 0x20 -+#define XDMA_AST2700_CTRL 0x38 -+#define XDMA_AST2700_CTRL_US_COMP BIT(16) -+#define XDMA_AST2700_CTRL_DS_COMP BIT(17) -+#define XDMA_AST2700_CTRL_DS_DIRTY BIT(18) -+#define XDMA_AST2700_CTRL_IDLE BIT(19) -+#define XDMA_AST2700_CTRL_DS_SIZE_256 BIT(20) -+#define XDMA_AST2700_STATUS 0x3c -+#define XDMA_AST2700_STATUS_US_COMP BIT(16) -+#define XDMA_AST2700_STATUS_DS_COMP BIT(17) -+#define XDMA_AST2700_STATUS_DS_DIRTY BIT(18) -+#define XDMA_AST2700_STATUS_IDLE BIT(19) -+#define XDMA_AST2700_INPRG_DS_CMD00 0x40 -+#define XDMA_AST2700_INPRG_DS_CMD01 0x44 -+#define XDMA_AST2700_INPRG_DS_CMD10 0x48 -+#define XDMA_AST2700_INPRG_DS_CMD11 0x4c -+#define XDMA_AST2700_INPRG_DS_CMD20 0x50 -+#define XDMA_AST2700_INPRG_DS_CMD21 0x54 -+#define XDMA_AST2700_INPRG_US_CMD00 0x60 -+#define XDMA_AST2700_INPRG_US_CMD01 0x64 -+#define XDMA_AST2700_INPRG_US_CMD10 0x68 -+#define XDMA_AST2700_INPRG_US_CMD11 0x6c -+#define XDMA_AST2700_INPRG_US_CMD20 0x70 -+#define XDMA_AST2700_INPRG_US_CMD21 0x74 ++/* OTPSTRAP */ ++#define OTPSTRAP0_ADDR STRAP_REGION_START_ADDR ++#define OTPSTRAP14_ADDR (OTPSTRAP0_ADDR + 0xe) + -+struct aspeed_xdma_cmd { -+ u64 host_addr; -+ u64 pitch; -+ u64 cmd; -+ u64 reserved; ++#define OTPTOOL_VERSION(a, b, c) (((a) << 24) + ((b) << 12) + (c)) ++#define OTPTOOL_VERSION_MAJOR(x) (((x) >> 24) & 0xff) ++#define OTPTOOL_VERSION_PATCHLEVEL(x) (((x) >> 12) & 0xfff) ++#define OTPTOOL_VERSION_SUBLEVEL(x) ((x) & 0xfff) ++#define OTPTOOL_COMPT_VERSION 2 ++ ++enum otp_error_code { ++ OTP_SUCCESS, ++ OTP_READ_FAIL, ++ OTP_PROG_FAIL, ++ OTP_CMP_FAIL, +}; + -+struct aspeed_xdma_regs { -+ u8 bmc_cmdq_addr; -+ u8 bmc_cmdq_addr_ext; -+ u8 bmc_cmdq_endp; -+ u8 bmc_cmdq_writep; -+ u8 bmc_cmdq_readp; -+ u8 control; -+ u8 status; ++enum aspeed_otp_master_id { ++ OTP_M0 = 0, ++ OTP_M1, ++ OTP_M2, ++ OTP_M3, ++ OTP_M4, ++ OTP_M5, ++ OTP_MID_MAX, ++}; ++ ++struct aspeed_otp { ++ struct miscdevice miscdev; ++ struct device *dev; ++ void __iomem *base; ++ u32 chip_revid0; ++ u32 chip_revid1; ++ bool is_open; ++ int gbl_ecc_en; ++ u8 *data; ++}; ++ ++enum otp_ioctl_cmds { ++ GET_ECC_STATUS = 1, ++ SET_ECC_ENABLE, ++}; ++ ++enum otp_ecc_codes { ++ OTP_ECC_MISMATCH = -1, ++ OTP_ECC_DISABLE = 0, ++ OTP_ECC_ENABLE = 1, +}; + -+struct aspeed_xdma_status_bits { -+ u32 us_comp; -+ u32 ds_comp; -+ u32 ds_dirty; -+}; ++static void otp_unlock(struct device *dev) ++{ ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); + -+struct aspeed_xdma; ++ writel(OTP_PASSWD, ctx->base + OTP_KEY); ++} + -+struct aspeed_xdma_chip { -+ u32 control; -+ u32 scu_bmc_class; -+ u32 scu_misc_ctrl; -+ u32 scu_misc_mask; -+ u32 scu_disable_mask; -+ u32 scu_pcie_conf; -+ u32 scu_pcie_ctrl; -+ unsigned int queue_entry_size; -+ struct aspeed_xdma_regs regs; -+ struct aspeed_xdma_status_bits status_bits; -+ unsigned int (*set_cmd)(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, u64 bmc_addr); -+}; ++static void otp_lock(struct device *dev) ++{ ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); + -+struct aspeed_xdma_client; ++ writel(0x1, ctx->base + OTP_KEY); ++} + -+struct aspeed_xdma { -+ struct kobject kobj; -+ const struct aspeed_xdma_chip *chip; ++static int wait_complete(struct device *dev) ++{ ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ int ret; ++ u32 val; + -+ int irq; -+ int pcie_irq; -+ struct clk *clock; -+ struct device *dev; -+ void __iomem *base; -+ resource_size_t res_size; -+ resource_size_t res_start; -+ struct reset_control *reset; -+ struct reset_control *reset_rc; ++ ret = readl_poll_timeout(ctx->base + OTP_STATUS, val, (val == 0x0), ++ 1, OTP_TIMEOUT_US); ++ if (ret) ++ dev_warn(dev, "timeout. sts:0x%x\n", val); + -+ /* Protects current_client */ -+ spinlock_t client_lock; -+ struct aspeed_xdma_client *current_client; ++ return ret; ++} + -+ /* Protects engine configuration */ -+ spinlock_t engine_lock; -+ struct aspeed_xdma_cmd *cmdq; -+ unsigned int cmd_idx; -+ bool in_reset; -+ bool upstream; ++static int otp_read_data(struct aspeed_otp *ctx, u32 offset, u16 *data) ++{ ++ struct device *dev = ctx->dev; ++ int ret; + -+ /* Queue waiters for idle engine */ -+ wait_queue_head_t wait; ++ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); ++ writel(offset, ctx->base + OTP_ADDR); ++ writel(OTP_CMD_READ, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_READ_FAIL; + -+ struct work_struct reset_work; ++ data[0] = readl(ctx->base + OTP_RDATA); + -+ phys_addr_t mem_phys; -+ phys_addr_t mem_size; -+ void *mem_virt; -+ dma_addr_t mem_coherent; -+ dma_addr_t cmdq_phys; -+ struct gen_pool *pool; ++ return 0; ++} + -+ struct miscdevice misc; -+}; ++static int otp_prog_data(struct aspeed_otp *ctx, u32 offset, u16 data) ++{ ++ struct device *dev = ctx->dev; ++ int ret; + -+struct aspeed_xdma_client { -+ struct aspeed_xdma *ctx; ++ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); ++ writel(offset, ctx->base + OTP_ADDR); ++ writel(data, ctx->base + OTP_WDATA_0); ++ writel(OTP_CMD_PROG, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_PROG_FAIL; + -+ bool error; -+ bool in_progress; -+ void *virt; -+ dma_addr_t phys; -+ u32 size; -+}; ++ return 0; ++} + -+#define CREATE_TRACE_POINTS -+#include ++static int otp_prog_multi_data(struct aspeed_otp *ctx, u32 offset, u32 *data, int count) ++{ ++ struct device *dev = ctx->dev; ++ int ret; + -+static u32 aspeed_xdma_readl(struct aspeed_xdma *ctx, u8 reg) ++ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); ++ writel(offset, ctx->base + OTP_ADDR); ++ for (int i = 0; i < count; i++) ++ writel(data[i], ctx->base + OTP_WDATA_0 + 4 * i); ++ ++ writel(OTP_CMD_PROG_MULTI, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_PROG_FAIL; ++ ++ return 0; ++} ++ ++static int aspeed_otp_read(struct aspeed_otp *ctx, int offset, ++ void *buf, int size) +{ -+ u32 v = readl(ctx->base + reg); ++ struct device *dev = ctx->dev; ++ u16 *data = buf; ++ int ret; + -+ dev_dbg(ctx->dev, "read %02x[%08x]\n", reg, v); -+ return v; ++ otp_unlock(dev); ++ for (int i = 0; i < size; i++) { ++ ret = otp_read_data(ctx, offset + i, data + i); ++ if (ret) { ++ dev_warn(ctx->dev, "read failed\n"); ++ break; ++ } ++ } ++ ++ otp_lock(dev); ++ return ret; +} + -+static void aspeed_xdma_writel(struct aspeed_xdma *ctx, u8 reg, u32 val) ++static int aspeed_otp_write(struct aspeed_otp *ctx, int offset, ++ const void *buf, int size) +{ -+ writel(val, ctx->base + reg); -+ dev_dbg(ctx->dev, "write %02x[%08x]\n", reg, val); ++ struct device *dev = ctx->dev; ++ u32 *data32 = (u32 *)buf; ++ u16 *data = (u16 *)buf; ++ int ret; ++ ++ otp_unlock(dev); ++ ++ if (size == 1) ++ ret = otp_prog_data(ctx, offset, data[0]); ++ else ++ ret = otp_prog_multi_data(ctx, offset, data32, size / 2); ++ ++ if (ret) ++ dev_warn(ctx->dev, "prog failed\n"); ++ ++ otp_lock(dev); ++ return ret; +} + -+static void aspeed_xdma_init_eng(struct aspeed_xdma *ctx) ++static int aspeed_otp_ecc_en(struct aspeed_otp *ctx) +{ -+ unsigned long flags; ++ struct device *dev = ctx->dev; ++ int ret = 0; + -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_endp, -+ ctx->chip->queue_entry_size * XDMA_NUM_CMDS); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_readp, -+ XDMA_BMC_CMDQ_READP_RESET); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, 0); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.control, ctx->chip->control); -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr, ctx->cmdq_phys); -+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT -+ if (ctx->chip->regs.bmc_cmdq_addr_ext) -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_addr_ext, ctx->cmdq_phys >> 32); -+#endif ++ /* Check ecc is already enabled */ ++ if (ctx->gbl_ecc_en == 1) ++ return 0; + -+ ctx->cmd_idx = 0; -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ otp_unlock(dev); ++ ++ /* enable cfg ecc */ ++ ret = otp_prog_data(ctx, OTPSTRAP14_ADDR, 0x1); ++ if (ret) { ++ dev_warn(dev, "%s: prog failed\n", __func__); ++ goto end; ++ } ++ ++ ctx->gbl_ecc_en = 1; ++end: ++ otp_lock(dev); ++ ++ return ret; +} + -+static unsigned int aspeed_xdma_ast2500_set_cmd(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, -+ u64 bmc_addr) ++static long aspeed_otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ -+ unsigned int rc = 1; -+ unsigned int pitch = 1; -+ unsigned int line_no = 1; -+ unsigned int line_size = op->len >> -+ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; -+ u64 cmd = XDMA_CMD_AST2500_CMD_IRQ_EN | XDMA_CMD_AST2500_CMD_IRQ_BMC | -+ XDMA_CMD_AST2500_CMD_ID; -+ u64 cmd_pitch = (op->direction ? XDMA_CMD_AST2500_PITCH_UPSTREAM : 0) | -+ XDMA_CMD_AST2500_PITCH_ID; ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ void __user *argp = (void __user *)arg; ++ struct otp_revid revid; ++ struct otp_read rdata; ++ struct otp_prog pdata; ++ int ret = 0; + -+ dev_dbg(ctx->dev, "xdma %s ast2500: bmc[%08llx] len[%08x] host[%08x]\n", -+ op->direction ? "upstream" : "downstream", bmc_addr, op->len, -+ (u32)op->host_addr); ++ switch (cmd) { ++ case ASPEED_OTP_READ_DATA: ++ if (copy_from_user(&rdata, argp, sizeof(struct otp_read))) ++ return -EFAULT; + -+ if (op->len > XDMA_CMD_AST2500_CMD_LINE_SIZE) { -+ unsigned int rem; -+ unsigned int total; ++ ret = aspeed_otp_read(ctx, rdata.offset, ctx->data, rdata.len); ++ if (ret) ++ return -EFAULT; + -+ line_no = op->len / XDMA_CMD_AST2500_CMD_LINE_SIZE; -+ total = XDMA_CMD_AST2500_CMD_LINE_SIZE * line_no; -+ rem = (op->len - total) >> -+ XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; -+ line_size = XDMA_CMD_AST2500_CMD_LINE_SIZE; -+ pitch = line_size >> XDMA_CMD_AST2500_PITCH_SHIFT; -+ line_size >>= XDMA_CMD_AST2500_CMD_LINE_SIZE_SHIFT; ++ if (copy_to_user(rdata.data, ctx->data, rdata.len * 2)) ++ return -EFAULT; + -+ if (rem) { -+ u32 rbmc = bmc_addr + total; ++ break; + -+ cmds[1].host_addr = op->host_addr + (u64)total; -+ cmds[1].pitch = cmd_pitch | -+ ((u64)rbmc & XDMA_CMD_AST2500_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, 1) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, 1); -+ cmds[1].cmd = cmd | -+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, 1) | -+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, -+ rem); -+ cmds[1].reserved = 0ULL; ++ case ASPEED_OTP_PROG_DATA: ++ if (copy_from_user(&pdata, argp, sizeof(struct otp_prog))) ++ return -EFAULT; + -+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, -+ 16, 1, &cmds[1], sizeof(*cmds), -+ true); ++ ret = aspeed_otp_write(ctx, pdata.w_offset, pdata.data, pdata.len); ++ break; + -+ cmd &= ~(XDMA_CMD_AST2500_CMD_IRQ_EN | -+ XDMA_CMD_AST2500_CMD_IRQ_BMC); ++ case ASPEED_OTP_GET_ECC: ++ if (copy_to_user(argp, &ctx->gbl_ecc_en, sizeof(ctx->gbl_ecc_en))) ++ return -EFAULT; ++ break; + -+ rc++; -+ } ++ case ASPEED_OTP_SET_ECC: ++ ret = aspeed_otp_ecc_en(ctx); ++ break; ++ ++ case ASPEED_OTP_GET_REVID: ++ revid.revid0 = ctx->chip_revid0; ++ revid.revid1 = ctx->chip_revid1; ++ if (copy_to_user(argp, &revid, sizeof(struct otp_revid))) ++ return -EFAULT; ++ break; ++ default: ++ dev_warn(ctx->dev, "cmd 0x%x is not supported\n", cmd); ++ break; + } + -+ cmds[0].host_addr = op->host_addr; -+ cmds[0].pitch = cmd_pitch | -+ (bmc_addr & XDMA_CMD_AST2500_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_HOST, pitch) | -+ FIELD_PREP(XDMA_CMD_AST2500_PITCH_BMC, pitch); -+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_NO, line_no) | -+ FIELD_PREP(XDMA_CMD_AST2500_CMD_LINE_SIZE, line_size); -+ cmds[0].reserved = 0ULL; ++ return ret; ++} + -+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, -+ sizeof(*cmds), true); ++static int aspeed_otp_ecc_init(struct device *dev) ++{ ++ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ int ret; ++ u32 val; + -+ return rc; ++ otp_unlock(dev); ++ ++ /* Check cfg_ecc_en */ ++ writel(0, ctx->base + OTP_ECC_EN); ++ writel(OTPSTRAP14_ADDR, ctx->base + OTP_ADDR); ++ writel(OTP_CMD_READ, ctx->base + OTP_CMD); ++ ret = wait_complete(dev); ++ if (ret) ++ return OTP_READ_FAIL; ++ ++ val = readl(ctx->base + OTP_RDATA); ++ if (val & 0x1) ++ ctx->gbl_ecc_en = 0x1; ++ else ++ ctx->gbl_ecc_en = 0x0; ++ ++ otp_lock(dev); ++ ++ return 0; +} + -+static unsigned int aspeed_xdma_ast2600_set_cmd(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, -+ u64 bmc_addr) ++static int aspeed_otp_open(struct inode *inode, struct file *file) +{ -+ unsigned int rc = 1; -+ unsigned int pitch = 1; -+ unsigned int line_no = 1; -+ unsigned int line_size = op->len; -+ u64 cmd = XDMA_CMD_AST2600_CMD_IRQ_BMC | -+ (op->direction ? XDMA_CMD_AST2600_CMD_UPSTREAM : 0); ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+ if (op->host_addr & 0xffffffff00000000ULL || -+ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) -+ cmd |= XDMA_CMD_AST2600_CMD_64_EN; ++ spin_lock(&otp_state_lock); + -+ dev_dbg(ctx->dev, "xdma %s ast2600: bmc[%08llx] len[%08x] host[%016llx]\n", -+ op->direction ? "upstream" : "downstream", -+ bmc_addr, op->len, op->host_addr); ++ if (ctx->is_open) { ++ spin_unlock(&otp_state_lock); ++ return -EBUSY; ++ } + -+ if ((op->host_addr & 0xff) + op->len > XDMA_CMD_AST2600_CMD_LINE_SIZE) { -+ unsigned int rem; -+ unsigned int total; ++ ctx->is_open = true; + -+ line_no = op->len / XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; -+ total = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE * line_no; -+ rem = op->len - total; -+ line_size = XDMA_CMD_AST2600_CMD_MULTILINE_SIZE; -+ pitch = line_size; ++ spin_unlock(&otp_state_lock); + -+ if (rem) { -+ u32 rbmc = bmc_addr + total; ++ return 0; ++} + -+ cmds[1].host_addr = op->host_addr + (u64)total; -+ cmds[1].pitch = -+ ((u64)rbmc & XDMA_CMD_AST2600_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, 1) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, 1); -+ cmds[1].cmd = cmd | -+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, 1) | -+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, -+ rem); -+ cmds[1].reserved = 0ULL; ++static int aspeed_otp_release(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *c = file->private_data; ++ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); + -+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, -+ 16, 1, &cmds[1], sizeof(*cmds), -+ true); ++ spin_lock(&otp_state_lock); + -+ cmd &= ~XDMA_CMD_AST2600_CMD_IRQ_BMC; ++ ctx->is_open = false; + -+ rc++; -+ } -+ } ++ spin_unlock(&otp_state_lock); + -+ cmds[0].host_addr = op->host_addr; -+ cmds[0].pitch = (bmc_addr & XDMA_CMD_AST2600_PITCH_ADDR) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_HOST, pitch) | -+ FIELD_PREP(XDMA_CMD_AST2600_PITCH_BMC, pitch); -+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_NO, line_no) | -+ FIELD_PREP(XDMA_CMD_AST2600_CMD_LINE_SIZE, line_size); -+ cmds[0].reserved = 0ULL; ++ return 0; ++} + -+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, -+ sizeof(*cmds), true); ++static const struct file_operations otp_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = aspeed_otp_ioctl, ++ .open = aspeed_otp_open, ++ .release = aspeed_otp_release, ++}; + -+ return rc; -+} ++static const struct of_device_id aspeed_otp_of_matches[] = { ++ { .compatible = "aspeed,ast2700-otp" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); + -+static unsigned int aspeed_xdma_ast2700_set_cmd(struct aspeed_xdma *ctx, -+ struct aspeed_xdma_cmd cmds[2], -+ struct aspeed_xdma_op *op, -+ u64 bmc_addr) ++static int aspeed_otp_probe(struct platform_device *pdev) +{ -+ unsigned int rc = 1; -+ unsigned int pitch = 1; -+ unsigned int line_no = 1; -+ unsigned int line_size = op->len; -+ u64 cmd = XDMA_CMD_AST2700_CMD_IRQ_BMC | -+ (op->direction ? XDMA_CMD_AST2700_CMD_UPSTREAM : 0); ++ struct device *dev = &pdev->dev; ++ struct regmap *scu0, *scu1; ++ struct aspeed_otp *priv; ++ struct resource *res; ++ int rc; + -+ if (op->host_addr & 0xffffffff00000000ULL || -+ (op->host_addr + (u64)op->len) & 0xffffffff00000000ULL) -+ cmd |= XDMA_CMD_AST2700_CMD_64_EN; ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; + -+ dev_dbg(ctx->dev, "xdma %s ast2700: bmc[%016llx] len[%08x] host[%016llx]\n", -+ op->direction ? "upstream" : "downstream", -+ bmc_addr, op->len, op->host_addr); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); ++ return -ENOENT; ++ } + -+ if (op->len > XDMA_CMD_AST2700_CMD_LINE_SIZE) { -+ unsigned int rem; -+ unsigned int total; ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (!priv->base) ++ return -EIO; + -+ line_no = op->len / XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; -+ total = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE * line_no; -+ rem = op->len - total; -+ line_size = XDMA_CMD_AST2700_CMD_MULTILINE_SIZE; -+ pitch = line_size; ++ scu0 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu0"); ++ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu1"); ++ if (IS_ERR(scu0) || IS_ERR(scu1)) { ++ dev_err(dev, "failed to find SCU regmap\n"); ++ return PTR_ERR(scu0) || PTR_ERR(scu1); ++ } + -+ if (rem) { -+ u64 rbmc = bmc_addr + total; ++ regmap_read(scu0, 0x0, &priv->chip_revid0); ++ regmap_read(scu1, 0x0, &priv->chip_revid1); + -+ cmds[1].host_addr = op->host_addr + (u64)total; -+ cmds[1].pitch = -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, 1) | -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, 1); -+ cmds[1].cmd = cmd | -+ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, 1) | -+ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, -+ rem); -+ cmds[1].reserved = rbmc & XDMA_CMD_AST2700_BMC_ADDR; ++ priv->dev = dev; ++ dev_set_drvdata(dev, priv); + -+ print_hex_dump_debug("xdma rem ", DUMP_PREFIX_OFFSET, -+ 16, 1, &cmds[1], sizeof(*cmds), -+ true); ++ /* OTP ECC init */ ++ rc = aspeed_otp_ecc_init(dev); ++ if (rc) ++ return -EIO; + -+ cmd &= ~XDMA_CMD_AST2700_CMD_IRQ_BMC; ++ priv->data = kmalloc(OTP_MEMORY_SIZE, GFP_KERNEL); ++ if (!priv->data) ++ return -ENOMEM; + -+ rc++; -+ } ++ /* Set up the miscdevice */ ++ priv->miscdev.minor = MISC_DYNAMIC_MINOR; ++ priv->miscdev.name = "aspeed-otp"; ++ priv->miscdev.fops = &otp_fops; ++ ++ /* Register the device */ ++ rc = misc_register(&priv->miscdev); ++ if (rc) { ++ dev_err(dev, "Unable to register device\n"); ++ return rc; + } -+ cmds[0].host_addr = op->host_addr; -+ cmds[0].pitch = -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_HOST, pitch) | -+ FIELD_PREP(XDMA_CMD_AST2700_PITCH_BMC, pitch); -+ cmds[0].cmd = cmd | FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_NO, line_no) | -+ FIELD_PREP(XDMA_CMD_AST2700_CMD_LINE_SIZE, line_size); -+ cmds[0].reserved = bmc_addr & XDMA_CMD_AST2700_BMC_ADDR; + -+ print_hex_dump_debug("xdma cmd ", DUMP_PREFIX_OFFSET, 16, 1, cmds, -+ sizeof(*cmds), true); ++ dev_info(dev, "Aspeed OTP driver successfully registered\n"); + -+ return rc; ++ return 0; +} + -+static int aspeed_xdma_start(struct aspeed_xdma *ctx, unsigned int num_cmds, -+ struct aspeed_xdma_cmd cmds[2], bool upstream, -+ struct aspeed_xdma_client *client) ++static void aspeed_otp_remove(struct platform_device *pdev) +{ -+ unsigned int i; -+ int rc = -EBUSY; -+ unsigned long flags; ++ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); + -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ if (ctx->in_reset) -+ goto unlock; ++ kfree(ctx->data); ++ misc_deregister(&ctx->miscdev); ++} + -+ spin_lock(&ctx->client_lock); -+ if (ctx->current_client) { -+ spin_unlock(&ctx->client_lock); -+ goto unlock; -+ } ++static struct platform_driver aspeed_otp_driver = { ++ .probe = aspeed_otp_probe, ++ .remove = aspeed_otp_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_otp_of_matches, ++ }, ++}; + -+ client->error = false; -+ client->in_progress = true; -+ ctx->current_client = client; -+ spin_unlock(&ctx->client_lock); ++module_platform_driver(aspeed_otp_driver); + -+ ctx->upstream = upstream; -+ for (i = 0; i < num_cmds; ++i) { -+ trace_xdma_start(ctx, &cmds[i]); -+ /* -+ * Use memcpy_toio here to get some barriers before starting -+ * the operation. The command(s) need to be in physical memory -+ * before the XDMA engine starts. -+ */ -+ memcpy_toio(&ctx->cmdq[ctx->cmd_idx], &cmds[i], -+ sizeof(struct aspeed_xdma_cmd)); -+ ctx->cmd_idx = (ctx->cmd_idx + 1) % XDMA_NUM_CMDS; -+ } ++MODULE_AUTHOR("Neal Liu "); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("ASPEED OTP Driver"); +diff --git a/drivers/soc/aspeed/espi/Kconfig b/drivers/soc/aspeed/espi/Kconfig +--- a/drivers/soc/aspeed/espi/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/Kconfig 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,22 @@ ++menu "ASPEED eSPI drivers" + -+ aspeed_xdma_writel(ctx, ctx->chip->regs.bmc_cmdq_writep, -+ ctx->cmd_idx * ctx->chip->queue_entry_size); -+ rc = 0; ++config ASPEED_ESPI ++ tristate "ASPEED eSPI slave driver" ++ help ++ Enable driver support for Aspeed AST2700 eSPI engine. The eSPI engine ++ plays as a slave device in BMC to communicate with the Host over ++ the eSPI interface. The four eSPI channels, namely peripheral, ++ virtual wire, out-of-band, and flash are supported. + -+unlock: -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); -+ return rc; -+} ++config AST2700_RTC_OVER_ESPI ++ tristate "ASPEED AST2700 RTC over eSPI drvier" ++ depends on HAS_IOMEM ++ help ++ Enable driver support for Aspeed AST2700 RTC over eSPI function. ++ Periodically copies RAM content from a RTC tm to a memory-mapped eSPI region. + -+static void aspeed_xdma_done(struct aspeed_xdma *ctx, bool error) -+{ -+ unsigned long flags; ++config AST2700_INTEL_ESPI_MMBI ++ bool "Enable support for AST2700 ESPI MMBI controller with Intel PCH host" ++ depends on ASPEED_ESPI + -+ spin_lock_irqsave(&ctx->client_lock, flags); -+ if (ctx->current_client) { -+ ctx->current_client->error = error; -+ ctx->current_client->in_progress = false; -+ ctx->current_client = NULL; -+ } -+ spin_unlock_irqrestore(&ctx->client_lock, flags); ++endmenu +diff --git a/drivers/soc/aspeed/espi/Makefile b/drivers/soc/aspeed/espi/Makefile +--- a/drivers/soc/aspeed/espi/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/Makefile 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,7 @@ ++obj-$(CONFIG_ASPEED_ESPI) += \ ++ ast2700-espi.o \ ++ ast2600-espi.o \ ++ ast2500-espi.o \ ++ aspeed-espi.o ++obj-$(CONFIG_AST2700_RTC_OVER_ESPI) += ast2700-rtc-over-espi.o ++obj-$(CONFIG_AST2700_INTEL_ESPI_MMBI) += ast2700-intel-espi-mmbi.o +\ No newline at end of file +diff --git a/drivers/soc/aspeed/espi/aspeed-espi-comm.h b/drivers/soc/aspeed/espi/aspeed-espi-comm.h +--- a/drivers/soc/aspeed/espi/aspeed-espi-comm.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/aspeed-espi-comm.h 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,215 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Aspeed eSPI protocol definitions and IOCTL methods ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_ESPI_COMM_H__ ++#define __ASPEED_ESPI_COMM_H__ + -+ wake_up_interruptible_all(&ctx->wait); -+} ++#include ++#include + -+static irqreturn_t aspeed_xdma_irq(int irq, void *arg) -+{ -+ struct aspeed_xdma *ctx = arg; -+ u32 status; ++/* ++ * eSPI cycle type encoding ++ * ++ * Section 5.1 Cycle Types and Packet Format, ++ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. ++ */ ++#define ESPI_PERIF_MEMRD32 0x00 ++#define ESPI_PERIF_MEMRD64 0x02 ++#define ESPI_PERIF_MEMWR32 0x01 ++#define ESPI_PERIF_MEMWR64 0x03 ++#define ESPI_PERIF_MSG 0x10 ++#define ESPI_PERIF_MSG_D 0x11 ++#define ESPI_PERIF_SUC_CMPLT 0x06 ++#define ESPI_PERIF_SUC_CMPLT_D_MIDDLE 0x09 ++#define ESPI_PERIF_SUC_CMPLT_D_FIRST 0x0b ++#define ESPI_PERIF_SUC_CMPLT_D_LAST 0x0d ++#define ESPI_PERIF_SUC_CMPLT_D_ONLY 0x0f ++#define ESPI_PERIF_UNSUC_CMPLT 0x0c ++#define ESPI_OOB_MSG 0x21 ++#define ESPI_FLASH_READ 0x00 ++#define ESPI_FLASH_WRITE 0x01 ++#define ESPI_FLASH_ERASE 0x02 ++#define ESPI_FLASH_SUC_CMPLT 0x06 ++#define ESPI_FLASH_SUC_CMPLT_D_MIDDLE 0x09 ++#define ESPI_FLASH_SUC_CMPLT_D_FIRST 0x0b ++#define ESPI_FLASH_SUC_CMPLT_D_LAST 0x0d ++#define ESPI_FLASH_SUC_CMPLT_D_ONLY 0x0f ++#define ESPI_FLASH_UNSUC_CMPLT 0x0c + -+ spin_lock(&ctx->engine_lock); -+ status = aspeed_xdma_readl(ctx, ctx->chip->regs.status); ++/* ++ * eSPI packet format structure ++ * ++ * Section 5.1 Cycle Types and Packet Format, ++ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. ++ */ ++struct espi_comm_hdr { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++}; + -+ trace_xdma_irq(status); ++struct espi_perif_mem32 { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u32 addr_be; ++ u8 data[]; ++} __packed; + -+ if (status & ctx->chip->status_bits.ds_dirty) { -+ aspeed_xdma_done(ctx, true); -+ } else { -+ if (status & ctx->chip->status_bits.us_comp) { -+ if (ctx->upstream) -+ aspeed_xdma_done(ctx, false); -+ } ++struct espi_perif_mem64 { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u32 addr_be; ++ u8 data[]; ++} __packed; + -+ if (status & ctx->chip->status_bits.ds_comp) { -+ if (!ctx->upstream) -+ aspeed_xdma_done(ctx, false); -+ } -+ } ++struct espi_perif_msg { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 msg_code; ++ u8 msg_byte[4]; ++ u8 data[]; ++} __packed; + -+ aspeed_xdma_writel(ctx, ctx->chip->regs.status, status); -+ spin_unlock(&ctx->engine_lock); ++struct espi_perif_cmplt { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 data[]; ++} __packed; + -+ return IRQ_HANDLED; -+} ++struct espi_oob_msg { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 data[]; ++}; + -+static void aspeed_xdma_reset(struct aspeed_xdma *ctx) -+{ -+ unsigned long flags; ++struct espi_flash_rwe { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u32 addr_be; ++ u8 data[]; ++} __packed; + -+ trace_xdma_reset(ctx); ++struct espi_flash_cmplt { ++ u8 cyc; ++ u8 len_h : 4; ++ u8 tag : 4; ++ u8 len_l; ++ u8 data[]; ++} __packed; + -+ reset_control_assert(ctx->reset); -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); -+ reset_control_deassert(ctx->reset); -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); ++#define ESPI_MAX_PLD_LEN BIT(12) + -+ aspeed_xdma_init_eng(ctx); ++/* ++ * Aspeed IOCTL for eSPI raw packet send/receive ++ * ++ * This IOCTL interface works in the eSPI packet in/out paradigm. ++ * ++ * Only the virtual wire IOCTL is a special case which does not send ++ * or receive an eSPI packet. However, to keep a more consisten use from ++ * userspace, we make all of the four channel drivers serve through the ++ * IOCTL interface. ++ * ++ * For the eSPI packet format, refer to ++ * Section 5.1 Cycle Types and Packet Format, ++ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. ++ * ++ * For the example user apps using these IOCTL, refer to ++ * https://github.com/AspeedTech-BMC/aspeed_app/tree/master/espi_test ++ */ ++#define __ASPEED_ESPI_IOCTL_MAGIC 0xb8 + -+ aspeed_xdma_done(ctx, true); ++/* ++ * we choose the longest header and the max payload size ++ * based on the Intel specification to define the maximum ++ * eSPI packet length ++ */ ++#define ESPI_MAX_PKT_LEN (sizeof(struct espi_perif_msg) + ESPI_MAX_PLD_LEN) + -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ ctx->in_reset = false; -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++struct aspeed_espi_ioc { ++ u32 pkt_len; ++ u8 *pkt; ++}; + -+ wake_up_interruptible(&ctx->wait); -+} ++/* ++ * Peripheral Channel (CH0) ++ * - ASPEED_ESPI_PERIF_PC_GET_RX ++ * Receive an eSPI Posted/Completion packet ++ * - ASPEED_ESPI_PERIF_PC_PUT_TX ++ * Transmit an eSPI Posted/Completion packet ++ * - ASPEED_ESPI_PERIF_NP_PUT_TX ++ * Transmit an eSPI Non-Posted packet ++ */ ++#define ASPEED_ESPI_PERIF_PC_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x00, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_PERIF_PC_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x01, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_PERIF_NP_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x02, struct aspeed_espi_ioc) ++/* ++ * Virtual Wire Channel (CH1) ++ * - ASPEED_ESPI_VW_GET_GPIO_VAL ++ * Read the input value of GPIO over the VW channel ++ * - ASPEED_ESPI_VW_PUT_GPIO_VAL ++ * Write the output value of GPIO over the VW channel ++ * - ASPEED_ESPI_VW_GET_GPIO_VAL1 (new feature in AST2700) ++ * Read the input value1 of GPIO over the VW channel ++ * - ASPEED_ESPI_VW_PUT_GPIO_VAL1 (new feature in AST2700) ++ * Write the output value1 of GPIO over the VW channel ++ * - ASPEED_ESPI_VW_GET_PCH_GENERIC ++ * Read the input value of PCH Generic Indexes over the VW channel ++ * - ASPEED_ESPI_VW_PUT_PCH_GENERIC ++ * Write the output value of PCH Generic Indexes over the VW channel ++ */ ++#define ASPEED_ESPI_VW_GET_GPIO_VAL _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x10, u32) ++#define ASPEED_ESPI_VW_PUT_GPIO_VAL _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x11, u32) ++#ifdef CONFIG_ARM64 ++#define ASPEED_ESPI_VW_GET_GPIO_VAL1 _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x12, u32) ++#define ASPEED_ESPI_VW_PUT_GPIO_VAL1 _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x13, u32) ++#endif ++#define ASPEED_ESPI_VW_GET_PCH_GENERIC _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x14, u32) ++#define ASPEED_ESPI_VW_PUT_PCH_GENERIC _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x15, u32) ++/* ++ * Out-of-band Channel (CH2) ++ * - ASPEED_ESPI_OOB_GET_RX ++ * Receive an eSPI OOB packet ++ * - ASPEED_ESPI_OOB_PUT_TX ++ * Transmit an eSPI OOB packet ++ */ ++#define ASPEED_ESPI_OOB_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x20, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_OOB_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x21, struct aspeed_espi_ioc) ++/* ++ * Flash Channel (CH3) ++ * - ASPEED_ESPI_FLASH_GET_RX ++ * Receive an eSPI flash packet ++ * - ASPEED_ESPI_FLASH_PUT_TX ++ * Transmit an eSPI flash packet ++ */ ++#define ASPEED_ESPI_FLASH_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x30, struct aspeed_espi_ioc) ++#define ASPEED_ESPI_FLASH_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ ++ 0x31, struct aspeed_espi_ioc) + -+static void aspeed_xdma_reset_work(struct work_struct *work) -+{ -+ struct aspeed_xdma *ctx = container_of(work, struct aspeed_xdma, -+ reset_work); ++#endif +diff --git a/drivers/soc/aspeed/espi/aspeed-espi.c b/drivers/soc/aspeed/espi/aspeed-espi.c +--- a/drivers/soc/aspeed/espi/aspeed-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/aspeed-espi.c 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,256 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Unified Aspeed eSPI driver (AST2500/AST2600/AST2700) ++ * ++ * This wraps the existing SoC-specific implementations behind a ++ * single driver name and compatible strings. For now we keep the ++ * full implementations in their original files and dispatch through ++ * small per-SoC glue ops. ++ */ + -+ aspeed_xdma_reset(ctx); -+} ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+static irqreturn_t aspeed_xdma_pcie_irq(int irq, void *arg) -+{ -+ struct aspeed_xdma *ctx = arg; ++#include "aspeed-espi.h" ++#include "ast2500-espi.h" ++#include "ast2600-espi.h" ++#include "ast2700-espi.h" + -+ trace_xdma_perst(ctx); ++struct aspeed_espi_ops { ++ enum aspeed_espi_platform_id platform_id; ++ void (*espi_pre_init)(struct aspeed_espi *espi); ++ void (*espi_post_init)(struct aspeed_espi *espi); ++ void (*espi_deinit)(struct aspeed_espi *espi); ++ int (*espi_perif_probe)(struct aspeed_espi *espi); ++ int (*espi_perif_remove)(struct aspeed_espi *espi); ++ int (*espi_vw_probe)(struct aspeed_espi *espi); ++ int (*espi_vw_remove)(struct aspeed_espi *espi); ++ int (*espi_oob_probe)(struct aspeed_espi *espi); ++ int (*espi_oob_remove)(struct aspeed_espi *espi); ++ int (*espi_flash_probe)(struct aspeed_espi *espi); ++ int (*espi_flash_remove)(struct aspeed_espi *espi); ++ irqreturn_t (*espi_isr)(int irq, void *espi); ++}; + -+ spin_lock(&ctx->engine_lock); -+ if (ctx->in_reset) { -+ spin_unlock(&ctx->engine_lock); -+ return IRQ_HANDLED; -+ } ++static const struct aspeed_espi_ops aspeed_espi_ast2500_ops = { ++ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2500, ++ .espi_pre_init = ast2500_espi_pre_init, ++ .espi_post_init = ast2500_espi_post_init, ++ .espi_deinit = ast2500_espi_deinit, ++ .espi_perif_probe = ast2500_espi_perif_probe, ++ .espi_perif_remove = ast2500_espi_perif_remove, ++ .espi_vw_probe = ast2500_espi_vw_probe, ++ .espi_vw_remove = ast2500_espi_vw_remove, ++ .espi_oob_probe = ast2500_espi_oob_probe, ++ .espi_oob_remove = ast2500_espi_oob_remove, ++ .espi_flash_probe = ast2500_espi_flash_probe, ++ .espi_flash_remove = ast2500_espi_flash_remove, ++ .espi_isr = ast2500_espi_isr, ++}; + -+ ctx->in_reset = true; -+ spin_unlock(&ctx->engine_lock); ++static const struct aspeed_espi_ops aspeed_espi_ast2600_ops = { ++ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2600, ++ .espi_pre_init = ast2600_espi_pre_init, ++ .espi_post_init = ast2600_espi_post_init, ++ .espi_deinit = ast2600_espi_deinit, ++ .espi_perif_probe = ast2600_espi_perif_probe, ++ .espi_perif_remove = ast2600_espi_perif_remove, ++ .espi_vw_probe = ast2600_espi_vw_probe, ++ .espi_vw_remove = ast2600_espi_vw_remove, ++ .espi_oob_probe = ast2600_espi_oob_probe, ++ .espi_oob_remove = ast2600_espi_oob_remove, ++ .espi_flash_probe = ast2600_espi_flash_probe, ++ .espi_flash_remove = ast2600_espi_flash_remove, ++ .espi_isr = ast2600_espi_isr, ++}; + -+ schedule_work(&ctx->reset_work); -+ return IRQ_HANDLED; -+} ++static const struct aspeed_espi_ops aspeed_espi_ast2700_ops = { ++ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2700, ++ .espi_pre_init = ast2700_espi_pre_init, ++ .espi_post_init = ast2700_espi_post_init, ++ .espi_deinit = ast2700_espi_deinit, ++ .espi_perif_probe = ast2700_espi_perif_probe, ++ .espi_perif_remove = ast2700_espi_perif_remove, ++ .espi_vw_probe = ast2700_espi_vw_probe, ++ .espi_vw_remove = ast2700_espi_vw_remove, ++ .espi_oob_probe = ast2700_espi_oob_probe, ++ .espi_oob_remove = ast2700_espi_oob_remove, ++ .espi_flash_probe = ast2700_espi_flash_probe, ++ .espi_flash_remove = ast2700_espi_flash_remove, ++ .espi_isr = ast2700_espi_isr, ++}; + -+static ssize_t aspeed_xdma_write(struct file *file, const char __user *buf, -+ size_t len, loff_t *offset) ++static const struct of_device_id aspeed_espi_of_matches[] = { ++ { .compatible = "aspeed,ast2500-espi", .data = &aspeed_espi_ast2500_ops }, ++ { .compatible = "aspeed,ast2600-espi", .data = &aspeed_espi_ast2600_ops }, ++ { .compatible = "aspeed,ast2700-espi", .data = &aspeed_espi_ast2700_ops }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_espi_of_matches); ++ ++static int aspeed_espi_probe(struct platform_device *pdev) +{ ++ const struct of_device_id *match; ++ struct device *dev = &pdev->dev; ++ struct aspeed_espi *espi; ++ struct resource *res; + int rc; -+ unsigned int num_cmds; -+ struct aspeed_xdma_op op; -+ struct aspeed_xdma_cmd cmds[2]; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; -+ -+ if (len != sizeof(op)) -+ return -EINVAL; + -+ if (READ_ONCE(client->in_progress)) -+ return -EBUSY; ++ espi = devm_kzalloc(dev, sizeof(*espi), GFP_KERNEL); ++ if (!espi) ++ return -ENOMEM; + -+ if (copy_from_user(&op, buf, len)) -+ return -EFAULT; ++ espi->dev = dev; ++ match = of_match_device(aspeed_espi_of_matches, dev); ++ if (!match) ++ return -ENODEV; ++ espi->ops = match->data; + -+ if (!op.len || op.len > client->size || -+ op.direction > ASPEED_XDMA_DIRECTION_UPSTREAM) -+ return -EINVAL; ++ espi->pdev = pdev; + -+ num_cmds = ctx->chip->set_cmd(ctx, cmds, &op, client->phys); -+ do { -+ rc = aspeed_xdma_start(ctx, num_cmds, cmds, !!op.direction, -+ client); -+ if (!rc) -+ break; ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ++ if (rc) { ++ dev_err(dev, "cannot set 64-bits DMA mask\n"); ++ return rc; ++ } + -+ if ((file->f_flags & O_NONBLOCK) || rc != -EBUSY) -+ return rc; ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(dev, "cannot get resource\n"); ++ return -ENODEV; ++ } + -+ rc = wait_event_interruptible(ctx->wait, -+ !(ctx->current_client || -+ ctx->in_reset)); -+ } while (!rc); ++ espi->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(espi->regs)) { ++ dev_err(dev, "cannot map registers\n"); ++ return PTR_ERR(espi->regs); ++ } + -+ if (rc) -+ return -EINTR; ++ espi->irq = platform_get_irq(pdev, 0); ++ if (espi->irq < 0) { ++ dev_err(dev, "cannot get IRQ number\n"); ++ return -ENODEV; ++ } + -+ if (!(file->f_flags & O_NONBLOCK)) { -+ rc = wait_event_interruptible(ctx->wait, !client->in_progress); -+ if (rc) -+ return -EINTR; ++ espi->rst = devm_reset_control_get_optional(&pdev->dev, NULL); ++ if (IS_ERR(espi->rst)) { ++ dev_err(dev, "cannot get reset control\n"); ++ return PTR_ERR(espi->rst); ++ } + -+ if (client->error) -+ return -EIO; ++ espi->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(espi->clk)) { ++ dev_err(dev, "cannot get clock control\n"); ++ return PTR_ERR(espi->clk); + } + -+ return len; -+} -+ -+static __poll_t aspeed_xdma_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ __poll_t mask = 0; -+ __poll_t req = poll_requested_events(wait); -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; ++ rc = clk_prepare_enable(espi->clk); ++ if (rc) { ++ dev_err(dev, "cannot enable clocks\n"); ++ return rc; ++ } + -+ if (req & (EPOLLIN | EPOLLRDNORM)) { -+ if (READ_ONCE(client->in_progress)) -+ poll_wait(file, &ctx->wait, wait); ++ espi->ops->espi_pre_init(espi); + -+ if (!READ_ONCE(client->in_progress)) { -+ if (READ_ONCE(client->error)) -+ mask |= EPOLLERR; -+ else -+ mask |= EPOLLIN | EPOLLRDNORM; -+ } ++ rc = espi->ops->espi_perif_probe(espi); ++ if (rc) { ++ dev_err(dev, "cannot init peripheral channel, rc=%d\n", rc); ++ return rc; + } + -+ if (req & (EPOLLOUT | EPOLLWRNORM)) { -+ if (READ_ONCE(ctx->current_client)) -+ poll_wait(file, &ctx->wait, wait); -+ -+ if (!READ_ONCE(ctx->current_client)) -+ mask |= EPOLLOUT | EPOLLWRNORM; ++ rc = espi->ops->espi_vw_probe(espi); ++ if (rc) { ++ dev_err(dev, "cannot init vw channel, rc=%d\n", rc); ++ goto err_remove_perif; + } + -+ if (mask) -+ aspeed_xdma_reset(ctx); ++ rc = espi->ops->espi_oob_probe(espi); ++ if (rc) { ++ dev_err(dev, "cannot init oob channel, rc=%d\n", rc); ++ goto err_remove_vw; ++ } + -+ return mask; -+} ++ rc = espi->ops->espi_flash_probe(espi); ++ if (rc) { ++ dev_err(dev, "cannot init flash channel, rc=%d\n", rc); ++ goto err_remove_oob; ++ } + -+static long aspeed_xdma_ioctl(struct file *file, unsigned int cmd, -+ unsigned long param) -+{ -+ unsigned long flags; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; ++ rc = devm_request_irq(dev, espi->irq, espi->ops->espi_isr, 0, ++ dev_name(dev), espi); ++ if (rc) { ++ dev_err(dev, "cannot request IRQ\n"); ++ goto err_remove_flash; ++ } + -+ switch (cmd) { -+ case ASPEED_XDMA_IOCTL_RESET: -+ spin_lock_irqsave(&ctx->engine_lock, flags); -+ if (ctx->in_reset) { -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); -+ return 0; ++ if (espi->perif.mmbi.enable) { ++ rc = devm_request_irq(dev, espi->perif.mmbi.irq, ++ espi->perif.mmbi.mmbi_isr, 0, dev_name(dev), ++ espi); ++ if (rc) { ++ dev_err(dev, "cannot request MMBI IRQ\n"); ++ goto err_remove_flash; + } ++ } + -+ ctx->in_reset = true; -+ spin_unlock_irqrestore(&ctx->engine_lock, flags); ++ espi->ops->espi_post_init(espi); + -+ if (READ_ONCE(ctx->current_client)) -+ dev_warn(ctx->dev, -+ "User reset with transfer in progress.\n"); ++ platform_set_drvdata(pdev, espi); + -+ aspeed_xdma_reset(ctx); -+ break; -+ default: -+ return -EINVAL; -+ } ++ dev_info(dev, "module loaded\n"); + + return 0; -+} -+ -+static void aspeed_xdma_vma_close(struct vm_area_struct *vma) -+{ -+ int rc; -+ struct aspeed_xdma_client *client = vma->vm_private_data; -+ -+ rc = wait_event_interruptible(client->ctx->wait, !client->in_progress); -+ if (rc) -+ return; + -+ gen_pool_free(client->ctx->pool, (unsigned long)client->virt, -+ client->size); -+ trace_xdma_unmap(client); ++err_remove_flash: ++ espi->ops->espi_flash_remove(espi); ++err_remove_oob: ++ espi->ops->espi_oob_remove(espi); ++err_remove_vw: ++ espi->ops->espi_vw_remove(espi); ++err_remove_perif: ++ espi->ops->espi_perif_remove(espi); + -+ client->virt = NULL; -+ client->phys = 0; -+ client->size = 0; ++ return rc; +} + -+static const struct vm_operations_struct aspeed_xdma_vm_ops = { -+ .close = aspeed_xdma_vma_close, -+}; -+ -+static int aspeed_xdma_mmap(struct file *file, struct vm_area_struct *vma) ++static void aspeed_espi_remove(struct platform_device *pdev) +{ -+ int rc; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; -+ -+ /* restrict file to one mapping */ -+ if (client->size) -+ return -EBUSY; ++ struct aspeed_espi *espi; ++ struct device *dev; + -+ client->size = vma->vm_end - vma->vm_start; -+ client->virt = gen_pool_dma_alloc(ctx->pool, client->size, -+ &client->phys); -+ if (!client->virt) { -+ trace_xdma_mmap_error(client, 0UL); -+ client->phys = 0; -+ client->size = 0; -+ return -ENOMEM; -+ } ++ dev = &pdev->dev; + -+ vma->vm_pgoff = (client->phys - ctx->mem_phys) >> PAGE_SHIFT; -+ vma->vm_ops = &aspeed_xdma_vm_ops; -+ vma->vm_private_data = client; -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ espi = dev_get_drvdata(dev); + -+ rc = io_remap_pfn_range(vma, vma->vm_start, client->phys >> PAGE_SHIFT, -+ client->size, vma->vm_page_prot); -+ if (rc) { -+ dev_warn(ctx->dev, "mmap err: v[%08lx] to p[%pa], s[%08x]\n", -+ vma->vm_start, &client->phys, client->size); ++ espi->ops->espi_deinit(espi); + -+ gen_pool_free(ctx->pool, (unsigned long)client->virt, -+ client->size); ++ if (espi->ops->espi_perif_remove(espi)) ++ dev_warn(dev, "cannot remove peripheral channel\n"); ++ if (espi->ops->espi_vw_remove(espi)) ++ dev_warn(dev, "cannot remove vw channel\n"); ++ if (espi->ops->espi_oob_remove(espi)) ++ dev_warn(dev, "cannot remove oob channel\n"); ++ if (espi->ops->espi_flash_remove(espi)) ++ dev_warn(dev, "cannot remove flash channel\n"); ++} + -+ trace_xdma_mmap_error(client, vma->vm_start); -+ client->virt = NULL; -+ client->phys = 0; -+ client->size = 0; -+ return rc; -+ } ++static struct platform_driver aspeed_espi_driver = { ++ .driver = { ++ .name = "aspeed-espi", ++ .of_match_table = aspeed_espi_of_matches, ++ }, ++ .probe = aspeed_espi_probe, ++ .remove = aspeed_espi_remove, ++}; + -+ trace_xdma_mmap(client); -+ dev_dbg(ctx->dev, "mmap: v[%08lx] to p[%pa], s[%08x]\n", -+ vma->vm_start, &client->phys, client->size); ++module_platform_driver(aspeed_espi_driver); + -+ return 0; -+} ++MODULE_AUTHOR("Aspeed Technology Inc."); ++MODULE_DESCRIPTION("Aspeed eSPI controller (AST2500/2600/2700)"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/soc/aspeed/espi/aspeed-espi.h b/drivers/soc/aspeed/espi/aspeed-espi.h +--- a/drivers/soc/aspeed/espi/aspeed-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/aspeed-espi.h 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,231 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Unified eSPI driver header file and data structures ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_ESPI_H__ ++#define __ASPEED_ESPI_H__ + -+static int aspeed_xdma_open(struct inode *inode, struct file *file) -+{ -+ struct miscdevice *misc = file->private_data; -+ struct aspeed_xdma *ctx = container_of(misc, struct aspeed_xdma, misc); -+ struct aspeed_xdma_client *client = kzalloc(sizeof(*client), -+ GFP_KERNEL); ++#include "linux/platform_device.h" ++#include ++#include ++#include + -+ if (!client) -+ return -ENOMEM; ++#define PERIF_MMBI_INST_NUM 8 ++#define DEVICE_NAME "aspeed-espi" + -+ kobject_get(&ctx->kobj); -+ client->ctx = ctx; -+ file->private_data = client; -+ return 0; -+} ++enum aspeed_espi_platform_id { ++ ASPEED_ESPI_PLATFORM_ID_AST2500 = 0x2500, ++ ASPEED_ESPI_PLATFORM_ID_AST2600 = 0x2600, ++ ASPEED_ESPI_PLATFORM_ID_AST2700 = 0x2700, ++}; + -+static int aspeed_xdma_release(struct inode *inode, struct file *file) -+{ -+ bool reset = false; -+ unsigned long flags; -+ struct aspeed_xdma_client *client = file->private_data; -+ struct aspeed_xdma *ctx = client->ctx; ++/* consistent with DTS property "flash-safs-mode" */ ++enum aspeed_edaf_mode { ++ EDAF_MODE_MIX = 0x0, ++ EDAF_MODE_SW, ++ EDAF_MODE_HW, ++ EDAF_MODES, ++}; + -+ spin_lock_irqsave(&ctx->client_lock, flags); -+ if (client == ctx->current_client) { -+ spin_lock(&ctx->engine_lock); -+ if (ctx->in_reset) { -+ ctx->current_client = NULL; -+ } else { -+ ctx->in_reset = true; -+ reset = true; -+ } -+ spin_unlock(&ctx->engine_lock); -+ } -+ spin_unlock_irqrestore(&ctx->client_lock, flags); ++struct aspeed_espi_perif; + -+ if (reset) -+ aspeed_xdma_reset(ctx); ++struct aspeed_espi_perif_mmbi { ++ void *b2h_virt; ++ void *h2b_virt; ++ dma_addr_t b2h_addr; ++ dma_addr_t h2b_addr; ++ struct miscdevice b2h_mdev; ++ struct miscdevice h2b_mdev; ++ bool host_rwp_update; ++ wait_queue_head_t wq; ++ struct aspeed_espi_perif *perif; ++}; + -+ if (client->virt) { -+ gen_pool_free(ctx->pool, (unsigned long)client->virt, -+ client->size); -+ trace_xdma_unmap(client); -+ } ++struct aspeed_espi_perif { ++ struct { ++ bool enable; ++ int irq; ++ void *virt; ++ dma_addr_t taddr; ++ dma_addr_t saddr; ++ resource_size_t size; ++ u32 inst_num; ++ u32 inst_size; ++ irqreturn_t (*mmbi_isr)(int irq, void *espi); ++ struct aspeed_espi_perif_mmbi inst[PERIF_MMBI_INST_NUM]; ++ } mmbi; + -+ kfree(client); -+ kobject_put(&ctx->kobj); -+ return 0; -+} ++ struct { ++ bool enable; ++ void *virt; ++ dma_addr_t taddr; ++ dma_addr_t saddr; ++ resource_size_t size; ++ } mcyc; + -+static const struct file_operations aspeed_xdma_fops = { -+ .owner = THIS_MODULE, -+ .write = aspeed_xdma_write, -+ .poll = aspeed_xdma_poll, -+ .unlocked_ioctl = aspeed_xdma_ioctl, -+ .mmap = aspeed_xdma_mmap, -+ .open = aspeed_xdma_open, -+ .release = aspeed_xdma_release, -+}; ++ struct { ++ bool enable; ++ void *np_tx_virt; ++ dma_addr_t np_tx_addr; ++ void *pc_tx_virt; ++ dma_addr_t pc_tx_addr; ++ void *pc_rx_virt; ++ dma_addr_t pc_rx_addr; ++ } dma; + -+static int aspeed_xdma_init_scu(struct aspeed_xdma *ctx, struct device *dev) -+{ -+ struct regmap *scu = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "aspeed,scu"); ++ bool rtc_enable; ++ bool rx_ready; ++ wait_queue_head_t wq; + -+ if (!IS_ERR(scu)) { -+ u32 selection; -+ bool pcie_device_bmc = true; -+ const u32 bmc = SCU_PCIE_CONF_BMC_EN | -+ SCU_PCIE_CONF_BMC_EN_MSI | SCU_PCIE_CONF_BMC_EN_IRQ | -+ SCU_PCIE_CONF_BMC_EN_DMA; -+ const u32 vga = SCU_PCIE_CONF_VGA_EN | -+ SCU_PCIE_CONF_VGA_EN_MSI | SCU_PCIE_CONF_VGA_EN_IRQ | -+ SCU_PCIE_CONF_VGA_EN_DMA; -+ const char *pcie = NULL; ++ spinlock_t lock; // protects rx_ready ++ struct mutex np_tx_mtx; // protects np_tx_virt/addr ++ struct mutex pc_tx_mtx; // protects pc_tx_virt/addr ++ struct mutex pc_rx_mtx; // protects pc_rx_virt/addr + -+ if (!of_property_read_string(dev->of_node, -+ "aspeed,pcie-device", &pcie)) { -+ if (!strcmp(pcie, "vga")) { -+ pcie_device_bmc = false; -+ } else if (strcmp(pcie, "bmc")) { -+ dev_err(dev, -+ "Invalid pcie-device property %s.\n", -+ pcie); -+ return -EINVAL; -+ } -+ } ++ struct miscdevice mdev; ++}; + -+ if (pcie_device_bmc) { -+ selection = bmc; -+ regmap_update_bits(scu, ctx->chip->scu_bmc_class, -+ SCU_BMC_CLASS_REV_MASK, -+ SCU_BMC_CLASS_REV_XDMA); -+ } else { -+ selection = vga; -+ } ++struct aspeed_espi_vw { ++ struct { ++ bool hw_mode; ++ u32 grp; ++ u32 dir0; ++ u32 dir1; ++ u32 val0; ++ u32 val1; ++ } gpio; + -+ regmap_update_bits(scu, ctx->chip->scu_pcie_conf, bmc | vga, -+ selection); ++ struct { ++ u8 pch_generic; ++ } platform; + -+ if (ctx->chip->scu_misc_ctrl) { -+ regmap_update_bits(scu, ctx->chip->scu_misc_ctrl, -+ ctx->chip->scu_misc_mask, -+ ctx->chip->scu_misc_mask); ++ struct { ++ bool enabled; ++ spinlock_t pltrst_lock; // protects pltrst_status ++ wait_queue_head_t pltrst_wq; ++ char pltrst_status; ++ bool pltrst_avail; ++ } pltrst; + -+ regmap_update_bits(scu, SCU_AST2600_DEBUG_CTRL, -+ ctx->chip->scu_disable_mask, 0); -+ } ++ struct miscdevice pltrst_mdev; ++ struct miscdevice mdev; ++}; + -+ if (ctx->chip->scu_pcie_ctrl) { -+ regmap_update_bits(scu, ctx->chip->scu_pcie_ctrl, -+ SCU_AST2700_PCIE_CTRL_DMA_EN, -+ SCU_AST2700_PCIE_CTRL_DMA_EN); -+ } -+ } else { -+ dev_warn(dev, "Unable to configure PCIe: %ld; continuing.\n", -+ PTR_ERR(scu)); -+ } ++struct ast2700_espi_oob_dma_tx_desc { ++ u32 data_addrl; ++ u32 data_addrh; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 msg_type : 3; ++ u8 raz0 : 1; ++ u8 pec : 1; ++ u8 int_en : 1; ++ u8 pause : 1; ++ u8 raz1 : 1; ++ u32 raz2; ++ u32 raz3; ++ u32 pad[3]; ++} __packed; + -+ return 0; -+} ++struct ast2700_espi_oob_dma_rx_desc { ++ u32 data_addrl; ++ u32 data_addrh; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 raz : 7; ++ u8 dirty : 1; ++ u32 pad; ++} __packed; + -+static void aspeed_xdma_kobject_release(struct kobject *kobj) -+{ -+ struct aspeed_xdma *ctx = container_of(kobj, struct aspeed_xdma, kobj); ++struct ast2600_espi_oob_dma_tx_desc { ++ u32 data_addr; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 msg_type : 3; ++ u8 raz0 : 1; ++ u8 pec : 1; ++ u8 int_en : 1; ++ u8 pause : 1; ++ u8 raz1 : 1; ++ u32 raz2; ++ u32 raz3; ++} __packed; + -+ if (ctx->pcie_irq >= 0) -+ free_irq(ctx->pcie_irq, ctx); ++struct ast2600_espi_oob_dma_rx_desc { ++ u32 data_addr; ++ u8 cyc; ++ u16 tag : 4; ++ u16 len : 12; ++ u8 raz : 7; ++ u8 dirty : 1; ++} __packed; + -+ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); ++struct aspeed_espi_oob { ++ struct { ++ bool enable; ++ void *txd_virt; ++ dma_addr_t txd_addr; ++ void *rxd_virt; ++ dma_addr_t rxd_addr; ++ void *tx_virt; ++ dma_addr_t tx_addr; ++ void *rx_virt; ++ dma_addr_t rx_addr; ++ } dma; + -+ gen_pool_destroy(ctx->pool); ++ bool rx_ready; ++ wait_queue_head_t wq; + -+ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, -+ ctx->mem_coherent); ++ spinlock_t lock; // protects rx_ready ++ struct mutex tx_mtx; // protects tx_virt/addr ++ struct mutex rx_mtx; // protects rx_virt/addr + -+ if (ctx->reset_rc) -+ reset_control_put(ctx->reset_rc); -+ reset_control_put(ctx->reset); ++ struct miscdevice mdev; ++}; + -+ clk_put(ctx->clock); ++struct aspeed_espi_flash { ++ struct { ++ u32 mode; ++ phys_addr_t taddr; ++ resource_size_t size; ++ } edaf; + -+ free_irq(ctx->irq, ctx); ++ struct { ++ bool enable; ++ void *tx_virt; ++ dma_addr_t tx_addr; ++ void *rx_virt; ++ dma_addr_t rx_addr; ++ } dma; + -+ iounmap(ctx->base); -+ release_mem_region(ctx->res_start, ctx->res_size); ++ bool rx_ready; ++ wait_queue_head_t wq; + -+ kfree(ctx); -+} ++ spinlock_t lock; // protects rx_ready ++ struct mutex rx_mtx; // protects rx_virt/addr ++ struct mutex tx_mtx; // protects tx_virt/addr + -+static const struct kobj_type aspeed_xdma_kobject_type = { -+ .release = aspeed_xdma_kobject_release, ++ struct miscdevice mdev; +}; + -+static int aspeed_xdma_iomap(struct aspeed_xdma *ctx, -+ struct platform_device *pdev) -+{ -+ resource_size_t size; -+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++struct aspeed_espi { ++ struct platform_device *pdev; ++ struct device *dev; ++ void __iomem *regs; ++ struct reset_control *rst; ++ struct clk *clk; ++ int dev_id; ++ int irq; + -+ if (!res) -+ return -ENOMEM; ++ struct aspeed_espi_perif perif; ++ struct aspeed_espi_vw vw; ++ struct aspeed_espi_oob oob; ++ struct aspeed_espi_flash flash; ++ const struct aspeed_espi_ops *ops; ++}; + -+ size = resource_size(res); -+ if (!request_mem_region(res->start, size, dev_name(ctx->dev))) -+ return -ENOMEM; ++#endif // __ASPEED_ESPI_H__ +diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/ast2500-espi.c +--- a/drivers/soc/aspeed/espi/ast2500-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2500-espi.c 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,1510 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ ctx->base = ioremap(res->start, size); -+ if (!ctx->base) { -+ release_mem_region(res->start, size); -+ return -ENOMEM; -+ } ++#include "ast2500-espi.h" ++#include "aspeed-espi-comm.h" ++#include "aspeed-espi.h" + -+ ctx->res_start = res->start; -+ ctx->res_size = size; ++#define PERIF_MCYC_UNLOCK 0xfedc756e ++#define PERIF_MCYC_ALIGN SZ_64K + -+ return 0; -+} ++#define FLASH_SAFS_ALIGN SZ_16M + -+static int aspeed_xdma_probe(struct platform_device *pdev) ++/* peripheral channel (CH0) */ ++static long ast2500_espi_perif_pc_get_rx(struct file *fp, ++ struct aspeed_espi_perif *perif, ++ struct aspeed_espi_ioc *ioc) +{ -+ int rc, id; -+ struct aspeed_xdma *ctx; -+ struct reserved_mem *mem; -+ struct device *dev = &pdev->dev; -+ struct device_node *memory_region; -+ const void *md = of_device_get_match_data(dev); -+ bool rc_f; -+ -+ if (!md) -+ return -ENODEV; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; + -+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return -ENOMEM; ++ espi = container_of(perif, struct aspeed_espi, perif); + -+ ctx->chip = md; -+ ctx->dev = dev; -+ platform_set_drvdata(pdev, ctx); -+ spin_lock_init(&ctx->client_lock); -+ spin_lock_init(&ctx->engine_lock); -+ INIT_WORK(&ctx->reset_work, aspeed_xdma_reset_work); -+ init_waitqueue_head(&ctx->wait); ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&perif->pc_rx_mtx)) ++ return -EAGAIN; + -+ rc_f = of_find_property(dev->of_node, "pcie_rc", NULL) ? 1 : 0; ++ if (!perif->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&perif->pc_rx_mtx); + -+ rc = aspeed_xdma_iomap(ctx, pdev); -+ if (rc) { -+ dev_err(dev, "Failed to map registers.\n"); -+ goto err_nomap; ++ if (!perif->rx_ready) { ++ rc = wait_event_interruptible(perif->wq, perif->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; ++ } ++ } + } + -+ ctx->irq = platform_get_irq(pdev, 0); -+ if (ctx->irq < 0) { -+ dev_err(dev, "Failed to find IRQ.\n"); -+ rc = ctx->irq; -+ goto err_noirq; -+ } ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_PERIF_PC_RX_CTRL); ++ cyc = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_LEN, reg); + -+ rc = request_irq(ctx->irq, aspeed_xdma_irq, 0, dev_name(dev), ctx); -+ if (rc < 0) { -+ dev_err(dev, "Failed to request IRQ %d.\n", ctx->irq); -+ goto err_noirq; ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ switch (cyc) { ++ case ESPI_PERIF_MSG: ++ pkt_len = sizeof(struct espi_perif_msg); ++ break; ++ case ESPI_PERIF_MSG_D: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_perif_msg); ++ break; ++ case ESPI_PERIF_SUC_CMPLT_D_MIDDLE: ++ case ESPI_PERIF_SUC_CMPLT_D_FIRST: ++ case ESPI_PERIF_SUC_CMPLT_D_LAST: ++ case ESPI_PERIF_SUC_CMPLT_D_ONLY: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_perif_cmplt); ++ break; ++ case ESPI_PERIF_SUC_CMPLT: ++ case ESPI_PERIF_UNSUC_CMPLT: ++ pkt_len = sizeof(struct espi_perif_cmplt); ++ break; ++ default: ++ rc = -EFAULT; ++ goto unlock_mtx_n_out; + } + -+ ctx->clock = clk_get(dev, NULL); -+ if (IS_ERR(ctx->clock)) { -+ dev_err(dev, "Failed to request clock.\n"); -+ rc = PTR_ERR(ctx->clock); -+ goto err_noclk; ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; + } + -+ ctx->reset = reset_control_get_exclusive(dev, NULL); -+ if (IS_ERR(ctx->reset)) { -+ dev_err(dev, "Failed to request reset control.\n"); -+ rc = PTR_ERR(ctx->reset); -+ goto err_noreset; ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; + } + -+ if (rc_f) { -+ ctx->reset_rc = reset_control_get_exclusive(dev, "root-complex"); -+ if (IS_ERR(ctx->reset_rc)) { -+ dev_dbg(dev, "Failed to request reset RC control.\n"); -+ ctx->reset_rc = NULL; -+ } -+ } ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; + -+ memory_region = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (!memory_region) { -+ dev_err(dev, "Failed to find memory-region.\n"); -+ rc = -ENOMEM; -+ goto err_nomem; ++ if (perif->dma.enable) { ++ memcpy(hdr + 1, perif->dma.pc_rx_virt, pkt_len - sizeof(*hdr)); ++ } else { ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ reg = readl(espi->regs + ESPI_PERIF_PC_RX_DATA) & 0xff; + } + -+ mem = of_reserved_mem_lookup(memory_region); -+ of_node_put(memory_region); -+ if (!mem) { -+ dev_err(dev, "Failed to find reserved memory.\n"); -+ rc = -ENOMEM; -+ goto err_nomem; ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; + } + -+ ctx->mem_phys = mem->base; -+ ctx->mem_size = mem->size; ++ spin_lock_irqsave(&perif->lock, flags); + -+ rc = of_reserved_mem_device_init(dev); -+ if (rc) { -+ dev_err(dev, "Failed to init reserved memory.\n"); -+ goto err_nomem; -+ } ++ writel(ESPI_PERIF_PC_RX_CTRL_SERV_PEND, espi->regs + ESPI_PERIF_PC_RX_CTRL); ++ perif->rx_ready = 0; + -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); -+ if (rc) { -+ dev_err(dev, "Failed to mask DMA.\n"); -+ goto err_nomem; -+ } ++ spin_unlock_irqrestore(&perif->lock, flags); + -+ ctx->mem_virt = dma_alloc_coherent(dev, ctx->mem_size, -+ &ctx->mem_coherent, __GFP_NOWARN); -+ if (!ctx->mem_virt) { -+ dev_err(dev, "Failed to allocate reserved memory.\n"); -+ rc = -ENOMEM; -+ goto err_nomem; -+ } ++ rc = 0; + -+ ctx->pool = gen_pool_create(ilog2(PAGE_SIZE), -1); -+ if (!ctx->pool) { -+ dev_err(dev, "Failed to setup genalloc pool.\n"); -+ rc = -ENOMEM; -+ goto err_nopool; -+ } ++free_n_out: ++ vfree(pkt); + -+ rc = gen_pool_add_virt(ctx->pool, (unsigned long)ctx->mem_virt, -+ ctx->mem_phys, ctx->mem_size, -1); -+ if (rc) { -+ dev_err(ctx->dev, "Failed to add memory to genalloc pool.\n"); -+ goto err_pool_scu_clk; -+ } ++unlock_mtx_n_out: ++ mutex_unlock(&perif->pc_rx_mtx); + -+ rc = aspeed_xdma_init_scu(ctx, dev); -+ if (rc) -+ goto err_pool_scu_clk; ++ return rc; ++} + -+ rc = clk_prepare_enable(ctx->clock); -+ if (rc) { -+ dev_err(dev, "Failed to enable the clock.\n"); -+ goto err_pool_scu_clk; -+ } ++static long ast2500_espi_perif_pc_put_tx(struct file *fp, ++ struct aspeed_espi_perif *perif, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+ if (ctx->reset_rc) { -+ rc = reset_control_deassert(ctx->reset_rc); -+ if (rc) { -+ dev_err(dev, "Failed to clear the RC reset.\n"); -+ goto err_reset_rc; -+ } -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); -+ } ++ espi = container_of(perif, struct aspeed_espi, perif); + -+ rc = reset_control_deassert(ctx->reset); -+ if (rc) { -+ dev_err(dev, "Failed to clear the reset.\n"); -+ goto err_reset; ++ if (!mutex_trylock(&perif->pc_tx_mtx)) ++ return -EAGAIN; ++ ++ reg = readl(espi->regs + ESPI_PERIF_PC_TX_CTRL); ++ if (reg & ESPI_PERIF_PC_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_n_out; + } -+ usleep_range(XDMA_ENGINE_SETUP_TIME_MIN_US, -+ XDMA_ENGINE_SETUP_TIME_MAX_US); + -+ ctx->cmdq = gen_pool_dma_alloc(ctx->pool, XDMA_CMDQ_SIZE, -+ &ctx->cmdq_phys); -+ if (!ctx->cmdq) { -+ dev_err(ctx->dev, "Failed to genalloc cmdq.\n"); ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { + rc = -ENOMEM; -+ goto err_pool; ++ goto unlock_n_out; + } + -+ aspeed_xdma_init_eng(ctx); -+ -+ id = of_alias_get_id(dev->of_node, "xdma"); -+ if (id < 0) -+ id = 0; ++ hdr = (struct espi_comm_hdr *)pkt; + -+ ctx->misc.minor = MISC_DYNAMIC_MINOR; -+ ctx->misc.fops = &aspeed_xdma_fops; -+ ctx->misc.name = kasprintf(GFP_KERNEL, "%s%d", DEVICE_NAME, id); -+ ctx->misc.parent = dev; -+ rc = misc_register(&ctx->misc); -+ if (rc) { -+ dev_err(dev, "Failed to register xdma miscdevice.\n"); -+ goto err_misc; ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; + } + + /* -+ * This interrupt could fire immediately so only request it once the -+ * engine and driver are initialized. ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers + */ -+ ctx->pcie_irq = platform_get_irq(pdev, 1); -+ if (ctx->pcie_irq < 0) { -+ dev_warn(dev, "Failed to find PCI-E IRQ.\n"); ++ if (perif->dma.enable) { ++ memcpy(perif->dma.pc_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); + } else { -+ rc = request_irq(ctx->pcie_irq, aspeed_xdma_pcie_irq, -+ IRQF_SHARED, dev_name(dev), ctx); -+ if (rc < 0) { -+ dev_warn(dev, "Failed to request PCI-E IRQ %d.\n", rc); -+ ctx->pcie_irq = -1; -+ } ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_PERIF_PC_TX_DATA); + } + -+ kobject_init(&ctx->kobj, &aspeed_xdma_kobject_type); -+ return 0; -+ -+err_misc: -+ gen_pool_free(ctx->pool, (unsigned long)ctx->cmdq, XDMA_CMDQ_SIZE); -+err_pool: -+ reset_control_assert(ctx->reset); -+err_reset: -+ if (ctx->reset_rc) -+ reset_control_assert(ctx->reset_rc); -+err_reset_rc: -+ clk_disable_unprepare(ctx->clock); -+err_pool_scu_clk: -+ gen_pool_destroy(ctx->pool); -+err_nopool: -+ dma_free_coherent(ctx->dev, ctx->mem_size, ctx->mem_virt, -+ ctx->mem_coherent); -+err_nomem: -+ if (ctx->reset_rc) -+ reset_control_put(ctx->reset_rc); -+ reset_control_put(ctx->reset); -+err_noreset: -+ clk_put(ctx->clock); -+err_noclk: -+ free_irq(ctx->irq, ctx); -+err_noirq: -+ iounmap(ctx->base); -+ release_mem_region(ctx->res_start, ctx->res_size); -+err_nomap: -+ kfree(ctx); -+ return rc; -+} -+ -+static void aspeed_xdma_remove(struct platform_device *pdev) -+{ -+ struct aspeed_xdma *ctx = platform_get_drvdata(pdev); -+ -+ reset_control_assert(ctx->reset); -+ if (ctx->reset_rc) -+ reset_control_assert(ctx->reset_rc); -+ clk_disable_unprepare(ctx->clock); -+ -+ aspeed_xdma_done(ctx, true); -+ -+ misc_deregister(&ctx->misc); -+ kobject_put(&ctx->kobj); -+} -+ -+static const struct aspeed_xdma_chip aspeed_ast2500_xdma_chip = { -+ .control = XDMA_AST2500_CTRL_US_COMP | XDMA_AST2500_CTRL_DS_COMP | -+ XDMA_AST2500_CTRL_DS_DIRTY | XDMA_AST2500_CTRL_DS_SIZE_256 | -+ XDMA_AST2500_CTRL_DS_TIMEOUT | XDMA_AST2500_CTRL_DS_CHECK_ID, -+ .scu_bmc_class = SCU_AST2500_BMC_CLASS_REV, -+ .scu_misc_ctrl = 0, -+ .scu_pcie_conf = SCU_AST2500_PCIE_CONF, -+ .scu_pcie_ctrl = 0, -+ .queue_entry_size = XDMA_AST2500_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2500_BMC_CMDQ_ADDR, -+ .bmc_cmdq_addr_ext = 0, -+ .bmc_cmdq_endp = XDMA_AST2500_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2500_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2500_BMC_CMDQ_READP, -+ .control = XDMA_AST2500_CTRL, -+ .status = XDMA_AST2500_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2500_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2500_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2500_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2500_set_cmd, -+}; -+ -+static const struct aspeed_xdma_chip aspeed_ast2600_xdma_chip = { -+ .control = XDMA_AST2600_CTRL_US_COMP | XDMA_AST2600_CTRL_DS_COMP | -+ XDMA_AST2600_CTRL_DS_DIRTY | XDMA_AST2600_CTRL_DS_SIZE_256, -+ .scu_bmc_class = SCU_AST2600_BMC_CLASS_REV, -+ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, -+ .scu_misc_mask = SCU_AST2600_MISC_CTRL_XDMA_BMC, -+ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE, -+ .scu_pcie_conf = SCU_AST2600_PCIE_CONF, -+ .scu_pcie_ctrl = 0, -+ .queue_entry_size = XDMA_AST2600_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2600_BMC_CMDQ_ADDR, -+ .bmc_cmdq_addr_ext = 0, -+ .bmc_cmdq_endp = XDMA_AST2600_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2600_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2600_BMC_CMDQ_READP, -+ .control = XDMA_AST2600_CTRL, -+ .status = XDMA_AST2600_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2600_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2600_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2600_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2600_set_cmd, -+}; -+ -+static const struct aspeed_xdma_chip aspeed_ast2700_xdma0_chip = { -+ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | -+ XDMA_AST2700_CTRL_DS_DIRTY, -+ .scu_bmc_class = SCU_AST2700_PCIE0_BMC_CLASS_REV, -+ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, -+ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, -+ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, -+ .scu_pcie_conf = SCU_AST2700_PCIE0_CONF, -+ .scu_pcie_ctrl = SCU_AST2700_PCIE0_CTRL, -+ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, -+ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, -+ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, -+ .control = XDMA_AST2700_CTRL, -+ .status = XDMA_AST2700_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2700_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2700_set_cmd, -+}; -+ -+static const struct aspeed_xdma_chip aspeed_ast2700_xdma1_chip = { -+ .control = XDMA_AST2700_CTRL_US_COMP | XDMA_AST2700_CTRL_DS_COMP | -+ XDMA_AST2700_CTRL_DS_DIRTY, -+ .scu_bmc_class = SCU_AST2700_PCIE1_BMC_CLASS_REV, -+ .scu_misc_ctrl = SCU_AST2600_MISC_CTRL, -+ .scu_misc_mask = SCU_AST2700_MISC_CTRL_XDMA_CLIENT, -+ .scu_disable_mask = DEBUG_CTRL_AST2600_XDMA_DISABLE | DEBUG_CTRL_AST2700_XDMA_DISABLE, -+ .scu_pcie_conf = SCU_AST2700_PCIE1_CONF, -+ .scu_pcie_ctrl = SCU_AST2700_PCIE1_CTRL, -+ .queue_entry_size = XDMA_AST2700_QUEUE_ENTRY_SIZE, -+ .regs = { -+ .bmc_cmdq_addr = XDMA_AST2700_BMC_CMDQ_ADDR0, -+ .bmc_cmdq_addr_ext = XDMA_AST2700_BMC_CMDQ_ADDR1, -+ .bmc_cmdq_endp = XDMA_AST2700_BMC_CMDQ_ENDP, -+ .bmc_cmdq_writep = XDMA_AST2700_BMC_CMDQ_WRITEP, -+ .bmc_cmdq_readp = XDMA_AST2700_BMC_CMDQ_READP, -+ .control = XDMA_AST2700_CTRL, -+ .status = XDMA_AST2700_STATUS, -+ }, -+ .status_bits = { -+ .us_comp = XDMA_AST2700_STATUS_US_COMP, -+ .ds_comp = XDMA_AST2700_STATUS_DS_COMP, -+ .ds_dirty = XDMA_AST2700_STATUS_DS_DIRTY, -+ }, -+ .set_cmd = aspeed_xdma_ast2700_set_cmd, -+}; -+ -+static const struct of_device_id aspeed_xdma_match[] = { -+ { -+ .compatible = "aspeed,ast2500-xdma", -+ .data = &aspeed_ast2500_xdma_chip, -+ }, -+ { -+ .compatible = "aspeed,ast2600-xdma", -+ .data = &aspeed_ast2600_xdma_chip, -+ }, -+ { -+ .compatible = "aspeed,ast2700-xdma0", -+ .data = &aspeed_ast2700_xdma0_chip, -+ }, -+ { -+ .compatible = "aspeed,ast2700-xdma1", -+ .data = &aspeed_ast2700_xdma1_chip, -+ }, -+ { }, -+}; -+ -+static struct platform_driver aspeed_xdma_driver = { -+ .probe = aspeed_xdma_probe, -+ .remove = aspeed_xdma_remove, -+ .driver = { -+ .name = DEVICE_NAME, -+ .of_match_table = aspeed_xdma_match, -+ }, -+}; -+ -+module_platform_driver(aspeed_xdma_driver); -+ -+MODULE_AUTHOR("Eddie James"); -+MODULE_DESCRIPTION("ASPEED XDMA Engine Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/ast2600-otp.c b/drivers/soc/aspeed/ast2600-otp.c ---- a/drivers/soc/aspeed/ast2600-otp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2600-otp.c 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,638 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) ASPEED Technology Inc. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define ASPEED_REVISION_ID0 0x04 -+#define ASPEED_REVISION_ID1 0x14 -+#define ID0_AST2600A0 0x05000303 -+#define ID1_AST2600A0 0x05000303 -+#define ID0_AST2600A1 0x05010303 -+#define ID1_AST2600A1 0x05010303 -+#define ID0_AST2600A2 0x05010303 -+#define ID1_AST2600A2 0x05020303 -+#define ID0_AST2600A3 0x05030303 -+#define ID1_AST2600A3 0x05030303 -+#define ID0_AST2620A1 0x05010203 -+#define ID1_AST2620A1 0x05010203 -+#define ID0_AST2620A2 0x05010203 -+#define ID1_AST2620A2 0x05020203 -+#define ID0_AST2620A3 0x05030203 -+#define ID1_AST2620A3 0x05030203 -+#define ID0_AST2605A2 0x05010103 -+#define ID1_AST2605A2 0x05020103 -+#define ID0_AST2605A3 0x05030103 -+#define ID1_AST2605A3 0x05030103 -+#define ID0_AST2625A3 0x05030403 -+#define ID1_AST2625A3 0x05030403 ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+#define OTP_PROTECT_KEY 0x0 -+#define OTP_PASSWD 0x349fe38a -+#define OTP_COMMAND 0x4 -+#define OTP_TIMING 0x8 -+#define OTP_ADDR 0x10 -+#define OTP_STATUS 0x14 -+#define OTP_COMPARE_1 0x20 -+#define OTP_COMPARE_2 0x24 -+#define OTP_COMPARE_3 0x28 -+#define OTP_COMPARE_4 0x2c -+#define SW_REV_ID0 0x68 -+#define SW_REV_ID1 0x6c -+#define SEC_KEY_NUM 0x78 -+#define RETRY 20 ++ reg = FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_LEN, len) ++ | ESPI_PERIF_PC_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_PERIF_PC_TX_CTRL); + -+struct aspeed_otp { -+ struct miscdevice miscdev; -+ void __iomem *reg_base; -+ bool is_open; -+ u32 otp_ver; -+ u32 *data; -+}; ++ rc = 0; + -+static DEFINE_SPINLOCK(otp_state_lock); ++free_n_out: ++ vfree(pkt); + -+static inline u32 aspeed_otp_read(struct aspeed_otp *ctx, u32 reg) -+{ -+ int val; ++unlock_n_out: ++ mutex_unlock(&perif->pc_tx_mtx); + -+ val = readl(ctx->reg_base + reg); -+ // printk("read:reg = 0x%08x, val = 0x%08x\n", reg, val); -+ return val; ++ return rc; +} + -+static inline void aspeed_otp_write(struct aspeed_otp *ctx, u32 val, u32 reg) ++static long ast2500_espi_perif_np_put_tx(struct file *fp, ++ struct aspeed_espi_perif *perif, ++ struct aspeed_espi_ioc *ioc) +{ -+ // printk("write:reg = 0x%08x, val = 0x%08x\n", reg, val); -+ writel(val, ctx->reg_base + reg); -+} ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+static uint32_t chip_version(u32 revid0, u32 revid1) -+{ -+ if (revid0 == ID0_AST2600A0 && revid1 == ID1_AST2600A0) { -+ /* AST2600-A0 */ -+ return OTP_A0; -+ } else if (revid0 == ID0_AST2600A1 && revid1 == ID1_AST2600A1) { -+ /* AST2600-A1 */ -+ return OTP_A1; -+ } else if (revid0 == ID0_AST2600A2 && revid1 == ID1_AST2600A2) { -+ /* AST2600-A2 */ -+ return OTP_A2; -+ } else if (revid0 == ID0_AST2600A3 && revid1 == ID1_AST2600A3) { -+ /* AST2600-A3 */ -+ return OTP_A3; -+ } else if (revid0 == ID0_AST2620A1 && revid1 == ID1_AST2620A1) { -+ /* AST2620-A1 */ -+ return OTP_A1; -+ } else if (revid0 == ID0_AST2620A2 && revid1 == ID1_AST2620A2) { -+ /* AST2620-A2 */ -+ return OTP_A2; -+ } else if (revid0 == ID0_AST2620A3 && revid1 == ID1_AST2620A3) { -+ /* AST2620-A3 */ -+ return OTP_A3; -+ } else if (revid0 == ID0_AST2605A2 && revid1 == ID1_AST2605A2) { -+ /* AST2605-A2 */ -+ return OTP_A2; -+ } else if (revid0 == ID0_AST2605A3 && revid1 == ID1_AST2605A3) { -+ /* AST2605-A3 */ -+ return OTP_A3; -+ } else if (revid0 == ID0_AST2625A3 && revid1 == ID1_AST2625A3) { -+ /* AST2605-A3 */ -+ return OTP_A3; ++ espi = container_of(perif, struct aspeed_espi, perif); ++ ++ if (!mutex_trylock(&perif->np_tx_mtx)) ++ return -EAGAIN; ++ ++ reg = readl(espi->regs + ESPI_PERIF_NP_TX_CTRL); ++ if (reg & ESPI_PERIF_NP_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_n_out; + } -+ return -1; -+} + -+static void wait_complete(struct aspeed_otp *ctx) -+{ -+ int reg; -+ int i = 0; ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_n_out; ++ } + -+ do { -+ reg = aspeed_otp_read(ctx, OTP_STATUS); -+ if ((reg & 0x6) == 0x6) -+ i++; -+ } while (i != 2); -+} ++ hdr = (struct espi_comm_hdr *)pkt; + -+static void otp_write(struct aspeed_otp *ctx, u32 otp_addr, u32 val) -+{ -+ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address -+ aspeed_otp_write(ctx, val, OTP_COMPARE_1); //write val -+ aspeed_otp_write(ctx, 0x23b1e362, OTP_COMMAND); //write command -+ wait_complete(ctx); -+} ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+static void otp_soak(struct aspeed_otp *ctx, int soak) -+{ -+ if (ctx->otp_ver == OTP_A2 || ctx->otp_ver == OTP_A3) { -+ switch (soak) { -+ case 0: //default -+ otp_write(ctx, 0x3000, 0x0); // Write MRA -+ otp_write(ctx, 0x5000, 0x0); // Write MRB -+ otp_write(ctx, 0x1000, 0x0); // Write MR -+ break; -+ case 1: //normal program -+ otp_write(ctx, 0x3000, 0x1320); // Write MRA -+ otp_write(ctx, 0x5000, 0x1008); // Write MRB -+ otp_write(ctx, 0x1000, 0x0024); // Write MR -+ aspeed_otp_write(ctx, 0x04191388, OTP_TIMING); // 200us -+ break; -+ case 2: //soak program -+ otp_write(ctx, 0x3000, 0x1320); // Write MRA -+ otp_write(ctx, 0x5000, 0x0007); // Write MRB -+ otp_write(ctx, 0x1000, 0x0100); // Write MR -+ aspeed_otp_write(ctx, 0x04193a98, OTP_TIMING); // 600us -+ break; -+ } ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (perif->dma.enable) { ++ memcpy(perif->dma.np_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); + } else { -+ switch (soak) { -+ case 0: //default -+ otp_write(ctx, 0x3000, 0x0); // Write MRA -+ otp_write(ctx, 0x5000, 0x0); // Write MRB -+ otp_write(ctx, 0x1000, 0x0); // Write MR -+ break; -+ case 1: //normal program -+ otp_write(ctx, 0x3000, 0x4021); // Write MRA -+ otp_write(ctx, 0x5000, 0x302f); // Write MRB -+ otp_write(ctx, 0x1000, 0x4020); // Write MR -+ aspeed_otp_write(ctx, 0x04190760, OTP_TIMING); // 75us -+ break; -+ case 2: //soak program -+ otp_write(ctx, 0x3000, 0x4021); // Write MRA -+ otp_write(ctx, 0x5000, 0x1027); // Write MRB -+ otp_write(ctx, 0x1000, 0x4820); // Write MR -+ aspeed_otp_write(ctx, 0x041930d4, OTP_TIMING); // 500us -+ break; -+ } ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_PERIF_NP_TX_DATA); + } + -+ wait_complete(ctx); -+} ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+static int verify_bit(struct aspeed_otp *ctx, u32 otp_addr, int bit_offset, int value) -+{ -+ u32 ret[2]; ++ reg = FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_LEN, len) ++ | ESPI_PERIF_NP_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_PERIF_NP_TX_CTRL); + -+ if (otp_addr % 2 == 0) -+ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //Read address -+ else -+ aspeed_otp_write(ctx, otp_addr - 1, OTP_ADDR); //Read address ++ rc = 0; + -+ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read -+ wait_complete(ctx); -+ ret[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); -+ ret[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); ++free_n_out: ++ vfree(pkt); + -+ if (otp_addr % 2 == 0) { -+ if (((ret[0] >> bit_offset) & 1) == value) -+ return 0; -+ else -+ return -1; -+ } else { -+ if (((ret[1] >> bit_offset) & 1) == value) -+ return 0; -+ else -+ return -1; -+ } ++unlock_n_out: ++ mutex_unlock(&perif->np_tx_mtx); ++ ++ return rc; +} + -+static void otp_prog(struct aspeed_otp *ctx, u32 otp_addr, u32 prog_bit) ++static long ast2500_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ otp_write(ctx, 0x0, prog_bit); -+ aspeed_otp_write(ctx, otp_addr, OTP_ADDR); //write address -+ aspeed_otp_write(ctx, prog_bit, OTP_COMPARE_1); //write data -+ aspeed_otp_write(ctx, 0x23b1e364, OTP_COMMAND); //write command -+ wait_complete(ctx); ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi_ioc ioc; ++ ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); ++ ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) ++ return -EFAULT; ++ ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case ASPEED_ESPI_PERIF_PC_GET_RX: ++ return ast2500_espi_perif_pc_get_rx(fp, perif, &ioc); ++ case ASPEED_ESPI_PERIF_PC_PUT_TX: ++ return ast2500_espi_perif_pc_put_tx(fp, perif, &ioc); ++ case ASPEED_ESPI_PERIF_NP_PUT_TX: ++ return ast2500_espi_perif_np_put_tx(fp, perif, &ioc); ++ default: ++ break; ++ }; ++ ++ return -EINVAL; +} + -+static void _otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) ++static int ast2500_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) +{ -+ int prog_bit; ++ struct aspeed_espi_perif *perif; ++ unsigned long vm_size; ++ pgprot_t vm_prot; + -+ if (prog_address % 2 == 0) { -+ if (value) -+ prog_bit = ~(0x1 << bit_offset); -+ else -+ return; -+ } else { -+ if (ctx->otp_ver != OTP_A3) -+ prog_address |= 1 << 15; -+ if (!value) -+ prog_bit = 0x1 << bit_offset; -+ else -+ return; -+ } -+ otp_prog(ctx, prog_address, prog_bit); ++ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); ++ if (!perif->mcyc.enable) ++ return -EPERM; ++ ++ vm_size = vma->vm_end - vma->vm_start; ++ vm_prot = vma->vm_page_prot; ++ ++ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > perif->mcyc.size) ++ return -EINVAL; ++ ++ vm_prot = pgprot_noncached(vm_prot); ++ ++ if (remap_pfn_range(vma, vma->vm_start, ++ (perif->mcyc.taddr >> PAGE_SHIFT) + vma->vm_pgoff, ++ vm_size, vm_prot)) ++ return -EAGAIN; ++ ++ return 0; +} + -+static int otp_prog_bit(struct aspeed_otp *ctx, u32 value, u32 prog_address, u32 bit_offset) ++static const struct file_operations ast2500_espi_perif_fops = { ++ .owner = THIS_MODULE, ++ .mmap = ast2500_espi_perif_mmap, ++ .unlocked_ioctl = ast2500_espi_perif_ioctl, ++}; ++ ++static void ast2500_espi_perif_isr(struct aspeed_espi *espi) +{ -+ int pass; -+ int i; ++ struct aspeed_espi_perif *perif; ++ unsigned long flags; ++ u32 sts; + -+ otp_soak(ctx, 1); -+ _otp_prog_bit(ctx, value, prog_address, bit_offset); -+ pass = 0; ++ perif = &espi->perif; + -+ for (i = 0; i < RETRY; i++) { -+ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { -+ otp_soak(ctx, 2); -+ _otp_prog_bit(ctx, value, prog_address, bit_offset); -+ if (verify_bit(ctx, prog_address, bit_offset, value) != 0) { -+ otp_soak(ctx, 1); -+ } else { -+ pass = 1; -+ break; -+ } -+ } else { -+ pass = 1; -+ break; -+ } ++ sts = readl(espi->regs + ESPI_INT_STS); ++ ++ if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { ++ writel(ESPI_INT_STS_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ ++ spin_lock_irqsave(&perif->lock, flags); ++ perif->rx_ready = true; ++ spin_unlock_irqrestore(&perif->lock, flags); ++ ++ wake_up_interruptible(&perif->wq); + } -+ otp_soak(ctx, 0); -+ return pass; +} + -+static void otp_read_conf_dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) ++static void ast2500_espi_perif_reset(struct aspeed_espi *espi) +{ -+ u32 config_offset; ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ u32 reg, mask; + -+ config_offset = 0x800; -+ config_offset |= (offset / 8) * 0x200; -+ config_offset |= (offset % 8) * 0x2; ++ dev = espi->dev; + -+ aspeed_otp_write(ctx, config_offset, OTP_ADDR); //Read address -+ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read -+ wait_complete(ctx); -+ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); -+} ++ perif = &espi->perif; + -+static void otp_read_conf(struct aspeed_otp *ctx, u32 offset, u32 len) -+{ -+ int i, j; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_PERIF); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); + -+ otp_soak(ctx, 0); -+ for (i = offset, j = 0; j < len; i++, j++) -+ otp_read_conf_dw(ctx, i, &ctx->data[j]); -+} ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_SW_RST ++ | ESPI_CTRL_PERIF_NP_RX_SW_RST ++ | ESPI_CTRL_PERIF_PC_TX_SW_RST ++ | ESPI_CTRL_PERIF_PC_RX_SW_RST ++ | ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+static void otp_read_data_2dw(struct aspeed_otp *ctx, u32 offset, u32 *buf) -+{ -+ aspeed_otp_write(ctx, offset, OTP_ADDR); //Read address -+ aspeed_otp_write(ctx, 0x23b1e361, OTP_COMMAND); //trigger read -+ wait_complete(ctx); -+ buf[0] = aspeed_otp_read(ctx, OTP_COMPARE_1); -+ buf[1] = aspeed_otp_read(ctx, OTP_COMPARE_2); -+} ++ udelay(1); + -+static void otp_read_data(struct aspeed_otp *ctx, u32 offset, u32 len) -+{ -+ int i, j; -+ u32 ret[2]; ++ reg |= (ESPI_CTRL_PERIF_NP_TX_SW_RST ++ | ESPI_CTRL_PERIF_NP_RX_SW_RST ++ | ESPI_CTRL_PERIF_PC_TX_SW_RST ++ | ESPI_CTRL_PERIF_PC_RX_SW_RST); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ otp_soak(ctx, 0); ++ if (perif->mcyc.enable) { ++ mask = ~(perif->mcyc.size - 1); ++ writel(PERIF_MCYC_UNLOCK, espi->regs + ESPI_PERIF_MCYC_MASK); ++ writel(mask, espi->regs + ESPI_PERIF_MCYC_MASK); + -+ i = offset; -+ j = 0; -+ if (offset % 2) { -+ otp_read_data_2dw(ctx, i - 1, ret); -+ ctx->data[0] = ret[1]; -+ i++; -+ j++; ++ writel(perif->mcyc.saddr, espi->regs + ESPI_PERIF_MCYC_SADDR); ++ writel(perif->mcyc.taddr, espi->regs + ESPI_PERIF_MCYC_TADDR); + } -+ for (; j < len; i += 2, j += 2) -+ otp_read_data_2dw(ctx, i, &ctx->data[j]); ++ ++ if (perif->dma.enable) { ++ writel(perif->dma.np_tx_addr, espi->regs + ESPI_PERIF_NP_TX_DMA); ++ writel(perif->dma.pc_tx_addr, espi->regs + ESPI_PERIF_PC_TX_DMA); ++ writel(perif->dma.pc_rx_addr, espi->regs + ESPI_PERIF_PC_RX_DMA); ++ ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); ++ } ++ ++ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_PERIF_PC_RX_CMPLT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_PERIF_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); +} + -+static int otp_prog_data(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) ++int ast2500_espi_perif_probe(struct aspeed_espi *espi) +{ -+ u32 read[2]; -+ int otp_bit; ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ int rc; + -+ if (dw_offset % 2 == 0) { -+ otp_read_data_2dw(ctx, dw_offset, read); -+ otp_bit = (read[0] >> bit_offset) & 0x1; ++ dev = espi->dev; + -+ if (otp_bit == 1 && value == 0) { -+ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); -+ pr_err("OTP is programed, which can't be cleaned\n"); -+ return -EINVAL; ++ perif = &espi->perif; ++ ++ init_waitqueue_head(&perif->wq); ++ ++ spin_lock_init(&perif->lock); ++ ++ mutex_init(&perif->np_tx_mtx); ++ mutex_init(&perif->pc_tx_mtx); ++ mutex_init(&perif->pc_rx_mtx); ++ ++ perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); ++ if (perif->mcyc.enable) { ++ rc = of_property_read_u32(dev->of_node, "perif-mcyc-src-addr", (u32 *)&perif->mcyc.saddr); ++ if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); ++ return -ENODEV; + } -+ } else { -+ otp_read_data_2dw(ctx, dw_offset - 1, read); -+ otp_bit = (read[1] >> bit_offset) & 0x1; + -+ if (otp_bit == 0 && value == 1) { -+ pr_err("OTPDATA%X[%X] = 1\n", dw_offset, bit_offset); -+ pr_err("OTP is programed, which can't be writen\n"); ++ rc = of_property_read_u32(dev->of_node, "perif-mcyc-size", (u32 *)&perif->mcyc.size); ++ if (rc || !IS_ALIGNED(perif->mcyc.size, PERIF_MCYC_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); + return -EINVAL; + } -+ } -+ if (otp_bit == value) { -+ pr_err("OTPDATA%X[%X] = %d\n", dw_offset, bit_offset, value); -+ pr_err("No need to program\n"); -+ return 0; -+ } + -+ return otp_prog_bit(ctx, value, dw_offset, bit_offset); -+} ++ perif->mcyc.virt = dmam_alloc_coherent(dev, perif->mcyc.size, ++ &perif->mcyc.taddr, GFP_KERNEL); ++ if (!perif->mcyc.virt) { ++ dev_err(dev, "cannot allocate memory cycle\n"); ++ return -ENOMEM; ++ } ++ } + -+static int otp_prog_conf(struct aspeed_otp *ctx, u32 value, u32 dw_offset, u32 bit_offset) -+{ -+ u32 read; -+ u32 prog_address = 0; -+ int otp_bit; ++ perif->dma.enable = of_property_read_bool(dev->of_node, "perif-dma-mode"); ++ if (perif->dma.enable) { ++ perif->dma.pc_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.pc_tx_addr, GFP_KERNEL); ++ if (!perif->dma.pc_tx_virt) { ++ dev_err(dev, "cannot allocate posted TX DMA buffer\n"); ++ return -ENOMEM; ++ } + -+ otp_read_conf_dw(ctx, dw_offset, &read); ++ perif->dma.pc_rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.pc_rx_addr, GFP_KERNEL); ++ if (!perif->dma.pc_rx_virt) { ++ dev_err(dev, "cannot allocate posted RX DMA buffer\n"); ++ return -ENOMEM; ++ } + -+ prog_address = 0x800; -+ prog_address |= (dw_offset / 8) * 0x200; -+ prog_address |= (dw_offset % 8) * 0x2; -+ otp_bit = (read >> bit_offset) & 0x1; -+ if (otp_bit == value) { -+ pr_err("OTPCFG%X[%X] = %d\n", dw_offset, bit_offset, value); -+ pr_err("No need to program\n"); -+ return 0; ++ perif->dma.np_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, ++ &perif->dma.np_tx_addr, GFP_KERNEL); ++ if (!perif->dma.np_tx_virt) { ++ dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); ++ return -ENOMEM; ++ } + } -+ if (otp_bit == 1 && value == 0) { -+ pr_err("OTPCFG%X[%X] = 1\n", dw_offset, bit_offset); -+ pr_err("OTP is programed, which can't be clean\n"); -+ return -EINVAL; ++ ++ perif->mdev.parent = dev; ++ perif->mdev.minor = MISC_DYNAMIC_MINOR; ++ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral", DEVICE_NAME); ++ perif->mdev.fops = &ast2500_espi_perif_fops; ++ rc = misc_register(&perif->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", perif->mdev.name); ++ return rc; + } + -+ return otp_prog_bit(ctx, value, prog_address, bit_offset); ++ ast2500_espi_perif_reset(espi); ++ ++ return 0; +} + -+struct aspeed_otp *glob_ctx; ++int ast2500_espi_perif_remove(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ u32 reg; ++ ++ dev = espi->dev; ++ ++ perif = &espi->perif; + -+void otp_read_data_buf(u32 offset, u32 *buf, u32 len) -+{ -+ int i, j; -+ u32 ret[2]; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_PERIF); ++ writel(reg, espi->regs + ESPI_INT_EN); + -+ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ otp_soak(glob_ctx, 0); ++ if (perif->mcyc.enable) ++ dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, ++ perif->mcyc.taddr); + -+ i = offset; -+ j = 0; -+ if (offset % 2) { -+ otp_read_data_2dw(glob_ctx, i - 1, ret); -+ buf[0] = ret[1]; -+ i++; -+ j++; ++ if (perif->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.np_tx_virt, ++ perif->dma.np_tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_tx_virt, ++ perif->dma.pc_tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_rx_virt, ++ perif->dma.pc_rx_addr); + } -+ for (; j < len; i += 2, j += 2) -+ otp_read_data_2dw(glob_ctx, i, &buf[j]); -+ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); -+} -+EXPORT_SYMBOL(otp_read_data_buf); + -+void otp_read_conf_buf(u32 offset, u32 *buf, u32 len) -+{ -+ int i, j; ++ mutex_destroy(&perif->np_tx_mtx); ++ mutex_destroy(&perif->pc_tx_mtx); ++ mutex_destroy(&perif->pc_rx_mtx); + -+ aspeed_otp_write(glob_ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ otp_soak(glob_ctx, 0); -+ for (i = offset, j = 0; j < len; i++, j++) -+ otp_read_conf_dw(glob_ctx, i, &buf[j]); -+ aspeed_otp_write(glob_ctx, 0, OTP_PROTECT_KEY); ++ misc_deregister(&perif->mdev); ++ ++ return 0; +} -+EXPORT_SYMBOL(otp_read_conf_buf); + -+static long otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++/* virtual wire channel (CH1) */ ++static long ast2500_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); -+ void __user *argp = (void __user *)arg; -+ struct otp_read xfer; -+ struct otp_prog prog; -+ u32 reg_read[2]; -+ int ret = 0; -+ -+ switch (cmd) { -+ case ASPEED_OTP_READ_DATA: -+ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) -+ return -EFAULT; -+ if ((xfer.offset + xfer.len) > 0x800) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } ++ struct aspeed_espi_vw *vw; ++ struct aspeed_espi *espi; ++ u32 gpio; + -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ otp_read_data(ctx, xfer.offset, xfer.len); -+ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); ++ vw = container_of(fp->private_data, struct aspeed_espi_vw, mdev); ++ espi = container_of(vw, struct aspeed_espi, vw); ++ gpio = vw->gpio.val0; + -+ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) -+ return -EFAULT; -+ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) ++ switch (cmd) { ++ case ASPEED_ESPI_VW_GET_GPIO_VAL: ++ if (put_user(gpio, (u32 __user *)arg)) + return -EFAULT; + break; -+ case ASPEED_OTP_READ_CONF: -+ if (copy_from_user(&xfer, argp, sizeof(struct otp_read))) ++ case ASPEED_ESPI_VW_PUT_GPIO_VAL: ++ if (get_user(gpio, (u32 __user *)arg)) + return -EFAULT; -+ if ((xfer.offset + xfer.len) > 0x800) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } -+ -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ otp_read_conf(ctx, xfer.offset, xfer.len); -+ aspeed_otp_write(ctx, 0, OTP_PROTECT_KEY); + -+ if (copy_to_user(xfer.data, ctx->data, xfer.len * 4)) -+ return -EFAULT; -+ if (copy_to_user(argp, &xfer, sizeof(struct otp_read))) -+ return -EFAULT; -+ break; -+ case ASPEED_OTP_PROG_DATA: -+ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) -+ return -EFAULT; -+ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } -+ if (prog.dw_offset >= 0x800) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ ret = otp_prog_data(ctx, prog.value, prog.dw_offset, prog.bit_offset); -+ break; -+ case ASPEED_OTP_PROG_CONF: -+ if (copy_from_user(&prog, argp, sizeof(struct otp_prog))) -+ return -EFAULT; -+ if (prog.bit_offset >= 32 || (prog.value != 0 && prog.value != 1)) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } -+ if (prog.dw_offset >= 0x20) { -+ pr_err("out of range"); -+ return -EINVAL; -+ } -+ aspeed_otp_write(ctx, OTP_PASSWD, OTP_PROTECT_KEY); -+ ret = otp_prog_conf(ctx, prog.value, prog.dw_offset, prog.bit_offset); -+ break; -+ case ASPEED_OTP_VER: -+ if (copy_to_user(argp, &ctx->otp_ver, sizeof(u32))) -+ return -EFAULT; -+ break; -+ case ASPEED_OTP_SW_RID: -+ reg_read[0] = aspeed_otp_read(ctx, SW_REV_ID0); -+ reg_read[1] = aspeed_otp_read(ctx, SW_REV_ID1); -+ if (copy_to_user(argp, reg_read, sizeof(u32) * 2)) -+ return -EFAULT; -+ break; -+ case ASPEED_SEC_KEY_NUM: -+ reg_read[0] = aspeed_otp_read(ctx, SEC_KEY_NUM) & 7; -+ if (copy_to_user(argp, reg_read, sizeof(u32))) -+ return -EFAULT; ++ writel(gpio, espi->regs + ESPI_VW_GPIO_VAL); + break; -+ } -+ return ret; ++ default: ++ return -EINVAL; ++ }; ++ ++ return 0; +} + -+static int otp_open(struct inode *inode, struct file *file) ++static const struct file_operations ast2500_espi_vw_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2500_espi_vw_ioctl, ++}; ++ ++static void ast2500_espi_vw_isr(struct aspeed_espi *espi) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ struct aspeed_espi_vw *vw; ++ u32 reg, sts, sts_sysevt; + -+ spin_lock(&otp_state_lock); ++ vw = &espi->vw; + -+ if (ctx->is_open) { -+ spin_unlock(&otp_state_lock); -+ return -EBUSY; -+ } ++ sts = readl(espi->regs + ESPI_INT_STS); + -+ ctx->is_open = true; ++ if (sts & ESPI_INT_STS_VW_SYSEVT) { ++ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT_INT_STS); + -+ spin_unlock(&otp_state_lock); ++ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN) { ++ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_HOST_RST_ACK; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT); ++ writel(ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); ++ } + -+ return 0; -+} ++ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN) { ++ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_OOB_RST_ACK; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT); ++ writel(ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); ++ } + -+static int otp_release(struct inode *inode, struct file *file) -+{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); ++ } + -+ spin_lock(&otp_state_lock); ++ if (sts & ESPI_INT_STS_VW_SYSEVT1) { ++ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT1_INT_STS); + -+ ctx->is_open = false; ++ if (sts_sysevt & ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN) { ++ reg = readl(espi->regs + ESPI_VW_SYSEVT1) | ESPI_VW_SYSEVT1_SUSPEND_ACK; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT1); ++ writel(ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN, espi->regs + ESPI_VW_SYSEVT1_INT_STS); ++ } + -+ spin_unlock(&otp_state_lock); ++ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); ++ } + -+ return 0; ++ if (sts & ESPI_INT_STS_VW_GPIO) { ++ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); ++ writel(ESPI_INT_STS_VW_GPIO, espi->regs + ESPI_INT_STS); ++ } +} + -+static const struct file_operations otp_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = otp_ioctl, -+ .open = otp_open, -+ .release = otp_release, -+}; ++static void ast2500_espi_vw_reset(struct aspeed_espi *espi) ++{ ++ u32 reg; ++ struct aspeed_espi_vw *vw = &espi->vw; + -+static const struct of_device_id aspeed_otp_of_matches[] = { -+ { .compatible = "aspeed,ast2600-sbc" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_VW); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_VW, espi->regs + ESPI_INT_STS); + -+static int aspeed_otp_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct regmap *scu; -+ struct aspeed_otp *priv; -+ struct resource *res; -+ u32 revid0, revid1; -+ int rc; ++ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); + -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ glob_ctx = priv; -+ if (!priv) -+ return -ENOMEM; ++ /* Host Reset Warn and OOB Reset Warn system events */ ++ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_T2) ++ | ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN ++ | ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_T2); + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); -+ return -ENOENT; -+ } ++ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_EN) ++ | ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN ++ | ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_EN); + -+ priv->reg_base = devm_ioremap_resource(&pdev->dev, res); -+ if (!priv->reg_base) -+ return -EIO; ++ /* Suspend Warn system event */ ++ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_T0) | ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_T0); + -+ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(dev, "failed to find 2600 SCU regmap\n"); -+ return PTR_ERR(scu); -+ } ++ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_EN) | ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_EN); + -+ regmap_read(scu, ASPEED_REVISION_ID0, &revid0); -+ regmap_read(scu, ASPEED_REVISION_ID1, &revid1); ++ reg = readl(espi->regs + ESPI_INT_EN) ++ | ESPI_INT_EN_VW_GPIO ++ | ESPI_INT_EN_VW_SYSEVT ++ | ESPI_INT_EN_VW_SYSEVT1; ++ writel(reg, espi->regs + ESPI_INT_EN); + -+ priv->otp_ver = chip_version(revid0, revid1); ++ reg = readl(espi->regs + ESPI_VW_SYSEVT) ++ | ESPI_VW_SYSEVT_SLV_BOOT_STS ++ | ESPI_VW_SYSEVT_SLV_BOOT_DONE; ++ writel(reg, espi->regs + ESPI_VW_SYSEVT); + -+ if (priv->otp_ver == -1) { -+ dev_err(dev, "invalid SCU\n"); -+ return -EINVAL; -+ } ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ((vw->gpio.hw_mode) ? 0 : ESPI_CTRL_VW_GPIO_SW) ++ | ESPI_CTRL_VW_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); ++} + -+ priv->data = kmalloc(8192, GFP_KERNEL); -+ if (!priv->data) -+ return -ENOMEM; ++int ast2500_espi_vw_probe(struct aspeed_espi *espi) ++{ ++ int rc; ++ struct device *dev = espi->dev; ++ struct aspeed_espi_vw *vw = &espi->vw; + -+ dev_set_drvdata(dev, priv); ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_STS); + -+ /* Set up the miscdevice */ -+ priv->miscdev.minor = MISC_DYNAMIC_MINOR; -+ priv->miscdev.name = "aspeed-otp"; -+ priv->miscdev.fops = &otp_fops; ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT1_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT1_INT_STS); + -+ /* Register the device */ -+ rc = misc_register(&priv->miscdev); ++ vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); ++ ++ vw->mdev.parent = dev; ++ vw->mdev.minor = MISC_DYNAMIC_MINOR; ++ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw", DEVICE_NAME); ++ vw->mdev.fops = &ast2500_espi_vw_fops; ++ rc = misc_register(&vw->mdev); + if (rc) { -+ dev_err(dev, "Unable to register device\n"); ++ dev_err(dev, "cannot register device %s\n", vw->mdev.name); + return rc; + } + ++ ast2500_espi_vw_reset(espi); ++ + return 0; +} + -+static void aspeed_otp_remove(struct platform_device *pdev) ++int ast2500_espi_vw_remove(struct aspeed_espi *espi) +{ -+ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); ++ struct aspeed_espi_vw *vw; ++ u32 reg; + -+ kfree(ctx->data); -+ misc_deregister(&ctx->miscdev); -+} ++ vw = &espi->vw; + -+static struct platform_driver aspeed_otp_driver = { -+ .probe = aspeed_otp_probe, -+ .remove = aspeed_otp_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_otp_of_matches, -+ }, -+}; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_VW); ++ writel(reg, espi->regs + ESPI_INT_EN); + -+module_platform_driver(aspeed_otp_driver); ++ misc_deregister(&vw->mdev); + -+MODULE_AUTHOR("Neal Liu "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("ASPEED OTP Driver"); -diff --git a/drivers/soc/aspeed/ast2700-otp.c b/drivers/soc/aspeed/ast2700-otp.c ---- a/drivers/soc/aspeed/ast2700-otp.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/ast2700-otp.c 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,565 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2024 Aspeed Technology Inc. -+ */ ++ return 0; ++} + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++/* out-of-band channel (CH2) */ ++static long ast2500_espi_oob_get_rx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; + -+static DEFINE_SPINLOCK(otp_state_lock); ++ espi = container_of(oob, struct aspeed_espi, oob); + -+/*********************** -+ * * -+ * OTP regs definition * -+ * * -+ ***********************/ -+#define OTP_REG_SIZE 0x200 ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&oob->rx_mtx)) ++ return -EAGAIN; + -+#define OTP_PASSWD 0x349fe38a -+#define OTP_CMD_READ 0x23b1e361 -+#define OTP_CMD_PROG 0x23b1e364 -+#define OTP_CMD_PROG_MULTI 0x23b1e365 -+#define OTP_CMD_CMP 0x23b1e363 -+#define OTP_CMD_BIST 0x23b1e368 ++ if (!oob->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&oob->rx_mtx); + -+#define OTP_CMD_OFFSET 0x20 -+#define OTP_MASTER OTP_M0 ++ if (!oob->rx_ready) { ++ rc = wait_event_interruptible(oob->wq, oob->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; ++ } ++ } ++ } + -+#define OTP_KEY 0x0 -+#define OTP_CMD (OTP_MASTER * OTP_CMD_OFFSET + 0x4) -+#define OTP_WDATA_0 (OTP_MASTER * OTP_CMD_OFFSET + 0x8) -+#define OTP_WDATA_1 (OTP_MASTER * OTP_CMD_OFFSET + 0xc) -+#define OTP_WDATA_2 (OTP_MASTER * OTP_CMD_OFFSET + 0x10) -+#define OTP_WDATA_3 (OTP_MASTER * OTP_CMD_OFFSET + 0x14) -+#define OTP_STATUS (OTP_MASTER * OTP_CMD_OFFSET + 0x18) -+#define OTP_ADDR (OTP_MASTER * OTP_CMD_OFFSET + 0x1c) -+#define OTP_RDATA (OTP_MASTER * OTP_CMD_OFFSET + 0x20) ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_OOB_RX_CTRL); ++ cyc = FIELD_GET(ESPI_OOB_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_OOB_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_OOB_RX_CTRL_LEN, reg); + -+#define OTP_DBG00 0x0C4 -+#define OTP_DBG01 0x0C8 -+#define OTP_MASTER_PID 0x0D0 -+#define OTP_ECC_EN 0x0D4 -+#define OTP_CMD_LOCK 0x0D8 -+#define OTP_SW_RST 0x0DC -+#define OTP_SLV_ID 0x0E0 -+#define OTP_PMC_CQ 0x0E4 -+#define OTP_FPGA 0x0EC -+#define OTP_CLR_FPGA 0x0F0 -+#define OTP_REGION_ROM_PATCH 0x100 -+#define OTP_REGION_OTPCFG 0x104 -+#define OTP_REGION_OTPSTRAP 0x108 -+#define OTP_REGION_OTPSTRAP_EXT 0x10C -+#define OTP_REGION_SECURE0 0x120 -+#define OTP_REGION_SECURE0_RANGE 0x124 -+#define OTP_REGION_SECURE1 0x128 -+#define OTP_REGION_SECURE1_RANGE 0x12C -+#define OTP_REGION_SECURE2 0x130 -+#define OTP_REGION_SECURE2_RANGE 0x134 -+#define OTP_REGION_SECURE3 0x138 -+#define OTP_REGION_SECURE3_RANGE 0x13C -+#define OTP_REGION_USR0 0x140 -+#define OTP_REGION_USR0_RANGE 0x144 -+#define OTP_REGION_USR1 0x148 -+#define OTP_REGION_USR1_RANGE 0x14C -+#define OTP_REGION_USR2 0x150 -+#define OTP_REGION_USR2_RANGE 0x154 -+#define OTP_REGION_USR3 0x158 -+#define OTP_REGION_USR3_RANGE 0x15C -+#define OTP_REGION_CALIPTRA_0 0x160 -+#define OTP_REGION_CALIPTRA_0_RANGE 0x164 -+#define OTP_REGION_CALIPTRA_1 0x168 -+#define OTP_REGION_CALIPTRA_1_RANGE 0x16C -+#define OTP_REGION_CALIPTRA_2 0x170 -+#define OTP_REGION_CALIPTRA_2_RANGE 0x174 -+#define OTP_REGION_CALIPTRA_3 0x178 -+#define OTP_REGION_CALIPTRA_3_RANGE 0x17C -+#define OTP_RBP_SOC_SVN 0x180 -+#define OTP_RBP_SOC_KEYRETIRE 0x184 -+#define OTP_RBP_CALIP_SVN 0x188 -+#define OTP_RBP_CALIP_KEYRETIRE 0x18C -+#define OTP_PUF 0x1A0 -+#define OTP_MASTER_ID 0x1B0 -+#define OTP_MASTER_ID_EXT 0x1B4 -+#define OTP_R_MASTER_ID 0x1B8 -+#define OTP_R_MASTER_ID_EXT 0x1BC -+#define OTP_SOC_ECCKEY 0x1C0 -+#define OTP_SEC_BOOT_EN 0x1C4 -+#define OTP_SOC_KEY 0x1C8 -+#define OTP_CALPITRA_MANU_KEY 0x1CC -+#define OTP_CALPITRA_OWNER_KEY 0x1D0 -+#define OTP_FW_ID_LSB 0x1D4 -+#define OTP_FW_ID_MSB 0x1D8 -+#define OTP_CALIP_FMC_SVN 0x1DC -+#define OTP_CALIP_RUNTIME_SVN0 0x1E0 -+#define OTP_CALIP_RUNTIME_SVN1 0x1E4 -+#define OTP_CALIP_RUNTIME_SVN2 0x1E8 -+#define OTP_CALIP_RUNTIME_SVN3 0x1EC -+#define OTP_SVN_WLOCK 0x1F0 -+#define OTP_INTR_EN 0x200 -+#define OTP_INTR_STS 0x204 -+#define OTP_INTR_MID 0x208 -+#define OTP_INTR_FUNC_INFO 0x20C -+#define OTP_INTR_M_INFO 0x210 -+#define OTP_INTR_R_INFO 0x214 ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++ ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } ++ ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+#define OTP_PMC 0x400 -+#define OTP_DAP 0x500 ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; + -+/* OTP status: [0] */ -+#define OTP_STS_IDLE 0x0 -+#define OTP_STS_BUSY 0x1 ++ if (oob->dma.enable) { ++ memcpy(hdr + 1, oob->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ } else { ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; ++ } + -+/* OTP cmd status: [7:4] */ -+#define OTP_GET_CMD_STS(x) (((x) & 0xF0) >> 4) -+#define OTP_STS_PASS 0x0 -+#define OTP_STS_FAIL 0x1 -+#define OTP_STS_CMP_FAIL 0x2 -+#define OTP_STS_REGION_FAIL 0x3 -+#define OTP_STS_MASTER_FAIL 0x4 ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+/* OTP ECC EN */ -+#define ECC_ENABLE 0x1 -+#define ECC_DISABLE 0x0 -+#define ECCBRP_EN BIT(0) ++ spin_lock_irqsave(&oob->lock, flags); + -+#define ROM_REGION_START_ADDR 0x0 -+#define ROM_REGION_END_ADDR 0x3e0 -+#define RBP_REGION_START_ADDR ROM_REGION_END_ADDR -+#define RBP_REGION_END_ADDR 0x400 -+#define CONF_REGION_START_ADDR RBP_REGION_END_ADDR -+#define CONF_REGION_END_ADDR 0x420 -+#define STRAP_REGION_START_ADDR CONF_REGION_END_ADDR -+#define STRAP_REGION_END_ADDR 0x430 -+#define STRAPEXT_REGION_START_ADDR STRAP_REGION_END_ADDR -+#define STRAPEXT_REGION_END_ADDR 0x440 -+#define USER_REGION_START_ADDR STRAPEXT_REGION_END_ADDR -+#define USER_REGION_END_ADDR 0x1000 -+#define SEC_REGION_START_ADDR USER_REGION_END_ADDR -+#define SEC_REGION_END_ADDR 0x1c00 -+#define CAL_REGION_START_ADDR SEC_REGION_END_ADDR -+#define CAL_REGION_END_ADDR 0x1f80 -+#define SW_PUF_REGION_START_ADDR CAL_REGION_END_ADDR -+#define SW_PUF_REGION_END_ADDR 0x1fc0 -+#define HW_PUF_REGION_START_ADDR SW_PUF_REGION_END_ADDR -+#define HW_PUF_REGION_END_ADDR 0x2000 ++ writel(ESPI_OOB_RX_CTRL_SERV_PEND, espi->regs + ESPI_OOB_RX_CTRL); ++ oob->rx_ready = 0; + -+#define OTP_MEMORY_SIZE (HW_PUF_REGION_END_ADDR * 2) ++ spin_unlock_irqrestore(&oob->lock, flags); + -+#define OTP_TIMEOUT_US 10000 ++ rc = 0; + -+/* OTPSTRAP */ -+#define OTPSTRAP0_ADDR STRAP_REGION_START_ADDR -+#define OTPSTRAP14_ADDR (OTPSTRAP0_ADDR + 0xe) ++free_n_out: ++ vfree(pkt); + -+#define OTPTOOL_VERSION(a, b, c) (((a) << 24) + ((b) << 12) + (c)) -+#define OTPTOOL_VERSION_MAJOR(x) (((x) >> 24) & 0xff) -+#define OTPTOOL_VERSION_PATCHLEVEL(x) (((x) >> 12) & 0xfff) -+#define OTPTOOL_VERSION_SUBLEVEL(x) ((x) & 0xfff) -+#define OTPTOOL_COMPT_VERSION 2 ++unlock_mtx_n_out: ++ mutex_unlock(&oob->rx_mtx); + -+enum otp_error_code { -+ OTP_SUCCESS, -+ OTP_READ_FAIL, -+ OTP_PROG_FAIL, -+ OTP_CMP_FAIL, -+}; ++ return rc; ++} + -+enum aspeed_otp_master_id { -+ OTP_M0 = 0, -+ OTP_M1, -+ OTP_M2, -+ OTP_M3, -+ OTP_M4, -+ OTP_M5, -+ OTP_MID_MAX, -+}; ++static long ast2500_espi_oob_put_tx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+struct aspeed_otp { -+ struct miscdevice miscdev; -+ struct device *dev; -+ void __iomem *base; -+ u32 chip_revid0; -+ u32 chip_revid1; -+ bool is_open; -+ int gbl_ecc_en; -+ u8 *data; -+}; ++ espi = container_of(oob, struct aspeed_espi, oob); + -+enum otp_ioctl_cmds { -+ GET_ECC_STATUS = 1, -+ SET_ECC_ENABLE, -+}; ++ if (!mutex_trylock(&oob->tx_mtx)) ++ return -EAGAIN; + -+enum otp_ecc_codes { -+ OTP_ECC_MISMATCH = -1, -+ OTP_ECC_DISABLE = 0, -+ OTP_ECC_ENABLE = 1, -+}; ++ reg = readl(espi->regs + ESPI_OOB_TX_CTRL); ++ if (reg & ESPI_OOB_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_mtx_n_out; ++ } + -+static void otp_unlock(struct device *dev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } + -+ writel(OTP_PASSWD, ctx->base + OTP_KEY); -+} ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+static void otp_lock(struct device *dev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); ++ hdr = (struct espi_comm_hdr *)pkt; + -+ writel(0x1, ctx->base + OTP_KEY); -+} ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+static int wait_complete(struct device *dev) -+{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); -+ int ret; -+ u32 val; ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (oob->dma.enable) { ++ memcpy(oob->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); ++ } else { ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); ++ } + -+ ret = readl_poll_timeout(ctx->base + OTP_STATUS, val, (val == 0x0), -+ 1, OTP_TIMEOUT_US); -+ if (ret) -+ dev_warn(dev, "timeout. sts:0x%x\n", val); ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ return ret; -+} ++ reg = FIELD_PREP(ESPI_OOB_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_OOB_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_OOB_TX_CTRL_LEN, len) ++ | ESPI_OOB_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_OOB_TX_CTRL); + -+static int otp_read_data(struct aspeed_otp *ctx, u32 offset, u16 *data) -+{ -+ struct device *dev = ctx->dev; -+ int ret; ++ rc = 0; + -+ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); -+ writel(offset, ctx->base + OTP_ADDR); -+ writel(OTP_CMD_READ, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_READ_FAIL; ++free_n_out: ++ vfree(pkt); + -+ data[0] = readl(ctx->base + OTP_RDATA); ++unlock_mtx_n_out: ++ mutex_unlock(&oob->tx_mtx); + -+ return 0; ++ return rc; +} + -+static int otp_prog_data(struct aspeed_otp *ctx, u32 offset, u16 data) ++static long ast2500_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct device *dev = ctx->dev; -+ int ret; ++ struct aspeed_espi_oob *oob; ++ struct aspeed_espi_ioc ioc; + -+ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); -+ writel(offset, ctx->base + OTP_ADDR); -+ writel(data, ctx->base + OTP_WDATA_0); -+ writel(OTP_CMD_PROG, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_PROG_FAIL; ++ oob = container_of(fp->private_data, struct aspeed_espi_oob, mdev); + -+ return 0; ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) ++ return -EFAULT; ++ ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case ASPEED_ESPI_OOB_GET_RX: ++ return ast2500_espi_oob_get_rx(fp, oob, &ioc); ++ case ASPEED_ESPI_OOB_PUT_TX: ++ return ast2500_espi_oob_put_tx(fp, oob, &ioc); ++ default: ++ break; ++ }; ++ ++ return -EINVAL; +} + -+static int otp_prog_multi_data(struct aspeed_otp *ctx, u32 offset, u32 *data, int count) ++static const struct file_operations ast2500_espi_oob_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2500_espi_oob_ioctl, ++}; ++ ++static void ast2500_espi_oob_isr(struct aspeed_espi *espi) +{ -+ struct device *dev = ctx->dev; -+ int ret; ++ struct aspeed_espi_oob *oob; ++ unsigned long flags; ++ u32 sts; + -+ writel(ctx->gbl_ecc_en, ctx->base + OTP_ECC_EN); -+ writel(offset, ctx->base + OTP_ADDR); -+ for (int i = 0; i < count; i++) -+ writel(data[i], ctx->base + OTP_WDATA_0 + 4 * i); ++ oob = &espi->oob; + -+ writel(OTP_CMD_PROG_MULTI, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_PROG_FAIL; ++ sts = readl(espi->regs + ESPI_INT_STS); + -+ return 0; ++ if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { ++ writel(ESPI_INT_STS_OOB_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ ++ spin_lock_irqsave(&oob->lock, flags); ++ oob->rx_ready = true; ++ spin_unlock_irqrestore(&oob->lock, flags); ++ ++ wake_up_interruptible(&oob->wq); ++ } +} + -+static int aspeed_otp_read(struct aspeed_otp *ctx, int offset, -+ void *buf, int size) ++static void ast2500_espi_oob_reset(struct aspeed_espi *espi) +{ -+ struct device *dev = ctx->dev; -+ u16 *data = buf; -+ int ret; ++ struct aspeed_espi_oob *oob; ++ u32 reg; + -+ otp_unlock(dev); -+ for (int i = 0; i < size; i++) { -+ ret = otp_read_data(ctx, offset + i, data + i); -+ if (ret) { -+ dev_warn(ctx->dev, "read failed\n"); -+ break; -+ } ++ oob = &espi->oob; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_OOB); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_OOB, espi->regs + ESPI_INT_STS); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_OOB_TX_SW_RST ++ | ESPI_CTRL_OOB_RX_SW_RST ++ | ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN ++ | ESPI_CTRL_OOB_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ udelay(1); ++ ++ reg |= (ESPI_CTRL_OOB_TX_SW_RST | ESPI_CTRL_OOB_RX_SW_RST); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ if (oob->dma.enable) { ++ writel(oob->dma.tx_addr, espi->regs + ESPI_OOB_TX_DMA); ++ writel(oob->dma.rx_addr, espi->regs + ESPI_OOB_RX_DMA); ++ ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); + } + -+ otp_lock(dev); -+ return ret; ++ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_OOB_RX_CMPLT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_OOB_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); +} + -+static int aspeed_otp_write(struct aspeed_otp *ctx, int offset, -+ const void *buf, int size) ++int ast2500_espi_oob_probe(struct aspeed_espi *espi) +{ -+ struct device *dev = ctx->dev; -+ u32 *data32 = (u32 *)buf; -+ u16 *data = (u16 *)buf; -+ int ret; ++ struct aspeed_espi_oob *oob; ++ struct device *dev; ++ int rc; + -+ otp_unlock(dev); ++ dev = espi->dev; + -+ if (size == 1) -+ ret = otp_prog_data(ctx, offset, data[0]); -+ else -+ ret = otp_prog_multi_data(ctx, offset, data32, size / 2); ++ oob = &espi->oob; + -+ if (ret) -+ dev_warn(ctx->dev, "prog failed\n"); ++ init_waitqueue_head(&oob->wq); + -+ otp_lock(dev); -+ return ret; -+} ++ spin_lock_init(&oob->lock); + -+static int aspeed_otp_ecc_en(struct aspeed_otp *ctx) -+{ -+ struct device *dev = ctx->dev; -+ int ret = 0; ++ mutex_init(&oob->tx_mtx); ++ mutex_init(&oob->rx_mtx); + -+ /* Check ecc is already enabled */ -+ if (ctx->gbl_ecc_en == 1) -+ return 0; ++ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); ++ if (oob->dma.enable) { ++ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.tx_addr, GFP_KERNEL); ++ if (!oob->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } + -+ otp_unlock(dev); ++ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.rx_addr, GFP_KERNEL); ++ if (!oob->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } ++ } + -+ /* enable cfg ecc */ -+ ret = otp_prog_data(ctx, OTPSTRAP14_ADDR, 0x1); -+ if (ret) { -+ dev_warn(dev, "%s: prog failed\n", __func__); -+ goto end; ++ oob->mdev.parent = dev; ++ oob->mdev.minor = MISC_DYNAMIC_MINOR; ++ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob", DEVICE_NAME); ++ oob->mdev.fops = &ast2500_espi_oob_fops; ++ rc = misc_register(&oob->mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", oob->mdev.name); ++ return rc; + } + -+ ctx->gbl_ecc_en = 1; -+end: -+ otp_lock(dev); ++ ast2500_espi_oob_reset(espi); + -+ return ret; ++ return 0; +} + -+static long aspeed_otp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++int ast2500_espi_oob_remove(struct aspeed_espi *espi) +{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); -+ void __user *argp = (void __user *)arg; -+ struct otp_revid revid; -+ struct otp_read rdata; -+ struct otp_prog pdata; -+ int ret = 0; -+ -+ switch (cmd) { -+ case ASPEED_OTP_READ_DATA: -+ if (copy_from_user(&rdata, argp, sizeof(struct otp_read))) -+ return -EFAULT; -+ -+ ret = aspeed_otp_read(ctx, rdata.offset, ctx->data, rdata.len); -+ if (ret) -+ return -EFAULT; ++ struct aspeed_espi_oob *oob; ++ struct device *dev; ++ u32 reg; + -+ if (copy_to_user(rdata.data, ctx->data, rdata.len * 2)) -+ return -EFAULT; ++ dev = espi->dev; + -+ break; ++ oob = &espi->oob; + -+ case ASPEED_OTP_PROG_DATA: -+ if (copy_from_user(&pdata, argp, sizeof(struct otp_prog))) -+ return -EFAULT; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_OOB); ++ writel(reg, espi->regs + ESPI_INT_EN); + -+ ret = aspeed_otp_write(ctx, pdata.w_offset, pdata.data, pdata.len); -+ break; ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_OOB_TX_DMA_EN ++ | ESPI_CTRL_OOB_RX_DMA_EN ++ | ESPI_CTRL_OOB_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ case ASPEED_OTP_GET_ECC: -+ if (copy_to_user(argp, &ctx->gbl_ecc_en, sizeof(ctx->gbl_ecc_en))) -+ return -EFAULT; -+ break; ++ if (oob->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.tx_virt, oob->dma.tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.rx_virt, oob->dma.rx_addr); ++ } + -+ case ASPEED_OTP_SET_ECC: -+ ret = aspeed_otp_ecc_en(ctx); -+ break; ++ mutex_destroy(&oob->tx_mtx); ++ mutex_destroy(&oob->rx_mtx); + -+ case ASPEED_OTP_GET_REVID: -+ revid.revid0 = ctx->chip_revid0; -+ revid.revid1 = ctx->chip_revid1; -+ if (copy_to_user(argp, &revid, sizeof(struct otp_revid))) -+ return -EFAULT; -+ break; -+ default: -+ dev_warn(ctx->dev, "cmd 0x%x is not supported\n", cmd); -+ break; -+ } ++ misc_deregister(&oob->mdev); + -+ return ret; ++ return 0; +} + -+static int aspeed_otp_ecc_init(struct device *dev) ++/* flash channel (CH3) */ ++static long ast2500_espi_flash_get_rx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) +{ -+ struct aspeed_otp *ctx = dev_get_drvdata(dev); -+ int ret; -+ u32 val; ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ unsigned long flags; ++ u32 pkt_len; ++ u8 *pkt; ++ int i, rc; + -+ otp_unlock(dev); ++ rc = 0; + -+ /* Check cfg_ecc_en */ -+ writel(0, ctx->base + OTP_ECC_EN); -+ writel(OTPSTRAP14_ADDR, ctx->base + OTP_ADDR); -+ writel(OTP_CMD_READ, ctx->base + OTP_CMD); -+ ret = wait_complete(dev); -+ if (ret) -+ return OTP_READ_FAIL; ++ espi = container_of(flash, struct aspeed_espi, flash); + -+ val = readl(ctx->base + OTP_RDATA); -+ if (val & 0x1) -+ ctx->gbl_ecc_en = 0x1; -+ else -+ ctx->gbl_ecc_en = 0x0; ++ if (fp->f_flags & O_NONBLOCK) { ++ if (!mutex_trylock(&flash->rx_mtx)) ++ return -EAGAIN; + -+ otp_lock(dev); ++ if (!flash->rx_ready) { ++ rc = -ENODATA; ++ goto unlock_mtx_n_out; ++ } ++ } else { ++ mutex_lock(&flash->rx_mtx); ++ ++ if (!flash->rx_ready) { ++ rc = wait_event_interruptible(flash->wq, flash->rx_ready); ++ if (rc == -ERESTARTSYS) { ++ rc = -EINTR; ++ goto unlock_mtx_n_out; ++ } ++ } ++ } ++ ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ reg = readl(espi->regs + ESPI_FLASH_RX_CTRL); ++ cyc = FIELD_GET(ESPI_FLASH_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_FLASH_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_FLASH_RX_CTRL_LEN, reg); ++ ++ /* ++ * calculate the length of the rest part of the ++ * eSPI packet to be read from HW and copied to ++ * user space. ++ */ ++ switch (cyc) { ++ case ESPI_FLASH_WRITE: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_READ: ++ case ESPI_FLASH_ERASE: ++ pkt_len = sizeof(struct espi_flash_rwe); ++ break; ++ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: ++ case ESPI_FLASH_SUC_CMPLT_D_FIRST: ++ case ESPI_FLASH_SUC_CMPLT_D_LAST: ++ case ESPI_FLASH_SUC_CMPLT_D_ONLY: ++ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + ++ sizeof(struct espi_flash_cmplt); ++ break; ++ case ESPI_FLASH_SUC_CMPLT: ++ case ESPI_FLASH_UNSUC_CMPLT: ++ pkt_len = sizeof(struct espi_flash_cmplt); ++ break; ++ default: ++ rc = -EFAULT; ++ goto unlock_mtx_n_out; ++ } + -+ return 0; -+} ++ if (ioc->pkt_len < pkt_len) { ++ rc = -EINVAL; ++ goto unlock_mtx_n_out; ++ } + -+static int aspeed_otp_open(struct inode *inode, struct file *file) -+{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ pkt = vmalloc(pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; ++ } + -+ spin_lock(&otp_state_lock); ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = cyc; ++ hdr->tag = tag; ++ hdr->len_h = len >> 8; ++ hdr->len_l = len & 0xff; + -+ if (ctx->is_open) { -+ spin_unlock(&otp_state_lock); -+ return -EBUSY; ++ if (flash->dma.enable) { ++ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ } else { ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_FLASH_RX_DATA) & 0xff; + } + -+ ctx->is_open = true; ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } + -+ spin_unlock(&otp_state_lock); ++ spin_lock_irqsave(&flash->lock, flags); + -+ return 0; -+} ++ writel(ESPI_FLASH_RX_CTRL_SERV_PEND, espi->regs + ESPI_FLASH_RX_CTRL); ++ flash->rx_ready = 0; + -+static int aspeed_otp_release(struct inode *inode, struct file *file) -+{ -+ struct miscdevice *c = file->private_data; -+ struct aspeed_otp *ctx = container_of(c, struct aspeed_otp, miscdev); ++ spin_unlock_irqrestore(&flash->lock, flags); + -+ spin_lock(&otp_state_lock); ++ rc = 0; + -+ ctx->is_open = false; ++free_n_out: ++ vfree(pkt); + -+ spin_unlock(&otp_state_lock); ++unlock_mtx_n_out: ++ mutex_unlock(&flash->rx_mtx); + -+ return 0; ++ return rc; +} + -+static const struct file_operations otp_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = aspeed_otp_ioctl, -+ .open = aspeed_otp_open, -+ .release = aspeed_otp_release, -+}; ++static long ast2500_espi_flash_put_tx(struct file *fp, ++ struct aspeed_espi_flash *flash, ++ struct aspeed_espi_ioc *ioc) ++{ ++ u32 reg, cyc, tag, len; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u8 *pkt; ++ int i, rc; + -+static const struct of_device_id aspeed_otp_of_matches[] = { -+ { .compatible = "aspeed,ast2700-otp" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, aspeed_otp_of_matches); ++ espi = container_of(flash, struct aspeed_espi, flash); + -+static int aspeed_otp_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct regmap *scu0, *scu1; -+ struct aspeed_otp *priv; -+ struct resource *res; -+ int rc; ++ if (!mutex_trylock(&flash->tx_mtx)) ++ return -EAGAIN; + -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; ++ reg = readl(espi->regs + ESPI_FLASH_TX_CTRL); ++ if (reg & ESPI_FLASH_TX_CTRL_TRIG_PEND) { ++ rc = -EBUSY; ++ goto unlock_mtx_n_out; ++ } + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); -+ return -ENOENT; ++ pkt = vmalloc(ioc->pkt_len); ++ if (!pkt) { ++ rc = -ENOMEM; ++ goto unlock_mtx_n_out; + } + -+ priv->base = devm_ioremap_resource(&pdev->dev, res); -+ if (!priv->base) -+ return -EIO; ++ hdr = (struct espi_comm_hdr *)pkt; + -+ scu0 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu0"); -+ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu1"); -+ if (IS_ERR(scu0) || IS_ERR(scu1)) { -+ dev_err(dev, "failed to find SCU regmap\n"); -+ return PTR_ERR(scu0) || PTR_ERR(scu1); ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; + } + -+ regmap_read(scu0, 0x0, &priv->chip_revid0); -+ regmap_read(scu1, 0x0, &priv->chip_revid1); -+ -+ priv->dev = dev; -+ dev_set_drvdata(dev, priv); ++ /* ++ * common header (i.e. cycle type, tag, and length) ++ * part is written to HW registers ++ */ ++ if (flash->dma.enable) { ++ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ dma_wmb(); ++ } else { ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_FLASH_TX_DATA); ++ } + -+ /* OTP ECC init */ -+ rc = aspeed_otp_ecc_init(dev); -+ if (rc) -+ return -EIO; ++ cyc = hdr->cyc; ++ tag = hdr->tag; ++ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ priv->data = kmalloc(OTP_MEMORY_SIZE, GFP_KERNEL); -+ if (!priv->data) -+ return -ENOMEM; ++ reg = FIELD_PREP(ESPI_FLASH_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_FLASH_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_FLASH_TX_CTRL_LEN, len) ++ | ESPI_FLASH_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_FLASH_TX_CTRL); + -+ /* Set up the miscdevice */ -+ priv->miscdev.minor = MISC_DYNAMIC_MINOR; -+ priv->miscdev.name = "aspeed-otp"; -+ priv->miscdev.fops = &otp_fops; ++ rc = 0; + -+ /* Register the device */ -+ rc = misc_register(&priv->miscdev); -+ if (rc) { -+ dev_err(dev, "Unable to register device\n"); -+ return rc; -+ } ++free_n_out: ++ vfree(pkt); + -+ dev_info(dev, "Aspeed OTP driver successfully registered\n"); ++unlock_mtx_n_out: ++ mutex_unlock(&flash->tx_mtx); + -+ return 0; ++ return rc; +} + -+static void aspeed_otp_remove(struct platform_device *pdev) ++static long ast2500_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ -+ struct aspeed_otp *ctx = dev_get_drvdata(&pdev->dev); -+ -+ kfree(ctx->data); -+ misc_deregister(&ctx->miscdev); -+} -+ -+static struct platform_driver aspeed_otp_driver = { -+ .probe = aspeed_otp_probe, -+ .remove = aspeed_otp_remove, -+ .driver = { -+ .name = KBUILD_MODNAME, -+ .of_match_table = aspeed_otp_of_matches, -+ }, -+}; -+ -+module_platform_driver(aspeed_otp_driver); -+ -+MODULE_AUTHOR("Neal Liu "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("ASPEED OTP Driver"); -diff --git a/drivers/soc/aspeed/espi/Kconfig b/drivers/soc/aspeed/espi/Kconfig ---- a/drivers/soc/aspeed/espi/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/Kconfig 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,18 @@ -+menu "ASPEED eSPI drivers" ++ struct aspeed_espi_flash *flash; ++ struct aspeed_espi_ioc ioc; + -+config ASPEED_ESPI -+ tristate "ASPEED eSPI slave driver" -+ help -+ Enable driver support for Aspeed AST2700 eSPI engine. The eSPI engine -+ plays as a slave device in BMC to communicate with the Host over -+ the eSPI interface. The four eSPI channels, namely peripheral, -+ virtual wire, out-of-band, and flash are supported. ++ flash = container_of(fp->private_data, struct aspeed_espi_flash, mdev); + -+config AST2700_RTC_OVER_ESPI -+ tristate "ASPEED AST2700 RTC over eSPI drvier" -+ depends on HAS_IOMEM -+ help -+ Enable driver support for Aspeed AST2700 RTC over eSPI function. -+ Periodically copies RAM content from a RTC tm to a memory-mapped eSPI region. ++ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) ++ return -EFAULT; + -+endmenu -diff --git a/drivers/soc/aspeed/espi/Makefile b/drivers/soc/aspeed/espi/Makefile ---- a/drivers/soc/aspeed/espi/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/Makefile 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,6 @@ -+obj-$(CONFIG_ASPEED_ESPI) += \ -+ ast2700-espi.o \ -+ ast2600-espi.o \ -+ ast2500-espi.o \ -+ aspeed-espi.o -+obj-$(CONFIG_AST2700_RTC_OVER_ESPI) += ast2700-rtc-over-espi.o -diff --git a/drivers/soc/aspeed/espi/aspeed-espi-comm.h b/drivers/soc/aspeed/espi/aspeed-espi-comm.h ---- a/drivers/soc/aspeed/espi/aspeed-espi-comm.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/aspeed-espi-comm.h 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,207 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * Aspeed eSPI protocol definitions and IOCTL methods -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#ifndef __ASPEED_ESPI_COMM_H__ -+#define __ASPEED_ESPI_COMM_H__ ++ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) ++ return -EINVAL; + -+#include -+#include ++ switch (cmd) { ++ case ASPEED_ESPI_FLASH_GET_RX: ++ return ast2500_espi_flash_get_rx(fp, flash, &ioc); ++ case ASPEED_ESPI_FLASH_PUT_TX: ++ return ast2500_espi_flash_put_tx(fp, flash, &ioc); ++ default: ++ break; ++ }; + -+/* -+ * eSPI cycle type encoding -+ * -+ * Section 5.1 Cycle Types and Packet Format, -+ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. -+ */ -+#define ESPI_PERIF_MEMRD32 0x00 -+#define ESPI_PERIF_MEMRD64 0x02 -+#define ESPI_PERIF_MEMWR32 0x01 -+#define ESPI_PERIF_MEMWR64 0x03 -+#define ESPI_PERIF_MSG 0x10 -+#define ESPI_PERIF_MSG_D 0x11 -+#define ESPI_PERIF_SUC_CMPLT 0x06 -+#define ESPI_PERIF_SUC_CMPLT_D_MIDDLE 0x09 -+#define ESPI_PERIF_SUC_CMPLT_D_FIRST 0x0b -+#define ESPI_PERIF_SUC_CMPLT_D_LAST 0x0d -+#define ESPI_PERIF_SUC_CMPLT_D_ONLY 0x0f -+#define ESPI_PERIF_UNSUC_CMPLT 0x0c -+#define ESPI_OOB_MSG 0x21 -+#define ESPI_FLASH_READ 0x00 -+#define ESPI_FLASH_WRITE 0x01 -+#define ESPI_FLASH_ERASE 0x02 -+#define ESPI_FLASH_SUC_CMPLT 0x06 -+#define ESPI_FLASH_SUC_CMPLT_D_MIDDLE 0x09 -+#define ESPI_FLASH_SUC_CMPLT_D_FIRST 0x0b -+#define ESPI_FLASH_SUC_CMPLT_D_LAST 0x0d -+#define ESPI_FLASH_SUC_CMPLT_D_ONLY 0x0f -+#define ESPI_FLASH_UNSUC_CMPLT 0x0c ++ return -EINVAL; ++} + -+/* -+ * eSPI packet format structure -+ * -+ * Section 5.1 Cycle Types and Packet Format, -+ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. -+ */ -+struct espi_comm_hdr { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; ++static const struct file_operations ast2500_espi_flash_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = ast2500_espi_flash_ioctl, +}; + -+struct espi_perif_mem32 { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; -+ u32 addr_be; -+ u8 data[]; -+} __packed; -+ -+struct espi_perif_mem64 { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; -+ u32 addr_be; -+ u8 data[]; -+} __packed; -+ -+struct espi_perif_msg { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; -+ u8 msg_code; -+ u8 msg_byte[4]; -+ u8 data[]; -+} __packed; -+ -+struct espi_perif_cmplt { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; -+ u8 data[]; -+} __packed; -+ -+struct espi_oob_msg { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; -+ u8 data[]; -+}; ++static void ast2500_espi_flash_isr(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_flash *flash; ++ unsigned long flags; ++ u32 sts; + -+struct espi_flash_rwe { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; -+ u32 addr_be; -+ u8 data[]; -+} __packed; ++ flash = &espi->flash; + -+struct espi_flash_cmplt { -+ u8 cyc; -+ u8 len_h : 4; -+ u8 tag : 4; -+ u8 len_l; -+ u8 data[]; -+} __packed; ++ sts = readl(espi->regs + ESPI_INT_STS); + -+#define ESPI_MAX_PLD_LEN BIT(12) ++ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { ++ spin_lock_irqsave(&flash->lock, flags); ++ flash->rx_ready = true; ++ spin_unlock_irqrestore(&flash->lock, flags); + -+/* -+ * Aspeed IOCTL for eSPI raw packet send/receive -+ * -+ * This IOCTL interface works in the eSPI packet in/out paradigm. -+ * -+ * Only the virtual wire IOCTL is a special case which does not send -+ * or receive an eSPI packet. However, to keep a more consisten use from -+ * userspace, we make all of the four channel drivers serve through the -+ * IOCTL interface. -+ * -+ * For the eSPI packet format, refer to -+ * Section 5.1 Cycle Types and Packet Format, -+ * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. -+ * -+ * For the example user apps using these IOCTL, refer to -+ * https://github.com/AspeedTech-BMC/aspeed_app/tree/master/espi_test -+ */ -+#define __ASPEED_ESPI_IOCTL_MAGIC 0xb8 ++ wake_up_interruptible(&flash->wq); + -+/* -+ * we choose the longest header and the max payload size -+ * based on the Intel specification to define the maximum -+ * eSPI packet length -+ */ -+#define ESPI_MAX_PKT_LEN (sizeof(struct espi_perif_msg) + ESPI_MAX_PLD_LEN) ++ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ } ++} + -+struct aspeed_espi_ioc { -+ u32 pkt_len; -+ u8 *pkt; -+}; ++static void ast2500_espi_flash_reset(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_flash *flash = &espi->flash; ++ u32 reg; + -+/* -+ * Peripheral Channel (CH0) -+ * - ASPEED_ESPI_PERIF_PC_GET_RX -+ * Receive an eSPI Posted/Completion packet -+ * - ASPEED_ESPI_PERIF_PC_PUT_TX -+ * Transmit an eSPI Posted/Completion packet -+ * - ASPEED_ESPI_PERIF_NP_PUT_TX -+ * Transmit an eSPI Non-Posted packet -+ */ -+#define ASPEED_ESPI_PERIF_PC_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x00, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_PERIF_PC_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x01, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_PERIF_NP_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x02, struct aspeed_espi_ioc) -+/* -+ * Virtual Wire Channel (CH1) -+ * - ASPEED_ESPI_VW_GET_GPIO_VAL -+ * Read the input value of GPIO over the VW channel -+ * - ASPEED_ESPI_VW_PUT_GPIO_VAL -+ * Write the output value of GPIO over the VW channel -+ * - ASPEED_ESPI_VW_GET_GPIO_VAL1 (new feature in AST2700) -+ * Read the input value1 of GPIO over the VW channel -+ * - ASPEED_ESPI_VW_PUT_GPIO_VAL1 (new feature in AST2700) -+ * Write the output value1 of GPIO over the VW channel -+ */ -+#define ASPEED_ESPI_VW_GET_GPIO_VAL _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x10, u32) -+#define ASPEED_ESPI_VW_PUT_GPIO_VAL _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x11, u32) -+#ifdef CONFIG_ARM64 -+#define ASPEED_ESPI_VW_GET_GPIO_VAL1 _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x12, u32) -+#define ASPEED_ESPI_VW_PUT_GPIO_VAL1 _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x13, u32) -+#endif -+/* -+ * Out-of-band Channel (CH2) -+ * - ASPEED_ESPI_OOB_GET_RX -+ * Receive an eSPI OOB packet -+ * - ASPEED_ESPI_OOB_PUT_TX -+ * Transmit an eSPI OOB packet -+ */ -+#define ASPEED_ESPI_OOB_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x20, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_OOB_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x21, struct aspeed_espi_ioc) -+/* -+ * Flash Channel (CH3) -+ * - ASPEED_ESPI_FLASH_GET_RX -+ * Receive an eSPI flash packet -+ * - ASPEED_ESPI_FLASH_PUT_TX -+ * Transmit an eSPI flash packet -+ */ -+#define ASPEED_ESPI_FLASH_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x30, struct aspeed_espi_ioc) -+#define ASPEED_ESPI_FLASH_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ -+ 0x31, struct aspeed_espi_ioc) ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_FLASH); ++ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS); + -+#endif -diff --git a/drivers/soc/aspeed/espi/aspeed-espi.c b/drivers/soc/aspeed/espi/aspeed-espi.c ---- a/drivers/soc/aspeed/espi/aspeed-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/aspeed-espi.c 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,256 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Unified Aspeed eSPI driver (AST2500/AST2600/AST2700) -+ * -+ * This wraps the existing SoC-specific implementations behind a -+ * single driver name and compatible strings. For now we keep the -+ * full implementations in their original files and dispatch through -+ * small per-SoC glue ops. -+ */ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_FLASH_TX_SW_RST ++ | ESPI_CTRL_FLASH_RX_SW_RST ++ | ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN ++ | ESPI_CTRL_FLASH_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ udelay(1); + -+#include "aspeed-espi.h" -+#include "ast2500-espi.h" -+#include "ast2600-espi.h" -+#include "ast2700-espi.h" ++ reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST); ++ writel(reg, espi->regs + ESPI_CTRL); + -+struct aspeed_espi_ops { -+ enum aspeed_espi_platform_id platform_id; -+ void (*espi_pre_init)(struct aspeed_espi *espi); -+ void (*espi_post_init)(struct aspeed_espi *espi); -+ void (*espi_deinit)(struct aspeed_espi *espi); -+ int (*espi_perif_probe)(struct aspeed_espi *espi); -+ int (*espi_perif_remove)(struct aspeed_espi *espi); -+ int (*espi_vw_probe)(struct aspeed_espi *espi); -+ int (*espi_vw_remove)(struct aspeed_espi *espi); -+ int (*espi_oob_probe)(struct aspeed_espi *espi); -+ int (*espi_oob_remove)(struct aspeed_espi *espi); -+ int (*espi_flash_probe)(struct aspeed_espi *espi); -+ int (*espi_flash_remove)(struct aspeed_espi *espi); -+ irqreturn_t (*espi_isr)(int irq, void *espi); -+}; ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ reg = FIELD_PREP(ESPI_FLASH_SAFS_TADDR_BASE, flash->edaf.taddr >> 24) ++ | FIELD_PREP(ESPI_FLASH_SAFS_TADDR_MASK, (~(flash->edaf.size - 1)) >> 24); ++ writel(reg, espi->regs + ESPI_FLASH_SAFS_TADDR); ++ } else { ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SAFS_SW_MODE; ++ writel(reg, espi->regs + ESPI_CTRL); ++ } + -+static const struct aspeed_espi_ops aspeed_espi_ast2500_ops = { -+ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2500, -+ .espi_pre_init = ast2500_espi_pre_init, -+ .espi_post_init = ast2500_espi_post_init, -+ .espi_deinit = ast2500_espi_deinit, -+ .espi_perif_probe = ast2500_espi_perif_probe, -+ .espi_perif_remove = ast2500_espi_perif_remove, -+ .espi_vw_probe = ast2500_espi_vw_probe, -+ .espi_vw_remove = ast2500_espi_vw_remove, -+ .espi_oob_probe = ast2500_espi_oob_probe, -+ .espi_oob_remove = ast2500_espi_oob_remove, -+ .espi_flash_probe = ast2500_espi_flash_probe, -+ .espi_flash_remove = ast2500_espi_flash_remove, -+ .espi_isr = ast2500_espi_isr, -+}; ++ if (flash->dma.enable) { ++ writel(flash->dma.tx_addr, espi->regs + ESPI_FLASH_TX_DMA); ++ writel(flash->dma.rx_addr, espi->regs + ESPI_FLASH_RX_DMA); + -+static const struct aspeed_espi_ops aspeed_espi_ast2600_ops = { -+ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2600, -+ .espi_pre_init = ast2600_espi_pre_init, -+ .espi_post_init = ast2600_espi_post_init, -+ .espi_deinit = ast2600_espi_deinit, -+ .espi_perif_probe = ast2600_espi_perif_probe, -+ .espi_perif_remove = ast2600_espi_perif_remove, -+ .espi_vw_probe = ast2600_espi_vw_probe, -+ .espi_vw_remove = ast2600_espi_vw_remove, -+ .espi_oob_probe = ast2600_espi_oob_probe, -+ .espi_oob_remove = ast2600_espi_oob_remove, -+ .espi_flash_probe = ast2600_espi_flash_probe, -+ .espi_flash_remove = ast2600_espi_flash_remove, -+ .espi_isr = ast2600_espi_isr, -+}; ++ reg = readl(espi->regs + ESPI_CTRL) ++ | ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CTRL); ++ } + -+static const struct aspeed_espi_ops aspeed_espi_ast2700_ops = { -+ .platform_id = ASPEED_ESPI_PLATFORM_ID_AST2700, -+ .espi_pre_init = ast2700_espi_pre_init, -+ .espi_post_init = ast2700_espi_post_init, -+ .espi_deinit = ast2700_espi_deinit, -+ .espi_perif_probe = ast2700_espi_perif_probe, -+ .espi_perif_remove = ast2700_espi_perif_remove, -+ .espi_vw_probe = ast2700_espi_vw_probe, -+ .espi_vw_remove = ast2700_espi_vw_remove, -+ .espi_oob_probe = ast2700_espi_oob_probe, -+ .espi_oob_remove = ast2700_espi_oob_remove, -+ .espi_flash_probe = ast2700_espi_flash_probe, -+ .espi_flash_remove = ast2700_espi_flash_remove, -+ .espi_isr = ast2700_espi_isr, -+}; ++ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_FLASH_RX_CMPLT; ++ writel(reg, espi->regs + ESPI_INT_EN); + -+static const struct of_device_id aspeed_espi_of_matches[] = { -+ { .compatible = "aspeed,ast2500-espi", .data = &aspeed_espi_ast2500_ops }, -+ { .compatible = "aspeed,ast2600-espi", .data = &aspeed_espi_ast2600_ops }, -+ { .compatible = "aspeed,ast2700-espi", .data = &aspeed_espi_ast2700_ops }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, aspeed_espi_of_matches); ++ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY; ++ writel(reg, espi->regs + ESPI_CTRL); ++} + -+static int aspeed_espi_probe(struct platform_device *pdev) ++int ast2500_espi_flash_probe(struct aspeed_espi *espi) +{ -+ const struct of_device_id *match; -+ struct device *dev = &pdev->dev; -+ struct aspeed_espi *espi; -+ struct resource *res; ++ struct aspeed_espi_flash *flash; ++ struct device *dev; + int rc; + -+ espi = devm_kzalloc(dev, sizeof(*espi), GFP_KERNEL); -+ if (!espi) -+ return -ENOMEM; ++ dev = espi->dev; + -+ espi->dev = dev; -+ match = of_match_device(aspeed_espi_of_matches, dev); -+ if (!match) -+ return -ENODEV; -+ espi->ops = match->data; ++ flash = &espi->flash; + -+ espi->pdev = pdev; ++ init_waitqueue_head(&flash->wq); + -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); -+ if (rc) { -+ dev_err(dev, "cannot set 64-bits DMA mask\n"); -+ return rc; -+ } ++ spin_lock_init(&flash->lock); + -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(dev, "cannot get resource\n"); -+ return -ENODEV; -+ } ++ mutex_init(&flash->tx_mtx); ++ mutex_init(&flash->rx_mtx); + -+ espi->regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR(espi->regs)) { -+ dev_err(dev, "cannot map registers\n"); -+ return PTR_ERR(espi->regs); -+ } ++ flash->edaf.mode = EDAF_MODE_MIX; + -+ espi->irq = platform_get_irq(pdev, 0); -+ if (espi->irq < 0) { -+ dev_err(dev, "cannot get IRQ number\n"); -+ return -ENODEV; -+ } ++ of_property_read_u32(dev->of_node, "flash-safs-mode", &flash->edaf.mode); ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ rc = of_property_read_u32(dev->of_node, "flash-safs-tgt-addr", (u32 *)&flash->edaf.taddr); ++ if (rc || !IS_ALIGNED(flash->edaf.taddr, FLASH_SAFS_ALIGN)) { ++ dev_err(dev, "cannot get 16MB-aligned SAFS target address\n"); ++ return -ENODEV; ++ } + -+ espi->rst = devm_reset_control_get_optional(&pdev->dev, NULL); -+ if (IS_ERR(espi->rst)) { -+ dev_err(dev, "cannot get reset control\n"); -+ return PTR_ERR(espi->rst); ++ rc = of_property_read_u32(dev->of_node, "flash-safs-size", (u32 *)&flash->edaf.size); ++ if (rc || !IS_ALIGNED(flash->edaf.size, FLASH_SAFS_ALIGN)) { ++ dev_err(dev, "cannot get 16MB-aligned SAFS size\n"); ++ return -ENODEV; ++ } + } + -+ espi->clk = devm_clk_get(dev, NULL); -+ if (IS_ERR(espi->clk)) { -+ dev_err(dev, "cannot get clock control\n"); -+ return PTR_ERR(espi->clk); ++ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); ++ if (flash->dma.enable) { ++ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); ++ if (!flash->dma.tx_virt) { ++ dev_err(dev, "cannot allocate DMA TX buffer\n"); ++ return -ENOMEM; ++ } ++ ++ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); ++ if (!flash->dma.rx_virt) { ++ dev_err(dev, "cannot allocate DMA RX buffer\n"); ++ return -ENOMEM; ++ } + } + -+ rc = clk_prepare_enable(espi->clk); ++ flash->mdev.parent = dev; ++ flash->mdev.minor = MISC_DYNAMIC_MINOR; ++ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash", DEVICE_NAME); ++ flash->mdev.fops = &ast2500_espi_flash_fops; ++ rc = misc_register(&flash->mdev); + if (rc) { -+ dev_err(dev, "cannot enable clocks\n"); ++ dev_err(dev, "cannot register device %s\n", flash->mdev.name); + return rc; + } + -+ espi->ops->espi_pre_init(espi); ++ ast2500_espi_flash_reset(espi); + -+ rc = espi->ops->espi_perif_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init peripheral channel, rc=%d\n", rc); -+ return rc; -+ } ++ return 0; ++} + -+ rc = espi->ops->espi_vw_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init vw channel, rc=%d\n", rc); -+ goto err_remove_perif; -+ } ++int ast2500_espi_flash_remove(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_flash *flash; ++ struct device *dev; ++ u32 reg; + -+ rc = espi->ops->espi_oob_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init oob channel, rc=%d\n", rc); -+ goto err_remove_vw; -+ } ++ dev = espi->dev; + -+ rc = espi->ops->espi_flash_probe(espi); -+ if (rc) { -+ dev_err(dev, "cannot init flash channel, rc=%d\n", rc); -+ goto err_remove_oob; -+ } ++ flash = &espi->flash; + -+ rc = devm_request_irq(dev, espi->irq, espi->ops->espi_isr, 0, -+ dev_name(dev), espi); -+ if (rc) { -+ dev_err(dev, "cannot request IRQ\n"); -+ goto err_remove_flash; -+ } ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_FLASH); ++ writel(reg, espi->regs + ESPI_INT_EN); + -+ if (espi->perif.mmbi.enable) { -+ rc = devm_request_irq(dev, espi->perif.mmbi.irq, -+ espi->perif.mmbi.mmbi_isr, 0, dev_name(dev), -+ espi); -+ if (rc) { -+ dev_err(dev, "cannot request MMBI IRQ\n"); -+ goto err_remove_flash; -+ } -+ } ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN ++ | ESPI_CTRL_FLASH_RX_DMA_EN ++ | ESPI_CTRL_FLASH_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); + -+ espi->ops->espi_post_init(espi); ++ if (flash->dma.enable) { ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); ++ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); ++ } + -+ platform_set_drvdata(pdev, espi); ++ mutex_destroy(&flash->tx_mtx); ++ mutex_destroy(&flash->rx_mtx); + -+ dev_info(dev, "module loaded\n"); ++ misc_deregister(&flash->mdev); + + return 0; ++} + -+err_remove_flash: -+ espi->ops->espi_flash_remove(espi); -+err_remove_oob: -+ espi->ops->espi_oob_remove(espi); -+err_remove_vw: -+ espi->ops->espi_vw_remove(espi); -+err_remove_perif: -+ espi->ops->espi_perif_remove(espi); ++/* global control */ ++irqreturn_t ast2500_espi_isr(int irq, void *arg) ++{ ++ struct aspeed_espi *espi; ++ u32 sts; + -+ return rc; ++ espi = (struct aspeed_espi *)arg; ++ ++ sts = readl(espi->regs + ESPI_INT_STS); ++ if (!sts) ++ return IRQ_NONE; ++ ++ if (sts & ESPI_INT_STS_PERIF) ++ ast2500_espi_perif_isr(espi); ++ ++ if (sts & ESPI_INT_STS_VW) ++ ast2500_espi_vw_isr(espi); ++ ++ if (sts & ESPI_INT_STS_OOB) ++ ast2500_espi_oob_isr(espi); ++ ++ if (sts & ESPI_INT_STS_FLASH) ++ ast2500_espi_flash_isr(espi); ++ ++ if (sts & ESPI_INT_STS_RST_DEASSERT) { ++ ast2500_espi_perif_reset(espi); ++ ast2500_espi_vw_reset(espi); ++ ast2500_espi_oob_reset(espi); ++ ast2500_espi_flash_reset(espi); ++ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); ++ } ++ ++ return IRQ_HANDLED; +} + -+static void aspeed_espi_remove(struct platform_device *pdev) ++void ast2500_espi_pre_init(struct aspeed_espi *espi) +{ -+ struct aspeed_espi *espi; -+ struct device *dev; ++ u32 reg; + -+ dev = &pdev->dev; ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++} + -+ espi = dev_get_drvdata(dev); ++void ast2500_espi_post_init(struct aspeed_espi *espi) ++{ ++ u32 reg; + -+ espi->ops->espi_deinit(espi); ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg |= ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++} ++ ++void ast2500_espi_deinit(struct aspeed_espi *espi) ++{ ++ u32 reg; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~(ESPI_INT_EN_RST_DEASSERT); ++ writel(reg, espi->regs + ESPI_INT_EN); ++} +diff --git a/drivers/soc/aspeed/espi/ast2500-espi.h b/drivers/soc/aspeed/espi/ast2500-espi.h +--- a/drivers/soc/aspeed/espi/ast2500-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2500-espi.h 2026-07-17 08:44:55.291306535 +0000 +@@ -0,0 +1,258 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Register definitions for Aspeed AST2500 eSPI controller ++ * Copyright 2023 Aspeed Technology Inc. ++ */ ++#ifndef _AST2500_ESPI_H_ ++#define _AST2500_ESPI_H_ ++ ++#include ++#include ++#include "aspeed-espi.h" ++ ++/* registers */ ++#define ESPI_CTRL 0x000 ++#define ESPI_CTRL_FLASH_TX_SW_RST BIT(31) ++#define ESPI_CTRL_FLASH_RX_SW_RST BIT(30) ++#define ESPI_CTRL_OOB_TX_SW_RST BIT(29) ++#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) ++#define ESPI_CTRL_PERIF_NP_TX_SW_RST BIT(27) ++#define ESPI_CTRL_PERIF_NP_RX_SW_RST BIT(26) ++#define ESPI_CTRL_PERIF_PC_TX_SW_RST BIT(25) ++#define ESPI_CTRL_PERIF_PC_RX_SW_RST BIT(24) ++#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) ++#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) ++#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) ++#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) ++#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) ++#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) ++#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) ++#define ESPI_CTRL_FLASH_SAFS_SW_MODE BIT(10) ++#define ESPI_CTRL_VW_GPIO_SW BIT(9) ++#define ESPI_CTRL_FLASH_SW_RDY BIT(7) ++#define ESPI_CTRL_OOB_SW_RDY BIT(4) ++#define ESPI_CTRL_VW_SW_RDY BIT(3) ++#define ESPI_CTRL_PERIF_SW_RDY BIT(1) ++#define ESPI_STS 0x004 ++#define ESPI_INT_STS 0x008 ++#define ESPI_INT_STS_RST_DEASSERT BIT(31) ++#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) ++#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) ++#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) ++#define ESPI_INT_STS_OOB_TX_ERR BIT(20) ++#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) ++#define ESPI_INT_STS_OOB_TX_ABT BIT(18) ++#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) ++#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) ++#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) ++#define ESPI_INT_STS_OOB_RX_ABT BIT(14) ++#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) ++#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) ++#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) ++#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) ++#define ESPI_INT_STS_VW_GPIO BIT(9) ++#define ESPI_INT_STS_VW_SYSEVT BIT(8) ++#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) ++#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) ++#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) ++#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) ++#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) ++#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) ++#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) ++#define ESPI_INT_EN 0x00c ++#define ESPI_INT_EN_RST_DEASSERT BIT(31) ++#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) ++#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) ++#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) ++#define ESPI_INT_EN_OOB_TX_ERR BIT(20) ++#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) ++#define ESPI_INT_EN_OOB_TX_ABT BIT(18) ++#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) ++#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) ++#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) ++#define ESPI_INT_EN_OOB_RX_ABT BIT(14) ++#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) ++#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) ++#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) ++#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) ++#define ESPI_INT_EN_VW_GPIO BIT(9) ++#define ESPI_INT_EN_VW_SYSEVT BIT(8) ++#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) ++#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) ++#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) ++#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) ++#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) ++#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) ++#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) ++#define ESPI_PERIF_PC_RX_DMA 0x010 ++#define ESPI_PERIF_PC_RX_CTRL 0x014 ++#define ESPI_PERIF_PC_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_PERIF_PC_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_PC_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_PC_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_PC_RX_DATA 0x018 ++#define ESPI_PERIF_PC_TX_DMA 0x020 ++#define ESPI_PERIF_PC_TX_CTRL 0x024 ++#define ESPI_PERIF_PC_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_PERIF_PC_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_PC_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_PC_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_PC_TX_DATA 0x028 ++#define ESPI_PERIF_NP_TX_DMA 0x030 ++#define ESPI_PERIF_NP_TX_CTRL 0x034 ++#define ESPI_PERIF_NP_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_PERIF_NP_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_PERIF_NP_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_PERIF_NP_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_PERIF_NP_TX_DATA 0x038 ++#define ESPI_OOB_RX_DMA 0x040 ++#define ESPI_OOB_RX_CTRL 0x044 ++#define ESPI_OOB_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_OOB_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_OOB_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_OOB_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_OOB_RX_DATA 0x048 ++#define ESPI_OOB_TX_DMA 0x050 ++#define ESPI_OOB_TX_CTRL 0x054 ++#define ESPI_OOB_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_OOB_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_OOB_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_OOB_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_OOB_TX_DATA 0x058 ++#define ESPI_FLASH_RX_DMA 0x060 ++#define ESPI_FLASH_RX_CTRL 0x064 ++#define ESPI_FLASH_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_FLASH_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_FLASH_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_FLASH_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_FLASH_RX_DATA 0x068 ++#define ESPI_FLASH_TX_DMA 0x070 ++#define ESPI_FLASH_TX_CTRL 0x074 ++#define ESPI_FLASH_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_FLASH_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_FLASH_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_FLASH_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_FLASH_TX_DATA 0x078 ++#define ESPI_PERIF_MCYC_SADDR 0x084 ++#define ESPI_PERIF_MCYC_TADDR 0x088 ++#define ESPI_PERIF_MCYC_MASK 0x08c ++#define ESPI_FLASH_SAFS_TADDR 0x090 ++#define ESPI_FLASH_SAFS_TADDR_BASE GENMASK(31, 24) ++#define ESPI_FLASH_SAFS_TADDR_MASK GENMASK(15, 8) ++#define ESPI_VW_SYSEVT_INT_EN 0x094 ++#define ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT 0x098 ++#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27) ++#define ESPI_VW_SYSEVT_SLV_BOOT_STS BIT(23) ++#define ESPI_VW_SYSEVT_SLV_BOOT_DONE BIT(20) ++#define ESPI_VW_SYSEVT_OOB_RST_ACK BIT(16) ++#define ESPI_VW_SYSEVT_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_OOB_RST_WARN BIT(6) ++#define ESPI_VW_GPIO_VAL 0x09c ++#define ESPI_GEN_CAP_N_CONF 0x0a0 ++#define ESPI_CH0_CAP_N_CONF 0x0a4 ++#define ESPI_CH1_CAP_N_CONF 0x0a8 ++#define ESPI_CH2_CAP_N_CONF 0x0ac ++#define ESPI_CH3_CAP_N_CONF 0x0b0 ++#define ESPI_CH3_CAP_N_CONF2 0x0b4 ++#define ESPI_VW_GPIO_DIR 0x0c0 ++#define ESPI_VW_GPIO_GRP 0x0c4 ++#define ESPI_VW_SYSEVT1_INT_EN 0x100 ++#define ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN BIT(0) ++#define ESPI_VW_SYSEVT1 0x104 ++#define ESPI_VW_SYSEVT1_SUSPEND_ACK BIT(20) ++#define ESPI_VW_SYSEVT1_SUSPEND_WARN BIT(0) ++#define ESPI_VW_SYSEVT_INT_T0 0x110 ++#define ESPI_VW_SYSEVT_INT_T1 0x114 ++#define ESPI_VW_SYSEVT_INT_T2 0x118 ++#define ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT_INT_STS 0x11c ++#define ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN BIT(8) ++#define ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT1_INT_T0 0x120 ++#define ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN BIT(0) ++#define ESPI_VW_SYSEVT1_INT_T1 0x124 ++#define ESPI_VW_SYSEVT1_INT_T2 0x128 ++#define ESPI_VW_SYSEVT1_INT_STS 0x12c ++#define ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN BIT(0) ++ ++/* collect ESPI_INT_EN bits for convenience */ ++#define ESPI_INT_EN_PERIF \ ++ (ESPI_INT_EN_PERIF_NP_TX_ABT | \ ++ ESPI_INT_EN_PERIF_PC_TX_ABT | \ ++ ESPI_INT_EN_PERIF_NP_RX_ABT | \ ++ ESPI_INT_EN_PERIF_PC_RX_ABT | \ ++ ESPI_INT_EN_PERIF_NP_TX_ERR | \ ++ ESPI_INT_EN_PERIF_PC_TX_ERR | \ ++ ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ ++ ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ ++ ESPI_INT_EN_PERIF_PC_RX_CMPLT) ++ ++#define ESPI_INT_EN_VW \ ++ (ESPI_INT_EN_VW_SYSEVT1 | \ ++ ESPI_INT_EN_VW_GPIO | \ ++ ESPI_INT_EN_VW_SYSEVT) ++ ++#define ESPI_INT_EN_OOB \ ++ (ESPI_INT_EN_OOB_RX_TMOUT | \ ++ ESPI_INT_EN_OOB_TX_ERR | \ ++ ESPI_INT_EN_OOB_TX_ABT | \ ++ ESPI_INT_EN_OOB_RX_ABT | \ ++ ESPI_INT_EN_OOB_TX_CMPLT | \ ++ ESPI_INT_EN_OOB_RX_CMPLT) ++ ++#define ESPI_INT_EN_FLASH \ ++ (ESPI_INT_EN_FLASH_TX_ERR | \ ++ ESPI_INT_EN_FLASH_TX_ABT | \ ++ ESPI_INT_EN_FLASH_RX_ABT | \ ++ ESPI_INT_EN_FLASH_TX_CMPLT | \ ++ ESPI_INT_EN_FLASH_RX_CMPLT) ++ ++/* collect ESPI_INT_STS bits for convenience */ ++#define ESPI_INT_STS_PERIF \ ++ (ESPI_INT_STS_PERIF_NP_TX_ABT | \ ++ ESPI_INT_STS_PERIF_PC_TX_ABT | \ ++ ESPI_INT_STS_PERIF_NP_RX_ABT | \ ++ ESPI_INT_STS_PERIF_PC_RX_ABT | \ ++ ESPI_INT_STS_PERIF_NP_TX_ERR | \ ++ ESPI_INT_STS_PERIF_PC_TX_ERR | \ ++ ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ ++ ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ ++ ESPI_INT_STS_PERIF_PC_RX_CMPLT) + -+ if (espi->ops->espi_perif_remove(espi)) -+ dev_warn(dev, "cannot remove peripheral channel\n"); -+ if (espi->ops->espi_vw_remove(espi)) -+ dev_warn(dev, "cannot remove vw channel\n"); -+ if (espi->ops->espi_oob_remove(espi)) -+ dev_warn(dev, "cannot remove oob channel\n"); -+ if (espi->ops->espi_flash_remove(espi)) -+ dev_warn(dev, "cannot remove flash channel\n"); -+} ++#define ESPI_INT_STS_VW \ ++ (ESPI_INT_STS_VW_SYSEVT1 | \ ++ ESPI_INT_STS_VW_GPIO | \ ++ ESPI_INT_STS_VW_SYSEVT) + -+static struct platform_driver aspeed_espi_driver = { -+ .driver = { -+ .name = "aspeed-espi", -+ .of_match_table = aspeed_espi_of_matches, -+ }, -+ .probe = aspeed_espi_probe, -+ .remove = aspeed_espi_remove, -+}; ++#define ESPI_INT_STS_OOB \ ++ (ESPI_INT_STS_OOB_RX_TMOUT | \ ++ ESPI_INT_STS_OOB_TX_ERR | \ ++ ESPI_INT_STS_OOB_TX_ABT | \ ++ ESPI_INT_STS_OOB_RX_ABT | \ ++ ESPI_INT_STS_OOB_TX_CMPLT | \ ++ ESPI_INT_STS_OOB_RX_CMPLT) + -+module_platform_driver(aspeed_espi_driver); ++#define ESPI_INT_STS_FLASH \ ++ (ESPI_INT_STS_FLASH_TX_ERR | \ ++ ESPI_INT_STS_FLASH_TX_ABT | \ ++ ESPI_INT_STS_FLASH_RX_ABT | \ ++ ESPI_INT_STS_FLASH_TX_CMPLT | \ ++ ESPI_INT_STS_FLASH_RX_CMPLT) + -+MODULE_AUTHOR("Aspeed Technology Inc."); -+MODULE_DESCRIPTION("Aspeed eSPI controller (AST2500/2600/2700)"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/soc/aspeed/espi/aspeed-espi.h b/drivers/soc/aspeed/espi/aspeed-espi.h ---- a/drivers/soc/aspeed/espi/aspeed-espi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/aspeed-espi.h 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,227 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ ++/* operators for framework initialization */ ++void ast2500_espi_pre_init(struct aspeed_espi *espi); ++void ast2500_espi_post_init(struct aspeed_espi *espi); ++void ast2500_espi_deinit(struct aspeed_espi *espi); ++int ast2500_espi_perif_probe(struct aspeed_espi *espi); ++int ast2500_espi_perif_remove(struct aspeed_espi *espi); ++int ast2500_espi_vw_probe(struct aspeed_espi *espi); ++int ast2500_espi_vw_remove(struct aspeed_espi *espi); ++int ast2500_espi_oob_probe(struct aspeed_espi *espi); ++int ast2500_espi_oob_remove(struct aspeed_espi *espi); ++int ast2500_espi_flash_probe(struct aspeed_espi *espi); ++int ast2500_espi_flash_remove(struct aspeed_espi *espi); ++irqreturn_t ast2500_espi_isr(int irq, void *arg); ++#endif +diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/ast2600-espi.c +--- a/drivers/soc/aspeed/espi/ast2600-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2600-espi.c 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,1910 @@ ++// SPDX-License-Identifier: GPL-2.0+ +/* -+ * Unified eSPI driver header file and data structures -+ * Copyright 2026 Aspeed Technology Inc. ++ * Copyright 2023 Aspeed Technology Inc. + */ -+#ifndef __ASPEED_ESPI_H__ -+#define __ASPEED_ESPI_H__ -+ -+#include "linux/platform_device.h" -+#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include +#include -+#include ++#include ++#include ++#include ++#include ++#include ++#include + -+#define PERIF_MMBI_INST_NUM 8 -+#define DEVICE_NAME "aspeed-espi" ++#include "ast2600-espi.h" ++#include "aspeed-espi-comm.h" ++#include "aspeed-espi.h" + -+enum aspeed_espi_platform_id { -+ ASPEED_ESPI_PLATFORM_ID_AST2500 = 0x2500, -+ ASPEED_ESPI_PLATFORM_ID_AST2600 = 0x2600, -+ ASPEED_ESPI_PLATFORM_ID_AST2700 = 0x2700, -+}; ++#define PERIF_MCYC_ALIGN SZ_64K ++#define PERIF_MMBI_ALIGN SZ_64K ++#define PERIF_MMBI_INST_NUM 8 + -+/* consistent with DTS property "flash-safs-mode" */ -+enum aspeed_edaf_mode { -+ EDAF_MODE_MIX = 0x0, -+ EDAF_MODE_SW, -+ EDAF_MODE_HW, -+ EDAF_MODES, -+}; ++#define OOB_DMA_RPTR_KEY 0x45538073 ++#define OOB_DMA_DESC_NUM 8 ++#define OOB_DMA_DESC_CUSTOM 0x4 + -+struct aspeed_espi_perif; ++#define FLASH_EDAF_ALIGN SZ_16M + -+struct aspeed_espi_perif_mmbi { -+ void *b2h_virt; -+ void *h2b_virt; -+ dma_addr_t b2h_addr; -+ dma_addr_t h2b_addr; -+ struct miscdevice b2h_mdev; -+ struct miscdevice h2b_mdev; -+ bool host_rwp_update; -+ wait_queue_head_t wq; ++/* peripheral channel (CH0) */ ++static int ast2600_espi_mmbi_b2h_mmap(struct file *fp, struct vm_area_struct *vma) ++{ ++ struct aspeed_espi_perif_mmbi *mmbi; + struct aspeed_espi_perif *perif; -+}; -+ -+struct aspeed_espi_perif { -+ struct { -+ bool enable; -+ int irq; -+ void *virt; -+ dma_addr_t taddr; -+ dma_addr_t saddr; -+ resource_size_t size; -+ u32 inst_num; -+ u32 inst_size; -+ irqreturn_t (*mmbi_isr)(int irq, void *espi); -+ struct aspeed_espi_perif_mmbi inst[PERIF_MMBI_INST_NUM]; -+ } mmbi; -+ -+ struct { -+ bool enable; -+ void *virt; -+ dma_addr_t taddr; -+ dma_addr_t saddr; -+ resource_size_t size; -+ } mcyc; -+ -+ struct { -+ bool enable; -+ void *np_tx_virt; -+ dma_addr_t np_tx_addr; -+ void *pc_tx_virt; -+ dma_addr_t pc_tx_addr; -+ void *pc_rx_virt; -+ dma_addr_t pc_rx_addr; -+ } dma; -+ -+ bool rtc_enable; -+ bool rx_ready; -+ wait_queue_head_t wq; -+ -+ spinlock_t lock; // protects rx_ready -+ struct mutex np_tx_mtx; // protects np_tx_virt/addr -+ struct mutex pc_tx_mtx; // protects pc_tx_virt/addr -+ struct mutex pc_rx_mtx; // protects pc_rx_virt/addr ++ struct aspeed_espi *espi; ++ unsigned long vm_size; ++ pgprot_t prot; + -+ struct miscdevice mdev; -+}; ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, b2h_mdev); + -+struct aspeed_espi_vw { -+ struct { -+ bool hw_mode; -+ u32 grp; -+ u32 dir0; -+ u32 dir1; -+ u32 val0; -+ u32 val1; -+ } gpio; ++ perif = mmbi->perif; + -+ struct { -+ bool enabled; -+ spinlock_t pltrst_lock; // protects pltrst_status -+ wait_queue_head_t pltrst_wq; -+ char pltrst_status; -+ bool pltrst_avail; -+ } pltrst; ++ espi = container_of(perif, struct aspeed_espi, perif); + -+ struct miscdevice pltrst_mdev; -+ struct miscdevice mdev; -+}; ++ vm_size = vma->vm_end - vma->vm_start; ++ prot = vma->vm_page_prot; + -+struct ast2700_espi_oob_dma_tx_desc { -+ u32 data_addrl; -+ u32 data_addrh; -+ u8 cyc; -+ u16 tag : 4; -+ u16 len : 12; -+ u8 msg_type : 3; -+ u8 raz0 : 1; -+ u8 pec : 1; -+ u8 int_en : 1; -+ u8 pause : 1; -+ u8 raz1 : 1; -+ u32 raz2; -+ u32 raz3; -+ u32 pad[3]; -+} __packed; ++ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) ++ return -EINVAL; + -+struct ast2700_espi_oob_dma_rx_desc { -+ u32 data_addrl; -+ u32 data_addrh; -+ u8 cyc; -+ u16 tag : 4; -+ u16 len : 12; -+ u8 raz : 7; -+ u8 dirty : 1; -+ u32 pad; -+} __packed; ++ prot = pgprot_noncached(prot); + -+struct ast2600_espi_oob_dma_tx_desc { -+ u32 data_addr; -+ u8 cyc; -+ u16 tag : 4; -+ u16 len : 12; -+ u8 msg_type : 3; -+ u8 raz0 : 1; -+ u8 pec : 1; -+ u8 int_en : 1; -+ u8 pause : 1; -+ u8 raz1 : 1; -+ u32 raz2; -+ u32 raz3; -+} __packed; ++ if (remap_pfn_range(vma, vma->vm_start, ++ (mmbi->b2h_addr >> PAGE_SHIFT) + vma->vm_pgoff, ++ vm_size, prot)) ++ return -EAGAIN; + -+struct ast2600_espi_oob_dma_rx_desc { -+ u32 data_addr; -+ u8 cyc; -+ u16 tag : 4; -+ u16 len : 12; -+ u8 raz : 7; -+ u8 dirty : 1; -+} __packed; ++ return 0; ++} + -+struct aspeed_espi_oob { -+ struct { -+ bool enable; -+ void *txd_virt; -+ dma_addr_t txd_addr; -+ void *rxd_virt; -+ dma_addr_t rxd_addr; -+ void *tx_virt; -+ dma_addr_t tx_addr; -+ void *rx_virt; -+ dma_addr_t rx_addr; -+ } dma; ++static int ast2600_espi_mmbi_h2b_mmap(struct file *fp, struct vm_area_struct *vma) ++{ ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; ++ unsigned long vm_size; ++ pgprot_t prot; + -+ bool rx_ready; -+ wait_queue_head_t wq; ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); + -+ spinlock_t lock; // protects rx_ready -+ struct mutex tx_mtx; // protects tx_virt/addr -+ struct mutex rx_mtx; // protects rx_virt/addr ++ perif = mmbi->perif; + -+ struct miscdevice mdev; -+}; ++ espi = container_of(perif, struct aspeed_espi, perif); + -+struct aspeed_espi_flash { -+ struct { -+ u32 mode; -+ phys_addr_t taddr; -+ resource_size_t size; -+ } edaf; ++ vm_size = vma->vm_end - vma->vm_start; ++ prot = vma->vm_page_prot; + -+ struct { -+ bool enable; -+ void *tx_virt; -+ dma_addr_t tx_addr; -+ void *rx_virt; -+ dma_addr_t rx_addr; -+ } dma; ++ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) ++ return -EINVAL; + -+ bool rx_ready; -+ wait_queue_head_t wq; ++ prot = pgprot_noncached(prot); + -+ spinlock_t lock; // protects rx_ready -+ struct mutex rx_mtx; // protects rx_virt/addr -+ struct mutex tx_mtx; // protects tx_virt/addr ++ if (remap_pfn_range(vma, vma->vm_start, ++ (mmbi->h2b_addr >> PAGE_SHIFT) + vma->vm_pgoff, ++ vm_size, prot)) ++ return -EAGAIN; + -+ struct miscdevice mdev; -+}; ++ return 0; ++} + -+struct aspeed_espi { -+ struct platform_device *pdev; -+ struct device *dev; -+ void __iomem *regs; -+ struct reset_control *rst; -+ struct clk *clk; -+ int dev_id; -+ int irq; ++static __poll_t ast2600_espi_mmbi_h2b_poll(struct file *fp, struct poll_table_struct *pt) ++{ ++ struct aspeed_espi_perif_mmbi *mmbi; + -+ struct aspeed_espi_perif perif; -+ struct aspeed_espi_vw vw; -+ struct aspeed_espi_oob oob; -+ struct aspeed_espi_flash flash; -+ const struct aspeed_espi_ops *ops; -+}; ++ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); + -+#endif // __ASPEED_ESPI_H__ -diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/ast2500-espi.c ---- a/drivers/soc/aspeed/espi/ast2500-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/ast2500-espi.c 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,1510 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+/* -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ poll_wait(fp, &mmbi->wq, pt); + -+#include "ast2500-espi.h" -+#include "aspeed-espi-comm.h" -+#include "aspeed-espi.h" ++ if (!mmbi->host_rwp_update) ++ return 0; + -+#define PERIF_MCYC_UNLOCK 0xfedc756e -+#define PERIF_MCYC_ALIGN SZ_64K ++ mmbi->host_rwp_update = false; + -+#define FLASH_SAFS_ALIGN SZ_16M ++ return EPOLLIN; ++} + -+/* peripheral channel (CH0) */ -+static long ast2500_espi_perif_pc_get_rx(struct file *fp, ++static long ast2600_espi_perif_pc_get_rx(struct file *fp, + struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ @@ -75980,7 +77844,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2500_espi_perif_pc_put_tx(struct file *fp, ++static long ast2600_espi_perif_pc_put_tx(struct file *fp, + struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ @@ -76047,7 +77911,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2500_espi_perif_np_put_tx(struct file *fp, ++static long ast2600_espi_perif_np_put_tx(struct file *fp, + struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ @@ -76114,7 +77978,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2500_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2600_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_perif *perif; + struct aspeed_espi_ioc ioc; @@ -76129,11 +77993,11 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + + switch (cmd) { + case ASPEED_ESPI_PERIF_PC_GET_RX: -+ return ast2500_espi_perif_pc_get_rx(fp, perif, &ioc); ++ return ast2600_espi_perif_pc_get_rx(fp, perif, &ioc); + case ASPEED_ESPI_PERIF_PC_PUT_TX: -+ return ast2500_espi_perif_pc_put_tx(fp, perif, &ioc); ++ return ast2600_espi_perif_pc_put_tx(fp, perif, &ioc); + case ASPEED_ESPI_PERIF_NP_PUT_TX: -+ return ast2500_espi_perif_np_put_tx(fp, perif, &ioc); ++ return ast2600_espi_perif_np_put_tx(fp, perif, &ioc); + default: + break; + }; @@ -76141,7 +78005,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return -EINVAL; +} + -+static int ast2500_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) ++static int ast2600_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct aspeed_espi_perif *perif; + unsigned long vm_size; @@ -76167,13 +78031,61 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return 0; +} + -+static const struct file_operations ast2500_espi_perif_fops = { ++static const struct file_operations ast2600_espi_mmbi_b2h_fops = { + .owner = THIS_MODULE, -+ .mmap = ast2500_espi_perif_mmap, -+ .unlocked_ioctl = ast2500_espi_perif_ioctl, ++ .mmap = ast2600_espi_mmbi_b2h_mmap, +}; + -+static void ast2500_espi_perif_isr(struct aspeed_espi *espi) ++static const struct file_operations ast2600_espi_mmbi_h2b_fops = { ++ .owner = THIS_MODULE, ++ .mmap = ast2600_espi_mmbi_h2b_mmap, ++ .poll = ast2600_espi_mmbi_h2b_poll, ++}; ++ ++static const struct file_operations ast2600_espi_perif_fops = { ++ .owner = THIS_MODULE, ++ .mmap = ast2600_espi_perif_mmap, ++ .unlocked_ioctl = ast2600_espi_perif_ioctl, ++}; ++ ++static irqreturn_t ast2600_espi_perif_mmbi_isr(int irq, void *arg) ++{ ++ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_espi_perif *perif; ++ struct aspeed_espi *espi; ++ u32 sts, tmp; ++ u32 *p; ++ int i; ++ ++ espi = (struct aspeed_espi *)arg; ++ ++ perif = &espi->perif; ++ ++ sts = readl(espi->regs + ESPI_MMBI_INT_STS); ++ if (!sts) ++ return IRQ_NONE; ++ ++ for (i = 0, tmp = sts; i < PERIF_MMBI_INST_NUM; ++i, tmp >>= 2) { ++ if (!(tmp & 0x3)) ++ continue; ++ ++ mmbi = &perif->mmbi.inst[i]; ++ ++ p = (u32 *)mmbi->h2b_virt; ++ p[0] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i)); ++ p[1] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i) + 4); ++ ++ mmbi->host_rwp_update = true; ++ ++ wake_up_interruptible(&mmbi->wq); ++ } ++ ++ writel(sts, espi->regs + ESPI_MMBI_INT_STS); ++ ++ return IRQ_HANDLED; ++} ++ ++static void ast2600_espi_perif_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_perif *perif; + unsigned long flags; @@ -76194,21 +78106,13 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + } +} + -+static void ast2500_espi_perif_reset(struct aspeed_espi *espi) ++static void ast2600_espi_perif_sw_reset(struct aspeed_espi *espi) +{ -+ struct aspeed_espi_perif *perif; + struct device *dev; -+ u32 reg, mask; ++ u32 reg; + + dev = espi->dev; + -+ perif = &espi->perif; -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_PERIF); -+ writel(reg, espi->regs + ESPI_INT_EN); -+ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); -+ + reg = readl(espi->regs + ESPI_CTRL); + reg &= ~(ESPI_CTRL_PERIF_NP_TX_SW_RST + | ESPI_CTRL_PERIF_NP_RX_SW_RST @@ -76227,14 +78131,64 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + | ESPI_CTRL_PERIF_PC_TX_SW_RST + | ESPI_CTRL_PERIF_PC_RX_SW_RST); + writel(reg, espi->regs + ESPI_CTRL); ++} ++ ++static void ast2600_espi_perif_reset(struct aspeed_espi *espi) ++{ ++ struct aspeed_espi_perif *perif; ++ struct device *dev; ++ u32 reg, mask; ++ ++ dev = espi->dev; ++ ++ perif = &espi->perif; ++ ++ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); ++ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); ++ ++ writel(0x0, espi->regs + ESPI_MMBI_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_STS); ++ ++ reg = readl(espi->regs + ESPI_CTRL2); ++ reg &= ~(ESPI_CTRL2_MCYC_RD_DIS_WDT | ESPI_CTRL2_MCYC_WR_DIS_WDT); ++ writel(reg, espi->regs + ESPI_CTRL2); ++ ++ reg = readl(espi->regs + ESPI_CTRL); ++ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_TX_DMA_EN ++ | ESPI_CTRL_PERIF_PC_RX_DMA_EN ++ | ESPI_CTRL_PERIF_SW_RDY); ++ writel(reg, espi->regs + ESPI_CTRL); ++ ++ if (perif->mmbi.enable) { ++ reg = readl(espi->regs + ESPI_MMBI_CTRL); ++ reg &= ~(ESPI_MMBI_CTRL_EN); ++ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ ++ mask = ~(perif->mmbi.size - 1); ++ writel(mask, espi->regs + ESPI_PERIF_MMBI_MASK); ++ writel(perif->mmbi.saddr, espi->regs + ESPI_PERIF_MMBI_SADDR); ++ writel(perif->mmbi.taddr, espi->regs + ESPI_PERIF_MMBI_TADDR); ++ ++ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_EN); ++ ++ reg = FIELD_PREP(ESPI_MMBI_CTRL_INST_SZ, perif->mmbi.inst_size) ++ | FIELD_PREP(ESPI_MMBI_CTRL_TOTAL_SZ, perif->mmbi.inst_size) ++ | ESPI_MMBI_CTRL_EN; ++ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ ++ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MMBI_RD_DIS | ESPI_CTRL2_MMBI_WR_DIS); ++ writel(reg, espi->regs + ESPI_CTRL2); ++ } + + if (perif->mcyc.enable) { + mask = ~(perif->mcyc.size - 1); -+ writel(PERIF_MCYC_UNLOCK, espi->regs + ESPI_PERIF_MCYC_MASK); + writel(mask, espi->regs + ESPI_PERIF_MCYC_MASK); -+ + writel(perif->mcyc.saddr, espi->regs + ESPI_PERIF_MCYC_SADDR); + writel(perif->mcyc.taddr, espi->regs + ESPI_PERIF_MCYC_TADDR); ++ ++ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); ++ writel(reg, espi->regs + ESPI_CTRL2); + } + + if (perif->dma.enable) { @@ -76249,18 +78203,19 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + writel(reg, espi->regs + ESPI_CTRL); + } + -+ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_PERIF_PC_RX_CMPLT; -+ writel(reg, espi->regs + ESPI_INT_EN); -+ ++ writel(ESPI_INT_EN_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ + reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_PERIF_SW_RDY; + writel(reg, espi->regs + ESPI_CTRL); +} + -+int ast2500_espi_perif_probe(struct aspeed_espi *espi) ++int ast2600_espi_perif_probe(struct aspeed_espi *espi) +{ ++ struct aspeed_espi_perif_mmbi *mmbi; + struct aspeed_espi_perif *perif; ++ struct platform_device *pdev; + struct device *dev; -+ int rc; ++ int i, rc; + + dev = espi->dev; + @@ -76274,8 +78229,79 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + mutex_init(&perif->pc_tx_mtx); + mutex_init(&perif->pc_rx_mtx); + ++ perif->mmbi.enable = of_property_read_bool(dev->of_node, "perif-mmbi-enable"); ++ if (perif->mmbi.enable) { ++ pdev = espi->pdev; ++ ++ perif->mmbi.irq = platform_get_irq(pdev, 1); ++ if (perif->mmbi.irq < 0) { ++ dev_err(dev, "cannot get MMBI IRQ number\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "perif-mmbi-src-addr", (u32 *)&perif->mmbi.saddr); ++ if (rc || !IS_ALIGNED(perif->mmbi.saddr, PERIF_MMBI_ALIGN)) { ++ dev_err(dev, "cannot get 64KB-aligned MMBI host address\n"); ++ return -ENODEV; ++ } ++ ++ rc = of_property_read_u32(dev->of_node, "perif-mmbi-instance-size", (u32 *)&perif->mmbi.inst_size); ++ if (rc || perif->mmbi.inst_size >= MMBI_INST_SIZE_TYPES) { ++ dev_err(dev, "cannot get valid MMBI instance size\n"); ++ return -EINVAL; ++ } ++ ++ perif->mmbi.size = (SZ_8K << perif->mmbi.inst_size) * PERIF_MMBI_INST_NUM; ++ perif->mmbi.virt = dmam_alloc_coherent(dev, perif->mmbi.size, ++ &perif->mmbi.taddr, GFP_KERNEL); ++ if (!perif->mmbi.virt) { ++ dev_err(dev, "cannot allocate MMBI\n"); ++ return -ENOMEM; ++ } ++ ++ perif->mmbi.mmbi_isr = ast2600_espi_perif_mmbi_isr; ++ ++ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { ++ mmbi = &perif->mmbi.inst[i]; ++ ++ init_waitqueue_head(&mmbi->wq); ++ ++ mmbi->perif = perif; ++ mmbi->host_rwp_update = false; ++ ++ mmbi->b2h_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * i); ++ mmbi->b2h_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * i); ++ mmbi->b2h_mdev.parent = dev; ++ mmbi->b2h_mdev.minor = MISC_DYNAMIC_MINOR; ++ mmbi->b2h_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-b2h%d", DEVICE_NAME, i); ++ mmbi->b2h_mdev.fops = &ast2600_espi_mmbi_b2h_fops; ++ rc = misc_register(&mmbi->b2h_mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", mmbi->b2h_mdev.name); ++ return rc; ++ } ++ ++ mmbi->h2b_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); ++ mmbi->h2b_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); ++ mmbi->h2b_mdev.parent = dev; ++ mmbi->h2b_mdev.minor = MISC_DYNAMIC_MINOR; ++ mmbi->h2b_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-h2b%d", DEVICE_NAME, i); ++ mmbi->h2b_mdev.fops = &ast2600_espi_mmbi_h2b_fops; ++ rc = misc_register(&mmbi->h2b_mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", mmbi->h2b_mdev.name); ++ return rc; ++ } ++ } ++ } ++ + perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); + if (perif->mcyc.enable) { ++ if (perif->mmbi.enable) { ++ dev_err(dev, "cannot enable memory cycle, occupied by MMBI\n"); ++ return -EPERM; ++ } ++ + rc = of_property_read_u32(dev->of_node, "perif-mcyc-src-addr", (u32 *)&perif->mcyc.saddr); + if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { + dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); @@ -76323,31 +78349,35 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + perif->mdev.parent = dev; + perif->mdev.minor = MISC_DYNAMIC_MINOR; + perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral", DEVICE_NAME); -+ perif->mdev.fops = &ast2500_espi_perif_fops; ++ perif->mdev.fops = &ast2600_espi_perif_fops; + rc = misc_register(&perif->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", perif->mdev.name); + return rc; + } + -+ ast2500_espi_perif_reset(espi); ++ ast2600_espi_perif_reset(espi); + + return 0; +} + -+int ast2500_espi_perif_remove(struct aspeed_espi *espi) ++int ast2600_espi_perif_remove(struct aspeed_espi *espi) +{ ++ struct aspeed_espi_perif_mmbi *mmbi; + struct aspeed_espi_perif *perif; + struct device *dev; + u32 reg; ++ int i; + + dev = espi->dev; + + perif = &espi->perif; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_PERIF); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); ++ ++ reg = readl(espi->regs + ESPI_CTRL2); ++ reg |= (ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); ++ writel(reg, espi->regs + ESPI_CTRL2); + + reg = readl(espi->regs + ESPI_CTRL); + reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN @@ -76356,6 +78386,21 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + | ESPI_CTRL_PERIF_SW_RDY); + writel(reg, espi->regs + ESPI_CTRL); + ++ if (perif->mmbi.enable) { ++ reg = readl(espi->regs + ESPI_MMBI_CTRL); ++ reg &= ~ESPI_MMBI_CTRL_EN; ++ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ ++ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { ++ mmbi = &perif->mmbi.inst[i]; ++ misc_deregister(&mmbi->b2h_mdev); ++ misc_deregister(&mmbi->h2b_mdev); ++ } ++ ++ dmam_free_coherent(dev, perif->mmbi.size, perif->mmbi.virt, ++ perif->mmbi.taddr); ++ } ++ + if (perif->mcyc.enable) + dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, + perif->mcyc.taddr); @@ -76379,27 +78424,42 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as +} + +/* virtual wire channel (CH1) */ -+static long ast2500_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2600_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_vw *vw; + struct aspeed_espi *espi; -+ u32 gpio; ++ u32 gpio, hw_mode; + + vw = container_of(fp->private_data, struct aspeed_espi_vw, mdev); + espi = container_of(vw, struct aspeed_espi, vw); + gpio = vw->gpio.val0; ++ hw_mode = vw->gpio.hw_mode; ++ ++ if (hw_mode) { ++ dev_err(espi->dev, "HW mode: vGPIO reflect on physical GPIO. Get state from GPIO driver.\n"); ++ return -EFAULT; ++ } + + switch (cmd) { + case ASPEED_ESPI_VW_GET_GPIO_VAL: -+ if (put_user(gpio, (u32 __user *)arg)) ++ if (put_user(gpio, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to get vGPIO value\n"); + return -EFAULT; ++ } ++ ++ dev_info(espi->dev, "Get vGPIO value: 0x%x\n", gpio); + break; ++ + case ASPEED_ESPI_VW_PUT_GPIO_VAL: -+ if (get_user(gpio, (u32 __user *)arg)) ++ if (get_user(gpio, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to put vGPIO value\n"); + return -EFAULT; ++ } + ++ dev_info(espi->dev, "Put vGPIO value: 0x%x\n", gpio); + writel(gpio, espi->regs + ESPI_VW_GPIO_VAL); + break; ++ + default: + return -EINVAL; + }; @@ -76407,142 +78467,97 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return 0; +} + -+static const struct file_operations ast2500_espi_vw_fops = { ++static const struct file_operations ast2600_espi_vw_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2500_espi_vw_ioctl, ++ .unlocked_ioctl = ast2600_espi_vw_ioctl, +}; + -+static void ast2500_espi_vw_isr(struct aspeed_espi *espi) ++static void ast2600_espi_vw_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_vw *vw; -+ u32 reg, sts, sts_sysevt; ++ u32 sts; + + vw = &espi->vw; + + sts = readl(espi->regs + ESPI_INT_STS); + -+ if (sts & ESPI_INT_STS_VW_SYSEVT) { -+ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ -+ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN) { -+ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_HOST_RST_ACK; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT); -+ writel(ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ } -+ -+ if (sts_sysevt & ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN) { -+ reg = readl(espi->regs + ESPI_VW_SYSEVT) | ESPI_VW_SYSEVT_OOB_RST_ACK; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT); -+ writel(ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN, espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ } -+ -+ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); -+ } -+ -+ if (sts & ESPI_INT_STS_VW_SYSEVT1) { -+ sts_sysevt = readl(espi->regs + ESPI_VW_SYSEVT1_INT_STS); -+ -+ if (sts_sysevt & ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN) { -+ reg = readl(espi->regs + ESPI_VW_SYSEVT1) | ESPI_VW_SYSEVT1_SUSPEND_ACK; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT1); -+ writel(ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN, espi->regs + ESPI_VW_SYSEVT1_INT_STS); -+ } -+ -+ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); -+ } -+ + if (sts & ESPI_INT_STS_VW_GPIO) { + vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); + writel(ESPI_INT_STS_VW_GPIO, espi->regs + ESPI_INT_STS); ++ } else if (sts & ESPI_INT_STS_VW_SYSEVT) { ++ /* Handle system event */ ++ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); ++ } else if (sts & (ESPI_INT_STS_VW_SYSEVT1)) { ++ /* Handle system event1 */ ++ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); + } +} + -+static void ast2500_espi_vw_reset(struct aspeed_espi *espi) ++static void ast2600_espi_vw_reset(struct aspeed_espi *espi) +{ + u32 reg; + struct aspeed_espi_vw *vw = &espi->vw; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_VW); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); + writel(ESPI_INT_STS_VW, espi->regs + ESPI_INT_STS); + -+ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); -+ -+ /* Host Reset Warn and OOB Reset Warn system events */ -+ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_T2) -+ | ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN -+ | ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_T2); -+ -+ reg = readl(espi->regs + ESPI_VW_SYSEVT_INT_EN) -+ | ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN -+ | ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT_INT_EN); -+ -+ /* Suspend Warn system event */ -+ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_T0) | ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_T0); ++ writel(vw->gpio.dir0, espi->regs + ESPI_VW_GPIO_DIR); + -+ reg = readl(espi->regs + ESPI_VW_SYSEVT1_INT_EN) | ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT1_INT_EN); ++ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); + -+ reg = readl(espi->regs + ESPI_INT_EN) -+ | ESPI_INT_EN_VW_GPIO -+ | ESPI_INT_EN_VW_SYSEVT -+ | ESPI_INT_EN_VW_SYSEVT1; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_VW_TX_SORT); ++ writel(reg, espi->regs + ESPI_CTRL2); + -+ reg = readl(espi->regs + ESPI_VW_SYSEVT) -+ | ESPI_VW_SYSEVT_SLV_BOOT_STS -+ | ESPI_VW_SYSEVT_SLV_BOOT_DONE; -+ writel(reg, espi->regs + ESPI_VW_SYSEVT); ++ writel(ESPI_INT_EN_VW_GPIO, espi->regs + ESPI_INT_EN); + + reg = readl(espi->regs + ESPI_CTRL) + | ((vw->gpio.hw_mode) ? 0 : ESPI_CTRL_VW_GPIO_SW) + | ESPI_CTRL_VW_SW_RDY; + writel(reg, espi->regs + ESPI_CTRL); ++ ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T0); ++ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T1); ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg |= ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); ++ ++ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_EN); ++ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_EN); ++ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_T0); +} + -+int ast2500_espi_vw_probe(struct aspeed_espi *espi) ++int ast2600_espi_vw_probe(struct aspeed_espi *espi) +{ + int rc; + struct device *dev = espi->dev; + struct aspeed_espi_vw *vw = &espi->vw; + -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_STS); -+ -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT1_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT1_INT_STS); -+ + vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); ++ of_property_read_u32(dev->of_node, "vw-gpio-direction", &vw->gpio.dir0); + + vw->mdev.parent = dev; + vw->mdev.minor = MISC_DYNAMIC_MINOR; + vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw", DEVICE_NAME); -+ vw->mdev.fops = &ast2500_espi_vw_fops; ++ vw->mdev.fops = &ast2600_espi_vw_fops; + rc = misc_register(&vw->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", vw->mdev.name); + return rc; + } + -+ ast2500_espi_vw_reset(espi); ++ ast2600_espi_vw_reset(espi); + + return 0; +} + -+int ast2500_espi_vw_remove(struct aspeed_espi *espi) ++int ast2600_espi_vw_remove(struct aspeed_espi *espi) +{ + struct aspeed_espi_vw *vw; -+ u32 reg; + + vw = &espi->vw; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_VW); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); + + misc_deregister(&vw->mdev); + @@ -76550,7 +78565,72 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as +} + +/* out-of-band channel (CH2) */ -+static long ast2500_espi_oob_get_rx(struct file *fp, ++static long ast2600_espi_oob_dma_get_rx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ struct ast2600_espi_oob_dma_rx_desc *d; ++ struct ast2600_espi_oob_dma_rx_desc *rx_descs; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u32 wptr, pkt_len; ++ unsigned long flags; ++ u8 *pkt; ++ int rc; ++ ++ espi = container_of(oob, struct aspeed_espi, oob); ++ ++ wptr = FIELD_PREP(ESPI_OOB_RX_DESC_WPTR_WP, readl(espi->regs + ESPI_OOB_RX_DESC_WPTR)); ++ ++ rx_descs = (struct ast2600_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ d = &rx_descs[wptr]; ++ ++ if (!d->dirty) ++ return -EFAULT; ++ ++ pkt_len = ((d->len) ? d->len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++ ++ if (ioc->pkt_len < pkt_len) ++ return -EINVAL; ++ ++ pkt = vmalloc(pkt_len); ++ if (!pkt) ++ return -ENOMEM; ++ ++ hdr = (struct espi_comm_hdr *)pkt; ++ hdr->cyc = d->cyc; ++ hdr->tag = d->tag; ++ hdr->len_h = d->len >> 8; ++ hdr->len_l = d->len & 0xff; ++ memcpy(hdr + 1, oob->dma.rx_virt + (PAGE_SIZE * wptr), pkt_len - sizeof(*hdr)); ++ ++ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ spin_lock_irqsave(&oob->lock, flags); ++ ++ /* make current descriptor available again */ ++ d->dirty = 0; ++ ++ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; ++ writel(wptr | ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); ++ ++ /* set ready flag base on the next RX descriptor */ ++ oob->rx_ready = rx_descs[wptr].dirty; ++ ++ spin_unlock_irqrestore(&oob->lock, flags); ++ ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++ return rc; ++} ++ ++static long ast2600_espi_oob_get_rx(struct file *fp, + struct aspeed_espi_oob *oob, + struct aspeed_espi_ioc *ioc) +{ @@ -76584,6 +78664,11 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + } + } + ++ if (oob->dma.enable) { ++ rc = ast2600_espi_oob_dma_get_rx(fp, oob, ioc); ++ goto unlock_mtx_n_out; ++ } ++ + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers @@ -76617,12 +78702,8 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + -+ if (oob->dma.enable) { -+ memcpy(hdr + 1, oob->dma.rx_virt, pkt_len - sizeof(*hdr)); -+ } else { -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; -+ } ++ for (i = sizeof(*hdr); i < pkt_len; ++i) ++ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; @@ -76647,7 +78728,65 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2500_espi_oob_put_tx(struct file *fp, ++static long ast2600_espi_oob_dma_put_tx(struct file *fp, ++ struct aspeed_espi_oob *oob, ++ struct aspeed_espi_ioc *ioc) ++{ ++ struct ast2600_espi_oob_dma_tx_desc *d; ++ struct ast2600_espi_oob_dma_tx_desc *tx_descs; ++ struct aspeed_espi *espi; ++ struct espi_comm_hdr *hdr; ++ u32 rptr, wptr; ++ u8 *pkt; ++ int rc; ++ ++ espi = container_of(oob, struct aspeed_espi, oob); ++ ++ pkt = vzalloc(ioc->pkt_len); ++ if (!pkt) ++ return -ENOMEM; ++ ++ hdr = (struct espi_comm_hdr *)pkt; ++ ++ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { ++ rc = -EFAULT; ++ goto free_n_out; ++ } ++ ++ /* kick HW to update descriptor read/write pointer */ ++ writel(ESPI_OOB_TX_DESC_RPTR_UPDATE, espi->regs + ESPI_OOB_TX_DESC_RPTR); ++ ++ rptr = readl(espi->regs + ESPI_OOB_TX_DESC_RPTR); ++ wptr = readl(espi->regs + ESPI_OOB_TX_DESC_WPTR); ++ ++ if (((wptr + 1) % OOB_DMA_DESC_NUM) == rptr) { ++ rc = -EBUSY; ++ goto free_n_out; ++ } ++ ++ tx_descs = (struct ast2600_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ d = &tx_descs[wptr]; ++ d->cyc = hdr->cyc; ++ d->tag = hdr->tag; ++ d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ d->msg_type = OOB_DMA_DESC_CUSTOM; ++ ++ memcpy(oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ ++ dma_wmb(); ++ ++ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; ++ writel(wptr | ESPI_OOB_TX_DESC_WPTR_SEND_EN, espi->regs + ESPI_OOB_TX_DESC_WPTR); ++ ++ rc = 0; ++ ++free_n_out: ++ vfree(pkt); ++ ++ return rc; ++} ++ ++static long ast2600_espi_oob_put_tx(struct file *fp, + struct aspeed_espi_oob *oob, + struct aspeed_espi_ioc *ioc) +{ @@ -76662,6 +78801,11 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + if (!mutex_trylock(&oob->tx_mtx)) + return -EAGAIN; + ++ if (oob->dma.enable) { ++ rc = ast2600_espi_oob_dma_put_tx(fp, oob, ioc); ++ goto unlock_mtx_n_out; ++ } ++ + reg = readl(espi->regs + ESPI_OOB_TX_CTRL); + if (reg & ESPI_OOB_TX_CTRL_TRIG_PEND) { + rc = -EBUSY; @@ -76690,13 +78834,8 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ if (oob->dma.enable) { -+ memcpy(oob->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); -+ } ++ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) ++ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); + + cyc = hdr->cyc; + tag = hdr->tag; @@ -76719,7 +78858,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2500_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2600_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_oob *oob; + struct aspeed_espi_ioc ioc; @@ -76734,22 +78873,20 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + + switch (cmd) { + case ASPEED_ESPI_OOB_GET_RX: -+ return ast2500_espi_oob_get_rx(fp, oob, &ioc); ++ return ast2600_espi_oob_get_rx(fp, oob, &ioc); + case ASPEED_ESPI_OOB_PUT_TX: -+ return ast2500_espi_oob_put_tx(fp, oob, &ioc); -+ default: -+ break; ++ return ast2600_espi_oob_put_tx(fp, oob, &ioc); + }; + + return -EINVAL; +} + -+static const struct file_operations ast2500_espi_oob_fops = { ++static const struct file_operations ast2600_espi_oob_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2500_espi_oob_ioctl, ++ .unlocked_ioctl = ast2600_espi_oob_ioctl, +}; + -+static void ast2500_espi_oob_isr(struct aspeed_espi *espi) ++static void ast2600_espi_oob_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; + unsigned long flags; @@ -76770,16 +78907,16 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + } +} + -+static void ast2500_espi_oob_reset(struct aspeed_espi *espi) ++static void ast2600_espi_oob_reset(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; ++ struct ast2600_espi_oob_dma_tx_desc *tx_desc; ++ struct ast2600_espi_oob_dma_rx_desc *rx_desc; ++ dma_addr_t tx_addr, rx_addr; + u32 reg; ++ int i; + -+ oob = &espi->oob; -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_OOB); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); + writel(ESPI_INT_STS_OOB, espi->regs + ESPI_INT_STS); + + reg = readl(espi->regs + ESPI_CTRL); @@ -76795,24 +78932,50 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + reg |= (ESPI_CTRL_OOB_TX_SW_RST | ESPI_CTRL_OOB_RX_SW_RST); + writel(reg, espi->regs + ESPI_CTRL); + ++ oob = &espi->oob; ++ + if (oob->dma.enable) { -+ writel(oob->dma.tx_addr, espi->regs + ESPI_OOB_TX_DMA); -+ writel(oob->dma.rx_addr, espi->regs + ESPI_OOB_RX_DMA); ++ tx_addr = oob->dma.tx_addr; ++ rx_addr = oob->dma.rx_addr; ++ ++ tx_desc = (struct ast2600_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ rx_desc = (struct ast2600_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ ++ for (i = 0; i < OOB_DMA_DESC_NUM; ++i) { ++ tx_desc[i].data_addr = tx_addr; ++ tx_addr += PAGE_SIZE; ++ ++ rx_desc[i].data_addr = rx_addr; ++ rx_desc[i].dirty = 0; ++ rx_addr += PAGE_SIZE; ++ } ++ ++ writel(oob->dma.txd_addr, espi->regs + ESPI_OOB_TX_DMA); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_TX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_OOB_TX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_TX_DESC_NUM); ++ ++ writel(oob->dma.rxd_addr, espi->regs + ESPI_OOB_RX_DMA); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_RX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_OOB_RX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_RX_DESC_NUM); + + reg = readl(espi->regs + ESPI_CTRL) + | ESPI_CTRL_OOB_TX_DMA_EN + | ESPI_CTRL_OOB_RX_DMA_EN; + writel(reg, espi->regs + ESPI_CTRL); ++ ++ /* activate RX DMA to make OOB_FREE */ ++ writel(ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); + } + -+ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_OOB_RX_CMPLT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_OOB_RX_CMPLT, espi->regs + ESPI_INT_EN); + + reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_OOB_SW_RDY; + writel(reg, espi->regs + ESPI_CTRL); +} + -+int ast2500_espi_oob_probe(struct aspeed_espi *espi) ++int ast2600_espi_oob_probe(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; + struct device *dev; @@ -76831,13 +78994,30 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + + oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); + if (oob->dma.enable) { -+ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.tx_addr, GFP_KERNEL); ++ oob->dma.txd_virt = dmam_alloc_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_tx_desc) * ++ OOB_DMA_DESC_NUM, ++ &oob->dma.txd_addr, GFP_KERNEL); ++ if (!oob->dma.txd_virt) { ++ dev_err(dev, "cannot allocate DMA TX descriptor\n"); ++ return -ENOMEM; ++ } ++ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.tx_addr, GFP_KERNEL); + if (!oob->dma.tx_virt) { + dev_err(dev, "cannot allocate DMA TX buffer\n"); + return -ENOMEM; + } + -+ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &oob->dma.rx_addr, GFP_KERNEL); ++ oob->dma.rxd_virt = dmam_alloc_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_rx_desc) * ++ OOB_DMA_DESC_NUM, ++ &oob->dma.rxd_addr, GFP_KERNEL); ++ if (!oob->dma.rxd_virt) { ++ dev_err(dev, "cannot allocate DMA RX descriptor\n"); ++ return -ENOMEM; ++ } ++ ++ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.rx_addr, GFP_KERNEL); + if (!oob->dma.rx_virt) { + dev_err(dev, "cannot allocate DMA TX buffer\n"); + return -ENOMEM; @@ -76847,19 +79027,19 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + oob->mdev.parent = dev; + oob->mdev.minor = MISC_DYNAMIC_MINOR; + oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob", DEVICE_NAME); -+ oob->mdev.fops = &ast2500_espi_oob_fops; ++ oob->mdev.fops = &ast2600_espi_oob_fops; + rc = misc_register(&oob->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", oob->mdev.name); + return rc; + } + -+ ast2500_espi_oob_reset(espi); ++ ast2600_espi_oob_reset(espi); + + return 0; +} + -+int ast2500_espi_oob_remove(struct aspeed_espi *espi) ++int ast2600_espi_oob_remove(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; + struct device *dev; @@ -76869,9 +79049,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + + oob = &espi->oob; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_OOB); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); + + reg = readl(espi->regs + ESPI_CTRL); + reg &= ~(ESPI_CTRL_OOB_TX_DMA_EN @@ -76880,8 +79058,19 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + writel(reg, espi->regs + ESPI_CTRL); + + if (oob->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.tx_virt, oob->dma.tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, oob->dma.rx_virt, oob->dma.rx_addr); ++ dmam_free_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_tx_desc) * ++ OOB_DMA_DESC_NUM, ++ oob->dma.txd_virt, oob->dma.txd_addr); ++ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, ++ oob->dma.tx_virt, oob->dma.tx_addr); ++ ++ dmam_free_coherent(dev, ++ sizeof(struct ast2600_espi_oob_dma_rx_desc) * ++ OOB_DMA_DESC_NUM, ++ oob->dma.rxd_virt, oob->dma.rxd_addr); ++ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, ++ oob->dma.rx_virt, oob->dma.rx_addr); + } + + mutex_destroy(&oob->tx_mtx); @@ -76893,7 +79082,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as +} + +/* flash channel (CH3) */ -+static long ast2500_espi_flash_get_rx(struct file *fp, ++static long ast2600_espi_flash_get_rx(struct file *fp, + struct aspeed_espi_flash *flash, + struct aspeed_espi_ioc *ioc) +{ @@ -77015,7 +79204,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2500_espi_flash_put_tx(struct file *fp, ++static long ast2600_espi_flash_put_tx(struct file *fp, + struct aspeed_espi_flash *flash, + struct aspeed_espi_ioc *ioc) +{ @@ -77082,7 +79271,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2500_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2600_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_flash *flash; + struct aspeed_espi_ioc ioc; @@ -77097,9 +79286,9 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + + switch (cmd) { + case ASPEED_ESPI_FLASH_GET_RX: -+ return ast2500_espi_flash_get_rx(fp, flash, &ioc); ++ return ast2600_espi_flash_get_rx(fp, flash, &ioc); + case ASPEED_ESPI_FLASH_PUT_TX: -+ return ast2500_espi_flash_put_tx(fp, flash, &ioc); ++ return ast2600_espi_flash_put_tx(fp, flash, &ioc); + default: + break; + }; @@ -77107,12 +79296,12 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return -EINVAL; +} + -+static const struct file_operations ast2500_espi_flash_fops = { ++static const struct file_operations ast2600_espi_flash_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2500_espi_flash_ioctl, ++ .unlocked_ioctl = ast2600_espi_flash_ioctl, +}; + -+static void ast2500_espi_flash_isr(struct aspeed_espi *espi) ++static void ast2600_espi_flash_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_flash *flash; + unsigned long flags; @@ -77123,24 +79312,24 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + sts = readl(espi->regs + ESPI_INT_STS); + + if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { ++ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ + spin_lock_irqsave(&flash->lock, flags); + flash->rx_ready = true; + spin_unlock_irqrestore(&flash->lock, flags); + + wake_up_interruptible(&flash->wq); -+ -+ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); + } +} + -+static void ast2500_espi_flash_reset(struct aspeed_espi *espi) ++static void ast2600_espi_flash_reset(struct aspeed_espi *espi) +{ -+ struct aspeed_espi_flash *flash = &espi->flash; ++ struct aspeed_espi_flash *flash; + u32 reg; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_FLASH); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ flash = &espi->flash; ++ ++ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); + writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS); + + reg = readl(espi->regs + ESPI_CTRL); @@ -77156,13 +79345,14 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST); + writel(reg, espi->regs + ESPI_CTRL); + ++ reg = readl(espi->regs + ESPI_CTRL) & ~ESPI_CTRL_FLASH_EDAF_MODE; ++ reg |= FIELD_PREP(ESPI_CTRL_FLASH_EDAF_MODE, flash->edaf.mode); ++ writel(reg, espi->regs + ESPI_CTRL); ++ + if (flash->edaf.mode == EDAF_MODE_MIX) { -+ reg = FIELD_PREP(ESPI_FLASH_SAFS_TADDR_BASE, flash->edaf.taddr >> 24) -+ | FIELD_PREP(ESPI_FLASH_SAFS_TADDR_MASK, (~(flash->edaf.size - 1)) >> 24); -+ writel(reg, espi->regs + ESPI_FLASH_SAFS_TADDR); -+ } else { -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SAFS_SW_MODE; -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = FIELD_PREP(ESPI_FLASH_EDAF_TADDR_BASE, flash->edaf.taddr >> 24) ++ | FIELD_PREP(ESPI_FLASH_EDAF_TADDR_MASK, (~(flash->edaf.size - 1)) >> 24); ++ writel(reg, espi->regs + ESPI_FLASH_EDAF_TADDR); + } + + if (flash->dma.enable) { @@ -77171,18 +79361,17 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + + reg = readl(espi->regs + ESPI_CTRL) + | ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN; ++ | ESPI_CTRL_FLASH_RX_DMA_EN; + writel(reg, espi->regs + ESPI_CTRL); + } + -+ reg = readl(espi->regs + ESPI_INT_EN) | ESPI_INT_EN_FLASH_RX_CMPLT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_FLASH_RX_CMPLT, espi->regs + ESPI_INT_EN); + + reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY; + writel(reg, espi->regs + ESPI_CTRL); +} + -+int ast2500_espi_flash_probe(struct aspeed_espi *espi) ++int ast2600_espi_flash_probe(struct aspeed_espi *espi) +{ + struct aspeed_espi_flash *flash; + struct device *dev; @@ -77199,18 +79388,18 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + mutex_init(&flash->tx_mtx); + mutex_init(&flash->rx_mtx); + -+ flash->edaf.mode = EDAF_MODE_MIX; ++ flash->edaf.mode = EDAF_MODE_HW; + + of_property_read_u32(dev->of_node, "flash-safs-mode", &flash->edaf.mode); + if (flash->edaf.mode == EDAF_MODE_MIX) { + rc = of_property_read_u32(dev->of_node, "flash-safs-tgt-addr", (u32 *)&flash->edaf.taddr); -+ if (rc || !IS_ALIGNED(flash->edaf.taddr, FLASH_SAFS_ALIGN)) { ++ if (rc || !IS_ALIGNED(flash->edaf.taddr, FLASH_EDAF_ALIGN)) { + dev_err(dev, "cannot get 16MB-aligned SAFS target address\n"); + return -ENODEV; + } + + rc = of_property_read_u32(dev->of_node, "flash-safs-size", (u32 *)&flash->edaf.size); -+ if (rc || !IS_ALIGNED(flash->edaf.size, FLASH_SAFS_ALIGN)) { ++ if (rc || !IS_ALIGNED(flash->edaf.size, FLASH_EDAF_ALIGN)) { + dev_err(dev, "cannot get 16MB-aligned SAFS size\n"); + return -ENODEV; + } @@ -77234,19 +79423,19 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + flash->mdev.parent = dev; + flash->mdev.minor = MISC_DYNAMIC_MINOR; + flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash", DEVICE_NAME); -+ flash->mdev.fops = &ast2500_espi_flash_fops; ++ flash->mdev.fops = &ast2600_espi_flash_fops; + rc = misc_register(&flash->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", flash->mdev.name); + return rc; + } + -+ ast2500_espi_flash_reset(espi); ++ ast2600_espi_flash_reset(espi); + + return 0; +} + -+int ast2500_espi_flash_remove(struct aspeed_espi *espi) ++int ast2600_espi_flash_remove(struct aspeed_espi *espi) +{ + struct aspeed_espi_flash *flash; + struct device *dev; @@ -77256,9 +79445,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + + flash = &espi->flash; + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_FLASH); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); + + reg = readl(espi->regs + ESPI_CTRL); + reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN @@ -77280,7 +79467,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as +} + +/* global control */ -+irqreturn_t ast2500_espi_isr(int irq, void *arg) ++irqreturn_t ast2600_espi_isr(int irq, void *arg) +{ + struct aspeed_espi *espi; + u32 sts; @@ -77292,68 +79479,64 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.c b/drivers/soc/aspeed/espi/as + return IRQ_NONE; + + if (sts & ESPI_INT_STS_PERIF) -+ ast2500_espi_perif_isr(espi); ++ ast2600_espi_perif_isr(espi); + + if (sts & ESPI_INT_STS_VW) -+ ast2500_espi_vw_isr(espi); ++ ast2600_espi_vw_isr(espi); + + if (sts & ESPI_INT_STS_OOB) -+ ast2500_espi_oob_isr(espi); ++ ast2600_espi_oob_isr(espi); + + if (sts & ESPI_INT_STS_FLASH) -+ ast2500_espi_flash_isr(espi); ++ ast2600_espi_flash_isr(espi); + + if (sts & ESPI_INT_STS_RST_DEASSERT) { -+ ast2500_espi_perif_reset(espi); -+ ast2500_espi_vw_reset(espi); -+ ast2500_espi_oob_reset(espi); -+ ast2500_espi_flash_reset(espi); -+ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); ++ /* this will clear all interrupt enable and status */ ++ reset_control_assert(espi->rst); ++ reset_control_deassert(espi->rst); ++ ++ ast2600_espi_perif_sw_reset(espi); ++ ast2600_espi_perif_reset(espi); ++ ast2600_espi_vw_reset(espi); ++ ast2600_espi_oob_reset(espi); ++ ast2600_espi_flash_reset(espi); ++ ++ /* re-enable eSPI_RESET# interrupt */ ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); + } + + return IRQ_HANDLED; +} + -+void ast2500_espi_pre_init(struct aspeed_espi *espi) ++void ast2600_espi_pre_init(struct aspeed_espi *espi) +{ -+ u32 reg; -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); +} + -+void ast2500_espi_post_init(struct aspeed_espi *espi) ++void ast2600_espi_post_init(struct aspeed_espi *espi) +{ -+ u32 reg; -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg |= ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); +} + -+void ast2500_espi_deinit(struct aspeed_espi *espi) ++void ast2600_espi_deinit(struct aspeed_espi *espi) +{ -+ u32 reg; -+ -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~(ESPI_INT_EN_RST_DEASSERT); -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); +} -diff --git a/drivers/soc/aspeed/espi/ast2500-espi.h b/drivers/soc/aspeed/espi/ast2500-espi.h ---- a/drivers/soc/aspeed/espi/ast2500-espi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/ast2500-espi.h 2026-04-08 18:03:48.312705284 +0000 -@@ -0,0 +1,258 @@ +diff --git a/drivers/soc/aspeed/espi/ast2600-espi.h b/drivers/soc/aspeed/espi/ast2600-espi.h +--- a/drivers/soc/aspeed/espi/ast2600-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2600-espi.h 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,306 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* -+ * Register definitions for Aspeed AST2500 eSPI controller ++ * Register definitions for Aspeed AST2600 eSPI controller + * Copyright 2023 Aspeed Technology Inc. + */ -+#ifndef _AST2500_ESPI_H_ -+#define _AST2500_ESPI_H_ ++#ifndef _AST2600_ESPI_H_ ++#define _AST2600_ESPI_H_ + +#include +#include ++ +#include "aspeed-espi.h" + +/* registers */ @@ -77373,7 +79556,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.h b/drivers/soc/aspeed/espi/as +#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) +#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) +#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) -+#define ESPI_CTRL_FLASH_SAFS_SW_MODE BIT(10) ++#define ESPI_CTRL_FLASH_EDAF_MODE GENMASK(11, 10) +#define ESPI_CTRL_VW_GPIO_SW BIT(9) +#define ESPI_CTRL_FLASH_SW_RDY BIT(7) +#define ESPI_CTRL_OOB_SW_RDY BIT(4) @@ -77479,22 +79662,41 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.h b/drivers/soc/aspeed/espi/as +#define ESPI_FLASH_TX_CTRL_TAG GENMASK(11, 8) +#define ESPI_FLASH_TX_CTRL_CYC GENMASK(7, 0) +#define ESPI_FLASH_TX_DATA 0x078 ++#define ESPI_CTRL2 0x080 ++#define ESPI_CTRL2_VW_TX_SORT BIT(30) ++#define ESPI_CTRL2_MCYC_RD_DIS_WDT BIT(11) ++#define ESPI_CTRL2_MCYC_WR_DIS_WDT BIT(10) ++#define ESPI_CTRL2_MCYC_RD_DIS BIT(6) ++#define ESPI_CTRL2_MMBI_RD_DIS ESPI_CTRL2_MCYC_RD_DIS ++#define ESPI_CTRL2_MCYC_WR_DIS BIT(4) ++#define ESPI_CTRL2_MMBI_WR_DIS ESPI_CTRL2_MCYC_WR_DIS +#define ESPI_PERIF_MCYC_SADDR 0x084 ++#define ESPI_PERIF_MMBI_SADDR ESPI_PERIF_MCYC_SADDR +#define ESPI_PERIF_MCYC_TADDR 0x088 ++#define ESPI_PERIF_MMBI_TADDR ESPI_PERIF_MCYC_TADDR +#define ESPI_PERIF_MCYC_MASK 0x08c -+#define ESPI_FLASH_SAFS_TADDR 0x090 -+#define ESPI_FLASH_SAFS_TADDR_BASE GENMASK(31, 24) -+#define ESPI_FLASH_SAFS_TADDR_MASK GENMASK(15, 8) ++#define ESPI_PERIF_MMBI_MASK ESPI_PERIF_MCYC_MASK ++#define ESPI_FLASH_EDAF_TADDR 0x090 ++#define ESPI_FLASH_EDAF_TADDR_BASE GENMASK(31, 24) ++#define ESPI_FLASH_EDAF_TADDR_MASK GENMASK(15, 8) +#define ESPI_VW_SYSEVT_INT_EN 0x094 -+#define ESPI_VW_SYSEVT_INT_EN_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_INT_EN_OOB_RST_WARN BIT(6) +#define ESPI_VW_SYSEVT 0x098 +#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27) ++#define ESPI_VW_SYSEVT_RST_CPU_INIT BIT(26) +#define ESPI_VW_SYSEVT_SLV_BOOT_STS BIT(23) ++#define ESPI_VW_SYSEVT_NON_FATAL_ERR BIT(22) ++#define ESPI_VW_SYSEVT_FATAL_ERR BIT(21) +#define ESPI_VW_SYSEVT_SLV_BOOT_DONE BIT(20) +#define ESPI_VW_SYSEVT_OOB_RST_ACK BIT(16) ++#define ESPI_VW_SYSEVT_NMI_OUT BIT(10) ++#define ESPI_VW_SYSEVT_SMI_OUT BIT(9) +#define ESPI_VW_SYSEVT_HOST_RST_WARN BIT(8) +#define ESPI_VW_SYSEVT_OOB_RST_WARN BIT(6) ++#define ESPI_VW_SYSEVT_PLTRSTN BIT(5) ++#define ESPI_VW_SYSEVT_SUSPEND BIT(4) ++#define ESPI_VW_SYSEVT_S5_SLEEP BIT(2) ++#define ESPI_VW_SYSEVT_S4_SLEEP BIT(1) ++#define ESPI_VW_SYSEVT_S3_SLEEP BIT(0) +#define ESPI_VW_GPIO_VAL 0x09c +#define ESPI_GEN_CAP_N_CONF 0x0a0 +#define ESPI_CH0_CAP_N_CONF 0x0a4 @@ -77504,25 +79706,39 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.h b/drivers/soc/aspeed/espi/as +#define ESPI_CH3_CAP_N_CONF2 0x0b4 +#define ESPI_VW_GPIO_DIR 0x0c0 +#define ESPI_VW_GPIO_GRP 0x0c4 ++#define ESPI_INT_EN_CLR 0x0fc +#define ESPI_VW_SYSEVT1_INT_EN 0x100 -+#define ESPI_VW_SYSEVT1_INT_EN_SUSPEND_WARN BIT(0) +#define ESPI_VW_SYSEVT1 0x104 +#define ESPI_VW_SYSEVT1_SUSPEND_ACK BIT(20) +#define ESPI_VW_SYSEVT1_SUSPEND_WARN BIT(0) +#define ESPI_VW_SYSEVT_INT_T0 0x110 +#define ESPI_VW_SYSEVT_INT_T1 0x114 +#define ESPI_VW_SYSEVT_INT_T2 0x118 -+#define ESPI_VW_SYSEVT_INT_T2_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_INT_T2_OOB_RST_WARN BIT(6) +#define ESPI_VW_SYSEVT_INT_STS 0x11c -+#define ESPI_VW_SYSEVT_INT_STS_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_INT_STS_OOB_RST_WARN BIT(6) +#define ESPI_VW_SYSEVT1_INT_T0 0x120 -+#define ESPI_VW_SYSEVT1_INT_T0_SUSPEND_WARN BIT(0) +#define ESPI_VW_SYSEVT1_INT_T1 0x124 +#define ESPI_VW_SYSEVT1_INT_T2 0x128 +#define ESPI_VW_SYSEVT1_INT_STS 0x12c -+#define ESPI_VW_SYSEVT1_INT_STS_SUSPEND_WARN BIT(0) ++#define ESPI_OOB_RX_DESC_NUM 0x130 ++#define ESPI_OOB_RX_DESC_RPTR 0x134 ++#define ESPI_OOB_RX_DESC_RPTR_UPDATE BIT(31) ++#define ESPI_OOB_RX_DESC_RPTR_RP GENMASK(11, 0) ++#define ESPI_OOB_RX_DESC_WPTR 0x138 ++#define ESPI_OOB_RX_DESC_WPTR_RECV_EN BIT(31) ++#define ESPI_OOB_RX_DESC_WPTR_SP GENMASK(27, 16) ++#define ESPI_OOB_RX_DESC_WPTR_WP GENMASK(11, 0) ++#define ESPI_OOB_TX_DESC_NUM 0x140 ++#define ESPI_OOB_TX_DESC_RPTR 0x144 ++#define ESPI_OOB_TX_DESC_RPTR_UPDATE BIT(31) ++#define ESPI_OOB_TX_DESC_WPTR 0x148 ++#define ESPI_OOB_TX_DESC_WPTR_SEND_EN BIT(31) ++#define ESPI_MMBI_CTRL 0x800 ++#define ESPI_MMBI_CTRL_INST_SZ GENMASK(10, 8) ++#define ESPI_MMBI_CTRL_TOTAL_SZ GENMASK(6, 4) ++#define ESPI_MMBI_CTRL_EN BIT(0) ++#define ESPI_MMBI_INT_STS 0x808 ++#define ESPI_MMBI_INT_EN 0x80c ++#define ESPI_MMBI_HOST_RWP(x) (0x810 + ((x) << 3)) + +/* collect ESPI_INT_EN bits for convenience */ +#define ESPI_INT_EN_PERIF \ @@ -77588,138 +79804,87 @@ diff --git a/drivers/soc/aspeed/espi/ast2500-espi.h b/drivers/soc/aspeed/espi/as + ESPI_INT_STS_FLASH_TX_CMPLT | \ + ESPI_INT_STS_FLASH_RX_CMPLT) + -+/* operators for framework initialization */ -+void ast2500_espi_pre_init(struct aspeed_espi *espi); -+void ast2500_espi_post_init(struct aspeed_espi *espi); -+void ast2500_espi_deinit(struct aspeed_espi *espi); -+int ast2500_espi_perif_probe(struct aspeed_espi *espi); -+int ast2500_espi_perif_remove(struct aspeed_espi *espi); -+int ast2500_espi_vw_probe(struct aspeed_espi *espi); -+int ast2500_espi_vw_remove(struct aspeed_espi *espi); -+int ast2500_espi_oob_probe(struct aspeed_espi *espi); -+int ast2500_espi_oob_remove(struct aspeed_espi *espi); -+int ast2500_espi_flash_probe(struct aspeed_espi *espi); -+int ast2500_espi_flash_remove(struct aspeed_espi *espi); -+irqreturn_t ast2500_espi_isr(int irq, void *arg); ++ ++/* consistent with DTS property "perif-mmbi-instance-size" */ ++enum ast2600_mmbi_instance_size { ++ MMBI_INST_SIZE_8KB = 0x0, ++ MMBI_INST_SIZE_16KB, ++ MMBI_INST_SIZE_32KB, ++ MMBI_INST_SIZE_64KB, ++ MMBI_INST_SIZE_128KB, ++ MMBI_INST_SIZE_256KB, ++ MMBI_INST_SIZE_512KB, ++ MMBI_INST_SIZE_1024KB, ++ MMBI_INST_SIZE_TYPES, ++}; ++ ++/* function operators */ ++void ast2600_espi_pre_init(struct aspeed_espi *espi); ++void ast2600_espi_post_init(struct aspeed_espi *espi); ++void ast2600_espi_deinit(struct aspeed_espi *espi); ++int ast2600_espi_perif_probe(struct aspeed_espi *espi); ++int ast2600_espi_perif_remove(struct aspeed_espi *espi); ++int ast2600_espi_vw_probe(struct aspeed_espi *espi); ++int ast2600_espi_vw_remove(struct aspeed_espi *espi); ++int ast2600_espi_oob_probe(struct aspeed_espi *espi); ++int ast2600_espi_oob_remove(struct aspeed_espi *espi); ++int ast2600_espi_flash_probe(struct aspeed_espi *espi); ++int ast2600_espi_flash_remove(struct aspeed_espi *espi); ++irqreturn_t ast2600_espi_isr(int irq, void *arg); +#endif -diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/ast2600-espi.c ---- a/drivers/soc/aspeed/espi/ast2600-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/ast2600-espi.c 2026-04-08 18:03:48.313705265 +0000 -@@ -0,0 +1,1910 @@ +diff --git a/drivers/soc/aspeed/espi/ast2700-espi.c b/drivers/soc/aspeed/espi/ast2700-espi.c +--- a/drivers/soc/aspeed/espi/ast2700-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-espi.c 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,1960 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Aspeed Technology Inc. + */ ++#include "linux/err.h" ++#include "linux/fs.h" ++#include "linux/printk.h" ++#include "linux/spinlock.h" ++#include "linux/stddef.h" ++#include "linux/wait.h" ++#include "linux/kernel.h" +#include +#include +#include +#include +#include +#include -+#include ++#include +#include ++#include ++#include ++#include +#include +#include ++#include +#include +#include +#include +#include +#include +#include -+#include ++#include + -+#include "ast2600-espi.h" ++#include "ast2700-espi.h" +#include "aspeed-espi-comm.h" +#include "aspeed-espi.h" + ++static DEFINE_IDA(ast2700_espi_ida); ++ +#define PERIF_MCYC_ALIGN SZ_64K -+#define PERIF_MMBI_ALIGN SZ_64K -+#define PERIF_MMBI_INST_NUM 8 + -+#define OOB_DMA_RPTR_KEY 0x45538073 ++#define OOB_DMA_RPTR_KEY 0x4f4f4253 +#define OOB_DMA_DESC_NUM 8 +#define OOB_DMA_DESC_CUSTOM 0x4 + +#define FLASH_EDAF_ALIGN SZ_16M + +/* peripheral channel (CH0) */ -+static int ast2600_espi_mmbi_b2h_mmap(struct file *fp, struct vm_area_struct *vma) -+{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct aspeed_espi *espi; -+ unsigned long vm_size; -+ pgprot_t prot; -+ -+ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, b2h_mdev); -+ -+ perif = mmbi->perif; -+ -+ espi = container_of(perif, struct aspeed_espi, perif); -+ -+ vm_size = vma->vm_end - vma->vm_start; -+ prot = vma->vm_page_prot; -+ -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) -+ return -EINVAL; -+ -+ prot = pgprot_noncached(prot); -+ -+ if (remap_pfn_range(vma, vma->vm_start, -+ (mmbi->b2h_addr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, prot)) -+ return -EAGAIN; -+ -+ return 0; -+} -+ -+static int ast2600_espi_mmbi_h2b_mmap(struct file *fp, struct vm_area_struct *vma) -+{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct aspeed_espi *espi; -+ unsigned long vm_size; -+ pgprot_t prot; -+ -+ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); -+ -+ perif = mmbi->perif; -+ -+ espi = container_of(perif, struct aspeed_espi, perif); -+ -+ vm_size = vma->vm_end - vma->vm_start; -+ prot = vma->vm_page_prot; -+ -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (SZ_4K << perif->mmbi.inst_size)) -+ return -EINVAL; -+ -+ prot = pgprot_noncached(prot); -+ -+ if (remap_pfn_range(vma, vma->vm_start, -+ (mmbi->h2b_addr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, prot)) -+ return -EAGAIN; -+ -+ return 0; -+} -+ -+static __poll_t ast2600_espi_mmbi_h2b_poll(struct file *fp, struct poll_table_struct *pt) -+{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ -+ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); -+ -+ poll_wait(fp, &mmbi->wq, pt); -+ -+ if (!mmbi->host_rwp_update) -+ return 0; -+ -+ mmbi->host_rwp_update = false; -+ -+ return EPOLLIN; -+} -+ -+static long ast2600_espi_perif_pc_get_rx(struct file *fp, ++static long ast2700_espi_perif_pc_get_rx(struct file *fp, + struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ @@ -77757,10 +79922,10 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ reg = readl(espi->regs + ESPI_PERIF_PC_RX_CTRL); -+ cyc = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_PERIF_PC_RX_CTRL_LEN, reg); ++ reg = readl(espi->regs + ESPI_CH0_PC_RX_CTRL); ++ cyc = FIELD_GET(ESPI_CH0_PC_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_CH0_PC_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_CH0_PC_RX_CTRL_LEN, reg); + + /* + * calculate the length of the rest part of the @@ -77812,7 +79977,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + memcpy(hdr + 1, perif->dma.pc_rx_virt, pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) -+ reg = readl(espi->regs + ESPI_PERIF_PC_RX_DATA) & 0xff; ++ reg = readl(espi->regs + ESPI_CH0_PC_RX_DATA) & 0xff; + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { @@ -77822,7 +79987,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + spin_lock_irqsave(&perif->lock, flags); + -+ writel(ESPI_PERIF_PC_RX_CTRL_SERV_PEND, espi->regs + ESPI_PERIF_PC_RX_CTRL); ++ writel(ESPI_CH0_PC_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH0_PC_RX_CTRL); + perif->rx_ready = 0; + + spin_unlock_irqrestore(&perif->lock, flags); @@ -77838,7 +80003,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_perif_pc_put_tx(struct file *fp, ++static long ast2700_espi_perif_pc_put_tx(struct file *fp, + struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ @@ -77853,8 +80018,8 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + if (!mutex_trylock(&perif->pc_tx_mtx)) + return -EAGAIN; + -+ reg = readl(espi->regs + ESPI_PERIF_PC_TX_CTRL); -+ if (reg & ESPI_PERIF_PC_TX_CTRL_TRIG_PEND) { ++ reg = readl(espi->regs + ESPI_CH0_PC_TX_CTRL); ++ if (reg & ESPI_CH0_PC_TX_CTRL_TRIG_PEND) { + rc = -EBUSY; + goto unlock_n_out; + } @@ -77881,18 +80046,18 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_PERIF_PC_TX_DATA); ++ writel(pkt[i], espi->regs + ESPI_CH0_PC_TX_DATA); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ reg = FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_PERIF_PC_TX_CTRL_LEN, len) -+ | ESPI_PERIF_PC_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_PERIF_PC_TX_CTRL); ++ reg = FIELD_PREP(ESPI_CH0_PC_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_CH0_PC_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_CH0_PC_TX_CTRL_LEN, len) ++ | ESPI_CH0_PC_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_CH0_PC_TX_CTRL); + + rc = 0; + @@ -77905,7 +80070,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_perif_np_put_tx(struct file *fp, ++static long ast2700_espi_perif_np_put_tx(struct file *fp, + struct aspeed_espi_perif *perif, + struct aspeed_espi_ioc *ioc) +{ @@ -77920,8 +80085,8 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + if (!mutex_trylock(&perif->np_tx_mtx)) + return -EAGAIN; + -+ reg = readl(espi->regs + ESPI_PERIF_NP_TX_CTRL); -+ if (reg & ESPI_PERIF_NP_TX_CTRL_TRIG_PEND) { ++ reg = readl(espi->regs + ESPI_CH0_NP_TX_CTRL); ++ if (reg & ESPI_CH0_NP_TX_CTRL_TRIG_PEND) { + rc = -EBUSY; + goto unlock_n_out; + } @@ -77948,18 +80113,18 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_PERIF_NP_TX_DATA); ++ writel(pkt[i], espi->regs + ESPI_CH0_NP_TX_DATA); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ reg = FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_PERIF_NP_TX_CTRL_LEN, len) -+ | ESPI_PERIF_NP_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_PERIF_NP_TX_CTRL); ++ reg = FIELD_PREP(ESPI_CH0_NP_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_CH0_NP_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_CH0_NP_TX_CTRL_LEN, len) ++ | ESPI_CH0_NP_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_CH0_NP_TX_CTRL); + + rc = 0; + @@ -77972,7 +80137,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2700_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_perif *perif; + struct aspeed_espi_ioc ioc; @@ -77987,11 +80152,11 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + switch (cmd) { + case ASPEED_ESPI_PERIF_PC_GET_RX: -+ return ast2600_espi_perif_pc_get_rx(fp, perif, &ioc); ++ return ast2700_espi_perif_pc_get_rx(fp, perif, &ioc); + case ASPEED_ESPI_PERIF_PC_PUT_TX: -+ return ast2600_espi_perif_pc_put_tx(fp, perif, &ioc); ++ return ast2700_espi_perif_pc_put_tx(fp, perif, &ioc); + case ASPEED_ESPI_PERIF_NP_PUT_TX: -+ return ast2600_espi_perif_np_put_tx(fp, perif, &ioc); ++ return ast2700_espi_perif_np_put_tx(fp, perif, &ioc); + default: + break; + }; @@ -77999,7 +80164,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return -EINVAL; +} + -+static int ast2600_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) ++static int ast2700_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct aspeed_espi_perif *perif; + unsigned long vm_size; @@ -78025,61 +80190,13 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return 0; +} + -+static const struct file_operations ast2600_espi_mmbi_b2h_fops = { -+ .owner = THIS_MODULE, -+ .mmap = ast2600_espi_mmbi_b2h_mmap, -+}; -+ -+static const struct file_operations ast2600_espi_mmbi_h2b_fops = { -+ .owner = THIS_MODULE, -+ .mmap = ast2600_espi_mmbi_h2b_mmap, -+ .poll = ast2600_espi_mmbi_h2b_poll, -+}; -+ -+static const struct file_operations ast2600_espi_perif_fops = { ++static const struct file_operations ast2700_espi_perif_fops = { + .owner = THIS_MODULE, -+ .mmap = ast2600_espi_perif_mmap, -+ .unlocked_ioctl = ast2600_espi_perif_ioctl, ++ .mmap = ast2700_espi_perif_mmap, ++ .unlocked_ioctl = ast2700_espi_perif_ioctl, +}; + -+static irqreturn_t ast2600_espi_perif_mmbi_isr(int irq, void *arg) -+{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct aspeed_espi *espi; -+ u32 sts, tmp; -+ u32 *p; -+ int i; -+ -+ espi = (struct aspeed_espi *)arg; -+ -+ perif = &espi->perif; -+ -+ sts = readl(espi->regs + ESPI_MMBI_INT_STS); -+ if (!sts) -+ return IRQ_NONE; -+ -+ for (i = 0, tmp = sts; i < PERIF_MMBI_INST_NUM; ++i, tmp >>= 2) { -+ if (!(tmp & 0x3)) -+ continue; -+ -+ mmbi = &perif->mmbi.inst[i]; -+ -+ p = (u32 *)mmbi->h2b_virt; -+ p[0] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i)); -+ p[1] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i) + 4); -+ -+ mmbi->host_rwp_update = true; -+ -+ wake_up_interruptible(&mmbi->wq); -+ } -+ -+ writel(sts, espi->regs + ESPI_MMBI_INT_STS); -+ -+ return IRQ_HANDLED; -+} -+ -+static void ast2600_espi_perif_isr(struct aspeed_espi *espi) ++static void ast2700_espi_perif_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_perif *perif; + unsigned long flags; @@ -78087,10 +80204,10 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + perif = &espi->perif; + -+ sts = readl(espi->regs + ESPI_INT_STS); ++ sts = readl(espi->regs + ESPI_CH0_INT_STS); + -+ if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { -+ writel(ESPI_INT_STS_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ if (sts & ESPI_CH0_INT_STS_PC_RX_CMPLT) { ++ writel(ESPI_CH0_INT_STS_PC_RX_CMPLT, espi->regs + ESPI_CH0_INT_STS); + + spin_lock_irqsave(&perif->lock, flags); + perif->rx_ready = true; @@ -78100,116 +80217,113 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + } +} + -+static void ast2600_espi_perif_sw_reset(struct aspeed_espi *espi) ++static void ast2700_espi_perif_sw_reset(struct aspeed_espi *espi) +{ + struct device *dev; + u32 reg; + + dev = espi->dev; + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_SW_RST -+ | ESPI_CTRL_PERIF_NP_RX_SW_RST -+ | ESPI_CTRL_PERIF_PC_TX_SW_RST -+ | ESPI_CTRL_PERIF_PC_RX_SW_RST -+ | ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH0_CTRL); ++ reg &= ~(ESPI_CH0_CTRL_NP_TX_RST ++ | ESPI_CH0_CTRL_NP_RX_RST ++ | ESPI_CH0_CTRL_PC_TX_RST ++ | ESPI_CH0_CTRL_PC_RX_RST ++ | ESPI_CH0_CTRL_NP_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_RX_DMA_EN ++ | ESPI_CH0_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH0_CTRL); + + udelay(1); + -+ reg |= (ESPI_CTRL_PERIF_NP_TX_SW_RST -+ | ESPI_CTRL_PERIF_NP_RX_SW_RST -+ | ESPI_CTRL_PERIF_PC_TX_SW_RST -+ | ESPI_CTRL_PERIF_PC_RX_SW_RST); -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg |= (ESPI_CH0_CTRL_NP_TX_RST ++ | ESPI_CH0_CTRL_NP_RX_RST ++ | ESPI_CH0_CTRL_PC_TX_RST ++ | ESPI_CH0_CTRL_PC_RX_RST); ++ writel(reg, espi->regs + ESPI_CH0_CTRL); +} + -+static void ast2600_espi_perif_reset(struct aspeed_espi *espi) ++static void ast2700_espi_perif_reset(struct aspeed_espi *espi) +{ + struct aspeed_espi_perif *perif; + struct device *dev; -+ u32 reg, mask; ++ u64 mask; ++ u32 reg; + + dev = espi->dev; + -+ perif = &espi->perif; -+ -+ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_PERIF, espi->regs + ESPI_INT_STS); -+ -+ writel(0x0, espi->regs + ESPI_MMBI_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_STS); -+ -+ reg = readl(espi->regs + ESPI_CTRL2); -+ reg &= ~(ESPI_CTRL2_MCYC_RD_DIS_WDT | ESPI_CTRL2_MCYC_WR_DIS_WDT); -+ writel(reg, espi->regs + ESPI_CTRL2); -+ -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); -+ -+ if (perif->mmbi.enable) { -+ reg = readl(espi->regs + ESPI_MMBI_CTRL); -+ reg &= ~(ESPI_MMBI_CTRL_EN); -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ perif = &espi->perif; + -+ mask = ~(perif->mmbi.size - 1); -+ writel(mask, espi->regs + ESPI_PERIF_MMBI_MASK); -+ writel(perif->mmbi.saddr, espi->regs + ESPI_PERIF_MMBI_SADDR); -+ writel(perif->mmbi.taddr, espi->regs + ESPI_PERIF_MMBI_TADDR); ++ writel(0x0, espi->regs + ESPI_CH0_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_CH0_INT_STS); + -+ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_EN); ++ reg = readl(espi->regs + ESPI_CH0_CTRL); ++ reg &= ~(ESPI_CH0_CTRL_MCYC_RD_DIS_WDT | ESPI_CH0_CTRL_MCYC_WR_DIS_WDT); ++ writel(reg, espi->regs + ESPI_CH0_CTRL); + -+ reg = FIELD_PREP(ESPI_MMBI_CTRL_INST_SZ, perif->mmbi.inst_size) -+ | FIELD_PREP(ESPI_MMBI_CTRL_TOTAL_SZ, perif->mmbi.inst_size) -+ | ESPI_MMBI_CTRL_EN; -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ reg = readl(espi->regs + ESPI_CH0_MCYC1_MASKL); ++ reg &= ~ESPI_CH0_MCYC1_MASKL_EN; ++ writel(reg, espi->regs + ESPI_CH0_MCYC1_MASKL); + -+ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MMBI_RD_DIS | ESPI_CTRL2_MMBI_WR_DIS); -+ writel(reg, espi->regs + ESPI_CTRL2); -+ } ++ reg = readl(espi->regs + ESPI_CH0_CTRL); ++ reg |= (ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); ++ reg &= ~(ESPI_CH0_CTRL_NP_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_RX_DMA_EN ++ | ESPI_CH0_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH0_CTRL); + + if (perif->mcyc.enable) { + mask = ~(perif->mcyc.size - 1); -+ writel(mask, espi->regs + ESPI_PERIF_MCYC_MASK); -+ writel(perif->mcyc.saddr, espi->regs + ESPI_PERIF_MCYC_SADDR); -+ writel(perif->mcyc.taddr, espi->regs + ESPI_PERIF_MCYC_TADDR); ++ writel(upper_32_bits(mask), espi->regs + ESPI_CH0_MCYC1_MASKH); ++ writel(mask & 0xffffffff, espi->regs + ESPI_CH0_MCYC1_MASKL); ++ writel(upper_32_bits(perif->mcyc.saddr), espi->regs + ESPI_CH0_MCYC1_SADDRH); ++ writel((perif->mcyc.saddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC1_SADDRL); ++ writel(upper_32_bits(perif->mcyc.taddr), espi->regs + ESPI_CH0_MCYC1_TADDRH); ++ writel((perif->mcyc.taddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC1_TADDRL); + -+ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); -+ writel(reg, espi->regs + ESPI_CTRL2); ++ reg = readl(espi->regs + ESPI_CH0_MCYC1_MASKL) | ESPI_CH0_MCYC1_MASKL_EN; ++ writel(reg, espi->regs + ESPI_CH0_MCYC1_MASKL); ++ ++ reg = readl(espi->regs + ESPI_CH0_CTRL); ++ reg &= ~(ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); ++ writel(reg, espi->regs + ESPI_CH0_CTRL); + } + + if (perif->dma.enable) { -+ writel(perif->dma.np_tx_addr, espi->regs + ESPI_PERIF_NP_TX_DMA); -+ writel(perif->dma.pc_tx_addr, espi->regs + ESPI_PERIF_PC_TX_DMA); -+ writel(perif->dma.pc_rx_addr, espi->regs + ESPI_PERIF_PC_RX_DMA); ++ writel(upper_32_bits(perif->dma.np_tx_addr), espi->regs + ESPI_CH0_NP_TX_DMAH); ++ writel((perif->dma.np_tx_addr & 0xffffffff), espi->regs + ESPI_CH0_NP_TX_DMAL); ++ writel(upper_32_bits(perif->dma.pc_tx_addr), espi->regs + ESPI_CH0_PC_TX_DMAH); ++ writel((perif->dma.pc_tx_addr & 0xffffffff), espi->regs + ESPI_CH0_PC_TX_DMAL); ++ writel(upper_32_bits(perif->dma.pc_rx_addr), espi->regs + ESPI_CH0_PC_RX_DMAH); ++ writel((perif->dma.pc_rx_addr & 0xffffffff), espi->regs + ESPI_CH0_PC_RX_DMAL); + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH0_CTRL) ++ | ESPI_CH0_CTRL_NP_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ } ++ if (perif->rtc_enable) { ++ reg = readl(espi->regs + ESPI_CAP_GEN) ++ | ESPI_CAP_GEN_RTC_SUP; ++ writel(reg, espi->regs + ESPI_CAP_GEN); + } + -+ writel(ESPI_INT_EN_PERIF_PC_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ writel(ESPI_CH0_INT_EN_PC_RX_CMPLT, espi->regs + ESPI_CH0_INT_EN); + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_PERIF_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH0_CTRL) | ESPI_CH0_CTRL_SW_RDY; ++ writel(reg, espi->regs + ESPI_CH0_CTRL); +} + -+int ast2600_espi_perif_probe(struct aspeed_espi *espi) ++int ast2700_espi_perif_probe(struct aspeed_espi *espi) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; + struct aspeed_espi_perif *perif; -+ struct platform_device *pdev; ++ struct device_node *np; + struct device *dev; -+ int i, rc; ++ u64 temp; ++ int rc; + + dev = espi->dev; + @@ -78223,90 +80337,31 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + mutex_init(&perif->pc_tx_mtx); + mutex_init(&perif->pc_rx_mtx); + -+ perif->mmbi.enable = of_property_read_bool(dev->of_node, "perif-mmbi-enable"); -+ if (perif->mmbi.enable) { -+ pdev = espi->pdev; -+ -+ perif->mmbi.irq = platform_get_irq(pdev, 1); -+ if (perif->mmbi.irq < 0) { -+ dev_err(dev, "cannot get MMBI IRQ number\n"); -+ return -ENODEV; -+ } -+ -+ rc = of_property_read_u32(dev->of_node, "perif-mmbi-src-addr", (u32 *)&perif->mmbi.saddr); -+ if (rc || !IS_ALIGNED(perif->mmbi.saddr, PERIF_MMBI_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned MMBI host address\n"); -+ return -ENODEV; -+ } -+ -+ rc = of_property_read_u32(dev->of_node, "perif-mmbi-instance-size", (u32 *)&perif->mmbi.inst_size); -+ if (rc || perif->mmbi.inst_size >= MMBI_INST_SIZE_TYPES) { -+ dev_err(dev, "cannot get valid MMBI instance size\n"); -+ return -EINVAL; -+ } -+ -+ perif->mmbi.size = (SZ_8K << perif->mmbi.inst_size) * PERIF_MMBI_INST_NUM; -+ perif->mmbi.virt = dmam_alloc_coherent(dev, perif->mmbi.size, -+ &perif->mmbi.taddr, GFP_KERNEL); -+ if (!perif->mmbi.virt) { -+ dev_err(dev, "cannot allocate MMBI\n"); -+ return -ENOMEM; -+ } -+ -+ perif->mmbi.mmbi_isr = ast2600_espi_perif_mmbi_isr; -+ -+ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { -+ mmbi = &perif->mmbi.inst[i]; -+ -+ init_waitqueue_head(&mmbi->wq); -+ -+ mmbi->perif = perif; -+ mmbi->host_rwp_update = false; -+ -+ mmbi->b2h_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * i); -+ mmbi->b2h_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * i); -+ mmbi->b2h_mdev.parent = dev; -+ mmbi->b2h_mdev.minor = MISC_DYNAMIC_MINOR; -+ mmbi->b2h_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-b2h%d", DEVICE_NAME, i); -+ mmbi->b2h_mdev.fops = &ast2600_espi_mmbi_b2h_fops; -+ rc = misc_register(&mmbi->b2h_mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", mmbi->b2h_mdev.name); -+ return rc; -+ } -+ -+ mmbi->h2b_virt = perif->mmbi.virt + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); -+ mmbi->h2b_addr = perif->mmbi.taddr + ((SZ_4K << perif->mmbi.inst_size) * (i + PERIF_MMBI_INST_NUM)); -+ mmbi->h2b_mdev.parent = dev; -+ mmbi->h2b_mdev.minor = MISC_DYNAMIC_MINOR; -+ mmbi->h2b_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi-h2b%d", DEVICE_NAME, i); -+ mmbi->h2b_mdev.fops = &ast2600_espi_mmbi_h2b_fops; -+ rc = misc_register(&mmbi->h2b_mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", mmbi->h2b_mdev.name); -+ return rc; -+ } -+ } -+ } -+ + perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); + if (perif->mcyc.enable) { -+ if (perif->mmbi.enable) { -+ dev_err(dev, "cannot enable memory cycle, occupied by MMBI\n"); -+ return -EPERM; -+ } -+ -+ rc = of_property_read_u32(dev->of_node, "perif-mcyc-src-addr", (u32 *)&perif->mcyc.saddr); -+ if (rc || !IS_ALIGNED(perif->mcyc.saddr, PERIF_MCYC_ALIGN)) { ++ rc = of_property_read_u64(dev->of_node, "perif-mcyc-src-addr", &temp); ++ if (rc || !IS_ALIGNED(temp, PERIF_MCYC_ALIGN)) { + dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); + return -ENODEV; + } ++ perif->mcyc.saddr = temp; + -+ rc = of_property_read_u32(dev->of_node, "perif-mcyc-size", (u32 *)&perif->mcyc.size); -+ if (rc || !IS_ALIGNED(perif->mcyc.size, PERIF_MCYC_ALIGN)) { ++ rc = of_property_read_u64(dev->of_node, "perif-mcyc-size", &temp); ++ if (rc || !IS_ALIGNED(temp, PERIF_MCYC_ALIGN)) { + dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); + return -EINVAL; + } ++ perif->mcyc.size = temp; ++ ++ np = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (np) { ++ of_reserved_mem_device_init(dev); ++ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ++ if (rc) { ++ dev_err(dev, "Failed to mask DMA.\n"); ++ return -ENODEV; ++ } ++ } + + perif->mcyc.virt = dmam_alloc_coherent(dev, perif->mcyc.size, + &perif->mcyc.taddr, GFP_KERNEL); @@ -78339,61 +80394,51 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return -ENOMEM; + } + } ++ perif->rtc_enable = of_property_read_bool(dev->of_node, "perif-rtc-enable"); + + perif->mdev.parent = dev; + perif->mdev.minor = MISC_DYNAMIC_MINOR; -+ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral", DEVICE_NAME); -+ perif->mdev.fops = &ast2600_espi_perif_fops; ++ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral%d", DEVICE_NAME, espi->dev_id); ++ perif->mdev.fops = &ast2700_espi_perif_fops; + rc = misc_register(&perif->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", perif->mdev.name); + return rc; + } + -+ ast2600_espi_perif_reset(espi); ++ ast2700_espi_perif_reset(espi); + ++ of_platform_populate(dev->of_node, NULL, NULL, dev); + return 0; +} + -+int ast2600_espi_perif_remove(struct aspeed_espi *espi) ++int ast2700_espi_perif_remove(struct aspeed_espi *espi) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; + struct aspeed_espi_perif *perif; + struct device *dev; + u32 reg; -+ int i; + + dev = espi->dev; + + perif = &espi->perif; + -+ writel(ESPI_INT_EN_PERIF, espi->regs + ESPI_INT_EN_CLR); -+ -+ reg = readl(espi->regs + ESPI_CTRL2); -+ reg |= (ESPI_CTRL2_MCYC_RD_DIS | ESPI_CTRL2_MCYC_WR_DIS); -+ writel(reg, espi->regs + ESPI_CTRL2); -+ -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_PERIF_NP_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_TX_DMA_EN -+ | ESPI_CTRL_PERIF_PC_RX_DMA_EN -+ | ESPI_CTRL_PERIF_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ writel(0x0, espi->regs + ESPI_CH0_INT_EN); + -+ if (perif->mmbi.enable) { -+ reg = readl(espi->regs + ESPI_MMBI_CTRL); -+ reg &= ~ESPI_MMBI_CTRL_EN; -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ reg = readl(espi->regs + ESPI_CH0_MCYC0_MASKL); ++ reg &= ~ESPI_CH0_MCYC0_MASKL_EN; ++ writel(reg, espi->regs + ESPI_CH0_MCYC0_MASKL); + -+ for (i = 0; i < PERIF_MMBI_INST_NUM; ++i) { -+ mmbi = &perif->mmbi.inst[i]; -+ misc_deregister(&mmbi->b2h_mdev); -+ misc_deregister(&mmbi->h2b_mdev); -+ } ++ reg = readl(espi->regs + ESPI_CH0_MCYC1_MASKL); ++ reg &= ~ESPI_CH0_MCYC1_MASKL_EN; ++ writel(reg, espi->regs + ESPI_CH0_MCYC1_MASKL); + -+ dmam_free_coherent(dev, perif->mmbi.size, perif->mmbi.virt, -+ perif->mmbi.taddr); -+ } ++ reg = readl(espi->regs + ESPI_CH0_CTRL); ++ reg |= (ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); ++ reg &= ~(ESPI_CH0_CTRL_NP_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_TX_DMA_EN ++ | ESPI_CH0_CTRL_PC_RX_DMA_EN ++ | ESPI_CH0_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH0_CTRL); + + if (perif->mcyc.enable) + dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, @@ -78418,16 +80463,21 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as +} + +/* virtual wire channel (CH1) */ -+static long ast2600_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2700_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_vw *vw; + struct aspeed_espi *espi; -+ u32 gpio, hw_mode; ++ u32 gpio0, gpio1; ++ u8 pch_generic; ++ u32 reg; ++ u32 hw_mode; + + vw = container_of(fp->private_data, struct aspeed_espi_vw, mdev); + espi = container_of(vw, struct aspeed_espi, vw); -+ gpio = vw->gpio.val0; ++ gpio0 = vw->gpio.val0; ++ gpio1 = vw->gpio.val1; + hw_mode = vw->gpio.hw_mode; ++ pch_generic = vw->platform.pch_generic; + + if (hw_mode) { + dev_err(espi->dev, "HW mode: vGPIO reflect on physical GPIO. Get state from GPIO driver.\n"); @@ -78436,24 +80486,63 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + switch (cmd) { + case ASPEED_ESPI_VW_GET_GPIO_VAL: -+ if (put_user(gpio, (u32 __user *)arg)) { -+ dev_err(espi->dev, "failed to get vGPIO value\n"); ++ if (put_user(gpio0, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to get vGPIO value0\n"); + return -EFAULT; + } + -+ dev_info(espi->dev, "Get vGPIO value: 0x%x\n", gpio); ++ dev_info(espi->dev, "Get vGPIO value0: 0x%x\n", gpio0); + break; + + case ASPEED_ESPI_VW_PUT_GPIO_VAL: -+ if (get_user(gpio, (u32 __user *)arg)) { -+ dev_err(espi->dev, "failed to put vGPIO value\n"); ++ if (get_user(gpio0, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to put vGPIO value0\n"); + return -EFAULT; + } + -+ dev_info(espi->dev, "Put vGPIO value: 0x%x\n", gpio); -+ writel(gpio, espi->regs + ESPI_VW_GPIO_VAL); ++ dev_info(espi->dev, "Put vGPIO value0: 0x%x\n", gpio0); ++ writel(gpio0, espi->regs + ESPI_CH1_GPIO_VAL0); ++ break; ++#ifdef CONFIG_ARM64 ++ case ASPEED_ESPI_VW_GET_GPIO_VAL1: ++ if (put_user(gpio1, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to get vGPIO value1\n"); ++ return -EFAULT; ++ } ++ ++ dev_info(espi->dev, "Get vGPIO value1: 0x%x\n", gpio1); ++ break; ++ ++ case ASPEED_ESPI_VW_PUT_GPIO_VAL1: ++ if (get_user(gpio1, (u32 __user *)arg)) { ++ dev_err(espi->dev, "failed to put vGPIO value1\n"); ++ return -EFAULT; ++ } ++ ++ dev_info(espi->dev, "Put vGPIO value1: 0x%x\n", gpio1); ++ writel(gpio1, espi->regs + ESPI_CH1_GPIO_VAL1); ++ break; ++#endif ++ case ASPEED_ESPI_VW_GET_PCH_GENERIC: ++ if (put_user(pch_generic, (u8 __user *)arg)) { ++ dev_err(espi->dev, "failed to get PCH generic event status\n"); ++ return -EFAULT; ++ } ++ ++ dev_info(espi->dev, "Get PCH generic event status: 0x%x\n", pch_generic); + break; ++ case ASPEED_ESPI_VW_PUT_PCH_GENERIC: ++ if (get_user(pch_generic, (u8 __user *)arg)) { ++ dev_err(espi->dev, "failed to put PCH generic event status\n"); ++ return -EFAULT; ++ } + ++ dev_info(espi->dev, "Put PCH generic event status: 0x%x\n", pch_generic); ++ reg = readl(espi->regs + ESPI_CH1_EVT1); ++ reg &= ~ESPI_CH1_EVT1_BMC_GENE; ++ reg |= FIELD_PREP(ESPI_CH1_EVT1_BMC_GENE, pch_generic); ++ writel(reg, espi->regs + ESPI_CH1_EVT1); ++ break; + default: + return -EINVAL; + }; @@ -78461,97 +80550,245 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return 0; +} + -+static const struct file_operations ast2600_espi_vw_fops = { ++static const struct file_operations ast2700_espi_vw_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2600_espi_vw_ioctl, ++ .unlocked_ioctl = ast2700_espi_vw_ioctl, +}; + -+static void ast2600_espi_vw_isr(struct aspeed_espi *espi) ++static inline struct aspeed_espi_vw *to_ast2700_espi_pltrst(struct file *filp) ++{ ++ return container_of(filp->private_data, struct aspeed_espi_vw, ++ pltrst_mdev); ++} ++ ++static int ast2700_espi_vw_pltrst_open(struct inode *inode, struct file *filp) ++{ ++ struct aspeed_espi_vw *priv = to_ast2700_espi_pltrst(filp); ++ ++ if ((filp->f_flags & O_ACCMODE) != O_RDONLY) ++ return -EACCES; ++ priv->pltrst.pltrst_avail = true ; /*Setting true returns first data after file open*/ ++ ++ return 0; ++} ++ ++static ssize_t ast2700_espi_vw_pltrst_read(struct file *filp, char __user *buf, ++ size_t count, loff_t *offset) ++{ ++ struct aspeed_espi_vw *vw = to_ast2700_espi_pltrst(filp); ++ DECLARE_WAITQUEUE(wait, current); ++ char data, old_sample; ++ int ret = 0; ++ ++ spin_lock_irq(&vw->pltrst.pltrst_lock); ++ ++ if (filp->f_flags & O_NONBLOCK) { ++ if (!vw->pltrst.pltrst_avail) { ++ ret = -EAGAIN; ++ goto out_unlock; ++ } ++ data = vw->pltrst.pltrst_status; ++ vw->pltrst.pltrst_avail = false; ++ } else { ++ add_wait_queue(&vw->pltrst.pltrst_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ old_sample = vw->pltrst.pltrst_status; ++ ++ do { ++ if (old_sample != vw->pltrst.pltrst_status) { ++ data = vw->pltrst.pltrst_status; ++ vw->pltrst.pltrst_avail = false; ++ break; ++ } ++ ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ } else { ++ spin_unlock_irq(&vw->pltrst.pltrst_lock); ++ schedule(); ++ spin_lock_irq(&vw->pltrst.pltrst_lock); ++ } ++ } while (!ret); ++ ++ remove_wait_queue(&vw->pltrst.pltrst_wq, &wait); ++ set_current_state(TASK_RUNNING); ++ } ++out_unlock: ++ spin_unlock_irq(&vw->pltrst.pltrst_lock); ++ if (ret) ++ return ret; ++ ++ ret = put_user(data, buf); ++ if (!ret) ++ ret = sizeof(data); ++ ++ return ret; ++} ++ ++static unsigned int ast2700_espi_vw_pltrst_poll(struct file *file, ++ poll_table *wait) ++{ ++ struct aspeed_espi_vw *vw = to_ast2700_espi_pltrst(file); ++ unsigned int mask = 0; ++ ++ poll_wait(file, &vw->pltrst.pltrst_wq, wait); ++ if (vw->pltrst.pltrst_avail) ++ mask |= POLLIN; ++ return mask; ++} ++ ++static const struct file_operations ast2700_espi_vw_pltrst_fops = { ++ .owner = THIS_MODULE, ++ .open = ast2700_espi_vw_pltrst_open, ++ .read = ast2700_espi_vw_pltrst_read, ++ .poll = ast2700_espi_vw_pltrst_poll, ++}; ++ ++static void ast2700_espi_vw_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_vw *vw; ++ u32 sts_evt; + u32 sts; ++ u32 evt; ++ unsigned long flags; + + vw = &espi->vw; + -+ sts = readl(espi->regs + ESPI_INT_STS); ++ sts = readl(espi->regs + ESPI_CH1_INT_STS); + -+ if (sts & ESPI_INT_STS_VW_GPIO) { -+ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); -+ writel(ESPI_INT_STS_VW_GPIO, espi->regs + ESPI_INT_STS); -+ } else if (sts & ESPI_INT_STS_VW_SYSEVT) { -+ /* Handle system event */ -+ writel(ESPI_INT_STS_VW_SYSEVT, espi->regs + ESPI_INT_STS); -+ } else if (sts & (ESPI_INT_STS_VW_SYSEVT1)) { -+ /* Handle system event1 */ -+ writel(ESPI_INT_STS_VW_SYSEVT1, espi->regs + ESPI_INT_STS); ++ if (sts & ESPI_CH1_INT_STS_GPIO) { ++ vw->gpio.val0 = readl(espi->regs + ESPI_CH1_GPIO_VAL0); ++ vw->gpio.val1 = readl(espi->regs + ESPI_CH1_GPIO_VAL1); ++ /* Workaround for AST2700 VW INT_STS */ ++ writel(ESPI_CH1_INT_STS_GPIO_CLR, espi->regs + ESPI_CH1_INT_STS); ++ } ++ ++ if (sts & ESPI_CH1_INT_STS_EVT1) { ++ sts_evt = readl(espi->regs + ESPI_CH1_EVT1_INT_STS); ++ if (sts_evt & ESPI_CH1_EVT1_INT_STS_PCH_GENE) { ++ vw->platform.pch_generic = FIELD_GET(ESPI_CH1_EVT1_PCH_GENE, ++ readl(espi->regs + ESPI_CH1_EVT1)); ++ ++ writel(ESPI_CH1_EVT1_INT_STS_PCH_GENE, ++ espi->regs + ESPI_CH1_EVT1_INT_STS); ++ } ++ ++ writel(ESPI_CH1_INT_STS_EVT1_CLR, espi->regs + ESPI_CH1_INT_STS); ++ } ++ ++ if (sts & ESPI_CH1_INT_STS_EVT0) { ++ sts_evt = readl(espi->regs + ESPI_CH1_EVT0_INT_STS); ++ evt = readl(espi->regs + ESPI_CH1_EVT0); ++ if (sts_evt & ESPI_CH1_EVT0_INT_STS_PLTRSTN || vw->pltrst.pltrst_status == 'U') { ++ spin_lock_irqsave(&vw->pltrst.pltrst_lock, flags); ++ vw->pltrst.pltrst_status = (evt & ESPI_CH1_EVT0_PLTRSTN) ? '1' : '0'; ++ vw->pltrst.pltrst_avail = true; ++ spin_unlock_irqrestore(&vw->pltrst.pltrst_lock, flags); ++ ++ writel(ESPI_CH1_EVT0_INT_STS_PLTRSTN, ++ espi->regs + ESPI_CH1_EVT0_INT_STS); ++ ++ wake_up_interruptible(&vw->pltrst.pltrst_wq); ++ } ++ writel(ESPI_CH1_INT_STS_EVT0, espi->regs + ESPI_CH1_INT_STS); + } +} + -+static void ast2600_espi_vw_reset(struct aspeed_espi *espi) ++static void ast2700_espi_vw_reset(struct aspeed_espi *espi) +{ + u32 reg; + struct aspeed_espi_vw *vw = &espi->vw; + -+ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_VW, espi->regs + ESPI_INT_STS); -+ -+ writel(vw->gpio.dir0, espi->regs + ESPI_VW_GPIO_DIR); -+ -+ vw->gpio.val0 = readl(espi->regs + ESPI_VW_GPIO_VAL); ++ writel(0x0, espi->regs + ESPI_CH1_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_CH1_INT_STS); + -+ reg = readl(espi->regs + ESPI_CTRL2) & ~(ESPI_CTRL2_VW_TX_SORT); -+ writel(reg, espi->regs + ESPI_CTRL2); ++ writel(vw->gpio.grp, espi->regs + ESPI_CH1_GPIO_GRP); ++ writel(vw->gpio.dir0, espi->regs + ESPI_CH1_GPIO_DIR0); ++ writel(vw->gpio.dir1, espi->regs + ESPI_CH1_GPIO_DIR1); + -+ writel(ESPI_INT_EN_VW_GPIO, espi->regs + ESPI_INT_EN); ++ vw->gpio.val0 = readl(espi->regs + ESPI_CH1_GPIO_VAL0); ++ vw->gpio.val1 = readl(espi->regs + ESPI_CH1_GPIO_VAL1); ++ vw->platform.pch_generic = FIELD_GET(ESPI_CH1_EVT1_PCH_GENE, ++ readl(espi->regs + ESPI_CH1_EVT1)); + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ((vw->gpio.hw_mode) ? 0 : ESPI_CTRL_VW_GPIO_SW) -+ | ESPI_CTRL_VW_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ writel(ESPI_CH1_EVT1_INT_T2_PCH_GENE, ++ espi->regs + ESPI_CH1_EVT1_INT_T2); ++ writel(ESPI_CH1_EVT1_INT_EN_PCH_GENE, espi->regs + ESPI_CH1_EVT1_INT_EN); + -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T0); -+ writel(0x0, espi->regs + ESPI_VW_SYSEVT_INT_T1); ++ if (vw->pltrst.enabled) { ++ spin_lock(&vw->pltrst.pltrst_lock); ++ vw->pltrst.pltrst_status = 'U'; /* Unknown */ ++ vw->pltrst.pltrst_avail = true; ++ spin_unlock(&vw->pltrst.pltrst_lock); + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg |= ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++ writel(ESPI_CH1_EVT0_INT_T2_PLTRSTN, ++ espi->regs + ESPI_CH1_EVT0_INT_T2); ++ writel(ESPI_CH1_EVT0_INT_EN_PLTRSTN, espi->regs + ESPI_CH1_EVT0_INT_EN); ++ writel(ESPI_CH1_INT_EN_GPIO | ESPI_CH1_INT_EN_SYS_EVT1 | ESPI_CH1_INT_EN_SYS_EVT0, ++ espi->regs + ESPI_CH1_INT_EN); ++ } else { ++ writel(ESPI_CH1_INT_EN_GPIO | ESPI_CH1_INT_EN_SYS_EVT1, espi->regs + ESPI_CH1_INT_EN); ++ } + -+ writel(0xffffffff, espi->regs + ESPI_VW_SYSEVT_INT_EN); -+ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_EN); -+ writel(0x1, espi->regs + ESPI_VW_SYSEVT1_INT_T0); ++ reg = readl(espi->regs + ESPI_CH1_CTRL) ++ | ((vw->gpio.hw_mode) ? ESPI_CH1_CTRL_GPIO_HW : 0) ++ | ESPI_CH1_CTRL_SW_RDY; ++ writel(reg, espi->regs + ESPI_CH1_CTRL); +} + -+int ast2600_espi_vw_probe(struct aspeed_espi *espi) ++int ast2700_espi_vw_probe(struct aspeed_espi *espi) +{ + int rc; + struct device *dev = espi->dev; + struct aspeed_espi_vw *vw = &espi->vw; + + vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); -+ of_property_read_u32(dev->of_node, "vw-gpio-direction", &vw->gpio.dir0); ++ of_property_read_u32(dev->of_node, "vw-gpio-group", &vw->gpio.grp); ++ of_property_read_u32_index(dev->of_node, "vw-gpio-direction", 0, &vw->gpio.dir0); ++ of_property_read_u32_index(dev->of_node, "vw-gpio-direction", 1, &vw->gpio.dir1); ++ ++ vw->pltrst.enabled = of_property_read_bool(dev->of_node, "vw-pltrst-monitor"); ++ if (vw->pltrst.enabled) { ++ spin_lock_init(&vw->pltrst.pltrst_lock); ++ init_waitqueue_head(&vw->pltrst.pltrst_wq); ++ vw->pltrst.pltrst_status = 'U'; /* Unknown */ ++ vw->pltrst.pltrst_avail = false; ++ vw->pltrst_mdev.parent = dev; ++ vw->pltrst_mdev.minor = MISC_DYNAMIC_MINOR; ++ vw->pltrst_mdev.name = ++ devm_kasprintf(dev, GFP_KERNEL, "%s-pltrstn%d", ++ DEVICE_NAME, espi->dev_id); ++ vw->pltrst_mdev.fops = &ast2700_espi_vw_pltrst_fops; ++ rc = misc_register(&vw->pltrst_mdev); ++ if (rc) { ++ dev_err(dev, "cannot register device %s\n", vw->pltrst_mdev.name); ++ return rc; ++ } ++ } + + vw->mdev.parent = dev; + vw->mdev.minor = MISC_DYNAMIC_MINOR; -+ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw", DEVICE_NAME); -+ vw->mdev.fops = &ast2600_espi_vw_fops; ++ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw%d", DEVICE_NAME, espi->dev_id); ++ vw->mdev.fops = &ast2700_espi_vw_fops; + rc = misc_register(&vw->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", vw->mdev.name); + return rc; + } + -+ ast2600_espi_vw_reset(espi); ++ ast2700_espi_vw_reset(espi); + + return 0; +} + -+int ast2600_espi_vw_remove(struct aspeed_espi *espi) ++int ast2700_espi_vw_remove(struct aspeed_espi *espi) +{ + struct aspeed_espi_vw *vw; + + vw = &espi->vw; + -+ writel(ESPI_INT_EN_VW, espi->regs + ESPI_INT_EN_CLR); ++ writel(0x0, espi->regs + ESPI_CH1_INT_EN); + + misc_deregister(&vw->mdev); + @@ -78559,12 +80796,12 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as +} + +/* out-of-band channel (CH2) */ -+static long ast2600_espi_oob_dma_get_rx(struct file *fp, ++static long ast2700_espi_oob_dma_get_rx(struct file *fp, + struct aspeed_espi_oob *oob, + struct aspeed_espi_ioc *ioc) +{ -+ struct ast2600_espi_oob_dma_rx_desc *d; -+ struct ast2600_espi_oob_dma_rx_desc *rx_descs; ++ struct ast2700_espi_oob_dma_rx_desc *d; ++ struct ast2700_espi_oob_dma_rx_desc *rx_descs; + struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; + u32 wptr, pkt_len; @@ -78574,9 +80811,9 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + espi = container_of(oob, struct aspeed_espi, oob); + -+ wptr = FIELD_PREP(ESPI_OOB_RX_DESC_WPTR_WP, readl(espi->regs + ESPI_OOB_RX_DESC_WPTR)); ++ wptr = FIELD_PREP(ESPI_CH2_RX_DESC_WPTR_WP, readl(espi->regs + ESPI_CH2_RX_DESC_WPTR)); + -+ rx_descs = (struct ast2600_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ rx_descs = (struct ast2700_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; + d = &rx_descs[wptr]; + + if (!d->dirty) @@ -78608,8 +80845,8 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + /* make current descriptor available again */ + d->dirty = 0; + -+ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; -+ writel(wptr | ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); ++ wptr = ((wptr + 1) % OOB_DMA_DESC_NUM); ++ writel(wptr | ESPI_CH2_RX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_RX_DESC_WPTR); + + /* set ready flag base on the next RX descriptor */ + oob->rx_ready = rx_descs[wptr].dirty; @@ -78624,7 +80861,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_oob_get_rx(struct file *fp, ++static long ast2700_espi_oob_get_rx(struct file *fp, + struct aspeed_espi_oob *oob, + struct aspeed_espi_ioc *ioc) +{ @@ -78659,7 +80896,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + } + + if (oob->dma.enable) { -+ rc = ast2600_espi_oob_dma_get_rx(fp, oob, ioc); ++ rc = ast2700_espi_oob_dma_get_rx(fp, oob, ioc); + goto unlock_mtx_n_out; + } + @@ -78667,10 +80904,10 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ reg = readl(espi->regs + ESPI_OOB_RX_CTRL); -+ cyc = FIELD_GET(ESPI_OOB_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_OOB_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_OOB_RX_CTRL_LEN, reg); ++ reg = readl(espi->regs + ESPI_CH2_RX_CTRL); ++ cyc = FIELD_GET(ESPI_CH2_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_CH2_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_CH2_RX_CTRL_LEN, reg); + + /* + * calculate the length of the rest part of the @@ -78696,8 +80933,10 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_OOB_RX_DATA) & 0xff; ++ for (i = sizeof(*hdr); i < pkt_len; ++i) { ++ reg = readl(espi->regs + ESPI_CH2_RX_DATA); ++ pkt[i] = reg & 0xff; ++ } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; @@ -78706,7 +80945,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + spin_lock_irqsave(&oob->lock, flags); + -+ writel(ESPI_OOB_RX_CTRL_SERV_PEND, espi->regs + ESPI_OOB_RX_CTRL); ++ writel(ESPI_CH2_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH2_RX_CTRL); + oob->rx_ready = 0; + + spin_unlock_irqrestore(&oob->lock, flags); @@ -78722,12 +80961,12 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_oob_dma_put_tx(struct file *fp, ++static long ast2700_espi_oob_dma_put_tx(struct file *fp, + struct aspeed_espi_oob *oob, + struct aspeed_espi_ioc *ioc) +{ -+ struct ast2600_espi_oob_dma_tx_desc *d; -+ struct ast2600_espi_oob_dma_tx_desc *tx_descs; ++ struct ast2700_espi_oob_dma_tx_desc *d; ++ struct ast2700_espi_oob_dma_tx_desc *tx_descs; + struct aspeed_espi *espi; + struct espi_comm_hdr *hdr; + u32 rptr, wptr; @@ -78748,17 +80987,17 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + } + + /* kick HW to update descriptor read/write pointer */ -+ writel(ESPI_OOB_TX_DESC_RPTR_UPDATE, espi->regs + ESPI_OOB_TX_DESC_RPTR); ++ writel(ESPI_CH2_TX_DESC_RPTR_UPT, espi->regs + ESPI_CH2_TX_DESC_RPTR); + -+ rptr = readl(espi->regs + ESPI_OOB_TX_DESC_RPTR); -+ wptr = readl(espi->regs + ESPI_OOB_TX_DESC_WPTR); ++ rptr = readl(espi->regs + ESPI_CH2_TX_DESC_RPTR); ++ wptr = readl(espi->regs + ESPI_CH2_TX_DESC_WPTR); + + if (((wptr + 1) % OOB_DMA_DESC_NUM) == rptr) { + rc = -EBUSY; + goto free_n_out; + } + -+ tx_descs = (struct ast2600_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ tx_descs = (struct ast2700_espi_oob_dma_tx_desc *)oob->dma.txd_virt; + d = &tx_descs[wptr]; + d->cyc = hdr->cyc; + d->tag = hdr->tag; @@ -78770,7 +81009,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + dma_wmb(); + + wptr = (wptr + 1) % OOB_DMA_DESC_NUM; -+ writel(wptr | ESPI_OOB_TX_DESC_WPTR_SEND_EN, espi->regs + ESPI_OOB_TX_DESC_WPTR); ++ writel(wptr | ESPI_CH2_TX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_TX_DESC_WPTR); + + rc = 0; + @@ -78780,7 +81019,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_oob_put_tx(struct file *fp, ++static long ast2700_espi_oob_put_tx(struct file *fp, + struct aspeed_espi_oob *oob, + struct aspeed_espi_ioc *ioc) +{ @@ -78796,12 +81035,12 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return -EAGAIN; + + if (oob->dma.enable) { -+ rc = ast2600_espi_oob_dma_put_tx(fp, oob, ioc); ++ rc = ast2700_espi_oob_dma_put_tx(fp, oob, ioc); + goto unlock_mtx_n_out; + } + -+ reg = readl(espi->regs + ESPI_OOB_TX_CTRL); -+ if (reg & ESPI_OOB_TX_CTRL_TRIG_PEND) { ++ reg = readl(espi->regs + ESPI_CH2_TX_CTRL); ++ if (reg & ESPI_CH2_TX_CTRL_TRIG_PEND) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } @@ -78829,17 +81068,17 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + * part is written to HW registers + */ + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_OOB_TX_DATA); ++ writel(pkt[i], espi->regs + ESPI_CH2_TX_DATA); + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ reg = FIELD_PREP(ESPI_OOB_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_OOB_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_OOB_TX_CTRL_LEN, len) -+ | ESPI_OOB_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_OOB_TX_CTRL); ++ reg = FIELD_PREP(ESPI_CH2_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_CH2_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_CH2_TX_CTRL_LEN, len) ++ | ESPI_CH2_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_CH2_TX_CTRL); + + rc = 0; + @@ -78852,7 +81091,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2700_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_oob *oob; + struct aspeed_espi_ioc ioc; @@ -78867,20 +81106,20 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + switch (cmd) { + case ASPEED_ESPI_OOB_GET_RX: -+ return ast2600_espi_oob_get_rx(fp, oob, &ioc); ++ return ast2700_espi_oob_get_rx(fp, oob, &ioc); + case ASPEED_ESPI_OOB_PUT_TX: -+ return ast2600_espi_oob_put_tx(fp, oob, &ioc); ++ return ast2700_espi_oob_put_tx(fp, oob, &ioc); + }; + + return -EINVAL; +} + -+static const struct file_operations ast2600_espi_oob_fops = { ++static const struct file_operations ast2700_espi_oob_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2600_espi_oob_ioctl, ++ .unlocked_ioctl = ast2700_espi_oob_ioctl, +}; + -+static void ast2600_espi_oob_isr(struct aspeed_espi *espi) ++static void ast2700_espi_oob_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; + unsigned long flags; @@ -78888,10 +81127,10 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + oob = &espi->oob; + -+ sts = readl(espi->regs + ESPI_INT_STS); ++ sts = readl(espi->regs + ESPI_CH2_INT_STS); + -+ if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { -+ writel(ESPI_INT_STS_OOB_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ if (sts & ESPI_CH2_INT_STS_RX_CMPLT) { ++ writel(ESPI_CH2_INT_STS_RX_CMPLT, espi->regs + ESPI_CH2_INT_STS); + + spin_lock_irqsave(&oob->lock, flags); + oob->rx_ready = true; @@ -78901,75 +81140,80 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + } +} + -+static void ast2600_espi_oob_reset(struct aspeed_espi *espi) ++static void ast2700_espi_oob_reset(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; -+ struct ast2600_espi_oob_dma_tx_desc *tx_desc; -+ struct ast2600_espi_oob_dma_rx_desc *rx_desc; ++ struct ast2700_espi_oob_dma_tx_desc *tx_desc; ++ struct ast2700_espi_oob_dma_rx_desc *rx_desc; + dma_addr_t tx_addr, rx_addr; + u32 reg; + int i; + -+ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_OOB, espi->regs + ESPI_INT_STS); ++ oob = &espi->oob; + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_OOB_TX_SW_RST -+ | ESPI_CTRL_OOB_RX_SW_RST -+ | ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN -+ | ESPI_CTRL_OOB_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ writel(0x0, espi->regs + ESPI_CH2_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_CH2_INT_STS); + -+ udelay(1); ++ reg = readl(espi->regs + ESPI_CH2_CTRL); ++ reg &= ~(ESPI_CH2_CTRL_TX_RST ++ | ESPI_CH2_CTRL_RX_RST ++ | ESPI_CH2_CTRL_TX_DMA_EN ++ | ESPI_CH2_CTRL_RX_DMA_EN ++ | ESPI_CH2_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + -+ reg |= (ESPI_CTRL_OOB_TX_SW_RST | ESPI_CTRL_OOB_RX_SW_RST); -+ writel(reg, espi->regs + ESPI_CTRL); ++ udelay(1); + -+ oob = &espi->oob; ++ reg |= (ESPI_CH2_CTRL_TX_RST | ESPI_CH2_CTRL_RX_RST); ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + + if (oob->dma.enable) { + tx_addr = oob->dma.tx_addr; + rx_addr = oob->dma.rx_addr; + -+ tx_desc = (struct ast2600_espi_oob_dma_tx_desc *)oob->dma.txd_virt; -+ rx_desc = (struct ast2600_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ tx_desc = (struct ast2700_espi_oob_dma_tx_desc *)oob->dma.txd_virt; ++ rx_desc = (struct ast2700_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; + + for (i = 0; i < OOB_DMA_DESC_NUM; ++i) { -+ tx_desc[i].data_addr = tx_addr; ++ tx_desc[i].data_addrh = upper_32_bits(tx_addr); ++ tx_desc[i].data_addrl = tx_addr & 0xffffffff; + tx_addr += PAGE_SIZE; + -+ rx_desc[i].data_addr = rx_addr; ++ rx_desc[i].data_addrh = upper_32_bits(rx_addr); ++ rx_desc[i].data_addrl = rx_addr & 0xffffffff; + rx_desc[i].dirty = 0; + rx_addr += PAGE_SIZE; + } + -+ writel(oob->dma.txd_addr, espi->regs + ESPI_OOB_TX_DMA); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_TX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_OOB_TX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_TX_DESC_NUM); ++ writel(upper_32_bits(oob->dma.txd_addr), espi->regs + ESPI_CH2_TX_DMAH); ++ writel(oob->dma.txd_addr & 0xffffffff, espi->regs + ESPI_CH2_TX_DMAL); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_TX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_CH2_TX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_TX_DESC_EPTR); + -+ writel(oob->dma.rxd_addr, espi->regs + ESPI_OOB_RX_DMA); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_OOB_RX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_OOB_RX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_OOB_RX_DESC_NUM); ++ writel(upper_32_bits(oob->dma.rxd_addr), espi->regs + ESPI_CH2_RX_DMAH); ++ writel(oob->dma.rxd_addr & 0xffffffff, espi->regs + ESPI_CH2_RX_DMAL); ++ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_RX_DESC_RPTR); ++ writel(0x0, espi->regs + ESPI_CH2_RX_DESC_WPTR); ++ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_RX_DESC_EPTR); + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH2_CTRL) ++ | ESPI_CH2_CTRL_TX_DMA_EN ++ | ESPI_CH2_CTRL_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + + /* activate RX DMA to make OOB_FREE */ -+ writel(ESPI_OOB_RX_DESC_WPTR_RECV_EN, espi->regs + ESPI_OOB_RX_DESC_WPTR); ++ reg = readl(espi->regs + ESPI_CH2_RX_DESC_WPTR) | ESPI_CH2_RX_DESC_WPTR_VALID; ++ writel(reg, espi->regs + ESPI_CH2_RX_DESC_WPTR); + } + -+ writel(ESPI_INT_EN_OOB_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ writel(ESPI_CH2_INT_EN_RX_CMPLT, espi->regs + ESPI_CH2_INT_EN); + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_OOB_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH2_CTRL) | ESPI_CH2_CTRL_SW_RDY; ++ writel(reg, espi->regs + ESPI_CH2_CTRL); +} + -+int ast2600_espi_oob_probe(struct aspeed_espi *espi) ++int ast2700_espi_oob_probe(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; + struct device *dev; @@ -78989,9 +81233,9 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); + if (oob->dma.enable) { + oob->dma.txd_virt = dmam_alloc_coherent(dev, -+ sizeof(struct ast2600_espi_oob_dma_tx_desc) * ++ sizeof(struct ast2700_espi_oob_dma_tx_desc) * + OOB_DMA_DESC_NUM, -+ &oob->dma.txd_addr, GFP_KERNEL); ++ &oob->dma.txd_addr, GFP_KERNEL); + if (!oob->dma.txd_virt) { + dev_err(dev, "cannot allocate DMA TX descriptor\n"); + return -ENOMEM; @@ -79003,7 +81247,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + } + + oob->dma.rxd_virt = dmam_alloc_coherent(dev, -+ sizeof(struct ast2600_espi_oob_dma_rx_desc) * ++ sizeof(struct ast2700_espi_oob_dma_rx_desc) * + OOB_DMA_DESC_NUM, + &oob->dma.rxd_addr, GFP_KERNEL); + if (!oob->dma.rxd_virt) { @@ -79020,20 +81264,20 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + oob->mdev.parent = dev; + oob->mdev.minor = MISC_DYNAMIC_MINOR; -+ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob", DEVICE_NAME); -+ oob->mdev.fops = &ast2600_espi_oob_fops; ++ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob%d", DEVICE_NAME, espi->dev_id); ++ oob->mdev.fops = &ast2700_espi_oob_fops; + rc = misc_register(&oob->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", oob->mdev.name); + return rc; + } + -+ ast2600_espi_oob_reset(espi); ++ ast2700_espi_oob_reset(espi); + + return 0; +} + -+int ast2600_espi_oob_remove(struct aspeed_espi *espi) ++int ast2700_espi_oob_remove(struct aspeed_espi *espi) +{ + struct aspeed_espi_oob *oob; + struct device *dev; @@ -79043,25 +81287,20 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + oob = &espi->oob; + -+ writel(ESPI_INT_EN_OOB, espi->regs + ESPI_INT_EN_CLR); ++ writel(0x0, espi->regs + ESPI_CH2_INT_EN); + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_OOB_TX_DMA_EN -+ | ESPI_CTRL_OOB_RX_DMA_EN -+ | ESPI_CTRL_OOB_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH2_CTRL); ++ reg &= ~(ESPI_CH2_CTRL_TX_DMA_EN ++ | ESPI_CH2_CTRL_RX_DMA_EN ++ | ESPI_CH2_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH2_CTRL); + + if (oob->dma.enable) { -+ dmam_free_coherent(dev, -+ sizeof(struct ast2600_espi_oob_dma_tx_desc) * -+ OOB_DMA_DESC_NUM, ++ dmam_free_coherent(dev, sizeof(struct ast2700_espi_oob_dma_tx_desc) * OOB_DMA_DESC_NUM, + oob->dma.txd_virt, oob->dma.txd_addr); + dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, + oob->dma.tx_virt, oob->dma.tx_addr); -+ -+ dmam_free_coherent(dev, -+ sizeof(struct ast2600_espi_oob_dma_rx_desc) * -+ OOB_DMA_DESC_NUM, ++ dmam_free_coherent(dev, sizeof(struct ast2700_espi_oob_dma_rx_desc) * OOB_DMA_DESC_NUM, + oob->dma.rxd_virt, oob->dma.rxd_addr); + dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, + oob->dma.rx_virt, oob->dma.rx_addr); @@ -79076,7 +81315,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as +} + +/* flash channel (CH3) */ -+static long ast2600_espi_flash_get_rx(struct file *fp, ++static long ast2700_espi_flash_get_rx(struct file *fp, + struct aspeed_espi_flash *flash, + struct aspeed_espi_ioc *ioc) +{ @@ -79116,10 +81355,10 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ -+ reg = readl(espi->regs + ESPI_FLASH_RX_CTRL); -+ cyc = FIELD_GET(ESPI_FLASH_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_FLASH_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_FLASH_RX_CTRL_LEN, reg); ++ reg = readl(espi->regs + ESPI_CH3_RX_CTRL); ++ cyc = FIELD_GET(ESPI_CH3_RX_CTRL_CYC, reg); ++ tag = FIELD_GET(ESPI_CH3_RX_CTRL_TAG, reg); ++ len = FIELD_GET(ESPI_CH3_RX_CTRL_LEN, reg); + + /* + * calculate the length of the rest part of the @@ -79172,7 +81411,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_FLASH_RX_DATA) & 0xff; ++ pkt[i] = readl(espi->regs + ESPI_CH3_RX_DATA) & 0xff; + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { @@ -79182,7 +81421,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + spin_lock_irqsave(&flash->lock, flags); + -+ writel(ESPI_FLASH_RX_CTRL_SERV_PEND, espi->regs + ESPI_FLASH_RX_CTRL); ++ writel(ESPI_CH3_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH3_RX_CTRL); + flash->rx_ready = 0; + + spin_unlock_irqrestore(&flash->lock, flags); @@ -79198,7 +81437,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_flash_put_tx(struct file *fp, ++static long ast2700_espi_flash_put_tx(struct file *fp, + struct aspeed_espi_flash *flash, + struct aspeed_espi_ioc *ioc) +{ @@ -79213,8 +81452,8 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + if (!mutex_trylock(&flash->tx_mtx)) + return -EAGAIN; + -+ reg = readl(espi->regs + ESPI_FLASH_TX_CTRL); -+ if (reg & ESPI_FLASH_TX_CTRL_TRIG_PEND) { ++ reg = readl(espi->regs + ESPI_CH3_TX_CTRL); ++ if (reg & ESPI_CH3_TX_CTRL_TRIG_PEND) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } @@ -79241,18 +81480,18 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_FLASH_TX_DATA); ++ writel(pkt[i], espi->regs + ESPI_CH3_TX_DATA); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + -+ reg = FIELD_PREP(ESPI_FLASH_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_FLASH_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_FLASH_TX_CTRL_LEN, len) -+ | ESPI_FLASH_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_FLASH_TX_CTRL); ++ reg = FIELD_PREP(ESPI_CH3_TX_CTRL_CYC, cyc) ++ | FIELD_PREP(ESPI_CH3_TX_CTRL_TAG, tag) ++ | FIELD_PREP(ESPI_CH3_TX_CTRL_LEN, len) ++ | ESPI_CH3_TX_CTRL_TRIG_PEND; ++ writel(reg, espi->regs + ESPI_CH3_TX_CTRL); + + rc = 0; + @@ -79265,7 +81504,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + return rc; +} + -+static long ast2600_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static long ast2700_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_flash *flash; + struct aspeed_espi_ioc ioc; @@ -79280,22 +81519,20 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + switch (cmd) { + case ASPEED_ESPI_FLASH_GET_RX: -+ return ast2600_espi_flash_get_rx(fp, flash, &ioc); ++ return ast2700_espi_flash_get_rx(fp, flash, &ioc); + case ASPEED_ESPI_FLASH_PUT_TX: -+ return ast2600_espi_flash_put_tx(fp, flash, &ioc); -+ default: -+ break; ++ return ast2700_espi_flash_put_tx(fp, flash, &ioc); + }; + + return -EINVAL; +} + -+static const struct file_operations ast2600_espi_flash_fops = { ++static const struct file_operations ast2700_espi_flash_fops = { + .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2600_espi_flash_ioctl, ++ .unlocked_ioctl = ast2700_espi_flash_ioctl, +}; + -+static void ast2600_espi_flash_isr(struct aspeed_espi *espi) ++static void ast2700_espi_flash_isr(struct aspeed_espi *espi) +{ + struct aspeed_espi_flash *flash; + unsigned long flags; @@ -79303,10 +81540,10 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + flash = &espi->flash; + -+ sts = readl(espi->regs + ESPI_INT_STS); ++ sts = readl(espi->regs + ESPI_CH3_INT_STS); + -+ if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { -+ writel(ESPI_INT_STS_FLASH_RX_CMPLT, espi->regs + ESPI_INT_STS); ++ if (sts & ESPI_CH3_INT_STS_RX_CMPLT) { ++ writel(ESPI_CH3_INT_STS_RX_CMPLT, espi->regs + ESPI_CH3_INT_STS); + + spin_lock_irqsave(&flash->lock, flags); + flash->rx_ready = true; @@ -79316,59 +81553,65 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + } +} + -+static void ast2600_espi_flash_reset(struct aspeed_espi *espi) ++static void ast2700_espi_flash_reset(struct aspeed_espi *espi) +{ -+ struct aspeed_espi_flash *flash; + u32 reg; ++ u64 mask; ++ struct aspeed_espi_flash *flash = &espi->flash; + -+ flash = &espi->flash; -+ -+ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); -+ writel(ESPI_INT_STS_FLASH, espi->regs + ESPI_INT_STS); ++ writel(0x0, espi->regs + ESPI_CH3_INT_EN); ++ writel(0xffffffff, espi->regs + ESPI_CH3_INT_STS); + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_FLASH_TX_SW_RST -+ | ESPI_CTRL_FLASH_RX_SW_RST -+ | ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN -+ | ESPI_CTRL_FLASH_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH3_CTRL); ++ reg &= ~(ESPI_CH3_CTRL_TX_RST ++ | ESPI_CH3_CTRL_RX_RST ++ | ESPI_CH3_CTRL_TX_DMA_EN ++ | ESPI_CH3_CTRL_RX_DMA_EN ++ | ESPI_CH3_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + + udelay(1); + -+ reg |= (ESPI_CTRL_FLASH_TX_SW_RST | ESPI_CTRL_FLASH_RX_SW_RST); -+ writel(reg, espi->regs + ESPI_CTRL); -+ -+ reg = readl(espi->regs + ESPI_CTRL) & ~ESPI_CTRL_FLASH_EDAF_MODE; -+ reg |= FIELD_PREP(ESPI_CTRL_FLASH_EDAF_MODE, flash->edaf.mode); -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg |= (ESPI_CH3_CTRL_TX_RST | ESPI_CH3_CTRL_RX_RST); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + -+ if (flash->edaf.mode == EDAF_MODE_MIX) { -+ reg = FIELD_PREP(ESPI_FLASH_EDAF_TADDR_BASE, flash->edaf.taddr >> 24) -+ | FIELD_PREP(ESPI_FLASH_EDAF_TADDR_MASK, (~(flash->edaf.size - 1)) >> 24); -+ writel(reg, espi->regs + ESPI_FLASH_EDAF_TADDR); ++ if (flash->edaf.mode == EDAF_MODE_MIX) { ++ mask = ~(flash->edaf.size - 1); ++ writel(upper_32_bits(mask), espi->regs + ESPI_CH3_EDAF_MASKH); ++ writel(mask & 0xffffffff, espi->regs + ESPI_CH3_EDAF_MASKL); ++ writel(upper_32_bits(flash->edaf.taddr), espi->regs + ESPI_CH3_EDAF_TADDRH); ++ writel(flash->edaf.taddr & 0xffffffff, espi->regs + ESPI_CH3_EDAF_TADDRL); + } + ++ reg = readl(espi->regs + ESPI_CH3_CTRL) & ~ESPI_CH3_CTRL_EDAF_MODE; ++ reg |= FIELD_PREP(ESPI_CH3_CTRL_EDAF_MODE, flash->edaf.mode); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ + if (flash->dma.enable) { -+ writel(flash->dma.tx_addr, espi->regs + ESPI_FLASH_TX_DMA); -+ writel(flash->dma.rx_addr, espi->regs + ESPI_FLASH_RX_DMA); ++ writel(upper_32_bits(flash->dma.tx_addr), espi->regs + ESPI_CH3_TX_DMAH); ++ writel(flash->dma.tx_addr & 0xffffffff, espi->regs + ESPI_CH3_TX_DMAL); ++ writel(upper_32_bits(flash->dma.rx_addr), espi->regs + ESPI_CH3_RX_DMAH); ++ writel(flash->dma.rx_addr & 0xffffffff, espi->regs + ESPI_CH3_RX_DMAL); + -+ reg = readl(espi->regs + ESPI_CTRL) -+ | ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH3_CTRL) ++ | ESPI_CH3_CTRL_TX_DMA_EN ++ | ESPI_CH3_CTRL_RX_DMA_EN; ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + } + -+ writel(ESPI_INT_EN_FLASH_RX_CMPLT, espi->regs + ESPI_INT_EN); ++ writel(ESPI_CH3_INT_EN_RX_CMPLT, espi->regs + ESPI_CH3_INT_EN); + -+ reg = readl(espi->regs + ESPI_CTRL) | ESPI_CTRL_FLASH_SW_RDY; -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH3_CTRL) | ESPI_CH3_CTRL_SW_RDY; ++ writel(reg, espi->regs + ESPI_CH3_CTRL); +} + -+int ast2600_espi_flash_probe(struct aspeed_espi *espi) ++int ast2700_espi_flash_probe(struct aspeed_espi *espi) +{ + struct aspeed_espi_flash *flash; ++ struct device_node *np; ++ struct resource res; + struct device *dev; ++ void *virt; + int rc; + + dev = espi->dev; @@ -79384,18 +81627,24 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + flash->edaf.mode = EDAF_MODE_HW; + -+ of_property_read_u32(dev->of_node, "flash-safs-mode", &flash->edaf.mode); ++ of_property_read_u32(dev->of_node, "flash-edaf-mode", &flash->edaf.mode); ++ dev_info(dev, "eDAF mode: 0x%x\n", flash->edaf.mode); + if (flash->edaf.mode == EDAF_MODE_MIX) { -+ rc = of_property_read_u32(dev->of_node, "flash-safs-tgt-addr", (u32 *)&flash->edaf.taddr); -+ if (rc || !IS_ALIGNED(flash->edaf.taddr, FLASH_EDAF_ALIGN)) { -+ dev_err(dev, "cannot get 16MB-aligned SAFS target address\n"); ++ np = of_parse_phandle(dev->of_node, "flash-edaf-tgt-addr", 0); ++ if (!np || of_address_to_resource(np, 0, &res)) { ++ dev_err(dev, "cannot get eDAF memory region\n"); + return -ENODEV; + } + -+ rc = of_property_read_u32(dev->of_node, "flash-safs-size", (u32 *)&flash->edaf.size); -+ if (rc || !IS_ALIGNED(flash->edaf.size, FLASH_EDAF_ALIGN)) { -+ dev_err(dev, "cannot get 16MB-aligned SAFS size\n"); -+ return -ENODEV; ++ of_node_put(np); ++ ++ flash->edaf.taddr = res.start; ++ flash->edaf.size = resource_size(&res); ++ ++ virt = devm_ioremap_resource(dev, &res); ++ if (!virt) { ++ dev_err(dev, "cannot map eDAF memory region\n"); ++ return -ENOMEM; + } + } + @@ -79416,20 +81665,20 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + flash->mdev.parent = dev; + flash->mdev.minor = MISC_DYNAMIC_MINOR; -+ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash", DEVICE_NAME); -+ flash->mdev.fops = &ast2600_espi_flash_fops; ++ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash%d", DEVICE_NAME, espi->dev_id); ++ flash->mdev.fops = &ast2700_espi_flash_fops; + rc = misc_register(&flash->mdev); + if (rc) { + dev_err(dev, "cannot register device %s\n", flash->mdev.name); + return rc; + } + -+ ast2600_espi_flash_reset(espi); ++ ast2700_espi_flash_reset(espi); + + return 0; +} + -+int ast2600_espi_flash_remove(struct aspeed_espi *espi) ++int ast2700_espi_flash_remove(struct aspeed_espi *espi) +{ + struct aspeed_espi_flash *flash; + struct device *dev; @@ -79439,13 +81688,13 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as + + flash = &espi->flash; + -+ writel(ESPI_INT_EN_FLASH, espi->regs + ESPI_INT_EN_CLR); ++ writel(0x0, espi->regs + ESPI_CH3_INT_EN); + -+ reg = readl(espi->regs + ESPI_CTRL); -+ reg &= ~(ESPI_CTRL_FLASH_TX_DMA_EN -+ | ESPI_CTRL_FLASH_RX_DMA_EN -+ | ESPI_CTRL_FLASH_SW_RDY); -+ writel(reg, espi->regs + ESPI_CTRL); ++ reg = readl(espi->regs + ESPI_CH3_CTRL); ++ reg &= ~(ESPI_CH3_CTRL_TX_DMA_EN ++ | ESPI_CH3_CTRL_RX_DMA_EN ++ | ESPI_CH3_CTRL_SW_RDY); ++ writel(reg, espi->regs + ESPI_CH3_CTRL); + + if (flash->dma.enable) { + dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); @@ -79461,887 +81710,1376 @@ diff --git a/drivers/soc/aspeed/espi/ast2600-espi.c b/drivers/soc/aspeed/espi/as +} + +/* global control */ -+irqreturn_t ast2600_espi_isr(int irq, void *arg) ++irqreturn_t ast2700_espi_isr(int irq, void *arg) +{ -+ struct aspeed_espi *espi; + u32 sts; -+ -+ espi = (struct aspeed_espi *)arg; ++ struct aspeed_espi *espi = (struct aspeed_espi *)arg; + + sts = readl(espi->regs + ESPI_INT_STS); + if (!sts) + return IRQ_NONE; + -+ if (sts & ESPI_INT_STS_PERIF) -+ ast2600_espi_perif_isr(espi); ++ if (sts & ESPI_INT_STS_CH0) ++ ast2700_espi_perif_isr(espi); + -+ if (sts & ESPI_INT_STS_VW) -+ ast2600_espi_vw_isr(espi); ++ if (sts & ESPI_INT_STS_CH1) ++ ast2700_espi_vw_isr(espi); + -+ if (sts & ESPI_INT_STS_OOB) -+ ast2600_espi_oob_isr(espi); ++ if (sts & ESPI_INT_STS_CH2) ++ ast2700_espi_oob_isr(espi); + -+ if (sts & ESPI_INT_STS_FLASH) -+ ast2600_espi_flash_isr(espi); ++ if (sts & ESPI_INT_STS_CH3) ++ ast2700_espi_flash_isr(espi); + + if (sts & ESPI_INT_STS_RST_DEASSERT) { -+ /* this will clear all interrupt enable and status */ -+ reset_control_assert(espi->rst); -+ reset_control_deassert(espi->rst); -+ -+ ast2600_espi_perif_sw_reset(espi); -+ ast2600_espi_perif_reset(espi); -+ ast2600_espi_vw_reset(espi); -+ ast2600_espi_oob_reset(espi); -+ ast2600_espi_flash_reset(espi); -+ -+ /* re-enable eSPI_RESET# interrupt */ -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); ++ ast2700_espi_perif_sw_reset(espi); ++ ast2700_espi_perif_reset(espi); ++ ast2700_espi_vw_reset(espi); ++ ast2700_espi_oob_reset(espi); ++ ast2700_espi_flash_reset(espi); ++ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); + } + + return IRQ_HANDLED; +} + -+void ast2600_espi_pre_init(struct aspeed_espi *espi) ++void ast2700_espi_pre_init(struct aspeed_espi *espi) +{ -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); ++ struct device *dev; ++ struct regmap *scu1; ++ u32 reg; ++ int rc; ++ ++ dev = espi->dev; ++ ++ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,syscon"); ++ if (IS_ERR(scu1)) { ++ dev_err(dev, "failed to find SCU1 regmap, error %ld\n", PTR_ERR(scu1)); ++ return; ++ } ++ rc = regmap_update_bits(scu1, SCU1_DDR, ++ SCU1_DDR_DIS_ESPI0_AHB | SCU1_DDR_DIS_ESPI1_AHB, ++ 0); ++ if (rc) { ++ dev_err(dev, "failed to update SCU1 regmap, error %d\n", rc); ++ return; ++ } ++ ++ espi->dev_id = ida_alloc(&ast2700_espi_ida, GFP_KERNEL); ++ if (espi->dev_id < 0) { ++ dev_err(dev, "cannote allocate device ID, error %d\n", espi->dev_id); ++ return; ++ } ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); +} + -+void ast2600_espi_post_init(struct aspeed_espi *espi) ++void ast2700_espi_post_init(struct aspeed_espi *espi) +{ -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN); ++ u32 reg; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg |= ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); +} + -+void ast2600_espi_deinit(struct aspeed_espi *espi) ++void ast2700_espi_deinit(struct aspeed_espi *espi) +{ -+ writel(ESPI_INT_EN_RST_DEASSERT, espi->regs + ESPI_INT_EN_CLR); ++ struct device *dev; ++ u32 reg; ++ ++ dev = espi->dev; ++ ++ reg = readl(espi->regs + ESPI_INT_EN); ++ reg &= ~ESPI_INT_EN_RST_DEASSERT; ++ writel(reg, espi->regs + ESPI_INT_EN); +} -diff --git a/drivers/soc/aspeed/espi/ast2600-espi.h b/drivers/soc/aspeed/espi/ast2600-espi.h ---- a/drivers/soc/aspeed/espi/ast2600-espi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/ast2600-espi.h 2026-04-08 18:03:48.313705265 +0000 -@@ -0,0 +1,306 @@ +diff --git a/drivers/soc/aspeed/espi/ast2700-espi.h b/drivers/soc/aspeed/espi/ast2700-espi.h +--- a/drivers/soc/aspeed/espi/ast2700-espi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-espi.h 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,300 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* -+ * Register definitions for Aspeed AST2600 eSPI controller ++ * Register definitions for Aspeed AST2700 eSPI controller + * Copyright 2023 Aspeed Technology Inc. + */ -+#ifndef _AST2600_ESPI_H_ -+#define _AST2600_ESPI_H_ ++#ifndef _AST2700_ESPI_H_ ++#define _AST2700_ESPI_H_ + +#include -+#include -+ +#include "aspeed-espi.h" + -+/* registers */ -+#define ESPI_CTRL 0x000 -+#define ESPI_CTRL_FLASH_TX_SW_RST BIT(31) -+#define ESPI_CTRL_FLASH_RX_SW_RST BIT(30) -+#define ESPI_CTRL_OOB_TX_SW_RST BIT(29) -+#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) -+#define ESPI_CTRL_PERIF_NP_TX_SW_RST BIT(27) -+#define ESPI_CTRL_PERIF_NP_RX_SW_RST BIT(26) -+#define ESPI_CTRL_PERIF_PC_TX_SW_RST BIT(25) -+#define ESPI_CTRL_PERIF_PC_RX_SW_RST BIT(24) -+#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) -+#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) -+#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) -+#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) -+#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) -+#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) -+#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) -+#define ESPI_CTRL_FLASH_EDAF_MODE GENMASK(11, 10) -+#define ESPI_CTRL_VW_GPIO_SW BIT(9) -+#define ESPI_CTRL_FLASH_SW_RDY BIT(7) -+#define ESPI_CTRL_OOB_SW_RDY BIT(4) -+#define ESPI_CTRL_VW_SW_RDY BIT(3) -+#define ESPI_CTRL_PERIF_SW_RDY BIT(1) -+#define ESPI_STS 0x004 -+#define ESPI_INT_STS 0x008 -+#define ESPI_INT_STS_RST_DEASSERT BIT(31) -+#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) -+#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) -+#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) -+#define ESPI_INT_STS_OOB_TX_ERR BIT(20) -+#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) -+#define ESPI_INT_STS_OOB_TX_ABT BIT(18) -+#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) -+#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) -+#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) -+#define ESPI_INT_STS_OOB_RX_ABT BIT(14) -+#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) -+#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) -+#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) -+#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) -+#define ESPI_INT_STS_VW_GPIO BIT(9) -+#define ESPI_INT_STS_VW_SYSEVT BIT(8) -+#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) -+#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) -+#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) -+#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) -+#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) -+#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) -+#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) -+#define ESPI_INT_EN 0x00c -+#define ESPI_INT_EN_RST_DEASSERT BIT(31) -+#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) -+#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) -+#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) -+#define ESPI_INT_EN_OOB_TX_ERR BIT(20) -+#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) -+#define ESPI_INT_EN_OOB_TX_ABT BIT(18) -+#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) -+#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) -+#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) -+#define ESPI_INT_EN_OOB_RX_ABT BIT(14) -+#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) -+#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) -+#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) -+#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) -+#define ESPI_INT_EN_VW_GPIO BIT(9) -+#define ESPI_INT_EN_VW_SYSEVT BIT(8) -+#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) -+#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) -+#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) -+#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) -+#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) -+#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) -+#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) -+#define ESPI_PERIF_PC_RX_DMA 0x010 -+#define ESPI_PERIF_PC_RX_CTRL 0x014 -+#define ESPI_PERIF_PC_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_PERIF_PC_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_PC_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_PC_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_PC_RX_DATA 0x018 -+#define ESPI_PERIF_PC_TX_DMA 0x020 -+#define ESPI_PERIF_PC_TX_CTRL 0x024 -+#define ESPI_PERIF_PC_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_PERIF_PC_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_PC_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_PC_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_PC_TX_DATA 0x028 -+#define ESPI_PERIF_NP_TX_DMA 0x030 -+#define ESPI_PERIF_NP_TX_CTRL 0x034 -+#define ESPI_PERIF_NP_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_PERIF_NP_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_PERIF_NP_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_PERIF_NP_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_PERIF_NP_TX_DATA 0x038 -+#define ESPI_OOB_RX_DMA 0x040 -+#define ESPI_OOB_RX_CTRL 0x044 -+#define ESPI_OOB_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_OOB_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_OOB_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_OOB_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_OOB_RX_DATA 0x048 -+#define ESPI_OOB_TX_DMA 0x050 -+#define ESPI_OOB_TX_CTRL 0x054 -+#define ESPI_OOB_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_OOB_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_OOB_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_OOB_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_OOB_TX_DATA 0x058 -+#define ESPI_FLASH_RX_DMA 0x060 -+#define ESPI_FLASH_RX_CTRL 0x064 -+#define ESPI_FLASH_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_FLASH_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_FLASH_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_FLASH_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_FLASH_RX_DATA 0x068 -+#define ESPI_FLASH_TX_DMA 0x070 -+#define ESPI_FLASH_TX_CTRL 0x074 -+#define ESPI_FLASH_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_FLASH_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_FLASH_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_FLASH_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_FLASH_TX_DATA 0x078 -+#define ESPI_CTRL2 0x080 -+#define ESPI_CTRL2_VW_TX_SORT BIT(30) -+#define ESPI_CTRL2_MCYC_RD_DIS_WDT BIT(11) -+#define ESPI_CTRL2_MCYC_WR_DIS_WDT BIT(10) -+#define ESPI_CTRL2_MCYC_RD_DIS BIT(6) -+#define ESPI_CTRL2_MMBI_RD_DIS ESPI_CTRL2_MCYC_RD_DIS -+#define ESPI_CTRL2_MCYC_WR_DIS BIT(4) -+#define ESPI_CTRL2_MMBI_WR_DIS ESPI_CTRL2_MCYC_WR_DIS -+#define ESPI_PERIF_MCYC_SADDR 0x084 -+#define ESPI_PERIF_MMBI_SADDR ESPI_PERIF_MCYC_SADDR -+#define ESPI_PERIF_MCYC_TADDR 0x088 -+#define ESPI_PERIF_MMBI_TADDR ESPI_PERIF_MCYC_TADDR -+#define ESPI_PERIF_MCYC_MASK 0x08c -+#define ESPI_PERIF_MMBI_MASK ESPI_PERIF_MCYC_MASK -+#define ESPI_FLASH_EDAF_TADDR 0x090 -+#define ESPI_FLASH_EDAF_TADDR_BASE GENMASK(31, 24) -+#define ESPI_FLASH_EDAF_TADDR_MASK GENMASK(15, 8) -+#define ESPI_VW_SYSEVT_INT_EN 0x094 -+#define ESPI_VW_SYSEVT 0x098 -+#define ESPI_VW_SYSEVT_HOST_RST_ACK BIT(27) -+#define ESPI_VW_SYSEVT_RST_CPU_INIT BIT(26) -+#define ESPI_VW_SYSEVT_SLV_BOOT_STS BIT(23) -+#define ESPI_VW_SYSEVT_NON_FATAL_ERR BIT(22) -+#define ESPI_VW_SYSEVT_FATAL_ERR BIT(21) -+#define ESPI_VW_SYSEVT_SLV_BOOT_DONE BIT(20) -+#define ESPI_VW_SYSEVT_OOB_RST_ACK BIT(16) -+#define ESPI_VW_SYSEVT_NMI_OUT BIT(10) -+#define ESPI_VW_SYSEVT_SMI_OUT BIT(9) -+#define ESPI_VW_SYSEVT_HOST_RST_WARN BIT(8) -+#define ESPI_VW_SYSEVT_OOB_RST_WARN BIT(6) -+#define ESPI_VW_SYSEVT_PLTRSTN BIT(5) -+#define ESPI_VW_SYSEVT_SUSPEND BIT(4) -+#define ESPI_VW_SYSEVT_S5_SLEEP BIT(2) -+#define ESPI_VW_SYSEVT_S4_SLEEP BIT(1) -+#define ESPI_VW_SYSEVT_S3_SLEEP BIT(0) -+#define ESPI_VW_GPIO_VAL 0x09c -+#define ESPI_GEN_CAP_N_CONF 0x0a0 -+#define ESPI_CH0_CAP_N_CONF 0x0a4 -+#define ESPI_CH1_CAP_N_CONF 0x0a8 -+#define ESPI_CH2_CAP_N_CONF 0x0ac -+#define ESPI_CH3_CAP_N_CONF 0x0b0 -+#define ESPI_CH3_CAP_N_CONF2 0x0b4 -+#define ESPI_VW_GPIO_DIR 0x0c0 -+#define ESPI_VW_GPIO_GRP 0x0c4 -+#define ESPI_INT_EN_CLR 0x0fc -+#define ESPI_VW_SYSEVT1_INT_EN 0x100 -+#define ESPI_VW_SYSEVT1 0x104 -+#define ESPI_VW_SYSEVT1_SUSPEND_ACK BIT(20) -+#define ESPI_VW_SYSEVT1_SUSPEND_WARN BIT(0) -+#define ESPI_VW_SYSEVT_INT_T0 0x110 -+#define ESPI_VW_SYSEVT_INT_T1 0x114 -+#define ESPI_VW_SYSEVT_INT_T2 0x118 -+#define ESPI_VW_SYSEVT_INT_STS 0x11c -+#define ESPI_VW_SYSEVT1_INT_T0 0x120 -+#define ESPI_VW_SYSEVT1_INT_T1 0x124 -+#define ESPI_VW_SYSEVT1_INT_T2 0x128 -+#define ESPI_VW_SYSEVT1_INT_STS 0x12c -+#define ESPI_OOB_RX_DESC_NUM 0x130 -+#define ESPI_OOB_RX_DESC_RPTR 0x134 -+#define ESPI_OOB_RX_DESC_RPTR_UPDATE BIT(31) -+#define ESPI_OOB_RX_DESC_RPTR_RP GENMASK(11, 0) -+#define ESPI_OOB_RX_DESC_WPTR 0x138 -+#define ESPI_OOB_RX_DESC_WPTR_RECV_EN BIT(31) -+#define ESPI_OOB_RX_DESC_WPTR_SP GENMASK(27, 16) -+#define ESPI_OOB_RX_DESC_WPTR_WP GENMASK(11, 0) -+#define ESPI_OOB_TX_DESC_NUM 0x140 -+#define ESPI_OOB_TX_DESC_RPTR 0x144 -+#define ESPI_OOB_TX_DESC_RPTR_UPDATE BIT(31) -+#define ESPI_OOB_TX_DESC_WPTR 0x148 -+#define ESPI_OOB_TX_DESC_WPTR_SEND_EN BIT(31) -+#define ESPI_MMBI_CTRL 0x800 -+#define ESPI_MMBI_CTRL_INST_SZ GENMASK(10, 8) -+#define ESPI_MMBI_CTRL_TOTAL_SZ GENMASK(6, 4) -+#define ESPI_MMBI_CTRL_EN BIT(0) -+#define ESPI_MMBI_INT_STS 0x808 -+#define ESPI_MMBI_INT_EN 0x80c -+#define ESPI_MMBI_HOST_RWP(x) (0x810 + ((x) << 3)) -+ -+/* collect ESPI_INT_EN bits for convenience */ -+#define ESPI_INT_EN_PERIF \ -+ (ESPI_INT_EN_PERIF_NP_TX_ABT | \ -+ ESPI_INT_EN_PERIF_PC_TX_ABT | \ -+ ESPI_INT_EN_PERIF_NP_RX_ABT | \ -+ ESPI_INT_EN_PERIF_PC_RX_ABT | \ -+ ESPI_INT_EN_PERIF_NP_TX_ERR | \ -+ ESPI_INT_EN_PERIF_PC_TX_ERR | \ -+ ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ -+ ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ -+ ESPI_INT_EN_PERIF_PC_RX_CMPLT) -+ -+#define ESPI_INT_EN_VW \ -+ (ESPI_INT_EN_VW_SYSEVT1 | \ -+ ESPI_INT_EN_VW_GPIO | \ -+ ESPI_INT_EN_VW_SYSEVT) -+ -+#define ESPI_INT_EN_OOB \ -+ (ESPI_INT_EN_OOB_RX_TMOUT | \ -+ ESPI_INT_EN_OOB_TX_ERR | \ -+ ESPI_INT_EN_OOB_TX_ABT | \ -+ ESPI_INT_EN_OOB_RX_ABT | \ -+ ESPI_INT_EN_OOB_TX_CMPLT | \ -+ ESPI_INT_EN_OOB_RX_CMPLT) ++/* SCU regiseters */ ++#define SCU1_DDR 0x0c8 ++#define SCU1_DDR_DIS_ESPI0_AHB BIT(0) ++#define SCU1_DDR_DIS_ESPI1_AHB BIT(1) + -+#define ESPI_INT_EN_FLASH \ -+ (ESPI_INT_EN_FLASH_TX_ERR | \ -+ ESPI_INT_EN_FLASH_TX_ABT | \ -+ ESPI_INT_EN_FLASH_RX_ABT | \ -+ ESPI_INT_EN_FLASH_TX_CMPLT | \ -+ ESPI_INT_EN_FLASH_RX_CMPLT) ++/* global registers */ ++#define ESPI_CTRL 0x000 ++#define ESPI_STS 0x004 ++#define ESPI_INT_STS 0x008 ++#define ESPI_INT_STS_RST_DEASSERT BIT(31) ++#define ESPI_INT_STS_RST_ASSERT BIT(30) ++#define ESPI_INT_STS_CH3 BIT(3) ++#define ESPI_INT_STS_CH2 BIT(2) ++#define ESPI_INT_STS_CH1 BIT(1) ++#define ESPI_INT_STS_CH0 BIT(0) ++#define ESPI_INT_EN 0x00c ++#define ESPI_INT_EN_RST_DEASSERT BIT(31) ++#define ESPI_INT_EN_RST_ASSERT BIT(30) ++#define ESPI_DEV_ID 0x010 ++#define ESPI_CAP_GEN 0x014 ++#define ESPI_CAP_GEN_RTC_SUP BIT(29) ++#define ESPI_CAP_CH0 0x018 ++#define ESPI_CAP_CH1 0x01c ++#define ESPI_CAP_CH2 0x020 ++#define ESPI_CAP_CH3_0 0x024 ++#define ESPI_CAP_CH3_1 0x028 ++#define ESPI_DEV_STS 0x030 ++#define ESPI_DBG_CTRL 0x034 ++#define ESPI_DBG_ADDRL 0x038 ++#define ESPI_DBG_ADDRH 0x03c ++#define ESPI_DBG_CMD 0x040 ++#define ESPI_DBG_RES 0x044 ++#define ESPI_CH_ACC_CTRL 0x04c ++#define ESPI_CH_ACC_OFST1 0x050 ++#define ESPI_CH_ACC_OFST2 0x054 ++#define ESPI_WPROT0 0x0f8 ++#define ESPI_WPROT1 0x0fc + -+/* collect ESPI_INT_STS bits for convenience */ -+#define ESPI_INT_STS_PERIF \ -+ (ESPI_INT_STS_PERIF_NP_TX_ABT | \ -+ ESPI_INT_STS_PERIF_PC_TX_ABT | \ -+ ESPI_INT_STS_PERIF_NP_RX_ABT | \ -+ ESPI_INT_STS_PERIF_PC_RX_ABT | \ -+ ESPI_INT_STS_PERIF_NP_TX_ERR | \ -+ ESPI_INT_STS_PERIF_PC_TX_ERR | \ -+ ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ -+ ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ -+ ESPI_INT_STS_PERIF_PC_RX_CMPLT) ++/* peripheral channel (ch0) registers */ ++#define ESPI_CH0_CTRL 0x100 ++#define ESPI_CH0_CTRL_NP_TX_RST BIT(31) ++#define ESPI_CH0_CTRL_NP_RX_RST BIT(30) ++#define ESPI_CH0_CTRL_PC_TX_RST BIT(29) ++#define ESPI_CH0_CTRL_PC_RX_RST BIT(28) ++#define ESPI_CH0_CTRL_NP_TX_DMA_EN BIT(19) ++#define ESPI_CH0_CTRL_PC_TX_DMA_EN BIT(17) ++#define ESPI_CH0_CTRL_PC_RX_DMA_EN BIT(16) ++#define ESPI_CH0_CTRL_MCYC_RD_DIS_WDT BIT(9) ++#define ESPI_CH0_CTRL_MCYC_WR_DIS_WDT BIT(8) ++#define ESPI_CH0_CTRL_MCYC_RD_DIS BIT(6) ++#define ESPI_CH0_CTRL_MCYC_WR_DIS BIT(4) ++#define ESPI_CH0_CTRL_SW_RDY BIT(1) ++#define ESPI_CH0_STS 0x104 ++#define ESPI_CH0_INT_STS 0x108 ++#define ESPI_CH0_INT_STS_PC_RX_CMPLT BIT(0) ++#define ESPI_CH0_INT_EN 0x10c ++#define ESPI_CH0_INT_EN_PC_RX_CMPLT BIT(0) ++#define ESPI_CH0_PC_RX_DMAL 0x110 ++#define ESPI_CH0_PC_RX_DMAH 0x114 ++#define ESPI_CH0_PC_RX_CTRL 0x118 ++#define ESPI_CH0_PC_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_CH0_PC_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH0_PC_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH0_PC_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH0_PC_RX_DATA 0x11c ++#define ESPI_CH0_PC_TX_DMAL 0x120 ++#define ESPI_CH0_PC_TX_DMAH 0x124 ++#define ESPI_CH0_PC_TX_CTRL 0x128 ++#define ESPI_CH0_PC_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH0_PC_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH0_PC_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH0_PC_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH0_PC_TX_DATA 0x12c ++#define ESPI_CH0_NP_TX_DMAL 0x130 ++#define ESPI_CH0_NP_TX_DMAH 0x134 ++#define ESPI_CH0_NP_TX_CTRL 0x138 ++#define ESPI_CH0_NP_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH0_NP_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH0_NP_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH0_NP_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH0_NP_TX_DATA 0x13c ++#define ESPI_CH0_MCYC0_SADDRL 0x140 ++#define ESPI_CH0_MCYC0_SADDRH 0x144 ++#define ESPI_CH0_MCYC0_TADDRL 0x148 ++#define ESPI_CH0_MCYC0_TADDRH 0x14c ++#define ESPI_CH0_MCYC0_MASKL 0x150 ++#define ESPI_CH0_MCYC0_MASKL_EN BIT(0) ++#define ESPI_CH0_MCYC0_MASKH 0x154 ++#define ESPI_CH0_MCYC1_SADDRL 0x158 ++#define ESPI_CH0_MCYC1_SADDRH 0x15c ++#define ESPI_CH0_MCYC1_TADDRL 0x160 ++#define ESPI_CH0_MCYC1_TADDRH 0x164 ++#define ESPI_CH0_MCYC1_MASKL 0x168 ++#define ESPI_CH0_MCYC1_MASKL_EN BIT(0) ++#define ESPI_CH0_MCYC1_MASKH 0x16c ++#define ESPI_CH0_WPROT0 0x1f8 ++#define ESPI_CH0_WPROT1 0x1fc + -+#define ESPI_INT_STS_VW \ -+ (ESPI_INT_STS_VW_SYSEVT1 | \ -+ ESPI_INT_STS_VW_GPIO | \ -+ ESPI_INT_STS_VW_SYSEVT) ++/* virtual wire channel (ch1) registers */ ++#define ESPI_CH1_CTRL 0x200 ++#define ESPI_CH1_CTRL_GPIO_HW BIT(9) ++#define ESPI_CH1_CTRL_SW_RDY BIT(1) ++#define ESPI_CH1_STS 0x204 ++#define ESPI_CH1_INT_STS 0x208 ++#define ESPI_CH1_INT_STS_GPIO BIT(1) ++#define ESPI_CH1_INT_STS_GPIO_CLR BIT(2) ++#define ESPI_CH1_INT_STS_EVT1 BIT(2) ++#define ESPI_CH1_INT_STS_EVT1_CLR BIT(1) ++#define ESPI_CH1_INT_STS_EVT0 BIT(0) ++#define ESPI_CH1_INT_EN 0x20c ++#define ESPI_CH1_INT_EN_GPIO BIT(1) ++#define ESPI_CH1_INT_EN_SYS_EVT1 BIT(2) ++#define ESPI_CH1_INT_EN_SYS_EVT0 BIT(0) ++#define ESPI_CH1_EVT0 0x210 ++#define ESPI_CH1_EVT0_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_SCI_EVT BIT(24) ++#define ESPI_CH1_EVT0_INT_EN 0x214 ++#define ESPI_CH1_EVT0_INT_EN_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_T0 0x218 ++#define ESPI_CH1_EVT0_INT_T0_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_T1 0x21c ++#define ESPI_CH1_EVT0_INT_T1_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_T2 0x220 ++#define ESPI_CH1_EVT0_INT_T2_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT0_INT_STS 0x224 ++#define ESPI_CH1_EVT0_INT_STS_PLTRSTN BIT(5) ++#define ESPI_CH1_EVT1 0x230 ++#define ESPI_CH1_EVT1_PCH_GENE GENMASK(15, 8) ++#define ESPI_CH1_EVT1_BMC_GENE GENMASK(31, 24) ++#define ESPI_CH1_EVT1_INT_EN 0x234 ++#define ESPI_CH1_EVT1_INT_EN_PCH_GENE GENMASK(15, 8) ++#define ESPI_CH1_EVT1_INT_T0 0x238 ++#define ESPI_CH1_EVT1_INT_T0_PCH_GENE GENMASK(15, 8) ++#define ESPI_CH1_EVT1_INT_T1 0x23c ++#define ESPI_CH1_EVT1_INT_T1_PCH_GENE GENMASK(15, 8) ++#define ESPI_CH1_EVT1_INT_T2 0x240 ++#define ESPI_CH1_EVT1_INT_T2_PCH_GENE GENMASK(15, 8) ++#define ESPI_CH1_EVT1_INT_STS 0x244 ++#define ESPI_CH1_EVT1_INT_STS_PCH_GENE GENMASK(15, 8) ++#define ESPI_CH1_GPIO_VAL0 0x250 ++#define ESPI_CH1_GPIO_VAL1 0x254 ++#define ESPI_CH1_GPIO_DIR0 0x258 ++#define ESPI_CH1_GPIO_DIR1 0x25c ++#define ESPI_CH1_GPIO_RSTSEL0 0x260 ++#define ESPI_CH1_GPIO_RSTSEL1 0x264 ++#define ESPI_CH1_GPIO_GRP 0x268 ++#define ESPI_CH1_GP50_DIR0 0x270 ++#define ESPI_CH1_GP50_DIR1 0x274 ++#define ESPI_CH1_GP50_VAL0 0x278 ++#define ESPI_CH1_GP50_VAL1 0x27c ++#define ESPI_CH1_SW_INT 0x280 ++#define ESPI_CH1_INT_RSTSEL0 0x284 ++#define ESPI_CH1_INT_RSTSEL1 0x288 ++#define ESPI_CH1_WPROT0 0x2f8 ++#define ESPI_CH1_WPROT1 0x2fc + -+#define ESPI_INT_STS_OOB \ -+ (ESPI_INT_STS_OOB_RX_TMOUT | \ -+ ESPI_INT_STS_OOB_TX_ERR | \ -+ ESPI_INT_STS_OOB_TX_ABT | \ -+ ESPI_INT_STS_OOB_RX_ABT | \ -+ ESPI_INT_STS_OOB_TX_CMPLT | \ -+ ESPI_INT_STS_OOB_RX_CMPLT) ++/* out-of-band channel (ch2) registers */ ++#define ESPI_CH2_CTRL 0x300 ++#define ESPI_CH2_CTRL_TX_RST BIT(31) ++#define ESPI_CH2_CTRL_RX_RST BIT(30) ++#define ESPI_CH2_CTRL_TX_DMA_EN BIT(17) ++#define ESPI_CH2_CTRL_RX_DMA_EN BIT(16) ++#define ESPI_CH2_CTRL_SW_RDY BIT(4) ++#define ESPI_CH2_STS 0x304 ++#define ESPI_CH2_INT_STS 0x308 ++#define ESPI_CH2_INT_STS_RX_CMPLT BIT(0) ++#define ESPI_CH2_INT_EN 0x30c ++#define ESPI_CH2_INT_EN_RX_CMPLT BIT(0) ++#define ESPI_CH2_RX_DMAL 0x310 ++#define ESPI_CH2_RX_DMAH 0x314 ++#define ESPI_CH2_RX_CTRL 0x318 ++#define ESPI_CH2_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_CH2_RX_CTRL_PEC BIT(24) ++#define ESPI_CH2_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH2_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH2_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH2_RX_DATA 0x31c ++#define ESPI_CH2_TX_DMAL 0x320 ++#define ESPI_CH2_TX_DMAH 0x324 ++#define ESPI_CH2_TX_CTRL 0x328 ++#define ESPI_CH2_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH2_TX_CTRL_PEC BIT(24) ++#define ESPI_CH2_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH2_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH2_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH2_TX_DATA 0x32c ++#define ESPI_CH2_RX_DESC_EPTR 0x330 ++#define ESPI_CH2_RX_DESC_RPTR 0x334 ++#define ESPI_CH2_RX_DESC_RPTR_UPDATE BIT(31) ++#define ESPI_CH2_RX_DESC_RPTR_RP GENMASK(11, 0) ++#define ESPI_CH2_RX_DESC_WPTR 0x338 ++#define ESPI_CH2_RX_DESC_WPTR_VALID BIT(31) ++#define ESPI_CH2_RX_DESC_WPTR_SP GENMASK(27, 16) ++#define ESPI_CH2_RX_DESC_WPTR_WP GENMASK(11, 0) ++#define ESPI_CH2_RX_DESC_TMOUT 0x33c ++#define ESPI_CH2_TX_DESC_EPTR 0x340 ++#define ESPI_CH2_TX_DESC_RPTR 0x344 ++#define ESPI_CH2_TX_DESC_RPTR_UPT BIT(31) ++#define ESPI_CH2_TX_DESC_WPTR 0x348 ++#define ESPI_CH2_TX_DESC_WPTR_VALID BIT(31) ++#define ESPI_CH2_WPROT0 0x3f8 ++#define ESPI_CH2_WPROT1 0x3fc + -+#define ESPI_INT_STS_FLASH \ -+ (ESPI_INT_STS_FLASH_TX_ERR | \ -+ ESPI_INT_STS_FLASH_TX_ABT | \ -+ ESPI_INT_STS_FLASH_RX_ABT | \ -+ ESPI_INT_STS_FLASH_TX_CMPLT | \ -+ ESPI_INT_STS_FLASH_RX_CMPLT) ++/* flash channel (ch3) registers */ ++#define ESPI_CH3_CTRL 0x400 ++#define ESPI_CH3_CTRL_TX_RST BIT(31) ++#define ESPI_CH3_CTRL_RX_RST BIT(30) ++#define ESPI_CH3_CTRL_TX_DMA_EN BIT(17) ++#define ESPI_CH3_CTRL_RX_DMA_EN BIT(16) ++#define ESPI_CH3_CTRL_EDAF_MODE GENMASK(9, 8) ++#define ESPI_CH3_CTRL_SW_RDY BIT(5) ++#define ESPI_CH3_STS 0x404 ++#define ESPI_CH3_INT_STS 0x408 ++#define ESPI_CH3_INT_STS_RX_CMPLT BIT(0) ++#define ESPI_CH3_INT_EN 0x40c ++#define ESPI_CH3_INT_EN_RX_CMPLT BIT(0) ++#define ESPI_CH3_RX_DMAL 0x410 ++#define ESPI_CH3_RX_DMAH 0x414 ++#define ESPI_CH3_RX_CTRL 0x418 ++#define ESPI_CH3_RX_CTRL_SERV_PEND BIT(31) ++#define ESPI_CH3_RX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH3_RX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH3_RX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH3_RX_DATA 0x41c ++#define ESPI_CH3_TX_DMAL 0x420 ++#define ESPI_CH3_TX_DMAH 0x424 ++#define ESPI_CH3_TX_CTRL 0x428 ++#define ESPI_CH3_TX_CTRL_TRIG_PEND BIT(31) ++#define ESPI_CH3_TX_CTRL_LEN GENMASK(23, 12) ++#define ESPI_CH3_TX_CTRL_TAG GENMASK(11, 8) ++#define ESPI_CH3_TX_CTRL_CYC GENMASK(7, 0) ++#define ESPI_CH3_TX_DATA 0x42c ++#define ESPI_CH3_EDAF_TADDRL 0x430 ++#define ESPI_CH3_EDAF_TADDRH 0x434 ++#define ESPI_CH3_EDAF_MASKL 0x438 ++#define ESPI_CH3_EDAF_MASKH 0x43c ++#define ESPI_CH3_WPROT0 0x4f8 ++#define ESPI_CH3_WPROT1 0x4fc + ++/* eDAF filter registers */ ++#define ESPI_EDAF_FLTR_SADDR0 0x510 ++#define ESPI_EDAF_FLTR_EADDR0 0x514 ++#define ESPI_EDAF_FLTR_SADDR1 0x518 ++#define ESPI_EDAF_FLTR_EADDR1 0x51c ++#define ESPI_EDAF_FLTR_SADDR2 0x520 ++#define ESPI_EDAF_FLTR_EADDR2 0x524 ++#define ESPI_EDAF_FLTR_SADDR3 0x528 ++#define ESPI_EDAF_FLTR_EADDR3 0x52c ++#define ESPI_EDAF_FLTR_SADDR4 0x530 ++#define ESPI_EDAF_FLTR_EADDR4 0x534 ++#define ESPI_EDAF_FLTR_SADDR5 0x538 ++#define ESPI_EDAF_FLTR_EADDR5 0x53c ++#define ESPI_EDAF_FLTR_SADDR6 0x540 ++#define ESPI_EDAF_FLTR_EADDR6 0x544 ++#define ESPI_EDAF_FLTR_SADDR7 0x548 ++#define ESPI_EDAF_FLTR_EADDR7 0x54c ++#define ESPI_EDAF_FLTR_SADDR8 0x550 ++#define ESPI_EDAF_FLTR_EADDR8 0x554 ++#define ESPI_EDAF_FLTR_SADDR9 0x558 ++#define ESPI_EDAF_FLTR_EADDR9 0x55c ++#define ESPI_EDAF_FLTR_SADDR10 0x560 ++#define ESPI_EDAF_FLTR_EADDR10 0x564 ++#define ESPI_EDAF_FLTR_SADDR11 0x568 ++#define ESPI_EDAF_FLTR_EADDR11 0x56c ++#define ESPI_EDAF_FLTR_SADDR12 0x570 ++#define ESPI_EDAF_FLTR_EADDR12 0x574 ++#define ESPI_EDAF_FLTR_SADDR13 0x578 ++#define ESPI_EDAF_FLTR_EADDR13 0x57c ++#define ESPI_EDAF_FLTR_SADDR14 0x580 ++#define ESPI_EDAF_FLTR_EADDR14 0x584 ++#define ESPI_EDAF_FLTR_SADDR15 0x588 ++#define ESPI_EDAF_FLTR_EADDR15 0x58c ++#define ESPI_EDAF_WPROT0 0x5f8 ++#define ESPI_EDAF_WPROT1 0x5fc + -+/* consistent with DTS property "perif-mmbi-instance-size" */ -+enum ast2600_mmbi_instance_size { -+ MMBI_INST_SIZE_8KB = 0x0, -+ MMBI_INST_SIZE_16KB, -+ MMBI_INST_SIZE_32KB, -+ MMBI_INST_SIZE_64KB, -+ MMBI_INST_SIZE_128KB, -+ MMBI_INST_SIZE_256KB, -+ MMBI_INST_SIZE_512KB, -+ MMBI_INST_SIZE_1024KB, -+ MMBI_INST_SIZE_TYPES, -+}; ++void ast2700_espi_pre_init(struct aspeed_espi *espi); ++void ast2700_espi_post_init(struct aspeed_espi *espi); ++void ast2700_espi_deinit(struct aspeed_espi *espi); ++int ast2700_espi_perif_probe(struct aspeed_espi *espi); ++int ast2700_espi_perif_remove(struct aspeed_espi *espi); ++int ast2700_espi_vw_probe(struct aspeed_espi *espi); ++int ast2700_espi_vw_remove(struct aspeed_espi *espi); ++int ast2700_espi_oob_probe(struct aspeed_espi *espi); ++int ast2700_espi_oob_remove(struct aspeed_espi *espi); ++int ast2700_espi_flash_probe(struct aspeed_espi *espi); ++int ast2700_espi_flash_remove(struct aspeed_espi *espi); ++irqreturn_t ast2700_espi_isr(int irq, void *arg); + -+/* function operators */ -+void ast2600_espi_pre_init(struct aspeed_espi *espi); -+void ast2600_espi_post_init(struct aspeed_espi *espi); -+void ast2600_espi_deinit(struct aspeed_espi *espi); -+int ast2600_espi_perif_probe(struct aspeed_espi *espi); -+int ast2600_espi_perif_remove(struct aspeed_espi *espi); -+int ast2600_espi_vw_probe(struct aspeed_espi *espi); -+int ast2600_espi_vw_remove(struct aspeed_espi *espi); -+int ast2600_espi_oob_probe(struct aspeed_espi *espi); -+int ast2600_espi_oob_remove(struct aspeed_espi *espi); -+int ast2600_espi_flash_probe(struct aspeed_espi *espi); -+int ast2600_espi_flash_remove(struct aspeed_espi *espi); -+irqreturn_t ast2600_espi_isr(int irq, void *arg); +#endif -diff --git a/drivers/soc/aspeed/espi/ast2700-espi.c b/drivers/soc/aspeed/espi/ast2700-espi.c ---- a/drivers/soc/aspeed/espi/ast2700-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/ast2700-espi.c 2026-04-08 18:03:48.313705265 +0000 -@@ -0,0 +1,2180 @@ -+// SPDX-License-Identifier: GPL-2.0+ +diff --git a/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.c b/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.c +--- a/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.c 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,1352 @@ ++// SPDX-License-Identifier: GPL-2.0 +/* -+ * Copyright 2023 Aspeed Technology Inc. ++ * Copyright (C) ASPEED Technology Inc. + */ -+#include "linux/err.h" -+#include "linux/fs.h" -+#include "linux/printk.h" -+#include "linux/spinlock.h" -+#include "linux/stddef.h" -+#include "linux/wait.h" -+#include "linux/wordpart.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include +#include -+#include -+#include -+#include ++#include ++#include ++#include ++#include ++#include +#include -+#include -+#include ++#include ++#include +#include +#include -+#include -+#include +#include -+#include -+#include -+#include -+#include -+#include +#include -+#include +#include ++#include ++#include + +#include "ast2700-espi.h" -+#include "aspeed-espi-comm.h" -+#include "aspeed-espi.h" ++#include "ast2700-intel-espi-mmbi.h" ++ ++#define HOST_READ_SCI_STATUS_BIT(x) ((x) & ESPI_CH1_EVT0_SCI_EVT) ++#define HOST_READ_RESET_REQ_BIT(x) ((x) & BIT(0)) ++#define HOST_READ_IF_UP_BIT(x) ((x) & BIT(1)) ++#define HOST_READ_RDY_BIT(x) ((x) & BIT(0)) ++#define HOST_READ_BUF_PTR(x) ((x) & GENMASK(31, 2)) ++ ++/* LPC register for SCI interrupt */ ++#define AST_LPC_SWCR0300 0x180 ++#define LPC_BMC_TRIG_WAKEUP_EVT_STS BIT(8) ++ ++#define AST_LPC_SWCR0704 0x184 ++#define LPC_BMC_TRIG_WAKEUP_EVT_EN BIT(8) ++ ++#define AST_LPC_SWCR0B08 0x188 ++#define LPC_BMC_TRIG_WAKEUP_EVT BIT(6) ++ ++#define AST_LPC_ACPIB3B0 0x1A8 ++#define LPC_BMC_TRIG_SCI_EVT_STS BIT(8) ++ ++#define AST_LPC_ACPIB7B4 0x1AC ++#define LPC_BMC_TRIG_SCI_EVT_EN BIT(8) ++ ++#define MMBI_SIGNATURE_STRING "#MMBI$" ++#define MMBI_BUF_TYPE_VPSCB_V1 0x01 /* Variable Packet Size Circular Buffer */ ++#define MMBI_MEMORY_SIZE SZ_64M ++#define MMBI_MAX_SUPPORT_PROTOCOLS 5 ++#define MMBI_HDR_LEN_MASK GENMASK(23, 2) ++#define MMBI_HDR_PADDING_MASK GENMASK(1, 0) ++#define MMBI_HDR_TYPE_MASK GENMASK(27, 24) ++#define AST2700_ESPI_MMBI_MAX_CHANNELS 8 ++ ++enum ast2700_espi_mmbi_state { /* B_U B_R H_U H_R */ ++ ESPI_MMBI_INIT_IN_PROGRESS = 0x00, /* 0 0 0 0 */ ++ ESPI_MMBI_INIT_COMPLETED = 0x08, /* 1 0 0 0 */ ++ ESPI_MMBI_NORMAL_RUNTIME = 0x0A, /* 1 0 1 0 */ ++ ESPI_MMBI_RESET_REQ_BY_BMC = 0x0E, /* 1 1 1 0 */ ++ ESPI_MMBI_RESET_REQ_BY_HOST = 0x0B, /* 1 0 1 1 */ ++ ESPI_MMBI_RESET_ACKED = 0x0F, /* 1 1 1 1 */ ++ ESPI_MMBI_TRANS_TO_INIT = 0x07, /* 0 1 1 1 */ ++ ESPI_MMBI_INIT_MISMATCH = 0x09, /* 1 0 0 1 */ ++ ESPI_MMBI_POWER_UP_OR_ERROR = 0x10, /* other states */ ++}; ++ ++// addresses below, if not specified, are all virtual addresses ++struct aspeed_mmbi_host_rws { ++ u32 host_rst_req: 1; ++ u32 host_if_up: 1; ++ u32 host_buf_wr_ptr: 30; /* Address that host should write data into */ ++ u32 host_rdy: 1; ++ u32 rsvd: 1; ++ u32 bmc_buf_rd_ptr: 30; /* Address that host should read data from */ ++}; ++ ++struct aspeed_mmbi_host_ros { ++ u32 bmc_rst_req: 1; ++ u32 bmc_if_up: 1; ++ u32 bmc_buf_wr_ptr: 30; /* Address that BMC should write data into */ ++ u32 bmc_rdy: 1; ++ u32 rsvd: 1; ++ u32 host_buf_rd_ptr: 30; /* Address that BMC should read data from */ ++}; ++ ++/* MMBI Variable Packet Size Circular Buffer(VPSCB) v1 descriptor ++ * memory addresses are all offset ++ */ ++struct aspeed_mmbi_vpscb_desc { ++ u32 host_ros_pointer; /* Host ROS Offset */ ++ u32 host_rws_pointer; /* Host RWS Offset */ ++ u8 host_int_type; /* how BMC interrupt host */ ++ u8 host_int_location; /* which channel BMC interrupt host given int_type */ ++ u8 rsvd1[3]; ++ u8 host_int_val; /* what BMC will write to interrupt host */ ++ u8 bmc_int_type; /* how host interrupt BMC */ ++ u32 bmc_int_location; /* which address host should write to interrupt BMC */ ++ u8 rsvd2[4]; ++ u8 bmc_int_val; /* what host will write to interrupt BMC */ ++} __packed; + -+static DEFINE_IDA(ast2700_espi_ida); ++/* memory addresses are all physical */ ++struct aspeed_mmbi_desc { ++ u8 mmbi_signature[6]; ++ u8 mmbi_version; ++ u8 os_use; ++ u32 b2h_buf_base_addr; ++ u32 h2b_buf_base_addr; ++ u32 b2h_buf_len; ++ u32 h2b_buf_len; ++ u8 buf_type; ++ u8 rsvd1[7]; ++ struct aspeed_mmbi_vpscb_desc buf_desc; ++ u8 rsvd2[8]; ++}; + -+#define PERIF_MCYC_ALIGN SZ_64K -+#define PERIF_MMBI_ALIGN SZ_64M ++struct aspeed_mmbi_protocol_dev { ++ struct miscdevice miscdev; ++ struct aspeed_espi_mmbi_channel *channel; ++ u8 protocol_type; ++ bool data_available; ++ /* ++ * If user space application is opened for read, then only process ++ * the data and copy to userspace. Otherwise, discard the command and ++ * process the remaining commands (can be different protocol type) ++ */ ++ bool process_data; ++ wait_queue_head_t queue; ++}; + -+#define OOB_DMA_RPTR_KEY 0x4f4f4253 -+#define OOB_DMA_DESC_NUM 8 -+#define OOB_DMA_DESC_CUSTOM 0x4 ++struct aspeed_espi_mmbi_channel { ++ u8 *desc; ++ u8 *host_ros; ++ u8 *host_rws; ++ u8 *b2h_circular_buf; ++ u32 b2h_circular_buf_size; ++ u8 *h2b_circular_buf; ++ u32 h2b_circular_buf_size; ++ struct ast2700_espi_mmbi *priv; + -+#define FLASH_EDAF_ALIGN SZ_16M ++ struct aspeed_mmbi_protocol_dev protocols[MMBI_MAX_SUPPORT_PROTOCOLS]; + -+/* peripheral channel (CH0) */ -+static int ast2700_espi_mmbi_b2h_mmap(struct file *fp, struct vm_area_struct *vma) ++ enum ast2700_espi_mmbi_state state; ++ ++ bool enabled; ++ int chann_idx; ++}; ++ ++struct ast2700_espi_mmbi { ++ struct regmap *mmbi_map; ++ struct regmap *espi_map; ++ struct regmap *lpc_map; ++ struct device *dev; ++ struct aspeed_espi_mmbi_channel channels[AST2700_ESPI_MMBI_MAX_CHANNELS]; ++ phys_addr_t host_map_addr; ++ dma_addr_t mmbi_phys_addr; ++ u8 *mmbi_virt_addr; ++ resource_size_t mmbi_size; ++ u32 mmbi_instances; ++}; ++ ++struct ast2700_espi_mmbi_hdr { ++ u32 data; ++}; ++ ++static void raise_sci_interrupt(struct aspeed_espi_mmbi_channel *channel) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct aspeed_espi *espi; -+ unsigned long vm_size; -+ pgprot_t prot; ++ u32 val; ++ int retry; ++ struct regmap *lpc_regmap = channel->priv->lpc_map; + -+ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, b2h_mdev); ++ dev_dbg(channel->priv->dev, "Raising SCI interrupt...\n"); + -+ perif = mmbi->perif; ++ regmap_write_bits(lpc_regmap, AST_LPC_ACPIB7B4, LPC_BMC_TRIG_SCI_EVT_EN, ++ LPC_BMC_TRIG_SCI_EVT_EN); + -+ espi = container_of(perif, struct aspeed_espi, perif); -+ vm_size = vma->vm_end - vma->vm_start; -+ prot = vma->vm_page_prot; ++ regmap_write_bits(lpc_regmap, AST_LPC_SWCR0704, ++ LPC_BMC_TRIG_WAKEUP_EVT_EN, ++ LPC_BMC_TRIG_WAKEUP_EVT_EN); + -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (perif->mmbi.inst_size >> 1)) -+ return -EINVAL; ++ regmap_write_bits(lpc_regmap, AST_LPC_SWCR0B08, LPC_BMC_TRIG_WAKEUP_EVT, ++ LPC_BMC_TRIG_WAKEUP_EVT); + -+ prot = pgprot_noncached(prot); ++ /* ++ * Just asserting the SCI VW will trigger the SCI event continuosly. ++ * So BMC must deassert SCI VW to avoid it. ++ * ESPI098[24] reading will confirm Host read data or not. ++ * - 0 means host read the data ++ * - 1 means host not yet read data, so retry with 1us delay. ++ */ ++ retry = 30; ++ while (retry) { ++ if (regmap_read(channel->priv->espi_map, ESPI_CH1_EVT0, &val)) { ++ dev_err(channel->priv->dev, "Unable to read ESPI210\n"); ++ break; ++ } + -+ if (remap_pfn_range(vma, vma->vm_start, -+ (mmbi->b2h_addr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, prot)) -+ return -EAGAIN; ++ if (HOST_READ_SCI_STATUS_BIT(val) == 0) ++ break; + -+ return 0; ++ retry--; ++ dev_dbg(channel->priv->dev, ++ "Host SCI handler not invoked(ESPI210: 0x%0x), so retry(%d) after 1us...\n", ++ val, retry); ++ udelay(1); ++ } ++ ++ regmap_write_bits(lpc_regmap, AST_LPC_SWCR0300, ++ LPC_BMC_TRIG_WAKEUP_EVT_STS, ++ LPC_BMC_TRIG_WAKEUP_EVT_STS); ++ ++ regmap_write_bits(lpc_regmap, AST_LPC_ACPIB3B0, ++ LPC_BMC_TRIG_SCI_EVT_STS, ++ LPC_BMC_TRIG_SCI_EVT_STS); +} + -+static int ast2700_espi_mmbi_h2b_mmap(struct file *fp, struct vm_area_struct *vma) ++static void raise_missing_sci(struct aspeed_espi_mmbi_channel *channel) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct aspeed_espi *espi; -+ unsigned long vm_size; -+ pgprot_t prot; ++ int chan_idx; ++ u32 h_rwp1; ++ u32 b2h_rp, b2h_wp; ++ struct aspeed_mmbi_host_ros *h_ros_ptr; + -+ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); ++ chan_idx = channel->chann_idx; ++ if (regmap_read(channel->priv->mmbi_map, ESPI_MMBI_HOST_READ_RWP1(chan_idx), &h_rwp1)) { ++ dev_err(channel->priv->dev, "Failed to read Host RWP1\n"); ++ return; ++ } ++ if (!HOST_READ_RDY_BIT(h_rwp1)) { ++ // Host is not ready, no point in raising the SCI ++ return; ++ } + -+ perif = mmbi->perif; ++ h_ros_ptr = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ b2h_wp = h_ros_ptr->bmc_buf_wr_ptr << 2; ++ b2h_rp = HOST_READ_BUF_PTR(h_rwp1); + -+ espi = container_of(perif, struct aspeed_espi, perif); ++ if (b2h_wp == b2h_rp) { ++ // Host has read all outstanding SCI data, ++ // Do not raise another SCI. ++ return; ++ } + -+ vm_size = vma->vm_end - vma->vm_start; -+ prot = vma->vm_page_prot; ++ dev_dbg(channel->priv->dev, ++ "Host not read the data yet, so rising SCI interrupt again...\n"); ++ raise_sci_interrupt(channel); ++} + -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > (perif->mmbi.inst_size >> 1)) -+ return -EINVAL; ++static int ast2700_espi_mmbi_parse_hdr(struct aspeed_espi_mmbi_channel *channel, ++ u32 *data_length, u8 *type, ++ u32 *unread_data_len, u8 *padding) ++{ ++ u32 h2b_rp, h2b_wp; ++ struct ast2700_espi_mmbi_hdr header; ++ struct aspeed_mmbi_host_ros *h_ros_ptr; ++ struct aspeed_mmbi_host_rws *h_rws_ptr; + -+ prot = pgprot_noncached(prot); ++ h_ros_ptr = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ h_rws_ptr = (struct aspeed_mmbi_host_rws *)channel->host_rws; + -+ if (remap_pfn_range(vma, vma->vm_start, -+ (mmbi->h2b_addr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, prot)) ++ h2b_wp = h_rws_ptr->host_buf_wr_ptr << 2; ++ h2b_rp = h_ros_ptr->host_buf_rd_ptr << 2; ++ ++ if (h2b_wp >= h2b_rp) ++ *unread_data_len = h2b_wp - h2b_rp; ++ else ++ *unread_data_len = channel->h2b_circular_buf_size - h2b_rp + h2b_wp; ++ ++ if (*unread_data_len < sizeof(struct ast2700_espi_mmbi_hdr)) { ++ dev_dbg(channel->priv->dev, "No data to read(%d -%d)\n", h2b_wp, ++ h2b_rp); + return -EAGAIN; ++ } ++ ++ dev_dbg(channel->priv->dev, "READ MMBI header from: 0x%0lx\n", ++ (ssize_t)(channel->h2b_circular_buf + h2b_rp)); ++ ++ if (h2b_rp + sizeof(struct ast2700_espi_mmbi_hdr) <= channel->h2b_circular_buf_size) { ++ memcpy_fromio((void *)&header, channel->h2b_circular_buf + h2b_rp, ++ sizeof(struct ast2700_espi_mmbi_hdr)); ++ } else { ++ ssize_t chunk_len = channel->h2b_circular_buf_size - h2b_rp; ++ ++ memcpy_fromio((void *)&header, channel->h2b_circular_buf + h2b_rp, ++ chunk_len); ++ memcpy_fromio(((u8 *)&header) + chunk_len, channel->h2b_circular_buf, ++ sizeof(struct ast2700_espi_mmbi_hdr) - chunk_len); ++ } ++ ++ *padding = FIELD_GET(MMBI_HDR_PADDING_MASK, header.data); ++ *data_length = FIELD_GET(MMBI_HDR_LEN_MASK, header.data) * 4 - *padding; ++ *type = FIELD_GET(MMBI_HDR_TYPE_MASK, header.data); + + return 0; +} + -+static __poll_t ast2700_espi_mmbi_h2b_poll(struct file *fp, struct poll_table_struct *pt) ++static void ast2700_espi_mmbi_update_host_ros(struct aspeed_espi_mmbi_channel *channel, ++ u32 read_len, u32 write_len) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; ++ struct aspeed_mmbi_host_ros *h_ros_ptr; ++ u32 rd_ptr, wr_ptr; + -+ mmbi = container_of(fp->private_data, struct aspeed_espi_perif_mmbi, h2b_mdev); ++ h_ros_ptr = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ rd_ptr = h_ros_ptr->host_buf_rd_ptr << 2; ++ wr_ptr = h_ros_ptr->bmc_buf_wr_ptr << 2; + -+ poll_wait(fp, &mmbi->wq, pt); ++ /* Advance the B2H CB offset for next write */ ++ if ((wr_ptr + write_len) <= channel->b2h_circular_buf_size) ++ wr_ptr += write_len; ++ else ++ wr_ptr = wr_ptr + write_len - channel->b2h_circular_buf_size; + -+ if (!mmbi->host_rwp_update) -+ return 0; ++ /* Advance the H2B CB offset till where BMC read data */ ++ if ((rd_ptr + read_len) <= channel->h2b_circular_buf_size) ++ rd_ptr += read_len; ++ else ++ rd_ptr = rd_ptr + read_len - channel->h2b_circular_buf_size; + -+ mmbi->host_rwp_update = false; ++ h_ros_ptr->bmc_buf_wr_ptr = wr_ptr >> 2; ++ h_ros_ptr->host_buf_rd_ptr = rd_ptr >> 2; + -+ return EPOLLIN; ++ dev_dbg(channel->priv->dev, "Updating HROS - h2b_rp: 0x%0x, b2h_wp: 0x%0x\n", ++ rd_ptr, wr_ptr); ++ ++ if (write_len != 0) ++ raise_sci_interrupt(channel); +} + -+static long ast2700_espi_perif_pc_get_rx(struct file *fp, -+ struct aspeed_espi_perif *perif, -+ struct aspeed_espi_ioc *ioc) ++static void ast2700_espi_mmbi_host_ifup_handler(struct aspeed_espi_mmbi_channel *channel) +{ -+ u32 reg, cyc, tag, len; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ unsigned long flags; -+ u32 pkt_len; -+ u8 *pkt; -+ int i, rc; ++ struct aspeed_mmbi_host_ros *hros; + -+ espi = container_of(perif, struct aspeed_espi, perif); -+ -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&perif->pc_rx_mtx)) -+ return -EAGAIN; -+ -+ if (!perif->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; -+ } -+ } else { -+ mutex_lock(&perif->pc_rx_mtx); ++ dev_info(channel->priv->dev, ++ "Handle Host interface up request on MMBI channel(%d), state %d\n", ++ channel->chann_idx, channel->state); + -+ if (!perif->rx_ready) { -+ rc = wait_event_interruptible(perif->wq, perif->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } -+ } ++ switch (channel->state) { ++ case ESPI_MMBI_INIT_COMPLETED: ++ hros = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ hros->bmc_rdy = 1; ++ channel->state = ESPI_MMBI_NORMAL_RUNTIME; ++ break; ++ default: ++ dev_err(channel->priv->dev, ++ "MMBI channel(%d) host state error\n", ++ channel->chann_idx); ++ channel->state = ESPI_MMBI_POWER_UP_OR_ERROR; ++ break; + } ++} + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_CH0_PC_RX_CTRL); -+ cyc = FIELD_GET(ESPI_CH0_PC_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_CH0_PC_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_CH0_PC_RX_CTRL_LEN, reg); ++static void ast2700_espi_mmbi_host_reset_handler(struct aspeed_espi_mmbi_channel *channel) ++{ ++ dev_info(channel->priv->dev, ++ "Handle Host reset request on MMBI channel(%d), state %d\n", ++ channel->chann_idx, channel->state); + -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ switch (cyc) { -+ case ESPI_PERIF_MSG: -+ pkt_len = sizeof(struct espi_perif_msg); -+ break; -+ case ESPI_PERIF_MSG_D: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_perif_msg); -+ break; -+ case ESPI_PERIF_SUC_CMPLT_D_MIDDLE: -+ case ESPI_PERIF_SUC_CMPLT_D_FIRST: -+ case ESPI_PERIF_SUC_CMPLT_D_LAST: -+ case ESPI_PERIF_SUC_CMPLT_D_ONLY: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_perif_cmplt); ++ switch (channel->state) { ++ case ESPI_MMBI_INIT_COMPLETED: ++ memset(channel->host_ros, 0, sizeof(struct aspeed_mmbi_host_ros)); ++ dev_err(channel->priv->dev, ++ "Initialization mismatch on MMBI channel(%d)\n", ++ channel->chann_idx); ++ channel->state = ESPI_MMBI_INIT_MISMATCH; + break; -+ case ESPI_PERIF_SUC_CMPLT: -+ case ESPI_PERIF_UNSUC_CMPLT: -+ pkt_len = sizeof(struct espi_perif_cmplt); ++ case ESPI_MMBI_NORMAL_RUNTIME: ++ /* Host requested for MMBI buffer reset */ ++ memset(channel->host_ros, 0, sizeof(struct aspeed_mmbi_host_ros)); ++ channel->state = ESPI_MMBI_INIT_IN_PROGRESS; ++ ++ dev_info(channel->priv->dev, ++ "Handle Host reset request on MMBI channel(%d)\n", ++ channel->chann_idx); + break; + default: -+ rc = -EFAULT; -+ goto unlock_mtx_n_out; ++ dev_err(channel->priv->dev, ++ "Invalid state(%d) on MMBI channel(%d) to handle Host reset request\n", ++ channel->state, channel->chann_idx); ++ channel->state = ESPI_MMBI_POWER_UP_OR_ERROR; ++ break; + } ++} + -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; ++static void ast2700_espi_mmbi_host_write_handler(struct aspeed_espi_mmbi_channel *channel) ++{ ++ u32 data_length, unread_data_len; ++ u8 type, padding; ++ int rc, i; ++ bool handled = false; ++ ++ dev_info(channel->priv->dev, ++ "Handle Host write on MMBI channel(%d)\n", ++ channel->chann_idx); ++ ++ rc = ast2700_espi_mmbi_parse_hdr(channel, &data_length, &type, &unread_data_len, &padding); ++ if (rc) { ++ dev_dbg(channel->priv->dev, ++ "Failed to parse MMBI header on MMBI channel(%d)\n", ++ channel->chann_idx); ++ return; + } + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; ++ for (i = 0; i < MMBI_MAX_SUPPORT_PROTOCOLS; i++) { ++ if (channel->protocols[i].protocol_type == type) { ++ if (channel->protocols[i].process_data) { ++ handled = true; ++ channel->protocols[i].data_available = true; ++ wake_up_interruptible(&channel->protocols[i].queue); ++ } else { ++ ast2700_espi_mmbi_update_host_ros(channel, ++ data_length + ++ sizeof(struct ast2700_espi_mmbi_hdr) + ++ padding, ++ 0); ++ } ++ } + } + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; ++ if (!handled) { ++ dev_warn(channel->priv->dev, ++ "No handler for protocol type %d on MMBI channel(%d), discard data\n", ++ type, channel->chann_idx); ++ ast2700_espi_mmbi_update_host_ros(channel, data_length + ++ sizeof(struct ast2700_espi_mmbi_hdr) + ++ padding, 0); ++ raise_sci_interrupt(channel); ++ } + -+ if (perif->dma.enable) { -+ memcpy(hdr + 1, perif->dma.pc_rx_virt, pkt_len - sizeof(*hdr)); -+ } else { -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ reg = readl(espi->regs + ESPI_CH0_PC_RX_DATA) & 0xff; ++ dev_dbg(channel->priv->dev, "MMBI header parsed on MMBI channel(%d)\n", ++ channel->chann_idx); ++ dev_dbg(channel->priv->dev, "data len %d unread %d, type %d, padding %d\n", ++ data_length, unread_data_len, type, padding); ++} ++ ++static void ast2700_espi_mmbi_host_rwp0_handler(struct aspeed_espi_mmbi_channel *channel) ++{ ++ int chan_idx; ++ u32 h_rws_cur, h_rws_prev; ++ ++ memcpy_fromio(&h_rws_prev, channel->host_rws, sizeof(h_rws_prev)); ++ chan_idx = channel->chann_idx; ++ if (regmap_read(channel->priv->mmbi_map, ESPI_MMBI_HOST_READ_RWP0(chan_idx), &h_rws_cur)) { ++ dev_err(channel->priv->dev, "Failed to read Host RWP0\n"); ++ return; + } + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; ++ dev_dbg(channel->priv->dev, "Host RWP0 changed on MMBI channel(%d): 0x%08x -> 0x%08x\n", ++ channel->chann_idx, h_rws_prev, h_rws_cur); ++ ++ memcpy_fromio(channel->host_rws, &h_rws_cur, sizeof(h_rws_cur)); ++ if (HOST_READ_RESET_REQ_BIT(h_rws_cur) & ~HOST_READ_RESET_REQ_BIT(h_rws_prev)) ++ ast2700_espi_mmbi_host_reset_handler(channel); ++ else if (HOST_READ_IF_UP_BIT(h_rws_cur) & ~HOST_READ_IF_UP_BIT(h_rws_prev)) ++ ast2700_espi_mmbi_host_ifup_handler(channel); ++ else ++ ast2700_espi_mmbi_host_write_handler(channel); ++} ++ ++static void ast2700_espi_mmbi_host_rwp1_handler(struct aspeed_espi_mmbi_channel *channel) ++{ ++ int chan_idx; ++ u32 h_rws_cur, h_rws_prev; ++ ++ memcpy_fromio(&h_rws_prev, channel->host_rws + 4, sizeof(h_rws_prev)); ++ chan_idx = channel->chann_idx; ++ if (regmap_read(channel->priv->mmbi_map, ESPI_MMBI_HOST_READ_RWP1(chan_idx), &h_rws_cur)) { ++ dev_err(channel->priv->dev, "Failed to read Host RWP1\n"); ++ return; + } + -+ spin_lock_irqsave(&perif->lock, flags); ++ dev_dbg(channel->priv->dev, "Host RWP1 changed on MMBI channel(%d): 0x%08x -> 0x%08x\n", ++ channel->chann_idx, h_rws_prev, h_rws_cur); + -+ writel(ESPI_CH0_PC_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH0_PC_RX_CTRL); -+ perif->rx_ready = 0; ++ memcpy_fromio(channel->host_rws + 4, &h_rws_cur, sizeof(h_rws_cur)); ++} + -+ spin_unlock_irqrestore(&perif->lock, flags); ++static void send_bmc_up_request(struct aspeed_espi_mmbi_channel *channel) ++{ ++ struct aspeed_mmbi_host_ros *hros; + -+ rc = 0; ++ if (!channel->enabled || channel->state != ESPI_MMBI_INIT_IN_PROGRESS) { ++ dev_err(channel->priv->dev, ++ "Invalid state(%d) on MMBI channel(%d) to send BMC interface up request\n", ++ channel->state, channel->chann_idx); ++ return; ++ } + -+free_n_out: -+ vfree(pkt); ++ hros = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ hros->bmc_if_up = 1; ++ channel->state = ESPI_MMBI_INIT_COMPLETED; + -+unlock_mtx_n_out: -+ mutex_unlock(&perif->pc_rx_mtx); ++ dev_info(channel->priv->dev, ++ "Send BMC interface up request on MMBI channel(%d)\n", ++ channel->chann_idx); + -+ return rc; ++ raise_sci_interrupt(channel); +} + -+static long ast2700_espi_perif_pc_put_tx(struct file *fp, -+ struct aspeed_espi_perif *perif, -+ struct aspeed_espi_ioc *ioc) ++static int send_bmc_reset_request(struct aspeed_espi_mmbi_channel *channel) +{ -+ u32 reg, cyc, tag, len; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ u8 *pkt; -+ int i, rc; ++ struct aspeed_mmbi_host_ros *hros; ++ ++ hros = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ if (!channel->enabled) { ++ dev_err(channel->priv->dev, ++ "Invalid state(%d) on MMBI channel(%d) to send BMC reset request\n", ++ channel->enabled, channel->chann_idx); ++ return -EINVAL; ++ } ++ memset(channel->host_ros, 0, sizeof(struct aspeed_mmbi_host_ros)); ++ hros->bmc_rst_req = 1; ++ raise_sci_interrupt(channel); ++ ++ return 0; ++} ++ ++static struct aspeed_mmbi_protocol_dev *file_aspeed_espi_mmbi(struct file *file) ++{ ++ return container_of(file->private_data, struct aspeed_mmbi_protocol_dev, ++ miscdev); ++} ++ ++static int ast2700_espi_mmbi_open(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++static int ast2700_espi_mmbi_release(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++static unsigned int ast2700_espi_mmbi_poll(struct file *filp, poll_table *wait) ++{ ++ struct aspeed_mmbi_protocol_dev *protocol = file_aspeed_espi_mmbi(filp); ++ ++ poll_wait(filp, &protocol->queue, wait); ++ ++ return protocol->data_available ? POLLIN : 0; ++} + -+ espi = container_of(perif, struct aspeed_espi, perif); ++static int get_mmbi_config(struct aspeed_espi_mmbi_channel *channel, void __user *userbuf) ++{ ++ struct aspeed_mmbi_host_ros *h_ros_ptr; ++ struct aspeed_mmbi_host_rws *h_rws_ptr; ++ struct aspeed_mmbi_get_config get_conf; + -+ if (!mutex_trylock(&perif->pc_tx_mtx)) -+ return -EAGAIN; ++ h_ros_ptr = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ h_rws_ptr = (struct aspeed_mmbi_host_rws *)channel->host_rws; + -+ reg = readl(espi->regs + ESPI_CH0_PC_TX_CTRL); -+ if (reg & ESPI_CH0_PC_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_n_out; -+ } ++ get_conf.h_rdy = h_rws_ptr->host_rdy ? true : false; ++ get_conf.h2b_wp = h_rws_ptr->host_buf_wr_ptr << 2; ++ get_conf.b2h_rp = h_rws_ptr->bmc_buf_rd_ptr << 2; ++ get_conf.h2b_rp = h_ros_ptr->host_buf_rd_ptr << 2; ++ get_conf.b2h_wp = h_ros_ptr->bmc_buf_wr_ptr << 2; + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_n_out; ++ if (copy_to_user(userbuf, &get_conf, sizeof(get_conf))) { ++ dev_err(channel->priv->dev, "copy to user failed\n"); ++ return -EFAULT; + } ++ return 0; ++} + -+ hdr = (struct espi_comm_hdr *)pkt; ++static int get_b2h_empty_space(struct aspeed_espi_mmbi_channel *channel, ++ ssize_t *length) ++{ ++ struct aspeed_mmbi_host_ros *h_ros_ptr; ++ struct aspeed_mmbi_host_rws *h_rws_ptr; ++ ssize_t avail_buf_len; + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ h_ros_ptr = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ h_rws_ptr = (struct aspeed_mmbi_host_rws *)channel->host_rws; + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ if (perif->dma.enable) { -+ memcpy(perif->dma.pc_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_CH0_PC_TX_DATA); -+ } ++ if (h_ros_ptr->bmc_buf_wr_ptr >= h_rws_ptr->bmc_buf_rd_ptr) ++ avail_buf_len = channel->b2h_circular_buf_size - (h_ros_ptr->bmc_buf_wr_ptr << 2) + ++ (h_rws_ptr->bmc_buf_rd_ptr << 2); ++ else ++ avail_buf_len = (h_rws_ptr->bmc_buf_rd_ptr << 2) - (h_ros_ptr->bmc_buf_wr_ptr << 2); + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ dev_dbg(channel->priv->dev, "B2H buffer empty space: %ld\n", avail_buf_len); + -+ reg = FIELD_PREP(ESPI_CH0_PC_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_CH0_PC_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_CH0_PC_TX_CTRL_LEN, len) -+ | ESPI_CH0_PC_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_CH0_PC_TX_CTRL); ++ *length = avail_buf_len; + -+ rc = 0; ++ return 0; ++} + -+free_n_out: -+ vfree(pkt); ++static long ast2700_espi_mmbi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ struct aspeed_mmbi_protocol_dev *protocol = file_aspeed_espi_mmbi(filp); ++ struct aspeed_espi_mmbi_channel *channel = protocol->channel; ++ void __user *userbuf = (void __user *)arg; ++ ssize_t length; ++ int ret; + -+unlock_n_out: -+ mutex_unlock(&perif->pc_tx_mtx); ++ switch (cmd) { ++ case ASPEED_MMBI_CTRL_IOCTL_GET_B2H_EMPTY_SPACE: ++ ret = get_b2h_empty_space(channel, &length); ++ if (ret == 0 && copy_to_user(userbuf, &length, sizeof(length))) { ++ dev_err(channel->priv->dev, "copy to user failed\n"); ++ ret = -EFAULT; ++ } ++ break; + -+ return rc; ++ case ASPEED_MMBI_CTRL_IOCTL_SEND_RESET_REQUEST: ++ ret = send_bmc_reset_request(channel); ++ break; ++ ++ case ASPEED_MMBI_CTRL_IOCTL_GET_CONFIG: ++ ret = get_mmbi_config(channel, userbuf); ++ break; ++ ++ default: ++ dev_err(channel->priv->dev, "Command not found\n"); ++ ret = -ENOTTY; ++ } ++ ++ return ret; +} + -+static long ast2700_espi_perif_np_put_tx(struct file *fp, -+ struct aspeed_espi_perif *perif, -+ struct aspeed_espi_ioc *ioc) ++static ssize_t ast2700_espi_mmbi_read(struct file *filp, char *buff, size_t count, ++ loff_t *offp) +{ -+ u32 reg, cyc, tag, len; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ u8 *pkt; -+ int i, rc; -+ -+ espi = container_of(perif, struct aspeed_espi, perif); ++ struct aspeed_mmbi_protocol_dev *protocol = file_aspeed_espi_mmbi(filp); ++ struct aspeed_espi_mmbi_channel *channel = protocol->channel; ++ struct ast2700_espi_mmbi *priv = channel->priv; ++ struct aspeed_mmbi_host_ros *h_ros_ptr; ++ u32 data_length, unread_data_len; ++ u32 buf_rd_offset; ++ u8 type, padding; ++ ssize_t rd_offset, rd_len; ++ ssize_t ret; + -+ if (!mutex_trylock(&perif->np_tx_mtx)) ++ protocol->process_data = true; ++ if (!protocol->data_available && (filp->f_flags & O_NONBLOCK)) { ++ raise_missing_sci(channel); + return -EAGAIN; ++ } + -+ reg = readl(espi->regs + ESPI_CH0_NP_TX_CTRL); -+ if (reg & ESPI_CH0_NP_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_n_out; ++ dev_dbg(priv->dev, "%s: count:%ld, Type: %d\n", __func__, count, ++ protocol->protocol_type); ++ ++ ret = wait_event_interruptible(protocol->queue, protocol->data_available); ++ if (ret == -ERESTARTSYS) { ++ ret = -EINTR; ++ goto err_out; + } + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_n_out; ++ ret = ast2700_espi_mmbi_parse_hdr(channel, &data_length, &type, &unread_data_len, &padding); ++ if (ret) { ++ dev_err(priv->dev, "%s: Failed to parse MMBI header\n", __func__); ++ goto err_out; + } + -+ hdr = (struct espi_comm_hdr *)pkt; ++ dev_dbg(priv->dev, "%s: PKT Length: %d, Type: %d, Unread Data Length: %d, Padding: %d\n", ++ __func__, data_length, type, unread_data_len, padding); + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; ++ if (data_length > count) { ++ dev_err(priv->dev, "%s: Buffer too small, need %d\n", __func__, ++ data_length); ++ ret = -EMSGSIZE; ++ /* discard this packet */ ++ ast2700_espi_mmbi_update_host_ros(channel, data_length + sizeof(struct ast2700_espi_mmbi_hdr) + padding, 0); ++ goto err_out; + } + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ if (perif->dma.enable) { -+ memcpy(perif->dma.np_tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_CH0_NP_TX_DATA); ++ if (type != protocol->protocol_type) { ++ dev_err(priv->dev, "%s: Protocol type mismatch, expected %d\n", ++ __func__, protocol->protocol_type); ++ ret = -EPROTO; ++ goto err_out; + } + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ h_ros_ptr = (struct aspeed_mmbi_host_ros *)channel->host_ros; ++ buf_rd_offset = h_ros_ptr->host_buf_rd_ptr << 2; ++ if (buf_rd_offset + sizeof(struct ast2700_espi_mmbi_hdr) <= channel->h2b_circular_buf_size) ++ rd_offset = buf_rd_offset + sizeof(struct ast2700_espi_mmbi_hdr); ++ else ++ rd_offset = (buf_rd_offset + sizeof(struct ast2700_espi_mmbi_hdr)) - channel->h2b_circular_buf_size; + -+ reg = FIELD_PREP(ESPI_CH0_NP_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_CH0_NP_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_CH0_NP_TX_CTRL_LEN, len) -+ | ESPI_CH0_NP_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_CH0_NP_TX_CTRL); ++ rd_len = data_length; + -+ rc = 0; ++ dev_dbg(priv->dev, "%s: Read from offset: 0x%0lx, length: %ld\n", ++ __func__, rd_offset, rd_len); + -+free_n_out: -+ vfree(pkt); ++ if (unread_data_len < rd_len + sizeof(struct ast2700_espi_mmbi_hdr)) { ++ dev_err(priv->dev, "%s: Not enough data to read, need %ld\n", ++ __func__, rd_len + sizeof(struct ast2700_espi_mmbi_hdr)); ++ ret = -EAGAIN; ++ goto err_out; ++ } + -+unlock_n_out: -+ mutex_unlock(&perif->np_tx_mtx); ++ if ((channel->h2b_circular_buf_size - rd_offset) >= rd_len) { ++ if (copy_to_user(buff, channel->h2b_circular_buf + rd_offset, rd_len)) { ++ dev_err(priv->dev, "%s: copy to user failed\n", __func__); ++ ret = -EFAULT; ++ goto err_out; ++ } ++ } else { ++ ssize_t chunk_len = channel->h2b_circular_buf_size - rd_offset; + -+ return rc; ++ if (copy_to_user(buff, channel->h2b_circular_buf + rd_offset, chunk_len) || ++ copy_to_user(buff + chunk_len, channel->h2b_circular_buf, ++ rd_len - chunk_len)) { ++ dev_err(priv->dev, "%s: copy to user failed\n", __func__); ++ ret = -EFAULT; ++ goto err_out; ++ } ++ } ++ ++ *offp += rd_len; ++ ret = rd_len; ++ ast2700_espi_mmbi_update_host_ros(channel, rd_len + sizeof(struct ast2700_espi_mmbi_hdr) + padding, 0); ++ dev_dbg(priv->dev, "%s: Read %ld bytes from MMBI\n", __func__, rd_len); ++err_out: ++ /* ++ * Raise the missing SCI's by checking pointer for host ++ * read acknowledgment. This will work around the Missing ++ * SCI bug on host side. * ++ */ ++ dev_warn(priv->dev, "%s: Check and raise missing SCI\n", __func__); ++ raise_missing_sci(channel); ++ ++ protocol->data_available = false; ++ ++ ast2700_espi_mmbi_host_write_handler(channel); ++ ++ return ret; +} + -+static long ast2700_espi_perif_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static ssize_t ast2700_espi_mmbi_write(struct file *filp, const char *buffer, size_t len, ++ loff_t *offp) +{ -+ struct aspeed_espi_perif *perif; -+ struct aspeed_espi_ioc ioc; ++ struct aspeed_mmbi_protocol_dev *protocol = file_aspeed_espi_mmbi(filp); ++ struct aspeed_espi_mmbi_channel *channel = protocol->channel; ++ struct ast2700_espi_mmbi *priv = channel->priv; ++ struct aspeed_mmbi_host_rws *h_rws_ptr; ++ struct aspeed_mmbi_host_ros *h_ros_ptr; ++ struct ast2700_espi_mmbi_hdr header; + -+ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); ++ ssize_t wr_offset, avail_buf_len, padding, chunk_len; + -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; ++ h_rws_ptr = (struct aspeed_mmbi_host_rws *)channel->host_rws; ++ h_ros_ptr = (struct aspeed_mmbi_host_ros *)channel->host_ros; + -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) -+ return -EINVAL; ++ dev_dbg(priv->dev, "%s: length:%ld , type: %d\n", __func__, len, ++ protocol->protocol_type); + -+ switch (cmd) { -+ case ASPEED_ESPI_PERIF_PC_GET_RX: -+ return ast2700_espi_perif_pc_get_rx(fp, perif, &ioc); -+ case ASPEED_ESPI_PERIF_PC_PUT_TX: -+ return ast2700_espi_perif_pc_put_tx(fp, perif, &ioc); -+ case ASPEED_ESPI_PERIF_NP_PUT_TX: -+ return ast2700_espi_perif_np_put_tx(fp, perif, &ioc); -+ default: -+ break; -+ }; ++ if (!h_rws_ptr->host_if_up) { ++ dev_err(priv->dev, "%s: Host interface not up\n", __func__); ++ return -EAGAIN; ++ } + -+ return -EINVAL; -+} ++ if (get_b2h_empty_space(channel, &avail_buf_len)) { ++ dev_err(priv->dev, "%s: Failed to get B2H empty space\n", __func__); ++ return -EFAULT; ++ } + -+static int ast2700_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) -+{ -+ struct aspeed_espi_perif *perif; -+ unsigned long vm_size; -+ pgprot_t vm_prot; ++ if ((len + sizeof(struct ast2700_espi_mmbi_hdr)) > avail_buf_len) { ++ dev_err(priv->dev, "%s: No enough space in B2H buffer, need %ld but have %ld\n", ++ __func__, len + sizeof(struct ast2700_espi_mmbi_hdr), avail_buf_len); ++ return -ENOSPC; ++ } ++ padding = (4 - (len % 4)) % 4; ++ header.data = (protocol->protocol_type << 24) | (len & MMBI_HDR_LEN_MASK) | (padding & MMBI_HDR_PADDING_MASK); ++ wr_offset = h_ros_ptr->bmc_buf_wr_ptr << 2; + -+ perif = container_of(fp->private_data, struct aspeed_espi_perif, mdev); -+ if (!perif->mcyc.enable) -+ return -EPERM; ++ if (wr_offset + sizeof(struct ast2700_espi_mmbi_hdr) <= channel->b2h_circular_buf_size) { ++ memcpy_toio(channel->b2h_circular_buf + wr_offset, (void *)&header, ++ sizeof(struct ast2700_espi_mmbi_hdr)); ++ wr_offset += sizeof(struct ast2700_espi_mmbi_hdr); ++ if (wr_offset == channel->b2h_circular_buf_size) ++ wr_offset = 0; ++ } else { ++ chunk_len = channel->b2h_circular_buf_size - wr_offset; + -+ vm_size = vma->vm_end - vma->vm_start; -+ vm_prot = vma->vm_page_prot; ++ memcpy_toio(channel->b2h_circular_buf + wr_offset, (void *)&header, ++ chunk_len); ++ memcpy_toio(channel->b2h_circular_buf, ((u8 *)&header) + chunk_len, ++ sizeof(struct ast2700_espi_mmbi_hdr) - chunk_len); ++ wr_offset = sizeof(struct ast2700_espi_mmbi_hdr) - chunk_len; ++ } + -+ if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > perif->mcyc.size) -+ return -EINVAL; ++ if (wr_offset + len <= channel->b2h_circular_buf_size) { ++ if (copy_from_user(channel->b2h_circular_buf + wr_offset, buffer, len)) { ++ dev_err(priv->dev, "%s: copy from user failed\n", __func__); ++ return -EFAULT; ++ } ++ wr_offset += len; ++ if (wr_offset == channel->b2h_circular_buf_size) ++ wr_offset = 0; ++ } else { ++ chunk_len = channel->b2h_circular_buf_size - wr_offset; + -+ vm_prot = pgprot_noncached(vm_prot); ++ if (copy_from_user(channel->b2h_circular_buf + wr_offset, buffer, chunk_len) || ++ copy_from_user(channel->b2h_circular_buf, ++ buffer + chunk_len, len - chunk_len)) { ++ dev_err(priv->dev, "%s: copy from user failed\n", __func__); ++ return -EFAULT; ++ } ++ wr_offset = len - chunk_len; ++ } + -+ if (remap_pfn_range(vma, vma->vm_start, -+ (perif->mcyc.taddr >> PAGE_SHIFT) + vma->vm_pgoff, -+ vm_size, vm_prot)) -+ return -EAGAIN; ++ *offp += len; ++ ast2700_espi_mmbi_update_host_ros(channel, 0, len + sizeof(struct ast2700_espi_mmbi_hdr) + padding); ++ dev_dbg(priv->dev, "%s: Write %ld bytes to MMBI\n", __func__, len); + -+ return 0; ++ return len; +} + -+static const struct file_operations ast2700_espi_mmbi_b2h_fops = { ++static const struct file_operations ast2700_espi_mmbi_fops = { + .owner = THIS_MODULE, -+ .mmap = ast2700_espi_mmbi_b2h_mmap, ++ .open = ast2700_espi_mmbi_open, ++ .release = ast2700_espi_mmbi_release, ++ .read = ast2700_espi_mmbi_read, ++ .write = ast2700_espi_mmbi_write, ++ .unlocked_ioctl = ast2700_espi_mmbi_ioctl, ++ .poll = ast2700_espi_mmbi_poll +}; + -+static const struct file_operations ast2700_espi_mmbi_h2b_fops = { -+ .owner = THIS_MODULE, -+ .mmap = ast2700_espi_mmbi_h2b_mmap, -+ .poll = ast2700_espi_mmbi_h2b_poll, ++static const struct regmap_config aspeed_espi_mmbi_regmap_cfg = { ++ .name = "mmbi", ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x04C, +}; + -+static const struct file_operations ast2700_espi_perif_fops = { -+ .owner = THIS_MODULE, -+ .mmap = ast2700_espi_perif_mmap, -+ .unlocked_ioctl = ast2700_espi_perif_ioctl, ++static const struct regmap_config aspeed_espi_regmap_cfg = { ++ .name = "espi", ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x7fc, ++}; ++ ++static const struct of_device_id aspeed_espi_mmbi_of_match[] = { ++ { .compatible = "aspeed,ast2700-intel-espi-mmbi" }, ++ { /* sentinel */ } +}; ++MODULE_DEVICE_TABLE(of, aspeed_espi_mmbi_of_match); + -+static irqreturn_t ast2700_espi_perif_mmbi_isr(int irq, void *arg) ++static char *get_protocol_suffix(u8 type) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct aspeed_espi *espi; -+ u32 sts, tmp; -+ u32 *p; -+ int i; ++ switch (type) { ++ case MMBI_PROTOCOL_IPMI: ++ return "ipmi"; ++ case MMBI_PROTOCOL_SEAMLESS: ++ return "seamless"; ++ case MMBI_PROTOCOL_RAS_OFFLOAD: ++ return "ras_offload"; ++ case MMBI_PROTOCOL_MCTP: ++ return "mctp"; ++ case MMBI_PROTOCOL_VENDOR: ++ return "vendor"; ++ default: ++ break; ++ } + -+ espi = (struct aspeed_espi *)arg; ++ return NULL; ++} + -+ perif = &espi->perif; ++static irqreturn_t ast2700_espi_mmbi_irq_handler(int irq, void *arg) ++{ ++ struct ast2700_espi_mmbi *priv = arg; ++ u32 status; ++ int idx; + -+ sts = readl(espi->regs + ESPI_MMBI_INT_STS); -+ if (!sts) -+ return IRQ_NONE; ++ regmap_read(priv->mmbi_map, ESPI_MMBI_INT_STATUS, &status); ++ /* Clear interrupt */ ++ regmap_write(priv->mmbi_map, ESPI_MMBI_INT_STATUS, status); + -+ for (i = 0, tmp = sts; i < perif->mmbi.inst_num; ++i, tmp >>= 2) { -+ if (!(tmp & 0x3)) ++ for (idx = 0; idx < AST2700_ESPI_MMBI_MAX_CHANNELS; idx++) { ++ /* ++ * Host RWP 0: It gets updated after Host reads data and also ++ * when host want to send reset MMBI buffer request. So ++ * Handle reset request and ignore read pointer update. ++ * Host RWP 1: It gets updated when host write data on H2B, ++ * So process the request by invoking corresponding device. ++ */ ++ if (!priv->channels[idx].enabled) + continue; + -+ mmbi = &perif->mmbi.inst[i]; -+ -+ p = (u32 *)mmbi->h2b_virt; -+ p[0] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i)); -+ p[1] = readl(espi->regs + ESPI_MMBI_HOST_RWP(i) + 4); -+ -+ mmbi->host_rwp_update = true; -+ -+ wake_up_interruptible(&mmbi->wq); ++ if ((status >> (idx * 2)) & ESPI_MMBI_HOST_RWS0_INT) { ++ // Host_RWS[0:3] update ++ ast2700_espi_mmbi_host_rwp0_handler(&priv->channels[idx]); ++ } else if (status >> (idx * 2) & ESPI_MMBI_HOST_RWS1_INT) { ++ // Host_RWS[4:7] update ++ ast2700_espi_mmbi_host_rwp1_handler(&priv->channels[idx]); ++ } + } -+ -+ writel(sts, espi->regs + ESPI_MMBI_INT_STS); ++ dev_info(priv->dev, "MMBI IRQ Status: %d\n", status); + + return IRQ_HANDLED; +} + -+static void ast2700_espi_perif_isr(struct aspeed_espi *espi) ++static void aspeed_espi_mmbi_release_rmem(void *d) +{ -+ struct aspeed_espi_perif *perif; -+ unsigned long flags; -+ u32 sts; ++ of_reserved_mem_device_release(d); ++} + -+ perif = &espi->perif; ++static void aspeed_espi_mmbi_dma_finish(struct ast2700_espi_mmbi *priv) ++{ ++ dma_free_coherent(priv->dev, MMBI_MEMORY_SIZE, priv->mmbi_virt_addr, priv->mmbi_phys_addr); ++} + -+ sts = readl(espi->regs + ESPI_CH0_INT_STS); ++static int aspeed_espi_mmbi_dma_init(struct ast2700_espi_mmbi *priv) ++{ ++ int rc; + -+ if (sts & ESPI_CH0_INT_STS_PC_RX_CMPLT) { -+ writel(ESPI_CH0_INT_STS_PC_RX_CMPLT, espi->regs + ESPI_CH0_INT_STS); ++ rc = dma_set_mask_and_coherent(priv->dev, DMA_BIT_MASK(64)); ++ if (rc) { ++ dev_err(priv->dev, "cannot set 64-bits DMA mask\n"); ++ return rc; ++ } + -+ spin_lock_irqsave(&perif->lock, flags); -+ perif->rx_ready = true; -+ spin_unlock_irqrestore(&perif->lock, flags); ++ /* DMA pool initialization */ ++ rc = of_reserved_mem_device_init(priv->dev); ++ if (rc) { ++ dev_err(priv->dev, "device does not have specific DMA pool: %d\n", ++ rc); ++ return rc; ++ } + -+ wake_up_interruptible(&perif->wq); ++ rc = devm_add_action_or_reset(priv->dev, aspeed_espi_mmbi_release_rmem, ++ priv->dev); ++ if (rc) { ++ dev_err(priv->dev, "Failed to add action to release reserved memory: %d\n", ++ rc); ++ return rc; ++ } ++ ++ priv->mmbi_virt_addr = dma_alloc_coherent(priv->dev, MMBI_MEMORY_SIZE, ++ &priv->mmbi_phys_addr, GFP_KERNEL); ++ ++ if (!priv->mmbi_virt_addr) { ++ dev_err(priv->dev, "Failed to allocate DMA memory for MMBI\n"); ++ return -ENOMEM; + } ++ priv->mmbi_size = MMBI_MEMORY_SIZE; ++ ++ return 0; +} + -+static void ast2700_espi_perif_sw_reset(struct aspeed_espi *espi) ++static int aspeed_espi_mmbi_hw_init(struct ast2700_espi_mmbi *priv) +{ -+ struct device *dev; ++ u64 mask; + u32 reg; + -+ dev = espi->dev; ++ /* disable interrupt and clear status */ ++ regmap_write(priv->espi_map, ESPI_CH0_INT_EN, 0x0); ++ regmap_write(priv->espi_map, ESPI_CH0_INT_STS, 0xffffffff); + -+ reg = readl(espi->regs + ESPI_CH0_CTRL); ++ regmap_write(priv->mmbi_map, ESPI_MMBI_INT_ENABLE, 0); ++ regmap_write(priv->mmbi_map, ESPI_MMBI_INT_STATUS, 0xffffffff); ++ ++ /* disable address masking, in here we use memory cycle 0 */ ++ regmap_read(priv->espi_map, ESPI_CH0_MCYC0_MASKL, ®); ++ reg &= ~ESPI_CH0_MCYC0_MASKL_EN; ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_MASKL, reg); ++ ++ regmap_read(priv->espi_map, ESPI_CH0_MCYC0_MASKL, ®); ++ reg &= ~ESPI_CH0_MCYC0_MASKL_EN; ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_MASKL, reg); ++ ++ /* disable memory w/r and clear eSPI CH0 queues */ ++ regmap_read(priv->espi_map, ESPI_CH0_CTRL, ®); ++ reg |= (ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); + reg &= ~(ESPI_CH0_CTRL_NP_TX_RST + | ESPI_CH0_CTRL_NP_RX_RST + | ESPI_CH0_CTRL_PC_TX_RST @@ -80350,7 +83088,7 @@ diff --git a/drivers/soc/aspeed/espi/ast2700-espi.c b/drivers/soc/aspeed/espi/as + | ESPI_CH0_CTRL_PC_TX_DMA_EN + | ESPI_CH0_CTRL_PC_RX_DMA_EN + | ESPI_CH0_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ regmap_write(priv->espi_map, ESPI_CH0_CTRL, reg); + + udelay(1); + @@ -80358,2158 +83096,3100 @@ diff --git a/drivers/soc/aspeed/espi/ast2700-espi.c b/drivers/soc/aspeed/espi/as + | ESPI_CH0_CTRL_NP_RX_RST + | ESPI_CH0_CTRL_PC_TX_RST + | ESPI_CH0_CTRL_PC_RX_RST); -+ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ regmap_write(priv->espi_map, ESPI_CH0_CTRL, reg); ++ ++ /* disable espi-mmbi controller before configuring espi address */ ++ regmap_read(priv->mmbi_map, ESPI_MMBI_CTRL, ®); ++ reg &= ~ESPI_MMBI_CTRL_EN; ++ regmap_write(priv->mmbi_map, ESPI_MMBI_CTRL, reg); ++ ++ mask = ~(priv->mmbi_size - 1); ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_MASKH, mask >> 32); ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_MASKL, mask & 0xffffffff); ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_SADDRH, priv->host_map_addr >> 32); ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_SADDRL, priv->host_map_addr & 0xffffffff); ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_TADDRH, priv->mmbi_phys_addr >> 32); ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_TADDRL, priv->mmbi_phys_addr & 0xffffffff); ++ ++ /* enable espi-mmbi controller and configure instances */ ++ reg = FIELD_PREP(ESPI_MMBI_CTRL_INST_NUM, ilog2(priv->mmbi_instances)) | ESPI_MMBI_CTRL_EN; ++ regmap_write(priv->mmbi_map, ESPI_MMBI_CTRL, reg); ++ ++ /* enable eSPI memory cycle 0 */ ++ regmap_read(priv->espi_map, ESPI_CH0_MCYC0_MASKL, ®); ++ reg |= ESPI_CH0_MCYC0_MASKL_EN; ++ regmap_write(priv->espi_map, ESPI_CH0_MCYC0_MASKL, reg); ++ ++ /* keep Peripheral Channel Mem Read/Write after WDT reset */ ++ regmap_read(priv->espi_map, ESPI_CH0_CTRL, ®); ++ reg &= ~(ESPI_CH0_CTRL_MCYC_RD_DIS_WDT | ESPI_CH0_CTRL_MCYC_WR_DIS_WDT); ++ regmap_write(priv->espi_map, ESPI_CH0_CTRL, reg); ++ ++ return 0; +} + -+static void ast2700_espi_perif_reset(struct aspeed_espi *espi) ++static struct aspeed_mmbi_desc aspeed_espi_mmbi_desc_init(struct aspeed_espi_mmbi_channel channel) +{ -+ struct aspeed_espi_perif *perif; -+ struct device *dev; -+ u64 mask; -+ u32 reg; -+ -+ dev = espi->dev; -+ -+ perif = &espi->perif; -+ -+ writel(0x0, espi->regs + ESPI_CH0_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_CH0_INT_STS); -+ -+ writel(0x0, espi->regs + ESPI_MMBI_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_MMBI_INT_STS); ++ struct aspeed_mmbi_desc desc; + -+ reg = readl(espi->regs + ESPI_CH0_CTRL); -+ reg &= ~(ESPI_CH0_CTRL_MCYC_RD_DIS_WDT | ESPI_CH0_CTRL_MCYC_WR_DIS_WDT); -+ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ memset(&desc, 0, sizeof(desc)); ++ memcpy_fromio(desc.mmbi_signature, MMBI_SIGNATURE_STRING, sizeof(desc.mmbi_signature)); ++ desc.mmbi_version = 1; ++ desc.os_use = 1; ++ desc.buf_type = MMBI_BUF_TYPE_VPSCB_V1; + -+ reg = readl(espi->regs + ESPI_CH0_MCYC0_MASKL); -+ reg &= ~ESPI_CH0_MCYC0_MASKL_EN; -+ writel(reg, espi->regs + ESPI_CH0_MCYC0_MASKL); ++ desc.buf_desc.host_ros_pointer = (channel.host_ros - channel.desc) >> 3; ++ desc.buf_desc.host_rws_pointer = (channel.host_rws - channel.desc) >> 3; + -+ reg = readl(espi->regs + ESPI_CH0_MCYC1_MASKL); -+ reg &= ~ESPI_CH0_MCYC1_MASKL_EN; -+ writel(reg, espi->regs + ESPI_CH0_MCYC1_MASKL); ++ desc.buf_desc.host_int_type = 3; ++ desc.buf_desc.host_int_location = 0x06; ++ desc.buf_desc.host_int_val = 0x11; ++ desc.buf_desc.bmc_int_type = 0; ++ desc.buf_desc.bmc_int_location = 0x00; ++ desc.buf_desc.bmc_int_val = 0x00; + -+ reg = readl(espi->regs + ESPI_CH0_CTRL); -+ reg |= (ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); -+ reg &= ~(ESPI_CH0_CTRL_NP_TX_DMA_EN -+ | ESPI_CH0_CTRL_PC_TX_DMA_EN -+ | ESPI_CH0_CTRL_PC_RX_DMA_EN -+ | ESPI_CH0_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ desc.b2h_buf_base_addr = (u32)(channel.b2h_circular_buf - channel.desc) >> 3; ++ desc.h2b_buf_base_addr = (u32)(channel.h2b_circular_buf - channel.desc) >> 3; ++ desc.b2h_buf_len = channel.b2h_circular_buf_size; ++ desc.h2b_buf_len = channel.h2b_circular_buf_size; + -+ if (perif->mmbi.enable) { -+ reg = readl(espi->regs + ESPI_MMBI_CTRL); -+ reg &= ~ESPI_MMBI_CTRL_EN; -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ return desc; ++} + -+ mask = ~(perif->mmbi.size - 1); -+ writel(upper_32_bits(mask), espi->regs + ESPI_CH0_MCYC0_MASKH); -+ writel(mask & 0xffffffff, espi->regs + ESPI_CH0_MCYC0_MASKL); -+ writel(upper_32_bits(perif->mmbi.saddr), espi->regs + ESPI_CH0_MCYC0_SADDRH); -+ writel((perif->mmbi.saddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC0_SADDRL); -+ writel(upper_32_bits(perif->mmbi.taddr), espi->regs + ESPI_CH0_MCYC0_TADDRH); -+ writel((perif->mmbi.taddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC0_TADDRL); ++static int aspeed_espi_mmbi_chann_init(struct ast2700_espi_mmbi *priv, struct device_node *node, int seq, u8 idx) ++{ ++ struct aspeed_mmbi_desc desc; ++ int rc, i; ++ u8 protocol_num; ++ u8 protocol_supported[MMBI_MAX_SUPPORT_PROTOCOLS]; ++ u32 b2h_offset, h2b_offset; ++ u32 instance_size; + -+ writel((0x1 << (perif->mmbi.inst_num * 2)) - 1, espi->regs + ESPI_MMBI_INT_EN); ++ memset(&priv->channels[idx], 0, sizeof(struct aspeed_espi_mmbi_channel)); + -+ reg = FIELD_PREP(ESPI_MMBI_CTRL_INST_NUM, count_trailing_zeros(perif->mmbi.inst_num)) -+ | ESPI_MMBI_CTRL_EN; -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ /* configure virtual memory addresses ++ * In AST2700, memory layout is divided into B2H and H2B, ++ * B2H allocate top half and H2B allocate bottom half. ++ * For multiple instances, each instance is allocated separately in B2H and H2B. ++ * MMBI Capability Descriptor is at the top of B2H ++ */ ++ instance_size = MMBI_MEMORY_SIZE / 2 / priv->mmbi_instances; ++ b2h_offset = idx * instance_size; ++ h2b_offset = MMBI_MEMORY_SIZE / 2 + idx * instance_size; ++ ++ priv->channels[idx].desc = (u8 *)priv->mmbi_virt_addr + b2h_offset; ++ priv->channels[idx].host_ros = priv->channels[idx].desc + sizeof(struct aspeed_mmbi_desc); ++ priv->channels[idx].b2h_circular_buf = priv->channels[idx].host_ros + sizeof(struct aspeed_mmbi_host_ros); ++ priv->channels[idx].host_rws = (u8 *)priv->mmbi_virt_addr + h2b_offset; ++ priv->channels[idx].h2b_circular_buf = priv->channels[idx].host_rws + sizeof(struct aspeed_mmbi_host_rws); ++ ++ /* make sure buffer starts 4byte-aligned */ ++ priv->channels[idx].b2h_circular_buf = (u8 *)ALIGN((uintptr_t)priv->channels[idx].b2h_circular_buf, 4); ++ priv->channels[idx].h2b_circular_buf = (u8 *)ALIGN((uintptr_t)priv->channels[idx].h2b_circular_buf, 4); ++ ++ priv->channels[idx].b2h_circular_buf_size = instance_size - sizeof(struct aspeed_mmbi_desc) - ++ sizeof(struct aspeed_mmbi_host_ros); ++ priv->channels[idx].h2b_circular_buf_size = instance_size - sizeof(struct aspeed_mmbi_host_rws); ++ ++ memset(priv->channels[idx].host_ros, 0, sizeof(struct aspeed_mmbi_host_ros)); ++ memset(priv->channels[idx].host_rws, 0, sizeof(struct aspeed_mmbi_host_rws)); ++ priv->channels[idx].state = ESPI_MMBI_INIT_IN_PROGRESS; ++ ++ dev_dbg(priv->dev, "MMBI: Channel(%d) desc: 0x%llx, host_ros: 0x%llx, host_rws: 0x%llx\n", ++ idx, (unsigned long long)priv->channels[idx].desc, (unsigned long long)priv->channels[idx].host_ros, ++ (unsigned long long)priv->channels[idx].host_rws); ++ dev_dbg(priv->dev, "MMBI: Channel(%d) b2h_circular_buf: 0x%llx, h2b_circular_buf: 0x%llx\n", ++ idx, (unsigned long long)priv->channels[idx].b2h_circular_buf, ++ (unsigned long long)priv->channels[idx].h2b_circular_buf); ++ ++ desc = aspeed_espi_mmbi_desc_init(priv->channels[idx]); ++ memcpy_fromio(priv->channels[idx].desc, &desc, sizeof(desc)); ++ ++ priv->channels[idx].enabled = true; ++ priv->channels[idx].chann_idx = idx; ++ ++ if (!node) { ++ dev_err(priv->dev, "Fail to read instance"); ++ return -ENODEV; ++ } + -+ reg = readl(espi->regs + ESPI_CH0_MCYC0_MASKL) | ESPI_CH0_MCYC0_MASKL_EN; -+ writel(reg, espi->regs + ESPI_CH0_MCYC0_MASKL); ++ protocol_num = of_property_count_u8_elems(node, "protocols"); ++ if (protocol_num < 0 || protocol_num > MMBI_MAX_SUPPORT_PROTOCOLS) { ++ dev_err(priv->dev, "Invalid number of protocols: %d\n", protocol_num); ++ return -EINVAL; ++ } + -+ reg = readl(espi->regs + ESPI_CH0_CTRL); -+ reg &= ~(ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); -+ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ rc = of_property_read_u8_array(node, "protocols", protocol_supported, protocol_num); ++ if (rc) { ++ dev_err(priv->dev, "Failed to read protocols for channel %d: %d\n", idx, rc); ++ return rc; + } + -+ if (perif->mcyc.enable) { -+ mask = ~(perif->mcyc.size - 1); -+ writel(upper_32_bits(mask), espi->regs + ESPI_CH0_MCYC1_MASKH); -+ writel(mask & 0xffffffff, espi->regs + ESPI_CH0_MCYC1_MASKL); -+ writel(upper_32_bits(perif->mcyc.saddr), espi->regs + ESPI_CH0_MCYC1_SADDRH); -+ writel((perif->mcyc.saddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC1_SADDRL); -+ writel(upper_32_bits(perif->mcyc.taddr), espi->regs + ESPI_CH0_MCYC1_TADDRH); -+ writel((perif->mcyc.taddr & 0xffffffff), espi->regs + ESPI_CH0_MCYC1_TADDRL); ++ memset(priv->channels[idx].protocols, 0, sizeof(priv->channels[idx].protocols)); ++ for (i = 0; i < protocol_num; i++) { ++ u8 proto; ++ char *proto_name; + -+ reg = readl(espi->regs + ESPI_CH0_MCYC1_MASKL) | ESPI_CH0_MCYC1_MASKL_EN; -+ writel(reg, espi->regs + ESPI_CH0_MCYC1_MASKL); ++ proto = protocol_supported[i]; ++ proto_name = get_protocol_suffix(proto); + -+ reg = readl(espi->regs + ESPI_CH0_CTRL); -+ reg &= ~(ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); -+ writel(reg, espi->regs + ESPI_CH0_CTRL); -+ } ++ if (!proto_name) { ++ dev_err(priv->dev, "Unsupported protocol type: %d\n", proto); ++ continue; ++ } + -+ if (perif->dma.enable) { -+ writel(upper_32_bits(perif->dma.np_tx_addr), espi->regs + ESPI_CH0_NP_TX_DMAH); -+ writel((perif->dma.np_tx_addr & 0xffffffff), espi->regs + ESPI_CH0_NP_TX_DMAL); -+ writel(upper_32_bits(perif->dma.pc_tx_addr), espi->regs + ESPI_CH0_PC_TX_DMAH); -+ writel((perif->dma.pc_tx_addr & 0xffffffff), espi->regs + ESPI_CH0_PC_TX_DMAL); -+ writel(upper_32_bits(perif->dma.pc_rx_addr), espi->regs + ESPI_CH0_PC_RX_DMAH); -+ writel((perif->dma.pc_rx_addr & 0xffffffff), espi->regs + ESPI_CH0_PC_RX_DMAL); ++ priv->channels[idx].protocols[i].miscdev.name = ++ devm_kasprintf(priv->dev, GFP_KERNEL, "%s%d-%s%d-%s", "espi-mmbi", seq, "chann", idx, proto_name); ++ priv->channels[idx].protocols[i].miscdev.minor = MISC_DYNAMIC_MINOR; ++ priv->channels[idx].protocols[i].miscdev.fops = &ast2700_espi_mmbi_fops; /* to be implemented */ ++ priv->channels[idx].protocols[i].miscdev.parent = priv->dev; ++ rc = misc_register(&priv->channels[idx].protocols[i].miscdev); ++ if (rc) { ++ dev_err(priv->dev, "Failed to register miscdev for protocol %s: %d\n", ++ proto_name, rc); ++ continue; ++ } ++ priv->channels[idx].protocols[i].protocol_type = proto; ++ priv->channels[idx].protocols[i].channel = &priv->channels[idx]; ++ priv->channels[idx].protocols[i].data_available = false; ++ priv->channels[idx].protocols[i].process_data = false; + -+ reg = readl(espi->regs + ESPI_CH0_CTRL) -+ | ESPI_CH0_CTRL_NP_TX_DMA_EN -+ | ESPI_CH0_CTRL_PC_TX_DMA_EN -+ | ESPI_CH0_CTRL_PC_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CH0_CTRL); -+ } -+ if (perif->rtc_enable) { -+ reg = readl(espi->regs + ESPI_CAP_GEN) -+ | ESPI_CAP_GEN_RTC_SUP; -+ writel(reg, espi->regs + ESPI_CAP_GEN); ++ init_waitqueue_head(&priv->channels[idx].protocols[i].queue); + } ++ priv->channels[idx].priv = priv; + -+ writel(ESPI_CH0_INT_EN_PC_RX_CMPLT, espi->regs + ESPI_CH0_INT_EN); ++ dev_info(priv->dev, "MMBI: Channel(%d) protocols: %*ph\n", idx, ++ protocol_num, protocol_supported); + -+ reg = readl(espi->regs + ESPI_CH0_CTRL) | ESPI_CH0_CTRL_SW_RDY; -+ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ return 0; +} + -+int ast2700_espi_perif_probe(struct aspeed_espi *espi) ++static int aspeed_espi_mmbi_probe(struct platform_device *pdev) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct platform_device *pdev; -+ struct device_node *np; -+ struct resource res; -+ struct device *dev; -+ int i, rc; -+ u64 temp; -+ -+ dev = espi->dev; -+ -+ perif = &espi->perif; -+ -+ init_waitqueue_head(&perif->wq); -+ -+ spin_lock_init(&perif->lock); -+ -+ mutex_init(&perif->np_tx_mtx); -+ mutex_init(&perif->pc_tx_mtx); -+ mutex_init(&perif->pc_rx_mtx); -+ -+ perif->mmbi.enable = of_property_read_bool(dev->of_node, "perif-mmbi-enable"); -+ if (perif->mmbi.enable) { -+ pdev = container_of(dev, struct platform_device, dev); -+ -+ perif->mmbi.irq = platform_get_irq(pdev, 1); -+ if (perif->mmbi.irq < 0) { -+ dev_err(dev, "cannot get MMBI IRQ number\n"); -+ return -ENODEV; -+ } ++ const struct of_device_id *dev_id; ++ struct ast2700_espi_mmbi *priv; ++ struct aspeed_espi *espi; ++ struct device_node *node; ++ void __iomem *regs; ++ int rc, seq, irq; ++ u32 i, mmbi_irq_en; + -+ rc = of_property_read_u64(dev->of_node, "perif-mmbi-src-addr", &temp); -+ if (rc || !IS_ALIGNED(temp, PERIF_MMBI_ALIGN)) { -+ dev_err(dev, "cannot get 64MB-aligned MMBI host address\n"); -+ return -ENODEV; -+ } -+ perif->mmbi.saddr = temp; -+ rc = of_property_read_u32(dev->of_node, "perif-mmbi-instance-num", &perif->mmbi.inst_num); -+ if (rc || -+ perif->mmbi.inst_num == 0 || -+ perif->mmbi.inst_num > PERIF_MMBI_INST_NUM || -+ (perif->mmbi.inst_num & (perif->mmbi.inst_num - 1))) { -+ dev_err(dev, "cannot get valid MMBI instance number, expect 1/2/4/8\n"); -+ return -EINVAL; -+ } ++ dev_info(&pdev->dev, "ASPEED eSPI MMBI driver probe\n"); ++ if (!pdev->dev.parent) { ++ dev_err(&pdev->dev, "Device has no parent\n"); ++ return -ENODEV; ++ } + -+ np = of_parse_phandle(dev->of_node, "perif-mmbi-tgt-memory", 0); -+ if (!np || of_address_to_resource(np, 0, &res)) { -+ dev_err(dev, "cannot get MMBI memory region\n"); -+ return -ENODEV; -+ } ++ espi = dev_get_drvdata(pdev->dev.parent); ++ if (!espi) { ++ dev_err(&pdev->dev, "Failed to get eSPI controller data\n"); ++ return -EPROBE_DEFER; ++ } + -+ of_node_put(np); ++ dev_id = of_match_device(aspeed_espi_mmbi_of_match, &pdev->dev); ++ if (!dev_id) { ++ rc = PTR_ERR(dev_id); ++ goto out; ++ } + -+ perif->mmbi.taddr = res.start; -+ perif->mmbi.size = resource_size(&res); -+ perif->mmbi.inst_size = perif->mmbi.size / perif->mmbi.inst_num; -+ if (!IS_ALIGNED(perif->mmbi.taddr, PERIF_MMBI_ALIGN) || -+ !IS_ALIGNED(perif->mmbi.size, PERIF_MMBI_ALIGN)) { -+ dev_err(dev, "cannot get 64MB-aligned MMBI address/size\n"); -+ return -EINVAL; -+ } ++ priv = devm_kzalloc(&pdev->dev, sizeof(struct ast2700_espi_mmbi), GFP_KERNEL); ++ if (!priv) { ++ rc = -ENOMEM; ++ goto out; ++ } + -+ perif->mmbi.virt = devm_ioremap_resource(dev, &res); -+ if (!perif->mmbi.virt) { -+ dev_err(dev, "cannot map MMBI memory region\n"); -+ return -ENOMEM; -+ } ++ priv->dev = &pdev->dev; + -+ memset_io(perif->mmbi.virt, 0, perif->mmbi.size); -+ perif->mmbi.mmbi_isr = ast2700_espi_perif_mmbi_isr; ++ regs = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(regs)) { ++ rc = PTR_ERR(regs); ++ goto out; ++ } + -+ for (i = 0; i < perif->mmbi.inst_num; ++i) { -+ mmbi = &perif->mmbi.inst[i]; ++ /* MMBI register map */ ++ priv->mmbi_map = devm_regmap_init_mmio(&pdev->dev, regs, &aspeed_espi_mmbi_regmap_cfg); ++ if (IS_ERR(priv->mmbi_map)) { ++ rc = PTR_ERR(priv->mmbi_map); ++ goto out; ++ } + -+ init_waitqueue_head(&mmbi->wq); ++ /* ESPI register map */ ++ priv->espi_map = devm_regmap_init_mmio(priv->dev, espi->regs, ++ &aspeed_espi_regmap_cfg); ++ if (IS_ERR(priv->espi_map)) { ++ rc = PTR_ERR(priv->espi_map); ++ goto out; ++ } + -+ mmbi->perif = perif; -+ mmbi->host_rwp_update = false; ++ /* LPC register map */ ++ priv->lpc_map = syscon_regmap_lookup_by_phandle(priv->dev->of_node, ++ "aspeed,lpc"); ++ if (IS_ERR(priv->lpc_map)) { ++ rc = PTR_ERR(priv->lpc_map); ++ goto out; ++ } + -+ mmbi->b2h_virt = perif->mmbi.virt + ((perif->mmbi.inst_size >> 1) * i); -+ mmbi->b2h_addr = perif->mmbi.taddr + ((perif->mmbi.inst_size >> 1) * i); -+ mmbi->b2h_mdev.parent = dev; -+ mmbi->b2h_mdev.minor = MISC_DYNAMIC_MINOR; -+ mmbi->b2h_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi%d-b2h%d", -+ DEVICE_NAME, espi->dev_id, i); -+ mmbi->b2h_mdev.fops = &ast2700_espi_mmbi_b2h_fops; -+ rc = misc_register(&mmbi->b2h_mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", mmbi->b2h_mdev.name); -+ return rc; -+ } ++ rc = aspeed_espi_mmbi_dma_init(priv); ++ if (rc) ++ goto out_dma; + -+ mmbi->h2b_virt = perif->mmbi.virt + ((perif->mmbi.inst_size >> 1) * (i + perif->mmbi.inst_num)); -+ mmbi->h2b_addr = perif->mmbi.taddr + ((perif->mmbi.inst_size >> 1) * (i + perif->mmbi.inst_num)); -+ mmbi->h2b_mdev.parent = dev; -+ mmbi->h2b_mdev.minor = MISC_DYNAMIC_MINOR; -+ mmbi->h2b_mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-mmbi%d-h2b%d", -+ DEVICE_NAME, espi->dev_id, i); -+ mmbi->h2b_mdev.fops = &ast2700_espi_mmbi_h2b_fops; -+ rc = misc_register(&mmbi->h2b_mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", mmbi->h2b_mdev.name); -+ return rc; -+ } -+ } ++ rc = of_property_read_u64(priv->dev->of_node, "host-map-addr", &priv->host_map_addr); ++ if (rc) { ++ dev_err(priv->dev, "Failed to read host-map-addr property\n"); ++ goto out_dma; + } + -+ perif->mcyc.enable = of_property_read_bool(dev->of_node, "perif-mcyc-enable"); -+ if (perif->mcyc.enable) { -+ rc = of_property_read_u64(dev->of_node, "perif-mcyc-src-addr", &temp); -+ if (rc || !IS_ALIGNED(temp, PERIF_MCYC_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned memory cycle host address\n"); -+ return -ENODEV; -+ } -+ perif->mcyc.saddr = temp; ++ rc = of_property_read_u32(priv->dev->of_node, "instances", &priv->mmbi_instances); ++ if (rc) { ++ dev_err(priv->dev, "Failed to read mmbi instances property, set to 1\n"); ++ priv->mmbi_instances = 1; ++ } + -+ rc = of_property_read_u64(dev->of_node, "perif-mcyc-size", &temp); -+ if (rc || !IS_ALIGNED(temp, PERIF_MCYC_ALIGN)) { -+ dev_err(dev, "cannot get 64KB-aligned memory cycle size\n"); -+ return -EINVAL; -+ } -+ perif->mcyc.size = temp; ++ /* check priv->mmbi_instances is less than 8 and is power of 2 */ ++ if (priv->mmbi_instances > 8 || !is_power_of_2(priv->mmbi_instances)) { ++ dev_err(priv->dev, "Invalid number of MMBI instances: %d\n", priv->mmbi_instances); ++ rc = -EINVAL; ++ goto out_dma; ++ } + -+ np = of_parse_phandle(dev->of_node, "memory-region", 0); -+ if (np) { -+ of_reserved_mem_device_init(dev); -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); -+ if (rc) { -+ dev_err(dev, "Failed to mask DMA.\n"); -+ return -ENODEV; -+ } -+ } ++ dev_info(priv->dev, "MMBI: HostAddr:0x%llx, Physical addr:0x%llx, instances: %d\n", ++ priv->host_map_addr, priv->mmbi_phys_addr, priv->mmbi_instances); + -+ perif->mcyc.virt = dmam_alloc_coherent(dev, perif->mcyc.size, -+ &perif->mcyc.taddr, GFP_KERNEL); -+ if (!perif->mcyc.virt) { -+ dev_err(dev, "cannot allocate memory cycle\n"); -+ return -ENOMEM; -+ } ++ rc = aspeed_espi_mmbi_hw_init(priv); ++ if (rc) { ++ dev_err(priv->dev, "Failed to initialize MMBI hardware: %d\n", rc); ++ goto out_dma; + } + -+ perif->dma.enable = of_property_read_bool(dev->of_node, "perif-dma-mode"); -+ if (perif->dma.enable) { -+ perif->dma.pc_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.pc_tx_addr, GFP_KERNEL); -+ if (!perif->dma.pc_tx_virt) { -+ dev_err(dev, "cannot allocate posted TX DMA buffer\n"); -+ return -ENOMEM; -+ } -+ -+ perif->dma.pc_rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.pc_rx_addr, GFP_KERNEL); -+ if (!perif->dma.pc_rx_virt) { -+ dev_err(dev, "cannot allocate posted RX DMA buffer\n"); -+ return -ENOMEM; -+ } ++ seq = of_alias_get_id(priv->dev->of_node, "espi-mmbi"); ++ if (seq < 0) { ++ dev_err(priv->dev, "Failed to get device sequence id\n"); ++ seq = 0; ++ } + -+ perif->dma.np_tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, -+ &perif->dma.np_tx_addr, GFP_KERNEL); -+ if (!perif->dma.np_tx_virt) { -+ dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); -+ return -ENOMEM; -+ } ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ rc = irq; ++ goto out_dma; + } -+ perif->rtc_enable = of_property_read_bool(dev->of_node, "perif-rtc-enable"); + -+ perif->mdev.parent = dev; -+ perif->mdev.minor = MISC_DYNAMIC_MINOR; -+ perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-peripheral%d", DEVICE_NAME, espi->dev_id); -+ perif->mdev.fops = &ast2700_espi_perif_fops; -+ rc = misc_register(&perif->mdev); ++ rc = devm_request_irq(priv->dev, irq, ast2700_espi_mmbi_irq_handler, IRQF_SHARED, ++ dev_name(priv->dev), priv); + if (rc) { -+ dev_err(dev, "cannot register device %s\n", perif->mdev.name); -+ return rc; ++ dev_err(priv->dev, "Failed to request IRQ: %d\n", rc); ++ goto out_dma; + } + -+ ast2700_espi_perif_reset(espi); ++ mmbi_irq_en = 0; ++ for_each_child_of_node(priv->dev->of_node, node) { ++ rc = of_property_read_u32(node, "channel", &i); ++ if (rc || i >= AST2700_ESPI_MMBI_MAX_CHANNELS || priv->channels[i].enabled) ++ continue; ++ rc = aspeed_espi_mmbi_chann_init(priv, node, seq, i); ++ if (rc) ++ dev_err(priv->dev, "MMBI: Channel(%d) init failed\n", ++ i); ++ else ++ mmbi_irq_en += (0x03 << (i * 2)); ++ } + ++ regmap_write(priv->mmbi_map, ESPI_MMBI_INT_ENABLE, mmbi_irq_en); ++ ++ for (i = 0; i < AST2700_ESPI_MMBI_MAX_CHANNELS; i++) { ++ if (priv->channels[i].enabled) ++ send_bmc_up_request(&priv->channels[i]); ++ } ++ ++ dev_set_drvdata(priv->dev, priv); + return 0; ++out_dma: ++ if (priv->mmbi_virt_addr) ++ aspeed_espi_mmbi_dma_finish(priv); ++out: ++ dev_err(&pdev->dev, "Failed to probe ASPEED eSPI MMBI: %d\n", rc); ++ return rc; +} + -+int ast2700_espi_perif_remove(struct aspeed_espi *espi) ++static void aspeed_espi_mmbi_remove(struct platform_device *pdev) +{ -+ struct aspeed_espi_perif_mmbi *mmbi; -+ struct aspeed_espi_perif *perif; -+ struct device *dev; -+ u32 reg; -+ int i; -+ -+ dev = espi->dev; -+ -+ perif = &espi->perif; ++ struct ast2700_espi_mmbi *priv = dev_get_drvdata(&pdev->dev); ++ u32 i, j, reg; + -+ writel(0x0, espi->regs + ESPI_CH0_INT_EN); -+ writel(0x0, espi->regs + ESPI_MMBI_INT_EN); ++ if (!priv) ++ return; + -+ reg = readl(espi->regs + ESPI_CH0_MCYC0_MASKL); -+ reg &= ~ESPI_CH0_MCYC0_MASKL_EN; -+ writel(reg, espi->regs + ESPI_CH0_MCYC0_MASKL); ++ regmap_write(priv->mmbi_map, ESPI_MMBI_INT_ENABLE, 0); + -+ reg = readl(espi->regs + ESPI_CH0_MCYC1_MASKL); -+ reg &= ~ESPI_CH0_MCYC1_MASKL_EN; -+ writel(reg, espi->regs + ESPI_CH0_MCYC1_MASKL); ++ regmap_read(priv->mmbi_map, ESPI_MMBI_CTRL, ®); ++ reg &= ~ESPI_MMBI_CTRL_EN; ++ regmap_write(priv->mmbi_map, ESPI_MMBI_CTRL, reg); + -+ reg = readl(espi->regs + ESPI_CH0_CTRL); -+ reg |= (ESPI_CH0_CTRL_MCYC_RD_DIS | ESPI_CH0_CTRL_MCYC_WR_DIS); -+ reg &= ~(ESPI_CH0_CTRL_NP_TX_DMA_EN -+ | ESPI_CH0_CTRL_PC_TX_DMA_EN -+ | ESPI_CH0_CTRL_PC_RX_DMA_EN -+ | ESPI_CH0_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH0_CTRL); ++ for (i = 0; i < AST2700_ESPI_MMBI_MAX_CHANNELS; i++) { ++ if (!priv->channels[i].enabled) ++ continue; + -+ if (perif->mmbi.enable) { -+ reg = readl(espi->regs + ESPI_MMBI_CTRL); -+ reg &= ~ESPI_MMBI_CTRL_EN; -+ writel(reg, espi->regs + ESPI_MMBI_CTRL); ++ for (j = 0; j < MMBI_MAX_SUPPORT_PROTOCOLS; j++) { ++ if (!priv->channels[i].protocols[j].miscdev.this_device) ++ continue; + -+ for (i = 0; i < perif->mmbi.inst_num; ++i) { -+ mmbi = &perif->mmbi.inst[i]; -+ misc_deregister(&mmbi->b2h_mdev); -+ misc_deregister(&mmbi->h2b_mdev); ++ misc_deregister(&priv->channels[i].protocols[j].miscdev); + } -+ -+ devm_iounmap(dev, perif->mmbi.virt); + } + -+ if (perif->mcyc.enable) -+ dmam_free_coherent(dev, perif->mcyc.size, perif->mcyc.virt, -+ perif->mcyc.taddr); -+ -+ if (perif->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.np_tx_virt, -+ perif->dma.np_tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_tx_virt, -+ perif->dma.pc_tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, perif->dma.pc_rx_virt, -+ perif->dma.pc_rx_addr); -+ } ++ if (priv->mmbi_virt_addr) ++ aspeed_espi_mmbi_dma_finish(priv); + -+ mutex_destroy(&perif->np_tx_mtx); -+ mutex_destroy(&perif->pc_tx_mtx); -+ mutex_destroy(&perif->pc_rx_mtx); ++ dev_info(&pdev->dev, "ASPEED eSPI MMBI driver removed\n"); ++} + -+ misc_deregister(&perif->mdev); ++static struct platform_driver aspeed_espi_mmbi_driver = { ++ .driver = { ++ .name = "ast2700-intel-espi-mmbi", ++ .of_match_table = aspeed_espi_mmbi_of_match, ++ }, ++ .probe = aspeed_espi_mmbi_probe, ++ .remove = aspeed_espi_mmbi_remove, ++}; + -+ return 0; -+} ++module_platform_driver(aspeed_espi_mmbi_driver); + -+/* virtual wire channel (CH1) */ -+static long ast2700_espi_vw_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -+{ -+ struct aspeed_espi_vw *vw; -+ struct aspeed_espi *espi; -+ u32 gpio0, gpio1; -+ u32 hw_mode; ++MODULE_AUTHOR("Your Name "); ++MODULE_DESCRIPTION("ASPEED eSPI MMBI driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.h b/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.h +--- a/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-intel-espi-mmbi.h 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2025 Aspeed Technology Inc. ++ */ ++#ifndef _AST2700_ESPI_MMBI_H_ ++#define _AST2700_ESPI_MMBI_H_ + -+ vw = container_of(fp->private_data, struct aspeed_espi_vw, mdev); -+ espi = container_of(vw, struct aspeed_espi, vw); -+ gpio0 = vw->gpio.val0; -+ gpio1 = vw->gpio.val1; -+ hw_mode = vw->gpio.hw_mode; ++/* MMBI Control registers */ ++#define ESPI_MMBI_CTRL 0x00 ++#define ESPI_MMBI_CTRL_INST_NUM GENMASK(6, 4) ++#define ESPI_MMBI_CTRL_EN BIT(0) ++#define ESPI_MMBI_INT_STATUS 0x08 ++#define ESPI_MMBI_HOST_RWS0_INT BIT(0) ++#define ESPI_MMBI_HOST_RWS1_INT BIT(1) ++#define ESPI_MMBI_INT_ENABLE 0x0C + -+ if (hw_mode) { -+ dev_err(espi->dev, "HW mode: vGPIO reflect on physical GPIO. Get state from GPIO driver.\n"); -+ return -EFAULT; -+ } ++#define ESPI_MMBI_HOST_RWP0_VAL 0x10 ++#define ESPI_MMBI_HOST_RWP1_VAL 0x14 + -+ switch (cmd) { -+ case ASPEED_ESPI_VW_GET_GPIO_VAL: -+ if (put_user(gpio0, (u32 __user *)arg)) { -+ dev_err(espi->dev, "failed to get vGPIO value0\n"); -+ return -EFAULT; -+ } ++#define ESPI_MMBI_HOST_READ_RWP0(idx) (((idx) * 8) + ESPI_MMBI_HOST_RWP0_VAL) ++#define ESPI_MMBI_HOST_READ_RWP1(idx) (((idx) * 8) + ESPI_MMBI_HOST_RWP1_VAL) + -+ dev_info(espi->dev, "Get vGPIO value0: 0x%x\n", gpio0); -+ break; ++struct aspeed_mmbi_get_empty_space { ++ __u32 length; ++}; + -+ case ASPEED_ESPI_VW_PUT_GPIO_VAL: -+ if (get_user(gpio0, (u32 __user *)arg)) { -+ dev_err(espi->dev, "failed to put vGPIO value0\n"); -+ return -EFAULT; -+ } ++struct aspeed_mmbi_get_config { ++ bool h_rdy; ++ __u32 h2b_wp; ++ __u32 b2h_rp; ++ __u32 h2b_rp; ++ __u32 b2h_wp; ++}; + -+ dev_info(espi->dev, "Put vGPIO value0: 0x%x\n", gpio0); -+ writel(gpio0, espi->regs + ESPI_CH1_GPIO_VAL0); -+ break; -+#ifdef CONFIG_ARM64 -+ case ASPEED_ESPI_VW_GET_GPIO_VAL1: -+ if (put_user(gpio1, (u32 __user *)arg)) { -+ dev_err(espi->dev, "failed to get vGPIO value1\n"); -+ return -EFAULT; -+ } ++#define __ASPEED_MMBI_CTRL_IOCTL_MAGIC 0xbb ++/* ++ * This IOCTL is meant to read empty space in B2H buffer ++ * in a specific channel ++ */ ++#define ASPEED_MMBI_CTRL_IOCTL_GET_B2H_EMPTY_SPACE \ ++ _IOWR(__ASPEED_MMBI_CTRL_IOCTL_MAGIC, 0x00, \ ++ struct aspeed_mmbi_get_empty_space) + -+ dev_info(espi->dev, "Get vGPIO value1: 0x%x\n", gpio1); -+ break; ++/* This IOCTL to send BMC reset request */ ++#define ASPEED_MMBI_CTRL_IOCTL_SEND_RESET_REQUEST \ ++ _IOW(__ASPEED_MMBI_CTRL_IOCTL_MAGIC, 0x01, int) + -+ case ASPEED_ESPI_VW_PUT_GPIO_VAL1: -+ if (get_user(gpio1, (u32 __user *)arg)) { -+ dev_err(espi->dev, "failed to put vGPIO value1\n"); -+ return -EFAULT; -+ } ++/* This IOCTL is to Get Config in HROP and HRWP */ ++#define ASPEED_MMBI_CTRL_IOCTL_GET_CONFIG \ ++ _IOWR(__ASPEED_MMBI_CTRL_IOCTL_MAGIC, 0x02, \ ++ struct aspeed_mmbi_get_config) + -+ dev_info(espi->dev, "Put vGPIO value1: 0x%x\n", gpio1); -+ writel(gpio1, espi->regs + ESPI_CH1_GPIO_VAL1); -+ break; +#endif -+ default: -+ return -EINVAL; -+ }; +diff --git a/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c b/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c +--- a/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,193 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2025 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ return 0; -+} ++#define SYNC_INTERVAL_MS_DEFAULT 1000 + -+static const struct file_operations ast2700_espi_vw_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2700_espi_vw_ioctl, ++#define RTC_REG0 0x0 ++#define RTC_REG0_MINUTES_ALARM GENMASK(31, 24) ++#define RTC_REG0_MINUTES GENMASK(23, 16) ++#define RTC_REG0_SECONDS_ALARM GENMASK(15, 8) ++#define RTC_REG0_SECONDS GENMASK(7, 0) ++#define RTC_REG1 0x4 ++#define RTC_REG1_DAY_OF_MONTH GENMASK(31, 24) ++#define RTC_REG1_DAY_OF_WEEK GENMASK(23, 16) ++#define RTC_REG1_HOURS_ALARM GENMASK(15, 8) ++#define RTC_REG1_HOURS GENMASK(7, 0) ++#define RTC_REG2 0x8 ++#define RTC_REG2_YEAR GENMASK(15, 8) ++#define RTC_REG2_MONTH GENMASK(7, 0) ++#define RTC_REG_MAX 0xc ++ ++struct rtc_espi_sync { ++ struct rtc_device *rtc_dev; ++ struct device *dev; ++ void __iomem *espi_ram_base; ++ u32 interval_ms; ++ struct delayed_work sync_work; ++ struct workqueue_struct *wq; +}; + -+static inline struct aspeed_espi_vw *to_ast2700_espi_pltrst(struct file *filp) ++static void sync_rtc_to_espi_work(struct work_struct *work) +{ -+ return container_of(filp->private_data, struct aspeed_espi_vw, -+ pltrst_mdev); -+} ++ struct rtc_espi_sync *ctx = container_of(to_delayed_work(work), struct rtc_espi_sync, sync_work); ++ struct rtc_time tm; ++ u32 reg, sec, min, hour, day_of_week, day_of_month, month, year; ++ u32 sec_alarm, min_alarm, hour_alarm; ++ int ret; + -+static int ast2700_espi_vw_pltrst_open(struct inode *inode, struct file *filp) -+{ -+ struct aspeed_espi_vw *priv = to_ast2700_espi_pltrst(filp); ++ if (!ctx->espi_ram_base) { ++ dev_err(ctx->dev, "eSPI RAM base not mapped\n"); ++ return; ++ } + -+ if ((filp->f_flags & O_ACCMODE) != O_RDONLY) -+ return -EACCES; -+ priv->pltrst.pltrst_avail = true ; /*Setting true returns first data after file open*/ ++ ret = rtc_read_time(ctx->rtc_dev, &tm); ++ if (ret < 0) { ++ dev_err(ctx->dev, "Failed to read RTC time: %d\n", ret); ++ goto reschedule; ++ } ++ dev_dbg(ctx->dev, "%s: tm data: secs=%d, mins=%d, hours=%d, mon=%d, year=%d\n", ++ __func__, tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_year); ++ dev_dbg(ctx->dev, "%s: tm data: wday(day of week)=%d, mday(day of month)=%d\n", ++ __func__, tm.tm_wday, tm.tm_mday); + -+ return 0; ++ /* Write the eSPI RAM registers */ ++ reg = FIELD_PREP(RTC_REG0_SECONDS, tm.tm_sec) | ++ FIELD_PREP(RTC_REG0_MINUTES, tm.tm_min); ++ writel(reg, (ctx->espi_ram_base + RTC_REG0)); ++ ++ reg = FIELD_PREP(RTC_REG1_HOURS, tm.tm_hour) | ++ FIELD_PREP(RTC_REG1_DAY_OF_MONTH, tm.tm_mday) | ++ FIELD_PREP(RTC_REG1_DAY_OF_WEEK, tm.tm_wday + 1); ++ writel(reg, ctx->espi_ram_base + RTC_REG1); ++ ++ reg = FIELD_PREP(RTC_REG2_MONTH, tm.tm_mon + 1) | ++ FIELD_PREP(RTC_REG2_YEAR, tm.tm_year % 100); ++ writel(reg, ctx->espi_ram_base + RTC_REG2); ++ ++ /* Read back the values to verify */ ++ reg = readl(ctx->espi_ram_base + RTC_REG0); ++ sec = FIELD_GET(RTC_REG0_SECONDS, reg); ++ sec_alarm = FIELD_GET(RTC_REG0_SECONDS_ALARM, reg); ++ min = FIELD_GET(RTC_REG0_MINUTES, reg); ++ min_alarm = FIELD_GET(RTC_REG0_MINUTES_ALARM, reg); ++ ++ reg = readl(ctx->espi_ram_base + RTC_REG1); ++ hour = FIELD_GET(RTC_REG1_HOURS, reg); ++ hour_alarm = FIELD_GET(RTC_REG1_HOURS_ALARM, reg); ++ day_of_week = FIELD_GET(RTC_REG1_DAY_OF_WEEK, reg); ++ day_of_month = FIELD_GET(RTC_REG1_DAY_OF_MONTH, reg); ++ ++ reg = readl(ctx->espi_ram_base + RTC_REG2); ++ month = FIELD_GET(RTC_REG2_MONTH, reg); ++ year = FIELD_GET(RTC_REG2_YEAR, reg); ++ ++ dev_dbg(ctx->dev, "%s: eSPI RAM data: secs=%d, mins=%d, hours=%d, ", ++ __func__, sec, min, hour); ++ dev_dbg(ctx->dev, "day_of_week=%d, day_of_month=%d, month=%d, year=%d\n", ++ day_of_week, day_of_month, month, year); ++ dev_dbg(ctx->dev, "%s: eSPI RAM data: secs_alarm=%d, mins_alarm=%d, hours_alarm=%d\n", ++ __func__, sec_alarm, min_alarm, hour_alarm); ++ ++reschedule: ++ queue_delayed_work(ctx->wq, &ctx->sync_work, msecs_to_jiffies(ctx->interval_ms)); +} + -+static ssize_t ast2700_espi_vw_pltrst_read(struct file *filp, char __user *buf, -+ size_t count, loff_t *offset) ++static int rtc_espi_sync_probe(struct platform_device *pdev) +{ -+ struct aspeed_espi_vw *vw = to_ast2700_espi_pltrst(filp); -+ DECLARE_WAITQUEUE(wait, current); -+ char data, old_sample; -+ int ret = 0; ++ struct device *dev = &pdev->dev; ++ struct rtc_espi_sync *ctx; ++ struct device_node *np = dev->of_node; ++ struct rtc_device *rtc; ++ struct resource *res; ++ u32 interval = SYNC_INTERVAL_MS_DEFAULT; + -+ spin_lock_irq(&vw->pltrst.pltrst_lock); ++ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; + -+ if (filp->f_flags & O_NONBLOCK) { -+ if (!vw->pltrst.pltrst_avail) { -+ ret = -EAGAIN; -+ goto out_unlock; -+ } -+ data = vw->pltrst.pltrst_status; -+ vw->pltrst.pltrst_avail = false; -+ } else { -+ add_wait_queue(&vw->pltrst.pltrst_wq, &wait); -+ set_current_state(TASK_INTERRUPTIBLE); ++ /* Ensure the device tree node is valid */ ++ rtc = rtc_class_open("rtc0"); ++ if (!rtc) { ++ dev_info(dev, "RTC not ready, deferring probe\n"); ++ return -EPROBE_DEFER; ++ } + -+ old_sample = vw->pltrst.pltrst_status; ++ /* Check if the RTC device supports the read_time operation */ ++ if (!rtc->ops || !rtc->ops->read_time) { ++ dev_err(dev, "RTC device does not support read_time operation\n"); ++ rtc_class_close(rtc); ++ return -ENODEV; ++ } + -+ do { -+ if (old_sample != vw->pltrst.pltrst_status) { -+ data = vw->pltrst.pltrst_status; -+ vw->pltrst.pltrst_avail = false; -+ break; -+ } ++ ctx->rtc_dev = rtc; ++ ctx->dev = dev; + -+ if (signal_pending(current)) { -+ ret = -ERESTARTSYS; -+ } else { -+ spin_unlock_irq(&vw->pltrst.pltrst_lock); -+ schedule(); -+ spin_lock_irq(&vw->pltrst.pltrst_lock); -+ } -+ } while (!ret); ++ /* Get eSPI RAM handle and map */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ctx->espi_ram_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ctx->espi_ram_base)) ++ return PTR_ERR(ctx->espi_ram_base); + -+ remove_wait_queue(&vw->pltrst.pltrst_wq, &wait); -+ set_current_state(TASK_RUNNING); -+ } -+out_unlock: -+ spin_unlock_irq(&vw->pltrst.pltrst_lock); -+ if (ret) -+ return ret; ++ if (!ctx->espi_ram_base) ++ memset(ctx->espi_ram_base, 0, RTC_REG_MAX); + -+ ret = put_user(data, buf); -+ if (!ret) -+ ret = sizeof(data); ++ /* Optional: polling interval */ ++ of_property_read_u32(np, "interval-ms", &interval); ++ ctx->interval_ms = interval; + -+ return ret; ++ ctx->wq = alloc_workqueue("rtc_espi_sync_wq", WQ_UNBOUND, 0); ++ if (!ctx->wq) ++ return -ENOMEM; ++ ++ INIT_DELAYED_WORK(&ctx->sync_work, sync_rtc_to_espi_work); ++ queue_delayed_work(ctx->wq, &ctx->sync_work, 0); ++ ++ dev_info(dev, "RTC-eSPI RAM sync initialized, every %u ms\n", ctx->interval_ms); ++ ++ platform_set_drvdata(pdev, ctx); ++ return 0; +} + -+static unsigned int ast2700_espi_vw_pltrst_poll(struct file *file, -+ poll_table *wait) ++static void rtc_espi_sync_remove(struct platform_device *pdev) +{ -+ struct aspeed_espi_vw *vw = to_ast2700_espi_pltrst(file); -+ unsigned int mask = 0; ++ struct rtc_espi_sync *ctx = platform_get_drvdata(pdev); + -+ poll_wait(file, &vw->pltrst.pltrst_wq, wait); -+ if (vw->pltrst.pltrst_avail) -+ mask |= POLLIN; -+ return mask; ++ cancel_delayed_work_sync(&ctx->sync_work); ++ destroy_workqueue(ctx->wq); ++ ++ if (ctx->rtc_dev) ++ rtc_class_close(ctx->rtc_dev); +} + -+static const struct file_operations ast2700_espi_vw_pltrst_fops = { -+ .owner = THIS_MODULE, -+ .open = ast2700_espi_vw_pltrst_open, -+ .read = ast2700_espi_vw_pltrst_read, -+ .poll = ast2700_espi_vw_pltrst_poll, ++static const struct of_device_id rtc_espi_sync_of_match[] = { ++ { .compatible = "aspeed,ast2700-rtc-over-espi" }, ++ { } +}; ++MODULE_DEVICE_TABLE(of, rtc_espi_sync_of_match); + -+static void ast2700_espi_vw_isr(struct aspeed_espi *espi) -+{ -+ struct aspeed_espi_vw *vw; -+ u32 sts; -+ u32 sts_evt0; -+ u32 evt0; -+ unsigned long flags; ++static struct platform_driver rtc_espi_sync_driver = { ++ .driver = { ++ .name = "ast2700-rtc-over-espi", ++ .of_match_table = rtc_espi_sync_of_match, ++ }, ++ .probe = rtc_espi_sync_probe, ++ .remove = rtc_espi_sync_remove, ++}; ++module_platform_driver(rtc_espi_sync_driver); + -+ vw = &espi->vw; ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Kevin Chen "); ++MODULE_DESCRIPTION("RTC to eSPI RAM sync driver"); +diff --git a/drivers/soc/aspeed/mmbi/Kconfig b/drivers/soc/aspeed/mmbi/Kconfig +--- a/drivers/soc/aspeed/mmbi/Kconfig 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/Kconfig 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,17 @@ ++menu "ASPEED MMBI driver" ++ ++config ASPEED_MMBI ++ tristate "ASPEED MMBI driver" ++ help ++ Enable support for the ASPEED MMBI driver. This driver ++ provides a communication channel between the Host and the BMC ++ over PCIe using the MMBI (Memory Mapped Buffer Interface). + -+ sts = readl(espi->regs + ESPI_CH1_INT_STS); ++config ASPEED_PCIE_MMBI ++ tristate "ASPEED PCIE MMBI" ++ depends on ASPEED_MMBI + -+ if (sts & ESPI_CH1_INT_STS_GPIO) { -+ vw->gpio.val0 = readl(espi->regs + ESPI_CH1_GPIO_VAL0); -+ vw->gpio.val1 = readl(espi->regs + ESPI_CH1_GPIO_VAL1); -+ writel(ESPI_CH1_INT_STS_GPIO, espi->regs + ESPI_CH1_INT_STS); -+ } ++config ASPEED_HOST_PCIE_MMBI ++ tristate "ASPEED Host PCIE MMBI" + -+ if (sts & ESPI_CH1_INT_STS_EVT0) { -+ sts_evt0 = readl(espi->regs + ESPI_CH1_EVT0_INT_STS); -+ evt0 = readl(espi->regs + ESPI_CH1_EVT0); -+ if (sts_evt0 & ESPI_CH1_EVT0_INT_STS_PLTRSTN || vw->pltrst.pltrst_status == 'U') { -+ spin_lock_irqsave(&vw->pltrst.pltrst_lock, flags); -+ vw->pltrst.pltrst_status = (evt0 & ESPI_CH1_EVT0_PLTRSTN) ? '1' : '0'; -+ vw->pltrst.pltrst_avail = true; -+ spin_unlock_irqrestore(&vw->pltrst.pltrst_lock, flags); ++endmenu +diff --git a/drivers/soc/aspeed/mmbi/Makefile b/drivers/soc/aspeed/mmbi/Makefile +--- a/drivers/soc/aspeed/mmbi/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/Makefile 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,3 @@ ++obj-$(CONFIG_ASPEED_MMBI) += aspeed-mmbi.o aspeed-mmbi-bmc.o aspeed-mmbi-host.o ++obj-$(CONFIG_ASPEED_PCIE_MMBI) += aspeed-pcie-mmbi.o ++obj-$(CONFIG_ASPEED_HOST_PCIE_MMBI) += aspeed-host-pcie-mmbi.o +diff --git a/drivers/soc/aspeed/mmbi/aspeed-host-pcie-mmbi.c b/drivers/soc/aspeed/mmbi/aspeed-host-pcie-mmbi.c +--- a/drivers/soc/aspeed/mmbi/aspeed-host-pcie-mmbi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-host-pcie-mmbi.c 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,287 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* Implements PCIe Transport in host side for MMBI protocol ++ * Copyright 2026 Aspeed Technology Inc. ++ */ + -+ writel(ESPI_CH1_EVT0_INT_STS_PLTRSTN, -+ espi->regs + ESPI_CH1_EVT0_INT_STS); ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ wake_up_interruptible(&vw->pltrst.pltrst_wq); -+ } -+ writel(ESPI_CH1_INT_STS_EVT0, espi->regs + ESPI_CH1_INT_STS); ++#include "aspeed-mmbi.h" ++ ++#define DRIVER_NAME "aspeed-host-pcie-mmbi" ++#define MAX_MSI_NUM 32 ++/* Value for triggering interrupt */ ++#define ASPEED_E2M_INT_VAL 0xC6 ++ ++struct aspeed_host_pcie_mmbi_ins { ++ struct mmbi_ins_desc mmbi_desc; ++ int irq; ++ int msi_index; ++ int instance_index; ++ int bar_index; ++ const char *dev_name; ++ bool valid; ++}; ++ ++struct aspeed_host_pcie_mmbi { ++ struct device *dev; ++ struct aspeed_host_pcie_mmbi_ins instances[PCI_STD_NUM_BARS]; ++ int msi_nums; ++}; ++ ++/* ++ * app_interface: per-channel application interface selection. ++ * 0 = ioctl (MMBI_APP_INTF_IOCTL) ++ * 1 = mctp-netdev (MMBI_APP_INTF_MCTP_NETDEV) ++ * ++ * One value applies to every channel. Multiple values map by channel index, ++ * and omitted channels keep their defaults. ++ * Examples: ++ * app_interface=1 -> all channels use mctp-netdev ++ * app_interface=1,0,0,0,1 -> ch0/ch4 use mctp-netdev ++ * Invalid values fall back to the default map. ++ */ ++static const uint app_interface_defaults[MMBI_MAX_CHANNELS] = { ++ [0] = MMBI_APP_INTF_MCTP_NETDEV, ++}; ++ ++static uint app_interface[MMBI_MAX_CHANNELS] = { [0] = MMBI_APP_INTF_MCTP_NETDEV }; ++static int app_interface_count; ++module_param_array(app_interface, uint, &app_interface_count, 0444); ++MODULE_PARM_DESC(app_interface, ++ "App interface per channel: 0=ioctl, 1=mctp-netdev; one value applies to all channels"); ++ ++static const char * ++aspeed_host_pcie_mmbi_app_interface_name(enum mmbi_app_interface intf) ++{ ++ switch (intf) { ++ case MMBI_APP_INTF_MCTP_NETDEV: ++ return "mctp-netdev"; ++ case MMBI_APP_INTF_IOCTL: ++ default: ++ return "ioctl"; + } +} + -+static void ast2700_espi_vw_reset(struct aspeed_espi *espi) ++static int aspeed_host_pcie_mmbi_validate_app_interface(struct device *dev) +{ -+ u32 reg; -+ struct aspeed_espi_vw *vw = &espi->vw; ++ int i; + -+ writel(0x0, espi->regs + ESPI_CH1_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_CH1_INT_STS); ++ for (i = 0; i < app_interface_count; i++) { ++ if (app_interface[i] > MMBI_APP_INTF_MCTP_NETDEV) { ++ dev_warn(dev, ++ "invalid app_interface[%d]=%u, using defaults\n", ++ i, app_interface[i]); ++ return -EINVAL; ++ } ++ } + -+ writel(vw->gpio.grp, espi->regs + ESPI_CH1_GPIO_GRP); -+ writel(vw->gpio.dir0, espi->regs + ESPI_CH1_GPIO_DIR0); -+ writel(vw->gpio.dir1, espi->regs + ESPI_CH1_GPIO_DIR1); ++ return 0; ++} + -+ vw->gpio.val0 = readl(espi->regs + ESPI_CH1_GPIO_VAL0); -+ vw->gpio.val1 = readl(espi->regs + ESPI_CH1_GPIO_VAL1); ++static enum mmbi_app_interface ++aspeed_host_pcie_mmbi_get_app_interface(int channel, bool use_defaults) ++{ ++ if (use_defaults) ++ return app_interface_defaults[channel]; + -+ if (vw->pltrst.enabled) { -+ spin_lock(&vw->pltrst.pltrst_lock); -+ vw->pltrst.pltrst_status = 'U'; /* Unknown */ -+ vw->pltrst.pltrst_avail = true; -+ spin_unlock(&vw->pltrst.pltrst_lock); ++ if (app_interface_count == 1) ++ return app_interface[0]; + -+ writel(ESPI_CH1_EVT0_INT_T2_PLTRSTN, -+ espi->regs + ESPI_CH1_EVT0_INT_T2); -+ writel(ESPI_CH1_EVT0_INT_EN_PLTRSTN, espi->regs + ESPI_CH1_EVT0_INT_EN); -+ writel(ESPI_CH1_INT_EN_GPIO | ESPI_CH1_INT_EN_SYS_EVT0, -+ espi->regs + ESPI_CH1_INT_EN); -+ } else { -+ writel(ESPI_CH1_INT_EN_GPIO, espi->regs + ESPI_CH1_INT_EN); -+ } ++ if (channel < app_interface_count) ++ return app_interface[channel]; + -+ reg = readl(espi->regs + ESPI_CH1_CTRL) -+ | ((vw->gpio.hw_mode) ? ESPI_CH1_CTRL_GPIO_HW : 0) -+ | ESPI_CH1_CTRL_SW_RDY; -+ writel(reg, espi->regs + ESPI_CH1_CTRL); ++ return app_interface_defaults[channel]; +} + -+int ast2700_espi_vw_probe(struct aspeed_espi *espi) ++static void ++aspeed_host_pcie_mmbi_init_app_interfaces(struct mmbi_ins_desc *mmbi_desc, ++ bool use_defaults) +{ -+ int rc; -+ struct device *dev = espi->dev; -+ struct aspeed_espi_vw *vw = &espi->vw; ++ int i; + -+ vw->gpio.hw_mode = of_property_read_bool(dev->of_node, "vw-gpio-hw-mode"); -+ of_property_read_u32(dev->of_node, "vw-gpio-group", &vw->gpio.grp); -+ of_property_read_u32_index(dev->of_node, "vw-gpio-direction", 0, &vw->gpio.dir0); -+ of_property_read_u32_index(dev->of_node, "vw-gpio-direction", 1, &vw->gpio.dir1); ++ for (i = 0; i < MMBI_MAX_CHANNELS; i++) ++ mmbi_desc->chan_desc[i].app_interface = ++ aspeed_host_pcie_mmbi_get_app_interface(i, ++ use_defaults); ++} + -+ vw->pltrst.enabled = of_property_read_bool(dev->of_node, "vw-pltrst-monitor"); -+ if (vw->pltrst.enabled) { -+ spin_lock_init(&vw->pltrst.pltrst_lock); -+ init_waitqueue_head(&vw->pltrst.pltrst_wq); -+ vw->pltrst.pltrst_status = 'U'; /* Unknown */ -+ vw->pltrst.pltrst_avail = false; -+ vw->pltrst_mdev.parent = dev; -+ vw->pltrst_mdev.minor = MISC_DYNAMIC_MINOR; -+ vw->pltrst_mdev.name = -+ devm_kasprintf(dev, GFP_KERNEL, "%s-pltrstn%d", -+ DEVICE_NAME, espi->dev_id); -+ vw->pltrst_mdev.fops = &ast2700_espi_vw_pltrst_fops; -+ rc = misc_register(&vw->pltrst_mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", vw->pltrst_mdev.name); -+ return rc; ++static void aspeed_host_pcie_mmbi_log_app_interfaces(struct device *dev, ++ struct mmbi_ins_desc *mmbi_desc, ++ int bar) ++{ ++ struct mmbi_chan_desc *chan_desc; ++ const char *interface; ++ int i; ++ ++ for (i = 0; i < mmbi_desc->num_of_channels; i++) { ++ chan_desc = &mmbi_desc->chan_desc[i]; ++ interface = aspeed_host_pcie_mmbi_app_interface_name(chan_desc->app_interface); ++ dev_info(dev, "BAR %d channel %d application interface: %s\n", ++ bar, i, interface); ++ } ++} ++ ++static int aspeed_pci_host_mmbi_setup(struct pci_dev *pdev) ++{ ++ struct aspeed_host_pcie_mmbi *mmbi = pci_get_drvdata(pdev); ++ struct aspeed_host_pcie_mmbi_ins *ins; ++ struct device *dev = &pdev->dev; ++ struct mmbi_ins_desc *mmbi_desc; ++ int i, ret, instance_id; ++ int last_ret = -ENODEV; ++ bool use_default_app_interfaces; ++ resource_size_t start, size; ++ ++ ret = aspeed_host_pcie_mmbi_validate_app_interface(dev); ++ use_default_app_interfaces = ret != 0; ++ ++ instance_id = 0; ++ for (i = 0; i < PCI_STD_NUM_BARS; i++) { ++ ins = &mmbi->instances[i]; ++ mmbi_desc = &ins->mmbi_desc; ++ start = pci_resource_start(pdev, i); ++ size = pci_resource_len(pdev, i); ++ ++ if (!start || !size) { ++ dev_err(dev, ++ "Invalid BAR %d with start 0x%llx and size 0x%llx\n", ++ i, (unsigned long long)start, ++ (unsigned long long)size); ++ continue; + } -+ } + -+ vw->mdev.parent = dev; -+ vw->mdev.minor = MISC_DYNAMIC_MINOR; -+ vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-vw%d", DEVICE_NAME, espi->dev_id); -+ vw->mdev.fops = &ast2700_espi_vw_fops; -+ rc = misc_register(&vw->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", vw->mdev.name); -+ return rc; ++ mmbi_desc->desc_virt = pci_ioremap_bar(pdev, i); ++ if (!mmbi_desc->desc_virt) { ++ dev_err(dev, "Failed to ioremap BAR %d\n", i); ++ continue; ++ } ++ mmbi_desc->dev = dev; ++ mmbi_desc->role = MMBI_ROLE_HOST; ++ ++ aspeed_host_pcie_mmbi_init_app_interfaces(mmbi_desc, ++ use_default_app_interfaces); ++ ret = mmbi_instance_init(mmbi_desc); ++ if (ret) { ++ dev_info(dev, "Failed to initialize MMBI at bar %d, error %d\n", i, ret); ++ /* Unmap the previously mapped BAR */ ++ iounmap(mmbi_desc->desc_virt); ++ last_ret = ret; ++ } else { ++ ins->instance_index = instance_id++; ++ ins->bar_index = i; ++ ins->valid = true; ++ ins->dev_name = devm_kasprintf(dev, GFP_KERNEL, "mmbi_host_ins%d", ins->instance_index); ++ aspeed_host_pcie_mmbi_log_app_interfaces(dev, mmbi_desc, i); ++ ++ if (mmbi_desc->host_int_type == MMBI_HOST_INT_PCIE) { ++ if (mmbi_desc->host_int_location > mmbi->msi_nums) { ++ dev_err(dev, ++ "Invalid host interrupt location %d for MMBI instance at BAR %d, use MSI 0\n", ++ mmbi_desc->host_int_location, ++ i); ++ ins->msi_index = 0; ++ } else { ++ ins->msi_index = mmbi_desc->host_int_location; ++ } ++ } ++ } + } + -+ ast2700_espi_vw_reset(espi); ++ if (!instance_id) ++ return last_ret; + + return 0; +} + -+int ast2700_espi_vw_remove(struct aspeed_espi *espi) ++static int aspeed_pci_host_pcie_mmbi_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) +{ -+ struct aspeed_espi_vw *vw; ++ struct aspeed_host_pcie_mmbi *mmbi; ++ int rc = 0; + -+ vw = &espi->vw; ++ mmbi = devm_kzalloc(&pdev->dev, sizeof(*mmbi), GFP_KERNEL); ++ if (!mmbi) ++ return -ENOMEM; + -+ writel(0x0, espi->regs + ESPI_CH1_INT_EN); ++ mmbi->dev = &pdev->dev; + -+ misc_deregister(&vw->mdev); ++ rc = pci_enable_device(pdev); ++ if (rc) ++ return rc; ++ ++ pci_set_master(pdev); ++ pci_set_drvdata(pdev, mmbi); ++ rc = aspeed_pci_host_mmbi_setup(pdev); ++ if (rc) { ++ dev_err(&pdev->dev, "Failed to setup MMBI instances\n"); ++ goto err_pci; ++ } + ++ dev_info(&pdev->dev, "ASPEED Host PCIe MMBI device probed successfully\n"); + return 0; ++ ++err_pci: ++ pci_disable_device(pdev); ++ return dev_err_probe(&pdev->dev, rc, ++ "Failed to probe ASPEED Host PCIe MMBI device\n"); +} + -+/* out-of-band channel (CH2) */ -+static long ast2700_espi_oob_dma_get_rx(struct file *fp, -+ struct aspeed_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) ++static void aspeed_pci_host_pcie_mmbi_remove(struct pci_dev *pdev) +{ -+ struct ast2700_espi_oob_dma_rx_desc *d; -+ struct ast2700_espi_oob_dma_rx_desc *rx_descs; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ u32 wptr, pkt_len; -+ unsigned long flags; -+ u8 *pkt; -+ int rc; -+ -+ espi = container_of(oob, struct aspeed_espi, oob); ++ struct aspeed_host_pcie_mmbi *mmbi; ++ struct aspeed_host_pcie_mmbi_ins *ins; ++ int i; + -+ wptr = FIELD_PREP(ESPI_CH2_RX_DESC_WPTR_WP, readl(espi->regs + ESPI_CH2_RX_DESC_WPTR)); ++ mmbi = pci_get_drvdata(pdev); ++ for (i = 0; i < PCI_STD_NUM_BARS; i++) { ++ ins = &mmbi->instances[i]; ++ if (ins->valid) { ++ mmbi_instance_remove(&ins->mmbi_desc); ++ pci_iounmap(pdev, ins->mmbi_desc.desc_virt); ++ } ++ } + -+ rx_descs = (struct ast2700_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; -+ d = &rx_descs[wptr]; ++ pci_disable_device(pdev); ++} + -+ if (!d->dirty) -+ return -EFAULT; ++static struct pci_device_id aspeed_host_pcie_mmbi_pci_ids[] = { ++ { PCI_DEVICE(0x1A03, 0x2402), .class = 0x0C0C00, .class_mask = (0xFFFF00)}, ++ { 0, } ++}; + -+ pkt_len = ((d->len) ? d->len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++MODULE_DEVICE_TABLE(pci, aspeed_host_pcie_mmbi_pci_ids); + -+ if (ioc->pkt_len < pkt_len) -+ return -EINVAL; ++static struct pci_driver aspeed_host_pcie_mmbi_driver = { ++ .name = DRIVER_NAME, ++ .id_table = aspeed_host_pcie_mmbi_pci_ids, ++ .probe = aspeed_pci_host_pcie_mmbi_probe, ++ .remove = aspeed_pci_host_pcie_mmbi_remove, ++}; + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) -+ return -ENOMEM; ++static int __init aspeed_host_pcie_mmbi_init(void) ++{ ++ return pci_register_driver(&aspeed_host_pcie_mmbi_driver); ++} + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = d->cyc; -+ hdr->tag = d->tag; -+ hdr->len_h = d->len >> 8; -+ hdr->len_l = d->len & 0xff; -+ memcpy(hdr + 1, oob->dma.rx_virt + (PAGE_SIZE * wptr), pkt_len - sizeof(*hdr)); ++static void aspeed_host_pcie_mmbi_exit(void) ++{ ++ /* unregister pci driver */ ++ pci_unregister_driver(&aspeed_host_pcie_mmbi_driver); ++} + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++late_initcall(aspeed_host_pcie_mmbi_init); ++module_exit(aspeed_host_pcie_mmbi_exit); + -+ spin_lock_irqsave(&oob->lock, flags); ++MODULE_AUTHOR("YH Chung "); ++MODULE_DESCRIPTION("ASPEED Host PCIE MMBI Transport Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.c b/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.c +--- a/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.c 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,216 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* Implements MMBI protocol for BMC side with VPSCB buffer type ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aspeed-mmbi.h" ++#include "aspeed-mmbi-internal.h" ++#include "aspeed-mmbi-bmc.h" ++ ++#define MMBI_PUT_U32_MSB(base, off, val) \ ++ do { \ ++ for (int __i = 0; __i < 4; __i++) { \ ++ iowrite8((u8)((val) >> (24 - (__i * 8))), \ ++ (base) + ((off)++)); \ ++ } \ ++ } while (0) + -+ /* make current descriptor available again */ -+ d->dirty = 0; ++static void mmbi_channel_state_handler(struct mmbi_chan_desc *chan, u8 __iomem *desc_virt) ++{ ++ enum mmbi_state prev_state, cur_state; ++ struct mmbi_buf_vpscb *vpscb; ++ u8 __iomem *host_ros_virt, *host_rws_virt; ++ u32 unhandled_len, avail_len; + -+ wptr = ((wptr + 1) % OOB_DMA_DESC_NUM); -+ writel(wptr | ESPI_CH2_RX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_RX_DESC_WPTR); ++ prev_state = chan->priv->state; + -+ /* set ready flag base on the next RX descriptor */ -+ oob->rx_ready = rx_descs[wptr].dirty; ++ vpscb = &chan->buffer_desc; ++ host_ros_virt = desc_virt + vpscb->h_ros_p; ++ host_rws_virt = desc_virt + vpscb->h_rws_p; ++ mmbi_channel_state_update(chan, desc_virt); ++ cur_state = chan->priv->state; + -+ spin_unlock_irqrestore(&oob->lock, flags); ++ switch (cur_state) { ++ case INIT_MISMATCH: ++ memset_io(host_ros_virt, 0, 8); ++ memset_io(host_rws_virt, 0, 8); ++ mmbi_set_up(host_ros_virt); ++ chan->priv->state = INIT_COMPLETED; ++ break; ++ case NORMAL_RUNTIME: ++ mmbi_set_ready(host_ros_virt); + -+ rc = 0; ++ unhandled_len = mmbi_channel_unhandled_length(host_ros_virt, host_rws_virt, chan->h2b_l); ++ if (unhandled_len >= MMBI_PKT_MIN_SIZE) ++ chan->priv->rx_ready = true; ++ else ++ chan->priv->rx_ready = false; ++ avail_len = mmbi_channel_avail_length(host_rws_virt, host_ros_virt, chan->b2h_l); ++ if (avail_len <= MMBI_PKT_MIN_SIZE) ++ chan->priv->tx_ready = false; ++ else ++ chan->priv->tx_ready = true; + -+free_n_out: -+ vfree(pkt); ++ break; ++ case RESET_REQ_BY_HOST: ++ mmbi_clr_ready(host_ros_virt); ++ mmbi_set_rst(host_ros_virt); ++ chan->priv->state = RESET_ACKED; ++ // TODO: consume all pending data from host and then trigger initialization ++ break; ++ case RESET_ACKED: ++ mmbi_clr_up(host_ros_virt); ++ chan->priv->state = TRANS_TO_INIT; ++ memset_io(host_ros_virt, 0, 8); ++ memset_io(host_rws_virt, 0, 8); ++ mmbi_set_up(host_ros_virt); ++ chan->priv->state = INIT_COMPLETED; ++ default: ++ /* other states do not require action from BMC side */ ++ break; ++ } + -+ return rc; ++ return; +} + -+static long ast2700_espi_oob_get_rx(struct file *fp, -+ struct aspeed_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) ++static int mmbi_buffer_init_vpscb_bmc(u8 __iomem *buf_virt, struct mmbi_buf_vpscb *buf_desc) +{ -+ u32 reg, cyc, tag, len; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ unsigned long flags; -+ u32 pkt_len; -+ u8 *pkt; -+ int i, rc; ++ u32 offset; + -+ espi = container_of(oob, struct aspeed_espi, oob); ++ offset = 0; + -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&oob->rx_mtx)) -+ return -EAGAIN; ++ iowrite8(MMBI_BUFFER_TYPE_VPSCB & MMBI_BUF_TYPE_MASK, buf_virt + offset++); ++ iowrite8(buf_desc->host_int_val_ch & MMBI_INT_VAL_MASK, buf_virt + offset++); ++ iowrite8(buf_desc->bmc_int_val_ch & MMBI_INT_VAL_MASK, buf_virt + offset++); + -+ if (!oob->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; -+ } -+ } else { -+ mutex_lock(&oob->rx_mtx); ++ memset_io(buf_virt + offset, 0, 5); ++ offset += 5; /* reserved bytes */ + -+ if (!oob->rx_ready) { -+ rc = wait_event_interruptible(oob->wq, oob->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } -+ } -+ } ++ MMBI_PUT_U32_MSB(buf_virt, offset, MMBI_BUF_ADDR_ALIGN(buf_desc->h_ros_p)); ++ MMBI_PUT_U32_MSB(buf_virt, offset, MMBI_BUF_ADDR_ALIGN(buf_desc->h_rws_p)); + -+ if (oob->dma.enable) { -+ rc = ast2700_espi_oob_dma_get_rx(fp, oob, ioc); -+ goto unlock_mtx_n_out; -+ } ++ pr_info("%s: h_ros_p=0x%x, h_rws_p=0x%x\n", __func__, ++ buf_desc->h_ros_p, buf_desc->h_rws_p); ++ return 0; ++} + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_CH2_RX_CTRL); -+ cyc = FIELD_GET(ESPI_CH2_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_CH2_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_CH2_RX_CTRL_LEN, reg); ++static int mmbi_channel_init_bmc(u8 __iomem *desc_virt, struct mmbi_chan_desc *chan_desc) ++{ ++ u32 offset = 0; ++ struct mmbi_buf_vpscb *buffer_desc; + -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + sizeof(struct espi_comm_hdr); ++ chan_desc->priv->peer_ready = false; ++ MMBI_PUT_U32_MSB(desc_virt, offset, MMBI_BUF_ADDR_ALIGN(chan_desc->b2h_ba_offset)); ++ MMBI_PUT_U32_MSB(desc_virt, offset, MMBI_BUF_ADDR_ALIGN(chan_desc->h2b_ba_offset)); ++ MMBI_PUT_U32_MSB(desc_virt, offset, chan_desc->b2h_l); ++ MMBI_PUT_U32_MSB(desc_virt, offset, chan_desc->h2b_l); + -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } ++ pr_info("%s: b2h_ba_offset=0x%x, h2b_ba_offset=0x%x\n", __func__, ++ chan_desc->b2h_ba_offset, chan_desc->h2b_ba_offset); ++ pr_info("%s: b2h_l=0x%x, h2b_l=0x%x\n", __func__, chan_desc->b2h_l, ++ chan_desc->h2b_l); ++ pr_info("%s: buffer_type=%u\n", __func__, chan_desc->buffer_type); + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; ++ if (chan_desc->buffer_type == MMBI_BUFFER_TYPE_VPSCB) { ++ buffer_desc = &chan_desc->buffer_desc; ++ return mmbi_buffer_init_vpscb_bmc(desc_virt + offset, buffer_desc); + } + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; ++ pr_err("\t%s: unsupported buffer type %d\n", __func__, ++ chan_desc->buffer_type); ++ return -EINVAL; ++} + -+ for (i = sizeof(*hdr); i < pkt_len; ++i) { -+ reg = readl(espi->regs + ESPI_CH2_RX_DATA); -+ pkt[i] = reg & 0xff; ++void mmbi_channel_irq_bmc(struct mmbi_chan_desc *chan) ++{ ++ mmbi_channel_state_handler(chan, chan->mmbi->desc_virt); ++} ++EXPORT_SYMBOL_GPL(mmbi_channel_irq_bmc); ++ ++int mmbi_instance_init_bmc(struct mmbi_ins_desc *mmbi) ++{ ++ int rc, offset; ++ u32 i; ++ u8 __iomem *desc_virt; ++ struct mmbi_buf_vpscb *vpscb; ++ ++ if (mmbi->num_of_channels > MMBI_MAX_CHANNELS) { ++ dev_err(mmbi->dev, "%s: invalid number of instances %d\n", ++ __func__, mmbi->num_of_channels); ++ return -EINVAL; + } + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; ++ desc_virt = mmbi->desc_virt; ++ i = FIELD_PREP((u32)MMBI_NOI_MASK, mmbi->num_of_channels); ++ i |= FIELD_PREP((u32)MMBI_OS_USE_MASK, mmbi->os_use); ++ dev_info(mmbi->dev, "%s: MMBI version 0x%x, os_use %u, num_of_channels %u i %u\n", ++ __func__, mmbi->mmbi_version, mmbi->os_use, mmbi->num_of_channels, i); ++ ++ memcpy_toio(desc_virt, MMBI_SIGNATURE, sizeof(MMBI_SIGNATURE) - 1); ++ desc_virt += sizeof(MMBI_SIGNATURE) - 1; ++ ++ iowrite8(mmbi->mmbi_version & MMBI_VERSION_MASK, desc_virt++); ++ iowrite8(i, desc_virt++); ++ ++ mmbi->chan_desc[0].priv->state = INIT_IN_PROGRESS; ++ mmbi->chan_desc[0].index = 0; ++ mmbi->chan_desc[0].mmbi = mmbi; ++ rc = mmbi_channel_init_bmc(desc_virt, &mmbi->chan_desc[0]); ++ if (rc) { ++ dev_err(mmbi->dev, "%s: failed to init channel 0\n", __func__); ++ return rc; + } ++ desc_virt += MMBI_DESC_SIZE_CHANNEL; + -+ spin_lock_irqsave(&oob->lock, flags); ++ iowrite8(mmbi->host_int_type & MMBI_INT_TYPE_MASK, desc_virt++); ++ iowrite8(mmbi->host_int_location, desc_virt++); + -+ writel(ESPI_CH2_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH2_RX_CTRL); -+ oob->rx_ready = 0; ++ memset_io(desc_virt, 0, 3); ++ desc_virt += 3; /* reserved bytes */ ++ iowrite8(mmbi->host_int_value, desc_virt++); ++ iowrite8(mmbi->bmc_int_type & MMBI_INT_TYPE_MASK, desc_virt++); ++ offset = desc_virt - mmbi->desc_virt; ++ desc_virt += sizeof(mmbi->bmc_int_location); ++ MMBI_PUT_U32_MSB(mmbi->desc_virt, offset, mmbi->bmc_int_location); + -+ spin_unlock_irqrestore(&oob->lock, flags); ++ memset_io(desc_virt, 0, 4); ++ desc_virt += 4; /* reserved bytes */ + -+ rc = 0; ++ iowrite8(mmbi->bmc_int_value, desc_virt++); + -+free_n_out: -+ vfree(pkt); ++ memset_io(desc_virt, 0, 8); ++ desc_virt += 8; /* reserved bytes */ + -+unlock_mtx_n_out: -+ mutex_unlock(&oob->rx_mtx); ++ if (mmbi->num_of_channels > 1) { ++ for (i = 1; i < mmbi->num_of_channels; i++) { ++ mmbi->chan_desc[i].priv->state = INIT_IN_PROGRESS; ++ mmbi->chan_desc[i].index = i; ++ mmbi->chan_desc[i].mmbi = mmbi; ++ rc = mmbi_channel_init_bmc(desc_virt, &mmbi->chan_desc[i]); ++ if (rc) { ++ dev_err(mmbi->dev, "%s: failed to init channel %d\n", __func__, i); ++ return rc; ++ } ++ desc_virt += MMBI_DESC_SIZE_CHANNEL; ++ } ++ } ++ ++ for (i = 0; i < mmbi->num_of_channels; i++) { ++ vpscb = &mmbi->chan_desc[i].buffer_desc; ++ memset_io(mmbi->desc_virt + vpscb->h_ros_p, 0, 8); ++ memset_io(mmbi->desc_virt + vpscb->h_rws_p, 0, 8); ++ ++ mmbi_set_up(mmbi->desc_virt + vpscb->h_ros_p); ++ mmbi_set_ready(mmbi->desc_virt + vpscb->h_ros_p); ++ mmbi->chan_desc[i].priv->state = INIT_COMPLETED; ++ } + + return rc; +} ++EXPORT_SYMBOL_GPL(mmbi_instance_init_bmc); +diff --git a/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.h b/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.h +--- a/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-mmbi-bmc.h 2026-07-17 08:44:55.292306520 +0000 +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_MMBI_BMC_H__ ++#define __ASPEED_MMBI_BMC_H__ + -+static long ast2700_espi_oob_dma_put_tx(struct file *fp, -+ struct aspeed_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) -+{ -+ struct ast2700_espi_oob_dma_tx_desc *d; -+ struct ast2700_espi_oob_dma_tx_desc *tx_descs; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ u32 rptr, wptr; -+ u8 *pkt; -+ int rc; ++#include "aspeed-mmbi.h" + -+ espi = container_of(oob, struct aspeed_espi, oob); ++int mmbi_instance_init_bmc(struct mmbi_ins_desc *mmbi); ++void mmbi_channel_irq_bmc(struct mmbi_chan_desc *chan); + -+ pkt = vzalloc(ioc->pkt_len); -+ if (!pkt) -+ return -ENOMEM; ++#endif /* __ASPEED_MMBI_BMC_H__ */ +diff --git a/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.c b/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.c +--- a/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.c 2026-07-17 08:44:55.293306504 +0000 +@@ -0,0 +1,230 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* Implements MMBI protocol for Host side with VPSCB buffer type ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aspeed-mmbi.h" ++#include "aspeed-mmbi-internal.h" ++#include "aspeed-mmbi-host.h" + -+ hdr = (struct espi_comm_hdr *)pkt; ++static void mmbi_channel_state_handler(struct mmbi_chan_desc *chan, u8 __iomem *desc_virt) ++{ ++ enum mmbi_state prev_state, cur_state; ++ struct mmbi_buf_vpscb *vpscb; ++ u8 __iomem *host_rws_virt; ++ u8 __iomem *host_ros_virt; ++ u32 host_int_location; ++ u32 unhandled_len, avail_len; + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ prev_state = chan->priv->state; + -+ /* kick HW to update descriptor read/write pointer */ -+ writel(ESPI_CH2_TX_DESC_RPTR_UPT, espi->regs + ESPI_CH2_TX_DESC_RPTR); ++ host_int_location = chan->mmbi->host_int_location; ++ vpscb = &chan->buffer_desc; ++ host_rws_virt = desc_virt + vpscb->h_rws_p; ++ host_ros_virt = desc_virt + vpscb->h_ros_p; ++ mmbi_channel_state_update(chan, desc_virt); ++ cur_state = chan->priv->state; + -+ rptr = readl(espi->regs + ESPI_CH2_TX_DESC_RPTR); -+ wptr = readl(espi->regs + ESPI_CH2_TX_DESC_WPTR); ++ switch (cur_state) { ++ case INIT_COMPLETED: ++ mmbi_set_ready(host_rws_virt); ++ mmbi_set_up(host_rws_virt); ++ chan->priv->state = NORMAL_RUNTIME; ++ break; ++ case NORMAL_RUNTIME: ++ mmbi_set_ready(host_rws_virt); + -+ if (((wptr + 1) % OOB_DMA_DESC_NUM) == rptr) { -+ rc = -EBUSY; -+ goto free_n_out; ++ unhandled_len = mmbi_channel_unhandled_length(host_rws_virt, ++ host_ros_virt, chan->b2h_l); ++ if (unhandled_len >= MMBI_PKT_MIN_SIZE) ++ chan->priv->rx_ready = true; ++ else ++ chan->priv->rx_ready = false; ++ avail_len = mmbi_channel_avail_length(host_ros_virt, host_rws_virt, chan->h2b_l); ++ if (avail_len <= MMBI_PKT_MIN_SIZE) ++ chan->priv->tx_ready = false; ++ else ++ chan->priv->tx_ready = true; ++ break; ++ case RESET_REQ_BY_BMC: ++ // TODO: consume all data from B2H buffer ++ mmbi_clr_ready(host_rws_virt); ++ mmbi_set_rst(host_rws_virt); ++ chan->priv->state = RESET_ACKED; ++ break; ++ default: ++ /* other states do not require action from host side */ ++ break; + } ++} + -+ tx_descs = (struct ast2700_espi_oob_dma_tx_desc *)oob->dma.txd_virt; -+ d = &tx_descs[wptr]; -+ d->cyc = hdr->cyc; -+ d->tag = hdr->tag; -+ d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); -+ d->msg_type = OOB_DMA_DESC_CUSTOM; ++static int mmbi_instance_signature_check(u8 __iomem *desc_virt) ++{ ++ char signature[sizeof(MMBI_SIGNATURE) - 1]; + -+ memcpy(oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, ioc->pkt_len - sizeof(*hdr)); ++ memcpy_fromio(signature, desc_virt, sizeof(MMBI_SIGNATURE) - 1); ++ if (memcmp(signature, MMBI_SIGNATURE, sizeof(MMBI_SIGNATURE) - 1) != 0) ++ return -EINVAL; + -+ dma_wmb(); ++ return 0; ++} + -+ wptr = (wptr + 1) % OOB_DMA_DESC_NUM; -+ writel(wptr | ESPI_CH2_TX_DESC_WPTR_VALID, espi->regs + ESPI_CH2_TX_DESC_WPTR); ++static int mmbi_chan_init_vpscb_host(u8 __iomem *buf_virt, struct mmbi_buf_vpscb *buf_desc) ++{ ++ u32 offset = 0; + -+ rc = 0; ++ buf_desc->host_int_val_ch = ioread8(buf_virt + offset++) & MMBI_INT_VAL_MASK; ++ buf_desc->bmc_int_val_ch = ioread8(buf_virt + offset++) & MMBI_INT_VAL_MASK; + -+free_n_out: -+ vfree(pkt); ++ offset += 5; /* reserved bytes */ + -+ return rc; ++ memcpy_fromio(&buf_desc->h_ros_p, buf_virt + offset, sizeof(buf_desc->h_ros_p)); ++ memcpy_fromio(&buf_desc->h_rws_p, buf_virt + offset + 4, sizeof(buf_desc->h_rws_p)); ++ buf_desc->h_ros_p = be32_to_cpu(buf_desc->h_ros_p); ++ buf_desc->h_rws_p = be32_to_cpu(buf_desc->h_rws_p); ++ buf_desc->h_ros_p = MMBI_BUF_ADDR_ALIGN(buf_desc->h_ros_p); ++ buf_desc->h_rws_p = MMBI_BUF_ADDR_ALIGN(buf_desc->h_rws_p); ++ ++ pr_info("%s: h_ros_p=0x%x, h_rws_p=0x%x\n", __func__, ++ buf_desc->h_ros_p, buf_desc->h_rws_p); ++ return 0; +} + -+static long ast2700_espi_oob_put_tx(struct file *fp, -+ struct aspeed_espi_oob *oob, -+ struct aspeed_espi_ioc *ioc) ++static int mmbi_channel_init_host(u8 __iomem *desc_virt, struct mmbi_chan_desc *chan_desc) +{ -+ u32 reg, cyc, tag, len; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ u8 *pkt; -+ int i, rc; ++ struct mmbi_buf_vpscb *buffer_desc; ++ int rc; + -+ espi = container_of(oob, struct aspeed_espi, oob); ++ if (!desc_virt || !IS_ALIGNED((unsigned long)desc_virt, sizeof(long))) { ++ pr_err("%s: invalid desc_virt address %p\n", __func__, desc_virt); ++ return -EINVAL; ++ } + -+ if (!mutex_trylock(&oob->tx_mtx)) -+ return -EAGAIN; ++ chan_desc->priv->peer_ready = false; ++ memcpy_fromio(&chan_desc->b2h_ba_offset, desc_virt, sizeof(chan_desc->b2h_ba_offset)); ++ memcpy_fromio(&chan_desc->h2b_ba_offset, desc_virt + 4, sizeof(chan_desc->h2b_ba_offset)); ++ memcpy_fromio(&chan_desc->b2h_l, desc_virt + 8, sizeof(chan_desc->b2h_l)); ++ memcpy_fromio(&chan_desc->h2b_l, desc_virt + 12, sizeof(chan_desc->h2b_l)); ++ chan_desc->b2h_ba_offset = be32_to_cpu(chan_desc->b2h_ba_offset); ++ chan_desc->h2b_ba_offset = be32_to_cpu(chan_desc->h2b_ba_offset); ++ chan_desc->b2h_l = be32_to_cpu(chan_desc->b2h_l); ++ chan_desc->h2b_l = be32_to_cpu(chan_desc->h2b_l); ++ chan_desc->b2h_ba_offset = MMBI_BUF_ADDR_ALIGN(chan_desc->b2h_ba_offset); ++ chan_desc->h2b_ba_offset = MMBI_BUF_ADDR_ALIGN(chan_desc->h2b_ba_offset); ++ desc_virt += 16; ++ chan_desc->buffer_type = ioread8(desc_virt++) & MMBI_BUF_TYPE_MASK; + -+ if (oob->dma.enable) { -+ rc = ast2700_espi_oob_dma_put_tx(fp, oob, ioc); -+ goto unlock_mtx_n_out; ++ pr_info("%s: b2h_ba_offset=0x%x, h2b_ba_offset=0x%x\n", __func__, ++ chan_desc->b2h_ba_offset, chan_desc->h2b_ba_offset); ++ pr_info("%s: b2h_l=0x%x, h2b_l=0x%x\n", __func__, chan_desc->b2h_l, chan_desc->h2b_l); ++ pr_info("%s: buffer_type=%u\n", __func__, chan_desc->buffer_type); ++ ++ if (chan_desc->buffer_type == MMBI_BUFFER_TYPE_VPSCB) { ++ buffer_desc = &chan_desc->buffer_desc; ++ rc = mmbi_chan_init_vpscb_host(desc_virt, buffer_desc); + } + -+ reg = readl(espi->regs + ESPI_CH2_TX_CTRL); -+ if (reg & ESPI_CH2_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_mtx_n_out; ++ if (!rc) ++ mmbi_channel_state_handler(chan_desc, chan_desc->mmbi->desc_virt); ++ ++ return rc; ++} ++ ++void mmbi_channel_irq_host(struct mmbi_chan_desc *chan) ++{ ++ mmbi_channel_state_handler(chan, chan->mmbi->desc_virt); ++} ++EXPORT_SYMBOL_GPL(mmbi_channel_irq_host); ++ ++int mmbi_instance_init_host(struct mmbi_ins_desc *mmbi) ++{ ++ struct device *dev = mmbi->dev; ++ struct mmbi_chan_desc *chan; ++ int rc; ++ u8 __iomem *desc_virt; ++ u8 chan_idx, val; ++ ++ desc_virt = mmbi->desc_virt; ++ if (mmbi_instance_signature_check(desc_virt)) { ++ dev_err(dev, "Invalid MMBI signature\n"); ++ return -EINVAL; + } + -+ if (ioc->pkt_len > ESPI_MAX_PKT_LEN) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; ++ desc_virt += sizeof(MMBI_SIGNATURE) - 1; // exclude signature from descriptor parsing ++ chan_idx = 0; ++ ++ mmbi->mmbi_version = ioread8(desc_virt++) & MMBI_VERSION_MASK; ++ if (mmbi->mmbi_version < MMBI_VERSION_1_0 || mmbi->mmbi_version > MMBI_VERSION_1_1) { ++ dev_err(dev, "Unsupported MMBI version: %d\n", mmbi->mmbi_version); ++ return -EINVAL; + } + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; ++ if (mmbi->mmbi_version == MMBI_VERSION_1_1) { ++ val = ioread8(desc_virt++); ++ mmbi->os_use = val & MMBI_OS_USE_MASK; ++ mmbi->num_of_channels = (val & MMBI_NOI_MASK) >> 4; ++ if (mmbi->num_of_channels > MMBI_MAX_CHANNELS || mmbi->num_of_channels == 0) { ++ dev_err(dev, "Invalid number of instances: %d\n", mmbi->num_of_channels); ++ return -EINVAL; ++ } ++ } else { ++ mmbi->os_use = ioread8(desc_virt++) & MMBI_OS_USE_MASK; + } ++ dev_info(dev, "MMBI version 0x%x, os_use %u, num_of_channels %u\n", ++ mmbi->mmbi_version, mmbi->os_use, mmbi->num_of_channels); + -+ hdr = (struct espi_comm_hdr *)pkt; ++ chan = &mmbi->chan_desc[chan_idx]; ++ chan->index = chan_idx; ++ chan->mmbi = mmbi; ++ rc = mmbi_channel_init_host(desc_virt, chan); ++ if (rc) { ++ dev_err(dev, "Failed to initialize MMBI channel %d\n", chan_idx); ++ return rc; ++ } + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; ++ desc_virt += MMBI_DESC_SIZE_CHANNEL; ++ chan_idx++; ++ ++ mmbi->host_int_type = ioread8(desc_virt++) & MMBI_INT_TYPE_MASK; ++ mmbi->host_int_location = ioread8(desc_virt++); ++ desc_virt += 3; // reserved 3 bytes ++ mmbi->host_int_value = ioread8(desc_virt++); ++ mmbi->bmc_int_type = ioread8(desc_virt++) & MMBI_INT_TYPE_MASK; ++ memcpy_fromio(&mmbi->bmc_int_location, desc_virt, sizeof(mmbi->bmc_int_location)); ++ mmbi->bmc_int_location = be32_to_cpu(mmbi->bmc_int_location); ++ desc_virt += sizeof(mmbi->bmc_int_location) + 4; // reserved 4 bytes ++ mmbi->bmc_int_value = ioread8(desc_virt++); ++ ++ desc_virt += 8; // reserved 8 bytes ++ ++ dev_info(dev, "host_int_type=%u, host_int_location=0x%x, host_int_value=0x%x\n", ++ mmbi->host_int_type, mmbi->host_int_location, mmbi->host_int_value); ++ dev_info(dev, "bmc_int_type=%u, bmc_int_location=0x%x, bmc_int_value=0x%x\n", ++ mmbi->bmc_int_type, mmbi->bmc_int_location, mmbi->bmc_int_value); ++ dev_info(dev, "offset after init header=0x%lx\n", ++ (unsigned long)(desc_virt - mmbi->desc_virt)); ++ ++ if (chan_idx < mmbi->num_of_channels) { ++ dev_info(dev, "Initializing additional MMBI channels\n"); ++ for (; chan_idx < mmbi->num_of_channels; chan_idx++) { ++ chan = &mmbi->chan_desc[chan_idx]; ++ chan->index = chan_idx; ++ chan->mmbi = mmbi; ++ rc = mmbi_channel_init_host(desc_virt, chan); ++ if (rc) { ++ dev_err(dev, "Failed to initialize MMBI channel %d\n", chan_idx); ++ return rc; ++ } ++ desc_virt += MMBI_DESC_SIZE_CHANNEL; ++ } + } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mmbi_instance_init_host); +diff --git a/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.h b/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.h +--- a/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-mmbi-host.h 2026-07-17 08:44:55.293306504 +0000 +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_MMBI_HOST_H__ ++#define __ASPEED_MMBI_HOST_H__ + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_CH2_TX_DATA); ++#include "aspeed-mmbi.h" + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++int mmbi_instance_init_host(struct mmbi_ins_desc *mmbi); ++void mmbi_channel_irq_host(struct mmbi_chan_desc *chan); + -+ reg = FIELD_PREP(ESPI_CH2_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_CH2_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_CH2_TX_CTRL_LEN, len) -+ | ESPI_CH2_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_CH2_TX_CTRL); ++#endif /* __ASPEED_MMBI_HOST_H__ */ +diff --git a/drivers/soc/aspeed/mmbi/aspeed-mmbi-internal.h b/drivers/soc/aspeed/mmbi/aspeed-mmbi-internal.h +--- a/drivers/soc/aspeed/mmbi/aspeed-mmbi-internal.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-mmbi-internal.h 2026-07-17 08:44:55.293306504 +0000 +@@ -0,0 +1,32 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_MMBI_INTERNAL_H__ ++#define __ASPEED_MMBI_INTERNAL_H__ + -+ rc = 0; ++#include + -+free_n_out: -+ vfree(pkt); ++#include "aspeed-mmbi.h" ++ ++struct net_device; ++ ++struct mmbi_chan_priv { ++ enum mmbi_state state; /* Current MMBI state of this channel */ ++ struct miscdevice miscdev; /* MMBI char device for this channel */ ++ wait_queue_head_t rx_wait; /* Wait queue for rx data available */ ++ wait_queue_head_t tx_wait; /* Wait queue for tx data available */ ++ bool rx_ready; /* Flag indicating if has data to be read */ ++ bool tx_ready; /* Flag indicating if ready to transmit data */ ++ spinlock_t rx_lock; /* Lock to prevent rx ready flag race */ ++ spinlock_t tx_lock; /* Lock to prevent tx ready flag race */ ++ u32 read_ptr; /* Current cached read pointer */ ++ u32 write_ptr; /* Current cached write pointer */ ++ bool peer_ready; /* Flag indicating peer ready bit */ ++ struct delayed_work poll_work; /* Work struct for polling check status */ ++ bool running; ++ struct mmbi_chan_desc *chan; /* Back pointer used by misc open */ ++ struct net_device *ndev; /* MCTP netdev when the channel selects it */ ++}; ++ ++#endif /* __ASPEED_MMBI_INTERNAL_H__ */ +diff --git a/drivers/soc/aspeed/mmbi/aspeed-mmbi.c b/drivers/soc/aspeed/mmbi/aspeed-mmbi.c +--- a/drivers/soc/aspeed/mmbi/aspeed-mmbi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-mmbi.c 2026-07-17 08:44:55.293306504 +0000 +@@ -0,0 +1,939 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * ASPEED MMBI (Memory Mapped Bus Interface) Protocol Driver ++ * ++ * Copyright (c) 2026 ASPEED Technology Inc. ++ */ + -+unlock_mtx_n_out: -+ mutex_unlock(&oob->tx_mtx); ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+ return rc; -+} ++#include ++#include ++#include + -+static long ast2700_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -+{ -+ struct aspeed_espi_oob *oob; -+ struct aspeed_espi_ioc ioc; ++#include "aspeed-mmbi.h" ++#include "aspeed-mmbi-internal.h" ++#include "aspeed-mmbi-bmc.h" ++#include "aspeed-mmbi-host.h" + -+ oob = container_of(fp->private_data, struct aspeed_espi_oob, mdev); ++static DEFINE_IDA(mmbi_ida); + -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; ++#define MMBI_MCTP_MIN_MTU (sizeof(struct mctp_hdr) + 64) ++#define MMBI_MCTP_RX_BUF_SIZE SZ_4K + -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) -+ return -EINVAL; ++struct mmbi_mctp_netdev { ++ struct mmbi_chan_desc *chan; ++}; + -+ switch (cmd) { -+ case ASPEED_ESPI_OOB_GET_RX: -+ return ast2700_espi_oob_get_rx(fp, oob, &ioc); -+ case ASPEED_ESPI_OOB_PUT_TX: -+ return ast2700_espi_oob_put_tx(fp, oob, &ioc); -+ }; ++static void mmbi_channel_start_polling(struct mmbi_chan_desc *chan_desc) ++{ ++ if (chan_desc->priv->running) ++ return; + -+ return -EINVAL; ++ schedule_delayed_work(&chan_desc->priv->poll_work, ++ msecs_to_jiffies(chan_desc->poll_interval_ms)); ++ chan_desc->priv->running = true; +} + -+static const struct file_operations ast2700_espi_oob_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2700_espi_oob_ioctl, -+}; -+ -+static void ast2700_espi_oob_isr(struct aspeed_espi *espi) ++static void mmbi_channel_stop_polling(struct mmbi_chan_desc *chan_desc) +{ -+ struct aspeed_espi_oob *oob; -+ unsigned long flags; -+ u32 sts; ++ if (!chan_desc->priv->running) ++ return; + -+ oob = &espi->oob; ++ cancel_delayed_work_sync(&chan_desc->priv->poll_work); ++ chan_desc->priv->running = false; ++} + -+ sts = readl(espi->regs + ESPI_CH2_INT_STS); ++static void mmbi_update_rd_ptr(u8 __iomem *read_ptr, u32 read_size, u32 read_buf_size) ++{ ++ u32 reg, low, addr, reg_rb; ++ u32 retry_cnt; ++ if (!read_size || (read_size & 3)) { ++ pr_err("%s: read_size must be non-zero and multiple of 4\n", __func__); ++ return; ++ } + -+ if (sts & ESPI_CH2_INT_STS_RX_CMPLT) { -+ writel(ESPI_CH2_INT_STS_RX_CMPLT, espi->regs + ESPI_CH2_INT_STS); ++ read_ptr += sizeof(u32); /* read pointer is located at offset 4 in the structure */ ++ reg = ioread32(read_ptr); + -+ spin_lock_irqsave(&oob->lock, flags); -+ oob->rx_ready = true; -+ spin_unlock_irqrestore(&oob->lock, flags); ++ low = reg & ~MMBI_PTR_ADDR_MASK; /* bits [1:0] */ ++ addr = reg & MMBI_PTR_ADDR_MASK; /* bits [31:2] */ + -+ wake_up_interruptible(&oob->wq); -+ } ++ addr = (addr + read_size) % read_buf_size; ++ addr &= MMBI_PTR_ADDR_MASK; ++ retry_cnt = 0; ++ iowrite32(addr | low, read_ptr); ++ do { ++ reg_rb = ioread32(read_ptr); ++ retry_cnt++; ++ if (retry_cnt > 5) { ++ pr_info("%s: read pointer update not reflected after %u retries, reg 0x%x, expected 0x%x\n", ++ __func__, retry_cnt, reg_rb, addr | low); ++ break; ++ } ++ } while (reg_rb != (addr | low)); +} + -+static void ast2700_espi_oob_reset(struct aspeed_espi *espi) ++static void mmbi_update_wr_ptr(u8 __iomem *write_ptr, u32 write_size, u32 write_buf_size) +{ -+ struct aspeed_espi_oob *oob; -+ struct ast2700_espi_oob_dma_tx_desc *tx_desc; -+ struct ast2700_espi_oob_dma_rx_desc *rx_desc; -+ dma_addr_t tx_addr, rx_addr; -+ u32 reg; -+ int i; ++ u32 reg, low, addr, reg_rb; ++ u32 retry_cnt; ++ if (!write_size || (write_size & 3)) { ++ pr_err("%s: write_size must be non-zero and multiple of 4\n", __func__); ++ return; ++ } + -+ oob = &espi->oob; ++ reg = ioread32(write_ptr); + -+ writel(0x0, espi->regs + ESPI_CH2_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_CH2_INT_STS); ++ low = reg & ~MMBI_PTR_ADDR_MASK; /* bits [1:0] */ ++ addr = reg & MMBI_PTR_ADDR_MASK; /* bits [31:2] */ + -+ reg = readl(espi->regs + ESPI_CH2_CTRL); -+ reg &= ~(ESPI_CH2_CTRL_TX_RST -+ | ESPI_CH2_CTRL_RX_RST -+ | ESPI_CH2_CTRL_TX_DMA_EN -+ | ESPI_CH2_CTRL_RX_DMA_EN -+ | ESPI_CH2_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH2_CTRL); ++ addr = (addr + write_size) % write_buf_size; ++ addr &= MMBI_PTR_ADDR_MASK; + -+ udelay(1); ++ retry_cnt = 0; ++ iowrite32(addr | low, write_ptr); ++ do { ++ reg_rb = ioread32(write_ptr); ++ retry_cnt++; ++ if (retry_cnt > 5) { ++ pr_info("%s: write pointer update not reflected after %u retries, reg 0x%x, expected 0x%x\n", ++ __func__, retry_cnt, reg_rb, addr | low); ++ break; ++ } ++ } while (reg_rb != (addr | low)); ++} + -+ reg |= (ESPI_CH2_CTRL_TX_RST | ESPI_CH2_CTRL_RX_RST); -+ writel(reg, espi->regs + ESPI_CH2_CTRL); ++static void mmbi_parse_hdr(u8 __iomem *buf_virt, u32 *payload_len, u8 *padding, ++ u8 *protocol) ++{ ++ u8 hdr[MMBI_PKT_HDR_SIZE]; ++ u32 word24; ++ int i; + -+ if (oob->dma.enable) { -+ tx_addr = oob->dma.tx_addr; -+ rx_addr = oob->dma.rx_addr; ++ /* Read header bytes via ioread8() */ ++ for (i = 0; i < MMBI_PKT_HDR_SIZE; i++) ++ hdr[i] = ioread8(buf_virt + i); + -+ tx_desc = (struct ast2700_espi_oob_dma_tx_desc *)oob->dma.txd_virt; -+ rx_desc = (struct ast2700_espi_oob_dma_rx_desc *)oob->dma.rxd_virt; ++ /* Byte0..2 form the 24-bit field: [23:2]=len, [1:0]=pad */ ++ word24 = ((u32)hdr[0] << 16) | ((u32)hdr[1] << 8) | (u32)hdr[2]; + -+ for (i = 0; i < OOB_DMA_DESC_NUM; ++i) { -+ tx_desc[i].data_addrh = upper_32_bits(tx_addr); -+ tx_desc[i].data_addrl = tx_addr & 0xffffffff; -+ tx_addr += PAGE_SIZE; ++ *padding = word24 & 0x3; ++ *payload_len = (word24 >> 2) * 4 - *padding; ++ *protocol = hdr[3] & MMBI_PKT_PROTOCOL_MASK; ++} + -+ rx_desc[i].data_addrh = upper_32_bits(rx_addr); -+ rx_desc[i].data_addrl = rx_addr & 0xffffffff; -+ rx_desc[i].dirty = 0; -+ rx_addr += PAGE_SIZE; -+ } ++static int mmbi_read(struct mmbi_chan_desc *chan, u8 *data, size_t read_len) ++{ ++ struct mmbi_ins_desc *mmbi = chan->mmbi; ++ u32 read_ptr, write_ptr, read_offset, buf_size, unhandled_len, base_addr; ++ u32 data_len, pkt_len; ++ u8 protocol, padding; ++ size_t first_part; ++ u8 __iomem *desc_virt; + -+ writel(upper_32_bits(oob->dma.txd_addr), espi->regs + ESPI_CH2_TX_DMAH); -+ writel(oob->dma.txd_addr & 0xffffffff, espi->regs + ESPI_CH2_TX_DMAL); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_TX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_CH2_TX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_TX_DESC_EPTR); ++ if (read_len == 0) ++ return -EINVAL; + -+ writel(upper_32_bits(oob->dma.rxd_addr), espi->regs + ESPI_CH2_RX_DMAH); -+ writel(oob->dma.rxd_addr & 0xffffffff, espi->regs + ESPI_CH2_RX_DMAL); -+ writel(OOB_DMA_RPTR_KEY, espi->regs + ESPI_CH2_RX_DESC_RPTR); -+ writel(0x0, espi->regs + ESPI_CH2_RX_DESC_WPTR); -+ writel(OOB_DMA_DESC_NUM, espi->regs + ESPI_CH2_RX_DESC_EPTR); ++ if (mmbi->role == MMBI_ROLE_BMC) { ++ read_ptr = chan->buffer_desc.h_ros_p; ++ write_ptr = chan->buffer_desc.h_rws_p; ++ buf_size = chan->h2b_l; ++ base_addr = chan->h2b_ba_offset; ++ } else { ++ read_ptr = chan->buffer_desc.h_rws_p; ++ write_ptr = chan->buffer_desc.h_ros_p; ++ buf_size = chan->b2h_l; ++ base_addr = chan->b2h_ba_offset; ++ } ++ ++ unhandled_len = mmbi_channel_unhandled_length(mmbi->desc_virt + read_ptr, ++ mmbi->desc_virt + write_ptr, ++ buf_size); ++ ++ if (unhandled_len == 0) ++ return 0; + -+ reg = readl(espi->regs + ESPI_CH2_CTRL) -+ | ESPI_CH2_CTRL_TX_DMA_EN -+ | ESPI_CH2_CTRL_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CH2_CTRL); ++ desc_virt = mmbi->desc_virt + base_addr; ++ data_len = 0; ++ read_offset = MMBI_PTR_ADDR_ALIGN(be32_to_cpu(ioread32be(mmbi->desc_virt + read_ptr + sizeof(u32)))); ++ /* MMBI read/write has 4byte unit, so reading header will never have bundary issues */ ++ mmbi_parse_hdr(desc_virt + read_offset, &data_len, &padding, &protocol); ++ ++ if (data_len > read_len) { ++ pr_warn("%s: not enough space(%ld) to read data(%u) for chan %u\n", ++ __func__, read_len, data_len, chan->index); ++ pr_warn("%s: read_offset %d padding %d protocol %d buf_size %d", ++ __func__, read_offset, padding, protocol, buf_size); ++ return -EINVAL; ++ } ++ pkt_len = MMBI_PKT_HDR_SIZE + data_len + padding; + -+ /* activate RX DMA to make OOB_FREE */ -+ reg = readl(espi->regs + ESPI_CH2_RX_DESC_WPTR) | ESPI_CH2_RX_DESC_WPTR_VALID; -+ writel(reg, espi->regs + ESPI_CH2_RX_DESC_WPTR); ++ if (unhandled_len > pkt_len) { ++ pr_warn("%s: more data unhandled than current packet length for chan %u, unhandled_len %u pkt_len %u\n", ++ __func__, chan->index, unhandled_len, pkt_len); + } + -+ writel(ESPI_CH2_INT_EN_RX_CMPLT, espi->regs + ESPI_CH2_INT_EN); ++ /* move offset to payload */ ++ read_offset = (read_offset + MMBI_PKT_HDR_SIZE) % buf_size; ++ if (protocol == MMBI_PROTOCOL_MCTP) { ++ if (read_offset + data_len <= buf_size) { ++ memcpy_fromio(data, desc_virt + read_offset, data_len); ++ } else { ++ first_part = buf_size - read_offset; ++ memcpy_fromio(data, desc_virt + read_offset, first_part); ++ memcpy_fromio(data + first_part, desc_virt, data_len - first_part); ++ } ++ } else { ++ pr_warn("%s: unsupported protocol %u on chan %u, discard packet\n", ++ __func__, protocol, chan->index); ++ } ++ mmbi_update_rd_ptr(mmbi->desc_virt + read_ptr, pkt_len, buf_size); + -+ reg = readl(espi->regs + ESPI_CH2_CTRL) | ESPI_CH2_CTRL_SW_RDY; -+ writel(reg, espi->regs + ESPI_CH2_CTRL); ++ return protocol == MMBI_PROTOCOL_MCTP ? data_len : 0; +} + -+int ast2700_espi_oob_probe(struct aspeed_espi *espi) ++static int mmbi_write(struct mmbi_chan_desc *chan, const u8 *data, size_t data_len, u8 protocol) +{ -+ struct aspeed_espi_oob *oob; -+ struct device *dev; -+ int rc; -+ -+ dev = espi->dev; ++ struct mmbi_ins_desc *mmbi = chan->mmbi; ++ u32 read_ptr, write_ptr, write_offset, buf_size, avail_len, val, base_addr, total_len; ++ u8 __iomem *desc_virt; ++ size_t first_part; ++ u8 padding = 0; + -+ oob = &espi->oob; ++ if (data_len == 0) ++ return -EINVAL; + -+ init_waitqueue_head(&oob->wq); ++ if (!chan->priv->peer_ready) { ++ pr_info("%s: chan %d peer not ready to receive data", __func__, ++ chan->index); ++ return -EAGAIN; ++ } + -+ spin_lock_init(&oob->lock); ++ if (mmbi->role == MMBI_ROLE_BMC) { ++ read_ptr = chan->buffer_desc.h_rws_p; ++ write_ptr = chan->buffer_desc.h_ros_p; ++ buf_size = chan->b2h_l; ++ base_addr = chan->b2h_ba_offset; ++ } else { ++ read_ptr = chan->buffer_desc.h_ros_p; ++ write_ptr = chan->buffer_desc.h_rws_p; ++ buf_size = chan->h2b_l; ++ base_addr = chan->h2b_ba_offset; ++ } + -+ mutex_init(&oob->tx_mtx); -+ mutex_init(&oob->rx_mtx); ++ avail_len = mmbi_channel_avail_length(mmbi->desc_virt + read_ptr, ++ mmbi->desc_virt + write_ptr, ++ buf_size); + -+ oob->dma.enable = of_property_read_bool(dev->of_node, "oob-dma-mode"); -+ if (oob->dma.enable) { -+ oob->dma.txd_virt = dmam_alloc_coherent(dev, -+ sizeof(struct ast2700_espi_oob_dma_tx_desc) * -+ OOB_DMA_DESC_NUM, -+ &oob->dma.txd_addr, GFP_KERNEL); -+ if (!oob->dma.txd_virt) { -+ dev_err(dev, "cannot allocate DMA TX descriptor\n"); -+ return -ENOMEM; -+ } -+ oob->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.tx_addr, GFP_KERNEL); -+ if (!oob->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++ total_len = data_len + ((protocol == MMBI_PROTOCOL_MCTP) ? 4 : 0); + -+ oob->dma.rxd_virt = dmam_alloc_coherent(dev, -+ sizeof(struct ast2700_espi_oob_dma_rx_desc) * -+ OOB_DMA_DESC_NUM, -+ &oob->dma.rxd_addr, GFP_KERNEL); -+ if (!oob->dma.rxd_virt) { -+ dev_err(dev, "cannot allocate DMA RX descriptor\n"); -+ return -ENOMEM; -+ } ++ /* at least 8bytes for 4byte header and 4byte data */ ++ if (avail_len < MMBI_PKT_MIN_SIZE) { ++ pr_info("%s: no available buffer to write for chan %u\n", ++ __func__, chan->index); ++ return -EAGAIN; ++ } + -+ oob->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, &oob->dma.rx_addr, GFP_KERNEL); -+ if (!oob->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++ if (total_len > avail_len) { ++ pr_info("%s: no available buffer to write for chan %u, requested %d, avail %d\n", ++ __func__, chan->index, total_len, avail_len); ++ return -EAGAIN; + } + -+ oob->mdev.parent = dev; -+ oob->mdev.minor = MISC_DYNAMIC_MINOR; -+ oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-oob%d", DEVICE_NAME, espi->dev_id); -+ oob->mdev.fops = &ast2700_espi_oob_fops; -+ rc = misc_register(&oob->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", oob->mdev.name); -+ return rc; ++ write_offset = MMBI_PTR_ADDR_ALIGN(be32_to_cpu(ioread32be(mmbi->desc_virt + write_ptr))); ++ desc_virt = mmbi->desc_virt + base_addr; ++ /* Multi-protocol header */ ++ if (protocol == MMBI_PROTOCOL_MCTP) { ++ padding = (4 - (data_len & 3)) & 3; /* Pad to 4 bytes */ ++ total_len += padding; ++ val = (data_len + padding) >> 2; ++ val &= 0x3FFFFF; /* 22 bits for length */ ++ iowrite8((val >> 14) & 0xff, desc_virt + write_offset); ++ write_offset++; ++ iowrite8((val >> 6) & 0xff, desc_virt + write_offset); ++ write_offset++; ++ iowrite8(((val << 2) & 0xff) | padding, desc_virt + write_offset); ++ write_offset++; ++ iowrite8(MMBI_PKT_PROTOCOL_MASK & protocol, desc_virt + write_offset); ++ write_offset++; ++ } ++ ++ write_offset %= buf_size; ++ if (write_offset + data_len < buf_size) { ++ memcpy_toio(desc_virt + write_offset, data, data_len); ++ } else { ++ first_part = buf_size - write_offset; ++ memcpy_toio(desc_virt + write_offset, data, first_part); ++ memcpy_toio(desc_virt, data + first_part, data_len - first_part); + } ++ mmbi_update_wr_ptr(mmbi->desc_virt + write_ptr, total_len, buf_size); ++ return data_len; ++} + -+ ast2700_espi_oob_reset(espi); ++static int mmbi_misc_open(struct inode *inode, struct file *file) ++{ ++ struct miscdevice *miscdev = file->private_data; ++ struct mmbi_chan_priv *priv = container_of(miscdev, struct mmbi_chan_priv, ++ miscdev); ++ struct mmbi_chan_desc *chan_desc = priv->chan; + ++ file->private_data = chan_desc; ++ mmbi_channel_state_update(chan_desc, chan_desc->mmbi->desc_virt); ++ mmbi_channel_start_polling(chan_desc); + return 0; +} + -+int ast2700_espi_oob_remove(struct aspeed_espi *espi) ++static int mmbi_misc_release(struct inode *inode, struct file *file) +{ -+ struct aspeed_espi_oob *oob; -+ struct device *dev; -+ u32 reg; ++ struct mmbi_chan_desc *chan_desc = file->private_data; + -+ dev = espi->dev; ++ mmbi_channel_stop_polling(chan_desc); ++ return 0; ++} + -+ oob = &espi->oob; ++static ssize_t mmbi_misc_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct mmbi_chan_desc *chan_desc = file->private_data; ++ ssize_t ret; ++ void *kbuf; + -+ writel(0x0, espi->regs + ESPI_CH2_INT_EN); ++ if (count == 0) ++ return 0; + -+ reg = readl(espi->regs + ESPI_CH2_CTRL); -+ reg &= ~(ESPI_CH2_CTRL_TX_DMA_EN -+ | ESPI_CH2_CTRL_RX_DMA_EN -+ | ESPI_CH2_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH2_CTRL); ++ kbuf = kmalloc(count, GFP_KERNEL); ++ if (!kbuf) ++ return -ENOMEM; + -+ if (oob->dma.enable) { -+ dmam_free_coherent(dev, sizeof(struct ast2700_espi_oob_dma_tx_desc) * OOB_DMA_DESC_NUM, -+ oob->dma.txd_virt, oob->dma.txd_addr); -+ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, -+ oob->dma.tx_virt, oob->dma.tx_addr); -+ dmam_free_coherent(dev, sizeof(struct ast2700_espi_oob_dma_rx_desc) * OOB_DMA_DESC_NUM, -+ oob->dma.rxd_virt, oob->dma.rxd_addr); -+ dmam_free_coherent(dev, PAGE_SIZE * OOB_DMA_DESC_NUM, -+ oob->dma.rx_virt, oob->dma.rx_addr); ++ spin_lock(&chan_desc->priv->rx_lock); ++ chan_desc->priv->rx_ready = false; ++ ret = mmbi_read(chan_desc, kbuf, count); ++ spin_unlock(&chan_desc->priv->rx_lock); ++ if (ret > 0) { ++ if (copy_to_user(buf, kbuf, ret)) { ++ pr_err("%s: chan %u copy %zd bytes to user failed\n", ++ __func__, chan_desc->index, ret); ++ ret = -EFAULT; ++ } ++ } else { ++ pr_err("%s: read failed for chan %u ret %zd\n", __func__, chan_desc->index, ret); + } ++ kfree(kbuf); ++ return ret; ++} + -+ mutex_destroy(&oob->tx_mtx); -+ mutex_destroy(&oob->rx_mtx); ++static ssize_t mmbi_misc_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct mmbi_chan_desc *chan_desc = file->private_data; ++ ssize_t ret; ++ void *kbuf; + -+ misc_deregister(&oob->mdev); ++ if (count == 0) ++ return 0; + -+ return 0; ++ kbuf = memdup_user(buf, count); ++ if (IS_ERR(kbuf)) ++ return PTR_ERR(kbuf); ++ ++ /* currently only MCTP defined in the spec */ ++ spin_lock(&chan_desc->priv->tx_lock); ++ ret = mmbi_write(chan_desc, kbuf, count, MMBI_PROTOCOL_MCTP); ++ spin_unlock(&chan_desc->priv->tx_lock); ++ kfree(kbuf); ++ return ret; +} + -+/* flash channel (CH3) */ -+static long ast2700_espi_flash_get_rx(struct file *fp, -+ struct aspeed_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) ++static __poll_t mmbi_misc_poll(struct file *file, struct poll_table_struct *pt) +{ -+ u32 reg, cyc, tag, len; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ unsigned long flags; -+ u32 pkt_len; -+ u8 *pkt; -+ int i, rc; -+ -+ rc = 0; ++ struct mmbi_chan_desc *chan_desc = file->private_data; ++ struct mmbi_ins_desc *mmbi = chan_desc->mmbi; ++ __poll_t ret, requested_events; + -+ espi = container_of(flash, struct aspeed_espi, flash); ++ ret = 0; ++ requested_events = poll_requested_events(pt); ++ mmbi_channel_state_update(chan_desc, mmbi->desc_virt); + -+ if (fp->f_flags & O_NONBLOCK) { -+ if (!mutex_trylock(&flash->rx_mtx)) -+ return -EAGAIN; ++ if (chan_desc->priv->state != NORMAL_RUNTIME && chan_desc->priv->state != RESET_REQ_BY_BMC) { ++ pr_info("%s: chan %u invalid state 0x%x, not ready for polling\n", ++ __func__, chan_desc->index, chan_desc->priv->state); ++ return ret; ++ } + -+ if (!flash->rx_ready) { -+ rc = -ENODATA; -+ goto unlock_mtx_n_out; -+ } -+ } else { -+ mutex_lock(&flash->rx_mtx); ++ if (requested_events & POLL_IN) ++ poll_wait(file, &chan_desc->priv->rx_wait, pt); ++ if (requested_events & POLL_OUT) ++ poll_wait(file, &chan_desc->priv->tx_wait, pt); + -+ if (!flash->rx_ready) { -+ rc = wait_event_interruptible(flash->wq, flash->rx_ready); -+ if (rc == -ERESTARTSYS) { -+ rc = -EINTR; -+ goto unlock_mtx_n_out; -+ } -+ } -+ } ++ if (chan_desc->priv->rx_ready) ++ ret |= POLL_IN | POLLRDNORM; ++ if (chan_desc->priv->tx_ready) ++ ret |= POLL_OUT | POLLWRNORM; + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ reg = readl(espi->regs + ESPI_CH3_RX_CTRL); -+ cyc = FIELD_GET(ESPI_CH3_RX_CTRL_CYC, reg); -+ tag = FIELD_GET(ESPI_CH3_RX_CTRL_TAG, reg); -+ len = FIELD_GET(ESPI_CH3_RX_CTRL_LEN, reg); ++ return ret; ++} + -+ /* -+ * calculate the length of the rest part of the -+ * eSPI packet to be read from HW and copied to -+ * user space. -+ */ -+ switch (cyc) { -+ case ESPI_FLASH_WRITE: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_rwe); -+ break; -+ case ESPI_FLASH_READ: -+ case ESPI_FLASH_ERASE: -+ pkt_len = sizeof(struct espi_flash_rwe); -+ break; -+ case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: -+ case ESPI_FLASH_SUC_CMPLT_D_FIRST: -+ case ESPI_FLASH_SUC_CMPLT_D_LAST: -+ case ESPI_FLASH_SUC_CMPLT_D_ONLY: -+ pkt_len = ((len) ? len : ESPI_MAX_PLD_LEN) + -+ sizeof(struct espi_flash_cmplt); -+ break; -+ case ESPI_FLASH_SUC_CMPLT: -+ case ESPI_FLASH_UNSUC_CMPLT: -+ pkt_len = sizeof(struct espi_flash_cmplt); -+ break; -+ default: -+ rc = -EFAULT; -+ goto unlock_mtx_n_out; -+ } ++static const struct file_operations mmbi_fops = { ++ .owner = THIS_MODULE, ++ .open = mmbi_misc_open, ++ .read = mmbi_misc_read, ++ .write = mmbi_misc_write, ++ .poll = mmbi_misc_poll, ++ .release = mmbi_misc_release, ++}; + -+ if (ioc->pkt_len < pkt_len) { -+ rc = -EINVAL; -+ goto unlock_mtx_n_out; -+ } ++static void mmbi_channel_refresh_locked(struct mmbi_chan_desc *chan_desc) ++{ ++ if (chan_desc->mmbi->role == MMBI_ROLE_BMC) ++ mmbi_channel_irq_bmc(chan_desc); ++ else ++ mmbi_channel_irq_host(chan_desc); ++} + -+ pkt = vmalloc(pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; -+ } ++#if IS_ENABLED(CONFIG_MCTP) ++static void mmbi_mctp_update_queue_state(struct mmbi_chan_desc *chan_desc) ++{ ++ struct net_device *ndev = chan_desc->priv->ndev; + -+ hdr = (struct espi_comm_hdr *)pkt; -+ hdr->cyc = cyc; -+ hdr->tag = tag; -+ hdr->len_h = len >> 8; -+ hdr->len_l = len & 0xff; ++ if (!ndev || !netif_running(ndev)) ++ return; + -+ if (flash->dma.enable) { -+ memcpy(hdr + 1, flash->dma.rx_virt, pkt_len - sizeof(*hdr)); ++ if (chan_desc->priv->state == NORMAL_RUNTIME) { ++ netif_carrier_on(ndev); ++ if (chan_desc->priv->tx_ready) ++ netif_wake_queue(ndev); ++ else ++ netif_stop_queue(ndev); + } else { -+ for (i = sizeof(*hdr); i < pkt_len; ++i) -+ pkt[i] = readl(espi->regs + ESPI_CH3_RX_DATA) & 0xff; ++ netif_carrier_off(ndev); ++ netif_stop_queue(ndev); + } ++} + -+ if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; ++static void mmbi_mctp_handle_rx(struct mmbi_chan_desc *chan_desc) ++{ ++ struct net_device *ndev = chan_desc->priv->ndev; ++ u8 *tmp_buf; ++ ++ if (!ndev || !netif_running(ndev)) ++ goto out; ++ ++ tmp_buf = kmalloc(MMBI_MCTP_RX_BUF_SIZE, GFP_KERNEL); ++ if (!tmp_buf) { ++ ndev->stats.rx_dropped++; ++ goto out; + } + -+ spin_lock_irqsave(&flash->lock, flags); ++ while (netif_running(ndev)) { ++ struct sk_buff *skb; ++ struct mctp_skb_cb *cb; ++ int ret; + -+ writel(ESPI_CH3_RX_CTRL_SERV_PEND, espi->regs + ESPI_CH3_RX_CTRL); -+ flash->rx_ready = 0; ++ if (!READ_ONCE(chan_desc->priv->rx_ready)) ++ break; + -+ spin_unlock_irqrestore(&flash->lock, flags); ++ spin_lock(&chan_desc->priv->rx_lock); ++ spin_lock(&chan_desc->priv->tx_lock); ++ ret = mmbi_read(chan_desc, tmp_buf, MMBI_MCTP_RX_BUF_SIZE); ++ mmbi_channel_refresh_locked(chan_desc); ++ spin_unlock(&chan_desc->priv->tx_lock); ++ spin_unlock(&chan_desc->priv->rx_lock); + -+ rc = 0; ++ if (ret <= 0) { ++ if (ret < 0) ++ ndev->stats.rx_dropped++; ++ break; ++ } + -+free_n_out: -+ vfree(pkt); ++ skb = netdev_alloc_skb(ndev, ret); ++ if (!skb) { ++ ndev->stats.rx_dropped++; ++ break; ++ } + -+unlock_mtx_n_out: -+ mutex_unlock(&flash->rx_mtx); ++ skb_put_data(skb, tmp_buf, ret); ++ skb->protocol = htons(ETH_P_MCTP); ++ skb_reset_mac_header(skb); ++ skb_reset_network_header(skb); + -+ return rc; ++ cb = __mctp_cb(skb); ++ cb->halen = 0; ++ ++ netif_wake_queue(ndev); ++ ndev->stats.rx_packets++; ++ ndev->stats.rx_bytes += ret; ++ if (netif_rx(skb) == NET_RX_DROP) ++ ndev->stats.rx_dropped++; ++ } ++ ++ kfree(tmp_buf); ++out: ++ mmbi_mctp_update_queue_state(chan_desc); +} + -+static long ast2700_espi_flash_put_tx(struct file *fp, -+ struct aspeed_espi_flash *flash, -+ struct aspeed_espi_ioc *ioc) ++static netdev_tx_t mmbi_mctp_ndo_start_xmit(struct sk_buff *skb, ++ struct net_device *ndev) +{ -+ u32 reg, cyc, tag, len; -+ struct aspeed_espi *espi; -+ struct espi_comm_hdr *hdr; -+ u8 *pkt; -+ int i, rc; -+ -+ espi = container_of(flash, struct aspeed_espi, flash); ++ struct mmbi_mctp_netdev *mmbi_ndev = netdev_priv(ndev); ++ struct mmbi_chan_desc *chan_desc = mmbi_ndev->chan; ++ int ret; + -+ if (!mutex_trylock(&flash->tx_mtx)) -+ return -EAGAIN; ++ spin_lock(&chan_desc->priv->rx_lock); ++ spin_lock(&chan_desc->priv->tx_lock); ++ ret = mmbi_write(chan_desc, skb->data, skb->len, MMBI_PROTOCOL_MCTP); ++ mmbi_channel_refresh_locked(chan_desc); ++ spin_unlock(&chan_desc->priv->tx_lock); ++ spin_unlock(&chan_desc->priv->rx_lock); + -+ reg = readl(espi->regs + ESPI_CH3_TX_CTRL); -+ if (reg & ESPI_CH3_TX_CTRL_TRIG_PEND) { -+ rc = -EBUSY; -+ goto unlock_mtx_n_out; ++ if (ret == -EAGAIN) { ++ netif_stop_queue(ndev); ++ return NETDEV_TX_BUSY; + } + -+ pkt = vmalloc(ioc->pkt_len); -+ if (!pkt) { -+ rc = -ENOMEM; -+ goto unlock_mtx_n_out; ++ if (ret < 0) { ++ ndev->stats.tx_dropped++; ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; + } + -+ hdr = (struct espi_comm_hdr *)pkt; ++ ndev->stats.tx_packets++; ++ ndev->stats.tx_bytes += ret; ++ dev_kfree_skb_any(skb); ++ mmbi_mctp_update_queue_state(chan_desc); + -+ if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { -+ rc = -EFAULT; -+ goto free_n_out; -+ } ++ return NETDEV_TX_OK; ++} + -+ /* -+ * common header (i.e. cycle type, tag, and length) -+ * part is written to HW registers -+ */ -+ if (flash->dma.enable) { -+ memcpy(flash->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); -+ dma_wmb(); -+ } else { -+ for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) -+ writel(pkt[i], espi->regs + ESPI_CH3_TX_DATA); -+ } ++static int mmbi_mctp_ndo_open(struct net_device *ndev) ++{ ++ struct mmbi_mctp_netdev *mmbi_ndev = netdev_priv(ndev); ++ struct mmbi_chan_desc *chan_desc = mmbi_ndev->chan; + -+ cyc = hdr->cyc; -+ tag = hdr->tag; -+ len = (hdr->len_h << 8) | (hdr->len_l & 0xff); ++ spin_lock(&chan_desc->priv->rx_lock); ++ spin_lock(&chan_desc->priv->tx_lock); ++ mmbi_channel_refresh_locked(chan_desc); ++ spin_unlock(&chan_desc->priv->tx_lock); ++ spin_unlock(&chan_desc->priv->rx_lock); + -+ reg = FIELD_PREP(ESPI_CH3_TX_CTRL_CYC, cyc) -+ | FIELD_PREP(ESPI_CH3_TX_CTRL_TAG, tag) -+ | FIELD_PREP(ESPI_CH3_TX_CTRL_LEN, len) -+ | ESPI_CH3_TX_CTRL_TRIG_PEND; -+ writel(reg, espi->regs + ESPI_CH3_TX_CTRL); ++ mmbi_channel_start_polling(chan_desc); ++ mmbi_mctp_handle_rx(chan_desc); ++ mmbi_mctp_update_queue_state(chan_desc); ++ return 0; ++} + -+ rc = 0; ++static int mmbi_mctp_ndo_stop(struct net_device *ndev) ++{ ++ struct mmbi_mctp_netdev *mmbi_ndev = netdev_priv(ndev); + -+free_n_out: -+ vfree(pkt); ++ netif_stop_queue(ndev); ++ mmbi_channel_stop_polling(mmbi_ndev->chan); ++ return 0; ++} + -+unlock_mtx_n_out: -+ mutex_unlock(&flash->tx_mtx); ++static const struct net_device_ops mmbi_mctp_netdev_ops = { ++ .ndo_open = mmbi_mctp_ndo_open, ++ .ndo_stop = mmbi_mctp_ndo_stop, ++ .ndo_start_xmit = mmbi_mctp_ndo_start_xmit, ++}; + -+ return rc; ++static void mmbi_mctp_net_setup(struct net_device *ndev) ++{ ++ ndev->type = ARPHRD_MCTP; ++ ndev->flags = IFF_NOARP; ++ ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; ++ ndev->hard_header_len = 0; ++ ndev->addr_len = 0; ++ ndev->netdev_ops = &mmbi_mctp_netdev_ops; +} + -+static long ast2700_espi_flash_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) ++static int mmbi_channel_init_mctp_netdev(struct mmbi_chan_desc *chan_desc) +{ -+ struct aspeed_espi_flash *flash; -+ struct aspeed_espi_ioc ioc; ++ struct mmbi_ins_desc *mmbi = chan_desc->mmbi; ++ struct mmbi_mctp_netdev *mmbi_ndev; ++ struct net_device *ndev; ++ char ifname[IFNAMSIZ]; ++ u32 max_mtu; ++ int ret; + -+ flash = container_of(fp->private_data, struct aspeed_espi_flash, mdev); ++ max_mtu = min(chan_desc->b2h_l, chan_desc->h2b_l); ++ if (max_mtu <= MMBI_PKT_HDR_SIZE || ++ max_mtu - MMBI_PKT_HDR_SIZE < MMBI_MCTP_MIN_MTU) { ++ dev_err(mmbi->dev, ++ "channel %u MMBI buffers are too small for MCTP netdev\n", ++ chan_desc->index); ++ return -EINVAL; ++ } + -+ if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) -+ return -EFAULT; ++ snprintf(ifname, sizeof(ifname), "mctpmm%dch%d", ++ mmbi->ins_id, chan_desc->index); ++ ndev = alloc_netdev(sizeof(*mmbi_ndev), ifname, NET_NAME_UNKNOWN, ++ mmbi_mctp_net_setup); ++ if (!ndev) ++ return -ENOMEM; + -+ if (ioc.pkt_len > ESPI_MAX_PKT_LEN) -+ return -EINVAL; ++ SET_NETDEV_DEV(ndev, mmbi->dev); ++ ndev->dev.of_node = mmbi->dev->of_node; ++ ndev->min_mtu = MMBI_MCTP_MIN_MTU; ++ ndev->max_mtu = max_mtu - MMBI_PKT_HDR_SIZE; ++ ndev->mtu = ndev->max_mtu; + -+ switch (cmd) { -+ case ASPEED_ESPI_FLASH_GET_RX: -+ return ast2700_espi_flash_get_rx(fp, flash, &ioc); -+ case ASPEED_ESPI_FLASH_PUT_TX: -+ return ast2700_espi_flash_put_tx(fp, flash, &ioc); -+ }; ++ mmbi_ndev = netdev_priv(ndev); ++ mmbi_ndev->chan = chan_desc; ++ chan_desc->priv->ndev = ndev; + -+ return -EINVAL; -+} ++ ret = mctp_register_netdev(ndev, NULL, MCTP_PHYS_BINDING_MMBI); ++ if (ret) { ++ dev_err(mmbi->dev, ++ "failed to register MCTP netdev for channel %u: %d\n", ++ chan_desc->index, ret); ++ chan_desc->priv->ndev = NULL; ++ free_netdev(ndev); ++ return ret; ++ } + -+static const struct file_operations ast2700_espi_flash_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = ast2700_espi_flash_ioctl, -+}; ++ dev_info(mmbi->dev, "registered MCTP netdev %s for channel %u\n", ++ ndev->name, chan_desc->index); + -+static void ast2700_espi_flash_isr(struct aspeed_espi *espi) ++ return 0; ++} ++ ++static void mmbi_channel_remove_mctp_netdev(struct mmbi_chan_desc *chan_desc) +{ -+ struct aspeed_espi_flash *flash; -+ unsigned long flags; -+ u32 sts; ++ struct net_device *ndev = chan_desc->priv->ndev; + -+ flash = &espi->flash; ++ if (!ndev) ++ return; + -+ sts = readl(espi->regs + ESPI_CH3_INT_STS); ++ mctp_unregister_netdev(ndev); ++ free_netdev(ndev); ++ chan_desc->priv->ndev = NULL; ++} ++#else ++static void mmbi_mctp_handle_rx(struct mmbi_chan_desc *chan_desc) ++{ ++} + -+ if (sts & ESPI_CH3_INT_STS_RX_CMPLT) { -+ writel(ESPI_CH3_INT_STS_RX_CMPLT, espi->regs + ESPI_CH3_INT_STS); ++static int mmbi_channel_init_mctp_netdev(struct mmbi_chan_desc *chan_desc) ++{ ++ struct mmbi_ins_desc *mmbi = chan_desc->mmbi; + -+ spin_lock_irqsave(&flash->lock, flags); -+ flash->rx_ready = true; -+ spin_unlock_irqrestore(&flash->lock, flags); ++ dev_err(mmbi->dev, "MCTP support is not reachable for MMBI netdev mode\n"); ++ return -EOPNOTSUPP; ++} + -+ wake_up_interruptible(&flash->wq); -+ } ++static void mmbi_channel_remove_mctp_netdev(struct mmbi_chan_desc *chan_desc) ++{ +} ++#endif + -+static void ast2700_espi_flash_reset(struct aspeed_espi *espi) ++static int mmbi_channel_init_miscdev(struct mmbi_chan_desc *chan_desc) +{ -+ u32 reg; -+ u64 mask; -+ struct aspeed_espi_flash *flash = &espi->flash; ++ struct mmbi_ins_desc *mmbi = chan_desc->mmbi; ++ int ret; + -+ writel(0x0, espi->regs + ESPI_CH3_INT_EN); -+ writel(0xffffffff, espi->regs + ESPI_CH3_INT_STS); ++ chan_desc->priv->miscdev.name = ++ devm_kasprintf(mmbi->dev, GFP_KERNEL, "mmbi%d-ch%d", ++ mmbi->ins_id, chan_desc->index); + -+ reg = readl(espi->regs + ESPI_CH3_CTRL); -+ reg &= ~(ESPI_CH3_CTRL_TX_RST -+ | ESPI_CH3_CTRL_RX_RST -+ | ESPI_CH3_CTRL_TX_DMA_EN -+ | ESPI_CH3_CTRL_RX_DMA_EN -+ | ESPI_CH3_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ if (!chan_desc->priv->miscdev.name) { ++ pr_err("%s: failed to allocate misc device name for chan %u\n", ++ __func__, chan_desc->index); ++ return -ENOMEM; ++ } ++ chan_desc->priv->miscdev.minor = MISC_DYNAMIC_MINOR; ++ chan_desc->priv->miscdev.fops = &mmbi_fops; ++ chan_desc->priv->miscdev.parent = mmbi->dev; + -+ udelay(1); ++ ret = misc_register(&chan_desc->priv->miscdev); ++ if (ret) ++ pr_err("%s: failed to register misc device for chan %u\n", ++ __func__, chan_desc->index); + -+ reg |= (ESPI_CH3_CTRL_TX_RST | ESPI_CH3_CTRL_RX_RST); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ return ret; ++} + -+ if (flash->edaf.mode == EDAF_MODE_MIX) { -+ mask = ~(flash->edaf.size - 1); -+ writel(upper_32_bits(mask), espi->regs + ESPI_CH3_EDAF_MASKH); -+ writel(mask & 0xffffffff, espi->regs + ESPI_CH3_EDAF_MASKL); -+ writel(upper_32_bits(flash->edaf.taddr), espi->regs + ESPI_CH3_EDAF_TADDRH); -+ writel(flash->edaf.taddr & 0xffffffff, espi->regs + ESPI_CH3_EDAF_TADDRL); ++static void mmbi_channel_remove_miscdev(struct mmbi_chan_desc *chan_desc) ++{ ++ misc_deregister(&chan_desc->priv->miscdev); ++} ++ ++static int mmbi_channel_init_user_interface(struct mmbi_chan_desc *chan_desc) ++{ ++ switch (chan_desc->app_interface) { ++ case MMBI_APP_INTF_MCTP_NETDEV: ++ return mmbi_channel_init_mctp_netdev(chan_desc); ++ case MMBI_APP_INTF_IOCTL: ++ default: ++ return mmbi_channel_init_miscdev(chan_desc); + } ++} + -+ reg = readl(espi->regs + ESPI_CH3_CTRL) & ~ESPI_CH3_CTRL_EDAF_MODE; -+ reg |= FIELD_PREP(ESPI_CH3_CTRL_EDAF_MODE, flash->edaf.mode); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++static void mmbi_channel_remove_user_interface(struct mmbi_chan_desc *chan_desc) ++{ ++ switch (chan_desc->app_interface) { ++ case MMBI_APP_INTF_MCTP_NETDEV: ++ mmbi_channel_remove_mctp_netdev(chan_desc); ++ break; ++ case MMBI_APP_INTF_IOCTL: ++ default: ++ mmbi_channel_remove_miscdev(chan_desc); ++ break; ++ } ++} + -+ if (flash->dma.enable) { -+ writel(upper_32_bits(flash->dma.tx_addr), espi->regs + ESPI_CH3_TX_DMAH); -+ writel(flash->dma.tx_addr & 0xffffffff, espi->regs + ESPI_CH3_TX_DMAL); -+ writel(upper_32_bits(flash->dma.rx_addr), espi->regs + ESPI_CH3_RX_DMAH); -+ writel(flash->dma.rx_addr & 0xffffffff, espi->regs + ESPI_CH3_RX_DMAL); ++static int mmbi_instance_init_user_interface(struct mmbi_ins_desc *mmbi) ++{ ++ int i, ret; + -+ reg = readl(espi->regs + ESPI_CH3_CTRL) -+ | ESPI_CH3_CTRL_TX_DMA_EN -+ | ESPI_CH3_CTRL_RX_DMA_EN; -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ for (i = 0; i < mmbi->num_of_channels; i++) { ++ ret = mmbi_channel_init_user_interface(&mmbi->chan_desc[i]); ++ if (ret) ++ goto err_remove_interfaces; + } + -+ writel(ESPI_CH3_INT_EN_RX_CMPLT, espi->regs + ESPI_CH3_INT_EN); ++ return 0; + -+ reg = readl(espi->regs + ESPI_CH3_CTRL) | ESPI_CH3_CTRL_SW_RDY; -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++err_remove_interfaces: ++ while (i > 0) { ++ i--; ++ mmbi_channel_remove_user_interface(&mmbi->chan_desc[i]); ++ } ++ ++ return ret; +} + -+int ast2700_espi_flash_probe(struct aspeed_espi *espi) ++static void mmbi_instance_remove_user_interface(struct mmbi_ins_desc *mmbi) +{ -+ struct aspeed_espi_flash *flash; -+ struct device_node *np; -+ struct resource res; -+ struct device *dev; -+ void *virt; -+ int rc; -+ -+ dev = espi->dev; ++ int i; + -+ flash = &espi->flash; ++ for (i = 0; i < mmbi->num_of_channels; i++) ++ mmbi_channel_remove_user_interface(&mmbi->chan_desc[i]); ++} + -+ init_waitqueue_head(&flash->wq); ++static void mmbi_channel_poll_handler(struct work_struct *work) ++{ ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct mmbi_chan_priv *priv = container_of(dwork, struct mmbi_chan_priv, poll_work); ++ struct mmbi_chan_desc *chan_desc = priv->chan; ++ struct mmbi_ins_desc *mmbi = chan_desc->mmbi; ++ u8 __iomem *desc_virt = mmbi->desc_virt; + -+ spin_lock_init(&flash->lock); ++ u32 state_location, read_ptr, write_ptr; + -+ mutex_init(&flash->tx_mtx); -+ mutex_init(&flash->rx_mtx); ++ state_location = (mmbi->role == MMBI_ROLE_BMC) ? chan_desc->buffer_desc.h_rws_p : ++ chan_desc->buffer_desc.h_ros_p; + -+ flash->edaf.mode = EDAF_MODE_HW; ++ write_ptr = ioread32(desc_virt + state_location); ++ read_ptr = ioread32(desc_virt + state_location + sizeof(u32)); + -+ of_property_read_u32(dev->of_node, "flash-edaf-mode", &flash->edaf.mode); -+ dev_info(dev, "eDAF mode: 0x%x\n", flash->edaf.mode); -+ if (flash->edaf.mode == EDAF_MODE_MIX) { -+ np = of_parse_phandle(dev->of_node, "flash-edaf-tgt-addr", 0); -+ if (!np || of_address_to_resource(np, 0, &res)) { -+ dev_err(dev, "cannot get eDAF memory region\n"); -+ return -ENODEV; -+ } ++ if (write_ptr != chan_desc->priv->write_ptr || read_ptr != chan_desc->priv->read_ptr) { ++ chan_desc->priv->write_ptr = write_ptr; ++ chan_desc->priv->read_ptr = read_ptr; + -+ of_node_put(np); ++ spin_lock(&chan_desc->priv->rx_lock); ++ spin_lock(&chan_desc->priv->tx_lock); ++ mmbi_channel_refresh_locked(chan_desc); ++ spin_unlock(&chan_desc->priv->tx_lock); ++ spin_unlock(&chan_desc->priv->rx_lock); + -+ flash->edaf.taddr = res.start; -+ flash->edaf.size = resource_size(&res); ++ } + -+ virt = devm_ioremap_resource(dev, &res); -+ if (!virt) { -+ dev_err(dev, "cannot map eDAF memory region\n"); -+ return -ENOMEM; -+ } ++ if (chan_desc->app_interface == MMBI_APP_INTF_MCTP_NETDEV) { ++ mmbi_mctp_handle_rx(chan_desc); ++ } else { ++ if (chan_desc->priv->rx_ready) ++ wake_up_interruptible(&chan_desc->priv->rx_wait); ++ if (chan_desc->priv->tx_ready) ++ wake_up_interruptible(&chan_desc->priv->tx_wait); + } + -+ flash->dma.enable = of_property_read_bool(dev->of_node, "flash-dma-mode"); -+ if (flash->dma.enable) { -+ flash->dma.tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.tx_addr, GFP_KERNEL); -+ if (!flash->dma.tx_virt) { -+ dev_err(dev, "cannot allocate DMA TX buffer\n"); -+ return -ENOMEM; -+ } ++ /* Reschedule the work */ ++ schedule_delayed_work(&chan_desc->priv->poll_work, msecs_to_jiffies(chan_desc->poll_interval_ms)); ++} + -+ flash->dma.rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE, &flash->dma.rx_addr, GFP_KERNEL); -+ if (!flash->dma.rx_virt) { -+ dev_err(dev, "cannot allocate DMA RX buffer\n"); -+ return -ENOMEM; -+ } ++static int mmbi_instance_init_channel_priv(struct mmbi_ins_desc *mmbi) ++{ ++ int i; ++ struct mmbi_chan_desc *chan_desc; ++ ++ for (i = 0; i < mmbi->num_of_channels; i++) { ++ chan_desc = &mmbi->chan_desc[i]; ++ INIT_DELAYED_WORK(&chan_desc->priv->poll_work, mmbi_channel_poll_handler); ++ spin_lock_init(&chan_desc->priv->rx_lock); ++ spin_lock_init(&chan_desc->priv->tx_lock); ++ init_waitqueue_head(&chan_desc->priv->rx_wait); ++ init_waitqueue_head(&chan_desc->priv->tx_wait); ++ chan_desc->priv->tx_ready = true; ++ chan_desc->priv->rx_ready = false; ++ chan_desc->priv->read_ptr = 0; ++ chan_desc->priv->write_ptr = 0; ++ chan_desc->priv->running = false; ++ if (chan_desc->poll_interval_ms == 0) ++ chan_desc->poll_interval_ms = MMBI_POLL_INTERVAL_MS; ++ chan_desc->priv->ndev = NULL; + } ++ return 0; ++} + -+ flash->mdev.parent = dev; -+ flash->mdev.minor = MISC_DYNAMIC_MINOR; -+ flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s-flash%d", DEVICE_NAME, espi->dev_id); -+ flash->mdev.fops = &ast2700_espi_flash_fops; -+ rc = misc_register(&flash->mdev); -+ if (rc) { -+ dev_err(dev, "cannot register device %s\n", flash->mdev.name); -+ return rc; -+ } ++int mmbi_channel_avail_length(u8 __iomem *read_structure, ++ u8 __iomem *write_structure, u32 buf_size) ++{ ++ u32 __read_offset = ioread32be(read_structure + sizeof(u32)); ++ u32 __write_offset = ioread32be(write_structure); ++ ++ __read_offset = MMBI_PTR_ADDR_ALIGN(be32_to_cpu(__read_offset)); ++ __write_offset = MMBI_PTR_ADDR_ALIGN(be32_to_cpu(__write_offset)); + -+ ast2700_espi_flash_reset(espi); ++ return (__write_offset >= __read_offset) ? ++ (buf_size - __write_offset + __read_offset) : ++ (__read_offset - __write_offset); ++} ++EXPORT_SYMBOL_GPL(mmbi_channel_avail_length); + -+ return 0; ++int mmbi_channel_unhandled_length(u8 __iomem *read_structure, ++ u8 __iomem *write_structure, u32 buf_size) ++{ ++ u32 __read_offset = ioread32be(read_structure + sizeof(u32)); ++ u32 __write_offset = ioread32be(write_structure); ++ ++ __read_offset = MMBI_PTR_ADDR_ALIGN(be32_to_cpu(__read_offset)); ++ __write_offset = MMBI_PTR_ADDR_ALIGN(be32_to_cpu(__write_offset)); ++ return (__write_offset >= __read_offset) ? ++ (__write_offset - __read_offset) : ++ (buf_size - __read_offset + __write_offset); +} ++EXPORT_SYMBOL_GPL(mmbi_channel_unhandled_length); + -+int ast2700_espi_flash_remove(struct aspeed_espi *espi) ++void mmbi_channel_state_update(struct mmbi_chan_desc *chan, u8 __iomem *desc_virt) +{ -+ struct aspeed_espi_flash *flash; -+ struct device *dev; -+ u32 reg; ++ u8 __iomem *host_rws_vmem; ++ u8 __iomem *host_ros_vmem; ++ u8 host_rws_val; ++ u8 host_ros_val; ++ struct mmbi_buf_vpscb *buf_desc; ++ enum mmbi_state prev_state; + -+ dev = espi->dev; ++ if (chan->buffer_type == MMBI_BUFFER_TYPE_VPSCB) { ++ prev_state = chan->priv->state; ++ buf_desc = &chan->buffer_desc; ++ host_rws_vmem = desc_virt + buf_desc->h_rws_p; ++ host_ros_vmem = desc_virt + buf_desc->h_ros_p; ++ host_rws_val = ioread8(host_rws_vmem); ++ host_ros_val = ioread8(host_ros_vmem); ++ chan->priv->state = (MMBI_STATE_GET_IF_UP(host_ros_val) << 2) | ++ (MMBI_STATE_GET_RST(host_ros_val) << 2) | ++ MMBI_STATE_GET_IF_UP(host_rws_val) | ++ MMBI_STATE_GET_RST(host_rws_val); + -+ flash = &espi->flash; ++ chan->priv->peer_ready = (chan->mmbi->role == MMBI_ROLE_BMC) ? ++ mmbi_get_ready(host_rws_vmem) : ++ mmbi_get_ready(host_ros_vmem); ++ } else { ++ pr_err("\t%s: unsupported buffer type %d\n", ++ __func__, chan->buffer_type); ++ chan->priv->state = POWER_UP_OR_ERROR; ++ } ++} ++EXPORT_SYMBOL_GPL(mmbi_channel_state_update); + -+ writel(0x0, espi->regs + ESPI_CH3_INT_EN); ++int mmbi_instance_init(struct mmbi_ins_desc *mmbi) ++{ ++ int rc, i; + -+ reg = readl(espi->regs + ESPI_CH3_CTRL); -+ reg &= ~(ESPI_CH3_CTRL_TX_DMA_EN -+ | ESPI_CH3_CTRL_RX_DMA_EN -+ | ESPI_CH3_CTRL_SW_RDY); -+ writel(reg, espi->regs + ESPI_CH3_CTRL); ++ if (!mmbi) { ++ rc = -EINVAL; ++ goto out_fail; ++ } + -+ if (flash->dma.enable) { -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.tx_virt, flash->dma.tx_addr); -+ dmam_free_coherent(dev, PAGE_SIZE, flash->dma.rx_virt, flash->dma.rx_addr); ++ if (mmbi->role == MMBI_ROLE_BMC) { ++ if (mmbi->mmbi_version != MMBI_VERSION_1_0 && ++ mmbi->mmbi_version != MMBI_VERSION_1_1) { ++ rc = -EINVAL; ++ goto out_fail; ++ } + } + -+ mutex_destroy(&flash->tx_mtx); -+ mutex_destroy(&flash->rx_mtx); ++ mmbi->ins_id = ida_alloc(&mmbi_ida, GFP_KERNEL); ++ if (mmbi->ins_id < 0) { ++ rc = mmbi->ins_id; ++ goto out_fail; ++ } + -+ misc_deregister(&flash->mdev); ++ for (i = 0; i < MMBI_MAX_CHANNELS; i++) { ++ mmbi->chan_desc[i].priv = devm_kzalloc(mmbi->dev, sizeof(*mmbi->chan_desc[i].priv), GFP_KERNEL); ++ if (!mmbi->chan_desc[i].priv) { ++ rc = -ENOMEM; ++ goto out_fail; ++ } ++ mmbi->chan_desc[i].priv->chan = &mmbi->chan_desc[i]; ++ } ++ ++ if (mmbi->role == MMBI_ROLE_BMC) ++ rc = mmbi_instance_init_bmc(mmbi); ++ else ++ rc = mmbi_instance_init_host(mmbi); ++ ++ if (rc) ++ goto ida_free; ++ ++ rc = mmbi_instance_init_channel_priv(mmbi); ++ if (rc) ++ goto ida_free; ++ ++ rc = mmbi_instance_init_user_interface(mmbi); ++ if (rc) ++ goto ida_free; + + return 0; ++ ++ida_free: ++ ida_free(&mmbi_ida, mmbi->ins_id); ++out_fail: ++ pr_err("%s: failed to init MMBI instance\n", ++ __func__); ++ return rc; +} ++EXPORT_SYMBOL_GPL(mmbi_instance_init); + -+/* global control */ -+irqreturn_t ast2700_espi_isr(int irq, void *arg) ++void mmbi_instance_remove(struct mmbi_ins_desc *mmbi) +{ -+ u32 sts; -+ struct aspeed_espi *espi = (struct aspeed_espi *)arg; -+ -+ sts = readl(espi->regs + ESPI_INT_STS); -+ if (!sts) -+ return IRQ_NONE; -+ -+ if (sts & ESPI_INT_STS_CH0) -+ ast2700_espi_perif_isr(espi); ++ int i; ++ u8 __iomem *struct_desc; + -+ if (sts & ESPI_INT_STS_CH1) -+ ast2700_espi_vw_isr(espi); ++ if (!mmbi) ++ return; + -+ if (sts & ESPI_INT_STS_CH2) -+ ast2700_espi_oob_isr(espi); ++ for (i = 0; i < mmbi->num_of_channels; i++) ++ mmbi_channel_stop_polling(&mmbi->chan_desc[i]); + -+ if (sts & ESPI_INT_STS_CH3) -+ ast2700_espi_flash_isr(espi); ++ mmbi_instance_remove_user_interface(mmbi); + -+ if (sts & ESPI_INT_STS_RST_DEASSERT) { -+ ast2700_espi_perif_sw_reset(espi); -+ ast2700_espi_perif_reset(espi); -+ ast2700_espi_vw_reset(espi); -+ ast2700_espi_oob_reset(espi); -+ ast2700_espi_flash_reset(espi); -+ writel(ESPI_INT_STS_RST_DEASSERT, espi->regs + ESPI_INT_STS); ++ for (i = 0; i < mmbi->num_of_channels; i++) { ++ struct_desc = ++ mmbi->desc_virt + ++ (mmbi->role == MMBI_ROLE_BMC ? ++ mmbi->chan_desc[i].buffer_desc.h_ros_p : ++ mmbi->chan_desc[i].buffer_desc.h_rws_p); ++ mmbi_clr_ready(struct_desc); ++ mmbi_clr_up(struct_desc); + } + -+ return IRQ_HANDLED; ++ ida_free(&mmbi_ida, mmbi->ins_id); +} ++EXPORT_SYMBOL_GPL(mmbi_instance_remove); +diff --git a/drivers/soc/aspeed/mmbi/aspeed-mmbi.h b/drivers/soc/aspeed/mmbi/aspeed-mmbi.h +--- a/drivers/soc/aspeed/mmbi/aspeed-mmbi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-mmbi.h 2026-07-17 08:44:55.293306504 +0000 +@@ -0,0 +1,174 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2026 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_MMBI_H__ ++#define __ASPEED_MMBI_H__ + -+void ast2700_espi_pre_init(struct aspeed_espi *espi) -+{ -+ struct device *dev; -+ struct regmap *scu1; -+ u32 reg; -+ int rc; ++#include ++#include ++#include ++#include ++#include + -+ dev = espi->dev; ++#define MMBI_SIGNATURE "#MMBI$" ++#define MMBI_DESC_SIZE_PREFIX 8 ++#define MMBI_DESC_SIZE_CHANNEL 32 ++#define MMBI_DESC_SIZE_INTERRUPT 24 + -+ scu1 = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); -+ if (IS_ERR(scu1)) { -+ dev_err(dev, "failed to find SCU1 regmap, error %ld\n", PTR_ERR(scu1)); -+ return; -+ } -+ rc = regmap_update_bits(scu1, SCU1_DDR, -+ SCU1_DDR_DIS_ESPI0_AHB | SCU1_DDR_DIS_ESPI1_AHB, -+ 0); -+ if (!rc) { -+ dev_err(dev, "failed to update SCU1 regmap, error %d\n", rc); -+ return; -+ } ++#define MMBI_VERSION_1_0 0x01 ++#define MMBI_VERSION_1_1 0x02 ++#define MMBI_VERSION_MASK GENMASK(3, 0) ++#define MMBI_MAX_CHANNELS 7 ++#define MMBI_NOI_MASK GENMASK(6, 4) ++#define MMBI_OS_USE_MASK BIT(0) ++#define MMBI_BUF_ADDR_ALIGN(addr) (ALIGN_DOWN(addr, 8)) ++#define MMBI_BUF_TYPE_MASK GENMASK(3, 0) ++#define MMBI_INT_VAL_MASK GENMASK(3, 0) ++#define MMBI_INT_TYPE_MASK GENMASK(2, 0) ++#define MMBI_PTR_ADDR_MASK GENMASK(31, 2) ++#define MMBI_PTR_ADDR_ALIGN(addr) (ALIGN_DOWN(addr, 4)) ++#define MMBI_PKT_PADDING_MASK GENMASK(1, 0) ++#define MMBI_PKT_PROTOCOL_MASK GENMASK(3, 0) ++#define MMBI_PKT_MIN_SIZE 8 ++#define MMBI_PKT_HDR_SIZE 4 + -+ espi->dev_id = ida_alloc(&ast2700_espi_ida, GFP_KERNEL); -+ if (espi->dev_id < 0) { -+ dev_err(dev, "cannote allocate device ID, error %d\n", espi->dev_id); -+ return; -+ } ++#define MMBI_STATE_RDY_MSK BIT(0) ++#define MMBI_STATE_RST_MSK BIT(0) ++#define MMBI_STATE_IF_UP_MSK BIT(1) + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++#define MMBI_STATE_GET_RST(val) ((val) & MMBI_STATE_RST_MSK) ++#define MMBI_STATE_GET_IF_UP(val) ((val) & MMBI_STATE_IF_UP_MSK) ++ ++#define MMBI_POLL_INTERVAL_MS 20 ++ ++static __always_inline u8 mmbi_get_ready(u8 __iomem *devm_virt) ++{ ++ return ioread8(devm_virt + sizeof(u32)) & MMBI_STATE_RDY_MSK; +} + -+void ast2700_espi_post_init(struct aspeed_espi *espi) ++static __always_inline void mmbi_set_ready(u8 __iomem *devm_virt) +{ -+ u32 reg; ++ iowrite8(ioread8(devm_virt + sizeof(u32)) | MMBI_STATE_RDY_MSK, ++ devm_virt + sizeof(u32)); ++} + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg |= ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++static __always_inline void mmbi_clr_ready(u8 __iomem *devm_virt) ++{ ++ iowrite8(ioread8(devm_virt + sizeof(u32)) & ~MMBI_STATE_RDY_MSK, ++ devm_virt + sizeof(u32)); +} + -+void ast2700_espi_deinit(struct aspeed_espi *espi) ++static __always_inline void mmbi_set_up(u8 __iomem *devm_virt) +{ -+ struct device *dev; -+ u32 reg; ++ iowrite8(ioread8(devm_virt) | MMBI_STATE_IF_UP_MSK, devm_virt); ++} + -+ dev = espi->dev; ++static __always_inline void mmbi_clr_up(u8 __iomem *devm_virt) ++{ ++ iowrite8(ioread8(devm_virt) & ~MMBI_STATE_IF_UP_MSK, devm_virt); ++} + -+ reg = readl(espi->regs + ESPI_INT_EN); -+ reg &= ~ESPI_INT_EN_RST_DEASSERT; -+ writel(reg, espi->regs + ESPI_INT_EN); ++static __always_inline void mmbi_set_rst(u8 __iomem *devm_virt) ++{ ++ iowrite8(ioread8(devm_virt) | MMBI_STATE_RST_MSK, devm_virt); +} -diff --git a/drivers/soc/aspeed/espi/ast2700-espi.h b/drivers/soc/aspeed/espi/ast2700-espi.h ---- a/drivers/soc/aspeed/espi/ast2700-espi.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/ast2700-espi.h 2026-04-08 18:03:48.313705265 +0000 -@@ -0,0 +1,296 @@ -+/* SPDX-License-Identifier: GPL-2.0+ */ -+/* -+ * Register definitions for Aspeed AST2700 eSPI controller -+ * Copyright 2023 Aspeed Technology Inc. -+ */ -+#ifndef _AST2700_ESPI_H_ -+#define _AST2700_ESPI_H_ + -+#include -+#include "aspeed-espi.h" ++static __always_inline void mmbi_clr_rst(u8 __iomem *devm_virt) ++{ ++ iowrite8(ioread8(devm_virt) & ~MMBI_STATE_RST_MSK, devm_virt); ++} + -+/* SCU regiseters */ -+#define SCU1_DDR 0x0c8 -+#define SCU1_DDR_DIS_ESPI0_AHB BIT(0) -+#define SCU1_DDR_DIS_ESPI1_AHB BIT(1) ++enum mmbi_role { ++ MMBI_ROLE_HOST = 0, ++ MMBI_ROLE_BMC = 1, ++}; + -+/* global registers */ -+#define ESPI_CTRL 0x000 -+#define ESPI_STS 0x004 -+#define ESPI_INT_STS 0x008 -+#define ESPI_INT_STS_RST_DEASSERT BIT(31) -+#define ESPI_INT_STS_RST_ASSERT BIT(30) -+#define ESPI_INT_STS_CH3 BIT(3) -+#define ESPI_INT_STS_CH2 BIT(2) -+#define ESPI_INT_STS_CH1 BIT(1) -+#define ESPI_INT_STS_CH0 BIT(0) -+#define ESPI_INT_EN 0x00c -+#define ESPI_INT_EN_RST_DEASSERT BIT(31) -+#define ESPI_INT_EN_RST_ASSERT BIT(30) -+#define ESPI_DEV_ID 0x010 -+#define ESPI_CAP_GEN 0x014 -+#define ESPI_CAP_GEN_RTC_SUP BIT(29) -+#define ESPI_CAP_CH0 0x018 -+#define ESPI_CAP_CH1 0x01c -+#define ESPI_CAP_CH2 0x020 -+#define ESPI_CAP_CH3_0 0x024 -+#define ESPI_CAP_CH3_1 0x028 -+#define ESPI_DEV_STS 0x030 -+#define ESPI_DBG_CTRL 0x034 -+#define ESPI_DBG_ADDRL 0x038 -+#define ESPI_DBG_ADDRH 0x03c -+#define ESPI_DBG_CMD 0x040 -+#define ESPI_DBG_RES 0x044 -+#define ESPI_CH_ACC_CTRL 0x04c -+#define ESPI_CH_ACC_OFST1 0x050 -+#define ESPI_CH_ACC_OFST2 0x054 -+#define ESPI_WPROT0 0x0f8 -+#define ESPI_WPROT1 0x0fc ++enum mmbi_host_int_type { ++ MMBI_HOST_INT_NONE = 0, ++ MMBI_HOST_INT_PCIE = 1, ++ MMBI_HOST_INT_GPIO = 2, ++ MMBI_HOST_INT_ESPI_VW = 3, ++}; + -+/* peripheral channel (ch0) registers */ -+#define ESPI_CH0_CTRL 0x100 -+#define ESPI_CH0_CTRL_NP_TX_RST BIT(31) -+#define ESPI_CH0_CTRL_NP_RX_RST BIT(30) -+#define ESPI_CH0_CTRL_PC_TX_RST BIT(29) -+#define ESPI_CH0_CTRL_PC_RX_RST BIT(28) -+#define ESPI_CH0_CTRL_NP_TX_DMA_EN BIT(19) -+#define ESPI_CH0_CTRL_PC_TX_DMA_EN BIT(17) -+#define ESPI_CH0_CTRL_PC_RX_DMA_EN BIT(16) -+#define ESPI_CH0_CTRL_MCYC_RD_DIS_WDT BIT(9) -+#define ESPI_CH0_CTRL_MCYC_WR_DIS_WDT BIT(8) -+#define ESPI_CH0_CTRL_MCYC_RD_DIS BIT(6) -+#define ESPI_CH0_CTRL_MCYC_WR_DIS BIT(4) -+#define ESPI_CH0_CTRL_SW_RDY BIT(1) -+#define ESPI_CH0_STS 0x104 -+#define ESPI_CH0_INT_STS 0x108 -+#define ESPI_CH0_INT_STS_PC_RX_CMPLT BIT(0) -+#define ESPI_CH0_INT_EN 0x10c -+#define ESPI_CH0_INT_EN_PC_RX_CMPLT BIT(0) -+#define ESPI_CH0_PC_RX_DMAL 0x110 -+#define ESPI_CH0_PC_RX_DMAH 0x114 -+#define ESPI_CH0_PC_RX_CTRL 0x118 -+#define ESPI_CH0_PC_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_CH0_PC_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH0_PC_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH0_PC_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH0_PC_RX_DATA 0x11c -+#define ESPI_CH0_PC_TX_DMAL 0x120 -+#define ESPI_CH0_PC_TX_DMAH 0x124 -+#define ESPI_CH0_PC_TX_CTRL 0x128 -+#define ESPI_CH0_PC_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH0_PC_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH0_PC_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH0_PC_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH0_PC_TX_DATA 0x12c -+#define ESPI_CH0_NP_TX_DMAL 0x130 -+#define ESPI_CH0_NP_TX_DMAH 0x134 -+#define ESPI_CH0_NP_TX_CTRL 0x138 -+#define ESPI_CH0_NP_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH0_NP_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH0_NP_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH0_NP_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH0_NP_TX_DATA 0x13c -+#define ESPI_CH0_MCYC0_SADDRL 0x140 -+#define ESPI_CH0_MCYC0_SADDRH 0x144 -+#define ESPI_CH0_MCYC0_TADDRL 0x148 -+#define ESPI_CH0_MCYC0_TADDRH 0x14c -+#define ESPI_CH0_MCYC0_MASKL 0x150 -+#define ESPI_CH0_MCYC0_MASKL_EN BIT(0) -+#define ESPI_CH0_MCYC0_MASKH 0x154 -+#define ESPI_CH0_MCYC1_SADDRL 0x158 -+#define ESPI_CH0_MCYC1_SADDRH 0x15c -+#define ESPI_CH0_MCYC1_TADDRL 0x160 -+#define ESPI_CH0_MCYC1_TADDRH 0x164 -+#define ESPI_CH0_MCYC1_MASKL 0x168 -+#define ESPI_CH0_MCYC1_MASKL_EN BIT(0) -+#define ESPI_CH0_MCYC1_MASKH 0x16c -+#define ESPI_CH0_WPROT0 0x1f8 -+#define ESPI_CH0_WPROT1 0x1fc ++enum mmbi_bmc_int_type { ++ MMBI_BMC_INT_NONE = 0, ++ MMBI_BMC_INT_MEM = 1, ++ MMBI_BMC_INT_INBAND = 2, ++}; + -+/* virtual wire channel (ch1) registers */ -+#define ESPI_CH1_CTRL 0x200 -+#define ESPI_CH1_CTRL_GPIO_HW BIT(9) -+#define ESPI_CH1_CTRL_SW_RDY BIT(1) -+#define ESPI_CH1_STS 0x204 -+#define ESPI_CH1_INT_STS 0x208 -+#define ESPI_CH1_INT_STS_GPIO BIT(2) -+#define ESPI_CH1_INT_STS_EVT0 BIT(0) -+#define ESPI_CH1_INT_EN 0x20c -+#define ESPI_CH1_INT_EN_GPIO BIT(2) -+#define ESPI_CH1_INT_EN_SYS_EVT0 BIT(0) -+#define ESPI_CH1_EVT0 0x210 -+#define ESPI_CH1_EVT0_PLTRSTN BIT(5) -+#define ESPI_CH1_EVT0_INT_EN 0x214 -+#define ESPI_CH1_EVT0_INT_EN_PLTRSTN BIT(5) -+#define ESPI_CH1_EVT0_INT_T0 0x218 -+#define ESPI_CH1_EVT0_INT_T0_PLTRSTN BIT(5) -+#define ESPI_CH1_EVT0_INT_T1 0x21c -+#define ESPI_CH1_EVT0_INT_T1_PLTRSTN BIT(5) -+#define ESPI_CH1_EVT0_INT_T2 0x220 -+#define ESPI_CH1_EVT0_INT_T2_PLTRSTN BIT(5) -+#define ESPI_CH1_EVT0_INT_STS 0x224 -+#define ESPI_CH1_EVT0_INT_STS_PLTRSTN BIT(5) -+#define ESPI_CH1_EVT1 0x230 -+#define ESPI_CH1_EVT1_INT_EN 0x234 -+#define ESPI_CH1_EVT1_INT_T0 0x238 -+#define ESPI_CH1_EVT1_INT_T1 0x23c -+#define ESPI_CH1_EVT1_INT_T2 0x240 -+#define ESPI_CH1_EVT1_INT_STS 0x244 -+#define ESPI_CH1_GPIO_VAL0 0x250 -+#define ESPI_CH1_GPIO_VAL1 0x254 -+#define ESPI_CH1_GPIO_DIR0 0x258 -+#define ESPI_CH1_GPIO_DIR1 0x25c -+#define ESPI_CH1_GPIO_RSTSEL0 0x260 -+#define ESPI_CH1_GPIO_RSTSEL1 0x264 -+#define ESPI_CH1_GPIO_GRP 0x268 -+#define ESPI_CH1_GP50_DIR0 0x270 -+#define ESPI_CH1_GP50_DIR1 0x274 -+#define ESPI_CH1_GP50_VAL0 0x278 -+#define ESPI_CH1_GP50_VAL1 0x27c -+#define ESPI_CH1_SW_INT 0x280 -+#define ESPI_CH1_INT_RSTSEL0 0x284 -+#define ESPI_CH1_INT_RSTSEL1 0x288 -+#define ESPI_CH1_WPROT0 0x2f8 -+#define ESPI_CH1_WPROT1 0x2fc ++enum mmbi_state { /* B_U B_R H_U H_R */ ++ INIT_IN_PROGRESS = 0x00, /* 0 0 0 0 */ ++ INIT_COMPLETED = 0x08, /* 1 0 0 0 */ ++ NORMAL_RUNTIME = 0x0A, /* 1 0 1 0 */ ++ RESET_REQ_BY_BMC = 0x0E, /* 1 1 1 0 */ ++ RESET_REQ_BY_HOST = 0x0B, /* 1 0 1 1 */ ++ RESET_ACKED = 0x0F, /* 1 1 1 1 */ ++ TRANS_TO_INIT = 0x07, /* 0 1 1 1 */ ++ INIT_MISMATCH = 0x09, /* 1 0 0 1 */ ++ POWER_UP_OR_ERROR = 0x80000000, ++}; + -+/* out-of-band channel (ch2) registers */ -+#define ESPI_CH2_CTRL 0x300 -+#define ESPI_CH2_CTRL_TX_RST BIT(31) -+#define ESPI_CH2_CTRL_RX_RST BIT(30) -+#define ESPI_CH2_CTRL_TX_DMA_EN BIT(17) -+#define ESPI_CH2_CTRL_RX_DMA_EN BIT(16) -+#define ESPI_CH2_CTRL_SW_RDY BIT(4) -+#define ESPI_CH2_STS 0x304 -+#define ESPI_CH2_INT_STS 0x308 -+#define ESPI_CH2_INT_STS_RX_CMPLT BIT(0) -+#define ESPI_CH2_INT_EN 0x30c -+#define ESPI_CH2_INT_EN_RX_CMPLT BIT(0) -+#define ESPI_CH2_RX_DMAL 0x310 -+#define ESPI_CH2_RX_DMAH 0x314 -+#define ESPI_CH2_RX_CTRL 0x318 -+#define ESPI_CH2_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_CH2_RX_CTRL_PEC BIT(24) -+#define ESPI_CH2_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH2_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH2_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH2_RX_DATA 0x31c -+#define ESPI_CH2_TX_DMAL 0x320 -+#define ESPI_CH2_TX_DMAH 0x324 -+#define ESPI_CH2_TX_CTRL 0x328 -+#define ESPI_CH2_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH2_TX_CTRL_PEC BIT(24) -+#define ESPI_CH2_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH2_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH2_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH2_TX_DATA 0x32c -+#define ESPI_CH2_RX_DESC_EPTR 0x330 -+#define ESPI_CH2_RX_DESC_RPTR 0x334 -+#define ESPI_CH2_RX_DESC_RPTR_UPDATE BIT(31) -+#define ESPI_CH2_RX_DESC_RPTR_RP GENMASK(11, 0) -+#define ESPI_CH2_RX_DESC_WPTR 0x338 -+#define ESPI_CH2_RX_DESC_WPTR_VALID BIT(31) -+#define ESPI_CH2_RX_DESC_WPTR_SP GENMASK(27, 16) -+#define ESPI_CH2_RX_DESC_WPTR_WP GENMASK(11, 0) -+#define ESPI_CH2_RX_DESC_TMOUT 0x33c -+#define ESPI_CH2_TX_DESC_EPTR 0x340 -+#define ESPI_CH2_TX_DESC_RPTR 0x344 -+#define ESPI_CH2_TX_DESC_RPTR_UPT BIT(31) -+#define ESPI_CH2_TX_DESC_WPTR 0x348 -+#define ESPI_CH2_TX_DESC_WPTR_VALID BIT(31) -+#define ESPI_CH2_WPROT0 0x3f8 -+#define ESPI_CH2_WPROT1 0x3fc ++enum mmbi_buffer_type { ++ MMBI_BUFFER_TYPE_VPSCB = 0x01, ++}; + -+/* flash channel (ch3) registers */ -+#define ESPI_CH3_CTRL 0x400 -+#define ESPI_CH3_CTRL_TX_RST BIT(31) -+#define ESPI_CH3_CTRL_RX_RST BIT(30) -+#define ESPI_CH3_CTRL_TX_DMA_EN BIT(17) -+#define ESPI_CH3_CTRL_RX_DMA_EN BIT(16) -+#define ESPI_CH3_CTRL_EDAF_MODE GENMASK(9, 8) -+#define ESPI_CH3_CTRL_SW_RDY BIT(5) -+#define ESPI_CH3_STS 0x404 -+#define ESPI_CH3_INT_STS 0x408 -+#define ESPI_CH3_INT_STS_RX_CMPLT BIT(0) -+#define ESPI_CH3_INT_EN 0x40c -+#define ESPI_CH3_INT_EN_RX_CMPLT BIT(0) -+#define ESPI_CH3_RX_DMAL 0x410 -+#define ESPI_CH3_RX_DMAH 0x414 -+#define ESPI_CH3_RX_CTRL 0x418 -+#define ESPI_CH3_RX_CTRL_SERV_PEND BIT(31) -+#define ESPI_CH3_RX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH3_RX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH3_RX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH3_RX_DATA 0x41c -+#define ESPI_CH3_TX_DMAL 0x420 -+#define ESPI_CH3_TX_DMAH 0x424 -+#define ESPI_CH3_TX_CTRL 0x428 -+#define ESPI_CH3_TX_CTRL_TRIG_PEND BIT(31) -+#define ESPI_CH3_TX_CTRL_LEN GENMASK(23, 12) -+#define ESPI_CH3_TX_CTRL_TAG GENMASK(11, 8) -+#define ESPI_CH3_TX_CTRL_CYC GENMASK(7, 0) -+#define ESPI_CH3_TX_DATA 0x42c -+#define ESPI_CH3_EDAF_TADDRL 0x430 -+#define ESPI_CH3_EDAF_TADDRH 0x434 -+#define ESPI_CH3_EDAF_MASKL 0x438 -+#define ESPI_CH3_EDAF_MASKH 0x43c -+#define ESPI_CH3_WPROT0 0x4f8 -+#define ESPI_CH3_WPROT1 0x4fc ++enum mmbi_multi_protocol { ++ MMBI_PROTOCOL_MCTP = 0x04, ++}; + -+/* eDAF filter registers */ -+#define ESPI_EDAF_FLTR_SADDR0 0x510 -+#define ESPI_EDAF_FLTR_EADDR0 0x514 -+#define ESPI_EDAF_FLTR_SADDR1 0x518 -+#define ESPI_EDAF_FLTR_EADDR1 0x51c -+#define ESPI_EDAF_FLTR_SADDR2 0x520 -+#define ESPI_EDAF_FLTR_EADDR2 0x524 -+#define ESPI_EDAF_FLTR_SADDR3 0x528 -+#define ESPI_EDAF_FLTR_EADDR3 0x52c -+#define ESPI_EDAF_FLTR_SADDR4 0x530 -+#define ESPI_EDAF_FLTR_EADDR4 0x534 -+#define ESPI_EDAF_FLTR_SADDR5 0x538 -+#define ESPI_EDAF_FLTR_EADDR5 0x53c -+#define ESPI_EDAF_FLTR_SADDR6 0x540 -+#define ESPI_EDAF_FLTR_EADDR6 0x544 -+#define ESPI_EDAF_FLTR_SADDR7 0x548 -+#define ESPI_EDAF_FLTR_EADDR7 0x54c -+#define ESPI_EDAF_FLTR_SADDR8 0x550 -+#define ESPI_EDAF_FLTR_EADDR8 0x554 -+#define ESPI_EDAF_FLTR_SADDR9 0x558 -+#define ESPI_EDAF_FLTR_EADDR9 0x55c -+#define ESPI_EDAF_FLTR_SADDR10 0x560 -+#define ESPI_EDAF_FLTR_EADDR10 0x564 -+#define ESPI_EDAF_FLTR_SADDR11 0x568 -+#define ESPI_EDAF_FLTR_EADDR11 0x56c -+#define ESPI_EDAF_FLTR_SADDR12 0x570 -+#define ESPI_EDAF_FLTR_EADDR12 0x574 -+#define ESPI_EDAF_FLTR_SADDR13 0x578 -+#define ESPI_EDAF_FLTR_EADDR13 0x57c -+#define ESPI_EDAF_FLTR_SADDR14 0x580 -+#define ESPI_EDAF_FLTR_EADDR14 0x584 -+#define ESPI_EDAF_FLTR_SADDR15 0x588 -+#define ESPI_EDAF_FLTR_EADDR15 0x58c -+#define ESPI_EDAF_WPROT0 0x5f8 -+#define ESPI_EDAF_WPROT1 0x5fc ++enum mmbi_app_interface { ++ MMBI_APP_INTF_IOCTL = 0, ++ MMBI_APP_INTF_MCTP_NETDEV, ++}; ++ ++struct mmbi_chan_priv; ++ ++struct mmbi_buf_vpscb { ++ u32 h_ros_p; /* Host Read Pointer address offset */ ++ u32 h_rws_p; /* Host Read Write Pointer address offset */ ++ u8 host_int_val_ch; /* Host interrupt value, only used in MMBI v1.1 */ ++ u8 bmc_int_val_ch; /* Host interrupt value, only used in MMBI v1.1 */ ++}; ++ ++struct mmbi_chan_desc { ++ u32 b2h_ba_offset; /* B2H Buffer Base Address */ ++ u32 h2b_ba_offset; /* H2B Buffer Base Address */ ++ u32 b2h_l; /* B2H Buffer Length */ ++ u32 h2b_l; /* H2B Buffer Length */ ++ u8 buffer_type; /* Buffer Type defined by enum mmbi_buffer_type */ ++ u8 index; /* Channel Index filled by mmbi_instance_init */ ++ enum mmbi_app_interface app_interface; /* User interface for this channel */ ++ struct mmbi_buf_vpscb buffer_desc; /* buffer descriptor information */ ++ struct mmbi_ins_desc *mmbi; /* Back pointer to instance descriptor */ ++ struct mmbi_chan_priv *priv; /* Internal runtime state */ ++ u32 poll_interval_ms; /* polling interval in ms, default 10ms */ ++}; ++ ++struct mmbi_ins_desc { ++ u8 __iomem *desc_virt; /* MMBI Instance Base Address */ ++ u8 mmbi_version; ++ u8 num_of_channels; /* NOI field, recording number of channels */ ++ u8 os_use; ++ u8 host_int_type; ++ u8 host_int_location; ++ u8 host_int_value; ++ u8 bmc_int_type; ++ u8 bmc_int_value; ++ u32 bmc_int_location; ++ int ins_id; ++ struct mmbi_chan_desc chan_desc[MMBI_MAX_CHANNELS]; ++ struct device *dev; ++ enum mmbi_role role; ++}; ++ ++void mmbi_channel_state_update(struct mmbi_chan_desc *chan, ++ u8 __iomem *desc_virt); ++int mmbi_channel_avail_length(u8 __iomem *read_structure, ++ u8 __iomem *write_structure, u32 buf_size); ++int mmbi_channel_unhandled_length(u8 __iomem *read_structure, ++ u8 __iomem *write_structure, u32 buf_size); ++int mmbi_instance_init(struct mmbi_ins_desc *mmbi); ++void mmbi_instance_remove(struct mmbi_ins_desc *mmbi); ++ ++#endif +diff --git a/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.c b/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.c +--- a/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.c 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.c 2026-07-17 08:44:55.293306504 +0000 +@@ -0,0 +1,481 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright (C) ASPEED Technology Inc. ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "aspeed-pcie-mmbi.h" ++#include "aspeed-mmbi.h" ++ ++/* AST2700 E2M */ ++#define ASPEED_E2M_EVENT 0x0D0 ++#define ASPEED_E2M_EVENT_SET 0x0D4 ++#define ASPEED_E2M_EVENT_CLR 0x0D8 ++#define ASPEED_E2M_EVENT_EN 0x0DC ++#define ASPEED_E2M_ADRMAP00 0x100 ++#define ASPEED_E2M_WIRQA0 0x180 ++#define ASPEED_E2M_WIRQV0 0x1C0 ++#define ASPEED_E2M_SPROT_SIDG0 0x210 ++#define ASPEED_E2M_SPROT_CTL0 0x280 ++#define ASPEED_E2M_SPROT_ADR0 0x2C0 ++/* Value for triggering interrupt */ ++#define ASPEED_E2M_INT_VAL 0xC6 ++ ++/* AST2700 SCU */ ++#define ASPEED_SCU_DECODE_DEV BIT(18) ++#define ASPEED_SCU_INT_EN BIT(23) ++struct aspeed_platform { ++ int (*mmbi_init)(struct platform_device *pdev); ++}; ++ ++#define ASPEED_MMBI_APP_INTF_PROP "aspeed,mmbi-interface" ++ ++struct aspeed_pcie_mmbi { ++ struct device *dev; ++ struct regmap *device; ++ struct regmap *e2m; ++ const struct aspeed_platform *platform; ++ /* E2M index */ ++ int id; ++ int pid; ++ int msi_index; ++ int scu_bar_offset; ++ int e2m_index; ++ int e2m_h2b_int; ++ int num_of_channels; ++ bool host_int_en; ++ ++ /* Memory Mapping */ ++ void __iomem *mem_virt; ++ dma_addr_t mem_phy; ++ phys_addr_t mem_size; ++ ++ struct mmbi_ins_desc mmbi_desc; ++}; ++ ++static const char * ++aspeed_pcie_mmbi_app_interface_name(enum mmbi_app_interface app_interface) ++{ ++ switch (app_interface) { ++ case MMBI_APP_INTF_MCTP_NETDEV: ++ return "mctp-netdev"; ++ case MMBI_APP_INTF_IOCTL: ++ default: ++ return "ioctl"; ++ } ++} ++ ++static int aspeed_pcie_mmbi_parse_app_interface_string(struct device *dev, ++ const char *interface, ++ enum mmbi_app_interface *app_interface) ++{ ++ if (!strcmp(interface, "ioctl")) { ++ *app_interface = MMBI_APP_INTF_IOCTL; ++ return 0; ++ } ++ ++ if (!strcmp(interface, "mctp-netdev")) { ++ *app_interface = MMBI_APP_INTF_MCTP_NETDEV; ++ return 0; ++ } + -+/* MMBI registers */ -+#define ESPI_MMBI_CTRL 0x800 -+#define ESPI_MMBI_CTRL_INST_NUM GENMASK(6, 4) -+#define ESPI_MMBI_CTRL_EN BIT(0) -+#define ESPI_MMBI_INT_STS 0x808 -+#define ESPI_MMBI_INT_EN 0x80c -+#define ESPI_MMBI_HOST_RWP(x) (0x810 + ((x) << 3)) ++ dev_err(dev, "invalid %s value '%s'\n", ++ ASPEED_MMBI_APP_INTF_PROP, interface); ++ return -EINVAL; ++} + -+void ast2700_espi_pre_init(struct aspeed_espi *espi); -+void ast2700_espi_post_init(struct aspeed_espi *espi); -+void ast2700_espi_deinit(struct aspeed_espi *espi); -+int ast2700_espi_perif_probe(struct aspeed_espi *espi); -+int ast2700_espi_perif_remove(struct aspeed_espi *espi); -+int ast2700_espi_vw_probe(struct aspeed_espi *espi); -+int ast2700_espi_vw_remove(struct aspeed_espi *espi); -+int ast2700_espi_oob_probe(struct aspeed_espi *espi); -+int ast2700_espi_oob_remove(struct aspeed_espi *espi); -+int ast2700_espi_flash_probe(struct aspeed_espi *espi); -+int ast2700_espi_flash_remove(struct aspeed_espi *espi); -+irqreturn_t ast2700_espi_isr(int irq, void *arg); ++static int aspeed_pcie_mmbi_parse_app_interface(struct device *dev, ++ struct mmbi_ins_desc *mmbi_desc) ++{ ++ int count, i, ret; + -+#endif -diff --git a/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c b/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c ---- a/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/espi/ast2700-rtc-over-espi.c 2026-04-08 18:03:48.313705265 +0000 -@@ -0,0 +1,193 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright 2025 Aspeed Technology Inc. ++ for (i = 0; i < mmbi_desc->num_of_channels; i++) ++ mmbi_desc->chan_desc[i].app_interface = MMBI_APP_INTF_IOCTL; ++ ++ if (!of_find_property(dev->of_node, ASPEED_MMBI_APP_INTF_PROP, NULL)) ++ return 0; ++ ++ count = of_property_count_strings(dev->of_node, ASPEED_MMBI_APP_INTF_PROP); ++ if (count < 0) { ++ dev_err(dev, "invalid %s property\n", ASPEED_MMBI_APP_INTF_PROP); ++ return count; ++ } ++ ++ if (count != 1 && count != mmbi_desc->num_of_channels) { ++ dev_err(dev, "%s must contain either 1 or %u strings\n", ++ ASPEED_MMBI_APP_INTF_PROP, ++ (u32)mmbi_desc->num_of_channels); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < mmbi_desc->num_of_channels; i++) { ++ const char *interface; ++ int index = count == 1 ? 0 : i; ++ ++ ret = of_property_read_string_index(dev->of_node, ++ ASPEED_MMBI_APP_INTF_PROP, ++ index, &interface); ++ if (ret) ++ return ret; ++ ++ ret = aspeed_pcie_mmbi_parse_app_interface_string(dev, interface, ++ &mmbi_desc->chan_desc[i].app_interface); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Detailed Buffer Layout: ++ * ++ * +---------------------------------------+ ++ * | HOST READ ONLY REGION (50%) | ++ * +---------------------------------------+ ++ * | mmbi_desc | ++ * | - Size: 32 + 32*noi bytes | ++ * +---------------------------------------+ ++ * | Interrupt Region (8 bytes) | ++ * | - trigger_int (1 byte) | ++ * | - reserved (7 bytes) | ++ * +---------------------------------------+ ++ * | Channel 0: | ++ * | hos_ro (8 bytes) | ++ * | - First 8 bytes after interrupt | ++ * | b2h_buffer | ++ * | - Channel 0 B2H buffer space | ++ * +---------------------------------------+ ++ * | Channel 1: | ++ * | hos_ro (8 bytes) | ++ * | b2h_buffer | ++ * | - Channel 1 B2H buffer space | ++ * +---------------------------------------+ ++ * | ... | ++ * +---------------------------------------+ ++ * | Channel 6: | ++ * | hos_ro (8 bytes) | ++ * | b2h_buffer | ++ * | - Channel 6 B2H buffer space | ++ * +---------------------------------------+ ++ * | HOST READ/WRITE REGION (50%) | ++ * +---------------------------------------+ ++ * | Channel 0: | ++ * | hos_rw (8 bytes) | ++ * | - First 8 bytes of this region | ++ * | h2b_buffer | ++ * | - Channel 0 H2B buffer space | ++ * +---------------------------------------+ ++ * | Channel 1: | ++ * | hos_rw (8 bytes) | ++ * | h2b_buffer | ++ * | - Channel 1 H2B buffer space | ++ * +---------------------------------------+ ++ * | ... | ++ * +---------------------------------------+ ++ * | Channel 6: | ++ * | hos_rw (8 bytes) | ++ * | h2b_buffer | ++ * | - Channel 6 H2B buffer space | ++ * +---------------------------------------+ + */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++static int aspeed_pcie_mmbi_init(struct aspeed_pcie_mmbi *mmbi) ++{ ++ struct mmbi_ins_desc *mmbi_desc = &mmbi->mmbi_desc; ++ struct mmbi_buf_vpscb *vpscb; ++ struct device *dev = mmbi->dev; ++ u32 desc_size, b2h_buf_size, h2b_buf_size; ++ int i, ret; + -+#define SYNC_INTERVAL_MS_DEFAULT 1000 ++ desc_size = MMBI_DESC_SIZE_PREFIX + MMBI_DESC_SIZE_INTERRUPT + ++ MMBI_DESC_SIZE_CHANNEL * mmbi->num_of_channels + 8; + -+#define RTC_REG0 0x0 -+#define RTC_REG0_MINUTES_ALARM GENMASK(31, 24) -+#define RTC_REG0_MINUTES GENMASK(23, 16) -+#define RTC_REG0_SECONDS_ALARM GENMASK(15, 8) -+#define RTC_REG0_SECONDS GENMASK(7, 0) -+#define RTC_REG1 0x4 -+#define RTC_REG1_DAY_OF_MONTH GENMASK(31, 24) -+#define RTC_REG1_DAY_OF_WEEK GENMASK(23, 16) -+#define RTC_REG1_HOURS_ALARM GENMASK(15, 8) -+#define RTC_REG1_HOURS GENMASK(7, 0) -+#define RTC_REG2 0x8 -+#define RTC_REG2_YEAR GENMASK(15, 8) -+#define RTC_REG2_MONTH GENMASK(7, 0) -+#define RTC_REG_MAX 0xc ++ if ((mmbi->mem_size >> 1) <= desc_size) { ++ dev_err(dev, "MMBI memory size too small for B2H buffer\n"); ++ return -EINVAL; ++ } + -+struct rtc_espi_sync { -+ struct rtc_device *rtc_dev; -+ struct device *dev; -+ void __iomem *espi_ram_base; -+ u32 interval_ms; -+ struct delayed_work sync_work; -+ struct workqueue_struct *wq; -+}; ++ b2h_buf_size = ++ rounddown_pow_of_two(((mmbi->mem_size >> 1) - desc_size) / ++ mmbi->num_of_channels); ++ ++ h2b_buf_size = rounddown_pow_of_two((mmbi->mem_size >> 1) / mmbi->num_of_channels); ++ ++ mmbi_desc->desc_virt = mmbi->mem_virt; ++ mmbi_desc->mmbi_version = MMBI_VERSION_1_1; ++ mmbi_desc->os_use = 1; ++ mmbi_desc->num_of_channels = mmbi->num_of_channels; ++ mmbi_desc->host_int_type = MMBI_HOST_INT_PCIE; ++ mmbi_desc->host_int_location = mmbi->msi_index; ++ mmbi_desc->host_int_value = 0; /* cleared to 0 */ ++ mmbi_desc->bmc_int_type = MMBI_BMC_INT_INBAND; ++ mmbi_desc->bmc_int_location = 0; /* reserved */ ++ mmbi_desc->bmc_int_value = 0; /* cleared to 0 */ ++ mmbi_desc->dev = dev; ++ mmbi_desc->role = MMBI_ROLE_BMC; ++ ++ for (i = 0; i < mmbi_desc->num_of_channels; i++) { ++ vpscb = &mmbi_desc->chan_desc[i].buffer_desc; ++ vpscb->h_ros_p = desc_size + i * b2h_buf_size; ++ vpscb->h_rws_p = (mmbi->mem_size >> 1) + i * h2b_buf_size; ++ vpscb->host_int_val_ch = 0; ++ vpscb->bmc_int_val_ch = 0; ++ ++ mmbi_desc->chan_desc[i].b2h_ba_offset = vpscb->h_ros_p + 8; ++ mmbi_desc->chan_desc[i].h2b_ba_offset = vpscb->h_rws_p + 8; ++ mmbi_desc->chan_desc[i].b2h_l = b2h_buf_size - 8; ++ mmbi_desc->chan_desc[i].h2b_l = h2b_buf_size - 8; ++ // mmbi_desc->chan_desc[i].h2b_ba_offset = vpscb->h_rws_p + 16; ++ // mmbi_desc->chan_desc[i].b2h_l = 0x100; ++ // mmbi_desc->chan_desc[i].h2b_l = 0x100; ++ mmbi_desc->chan_desc[i].buffer_type = MMBI_BUFFER_TYPE_VPSCB; ++ } ++ ret = mmbi_instance_init(mmbi_desc); ++ if (ret) ++ dev_err(dev, "MMBI instance init failed: %d\n", ret); + -+static void sync_rtc_to_espi_work(struct work_struct *work) ++ return ret; ++} ++ ++/* ++ * AST2700 PCIe MMBI (SCU & E2M) ++ * SoC | 0 | 1 | ++ * PCI class | MFD (0xFF_00_00) | MMBI (0x0C_0C_00) | ++ * Node | 0 1 | 0 | ++ * PID | 3 4 5 6 11 12 13 14 | 2 3 4 5 6 7 | ++ * E2M index | 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 | ++ * BAR index | 2 3 4 5 2 3 4 5 | 0 1 2 3 4 5 | ++ * SCU BAR | 3c 4c 5c 6c 3c 4c 5c 6c | 1c 50 3c 4c 5c 6c | ++ * E2M H2B Int | 0 1 2 3 0 1 2 3 | 0 1 2 3 4 5 | (bit) ++ */ ++static int aspeed_ast2700_pcie_mmbi_init(struct platform_device *pdev) +{ -+ struct rtc_espi_sync *ctx = container_of(to_delayed_work(work), struct rtc_espi_sync, sync_work); -+ struct rtc_time tm; -+ u32 reg, sec, min, hour, day_of_week, day_of_month, month, year; -+ u32 sec_alarm, min_alarm, hour_alarm; -+ int ret; ++ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ u32 e2m_index, pid; ++ struct resource res; ++ int ret, i; + -+ if (!ctx->espi_ram_base) { -+ dev_err(ctx->dev, "eSPI RAM base not mapped\n"); -+ return; ++ /* Get register map*/ ++ mmbi->e2m = syscon_node_to_regmap(dev->of_node->parent); ++ if (IS_ERR(mmbi->e2m)) { ++ dev_err(dev, "failed to find e2m regmap\n"); ++ return PTR_ERR(mmbi->e2m); ++ } ++ if (of_address_to_resource(dev->of_node->parent, 0, &res)) { ++ dev_err(dev, "Failed to get e2m resource\n"); ++ return -EINVAL; ++ } ++ if (res.start == 0x14c1d000) ++ mmbi->id = 2; ++ else if (res.start == 0x12c22000) ++ mmbi->id = 1; ++ else ++ mmbi->id = 0; /* 0x12c21000 */ ++ ++ mmbi->device = syscon_regmap_lookup_by_phandle(dev->of_node->parent, "aspeed,device"); ++ if (IS_ERR(mmbi->device)) { ++ dev_err(dev, "failed to find device regmap\n"); ++ return PTR_ERR(mmbi->device); + } + -+ ret = rtc_read_time(ctx->rtc_dev, &tm); ++ ret = of_property_read_u32(dev->of_node, "index", &mmbi->e2m_index); + if (ret < 0) { -+ dev_err(ctx->dev, "Failed to read RTC time: %d\n", ret); -+ goto reschedule; ++ dev_err(dev, "cannot get mmbi index value\n"); ++ return ret; + } -+ dev_dbg(ctx->dev, "%s: tm data: secs=%d, mins=%d, hours=%d, mon=%d, year=%d\n", -+ __func__, tm.tm_sec, tm.tm_min, tm.tm_hour, tm.tm_mon, tm.tm_year); -+ dev_dbg(ctx->dev, "%s: tm data: wday(day of week)=%d, mday(day of month)=%d\n", -+ __func__, tm.tm_wday, tm.tm_mday); + -+ /* Write the eSPI RAM registers */ -+ reg = FIELD_PREP(RTC_REG0_SECONDS, tm.tm_sec) | -+ FIELD_PREP(RTC_REG0_MINUTES, tm.tm_min); -+ writel(reg, (ctx->espi_ram_base + RTC_REG0)); ++ ret = of_property_read_u32(dev->of_node, "pid", &mmbi->pid); ++ if (ret < 0) { ++ dev_err(dev, "cannot get mmbi pid value\n"); ++ return ret; ++ } + -+ reg = FIELD_PREP(RTC_REG1_HOURS, tm.tm_hour) | -+ FIELD_PREP(RTC_REG1_DAY_OF_MONTH, tm.tm_mday) | -+ FIELD_PREP(RTC_REG1_DAY_OF_WEEK, tm.tm_wday + 1); -+ writel(reg, ctx->espi_ram_base + RTC_REG1); ++ ret = of_property_read_u32(dev->of_node, "bar", &mmbi->scu_bar_offset); ++ if (ret < 0) { ++ dev_err(dev, "cannot get mmbi bar value\n"); ++ return ret; ++ } + -+ reg = FIELD_PREP(RTC_REG2_MONTH, tm.tm_mon + 1) | -+ FIELD_PREP(RTC_REG2_YEAR, tm.tm_year % 100); -+ writel(reg, ctx->espi_ram_base + RTC_REG2); ++ e2m_index = mmbi->e2m_index; ++ pid = mmbi->pid; ++ mmbi->e2m_h2b_int += mmbi->e2m_index; ++ if (mmbi->id < 2) { ++ /* PCIe device class, sub-class, protocol and reversion */ ++ regmap_write(mmbi->device, 0x18, 0x0C0C0027); ++ } else { ++ regmap_write(mmbi->device, 0x18, 0x0C0C0027); ++ regmap_write(mmbi->device, 0x78, ASPEED_SCU_INT_EN | ASPEED_SCU_DECODE_DEV); ++ } + -+ /* Read back the values to verify */ -+ reg = readl(ctx->espi_ram_base + RTC_REG0); -+ sec = FIELD_GET(RTC_REG0_SECONDS, reg); -+ sec_alarm = FIELD_GET(RTC_REG0_SECONDS_ALARM, reg); -+ min = FIELD_GET(RTC_REG0_MINUTES, reg); -+ min_alarm = FIELD_GET(RTC_REG0_MINUTES_ALARM, reg); ++ /* MSI */ ++ regmap_update_bits(mmbi->device, 0x74, GENMASK(7, 4), BIT(7) | (5 << 4)); + -+ reg = readl(ctx->espi_ram_base + RTC_REG1); -+ hour = FIELD_GET(RTC_REG1_HOURS, reg); -+ hour_alarm = FIELD_GET(RTC_REG1_HOURS_ALARM, reg); -+ day_of_week = FIELD_GET(RTC_REG1_DAY_OF_WEEK, reg); -+ day_of_month = FIELD_GET(RTC_REG1_DAY_OF_MONTH, reg); ++ regmap_update_bits(mmbi->device, 0x70, BIT(25) | BIT(17) | BIT(9) | BIT(1), ++ BIT(25) | BIT(17) | BIT(9) | BIT(1)); + -+ reg = readl(ctx->espi_ram_base + RTC_REG2); -+ month = FIELD_GET(RTC_REG2_MONTH, reg); -+ year = FIELD_GET(RTC_REG2_YEAR, reg); ++ /* Calculate the BAR Size */ ++ for (i = 1; i < 16; i++) { ++ /* bar size check for 4k align */ ++ if ((mmbi->mem_size / 4096) == (1 << (i - 1))) ++ break; ++ } ++ if (i == 16) { ++ i = 0; ++ dev_warn(dev, "Bar size not align for 4K : %dK\n", (u32)mmbi->mem_size / 1024); ++ } ++ regmap_write(mmbi->device, mmbi->scu_bar_offset, (mmbi->mem_phy >> 4) | i); ++ regmap_write(mmbi->e2m, ASPEED_E2M_ADRMAP00 + (4 * pid), (mmbi->mem_phy >> 4) | i); + -+ dev_dbg(ctx->dev, "%s: eSPI RAM data: secs=%d, mins=%d, hours=%d, ", -+ __func__, sec, min, hour); -+ dev_dbg(ctx->dev, "day_of_week=%d, day_of_month=%d, month=%d, year=%d\n", -+ day_of_week, day_of_month, month, year); -+ dev_dbg(ctx->dev, "%s: eSPI RAM data: secs_alarm=%d, mins_alarm=%d, hours_alarm=%d\n", -+ __func__, sec_alarm, min_alarm, hour_alarm); ++ ret = aspeed_pcie_mmbi_init(mmbi); ++ if (ret < 0) { ++ dev_err(dev, "Initialize MMBI device failed.\n"); ++ return ret; ++ } + -+reschedule: -+ queue_delayed_work(ctx->wq, &ctx->sync_work, msecs_to_jiffies(ctx->interval_ms)); ++ return 0; +} + -+static int rtc_espi_sync_probe(struct platform_device *pdev) ++struct aspeed_platform ast2700_platform = { ++ .mmbi_init = aspeed_ast2700_pcie_mmbi_init, ++}; ++ ++static const struct of_device_id aspeed_pcie_mmbi_of_matches[] = { ++ { .compatible = "aspeed,ast2700-pcie-mmbi", .data = &ast2700_platform }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_pcie_mmbi_of_matches); ++ ++static int aspeed_pcie_mmbi_probe(struct platform_device *pdev) +{ ++ struct aspeed_pcie_mmbi *mmbi; ++ struct mmbi_ins_desc *mmbi_desc; + struct device *dev = &pdev->dev; -+ struct rtc_espi_sync *ctx; -+ struct device_node *np = dev->of_node; -+ struct rtc_device *rtc; -+ struct resource *res; -+ u32 interval = SYNC_INTERVAL_MS_DEFAULT; ++ struct resource res; ++ struct device_node *np; ++ const void *md; ++ int i, ret = 0; + -+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) ++ md = of_device_get_match_data(dev); ++ if (!md) ++ return -ENODEV; ++ ++ mmbi = devm_kzalloc(&pdev->dev, sizeof(struct aspeed_pcie_mmbi), GFP_KERNEL); ++ if (!mmbi) + return -ENOMEM; ++ dev_set_drvdata(dev, mmbi); + -+ /* Ensure the device tree node is valid */ -+ rtc = rtc_class_open("rtc0"); -+ if (!rtc) { -+ dev_info(dev, "RTC not ready, deferring probe\n"); -+ return -EPROBE_DEFER; -+ } ++ mmbi->dev = dev; ++ mmbi->platform = md; + -+ /* Check if the RTC device supports the read_time operation */ -+ if (!rtc->ops || !rtc->ops->read_time) { -+ dev_err(dev, "RTC device does not support read_time operation\n"); -+ rtc_class_close(rtc); -+ return -ENODEV; ++ /* Get MMBI memory size */ ++ np = of_parse_phandle(dev->of_node, "memory-region", 0); ++ if (!np || of_address_to_resource(np, 0, &res)) { ++ dev_err(dev, "Failed to find memory-region.\n"); ++ ret = -ENOMEM; ++ goto out_region; + } + -+ ctx->rtc_dev = rtc; -+ ctx->dev = dev; ++ of_node_put(np); + -+ /* Get eSPI RAM handle and map */ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ ctx->espi_ram_base = devm_ioremap_resource(dev, res); -+ if (IS_ERR(ctx->espi_ram_base)) -+ return PTR_ERR(ctx->espi_ram_base); ++ mmbi->mem_phy = res.start; ++ mmbi->mem_size = resource_size(&res); ++ mmbi->mem_virt = ioremap(mmbi->mem_phy, mmbi->mem_size); ++ if (!mmbi->mem_virt) { ++ dev_err(dev, "cannot map mmbi memory region\n"); ++ ret = -ENOMEM; ++ goto out_region; ++ } + -+ if (!ctx->espi_ram_base) -+ memset(ctx->espi_ram_base, 0, RTC_REG_MAX); ++ mmbi_desc = &mmbi->mmbi_desc; ++ memset(mmbi_desc, 0, sizeof(struct mmbi_ins_desc)); + -+ /* Optional: polling interval */ -+ of_property_read_u32(np, "interval-ms", &interval); -+ ctx->interval_ms = interval; ++ ret = of_property_read_u32(dev->of_node, "noi", &mmbi->num_of_channels); ++ if (ret || mmbi->num_of_channels == 0 || ++ mmbi->num_of_channels > MMBI_MAX_CHANNELS) { ++ dev_err(dev, "ret %d, MMBI NOI %d\n", ret, mmbi->num_of_channels); ++ goto out_unmap; ++ } ++ mmbi_desc->num_of_channels = mmbi->num_of_channels; + -+ ctx->wq = alloc_workqueue("rtc_espi_sync_wq", WQ_UNBOUND, 0); -+ if (!ctx->wq) -+ return -ENOMEM; ++ ret = aspeed_pcie_mmbi_parse_app_interface(dev, mmbi_desc); ++ if (ret) ++ goto out_unmap; + -+ INIT_DELAYED_WORK(&ctx->sync_work, sync_rtc_to_espi_work); -+ queue_delayed_work(ctx->wq, &ctx->sync_work, 0); ++ /* B2H Interrupt */ ++ mmbi->host_int_en = true; ++ ret = of_property_read_u32(dev->of_node, "msi", &mmbi->msi_index); ++ if (ret) { ++ dev_err(dev, "cannot get valid MMBI B2H interrupt location\n"); ++ mmbi->host_int_en = false; ++ } + -+ dev_info(dev, "RTC-eSPI RAM sync initialized, every %u ms\n", ctx->interval_ms); ++ ret = mmbi->platform->mmbi_init(pdev); ++ if (ret) { ++ dev_err(dev, "Initialize pcie mmbi failed\n"); ++ goto out_unmap; ++ } ++ ++ for (i = 0; i < mmbi_desc->num_of_channels; i++) ++ dev_info(dev, "channel %d application interface: %s\n", ++ i, aspeed_pcie_mmbi_app_interface_name(mmbi_desc->chan_desc[i].app_interface)); ++ dev_info(dev, "ASPEED PCIe MMBI Dev %d: driver successfully loaded.\n", mmbi->id); + -+ platform_set_drvdata(pdev, ctx); + return 0; ++out_unmap: ++ iounmap(mmbi->mem_virt); ++out_region: ++ devm_kfree(dev, mmbi); ++ dev_warn(dev, "aspeed pcie mmbi: driver init failed (ret=%d)!\n", ret); ++ return ret; +} + -+static void rtc_espi_sync_remove(struct platform_device *pdev) ++static void aspeed_pcie_mmbi_remove(struct platform_device *pdev) +{ -+ struct rtc_espi_sync *ctx = platform_get_drvdata(pdev); -+ -+ cancel_delayed_work_sync(&ctx->sync_work); -+ destroy_workqueue(ctx->wq); ++ struct aspeed_pcie_mmbi *mmbi = platform_get_drvdata(pdev); + -+ if (ctx->rtc_dev) -+ rtc_class_close(ctx->rtc_dev); ++ mmbi_instance_remove(&mmbi->mmbi_desc); ++ iounmap(mmbi->mem_virt); ++ devm_kfree(&pdev->dev, mmbi); +} + -+static const struct of_device_id rtc_espi_sync_of_match[] = { -+ { .compatible = "aspeed,ast2700-rtc-over-espi" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rtc_espi_sync_of_match); -+ -+static struct platform_driver rtc_espi_sync_driver = { -+ .driver = { -+ .name = "ast2700-rtc-over-espi", -+ .of_match_table = rtc_espi_sync_of_match, ++static struct platform_driver aspeed_pcie_mmbi_driver = { ++ .probe = aspeed_pcie_mmbi_probe, ++ .remove = aspeed_pcie_mmbi_remove, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .of_match_table = aspeed_pcie_mmbi_of_matches, + }, -+ .probe = rtc_espi_sync_probe, -+ .remove = rtc_espi_sync_remove, +}; -+module_platform_driver(rtc_espi_sync_driver); + ++module_platform_driver(aspeed_pcie_mmbi_driver); ++ ++MODULE_AUTHOR("Jacky Chou "); ++MODULE_DESCRIPTION("ASPEED PCI-E MMBI Driver"); +MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Kevin Chen "); -+MODULE_DESCRIPTION("RTC to eSPI RAM sync driver"); +diff --git a/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.h b/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.h +--- a/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.h 1970-01-01 00:00:00.000000000 +0000 ++++ b/drivers/soc/aspeed/mmbi/aspeed-pcie-mmbi.h 2026-07-17 08:44:55.293306504 +0000 +@@ -0,0 +1,24 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2024 Aspeed Technology Inc. ++ */ ++#ifndef __ASPEED_PCIE_MMBI_H__ ++#define __ASPEED_PCIE_MMBI_H__ ++ ++struct aspeed_pcie_mmbi; ++ ++struct aspeed_mmbi_channel { ++ struct aspeed_pcie_mmbi *mmbi; ++ struct device *dev; ++ ++ bool bmc_int_en; ++ u8 bmc_int_value; ++ u32 bmc_int_location; ++ u8 __iomem *bmc_int_vmem; ++ ++ bool host_int_en; ++ u8 host_int_location; ++ u8 host_int_value; ++}; ++ ++#endif diff --git a/drivers/soc/aspeed/rvas/Kconfig b/drivers/soc/aspeed/rvas/Kconfig --- a/drivers/soc/aspeed/rvas/Kconfig 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/Kconfig 2026-04-08 18:03:48.313705265 +0000 ++++ b/drivers/soc/aspeed/rvas/Kconfig 2026-07-17 08:44:55.293306504 +0000 @@ -0,0 +1,9 @@ +menu "ASPEED RVAS drivers" + @@ -82522,14 +86202,14 @@ diff --git a/drivers/soc/aspeed/rvas/Kconfig b/drivers/soc/aspeed/rvas/Kconfig +endmenu diff --git a/drivers/soc/aspeed/rvas/Makefile b/drivers/soc/aspeed/rvas/Makefile --- a/drivers/soc/aspeed/rvas/Makefile 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/Makefile 2026-04-08 18:03:48.313705265 +0000 ++++ b/drivers/soc/aspeed/rvas/Makefile 2026-07-17 08:44:55.293306504 +0000 @@ -0,0 +1,3 @@ +obj-$(CONFIG_ASPEED_RVAS) += rvas.o +rvas-y := video_main.o hardware_engines.o video_engine.o + diff --git a/drivers/soc/aspeed/rvas/hardware_engines.c b/drivers/soc/aspeed/rvas/hardware_engines.c --- a/drivers/soc/aspeed/rvas/hardware_engines.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/hardware_engines.c 2026-04-08 18:03:48.313705265 +0000 ++++ b/drivers/soc/aspeed/rvas/hardware_engines.c 2026-07-17 08:44:55.293306504 +0000 @@ -0,0 +1,2201 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -84734,8 +88414,8 @@ diff --git a/drivers/soc/aspeed/rvas/hardware_engines.c b/drivers/soc/aspeed/rva + diff --git a/drivers/soc/aspeed/rvas/hardware_engines.h b/drivers/soc/aspeed/rvas/hardware_engines.h --- a/drivers/soc/aspeed/rvas/hardware_engines.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/hardware_engines.h 2026-04-08 18:03:48.314705247 +0000 -@@ -0,0 +1,550 @@ ++++ b/drivers/soc/aspeed/rvas/hardware_engines.h 2026-07-17 08:44:55.294306489 +0000 +@@ -0,0 +1,554 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of the ASPEED Linux Device Driver for ASPEED Baseboard Management Controller. @@ -84827,7 +88507,11 @@ diff --git a/drivers/soc/aspeed/rvas/hardware_engines.h b/drivers/soc/aspeed/rva +#define SCU_RVAS_STOP_CLOCK_BIT BIT(25) +// +//MCR -edac -+#define MCR_CONF 0x04 /* configuration register */ ++#define MCR_CONF 0x04 /* configuration register */ ++ ++#define MCR_CONF_AST27XX 0x10 /* configuration register */ ++#define MCR_GRAPHIC_MEM_CONF_AST27XX 0x100 /* graphic memory configuration register */ ++#define MCR_GRAPHIC_MEM_BASE_AST27XX 0x10C /* graphic memory base: [25:17]vga1 in 16MB; [9:1]vga0 in 16MB */ + +//DP +#define DPTX_Configuration_Register (0x100) @@ -85079,7 +88763,7 @@ diff --git a/drivers/soc/aspeed/rvas/hardware_engines.h b/drivers/soc/aspeed/rva + +struct VGAMemInfo { + u32 dwVGASize; -+ u32 dwDRAMSize; ++ u64 qwDRAMSize; + phys_addr_t qwFBPhysStart; +}; + @@ -85149,7 +88833,7 @@ diff --git a/drivers/soc/aspeed/rvas/hardware_engines.h b/drivers/soc/aspeed/rva + +struct aspeed_rvas_config { + u8 version; -+ const u32 *dram_table; ++ const u64 *dram_table; +}; + +struct AstRVAS { @@ -85288,7 +88972,7 @@ diff --git a/drivers/soc/aspeed/rvas/hardware_engines.h b/drivers/soc/aspeed/rva +#endif // __HARDWAREENGINES_H__ diff --git a/drivers/soc/aspeed/rvas/video.h b/drivers/soc/aspeed/rvas/video.h --- a/drivers/soc/aspeed/rvas/video.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video.h 2026-04-08 18:03:48.314705247 +0000 ++++ b/drivers/soc/aspeed/rvas/video.h 2026-07-17 08:44:55.294306489 +0000 @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/****************************************************************************** @@ -85333,8 +89017,8 @@ diff --git a/drivers/soc/aspeed/rvas/video.h b/drivers/soc/aspeed/rvas/video.h +#endif // __RVAS_VIDEO_H__ diff --git a/drivers/soc/aspeed/rvas/video_debug.h b/drivers/soc/aspeed/rvas/video_debug.h --- a/drivers/soc/aspeed/rvas/video_debug.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_debug.h 2026-04-08 18:03:48.314705247 +0000 -@@ -0,0 +1,35 @@ ++++ b/drivers/soc/aspeed/rvas/video_debug.h 2026-07-17 08:44:55.294306489 +0000 +@@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019-2021 ASPEED Technology Inc. @@ -85352,27 +89036,30 @@ diff --git a/drivers/soc/aspeed/rvas/video_debug.h b/drivers/soc/aspeed/rvas/vid +//#define HARDWARE_ENGINE_DEBUG + +#ifdef RVAS_VIDEO_DEBUG -+#define VIDEO_DBG(fmt, args...) ({ dev_printk(KERNEL_INFO, pAstRVAS->pdev, "%s() " fmt, __func__, ## args); }) ++#define VIDEO_DBG(fmt, args...) \ ++dev_info(pAstRVAS->pdev, "%s() " fmt, __func__, ##args) +#else -+#define VIDEO_DBG(fmt, args...) do { (void)(fmt); (void)(args); } while (0) ++#define VIDEO_DBG(...) +#endif // RVAS_VIDEO_DEBUG + +#ifdef VIDEO_ENGINE_DEBUG -+#define VIDEO_ENG_DBG(fmt, args...) ({ dev_printk(KERNEL_INFO, pAstRVAS->pdev, "%s() " fmt, __func__, ## args); }) ++#define VIDEO_ENG_DBG(fmt, args...) \ ++dev_info(pAstRVAS->pdev, "%s() " fmt, __func__, ##args) +#else -+#define VIDEO_ENG_DBG(fmt, args...) do { (void)(fmt); (void)(args); } while (0) ++#define VIDEO_ENG_DBG(...) +#endif // RVAS_VIDEO_DEBUG + +#ifdef HARDWARE_ENGINE_DEBUG -+#define HW_ENG_DBG(fmt, args...) ({ dev_printk(KERNEL_INFO, pAstRVAS->pdev, "%s() " fmt, __func__, ## args); }) ++#define HW_ENG_DBG(fmt, args...) \ ++pr_info("%s() " fmt, __func__, ##args) +#else -+#define HW_ENG_DBG(fmt, args...) do { (void)(fmt); (void)(args); } while (0) ++#define HW_ENG_DBG(...) +#endif // RVAS_VIDEO_DEBUG + +#endif // AST_VIDEO_DEBUG_H_ diff --git a/drivers/soc/aspeed/rvas/video_engine.c b/drivers/soc/aspeed/rvas/video_engine.c --- a/drivers/soc/aspeed/rvas/video_engine.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_engine.c 2026-04-08 18:03:48.314705247 +0000 ++++ b/drivers/soc/aspeed/rvas/video_engine.c 2026-07-17 08:44:55.294306489 +0000 @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* @@ -85988,7 +89675,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_engine.c b/drivers/soc/aspeed/rvas/vi +{ + u32 vga_mem_size, mem_size; + -+ mem_size = pAstRVAS->FBInfo.dwDRAMSize; ++ mem_size = pAstRVAS->FBInfo.qwDRAMSize; + vga_mem_size = pAstRVAS->FBInfo.dwVGASize; + VIDEO_ENG_DBG("VGA Info : MEM Size %dMB, VGA Mem Size %dMB\n", mem_size / 1024 / 1024, vga_mem_size / 1024 / 1024); + return (mem_size - vga_mem_size); @@ -86714,7 +90401,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_engine.c b/drivers/soc/aspeed/rvas/vi +} diff --git a/drivers/soc/aspeed/rvas/video_engine.h b/drivers/soc/aspeed/rvas/video_engine.h --- a/drivers/soc/aspeed/rvas/video_engine.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_engine.h 2026-04-08 18:03:48.314705247 +0000 ++++ b/drivers/soc/aspeed/rvas/video_engine.h 2026-07-17 08:44:55.294306489 +0000 @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* @@ -86988,7 +90675,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_engine.h b/drivers/soc/aspeed/rvas/vi +#endif // __VIDEO_ENGINE_H__ diff --git a/drivers/soc/aspeed/rvas/video_ioctl.h b/drivers/soc/aspeed/rvas/video_ioctl.h --- a/drivers/soc/aspeed/rvas/video_ioctl.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_ioctl.h 2026-04-08 18:03:48.314705247 +0000 ++++ b/drivers/soc/aspeed/rvas/video_ioctl.h 2026-07-17 08:44:55.294306489 +0000 @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* @@ -87267,8 +90954,8 @@ diff --git a/drivers/soc/aspeed/rvas/video_ioctl.h b/drivers/soc/aspeed/rvas/vid +#endif // _VIDEO_IOCTL_H diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/video_main.c --- a/drivers/soc/aspeed/rvas/video_main.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/soc/aspeed/rvas/video_main.c 2026-04-08 18:03:48.313705265 +0000 -@@ -0,0 +1,1854 @@ ++++ b/drivers/soc/aspeed/rvas/video_main.c 2026-07-17 08:44:55.294306489 +0000 +@@ -0,0 +1,1866 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * File Name : video_main.c @@ -87531,13 +91218,13 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide + pAstRVAS->FBInfo.qwFBPhysStart = (pAstRVAS->config->version == 7) + ? DDR_BASE_27 + : DDR_BASE; -+ pAstRVAS->FBInfo.qwFBPhysStart += pAstRVAS->FBInfo.dwDRAMSize - pAstRVAS->FBInfo.dwVGASize + dw_offset; ++ pAstRVAS->FBInfo.qwFBPhysStart += pAstRVAS->FBInfo.qwDRAMSize - pAstRVAS->FBInfo.dwVGASize + dw_offset; + if (pAstRVAS->rvas_index == 1) + pAstRVAS->FBInfo.qwFBPhysStart -= pAstRVAS->FBInfo.dwVGASize; + -+ HW_ENG_DBG("Frame buffer start address: %#x, dram size: %#x, vga size: %#x\n", ++ HW_ENG_DBG("Frame buffer start address: %#llx, dram size: %#x, vga size: %#x\n", + pAstRVAS->FBInfo.qwFBPhysStart, -+ pAstRVAS->FBInfo.dwDRAMSize, ++ pAstRVAS->FBInfo.qwDRAMSize, + pAstRVAS->FBInfo.dwVGASize); + + return pAstRVAS->FBInfo.qwFBPhysStart; @@ -88421,16 +92108,13 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide + +static void reset_rvas_engine(struct AstRVAS *pAstRVAS) +{ -+ if (pAstRVAS->config->version == 7) { -+ regmap_write(pAstRVAS->scu, 0x200, 0x200); -+ mdelay(200); -+ regmap_write(pAstRVAS->scu, 0x244, 0x2000000); -+ mdelay(100); -+ regmap_write(pAstRVAS->scu, 0x204, 0x200); -+ } else { -+ disable_rvas_engines(pAstRVAS); -+ enable_rvas_engines(pAstRVAS); -+ } ++ disable_rvas_engines(pAstRVAS); ++ mdelay(200); ++ if (pAstRVAS->config->version == 7) ++ reset_control_deassert(pAstRVAS->rvas_reset); ++ mdelay(200); ++ enable_rvas_engines(pAstRVAS); ++ + rvas_init(pAstRVAS); +} + @@ -88922,22 +92606,22 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide + if (!pAstRVAS->dp_base) + dev_err(&pdev->dev, "failed to iomem of display port\n"); + } -+ if (pAstRVAS->config->version == 7) { -+ pAstRVAS->FBInfo.dwDRAMSize = 0x40000000; // 1GB -+ // VGA size is fixed with 32MB -+ pAstRVAS->FBInfo.dwVGASize = 0x2000000; -+ } else { ++ ++ if (pAstRVAS->config->version == 7) ++ edac_node = of_find_compatible_node(NULL, NULL, "aspeed,ast2700-sdram-edac"); ++ else + edac_node = of_find_compatible_node(NULL, NULL, "aspeed,ast2600-sdram-edac"); -+ if (!edac_node) { -+ dev_err(&pdev->dev, "cannot find edac node\n"); -+ } else { -+ mcr_base = of_iomap(edac_node, 0); -+ if (!mcr_base) -+ dev_err(&pdev->dev, "failed to iomem of MCR\n"); -+ } + -+ set_FBInfo_size(pAstRVAS, mcr_base); ++ if (!edac_node) { ++ dev_err(&pdev->dev, "cannot find edac node\n"); ++ } else { ++ mcr_base = of_iomap(edac_node, 0); ++ if (!mcr_base) ++ dev_err(&pdev->dev, "failed to iomem of MCR\n"); + } ++ ++ set_FBInfo_size(pAstRVAS, mcr_base); ++ + //scu + if (pAstRVAS->config->version == 7) { + sdram_scu = syscon_regmap_lookup_by_compatible("aspeed,ast2700-scu0"); @@ -89033,7 +92717,7 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide + enable_video_interrupt(pAstRVAS); +} + -+static int video_drv_remove(struct platform_device *pdev) ++static void video_drv_remove(struct platform_device *pdev) +{ + struct AstRVAS *pAstRVAS = NULL; + @@ -89054,31 +92738,39 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide + + free_video_engine_memory(pAstRVAS); + pr_info("RVAS: driver successfully unloaded.\n"); -+ return 0; +} + -+static const u32 ast2400_dram_table[] = { ++static const u64 ast2400_dram_table[] = { + 0x04000000, //64MB + 0x08000000, //128MB + 0x10000000, //256MB + 0x20000000, //512MB +}; + -+static const u32 ast2500_dram_table[] = { ++static const u64 ast2500_dram_table[] = { + 0x08000000, //128MB + 0x10000000, //256MB + 0x20000000, //512MB + 0x40000000, //1024MB +}; + -+static const u32 ast2600_dram_table[] = { ++static const u64 ast2600_dram_table[] = { ++ 0x10000000, //256MB ++ 0x20000000, //512MB ++ 0x40000000, //1024MB ++ 0x80000000, //2048MB ++}; ++ ++static const u64 ast2700_dram_table[] = { + 0x10000000, //256MB + 0x20000000, //512MB + 0x40000000, //1024MB + 0x80000000, //2048MB ++ 0x100000000, //4GB ++ 0x200000000, //8GB +}; + -+static const u32 aspeed_vga_table[] = { ++static const u64 aspeed_vga_table[] = { + 0x800000, //8MB + 0x1000000, //16MB + 0x2000000, //32MB @@ -89092,16 +92784,23 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide + +static const struct aspeed_rvas_config ast2700_config = { + .version = 7, -+ .dram_table = ast2600_dram_table, ++ .dram_table = ast2700_dram_table, +}; + +static void set_FBInfo_size(struct AstRVAS *pAstRVAS, void __iomem *mcr_base) +{ -+ u32 reg_mcr004 = readl(mcr_base + MCR_CONF); ++ if (pAstRVAS->config->version == 7) { ++ u32 reg_mcr010 = readl(mcr_base + MCR_CONF_AST27XX); ++ u32 reg_mcr100 = readl(mcr_base + MCR_GRAPHIC_MEM_CONF_AST27XX); + -+ pAstRVAS->FBInfo.dwDRAMSize = pAstRVAS->config->dram_table[reg_mcr004 & 0x3]; ++ pAstRVAS->FBInfo.qwDRAMSize = pAstRVAS->config->dram_table[((reg_mcr010 & 0x1c) >> 2)]; ++ pAstRVAS->FBInfo.dwVGASize = (reg_mcr100 & 0x1) ? 0x4000000 : 0x2000000; ++ } else { ++ u32 reg_mcr004 = readl(mcr_base + MCR_CONF); + -+ pAstRVAS->FBInfo.dwVGASize = aspeed_vga_table[((reg_mcr004 & 0xC) >> 2)]; ++ pAstRVAS->FBInfo.qwDRAMSize = pAstRVAS->config->dram_table[reg_mcr004 & 0x3]; ++ pAstRVAS->FBInfo.dwVGASize = aspeed_vga_table[((reg_mcr004 & 0xC) >> 2)]; ++ } +} + +static const struct of_device_id ast_rvas_match[] = { @@ -89124,8 +92823,8 @@ diff --git a/drivers/soc/aspeed/rvas/video_main.c b/drivers/soc/aspeed/rvas/vide +MODULE_DESCRIPTION("RVAS video driver module for AST2600"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig ---- a/drivers/spi/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/spi/Kconfig 2026-04-08 18:03:35.072947026 +0000 +--- a/drivers/spi/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/spi/Kconfig 2026-07-17 08:44:41.989511562 +0000 @@ -129,6 +129,16 @@ controller (SPI) for the host firmware. The implementation only supports SPI NOR. @@ -89144,8 +92843,8 @@ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig tristate "Atmel SPI Controller" depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile ---- a/drivers/spi/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/spi/Makefile 2026-04-08 18:03:39.964857803 +0000 +--- a/drivers/spi/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/spi/Makefile 2026-07-17 08:44:47.224430873 +0000 @@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o @@ -89155,8 +92854,8 @@ diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c ---- a/drivers/spi/spi-aspeed-smc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/spi/spi-aspeed-smc.c 2026-04-08 18:03:48.249706435 +0000 +--- a/drivers/spi/spi-aspeed-smc.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/spi/spi-aspeed-smc.c 2026-07-17 08:44:55.229307491 +0000 @@ -7,10 +7,16 @@ */ @@ -90935,7 +94634,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c + goto end; + } + - ret = devm_spi_register_controller(dev, ctlr); + ret = spi_register_controller(ctlr); - if (ret) + if (ret) { dev_err(&pdev->dev, "spi_register_controller failed\n"); @@ -90946,9 +94645,9 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c return ret; } -@@ -783,6 +1753,13 @@ - { - struct aspeed_spi *aspi = platform_get_drvdata(pdev); +@@ -786,6 +1756,13 @@ + + spi_unregister_controller(ctlr); + if (aspi->op_buf) { + dma_free_coherent(aspi->dev, @@ -90960,7 +94659,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c aspeed_spi_enable(aspi, false); } -@@ -795,19 +1772,20 @@ +@@ -798,19 +1775,20 @@ * The address range is encoded with absolute addresses in the overall * mapping window. */ @@ -90985,7 +94684,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c } /* -@@ -817,18 +1795,18 @@ +@@ -820,18 +1798,18 @@ #define AST2600_SEG_ADDR_MASK 0x0ff00000 @@ -91008,7 +94707,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c /* segment is disabled */ if (!end_offset) -@@ -838,28 +1816,193 @@ +@@ -841,28 +1819,193 @@ } static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi, @@ -91207,7 +94906,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c memcpy_fromio(test_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0) { #if defined(VERBOSE_DEBUG) -@@ -872,7 +2015,10 @@ +@@ -875,7 +2018,10 @@ return true; } @@ -91219,7 +94918,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c /* * The timing register is shared by all devices. Only update for CE0. -@@ -902,7 +2048,7 @@ +@@ -905,7 +2051,7 @@ pass = aspeed_spi_check_reads(chip, golden_buf, test_buf); dev_dbg(aspi->dev, " * [%08x] %d HCLK delay, %dns DI delay : %s", @@ -91228,7 +94927,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c pass ? "PASS" : "FAIL"); if (pass) { pass_count++; -@@ -948,17 +2094,6 @@ +@@ -951,17 +2097,6 @@ return cnt >= 64; } @@ -91246,7 +94945,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) { struct aspeed_spi *aspi = chip->aspi; -@@ -969,10 +2104,17 @@ +@@ -972,10 +2107,17 @@ u8 *golden_buf = NULL; u8 *test_buf = NULL; int i, rc, best_div = -1; @@ -91264,7 +94963,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c /* * use the related low frequency to get check calibration data * and get golden data. -@@ -988,7 +2130,7 @@ +@@ -991,7 +2133,7 @@ memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); if (!aspeed_spi_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) { @@ -91273,7 +94972,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c goto no_calib; } -@@ -998,42 +2140,81 @@ +@@ -1001,42 +2143,81 @@ #endif /* Now we iterate the HCLK dividers until we find our breaking point */ @@ -91367,7 +95066,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c #define TIMING_REG_AST2600(chip) \ ((chip)->aspi->regs + (chip)->aspi->data->timing + \ (chip)->cs * 4) -@@ -1041,60 +2222,136 @@ +@@ -1044,60 +2225,136 @@ static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, const u8 *golden_buf, u8 *test_buf) { @@ -91534,7 +95233,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c .max_cs = 5, .hastype = true, .we0 = 16, -@@ -1102,13 +2359,17 @@ +@@ -1105,13 +2362,17 @@ .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xfffff0ff, .hdiv_max = 1, @@ -91552,7 +95251,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c .max_cs = 1, .hastype = false, .we0 = 0, -@@ -1116,11 +2377,13 @@ +@@ -1119,11 +2380,13 @@ .timing = 0x14, .hclk_mask = 0xfffff0ff, .hdiv_max = 1, @@ -91566,7 +95265,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c .max_cs = 3, .hastype = true, .we0 = 16, -@@ -1128,13 +2391,17 @@ +@@ -1131,13 +2394,17 @@ .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xffffd0ff, .hdiv_max = 1, @@ -91584,7 +95283,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c .max_cs = 2, .hastype = false, .we0 = 16, -@@ -1142,13 +2409,17 @@ +@@ -1145,13 +2412,17 @@ .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xffffd0ff, .hdiv_max = 1, @@ -91602,7 +95301,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c .max_cs = 3, .hastype = false, .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD, -@@ -1157,13 +2428,17 @@ +@@ -1160,13 +2431,17 @@ .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xf0fff0ff, .hdiv_max = 2, @@ -91620,7 +95319,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c .max_cs = 2, .hastype = false, .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD, -@@ -1172,10 +2447,53 @@ +@@ -1175,10 +2450,53 @@ .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xf0fff0ff, .hdiv_max = 2, @@ -91674,7 +95373,7 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c }; static const struct of_device_id aspeed_spi_matches[] = { -@@ -1185,6 +2503,8 @@ +@@ -1188,6 +2506,8 @@ { .compatible = "aspeed,ast2500-spi", .data = &ast2500_spi_data }, { .compatible = "aspeed,ast2600-fmc", .data = &ast2600_fmc_data }, { .compatible = "aspeed,ast2600-spi", .data = &ast2600_spi_data }, @@ -91685,8 +95384,8 @@ diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c MODULE_DEVICE_TABLE(of, aspeed_spi_matches); diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c --- a/drivers/spi/spi-aspeed-txrx.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/spi/spi-aspeed-txrx.c 2026-04-08 18:03:48.243706544 +0000 -@@ -0,0 +1,622 @@ ++++ b/drivers/spi/spi-aspeed-txrx.c 2026-07-17 08:44:55.223307583 +0000 +@@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) ASPEED Technology Inc. @@ -91739,8 +95438,9 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + u32 ahb_clk; + u32 ctrl_val[5]; + void __iomem *chip_ahb_base[5]; -+ u8 cs_change; ++ struct spi_device *cs_change_spi; + const struct aspeed_spi_info *info; ++ u32 saved_host_cmd_ctrl; +}; + +struct aspeed_spi_info { @@ -92027,6 +95727,11 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + writel(ctrl_val, ctrl_reg); +} + ++static void aspeed_spi_restore_host_cmd_ctrl(struct aspeed_spi_host *host) ++{ ++ writel(host->saved_host_cmd_ctrl, host->ctrl_reg + SPI_MISC_CTRL); ++} ++ +static void aspeed_spi_transfer_tx(struct aspeed_spi_host *host, const u8 *tx_buf, + u8 *rx_buf, void *dst, u32 len) +{ @@ -92040,6 +95745,18 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + } +} + ++/* ++ * aspeed_spi_transfer - transfer_one_message callback ++ * ++ * SPI_MISC_CTRL holds the host command configuration and must be cleared ++ * before entering user mode; it is saved here and restored only when CS ++ * is finally deasserted. ++ * ++ * cs_change on the last transfer keeps CS active across messages ++ * (cs_change_spi tracks the owning device). cs_change on any earlier ++ * transfer toggles CS in place. A stale CS held by another device is ++ * force-released before the new message begins. ++ */ +static int aspeed_spi_transfer(struct spi_controller *ctlr, + struct spi_message *msg) +{ @@ -92052,22 +95769,38 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + u8 *rx_buf; + u32 cs; + u32 j = 0; -+ u32 ctrl_val, normal_mode; ++ u32 ctrl_val; + void __iomem *ctrl_reg; ++ bool is_last; ++ bool keep_cs = false; ++ ++ /* Drop a CS held by another device before starting this message. */ ++ if (host->cs_change_spi && host->cs_change_spi != spi) { ++ dev_warn(dev, "CS was kept active for another SPI device, releasing it\n"); ++ ++ aspeed_spi_stop_user(host->cs_change_spi); ++ aspeed_spi_restore_host_cmd_ctrl(host); + -+ if (host->cs_change == 0) ++ host->cs_change_spi = NULL; ++ } ++ ++ /* Backup host command mode information from MISC_CTRL register before activating CS */ ++ if (!host->cs_change_spi) { ++ host->saved_host_cmd_ctrl = readl(host->ctrl_reg + SPI_MISC_CTRL); ++ ++ writel(0x0, host->ctrl_reg + SPI_MISC_CTRL); + aspeed_spi_start_user(spi); ++ } + + cs = spi_get_chipselect(spi, 0); + ctrl_reg = host->ctrl_reg + SPI_CE0_CTRL + cs * 4; + ctrl_val = readl(ctrl_reg); + -+ normal_mode = readl(host->ctrl_reg + SPI_MISC_CTRL); -+ writel(0x0, host->ctrl_reg + SPI_MISC_CTRL); -+ + dev_dbg(dev, "cs: %d\n", cs); + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ is_last = list_is_last(&xfer->transfer_list, &msg->transfers); ++ + dev_dbg(dev, + "xfer[%d]: width %d, len %u, tx %p, rx %p\n", + j, @@ -92079,9 +95812,9 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + + if (tx_buf) { + ctrl_val &= ~SPI_IO_MASK; -+ if (spi->mode & SPI_TX_DUAL) ++ if (xfer->tx_nbits == SPI_NBITS_DUAL) + ctrl_val |= SPI_DUAL_IO_MODE; -+ else if (spi->mode & SPI_TX_QUAD) ++ else if (xfer->tx_nbits == SPI_NBITS_QUAD) + ctrl_val |= SPI_QUAD_IO_MODE; + writel(ctrl_val, ctrl_reg); + @@ -92097,9 +95830,9 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + + if (rx_buf && rx_buf != tx_buf) { + ctrl_val &= ~SPI_IO_MASK; -+ if (spi->mode & SPI_RX_DUAL) ++ if (xfer->rx_nbits == SPI_NBITS_DUAL) + ctrl_val |= SPI_DUAL_IO_MODE; -+ else if (spi->mode & SPI_RX_QUAD) ++ else if (xfer->rx_nbits == SPI_NBITS_QUAD) + ctrl_val |= SPI_QUAD_IO_MODE; + writel(ctrl_val, ctrl_reg); + @@ -92112,16 +95845,32 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c + } + + msg->actual_length += xfer->len; -+ host->cs_change = xfer->cs_change; ++ ++ /* Non-final cs_change toggles CS; final cs_change keeps it active. */ ++ if (xfer->cs_change) { ++ if (is_last) { ++ keep_cs = true; ++ } else { ++ aspeed_spi_stop_user(spi); ++ /* CS deassert-to-reassert delay; uses xfer->cs_change_delay if set, else 10us */ ++ spi_transfer_cs_change_delay_exec(msg, xfer); ++ aspeed_spi_start_user(spi); ++ } ++ } ++ + j++; + } + -+ if (host->cs_change == 0) ++ if (!keep_cs) { + aspeed_spi_stop_user(spi); ++ aspeed_spi_restore_host_cmd_ctrl(host); + -+ msg->status = 0; ++ host->cs_change_spi = NULL; ++ } else { ++ host->cs_change_spi = spi; ++ } + -+ writel(normal_mode, host->ctrl_reg + SPI_MISC_CTRL); ++ msg->status = 0; + + spi_finalize_current_message(ctlr); + @@ -92308,10 +96057,9 @@ diff --git a/drivers/spi/spi-aspeed-txrx.c b/drivers/spi/spi-aspeed-txrx.c +MODULE_AUTHOR("Ryan Chen"); +MODULE_AUTHOR("Chin-Ting Kuo"); +MODULE_LICENSE("GPL"); -+ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/8250_aspeed.c --- a/drivers/tty/serial/8250/8250_aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/tty/serial/8250/8250_aspeed.c 2026-04-08 18:03:48.129708627 +0000 ++++ b/drivers/tty/serial/8250/8250_aspeed.c 2026-07-17 08:44:55.106309387 +0000 @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 +/* @@ -92859,8 +96607,8 @@ diff --git a/drivers/tty/serial/8250/8250_aspeed.c b/drivers/tty/serial/8250/825 +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Aspeed UART Driver"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig ---- a/drivers/tty/serial/8250/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/tty/serial/8250/Kconfig 2026-04-08 18:03:34.152963806 +0000 +--- a/drivers/tty/serial/8250/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/tty/serial/8250/Kconfig 2026-07-17 08:44:41.131524786 +0000 @@ -256,6 +256,15 @@ To compile this driver as a module, choose M here: the module will be called 8250_accent. @@ -92878,8 +96626,8 @@ diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig tristate "Aspeed Virtual UART" depends on SERIAL_8250 diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile ---- a/drivers/tty/serial/8250/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/tty/serial/8250/Makefile 2026-04-08 18:03:38.836878376 +0000 +--- a/drivers/tty/serial/8250/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/tty/serial/8250/Makefile 2026-07-17 08:44:46.092448321 +0000 @@ -21,6 +21,7 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o @@ -92889,9 +96637,9 @@ diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig ---- a/drivers/ufs/host/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/ufs/host/Kconfig 2026-04-08 18:03:34.751952881 +0000 -@@ -142,3 +142,12 @@ +--- a/drivers/ufs/host/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/ufs/host/Kconfig 2026-07-17 08:44:41.686516232 +0000 +@@ -143,3 +143,12 @@ Select this if you have UFS controller on Unisoc chipset. If unsure, say N. @@ -92905,8 +96653,8 @@ diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig + Selects this if you have Aspeed platform with UFS controller. + If unsure, say N. diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile ---- a/drivers/ufs/host/Makefile 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/ufs/host/Makefile 2026-04-08 18:03:39.631863876 +0000 +--- a/drivers/ufs/host/Makefile 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/ufs/host/Makefile 2026-07-17 08:44:46.885436099 +0000 @@ -12,3 +12,4 @@ obj-$(CONFIG_SCSI_UFS_RENESAS) += ufs-renesas.o obj-$(CONFIG_SCSI_UFS_SPRD) += ufs-sprd.o @@ -92914,7 +96662,7 @@ diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile +obj-$(CONFIG_SCSI_UFS_ASPEED) += ufs-aspeed.o diff --git a/drivers/ufs/host/ufs-aspeed.c b/drivers/ufs/host/ufs-aspeed.c --- a/drivers/ufs/host/ufs-aspeed.c 1970-01-01 00:00:00.000000000 +0000 -+++ b/drivers/ufs/host/ufs-aspeed.c 2026-04-08 18:03:48.204707257 +0000 ++++ b/drivers/ufs/host/ufs-aspeed.c 2026-07-17 08:44:55.182308215 +0000 @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2019 ASPEED Technology Inc. */ @@ -93342,8 +97090,8 @@ diff --git a/drivers/ufs/host/ufs-aspeed.c b/drivers/ufs/host/ufs-aspeed.c +MODULE_DESCRIPTION("Aspeed UFS host controller platform driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig ---- a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig 2026-04-08 18:03:48.336704845 +0000 +--- a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/Kconfig 2026-07-17 08:44:55.317306134 +0000 @@ -4,5 +4,6 @@ depends on ARCH_ASPEED || COMPILE_TEST depends on USB_LIBCOMPOSITE @@ -93354,37 +97102,17 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/Kconfig b/drivers/usb/gadget/udc + and AST2700 family SoCs supporting the "vHub" functionality and + USB2.0 diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c ---- a/drivers/usb/gadget/udc/aspeed-vhub/core.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c 2026-04-08 18:03:48.336704845 +0000 -@@ -23,9 +23,27 @@ +--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c 2026-07-17 08:44:55.317306134 +0000 +@@ -23,6 +23,7 @@ #include #include #include +#include -+#include #include "vhub.h" -+#define ASPEED_G7_SCU_VHUB_USB_FUNC_OFFSET 0x410 -+ -+enum ast_g7_pcie { -+ NOT_SUPPORTED, -+ PCIE_EHCI, -+ PCIE_XHCI, -+}; -+ -+struct ast_vhub_match_data { -+ enum ast_g7_pcie g7_pcie; -+ u32 usb_mode_mask; -+ u32 xhci_mode_mask; -+ u32 txfifo_fix_reg; -+ u32 txfifo_fix_val; -+}; -+ - void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req, - int status) - { -@@ -239,6 +257,11 @@ +@@ -239,9 +240,24 @@ if (vhub->force_usb1) ctrl |= VHUB_CTRL_FULL_SPEED_ONLY; @@ -93396,70 +97124,29 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ ctrl |= VHUB_CTRL_UPSTREAM_CONNECT; writel(ctrl, vhub->regs + AST_VHUB_CTRL); -@@ -253,6 +276,52 @@ - vhub->regs + AST_VHUB_IER); - } - -+static int ast_vhub_init_pcie(struct ast_vhub *vhub, const struct ast_vhub_match_data *pdata) -+{ -+ struct device *dev = &vhub->pdev->dev; -+ struct regmap *pcie_device; -+ struct regmap *scu; -+ u32 scu_usb; -+ int rc = 0; -+ -+ scu = syscon_regmap_lookup_by_phandle(dev->of_node, "aspeed,scu"); -+ if (IS_ERR(scu)) { -+ dev_err(dev, "failed to find SCU regmap\n"); -+ return PTR_ERR(scu); -+ } ++ /* AST2700 SoC0 vhub0 only: force retry via CRC error injection ++ * when DRAM access is delayed due to bus contention. ++ */ ++ if (vhub->txfifo_retry_quirk) { ++ u32 val = readl(vhub->regs + AST_VHUB_PHY_00); + -+ regmap_read(scu, ASPEED_G7_SCU_VHUB_USB_FUNC_OFFSET, &scu_usb); -+ -+ /* Check EHCI or xHCI to virtual hub */ -+ if ((scu_usb & pdata->usb_mode_mask) == 0) { -+ pcie_device = syscon_regmap_lookup_by_phandle(dev->of_node, -+ "aspeed,device"); -+ if (IS_ERR(pcie_device)) { -+ dev_err(dev, "failed to find PCIe device regmap\n"); -+ return PTR_ERR(pcie_device); -+ } -+ if (pdata->g7_pcie == PCIE_XHCI) { -+ /* Check PCIe xHCI or BMC xHCI to virtual hub */ -+ if ((scu_usb & pdata->xhci_mode_mask) == 0) { -+ dev_info(dev, "PCIe xHCI to vhub\n"); -+ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev -+ /* Turn on PCIe xHCI without MSI */ -+ regmap_update_bits(pcie_device, 0x70, -+ BIT(19) | BIT(11) | BIT(3), -+ BIT(19) | BIT(11) | BIT(3)); -+ } -+ } else if (pdata->g7_pcie == PCIE_EHCI) { -+ dev_info(dev, "PCIe EHCI to vhub\n"); -+ //EnPCIaMSI_EnPCIaIntA_EnPCIaMst_EnPCIaDev -+ /* Turn on PCIe EHCI without MSI */ -+ regmap_update_bits(pcie_device, 0x70, -+ BIT(18) | BIT(10) | BIT(2), -+ BIT(18) | BIT(10) | BIT(2)); -+ } ++ val |= AST_VHUB0_FIFO_FORCE_RETRY; ++ writel(val, vhub->regs + AST_VHUB_PHY_00); + } -+ return rc; -+} + - static void ast_vhub_remove(struct platform_device *pdev) - { - struct ast_vhub *vhub = platform_get_drvdata(pdev); -@@ -280,6 +349,9 @@ + /* Enable some interrupts */ + writel(VHUB_IRQ_HUB_EP0_IN_ACK_STALL | + VHUB_IRQ_HUB_EP0_OUT_ACK_STALL | +@@ -280,6 +296,8 @@ if (vhub->clk) clk_disable_unprepare(vhub->clk); -+ if (vhub->rst) -+ reset_control_assert(vhub->rst); ++ reset_control_assert(vhub->rst); + spin_unlock_irqrestore(&vhub->lock, flags); if (vhub->ep0_bufs) -@@ -291,6 +363,61 @@ +@@ -291,9 +309,65 @@ vhub->ep0_bufs = NULL; } @@ -93521,53 +97208,31 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ static int ast_vhub_probe(struct platform_device *pdev) { enum usb_device_speed max_speed; -@@ -298,11 +425,19 @@ ++ const u64 *dma_mask_ptr; + struct ast_vhub *vhub; struct resource *res; int i, rc = 0; - const struct device_node *np = pdev->dev.of_node; -+ const struct ast_vhub_match_data *pdata; -+ u32 val; - - vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL); - if (!vhub) - return -ENOMEM; - -+ pdata = of_device_get_match_data(&pdev->dev); -+ if (IS_ERR(pdata)) { -+ dev_err(&pdev->dev, "Couldn't get match data\n"); -+ return -ENODEV; -+ } -+ - rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports", - &vhub->max_ports); - if (rc < 0) -@@ -323,6 +458,8 @@ +@@ -323,6 +397,10 @@ if (!vhub->epns) return -ENOMEM; + vhub->enlarge_fifo = of_property_read_bool(np, "aspeed,enlarge-fifo"); ++ vhub->txfifo_retry_quirk = ++ of_property_read_bool(np, "aspeed,txfifo-retry-quirk"); + spin_lock_init(&vhub->lock); vhub->pdev = pdev; vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1, -@@ -337,6 +474,13 @@ - - platform_set_drvdata(pdev, vhub); +@@ -348,6 +426,21 @@ + goto err; + } + vhub->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); -+ + if (IS_ERR(vhub->rst)) { + rc = PTR_ERR(vhub->rst); + goto err; + } + - vhub->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(vhub->clk)) { - rc = PTR_ERR(vhub->clk); -@@ -348,6 +492,28 @@ - goto err; - } - + if (vhub->rst) { + mdelay(10); + rc = reset_control_deassert(vhub->rst); @@ -93575,127 +97240,54 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/ + goto err; + } + -+ if (pdata->g7_pcie != NOT_SUPPORTED) { -+ rc = ast_vhub_init_pcie(vhub, pdata); -+ if (rc) -+ goto err; -+ -+ /* For G7 PortA/B, enable the option of TXFIFO fix. -+ * It forces the CRC error for a re-try when vHub cannot fetch DRAM in time. -+ */ -+ val = readl(vhub->regs + pdata->txfifo_fix_reg); -+ writel(pdata->txfifo_fix_val | val, -+ vhub->regs + pdata->txfifo_fix_reg); -+ } -+ + ast_vhub_init_uart(&pdev->dev, vhub); + /* Check if we need to limit the HW to USB1 */ max_speed = usb_get_maximum_speed(&pdev->dev); if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH) -@@ -370,6 +536,12 @@ +@@ -370,6 +463,13 @@ goto err; } -+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); -+ if (rc) { -+ dev_warn(&pdev->dev, "No suitable DMA available\n"); -+ goto err; ++ dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev); ++ if (dma_mask_ptr) { ++ rc = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr); ++ if (rc) ++ goto err; + } + /* * Allocate DMA buffers for all EP0s in one chunk, * one per port and one for the vHub itself -@@ -412,15 +584,86 @@ +@@ -412,15 +512,25 @@ return rc; } -+static const struct ast_vhub_match_data aspeed_vhub_match_data = { -+ .g7_pcie = NOT_SUPPORTED, -+}; -+ -+static const struct ast_vhub_match_data aspeed_g7_vhuba0_match_data = { -+ .g7_pcie = PCIE_EHCI, -+ .usb_mode_mask = GENMASK(25, 24), -+ .xhci_mode_mask = 0, -+ .txfifo_fix_reg = 0x800, -+ .txfifo_fix_val = BIT(13), -+}; -+ -+static const struct ast_vhub_match_data aspeed_g7_vhubb0_match_data = { -+ .g7_pcie = PCIE_EHCI, -+ .usb_mode_mask = GENMASK(29, 28), -+ .xhci_mode_mask = 0, -+ .txfifo_fix_reg = 0x800, -+ .txfifo_fix_val = BIT(13), -+}; -+ -+static const struct ast_vhub_match_data aspeed_g7_vhuba1_match_data = { -+ .g7_pcie = PCIE_XHCI, -+ .usb_mode_mask = GENMASK(3, 2), -+ .xhci_mode_mask = BIT_MASK(9), -+ .txfifo_fix_reg = 0x80C, -+ .txfifo_fix_val = BIT(31), -+}; -+ -+static const struct ast_vhub_match_data aspeed_g7_vhubb1_match_data = { -+ .g7_pcie = PCIE_XHCI, -+ .usb_mode_mask = GENMASK(7, 6), -+ .xhci_mode_mask = BIT_MASK(10), -+ .txfifo_fix_reg = 0x80C, -+ .txfifo_fix_val = BIT(31), -+}; -+ -+static const struct ast_vhub_match_data aspeed_g7_vhubc_match_data = { -+ .g7_pcie = NOT_SUPPORTED, -+}; -+ -+static const struct ast_vhub_match_data aspeed_g7_vhubd_match_data = { -+ .g7_pcie = NOT_SUPPORTED, -+}; ++static const u64 dma_mask_32 = DMA_BIT_MASK(32); ++static const u64 dma_mask_64 = DMA_BIT_MASK(64); + static const struct of_device_id ast_vhub_dt_ids[] = { { .compatible = "aspeed,ast2400-usb-vhub", -+ .data = &aspeed_vhub_match_data, ++ .data = &dma_mask_32, }, { .compatible = "aspeed,ast2500-usb-vhub", -+ .data = &aspeed_vhub_match_data, ++ .data = &dma_mask_32, }, { .compatible = "aspeed,ast2600-usb-vhub", -+ .data = &aspeed_vhub_match_data, -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb-vhuba0", -+ .data = &aspeed_g7_vhuba0_match_data, -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb-vhubb0", -+ .data = &aspeed_g7_vhubb0_match_data, -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb-vhuba1", -+ .data = &aspeed_g7_vhuba1_match_data, -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb-vhubb1", -+ .data = &aspeed_g7_vhubb1_match_data, -+ }, -+ { -+ .compatible = "aspeed,ast2700-usb-vhubc", -+ .data = &aspeed_g7_vhubc_match_data, ++ .data = &dma_mask_32, + }, + { -+ .compatible = "aspeed,ast2700-usb-vhubd", -+ .data = &aspeed_g7_vhubd_match_data, ++ .compatible = "aspeed,ast2700-usb-vhub", ++ .data = &dma_mask_64, }, { } }; diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c ---- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c 2026-04-08 18:03:48.336704845 +0000 +--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c 2026-07-17 08:44:55.317306134 +0000 @@ -116,10 +116,14 @@ if (wValue == USB_DEVICE_REMOTE_WAKEUP) { @@ -93723,8 +97315,8 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/a } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c ---- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c 2026-04-08 18:03:48.336704845 +0000 +--- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c 2026-07-17 08:44:55.317306134 +0000 @@ -340,7 +340,9 @@ struct ast_vhub *vhub = ep->vhub; unsigned long flags; @@ -93826,8 +97418,8 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/a struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c ---- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c 2026-04-08 18:03:48.336704845 +0000 +--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c 2026-07-17 08:44:55.317306134 +0000 @@ -221,9 +221,8 @@ EPDBG(ep, "Hub remote wakeup %s\n", is_set ? "enabled" : "disabled"); @@ -93873,9 +97465,16 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/a return ast_vhub_simple_reply(ep, diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h ---- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h 2026-04-08 18:03:48.336704845 +0000 -@@ -35,6 +35,7 @@ +--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h 2026-07-17 08:44:55.317306134 +0000 +@@ -29,12 +29,14 @@ + #define AST_VHUB_EP1_STS_CHG 0x3C /* Endpoint 1 Status Change Bitmap Data */ + #define AST_VHUB_SETUP0 0x80 /* Root Device Setup Data Buffer0 */ + #define AST_VHUB_SETUP1 0x84 /* Root Device Setup Data Buffer1 */ ++#define AST_VHUB_PHY_00 0x800 /* USB PHY Control/Status #1 */ + + /* Main control reg */ + #define VHUB_CTRL_PHY_CLK (1 << 31) #define VHUB_CTRL_PHY_LOOP_TEST (1 << 25) #define VHUB_CTRL_DN_PWN (1 << 24) #define VHUB_CTRL_DP_PWN (1 << 23) @@ -93883,7 +97482,21 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ #define VHUB_CTRL_LONG_DESC (1 << 18) #define VHUB_CTRL_ISO_RSP_CTRL (1 << 17) #define VHUB_CTRL_SPLIT_IN (1 << 16) -@@ -199,6 +200,14 @@ +@@ -184,6 +186,13 @@ + #define VHUB_EP_DMA_SET_CPU_WPTR(x) (x) + #define VHUB_EP_DMA_SINGLE_KICK (1 << 0) /* WPTR = 1 for single mode */ + ++/******************************************* ++ * * ++ * PHY Control/Status register definitions * ++ * * ++ *******************************************/ ++#define AST_VHUB0_FIFO_FORCE_RETRY BIT(13) ++ + /******************************* + * * + * DMA descriptors definitions * +@@ -199,6 +208,14 @@ #define VHUB_DSC1_IN_SET_LEN(x) ((x) & 0xfff) #define VHUB_DSC1_IN_LEN(x) ((x) & 0xfff) @@ -93898,7 +97511,7 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ /**************************************** * * * Data structures and misc definitions * -@@ -218,6 +227,8 @@ +@@ -218,6 +235,8 @@ * values are 256 and 32) */ @@ -93907,21 +97520,23 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ struct ast_vhub; struct ast_vhub_dev; -@@ -388,6 +399,8 @@ +@@ -388,6 +407,7 @@ spinlock_t lock; struct work_struct wake_work; struct clk *clk; + struct reset_control *rst; -+ /* EP0 DMA buffers allocated in one chunk */ void *ep0_bufs; -@@ -417,8 +430,12 @@ +@@ -417,8 +437,15 @@ /* Force full speed only */ bool force_usb1 : 1; + /* Enlarge FIFO for ast2700 soc0 vhub1 */ + bool enlarge_fifo : 1; ++ ++ /* Force TXFIFO retry when is DRAM busy for ast2700 soc0 vhub0 */ ++ bool txfifo_retry_quirk : 1; + /* Upstream bus speed captured at bus reset */ unsigned int speed; @@ -93930,8 +97545,8 @@ diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/ /* Standard USB Descriptors of the vhub. */ struct usb_device_descriptor vhub_dev_desc; diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c ---- a/drivers/usb/gadget/udc/aspeed_udc.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/usb/gadget/udc/aspeed_udc.c 2026-04-08 18:03:48.296705576 +0000 +--- a/drivers/usb/gadget/udc/aspeed_udc.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/usb/gadget/udc/aspeed_udc.c 2026-07-17 08:44:55.275306782 +0000 @@ -156,7 +156,7 @@ #define AST_EP_DMA_DESC_PID_DATA1 (2 << 14) #define AST_EP_DMA_DESC_PID_MDATA (3 << 14) @@ -94123,8 +97738,8 @@ diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed for (i = 0; i < AST_UDC_NUM_ENDPOINTS; i++) { ep = &udc->ep[i]; diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c ---- a/drivers/watchdog/aspeed_wdt.c 2025-08-01 08:48:47.000000000 +0000 -+++ b/drivers/watchdog/aspeed_wdt.c 2026-04-08 18:03:48.256706307 +0000 +--- a/drivers/watchdog/aspeed_wdt.c 2026-06-19 11:42:39.000000000 +0000 ++++ b/drivers/watchdog/aspeed_wdt.c 2026-07-17 08:44:55.236307383 +0000 @@ -35,6 +35,8 @@ u32 irq_shift; u32 irq_mask; @@ -94357,7 +97972,7 @@ diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c aspeed_wdt_update_bootstatus(pdev, wdt); diff --git a/include/dt-bindings/clock/aspeed,ast1700-clk.h b/include/dt-bindings/clock/aspeed,ast1700-clk.h --- a/include/dt-bindings/clock/aspeed,ast1700-clk.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed,ast1700-clk.h 2026-04-08 18:03:48.454702689 +0000 ++++ b/include/dt-bindings/clock/aspeed,ast1700-clk.h 2026-07-17 08:44:55.433304346 +0000 @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -94458,7 +98073,7 @@ diff --git a/include/dt-bindings/clock/aspeed,ast1700-clk.h b/include/dt-binding +#endif diff --git a/include/dt-bindings/clock/aspeed,ast1800-clk.h b/include/dt-bindings/clock/aspeed,ast1800-clk.h --- a/include/dt-bindings/clock/aspeed,ast1800-clk.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed,ast1800-clk.h 2026-04-08 18:03:48.463702525 +0000 ++++ b/include/dt-bindings/clock/aspeed,ast1800-clk.h 2026-07-17 08:44:55.443304192 +0000 @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -94566,8 +98181,8 @@ diff --git a/include/dt-bindings/clock/aspeed,ast1800-clk.h b/include/dt-binding +#endif diff --git a/include/dt-bindings/clock/aspeed,ast2700-scu.h b/include/dt-bindings/clock/aspeed,ast2700-scu.h --- a/include/dt-bindings/clock/aspeed,ast2700-scu.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed,ast2700-scu.h 2026-04-08 18:03:48.468702433 +0000 -@@ -0,0 +1,167 @@ ++++ b/include/dt-bindings/clock/aspeed,ast2700-scu.h 2026-07-17 08:44:55.448304115 +0000 +@@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Device Tree binding constants for AST2700 clock controller. @@ -94733,11 +98348,13 @@ diff --git a/include/dt-bindings/clock/aspeed,ast2700-scu.h b/include/dt-binding +#define SCU1_CLK_GATE_PORTDUSB2CLK 85 +#define SCU1_CLK_GATE_LTPI1TXCLK 86 +#define SCU1_CLK_I3C 87 ++#define SCU1_CLK_HPLL_DIV4 88 ++#define SCU1_CLK_PECI 89 + +#endif diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h ---- a/include/dt-bindings/clock/aspeed-clock.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/clock/aspeed-clock.h 2026-04-08 18:03:48.459702598 +0000 +--- a/include/dt-bindings/clock/aspeed-clock.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/dt-bindings/clock/aspeed-clock.h 2026-07-17 08:44:55.438304269 +0000 @@ -53,5 +53,6 @@ #define ASPEED_RESET_AHB 8 #define ASPEED_RESET_CRT1 9 @@ -94746,8 +98363,8 @@ diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/cloc #endif diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h ---- a/include/dt-bindings/clock/ast2600-clock.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/clock/ast2600-clock.h 2026-04-08 18:03:48.473702342 +0000 +--- a/include/dt-bindings/clock/ast2600-clock.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/dt-bindings/clock/ast2600-clock.h 2026-07-17 08:44:55.453304038 +0000 @@ -72,7 +72,7 @@ #define ASPEED_CLK_D1CLK 55 #define ASPEED_CLK_VCLK 56 @@ -94793,7 +98410,7 @@ diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clo #define ASPEED_RESET_AHB 1 diff --git a/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h b/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h --- a/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h 2026-04-08 18:03:48.478702251 +0000 ++++ b/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h 2026-07-17 08:44:55.457303977 +0000 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -94817,8 +98434,8 @@ diff --git a/include/dt-bindings/interrupt-controller/aspeed-e2m-ic.h b/include/ + +#endif /* _DT_BINDINGS_INTERRUPT_CONTROLLER_ASPEED_E2M_IC_H_ */ diff --git a/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h b/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h ---- a/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2026-04-08 18:03:48.483702159 +0000 +--- a/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h 2026-07-17 08:44:55.462303900 +0000 @@ -20,4 +20,18 @@ #define ASPEED_AST2600_SCU_IC1_LPC_RESET_LO_TO_HI 0 #define ASPEED_AST2600_SCU_IC1_LPC_RESET_HI_TO_LO 1 @@ -94840,7 +98457,7 @@ diff --git a/include/dt-bindings/interrupt-controller/aspeed-scu-ic.h b/include/ #endif /* _DT_BINDINGS_INTERRUPT_CONTROLLER_ASPEED_SCU_IC_H_ */ diff --git a/include/dt-bindings/reset/aspeed,ast1700-reset.h b/include/dt-bindings/reset/aspeed,ast1700-reset.h --- a/include/dt-bindings/reset/aspeed,ast1700-reset.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/reset/aspeed,ast1700-reset.h 2026-04-08 18:03:48.497701904 +0000 ++++ b/include/dt-bindings/reset/aspeed,ast1700-reset.h 2026-07-17 08:44:55.477303668 +0000 @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -94913,7 +98530,7 @@ diff --git a/include/dt-bindings/reset/aspeed,ast1700-reset.h b/include/dt-bindi +#endif /* _MACH_ASPEED_AST1700_RESET_H_ */ diff --git a/include/dt-bindings/reset/aspeed,ast1800-reset.h b/include/dt-bindings/reset/aspeed,ast1800-reset.h --- a/include/dt-bindings/reset/aspeed,ast1800-reset.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/reset/aspeed,ast1800-reset.h 2026-04-08 18:03:48.507701721 +0000 ++++ b/include/dt-bindings/reset/aspeed,ast1800-reset.h 2026-07-17 08:44:55.486303530 +0000 @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -94978,7 +98595,7 @@ diff --git a/include/dt-bindings/reset/aspeed,ast1800-reset.h b/include/dt-bindi +#endif /* _MACH_ASPEED_AST1800_RESET_H_ */ diff --git a/include/dt-bindings/reset/aspeed,ast2700-scu.h b/include/dt-bindings/reset/aspeed,ast2700-scu.h --- a/include/dt-bindings/reset/aspeed,ast2700-scu.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/dt-bindings/reset/aspeed,ast2700-scu.h 2026-04-08 18:03:48.502701812 +0000 ++++ b/include/dt-bindings/reset/aspeed,ast2700-scu.h 2026-07-17 08:44:55.482303591 +0000 @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* @@ -95106,8 +98723,8 @@ diff --git a/include/dt-bindings/reset/aspeed,ast2700-scu.h b/include/dt-binding + +#endif /* _MACH_ASPEED_AST2700_RESET_H_ */ diff --git a/include/dt-bindings/watchdog/aspeed-wdt.h b/include/dt-bindings/watchdog/aspeed-wdt.h ---- a/include/dt-bindings/watchdog/aspeed-wdt.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/dt-bindings/watchdog/aspeed-wdt.h 2026-04-08 18:03:48.493701977 +0000 +--- a/include/dt-bindings/watchdog/aspeed-wdt.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/dt-bindings/watchdog/aspeed-wdt.h 2026-07-17 08:44:55.472303745 +0000 @@ -89,4 +89,142 @@ #define AST2600_WDT_RESET2_DEFAULT 0x03fffff1 @@ -95253,7 +98870,7 @@ diff --git a/include/dt-bindings/watchdog/aspeed-wdt.h b/include/dt-bindings/wat #endif diff --git a/include/linux/aspeed-mctp.h b/include/linux/aspeed-mctp.h --- a/include/linux/aspeed-mctp.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/aspeed-mctp.h 2026-04-08 18:03:48.444702872 +0000 ++++ b/include/linux/aspeed-mctp.h 2026-07-17 08:44:55.423304501 +0000 @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2025 ASPEED Tech */ @@ -95413,8 +99030,8 @@ diff --git a/include/linux/aspeed-mctp.h b/include/linux/aspeed-mctp.h + +#endif /* __LINUX_ASPEED_MCTP_H */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h ---- a/include/linux/clk-provider.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/linux/clk-provider.h 2026-04-08 18:03:49.279687616 +0000 +--- a/include/linux/clk-provider.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/linux/clk-provider.h 2026-07-17 08:44:56.357290105 +0000 @@ -623,6 +623,24 @@ NULL, (flags), (reg), (bit_idx), \ (clk_gate_flags), (lock)) @@ -95442,8 +99059,8 @@ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h * @dev: device that is registering this clock diff --git a/include/linux/mctp-pcie-vdm.h b/include/linux/mctp-pcie-vdm.h --- a/include/linux/mctp-pcie-vdm.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/mctp-pcie-vdm.h 2026-04-08 18:03:49.251688128 +0000 -@@ -0,0 +1,36 @@ ++++ b/include/linux/mctp-pcie-vdm.h 2026-07-17 08:44:56.330290521 +0000 +@@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mctp-pcie-vdm.h - MCTP-over-PCIe-VDM (DMTF DSP0238) transport binding Interface @@ -95455,9 +99072,10 @@ diff --git a/include/linux/mctp-pcie-vdm.h b/include/linux/mctp-pcie-vdm.h +#define __LINUX_MCTP_PCIE_VDM_H + +#include ++#include +#include + -+#ifdef CONFIG_MCTP_TRANSPORT_PCIE_VDM ++#if IS_ENABLED(CONFIG_MCTP_TRANSPORT_PCIE_VDM) + +/** + * @send_packet: referenced to send packets with PCIe VDM header packed. @@ -95474,15 +99092,17 @@ diff --git a/include/linux/mctp-pcie-vdm.h b/include/linux/mctp-pcie-vdm.h +}; + +struct net_device *mctp_pcie_vdm_add_dev(struct device *dev, -+ const struct mctp_pcie_vdm_ops *ops); ++ const struct mctp_pcie_vdm_ops *ops, ++ const char *ifname); +void mctp_pcie_vdm_receive_packet(struct net_device *ndev); +void mctp_pcie_vdm_remove_dev(struct net_device *ndev); ++void mctp_pcie_vdm_set_carrier(struct net_device *ndev, bool up); + +#endif /* CONFIG_MCTP_TRANSPORT_PCIE_VDM */ +#endif /* __LINUX_MCTP_PCIE_VDM_H */ diff --git a/include/linux/pwm.h b/include/linux/pwm.h ---- a/include/linux/pwm.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/linux/pwm.h 2026-04-08 18:03:49.312687013 +0000 +--- a/include/linux/pwm.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/linux/pwm.h 2026-07-17 08:44:56.389289611 +0000 @@ -405,6 +405,9 @@ int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner); #define devm_pwmchip_add(dev, chip) __devm_pwmchip_add(dev, chip, THIS_MODULE) @@ -95495,7 +99115,7 @@ diff --git a/include/linux/pwm.h b/include/linux/pwm.h struct pwm_device *of_pwm_single_xlate(struct pwm_chip *chip, diff --git a/include/linux/soc/aspeed/aspeed-otp.h b/include/linux/soc/aspeed/aspeed-otp.h --- a/include/linux/soc/aspeed/aspeed-otp.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/soc/aspeed/aspeed-otp.h 2026-04-08 18:03:48.435703036 +0000 ++++ b/include/linux/soc/aspeed/aspeed-otp.h 2026-07-17 08:44:55.413304655 +0000 @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ +/* @@ -95511,7 +99131,7 @@ diff --git a/include/linux/soc/aspeed/aspeed-otp.h b/include/linux/soc/aspeed/as +#endif /* _LINUX_ASPEED_OTP_H */ diff --git a/include/linux/soc/aspeed/aspeed-udma.h b/include/linux/soc/aspeed/aspeed-udma.h --- a/include/linux/soc/aspeed/aspeed-udma.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/linux/soc/aspeed/aspeed-udma.h 2026-04-08 18:03:48.430703128 +0000 ++++ b/include/linux/soc/aspeed/aspeed-udma.h 2026-07-17 08:44:55.408304732 +0000 @@ -0,0 +1,26 @@ +#ifndef __ASPEED_UDMA_H__ +#define __ASPEED_UDMA_H__ @@ -95541,7 +99161,7 @@ diff --git a/include/linux/soc/aspeed/aspeed-udma.h b/include/linux/soc/aspeed/a +#endif diff --git a/include/soc/aspeed/reset-aspeed.h b/include/soc/aspeed/reset-aspeed.h --- a/include/soc/aspeed/reset-aspeed.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/soc/aspeed/reset-aspeed.h 2026-04-08 18:03:48.391703840 +0000 ++++ b/include/soc/aspeed/reset-aspeed.h 2026-07-17 08:44:55.370305318 +0000 @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* @@ -95566,7 +99186,7 @@ diff --git a/include/soc/aspeed/reset-aspeed.h b/include/soc/aspeed/reset-aspeed +#endif /* __RESET_ASPEED_H__ */ diff --git a/include/trace/events/xdma.h b/include/trace/events/xdma.h --- a/include/trace/events/xdma.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/trace/events/xdma.h 2026-04-08 18:03:49.268687817 +0000 ++++ b/include/trace/events/xdma.h 2026-07-17 08:44:56.346290274 +0000 @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + @@ -95696,7 +99316,7 @@ diff --git a/include/trace/events/xdma.h b/include/trace/events/xdma.h +#include diff --git a/include/uapi/linux/aspeed-mctp.h b/include/uapi/linux/aspeed-mctp.h --- a/include/uapi/linux/aspeed-mctp.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/aspeed-mctp.h 2026-04-08 18:03:48.425703219 +0000 ++++ b/include/uapi/linux/aspeed-mctp.h 2026-07-17 08:44:55.403304809 +0000 @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2020 Intel Corporation */ @@ -95835,8 +99455,8 @@ diff --git a/include/uapi/linux/aspeed-mctp.h b/include/uapi/linux/aspeed-mctp.h + +#endif /* _UAPI_LINUX_ASPEED_MCTP_H */ diff --git a/include/uapi/linux/aspeed-video.h b/include/uapi/linux/aspeed-video.h ---- a/include/uapi/linux/aspeed-video.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/uapi/linux/aspeed-video.h 2026-04-08 18:03:48.401703657 +0000 +--- a/include/uapi/linux/aspeed-video.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/uapi/linux/aspeed-video.h 2026-07-17 08:44:55.379305179 +0000 @@ -8,6 +8,15 @@ #include @@ -95855,7 +99475,7 @@ diff --git a/include/uapi/linux/aspeed-video.h b/include/uapi/linux/aspeed-video diff --git a/include/uapi/linux/aspeed-xdma.h b/include/uapi/linux/aspeed-xdma.h --- a/include/uapi/linux/aspeed-xdma.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/aspeed-xdma.h 2026-04-08 18:03:48.410703493 +0000 ++++ b/include/uapi/linux/aspeed-xdma.h 2026-07-17 08:44:55.389305025 +0000 @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* Copyright IBM Corp 2019 */ @@ -95901,7 +99521,7 @@ diff --git a/include/uapi/linux/aspeed-xdma.h b/include/uapi/linux/aspeed-xdma.h +#endif /* _UAPI_LINUX_ASPEED_XDMA_H_ */ diff --git a/include/uapi/linux/otp_ast2600.h b/include/uapi/linux/otp_ast2600.h --- a/include/uapi/linux/otp_ast2600.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/otp_ast2600.h 2026-04-08 18:03:48.415703402 +0000 ++++ b/include/uapi/linux/otp_ast2600.h 2026-07-17 08:44:55.394304948 +0000 @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ +/* @@ -95944,7 +99564,7 @@ diff --git a/include/uapi/linux/otp_ast2600.h b/include/uapi/linux/otp_ast2600.h +#endif /* _UAPI_LINUX_OTP_AST2600_H */ diff --git a/include/uapi/linux/otp_ast2700.h b/include/uapi/linux/otp_ast2700.h --- a/include/uapi/linux/otp_ast2700.h 1970-01-01 00:00:00.000000000 +0000 -+++ b/include/uapi/linux/otp_ast2700.h 2026-04-08 18:03:48.396703749 +0000 ++++ b/include/uapi/linux/otp_ast2700.h 2026-07-17 08:44:55.375305240 +0000 @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ +/* @@ -95994,8 +99614,8 @@ diff --git a/include/uapi/linux/otp_ast2700.h b/include/uapi/linux/otp_ast2700.h + +#endif /* _UAPI_LINUX_OTP_AST2700_H */ diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h ---- a/include/uapi/linux/videodev2.h 2025-08-01 08:48:47.000000000 +0000 -+++ b/include/uapi/linux/videodev2.h 2026-04-08 18:03:49.290687415 +0000 +--- a/include/uapi/linux/videodev2.h 2026-06-19 11:42:39.000000000 +0000 ++++ b/include/uapi/linux/videodev2.h 2026-07-17 08:44:56.367289951 +0000 @@ -880,6 +880,7 @@ /* Flags */ #define V4L2_PIX_FMT_FLAG_PREMUL_ALPHA 0x00000001 diff --git a/patches-sonic/cisco-hwmon-pmbus_core-pec-support-check.patch b/patches-sonic/cisco-hwmon-pmbus_core-pec-support-check.patch deleted file mode 100644 index bd6d5cedd..000000000 --- a/patches-sonic/cisco-hwmon-pmbus_core-pec-support-check.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 8451736f488d2e24da57a6d6d8a3880bd023812b Mon Sep 17 00:00:00 2001 -From: Madhava Reddy Siddareddygari -Date: Tue, 9 Nov 2021 10:54:31 -0800 -Subject: [PATCH] hwmon: (pmbus_core) Check adapter PEC support - -Currently, for Packet Error Checking (PEC) only the controller -is checked for support. This causes problems on the cisco-8000 -platform where a SMBUS transaction errors are observed. This is -because PEC has to be enabled only if both controller and -adapter supports it. - -Added code to check PEC capability for adapter and enable it -only if both controller and adapter supports PEC. - -This patch was applied to Linux 5.14 in commit -4e5418f787ec56d7fe3c6efee486b8f508c58baf -(hwmon: (pmbus_core) Check adapter PEC support). - -Signed-off-by: Madhava Reddy Siddareddygari ---- - drivers/hwmon/pmbus/pmbus_core.c | 10 ++++++---- - 1 file changed, 6 insertions(+), 4 deletions(-) - -diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c -index 192442b3b..634615324 100644 ---- a/drivers/hwmon/pmbus/pmbus_core.c -+++ b/drivers/hwmon/pmbus/pmbus_core.c -@@ -2203,10 +2203,12 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, - data->has_status_word = true; - } - -- /* Enable PEC if the controller supports it */ -- ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); -- if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) -- client->flags |= I2C_CLIENT_PEC; -+ /* Enable PEC if the controller and bus support it */ -+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) { -+ ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); -+ if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) -+ client->flags |= I2C_CLIENT_PEC; -+ } - - /* - * Check if the chip is write protected. If it is, we can not clear --- -2.25.1 - diff --git a/patches-sonic/cisco-x86-gpio-config.patch b/patches-sonic/cisco-x86-gpio-config.patch deleted file mode 100644 index 5df46b7b1..000000000 --- a/patches-sonic/cisco-x86-gpio-config.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 7106f3961be1dc0d921efe5c719ada6096307227 Mon Sep 17 00:00:00 2001 -From: Madhava Reddy Siddareddygari -Date: Tue, 10 Aug 2021 12:51:47 -0700 -Subject: [PATCH] x86/Kconfig: Introduce ARCH_NR_GPIO - -The x86 platform did not allow configuring the maximum number of GPIOs -supported, although the ARM platform did. For cisco-8000 platform, -each FPGA gpio IP block can support 1K pins. Distributed chassis with -Route Processor and Fabric cards can have 10 such IP blocks, along with -additional pins through i2c gpio extenders. - -This patch supports configurable number of GPIO's at kernel config time -similar to ARM platform. - -Signed-off-by: Madhava Reddy Siddareddygari ---- - arch/x86/Kconfig | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig -index d2453b251..e894cd71d 100644 ---- a/arch/x86/Kconfig -+++ b/arch/x86/Kconfig -@@ -342,6 +342,11 @@ config SCHED_MC_PRIO - - config ARCH_NR_GPIO - int -+ prompt "Maximum number of GPIO's" - default 1024 if X86_64 - default 512 -+ help -+ Maximum number of GPIOs in the system. -+ -+ If unsure, leave the default value. - - config ARCH_SUSPEND_POSSIBLE - def_bool y --- -2.26.2 - diff --git a/patches-sonic/mmc-sdhci-of-aspeed-Improve-CMD6-timing_v6.18.patch b/patches-sonic/mmc-sdhci-of-aspeed-Improve-CMD6-timing_v6.18.patch deleted file mode 100644 index 4a32648e4..000000000 --- a/patches-sonic/mmc-sdhci-of-aspeed-Improve-CMD6-timing_v6.18.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 5f1adff1a89dd68acfe81157b9c20b38d87ba18f Mon Sep 17 00:00:00 2001 -From: Cool Lee -Date: Fri, 10 Apr 2026 15:37:51 +0800 -Subject: [PATCH] mmc: sdhci-of-aspeed: Improve CMD6 timing - -The timing is critical before select_hs200 due to a 1_8V_SIGNAL is set. -When this is set, the post edge between CLK and CMD is tight. Make an -inverse to get a tolerance before CMD6 to switch HS200. - -Signed-off-by: Cool Lee -(cherry picked from commit e3532140fbe86fe88abacde37aebbbd5520fbc9d) -Upstream: https://github.com/AspeedTech-BMC/linux/commit/e3532140fbe86fe88abacde37aebbbd5520fbc9d -Signed-off-by: Prajjwal Singh ---- - drivers/mmc/host/sdhci-of-aspeed.c | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - -diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c -index 6d309f47d..e4a1e91c5 100644 ---- a/drivers/mmc/host/sdhci-of-aspeed.c -+++ b/drivers/mmc/host/sdhci-of-aspeed.c -@@ -312,6 +312,24 @@ static int aspeed_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) - - static void aspeed_sdhci_voltage_switch(struct sdhci_host *host) - { -+ struct sdhci_pltfm_host *pltfm_priv; -+ struct aspeed_sdhci *sdhci; -+ struct aspeed_sdc *sdc; -+ u32 out_phase, enable_mask, inverted = 1; -+ -+ pltfm_priv = sdhci_priv(host); -+ sdhci = sdhci_pltfm_priv(pltfm_priv); -+ sdc = sdhci->parent; -+ -+ enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN | ASPEED_SDC_S0_PHASE_IN_EN; -+ -+ inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; -+ out_phase = readl(sdc->regs + ASPEED_SDC_PHASE) & ASPEED_SDC_S0_PHASE_OUT; -+ out_phase |= (inverted << ASPEED_SDC_S0_PHASE_OUT_SHIFT); -+ -+ /* output inverse */ -+ writel(out_phase | enable_mask, sdc->regs + ASPEED_SDC_PHASE); -+ - mdelay(30); - } - --- -2.47.3 - diff --git a/patches-sonic/mmc-sdhci-of-aspeed-Optimize-tuning-mechanism_v6.12.patch b/patches-sonic/mmc-sdhci-of-aspeed-Optimize-tuning-mechanism_v6.12.patch deleted file mode 100644 index 0ed7da118..000000000 --- a/patches-sonic/mmc-sdhci-of-aspeed-Optimize-tuning-mechanism_v6.12.patch +++ /dev/null @@ -1,231 +0,0 @@ -From 2af0060637db659bd20798de0e0896ce0b0fa1e6 Mon Sep 17 00:00:00 2001 -From: Cool Lee -Date: Fri, 24 Apr 2026 10:43:49 +0800 -Subject: [PATCH] mmc: sdhci-of-aspeed: Optimize tuning mechanism - -Rewrote aspeed_sdhci_execute_tuning with circular window detection -across all 30 phase positions. - -Signed-off-by: Cool Lee -(cherry picked from commit 5fa88b13a96cf0059009f13cdf0b5b8b7b9c51ea) -Upstream: https://github.com/AspeedTech-BMC/linux/commit/5fa88b13a96cf0059009f13cdf0b5b8b7b9c51ea -Signed-off-by: Prajjwal Singh ---- - drivers/mmc/host/sdhci-of-aspeed.c | 178 ++++++++++++++++------------- - 1 file changed, 96 insertions(+), 82 deletions(-) - -diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c -index e4a1e91c5..684301b5d 100644 ---- a/drivers/mmc/host/sdhci-of-aspeed.c -+++ b/drivers/mmc/host/sdhci-of-aspeed.c -@@ -201,111 +201,125 @@ static void aspeed_sdhci_reset(struct sdhci_host *host, u8 mask) - sdhci_reset(host, mask); - } - -+/* -+ * aspeed_sdhci_find_center_tap - find the midpoint of the widest passing window -+ * @pass: boolean array [0..total-1], true = tuning passed at this position -+ * @total: number of phase positions (ASPEED_SDHCI_NR_TAPS * 2 = 30) -+ * -+ * Phase space layout (15 taps = half cycle at 200 MHz, ~167 ps/tap): -+ * pos [0..14] - rising edge, tap delay 0..14 ( 0° .. ~168°) -+ * pos [15..29] - falling edge, tap delay 0..14 (180° .. ~348°) -+ * -+ * The array is treated as circular so windows that wrap across the -+ * rising/falling boundary (around 0°/360°) are detected correctly. -+ * -+ * Returns a 5-bit phase_bits value: -+ * bits [3:0] = tap index -+ * bit [4] = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK (set for falling edge) -+ */ -+static u32 aspeed_sdhci_find_center_tap(const bool *pass, int total) -+{ -+ int pos, cur_start = -1, cur_len = 0, best_start = 0, best_len = 0; -+ int center_pos; -+ -+ /* -+ * Double-scan the circular buffer: the second pass detects windows -+ * that straddle the index-0 wrap-around boundary. The window length -+ * is capped at 'total' so a full-circle window is counted only once. -+ */ -+ for (pos = 0; pos < total * 2; pos++) { -+ if (pass[pos % total]) { -+ if (cur_start < 0) -+ cur_start = pos; -+ if (++cur_len > total) -+ cur_len = total; -+ if (cur_len > best_len) { -+ best_len = cur_len; -+ best_start = cur_start; -+ } -+ } else { -+ cur_start = -1; -+ cur_len = 0; -+ } -+ } -+ -+ if (!best_len) -+ return 0; -+ -+ center_pos = (best_start + best_len / 2) % total; -+ -+ if (center_pos < ASPEED_SDHCI_NR_TAPS) -+ return (u32)center_pos; -+ -+ return (u32)(center_pos - ASPEED_SDHCI_NR_TAPS) | ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; -+} -+ - static int aspeed_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) - { - struct sdhci_pltfm_host *pltfm_priv; - struct aspeed_sdhci *sdhci; - struct aspeed_sdc *sdc; - struct device *dev; -- -- u32 val, left, right, edge; -- u32 window, oldwindow = 0, center; -- u32 in_phase, out_phase, enable_mask, inverted = 0; -+ /* -+ * 15 taps cover half a 200 MHz clock cycle (2.5 ns ≈ 167 ps/tap). -+ * Rising and falling edges together span a full cycle: 30 positions. -+ */ -+ const int total = ASPEED_SDHCI_NR_TAPS * 2; -+ bool pass[ASPEED_SDHCI_NR_TAPS * 2]; -+ u32 enable_mask, base, val, phase_bits; -+ int pos; - - dev = mmc_dev(host->mmc); - pltfm_priv = sdhci_priv(host); - sdhci = sdhci_pltfm_priv(pltfm_priv); - sdc = sdhci->parent; - -- out_phase = readl(sdc->regs + ASPEED_SDC_PHASE) & ASPEED_SDC_S0_PHASE_OUT; -- - enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN | ASPEED_SDC_S0_PHASE_IN_EN; -- -- /* -- * There are two window upon clock rising and falling edge. -- * Iterate each tap delay to find the valid window and choose the -- * bigger one, set the tap delay at the middle of window. -- */ -- for (edge = 0; edge < 2; edge++) { -- if (edge == 1) -- inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; -- -- val = (out_phase | enable_mask | (inverted << ASPEED_SDC_S0_PHASE_IN_SHIFT)); -- -- /* find the left boundary */ -- for (left = 0; left < ASPEED_SDHCI_NR_TAPS + 1; left++) { -- in_phase = val | (left << ASPEED_SDC_S0_PHASE_IN_SHIFT); -- writel(in_phase, sdc->regs + ASPEED_SDC_PHASE); -- -- if (!mmc_send_tuning(host->mmc, opcode, NULL)) -- break; -- } -- -- /* find the right boundary */ -- for (right = left + 1; right < ASPEED_SDHCI_NR_TAPS + 1; right++) { -- in_phase = val | (right << ASPEED_SDC_S0_PHASE_IN_SHIFT); -- writel(in_phase, sdc->regs + ASPEED_SDC_PHASE); -- -- if (mmc_send_tuning(host->mmc, opcode, NULL)) -- break; -- } -- -- window = right - left; -- pr_debug("tuning window[%d][%d~%d] = %d\n", edge, left, right, window); -- -- if (window > oldwindow) { -- oldwindow = window; -- center = (((right - 1) + left) / 2) | inverted; -- } -+ base = readl(sdc->regs + ASPEED_SDC_PHASE) & ASPEED_SDC_S0_PHASE_OUT; -+ -+ /* -------- Step 1: scan all input-phase positions -------- */ -+ for (pos = 0; pos < total; pos++) { -+ phase_bits = (pos < ASPEED_SDHCI_NR_TAPS) -+ ? (u32)pos -+ : (u32)(pos - ASPEED_SDHCI_NR_TAPS) | ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; -+ -+ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_IN_SHIFT); -+ writel(val, sdc->regs + ASPEED_SDC_PHASE); -+ pass[pos] = !mmc_send_tuning(host->mmc, opcode, NULL); -+ -+ dev_dbg(dev, "in pos=%2d (%s tap%2u) %s\n", pos, -+ pos < ASPEED_SDHCI_NR_TAPS ? "rise" : "fall", -+ pos % ASPEED_SDHCI_NR_TAPS, -+ pass[pos] ? "pass" : "fail"); - } - -- val = (out_phase | enable_mask | (center << ASPEED_SDC_S0_PHASE_IN_SHIFT)); -+ phase_bits = aspeed_sdhci_find_center_tap(pass, total); -+ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_IN_SHIFT); - writel(val, sdc->regs + ASPEED_SDC_PHASE); -+ dev_dbg(dev, "input center: phase_bits=0x%02x reg=0x%08x\n", phase_bits, val); - -- pr_debug("input tuning result=%x\n", val); -- -- inverted = 0; -- out_phase = val & ~ASPEED_SDC_S0_PHASE_OUT; -- in_phase = out_phase; -- oldwindow = 0; -- -- for (edge = 0; edge < 2; edge++) { -- if (edge == 1) -- inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; -+ /* -------- Step 2: scan all output-phase positions -------- */ -+ base = val & ~ASPEED_SDC_S0_PHASE_OUT; - -- val = (in_phase | enable_mask | (inverted << ASPEED_SDC_S0_PHASE_OUT_SHIFT)); -- -- /* find the left boundary */ -- for (left = 0; left < ASPEED_SDHCI_NR_TAPS + 1; left++) { -- out_phase = val | (left << ASPEED_SDC_S0_PHASE_OUT_SHIFT); -- writel(out_phase, sdc->regs + ASPEED_SDC_PHASE); -- -- if (!mmc_send_tuning(host->mmc, opcode, NULL)) -- break; -- } -+ for (pos = 0; pos < total; pos++) { -+ phase_bits = (pos < ASPEED_SDHCI_NR_TAPS) -+ ? (u32)pos -+ : (u32)(pos - ASPEED_SDHCI_NR_TAPS) | ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; - -- /* find the right boundary */ -- for (right = left + 1; right < ASPEED_SDHCI_NR_TAPS + 1; right++) { -- out_phase = val | (right << ASPEED_SDC_S0_PHASE_OUT_SHIFT); -- writel(out_phase, sdc->regs + ASPEED_SDC_PHASE); -+ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_OUT_SHIFT); -+ writel(val, sdc->regs + ASPEED_SDC_PHASE); -+ pass[pos] = !mmc_send_tuning(host->mmc, opcode, NULL); - -- if (mmc_send_tuning(host->mmc, opcode, NULL)) -- break; -- } -- -- window = right - left; -- pr_debug("tuning window[%d][%d~%d] = %d\n", edge, left, right, window); -- -- if (window > oldwindow) { -- oldwindow = window; -- center = (((right - 1) + left) / 2) | inverted; -- } -+ dev_dbg(dev, "out pos=%2d (%s tap%2u) %s\n", pos, -+ pos < ASPEED_SDHCI_NR_TAPS ? "rise" : "fall", -+ pos % ASPEED_SDHCI_NR_TAPS, -+ pass[pos] ? "pass" : "fail"); - } - -- val = (in_phase | enable_mask | (center << ASPEED_SDC_S0_PHASE_OUT_SHIFT)); -+ phase_bits = aspeed_sdhci_find_center_tap(pass, total); -+ val = base | enable_mask | (phase_bits << ASPEED_SDC_S0_PHASE_OUT_SHIFT); - writel(val, sdc->regs + ASPEED_SDC_PHASE); -- -- pr_debug("output tuning result=%x\n", val); -+ dev_dbg(dev, "output center: phase_bits=0x%02x reg=0x%08x\n", phase_bits, val); - - return mmc_send_tuning(host->mmc, opcode, NULL); - } --- -2.47.3 - diff --git a/patches-sonic/nexthop-b27-dts.patch b/patches-sonic/nexthop-b27-dts.patch index eef9956a7..ab976e338 100644 --- a/patches-sonic/nexthop-b27-dts.patch +++ b/patches-sonic/nexthop-b27-dts.patch @@ -27,9 +27,9 @@ index 4458a1b..1b26168 100644 dtb-$(CONFIG_ARCH_ASPEED) += \ ast2700-evb.dtb \ + nexthop-b27-r0.dtb \ + ast2700-evb_ast1700-evb.dtb \ + ast2700-evb_ast1700-evb-dual.dtb \ ast2700-raw.dtb \ - ast2700-evb-s0.dtb \ - ast2700-evb-s1.dtb \ diff --git a/arch/arm64/boot/dts/aspeed/nexthop-b27-r0.dts b/arch/arm64/boot/dts/aspeed/nexthop-b27-r0.dts new file mode 100644 index 0000000..edad94f diff --git a/patches-sonic/qsa-2026-apparmor/0001-apparmor-validate-DFA-start-states-are-in-bounds-in-.patch b/patches-sonic/qsa-2026-apparmor/0001-apparmor-validate-DFA-start-states-are-in-bounds-in-.patch deleted file mode 100644 index 3447056fa..000000000 --- a/patches-sonic/qsa-2026-apparmor/0001-apparmor-validate-DFA-start-states-are-in-bounds-in-.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0bc5b34e913eadcd3bcc51e4d365bf35f81b5c1f Mon Sep 17 00:00:00 2001 -From: Massimiliano Pellizzer -Date: Thu, 15 Jan 2026 15:30:50 +0100 -Subject: [PATCH 01/11] apparmor: validate DFA start states are in bounds in - unpack_pdb - -Start states are read from untrusted data and used as indexes into the -DFA state tables. The aa_dfa_next() function call in unpack_pdb() will -access dfa->tables[YYTD_ID_BASE][start], and if the start state exceeds -the number of states in the DFA, this results in an out-of-bound read. - -================================================================== - BUG: KASAN: slab-out-of-bounds in aa_dfa_next+0x2a1/0x360 - Read of size 4 at addr ffff88811956fb90 by task su/1097 - ... - -Reject policies with out-of-bounds start states during unpacking -to prevent the issue. - -Fixes: ad5ff3db53c6 ("AppArmor: Add ability to load extended policy") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: Massimiliano Pellizzer -Signed-off-by: John Johansen ---- - security/apparmor/policy_unpack.c | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c -index 3483c595f999..e472442274d1 100644 ---- a/security/apparmor/policy_unpack.c -+++ b/security/apparmor/policy_unpack.c -@@ -760,7 +760,17 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy, - if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) { - /* default start state for xmatch and file dfa */ - pdb->start[AA_CLASS_FILE] = DFA_START; -- } /* setup class index */ -+ } -+ -+ size_t state_count = pdb->dfa->tables[YYTD_ID_BASE]->td_lolen; -+ -+ if (pdb->start[0] >= state_count || -+ pdb->start[AA_CLASS_FILE] >= state_count) { -+ *info = "invalid dfa start state"; -+ goto fail; -+ } -+ -+ /* setup class index */ - for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) { - pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0], - i); --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0002-apparmor-fix-memory-leak-in-verify_header.patch b/patches-sonic/qsa-2026-apparmor/0002-apparmor-fix-memory-leak-in-verify_header.patch deleted file mode 100644 index a7986efbc..000000000 --- a/patches-sonic/qsa-2026-apparmor/0002-apparmor-fix-memory-leak-in-verify_header.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 66ecb6689b1ee73fd2ecf7aea226ef21d72163cd Mon Sep 17 00:00:00 2001 -From: Massimiliano Pellizzer -Date: Tue, 20 Jan 2026 15:24:04 +0100 -Subject: [PATCH 02/11] apparmor: fix memory leak in verify_header - -The function sets `*ns = NULL` on every call, leaking the namespace -string allocated in previous iterations when multiple profiles are -unpacked. This also breaks namespace consistency checking since *ns -is always NULL when the comparison is made. - -Remove the incorrect assignment. -The caller (aa_unpack) initializes *ns to NULL once before the loop, -which is sufficient. - -Fixes: dd51c8485763 ("apparmor: provide base for multiple profiles to be replaced at once") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: Massimiliano Pellizzer -Signed-off-by: John Johansen ---- - security/apparmor/policy_unpack.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c -index e472442274d1..0232fc29a234 100644 ---- a/security/apparmor/policy_unpack.c -+++ b/security/apparmor/policy_unpack.c -@@ -1140,7 +1140,6 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) - { - int error = -EPROTONOSUPPORT; - const char *name = NULL; -- *ns = NULL; - - /* get the interface version */ - if (!aa_unpack_u32(e, &e->version, "version")) { --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0003-apparmor-replace-recursive-profile-removal-with-iter.patch b/patches-sonic/qsa-2026-apparmor/0003-apparmor-replace-recursive-profile-removal-with-iter.patch deleted file mode 100644 index 26a038e22..000000000 --- a/patches-sonic/qsa-2026-apparmor/0003-apparmor-replace-recursive-profile-removal-with-iter.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 83417f15ea8253d96e6636fbb1c1cf95c0e6b6af Mon Sep 17 00:00:00 2001 -From: Massimiliano Pellizzer -Date: Tue, 13 Jan 2026 09:09:43 +0100 -Subject: [PATCH 03/11] apparmor: replace recursive profile removal with - iterative approach - -The profile removal code uses recursion when removing nested profiles, -which can lead to kernel stack exhaustion and system crashes. - -Reproducer: - $ pf='a'; for ((i=0; i<1024; i++)); do - echo -e "profile $pf { \n }" | apparmor_parser -K -a; - pf="$pf//x"; - done - $ echo -n a > /sys/kernel/security/apparmor/.remove - -Replace the recursive __aa_profile_list_release() approach with an -iterative approach in __remove_profile(). The function repeatedly -finds and removes leaf profiles until the entire subtree is removed, -maintaining the same removal semantic without recursion. - -Fixes: c88d4c7b049e ("AppArmor: core policy routines") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: Massimiliano Pellizzer -Signed-off-by: John Johansen ---- - security/apparmor/policy.c | 30 +++++++++++++++++++++++++++--- - 1 file changed, 27 insertions(+), 3 deletions(-) - -diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c -index 105706abf281..f6869ea00d1d 100644 ---- a/security/apparmor/policy.c -+++ b/security/apparmor/policy.c -@@ -184,19 +184,43 @@ static void __list_remove_profile(struct aa_profile *profile) - } - - /** -- * __remove_profile - remove old profile, and children -- * @profile: profile to be replaced (NOT NULL) -+ * __remove_profile - remove profile, and children -+ * @profile: profile to be removed (NOT NULL) - * - * Requires: namespace list lock be held, or list not be shared - */ - static void __remove_profile(struct aa_profile *profile) - { -+ struct aa_profile *curr, *to_remove; -+ - AA_BUG(!profile); - AA_BUG(!profile->ns); - AA_BUG(!mutex_is_locked(&profile->ns->lock)); - - /* release any children lists first */ -- __aa_profile_list_release(&profile->base.profiles); -+ if (!list_empty(&profile->base.profiles)) { -+ curr = list_first_entry(&profile->base.profiles, struct aa_profile, base.list); -+ -+ while (curr != profile) { -+ -+ while (!list_empty(&curr->base.profiles)) -+ curr = list_first_entry(&curr->base.profiles, -+ struct aa_profile, base.list); -+ -+ to_remove = curr; -+ if (!list_is_last(&to_remove->base.list, -+ &aa_deref_parent(curr)->base.profiles)) -+ curr = list_next_entry(to_remove, base.list); -+ else -+ curr = aa_deref_parent(curr); -+ -+ /* released by free_profile */ -+ aa_label_remove(&to_remove->label); -+ __aafs_profile_rmdir(to_remove); -+ __list_remove_profile(to_remove); -+ } -+ } -+ - /* released by free_profile */ - aa_label_remove(&profile->label); - __aafs_profile_rmdir(profile); --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0004-apparmor-fix-limit-the-number-of-levels-of-policy-na.patch b/patches-sonic/qsa-2026-apparmor/0004-apparmor-fix-limit-the-number-of-levels-of-policy-na.patch deleted file mode 100644 index a506aa58f..000000000 --- a/patches-sonic/qsa-2026-apparmor/0004-apparmor-fix-limit-the-number-of-levels-of-policy-na.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 4fd74536d8c7e2fc69dc58907a2b082e393cfff4 Mon Sep 17 00:00:00 2001 -From: John Johansen -Date: Tue, 3 Mar 2026 11:08:02 -0800 -Subject: [PATCH 04/11] apparmor: fix: limit the number of levels of policy - namespaces - -Currently the number of policy namespaces is not bounded relying on -the user namespace limit. However policy namespaces aren't strictly -tied to user namespaces and it is possible to create them and nest -them arbitrarily deep which can be used to exhaust system resource. - -Hard cap policy namespaces to the same depth as user namespaces. - -Fixes: c88d4c7b049e8 ("AppArmor: core policy routines") -Reported-by: Qualys Security Advisory -Reviewed-by: Ryan Lee -Reviewed-by: Cengiz Can -Signed-off-by: John Johansen ---- - security/apparmor/include/policy_ns.h | 2 ++ - security/apparmor/policy_ns.c | 2 ++ - 2 files changed, 4 insertions(+) - -diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h -index d646070fd966..cc6e84151812 100644 ---- a/security/apparmor/include/policy_ns.h -+++ b/security/apparmor/include/policy_ns.h -@@ -18,6 +18,8 @@ - #include "label.h" - #include "policy.h" - -+/* Match max depth of user namespaces */ -+#define MAX_NS_DEPTH 32 - - /* struct aa_ns_acct - accounting of profiles in namespace - * @max_size: maximum space allowed for all profiles in namespace -diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c -index 1f02cfe1d974..06c0bde97a63 100644 ---- a/security/apparmor/policy_ns.c -+++ b/security/apparmor/policy_ns.c -@@ -223,6 +223,8 @@ static struct aa_ns *__aa_create_ns(struct aa_ns *parent, const char *name, - AA_BUG(!name); - AA_BUG(!mutex_is_locked(&parent->lock)); - -+ if (parent->level > MAX_NS_DEPTH) -+ return ERR_PTR(-ENOSPC); - ns = alloc_ns(parent->base.hname, name); - if (!ns) - return ERR_PTR(-ENOMEM); --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0005-apparmor-fix-side-effect-bug-in-match_char-macro-usa.patch b/patches-sonic/qsa-2026-apparmor/0005-apparmor-fix-side-effect-bug-in-match_char-macro-usa.patch deleted file mode 100644 index df6b18709..000000000 --- a/patches-sonic/qsa-2026-apparmor/0005-apparmor-fix-side-effect-bug-in-match_char-macro-usa.patch +++ /dev/null @@ -1,124 +0,0 @@ -From 05302d7b0886b14de3d0e91a1b0c332f9ea0d292 Mon Sep 17 00:00:00 2001 -From: Massimiliano Pellizzer -Date: Thu, 29 Jan 2026 17:08:25 +0100 -Subject: [PATCH 05/11] apparmor: fix side-effect bug in match_char() macro - usage - -The match_char() macro evaluates its character parameter multiple -times when traversing differential encoding chains. When invoked -with *str++, the string pointer advances on each iteration of the -inner do-while loop, causing the DFA to check different characters -at each iteration and therefore skip input characters. -This results in out-of-bounds reads when the pointer advances past -the input buffer boundary. - -[ 94.984676] ================================================================== -[ 94.985301] BUG: KASAN: slab-out-of-bounds in aa_dfa_match+0x5ae/0x760 -[ 94.985655] Read of size 1 at addr ffff888100342000 by task file/976 - -[ 94.986319] CPU: 7 UID: 1000 PID: 976 Comm: file Not tainted 6.19.0-rc7-next-20260127 #1 PREEMPT(lazy) -[ 94.986322] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 -[ 94.986329] Call Trace: -[ 94.986341] -[ 94.986347] dump_stack_lvl+0x5e/0x80 -[ 94.986374] print_report+0xc8/0x270 -[ 94.986384] ? aa_dfa_match+0x5ae/0x760 -[ 94.986388] kasan_report+0x118/0x150 -[ 94.986401] ? aa_dfa_match+0x5ae/0x760 -[ 94.986405] aa_dfa_match+0x5ae/0x760 -[ 94.986408] __aa_path_perm+0x131/0x400 -[ 94.986418] aa_path_perm+0x219/0x2f0 -[ 94.986424] apparmor_file_open+0x345/0x570 -[ 94.986431] security_file_open+0x5c/0x140 -[ 94.986442] do_dentry_open+0x2f6/0x1120 -[ 94.986450] vfs_open+0x38/0x2b0 -[ 94.986453] ? may_open+0x1e2/0x2b0 -[ 94.986466] path_openat+0x231b/0x2b30 -[ 94.986469] ? __x64_sys_openat+0xf8/0x130 -[ 94.986477] do_file_open+0x19d/0x360 -[ 94.986487] do_sys_openat2+0x98/0x100 -[ 94.986491] __x64_sys_openat+0xf8/0x130 -[ 94.986499] do_syscall_64+0x8e/0x660 -[ 94.986515] ? count_memcg_events+0x15f/0x3c0 -[ 94.986526] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 94.986540] ? handle_mm_fault+0x1639/0x1ef0 -[ 94.986551] ? vma_start_read+0xf0/0x320 -[ 94.986558] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 94.986561] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 94.986563] ? fpregs_assert_state_consistent+0x50/0xe0 -[ 94.986572] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 94.986574] ? arch_exit_to_user_mode_prepare+0x9/0xb0 -[ 94.986587] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 94.986588] ? irqentry_exit+0x3c/0x590 -[ 94.986595] entry_SYSCALL_64_after_hwframe+0x76/0x7e -[ 94.986597] RIP: 0033:0x7fda4a79c3ea - -Fix by extracting the character value before invoking match_char, -ensuring single evaluation per outer loop. - -Fixes: 074c1cd798cb ("apparmor: dfa move character match into a macro") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: Massimiliano Pellizzer -Signed-off-by: John Johansen ---- - security/apparmor/match.c | 30 ++++++++++++++++++++---------- - 1 file changed, 20 insertions(+), 10 deletions(-) - -diff --git a/security/apparmor/match.c b/security/apparmor/match.c -index 12e036f8ce0f..5fbfdd75afb5 100644 ---- a/security/apparmor/match.c -+++ b/security/apparmor/match.c -@@ -408,13 +408,18 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start, - if (dfa->tables[YYTD_ID_EC]) { - /* Equivalence class table defined */ - u8 *equiv = EQUIV_TABLE(dfa); -- for (; len; len--) -- match_char(state, def, base, next, check, -- equiv[(u8) *str++]); -+ for (; len; len--) { -+ u8 c = equiv[(u8) *str]; -+ -+ match_char(state, def, base, next, check, c); -+ str++; -+ } - } else { - /* default is direct to next state */ -- for (; len; len--) -- match_char(state, def, base, next, check, (u8) *str++); -+ for (; len; len--) { -+ match_char(state, def, base, next, check, (u8) *str); -+ str++; -+ } - } - - return state; -@@ -448,13 +453,18 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str) - /* Equivalence class table defined */ - u8 *equiv = EQUIV_TABLE(dfa); - /* default is direct to next state */ -- while (*str) -- match_char(state, def, base, next, check, -- equiv[(u8) *str++]); -+ while (*str) { -+ u8 c = equiv[(u8) *str]; -+ -+ match_char(state, def, base, next, check, c); -+ str++; -+ } - } else { - /* default is direct to next state */ -- while (*str) -- match_char(state, def, base, next, check, (u8) *str++); -+ while (*str) { -+ match_char(state, def, base, next, check, (u8) *str); -+ str++; -+ } - } - - return state; --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0006-apparmor-fix-missing-bounds-check-on-DEFAULT-table-i.patch b/patches-sonic/qsa-2026-apparmor/0006-apparmor-fix-missing-bounds-check-on-DEFAULT-table-i.patch deleted file mode 100644 index 9ba69881d..000000000 --- a/patches-sonic/qsa-2026-apparmor/0006-apparmor-fix-missing-bounds-check-on-DEFAULT-table-i.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 32ef9e21844645607f4d24203cede23e1a76ceec Mon Sep 17 00:00:00 2001 -From: Massimiliano Pellizzer -Date: Thu, 29 Jan 2026 16:51:11 +0100 -Subject: [PATCH 06/11] apparmor: fix missing bounds check on DEFAULT table in - verify_dfa() - -The verify_dfa() function only checks DEFAULT_TABLE bounds when the state -is not differentially encoded. - -When the verification loop traverses the differential encoding chain, -it reads k = DEFAULT_TABLE[j] and uses k as an array index without -validation. A malformed DFA with DEFAULT_TABLE[j] >= state_count, -therefore, causes both out-of-bounds reads and writes. - -[ 57.179855] ================================================================== -[ 57.180549] BUG: KASAN: slab-out-of-bounds in verify_dfa+0x59a/0x660 -[ 57.180904] Read of size 4 at addr ffff888100eadec4 by task su/993 - -[ 57.181554] CPU: 1 UID: 0 PID: 993 Comm: su Not tainted 6.19.0-rc7-next-20260127 #1 PREEMPT(lazy) -[ 57.181558] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 -[ 57.181563] Call Trace: -[ 57.181572] -[ 57.181577] dump_stack_lvl+0x5e/0x80 -[ 57.181596] print_report+0xc8/0x270 -[ 57.181605] ? verify_dfa+0x59a/0x660 -[ 57.181608] kasan_report+0x118/0x150 -[ 57.181620] ? verify_dfa+0x59a/0x660 -[ 57.181623] verify_dfa+0x59a/0x660 -[ 57.181627] aa_dfa_unpack+0x1610/0x1740 -[ 57.181629] ? __kmalloc_cache_noprof+0x1d0/0x470 -[ 57.181640] unpack_pdb+0x86d/0x46b0 -[ 57.181647] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 57.181653] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 57.181656] ? aa_unpack_nameX+0x1a8/0x300 -[ 57.181659] aa_unpack+0x20b0/0x4c30 -[ 57.181662] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 57.181664] ? stack_depot_save_flags+0x33/0x700 -[ 57.181681] ? kasan_save_track+0x4f/0x80 -[ 57.181683] ? kasan_save_track+0x3e/0x80 -[ 57.181686] ? __kasan_kmalloc+0x93/0xb0 -[ 57.181688] ? __kvmalloc_node_noprof+0x44a/0x780 -[ 57.181693] ? aa_simple_write_to_buffer+0x54/0x130 -[ 57.181697] ? policy_update+0x154/0x330 -[ 57.181704] aa_replace_profiles+0x15a/0x1dd0 -[ 57.181707] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 57.181710] ? __kvmalloc_node_noprof+0x44a/0x780 -[ 57.181712] ? aa_loaddata_alloc+0x77/0x140 -[ 57.181715] ? srso_alias_return_thunk+0x5/0xfbef5 -[ 57.181717] ? _copy_from_user+0x2a/0x70 -[ 57.181730] policy_update+0x17a/0x330 -[ 57.181733] profile_replace+0x153/0x1a0 -[ 57.181735] ? rw_verify_area+0x93/0x2d0 -[ 57.181740] vfs_write+0x235/0xab0 -[ 57.181745] ksys_write+0xb0/0x170 -[ 57.181748] do_syscall_64+0x8e/0x660 -[ 57.181762] entry_SYSCALL_64_after_hwframe+0x76/0x7e -[ 57.181765] RIP: 0033:0x7f6192792eb2 - -Remove the MATCH_FLAG_DIFF_ENCODE condition to validate all DEFAULT_TABLE -entries unconditionally. - -Fixes: 031dcc8f4e84 ("apparmor: dfa add support for state differential encoding") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: Massimiliano Pellizzer -Signed-off-by: John Johansen ---- - security/apparmor/match.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/security/apparmor/match.c b/security/apparmor/match.c -index 5fbfdd75afb5..98cf3b1f1649 100644 ---- a/security/apparmor/match.c -+++ b/security/apparmor/match.c -@@ -160,9 +160,10 @@ static int verify_dfa(struct aa_dfa *dfa) - if (state_count == 0) - goto out; - for (i = 0; i < state_count; i++) { -- if (!(BASE_TABLE(dfa)[i] & MATCH_FLAG_DIFF_ENCODE) && -- (DEFAULT_TABLE(dfa)[i] >= state_count)) -+ if (DEFAULT_TABLE(dfa)[i] >= state_count) { -+ pr_err("AppArmor DFA default state out of bounds"); - goto out; -+ } - if (BASE_TABLE(dfa)[i] & MATCH_FLAGS_INVALID) { - pr_err("AppArmor DFA state with invalid match flags"); - goto out; --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0007-apparmor-Fix-double-free-of-ns_name-in-aa_replace_pr.patch b/patches-sonic/qsa-2026-apparmor/0007-apparmor-Fix-double-free-of-ns_name-in-aa_replace_pr.patch deleted file mode 100644 index f87545d8b..000000000 --- a/patches-sonic/qsa-2026-apparmor/0007-apparmor-Fix-double-free-of-ns_name-in-aa_replace_pr.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 93eb370bd91e1f3f9eaa53a1a8fa910903febeed Mon Sep 17 00:00:00 2001 -From: John Johansen -Date: Wed, 10 Sep 2025 06:22:17 -0700 -Subject: [PATCH 07/11] apparmor: Fix double free of ns_name in - aa_replace_profiles() - -if ns_name is NULL after -1071 error = aa_unpack(udata, &lh, &ns_name); - -and if ent->ns_name contains an ns_name in -1089 } else if (ent->ns_name) { - -then ns_name is assigned the ent->ns_name -1095 ns_name = ent->ns_name; - -however ent->ns_name is freed at -1262 aa_load_ent_free(ent); - -and then again when freeing ns_name at -1270 kfree(ns_name); - -Fix this by NULLing out ent->ns_name after it is transferred to ns_name - -Fixes: 04dc715e24d08 ("apparmor: audit policy ns specified in policy load") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: John Johansen ---- - security/apparmor/policy.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c -index f6869ea00d1d..9a4e29cdd8c0 100644 ---- a/security/apparmor/policy.c -+++ b/security/apparmor/policy.c -@@ -1118,6 +1118,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, - goto fail; - } - ns_name = ent->ns_name; -+ ent->ns_name = NULL; - } else - count++; - } --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0008-apparmor-fix-unprivileged-local-user-can-do-privileg.patch b/patches-sonic/qsa-2026-apparmor/0008-apparmor-fix-unprivileged-local-user-can-do-privileg.patch deleted file mode 100644 index eb300c4ed..000000000 --- a/patches-sonic/qsa-2026-apparmor/0008-apparmor-fix-unprivileged-local-user-can-do-privileg.patch +++ /dev/null @@ -1,187 +0,0 @@ -From 1e21c55c0b49ec1e88dd519f5d44695feb2adad9 Mon Sep 17 00:00:00 2001 -From: John Johansen -Date: Fri, 7 Nov 2025 08:36:04 -0800 -Subject: [PATCH 08/11] apparmor: fix unprivileged local user can do privileged - policy management - -An unprivileged local user can load, replace, and remove profiles by -opening the apparmorfs interfaces, via a confused deputy attack, by -passing the opened fd to a privileged process, and getting the -privileged process to write to the interface. - -This does require a privileged target that can be manipulated to do -the write for the unprivileged process, but once such access is -achieved full policy management is possible and all the possible -implications that implies: removing confinement, DoS of system or -target applications by denying all execution, by-passing the -unprivileged user namespace restriction, to exploiting kernel bugs for -a local privilege escalation. - -The policy management interface can not have its permissions simply -changed from 0666 to 0600 because non-root processes need to be able -to load policy to different policy namespaces. - -Instead ensure the task writing the interface has privileges that -are a subset of the task that opened the interface. This is already -done via policy for confined processes, but unconfined can delegate -access to the opened fd, by-passing the usual policy check. - -Fixes: c88d4c7b049e8 ("AppArmor: core policy routines") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: John Johansen ---- - security/apparmor/apparmorfs.c | 16 ++++++++------ - security/apparmor/include/policy.h | 2 +- - security/apparmor/policy.c | 34 +++++++++++++++++++++++++++++- - 3 files changed, 43 insertions(+), 9 deletions(-) - -diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c -index 01b923d97a44..c708f0bfe594 100644 ---- a/security/apparmor/apparmorfs.c -+++ b/security/apparmor/apparmorfs.c -@@ -412,7 +412,8 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, - } - - static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, -- loff_t *pos, struct aa_ns *ns) -+ loff_t *pos, struct aa_ns *ns, -+ const struct cred *ocred) - { - struct aa_loaddata *data; - struct aa_label *label; -@@ -423,7 +424,7 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, - /* high level check about policy management - fine grained in - * below after unpack - */ -- error = aa_may_manage_policy(current_cred(), label, ns, mask); -+ error = aa_may_manage_policy(current_cred(), label, ns, ocred, mask); - if (error) - goto end_section; - -@@ -444,7 +445,8 @@ static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, - loff_t *pos) - { - struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); -- int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns); -+ int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns, -+ f->f_cred); - - aa_put_ns(ns); - -@@ -462,7 +464,7 @@ static ssize_t profile_replace(struct file *f, const char __user *buf, - { - struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); - int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, -- buf, size, pos, ns); -+ buf, size, pos, ns, f->f_cred); - aa_put_ns(ns); - - return error; -@@ -487,7 +489,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, - * below after unpack - */ - error = aa_may_manage_policy(current_cred(), label, ns, -- AA_MAY_REMOVE_POLICY); -+ f->f_cred, AA_MAY_REMOVE_POLICY); - if (error) - goto out; - -@@ -1804,7 +1806,7 @@ static int ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir, - int error; - - label = begin_current_label_crit_section(); -- error = aa_may_manage_policy(current_cred(), label, NULL, -+ error = aa_may_manage_policy(current_cred(), label, NULL, NULL, - AA_MAY_LOAD_POLICY); - end_current_label_crit_section(label); - if (error) -@@ -1854,7 +1856,7 @@ static int ns_rmdir_op(struct inode *dir, struct dentry *dentry) - int error; - - label = begin_current_label_crit_section(); -- error = aa_may_manage_policy(current_cred(), label, NULL, -+ error = aa_may_manage_policy(current_cred(), label, NULL, NULL, - AA_MAY_LOAD_POLICY); - end_current_label_crit_section(label); - if (error) -diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h -index 75088cc310b6..b8c35972883c 100644 ---- a/security/apparmor/include/policy.h -+++ b/security/apparmor/include/policy.h -@@ -393,7 +393,7 @@ bool aa_policy_admin_capable(const struct cred *subj_cred, - struct aa_label *label, struct aa_ns *ns); - int aa_may_manage_policy(const struct cred *subj_cred, - struct aa_label *label, struct aa_ns *ns, -- u32 mask); -+ const struct cred *ocred, u32 mask); - bool aa_current_policy_view_capable(struct aa_ns *ns); - bool aa_current_policy_admin_capable(struct aa_ns *ns); - -diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c -index 9a4e29cdd8c0..29f1cfd75090 100644 ---- a/security/apparmor/policy.c -+++ b/security/apparmor/policy.c -@@ -894,17 +894,44 @@ bool aa_current_policy_admin_capable(struct aa_ns *ns) - return res; - } - -+static bool is_subset_of_obj_privilege(const struct cred *cred, -+ struct aa_label *label, -+ const struct cred *ocred) -+{ -+ if (cred == ocred) -+ return true; -+ -+ if (!aa_label_is_subset(label, cred_label(ocred))) -+ return false; -+ /* don't allow crossing userns for now */ -+ if (cred->user_ns != ocred->user_ns) -+ return false; -+ if (!cap_issubset(cred->cap_inheritable, ocred->cap_inheritable)) -+ return false; -+ if (!cap_issubset(cred->cap_permitted, ocred->cap_permitted)) -+ return false; -+ if (!cap_issubset(cred->cap_effective, ocred->cap_effective)) -+ return false; -+ if (!cap_issubset(cred->cap_bset, ocred->cap_bset)) -+ return false; -+ if (!cap_issubset(cred->cap_ambient, ocred->cap_ambient)) -+ return false; -+ return true; -+} -+ -+ - /** - * aa_may_manage_policy - can the current task manage policy - * @subj_cred: subjects cred - * @label: label to check if it can manage policy - * @ns: namespace being managed by @label (may be NULL if @label's ns) -+ * @ocred: object cred if request is coming from an open object - * @mask: contains the policy manipulation operation being done - * - * Returns: 0 if the task is allowed to manipulate policy else error - */ - int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label, -- struct aa_ns *ns, u32 mask) -+ struct aa_ns *ns, const struct cred *ocred, u32 mask) - { - const char *op; - -@@ -920,6 +947,11 @@ int aa_may_manage_policy(const struct cred *subj_cred, struct aa_label *label, - return audit_policy(label, op, NULL, NULL, "policy_locked", - -EACCES); - -+ if (ocred && !is_subset_of_obj_privilege(subj_cred, label, ocred)) -+ return audit_policy(label, op, NULL, NULL, -+ "not privileged for target profile", -+ -EACCES); -+ - if (!aa_policy_admin_capable(subj_cred, label, ns)) - return audit_policy(label, op, NULL, NULL, "not policy admin", - -EACCES); --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0009-apparmor-fix-differential-encoding-verification.patch b/patches-sonic/qsa-2026-apparmor/0009-apparmor-fix-differential-encoding-verification.patch deleted file mode 100644 index 9266e2bb6..000000000 --- a/patches-sonic/qsa-2026-apparmor/0009-apparmor-fix-differential-encoding-verification.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 4bf3bc43e05ddcf84032d415861308ac5c895732 Mon Sep 17 00:00:00 2001 -From: John Johansen -Date: Fri, 17 Oct 2025 01:53:00 -0700 -Subject: [PATCH 09/11] apparmor: fix differential encoding verification - -Differential encoding allows loops to be created if it is abused. To -prevent this the unpack should verify that a diff-encode chain -terminates. - -Unfortunately the differential encode verification had two bugs. - -1. it conflated states that had gone through check and already been - marked, with states that were currently being checked and marked. - This means that loops in the current chain being verified are treated - as a chain that has already been verified. - -2. the order bailout on already checked states compared current chain - check iterators j,k instead of using the outer loop iterator i. - Meaning a step backwards in states in the current chain verification - was being mistaken for moving to an already verified state. - -Move to a double mark scheme where already verified states get a -different mark, than the current chain being kept. This enables us -to also drop the backwards verification check that was the cause of -the second error as any already verified state is already marked. - -Fixes: 031dcc8f4e84 ("apparmor: dfa add support for state differential encoding") -Reported-by: Qualys Security Advisory -Tested-by: Salvatore Bonaccorso -Reviewed-by: Georgia Garcia -Reviewed-by: Cengiz Can -Signed-off-by: John Johansen ---- - security/apparmor/include/match.h | 1 + - security/apparmor/match.c | 23 +++++++++++++++++++---- - 2 files changed, 20 insertions(+), 4 deletions(-) - -diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h -index ae31a8a631fc..a86f74b59360 100644 ---- a/security/apparmor/include/match.h -+++ b/security/apparmor/include/match.h -@@ -181,6 +181,7 @@ static inline void aa_put_dfa(struct aa_dfa *dfa) - #define MATCH_FLAG_DIFF_ENCODE 0x80000000 - #define MARK_DIFF_ENCODE 0x40000000 - #define MATCH_FLAG_OOB_TRANSITION 0x20000000 -+#define MARK_DIFF_ENCODE_VERIFIED 0x10000000 - #define MATCH_FLAGS_MASK 0xff000000 - #define MATCH_FLAGS_VALID (MATCH_FLAG_DIFF_ENCODE | MATCH_FLAG_OOB_TRANSITION) - #define MATCH_FLAGS_INVALID (MATCH_FLAGS_MASK & ~MATCH_FLAGS_VALID) -diff --git a/security/apparmor/match.c b/security/apparmor/match.c -index 98cf3b1f1649..a37a5649802b 100644 ---- a/security/apparmor/match.c -+++ b/security/apparmor/match.c -@@ -202,16 +202,31 @@ static int verify_dfa(struct aa_dfa *dfa) - size_t j, k; - - for (j = i; -- (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && -- !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE); -+ ((BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE) && -+ !(BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE_VERIFIED)); - j = k) { -+ if (BASE_TABLE(dfa)[j] & MARK_DIFF_ENCODE) -+ /* loop in current chain */ -+ goto out; - k = DEFAULT_TABLE(dfa)[j]; - if (j == k) -+ /* self loop */ - goto out; -- if (k < j) -- break; /* already verified */ - BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE; - } -+ /* move mark to verified */ -+ for (j = i; -+ (BASE_TABLE(dfa)[j] & MATCH_FLAG_DIFF_ENCODE); -+ j = k) { -+ k = DEFAULT_TABLE(dfa)[j]; -+ if (j < i) -+ /* jumps to state/chain that has been -+ * verified -+ */ -+ break; -+ BASE_TABLE(dfa)[j] &= ~MARK_DIFF_ENCODE; -+ BASE_TABLE(dfa)[j] |= MARK_DIFF_ENCODE_VERIFIED; -+ } - } - error = 0; - --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0010-apparmor-fix-race-on-rawdata-dereference.patch b/patches-sonic/qsa-2026-apparmor/0010-apparmor-fix-race-on-rawdata-dereference.patch deleted file mode 100644 index 00a52913a..000000000 --- a/patches-sonic/qsa-2026-apparmor/0010-apparmor-fix-race-on-rawdata-dereference.patch +++ /dev/null @@ -1,446 +0,0 @@ -From c88a5ec7c46387ceab1f9d550c035a76faf0f3d1 Mon Sep 17 00:00:00 2001 -From: John Johansen -Date: Tue, 24 Feb 2026 10:20:02 -0800 -Subject: [PATCH 10/11] apparmor: fix race on rawdata dereference -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -There is a race condition that leads to a use-after-free situation: -because the rawdata inodes are not refcounted, an attacker can start -open()ing one of the rawdata files, and at the same time remove the -last reference to this rawdata (by removing the corresponding profile, -for example), which frees its struct aa_loaddata; as a result, when -seq_rawdata_open() is reached, i_private is a dangling pointer and -freed memory is accessed. - -The rawdata inodes weren't refcounted to avoid a circular refcount and -were supposed to be held by the profile rawdata reference. However -during profile removal there is a window where the vfs and profile -destruction race, resulting in the use after free. - -Fix this by moving to a double refcount scheme. Where the profile -refcount on rawdata is used to break the circular dependency. Allowing -for freeing of the rawdata once all inode references to the rawdata -are put. - -Fixes: 5d5182cae401 ("apparmor: move to per loaddata files, instead of replicating in profiles") -Reported-by: Qualys Security Advisory -Reviewed-by: Georgia Garcia -Reviewed-by: Maxime Bélair -Reviewed-by: Cengiz Can -Tested-by: Salvatore Bonaccorso -Signed-off-by: John Johansen ---- - security/apparmor/apparmorfs.c | 35 ++++++----- - security/apparmor/include/policy_unpack.h | 71 ++++++++++++++--------- - security/apparmor/policy.c | 12 ++-- - security/apparmor/policy_unpack.c | 32 +++++++--- - 4 files changed, 93 insertions(+), 57 deletions(-) - -diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c -index c708f0bfe594..45f44978537e 100644 ---- a/security/apparmor/apparmorfs.c -+++ b/security/apparmor/apparmorfs.c -@@ -79,7 +79,7 @@ static void rawdata_f_data_free(struct rawdata_f_data *private) - if (!private) - return; - -- aa_put_loaddata(private->loaddata); -+ aa_put_i_loaddata(private->loaddata); - kvfree(private); - } - -@@ -404,7 +404,8 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf, - - data->size = copy_size; - if (copy_from_user(data->data, userbuf, copy_size)) { -- aa_put_loaddata(data); -+ /* trigger free - don't need to put pcount */ -+ aa_put_i_loaddata(data); - return ERR_PTR(-EFAULT); - } - -@@ -432,7 +433,10 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, - error = PTR_ERR(data); - if (!IS_ERR(data)) { - error = aa_replace_profiles(ns, label, mask, data); -- aa_put_loaddata(data); -+ /* put pcount, which will put count and free if no -+ * profiles referencing it. -+ */ -+ aa_put_profile_loaddata(data); - } - end_section: - end_current_label_crit_section(label); -@@ -503,7 +507,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, - if (!IS_ERR(data)) { - data->data[size] = 0; - error = aa_remove_profiles(ns, label, data->data, size); -- aa_put_loaddata(data); -+ aa_put_profile_loaddata(data); - } - out: - end_current_label_crit_section(label); -@@ -1242,18 +1246,17 @@ static const struct file_operations seq_rawdata_ ##NAME ##_fops = { \ - static int seq_rawdata_open(struct inode *inode, struct file *file, - int (*show)(struct seq_file *, void *)) - { -- struct aa_loaddata *data = __aa_get_loaddata(inode->i_private); -+ struct aa_loaddata *data = aa_get_i_loaddata(inode->i_private); - int error; - - if (!data) -- /* lost race this ent is being reaped */ - return -ENOENT; - - error = single_open(file, show, data); - if (error) { - AA_BUG(file->private_data && - ((struct seq_file *)file->private_data)->private); -- aa_put_loaddata(data); -+ aa_put_i_loaddata(data); - } - - return error; -@@ -1264,7 +1267,7 @@ static int seq_rawdata_release(struct inode *inode, struct file *file) - struct seq_file *seq = (struct seq_file *) file->private_data; - - if (seq) -- aa_put_loaddata(seq->private); -+ aa_put_i_loaddata(seq->private); - - return single_release(inode, file); - } -@@ -1376,9 +1379,8 @@ static int rawdata_open(struct inode *inode, struct file *file) - if (!aa_current_policy_view_capable(NULL)) - return -EACCES; - -- loaddata = __aa_get_loaddata(inode->i_private); -+ loaddata = aa_get_i_loaddata(inode->i_private); - if (!loaddata) -- /* lost race: this entry is being reaped */ - return -ENOENT; - - private = rawdata_f_data_alloc(loaddata->size); -@@ -1403,7 +1405,7 @@ static int rawdata_open(struct inode *inode, struct file *file) - return error; - - fail_private_alloc: -- aa_put_loaddata(loaddata); -+ aa_put_i_loaddata(loaddata); - return error; - } - -@@ -1420,9 +1422,9 @@ static void remove_rawdata_dents(struct aa_loaddata *rawdata) - - for (i = 0; i < AAFS_LOADDATA_NDENTS; i++) { - if (!IS_ERR_OR_NULL(rawdata->dents[i])) { -- /* no refcounts on i_private */ - aafs_remove(rawdata->dents[i]); - rawdata->dents[i] = NULL; -+ aa_put_i_loaddata(rawdata); - } - } - } -@@ -1461,18 +1463,21 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata) - if (IS_ERR(dir)) - /* ->name freed when rawdata freed */ - return PTR_ERR(dir); -+ aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_DIR] = dir; - - dent = aafs_create_file("abi", S_IFREG | 0444, dir, rawdata, - &seq_rawdata_abi_fops); - if (IS_ERR(dent)) - goto fail; -+ aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_ABI] = dent; - - dent = aafs_create_file("revision", S_IFREG | 0444, dir, rawdata, - &seq_rawdata_revision_fops); - if (IS_ERR(dent)) - goto fail; -+ aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_REVISION] = dent; - - if (aa_g_hash_policy) { -@@ -1480,6 +1485,7 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata) - rawdata, &seq_rawdata_hash_fops); - if (IS_ERR(dent)) - goto fail; -+ aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_HASH] = dent; - } - -@@ -1488,24 +1494,25 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata) - &seq_rawdata_compressed_size_fops); - if (IS_ERR(dent)) - goto fail; -+ aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent; - - dent = aafs_create_file("raw_data", S_IFREG | 0444, - dir, rawdata, &rawdata_fops); - if (IS_ERR(dent)) - goto fail; -+ aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_DATA] = dent; - d_inode(dent)->i_size = rawdata->size; - - rawdata->ns = aa_get_ns(ns); - list_add(&rawdata->list, &ns->rawdata_list); -- /* no refcount on inode rawdata */ - - return 0; - - fail: - remove_rawdata_dents(rawdata); -- -+ aa_put_i_loaddata(rawdata); - return PTR_ERR(dent); - } - #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ -diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h -index a6f4611ee50c..4f800fbb805a 100644 ---- a/security/apparmor/include/policy_unpack.h -+++ b/security/apparmor/include/policy_unpack.h -@@ -87,17 +87,29 @@ struct aa_ext { - u32 version; - }; - --/* -- * struct aa_loaddata - buffer of policy raw_data set -+/* struct aa_loaddata - buffer of policy raw_data set -+ * @count: inode/filesystem refcount - use aa_get_i_loaddata() -+ * @pcount: profile refcount - use aa_get_profile_loaddata() -+ * @list: list the loaddata is on -+ * @work: used to do a delayed cleanup -+ * @dents: refs to dents created in aafs -+ * @ns: the namespace this loaddata was loaded into -+ * @name: -+ * @size: the size of the data that was loaded -+ * @compressed_size: the size of the data when it is compressed -+ * @revision: unique revision count that this data was loaded as -+ * @abi: the abi number the loaddata uses -+ * @hash: a hash of the loaddata, used to help dedup data - * -- * there is no loaddata ref for being on ns list, nor a ref from -- * d_inode(@dentry) when grab a ref from these, @ns->lock must be held -- * && __aa_get_loaddata() needs to be used, and the return value -- * checked, if NULL the loaddata is already being reaped and should be -- * considered dead. -+ * There is no loaddata ref for being on ns->rawdata_list, so -+ * @ns->lock must be held when walking the list. Dentries and -+ * inode opens hold refs on @count; profiles hold refs on @pcount. -+ * When the last @pcount drops, do_ploaddata_rmfs() removes the -+ * fs entries and drops the associated @count ref. - */ - struct aa_loaddata { - struct kref count; -+ struct kref pcount; - struct list_head list; - struct work_struct work; - struct dentry *dents[AAFS_LOADDATA_NDENTS]; -@@ -119,52 +131,55 @@ struct aa_loaddata { - int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); - - /** -- * __aa_get_loaddata - get a reference count to uncounted data reference -+ * aa_get_loaddata - get a reference count from a counted data reference - * @data: reference to get a count on - * -- * Returns: pointer to reference OR NULL if race is lost and reference is -- * being repeated. -- * Requires: @data->ns->lock held, and the return code MUST be checked -- * -- * Use only from inode->i_private and @data->list found references -+ * Returns: pointer to reference -+ * Requires: @data to have a valid reference count on it. It is a bug -+ * if the race to reap can be encountered when it is used. - */ - static inline struct aa_loaddata * --__aa_get_loaddata(struct aa_loaddata *data) -+aa_get_i_loaddata(struct aa_loaddata *data) - { -- if (data && kref_get_unless_zero(&(data->count))) -- return data; - -- return NULL; -+ if (data) -+ kref_get(&(data->count)); -+ return data; - } - -+ - /** -- * aa_get_loaddata - get a reference count from a counted data reference -+ * aa_get_profile_loaddata - get a profile reference count on loaddata - * @data: reference to get a count on - * -- * Returns: point to reference -- * Requires: @data to have a valid reference count on it. It is a bug -- * if the race to reap can be encountered when it is used. -+ * Returns: pointer to reference -+ * Requires: @data to have a valid reference count on it. - */ - static inline struct aa_loaddata * --aa_get_loaddata(struct aa_loaddata *data) -+aa_get_profile_loaddata(struct aa_loaddata *data) - { -- struct aa_loaddata *tmp = __aa_get_loaddata(data); -- -- AA_BUG(data && !tmp); -- -- return tmp; -+ if (data) -+ kref_get(&(data->pcount)); -+ return data; - } - - void __aa_loaddata_update(struct aa_loaddata *data, long revision); - bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); - void aa_loaddata_kref(struct kref *kref); -+void aa_ploaddata_kref(struct kref *kref); - struct aa_loaddata *aa_loaddata_alloc(size_t size); --static inline void aa_put_loaddata(struct aa_loaddata *data) -+static inline void aa_put_i_loaddata(struct aa_loaddata *data) - { - if (data) - kref_put(&data->count, aa_loaddata_kref); - } - -+static inline void aa_put_profile_loaddata(struct aa_loaddata *data) -+{ -+ if (data) -+ kref_put(&data->pcount, aa_ploaddata_kref); -+} -+ - #if IS_ENABLED(CONFIG_KUNIT) - bool aa_inbounds(struct aa_ext *e, size_t size); - size_t aa_unpack_u16_chunk(struct aa_ext *e, char **chunk); -diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c -index 29f1cfd75090..b5ae0314b384 100644 ---- a/security/apparmor/policy.c -+++ b/security/apparmor/policy.c -@@ -338,7 +338,7 @@ void aa_free_profile(struct aa_profile *profile) - } - - kfree_sensitive(profile->hash); -- aa_put_loaddata(profile->rawdata); -+ aa_put_profile_loaddata(profile->rawdata); - aa_label_destroy(&profile->label); - - kfree_sensitive(profile); -@@ -1123,7 +1123,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, - LIST_HEAD(lh); - - op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD; -- aa_get_loaddata(udata); -+ aa_get_profile_loaddata(udata); - /* released below */ - error = aa_unpack(udata, &lh, &ns_name); - if (error) -@@ -1175,10 +1175,10 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, - if (aa_rawdata_eq(rawdata_ent, udata)) { - struct aa_loaddata *tmp; - -- tmp = __aa_get_loaddata(rawdata_ent); -+ tmp = aa_get_profile_loaddata(rawdata_ent); - /* check we didn't fail the race */ - if (tmp) { -- aa_put_loaddata(udata); -+ aa_put_profile_loaddata(udata); - udata = tmp; - break; - } -@@ -1191,7 +1191,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, - struct aa_profile *p; - - if (aa_g_export_binary) -- ent->new->rawdata = aa_get_loaddata(udata); -+ ent->new->rawdata = aa_get_profile_loaddata(udata); - error = __lookup_replace(ns, ent->new->base.hname, - !(mask & AA_MAY_REPLACE_POLICY), - &ent->old, &info); -@@ -1324,7 +1324,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, - - out: - aa_put_ns(ns); -- aa_put_loaddata(udata); -+ aa_put_profile_loaddata(udata); - kfree(ns_name); - - if (error) -diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c -index 0232fc29a234..36175b99914b 100644 ---- a/security/apparmor/policy_unpack.c -+++ b/security/apparmor/policy_unpack.c -@@ -108,34 +108,47 @@ bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r) - return memcmp(l->data, r->data, r->compressed_size ?: r->size) == 0; - } - -+static void do_loaddata_free(struct aa_loaddata *d) -+{ -+ kfree_sensitive(d->hash); -+ kfree_sensitive(d->name); -+ kvfree(d->data); -+ kfree_sensitive(d); -+} -+ -+void aa_loaddata_kref(struct kref *kref) -+{ -+ struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); -+ -+ do_loaddata_free(d); -+} -+ - /* - * need to take the ns mutex lock which is NOT safe most places that - * put_loaddata is called, so we have to delay freeing it - */ --static void do_loaddata_free(struct work_struct *work) -+static void do_ploaddata_rmfs(struct work_struct *work) - { - struct aa_loaddata *d = container_of(work, struct aa_loaddata, work); - struct aa_ns *ns = aa_get_ns(d->ns); - - if (ns) { - mutex_lock_nested(&ns->lock, ns->level); -+ /* remove fs ref to loaddata */ - __aa_fs_remove_rawdata(d); - mutex_unlock(&ns->lock); - aa_put_ns(ns); - } -- -- kfree_sensitive(d->hash); -- kfree_sensitive(d->name); -- kvfree(d->data); -- kfree_sensitive(d); -+ /* called by dropping last pcount, so drop its associated icount */ -+ aa_put_i_loaddata(d); - } - --void aa_loaddata_kref(struct kref *kref) -+void aa_ploaddata_kref(struct kref *kref) - { -- struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); -+ struct aa_loaddata *d = container_of(kref, struct aa_loaddata, pcount); - - if (d) { -- INIT_WORK(&d->work, do_loaddata_free); -+ INIT_WORK(&d->work, do_ploaddata_rmfs); - schedule_work(&d->work); - } - } -@@ -153,6 +166,7 @@ struct aa_loaddata *aa_loaddata_alloc(size_t size) - return ERR_PTR(-ENOMEM); - } - kref_init(&d->count); -+ kref_init(&d->pcount); - INIT_LIST_HEAD(&d->list); - - return d; --- -2.53.0 - diff --git a/patches-sonic/qsa-2026-apparmor/0011-apparmor-fix-race-between-freeing-data-and-fs-access.patch b/patches-sonic/qsa-2026-apparmor/0011-apparmor-fix-race-between-freeing-data-and-fs-access.patch deleted file mode 100644 index d82fa012e..000000000 --- a/patches-sonic/qsa-2026-apparmor/0011-apparmor-fix-race-between-freeing-data-and-fs-access.patch +++ /dev/null @@ -1,717 +0,0 @@ -From 070021c421ed59f4886aad6a3e80f636227d62e5 Mon Sep 17 00:00:00 2001 -From: John Johansen -Date: Sun, 1 Mar 2026 16:10:51 -0800 -Subject: [PATCH 11/11] apparmor: fix race between freeing data and fs - accessing it -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -AppArmor was putting the reference to i_private data on its end after -removing the original entry from the file system. However the inode -can and does live beyond that point and it is possible that some of -the fs call back functions will be invoked after the reference has -been put, which results in a race between freeing the data and -accessing it through the fs. - -While the rawdata/loaddata is the most likely candidate to fail the -race, as it has the fewest references. If properly crafted it might be -possible to trigger a race for the other types stored in i_private. - -Fix this by moving the put of i_private referenced data to the correct -place which is during inode eviction. - -Fixes: c961ee5f21b20 ("apparmor: convert from securityfs to apparmorfs for policy ns files") -Reported-by: Qualys Security Advisory -Reviewed-by: Georgia Garcia -Reviewed-by: Maxime Bélair -Reviewed-by: Cengiz Can -Signed-off-by: John Johansen ---- - security/apparmor/apparmorfs.c | 194 +++++++++++++--------- - security/apparmor/include/label.h | 16 +- - security/apparmor/include/lib.h | 12 ++ - security/apparmor/include/policy.h | 8 +- - security/apparmor/include/policy_unpack.h | 6 +- - security/apparmor/label.c | 12 +- - security/apparmor/policy_unpack.c | 6 +- - 7 files changed, 153 insertions(+), 101 deletions(-) - -diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c -index 45f44978537e..c70e1daad21a 100644 ---- a/security/apparmor/apparmorfs.c -+++ b/security/apparmor/apparmorfs.c -@@ -32,6 +32,7 @@ - #include "include/crypto.h" - #include "include/ipc.h" - #include "include/label.h" -+#include "include/lib.h" - #include "include/policy.h" - #include "include/policy_ns.h" - #include "include/resource.h" -@@ -62,6 +63,7 @@ - * securityfs and apparmorfs filesystems. - */ - -+#define IREF_POISON 101 - - /* - * support fns -@@ -153,6 +155,71 @@ static int aafs_show_path(struct seq_file *seq, struct dentry *dentry) - return 0; - } - -+static struct aa_ns *get_ns_common_ref(struct aa_common_ref *ref) -+{ -+ if (ref) { -+ struct aa_label *reflabel = container_of(ref, struct aa_label, -+ count); -+ return aa_get_ns(labels_ns(reflabel)); -+ } -+ -+ return NULL; -+} -+ -+static struct aa_proxy *get_proxy_common_ref(struct aa_common_ref *ref) -+{ -+ if (ref) -+ return aa_get_proxy(container_of(ref, struct aa_proxy, count)); -+ -+ return NULL; -+} -+ -+static struct aa_loaddata *get_loaddata_common_ref(struct aa_common_ref *ref) -+{ -+ if (ref) -+ return aa_get_i_loaddata(container_of(ref, struct aa_loaddata, -+ count)); -+ return NULL; -+} -+ -+static void aa_put_common_ref(struct aa_common_ref *ref) -+{ -+ if (!ref) -+ return; -+ -+ switch (ref->reftype) { -+ case REF_RAWDATA: -+ aa_put_i_loaddata(container_of(ref, struct aa_loaddata, -+ count)); -+ break; -+ case REF_PROXY: -+ aa_put_proxy(container_of(ref, struct aa_proxy, -+ count)); -+ break; -+ case REF_NS: -+ /* ns count is held on its unconfined label */ -+ aa_put_ns(labels_ns(container_of(ref, struct aa_label, count))); -+ break; -+ default: -+ AA_BUG(true, "unknown refcount type"); -+ break; -+ } -+} -+ -+static void aa_get_common_ref(struct aa_common_ref *ref) -+{ -+ kref_get(&ref->count); -+} -+ -+static void aafs_evict(struct inode *inode) -+{ -+ struct aa_common_ref *ref = inode->i_private; -+ -+ clear_inode(inode); -+ aa_put_common_ref(ref); -+ inode->i_private = (void *) IREF_POISON; -+} -+ - static void aafs_free_inode(struct inode *inode) - { - if (S_ISLNK(inode->i_mode)) -@@ -162,6 +229,7 @@ static void aafs_free_inode(struct inode *inode) - - static const struct super_operations aafs_super_ops = { - .statfs = simple_statfs, -+ .evict_inode = aafs_evict, - .free_inode = aafs_free_inode, - .show_path = aafs_show_path, - }; -@@ -262,7 +330,8 @@ static int __aafs_setup_d_inode(struct inode *dir, struct dentry *dentry, - * aafs_remove(). Will return ERR_PTR on failure. - */ - static struct dentry *aafs_create(const char *name, umode_t mode, -- struct dentry *parent, void *data, void *link, -+ struct dentry *parent, -+ struct aa_common_ref *data, void *link, - const struct file_operations *fops, - const struct inode_operations *iops) - { -@@ -299,6 +368,9 @@ static struct dentry *aafs_create(const char *name, umode_t mode, - goto fail_dentry; - inode_unlock(dir); - -+ if (data) -+ aa_get_common_ref(data); -+ - return dentry; - - fail_dentry: -@@ -323,7 +395,8 @@ static struct dentry *aafs_create(const char *name, umode_t mode, - * see aafs_create - */ - static struct dentry *aafs_create_file(const char *name, umode_t mode, -- struct dentry *parent, void *data, -+ struct dentry *parent, -+ struct aa_common_ref *data, - const struct file_operations *fops) - { - return aafs_create(name, mode, parent, data, NULL, fops, NULL); -@@ -448,7 +521,7 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size, - static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, - loff_t *pos) - { -- struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); -+ struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); - int error = policy_update(AA_MAY_LOAD_POLICY, buf, size, pos, ns, - f->f_cred); - -@@ -466,7 +539,7 @@ static const struct file_operations aa_fs_profile_load = { - static ssize_t profile_replace(struct file *f, const char __user *buf, - size_t size, loff_t *pos) - { -- struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); -+ struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); - int error = policy_update(AA_MAY_LOAD_POLICY | AA_MAY_REPLACE_POLICY, - buf, size, pos, ns, f->f_cred); - aa_put_ns(ns); -@@ -486,7 +559,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf, - struct aa_loaddata *data; - struct aa_label *label; - ssize_t error; -- struct aa_ns *ns = aa_get_ns(f->f_inode->i_private); -+ struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private); - - label = begin_current_label_crit_section(); - /* high level check about policy management - fine grained in -@@ -576,7 +649,7 @@ static int ns_revision_open(struct inode *inode, struct file *file) - if (!rev) - return -ENOMEM; - -- rev->ns = aa_get_ns(inode->i_private); -+ rev->ns = get_ns_common_ref(inode->i_private); - if (!rev->ns) - rev->ns = aa_get_current_ns(); - file->private_data = rev; -@@ -1054,7 +1127,7 @@ static const struct file_operations seq_profile_ ##NAME ##_fops = { \ - static int seq_profile_open(struct inode *inode, struct file *file, - int (*show)(struct seq_file *, void *)) - { -- struct aa_proxy *proxy = aa_get_proxy(inode->i_private); -+ struct aa_proxy *proxy = get_proxy_common_ref(inode->i_private); - int error = single_open(file, show, proxy); - - if (error) { -@@ -1246,7 +1319,7 @@ static const struct file_operations seq_rawdata_ ##NAME ##_fops = { \ - static int seq_rawdata_open(struct inode *inode, struct file *file, - int (*show)(struct seq_file *, void *)) - { -- struct aa_loaddata *data = aa_get_i_loaddata(inode->i_private); -+ struct aa_loaddata *data = get_loaddata_common_ref(inode->i_private); - int error; - - if (!data) -@@ -1379,7 +1452,7 @@ static int rawdata_open(struct inode *inode, struct file *file) - if (!aa_current_policy_view_capable(NULL)) - return -EACCES; - -- loaddata = aa_get_i_loaddata(inode->i_private); -+ loaddata = get_loaddata_common_ref(inode->i_private); - if (!loaddata) - return -ENOENT; - -@@ -1424,7 +1497,6 @@ static void remove_rawdata_dents(struct aa_loaddata *rawdata) - if (!IS_ERR_OR_NULL(rawdata->dents[i])) { - aafs_remove(rawdata->dents[i]); - rawdata->dents[i] = NULL; -- aa_put_i_loaddata(rawdata); - } - } - } -@@ -1463,45 +1535,41 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata) - if (IS_ERR(dir)) - /* ->name freed when rawdata freed */ - return PTR_ERR(dir); -- aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_DIR] = dir; - -- dent = aafs_create_file("abi", S_IFREG | 0444, dir, rawdata, -+ dent = aafs_create_file("abi", S_IFREG | 0444, dir, &rawdata->count, - &seq_rawdata_abi_fops); - if (IS_ERR(dent)) - goto fail; -- aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_ABI] = dent; - -- dent = aafs_create_file("revision", S_IFREG | 0444, dir, rawdata, -- &seq_rawdata_revision_fops); -+ dent = aafs_create_file("revision", S_IFREG | 0444, dir, -+ &rawdata->count, -+ &seq_rawdata_revision_fops); - if (IS_ERR(dent)) - goto fail; -- aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_REVISION] = dent; - - if (aa_g_hash_policy) { - dent = aafs_create_file("sha256", S_IFREG | 0444, dir, -- rawdata, &seq_rawdata_hash_fops); -+ &rawdata->count, -+ &seq_rawdata_hash_fops); - if (IS_ERR(dent)) - goto fail; -- aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_HASH] = dent; - } - - dent = aafs_create_file("compressed_size", S_IFREG | 0444, dir, -- rawdata, -+ &rawdata->count, - &seq_rawdata_compressed_size_fops); - if (IS_ERR(dent)) - goto fail; -- aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_COMPRESSED_SIZE] = dent; - -- dent = aafs_create_file("raw_data", S_IFREG | 0444, -- dir, rawdata, &rawdata_fops); -+ dent = aafs_create_file("raw_data", S_IFREG | 0444, dir, -+ &rawdata->count, &rawdata_fops); - if (IS_ERR(dent)) - goto fail; -- aa_get_i_loaddata(rawdata); - rawdata->dents[AAFS_LOADDATA_DATA] = dent; - d_inode(dent)->i_size = rawdata->size; - -@@ -1512,7 +1580,6 @@ int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata) - - fail: - remove_rawdata_dents(rawdata); -- aa_put_i_loaddata(rawdata); - return PTR_ERR(dent); - } - #endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ -@@ -1536,13 +1603,10 @@ void __aafs_profile_rmdir(struct aa_profile *profile) - __aafs_profile_rmdir(child); - - for (i = AAFS_PROF_SIZEOF - 1; i >= 0; --i) { -- struct aa_proxy *proxy; - if (!profile->dents[i]) - continue; - -- proxy = d_inode(profile->dents[i])->i_private; - aafs_remove(profile->dents[i]); -- aa_put_proxy(proxy); - profile->dents[i] = NULL; - } - } -@@ -1576,14 +1640,7 @@ static struct dentry *create_profile_file(struct dentry *dir, const char *name, - struct aa_profile *profile, - const struct file_operations *fops) - { -- struct aa_proxy *proxy = aa_get_proxy(profile->label.proxy); -- struct dentry *dent; -- -- dent = aafs_create_file(name, S_IFREG | 0444, dir, proxy, fops); -- if (IS_ERR(dent)) -- aa_put_proxy(proxy); -- -- return dent; -+ return aafs_create_file(name, S_IFREG | 0444, dir, &profile->label.proxy->count, fops); - } - - #ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY -@@ -1629,7 +1686,8 @@ static const char *rawdata_get_link_base(struct dentry *dentry, - struct delayed_call *done, - const char *name) - { -- struct aa_proxy *proxy = inode->i_private; -+ struct aa_common_ref *ref = inode->i_private; -+ struct aa_proxy *proxy = container_of(ref, struct aa_proxy, count); - struct aa_label *label; - struct aa_profile *profile; - char *target; -@@ -1762,27 +1820,24 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) - if (profile->rawdata) { - if (aa_g_hash_policy) { - dent = aafs_create("raw_sha256", S_IFLNK | 0444, dir, -- profile->label.proxy, NULL, NULL, -- &rawdata_link_sha256_iops); -+ &profile->label.proxy->count, NULL, -+ NULL, &rawdata_link_sha256_iops); - if (IS_ERR(dent)) - goto fail; -- aa_get_proxy(profile->label.proxy); - profile->dents[AAFS_PROF_RAW_HASH] = dent; - } - dent = aafs_create("raw_abi", S_IFLNK | 0444, dir, -- profile->label.proxy, NULL, NULL, -+ &profile->label.proxy->count, NULL, NULL, - &rawdata_link_abi_iops); - if (IS_ERR(dent)) - goto fail; -- aa_get_proxy(profile->label.proxy); - profile->dents[AAFS_PROF_RAW_ABI] = dent; - - dent = aafs_create("raw_data", S_IFLNK | 0444, dir, -- profile->label.proxy, NULL, NULL, -+ &profile->label.proxy->count, NULL, NULL, - &rawdata_link_data_iops); - if (IS_ERR(dent)) - goto fail; -- aa_get_proxy(profile->label.proxy); - profile->dents[AAFS_PROF_RAW_DATA] = dent; - } - #endif /*CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ -@@ -1819,7 +1874,7 @@ static int ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir, - if (error) - return error; - -- parent = aa_get_ns(dir->i_private); -+ parent = get_ns_common_ref(dir->i_private); - AA_BUG(d_inode(ns_subns_dir(parent)) != dir); - - /* we have to unlock and then relock to get locking order right -@@ -1869,7 +1924,7 @@ static int ns_rmdir_op(struct inode *dir, struct dentry *dentry) - if (error) - return error; - -- parent = aa_get_ns(dir->i_private); -+ parent = get_ns_common_ref(dir->i_private); - /* rmdir calls the generic securityfs functions to remove files - * from the apparmor dir. It is up to the apparmor ns locking - * to avoid races. -@@ -1939,27 +1994,6 @@ void __aafs_ns_rmdir(struct aa_ns *ns) - - __aa_fs_list_remove_rawdata(ns); - -- if (ns_subns_dir(ns)) { -- sub = d_inode(ns_subns_dir(ns))->i_private; -- aa_put_ns(sub); -- } -- if (ns_subload(ns)) { -- sub = d_inode(ns_subload(ns))->i_private; -- aa_put_ns(sub); -- } -- if (ns_subreplace(ns)) { -- sub = d_inode(ns_subreplace(ns))->i_private; -- aa_put_ns(sub); -- } -- if (ns_subremove(ns)) { -- sub = d_inode(ns_subremove(ns))->i_private; -- aa_put_ns(sub); -- } -- if (ns_subrevision(ns)) { -- sub = d_inode(ns_subrevision(ns))->i_private; -- aa_put_ns(sub); -- } -- - for (i = AAFS_NS_SIZEOF - 1; i >= 0; --i) { - aafs_remove(ns->dents[i]); - ns->dents[i] = NULL; -@@ -1984,40 +2018,40 @@ static int __aafs_ns_mkdir_entries(struct aa_ns *ns, struct dentry *dir) - return PTR_ERR(dent); - ns_subdata_dir(ns) = dent; - -- dent = aafs_create_file("revision", 0444, dir, ns, -+ dent = aafs_create_file("revision", 0444, dir, -+ &ns->unconfined->label.count, - &aa_fs_ns_revision_fops); - if (IS_ERR(dent)) - return PTR_ERR(dent); -- aa_get_ns(ns); - ns_subrevision(ns) = dent; - -- dent = aafs_create_file(".load", 0640, dir, ns, -- &aa_fs_profile_load); -+ dent = aafs_create_file(".load", 0640, dir, -+ &ns->unconfined->label.count, -+ &aa_fs_profile_load); - if (IS_ERR(dent)) - return PTR_ERR(dent); -- aa_get_ns(ns); - ns_subload(ns) = dent; - -- dent = aafs_create_file(".replace", 0640, dir, ns, -- &aa_fs_profile_replace); -+ dent = aafs_create_file(".replace", 0640, dir, -+ &ns->unconfined->label.count, -+ &aa_fs_profile_replace); - if (IS_ERR(dent)) - return PTR_ERR(dent); -- aa_get_ns(ns); - ns_subreplace(ns) = dent; - -- dent = aafs_create_file(".remove", 0640, dir, ns, -- &aa_fs_profile_remove); -+ dent = aafs_create_file(".remove", 0640, dir, -+ &ns->unconfined->label.count, -+ &aa_fs_profile_remove); - if (IS_ERR(dent)) - return PTR_ERR(dent); -- aa_get_ns(ns); - ns_subremove(ns) = dent; - - /* use create_dentry so we can supply private data */ -- dent = aafs_create("namespaces", S_IFDIR | 0755, dir, ns, NULL, NULL, -- &ns_dir_inode_operations); -+ dent = aafs_create("namespaces", S_IFDIR | 0755, dir, -+ &ns->unconfined->label.count, -+ NULL, NULL, &ns_dir_inode_operations); - if (IS_ERR(dent)) - return PTR_ERR(dent); -- aa_get_ns(ns); - ns_subns_dir(ns) = dent; - - return 0; -diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h -index 2a72e6b17d68..5aca0f612f8f 100644 ---- a/security/apparmor/include/label.h -+++ b/security/apparmor/include/label.h -@@ -101,7 +101,7 @@ enum label_flags { - - struct aa_label; - struct aa_proxy { -- struct kref count; -+ struct aa_common_ref count; - struct aa_label __rcu *label; - }; - -@@ -121,7 +121,7 @@ struct label_it { - * @ent: set of profiles for label, actual size determined by @size - */ - struct aa_label { -- struct kref count; -+ struct aa_common_ref count; - struct rb_node node; - struct rcu_head rcu; - struct aa_proxy *proxy; -@@ -373,7 +373,7 @@ int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules, - */ - static inline struct aa_label *__aa_get_label(struct aa_label *l) - { -- if (l && kref_get_unless_zero(&l->count)) -+ if (l && kref_get_unless_zero(&l->count.count)) - return l; - - return NULL; -@@ -382,7 +382,7 @@ static inline struct aa_label *__aa_get_label(struct aa_label *l) - static inline struct aa_label *aa_get_label(struct aa_label *l) - { - if (l) -- kref_get(&(l->count)); -+ kref_get(&(l->count.count)); - - return l; - } -@@ -402,7 +402,7 @@ static inline struct aa_label *aa_get_label_rcu(struct aa_label __rcu **l) - rcu_read_lock(); - do { - c = rcu_dereference(*l); -- } while (c && !kref_get_unless_zero(&c->count)); -+ } while (c && !kref_get_unless_zero(&c->count.count)); - rcu_read_unlock(); - - return c; -@@ -442,7 +442,7 @@ static inline struct aa_label *aa_get_newest_label(struct aa_label *l) - static inline void aa_put_label(struct aa_label *l) - { - if (l) -- kref_put(&l->count, aa_label_kref); -+ kref_put(&l->count.count, aa_label_kref); - } - - -@@ -452,7 +452,7 @@ void aa_proxy_kref(struct kref *kref); - static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy) - { - if (proxy) -- kref_get(&(proxy->count)); -+ kref_get(&(proxy->count.count)); - - return proxy; - } -@@ -460,7 +460,7 @@ static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy) - static inline void aa_put_proxy(struct aa_proxy *proxy) - { - if (proxy) -- kref_put(&proxy->count, aa_proxy_kref); -+ kref_put(&proxy->count.count, aa_proxy_kref); - } - - void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new); -diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h -index 1ec00113a056..e76470c8b84c 100644 ---- a/security/apparmor/include/lib.h -+++ b/security/apparmor/include/lib.h -@@ -71,6 +71,18 @@ void aa_info_message(const char *str); - /* Security blob offsets */ - extern struct lsm_blob_sizes apparmor_blob_sizes; - -+enum reftype { -+ REF_NS, -+ REF_PROXY, -+ REF_RAWDATA, -+}; -+ -+/* common reference count used by data the shows up in aafs */ -+struct aa_common_ref { -+ struct kref count; -+ enum reftype reftype; -+}; -+ - /** - * aa_strneq - compare null terminated @str to a non null terminated substring - * @str: a null terminated string -diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h -index b8c35972883c..dbd6af6034f1 100644 ---- a/security/apparmor/include/policy.h -+++ b/security/apparmor/include/policy.h -@@ -329,7 +329,7 @@ static inline aa_state_t ANY_RULE_MEDIATES(struct list_head *head, - static inline struct aa_profile *aa_get_profile(struct aa_profile *p) - { - if (p) -- kref_get(&(p->label.count)); -+ kref_get(&(p->label.count.count)); - - return p; - } -@@ -343,7 +343,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p) - */ - static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) - { -- if (p && kref_get_unless_zero(&p->label.count)) -+ if (p && kref_get_unless_zero(&p->label.count.count)) - return p; - - return NULL; -@@ -363,7 +363,7 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) - rcu_read_lock(); - do { - c = rcu_dereference(*p); -- } while (c && !kref_get_unless_zero(&c->label.count)); -+ } while (c && !kref_get_unless_zero(&c->label.count.count)); - rcu_read_unlock(); - - return c; -@@ -376,7 +376,7 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) - static inline void aa_put_profile(struct aa_profile *p) - { - if (p) -- kref_put(&p->label.count, aa_label_kref); -+ kref_put(&p->label.count.count, aa_label_kref); - } - - static inline int AUDIT_MODE(struct aa_profile *profile) -diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h -index 4f800fbb805a..e5a95dc4da1f 100644 ---- a/security/apparmor/include/policy_unpack.h -+++ b/security/apparmor/include/policy_unpack.h -@@ -108,7 +108,7 @@ struct aa_ext { - * fs entries and drops the associated @count ref. - */ - struct aa_loaddata { -- struct kref count; -+ struct aa_common_ref count; - struct kref pcount; - struct list_head list; - struct work_struct work; -@@ -143,7 +143,7 @@ aa_get_i_loaddata(struct aa_loaddata *data) - { - - if (data) -- kref_get(&(data->count)); -+ kref_get(&(data->count.count)); - return data; - } - -@@ -171,7 +171,7 @@ struct aa_loaddata *aa_loaddata_alloc(size_t size); - static inline void aa_put_i_loaddata(struct aa_loaddata *data) - { - if (data) -- kref_put(&data->count, aa_loaddata_kref); -+ kref_put(&data->count.count, aa_loaddata_kref); - } - - static inline void aa_put_profile_loaddata(struct aa_loaddata *data) -diff --git a/security/apparmor/label.c b/security/apparmor/label.c -index c71e4615dd46..e8e5baaf05fa 100644 ---- a/security/apparmor/label.c -+++ b/security/apparmor/label.c -@@ -52,7 +52,8 @@ static void free_proxy(struct aa_proxy *proxy) - - void aa_proxy_kref(struct kref *kref) - { -- struct aa_proxy *proxy = container_of(kref, struct aa_proxy, count); -+ struct aa_proxy *proxy = container_of(kref, struct aa_proxy, -+ count.count); - - free_proxy(proxy); - } -@@ -63,7 +64,8 @@ struct aa_proxy *aa_alloc_proxy(struct aa_label *label, gfp_t gfp) - - new = kzalloc(sizeof(struct aa_proxy), gfp); - if (new) { -- kref_init(&new->count); -+ kref_init(&new->count.count); -+ new->count.reftype = REF_PROXY; - rcu_assign_pointer(new->label, aa_get_label(label)); - } - return new; -@@ -371,7 +373,8 @@ static void label_free_rcu(struct rcu_head *head) - - void aa_label_kref(struct kref *kref) - { -- struct aa_label *label = container_of(kref, struct aa_label, count); -+ struct aa_label *label = container_of(kref, struct aa_label, -+ count.count); - struct aa_ns *ns = labels_ns(label); - - if (!ns) { -@@ -408,7 +411,8 @@ bool aa_label_init(struct aa_label *label, int size, gfp_t gfp) - - label->size = size; /* doesn't include null */ - label->vec[size] = NULL; /* null terminate */ -- kref_init(&label->count); -+ kref_init(&label->count.count); -+ label->count.reftype = REF_NS; /* for aafs purposes */ - RB_CLEAR_NODE(&label->node); - - return true; -diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c -index 36175b99914b..b14ead4075ae 100644 ---- a/security/apparmor/policy_unpack.c -+++ b/security/apparmor/policy_unpack.c -@@ -118,7 +118,8 @@ static void do_loaddata_free(struct aa_loaddata *d) - - void aa_loaddata_kref(struct kref *kref) - { -- struct aa_loaddata *d = container_of(kref, struct aa_loaddata, count); -+ struct aa_loaddata *d = container_of(kref, struct aa_loaddata, -+ count.count); - - do_loaddata_free(d); - } -@@ -165,7 +166,8 @@ struct aa_loaddata *aa_loaddata_alloc(size_t size) - kfree(d); - return ERR_PTR(-ENOMEM); - } -- kref_init(&d->count); -+ kref_init(&d->count.count); -+ d->count.reftype = REF_RAWDATA; - kref_init(&d->pcount); - INIT_LIST_HEAD(&d->list); - --- -2.53.0 - diff --git a/patches-sonic/series b/patches-sonic/series index 5dad7726b..db1ce734a 100644 --- a/patches-sonic/series +++ b/patches-sonic/series @@ -98,7 +98,7 @@ driver-i2c-smbus-add-disable_spd-module-parameter.patch 0027-platform-mellanox-mlx-platform-Change-register-name.patch 0028-platform-mellanox-mlxreg-dpu-Add-initial-support-for.patch 0029-platform-mellanox-Introduce-support-of-Nvidia-smart-.patch -0030-hwmon-mlxreg-fan-Separate-methods-of-fan-setting-com.patch +#0030-hwmon-mlxreg-fan-Separate-methods-of-fan-setting-com.patch 0031-platform-mellanox-indicate-deferred-I2C-bus-creation.patch 0032-platform_data-mlxreg-Add-capability-bit-and-mask-fie.patch 0033-platform-mellanox-mlxreg-hotplug-Add-support-for-new.patch @@ -115,7 +115,7 @@ driver-i2c-smbus-add-disable_spd-module-parameter.patch 0044-platform_data-mlxreg-Add-non-sticky-bit-indication-f.patch 0045-platform-mellanox-mlxreg-hotplug-Add-handling-of-non.patch 0046-platform-mellanox-nvsw-bmc-Add-system-control-and-mo.patch -0047-hwmon-mlxreg-fan-Prevent-fans-from-getting-stuck-at-.patch +#0047-hwmon-mlxreg-fan-Prevent-fans-from-getting-stuck-at-.patch 0052-platform-mellanox-Introduce-support-for-switches-equ.patch 0053-platform-mellanox-mlx-platform-Add-support-for-new-X.patch 0054-i2c-asf-Introduce-MCTP-support-over-ASF-controller.patch @@ -148,11 +148,9 @@ driver-i2c-smbus-add-disable_spd-module-parameter.patch # Cisco patches for 5.10 kernel cisco-mtd-part.patch cisco-mdio-mux-support-acpi.patch -#cisco-x86-gpio-config.patch # Deprecated, GPIOs are effectively unlimited cisco-acpi-spi-nor.patch cisco-Enable-static-memory-reservation-for-OIRable-PCIe-de.patch cisco-npu-disable-other-bars.patch -#cisco-hwmon-pmbus_core-pec-support-check.patch # Upstreamed 0001-regmap-i2c-add-SMBus-byte-word-reg16-bus-for-adapter.patch 0002-regmap-i2c-fix-sparse-warning-in-regmap_smbus_word_w.patch @@ -162,12 +160,7 @@ cisco-npu-disable-other-bars.patch # # Marvell platform patches for 6.1 kernel 0001-armhf_secondary_boot_online.patch -#0002-arm64-dts-Update-cache-properties-for-marvell.patch # Upstreamed -#0003-arm64-dts-marvell-Add-NAND-flash-controller-to-AC5.patch # Upstreamed -#0004-arm64-dts-ac5-add-mmc-node-and-clock.patch # Upstreamed 0005-dts-ac5-marvell-Add-switching-watchdog-node.patch -#0006-mmc-xenon-Add-ac5-support-via-bounce-buffer.patch # Upstreamed -#0007-usb-ehci-Add-support-for-ac5.patch # Upstreamed but no 64-bit, so the next 0007 0007-usb-ehci-Add-support-for-ac5-with-DMA-mask-64-bit.patch 0008-mv6xxx-Fix-i2c-lock-due-to-arb-loss.patch 0009-dt-bindings-marvell-Add-ARMADA-7K-properties.patch @@ -214,7 +207,7 @@ cisco-npu-disable-other-bars.patch 0027-pcie-if-hotplug-enabled-do-immediate-reset-on-panic.patch # amd sfh hid -0001-amd_sfh_hid_client_init_log_no_error_for_EOPNOTSUPP.patch +#0001-amd_sfh_hid_client_init_log_no_error_for_EOPNOTSUPP.patch # Security patch #0001-Change-the-system.map-file-permission-only-readable-.patch @@ -230,7 +223,7 @@ cisco-npu-disable-other-bars.patch 0004-xilinx-spi-platform_get_irq_optional.patch 0005-xilinx-spi-use-device-property.patch driver-tty-serial-8250-add-support-for-fintek-F81214E.patch # Linux upstream pending -0001-PM-runtime-Add-new-devm-functions.patch +#0001-PM-runtime-Add-new-devm-functions.patch 0007-i2c-xiic-Relocate-xiic_i2c_runtime_suspend-and-xiic_.patch 0008-i2c-xiic-Add-atomic-transfer-support.patch 0009-i2c-i2c-xiic-Replace-dev_err-with-dev_err_probe-in-p.patch @@ -242,10 +235,6 @@ driver-tty-serial-8250-add-support-for-fintek-F81214E.patch # Linux upstream pen 0015-i2c-xiic-use-numbered-adapter-registration.patch 0016-i2c-xiic-skip-input-clock-setup-on-non-OF-systems.patch -# Fix to avoid kernel panic on Kernel 6.1.94 -# https://github.com/sonic-net/sonic-buildimage/issues/20901 -#PCI-ASPM-Fix-link-state-exit-during-switch-upstream.patch # Upstreamed - # 0001-PCI-AER-Simplify-pci_print_aer.patch 0002-PCI-AER-Update-statistics-before-ratelimiting.patch @@ -260,18 +249,6 @@ driver-tty-serial-8250-add-support-for-fintek-F81214E.patch # Linux upstream pen 0011-PCI-AER-Fix-NULL-pointer-access-by-aer_info.patch 0012-PCI-AER-Avoid-NULL-pointer-dereference-in-aer_rateli.patch -# QID-45097: Crackarmor fixes -qsa-2026-apparmor/0001-apparmor-validate-DFA-start-states-are-in-bounds-in-.patch -qsa-2026-apparmor/0002-apparmor-fix-memory-leak-in-verify_header.patch -qsa-2026-apparmor/0003-apparmor-replace-recursive-profile-removal-with-iter.patch -qsa-2026-apparmor/0004-apparmor-fix-limit-the-number-of-levels-of-policy-na.patch -qsa-2026-apparmor/0005-apparmor-fix-side-effect-bug-in-match_char-macro-usa.patch -qsa-2026-apparmor/0006-apparmor-fix-missing-bounds-check-on-DEFAULT-table-i.patch -qsa-2026-apparmor/0007-apparmor-Fix-double-free-of-ns_name-in-aa_replace_pr.patch -qsa-2026-apparmor/0008-apparmor-fix-unprivileged-local-user-can-do-privileg.patch -qsa-2026-apparmor/0009-apparmor-fix-differential-encoding-verification.patch -qsa-2026-apparmor/0010-apparmor-fix-race-on-rawdata-dereference.patch -qsa-2026-apparmor/0011-apparmor-fix-race-between-freeing-data-and-fs-access.patch # Patches for EVPN MH 0001-vxlan-bridge-Add-NDA_FLAGS_EXT-support-with-NTF_EXT_.patch 0002-net-bridge-vxlan-Protocol-field-in-bridge-fdb.patch @@ -285,8 +262,6 @@ qsa-2026-apparmor/0011-apparmor-fix-race-between-freeing-data-and-fs-access.patc ############################################################ ###-> aspeed aspeed-ast2700-support.patch -mmc-sdhci-of-aspeed-Improve-CMD6-timing_v6.18.patch -mmc-sdhci-of-aspeed-Optimize-tuning-mechanism_v6.12.patch nexthop-b27-dts.patch 0001-Add-device-tree-for-Nokia-BMC-H6-128-platform.patch arista_goldfinch-dts.patch @@ -296,13 +271,13 @@ micas-m2-w6950-128oc-dts.patch ###-> aspeed-end ###-> nvidia_aspeed_bmc-start -0001-mctp-driver-net-Extend-MCTP-support.patch -0002-mctp-aspeed-IRoT-Add-initial-support.patch -0003-mctp-driver-net-Add-global-APIs-for-MCTP.patch -0004-mctp-driver-net-Fix-MCTP-over-VDM-PCIe.patch -0005-mctp-driver-net-Fix-MCTP-over-USB.patch -0006-jtag-Add-JTAG-core-headers-GPIO-mux-and-locking-support.patch -0007-JTAG-Aspeed-Fix-kernel-configuration.patch +#0001-mctp-driver-net-Extend-MCTP-support.patch +#0002-mctp-aspeed-IRoT-Add-initial-support.patch +#0003-mctp-driver-net-Add-global-APIs-for-MCTP.patch +#0004-mctp-driver-net-Fix-MCTP-over-VDM-PCIe.patch +#0005-mctp-driver-net-Fix-MCTP-over-USB.patch +#0006-jtag-Add-JTAG-core-headers-GPIO-mux-and-locking-support.patch +#0007-JTAG-Aspeed-Fix-kernel-configuration.patch ###-> nvidia_aspeed_bmc-end #