From 1e240f60a501874bccd3602de571c0a46f50cc7d Mon Sep 17 00:00:00 2001 From: Tony Butler Date: Sun, 27 Nov 2022 19:14:48 -0800 Subject: [PATCH 001/110] kernel: modules/lib-lz4: add lz4hc_compress enable option `CONFIG_CRYPTO_LZ4HC` to match default kernel config this only adds the `lz4hc_compress` module, and has no effect on the `lz4_decompress` module which already supports any flavor Signed-off-by: Tony Butler --- package/kernel/linux/modules/lib.mk | 4 +++- package/kernel/linux/modules/other.mk | 4 ++++ target/linux/generic/config-5.15 | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package/kernel/linux/modules/lib.mk b/package/kernel/linux/modules/lib.mk index 81ceb1f76ce..d4d44ad8f0a 100644 --- a/package/kernel/linux/modules/lib.mk +++ b/package/kernel/linux/modules/lib.mk @@ -152,13 +152,15 @@ define KernelPackage/lib-lz4 DEPENDS:=+kmod-crypto-acompress KCONFIG:= \ CONFIG_CRYPTO_LZ4 \ + CONFIG_CRYPTO_LZ4HC \ CONFIG_LZ4_COMPRESS \ CONFIG_LZ4_DECOMPRESS FILES:= \ $(LINUX_DIR)/crypto/lz4.ko \ $(LINUX_DIR)/lib/lz4/lz4_compress.ko \ + $(LINUX_DIR)/lib/lz4/lz4hc_compress.ko \ $(LINUX_DIR)/lib/lz4/lz4_decompress.ko - AUTOLOAD:=$(call AutoProbe,lz4 lz4_compress lz4_decompress) + AUTOLOAD:=$(call AutoProbe,lz4 lz4_compress lz4hc_compress lz4_decompress) endef define KernelPackage/lib-lz4/description diff --git a/package/kernel/linux/modules/other.mk b/package/kernel/linux/modules/other.mk index 58dd2d6c25f..c5f944ed319 100644 --- a/package/kernel/linux/modules/other.mk +++ b/package/kernel/linux/modules/other.mk @@ -1010,6 +1010,10 @@ define KernelPackage/zram/config bool "lz4" select PACKAGE_kmod-lib-lz4 + config ZRAM_DEF_COMP_LZ4HC + bool "lz4-hc" + select PACKAGE_kmod-lib-lz4hc + config ZRAM_DEF_COMP_ZSTD bool "zstd" select PACKAGE_kmod-lib-zstd diff --git a/target/linux/generic/config-5.15 b/target/linux/generic/config-5.15 index b3541ab95bf..52aba2fa0e6 100644 --- a/target/linux/generic/config-5.15 +++ b/target/linux/generic/config-5.15 @@ -7547,6 +7547,7 @@ CONFIG_ZONE_DMA=y # CONFIG_ZPOOL is not set # CONFIG_ZRAM is not set # CONFIG_ZRAM_DEF_COMP_LZ4 is not set +# CONFIG_ZRAM_DEF_COMP_LZ4HC is not set # CONFIG_ZRAM_DEF_COMP_LZO is not set # CONFIG_ZRAM_DEF_COMP_LZORLE is not set # CONFIG_ZRAM_DEF_COMP_ZSTD is not set From 19b3b14e54ca30848cab5bdd9bf441b25ec025de Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 1 Oct 2022 23:47:53 +0200 Subject: [PATCH 002/110] sunxi: fix 253-sunxi-h5-add-support-for-nanopi-r1s-h5 patch offset This showed up in the log: |Hunk #1 succeeded at 555 (offset -83 lines). Signed-off-by: Christian Lamparter --- .../patches/253-sunxi-h5-add-support-for-nanopi-r1s-h5.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/boot/uboot-sunxi/patches/253-sunxi-h5-add-support-for-nanopi-r1s-h5.patch b/package/boot/uboot-sunxi/patches/253-sunxi-h5-add-support-for-nanopi-r1s-h5.patch index b89351d2e90..c6a8cd70d18 100644 --- a/package/boot/uboot-sunxi/patches/253-sunxi-h5-add-support-for-nanopi-r1s-h5.patch +++ b/package/boot/uboot-sunxi/patches/253-sunxi-h5-add-support-for-nanopi-r1s-h5.patch @@ -29,7 +29,7 @@ diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index b8a382d1539..ed3d360bb10 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile -@@ -638,6 +638,7 @@ dtb-$(CONFIG_MACH_SUN50I_H5) += \ +@@ -555,6 +555,7 @@ dtb-$(CONFIG_MACH_SUN50I_H5) += \ sun50i-h5-libretech-all-h5-cc.dtb \ sun50i-h5-nanopi-neo2.dtb \ sun50i-h5-nanopi-neo-plus2.dtb \ From a125d46a287061f0a5a81c9009880b957212d74e Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Thu, 1 Sep 2022 23:18:19 +0800 Subject: [PATCH 003/110] sunxi: modules: remove useless AddDepends/rtc AddDepends/rtc has been removed in commit 3da7fe6, same here. Fixes: 3da7fe6 ("kernel: remove useless AddDepends/rtc abstraction") Signed-off-by: Chukun Pan (RTC_SUPPORT - though this is implied by the TARGET_sunxi) Signed-off-by: Christian Lamparter --- target/linux/sunxi/modules.mk | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/target/linux/sunxi/modules.mk b/target/linux/sunxi/modules.mk index 99f4db76044..0c8ed83d0d8 100644 --- a/target/linux/sunxi/modules.mk +++ b/target/linux/sunxi/modules.mk @@ -5,8 +5,7 @@ define KernelPackage/rtc-sunxi SUBMENU:=$(OTHER_MENU) TITLE:=Sunxi SoC built-in RTC support - DEPENDS:=@TARGET_sunxi - $(call AddDepends/rtc) + DEPENDS:=@(TARGET_sunxi&&RTC_SUPPORT) KCONFIG:= \ CONFIG_RTC_DRV_SUNXI \ CONFIG_RTC_CLASS=y @@ -23,8 +22,7 @@ $(eval $(call KernelPackage,rtc-sunxi)) define KernelPackage/sunxi-ir SUBMENU:=$(OTHER_MENU) TITLE:=Sunxi SoC built-in IR support (A20) - DEPENDS:=@TARGET_sunxi +kmod-input-core - $(call AddDepends/rtc) + DEPENDS:=@(TARGET_sunxi&&RTC_SUPPORT) +kmod-input-core KCONFIG:= \ CONFIG_MEDIA_SUPPORT=y \ CONFIG_MEDIA_RC_SUPPORT=y \ From b40a047f1fefa086826b3c65fa1ed34cc8a4a3ed Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Fri, 2 Sep 2022 23:18:10 +0800 Subject: [PATCH 004/110] sunxi: fix sunxi-ir kconfig and description Removed a20 in description, since it only works for a10, a13 and a31. Also refreshed kernel config. Fixes: #10466 Signed-off-by: Chukun Pan (moved fixes, refreshed, added CONFIG_DVB_USB=n) Signed-off-by: Christian Lamparter --- target/linux/generic/config-5.10 | 6 ++++++ target/linux/generic/config-5.15 | 6 ++++++ target/linux/sunxi/modules.mk | 5 +++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/target/linux/generic/config-5.10 b/target/linux/generic/config-5.10 index 722b7578f8d..ebf30fdc26e 100644 --- a/target/linux/generic/config-5.10 +++ b/target/linux/generic/config-5.10 @@ -1721,6 +1721,7 @@ CONFIG_DVB_MAX_ADAPTERS=16 # CONFIG_DVB_TUNER_DIB0090 is not set # CONFIG_DVB_TUNER_ITD1000 is not set # CONFIG_DVB_ULE_DEBUG is not set +# CONFIG_DVB_USB is not set # CONFIG_DVB_USB_V2 is not set # CONFIG_DVB_VES1820 is not set # CONFIG_DVB_VES1X93 is not set @@ -2793,8 +2794,11 @@ CONFIG_IP_VS_MH_TAB_INDEX=10 # CONFIG_IR_RC5_DECODER is not set # CONFIG_IR_RC6_DECODER is not set # CONFIG_IR_REDRAT3 is not set +# CONFIG_IR_SERIAL is not set +# CONFIG_IR_SIR is not set # CONFIG_IR_SONY_DECODER is not set # CONFIG_IR_STREAMZAP is not set +# CONFIG_IR_TOY is not set # CONFIG_IR_TTUSBIR is not set # CONFIG_ISA_BUS is not set # CONFIG_ISA_BUS_API is not set @@ -3060,6 +3064,7 @@ CONFIG_LIB_MEMNEQ=y CONFIG_LINEAR_RANGES=y # CONFIG_LIQUIDIO is not set # CONFIG_LIQUIDIO_VF is not set +# CONFIG_LIRC is not set # CONFIG_LIS3L02DQ is not set # CONFIG_LKDTM is not set CONFIG_LLC=y @@ -4716,6 +4721,7 @@ CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY=3 # CONFIG_RC_DECODERS is not set # CONFIG_RC_LOOPBACK is not set # CONFIG_RC_MAP is not set +# CONFIG_RC_XBOX_DVD is not set # CONFIG_RDS is not set # CONFIG_RD_BZIP2 is not set # CONFIG_RD_GZIP is not set diff --git a/target/linux/generic/config-5.15 b/target/linux/generic/config-5.15 index 52aba2fa0e6..304d99c19cf 100644 --- a/target/linux/generic/config-5.15 +++ b/target/linux/generic/config-5.15 @@ -1797,6 +1797,7 @@ CONFIG_DVB_MAX_ADAPTERS=16 # CONFIG_DVB_TUNER_DIB0090 is not set # CONFIG_DVB_TUNER_ITD1000 is not set # CONFIG_DVB_ULE_DEBUG is not set +# CONFIG_DVB_USB is not set # CONFIG_DVB_USB_V2 is not set # CONFIG_DVB_VES1820 is not set # CONFIG_DVB_VES1X93 is not set @@ -2903,8 +2904,11 @@ CONFIG_IP_VS_MH_TAB_INDEX=10 # CONFIG_IR_RC5_DECODER is not set # CONFIG_IR_RC6_DECODER is not set # CONFIG_IR_REDRAT3 is not set +# CONFIG_IR_SERIAL is not set +# CONFIG_IR_SIR is not set # CONFIG_IR_SONY_DECODER is not set # CONFIG_IR_STREAMZAP is not set +# CONFIG_IR_TOY is not set # CONFIG_IR_TTUSBIR is not set # CONFIG_ISA_BUS is not set # CONFIG_ISA_BUS_API is not set @@ -3173,6 +3177,7 @@ CONFIG_LIB_MEMNEQ=y CONFIG_LINEAR_RANGES=y # CONFIG_LIQUIDIO is not set # CONFIG_LIQUIDIO_VF is not set +# CONFIG_LIRC is not set # CONFIG_LIS3L02DQ is not set # CONFIG_LITEX_LITEETH is not set # CONFIG_LITEX_SOC_CONTROLLER is not set @@ -4945,6 +4950,7 @@ CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY=3 # CONFIG_RC_DECODERS is not set # CONFIG_RC_LOOPBACK is not set # CONFIG_RC_MAP is not set +# CONFIG_RC_XBOX_DVD is not set # CONFIG_RDS is not set # CONFIG_RD_BZIP2 is not set # CONFIG_RD_GZIP is not set diff --git a/target/linux/sunxi/modules.mk b/target/linux/sunxi/modules.mk index 0c8ed83d0d8..8bbafe98a7b 100644 --- a/target/linux/sunxi/modules.mk +++ b/target/linux/sunxi/modules.mk @@ -21,19 +21,20 @@ $(eval $(call KernelPackage,rtc-sunxi)) define KernelPackage/sunxi-ir SUBMENU:=$(OTHER_MENU) - TITLE:=Sunxi SoC built-in IR support (A20) + TITLE:=Sunxi SoC built-in IR support DEPENDS:=@(TARGET_sunxi&&RTC_SUPPORT) +kmod-input-core KCONFIG:= \ CONFIG_MEDIA_SUPPORT=y \ CONFIG_MEDIA_RC_SUPPORT=y \ CONFIG_RC_DEVICES=y \ + CONFIG_RC_CORE=y \ CONFIG_IR_SUNXI FILES:=$(LINUX_DIR)/drivers/media/rc/sunxi-cir.ko AUTOLOAD:=$(call AutoLoad,50,sunxi-cir) endef define KernelPackage/sunxi-ir/description - Support for the AllWinner sunXi SoC's onboard IR (A20) + Support for the AllWinner sunXi SoC's onboard IR endef $(eval $(call KernelPackage,sunxi-ir)) From f6c61571fb31a2917cdc7a68040a4bdc851e9112 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 12 Nov 2022 23:01:33 +0100 Subject: [PATCH 005/110] bcm53xx: remove MR32's openwrt specific patch for LEDs The patch managed to sneak into v5.15 whereas it was properly deleted for v5.10. Fixes: 8f6e2bb17886 ("bcm53xx: remove MR32's specific get_leds_dt code") Signed-off-by: Christian Lamparter --- .../331-Meraki-MR32-Status-LEDs.patch | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 target/linux/bcm53xx/patches-5.15/331-Meraki-MR32-Status-LEDs.patch diff --git a/target/linux/bcm53xx/patches-5.15/331-Meraki-MR32-Status-LEDs.patch b/target/linux/bcm53xx/patches-5.15/331-Meraki-MR32-Status-LEDs.patch deleted file mode 100644 index 92a71e58fe8..00000000000 --- a/target/linux/bcm53xx/patches-5.15/331-Meraki-MR32-Status-LEDs.patch +++ /dev/null @@ -1,28 +0,0 @@ -From: Christian Lamparter -Date: Thu, 7 Jun 2018 19:29:12 +0200 -Subject: bcm53xx: add LED status label alias for Meraki MR32 - -add an led-status alias label. This is used by OpenWrt's LED -DTS lookup function to identifiy the indicator LED - -Signed-off-by: Christian Lamparter - ---- a/arch/arm/boot/dts/bcm53016-meraki-mr32.dts -+++ b/arch/arm/boot/dts/bcm53016-meraki-mr32.dts -@@ -27,6 +27,7 @@ - - aliases { - serial1 = &uart2; -+ led-status = &led_status; - }; - - leds { -@@ -66,7 +67,7 @@ - max-brightness = <255>; - }; - -- green { -+ led_status: green { - /* SYS-LED 1 - Tricolor */ - function = LED_FUNCTION_POWER; - color = ; From 50d20db6a8fd2aaed3322578c6e5a2960a8d3f4d Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 3 Dec 2022 22:20:37 +0100 Subject: [PATCH 006/110] uml: switch to Kernel 5.15 Switch over from testing version. Signed-off-by: Christian Lamparter --- target/linux/uml/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/linux/uml/Makefile b/target/linux/uml/Makefile index 90770cc0e42..9f6b9644f85 100644 --- a/target/linux/uml/Makefile +++ b/target/linux/uml/Makefile @@ -13,8 +13,7 @@ BOARD:=uml BOARDNAME:=User Mode Linux FEATURES:=audio ext4 rootfs-part squashfs -KERNEL_PATCHVER:=5.10 -KERNEL_TESTING_PATCHVER:=5.15 +KERNEL_PATCHVER:=5.15 include $(INCLUDE_DIR)/target.mk From 772434e7078e6de0cc1c7505d29ae923dc3e32bc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 12 Dec 2022 00:54:16 +0100 Subject: [PATCH 007/110] gemini: Set proper default networks Some Gemini devices are NAS type devices and need to ask for DHCP IP on eth0. Some has a LAN/WAN setup. Add sensible defaults for all known devices. Signed-off-by: Linus Walleij --- .../gemini/base-files/etc/board.d/02_network | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/target/linux/gemini/base-files/etc/board.d/02_network b/target/linux/gemini/base-files/etc/board.d/02_network index d6ee5cadfe2..93a02a7564e 100644 --- a/target/linux/gemini/base-files/etc/board.d/02_network +++ b/target/linux/gemini/base-files/etc/board.d/02_network @@ -9,16 +9,31 @@ dlink,dir-685) ucidef_set_interface "eth" device "eth0" protocol "none" ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3" "wan" ;; +dlink,dns-313|\ +edimax,ns-2502|\ +raidsonic,ib-4220-b|\ +wiliboard,wbd111|\ +wiliboard,wbd222) + # NAS machines with a single ethernet port + ucidef_set_interface_lan "eth0" "dhcp" + ;; itian,sq201) # These are all connected to eth1 thru VSC7395 ucidef_set_interface "eth" device "eth1" protocol "none" ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" "eth0" ;; +ssi,1328) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ;; storlink,gemini324) # These are all connected to eth1 thru VSC7385 ucidef_set_interface "eth" device "eth1" protocol "none" ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" "eth0" ;; +teltonika,rut1xx) + # One ethernet LAN which is offering DHCP + ucidef_set_interface_lan "eth0" + ;; esac board_config_flush From c3c9ef598cd03a05e24a85d1dcbd26db70df01d0 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Tue, 13 Dec 2022 00:15:28 +0100 Subject: [PATCH 008/110] generic: fix wrong PTR_ERR use in of net mac address ascii patch nvmem_cell_read return a pointer error when an error occurs. Currently we convert the pointer error to an int while the rest of the function return a void* and expcet an error pointer. Fix this PTR_ERR msuse fixing compilation warning. Fixes the following compilation warning: net/ethernet/eth.c: In function 'nvmem_cell_get_mac_address': net/ethernet/eth.c:547:24: warning: returning 'long int' from a function with return type 'void *' makes pointer from integer without a cast [-Wint-conversion] 547 | return PTR_ERR(mac); | ^~~~~~~~~~~~ net/ethernet/eth.c: In function 'nvmem_cell_get_mac_address_ascii': net/ethernet/eth.c:564:24: warning: returning 'long int' from a function with return type 'void *' makes pointer from integer without a cast [-Wint-conversion] 564 | return PTR_ERR(mac_ascii); | ^~~~~~~~~~~~~~~~~~ Fixes: 7b863af180da ("kernel: move mac-address-ascii patches to generic") Signed-off-by: Christian Marangi --- .../hack-5.10/601-of_net-add-mac-address-ascii-support.patch | 4 ++-- .../hack-5.15/601-of_net-add-mac-address-ascii-support.patch | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/linux/generic/hack-5.10/601-of_net-add-mac-address-ascii-support.patch b/target/linux/generic/hack-5.10/601-of_net-add-mac-address-ascii-support.patch index f73e262bba8..5dd70f5f718 100644 --- a/target/linux/generic/hack-5.10/601-of_net-add-mac-address-ascii-support.patch +++ b/target/linux/generic/hack-5.10/601-of_net-add-mac-address-ascii-support.patch @@ -22,7 +22,7 @@ Submitted-by: Yousong Zhou + + mac = nvmem_cell_read(cell, &len); + if (IS_ERR(mac)) -+ return PTR_ERR(mac); ++ return mac; + if (len != ETH_ALEN) { + kfree(mac); + return ERR_PTR(-EINVAL); @@ -39,7 +39,7 @@ Submitted-by: Yousong Zhou + + mac_ascii = nvmem_cell_read(cell, &len); + if (IS_ERR(mac_ascii)) -+ return PTR_ERR(mac_ascii); ++ return mac_ascii; + if (len != ETH_ALEN*2+5) { + kfree(mac_ascii); + return ERR_PTR(-EINVAL); diff --git a/target/linux/generic/hack-5.15/601-of_net-add-mac-address-ascii-support.patch b/target/linux/generic/hack-5.15/601-of_net-add-mac-address-ascii-support.patch index 83483751f86..4ab05b4ea6f 100644 --- a/target/linux/generic/hack-5.15/601-of_net-add-mac-address-ascii-support.patch +++ b/target/linux/generic/hack-5.15/601-of_net-add-mac-address-ascii-support.patch @@ -22,7 +22,7 @@ Submitted-by: Yousong Zhou + + mac = nvmem_cell_read(cell, &len); + if (IS_ERR(mac)) -+ return PTR_ERR(mac); ++ return mac; + if (len != ETH_ALEN) { + kfree(mac); + return ERR_PTR(-EINVAL); @@ -39,7 +39,7 @@ Submitted-by: Yousong Zhou + + mac_ascii = nvmem_cell_read(cell, &len); + if (IS_ERR(mac_ascii)) -+ return PTR_ERR(mac_ascii); ++ return mac_ascii; + if (len != ETH_ALEN*2+5) { + kfree(mac_ascii); + return ERR_PTR(-EINVAL); From 05dbdcbd32644b57d317ec16688c91b4b413c975 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Mon, 12 Dec 2022 23:47:25 +0100 Subject: [PATCH 009/110] generic: move pending 870 ca8210 fix crash patch to backport Patch got merged upstream in kernel 6.1. Move it to backport to better track it. Signed-off-by: Christian Marangi --- .../888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch} | 0 .../883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename target/linux/generic/{pending-5.10/870-ca8210-Fix-crash-by-zero-initializing-data.patch => backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch} (100%) rename target/linux/generic/{pending-5.15/870-ca8210-Fix-crash-by-zero-initializing-data.patch => backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch} (100%) diff --git a/target/linux/generic/pending-5.10/870-ca8210-Fix-crash-by-zero-initializing-data.patch b/target/linux/generic/backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch similarity index 100% rename from target/linux/generic/pending-5.10/870-ca8210-Fix-crash-by-zero-initializing-data.patch rename to target/linux/generic/backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch diff --git a/target/linux/generic/pending-5.15/870-ca8210-Fix-crash-by-zero-initializing-data.patch b/target/linux/generic/backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch similarity index 100% rename from target/linux/generic/pending-5.15/870-ca8210-Fix-crash-by-zero-initializing-data.patch rename to target/linux/generic/backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch From e046500fe9aa8fc650ca898079afb22b2c13bfb6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 13 Dec 2022 10:25:26 +0100 Subject: [PATCH 010/110] ath9k: add missing dependency on random core Reported-by: Hartmut Birr Signed-off-by: Felix Fietkau --- package/kernel/mac80211/ath.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/kernel/mac80211/ath.mk b/package/kernel/mac80211/ath.mk index 9af8c4665d1..12d669364c5 100644 --- a/package/kernel/mac80211/ath.mk +++ b/package/kernel/mac80211/ath.mk @@ -191,7 +191,7 @@ define KernelPackage/ath9k-common TITLE:=Atheros 802.11n wireless devices (common code for ath9k and ath9k_htc) URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath9k HIDDEN:=1 - DEPENDS+= @PCI_SUPPORT||USB_SUPPORT||TARGET_ath79 +kmod-ath +@DRIVER_11N_SUPPORT + DEPENDS+= @PCI_SUPPORT||USB_SUPPORT||TARGET_ath79 +kmod-ath +@DRIVER_11N_SUPPORT +kmod-random-core FILES:= \ $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath9k/ath9k_common.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath9k/ath9k_hw.ko From 3bc060440ab6f797f7cde804639f8e211ea0cf82 Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Sun, 20 Nov 2022 16:54:59 +0100 Subject: [PATCH 011/110] hostapd: remove an unused function from ubus.c eee80211_frequency_to_channel() isn't used anymore, which is a leftover from: 2a31e9ca97 "hostapd: add op-class to get_status output" Signed-off-by: Andre Heider --- .../services/hostapd/src/src/ap/ubus.c | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/package/network/services/hostapd/src/src/ap/ubus.c b/package/network/services/hostapd/src/src/ap/ubus.c index b7b83438920..50012252e4f 100644 --- a/package/network/services/hostapd/src/src/ap/ubus.c +++ b/package/network/services/hostapd/src/src/ap/ubus.c @@ -390,32 +390,6 @@ hostapd_bss_get_features(struct ubus_context *ctx, struct ubus_object *obj, return 0; } -/* Imported from iw/util.c - * https://git.kernel.org/pub/scm/linux/kernel/git/jberg/iw.git/tree/util.c?id=4b25ae3537af48dbf9d0abf94132e5ba01b32c18#n200 - */ -int ieee80211_frequency_to_channel(int freq) -{ - /* see 802.11-2007 17.3.8.3.2 and Annex J */ - if (freq == 2484) - return 14; - /* see 802.11ax D6.1 27.3.23.2 and Annex E */ - else if (freq == 5935) - return 2; - else if (freq < 2484) - return (freq - 2407) / 5; - else if (freq >= 4910 && freq <= 4980) - return (freq - 4000) / 5; - else if (freq < 5950) - return (freq - 5000) / 5; - else if (freq <= 45000) /* DMG band lower limit */ - /* see 802.11ax D6.1 27.3.23.2 */ - return (freq - 5950) / 5; - else if (freq >= 58320 && freq <= 70200) - return (freq - 56160) / 2160; - else - return 0; -} - static int hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, From 2d36f60d88e9fe3af858a8d4661f0601770f5baa Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Fri, 25 Nov 2022 17:37:59 +0100 Subject: [PATCH 012/110] hostapd: fix 350-nl80211_del_beacon_bss.patch Pass the expected struct: ../src/drivers/driver_nl80211.c: In function 'wpa_driver_nl80211_del_beacon': ../src/drivers/driver_nl80211.c:2945:31: warning: passing argument 1 of 'nl80211_bss_msg' from incompatible pointer type [-Wincompatible-pointer-types] 2945 | msg = nl80211_bss_msg(drv, 0, NL80211_CMD_DEL_BEACON); | ^~~ | | | struct wpa_driver_nl80211_data * ../src/drivers/driver_nl80211.c:695:50: note: expected 'struct i802_bss *' but argument is of type 'struct wpa_driver_nl80211_data *' 695 | struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd) | ~~~~~~~~~~~~~~~~~^~~ Fixes: 35ff1affe8 "hostapd: update to 2022-05-08" Signed-off-by: Andre Heider --- .../services/hostapd/patches/350-nl80211_del_beacon_bss.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/services/hostapd/patches/350-nl80211_del_beacon_bss.patch b/package/network/services/hostapd/patches/350-nl80211_del_beacon_bss.patch index 85298df7eab..a88b63e913d 100644 --- a/package/network/services/hostapd/patches/350-nl80211_del_beacon_bss.patch +++ b/package/network/services/hostapd/patches/350-nl80211_del_beacon_bss.patch @@ -10,7 +10,7 @@ bss->freq = 0; nl80211_put_wiphy_data_ap(bss); - msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON); -+ msg = nl80211_bss_msg(drv, 0, NL80211_CMD_DEL_BEACON); ++ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_BEACON); return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); } From 7c63295bf441cbd1e5de6e1b2a35616455b0e15e Mon Sep 17 00:00:00 2001 From: Andre Heider Date: Sat, 26 Nov 2022 12:58:21 +0100 Subject: [PATCH 013/110] treewide: remove DRIVER_11N_SUPPORT hostapd's compile time option CONFIG_IEEE80211N was removed almost 3 years ago, 80.211n/HT is always included since then. Noticed because `hostapd -v11n` confusingly returned an error. See hostapd's commit: f3bcd69603 "Remove CONFIG_IEEE80211N build option" Signed-off-by: Andre Heider --- package/kernel/ath10k-ct/Makefile | 2 +- package/kernel/mac80211/Makefile | 8 ++++---- package/kernel/mac80211/ath.mk | 8 ++++---- package/kernel/mac80211/broadcom.mk | 4 ++-- package/kernel/mac80211/intel.mk | 2 +- package/kernel/mac80211/marvell.mk | 6 +++--- package/kernel/mac80211/ralink.mk | 2 +- package/kernel/mac80211/realtek.mk | 4 ++-- package/kernel/mt76/Makefile | 2 +- package/kernel/mwlwifi/Makefile | 2 +- package/kernel/rtl8812au-ct/Makefile | 2 +- package/network/services/hostapd/Config.in | 4 ---- package/network/services/hostapd/Makefile | 7 ------- .../services/hostapd/src/src/utils/build_features.h | 4 ---- 14 files changed, 21 insertions(+), 36 deletions(-) diff --git a/package/kernel/ath10k-ct/Makefile b/package/kernel/ath10k-ct/Makefile index acdaa5a6fc9..0c1334cc46e 100644 --- a/package/kernel/ath10k-ct/Makefile +++ b/package/kernel/ath10k-ct/Makefile @@ -29,7 +29,7 @@ include $(INCLUDE_DIR)/package.mk define KernelPackage/ath10k-ct SUBMENU:=Wireless Drivers TITLE:=ath10k-ct driver optimized for CT ath10k firmware - DEPENDS:=+kmod-mac80211 +kmod-ath +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT @PCI_SUPPORT +kmod-hwmon-core + DEPENDS:=+kmod-mac80211 +kmod-ath +@DRIVER_11AC_SUPPORT @PCI_SUPPORT +kmod-hwmon-core FILES:=\ $(PKG_BUILD_DIR)/ath10k$(CT_KVER)/ath10k_pci.ko \ $(PKG_BUILD_DIR)/ath10k$(CT_KVER)/ath10k_core.ko diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile index aa40e8818c7..58f07e1eeb2 100644 --- a/package/kernel/mac80211/Makefile +++ b/package/kernel/mac80211/Makefile @@ -163,7 +163,7 @@ endef define KernelPackage/mac80211-hwsim $(call KernelPackage/mac80211/Default) TITLE:=mac80211 HW simulation device - DEPENDS+= +kmod-mac80211 +@DRIVER_11AX_SUPPORT +@DRIVER_11AC_SUPPORT +@DRIVER_11N_SUPPORT + DEPENDS+= +kmod-mac80211 +@DRIVER_11AX_SUPPORT +@DRIVER_11AC_SUPPORT FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/mac80211_hwsim.ko AUTOLOAD:=$(call AutoProbe,mac80211_hwsim) endef @@ -172,7 +172,7 @@ endef define KernelPackage/mt7601u $(call KernelPackage/mac80211/Default) TITLE:=MT7601U-based USB dongles Wireless Driver - DEPENDS+= +kmod-mac80211 +@DRIVER_11N_SUPPORT @USB_SUPPORT +kmod-usb-core +mt7601u-firmware + DEPENDS+= +kmod-mac80211 @USB_SUPPORT +kmod-usb-core +mt7601u-firmware FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/mediatek/mt7601u/mt7601u.ko AUTOLOAD:=$(call AutoProbe,mt7601u) endef @@ -180,7 +180,7 @@ endef define KernelPackage/rsi91x $(call KernelPackage/mac80211/Default) TITLE:=Redpine Signals Inc 91x WLAN driver support - DEPENDS+= +kmod-mac80211 +rs9113-firmware +@DRIVER_11N_SUPPORT + DEPENDS+= +kmod-mac80211 +rs9113-firmware FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/rsi/rsi_91x.ko endef @@ -204,7 +204,7 @@ endef define KernelPackage/wlcore $(call KernelPackage/mac80211/Default) TITLE:=TI common driver part - DEPENDS+= +kmod-mmc +kmod-mac80211 +@DRIVER_11N_SUPPORT + DEPENDS+= +kmod-mmc +kmod-mac80211 FILES:= \ $(PKG_BUILD_DIR)/drivers/net/wireless/ti/wlcore/wlcore.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/ti/wlcore/wlcore_sdio.ko diff --git a/package/kernel/mac80211/ath.mk b/package/kernel/mac80211/ath.mk index 12d669364c5..af8c3b93d3c 100644 --- a/package/kernel/mac80211/ath.mk +++ b/package/kernel/mac80211/ath.mk @@ -154,7 +154,7 @@ define KernelPackage/ath6kl TITLE:=Atheros FullMAC wireless devices (common code for ath6kl_sdio and ath6kl_usb) URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath6kl HIDDEN:=1 - DEPENDS+= +kmod-ath +@DRIVER_11N_SUPPORT + DEPENDS+= +kmod-ath FILES:= $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath6kl/ath6kl_core.ko endef @@ -191,7 +191,7 @@ define KernelPackage/ath9k-common TITLE:=Atheros 802.11n wireless devices (common code for ath9k and ath9k_htc) URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath9k HIDDEN:=1 - DEPENDS+= @PCI_SUPPORT||USB_SUPPORT||TARGET_ath79 +kmod-ath +@DRIVER_11N_SUPPORT +kmod-random-core + DEPENDS+= @PCI_SUPPORT||USB_SUPPORT||TARGET_ath79 +kmod-ath +kmod-random-core FILES:= \ $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath9k/ath9k_common.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath9k/ath9k_hw.ko @@ -255,7 +255,7 @@ define KernelPackage/ath10k $(call KernelPackage/mac80211/Default) TITLE:=Atheros 802.11ac wireless cards support URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath10k - DEPENDS+= @PCI_SUPPORT +kmod-ath +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT \ + DEPENDS+= @PCI_SUPPORT +kmod-ath +@DRIVER_11AC_SUPPORT \ +ATH10K_THERMAL:kmod-hwmon-core +ATH10K_THERMAL:kmod-thermal FILES:= \ $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath10k/ath10k_core.ko \ @@ -293,7 +293,7 @@ endef define KernelPackage/carl9170 $(call KernelPackage/mac80211/Default) TITLE:=Driver for Atheros AR9170 USB sticks - DEPENDS:=@USB_SUPPORT +kmod-mac80211 +kmod-ath +kmod-usb-core +kmod-input-core +@DRIVER_11N_SUPPORT +carl9170-firmware + DEPENDS:=@USB_SUPPORT +kmod-mac80211 +kmod-ath +kmod-usb-core +kmod-input-core +carl9170-firmware FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ath/carl9170/carl9170.ko AUTOLOAD:=$(call AutoProbe,carl9170) endef diff --git a/package/kernel/mac80211/broadcom.mk b/package/kernel/mac80211/broadcom.mk index b426ec2b190..67599acbade 100644 --- a/package/kernel/mac80211/broadcom.mk +++ b/package/kernel/mac80211/broadcom.mk @@ -381,7 +381,7 @@ define KernelPackage/brcmsmac $(call KernelPackage/mac80211/Default) TITLE:=Broadcom IEEE802.11n PCIe SoftMAC WLAN driver URL:=https://wireless.wiki.kernel.org/en/users/drivers/brcm80211 - DEPENDS+= +kmod-mac80211 +@DRIVER_11N_SUPPORT +!TARGET_bcm47xx:kmod-bcma +kmod-lib-cordic +kmod-lib-crc8 +kmod-brcmutil +!BRCMSMAC_USE_FW_FROM_WL:brcmsmac-firmware + DEPENDS+= +kmod-mac80211 +!TARGET_bcm47xx:kmod-bcma +kmod-lib-cordic +kmod-lib-crc8 +kmod-brcmutil +!BRCMSMAC_USE_FW_FROM_WL:brcmsmac-firmware FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcmsmac.ko AUTOLOAD:=$(call AutoProbe,brcmsmac) MENU:=1 @@ -413,7 +413,7 @@ define KernelPackage/brcmfmac $(call KernelPackage/mac80211/Default) TITLE:=Broadcom IEEE802.11n USB FullMAC WLAN driver URL:=https://wireless.wiki.kernel.org/en/users/drivers/brcm80211 - DEPENDS+= @USB_SUPPORT +kmod-cfg80211 +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT \ + DEPENDS+= @USB_SUPPORT +kmod-cfg80211 +@DRIVER_11AC_SUPPORT \ +kmod-brcmutil +BRCMFMAC_SDIO:kmod-mmc @!TARGET_uml \ +BRCMFMAC_USB:kmod-usb-core +BRCMFMAC_USB:brcmfmac-firmware-usb FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko diff --git a/package/kernel/mac80211/intel.mk b/package/kernel/mac80211/intel.mk index 50eb561d188..8d374d73e74 100644 --- a/package/kernel/mac80211/intel.mk +++ b/package/kernel/mac80211/intel.mk @@ -6,7 +6,7 @@ config-$(CONFIG_PACKAGE_IWLWIFI_DEBUGFS)+= IWLWIFI_DEBUGFS define KernelPackage/iwlwifi $(call KernelPackage/mac80211/Default) - DEPENDS:= +kmod-mac80211 @PCI_SUPPORT +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT +@DRIVER_11AX_SUPPORT + DEPENDS:= +kmod-mac80211 @PCI_SUPPORT +@DRIVER_11AC_SUPPORT +@DRIVER_11AX_SUPPORT TITLE:=Intel AGN Wireless support FILES:= \ $(PKG_BUILD_DIR)/drivers/net/wireless/intel/iwlwifi/iwlwifi.ko \ diff --git a/package/kernel/mac80211/marvell.mk b/package/kernel/mac80211/marvell.mk index 764d7f1f84d..dbd07a80da5 100644 --- a/package/kernel/mac80211/marvell.mk +++ b/package/kernel/mac80211/marvell.mk @@ -9,7 +9,7 @@ define KernelPackage/mwl8k $(call KernelPackage/mac80211/Default) TITLE:=Driver for Marvell TOPDOG 802.11 Wireless cards URL:=https://wireless.wiki.kernel.org/en/users/drivers/mwl8k - DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +@DRIVER_11N_SUPPORT +mwl8k-firmware + DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +mwl8k-firmware FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mwl8k.ko AUTOLOAD:=$(call AutoProbe,mwl8k) endef @@ -23,7 +23,7 @@ define KernelPackage/mwifiex-pcie $(call KernelPackage/mac80211/Default) TITLE:=Driver for Marvell 802.11n/802.11ac PCIe Wireless cards URL:=https://wireless.wiki.kernel.org/en/users/drivers/mwifiex - DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT +mwifiex-pcie-firmware + DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +@DRIVER_11AC_SUPPORT +mwifiex-pcie-firmware FILES:= \ $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mwifiex/mwifiex.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mwifiex/mwifiex_pcie.ko @@ -38,7 +38,7 @@ define KernelPackage/mwifiex-sdio $(call KernelPackage/mac80211/Default) TITLE:=Driver for Marvell 802.11n/802.11ac SDIO Wireless cards URL:=https://wireless.wiki.kernel.org/en/users/drivers/mwifiex - DEPENDS+= +kmod-mmc +kmod-mac80211 +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT +mwifiex-sdio-firmware + DEPENDS+= +kmod-mmc +kmod-mac80211 +@DRIVER_11AC_SUPPORT +mwifiex-sdio-firmware FILES:= \ $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mwifiex/mwifiex.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mwifiex/mwifiex_sdio.ko diff --git a/package/kernel/mac80211/ralink.mk b/package/kernel/mac80211/ralink.mk index 6646c09ee2e..83d208ee1a0 100644 --- a/package/kernel/mac80211/ralink.mk +++ b/package/kernel/mac80211/ralink.mk @@ -88,7 +88,7 @@ endef define KernelPackage/rt2800-lib $(call KernelPackage/rt2x00/Default) - DEPENDS+= @(PCI_SUPPORT||USB_SUPPORT||TARGET_ramips) +kmod-rt2x00-lib +kmod-lib-crc-ccitt +@DRIVER_11N_SUPPORT + DEPENDS+= @(PCI_SUPPORT||USB_SUPPORT||TARGET_ramips) +kmod-rt2x00-lib +kmod-lib-crc-ccitt HIDDEN:=1 TITLE+= (rt2800 LIB) FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ralink/rt2x00/rt2800lib.ko diff --git a/package/kernel/mac80211/realtek.mk b/package/kernel/mac80211/realtek.mk index 5aa9171bd31..9c143583265 100644 --- a/package/kernel/mac80211/realtek.mk +++ b/package/kernel/mac80211/realtek.mk @@ -38,7 +38,7 @@ endef define KernelPackage/rtlwifi $(call KernelPackage/mac80211/Default) TITLE:=Realtek common driver part - DEPENDS+= @(PCI_SUPPORT||USB_SUPPORT) +kmod-mac80211 +@DRIVER_11N_SUPPORT + DEPENDS+= @(PCI_SUPPORT||USB_SUPPORT) +kmod-mac80211 FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtlwifi/rtlwifi.ko HIDDEN:=1 endef @@ -169,7 +169,7 @@ endef define KernelPackage/rtw88 $(call KernelPackage/mac80211/Default) TITLE:=Realtek RTL8822BE/RTL8822CE/RTL8723DE - DEPENDS+= @(PCI_SUPPORT) +kmod-mac80211 +@DRIVER_11AC_SUPPORT +@DRIVER_11N_SUPPORT + DEPENDS+= @(PCI_SUPPORT) +kmod-mac80211 +@DRIVER_11AC_SUPPORT FILES:=\ $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8822be.ko \ $(PKG_BUILD_DIR)/drivers/net/wireless/realtek/rtw88/rtw88_8822b.ko \ diff --git a/package/kernel/mt76/Makefile b/package/kernel/mt76/Makefile index cb1f8743bc0..a086fed1f93 100644 --- a/package/kernel/mt76/Makefile +++ b/package/kernel/mt76/Makefile @@ -40,7 +40,7 @@ define KernelPackage/mt76-default SUBMENU:=Wireless Drivers DEPENDS:= \ +kmod-mac80211 \ - +@DRIVER_11AC_SUPPORT +@DRIVER_11N_SUPPORT + +@DRIVER_11AC_SUPPORT endef define KernelPackage/mt76 diff --git a/package/kernel/mwlwifi/Makefile b/package/kernel/mwlwifi/Makefile index f486c3c2234..bb472b2a54a 100644 --- a/package/kernel/mwlwifi/Makefile +++ b/package/kernel/mwlwifi/Makefile @@ -29,7 +29,7 @@ include $(INCLUDE_DIR)/package.mk define KernelPackage/mwlwifi SUBMENU:=Wireless Drivers TITLE:=Marvell 88W8864/88W8897/88W8964/88W8997 wireless driver - DEPENDS:=+kmod-mac80211 +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT @PCI_SUPPORT @TARGET_mvebu + DEPENDS:=+kmod-mac80211 +@DRIVER_11AC_SUPPORT @PCI_SUPPORT @TARGET_mvebu FILES:=$(PKG_BUILD_DIR)/mwlwifi.ko AUTOLOAD:=$(call AutoLoad,50,mwlwifi) endef diff --git a/package/kernel/rtl8812au-ct/Makefile b/package/kernel/rtl8812au-ct/Makefile index aa2d7a2ea9a..eb2aaa6bf50 100644 --- a/package/kernel/rtl8812au-ct/Makefile +++ b/package/kernel/rtl8812au-ct/Makefile @@ -24,7 +24,7 @@ include $(INCLUDE_DIR)/package.mk define KernelPackage/rtl8812au-ct SUBMENU:=Wireless Drivers TITLE:=Driver for Realtek 8812 AU devices comfast 912-ac, etc - DEPENDS:=+kmod-cfg80211 +kmod-usb-core +@DRIVER_11N_SUPPORT +@DRIVER_11AC_SUPPORT + DEPENDS:=+kmod-cfg80211 +kmod-usb-core +@DRIVER_11AC_SUPPORT FILES:=\ $(PKG_BUILD_DIR)/rtl8812au.ko AUTOLOAD:=$(call AutoProbe,rtl8812au) diff --git a/package/network/services/hostapd/Config.in b/package/network/services/hostapd/Config.in index 21ed42fa077..dd8b9b4de71 100644 --- a/package/network/services/hostapd/Config.in +++ b/package/network/services/hostapd/Config.in @@ -68,10 +68,6 @@ config DRIVER_WEXT_SUPPORT select KERNEL_WIRELESS_EXT default n -config DRIVER_11N_SUPPORT - bool - default n - config DRIVER_11AC_SUPPORT bool default n diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile index 0eb8279453e..4fa9a6ae46f 100644 --- a/package/network/services/hostapd/Makefile +++ b/package/network/services/hostapd/Makefile @@ -28,7 +28,6 @@ PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_hostapd-mini \ CONFIG_WPA_RFKILL_SUPPORT \ CONFIG_DRIVER_WEXT_SUPPORT \ - CONFIG_DRIVER_11N_SUPPORT \ CONFIG_DRIVER_11AC_SUPPORT \ CONFIG_DRIVER_11AX_SUPPORT \ CONFIG_WPA_ENABLE_WEP @@ -71,11 +70,6 @@ include $(INCLUDE_DIR)/package.mk STAMP_CONFIGURED:=$(STAMP_CONFIGURED)_$(CONFIG_WPA_MSG_MIN_PRIORITY) - -ifneq ($(CONFIG_DRIVER_11N_SUPPORT),) - HOSTAPD_IEEE80211N:=y -endif - ifneq ($(CONFIG_DRIVER_11AC_SUPPORT),) HOSTAPD_IEEE80211AC:=y endif @@ -87,7 +81,6 @@ endif DRIVER_MAKEOPTS= \ CONFIG_ACS=$(CONFIG_PACKAGE_kmod-cfg80211) \ CONFIG_DRIVER_NL80211=$(CONFIG_PACKAGE_kmod-cfg80211) \ - CONFIG_IEEE80211N=$(HOSTAPD_IEEE80211N) \ CONFIG_IEEE80211AC=$(HOSTAPD_IEEE80211AC) \ CONFIG_IEEE80211AX=$(HOSTAPD_IEEE80211AX) \ CONFIG_DRIVER_WEXT=$(CONFIG_DRIVER_WEXT_SUPPORT) \ diff --git a/package/network/services/hostapd/src/src/utils/build_features.h b/package/network/services/hostapd/src/src/utils/build_features.h index 138a799e75b..553769ecebc 100644 --- a/package/network/services/hostapd/src/src/utils/build_features.h +++ b/package/network/services/hostapd/src/src/utils/build_features.h @@ -7,10 +7,6 @@ static inline int has_feature(const char *feat) if (!strcmp(feat, "eap")) return 1; #endif -#ifdef CONFIG_IEEE80211N - if (!strcmp(feat, "11n")) - return 1; -#endif #ifdef CONFIG_IEEE80211AC if (!strcmp(feat, "11ac")) return 1; From a4339c5a21d67014a4bb25dbb57347514c33433d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 13 Dec 2022 21:14:18 +0100 Subject: [PATCH 014/110] mac80211: add a fix for a crash in __ieee80211_rx_h_amsdu Fix initialization of rx->link_sta Signed-off-by: Felix Fietkau --- ...x-initialization-of-rx-link-and-rx-l.patch | 413 ++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch diff --git a/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch b/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch new file mode 100644 index 00000000000..5652c14c9b8 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch @@ -0,0 +1,413 @@ +From: Felix Fietkau +Date: Tue, 13 Dec 2022 21:03:19 +0100 +Subject: [PATCH] wifi: mac80211: fix initialization of rx->link and + rx->link_sta + +There are some codepaths that do not initialize rx->link_sta properly. This +causes a crash in places which assume that rx->link_sta is valid if rx->sta +is valid. +One known instance is triggered by __ieee80211_rx_h_amsdu being called from +fast-rx. + +Since the initialization of rx->link and rx->link_sta is rather convoluted +and duplicated in many places, clean it up by using a helper function to +set it. + +Fixes: ccdde7c74ffd ("wifi: mac80211: properly implement MLO key handling") +Fixes: b320d6c456ff ("wifi: mac80211: use correct rx link_sta instead of default") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -4067,6 +4067,55 @@ static void ieee80211_invoke_rx_handlers + #undef CALL_RXH + } + ++static bool ++ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id) ++{ ++ if (!sta->mlo) ++ return false; ++ ++ return !!(sta->valid_links & BIT(link_id)); ++} ++ ++static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx, ++ u8 link_id) ++{ ++ if (!ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta, link_id)) ++ return false; ++ ++ rx->link_id = link_id; ++ rx->link = rcu_dereference(rx->sdata->link[link_id]); ++ rx->link_sta = rcu_dereference(rx->sta->link[link_id]); ++ ++ return rx->link && rx->link_sta; ++} ++ ++static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx, ++ struct ieee80211_sta *pubsta, ++ int link_id) ++{ ++ struct sta_info *sta; ++ ++ sta = container_of(pubsta, struct sta_info, sta); ++ ++ rx->link_id = link_id; ++ rx->sta = sta; ++ ++ if (sta) { ++ rx->local = sta->sdata->local; ++ rx->sdata = sta->sdata; ++ rx->link_sta = &sta->deflink; ++ ++ if (link_id >= 0 && ++ !ieee80211_rx_data_set_link(rx, link_id)) ++ return false; ++ } ++ ++ if (link_id < 0) ++ rx->link = &rx->sdata->deflink; ++ ++ return true; ++} ++ + /* + * This function makes calls into the RX path, therefore + * it has to be invoked under RCU read lock. +@@ -4075,16 +4124,19 @@ void ieee80211_release_reorder_timeout(s + { + struct sk_buff_head frames; + struct ieee80211_rx_data rx = { +- .sta = sta, +- .sdata = sta->sdata, +- .local = sta->local, + /* This is OK -- must be QoS data frame */ + .security_idx = tid, + .seqno_idx = tid, +- .link_id = -1, + }; + struct tid_ampdu_rx *tid_agg_rx; +- u8 link_id; ++ int link_id = -1; ++ ++ /* FIXME: statistics won't be right with this */ ++ if (sta->sta.valid_links) ++ link_id = ffs(sta->sta.valid_links) - 1; ++ ++ if (!ieee80211_rx_data_set_sta(&rx, &sta->sta, link_id)) ++ return; + + tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); + if (!tid_agg_rx) +@@ -4104,10 +4156,6 @@ void ieee80211_release_reorder_timeout(s + }; + drv_event_callback(rx.local, rx.sdata, &event); + } +- /* FIXME: statistics won't be right with this */ +- link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0; +- rx.link = rcu_dereference(sta->sdata->link[link_id]); +- rx.link_sta = rcu_dereference(sta->link[link_id]); + + ieee80211_rx_handlers(&rx, &frames); + } +@@ -4123,7 +4171,6 @@ void ieee80211_mark_rx_ba_filtered_frame + /* This is OK -- must be QoS data frame */ + .security_idx = tid, + .seqno_idx = tid, +- .link_id = -1, + }; + int i, diff; + +@@ -4134,10 +4181,8 @@ void ieee80211_mark_rx_ba_filtered_frame + + sta = container_of(pubsta, struct sta_info, sta); + +- rx.sta = sta; +- rx.sdata = sta->sdata; +- rx.link = &rx.sdata->deflink; +- rx.local = sta->local; ++ if (!ieee80211_rx_data_set_sta(&rx, pubsta, -1)) ++ return; + + rcu_read_lock(); + tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); +@@ -4524,15 +4569,6 @@ void ieee80211_check_fast_rx_iface(struc + mutex_unlock(&local->sta_mtx); + } + +-static bool +-ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id) +-{ +- if (!sta->mlo) +- return false; +- +- return !!(sta->valid_links & BIT(link_id)); +-} +- + static void ieee80211_rx_8023(struct ieee80211_rx_data *rx, + struct ieee80211_fast_rx *fast_rx, + int orig_len) +@@ -4643,7 +4679,6 @@ static bool ieee80211_invoke_fast_rx(str + struct sk_buff *skb = rx->skb; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); +- struct sta_info *sta = rx->sta; + int orig_len = skb->len; + int hdrlen = ieee80211_hdrlen(hdr->frame_control); + int snap_offs = hdrlen; +@@ -4655,7 +4690,6 @@ static bool ieee80211_invoke_fast_rx(str + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + } addrs __aligned(2); +- struct link_sta_info *link_sta; + struct ieee80211_sta_rx_stats *stats; + + /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write +@@ -4758,18 +4792,10 @@ static bool ieee80211_invoke_fast_rx(str + drop: + dev_kfree_skb(skb); + +- if (rx->link_id >= 0) { +- link_sta = rcu_dereference(sta->link[rx->link_id]); +- if (!link_sta) +- return true; +- } else { +- link_sta = &sta->deflink; +- } +- + if (fast_rx->uses_rss) +- stats = this_cpu_ptr(link_sta->pcpu_rx_stats); ++ stats = this_cpu_ptr(rx->link_sta->pcpu_rx_stats); + else +- stats = &link_sta->rx_stats; ++ stats = &rx->link_sta->rx_stats; + + stats->dropped++; + return true; +@@ -4787,7 +4813,6 @@ static bool ieee80211_prepare_and_rx_han + struct ieee80211_local *local = rx->local; + struct ieee80211_sub_if_data *sdata = rx->sdata; + struct ieee80211_hdr *hdr = (void *)skb->data; +- struct link_sta_info *link_sta = NULL; + struct ieee80211_link_data *link; + + rx->skb = skb; +@@ -4810,35 +4835,6 @@ static bool ieee80211_prepare_and_rx_han + if (!ieee80211_accept_frame(rx)) + return false; + +- if (rx->link_id >= 0) { +- link = rcu_dereference(rx->sdata->link[rx->link_id]); +- +- /* we might race link removal */ +- if (!link) +- return true; +- rx->link = link; +- +- if (rx->sta) { +- rx->link_sta = +- rcu_dereference(rx->sta->link[rx->link_id]); +- if (!rx->link_sta) +- return true; +- } +- } else { +- if (rx->sta) +- rx->link_sta = &rx->sta->deflink; +- +- rx->link = &sdata->deflink; +- } +- +- if (unlikely(!is_multicast_ether_addr(hdr->addr1) && +- rx->link_id >= 0 && rx->sta && rx->sta->sta.mlo)) { +- link_sta = rcu_dereference(rx->sta->link[rx->link_id]); +- +- if (WARN_ON_ONCE(!link_sta)) +- return true; +- } +- + if (!consume) { + struct skb_shared_hwtstamps *shwt; + +@@ -4858,16 +4854,16 @@ static bool ieee80211_prepare_and_rx_han + shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp; + } + +- if (unlikely(link_sta)) { ++ if (unlikely(rx->sta && rx->sta->sta.mlo)) { + /* translate to MLD addresses */ + if (ether_addr_equal(link->conf->addr, hdr->addr1)) + ether_addr_copy(hdr->addr1, rx->sdata->vif.addr); +- if (ether_addr_equal(link_sta->addr, hdr->addr2)) ++ if (ether_addr_equal(rx->link_sta->addr, hdr->addr2)) + ether_addr_copy(hdr->addr2, rx->sta->addr); + /* translate A3 only if it's the BSSID */ + if (!ieee80211_has_tods(hdr->frame_control) && + !ieee80211_has_fromds(hdr->frame_control)) { +- if (ether_addr_equal(link_sta->addr, hdr->addr3)) ++ if (ether_addr_equal(rx->link_sta->addr, hdr->addr3)) + ether_addr_copy(hdr->addr3, rx->sta->addr); + else if (ether_addr_equal(link->conf->addr, hdr->addr3)) + ether_addr_copy(hdr->addr3, rx->sdata->vif.addr); +@@ -4888,6 +4884,7 @@ static void __ieee80211_rx_handle_8023(s + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_fast_rx *fast_rx; + struct ieee80211_rx_data rx; ++ int link_id = -1; + + memset(&rx, 0, sizeof(rx)); + rx.skb = skb; +@@ -4904,12 +4901,8 @@ static void __ieee80211_rx_handle_8023(s + if (!pubsta) + goto drop; + +- rx.sta = container_of(pubsta, struct sta_info, sta); +- rx.sdata = rx.sta->sdata; +- +- if (status->link_valid && +- !ieee80211_rx_is_valid_sta_link_id(pubsta, status->link_id)) +- goto drop; ++ if (status->link_valid) ++ link_id = status->link_id; + + /* + * TODO: Should the frame be dropped if the right link_id is not +@@ -4918,19 +4911,8 @@ static void __ieee80211_rx_handle_8023(s + * link_id is used only for stats purpose and updating the stats on + * the deflink is fine? + */ +- if (status->link_valid) +- rx.link_id = status->link_id; +- +- if (rx.link_id >= 0) { +- struct ieee80211_link_data *link; +- +- link = rcu_dereference(rx.sdata->link[rx.link_id]); +- if (!link) +- goto drop; +- rx.link = link; +- } else { +- rx.link = &rx.sdata->deflink; +- } ++ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id)) ++ goto drop; + + fast_rx = rcu_dereference(rx.sta->fast_rx); + if (!fast_rx) +@@ -4948,6 +4930,8 @@ static bool ieee80211_rx_for_interface(s + { + struct link_sta_info *link_sta; + struct ieee80211_hdr *hdr = (void *)skb->data; ++ struct sta_info *sta; ++ int link_id = -1; + + /* + * Look up link station first, in case there's a +@@ -4957,24 +4941,19 @@ static bool ieee80211_rx_for_interface(s + */ + link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2); + if (link_sta) { +- rx->sta = link_sta->sta; +- rx->link_id = link_sta->link_id; ++ sta = link_sta->sta; ++ link_id = link_sta->link_id; + } else { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + +- rx->sta = sta_info_get_bss(rx->sdata, hdr->addr2); +- if (rx->sta) { +- if (status->link_valid && +- !ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta, +- status->link_id)) +- return false; +- +- rx->link_id = status->link_valid ? status->link_id : -1; +- } else { +- rx->link_id = -1; +- } ++ sta = sta_info_get_bss(rx->sdata, hdr->addr2); ++ if (status->link_valid) ++ link_id = status->link_id; + } + ++ if (!ieee80211_rx_data_set_sta(rx, &sta->sta, link_id)) ++ return false; ++ + return ieee80211_prepare_and_rx_handle(rx, skb, consume); + } + +@@ -5033,19 +5012,15 @@ static void __ieee80211_rx_handle_packet + + if (ieee80211_is_data(fc)) { + struct sta_info *sta, *prev_sta; +- u8 link_id = status->link_id; ++ int link_id = -1; + +- if (pubsta) { +- rx.sta = container_of(pubsta, struct sta_info, sta); +- rx.sdata = rx.sta->sdata; ++ if (status->link_valid) ++ link_id = status->link_id; + +- if (status->link_valid && +- !ieee80211_rx_is_valid_sta_link_id(pubsta, link_id)) ++ if (pubsta) { ++ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id)) + goto out; + +- if (status->link_valid) +- rx.link_id = status->link_id; +- + /* + * In MLO connection, fetch the link_id using addr2 + * when the driver does not pass link_id in status. +@@ -5063,7 +5038,7 @@ static void __ieee80211_rx_handle_packet + if (!link_sta) + goto out; + +- rx.link_id = link_sta->link_id; ++ ieee80211_rx_data_set_link(&rx, link_sta->link_id); + } + + if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) +@@ -5079,30 +5054,25 @@ static void __ieee80211_rx_handle_packet + continue; + } + +- if ((status->link_valid && +- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta, +- link_id)) || +- (!status->link_valid && prev_sta->sta.mlo)) ++ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta, ++ link_id)) ++ goto out; ++ ++ if (!status->link_valid && prev_sta->sta.mlo) + continue; + +- rx.link_id = status->link_valid ? link_id : -1; +- rx.sta = prev_sta; +- rx.sdata = prev_sta->sdata; + ieee80211_prepare_and_rx_handle(&rx, skb, false); + + prev_sta = sta; + } + + if (prev_sta) { +- if ((status->link_valid && +- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta, +- link_id)) || +- (!status->link_valid && prev_sta->sta.mlo)) ++ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta, ++ link_id)) + goto out; + +- rx.link_id = status->link_valid ? link_id : -1; +- rx.sta = prev_sta; +- rx.sdata = prev_sta->sdata; ++ if (!status->link_valid && prev_sta->sta.mlo) ++ goto out; + + if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) + return; From 0ec8d991c24ab18e79bbbab279d0641b81161ca4 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Sun, 27 Nov 2022 14:01:58 +0000 Subject: [PATCH 015/110] ramips: add support for Etisalat S3 Etisalat S3 is a wireless WiFi 5 router manufactured by Sercomm company. Device specification -------------------- SoC Type: MediaTek MT7621AT RAM: 256 MiB Flash: 128 MiB Wireless 2.4 GHz (MT7603EN): b/g/n, 2x2 Wireless 5 GHz (MT7615E): a/n/ac, 4x4 Ethernet: 5x GbE (WAN, LAN1, LAN2, LAN3, LAN4) USB ports: 1x USB3.0 Button: 2 buttons (Reset & WPS) LEDs: - 1x Status (RGB) - 1x 2.4G (blue, hardware, mt76-phy0) - 1x 5G (blue, hardware, mt76-phy1) Power: 12 VDC, 1.5 A Connector type: barrel Bootloader: U-Boot Installation ----------------- 1. Login to the router web interface under admin account 2. Navigate to Settings -> Configuration -> Save to Computer 3. Decode the configuration. For example, using cfgtool.py tool (see related section): cfgtool.py -u configurationBackup.cfg 4. Open configurationBackup.xml and find the following line: 5. Insert the following line after and save: 6. Encode the configuration. For example, using cfgtool.py tool: cfgtool.py -p configurationBackup.xml 7. Upload the changed configuration (configurationBackup_changed.cfg) to the router 8. Login to the router web interface (SuperUser:ETxxxxxxxxxx, where ETxxxxxxxxxx is the serial number from the backplate label) 9. Navigate to Settings -> WAN -> Add static IP interface (e.g. 10.0.0.1/255.255.255.0) 10. Navigate to Settings -> Remote cotrol -> Add SSH, port 22, 10.0.0.0/255.255.255.0 and interface created before 11. Change IP of your client to 10.0.0.2/255.255.255.0 and connect the ethernet cable to the WAN port of the router 12. Connect to the router using SSH shell under SuperUser account 13. Run in SSH shell: sh 14. Make a mtd backup (optional, see related section) 15. Change bootflag to Sercomm1 and reboot: printf 1 | dd bs=1 seek=7 count=1 of=/dev/mtdblock3 reboot 16. Login to the router web interface under admin account 17. Remove dots from the OpenWrt factory image filename 18. Update firmware via web using OpenWrt factory image Revert to stock --------------- Change bootflag to Sercomm1 in OpenWrt CLI and then reboot: printf 1 | dd bs=1 seek=7 count=1 of=/dev/mtdblock3 mtd backup ---------- 1. Set up a tftp server (e.g. tftpd64 for windows) 2. Connect to a router using SSH shell and run the following commands: cd /tmp for i in 0 1 2 3 4 5 6 7 8 9 10; do nanddump -f mtd$i /dev/mtd$i; \ tftp -l mtd$i -p 10.0.0.2; md5sum mtd$i >> mtd.md5; rm mtd$i; done tftp -l mtd.md5 -p 10.0.0.2 Recovery -------- Use sercomm-recovery tool. Link: https://github.com/danitool/sercomm-recovery MAC Addresses ------------- +-----+------------+---------+ | use | address | example | +-----+------------+---------+ | LAN | label | *:50 | | WAN | label + 11 | *:5b | | 2g | label + 2 | *:52 | | 5g | label + 3 | *:53 | +-----+------------+---------+ The label MAC address was found in Factory 0x21000 cfgtool.py ---------- A tool for decoding and encoding Sercomm configs. Link: https://github.com/r3d5ky/sercomm_cfg_unpacker Co-authored-by: Karim Dehouche Co-authored-by: Maximilian Weinmann Signed-off-by: Mikhail Zhilkin --- package/boot/uboot-envtools/files/ramips | 1 + .../linux/ramips/dts/mt7621_etisalat_s3.dts | 247 ++++++++++++++++++ target/linux/ramips/image/common-sercomm.mk | 6 +- target/linux/ramips/image/mt7621.mk | 13 + .../mt7621/base-files/etc/board.d/01_leds | 1 + .../mt7621/base-files/lib/upgrade/platform.sh | 1 + 6 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 target/linux/ramips/dts/mt7621_etisalat_s3.dts diff --git a/package/boot/uboot-envtools/files/ramips b/package/boot/uboot-envtools/files/ramips index 6c649bfaccf..62c0908a6fe 100644 --- a/package/boot/uboot-envtools/files/ramips +++ b/package/boot/uboot-envtools/files/ramips @@ -33,6 +33,7 @@ ampedwireless,ally-r1900k) ;; beeline,smartbox-giga|\ beeline,smartbox-turbo|\ +etisalat,s3|\ rostelecom,rt-sf-1) ubootenv_add_uci_config "/dev/mtd0" "0x80000" "0x1000" "0x20000" ;; diff --git a/target/linux/ramips/dts/mt7621_etisalat_s3.dts b/target/linux/ramips/dts/mt7621_etisalat_s3.dts new file mode 100644 index 00000000000..65d168fd803 --- /dev/null +++ b/target/linux/ramips/dts/mt7621_etisalat_s3.dts @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7621.dtsi" + +#include +#include +#include + +/ { + compatible = "etisalat,s3", "mediatek,mt7621-soc"; + model = "Etisalat S3"; + + aliases { + label-mac-device = &gmac0; + + led-boot = &led_status_green; + led-failsafe = &led_status_red; + led-running = &led_status_green; + led-upgrade = &led_status_red; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + label = "blue:wan"; + color = ; + function = LED_FUNCTION_WAN; + gpios = <&gpio 13 GPIO_ACTIVE_HIGH>; + }; + + led_status_green: led-1 { + label = "green:status"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio 15 GPIO_ACTIVE_HIGH>; + }; + + led_status_red: led-2 { + label = "red:status"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio 16 GPIO_ACTIVE_HIGH>; + }; + }; + + keys { + compatible = "gpio-keys"; + + wps { + label = "wps"; + gpios = <&gpio 11 GPIO_ACTIVE_HIGH>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&gpio 14 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + ubi-concat { + compatible = "mtd-concat"; + devices = <&ubiconcat0 &ubiconcat1 &ubiconcat2>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "ubi"; + reg = <0x0 0x4f80000>; + }; + }; + }; +}; + +&nand { + status = "okay"; + + partitions { + compatible = "sercomm,sc-partitions", "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x100000>; + sercomm,scpart-id = <0>; + read-only; + }; + + partition@100000 { + label = "dynamic partition map"; + reg = <0x100000 0x100000>; + sercomm,scpart-id = <1>; + read-only; + }; + + factory: partition@200000 { + label = "Factory"; + reg = <0x200000 0x100000>; + sercomm,scpart-id = <2>; + read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_factory_21000: macaddr@21000 { + reg = <0x21000 0x6>; + }; + }; + + partition@300000 { + label = "Boot Flag"; + reg = <0x300000 0x100000>; + sercomm,scpart-id = <3>; + }; + + partition@400000 { + label = "kernel"; + reg = <0x400000 0x600000>; + sercomm,scpart-id = <4>; + }; + + partition@a00000 { + label = "Kernel 2"; + reg = <0xa00000 0x600000>; + sercomm,scpart-id = <5>; + read-only; + }; + + ubiconcat0: partition@1000000 { + label = "File System 1"; + reg = <0x1000000 0x2000000>; + sercomm,scpart-id = <6>; + }; + + partition@3000000 { + label = "File System 2"; + reg = <0x3000000 0x2000000>; + sercomm,scpart-id = <7>; + read-only; + }; + + ubiconcat1: partition@5000000 { + label = "Configuration/log"; + reg = <0x5000000 0x1400000>; + sercomm,scpart-id = <8>; + }; + + ubiconcat2: partition@6400000 { + label = "application tmp buffer (Ftool)"; + reg = <0x6400000 0x1b80000>; + sercomm,scpart-id = <9>; + }; + }; +}; + +&pcie { + status = "okay"; +}; + +&pcie0 { + wifi@0,0 { + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + mediatek,mtd-eeprom = <&factory 0x8000>; + ieee80211-freq-limit = <5000000 6000000>; + + nvmem-cells = <&macaddr_factory_21000>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <(3)>; + }; +}; + +&pcie1 { + wifi@0,0 { + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + mediatek,mtd-eeprom = <&factory 0x0>; + ieee80211-freq-limit = <2400000 2500000>; + + nvmem-cells = <&macaddr_factory_21000>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <(2)>; + }; +}; + +&gmac0 { + nvmem-cells = <&macaddr_factory_21000>; + nvmem-cell-names = "mac-address"; +}; + +&gmac1 { + status = "okay"; + label = "wan"; + phy-handle = <ðphy0>; + + nvmem-cells = <&macaddr_factory_21000>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <(11)>; +}; + +&mdio { + ethphy0: ethernet-phy@0 { + reg = <0>; + }; +}; + +&switch0 { + ports { + port@1 { + status = "okay"; + label = "lan1"; + }; + + port@2 { + status = "okay"; + label = "lan2"; + }; + + port@3 { + status = "okay"; + label = "lan3"; + }; + + port@4 { + status = "okay"; + label = "lan4"; + }; + }; +}; + +&uartlite3 { + status = "okay"; +}; + +&state_default { + gpio { + groups = "i2c", "jtag"; + function = "gpio"; + }; +}; diff --git a/target/linux/ramips/image/common-sercomm.mk b/target/linux/ramips/image/common-sercomm.mk index e96cba879c4..1cd6d1ece77 100644 --- a/target/linux/ramips/image/common-sercomm.mk +++ b/target/linux/ramips/image/common-sercomm.mk @@ -79,9 +79,9 @@ define Device/sercomm_dxx lzma -a0 | uImage lzma IMAGES += factory.img IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata - IMAGE/factory.img := append-ubi | sercomm-part-tag rootfs | \ - sercomm-prepend-tagged-kernel kernel | gzip | sercomm-payload | \ - sercomm-crypto + IMAGE/factory.img := append-ubi | check-size | \ + sercomm-part-tag rootfs | sercomm-prepend-tagged-kernel kernel | \ + gzip | sercomm-payload | sercomm-crypto SERCOMM_KERNEL_OFFSET := 0x400100 SERCOMM_ROOTFS_OFFSET := 0x1000000 endef diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk index 08aa592be83..fd99fa22c21 100644 --- a/target/linux/ramips/image/mt7621.mk +++ b/target/linux/ramips/image/mt7621.mk @@ -798,6 +798,19 @@ define Device/elecom_wrc-2533gst2 endef TARGET_DEVICES += elecom_wrc-2533gst2 +define Device/etisalat_s3 + $(Device/sercomm_dxx) + IMAGE_SIZE := 32768k + SERCOMM_HWID := DDK + SERCOMM_HWVER := 10000 + SERCOMM_SWVER := 4009 + DEVICE_VENDOR := Etisalat + DEVICE_MODEL := S3 + DEVICE_PACKAGES := kmod-mt7603 kmod-mt7615e kmod-mt7615-firmware \ + kmod-usb3 uboot-envtools +endef +TARGET_DEVICES += etisalat_s3 + define Device/firefly_firewrt $(Device/dsa-migration) IMAGE_SIZE := 16064k diff --git a/target/linux/ramips/mt7621/base-files/etc/board.d/01_leds b/target/linux/ramips/mt7621/base-files/etc/board.d/01_leds index 42e50451e30..6e8d35ea493 100644 --- a/target/linux/ramips/mt7621/base-files/etc/board.d/01_leds +++ b/target/linux/ramips/mt7621/base-files/etc/board.d/01_leds @@ -26,6 +26,7 @@ asus,rt-n56u-b1) beeline,smartbox-flash|\ beeline,smartbox-giga|\ beeline,smartbox-turbo|\ +etisalat,s3|\ rostelecom,rt-sf-1) ucidef_set_led_netdev "wan" "wan" "blue:wan" "wan" ;; diff --git a/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh b/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh index 06a35077dc1..c665820169e 100755 --- a/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh +++ b/target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh @@ -64,6 +64,7 @@ platform_do_upgrade() { dlink,dir-2660-a1|\ dlink,dir-3060-a1|\ dlink,dir-853-a3|\ + etisalat,s3|\ h3c,tx1800-plus|\ h3c,tx1801-plus|\ h3c,tx1806|\ From 550e5b218453172de16309e3e1d1a8c451c8b3c3 Mon Sep 17 00:00:00 2001 From: Andrew Cameron Date: Sun, 6 Nov 2022 14:43:37 +0000 Subject: [PATCH 016/110] ath79: add support for TP-Link CPE605-v1 TP-Link CPE605-v1 is an outdoor wireless CPE for 5 GHz with one Ethernet port based on Atheros AR9344 Specifications: - 560/450/225 MHz (CPU/DDR/AHB) - 1x 10/100 Mbps Ethernet - 64 MB of DDR2 RAM - 8 MB of SPI-NOR Flash - 23dBi high-gain directional antenna and a dedicated metal reflector - Power, LAN, WLAN5G green LEDs - 3x green RSSI LEDs Flashing instructions: Flash factory image through stock firmware WEB UI or through TFTP To get to TFTP recovery just hold reset button while powering on for around 4-5 seconds and release. Rename factory image to recovery.bin Stock TFTP server IP:192.168.0.100 Stock device TFTP adress:192.168.0.254 Signed-off-by: Andrew Cameron --- .../ath79/dts/ar9344_tplink_cpe605-v1.dts | 33 +++++++++++++++++++ .../generic/base-files/etc/board.d/01_leds | 1 + .../generic/base-files/etc/board.d/02_network | 1 + target/linux/ath79/image/generic-tp-link.mk | 10 ++++++ 4 files changed, 45 insertions(+) create mode 100644 target/linux/ath79/dts/ar9344_tplink_cpe605-v1.dts diff --git a/target/linux/ath79/dts/ar9344_tplink_cpe605-v1.dts b/target/linux/ath79/dts/ar9344_tplink_cpe605-v1.dts new file mode 100644 index 00000000000..0c6eee7ba63 --- /dev/null +++ b/target/linux/ath79/dts/ar9344_tplink_cpe605-v1.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "ar9344_tplink_cpe.dtsi" + +/ { + model = "TP-Link CPE605 v1"; + compatible = "tplink,cpe605-v1", "qca,ar9344"; + + aliases { + led-boot = &led_lan; + led-failsafe = &led_lan; + led-upgrade = &led_lan; + }; + + leds { + compatible = "gpio-leds"; + + led_lan: lan { + label = "green:lan"; + gpios = <&gpio 11 GPIO_ACTIVE_LOW>; + }; + + wlan5g { + label = "green:wlan5g"; + gpios = <&gpio 13 GPIO_ACTIVE_LOW>; + linux,default-trigger = "phy0tpt"; + }; + }; +}; + +ð1 { + compatible = "syscon", "simple-mfd"; +}; diff --git a/target/linux/ath79/generic/base-files/etc/board.d/01_leds b/target/linux/ath79/generic/base-files/etc/board.d/01_leds index cfcd2dfeda5..f0610f39e91 100644 --- a/target/linux/ath79/generic/base-files/etc/board.d/01_leds +++ b/target/linux/ath79/generic/base-files/etc/board.d/01_leds @@ -18,6 +18,7 @@ glinet,6416|\ glinet,gl-ar300m-lite|\ glinet,gl-ar300m16|\ pcs,cap324|\ +tplink,cpe605-v1|\ tplink,cpe610-v1|\ tplink,cpe610-v2|\ tplink,tl-wa1201-v2) diff --git a/target/linux/ath79/generic/base-files/etc/board.d/02_network b/target/linux/ath79/generic/base-files/etc/board.d/02_network index e14240ba0bf..529d33782fa 100644 --- a/target/linux/ath79/generic/base-files/etc/board.d/02_network +++ b/target/linux/ath79/generic/base-files/etc/board.d/02_network @@ -77,6 +77,7 @@ ath79_setup_interfaces() tplink,cpe210-v3|\ tplink,cpe510-v2|\ tplink,cpe510-v3|\ + tplink,cpe605-v1|\ tplink,cpe610-v1|\ tplink,cpe610-v2|\ tplink,cpe710-v1|\ diff --git a/target/linux/ath79/image/generic-tp-link.mk b/target/linux/ath79/image/generic-tp-link.mk index 598085e50b0..314ece1e033 100644 --- a/target/linux/ath79/image/generic-tp-link.mk +++ b/target/linux/ath79/image/generic-tp-link.mk @@ -350,6 +350,16 @@ define Device/tplink_cpe510-v3 endef TARGET_DEVICES += tplink_cpe510-v3 +define Device/tplink_cpe605-v1 + $(Device/tplink-safeloader-okli) + SOC := ar9344 + IMAGE_SIZE := 7680k + DEVICE_MODEL := CPE605 + DEVICE_VARIANT := v1 + TPLINK_BOARD_ID := CPE605V1 +endef +TARGET_DEVICES += tplink_cpe605-v1 + define Device/tplink_cpe610-v1 $(Device/tplink-safeloader-okli) SOC := ar9344 From 26d10bad7c8b23e36d8e60513cc418bb25f6cb49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Sun, 27 Nov 2022 16:27:47 +0100 Subject: [PATCH 017/110] libpcap: add support for B.A.T.M.A.N. Advanced MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the layer 2 mesh routing protocol B.A.T.M.A.N. Advanced. "batadv" can be used to filter on batman-adv packets. It also allows later filters to look at frames inside the tunnel when both "version" and "type" are specified. Documentation for the batman-adv protocol can be found at the following locations: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/batman-adv.rst https://www.open-mesh.org/ -- This is a backport of the following upstream pull request: https://github.com/the-tcpdump-group/libpcap/pull/980 -> "Add support for B.A.T.M.A.N. Advanced #980" Signed-off-by: Linus Lüssing --- ...dd-support-for-B.A.T.M.A.N.-Advanced.patch | 642 ++++++++++++++++++ 1 file changed, 642 insertions(+) create mode 100644 package/libs/libpcap/patches/300-Add-support-for-B.A.T.M.A.N.-Advanced.patch diff --git a/package/libs/libpcap/patches/300-Add-support-for-B.A.T.M.A.N.-Advanced.patch b/package/libs/libpcap/patches/300-Add-support-for-B.A.T.M.A.N.-Advanced.patch new file mode 100644 index 00000000000..ce810657037 --- /dev/null +++ b/package/libs/libpcap/patches/300-Add-support-for-B.A.T.M.A.N.-Advanced.patch @@ -0,0 +1,642 @@ +From b86b960fbd5c215c9c0f43544935b9a25d9445c5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Linus=20L=C3=BCssing?= +Date: Mon, 23 Nov 2020 00:38:22 +0100 +Subject: [PATCH] Add support for B.A.T.M.A.N. Advanced +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds support for the layer 2 mesh routing protocol +B.A.T.M.A.N. Advanced. "batadv" can be used to filter on batman-adv +packets. It also allows later filters to look at frames inside the +tunnel when both "version" and "type" are specified. + +Documentation for the batman-adv protocol can be found at the following +locations: + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/batman-adv.rst +https://www.open-mesh.org/ + +Signed-off-by: Linus Lüssing +--- + Makefile.in | 2 + + batadv_legacy_packet.h | 77 +++++++++++++++++++ + batadv_packet.h | 78 ++++++++++++++++++++ + ethertype.h | 3 + + gencode.c | 164 +++++++++++++++++++++++++++++++++++++++++ + gencode.h | 3 + + grammar.y.in | 32 +++++++- + nametoaddr.c | 59 +++++++++++++++ + pcap-filter.manmisc.in | 35 ++++++++- + pcap/namedb.h | 2 + + scanner.l | 1 + + 11 files changed, 453 insertions(+), 3 deletions(-) + create mode 100644 batadv_legacy_packet.h + create mode 100644 batadv_packet.h + +--- a/Makefile.in ++++ b/Makefile.in +@@ -134,6 +134,8 @@ PUBHDR = \ + HDR = $(PUBHDR) \ + arcnet.h \ + atmuni31.h \ ++ batadv_legacy_packet.h \ ++ batadv_packet.h \ + diag-control.h \ + ethertype.h \ + extract.h \ +--- /dev/null ++++ b/batadv_legacy_packet.h +@@ -0,0 +1,77 @@ ++/* SPDX-License-Identifier: BSD-3 */ ++/* Copyright (C) 2020 Linus Lüssing */ ++ ++#ifndef _BATADV_LEGACY_PACKET_H_ ++#define _BATADV_LEGACY_PACKET_H_ ++ ++enum batadv_legacy_packettype { ++ BATADV_LEGACY_IV_OGM = 0x01, ++ BATADV_LEGACY_ICMP = 0x02, ++ BATADV_LEGACY_UNICAST = 0x03, ++ BATADV_LEGACY_BCAST = 0x04, ++ BATADV_LEGACY_VIS = 0x05, ++ BATADV_LEGACY_UNICAST_FRAG = 0x06, ++ BATADV_LEGACY_TT_QUERY = 0x07, ++ BATADV_LEGACY_ROAM_ADV = 0x08, ++ BATADV_LEGACY_UNICAST_4ADDR = 0x09, ++ BATADV_LEGACY_CODED = 0x0a, ++}; ++ ++#define ETH_ALEN 6 ++ ++struct batadv_legacy_unicast_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t ttvn; ++ uint8_t dest[ETH_ALEN]; ++}; ++ ++struct batadv_legacy_unicast_4addr_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t src[ETH_ALEN]; ++ uint8_t subtype; ++ uint8_t reserved; ++}; ++ ++struct batadv_legacy_unicast_frag_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t ttvn; ++ uint8_t dest[ETH_ALEN]; ++ uint8_t flags; ++ uint8_t align; ++ uint8_t orig[ETH_ALEN]; ++ uint8_t seqno[2]; /* 2-byte integral value */ ++}; ++ ++struct batadv_legacy_bcast_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t reserved; ++ uint8_t seqno[4]; /* 4-byte integral value */ ++ uint8_t orig[ETH_ALEN]; ++}; ++ ++struct batadv_legacy_coded_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t first_ttvn; ++ uint8_t first_source[ETH_ALEN]; ++ uint8_t first_orig_dest[ETH_ALEN]; ++ uint8_t first_crc[4]; /* 4-byte integral value */ ++ uint8_t second_ttl; ++ uint8_t second_ttvn; ++ uint8_t second_dest[ETH_ALEN]; ++ uint8_t second_source[ETH_ALEN]; ++ uint8_t second_orig_dest[ETH_ALEN]; ++ uint8_t second_crc[4]; /* 4-byte integral value */ ++ uint8_t coded_len[2]; /* 2-byte integral value */ ++}; ++ ++#endif /* _BATADV_LEGACY_PACKET_H_ */ +--- /dev/null ++++ b/batadv_packet.h +@@ -0,0 +1,78 @@ ++/* SPDX-License-Identifier: BSD-3 */ ++/* Copyright (C) 2020 Linus Lüssing */ ++ ++#ifndef _BATADV_PACKET_H_ ++#define _BATADV_PACKET_H_ ++ ++/* For the definitive and most recent packet format definition, ++ * see the batadv_packet.h in the Linux kernel. ++ */ ++ ++enum batadv_packettype { ++ BATADV_IV_OGM = 0x00, ++ BATADV_BCAST = 0x01, ++ BATADV_CODED = 0x02, ++ BATADV_ELP = 0x03, ++ BATADV_OGM2 = 0x04, ++ BATADV_UNICAST = 0x40, ++ BATADV_UNICAST_FRAG = 0x41, ++ BATADV_UNICAST_4ADDR = 0x42, ++ BATADV_ICMP = 0x43, ++ BATADV_UNICAST_TVLV = 0x44, ++}; ++ ++#define ETH_ALEN 6 ++ ++struct batadv_unicast_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t ttvn; ++ uint8_t dest[ETH_ALEN]; ++}; ++ ++struct batadv_unicast_4addr_packet { ++ struct batadv_unicast_packet u; ++ uint8_t src[ETH_ALEN]; ++ uint8_t subtype; ++ uint8_t reserved; ++}; ++ ++struct batadv_frag_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t num_pri; /* number and priority */ ++ uint8_t dest[ETH_ALEN]; ++ uint8_t orig[ETH_ALEN]; ++ uint8_t seqno[2]; /* 2-byte integral value */ ++ uint8_t total_size[2]; /* 2-byte integral value */ ++}; ++ ++struct batadv_bcast_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t reserved; ++ uint8_t seqno[4]; /* 4-byte integral value */ ++ uint8_t orig[ETH_ALEN]; ++}; ++ ++struct batadv_coded_packet { ++ uint8_t packet_type; ++ uint8_t version; ++ uint8_t ttl; ++ uint8_t first_ttvn; ++ uint8_t first_source[ETH_ALEN]; ++ uint8_t first_orig_dest[ETH_ALEN]; ++ uint8_t first_crc[4]; /* 4-byte integral value */ ++ uint8_t second_ttl; ++ uint8_t second_ttvn; ++ uint8_t second_dest[ETH_ALEN]; ++ uint8_t second_source[ETH_ALEN]; ++ uint8_t second_orig_dest[ETH_ALEN]; ++ uint8_t second_crc[4]; /* 4-byte integral value */ ++ uint8_t coded_len[2]; /* 2-byte integral value */ ++}; ++ ++#endif /* _BATADV_PACKET_H_ */ +--- a/ethertype.h ++++ b/ethertype.h +@@ -49,6 +49,9 @@ + #ifndef ETHERTYPE_TRAIL + #define ETHERTYPE_TRAIL 0x1000 + #endif ++#ifndef ETHERTYPE_BATMAN ++#define ETHERTYPE_BATMAN 0x4305 /* B.A.T.M.A.N. Advanced */ ++#endif + #ifndef ETHERTYPE_MOPDL + #define ETHERTYPE_MOPDL 0x6001 + #endif +--- a/gencode.c ++++ b/gencode.c +@@ -74,6 +74,8 @@ + #include "atmuni31.h" + #include "sunatmpos.h" + #include "ppp.h" ++#include "batadv_packet.h" ++#include "batadv_legacy_packet.h" + #include "pcap/sll.h" + #include "pcap/ipnet.h" + #include "arcnet.h" +@@ -9501,6 +9503,168 @@ gen_geneve(compiler_state_t *cstate, bpf + return b1; + } + ++static struct block * ++gen_batadv_check_version(compiler_state_t *cstate, struct block *b0, bpf_u_int32 version) ++{ ++ struct block *b1; ++ ++ if (version > UINT8_MAX) ++ bpf_error(cstate, ++ "batman-adv compatibility version number %u unsupported", ++ version); ++ ++ b1 = gen_cmp(cstate, OR_LINKPL, 1, BPF_B, version); ++ gen_and(b0, b1); ++ ++ return b1; ++} ++ ++static struct block * ++gen_batadv_check_type(compiler_state_t *cstate, struct block *b0, ++ bpf_u_int32 version, bpf_u_int32 type) ++{ ++ struct block *b1; ++ ++ switch (version) { ++ case 14: ++ case 15: ++ if (type > UINT8_MAX) ++ bpf_error(cstate, ++ "batman-adv packet type %u unsupported for compatibility version %u", ++ type, version); ++ ++ b1 = gen_cmp(cstate, OR_LINKPL, 0, BPF_B, type); ++ gen_and(b0, b1); ++ b0 = b1; ++ ++ break; ++ default: ++ bpf_error(cstate, ++ "batman-adv compatibility version number %u unsupported", ++ version); ++ } ++ ++ return b0; ++} ++ ++ ++static void gen_batadv_push_offset(compiler_state_t *cstate, u_int offset) ++{ ++ PUSH_LINKHDR(cstate, DLT_EN10MB, cstate->off_linkpl.is_variable, ++ cstate->off_linkpl.constant_part + cstate->off_nl + offset, ++ cstate->off_linkpl.reg); ++ ++ cstate->off_linktype.constant_part += cstate->off_linkhdr.constant_part; ++ cstate->off_linkpl.constant_part += cstate->off_linkhdr.constant_part; ++ ++ cstate->off_nl = 0; ++ cstate->off_nl_nosnap = 0; /* no 802.2 LLC */ ++} ++ ++static void ++gen_batadv_offsets_v14(compiler_state_t *cstate, bpf_u_int32 type) ++{ ++ size_t offset; ++ ++ switch (type) { ++ case BATADV_LEGACY_UNICAST: /* 0x03 */ ++ offset = sizeof(struct batadv_legacy_unicast_packet); ++ break; ++ case BATADV_LEGACY_BCAST: /* 0x04 */ ++ offset = sizeof(struct batadv_legacy_bcast_packet); ++ break; ++ case BATADV_LEGACY_UNICAST_FRAG: /* 0x06 */ ++ offset = sizeof(struct batadv_legacy_unicast_frag_packet); ++ break; ++ case BATADV_LEGACY_UNICAST_4ADDR: /* 0x09 */ ++ offset = sizeof(struct batadv_legacy_unicast_4addr_packet); ++ break; ++ case BATADV_LEGACY_CODED: /* 0x0a */ ++ offset = sizeof(struct batadv_legacy_coded_packet); ++ break; ++ default: ++ offset = 0; ++ } ++ ++ if (offset) ++ gen_batadv_push_offset(cstate, (u_int)offset); ++} ++ ++static void ++gen_batadv_offsets_v15(compiler_state_t *cstate, bpf_u_int32 type) ++{ ++ size_t offset; ++ ++ switch (type) { ++ case BATADV_BCAST: /* 0x01 */ ++ offset = sizeof(struct batadv_bcast_packet); ++ break; ++ case BATADV_CODED: /* 0x02 */ ++ offset = sizeof(struct batadv_coded_packet); ++ break; ++ case BATADV_UNICAST: /* 0x40 */ ++ offset = sizeof(struct batadv_unicast_packet); ++ break; ++ case BATADV_UNICAST_FRAG: /* 0x41 */ ++ offset = sizeof(struct batadv_frag_packet); ++ break; ++ case BATADV_UNICAST_4ADDR: /* 0x42 */ ++ offset = sizeof(struct batadv_unicast_4addr_packet); ++ break; ++ case BATADV_UNICAST_TVLV: ++ /* unsupported for now, needs variable offset to ++ * take tvlv_len into account ++ */ ++ /* fall through */ ++ default: ++ offset = 0; ++ } ++ ++ if (offset) ++ gen_batadv_push_offset(cstate, (u_int)offset); ++} ++ ++static void ++gen_batadv_offsets(compiler_state_t *cstate, bpf_u_int32 version, bpf_u_int32 type) ++{ ++ switch (version) { ++ case 14: ++ gen_batadv_offsets_v14(cstate, type); ++ break; ++ case 15: ++ gen_batadv_offsets_v15(cstate, type); ++ break; ++ default: ++ break; ++ } ++} ++ ++struct block * ++gen_batadv(compiler_state_t *cstate, bpf_u_int32 version, int has_version, ++ bpf_u_int32 type, int has_type) ++{ ++ struct block *b0; ++ ++ /* ++ * Catch errors reported by us and routines below us, and return NULL ++ * on an error. ++ */ ++ if (setjmp(cstate->top_ctx)) ++ return (NULL); ++ ++ b0 = gen_linktype(cstate, ETHERTYPE_BATMAN); ++ ++ if (has_version) ++ b0 = gen_batadv_check_version(cstate, b0, version); ++ ++ if (has_type) { ++ b0 = gen_batadv_check_type(cstate, b0, version, type); ++ gen_batadv_offsets(cstate, version, type); ++ } ++ ++ return b0; ++} ++ + /* Check that the encapsulated frame has a link layer header + * for Ethernet filters. */ + static struct block * +--- a/gencode.h ++++ b/gencode.h +@@ -346,6 +346,9 @@ struct block *gen_pppoes(compiler_state_ + + struct block *gen_geneve(compiler_state_t *, bpf_u_int32, int); + ++struct block *gen_batadv(compiler_state_t *, bpf_u_int32, int, ++ bpf_u_int32, int); ++ + struct block *gen_atmfield_code(compiler_state_t *, int, bpf_u_int32, + int, int); + struct block *gen_atmtype_abbrev(compiler_state_t *, int); +--- a/grammar.y.in ++++ b/grammar.y.in +@@ -347,6 +347,7 @@ DIAG_OFF_BISON_BYACC + %type mtp2type + %type mtp3field + %type mtp3fieldvalue mtp3value mtp3listvalue ++%type pbatadv + + + %token DST SRC HOST GATEWAY +@@ -365,7 +366,7 @@ DIAG_OFF_BISON_BYACC + %token LEN + %token IPV6 ICMPV6 AH ESP + %token VLAN MPLS +-%token PPPOED PPPOES GENEVE ++%token PPPOED PPPOES GENEVE BATADV + %token ISO ESIS CLNP ISIS L1 L2 IIH LSP SNP CSNP PSNP + %token STP + %token IPX +@@ -592,11 +593,40 @@ other: pqual TK_BROADCAST { CHECK_PTR_ + | PPPOES { CHECK_PTR_VAL(($$ = gen_pppoes(cstate, 0, 0))); } + | GENEVE pnum { CHECK_PTR_VAL(($$ = gen_geneve(cstate, $2, 1))); } + | GENEVE { CHECK_PTR_VAL(($$ = gen_geneve(cstate, 0, 0))); } ++ | BATADV pbatadv { $$ = $2; } + | pfvar { $$ = $1; } + | pqual p80211 { $$ = $2; } + | pllc { $$ = $1; } + ; + ++pbatadv: { CHECK_PTR_VAL(($$ = gen_batadv(cstate, 0, 0, 0, 0))); } ++ | pnum { CHECK_PTR_VAL(($$ = gen_batadv(cstate, $1, 1, 0, 0))); } ++ | pnum pnum { CHECK_PTR_VAL(($$ = gen_batadv(cstate, $1, 1, $2, 1))); } ++ | pnum ID ++ { ++ int type; ++ ++ switch ($1) { ++ case 14: ++ type = pcap_nametobatadvtype_v14($2); ++ break; ++ case 15: ++ type = pcap_nametobatadvtype_v15($2); ++ break; ++ default: ++ bpf_set_error(cstate, "batman-adv compatibility version number %u unsupported", $1); ++ YYABORT; ++ } ++ ++ if (type == PROTO_UNDEF) { ++ bpf_set_error(cstate, "invalid batman-adv packet type value \"%s\"", $2); ++ YYABORT; ++ } ++ ++ CHECK_PTR_VAL(($$ = gen_batadv(cstate, $1, 1, type, 1))); ++ } ++ ; ++ + pfvar: PF_IFNAME ID { CHECK_PTR_VAL($2); CHECK_PTR_VAL(($$ = gen_pf_ifname(cstate, $2))); } + | PF_RSET ID { CHECK_PTR_VAL($2); CHECK_PTR_VAL(($$ = gen_pf_ruleset(cstate, $2))); } + | PF_RNR NUM { CHECK_PTR_VAL(($$ = gen_pf_rnr(cstate, $2))); } +--- a/nametoaddr.c ++++ b/nametoaddr.c +@@ -136,8 +136,12 @@ + + #include "diag-control.h" + ++#include "batadv_packet.h" ++#include "batadv_legacy_packet.h" ++ + #include "gencode.h" + #include ++ + #include "nametoaddr.h" + + #ifdef HAVE_OS_PROTO_H +@@ -604,6 +608,7 @@ PCAP_API_DEF struct eproto eproto_db[] = + { "moprc", ETHERTYPE_MOPRC }, + { "rarp", ETHERTYPE_REVARP }, + { "sca", ETHERTYPE_SCA }, ++ { "batadv", ETHERTYPE_BATMAN }, + { (char *)0, 0 } + }; + +@@ -638,6 +643,60 @@ pcap_nametollc(const char *s) + + while (p->s != 0) { + if (strcmp(p->s, s) == 0) ++ return p->p; ++ p += 1; ++ } ++ return PROTO_UNDEF; ++} ++ ++/* Static data base of batman-adv v14 packet type values. */ ++static struct eproto batadv_type_db_v14[] = { ++ { "iv_ogm", BATADV_LEGACY_IV_OGM }, ++ { "icmp", BATADV_LEGACY_ICMP }, ++ { "unicast", BATADV_LEGACY_UNICAST }, ++ { "bcast", BATADV_LEGACY_BCAST }, ++ { "vis", BATADV_LEGACY_VIS }, ++ { "unicast_frag", BATADV_LEGACY_UNICAST_FRAG }, ++ { "tt_query", BATADV_LEGACY_TT_QUERY }, ++ { "roam_adv", BATADV_LEGACY_ROAM_ADV }, ++ { "unicast_4addr", BATADV_LEGACY_UNICAST_4ADDR }, ++ { "coded", BATADV_LEGACY_CODED }, ++ { (char *)0, 0 } ++}; ++ ++int pcap_nametobatadvtype_v14(const char *s) ++{ ++ struct eproto *p = batadv_type_db_v14; ++ ++ while (p->s != 0) { ++ if (strcmp(p->s, s) == 0) ++ return p->p; ++ p += 1; ++ } ++ return PROTO_UNDEF; ++} ++ ++/* Static data base of batman-adv v15 packet type values. */ ++static struct eproto batadv_type_db_v15[] = { ++ { "iv_ogm", BATADV_IV_OGM }, ++ { "bcast", BATADV_BCAST }, ++ { "coded", BATADV_CODED }, ++ { "elp", BATADV_ELP }, ++ { "ogm2", BATADV_OGM2 }, ++ { "unicast", BATADV_UNICAST }, ++ { "unicast_frag", BATADV_UNICAST_FRAG }, ++ { "unicast_4addr", BATADV_UNICAST_4ADDR }, ++ { "icmp", BATADV_ICMP }, ++ { "unicast_tvlv", BATADV_UNICAST_TVLV }, ++ { (char *)0, 0 } ++}; ++ ++int pcap_nametobatadvtype_v15(const char *s) ++{ ++ struct eproto *p = batadv_type_db_v15; ++ ++ while (p->s != 0) { ++ if (strcmp(p->s, s) == 0) + return p->p; + p += 1; + } +--- a/pcap-filter.manmisc.in ++++ b/pcap-filter.manmisc.in +@@ -98,6 +98,7 @@ protos are: + .BR arp , + .BR rarp , + .BR decnet , ++.BR batadv , + .B tcp + and + .BR udp . +@@ -361,7 +362,7 @@ True if the packet is an IPv6 multicast + .IP "\fBether proto \fIprotocol\fR" + True if the packet is of ether type \fIprotocol\fR. + \fIProtocol\fP can be a number or one of the names +-\fBaarp\fP, \fBarp\fP, \fBatalk\fP, \fBdecnet\fP, \fBip\fP, \fBip6\fP, ++\fBaarp\fP, \fBarp\fP, \fBatalk\fP, \fBbatadv\fP, \fBdecnet\fP, \fBip\fP, \fBip6\fP, + \fBipx\fP, \fBiso\fP, \fBlat\fP, \fBloopback\fP, \fBmopdl\fP, \fBmoprc\fP, \fBnetbeui\fP, + \fBrarp\fP, \fBsca\fP or \fBstp\fP. + Note these identifiers (except \fBloopback\fP) are also keywords +@@ -415,7 +416,7 @@ the filter checks for the IPX etype in a + DSAP in the LLC header, the 802.3-with-no-LLC-header encapsulation of + IPX, and the IPX etype in a SNAP frame. + .RE +-.IP "\fBip\fR, \fBip6\fR, \fBarp\fR, \fBrarp\fR, \fBatalk\fR, \fBaarp\fR, \fBdecnet\fR, \fBiso\fR, \fBstp\fR, \fBipx\fR, \fBnetbeui\fP" ++.IP "\fBip\fR, \fBip6\fR, \fBarp\fR, \fBrarp\fR, \fBatalk\fR, \fBaarp\fR, \fBdecnet\fR, \fBiso\fR, \fBstp\fR, \fBipx\fR, \fBnetbeui\fP, \fBbatadv\fP" + Abbreviations for: + .in +.5i + .nf +@@ -752,6 +753,36 @@ For example: + filters IPv4 protocol encapsulated in Geneve with VNI 0xb. This will + match both IPv4 directly encapsulated in Geneve as well as IPv4 contained + inside an Ethernet frame. ++.IP "\fBbatadv \fI[version] \fI[type]\fR" ++True if the packet is a B.A.T.M.A.N. Advanced packet (Ethernet type 0x4305). ++If the optional \fIversion\fR is specified, only true if the packet has the ++specified batman-adv compatibility \fIversion\fR. If the optional \fIversion\fR ++and \fItype\fR are specified, only true if the packet has both the specified ++batman-adv compatibility \fIversion\fR and batman-adv packet \fItype\fR. ++.IP ++\fIversion\fR may be a number from 0 to 255, though only compatibility version ++14 and 15 were actually deployed in the wild. Version 15 is the current version, ++14 is considered deprecated. ++.IP ++\fItype\fR is currently only defined for compatibility \fIversion\fR 14 and 15. ++\fItype\fR may be a number from 0 to 255 for compatibility \fIversion\fR 14 and 15. ++.IP ++The following packet \fItype\fR aliases are available for compat \fIversion\fR 14: ++\fBiv_ogm\fP, \fBicmp\fP, \fBunicast\fP, \fBbcast\fP, \fBvis\fP, \fBunicast-frag\fP, ++\fBtt_query\fP, \fBroam_adv\fP, \fBunicast_4addr\fP, \fPcoded\fP. ++.IP ++The following packet \fItype\fR aliases are available for compat \fIversion\fR 15: ++\fBiv_ogm\fP, \fBbcast\fP, \fBcoded\fP, \fBelp\fP, \fBogm2\fP, \fBunicast\fP, ++\fBunicast_frag\fP, \fBunicast_4addr\fP, \fBicmp\fP, \fPunicast_tvlv\fP. ++.IP ++Note that when the \fBbatadv\fR keyword is encountered in an expression and ++a batman-adv packet \fItype\fR is provided which specifies an encapsulating ++packet type then it changes the decoding offsets for the remainder of the ++expression on the assumption that the packet is a batman-adv packet. For compat ++\fIversion\fR 14 these are packet \fItype\fRs \fBunicast\fP, \fBbcast\fP, ++\fBunicast_frag\fP, \fBunicast_4addr\fP and \fBcoded\fP. For compat \fIversion\fR ++15 these are currently packet \fItype\fRs \fBbcast\fP, \fBcoded\fP, \fBunicast\fP, ++\fBunicast_frag\fP and \fBunicast_4addr\fP. + .IP "\fBiso proto \fIprotocol\fR" + True if the packet is an OSI packet of protocol type \fIprotocol\fP. + \fIProtocol\fP can be a number or one of the names +--- a/pcap/namedb.h ++++ b/pcap/namedb.h +@@ -69,6 +69,8 @@ PCAP_API int pcap_nametoportrange(const + PCAP_API int pcap_nametoproto(const char *); + PCAP_API int pcap_nametoeproto(const char *); + PCAP_API int pcap_nametollc(const char *); ++PCAP_API int pcap_nametobatadvtype_v14(const char *); ++PCAP_API int pcap_nametobatadvtype_v15(const char *); + /* + * If a protocol is unknown, PROTO_UNDEF is returned. + * Also, pcap_nametoport() returns the protocol along with the port number. +--- a/scanner.l ++++ b/scanner.l +@@ -344,6 +344,7 @@ mpls return MPLS; + pppoed return PPPOED; + pppoes return PPPOES; + geneve return GENEVE; ++batadv return BATADV; + + lane return LANE; + llc return LLC; From 266b261d39f9af5b788091d4b19839deb46761ad Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 14 Dec 2022 02:11:49 +0100 Subject: [PATCH 018/110] ipq806x: 5.15: fix wrong boot-partitions values for split partitions The refreshed patch actually use the format of instead of . This cause boot fail since the rootfs can't be mounted with these wrong values. Fix it to the correct format in each affected dts. Fixes: #11498 Fixes: 6134ba4a34db ("ipq806x: 5.15: add boot-partitions binding to fix block warning") Tested-by: Matt Buczko # Askey RT4230W Signed-off-by: Christian Marangi --- .../files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts | 2 +- .../files-5.15/arch/arm/boot/dts/qcom-ipq8065-rt4230w-rev6.dts | 2 +- .../files-5.15/arch/arm/boot/dts/qcom-ipq8065-tr4400-v2.dts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts index ee257f9d408..5a7088892ad 100644 --- a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts +++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts @@ -119,7 +119,7 @@ nand-ecc-step-size = <512>; nand-is-boot-medium; - qcom,boot-partitions = <0x0 0x1180000 0x5340000 0x6400000>; + qcom,boot-partitions = <0x0 0x1180000 0x5340000 0x10c0000>; partitions { compatible = "fixed-partitions"; diff --git a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-rt4230w-rev6.dts b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-rt4230w-rev6.dts index 10844ecca74..8d9601b6324 100644 --- a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-rt4230w-rev6.dts +++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-rt4230w-rev6.dts @@ -138,7 +138,7 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; - qcom,boot-partitions = <0x0 0x1180000 0x1340000 0x2400000>; + qcom,boot-partitions = <0x0 0x1180000 0x1340000 0x10c0000>; partitions { compatible = "fixed-partitions"; diff --git a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-tr4400-v2.dts b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-tr4400-v2.dts index 075c1f7eee3..7b236af253e 100644 --- a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-tr4400-v2.dts +++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8065-tr4400-v2.dts @@ -127,7 +127,7 @@ nand-bus-width = <8>; nand-ecc-step-size = <512>; - qcom,boot-partitions = <0x0 0x1180000 0x5340000 0x6400000>; + qcom,boot-partitions = <0x0 0x1180000 0x5340000 0x10c0000>; partitions { compatible = "fixed-partitions"; From 823ddab80e2b5f4e9d121d22986f9cae0b9948b0 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 14 Dec 2022 02:17:24 +0100 Subject: [PATCH 019/110] ipq806x: 5.15: standardize wpq864 partition table Compex WPQ864 contains a non standard partition table. Each partition node should be named partition and should contain a valid reg. Fix an extra "-" present after the reg for SBL2_1 partition. Also add "0:" to each qcom default partition following smem naming. Signed-off-by: Christian Marangi --- .../arch/arm/boot/dts/qcom-ipq8064-wpq864.dts | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts index 5a7088892ad..359b4cd470c 100644 --- a/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts +++ b/target/linux/ipq806x/files-5.15/arch/arm/boot/dts/qcom-ipq8064-wpq864.dts @@ -126,133 +126,133 @@ #address-cells = <1>; #size-cells = <1>; - SBL1@0 { - label = "SBL1"; + partition@0 { + label = "0:SBL1"; reg = <0x0000000 0x0040000>; read-only; }; - MIBIB@40000 { - label = "MIBIB"; + partition@40000 { + label = "0:MIBIB"; reg = <0x0040000 0x0140000>; read-only; }; - SBL2@180000 { - label = "SBL2"; + partition@180000 { + label = "0:SBL2"; reg = <0x0180000 0x0140000>; read-only; }; - SBL3@2c0000 { - label = "SBL3"; + partition@2c0000 { + label = "0:SBL3"; reg = <0x02c0000 0x0280000>; read-only; }; - DDRCONFIG@540000 { - label = "DDRCONFIG"; + partition@540000 { + label = "0:DDRCONFIG"; reg = <0x0540000 0x0120000>; read-only; }; - SSD@660000 { - label = "SSD"; + partition@660000 { + label = "0:SSD"; reg = <0x0660000 0x0120000>; read-only; }; - TZ@780000 { - label = "TZ"; + partition@780000 { + label = "0:TZ"; reg = <0x0780000 0x0280000>; read-only; }; - RPM@a00000 { - label = "RPM"; + partition@a00000 { + label = "0:RPM"; reg = <0x0a00000 0x0280000>; read-only; }; - APPSBL@c80000 { - label = "APPSBL"; + partition@c80000 { + label = "0:APPSBL"; reg = <0x0c80000 0x0500000>; read-only; }; - APPSBLENV@1180000 { - label = "APPSBLENV"; + partition@1180000 { + label = "0:APPSBLENV"; reg = <0x1180000 0x0080000>; }; - ART@1200000 { - label = "ART"; + partition@1200000 { + label = "0:ART"; reg = <0x1200000 0x0140000>; }; - ubi@1340000 { + partition@1340000 { label = "ubi"; reg = <0x1340000 0x4000000>; }; - BOOTCONFIG@5340000 { - label = "BOOTCONFIG"; + partition@5340000 { + label = "0:BOOTCONFIG"; reg = <0x5340000 0x0060000>; }; - SBL2-1@53a0000- { - label = "SBL2_1"; + partition@53a0000 { + label = "0:SBL2_1"; reg = <0x53a0000 0x0140000>; read-only; }; - SBL3-1@54e0000 { - label = "SBL3_1"; + partition@54e0000 { + label = "0:SBL3_1"; reg = <0x54e0000 0x0280000>; read-only; }; - DDRCONFIG-1@5760000 { - label = "DDRCONFIG_1"; + partition@5760000 { + label = "0:DDRCONFIG_1"; reg = <0x5760000 0x0120000>; read-only; }; - SSD-1@5880000 { - label = "SSD_1"; + partition@5880000 { + label = "0:SSD_1"; reg = <0x5880000 0x0120000>; read-only; }; - TZ-1@59a0000 { - label = "TZ_1"; + partition@59a0000 { + label = "0:TZ_1"; reg = <0x59a0000 0x0280000>; read-only; }; - RPM-1@5c20000 { - label = "RPM_1"; + partition@5c20000 { + label = "0:RPM_1"; reg = <0x5c20000 0x0280000>; read-only; }; - BOOTCONFIG1@5ea0000 { - label = "BOOTCONFIG1"; + partition@5ea0000 { + label = "0:BOOTCONFIG1"; reg = <0x5ea0000 0x0060000>; }; - APPSBL-1@5f00000 { - label = "APPSBL_1"; + partition@5f00000 { + label = "0:APPSBL_1"; reg = <0x5f00000 0x0500000>; read-only; }; - ubi-1@6400000 { + partition@6400000 { label = "ubi_1"; reg = <0x6400000 0x4000000>; }; - unused@a400000 { + partition@a400000 { label = "unused"; reg = <0xa400000 0x5c00000>; }; From b7a7a97037c4c7490ae3782d2d3be31c010a2fa2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 14 Dec 2022 13:53:40 +0100 Subject: [PATCH 020/110] mac80211: fix connecting multiple wds stations to an AP A faulty check prevented the AP_VLAN interfaces from being brought up Signed-off-by: Felix Fietkau --- ...-wifi-mac80211-fix-MLO-AP_VLAN-check.patch | 25 +++++++++++++++++++ ...port-for-restricting-netdev-features.patch | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 package/kernel/mac80211/patches/subsys/308-wifi-mac80211-fix-MLO-AP_VLAN-check.patch diff --git a/package/kernel/mac80211/patches/subsys/308-wifi-mac80211-fix-MLO-AP_VLAN-check.patch b/package/kernel/mac80211/patches/subsys/308-wifi-mac80211-fix-MLO-AP_VLAN-check.patch new file mode 100644 index 00000000000..2d181e3a68e --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/308-wifi-mac80211-fix-MLO-AP_VLAN-check.patch @@ -0,0 +1,25 @@ +From: Felix Fietkau +Date: Wed, 14 Dec 2022 13:46:38 +0100 +Subject: [PATCH] wifi: mac80211: fix MLO + AP_VLAN check + +Instead of preventing adding AP_VLAN to MLO enabled APs, this check was +preventing adding more than one 4-addr AP_VLAN regardless of the MLO status. +Fix this by adding missing extra checks. + +Fixes: ae960ee90bb1 ("wifi: mac80211: prevent VLANs on MLDs") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/iface.c ++++ b/net/mac80211/iface.c +@@ -364,7 +364,9 @@ static int ieee80211_check_concurrent_if + + /* No support for VLAN with MLO yet */ + if (iftype == NL80211_IFTYPE_AP_VLAN && +- nsdata->wdev.use_4addr) ++ sdata->wdev.use_4addr && ++ nsdata->vif.type == NL80211_IFTYPE_AP && ++ nsdata->vif.valid_links) + return -EOPNOTSUPP; + + /* diff --git a/package/kernel/mac80211/patches/subsys/310-mac80211-add-support-for-restricting-netdev-features.patch b/package/kernel/mac80211/patches/subsys/310-mac80211-add-support-for-restricting-netdev-features.patch index cd6048b4fd0..3d286d080d3 100644 --- a/package/kernel/mac80211/patches/subsys/310-mac80211-add-support-for-restricting-netdev-features.patch +++ b/package/kernel/mac80211/patches/subsys/310-mac80211-add-support-for-restricting-netdev-features.patch @@ -80,7 +80,7 @@ Signed-off-by: Felix Fietkau --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c -@@ -2179,6 +2179,7 @@ int ieee80211_if_add(struct ieee80211_lo +@@ -2181,6 +2181,7 @@ int ieee80211_if_add(struct ieee80211_lo ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; ndev->hw_features |= ndev->features & MAC80211_SUPPORTED_FEATURES_TX; From 230f2fccd14ed98bbb9e7302d1da67a934f71791 Mon Sep 17 00:00:00 2001 From: Stijn Tintel Date: Wed, 14 Dec 2022 18:53:39 +0200 Subject: [PATCH 021/110] qoriq: switch to kernel 5.15 The testing kernel has been running fine for months on a rather complex network setup, with one exception being a crash in __xfrm_state_delete(). This crash has been fixed in kernel 5.15.82, so let's switch the qoriq target to kernel 5.15 by default. Signed-off-by: Stijn Tintel --- target/linux/qoriq/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/linux/qoriq/Makefile b/target/linux/qoriq/Makefile index a92186fd6e8..5e8e417d3f4 100644 --- a/target/linux/qoriq/Makefile +++ b/target/linux/qoriq/Makefile @@ -11,8 +11,7 @@ CPU_TYPE:=e5500 FEATURES:=boot-part ext4 fpu legacy-sdcard powerpc64 ramdisk rootfs-part rtc source-only SUBTARGETS:=generic -KERNEL_PATCHVER:=5.10 -KERNEL_TESTING_PATCHVER:=5.15 +KERNEL_PATCHVER:=5.15 KERNELNAME:=zImage From f9bd655f160978a04928d5139f88baca8c78995e Mon Sep 17 00:00:00 2001 From: Stijn Tintel Date: Wed, 14 Dec 2022 19:10:48 +0200 Subject: [PATCH 022/110] kernel: add missing symbol to 5.15 config Kernel 5.15.82 added a prompt for the FUNCTION_ERROR_INJECTION symbol. This is exposed in builds with CONFIG_KERNEL_KPROBES enabled, causing those builds to fail due to a missing symbol. Add the symbol to fix this. Fixes: 68426e54eda4 ("kernel: bump 5.15 to 5.15.82") Signed-off-by: Stijn Tintel --- target/linux/generic/config-5.15 | 1 + 1 file changed, 1 insertion(+) diff --git a/target/linux/generic/config-5.15 b/target/linux/generic/config-5.15 index 304d99c19cf..a91e0fc8e9b 100644 --- a/target/linux/generic/config-5.15 +++ b/target/linux/generic/config-5.15 @@ -2110,6 +2110,7 @@ CONFIG_FSNOTIFY=y # CONFIG_FUJITSU_ES is not set # CONFIG_FUJITSU_LAPTOP is not set # CONFIG_FUJITSU_TABLET is not set +# CONFIG_FUNCTION_ERROR_INJECTION is not set # CONFIG_FUNCTION_TRACER is not set # CONFIG_FUSE_FS is not set # CONFIG_FUSION is not set From b3dd15ca0adce5ad64895feb90971a86a26f13a6 Mon Sep 17 00:00:00 2001 From: Stijn Tintel Date: Wed, 14 Dec 2022 19:17:43 +0200 Subject: [PATCH 023/110] kernel: add missing symbol to 5.10 config Kernel 5.10.158 added a prompt for the FUNCTION_ERROR_INJECTION symbol. This is exposed in builds with CONFIG_KERNEL_KPROBES enabled, causing those builds to fail due to a missing symbol. Add the symbol to fix this. Fixes: 6801c460b6a7 ("kernel: bump 5.10 to 5.10.158") Signed-off-by: Stijn Tintel --- target/linux/generic/config-5.10 | 1 + 1 file changed, 1 insertion(+) diff --git a/target/linux/generic/config-5.10 b/target/linux/generic/config-5.10 index ebf30fdc26e..95509935c2a 100644 --- a/target/linux/generic/config-5.10 +++ b/target/linux/generic/config-5.10 @@ -2027,6 +2027,7 @@ CONFIG_FSNOTIFY=y # CONFIG_FUJITSU_ES is not set # CONFIG_FUJITSU_LAPTOP is not set # CONFIG_FUJITSU_TABLET is not set +# CONFIG_FUNCTION_ERROR_INJECTION is not set # CONFIG_FUNCTION_TRACER is not set # CONFIG_FUSE_FS is not set # CONFIG_FUSION is not set From bd911b45389b3da299948b457a1fc645febd2248 Mon Sep 17 00:00:00 2001 From: Stijn Tintel Date: Wed, 14 Dec 2022 20:11:45 +0200 Subject: [PATCH 024/110] tools/dosfstools: fix PKG_SOURCE Both mirrors provided in the Makefile only serve gzipped tarballs. Fixes: #10871 Fixes: 9edfe7dd13d9 ("source: Switch to xz for packages and tools where possible") Signed-off-by: Stijn Tintel --- tools/dosfstools/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/dosfstools/Makefile b/tools/dosfstools/Makefile index 7c834dfd497..b332efdb96c 100644 --- a/tools/dosfstools/Makefile +++ b/tools/dosfstools/Makefile @@ -11,10 +11,10 @@ PKG_NAME:=dosfstools PKG_CPE_ID:=cpe:/a:dosfstools_project:dosfstools PKG_VERSION:=4.2 -PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/dosfstools/dosfstools/releases/download/v$(PKG_VERSION)/ \ http://fossies.org/linux/misc -PKG_HASH:=ba7c716ff9b8208a3bba5094a77584a7dc814141de09ab4ce1ae9b84bbcd7844 +PKG_HASH:=64926eebf90092dca21b14259a5301b7b98e7b1943e8a201c7d726084809b527 HOST_FIXUP:=autoreconf From b6fa423907f7e09a173407fd9e9c91ccd907a91d Mon Sep 17 00:00:00 2001 From: John Audia Date: Wed, 14 Dec 2022 10:11:04 -0500 Subject: [PATCH 025/110] kernel: bump 5.15 to 5.15.83 Removed upstreamed: backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch[1] All patches automatically rebased 1. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v5.15.83&id=246bcd05ba6cc43b34ac0bb4bac3ea94a4efa07c Build system: x86_64 Build-tested: bcm2711/RPi4B Run-tested: bcm2711/RPi4B Signed-off-by: John Audia --- include/kernel-5.15 | 4 +-- ...2-Allow-exporting-of-a-struct-dmabuf.patch | 4 +-- ...ce-quirks-for-Freeway-Airmouse-T3-an.patch | 4 +-- ...-t-prevent-IRQ-usage-of-output-GPIOs.patch | 4 +-- ...2c-Add-driver-for-Sony-IMX477-sensor.patch | 2 +- ...vicetree-Add-documentation-for-imx37.patch | 2 +- ...a-i2c-Add-IMX519-CMOS-sensor-binding.patch | 2 +- .../950-0835-clk-Add-clk_drop_range.patch | 2 +- ...clk-Fix-clk_get_parent-documentation.patch | 2 +- .../950-0865-clk-Add-clk_get_rate_range.patch | 6 ++-- ...tree-gpio-hogs-on-dual-role-gpio-pin.patch | 4 +-- ...-mm-multigenerational-lru-groundwork.patch | 8 ++--- ...multigenerational-lru-mm_struct-list.patch | 6 ++-- ...Support-public-address-configuration.patch | 4 +-- ...Fix-application-of-sizeof-to-pointer.patch | 2 +- ...-Fix-crash-by-zero-initializing-data.patch | 30 ------------------- .../701-staging-add-fsl_ppfe-driver.patch | 2 +- ...-spi-mediatek-add-mt7986-spi-support.patch | 14 ++++----- ...an-entry-for-the-IEI-WT61P803-PUZZLE.patch | 2 +- 19 files changed, 37 insertions(+), 67 deletions(-) delete mode 100644 target/linux/generic/backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch diff --git a/include/kernel-5.15 b/include/kernel-5.15 index 18444aaa84a..9a619067df0 100644 --- a/include/kernel-5.15 +++ b/include/kernel-5.15 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.15 = .82 -LINUX_KERNEL_HASH-5.15.82 = fceef6bb79bac494663ccde34453521fc616cd94272fd30564752b3742381b65 +LINUX_VERSION-5.15 = .83 +LINUX_KERNEL_HASH-5.15.83 = 40590843c04c85789105157f69efbd71a4efe87ae2568e40d1b7258c3f747ff3 diff --git a/target/linux/bcm27xx/patches-5.15/950-0132-media-videobuf2-Allow-exporting-of-a-struct-dmabuf.patch b/target/linux/bcm27xx/patches-5.15/950-0132-media-videobuf2-Allow-exporting-of-a-struct-dmabuf.patch index d9d644a59ca..04796a589ac 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0132-media-videobuf2-Allow-exporting-of-a-struct-dmabuf.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0132-media-videobuf2-Allow-exporting-of-a-struct-dmabuf.patch @@ -19,7 +19,7 @@ Signed-off-by: Dave Stevenson --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c -@@ -2144,12 +2144,12 @@ static int __find_plane_by_offset(struct +@@ -2195,12 +2195,12 @@ static int __find_plane_by_offset(struct return -EINVAL; } @@ -35,7 +35,7 @@ Signed-off-by: Dave Stevenson struct dma_buf *dbuf; if (q->memory != VB2_MEMORY_MMAP) { -@@ -2201,6 +2201,21 @@ int vb2_core_expbuf(struct vb2_queue *q, +@@ -2252,6 +2252,21 @@ int vb2_core_expbuf(struct vb2_queue *q, return -EINVAL; } diff --git a/target/linux/bcm27xx/patches-5.15/950-0163-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch b/target/linux/bcm27xx/patches-5.15/950-0163-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch index 99edfa714bb..62552a54471 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0163-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0163-hid-usb-Add-device-quirks-for-Freeway-Airmouse-T3-an.patch @@ -33,7 +33,7 @@ Signed-off-by: Jonathan Bell #define USB_VENDOR_ID_BELKIN 0x050d #define USB_DEVICE_ID_FLIP_KVM 0x3201 -@@ -1310,6 +1313,9 @@ +@@ -1313,6 +1316,9 @@ #define USB_VENDOR_ID_XAT 0x2505 #define USB_DEVICE_ID_XAT_CSR 0x0220 @@ -53,7 +53,7 @@ Signed-off-by: Jonathan Bell { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE2), HID_QUIRK_ALWAYS_POLL }, -@@ -195,6 +196,7 @@ static const struct hid_device_id hid_qu +@@ -198,6 +199,7 @@ static const struct hid_device_id hid_qu { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_GROUP_AUDIO), HID_QUIRK_NOGET }, diff --git a/target/linux/bcm27xx/patches-5.15/950-0238-gpiolib-Don-t-prevent-IRQ-usage-of-output-GPIOs.patch b/target/linux/bcm27xx/patches-5.15/950-0238-gpiolib-Don-t-prevent-IRQ-usage-of-output-GPIOs.patch index ef43c8c73ae..b7146a27385 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0238-gpiolib-Don-t-prevent-IRQ-usage-of-output-GPIOs.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0238-gpiolib-Don-t-prevent-IRQ-usage-of-output-GPIOs.patch @@ -26,7 +26,7 @@ Signed-off-by: Phil Elwell /* Device and char device-related information */ static DEFINE_IDA(gpio_ida); static dev_t gpio_devt; -@@ -2361,8 +2363,8 @@ int gpiod_direction_output(struct gpio_d +@@ -2392,8 +2394,8 @@ int gpiod_direction_output(struct gpio_d value = !!value; /* GPIOs used for enabled IRQs shall not be set as output */ @@ -37,7 +37,7 @@ Signed-off-by: Phil Elwell gpiod_err(desc, "%s: tried to set a GPIO tied to an IRQ as output\n", __func__); -@@ -3180,8 +3182,8 @@ int gpiochip_lock_as_irq(struct gpio_chi +@@ -3211,8 +3213,8 @@ int gpiochip_lock_as_irq(struct gpio_chi } /* To be valid for IRQ the line needs to be input or open drain */ diff --git a/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch b/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch index 4f94a9f11dc..ce189789789 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch @@ -25,7 +25,7 @@ Signed-off-by: Naushir Patuck --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17526,6 +17526,14 @@ T: git git://linuxtv.org/media_tree.git +@@ -17521,6 +17521,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml F: drivers/media/i2c/imx412.c diff --git a/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch b/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch index 8c75f80baa9..bf0a69ffe5b 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch @@ -132,7 +132,7 @@ Signed-off-by: David Plowman +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17540,6 +17540,7 @@ M: Raspberry Pi Kernel Maintenance +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17544,6 +17544,14 @@ F: Documentation/devicetree/bindings/med +@@ -17539,6 +17539,14 @@ F: Documentation/devicetree/bindings/med F: Documentation/devicetree/bindings/media/i2c/imx477.yaml F: drivers/media/i2c/imx477.c diff --git a/target/linux/bcm27xx/patches-5.15/950-0835-clk-Add-clk_drop_range.patch b/target/linux/bcm27xx/patches-5.15/950-0835-clk-Add-clk_drop_range.patch index ce3831d3f9e..cc6ca7ddae5 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0835-clk-Add-clk_drop_range.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0835-clk-Add-clk_drop_range.patch @@ -37,7 +37,7 @@ Signed-off-by: Maxime Ripard rate = clk_get_rate(clk); --- a/include/linux/clk.h +++ b/include/linux/clk.h -@@ -987,6 +987,17 @@ static inline void clk_bulk_disable_unpr +@@ -1096,6 +1096,17 @@ static inline void clk_bulk_disable_unpr } /** diff --git a/target/linux/bcm27xx/patches-5.15/950-0845-clk-Fix-clk_get_parent-documentation.patch b/target/linux/bcm27xx/patches-5.15/950-0845-clk-Fix-clk_get_parent-documentation.patch index 9b821442160..b5339cf4377 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0845-clk-Fix-clk_get_parent-documentation.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0845-clk-Fix-clk_get_parent-documentation.patch @@ -55,7 +55,7 @@ Signed-off-by: Maxime Ripard KUNIT_CASE(clk_test_orphan_transparent_multiple_parent_mux_set_parent_set_range_modified), --- a/include/linux/clk.h +++ b/include/linux/clk.h -@@ -744,8 +744,9 @@ int clk_set_parent(struct clk *clk, stru +@@ -829,8 +829,9 @@ int clk_set_parent(struct clk *clk, stru * clk_get_parent - get the parent clock source for this clock * @clk: clock source * diff --git a/target/linux/bcm27xx/patches-5.15/950-0865-clk-Add-clk_get_rate_range.patch b/target/linux/bcm27xx/patches-5.15/950-0865-clk-Add-clk_get_rate_range.patch index 09d5dc4bce4..b861cb32642 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0865-clk-Add-clk_get_rate_range.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0865-clk-Add-clk_get_rate_range.patch @@ -55,7 +55,7 @@ Signed-off-by: Maxime Ripard * --- a/include/linux/clk.h +++ b/include/linux/clk.h -@@ -714,6 +714,17 @@ bool clk_has_parent(struct clk *clk, str +@@ -799,6 +799,17 @@ bool clk_has_parent(struct clk *clk, str int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max); /** @@ -73,7 +73,7 @@ Signed-off-by: Maxime Ripard * clk_set_min_rate - set a minimum clock rate for a clock source * @clk: clock source * @rate: desired minimum clock rate in Hz, inclusive -@@ -909,6 +920,16 @@ static inline int clk_set_rate_range(str +@@ -1018,6 +1029,16 @@ static inline int clk_set_rate_range(str return 0; } @@ -90,7 +90,7 @@ Signed-off-by: Maxime Ripard static inline int clk_set_min_rate(struct clk *clk, unsigned long rate) { return 0; -@@ -999,6 +1020,44 @@ static inline int clk_drop_range(struct +@@ -1108,6 +1129,44 @@ static inline int clk_drop_range(struct } /** diff --git a/target/linux/bcm63xx/patches-5.15/143-gpio-fix-device-tree-gpio-hogs-on-dual-role-gpio-pin.patch b/target/linux/bcm63xx/patches-5.15/143-gpio-fix-device-tree-gpio-hogs-on-dual-role-gpio-pin.patch index fe134a3a44b..cfd53525a9e 100644 --- a/target/linux/bcm63xx/patches-5.15/143-gpio-fix-device-tree-gpio-hogs-on-dual-role-gpio-pin.patch +++ b/target/linux/bcm63xx/patches-5.15/143-gpio-fix-device-tree-gpio-hogs-on-dual-role-gpio-pin.patch @@ -116,7 +116,7 @@ Signed-off-by: Jonas Gorski } --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c -@@ -1768,7 +1768,8 @@ int gpiochip_add_pingroup_range(struct g +@@ -1799,7 +1799,8 @@ int gpiochip_add_pingroup_range(struct g list_add_tail(&pin_range->node, &gdev->pin_ranges); @@ -126,7 +126,7 @@ Signed-off-by: Jonas Gorski } EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range); -@@ -1825,7 +1826,7 @@ int gpiochip_add_pin_range(struct gpio_c +@@ -1856,7 +1857,7 @@ int gpiochip_add_pin_range(struct gpio_c list_add_tail(&pin_range->node, &gdev->pin_ranges); diff --git a/target/linux/generic/backport-5.15/020-v6.1-04-mm-multigenerational-lru-groundwork.patch b/target/linux/generic/backport-5.15/020-v6.1-04-mm-multigenerational-lru-groundwork.patch index 533ea2aa200..e057b29c470 100644 --- a/target/linux/generic/backport-5.15/020-v6.1-04-mm-multigenerational-lru-groundwork.patch +++ b/target/linux/generic/backport-5.15/020-v6.1-04-mm-multigenerational-lru-groundwork.patch @@ -92,7 +92,7 @@ Change-Id: I71de7cd15b8dfa6f9fdd838023474693c4fee0a7 } --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h -@@ -432,6 +432,18 @@ static inline void cgroup_put(struct cgr +@@ -433,6 +433,18 @@ static inline void cgroup_put(struct cgr css_put(&cgrp->self); } @@ -111,7 +111,7 @@ Change-Id: I71de7cd15b8dfa6f9fdd838023474693c4fee0a7 /** * task_css_set_check - obtain a task's css_set with extra access conditions * @task: the task to obtain css_set for -@@ -446,7 +458,6 @@ static inline void cgroup_put(struct cgr +@@ -447,7 +459,6 @@ static inline void cgroup_put(struct cgr * as locks used during the cgroup_subsys::attach() methods. */ #ifdef CONFIG_PROVE_RCU @@ -119,7 +119,7 @@ Change-Id: I71de7cd15b8dfa6f9fdd838023474693c4fee0a7 extern spinlock_t css_set_lock; #define task_css_set_check(task, __c) \ rcu_dereference_check((task)->cgroups, \ -@@ -707,6 +718,8 @@ struct cgroup; +@@ -708,6 +719,8 @@ struct cgroup; static inline u64 cgroup_id(const struct cgroup *cgrp) { return 1; } static inline void css_get(struct cgroup_subsys_state *css) {} static inline void css_put(struct cgroup_subsys_state *css) {} @@ -593,7 +593,7 @@ Change-Id: I71de7cd15b8dfa6f9fdd838023474693c4fee0a7 VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING, --- a/mm/memcontrol.c +++ b/mm/memcontrol.c -@@ -5226,6 +5226,7 @@ static struct mem_cgroup *mem_cgroup_all +@@ -5237,6 +5237,7 @@ static struct mem_cgroup *mem_cgroup_all memcg->deferred_split_queue.split_queue_len = 0; #endif idr_replace(&mem_cgroup_idr, memcg, memcg->id.id); diff --git a/target/linux/generic/backport-5.15/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch b/target/linux/generic/backport-5.15/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch index 736b4f5152e..53dce95ffb2 100644 --- a/target/linux/generic/backport-5.15/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch +++ b/target/linux/generic/backport-5.15/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch @@ -339,7 +339,7 @@ Change-Id: I25d9eda8c6bdc7c3653b9f210a159d6c247c81e8 /* will mmdrop() in finish_task_switch(). */ --- a/mm/memcontrol.c +++ b/mm/memcontrol.c -@@ -5163,6 +5163,7 @@ static void __mem_cgroup_free(struct mem +@@ -5174,6 +5174,7 @@ static void __mem_cgroup_free(struct mem static void mem_cgroup_free(struct mem_cgroup *memcg) { @@ -347,7 +347,7 @@ Change-Id: I25d9eda8c6bdc7c3653b9f210a159d6c247c81e8 memcg_wb_domain_exit(memcg); __mem_cgroup_free(memcg); } -@@ -6195,6 +6196,29 @@ static void mem_cgroup_move_task(void) +@@ -6206,6 +6207,29 @@ static void mem_cgroup_move_task(void) } #endif @@ -377,7 +377,7 @@ Change-Id: I25d9eda8c6bdc7c3653b9f210a159d6c247c81e8 static int seq_puts_memcg_tunable(struct seq_file *m, unsigned long value) { if (value == PAGE_COUNTER_MAX) -@@ -6538,6 +6562,7 @@ struct cgroup_subsys memory_cgrp_subsys +@@ -6549,6 +6573,7 @@ struct cgroup_subsys memory_cgrp_subsys .css_reset = mem_cgroup_css_reset, .css_rstat_flush = mem_cgroup_css_rstat_flush, .can_attach = mem_cgroup_can_attach, diff --git a/target/linux/generic/backport-5.15/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch b/target/linux/generic/backport-5.15/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch index daa3cac4eff..cf39ba1abd8 100644 --- a/target/linux/generic/backport-5.15/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch +++ b/target/linux/generic/backport-5.15/821-v5.16-Bluetooth-btusb-Support-public-address-configuration.patch @@ -17,7 +17,7 @@ Signed-off-by: Marcel Holtmann --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c -@@ -2263,6 +2263,23 @@ struct btmtk_section_map { +@@ -2268,6 +2268,23 @@ struct btmtk_section_map { }; } __packed; @@ -41,7 +41,7 @@ Signed-off-by: Marcel Holtmann static void btusb_mtk_wmt_recv(struct urb *urb) { struct hci_dev *hdev = urb->context; -@@ -3914,6 +3931,7 @@ static int btusb_probe(struct usb_interf +@@ -3919,6 +3936,7 @@ static int btusb_probe(struct usb_interf hdev->shutdown = btusb_mtk_shutdown; hdev->manufacturer = 70; hdev->cmd_timeout = btusb_mtk_cmd_timeout; diff --git a/target/linux/generic/backport-5.15/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch b/target/linux/generic/backport-5.15/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch index b324cb1aa80..9b8d8635a85 100644 --- a/target/linux/generic/backport-5.15/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch +++ b/target/linux/generic/backport-5.15/822-v5.17-Bluetooth-btusb-Fix-application-of-sizeof-to-pointer.patch @@ -18,7 +18,7 @@ Signed-off-by: Marcel Holtmann --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c -@@ -2268,7 +2268,7 @@ static int btusb_set_bdaddr_mtk(struct h +@@ -2273,7 +2273,7 @@ static int btusb_set_bdaddr_mtk(struct h struct sk_buff *skb; long ret; diff --git a/target/linux/generic/backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch b/target/linux/generic/backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch deleted file mode 100644 index 1fbf78dd3d8..00000000000 --- a/target/linux/generic/backport-5.15/883-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 1e24c54da257ab93cff5826be8a793b014a5dc9c Mon Sep 17 00:00:00 2001 -From: Hauke Mehrtens -Date: Mon, 21 Nov 2022 01:22:01 +0100 -Subject: ca8210: Fix crash by zero initializing data - -The struct cas_control embeds multiple generic SPI structures and we -have to make sure these structures are initialized to default values. -This driver does not set all attributes. When using kmalloc before some -attributes were not initialized and contained random data which caused -random crashes at bootup. - -Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") -Signed-off-by: Hauke Mehrtens -Link: https://lore.kernel.org/r/20221121002201.1339636-1-hauke@hauke-m.de -Signed-off-by: Stefan Schmidt ---- - drivers/net/ieee802154/ca8210.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/net/ieee802154/ca8210.c -+++ b/drivers/net/ieee802154/ca8210.c -@@ -927,7 +927,7 @@ static int ca8210_spi_transfer( - - dev_dbg(&spi->dev, "%s called\n", __func__); - -- cas_ctl = kmalloc(sizeof(*cas_ctl), GFP_ATOMIC); -+ cas_ctl = kzalloc(sizeof(*cas_ctl), GFP_ATOMIC); - if (!cas_ctl) - return -ENOMEM; - diff --git a/target/linux/layerscape/patches-5.15/701-staging-add-fsl_ppfe-driver.patch b/target/linux/layerscape/patches-5.15/701-staging-add-fsl_ppfe-driver.patch index 5315f1f1b62..00c8e869beb 100644 --- a/target/linux/layerscape/patches-5.15/701-staging-add-fsl_ppfe-driver.patch +++ b/target/linux/layerscape/patches-5.15/701-staging-add-fsl_ppfe-driver.patch @@ -854,7 +854,7 @@ Signed-off-by: Pawel Dembicki +}; --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -7529,6 +7529,14 @@ F: drivers/ptp/ptp_qoriq.c +@@ -7526,6 +7526,14 @@ F: drivers/ptp/ptp_qoriq.c F: drivers/ptp/ptp_qoriq_debugfs.c F: include/linux/fsl/ptp_qoriq.h diff --git a/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support.patch b/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support.patch index f10d57a7853..04da1765f37 100644 --- a/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support.patch +++ b/target/linux/mediatek/patches-5.15/213-spi-mediatek-add-mt7986-spi-support.patch @@ -816,7 +816,7 @@ Signed-off-by: Sam Shih err_put_master: spi_master_put(master); -@@ -917,8 +1296,10 @@ static int mtk_spi_remove(struct platfor +@@ -920,8 +1299,10 @@ static int mtk_spi_remove(struct platfor mtk_spi_reset(mdata); @@ -826,9 +826,9 @@ Signed-off-by: Sam Shih + clk_unprepare(mdata->spi_hclk); + } - return 0; - } -@@ -934,8 +1315,10 @@ static int mtk_spi_suspend(struct device + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); +@@ -940,8 +1321,10 @@ static int mtk_spi_suspend(struct device if (ret) return ret; @@ -840,7 +840,7 @@ Signed-off-by: Sam Shih return ret; } -@@ -952,11 +1335,20 @@ static int mtk_spi_resume(struct device +@@ -958,11 +1341,20 @@ static int mtk_spi_resume(struct device dev_err(dev, "failed to enable spi_clk (%d)\n", ret); return ret; } @@ -862,7 +862,7 @@ Signed-off-by: Sam Shih return ret; } -@@ -968,10 +1360,13 @@ static int mtk_spi_runtime_suspend(struc +@@ -974,10 +1366,13 @@ static int mtk_spi_runtime_suspend(struc struct spi_master *master = dev_get_drvdata(dev); struct mtk_spi *mdata = spi_master_get_devdata(master); @@ -878,7 +878,7 @@ Signed-off-by: Sam Shih return 0; } -@@ -982,13 +1377,31 @@ static int mtk_spi_runtime_resume(struct +@@ -988,13 +1383,31 @@ static int mtk_spi_runtime_resume(struct struct mtk_spi *mdata = spi_master_get_devdata(master); int ret; diff --git a/target/linux/mvebu/patches-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch b/target/linux/mvebu/patches-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch index f2f4748e001..b8accf180bb 100644 --- a/target/linux/mvebu/patches-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch +++ b/target/linux/mvebu/patches-5.15/907-MAINTAINERS-Add-an-entry-for-the-IEI-WT61P803-PUZZLE.patch @@ -16,7 +16,7 @@ Cc: Robert Marko --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -9066,6 +9066,22 @@ F: include/net/nl802154.h +@@ -9063,6 +9063,22 @@ F: include/net/nl802154.h F: net/ieee802154/ F: net/mac802154/ From 90120a1552ba5e55c4a03aca1920831116ab50a9 Mon Sep 17 00:00:00 2001 From: John Audia Date: Wed, 14 Dec 2022 10:13:21 -0500 Subject: [PATCH 026/110] kernel: bump 5.10 to 5.10.159 Removed upstreamed: backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch[1] All patches automatically rebased. 1. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v5.10.159&id=0a8e66e375736ea12c11f0ef238ba2a8efec460b Build system: x86_64 Build-tested: ramips/tplink_archer-a6-v3 Run-tested: ramips/tplink_archer-a6-v3 Signed-off-by: John Audia --- include/kernel-5.10 | 4 +-- ...the-dst-buffer-to-of_get_mac_address.patch | 2 +- ...t-skb-offload_fwd_mark-when-not-offl.patch | 6 ++-- ...-Fix-crash-by-zero-initializing-data.patch | 30 ------------------- ...-host-by-default-on-rk3399-rock-pi-4.patch | 2 +- 5 files changed, 7 insertions(+), 37 deletions(-) delete mode 100644 target/linux/generic/backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch diff --git a/include/kernel-5.10 b/include/kernel-5.10 index b7179b8567e..bd40dc0f407 100644 --- a/include/kernel-5.10 +++ b/include/kernel-5.10 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.10 = .158 -LINUX_KERNEL_HASH-5.10.158 = 1e0a24bb5510caa18b3601b25e12cc2a1ce123948de551f4f2cdbb40aea707e7 +LINUX_VERSION-5.10 = .159 +LINUX_KERNEL_HASH-5.10.159 = 1ba9bf57b6bf36d76447d5044b80b746cb5fd61d981c811603dc763b7789cea7 diff --git a/target/linux/generic/backport-5.10/732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch b/target/linux/generic/backport-5.10/732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch index 83aae57d280..928e1ca0dca 100644 --- a/target/linux/generic/backport-5.10/732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch +++ b/target/linux/generic/backport-5.10/732-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch @@ -222,7 +222,7 @@ Signed-off-by: David S. Miller mdio = of_find_node_by_phandle(*ph); --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c -@@ -1449,10 +1449,10 @@ static int greth_of_probe(struct platfor +@@ -1450,10 +1450,10 @@ static int greth_of_probe(struct platfor break; } if (i == 6) { diff --git a/target/linux/generic/backport-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch b/target/linux/generic/backport-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch index ab4fdf85093..51f87904ef1 100644 --- a/target/linux/generic/backport-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch +++ b/target/linux/generic/backport-5.10/744-v5.15-net-dsa-don-t-set-skb-offload_fwd_mark-when-not-offl.patch @@ -82,9 +82,9 @@ Signed-off-by: David S. Miller memmove(skb->data - ETH_HLEN, --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c -@@ -24,7 +24,7 @@ static struct sk_buff *ksz_common_rcv(st - - pskb_trim_rcsum(skb, skb->len - len); +@@ -25,7 +25,7 @@ static struct sk_buff *ksz_common_rcv(st + if (pskb_trim_rcsum(skb, skb->len - len)) + return NULL; - skb->offload_fwd_mark = true; + dsa_default_offload_fwd_mark(skb); diff --git a/target/linux/generic/backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch b/target/linux/generic/backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch deleted file mode 100644 index 3f14988d9a4..00000000000 --- a/target/linux/generic/backport-5.10/888-v6.1-ca8210-Fix-crash-by-zero-initializing-data.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 1e24c54da257ab93cff5826be8a793b014a5dc9c Mon Sep 17 00:00:00 2001 -From: Hauke Mehrtens -Date: Mon, 21 Nov 2022 01:22:01 +0100 -Subject: ca8210: Fix crash by zero initializing data - -The struct cas_control embeds multiple generic SPI structures and we -have to make sure these structures are initialized to default values. -This driver does not set all attributes. When using kmalloc before some -attributes were not initialized and contained random data which caused -random crashes at bootup. - -Fixes: ded845a781a5 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") -Signed-off-by: Hauke Mehrtens -Link: https://lore.kernel.org/r/20221121002201.1339636-1-hauke@hauke-m.de -Signed-off-by: Stefan Schmidt ---- - drivers/net/ieee802154/ca8210.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/net/ieee802154/ca8210.c -+++ b/drivers/net/ieee802154/ca8210.c -@@ -926,7 +926,7 @@ static int ca8210_spi_transfer( - - dev_dbg(&spi->dev, "%s called\n", __func__); - -- cas_ctl = kmalloc(sizeof(*cas_ctl), GFP_ATOMIC); -+ cas_ctl = kzalloc(sizeof(*cas_ctl), GFP_ATOMIC); - if (!cas_ctl) - return -ENOMEM; - diff --git a/target/linux/rockchip/patches-5.10/104-rockchip-use-USB-host-by-default-on-rk3399-rock-pi-4.patch b/target/linux/rockchip/patches-5.10/104-rockchip-use-USB-host-by-default-on-rk3399-rock-pi-4.patch index da87227564d..56166783a58 100644 --- a/target/linux/rockchip/patches-5.10/104-rockchip-use-USB-host-by-default-on-rk3399-rock-pi-4.patch +++ b/target/linux/rockchip/patches-5.10/104-rockchip-use-USB-host-by-default-on-rk3399-rock-pi-4.patch @@ -21,7 +21,7 @@ Signed-off-by: Heiko Stuebner --- a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dtsi -@@ -680,7 +680,7 @@ +@@ -679,7 +679,7 @@ &usbdrd_dwc3_0 { status = "okay"; From 232879a7b7f8794c5138a66d60ba860dea1d45f3 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Thu, 15 Dec 2022 14:24:25 +0100 Subject: [PATCH 027/110] firmware-utils: bump to git HEAD Adds support for building TP-Link CPE605v1 factory images bd856eff4850 tplink-safeloader: add TP-Link CPE605 v1 Support Signed-off-by: Sander Vanheule --- tools/firmware-utils/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 731bc8a04c5..34b8943d5f4 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -11,9 +11,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware-utils.git -PKG_SOURCE_DATE:=2022-09-12 -PKG_SOURCE_VERSION:=0c92b20ad488a4fb5fb290f6d1b893df45761275 -PKG_MIRROR_HASH:=2cfd36e749b51d539cffe22c77a4c3ccd3bac2eca482affe23e1cab539e8bd16 +PKG_SOURCE_DATE:=2022-12-15 +PKG_SOURCE_VERSION:=bd856eff48509fa4511169ee55f639ede8dd4ac6 +PKG_MIRROR_HASH:=5765389fdb19e0ea2d8f1f586d60580475f6a9917a46777200a3a011f5f654fa include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/cmake.mk From 1bef8d62d42d1dfbda04e8280930488b2661f0fa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 14 Dec 2022 01:15:48 +0100 Subject: [PATCH 028/110] bcm53xx: Patch to support BCMA rev 11 A backported patch to the BCMA driver necessary to support the DWL-8610AP and DIR-890L. Patch will be in upstream v6.2. Signed-off-by: Linus Walleij --- .../039-v6.2-bcma-support-SPROM-rev-11.patch | 28 +++++++++++++++++++ .../036-v6.2-bcma-support-SPROM-rev-11.patch | 28 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 target/linux/bcm53xx/patches-5.10/039-v6.2-bcma-support-SPROM-rev-11.patch create mode 100644 target/linux/bcm53xx/patches-5.15/036-v6.2-bcma-support-SPROM-rev-11.patch diff --git a/target/linux/bcm53xx/patches-5.10/039-v6.2-bcma-support-SPROM-rev-11.patch b/target/linux/bcm53xx/patches-5.10/039-v6.2-bcma-support-SPROM-rev-11.patch new file mode 100644 index 00000000000..7c9eb5537e1 --- /dev/null +++ b/target/linux/bcm53xx/patches-5.10/039-v6.2-bcma-support-SPROM-rev-11.patch @@ -0,0 +1,28 @@ +From b9457a04eb89645049fdf427c13e6a18d5501895 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Tue, 11 Oct 2022 14:24:40 +0200 +Subject: [PATCH] bcma: support SPROM rev 11 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Rev 11 works fine for me to set the MAC address of gmac0 and +gmac1 in the D-Link DWL-8610AP. + +Cc: Rafał Miłecki +Signed-off-by: Linus Walleij +--- + drivers/bcma/sprom.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/bcma/sprom.c ++++ b/drivers/bcma/sprom.c +@@ -165,7 +165,7 @@ static int bcma_sprom_valid(struct bcma_ + return err; + + revision = sprom[words - 1] & SSB_SPROM_REVISION_REV; +- if (revision != 8 && revision != 9 && revision != 10) { ++ if (revision < 8 || revision > 11) { + pr_err("Unsupported SPROM revision: %d\n", revision); + return -ENOENT; + } diff --git a/target/linux/bcm53xx/patches-5.15/036-v6.2-bcma-support-SPROM-rev-11.patch b/target/linux/bcm53xx/patches-5.15/036-v6.2-bcma-support-SPROM-rev-11.patch new file mode 100644 index 00000000000..7c9eb5537e1 --- /dev/null +++ b/target/linux/bcm53xx/patches-5.15/036-v6.2-bcma-support-SPROM-rev-11.patch @@ -0,0 +1,28 @@ +From b9457a04eb89645049fdf427c13e6a18d5501895 Mon Sep 17 00:00:00 2001 +From: Linus Walleij +Date: Tue, 11 Oct 2022 14:24:40 +0200 +Subject: [PATCH] bcma: support SPROM rev 11 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Rev 11 works fine for me to set the MAC address of gmac0 and +gmac1 in the D-Link DWL-8610AP. + +Cc: Rafał Miłecki +Signed-off-by: Linus Walleij +--- + drivers/bcma/sprom.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/bcma/sprom.c ++++ b/drivers/bcma/sprom.c +@@ -165,7 +165,7 @@ static int bcma_sprom_valid(struct bcma_ + return err; + + revision = sprom[words - 1] & SSB_SPROM_REVISION_REV; +- if (revision != 8 && revision != 9 && revision != 10) { ++ if (revision < 8 || revision > 11) { + pr_err("Unsupported SPROM revision: %d\n", revision); + return -ENOENT; + } From 708ea56fb9e2b4ab77e726085d572bf80a4cc76a Mon Sep 17 00:00:00 2001 From: Johannes Heimansberg Date: Mon, 12 Dec 2022 15:39:58 +0100 Subject: [PATCH 029/110] bcm27xx: bcm2711: add kmod-r8169 Some carrier boards [1][2] for the Raspberry Pi CM4 that are specifically designed to be used as routers come with secondary NICs using a Realtek RTL8111 Gigabit Ethernet chip. When using such a board as a router with OpenWrt, it is very helpful when both NICs are working by default. Since the Raspberry Pi 4 and the CM4 have plenty of disk space, it should cause no harm to include the kmod-r8169. [1] https://wiki.dfrobot.com/Compute_Module_4_IoT_Router_Board_Mini_SKU_DFR0767 [2] https://www.waveshare.com/wiki/CM4-DUAL-ETH-MINI Signed-off-by: Johannes Heimansberg (r8169 should pull in the necessary dependencies.) Signed-off-by: Christian Lamparter --- target/linux/bcm27xx/image/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/linux/bcm27xx/image/Makefile b/target/linux/bcm27xx/image/Makefile index 1334386c574..999361e8cde 100644 --- a/target/linux/bcm27xx/image/Makefile +++ b/target/linux/bcm27xx/image/Makefile @@ -163,7 +163,8 @@ define Device/rpi-4 cypress-firmware-43455-sdio \ brcmfmac-nvram-43455-sdio \ kmod-brcmfmac wpad-basic-wolfssl \ - kmod-usb-net-lan78xx + kmod-usb-net-lan78xx \ + kmod-r8169 IMAGE/sysupgrade.img.gz := boot-common | boot-2711 | sdcard-img | gzip | append-metadata IMAGE/factory.img.gz := boot-common | boot-2711 | sdcard-img | gzip endef From 1b125aabf4c5deb54bc5ea7f1bc3f8516f5ff79c Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 09:20:22 +0100 Subject: [PATCH 030/110] ath79: convert Araknis AN-300-AP-I-N WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Signed-off-by: Nick Hainke --- .../dts/ar9344_araknis_an-300-ap-i-n.dts | 19 ++++++++++++------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 1 - 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts b/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts index a886b4c0010..07dc8da5063 100644 --- a/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts +++ b/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts @@ -87,11 +87,10 @@ ath9k: wifi@0,0,0 { compatible = "pci168c,0030"; reg = <0x0 0 0 0 0>; - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <1>; qca,disable-5ghz; - qca,no-eeprom; #gpio-cells = <2>; gpio-controller; }; @@ -102,10 +101,8 @@ qca,disable-2ghz; - mtd-cal-data = <&art 0x1000>; - - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <2>; }; @@ -117,4 +114,12 @@ macaddr_art_0: macaddr@0 { reg = <0x0 0x6>; }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index d6d5b257ffe..52f015ed5fd 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -83,7 +83,6 @@ case "$FIRMWARE" in ;; "ath9k-eeprom-pci-0000:00:00.0.bin") case $board in - araknis,an-300-ap-i-n|\ atheros,db120|\ engenius,eap600|\ engenius,ecb600|\ From 21495c92dc176a4615e4ecb7a627fdcb31c3aec5 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 09:29:57 +0100 Subject: [PATCH 031/110] ath79: convert Atheros DB120 WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Signed-off-by: Nick Hainke (merged art-node back into partition-node) Signed-off-by: Christian Lamparter --- .../linux/ath79/dts/ar9344_atheros_db120.dts | 42 +++++++++++-------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 1 - 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_atheros_db120.dts b/target/linux/ath79/dts/ar9344_atheros_db120.dts index f0173ebdc79..8811d4f4576 100644 --- a/target/linux/ath79/dts/ar9344_atheros_db120.dts +++ b/target/linux/ath79/dts/ar9344_atheros_db120.dts @@ -135,10 +135,30 @@ reg = <0x7e0000 0x010000>; }; - art: partition@7f0000 { + partition@7f0000 { label = "art"; reg = <0x7f0000 0x010000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_art_6: macaddr@6 { + reg = <0x6 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; }; }; @@ -204,7 +224,8 @@ ath9k: wifi@0,0 { compatible = "pci168c,0030"; reg = <0x0000 0 0 0 0>; - qca,no-eeprom; + nvmem-cells = <&calibration_art_5000>; + nvmem-cell-names = "calibration"; ieee80211-freq-limit = <4900000 5990000>; #gpio-cells = <2>; gpio-controller; @@ -214,7 +235,8 @@ &wmac { status = "okay"; - mtd-cal-data = <&art 0x1000>; + nvmem-cells = <&calibration_art_1000>; + nvmem-cell-names = "calibration"; }; &usb { @@ -224,17 +246,3 @@ &usb_phy { status = "okay"; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; - - macaddr_art_6: macaddr@6 { - reg = <0x6 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 52f015ed5fd..0001fa15907 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -83,7 +83,6 @@ case "$FIRMWARE" in ;; "ath9k-eeprom-pci-0000:00:00.0.bin") case $board in - atheros,db120|\ engenius,eap600|\ engenius,ecb600|\ mercury,mw4530r-v1|\ From 4845b60525e158f72da56a7468dda9243820c5ea Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 09:44:55 +0100 Subject: [PATCH 032/110] ath79: convert boards based on senao_ap-dual.dtsi WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Signed-off-by: Nick Hainke --- .../linux/ath79/dts/ar9344_engenius_eap600.dts | 16 ++++++++++++---- .../linux/ath79/dts/ar9344_engenius_ecb600.dts | 16 ++++++++++++---- .../linux/ath79/dts/ar9344_senao_ap-dual.dtsi | 3 --- .../linux/ath79/dts/ar9344_watchguard_ap100.dts | 17 +++++++++++++++-- .../linux/ath79/dts/ar9344_watchguard_ap200.dts | 16 ++++++++++++---- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 4 ---- 6 files changed, 51 insertions(+), 21 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_engenius_eap600.dts b/target/linux/ath79/dts/ar9344_engenius_eap600.dts index 50e0674be48..618660802c0 100644 --- a/target/linux/ath79/dts/ar9344_engenius_eap600.dts +++ b/target/linux/ath79/dts/ar9344_engenius_eap600.dts @@ -36,14 +36,14 @@ &pcie { wifi@0,0,0 { - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; }; }; &wmac { - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <(-1)>; }; @@ -55,4 +55,12 @@ macaddr_art_0: macaddr@0 { reg = <0x0 0x6>; }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; diff --git a/target/linux/ath79/dts/ar9344_engenius_ecb600.dts b/target/linux/ath79/dts/ar9344_engenius_ecb600.dts index ad32dc9e392..5f6ffb130d7 100644 --- a/target/linux/ath79/dts/ar9344_engenius_ecb600.dts +++ b/target/linux/ath79/dts/ar9344_engenius_ecb600.dts @@ -30,15 +30,15 @@ &pcie { wifi@0,0,0 { - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <(-2)>; }; }; &wmac { - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <(-1)>; }; @@ -50,4 +50,12 @@ macaddr_art_0: macaddr@0 { reg = <0x0 0x6>; }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; diff --git a/target/linux/ath79/dts/ar9344_senao_ap-dual.dtsi b/target/linux/ath79/dts/ar9344_senao_ap-dual.dtsi index c963138f6a1..c0e95065d5a 100644 --- a/target/linux/ath79/dts/ar9344_senao_ap-dual.dtsi +++ b/target/linux/ath79/dts/ar9344_senao_ap-dual.dtsi @@ -63,7 +63,6 @@ compatible = "pci168c,0030"; reg = <0x0 0 0 0 0>; ieee80211-freq-limit = <2402000 2482000>; - qca,no-eeprom; #gpio-cells = <2>; gpio-controller; }; @@ -73,6 +72,4 @@ status = "okay"; ieee80211-freq-limit = <4900000 5990000>; - - mtd-cal-data = <&art 0x1000>; }; diff --git a/target/linux/ath79/dts/ar9344_watchguard_ap100.dts b/target/linux/ath79/dts/ar9344_watchguard_ap100.dts index d2ebe14979d..3cd3fb70e60 100644 --- a/target/linux/ath79/dts/ar9344_watchguard_ap100.dts +++ b/target/linux/ath79/dts/ar9344_watchguard_ap100.dts @@ -63,13 +63,18 @@ &pcie { status = "disabled"; + + wifi@0,0,0 { + nvmem-cells = <&calibration_art_5000>; + nvmem-cell-names = "calibration"; + }; }; &wmac { /delete-property/ qca,disable-2ghz; - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <(-2)>; }; @@ -81,4 +86,12 @@ macaddr_art_0: macaddr@0 { reg = <0x0 0x6>; }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; diff --git a/target/linux/ath79/dts/ar9344_watchguard_ap200.dts b/target/linux/ath79/dts/ar9344_watchguard_ap200.dts index 0cd966476c2..88c7637fc9b 100644 --- a/target/linux/ath79/dts/ar9344_watchguard_ap200.dts +++ b/target/linux/ath79/dts/ar9344_watchguard_ap200.dts @@ -63,15 +63,15 @@ &pcie { wifi@0,0,0 { - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <(-1)>; }; }; &wmac { - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <(-2)>; }; @@ -83,4 +83,12 @@ macaddr_art_0: macaddr@0 { reg = <0x0 0x6>; }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 0001fa15907..9ded116e9dd 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -83,13 +83,9 @@ case "$FIRMWARE" in ;; "ath9k-eeprom-pci-0000:00:00.0.bin") case $board in - engenius,eap600|\ - engenius,ecb600|\ mercury,mw4530r-v1|\ ocedo,raccoon|\ ubnt,unifi-ap-pro|\ - watchguard,ap100|\ - watchguard,ap200|\ winchannel,wb2000) caldata_extract "art" 0x5000 0x440 ;; From 783936c1f739b506cdd0f38376ac1fca96f0a418 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 09:50:39 +0100 Subject: [PATCH 033/110] ath79: Mercury MW4530R v1 already uses nvmem-cells Remove the caldata extraction in userspace. The board already uses nvmem-cells since commit e354b01baf88 ("ath79: calibrate all ar9344 tl-WDRxxxx with nvmem") Signed-off-by: Nick Hainke --- .../generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom | 1 - 1 file changed, 1 deletion(-) diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 9ded116e9dd..78d6f03876a 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -83,7 +83,6 @@ case "$FIRMWARE" in ;; "ath9k-eeprom-pci-0000:00:00.0.bin") case $board in - mercury,mw4530r-v1|\ ocedo,raccoon|\ ubnt,unifi-ap-pro|\ winchannel,wb2000) From f63cf33aa74ca33d944f11fdb15b162ee2cd704d Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 09:54:57 +0100 Subject: [PATCH 034/110] ath79: convert OCEDO Raccoon WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Signed-off-by: Nick Hainke (merged art into partition node, removed stale uboot label) Signed-off-by: Christian Lamparter --- .../linux/ath79/dts/ar9344_ocedo_raccoon.dts | 56 ++++++++++--------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 1 - 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_ocedo_raccoon.dts b/target/linux/ath79/dts/ar9344_ocedo_raccoon.dts index 8ac035f1258..8678315a44a 100644 --- a/target/linux/ath79/dts/ar9344_ocedo_raccoon.dts +++ b/target/linux/ath79/dts/ar9344_ocedo_raccoon.dts @@ -79,7 +79,7 @@ #address-cells = <1>; #size-cells = <1>; - uboot: partition@0 { + partition@0 { label = "u-boot"; reg = <0x000000 0x040000>; read-only; @@ -114,10 +114,34 @@ read-only; }; - art: partition@ff0000 { + partition@ff0000 { label = "art"; reg = <0xff0000 0x010000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_art_6: macaddr@6 { + reg = <0x6 0x6>; + }; + + macaddr_art_c: macaddr@c { + reg = <0xc 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; }; }; @@ -129,9 +153,8 @@ ath9k: wifi@0,0 { compatible = "pci168c,0030"; reg = <0x0000 0 0 0 0>; - nvmem-cells = <&macaddr_art_c>; - nvmem-cell-names = "mac-address"; - qca,no-eeprom; + nvmem-cells = <&macaddr_art_c>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; #gpio-cells = <2>; gpio-controller; }; @@ -140,9 +163,8 @@ &wmac { status = "okay"; - mtd-cal-data = <&art 0x1000>; - nvmem-cells = <&macaddr_art_6>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_6>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; }; &mdio0 { @@ -175,21 +197,3 @@ txd-delay = <0>; }; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; - - macaddr_art_6: macaddr@6 { - reg = <0x6 0x6>; - }; - - macaddr_art_c: macaddr@c { - reg = <0xc 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 78d6f03876a..825e4ff99ce 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -83,7 +83,6 @@ case "$FIRMWARE" in ;; "ath9k-eeprom-pci-0000:00:00.0.bin") case $board in - ocedo,raccoon|\ ubnt,unifi-ap-pro|\ winchannel,wb2000) caldata_extract "art" 0x5000 0x440 From fd456106aa8730bb9d407b66d1eef5e26b5cb63e Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 09:57:28 +0100 Subject: [PATCH 035/110] ath79: convert Ubiquiti UniFi AP Pro WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Signed-off-by: Nick Hainke (merged art node back into partition-node) Signed-off-by: Christian Lamparter --- .../ath79/dts/ar9344_ubnt_unifi-ap-pro.dts | 34 ++++++++++++------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 1 - 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_ubnt_unifi-ap-pro.dts b/target/linux/ath79/dts/ar9344_ubnt_unifi-ap-pro.dts index 8471be9b6b8..55626c42995 100644 --- a/target/linux/ath79/dts/ar9344_ubnt_unifi-ap-pro.dts +++ b/target/linux/ath79/dts/ar9344_ubnt_unifi-ap-pro.dts @@ -100,10 +100,26 @@ read-only; }; - art: partition@ff0000 { + partition@ff0000 { label = "art"; reg = <0xff0000 0x10000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; }; }; @@ -115,14 +131,16 @@ wifi@0,0 { compatible = "pci168c,0033"; reg = <0 0 0 0 0>; - qca,no-eeprom; + nvmem-cells = <&calibration_art_5000>; + nvmem-cell-names = "calibration"; }; }; &wmac { status = "okay"; - mtd-cal-data = <&art 0x1000>; + nvmem-cells = <&calibration_art_1000>; + nvmem-cell-names = "calibration"; }; &mdio0 { @@ -150,13 +168,3 @@ phy-mode = "rgmii"; phy-handle = <&phy0>; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 825e4ff99ce..a91885c3fe2 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -83,7 +83,6 @@ case "$FIRMWARE" in ;; "ath9k-eeprom-pci-0000:00:00.0.bin") case $board in - ubnt,unifi-ap-pro|\ winchannel,wb2000) caldata_extract "art" 0x5000 0x440 ;; From 08c114ee16ae5aa3c07241c36e6f76cc7abcd137 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 10:08:39 +0100 Subject: [PATCH 036/110] ath79: convert Winchannel WB2000 WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Signed-off-by: Nick Hainke (removed mtd-cal-data property, merged art + addr nodes back into partition) Signed-off-by: Christian Lamparter --- .../ath79/dts/ar9344_winchannel_wb2000.dts | 43 +++++++++++-------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 3 -- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_winchannel_wb2000.dts b/target/linux/ath79/dts/ar9344_winchannel_wb2000.dts index cb595594264..cbde191ac78 100644 --- a/target/linux/ath79/dts/ar9344_winchannel_wb2000.dts +++ b/target/linux/ath79/dts/ar9344_winchannel_wb2000.dts @@ -119,16 +119,36 @@ read-only; }; - art: partition@fe0000 { + partition@fe0000 { label = "art"; reg = <0xfe0000 0x10000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; - addr: partition@ff0000 { + partition@ff0000 { label = "addr"; reg = <0xff0000 0x10000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_addr_0: macaddr@0 { + reg = <0x0 0x6>; + }; }; }; }; @@ -140,9 +160,8 @@ ath9k: wifi@0,0 { compatible = "pci168c,0030"; reg = <0x0000 0 0 0 0>; - qca,no-eeprom; - nvmem-cells = <&macaddr_addr_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_addr_0>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <0x10>; #gpio-cells = <2>; gpio-controller; @@ -160,9 +179,8 @@ &wmac { status = "okay"; - mtd-cal-data = <&art 0x1000>; - nvmem-cells = <&macaddr_addr_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_addr_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; }; &mdio0 { @@ -193,12 +211,3 @@ }; }; -&addr { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_addr_0: macaddr@0 { - reg = <0x0 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index a91885c3fe2..9640baac172 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -83,9 +83,6 @@ case "$FIRMWARE" in ;; "ath9k-eeprom-pci-0000:00:00.0.bin") case $board in - winchannel,wb2000) - caldata_extract "art" 0x5000 0x440 - ;; avm,fritz300e) caldata_extract_reverse "urloader" 0x1541 0x440 ;; From ae5cb164cd886db5f24d2c9b232d7430fe3d9f3e Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 10:41:47 +0100 Subject: [PATCH 037/110] ath79: switch to upstream ieee80211-freq-limit for Araknis AN-300-AP-I-N Commit 4c8dd973ef8e ("ath9k: OF: qca,disable-(2|5)ghz => ieee80211-freq-limit") removed "qca,disable-5ghz" and "qca,disable-2ghz". Switch to upstream ieee80211-freq-limit. Signed-off-by: Nick Hainke --- target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts b/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts index 07dc8da5063..ac39a626790 100644 --- a/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts +++ b/target/linux/ath79/dts/ar9344_araknis_an-300-ap-i-n.dts @@ -90,7 +90,7 @@ nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <1>; - qca,disable-5ghz; + ieee80211-freq-limit = <2402000 2482000>; #gpio-cells = <2>; gpio-controller; }; @@ -99,7 +99,7 @@ &wmac { status = "okay"; - qca,disable-2ghz; + ieee80211-freq-limit = <4900000 5990000>; nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; nvmem-cell-names = "mac-address", "calibration"; From 4146701469ec53c09badeb6382170ae51fbd2aad Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 10:45:24 +0100 Subject: [PATCH 038/110] ath79: replace reference to legacy qca,disable-2ghz in WatchGuard AP100 Commit 4c8dd973ef8e ("ath9k: OF: qca,disable-(2|5)ghz => ieee80211-freq-limit") removed "qca,disable-5ghz" and "qca,disable-2ghz". Signed-off-by: Nick Hainke (converted patch to remove the ieee80211-freq-limit property instead) Signed-off-by: Christian Lamparter --- target/linux/ath79/dts/ar9344_watchguard_ap100.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/ath79/dts/ar9344_watchguard_ap100.dts b/target/linux/ath79/dts/ar9344_watchguard_ap100.dts index 3cd3fb70e60..0ab10c36ce2 100644 --- a/target/linux/ath79/dts/ar9344_watchguard_ap100.dts +++ b/target/linux/ath79/dts/ar9344_watchguard_ap100.dts @@ -71,7 +71,7 @@ }; &wmac { - /delete-property/ qca,disable-2ghz; + /delete-property/ ieee80211-freq-limit; nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; nvmem-cell-names = "mac-address", "calibration"; From 0f227720f99e340734521d8326c44d9ae1f40fef Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 15 Dec 2022 15:20:26 +0100 Subject: [PATCH 039/110] ath79: fix dtc warnings in eap1200h |109.3-19: Warning (reg_format): macaddr@0:reg:property has invalid length (8 bytes) |113.3-24: Warning (reg_format): calibration@1000:reg: property has invalid length (8 bytes) |117.3-24: Warning (reg_format): calibration@5000:reg: property has invalid length (8 bytes) also integrate the art-nodes nodes back into the partition-subnode and change the calibration labels to match what everyone else is doing. Signed-off-by: Christian Lamparter --- .../ath79/dts/qca9557_engenius_eap1200h.dts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/target/linux/ath79/dts/qca9557_engenius_eap1200h.dts b/target/linux/ath79/dts/qca9557_engenius_eap1200h.dts index 13c03e717a9..beb3e5ee075 100644 --- a/target/linux/ath79/dts/qca9557_engenius_eap1200h.dts +++ b/target/linux/ath79/dts/qca9557_engenius_eap1200h.dts @@ -57,10 +57,26 @@ }; &partitions { - art: partition@ff0000 { + partition@ff0000 { label = "art"; reg = <0xff0000 0x010000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x844>; + }; }; }; @@ -89,7 +105,7 @@ &wmac { status = "okay"; - nvmem-cells = <&macaddr_art_0>, <&calibration_ath9k>; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <1>; }; @@ -97,23 +113,7 @@ &ath10k { status = "okay"; - nvmem-cells = <&macaddr_art_0>, <&calibration_ath10k>; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <2>; }; - -&art { - compatible = "nvmem-cells"; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; - - calibration_ath9k: calibration@1000 { - reg = <0x1000 0x440>; - }; - - calibration_ath10k: calibration@5000 { - reg = <0x5000 0x844>; - }; -}; From 0dacf144952a101e00e3fea456ab17be1829e217 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 15 Dec 2022 15:25:45 +0100 Subject: [PATCH 040/110] ath79: fix dtc warnings in eap1750h |109.3-19: Warning (reg_format): macaddr@0:reg:property has invalid length (8 bytes) |113.3-24: Warning (reg_format): calibration@1000:reg: property has invalid length (8 bytes) |117.3-24: Warning (reg_format): calibration@5000:reg: property has invalid length (8 bytes) also integrate the art-nodes nodes back into the partition-subnode and change the calibration labels to match what everyone else is doing. Signed-off-by: Christian Lamparter --- .../ath79/dts/qca9558_engenius_eap1750h.dts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/target/linux/ath79/dts/qca9558_engenius_eap1750h.dts b/target/linux/ath79/dts/qca9558_engenius_eap1750h.dts index 8a642784269..605030455c4 100644 --- a/target/linux/ath79/dts/qca9558_engenius_eap1750h.dts +++ b/target/linux/ath79/dts/qca9558_engenius_eap1750h.dts @@ -57,10 +57,26 @@ }; &partitions { - art: partition@ff0000 { + partition@ff0000 { label = "art"; reg = <0xff0000 0x010000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x844>; + }; }; }; @@ -89,7 +105,7 @@ &wmac { status = "okay"; - nvmem-cells = <&macaddr_art_0>, <&calibration_ath9k>; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <1>; }; @@ -97,23 +113,7 @@ &ath10k { status = "okay"; - nvmem-cells = <&macaddr_art_0>, <&calibration_ath10k>; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <2>; }; - -&art { - compatible = "nvmem-cells"; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; - - calibration_ath9k: calibration@1000 { - reg = <0x1000 0x440>; - }; - - calibration_ath10k: calibration@5000 { - reg = <0x5000 0x844>; - }; -}; From d8da5002a2429edb8a051fb45e258596704b11ab Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 15 Dec 2022 15:29:04 +0100 Subject: [PATCH 041/110] ath79: fix dtc warnings in ruckus zf7372 |:69.4-14: Warning (reg_format): beamforming-2g-gpio@0:reg: property has invalid length (4 bytes) |:85.4-14: Warning (reg_format): beamforming-5g-gpio@0:reg: property has invalid length (4 bytes) Signed-off-by: Christian Lamparter --- target/linux/ath79/dts/ar9344_ruckus_zf7372.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/linux/ath79/dts/ar9344_ruckus_zf7372.dts b/target/linux/ath79/dts/ar9344_ruckus_zf7372.dts index 4bf7276db5b..2cf72409311 100644 --- a/target/linux/ath79/dts/ar9344_ruckus_zf7372.dts +++ b/target/linux/ath79/dts/ar9344_ruckus_zf7372.dts @@ -63,6 +63,8 @@ mosi-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>; sck-gpios = <&gpio 14 GPIO_ACTIVE_HIGH>; num-chipselects = <0>; + #address-cells = <1>; + #size-cells = <0>; beamforming-2g-gpio@0 { compatible = "fairchild,74hc595"; @@ -79,6 +81,8 @@ mosi-gpios = <&ath9k 15 GPIO_ACTIVE_HIGH>; sck-gpios = <&ath9k 14 GPIO_ACTIVE_HIGH>; num-chipselects = <0>; + #address-cells = <1>; + #size-cells = <0>; beamforming-5g-gpio@0 { compatible = "fairchild,74hc595"; From fef55d5ffd31bd6e0d157df784f47fe8ce0f1494 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Thu, 15 Dec 2022 17:44:38 +0100 Subject: [PATCH 042/110] firmware-utils: fix archive checksum PKG_SOURCE_DATE was modified after updating PKG_MIRROR_HASH, causing the latter to change. This results in a warning during builds and rejected downloads. Fixes: 232879a7b7f8 ("firmware-utils: bump to git HEAD") Signed-off-by: Sander Vanheule --- tools/firmware-utils/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 34b8943d5f4..1c47a3109a3 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -13,7 +13,7 @@ PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware-utils.git PKG_SOURCE_DATE:=2022-12-15 PKG_SOURCE_VERSION:=bd856eff48509fa4511169ee55f639ede8dd4ac6 -PKG_MIRROR_HASH:=5765389fdb19e0ea2d8f1f586d60580475f6a9917a46777200a3a011f5f654fa +PKG_MIRROR_HASH:=4dc754b403b10f43dc2871f17ef7b223c85ad3b14c57899497f0b6bb304756d9 include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/cmake.mk From 8fb15ea52a02b0578c11897afd1a87f8502d8f52 Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Fri, 9 Sep 2022 22:08:16 +0900 Subject: [PATCH 043/110] realtek: copy dts/files/patches/configs for 5.15 Copy dts/files/patches/configs from 5.10 to 5.15. Signed-off-by: INAGAKI Hiroshi [refresh with updated DGS-1210 dts files] Signed-off-by: Sander Vanheule --- .../rtl8380_d-link_dgs-1210-10mp-f.dts | 88 + .../dts-5.15/rtl8380_engenius_ews2910p.dts | 229 + .../realtek/dts-5.15/rtl8380_hpe_1920-8g.dts | 113 + .../dts-5.15/rtl8380_netgear_gigabit.dtsi | 85 + .../dts-5.15/rtl8380_netgear_gigabit_1xx.dtsi | 60 + .../dts-5.15/rtl8380_netgear_gigabit_3xx.dtsi | 60 + .../dts-5.15/rtl8380_netgear_gs108t-v3.dts | 35 + .../dts-5.15/rtl8380_netgear_gs110tpp-v1.dts | 57 + .../dts-5.15/rtl8380_netgear_gs308t-v1.dts | 35 + .../dts-5.15/rtl8380_netgear_gs310tp-v1.dts | 25 + .../rtl8380_panasonic_m8eg-pn28080k.dts | 133 + .../dts-5.15/rtl8380_tplink_sg2008p-v1.dts | 28 + .../dts-5.15/rtl8380_tplink_sg2210p-v3.dts | 16 + .../dts-5.15/rtl8380_tplink_sg2xxx.dtsi | 183 + .../dts-5.15/rtl8380_zyxel_gs1900-10hp.dts | 75 + .../dts-5.15/rtl8380_zyxel_gs1900-8.dts | 12 + .../dts-5.15/rtl8380_zyxel_gs1900-8hp-v1.dts | 12 + .../dts-5.15/rtl8380_zyxel_gs1900-8hp-v2.dts | 12 + .../dts-5.15/rtl8380_zyxel_gs1900.dtsi | 149 + .../dts-5.15/rtl8382_allnet_all-sg8208m.dts | 141 + .../dts-5.15/rtl8382_d-link_dgs-1210-10p.dts | 102 + .../dts-5.15/rtl8382_d-link_dgs-1210-16.dts | 81 + .../dts-5.15/rtl8382_d-link_dgs-1210-20.dts | 82 + .../dts-5.15/rtl8382_d-link_dgs-1210-28.dts | 11 + .../rtl8382_d-link_dgs-1210-28_common.dtsi | 91 + .../rtl8382_d-link_dgs-1210-28mp-f.dts | 40 + .../realtek/dts-5.15/rtl8382_hpe_1920-16g.dts | 48 + .../realtek/dts-5.15/rtl8382_hpe_1920-24g.dts | 68 + .../realtek/dts-5.15/rtl8382_hpe_1920.dtsi | 117 + .../dts-5.15/rtl8382_inaba_aml2-17gp.dts | 158 + .../dts-5.15/rtl8382_iodata_bsh-g24mb.dts | 197 + .../rtl8382_panasonic_m16eg-pn28160k.dts | 171 + .../rtl8382_panasonic_m24eg-pn28240k.dts | 190 + .../dts-5.15/rtl8382_zyxel_gs1900-16.dts | 36 + .../dts-5.15/rtl8382_zyxel_gs1900-24-v1.dts | 128 + .../dts-5.15/rtl8382_zyxel_gs1900-24e.dts | 63 + .../dts-5.15/rtl8382_zyxel_gs1900-24hp-v1.dts | 125 + .../dts-5.15/rtl8382_zyxel_gs1900-24hp-v2.dts | 121 + target/linux/realtek/dts-5.15/rtl838x.dtsi | 287 ++ .../realtek/dts-5.15/rtl838x_hpe_1920.dtsi | 96 + .../dts-5.15/rtl8393_d-link_dgs-1210-52.dts | 163 + .../rtl8393_panasonic_m48eg-pn28480k.dts | 380 ++ .../dts-5.15/rtl8393_tplink_sg2452p-v4.dts | 422 ++ .../dts-5.15/rtl8393_zyxel_gs1900-48.dts | 320 ++ target/linux/realtek/dts-5.15/rtl839x.dtsi | 319 ++ .../rtl839x_d-link_dgs-1210_gpio.dtsi | 75 + .../rtl83xx_d-link_dgs-1210_common.dtsi | 83 + .../rtl83xx_d-link_dgs-1210_gpio.dtsi | 27 + .../rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi | 196 + .../dts-5.15/rtl9302_zyxel_xgs1250-12.dts | 324 ++ target/linux/realtek/dts-5.15/rtl930x.dtsi | 175 + target/linux/realtek/dts-5.15/rtl931x.dtsi | 200 + .../bindings/realtek,otto-timer.yaml | 85 + .../mips/include/asm/mach-rtl838x/ioremap.h | 29 + .../include/asm/mach-rtl838x/mach-rtl83xx.h | 416 ++ .../arch/mips/kernel/cevt-rtl9300.c | 200 + .../files-5.15/arch/mips/rtl838x/Makefile | 5 + .../files-5.15/arch/mips/rtl838x/Platform | 5 + .../files-5.15/arch/mips/rtl838x/prom.c | 216 + .../files-5.15/arch/mips/rtl838x/setup.c | 105 + .../files-5.15/drivers/clk/realtek/Kconfig | 19 + .../files-5.15/drivers/clk/realtek/Makefile | 2 + .../drivers/clk/realtek/clk-rtl838x-sram.S | 150 + .../drivers/clk/realtek/clk-rtl839x-sram.S | 142 + .../drivers/clk/realtek/clk-rtl83xx.c | 766 ++++ .../drivers/clk/realtek/clk-rtl83xx.h | 75 + .../drivers/clocksource/timer-rtl-otto.c | 299 ++ .../files-5.15/drivers/gpio/gpio-rtl8231.c | 355 ++ .../drivers/i2c/busses/i2c-rtl9300.c | 488 ++ .../drivers/i2c/busses/i2c-rtl9300.h | 62 + .../drivers/i2c/muxes/i2c-mux-rtl9300.c | 293 ++ .../drivers/net/dsa/rtl83xx/Kconfig | 8 + .../drivers/net/dsa/rtl83xx/Makefile | 3 + .../drivers/net/dsa/rtl83xx/common.c | 1696 +++++++ .../drivers/net/dsa/rtl83xx/debugfs.c | 727 +++ .../files-5.15/drivers/net/dsa/rtl83xx/dsa.c | 2259 +++++++++ .../files-5.15/drivers/net/dsa/rtl83xx/qos.c | 576 +++ .../drivers/net/dsa/rtl83xx/rtl838x.c | 2054 +++++++++ .../drivers/net/dsa/rtl83xx/rtl838x.h | 1093 +++++ .../drivers/net/dsa/rtl83xx/rtl839x.c | 1937 ++++++++ .../drivers/net/dsa/rtl83xx/rtl83xx.h | 137 + .../drivers/net/dsa/rtl83xx/rtl930x.c | 2560 +++++++++++ .../drivers/net/dsa/rtl83xx/rtl931x.c | 1701 +++++++ .../files-5.15/drivers/net/dsa/rtl83xx/tc.c | 409 ++ .../drivers/net/ethernet/rtl838x_eth.c | 2588 +++++++++++ .../drivers/net/ethernet/rtl838x_eth.h | 457 ++ .../files-5.15/drivers/net/phy/rtl83xx-phy.c | 4018 +++++++++++++++++ .../files-5.15/drivers/net/phy/rtl83xx-phy.h | 68 + .../include/dt-bindings/clock/rtl83xx-clk.h | 15 + ...s-gpio-binding-for-realtek-otto-gpio.patch | 103 + ...3-gpio-add-realtek-otto-gpio-support.patch | 394 ++ ...altek-rtl838x-rtl839x-spi-controller.patch | 57 + ...ltek-rtl838x-rtl839x-spi-controllers.patch | 248 + ...-add-realtek-rtl838x-rtl839x-support.patch | 78 + ...rtl838x-rtl839x-interrupt-controller.patch | 211 + ...ealtek-otto-fix-gpio-line-irq-offset.patch | 22 + ...hdog-add-realtek-otto-watchdog-timer.patch | 467 ++ ...pers-to-extract-clause-45-regad-and-.patch | 53 + ...k-otto-Support-reversed-port-layouts.patch | 123 + ...ltek-otto-Support-per-cpu-interrupts.patch | 153 + ...pio-realtek-otto-Add-RTL930x-support.patch | 29 + ...pio-realtek-otto-Add-RTL931x-support.patch | 30 + .../300-mips-add-rtl838x-platform.patch | 96 + .../301-gpio-add-rtl8231-driver.patch | 50 + .../302-clocksource-add-otto-driver.patch | 40 + ...e-dependencies-for-gpio-realtek-otto.patch | 26 + ...pdate-dependency-for-spi-realtek-rtl.patch | 25 + ...pdate-dependency-for-irq-realtek-rtl.patch | 22 + ...date-dependency-for-realtek-otto-wdt.patch | 32 + ...308-otto-wdt-fix-missing-math-header.patch | 28 + .../309-cevt-rtl9300-support.patch | 54 + .../310-add-i2c-rtl9300-support.patch | 46 + .../311-add-i2c-mux-rtl9300-support.patch | 46 + .../patches-5.15/312-rt9313-support.patch | 76 + ...chip-irq-realtek-rtl-add-VPE-support.patch | 407 ++ .../316-otto-gpio-uniprocessor-irq-mask.patch | 51 + ...io-realtek-otto-switch-to-32-bit-I-O.patch | 373 ++ .../318-add-rtl83xx-clk-support.patch | 33 + ...hip-irq-realtek-rtl-fix-VPE-affinity.patch | 159 + ...t-dsa-add-support-for-rtl838x-switch.patch | 40 + ...-add-rtl838x-support-for-tag-trailer.patch | 61 + ...a-increase-dsa-max-ports-for-rtl838x.patch | 32 + ...net-add-support-for-rtl838x-ethernet.patch | 48 + ...nclude-linux-add-phy-ops-for-rtl838x.patch | 34 + ...vers-net-phy-eee-support-for-rtl838x.patch | 61 + ...04-include-linux-add-phy-hsgmii-mode.patch | 33 + .../patches-5.15/705-add-rtl-phy.patch | 39 + ...rease-phy-address-number-for-rtl839x.patch | 32 + .../patches-5.15/708-brflood-api.patch | 162 + .../patches-5.15/709-lag-offloading.patch | 781 ++++ ...sfp-re-probe-modules-on-DEV_UP-event.patch | 26 + ...11-net-phy-add-an-MDIO-SMBus-library.patch | 168 + ...12-net-phy-sfp-add-support-for-SMBus.patch | 99 + ...e-better-brport-flags-when-ports-lea.patch | 148 + ...rt-hardware-assisted-indirect-access.patch | 840 ++++ target/linux/realtek/rtl838x/config-5.15 | 228 + target/linux/realtek/rtl839x/config-5.15 | 223 + target/linux/realtek/rtl930x/config-5.15 | 211 + target/linux/realtek/rtl931x/config-5.15 | 225 + 139 files changed, 39485 insertions(+) create mode 100644 target/linux/realtek/dts-5.15/rtl8380_d-link_dgs-1210-10mp-f.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_engenius_ews2910p.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_hpe_1920-8g.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_1xx.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_3xx.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8380_netgear_gs108t-v3.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_netgear_gs110tpp-v1.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_netgear_gs308t-v1.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_netgear_gs310tp-v1.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_panasonic_m8eg-pn28080k.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_tplink_sg2008p-v1.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_tplink_sg2210p-v3.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_tplink_sg2xxx.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-10hp.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v1.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v2.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8382_allnet_all-sg8208m.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-10p.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-16.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-20.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28_common.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28mp-f.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_hpe_1920-16g.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_hpe_1920-24g.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_hpe_1920.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8382_inaba_aml2-17gp.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_iodata_bsh-g24mb.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_panasonic_m16eg-pn28160k.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_panasonic_m24eg-pn28240k.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-16.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24-v1.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24e.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v1.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v2.dts create mode 100644 target/linux/realtek/dts-5.15/rtl838x.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl838x_hpe_1920.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl8393_d-link_dgs-1210-52.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8393_panasonic_m48eg-pn28480k.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8393_tplink_sg2452p-v4.dts create mode 100644 target/linux/realtek/dts-5.15/rtl8393_zyxel_gs1900-48.dts create mode 100644 target/linux/realtek/dts-5.15/rtl839x.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl839x_d-link_dgs-1210_gpio.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_common.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_gpio.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl9302_zyxel_xgs1250-12.dts create mode 100644 target/linux/realtek/dts-5.15/rtl930x.dtsi create mode 100644 target/linux/realtek/dts-5.15/rtl931x.dtsi create mode 100644 target/linux/realtek/files-5.15/Documentation/devicetree/bindings/realtek,otto-timer.yaml create mode 100644 target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/ioremap.h create mode 100644 target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h create mode 100644 target/linux/realtek/files-5.15/arch/mips/kernel/cevt-rtl9300.c create mode 100644 target/linux/realtek/files-5.15/arch/mips/rtl838x/Makefile create mode 100644 target/linux/realtek/files-5.15/arch/mips/rtl838x/Platform create mode 100644 target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c create mode 100644 target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c create mode 100644 target/linux/realtek/files-5.15/drivers/clk/realtek/Kconfig create mode 100644 target/linux/realtek/files-5.15/drivers/clk/realtek/Makefile create mode 100644 target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl838x-sram.S create mode 100644 target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl839x-sram.S create mode 100644 target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.c create mode 100644 target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.h create mode 100644 target/linux/realtek/files-5.15/drivers/clocksource/timer-rtl-otto.c create mode 100644 target/linux/realtek/files-5.15/drivers/gpio/gpio-rtl8231.c create mode 100644 target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.c create mode 100644 target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.h create mode 100644 target/linux/realtek/files-5.15/drivers/i2c/muxes/i2c-mux-rtl9300.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Kconfig create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Makefile create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/debugfs.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/qos.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.h create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl839x.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl83xx.h create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl930x.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/tc.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h create mode 100644 target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c create mode 100644 target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.h create mode 100644 target/linux/realtek/files-5.15/include/dt-bindings/clock/rtl83xx-clk.h create mode 100644 target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch create mode 100644 target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch create mode 100644 target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch create mode 100644 target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch create mode 100644 target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch create mode 100644 target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch create mode 100644 target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch create mode 100644 target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch create mode 100644 target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch create mode 100644 target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch create mode 100644 target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch create mode 100644 target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch create mode 100644 target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch create mode 100644 target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch create mode 100644 target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch create mode 100644 target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch create mode 100644 target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch create mode 100644 target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch create mode 100644 target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch create mode 100644 target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch create mode 100644 target/linux/realtek/patches-5.15/308-otto-wdt-fix-missing-math-header.patch create mode 100644 target/linux/realtek/patches-5.15/309-cevt-rtl9300-support.patch create mode 100644 target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch create mode 100644 target/linux/realtek/patches-5.15/311-add-i2c-mux-rtl9300-support.patch create mode 100644 target/linux/realtek/patches-5.15/312-rt9313-support.patch create mode 100644 target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch create mode 100644 target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch create mode 100644 target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch create mode 100644 target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch create mode 100644 target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch create mode 100644 target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch create mode 100644 target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch create mode 100644 target/linux/realtek/patches-5.15/702-net-dsa-increase-dsa-max-ports-for-rtl838x.patch create mode 100644 target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch create mode 100644 target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch create mode 100644 target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch create mode 100644 target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch create mode 100644 target/linux/realtek/patches-5.15/705-add-rtl-phy.patch create mode 100644 target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch create mode 100644 target/linux/realtek/patches-5.15/708-brflood-api.patch create mode 100644 target/linux/realtek/patches-5.15/709-lag-offloading.patch create mode 100644 target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch create mode 100644 target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch create mode 100644 target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch create mode 100644 target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch create mode 100644 target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch create mode 100644 target/linux/realtek/rtl838x/config-5.15 create mode 100644 target/linux/realtek/rtl839x/config-5.15 create mode 100644 target/linux/realtek/rtl930x/config-5.15 create mode 100644 target/linux/realtek/rtl931x/config-5.15 diff --git a/target/linux/realtek/dts-5.15/rtl8380_d-link_dgs-1210-10mp-f.dts b/target/linux/realtek/dts-5.15/rtl8380_d-link_dgs-1210-10mp-f.dts new file mode 100644 index 00000000000..e7db688ad52 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_d-link_dgs-1210-10mp-f.dts @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" +#include "rtl83xx_d-link_dgs-1210_gpio.dtsi" + +/ { + compatible = "d-link,dgs-1210-10mp-f", "realtek,rtl8382-soc", "realtek,rtl838x-soc"; + + model = "D-Link DGS-1210-10MP F"; +}; + +&leds { + link_act { + label = "green:link_act"; + gpios = <&gpio1 28 GPIO_ACTIVE_LOW>; + }; + + poe { + label = "green:poe"; + gpios = <&gpio1 29 GPIO_ACTIVE_LOW>; + }; + + poe_max { + label = "yellow:poe_max"; + gpios = <&gpio1 27 GPIO_ACTIVE_LOW>; + }; +}; + +&keys { + mode { + label = "mode"; + gpios = <&gpio1 30 GPIO_ACTIVE_LOW>; + linux,code = ; + }; +}; + +&uart1 { + status = "okay"; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + INTERNAL_PHY(24) + INTERNAL_PHY(26) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + SWITCH_SFP_PORT(24, 9, rgmii-id) + SWITCH_SFP_PORT(26, 10, rgmii-id) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_engenius_ews2910p.dts b/target/linux/realtek/dts-5.15/rtl8380_engenius_ews2910p.dts new file mode 100644 index 00000000000..9b5ddb5ed41 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_engenius_ews2910p.dts @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl838x.dtsi" + +#include +#include + +/ { + compatible = "engenius,ews2910p", "realtek,rtl838x-soc"; + model = "EnGenius EWS2910P"; + + aliases { + led-boot = &led_power; + led-failsafe = &led_fault; + led-running = &led_power; + led-upgrade = &led_power; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x10000000>; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio0 11 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + led_mode { + label = "led-mode"; + gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <0>; + + poe_enable { + gpio-hog; + gpios = <1 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "poe-enable"; + }; + + sff_p9_gpios { + gpio-hog; + gpios = < 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>, + < 11 GPIO_ACTIVE_HIGH>, /* los-gpio */ + < 12 GPIO_ACTIVE_LOW>; /* mod-def0-gpio */ + input; + line-name = "sff-p9-gpios"; + }; + }; + + gpio-export { + compatible = "gpio-export"; + + sff-p9-tx-disable { + gpio-export,name = "sff-p9-tx-disable"; + gpio-export,output = <1>; + gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + }; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + }; + + leds { + compatible = "gpio-leds"; + + led_power: led-0 { + label = "green:power"; + gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + }; + + led_lan_mode: led-1 { + label = "green:lan-mode"; + gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; + }; + + led_fault: led-2 { + label = "amber:fault"; + gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; + }; + + led_poe_max: led-3 { + label = "amber:poe-max"; + gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + }; + }; + + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + sfp1: sfp-p10 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + tx-disable-gpio = <&gpio1 13 GPIO_ACTIVE_HIGH>; + los-gpio = <&gpio1 14 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 21 GPIO_ACTIVE_LOW>; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x80000>; + read-only; + }; + partition@80000 { + label = "u-boot-env"; + reg = <0x80000 0x10000>; + read-only; + }; + partition@90000 { + label = "u-boot-env2"; + reg = <0x90000 0x10000>; + }; + partition@a0000 { + label = "rootfs_data"; + reg = <0xa0000 0xd60000>; + }; + partition@e00000 { + label = "jffs2-log"; + reg = <0xe00000 0x200000>; + }; + partition@1000000 { + compatible = "openwrt,uimage"; + label = "firmware"; + reg = <0x1000000 0x800000>; + openwrt,ih-magic = <0x03802910>; + }; + partition@1800000 { + label = "firmware2"; + reg = <0x1800000 0x800000>; + }; + }; + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + INTERNAL_PHY(24) + INTERNAL_PHY(26) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + SWITCH_SFP_PORT(24, 9, 1000base-x) + + port@26 { + reg = <26>; + label = "lan10"; + phy-mode = "1000base-x"; + phy-handle = <&phy26>; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; + +&uart1 { + status = "okay"; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_hpe_1920-8g.dts b/target/linux/realtek/dts-5.15/rtl8380_hpe_1920-8g.dts new file mode 100644 index 00000000000..b51c75f3552 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_hpe_1920-8g.dts @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl838x_hpe_1920.dtsi" + +/ { + compatible = "hpe,1920-8g", "realtek,rtl838x-soc"; + model = "HPE 1920-8G (JG920A)"; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <0>; + }; + + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-0 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 26 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 25 GPIO_ACTIVE_LOW>; + // tx-fault and tx-disable unconnected + }; + + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-1 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 21 GPIO_ACTIVE_LOW>; + // tx-fault and tx-disable unconnected + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + INTERNAL_PHY(24) + INTERNAL_PHY(26) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + port@24 { + reg = <24>; + label = "lan9"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp0>; + }; + + port@26 { + reg = <26>; + label = "lan10"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit.dtsi b/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit.dtsi new file mode 100644 index 00000000000..5e587f278d6 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit.dtsi @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl838x.dtsi" + +#include +#include + +/ { + compatible = "realtek,rtl838x-soc"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + keys { + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + + compatible = "gpio-keys"; + + mode { + label = "reset"; + gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + open-source; + }; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <31>; + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_1xx.dtsi b/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_1xx.dtsi new file mode 100644 index 00000000000..fd44543bb42 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_1xx.dtsi @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit.dtsi" + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0000000 0x00e0000>; + read-only; + }; + + partition@e0000 { + label = "u-boot-env"; + reg = <0x00e0000 0x0010000>; + }; + + partition@f0000 { + label = "u-boot-env2"; + reg = <0x00f0000 0x0010000>; + }; + + partition@100000 { + label = "jffs"; + reg = <0x0100000 0x0100000>; + read-only; + }; + + partition@200000 { + label = "jffs2"; + reg = <0x0200000 0x0100000>; + read-only; + }; + + partition@300000 { + label = "firmware"; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x4e474520>; + reg = <0x0300000 0x0e80000>; + }; + + partition@1180000 { + label = "runtime2"; + reg = <0x1180000 0x0e80000>; + read-only; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_3xx.dtsi b/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_3xx.dtsi new file mode 100644 index 00000000000..0ade665c988 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_netgear_gigabit_3xx.dtsi @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit.dtsi" + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0000000 0x00e0000>; + read-only; + }; + + partition@e0000 { + label = "u-boot-env"; + reg = <0x00e0000 0x0010000>; + }; + + partition@f0000 { + label = "u-boot-env2"; + reg = <0x00f0000 0x0010000>; + }; + + partition@100000 { + label = "jffs"; + reg = <0x0100000 0x0100000>; + read-only; + }; + + partition@200000 { + label = "jffs2"; + reg = <0x0200000 0x0100000>; + read-only; + }; + + partition@300000 { + label = "firmware"; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x4e474335>; + reg = <0x0300000 0x0e80000>; + }; + + partition@1180000 { + label = "runtime2"; + reg = <0x1180000 0x0e80000>; + read-only; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_netgear_gs108t-v3.dts b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs108t-v3.dts new file mode 100644 index 00000000000..e149834d446 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs108t-v3.dts @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit_1xx.dtsi" + +#include + +/ { + compatible = "netgear,gs108t-v3", "realtek,rtl838x-soc"; + model = "Netgear GS108T v3"; + + aliases { + led-boot = &led_power_green; + led-failsafe = &led_power_amber; + led-running = &led_power_green; + led-upgrade = &led_power_amber; + }; + + leds { + compatible = "gpio-leds"; + + led_power_amber: led-0 { + label = "amber:power"; + color = ; + function = LED_FUNCTION_POWER; + gpios = <&gpio1 32 GPIO_ACTIVE_LOW>; + }; + + led_power_green: led-1 { + label = "green:power"; + color = ; + function = LED_FUNCTION_POWER; + gpios = <&gpio1 31 GPIO_ACTIVE_LOW>; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_netgear_gs110tpp-v1.dts b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs110tpp-v1.dts new file mode 100644 index 00000000000..b61af62ea53 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs110tpp-v1.dts @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit_1xx.dtsi" +#include + +/ { + compatible = "netgear,gs110tpp-v1", "realtek,rtl838x-soc"; + model = "Netgear GS110TPP v1"; + + aliases { + led-boot = &led_status_green; + led-failsafe = &led_status_red; + led-running = &led_status_green; + led-upgrade = &led_status_blue; + }; + + leds { + compatible = "gpio-leds"; + + led_status_red: led-0 { + label = "red:status"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio1 31 GPIO_ACTIVE_LOW>; + }; + + led_status_green: led-1 { + label = "green:status"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio1 32 GPIO_ACTIVE_LOW>; + }; + + led_status_blue: led-2 { + label = "blue:status"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio1 34 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&uart1 { + status = "okay"; +}; + +&mdio { + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) +}; + +&switch0 { + ports { + SWITCH_PORT(16, 9, qsgmii) + SWITCH_PORT(17, 10, qsgmii) + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_netgear_gs308t-v1.dts b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs308t-v1.dts new file mode 100644 index 00000000000..cd69e80186c --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs308t-v1.dts @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit_3xx.dtsi" + +#include + +/ { + compatible = "netgear,gs308t-v1", "realtek,rtl838x-soc"; + model = "Netgear GS308T v1"; + + aliases { + led-boot = &led_power_green; + led-failsafe = &led_power_amber; + led-running = &led_power_green; + led-upgrade = &led_power_amber; + }; + + leds { + compatible = "gpio-leds"; + + led_power_amber: led-0 { + label = "amber:power"; + color = ; + function = LED_FUNCTION_POWER; + gpios = <&gpio1 32 GPIO_ACTIVE_LOW>; + }; + + led_power_green: led-1 { + label = "green:power"; + color = ; + function = LED_FUNCTION_POWER; + gpios = <&gpio1 31 GPIO_ACTIVE_LOW>; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_netgear_gs310tp-v1.dts b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs310tp-v1.dts new file mode 100644 index 00000000000..dacd504ac49 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_netgear_gs310tp-v1.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_netgear_gigabit_3xx.dtsi" + +/ { + compatible = "netgear,gs310tp-v1", "realtek,rtl838x-soc"; + model = "Netgear GS310TP v1"; + +}; + +&uart1 { + status = "okay"; +}; + +&mdio { + INTERNAL_PHY(24) + INTERNAL_PHY(26) +}; + +&switch0 { + ports { + SWITCH_SFP_PORT(24, 9, rgmii-id) + SWITCH_SFP_PORT(26, 10, rgmii-id) + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_panasonic_m8eg-pn28080k.dts b/target/linux/realtek/dts-5.15/rtl8380_panasonic_m8eg-pn28080k.dts new file mode 100644 index 00000000000..f9d58f5b66e --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_panasonic_m8eg-pn28080k.dts @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi" + +#include + +/ { + compatible = "panasonic,m8eg-pn28080k", "realtek,rtl8380-soc"; + model = "Panasonic Switch-M8eG PN28080K"; + + aliases { + led-boot = &led_status_eco_green; + led-failsafe = &led_status_eco_amber; + led-running = &led_status_eco_green; + led-upgrade = &led_status_eco_green; + }; + + sfp0: sfp-p9 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>; + }; +}; + +&leds { + led_status_eco_amber: led-5 { + label = "amber:status_eco"; + gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + }; + + led_status_eco_green: led-6 { + label = "green:status_eco"; + gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <2>; + }; +}; + +&i2c_gpio_0 { + scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; +}; + +&i2c_gpio_1 { + scl-gpios = <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; +}; + +&gpio1 { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; +}; + +&gpio2 { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; +}; + +&i2c_switch { + i2c0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; +}; + +ðernet0 { + mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + INTERNAL_PHY(24) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + port@24 { + reg = <24>; + label = "lan9"; + phy-mode = "1000base-x"; + phy-handle = <&phy24>; + managed = "in-band-status"; + sfp = <&sfp0>; + }; + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2008p-v1.dts b/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2008p-v1.dts new file mode 100644 index 00000000000..bf43e412b1c --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2008p-v1.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_tplink_sg2xxx.dtsi" + +/ { + compatible = "tplink,sg2008p-v1", "realtek,rtl838x-soc"; + model = "TP-Link SG2008P v1"; +}; + +&tps23861_20 { + status = "disabled"; +}; + +&phy24 { + status = "disabled"; +}; + +&phy26 { + status = "disabled"; +}; + +&port24 { + status = "disabled"; +}; + +&port26 { + status = "disabled"; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2210p-v3.dts b/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2210p-v3.dts new file mode 100644 index 00000000000..4b0022c3889 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2210p-v3.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_tplink_sg2xxx.dtsi" + +/ { + compatible = "tplink,sg2210p-v3", "realtek,rtl838x-soc"; + model = "TP-Link SG2210P v3"; +}; + +&port24 { + label = "lan-sfp2"; +}; + +&port26 { + label = "lan-sfp1"; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2xxx.dtsi b/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2xxx.dtsi new file mode 100644 index 00000000000..e727a9405ad --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_tplink_sg2xxx.dtsi @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl838x.dtsi" + +#include +#include + +/ { + aliases { + led-boot = &led_power; + led-failsafe = &led_power; + led-running = &led_power; + led-upgrade = &led_power; + label-mac-device = ðernet0; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x10000000>; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio0 3 GPIO_ACTIVE_LOW>; + }; + + leds { + compatible = "gpio-leds"; + + led_power: led-0 { + label = "green:power"; + gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; + }; + }; + + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + + tps23861_20: tps23861@20 { + compatible = "ti,tps23861"; + reg = <0x20>; + shunt-resistor-micro-ohms = <255000>; + }; + + tps23861_28: tps23861@28 { + compatible = "ti,tps23861"; + reg = <0x28>; + shunt-resistor-micro-ohms = <255000>; + }; + }; + + watchdog { + compatible = "linux,wdt-gpio"; + gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>; + hw_algo = "toggle"; + /* SGM706 specs: typical 1.6s, but minimum 1.0s. */ + hw_margin_ms = <1000>; + }; +}; + +&gpio0 { + watchdog-enable { + gpio-hog; + gpios = <14 GPIO_ACTIVE_LOW>; + output-low; + line-name = "watchdog-enable"; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0xe0000>; + read-only; + }; + partition@e0000 { + label = "u-boot-env"; + reg = <0xe0000 0x20000>; + }; + partition@100000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x100000 0x1a00000>; + }; + partition@1b00000 { + label = "usrappfs"; + reg = <0x1b00000 0x400000>; + }; + partition@1f00000 { + compatible = "nvmem-cells"; + label = "para"; + reg = <0x1f00000 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + read-only; + + factory_macaddr: macaddr@fdff4 { + reg = <0xfdff4 0x6>; + }; + }; + }; + }; +}; + +ðernet0 { + nvmem-cells = <&factory_macaddr>; + nvmem-cell-names = "mac-address"; + + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + INTERNAL_PHY(24) + INTERNAL_PHY(26) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(15, 1, internal) + SWITCH_PORT(14, 2, internal) + SWITCH_PORT(13, 3, internal) + SWITCH_PORT(12, 4, internal) + SWITCH_PORT(11, 5, internal) + SWITCH_PORT(10, 6, internal) + SWITCH_PORT(9, 7, internal) + SWITCH_PORT(8, 8, internal) + + SWITCH_SFP_PORT(24, 9, 1000base-x) + SWITCH_SFP_PORT(26, 10, 1000base-x) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-10hp.dts b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-10hp.dts new file mode 100644 index 00000000000..82df6789a95 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-10hp.dts @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-10hp", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-10HP Switch"; + + /* i2c of the left SFP cage: port 9 */ + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-p9 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; + }; + + /* i2c of the right SFP cage: port 10 */ + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-p10 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>; + }; +}; + +&uart1 { + status = "okay"; +}; + +&mdio { + INTERNAL_PHY(24) + INTERNAL_PHY(26) +}; + +&switch0 { + ports { + port@24 { + reg = <24>; + label = "lan9"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp0>; + }; + + port@26 { + reg = <26>; + label = "lan10"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8.dts b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8.dts new file mode 100644 index 00000000000..e9c5efe6039 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-8", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-8 Switch"; +}; + +&gpio1 { + /delete-node/ poe_enable; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v1.dts b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v1.dts new file mode 100644 index 00000000000..5ee340eac68 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v1.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-8hp-v1", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-8HP v1 Switch"; +}; + +&uart1 { + status = "okay"; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v2.dts b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v2.dts new file mode 100644 index 00000000000..07684622555 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900-8hp-v2.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-8hp-v2", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-8HP v2 Switch"; +}; + +&uart1 { + status = "okay"; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900.dtsi b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900.dtsi new file mode 100644 index 00000000000..5993c1b798d --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8380_zyxel_gs1900.dtsi @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl838x.dtsi" + +#include +#include + +/ { + aliases { + led-boot = &led_sys; + led-failsafe = &led_sys; + led-running = &led_sys; + led-upgrade = &led_sys; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + reset { + label = "reset"; + gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + compatible = "gpio-leds"; + + led_sys: sys { + label = "green:sys"; + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + }; + }; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <0>; + + poe_enable { + gpio-hog; + gpios = <13 GPIO_ACTIVE_HIGH>; + output-high; + }; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x40000>; + read-only; + }; + partition@40000 { + label = "u-boot-env"; + reg = <0x40000 0x10000>; + read-only; + }; + partition@50000 { + label = "u-boot-env2"; + reg = <0x50000 0x10000>; + }; + partition@60000 { + label = "jffs"; + reg = <0x60000 0x100000>; + }; + partition@160000 { + label = "jffs2"; + reg = <0x160000 0x100000>; + }; + partition@b260000 { + label = "firmware"; + reg = <0x260000 0x6d0000>; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x83800000>; + }; + partition@930000 { + label = "runtime2"; + reg = <0x930000 0x6d0000>; + }; + }; + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_allnet_all-sg8208m.dts b/target/linux/realtek/dts-5.15/rtl8382_allnet_all-sg8208m.dts new file mode 100644 index 00000000000..320cb08ac7d --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_allnet_all-sg8208m.dts @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl838x.dtsi" + +#include +#include + +/ { + compatible = "allnet,all-sg8208m", "realtek,rtl838x-soc"; + model = "ALLNET ALL-SG8208M"; + + aliases { + led-boot = &led_sys; + led-failsafe = &led_sys; + led-running = &led_sys; + led-upgrade = &led_sys; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + /* is this pin 3 on the external RTL8231 (&gpio1)? */ + /*reset { + label = "reset"; + gpios = <&gpio0 67 GPIO_ACTIVE_LOW>; + linux,code = ; + };*/ + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + compatible = "gpio-leds"; + + led_sys: sys { + label = "green:sys"; + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + }; + // GPIO 25: power on/off all port leds + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@80000 { + label = "u-boot-env"; + reg = <0x80000 0x10000>; + read-only; + }; + + partition@90000 { + label = "u-boot-env2"; + reg = <0x90000 0x10000>; + }; + + partition@a0000 { + label = "jffs"; + reg = <0xa0000 0x100000>; + }; + + partition@1a0000 { + label = "jffs2"; + reg = <0x1a0000 0x100000>; + }; + + partition@2a0000 { + label = "firmware"; + reg = <0x2a0000 0xd60000>; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x00000006>; + }; + }; + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-10p.dts b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-10p.dts new file mode 100644 index 00000000000..16934ede3b8 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-10p.dts @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" + +/ { + compatible = "d-link,dgs-1210-10p", "realtek,rtl838x-soc"; + model = "D-Link DGS-1210-10P"; + + keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + mode { + label = "mode"; + gpios = <&gpio1 30 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset { + label = "reset"; + gpios = <&gpio1 33 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + link_act { + label = "green:link_act"; + gpios = <&gpio1 28 GPIO_ACTIVE_LOW>; + }; + + poe { + label = "green:poe"; + gpios = <&gpio1 29 GPIO_ACTIVE_LOW>; + }; + + poe_max { + label = "yellow:poe_max"; + gpios = <&gpio1 27 GPIO_ACTIVE_LOW>; + }; + }; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <0>; + }; +}; + +&uart1 { + status = "okay"; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + INTERNAL_PHY(24) + INTERNAL_PHY(26) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + SWITCH_SFP_PORT(24, 9, rgmii-id) + SWITCH_SFP_PORT(26, 10, rgmii-id) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-16.dts b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-16.dts new file mode 100644 index 00000000000..35f41b9e4d5 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-16.dts @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" + +/ { + compatible = "d-link,dgs-1210-16", "realtek,rtl838x-soc"; + model = "D-Link DGS-1210-16"; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + EXTERNAL_SFP_PHY(24) + EXTERNAL_SFP_PHY(25) + EXTERNAL_SFP_PHY(26) + EXTERNAL_SFP_PHY(27) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(24, 17, qsgmii) + SWITCH_PORT(25, 18, qsgmii) + SWITCH_PORT(26, 19, qsgmii) + SWITCH_PORT(27, 20, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-20.dts b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-20.dts new file mode 100644 index 00000000000..dacc50676d4 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-20.dts @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" +#include "rtl83xx_d-link_dgs-1210_gpio.dtsi" + +/ { + compatible = "d-link,dgs-1210-20", "realtek,rtl838x-soc"; + model = "D-Link DGS-1210-20"; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + EXTERNAL_SFP_PHY(24) + EXTERNAL_SFP_PHY(25) + EXTERNAL_SFP_PHY(26) + EXTERNAL_SFP_PHY(27) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(24, 17, qsgmii) + SWITCH_PORT(25, 18, qsgmii) + SWITCH_PORT(26, 19, qsgmii) + SWITCH_PORT(27, 20, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28.dts b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28.dts new file mode 100644 index 00000000000..0bcb196b7c9 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" +#include "rtl83xx_d-link_dgs-1210_gpio.dtsi" +#include "rtl8382_d-link_dgs-1210-28_common.dtsi" + +/ { + compatible = "d-link,dgs-1210-28", "realtek,rtl838x-soc"; + model = "D-Link DGS-1210-28"; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28_common.dtsi b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28_common.dtsi new file mode 100644 index 00000000000..17866d5f038 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28_common.dtsi @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + EXTERNAL_SFP_PHY(24) + EXTERNAL_SFP_PHY(25) + EXTERNAL_SFP_PHY(26) + EXTERNAL_SFP_PHY(27) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + SWITCH_PORT(24, 25, qsgmii) + SWITCH_PORT(25, 26, qsgmii) + SWITCH_PORT(26, 27, qsgmii) + SWITCH_PORT(27, 28, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28mp-f.dts b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28mp-f.dts new file mode 100644 index 00000000000..ce008229b33 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_d-link_dgs-1210-28mp-f.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" +#include "rtl83xx_d-link_dgs-1210_gpio.dtsi" +#include "rtl8382_d-link_dgs-1210-28_common.dtsi" + +/ { + compatible = "d-link,dgs-1210-28mp-f", "realtek,rtl8382-soc", "realtek,rtl838x-soc"; + model = "D-Link DGS-1210-28MP F"; +}; + +&leds { + link_act { + label = "green:link_act"; + gpios = <&gpio1 28 GPIO_ACTIVE_LOW>; + }; + + poe { + label = "green:poe"; + gpios = <&gpio1 29 GPIO_ACTIVE_LOW>; + }; + + poe_max { + label = "yellow:poe_max"; + gpios = <&gpio1 27 GPIO_ACTIVE_LOW>; + }; +}; + +&keys { + mode { + label = "mode"; + gpios = <&gpio1 30 GPIO_ACTIVE_LOW>; + linux,code = ; + }; +}; + +&uart1 { + status = "okay"; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_hpe_1920-16g.dts b/target/linux/realtek/dts-5.15/rtl8382_hpe_1920-16g.dts new file mode 100644 index 00000000000..59043b2f6f7 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_hpe_1920-16g.dts @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl8382_hpe_1920.dtsi" + +/ { + compatible = "hpe,1920-16g", "realtek,rtl838x-soc"; + model = "HPE 1920-16G (JG923A)"; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + SWITCH_PORT(16, 9, qsgmii) + SWITCH_PORT(17, 10, qsgmii) + SWITCH_PORT(18, 11, qsgmii) + SWITCH_PORT(19, 12, qsgmii) + SWITCH_PORT(20, 13, qsgmii) + SWITCH_PORT(21, 14, qsgmii) + SWITCH_PORT(22, 15, qsgmii) + SWITCH_PORT(23, 16, qsgmii) + + SWITCH_PORT(24, 17, qsgmii) + SWITCH_PORT(25, 18, qsgmii) + SWITCH_PORT(26, 19, qsgmii) + SWITCH_PORT(27, 20, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_hpe_1920-24g.dts b/target/linux/realtek/dts-5.15/rtl8382_hpe_1920-24g.dts new file mode 100644 index 00000000000..61781c708e2 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_hpe_1920-24g.dts @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl8382_hpe_1920.dtsi" + +/ { + compatible = "hpe,1920-24g", "realtek,rtl838x-soc"; + model = "HPE 1920-24G (JG924A)"; +}; + +&mdio { + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + SWITCH_PORT(24, 25, qsgmii) + SWITCH_PORT(25, 26, qsgmii) + SWITCH_PORT(26, 27, qsgmii) + SWITCH_PORT(27, 28, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_hpe_1920.dtsi b/target/linux/realtek/dts-5.15/rtl8382_hpe_1920.dtsi new file mode 100644 index 00000000000..5368b41c276 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_hpe_1920.dtsi @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl838x_hpe_1920.dtsi" + +/ { + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <0>; + }; + + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-0 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 21 GPIO_ACTIVE_LOW>; + // tx-fault unconnected + // tx-disable connected to RTL8214FC + }; + + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-1 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 26 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 25 GPIO_ACTIVE_LOW>; + // tx-fault unconnected + // tx-disable connected to RTL8214FC + }; + + i2c2: i2c-gpio-2 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 27 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 28 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp2: sfp-2 { + compatible = "sff,sfp"; + i2c-bus = <&i2c2>; + los-gpio = <&gpio1 30 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 29 GPIO_ACTIVE_LOW>; + // tx-fault unconnected + // tx-disable connected to RTL8214FC + }; + + i2c3: i2c-gpio-3 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 32 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp3: sfp-3 { + compatible = "sff,sfp"; + i2c-bus = <&i2c3>; + los-gpio = <&gpio1 34 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 33 GPIO_ACTIVE_LOW>; + // tx-fault unconnected + // tx-disable connected to RTL8214FC + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + EXTERNAL_SFP_PHY_FULL(24, 0) + EXTERNAL_SFP_PHY_FULL(25, 1) + EXTERNAL_SFP_PHY_FULL(26, 2) + EXTERNAL_SFP_PHY_FULL(27, 3) + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_inaba_aml2-17gp.dts b/target/linux/realtek/dts-5.15/rtl8382_inaba_aml2-17gp.dts new file mode 100644 index 00000000000..48081414941 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_inaba_aml2-17gp.dts @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" + +#include +#include + +/ { + compatible = "inaba,aml2-17gp", "realtek,rtl838x-soc"; + model = "INABA Abaniact AML2-17GP"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + keys { + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@80000 { + label = "u-boot-env"; + reg = <0x80000 0x10000>; + read-only; + }; + + partition@90000 { + label = "u-boot-env2"; + reg = <0x90000 0x10000>; + }; + + partition@a0000 { + label = "jffs2_cfg"; + reg = <0xa0000 0x400000>; + read-only; + }; + + partition@4a0000 { + label = "jffs2_log"; + reg = <0x4a0000 0x100000>; + read-only; + }; + + partition@5a0000 { + compatible = "openwrt,uimage", "denx,uimage"; + label = "firmware"; + reg = <0x5a0000 0xd30000>; + openwrt,ih-magic = <0x83800000>; + }; + + partition@12d0000 { + label = "runtime2"; + reg = <0x12d0000 0xd30000>; + }; + }; + }; +}; + +ðernet0 { + mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + EXTERNAL_PHY(24) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + SWITCH_PORT(16, 9, qsgmii) + SWITCH_PORT(17, 10, qsgmii) + SWITCH_PORT(18, 11, qsgmii) + SWITCH_PORT(19, 12, qsgmii) + SWITCH_PORT(20, 13, qsgmii) + SWITCH_PORT(21, 14, qsgmii) + SWITCH_PORT(22, 15, qsgmii) + SWITCH_PORT(23, 16, qsgmii) + + port@24 { + reg = <24>; + label = "wan"; + phy-handle = <&phy24>; + phy-mode = "qsgmii"; + }; + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_iodata_bsh-g24mb.dts b/target/linux/realtek/dts-5.15/rtl8382_iodata_bsh-g24mb.dts new file mode 100644 index 00000000000..d19960c1086 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_iodata_bsh-g24mb.dts @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" + +#include +#include +#include + +/ { + compatible = "iodata,bsh-g24mb", "realtek,rtl838x-soc"; + model = "I-O DATA BSH-G24MB"; + + aliases { + led-boot = &led_sys_loop; + led-failsafe = &led_sys_loop; + led-upgrade = &led_sys_loop; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + compatible = "gpio-leds"; + + led_sys_loop: led { + label = "red:sys_loop"; + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_STATUS; + }; + }; + + keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + reset { + label = "reset"; + gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <0>; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@80000 { + label = "u-boot-env"; + reg = <0x80000 0x10000>; + read-only; + }; + + partition@90000 { + label = "u-boot-env2"; + reg = <0x90000 0x10000>; + }; + + partition@a0000 { + label = "jffs2_cfg"; + reg = <0xa0000 0x100000>; + read-only; + }; + + partition@1a0000 { + label = "jffs2_log"; + reg = <0x1a0000 0x100000>; + read-only; + }; + + /* + * use 2x OS partitions in OpenWrt + * + * 0x2A0000-0x94FFFF: RUNTIME + * 0x950000-0xFFFFFF: RUNTIME2 (not used in stock) + */ + partition@2a0000 { + compatible = "openwrt,uimage", "denx,uimage"; + label = "firmware"; + reg = <0x2a0000 0xd60000>; + openwrt,ih-magic = <0x83800013>; + }; + }; + }; +}; + +ðernet0 { + mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_panasonic_m16eg-pn28160k.dts b/target/linux/realtek/dts-5.15/rtl8382_panasonic_m16eg-pn28160k.dts new file mode 100644 index 00000000000..c3b29a87ff3 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_panasonic_m16eg-pn28160k.dts @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi" + +#include + +/ { + compatible = "panasonic,m16eg-pn28160k", "realtek,rtl8382-soc"; + model = "Panasonic Switch-M16eG PN28160K"; + + aliases { + led-boot = &led_status_eco_green; + led-failsafe = &led_status_eco_amber; + led-running = &led_status_eco_green; + led-upgrade = &led_status_eco_green; + }; + + /* + * sfp0/1 are "combo" port with each TP port (23/24), and they are + * connected to the RTL8218FB. Currently, there is no support for + * the chip and only TP ports work by the RTL8218D support. + */ + sfp0: sfp-p23 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>; + }; + + sfp1: sfp-p24 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + tx-fault-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 5 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 6 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>; + }; +}; + +&leds { + led_status_eco_amber: led-5 { + label = "amber:status_eco"; + gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + }; + + led_status_eco_green: led-6 { + label = "green:status_eco"; + gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <2>; + }; +}; + +&i2c_gpio_0 { + scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; +}; + +&i2c_gpio_1 { + scl-gpios = <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; +}; + +&gpio2 { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + + /* + * GPIO12 (IO1_4): RTL8218FB + * + * This GPIO pin should be specified as "reset-gpio" in mdio node, but + * RTL8218FB phy won't be configured on RTL8218D support in the current + * phy driver. So, ethernet ports on the phy will be broken after hard- + * resetting. + * (RTL8218FB phy will be detected as RTL8218D by the phy driver) + * At the moment, configure this GPIO pin as gpio-hog to avoid breaking + * by resetting. + */ + ext_switch_reset { + gpio-hog; + gpios = <12 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "ext-switch-reset"; + }; +}; + +&i2c_switch { + i2c0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + i2c1: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; +}; + +ðernet0 { + mdio-bus { + compatible = "realtek,rtl838x-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + /* RTL8218FB */ + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(8, 1, internal) + SWITCH_PORT(9, 2, internal) + SWITCH_PORT(10, 3, internal) + SWITCH_PORT(11, 4, internal) + SWITCH_PORT(12, 5, internal) + SWITCH_PORT(13, 6, internal) + SWITCH_PORT(14, 7, internal) + SWITCH_PORT(15, 8, internal) + + SWITCH_PORT(16, 9, qsgmii) + SWITCH_PORT(17, 10, qsgmii) + SWITCH_PORT(18, 11, qsgmii) + SWITCH_PORT(19, 12, qsgmii) + SWITCH_PORT(20, 13, qsgmii) + SWITCH_PORT(21, 14, qsgmii) + SWITCH_PORT(22, 15, qsgmii) + SWITCH_PORT(23, 16, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_panasonic_m24eg-pn28240k.dts b/target/linux/realtek/dts-5.15/rtl8382_panasonic_m24eg-pn28240k.dts new file mode 100644 index 00000000000..1fa9d2cdc7f --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_panasonic_m24eg-pn28240k.dts @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl838x.dtsi" +#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi" + +#include + +/ { + compatible = "panasonic,m24eg-pn28240k", "realtek,rtl8382-soc"; + model = "Panasonic Switch-M24eG PN28240K"; + + aliases { + led-boot = &led_status_eco_green; + led-failsafe = &led_status_eco_amber; + led-running = &led_status_eco_green; + led-upgrade = &led_status_eco_green; + }; + + /* + * sfp0/1 are "combo" port with each TP port (23/24), and they are + * connected to the RTL8218FB. Currently, there is no support for + * the chip and only TP ports work by the RTL8218D support. + */ + sfp0: sfp-p23 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>; + }; + + sfp1: sfp-p24 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + tx-fault-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 5 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 6 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>; + }; +}; + +&leds { + led_status_eco_amber: led-5 { + label = "amber:status_eco"; + gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + }; + + led_status_eco_green: led-6 { + label = "green:status_eco"; + gpios = <&gpio2 2 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <2>; + }; +}; + +&i2c_gpio_0 { + scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; +}; + +&i2c_gpio_1 { + scl-gpios = <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; +}; + +&gpio2 { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + + /* + * GPIO12 (IO1_4): RTL8218B + RTL8218FB + * + * This GPIO pin should be specified as "reset-gpio" in mdio node, + * but the current configuration of RTL8218B phy in the phy driver + * seems to be incomplete and RTL8218FB phy won't be configured on + * RTL8218D support. So, ethernet ports on these phys will be broken + * after hard-resetting. + * (RTL8218FB phy will be detected as RTL8218D by the phy driver) + * At the moment, configure this GPIO pin as gpio-hog to avoid breaking + * by resetting. + */ + ext_switch_reset { + gpio-hog; + gpios = <12 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "ext-switch-reset"; + }; +}; + +&i2c_switch { + i2c0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + i2c1: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; +}; + +ðernet0 { + mdio-bus { + compatible = "realtek,rtl838x-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + INTERNAL_PHY(8) + INTERNAL_PHY(9) + INTERNAL_PHY(10) + INTERNAL_PHY(11) + INTERNAL_PHY(12) + INTERNAL_PHY(13) + INTERNAL_PHY(14) + INTERNAL_PHY(15) + + /* RTL8218FB */ + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-16.dts b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-16.dts new file mode 100644 index 00000000000..ac2eea7015d --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-16.dts @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-16", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-16"; +}; + +&mdio { + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) +}; + +&switch0 { + ports { + SWITCH_PORT(16, 9, qsgmii) + SWITCH_PORT(17, 10, qsgmii) + SWITCH_PORT(18, 11, qsgmii) + SWITCH_PORT(19, 12, qsgmii) + SWITCH_PORT(20, 13, qsgmii) + SWITCH_PORT(21, 14, qsgmii) + SWITCH_PORT(22, 15, qsgmii) + SWITCH_PORT(23, 16, qsgmii) + }; +}; + +&gpio1 { + /delete-node/ poe_enable; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24-v1.dts b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24-v1.dts new file mode 100644 index 00000000000..81482dde109 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24-v1.dts @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-24-v1", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-24 v1"; + + memory@0 { + reg = <0x0 0x4000000>; + }; + + /* i2c of the left SFP cage: port 25 */ + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-p25 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; + }; + + /* i2c of the right SFP cage: port 26 */ + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-p26 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>; + }; +}; + +&uart1 { + status = "okay"; +}; + +&mdio { + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + INTERNAL_PHY(24) + INTERNAL_PHY(26) +}; + +&switch0 { + ports { + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + port@24 { + reg = <24>; + label = "lan25"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp0>; + }; + + port@26 { + reg = <26>; + label = "lan26"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + }; +}; + +&gpio1 { + /delete-node/ poe_enable; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24e.dts b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24e.dts new file mode 100644 index 00000000000..3d00034a3b5 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24e.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-24e", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-24E"; +}; + +&mdio { + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) +}; + +&switch0 { + ports { + SWITCH_PORT(1, 1, qsgmii) + SWITCH_PORT(0, 2, qsgmii) + SWITCH_PORT(3, 3, qsgmii) + SWITCH_PORT(2, 4, qsgmii) + SWITCH_PORT(5, 5, qsgmii) + SWITCH_PORT(4, 6, qsgmii) + SWITCH_PORT(7, 7, qsgmii) + SWITCH_PORT(6, 8, qsgmii) + + SWITCH_PORT(9, 9, internal) + SWITCH_PORT(8, 10, internal) + SWITCH_PORT(11, 11, internal) + SWITCH_PORT(10, 12, internal) + SWITCH_PORT(13, 13, internal) + SWITCH_PORT(12, 14, internal) + SWITCH_PORT(15, 15, internal) + SWITCH_PORT(14, 16, internal) + + SWITCH_PORT(17, 17, qsgmii) + SWITCH_PORT(16, 18, qsgmii) + SWITCH_PORT(19, 19, qsgmii) + SWITCH_PORT(18, 20, qsgmii) + SWITCH_PORT(21, 21, qsgmii) + SWITCH_PORT(20, 22, qsgmii) + SWITCH_PORT(23, 23, qsgmii) + SWITCH_PORT(22, 24, qsgmii) + }; +}; + +&gpio1 { + /delete-node/ poe_enable; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v1.dts b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v1.dts new file mode 100644 index 00000000000..7bb3410a318 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v1.dts @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-24hp-v1", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-24HP v1"; + + memory@0 { + reg = <0x0 0x4000000>; + }; + + /* i2c of the left SFP cage: port 25 */ + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-p25 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; + }; + + /* i2c of the right SFP cage: port 26 */ + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-p26 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>; + }; +}; + +&uart1 { + status = "okay"; +}; + +&mdio { + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + INTERNAL_PHY(24) + INTERNAL_PHY(26) +}; + +&switch0 { + ports { + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + port@24 { + reg = <24>; + label = "lan25"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp0>; + }; + + port@26 { + reg = <26>; + label = "lan26"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + }; +}; + diff --git a/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v2.dts b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v2.dts new file mode 100644 index 00000000000..7b6a9a1e7fd --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8382_zyxel_gs1900-24hp-v2.dts @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" + +/ { + compatible = "zyxel,gs1900-24hp-v2", "realtek,rtl838x-soc"; + model = "ZyXEL GS1900-24HP v2 Switch"; + + /* i2c of the left SFP cage: port 25 */ + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-p25 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; + }; + + /* i2c of the right SFP cage: port 26 */ + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-p26 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>; + }; +}; + +&uart1 { + status = "okay"; +}; + +&mdio { + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + INTERNAL_PHY(24) + INTERNAL_PHY(26) +}; + +&switch0 { + ports { + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, internal) + SWITCH_PORT(9, 10, internal) + SWITCH_PORT(10, 11, internal) + SWITCH_PORT(11, 12, internal) + SWITCH_PORT(12, 13, internal) + SWITCH_PORT(13, 14, internal) + SWITCH_PORT(14, 15, internal) + SWITCH_PORT(15, 16, internal) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + + port@24 { + reg = <24>; + label = "lan25"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp0>; + }; + + port@26 { + reg = <26>; + label = "lan26"; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl838x.dtsi b/target/linux/realtek/dts-5.15/rtl838x.dtsi new file mode 100644 index 00000000000..64e13e1ff37 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl838x.dtsi @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include + +/dts-v1/; + +#define STRINGIZE(s) #s +#define LAN_LABEL(p, s) STRINGIZE(p ## s) +#define SWITCH_PORT_LABEL(n) LAN_LABEL(lan, n) + +#define INTERNAL_PHY(n) \ + phy##n: ethernet-phy@##n { \ + reg = <##n>; \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + phy-is-integrated; \ + }; + +#define EXTERNAL_PHY(n) \ + phy##n: ethernet-phy@##n { \ + reg = <##n>; \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + }; + +#define EXTERNAL_SFP_PHY(n) \ + phy##n: ethernet-phy@##n { \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + sfp; \ + media = "fibre"; \ + reg = <##n>; \ + }; + +#define EXTERNAL_SFP_PHY_FULL(n, s) \ + phy##n: ethernet-phy@##n { \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + sfp = <&sfp##s>; \ + reg = <##n>; \ + }; + +#define SWITCH_PORT(n, s, m) \ + port##n: port@##n { \ + reg = <##n>; \ + label = SWITCH_PORT_LABEL(s) ; \ + phy-handle = <&phy##n>; \ + phy-mode = #m ; \ + }; + +#define SWITCH_SFP_PORT(n, s, m) \ + port##n: port@##n { \ + reg = <##n>; \ + label = SWITCH_PORT_LABEL(s) ; \ + phy-handle = <&phy##n>; \ + phy-mode = #m ; \ + fixed-link { \ + speed = <1000>; \ + full-duplex; \ + }; \ + }; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "realtek,rtl838x-soc"; + + osc: oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + + ccu: clock-controller { + compatible = "realtek,rtl8380-clock"; + #clock-cells = <1>; + clocks = <&osc>; + clock-names = "ref_clk"; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "mips,mips4KEc"; + reg = <0>; + clocks = <&ccu CLK_CPU>; + operating-points-v2 = <&cpu_opp_table>; + }; + }; + + cpu_opp_table: opp-table-0 { + compatible = "operating-points-v2"; + opp-shared; + + opp00 { + opp-hz = /bits/ 64 <325000000>; + }; + opp01 { + opp-hz = /bits/ 64 <350000000>; + }; + opp02 { + opp-hz = /bits/ 64 <375000000>; + }; + opp03 { + opp-hz = /bits/ 64 <400000000>; + }; + opp04 { + opp-hz = /bits/ 64 <425000000>; + }; + opp05 { + opp-hz = /bits/ 64 <450000000>; + }; + opp06 { + opp-hz = /bits/ 64 <475000000>; + }; + opp07 { + opp-hz = /bits/ 64 <500000000>; + }; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + cpuintc: cpuintc { + compatible = "mti,cpu-interrupt-controller"; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x18000000 0x10000>; + + intc: interrupt-controller@3000 { + compatible = "realtek,rtl8380-intc", "realtek,rtl-intc"; + reg = <0x3000 0x18>; + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>, <3>, <4>, <5>, <6>; + }; + + spi0: spi@1200 { + compatible = "realtek,rtl8380-spi"; + reg = <0x1200 0x100>; + + #address-cells = <1>; + #size-cells = <0>; + }; + + timer0: timer@3100 { + compatible = "realtek,rtl8380-timer", "realtek,otto-timer"; + reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>, + <0x3130 0x10>, <0x3140 0x10>; + + interrupt-parent = <&intc>; + interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>; + clocks = <&ccu CLK_LXB>; + }; + + uart0: uart@2000 { + compatible = "ns16550a"; + reg = <0x2000 0x100>; + + clocks = <&ccu CLK_LXB>; + + interrupt-parent = <&intc>; + interrupts = <31 1>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + }; + + uart1: uart@2100 { + pinctrl-names = "default"; + pinctrl-0 = <&enable_uart1>; + + compatible = "ns16550a"; + reg = <0x2100 0x100>; + + clocks = <&ccu CLK_LXB>; + + interrupt-parent = <&intc>; + interrupts = <30 0>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + + status = "disabled"; + }; + + watchdog0: watchdog@3150 { + compatible = "realtek,rtl8380-wdt"; + reg = <0x3150 0xc>; + + realtek,reset-mode = "soc"; + + clocks = <&ccu CLK_LXB>; + timeout-sec = <30>; + + interrupt-parent = <&intc>; + interrupt-names = "phase1", "phase2"; + interrupts = <19 3>, <18 4>; + }; + + gpio0: gpio-controller@3500 { + compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio"; + reg = <0x3500 0x20>; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <24>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <23 3>; + }; + }; + + pinmux: pinmux@1b001000 { + compatible = "pinctrl-single"; + reg = <0x1b001000 0x4>; + + pinctrl-single,bit-per-mux; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x1>; + #pinctrl-cells = <2>; + + enable_uart1: pinmux_enable_uart1 { + pinctrl-single,bits = <0x0 0x10 0x10>; + }; + }; + + /* LED_GLB_CTRL */ + pinmux_led: pinmux@1b00a000 { + compatible = "pinctrl-single"; + reg = <0x1b00a000 0x4>; + + pinctrl-single,bit-per-mux; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x1>; + #pinctrl-cells = <2>; + + /* enable GPIO 0 */ + pinmux_disable_sys_led: disable_sys_led { + pinctrl-single,bits = <0x0 0x0 0x8000>; + }; + }; + + ethernet0: ethernet@1b00a300 { + compatible = "realtek,rtl838x-eth"; + reg = <0x1b00a300 0x100>; + interrupt-parent = <&intc>; + interrupts = <24 3>; + #interrupt-cells = <1>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + sram0: sram@9f000000 { + compatible = "mmio-sram"; + reg = <0x9f000000 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x9f000000 0x10000>; + }; + + switch0: switch@1b000000 { + compatible = "realtek,rtl83xx-switch"; + + interrupt-parent = <&intc>; + interrupts = <20 2>; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl838x_hpe_1920.dtsi b/target/linux/realtek/dts-5.15/rtl838x_hpe_1920.dtsi new file mode 100644 index 00000000000..8e29af62bbf --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl838x_hpe_1920.dtsi @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include +#include + +/ { + chosen { + bootargs = "console=ttyS0,38400"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + watchdog1: watchdog { + // PT7A7514 + compatible = "linux,wdt-gpio"; + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + hw_algo = "toggle"; + hw_margin_ms = <1000>; + always-running; + + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + }; +}; + +&watchdog0 { + status = "disabled"; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + m25p,fast-read; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bootware_basic"; + reg = <0x0 0x50000>; + read-only; + }; + + partition@0x60000 { + label = "bootware_data"; + reg = <0x60000 0x30000>; + read-only; + }; + + partition@0x90000 { + label = "bootware_extend"; + reg = <0x90000 0x40000>; + read-only; + }; + + partition@0x100000 { + label = "bootware_basic_backup"; + reg = <0x100000 0x50000>; + read-only; + }; + + partition@0x160000 { + label = "bootware_data_backup"; + reg = <0x160000 0x30000>; + read-only; + }; + + partition@0x190000 { + label = "bootware_extend_backup"; + reg = <0x190000 0x40000>; + read-only; + }; + + partition@0x300000 { + label = "firmware"; + compatible = "h3c,vfs-firmware"; + reg = <0x300000 0x1cf0000>; + }; + + partition@0x1ff0000 { + label = "factory"; + reg = <0x1ff0000 0x10000>; + read-only; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8393_d-link_dgs-1210-52.dts b/target/linux/realtek/dts-5.15/rtl8393_d-link_dgs-1210-52.dts new file mode 100644 index 00000000000..5b876e7c431 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8393_d-link_dgs-1210-52.dts @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl839x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" +#include "rtl83xx_d-link_dgs-1210_gpio.dtsi" +#include "rtl839x_d-link_dgs-1210_gpio.dtsi" + +/ { + compatible = "d-link,dgs-1210-52", "realtek,rtl8393-soc"; + model = "D-Link DGS-1210-52"; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + /* External phy RTL8218B #1 */ + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + /* External phy RTL8218B #2 */ + EXTERNAL_PHY(8) + EXTERNAL_PHY(9) + EXTERNAL_PHY(10) + EXTERNAL_PHY(11) + EXTERNAL_PHY(12) + EXTERNAL_PHY(13) + EXTERNAL_PHY(14) + EXTERNAL_PHY(15) + + /* External phy RTL8218B #3 */ + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + /* External phy RTL8218B #4 */ + EXTERNAL_PHY(24) + EXTERNAL_PHY(25) + EXTERNAL_PHY(26) + EXTERNAL_PHY(27) + EXTERNAL_PHY(28) + EXTERNAL_PHY(29) + EXTERNAL_PHY(30) + EXTERNAL_PHY(31) + + /* External phy RTL8218B #5 */ + EXTERNAL_PHY(32) + EXTERNAL_PHY(33) + EXTERNAL_PHY(34) + EXTERNAL_PHY(35) + EXTERNAL_PHY(36) + EXTERNAL_PHY(37) + EXTERNAL_PHY(38) + EXTERNAL_PHY(39) + + /* External phy RTL8218B #6 */ + EXTERNAL_PHY(40) + EXTERNAL_PHY(41) + EXTERNAL_PHY(42) + EXTERNAL_PHY(43) + EXTERNAL_PHY(44) + EXTERNAL_PHY(45) + EXTERNAL_PHY(46) + EXTERNAL_PHY(47) + + /* External phy RTL8214FC */ + EXTERNAL_SFP_PHY_FULL(48, 0) + EXTERNAL_SFP_PHY_FULL(49, 1) + EXTERNAL_SFP_PHY_FULL(50, 2) + EXTERNAL_SFP_PHY_FULL(51, 3) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, qsgmii) + SWITCH_PORT(9, 10, qsgmii) + SWITCH_PORT(10, 11, qsgmii) + SWITCH_PORT(11, 12, qsgmii) + SWITCH_PORT(12, 13, qsgmii) + SWITCH_PORT(13, 14, qsgmii) + SWITCH_PORT(14, 15, qsgmii) + SWITCH_PORT(15, 16, qsgmii) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + SWITCH_PORT(24, 25, qsgmii) + SWITCH_PORT(25, 26, qsgmii) + SWITCH_PORT(26, 27, qsgmii) + SWITCH_PORT(27, 28, qsgmii) + SWITCH_PORT(28, 29, qsgmii) + SWITCH_PORT(29, 30, qsgmii) + SWITCH_PORT(30, 31, qsgmii) + SWITCH_PORT(31, 32, qsgmii) + + SWITCH_PORT(32, 33, qsgmii) + SWITCH_PORT(33, 34, qsgmii) + SWITCH_PORT(34, 35, qsgmii) + SWITCH_PORT(35, 36, qsgmii) + SWITCH_PORT(36, 37, qsgmii) + SWITCH_PORT(37, 38, qsgmii) + SWITCH_PORT(38, 39, qsgmii) + SWITCH_PORT(39, 40, qsgmii) + + SWITCH_PORT(40, 41, qsgmii) + SWITCH_PORT(41, 42, qsgmii) + SWITCH_PORT(42, 43, qsgmii) + SWITCH_PORT(43, 44, qsgmii) + SWITCH_PORT(44, 45, qsgmii) + SWITCH_PORT(45, 46, qsgmii) + SWITCH_PORT(46, 47, qsgmii) + SWITCH_PORT(47, 48, qsgmii) + + SWITCH_PORT(48, 49, qsgmii) + SWITCH_PORT(49, 50, qsgmii) + SWITCH_PORT(50, 51, qsgmii) + SWITCH_PORT(51, 52, qsgmii) + + /* CPU-Port */ + port@52 { + ethernet = <ðernet0>; + reg = <52>; + phy-mode = "qsgmii"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8393_panasonic_m48eg-pn28480k.dts b/target/linux/realtek/dts-5.15/rtl8393_panasonic_m48eg-pn28480k.dts new file mode 100644 index 00000000000..6a7eb6eb813 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8393_panasonic_m48eg-pn28480k.dts @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl839x.dtsi" +#include "rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi" + +#include + +/ { + compatible = "panasonic,m48eg-pn28480k", "realtek,rtl8393-soc"; + model = "Panasonic Switch-M48eG PN28480K"; + + aliases { + led-boot = &led_status_eco_green; + led-failsafe = &led_status_eco_amber; + led-running = &led_status_eco_green; + led-upgrade = &led_status_eco_green; + }; + + fan: gpio-fan { + compatible = "gpio-fan"; + gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>; + /* the actual speeds (rpm) are unknown, just use dummy values */ + gpio-fan,speed-map = <1 0>, <2 1>; + #cooling-cells = <2>; + }; + + /* + * sfp0/1/2/3 are "combo" port with each TP port (45/46/47/48), + * and they are connected to the RTL8218FB. Currently, there is + * no support for the chip and only TP ports work by the RTL8218B + * support. + */ + sfp0: sfp-p45 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + tx-fault-gpio = <&gpio1 0 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>; + }; + + sfp1: sfp-p46 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + tx-fault-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 5 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 6 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 7 GPIO_ACTIVE_HIGH>; + }; + + sfp2: sfp-p47 { + compatible = "sff,sfp"; + i2c-bus = <&i2c2>; + tx-fault-gpio = <&gpio1 8 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 10 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 11 GPIO_ACTIVE_HIGH>; + }; + + sfp3: sfp-p48 { + compatible = "sff,sfp"; + i2c-bus = <&i2c3>; + tx-fault-gpio = <&gpio1 12 GPIO_ACTIVE_HIGH>; + tx-disable-gpio = <&gpio1 13 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 14 GPIO_ACTIVE_LOW>; + los-gpio = <&gpio1 15 GPIO_ACTIVE_HIGH>; + }; + + thermal-zones { + /* + * Zone for SoC temperature + * + * Fan speed: + * + * - 0-44 celsius: Low + * - 45-54 celsius: High + */ + cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <2000>; + + thermal-sensors = <&tsens_soc>; + + trips { + cpu_alert: trip-point { + temperature = <45000>; + hysteresis = <4000>; + type = "active"; + }; + + cpu_crit { + temperature = <55000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map { + trip = <&cpu_alert>; + cooling-device = <&fan 0 1>; + }; + }; + }; + + /* + * Zone for system temperature + * + * Fan speed: + * + * - 0-39 celsius: Low + * - 40-49 celsius: High + * + * Note: official recommended ranges of temperature on each + * fan speed setting: + * + * - Low speed : 0-40 celsius + * - High speed: 0-50 celsius + * + * (stock firmware doesn't support auto-selection of + * speed and need to be selected manually by user) + */ + sys-thermal { + polling-delay-passive = <1000>; + polling-delay = <2000>; + + thermal-sensors = <&tsens_sys>; + + trips { + sys_alert: trip-point { + temperature = <40000>; + hysteresis = <4000>; + type = "active"; + }; + + sys_crit { + temperature = <50000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + + cooling-maps { + map { + trip = <&sys_alert>; + cooling-device = <&fan 0 1>; + }; + }; + }; + }; +}; + +&leds { + led_status_eco_amber: led-5 { + label = "amber:status_eco"; + gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <1>; + }; + + led_status_eco_green: led-6 { + label = "green:status_eco"; + gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_STATUS; + function-enumerator = <2>; + }; +}; + +&i2c_gpio_0 { + scl-gpios = <&gpio0 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + + /* Microchip TCN75A (for SoC) */ + tsens_soc: sensor@48 { + compatible = "microchip,tcn75"; + reg = <0x48>; + #thermal-sensor-cells = <0>; + }; + + /* Microchip TCN75A (for System) */ + tsens_sys: sensor@49 { + compatible = "microchip,tcn75"; + reg = <0x49>; + #thermal-sensor-cells = <0>; + }; +}; + +&i2c_gpio_1 { + scl-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio0 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; +}; + +&gpio2 { + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gpio0>; + interrupts = <18 IRQ_TYPE_EDGE_FALLING>; + + /* + * GPIO12 (IO1_4): 5x RTL8218B + RTL8218FB + * + * This GPIO pin should be specified as "reset-gpio" in mdio node, + * but the current configuration of RTL8218B phy in the phy driver + * seems to be incomplete and RTL8218FB phy won't be configured on + * RTL8218D support. So, ethernet ports on these phys will be broken + * after hard-resetting. + * (RTL8218FB phy will be detected as RTL8218D by the phy driver) + * At the moment, configure this GPIO pin as gpio-hog to avoid breaking + * by resetting. + */ + ext_switch_reset { + gpio-hog; + gpios = <12 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "ext-switch-reset"; + }; +}; + +&i2c_switch { + i2c0: i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + i2c1: i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + i2c2: i2c@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + i2c3: i2c@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; +}; + +ðernet0 { + mdio-bus { + compatible = "realtek,rtl838x-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + EXTERNAL_PHY(8) + EXTERNAL_PHY(9) + EXTERNAL_PHY(10) + EXTERNAL_PHY(11) + EXTERNAL_PHY(12) + EXTERNAL_PHY(13) + EXTERNAL_PHY(14) + EXTERNAL_PHY(15) + + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + EXTERNAL_PHY(24) + EXTERNAL_PHY(25) + EXTERNAL_PHY(26) + EXTERNAL_PHY(27) + EXTERNAL_PHY(28) + EXTERNAL_PHY(29) + EXTERNAL_PHY(30) + EXTERNAL_PHY(31) + + EXTERNAL_PHY(32) + EXTERNAL_PHY(33) + EXTERNAL_PHY(34) + EXTERNAL_PHY(35) + EXTERNAL_PHY(36) + EXTERNAL_PHY(37) + EXTERNAL_PHY(38) + EXTERNAL_PHY(39) + + /* RTL8218FB */ + EXTERNAL_PHY(40) + EXTERNAL_PHY(41) + EXTERNAL_PHY(42) + EXTERNAL_PHY(43) + EXTERNAL_PHY(44) + EXTERNAL_PHY(45) + EXTERNAL_PHY(46) + EXTERNAL_PHY(47) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 1, qsgmii) + SWITCH_PORT(1, 2, qsgmii) + SWITCH_PORT(2, 3, qsgmii) + SWITCH_PORT(3, 4, qsgmii) + SWITCH_PORT(4, 5, qsgmii) + SWITCH_PORT(5, 6, qsgmii) + SWITCH_PORT(6, 7, qsgmii) + SWITCH_PORT(7, 8, qsgmii) + + SWITCH_PORT(8, 9, qsgmii) + SWITCH_PORT(9, 10, qsgmii) + SWITCH_PORT(10, 11, qsgmii) + SWITCH_PORT(11, 12, qsgmii) + SWITCH_PORT(12, 13, qsgmii) + SWITCH_PORT(13, 14, qsgmii) + SWITCH_PORT(14, 15, qsgmii) + SWITCH_PORT(15, 16, qsgmii) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + SWITCH_PORT(24, 25, qsgmii) + SWITCH_PORT(25, 26, qsgmii) + SWITCH_PORT(26, 27, qsgmii) + SWITCH_PORT(27, 28, qsgmii) + SWITCH_PORT(28, 29, qsgmii) + SWITCH_PORT(29, 30, qsgmii) + SWITCH_PORT(30, 31, qsgmii) + SWITCH_PORT(31, 32, qsgmii) + + SWITCH_PORT(32, 33, qsgmii) + SWITCH_PORT(33, 34, qsgmii) + SWITCH_PORT(34, 35, qsgmii) + SWITCH_PORT(35, 36, qsgmii) + SWITCH_PORT(36, 37, qsgmii) + SWITCH_PORT(37, 38, qsgmii) + SWITCH_PORT(38, 39, qsgmii) + SWITCH_PORT(39, 40, qsgmii) + + SWITCH_PORT(40, 41, qsgmii) + SWITCH_PORT(41, 42, qsgmii) + SWITCH_PORT(42, 43, qsgmii) + SWITCH_PORT(43, 44, qsgmii) + SWITCH_PORT(44, 45, qsgmii) + SWITCH_PORT(45, 46, qsgmii) + SWITCH_PORT(46, 47, qsgmii) + SWITCH_PORT(47, 48, qsgmii) + + port@52 { + ethernet = <ðernet0>; + reg = <52>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8393_tplink_sg2452p-v4.dts b/target/linux/realtek/dts-5.15/rtl8393_tplink_sg2452p-v4.dts new file mode 100644 index 00000000000..b7300cfcbee --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8393_tplink_sg2452p-v4.dts @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "rtl839x.dtsi" + +#include +#include +#include + +/ { + compatible = "tplink,sg2452p-v4", "realtek,rtl8393-soc"; + model = "TP-Link SG2452P v4"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x10000000>; + }; + + aliases { + led-boot = &led_sys; + led-failsafe = &led_sys; + led-running = &led_sys; + led-upgrade = &led_sys; + label-mac-device = ðernet0; + }; + + chosen { + bootargs = "console=ttyS0,38400"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + speed { + label = "speed"; + gpios = <&gpio0 19 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio_fan_sys { + compatible = "gpio-fan"; + alarm-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + }; + + gpio_fan_psu_1 { + pinctrl-names = "default"; + pinctrl-0 = <&disable_jtag>; + compatible = "gpio-fan"; + + alarm-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>; + gpios = <&gpio0 4 GPIO_ACTIVE_LOW>; + /* the actual speeds (rpm) are unknown, just use dummy values */ + gpio-fan,speed-map = <1 0>, <2 1>; + #cooling-cells = <2>; + }; + + gpio_fan_psu_2 { + /* This fan runs in parallel to PSU1 fan, but has a separate + * alarm GPIO. This is not (yet) supported by the gpio-fan driver, + * so a separate instance is added + */ + compatible = "gpio-fan"; + alarm-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>; + }; + + leds { + pinctrl-names = "default"; + compatible = "gpio-leds"; + + led-0 { + label = "green:speed"; + gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_INDICATOR; + }; + + led-1 { + label = "green:poe"; + gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_INDICATOR; + }; + + led_sys: led-2 { + label = "green:sys"; + gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_STATUS; + }; + + led-3 { + label = "green:fan"; + gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; + color = ; + function = LED_FUNCTION_STATUS; + }; + + led-4 { + label = "amber:fan"; + gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>; + color = ; + function = "fault-fan"; + }; + + led-5 { + label = "green:poe-max"; + gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>; + color = ; + function = "alarm-poe"; + }; + }; + + i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + + /* LAN9 - LAN12 */ + tps23861@5 { + compatible = "ti,tps23861"; + reg = <0x05>; + }; + + /* LAN17 - LAN20 */ + tps23861@6 { + compatible = "ti,tps23861"; + reg = <0x06>; + }; + + /* LAN45 - LAN48 */ + tps23861@9 { + compatible = "ti,tps23861"; + reg = <0x09>; + }; + + /* LAN37 - LAN40 */ + tps23861@a { + compatible = "ti,tps23861"; + reg = <0x0a>; + }; + + /* LAN1 - LAN4 */ + tps23861@14 { + compatible = "ti,tps23861"; + reg = <0x14>; + }; + + /* LAN25 - LAN28 */ + tps23861@24 { + compatible = "ti,tps23861"; + reg = <0x24>; + }; + + /* LAN33 - LAN 36 */ + tps23861@25 { + compatible = "ti,tps23861"; + reg = <0x25>; + }; + + /* LAN41 - LAN44 */ + tps23861@26 { + compatible = "ti,tps23861"; + reg = <0x26>; + }; + + /* LAN13 - LAN16 */ + tps23861@29 { + compatible = "ti,tps23861"; + reg = <0x29>; + }; + + /* LAN29 - LAN32 */ + tps23861@2c { + compatible = "ti,tps23861"; + reg = <0x2c>; + }; + + /* LAN5 - LAN8 */ + tps23861@48 { + compatible = "ti,tps23861"; + reg = <0x48>; + }; + + /* LAN21 - LAN24 */ + tps23861@49 { + compatible = "ti,tps23861"; + reg = <0x49>; + }; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; + }; +}; + +&gpio0 { + poe-enable { + gpio-hog; + gpios = <23 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "poe-enable"; + }; +}; + +&spi0 { + status = "okay"; + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0xe0000>; + read-only; + }; + partition@e0000 { + label = "u-boot-env"; + reg = <0xe0000 0x20000>; + }; + + /* We use the "sys", "usrimg1" and "usrimg2" partitions + * as firmware since the kernel needs to be in "sys", but the + * partition is too small to hold the "rootfs" as well. + * The original partition map contains: + * + * partition@100000 { + * label = "sys"; + * reg = <0x100000 0x600000>; + * }; + * partition@700000 { + * label = "usrimg1"; + * reg = <0x700000 0xa00000>; + * }; + * partition@1100000 { + * label = "usrimg2"; + * reg = <0x1100000 0xa00000>; + * }; + */ + + partition@100000 { + label = "firmware"; + reg = <0x100000 0x1a00000>; + }; + partition@1b00000 { + label = "usrappfs"; + reg = <0x1b00000 0x400000>; + }; + partition@1f00000 { + compatible = "nvmem-cells"; + label = "para"; + reg = <0x1f00000 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + read-only; + + factory_macaddr: macaddr@fdff4 { + reg = <0xfdff4 0x6>; + }; + }; + }; + }; +}; + +ðernet0 { + nvmem-cells = <&factory_macaddr>; + nvmem-cell-names = "mac-address"; + + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + #address-cells = <1>; + #size-cells = <0>; + + /* External phy RTL8218B #1 */ + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + /* External phy RTL8218B #2 */ + EXTERNAL_PHY(8) + EXTERNAL_PHY(9) + EXTERNAL_PHY(10) + EXTERNAL_PHY(11) + EXTERNAL_PHY(12) + EXTERNAL_PHY(13) + EXTERNAL_PHY(14) + EXTERNAL_PHY(15) + + /* External phy RTL8218B #3 */ + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + /* External phy RTL8218B #4 */ + EXTERNAL_PHY(24) + EXTERNAL_PHY(25) + EXTERNAL_PHY(26) + EXTERNAL_PHY(27) + EXTERNAL_PHY(28) + EXTERNAL_PHY(29) + EXTERNAL_PHY(30) + EXTERNAL_PHY(31) + + /* External phy RTL8218B #5 */ + EXTERNAL_PHY(32) + EXTERNAL_PHY(33) + EXTERNAL_PHY(34) + EXTERNAL_PHY(35) + EXTERNAL_PHY(36) + EXTERNAL_PHY(37) + EXTERNAL_PHY(38) + EXTERNAL_PHY(39) + + /* External phy RTL8218B #6 */ + EXTERNAL_PHY(40) + EXTERNAL_PHY(41) + EXTERNAL_PHY(42) + EXTERNAL_PHY(43) + EXTERNAL_PHY(44) + EXTERNAL_PHY(45) + EXTERNAL_PHY(46) + EXTERNAL_PHY(47) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 01, qsgmii) + SWITCH_PORT(1, 02, qsgmii) + SWITCH_PORT(2, 03, qsgmii) + SWITCH_PORT(3, 04, qsgmii) + SWITCH_PORT(4, 05, qsgmii) + SWITCH_PORT(5, 06, qsgmii) + SWITCH_PORT(6, 07, qsgmii) + SWITCH_PORT(7, 08, qsgmii) + + SWITCH_PORT(8, 09, qsgmii) + SWITCH_PORT(9, 10, qsgmii) + SWITCH_PORT(10, 11, qsgmii) + SWITCH_PORT(11, 12, qsgmii) + SWITCH_PORT(12, 13, qsgmii) + SWITCH_PORT(13, 14, qsgmii) + SWITCH_PORT(14, 15, qsgmii) + SWITCH_PORT(15, 16, qsgmii) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + SWITCH_PORT(24, 25, qsgmii) + SWITCH_PORT(25, 26, qsgmii) + SWITCH_PORT(26, 27, qsgmii) + SWITCH_PORT(27, 28, qsgmii) + SWITCH_PORT(28, 29, qsgmii) + SWITCH_PORT(29, 30, qsgmii) + SWITCH_PORT(30, 31, qsgmii) + SWITCH_PORT(31, 32, qsgmii) + + SWITCH_PORT(32, 33, qsgmii) + SWITCH_PORT(33, 34, qsgmii) + SWITCH_PORT(34, 35, qsgmii) + SWITCH_PORT(35, 36, qsgmii) + SWITCH_PORT(36, 37, qsgmii) + SWITCH_PORT(37, 38, qsgmii) + SWITCH_PORT(38, 39, qsgmii) + SWITCH_PORT(39, 40, qsgmii) + + SWITCH_PORT(40, 41, qsgmii) + SWITCH_PORT(41, 42, qsgmii) + SWITCH_PORT(42, 43, qsgmii) + SWITCH_PORT(43, 44, qsgmii) + SWITCH_PORT(44, 45, qsgmii) + SWITCH_PORT(45, 46, qsgmii) + SWITCH_PORT(46, 47, qsgmii) + SWITCH_PORT(47, 48, qsgmii) + + /* CPU-Port */ + port@52 { + ethernet = <ðernet0>; + reg = <52>; + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl8393_zyxel_gs1900-48.dts b/target/linux/realtek/dts-5.15/rtl8393_zyxel_gs1900-48.dts new file mode 100644 index 00000000000..dd392c5a9be --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl8393_zyxel_gs1900-48.dts @@ -0,0 +1,320 @@ +/dts-v1/; + +#include "rtl839x.dtsi" + +#include +#include + +/ { + compatible = "zyxel,gs1900-48", "realtek,rtl8393-soc"; + model = "Zyxel GS1900-48"; + + aliases { + led-boot = &led_sys; + led-failsafe = &led_sys; + led-running = &led_sys; + led-upgrade = &led_sys; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + compatible = "gpio-leds"; + + led_sys: sys { + label = "green:sys"; + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + }; + }; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + indirect-access-bus-id = <3>; + gpio-controller; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; + }; + + keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + mode { + label = "reset"; + gpios = <&gpio1 3 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + /* i2c of the left SFP cage: port 49 */ + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-p9 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; + }; + + /* i2c of the right SFP cage: port 50 */ + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-p10 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>; + }; +}; + +&spi0 { + status = "okay"; + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x40000>; + read-only; + }; + partition@40000 { + label = "u-boot-env"; + reg = <0x40000 0x10000>; + read-only; + }; + partition@50000 { + label = "u-boot-env2"; + reg = <0x50000 0x10000>; + read-only; + }; + partition@60000 { + label = "jffs"; + reg = <0x60000 0x100000>; + }; + partition@160000 { + label = "jffs2"; + reg = <0x160000 0x100000>; + }; + partition@b260000 { + label = "firmware"; + reg = <0x260000 0xda0000>; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x83800000>; + }; + partition@930000 { + label = "runtime2"; + reg = <0x930000 0x6d0000>; + }; + }; + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + /* External phy RTL8218B #1 */ + EXTERNAL_PHY(0) + EXTERNAL_PHY(1) + EXTERNAL_PHY(2) + EXTERNAL_PHY(3) + EXTERNAL_PHY(4) + EXTERNAL_PHY(5) + EXTERNAL_PHY(6) + EXTERNAL_PHY(7) + + /* External phy RTL8218B #2 */ + EXTERNAL_PHY(8) + EXTERNAL_PHY(9) + EXTERNAL_PHY(10) + EXTERNAL_PHY(11) + EXTERNAL_PHY(12) + EXTERNAL_PHY(13) + EXTERNAL_PHY(14) + EXTERNAL_PHY(15) + + /* External phy RTL8218B #3 */ + EXTERNAL_PHY(16) + EXTERNAL_PHY(17) + EXTERNAL_PHY(18) + EXTERNAL_PHY(19) + EXTERNAL_PHY(20) + EXTERNAL_PHY(21) + EXTERNAL_PHY(22) + EXTERNAL_PHY(23) + + /* External phy RTL8218B #4 */ + EXTERNAL_PHY(24) + EXTERNAL_PHY(25) + EXTERNAL_PHY(26) + EXTERNAL_PHY(27) + EXTERNAL_PHY(28) + EXTERNAL_PHY(29) + EXTERNAL_PHY(30) + EXTERNAL_PHY(31) + + /* External phy RTL8218B #5 */ + EXTERNAL_PHY(32) + EXTERNAL_PHY(33) + EXTERNAL_PHY(34) + EXTERNAL_PHY(35) + EXTERNAL_PHY(36) + EXTERNAL_PHY(37) + EXTERNAL_PHY(38) + EXTERNAL_PHY(39) + + /* External phy RTL8218B #6 */ + EXTERNAL_PHY(40) + EXTERNAL_PHY(41) + EXTERNAL_PHY(42) + EXTERNAL_PHY(43) + EXTERNAL_PHY(44) + EXTERNAL_PHY(45) + EXTERNAL_PHY(46) + EXTERNAL_PHY(47) + + /* RTL8393 Internal SerDes */ + INTERNAL_PHY(48) + INTERNAL_PHY(49) + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + SWITCH_PORT(0, 01, qsgmii) + SWITCH_PORT(1, 02, qsgmii) + SWITCH_PORT(2, 03, qsgmii) + SWITCH_PORT(3, 04, qsgmii) + SWITCH_PORT(4, 05, qsgmii) + SWITCH_PORT(5, 06, qsgmii) + SWITCH_PORT(6, 07, qsgmii) + SWITCH_PORT(7, 08, qsgmii) + + SWITCH_PORT(8, 09, qsgmii) + SWITCH_PORT(9, 10, qsgmii) + SWITCH_PORT(10, 11, qsgmii) + SWITCH_PORT(11, 12, qsgmii) + SWITCH_PORT(12, 13, qsgmii) + SWITCH_PORT(13, 14, qsgmii) + SWITCH_PORT(14, 15, qsgmii) + SWITCH_PORT(15, 16, qsgmii) + + SWITCH_PORT(16, 17, qsgmii) + SWITCH_PORT(17, 18, qsgmii) + SWITCH_PORT(18, 19, qsgmii) + SWITCH_PORT(19, 20, qsgmii) + SWITCH_PORT(20, 21, qsgmii) + SWITCH_PORT(21, 22, qsgmii) + SWITCH_PORT(22, 23, qsgmii) + SWITCH_PORT(23, 24, qsgmii) + + SWITCH_PORT(24, 25, qsgmii) + SWITCH_PORT(25, 26, qsgmii) + SWITCH_PORT(26, 27, qsgmii) + SWITCH_PORT(27, 28, qsgmii) + SWITCH_PORT(28, 29, qsgmii) + SWITCH_PORT(29, 30, qsgmii) + SWITCH_PORT(30, 31, qsgmii) + SWITCH_PORT(31, 32, qsgmii) + + SWITCH_PORT(32, 33, qsgmii) + SWITCH_PORT(33, 34, qsgmii) + SWITCH_PORT(34, 35, qsgmii) + SWITCH_PORT(35, 36, qsgmii) + SWITCH_PORT(36, 37, qsgmii) + SWITCH_PORT(37, 38, qsgmii) + SWITCH_PORT(38, 39, qsgmii) + SWITCH_PORT(39, 40, qsgmii) + + SWITCH_PORT(40, 41, qsgmii) + SWITCH_PORT(41, 42, qsgmii) + SWITCH_PORT(42, 43, qsgmii) + SWITCH_PORT(43, 44, qsgmii) + SWITCH_PORT(44, 45, qsgmii) + SWITCH_PORT(45, 46, qsgmii) + SWITCH_PORT(46, 47, qsgmii) + SWITCH_PORT(47, 48, qsgmii) + + /* SFP cages */ + port@48 { + reg = <48>; + label = "lan49"; + phy-mode = "sgmii"; + phy-handle = <&phy48>; + sfp = <&sfp0>; + + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + + }; + + port@49 { + reg = <49>; + label = "lan50"; + phy-mode = "sgmii"; + phy-handle = <&phy49>; + sfp = <&sfp1>; + + fixed-link { + speed = <1000>; + full-duplex; + pause; + }; + + }; + + /* CPU-Port */ + port@52 { + ethernet = <ðernet0>; + reg = <52>; + phy-mode = "qsgmii"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl839x.dtsi b/target/linux/realtek/dts-5.15/rtl839x.dtsi new file mode 100644 index 00000000000..91d6e17a9ea --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl839x.dtsi @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include + +/dts-v1/; + +#define STRINGIZE(s) #s +#define LAN_LABEL(p, s) STRINGIZE(p ## s) +#define SWITCH_PORT_LABEL(n) LAN_LABEL(lan, n) + +#define INTERNAL_PHY(n) \ + phy##n: ethernet-phy@##n { \ + reg = <##n>; \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + phy-is-integrated; \ + }; + +#define EXTERNAL_PHY(n) \ + phy##n: ethernet-phy@##n { \ + reg = <##n>; \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + }; + +#define EXTERNAL_SFP_PHY(n) \ + phy##n: ethernet-phy@##n { \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + sfp; \ + media = "fibre"; \ + reg = <##n>; \ + }; + +#define EXTERNAL_SFP_PHY_FULL(n, s) \ + phy##n: ethernet-phy@##n { \ + compatible = "ethernet-phy-ieee802.3-c22"; \ + sfp = <&sfp##s>; \ + reg = <##n>; \ + }; + +#define SWITCH_PORT(n, s, m) \ + port@##n { \ + reg = <##n>; \ + label = SWITCH_PORT_LABEL(s) ; \ + phy-handle = <&phy##n>; \ + phy-mode = #m ; \ + }; + +#define SWITCH_SFP_PORT(n, s, m) \ + port@##n { \ + reg = <##n>; \ + label = SWITCH_PORT_LABEL(s) ; \ + phy-handle = <&phy##n>; \ + phy-mode = #m ; \ + fixed-link { \ + speed = <1000>; \ + full-duplex; \ + }; \ + }; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "realtek,rtl839x-soc"; + + osc: oscillator { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + + ccu: clock-controller { + compatible = "realtek,rtl8390-clock"; + #clock-cells = <1>; + clocks = <&osc>; + clock-names = "ref_clk"; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "mips,mips34Kc"; + reg = <0>; + clocks = <&ccu CLK_CPU>; + operating-points-v2 = <&cpu_opp_table>; + }; + + cpu@1 { + compatible = "mips,mips34Kc"; + reg = <1>; + clocks = <&ccu CLK_CPU>; + operating-points-v2 = <&cpu_opp_table>; + }; + }; + + cpu_opp_table: opp-table-0 { + compatible = "operating-points-v2"; + opp-shared; + + opp00 { + opp-hz = /bits/ 64 <425000000>; + }; + opp01 { + opp-hz = /bits/ 64 <450000000>; + }; + opp02 { + opp-hz = /bits/ 64 <475000000>; + }; + opp03 { + opp-hz = /bits/ 64 <500000000>; + }; + opp04 { + opp-hz = /bits/ 64 <525000000>; + }; + opp05 { + opp-hz = /bits/ 64 <550000000>; + }; + opp06 { + opp-hz = /bits/ 64 <575000000>; + }; + opp07 { + opp-hz = /bits/ 64 <600000000>; + }; + opp08 { + opp-hz = /bits/ 64 <625000000>; + }; + opp09 { + opp-hz = /bits/ 64 <650000000>; + }; + opp10 { + opp-hz = /bits/ 64 <675000000>; + }; + opp11 { + opp-hz = /bits/ 64 <700000000>; + }; + opp12 { + opp-hz = /bits/ 64 <725000000>; + }; + opp13 { + opp-hz = /bits/ 64 <750000000>; + }; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + cpuintc: cpuintc { + compatible = "mti,cpu-interrupt-controller"; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x18000000 0x10000>; + + intc: interrupt-controller@3000 { + compatible = "realtek,rtl8390-intc", "realtek,rtl-intc"; + reg = <0x3000 0x18>, <0x3018 0x18>; + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>, <3>, <4>, <5>, <6>; + }; + + spi0: spi@1200 { + compatible = "realtek,rtl8380-spi"; + reg = <0x1200 0x100>; + + #address-cells = <1>; + #size-cells = <0>; + }; + + timer0: timer@3100 { + compatible = "realtek,rtl8390-timer", "realtek,otto-timer"; + reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>, + <0x3130 0x10>, <0x3140 0x10>; + + interrupt-parent = <&intc>; + interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>; + clocks = <&ccu CLK_LXB>; + }; + + uart0: uart@2000 { + compatible = "ns16550a"; + reg = <0x2000 0x100>; + + clocks = <&ccu CLK_LXB>; + + interrupt-parent = <&intc>; + interrupts = <31 1>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + }; + + uart1: uart@2100 { + pinctrl-names = "default"; + pinctrl-0 = <&enable_uart1>; + + compatible = "ns16550a"; + reg = <0x2100 0x100>; + + clocks = <&ccu CLK_LXB>; + + interrupt-parent = <&intc>; + interrupts = <30 2>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + + status = "disabled"; + }; + + gpio0: gpio-controller@3500 { + compatible = "realtek,rtl8390-gpio", "realtek,otto-gpio"; + reg = <0x3500 0x20>; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <24>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <23 2>; + }; + + watchdog0: watchdog@3150 { + compatible = "realtek,rtl8390-wdt"; + reg = <0x3150 0xc>; + + realtek,reset-mode = "soc"; + + clocks = <&ccu CLK_LXB>; + timeout-sec = <30>; + + interrupt-parent = <&intc>; + interrupt-names = "phase1", "phase2"; + interrupts = <19 4>, <18 4>; + }; + + }; + + pinmux@1b000004 { + compatible = "pinctrl-single"; + reg = <0x1b000004 0x4>; + + pinctrl-single,bit-per-mux; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x1>; + #pinctrl-cells = <2>; + + enable_uart1: pinmux_enable_uart1 { + pinctrl-single,bits = <0x0 0x1 0x3>; + }; + + disable_jtag: pinmux_disable_jtag { + pinctrl-single,bits = <0x0 0x2 0x3>; + }; + }; + + /* LED_GLB_CTRL */ + pinmux@1b0000e4 { + compatible = "pinctrl-single"; + reg = <0x1b0000e4 0x4>; + + pinctrl-single,bit-per-mux; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x1>; + #pinctrl-cells = <2>; + + /* enable GPIO 0 */ + pinmux_disable_sys_led: disable_sys_led { + pinctrl-single,bits = <0x0 0x0 0x4000>; + }; + }; + + ethernet0: ethernet@1b00a300 { + compatible = "realtek,rtl838x-eth"; + reg = <0x1b00a300 0x100>; + + interrupt-parent = <&intc>; + interrupts = <24 3>; + + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + sram0: sram@9f000000 { + compatible = "mmio-sram"; + reg = <0x9f000000 0x18000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x9f000000 0x18000>; + }; + + switch0: switch@1b000000 { + status = "okay"; + compatible = "realtek,rtl83xx-switch"; + + interrupt-parent = <&intc>; + interrupts = <20 2>; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl839x_d-link_dgs-1210_gpio.dtsi b/target/linux/realtek/dts-5.15/rtl839x_d-link_dgs-1210_gpio.dtsi new file mode 100644 index 00000000000..260ab67ef3e --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl839x_d-link_dgs-1210_gpio.dtsi @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/ { + /* Lan 49 */ + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-p49 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 8 GPIO_ACTIVE_LOW>; + /* tx-disable-gpio handled by RTL8214FC based on media setting */ + }; + + /* Lan 50 */ + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-p50 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 4 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; + /* tx-disable-gpio handled by RTL8214FC based on media setting */ + }; + + /* Lan 51 */ + i2c2: i2c-gpio-2 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 22 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp2: sfp-p51 { + compatible = "sff,sfp"; + i2c-bus = <&i2c2>; + los-gpio = <&gpio1 25 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 24 GPIO_ACTIVE_LOW>; + /* tx-disable-gpio handled by RTL8214FC based on media setting */ + }; + + /* Lan 52 */ + i2c3: i2c-gpio-3 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 11 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 12 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp3: sfp-p52 { + compatible = "sff,sfp"; + i2c-bus = <&i2c3>; + los-gpio = <&gpio1 14 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 13 GPIO_ACTIVE_LOW>; + /* tx-disable-gpio handled by RTL8214FC based on media setting */ + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_common.dtsi b/target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_common.dtsi new file mode 100644 index 00000000000..1e3cafa9381 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_common.dtsi @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include +#include + +/ { + aliases { + led-boot = &led_power; + led-failsafe = &led_power; + led-running = &led_power; + led-upgrade = &led_power; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + leds: leds { + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + compatible = "gpio-leds"; + + led_power: power { + label = "green:power"; + gpios = <&gpio0 0 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&gpio0 { + indirect-access-bus-id = <0>; +}; + +&spi0 { + status = "okay"; + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x00000000 0x80000>; + read-only; + }; + partition@80000 { + label = "u-boot-env"; + reg = <0x00080000 0x40000>; + }; + partition@c0000 { + label = "board-name"; + reg = <0x000c0000 0x40000>; + }; + partition@280000 { + label = "firmware"; + compatible = "denx,uimage"; + reg = <0x00100000 0xd80000>; + }; + partition@be80000 { + label = "kernel2"; + reg = <0x00e80000 0x180000>; + }; + partition@1000000 { + label = "sysinfo"; + reg = <0x01000000 0x40000>; + }; + partition@1040000 { + label = "rootfs2"; + reg = <0x01040000 0xc00000>; + }; + partition@1c40000 { + label = "jffs2"; + reg = <0x01c40000 0x3c0000>; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_gpio.dtsi b/target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_gpio.dtsi new file mode 100644 index 00000000000..b1477aa1821 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl83xx_d-link_dgs-1210_gpio.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/ { + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio1 34 GPIO_ACTIVE_LOW>; + open-source; + }; + + keys: keys { + compatible = "gpio-keys-polled"; + poll-interval = <20>; + + reset { + label = "reset"; + gpios = <&gpio1 33 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio1: rtl8231-gpio { + compatible = "realtek,rtl8231-gpio"; + #gpio-cells = <2>; + gpio-controller; + indirect-access-bus-id = <0>; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi b/target/linux/realtek/dts-5.15/rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi new file mode 100644 index 00000000000..fb2aa18d218 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl83xx_panasonic_mxxeg-pn28xx0k.dtsi @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include +#include +#include + +/ { + chosen { + bootargs = "console=ttyS0,9600"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + leds: leds { + compatible = "gpio-leds"; + + led-0 { + label = "amber:any_col"; + gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_FAULT; + }; + + led-1 { + label = "green:giga"; + gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <1>; + }; + + led-2 { + label = "green:100m"; + gpios = <&gpio2 9 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <2>; + }; + + led-3 { + label = "green:full"; + gpios = <&gpio2 10 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <3>; + }; + + led-4 { + label = "green:loop_history"; + gpios = <&gpio2 11 GPIO_ACTIVE_LOW>; + color = ; + function = LED_FUNCTION_INDICATOR; + function-enumerator = <4>; + }; + }; + + keys { + compatible = "gpio-keys"; + + led_mode { + label = "led-mode"; + gpios = <&gpio2 15 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>; + }; + + i2c_gpio_0: i2c-gpio-0 { + compatible = "i2c-gpio"; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + + gpio1: gpio@20 { + compatible = "nxp,pca9555"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + }; + + gpio2: gpio@75 { + compatible = "nxp,pca9539"; + reg = <0x75>; + gpio-controller; + #gpio-cells = <2>; + + /* + * GPIO14 (IO1_6): Shift Register RESET (port LED) + * - Switch-M8eG PN28080K: 3x 74HC164 + * - Switch-M16eG PN28160K: 4x 74HC164 + * - Switch-M24eG PN28240K: 6x 74HC164 + * - Switch-M48eG PN28480K: 12x 74HC164 + */ + portled_sregister_reset { + gpio-hog; + gpios = <14 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "portled-sregister-reset"; + }; + }; + }; + + i2c_gpio_1: i2c-gpio-1 { + compatible = "i2c-gpio"; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + + i2c_switch: i2c-switch@70 { + compatible = "nxp,pca9545"; + reset-gpios = <&gpio2 13 GPIO_ACTIVE_LOW>; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@80000 { + label = "u-boot-env"; + reg = <0x80000 0x10000>; + }; + + partition@90000 { + label = "u-boot-env2"; + reg = <0x90000 0x10000>; + }; + + partition@a0000 { + label = "sysinfo"; + reg = <0xa0000 0x60000>; + read-only; + }; + + /* + * Filesystem area in stock firmware + * (0x100000-0x1DFFFFF) + * + * stock firmware images are required to pass + * the checking by the U-Boot, also for OpenWrt + * + * in OpenWrt: + * - 0x100000-0xDFFFFF (13M): stock images + * - 0xE00000-0x1DFFFFF(16M): OpenWrt image + */ + partition@100000 { + label = "fs_reserved"; + reg = <0x100000 0xd00000>; + }; + + partition@e00000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0xe00000 0x1000000>; + }; + + partition@1e00000 { + label = "vlog_data"; + reg = <0x1e00000 0x100000>; + read-only; + }; + + partition@1f00000 { + label = "elog_data"; + reg = <0x1f00000 0x100000>; + read-only; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl9302_zyxel_xgs1250-12.dts b/target/linux/realtek/dts-5.15/rtl9302_zyxel_xgs1250-12.dts new file mode 100644 index 00000000000..a57fc00c6e9 --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl9302_zyxel_xgs1250-12.dts @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/dts-v1/; + +#include "rtl930x.dtsi" + +#include +#include +#include + +/ { + compatible = "zyxel,xgs1250-12", "realtek,rtl838x-soc"; + model = "Zyxel XGS1250-12 Switch"; + + aliases { + led-boot = &led_pwr_sys; + led-failsafe = &led_pwr_sys; + led-running = &led_pwr_sys; + led-upgrade = &led_pwr_sys; + }; + + keys { + compatible = "gpio-keys"; + + mode { + label = "reset"; + gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + /* i2c of the SFP cage: port 12 */ + i2c0: i2c-rtl9300 { + compatible = "realtek,rtl9300-i2c"; + reg = <0x1b00036c 0x3c>; + #address-cells = <1>; + #size-cells = <0>; + sda-pin = <10>; + scl-pin = <8>; + clock-frequency = <100000>; + }; + + leds { + compatible = "gpio-leds"; + + pinctrl-names = "default"; + pinctrl-0 = <&pinmux_disable_sys_led>; + + led_pwr_sys: led-0 { + label = "green:power"; + color = ; + function = LED_FUNCTION_POWER; + gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + }; + }; + + sfp0: sfp-p12 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio0 20 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio0 16 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio0 15 GPIO_ACTIVE_HIGH>; + }; + + led_set: led_set@0 { + compatible = "realtek,rtl9300-leds"; + led_set0 = <0x0000 0xffff 0x0a20 0x0b80>; // LED set 0: 1000Mbps, 10/100Mbps + led_set1 = <0x0a0b 0x0a28 0x0a82 0x0a0b>; // LED set 1: (10G, 5G, 2.5G) (2.5G, 1G) + // (5G, 10/100) (10G, 5G, 2.5G) + led_set2 = <0x0000 0xffff 0x0a20 0x0a01>; // LED set 2: 1000MBit, 10GBit + }; +}; + +&spi0 { + status = "okay"; + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0xe0000>; + read-only; + }; + partition@e0000 { + label = "u-boot-env"; + reg = <0xe0000 0x10000>; + }; + partition@f0000 { + label = "u-boot-env2"; + reg = <0xf0000 0x10000>; + read-only; + }; + partition@100000 { + label = "jffs"; + reg = <0x100000 0x100000>; + }; + partition@200000 { + label = "jffs2"; + reg = <0x200000 0x100000>; + }; + partition@b300000 { + label = "firmware"; + reg = <0x300000 0xce0000>; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = <0x93001250>; + }; + partition@fe0000 { + label = "log"; + reg = <0xfe0000 0x20000>; + }; + }; + }; +}; + +ðernet0 { + mdio: mdio-bus { + compatible = "realtek,rtl838x-mdio"; + regmap = <ðernet0>; + #address-cells = <1>; + #size-cells = <0>; + + /* External RTL8218D PHY */ + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 0>; + sds = < 2 >; + // Disabled because we do not know how to bring up again + // reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; + }; + phy1: ethernet-phy@1 { + reg = <1>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 1>; + }; + phy2: ethernet-phy@2 { + reg = <2>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 2>; + }; + phy3: ethernet-phy@3 { + reg = <3>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 3>; + }; + phy4: ethernet-phy@4 { + reg = <4>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 4>; + }; + phy5: ethernet-phy@5 { + reg = <5>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 5>; + }; + phy6: ethernet-phy@6 { + reg = <6>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 6>; + }; + phy7: ethernet-phy@7 { + reg = <7>; + compatible = "ethernet-phy-ieee802.3-c22"; + rtl9300,smi-address = <0 7>; + }; + + /* External Aquantia 113C PHYs */ + phy24: ethernet-phy@24 { + reg = <24>; + compatible = "ethernet-phy-ieee802.3-c45"; + rtl9300,smi-address = <1 8>; + sds = < 6 >; + // Disabled because we do not know how to bring up again + // reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; + }; + + phy25: ethernet-phy@25 { + reg = <25>; + compatible = "ethernet-phy-ieee802.3-c45"; + rtl9300,smi-address = <2 8>; + sds = < 7 >; + // Disabled because we do not know how to bring up again + // reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; + }; + + phy26: ethernet-phy@26 { + reg = <26>; + compatible = "ethernet-phy-ieee802.3-c45"; + rtl9300,smi-address = <3 8>; + sds = < 8 >; + // Disabled because we do not know how to bring up again + // reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; + }; + + /* SFP Ports */ + phy27: ethernet-phy@27 { + compatible = "ethernet-phy-ieee802.3-c22"; + phy-is-integrated; + reg = <27>; + rtl9300,smi-address = <4 0>; + sds = < 9 >; + }; + + }; +}; + +&switch0 { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "lan1"; + phy-handle = <&phy0>; + phy-mode = "xgmii"; + led-set = <0>; + }; + port@1 { + reg = <1>; + label = "lan2"; + phy-handle = <&phy1>; + phy-mode = "xgmii"; + led-set = <0>; + }; + port@2 { + reg = <2>; + label = "lan3"; + phy-handle = <&phy2>; + phy-mode = "xgmii"; + led-set = <0>; + }; + port@3 { + reg = <3>; + label = "lan4"; + phy-handle = <&phy3>; + phy-mode = "xgmii"; + led-set = <0>; + }; + port@4 { + reg = <4>; + label = "lan5"; + phy-handle = <&phy4>; + phy-mode = "xgmii"; + led-set = <0>; + }; + port@5 { + reg = <5>; + label = "lan6"; + phy-handle = <&phy5>; + phy-mode = "xgmii"; + led-set = <0>; + }; + port@6 { + reg = <6>; + label = "lan7"; + phy-handle = <&phy6>; + phy-mode = "xgmii"; + led-set = <0>; + }; + port@7 { + reg = <7>; + label = "lan8"; + phy-handle = <&phy7>; + phy-mode = "xgmii"; + led-set = <0>; + }; + + port@24 { + reg = <24>; + label = "lan9"; + phy-mode = "usxgmii"; + phy-handle = <&phy24>; + led-set = <1>; + }; + port@25 { + reg = <25>; + label = "lan10"; + phy-mode = "usxgmii"; + phy-handle = <&phy25>; + led-set = <1>; + }; + port@26 { + reg = <26>; + label = "lan11"; + phy-mode = "usxgmii"; + phy-handle = <&phy26>; + led-set = <1>; + }; + + port@27 { + reg = <27>; + label = "lan12"; + phy-mode = "10gbase-r"; + phy-handle = <&phy27>; + sfp = <&sfp0>; + led-set = <2>; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; + + }; + + port@28 { + ethernet = <ðernet0>; + reg = <28>; + phy-mode = "internal"; + fixed-link { + speed = <10000>; + full-duplex; + }; + }; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl930x.dtsi b/target/linux/realtek/dts-5.15/rtl930x.dtsi new file mode 100644 index 00000000000..c2a992a174c --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl930x.dtsi @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "realtek,rtl838x-soc"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + frequency = <800000000>; + + cpu@0 { + compatible = "mips,mips34Kc"; + reg = <0>; + }; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + cpuintc: cpuintc { + compatible = "mti,cpu-interrupt-controller"; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + + lx_clk: lx_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <175000000>; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x18000000 0x10000>; + + intc: interrupt-controller@3000 { + compatible = "realtek,rtl9300-intc", "realtek,rtl-intc"; + reg = <0x3000 0x18>, <0x3018 0x18>; + interrupt-controller; + #interrupt-cells = <2>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>, <3>, <4>, <5>, <6>, <7>; + }; + + rtl9300clock: rtl9300clock@3200 { + compatible = "realtek,rtl9300clock"; + reg = <0x3200 0x10>, <0x3210 0x10>; + + interrupt-parent = <&intc>; + interrupts = <7 5>, <8 5>; + }; + + spi0: spi@1200 { + compatible = "realtek,rtl8380-spi"; + reg = <0x1200 0x100>; + + #address-cells = <1>; + #size-cells = <0>; + }; + + uart0: uart@2000 { + compatible = "ns16550a"; + reg = <0x2000 0x100>; + + clocks = <&lx_clk>; + + interrupt-parent = <&intc>; + interrupts = <30 1>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + }; + + uart1: uart@2100 { + compatible = "ns16550a"; + reg = <0x2100 0x100>; + + clocks = <&lx_clk>; + + interrupt-parent = <&intc>; + interrupts = <31 0>; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + + status = "disabled"; + }; + + watchdog0: watchdog@3260 { + compatible = "realtek,rtl9300-wdt"; + reg = <0x3260 0xc>; + + realtek,reset-mode = "soc"; + + clocks = <&lx_clk>; + timeout-sec = <30>; + + interrupt-parent = <&intc>; + interrupt-names = "phase1", "phase2"; + interrupts = <5 4>, <6 4>; + }; + + gpio0: gpio-controller@3300 { + compatible = "realtek,rtl9300-gpio", "realtek,otto-gpio"; + reg = <0x3300 0x1c>, <0x3338 0x8>; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <24>; + + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupts = <13 1>; + }; + + }; + + pinmux_led: pinmux@1b00cc00 { + compatible = "pinctrl-single"; + reg = <0x1b00cc00 0x4>; + + pinctrl-single,bit-per-mux; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x1>; + #pinctrl-cells = <2>; + + /* enable GPIO 0 */ + pinmux_disable_sys_led: disable_sys_led { + pinctrl-single,bits = <0x0 0x0 0x1000>; + }; + }; + + ethernet0: ethernet@1b00a300 { + compatible = "realtek,rtl838x-eth"; + reg = <0x1b00a300 0x100>; + + interrupt-parent = <&intc>; + interrupts = <24 3>; + + phy-mode = "internal"; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + switch0: switch@1b000000 { + compatible = "realtek,rtl83xx-switch"; + status = "okay"; + + interrupt-parent = <&intc>; + interrupts = <23 2>; + }; +}; diff --git a/target/linux/realtek/dts-5.15/rtl931x.dtsi b/target/linux/realtek/dts-5.15/rtl931x.dtsi new file mode 100644 index 00000000000..a5166141e6f --- /dev/null +++ b/target/linux/realtek/dts-5.15/rtl931x.dtsi @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include + +/ { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "realtek,rtl838x-soc"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + frequency = <1000000000>; + + cpu@0 { + compatible = "mti,interaptive"; + reg = <0>; + }; + + cpu@1 { + compatible = "mti,interaptive"; + reg = <1>; + }; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x10000000>; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + lx_clk: lx_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + }; + + cpuclock: cpuclock@0 { + #clock-cells = <0>; + compatible = "fixed-clock"; + + /* FIXME: there should be way to detect this */ + clock-frequency = <1000000000>; + }; + + cpuintc: cpuintc { + compatible = "mti,cpu-interrupt-controller"; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + + gic: interrupt-controller@1ddc0000 { + compatible = "mti,gic"; + reg = <0x1ddc0000 0x20000>; + + interrupt-controller; + #interrupt-cells = <3>; + + /* + * Declare the interrupt-parent even though the mti,gic + * binding doesn't require it, such that the kernel can + * figure out that cpu_intc is the root interrupt + * controller & should be probed first. + */ + interrupt-parent = <&cpuintc>; + + timer { + compatible = "mti,gic-timer"; + interrupts = ; + clocks = <&cpuclock>; + }; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x18000000 0x10000>; + + spi0: spi@1200 { + status = "okay"; + + compatible = "realtek,rtl8380-spi"; + reg = <0x1200 0x100>; + + #address-cells = <1>; + #size-cells = <0>; + }; + + watchdog0: watchdog@3260 { + compatible = "realtek,rtl9310-wdt"; + reg = <0x3260 0xc>; + + realtek,reset-mode = "soc"; + + clocks = <&lx_clk>; + timeout-sec = <30>; + + interrupt-parent = <&gic>; + interrupt-names = "phase1", "phase2"; + interrupts = , ; + }; + + gpio0: gpio-controller@3300 { + compatible = "realtek,rtl9310-gpio", "realtek,otto-gpio"; + reg = <0x3300 0x1c>; + + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + + interrupt-controller; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + uart0: uart@2000 { + compatible = "ns16550a"; + reg = <0x2000 0x100>; + + clock-frequency = <200000000>; + + interrupt-parent = <&gic>; + #interrupt-cells = <3>; + interrupts = ; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + }; + + uart1: uart@2100 { + compatible = "ns16550a"; + reg = <0x2100 0x100>; + + clock-frequency = <200000000>; + + interrupt-parent = <&gic>; + #interrupt-cells = <3>; + interrupts = ; + + reg-io-width = <1>; + reg-shift = <2>; + fifo-size = <1>; + no-loopback-test; + + status = "disabled"; + }; + }; + + pinmux: pinmux@1b001358 { + compatible = "pinctrl-single"; + reg = <0x1b001358 0x4>; + + pinctrl-single,bit-per-mux; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x1>; + #pinctrl-cells = <2>; + + /* Enable GPIO6 and GPIO7, possibly unknown others */ + pinmux_disable_jtag: disable_jtag { + pinctrl-single,bits = <0x0 0x0 0x8000>; + }; + + /* Controls GPIO0 */ + pinmux_disable_sys_led: disable_sys_led { + pinctrl-single,bits = <0x0 0x0 0x100>; + }; + }; + + ethernet0: ethernet@1b00a300 { + status = "okay"; + compatible = "realtek,rtl838x-eth"; + reg = <0x1b00a300 0x100>; + interrupt-parent = <&gic>; + #interrupt-cells = <3>; + interrupts = ; + phy-mode = "internal"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + + switch0: switch@1b000000 { + compatible = "realtek,rtl83xx-switch"; + status = "okay"; + + interrupt-parent = <&gic>; + #interrupt-cells = <3>; + interrupts = ; + }; +}; diff --git a/target/linux/realtek/files-5.15/Documentation/devicetree/bindings/realtek,otto-timer.yaml b/target/linux/realtek/files-5.15/Documentation/devicetree/bindings/realtek,otto-timer.yaml new file mode 100644 index 00000000000..b508362a728 --- /dev/null +++ b/target/linux/realtek/files-5.15/Documentation/devicetree/bindings/realtek,otto-timer.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/realtek,rtl8300-timer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Realtek Timer Device Tree Bindings + +maintainers: + - Markus Stockhausen + +description: | + The Realtek SOCs of the RTL83XX and RTL93XX series have at least 5 known + timers with corresponding interrupt lines . Their speed is derived from the + Lexra Bus (LXB) by dividers. Each timer has a block of 4 control registers in + the address range 0xb800xxxx with following start offsets. + + RTL83XX: 0x3100, 0x3110, 0x3120, 0x3130, 0x3140 + RTL93XX: 0x3200, 0x3210, 0x3220, 0x3230, 0x3240 + +properties: + compatible: + items: + - enum: + - realtek,rtl8380-timer + - realtek,rtl8390-timer + - realtek,rtl9300-timer + - const: realtek,otto-timer + + reg: + minItems: 5 + maxItems: 5 + description: + List of timer register addresses. + + interrupts: + minItems: 5 + maxItems: 5 + description: + List of timer interrupts. + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + timer0: timer@3100 { + compatible = "realtek,rtl8380-timer", "realtek,otto-timer"; + reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>, + <0x3130 0x10>, <0x3140 0x10>; + + interrupt-parent = <&intc>; + interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>; + clocks = <&ccu CLK_LXB>; + }; + - | + timer0: timer@3100 { + compatible = "realtek,rtl8390-timer", "realtek,otto-timer"; + reg = <0x3100 0x10>, <0x3110 0x10>, <0x3120 0x10>, + <0x3130 0x10>, <0x3140 0x10>; + + interrupt-parent = <&intc>; + interrupts = <29 4>, <28 4>, <17 4>, <16 4>, <15 4>; + clocks = <&ccu CLK_LXB>; + }; + - | + timer0: timer@3200 { + compatible = "realtek,rtl9300-timer", "realtek,otto-timer"; + reg = <0x3200 0x10>, <0x3210 0x10>, <0x3220 0x10>, + <0x3230 0x10>, <0x3240 0x10>; + + interrupt-parent = <&intc>; + interrupts = <7 4>, <8 4>, <9 4>, <10 4>, <11 4>; + clocks = <&ccu CLK_LXB>; + }; + +... diff --git a/target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/ioremap.h b/target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/ioremap.h new file mode 100644 index 00000000000..c49a0957923 --- /dev/null +++ b/target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/ioremap.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef RTL838X_IOREMAP_H_ +#define RTL838X_IOREMAP_H_ + +static inline int is_rtl838x_internal_registers(phys_addr_t offset) +{ + /* IO-Block */ + if (offset >= 0xb8000000 && offset < 0xb9000000) + return 1; + /* Switch block */ + if (offset >= 0xbb000000 && offset < 0xbc000000) + return 1; + return 0; +} + +static inline void __iomem *plat_ioremap(phys_addr_t offset, unsigned long size, + unsigned long flags) +{ + if (is_rtl838x_internal_registers(offset)) + return (void __iomem *)offset; + return NULL; +} + +static inline int plat_iounmap(const volatile void __iomem *addr) +{ + return is_rtl838x_internal_registers((unsigned long)addr); +} + +#endif diff --git a/target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h b/target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h new file mode 100644 index 00000000000..d95e5fb0987 --- /dev/null +++ b/target/linux/realtek/files-5.15/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h @@ -0,0 +1,416 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * Copyright (C) 2020 B. Koblitz + */ +#ifndef _MACH_RTL838X_H_ +#define _MACH_RTL838X_H_ + +#include +/* + * Register access macros + */ + +#define RTL838X_SW_BASE ((volatile void *) 0xBB000000) + +#define rtl83xx_r32(reg) readl(reg) +#define rtl83xx_w32(val, reg) writel(val, reg) +#define rtl83xx_w32_mask(clear, set, reg) rtl83xx_w32((rtl83xx_r32(reg) & ~(clear)) | (set), reg) + +#define rtl83xx_r8(reg) readb(reg) +#define rtl83xx_w8(val, reg) writeb(val, reg) + +#define sw_r32(reg) readl(RTL838X_SW_BASE + reg) +#define sw_w32(val, reg) writel(val, RTL838X_SW_BASE + reg) +#define sw_w32_mask(clear, set, reg) \ + sw_w32((sw_r32(reg) & ~(clear)) | (set), reg) +#define sw_r64(reg) ((((u64)readl(RTL838X_SW_BASE + reg)) << 32) | \ + readl(RTL838X_SW_BASE + reg + 4)) + +#define sw_w64(val, reg) do { \ + writel((u32)((val) >> 32), RTL838X_SW_BASE + reg); \ + writel((u32)((val) & 0xffffffff), \ + RTL838X_SW_BASE + reg + 4); \ + } while (0) + +/* + * SPRAM + */ +#define RTL838X_ISPRAM_BASE 0x0 +#define RTL838X_DSPRAM_BASE 0x0 + +/* + * IRQ Controller + */ +#define RTL838X_IRQ_CPU_BASE 0 +#define RTL838X_IRQ_CPU_NUM 8 +#define RTL838X_IRQ_ICTL_BASE (RTL838X_IRQ_CPU_BASE + RTL838X_IRQ_CPU_NUM) +#define RTL838X_IRQ_ICTL_NUM 32 + +#define RTL83XX_IRQ_UART0 31 +#define RTL83XX_IRQ_UART1 30 +#define RTL83XX_IRQ_TC0 29 +#define RTL83XX_IRQ_TC1 28 +#define RTL83XX_IRQ_OCPTO 27 +#define RTL83XX_IRQ_HLXTO 26 +#define RTL83XX_IRQ_SLXTO 25 +#define RTL83XX_IRQ_NIC 24 +#define RTL83XX_IRQ_GPIO_ABCD 23 +#define RTL83XX_IRQ_GPIO_EFGH 22 +#define RTL83XX_IRQ_RTC 21 +#define RTL83XX_IRQ_SWCORE 20 +#define RTL83XX_IRQ_WDT_IP1 19 +#define RTL83XX_IRQ_WDT_IP2 18 + +#define RTL9300_UART1_IRQ 31 +#define RTL9300_UART0_IRQ 30 +#define RTL9300_USB_H2_IRQ 28 +#define RTL9300_NIC_IRQ 24 +#define RTL9300_SWCORE_IRQ 23 +#define RTL9300_GPIO_ABC_IRQ 13 +#define RTL9300_TC4_IRQ 11 +#define RTL9300_TC3_IRQ 10 +#define RTL9300_TC2_IRQ 9 +#define RTL9300_TC1_IRQ 8 +#define RTL9300_TC0_IRQ 7 + + +/* + * MIPS32R2 counter + */ +#define RTL838X_COMPARE_IRQ (RTL838X_IRQ_CPU_BASE + 7) + +/* + * ICTL + * Base address 0xb8003000UL + */ +#define RTL838X_ICTL1_IRQ (RTL838X_IRQ_CPU_BASE + 2) +#define RTL838X_ICTL2_IRQ (RTL838X_IRQ_CPU_BASE + 3) +#define RTL838X_ICTL3_IRQ (RTL838X_IRQ_CPU_BASE + 4) +#define RTL838X_ICTL4_IRQ (RTL838X_IRQ_CPU_BASE + 5) +#define RTL838X_ICTL5_IRQ (RTL838X_IRQ_CPU_BASE + 6) + +#define GIMR (0x00) +#define UART0_IE (1 << 31) +#define UART1_IE (1 << 30) +#define TC0_IE (1 << 29) +#define TC1_IE (1 << 28) +#define OCPTO_IE (1 << 27) +#define HLXTO_IE (1 << 26) +#define SLXTO_IE (1 << 25) +#define NIC_IE (1 << 24) +#define GPIO_ABCD_IE (1 << 23) +#define GPIO_EFGH_IE (1 << 22) +#define RTC_IE (1 << 21) +#define WDT_IP1_IE (1 << 19) +#define WDT_IP2_IE (1 << 18) + +#define GISR (0x04) +#define UART0_IP (1 << 31) +#define UART1_IP (1 << 30) +#define TC0_IP (1 << 29) +#define TC1_IP (1 << 28) +#define OCPTO_IP (1 << 27) +#define HLXTO_IP (1 << 26) +#define SLXTO_IP (1 << 25) +#define NIC_IP (1 << 24) +#define GPIO_ABCD_IP (1 << 23) +#define GPIO_EFGH_IP (1 << 22) +#define RTC_IP (1 << 21) +#define WDT_IP1_IP (1 << 19) +#define WDT_IP2_IP (1 << 18) + + +/* Interrupt Routing Selection */ +#define UART0_RS 2 +#define UART1_RS 1 +#define TC0_RS 5 +#define TC1_RS 1 +#define OCPTO_RS 1 +#define HLXTO_RS 1 +#define SLXTO_RS 1 +#define NIC_RS 4 +#define GPIO_ABCD_RS 4 +#define GPIO_EFGH_RS 4 +#define RTC_RS 4 +#define SWCORE_RS 3 +#define WDT_IP1_RS 4 +#define WDT_IP2_RS 5 + +/* Interrupt IRQ Assignments */ +#define UART0_IRQ 31 +#define UART1_IRQ 30 +#define TC0_IRQ 29 +#define TC1_IRQ 28 +#define OCPTO_IRQ 27 +#define HLXTO_IRQ 26 +#define SLXTO_IRQ 25 +#define NIC_IRQ 24 +#define GPIO_ABCD_IRQ 23 +#define GPIO_EFGH_IRQ 22 +#define RTC_IRQ 21 +#define SWCORE_IRQ 20 +#define WDT_IP1_IRQ 19 +#define WDT_IP2_IRQ 18 + +#define SYSTEM_FREQ 200000000 +#define RTL838X_UART0_BASE ((volatile void *)(0xb8002000UL)) +#define RTL838X_UART0_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */ +#define RTL838X_UART0_FREQ (SYSTEM_FREQ - RTL838X_UART0_BAUD * 24) +#define RTL838X_UART0_MAPBASE 0x18002000UL +#define RTL838X_UART0_MAPSIZE 0x100 +#define RTL838X_UART0_IRQ UART0_IRQ + +#define RTL838X_UART1_BASE ((volatile void *)(0xb8002100UL)) +#define RTL838X_UART1_BAUD 38400 /* ex. 19200 or 38400 or 57600 or 115200 */ +#define RTL838X_UART1_FREQ (SYSTEM_FREQ - RTL838X_UART1_BAUD * 24) +#define RTL838X_UART1_MAPBASE 0x18002100UL +#define RTL838X_UART1_MAPSIZE 0x100 +#define RTL838X_UART1_IRQ UART1_IRQ + +#define UART0_RBR (RTL838X_UART0_BASE + 0x000) +#define UART0_THR (RTL838X_UART0_BASE + 0x000) +#define UART0_DLL (RTL838X_UART0_BASE + 0x000) +#define UART0_IER (RTL838X_UART0_BASE + 0x004) +#define UART0_DLM (RTL838X_UART0_BASE + 0x004) +#define UART0_IIR (RTL838X_UART0_BASE + 0x008) +#define UART0_FCR (RTL838X_UART0_BASE + 0x008) +#define UART0_LCR (RTL838X_UART0_BASE + 0x00C) +#define UART0_MCR (RTL838X_UART0_BASE + 0x010) +#define UART0_LSR (RTL838X_UART0_BASE + 0x014) + +#define UART1_RBR (RTL838X_UART1_BASE + 0x000) +#define UART1_THR (RTL838X_UART1_BASE + 0x000) +#define UART1_DLL (RTL838X_UART1_BASE + 0x000) +#define UART1_IER (RTL838X_UART1_BASE + 0x004) +#define UART1_DLM (RTL838X_UART1_BASE + 0x004) +#define UART1_IIR (RTL838X_UART1_BASE + 0x008) +#define UART1_FCR (RTL838X_UART1_BASE + 0x008) +#define UART1_LCR (RTL838X_UART1_BASE + 0x00C) +#define UART1_MCR (RTL838X_UART1_BASE + 0x010) +#define UART1_LSR (RTL838X_UART1_BASE + 0x014) + +/* + * Memory Controller + */ +#define MC_MCR 0xB8001000 +#define MC_MCR_VAL 0x00000000 + +#define MC_DCR 0xB8001004 +#define MC_DCR0_VAL 0x54480000 + +#define MC_DTCR 0xB8001008 +#define MC_DTCR_VAL 0xFFFF05C0 + +/* + * GPIO + */ +#define GPIO_CTRL_REG_BASE ((volatile void *) 0xb8003500) +#define RTL838X_GPIO_PABC_CNR (GPIO_CTRL_REG_BASE + 0x0) +#define RTL838X_GPIO_PABC_TYPE (GPIO_CTRL_REG_BASE + 0x04) +#define RTL838X_GPIO_PABC_DIR (GPIO_CTRL_REG_BASE + 0x8) +#define RTL838X_GPIO_PABC_DATA (GPIO_CTRL_REG_BASE + 0xc) +#define RTL838X_GPIO_PABC_ISR (GPIO_CTRL_REG_BASE + 0x10) +#define RTL838X_GPIO_PAB_IMR (GPIO_CTRL_REG_BASE + 0x14) +#define RTL838X_GPIO_PC_IMR (GPIO_CTRL_REG_BASE + 0x18) + +#define RTL930X_GPIO_CTRL_REG_BASE ((volatile void *) 0xb8003300) +#define RTL930X_GPIO_PABCD_DIR (RTL930X_GPIO_CTRL_REG_BASE + 0x8) +#define RTL930X_GPIO_PABCD_DAT (RTL930X_GPIO_CTRL_REG_BASE + 0xc) +#define RTL930X_GPIO_PABCD_ISR (RTL930X_GPIO_CTRL_REG_BASE + 0x10) +#define RTL930X_GPIO_PAB_IMR (RTL930X_GPIO_CTRL_REG_BASE + 0x14) +#define RTL930X_GPIO_PCD_IMR (RTL930X_GPIO_CTRL_REG_BASE + 0x18) + +#define RTL838X_MODEL_NAME_INFO (0x00D4) +#define RTL839X_MODEL_NAME_INFO (0x0FF0) +#define RTL93XX_MODEL_NAME_INFO (0x0004) +#define RTL931X_CHIP_INFO_ADDR (0x0008) + +#define RTL838X_LED_GLB_CTRL (0xA000) +#define RTL839X_LED_GLB_CTRL (0x00E4) +#define RTL9302_LED_GLB_CTRL (0xcc00) +#define RTL930X_LED_GLB_CTRL (0xCC00) +#define RTL931X_LED_GLB_CTRL (0x0600) + +#define RTL838X_EXT_GPIO_DIR (0xA08C) +#define RTL839X_EXT_GPIO_DIR (0x0214) +#define RTL838X_EXT_GPIO_DATA (0xA094) +#define RTL839X_EXT_GPIO_DATA (0x021c) +#define RTL838X_EXT_GPIO_INDRT_ACCESS (0xA09C) +#define RTL839X_EXT_GPIO_INDRT_ACCESS (0x0224) +#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0) +#define RTL838X_DMY_REG5 (0x0144) +#define RTL838X_EXTRA_GPIO_CTRL (0xA0E0) + +#define RTL838X_GMII_INTF_SEL (0x1000) +#define RTL838X_IO_DRIVING_ABILITY_CTRL (0x1010) + +#define RTL838X_GPIO_A7 31 +#define RTL838X_GPIO_A6 30 +#define RTL838X_GPIO_A5 29 +#define RTL838X_GPIO_A4 28 +#define RTL838X_GPIO_A3 27 +#define RTL838X_GPIO_A2 26 +#define RTL838X_GPIO_A1 25 +#define RTL838X_GPIO_A0 24 +#define RTL838X_GPIO_B7 23 +#define RTL838X_GPIO_B6 22 +#define RTL838X_GPIO_B5 21 +#define RTL838X_GPIO_B4 20 +#define RTL838X_GPIO_B3 19 +#define RTL838X_GPIO_B2 18 +#define RTL838X_GPIO_B1 17 +#define RTL838X_GPIO_B0 16 +#define RTL838X_GPIO_C7 15 +#define RTL838X_GPIO_C6 14 +#define RTL838X_GPIO_C5 13 +#define RTL838X_GPIO_C4 12 +#define RTL838X_GPIO_C3 11 +#define RTL838X_GPIO_C2 10 +#define RTL838X_GPIO_C1 9 +#define RTL838X_GPIO_C0 8 + +#define RTL838X_INT_RW_CTRL (0x0058) +#define RTL838X_EXT_VERSION (0x00D0) +#define RTL838X_PLL_CML_CTRL (0x0FF8) +#define RTL838X_STRAP_DBG (0x100C) + +/* + * Reset + */ +#define RGCR (0x1E70) +#define RTL838X_RST_GLB_CTRL_0 (0x003c) +#define RTL838X_RST_GLB_CTRL_1 (0x0040) +#define RTL839X_RST_GLB_CTRL (0x0014) +#define RTL930X_RST_GLB_CTRL_0 (0x000c) +#define RTL931X_RST_GLB_CTRL (0x0400) + +/* LED control by switch */ +#define RTL838X_LED_MODE_SEL (0x1004) +#define RTL838X_LED_MODE_CTRL (0xA004) +#define RTL838X_LED_P_EN_CTRL (0xA008) + +/* LED control by software */ +#define RTL838X_LED_SW_CTRL (0x0128) +#define RTL839X_LED_SW_CTRL (0xA00C) +#define RTL838X_LED_SW_P_EN_CTRL (0xA010) +#define RTL839X_LED_SW_P_EN_CTRL (0x012C) +#define RTL838X_LED0_SW_P_EN_CTRL (0xA010) +#define RTL839X_LED0_SW_P_EN_CTRL (0x012C) +#define RTL838X_LED1_SW_P_EN_CTRL (0xA014) +#define RTL839X_LED1_SW_P_EN_CTRL (0x0130) +#define RTL838X_LED2_SW_P_EN_CTRL (0xA018) +#define RTL839X_LED2_SW_P_EN_CTRL (0x0134) +#define RTL838X_LED_SW_P_CTRL (0xA01C) +#define RTL839X_LED_SW_P_CTRL (0x0144) + +#define RTL839X_MAC_EFUSE_CTRL (0x02ac) + +/* + * MDIO via Realtek's SMI interface + */ +#define RTL838X_SMI_GLB_CTRL (0xa100) +#define RTL838X_SMI_ACCESS_PHY_CTRL_0 (0xa1b8) +#define RTL838X_SMI_ACCESS_PHY_CTRL_1 (0xa1bc) +#define RTL838X_SMI_ACCESS_PHY_CTRL_2 (0xa1c0) +#define RTL838X_SMI_ACCESS_PHY_CTRL_3 (0xa1c4) +#define RTL838X_SMI_PORT0_5_ADDR_CTRL (0xa1c8) +#define RTL838X_SMI_POLL_CTRL (0xa17c) + +#define RTL839X_SMI_GLB_CTRL (0x03f8) +#define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc) +#define RTL839X_PHYREG_ACCESS_CTRL (0x03DC) +#define RTL839X_PHYREG_CTRL (0x03E0) +#define RTL839X_PHYREG_PORT_CTRL (0x03E4) +#define RTL839X_PHYREG_DATA_CTRL (0x03F0) +#define RTL839X_PHYREG_MMD_CTRL (0x3F4) + +#define RTL930X_SMI_GLB_CTRL (0xCA00) +#define RTL930X_SMI_POLL_CTRL (0xca90) +#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08) +#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C) +#define RTL930X_SMI_PORT0_5_ADDR (0xCB80) +#define RTL930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70) +#define RTL930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74) +#define RTL930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78) +#define RTL930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C) + +#define RTL931X_SMI_GLB_CTRL1 (0x0CBC) +#define RTL931X_SMI_GLB_CTRL0 (0x0CC0) +#define RTL931X_SMI_PORT_POLLING_CTRL (0x0CCC) +#define RTL931X_SMI_PORT_ADDR (0x0C74) +#define RTL931X_SMI_PORT_POLLING_SEL (0x0C9C) +#define RTL9310_SMI_PORT_POLLING_CTRL (0x0CCC) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08) +#define RTL931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10) +#define RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL (0x0C14) +#define RTL931X_SMI_INDRT_ACCESS_MMD_CTRL (0xC18) +#define RTL931X_MAC_L2_GLOBAL_CTRL2 (0x1358) +#define RTL931X_MAC_L2_GLOBAL_CTRL1 (0x5548) + +/* + * Switch interrupts + */ +#define RTL838X_IMR_GLB (0x1100) +#define RTL838X_IMR_PORT_LINK_STS_CHG (0x1104) +#define RTL838X_ISR_GLB_SRC (0x1148) +#define RTL838X_ISR_PORT_LINK_STS_CHG (0x114C) + +#define RTL839X_IMR_GLB (0x0064) +#define RTL839X_IMR_PORT_LINK_STS_CHG (0x0068) +#define RTL839X_ISR_GLB_SRC (0x009c) +#define RTL839X_ISR_PORT_LINK_STS_CHG (0x00a0) + +#define RTL930X_IMR_GLB (0xC628) +#define RTL930X_IMR_PORT_LINK_STS_CHG (0xC62C) +#define RTL930X_ISR_GLB (0xC658) +#define RTL930X_ISR_PORT_LINK_STS_CHG (0xC660) + +// IMR_GLB does not exit on RTL931X +#define RTL931X_IMR_PORT_LINK_STS_CHG (0x126C) +#define RTL931X_ISR_GLB_SRC (0x12B4) +#define RTL931X_ISR_PORT_LINK_STS_CHG (0x12B8) + +/* Definition of family IDs */ +#define RTL8389_FAMILY_ID (0x8389) +#define RTL8328_FAMILY_ID (0x8328) +#define RTL8390_FAMILY_ID (0x8390) +#define RTL8350_FAMILY_ID (0x8350) +#define RTL8380_FAMILY_ID (0x8380) +#define RTL8330_FAMILY_ID (0x8330) +#define RTL9300_FAMILY_ID (0x9300) +#define RTL9310_FAMILY_ID (0x9310) + +/* SPI Support */ +#define RTL931X_SPI_CTRL0 (0x103C) + +/* Basic SoC Features */ +#define RTL838X_CPU_PORT 28 +#define RTL839X_CPU_PORT 52 +#define RTL930X_CPU_PORT 28 +#define RTL931X_CPU_PORT 56 + +struct rtl83xx_soc_info { + unsigned char *name; + unsigned int id; + unsigned int family; + unsigned char *compatible; + volatile void *sw_base; + volatile void *icu_base; + int cpu_port; +}; + +/* rtl83xx-related functions used across subsystems */ +int rtl838x_smi_wait_op(int timeout); +int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val); + +#endif /* _MACH_RTL838X_H_ */ diff --git a/target/linux/realtek/files-5.15/arch/mips/kernel/cevt-rtl9300.c b/target/linux/realtek/files-5.15/arch/mips/kernel/cevt-rtl9300.c new file mode 100644 index 00000000000..1c8c30de5d5 --- /dev/null +++ b/target/linux/realtek/files-5.15/arch/mips/kernel/cevt-rtl9300.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Timer registers + * the RTL9300/9310 SoCs have 6 timers, each register block 0x10 apart + */ +#define RTL9300_TC_DATA 0x0 +#define RTL9300_TC_CNT 0x4 +#define RTL9300_TC_CTRL 0x8 +#define RTL9300_TC_CTRL_MODE BIT(24) +#define RTL9300_TC_CTRL_EN BIT(28) +#define RTL9300_TC_INT 0xc +#define RTL9300_TC_INT_IP BIT(16) +#define RTL9300_TC_INT_IE BIT(20) + +// Timer modes +#define TIMER_MODE_REPEAT 1 +#define TIMER_MODE_ONCE 0 + +// Minimum divider is 2 +#define DIVISOR_RTL9300 2 + +#define N_BITS 28 + +#define RTL9300_CLOCK_RATE 87500000 + +struct rtl9300_clk_dev { + struct clock_event_device clkdev; + void __iomem *base; +}; + +static void __iomem *rtl9300_tc_base(struct clock_event_device *clk) +{ + struct rtl9300_clk_dev *rtl_clk = container_of(clk, struct rtl9300_clk_dev, clkdev); + + return rtl_clk->base; +} + +static irqreturn_t rtl9300_timer_interrupt(int irq, void *dev_id) +{ + struct rtl9300_clk_dev *rtl_clk = dev_id; + struct clock_event_device *clk = &rtl_clk->clkdev; + + u32 v = readl(rtl_clk->base + RTL9300_TC_INT); + + // Acknowledge the IRQ + v |= RTL9300_TC_INT_IP; + writel(v, rtl_clk->base + RTL9300_TC_INT); + + clk->event_handler(clk); + return IRQ_HANDLED; +} + +static void rtl9300_clock_stop(void __iomem *base) +{ + u32 v; + + writel(0, base + RTL9300_TC_CTRL); + + // Acknowledge possibly pending IRQ + v = readl(base + RTL9300_TC_INT); + writel(v | RTL9300_TC_INT_IP, base + RTL9300_TC_INT); +} + +static void rtl9300_timer_start(void __iomem *base, bool periodic) +{ + u32 v = (periodic ? RTL9300_TC_CTRL_MODE : 0) | RTL9300_TC_CTRL_EN | DIVISOR_RTL9300; + + writel(0, base + RTL9300_TC_CNT); + pr_debug("------------- starting timer base %08x\n", (u32)base); + writel(v, base + RTL9300_TC_CTRL); +} + +static int rtl9300_next_event(unsigned long delta, struct clock_event_device *clk) +{ + void __iomem *base = rtl9300_tc_base(clk); + + rtl9300_clock_stop(base); + writel(delta, base + RTL9300_TC_DATA); + rtl9300_timer_start(base, TIMER_MODE_ONCE); + + return 0; +} + +static int rtl9300_state_periodic(struct clock_event_device *clk) +{ + void __iomem *base = rtl9300_tc_base(clk); + + pr_debug("------------- rtl9300_state_periodic %08x\n", (u32)base); + rtl9300_clock_stop(base); + writel(RTL9300_CLOCK_RATE / HZ, base + RTL9300_TC_DATA); + rtl9300_timer_start(base, TIMER_MODE_REPEAT); + return 0; +} + +static int rtl9300_state_oneshot(struct clock_event_device *clk) +{ + void __iomem *base = rtl9300_tc_base(clk); + + pr_debug("------------- rtl9300_state_oneshot %08x\n", (u32)base); + rtl9300_clock_stop(base); + writel(RTL9300_CLOCK_RATE / HZ, base + RTL9300_TC_DATA); + rtl9300_timer_start(base, TIMER_MODE_ONCE); + return 0; +} + +static int rtl9300_shutdown(struct clock_event_device *clk) +{ + void __iomem *base = rtl9300_tc_base(clk); + + pr_debug("------------- rtl9300_shutdown %08x\n", (u32)base); + rtl9300_clock_stop(base); + return 0; +} + +static void rtl9300_clock_setup(void __iomem *base) +{ + u32 v; + + // Disable timer + writel(0, base + RTL9300_TC_CTRL); + + // Acknowledge possibly pending IRQ + v = readl(base + RTL9300_TC_INT); + writel(v | RTL9300_TC_INT_IP, base + RTL9300_TC_INT); + + // Setup maximum period (for use as clock-source) + writel(0x0fffffff, base + RTL9300_TC_DATA); +} + +static DEFINE_PER_CPU(struct rtl9300_clk_dev, rtl9300_clockevent); +static DEFINE_PER_CPU(char [18], rtl9300_clock_name); + +void rtl9300_clockevent_init(void) +{ + int cpu = smp_processor_id(); + int irq; + struct rtl9300_clk_dev *rtl_clk = &per_cpu(rtl9300_clockevent, cpu); + struct clock_event_device *cd = &rtl_clk->clkdev; + unsigned char *name = per_cpu(rtl9300_clock_name, cpu); + unsigned long flags = IRQF_PERCPU | IRQF_TIMER; + struct device_node *node; + + pr_info("%s called for cpu%d\n", __func__, cpu); + BUG_ON(cpu > 3); /* Only have 4 general purpose timers */ + + node = of_find_compatible_node(NULL, NULL, "realtek,rtl9300clock"); + if (!node) { + pr_err("No DT entry found for realtek,rtl9300clock\n"); + return; + } + + irq = irq_of_parse_and_map(node, cpu); + pr_info("%s using IRQ %d\n", __func__, irq); + + rtl_clk->base = of_iomap(node, cpu); + if (!rtl_clk->base) { + pr_err("cannot map timer for cpu %d", cpu); + return; + } + + rtl9300_clock_setup(rtl_clk->base); + + sprintf(name, "rtl9300-counter-%d", cpu); + cd->name = name; + cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + + clockevent_set_clock(cd, RTL9300_CLOCK_RATE); + + cd->max_delta_ns = clockevent_delta2ns(0x0fffffff, cd); + cd->max_delta_ticks = 0x0fffffff; + cd->min_delta_ns = clockevent_delta2ns(0x20, cd); + cd->min_delta_ticks = 0x20; + cd->rating = 300; + cd->irq = irq; + cd->cpumask = cpumask_of(cpu); + cd->set_next_event = rtl9300_next_event; + cd->set_state_shutdown = rtl9300_shutdown; + cd->set_state_periodic = rtl9300_state_periodic; + cd->set_state_oneshot = rtl9300_state_oneshot; + clockevents_register_device(cd); + + irq_set_affinity(irq, cd->cpumask); + + if (request_irq(irq, rtl9300_timer_interrupt, flags, name, rtl_clk)) + pr_err("Failed to request irq %d (%s)\n", irq, name); + + writel(RTL9300_TC_INT_IE, rtl_clk->base + RTL9300_TC_INT); +} diff --git a/target/linux/realtek/files-5.15/arch/mips/rtl838x/Makefile b/target/linux/realtek/files-5.15/arch/mips/rtl838x/Makefile new file mode 100644 index 00000000000..a9d1666d465 --- /dev/null +++ b/target/linux/realtek/files-5.15/arch/mips/rtl838x/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the rtl838x specific parts of the kernel +# + +obj-y := setup.o prom.o diff --git a/target/linux/realtek/files-5.15/arch/mips/rtl838x/Platform b/target/linux/realtek/files-5.15/arch/mips/rtl838x/Platform new file mode 100644 index 00000000000..9d45d2ddd5b --- /dev/null +++ b/target/linux/realtek/files-5.15/arch/mips/rtl838x/Platform @@ -0,0 +1,5 @@ +# +# Realtek RTL838x SoCs +# +cflags-$(CONFIG_RTL83XX) += -I$(srctree)/arch/mips/include/asm/mach-rtl838x/ +load-$(CONFIG_RTL83XX) += 0xffffffff80000000 diff --git a/target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c b/target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c new file mode 100644 index 00000000000..dd1b2b170d3 --- /dev/null +++ b/target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * prom.c + * Early intialization code for the Realtek RTL838X SoC + * + * based on the original BSP by + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * Copyright (C) 2020 B. Koblitz + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern char arcs_cmdline[]; +extern const char __appended_dtb; + +struct rtl83xx_soc_info soc_info; +const void *fdt; + +#ifdef CONFIG_MIPS_MT_SMP +extern const struct plat_smp_ops vsmp_smp_ops; +static struct plat_smp_ops rtl_smp_ops; + +static void rtl_init_secondary(void) +{ +#ifndef CONFIG_CEVT_R4K +/* + * These devices are low on resources. There might be the chance that CEVT_R4K + * is not enabled in kernel build. Nevertheless the timer and interrupt 7 might + * be active by default after startup of secondary VPE. With no registered + * handler that leads to continuous unhandeled interrupts. In this case disable + * counting (DC) in the core and confirm a pending interrupt. + */ + write_c0_cause(read_c0_cause() | CAUSEF_DC); + write_c0_compare(0); +#endif /* CONFIG_CEVT_R4K */ +/* + * Enable all CPU interrupts, as everything is managed by the external + * controller. TODO: Standard vsmp_init_secondary() has special treatment for + * Malta if external GIC is available. Maybe we need this too. + */ + if (mips_gic_present()) + pr_warn("%s: GIC present. Maybe interrupt enabling required.\n", __func__); + else + set_c0_status(ST0_IM); +} +#endif /* CONFIG_MIPS_MT_SMP */ + +const char *get_system_type(void) +{ + return soc_info.name; +} + +void __init prom_free_prom_memory(void) +{ + +} + +void __init device_tree_init(void) +{ + if (!fdt_check_header(&__appended_dtb)) { + fdt = &__appended_dtb; + pr_info("Using appended Device Tree.\n"); + } + initial_boot_params = (void *)fdt; + unflatten_and_copy_device_tree(); +} + +void __init identify_rtl9302(void) +{ + switch (sw_r32(RTL93XX_MODEL_NAME_INFO) & 0xfffffff0) { + case 0x93020810: + soc_info.name = "RTL9302A 12x2.5G"; + break; + case 0x93021010: + soc_info.name = "RTL9302B 8x2.5G"; + break; + case 0x93021810: + soc_info.name = "RTL9302C 16x2.5G"; + break; + case 0x93022010: + soc_info.name = "RTL9302D 24x2.5G"; + break; + case 0x93020800: + soc_info.name = "RTL9302A"; + break; + case 0x93021000: + soc_info.name = "RTL9302B"; + break; + case 0x93021800: + soc_info.name = "RTL9302C"; + break; + case 0x93022000: + soc_info.name = "RTL9302D"; + break; + case 0x93023001: + soc_info.name = "RTL9302F"; + break; + default: + soc_info.name = "RTL9302"; + } +} + +void __init prom_init(void) +{ + uint32_t model; + + /* uart0 */ + setup_8250_early_printk_port(0xb8002000, 2, 0); + + model = sw_r32(RTL838X_MODEL_NAME_INFO); + pr_info("RTL838X model is %x\n", model); + model = model >> 16 & 0xFFFF; + + if ((model != 0x8328) && (model != 0x8330) && (model != 0x8332) + && (model != 0x8380) && (model != 0x8382)) { + model = sw_r32(RTL839X_MODEL_NAME_INFO); + pr_info("RTL839X model is %x\n", model); + model = model >> 16 & 0xFFFF; + } + + if ((model & 0x8390) != 0x8380 && (model & 0x8390) != 0x8390) { + model = sw_r32(RTL93XX_MODEL_NAME_INFO); + pr_info("RTL93XX model is %x\n", model); + model = model >> 16 & 0xFFFF; + } + + soc_info.id = model; + + switch (model) { + case 0x8328: + soc_info.name = "RTL8328"; + soc_info.family = RTL8328_FAMILY_ID; + break; + case 0x8332: + soc_info.name = "RTL8332"; + soc_info.family = RTL8380_FAMILY_ID; + break; + case 0x8380: + soc_info.name = "RTL8380"; + soc_info.family = RTL8380_FAMILY_ID; + break; + case 0x8382: + soc_info.name = "RTL8382"; + soc_info.family = RTL8380_FAMILY_ID; + break; + case 0x8390: + soc_info.name = "RTL8390"; + soc_info.family = RTL8390_FAMILY_ID; + break; + case 0x8391: + soc_info.name = "RTL8391"; + soc_info.family = RTL8390_FAMILY_ID; + break; + case 0x8392: + soc_info.name = "RTL8392"; + soc_info.family = RTL8390_FAMILY_ID; + break; + case 0x8393: + soc_info.name = "RTL8393"; + soc_info.family = RTL8390_FAMILY_ID; + break; + case 0x9301: + soc_info.name = "RTL9301"; + soc_info.family = RTL9300_FAMILY_ID; + break; + case 0x9302: + identify_rtl9302(); + soc_info.family = RTL9300_FAMILY_ID; + break; + case 0x9303: + soc_info.name = "RTL9303"; + soc_info.family = RTL9300_FAMILY_ID; + break; + case 0x9313: + soc_info.name = "RTL9313"; + soc_info.family = RTL9310_FAMILY_ID; + break; + default: + soc_info.name = "DEFAULT"; + soc_info.family = 0; + } + + pr_info("SoC Type: %s\n", get_system_type()); + + fw_init_cmdline(); + + mips_cpc_probe(); + + if (!register_cps_smp_ops()) + return; + +#ifdef CONFIG_MIPS_MT_SMP + if (cpu_has_mipsmt) { + rtl_smp_ops = vsmp_smp_ops; + rtl_smp_ops.init_secondary = rtl_init_secondary; + register_smp_ops(&rtl_smp_ops); + return; + } +#endif + + register_up_smp_ops(); +} diff --git a/target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c b/target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c new file mode 100644 index 00000000000..b4d415ab442 --- /dev/null +++ b/target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Setup for the Realtek RTL838X SoC: + * Memory, Timer and Serial + * + * Copyright (C) 2020 B. Koblitz + * based on the original BSP by + * Copyright (C) 2006-2012 Tony Wu (tonywu@realtek.com) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mach-rtl83xx.h" + +extern struct rtl83xx_soc_info soc_info; + +void __init plat_mem_setup(void) +{ + void *dtb; + + set_io_port_base(KSEG1); + + if (fw_passed_dtb) /* UHI interface */ + dtb = (void *)fw_passed_dtb; + else if (&__dtb_start[0] != &__dtb_end[0]) + dtb = (void *)__dtb_start; + else + panic("no dtb found"); + + /* + * Load the devicetree. This causes the chosen node to be + * parsed resulting in our memory appearing + */ + __dt_setup_arch(dtb); +} + +void plat_time_init_fallback(void) +{ + struct device_node *np; + u32 freq = 500000000; + + np = of_find_node_by_name(NULL, "cpus"); + if (!np) { + pr_err("Missing 'cpus' DT node, using default frequency."); + } else { + if (of_property_read_u32(np, "frequency", &freq) < 0) + pr_err("No 'frequency' property in DT, using default."); + else + pr_info("CPU frequency from device tree: %dMHz", freq / 1000000); + of_node_put(np); + } + mips_hpt_frequency = freq / 2; +} + +void __init plat_time_init(void) +{ +/* + * Initialization routine resembles generic MIPS plat_time_init() with + * lazy error handling. The final fallback is only needed until we have + * converted all device trees to new clock syntax. + */ + struct device_node *np; + struct clk *clk; + + of_clk_init(NULL); + + mips_hpt_frequency = 0; + np = of_get_cpu_node(0, NULL); + if (!np) { + pr_err("Failed to get CPU node\n"); + } else { + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk)); + } else { + mips_hpt_frequency = clk_get_rate(clk) / 2; + clk_put(clk); + } + } + + if (!mips_hpt_frequency) + plat_time_init_fallback(); + + timer_probe(); +} + +void __init arch_init_irq(void) +{ + irqchip_init(); +} diff --git a/target/linux/realtek/files-5.15/drivers/clk/realtek/Kconfig b/target/linux/realtek/files-5.15/drivers/clk/realtek/Kconfig new file mode 100644 index 00000000000..4cf3cd96339 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/clk/realtek/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig COMMON_CLK_REALTEK + bool "Support for Realtek's clock controllers" + depends on RTL83XX + +if COMMON_CLK_REALTEK + +config COMMON_CLK_RTL83XX + bool "Clock driver for Realtek RTL83XX" + depends on RTL83XX + select SRAM + help + This driver adds support for the Realtek RTL83xx series basic clocks. + This includes chips in the RTL838x series, such as RTL8380, RTL8381, + RTL832, as well as chips from the RTL839x series, such as RTL8390, + RT8391, RTL8392, RTL8393 and RTL8396. + +endif diff --git a/target/linux/realtek/files-5.15/drivers/clk/realtek/Makefile b/target/linux/realtek/files-5.15/drivers/clk/realtek/Makefile new file mode 100644 index 00000000000..7bc4ed910c5 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/clk/realtek/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_COMMON_CLK_RTL83XX) += clk-rtl83xx.o clk-rtl838x-sram.o clk-rtl839x-sram.o diff --git a/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl838x-sram.S b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl838x-sram.S new file mode 100644 index 00000000000..527436bbab4 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl838x-sram.S @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Realtek RTL838X SRAM clock setters + * Copyright (C) 2022 Markus Stockhausen + */ + +#include + +#include "clk-rtl83xx.h" + +#define rGLB $t0 +#define rCTR $t1 +#define rMSK $t2 +#define rSLP $t3 +#define rTMP $t4 + +.set noreorder + +.globl rtcl_838x_dram_start +rtcl_838x_dram_start: + +/* + * Functions start here and should avoid access to normal memory. REMARK! Do not forget about + * stack pointer and dirty caches that might interfere. + */ + +.globl rtcl_838x_dram_set_rate +.ent rtcl_838x_dram_set_rate +rtcl_838x_dram_set_rate: + +#ifdef CONFIG_RTL838X + + li rCTR, RTL_SW_CORE_BASE + addiu rGLB, rCTR, RTL838X_PLL_GLB_CTRL + ori rTMP, $0, CLK_CPU + beq $a0, rTMP, pre_cpu + ori rTMP, $0, CLK_MEM + beq $a0, rTMP, pre_mem + nop +pre_lxb: + ori rSLP, $0, RTL838X_GLB_CTRL_LXB_PLL_READY_MASK + addiu rCTR, rCTR, RTL838X_PLL_LXB_CTRL0 + b main_set + ori rMSK, $0, RTL838X_GLB_CTRL_EN_LXB_PLL_MASK +pre_mem: + /* simple 64K data cache flush to avoid unexpected memory access */ + li rMSK, RTL_SRAM_BASE + li rTMP, 2048 +pre_flush: + lw $0, 0(rMSK) + addiu rMSK, rMSK, 32 + addiu rTMP, rTMP, -1 + bne rTMP, $0, pre_flush + lw $0, -4(rMSK) + + ori rSLP, $0, RTL838X_GLB_CTRL_MEM_PLL_READY_MASK + addiu rCTR, rCTR, RTL838X_PLL_MEM_CTRL0 + b main_set + ori rMSK, $0, RTL838X_GLB_CTRL_EN_MEM_PLL_MASK +pre_cpu: + /* switch CPU to LXB clock */ + ori rMSK, $0, RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK + nor rMSK, rMSK, $0 + sync + lw rTMP, 0(rGLB) + and rTMP, rTMP, rMSK + sw rTMP, 0(rGLB) + sync + + ori rSLP, $0, RTL838X_GLB_CTRL_CPU_PLL_READY_MASK + addiu rCTR, rCTR, RTL838X_PLL_CPU_CTRL0 + ori rMSK, $0, RTL838X_GLB_CTRL_EN_CPU_PLL_MASK +main_set: + /* disable PLL */ + nor rMSK, rMSK, 0 + sync + lw rTMP, 0(rGLB) + sync + and rTMP, rTMP, rMSK + sync + sw rTMP, 0(rGLB) + + /* set new PLL values */ + sync + sw $a1, 0(rCTR) + sw $a2, 4(rCTR) + sync + + /* enable PLL (will reset it and clear ready status) */ + nor rMSK, rMSK, 0 + sync + lw rTMP, 0(rGLB) + sync + or rTMP, rTMP, rMSK + sync + sw rTMP, 0(rGLB) + + /* wait for PLL to become ready */ +wait_ready: + lw rTMP, 0(rGLB) + and rTMP, rTMP, rSLP + bne rTMP, $0, wait_ready + sync + + /* branch to post processing */ + ori rTMP, $0, CLK_CPU + beq $a0, rTMP, post_cpu + ori rTMP, $0, CLK_MEM + beq $a0, rTMP, post_mem + nop +post_lxb: + jr $ra + nop +post_mem: + jr $ra + nop +post_cpu: + /* stabilize clock to avoid crash, empirically determined */ + ori rSLP, $0, 0x3000 +wait_cpu: + bnez rSLP, wait_cpu + addiu rSLP, rSLP, -1 + + /* switch CPU to PLL clock */ + ori rMSK, $0, RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK + sync + lw rTMP, 0(rGLB) + or rTMP, rTMP, rMSK + sw rTMP, 0(rGLB) + sync + jr $ra + nop + +#else /* !CONFIG_RTL838X */ + + jr $ra + nop + +#endif + +.end rtcl_838x_dram_set_rate + +/* + * End marker. Do not delete. + */ + .word RTL_SRAM_MARKER +.globl rtcl_838x_dram_size +rtcl_838x_dram_size: + .word .-rtcl_838x_dram_start + diff --git a/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl839x-sram.S b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl839x-sram.S new file mode 100644 index 00000000000..cd43dfaabd0 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl839x-sram.S @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Realtek RTL839X SRAM clock setters + * Copyright (C) 2022 Markus Stockhausen + */ + +#include +#include + +#include "clk-rtl83xx.h" + +#define rGLB $t0 +#define rCTR $t1 +#define rMSK $t2 +#define rSLP1 $t3 +#define rSLP2 $t4 +#define rSLP3 $t5 +#define rTMP $t6 +#define rCP0 $t7 + +.set noreorder + +.globl rtcl_839x_dram_start +rtcl_839x_dram_start: + +/* + * Functions start here and should avoid access to normal memory. REMARK! Do not forget about + * stack pointer and dirty caches that might interfere. + */ + +.globl rtcl_839x_dram_set_rate +.ent rtcl_839x_dram_set_rate +rtcl_839x_dram_set_rate: + +#ifdef CONFIG_RTL839X + + /* disable MIPS 34K branch and return prediction */ + mfc0 rCP0, CP0_CONFIG, 7 + ori rTMP, rCP0, 0xc + mtc0 rTMP, CP0_CONFIG, 7 + + li rCTR, RTL_SW_CORE_BASE + addiu rGLB, rCTR, RTL839X_PLL_GLB_CTRL + ori rTMP, $0, CLK_CPU + beq $a0, rTMP, pre_cpu + ori rTMP, $0, CLK_MEM + beq $a0, rTMP, pre_mem + nop +pre_lxb: + li rSLP1, 0x400000 + li rSLP2, 0x400000 + li rSLP3, 0x400000 + addiu rCTR, rCTR, RTL839X_PLL_LXB_CTRL0 + b main_set + ori rMSK, $0, RTL839X_GLB_CTRL_LXB_CLKSEL_MASK +pre_mem: + /* try to avoid memory access with simple 64K data cache flush */ + li rMSK, RTL_SRAM_BASE + li rTMP, 2048 +pre_flush: + lw $0, 0(rMSK) + addiu rMSK, rMSK, 32 + addiu rTMP, rTMP, -1 + bne rTMP, $0, pre_flush + lw $0, -4(rMSK) + + li rSLP1, 0x10000 + li rSLP2, 0x10000 + li rSLP3, 0x10000 + addiu rCTR, rCTR, RTL839X_PLL_MEM_CTRL0 + b main_set + ori rMSK, $0, RTL839X_GLB_CTRL_MEM_CLKSEL_MASK +pre_cpu: + li rSLP1, 0x1000 + li rSLP2, 0x1000 + li rSLP3, 0x200 + addiu rCTR, rCTR, RTL839X_PLL_CPU_CTRL0 + ori rMSK, $0, RTL839X_GLB_CTRL_CPU_CLKSEL_MASK +main_set: + /* switch to fixed clock */ + sync + lw rTMP, 0(rGLB) + sync + or rTMP, rTMP, rMSK + sync + sw rTMP, 0(rGLB) + + /* wait until fixed clock in use */ + or rTMP, rSLP1, $0 +wait_fixclock: + bnez rTMP, wait_fixclock + addiu rTMP, rTMP, -1 + + /* set new PLL values */ + sync + sw $a1, 0(rCTR) + sw $a2, 4(rCTR) + sync + + /* wait for value takeover */ + or rTMP, rSLP2, $0 +wait_pll: + bnez rTMP, wait_pll + addiu rTMP, rTMP, -1 + + /* switch back to PLL clock*/ + nor rMSK, rMSK, $0 + sync + lw rTMP, 0(rGLB) + sync + and rTMP, rTMP, rMSK + sync + sw rTMP, 0(rGLB) + + /* wait until PLL clock in use */ + or rTMP, rSLP3, $0 +wait_pllclock: + bnez rTMP, wait_pllclock + addiu rTMP, rTMP, -1 + + /* restore branch prediction */ + mtc0 rCP0, CP0_CONFIG, 7 + jr $ra + nop + +#else /* !CONFIG_RTL839X */ + + jr $ra + nop + +#endif + +.end rtcl_839x_dram_set_rate + +/* + * End marker. Do not delete. + */ + .word RTL_SRAM_MARKER +.globl rtcl_839x_dram_size +rtcl_839x_dram_size: + .word .-rtcl_839x_dram_start + diff --git a/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.c b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.c new file mode 100644 index 00000000000..9b8183fbebd --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.c @@ -0,0 +1,766 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Realtek RTL83XX clock driver + * Copyright (C) 2022 Markus Stockhausen + * + * This driver provides basic clock support for the central core clock unit (CCU) and its PLLs + * inside the RTL838X and RTL8389X SOC. Currently CPU, memory and LXB clock information can be + * accessed. To make use of the driver add the following devices and configurations at the + * appropriate locations to the DT. + * + * #include + * + * sram0: sram@9f000000 { + * compatible = "mmio-sram"; + * reg = <0x9f000000 0x18000>; + * #address-cells = <1>; + * #size-cells = <1>; + * ranges = <0 0x9f000000 0x18000>; + * }; + * + * osc: oscillator { + * compatible = "fixed-clock"; + * #clock-cells = <0>; + * clock-frequency = <25000000>; + * }; + * + * ccu: clock-controller { + * compatible = "realtek,rtl8380-clock"; + * #clock-cells = <1>; + * clocks = <&osc>; + * clock-names = "ref_clk"; + * }; + * + * + * The SRAM part is needed to be able to set clocks. When changing clocks the code must not run + * from DRAM. Otherwise system might freeze. Take care to adjust CCU compatibility, SRAM address + * and size to the target SOC device. Afterwards one can access/identify the clocks in the other + * DT devices with <&ccu CLK_CPU>, <&ccu CLK_MEM> or <&ccu CLK_LXB>. Additionally the clocks can + * be used inside the kernel with + * + * cpu_clk = clk_get(NULL, "cpu_clk"); + * mem_clk = clk_get(NULL, "mem_clk"); + * lxb_clk = clk_get(NULL, "lxb_clk"); + * + * This driver can be directly used by the DT based cpufreq driver (CONFIG_CPUFREQ_DT) if CPU + * references the right clock and sane operating points (OPP) are provided. E.g. + * + * cpu@0 { + * compatible = "mips,mips4KEc"; + * reg = <0>; + * clocks = <&ccu CLK_CPU>; + * operating-points-v2 = <&cpu_opp_table>; + * }; + * + * cpu_opp_table: opp-table-0 { + * compatible = "operating-points-v2"; + * opp-shared; + * opp00 { + * opp-hz = /bits/ 64 <425000000>; + * }; + * ... + * } + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-rtl83xx.h" + +#define read_sw(reg) ioread32(((void *)RTL_SW_CORE_BASE) + reg) +#define read_soc(reg) ioread32(((void *)RTL_SOC_BASE) + reg) + +#define write_sw(val, reg) iowrite32(val, ((void *)RTL_SW_CORE_BASE) + reg) +#define write_soc(val, reg) iowrite32(val, ((void *)RTL_SOC_BASE) + reg) + +/* + * some hardware specific definitions + */ + +#define SOC_RTL838X 0 +#define SOC_RTL839X 1 +#define SOC_COUNT 2 + +#define MEM_DDR1 1 +#define MEM_DDR2 2 +#define MEM_DDR3 3 + +#define REG_CTRL0 0 +#define REG_CTRL1 1 +#define REG_COUNT 2 + +#define OSC_RATE 25000000 + +static const int rtcl_regs[SOC_COUNT][REG_COUNT][CLK_COUNT] = { + { + { RTL838X_PLL_CPU_CTRL0, RTL838X_PLL_MEM_CTRL0, RTL838X_PLL_LXB_CTRL0 }, + { RTL838X_PLL_CPU_CTRL1, RTL838X_PLL_MEM_CTRL1, RTL838X_PLL_LXB_CTRL1 }, + }, { + { RTL839X_PLL_CPU_CTRL0, RTL839X_PLL_MEM_CTRL0, RTL839X_PLL_LXB_CTRL0 }, + { RTL839X_PLL_CPU_CTRL1, RTL839X_PLL_MEM_CTRL1, RTL839X_PLL_LXB_CTRL1 }, + } +}; + +#define RTCL_REG_SET(_rate, _ctrl0, _ctrl1) \ + { \ + .rate = _rate, \ + .ctrl0 = _ctrl0, \ + .ctrl1 = _ctrl1, \ + } + +struct rtcl_reg_set { + unsigned int rate; + unsigned int ctrl0; + unsigned int ctrl1; +}; + +/* + * The following configuration tables are valid operation points for their corresponding PLLs. + * The magic numbers are precalculated mulitpliers and dividers to keep the driver simple. They + * also provide rates outside the allowed physical specifications. E.g. DDR3 memory has a lower + * limit of 303 MHz or the CPU might get unstable if set to anything above its startup frequency. + * Additionally the Realtek SOCs tend to expect CPU speed > MEM speed > LXB speed. The caller or + * DT configuration must take care that only valid operating points are selected. + */ + +static const struct rtcl_reg_set rtcl_838x_cpu_reg_set[] = { + RTCL_REG_SET(300000000, 0x045c8, 0x1414530e), + RTCL_REG_SET(325000000, 0x04648, 0x1414530e), + RTCL_REG_SET(350000000, 0x046c8, 0x1414530e), + RTCL_REG_SET(375000000, 0x04748, 0x1414530e), + RTCL_REG_SET(400000000, 0x045c8, 0x0c14530e), + RTCL_REG_SET(425000000, 0x04628, 0x0c14530e), + RTCL_REG_SET(450000000, 0x04688, 0x0c14530e), + RTCL_REG_SET(475000000, 0x046e8, 0x0c14530e), + RTCL_REG_SET(500000000, 0x04748, 0x0c14530e), + RTCL_REG_SET(525000000, 0x047a8, 0x0c14530e), + RTCL_REG_SET(550000000, 0x04808, 0x0c14530e), + RTCL_REG_SET(575000000, 0x04868, 0x0c14530e), + RTCL_REG_SET(600000000, 0x048c8, 0x0c14530e), + RTCL_REG_SET(625000000, 0x04928, 0x0c14530e) +}; + +static const struct rtcl_reg_set rtcl_838x_mem_reg_set[] = { + RTCL_REG_SET(200000000, 0x041bc, 0x14018C80), + RTCL_REG_SET(225000000, 0x0417c, 0x0c018C80), + RTCL_REG_SET(250000000, 0x041ac, 0x0c018C80), + RTCL_REG_SET(275000000, 0x0412c, 0x04018C80), + RTCL_REG_SET(300000000, 0x0414c, 0x04018c80), + RTCL_REG_SET(325000000, 0x0416c, 0x04018c80), + RTCL_REG_SET(350000000, 0x0418c, 0x04018c80), + RTCL_REG_SET(375000000, 0x041ac, 0x04018c80) +}; + +static const struct rtcl_reg_set rtcl_838x_lxb_reg_set[] = { + RTCL_REG_SET(100000000, 0x043c8, 0x001ad30e), + RTCL_REG_SET(125000000, 0x043c8, 0x001ad30e), + RTCL_REG_SET(150000000, 0x04508, 0x1c1ad30e), + RTCL_REG_SET(175000000, 0x04508, 0x1c1ad30e), + RTCL_REG_SET(200000000, 0x047c8, 0x001ad30e) +}; + +static const struct rtcl_reg_set rtcl_839x_cpu_reg_set[] = { + RTCL_REG_SET(400000000, 0x0414c, 0x00000005), + RTCL_REG_SET(425000000, 0x041ec, 0x00000006), + RTCL_REG_SET(450000000, 0x0417c, 0x00000005), + RTCL_REG_SET(475000000, 0x0422c, 0x00000006), + RTCL_REG_SET(500000000, 0x041ac, 0x00000005), + RTCL_REG_SET(525000000, 0x0426c, 0x00000006), + RTCL_REG_SET(550000000, 0x0412c, 0x00000004), + RTCL_REG_SET(575000000, 0x042ac, 0x00000006), + RTCL_REG_SET(600000000, 0x0414c, 0x00000004), + RTCL_REG_SET(625000000, 0x042ec, 0x00000006), + RTCL_REG_SET(650000000, 0x0416c, 0x00000004), + RTCL_REG_SET(675000000, 0x04324, 0x00000006), + RTCL_REG_SET(700000000, 0x0418c, 0x00000004), + RTCL_REG_SET(725000000, 0x0436c, 0x00000006), + RTCL_REG_SET(750000000, 0x0438c, 0x00000006), + RTCL_REG_SET(775000000, 0x043ac, 0x00000006), + RTCL_REG_SET(800000000, 0x043cc, 0x00000006), + RTCL_REG_SET(825000000, 0x043ec, 0x00000006), + RTCL_REG_SET(850000000, 0x0440c, 0x00000006) +}; + +static const struct rtcl_reg_set rtcl_839x_mem_reg_set[] = { + RTCL_REG_SET(100000000, 0x041cc, 0x00000000), + RTCL_REG_SET(125000000, 0x041ac, 0x00000007), + RTCL_REG_SET(150000000, 0x0414c, 0x00000006), + RTCL_REG_SET(175000000, 0x0418c, 0x00000006), + RTCL_REG_SET(200000000, 0x041cc, 0x00000006), + RTCL_REG_SET(225000000, 0x0417c, 0x00000005), + RTCL_REG_SET(250000000, 0x041ac, 0x00000005), + RTCL_REG_SET(275000000, 0x0412c, 0x00000004), + RTCL_REG_SET(300000000, 0x0414c, 0x00000004), + RTCL_REG_SET(325000000, 0x0416c, 0x00000004), + RTCL_REG_SET(350000000, 0x0418c, 0x00000004), + RTCL_REG_SET(375000000, 0x041ac, 0x00000004), + RTCL_REG_SET(400000000, 0x041cc, 0x00000004) +}; + +static const struct rtcl_reg_set rtcl_839x_lxb_reg_set[] = { + RTCL_REG_SET(50000000, 0x1414c, 0x00000003), + RTCL_REG_SET(100000000, 0x0814c, 0x00000003), + RTCL_REG_SET(150000000, 0x0414c, 0x00000003), + RTCL_REG_SET(200000000, 0x0414c, 0x00000007) +}; + +struct rtcl_rtab_set { + int count; + const struct rtcl_reg_set *rset; +}; + +#define RTCL_RTAB_SET(_rset) \ + { \ + .count = ARRAY_SIZE(_rset), \ + .rset = _rset, \ + } + +static const struct rtcl_rtab_set rtcl_rtab_set[SOC_COUNT][CLK_COUNT] = { + { + RTCL_RTAB_SET(rtcl_838x_cpu_reg_set), + RTCL_RTAB_SET(rtcl_838x_mem_reg_set), + RTCL_RTAB_SET(rtcl_838x_lxb_reg_set) + }, { + RTCL_RTAB_SET(rtcl_839x_cpu_reg_set), + RTCL_RTAB_SET(rtcl_839x_mem_reg_set), + RTCL_RTAB_SET(rtcl_839x_lxb_reg_set) + } +}; + +#define RTCL_ROUND_SET(_min, _max, _step) \ + { \ + .min = _min, \ + .max = _max, \ + .step = _step, \ + } + +struct rtcl_round_set { + unsigned long min; + unsigned long max; + unsigned long step; +}; + +static const struct rtcl_round_set rtcl_round_set[SOC_COUNT][CLK_COUNT] = { + { + RTCL_ROUND_SET(300000000, 625000000, 25000000), + RTCL_ROUND_SET(200000000, 375000000, 25000000), + RTCL_ROUND_SET(100000000, 200000000, 25000000) + }, { + RTCL_ROUND_SET(400000000, 850000000, 25000000), + RTCL_ROUND_SET(100000000, 400000000, 25000000), + RTCL_ROUND_SET(50000000, 200000000, 50000000) + } +}; + +static const int rtcl_divn3[] = { 2, 3, 4, 6 }; +static const int rtcl_xdiv[] = { 2, 4, 2 }; + +/* + * module data structures + */ + +#define RTCL_CLK_INFO(_idx, _name, _pname, _dname) \ + { \ + .idx = _idx, \ + .name = _name, \ + .parent_name = _pname, \ + .display_name = _dname, \ + } + +struct rtcl_clk_info { + unsigned int idx; + const char *name; + const char *parent_name; + const char *display_name; +}; + +struct rtcl_clk { + struct clk_hw hw; + unsigned int idx; + unsigned long min; + unsigned long max; + unsigned long rate; + unsigned long startup; +}; + +static const struct rtcl_clk_info rtcl_clk_info[CLK_COUNT] = { + RTCL_CLK_INFO(CLK_CPU, "cpu_clk", "ref_clk", "CPU"), + RTCL_CLK_INFO(CLK_MEM, "mem_clk", "ref_clk", "MEM"), + RTCL_CLK_INFO(CLK_LXB, "lxb_clk", "ref_clk", "LXB") +}; + +struct rtcl_dram { + int type; + int buswidth; +}; + +struct rtcl_sram { + int *pmark; + unsigned long vbase; +}; + +struct rtcl_ccu { + spinlock_t lock; + unsigned int soc; + struct rtcl_sram sram; + struct rtcl_dram dram; + struct device_node *np; + struct platform_device *pdev; + struct rtcl_clk clks[CLK_COUNT]; +}; + +struct rtcl_ccu *rtcl_ccu; + +#define rtcl_hw_to_clk(_hw) container_of(_hw, struct rtcl_clk, hw) + +/* + * SRAM relocatable assembler functions. The dram() parts point to normal kernel memory while + * the sram() parts are the same functions but relocated to SRAM. + */ + +extern void rtcl_838x_dram_start(void); +extern int rtcl_838x_dram_size; + +extern void (*rtcl_838x_dram_set_rate)(int clk_idx, int ctrl0, int ctrl1); +static void (*rtcl_838x_sram_set_rate)(int clk_idx, int ctrl0, int ctrl1); + +extern void rtcl_839x_dram_start(void); +extern int rtcl_839x_dram_size; + +extern void (*rtcl_839x_dram_set_rate)(int clk_idx, int ctrl0, int ctrl1); +static void (*rtcl_839x_sram_set_rate)(int clk_idx, int ctrl0, int ctrl1); + +/* + * clock setter/getter functions + */ + +static unsigned long rtcl_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct rtcl_clk *clk = rtcl_hw_to_clk(hw); + unsigned int ctrl0, ctrl1, div1, div2, cmu_ncode_in; + unsigned int cmu_sel_prediv, cmu_sel_div4, cmu_divn2, cmu_divn2_selb, cmu_divn3_sel; + + if ((clk->idx >= CLK_COUNT) || (!rtcl_ccu) || (rtcl_ccu->soc >= SOC_COUNT)) + return 0; + + ctrl0 = read_sw(rtcl_regs[rtcl_ccu->soc][REG_CTRL0][clk->idx]); + ctrl1 = read_sw(rtcl_regs[rtcl_ccu->soc][REG_CTRL1][clk->idx]); + + cmu_sel_prediv = 1 << RTL_PLL_CTRL0_CMU_SEL_PREDIV(ctrl0); + cmu_sel_div4 = RTL_PLL_CTRL0_CMU_SEL_DIV4(ctrl0) ? 4 : 1; + cmu_ncode_in = RTL_PLL_CTRL0_CMU_NCODE_IN(ctrl0) + 4; + cmu_divn2 = RTL_PLL_CTRL0_CMU_DIVN2(ctrl0) + 4; + + switch (rtcl_ccu->soc) { + case SOC_RTL838X: + if ((ctrl0 == 0) && (ctrl1 == 0) && (clk->idx == CLK_LXB)) + return 200000000; + + cmu_divn2_selb = RTL838X_PLL_CTRL1_CMU_DIVN2_SELB(ctrl1); + cmu_divn3_sel = rtcl_divn3[RTL838X_PLL_CTRL1_CMU_DIVN3_SEL(ctrl1)]; + break; + case SOC_RTL839X: + cmu_divn2_selb = RTL839X_PLL_CTRL1_CMU_DIVN2_SELB(ctrl1); + cmu_divn3_sel = rtcl_divn3[RTL839X_PLL_CTRL1_CMU_DIVN3_SEL(ctrl1)]; + break; + } + div1 = cmu_divn2_selb ? cmu_divn3_sel : cmu_divn2; + div2 = rtcl_xdiv[clk->idx]; + + return (((parent_rate / 16) * cmu_ncode_in) / (div1 * div2)) * + cmu_sel_prediv * cmu_sel_div4 * 16; +} + +static int rtcl_838x_set_rate(int clk_idx, const struct rtcl_reg_set *reg) +{ + unsigned long irqflags; +/* + * Runtime of this function (including locking) + * CPU: up to 14000 cycles / up to 56 us at 250 MHz (half default speed) + */ + spin_lock_irqsave(&rtcl_ccu->lock, irqflags); + rtcl_838x_sram_set_rate(clk_idx, reg->ctrl0, reg->ctrl1); + spin_unlock_irqrestore(&rtcl_ccu->lock, irqflags); + + return 0; +} + +static int rtcl_839x_set_rate(int clk_idx, const struct rtcl_reg_set *reg) +{ + unsigned long vpflags; + unsigned long irqflags; +/* + * Runtime of this function (including locking) + * CPU: up to 31000 cycles / up to 89 us at 350 MHz (half default speed) + */ + spin_lock_irqsave(&rtcl_ccu->lock, irqflags); + vpflags = dvpe(); + rtcl_839x_sram_set_rate(clk_idx, reg->ctrl0, reg->ctrl1); + evpe(vpflags); + spin_unlock_irqrestore(&rtcl_ccu->lock, irqflags); + + return 0; +} + +static int rtcl_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) +{ + int tab_idx; + struct rtcl_clk *clk = rtcl_hw_to_clk(hw); + const struct rtcl_rtab_set *rtab = &rtcl_rtab_set[rtcl_ccu->soc][clk->idx]; + const struct rtcl_round_set *round = &rtcl_round_set[rtcl_ccu->soc][clk->idx]; + + if ((parent_rate != OSC_RATE) || (!rtcl_ccu->sram.vbase)) + return -EINVAL; +/* + * Currently we do not know if SRAM is stable on these devices. Maybe someone changes memory in + * this region and does not care about proper allocation. So check if something might go wrong. + */ + if (unlikely(*rtcl_ccu->sram.pmark != RTL_SRAM_MARKER)) { + dev_err(&rtcl_ccu->pdev->dev, "SRAM code lost\n"); + return -EINVAL; + } + + tab_idx = (rate - round->min) / round->step; + if ((tab_idx < 0) || (tab_idx >= rtab->count) || (rtab->rset[tab_idx].rate != rate)) + return -EINVAL; + + rtcl_ccu->clks[clk->idx].rate = rate; + + switch (rtcl_ccu->soc) { + case SOC_RTL838X: + return rtcl_838x_set_rate(clk->idx, &rtab->rset[tab_idx]); + case SOC_RTL839X: + return rtcl_839x_set_rate(clk->idx, &rtab->rset[tab_idx]); + } + + return -ENXIO; +} + +static long rtcl_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) +{ + struct rtcl_clk *clk = rtcl_hw_to_clk(hw); + unsigned long rrate = max(clk->min, min(clk->max, rate)); + const struct rtcl_round_set *round = &rtcl_round_set[rtcl_ccu->soc][clk->idx]; + + rrate = ((rrate + (round->step >> 1)) / round->step) * round->step; + rrate -= (rrate > clk->max) ? round->step : 0; + rrate += (rrate < clk->min) ? round->step : 0; + + return rrate; +} + +/* + * Initialization functions to register the CCU and its clocks + */ + +#define RTCL_SRAM_FUNC(SOC, PBASE, FN) ({ \ + rtcl_##SOC##_sram_##FN = ((void *)&rtcl_##SOC##_dram_##FN \ + - (void *)&rtcl_##SOC##_dram_start) \ + + (void *)PBASE; }) + +static const struct clk_ops rtcl_clk_ops = { + .set_rate = rtcl_set_rate, + .round_rate = rtcl_round_rate, + .recalc_rate = rtcl_recalc_rate, +}; + +static int rtcl_ccu_create(struct device_node *np) +{ + int soc; + + if (of_device_is_compatible(np, "realtek,rtl8380-clock")) + soc = SOC_RTL838X; + else if (of_device_is_compatible(np, "realtek,rtl8390-clock")) + soc = SOC_RTL839X; + else + return -ENXIO; + + rtcl_ccu = kzalloc(sizeof(*rtcl_ccu), GFP_KERNEL); + if (IS_ERR(rtcl_ccu)) + return -ENOMEM; + + rtcl_ccu->np = np; + rtcl_ccu->soc = soc; + rtcl_ccu->dram.type = RTL_MC_MCR_DRAMTYPE(read_soc(RTL_MC_MCR)); + rtcl_ccu->dram.buswidth = RTL_MC_DCR_BUSWIDTH(read_soc(RTL_MC_DCR)); + spin_lock_init(&rtcl_ccu->lock); + + return 0; +} + +int rtcl_register_clkhw(int clk_idx) +{ + int ret; + struct clk *clk; + struct clk_init_data hw_init = { }; + struct rtcl_clk *rclk = &rtcl_ccu->clks[clk_idx]; + struct clk_parent_data parent_data = { .fw_name = rtcl_clk_info[clk_idx].parent_name }; + + rclk->idx = clk_idx; + rclk->hw.init = &hw_init; + + hw_init.num_parents = 1; + hw_init.ops = &rtcl_clk_ops; + hw_init.parent_data = &parent_data; + hw_init.name = rtcl_clk_info[clk_idx].name; + + ret = of_clk_hw_register(rtcl_ccu->np, &rclk->hw); + if (ret) + return ret; + + clk_hw_register_clkdev(&rclk->hw, rtcl_clk_info[clk_idx].name, NULL); + + clk = clk_get(NULL, rtcl_clk_info[clk_idx].name); + rclk->startup = clk_get_rate(clk); + clk_put(clk); + + switch (clk_idx) { + case CLK_CPU: + rclk->min = rtcl_round_set[rtcl_ccu->soc][clk_idx].min; + rclk->max = rtcl_round_set[rtcl_ccu->soc][clk_idx].max; + break; + default: +/* + * TODO: This driver supports PLL reclocking and nothing else. Additional required steps for non + * CPU PLLs are missing. E.g. if we want to change memory clocks the right way we must adapt a lot + * of other settings like MCR and DTRx timing registers (0xb80001000, 0xb8001008, ...) and initiate + * a DLL reset so that hardware operates in the allowed limits. This is far too complex without + * official support. Avoid this for now. + */ + rclk->min = rclk->max = rclk->startup; + break; + } + + return 0; +} + +static struct clk_hw *rtcl_get_clkhw(struct of_phandle_args *clkspec, void *prv) +{ + unsigned int idx = clkspec->args[0]; + + if (idx >= CLK_COUNT) { + pr_err("%s: Invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return &rtcl_ccu->clks[idx].hw; +} + +static int rtcl_ccu_register_clocks(void) +{ + int clk_idx, ret; + + for (clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) { + ret = rtcl_register_clkhw(clk_idx); + if (ret) { + pr_err("%s: Couldn't register %s clock\n", + __func__, rtcl_clk_info[clk_idx].display_name); + goto err_hw_unregister; + } + } + + ret = of_clk_add_hw_provider(rtcl_ccu->np, rtcl_get_clkhw, rtcl_ccu); + if (ret) { + pr_err("%s: Couldn't register clock provider of %s\n", + __func__, of_node_full_name(rtcl_ccu->np)); + goto err_hw_unregister; + } + + return 0; + +err_hw_unregister: + for (--clk_idx; clk_idx >= 0; --clk_idx) + clk_hw_unregister(&rtcl_ccu->clks[clk_idx].hw); + + return ret; +} + +int rtcl_init_sram(void) +{ + struct gen_pool *sram_pool; + phys_addr_t sram_pbase; + unsigned long sram_vbase; + struct device_node *node; + struct platform_device *pdev = NULL; + void *dram_start; + int dram_size; + const char *wrn = ", rate setting disabled.\n"; + + switch (rtcl_ccu->soc) { + case SOC_RTL838X: + dram_start = &rtcl_838x_dram_start; + dram_size = rtcl_838x_dram_size; + break; + case SOC_RTL839X: + dram_start = &rtcl_839x_dram_start; + dram_size = rtcl_839x_dram_size; + break; + default: + return -ENXIO; + } + + for_each_compatible_node(node, NULL, "mmio-sram") { + pdev = of_find_device_by_node(node); + if (pdev) { + of_node_put(node); + break; + } + } + + if (!pdev) { + dev_warn(&rtcl_ccu->pdev->dev, "no SRAM device found%s", wrn); + return -ENXIO; + } + + sram_pool = gen_pool_get(&pdev->dev, NULL); + if (!sram_pool) { + dev_warn(&rtcl_ccu->pdev->dev, "SRAM pool unavailable%s", wrn); + goto err_put_device; + } + + sram_vbase = gen_pool_alloc(sram_pool, dram_size); + if (!sram_vbase) { + dev_warn(&rtcl_ccu->pdev->dev, "can not allocate SRAM%s", wrn); + goto err_put_device; + } + + sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_vbase); + memcpy((void *)sram_pbase, dram_start, dram_size); + flush_icache_range((unsigned long)sram_pbase, (unsigned long)(sram_pbase + dram_size)); + + switch (rtcl_ccu->soc) { + case SOC_RTL838X: + RTCL_SRAM_FUNC(838x, sram_pbase, set_rate); + break; + case SOC_RTL839X: + RTCL_SRAM_FUNC(839x, sram_pbase, set_rate); + break; + } + + rtcl_ccu->sram.pmark = (int *)((void *)sram_pbase + (dram_size - 4)); + rtcl_ccu->sram.vbase = sram_vbase; + + return 0; + +err_put_device: + put_device(&pdev->dev); + + return -ENXIO; +} + +void rtcl_ccu_log_early(void) +{ + int clk_idx; + char meminfo[80], clkinfo[255], msg[255] = "rtl83xx-clk: initialized"; + + sprintf(meminfo, " (%d Bit DDR%d)", rtcl_ccu->dram.buswidth, rtcl_ccu->dram.type); + for (clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) { + sprintf(clkinfo, ", %s %lu MHz", rtcl_clk_info[clk_idx].display_name, + rtcl_ccu->clks[clk_idx].startup / 1000000); + if (clk_idx == CLK_MEM) + strcat(clkinfo, meminfo); + strcat(msg, clkinfo); + } + pr_info("%s\n", msg); +} + +void rtcl_ccu_log_late(void) +{ + int clk_idx; + struct rtcl_clk *rclk; + bool overclock = false; + char clkinfo[80], msg[255] = "rate setting enabled"; + + for (clk_idx = 0; clk_idx < CLK_COUNT; clk_idx++) { + rclk = &rtcl_ccu->clks[clk_idx]; + overclock |= rclk->max > rclk->startup; + sprintf(clkinfo, ", %s %lu-%lu MHz", rtcl_clk_info[clk_idx].display_name, + rclk->min / 1000000, rclk->max / 1000000); + strcat(msg, clkinfo); + } + if (overclock) + strcat(msg, ", OVERCLOCK AT OWN RISK"); + + dev_info(&rtcl_ccu->pdev->dev, "%s\n", msg); +} + +/* + * Early registration: This module provides core startup clocks that are needed for generic SOC + * init and for further builtin devices (e.g. UART). Register asap via clock framework. + */ + +static void __init rtcl_probe_early(struct device_node *np) +{ + if (rtcl_ccu_create(np)) + return; + + if (rtcl_ccu_register_clocks()) + kfree(rtcl_ccu); + else + rtcl_ccu_log_early(); +} + +CLK_OF_DECLARE_DRIVER(rtl838x_clk, "realtek,rtl8380-clock", rtcl_probe_early); +CLK_OF_DECLARE_DRIVER(rtl839x_clk, "realtek,rtl8390-clock", rtcl_probe_early); + +/* + * Late registration: Finally register as normal platform driver. At this point we can make use + * of other modules like SRAM. + */ + +static const struct of_device_id rtcl_dt_ids[] = { + { .compatible = "realtek,rtl8380-clock" }, + { .compatible = "realtek,rtl8390-clock" }, + {} +}; + +static int rtcl_probe_late(struct platform_device *pdev) +{ + int ret; + + if (!rtcl_ccu) { + dev_err(&pdev->dev, "early initialization not run"); + return -ENXIO; + } + rtcl_ccu->pdev = pdev; + ret = rtcl_init_sram(); + if (ret) + return ret; + + rtcl_ccu_log_late(); + + return 0; +} + +static struct platform_driver rtcl_platform_driver = { + .driver = { + .name = "rtl83xx-clk", + .of_match_table = rtcl_dt_ids, + }, + .probe = rtcl_probe_late, +}; + +static int __init rtcl_init_subsys(void) +{ + return platform_driver_register(&rtcl_platform_driver); +} + +/* + * The driver does not know when SRAM module has finally loaded. With an arch_initcall() we might + * overtake SRAM initialization. Be polite and give the system a little more time. + */ + +subsys_initcall(rtcl_init_subsys); diff --git a/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.h b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.h new file mode 100644 index 00000000000..a69b16b475e --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/clk/realtek/clk-rtl83xx.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Realtek RTL83XX clock headers + * Copyright (C) 2022 Markus Stockhausen + */ + +/* + * Switch registers (e.g. PLL) + */ + +#define RTL_SW_CORE_BASE (0xbb000000) + +#define RTL838X_PLL_GLB_CTRL (0x0fc0) +#define RTL838X_PLL_CPU_CTRL0 (0x0fc4) +#define RTL838X_PLL_CPU_CTRL1 (0x0fc8) +#define RTL838X_PLL_LXB_CTRL0 (0x0fd0) +#define RTL838X_PLL_LXB_CTRL1 (0x0fd4) +#define RTL838X_PLL_MEM_CTRL0 (0x0fdc) +#define RTL838X_PLL_MEM_CTRL1 (0x0fe0) + +#define RTL839X_PLL_GLB_CTRL (0x0024) +#define RTL839X_PLL_CPU_CTRL0 (0x0028) +#define RTL839X_PLL_CPU_CTRL1 (0x002c) +#define RTL839X_PLL_LXB_CTRL0 (0x0038) +#define RTL839X_PLL_LXB_CTRL1 (0x003c) +#define RTL839X_PLL_MEM_CTRL0 (0x0048) +#define RTL839X_PLL_MEM_CTRL1 (0x004c) + +#define RTL_PLL_CTRL0_CMU_SEL_PREDIV(v) (((v) >> 0) & 0x3) +#define RTL_PLL_CTRL0_CMU_SEL_DIV4(v) (((v) >> 2) & 0x1) +#define RTL_PLL_CTRL0_CMU_NCODE_IN(v) (((v) >> 4) & 0xff) +#define RTL_PLL_CTRL0_CMU_DIVN2(v) (((v) >> 12) & 0xff) + +#define RTL838X_GLB_CTRL_EN_CPU_PLL_MASK (1 << 0) +#define RTL838X_GLB_CTRL_EN_LXB_PLL_MASK (1 << 1) +#define RTL838X_GLB_CTRL_EN_MEM_PLL_MASK (1 << 2) +#define RTL838X_GLB_CTRL_CPU_PLL_READY_MASK (1 << 8) +#define RTL838X_GLB_CTRL_LXB_PLL_READY_MASK (1 << 9) +#define RTL838X_GLB_CTRL_MEM_PLL_READY_MASK (1 << 10) +#define RTL838X_GLB_CTRL_CPU_PLL_SC_MUX_MASK (1 << 12) + +#define RTL838X_PLL_CTRL1_CMU_DIVN2_SELB(v) (((v) >> 26) & 0x1) +#define RTL838X_PLL_CTRL1_CMU_DIVN3_SEL(v) (((v) >> 27) & 0x3) + +#define RTL839X_GLB_CTRL_CPU_CLKSEL_MASK (1 << 11) +#define RTL839X_GLB_CTRL_MEM_CLKSEL_MASK (1 << 12) +#define RTL839X_GLB_CTRL_LXB_CLKSEL_MASK (1 << 13) + +#define RTL839X_PLL_CTRL1_CMU_DIVN2_SELB(v) (((v) >> 2) & 0x1) +#define RTL839X_PLL_CTRL1_CMU_DIVN3_SEL(v) (((v) >> 0) & 0x3) + +/* + * Core registers (e.g. memory controller) + */ + +#define RTL_SOC_BASE (0xB8000000) + +#define RTL_MC_MCR (0x1000) +#define RTL_MC_DCR (0x1004) +#define RTL_MC_DTR0 (0x1008) +#define RTL_MC_DTR1 (0x100c) +#define RTL_MC_DTR2 (0x1010) +#define RTL_MC_DMCR (0x101c) +#define RTL_MC_DACCR (0x1500) +#define RTL_MC_DCDR (0x1060) + +#define RTL_MC_MCR_DRAMTYPE(v) ((((v) >> 28) & 0xf) + 1) +#define RTL_MC_DCR_BUSWIDTH(v) (8 << (((v) >> 24) & 0xf)) + +/* + * Other stuff + */ + +#define RTL_SRAM_MARKER (0x5eaf00d5) +#define RTL_SRAM_BASE (0x9f000000) diff --git a/target/linux/realtek/files-5.15/drivers/clocksource/timer-rtl-otto.c b/target/linux/realtek/files-5.15/drivers/clocksource/timer-rtl-otto.c new file mode 100644 index 00000000000..14e28e50f40 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/clocksource/timer-rtl-otto.c @@ -0,0 +1,299 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include + +#include "timer-of.h" + +#define RTTM_DATA 0x0 +#define RTTM_CNT 0x4 +#define RTTM_CTRL 0x8 +#define RTTM_INT 0xc + +#define RTTM_CTRL_ENABLE BIT(28) +#define RTTM_INT_PENDING BIT(16) +#define RTTM_INT_ENABLE BIT(20) + +/* + * The Otto platform provides multiple 28 bit timers/counters with the following + * operating logic. If enabled the timer counts up. Per timer one can set a + * maximum counter value as an end marker. If end marker is reached the timer + * fires an interrupt. If the timer "overflows" by reaching the end marker or + * by adding 1 to 0x0fffffff the counter is reset to 0. When this happens and + * the timer is in operating mode COUNTER it stops. In mode TIMER it will + * continue to count up. + */ + +#define RTTM_CTRL_COUNTER 0 +#define RTTM_CTRL_TIMER BIT(24) + +#define RTTM_BIT_COUNT 28 +#define RTTM_MIN_DELTA 8 +#define RTTM_MAX_DELTA CLOCKSOURCE_MASK(28) + +/* + * Timers are derived from the LXB clock frequency. Usually this is a fixed + * multiple of the 25 MHz oscillator. The 930X SOC is an exception from that. + * Its LXB clock has only dividers and uses the switch PLL of 2.45 GHz as its + * base. The only meaningful frequencies we can achieve from that are 175.000 + * MHz and 153.125 MHz. The greatest common divisor of all explained possible + * speeds is 3125000. Pin the timers to this 3.125 MHz reference frequency. + */ + +#define RTTM_TICKS_PER_SEC 3125000 + +struct rttm_cs { + struct timer_of to; + struct clocksource cs; +}; + +/* + * Simple internal register functions + */ + +static inline void rttm_set_counter(void __iomem *base, unsigned int counter) +{ + iowrite32(counter, base + RTTM_CNT); +} + +static inline unsigned int rttm_get_counter(void __iomem *base) +{ + return ioread32(base + RTTM_CNT); +} + +static inline void rttm_set_period(void __iomem *base, unsigned int period) +{ + iowrite32(period, base + RTTM_DATA); +} + +static inline void rttm_disable_timer(void __iomem *base) +{ + iowrite32(0, base + RTTM_CTRL); +} + +static inline void rttm_enable_timer(void __iomem *base, u32 mode, u32 divisor) +{ + iowrite32(RTTM_CTRL_ENABLE | mode | divisor, base + RTTM_CTRL); +} + +static inline void rttm_ack_irq(void __iomem *base) +{ + iowrite32(ioread32(base + RTTM_INT) | RTTM_INT_PENDING, base + RTTM_INT); +} + +static inline void rttm_enable_irq(void __iomem *base) +{ + iowrite32(RTTM_INT_ENABLE, base + RTTM_INT); +} + +static inline void rttm_disable_irq(void __iomem *base) +{ + iowrite32(0, base + RTTM_INT); +} + +/* + * Aggregated control functions for kernel clock framework + */ + +#define RTTM_DEBUG(base) \ + pr_debug("------------- %s %d %08x\n", __func__, \ + smp_processor_id(), (u32)base) + +static irqreturn_t rttm_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *clkevt = dev_id; + struct timer_of *to = to_timer_of(clkevt); + + rttm_ack_irq(to->of_base.base); + RTTM_DEBUG(to->of_base.base); + clkevt->event_handler(clkevt); + + return IRQ_HANDLED; +} + +static void rttm_stop_timer(void __iomem *base) +{ + rttm_disable_timer(base); + rttm_ack_irq(base); +} + +static void rttm_start_timer(struct timer_of *to, u32 mode) +{ + rttm_set_counter(to->of_base.base, 0); + rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC); +} + +static int rttm_next_event(unsigned long delta, struct clock_event_device *clkevt) +{ + struct timer_of *to = to_timer_of(clkevt); + + RTTM_DEBUG(to->of_base.base); + rttm_stop_timer(to->of_base.base); + rttm_set_period(to->of_base.base, delta); + rttm_start_timer(to, RTTM_CTRL_COUNTER); + + return 0; +} + +static int rttm_state_oneshot(struct clock_event_device *clkevt) +{ + struct timer_of *to = to_timer_of(clkevt); + + RTTM_DEBUG(to->of_base.base); + rttm_stop_timer(to->of_base.base); + rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); + rttm_start_timer(to, RTTM_CTRL_COUNTER); + + return 0; +} + +static int rttm_state_periodic(struct clock_event_device *clkevt) +{ + struct timer_of *to = to_timer_of(clkevt); + + RTTM_DEBUG(to->of_base.base); + rttm_stop_timer(to->of_base.base); + rttm_set_period(to->of_base.base, RTTM_TICKS_PER_SEC / HZ); + rttm_start_timer(to, RTTM_CTRL_TIMER); + + return 0; +} + +static int rttm_state_shutdown(struct clock_event_device *clkevt) +{ + struct timer_of *to = to_timer_of(clkevt); + + RTTM_DEBUG(to->of_base.base); + rttm_stop_timer(to->of_base.base); + + return 0; +} + +static void rttm_setup_timer(void __iomem *base) +{ + RTTM_DEBUG(base); + rttm_stop_timer(base); + rttm_set_period(base, 0); +} + +static u64 rttm_read_clocksource(struct clocksource *cs) +{ + struct rttm_cs *rcs = container_of(cs, struct rttm_cs, cs); + + return (u64)rttm_get_counter(rcs->to.of_base.base); +} + +/* + * Module initialization part. + */ + +static DEFINE_PER_CPU(struct timer_of, rttm_to) = { + .flags = TIMER_OF_BASE | TIMER_OF_CLOCK | TIMER_OF_IRQ, + .of_irq = { + .flags = IRQF_PERCPU | IRQF_TIMER, + .handler = rttm_timer_interrupt, + }, + .clkevt = { + .rating = 400, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_state_periodic = rttm_state_periodic, + .set_state_shutdown = rttm_state_shutdown, + .set_state_oneshot = rttm_state_oneshot, + .set_next_event = rttm_next_event + }, +}; + +static int rttm_enable_clocksource(struct clocksource *cs) +{ + struct rttm_cs *rcs = container_of(cs, struct rttm_cs, cs); + + rttm_disable_irq(rcs->to.of_base.base); + rttm_setup_timer(rcs->to.of_base.base); + rttm_enable_timer(rcs->to.of_base.base, RTTM_CTRL_TIMER, + rcs->to.of_clk.rate / RTTM_TICKS_PER_SEC); + + return 0; +} + +struct rttm_cs rttm_cs = { + .to = { + .flags = TIMER_OF_BASE | TIMER_OF_CLOCK, + }, + .cs = { + .name = "realtek_otto_timer", + .rating = 400, + .mask = CLOCKSOURCE_MASK(RTTM_BIT_COUNT), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .read = rttm_read_clocksource, + .enable = rttm_enable_clocksource + } +}; + +static u64 notrace rttm_read_clock(void) +{ + return (u64)rttm_get_counter(rttm_cs.to.of_base.base); +} + +static int rttm_cpu_starting(unsigned int cpu) +{ + struct timer_of *to = per_cpu_ptr(&rttm_to, cpu); + + RTTM_DEBUG(to->of_base.base); + to->clkevt.cpumask = cpumask_of(cpu); + irq_set_affinity(to->of_irq.irq, to->clkevt.cpumask); + clockevents_config_and_register(&to->clkevt, RTTM_TICKS_PER_SEC, + RTTM_MIN_DELTA, RTTM_MAX_DELTA); + rttm_enable_irq(to->of_base.base); + + return 0; +} + +static int __init rttm_probe(struct device_node *np) +{ + int cpu, cpu_rollback; + struct timer_of *to; + int clkidx = num_possible_cpus(); +/* + * Use the first n timers as per CPU clock event generators + */ + for_each_possible_cpu(cpu) { + to = per_cpu_ptr(&rttm_to, cpu); + to->of_irq.index = to->of_base.index = cpu; + if (timer_of_init(np, to)) { + pr_err("%s: setup of timer %d failed\n", __func__, cpu); + goto rollback; + } + rttm_setup_timer(to->of_base.base); + } +/* + * Activate the n'th+1 timer as a stable CPU clocksource. + */ + to = &rttm_cs.to; + to->of_base.index = clkidx; + timer_of_init(np, to); + if (rttm_cs.to.of_base.base && rttm_cs.to.of_clk.rate) { + clocksource_register_hz(&rttm_cs.cs, RTTM_TICKS_PER_SEC); + sched_clock_register(rttm_read_clock, RTTM_BIT_COUNT, RTTM_TICKS_PER_SEC); + } else + pr_err("%s: setup of timer %d as clocksoure failed", __func__, clkidx); + + return cpuhp_setup_state(CPUHP_AP_REALTEK_TIMER_STARTING, + "timer/realtek:online", + rttm_cpu_starting, NULL); +rollback: + pr_err("%s: timer registration failed\n", __func__); + for_each_possible_cpu(cpu_rollback) { + if (cpu_rollback == cpu) + break; + to = per_cpu_ptr(&rttm_to, cpu_rollback); + timer_of_cleanup(to); + } + + return -EINVAL; +} + +TIMER_OF_DECLARE(otto_timer, "realtek,otto-timer", rttm_probe); diff --git a/target/linux/realtek/files-5.15/drivers/gpio/gpio-rtl8231.c b/target/linux/realtek/files-5.15/drivers/gpio/gpio-rtl8231.c new file mode 100644 index 00000000000..368c4fa60fb --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/gpio/gpio-rtl8231.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +/* RTL8231 registers for LED control */ +#define RTL8231_LED_FUNC0 0x0000 +#define RTL8231_LED_FUNC1 0x0001 +#define RTL8231_READY_MASK 0x03f0 +#define RTL8231_READY_VALUE 0x0370 +#define RTL8231_GPIO_PIN_SEL(gpio) ((0x0002) + ((gpio) >> 4)) +#define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4)) +#define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4)) + +#define USEC_TIMEOUT 5000 + +#define RTL8231_SMI_BUS_ID_MAX 0x1F + +struct rtl8231_gpios { + struct gpio_chip gc; + struct device *dev; + u32 id; + u32 smi_bus_id; + u16 reg_shadow[0x20]; + u32 reg_cached; + int ext_gpio_indrt_access; +}; + +extern struct rtl83xx_soc_info soc_info; + +DEFINE_MUTEX(miim_lock); + +static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg) +{ + u32 t = 0, n = 0; + + reg &= 0x1f; + + /* Calculate read register address */ + t = (gpios->smi_bus_id << 2) | (reg << 7); + + /* Set execution bit: cleared when operation completed */ + t |= 1; + + // Start execution + sw_w32(t, gpios->ext_gpio_indrt_access); + do { + udelay(1); + t = sw_r32(gpios->ext_gpio_indrt_access); + n++; + } while ((t & 1) && (n < USEC_TIMEOUT)); + + if (n >= USEC_TIMEOUT) + return 0x80000000; + + pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id, + reg, (t & 0xffff0000) >> 16); + + return (t & 0xffff0000) >> 16; +} + +static int rtl8231_write(struct rtl8231_gpios *gpios, u32 reg, u32 data) +{ + u32 t = 0, n = 0; + + pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id, reg, data); + reg &= 0x1f; + + t = (gpios->smi_bus_id << 2) | (reg << 7) | (data << 16); + /* Set write bit */ + t |= 2; + + /* Set execution bit: cleared when operation completed */ + t |= 1; + + // Start execution + sw_w32(t, gpios->ext_gpio_indrt_access); + do { + udelay(1); + t = sw_r32(gpios->ext_gpio_indrt_access); + } while ((t & 1) && (n < USEC_TIMEOUT)); + + if (n >= USEC_TIMEOUT) + return -1; + + return 0; +} + +static u32 rtl8231_read_cached(struct rtl8231_gpios *gpios, u32 reg) +{ + if (reg > 0x1f) + return 0; + + if (gpios->reg_cached & (1 << reg)) + return gpios->reg_shadow[reg]; + + return rtl8231_read(gpios, reg); +} + +/* Set Direction of the RTL8231 pin: + * dir 1: input + * dir 0: output + */ +static int rtl8231_pin_dir(struct rtl8231_gpios *gpios, u32 gpio, u32 dir) +{ + u32 v; + int pin_sel_addr = RTL8231_GPIO_PIN_SEL(gpio); + int pin_dir_addr = RTL8231_GPIO_DIR(gpio); + int dpin = gpio % 16; + + if (gpio > 31) { + pr_debug("WARNING: HIGH pin\n"); + dpin += 5; + pin_dir_addr = pin_sel_addr; + } + + v = rtl8231_read_cached(gpios, pin_dir_addr); + if (v & 0x80000000) { + pr_err("Error reading RTL8231\n"); + return -1; + } + + v = (v & ~(1 << dpin)) | (dir << dpin); + rtl8231_write(gpios, pin_dir_addr, v); + gpios->reg_shadow[pin_dir_addr] = v; + gpios->reg_cached |= 1 << pin_dir_addr; + return 0; +} + +static int rtl8231_pin_dir_get(struct rtl8231_gpios *gpios, u32 gpio, u32 *dir) +{ + /* dir 1: input + * dir 0: output + */ + + u32 v; + int pin_dir_addr = RTL8231_GPIO_DIR(gpio); + int pin = gpio % 16; + + if (gpio > 31) { + pin_dir_addr = RTL8231_GPIO_PIN_SEL(gpio); + pin += 5; + } + + v = rtl8231_read(gpios, pin_dir_addr); + if (v & (1 << pin)) + *dir = 1; + else + *dir = 0; + return 0; +} + +static int rtl8231_pin_set(struct rtl8231_gpios *gpios, u32 gpio, u32 data) +{ + u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio)); + + pr_debug("%s: %d to %d\n", __func__, gpio, data); + if (v & 0x80000000) { + pr_err("Error reading RTL8231\n"); + return -1; + } + v = (v & ~(1 << (gpio % 16))) | (data << (gpio % 16)); + rtl8231_write(gpios, RTL8231_GPIO_DATA(gpio), v); + gpios->reg_shadow[RTL8231_GPIO_DATA(gpio)] = v; + gpios->reg_cached |= 1 << RTL8231_GPIO_DATA(gpio); + return 0; +} + +static int rtl8231_pin_get(struct rtl8231_gpios *gpios, u32 gpio, u16 *state) +{ + u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio)); + + if (v & 0x80000000) { + pr_err("Error reading RTL8231\n"); + return -1; + } + + *state = v & 0xffff; + return 0; +} + +static int rtl8231_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + int err; + struct rtl8231_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("%s: %d\n", __func__, offset); + mutex_lock(&miim_lock); + err = rtl8231_pin_dir(gpios, offset, 1); + mutex_unlock(&miim_lock); + return err; +} + +static int rtl8231_direction_output(struct gpio_chip *gc, unsigned int offset, int value) +{ + int err; + struct rtl8231_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("%s: %d\n", __func__, offset); + mutex_lock(&miim_lock); + err = rtl8231_pin_dir(gpios, offset, 0); + mutex_unlock(&miim_lock); + if (!err) + err = rtl8231_pin_set(gpios, offset, value); + return err; +} + +static int rtl8231_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + u32 v = 0; + struct rtl8231_gpios *gpios = gpiochip_get_data(gc); + + pr_debug("%s: %d\n", __func__, offset); + mutex_lock(&miim_lock); + rtl8231_pin_dir_get(gpios, offset, &v); + mutex_unlock(&miim_lock); + return v; +} + +static int rtl8231_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + u16 state = 0; + struct rtl8231_gpios *gpios = gpiochip_get_data(gc); + + mutex_lock(&miim_lock); + rtl8231_pin_get(gpios, offset, &state); + mutex_unlock(&miim_lock); + if (state & (1 << (offset % 16))) + return 1; + return 0; +} + +void rtl8231_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct rtl8231_gpios *gpios = gpiochip_get_data(gc); + + rtl8231_pin_set(gpios, offset, value); +} + +int rtl8231_init(struct rtl8231_gpios *gpios) +{ + u32 ret; + + pr_info("%s called, MDIO bus ID: %d\n", __func__, gpios->smi_bus_id); + + gpios->reg_cached = 0; + + if (soc_info.family == RTL8390_FAMILY_ID) { + // RTL8390: Enable external gpio in global led control register + sw_w32_mask(0x7 << 18, 0x4 << 18, RTL839X_LED_GLB_CTRL); + } else if (soc_info.family == RTL8380_FAMILY_ID) { + // RTL8380: Enable RTL8231 indirect access mode + sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL); + sw_w32_mask(3, 1, RTL838X_DMY_REG5); + } + + ret = rtl8231_read(gpios, RTL8231_LED_FUNC1); + if ((ret & 0x80000000) || ((ret & RTL8231_READY_MASK) != RTL8231_READY_VALUE)) + return -ENXIO; + + /* Select GPIO functionality and force input direction for pins 0-36 */ + rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(0), 0xffff); + rtl8231_write(gpios, RTL8231_GPIO_DIR(0), 0xffff); + rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(16), 0xffff); + rtl8231_write(gpios, RTL8231_GPIO_DIR(16), 0xffff); + rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(32), 0x03ff); + + /* Set LED_Start to enable drivers for output mode */ + rtl8231_write(gpios, RTL8231_LED_FUNC0, 1 << 1); + + return 0; +} + +static const struct of_device_id rtl8231_gpio_of_match[] = { + { .compatible = "realtek,rtl8231-gpio" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, rtl8231_gpio_of_match); + +static int rtl8231_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct rtl8231_gpios *gpios; + int err; + + pr_info("Probing RTL8231 GPIOs\n"); + + if (!np) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); + if (!gpios) + return -ENOMEM; + + gpios->id = soc_info.id; + if (soc_info.family == RTL8380_FAMILY_ID) { + gpios->ext_gpio_indrt_access = RTL838X_EXT_GPIO_INDRT_ACCESS; + } + + if (soc_info.family == RTL8390_FAMILY_ID) { + gpios->ext_gpio_indrt_access = RTL839X_EXT_GPIO_INDRT_ACCESS; + } + + err = of_property_read_u32(np, "indirect-access-bus-id", &gpios->smi_bus_id); + if (!err && gpios->smi_bus_id > RTL8231_SMI_BUS_ID_MAX) + err = -EINVAL; + + if (err) { + dev_err(dev, "invalid or missing indirect-access-bus-id\n"); + return err; + } + + err = rtl8231_init(gpios); + if (err) { + dev_err(dev, "no device found at bus address %d\n", gpios->smi_bus_id); + return err; + } + + gpios->dev = dev; + gpios->gc.base = -1; + gpios->gc.ngpio = 37; + gpios->gc.label = "rtl8231"; + gpios->gc.parent = dev; + gpios->gc.owner = THIS_MODULE; + gpios->gc.can_sleep = true; + + gpios->gc.direction_input = rtl8231_direction_input; + gpios->gc.direction_output = rtl8231_direction_output; + gpios->gc.set = rtl8231_gpio_set; + gpios->gc.get = rtl8231_gpio_get; + gpios->gc.get_direction = rtl8231_get_direction; + + err = devm_gpiochip_add_data(dev, &gpios->gc, gpios); + return err; +} + +static struct platform_driver rtl8231_gpio_driver = { + .driver = { + .name = "rtl8231-gpio", + .of_match_table = rtl8231_gpio_of_match, + }, + .probe = rtl8231_gpio_probe, +}; + +module_platform_driver(rtl8231_gpio_driver); + +MODULE_DESCRIPTION("Realtek RTL8231 GPIO expansion chip support"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.c b/target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.c new file mode 100644 index 00000000000..382dfd40109 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "i2c-rtl9300.h" + +#define REG(i, x) (i->base + x + (i->scl_num ? i->mst2_offset : 0)) +#define REG_MASK(i, clear, set, reg) \ + writel((readl(REG(i, reg)) & ~(clear)) | (set), REG(i, reg)) + +struct i2c_drv_data { + int scl0_pin; + int scl1_pin; + int sda0_pin; + struct i2c_algorithm *algo; + int (*read)(struct rtl9300_i2c *i2c, u8 *buf, int len); + int (*write)(struct rtl9300_i2c *i2c, u8 *buf, int len); + void (*reg_addr_set)(struct rtl9300_i2c *i2c, u32 reg, u16 len); + int (*config_xfer)(struct rtl9300_i2c *i2c, u16 addr, u16 len); + int (*execute_xfer)(struct rtl9300_i2c *i2c, char read_write, int size, + union i2c_smbus_data * data, int len); + void (*writel)(struct rtl9300_i2c *i2c, u32 data); + void (*config_io)(struct rtl9300_i2c *i2c, int scl_num, int sda_num); + u32 mst2_offset; +}; + +DEFINE_MUTEX(i2c_lock); + +static void rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len) +{ + // Set register address width + REG_MASK(i2c, 0x3 << RTL9300_I2C_CTRL2_MADDR_WIDTH, len << RTL9300_I2C_CTRL2_MADDR_WIDTH, + RTL9300_I2C_CTRL2); + + // Set register address + REG_MASK(i2c, 0xffffff << RTL9300_I2C_CTRL1_MEM_ADDR, reg << RTL9300_I2C_CTRL1_MEM_ADDR, + RTL9300_I2C_CTRL1); +} + +static void rtl9310_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len) +{ + // Set register address width + REG_MASK(i2c, 0x3 << RTL9310_I2C_CTRL_MADDR_WIDTH, len << RTL9310_I2C_CTRL_MADDR_WIDTH, + RTL9310_I2C_CTRL); + + // Set register address + writel(reg, REG(i2c, RTL9310_I2C_MEMADDR)); +} + +static void rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num) +{ + u32 v; + + // Set SCL pin + REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_GPIO8_SCL_SEL), RTL9300_I2C_CTRL1); + + // Set SDA pin + REG_MASK(i2c, 0x7 << RTL9300_I2C_CTRL1_SDA_OUT_SEL, + i2c->sda_num << RTL9300_I2C_CTRL1_SDA_OUT_SEL, RTL9300_I2C_CTRL1); + + // Set SDA pin to I2C functionality + v = readl(i2c->base + RTL9300_I2C_MST_GLB_CTRL); + v |= BIT(i2c->sda_num); + writel(v, i2c->base + RTL9300_I2C_MST_GLB_CTRL); +} + +static void rtl9310_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num) +{ + u32 v; + + // Set SCL pin + REG_MASK(i2c, 0, BIT(RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL + scl_num), RTL9310_I2C_MST_IF_SEL); + + // Set SDA pin + REG_MASK(i2c, 0x7 << RTL9310_I2C_CTRL_SDA_OUT_SEL, + i2c->sda_num << RTL9310_I2C_CTRL_SDA_OUT_SEL, RTL9310_I2C_CTRL); + + // Set SDA pin to I2C functionality + v = readl(i2c->base + RTL9310_I2C_MST_IF_SEL); + v |= BIT(i2c->sda_num); + writel(v, i2c->base + RTL9310_I2C_MST_IF_SEL); +} + +static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len) +{ + // Set bus frequency + REG_MASK(i2c, 0x3 << RTL9300_I2C_CTRL2_SCL_FREQ, + i2c->bus_freq << RTL9300_I2C_CTRL2_SCL_FREQ, RTL9300_I2C_CTRL2); + + // Set slave device address + REG_MASK(i2c, 0x7f << RTL9300_I2C_CTRL2_DEV_ADDR, + addr << RTL9300_I2C_CTRL2_DEV_ADDR, RTL9300_I2C_CTRL2); + + // Set data length + REG_MASK(i2c, 0xf << RTL9300_I2C_CTRL2_DATA_WIDTH, + ((len - 1) & 0xf) << RTL9300_I2C_CTRL2_DATA_WIDTH, RTL9300_I2C_CTRL2); + + // Set read mode to random + REG_MASK(i2c, 0x1 << RTL9300_I2C_CTRL2_READ_MODE, 0, RTL9300_I2C_CTRL2); + + return 0; +} + +static int rtl9310_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len) +{ + // Set bus frequency + REG_MASK(i2c, 0x3 << RTL9310_I2C_CTRL_SCL_FREQ, + i2c->bus_freq << RTL9310_I2C_CTRL_SCL_FREQ, RTL9310_I2C_CTRL); + + // Set slave device address + REG_MASK(i2c, 0x7f << RTL9310_I2C_CTRL_DEV_ADDR, + addr << RTL9310_I2C_CTRL_DEV_ADDR, RTL9310_I2C_CTRL); + + // Set data length + REG_MASK(i2c, 0xf << RTL9310_I2C_CTRL_DATA_WIDTH, + ((len - 1) & 0xf) << RTL9310_I2C_CTRL_DATA_WIDTH, RTL9310_I2C_CTRL); + + // Set read mode to random + REG_MASK(i2c, 0x1 << RTL9310_I2C_CTRL_READ_MODE, 0, RTL9310_I2C_CTRL); + + return 0; +} + +static int i2c_read(void __iomem *r0, u8 *buf, int len) +{ + int i; + u32 v; + + if (len > 16) + return -EIO; + + for (i = 0; i < len; i++) { + if (i % 4 == 0) + v = readl(r0 + i); + buf[i] = v; + v >>= 8; + } + + return len; +} + +static int i2c_write(void __iomem *r0, u8 *buf, int len) +{ + u32 v; + int i; + + if (len > 16) + return -EIO; + + for (i = 0; i < len; i++) { + if (! (i % 4)) + v = 0; + v <<= 8; + v |= buf[i]; + if (i % 4 == 3 || i == len - 1) + writel(v, r0 + (i / 4) * 4); + } + + return len; +} + +static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len) +{ + return i2c_read(REG(i2c, RTL9300_I2C_DATA_WORD0), buf, len); +} + +static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len) +{ + return i2c_write(REG(i2c, RTL9300_I2C_DATA_WORD0), buf, len); +} + +static int rtl9310_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len) +{ + return i2c_read(REG(i2c, RTL9310_I2C_DATA), buf, len); +} + +static int rtl9310_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len) +{ + return i2c_write(REG(i2c, RTL9310_I2C_DATA), buf, len); +} + +static void rtl9300_writel(struct rtl9300_i2c *i2c, u32 data) +{ + writel(data, REG(i2c, RTL9300_I2C_DATA_WORD0)); +} + +static void rtl9310_writel(struct rtl9300_i2c *i2c, u32 data) +{ + writel(data, REG(i2c, RTL9310_I2C_DATA)); +} + + +static int rtl9300_execute_xfer(struct rtl9300_i2c *i2c, char read_write, + int size, union i2c_smbus_data * data, int len) +{ + u32 v; + + if (read_write == I2C_SMBUS_READ) + REG_MASK(i2c, BIT(RTL9300_I2C_CTRL1_RWOP), 0, RTL9300_I2C_CTRL1); + else + REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_RWOP), RTL9300_I2C_CTRL1); + + REG_MASK(i2c, 0, BIT(RTL9300_I2C_CTRL1_I2C_TRIG), RTL9300_I2C_CTRL1); + do { + v = readl(REG(i2c, RTL9300_I2C_CTRL1)); + } while (v & BIT(RTL9300_I2C_CTRL1_I2C_TRIG)); + + if (v & BIT(RTL9300_I2C_CTRL1_I2C_FAIL)) + return -EIO; + + if (read_write == I2C_SMBUS_READ) { + if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){ + data->byte = readl(REG(i2c, RTL9300_I2C_DATA_WORD0)); + } else if (size == I2C_SMBUS_WORD_DATA) { + data->word = readl(REG(i2c, RTL9300_I2C_DATA_WORD0)); + } else if (len > 0) { + rtl9300_i2c_read(i2c, &data->block[0], len); + } + } + + return 0; +} + +static int rtl9310_execute_xfer(struct rtl9300_i2c *i2c, char read_write, + int size, union i2c_smbus_data * data, int len) +{ + u32 v; + + if (read_write == I2C_SMBUS_READ) + REG_MASK(i2c, BIT(RTL9310_I2C_CTRL_RWOP), 0, RTL9310_I2C_CTRL); + else + REG_MASK(i2c, 0, BIT(RTL9310_I2C_CTRL_RWOP), RTL9310_I2C_CTRL); + + REG_MASK(i2c, 0, BIT(RTL9310_I2C_CTRL_I2C_TRIG), RTL9310_I2C_CTRL); + do { + v = readl(REG(i2c, RTL9310_I2C_CTRL)); + } while (v & BIT(RTL9310_I2C_CTRL_I2C_TRIG)); + + if (v & BIT(RTL9310_I2C_CTRL_I2C_FAIL)) + return -EIO; + + if (read_write == I2C_SMBUS_READ) { + if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){ + data->byte = readl(REG(i2c, RTL9310_I2C_DATA)); + } else if (size == I2C_SMBUS_WORD_DATA) { + data->word = readl(REG(i2c, RTL9310_I2C_DATA)); + } else if (len > 0) { + rtl9310_i2c_read(i2c, &data->block[0], len); + } + } + + return 0; +} + +static int rtl9300_i2c_smbus_xfer(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + struct rtl9300_i2c *i2c = i2c_get_adapdata(adap); + struct i2c_drv_data *drv_data = (struct i2c_drv_data *)device_get_match_data(i2c->dev); + int len = 0, ret; + + mutex_lock(&i2c_lock); + switch (size) { + case I2C_SMBUS_QUICK: + drv_data->config_xfer(i2c, addr, 0); + drv_data->reg_addr_set(i2c, 0, 0); + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + drv_data->config_xfer(i2c, addr, 0); + drv_data->reg_addr_set(i2c, command, 1); + } else { + drv_data->config_xfer(i2c, addr, 1); + drv_data->reg_addr_set(i2c, 0, 0); + } + break; + + case I2C_SMBUS_BYTE_DATA: + pr_debug("I2C_SMBUS_BYTE_DATA %02x, read %d cmd %02x\n", addr, read_write, command); + drv_data->reg_addr_set(i2c, command, 1); + drv_data->config_xfer(i2c, addr, 1); + + if (read_write == I2C_SMBUS_WRITE) { + pr_debug("--> data %02x\n", data->byte); + drv_data->writel(i2c, data->byte); + } + break; + + case I2C_SMBUS_WORD_DATA: + pr_debug("I2C_SMBUS_WORD %02x, read %d\n", addr, read_write); + drv_data->reg_addr_set(i2c, command, 1); + drv_data->config_xfer(i2c, addr, 2); + if (read_write == I2C_SMBUS_WRITE) + drv_data->writel(i2c, data->word); + break; + + case I2C_SMBUS_BLOCK_DATA: + pr_debug("I2C_SMBUS_BLOCK_DATA %02x, read %d, len %d\n", + addr, read_write, data->block[0]); + drv_data->reg_addr_set(i2c, command, 1); + drv_data->config_xfer(i2c, addr, data->block[0]); + if (read_write == I2C_SMBUS_WRITE) + drv_data->write(i2c, &data->block[1], data->block[0]); + len = data->block[0]; + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + ret = drv_data->execute_xfer(i2c, read_write, size, data, len); + + mutex_unlock(&i2c_lock); + + return ret; +} + +static u32 rtl9300_i2c_func(struct i2c_adapter *a) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm rtl9300_i2c_algo = { + .smbus_xfer = rtl9300_i2c_smbus_xfer, + .functionality = rtl9300_i2c_func, +}; + +struct i2c_adapter_quirks rtl9300_i2c_quirks = { + .flags = I2C_AQ_NO_CLK_STRETCH, + .max_read_len = 16, + .max_write_len = 16, +}; + +static int rtl9300_i2c_probe(struct platform_device *pdev) +{ + struct resource *res; + struct rtl9300_i2c *i2c; + struct i2c_adapter *adap; + struct i2c_drv_data *drv_data; + struct device_node *node = pdev->dev.of_node; + u32 clock_freq, pin; + int ret = 0; + + pr_info("%s probing I2C adapter\n", __func__); + + if (!node) { + dev_err(i2c->dev, "No DT found\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + drv_data = (struct i2c_drv_data *) device_get_match_data(&pdev->dev); + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct rtl9300_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->base = devm_ioremap_resource(&pdev->dev, res); + i2c->mst2_offset = drv_data->mst2_offset; + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); + + pr_debug("%s base memory %08x\n", __func__, (u32)i2c->base); + i2c->dev = &pdev->dev; + + if (of_property_read_u32(node, "clock-frequency", &clock_freq)) { + clock_freq = I2C_MAX_STANDARD_MODE_FREQ; + } + switch(clock_freq) { + case I2C_MAX_STANDARD_MODE_FREQ: + i2c->bus_freq = RTL9300_I2C_STD_FREQ; + break; + + case I2C_MAX_FAST_MODE_FREQ: + i2c->bus_freq = RTL9300_I2C_FAST_FREQ; + break; + default: + dev_warn(i2c->dev, "clock-frequency %d not supported\n", clock_freq); + return -EINVAL; + } + + dev_info(&pdev->dev, "SCL speed %d, mode is %d\n", clock_freq, i2c->bus_freq); + + if (of_property_read_u32(node, "scl-pin", &pin)) { + dev_warn(i2c->dev, "SCL pin not found in DT, using default\n"); + pin = drv_data->scl0_pin; + } + if (!(pin == drv_data->scl0_pin || pin == drv_data->scl1_pin)) { + dev_warn(i2c->dev, "SCL pin %d not supported\n", pin); + return -EINVAL; + } + i2c->scl_num = pin == drv_data->scl0_pin ? 0 : 1; + pr_info("%s scl_num %d\n", __func__, i2c->scl_num); + + if (of_property_read_u32(node, "sda-pin", &pin)) { + dev_warn(i2c->dev, "SDA pin not found in DT, using default \n"); + pin = drv_data->sda0_pin; + } + i2c->sda_num = pin - drv_data->sda0_pin; + if (i2c->sda_num < 0 || i2c->sda_num > 7) { + dev_warn(i2c->dev, "SDA pin %d not supported\n", pin); + return -EINVAL; + } + pr_info("%s sda_num %d\n", __func__, i2c->sda_num); + + adap = &i2c->adap; + adap->owner = THIS_MODULE; + adap->algo = &rtl9300_i2c_algo; + adap->retries = 3; + adap->dev.parent = &pdev->dev; + i2c_set_adapdata(adap, i2c); + adap->dev.of_node = node; + strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); + + platform_set_drvdata(pdev, i2c); + + drv_data->config_io(i2c, i2c->scl_num, i2c->sda_num); + + ret = i2c_add_adapter(adap); + + return ret; +} + +static int rtl9300_i2c_remove(struct platform_device *pdev) +{ + struct rtl9300_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + + return 0; +} + +struct i2c_drv_data rtl9300_i2c_drv_data = { + .scl0_pin = 8, + .scl1_pin = 17, + .sda0_pin = 9, + .read = rtl9300_i2c_read, + .read = rtl9300_i2c_write, + .reg_addr_set = rtl9300_i2c_reg_addr_set, + .config_xfer = rtl9300_i2c_config_xfer, + .execute_xfer = rtl9300_execute_xfer, + .writel = rtl9300_writel, + .config_io = rtl9300_i2c_config_io, + .mst2_offset = 0x1c, +}; + +struct i2c_drv_data rtl9310_i2c_drv_data = { + .scl0_pin = 13, + .scl1_pin = 14, + .sda0_pin = 0, + .read = rtl9310_i2c_read, + .read = rtl9310_i2c_write, + .reg_addr_set = rtl9310_i2c_reg_addr_set, + .config_xfer = rtl9310_i2c_config_xfer, + .execute_xfer = rtl9310_execute_xfer, + .writel = rtl9310_writel, + .config_io = rtl9310_i2c_config_io, + .mst2_offset = 0x18, +}; + +static const struct of_device_id i2c_rtl9300_dt_ids[] = { + { .compatible = "realtek,rtl9300-i2c", .data = (void *) &rtl9300_i2c_drv_data }, + { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtl838x_eth_of_ids); + +static struct platform_driver rtl9300_i2c_driver = { + .probe = rtl9300_i2c_probe, + .remove = rtl9300_i2c_remove, + .driver = { + .name = "i2c-rtl9300", + .pm = NULL, + .of_match_table = i2c_rtl9300_dt_ids, + }, +}; + +module_platform_driver(rtl9300_i2c_driver); + +MODULE_AUTHOR("Birger Koblitz"); +MODULE_DESCRIPTION("RTL9300 I2C host driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.h b/target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.h new file mode 100644 index 00000000000..b10c38c91ca --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.h @@ -0,0 +1,62 @@ +#ifndef I2C_RTL9300_H +#define I2C_RTL9300_H + +#include + +#define RTL9300_I2C_CTRL1 0x00 +#define RTL9300_I2C_CTRL1_MEM_ADDR 8 +#define RTL9300_I2C_CTRL1_SDA_OUT_SEL 4 +#define RTL9300_I2C_CTRL1_GPIO8_SCL_SEL 3 +#define RTL9300_I2C_CTRL1_RWOP 2 +#define RTL9300_I2C_CTRL1_I2C_FAIL 1 +#define RTL9300_I2C_CTRL1_I2C_TRIG 0 + +#define RTL9300_I2C_CTRL2 0x04 +#define RTL9300_I2C_CTRL2_DRIVE_ACK_DELAY 20 +#define RTL9300_I2C_CTRL2_CHECK_ACK_DELAY 16 +#define RTL9300_I2C_CTRL2_READ_MODE 15 +#define RTL9300_I2C_CTRL2_DEV_ADDR 8 +#define RTL9300_I2C_CTRL2_DATA_WIDTH 4 +#define RTL9300_I2C_CTRL2_MADDR_WIDTH 2 +#define RTL9300_I2C_CTRL2_SCL_FREQ 0 + +#define RTL9300_I2C_DATA_WORD0 0x08 + +#define RTL9300_I2C_MST_GLB_CTRL 0x18 + +#define RTL9310_I2C_MST_IF_CTRL 0x00 + +#define RTL9310_I2C_MST_IF_SEL 0x04 +#define RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL 12 + +#define RTL9310_I2C_CTRL 0x08 +#define RTL9310_I2C_CTRL_SCL_FREQ 30 +#define RTL9310_I2C_CTRL_CHECK_ACK_DELAY 26 +#define RTL9310_I2C_CTRL_DRIVE_ACK_DELAY 22 +#define RTL9310_I2C_CTRL_SDA_OUT_SEL 18 +#define RTL9310_I2C_CTRL_DEV_ADDR 11 +#define RTL9310_I2C_CTRL_MADDR_WIDTH 9 +#define RTL9310_I2C_CTRL_DATA_WIDTH 5 +#define RTL9310_I2C_CTRL_READ_MODE 4 +#define RTL9310_I2C_CTRL_RWOP 2 +#define RTL9310_I2C_CTRL_I2C_FAIL 1 +#define RTL9310_I2C_CTRL_I2C_TRIG 0 + +#define RTL9310_I2C_MEMADDR 0x0c + +#define RTL9310_I2C_DATA 0x10 + +#define RTL9300_I2C_STD_FREQ 0 +#define RTL9300_I2C_FAST_FREQ 1 + +struct rtl9300_i2c { + void __iomem *base; + u32 mst2_offset; + struct device *dev; + struct i2c_adapter adap; + u8 bus_freq; + u8 sda_num; // SDA channel number + u8 scl_num; // SCL channel, mapping to master 1 or 2 +}; + +#endif diff --git a/target/linux/realtek/files-5.15/drivers/i2c/muxes/i2c-mux-rtl9300.c b/target/linux/realtek/files-5.15/drivers/i2c/muxes/i2c-mux-rtl9300.c new file mode 100644 index 00000000000..2f45b0a3ccb --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/i2c/muxes/i2c-mux-rtl9300.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C multiplexer for the 2 I2C Masters of the RTL9300 + * with up to 8 channels each, but which are not entirely + * independent of each other + */ +#include +#include +#include +#include +#include +#include + +#include "../busses/i2c-rtl9300.h" + +#define NUM_MASTERS 2 +#define NUM_BUSSES 8 + +#define REG(mst, x) (mux->base + x + (mst ? mux->i2c->mst2_offset : 0)) +#define REG_MASK(mst, clear, set, reg) \ + writel((readl(REG((mst),(reg))) & ~(clear)) | (set), REG((mst),(reg))) + +struct channel { + u8 sda_num; + u8 scl_num; +}; + +static struct channel channels[NUM_MASTERS * NUM_BUSSES]; + +struct rtl9300_mux { + void __iomem *base; + struct device *dev; + struct i2c_adapter *parent; + struct rtl9300_i2c * i2c; +}; + +struct i2c_mux_data { + int scl0_pin; + int scl1_pin; + int sda0_pin; + int sda_pins; + int (*i2c_mux_select)(struct i2c_mux_core *muxc, u32 chan); + int (*i2c_mux_deselect)(struct i2c_mux_core *muxc, u32 chan); + void (*sda_sel)(struct i2c_mux_core *muxc, int pin); +}; + +static int rtl9300_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct rtl9300_mux *mux = i2c_mux_priv(muxc); + + // Set SCL pin + REG_MASK(channels[chan].scl_num, 0, + BIT(RTL9300_I2C_CTRL1_GPIO8_SCL_SEL), RTL9300_I2C_CTRL1); + + // Set SDA pin + REG_MASK(channels[chan].scl_num, 0x7 << RTL9300_I2C_CTRL1_SDA_OUT_SEL, + channels[chan].sda_num << RTL9300_I2C_CTRL1_SDA_OUT_SEL, RTL9300_I2C_CTRL1); + + mux->i2c->sda_num = channels[chan].sda_num; + mux->i2c->scl_num = channels[chan].scl_num; + + return 0; +} + +static int rtl9310_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct rtl9300_mux *mux = i2c_mux_priv(muxc); + + // Set SCL pin + REG_MASK(0, 0, BIT(RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL + channels[chan].scl_num), + RTL9310_I2C_MST_IF_SEL); + + // Set SDA pin + REG_MASK(channels[chan].scl_num, 0xf << RTL9310_I2C_CTRL_SDA_OUT_SEL, + channels[chan].sda_num << RTL9310_I2C_CTRL_SDA_OUT_SEL, RTL9310_I2C_CTRL); + + mux->i2c->sda_num = channels[chan].sda_num; + mux->i2c->scl_num = channels[chan].scl_num; + + return 0; +} + +static int rtl9300_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + return 0; +} + +static void rtl9300_sda_sel(struct i2c_mux_core *muxc, int pin) +{ + struct rtl9300_mux *mux = i2c_mux_priv(muxc); + u32 v; + + // Set SDA pin to I2C functionality + v = readl(REG(0, RTL9300_I2C_MST_GLB_CTRL)); + v |= BIT(pin); + writel(v, REG(0, RTL9300_I2C_MST_GLB_CTRL)); +} + +static void rtl9310_sda_sel(struct i2c_mux_core *muxc, int pin) +{ + struct rtl9300_mux *mux = i2c_mux_priv(muxc); + u32 v; + + // Set SDA pin to I2C functionality + v = readl(REG(0, RTL9310_I2C_MST_IF_SEL)); + v |= BIT(pin); + writel(v, REG(0, RTL9310_I2C_MST_IF_SEL)); +} + +static struct device_node *mux_parent_adapter(struct device *dev, struct rtl9300_mux *mux) +{ + struct device_node *node = dev->of_node; + struct device_node *parent_np; + struct i2c_adapter *parent; + + parent_np = of_parse_phandle(node, "i2c-parent", 0); + if (!parent_np) { + dev_err(dev, "Cannot parse i2c-parent\n"); + return ERR_PTR(-ENODEV); + } + parent = of_find_i2c_adapter_by_node(parent_np); + of_node_put(parent_np); + if (!parent) + return ERR_PTR(-EPROBE_DEFER); + + if (!(of_device_is_compatible(parent_np, "realtek,rtl9300-i2c") + || of_device_is_compatible(parent_np, "realtek,rtl9310-i2c"))){ + dev_err(dev, "I2C parent not an RTL9300 I2C controller\n"); + return ERR_PTR(-ENODEV); + } + + mux->parent = parent; + mux->i2c = (struct rtl9300_i2c *)i2c_get_adapdata(parent); + mux->base = mux->i2c->base; + + return parent_np; +} + +struct i2c_mux_data rtl9300_i2c_mux_data = { + .scl0_pin = 8, + .scl1_pin = 17, + .sda0_pin = 9, + .sda_pins = 8, + .i2c_mux_select = rtl9300_i2c_mux_select, + .i2c_mux_deselect = rtl9300_i2c_mux_deselect, + .sda_sel = rtl9300_sda_sel, +}; + +struct i2c_mux_data rtl9310_i2c_mux_data = { + .scl0_pin = 13, + .scl1_pin = 14, + .sda0_pin = 0, + .sda_pins = 16, + .i2c_mux_select = rtl9310_i2c_mux_select, + .i2c_mux_deselect = rtl9300_i2c_mux_deselect, + .sda_sel = rtl9310_sda_sel, +}; + +static const struct of_device_id rtl9300_i2c_mux_of_match[] = { + { .compatible = "realtek,i2c-mux-rtl9300", .data = (void *) &rtl9300_i2c_mux_data}, + { .compatible = "realtek,i2c-mux-rtl9310", .data = (void *) &rtl9310_i2c_mux_data}, + {}, +}; + +MODULE_DEVICE_TABLE(of, rtl9300_i2c_mux_of_match); + +static int rtl9300_i2c_mux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *parent_np; + struct device_node *child; + struct i2c_mux_core *muxc; + struct rtl9300_mux *mux; + struct i2c_mux_data *mux_data; + int children; + int ret; + + pr_info("%s probing I2C adapter\n", __func__); + + if (!node) { + dev_err(dev, "No DT found\n"); + return -EINVAL; + } + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + mux->dev = dev; + + mux_data = (struct i2c_mux_data *) device_get_match_data(dev); + + parent_np = mux_parent_adapter(dev, mux); + if (IS_ERR(parent_np)) + return dev_err_probe(dev, PTR_ERR(parent_np), "i2c-parent adapter not found\n"); + + pr_info("%s base memory %08x\n", __func__, (u32)mux->base); + + children = of_get_child_count(node); + + muxc = i2c_mux_alloc(mux->parent, dev, children, 0, 0, + mux_data->i2c_mux_select, mux_data->i2c_mux_deselect); + if (!muxc) { + ret = -ENOMEM; + goto err_parent; + } + muxc->priv = mux; + + platform_set_drvdata(pdev, muxc); + + for_each_child_of_node(node, child) { + u32 chan; + u32 pin; + + ret = of_property_read_u32(child, "reg", &chan); + if (ret < 0) { + dev_err(dev, "no reg property for node '%pOFn'\n", + child); + goto err_children; + } + + if (chan >= NUM_MASTERS * NUM_BUSSES) { + dev_err(dev, "invalid reg %u\n", chan); + ret = -EINVAL; + goto err_children; + } + + if (of_property_read_u32(child, "scl-pin", &pin)) { + dev_warn(dev, "SCL pin not found in DT, using default\n"); + pin = mux_data->scl0_pin; + } + if (!(pin == mux_data->scl0_pin || pin == mux_data->scl1_pin)) { + dev_warn(dev, "SCL pin %d not supported\n", pin); + ret = -EINVAL; + goto err_children; + } + channels[chan].scl_num = pin == mux_data->scl0_pin ? 0 : 1; + pr_info("%s channel %d scl_num %d\n", __func__, chan, channels[chan].scl_num); + + if (of_property_read_u32(child, "sda-pin", &pin)) { + dev_warn(dev, "SDA pin not found in DT, using default \n"); + pin = mux_data->sda0_pin; + } + channels[chan].sda_num = pin - mux_data->sda0_pin; + if (channels[chan].sda_num < 0 || channels[chan].sda_num >= mux_data->sda_pins) { + dev_warn(dev, "SDA pin %d not supported\n", pin); + return -EINVAL; + } + pr_info("%s channel %d sda_num %d\n", __func__, chan, channels[chan].sda_num); + + mux_data->sda_sel(muxc, channels[chan].sda_num); + + ret = i2c_mux_add_adapter(muxc, 0, chan, 0); + if (ret) + goto err_children; + } + + dev_info(dev, "%d-port mux on %s adapter\n", children, mux->parent->name); + + return 0; + +err_children: + i2c_mux_del_adapters(muxc); +err_parent: + i2c_put_adapter(mux->parent); + + return ret; +} + +static int rtl9300_i2c_mux_remove(struct platform_device *pdev) +{ + struct i2c_mux_core *muxc = platform_get_drvdata(pdev); + + i2c_mux_del_adapters(muxc); + i2c_put_adapter(muxc->parent); + + return 0; +} + +static struct platform_driver i2c_mux_driver = { + .probe = rtl9300_i2c_mux_probe, + .remove = rtl9300_i2c_mux_remove, + .driver = { + .name = "i2c-mux-rtl9300", + .of_match_table = rtl9300_i2c_mux_of_match, + }, +}; +module_platform_driver(i2c_mux_driver); + +MODULE_DESCRIPTION("RTL9300 I2C multiplexer driver"); +MODULE_AUTHOR("Birger Koblitz"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Kconfig b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Kconfig new file mode 100644 index 00000000000..281f08054fa --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_RTL83XX + tristate "Realtek RTL838x/RTL839x switch support" + depends on RTL83XX + select NET_DSA_TAG_TRAILER + help + This driver adds support for Realtek RTL83xx series switching. + diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Makefile b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Makefile new file mode 100644 index 00000000000..8752c797004 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_NET_DSA_RTL83XX) += common.o dsa.o \ + rtl838x.o rtl839x.o rtl930x.o rtl931x.o debugfs.o qos.o tc.o diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c new file mode 100644 index 00000000000..e86ff9ccdfe --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c @@ -0,0 +1,1696 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rtl83xx.h" + +extern struct rtl83xx_soc_info soc_info; + +extern const struct rtl838x_reg rtl838x_reg; +extern const struct rtl838x_reg rtl839x_reg; +extern const struct rtl838x_reg rtl930x_reg; +extern const struct rtl838x_reg rtl931x_reg; + +extern const struct dsa_switch_ops rtl83xx_switch_ops; +extern const struct dsa_switch_ops rtl930x_switch_ops; + +DEFINE_MUTEX(smi_lock); + +int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port) +{ + u32 msti = 0; + u32 port_state[4]; + int index, bit; + int pos = port; + int n = priv->port_width << 1; + + /* Ports above or equal CPU port can never be configured */ + if (port >= priv->cpu_port) + return -1; + + mutex_lock(&priv->reg_mutex); + + /* For the RTL839x and following, the bits are left-aligned in the 64/128 bit field */ + if (priv->family_id == RTL8390_FAMILY_ID) + pos += 12; + if (priv->family_id == RTL9300_FAMILY_ID) + pos += 3; + if (priv->family_id == RTL9310_FAMILY_ID) + pos += 8; + + index = n - (pos >> 4) - 1; + bit = (pos << 1) % 32; + + priv->r->stp_get(priv, msti, port_state); + + mutex_unlock(&priv->reg_mutex); + + return (port_state[index] >> bit) & 3; +} + +static struct table_reg rtl838x_tbl_regs[] = { + TBL_DESC(0x6900, 0x6908, 3, 15, 13, 1), // RTL8380_TBL_L2 + TBL_DESC(0x6914, 0x6918, 18, 14, 12, 1), // RTL8380_TBL_0 + TBL_DESC(0xA4C8, 0xA4CC, 6, 14, 12, 1), // RTL8380_TBL_1 + + TBL_DESC(0x1180, 0x1184, 3, 16, 14, 0), // RTL8390_TBL_L2 + TBL_DESC(0x1190, 0x1194, 17, 15, 12, 0), // RTL8390_TBL_0 + TBL_DESC(0x6B80, 0x6B84, 4, 14, 12, 0), // RTL8390_TBL_1 + TBL_DESC(0x611C, 0x6120, 9, 8, 6, 0), // RTL8390_TBL_2 + + TBL_DESC(0xB320, 0xB334, 3, 18, 16, 0), // RTL9300_TBL_L2 + TBL_DESC(0xB340, 0xB344, 19, 16, 12, 0), // RTL9300_TBL_0 + TBL_DESC(0xB3A0, 0xB3A4, 20, 16, 13, 0), // RTL9300_TBL_1 + TBL_DESC(0xCE04, 0xCE08, 6, 14, 12, 0), // RTL9300_TBL_2 + TBL_DESC(0xD600, 0xD604, 30, 7, 6, 0), // RTL9300_TBL_HSB + TBL_DESC(0x7880, 0x7884, 22, 9, 8, 0), // RTL9300_TBL_HSA + + TBL_DESC(0x8500, 0x8508, 8, 19, 15, 0), // RTL9310_TBL_0 + TBL_DESC(0x40C0, 0x40C4, 22, 16, 14, 0), // RTL9310_TBL_1 + TBL_DESC(0x8528, 0x852C, 6, 18, 14, 0), // RTL9310_TBL_2 + TBL_DESC(0x0200, 0x0204, 9, 15, 12, 0), // RTL9310_TBL_3 + TBL_DESC(0x20dc, 0x20e0, 29, 7, 6, 0), // RTL9310_TBL_4 + TBL_DESC(0x7e1c, 0x7e20, 53, 8, 6, 0), // RTL9310_TBL_5 +}; + +void rtl_table_init(void) +{ + int i; + + for (i = 0; i < RTL_TBL_END; i++) + mutex_init(&rtl838x_tbl_regs[i].lock); +} + +/* + * Request access to table t in table access register r + * Returns a handle to a lock for that table + */ +struct table_reg *rtl_table_get(rtl838x_tbl_reg_t r, int t) +{ + if (r >= RTL_TBL_END) + return NULL; + + if (t >= BIT(rtl838x_tbl_regs[r].c_bit-rtl838x_tbl_regs[r].t_bit)) + return NULL; + + mutex_lock(&rtl838x_tbl_regs[r].lock); + rtl838x_tbl_regs[r].tbl = t; + + return &rtl838x_tbl_regs[r]; +} + +/* + * Release a table r, unlock the corresponding lock + */ +void rtl_table_release(struct table_reg *r) +{ + if (!r) + return; + +// pr_info("Unlocking %08x\n", (u32)r); + mutex_unlock(&r->lock); +// pr_info("Unlock done\n"); +} + +/* + * Reads table index idx into the data registers of the table + */ +void rtl_table_read(struct table_reg *r, int idx) +{ + u32 cmd = r->rmode ? BIT(r->c_bit) : 0; + + cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1)); + sw_w32(cmd, r->addr); + do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1)); +} + +/* + * Writes the content of the table data registers into the table at index idx + */ +void rtl_table_write(struct table_reg *r, int idx) +{ + u32 cmd = r->rmode ? 0 : BIT(r->c_bit); + + cmd |= BIT(r->c_bit + 1) | (r->tbl << r->t_bit) | (idx & (BIT(r->t_bit) - 1)); + sw_w32(cmd, r->addr); + do { } while (sw_r32(r->addr) & BIT(r->c_bit + 1)); +} + +/* + * Returns the address of the ith data register of table register r + * the address is relative to the beginning of the Switch-IO block at 0xbb000000 + */ +inline u16 rtl_table_data(struct table_reg *r, int i) +{ + if (i >= r->max_data) + i = r->max_data - 1; + return r->data + i * 4; +} + +inline u32 rtl_table_data_r(struct table_reg *r, int i) +{ + return sw_r32(rtl_table_data(r, i)); +} + +inline void rtl_table_data_w(struct table_reg *r, u32 v, int i) +{ + sw_w32(v, rtl_table_data(r, i)); +} + +/* Port register accessor functions for the RTL838x and RTL930X SoCs */ +void rtl838x_mask_port_reg(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)clear, (u32)set, reg); +} + +void rtl838x_set_port_reg(u64 set, int reg) +{ + sw_w32((u32)set, reg); +} + +u64 rtl838x_get_port_reg(int reg) +{ + return ((u64) sw_r32(reg)); +} + +/* Port register accessor functions for the RTL839x and RTL931X SoCs */ +void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg); + sw_w32_mask((u32)(clear & 0xffffffff), (u32)(set & 0xffffffff), reg + 4); +} + +u64 rtl839x_get_port_reg_be(int reg) +{ + u64 v = sw_r32(reg); + + v <<= 32; + v |= sw_r32(reg + 4); + return v; +} + +void rtl839x_set_port_reg_be(u64 set, int reg) +{ + sw_w32(set >> 32, reg); + sw_w32(set & 0xffffffff, reg + 4); +} + +void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg) +{ + sw_w32_mask((u32)clear, (u32)set, reg); + sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg + 4); +} + +void rtl839x_set_port_reg_le(u64 set, int reg) +{ + sw_w32(set, reg); + sw_w32(set >> 32, reg + 4); +} + +u64 rtl839x_get_port_reg_le(int reg) +{ + u64 v = sw_r32(reg + 4); + + v <<= 32; + v |= sw_r32(reg); + return v; +} + +int read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + return rtl838x_read_phy(port, page, reg, val); + case RTL8390_FAMILY_ID: + return rtl839x_read_phy(port, page, reg, val); + case RTL9300_FAMILY_ID: + return rtl930x_read_phy(port, page, reg, val); + case RTL9310_FAMILY_ID: + return rtl931x_read_phy(port, page, reg, val); + } + return -1; +} + +int write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + return rtl838x_write_phy(port, page, reg, val); + case RTL8390_FAMILY_ID: + return rtl839x_write_phy(port, page, reg, val); + case RTL9300_FAMILY_ID: + return rtl930x_write_phy(port, page, reg, val); + case RTL9310_FAMILY_ID: + return rtl931x_write_phy(port, page, reg, val); + } + return -1; +} + +static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *dn, *phy_node, *mii_np = dev->of_node; + struct mii_bus *bus; + int ret; + u32 pn; + + pr_debug("In %s\n", __func__); + mii_np = of_find_compatible_node(NULL, NULL, "realtek,rtl838x-mdio"); + if (mii_np) { + pr_debug("Found compatible MDIO node!\n"); + } else { + dev_err(priv->dev, "no %s child node found", "mdio-bus"); + return -ENODEV; + } + + priv->mii_bus = of_mdio_find_bus(mii_np); + if (!priv->mii_bus) { + pr_debug("Deferring probe of mdio bus\n"); + return -EPROBE_DEFER; + } + if (!of_device_is_available(mii_np)) + ret = -ENODEV; + + bus = devm_mdiobus_alloc(priv->ds->dev); + if (!bus) + return -ENOMEM; + + bus->name = "rtl838x slave mii"; + + /* + * Since the NIC driver is loaded first, we can use the mdio rw functions + * assigned there. + */ + bus->read = priv->mii_bus->read; + bus->write = priv->mii_bus->write; + bus->read_paged = priv->mii_bus->read_paged; + bus->write_paged = priv->mii_bus->write_paged; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", bus->name, dev->id); + + bus->parent = dev; + priv->ds->slave_mii_bus = bus; + priv->ds->slave_mii_bus->priv = priv->mii_bus->priv; + priv->ds->slave_mii_bus->access_capabilities = priv->mii_bus->access_capabilities; + + ret = mdiobus_register(priv->ds->slave_mii_bus); + if (ret && mii_np) { + of_node_put(dn); + return ret; + } + + dn = of_find_compatible_node(NULL, NULL, "realtek,rtl83xx-switch"); + if (!dn) { + dev_err(priv->dev, "No RTL switch node in DTS\n"); + return -ENODEV; + } + + for_each_node_by_name(dn, "port") { + phy_interface_t interface; + u32 led_set; + + if (!of_device_is_available(dn)) + continue; + + if (of_property_read_u32(dn, "reg", &pn)) + continue; + + pr_info("%s found port %d\n", __func__, pn); + phy_node = of_parse_phandle(dn, "phy-handle", 0); + if (!phy_node) { + if (pn != priv->cpu_port) + dev_err(priv->dev, "Port node %d misses phy-handle\n", pn); + continue; + } + + pr_info("%s port %d has phandle\n", __func__, pn); + if (of_property_read_u32(phy_node, "sds", &priv->ports[pn].sds_num)) + priv->ports[pn].sds_num = -1; + else { + pr_info("%s sds port %d is %d\n", __func__, pn, + priv->ports[pn].sds_num); + } + pr_info("%s port %d has SDS\n", __func__, priv->ports[pn].sds_num); + + if (of_get_phy_mode(dn, &interface)) + interface = PHY_INTERFACE_MODE_NA; + if (interface == PHY_INTERFACE_MODE_HSGMII) + priv->ports[pn].is2G5 = true; + if (interface == PHY_INTERFACE_MODE_USXGMII) + priv->ports[pn].is2G5 = priv->ports[pn].is10G = true; + if (interface == PHY_INTERFACE_MODE_10GBASER) + priv->ports[pn].is10G = true; + + if (of_property_read_u32(dn, "led-set", &led_set)) + led_set = 0; + priv->ports[pn].led_set = led_set; + + // Check for the integrated SerDes of the RTL8380M first + if (of_property_read_bool(phy_node, "phy-is-integrated") + && priv->id == 0x8380 && pn >= 24) { + pr_debug("----> FÓUND A SERDES\n"); + priv->ports[pn].phy = PHY_RTL838X_SDS; + continue; + } + + if (priv->id >= 0x9300) { + priv->ports[pn].phy_is_integrated = false; + if (of_property_read_bool(phy_node, "phy-is-integrated")) { + priv->ports[pn].phy_is_integrated = true; + priv->ports[pn].phy = PHY_RTL930X_SDS; + } + } else { + if (of_property_read_bool(phy_node, "phy-is-integrated") + && !of_property_read_bool(phy_node, "sfp")) { + priv->ports[pn].phy = PHY_RTL8218B_INT; + continue; + } + } + + if (!of_property_read_bool(phy_node, "phy-is-integrated") + && of_property_read_bool(phy_node, "sfp")) { + priv->ports[pn].phy = PHY_RTL8214FC; + continue; + } + + if (!of_property_read_bool(phy_node, "phy-is-integrated") + && !of_property_read_bool(phy_node, "sfp")) { + priv->ports[pn].phy = PHY_RTL8218B_EXT; + continue; + } + } + + /* Disable MAC polling the PHY so that we can start configuration */ + priv->r->set_port_reg_le(0ULL, priv->r->smi_poll_ctrl); + + /* Enable PHY control via SoC */ + if (priv->family_id == RTL8380_FAMILY_ID) { + /* Enable SerDes NWAY and PHY control via SoC */ + sw_w32_mask(BIT(7), BIT(15), RTL838X_SMI_GLB_CTRL); + } else if (priv->family_id == RTL8390_FAMILY_ID) { + /* Disable PHY polling via SoC */ + sw_w32_mask(BIT(7), 0, RTL839X_SMI_GLB_CTRL); + } + + /* Power on fibre ports and reset them if necessary */ + if (priv->ports[24].phy == PHY_RTL838X_SDS) { + pr_debug("Powering on fibre ports & reset\n"); + rtl8380_sds_power(24, 1); + rtl8380_sds_power(26, 1); + } + + pr_debug("%s done\n", __func__); + return 0; +} + +static int __init rtl83xx_get_l2aging(struct rtl838x_switch_priv *priv) +{ + int t = sw_r32(priv->r->l2_ctrl_1); + + t &= priv->family_id == RTL8380_FAMILY_ID ? 0x7fffff : 0x1FFFFF; + + if (priv->family_id == RTL8380_FAMILY_ID) + t = t * 128 / 625; /* Aging time in seconds. 0: L2 aging disabled */ + else + t = (t * 3) / 5; + + pr_debug("L2 AGING time: %d sec\n", t); + pr_debug("Dynamic aging for ports: %x\n", sw_r32(priv->r->l2_port_aging_out)); + return t; +} + +/* Caller must hold priv->reg_mutex */ +int rtl83xx_lag_add(struct dsa_switch *ds, int group, int port, struct netdev_lag_upper_info *info) +{ + struct rtl838x_switch_priv *priv = ds->priv; + int i; + u32 algomsk = 0; + u32 algoidx = 0; + + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + pr_err("%s: Only mode LACP 802.3ad (4) allowed.\n", __func__); + return -EINVAL; + } + + if (group >= priv->n_lags) { + pr_err("%s: LAG %d invalid.\n", __func__, group); + return -EINVAL; + } + + if (port >= priv->cpu_port) { + pr_err("%s: Port %d invalid.\n", __func__, port); + return -EINVAL; + } + + for (i = 0; i < priv->n_lags; i++) { + if (priv->lags_port_members[i] & BIT_ULL(port)) + break; + } + if (i != priv->n_lags) { + pr_err("%s: Port %d already member of LAG %d.\n", __func__, port, i); + return -ENOSPC; + } + switch(info->hash_type) { + case NETDEV_LAG_HASH_L2: + algomsk |= TRUNK_DISTRIBUTION_ALGO_DMAC_BIT; + algomsk |= TRUNK_DISTRIBUTION_ALGO_SMAC_BIT; + break; + case NETDEV_LAG_HASH_L23: + algomsk |= TRUNK_DISTRIBUTION_ALGO_DMAC_BIT; + algomsk |= TRUNK_DISTRIBUTION_ALGO_SMAC_BIT; + algomsk |= TRUNK_DISTRIBUTION_ALGO_SIP_BIT; //source ip + algomsk |= TRUNK_DISTRIBUTION_ALGO_DIP_BIT; //dest ip + algoidx = 1; + break; + case NETDEV_LAG_HASH_L34: + algomsk |= TRUNK_DISTRIBUTION_ALGO_SRC_L4PORT_BIT; //sport + algomsk |= TRUNK_DISTRIBUTION_ALGO_DST_L4PORT_BIT; //dport + algomsk |= TRUNK_DISTRIBUTION_ALGO_SIP_BIT; //source ip + algomsk |= TRUNK_DISTRIBUTION_ALGO_DIP_BIT; //dest ip + algoidx = 2; + break; + default: + algomsk |= 0x7f; + } + priv->r->set_distribution_algorithm(group, algoidx, algomsk); + priv->r->mask_port_reg_be(0, BIT_ULL(port), priv->r->trk_mbr_ctr(group)); + priv->lags_port_members[group] |= BIT_ULL(port); + + pr_info("%s: Added port %d to LAG %d. Members now %016llx.\n", + __func__, port, group, priv->lags_port_members[group]); + return 0; +} + +/* Caller must hold priv->reg_mutex */ +int rtl83xx_lag_del(struct dsa_switch *ds, int group, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + if (group >= priv->n_lags) { + pr_err("%s: LAG %d invalid.\n", __func__, group); + return -EINVAL; + } + + if (port >= priv->cpu_port) { + pr_err("%s: Port %d invalid.\n", __func__, port); + return -EINVAL; + } + + if (!(priv->lags_port_members[group] & BIT_ULL(port))) { + pr_err("%s: Port %d not member of LAG %d.\n", __func__, port, group); + return -ENOSPC; + } + + // 0x7f algo mask all + priv->r->mask_port_reg_be(BIT_ULL(port), 0, priv->r->trk_mbr_ctr(group)); + priv->lags_port_members[group] &= ~BIT_ULL(port); + + pr_info("%s: Removed port %d from LAG %d. Members now %016llx.\n", + __func__, port, group, priv->lags_port_members[group]); + return 0; +} + +/* + * Allocate a 64 bit octet counter located in the LOG HW table + */ +static int rtl83xx_octet_cntr_alloc(struct rtl838x_switch_priv *priv) +{ + int idx; + + mutex_lock(&priv->reg_mutex); + + idx = find_first_zero_bit(priv->octet_cntr_use_bm, MAX_COUNTERS); + if (idx >= priv->n_counters) { + mutex_unlock(&priv->reg_mutex); + return -1; + } + + set_bit(idx, priv->octet_cntr_use_bm); + mutex_unlock(&priv->reg_mutex); + + return idx; +} + +/* + * Allocate a 32-bit packet counter + * 2 32-bit packet counters share the location of a 64-bit octet counter + * Initially there are no free packet counters and 2 new ones need to be freed + * by allocating the corresponding octet counter + */ +int rtl83xx_packet_cntr_alloc(struct rtl838x_switch_priv *priv) +{ + int idx, j; + + mutex_lock(&priv->reg_mutex); + + /* Because initially no packet counters are free, the logic is reversed: + * a 0-bit means the counter is already allocated (for octets) + */ + idx = find_first_bit(priv->packet_cntr_use_bm, MAX_COUNTERS * 2); + if (idx >= priv->n_counters * 2) { + j = find_first_zero_bit(priv->octet_cntr_use_bm, MAX_COUNTERS); + if (j >= priv->n_counters) { + mutex_unlock(&priv->reg_mutex); + return -1; + } + set_bit(j, priv->octet_cntr_use_bm); + idx = j * 2; + set_bit(j * 2 + 1, priv->packet_cntr_use_bm); + + } else { + clear_bit(idx, priv->packet_cntr_use_bm); + } + + mutex_unlock(&priv->reg_mutex); + + return idx; +} + +/* + * Add an L2 nexthop entry for the L3 routing system / PIE forwarding in the SoC + * Use VID and MAC in rtl838x_l2_entry to identify either a free slot in the L2 hash table + * or mark an existing entry as a nexthop by setting it's nexthop bit + * Called from the L3 layer + * The index in the L2 hash table is filled into nh->l2_id; + */ +int rtl83xx_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct rtl83xx_nexthop *nh) +{ + struct rtl838x_l2_entry e; + u64 seed = priv->r->l2_hash_seed(nh->mac, nh->rvid); + u32 key = priv->r->l2_hash_key(priv, seed); + int i, idx = -1; + u64 entry; + + pr_debug("%s searching for %08llx vid %d with key %d, seed: %016llx\n", + __func__, nh->mac, nh->rvid, key, seed); + + e.type = L2_UNICAST; + u64_to_ether_addr(nh->mac, &e.mac[0]); + e.port = nh->port; + + // Loop over all entries in the hash-bucket and over the second block on 93xx SoCs + for (i = 0; i < priv->l2_bucket_size; i++) { + entry = priv->r->read_l2_entry_using_hash(key, i, &e); + + if (!e.valid || ((entry & 0x0fffffffffffffffULL) == seed)) { + idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1 + : ((key << 2) | i) & 0xffff; + break; + } + } + + if (idx < 0) { + pr_err("%s: No more L2 forwarding entries available\n", __func__); + return -1; + } + + // Found an existing (e->valid is true) or empty entry, make it a nexthop entry + nh->l2_id = idx; + if (e.valid) { + nh->port = e.port; + nh->vid = e.vid; // Save VID + nh->rvid = e.rvid; + nh->dev_id = e.stack_dev; + // If the entry is already a valid next hop entry, don't change it + if (e.next_hop) + return 0; + } else { + e.valid = true; + e.is_static = true; + e.rvid = nh->rvid; + e.is_ip_mc = false; + e.is_ipv6_mc = false; + e.block_da = false; + e.block_sa = false; + e.suspended = false; + e.age = 0; // With port-ignore + e.port = priv->port_ignore; + u64_to_ether_addr(nh->mac, &e.mac[0]); + } + e.next_hop = true; + e.nh_route_id = nh->id; // NH route ID takes place of VID + e.nh_vlan_target = false; + + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + + return 0; +} + +/* + * Removes a Layer 2 next hop entry in the forwarding database + * If it was static, the entire entry is removed, otherwise the nexthop bit is cleared + * and we wait until the entry ages out + */ +int rtl83xx_l2_nexthop_rm(struct rtl838x_switch_priv *priv, struct rtl83xx_nexthop *nh) +{ + struct rtl838x_l2_entry e; + u32 key = nh->l2_id >> 2; + int i = nh->l2_id & 0x3; + u64 entry = entry = priv->r->read_l2_entry_using_hash(key, i, &e); + + pr_debug("%s: id %d, key %d, index %d\n", __func__, nh->l2_id, key, i); + if (!e.valid) { + dev_err(priv->dev, "unknown nexthop, id %x\n", nh->l2_id); + return -1; + } + + if (e.is_static) + e.valid = false; + e.next_hop = false; + e.vid = nh->vid; // Restore VID + e.rvid = nh->rvid; + + priv->r->write_l2_entry_using_hash(key, i, &e); + + return 0; +} + +static int rtl83xx_handle_changeupper(struct rtl838x_switch_priv *priv, + struct net_device *ndev, + struct netdev_notifier_changeupper_info *info) +{ + struct net_device *upper = info->upper_dev; + struct netdev_lag_upper_info *lag_upper_info = NULL; + int i, j, err; + + if (!netif_is_lag_master(upper)) + return 0; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < priv->n_lags; i++) { + if ((!priv->lag_devs[i]) || (priv->lag_devs[i] == upper)) + break; + } + for (j = 0; j < priv->cpu_port; j++) { + if (priv->ports[j].dp->slave == ndev) + break; + } + if (j >= priv->cpu_port) { + err = -EINVAL; + goto out; + } + + if (info->linking) { + lag_upper_info = info->upper_info; + if (!priv->lag_devs[i]) + priv->lag_devs[i] = upper; + err = rtl83xx_lag_add(priv->ds, i, priv->ports[j].dp->index, lag_upper_info); + if (err) { + err = -EINVAL; + goto out; + } + } else { + if (!priv->lag_devs[i]) + err = -EINVAL; + err = rtl83xx_lag_del(priv->ds, i, priv->ports[j].dp->index); + if (err) { + err = -EINVAL; + goto out; + } + if (!priv->lags_port_members[i]) + priv->lag_devs[i] = NULL; + } + +out: + mutex_unlock(&priv->reg_mutex); + return 0; +} + +/* + * Is the lower network device a DSA slave network device of our RTL930X-switch? + * Unfortunately we cannot just follow dev->dsa_prt as this is only set for the + * DSA master device. + */ +int rtl83xx_port_is_under(const struct net_device * dev, struct rtl838x_switch_priv *priv) +{ + int i; + +// TODO: On 5.12: +// if(!dsa_slave_dev_check(dev)) { +// netdev_info(dev, "%s: not a DSA device.\n", __func__); +// return -EINVAL; +// } + + for (i = 0; i < priv->cpu_port; i++) { + if (!priv->ports[i].dp) + continue; + if (priv->ports[i].dp->slave == dev) + return i; + } + return -EINVAL; +} + +static int rtl83xx_netdevice_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); + struct rtl838x_switch_priv *priv; + int err; + + pr_debug("In: %s, event: %lu\n", __func__, event); + + if ((event != NETDEV_CHANGEUPPER) && (event != NETDEV_CHANGELOWERSTATE)) + return NOTIFY_DONE; + + priv = container_of(this, struct rtl838x_switch_priv, nb); + switch (event) { + case NETDEV_CHANGEUPPER: + err = rtl83xx_handle_changeupper(priv, ndev, ptr); + break; + } + + if (err) + return err; + + return NOTIFY_DONE; +} + +const static struct rhashtable_params route_ht_params = { + .key_len = sizeof(u32), + .key_offset = offsetof(struct rtl83xx_route, gw_ip), + .head_offset = offsetof(struct rtl83xx_route, linkage), +}; + +/* + * Updates an L3 next hop entry in the ROUTING table + */ +static int rtl83xx_l3_nexthop_update(struct rtl838x_switch_priv *priv, __be32 ip_addr, u64 mac) +{ + struct rtl83xx_route *r; + struct rhlist_head *tmp, *list; + + rcu_read_lock(); + list = rhltable_lookup(&priv->routes, &ip_addr, route_ht_params); + if (!list) { + rcu_read_unlock(); + return -ENOENT; + } + + rhl_for_each_entry_rcu(r, tmp, list, linkage) { + pr_info("%s: Setting up fwding: ip %pI4, GW mac %016llx\n", + __func__, &ip_addr, mac); + + // Reads the ROUTING table entry associated with the route + priv->r->route_read(r->id, r); + pr_info("Route with id %d to %pI4 / %d\n", r->id, &r->dst_ip, r->prefix_len); + + r->nh.mac = r->nh.gw = mac; + r->nh.port = priv->port_ignore; + r->nh.id = r->id; + + // Do we need to explicitly add a DMAC entry with the route's nh index? + if (priv->r->set_l3_egress_mac) + priv->r->set_l3_egress_mac(r->id, mac); + + // Update ROUTING table: map gateway-mac and switch-mac id to route id + rtl83xx_l2_nexthop_add(priv, &r->nh); + + r->attr.valid = true; + r->attr.action = ROUTE_ACT_FORWARD; + r->attr.type = 0; + r->attr.hit = false; // Reset route-used indicator + + // Add PIE entry with dst_ip and prefix_len + r->pr.dip = r->dst_ip; + r->pr.dip_m = inet_make_mask(r->prefix_len); + + if (r->is_host_route) { + int slot = priv->r->find_l3_slot(r, false); + + pr_info("%s: Got slot for route: %d\n", __func__, slot); + priv->r->host_route_write(slot, r); + } else { + priv->r->route_write(r->id, r); + r->pr.fwd_sel = true; + r->pr.fwd_data = r->nh.l2_id; + r->pr.fwd_act = PIE_ACT_ROUTE_UC; + } + + if (priv->r->set_l3_nexthop) + priv->r->set_l3_nexthop(r->nh.id, r->nh.l2_id, r->nh.if_id); + + if (r->pr.id < 0) { + r->pr.packet_cntr = rtl83xx_packet_cntr_alloc(priv); + if (r->pr.packet_cntr >= 0) { + pr_info("Using packet counter %d\n", r->pr.packet_cntr); + r->pr.log_sel = true; + r->pr.log_data = r->pr.packet_cntr; + } + priv->r->pie_rule_add(priv, &r->pr); + } else { + int pkts = priv->r->packet_cntr_read(r->pr.packet_cntr); + pr_info("%s: total packets: %d\n", __func__, pkts); + + priv->r->pie_rule_write(priv, r->pr.id, &r->pr); + } + } + rcu_read_unlock(); + return 0; +} + +static int rtl83xx_port_ipv4_resolve(struct rtl838x_switch_priv *priv, + struct net_device *dev, __be32 ip_addr) +{ + struct neighbour *n = neigh_lookup(&arp_tbl, &ip_addr, dev); + int err = 0; + u64 mac; + + if (!n) { + n = neigh_create(&arp_tbl, &ip_addr, dev); + if (IS_ERR(n)) + return PTR_ERR(n); + } + + /* If the neigh is already resolved, then go ahead and + * install the entry, otherwise start the ARP process to + * resolve the neigh. + */ + if (n->nud_state & NUD_VALID) { + mac = ether_addr_to_u64(n->ha); + pr_info("%s: resolved mac: %016llx\n", __func__, mac); + rtl83xx_l3_nexthop_update(priv, ip_addr, mac); + } else { + pr_info("%s: need to wait\n", __func__); + neigh_event_send(n, NULL); + } + + neigh_release(n); + return err; +} + +struct rtl83xx_walk_data { + struct rtl838x_switch_priv *priv; + int port; +}; + +static int rtl83xx_port_lower_walk(struct net_device *lower, struct netdev_nested_priv *_priv) +{ + struct rtl83xx_walk_data *data = (struct rtl83xx_walk_data *)_priv->data; + struct rtl838x_switch_priv *priv = data->priv; + int ret = 0; + int index; + + index = rtl83xx_port_is_under(lower, priv); + data->port = index; + if (index >= 0) { + pr_debug("Found DSA-port, index %d\n", index); + ret = 1; + } + + return ret; +} + +int rtl83xx_port_dev_lower_find(struct net_device *dev, struct rtl838x_switch_priv *priv) +{ + struct rtl83xx_walk_data data; + struct netdev_nested_priv _priv; + + data.priv = priv; + data.port = 0; + _priv.data = (void *)&data; + + netdev_walk_all_lower_dev(dev, rtl83xx_port_lower_walk, &_priv); + + return data.port; +} + +static struct rtl83xx_route *rtl83xx_route_alloc(struct rtl838x_switch_priv *priv, u32 ip) +{ + struct rtl83xx_route *r; + int idx = 0, err; + + mutex_lock(&priv->reg_mutex); + + idx = find_first_zero_bit(priv->route_use_bm, MAX_ROUTES); + pr_debug("%s id: %d, ip %pI4\n", __func__, idx, &ip); + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) { + mutex_unlock(&priv->reg_mutex); + return r; + } + + r->id = idx; + r->gw_ip = ip; + r->pr.id = -1; // We still need to allocate a rule in HW + r->is_host_route = false; + + err = rhltable_insert(&priv->routes, &r->linkage, route_ht_params); + if (err) { + pr_err("Could not insert new rule\n"); + mutex_unlock(&priv->reg_mutex); + goto out_free; + } + + set_bit(idx, priv->route_use_bm); + + mutex_unlock(&priv->reg_mutex); + + return r; + +out_free: + kfree(r); + return NULL; +} + + +static struct rtl83xx_route *rtl83xx_host_route_alloc(struct rtl838x_switch_priv *priv, u32 ip) +{ + struct rtl83xx_route *r; + int idx = 0, err; + + mutex_lock(&priv->reg_mutex); + + idx = find_first_zero_bit(priv->host_route_use_bm, MAX_HOST_ROUTES); + pr_debug("%s id: %d, ip %pI4\n", __func__, idx, &ip); + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) { + mutex_unlock(&priv->reg_mutex); + return r; + } + + /* We require a unique route ID irrespective of whether it is a prefix or host + * route (on RTL93xx) as we use this ID to associate a DMAC and next-hop entry */ + r->id = idx + MAX_ROUTES; + + r->gw_ip = ip; + r->pr.id = -1; // We still need to allocate a rule in HW + r->is_host_route = true; + + err = rhltable_insert(&priv->routes, &r->linkage, route_ht_params); + if (err) { + pr_err("Could not insert new rule\n"); + mutex_unlock(&priv->reg_mutex); + goto out_free; + } + + set_bit(idx, priv->host_route_use_bm); + + mutex_unlock(&priv->reg_mutex); + + return r; + +out_free: + kfree(r); + return NULL; +} + + + +static void rtl83xx_route_rm(struct rtl838x_switch_priv *priv, struct rtl83xx_route *r) +{ + int id; + + if (rhltable_remove(&priv->routes, &r->linkage, route_ht_params)) + dev_warn(priv->dev, "Could not remove route\n"); + + if (r->is_host_route) { + id = priv->r->find_l3_slot(r, false); + pr_debug("%s: Got id for host route: %d\n", __func__, id); + r->attr.valid = false; + priv->r->host_route_write(id, r); + clear_bit(r->id - MAX_ROUTES, priv->host_route_use_bm); + } else { + // If there is a HW representation of the route, delete it + if (priv->r->route_lookup_hw) { + id = priv->r->route_lookup_hw(r); + pr_info("%s: Got id for prefix route: %d\n", __func__, id); + r->attr.valid = false; + priv->r->route_write(id, r); + } + clear_bit(r->id, priv->route_use_bm); + } + + kfree(r); +} + +static int rtl83xx_fib4_del(struct rtl838x_switch_priv *priv, + struct fib_entry_notifier_info *info) +{ + struct fib_nh *nh = fib_info_nh(info->fi, 0); + struct rtl83xx_route *r; + struct rhlist_head *tmp, *list; + + pr_debug("In %s, ip %pI4, len %d\n", __func__, &info->dst, info->dst_len); + rcu_read_lock(); + list = rhltable_lookup(&priv->routes, &nh->fib_nh_gw4, route_ht_params); + if (!list) { + rcu_read_unlock(); + pr_err("%s: no such gateway: %pI4\n", __func__, &nh->fib_nh_gw4); + return -ENOENT; + } + rhl_for_each_entry_rcu(r, tmp, list, linkage) { + if (r->dst_ip == info->dst && r->prefix_len == info->dst_len) { + pr_info("%s: found a route with id %d, nh-id %d\n", + __func__, r->id, r->nh.id); + break; + } + } + rcu_read_unlock(); + + rtl83xx_l2_nexthop_rm(priv, &r->nh); + + pr_debug("%s: Releasing packet counter %d\n", __func__, r->pr.packet_cntr); + set_bit(r->pr.packet_cntr, priv->packet_cntr_use_bm); + priv->r->pie_rule_rm(priv, &r->pr); + + rtl83xx_route_rm(priv, r); + + nh->fib_nh_flags &= ~RTNH_F_OFFLOAD; + + return 0; +} + +/* + * On the RTL93xx, an L3 termination endpoint MAC address on which the router waits + * for packets to be routed needs to be allocated. + */ +static int rtl83xx_alloc_router_mac(struct rtl838x_switch_priv *priv, u64 mac) +{ + int i, free_mac = -1; + struct rtl93xx_rt_mac m; + + mutex_lock(&priv->reg_mutex); + for (i = 0; i < MAX_ROUTER_MACS; i++) { + priv->r->get_l3_router_mac(i, &m); + if (free_mac < 0 && !m.valid) { + free_mac = i; + continue; + } + if (m.valid && m.mac == mac) { + free_mac = i; + break; + } + } + + if (free_mac < 0) { + pr_err("No free router MACs, cannot offload\n"); + mutex_unlock(&priv->reg_mutex); + return -1; + } + + m.valid = true; + m.mac = mac; + m.p_type = 0; // An individual port, not a trunk port + m.p_id = 0x3f; // Listen on any port + m.p_id_mask = 0; + m.vid = 0; // Listen on any VLAN... + m.vid_mask = 0; // ... so mask needs to be 0 + m.mac_mask = 0xffffffffffffULL; // We want an exact match of the interface MAC + m.action = L3_FORWARD; // Route the packet + priv->r->set_l3_router_mac(free_mac, &m); + + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int rtl83xx_alloc_egress_intf(struct rtl838x_switch_priv *priv, u64 mac, int vlan) +{ + int i, free_mac = -1; + struct rtl838x_l3_intf intf; + u64 m; + + mutex_lock(&priv->reg_mutex); + for (i = 0; i < MAX_SMACS; i++) { + m = priv->r->get_l3_egress_mac(L3_EGRESS_DMACS + i); + if (free_mac < 0 && !m) { + free_mac = i; + continue; + } + if (m == mac) { + mutex_unlock(&priv->reg_mutex); + return i; + } + } + + if (free_mac < 0) { + pr_err("No free egress interface, cannot offload\n"); + return -1; + } + + // Set up default egress interface 1 + intf.vid = vlan; + intf.smac_idx = free_mac; + intf.ip4_mtu_id = 1; + intf.ip6_mtu_id = 1; + intf.ttl_scope = 1; // TTL + intf.hl_scope = 1; // Hop Limit + intf.ip4_icmp_redirect = intf.ip6_icmp_redirect = 2; // FORWARD + intf.ip4_pbr_icmp_redirect = intf.ip6_pbr_icmp_redirect = 2; // FORWARD; + priv->r->set_l3_egress_intf(free_mac, &intf); + + priv->r->set_l3_egress_mac(L3_EGRESS_DMACS + free_mac, mac); + + mutex_unlock(&priv->reg_mutex); + + return free_mac; +} + +static int rtl83xx_fib4_add(struct rtl838x_switch_priv *priv, + struct fib_entry_notifier_info *info) +{ + struct fib_nh *nh = fib_info_nh(info->fi, 0); + struct net_device *dev = fib_info_nh(info->fi, 0)->fib_nh_dev; + int port; + struct rtl83xx_route *r; + bool to_localhost; + int vlan = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : 0; + + pr_debug("In %s, ip %pI4, len %d\n", __func__, &info->dst, info->dst_len); + if (!info->dst) { + pr_info("Not offloading default route for now\n"); + return 0; + } + + pr_debug("GW: %pI4, interface name %s, mac %016llx, vlan %d\n", &nh->fib_nh_gw4, dev->name, + ether_addr_to_u64(dev->dev_addr), vlan + ); + + port = rtl83xx_port_dev_lower_find(dev, priv); + if (port < 0) + return -1; + + // For now we only work with routes that have a gateway and are not ourself +// if ((!nh->fib_nh_gw4) && (info->dst_len != 32)) +// return 0; + + if ((info->dst & 0xff) == 0xff) + return 0; + + // Do not offload routes to 192.168.100.x + if ((info->dst & 0xffffff00) == 0xc0a86400) + return 0; + + // Do not offload routes to 127.x.x.x + if ((info->dst & 0xff000000) == 0x7f000000) + return 0; + + // Allocate route or host-route (entry if hardware supports this) + if (info->dst_len == 32 && priv->r->host_route_write) + r = rtl83xx_host_route_alloc(priv, nh->fib_nh_gw4); + else + r = rtl83xx_route_alloc(priv, nh->fib_nh_gw4); + + if (!r) { + pr_err("%s: No more free route entries\n", __func__); + return -1; + } + + r->dst_ip = info->dst; + r->prefix_len = info->dst_len; + r->nh.rvid = vlan; + to_localhost = !nh->fib_nh_gw4; + + if (priv->r->set_l3_router_mac) { + u64 mac = ether_addr_to_u64(dev->dev_addr); + + pr_debug("Local route and router mac %016llx\n", mac); + + if (rtl83xx_alloc_router_mac(priv, mac)) + goto out_free_rt; + + // vid = 0: Do not care about VID + r->nh.if_id = rtl83xx_alloc_egress_intf(priv, mac, vlan); + if (r->nh.if_id < 0) + goto out_free_rmac; + + if (to_localhost) { + int slot; + + r->nh.mac = mac; + r->nh.port = priv->port_ignore; + r->attr.valid = true; + r->attr.action = ROUTE_ACT_TRAP2CPU; + r->attr.type = 0; + + slot = priv->r->find_l3_slot(r, false); + pr_debug("%s: Got slot for route: %d\n", __func__, slot); + priv->r->host_route_write(slot, r); + } + } + + // We need to resolve the mac address of the GW + if (!to_localhost) + rtl83xx_port_ipv4_resolve(priv, dev, nh->fib_nh_gw4); + + nh->fib_nh_flags |= RTNH_F_OFFLOAD; + + return 0; + +out_free_rmac: +out_free_rt: + return 0; +} + +static int rtl83xx_fib6_add(struct rtl838x_switch_priv *priv, + struct fib6_entry_notifier_info *info) +{ + pr_debug("In %s\n", __func__); +// nh->fib_nh_flags |= RTNH_F_OFFLOAD; + return 0; +} + +struct net_event_work { + struct work_struct work; + struct rtl838x_switch_priv *priv; + u64 mac; + u32 gw_addr; +}; + +static void rtl83xx_net_event_work_do(struct work_struct *work) +{ + struct net_event_work *net_work = + container_of(work, struct net_event_work, work); + struct rtl838x_switch_priv *priv = net_work->priv; + + rtl83xx_l3_nexthop_update(priv, net_work->gw_addr, net_work->mac); +} + +static int rtl83xx_netevent_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct rtl838x_switch_priv *priv; + struct net_device *dev; + struct neighbour *n = ptr; + int err, port; + struct net_event_work *net_work; + + priv = container_of(this, struct rtl838x_switch_priv, ne_nb); + + net_work = kzalloc(sizeof(*net_work), GFP_ATOMIC); + if (!net_work) + return NOTIFY_BAD; + + INIT_WORK(&net_work->work, rtl83xx_net_event_work_do); + net_work->priv = priv; + + switch (event) { + case NETEVENT_NEIGH_UPDATE: + if (n->tbl != &arp_tbl) + return NOTIFY_DONE; + dev = n->dev; + port = rtl83xx_port_dev_lower_find(dev, priv); + if (port < 0 || !(n->nud_state & NUD_VALID)) { + pr_debug("%s: Neigbour invalid, not updating\n", __func__); + kfree(net_work); + return NOTIFY_DONE; + } + + net_work->mac = ether_addr_to_u64(n->ha); + net_work->gw_addr = *(__be32 *) n->primary_key; + + pr_debug("%s: updating neighbour on port %d, mac %016llx\n", + __func__, port, net_work->mac); + schedule_work(&net_work->work); + if (err) + netdev_warn(dev, "failed to handle neigh update (err %d)\n", err); + break; + } + + return NOTIFY_DONE; +} + +struct rtl83xx_fib_event_work { + struct work_struct work; + union { + struct fib_entry_notifier_info fen_info; + struct fib6_entry_notifier_info fen6_info; + struct fib_rule_notifier_info fr_info; + }; + struct rtl838x_switch_priv *priv; + bool is_fib6; + unsigned long event; +}; + +static void rtl83xx_fib_event_work_do(struct work_struct *work) +{ + struct rtl83xx_fib_event_work *fib_work = + container_of(work, struct rtl83xx_fib_event_work, work); + struct rtl838x_switch_priv *priv = fib_work->priv; + struct fib_rule *rule; + int err; + + /* Protect internal structures from changes */ + rtnl_lock(); + pr_debug("%s: doing work, event %ld\n", __func__, fib_work->event); + switch (fib_work->event) { + case FIB_EVENT_ENTRY_ADD: + case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_APPEND: + if (fib_work->is_fib6) { + err = rtl83xx_fib6_add(priv, &fib_work->fen6_info); + } else { + err = rtl83xx_fib4_add(priv, &fib_work->fen_info); + fib_info_put(fib_work->fen_info.fi); + } + if (err) + pr_err("%s: FIB4 failed\n", __func__); + break; + case FIB_EVENT_ENTRY_DEL: + rtl83xx_fib4_del(priv, &fib_work->fen_info); + fib_info_put(fib_work->fen_info.fi); + break; + case FIB_EVENT_RULE_ADD: + case FIB_EVENT_RULE_DEL: + rule = fib_work->fr_info.rule; + if (!fib4_rule_default(rule)) + pr_err("%s: FIB4 default rule failed\n", __func__); + fib_rule_put(rule); + break; + } + rtnl_unlock(); + kfree(fib_work); +} + +/* Called with rcu_read_lock() */ +static int rtl83xx_fib_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct fib_notifier_info *info = ptr; + struct rtl838x_switch_priv *priv; + struct rtl83xx_fib_event_work *fib_work; + + if ((info->family != AF_INET && info->family != AF_INET6 && + info->family != RTNL_FAMILY_IPMR && + info->family != RTNL_FAMILY_IP6MR)) + return NOTIFY_DONE; + + priv = container_of(this, struct rtl838x_switch_priv, fib_nb); + + fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); + if (!fib_work) + return NOTIFY_BAD; + + INIT_WORK(&fib_work->work, rtl83xx_fib_event_work_do); + fib_work->priv = priv; + fib_work->event = event; + fib_work->is_fib6 = false; + + switch (event) { + case FIB_EVENT_ENTRY_ADD: + case FIB_EVENT_ENTRY_REPLACE: + case FIB_EVENT_ENTRY_APPEND: + case FIB_EVENT_ENTRY_DEL: + pr_debug("%s: FIB_ENTRY ADD/DEL, event %ld\n", __func__, event); + if (info->family == AF_INET) { + struct fib_entry_notifier_info *fen_info = ptr; + + if (fen_info->fi->fib_nh_is_v6) { + NL_SET_ERR_MSG_MOD(info->extack, + "IPv6 gateway with IPv4 route is not supported"); + kfree(fib_work); + return notifier_from_errno(-EINVAL); + } + + memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info)); + /* Take referece on fib_info to prevent it from being + * freed while work is queued. Release it afterwards. + */ + fib_info_hold(fib_work->fen_info.fi); + + } else if (info->family == AF_INET6) { + struct fib6_entry_notifier_info *fen6_info = ptr; + pr_warn("%s: FIB_RULE ADD/DEL for IPv6 not supported\n", __func__); + kfree(fib_work); + return NOTIFY_DONE; + } + break; + + case FIB_EVENT_RULE_ADD: + case FIB_EVENT_RULE_DEL: + pr_debug("%s: FIB_RULE ADD/DEL, event: %ld\n", __func__, event); + memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; + } + + schedule_work(&fib_work->work); + + return NOTIFY_DONE; +} + +static int __init rtl83xx_sw_probe(struct platform_device *pdev) +{ + int err = 0, i; + struct rtl838x_switch_priv *priv; + struct device *dev = &pdev->dev; + u64 bpdu_mask; + + pr_debug("Probing RTL838X switch device\n"); + if (!pdev->dev.of_node) { + dev_err(dev, "No DT found\n"); + return -EINVAL; + } + + // Initialize access to RTL switch tables + rtl_table_init(); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); + + if (!priv->ds) + return -ENOMEM; + priv->ds->dev = dev; + priv->ds->priv = priv; + priv->ds->ops = &rtl83xx_switch_ops; + priv->dev = dev; + + mutex_init(&priv->reg_mutex); + + priv->family_id = soc_info.family; + priv->id = soc_info.id; + switch(soc_info.family) { + case RTL8380_FAMILY_ID: + priv->ds->ops = &rtl83xx_switch_ops; + priv->cpu_port = RTL838X_CPU_PORT; + priv->port_mask = 0x1f; + priv->port_width = 1; + priv->irq_mask = 0x0FFFFFFF; + priv->r = &rtl838x_reg; + priv->ds->num_ports = 29; + priv->fib_entries = 8192; + rtl8380_get_version(priv); + priv->n_lags = 8; + priv->l2_bucket_size = 4; + priv->n_pie_blocks = 12; + priv->port_ignore = 0x1f; + priv->n_counters = 128; + break; + case RTL8390_FAMILY_ID: + priv->ds->ops = &rtl83xx_switch_ops; + priv->cpu_port = RTL839X_CPU_PORT; + priv->port_mask = 0x3f; + priv->port_width = 2; + priv->irq_mask = 0xFFFFFFFFFFFFFULL; + priv->r = &rtl839x_reg; + priv->ds->num_ports = 53; + priv->fib_entries = 16384; + rtl8390_get_version(priv); + priv->n_lags = 16; + priv->l2_bucket_size = 4; + priv->n_pie_blocks = 18; + priv->port_ignore = 0x3f; + priv->n_counters = 1024; + break; + case RTL9300_FAMILY_ID: + priv->ds->ops = &rtl930x_switch_ops; + priv->cpu_port = RTL930X_CPU_PORT; + priv->port_mask = 0x1f; + priv->port_width = 1; + priv->irq_mask = 0x0FFFFFFF; + priv->r = &rtl930x_reg; + priv->ds->num_ports = 29; + priv->fib_entries = 16384; + priv->version = RTL8390_VERSION_A; + priv->n_lags = 16; + sw_w32(1, RTL930X_ST_CTRL); + priv->l2_bucket_size = 8; + priv->n_pie_blocks = 16; + priv->port_ignore = 0x3f; + priv->n_counters = 2048; + break; + case RTL9310_FAMILY_ID: + priv->ds->ops = &rtl930x_switch_ops; + priv->cpu_port = RTL931X_CPU_PORT; + priv->port_mask = 0x3f; + priv->port_width = 2; + priv->irq_mask = 0xFFFFFFFFFFFFFULL; + priv->r = &rtl931x_reg; + priv->ds->num_ports = 57; + priv->fib_entries = 16384; + priv->version = RTL8390_VERSION_A; + priv->n_lags = 16; + priv->l2_bucket_size = 8; + break; + } + pr_debug("Chip version %c\n", priv->version); + + err = rtl83xx_mdio_probe(priv); + if (err) { + /* Probing fails the 1st time because of missing ethernet driver + * initialization. Use this to disable traffic in case the bootloader left if on + */ + return err; + } + err = dsa_register_switch(priv->ds); + if (err) { + dev_err(dev, "Error registering switch: %d\n", err); + return err; + } + + /* + * dsa_to_port returns dsa_port from the port list in + * dsa_switch_tree, the tree is built when the switch + * is registered by dsa_register_switch + */ + for (i = 0; i <= priv->cpu_port; i++) + priv->ports[i].dp = dsa_to_port(priv->ds, i); + + /* Enable link and media change interrupts. Are the SERDES masks needed? */ + sw_w32_mask(0, 3, priv->r->isr_glb_src); + + priv->r->set_port_reg_le(priv->irq_mask, priv->r->isr_port_link_sts_chg); + priv->r->set_port_reg_le(priv->irq_mask, priv->r->imr_port_link_sts_chg); + + priv->link_state_irq = platform_get_irq(pdev, 0); + pr_info("LINK state irq: %d\n", priv->link_state_irq); + switch (priv->family_id) { + case RTL8380_FAMILY_ID: + err = request_irq(priv->link_state_irq, rtl838x_switch_irq, + IRQF_SHARED, "rtl838x-link-state", priv->ds); + break; + case RTL8390_FAMILY_ID: + err = request_irq(priv->link_state_irq, rtl839x_switch_irq, + IRQF_SHARED, "rtl839x-link-state", priv->ds); + break; + case RTL9300_FAMILY_ID: + err = request_irq(priv->link_state_irq, rtl930x_switch_irq, + IRQF_SHARED, "rtl930x-link-state", priv->ds); + break; + case RTL9310_FAMILY_ID: + err = request_irq(priv->link_state_irq, rtl931x_switch_irq, + IRQF_SHARED, "rtl931x-link-state", priv->ds); + break; + } + if (err) { + dev_err(dev, "Error setting up switch interrupt.\n"); + /* Need to free allocated switch here */ + } + + /* Enable interrupts for switch, on RTL931x, the IRQ is always on globally */ + if (soc_info.family != RTL9310_FAMILY_ID) + sw_w32(0x1, priv->r->imr_glb); + + rtl83xx_get_l2aging(priv); + + rtl83xx_setup_qos(priv); + + priv->r->l3_setup(priv); + + /* Clear all destination ports for mirror groups */ + for (i = 0; i < 4; i++) + priv->mirror_group_ports[i] = -1; + + /* + * Register netdevice event callback to catch changes in link aggregation groups + */ + priv->nb.notifier_call = rtl83xx_netdevice_event; + if (register_netdevice_notifier(&priv->nb)) { + priv->nb.notifier_call = NULL; + dev_err(dev, "Failed to register LAG netdev notifier\n"); + goto err_register_nb; + } + + // Initialize hash table for L3 routing + rhltable_init(&priv->routes, &route_ht_params); + + /* + * Register netevent notifier callback to catch notifications about neighboring + * changes to update nexthop entries for L3 routing. + */ + priv->ne_nb.notifier_call = rtl83xx_netevent_event; + if (register_netevent_notifier(&priv->ne_nb)) { + priv->ne_nb.notifier_call = NULL; + dev_err(dev, "Failed to register netevent notifier\n"); + goto err_register_ne_nb; + } + + priv->fib_nb.notifier_call = rtl83xx_fib_event; + + /* + * Register Forwarding Information Base notifier to offload routes where + * where possible + * Only FIBs pointing to our own netdevs are programmed into + * the device, so no need to pass a callback. + */ + err = register_fib_notifier(&init_net, &priv->fib_nb, NULL, NULL); + if (err) + goto err_register_fib_nb; + + // TODO: put this into l2_setup() + // Flood BPDUs to all ports including cpu-port + if (soc_info.family != RTL9300_FAMILY_ID) { + bpdu_mask = soc_info.family == RTL8380_FAMILY_ID ? 0x1FFFFFFF : 0x1FFFFFFFFFFFFF; + priv->r->set_port_reg_be(bpdu_mask, priv->r->rma_bpdu_fld_pmask); + + // TRAP 802.1X frames (EAPOL) to the CPU-Port, bypass STP and VLANs + sw_w32(7, priv->r->spcl_trap_eapol_ctrl); + + rtl838x_dbgfs_init(priv); + } else { + rtl930x_dbgfs_init(priv); + } + + return 0; + +err_register_fib_nb: + unregister_netevent_notifier(&priv->ne_nb); +err_register_ne_nb: + unregister_netdevice_notifier(&priv->nb); +err_register_nb: + return err; +} + +static int rtl83xx_sw_remove(struct platform_device *pdev) +{ + // TODO: + pr_debug("Removing platform driver for rtl83xx-sw\n"); + return 0; +} + +static const struct of_device_id rtl83xx_switch_of_ids[] = { + { .compatible = "realtek,rtl83xx-switch"}, + { /* sentinel */ } +}; + + +MODULE_DEVICE_TABLE(of, rtl83xx_switch_of_ids); + +static struct platform_driver rtl83xx_switch_driver = { + .probe = rtl83xx_sw_probe, + .remove = rtl83xx_sw_remove, + .driver = { + .name = "rtl83xx-switch", + .pm = NULL, + .of_match_table = rtl83xx_switch_of_ids, + }, +}; + +module_platform_driver(rtl83xx_switch_driver); + +MODULE_AUTHOR("B. Koblitz"); +MODULE_DESCRIPTION("RTL83XX SoC Switch Driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/debugfs.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/debugfs.c new file mode 100644 index 00000000000..9a7c7714c64 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/debugfs.c @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include +#include "rtl83xx.h" + +#define RTL838X_DRIVER_NAME "rtl838x" + +#define RTL8380_LED_GLB_CTRL (0xA000) +#define RTL8380_LED_MODE_SEL (0x1004) +#define RTL8380_LED_MODE_CTRL (0xA004) +#define RTL8380_LED_P_EN_CTRL (0xA008) +#define RTL8380_LED_SW_CTRL (0xA00C) +#define RTL8380_LED0_SW_P_EN_CTRL (0xA010) +#define RTL8380_LED1_SW_P_EN_CTRL (0xA014) +#define RTL8380_LED2_SW_P_EN_CTRL (0xA018) +#define RTL8380_LED_SW_P_CTRL(p) (0xA01C + (((p) << 2))) + +#define RTL8390_LED_GLB_CTRL (0x00E4) +#define RTL8390_LED_SET_2_3_CTRL (0x00E8) +#define RTL8390_LED_SET_0_1_CTRL (0x00EC) +#define RTL8390_LED_COPR_SET_SEL_CTRL(p) (0x00F0 + (((p >> 4) << 2))) +#define RTL8390_LED_FIB_SET_SEL_CTRL(p) (0x0100 + (((p >> 4) << 2))) +#define RTL8390_LED_COPR_PMASK_CTRL(p) (0x0110 + (((p >> 5) << 2))) +#define RTL8390_LED_FIB_PMASK_CTRL(p) (0x00118 + (((p >> 5) << 2))) +#define RTL8390_LED_COMBO_CTRL(p) (0x0120 + (((p >> 5) << 2))) +#define RTL8390_LED_SW_CTRL (0x0128) +#define RTL8390_LED_SW_P_EN_CTRL(p) (0x012C + (((p / 10) << 2))) +#define RTL8390_LED_SW_P_CTRL(p) (0x0144 + (((p) << 2))) + +#define RTL838X_MIR_QID_CTRL(grp) (0xAD44 + (((grp) << 2))) +#define RTL838X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2))) +#define RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(grp) (0xAA70 + (((grp) << 2))) +#define RTL838X_MIR_RSPAN_TX_CTRL (0xA350) +#define RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL (0xAA80) +#define RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL (0xAA84) +#define RTL839X_MIR_RSPAN_VLAN_CTRL(grp) (0xA340 + (((grp) << 2))) +#define RTL839X_MIR_RSPAN_TX_CTRL (0x69b0) +#define RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL (0x2550) +#define RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL (0x2554) +#define RTL839X_MIR_SAMPLE_RATE_CTRL (0x2558) + +#define RTL838X_STAT_PRVTE_DROP_COUNTERS (0x6A00) +#define RTL839X_STAT_PRVTE_DROP_COUNTERS (0x3E00) +#define RTL930X_STAT_PRVTE_DROP_COUNTERS (0xB5B8) +#define RTL931X_STAT_PRVTE_DROP_COUNTERS (0xd800) + +int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port); +void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); +void rtl83xx_fast_age(struct dsa_switch *ds, int port); +u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port); +u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port); +int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate); +int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate); + + +const char *rtl838x_drop_cntr[] = { + "ALE_TX_GOOD_PKTS", "MAC_RX_DROP", "ACL_FWD_DROP", "HW_ATTACK_PREVENTION_DROP", + "RMA_DROP", "VLAN_IGR_FLTR_DROP", "INNER_OUTER_CFI_EQUAL_1_DROP", "PORT_MOVE_DROP", + "NEW_SA_DROP", "MAC_LIMIT_SYS_DROP", "MAC_LIMIT_VLAN_DROP", "MAC_LIMIT_PORT_DROP", + "SWITCH_MAC_DROP", "ROUTING_EXCEPTION_DROP", "DA_LKMISS_DROP", "RSPAN_DROP", + "ACL_LKMISS_DROP", "ACL_DROP", "INBW_DROP", "IGR_METER_DROP", + "ACCEPT_FRAME_TYPE_DROP", "STP_IGR_DROP", "INVALID_SA_DROP", "SA_BLOCKING_DROP", + "DA_BLOCKING_DROP", "L2_INVALID_DPM_DROP", "MCST_INVALID_DPM_DROP", "RX_FLOW_CONTROL_DROP", + "STORM_SPPRS_DROP", "LALS_DROP", "VLAN_EGR_FILTER_DROP", "STP_EGR_DROP", + "SRC_PORT_FILTER_DROP", "PORT_ISOLATION_DROP", "ACL_FLTR_DROP", "MIRROR_FLTR_DROP", + "TX_MAX_DROP", "LINK_DOWN_DROP", "FLOW_CONTROL_DROP", "BRIDGE .1d discards" +}; + +const char *rtl839x_drop_cntr[] = { + "ALE_TX_GOOD_PKTS", "ERROR_PKTS", "EGR_ACL_DROP", "EGR_METER_DROP", + "OAM", "CFM" "VLAN_IGR_FLTR", "VLAN_ERR", + "INNER_OUTER_CFI_EQUAL_1", "VLAN_TAG_FORMAT", "SRC_PORT_SPENDING_TREE", "INBW", + "RMA", "HW_ATTACK_PREVENTION", "PROTO_STORM", "MCAST_SA", + "IGR_ACL_DROP", "IGR_METER_DROP", "DFLT_ACTION_FOR_MISS_ACL_AND_C2SC", "NEW_SA", + "PORT_MOVE", "SA_BLOCKING", "ROUTING_EXCEPTION", "SRC_PORT_SPENDING_TREE_NON_FWDING", + "MAC_LIMIT", "UNKNOW_STORM", "MISS_DROP", "CPU_MAC_DROP", + "DA_BLOCKING", "SRC_PORT_FILTER_BEFORE_EGR_ACL", "VLAN_EGR_FILTER", "SPANNING_TRE", + "PORT_ISOLATION", "OAM_EGRESS_DROP", "MIRROR_ISOLATION", "MAX_LEN_BEFORE_EGR_ACL", + "SRC_PORT_FILTER_BEFORE_MIRROR", "MAX_LEN_BEFORE_MIRROR", "SPECIAL_CONGEST_BEFORE_MIRROR", + "LINK_STATUS_BEFORE_MIRROR", + "WRED_BEFORE_MIRROR", "MAX_LEN_AFTER_MIRROR", "SPECIAL_CONGEST_AFTER_MIRROR", + "LINK_STATUS_AFTER_MIRROR", + "WRED_AFTER_MIRROR" +}; + +const char *rtl930x_drop_cntr[] = { + "OAM_PARSER", "UC_RPF", "DEI_CFI", "MAC_IP_SUBNET_BASED_VLAN", "VLAN_IGR_FILTER", + "L2_UC_MC", "IPV_IP6_MC_BRIDGE", "PTP", "USER_DEF_0_3", "RESERVED", + "RESERVED1", "RESERVED2", "BPDU_RMA", "LACP", "LLDP", + "EAPOL", "XX_RMA", "L3_IPUC_NON_IP", "IP4_IP6_HEADER_ERROR", "L3_BAD_IP", + "L3_DIP_DMAC_MISMATCH", "IP4_IP_OPTION", "IP_UC_MC_ROUTING_LOOK_UP_MISS", "L3_DST_NULL_INTF", + "L3_PBR_NULL_INTF", + "HOST_NULL_INTF", "ROUTE_NULL_INTF", "BRIDGING_ACTION", "ROUTING_ACTION", "IPMC_RPF", + "L2_NEXTHOP_AGE_OUT", "L3_UC_TTL_FAIL", "L3_MC_TTL_FAIL", "L3_UC_MTU_FAIL", "L3_MC_MTU_FAIL", + "L3_UC_ICMP_REDIR", "IP6_MLD_OTHER_ACT", "ND", "IP_MC_RESERVED", "IP6_HBH", + "INVALID_SA", "L2_HASH_FULL", "NEW_SA", "PORT_MOVE_FORBID", "STATIC_PORT_MOVING", + "DYNMIC_PORT_MOVING", "L3_CRC", "MAC_LIMIT", "ATTACK_PREVENT", "ACL_FWD_ACTION", + "OAMPDU", "OAM_MUX", "TRUNK_FILTER", "ACL_DROP", "IGR_BW", + "ACL_METER", "VLAN_ACCEPT_FRAME_TYPE", "MSTP_SRC_DROP_DISABLED_BLOCKING", "SA_BLOCK", "DA_BLOCK", + "STORM_CONTROL", "VLAN_EGR_FILTER", "MSTP_DESTINATION_DROP", "SRC_PORT_FILTER", "PORT_ISOLATION", + "TX_MAX_FRAME_SIZE", "EGR_LINK_STATUS", "MAC_TX_DISABLE", "MAC_PAUSE_FRAME", "MAC_RX_DROP", + "MIRROR_ISOLATE", "RX_FC", "EGR_QUEUE", "HSM_RUNOUT", "ROUTING_DISABLE", "INVALID_L2_NEXTHOP_ENTRY", + "L3_MC_SRC_FLT", "CPUTAG_FLT", "FWD_PMSK_NULL", "IPUC_ROUTING_LOOKUP_MISS", "MY_DEV_DROP", + "STACK_NONUC_BLOCKING_PMSK", "STACK_PORT_NOT_FOUND", "ACL_LOOPBACK_DROP", "IP6_ROUTING_EXT_HEADER" +}; + +const char *rtl931x_drop_cntr[] = { + "ALE_RX_GOOD_PKTS", "RX_MAX_FRAME_SIZE", "MAC_RX_DROP", "OPENFLOW_IP_MPLS_TTL", "OPENFLOW_TBL_MISS", + "IGR_BW", "SPECIAL_CONGEST", "EGR_QUEUE", "RESERVED", "EGR_LINK_STATUS", "STACK_UCAST_NONUCAST_TTL", // 10 + "STACK_NONUC_BLOCKING_PMSK", "L2_CRC", "SRC_PORT_FILTER", "PARSER_PACKET_TOO_LONG", "PARSER_MALFORM_PACKET", + "MPLS_OVER_2_LBL", "EACL_METER", "IACL_METER", "PROTO_STORM", "INVALID_CAPWAP_HEADER", // 20 + "MAC_IP_SUBNET_BASED_VLAN", "OAM_PARSER", "UC_MC_RPF", "IP_MAC_BINDING_MATCH_MISMATCH", "SA_BLOCK", + "TUNNEL_IP_ADDRESS_CHECK", "EACL_DROP", "IACL_DROP", "ATTACK_PREVENT", "SYSTEM_PORT_LIMIT_LEARN", // 30, + "OAMPDU", "CCM_RX", "CFM_UNKNOWN_TYPE", "LBM_LBR_LTM_LTR", "Y_1731", "VLAN_LIMIT_LEARN", + "VLAN_ACCEPT_FRAME_TYPE", "CFI_1", "STATIC_DYNAMIC_PORT_MOVING", "PORT_MOVE_FORBID", // 40 + "L3_CRC", "BPDU_PTP_LLDP_EAPOL_RMA", "MSTP_SRC_DROP_DISABLED_BLOCKING", "INVALID_SA", "NEW_SA", + "VLAN_IGR_FILTER", "IGR_VLAN_CONVERT", "GRATUITOUS_ARP", "MSTP_SRC_DROP", "L2_HASH_FULL", // 50 + "MPLS_UNKNOWN_LBL", "L3_IPUC_NON_IP", "TTL", "MTU", "ICMP_REDIRECT", "STORM_CONTROL", "L3_DIP_DMAC_MISMATCH", + "IP4_IP_OPTION", "IP6_HBH_EXT_HEADER", "IP4_IP6_HEADER_ERROR", // 60 + "ROUTING_IP_ADDR_CHECK", "ROUTING_EXCEPTION", "DA_BLOCK", "OAM_MUX", "PORT_ISOLATION", "VLAN_EGR_FILTER", + "MIRROR_ISOLATE", "MSTP_DESTINATION_DROP", "L2_MC_BRIDGE", "IP_UC_MC_ROUTING_LOOK_UP_MISS", // 70 + "L2_UC", "L2_MC", "IP4_MC", "IP6_MC", "L3_UC_MC_ROUTE", "UNKNOWN_L2_UC_FLPM", "BC_FLPM", + "VLAN_PRO_UNKNOWN_L2_MC_FLPM", "VLAN_PRO_UNKNOWN_IP4_MC_FLPM", "VLAN_PROFILE_UNKNOWN_IP6_MC_FLPM" // 80, +}; + +static ssize_t rtl838x_common_read(char __user *buffer, size_t count, + loff_t *ppos, unsigned int value) +{ + char *buf; + ssize_t len; + + if (*ppos != 0) + return 0; + + buf = kasprintf(GFP_KERNEL, "0x%08x\n", value); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); + return -ENOSPC; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); + kfree(buf); + + return len; +} + +static ssize_t rtl838x_common_write(const char __user *buffer, size_t count, + loff_t *ppos, unsigned int *value) +{ + char b[32]; + ssize_t len; + int ret; + + if (*ppos != 0) + return -EINVAL; + + if (count >= sizeof(b)) + return -ENOSPC; + + len = simple_write_to_buffer(b, sizeof(b) - 1, ppos, + buffer, count); + if (len < 0) + return len; + + b[len] = '\0'; + ret = kstrtouint(b, 16, value); + if (ret) + return -EIO; + + return len; +} + +static ssize_t stp_state_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + int value = rtl83xx_port_get_stp_state(ds->priv, p->dp->index); + + if (value < 0) + return -EINVAL; + + return rtl838x_common_read(buffer, count, ppos, (u32)value); +} + +static ssize_t stp_state_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + u32 value; + size_t res = rtl838x_common_write(buffer, count, ppos, &value); + if (res < 0) + return res; + + rtl83xx_port_stp_state_set(p->dp->ds, p->dp->index, (u8)value); + + return res; +} + +static const struct file_operations stp_state_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = stp_state_read, + .write = stp_state_write, +}; + +static ssize_t drop_counter_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rtl838x_switch_priv *priv = filp->private_data; + int i; + const char **d; + u32 v; + char *buf; + int n = 0, len, offset; + int num; + + switch (priv->family_id) { + case RTL8380_FAMILY_ID: + d = rtl838x_drop_cntr; + offset = RTL838X_STAT_PRVTE_DROP_COUNTERS; + num = 40; + break; + case RTL8390_FAMILY_ID: + d = rtl839x_drop_cntr; + offset = RTL839X_STAT_PRVTE_DROP_COUNTERS; + num = 45; + break; + case RTL9300_FAMILY_ID: + d = rtl930x_drop_cntr; + offset = RTL930X_STAT_PRVTE_DROP_COUNTERS; + num = 85; + break; + case RTL9310_FAMILY_ID: + d = rtl931x_drop_cntr; + offset = RTL931X_STAT_PRVTE_DROP_COUNTERS; + num = 81; + break; + } + + buf = kmalloc(30 * num, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < num; i++) { + v = sw_r32(offset + (i << 2)) & 0xffff; + n += sprintf(buf + n, "%s: %d\n", d[i], v); + } + + if (count < strlen(buf)) { + kfree(buf); + return -ENOSPC; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); + kfree(buf); + + return len; +} + +static const struct file_operations drop_counter_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = drop_counter_read, +}; + +static void l2_table_print_entry(struct seq_file *m, struct rtl838x_switch_priv *priv, + struct rtl838x_l2_entry *e) +{ + u64 portmask; + int i; + + if (e->type == L2_UNICAST) { + seq_puts(m, "L2_UNICAST\n"); + + seq_printf(m, " mac %02x:%02x:%02x:%02x:%02x:%02x vid %u rvid %u\n", + e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5], + e->vid, e->rvid); + + seq_printf(m, " port %d age %d", e->port, e->age); + if (e->is_static) + seq_puts(m, " static"); + if (e->block_da) + seq_puts(m, " block_da"); + if (e->block_sa) + seq_puts(m, " block_sa"); + if (e->suspended) + seq_puts(m, " suspended"); + if (e->next_hop) + seq_printf(m, " next_hop route_id %u", e->nh_route_id); + seq_puts(m, "\n"); + + } else { + if (e->type == L2_MULTICAST) { + seq_puts(m, "L2_MULTICAST\n"); + + seq_printf(m, " mac %02x:%02x:%02x:%02x:%02x:%02x vid %u rvid %u\n", + e->mac[0], e->mac[1], e->mac[2], e->mac[3], e->mac[4], e->mac[5], + e->vid, e->rvid); + } + + if (e->type == IP4_MULTICAST || e->type == IP6_MULTICAST) { + seq_puts(m, (e->type == IP4_MULTICAST) ? + "IP4_MULTICAST\n" : "IP6_MULTICAST\n"); + + seq_printf(m, " gip %08x sip %08x vid %u rvid %u\n", + e->mc_gip, e->mc_sip, e->vid, e->rvid); + } + + portmask = priv->r->read_mcast_pmask(e->mc_portmask_index); + seq_printf(m, " index %u ports", e->mc_portmask_index); + for (i = 0; i < 64; i++) { + if (portmask & BIT_ULL(i)) + seq_printf(m, " %d", i); + } + seq_puts(m, "\n"); + } + + seq_puts(m, "\n"); +} + +static int l2_table_show(struct seq_file *m, void *v) +{ + struct rtl838x_switch_priv *priv = m->private; + struct rtl838x_l2_entry e; + int i, bucket, index; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < priv->fib_entries; i++) { + bucket = i >> 2; + index = i & 0x3; + priv->r->read_l2_entry_using_hash(bucket, index, &e); + + if (!e.valid) + continue; + + seq_printf(m, "Hash table bucket %d index %d ", bucket, index); + l2_table_print_entry(m, priv, &e); + } + + for (i = 0; i < 64; i++) { + priv->r->read_cam(i, &e); + + if (!e.valid) + continue; + + seq_printf(m, "CAM index %d ", i); + l2_table_print_entry(m, priv, &e); + } + + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int l2_table_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, l2_table_show, inode->i_private); +} + +static const struct file_operations l2_table_fops = { + .owner = THIS_MODULE, + .open = l2_table_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t age_out_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + struct rtl838x_switch_priv *priv = ds->priv; + int value = sw_r32(priv->r->l2_port_aging_out); + + if (value < 0) + return -EINVAL; + + return rtl838x_common_read(buffer, count, ppos, (u32)value); +} + +static ssize_t age_out_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + u32 value; + size_t res = rtl838x_common_write(buffer, count, ppos, &value); + if (res < 0) + return res; + + rtl83xx_fast_age(p->dp->ds, p->dp->index); + + return res; +} + +static const struct file_operations age_out_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = age_out_read, + .write = age_out_write, +}; + +static ssize_t port_egress_rate_read(struct file *filp, char __user *buffer, size_t count, + loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + struct rtl838x_switch_priv *priv = ds->priv; + int value; + if (priv->family_id == RTL8380_FAMILY_ID) + value = rtl838x_get_egress_rate(priv, p->dp->index); + else + value = rtl839x_get_egress_rate(priv, p->dp->index); + + if (value < 0) + return -EINVAL; + + return rtl838x_common_read(buffer, count, ppos, (u32)value); +} + +static ssize_t port_egress_rate_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct rtl838x_port *p = filp->private_data; + struct dsa_switch *ds = p->dp->ds; + struct rtl838x_switch_priv *priv = ds->priv; + u32 value; + size_t res = rtl838x_common_write(buffer, count, ppos, &value); + if (res < 0) + return res; + + if (priv->family_id == RTL8380_FAMILY_ID) + rtl838x_set_egress_rate(priv, p->dp->index, value); + else + rtl839x_set_egress_rate(priv, p->dp->index, value); + + return res; +} + +static const struct file_operations port_egress_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = port_egress_rate_read, + .write = port_egress_rate_write, +}; + + +static const struct debugfs_reg32 port_ctrl_regs[] = { + { .name = "port_isolation", .offset = RTL838X_PORT_ISO_CTRL(0), }, + { .name = "mac_force_mode", .offset = RTL838X_MAC_FORCE_MODE_CTRL, }, +}; + +void rtl838x_dbgfs_cleanup(struct rtl838x_switch_priv *priv) +{ + debugfs_remove_recursive(priv->dbgfs_dir); + +// kfree(priv->dbgfs_entries); +} + +static int rtl838x_dbgfs_port_init(struct dentry *parent, struct rtl838x_switch_priv *priv, + int port) +{ + struct dentry *port_dir; + struct debugfs_regset32 *port_ctrl_regset; + + port_dir = debugfs_create_dir(priv->ports[port].dp->name, parent); + + if (priv->family_id == RTL8380_FAMILY_ID) { + debugfs_create_x32("storm_rate_uc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_UC(port))); + + debugfs_create_x32("storm_rate_mc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_MC(port))); + + debugfs_create_x32("storm_rate_bc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port))); + } else { + debugfs_create_x32("storm_rate_uc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_UC_0(port))); + + debugfs_create_x32("storm_rate_mc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_MC_0(port))); + + debugfs_create_x32("storm_rate_bc", 0644, port_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_BC_0(port))); + } + + debugfs_create_u32("id", 0444, port_dir, (u32 *)&priv->ports[port].dp->index); + + port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL); + if (!port_ctrl_regset) + return -ENOMEM; + + port_ctrl_regset->regs = port_ctrl_regs; + port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs); + port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (port << 2)); + debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset); + + debugfs_create_file("stp_state", 0600, port_dir, &priv->ports[port], &stp_state_fops); + debugfs_create_file("age_out", 0600, port_dir, &priv->ports[port], &age_out_fops); + debugfs_create_file("port_egress_rate", 0600, port_dir, &priv->ports[port], + &port_egress_fops); + return 0; +} + +static int rtl838x_dbgfs_leds(struct dentry *parent, struct rtl838x_switch_priv *priv) +{ + struct dentry *led_dir; + int p; + char led_sw_p_ctrl_name[20]; + char port_led_name[20]; + + led_dir = debugfs_create_dir("led", parent); + + if (priv->family_id == RTL8380_FAMILY_ID) { + debugfs_create_x32("led_glb_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_GLB_CTRL)); + debugfs_create_x32("led_mode_sel", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_SEL)); + debugfs_create_x32("led_mode_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_CTRL)); + debugfs_create_x32("led_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_P_EN_CTRL)); + debugfs_create_x32("led_sw_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_CTRL)); + debugfs_create_x32("led0_sw_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED0_SW_P_EN_CTRL)); + debugfs_create_x32("led1_sw_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED1_SW_P_EN_CTRL)); + debugfs_create_x32("led2_sw_p_en_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED2_SW_P_EN_CTRL)); + for (p = 0; p < 28; p++) { + snprintf(led_sw_p_ctrl_name, sizeof(led_sw_p_ctrl_name), + "led_sw_p_ctrl.%02d", p); + debugfs_create_x32(led_sw_p_ctrl_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_P_CTRL(p))); + } + } else if (priv->family_id == RTL8390_FAMILY_ID) { + debugfs_create_x32("led_glb_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_GLB_CTRL)); + debugfs_create_x32("led_set_2_3", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_2_3_CTRL)); + debugfs_create_x32("led_set_0_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_0_1_CTRL)); + for (p = 0; p < 4; p++) { + snprintf(port_led_name, sizeof(port_led_name), "led_copr_set_sel.%1d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_SET_SEL_CTRL(p << 4))); + snprintf(port_led_name, sizeof(port_led_name), "led_fib_set_sel.%1d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_SET_SEL_CTRL(p << 4))); + } + debugfs_create_x32("led_copr_pmask_ctrl_0", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(0))); + debugfs_create_x32("led_copr_pmask_ctrl_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(32))); + debugfs_create_x32("led_fib_pmask_ctrl_0", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(0))); + debugfs_create_x32("led_fib_pmask_ctrl_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(32))); + debugfs_create_x32("led_combo_ctrl_0", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(0))); + debugfs_create_x32("led_combo_ctrl_1", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(32))); + debugfs_create_x32("led_sw_ctrl", 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_CTRL)); + for (p = 0; p < 5; p++) { + snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_en_ctrl.%1d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_EN_CTRL(p * 10))); + } + for (p = 0; p < 28; p++) { + snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_ctrl.%02d", p); + debugfs_create_x32(port_led_name, 0644, led_dir, + (u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_CTRL(p))); + } + } + return 0; +} + +void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv) +{ + struct dentry *rtl838x_dir; + struct dentry *port_dir; + struct dentry *mirror_dir; + struct debugfs_regset32 *port_ctrl_regset; + int ret, i; + char lag_name[10]; + char mirror_name[10]; + + pr_info("%s called\n", __func__); + rtl838x_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL); + if (!rtl838x_dir) + rtl838x_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL); + + priv->dbgfs_dir = rtl838x_dir; + + debugfs_create_u32("soc", 0444, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MODEL_NAME_INFO)); + + /* Create one directory per port */ + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) { + ret = rtl838x_dbgfs_port_init(rtl838x_dir, priv, i); + if (ret) + goto err; + } + } + + /* Create directory for CPU-port */ + port_dir = debugfs_create_dir("cpu_port", rtl838x_dir); + port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL); + if (!port_ctrl_regset) { + ret = -ENOMEM; + goto err; + } + + port_ctrl_regset->regs = port_ctrl_regs; + port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs); + port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (priv->cpu_port << 2)); + debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset); + debugfs_create_u8("id", 0444, port_dir, &priv->cpu_port); + + /* Create entries for LAGs */ + for (i = 0; i < priv->n_lags; i++) { + snprintf(lag_name, sizeof(lag_name), "lag.%02d", i); + if (priv->family_id == RTL8380_FAMILY_ID) + debugfs_create_x32(lag_name, 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i))); + else + debugfs_create_x64(lag_name, 0644, rtl838x_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i))); + } + + /* Create directories for mirror groups */ + for (i = 0; i < 4; i++) { + snprintf(mirror_name, sizeof(mirror_name), "mirror.%1d", i); + mirror_dir = debugfs_create_dir(mirror_name, rtl838x_dir); + if (priv->family_id == RTL8380_FAMILY_ID) { + debugfs_create_x32("ctrl", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_CTRL + i * 4)); + debugfs_create_x32("ingress_pm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 4)); + debugfs_create_x32("egress_pm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 4)); + debugfs_create_x32("qid", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_QID_CTRL(i))); + debugfs_create_x32("rspan_vlan", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL(i))); + debugfs_create_x32("rspan_vlan_mac", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(i))); + debugfs_create_x32("rspan_tx", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_CTRL)); + debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL)); + debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL)); + } else { + debugfs_create_x32("ctrl", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_CTRL + i * 4)); + debugfs_create_x64("ingress_pm", 0644, mirror_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->mir_spm + i * 8)); + debugfs_create_x64("egress_pm", 0644, mirror_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->mir_dpm + i * 8)); + debugfs_create_x32("rspan_vlan", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_VLAN_CTRL(i))); + debugfs_create_x32("rspan_tx", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_CTRL)); + debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL)); + debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL)); + debugfs_create_x64("sample_rate", 0644, mirror_dir, + (u64 *)(RTL838X_SW_BASE + RTL839X_MIR_SAMPLE_RATE_CTRL)); + } + } + + if (priv->family_id == RTL8380_FAMILY_ID) + debugfs_create_x32("bpdu_flood_mask", 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask)); + else + debugfs_create_x64("bpdu_flood_mask", 0644, rtl838x_dir, + (u64 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask)); + + if (priv->family_id == RTL8380_FAMILY_ID) + debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_CTRL)); + else + debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir, + (u32 *)(RTL838X_SW_BASE + RTL839X_VLAN_CTRL)); + + ret = rtl838x_dbgfs_leds(rtl838x_dir, priv); + if (ret) + goto err; + + debugfs_create_file("drop_counters", 0400, rtl838x_dir, priv, &drop_counter_fops); + + debugfs_create_file("l2_table", 0400, rtl838x_dir, priv, &l2_table_fops); + + return; +err: + rtl838x_dbgfs_cleanup(priv); +} + +void rtl930x_dbgfs_init(struct rtl838x_switch_priv *priv) +{ + struct dentry *dbg_dir; + + pr_info("%s called\n", __func__); + dbg_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL); + if (!dbg_dir) + dbg_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL); + + priv->dbgfs_dir = dbg_dir; + + debugfs_create_file("drop_counters", 0400, dbg_dir, priv, &drop_counter_fops); + + debugfs_create_file("l2_table", 0400, dbg_dir, priv, &l2_table_fops); +} diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c new file mode 100644 index 00000000000..6eea0dc9367 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c @@ -0,0 +1,2259 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include +#include "rtl83xx.h" + + +extern struct rtl83xx_soc_info soc_info; + + +static void rtl83xx_init_stats(struct rtl838x_switch_priv *priv) +{ + mutex_lock(&priv->reg_mutex); + + /* Enable statistics module: all counters plus debug. + * On RTL839x all counters are enabled by default + */ + if (priv->family_id == RTL8380_FAMILY_ID) + sw_w32_mask(0, 3, RTL838X_STAT_CTRL); + + /* Reset statistics counters */ + sw_w32_mask(0, 1, priv->r->stat_rst); + + mutex_unlock(&priv->reg_mutex); +} + +static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv) +{ + int i; + u64 v = 0; + + msleep(1000); + /* Enable all ports with a PHY, including the SFP-ports */ + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + v |= BIT_ULL(i); + } + + pr_info("%s: %16llx\n", __func__, v); + priv->r->set_port_reg_le(v, priv->r->smi_poll_ctrl); + + /* PHY update complete, there is no global PHY polling enable bit on the 9300 */ + if (priv->family_id == RTL8390_FAMILY_ID) + sw_w32_mask(0, BIT(7), RTL839X_SMI_GLB_CTRL); + else if(priv->family_id == RTL9300_FAMILY_ID) + sw_w32_mask(0, 0x8000, RTL838X_SMI_GLB_CTRL); +} + +const struct rtl83xx_mib_desc rtl83xx_mib[] = { + MIB_DESC(2, 0xf8, "ifInOctets"), + MIB_DESC(2, 0xf0, "ifOutOctets"), + MIB_DESC(1, 0xec, "dot1dTpPortInDiscards"), + MIB_DESC(1, 0xe8, "ifInUcastPkts"), + MIB_DESC(1, 0xe4, "ifInMulticastPkts"), + MIB_DESC(1, 0xe0, "ifInBroadcastPkts"), + MIB_DESC(1, 0xdc, "ifOutUcastPkts"), + MIB_DESC(1, 0xd8, "ifOutMulticastPkts"), + MIB_DESC(1, 0xd4, "ifOutBroadcastPkts"), + MIB_DESC(1, 0xd0, "ifOutDiscards"), + MIB_DESC(1, 0xcc, ".3SingleCollisionFrames"), + MIB_DESC(1, 0xc8, ".3MultipleCollisionFrames"), + MIB_DESC(1, 0xc4, ".3DeferredTransmissions"), + MIB_DESC(1, 0xc0, ".3LateCollisions"), + MIB_DESC(1, 0xbc, ".3ExcessiveCollisions"), + MIB_DESC(1, 0xb8, ".3SymbolErrors"), + MIB_DESC(1, 0xb4, ".3ControlInUnknownOpcodes"), + MIB_DESC(1, 0xb0, ".3InPauseFrames"), + MIB_DESC(1, 0xac, ".3OutPauseFrames"), + MIB_DESC(1, 0xa8, "DropEvents"), + MIB_DESC(1, 0xa4, "tx_BroadcastPkts"), + MIB_DESC(1, 0xa0, "tx_MulticastPkts"), + MIB_DESC(1, 0x9c, "CRCAlignErrors"), + MIB_DESC(1, 0x98, "tx_UndersizePkts"), + MIB_DESC(1, 0x94, "rx_UndersizePkts"), + MIB_DESC(1, 0x90, "rx_UndersizedropPkts"), + MIB_DESC(1, 0x8c, "tx_OversizePkts"), + MIB_DESC(1, 0x88, "rx_OversizePkts"), + MIB_DESC(1, 0x84, "Fragments"), + MIB_DESC(1, 0x80, "Jabbers"), + MIB_DESC(1, 0x7c, "Collisions"), + MIB_DESC(1, 0x78, "tx_Pkts64Octets"), + MIB_DESC(1, 0x74, "rx_Pkts64Octets"), + MIB_DESC(1, 0x70, "tx_Pkts65to127Octets"), + MIB_DESC(1, 0x6c, "rx_Pkts65to127Octets"), + MIB_DESC(1, 0x68, "tx_Pkts128to255Octets"), + MIB_DESC(1, 0x64, "rx_Pkts128to255Octets"), + MIB_DESC(1, 0x60, "tx_Pkts256to511Octets"), + MIB_DESC(1, 0x5c, "rx_Pkts256to511Octets"), + MIB_DESC(1, 0x58, "tx_Pkts512to1023Octets"), + MIB_DESC(1, 0x54, "rx_Pkts512to1023Octets"), + MIB_DESC(1, 0x50, "tx_Pkts1024to1518Octets"), + MIB_DESC(1, 0x4c, "rx_StatsPkts1024to1518Octets"), + MIB_DESC(1, 0x48, "tx_Pkts1519toMaxOctets"), + MIB_DESC(1, 0x44, "rx_Pkts1519toMaxOctets"), + MIB_DESC(1, 0x40, "rxMacDiscards") +}; + + +/* DSA callbacks */ + + +static enum dsa_tag_protocol rtl83xx_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mprot) +{ + /* The switch does not tag the frames, instead internally the header + * structure for each packet is tagged accordingly. + */ + return DSA_TAG_PROTO_TRAILER; +} + +/* + * Initialize all VLANS + */ +static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv) +{ + struct rtl838x_vlan_info info; + int i; + + pr_info("In %s\n", __func__); + + priv->r->vlan_profile_setup(0); + priv->r->vlan_profile_setup(1); + pr_info("UNKNOWN_MC_PMASK: %016llx\n", priv->r->read_mcast_pmask(UNKNOWN_MC_PMASK)); + priv->r->vlan_profile_dump(0); + + info.fid = 0; // Default Forwarding ID / MSTI + info.hash_uc_fid = false; // Do not build the L2 lookup hash with FID, but VID + info.hash_mc_fid = false; // Do the same for Multicast packets + info.profile_id = 0; // Use default Vlan Profile 0 + info.tagged_ports = 0; // Initially no port members + if (priv->family_id == RTL9310_FAMILY_ID) { + info.if_id = 0; + info.multicast_grp_mask = 0; + info.l2_tunnel_list_id = -1; + } + + // Initialize all vlans 0-4095 + for (i = 0; i < MAX_VLANS; i ++) + priv->r->vlan_set_tagged(i, &info); + + // reset PVIDs; defaults to 1 on reset + for (i = 0; i <= priv->ds->num_ports; i++) { + priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_INNER, 0); + priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_OUTER, 0); + priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_INNER, PBVLAN_MODE_UNTAG_AND_PRITAG); + priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_OUTER, PBVLAN_MODE_UNTAG_AND_PRITAG); + } + + // Set forwarding action based on inner VLAN tag + for (i = 0; i < priv->cpu_port; i++) + priv->r->vlan_fwd_on_inner(i, true); +} + +static void rtl83xx_setup_bpdu_traps(struct rtl838x_switch_priv *priv) +{ + int i; + + for (i = 0; i < priv->cpu_port; i++) + priv->r->set_receive_management_action(i, BPDU, COPY2CPU); +} + +static void rtl83xx_port_set_salrn(struct rtl838x_switch_priv *priv, + int port, bool enable) +{ + int shift = SALRN_PORT_SHIFT(port); + int val = enable ? SALRN_MODE_HARDWARE : SALRN_MODE_DISABLED; + + sw_w32_mask(SALRN_MODE_MASK << shift, val << shift, + priv->r->l2_port_new_salrn(port)); +} + +static int rtl83xx_setup(struct dsa_switch *ds) +{ + int i; + struct rtl838x_switch_priv *priv = ds->priv; + u64 port_bitmap = BIT_ULL(priv->cpu_port); + + pr_debug("%s called\n", __func__); + + /* Disable MAC polling the PHY so that we can start configuration */ + priv->r->set_port_reg_le(0ULL, priv->r->smi_poll_ctrl); + + for (i = 0; i < ds->num_ports; i++) + priv->ports[i].enable = false; + priv->ports[priv->cpu_port].enable = true; + + /* Isolate ports from each other: traffic only CPU <-> port */ + /* Setting bit j in register RTL838X_PORT_ISO_CTRL(i) allows + * traffic from source port i to destination port j + */ + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) { + priv->r->set_port_reg_be(BIT_ULL(priv->cpu_port) | BIT_ULL(i), + priv->r->port_iso_ctrl(i)); + port_bitmap |= BIT_ULL(i); + } + } + priv->r->set_port_reg_be(port_bitmap, priv->r->port_iso_ctrl(priv->cpu_port)); + + if (priv->family_id == RTL8380_FAMILY_ID) + rtl838x_print_matrix(); + else + rtl839x_print_matrix(); + + rtl83xx_init_stats(priv); + + rtl83xx_vlan_setup(priv); + + rtl83xx_setup_bpdu_traps(priv); + + ds->configure_vlan_while_not_filtering = true; + + priv->r->l2_learning_setup(); + + rtl83xx_port_set_salrn(priv, priv->cpu_port, false); + ds->assisted_learning_on_cpu_port = true; + + /* + * Make sure all frames sent to the switch's MAC are trapped to the CPU-port + * 0: FWD, 1: DROP, 2: TRAP2CPU + */ + if (priv->family_id == RTL8380_FAMILY_ID) + sw_w32(0x2, RTL838X_SPCL_TRAP_SWITCH_MAC_CTRL); + else + sw_w32(0x2, RTL839X_SPCL_TRAP_SWITCH_MAC_CTRL); + + /* Enable MAC Polling PHY again */ + rtl83xx_enable_phy_polling(priv); + pr_debug("Please wait until PHY is settled\n"); + msleep(1000); + priv->r->pie_init(priv); + + return 0; +} + +static int rtl93xx_setup(struct dsa_switch *ds) +{ + int i; + struct rtl838x_switch_priv *priv = ds->priv; + u32 port_bitmap = BIT(priv->cpu_port); + + pr_info("%s called\n", __func__); + + /* Disable MAC polling the PHY so that we can start configuration */ + if (priv->family_id == RTL9300_FAMILY_ID) + sw_w32(0, RTL930X_SMI_POLL_CTRL); + + if (priv->family_id == RTL9310_FAMILY_ID) { + sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL); + sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL + 4); + } + + // Disable all ports except CPU port + for (i = 0; i < ds->num_ports; i++) + priv->ports[i].enable = false; + priv->ports[priv->cpu_port].enable = true; + + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) { + priv->r->traffic_set(i, BIT_ULL(priv->cpu_port) | BIT_ULL(i)); + port_bitmap |= BIT_ULL(i); + } + } + priv->r->traffic_set(priv->cpu_port, port_bitmap); + + rtl930x_print_matrix(); + + // TODO: Initialize statistics + + rtl83xx_vlan_setup(priv); + + ds->configure_vlan_while_not_filtering = true; + + priv->r->l2_learning_setup(); + + rtl83xx_port_set_salrn(priv, priv->cpu_port, false); + ds->assisted_learning_on_cpu_port = true; + + rtl83xx_enable_phy_polling(priv); + + priv->r->pie_init(priv); + + priv->r->led_init(priv); + + return 0; +} + +static int rtl93xx_get_sds(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct device_node *dn; + u32 sds_num; + + if (!dev) + return -1; + if (dev->of_node) { + dn = dev->of_node; + if (of_property_read_u32(dn, "sds", &sds_num)) + sds_num = -1; + } else { + dev_err(dev, "No DT node.\n"); + return -1; + } + + return sds_num; +} + +static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + pr_debug("In %s port %d, state is %d", __func__, port, state->interface); + + if (!phy_interface_mode_is_rgmii(state->interface) && + state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_1000BASEX && + state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII && + state->interface != PHY_INTERFACE_MODE_GMII && + state->interface != PHY_INTERFACE_MODE_QSGMII && + state->interface != PHY_INTERFACE_MODE_INTERNAL && + state->interface != PHY_INTERFACE_MODE_SGMII) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, + "Unsupported interface: %d for port %d\n", + state->interface, port); + return; + } + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + /* With the exclusion of MII and Reverse MII, we support Gigabit, + * including Half duplex + */ + if (state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + } + + /* On both the 8380 and 8382, ports 24-27 are SFP ports */ + if (port >= 24 && port <= 27 && priv->family_id == RTL8380_FAMILY_ID) + phylink_set(mask, 1000baseX_Full); + + /* On the RTL839x family of SoCs, ports 48 to 51 are SFP ports */ + if (port >= 48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID) + phylink_set(mask, 1000baseX_Full); + + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static void rtl93xx_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + pr_debug("In %s port %d, state is %d (%s)", __func__, port, state->interface, + phy_modes(state->interface)); + + if (!phy_interface_mode_is_rgmii(state->interface) && + state->interface != PHY_INTERFACE_MODE_NA && + state->interface != PHY_INTERFACE_MODE_1000BASEX && + state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII && + state->interface != PHY_INTERFACE_MODE_GMII && + state->interface != PHY_INTERFACE_MODE_QSGMII && + state->interface != PHY_INTERFACE_MODE_XGMII && + state->interface != PHY_INTERFACE_MODE_HSGMII && + state->interface != PHY_INTERFACE_MODE_10GBASER && + state->interface != PHY_INTERFACE_MODE_10GKR && + state->interface != PHY_INTERFACE_MODE_USXGMII && + state->interface != PHY_INTERFACE_MODE_INTERNAL && + state->interface != PHY_INTERFACE_MODE_SGMII) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, + "Unsupported interface: %d for port %d\n", + state->interface, port); + return; + } + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + /* With the exclusion of MII and Reverse MII, we support Gigabit, + * including Half duplex + */ + if (state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + } + + // Internal phys of the RTL93xx family provide 10G + if (priv->ports[port].phy_is_integrated + && state->interface == PHY_INTERFACE_MODE_1000BASEX) { + phylink_set(mask, 1000baseX_Full); + } else if (priv->ports[port].phy_is_integrated) { + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 10000baseKR_Full); + phylink_set(mask, 10000baseSR_Full); + phylink_set(mask, 10000baseCR_Full); + } + if (state->interface == PHY_INTERFACE_MODE_INTERNAL) { + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 10000baseKR_Full); + phylink_set(mask, 10000baseT_Full); + phylink_set(mask, 10000baseSR_Full); + phylink_set(mask, 10000baseCR_Full); + } + + if (state->interface == PHY_INTERFACE_MODE_USXGMII) + phylink_set(mask, 10000baseT_Full); + + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + pr_debug("%s leaving supported: %*pb", __func__, __ETHTOOL_LINK_MODE_MASK_NBITS, supported); +} + +static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 speed; + u64 link; + + if (port < 0 || port > priv->cpu_port) + return -EINVAL; + + state->link = 0; + link = priv->r->get_port_reg_le(priv->r->mac_link_sts); + if (link & BIT_ULL(port)) + state->link = 1; + pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port)); + + state->duplex = 0; + if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port)) + state->duplex = 1; + + speed = priv->r->get_port_reg_le(priv->r->mac_link_spd_sts(port)); + speed >>= (port % 16) << 1; + switch (speed & 0x3) { + case 0: + state->speed = SPEED_10; + break; + case 1: + state->speed = SPEED_100; + break; + case 2: + state->speed = SPEED_1000; + break; + case 3: + if (priv->family_id == RTL9300_FAMILY_ID + && (port == 24 || port == 26)) /* Internal serdes */ + state->speed = SPEED_2500; + else + state->speed = SPEED_100; /* Is in fact 500Mbit */ + } + + state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX); + if (priv->r->get_port_reg_le(priv->r->mac_rx_pause_sts) & BIT_ULL(port)) + state->pause |= MLO_PAUSE_RX; + if (priv->r->get_port_reg_le(priv->r->mac_tx_pause_sts) & BIT_ULL(port)) + state->pause |= MLO_PAUSE_TX; + return 1; +} + +static int rtl93xx_phylink_mac_link_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 speed; + u64 link; + u64 media; + + if (port < 0 || port > priv->cpu_port) + return -EINVAL; + + /* + * On the RTL9300 for at least the RTL8226B PHY, the MAC-side link + * state needs to be read twice in order to read a correct result. + * This would not be necessary for ports connected e.g. to RTL8218D + * PHYs. + */ + state->link = 0; + link = priv->r->get_port_reg_le(priv->r->mac_link_sts); + link = priv->r->get_port_reg_le(priv->r->mac_link_sts); + if (link & BIT_ULL(port)) + state->link = 1; + + if (priv->family_id == RTL9310_FAMILY_ID) + media = priv->r->get_port_reg_le(RTL931X_MAC_LINK_MEDIA_STS); + + if (priv->family_id == RTL9300_FAMILY_ID) + media = sw_r32(RTL930X_MAC_LINK_MEDIA_STS); + + if (media & BIT_ULL(port)) + state->link = 1; + + pr_debug("%s: link state port %d: %llx, media %llx\n", __func__, port, + link & BIT_ULL(port), media); + + state->duplex = 0; + if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port)) + state->duplex = 1; + + speed = priv->r->get_port_reg_le(priv->r->mac_link_spd_sts(port)); + speed >>= (port % 8) << 2; + switch (speed & 0xf) { + case 0: + state->speed = SPEED_10; + break; + case 1: + state->speed = SPEED_100; + break; + case 2: + case 7: + state->speed = SPEED_1000; + break; + case 4: + state->speed = SPEED_10000; + break; + case 5: + case 8: + state->speed = SPEED_2500; + break; + case 6: + state->speed = SPEED_5000; + break; + default: + pr_err("%s: unknown speed: %d\n", __func__, (u32)speed & 0xf); + } + + if (priv->family_id == RTL9310_FAMILY_ID + && (port >= 52 || port <= 55)) { /* Internal serdes */ + state->speed = SPEED_10000; + state->link = 1; + state->duplex = 1; + } + + pr_debug("%s: speed is: %d %d\n", __func__, (u32)speed & 0xf, state->speed); + state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX); + if (priv->r->get_port_reg_le(priv->r->mac_rx_pause_sts) & BIT_ULL(port)) + state->pause |= MLO_PAUSE_RX; + if (priv->r->get_port_reg_le(priv->r->mac_tx_pause_sts) & BIT_ULL(port)) + state->pause |= MLO_PAUSE_TX; + return 1; +} + +static void rtl83xx_config_interface(int port, phy_interface_t interface) +{ + u32 old, int_shift, sds_shift; + + switch (port) { + case 24: + int_shift = 0; + sds_shift = 5; + break; + case 26: + int_shift = 3; + sds_shift = 0; + break; + default: + return; + } + + old = sw_r32(RTL838X_SDS_MODE_SEL); + switch (interface) { + case PHY_INTERFACE_MODE_1000BASEX: + if ((old >> sds_shift & 0x1f) == 4) + return; + sw_w32_mask(0x7 << int_shift, 1 << int_shift, RTL838X_INT_MODE_CTRL); + sw_w32_mask(0x1f << sds_shift, 4 << sds_shift, RTL838X_SDS_MODE_SEL); + break; + case PHY_INTERFACE_MODE_SGMII: + if ((old >> sds_shift & 0x1f) == 2) + return; + sw_w32_mask(0x7 << int_shift, 2 << int_shift, RTL838X_INT_MODE_CTRL); + sw_w32_mask(0x1f << sds_shift, 2 << sds_shift, RTL838X_SDS_MODE_SEL); + break; + default: + return; + } + pr_debug("configured port %d for interface %s\n", port, phy_modes(interface)); +} + +static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u32 reg; + int speed_bit = priv->family_id == RTL8380_FAMILY_ID ? 4 : 3; + + pr_debug("%s port %d, mode %x\n", __func__, port, mode); + + if (port == priv->cpu_port) { + /* Set Speed, duplex, flow control + * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL + * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN + * | MEDIA_SEL + */ + if (priv->family_id == RTL8380_FAMILY_ID) { + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + /* allow CRC errors on CPU-port */ + sw_w32_mask(0, 0x8, RTL838X_MAC_PORT_CTRL(priv->cpu_port)); + } else { + sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl(priv->cpu_port)); + } + return; + } + + reg = sw_r32(priv->r->mac_force_mode_ctrl(port)); + /* Auto-Negotiation does not work for MAC in RTL8390 */ + if (priv->family_id == RTL8380_FAMILY_ID) { + if (mode == MLO_AN_PHY || phylink_autoneg_inband(mode)) { + pr_debug("PHY autonegotiates\n"); + reg |= RTL838X_NWAY_EN; + sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); + rtl83xx_config_interface(port, state->interface); + return; + } + } + + if (mode != MLO_AN_FIXED) + pr_debug("Fixed state.\n"); + + /* Clear id_mode_dis bit, and the existing port mode, let + * RGMII_MODE_EN bet set by mac_link_{up,down} */ + if (priv->family_id == RTL8380_FAMILY_ID) { + reg &= ~(RTL838X_RX_PAUSE_EN | RTL838X_TX_PAUSE_EN); + if (state->pause & MLO_PAUSE_TXRX_MASK) { + if (state->pause & MLO_PAUSE_TX) + reg |= RTL838X_TX_PAUSE_EN; + reg |= RTL838X_RX_PAUSE_EN; + } + } else if (priv->family_id == RTL8390_FAMILY_ID) { + reg &= ~(RTL839X_RX_PAUSE_EN | RTL839X_TX_PAUSE_EN); + if (state->pause & MLO_PAUSE_TXRX_MASK) { + if (state->pause & MLO_PAUSE_TX) + reg |= RTL839X_TX_PAUSE_EN; + reg |= RTL839X_RX_PAUSE_EN; + } + } + + + reg &= ~(3 << speed_bit); + switch (state->speed) { + case SPEED_1000: + reg |= 2 << speed_bit; + break; + case SPEED_100: + reg |= 1 << speed_bit; + break; + default: + break; // Ignore, including 10MBit which has a speed value of 0 + } + + if (priv->family_id == RTL8380_FAMILY_ID) { + reg &= ~(RTL838X_DUPLEX_MODE | RTL838X_FORCE_LINK_EN); + if (state->link) + reg |= RTL838X_FORCE_LINK_EN; + if (state->duplex == RTL838X_DUPLEX_MODE) + reg |= RTL838X_DUPLEX_MODE; + } else if (priv->family_id == RTL8390_FAMILY_ID) { + reg &= ~(RTL839X_DUPLEX_MODE | RTL839X_FORCE_LINK_EN); + if (state->link) + reg |= RTL839X_FORCE_LINK_EN; + if (state->duplex == RTL839X_DUPLEX_MODE) + reg |= RTL839X_DUPLEX_MODE; + } + + // LAG members must use DUPLEX and we need to enable the link + if (priv->lagmembers & BIT_ULL(port)) { + switch(priv->family_id) { + case RTL8380_FAMILY_ID: + reg |= (RTL838X_DUPLEX_MODE | RTL838X_FORCE_LINK_EN); + break; + case RTL8390_FAMILY_ID: + reg |= (RTL839X_DUPLEX_MODE | RTL839X_FORCE_LINK_EN); + break; + } + } + + // Disable AN + if (priv->family_id == RTL8380_FAMILY_ID) + reg &= ~RTL838X_NWAY_EN; + sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); +} + +static void rtl931x_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + int sds_num; + u32 reg, band; + + sds_num = priv->ports[port].sds_num; + pr_info("%s: speed %d sds_num %d\n", __func__, state->speed, sds_num); + + switch (state->interface) { + case PHY_INTERFACE_MODE_HSGMII: + pr_info("%s setting mode PHY_INTERFACE_MODE_HSGMII\n", __func__); + band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_HSGMII); + rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_HSGMII); + band = rtl931x_sds_cmu_band_set(sds_num, true, 62, PHY_INTERFACE_MODE_HSGMII); + break; + case PHY_INTERFACE_MODE_1000BASEX: + band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_1000BASEX); + rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_1000BASEX); + break; + case PHY_INTERFACE_MODE_XGMII: + band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_XGMII); + rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_XGMII); + break; + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10GKR: + band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_10GBASER); + rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_10GBASER); + break; + case PHY_INTERFACE_MODE_USXGMII: + // Translates to MII_USXGMII_10GSXGMII + band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_USXGMII); + rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_USXGMII); + break; + case PHY_INTERFACE_MODE_SGMII: + pr_info("%s setting mode PHY_INTERFACE_MODE_SGMII\n", __func__); + band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_SGMII); + rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_SGMII); + band = rtl931x_sds_cmu_band_set(sds_num, true, 62, PHY_INTERFACE_MODE_SGMII); + break; + case PHY_INTERFACE_MODE_QSGMII: + band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_QSGMII); + rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_QSGMII); + break; + default: + pr_err("%s: unknown serdes mode: %s\n", + __func__, phy_modes(state->interface)); + return; + } + + reg = sw_r32(priv->r->mac_force_mode_ctrl(port)); + pr_info("%s reading FORCE_MODE_CTRL: %08x\n", __func__, reg); + + reg &= ~(RTL931X_DUPLEX_MODE | RTL931X_FORCE_EN | RTL931X_FORCE_LINK_EN); + + reg &= ~(0xf << 12); + reg |= 0x2 << 12; // Set SMI speed to 0x2 + + reg |= RTL931X_TX_PAUSE_EN | RTL931X_RX_PAUSE_EN; + + if (priv->lagmembers & BIT_ULL(port)) + reg |= RTL931X_DUPLEX_MODE; + + if (state->duplex == DUPLEX_FULL) + reg |= RTL931X_DUPLEX_MODE; + + sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); + +} + +static void rtl93xx_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct rtl838x_switch_priv *priv = ds->priv; + int sds_num, sds_mode; + u32 reg; + + pr_info("%s port %d, mode %x, phy-mode: %s, speed %d, link %d\n", __func__, + port, mode, phy_modes(state->interface), state->speed, state->link); + + // Nothing to be done for the CPU-port + if (port == priv->cpu_port) + return; + + if (priv->family_id == RTL9310_FAMILY_ID) + return rtl931x_phylink_mac_config(ds, port, mode, state); + + sds_num = priv->ports[port].sds_num; + pr_info("%s SDS is %d\n", __func__, sds_num); + if (sds_num >= 0) { + switch (state->interface) { + case PHY_INTERFACE_MODE_HSGMII: + sds_mode = 0x12; + break; + case PHY_INTERFACE_MODE_1000BASEX: + sds_mode = 0x04; + break; + case PHY_INTERFACE_MODE_XGMII: + sds_mode = 0x10; + break; + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10GKR: + sds_mode = 0x1b; // 10G 1000X Auto + break; + case PHY_INTERFACE_MODE_USXGMII: + sds_mode = 0x0d; + break; + default: + pr_err("%s: unknown serdes mode: %s\n", + __func__, phy_modes(state->interface)); + return; + } + if (state->interface == PHY_INTERFACE_MODE_10GBASER) + rtl9300_serdes_setup(sds_num, state->interface); + } + + reg = sw_r32(priv->r->mac_force_mode_ctrl(port)); + reg &= ~(0xf << 3); + + switch (state->speed) { + case SPEED_10000: + reg |= 4 << 3; + break; + case SPEED_5000: + reg |= 6 << 3; + break; + case SPEED_2500: + reg |= 5 << 3; + break; + case SPEED_1000: + reg |= 2 << 3; + break; + default: + reg |= 2 << 3; + break; + } + + if (state->link) + reg |= RTL930X_FORCE_LINK_EN; + + if (priv->lagmembers & BIT_ULL(port)) + reg |= RTL930X_DUPLEX_MODE | RTL930X_FORCE_LINK_EN; + + if (state->duplex == DUPLEX_FULL) + reg |= RTL930X_DUPLEX_MODE; + + if (priv->ports[port].phy_is_integrated) + reg &= ~RTL930X_FORCE_EN; // Clear MAC_FORCE_EN to allow SDS-MAC link + else + reg |= RTL930X_FORCE_EN; + + sw_w32(reg, priv->r->mac_force_mode_ctrl(port)); +} + +static void rtl83xx_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + /* Stop TX/RX to port */ + sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port)); + + // No longer force link + sw_w32_mask(0x3, 0, priv->r->mac_force_mode_ctrl(port)); +} + +static void rtl93xx_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u32 v = 0; + + /* Stop TX/RX to port */ + sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port)); + + // No longer force link + if (priv->family_id == RTL9300_FAMILY_ID) + v = RTL930X_FORCE_EN | RTL930X_FORCE_LINK_EN; + else if (priv->family_id == RTL9310_FAMILY_ID) + v = RTL931X_FORCE_EN | RTL931X_FORCE_LINK_EN; + sw_w32_mask(v, 0, priv->r->mac_force_mode_ctrl(port)); +} + +static void rtl83xx_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct rtl838x_switch_priv *priv = ds->priv; + /* Restart TX/RX to port */ + sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port)); + // TODO: Set speed/duplex/pauses +} + +static void rtl93xx_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + /* Restart TX/RX to port */ + sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port)); + // TODO: Set speed/duplex/pauses +} + +static void rtl83xx_get_strings(struct dsa_switch *ds, + int port, u32 stringset, u8 *data) +{ + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(rtl83xx_mib); i++) + strncpy(data + i * ETH_GSTRING_LEN, rtl83xx_mib[i].name, + ETH_GSTRING_LEN); +} + +static void rtl83xx_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +{ + struct rtl838x_switch_priv *priv = ds->priv; + const struct rtl83xx_mib_desc *mib; + int i; + u64 h; + + for (i = 0; i < ARRAY_SIZE(rtl83xx_mib); i++) { + mib = &rtl83xx_mib[i]; + + data[i] = sw_r32(priv->r->stat_port_std_mib + (port << 8) + 252 - mib->offset); + if (mib->size == 2) { + h = sw_r32(priv->r->stat_port_std_mib + (port << 8) + 248 - mib->offset); + data[i] |= h << 32; + } + } +} + +static int rtl83xx_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + if (sset != ETH_SS_STATS) + return 0; + + return ARRAY_SIZE(rtl83xx_mib); +} + +static int rtl83xx_mc_group_alloc(struct rtl838x_switch_priv *priv, int port) +{ + int mc_group = find_first_zero_bit(priv->mc_group_bm, MAX_MC_GROUPS - 1); + u64 portmask; + + if (mc_group >= MAX_MC_GROUPS - 1) + return -1; + + if (priv->is_lagmember[port]) { + pr_info("%s: %d is lag slave. ignore\n", __func__, port); + return 0; + } + + set_bit(mc_group, priv->mc_group_bm); + mc_group++; // We cannot use group 0, as this is used for lookup miss flooding + portmask = BIT_ULL(port) | BIT_ULL(priv->cpu_port); + priv->r->write_mcast_pmask(mc_group, portmask); + + return mc_group; +} + +static u64 rtl83xx_mc_group_add_port(struct rtl838x_switch_priv *priv, int mc_group, int port) +{ + u64 portmask = priv->r->read_mcast_pmask(mc_group); + + pr_debug("%s: %d\n", __func__, port); + if (priv->is_lagmember[port]) { + pr_info("%s: %d is lag slave. ignore\n", __func__, port); + return portmask; + } + portmask |= BIT_ULL(port); + priv->r->write_mcast_pmask(mc_group, portmask); + + return portmask; +} + +static u64 rtl83xx_mc_group_del_port(struct rtl838x_switch_priv *priv, int mc_group, int port) +{ + u64 portmask = priv->r->read_mcast_pmask(mc_group); + + pr_debug("%s: %d\n", __func__, port); + if (priv->is_lagmember[port]) { + pr_info("%s: %d is lag slave. ignore\n", __func__, port); + return portmask; + } + priv->r->write_mcast_pmask(mc_group, portmask); + if (portmask == BIT_ULL(priv->cpu_port)) { + portmask &= ~BIT_ULL(priv->cpu_port); + priv->r->write_mcast_pmask(mc_group, portmask); + clear_bit(mc_group, priv->mc_group_bm); + } + + return portmask; +} + +static void store_mcgroups(struct rtl838x_switch_priv *priv, int port) +{ + int mc_group; + + for (mc_group = 0; mc_group < MAX_MC_GROUPS; mc_group++) { + u64 portmask = priv->r->read_mcast_pmask(mc_group); + if (portmask & BIT_ULL(port)) { + priv->mc_group_saves[mc_group] = port; + rtl83xx_mc_group_del_port(priv, mc_group, port); + } + } +} + +static void load_mcgroups(struct rtl838x_switch_priv *priv, int port) +{ + int mc_group; + + for (mc_group = 0; mc_group < MAX_MC_GROUPS; mc_group++) { + if (priv->mc_group_saves[mc_group] == port) { + rtl83xx_mc_group_add_port(priv, mc_group, port); + priv->mc_group_saves[mc_group] = -1; + } + } +} + +static int rtl83xx_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phydev) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 v; + + pr_debug("%s: %x %d", __func__, (u32) priv, port); + priv->ports[port].enable = true; + + /* enable inner tagging on egress, do not keep any tags */ + priv->r->vlan_port_keep_tag_set(port, 0, 1); + + if (dsa_is_cpu_port(ds, port)) + return 0; + + /* add port to switch mask of CPU_PORT */ + priv->r->traffic_enable(priv->cpu_port, port); + + load_mcgroups(priv, port); + + if (priv->is_lagmember[port]) { + pr_debug("%s: %d is lag slave. ignore\n", __func__, port); + return 0; + } + + /* add all other ports in the same bridge to switch mask of port */ + v = priv->r->traffic_get(port); + v |= priv->ports[port].pm; + priv->r->traffic_set(port, v); + + // TODO: Figure out if this is necessary + if (priv->family_id == RTL9300_FAMILY_ID) { + sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_SABLK_CTRL); + sw_w32_mask(0, BIT(port), RTL930X_L2_PORT_DABLK_CTRL); + } + + if (priv->ports[port].sds_num < 0) + priv->ports[port].sds_num = rtl93xx_get_sds(phydev); + + return 0; +} + +static void rtl83xx_port_disable(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 v; + + pr_debug("%s %x: %d", __func__, (u32)priv, port); + /* you can only disable user ports */ + if (!dsa_is_user_port(ds, port)) + return; + + // BUG: This does not work on RTL931X + /* remove port from switch mask of CPU_PORT */ + priv->r->traffic_disable(priv->cpu_port, port); + store_mcgroups(priv, port); + + /* remove all other ports in the same bridge from switch mask of port */ + v = priv->r->traffic_get(port); + v &= ~priv->ports[port].pm; + priv->r->traffic_set(port, v); + + priv->ports[port].enable = false; +} + +static int rtl83xx_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + if (e->eee_enabled && !priv->eee_enabled) { + pr_info("Globally enabling EEE\n"); + priv->r->init_eee(priv, true); + } + + priv->r->port_eee_set(priv, port, e->eee_enabled); + + if (e->eee_enabled) + pr_info("Enabled EEE for port %d\n", port); + else + pr_info("Disabled EEE for port %d\n", port); + return 0; +} + +static int rtl83xx_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full; + + priv->r->eee_port_ability(priv, e, port); + + e->eee_enabled = priv->ports[port].eee_enabled; + + e->eee_active = !!(e->advertised & e->lp_advertised); + + return 0; +} + +static int rtl93xx_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + e->supported = SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full + | SUPPORTED_2500baseX_Full; + + priv->r->eee_port_ability(priv, e, port); + + e->eee_enabled = priv->ports[port].eee_enabled; + + e->eee_active = !!(e->advertised & e->lp_advertised); + + return 0; +} + +static int rtl83xx_set_ageing_time(struct dsa_switch *ds, unsigned int msec) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + priv->r->set_ageing_time(msec); + return 0; +} + +static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 port_bitmap = BIT_ULL(priv->cpu_port), v; + int i; + + pr_debug("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap); + + if (priv->is_lagmember[port]) { + pr_debug("%s: %d is lag slave. ignore\n", __func__, port); + return 0; + } + + mutex_lock(&priv->reg_mutex); + for (i = 0; i < ds->num_ports; i++) { + /* Add this port to the port matrix of the other ports in the + * same bridge. If the port is disabled, port matrix is kept + * and not being setup until the port becomes enabled. + */ + if (dsa_is_user_port(ds, i) && !priv->is_lagmember[i] && i != port) { + if (dsa_to_port(ds, i)->bridge_dev != bridge) + continue; + if (priv->ports[i].enable) + priv->r->traffic_enable(i, port); + + priv->ports[i].pm |= BIT_ULL(port); + port_bitmap |= BIT_ULL(i); + } + } + load_mcgroups(priv, port); + + /* Add all other ports to this port matrix. */ + if (priv->ports[port].enable) { + priv->r->traffic_enable(priv->cpu_port, port); + v = priv->r->traffic_get(port); + v |= port_bitmap; + priv->r->traffic_set(port, v); + } + priv->ports[port].pm |= port_bitmap; + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 port_bitmap = BIT_ULL(priv->cpu_port), v; + int i; + + pr_debug("%s %x: %d", __func__, (u32)priv, port); + mutex_lock(&priv->reg_mutex); + for (i = 0; i < ds->num_ports; i++) { + /* Remove this port from the port matrix of the other ports + * in the same bridge. If the port is disabled, port matrix + * is kept and not being setup until the port becomes enabled. + * And the other port's port matrix cannot be broken when the + * other port is still a VLAN-aware port. + */ + if (dsa_is_user_port(ds, i) && i != port) { + if (dsa_to_port(ds, i)->bridge_dev != bridge) + continue; + if (priv->ports[i].enable) + priv->r->traffic_disable(i, port); + + priv->ports[i].pm |= BIT_ULL(port); + port_bitmap &= ~BIT_ULL(i); + } + } + store_mcgroups(priv, port); + + /* Add all other ports to this port matrix. */ + if (priv->ports[port].enable) { + v = priv->r->traffic_get(port); + v |= port_bitmap; + priv->r->traffic_set(port, v); + } + priv->ports[port].pm &= ~port_bitmap; + + mutex_unlock(&priv->reg_mutex); +} + +void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + u32 msti = 0; + u32 port_state[4]; + int index, bit; + int pos = port; + struct rtl838x_switch_priv *priv = ds->priv; + int n = priv->port_width << 1; + + /* Ports above or equal CPU port can never be configured */ + if (port >= priv->cpu_port) + return; + + mutex_lock(&priv->reg_mutex); + + /* For the RTL839x and following, the bits are left-aligned, 838x and 930x + * have 64 bit fields, 839x and 931x have 128 bit fields + */ + if (priv->family_id == RTL8390_FAMILY_ID) + pos += 12; + if (priv->family_id == RTL9300_FAMILY_ID) + pos += 3; + if (priv->family_id == RTL9310_FAMILY_ID) + pos += 8; + + index = n - (pos >> 4) - 1; + bit = (pos << 1) % 32; + + priv->r->stp_get(priv, msti, port_state); + + pr_debug("Current state, port %d: %d\n", port, (port_state[index] >> bit) & 3); + port_state[index] &= ~(3 << bit); + + switch (state) { + case BR_STATE_DISABLED: /* 0 */ + port_state[index] |= (0 << bit); + break; + case BR_STATE_BLOCKING: /* 4 */ + case BR_STATE_LISTENING: /* 1 */ + port_state[index] |= (1 << bit); + break; + case BR_STATE_LEARNING: /* 2 */ + port_state[index] |= (2 << bit); + break; + case BR_STATE_FORWARDING: /* 3*/ + port_state[index] |= (3 << bit); + default: + break; + } + + priv->r->stp_set(priv, msti, port_state); + + mutex_unlock(&priv->reg_mutex); +} + +void rtl83xx_fast_age(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + int s = priv->family_id == RTL8390_FAMILY_ID ? 2 : 0; + + pr_debug("FAST AGE port %d\n", port); + mutex_lock(&priv->reg_mutex); + /* RTL838X_L2_TBL_FLUSH_CTRL register bits, 839x has 1 bit larger + * port fields: + * 0-4: Replacing port + * 5-9: Flushed/replaced port + * 10-21: FVID + * 22: Entry types: 1: dynamic, 0: also static + * 23: Match flush port + * 24: Match FVID + * 25: Flush (0) or replace (1) L2 entries + * 26: Status of action (1: Start, 0: Done) + */ + sw_w32(1 << (26 + s) | 1 << (23 + s) | port << (5 + (s / 2)), priv->r->l2_tbl_flush_ctrl); + + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & BIT(26 + s)); + + mutex_unlock(&priv->reg_mutex); +} + +void rtl931x_fast_age(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_info("%s port %d\n", __func__, port); + mutex_lock(&priv->reg_mutex); + sw_w32(port << 11, RTL931X_L2_TBL_FLUSH_CTRL + 4); + + sw_w32(BIT(24) | BIT(28), RTL931X_L2_TBL_FLUSH_CTRL); + + do { } while (sw_r32(RTL931X_L2_TBL_FLUSH_CTRL) & BIT (28)); + + mutex_unlock(&priv->reg_mutex); +} + +void rtl930x_fast_age(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + if (priv->family_id == RTL9310_FAMILY_ID) + return rtl931x_fast_age(ds, port); + + pr_debug("FAST AGE port %d\n", port); + mutex_lock(&priv->reg_mutex); + sw_w32(port << 11, RTL930X_L2_TBL_FLUSH_CTRL + 4); + + sw_w32(BIT(26) | BIT(30), RTL930X_L2_TBL_FLUSH_CTRL); + + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & BIT(30)); + + mutex_unlock(&priv->reg_mutex); +} + +static int rtl83xx_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct switchdev_trans *trans) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_debug("%s: port %d\n", __func__, port); + mutex_lock(&priv->reg_mutex); + + if (vlan_filtering) { + /* Enable ingress and egress filtering + * The VLAN_PORT_IGR_FILTER register uses 2 bits for each port to define + * the filter action: + * 0: Always Forward + * 1: Drop packet + * 2: Trap packet to CPU port + * The Egress filter used 1 bit per state (0: DISABLED, 1: ENABLED) + */ + if (port != priv->cpu_port) + priv->r->set_vlan_igr_filter(port, IGR_DROP); + + priv->r->set_vlan_egr_filter(port, EGR_ENABLE); + } else { + /* Disable ingress and egress filtering */ + if (port != priv->cpu_port) + priv->r->set_vlan_igr_filter(port, IGR_FORWARD); + + priv->r->set_vlan_egr_filter(port, EGR_DISABLE); + } + + /* Do we need to do something to the CPU-Port, too? */ + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct rtl838x_vlan_info info; + struct rtl838x_switch_priv *priv = ds->priv; + + priv->r->vlan_tables_read(0, &info); + + pr_debug("VLAN 0: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n", + info.tagged_ports, info.untagged_ports, info.profile_id, + info.hash_mc_fid, info.hash_uc_fid, info.fid); + + priv->r->vlan_tables_read(1, &info); + pr_debug("VLAN 1: Tagged ports %llx, untag %llx, profile %d, MC# %d, UC# %d, FID %x\n", + info.tagged_ports, info.untagged_ports, info.profile_id, + info.hash_mc_fid, info.hash_uc_fid, info.fid); + priv->r->vlan_set_untagged(1, info.untagged_ports); + pr_debug("SET: Untagged ports, VLAN %d: %llx\n", 1, info.untagged_ports); + + priv->r->vlan_set_tagged(1, &info); + pr_debug("SET: Tagged ports, VLAN %d: %llx\n", 1, info.tagged_ports); + + return 0; +} + +static void rtl83xx_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct rtl838x_vlan_info info; + struct rtl838x_switch_priv *priv = ds->priv; + int v; + + pr_debug("%s port %d, vid_begin %d, vid_end %d, flags %x\n", __func__, + port, vlan->vid_begin, vlan->vid_end, vlan->flags); + + if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { + dev_err(priv->dev, "VLAN out of range: %d - %d", + vlan->vid_begin, vlan->vid_end); + return; + } + + mutex_lock(&priv->reg_mutex); + + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + if (!v) + continue; + /* Set both inner and outer PVID of the port */ + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, v); + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, v); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER, + PBVLAN_MODE_UNTAG_AND_PRITAG); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER, + PBVLAN_MODE_UNTAG_AND_PRITAG); + + priv->ports[port].pvid = vlan->vid_end; + } + } + + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + /* Get port memberships of this vlan */ + priv->r->vlan_tables_read(v, &info); + + /* new VLAN? */ + if (!info.tagged_ports) { + info.fid = 0; + info.hash_mc_fid = false; + info.hash_uc_fid = false; + info.profile_id = 0; + } + + /* sanitize untagged_ports - must be a subset */ + if (info.untagged_ports & ~info.tagged_ports) + info.untagged_ports = 0; + + info.tagged_ports |= BIT_ULL(port); + if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED) + info.untagged_ports |= BIT_ULL(port); + + priv->r->vlan_set_untagged(v, info.untagged_ports); + pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); + + priv->r->vlan_set_tagged(v, &info); + pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + } + + mutex_unlock(&priv->reg_mutex); +} + +static int rtl83xx_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct rtl838x_vlan_info info; + struct rtl838x_switch_priv *priv = ds->priv; + int v; + u16 pvid; + + pr_debug("%s: port %d, vid_begin %d, vid_end %d, flags %x\n", __func__, + port, vlan->vid_begin, vlan->vid_end, vlan->flags); + + if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { + dev_err(priv->dev, "VLAN out of range: %d - %d", + vlan->vid_begin, vlan->vid_end); + return -ENOTSUPP; + } + + mutex_lock(&priv->reg_mutex); + pvid = priv->ports[port].pvid; + + for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { + /* Reset to default if removing the current PVID */ + if (v == pvid) { + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, 0); + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, 0); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER, + PBVLAN_MODE_UNTAG_AND_PRITAG); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER, + PBVLAN_MODE_UNTAG_AND_PRITAG); + } + /* Get port memberships of this vlan */ + priv->r->vlan_tables_read(v, &info); + + /* remove port from both tables */ + info.untagged_ports &= (~BIT_ULL(port)); + info.tagged_ports &= (~BIT_ULL(port)); + + priv->r->vlan_set_untagged(v, info.untagged_ports); + pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); + + priv->r->vlan_set_tagged(v, &info); + pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + } + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static void rtl83xx_setup_l2_uc_entry(struct rtl838x_l2_entry *e, int port, int vid, u64 mac) +{ + memset(e, 0, sizeof(*e)); + + e->type = L2_UNICAST; + e->valid = true; + + e->age = 3; + e->is_static = true; + + e->port = port; + + e->rvid = e->vid = vid; + e->is_ip_mc = e->is_ipv6_mc = false; + u64_to_ether_addr(mac, e->mac); +} + +static void rtl83xx_setup_l2_mc_entry(struct rtl838x_l2_entry *e, int vid, u64 mac, int mc_group) +{ + memset(e, 0, sizeof(*e)); + + e->type = L2_MULTICAST; + e->valid = true; + + e->mc_portmask_index = mc_group; + + e->rvid = e->vid = vid; + e->is_ip_mc = e->is_ipv6_mc = false; + u64_to_ether_addr(mac, e->mac); +} + +/* + * Uses the seed to identify a hash bucket in the L2 using the derived hash key and then loops + * over the entries in the bucket until either a matching entry is found or an empty slot + * Returns the filled in rtl838x_l2_entry and the index in the bucket when an entry was found + * when an empty slot was found and must exist is false, the index of the slot is returned + * when no slots are available returns -1 + */ +static int rtl83xx_find_l2_hash_entry(struct rtl838x_switch_priv *priv, u64 seed, + bool must_exist, struct rtl838x_l2_entry *e) +{ + int i, idx = -1; + u32 key = priv->r->l2_hash_key(priv, seed); + u64 entry; + + pr_debug("%s: using key %x, for seed %016llx\n", __func__, key, seed); + // Loop over all entries in the hash-bucket and over the second block on 93xx SoCs + for (i = 0; i < priv->l2_bucket_size; i++) { + entry = priv->r->read_l2_entry_using_hash(key, i, e); + pr_debug("valid %d, mac %016llx\n", e->valid, ether_addr_to_u64(&e->mac[0])); + if (must_exist && !e->valid) + continue; + if (!e->valid || ((entry & 0x0fffffffffffffffULL) == seed)) { + idx = i > 3 ? ((key >> 14) & 0xffff) | i >> 1 : ((key << 2) | i) & 0xffff; + break; + } + } + + return idx; +} + +/* + * Uses the seed to identify an entry in the CAM by looping over all its entries + * Returns the filled in rtl838x_l2_entry and the index in the CAM when an entry was found + * when an empty slot was found the index of the slot is returned + * when no slots are available returns -1 + */ +static int rtl83xx_find_l2_cam_entry(struct rtl838x_switch_priv *priv, u64 seed, + bool must_exist, struct rtl838x_l2_entry *e) +{ + int i, idx = -1; + u64 entry; + + for (i = 0; i < 64; i++) { + entry = priv->r->read_cam(i, e); + if (!must_exist && !e->valid) { + if (idx < 0) /* First empty entry? */ + idx = i; + break; + } else if ((entry & 0x0fffffffffffffffULL) == seed) { + pr_debug("Found entry in CAM\n"); + idx = i; + break; + } + } + return idx; +} + +static int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(addr); + struct rtl838x_l2_entry e; + int err = 0, idx; + u64 seed = priv->r->l2_hash_seed(mac, vid); + + if (priv->is_lagmember[port]) { + pr_debug("%s: %d is lag slave. ignore\n", __func__, port); + return 0; + } + + mutex_lock(&priv->reg_mutex); + + idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e); + + // Found an existing or empty entry + if (idx >= 0) { + rtl83xx_setup_l2_uc_entry(&e, port, vid, mac); + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + goto out; + } + + // Hash buckets full, try CAM + rtl83xx_find_l2_cam_entry(priv, seed, false, &e); + + if (idx >= 0) { + rtl83xx_setup_l2_uc_entry(&e, port, vid, mac); + priv->r->write_cam(idx, &e); + goto out; + } + + err = -ENOTSUPP; +out: + mutex_unlock(&priv->reg_mutex); + return err; +} + +static int rtl83xx_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(addr); + struct rtl838x_l2_entry e; + int err = 0, idx; + u64 seed = priv->r->l2_hash_seed(mac, vid); + + pr_debug("In %s, mac %llx, vid: %d\n", __func__, mac, vid); + mutex_lock(&priv->reg_mutex); + + idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e); + + if (idx >= 0) { + pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3); + e.valid = false; + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + goto out; + } + + /* Check CAM for spillover from hash buckets */ + rtl83xx_find_l2_cam_entry(priv, seed, true, &e); + + if (idx >= 0) { + e.valid = false; + priv->r->write_cam(idx, &e); + goto out; + } + err = -ENOENT; +out: + mutex_unlock(&priv->reg_mutex); + return err; +} + +static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct rtl838x_l2_entry e; + struct rtl838x_switch_priv *priv = ds->priv; + int i; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < priv->fib_entries; i++) { + priv->r->read_l2_entry_using_hash(i >> 2, i & 0x3, &e); + + if (!e.valid) + continue; + + if (e.port == port || e.port == RTL930X_PORT_IGNORE) + cb(e.mac, e.vid, e.is_static, data); + } + + for (i = 0; i < 64; i++) { + priv->r->read_cam(i, &e); + + if (!e.valid) + continue; + + if (e.port == port) + cb(e.mac, e.vid, e.is_static, data); + } + + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static int rtl83xx_port_mdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + if (priv->id >= 0x9300) + return -EOPNOTSUPP; + + return 0; +} + +static void rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(mdb->addr); + struct rtl838x_l2_entry e; + int err = 0, idx; + int vid = mdb->vid; + u64 seed = priv->r->l2_hash_seed(mac, vid); + int mc_group; + + pr_debug("In %s port %d, mac %llx, vid: %d\n", __func__, port, mac, vid); + + if (priv->is_lagmember[port]) { + pr_debug("%s: %d is lag slave. ignore\n", __func__, port); + return; + } + + mutex_lock(&priv->reg_mutex); + + idx = rtl83xx_find_l2_hash_entry(priv, seed, false, &e); + + // Found an existing or empty entry + if (idx >= 0) { + if (e.valid) { + pr_debug("Found an existing entry %016llx, mc_group %d\n", + ether_addr_to_u64(e.mac), e.mc_portmask_index); + rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port); + } else { + pr_debug("New entry for seed %016llx\n", seed); + mc_group = rtl83xx_mc_group_alloc(priv, port); + if (mc_group < 0) { + err = -ENOTSUPP; + goto out; + } + rtl83xx_setup_l2_mc_entry(&e, vid, mac, mc_group); + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + } + goto out; + } + + // Hash buckets full, try CAM + rtl83xx_find_l2_cam_entry(priv, seed, false, &e); + + if (idx >= 0) { + if (e.valid) { + pr_debug("Found existing CAM entry %016llx, mc_group %d\n", + ether_addr_to_u64(e.mac), e.mc_portmask_index); + rtl83xx_mc_group_add_port(priv, e.mc_portmask_index, port); + } else { + pr_debug("New entry\n"); + mc_group = rtl83xx_mc_group_alloc(priv, port); + if (mc_group < 0) { + err = -ENOTSUPP; + goto out; + } + rtl83xx_setup_l2_mc_entry(&e, vid, mac, mc_group); + priv->r->write_cam(idx, &e); + } + goto out; + } + + err = -ENOTSUPP; +out: + mutex_unlock(&priv->reg_mutex); + if (err) + dev_err(ds->dev, "failed to add MDB entry\n"); +} + +int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct rtl838x_switch_priv *priv = ds->priv; + u64 mac = ether_addr_to_u64(mdb->addr); + struct rtl838x_l2_entry e; + int err = 0, idx; + int vid = mdb->vid; + u64 seed = priv->r->l2_hash_seed(mac, vid); + u64 portmask; + + pr_debug("In %s, port %d, mac %llx, vid: %d\n", __func__, port, mac, vid); + + if (priv->is_lagmember[port]) { + pr_info("%s: %d is lag slave. ignore\n", __func__, port); + return 0; + } + + mutex_lock(&priv->reg_mutex); + + idx = rtl83xx_find_l2_hash_entry(priv, seed, true, &e); + + if (idx >= 0) { + pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3); + portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port); + if (!portmask) { + e.valid = false; + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); + } + goto out; + } + + /* Check CAM for spillover from hash buckets */ + rtl83xx_find_l2_cam_entry(priv, seed, true, &e); + + if (idx >= 0) { + portmask = rtl83xx_mc_group_del_port(priv, e.mc_portmask_index, port); + if (!portmask) { + e.valid = false; + priv->r->write_cam(idx, &e); + } + goto out; + } + // TODO: Re-enable with a newer kernel: err = -ENOENT; +out: + mutex_unlock(&priv->reg_mutex); + return err; +} + +static int rtl83xx_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress) +{ + /* We support 4 mirror groups, one destination port per group */ + int group; + struct rtl838x_switch_priv *priv = ds->priv; + int ctrl_reg, dpm_reg, spm_reg; + + pr_debug("In %s\n", __func__); + + for (group = 0; group < 4; group++) { + if (priv->mirror_group_ports[group] == mirror->to_local_port) + break; + } + if (group >= 4) { + for (group = 0; group < 4; group++) { + if (priv->mirror_group_ports[group] < 0) + break; + } + } + + if (group >= 4) + return -ENOSPC; + + ctrl_reg = priv->r->mir_ctrl + group * 4; + dpm_reg = priv->r->mir_dpm + group * 4 * priv->port_width; + spm_reg = priv->r->mir_spm + group * 4 * priv->port_width; + + pr_debug("Using group %d\n", group); + mutex_lock(&priv->reg_mutex); + + if (priv->family_id == RTL8380_FAMILY_ID) { + /* Enable mirroring to port across VLANs (bit 11) */ + sw_w32(1 << 11 | (mirror->to_local_port << 4) | 1, ctrl_reg); + } else { + /* Enable mirroring to destination port */ + sw_w32((mirror->to_local_port << 4) | 1, ctrl_reg); + } + + if (ingress && (priv->r->get_port_reg_be(spm_reg) & (1ULL << port))) { + mutex_unlock(&priv->reg_mutex); + return -EEXIST; + } + if ((!ingress) && (priv->r->get_port_reg_be(dpm_reg) & (1ULL << port))) { + mutex_unlock(&priv->reg_mutex); + return -EEXIST; + } + + if (ingress) + priv->r->mask_port_reg_be(0, 1ULL << port, spm_reg); + else + priv->r->mask_port_reg_be(0, 1ULL << port, dpm_reg); + + priv->mirror_group_ports[group] = mirror->to_local_port; + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static void rtl83xx_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + int group = 0; + struct rtl838x_switch_priv *priv = ds->priv; + int ctrl_reg, dpm_reg, spm_reg; + + pr_debug("In %s\n", __func__); + for (group = 0; group < 4; group++) { + if (priv->mirror_group_ports[group] == mirror->to_local_port) + break; + } + if (group >= 4) + return; + + ctrl_reg = priv->r->mir_ctrl + group * 4; + dpm_reg = priv->r->mir_dpm + group * 4 * priv->port_width; + spm_reg = priv->r->mir_spm + group * 4 * priv->port_width; + + mutex_lock(&priv->reg_mutex); + if (mirror->ingress) { + /* Ingress, clear source port matrix */ + priv->r->mask_port_reg_be(1ULL << port, 0, spm_reg); + } else { + /* Egress, clear destination port matrix */ + priv->r->mask_port_reg_be(1ULL << port, 0, dpm_reg); + } + + if (!(sw_r32(spm_reg) || sw_r32(dpm_reg))) { + priv->mirror_group_ports[group] = -1; + sw_w32(0, ctrl_reg); + } + + mutex_unlock(&priv->reg_mutex); +} + +static int rtl83xx_port_pre_bridge_flags(struct dsa_switch *ds, int port, unsigned long flags, struct netlink_ext_ack *extack) +{ + struct rtl838x_switch_priv *priv = ds->priv; + unsigned long features = 0; + pr_debug("%s: %d %lX\n", __func__, port, flags); + if (priv->r->enable_learning) + features |= BR_LEARNING; + if (priv->r->enable_flood) + features |= BR_FLOOD; + if (priv->r->enable_mcast_flood) + features |= BR_MCAST_FLOOD; + if (priv->r->enable_bcast_flood) + features |= BR_BCAST_FLOOD; + if (flags & ~(features)) + return -EINVAL; + + return 0; +} + +static int rtl83xx_port_bridge_flags(struct dsa_switch *ds, int port, unsigned long flags, struct netlink_ext_ack *extack) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_debug("%s: %d %lX\n", __func__, port, flags); + if (priv->r->enable_learning) + priv->r->enable_learning(port, !!(flags & BR_LEARNING)); + + if (priv->r->enable_flood) + priv->r->enable_flood(port, !!(flags & BR_FLOOD)); + + if (priv->r->enable_mcast_flood) + priv->r->enable_mcast_flood(port, !!(flags & BR_MCAST_FLOOD)); + + if (priv->r->enable_bcast_flood) + priv->r->enable_bcast_flood(port, !!(flags & BR_BCAST_FLOOD)); + + return 0; +} + +static bool rtl83xx_lag_can_offload(struct dsa_switch *ds, + struct net_device *lag, + struct netdev_lag_upper_info *info) +{ + int id; + + id = dsa_lag_id(ds->dst, lag); + if (id < 0 || id >= ds->num_lag_ids) + return false; + + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + return false; + } + if (info->hash_type != NETDEV_LAG_HASH_L2 && info->hash_type != NETDEV_LAG_HASH_L23) + return false; + + return true; +} + +static int rtl83xx_port_lag_change(struct dsa_switch *ds, int port) +{ + struct rtl838x_switch_priv *priv = ds->priv; + + pr_debug("%s: %d\n", __func__, port); + // Nothing to be done... + + return 0; +} + +static int rtl83xx_port_lag_join(struct dsa_switch *ds, int port, + struct net_device *lag, + struct netdev_lag_upper_info *info) +{ + struct rtl838x_switch_priv *priv = ds->priv; + int i, err = 0; + + if (!rtl83xx_lag_can_offload(ds, lag, info)) + return -EOPNOTSUPP; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < priv->n_lags; i++) { + if ((!priv->lag_devs[i]) || (priv->lag_devs[i] == lag)) + break; + } + if (port >= priv->cpu_port) { + err = -EINVAL; + goto out; + } + pr_info("port_lag_join: group %d, port %d\n",i, port); + if (!priv->lag_devs[i]) + priv->lag_devs[i] = lag; + + if (priv->lag_primary[i]==-1) { + priv->lag_primary[i]=port; + } else + priv->is_lagmember[port] = 1; + + priv->lagmembers |= (1ULL << port); + + pr_debug("lag_members = %llX\n", priv->lagmembers); + err = rtl83xx_lag_add(priv->ds, i, port, info); + if (err) { + err = -EINVAL; + goto out; + } + +out: + mutex_unlock(&priv->reg_mutex); + return err; + +} + +static int rtl83xx_port_lag_leave(struct dsa_switch *ds, int port, + struct net_device *lag) +{ + int i, group = -1, err; + struct rtl838x_switch_priv *priv = ds->priv; + + mutex_lock(&priv->reg_mutex); + for (i=0;in_lags;i++) { + if (priv->lags_port_members[i] & BIT_ULL(port)) { + group = i; + break; + } + } + + if (group == -1) { + pr_info("port_lag_leave: port %d is not a member\n", port); + err = -EINVAL; + goto out; + } + + if (port >= priv->cpu_port) { + err = -EINVAL; + goto out; + } + pr_info("port_lag_del: group %d, port %d\n",group, port); + priv->lagmembers &=~ (1ULL << port); + priv->lag_primary[i] = -1; + priv->is_lagmember[port] = 0; + pr_debug("lag_members = %llX\n", priv->lagmembers); + err = rtl83xx_lag_del(priv->ds, group, port); + if (err) { + err = -EINVAL; + goto out; + } + if (!priv->lags_port_members[i]) + priv->lag_devs[i] = NULL; + +out: + mutex_unlock(&priv->reg_mutex); + return 0; +} + +int dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg) +{ + u32 val; + u32 offset = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + if (phy_addr >= 24 && phy_addr <= 27 + && priv->ports[24].phy == PHY_RTL838X_SDS) { + if (phy_addr == 26) + offset = 0x100; + val = sw_r32(RTL838X_SDS4_FIB_REG0 + offset + (phy_reg << 2)) & 0xffff; + return val; + } + + read_phy(phy_addr, 0, phy_reg, &val); + return val; +} + +int dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val) +{ + u32 offset = 0; + struct rtl838x_switch_priv *priv = ds->priv; + + if (phy_addr >= 24 && phy_addr <= 27 + && priv->ports[24].phy == PHY_RTL838X_SDS) { + if (phy_addr == 26) + offset = 0x100; + sw_w32(val, RTL838X_SDS4_FIB_REG0 + offset + (phy_reg << 2)); + return 0; + } + return write_phy(phy_addr, 0, phy_reg, val); +} + +const struct dsa_switch_ops rtl83xx_switch_ops = { + .get_tag_protocol = rtl83xx_get_tag_protocol, + .setup = rtl83xx_setup, + + .phy_read = dsa_phy_read, + .phy_write = dsa_phy_write, + + .phylink_validate = rtl83xx_phylink_validate, + .phylink_mac_link_state = rtl83xx_phylink_mac_link_state, + .phylink_mac_config = rtl83xx_phylink_mac_config, + .phylink_mac_link_down = rtl83xx_phylink_mac_link_down, + .phylink_mac_link_up = rtl83xx_phylink_mac_link_up, + + .get_strings = rtl83xx_get_strings, + .get_ethtool_stats = rtl83xx_get_ethtool_stats, + .get_sset_count = rtl83xx_get_sset_count, + + .port_enable = rtl83xx_port_enable, + .port_disable = rtl83xx_port_disable, + + .get_mac_eee = rtl83xx_get_mac_eee, + .set_mac_eee = rtl83xx_set_mac_eee, + + .set_ageing_time = rtl83xx_set_ageing_time, + .port_bridge_join = rtl83xx_port_bridge_join, + .port_bridge_leave = rtl83xx_port_bridge_leave, + .port_stp_state_set = rtl83xx_port_stp_state_set, + .port_fast_age = rtl83xx_fast_age, + + .port_vlan_filtering = rtl83xx_vlan_filtering, + .port_vlan_prepare = rtl83xx_vlan_prepare, + .port_vlan_add = rtl83xx_vlan_add, + .port_vlan_del = rtl83xx_vlan_del, + + .port_fdb_add = rtl83xx_port_fdb_add, + .port_fdb_del = rtl83xx_port_fdb_del, + .port_fdb_dump = rtl83xx_port_fdb_dump, + + .port_mdb_prepare = rtl83xx_port_mdb_prepare, + .port_mdb_add = rtl83xx_port_mdb_add, + .port_mdb_del = rtl83xx_port_mdb_del, + + .port_mirror_add = rtl83xx_port_mirror_add, + .port_mirror_del = rtl83xx_port_mirror_del, + + .port_lag_change = rtl83xx_port_lag_change, + .port_lag_join = rtl83xx_port_lag_join, + .port_lag_leave = rtl83xx_port_lag_leave, + + .port_pre_bridge_flags = rtl83xx_port_pre_bridge_flags, + .port_bridge_flags = rtl83xx_port_bridge_flags, +}; + +const struct dsa_switch_ops rtl930x_switch_ops = { + .get_tag_protocol = rtl83xx_get_tag_protocol, + .setup = rtl93xx_setup, + + .phy_read = dsa_phy_read, + .phy_write = dsa_phy_write, + + .phylink_validate = rtl93xx_phylink_validate, + .phylink_mac_link_state = rtl93xx_phylink_mac_link_state, + .phylink_mac_config = rtl93xx_phylink_mac_config, + .phylink_mac_link_down = rtl93xx_phylink_mac_link_down, + .phylink_mac_link_up = rtl93xx_phylink_mac_link_up, + + .get_strings = rtl83xx_get_strings, + .get_ethtool_stats = rtl83xx_get_ethtool_stats, + .get_sset_count = rtl83xx_get_sset_count, + + .port_enable = rtl83xx_port_enable, + .port_disable = rtl83xx_port_disable, + + .get_mac_eee = rtl93xx_get_mac_eee, + .set_mac_eee = rtl83xx_set_mac_eee, + + .set_ageing_time = rtl83xx_set_ageing_time, + .port_bridge_join = rtl83xx_port_bridge_join, + .port_bridge_leave = rtl83xx_port_bridge_leave, + .port_stp_state_set = rtl83xx_port_stp_state_set, + .port_fast_age = rtl930x_fast_age, + + .port_vlan_filtering = rtl83xx_vlan_filtering, + .port_vlan_prepare = rtl83xx_vlan_prepare, + .port_vlan_add = rtl83xx_vlan_add, + .port_vlan_del = rtl83xx_vlan_del, + + .port_fdb_add = rtl83xx_port_fdb_add, + .port_fdb_del = rtl83xx_port_fdb_del, + .port_fdb_dump = rtl83xx_port_fdb_dump, + + .port_mdb_prepare = rtl83xx_port_mdb_prepare, + .port_mdb_add = rtl83xx_port_mdb_add, + .port_mdb_del = rtl83xx_port_mdb_del, + + .port_lag_change = rtl83xx_port_lag_change, + .port_lag_join = rtl83xx_port_lag_join, + .port_lag_leave = rtl83xx_port_lag_leave, + + .port_pre_bridge_flags = rtl83xx_port_pre_bridge_flags, + .port_bridge_flags = rtl83xx_port_bridge_flags, +}; diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/qos.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/qos.c new file mode 100644 index 00000000000..2fc8d37f3e4 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/qos.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include +#include "rtl83xx.h" + +static struct rtl838x_switch_priv *switch_priv; +extern struct rtl83xx_soc_info soc_info; + +enum scheduler_type { + WEIGHTED_FAIR_QUEUE = 0, + WEIGHTED_ROUND_ROBIN, +}; + +int max_available_queue[] = {0, 1, 2, 3, 4, 5, 6, 7}; +int default_queue_weights[] = {1, 1, 1, 1, 1, 1, 1, 1}; +int dot1p_priority_remapping[] = {0, 1, 2, 3, 4, 5, 6, 7}; + +static void rtl839x_read_scheduling_table(int port) +{ + u32 cmd = 1 << 9 /* Execute cmd */ + | 0 << 8 /* Read */ + | 0 << 6 /* Table type 0b00 */ + | (port & 0x3f); + rtl839x_exec_tbl2_cmd(cmd); +} + +static void rtl839x_write_scheduling_table(int port) +{ + u32 cmd = 1 << 9 /* Execute cmd */ + | 1 << 8 /* Write */ + | 0 << 6 /* Table type 0b00 */ + | (port & 0x3f); + rtl839x_exec_tbl2_cmd(cmd); +} + +static void rtl839x_read_out_q_table(int port) +{ + u32 cmd = 1 << 9 /* Execute cmd */ + | 0 << 8 /* Read */ + | 2 << 6 /* Table type 0b10 */ + | (port & 0x3f); + rtl839x_exec_tbl2_cmd(cmd); +} + +static void rtl838x_storm_enable(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + // Enable Storm control for that port for UC, MC, and BC + if (enable) + sw_w32(0x7, RTL838X_STORM_CTRL_LB_CTRL(port)); + else + sw_w32(0x0, RTL838X_STORM_CTRL_LB_CTRL(port)); +} + +u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port) +{ + u32 rate; + + if (port > priv->cpu_port) + return 0; + rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port)) & 0x3fff; + return rate; +} + +/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */ +int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate) +{ + u32 old_rate; + + if (port > priv->cpu_port) + return -1; + + old_rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port)); + sw_w32(rate, RTL838X_SCHED_P_EGR_RATE_CTRL(port)); + + return old_rate; +} + +/* Set the rate limit for a particular queue in Bits/s + * units of the rate is 16Kbps + */ +void rtl838x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port, + int queue, u32 rate) +{ + if (port > priv->cpu_port) + return; + if (queue > 7) + return; + sw_w32(rate, RTL838X_SCHED_Q_EGR_RATE_CTRL(port, queue)); +} + +static void rtl838x_rate_control_init(struct rtl838x_switch_priv *priv) +{ + int i; + + pr_info("Enabling Storm control\n"); + // TICK_PERIOD_PPS + if (priv->id == 0x8380) + sw_w32_mask(0x3ff << 20, 434 << 20, RTL838X_SCHED_LB_TICK_TKN_CTRL_0); + + // Set burst rate + sw_w32(0x00008000, RTL838X_STORM_CTRL_BURST_0); // UC + sw_w32(0x80008000, RTL838X_STORM_CTRL_BURST_1); // MC and BC + + // Set burst Packets per Second to 32 + sw_w32(0x00000020, RTL838X_STORM_CTRL_BURST_PPS_0); // UC + sw_w32(0x00200020, RTL838X_STORM_CTRL_BURST_PPS_1); // MC and BC + + // Include IFG in storm control, rate based on bytes/s (0 = packets) + sw_w32_mask(0, 1 << 6 | 1 << 5, RTL838X_STORM_CTRL); + // Bandwidth control includes preamble and IFG (10 Bytes) + sw_w32_mask(0, 1, RTL838X_SCHED_CTRL); + + // On SoCs except RTL8382M, set burst size of port egress + if (priv->id != 0x8382) + sw_w32_mask(0xffff, 0x800, RTL838X_SCHED_LB_THR); + + /* Enable storm control on all ports with a PHY and limit rates, + * for UC and MC for both known and unknown addresses */ + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) { + sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_UC(i)); + sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_MC(i)); + sw_w32(0x8000, RTL838X_STORM_CTRL_PORT_BC(i)); + rtl838x_storm_enable(priv, i, true); + } + } + + // Attack prevention, enable all attack prevention measures + //sw_w32(0x1ffff, RTL838X_ATK_PRVNT_CTRL); + /* Attack prevention, drop (bit = 0) problematic packets on all ports. + * Setting bit = 1 means: trap to CPU + */ + //sw_w32(0, RTL838X_ATK_PRVNT_ACT); + // Enable attack prevention on all ports + //sw_w32(0x0fffffff, RTL838X_ATK_PRVNT_PORT_EN); +} + +/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */ +u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port) +{ + u32 rate; + + pr_debug("%s: Getting egress rate on port %d to %d\n", __func__, port, rate); + if (port >= priv->cpu_port) + return 0; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7)); + rate <<= 12; + rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20; + + mutex_unlock(&priv->reg_mutex); + + return rate; +} + +/* Sets the rate limit, 10MBit/s is equal to a rate value of 625, returns previous rate */ +int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate) +{ + u32 old_rate; + + pr_debug("%s: Setting egress rate on port %d to %d\n", __func__, port, rate); + if (port >= priv->cpu_port) + return -1; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + old_rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7)) & 0xff; + old_rate <<= 12; + old_rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20; + sw_w32_mask(0xff, (rate >> 12) & 0xff, RTL839X_TBL_ACCESS_DATA_2(7)); + sw_w32_mask(0xfff << 20, rate << 20, RTL839X_TBL_ACCESS_DATA_2(8)); + + rtl839x_write_scheduling_table(port); + + mutex_unlock(&priv->reg_mutex); + + return old_rate; +} + +/* Set the rate limit for a particular queue in Bits/s + * units of the rate is 16Kbps + */ +void rtl839x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port, + int queue, u32 rate) +{ + int lsb = 128 + queue * 20; + int low_byte = 8 - (lsb >> 5); + int start_bit = lsb - (low_byte << 5); + u32 high_mask = 0xfffff >> (32 - start_bit); + + pr_debug("%s: Setting egress rate on port %d, queue %d to %d\n", + __func__, port, queue, rate); + if (port >= priv->cpu_port) + return; + if (queue > 7) + return; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + sw_w32_mask(0xfffff << start_bit, (rate & 0xfffff) << start_bit, + RTL839X_TBL_ACCESS_DATA_2(low_byte)); + if (high_mask) + sw_w32_mask(high_mask, (rate & 0xfffff) >> (32- start_bit), + RTL839X_TBL_ACCESS_DATA_2(low_byte - 1)); + + rtl839x_write_scheduling_table(port); + + mutex_unlock(&priv->reg_mutex); +} + +static void rtl839x_rate_control_init(struct rtl838x_switch_priv *priv) +{ + int p, q; + + pr_info("%s: enabling rate control\n", __func__); + /* Tick length and token size settings for SoC with 250MHz, + * RTL8350 family would use 50MHz + */ + // Set the special tick period + sw_w32(976563, RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL); + // Ingress tick period and token length 10G + sw_w32(18 << 11 | 151, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0); + // Ingress tick period and token length 1G + sw_w32(245 << 11 | 129, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1); + // Egress tick period 10G, bytes/token 10G and tick period 1G, bytes/token 1G + sw_w32(18 << 24 | 151 << 16 | 185 << 8 | 97, RTL839X_SCHED_LB_TICK_TKN_CTRL); + // Set the tick period of the CPU and the Token Len + sw_w32(3815 << 8 | 1, RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL); + + // Set the Weighted Fair Queueing burst size + sw_w32_mask(0xffff, 4500, RTL839X_SCHED_LB_THR); + + // Storm-rate calculation is based on bytes/sec (bit 5), include IFG (bit 6) + sw_w32_mask(0, 1 << 5 | 1 << 6, RTL839X_STORM_CTRL); + + /* Based on the rate control mode being bytes/s + * set tick period and token length for 10G + */ + sw_w32(18 << 10 | 151, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0); + /* and for 1G ports */ + sw_w32(246 << 10 | 129, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1); + + /* Set default burst rates on all ports (the same for 1G / 10G) with a PHY + * for UC, MC and BC + * For 1G port, the minimum burst rate is 1700, maximum 65535, + * For 10G ports it is 2650 and 1048575 respectively */ + for (p = 0; p < priv->cpu_port; p++) { + if (priv->ports[p].phy && !priv->ports[p].is10G) { + sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_UC_1(p)); + sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_MC_1(p)); + sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_BC_1(p)); + } + } + + /* Setup ingress/egress per-port rate control */ + for (p = 0; p < priv->cpu_port; p++) { + if (!priv->ports[p].phy) + continue; + + if (priv->ports[p].is10G) + rtl839x_set_egress_rate(priv, p, 625000); // 10GB/s + else + rtl839x_set_egress_rate(priv, p, 62500); // 1GB/s + + // Setup queues: all RTL83XX SoCs have 8 queues, maximum rate + for (q = 0; q < 8; q++) + rtl839x_egress_rate_queue_limit(priv, p, q, 0xfffff); + + if (priv->ports[p].is10G) { + // Set high threshold to maximum + sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p)); + } else { + // Set high threshold to maximum + sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_1(p)); + } + } + + // Set global ingress low watermark rate + sw_w32(65532, RTL839X_IGR_BWCTRL_CTRL_LB_THR); +} + + + +void rtl838x_setup_prio2queue_matrix(int *min_queues) +{ + int i; + u32 v; + + pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL838X_QM_INTPRI2QID_CTRL)); + for (i = 0; i < MAX_PRIOS; i++) + v |= i << (min_queues[i] * 3); + sw_w32(v, RTL838X_QM_INTPRI2QID_CTRL); +} + +void rtl839x_setup_prio2queue_matrix(int *min_queues) +{ + int i, q; + + pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL839X_QM_INTPRI2QID_CTRL(0))); + for (i = 0; i < MAX_PRIOS; i++) { + q = min_queues[i]; + sw_w32(i << (q * 3), RTL839X_QM_INTPRI2QID_CTRL(q)); + } +} + +/* Sets the CPU queue depending on the internal priority of a packet */ +void rtl83xx_setup_prio2queue_cpu_matrix(int *max_queues) +{ + int reg = soc_info.family == RTL8380_FAMILY_ID ? RTL838X_QM_PKT2CPU_INTPRI_MAP + : RTL839X_QM_PKT2CPU_INTPRI_MAP; + int i; + u32 v; + + pr_info("QM_PKT2CPU_INTPRI_MAP: %08x\n", sw_r32(reg)); + for (i = 0; i < MAX_PRIOS; i++) + v |= max_queues[i] << (i * 3); + sw_w32(v, reg); +} + +void rtl83xx_setup_default_prio2queue(void) +{ + if (soc_info.family == RTL8380_FAMILY_ID) { + rtl838x_setup_prio2queue_matrix(max_available_queue); + } else { + rtl839x_setup_prio2queue_matrix(max_available_queue); + } + rtl83xx_setup_prio2queue_cpu_matrix(max_available_queue); +} + +/* Sets the output queue assigned to a port, the port can be the CPU-port */ +void rtl839x_set_egress_queue(int port, int queue) +{ + sw_w32(queue << ((port % 10) *3), RTL839X_QM_PORT_QNUM(port)); +} + +/* Sets the priority assigned of an ingress port, the port can be the CPU-port */ +void rtl83xx_set_ingress_priority(int port, int priority) +{ + if (soc_info.family == RTL8380_FAMILY_ID) + sw_w32(priority << ((port % 10) *3), RTL838X_PRI_SEL_PORT_PRI(port)); + else + sw_w32(priority << ((port % 10) *3), RTL839X_PRI_SEL_PORT_PRI(port)); + +} + +int rtl839x_get_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port) +{ + u32 v; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + v = sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)); + + mutex_unlock(&priv->reg_mutex); + + if (v & BIT(19)) + return WEIGHTED_ROUND_ROBIN; + return WEIGHTED_FAIR_QUEUE; +} + +void rtl839x_set_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port, + enum scheduler_type sched) +{ + enum scheduler_type t = rtl839x_get_scheduling_algorithm(priv, port); + u32 v, oam_state, oam_port_state; + u32 count; + int i, egress_rate; + + mutex_lock(&priv->reg_mutex); + /* Check whether we need to empty the egress queue of that port due to Errata E0014503 */ + if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) { + // Read Operations, Adminstatrion and Management control register + oam_state = sw_r32(RTL839X_OAM_CTRL); + + // Get current OAM state + oam_port_state = sw_r32(RTL839X_OAM_PORT_ACT_CTRL(port)); + + // Disable OAM to block traffice + v = sw_r32(RTL839X_OAM_CTRL); + sw_w32_mask(0, 1, RTL839X_OAM_CTRL); + v = sw_r32(RTL839X_OAM_CTRL); + + // Set to trap action OAM forward (bits 1, 2) and OAM Mux Action Drop (bit 0) + sw_w32(0x2, RTL839X_OAM_PORT_ACT_CTRL(port)); + + // Set port egress rate to unlimited + egress_rate = rtl839x_set_egress_rate(priv, port, 0xFFFFF); + + // Wait until the egress used page count of that port is 0 + i = 0; + do { + usleep_range(100, 200); + rtl839x_read_out_q_table(port); + count = sw_r32(RTL839X_TBL_ACCESS_DATA_2(6)); + count >>= 20; + i++; + } while (i < 3500 && count > 0); + } + + // Actually set the scheduling algorithm + rtl839x_read_scheduling_table(port); + sw_w32_mask(BIT(19), sched ? BIT(19) : 0, RTL839X_TBL_ACCESS_DATA_2(8)); + rtl839x_write_scheduling_table(port); + + if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) { + // Restore OAM state to control register + sw_w32(oam_state, RTL839X_OAM_CTRL); + + // Restore trap action state + sw_w32(oam_port_state, RTL839X_OAM_PORT_ACT_CTRL(port)); + + // Restore port egress rate + rtl839x_set_egress_rate(priv, port, egress_rate); + } + + mutex_unlock(&priv->reg_mutex); +} + +void rtl839x_set_scheduling_queue_weights(struct rtl838x_switch_priv *priv, int port, + int *queue_weights) +{ + int i, lsb, low_byte, start_bit, high_mask; + + mutex_lock(&priv->reg_mutex); + + rtl839x_read_scheduling_table(port); + + for (i = 0; i < 8; i++) { + lsb = 48 + i * 8; + low_byte = 8 - (lsb >> 5); + start_bit = lsb - (low_byte << 5); + high_mask = 0x3ff >> (32 - start_bit); + sw_w32_mask(0x3ff << start_bit, (queue_weights[i] & 0x3ff) << start_bit, + RTL839X_TBL_ACCESS_DATA_2(low_byte)); + if (high_mask) + sw_w32_mask(high_mask, (queue_weights[i] & 0x3ff) >> (32- start_bit), + RTL839X_TBL_ACCESS_DATA_2(low_byte - 1)); + } + + rtl839x_write_scheduling_table(port); + mutex_unlock(&priv->reg_mutex); +} + +void rtl838x_config_qos(void) +{ + int i, p; + u32 v; + + pr_info("Setting up RTL838X QoS\n"); + pr_info("RTL838X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL838X_PRI_SEL_TBL_CTRL(0))); + rtl83xx_setup_default_prio2queue(); + + // Enable inner (bit 12) and outer (bit 13) priority remapping from DSCP + sw_w32_mask(0, BIT(12) | BIT(13), RTL838X_PRI_DSCP_INVLD_CTRL0); + + /* Set default weight for calculating internal priority, in prio selection group 0 + * Port based (prio 3), Port outer-tag (4), DSCP (5), Inner Tag (6), Outer Tag (7) + */ + v = 3 | (4 << 3) | (5 << 6) | (6 << 9) | (7 << 12); + sw_w32(v, RTL838X_PRI_SEL_TBL_CTRL(0)); + + // Set the inner and outer priority one-to-one to re-marked outer dot1p priority + v = 0; + for (p = 0; p < 8; p++) + v |= p << (3 * p); + sw_w32(v, RTL838X_RMK_OPRI_CTRL); + sw_w32(v, RTL838X_RMK_IPRI_CTRL); + + v = 0; + for (p = 0; p < 8; p++) + v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3); + sw_w32(v, RTL838X_PRI_SEL_IPRI_REMAP); + + // On all ports set scheduler type to WFQ + for (i = 0; i <= soc_info.cpu_port; i++) + sw_w32(0, RTL838X_SCHED_P_TYPE_CTRL(i)); + + // Enable egress scheduler for CPU-Port + sw_w32_mask(0, BIT(8), RTL838X_SCHED_LB_CTRL(soc_info.cpu_port)); + + // Enable egress drop allways on + sw_w32_mask(0, BIT(11), RTL838X_FC_P_EGR_DROP_CTRL(soc_info.cpu_port)); + + // Give special trap frames priority 7 (BPDUs) and routing exceptions: + sw_w32_mask(0, 7 << 3 | 7, RTL838X_QM_PKT2CPU_INTPRI_2); + // Give RMA frames priority 7: + sw_w32_mask(0, 7, RTL838X_QM_PKT2CPU_INTPRI_1); +} + +void rtl839x_config_qos(void) +{ + int port, p, q; + u32 v; + struct rtl838x_switch_priv *priv = switch_priv; + + pr_info("Setting up RTL839X QoS\n"); + pr_info("RTL839X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL839X_PRI_SEL_TBL_CTRL(0))); + rtl83xx_setup_default_prio2queue(); + + for (port = 0; port < soc_info.cpu_port; port++) + sw_w32(7, RTL839X_QM_PORT_QNUM(port)); + + // CPU-port gets queue number 7 + sw_w32(7, RTL839X_QM_PORT_QNUM(soc_info.cpu_port)); + + for (port = 0; port <= soc_info.cpu_port; port++) { + rtl83xx_set_ingress_priority(port, 0); + rtl839x_set_scheduling_algorithm(priv, port, WEIGHTED_FAIR_QUEUE); + rtl839x_set_scheduling_queue_weights(priv, port, default_queue_weights); + // Do re-marking based on outer tag + sw_w32_mask(0, BIT(port % 32), RTL839X_RMK_PORT_DEI_TAG_CTRL(port)); + } + + // Remap dot1p priorities to internal priority, for this the outer tag needs be re-marked + v = 0; + for (p = 0; p < 8; p++) + v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3); + sw_w32(v, RTL839X_PRI_SEL_IPRI_REMAP); + + /* Configure Drop Precedence for Drop Eligible Indicator (DEI) + * Index 0: 0 + * Index 1: 2 + * Each indicator is 2 bits long + */ + sw_w32(2 << 2, RTL839X_PRI_SEL_DEI2DP_REMAP); + + // Re-mark DEI: 4 bit-fields of 2 bits each, field 0 is bits 0-1, ... + sw_w32((0x1 << 2) | (0x1 << 4), RTL839X_RMK_DEI_CTRL); + + /* Set Congestion avoidance drop probability to 0 for drop precedences 0-2 (bits 24-31) + * low threshold (bits 0-11) to 4095 and high threshold (bits 12-23) to 4095 + * Weighted Random Early Detection (WRED) is used + */ + sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(0)); + sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(1)); + sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(2)); + + /* Set queue-based congestion avoidance properties, register fields are as + * for forward RTL839X_WRED_PORT_THR_CTRL + */ + for (q = 0; q < 8; q++) { + sw_w32(255 << 24 | 78 << 12 | 68, RTL839X_WRED_QUEUE_THR_CTRL(q, 0)); + sw_w32(255 << 24 | 74 << 12 | 64, RTL839X_WRED_QUEUE_THR_CTRL(q, 0)); + sw_w32(255 << 24 | 70 << 12 | 60, RTL839X_WRED_QUEUE_THR_CTRL(q, 0)); + } +} + +void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv) +{ + switch_priv = priv; + + pr_info("In %s\n", __func__); + + if (priv->family_id == RTL8380_FAMILY_ID) + return rtl838x_config_qos(); + else if (priv->family_id == RTL8390_FAMILY_ID) + return rtl839x_config_qos(); + + if (priv->family_id == RTL8380_FAMILY_ID) + rtl838x_rate_control_init(priv); + else if (priv->family_id == RTL8390_FAMILY_ID) + rtl839x_rate_control_init(priv); + +} diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.c new file mode 100644 index 00000000000..9ce50989790 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.c @@ -0,0 +1,2054 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include + +#include "rtl83xx.h" + +#define RTL838X_VLAN_PORT_TAG_STS_UNTAG 0x0 +#define RTL838X_VLAN_PORT_TAG_STS_TAGGED 0x1 +#define RTL838X_VLAN_PORT_TAG_STS_PRIORITY_TAGGED 0x2 + +#define RTL838X_VLAN_PORT_TAG_STS_CTRL_BASE 0xA530 +/* port 0-28 */ +#define RTL838X_VLAN_PORT_TAG_STS_CTRL(port) \ + RTL838X_VLAN_PORT_TAG_STS_CTRL_BASE + (port << 2) + +#define RTL838X_VLAN_PORT_TAG_STS_CTRL_EGR_P_OTAG_KEEP_MASK GENMASK(11,10) +#define RTL838X_VLAN_PORT_TAG_STS_CTRL_EGR_P_ITAG_KEEP_MASK GENMASK(9,8) +#define RTL838X_VLAN_PORT_TAG_STS_CTRL_IGR_P_OTAG_KEEP_MASK GENMASK(7,6) +#define RTL838X_VLAN_PORT_TAG_STS_CTRL_IGR_P_ITAG_KEEP_MASK GENMASK(5,4) +#define RTL838X_VLAN_PORT_TAG_STS_CTRL_OTAG_STS_MASK GENMASK(3,2) +#define RTL838X_VLAN_PORT_TAG_STS_CTRL_ITAG_STS_MASK GENMASK(1,0) + +extern struct mutex smi_lock; + +// see_dal_maple_acl_log2PhyTmplteField and src/app/diag_v2/src/diag_acl.c +/* Definition of the RTL838X-specific template field IDs as used in the PIE */ +enum template_field_id { + TEMPLATE_FIELD_SPMMASK = 0, + TEMPLATE_FIELD_SPM0 = 1, // Source portmask ports 0-15 + TEMPLATE_FIELD_SPM1 = 2, // Source portmask ports 16-28 + TEMPLATE_FIELD_RANGE_CHK = 3, + TEMPLATE_FIELD_DMAC0 = 4, // Destination MAC [15:0] + TEMPLATE_FIELD_DMAC1 = 5, // Destination MAC [31:16] + TEMPLATE_FIELD_DMAC2 = 6, // Destination MAC [47:32] + TEMPLATE_FIELD_SMAC0 = 7, // Source MAC [15:0] + TEMPLATE_FIELD_SMAC1 = 8, // Source MAC [31:16] + TEMPLATE_FIELD_SMAC2 = 9, // Source MAC [47:32] + TEMPLATE_FIELD_ETHERTYPE = 10, // Ethernet typ + TEMPLATE_FIELD_OTAG = 11, // Outer VLAN tag + TEMPLATE_FIELD_ITAG = 12, // Inner VLAN tag + TEMPLATE_FIELD_SIP0 = 13, // IPv4 or IPv6 source IP[15:0] or ARP/RARP + // source protocol address in header + TEMPLATE_FIELD_SIP1 = 14, // IPv4 or IPv6 source IP[31:16] or ARP/RARP + TEMPLATE_FIELD_DIP0 = 15, // IPv4 or IPv6 destination IP[15:0] + TEMPLATE_FIELD_DIP1 = 16, // IPv4 or IPv6 destination IP[31:16] + TEMPLATE_FIELD_IP_TOS_PROTO = 17, // IPv4 TOS/IPv6 traffic class and + // IPv4 proto/IPv6 next header fields + TEMPLATE_FIELD_L34_HEADER = 18, // packet with extra tag and IPv6 with auth, dest, + // frag, route, hop-by-hop option header, + // IGMP type, TCP flag + TEMPLATE_FIELD_L4_SPORT = 19, // TCP/UDP source port + TEMPLATE_FIELD_L4_DPORT = 20, // TCP/UDP destination port + TEMPLATE_FIELD_ICMP_IGMP = 21, + TEMPLATE_FIELD_IP_RANGE = 22, + TEMPLATE_FIELD_FIELD_SELECTOR_VALID = 23, // Field selector mask + TEMPLATE_FIELD_FIELD_SELECTOR_0 = 24, + TEMPLATE_FIELD_FIELD_SELECTOR_1 = 25, + TEMPLATE_FIELD_FIELD_SELECTOR_2 = 26, + TEMPLATE_FIELD_FIELD_SELECTOR_3 = 27, + TEMPLATE_FIELD_SIP2 = 28, // IPv6 source IP[47:32] + TEMPLATE_FIELD_SIP3 = 29, // IPv6 source IP[63:48] + TEMPLATE_FIELD_SIP4 = 30, // IPv6 source IP[79:64] + TEMPLATE_FIELD_SIP5 = 31, // IPv6 source IP[95:80] + TEMPLATE_FIELD_SIP6 = 32, // IPv6 source IP[111:96] + TEMPLATE_FIELD_SIP7 = 33, // IPv6 source IP[127:112] + TEMPLATE_FIELD_DIP2 = 34, // IPv6 destination IP[47:32] + TEMPLATE_FIELD_DIP3 = 35, // IPv6 destination IP[63:48] + TEMPLATE_FIELD_DIP4 = 36, // IPv6 destination IP[79:64] + TEMPLATE_FIELD_DIP5 = 37, // IPv6 destination IP[95:80] + TEMPLATE_FIELD_DIP6 = 38, // IPv6 destination IP[111:96] + TEMPLATE_FIELD_DIP7 = 39, // IPv6 destination IP[127:112] + TEMPLATE_FIELD_FWD_VID = 40, // Forwarding VLAN-ID + TEMPLATE_FIELD_FLOW_LABEL = 41, +}; + +/* + * The RTL838X SoCs use 5 fixed templates with definitions for which data fields are to + * be copied from the Ethernet Frame header into the 12 User-definable fields of the Packet + * Inspection Engine's buffer. The following defines the field contents for each of the fixed + * templates. Additionally, 3 user-definable templates can be set up via the definitions + * in RTL838X_ACL_TMPLTE_CTRL control registers. + * TODO: See all src/app/diag_v2/src/diag_pie.c + */ +#define N_FIXED_TEMPLATES 5 +static enum template_field_id fixed_templates[N_FIXED_TEMPLATES][N_FIXED_FIELDS] = +{ + { + TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1, TEMPLATE_FIELD_OTAG, + TEMPLATE_FIELD_SMAC0, TEMPLATE_FIELD_SMAC1, TEMPLATE_FIELD_SMAC2, + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_ITAG, TEMPLATE_FIELD_RANGE_CHK + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, + TEMPLATE_FIELD_DIP1,TEMPLATE_FIELD_IP_TOS_PROTO, TEMPLATE_FIELD_L4_SPORT, + TEMPLATE_FIELD_L4_DPORT, TEMPLATE_FIELD_ICMP_IGMP, TEMPLATE_FIELD_ITAG, + TEMPLATE_FIELD_RANGE_CHK, TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1 + }, { + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_ITAG, TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_IP_TOS_PROTO, + TEMPLATE_FIELD_L4_DPORT, TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_SIP0, + TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, TEMPLATE_FIELD_DIP1 + }, { + TEMPLATE_FIELD_DIP0, TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_DIP2, + TEMPLATE_FIELD_DIP3, TEMPLATE_FIELD_DIP4, TEMPLATE_FIELD_DIP5, + TEMPLATE_FIELD_DIP6, TEMPLATE_FIELD_DIP7, TEMPLATE_FIELD_L4_DPORT, + TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_ICMP_IGMP, TEMPLATE_FIELD_IP_TOS_PROTO + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_SIP2, + TEMPLATE_FIELD_SIP3, TEMPLATE_FIELD_SIP4, TEMPLATE_FIELD_SIP5, + TEMPLATE_FIELD_SIP6, TEMPLATE_FIELD_SIP7, TEMPLATE_FIELD_ITAG, + TEMPLATE_FIELD_RANGE_CHK, TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1 + }, +}; + +void rtl838x_print_matrix(void) +{ + unsigned volatile int *ptr8; + int i; + + ptr8 = RTL838X_SW_BASE + RTL838X_PORT_ISO_CTRL(0); + for (i = 0; i < 28; i += 8) + pr_debug("> %8x %8x %8x %8x %8x %8x %8x %8x\n", + ptr8[i + 0], ptr8[i + 1], ptr8[i + 2], ptr8[i + 3], + ptr8[i + 4], ptr8[i + 5], ptr8[i + 6], ptr8[i + 7]); + pr_debug("CPU_PORT> %8x\n", ptr8[28]); +} + +static inline int rtl838x_port_iso_ctrl(int p) +{ + return RTL838X_PORT_ISO_CTRL(p); +} + +static inline void rtl838x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_0) & BIT(15)); +} + +static inline void rtl838x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL838X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL838X_TBL_ACCESS_CTRL_1) & BIT(15)); +} + +static inline int rtl838x_tbl_access_data_0(int i) +{ + return RTL838X_TBL_ACCESS_DATA_0(i); +} + +static void rtl838x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v; + // Read VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0); + + rtl_table_read(r, vlan); + info->tagged_ports = sw_r32(rtl_table_data(r, 0)); + v = sw_r32(rtl_table_data(r, 1)); + pr_debug("VLAN_READ %d: %016llx %08x\n", vlan, info->tagged_ports, v); + rtl_table_release(r); + + info->profile_id = v & 0x7; + info->hash_mc_fid = !!(v & 0x8); + info->hash_uc_fid = !!(v & 0x10); + info->fid = (v >> 5) & 0x3f; + + // Read UNTAG table (0) via table register 1 + r = rtl_table_get(RTL8380_TBL_1, 0); + rtl_table_read(r, vlan); + info->untagged_ports = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); +} + +static void rtl838x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v; + // Access VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 0); + + sw_w32(info->tagged_ports, rtl_table_data(r, 0)); + + v = info->profile_id; + v |= info->hash_mc_fid ? 0x8 : 0; + v |= info->hash_uc_fid ? 0x10 : 0; + v |= ((u32)info->fid) << 5; + sw_w32(v, rtl_table_data(r, 1)); + + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +static void rtl838x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + // Access UNTAG table (0) via register 1 + struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 0); + + sw_w32(portmask & 0x1fffffff, rtl_table_data(r, 0)); + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer + */ +static void rtl838x_vlan_fwd_on_inner(int port, bool is_set) +{ + if (is_set) + sw_w32_mask(BIT(port), 0, RTL838X_VLAN_PORT_FWD); + else + sw_w32_mask(0, BIT(port), RTL838X_VLAN_PORT_FWD); +} + +static u64 rtl838x_l2_hash_seed(u64 mac, u32 vid) +{ + return mac << 12 | vid; +} + +/* + * Applies the same hash algorithm as the one used currently by the ASIC to the seed + * and returns a key into the L2 hash table + */ +static u32 rtl838x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 h1, h2, h3, h; + + if (sw_r32(priv->r->l2_ctrl_0) & 1) { + h1 = (seed >> 11) & 0x7ff; + h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); + + h2 = (seed >> 33) & 0x7ff; + h2 = ((h2 & 0x3f) << 5) | ((h2 >> 6) & 0x1f); + + h3 = (seed >> 44) & 0x7ff; + h3 = ((h3 & 0x7f) << 4) | ((h3 >> 7) & 0xf); + + h = h1 ^ h2 ^ h3 ^ ((seed >> 55) & 0x1ff); + h ^= ((seed >> 22) & 0x7ff) ^ (seed & 0x7ff); + } else { + h = ((seed >> 55) & 0x1ff) ^ ((seed >> 44) & 0x7ff) + ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) + ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff); + } + + return h; +} + +static inline int rtl838x_mac_force_mode_ctrl(int p) +{ + return RTL838X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl838x_mac_port_ctrl(int p) +{ + return RTL838X_MAC_PORT_CTRL(p); +} + +static inline int rtl838x_l2_port_new_salrn(int p) +{ + return RTL838X_L2_PORT_NEW_SALRN(p); +} + +static inline int rtl838x_l2_port_new_sa_fwd(int p) +{ + return RTL838X_L2_PORT_NEW_SA_FWD(p); +} + +static inline int rtl838x_mac_link_spd_sts(int p) +{ + return RTL838X_MAC_LINK_SPD_STS(p); +} + +inline static int rtl838x_trk_mbr_ctr(int group) +{ + return RTL838X_TRK_MBR_CTR + (group << 2); +} + +/* + * Fills an L2 entry structure from the SoC registers + */ +static void rtl838x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) +{ + /* Table contains different entry types, we need to identify the right one: + * Check for MC entries, first + * In contrast to the RTL93xx SoCs, there is no valid bit, use heuristics to + * identify valid entries + */ + e->is_ip_mc = !!(r[0] & BIT(22)); + e->is_ipv6_mc = !!(r[0] & BIT(21)); + e->type = L2_INVALID; + + if (!e->is_ip_mc && !e->is_ipv6_mc) { + e->mac[0] = (r[1] >> 20); + e->mac[1] = (r[1] >> 12); + e->mac[2] = (r[1] >> 4); + e->mac[3] = (r[1] & 0xf) << 4 | (r[2] >> 28); + e->mac[4] = (r[2] >> 20); + e->mac[5] = (r[2] >> 12); + + e->rvid = r[2] & 0xfff; + e->vid = r[0] & 0xfff; + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + e->is_static = !!((r[0] >> 19) & 1); + e->port = (r[0] >> 12) & 0x1f; + e->block_da = !!(r[1] & BIT(30)); + e->block_sa = !!(r[1] & BIT(31)); + e->suspended = !!(r[1] & BIT(29)); + e->next_hop = !!(r[1] & BIT(28)); + if (e->next_hop) { + pr_debug("Found next hop entry, need to read extra data\n"); + e->nh_vlan_target = !!(r[0] & BIT(9)); + e->nh_route_id = r[0] & 0x1ff; + e->vid = e->rvid; + } + e->age = (r[0] >> 17) & 0x3; + e->valid = true; + + /* A valid entry has one of mutli-cast, aging, sa/da-blocking, + * next-hop or static entry bit set */ + if (!(r[0] & 0x007c0000) && !(r[1] & 0xd0000000)) + e->valid = false; + else + e->type = L2_UNICAST; + } else { // L2 multicast + pr_debug("Got L2 MC entry: %08x %08x %08x\n", r[0], r[1], r[2]); + e->valid = true; + e->type = L2_MULTICAST; + e->mc_portmask_index = (r[0] >> 12) & 0x1ff; + } + } else { // IPv4 and IPv6 multicast + e->valid = true; + e->mc_portmask_index = (r[0] >> 12) & 0x1ff; + e->mc_gip = (r[1] << 20) | (r[2] >> 12); + e->rvid = r[2] & 0xfff; + } + if (e->is_ip_mc) + e->type = IP4_MULTICAST; + if (e->is_ipv6_mc) + e->type = IP6_MULTICAST; +} + +/* + * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry + */ +static void rtl838x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e) +{ + u64 mac = ether_addr_to_u64(e->mac); + + if (!e->valid) { + r[0] = r[1] = r[2] = 0; + return; + } + + r[0] = e->is_ip_mc ? BIT(22) : 0; + r[0] |= e->is_ipv6_mc ? BIT(21) : 0; + + if (!e->is_ip_mc && !e->is_ipv6_mc) { + r[1] = mac >> 20; + r[2] = (mac & 0xfffff) << 12; + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + r[0] |= e->is_static ? BIT(19) : 0; + r[0] |= (e->port & 0x3f) << 12; + r[0] |= e->vid; + r[1] |= e->block_da ? BIT(30) : 0; + r[1] |= e->block_sa ? BIT(31) : 0; + r[1] |= e->suspended ? BIT(29) : 0; + r[2] |= e->rvid & 0xfff; + if (e->next_hop) { + r[1] |= BIT(28); + r[0] |= e->nh_vlan_target ? BIT(9) : 0; + r[0] |= e->nh_route_id & 0x1ff; + } + r[0] |= (e->age & 0x3) << 17; + } else { // L2 Multicast + r[0] |= (e->mc_portmask_index & 0x1ff) << 12; + r[2] |= e->rvid & 0xfff; + r[0] |= e->vid & 0xfff; + pr_debug("FILL MC: %08x %08x %08x\n", r[0], r[1], r[2]); + } + } else { // IPv4 and IPv6 multicast + r[0] |= (e->mc_portmask_index & 0x1ff) << 12; + r[1] = e->mc_gip >> 20; + r[2] = e->mc_gip << 12; + r[2] |= e->rvid; + } +} + +/* + * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table + * hash is the id of the bucket and pos is the position of the entry in that bucket + * The data read from the SoC is filled into rtl838x_l2_entry + */ +static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0); // Access L2 Table 0 + u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket + int i; + + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl838x_fill_l2_entry(r, e); + if (!e->valid) + return 0; + + entry = (((u64) r[1]) << 32) | (r[2]); // mac and vid concatenated as hash seed + return entry; +} + +static void rtl838x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 0); + int i; + + u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket + + rtl838x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl838x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + u64 entry; + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl838x_fill_l2_entry(r, e); + if (!e->valid) + return 0; + + pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + + // Return MAC with concatenated VID ac concatenated ID + entry = (((u64) r[1]) << 32) | r[2]; + return entry; +} + +static void rtl838x_write_cam(int idx, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl838x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl838x_read_mcast_pmask(int idx) +{ + u32 portmask; + // Read MC_PMSK (2) via register RTL8380_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2); + + rtl_table_read(q, idx); + portmask = sw_r32(rtl_table_data(q, 0)); + rtl_table_release(q); + + return portmask; +} + +static void rtl838x_write_mcast_pmask(int idx, u64 portmask) +{ + // Access MC_PMSK (2) via register RTL8380_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8380_TBL_L2, 2); + + sw_w32(((u32)portmask) & 0x1fffffff, rtl_table_data(q, 0)); + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static void rtl838x_vlan_profile_setup(int profile) +{ + u32 pmask_id = UNKNOWN_MC_PMASK; + // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for unknown MC traffic flooding + u32 p = 1 | pmask_id << 1 | pmask_id << 10 | pmask_id << 19; + + sw_w32(p, RTL838X_VLAN_PROFILE(profile)); + + /* RTL8380 and RTL8390 use an index into the portmask table to set the + * unknown multicast portmask, setup a default at a safe location + * On RTL93XX, the portmask is directly set in the profile, + * see e.g. rtl9300_vlan_profile_setup + */ + rtl838x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0x1fffffff); +} + +static void rtl838x_l2_learning_setup(void) +{ + /* Set portmask for broadcast traffic and unknown unicast address flooding + * to the reserved entry in the portmask table used also for + * multicast flooding */ + sw_w32(UNKNOWN_MC_PMASK << 12 | UNKNOWN_MC_PMASK, RTL838X_L2_FLD_PMSK); + + /* Enable learning constraint system-wide (bit 0), per-port (bit 1) + * and per vlan (bit 2) */ + sw_w32(0x7, RTL838X_L2_LRN_CONSTRT_EN); + + // Limit learning to maximum: 16k entries, after that just flood (bits 0-1) + sw_w32((0x3fff << 2) | 0, RTL838X_L2_LRN_CONSTRT); + + // Do not trap ARP packets to CPU_PORT + sw_w32(0, RTL838X_SPCL_TRAP_ARP_CTRL); +} + +static void rtl838x_enable_learning(int port, bool enable) +{ + // Limit learning to maximum: 16k entries + + sw_w32_mask(0x3fff << 2, enable ? (0x3fff << 2) : 0, + RTL838X_L2_PORT_LRN_CONSTRT + (port << 2)); +} + +static void rtl838x_enable_flood(int port, bool enable) +{ + /* + * 0: Forward + * 1: Disable + * 2: to CPU + * 3: Copy to CPU + */ + sw_w32_mask(0x3, enable ? 0 : 1, + RTL838X_L2_PORT_LRN_CONSTRT + (port << 2)); +} + +static void rtl838x_enable_mcast_flood(int port, bool enable) +{ + +} + +static void rtl838x_enable_bcast_flood(int port, bool enable) +{ + +} + +static void rtl838x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 15 /* Execute cmd */ + | 1 << 14 /* Read */ + | 2 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 2; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); +} + +static void rtl838x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 15 /* Execute cmd */ + | 0 << 14 /* Write */ + | 2 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + + for (i = 0; i < 2; i++) + sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +u64 rtl838x_traffic_get(int source) +{ + return rtl838x_get_port_reg(rtl838x_port_iso_ctrl(source)); +} + +void rtl838x_traffic_set(int source, u64 dest_matrix) +{ + rtl838x_set_port_reg(dest_matrix, rtl838x_port_iso_ctrl(source)); +} + +void rtl838x_traffic_enable(int source, int dest) +{ + rtl838x_mask_port_reg(0, BIT(dest), rtl838x_port_iso_ctrl(source)); +} + +void rtl838x_traffic_disable(int source, int dest) +{ + rtl838x_mask_port_reg(BIT(dest), 0, rtl838x_port_iso_ctrl(source)); +} + +/* + * Enables or disables the EEE/EEEP capability of a port + */ +static void rtl838x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + u32 v; + + // This works only for Ethernet ports, and on the RTL838X, ports from 24 are SFP + if (port >= 24) + return; + + pr_debug("In %s: setting port %d to %d\n", __func__, port, enable); + v = enable ? 0x3 : 0x0; + + // Set EEE state for 100 (bit 9) & 1000MBit (bit 10) + sw_w32_mask(0x3 << 9, v << 9, priv->r->mac_force_mode_ctrl(port)); + + // Set TX/RX EEE state + if (enable) { + sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_TX_EN); + sw_w32_mask(0, BIT(port), RTL838X_EEE_PORT_RX_EN); + } else { + sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_TX_EN); + sw_w32_mask(BIT(port), 0, RTL838X_EEE_PORT_RX_EN); + } + priv->ports[port].eee_enabled = enable; +} + + +/* + * Get EEE own capabilities and negotiation result + */ +static int rtl838x_eee_port_ability(struct rtl838x_switch_priv *priv, + struct ethtool_eee *e, int port) +{ + u64 link; + + if (port >= 24) + return 0; + + link = rtl839x_get_port_reg_le(RTL838X_MAC_LINK_STS); + if (!(link & BIT(port))) + return 0; + + if (sw_r32(rtl838x_mac_force_mode_ctrl(port)) & BIT(9)) + e->advertised |= ADVERTISED_100baseT_Full; + + if (sw_r32(rtl838x_mac_force_mode_ctrl(port)) & BIT(10)) + e->advertised |= ADVERTISED_1000baseT_Full; + + if (sw_r32(RTL838X_MAC_EEE_ABLTY) & BIT(port)) { + e->lp_advertised = ADVERTISED_100baseT_Full; + e->lp_advertised |= ADVERTISED_1000baseT_Full; + return 1; + } + + return 0; +} + +static void rtl838x_init_eee(struct rtl838x_switch_priv *priv, bool enable) +{ + int i; + + pr_info("Setting up EEE, state: %d\n", enable); + sw_w32_mask(0x4, 0, RTL838X_SMI_GLB_CTRL); + + /* Set timers for EEE */ + sw_w32(0x5001411, RTL838X_EEE_TX_TIMER_GIGA_CTRL); + sw_w32(0x5001417, RTL838X_EEE_TX_TIMER_GELITE_CTRL); + + // Enable EEE MAC support on ports + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + rtl838x_port_eee_set(priv, i, enable); + } + priv->eee_enabled = enable; +} + +static void rtl838x_pie_lookup_enable(struct rtl838x_switch_priv *priv, int index) +{ + int block = index / PIE_BLOCK_SIZE; + u32 block_state = sw_r32(RTL838X_ACL_BLK_LOOKUP_CTRL); + + // Make sure rule-lookup is enabled in the block + if (!(block_state & BIT(block))) + sw_w32(block_state | BIT(block), RTL838X_ACL_BLK_LOOKUP_CTRL); +} + +static void rtl838x_pie_rule_del(struct rtl838x_switch_priv *priv, int index_from, int index_to) +{ + int block_from = index_from / PIE_BLOCK_SIZE; + int block_to = index_to / PIE_BLOCK_SIZE; + u32 v = (index_from << 1)| (index_to << 12 ) | BIT(0); + int block; + u32 block_state; + + pr_debug("%s: from %d to %d\n", __func__, index_from, index_to); + mutex_lock(&priv->reg_mutex); + + // Remember currently active blocks + block_state = sw_r32(RTL838X_ACL_BLK_LOOKUP_CTRL); + + // Make sure rule-lookup is disabled in the relevant blocks + for (block = block_from; block <= block_to; block++) { + if (block_state & BIT(block)) + sw_w32(block_state & (~BIT(block)), RTL838X_ACL_BLK_LOOKUP_CTRL); + } + + // Write from-to and execute bit into control register + sw_w32(v, RTL838X_ACL_CLR_CTRL); + + // Wait until command has completed + do { + } while (sw_r32(RTL838X_ACL_CLR_CTRL) & BIT(0)); + + // Re-enable rule lookup + for (block = block_from; block <= block_to; block++) { + if (!(block_state & BIT(block))) + sw_w32(block_state | BIT(block), RTL838X_ACL_BLK_LOOKUP_CTRL); + } + + mutex_unlock(&priv->reg_mutex); +} + +/* + * Reads the intermediate representation of the templated match-fields of the + * PIE rule in the pie_rule structure and fills in the raw data fields in the + * raw register space r[]. + * The register space configuration size is identical for the RTL8380/90 and RTL9300, + * however the RTL9310 has 2 more registers / fields and the physical field-ids + * are specific to every platform. + */ +static void rtl838x_write_pie_templated(u32 r[], struct pie_rule *pr, enum template_field_id t[]) +{ + int i; + enum template_field_id field_type; + u16 data, data_m; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + field_type = t[i]; + data = data_m = 0; + + switch (field_type) { + case TEMPLATE_FIELD_SPM0: + data = pr->spm; + data_m = pr->spm_m; + break; + case TEMPLATE_FIELD_SPM1: + data = pr->spm >> 16; + data_m = pr->spm_m >> 16; + break; + case TEMPLATE_FIELD_OTAG: + data = pr->otag; + data_m = pr->otag_m; + break; + case TEMPLATE_FIELD_SMAC0: + data = pr->smac[4]; + data = (data << 8) | pr->smac[5]; + data_m = pr->smac_m[4]; + data_m = (data_m << 8) | pr->smac_m[5]; + break; + case TEMPLATE_FIELD_SMAC1: + data = pr->smac[2]; + data = (data << 8) | pr->smac[3]; + data_m = pr->smac_m[2]; + data_m = (data_m << 8) | pr->smac_m[3]; + break; + case TEMPLATE_FIELD_SMAC2: + data = pr->smac[0]; + data = (data << 8) | pr->smac[1]; + data_m = pr->smac_m[0]; + data_m = (data_m << 8) | pr->smac_m[1]; + break; + case TEMPLATE_FIELD_DMAC0: + data = pr->dmac[4]; + data = (data << 8) | pr->dmac[5]; + data_m = pr->dmac_m[4]; + data_m = (data_m << 8) | pr->dmac_m[5]; + break; + case TEMPLATE_FIELD_DMAC1: + data = pr->dmac[2]; + data = (data << 8) | pr->dmac[3]; + data_m = pr->dmac_m[2]; + data_m = (data_m << 8) | pr->dmac_m[3]; + break; + case TEMPLATE_FIELD_DMAC2: + data = pr->dmac[0]; + data = (data << 8) | pr->dmac[1]; + data_m = pr->dmac_m[0]; + data_m = (data_m << 8) | pr->dmac_m[1]; + break; + case TEMPLATE_FIELD_ETHERTYPE: + data = pr->ethertype; + data_m = pr->ethertype_m; + break; + case TEMPLATE_FIELD_ITAG: + data = pr->itag; + data_m = pr->itag_m; + break; + case TEMPLATE_FIELD_RANGE_CHK: + data = pr->field_range_check; + data_m = pr->field_range_check_m; + break; + case TEMPLATE_FIELD_SIP0: + if (pr->is_ipv6) { + data = pr->sip6.s6_addr16[7]; + data_m = pr->sip6_m.s6_addr16[7]; + } else { + data = pr->sip; + data_m = pr->sip_m; + } + break; + case TEMPLATE_FIELD_SIP1: + if (pr->is_ipv6) { + data = pr->sip6.s6_addr16[6]; + data_m = pr->sip6_m.s6_addr16[6]; + } else { + data = pr->sip >> 16; + data_m = pr->sip_m >> 16; + } + break; + + case TEMPLATE_FIELD_SIP2: + case TEMPLATE_FIELD_SIP3: + case TEMPLATE_FIELD_SIP4: + case TEMPLATE_FIELD_SIP5: + case TEMPLATE_FIELD_SIP6: + case TEMPLATE_FIELD_SIP7: + data = pr->sip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + data_m = pr->sip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + break; + + case TEMPLATE_FIELD_DIP0: + if (pr->is_ipv6) { + data = pr->dip6.s6_addr16[7]; + data_m = pr->dip6_m.s6_addr16[7]; + } else { + data = pr->dip; + data_m = pr->dip_m; + } + break; + + case TEMPLATE_FIELD_DIP1: + if (pr->is_ipv6) { + data = pr->dip6.s6_addr16[6]; + data_m = pr->dip6_m.s6_addr16[6]; + } else { + data = pr->dip >> 16; + data_m = pr->dip_m >> 16; + } + break; + + case TEMPLATE_FIELD_DIP2: + case TEMPLATE_FIELD_DIP3: + case TEMPLATE_FIELD_DIP4: + case TEMPLATE_FIELD_DIP5: + case TEMPLATE_FIELD_DIP6: + case TEMPLATE_FIELD_DIP7: + data = pr->dip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + data_m = pr->dip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + break; + + case TEMPLATE_FIELD_IP_TOS_PROTO: + data = pr->tos_proto; + data_m = pr->tos_proto_m; + break; + case TEMPLATE_FIELD_L4_SPORT: + data = pr->sport; + data_m = pr->sport_m; + break; + case TEMPLATE_FIELD_L4_DPORT: + data = pr->dport; + data_m = pr->dport_m; + break; + case TEMPLATE_FIELD_ICMP_IGMP: + data = pr->icmp_igmp; + data_m = pr->icmp_igmp_m; + break; + default: + pr_info("%s: unknown field %d\n", __func__, field_type); + continue; + } + if (!(i % 2)) { + r[5 - i / 2] = data; + r[12 - i / 2] = data_m; + } else { + r[5 - i / 2] |= ((u32)data) << 16; + r[12 - i / 2] |= ((u32)data_m) << 16; + } + } +} + +/* + * Creates the intermediate representation of the templated match-fields of the + * PIE rule in the pie_rule structure by reading the raw data fields in the + * raw register space r[]. + * The register space configuration size is identical for the RTL8380/90 and RTL9300, + * however the RTL9310 has 2 more registers / fields and the physical field-ids + */ +static void rtl838x_read_pie_templated(u32 r[], struct pie_rule *pr, enum template_field_id t[]) +{ + int i; + enum template_field_id field_type; + u16 data, data_m; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + field_type = t[i]; + if (!(i % 2)) { + data = r[5 - i / 2]; + data_m = r[12 - i / 2]; + } else { + data = r[5 - i / 2] >> 16; + data_m = r[12 - i / 2] >> 16; + } + + switch (field_type) { + case TEMPLATE_FIELD_SPM0: + pr->spm = (pr->spn << 16) | data; + pr->spm_m = (pr->spn << 16) | data_m; + break; + case TEMPLATE_FIELD_SPM1: + pr->spm = data; + pr->spm_m = data_m; + break; + case TEMPLATE_FIELD_OTAG: + pr->otag = data; + pr->otag_m = data_m; + break; + case TEMPLATE_FIELD_SMAC0: + pr->smac[4] = data >> 8; + pr->smac[5] = data; + pr->smac_m[4] = data >> 8; + pr->smac_m[5] = data; + break; + case TEMPLATE_FIELD_SMAC1: + pr->smac[2] = data >> 8; + pr->smac[3] = data; + pr->smac_m[2] = data >> 8; + pr->smac_m[3] = data; + break; + case TEMPLATE_FIELD_SMAC2: + pr->smac[0] = data >> 8; + pr->smac[1] = data; + pr->smac_m[0] = data >> 8; + pr->smac_m[1] = data; + break; + case TEMPLATE_FIELD_DMAC0: + pr->dmac[4] = data >> 8; + pr->dmac[5] = data; + pr->dmac_m[4] = data >> 8; + pr->dmac_m[5] = data; + break; + case TEMPLATE_FIELD_DMAC1: + pr->dmac[2] = data >> 8; + pr->dmac[3] = data; + pr->dmac_m[2] = data >> 8; + pr->dmac_m[3] = data; + break; + case TEMPLATE_FIELD_DMAC2: + pr->dmac[0] = data >> 8; + pr->dmac[1] = data; + pr->dmac_m[0] = data >> 8; + pr->dmac_m[1] = data; + break; + case TEMPLATE_FIELD_ETHERTYPE: + pr->ethertype = data; + pr->ethertype_m = data_m; + break; + case TEMPLATE_FIELD_ITAG: + pr->itag = data; + pr->itag_m = data_m; + break; + case TEMPLATE_FIELD_RANGE_CHK: + pr->field_range_check = data; + pr->field_range_check_m = data_m; + break; + case TEMPLATE_FIELD_SIP0: + pr->sip = data; + pr->sip_m = data_m; + break; + case TEMPLATE_FIELD_SIP1: + pr->sip = (pr->sip << 16) | data; + pr->sip_m = (pr->sip << 16) | data_m; + break; + case TEMPLATE_FIELD_SIP2: + pr->is_ipv6 = true; + // Make use of limitiations on the position of the match values + ipv6_addr_set(&pr->sip6, pr->sip, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + ipv6_addr_set(&pr->sip6_m, pr->sip_m, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + case TEMPLATE_FIELD_SIP3: + case TEMPLATE_FIELD_SIP4: + case TEMPLATE_FIELD_SIP5: + case TEMPLATE_FIELD_SIP6: + case TEMPLATE_FIELD_SIP7: + break; + + case TEMPLATE_FIELD_DIP0: + pr->dip = data; + pr->dip_m = data_m; + break; + case TEMPLATE_FIELD_DIP1: + pr->dip = (pr->dip << 16) | data; + pr->dip_m = (pr->dip << 16) | data_m; + break; + case TEMPLATE_FIELD_DIP2: + pr->is_ipv6 = true; + ipv6_addr_set(&pr->dip6, pr->dip, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + ipv6_addr_set(&pr->dip6_m, pr->dip_m, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + case TEMPLATE_FIELD_DIP3: + case TEMPLATE_FIELD_DIP4: + case TEMPLATE_FIELD_DIP5: + case TEMPLATE_FIELD_DIP6: + case TEMPLATE_FIELD_DIP7: + break; + case TEMPLATE_FIELD_IP_TOS_PROTO: + pr->tos_proto = data; + pr->tos_proto_m = data_m; + break; + case TEMPLATE_FIELD_L4_SPORT: + pr->sport = data; + pr->sport_m = data_m; + break; + case TEMPLATE_FIELD_L4_DPORT: + pr->dport = data; + pr->dport_m = data_m; + break; + case TEMPLATE_FIELD_ICMP_IGMP: + pr->icmp_igmp = data; + pr->icmp_igmp_m = data_m; + break; + default: + pr_info("%s: unknown field %d\n", __func__, field_type); + } + } +} + +static void rtl838x_read_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + pr->spmmask_fix = (r[6] >> 22) & 0x3; + pr->spn = (r[6] >> 16) & 0x3f; + pr->mgnt_vlan = (r[6] >> 15) & 1; + pr->dmac_hit_sw = (r[6] >> 14) & 1; + pr->not_first_frag = (r[6] >> 13) & 1; + pr->frame_type_l4 = (r[6] >> 10) & 7; + pr->frame_type = (r[6] >> 8) & 3; + pr->otag_fmt = (r[6] >> 7) & 1; + pr->itag_fmt = (r[6] >> 6) & 1; + pr->otag_exist = (r[6] >> 5) & 1; + pr->itag_exist = (r[6] >> 4) & 1; + pr->frame_type_l2 = (r[6] >> 2) & 3; + pr->tid = r[6] & 3; + + pr->spmmask_fix_m = (r[13] >> 22) & 0x3; + pr->spn_m = (r[13] >> 16) & 0x3f; + pr->mgnt_vlan_m = (r[13] >> 15) & 1; + pr->dmac_hit_sw_m = (r[13] >> 14) & 1; + pr->not_first_frag_m = (r[13] >> 13) & 1; + pr->frame_type_l4_m = (r[13] >> 10) & 7; + pr->frame_type_m = (r[13] >> 8) & 3; + pr->otag_fmt_m = (r[13] >> 7) & 1; + pr->itag_fmt_m = (r[13] >> 6) & 1; + pr->otag_exist_m = (r[13] >> 5) & 1; + pr->itag_exist_m = (r[13] >> 4) & 1; + pr->frame_type_l2_m = (r[13] >> 2) & 3; + pr->tid_m = r[13] & 3; + + pr->valid = r[14] & BIT(31); + pr->cond_not = r[14] & BIT(30); + pr->cond_and1 = r[14] & BIT(29); + pr->cond_and2 = r[14] & BIT(28); + pr->ivalid = r[14] & BIT(27); + + pr->drop = (r[17] >> 14) & 3; + pr->fwd_sel = r[17] & BIT(13); + pr->ovid_sel = r[17] & BIT(12); + pr->ivid_sel = r[17] & BIT(11); + pr->flt_sel = r[17] & BIT(10); + pr->log_sel = r[17] & BIT(9); + pr->rmk_sel = r[17] & BIT(8); + pr->meter_sel = r[17] & BIT(7); + pr->tagst_sel = r[17] & BIT(6); + pr->mir_sel = r[17] & BIT(5); + pr->nopri_sel = r[17] & BIT(4); + pr->cpupri_sel = r[17] & BIT(3); + pr->otpid_sel = r[17] & BIT(2); + pr->itpid_sel = r[17] & BIT(1); + pr->shaper_sel = r[17] & BIT(0); +} + +static void rtl838x_write_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + r[6] = ((u32) (pr->spmmask_fix & 0x3)) << 22; + r[6] |= ((u32) (pr->spn & 0x3f)) << 16; + r[6] |= pr->mgnt_vlan ? BIT(15) : 0; + r[6] |= pr->dmac_hit_sw ? BIT(14) : 0; + r[6] |= pr->not_first_frag ? BIT(13) : 0; + r[6] |= ((u32) (pr->frame_type_l4 & 0x7)) << 10; + r[6] |= ((u32) (pr->frame_type & 0x3)) << 8; + r[6] |= pr->otag_fmt ? BIT(7) : 0; + r[6] |= pr->itag_fmt ? BIT(6) : 0; + r[6] |= pr->otag_exist ? BIT(5) : 0; + r[6] |= pr->itag_exist ? BIT(4) : 0; + r[6] |= ((u32) (pr->frame_type_l2 & 0x3)) << 2; + r[6] |= ((u32) (pr->tid & 0x3)); + + r[13] = ((u32) (pr->spmmask_fix_m & 0x3)) << 22; + r[13] |= ((u32) (pr->spn_m & 0x3f)) << 16; + r[13] |= pr->mgnt_vlan_m ? BIT(15) : 0; + r[13] |= pr->dmac_hit_sw_m ? BIT(14) : 0; + r[13] |= pr->not_first_frag_m ? BIT(13) : 0; + r[13] |= ((u32) (pr->frame_type_l4_m & 0x7)) << 10; + r[13] |= ((u32) (pr->frame_type_m & 0x3)) << 8; + r[13] |= pr->otag_fmt_m ? BIT(7) : 0; + r[13] |= pr->itag_fmt_m ? BIT(6) : 0; + r[13] |= pr->otag_exist_m ? BIT(5) : 0; + r[13] |= pr->itag_exist_m ? BIT(4) : 0; + r[13] |= ((u32) (pr->frame_type_l2_m & 0x3)) << 2; + r[13] |= ((u32) (pr->tid_m & 0x3)); + + r[14] = pr->valid ? BIT(31) : 0; + r[14] |= pr->cond_not ? BIT(30) : 0; + r[14] |= pr->cond_and1 ? BIT(29) : 0; + r[14] |= pr->cond_and2 ? BIT(28) : 0; + r[14] |= pr->ivalid ? BIT(27) : 0; + + if (pr->drop) + r[17] = 0x1 << 14; // Standard drop action + else + r[17] = 0; + r[17] |= pr->fwd_sel ? BIT(13) : 0; + r[17] |= pr->ovid_sel ? BIT(12) : 0; + r[17] |= pr->ivid_sel ? BIT(11) : 0; + r[17] |= pr->flt_sel ? BIT(10) : 0; + r[17] |= pr->log_sel ? BIT(9) : 0; + r[17] |= pr->rmk_sel ? BIT(8) : 0; + r[17] |= pr->meter_sel ? BIT(7) : 0; + r[17] |= pr->tagst_sel ? BIT(6) : 0; + r[17] |= pr->mir_sel ? BIT(5) : 0; + r[17] |= pr->nopri_sel ? BIT(4) : 0; + r[17] |= pr->cpupri_sel ? BIT(3) : 0; + r[17] |= pr->otpid_sel ? BIT(2) : 0; + r[17] |= pr->itpid_sel ? BIT(1) : 0; + r[17] |= pr->shaper_sel ? BIT(0) : 0; +} + +static int rtl838x_write_pie_action(u32 r[], struct pie_rule *pr) +{ + u16 *aif = (u16 *)&r[17]; + u16 data; + int fields_used = 0; + + aif--; + + pr_debug("%s, at %08x\n", __func__, (u32)aif); + /* Multiple actions can be linked to a match of a PIE rule, + * they have different precedence depending on their type and this precedence + * defines which Action Information Field (0-4) in the IACL table stores + * the additional data of the action (like e.g. the port number a packet is + * forwarded to) */ + // TODO: count bits in selectors to limit to a maximum number of actions + if (pr->fwd_sel) { // Forwarding action + data = pr->fwd_act << 13; + data |= pr->fwd_data; + data |= pr->bypass_all ? BIT(12) : 0; + data |= pr->bypass_ibc_sc ? BIT(11) : 0; + data |= pr->bypass_igr_stp ? BIT(10) : 0; + *aif-- = data; + fields_used++; + } + + if (pr->ovid_sel) { // Outer VID action + data = (pr->ovid_act & 0x3) << 12; + data |= pr->ovid_data; + *aif-- = data; + fields_used++; + } + + if (pr->ivid_sel) { // Inner VID action + data = (pr->ivid_act & 0x3) << 12; + data |= pr->ivid_data; + *aif-- = data; + fields_used++; + } + + if (pr->flt_sel) { // Filter action + *aif-- = pr->flt_data; + fields_used++; + } + + if (pr->log_sel) { // Log action + if (fields_used >= 4) + return -1; + *aif-- = pr->log_data; + fields_used++; + } + + if (pr->rmk_sel) { // Remark action + if (fields_used >= 4) + return -1; + *aif-- = pr->rmk_data; + fields_used++; + } + + if (pr->meter_sel) { // Meter action + if (fields_used >= 4) + return -1; + *aif-- = pr->meter_data; + fields_used++; + } + + if (pr->tagst_sel) { // Egress Tag Status action + if (fields_used >= 4) + return -1; + *aif-- = pr->tagst_data; + fields_used++; + } + + if (pr->mir_sel) { // Mirror action + if (fields_used >= 4) + return -1; + *aif-- = pr->mir_data; + fields_used++; + } + + if (pr->nopri_sel) { // Normal Priority action + if (fields_used >= 4) + return -1; + *aif-- = pr->nopri_data; + fields_used++; + } + + if (pr->cpupri_sel) { // CPU Priority action + if (fields_used >= 4) + return -1; + *aif-- = pr->nopri_data; + fields_used++; + } + + if (pr->otpid_sel) { // OTPID action + if (fields_used >= 4) + return -1; + *aif-- = pr->otpid_data; + fields_used++; + } + + if (pr->itpid_sel) { // ITPID action + if (fields_used >= 4) + return -1; + *aif-- = pr->itpid_data; + fields_used++; + } + + if (pr->shaper_sel) { // Traffic shaper action + if (fields_used >= 4) + return -1; + *aif-- = pr->shaper_data; + fields_used++; + } + + return 0; +} + +static void rtl838x_read_pie_action(u32 r[], struct pie_rule *pr) +{ + u16 *aif = (u16 *)&r[17]; + + aif--; + + pr_debug("%s, at %08x\n", __func__, (u32)aif); + if (pr->drop) + pr_debug("%s: Action Drop: %d", __func__, pr->drop); + + if (pr->fwd_sel){ // Forwarding action + pr->fwd_act = *aif >> 13; + pr->fwd_data = *aif--; + pr->bypass_all = pr->fwd_data & BIT(12); + pr->bypass_ibc_sc = pr->fwd_data & BIT(11); + pr->bypass_igr_stp = pr->fwd_data & BIT(10); + if (pr->bypass_all || pr->bypass_ibc_sc || pr->bypass_igr_stp) + pr->bypass_sel = true; + } + if (pr->ovid_sel) // Outer VID action + pr->ovid_data = *aif--; + if (pr->ivid_sel) // Inner VID action + pr->ivid_data = *aif--; + if (pr->flt_sel) // Filter action + pr->flt_data = *aif--; + if (pr->log_sel) // Log action + pr->log_data = *aif--; + if (pr->rmk_sel) // Remark action + pr->rmk_data = *aif--; + if (pr->meter_sel) // Meter action + pr->meter_data = *aif--; + if (pr->tagst_sel) // Egress Tag Status action + pr->tagst_data = *aif--; + if (pr->mir_sel) // Mirror action + pr->mir_data = *aif--; + if (pr->nopri_sel) // Normal Priority action + pr->nopri_data = *aif--; + if (pr->cpupri_sel) // CPU Priority action + pr->nopri_data = *aif--; + if (pr->otpid_sel) // OTPID action + pr->otpid_data = *aif--; + if (pr->itpid_sel) // ITPID action + pr->itpid_data = *aif--; + if (pr->shaper_sel) // Traffic shaper action + pr->shaper_data = *aif--; +} + +static void rtl838x_pie_rule_dump_raw(u32 r[]) +{ + pr_info("Raw IACL table entry:\n"); + pr_info("Match : %08x %08x %08x %08x %08x %08x\n", r[0], r[1], r[2], r[3], r[4], r[5]); + pr_info("Fixed : %08x\n", r[6]); + pr_info("Match M: %08x %08x %08x %08x %08x %08x\n", r[7], r[8], r[9], r[10], r[11], r[12]); + pr_info("Fixed M: %08x\n", r[13]); + pr_info("AIF : %08x %08x %08x\n", r[14], r[15], r[16]); + pr_info("Sel : %08x\n", r[17]); +} + +static void rtl838x_pie_rule_dump(struct pie_rule *pr) +{ + pr_info("Drop: %d, fwd: %d, ovid: %d, ivid: %d, flt: %d, log: %d, rmk: %d, meter: %d tagst: %d, mir: %d, nopri: %d, cpupri: %d, otpid: %d, itpid: %d, shape: %d\n", + pr->drop, pr->fwd_sel, pr->ovid_sel, pr->ivid_sel, pr->flt_sel, pr->log_sel, pr->rmk_sel, pr->log_sel, pr->tagst_sel, pr->mir_sel, pr->nopri_sel, + pr->cpupri_sel, pr->otpid_sel, pr->itpid_sel, pr->shaper_sel); + if (pr->fwd_sel) + pr_info("FWD: %08x\n", pr->fwd_data); + pr_info("TID: %x, %x\n", pr->tid, pr->tid_m); +} + +static int rtl838x_pie_rule_read(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr) +{ + // Read IACL table (1) via register 0 + struct table_reg *q = rtl_table_get(RTL8380_TBL_0, 1); + u32 r[18]; + int i; + int block = idx / PIE_BLOCK_SIZE; + u32 t_select = sw_r32(RTL838X_ACL_BLK_TMPLTE_CTRL(block)); + + memset(pr, 0, sizeof(*pr)); + rtl_table_read(q, idx); + for (i = 0; i < 18; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl838x_read_pie_fixed_fields(r, pr); + if (!pr->valid) + return 0; + + pr_info("%s: template_selectors %08x, tid: %d\n", __func__, t_select, pr->tid); + rtl838x_pie_rule_dump_raw(r); + + rtl838x_read_pie_templated(r, pr, fixed_templates[(t_select >> (pr->tid * 3)) & 0x7]); + + rtl838x_read_pie_action(r, pr); + + return 0; +} + +static int rtl838x_pie_rule_write(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr) +{ + // Access IACL table (1) via register 0 + struct table_reg *q = rtl_table_get(RTL8380_TBL_0, 1); + u32 r[18]; + int i, err = 0; + int block = idx / PIE_BLOCK_SIZE; + u32 t_select = sw_r32(RTL838X_ACL_BLK_TMPLTE_CTRL(block)); + + pr_debug("%s: %d, t_select: %08x\n", __func__, idx, t_select); + + for (i = 0; i < 18; i++) + r[i] = 0; + + if (!pr->valid) + goto err_out; + + rtl838x_write_pie_fixed_fields(r, pr); + + pr_debug("%s: template %d\n", __func__, (t_select >> (pr->tid * 3)) & 0x7); + rtl838x_write_pie_templated(r, pr, fixed_templates[(t_select >> (pr->tid * 3)) & 0x7]); + + if (rtl838x_write_pie_action(r, pr)) { + pr_err("Rule actions too complex\n"); + goto err_out; + } + +// rtl838x_pie_rule_dump_raw(r); + + for (i = 0; i < 18; i++) + sw_w32(r[i], rtl_table_data(q, i)); + +err_out: + rtl_table_write(q, idx); + rtl_table_release(q); + + return err; +} + +static bool rtl838x_pie_templ_has(int t, enum template_field_id field_type) +{ + int i; + enum template_field_id ft; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + ft = fixed_templates[t][i]; + if (field_type == ft) + return true; + } + + return false; +} + +static int rtl838x_pie_verify_template(struct rtl838x_switch_priv *priv, + struct pie_rule *pr, int t, int block) +{ + int i; + + if (!pr->is_ipv6 && pr->sip_m && !rtl838x_pie_templ_has(t, TEMPLATE_FIELD_SIP0)) + return -1; + + if (!pr->is_ipv6 && pr->dip_m && !rtl838x_pie_templ_has(t, TEMPLATE_FIELD_DIP0)) + return -1; + + if (pr->is_ipv6) { + if ((pr->sip6_m.s6_addr32[0] || pr->sip6_m.s6_addr32[1] + || pr->sip6_m.s6_addr32[2] || pr->sip6_m.s6_addr32[3]) + && !rtl838x_pie_templ_has(t, TEMPLATE_FIELD_SIP2)) + return -1; + if ((pr->dip6_m.s6_addr32[0] || pr->dip6_m.s6_addr32[1] + || pr->dip6_m.s6_addr32[2] || pr->dip6_m.s6_addr32[3]) + && !rtl838x_pie_templ_has(t, TEMPLATE_FIELD_DIP2)) + return -1; + } + + if (ether_addr_to_u64(pr->smac) && !rtl838x_pie_templ_has(t, TEMPLATE_FIELD_SMAC0)) + return -1; + + if (ether_addr_to_u64(pr->dmac) && !rtl838x_pie_templ_has(t, TEMPLATE_FIELD_DMAC0)) + return -1; + + // TODO: Check more + + i = find_first_zero_bit(&priv->pie_use_bm[block * 4], PIE_BLOCK_SIZE); + + if (i >= PIE_BLOCK_SIZE) + return -1; + + return i + PIE_BLOCK_SIZE * block; +} + +static int rtl838x_pie_rule_add(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx, block, j, t; + + pr_debug("In %s\n", __func__); + + mutex_lock(&priv->pie_mutex); + + for (block = 0; block < priv->n_pie_blocks; block++) { + for (j = 0; j < 3; j++) { + t = (sw_r32(RTL838X_ACL_BLK_TMPLTE_CTRL(block)) >> (j * 3)) & 0x7; + pr_debug("Testing block %d, template %d, template id %d\n", block, j, t); + idx = rtl838x_pie_verify_template(priv, pr, t, block); + if (idx >= 0) + break; + } + if (j < 3) + break; + } + + if (block >= priv->n_pie_blocks) { + mutex_unlock(&priv->pie_mutex); + return -EOPNOTSUPP; + } + + pr_debug("Using block: %d, index %d, template-id %d\n", block, idx, j); + set_bit(idx, priv->pie_use_bm); + + pr->valid = true; + pr->tid = j; // Mapped to template number + pr->tid_m = 0x3; + pr->id = idx; + + rtl838x_pie_lookup_enable(priv, idx); + rtl838x_pie_rule_write(priv, idx, pr); + + mutex_unlock(&priv->pie_mutex); + return 0; +} + +static void rtl838x_pie_rule_rm(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx = pr->id; + + rtl838x_pie_rule_del(priv, idx, idx); + clear_bit(idx, priv->pie_use_bm); +} + +/* + * Initializes the Packet Inspection Engine: + * powers it up, enables default matching templates for all blocks + * and clears all rules possibly installed by u-boot + */ +static void rtl838x_pie_init(struct rtl838x_switch_priv *priv) +{ + int i; + u32 template_selectors; + + mutex_init(&priv->pie_mutex); + + // Enable ACL lookup on all ports, including CPU_PORT + for (i = 0; i <= priv->cpu_port; i++) + sw_w32(1, RTL838X_ACL_PORT_LOOKUP_CTRL(i)); + + // Power on all PIE blocks + for (i = 0; i < priv->n_pie_blocks; i++) + sw_w32_mask(0, BIT(i), RTL838X_ACL_BLK_PWR_CTRL); + + // Include IPG in metering + sw_w32(1, RTL838X_METER_GLB_CTRL); + + // Delete all present rules + rtl838x_pie_rule_del(priv, 0, priv->n_pie_blocks * PIE_BLOCK_SIZE - 1); + + // Routing bypasses source port filter: disable write-protection, first + sw_w32_mask(0, 3, RTL838X_INT_RW_CTRL); + sw_w32_mask(0, 1, RTL838X_DMY_REG27); + sw_w32_mask(3, 0, RTL838X_INT_RW_CTRL); + + // Enable predefined templates 0, 1 and 2 for even blocks + template_selectors = 0 | (1 << 3) | (2 << 6); + for (i = 0; i < 6; i += 2) + sw_w32(template_selectors, RTL838X_ACL_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 0, 3 and 4 (IPv6 support) for odd blocks + template_selectors = 0 | (3 << 3) | (4 << 6); + for (i = 1; i < priv->n_pie_blocks; i += 2) + sw_w32(template_selectors, RTL838X_ACL_BLK_TMPLTE_CTRL(i)); + + // Group each pair of physical blocks together to a logical block + sw_w32(0b10101010101, RTL838X_ACL_BLK_GROUP_CTRL); +} + +static u32 rtl838x_packet_cntr_read(int counter) +{ + u32 v; + + // Read LOG table (3) via register RTL8380_TBL_0 + struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 3); + + pr_debug("In %s, id %d\n", __func__, counter); + rtl_table_read(r, counter / 2); + + pr_debug("Registers: %08x %08x\n", + sw_r32(rtl_table_data(r, 0)), sw_r32(rtl_table_data(r, 1))); + // The table has a size of 2 registers + if (counter % 2) + v = sw_r32(rtl_table_data(r, 0)); + else + v = sw_r32(rtl_table_data(r, 1)); + + rtl_table_release(r); + + return v; +} + +static void rtl838x_packet_cntr_clear(int counter) +{ + // Access LOG table (3) via register RTL8380_TBL_0 + struct table_reg *r = rtl_table_get(RTL8380_TBL_0, 3); + + pr_debug("In %s, id %d\n", __func__, counter); + // The table has a size of 2 registers + if (counter % 2) + sw_w32(0, rtl_table_data(r, 0)); + else + sw_w32(0, rtl_table_data(r, 1)); + + rtl_table_write(r, counter / 2); + + rtl_table_release(r); +} + +static void rtl838x_route_read(int idx, struct rtl83xx_route *rt) +{ + // Read ROUTING table (2) via register RTL8380_TBL_1 + struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 2); + + pr_debug("In %s, id %d\n", __func__, idx); + rtl_table_read(r, idx); + + // The table has a size of 2 registers + rt->nh.gw = sw_r32(rtl_table_data(r, 0)); + rt->nh.gw <<= 32; + rt->nh.gw |= sw_r32(rtl_table_data(r, 1)); + + rtl_table_release(r); +} + +static void rtl838x_route_write(int idx, struct rtl83xx_route *rt) +{ + // Access ROUTING table (2) via register RTL8380_TBL_1 + struct table_reg *r = rtl_table_get(RTL8380_TBL_1, 2); + + pr_debug("In %s, id %d, gw: %016llx\n", __func__, idx, rt->nh.gw); + sw_w32(rt->nh.gw >> 32, rtl_table_data(r, 0)); + sw_w32(rt->nh.gw, rtl_table_data(r, 1)); + rtl_table_write(r, idx); + + rtl_table_release(r); +} + +static int rtl838x_l3_setup(struct rtl838x_switch_priv *priv) +{ + // Nothing to be done + return 0; +} + +void rtl838x_vlan_port_keep_tag_set(int port, bool keep_outer, bool keep_inner) +{ + sw_w32(FIELD_PREP(RTL838X_VLAN_PORT_TAG_STS_CTRL_OTAG_STS_MASK, + keep_outer ? RTL838X_VLAN_PORT_TAG_STS_TAGGED : RTL838X_VLAN_PORT_TAG_STS_UNTAG) | + FIELD_PREP(RTL838X_VLAN_PORT_TAG_STS_CTRL_ITAG_STS_MASK, + keep_inner ? RTL838X_VLAN_PORT_TAG_STS_TAGGED : RTL838X_VLAN_PORT_TAG_STS_UNTAG), + RTL838X_VLAN_PORT_TAG_STS_CTRL(port)); +} + +void rtl838x_vlan_port_pvidmode_set(int port, enum pbvlan_type type, enum pbvlan_mode mode) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0x3, mode, RTL838X_VLAN_PORT_PB_VLAN + (port << 2)); + else + sw_w32_mask(0x3 << 14, mode << 14, RTL838X_VLAN_PORT_PB_VLAN + (port << 2)); +} + +void rtl838x_vlan_port_pvid_set(int port, enum pbvlan_type type, int pvid) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0xfff << 2, pvid << 2, RTL838X_VLAN_PORT_PB_VLAN + (port << 2)); + else + sw_w32_mask(0xfff << 16, pvid << 16, RTL838X_VLAN_PORT_PB_VLAN + (port << 2)); +} + +static int rtl838x_set_ageing_time(unsigned long msec) +{ + int t = sw_r32(RTL838X_L2_CTRL_1); + + t &= 0x7FFFFF; + t = t * 128 / 625; /* Aging time in seconds. 0: L2 aging disabled */ + pr_debug("L2 AGING time: %d sec\n", t); + + t = (msec * 625 + 127000) / 128000; + t = t > 0x7FFFFF ? 0x7FFFFF : t; + sw_w32_mask(0x7FFFFF, t, RTL838X_L2_CTRL_1); + pr_debug("Dynamic aging for ports: %x\n", sw_r32(RTL838X_L2_PORT_AGING_OUT)); + + return 0; +} + +static void rtl838x_set_igr_filter(int port, enum igr_filter state) +{ + sw_w32_mask(0x3 << ((port & 0xf)<<1), state << ((port & 0xf)<<1), + RTL838X_VLAN_PORT_IGR_FLTR + (((port >> 4) << 2))); +} + +static void rtl838x_set_egr_filter(int port, enum egr_filter state) +{ + sw_w32_mask(0x1 << (port % 0x1d), state << (port % 0x1d), + RTL838X_VLAN_PORT_EGR_FLTR + (((port / 29) << 2))); +} + +void rtl838x_set_distribution_algorithm(int group, int algoidx, u32 algomsk) +{ + algoidx &= 1; // RTL838X only supports 2 concurrent algorithms + sw_w32_mask(1 << (group % 8), algoidx << (group % 8), + RTL838X_TRK_HASH_IDX_CTRL + ((group >> 3) << 2)); + sw_w32(algomsk, RTL838X_TRK_HASH_CTRL + (algoidx << 2)); +} + +void rtl838x_set_receive_management_action(int port, rma_ctrl_t type, action_type_t action) +{ + switch(type) { + case BPDU: + sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1), + RTL838X_RMA_BPDU_CTRL + ((port >> 4) << 2)); + break; + case PTP: + sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1), + RTL838X_RMA_PTP_CTRL + ((port >> 4) << 2)); + break; + case LLTP: + sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1), + RTL838X_RMA_LLTP_CTRL + ((port >> 4) << 2)); + break; + default: + break; + } +} + +const struct rtl838x_reg rtl838x_reg = { + .mask_port_reg_be = rtl838x_mask_port_reg, + .set_port_reg_be = rtl838x_set_port_reg, + .get_port_reg_be = rtl838x_get_port_reg, + .mask_port_reg_le = rtl838x_mask_port_reg, + .set_port_reg_le = rtl838x_set_port_reg, + .get_port_reg_le = rtl838x_get_port_reg, + .stat_port_rst = RTL838X_STAT_PORT_RST, + .stat_rst = RTL838X_STAT_RST, + .stat_port_std_mib = RTL838X_STAT_PORT_STD_MIB, + .port_iso_ctrl = rtl838x_port_iso_ctrl, + .traffic_enable = rtl838x_traffic_enable, + .traffic_disable = rtl838x_traffic_disable, + .traffic_get = rtl838x_traffic_get, + .traffic_set = rtl838x_traffic_set, + .l2_ctrl_0 = RTL838X_L2_CTRL_0, + .l2_ctrl_1 = RTL838X_L2_CTRL_1, + .l2_port_aging_out = RTL838X_L2_PORT_AGING_OUT, + .set_ageing_time = rtl838x_set_ageing_time, + .smi_poll_ctrl = RTL838X_SMI_POLL_CTRL, + .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl838x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl838x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl838x_tbl_access_data_0, + .isr_glb_src = RTL838X_ISR_GLB_SRC, + .isr_port_link_sts_chg = RTL838X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL838X_IMR_PORT_LINK_STS_CHG, + .imr_glb = RTL838X_IMR_GLB, + .vlan_tables_read = rtl838x_vlan_tables_read, + .vlan_set_tagged = rtl838x_vlan_set_tagged, + .vlan_set_untagged = rtl838x_vlan_set_untagged, + .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl, + .vlan_profile_dump = rtl838x_vlan_profile_dump, + .vlan_profile_setup = rtl838x_vlan_profile_setup, + .vlan_fwd_on_inner = rtl838x_vlan_fwd_on_inner, + .set_vlan_igr_filter = rtl838x_set_igr_filter, + .set_vlan_egr_filter = rtl838x_set_egr_filter, + .enable_learning = rtl838x_enable_learning, + .enable_flood = rtl838x_enable_flood, + .enable_mcast_flood = rtl838x_enable_mcast_flood, + .enable_bcast_flood = rtl838x_enable_bcast_flood, + .stp_get = rtl838x_stp_get, + .stp_set = rtl838x_stp_set, + .mac_port_ctrl = rtl838x_mac_port_ctrl, + .l2_port_new_salrn = rtl838x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl838x_l2_port_new_sa_fwd, + .mir_ctrl = RTL838X_MIR_CTRL, + .mir_dpm = RTL838X_MIR_DPM_CTRL, + .mir_spm = RTL838X_MIR_SPM_CTRL, + .mac_link_sts = RTL838X_MAC_LINK_STS, + .mac_link_dup_sts = RTL838X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl838x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL838X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL838X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl838x_read_l2_entry_using_hash, + .write_l2_entry_using_hash = rtl838x_write_l2_entry_using_hash, + .read_cam = rtl838x_read_cam, + .write_cam = rtl838x_write_cam, + .vlan_port_keep_tag_set = rtl838x_vlan_port_keep_tag_set, + .vlan_port_pvidmode_set = rtl838x_vlan_port_pvidmode_set, + .vlan_port_pvid_set = rtl838x_vlan_port_pvid_set, + .trk_mbr_ctr = rtl838x_trk_mbr_ctr, + .rma_bpdu_fld_pmask = RTL838X_RMA_BPDU_FLD_PMSK, + .spcl_trap_eapol_ctrl = RTL838X_SPCL_TRAP_EAPOL_CTRL, + .init_eee = rtl838x_init_eee, + .port_eee_set = rtl838x_port_eee_set, + .eee_port_ability = rtl838x_eee_port_ability, + .l2_hash_seed = rtl838x_l2_hash_seed, + .l2_hash_key = rtl838x_l2_hash_key, + .read_mcast_pmask = rtl838x_read_mcast_pmask, + .write_mcast_pmask = rtl838x_write_mcast_pmask, + .pie_init = rtl838x_pie_init, + .pie_rule_read = rtl838x_pie_rule_read, + .pie_rule_write = rtl838x_pie_rule_write, + .pie_rule_add = rtl838x_pie_rule_add, + .pie_rule_rm = rtl838x_pie_rule_rm, + .l2_learning_setup = rtl838x_l2_learning_setup, + .packet_cntr_read = rtl838x_packet_cntr_read, + .packet_cntr_clear = rtl838x_packet_cntr_clear, + .route_read = rtl838x_route_read, + .route_write = rtl838x_route_write, + .l3_setup = rtl838x_l3_setup, + .set_distribution_algorithm = rtl838x_set_distribution_algorithm, + .set_receive_management_action = rtl838x_set_receive_management_action, +}; + +irqreturn_t rtl838x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 status = sw_r32(RTL838X_ISR_GLB_SRC); + u32 ports = sw_r32(RTL838X_ISR_PORT_LINK_STS_CHG); + u32 link; + int i; + + /* Clear status */ + sw_w32(ports, RTL838X_ISR_PORT_LINK_STS_CHG); + pr_info("RTL8380 Link change: status: %x, ports %x\n", status, ports); + + for (i = 0; i < 28; i++) { + if (ports & BIT(i)) { + link = sw_r32(RTL838X_MAC_LINK_STS); + if (link & BIT(i)) + dsa_port_phylink_mac_change(ds, i, true); + else + dsa_port_phylink_mac_change(ds, i, false); + } + } + return IRQ_HANDLED; +} + +int rtl838x_smi_wait_op(int timeout) +{ + int ret = 0; + u32 val; + + ret = readx_poll_timeout(sw_r32, RTL838X_SMI_ACCESS_PHY_CTRL_1, + val, !(val & 0x1), 20, timeout); + if (ret) + pr_err("%s: timeout\n", __func__); + + return ret; +} + +/* + * Reads a register in a page from the PHY + */ +int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + u32 park_page; + + if (port > 31) { + *val = 0xffff; + return 0; + } + + if (page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + park_page = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & ((0x1f << 15) | 0x2); + v = reg << 20 | page << 3; + sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1); + sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +/* + * Write to a register in a page of the PHY + */ +int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + u32 park_page; + + val &= 0xffff; + if (port > 31 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + sw_w32(BIT(port), RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + park_page = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_1) & ((0x1f << 15) | 0x2); + v = reg << 20 | page << 3 | 0x4; + sw_w32(v | park_page, RTL838X_SMI_ACCESS_PHY_CTRL_1); + sw_w32_mask(0, 1, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +/* + * Read an mmd register of a PHY + */ +int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val) +{ + u32 v; + + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + v = addr << 16 | reg; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3); + + /* mmd-access | read | cmd-start */ + v = 1 << 1 | 0 << 2 | 1; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +/* + * Write to an mmd register of a PHY + */ +int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val) +{ + u32 v; + + pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val); + val &= 0xffff; + mutex_lock(&smi_lock); + + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0); + mdelay(10); + + sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2); + + sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3); + sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3); + /* mmd-access | write | cmd-start */ + v = 1 << 1 | 1 << 2 | 1; + sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1); + + if (rtl838x_smi_wait_op(100000)) + goto timeout; + + mutex_unlock(&smi_lock); + return 0; + +timeout: + mutex_unlock(&smi_lock); + return -ETIMEDOUT; +} + +void rtl8380_get_version(struct rtl838x_switch_priv *priv) +{ + u32 rw_save, info_save; + u32 info; + + rw_save = sw_r32(RTL838X_INT_RW_CTRL); + sw_w32(rw_save | 0x3, RTL838X_INT_RW_CTRL); + + info_save = sw_r32(RTL838X_CHIP_INFO); + sw_w32(info_save | 0xA0000000, RTL838X_CHIP_INFO); + + info = sw_r32(RTL838X_CHIP_INFO); + sw_w32(info_save, RTL838X_CHIP_INFO); + sw_w32(rw_save, RTL838X_INT_RW_CTRL); + + if ((info & 0xFFFF) == 0x6275) { + if (((info >> 16) & 0x1F) == 0x1) + priv->version = RTL8380_VERSION_A; + else if (((info >> 16) & 0x1F) == 0x2) + priv->version = RTL8380_VERSION_B; + else + priv->version = RTL8380_VERSION_B; + } else { + priv->version = '-'; + } +} + +void rtl838x_vlan_profile_dump(int profile) +{ + u32 p; + + if (profile < 0 || profile > 7) + return; + + p = sw_r32(RTL838X_VLAN_PROFILE(profile)); + + pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \ + UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d", + profile, p & 1, (p >> 1) & 0x1ff, (p >> 10) & 0x1ff, (p >> 19) & 0x1ff); +} + +void rtl8380_sds_rst(int mac) +{ + u32 offset = (mac == 24) ? 0 : 0x100; + + sw_w32_mask(1 << 11, 0, RTL838X_SDS4_FIB_REG0 + offset); + sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset); + sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset); + sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset); + pr_debug("SERDES reset: %d\n", mac); +} + +int rtl8380_sds_power(int mac, int val) +{ + u32 mode = (val == 1) ? 0x4 : 0x9; + u32 offset = (mac == 24) ? 5 : 0; + + if ((mac != 24) && (mac != 26)) { + pr_err("%s: not a fibre port: %d\n", __func__, mac); + return -1; + } + + sw_w32_mask(0x1f << offset, mode << offset, RTL838X_SDS_MODE_SEL); + + rtl8380_sds_rst(mac); + + return 0; +} diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.h new file mode 100644 index 00000000000..19049e4c957 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl838x.h @@ -0,0 +1,1093 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _RTL838X_H +#define _RTL838X_H + +#include + +/* + * Register definition + */ +#define RTL838X_MAC_PORT_CTRL(port) (0xd560 + (((port) << 7))) +#define RTL839X_MAC_PORT_CTRL(port) (0x8004 + (((port) << 7))) +#define RTL930X_MAC_PORT_CTRL(port) (0x3260 + (((port) << 6))) +#define RTL931X_MAC_PORT_CTRL (0x6004) + +#define RTL930X_MAC_L2_PORT_CTRL(port) (0x3268 + (((port) << 6))) +#define RTL931X_MAC_L2_PORT_CTRL (0x6000) + +#define RTL838X_RST_GLB_CTRL_0 (0x003c) + +#define RTL838X_MAC_FORCE_MODE_CTRL (0xa104) +#define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc) +#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C) +#define RTL931X_MAC_FORCE_MODE_CTRL (0x0DCC) + +#define RTL838X_DMY_REG31 (0x3b28) +#define RTL838X_SDS_MODE_SEL (0x0028) +#define RTL838X_SDS_CFG_REG (0x0034) +#define RTL838X_INT_MODE_CTRL (0x005c) +#define RTL838X_CHIP_INFO (0x00d8) +#define RTL839X_CHIP_INFO (0x0ff4) +#define RTL838X_PORT_ISO_CTRL(port) (0x4100 + ((port) << 2)) +#define RTL839X_PORT_ISO_CTRL(port) (0x1400 + ((port) << 3)) + +/* Packet statistics */ +#define RTL838X_STAT_PORT_STD_MIB (0x1200) +#define RTL839X_STAT_PORT_STD_MIB (0xC000) +#define RTL930X_STAT_PORT_MIB_CNTR (0x0664) +#define RTL838X_STAT_RST (0x3100) +#define RTL839X_STAT_RST (0xF504) +#define RTL930X_STAT_RST (0x3240) +#define RTL931X_STAT_RST (0x7ef4) +#define RTL838X_STAT_PORT_RST (0x3104) +#define RTL839X_STAT_PORT_RST (0xF508) +#define RTL930X_STAT_PORT_RST (0x3244) +#define RTL931X_STAT_PORT_RST (0x7ef8) +#define RTL838X_STAT_CTRL (0x3108) +#define RTL839X_STAT_CTRL (0x04cc) +#define RTL930X_STAT_CTRL (0x3248) +#define RTL931X_STAT_CTRL (0x5720) + +/* Registers of the internal Serdes of the 8390 */ +#define RTL8390_SDS0_1_XSG0 (0xA000) +#define RTL8390_SDS0_1_XSG1 (0xA100) +#define RTL839X_SDS12_13_XSG0 (0xB800) +#define RTL839X_SDS12_13_XSG1 (0xB900) +#define RTL839X_SDS12_13_PWR0 (0xb880) +#define RTL839X_SDS12_13_PWR1 (0xb980) + +/* Registers of the internal Serdes of the 8380 */ +#define RTL838X_SDS4_FIB_REG0 (0xF800) +#define RTL838X_SDS4_REG28 (0xef80) +#define RTL838X_SDS4_DUMMY0 (0xef8c) +#define RTL838X_SDS5_EXT_REG6 (0xf18c) + +/* VLAN registers */ +#define RTL838X_VLAN_CTRL (0x3A74) +#define RTL838X_VLAN_PROFILE(idx) (0x3A88 + ((idx) << 2)) +#define RTL838X_VLAN_PORT_EGR_FLTR (0x3A84) +#define RTL838X_VLAN_PORT_PB_VLAN (0x3C00) +#define RTL838X_VLAN_PORT_IGR_FLTR (0x3A7C) + +#define RTL839X_VLAN_PROFILE(idx) (0x25C0 + (((idx) << 3))) +#define RTL839X_VLAN_CTRL (0x26D4) +#define RTL839X_VLAN_PORT_PB_VLAN (0x26D8) +#define RTL839X_VLAN_PORT_IGR_FLTR (0x27B4) +#define RTL839X_VLAN_PORT_EGR_FLTR (0x27C4) + +#define RTL930X_VLAN_PROFILE_SET(idx) (0x9c60 + (((idx) * 20))) +#define RTL930X_VLAN_CTRL (0x82D4) +#define RTL930X_VLAN_PORT_PB_VLAN (0x82D8) +#define RTL930X_VLAN_PORT_IGR_FLTR (0x83C0) +#define RTL930X_VLAN_PORT_EGR_FLTR (0x83C8) + +#define RTL931X_VLAN_PROFILE_SET(idx) (0x9800 + (((idx) * 28))) +#define RTL931X_VLAN_CTRL (0x94E4) +#define RTL931X_VLAN_PORT_IGR_CTRL (0x94E8) +#define RTL931X_VLAN_PORT_IGR_FLTR (0x96B4) +#define RTL931X_VLAN_PORT_EGR_FLTR (0x96C4) + +/* Table access registers */ +#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) +#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) +#define RTL838X_TBL_ACCESS_CTRL_1 (0xA4C8) +#define RTL838X_TBL_ACCESS_DATA_1(idx) (0xA4CC + ((idx) << 2)) + +#define RTL839X_TBL_ACCESS_CTRL_0 (0x1190) +#define RTL839X_TBL_ACCESS_DATA_0(idx) (0x1194 + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_CTRL_1 (0x6b80) +#define RTL839X_TBL_ACCESS_DATA_1(idx) (0x6b84 + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_CTRL_2 (0x611C) +#define RTL839X_TBL_ACCESS_DATA_2(i) (0x6120 + (((i) << 2))) + +#define RTL930X_TBL_ACCESS_CTRL_0 (0xB340) +#define RTL930X_TBL_ACCESS_DATA_0(idx) (0xB344 + ((idx) << 2)) +#define RTL930X_TBL_ACCESS_CTRL_1 (0xB3A0) +#define RTL930X_TBL_ACCESS_DATA_1(idx) (0xB3A4 + ((idx) << 2)) +#define RTL930X_TBL_ACCESS_CTRL_2 (0xCE04) +#define RTL930X_TBL_ACCESS_DATA_2(i) (0xCE08 + (((i) << 2))) + +#define RTL931X_TBL_ACCESS_CTRL_0 (0x8500) +#define RTL931X_TBL_ACCESS_DATA_0(idx) (0x8508 + ((idx) << 2)) +#define RTL931X_TBL_ACCESS_CTRL_1 (0x40C0) +#define RTL931X_TBL_ACCESS_DATA_1(idx) (0x40C4 + ((idx) << 2)) +#define RTL931X_TBL_ACCESS_CTRL_2 (0x8528) +#define RTL931X_TBL_ACCESS_DATA_2(i) (0x852C + (((i) << 2))) +#define RTL931X_TBL_ACCESS_CTRL_3 (0x0200) +#define RTL931X_TBL_ACCESS_DATA_3(i) (0x0204 + (((i) << 2))) +#define RTL931X_TBL_ACCESS_CTRL_4 (0x20DC) +#define RTL931X_TBL_ACCESS_DATA_4(i) (0x20E0 + (((i) << 2))) +#define RTL931X_TBL_ACCESS_CTRL_5 (0x7E1C) +#define RTL931X_TBL_ACCESS_DATA_5(i) (0x7E20 + (((i) << 2))) + +/* MAC handling */ +#define RTL838X_MAC_LINK_STS (0xa188) +#define RTL839X_MAC_LINK_STS (0x0390) +#define RTL930X_MAC_LINK_STS (0xCB10) +#define RTL931X_MAC_LINK_STS (0x0EC0) +#define RTL838X_MAC_LINK_SPD_STS(p) (0xa190 + (((p >> 4) << 2))) +#define RTL839X_MAC_LINK_SPD_STS(p) (0x03a0 + (((p >> 4) << 2))) +#define RTL930X_MAC_LINK_SPD_STS(p) (0xCB18 + (((p >> 3) << 2))) +#define RTL931X_MAC_LINK_SPD_STS (0x0ED0) +#define RTL838X_MAC_LINK_DUP_STS (0xa19c) +#define RTL839X_MAC_LINK_DUP_STS (0x03b0) +#define RTL930X_MAC_LINK_DUP_STS (0xCB28) +#define RTL931X_MAC_LINK_DUP_STS (0x0EF0) +#define RTL838X_MAC_TX_PAUSE_STS (0xa1a0) +#define RTL839X_MAC_TX_PAUSE_STS (0x03b8) +#define RTL930X_MAC_TX_PAUSE_STS (0xCB2C) +#define RTL931X_MAC_TX_PAUSE_STS (0x0EF8) +#define RTL838X_MAC_RX_PAUSE_STS (0xa1a4) +#define RTL839X_MAC_RX_PAUSE_STS (0x03c0) +#define RTL930X_MAC_RX_PAUSE_STS (0xCB30) +#define RTL931X_MAC_RX_PAUSE_STS (0x0F00) +#define RTL930X_MAC_LINK_MEDIA_STS (0xCB14) +#define RTL931X_MAC_LINK_MEDIA_STS (0x0EC8) + +/* MAC link state bits */ +#define RTL838X_FORCE_EN (1 << 0) +#define RTL838X_FORCE_LINK_EN (1 << 1) +#define RTL838X_NWAY_EN (1 << 2) +#define RTL838X_DUPLEX_MODE (1 << 3) +#define RTL838X_TX_PAUSE_EN (1 << 6) +#define RTL838X_RX_PAUSE_EN (1 << 7) +#define RTL838X_MAC_FORCE_FC_EN (1 << 8) + +#define RTL839X_FORCE_EN (1 << 0) +#define RTL839X_FORCE_LINK_EN (1 << 1) +#define RTL839X_DUPLEX_MODE (1 << 2) +#define RTL839X_TX_PAUSE_EN (1 << 5) +#define RTL839X_RX_PAUSE_EN (1 << 6) +#define RTL839X_MAC_FORCE_FC_EN (1 << 7) + +#define RTL930X_FORCE_EN (1 << 0) +#define RTL930X_FORCE_LINK_EN (1 << 1) +#define RTL930X_DUPLEX_MODE (1 << 2) +#define RTL930X_TX_PAUSE_EN (1 << 7) +#define RTL930X_RX_PAUSE_EN (1 << 8) +#define RTL930X_MAC_FORCE_FC_EN (1 << 9) + +#define RTL931X_FORCE_EN (1 << 9) +#define RTL931X_FORCE_LINK_EN (1 << 0) +#define RTL931X_DUPLEX_MODE (1 << 2) +#define RTL931X_MAC_FORCE_FC_EN (1 << 4) +#define RTL931X_TX_PAUSE_EN (1 << 16) +#define RTL931X_RX_PAUSE_EN (1 << 17) + +/* EEE */ +#define RTL838X_MAC_EEE_ABLTY (0xa1a8) +#define RTL838X_EEE_PORT_TX_EN (0x014c) +#define RTL838X_EEE_PORT_RX_EN (0x0150) +#define RTL838X_EEE_CLK_STOP_CTRL (0x0148) +#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) +#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) + +#define RTL839X_EEE_TX_TIMER_GELITE_CTRL (0x042C) +#define RTL839X_EEE_TX_TIMER_GIGA_CTRL (0x0430) +#define RTL839X_EEE_TX_TIMER_10G_CTRL (0x0434) +#define RTL839X_EEE_CTRL(p) (0x8008 + ((p) << 7)) +#define RTL839X_MAC_EEE_ABLTY (0x03C8) + +#define RTL930X_MAC_EEE_ABLTY (0xCB34) +#define RTL930X_EEE_CTRL(p) (0x3274 + ((p) << 6)) +#define RTL930X_EEEP_PORT_CTRL(p) (0x3278 + ((p) << 6)) + +/* L2 functionality */ +#define RTL838X_L2_CTRL_0 (0x3200) +#define RTL839X_L2_CTRL_0 (0x3800) +#define RTL930X_L2_CTRL (0x8FD8) +#define RTL931X_L2_CTRL (0xC800) +#define RTL838X_L2_CTRL_1 (0x3204) +#define RTL839X_L2_CTRL_1 (0x3804) +#define RTL930X_L2_AGE_CTRL (0x8FDC) +#define RTL931X_L2_AGE_CTRL (0xC804) +#define RTL838X_L2_PORT_AGING_OUT (0x3358) +#define RTL839X_L2_PORT_AGING_OUT (0x3b74) +#define RTL930X_L2_PORT_AGE_CTRL (0x8FE0) +#define RTL931X_L2_PORT_AGE_CTRL (0xc808) +#define RTL838X_TBL_ACCESS_L2_CTRL (0x6900) +#define RTL839X_TBL_ACCESS_L2_CTRL (0x1180) +#define RTL930X_TBL_ACCESS_L2_CTRL (0xB320) +#define RTL930X_TBL_ACCESS_L2_METHOD_CTRL (0xB324) +#define RTL838X_TBL_ACCESS_L2_DATA(idx) (0x6908 + ((idx) << 2)) +#define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2)) +#define RTL930X_TBL_ACCESS_L2_DATA(idx) (0xab08 + ((idx) << 2)) + +#define RTL838X_L2_TBL_FLUSH_CTRL (0x3370) +#define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0) +#define RTL930X_L2_TBL_FLUSH_CTRL (0x9404) +#define RTL931X_L2_TBL_FLUSH_CTRL (0xCD9C) + +#define RTL838X_L2_LRN_CONSTRT (0x329C) +#define RTL839X_L2_LRN_CONSTRT (0x3910) +#define RTL930X_L2_LRN_CONSTRT_CTRL (0x909c) +#define RTL931X_L2_LRN_CONSTRT_CTRL (0xC964) + +#define RTL838X_L2_FLD_PMSK (0x3288) +#define RTL839X_L2_FLD_PMSK (0x38EC) +#define RTL930X_L2_BC_FLD_PMSK (0x9068) +#define RTL931X_L2_BC_FLD_PMSK (0xC8FC) + +#define RTL930X_L2_UNKN_UC_FLD_PMSK (0x9064) +#define RTL931X_L2_UNKN_UC_FLD_PMSK (0xC8F4) + +#define RTL838X_L2_LRN_CONSTRT_EN (0x3368) +#define RTL838X_L2_PORT_LRN_CONSTRT (0x32A0) +#define RTL839X_L2_PORT_LRN_CONSTRT (0x3914) + +#define RTL838X_L2_PORT_NEW_SALRN(p) (0x328c + (((p >> 4) << 2))) +#define RTL839X_L2_PORT_NEW_SALRN(p) (0x38F0 + (((p >> 4) << 2))) +#define RTL930X_L2_PORT_SALRN(p) (0x8FEC + (((p >> 4) << 2))) +#define RTL931X_L2_PORT_NEW_SALRN(p) (0xC820 + (((p >> 4) << 2))) + +#define SALRN_PORT_SHIFT(p) ((p % 16) * 2) +#define SALRN_MODE_MASK 0x3 +#define SALRN_MODE_HARDWARE 0 +#define SALRN_MODE_DISABLED 2 + +#define RTL838X_L2_PORT_NEW_SA_FWD(p) (0x3294 + (((p >> 4) << 2))) +#define RTL839X_L2_PORT_NEW_SA_FWD(p) (0x3900 + (((p >> 4) << 2))) +#define RTL930X_L2_PORT_NEW_SA_FWD(p) (0x8FF4 + (((p / 10) << 2))) +#define RTL931X_L2_PORT_NEW_SA_FWD(p) (0xC830 + (((p / 10) << 2))) + +#define RTL930X_ST_CTRL (0x8798) + +#define RTL930X_L2_PORT_SABLK_CTRL (0x905c) +#define RTL930X_L2_PORT_DABLK_CTRL (0x9060) + +#define RTL838X_L2_PORT_LM_ACT(p) (0x3208 + ((p) << 2)) +#define RTL838X_VLAN_PORT_FWD (0x3A78) +#define RTL839X_VLAN_PORT_FWD (0x27AC) +#define RTL930X_VLAN_PORT_FWD (0x834C) +#define RTL931X_VLAN_PORT_FWD (0x95CC) +#define RTL838X_VLAN_FID_CTRL (0x3aa8) + +/* Port Mirroring */ +#define RTL838X_MIR_CTRL (0x5D00) +#define RTL838X_MIR_DPM_CTRL (0x5D20) +#define RTL838X_MIR_SPM_CTRL (0x5D10) + +#define RTL839X_MIR_CTRL (0x2500) +#define RTL839X_MIR_DPM_CTRL (0x2530) +#define RTL839X_MIR_SPM_CTRL (0x2510) + +#define RTL930X_MIR_CTRL (0xA2A0) +#define RTL930X_MIR_DPM_CTRL (0xA2C0) +#define RTL930X_MIR_SPM_CTRL (0xA2B0) + +#define RTL931X_MIR_CTRL (0xAF00) +#define RTL931X_MIR_DPM_CTRL (0xAF30) +#define RTL931X_MIR_SPM_CTRL (0xAF10) + +/* Storm/rate control and scheduling */ +#define RTL838X_STORM_CTRL (0x4700) +#define RTL839X_STORM_CTRL (0x1800) +#define RTL838X_STORM_CTRL_LB_CTRL(p) (0x4884 + (((p) << 2))) +#define RTL838X_STORM_CTRL_BURST_PPS_0 (0x4874) +#define RTL838X_STORM_CTRL_BURST_PPS_1 (0x4878) +#define RTL838X_STORM_CTRL_BURST_0 (0x487c) +#define RTL838X_STORM_CTRL_BURST_1 (0x4880) +#define RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0 (0x1804) +#define RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1 (0x1808) +#define RTL838X_SCHED_CTRL (0xB980) +#define RTL839X_SCHED_CTRL (0x60F4) +#define RTL838X_SCHED_LB_TICK_TKN_CTRL_0 (0xAD58) +#define RTL838X_SCHED_LB_TICK_TKN_CTRL_1 (0xAD5C) +#define RTL839X_SCHED_LB_TICK_TKN_CTRL_0 (0x1804) +#define RTL839X_SCHED_LB_TICK_TKN_CTRL_1 (0x1808) +#define RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL (0x2000) +#define RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0 (0x1604) +#define RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1 (0x1608) +#define RTL839X_SCHED_LB_TICK_TKN_CTRL (0x60F8) +#define RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL (0x6200) +#define RTL838X_SCHED_LB_THR (0xB984) +#define RTL839X_SCHED_LB_THR (0x60FC) +#define RTL838X_SCHED_P_EGR_RATE_CTRL(p) (0xC008 + (((p) << 7))) +#define RTL838X_SCHED_Q_EGR_RATE_CTRL(p, q) (0xC00C + (p << 7) + (((q) << 2))) +#define RTL838X_STORM_CTRL_PORT_BC_EXCEED (0x470C) +#define RTL838X_STORM_CTRL_PORT_MC_EXCEED (0x4710) +#define RTL838X_STORM_CTRL_PORT_UC_EXCEED (0x4714) +#define RTL839X_STORM_CTRL_PORT_BC_EXCEED(p) (0x180c + (((p >> 5) << 2))) +#define RTL839X_STORM_CTRL_PORT_MC_EXCEED(p) (0x1814 + (((p >> 5) << 2))) +#define RTL839X_STORM_CTRL_PORT_UC_EXCEED(p) (0x181c + (((p >> 5) << 2))) +#define RTL838X_STORM_CTRL_PORT_UC(p) (0x4718 + (((p) << 2))) +#define RTL838X_STORM_CTRL_PORT_MC(p) (0x478c + (((p) << 2))) +#define RTL838X_STORM_CTRL_PORT_BC(p) (0x4800 + (((p) << 2))) +#define RTL839X_STORM_CTRL_PORT_UC_0(p) (0x185C + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_UC_1(p) (0x1860 + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_MC_0(p) (0x19FC + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_MC_1(p) (0x1a00 + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_BC_0(p) (0x1B9C + (((p) << 3))) +#define RTL839X_STORM_CTRL_PORT_BC_1(p) (0x1BA0 + (((p) << 3))) +#define RTL839X_TBL_ACCESS_CTRL_2 (0x611C) +#define RTL839X_TBL_ACCESS_DATA_2(i) (0x6120 + (((i) << 2))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p) (0x1618 + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_10G_1(p) (0x161C + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_0(p) (0x1640 + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_PORT_CTRL_1(p) (0x1644 + (((p) << 3))) +#define RTL839X_IGR_BWCTRL_CTRL_LB_THR (0x1614) + +/* Link aggregation (Trunking) */ +#define TRUNK_DISTRIBUTION_ALGO_SPA_BIT 0x01 +#define TRUNK_DISTRIBUTION_ALGO_SMAC_BIT 0x02 +#define TRUNK_DISTRIBUTION_ALGO_DMAC_BIT 0x04 +#define TRUNK_DISTRIBUTION_ALGO_SIP_BIT 0x08 +#define TRUNK_DISTRIBUTION_ALGO_DIP_BIT 0x10 +#define TRUNK_DISTRIBUTION_ALGO_SRC_L4PORT_BIT 0x20 +#define TRUNK_DISTRIBUTION_ALGO_DST_L4PORT_BIT 0x40 +#define TRUNK_DISTRIBUTION_ALGO_MASKALL 0x7F + +#define TRUNK_DISTRIBUTION_ALGO_L2_SPA_BIT 0x01 +#define TRUNK_DISTRIBUTION_ALGO_L2_SMAC_BIT 0x02 +#define TRUNK_DISTRIBUTION_ALGO_L2_DMAC_BIT 0x04 +#define TRUNK_DISTRIBUTION_ALGO_L2_VLAN_BIT 0x08 +#define TRUNK_DISTRIBUTION_ALGO_L2_MASKALL 0xF + +#define TRUNK_DISTRIBUTION_ALGO_L3_SPA_BIT 0x01 +#define TRUNK_DISTRIBUTION_ALGO_L3_SMAC_BIT 0x02 +#define TRUNK_DISTRIBUTION_ALGO_L3_DMAC_BIT 0x04 +#define TRUNK_DISTRIBUTION_ALGO_L3_VLAN_BIT 0x08 +#define TRUNK_DISTRIBUTION_ALGO_L3_SIP_BIT 0x10 +#define TRUNK_DISTRIBUTION_ALGO_L3_DIP_BIT 0x20 +#define TRUNK_DISTRIBUTION_ALGO_L3_SRC_L4PORT_BIT 0x40 +#define TRUNK_DISTRIBUTION_ALGO_L3_DST_L4PORT_BIT 0x80 +#define TRUNK_DISTRIBUTION_ALGO_L3_PROTO_BIT 0x100 +#define TRUNK_DISTRIBUTION_ALGO_L3_FLOW_LABEL_BIT 0x200 +#define TRUNK_DISTRIBUTION_ALGO_L3_MASKALL 0x3FF + +#define RTL838X_TRK_MBR_CTR (0x3E00) +#define RTL838X_TRK_HASH_IDX_CTRL (0x3E20) +#define RTL838X_TRK_HASH_CTRL (0x3E24) + +#define RTL839X_TRK_MBR_CTR (0x2200) +#define RTL839X_TRK_HASH_IDX_CTRL (0x2280) +#define RTL839X_TRK_HASH_CTRL (0x2284) + +#define RTL930X_TRK_MBR_CTRL (0xA41C) +#define RTL930X_TRK_HASH_CTRL (0x9F80) + +#define RTL931X_TRK_MBR_CTRL (0xB8D0) +#define RTL931X_TRK_HASH_CTRL (0xBA70) + +/* Attack prevention */ +#define RTL838X_ATK_PRVNT_PORT_EN (0x5B00) +#define RTL838X_ATK_PRVNT_CTRL (0x5B04) +#define RTL838X_ATK_PRVNT_ACT (0x5B08) +#define RTL838X_ATK_PRVNT_STS (0x5B1C) + +/* 802.1X */ +#define RTL838X_RMA_BPDU_FLD_PMSK (0x4348) +#define RTL930X_RMA_BPDU_FLD_PMSK (0x9F18) +#define RTL931X_RMA_BPDU_FLD_PMSK (0x8950) +#define RTL839X_RMA_BPDU_FLD_PMSK (0x125C) + +#define RTL838X_SPCL_TRAP_CTRL (0x6980) +#define RTL838X_SPCL_TRAP_EAPOL_CTRL (0x6988) +#define RTL838X_SPCL_TRAP_ARP_CTRL (0x698C) +#define RTL838X_SPCL_TRAP_IGMP_CTRL (0x6984) +#define RTL838X_SPCL_TRAP_IPV6_CTRL (0x6994) +#define RTL838X_SPCL_TRAP_SWITCH_MAC_CTRL (0x6998) + +#define RTL839X_SPCL_TRAP_CTRL (0x1054) +#define RTL839X_SPCL_TRAP_EAPOL_CTRL (0x105C) +#define RTL839X_SPCL_TRAP_ARP_CTRL (0x1060) +#define RTL839X_SPCL_TRAP_IGMP_CTRL (0x1058) +#define RTL839X_SPCL_TRAP_IPV6_CTRL (0x1064) +#define RTL839X_SPCL_TRAP_SWITCH_MAC_CTRL (0x1068) +#define RTL839X_SPCL_TRAP_SWITCH_IPV4_ADDR_CTRL (0x106C) +#define RTL839X_SPCL_TRAP_CRC_CTRL (0x1070) +/* special port action controls */ +/* + values: + 0 = FORWARD (default) + 1 = DROP + 2 = TRAP2CPU + 3 = FLOOD IN ALL PORT + + Register encoding. + offset = CTRL + (port >> 4) << 2 + value/mask = 3 << ((port&0xF) << 1) +*/ + +typedef enum { + BPDU = 0, + PTP, + PTP_UDP, + PTP_ETH2, + LLTP, + EAPOL, + GRATARP, +} rma_ctrl_t; + +typedef enum { + FORWARD = 0, + DROP, + TRAP2CPU, + FLOODALL, + TRAP2MASTERCPU, + COPY2CPU, +} action_type_t; + +#define RTL838X_RMA_BPDU_CTRL (0x4330) +#define RTL839X_RMA_BPDU_CTRL (0x122C) +#define RTL930X_RMA_BPDU_CTRL (0x9E7C) +#define RTL931X_RMA_BPDU_CTRL (0x881C) + +#define RTL838X_RMA_PTP_CTRL (0x4338) +#define RTL839X_RMA_PTP_CTRL (0x123C) +#define RTL930X_RMA_PTP_CTRL (0x9E88) +#define RTL931X_RMA_PTP_CTRL (0x8834) + +#define RTL838X_RMA_LLTP_CTRL (0x4340) +#define RTL839X_RMA_LLTP_CTRL (0x124C) +#define RTL930X_RMA_LLTP_CTRL (0x9EFC) +#define RTL931X_RMA_LLTP_CTRL (0x8918) + +#define RTL930X_RMA_EAPOL_CTRL (0x9F08) +#define RTL931X_RMA_EAPOL_CTRL (0x8930) +#define RTL931X_TRAP_ARP_GRAT_PORT_ACT (0x8C04) + +/* QoS */ +#define RTL838X_QM_INTPRI2QID_CTRL (0x5F00) +#define RTL839X_QM_INTPRI2QID_CTRL(q) (0x1110 + (q << 2)) +#define RTL839X_QM_PORT_QNUM(p) (0x1130 + (((p / 10) << 2))) +#define RTL838X_PRI_SEL_PORT_PRI(p) (0x5FB8 + (((p / 10) << 2))) +#define RTL839X_PRI_SEL_PORT_PRI(p) (0x10A8 + (((p / 10) << 2))) +#define RTL838X_QM_PKT2CPU_INTPRI_MAP (0x5F10) +#define RTL839X_QM_PKT2CPU_INTPRI_MAP (0x1154) +#define RTL838X_PRI_SEL_CTRL (0x10E0) +#define RTL839X_PRI_SEL_CTRL (0x10E0) +#define RTL838X_PRI_SEL_TBL_CTRL(i) (0x5FD8 + (((i) << 2))) +#define RTL839X_PRI_SEL_TBL_CTRL(i) (0x10D0 + (((i) << 2))) +#define RTL838X_QM_PKT2CPU_INTPRI_0 (0x5F04) +#define RTL838X_QM_PKT2CPU_INTPRI_1 (0x5F08) +#define RTL838X_QM_PKT2CPU_INTPRI_2 (0x5F0C) +#define RTL839X_OAM_CTRL (0x2100) +#define RTL839X_OAM_PORT_ACT_CTRL(p) (0x2104 + (((p) << 2))) +#define RTL839X_RMK_PORT_DEI_TAG_CTRL(p) (0x6A9C + (((p >> 5) << 2))) +#define RTL839X_PRI_SEL_IPRI_REMAP (0x1080) +#define RTL838X_PRI_SEL_IPRI_REMAP (0x5F8C) +#define RTL839X_PRI_SEL_DEI2DP_REMAP (0x10EC) +#define RTL839X_PRI_SEL_DSCP2DP_REMAP_ADDR(i) (0x10F0 + (((i >> 4) << 2))) +#define RTL839X_RMK_DEI_CTRL (0x6AA4) +#define RTL839X_WRED_PORT_THR_CTRL(i) (0x6084 + ((i) << 2)) +#define RTL839X_WRED_QUEUE_THR_CTRL(q, i) (0x6090 + ((q) * 12) + ((i) << 2)) +#define RTL838X_PRI_DSCP_INVLD_CTRL0 (0x5FE8) +#define RTL838X_RMK_IPRI_CTRL (0xA460) +#define RTL838X_RMK_OPRI_CTRL (0xA464) +#define RTL838X_SCHED_P_TYPE_CTRL(p) (0xC04C + (((p) << 7))) +#define RTL838X_SCHED_LB_CTRL(p) (0xC004 + (((p) << 7))) +#define RTL838X_FC_P_EGR_DROP_CTRL(p) (0x6B1C + (((p) << 2))) + +/* Debug features */ +#define RTL930X_STAT_PRVTE_DROP_COUNTER0 (0xB5B8) + +/* Packet Inspection Engine */ +#define RTL838X_METER_GLB_CTRL (0x4B08) +#define RTL839X_METER_GLB_CTRL (0x1300) +#define RTL930X_METER_GLB_CTRL (0xa0a0) +#define RTL931X_METER_GLB_CTRL (0x411C) + +#define RTL839X_ACL_CTRL (0x1288) + +#define RTL838X_ACL_BLK_LOOKUP_CTRL (0x6100) +#define RTL839X_ACL_BLK_LOOKUP_CTRL (0x1280) +#define RTL930X_PIE_BLK_LOOKUP_CTRL (0xa5a0) +#define RTL931X_PIE_BLK_LOOKUP_CTRL (0x4180) + +#define RTL838X_ACL_BLK_PWR_CTRL (0x6104) +#define RTL839X_PS_ACL_PWR_CTRL (0x049c) + +#define RTL838X_ACL_BLK_TMPLTE_CTRL(block) (0x6108 + ((block) << 2)) +#define RTL839X_ACL_BLK_TMPLTE_CTRL(block) (0x128c + ((block) << 2)) +#define RTL930X_PIE_BLK_TMPLTE_CTRL(block) (0xa624 + ((block) << 2)) +#define RTL931X_PIE_BLK_TMPLTE_CTRL(block) (0x4214 + ((block) << 2)) + +#define RTL838X_ACL_BLK_GROUP_CTRL (0x615C) +#define RTL839X_ACL_BLK_GROUP_CTRL (0x12ec) + +#define RTL838X_ACL_CLR_CTRL (0x6168) +#define RTL839X_ACL_CLR_CTRL (0x12fc) +#define RTL930X_PIE_CLR_CTRL (0xa66c) +#define RTL931X_PIE_CLR_CTRL (0x42D8) + +#define RTL838X_DMY_REG27 (0x3378) + +#define RTL838X_ACL_PORT_LOOKUP_CTRL(p) (0x616C + (((p) << 2))) +#define RTL930X_ACL_PORT_LOOKUP_CTRL(p) (0xA784 + (((p) << 2))) +#define RTL931X_ACL_PORT_LOOKUP_CTRL(p) (0x44F8 + (((p) << 2))) + +#define RTL930X_PIE_BLK_PHASE_CTRL (0xA5A4) +#define RTL931X_PIE_BLK_PHASE_CTRL (0x4184) + +// PIE actions +#define PIE_ACT_COPY_TO_PORT 2 +#define PIE_ACT_REDIRECT_TO_PORT 4 +#define PIE_ACT_ROUTE_UC 6 +#define PIE_ACT_VID_ASSIGN 0 + +// L3 actions +#define L3_FORWARD 0 +#define L3_DROP 1 +#define L3_TRAP2CPU 2 +#define L3_COPY2CPU 3 +#define L3_TRAP2MASTERCPU 4 +#define L3_COPY2MASTERCPU 5 +#define L3_HARDDROP 6 + +// Route actions +#define ROUTE_ACT_FORWARD 0 +#define ROUTE_ACT_TRAP2CPU 1 +#define ROUTE_ACT_COPY2CPU 2 +#define ROUTE_ACT_DROP 3 + +/* L3 Routing */ +#define RTL839X_ROUTING_SA_CTRL 0x6afc +#define RTL930X_L3_HOST_TBL_CTRL (0xAB48) +#define RTL930X_L3_IPUC_ROUTE_CTRL (0xAB4C) +#define RTL930X_L3_IP6UC_ROUTE_CTRL (0xAB50) +#define RTL930X_L3_IPMC_ROUTE_CTRL (0xAB54) +#define RTL930X_L3_IP6MC_ROUTE_CTRL (0xAB58) +#define RTL930X_L3_IP_MTU_CTRL(i) (0xAB5C + ((i >> 1) << 2)) +#define RTL930X_L3_IP6_MTU_CTRL(i) (0xAB6C + ((i >> 1) << 2)) +#define RTL930X_L3_HW_LU_KEY_CTRL (0xAC9C) +#define RTL930X_L3_HW_LU_KEY_IP_CTRL (0xACA0) +#define RTL930X_L3_HW_LU_CTRL (0xACC0) +#define RTL930X_L3_IP_ROUTE_CTRL 0xab44 + +/* Port LED Control */ +#define RTL930X_LED_PORT_NUM_CTRL(p) (0xCC04 + (((p >> 4) << 2))) +#define RTL930X_LED_SET0_0_CTRL (0xCC28) +#define RTL930X_LED_PORT_COPR_SET_SEL_CTRL(p) (0xCC2C + (((p >> 4) << 2))) +#define RTL930X_LED_PORT_FIB_SET_SEL_CTRL(p) (0xCC34 + (((p >> 4) << 2))) +#define RTL930X_LED_PORT_COPR_MASK_CTRL (0xCC3C) +#define RTL930X_LED_PORT_FIB_MASK_CTRL (0xCC40) +#define RTL930X_LED_PORT_COMBO_MASK_CTRL (0xCC44) + +#define RTL931X_LED_PORT_NUM_CTRL(p) (0x0604 + (((p >> 4) << 2))) +#define RTL931X_LED_SET0_0_CTRL (0x0630) +#define RTL931X_LED_PORT_COPR_SET_SEL_CTRL(p) (0x0634 + (((p >> 4) << 2))) +#define RTL931X_LED_PORT_FIB_SET_SEL_CTRL(p) (0x0644 + (((p >> 4) << 2))) +#define RTL931X_LED_PORT_COPR_MASK_CTRL (0x0654) +#define RTL931X_LED_PORT_FIB_MASK_CTRL (0x065c) +#define RTL931X_LED_PORT_COMBO_MASK_CTRL (0x0664) + +#define MAX_VLANS 4096 +#define MAX_LAGS 16 +#define MAX_PRIOS 8 +#define RTL930X_PORT_IGNORE 0x3f +#define MAX_MC_GROUPS 512 +#define UNKNOWN_MC_PMASK (MAX_MC_GROUPS - 1) +#define PIE_BLOCK_SIZE 128 +#define MAX_PIE_ENTRIES (18 * PIE_BLOCK_SIZE) +#define N_FIXED_FIELDS 12 +#define N_FIXED_FIELDS_RTL931X 14 +#define MAX_COUNTERS 2048 +#define MAX_ROUTES 512 +#define MAX_HOST_ROUTES 1536 +#define MAX_INTF_MTUS 8 +#define DEFAULT_MTU 1536 +#define MAX_INTERFACES 100 +#define MAX_ROUTER_MACS 64 +#define L3_EGRESS_DMACS 2048 +#define MAX_SMACS 64 + +enum phy_type { + PHY_NONE = 0, + PHY_RTL838X_SDS = 1, + PHY_RTL8218B_INT = 2, + PHY_RTL8218B_EXT = 3, + PHY_RTL8214FC = 4, + PHY_RTL839X_SDS = 5, + PHY_RTL930X_SDS = 6, +}; + +enum pbvlan_type { + PBVLAN_TYPE_INNER = 0, + PBVLAN_TYPE_OUTER, +}; + +enum pbvlan_mode { + PBVLAN_MODE_UNTAG_AND_PRITAG = 0, + PBVLAN_MODE_UNTAG_ONLY, + PBVLAN_MODE_ALL_PKT, +}; + +struct rtl838x_port { + bool enable; + u64 pm; + u16 pvid; + bool eee_enabled; + enum phy_type phy; + bool phy_is_integrated; + bool is10G; + bool is2G5; + int sds_num; + int led_set; + const struct dsa_port *dp; +}; + +struct rtl838x_vlan_info { + u64 untagged_ports; + u64 tagged_ports; + u8 profile_id; + bool hash_mc_fid; + bool hash_uc_fid; + u8 fid; // AKA MSTI + + // The following fields are used only by the RTL931X + int if_id; // Interface (index in L3_EGR_INTF_IDX) + u16 multicast_grp_mask; + int l2_tunnel_list_id; +}; + +enum l2_entry_type { + L2_INVALID = 0, + L2_UNICAST = 1, + L2_MULTICAST = 2, + IP4_MULTICAST = 3, + IP6_MULTICAST = 4, +}; + +struct rtl838x_l2_entry { + u8 mac[6]; + u16 vid; + u16 rvid; + u8 port; + bool valid; + enum l2_entry_type type; + bool is_static; + bool is_ip_mc; + bool is_ipv6_mc; + bool block_da; + bool block_sa; + bool suspended; + bool next_hop; + int age; + u8 trunk; + bool is_trunk; + u8 stack_dev; + u16 mc_portmask_index; + u32 mc_gip; + u32 mc_sip; + u16 mc_mac_index; + u16 nh_route_id; + bool nh_vlan_target; // Only RTL83xx: VLAN used for next hop + + // The following is only valid on RTL931x + bool is_open_flow; + bool is_pe_forward; + bool is_local_forward; + bool is_remote_forward; + bool is_l2_tunnel; + int l2_tunnel_id; + int l2_tunnel_list_id; +}; + +enum fwd_rule_action { + FWD_RULE_ACTION_NONE = 0, + FWD_RULE_ACTION_FWD = 1, +}; + +enum pie_phase { + PHASE_VACL = 0, + PHASE_IACL = 1, +}; + +enum igr_filter { + IGR_FORWARD = 0, + IGR_DROP = 1, + IGR_TRAP = 2, +}; + +enum egr_filter { + EGR_DISABLE = 0, + EGR_ENABLE = 1, +}; + +/* Intermediate representation of a Packet Inspection Engine Rule + * as suggested by the Kernel's tc flower offload subsystem + * Field meaning is universal across SoC families, but data content is specific + * to SoC family (e.g. because of different port ranges) */ +struct pie_rule { + int id; + enum pie_phase phase; // Phase in which this template is applied + int packet_cntr; // ID of a packet counter assigned to this rule + int octet_cntr; // ID of a byte counter assigned to this rule + u32 last_packet_cnt; + u64 last_octet_cnt; + + // The following are requirements for the pie template + bool is_egress; + bool is_ipv6; // This is a rule with IPv6 fields + + // Fixed fields that are always matched against on RTL8380 + u8 spmmask_fix; + u8 spn; // Source port number + bool stacking_port; // Source port is stacking port + bool mgnt_vlan; // Packet arrived on management VLAN + bool dmac_hit_sw; // The packet's destination MAC matches one of the device's + bool content_too_deep; // The content of the packet cannot be parsed: too many layers + bool not_first_frag; // Not the first IP fragment + u8 frame_type_l4; // 0: UDP, 1: TCP, 2: ICMP/ICMPv6, 3: IGMP + u8 frame_type; // 0: ARP, 1: L2 only, 2: IPv4, 3: IPv6 + bool otag_fmt; // 0: outer tag packet, 1: outer priority tag or untagged + bool itag_fmt; // 0: inner tag packet, 1: inner priority tag or untagged + bool otag_exist; // packet with outer tag + bool itag_exist; // packet with inner tag + bool frame_type_l2; // 0: Ethernet, 1: LLC_SNAP, 2: LLC_Other, 3: Reserved + bool igr_normal_port; // Ingress port is not cpu or stacking port + u8 tid; // The template ID defining the what the templated fields mean + + // Masks for the fields that are always matched against on RTL8380 + u8 spmmask_fix_m; + u8 spn_m; + bool stacking_port_m; + bool mgnt_vlan_m; + bool dmac_hit_sw_m; + bool content_too_deep_m; + bool not_first_frag_m; + u8 frame_type_l4_m; + u8 frame_type_m; + bool otag_fmt_m; + bool itag_fmt_m; + bool otag_exist_m; + bool itag_exist_m; + bool frame_type_l2_m; + bool igr_normal_port_m; + u8 tid_m; + + // Logical operations between rules, special rules for rule numbers apply + bool valid; + bool cond_not; // Matches when conditions not match + bool cond_and1; // And this rule 2n with the next rule 2n+1 in same block + bool cond_and2; // And this rule m in block 2n with rule m in block 2n+1 + bool ivalid; + + // Actions to be performed + bool drop; // Drop the packet + bool fwd_sel; // Forward packet: to port, portmask, dest route, next rule, drop + bool ovid_sel; // So something to outer vlan-id: shift, re-assign + bool ivid_sel; // Do something to inner vlan-id: shift, re-assign + bool flt_sel; // Filter the packet when sending to certain ports + bool log_sel; // Log the packet in one of the LOG-table counters + bool rmk_sel; // Re-mark the packet, i.e. change the priority-tag + bool meter_sel; // Meter the packet, i.e. limit rate of this type of packet + bool tagst_sel; // Change the ergress tag + bool mir_sel; // Mirror the packet to a Link Aggregation Group + bool nopri_sel; // Change the normal priority + bool cpupri_sel; // Change the CPU priority + bool otpid_sel; // Change Outer Tag Protocol Identifier (802.1q) + bool itpid_sel; // Change Inner Tag Protocol Identifier (802.1q) + bool shaper_sel; // Apply traffic shaper + bool mpls_sel; // MPLS actions + bool bypass_sel; // Bypass actions + bool fwd_sa_lrn; // Learn the source address when forwarding + bool fwd_mod_to_cpu; // Forward the modified VLAN tag format to CPU-port + + // Fields used in predefined templates 0-2 on RTL8380 / 90 / 9300 + u64 spm; // Source Port Matrix + u16 otag; // Outer VLAN-ID + u8 smac[ETH_ALEN]; // Source MAC address + u8 dmac[ETH_ALEN]; // Destination MAC address + u16 ethertype; // Ethernet frame type field in ethernet header + u16 itag; // Inner VLAN-ID + u16 field_range_check; + u32 sip; // Source IP + struct in6_addr sip6; // IPv6 Source IP + u32 dip; // Destination IP + struct in6_addr dip6; // IPv6 Destination IP + u16 tos_proto; // IPv4: TOS + Protocol fields, IPv6: Traffic class + next header + u16 sport; // TCP/UDP source port + u16 dport; // TCP/UDP destination port + u16 icmp_igmp; + u16 tcp_info; + u16 dsap_ssap; // Destination / Source Service Access Point bytes (802.3) + + u64 spm_m; + u16 otag_m; + u8 smac_m[ETH_ALEN]; + u8 dmac_m[ETH_ALEN]; + u8 ethertype_m; + u16 itag_m; + u16 field_range_check_m; + u32 sip_m; + struct in6_addr sip6_m; // IPv6 Source IP mask + u32 dip_m; + struct in6_addr dip6_m; // IPv6 Destination IP mask + u16 tos_proto_m; + u16 sport_m; + u16 dport_m; + u16 icmp_igmp_m; + u16 tcp_info_m; + u16 dsap_ssap_m; + + // Data associated with actions + u8 fwd_act; // Type of forwarding action + // 0: permit, 1: drop, 2: copy to port id, 4: copy to portmask + // 4: redirect to portid, 5: redirect to portmask + // 6: route, 7: vlan leaky (only 8380) + u16 fwd_data; // Additional data for forwarding action, e.g. destination port + u8 ovid_act; + u16 ovid_data; // Outer VLAN ID + u8 ivid_act; + u16 ivid_data; // Inner VLAN ID + u16 flt_data; // Filtering data + u16 log_data; // ID of packet or octet counter in LOG table, on RTL93xx + // unnecessary since PIE-Rule-ID == LOG-counter-ID + bool log_octets; + u8 mpls_act; // MPLS action type + u16 mpls_lib_idx; // MPLS action data + + u16 rmk_data; // Data for remarking + u16 meter_data; // ID of meter for bandwidth control + u16 tagst_data; + u16 mir_data; + u16 nopri_data; + u16 cpupri_data; + u16 otpid_data; + u16 itpid_data; + u16 shaper_data; + + // Bypass actions, ignored on RTL8380 + bool bypass_all; // Not clear + bool bypass_igr_stp; // Bypass Ingress STP state + bool bypass_ibc_sc; // Bypass Ingress Bandwidth Control and Storm Control +}; + +struct rtl838x_l3_intf { + u16 vid; + u8 smac_idx; + u8 ip4_mtu_id; + u8 ip6_mtu_id; + u16 ip4_mtu; + u16 ip6_mtu; + u8 ttl_scope; + u8 hl_scope; + u8 ip4_icmp_redirect; + u8 ip6_icmp_redirect; + u8 ip4_pbr_icmp_redirect; + u8 ip6_pbr_icmp_redirect; +}; + +/* + * An entry in the RTL93XX SoC's ROUTER_MAC tables setting up a termination point + * for the L3 routing system. Packets arriving and matching an entry in this table + * will be considered for routing. + * Mask fields state whether the corresponding data fields matter for matching + */ +struct rtl93xx_rt_mac { + bool valid; // Valid or not + bool p_type; // Individual (0) or trunk (1) port + bool p_mask; // Whether the port type is used + u8 p_id; + u8 p_id_mask; // Mask for the port + u8 action; // Routing action performed: 0: FORWARD, 1: DROP, 2: TRAP2CPU + // 3: COPY2CPU, 4: TRAP2MASTERCPU, 5: COPY2MASTERCPU, 6: HARDDROP + u16 vid; + u16 vid_mask; + u64 mac; // MAC address used as source MAC in the routed packet + u64 mac_mask; +}; + +struct rtl83xx_nexthop { + u16 id; // ID: L3_NEXT_HOP table-index or route-index set in L2_NEXT_HOP + u32 dev_id; + u16 port; + u16 vid; // VLAN-ID for L2 table entry (saved from L2-UC entry) + u16 rvid; // Relay VID/FID for the L2 table entry + u64 mac; // The MAC address of the entry in the L2_NEXT_HOP table + u16 mac_id; + u16 l2_id; // Index of this next hop forwarding entry in L2 FIB table + u64 gw; // The gateway MAC address packets are forwarded to + int if_id; // Interface (into L3_EGR_INTF_IDX) +}; + +struct rtl838x_switch_priv; + +struct rtl83xx_flow { + unsigned long cookie; + struct rhash_head node; + struct rcu_head rcu_head; + struct rtl838x_switch_priv *priv; + struct pie_rule rule; + u32 flags; +}; + +struct rtl93xx_route_attr { + bool valid; + bool hit; + bool ttl_dec; + bool ttl_check; + bool dst_null; + bool qos_as; + u8 qos_prio; + u8 type; + u8 action; +}; + +struct rtl83xx_route { + u32 gw_ip; // IP of the route's gateway + u32 dst_ip; // IP of the destination net + struct in6_addr dst_ip6; + int prefix_len; // Network prefix len of the destination net + bool is_host_route; + int id; // ID number of this route + struct rhlist_head linkage; + u16 switch_mac_id; // Index into switch's own MACs, RTL839X only + struct rtl83xx_nexthop nh; + struct pie_rule pr; + struct rtl93xx_route_attr attr; +}; + +struct rtl838x_reg { + void (*mask_port_reg_be)(u64 clear, u64 set, int reg); + void (*set_port_reg_be)(u64 set, int reg); + u64 (*get_port_reg_be)(int reg); + void (*mask_port_reg_le)(u64 clear, u64 set, int reg); + void (*set_port_reg_le)(u64 set, int reg); + u64 (*get_port_reg_le)(int reg); + int stat_port_rst; + int stat_rst; + int stat_port_std_mib; + int (*port_iso_ctrl)(int p); + void (*traffic_enable)(int source, int dest); + void (*traffic_disable)(int source, int dest); + void (*traffic_set)(int source, u64 dest_matrix); + u64 (*traffic_get)(int source); + int l2_ctrl_0; + int l2_ctrl_1; + int smi_poll_ctrl; + u32 l2_port_aging_out; + int l2_tbl_flush_ctrl; + void (*exec_tbl0_cmd)(u32 cmd); + void (*exec_tbl1_cmd)(u32 cmd); + int (*tbl_access_data_0)(int i); + int isr_glb_src; + int isr_port_link_sts_chg; + int imr_port_link_sts_chg; + int imr_glb; + void (*vlan_tables_read)(u32 vlan, struct rtl838x_vlan_info *info); + void (*vlan_set_tagged)(u32 vlan, struct rtl838x_vlan_info *info); + void (*vlan_set_untagged)(u32 vlan, u64 portmask); + void (*vlan_profile_dump)(int index); + void (*vlan_profile_setup)(int profile); + void (*vlan_port_pvidmode_set)(int port, enum pbvlan_type type, enum pbvlan_mode mode); + void (*vlan_port_pvid_set)(int port, enum pbvlan_type type, int pvid); + void (*vlan_port_keep_tag_set)(int port, bool keep_outer, bool keep_inner); + void (*set_vlan_igr_filter)(int port, enum igr_filter state); + void (*set_vlan_egr_filter)(int port, enum egr_filter state); + void (*enable_learning)(int port, bool enable); + void (*enable_flood)(int port, bool enable); + void (*enable_mcast_flood)(int port, bool enable); + void (*enable_bcast_flood)(int port, bool enable); + void (*stp_get)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]); + void (*stp_set)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]); + int (*mac_force_mode_ctrl)(int port); + int (*mac_port_ctrl)(int port); + int (*l2_port_new_salrn)(int port); + int (*l2_port_new_sa_fwd)(int port); + int (*set_ageing_time)(unsigned long msec); + int mir_ctrl; + int mir_dpm; + int mir_spm; + int mac_link_sts; + int mac_link_dup_sts; + int (*mac_link_spd_sts)(int port); + int mac_rx_pause_sts; + int mac_tx_pause_sts; + u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e); + void (*write_l2_entry_using_hash)(u32 hash, u32 pos, struct rtl838x_l2_entry *e); + u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e); + void (*write_cam)(int idx, struct rtl838x_l2_entry *e); + int (*trk_mbr_ctr)(int group); + int rma_bpdu_fld_pmask; + int spcl_trap_eapol_ctrl; + void (*init_eee)(struct rtl838x_switch_priv *priv, bool enable); + void (*port_eee_set)(struct rtl838x_switch_priv *priv, int port, bool enable); + int (*eee_port_ability)(struct rtl838x_switch_priv *priv, + struct ethtool_eee *e, int port); + u64 (*l2_hash_seed)(u64 mac, u32 vid); + u32 (*l2_hash_key)(struct rtl838x_switch_priv *priv, u64 seed); + u64 (*read_mcast_pmask)(int idx); + void (*write_mcast_pmask)(int idx, u64 portmask); + void (*vlan_fwd_on_inner)(int port, bool is_set); + void (*pie_init)(struct rtl838x_switch_priv *priv); + int (*pie_rule_read)(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr); + int (*pie_rule_write)(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr); + int (*pie_rule_add)(struct rtl838x_switch_priv *priv, struct pie_rule *rule); + void (*pie_rule_rm)(struct rtl838x_switch_priv *priv, struct pie_rule *rule); + void (*l2_learning_setup)(void); + u32 (*packet_cntr_read)(int counter); + void (*packet_cntr_clear)(int counter); + void (*route_read)(int idx, struct rtl83xx_route *rt); + void (*route_write)(int idx, struct rtl83xx_route *rt); + void (*host_route_write)(int idx, struct rtl83xx_route *rt); + int (*l3_setup)(struct rtl838x_switch_priv *priv); + void (*set_l3_nexthop)(int idx, u16 dmac_id, u16 interface); + void (*get_l3_nexthop)(int idx, u16 *dmac_id, u16 *interface); + u64 (*get_l3_egress_mac)(u32 idx); + void (*set_l3_egress_mac)(u32 idx, u64 mac); + int (*find_l3_slot)(struct rtl83xx_route *rt, bool must_exist); + int (*route_lookup_hw)(struct rtl83xx_route *rt); + void (*get_l3_router_mac)(u32 idx, struct rtl93xx_rt_mac *m); + void (*set_l3_router_mac)(u32 idx, struct rtl93xx_rt_mac *m); + void (*set_l3_egress_intf)(int idx, struct rtl838x_l3_intf *intf); + void (*set_distribution_algorithm)(int group, int algoidx, u32 algomask); + void (*set_receive_management_action)(int port, rma_ctrl_t type, action_type_t action); + void (*led_init)(struct rtl838x_switch_priv *priv); +}; + +struct rtl838x_switch_priv { + /* Switch operation */ + struct dsa_switch *ds; + struct device *dev; + u16 id; + u16 family_id; + char version; + struct rtl838x_port ports[57]; + struct mutex reg_mutex; // Mutex for individual register manipulations + struct mutex pie_mutex; // Mutex for Packet Inspection Engine + int link_state_irq; + int mirror_group_ports[4]; + struct mii_bus *mii_bus; + const struct rtl838x_reg *r; + u8 cpu_port; + u8 port_mask; + u8 port_width; + u8 port_ignore; + u64 irq_mask; + u32 fib_entries; + int l2_bucket_size; + struct dentry *dbgfs_dir; + int n_lags; + u64 lags_port_members[MAX_LAGS]; + struct net_device *lag_devs[MAX_LAGS]; + u32 lag_primary[MAX_LAGS]; + u32 is_lagmember[57]; + u64 lagmembers; + struct notifier_block nb; // TODO: change to different name + struct notifier_block ne_nb; + struct notifier_block fib_nb; + bool eee_enabled; + unsigned long int mc_group_bm[MAX_MC_GROUPS >> 5]; + int mc_group_saves[MAX_MC_GROUPS]; + int n_pie_blocks; + struct rhashtable tc_ht; + unsigned long int pie_use_bm[MAX_PIE_ENTRIES >> 5]; + int n_counters; + unsigned long int octet_cntr_use_bm[MAX_COUNTERS >> 5]; + unsigned long int packet_cntr_use_bm[MAX_COUNTERS >> 4]; + struct rhltable routes; + unsigned long int route_use_bm[MAX_ROUTES >> 5]; + unsigned long int host_route_use_bm[MAX_HOST_ROUTES >> 5]; + struct rtl838x_l3_intf *interfaces[MAX_INTERFACES]; + u16 intf_mtus[MAX_INTF_MTUS]; + int intf_mtu_count[MAX_INTF_MTUS]; +}; + +void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv); +void rtl930x_dbgfs_init(struct rtl838x_switch_priv *priv); + +#endif /* _RTL838X_H */ diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl839x.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl839x.c new file mode 100644 index 00000000000..986a4b5f45c --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl839x.c @@ -0,0 +1,1937 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "rtl83xx.h" + +#define RTL839X_VLAN_PORT_TAG_STS_UNTAG 0x0 +#define RTL839X_VLAN_PORT_TAG_STS_TAGGED 0x1 +#define RTL839X_VLAN_PORT_TAG_STS_PRIORITY_TAGGED 0x2 + +#define RTL839X_VLAN_PORT_TAG_STS_CTRL_BASE 0x6828 +/* port 0-52 */ +#define RTL839X_VLAN_PORT_TAG_STS_CTRL(port) \ + RTL839X_VLAN_PORT_TAG_STS_CTRL_BASE + (port << 2) +#define RTL839X_VLAN_PORT_TAG_STS_CTRL_OTAG_STS_MASK GENMASK(7,6) +#define RTL839X_VLAN_PORT_TAG_STS_CTRL_ITAG_STS_MASK GENMASK(5,4) +#define RTL839X_VLAN_PORT_TAG_STS_CTRL_EGR_P_OTAG_KEEP_MASK GENMASK(3,3) +#define RTL839X_VLAN_PORT_TAG_STS_CTRL_EGR_P_ITAG_KEEP_MASK GENMASK(2,2) +#define RTL839X_VLAN_PORT_TAG_STS_CTRL_IGR_P_OTAG_KEEP_MASK GENMASK(1,1) +#define RTL839X_VLAN_PORT_TAG_STS_CTRL_IGR_P_ITAG_KEEP_MASK GENMASK(0,0) + +extern struct mutex smi_lock; +extern struct rtl83xx_soc_info soc_info; + +/* Definition of the RTL839X-specific template field IDs as used in the PIE */ +enum template_field_id { + TEMPLATE_FIELD_SPMMASK = 0, + TEMPLATE_FIELD_SPM0 = 1, // Source portmask ports 0-15 + TEMPLATE_FIELD_SPM1 = 2, // Source portmask ports 16-31 + TEMPLATE_FIELD_SPM2 = 3, // Source portmask ports 32-47 + TEMPLATE_FIELD_SPM3 = 4, // Source portmask ports 48-56 + TEMPLATE_FIELD_DMAC0 = 5, // Destination MAC [15:0] + TEMPLATE_FIELD_DMAC1 = 6, // Destination MAC [31:16] + TEMPLATE_FIELD_DMAC2 = 7, // Destination MAC [47:32] + TEMPLATE_FIELD_SMAC0 = 8, // Source MAC [15:0] + TEMPLATE_FIELD_SMAC1 = 9, // Source MAC [31:16] + TEMPLATE_FIELD_SMAC2 = 10, // Source MAC [47:32] + TEMPLATE_FIELD_ETHERTYPE = 11, // Ethernet frame type field + // Field-ID 12 is not used + TEMPLATE_FIELD_OTAG = 13, + TEMPLATE_FIELD_ITAG = 14, + TEMPLATE_FIELD_SIP0 = 15, + TEMPLATE_FIELD_SIP1 = 16, + TEMPLATE_FIELD_DIP0 = 17, + TEMPLATE_FIELD_DIP1 = 18, + TEMPLATE_FIELD_IP_TOS_PROTO = 19, + TEMPLATE_FIELD_IP_FLAG = 20, + TEMPLATE_FIELD_L4_SPORT = 21, + TEMPLATE_FIELD_L4_DPORT = 22, + TEMPLATE_FIELD_L34_HEADER = 23, + TEMPLATE_FIELD_ICMP_IGMP = 24, + TEMPLATE_FIELD_VID_RANG0 = 25, + TEMPLATE_FIELD_VID_RANG1 = 26, + TEMPLATE_FIELD_L4_PORT_RANG = 27, + TEMPLATE_FIELD_FIELD_SELECTOR_VALID = 28, + TEMPLATE_FIELD_FIELD_SELECTOR_0 = 29, + TEMPLATE_FIELD_FIELD_SELECTOR_1 = 30, + TEMPLATE_FIELD_FIELD_SELECTOR_2 = 31, + TEMPLATE_FIELD_FIELD_SELECTOR_3 = 32, + TEMPLATE_FIELD_FIELD_SELECTOR_4 = 33, + TEMPLATE_FIELD_FIELD_SELECTOR_5 = 34, + TEMPLATE_FIELD_SIP2 = 35, + TEMPLATE_FIELD_SIP3 = 36, + TEMPLATE_FIELD_SIP4 = 37, + TEMPLATE_FIELD_SIP5 = 38, + TEMPLATE_FIELD_SIP6 = 39, + TEMPLATE_FIELD_SIP7 = 40, + TEMPLATE_FIELD_OLABEL = 41, + TEMPLATE_FIELD_ILABEL = 42, + TEMPLATE_FIELD_OILABEL = 43, + TEMPLATE_FIELD_DPMMASK = 44, + TEMPLATE_FIELD_DPM0 = 45, + TEMPLATE_FIELD_DPM1 = 46, + TEMPLATE_FIELD_DPM2 = 47, + TEMPLATE_FIELD_DPM3 = 48, + TEMPLATE_FIELD_L2DPM0 = 49, + TEMPLATE_FIELD_L2DPM1 = 50, + TEMPLATE_FIELD_L2DPM2 = 51, + TEMPLATE_FIELD_L2DPM3 = 52, + TEMPLATE_FIELD_IVLAN = 53, + TEMPLATE_FIELD_OVLAN = 54, + TEMPLATE_FIELD_FWD_VID = 55, + TEMPLATE_FIELD_DIP2 = 56, + TEMPLATE_FIELD_DIP3 = 57, + TEMPLATE_FIELD_DIP4 = 58, + TEMPLATE_FIELD_DIP5 = 59, + TEMPLATE_FIELD_DIP6 = 60, + TEMPLATE_FIELD_DIP7 = 61, +}; + +// Number of fixed templates predefined in the SoC +#define N_FIXED_TEMPLATES 5 +static enum template_field_id fixed_templates[N_FIXED_TEMPLATES][N_FIXED_FIELDS] = +{ + { + TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1, TEMPLATE_FIELD_ITAG, + TEMPLATE_FIELD_SMAC0, TEMPLATE_FIELD_SMAC1, TEMPLATE_FIELD_SMAC2, + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_SPM2, TEMPLATE_FIELD_SPM3 + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, + TEMPLATE_FIELD_DIP1,TEMPLATE_FIELD_IP_TOS_PROTO, TEMPLATE_FIELD_L4_SPORT, + TEMPLATE_FIELD_L4_DPORT, TEMPLATE_FIELD_ICMP_IGMP, TEMPLATE_FIELD_SPM0, + TEMPLATE_FIELD_SPM1, TEMPLATE_FIELD_SPM2, TEMPLATE_FIELD_SPM3 + }, { + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_ITAG, TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_IP_TOS_PROTO, + TEMPLATE_FIELD_L4_DPORT, TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_SIP0, + TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, TEMPLATE_FIELD_DIP1 + }, { + TEMPLATE_FIELD_DIP0, TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_DIP2, + TEMPLATE_FIELD_DIP3, TEMPLATE_FIELD_DIP4, TEMPLATE_FIELD_DIP5, + TEMPLATE_FIELD_DIP6, TEMPLATE_FIELD_DIP7, TEMPLATE_FIELD_L4_DPORT, + TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_ICMP_IGMP, TEMPLATE_FIELD_IP_TOS_PROTO + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_SIP2, + TEMPLATE_FIELD_SIP3, TEMPLATE_FIELD_SIP4, TEMPLATE_FIELD_SIP5, + TEMPLATE_FIELD_SIP6, TEMPLATE_FIELD_SIP7, TEMPLATE_FIELD_SPM0, + TEMPLATE_FIELD_SPM1, TEMPLATE_FIELD_SPM2, TEMPLATE_FIELD_SPM3 + }, +}; + +void rtl839x_print_matrix(void) +{ + volatile u64 *ptr9; + int i; + + ptr9 = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); + for (i = 0; i < 52; i += 4) + pr_debug("> %16llx %16llx %16llx %16llx\n", + ptr9[i + 0], ptr9[i + 1], ptr9[i + 2], ptr9[i + 3]); + pr_debug("CPU_PORT> %16llx\n", ptr9[52]); +} + +static inline int rtl839x_port_iso_ctrl(int p) +{ + return RTL839X_PORT_ISO_CTRL(p); +} + +static inline void rtl839x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_0) & BIT(16)); +} + +static inline void rtl839x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_1) & BIT(16)); +} + +inline void rtl839x_exec_tbl2_cmd(u32 cmd) +{ + sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_2); + do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_2) & (1 << 9)); +} + +static inline int rtl839x_tbl_access_data_0(int i) +{ + return RTL839X_TBL_ACCESS_DATA_0(i); +} + +static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 u, v, w; + // Read VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0); + + rtl_table_read(r, vlan); + u = sw_r32(rtl_table_data(r, 0)); + v = sw_r32(rtl_table_data(r, 1)); + w = sw_r32(rtl_table_data(r, 2)); + rtl_table_release(r); + + info->tagged_ports = u; + info->tagged_ports = (info->tagged_ports << 21) | ((v >> 11) & 0x1fffff); + info->profile_id = w >> 30 | ((v & 1) << 2); + info->hash_mc_fid = !!(w & BIT(2)); + info->hash_uc_fid = !!(w & BIT(3)); + info->fid = (v >> 3) & 0xff; + + // Read UNTAG table (0) via table register 1 + r = rtl_table_get(RTL8390_TBL_1, 0); + rtl_table_read(r, vlan); + u = sw_r32(rtl_table_data(r, 0)); + v = sw_r32(rtl_table_data(r, 1)); + rtl_table_release(r); + + info->untagged_ports = u; + info->untagged_ports = (info->untagged_ports << 21) | ((v >> 11) & 0x1fffff); +} + +static void rtl839x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 u, v, w; + // Access VLAN table (0) via register 0 + struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 0); + + u = info->tagged_ports >> 21; + v = info->tagged_ports << 11; + v |= ((u32)info->fid) << 3; + v |= info->hash_uc_fid ? BIT(2) : 0; + v |= info->hash_mc_fid ? BIT(1) : 0; + v |= (info->profile_id & 0x4) ? 1 : 0; + w = ((u32)(info->profile_id & 3)) << 30; + + sw_w32(u, rtl_table_data(r, 0)); + sw_w32(v, rtl_table_data(r, 1)); + sw_w32(w, rtl_table_data(r, 2)); + + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +static void rtl839x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + u32 u, v; + + // Access UNTAG table (0) via table register 1 + struct table_reg *r = rtl_table_get(RTL8390_TBL_1, 0); + + u = portmask >> 21; + v = portmask << 11; + + sw_w32(u, rtl_table_data(r, 0)); + sw_w32(v, rtl_table_data(r, 1)); + rtl_table_write(r, vlan); + + rtl_table_release(r); +} + +/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer + */ +static void rtl839x_vlan_fwd_on_inner(int port, bool is_set) +{ + if (is_set) + rtl839x_mask_port_reg_be(BIT_ULL(port), 0ULL, RTL839X_VLAN_PORT_FWD); + else + rtl839x_mask_port_reg_be(0ULL, BIT_ULL(port), RTL839X_VLAN_PORT_FWD); +} + +/* + * Hash seed is vid (actually rvid) concatenated with the MAC address + */ +static u64 rtl839x_l2_hash_seed(u64 mac, u32 vid) +{ + u64 v = vid; + + v <<= 48; + v |= mac; + + return v; +} + +/* + * Applies the same hash algorithm as the one used currently by the ASIC to the seed + * and returns a key into the L2 hash table + */ +static u32 rtl839x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 h1, h2, h; + + if (sw_r32(priv->r->l2_ctrl_0) & 1) { + h1 = (u32) (((seed >> 60) & 0x3f) ^ ((seed >> 54) & 0x3f) + ^ ((seed >> 36) & 0x3f) ^ ((seed >> 30) & 0x3f) + ^ ((seed >> 12) & 0x3f) ^ ((seed >> 6) & 0x3f)); + h2 = (u32) (((seed >> 48) & 0x3f) ^ ((seed >> 42) & 0x3f) + ^ ((seed >> 24) & 0x3f) ^ ((seed >> 18) & 0x3f) + ^ (seed & 0x3f)); + h = (h1 << 6) | h2; + } else { + h = (seed >> 60) + ^ ((((seed >> 48) & 0x3f) << 6) | ((seed >> 54) & 0x3f)) + ^ ((seed >> 36) & 0xfff) ^ ((seed >> 24) & 0xfff) + ^ ((seed >> 12) & 0xfff) ^ (seed & 0xfff); + } + + return h; +} + +static inline int rtl839x_mac_force_mode_ctrl(int p) +{ + return RTL839X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl839x_mac_port_ctrl(int p) +{ + return RTL839X_MAC_PORT_CTRL(p); +} + +static inline int rtl839x_l2_port_new_salrn(int p) +{ + return RTL839X_L2_PORT_NEW_SALRN(p); +} + +static inline int rtl839x_l2_port_new_sa_fwd(int p) +{ + return RTL839X_L2_PORT_NEW_SA_FWD(p); +} + +static inline int rtl839x_mac_link_spd_sts(int p) +{ + return RTL839X_MAC_LINK_SPD_STS(p); +} + +static inline int rtl839x_trk_mbr_ctr(int group) +{ + return RTL839X_TRK_MBR_CTR + (group << 3); +} + +static void rtl839x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) +{ + /* Table contains different entry types, we need to identify the right one: + * Check for MC entries, first + */ + e->is_ip_mc = !!(r[2] & BIT(31)); + e->is_ipv6_mc = !!(r[2] & BIT(30)); + e->type = L2_INVALID; + if (!e->is_ip_mc && !e->is_ipv6_mc) { + e->mac[0] = (r[0] >> 12); + e->mac[1] = (r[0] >> 4); + e->mac[2] = ((r[1] >> 28) | (r[0] << 4)); + e->mac[3] = (r[1] >> 20); + e->mac[4] = (r[1] >> 12); + e->mac[5] = (r[1] >> 4); + + e->vid = (r[2] >> 4) & 0xfff; + e->rvid = (r[0] >> 20) & 0xfff; + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + e->is_static = !!((r[2] >> 18) & 1); + e->port = (r[2] >> 24) & 0x3f; + e->block_da = !!(r[2] & (1 << 19)); + e->block_sa = !!(r[2] & (1 << 20)); + e->suspended = !!(r[2] & (1 << 17)); + e->next_hop = !!(r[2] & (1 << 16)); + if (e->next_hop) { + pr_debug("Found next hop entry, need to read data\n"); + e->nh_vlan_target = !!(r[2] & BIT(15)); + e->nh_route_id = (r[2] >> 4) & 0x1ff; + e->vid = e->rvid; + } + e->age = (r[2] >> 21) & 3; + e->valid = true; + if (!(r[2] & 0xc0fd0000)) /* Check for valid entry */ + e->valid = false; + else + e->type = L2_UNICAST; + } else { + e->valid = true; + e->type = L2_MULTICAST; + e->mc_portmask_index = (r[2] >> 6) & 0xfff; + e->vid = e->rvid; + } + } else { // IPv4 and IPv6 multicast + e->vid = e->rvid = (r[0] << 20) & 0xfff; + e->mc_gip = r[1]; + e->mc_portmask_index = (r[2] >> 6) & 0xfff; + } + if (e->is_ip_mc) { + e->valid = true; + e->type = IP4_MULTICAST; + } + if (e->is_ipv6_mc) { + e->valid = true; + e->type = IP6_MULTICAST; + } + // pr_info("%s: vid %d, rvid: %d\n", __func__, e->vid, e->rvid); +} + +/* + * Fills the 3 SoC table registers r[] with the information in the rtl838x_l2_entry + */ +static void rtl839x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e) +{ + if (!e->valid) { + r[0] = r[1] = r[2] = 0; + return; + } + + r[2] = e->is_ip_mc ? BIT(31) : 0; + r[2] |= e->is_ipv6_mc ? BIT(30) : 0; + + if (!e->is_ip_mc && !e->is_ipv6_mc) { + r[0] = ((u32)e->mac[0]) << 12; + r[0] |= ((u32)e->mac[1]) << 4; + r[0] |= ((u32)e->mac[2]) >> 4; + r[1] = ((u32)e->mac[2]) << 28; + r[1] |= ((u32)e->mac[3]) << 20; + r[1] |= ((u32)e->mac[4]) << 12; + r[1] |= ((u32)e->mac[5]) << 4; + + if (!(e->mac[0] & 1)) { // Not multicast + r[2] |= e->is_static ? BIT(18) : 0; + r[0] |= ((u32)e->rvid) << 20; + r[2] |= e->port << 24; + r[2] |= e->block_da ? BIT(19) : 0; + r[2] |= e->block_sa ? BIT(20) : 0; + r[2] |= e->suspended ? BIT(17) : 0; + r[2] |= ((u32)e->age) << 21; + if (e->next_hop) { + r[2] |= BIT(16); + r[2] |= e->nh_vlan_target ? BIT(15) : 0; + r[2] |= (e->nh_route_id & 0x7ff) << 4; + } else { + r[2] |= e->vid << 4; + } + pr_debug("Write L2 NH: %08x %08x %08x\n", r[0], r[1], r[2]); + } else { // L2 Multicast + r[0] |= ((u32)e->rvid) << 20; + r[2] |= ((u32)e->mc_portmask_index) << 6; + } + } else { // IPv4 or IPv6 MC entry + r[0] = ((u32)e->rvid) << 20; + r[1] = e->mc_gip; + r[2] |= ((u32)e->mc_portmask_index) << 6; + } +} + +/* + * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table + * hash is the id of the bucket and pos is the position of the entry in that bucket + * The data read from the SoC is filled into rtl838x_l2_entry + */ +static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 0); + u32 idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket + int i; + + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl839x_fill_l2_entry(r, e); + if (!e->valid) + return 0; + + return rtl839x_l2_hash_seed(ether_addr_to_u64(&e->mac[0]), e->rvid); +} + +static void rtl839x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 0); + int i; + + u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket + + rtl839x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl839x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl839x_fill_l2_entry(r, e); + if (!e->valid) + return 0; + + pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]); + + // Return MAC with concatenated VID ac concatenated ID + return rtl839x_l2_hash_seed(ether_addr_to_u64(&e->mac[0]), e->rvid); +} + +static void rtl839x_write_cam(int idx, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl839x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl839x_read_mcast_pmask(int idx) +{ + u64 portmask; + // Read MC_PMSK (2) via register RTL8390_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2); + + rtl_table_read(q, idx); + portmask = sw_r32(rtl_table_data(q, 0)); + portmask <<= 32; + portmask |= sw_r32(rtl_table_data(q, 1)); + portmask >>= 11; // LSB is bit 11 in data registers + rtl_table_release(q); + + return portmask; +} + +static void rtl839x_write_mcast_pmask(int idx, u64 portmask) +{ + // Access MC_PMSK (2) via register RTL8380_TBL_L2 + struct table_reg *q = rtl_table_get(RTL8390_TBL_L2, 2); + + portmask <<= 11; // LSB is bit 11 in data registers + sw_w32((u32)(portmask >> 32), rtl_table_data(q, 0)); + sw_w32((u32)((portmask & 0xfffff800)), rtl_table_data(q, 1)); + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static void rtl839x_vlan_profile_setup(int profile) +{ + u32 p[2]; + u32 pmask_id = UNKNOWN_MC_PMASK; + + p[0] = pmask_id; // Use portmaks 0xfff for unknown IPv6 MC flooding + // Enable L2 Learning BIT 0, portmask UNKNOWN_MC_PMASK for IP/L2-MC traffic flooding + p[1] = 1 | pmask_id << 1 | pmask_id << 13; + + sw_w32(p[0], RTL839X_VLAN_PROFILE(profile)); + sw_w32(p[1], RTL839X_VLAN_PROFILE(profile) + 4); + + rtl839x_write_mcast_pmask(UNKNOWN_MC_PMASK, 0x001fffffffffffff); +} + +u64 rtl839x_traffic_get(int source) +{ + return rtl839x_get_port_reg_be(rtl839x_port_iso_ctrl(source)); +} + +void rtl839x_traffic_set(int source, u64 dest_matrix) +{ + rtl839x_set_port_reg_be(dest_matrix, rtl839x_port_iso_ctrl(source)); +} + +void rtl839x_traffic_enable(int source, int dest) +{ + rtl839x_mask_port_reg_be(0, BIT_ULL(dest), rtl839x_port_iso_ctrl(source)); +} + +void rtl839x_traffic_disable(int source, int dest) +{ + rtl839x_mask_port_reg_be(BIT_ULL(dest), 0, rtl839x_port_iso_ctrl(source)); +} + +static void rtl839x_l2_learning_setup(void) +{ + /* Set portmask for broadcast (offset bit 12) and unknown unicast (offset 0) + * address flooding to the reserved entry in the portmask table used + * also for multicast flooding */ + sw_w32(UNKNOWN_MC_PMASK << 12 | UNKNOWN_MC_PMASK, RTL839X_L2_FLD_PMSK); + + // Limit learning to maximum: 32k entries, after that just flood (bits 0-1) + sw_w32((0x7fff << 2) | 0, RTL839X_L2_LRN_CONSTRT); + + // Do not trap ARP packets to CPU_PORT + sw_w32(0, RTL839X_SPCL_TRAP_ARP_CTRL); +} + +static void rtl839x_enable_learning(int port, bool enable) +{ + // Limit learning to maximum: 32k entries + + sw_w32_mask(0x7fff << 2, enable ? (0x7fff << 2) : 0, + RTL839X_L2_PORT_LRN_CONSTRT + (port << 2)); +} + +static void rtl839x_enable_flood(int port, bool enable) +{ + /* + * 0: Forward + * 1: Disable + * 2: to CPU + * 3: Copy to CPU + */ + sw_w32_mask(0x3, enable ? 0 : 1, + RTL839X_L2_PORT_LRN_CONSTRT + (port << 2)); +} + +static void rtl839x_enable_mcast_flood(int port, bool enable) +{ + +} + +static void rtl839x_enable_bcast_flood(int port, bool enable) +{ + +} +irqreturn_t rtl839x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 status = sw_r32(RTL839X_ISR_GLB_SRC); + u64 ports = rtl839x_get_port_reg_le(RTL839X_ISR_PORT_LINK_STS_CHG); + u64 link; + int i; + + /* Clear status */ + rtl839x_set_port_reg_le(ports, RTL839X_ISR_PORT_LINK_STS_CHG); + pr_debug("RTL8390 Link change: status: %x, ports %llx\n", status, ports); + + for (i = 0; i < RTL839X_CPU_PORT; i++) { + if (ports & BIT_ULL(i)) { + link = rtl839x_get_port_reg_le(RTL839X_MAC_LINK_STS); + if (link & BIT_ULL(i)) + dsa_port_phylink_mac_change(ds, i, true); + else + dsa_port_phylink_mac_change(ds, i, false); + } + } + return IRQ_HANDLED; +} + +// TODO: unused +int rtl8390_sds_power(int mac, int val) +{ + u32 offset = (mac == 48) ? 0x0 : 0x100; + u32 mode = val ? 0 : 1; + + pr_debug("In %s: mac %d, set %d\n", __func__, mac, val); + + if ((mac != 48) && (mac != 49)) { + pr_err("%s: not an SFP port: %d\n", __func__, mac); + return -1; + } + + // Set bit 1003. 1000 starts at 7c + sw_w32_mask(BIT(11), mode << 11, RTL839X_SDS12_13_PWR0 + offset); + + return 0; +} + +static int rtl839x_smi_wait_op(int timeout) +{ + int ret = 0; + u32 val; + + ret = readx_poll_timeout(sw_r32, RTL839X_PHYREG_ACCESS_CTRL, + val, !(val & 0x1), 20, timeout); + if (ret) + pr_err("%s: timeout\n", __func__); + + return ret; +} + +int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + int err = 0; + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + // Take bug on RTL839x Rev <= C into account + if (port >= RTL839X_CPU_PORT) + return -EIO; + + mutex_lock(&smi_lock); + + sw_w32_mask(0xffff0000, port << 16, RTL839X_PHYREG_DATA_CTRL); + v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + sw_w32(0x1ff, RTL839X_PHYREG_CTRL); + + v |= 1; + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + err = rtl839x_smi_wait_op(100000); + if (err) + goto errout; + + *val = sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff; + +errout: + mutex_unlock(&smi_lock); + return err; +} + +int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + val &= 0xffff; + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + // Take bug on RTL839x Rev <= C into account + if (port >= RTL839X_CPU_PORT) + return -EIO; + + mutex_lock(&smi_lock); + + // Set PHY to access + rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL); + + sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL); + + v = reg << 5 | page << 10 | ((page == 0x1fff) ? 0x1f : 0) << 23; + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + sw_w32(0x1ff, RTL839X_PHYREG_CTRL); + + v |= BIT(3) | 1; /* Write operation and execute */ + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + err = rtl839x_smi_wait_op(100000); + if (err) + goto errout; + + if (sw_r32(RTL839X_PHYREG_ACCESS_CTRL) & 0x2) + err = -EIO; + +errout: + mutex_unlock(&smi_lock); + return err; +} + +/* + * Read an mmd register of the PHY + */ +int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + + // Take bug on RTL839x Rev <= C into account + if (port >= RTL839X_CPU_PORT) + return -EIO; + + mutex_lock(&smi_lock); + + // Set PHY to access + sw_w32_mask(0xffff << 16, port << 16, RTL839X_PHYREG_DATA_CTRL); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL); + + v = BIT(2) | BIT(0); // MMD-access | EXEC + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + err = rtl839x_smi_wait_op(100000); + if (err) + goto errout; + + // There is no error-checking via BIT 1 of v, as it does not seem to be set correctly + *val = (sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff); + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); + +errout: + mutex_unlock(&smi_lock); + return err; +} + +/* + * Write to an mmd register of the PHY + */ +int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + + // Take bug on RTL839x Rev <= C into account + if (port >= RTL839X_CPU_PORT) + return -EIO; + + mutex_lock(&smi_lock); + + // Set PHY to access + rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL); + + // Set data to write + sw_w32_mask(0xffff << 16, val << 16, RTL839X_PHYREG_DATA_CTRL); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL); + + v = BIT(3) | BIT(2) | BIT(0); // WRITE | MMD-access | EXEC + sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL); + + err = rtl839x_smi_wait_op(100000); + if (err) + goto errout; + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); + +errout: + mutex_unlock(&smi_lock); + return err; +} + +void rtl8390_get_version(struct rtl838x_switch_priv *priv) +{ + u32 info, model; + + sw_w32_mask(0xf << 28, 0xa << 28, RTL839X_CHIP_INFO); + info = sw_r32(RTL839X_CHIP_INFO); + + model = sw_r32(RTL839X_MODEL_NAME_INFO); + priv->version = RTL8390_VERSION_A + ((model & 0x3f) >> 1); + + pr_info("RTL839X Chip-Info: %x, version %c\n", info, priv->version); +} + +void rtl839x_vlan_profile_dump(int profile) +{ + u32 p[2]; + + if (profile < 0 || profile > 7) + return; + + p[0] = sw_r32(RTL839X_VLAN_PROFILE(profile)); + p[1] = sw_r32(RTL839X_VLAN_PROFILE(profile) + 4); + + pr_info("VLAN profile %d: L2 learning: %d, UNKN L2MC FLD PMSK %d, \ + UNKN IPMC FLD PMSK %d, UNKN IPv6MC FLD PMSK: %d", + profile, p[1] & 1, (p[1] >> 1) & 0xfff, (p[1] >> 13) & 0xfff, + (p[0]) & 0xfff); + pr_info("VLAN profile %d: raw %08x, %08x\n", profile, p[0], p[1]); +} + +static void rtl839x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 16 /* Execute cmd */ + | 0 << 15 /* Read */ + | 5 << 12 /* Table type 0b101 */ + | (msti & 0xfff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 4; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); +} + +static void rtl839x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 16 /* Execute cmd */ + | 1 << 15 /* Write */ + | 5 << 12 /* Table type 0b101 */ + | (msti & 0xfff); + for (i = 0; i < 4; i++) + sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +/* + * Enables or disables the EEE/EEEP capability of a port + */ +void rtl839x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + u32 v; + + // This works only for Ethernet ports, and on the RTL839X, ports above 47 are SFP + if (port >= 48) + return; + + enable = true; + pr_debug("In %s: setting port %d to %d\n", __func__, port, enable); + v = enable ? 0xf : 0x0; + + // Set EEE for 100, 500, 1000MBit and 10GBit + sw_w32_mask(0xf << 8, v << 8, rtl839x_mac_force_mode_ctrl(port)); + + // Set TX/RX EEE state + v = enable ? 0x3 : 0x0; + sw_w32(v, RTL839X_EEE_CTRL(port)); + + priv->ports[port].eee_enabled = enable; +} + +/* + * Get EEE own capabilities and negotiation result + */ +int rtl839x_eee_port_ability(struct rtl838x_switch_priv *priv, struct ethtool_eee *e, int port) +{ + u64 link, a; + + if (port >= 48) + return 0; + + link = rtl839x_get_port_reg_le(RTL839X_MAC_LINK_STS); + if (!(link & BIT_ULL(port))) + return 0; + + if (sw_r32(rtl839x_mac_force_mode_ctrl(port)) & BIT(8)) + e->advertised |= ADVERTISED_100baseT_Full; + + if (sw_r32(rtl839x_mac_force_mode_ctrl(port)) & BIT(10)) + e->advertised |= ADVERTISED_1000baseT_Full; + + a = rtl839x_get_port_reg_le(RTL839X_MAC_EEE_ABLTY); + pr_info("Link partner: %016llx\n", a); + if (rtl839x_get_port_reg_le(RTL839X_MAC_EEE_ABLTY) & BIT_ULL(port)) { + e->lp_advertised = ADVERTISED_100baseT_Full; + e->lp_advertised |= ADVERTISED_1000baseT_Full; + return 1; + } + + return 0; +} + +static void rtl839x_init_eee(struct rtl838x_switch_priv *priv, bool enable) +{ + int i; + + pr_info("Setting up EEE, state: %d\n", enable); + + // Set wake timer for TX and pause timer both to 0x21 + sw_w32_mask(0xff << 20| 0xff, 0x21 << 20| 0x21, RTL839X_EEE_TX_TIMER_GELITE_CTRL); + // Set pause wake timer for GIGA-EEE to 0x11 + sw_w32_mask(0xff << 20, 0x11 << 20, RTL839X_EEE_TX_TIMER_GIGA_CTRL); + // Set pause wake timer for 10GBit ports to 0x11 + sw_w32_mask(0xff << 20, 0x11 << 20, RTL839X_EEE_TX_TIMER_10G_CTRL); + + // Setup EEE on all ports + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + rtl839x_port_eee_set(priv, i, enable); + } + priv->eee_enabled = enable; +} + +static void rtl839x_pie_lookup_enable(struct rtl838x_switch_priv *priv, int index) +{ + int block = index / PIE_BLOCK_SIZE; + + sw_w32_mask(0, BIT(block), RTL839X_ACL_BLK_LOOKUP_CTRL); +} + +/* + * Delete a range of Packet Inspection Engine rules + */ +static int rtl839x_pie_rule_del(struct rtl838x_switch_priv *priv, int index_from, int index_to) +{ + u32 v = (index_from << 1)| (index_to << 13 ) | BIT(0); + + pr_debug("%s: from %d to %d\n", __func__, index_from, index_to); + mutex_lock(&priv->reg_mutex); + + // Write from-to and execute bit into control register + sw_w32(v, RTL839X_ACL_CLR_CTRL); + + // Wait until command has completed + do { + } while (sw_r32(RTL839X_ACL_CLR_CTRL) & BIT(0)); + + mutex_unlock(&priv->reg_mutex); + return 0; +} + +/* + * Reads the intermediate representation of the templated match-fields of the + * PIE rule in the pie_rule structure and fills in the raw data fields in the + * raw register space r[]. + * The register space configuration size is identical for the RTL8380/90 and RTL9300, + * however the RTL9310 has 2 more registers / fields and the physical field-ids are different + * on all SoCs + * On the RTL8390 the template mask registers are not word-aligned! + */ +static void rtl839x_write_pie_templated(u32 r[], struct pie_rule *pr, enum template_field_id t[]) +{ + int i; + enum template_field_id field_type; + u16 data, data_m; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + field_type = t[i]; + data = data_m = 0; + + switch (field_type) { + case TEMPLATE_FIELD_SPM0: + data = pr->spm; + data_m = pr->spm_m; + break; + case TEMPLATE_FIELD_SPM1: + data = pr->spm >> 16; + data_m = pr->spm_m >> 16; + break; + case TEMPLATE_FIELD_SPM2: + data = pr->spm >> 32; + data_m = pr->spm_m >> 32; + break; + case TEMPLATE_FIELD_SPM3: + data = pr->spm >> 48; + data_m = pr->spm_m >> 48; + break; + case TEMPLATE_FIELD_OTAG: + data = pr->otag; + data_m = pr->otag_m; + break; + case TEMPLATE_FIELD_SMAC0: + data = pr->smac[4]; + data = (data << 8) | pr->smac[5]; + data_m = pr->smac_m[4]; + data_m = (data_m << 8) | pr->smac_m[5]; + break; + case TEMPLATE_FIELD_SMAC1: + data = pr->smac[2]; + data = (data << 8) | pr->smac[3]; + data_m = pr->smac_m[2]; + data_m = (data_m << 8) | pr->smac_m[3]; + break; + case TEMPLATE_FIELD_SMAC2: + data = pr->smac[0]; + data = (data << 8) | pr->smac[1]; + data_m = pr->smac_m[0]; + data_m = (data_m << 8) | pr->smac_m[1]; + break; + case TEMPLATE_FIELD_DMAC0: + data = pr->dmac[4]; + data = (data << 8) | pr->dmac[5]; + data_m = pr->dmac_m[4]; + data_m = (data_m << 8) | pr->dmac_m[5]; + break; + case TEMPLATE_FIELD_DMAC1: + data = pr->dmac[2]; + data = (data << 8) | pr->dmac[3]; + data_m = pr->dmac_m[2]; + data_m = (data_m << 8) | pr->dmac_m[3]; + break; + case TEMPLATE_FIELD_DMAC2: + data = pr->dmac[0]; + data = (data << 8) | pr->dmac[1]; + data_m = pr->dmac_m[0]; + data_m = (data_m << 8) | pr->dmac_m[1]; + break; + case TEMPLATE_FIELD_ETHERTYPE: + data = pr->ethertype; + data_m = pr->ethertype_m; + break; + case TEMPLATE_FIELD_ITAG: + data = pr->itag; + data_m = pr->itag_m; + break; + case TEMPLATE_FIELD_SIP0: + if (pr->is_ipv6) { + data = pr->sip6.s6_addr16[7]; + data_m = pr->sip6_m.s6_addr16[7]; + } else { + data = pr->sip; + data_m = pr->sip_m; + } + break; + case TEMPLATE_FIELD_SIP1: + if (pr->is_ipv6) { + data = pr->sip6.s6_addr16[6]; + data_m = pr->sip6_m.s6_addr16[6]; + } else { + data = pr->sip >> 16; + data_m = pr->sip_m >> 16; + } + break; + + case TEMPLATE_FIELD_SIP2: + case TEMPLATE_FIELD_SIP3: + case TEMPLATE_FIELD_SIP4: + case TEMPLATE_FIELD_SIP5: + case TEMPLATE_FIELD_SIP6: + case TEMPLATE_FIELD_SIP7: + data = pr->sip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + data_m = pr->sip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + break; + + case TEMPLATE_FIELD_DIP0: + if (pr->is_ipv6) { + data = pr->dip6.s6_addr16[7]; + data_m = pr->dip6_m.s6_addr16[7]; + } else { + data = pr->dip; + data_m = pr->dip_m; + } + break; + + case TEMPLATE_FIELD_DIP1: + if (pr->is_ipv6) { + data = pr->dip6.s6_addr16[6]; + data_m = pr->dip6_m.s6_addr16[6]; + } else { + data = pr->dip >> 16; + data_m = pr->dip_m >> 16; + } + break; + + case TEMPLATE_FIELD_DIP2: + case TEMPLATE_FIELD_DIP3: + case TEMPLATE_FIELD_DIP4: + case TEMPLATE_FIELD_DIP5: + case TEMPLATE_FIELD_DIP6: + case TEMPLATE_FIELD_DIP7: + data = pr->dip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + data_m = pr->dip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + break; + + case TEMPLATE_FIELD_IP_TOS_PROTO: + data = pr->tos_proto; + data_m = pr->tos_proto_m; + break; + case TEMPLATE_FIELD_L4_SPORT: + data = pr->sport; + data_m = pr->sport_m; + break; + case TEMPLATE_FIELD_L4_DPORT: + data = pr->dport; + data_m = pr->dport_m; + break; + case TEMPLATE_FIELD_ICMP_IGMP: + data = pr->icmp_igmp; + data_m = pr->icmp_igmp_m; + break; + default: + pr_info("%s: unknown field %d\n", __func__, field_type); + } + + // On the RTL8390, the mask fields are not word aligned! + if (!(i % 2)) { + r[5 - i / 2] = data; + r[12 - i / 2] |= ((u32)data_m << 8); + } else { + r[5 - i / 2] |= ((u32)data) << 16; + r[12 - i / 2] |= ((u32)data_m) << 24; + r[11 - i / 2] |= ((u32)data_m) >> 8; + } + } +} + +/* + * Creates the intermediate representation of the templated match-fields of the + * PIE rule in the pie_rule structure by reading the raw data fields in the + * raw register space r[]. + * The register space configuration size is identical for the RTL8380/90 and RTL9300, + * however the RTL9310 has 2 more registers / fields and the physical field-ids + * On the RTL8390 the template mask registers are not word-aligned! + */ +void rtl839x_read_pie_templated(u32 r[], struct pie_rule *pr, enum template_field_id t[]) +{ + int i; + enum template_field_id field_type; + u16 data, data_m; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + field_type = t[i]; + if (!(i % 2)) { + data = r[5 - i / 2]; + data_m = r[12 - i / 2]; + } else { + data = r[5 - i / 2] >> 16; + data_m = r[12 - i / 2] >> 16; + } + + switch (field_type) { + case TEMPLATE_FIELD_SPM0: + pr->spm = (pr->spn << 16) | data; + pr->spm_m = (pr->spn << 16) | data_m; + break; + case TEMPLATE_FIELD_SPM1: + pr->spm = data; + pr->spm_m = data_m; + break; + case TEMPLATE_FIELD_OTAG: + pr->otag = data; + pr->otag_m = data_m; + break; + case TEMPLATE_FIELD_SMAC0: + pr->smac[4] = data >> 8; + pr->smac[5] = data; + pr->smac_m[4] = data >> 8; + pr->smac_m[5] = data; + break; + case TEMPLATE_FIELD_SMAC1: + pr->smac[2] = data >> 8; + pr->smac[3] = data; + pr->smac_m[2] = data >> 8; + pr->smac_m[3] = data; + break; + case TEMPLATE_FIELD_SMAC2: + pr->smac[0] = data >> 8; + pr->smac[1] = data; + pr->smac_m[0] = data >> 8; + pr->smac_m[1] = data; + break; + case TEMPLATE_FIELD_DMAC0: + pr->dmac[4] = data >> 8; + pr->dmac[5] = data; + pr->dmac_m[4] = data >> 8; + pr->dmac_m[5] = data; + break; + case TEMPLATE_FIELD_DMAC1: + pr->dmac[2] = data >> 8; + pr->dmac[3] = data; + pr->dmac_m[2] = data >> 8; + pr->dmac_m[3] = data; + break; + case TEMPLATE_FIELD_DMAC2: + pr->dmac[0] = data >> 8; + pr->dmac[1] = data; + pr->dmac_m[0] = data >> 8; + pr->dmac_m[1] = data; + break; + case TEMPLATE_FIELD_ETHERTYPE: + pr->ethertype = data; + pr->ethertype_m = data_m; + break; + case TEMPLATE_FIELD_ITAG: + pr->itag = data; + pr->itag_m = data_m; + break; + case TEMPLATE_FIELD_SIP0: + pr->sip = data; + pr->sip_m = data_m; + break; + case TEMPLATE_FIELD_SIP1: + pr->sip = (pr->sip << 16) | data; + pr->sip_m = (pr->sip << 16) | data_m; + break; + case TEMPLATE_FIELD_SIP2: + pr->is_ipv6 = true; + // Make use of limitiations on the position of the match values + ipv6_addr_set(&pr->sip6, pr->sip, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + ipv6_addr_set(&pr->sip6_m, pr->sip_m, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + case TEMPLATE_FIELD_SIP3: + case TEMPLATE_FIELD_SIP4: + case TEMPLATE_FIELD_SIP5: + case TEMPLATE_FIELD_SIP6: + case TEMPLATE_FIELD_SIP7: + break; + + case TEMPLATE_FIELD_DIP0: + pr->dip = data; + pr->dip_m = data_m; + break; + + case TEMPLATE_FIELD_DIP1: + pr->dip = (pr->dip << 16) | data; + pr->dip_m = (pr->dip << 16) | data_m; + break; + + case TEMPLATE_FIELD_DIP2: + pr->is_ipv6 = true; + ipv6_addr_set(&pr->dip6, pr->dip, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + ipv6_addr_set(&pr->dip6_m, pr->dip_m, r[5 - i / 2], + r[4 - i / 2], r[3 - i / 2]); + case TEMPLATE_FIELD_DIP3: + case TEMPLATE_FIELD_DIP4: + case TEMPLATE_FIELD_DIP5: + case TEMPLATE_FIELD_DIP6: + case TEMPLATE_FIELD_DIP7: + break; + case TEMPLATE_FIELD_IP_TOS_PROTO: + pr->tos_proto = data; + pr->tos_proto_m = data_m; + break; + case TEMPLATE_FIELD_L4_SPORT: + pr->sport = data; + pr->sport_m = data_m; + break; + case TEMPLATE_FIELD_L4_DPORT: + pr->dport = data; + pr->dport_m = data_m; + break; + case TEMPLATE_FIELD_ICMP_IGMP: + pr->icmp_igmp = data; + pr->icmp_igmp_m = data_m; + break; + default: + pr_info("%s: unknown field %d\n", __func__, field_type); + } + } +} + +static void rtl839x_read_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + pr->spmmask_fix = (r[6] >> 30) & 0x3; + pr->spn = (r[6] >> 24) & 0x3f; + pr->mgnt_vlan = (r[6] >> 23) & 1; + pr->dmac_hit_sw = (r[6] >> 22) & 1; + pr->not_first_frag = (r[6] >> 21) & 1; + pr->frame_type_l4 = (r[6] >> 18) & 7; + pr->frame_type = (r[6] >> 16) & 3; + pr->otag_fmt = (r[6] >> 15) & 1; + pr->itag_fmt = (r[6] >> 14) & 1; + pr->otag_exist = (r[6] >> 13) & 1; + pr->itag_exist = (r[6] >> 12) & 1; + pr->frame_type_l2 = (r[6] >> 10) & 3; + pr->tid = (r[6] >> 8) & 3; + + pr->spmmask_fix_m = (r[12] >> 6) & 0x3; + pr->spn_m = r[12] & 0x3f; + pr->mgnt_vlan_m = (r[13] >> 31) & 1; + pr->dmac_hit_sw_m = (r[13] >> 30) & 1; + pr->not_first_frag_m = (r[13] >> 29) & 1; + pr->frame_type_l4_m = (r[13] >> 26) & 7; + pr->frame_type_m = (r[13] >> 24) & 3; + pr->otag_fmt_m = (r[13] >> 23) & 1; + pr->itag_fmt_m = (r[13] >> 22) & 1; + pr->otag_exist_m = (r[13] >> 21) & 1; + pr->itag_exist_m = (r[13] >> 20) & 1; + pr->frame_type_l2_m = (r[13] >> 18) & 3; + pr->tid_m = (r[13] >> 16) & 3; + + pr->valid = r[13] & BIT(15); + pr->cond_not = r[13] & BIT(14); + pr->cond_and1 = r[13] & BIT(13); + pr->cond_and2 = r[13] & BIT(12); +} + +static void rtl839x_write_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + r[6] = ((u32) (pr->spmmask_fix & 0x3)) << 30; + r[6] |= ((u32) (pr->spn & 0x3f)) << 24; + r[6] |= pr->mgnt_vlan ? BIT(23) : 0; + r[6] |= pr->dmac_hit_sw ? BIT(22) : 0; + r[6] |= pr->not_first_frag ? BIT(21) : 0; + r[6] |= ((u32) (pr->frame_type_l4 & 0x7)) << 18; + r[6] |= ((u32) (pr->frame_type & 0x3)) << 16; + r[6] |= pr->otag_fmt ? BIT(15) : 0; + r[6] |= pr->itag_fmt ? BIT(14) : 0; + r[6] |= pr->otag_exist ? BIT(13) : 0; + r[6] |= pr->itag_exist ? BIT(12) : 0; + r[6] |= ((u32) (pr->frame_type_l2 & 0x3)) << 10; + r[6] |= ((u32) (pr->tid & 0x3)) << 8; + + r[12] |= ((u32) (pr->spmmask_fix_m & 0x3)) << 6; + r[12] |= (u32) (pr->spn_m & 0x3f); + r[13] |= pr->mgnt_vlan_m ? BIT(31) : 0; + r[13] |= pr->dmac_hit_sw_m ? BIT(30) : 0; + r[13] |= pr->not_first_frag_m ? BIT(29) : 0; + r[13] |= ((u32) (pr->frame_type_l4_m & 0x7)) << 26; + r[13] |= ((u32) (pr->frame_type_m & 0x3)) << 24; + r[13] |= pr->otag_fmt_m ? BIT(23) : 0; + r[13] |= pr->itag_fmt_m ? BIT(22) : 0; + r[13] |= pr->otag_exist_m ? BIT(21) : 0; + r[13] |= pr->itag_exist_m ? BIT(20) : 0; + r[13] |= ((u32) (pr->frame_type_l2_m & 0x3)) << 18; + r[13] |= ((u32) (pr->tid_m & 0x3)) << 16; + + r[13] |= pr->valid ? BIT(15) : 0; + r[13] |= pr->cond_not ? BIT(14) : 0; + r[13] |= pr->cond_and1 ? BIT(13) : 0; + r[13] |= pr->cond_and2 ? BIT(12) : 0; +} + +static void rtl839x_write_pie_action(u32 r[], struct pie_rule *pr) +{ + if (pr->drop) { + r[13] |= 0x9; // Set ACT_MASK_FWD & FWD_ACT = DROP + r[13] |= BIT(3); + } else { + r[13] |= pr->fwd_sel ? BIT(3) : 0; + r[13] |= pr->fwd_act; + } + r[13] |= pr->bypass_sel ? BIT(11) : 0; + r[13] |= pr->mpls_sel ? BIT(10) : 0; + r[13] |= pr->nopri_sel ? BIT(9) : 0; + r[13] |= pr->ovid_sel ? BIT(8) : 0; + r[13] |= pr->ivid_sel ? BIT(7) : 0; + r[13] |= pr->meter_sel ? BIT(6) : 0; + r[13] |= pr->mir_sel ? BIT(5) : 0; + r[13] |= pr->log_sel ? BIT(4) : 0; + + r[14] |= ((u32)(pr->fwd_data & 0x3fff)) << 18; + r[14] |= pr->log_octets ? BIT(17) : 0; + r[14] |= ((u32)(pr->log_data & 0x7ff)) << 4; + r[14] |= (pr->mir_data & 0x3) << 3; + r[14] |= ((u32)(pr->meter_data >> 7)) & 0x7; + r[15] |= (u32)(pr->meter_data) << 26; + r[15] |= ((u32)(pr->ivid_act) << 23) & 0x3; + r[15] |= ((u32)(pr->ivid_data) << 9) & 0xfff; + r[15] |= ((u32)(pr->ovid_act) << 6) & 0x3; + r[15] |= ((u32)(pr->ovid_data) >> 4) & 0xff; + r[16] |= ((u32)(pr->ovid_data) & 0xf) << 28; + r[16] |= ((u32)(pr->nopri_data) & 0x7) << 20; + r[16] |= ((u32)(pr->mpls_act) & 0x7) << 20; + r[16] |= ((u32)(pr->mpls_lib_idx) & 0x7) << 20; + r[16] |= pr->bypass_all ? BIT(9) : 0; + r[16] |= pr->bypass_igr_stp ? BIT(8) : 0; + r[16] |= pr->bypass_ibc_sc ? BIT(7) : 0; +} + +static void rtl839x_read_pie_action(u32 r[], struct pie_rule *pr) +{ + if (r[13] & BIT(3)) { // ACT_MASK_FWD set, is it a drop? + if ((r[14] & 0x7) == 1) { + pr->drop = true; + } else { + pr->fwd_sel = true; + pr->fwd_act = r[14] & 0x7; + } + } + + pr->bypass_sel = r[13] & BIT(11); + pr->mpls_sel = r[13] & BIT(10); + pr->nopri_sel = r[13] & BIT(9); + pr->ovid_sel = r[13] & BIT(8); + pr->ivid_sel = r[13] & BIT(7); + pr->meter_sel = r[13] & BIT(6); + pr->mir_sel = r[13] & BIT(5); + pr->log_sel = r[13] & BIT(4); + + // TODO: Read in data fields + + pr->bypass_all = r[16] & BIT(9); + pr->bypass_igr_stp = r[16] & BIT(8); + pr->bypass_ibc_sc = r[16] & BIT(7); +} + +void rtl839x_pie_rule_dump_raw(u32 r[]) +{ + pr_info("Raw IACL table entry:\n"); + pr_info("Match : %08x %08x %08x %08x %08x %08x\n", r[0], r[1], r[2], r[3], r[4], r[5]); + pr_info("Fixed : %06x\n", r[6] >> 8); + pr_info("Match M: %08x %08x %08x %08x %08x %08x\n", + (r[6] << 24) | (r[7] >> 8), (r[7] << 24) | (r[8] >> 8), (r[8] << 24) | (r[9] >> 8), + (r[9] << 24) | (r[10] >> 8), (r[10] << 24) | (r[11] >> 8), + (r[11] << 24) | (r[12] >> 8)); + pr_info("R[13]: %08x\n", r[13]); + pr_info("Fixed M: %06x\n", ((r[12] << 16) | (r[13] >> 16)) & 0xffffff); + pr_info("Valid / not / and1 / and2 : %1x\n", (r[13] >> 12) & 0xf); + pr_info("r 13-16: %08x %08x %08x %08x\n", r[13], r[14], r[15], r[16]); +} + +void rtl839x_pie_rule_dump(struct pie_rule *pr) +{ + pr_info("Drop: %d, fwd: %d, ovid: %d, ivid: %d, flt: %d, log: %d, rmk: %d, meter: %d tagst: %d, mir: %d, nopri: %d, cpupri: %d, otpid: %d, itpid: %d, shape: %d\n", + pr->drop, pr->fwd_sel, pr->ovid_sel, pr->ivid_sel, pr->flt_sel, pr->log_sel, pr->rmk_sel, pr->log_sel, pr->tagst_sel, pr->mir_sel, pr->nopri_sel, + pr->cpupri_sel, pr->otpid_sel, pr->itpid_sel, pr->shaper_sel); + if (pr->fwd_sel) + pr_info("FWD: %08x\n", pr->fwd_data); + pr_info("TID: %x, %x\n", pr->tid, pr->tid_m); +} + +static int rtl839x_pie_rule_read(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr) +{ + // Read IACL table (2) via register 0 + struct table_reg *q = rtl_table_get(RTL8380_TBL_0, 2); + u32 r[17]; + int i; + int block = idx / PIE_BLOCK_SIZE; + u32 t_select = sw_r32(RTL839X_ACL_BLK_TMPLTE_CTRL(block)); + + memset(pr, 0, sizeof(*pr)); + rtl_table_read(q, idx); + for (i = 0; i < 17; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl839x_read_pie_fixed_fields(r, pr); + if (!pr->valid) + return 0; + + pr_debug("%s: template_selectors %08x, tid: %d\n", __func__, t_select, pr->tid); + rtl839x_pie_rule_dump_raw(r); + + rtl839x_read_pie_templated(r, pr, fixed_templates[(t_select >> (pr->tid * 3)) & 0x7]); + + rtl839x_read_pie_action(r, pr); + + return 0; +} + +static int rtl839x_pie_rule_write(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr) +{ + // Access IACL table (2) via register 0 + struct table_reg *q = rtl_table_get(RTL8390_TBL_0, 2); + u32 r[17]; + int i; + int block = idx / PIE_BLOCK_SIZE; + u32 t_select = sw_r32(RTL839X_ACL_BLK_TMPLTE_CTRL(block)); + + pr_debug("%s: %d, t_select: %08x\n", __func__, idx, t_select); + + for (i = 0; i < 17; i++) + r[i] = 0; + + if (!pr->valid) { + rtl_table_write(q, idx); + rtl_table_release(q); + return 0; + } + rtl839x_write_pie_fixed_fields(r, pr); + + pr_debug("%s: template %d\n", __func__, (t_select >> (pr->tid * 3)) & 0x7); + rtl839x_write_pie_templated(r, pr, fixed_templates[(t_select >> (pr->tid * 3)) & 0x7]); + + rtl839x_write_pie_action(r, pr); + +// rtl839x_pie_rule_dump_raw(r); + + for (i = 0; i < 17; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); + + return 0; +} + +static bool rtl839x_pie_templ_has(int t, enum template_field_id field_type) +{ + int i; + enum template_field_id ft; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + ft = fixed_templates[t][i]; + if (field_type == ft) + return true; + } + + return false; +} + +static int rtl839x_pie_verify_template(struct rtl838x_switch_priv *priv, + struct pie_rule *pr, int t, int block) +{ + int i; + + if (!pr->is_ipv6 && pr->sip_m && !rtl839x_pie_templ_has(t, TEMPLATE_FIELD_SIP0)) + return -1; + + if (!pr->is_ipv6 && pr->dip_m && !rtl839x_pie_templ_has(t, TEMPLATE_FIELD_DIP0)) + return -1; + + if (pr->is_ipv6) { + if ((pr->sip6_m.s6_addr32[0] || pr->sip6_m.s6_addr32[1] + || pr->sip6_m.s6_addr32[2] || pr->sip6_m.s6_addr32[3]) + && !rtl839x_pie_templ_has(t, TEMPLATE_FIELD_SIP2)) + return -1; + if ((pr->dip6_m.s6_addr32[0] || pr->dip6_m.s6_addr32[1] + || pr->dip6_m.s6_addr32[2] || pr->dip6_m.s6_addr32[3]) + && !rtl839x_pie_templ_has(t, TEMPLATE_FIELD_DIP2)) + return -1; + } + + if (ether_addr_to_u64(pr->smac) && !rtl839x_pie_templ_has(t, TEMPLATE_FIELD_SMAC0)) + return -1; + + if (ether_addr_to_u64(pr->dmac) && !rtl839x_pie_templ_has(t, TEMPLATE_FIELD_DMAC0)) + return -1; + + // TODO: Check more + + i = find_first_zero_bit(&priv->pie_use_bm[block * 4], PIE_BLOCK_SIZE); + + if (i >= PIE_BLOCK_SIZE) + return -1; + + return i + PIE_BLOCK_SIZE * block; +} + +static int rtl839x_pie_rule_add(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx, block, j, t; + int min_block = 0; + int max_block = priv->n_pie_blocks / 2; + + if (pr->is_egress) { + min_block = max_block; + max_block = priv->n_pie_blocks; + } + + mutex_lock(&priv->pie_mutex); + + for (block = min_block; block < max_block; block++) { + for (j = 0; j < 2; j++) { + t = (sw_r32(RTL839X_ACL_BLK_TMPLTE_CTRL(block)) >> (j * 3)) & 0x7; + idx = rtl839x_pie_verify_template(priv, pr, t, block); + if (idx >= 0) + break; + } + if (j < 2) + break; + } + + if (block >= priv->n_pie_blocks) { + mutex_unlock(&priv->pie_mutex); + return -EOPNOTSUPP; + } + + set_bit(idx, priv->pie_use_bm); + + pr->valid = true; + pr->tid = j; // Mapped to template number + pr->tid_m = 0x3; + pr->id = idx; + + rtl839x_pie_lookup_enable(priv, idx); + rtl839x_pie_rule_write(priv, idx, pr); + + mutex_unlock(&priv->pie_mutex); + return 0; +} + +static void rtl839x_pie_rule_rm(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx = pr->id; + + rtl839x_pie_rule_del(priv, idx, idx); + clear_bit(idx, priv->pie_use_bm); +} + +static void rtl839x_pie_init(struct rtl838x_switch_priv *priv) +{ + int i; + u32 template_selectors; + + mutex_init(&priv->pie_mutex); + + // Power on all PIE blocks + for (i = 0; i < priv->n_pie_blocks; i++) + sw_w32_mask(0, BIT(i), RTL839X_PS_ACL_PWR_CTRL); + + // Set ingress and egress ACL blocks to 50/50: first Egress block is 9 + sw_w32_mask(0x1f, 9, RTL839X_ACL_CTRL); // Writes 9 to cutline field + + // Include IPG in metering + sw_w32(1, RTL839X_METER_GLB_CTRL); + + // Delete all present rules + rtl839x_pie_rule_del(priv, 0, priv->n_pie_blocks * PIE_BLOCK_SIZE - 1); + + // Enable predefined templates 0, 1 for blocks 0-2 + template_selectors = 0 | (1 << 3); + for (i = 0; i < 3; i++) + sw_w32(template_selectors, RTL839X_ACL_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 2, 3 for blocks 3-5 + template_selectors = 2 | (3 << 3); + for (i = 3; i < 6; i++) + sw_w32(template_selectors, RTL839X_ACL_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 1, 4 for blocks 6-8 + template_selectors = 2 | (3 << 3); + for (i = 6; i < 9; i++) + sw_w32(template_selectors, RTL839X_ACL_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 0, 1 for blocks 9-11 + template_selectors = 0 | (1 << 3); + for (i = 9; i < 12; i++) + sw_w32(template_selectors, RTL839X_ACL_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 2, 3 for blocks 12-14 + template_selectors = 2 | (3 << 3); + for (i = 12; i < 15; i++) + sw_w32(template_selectors, RTL839X_ACL_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 1, 4 for blocks 15-17 + template_selectors = 2 | (3 << 3); + for (i = 15; i < 18; i++) + sw_w32(template_selectors, RTL839X_ACL_BLK_TMPLTE_CTRL(i)); +} + +static u32 rtl839x_packet_cntr_read(int counter) +{ + u32 v; + + // Read LOG table (4) via register RTL8390_TBL_0 + struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 4); + + pr_debug("In %s, id %d\n", __func__, counter); + rtl_table_read(r, counter / 2); + + // The table has a size of 2 registers + if (counter % 2) + v = sw_r32(rtl_table_data(r, 0)); + else + v = sw_r32(rtl_table_data(r, 1)); + + rtl_table_release(r); + + return v; +} + +static void rtl839x_packet_cntr_clear(int counter) +{ + // Access LOG table (4) via register RTL8390_TBL_0 + struct table_reg *r = rtl_table_get(RTL8390_TBL_0, 4); + + pr_debug("In %s, id %d\n", __func__, counter); + // The table has a size of 2 registers + if (counter % 2) + sw_w32(0, rtl_table_data(r, 0)); + else + sw_w32(0, rtl_table_data(r, 1)); + + rtl_table_write(r, counter / 2); + + rtl_table_release(r); +} + +static void rtl839x_route_read(int idx, struct rtl83xx_route *rt) +{ + u64 v; + // Read ROUTING table (2) via register RTL8390_TBL_1 + struct table_reg *r = rtl_table_get(RTL8390_TBL_1, 2); + + pr_debug("In %s\n", __func__); + rtl_table_read(r, idx); + + // The table has a size of 2 registers + v = sw_r32(rtl_table_data(r, 0)); + v <<= 32; + v |= sw_r32(rtl_table_data(r, 1)); + rt->switch_mac_id = (v >> 12) & 0xf; + rt->nh.gw = v >> 16; + + rtl_table_release(r); +} + +static void rtl839x_route_write(int idx, struct rtl83xx_route *rt) +{ + u32 v; + + // Read ROUTING table (2) via register RTL8390_TBL_1 + struct table_reg *r = rtl_table_get(RTL8390_TBL_1, 2); + + pr_debug("In %s\n", __func__); + sw_w32(rt->nh.gw >> 16, rtl_table_data(r, 0)); + v = rt->nh.gw << 16; + v |= rt->switch_mac_id << 12; + sw_w32(v, rtl_table_data(r, 1)); + rtl_table_write(r, idx); + + rtl_table_release(r); +} + +/* + * Configure the switch's own MAC addresses used when routing packets + */ +static void rtl839x_setup_port_macs(struct rtl838x_switch_priv *priv) +{ + int i; + struct net_device *dev; + u64 mac; + + pr_debug("%s: got port %08x\n", __func__, (u32)priv->ports[priv->cpu_port].dp); + dev = priv->ports[priv->cpu_port].dp->slave; + mac = ether_addr_to_u64(dev->dev_addr); + + for (i = 0; i < 15; i++) { + mac++; // BUG: VRRP for testing + sw_w32(mac >> 32, RTL839X_ROUTING_SA_CTRL + i * 8); + sw_w32(mac, RTL839X_ROUTING_SA_CTRL + i * 8 + 4); + } +} + +int rtl839x_l3_setup(struct rtl838x_switch_priv *priv) +{ + rtl839x_setup_port_macs(priv); + + return 0; +} + +void rtl839x_vlan_port_keep_tag_set(int port, bool keep_outer, bool keep_inner) +{ + sw_w32(FIELD_PREP(RTL839X_VLAN_PORT_TAG_STS_CTRL_OTAG_STS_MASK, + keep_outer ? RTL839X_VLAN_PORT_TAG_STS_TAGGED : RTL839X_VLAN_PORT_TAG_STS_UNTAG) | + FIELD_PREP(RTL839X_VLAN_PORT_TAG_STS_CTRL_ITAG_STS_MASK, + keep_inner ? RTL839X_VLAN_PORT_TAG_STS_TAGGED : RTL839X_VLAN_PORT_TAG_STS_UNTAG), + RTL839X_VLAN_PORT_TAG_STS_CTRL(port)); +} + +void rtl839x_vlan_port_pvidmode_set(int port, enum pbvlan_type type, enum pbvlan_mode mode) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0x3, mode, RTL839X_VLAN_PORT_PB_VLAN + (port << 2)); + else + sw_w32_mask(0x3 << 14, mode << 14, RTL839X_VLAN_PORT_PB_VLAN + (port << 2)); +} + +void rtl839x_vlan_port_pvid_set(int port, enum pbvlan_type type, int pvid) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0xfff << 2, pvid << 2, RTL839X_VLAN_PORT_PB_VLAN + (port << 2)); + else + sw_w32_mask(0xfff << 16, pvid << 16, RTL839X_VLAN_PORT_PB_VLAN + (port << 2)); +} + +static int rtl839x_set_ageing_time(unsigned long msec) +{ + int t = sw_r32(RTL839X_L2_CTRL_1); + + t &= 0x1FFFFF; + t = t * 3 / 5; /* Aging time in seconds. 0: L2 aging disabled */ + pr_debug("L2 AGING time: %d sec\n", t); + + t = (msec * 5 + 2000) / 3000; + t = t > 0x1FFFFF ? 0x1FFFFF : t; + sw_w32_mask(0x1FFFFF, t, RTL839X_L2_CTRL_1); + pr_debug("Dynamic aging for ports: %x\n", sw_r32(RTL839X_L2_PORT_AGING_OUT)); + + return 0; +} + +static void rtl839x_set_igr_filter(int port, enum igr_filter state) +{ + sw_w32_mask(0x3 << ((port & 0xf)<<1), state << ((port & 0xf)<<1), + RTL839X_VLAN_PORT_IGR_FLTR + (((port >> 4) << 2))); +} + +static void rtl839x_set_egr_filter(int port, enum egr_filter state) +{ + sw_w32_mask(0x1 << (port % 0x20), state << (port % 0x20), + RTL839X_VLAN_PORT_EGR_FLTR + (((port >> 5) << 2))); +} + +void rtl839x_set_distribution_algorithm(int group, int algoidx, u32 algomsk) +{ + sw_w32_mask(3 << ((group & 0xf) << 1), algoidx << ((group & 0xf) << 1), + RTL839X_TRK_HASH_IDX_CTRL + ((group >> 4) << 2)); + sw_w32(algomsk, RTL839X_TRK_HASH_CTRL + (algoidx << 2)); +} + +void rtl839x_set_receive_management_action(int port, rma_ctrl_t type, action_type_t action) +{ + switch(type) { + case BPDU: + sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1), + RTL839X_RMA_BPDU_CTRL + ((port >> 4) << 2)); + break; + case PTP: + sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1), + RTL839X_RMA_PTP_CTRL + ((port >> 4) << 2)); + break; + case LLTP: + sw_w32_mask(3 << ((port & 0xf) << 1), (action & 0x3) << ((port & 0xf) << 1), + RTL839X_RMA_LLTP_CTRL + ((port >> 4) << 2)); + break; + default: + break; + } +} + +const struct rtl838x_reg rtl839x_reg = { + .mask_port_reg_be = rtl839x_mask_port_reg_be, + .set_port_reg_be = rtl839x_set_port_reg_be, + .get_port_reg_be = rtl839x_get_port_reg_be, + .mask_port_reg_le = rtl839x_mask_port_reg_le, + .set_port_reg_le = rtl839x_set_port_reg_le, + .get_port_reg_le = rtl839x_get_port_reg_le, + .stat_port_rst = RTL839X_STAT_PORT_RST, + .stat_rst = RTL839X_STAT_RST, + .stat_port_std_mib = RTL839X_STAT_PORT_STD_MIB, + .traffic_enable = rtl839x_traffic_enable, + .traffic_disable = rtl839x_traffic_disable, + .traffic_get = rtl839x_traffic_get, + .traffic_set = rtl839x_traffic_set, + .port_iso_ctrl = rtl839x_port_iso_ctrl, + .l2_ctrl_0 = RTL839X_L2_CTRL_0, + .l2_ctrl_1 = RTL839X_L2_CTRL_1, + .l2_port_aging_out = RTL839X_L2_PORT_AGING_OUT, + .set_ageing_time = rtl839x_set_ageing_time, + .smi_poll_ctrl = RTL839X_SMI_PORT_POLLING_CTRL, + .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl839x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl839x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl839x_tbl_access_data_0, + .isr_glb_src = RTL839X_ISR_GLB_SRC, + .isr_port_link_sts_chg = RTL839X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL839X_IMR_PORT_LINK_STS_CHG, + .imr_glb = RTL839X_IMR_GLB, + .vlan_tables_read = rtl839x_vlan_tables_read, + .vlan_set_tagged = rtl839x_vlan_set_tagged, + .vlan_set_untagged = rtl839x_vlan_set_untagged, + .vlan_profile_dump = rtl839x_vlan_profile_dump, + .vlan_profile_setup = rtl839x_vlan_profile_setup, + .vlan_fwd_on_inner = rtl839x_vlan_fwd_on_inner, + .vlan_port_keep_tag_set = rtl839x_vlan_port_keep_tag_set, + .vlan_port_pvidmode_set = rtl839x_vlan_port_pvidmode_set, + .vlan_port_pvid_set = rtl839x_vlan_port_pvid_set, + .set_vlan_igr_filter = rtl839x_set_igr_filter, + .set_vlan_egr_filter = rtl839x_set_egr_filter, + .enable_learning = rtl839x_enable_learning, + .enable_flood = rtl839x_enable_flood, + .enable_mcast_flood = rtl839x_enable_mcast_flood, + .enable_bcast_flood = rtl839x_enable_bcast_flood, + .stp_get = rtl839x_stp_get, + .stp_set = rtl839x_stp_set, + .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl, + .mac_port_ctrl = rtl839x_mac_port_ctrl, + .l2_port_new_salrn = rtl839x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl839x_l2_port_new_sa_fwd, + .mir_ctrl = RTL839X_MIR_CTRL, + .mir_dpm = RTL839X_MIR_DPM_CTRL, + .mir_spm = RTL839X_MIR_SPM_CTRL, + .mac_link_sts = RTL839X_MAC_LINK_STS, + .mac_link_dup_sts = RTL839X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl839x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash, + .write_l2_entry_using_hash = rtl839x_write_l2_entry_using_hash, + .read_cam = rtl839x_read_cam, + .write_cam = rtl839x_write_cam, + .trk_mbr_ctr = rtl839x_trk_mbr_ctr, + .rma_bpdu_fld_pmask = RTL839X_RMA_BPDU_FLD_PMSK, + .spcl_trap_eapol_ctrl = RTL839X_SPCL_TRAP_EAPOL_CTRL, + .init_eee = rtl839x_init_eee, + .port_eee_set = rtl839x_port_eee_set, + .eee_port_ability = rtl839x_eee_port_ability, + .l2_hash_seed = rtl839x_l2_hash_seed, + .l2_hash_key = rtl839x_l2_hash_key, + .read_mcast_pmask = rtl839x_read_mcast_pmask, + .write_mcast_pmask = rtl839x_write_mcast_pmask, + .pie_init = rtl839x_pie_init, + .pie_rule_read = rtl839x_pie_rule_read, + .pie_rule_write = rtl839x_pie_rule_write, + .pie_rule_add = rtl839x_pie_rule_add, + .pie_rule_rm = rtl839x_pie_rule_rm, + .l2_learning_setup = rtl839x_l2_learning_setup, + .packet_cntr_read = rtl839x_packet_cntr_read, + .packet_cntr_clear = rtl839x_packet_cntr_clear, + .route_read = rtl839x_route_read, + .route_write = rtl839x_route_write, + .l3_setup = rtl839x_l3_setup, + .set_distribution_algorithm = rtl839x_set_distribution_algorithm, + .set_receive_management_action = rtl839x_set_receive_management_action, +}; diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl83xx.h b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl83xx.h new file mode 100644 index 00000000000..107016469c6 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl83xx.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _NET_DSA_RTL83XX_H +#define _NET_DSA_RTL83XX_H + +#include +#include "rtl838x.h" + + +#define RTL8380_VERSION_A 'A' +#define RTL8390_VERSION_A 'A' +#define RTL8380_VERSION_B 'B' + +struct fdb_update_work { + struct work_struct work; + struct net_device *ndev; + u64 macs[]; +}; + +#define MIB_DESC(_size, _offset, _name) {.size = _size, .offset = _offset, .name = _name} +struct rtl83xx_mib_desc { + unsigned int size; + unsigned int offset; + const char *name; +}; + +/* API for switch table access */ +struct table_reg { + u16 addr; + u16 data; + u8 max_data; + u8 c_bit; + u8 t_bit; + u8 rmode; + u8 tbl; + struct mutex lock; +}; + +#define TBL_DESC(_addr, _data, _max_data, _c_bit, _t_bit, _rmode) \ + { .addr = _addr, .data = _data, .max_data = _max_data, .c_bit = _c_bit, \ + .t_bit = _t_bit, .rmode = _rmode \ + } + +typedef enum { + RTL8380_TBL_L2 = 0, + RTL8380_TBL_0, + RTL8380_TBL_1, + RTL8390_TBL_L2, + RTL8390_TBL_0, + RTL8390_TBL_1, + RTL8390_TBL_2, + RTL9300_TBL_L2, + RTL9300_TBL_0, + RTL9300_TBL_1, + RTL9300_TBL_2, + RTL9300_TBL_HSB, + RTL9300_TBL_HSA, + RTL9310_TBL_0, + RTL9310_TBL_1, + RTL9310_TBL_2, + RTL9310_TBL_3, + RTL9310_TBL_4, + RTL9310_TBL_5, + RTL_TBL_END +} rtl838x_tbl_reg_t; + +void rtl_table_init(void); +struct table_reg *rtl_table_get(rtl838x_tbl_reg_t r, int t); +void rtl_table_release(struct table_reg *r); +void rtl_table_read(struct table_reg *r, int idx); +void rtl_table_write(struct table_reg *r, int idx); +inline u16 rtl_table_data(struct table_reg *r, int i); +inline u32 rtl_table_data_r(struct table_reg *r, int i); +inline void rtl_table_data_w(struct table_reg *r, u32 v, int i); + +void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv); + +int rtl83xx_packet_cntr_alloc(struct rtl838x_switch_priv *priv); + +int rtl83xx_port_is_under(const struct net_device * dev, struct rtl838x_switch_priv *priv); + +int read_phy(u32 port, u32 page, u32 reg, u32 *val); +int write_phy(u32 port, u32 page, u32 reg, u32 val); + +/* Port register accessor functions for the RTL839x and RTL931X SoCs */ +void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg); +u64 rtl839x_get_port_reg_be(int reg); +void rtl839x_set_port_reg_be(u64 set, int reg); +void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg); +void rtl839x_set_port_reg_le(u64 set, int reg); +u64 rtl839x_get_port_reg_le(int reg); + +/* Port register accessor functions for the RTL838x and RTL930X SoCs */ +void rtl838x_mask_port_reg(u64 clear, u64 set, int reg); +void rtl838x_set_port_reg(u64 set, int reg); +u64 rtl838x_get_port_reg(int reg); + +/* RTL838x-specific */ +u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed); +irqreturn_t rtl838x_switch_irq(int irq, void *dev_id); +void rtl8380_get_version(struct rtl838x_switch_priv *priv); +void rtl838x_vlan_profile_dump(int index); +int rtl83xx_dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg); +void rtl8380_sds_rst(int mac); +int rtl8380_sds_power(int mac, int val); +void rtl838x_print_matrix(void); + +/* RTL839x-specific */ +u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed); +irqreturn_t rtl839x_switch_irq(int irq, void *dev_id); +void rtl8390_get_version(struct rtl838x_switch_priv *priv); +void rtl839x_vlan_profile_dump(int index); +int rtl83xx_dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val); +void rtl839x_exec_tbl2_cmd(u32 cmd); +void rtl839x_print_matrix(void); + +/* RTL930x-specific */ +u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed); +irqreturn_t rtl930x_switch_irq(int irq, void *dev_id); +irqreturn_t rtl839x_switch_irq(int irq, void *dev_id); +void rtl930x_vlan_profile_dump(int index); +int rtl9300_sds_power(int mac, int val); +void rtl9300_sds_rst(int sds_num, u32 mode); +int rtl9300_serdes_setup(int sds_num, phy_interface_t phy_mode); +void rtl930x_print_matrix(void); + +/* RTL931x-specific */ +irqreturn_t rtl931x_switch_irq(int irq, void *dev_id); +int rtl931x_sds_cmu_band_get(int sds, phy_interface_t mode); +int rtl931x_sds_cmu_band_set(int sds, bool enable, u32 band, phy_interface_t mode); +void rtl931x_sds_init(u32 sds, phy_interface_t mode); + +int rtl83xx_lag_add(struct dsa_switch *ds, int group, int port, struct netdev_lag_upper_info *info); +int rtl83xx_lag_del(struct dsa_switch *ds, int group, int port); + +#endif /* _NET_DSA_RTL83XX_H */ + diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl930x.c new file mode 100644 index 00000000000..5dde8353e2d --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl930x.c @@ -0,0 +1,2560 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include "rtl83xx.h" + +#define RTL930X_VLAN_PORT_TAG_STS_INTERNAL 0x0 +#define RTL930X_VLAN_PORT_TAG_STS_UNTAG 0x1 +#define RTL930X_VLAN_PORT_TAG_STS_TAGGED 0x2 +#define RTL930X_VLAN_PORT_TAG_STS_PRIORITY_TAGGED 0x3 + +#define RTL930X_VLAN_PORT_TAG_STS_CTRL_BASE 0xCE24 +/* port 0-28 */ +#define RTL930X_VLAN_PORT_TAG_STS_CTRL(port) \ + RTL930X_VLAN_PORT_TAG_STS_CTRL_BASE + (port << 2) +#define RTL930X_VLAN_PORT_TAG_STS_CTRL_EGR_OTAG_STS_MASK GENMASK(7,6) +#define RTL930X_VLAN_PORT_TAG_STS_CTRL_EGR_ITAG_STS_MASK GENMASK(5,4) +#define RTL930X_VLAN_PORT_TAG_STS_CTRL_EGR_P_OTAG_KEEP_MASK GENMASK(3,3) +#define RTL930X_VLAN_PORT_TAG_STS_CTRL_EGR_P_ITAG_KEEP_MASK GENMASK(2,2) +#define RTL930X_VLAN_PORT_TAG_STS_CTRL_IGR_P_OTAG_KEEP_MASK GENMASK(1,1) +#define RTL930X_VLAN_PORT_TAG_STS_CTRL_IGR_P_ITAG_KEEP_MASK GENMASK(0,0) + +extern struct mutex smi_lock; +extern struct rtl83xx_soc_info soc_info; + +/* Definition of the RTL930X-specific template field IDs as used in the PIE */ +enum template_field_id { + TEMPLATE_FIELD_SPM0 = 0, // Source portmask ports 0-15 + TEMPLATE_FIELD_SPM1 = 1, // Source portmask ports 16-31 + TEMPLATE_FIELD_DMAC0 = 2, // Destination MAC [15:0] + TEMPLATE_FIELD_DMAC1 = 3, // Destination MAC [31:16] + TEMPLATE_FIELD_DMAC2 = 4, // Destination MAC [47:32] + TEMPLATE_FIELD_SMAC0 = 5, // Source MAC [15:0] + TEMPLATE_FIELD_SMAC1 = 6, // Source MAC [31:16] + TEMPLATE_FIELD_SMAC2 = 7, // Source MAC [47:32] + TEMPLATE_FIELD_ETHERTYPE = 8, // Ethernet frame type field + TEMPLATE_FIELD_OTAG = 9, + TEMPLATE_FIELD_ITAG = 10, + TEMPLATE_FIELD_SIP0 = 11, + TEMPLATE_FIELD_SIP1 = 12, + TEMPLATE_FIELD_DIP0 = 13, + TEMPLATE_FIELD_DIP1 = 14, + TEMPLATE_FIELD_IP_TOS_PROTO = 15, + TEMPLATE_FIELD_L4_SPORT = 16, + TEMPLATE_FIELD_L4_DPORT = 17, + TEMPLATE_FIELD_L34_HEADER = 18, + TEMPLATE_FIELD_TCP_INFO = 19, + TEMPLATE_FIELD_FIELD_SELECTOR_VALID = 20, + TEMPLATE_FIELD_FIELD_SELECTOR_0 = 21, + TEMPLATE_FIELD_FIELD_SELECTOR_1 = 22, + TEMPLATE_FIELD_FIELD_SELECTOR_2 = 23, + TEMPLATE_FIELD_FIELD_SELECTOR_3 = 24, + TEMPLATE_FIELD_FIELD_SELECTOR_4 = 25, + TEMPLATE_FIELD_FIELD_SELECTOR_5 = 26, + TEMPLATE_FIELD_SIP2 = 27, + TEMPLATE_FIELD_SIP3 = 28, + TEMPLATE_FIELD_SIP4 = 29, + TEMPLATE_FIELD_SIP5 = 30, + TEMPLATE_FIELD_SIP6 = 31, + TEMPLATE_FIELD_SIP7 = 32, + TEMPLATE_FIELD_DIP2 = 33, + TEMPLATE_FIELD_DIP3 = 34, + TEMPLATE_FIELD_DIP4 = 35, + TEMPLATE_FIELD_DIP5 = 36, + TEMPLATE_FIELD_DIP6 = 37, + TEMPLATE_FIELD_DIP7 = 38, + TEMPLATE_FIELD_PKT_INFO = 39, + TEMPLATE_FIELD_FLOW_LABEL = 40, + TEMPLATE_FIELD_DSAP_SSAP = 41, + TEMPLATE_FIELD_SNAP_OUI = 42, + TEMPLATE_FIELD_FWD_VID = 43, + TEMPLATE_FIELD_RANGE_CHK = 44, + TEMPLATE_FIELD_VLAN_GMSK = 45, // VLAN Group Mask/IP range check + TEMPLATE_FIELD_DLP = 46, + TEMPLATE_FIELD_META_DATA = 47, + TEMPLATE_FIELD_SRC_FWD_VID = 48, + TEMPLATE_FIELD_SLP = 49, +}; + +/* The meaning of TEMPLATE_FIELD_VLAN depends on phase and the configuration in + * RTL930X_PIE_CTRL. We use always the same definition and map to the inner VLAN tag: + */ +#define TEMPLATE_FIELD_VLAN TEMPLATE_FIELD_ITAG + +// Number of fixed templates predefined in the RTL9300 SoC +#define N_FIXED_TEMPLATES 5 +// RTL9300 specific predefined templates +static enum template_field_id fixed_templates[N_FIXED_TEMPLATES][N_FIXED_FIELDS] = +{ + { + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_SMAC0, TEMPLATE_FIELD_SMAC1, TEMPLATE_FIELD_SMAC2, + TEMPLATE_FIELD_VLAN, TEMPLATE_FIELD_IP_TOS_PROTO, TEMPLATE_FIELD_DSAP_SSAP, + TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1 + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, + TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_IP_TOS_PROTO, TEMPLATE_FIELD_TCP_INFO, + TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_L4_DPORT, TEMPLATE_FIELD_VLAN, + TEMPLATE_FIELD_RANGE_CHK, TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1 + }, { + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_VLAN, TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_IP_TOS_PROTO, + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, + TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_L4_DPORT + }, { + TEMPLATE_FIELD_DIP0, TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_DIP2, + TEMPLATE_FIELD_DIP3, TEMPLATE_FIELD_DIP4, TEMPLATE_FIELD_DIP5, + TEMPLATE_FIELD_DIP6, TEMPLATE_FIELD_DIP7, TEMPLATE_FIELD_IP_TOS_PROTO, + TEMPLATE_FIELD_TCP_INFO, TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_L4_DPORT + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_SIP2, + TEMPLATE_FIELD_SIP3, TEMPLATE_FIELD_SIP4, TEMPLATE_FIELD_SIP5, + TEMPLATE_FIELD_SIP6, TEMPLATE_FIELD_SIP7, TEMPLATE_FIELD_VLAN, + TEMPLATE_FIELD_RANGE_CHK, TEMPLATE_FIELD_SPM1, TEMPLATE_FIELD_SPM1 + }, +}; + +void rtl930x_print_matrix(void) +{ + int i; + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + + for (i = 0; i < 29; i++) { + rtl_table_read(r, i); + pr_debug("> %08x\n", sw_r32(rtl_table_data(r, 0))); + } + rtl_table_release(r); +} + +inline void rtl930x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL930X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL930X_TBL_ACCESS_CTRL_0) & (1 << 17)); +} + +inline void rtl930x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL930X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL930X_TBL_ACCESS_CTRL_1) & (1 << 17)); +} + +inline int rtl930x_tbl_access_data_0(int i) +{ + return RTL930X_TBL_ACCESS_DATA_0(i); +} + +static inline int rtl930x_l2_port_new_salrn(int p) +{ + return RTL930X_L2_PORT_SALRN(p); +} + +static inline int rtl930x_l2_port_new_sa_fwd(int p) +{ + // TODO: The definition of the fields changed, because of the master-cpu in a stack + return RTL930X_L2_PORT_NEW_SA_FWD(p); +} + +inline static int rtl930x_trk_mbr_ctr(int group) +{ + return RTL930X_TRK_MBR_CTRL + (group << 2); +} + +static void rtl930x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w; + // Read VLAN table (1) via register 0 + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 1); + + rtl_table_read(r, vlan); + v = sw_r32(rtl_table_data(r, 0)); + w = sw_r32(rtl_table_data(r, 1)); + pr_debug("VLAN_READ %d: %08x %08x\n", vlan, v, w); + rtl_table_release(r); + + info->tagged_ports = v >> 3; + info->profile_id = (w >> 24) & 7; + info->hash_mc_fid = !!(w & BIT(27)); + info->hash_uc_fid = !!(w & BIT(28)); + info->fid = ((v & 0x7) << 3) | ((w >> 29) & 0x7); + + // Read UNTAG table via table register 2 + r = rtl_table_get(RTL9300_TBL_2, 0); + rtl_table_read(r, vlan); + v = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); + + info->untagged_ports = v >> 3; +} + +static void rtl930x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w; + // Access VLAN table (1) via register 0 + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 1); + + v = info->tagged_ports << 3; + v |= ((u32)info->fid) >> 3; + + w = ((u32)info->fid) << 29; + w |= info->hash_mc_fid ? BIT(27) : 0; + w |= info->hash_uc_fid ? BIT(28) : 0; + w |= info->profile_id << 24; + + sw_w32(v, rtl_table_data(r, 0)); + sw_w32(w, rtl_table_data(r, 1)); + + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +void rtl930x_vlan_profile_dump(int profile) +{ + u32 p[5]; + + if (profile < 0 || profile > 7) + return; + + p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile)); + p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4); + p[2] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 8) & 0x1FFFFFFF; + p[3] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 12) & 0x1FFFFFFF; + p[4] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 16) & 0x1FFFFFFF; + + pr_info("VLAN %d: L2 learn: %d; Unknown MC PMasks: L2 %0x, IPv4 %0x, IPv6: %0x", + profile, p[0] & (3 << 21), p[2], p[3], p[4]); + pr_info(" Routing enabled: IPv4 UC %c, IPv6 UC %c, IPv4 MC %c, IPv6 MC %c\n", + p[0] & BIT(17) ? 'y' : 'n', p[0] & BIT(16) ? 'y' : 'n', + p[0] & BIT(13) ? 'y' : 'n', p[0] & BIT(12) ? 'y' : 'n'); + pr_info(" Bridge enabled: IPv4 MC %c, IPv6 MC %c,\n", + p[0] & BIT(15) ? 'y' : 'n', p[0] & BIT(14) ? 'y' : 'n'); + pr_info("VLAN profile %d: raw %08x %08x %08x %08x %08x\n", + profile, p[0], p[1], p[2], p[3], p[4]); +} + +static void rtl930x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_2, 0); + + sw_w32(portmask << 3, rtl_table_data(r, 0)); + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +/* Sets the L2 forwarding to be based on either the inner VLAN tag or the outer + */ +static void rtl930x_vlan_fwd_on_inner(int port, bool is_set) +{ + // Always set all tag modes to fwd based on either inner or outer tag + if (is_set) + sw_w32_mask(0, 0xf, RTL930X_VLAN_PORT_FWD + (port << 2)); + else + sw_w32_mask(0xf, 0, RTL930X_VLAN_PORT_FWD + (port << 2)); +} + +static void rtl930x_vlan_profile_setup(int profile) +{ + u32 p[5]; + + pr_info("In %s\n", __func__); + p[0] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile)); + p[1] = sw_r32(RTL930X_VLAN_PROFILE_SET(profile) + 4); + + // Enable routing of Ipv4/6 Unicast and IPv4/6 Multicast traffic + p[0] |= BIT(17) | BIT(16) | BIT(13) | BIT(12); + p[2] = 0x1fffffff; // L2 unknown MC flooding portmask all ports, including the CPU-port + p[3] = 0x1fffffff; // IPv4 unknown MC flooding portmask + p[4] = 0x1fffffff; // IPv6 unknown MC flooding portmask + + sw_w32(p[0], RTL930X_VLAN_PROFILE_SET(profile)); + sw_w32(p[1], RTL930X_VLAN_PROFILE_SET(profile) + 4); + sw_w32(p[2], RTL930X_VLAN_PROFILE_SET(profile) + 8); + sw_w32(p[3], RTL930X_VLAN_PROFILE_SET(profile) + 12); + sw_w32(p[4], RTL930X_VLAN_PROFILE_SET(profile) + 16); +} + +static void rtl930x_l2_learning_setup(void) +{ + // Portmask for flooding broadcast traffic + sw_w32(0x1fffffff, RTL930X_L2_BC_FLD_PMSK); + + // Portmask for flooding unicast traffic with unknown destination + sw_w32(0x1fffffff, RTL930X_L2_UNKN_UC_FLD_PMSK); + + // Limit learning to maximum: 32k entries, after that just flood (bits 0-1) + sw_w32((0x7fff << 2) | 0, RTL930X_L2_LRN_CONSTRT_CTRL); +} + +static void rtl930x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 17 /* Execute cmd */ + | 0 << 16 /* Read */ + | 4 << 12 /* Table type 0b10 */ + | (msti & 0xfff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 2; i++) + port_state[i] = sw_r32(RTL930X_TBL_ACCESS_DATA_0(i)); + pr_debug("MSTI: %d STATE: %08x, %08x\n", msti, port_state[0], port_state[1]); +} + +static void rtl930x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 17 /* Execute cmd */ + | 1 << 16 /* Write */ + | 4 << 12 /* Table type 4 */ + | (msti & 0xfff); + + for (i = 0; i < 2; i++) + sw_w32(port_state[i], RTL930X_TBL_ACCESS_DATA_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +static inline int rtl930x_mac_force_mode_ctrl(int p) +{ + return RTL930X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl930x_mac_port_ctrl(int p) +{ + return RTL930X_MAC_L2_PORT_CTRL(p); +} + +static inline int rtl930x_mac_link_spd_sts(int p) +{ + return RTL930X_MAC_LINK_SPD_STS(p); +} + +static u64 rtl930x_l2_hash_seed(u64 mac, u32 vid) +{ + u64 v = vid; + + v <<= 48; + v |= mac; + + return v; +} + +/* + * Calculate both the block 0 and the block 1 hash by applyingthe same hash + * algorithm as the one used currently by the ASIC to the seed, and return + * both hashes in the lower and higher word of the return value since only 12 bit of + * the hash are significant + */ +static u32 rtl930x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 k0, k1, h1, h2, h; + + k0 = (u32) (((seed >> 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) + ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) + ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff)); + + h1 = (seed >> 11) & 0x7ff; + h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); + + h2 = (seed >> 33) & 0x7ff; + h2 = ((h2 & 0x3f) << 5)| ((h2 >> 6) & 0x3f); + + k1 = (u32) (((seed << 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) ^ h2 + ^ ((seed >> 22) & 0x7ff) ^ h1 + ^ (seed & 0x7ff)); + + // Algorithm choice for block 0 + if (sw_r32(RTL930X_L2_CTRL) & BIT(0)) + h = k1; + else + h = k0; + + /* Algorithm choice for block 1 + * Since k0 and k1 are < 2048, adding 2048 will offset the hash into the second + * half of hash-space + * 2048 is in fact the hash-table size 16384 divided by 4 hashes per bucket + * divided by 2 to divide the hash space in 2 + */ + if (sw_r32(RTL930X_L2_CTRL) & BIT(1)) + h |= (k1 + 2048) << 16; + else + h |= (k0 + 2048) << 16; + + return h; +} + +/* + * Fills an L2 entry structure from the SoC registers + */ +static void rtl930x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) +{ + pr_debug("In %s valid?\n", __func__); + e->valid = !!(r[2] & BIT(31)); + if (!e->valid) + return; + + pr_debug("In %s is valid\n", __func__); + e->is_ip_mc = false; + e->is_ipv6_mc = false; + + // TODO: Is there not a function to copy directly MAC memory? + e->mac[0] = (r[0] >> 24); + e->mac[1] = (r[0] >> 16); + e->mac[2] = (r[0] >> 8); + e->mac[3] = r[0]; + e->mac[4] = (r[1] >> 24); + e->mac[5] = (r[1] >> 16); + + e->next_hop = !!(r[2] & BIT(12)); + e->rvid = r[1] & 0xfff; + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + e->type = L2_UNICAST; + e->is_static = !!(r[2] & BIT(14)); + e->port = (r[2] >> 20) & 0x3ff; + // Check for trunk port + if (r[2] & BIT(30)) { + e->is_trunk = true; + e->stack_dev = (e->port >> 9) & 1; + e->trunk = e->port & 0x3f; + } else { + e->is_trunk = false; + e->stack_dev = (e->port >> 6) & 0xf; + e->port = e->port & 0x3f; + } + + e->block_da = !!(r[2] & BIT(15)); + e->block_sa = !!(r[2] & BIT(16)); + e->suspended = !!(r[2] & BIT(13)); + e->age = (r[2] >> 17) & 3; + e->valid = true; + // the UC_VID field in hardware is used for the VID or for the route id + if (e->next_hop) { + e->nh_route_id = r[2] & 0x7ff; + e->vid = 0; + } else { + e->vid = r[2] & 0xfff; + e->nh_route_id = 0; + } + } else { + e->valid = true; + e->type = L2_MULTICAST; + e->mc_portmask_index = (r[2] >> 16) & 0x3ff; + } +} + +/* + * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry + */ +static void rtl930x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e) +{ + u32 port; + + if (!e->valid) { + r[0] = r[1] = r[2] = 0; + return; + } + + r[2] = BIT(31); // Set valid bit + + r[0] = ((u32)e->mac[0]) << 24 | ((u32)e->mac[1]) << 16 + | ((u32)e->mac[2]) << 8 | ((u32)e->mac[3]); + r[1] = ((u32)e->mac[4]) << 24 | ((u32)e->mac[5]) << 16; + + r[2] |= e->next_hop ? BIT(12) : 0; + + if (e->type == L2_UNICAST) { + r[2] |= e->is_static ? BIT(14) : 0; + r[1] |= e->rvid & 0xfff; + r[2] |= (e->port & 0x3ff) << 20; + if (e->is_trunk) { + r[2] |= BIT(30); + port = e->stack_dev << 9 | (e->port & 0x3f); + } else { + port = (e->stack_dev & 0xf) << 6; + port |= e->port & 0x3f; + } + r[2] |= port << 20; + r[2] |= e->block_da ? BIT(15) : 0; + r[2] |= e->block_sa ? BIT(17) : 0; + r[2] |= e->suspended ? BIT(13) : 0; + r[2] |= (e->age & 0x3) << 17; + // the UC_VID field in hardware is used for the VID or for the route id + if (e->next_hop) + r[2] |= e->nh_route_id & 0x7ff; + else + r[2] |= e->vid & 0xfff; + } else { // L2_MULTICAST + r[2] |= (e->mc_portmask_index & 0x3ff) << 16; + r[2] |= e->mc_mac_index & 0x7ff; + } +} + +/* + * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table + * hash is the id of the bucket and pos is the position of the entry in that bucket + * The data read from the SoC is filled into rtl838x_l2_entry + */ +static u64 rtl930x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0); + u32 idx; + int i; + u64 mac; + u64 seed; + + pr_debug("%s: hash %08x, pos: %d\n", __func__, hash, pos); + + /* On the RTL93xx, 2 different hash algorithms are used making it a total of + * 8 buckets that need to be searched, 4 for each hash-half + * Use second hash space when bucket is between 4 and 8 */ + if (pos >= 4) { + pos -= 4; + hash >>= 16; + } else { + hash &= 0xffff; + } + + idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket + pr_debug("%s: NOW hash %08x, pos: %d\n", __func__, hash, pos); + + rtl_table_read(q, idx); + for (i = 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl930x_fill_l2_entry(r, e); + + pr_debug("%s: valid: %d, nh: %d\n", __func__, e->valid, e->next_hop); + if (!e->valid) + return 0; + + mac = ((u64)e->mac[0]) << 40 | ((u64)e->mac[1]) << 32 | ((u64)e->mac[2]) << 24 + | ((u64)e->mac[3]) << 16 | ((u64)e->mac[4]) << 8 | ((u64)e->mac[5]); + + seed = rtl930x_l2_hash_seed(mac, e->rvid); + pr_debug("%s: mac %016llx, seed %016llx\n", __func__, mac, seed); + // return vid with concatenated mac as unique id + return seed; +} + +static void rtl930x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 0); + u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket + int i; + + pr_debug("%s: hash %d, pos %d\n", __func__, hash, pos); + pr_debug("%s: index %d -> mac %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, idx, + e->mac[0], e->mac[1], e->mac[2], e->mac[3],e->mac[4],e->mac[5]); + + rtl930x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl930x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1); + int i; + + rtl_table_read(q, idx); + for (i= 0; i < 3; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl930x_fill_l2_entry(r, e); + if (!e->valid) + return 0; + + // return mac with concatenated vid as unique id + return ((u64)r[0] << 28) | ((r[1] & 0xffff0000) >> 4) | e->vid; +} + +static void rtl930x_write_cam(int idx, struct rtl838x_l2_entry *e) +{ + u32 r[3]; + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 1); // Access L2 Table 1 + int i; + + rtl930x_fill_l2_row(r, e); + + for (i= 0; i < 3; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); +} + +static u64 rtl930x_read_mcast_pmask(int idx) +{ + u32 portmask; + // Read MC_PORTMASK (2) via register RTL9300_TBL_L2 + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2); + + rtl_table_read(q, idx); + portmask = sw_r32(rtl_table_data(q, 0)); + portmask >>= 3; + rtl_table_release(q); + + pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, portmask); + return portmask; +} + +static void rtl930x_write_mcast_pmask(int idx, u64 portmask) +{ + u32 pm = portmask; + + // Access MC_PORTMASK (2) via register RTL9300_TBL_L2 + struct table_reg *q = rtl_table_get(RTL9300_TBL_L2, 2); + + pr_debug("%s: Index idx %d has portmask %08x\n", __func__, idx, pm); + pm <<= 3; + sw_w32(pm, rtl_table_data(q, 0)); + rtl_table_write(q, idx); + rtl_table_release(q); +} + +u64 rtl930x_traffic_get(int source) +{ + u32 v; + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + + rtl_table_read(r, source); + v = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); + return v >> 3; +} + +/* + * Enable traffic between a source port and a destination port matrix + */ +void rtl930x_traffic_set(int source, u64 dest_matrix) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + + sw_w32((dest_matrix << 3), rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl930x_traffic_enable(int source, int dest) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + rtl_table_read(r, source); + sw_w32_mask(0, BIT(dest + 3), rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl930x_traffic_disable(int source, int dest) +{ + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 6); + rtl_table_read(r, source); + sw_w32_mask(BIT(dest + 3), 0, rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl9300_dump_debug(void) +{ + int i; + u16 r = RTL930X_STAT_PRVTE_DROP_COUNTER0; + + for (i = 0; i < 10; i ++) { + pr_info("# %d %08x %08x %08x %08x %08x %08x %08x %08x\n", i * 8, + sw_r32(r), sw_r32(r + 4), sw_r32(r + 8), sw_r32(r + 12), + sw_r32(r + 16), sw_r32(r + 20), sw_r32(r + 24), sw_r32(r + 28)); + r += 32; + } + pr_info("# %08x %08x %08x %08x %08x\n", + sw_r32(r), sw_r32(r + 4), sw_r32(r + 8), sw_r32(r + 12), sw_r32(r + 16)); + rtl930x_print_matrix(); + pr_info("RTL930X_L2_PORT_SABLK_CTRL: %08x, RTL930X_L2_PORT_DABLK_CTRL %08x\n", + sw_r32(RTL930X_L2_PORT_SABLK_CTRL), sw_r32(RTL930X_L2_PORT_DABLK_CTRL) + + ); +} + +irqreturn_t rtl930x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 ports = sw_r32(RTL930X_ISR_PORT_LINK_STS_CHG); + u32 link; + int i; + + /* Clear status */ + sw_w32(ports, RTL930X_ISR_PORT_LINK_STS_CHG); + + for (i = 0; i < 28; i++) { + if (ports & BIT(i)) { + /* Read the register twice because of issues with latency at least + * with the external RTL8226 PHY on the XGS1210 */ + link = sw_r32(RTL930X_MAC_LINK_STS); + link = sw_r32(RTL930X_MAC_LINK_STS); + if (link & BIT(i)) + dsa_port_phylink_mac_change(ds, i, true); + else + dsa_port_phylink_mac_change(ds, i, false); + } + } + + return IRQ_HANDLED; +} + +int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, val); + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + val &= 0xffff; + mutex_lock(&smi_lock); + + sw_w32(BIT(port), RTL930X_SMI_ACCESS_PHY_CTRL_0); + sw_w32_mask(0xffff << 16, val << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + v = reg << 20 | page << 3 | 0x1f << 15 | BIT(2) | BIT(0); + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while (v & 0x1); + + if (v & 0x2) + err = -EIO; + + mutex_unlock(&smi_lock); + + return err; +} + +int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + int err = 0; + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + + sw_w32_mask(0xffff << 16, port << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + v = reg << 20 | page << 3 | 0x1f << 15 | 1; + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while ( v & 0x1); + + if (v & BIT(25)) { + pr_debug("Error reading phy %d, register %d\n", port, reg); + err = -EIO; + } + *val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x\n", __func__, port, page, reg, *val); + + mutex_unlock(&smi_lock); + + return err; +} + +/* + * Write to an mmd register of the PHY + */ +int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + + mutex_lock(&smi_lock); + + // Set PHY to access + sw_w32(BIT(port), RTL930X_SMI_ACCESS_PHY_CTRL_0); + + // Set data to write + sw_w32_mask(0xffff << 16, val << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); + + v = BIT(2) | BIT(1) | BIT(0); // WRITE | MMD-access | EXEC + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while (v & BIT(0)); + + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err); + mutex_unlock(&smi_lock); + return err; +} + +/* + * Read an mmd register of the PHY + */ +int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + + mutex_lock(&smi_lock); + + // Set PHY to access + sw_w32_mask(0xffff << 16, port << 16, RTL930X_SMI_ACCESS_PHY_CTRL_2); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | (regnum & 0xffff), RTL930X_SMI_ACCESS_PHY_CTRL_3); + + v = BIT(1) | BIT(0); // MMD-access | EXEC + sw_w32(v, RTL930X_SMI_ACCESS_PHY_CTRL_1); + + do { + v = sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_1); + } while (v & BIT(0)); + // There is no error-checking via BIT 25 of v, as it does not seem to be set correctly + *val = (sw_r32(RTL930X_SMI_ACCESS_PHY_CTRL_2) & 0xffff); + pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err); + + mutex_unlock(&smi_lock); + + return err; +} + +/* + * Calculate both the block 0 and the block 1 hash, and return in + * lower and higher word of the return value since only 12 bit of + * the hash are significant + */ +u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 k0, k1, h1, h2, h; + + k0 = (u32) (((seed >> 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) + ^ ((seed >> 33) & 0x7ff) ^ ((seed >> 22) & 0x7ff) + ^ ((seed >> 11) & 0x7ff) ^ (seed & 0x7ff)); + + h1 = (seed >> 11) & 0x7ff; + h1 = ((h1 & 0x1f) << 6) | ((h1 >> 5) & 0x3f); + + h2 = (seed >> 33) & 0x7ff; + h2 = ((h2 & 0x3f) << 5)| ((h2 >> 6) & 0x3f); + + k1 = (u32) (((seed << 55) & 0x1f) ^ ((seed >> 44) & 0x7ff) ^ h2 + ^ ((seed >> 22) & 0x7ff) ^ h1 + ^ (seed & 0x7ff)); + + // Algorithm choice for block 0 + if (sw_r32(RTL930X_L2_CTRL) & BIT(0)) + h = k1; + else + h = k0; + + /* Algorithm choice for block 1 + * Since k0 and k1 are < 2048, adding 2048 will offset the hash into the second + * half of hash-space + * 2048 is in fact the hash-table size 16384 divided by 4 hashes per bucket + * divided by 2 to divide the hash space in 2 + */ + if (sw_r32(RTL930X_L2_CTRL) & BIT(1)) + h |= (k1 + 2048) << 16; + else + h |= (k0 + 2048) << 16; + + return h; +} + +/* + * Enables or disables the EEE/EEEP capability of a port + */ +void rtl930x_port_eee_set(struct rtl838x_switch_priv *priv, int port, bool enable) +{ + u32 v; + + // This works only for Ethernet ports, and on the RTL930X, ports from 26 are SFP + if (port >= 26) + return; + + pr_debug("In %s: setting port %d to %d\n", __func__, port, enable); + v = enable ? 0x3f : 0x0; + + // Set EEE/EEEP state for 100, 500, 1000MBit and 2.5, 5 and 10GBit + sw_w32_mask(0, v << 10, rtl930x_mac_force_mode_ctrl(port)); + + // Set TX/RX EEE state + v = enable ? 0x3 : 0x0; + sw_w32(v, RTL930X_EEE_CTRL(port)); + + priv->ports[port].eee_enabled = enable; +} + +/* + * Get EEE own capabilities and negotiation result + */ +int rtl930x_eee_port_ability(struct rtl838x_switch_priv *priv, struct ethtool_eee *e, int port) +{ + u32 link, a; + + if (port >= 26) + return -ENOTSUPP; + + pr_info("In %s, port %d\n", __func__, port); + link = sw_r32(RTL930X_MAC_LINK_STS); + link = sw_r32(RTL930X_MAC_LINK_STS); + if (!(link & BIT(port))) + return 0; + + pr_info("Setting advertised\n"); + if (sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(10)) + e->advertised |= ADVERTISED_100baseT_Full; + + if (sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(12)) + e->advertised |= ADVERTISED_1000baseT_Full; + + if (priv->ports[port].is2G5 && sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(13)) { + pr_info("ADVERTISING 2.5G EEE\n"); + e->advertised |= ADVERTISED_2500baseX_Full; + } + + if (priv->ports[port].is10G && sw_r32(rtl930x_mac_force_mode_ctrl(port)) & BIT(15)) + e->advertised |= ADVERTISED_10000baseT_Full; + + a = sw_r32(RTL930X_MAC_EEE_ABLTY); + a = sw_r32(RTL930X_MAC_EEE_ABLTY); + pr_info("Link partner: %08x\n", a); + if (a & BIT(port)) { + e->lp_advertised = ADVERTISED_100baseT_Full; + e->lp_advertised |= ADVERTISED_1000baseT_Full; + if (priv->ports[port].is2G5) + e->lp_advertised |= ADVERTISED_2500baseX_Full; + if (priv->ports[port].is10G) + e->lp_advertised |= ADVERTISED_10000baseT_Full; + } + + // Read 2x to clear latched state + a = sw_r32(RTL930X_EEEP_PORT_CTRL(port)); + a = sw_r32(RTL930X_EEEP_PORT_CTRL(port)); + pr_info("%s RTL930X_EEEP_PORT_CTRL: %08x\n", __func__, a); + + return 0; +} + +static void rtl930x_init_eee(struct rtl838x_switch_priv *priv, bool enable) +{ + int i; + + pr_info("Setting up EEE, state: %d\n", enable); + + // Setup EEE on all ports + for (i = 0; i < priv->cpu_port; i++) { + if (priv->ports[i].phy) + rtl930x_port_eee_set(priv, i, enable); + } + + priv->eee_enabled = enable; +} +#define HASH_PICK(val, lsb, len) ((val & (((1 << len) - 1) << lsb)) >> lsb) + +static u32 rtl930x_l3_hash4(u32 ip, int algorithm, bool move_dip) +{ + u32 rows[4]; + u32 hash; + u32 s0, s1, pH; + + memset(rows, 0, sizeof(rows)); + + rows[0] = HASH_PICK(ip, 27, 5); + rows[1] = HASH_PICK(ip, 18, 9); + rows[2] = HASH_PICK(ip, 9, 9); + + if (!move_dip) + rows[3] = HASH_PICK(ip, 0, 9); + + if (!algorithm) { + hash = rows[0] ^ rows[1] ^ rows[2] ^ rows[3]; + } else { + s0 = rows[0] + rows[1] + rows[2]; + s1 = (s0 & 0x1ff) + ((s0 & (0x1ff << 9)) >> 9); + pH = (s1 & 0x1ff) + ((s1 & (0x1ff << 9)) >> 9); + hash = pH ^ rows[3]; + } + return hash; +} + +static u32 rtl930x_l3_hash6(struct in6_addr *ip6, int algorithm, bool move_dip) +{ + u32 rows[16]; + u32 hash; + u32 s0, s1, pH; + + rows[0] = (HASH_PICK(ip6->s6_addr[0], 6, 2) << 0); + rows[1] = (HASH_PICK(ip6->s6_addr[0], 0, 6) << 3) | HASH_PICK(ip6->s6_addr[1], 5, 3); + rows[2] = (HASH_PICK(ip6->s6_addr[1], 0, 5) << 4) | HASH_PICK(ip6->s6_addr[2], 4, 4); + rows[3] = (HASH_PICK(ip6->s6_addr[2], 0, 4) << 5) | HASH_PICK(ip6->s6_addr[3], 3, 5); + rows[4] = (HASH_PICK(ip6->s6_addr[3], 0, 3) << 6) | HASH_PICK(ip6->s6_addr[4], 2, 6); + rows[5] = (HASH_PICK(ip6->s6_addr[4], 0, 2) << 7) | HASH_PICK(ip6->s6_addr[5], 1, 7); + rows[6] = (HASH_PICK(ip6->s6_addr[5], 0, 1) << 8) | HASH_PICK(ip6->s6_addr[6], 0, 8); + rows[7] = (HASH_PICK(ip6->s6_addr[7], 0, 8) << 1) | HASH_PICK(ip6->s6_addr[8], 7, 1); + rows[8] = (HASH_PICK(ip6->s6_addr[8], 0, 7) << 2) | HASH_PICK(ip6->s6_addr[9], 6, 2); + rows[9] = (HASH_PICK(ip6->s6_addr[9], 0, 6) << 3) | HASH_PICK(ip6->s6_addr[10], 5, 3); + rows[10] = (HASH_PICK(ip6->s6_addr[10], 0, 5) << 4) | HASH_PICK(ip6->s6_addr[11], 4, 4); + if (!algorithm) { + rows[11] = (HASH_PICK(ip6->s6_addr[11], 0, 4) << 5) + | (HASH_PICK(ip6->s6_addr[12], 3, 5) << 0); + rows[12] = (HASH_PICK(ip6->s6_addr[12], 0, 3) << 6) + | (HASH_PICK(ip6->s6_addr[13], 2, 6) << 0); + rows[13] = (HASH_PICK(ip6->s6_addr[13], 0, 2) << 7) + | (HASH_PICK(ip6->s6_addr[14], 1, 7) << 0); + if (!move_dip) { + rows[14] = (HASH_PICK(ip6->s6_addr[14], 0, 1) << 8) + | (HASH_PICK(ip6->s6_addr[15], 0, 8) << 0); + } + hash = rows[0] ^ rows[1] ^ rows[2] ^ rows[3] ^ rows[4] ^ rows[5] ^ rows[6] + ^ rows[7] ^ rows[8] ^ rows[9] ^ rows[10] ^ rows[11] ^ rows[12] + ^ rows[13] ^ rows[14]; + } else { + rows[11] = (HASH_PICK(ip6->s6_addr[11], 0, 4) << 5); + rows[12] = (HASH_PICK(ip6->s6_addr[12], 3, 5) << 0); + rows[13] = (HASH_PICK(ip6->s6_addr[12], 0, 3) << 6) + | HASH_PICK(ip6->s6_addr[13], 2, 6); + rows[14] = (HASH_PICK(ip6->s6_addr[13], 0, 2) << 7) + | HASH_PICK(ip6->s6_addr[14], 1, 7); + if (!move_dip) { + rows[15] = (HASH_PICK(ip6->s6_addr[14], 0, 1) << 8) + | (HASH_PICK(ip6->s6_addr[15], 0, 8) << 0); + } + s0 = rows[12] + rows[13] + rows[14]; + s1 = (s0 & 0x1ff) + ((s0 & (0x1ff << 9)) >> 9); + pH = (s1 & 0x1ff) + ((s1 & (0x1ff << 9)) >> 9); + hash = rows[0] ^ rows[1] ^ rows[2] ^ rows[3] ^ rows[4] ^ rows[5] ^ rows[6] + ^ rows[7] ^ rows[8] ^ rows[9] ^ rows[10] ^ rows[11] ^ pH ^ rows[15]; + } + return hash; +} + +/* + * Read a prefix route entry from the L3_PREFIX_ROUTE_IPUC table + * We currently only support IPv4 and IPv6 unicast route + */ +static void rtl930x_route_read(int idx, struct rtl83xx_route *rt) +{ + u32 v, ip4_m; + bool host_route, default_route; + struct in6_addr ip6_m; + + // Read L3_PREFIX_ROUTE_IPUC table (2) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 2); + + rtl_table_read(r, idx); + // The table has a size of 11 registers + rt->attr.valid = !!(sw_r32(rtl_table_data(r, 0)) & BIT(31)); + if (!rt->attr.valid) + goto out; + + rt->attr.type = (sw_r32(rtl_table_data(r, 0)) >> 29) & 0x3; + + v = sw_r32(rtl_table_data(r, 10)); + host_route = !!(v & BIT(21)); + default_route = !!(v & BIT(20)); + rt->prefix_len = -1; + pr_info("%s: host route %d, default_route %d\n", __func__, host_route, default_route); + + switch (rt->attr.type) { + case 0: // IPv4 Unicast route + rt->dst_ip = sw_r32(rtl_table_data(r, 4)); + ip4_m = sw_r32(rtl_table_data(r, 9)); + pr_info("%s: Read ip4 mask: %08x\n", __func__, ip4_m); + rt->prefix_len = host_route ? 32 : -1; + rt->prefix_len = (rt->prefix_len < 0 && default_route) ? 0 : -1; + if (rt->prefix_len < 0) + rt->prefix_len = inet_mask_len(ip4_m); + break; + case 2: // IPv6 Unicast route + ipv6_addr_set(&rt->dst_ip6, + sw_r32(rtl_table_data(r, 1)), sw_r32(rtl_table_data(r, 2)), + sw_r32(rtl_table_data(r, 3)), sw_r32(rtl_table_data(r, 4))); + ipv6_addr_set(&ip6_m, + sw_r32(rtl_table_data(r, 6)), sw_r32(rtl_table_data(r, 7)), + sw_r32(rtl_table_data(r, 8)), sw_r32(rtl_table_data(r, 9))); + rt->prefix_len = host_route ? 128 : 0; + rt->prefix_len = (rt->prefix_len < 0 && default_route) ? 0 : -1; + if (rt->prefix_len < 0) + rt->prefix_len = find_last_bit((unsigned long int *)&ip6_m.s6_addr32, + 128); + break; + case 1: // IPv4 Multicast route + case 3: // IPv6 Multicast route + pr_warn("%s: route type not supported\n", __func__); + goto out; + } + + rt->attr.hit = !!(v & BIT(22)); + rt->attr.action = (v >> 18) & 3; + rt->nh.id = (v >> 7) & 0x7ff; + rt->attr.ttl_dec = !!(v & BIT(6)); + rt->attr.ttl_check = !!(v & BIT(5)); + rt->attr.dst_null = !!(v & BIT(4)); + rt->attr.qos_as = !!(v & BIT(3)); + rt->attr.qos_prio = v & 0x7; + pr_info("%s: index %d is valid: %d\n", __func__, idx, rt->attr.valid); + pr_info("%s: next_hop: %d, hit: %d, action :%d, ttl_dec %d, ttl_check %d, dst_null %d\n", + __func__, rt->nh.id, rt->attr.hit, rt->attr.action, + rt->attr.ttl_dec, rt->attr.ttl_check, rt->attr.dst_null); + pr_info("%s: GW: %pI4, prefix_len: %d\n", __func__, &rt->dst_ip, rt->prefix_len); +out: + rtl_table_release(r); +} + +static void rtl930x_net6_mask(int prefix_len, struct in6_addr *ip6_m) +{ + int o, b; + // Define network mask + o = prefix_len >> 3; + b = prefix_len & 0x7; + memset(ip6_m->s6_addr, 0xff, o); + ip6_m->s6_addr[o] |= b ? 0xff00 >> b : 0x00; +} + +/* + * Read a host route entry from the table using its index + * We currently only support IPv4 and IPv6 unicast route + */ +static void rtl930x_host_route_read(int idx, struct rtl83xx_route *rt) +{ + u32 v; + // Read L3_HOST_ROUTE_IPUC table (1) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 1); + + idx = ((idx / 6) * 8) + (idx % 6); + + pr_debug("In %s, physical index %d\n", __func__, idx); + rtl_table_read(r, idx); + // The table has a size of 5 (for UC, 11 for MC) registers + v = sw_r32(rtl_table_data(r, 0)); + rt->attr.valid = !!(v & BIT(31)); + if (!rt->attr.valid) + goto out; + rt->attr.type = (v >> 29) & 0x3; + switch (rt->attr.type) { + case 0: // IPv4 Unicast route + rt->dst_ip = sw_r32(rtl_table_data(r, 4)); + break; + case 2: // IPv6 Unicast route + ipv6_addr_set(&rt->dst_ip6, + sw_r32(rtl_table_data(r, 3)), sw_r32(rtl_table_data(r, 2)), + sw_r32(rtl_table_data(r, 1)), sw_r32(rtl_table_data(r, 0))); + break; + case 1: // IPv4 Multicast route + case 3: // IPv6 Multicast route + pr_warn("%s: route type not supported\n", __func__); + goto out; + } + + rt->attr.hit = !!(v & BIT(20)); + rt->attr.dst_null = !!(v & BIT(19)); + rt->attr.action = (v >> 17) & 3; + rt->nh.id = (v >> 6) & 0x7ff; + rt->attr.ttl_dec = !!(v & BIT(5)); + rt->attr.ttl_check = !!(v & BIT(4)); + rt->attr.qos_as = !!(v & BIT(3)); + rt->attr.qos_prio = v & 0x7; + pr_debug("%s: index %d is valid: %d\n", __func__, idx, rt->attr.valid); + pr_debug("%s: next_hop: %d, hit: %d, action :%d, ttl_dec %d, ttl_check %d, dst_null %d\n", + __func__, rt->nh.id, rt->attr.hit, rt->attr.action, rt->attr.ttl_dec, rt->attr.ttl_check, + rt->attr.dst_null); + pr_debug("%s: Destination: %pI4\n", __func__, &rt->dst_ip); + +out: + rtl_table_release(r); +} + +/* + * Write a host route entry from the table using its index + * We currently only support IPv4 and IPv6 unicast route + */ +static void rtl930x_host_route_write(int idx, struct rtl83xx_route *rt) +{ + u32 v; + // Access L3_HOST_ROUTE_IPUC table (1) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 1); + // The table has a size of 5 (for UC, 11 for MC) registers + + idx = ((idx / 6) * 8) + (idx % 6); + + pr_debug("%s: index %d is valid: %d\n", __func__, idx, rt->attr.valid); + pr_debug("%s: next_hop: %d, hit: %d, action :%d, ttl_dec %d, ttl_check %d, dst_null %d\n", + __func__, rt->nh.id, rt->attr.hit, rt->attr.action, rt->attr.ttl_dec, rt->attr.ttl_check, + rt->attr.dst_null); + pr_debug("%s: GW: %pI4, prefix_len: %d\n", __func__, &rt->dst_ip, rt->prefix_len); + + v = BIT(31); // Entry is valid + v |= (rt->attr.type & 0x3) << 29; + v |= rt->attr.hit ? BIT(20) : 0; + v |= rt->attr.dst_null ? BIT(19) : 0; + v |= (rt->attr.action & 0x3) << 17; + v |= (rt->nh.id & 0x7ff) << 6; + v |= rt->attr.ttl_dec ? BIT(5) : 0; + v |= rt->attr.ttl_check ? BIT(4) : 0; + v |= rt->attr.qos_as ? BIT(3) : 0; + v |= rt->attr.qos_prio & 0x7; + + sw_w32(v, rtl_table_data(r, 0)); + switch (rt->attr.type) { + case 0: // IPv4 Unicast route + sw_w32(0, rtl_table_data(r, 1)); + sw_w32(0, rtl_table_data(r, 2)); + sw_w32(0, rtl_table_data(r, 3)); + sw_w32(rt->dst_ip, rtl_table_data(r, 4)); + break; + case 2: // IPv6 Unicast route + sw_w32(rt->dst_ip6.s6_addr32[0], rtl_table_data(r, 1)); + sw_w32(rt->dst_ip6.s6_addr32[1], rtl_table_data(r, 2)); + sw_w32(rt->dst_ip6.s6_addr32[2], rtl_table_data(r, 3)); + sw_w32(rt->dst_ip6.s6_addr32[3], rtl_table_data(r, 4)); + break; + case 1: // IPv4 Multicast route + case 3: // IPv6 Multicast route + pr_warn("%s: route type not supported\n", __func__); + goto out; + } + + rtl_table_write(r, idx); + +out: + rtl_table_release(r); +} + +/* + * Look up the index of a prefix route in the routing table CAM for unicast IPv4/6 routes + * using hardware offload. + */ +static int rtl930x_route_lookup_hw(struct rtl83xx_route *rt) +{ + u32 ip4_m, v; + struct in6_addr ip6_m; + int i; + + if (rt->attr.type == 1 || rt->attr.type == 3) // Hardware only supports UC routes + return -1; + + sw_w32_mask(0x3 << 19, rt->attr.type, RTL930X_L3_HW_LU_KEY_CTRL); + if (rt->attr.type) { // IPv6 + rtl930x_net6_mask(rt->prefix_len, &ip6_m); + for (i = 0; i < 4; i++) + sw_w32(rt->dst_ip6.s6_addr32[0] & ip6_m.s6_addr32[0], + RTL930X_L3_HW_LU_KEY_IP_CTRL + (i << 2)); + } else { // IPv4 + ip4_m = inet_make_mask(rt->prefix_len); + sw_w32(0, RTL930X_L3_HW_LU_KEY_IP_CTRL); + sw_w32(0, RTL930X_L3_HW_LU_KEY_IP_CTRL + 4); + sw_w32(0, RTL930X_L3_HW_LU_KEY_IP_CTRL + 8); + v = rt->dst_ip & ip4_m; + pr_info("%s: searching for %pI4\n", __func__, &v); + sw_w32(v, RTL930X_L3_HW_LU_KEY_IP_CTRL + 12); + } + + // Execute CAM lookup in SoC + sw_w32(BIT(15), RTL930X_L3_HW_LU_CTRL); + + // Wait until execute bit clears and result is ready + do { + v = sw_r32(RTL930X_L3_HW_LU_CTRL); + } while (v & BIT(15)); + + pr_info("%s: found: %d, index: %d\n", __func__, !!(v & BIT(14)), v & 0x1ff); + + // Test if search successful (BIT 14 set) + if (v & BIT(14)) + return v & 0x1ff; + + return -1; +} + +static int rtl930x_find_l3_slot(struct rtl83xx_route *rt, bool must_exist) +{ + int t, s, slot_width, algorithm, addr, idx; + u32 hash; + struct rtl83xx_route route_entry; + + // IPv6 entries take up 3 slots + slot_width = (rt->attr.type == 0) || (rt->attr.type == 2) ? 1 : 3; + + for (t = 0; t < 2; t++) { + algorithm = (sw_r32(RTL930X_L3_HOST_TBL_CTRL) >> (2 + t)) & 0x1; + hash = rtl930x_l3_hash4(rt->dst_ip, algorithm, false); + + pr_debug("%s: table %d, algorithm %d, hash %04x\n", __func__, t, algorithm, hash); + + for (s = 0; s < 6; s += slot_width) { + addr = (t << 12) | ((hash & 0x1ff) << 3) | s; + pr_debug("%s physical address %d\n", __func__, addr); + idx = ((addr / 8) * 6) + (addr % 8); + pr_debug("%s logical address %d\n", __func__, idx); + + rtl930x_host_route_read(idx, &route_entry); + pr_debug("%s route valid %d, route dest: %pI4, hit %d\n", __func__, + rt->attr.valid, &rt->dst_ip, rt->attr.hit); + if (!must_exist && rt->attr.valid) + return idx; + if (must_exist && route_entry.dst_ip == rt->dst_ip) + return idx; + } + } + + return -1; +} + +/* + * Write a prefix route into the routing table CAM at position idx + * Currently only IPv4 and IPv6 unicast routes are supported + */ +static void rtl930x_route_write(int idx, struct rtl83xx_route *rt) +{ + u32 v, ip4_m; + struct in6_addr ip6_m; + // Access L3_PREFIX_ROUTE_IPUC table (2) via register RTL9300_TBL_1 + // The table has a size of 11 registers (20 for MC) + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 2); + + pr_debug("%s: index %d is valid: %d\n", __func__, idx, rt->attr.valid); + pr_debug("%s: nexthop: %d, hit: %d, action :%d, ttl_dec %d, ttl_check %d, dst_null %d\n", + __func__, rt->nh.id, rt->attr.hit, rt->attr.action, + rt->attr.ttl_dec, rt->attr.ttl_check, rt->attr.dst_null); + pr_debug("%s: GW: %pI4, prefix_len: %d\n", __func__, &rt->dst_ip, rt->prefix_len); + + v = rt->attr.valid ? BIT(31) : 0; + v |= (rt->attr.type & 0x3) << 29; + sw_w32(v, rtl_table_data(r, 0)); + + v = rt->attr.hit ? BIT(22) : 0; + v |= (rt->attr.action & 0x3) << 18; + v |= (rt->nh.id & 0x7ff) << 7; + v |= rt->attr.ttl_dec ? BIT(6) : 0; + v |= rt->attr.ttl_check ? BIT(5) : 0; + v |= rt->attr.dst_null ? BIT(6) : 0; + v |= rt->attr.qos_as ? BIT(6) : 0; + v |= rt->attr.qos_prio & 0x7; + v |= rt->prefix_len == 0 ? BIT(20) : 0; // set default route bit + + // set bit mask for entry type always to 0x3 + sw_w32(0x3 << 29, rtl_table_data(r, 5)); + + switch (rt->attr.type) { + case 0: // IPv4 Unicast route + sw_w32(0, rtl_table_data(r, 1)); + sw_w32(0, rtl_table_data(r, 2)); + sw_w32(0, rtl_table_data(r, 3)); + sw_w32(rt->dst_ip, rtl_table_data(r, 4)); + + v |= rt->prefix_len == 32 ? BIT(21) : 0; // set host-route bit + ip4_m = inet_make_mask(rt->prefix_len); + sw_w32(0, rtl_table_data(r, 6)); + sw_w32(0, rtl_table_data(r, 7)); + sw_w32(0, rtl_table_data(r, 8)); + sw_w32(ip4_m, rtl_table_data(r, 9)); + break; + case 2: // IPv6 Unicast route + sw_w32(rt->dst_ip6.s6_addr32[0], rtl_table_data(r, 1)); + sw_w32(rt->dst_ip6.s6_addr32[1], rtl_table_data(r, 2)); + sw_w32(rt->dst_ip6.s6_addr32[2], rtl_table_data(r, 3)); + sw_w32(rt->dst_ip6.s6_addr32[3], rtl_table_data(r, 4)); + + v |= rt->prefix_len == 128 ? BIT(21) : 0; // set host-route bit + + rtl930x_net6_mask(rt->prefix_len, &ip6_m); + + sw_w32(ip6_m.s6_addr32[0], rtl_table_data(r, 6)); + sw_w32(ip6_m.s6_addr32[1], rtl_table_data(r, 7)); + sw_w32(ip6_m.s6_addr32[2], rtl_table_data(r, 8)); + sw_w32(ip6_m.s6_addr32[3], rtl_table_data(r, 9)); + break; + case 1: // IPv4 Multicast route + case 3: // IPv6 Multicast route + pr_warn("%s: route type not supported\n", __func__); + rtl_table_release(r); + return; + } + sw_w32(v, rtl_table_data(r, 10)); + + pr_debug("%s: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", __func__, + sw_r32(rtl_table_data(r, 0)), sw_r32(rtl_table_data(r, 1)), sw_r32(rtl_table_data(r, 2)), + sw_r32(rtl_table_data(r, 3)), sw_r32(rtl_table_data(r, 4)), sw_r32(rtl_table_data(r, 5)), + sw_r32(rtl_table_data(r, 6)), sw_r32(rtl_table_data(r, 7)), sw_r32(rtl_table_data(r, 8)), + sw_r32(rtl_table_data(r, 9)), sw_r32(rtl_table_data(r, 10))); + + rtl_table_write(r, idx); + rtl_table_release(r); +} + + +/* + * Get the destination MAC and L3 egress interface ID of a nexthop entry from + * the SoC's L3_NEXTHOP table + */ +static void rtl930x_get_l3_nexthop(int idx, u16 *dmac_id, u16 *interface) +{ + u32 v; + // Read L3_NEXTHOP table (3) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 3); + + rtl_table_read(r, idx); + // The table has a size of 1 register + v = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); + + *dmac_id = (v >> 7) & 0x7fff; + *interface = v & 0x7f; +} + +static int rtl930x_l3_mtu_del(struct rtl838x_switch_priv *priv, int mtu) +{ + int i; + + for (i = 0; i < MAX_INTF_MTUS; i++) { + if (mtu == priv->intf_mtus[i]) + break; + } + if (i >= MAX_INTF_MTUS || !priv->intf_mtu_count[i]) { + pr_err("%s: No MTU slot found for MTU: %d\n", __func__, mtu); + return -EINVAL; + } + + priv->intf_mtu_count[i]--; +} + +static int rtl930x_l3_mtu_add(struct rtl838x_switch_priv *priv, int mtu) +{ + int i, free_mtu; + int mtu_id; + + // Try to find an existing mtu-value or a free slot + free_mtu = MAX_INTF_MTUS; + for (i = 0; i < MAX_INTF_MTUS && priv->intf_mtus[i] != mtu; i++) { + if ((!priv->intf_mtu_count[i]) && (free_mtu == MAX_INTF_MTUS)) + free_mtu = i; + } + i = (i < MAX_INTF_MTUS) ? i : free_mtu; + if (i < MAX_INTF_MTUS) { + mtu_id = i; + } else { + pr_err("%s: No free MTU slot available!\n", __func__); + return -EINVAL; + } + + priv->intf_mtus[i] = mtu; + pr_info("Writing MTU %d to slot %d\n", priv->intf_mtus[i], i); + // Set MTU-value of the slot TODO: distinguish between IPv4/IPv6 routes / slots + sw_w32_mask(0xffff << ((i % 2) * 16), priv->intf_mtus[i] << ((i % 2) * 16), + RTL930X_L3_IP_MTU_CTRL(i)); + sw_w32_mask(0xffff << ((i % 2) * 16), priv->intf_mtus[i] << ((i % 2) * 16), + RTL930X_L3_IP6_MTU_CTRL(i)); + + priv->intf_mtu_count[i]++; + + return mtu_id; +} + +/* + * Creates an interface for a route by setting up the HW tables in the SoC + */ +static int rtl930x_l3_intf_add(struct rtl838x_switch_priv *priv, struct rtl838x_l3_intf *intf) +{ + int i, intf_id, mtu_id; + // number of MTU-values < 16384 + + // Use the same IPv6 mtu as the ip4 mtu for this route if unset + intf->ip6_mtu = intf->ip6_mtu ? intf->ip6_mtu : intf->ip4_mtu; + + mtu_id = rtl930x_l3_mtu_add(priv, intf->ip4_mtu); + pr_info("%s: added mtu %d with mtu-id %d\n", __func__, intf->ip4_mtu, mtu_id); + if (mtu_id < 0) + return -ENOSPC; + intf->ip4_mtu_id = mtu_id; + intf->ip6_mtu_id = mtu_id; + + for (i = 0; i < MAX_INTERFACES; i++) { + if (!priv->interfaces[i]) + break; + } + if (i >= MAX_INTERFACES) { + pr_err("%s: cannot find free interface entry\n", __func__); + return -EINVAL; + } + intf_id = i; + priv->interfaces[i] = kzalloc(sizeof(struct rtl838x_l3_intf), GFP_KERNEL); + if (!priv->interfaces[i]) { + pr_err("%s: no memory to allocate new interface\n", __func__); + return -ENOMEM; + } +} + +/* + * Set the destination MAC and L3 egress interface ID for a nexthop entry in the SoC's + * L3_NEXTHOP table. The nexthop entry is identified by idx. + * dmac_id is the reference to the L2 entry in the L2 forwarding table, special values are + * 0x7ffe: TRAP2CPU + * 0x7ffd: TRAP2MASTERCPU + * 0x7fff: DMAC_ID_DROP + */ +static void rtl930x_set_l3_nexthop(int idx, u16 dmac_id, u16 interface) +{ + // Access L3_NEXTHOP table (3) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 3); + + pr_info("%s: Writing to L3_NEXTHOP table, index %d, dmac_id %d, interface %d\n", + __func__, idx, dmac_id, interface); + sw_w32(((dmac_id & 0x7fff) << 7) | (interface & 0x7f), rtl_table_data(r, 0)); + + pr_info("%s: %08x\n", __func__, sw_r32(rtl_table_data(r,0))); + rtl_table_write(r, idx); + rtl_table_release(r); +} + +static void rtl930x_pie_lookup_enable(struct rtl838x_switch_priv *priv, int index) +{ + int block = index / PIE_BLOCK_SIZE; + + sw_w32_mask(0, BIT(block), RTL930X_PIE_BLK_LOOKUP_CTRL); +} + +/* + * Reads the intermediate representation of the templated match-fields of the + * PIE rule in the pie_rule structure and fills in the raw data fields in the + * raw register space r[]. + * The register space configuration size is identical for the RTL8380/90 and RTL9300, + * however the RTL9310 has 2 more registers / fields and the physical field-ids are different + * on all SoCs + * On the RTL9300 the mask fields are not word-aligend! + */ +static void rtl930x_write_pie_templated(u32 r[], struct pie_rule *pr, enum template_field_id t[]) +{ + int i; + enum template_field_id field_type; + u16 data, data_m; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + field_type = t[i]; + data = data_m = 0; + + switch (field_type) { + case TEMPLATE_FIELD_SPM0: + data = pr->spm; + data_m = pr->spm_m; + break; + case TEMPLATE_FIELD_SPM1: + data = pr->spm >> 16; + data_m = pr->spm_m >> 16; + break; + case TEMPLATE_FIELD_OTAG: + data = pr->otag; + data_m = pr->otag_m; + break; + case TEMPLATE_FIELD_SMAC0: + data = pr->smac[4]; + data = (data << 8) | pr->smac[5]; + data_m = pr->smac_m[4]; + data_m = (data_m << 8) | pr->smac_m[5]; + break; + case TEMPLATE_FIELD_SMAC1: + data = pr->smac[2]; + data = (data << 8) | pr->smac[3]; + data_m = pr->smac_m[2]; + data_m = (data_m << 8) | pr->smac_m[3]; + break; + case TEMPLATE_FIELD_SMAC2: + data = pr->smac[0]; + data = (data << 8) | pr->smac[1]; + data_m = pr->smac_m[0]; + data_m = (data_m << 8) | pr->smac_m[1]; + break; + case TEMPLATE_FIELD_DMAC0: + data = pr->dmac[4]; + data = (data << 8) | pr->dmac[5]; + data_m = pr->dmac_m[4]; + data_m = (data_m << 8) | pr->dmac_m[5]; + break; + case TEMPLATE_FIELD_DMAC1: + data = pr->dmac[2]; + data = (data << 8) | pr->dmac[3]; + data_m = pr->dmac_m[2]; + data_m = (data_m << 8) | pr->dmac_m[3]; + break; + case TEMPLATE_FIELD_DMAC2: + data = pr->dmac[0]; + data = (data << 8) | pr->dmac[1]; + data_m = pr->dmac_m[0]; + data_m = (data_m << 8) | pr->dmac_m[1]; + break; + case TEMPLATE_FIELD_ETHERTYPE: + data = pr->ethertype; + data_m = pr->ethertype_m; + break; + case TEMPLATE_FIELD_ITAG: + data = pr->itag; + data_m = pr->itag_m; + break; + case TEMPLATE_FIELD_SIP0: + if (pr->is_ipv6) { + data = pr->sip6.s6_addr16[7]; + data_m = pr->sip6_m.s6_addr16[7]; + } else { + data = pr->sip; + data_m = pr->sip_m; + } + break; + case TEMPLATE_FIELD_SIP1: + if (pr->is_ipv6) { + data = pr->sip6.s6_addr16[6]; + data_m = pr->sip6_m.s6_addr16[6]; + } else { + data = pr->sip >> 16; + data_m = pr->sip_m >> 16; + } + break; + + case TEMPLATE_FIELD_SIP2: + case TEMPLATE_FIELD_SIP3: + case TEMPLATE_FIELD_SIP4: + case TEMPLATE_FIELD_SIP5: + case TEMPLATE_FIELD_SIP6: + case TEMPLATE_FIELD_SIP7: + data = pr->sip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + data_m = pr->sip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + break; + + case TEMPLATE_FIELD_DIP0: + if (pr->is_ipv6) { + data = pr->dip6.s6_addr16[7]; + data_m = pr->dip6_m.s6_addr16[7]; + } else { + data = pr->dip; + data_m = pr->dip_m; + } + break; + + case TEMPLATE_FIELD_DIP1: + if (pr->is_ipv6) { + data = pr->dip6.s6_addr16[6]; + data_m = pr->dip6_m.s6_addr16[6]; + } else { + data = pr->dip >> 16; + data_m = pr->dip_m >> 16; + } + break; + + case TEMPLATE_FIELD_DIP2: + case TEMPLATE_FIELD_DIP3: + case TEMPLATE_FIELD_DIP4: + case TEMPLATE_FIELD_DIP5: + case TEMPLATE_FIELD_DIP6: + case TEMPLATE_FIELD_DIP7: + data = pr->dip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + data_m = pr->dip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + break; + + case TEMPLATE_FIELD_IP_TOS_PROTO: + data = pr->tos_proto; + data_m = pr->tos_proto_m; + break; + case TEMPLATE_FIELD_L4_SPORT: + data = pr->sport; + data_m = pr->sport_m; + break; + case TEMPLATE_FIELD_L4_DPORT: + data = pr->dport; + data_m = pr->dport_m; + break; + case TEMPLATE_FIELD_DSAP_SSAP: + data = pr->dsap_ssap; + data_m = pr->dsap_ssap_m; + break; + case TEMPLATE_FIELD_TCP_INFO: + data = pr->tcp_info; + data_m = pr->tcp_info_m; + break; + case TEMPLATE_FIELD_RANGE_CHK: + pr_warn("Warning: TEMPLATE_FIELD_RANGE_CHK: not configured\n"); + break; + default: + pr_info("%s: unknown field %d\n", __func__, field_type); + } + + // On the RTL9300, the mask fields are not word aligned! + if (!(i % 2)) { + r[5 - i / 2] = data; + r[12 - i / 2] |= ((u32)data_m << 8); + } else { + r[5 - i / 2] |= ((u32)data) << 16; + r[12 - i / 2] |= ((u32)data_m) << 24; + r[11 - i / 2] |= ((u32)data_m) >> 8; + } + } +} + +static void rtl930x_read_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + pr->stacking_port = r[6] & BIT(31); + pr->spn = (r[6] >> 24) & 0x7f; + pr->mgnt_vlan = r[6] & BIT(23); + if (pr->phase == PHASE_IACL) + pr->dmac_hit_sw = r[6] & BIT(22); + else + pr->content_too_deep = r[6] & BIT(22); + pr->not_first_frag = r[6] & BIT(21); + pr->frame_type_l4 = (r[6] >> 18) & 7; + pr->frame_type = (r[6] >> 16) & 3; + pr->otag_fmt = (r[6] >> 15) & 1; + pr->itag_fmt = (r[6] >> 14) & 1; + pr->otag_exist = (r[6] >> 13) & 1; + pr->itag_exist = (r[6] >> 12) & 1; + pr->frame_type_l2 = (r[6] >> 10) & 3; + pr->igr_normal_port = (r[6] >> 9) & 1; + pr->tid = (r[6] >> 8) & 1; + + pr->stacking_port_m = r[12] & BIT(7); + pr->spn_m = r[12] & 0x7f; + pr->mgnt_vlan_m = r[13] & BIT(31); + if (pr->phase == PHASE_IACL) + pr->dmac_hit_sw_m = r[13] & BIT(30); + else + pr->content_too_deep_m = r[13] & BIT(30); + pr->not_first_frag_m = r[13] & BIT(29); + pr->frame_type_l4_m = (r[13] >> 26) & 7; + pr->frame_type_m = (r[13] >> 24) & 3; + pr->otag_fmt_m = r[13] & BIT(23); + pr->itag_fmt_m = r[13] & BIT(22); + pr->otag_exist_m = r[13] & BIT(21); + pr->itag_exist_m = r[13] & BIT (20); + pr->frame_type_l2_m = (r[13] >> 18) & 3; + pr->igr_normal_port_m = r[13] & BIT(17); + pr->tid_m = (r[13] >> 16) & 1; + + pr->valid = r[13] & BIT(15); + pr->cond_not = r[13] & BIT(14); + pr->cond_and1 = r[13] & BIT(13); + pr->cond_and2 = r[13] & BIT(12); +} + +static void rtl930x_write_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + r[6] = pr->stacking_port ? BIT(31) : 0; + r[6] |= ((u32) (pr->spn & 0x7f)) << 24; + r[6] |= pr->mgnt_vlan ? BIT(23) : 0; + if (pr->phase == PHASE_IACL) + r[6] |= pr->dmac_hit_sw ? BIT(22) : 0; + else + r[6] |= pr->content_too_deep ? BIT(22) : 0; + r[6] |= pr->not_first_frag ? BIT(21) : 0; + r[6] |= ((u32) (pr->frame_type_l4 & 0x7)) << 18; + r[6] |= ((u32) (pr->frame_type & 0x3)) << 16; + r[6] |= pr->otag_fmt ? BIT(15) : 0; + r[6] |= pr->itag_fmt ? BIT(14) : 0; + r[6] |= pr->otag_exist ? BIT(13) : 0; + r[6] |= pr->itag_exist ? BIT(12) : 0; + r[6] |= ((u32) (pr->frame_type_l2 & 0x3)) << 10; + r[6] |= pr->igr_normal_port ? BIT(9) : 0; + r[6] |= ((u32) (pr->tid & 0x1)) << 8; + + r[12] |= pr->stacking_port_m ? BIT(7) : 0; + r[12] |= (u32) (pr->spn_m & 0x7f); + r[13] |= pr->mgnt_vlan_m ? BIT(31) : 0; + if (pr->phase == PHASE_IACL) + r[13] |= pr->dmac_hit_sw_m ? BIT(30) : 0; + else + r[13] |= pr->content_too_deep_m ? BIT(30) : 0; + r[13] |= pr->not_first_frag_m ? BIT(29) : 0; + r[13] |= ((u32) (pr->frame_type_l4_m & 0x7)) << 26; + r[13] |= ((u32) (pr->frame_type_m & 0x3)) << 24; + r[13] |= pr->otag_fmt_m ? BIT(23) : 0; + r[13] |= pr->itag_fmt_m ? BIT(22) : 0; + r[13] |= pr->otag_exist_m ? BIT(21) : 0; + r[13] |= pr->itag_exist_m ? BIT(20) : 0; + r[13] |= ((u32) (pr->frame_type_l2_m & 0x3)) << 18; + r[13] |= pr->igr_normal_port_m ? BIT(17) : 0; + r[13] |= ((u32) (pr->tid_m & 0x1)) << 16; + + r[13] |= pr->valid ? BIT(15) : 0; + r[13] |= pr->cond_not ? BIT(14) : 0; + r[13] |= pr->cond_and1 ? BIT(13) : 0; + r[13] |= pr->cond_and2 ? BIT(12) : 0; +} + +static void rtl930x_write_pie_action(u32 r[], struct pie_rule *pr) +{ + // Either drop or forward + if (pr->drop) { + r[14] |= BIT(24) | BIT(25) | BIT(26); // Do Green, Yellow and Red drops + // Actually DROP, not PERMIT in Green / Yellow / Red + r[14] |= BIT(23) | BIT(22) | BIT(20); + } else { + r[14] |= pr->fwd_sel ? BIT(27) : 0; + r[14] |= pr->fwd_act << 18; + r[14] |= BIT(14); // We overwrite any drop + } + if (pr->phase == PHASE_VACL) + r[14] |= pr->fwd_sa_lrn ? BIT(15) : 0; + r[13] |= pr->bypass_sel ? BIT(5) : 0; + r[13] |= pr->nopri_sel ? BIT(4) : 0; + r[13] |= pr->tagst_sel ? BIT(3) : 0; + r[13] |= pr->ovid_sel ? BIT(1) : 0; + r[14] |= pr->ivid_sel ? BIT(31) : 0; + r[14] |= pr->meter_sel ? BIT(30) : 0; + r[14] |= pr->mir_sel ? BIT(29) : 0; + r[14] |= pr->log_sel ? BIT(28) : 0; + + r[14] |= ((u32)(pr->fwd_data & 0x3fff)) << 3; + r[15] |= pr->log_octets ? BIT(31) : 0; + r[15] |= (u32)(pr->meter_data) << 23; + + r[15] |= ((u32)(pr->ivid_act) << 21) & 0x3; + r[15] |= ((u32)(pr->ivid_data) << 9) & 0xfff; + r[16] |= ((u32)(pr->ovid_act) << 30) & 0x3; + r[16] |= ((u32)(pr->ovid_data) & 0xfff) << 16; + r[16] |= (pr->mir_data & 0x3) << 6; + r[17] |= ((u32)(pr->tagst_data) & 0xf) << 28; + r[17] |= ((u32)(pr->nopri_data) & 0x7) << 25; + r[17] |= pr->bypass_ibc_sc ? BIT(16) : 0; +} + +void rtl930x_pie_rule_dump_raw(u32 r[]) +{ + pr_info("Raw IACL table entry:\n"); + pr_info("r 0 - 7: %08x %08x %08x %08x %08x %08x %08x %08x\n", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); + pr_info("r 8 - 15: %08x %08x %08x %08x %08x %08x %08x %08x\n", + r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]); + pr_info("r 16 - 18: %08x %08x %08x\n", r[16], r[17], r[18]); + pr_info("Match : %08x %08x %08x %08x %08x %08x\n", r[0], r[1], r[2], r[3], r[4], r[5]); + pr_info("Fixed : %06x\n", r[6] >> 8); + pr_info("Match M: %08x %08x %08x %08x %08x %08x\n", + (r[6] << 24) | (r[7] >> 8), (r[7] << 24) | (r[8] >> 8), (r[8] << 24) | (r[9] >> 8), + (r[9] << 24) | (r[10] >> 8), (r[10] << 24) | (r[11] >> 8), + (r[11] << 24) | (r[12] >> 8)); + pr_info("R[13]: %08x\n", r[13]); + pr_info("Fixed M: %06x\n", ((r[12] << 16) | (r[13] >> 16)) & 0xffffff); + pr_info("Valid / not / and1 / and2 : %1x\n", (r[13] >> 12) & 0xf); + pr_info("r 13-16: %08x %08x %08x %08x\n", r[13], r[14], r[15], r[16]); +} + +static int rtl930x_pie_rule_write(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr) +{ + // Access IACL table (2) via register 0 + struct table_reg *q = rtl_table_get(RTL9300_TBL_0, 2); + u32 r[19]; + int i; + int block = idx / PIE_BLOCK_SIZE; + u32 t_select = sw_r32(RTL930X_PIE_BLK_TMPLTE_CTRL(block)); + + pr_debug("%s: %d, t_select: %08x\n", __func__, idx, t_select); + + for (i = 0; i < 19; i++) + r[i] = 0; + + if (!pr->valid) { + rtl_table_write(q, idx); + rtl_table_release(q); + return 0; + } + rtl930x_write_pie_fixed_fields(r, pr); + + pr_debug("%s: template %d\n", __func__, (t_select >> (pr->tid * 4)) & 0xf); + rtl930x_write_pie_templated(r, pr, fixed_templates[(t_select >> (pr->tid * 4)) & 0xf]); + + rtl930x_write_pie_action(r, pr); + +// rtl930x_pie_rule_dump_raw(r); + + for (i = 0; i < 19; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); + + return 0; +} + +static bool rtl930x_pie_templ_has(int t, enum template_field_id field_type) +{ + int i; + enum template_field_id ft; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + ft = fixed_templates[t][i]; + if (field_type == ft) + return true; + } + + return false; +} + +/* + * Verify that the rule pr is compatible with a given template t in block block + * Note that this function is SoC specific since the values of e.g. TEMPLATE_FIELD_SIP0 + * depend on the SoC + */ +static int rtl930x_pie_verify_template(struct rtl838x_switch_priv *priv, + struct pie_rule *pr, int t, int block) +{ + int i; + + if (!pr->is_ipv6 && pr->sip_m && !rtl930x_pie_templ_has(t, TEMPLATE_FIELD_SIP0)) + return -1; + + if (!pr->is_ipv6 && pr->dip_m && !rtl930x_pie_templ_has(t, TEMPLATE_FIELD_DIP0)) + return -1; + + if (pr->is_ipv6) { + if ((pr->sip6_m.s6_addr32[0] || pr->sip6_m.s6_addr32[1] + || pr->sip6_m.s6_addr32[2] || pr->sip6_m.s6_addr32[3]) + && !rtl930x_pie_templ_has(t, TEMPLATE_FIELD_SIP2)) + return -1; + if ((pr->dip6_m.s6_addr32[0] || pr->dip6_m.s6_addr32[1] + || pr->dip6_m.s6_addr32[2] || pr->dip6_m.s6_addr32[3]) + && !rtl930x_pie_templ_has(t, TEMPLATE_FIELD_DIP2)) + return -1; + } + + if (ether_addr_to_u64(pr->smac) && !rtl930x_pie_templ_has(t, TEMPLATE_FIELD_SMAC0)) + return -1; + + if (ether_addr_to_u64(pr->dmac) && !rtl930x_pie_templ_has(t, TEMPLATE_FIELD_DMAC0)) + return -1; + + // TODO: Check more + + i = find_first_zero_bit(&priv->pie_use_bm[block * 4], PIE_BLOCK_SIZE); + + if (i >= PIE_BLOCK_SIZE) + return -1; + + return i + PIE_BLOCK_SIZE * block; +} + +static int rtl930x_pie_rule_add(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx, block, j, t; + int min_block = 0; + int max_block = priv->n_pie_blocks / 2; + + if (pr->is_egress) { + min_block = max_block; + max_block = priv->n_pie_blocks; + } + pr_debug("In %s\n", __func__); + + mutex_lock(&priv->pie_mutex); + + for (block = min_block; block < max_block; block++) { + for (j = 0; j < 2; j++) { + t = (sw_r32(RTL930X_PIE_BLK_TMPLTE_CTRL(block)) >> (j * 4)) & 0xf; + pr_debug("Testing block %d, template %d, template id %d\n", block, j, t); + pr_debug("%s: %08x\n", + __func__, sw_r32(RTL930X_PIE_BLK_TMPLTE_CTRL(block))); + idx = rtl930x_pie_verify_template(priv, pr, t, block); + if (idx >= 0) + break; + } + if (j < 2) + break; + } + + if (block >= priv->n_pie_blocks) { + mutex_unlock(&priv->pie_mutex); + return -EOPNOTSUPP; + } + + pr_debug("Using block: %d, index %d, template-id %d\n", block, idx, j); + set_bit(idx, priv->pie_use_bm); + + pr->valid = true; + pr->tid = j; // Mapped to template number + pr->tid_m = 0x1; + pr->id = idx; + + rtl930x_pie_lookup_enable(priv, idx); + rtl930x_pie_rule_write(priv, idx, pr); + + mutex_unlock(&priv->pie_mutex); + return 0; +} + +/* + * Delete a range of Packet Inspection Engine rules + */ +static int rtl930x_pie_rule_del(struct rtl838x_switch_priv *priv, int index_from, int index_to) +{ + u32 v = (index_from << 1)| (index_to << 12 ) | BIT(0); + + pr_debug("%s: from %d to %d\n", __func__, index_from, index_to); + mutex_lock(&priv->reg_mutex); + + // Write from-to and execute bit into control register + sw_w32(v, RTL930X_PIE_CLR_CTRL); + + // Wait until command has completed + do { + } while (sw_r32(RTL930X_PIE_CLR_CTRL) & BIT(0)); + + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static void rtl930x_pie_rule_rm(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx = pr->id; + + rtl930x_pie_rule_del(priv, idx, idx); + clear_bit(idx, priv->pie_use_bm); +} + +static void rtl930x_pie_init(struct rtl838x_switch_priv *priv) +{ + int i; + u32 template_selectors; + + mutex_init(&priv->pie_mutex); + + pr_info("%s\n", __func__); + // Enable ACL lookup on all ports, including CPU_PORT + for (i = 0; i <= priv->cpu_port; i++) + sw_w32(1, RTL930X_ACL_PORT_LOOKUP_CTRL(i)); + + // Include IPG in metering + sw_w32_mask(0, 1, RTL930X_METER_GLB_CTRL); + + // Delete all present rules, block size is 128 on all SoC families + rtl930x_pie_rule_del(priv, 0, priv->n_pie_blocks * 128 - 1); + + // Assign blocks 0-7 to VACL phase (bit = 0), blocks 8-15 to IACL (bit = 1) + sw_w32(0xff00, RTL930X_PIE_BLK_PHASE_CTRL); + + // Enable predefined templates 0, 1 for first quarter of all blocks + template_selectors = 0 | (1 << 4); + for (i = 0; i < priv->n_pie_blocks / 4; i++) + sw_w32(template_selectors, RTL930X_PIE_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 2, 3 for second quarter of all blocks + template_selectors = 2 | (3 << 4); + for (i = priv->n_pie_blocks / 4; i < priv->n_pie_blocks / 2; i++) + sw_w32(template_selectors, RTL930X_PIE_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 0, 1 for third half of all blocks + template_selectors = 0 | (1 << 4); + for (i = priv->n_pie_blocks / 2; i < priv->n_pie_blocks * 3 / 4; i++) + sw_w32(template_selectors, RTL930X_PIE_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 2, 3 for fourth quater of all blocks + template_selectors = 2 | (3 << 4); + for (i = priv->n_pie_blocks * 3 / 4; i < priv->n_pie_blocks; i++) + sw_w32(template_selectors, RTL930X_PIE_BLK_TMPLTE_CTRL(i)); + +} + +/* + * Sets up an egress interface for L3 actions + * Actions for ip4/6_icmp_redirect, ip4/6_pbr_icmp_redirect are: + * 0: FORWARD, 1: DROP, 2: TRAP2CPU, 3: COPY2CPU, 4: TRAP2MASTERCPU 5: COPY2MASTERCPU + * 6: HARDDROP + * idx is the index in the HW interface table: idx < 0x80 + */ +static void rtl930x_set_l3_egress_intf(int idx, struct rtl838x_l3_intf *intf) +{ + u32 u, v; + // Read L3_EGR_INTF table (4) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 4); + + // The table has 2 registers + u = (intf->vid & 0xfff) << 9; + u |= (intf->smac_idx & 0x3f) << 3; + u |= (intf->ip4_mtu_id & 0x7); + + v = (intf->ip6_mtu_id & 0x7) << 28; + v |= (intf->ttl_scope & 0xff) << 20; + v |= (intf->hl_scope & 0xff) << 12; + v |= (intf->ip4_icmp_redirect & 0x7) << 9; + v |= (intf->ip6_icmp_redirect & 0x7)<< 6; + v |= (intf->ip4_pbr_icmp_redirect & 0x7) << 3; + v |= (intf->ip6_pbr_icmp_redirect & 0x7); + + sw_w32(u, rtl_table_data(r, 0)); + sw_w32(v, rtl_table_data(r, 1)); + + pr_info("%s writing to index %d: %08x %08x\n", __func__, idx, u, v); + rtl_table_write(r, idx & 0x7f); + rtl_table_release(r); +} + +/* + * Reads a MAC entry for L3 termination as entry point for routing + * from the hardware table + * idx is the index into the L3_ROUTER_MAC table + */ +static void rtl930x_get_l3_router_mac(u32 idx, struct rtl93xx_rt_mac *m) +{ + u32 v, w; + // Read L3_ROUTER_MAC table (0) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 0); + + rtl_table_read(r, idx); + // The table has a size of 7 registers, 64 entries + v = sw_r32(rtl_table_data(r, 0)); + w = sw_r32(rtl_table_data(r, 3)); + m->valid = !!(v & BIT(20)); + if (!m->valid) + goto out; + + m->p_type = !!(v & BIT(19)); + m->p_id = (v >> 13) & 0x3f; // trunk id of port + m->vid = v & 0xfff; + m->vid_mask = w & 0xfff; + m->action = sw_r32(rtl_table_data(r, 6)) & 0x7; + m->mac_mask = ((((u64)sw_r32(rtl_table_data(r, 5))) << 32) & 0xffffffffffffULL) + | (sw_r32(rtl_table_data(r, 4))); + m->mac = ((((u64)sw_r32(rtl_table_data(r, 1))) << 32) & 0xffffffffffffULL) + | (sw_r32(rtl_table_data(r, 2))); + // Bits L3_INTF and BMSK_L3_INTF are 0 + +out: + rtl_table_release(r); +} + +/* + * Writes a MAC entry for L3 termination as entry point for routing + * into the hardware table + * idx is the index into the L3_ROUTER_MAC table + */ +static void rtl930x_set_l3_router_mac(u32 idx, struct rtl93xx_rt_mac *m) +{ + u32 v, w; + // Read L3_ROUTER_MAC table (0) via register RTL9300_TBL_1 + struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 0); + + // The table has a size of 7 registers, 64 entries + v = BIT(20); // mac entry valid, port type is 0: individual + v |= (m->p_id & 0x3f) << 13; + v |= (m->vid & 0xfff); // Set the interface_id to the vlan id + + w = m->vid_mask; + w |= (m->p_id_mask & 0x3f) << 13; + + sw_w32(v, rtl_table_data(r, 0)); + sw_w32(w, rtl_table_data(r, 3)); + + // Set MAC address, L3_INTF (bit 12 in register 1) needs to be 0 + sw_w32((u32)(m->mac), rtl_table_data(r, 2)); + sw_w32(m->mac >> 32, rtl_table_data(r, 1)); + + // Set MAC address mask, BMSK_L3_INTF (bit 12 in register 5) needs to be 0 + sw_w32((u32)(m->mac_mask >> 32), rtl_table_data(r, 4)); + sw_w32((u32)m->mac_mask, rtl_table_data(r, 5)); + + sw_w32(m->action & 0x7, rtl_table_data(r, 6)); + + pr_debug("%s writing index %d: %08x %08x %08x %08x %08x %08x %08x\n", __func__, idx, + sw_r32(rtl_table_data(r, 0)), sw_r32(rtl_table_data(r, 1)), sw_r32(rtl_table_data(r, 2)), + sw_r32(rtl_table_data(r, 3)), sw_r32(rtl_table_data(r, 4)), sw_r32(rtl_table_data(r, 5)), + sw_r32(rtl_table_data(r, 6)) + ); + rtl_table_write(r, idx); + rtl_table_release(r); +} + +/* + * Get the Destination-MAC of an L3 egress interface or the Source MAC for routed packets + * from the SoC's L3_EGR_INTF_MAC table + * Indexes 0-2047 are DMACs, 2048+ are SMACs + */ +static u64 rtl930x_get_l3_egress_mac(u32 idx) +{ + u64 mac; + // Read L3_EGR_INTF_MAC table (2) via register RTL9300_TBL_2 + struct table_reg *r = rtl_table_get(RTL9300_TBL_2, 2); + + rtl_table_read(r, idx); + // The table has a size of 2 registers + mac = sw_r32(rtl_table_data(r, 0)); + mac <<= 32; + mac |= sw_r32(rtl_table_data(r, 1)); + rtl_table_release(r); + + return mac; +} +/* + * Set the Destination-MAC of a route or the Source MAC of an L3 egress interface + * in the SoC's L3_EGR_INTF_MAC table + * Indexes 0-2047 are DMACs, 2048+ are SMACs + */ +static void rtl930x_set_l3_egress_mac(u32 idx, u64 mac) +{ + // Access L3_EGR_INTF_MAC table (2) via register RTL9300_TBL_2 + struct table_reg *r = rtl_table_get(RTL9300_TBL_2, 2); + + // The table has a size of 2 registers + sw_w32(mac >> 32, rtl_table_data(r, 0)); + sw_w32(mac, rtl_table_data(r, 1)); + + pr_debug("%s: setting index %d to %016llx\n", __func__, idx, mac); + rtl_table_write(r, idx); + rtl_table_release(r); +} + +/* + * Configure L3 routing settings of the device: + * - MTUs + * - Egress interface + * - The router's MAC address on which routed packets are expected + * - MAC addresses used as source macs of routed packets + */ +int rtl930x_l3_setup(struct rtl838x_switch_priv *priv) +{ + int i; + + // Setup MTU with id 0 for default interface + for (i = 0; i < MAX_INTF_MTUS; i++) + priv->intf_mtu_count[i] = priv->intf_mtus[i] = 0; + + priv->intf_mtu_count[0] = 0; // Needs to stay forever + priv->intf_mtus[0] = DEFAULT_MTU; + sw_w32_mask(0xffff, DEFAULT_MTU, RTL930X_L3_IP_MTU_CTRL(0)); + sw_w32_mask(0xffff, DEFAULT_MTU, RTL930X_L3_IP6_MTU_CTRL(0)); + priv->intf_mtus[1] = DEFAULT_MTU; + sw_w32_mask(0xffff0000, DEFAULT_MTU << 16, RTL930X_L3_IP_MTU_CTRL(0)); + sw_w32_mask(0xffff0000, DEFAULT_MTU << 16, RTL930X_L3_IP6_MTU_CTRL(0)); + + sw_w32_mask(0xffff, DEFAULT_MTU, RTL930X_L3_IP_MTU_CTRL(1)); + sw_w32_mask(0xffff, DEFAULT_MTU, RTL930X_L3_IP6_MTU_CTRL(1)); + sw_w32_mask(0xffff0000, DEFAULT_MTU << 16, RTL930X_L3_IP_MTU_CTRL(1)); + sw_w32_mask(0xffff0000, DEFAULT_MTU << 16, RTL930X_L3_IP6_MTU_CTRL(1)); + + // Clear all source port MACs + for (i = 0; i < MAX_SMACS; i++) + rtl930x_set_l3_egress_mac(L3_EGRESS_DMACS + i, 0ULL); + + // Configure the default L3 hash algorithm + sw_w32_mask(BIT(2), 0, RTL930X_L3_HOST_TBL_CTRL); // Algorithm selection 0 = 0 + sw_w32_mask(0, BIT(3), RTL930X_L3_HOST_TBL_CTRL); // Algorithm selection 1 = 1 + + pr_info("L3_IPUC_ROUTE_CTRL %08x, IPMC_ROUTE %08x, IP6UC_ROUTE %08x, IP6MC_ROUTE %08x\n", + sw_r32(RTL930X_L3_IPUC_ROUTE_CTRL), sw_r32(RTL930X_L3_IPMC_ROUTE_CTRL), + sw_r32(RTL930X_L3_IP6UC_ROUTE_CTRL), sw_r32(RTL930X_L3_IP6MC_ROUTE_CTRL)); + sw_w32_mask(0, 1, RTL930X_L3_IPUC_ROUTE_CTRL); + sw_w32_mask(0, 1, RTL930X_L3_IP6UC_ROUTE_CTRL); + sw_w32_mask(0, 1, RTL930X_L3_IPMC_ROUTE_CTRL); + sw_w32_mask(0, 1, RTL930X_L3_IP6MC_ROUTE_CTRL); + + sw_w32(0x00002001, RTL930X_L3_IPUC_ROUTE_CTRL); + sw_w32(0x00014581, RTL930X_L3_IP6UC_ROUTE_CTRL); + sw_w32(0x00000501, RTL930X_L3_IPMC_ROUTE_CTRL); + sw_w32(0x00012881, RTL930X_L3_IP6MC_ROUTE_CTRL); + + pr_info("L3_IPUC_ROUTE_CTRL %08x, IPMC_ROUTE %08x, IP6UC_ROUTE %08x, IP6MC_ROUTE %08x\n", + sw_r32(RTL930X_L3_IPUC_ROUTE_CTRL), sw_r32(RTL930X_L3_IPMC_ROUTE_CTRL), + sw_r32(RTL930X_L3_IP6UC_ROUTE_CTRL), sw_r32(RTL930X_L3_IP6MC_ROUTE_CTRL)); + + // Trap non-ip traffic to the CPU-port (e.g. ARP so we stay reachable) + sw_w32_mask(0x3 << 8, 0x1 << 8, RTL930X_L3_IP_ROUTE_CTRL); + pr_info("L3_IP_ROUTE_CTRL %08x\n", sw_r32(RTL930X_L3_IP_ROUTE_CTRL)); + + // PORT_ISO_RESTRICT_ROUTE_CTRL ? + + // Do not use prefix route 0 because of HW limitations + set_bit(0, priv->route_use_bm); + + return 0; +} + +static u32 rtl930x_packet_cntr_read(int counter) +{ + u32 v; + + // Read LOG table (3) via register RTL9300_TBL_0 + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 3); + + pr_debug("In %s, id %d\n", __func__, counter); + rtl_table_read(r, counter / 2); + + pr_debug("Registers: %08x %08x\n", + sw_r32(rtl_table_data(r, 0)), sw_r32(rtl_table_data(r, 1))); + // The table has a size of 2 registers + if (counter % 2) + v = sw_r32(rtl_table_data(r, 0)); + else + v = sw_r32(rtl_table_data(r, 1)); + + rtl_table_release(r); + + return v; +} + +static void rtl930x_packet_cntr_clear(int counter) +{ + // Access LOG table (3) via register RTL9300_TBL_0 + struct table_reg *r = rtl_table_get(RTL9300_TBL_0, 3); + + pr_info("In %s, id %d\n", __func__, counter); + // The table has a size of 2 registers + if (counter % 2) + sw_w32(0, rtl_table_data(r, 0)); + else + sw_w32(0, rtl_table_data(r, 1)); + + rtl_table_write(r, counter / 2); + + rtl_table_release(r); +} + +void rtl930x_vlan_port_keep_tag_set(int port, bool keep_outer, bool keep_inner) +{ + sw_w32(FIELD_PREP(RTL930X_VLAN_PORT_TAG_STS_CTRL_EGR_OTAG_STS_MASK, + keep_outer ? RTL930X_VLAN_PORT_TAG_STS_TAGGED : RTL930X_VLAN_PORT_TAG_STS_UNTAG) | + FIELD_PREP(RTL930X_VLAN_PORT_TAG_STS_CTRL_EGR_ITAG_STS_MASK, + keep_inner ? RTL930X_VLAN_PORT_TAG_STS_TAGGED : RTL930X_VLAN_PORT_TAG_STS_UNTAG), + RTL930X_VLAN_PORT_TAG_STS_CTRL(port)); +} + +void rtl930x_vlan_port_pvidmode_set(int port, enum pbvlan_type type, enum pbvlan_mode mode) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0x3, mode, RTL930X_VLAN_PORT_PB_VLAN + (port << 2)); + else + sw_w32_mask(0x3 << 14, mode << 14 ,RTL930X_VLAN_PORT_PB_VLAN + (port << 2)); +} + +void rtl930x_vlan_port_pvid_set(int port, enum pbvlan_type type, int pvid) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0xfff << 2, pvid << 2, RTL930X_VLAN_PORT_PB_VLAN + (port << 2)); + else + sw_w32_mask(0xfff << 16, pvid << 16, RTL930X_VLAN_PORT_PB_VLAN + (port << 2)); +} + +static int rtl930x_set_ageing_time(unsigned long msec) +{ + int t = sw_r32(RTL930X_L2_AGE_CTRL); + + t &= 0x1FFFFF; + t = (t * 7) / 10; + pr_debug("L2 AGING time: %d sec\n", t); + + t = (msec / 100 + 6) / 7; + t = t > 0x1FFFFF ? 0x1FFFFF : t; + sw_w32_mask(0x1FFFFF, t, RTL930X_L2_AGE_CTRL); + pr_debug("Dynamic aging for ports: %x\n", sw_r32(RTL930X_L2_PORT_AGE_CTRL)); + + return 0; +} + +static void rtl930x_set_igr_filter(int port, enum igr_filter state) +{ + sw_w32_mask(0x3 << ((port & 0xf)<<1), state << ((port & 0xf)<<1), + RTL930X_VLAN_PORT_IGR_FLTR + (((port >> 4) << 2))); +} + +static void rtl930x_set_egr_filter(int port, enum egr_filter state) +{ + sw_w32_mask(0x1 << (port % 0x1D), state << (port % 0x1D), + RTL930X_VLAN_PORT_EGR_FLTR + (((port / 29) << 2))); +} + +void rtl930x_set_distribution_algorithm(int group, int algoidx, u32 algomsk) +{ + u32 l3shift = 0; + u32 newmask = 0; + + /* TODO: for now we set algoidx to 0 */ + algoidx = 0; + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SIP_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SIP_BIT; + } + if (algomsk & TRUNK_DISTRIBUTION_ALGO_DIP_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_DIP_BIT; + } + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SRC_L4PORT_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SRC_L4PORT_BIT; + } + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SRC_L4PORT_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SRC_L4PORT_BIT; + } + + if (l3shift == 4) { + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SMAC_BIT; + + if (algomsk & TRUNK_DISTRIBUTION_ALGO_DMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_DMAC_BIT; + } else { + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L2_SMAC_BIT; + if (algomsk & TRUNK_DISTRIBUTION_ALGO_DMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L2_DMAC_BIT; + } + + sw_w32(newmask << l3shift, RTL930X_TRK_HASH_CTRL + (algoidx << 2)); +} + +static void rtl930x_led_init(struct rtl838x_switch_priv *priv) +{ + int i, pos; + u32 v, pm = 0, set; + u32 setlen; + const __be32 *led_set; + char set_name[9]; + struct device_node *node; + + pr_info("%s called\n", __func__); + node = of_find_compatible_node(NULL, NULL, "realtek,rtl9300-leds"); + if (!node) { + pr_info("%s No compatible LED node found\n", __func__); + return; + } + + for (i= 0; i < priv->cpu_port; i++) { + pos = (i << 1) % 32; + sw_w32_mask(0x3 << pos, 0, RTL930X_LED_PORT_FIB_SET_SEL_CTRL(i)); + sw_w32_mask(0x3 << pos, 0, RTL930X_LED_PORT_COPR_SET_SEL_CTRL(i)); + + if (!priv->ports[i].phy) + continue; + + v = 0x1; + if (priv->ports[i].is10G) + v = 0x3; + if (priv->ports[i].phy_is_integrated) + v = 0x1; + sw_w32_mask(0x3 << pos, v << pos, RTL930X_LED_PORT_NUM_CTRL(i)); + + pm |= BIT(i); + + set = priv->ports[i].led_set; + sw_w32_mask(0, set << pos, RTL930X_LED_PORT_COPR_SET_SEL_CTRL(i)); + sw_w32_mask(0, set << pos, RTL930X_LED_PORT_FIB_SET_SEL_CTRL(i)); + } + + for (i = 0; i < 4; i++) { + sprintf(set_name, "led_set%d", i); + led_set = of_get_property(node, set_name, &setlen); + if (!led_set || setlen != 16) + break; + v = be32_to_cpup(led_set) << 16 | be32_to_cpup(led_set + 1); + sw_w32(v, RTL930X_LED_SET0_0_CTRL - 4 - i * 8); + v = be32_to_cpup(led_set + 2) << 16 | be32_to_cpup(led_set + 3); + sw_w32(v, RTL930X_LED_SET0_0_CTRL - i * 8); + } + + // Set LED mode to serial (0x1) + sw_w32_mask(0x3, 0x1, RTL930X_LED_GLB_CTRL); + + // Set port type masks + sw_w32(pm, RTL930X_LED_PORT_COPR_MASK_CTRL); + sw_w32(pm, RTL930X_LED_PORT_FIB_MASK_CTRL); + sw_w32(pm, RTL930X_LED_PORT_COMBO_MASK_CTRL); + + for (i = 0; i < 24; i++) + pr_info("%s %08x: %08x\n",__func__, 0xbb00cc00 + i * 4, sw_r32(0xcc00 + i * 4)); +} + +const struct rtl838x_reg rtl930x_reg = { + .mask_port_reg_be = rtl838x_mask_port_reg, + .set_port_reg_be = rtl838x_set_port_reg, + .get_port_reg_be = rtl838x_get_port_reg, + .mask_port_reg_le = rtl838x_mask_port_reg, + .set_port_reg_le = rtl838x_set_port_reg, + .get_port_reg_le = rtl838x_get_port_reg, + .stat_port_rst = RTL930X_STAT_PORT_RST, + .stat_rst = RTL930X_STAT_RST, + .stat_port_std_mib = RTL930X_STAT_PORT_MIB_CNTR, + .traffic_enable = rtl930x_traffic_enable, + .traffic_disable = rtl930x_traffic_disable, + .traffic_get = rtl930x_traffic_get, + .traffic_set = rtl930x_traffic_set, + .l2_ctrl_0 = RTL930X_L2_CTRL, + .l2_ctrl_1 = RTL930X_L2_AGE_CTRL, + .l2_port_aging_out = RTL930X_L2_PORT_AGE_CTRL, + .set_ageing_time = rtl930x_set_ageing_time, + .smi_poll_ctrl = RTL930X_SMI_POLL_CTRL, // TODO: Difference to RTL9300_SMI_PRVTE_POLLING_CTRL + .l2_tbl_flush_ctrl = RTL930X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl930x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl930x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl930x_tbl_access_data_0, + .isr_glb_src = RTL930X_ISR_GLB, + .isr_port_link_sts_chg = RTL930X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL930X_IMR_PORT_LINK_STS_CHG, + .imr_glb = RTL930X_IMR_GLB, + .vlan_tables_read = rtl930x_vlan_tables_read, + .vlan_set_tagged = rtl930x_vlan_set_tagged, + .vlan_set_untagged = rtl930x_vlan_set_untagged, + .vlan_profile_dump = rtl930x_vlan_profile_dump, + .vlan_profile_setup = rtl930x_vlan_profile_setup, + .vlan_fwd_on_inner = rtl930x_vlan_fwd_on_inner, + .set_vlan_igr_filter = rtl930x_set_igr_filter, + .set_vlan_egr_filter = rtl930x_set_egr_filter, + .stp_get = rtl930x_stp_get, + .stp_set = rtl930x_stp_set, + .mac_force_mode_ctrl = rtl930x_mac_force_mode_ctrl, + .mac_port_ctrl = rtl930x_mac_port_ctrl, + .l2_port_new_salrn = rtl930x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl930x_l2_port_new_sa_fwd, + .mir_ctrl = RTL930X_MIR_CTRL, + .mir_dpm = RTL930X_MIR_DPM_CTRL, + .mir_spm = RTL930X_MIR_SPM_CTRL, + .mac_link_sts = RTL930X_MAC_LINK_STS, + .mac_link_dup_sts = RTL930X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl930x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL930X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL930X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl930x_read_l2_entry_using_hash, + .write_l2_entry_using_hash = rtl930x_write_l2_entry_using_hash, + .read_cam = rtl930x_read_cam, + .write_cam = rtl930x_write_cam, + .vlan_port_keep_tag_set = rtl930x_vlan_port_keep_tag_set, + .vlan_port_pvidmode_set = rtl930x_vlan_port_pvidmode_set, + .vlan_port_pvid_set = rtl930x_vlan_port_pvid_set, + .trk_mbr_ctr = rtl930x_trk_mbr_ctr, + .rma_bpdu_fld_pmask = RTL930X_RMA_BPDU_FLD_PMSK, + .init_eee = rtl930x_init_eee, + .port_eee_set = rtl930x_port_eee_set, + .eee_port_ability = rtl930x_eee_port_ability, + .l2_hash_seed = rtl930x_l2_hash_seed, + .l2_hash_key = rtl930x_l2_hash_key, + .read_mcast_pmask = rtl930x_read_mcast_pmask, + .write_mcast_pmask = rtl930x_write_mcast_pmask, + .pie_init = rtl930x_pie_init, + .pie_rule_write = rtl930x_pie_rule_write, + .pie_rule_add = rtl930x_pie_rule_add, + .pie_rule_rm = rtl930x_pie_rule_rm, + .l2_learning_setup = rtl930x_l2_learning_setup, + .packet_cntr_read = rtl930x_packet_cntr_read, + .packet_cntr_clear = rtl930x_packet_cntr_clear, + .route_read = rtl930x_route_read, + .route_write = rtl930x_route_write, + .host_route_write = rtl930x_host_route_write, + .l3_setup = rtl930x_l3_setup, + .set_l3_nexthop = rtl930x_set_l3_nexthop, + .get_l3_nexthop = rtl930x_get_l3_nexthop, + .get_l3_egress_mac = rtl930x_get_l3_egress_mac, + .set_l3_egress_mac = rtl930x_set_l3_egress_mac, + .find_l3_slot = rtl930x_find_l3_slot, + .route_lookup_hw = rtl930x_route_lookup_hw, + .get_l3_router_mac = rtl930x_get_l3_router_mac, + .set_l3_router_mac = rtl930x_set_l3_router_mac, + .set_l3_egress_intf = rtl930x_set_l3_egress_intf, + .set_distribution_algorithm = rtl930x_set_distribution_algorithm, + .led_init = rtl930x_led_init, +}; diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c new file mode 100644 index 00000000000..ee8d6c2c737 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/rtl931x.c @@ -0,0 +1,1701 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "rtl83xx.h" + +#define RTL931X_VLAN_PORT_TAG_STS_INTERNAL 0x0 +#define RTL931X_VLAN_PORT_TAG_STS_UNTAG 0x1 +#define RTL931X_VLAN_PORT_TAG_STS_TAGGED 0x2 +#define RTL931X_VLAN_PORT_TAG_STS_PRIORITY_TAGGED 0x3 + +#define RTL931X_VLAN_PORT_TAG_CTRL_BASE 0x4860 +/* port 0-56 */ +#define RTL931X_VLAN_PORT_TAG_CTRL(port) \ + RTL931X_VLAN_PORT_TAG_CTRL_BASE + (port << 2) +#define RTL931X_VLAN_PORT_TAG_EGR_OTAG_STS_MASK GENMASK(13,12) +#define RTL931X_VLAN_PORT_TAG_EGR_ITAG_STS_MASK GENMASK(11,10) +#define RTL931X_VLAN_PORT_TAG_EGR_OTAG_KEEP_MASK GENMASK(9,9) +#define RTL931X_VLAN_PORT_TAG_EGR_ITAG_KEEP_MASK GENMASK(8,8) +#define RTL931X_VLAN_PORT_TAG_IGR_OTAG_KEEP_MASK GENMASK(7,7) +#define RTL931X_VLAN_PORT_TAG_IGR_ITAG_KEEP_MASK GENMASK(6,6) +#define RTL931X_VLAN_PORT_TAG_OTPID_IDX_MASK GENMASK(5,4) +#define RTL931X_VLAN_PORT_TAG_OTPID_KEEP_MASK GENMASK(3,3) +#define RTL931X_VLAN_PORT_TAG_ITPID_IDX_MASK GENMASK(2,1) +#define RTL931X_VLAN_PORT_TAG_ITPID_KEEP_MASK GENMASK(0,0) + +extern struct mutex smi_lock; +extern struct rtl83xx_soc_info soc_info; + +/* Definition of the RTL931X-specific template field IDs as used in the PIE */ +enum template_field_id { + TEMPLATE_FIELD_SPM0 = 1, + TEMPLATE_FIELD_SPM1 = 2, + TEMPLATE_FIELD_SPM2 = 3, + TEMPLATE_FIELD_SPM3 = 4, + TEMPLATE_FIELD_DMAC0 = 9, + TEMPLATE_FIELD_DMAC1 = 10, + TEMPLATE_FIELD_DMAC2 = 11, + TEMPLATE_FIELD_SMAC0 = 12, + TEMPLATE_FIELD_SMAC1 = 13, + TEMPLATE_FIELD_SMAC2 = 14, + TEMPLATE_FIELD_ETHERTYPE = 15, + TEMPLATE_FIELD_OTAG = 16, + TEMPLATE_FIELD_ITAG = 17, + TEMPLATE_FIELD_SIP0 = 18, + TEMPLATE_FIELD_SIP1 = 19, + TEMPLATE_FIELD_DIP0 = 20, + TEMPLATE_FIELD_DIP1 = 21, + TEMPLATE_FIELD_IP_TOS_PROTO = 22, + TEMPLATE_FIELD_L4_SPORT = 23, + TEMPLATE_FIELD_L4_DPORT = 24, + TEMPLATE_FIELD_L34_HEADER = 25, + TEMPLATE_FIELD_TCP_INFO = 26, + TEMPLATE_FIELD_SIP2 = 34, + TEMPLATE_FIELD_SIP3 = 35, + TEMPLATE_FIELD_SIP4 = 36, + TEMPLATE_FIELD_SIP5 = 37, + TEMPLATE_FIELD_SIP6 = 38, + TEMPLATE_FIELD_SIP7 = 39, + TEMPLATE_FIELD_DIP2 = 42, + TEMPLATE_FIELD_DIP3 = 43, + TEMPLATE_FIELD_DIP4 = 44, + TEMPLATE_FIELD_DIP5 = 45, + TEMPLATE_FIELD_DIP6 = 46, + TEMPLATE_FIELD_DIP7 = 47, + TEMPLATE_FIELD_FLOW_LABEL = 49, + TEMPLATE_FIELD_DSAP_SSAP = 50, + TEMPLATE_FIELD_FWD_VID = 52, + TEMPLATE_FIELD_RANGE_CHK = 53, + TEMPLATE_FIELD_SLP = 55, + TEMPLATE_FIELD_DLP = 56, + TEMPLATE_FIELD_META_DATA = 57, + TEMPLATE_FIELD_FIRST_MPLS1 = 60, + TEMPLATE_FIELD_FIRST_MPLS2 = 61, + TEMPLATE_FIELD_DPM3 = 8, +}; + +/* The meaning of TEMPLATE_FIELD_VLAN depends on phase and the configuration in + * RTL931X_PIE_CTRL. We use always the same definition and map to the inner VLAN tag: + */ +#define TEMPLATE_FIELD_VLAN TEMPLATE_FIELD_ITAG + +// Number of fixed templates predefined in the RTL9300 SoC +#define N_FIXED_TEMPLATES 5 +// RTL931x specific predefined templates +static enum template_field_id fixed_templates[N_FIXED_TEMPLATES][N_FIXED_FIELDS_RTL931X] = +{ + { + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_SMAC0, TEMPLATE_FIELD_SMAC1, TEMPLATE_FIELD_SMAC2, + TEMPLATE_FIELD_VLAN, TEMPLATE_FIELD_IP_TOS_PROTO, TEMPLATE_FIELD_DSAP_SSAP, + TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1, + TEMPLATE_FIELD_SPM2, TEMPLATE_FIELD_SPM3 + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, + TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_IP_TOS_PROTO, TEMPLATE_FIELD_TCP_INFO, + TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_L4_DPORT, TEMPLATE_FIELD_VLAN, + TEMPLATE_FIELD_RANGE_CHK, TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1, + TEMPLATE_FIELD_SPM2, TEMPLATE_FIELD_SPM3 + }, { + TEMPLATE_FIELD_DMAC0, TEMPLATE_FIELD_DMAC1, TEMPLATE_FIELD_DMAC2, + TEMPLATE_FIELD_VLAN, TEMPLATE_FIELD_ETHERTYPE, TEMPLATE_FIELD_IP_TOS_PROTO, + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_DIP0, + TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_L4_DPORT, + TEMPLATE_FIELD_META_DATA, TEMPLATE_FIELD_SLP + }, { + TEMPLATE_FIELD_DIP0, TEMPLATE_FIELD_DIP1, TEMPLATE_FIELD_DIP2, + TEMPLATE_FIELD_DIP3, TEMPLATE_FIELD_DIP4, TEMPLATE_FIELD_DIP5, + TEMPLATE_FIELD_DIP6, TEMPLATE_FIELD_DIP7, TEMPLATE_FIELD_IP_TOS_PROTO, + TEMPLATE_FIELD_TCP_INFO, TEMPLATE_FIELD_L4_SPORT, TEMPLATE_FIELD_L4_DPORT, + TEMPLATE_FIELD_RANGE_CHK, TEMPLATE_FIELD_SLP + }, { + TEMPLATE_FIELD_SIP0, TEMPLATE_FIELD_SIP1, TEMPLATE_FIELD_SIP2, + TEMPLATE_FIELD_SIP3, TEMPLATE_FIELD_SIP4, TEMPLATE_FIELD_SIP5, + TEMPLATE_FIELD_SIP6, TEMPLATE_FIELD_SIP7, TEMPLATE_FIELD_META_DATA, + TEMPLATE_FIELD_VLAN, TEMPLATE_FIELD_SPM0, TEMPLATE_FIELD_SPM1, + TEMPLATE_FIELD_SPM2, TEMPLATE_FIELD_SPM3 + }, +}; + +inline void rtl931x_exec_tbl0_cmd(u32 cmd) +{ + sw_w32(cmd, RTL931X_TBL_ACCESS_CTRL_0); + do { } while (sw_r32(RTL931X_TBL_ACCESS_CTRL_0) & (1 << 20)); +} + +inline void rtl931x_exec_tbl1_cmd(u32 cmd) +{ + sw_w32(cmd, RTL931X_TBL_ACCESS_CTRL_1); + do { } while (sw_r32(RTL931X_TBL_ACCESS_CTRL_1) & (1 << 17)); +} + +inline int rtl931x_tbl_access_data_0(int i) +{ + return RTL931X_TBL_ACCESS_DATA_0(i); +} + +void rtl931x_vlan_profile_dump(int index) +{ + u64 profile[4]; + + if (index < 0 || index > 15) + return; + + profile[0] = sw_r32(RTL931X_VLAN_PROFILE_SET(index)); + profile[1] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 4) & 0x1FFFFFFFULL) << 32 + | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 8) & 0xFFFFFFFF); + profile[2] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 16) & 0x1FFFFFFFULL) << 32 + | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 12) & 0xFFFFFFFF); + profile[3] = (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 20) & 0x1FFFFFFFULL) << 32 + | (sw_r32(RTL931X_VLAN_PROFILE_SET(index) + 24) & 0xFFFFFFFF); + + pr_info("VLAN %d: L2 learning: %d, L2 Unknown MultiCast Field %llx, \ + IPv4 Unknown MultiCast Field %llx, IPv6 Unknown MultiCast Field: %llx", + index, (u32) (profile[0] & (3 << 14)), profile[1], profile[2], profile[3]); +} + +static void rtl931x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 20 /* Execute cmd */ + | 0 << 19 /* Read */ + | 5 << 15 /* Table type 0b101 */ + | (msti & 0x3fff); + priv->r->exec_tbl0_cmd(cmd); + + for (i = 0; i < 4; i++) + port_state[i] = sw_r32(priv->r->tbl_access_data_0(i)); +} + +static void rtl931x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]) +{ + int i; + u32 cmd = 1 << 20 /* Execute cmd */ + | 1 << 19 /* Write */ + | 5 << 15 /* Table type 0b101 */ + | (msti & 0x3fff); + for (i = 0; i < 4; i++) + sw_w32(port_state[i], priv->r->tbl_access_data_0(i)); + priv->r->exec_tbl0_cmd(cmd); +} + +inline static int rtl931x_trk_mbr_ctr(int group) +{ + return RTL931X_TRK_MBR_CTRL + (group << 2); +} + +static void rtl931x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w, x, y; + // Read VLAN table (3) via register 0 + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 3); + + rtl_table_read(r, vlan); + v = sw_r32(rtl_table_data(r, 0)); + w = sw_r32(rtl_table_data(r, 1)); + x = sw_r32(rtl_table_data(r, 2)); + y = sw_r32(rtl_table_data(r, 3)); + rtl_table_release(r); + + pr_debug("VLAN_READ %d: %08x %08x %08x %08x\n", vlan, v, w, x, y); + info->tagged_ports = ((u64) v) << 25 | (w >> 7); + info->profile_id = (x >> 16) & 0xf; + info->fid = w & 0x7f; // AKA MSTI depending on context + info->hash_uc_fid = !!(x & BIT(31)); + info->hash_mc_fid = !!(x & BIT(30)); + info->if_id = (x >> 20) & 0x3ff; + info->profile_id = (x >> 16) & 0xf; + info->multicast_grp_mask = x & 0xffff; + if (x & BIT(31)) + info->l2_tunnel_list_id = y >> 18; + else + info->l2_tunnel_list_id = -1; + pr_debug("%s read tagged %016llx, profile-id %d, uc %d, mc %d, intf-id %d\n", __func__, + info->tagged_ports, info->profile_id, info->hash_uc_fid, info->hash_mc_fid, + info->if_id); + + // Read UNTAG table via table register 3 + r = rtl_table_get(RTL9310_TBL_3, 0); + rtl_table_read(r, vlan); + v = ((u64)sw_r32(rtl_table_data(r, 0))) << 25; + v |= sw_r32(rtl_table_data(r, 1)) >> 7; + rtl_table_release(r); + + info->untagged_ports = v; +} + +static void rtl931x_vlan_set_tagged(u32 vlan, struct rtl838x_vlan_info *info) +{ + u32 v, w, x, y; + // Access VLAN table (1) via register 0 + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 3); + + v = info->tagged_ports >> 25; + w = (info->tagged_ports & 0x1fffff) << 7; + w |= info->fid & 0x7f; + x = info->hash_uc_fid ? BIT(31) : 0; + x |= info->hash_mc_fid ? BIT(30) : 0; + x |= info->if_id & 0x3ff << 20; + x |= (info->profile_id & 0xf) << 16; + x |= info->multicast_grp_mask & 0xffff; + if (info->l2_tunnel_list_id >= 0) { + y = info->l2_tunnel_list_id << 18; + y |= BIT(31); + } else { + y = 0; + } + + sw_w32(v, rtl_table_data(r, 0)); + sw_w32(w, rtl_table_data(r, 1)); + sw_w32(x, rtl_table_data(r, 2)); + sw_w32(y, rtl_table_data(r, 3)); + + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +static void rtl931x_vlan_set_untagged(u32 vlan, u64 portmask) +{ + struct table_reg *r = rtl_table_get(RTL9310_TBL_3, 0); + + rtl839x_set_port_reg_be(portmask << 7, rtl_table_data(r, 0)); + rtl_table_write(r, vlan); + rtl_table_release(r); +} + +static inline int rtl931x_mac_force_mode_ctrl(int p) +{ + return RTL931X_MAC_FORCE_MODE_CTRL + (p << 2); +} + +static inline int rtl931x_mac_link_spd_sts(int p) +{ + return RTL931X_MAC_LINK_SPD_STS + (((p >> 3) << 2)); +} + +static inline int rtl931x_mac_port_ctrl(int p) +{ + return RTL931X_MAC_L2_PORT_CTRL + (p << 7); +} + +static inline int rtl931x_l2_port_new_salrn(int p) +{ + return RTL931X_L2_PORT_NEW_SALRN(p); +} + +static inline int rtl931x_l2_port_new_sa_fwd(int p) +{ + return RTL931X_L2_PORT_NEW_SA_FWD(p); +} + +irqreturn_t rtl931x_switch_irq(int irq, void *dev_id) +{ + struct dsa_switch *ds = dev_id; + u32 status = sw_r32(RTL931X_ISR_GLB_SRC); + u64 ports = rtl839x_get_port_reg_le(RTL931X_ISR_PORT_LINK_STS_CHG); + u64 link; + int i; + + /* Clear status */ + rtl839x_set_port_reg_le(ports, RTL931X_ISR_PORT_LINK_STS_CHG); + pr_debug("RTL931X Link change: status: %x, ports %016llx\n", status, ports); + + link = rtl839x_get_port_reg_le(RTL931X_MAC_LINK_STS); + // Must re-read this to get correct status + link = rtl839x_get_port_reg_le(RTL931X_MAC_LINK_STS); + pr_debug("RTL931X Link change: status: %x, link status %016llx\n", status, link); + + for (i = 0; i < 56; i++) { + if (ports & BIT_ULL(i)) { + if (link & BIT_ULL(i)) { + pr_info("%s port %d up\n", __func__, i); + dsa_port_phylink_mac_change(ds, i, true); + } else { + pr_info("%s port %d down\n", __func__, i); + dsa_port_phylink_mac_change(ds, i, false); + } + } + } + return IRQ_HANDLED; +} + +int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val) +{ + u32 v; + int err = 0; + + val &= 0xffff; + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + pr_debug("%s: writing to phy %d %d %d %d\n", __func__, port, page, reg, val); + /* Clear both port registers */ + sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2); + sw_w32(0, RTL931X_SMI_INDRT_ACCESS_CTRL_2 + 4); + sw_w32_mask(0, BIT(port % 32), RTL931X_SMI_INDRT_ACCESS_CTRL_2 + (port / 32) * 4); + + sw_w32_mask(0xffff, val, RTL931X_SMI_INDRT_ACCESS_CTRL_3); + + v = reg << 6 | page << 11 ; + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + sw_w32(0x1ff, RTL931X_SMI_INDRT_ACCESS_CTRL_1); + + v |= BIT(4) | 1; /* Write operation and execute */ + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); + + if (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x2) + err = -EIO; + + mutex_unlock(&smi_lock); + return err; +} + +int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val) +{ + u32 v; + + if (port > 63 || page > 4095 || reg > 31) + return -ENOTSUPP; + + mutex_lock(&smi_lock); + + sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL); + + v = reg << 6 | page << 11 | 1; + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + } while (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0) & 0x1); + + v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); + *val = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3); + *val = (*val & 0xffff0000) >> 16; + + pr_debug("%s: port %d, page: %d, reg: %x, val: %x, v: %08x\n", + __func__, port, page, reg, *val, v); + + mutex_unlock(&smi_lock); + return 0; +} + +/* + * Read an mmd register of the PHY + */ +int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val) +{ + int err = 0; + u32 v; + /* Select PHY register type + * If select 1G/10G MMD register type, registers EXT_PAGE, MAIN_PAGE and REG settings are don’t care. + * 0x0 Normal register (Clause 22) + * 0x1: 1G MMD register (MMD via Clause 22 registers 13 and 14) + * 0x2: 10G MMD register (MMD via Clause 45) + */ + int type = (regnum & MII_ADDR_C45)?2:1; + + mutex_lock(&smi_lock); + + // Set PHY to access via port-number + sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | mdiobus_c45_regad(regnum), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); + + v = type << 2 | BIT(0); // MMD-access-type | EXEC + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); + } while (v & BIT(0)); + + // Check for error condition + if (v & BIT(1)) + err = -EIO; + + *val = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) >> 16; + + pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, + port, devnum, mdiobus_c45_regad(regnum), *val, err); + + mutex_unlock(&smi_lock); + + return err; +} + +/* + * Write to an mmd register of the PHY + */ +int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val) +{ + int err = 0; + u32 v; + int type = (regnum & MII_ADDR_C45)?2:1; + u64 pm; + + mutex_lock(&smi_lock); + + // Set PHY to access via port-mask + pm = (u64)1 << port; + sw_w32((u32)pm, RTL931X_SMI_INDRT_ACCESS_CTRL_2); + sw_w32((u32)(pm >> 32), RTL931X_SMI_INDRT_ACCESS_CTRL_2 + 4); + + // Set data to write + sw_w32_mask(0xffff, val, RTL931X_SMI_INDRT_ACCESS_CTRL_3); + + // Set MMD device number and register to write to + sw_w32(devnum << 16 | mdiobus_c45_regad(regnum), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL); + + v = BIT(4) | type << 2 | BIT(0); // WRITE | MMD-access-type | EXEC + sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0); + + do { + v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0); + } while (v & BIT(0)); + + pr_debug("%s: port %d, dev: %x, regnum: %x, val: %x (err %d)\n", __func__, + port, devnum, mdiobus_c45_regad(regnum), val, err); + mutex_unlock(&smi_lock); + return err; +} + +void rtl931x_print_matrix(void) +{ + volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); + int i; + + for (i = 0; i < 52; i += 4) + pr_info("> %16llx %16llx %16llx %16llx\n", + ptr[i + 0], ptr[i + 1], ptr[i + 2], ptr[i + 3]); + pr_info("CPU_PORT> %16llx\n", ptr[52]); +} + +void rtl931x_set_receive_management_action(int port, rma_ctrl_t type, action_type_t action) +{ + u32 value = 0; + + /* hack for value mapping */ + if (type == GRATARP && action == COPY2CPU) + action = TRAP2MASTERCPU; + + switch(action) { + case FORWARD: + value = 0; + break; + case DROP: + value = 1; + break; + case TRAP2CPU: + value = 2; + break; + case TRAP2MASTERCPU: + value = 3; + break; + case FLOODALL: + value = 4; + break; + default: + break; + } + + switch(type) { + case BPDU: + sw_w32_mask(7 << ((port % 10) * 3), value << ((port % 10) * 3), RTL931X_RMA_BPDU_CTRL + ((port / 10) << 2)); + break; + case PTP: + //udp + sw_w32_mask(3 << 2, value << 2, RTL931X_RMA_PTP_CTRL + (port << 2)); + //eth2 + sw_w32_mask(3, value, RTL931X_RMA_PTP_CTRL + (port << 2)); + break; + case PTP_UDP: + sw_w32_mask(3 << 2, value << 2, RTL931X_RMA_PTP_CTRL + (port << 2)); + break; + case PTP_ETH2: + sw_w32_mask(3, value, RTL931X_RMA_PTP_CTRL + (port << 2)); + break; + case LLTP: + sw_w32_mask(7 << ((port % 10) * 3), value << ((port % 10) * 3), RTL931X_RMA_LLTP_CTRL + ((port / 10) << 2)); + break; + case EAPOL: + sw_w32_mask(7 << ((port % 10) * 3), value << ((port % 10) * 3), RTL931X_RMA_EAPOL_CTRL + ((port / 10) << 2)); + break; + case GRATARP: + sw_w32_mask(3 << ((port & 0xf) << 1), value << ((port & 0xf) << 1), RTL931X_TRAP_ARP_GRAT_PORT_ACT + ((port >> 4) << 2)); + break; + } +} + +u64 rtl931x_traffic_get(int source) +{ + u32 v; + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 6); + + rtl_table_read(r, source); + v = sw_r32(rtl_table_data(r, 0)); + rtl_table_release(r); + return v >> 3; +} + +/* + * Enable traffic between a source port and a destination port matrix + */ +void rtl931x_traffic_set(int source, u64 dest_matrix) +{ + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 6); + + sw_w32((dest_matrix << 3), rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl931x_traffic_enable(int source, int dest) +{ + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 6); + rtl_table_read(r, source); + sw_w32_mask(0, BIT(dest + 3), rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +void rtl931x_traffic_disable(int source, int dest) +{ + struct table_reg *r = rtl_table_get(RTL9310_TBL_0, 6); + rtl_table_read(r, source); + sw_w32_mask(BIT(dest + 3), 0, rtl_table_data(r, 0)); + rtl_table_write(r, source); + rtl_table_release(r); +} + +static u64 rtl931x_l2_hash_seed(u64 mac, u32 vid) +{ + u64 v = vid; + + v <<= 48; + v |= mac; + + return v; +} + +/* + * Calculate both the block 0 and the block 1 hash by applyingthe same hash + * algorithm as the one used currently by the ASIC to the seed, and return + * both hashes in the lower and higher word of the return value since only 12 bit of + * the hash are significant. + */ +static u32 rtl931x_l2_hash_key(struct rtl838x_switch_priv *priv, u64 seed) +{ + u32 h, h0, h1, h2, h3, h4, k0, k1; + + h0 = seed & 0xfff; + h1 = (seed >> 12) & 0xfff; + h2 = (seed >> 24) & 0xfff; + h3 = (seed >> 36) & 0xfff; + h4 = (seed >> 48) & 0xfff; + h4 = ((h4 & 0x7) << 9) | ((h4 >> 3) & 0x1ff); + k0 = h0 ^ h1 ^ h2 ^ h3 ^ h4; + + h0 = seed & 0xfff; + h0 = ((h0 & 0x1ff) << 3) | ((h0 >> 9) & 0x7); + h1 = (seed >> 12) & 0xfff; + h1 = ((h1 & 0x3f) << 6) | ((h1 >> 6) & 0x3f); + h2 = (seed >> 24) & 0xfff; + h3 = (seed >> 36) & 0xfff; + h3 = ((h3 & 0x3f) << 6) | ((h3 >> 6) & 0x3f); + h4 = (seed >> 48) & 0xfff; + k1 = h0 ^ h1 ^ h2 ^ h3 ^ h4; + + // Algorithm choice for block 0 + if (sw_r32(RTL931X_L2_CTRL) & BIT(0)) + h = k1; + else + h = k0; + + /* Algorithm choice for block 1 + * Since k0 and k1 are < 4096, adding 4096 will offset the hash into the second + * half of hash-space + * 4096 is in fact the hash-table size 32768 divided by 4 hashes per bucket + * divided by 2 to divide the hash space in 2 + */ + if (sw_r32(RTL931X_L2_CTRL) & BIT(1)) + h |= (k1 + 4096) << 16; + else + h |= (k0 + 4096) << 16; + + return h; +} + +/* + * Fills an L2 entry structure from the SoC registers + */ +static void rtl931x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e) +{ + pr_debug("In %s valid?\n", __func__); + e->valid = !!(r[0] & BIT(31)); + if (!e->valid) + return; + + pr_debug("%s: entry valid, raw: %08x %08x %08x %08x\n", __func__, r[0], r[1], r[2], r[3]); + e->is_ip_mc = false; + e->is_ipv6_mc = false; + + e->mac[0] = r[0] >> 8; + e->mac[1] = r[0]; + e->mac[2] = r[1] >> 24; + e->mac[3] = r[1] >> 16; + e->mac[4] = r[1] >> 8; + e->mac[5] = r[1]; + + e->is_open_flow = !!(r[0] & BIT(30)); + e->is_pe_forward = !!(r[0] & BIT(29)); + e->next_hop = !!(r[2] & BIT(30)); + e->rvid = (r[0] >> 16) & 0xfff; + + /* Is it a unicast entry? check multicast bit */ + if (!(e->mac[0] & 1)) { + e->type = L2_UNICAST; + e->is_l2_tunnel = !!(r[2] & BIT(31)); + e->is_static = !!(r[2] & BIT(13)); + e->port = (r[2] >> 19) & 0x3ff; + // Check for trunk port + if (r[2] & BIT(29)) { + e->is_trunk = true; + e->stack_dev = (e->port >> 9) & 1; + e->trunk = e->port & 0x3f; + } else { + e->is_trunk = false; + e->stack_dev = (e->port >> 6) & 0xf; + e->port = e->port & 0x3f; + } + + e->block_da = !!(r[2] & BIT(14)); + e->block_sa = !!(r[2] & BIT(15)); + e->suspended = !!(r[2] & BIT(12)); + e->age = (r[2] >> 16) & 3; + + // the UC_VID field in hardware is used for the VID or for the route id + if (e->next_hop) { + e->nh_route_id = r[2] & 0x7ff; + e->vid = 0; + } else { + e->vid = r[2] & 0xfff; + e->nh_route_id = 0; + } + if (e->is_l2_tunnel) + e->l2_tunnel_id = ((r[2] & 0xff) << 4) | (r[3] >> 28); + // TODO: Implement VLAN conversion + } else { + e->type = L2_MULTICAST; + e->is_local_forward = !!(r[2] & BIT(31)); + e->is_remote_forward = !!(r[2] & BIT(17)); + e->mc_portmask_index = (r[2] >> 18) & 0xfff; + e->l2_tunnel_list_id = (r[2] >> 4) & 0x1fff; + } +} + +/* + * Fills the 3 SoC table registers r[] with the information of in the rtl838x_l2_entry + */ +static void rtl931x_fill_l2_row(u32 r[], struct rtl838x_l2_entry *e) +{ + u32 port; + + if (!e->valid) { + r[0] = r[1] = r[2] = 0; + return; + } + + r[2] = BIT(31); // Set valid bit + + r[0] = ((u32)e->mac[0]) << 24 | ((u32)e->mac[1]) << 16 + | ((u32)e->mac[2]) << 8 | ((u32)e->mac[3]); + r[1] = ((u32)e->mac[4]) << 24 | ((u32)e->mac[5]) << 16; + + r[2] |= e->next_hop ? BIT(12) : 0; + + if (e->type == L2_UNICAST) { + r[2] |= e->is_static ? BIT(14) : 0; + r[1] |= e->rvid & 0xfff; + r[2] |= (e->port & 0x3ff) << 20; + if (e->is_trunk) { + r[2] |= BIT(30); + port = e->stack_dev << 9 | (e->port & 0x3f); + } else { + port = (e->stack_dev & 0xf) << 6; + port |= e->port & 0x3f; + } + r[2] |= port << 20; + r[2] |= e->block_da ? BIT(15) : 0; + r[2] |= e->block_sa ? BIT(17) : 0; + r[2] |= e->suspended ? BIT(13) : 0; + r[2] |= (e->age & 0x3) << 17; + // the UC_VID field in hardware is used for the VID or for the route id + if (e->next_hop) + r[2] |= e->nh_route_id & 0x7ff; + else + r[2] |= e->vid & 0xfff; + } else { // L2_MULTICAST + r[2] |= (e->mc_portmask_index & 0x3ff) << 16; + r[2] |= e->mc_mac_index & 0x7ff; + } +} + +/* + * Read an L2 UC or MC entry out of a hash bucket of the L2 forwarding table + * hash is the id of the bucket and pos is the position of the entry in that bucket + * The data read from the SoC is filled into rtl838x_l2_entry + */ +static u64 rtl931x_read_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[4]; + struct table_reg *q = rtl_table_get(RTL9310_TBL_0, 0); + u32 idx; + int i; + u64 mac; + u64 seed; + + pr_debug("%s: hash %08x, pos: %d\n", __func__, hash, pos); + + /* On the RTL93xx, 2 different hash algorithms are used making it a total of + * 8 buckets that need to be searched, 4 for each hash-half + * Use second hash space when bucket is between 4 and 8 */ + if (pos >= 4) { + pos -= 4; + hash >>= 16; + } else { + hash &= 0xffff; + } + + idx = (0 << 14) | (hash << 2) | pos; // Search SRAM, with hash and at pos in bucket + pr_debug("%s: NOW hash %08x, pos: %d\n", __func__, hash, pos); + + rtl_table_read(q, idx); + for (i = 0; i < 4; i++) + r[i] = sw_r32(rtl_table_data(q, i)); + + rtl_table_release(q); + + rtl931x_fill_l2_entry(r, e); + + pr_debug("%s: valid: %d, nh: %d\n", __func__, e->valid, e->next_hop); + if (!e->valid) + return 0; + + mac = ((u64)e->mac[0]) << 40 | ((u64)e->mac[1]) << 32 | ((u64)e->mac[2]) << 24 + | ((u64)e->mac[3]) << 16 | ((u64)e->mac[4]) << 8 | ((u64)e->mac[5]); + + seed = rtl931x_l2_hash_seed(mac, e->rvid); + pr_debug("%s: mac %016llx, seed %016llx\n", __func__, mac, seed); + // return vid with concatenated mac as unique id + return seed; +} + +static u64 rtl931x_read_cam(int idx, struct rtl838x_l2_entry *e) +{ + return 0; +} + +static void rtl931x_write_cam(int idx, struct rtl838x_l2_entry *e) +{ +} + +static void rtl931x_write_l2_entry_using_hash(u32 hash, u32 pos, struct rtl838x_l2_entry *e) +{ + u32 r[4]; + struct table_reg *q = rtl_table_get(RTL9310_TBL_0, 0); + u32 idx = (0 << 14) | (hash << 2) | pos; // Access SRAM, with hash and at pos in bucket + int i; + + pr_info("%s: hash %d, pos %d\n", __func__, hash, pos); + pr_info("%s: index %d -> mac %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, idx, + e->mac[0], e->mac[1], e->mac[2], e->mac[3],e->mac[4],e->mac[5]); + + rtl931x_fill_l2_row(r, e); + pr_info("%s: %d: %08x %08x %08x\n", __func__, idx, r[0], r[1], r[2]); + + for (i= 0; i < 4; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); + +} + +static void rtl931x_vlan_fwd_on_inner(int port, bool is_set) +{ + // Always set all tag modes to fwd based on either inner or outer tag + if (is_set) + sw_w32_mask(0, 0xf, RTL931X_VLAN_PORT_FWD + (port << 2)); + else + sw_w32_mask(0xf, 0, RTL931X_VLAN_PORT_FWD + (port << 2)); +} + +static void rtl931x_vlan_profile_setup(int profile) +{ + u32 p[7]; + int i; + + pr_info("In %s\n", __func__); + + if (profile > 15) + return; + + p[0] = sw_r32(RTL931X_VLAN_PROFILE_SET(profile)); + + // Enable routing of Ipv4/6 Unicast and IPv4/6 Multicast traffic + //p[0] |= BIT(17) | BIT(16) | BIT(13) | BIT(12); + p[0] |= 0x3 << 11; // COPY2CPU + + p[1] = 0x1FFFFFF; // L2 unknwon MC flooding portmask all ports, including the CPU-port + p[2] = 0xFFFFFFFF; + p[3] = 0x1FFFFFF; // IPv4 unknwon MC flooding portmask + p[4] = 0xFFFFFFFF; + p[5] = 0x1FFFFFF; // IPv6 unknwon MC flooding portmask + p[6] = 0xFFFFFFFF; + + for (i = 0; i < 7; i++) + sw_w32(p[i], RTL931X_VLAN_PROFILE_SET(profile) + i * 4); + pr_info("Leaving %s\n", __func__); +} + +static void rtl931x_l2_learning_setup(void) +{ + // Portmask for flooding broadcast traffic + rtl839x_set_port_reg_be(0x1FFFFFFFFFFFFFF, RTL931X_L2_BC_FLD_PMSK); + + // Portmask for flooding unicast traffic with unknown destination + rtl839x_set_port_reg_be(0x1FFFFFFFFFFFFFF, RTL931X_L2_UNKN_UC_FLD_PMSK); + + // Limit learning to maximum: 64k entries, after that just flood (bits 0-2) + sw_w32((0xffff << 3) | FORWARD, RTL931X_L2_LRN_CONSTRT_CTRL); +} + +static u64 rtl931x_read_mcast_pmask(int idx) +{ + u64 portmask; + // Read MC_PMSK (2) via register RTL9310_TBL_0 + struct table_reg *q = rtl_table_get(RTL9310_TBL_0, 2); + + rtl_table_read(q, idx); + portmask = sw_r32(rtl_table_data(q, 0)); + portmask <<= 32; + portmask |= sw_r32(rtl_table_data(q, 1)); + portmask >>= 7; + rtl_table_release(q); + + pr_debug("%s: Index idx %d has portmask %016llx\n", __func__, idx, portmask); + return portmask; +} + +static void rtl931x_write_mcast_pmask(int idx, u64 portmask) +{ + u64 pm = portmask; + + // Access MC_PMSK (2) via register RTL9310_TBL_0 + struct table_reg *q = rtl_table_get(RTL9310_TBL_0, 2); + + pr_debug("%s: Index idx %d has portmask %016llx\n", __func__, idx, pm); + pm <<= 7; + sw_w32((u32)(pm >> 32), rtl_table_data(q, 0)); + sw_w32((u32)pm, rtl_table_data(q, 1)); + rtl_table_write(q, idx); + rtl_table_release(q); +} + + +static int rtl931x_set_ageing_time(unsigned long msec) +{ + int t = sw_r32(RTL931X_L2_AGE_CTRL); + + t &= 0x1FFFFF; + t = (t * 8) / 10; + pr_debug("L2 AGING time: %d sec\n", t); + + t = (msec / 100 + 7) / 8; + t = t > 0x1FFFFF ? 0x1FFFFF : t; + sw_w32_mask(0x1FFFFF, t, RTL931X_L2_AGE_CTRL); + pr_debug("Dynamic aging for ports: %x\n", sw_r32(RTL931X_L2_PORT_AGE_CTRL)); + return 0; +} +void rtl931x_sw_init(struct rtl838x_switch_priv *priv) +{ +// rtl931x_sds_init(priv); +} + +static void rtl931x_pie_lookup_enable(struct rtl838x_switch_priv *priv, int index) +{ + int block = index / PIE_BLOCK_SIZE; + + sw_w32_mask(0, BIT(block), RTL931X_PIE_BLK_LOOKUP_CTRL); +} + +/* + * Fills the data in the intermediate representation in the pie_rule structure + * into a data field for a given template field field_type + * TODO: This function looks very similar to the function of the rtl9300, but + * since it uses the physical template_field_id, which are different for each + * SoC and there are other field types, it is actually not. If we would also use + * an intermediate representation for a field type, we would could have one + * pie_data_fill function for all SoCs, provided we have also for each SoC a + * function to map between physical and intermediate field type + */ +int rtl931x_pie_data_fill(enum template_field_id field_type, struct pie_rule *pr, u16 *data, u16 *data_m) +{ + *data = *data_m = 0; + + switch (field_type) { + case TEMPLATE_FIELD_SPM0: + *data = pr->spm; + *data_m = pr->spm_m; + break; + case TEMPLATE_FIELD_SPM1: + *data = pr->spm >> 16; + *data_m = pr->spm_m >> 16; + break; + case TEMPLATE_FIELD_OTAG: + *data = pr->otag; + *data_m = pr->otag_m; + break; + case TEMPLATE_FIELD_SMAC0: + *data = pr->smac[4]; + *data = (*data << 8) | pr->smac[5]; + *data_m = pr->smac_m[4]; + *data_m = (*data_m << 8) | pr->smac_m[5]; + break; + case TEMPLATE_FIELD_SMAC1: + *data = pr->smac[2]; + *data = (*data << 8) | pr->smac[3]; + *data_m = pr->smac_m[2]; + *data_m = (*data_m << 8) | pr->smac_m[3]; + break; + case TEMPLATE_FIELD_SMAC2: + *data = pr->smac[0]; + *data = (*data << 8) | pr->smac[1]; + *data_m = pr->smac_m[0]; + *data_m = (*data_m << 8) | pr->smac_m[1]; + break; + case TEMPLATE_FIELD_DMAC0: + *data = pr->dmac[4]; + *data = (*data << 8) | pr->dmac[5]; + *data_m = pr->dmac_m[4]; + *data_m = (*data_m << 8) | pr->dmac_m[5]; + break; + case TEMPLATE_FIELD_DMAC1: + *data = pr->dmac[2]; + *data = (*data << 8) | pr->dmac[3]; + *data_m = pr->dmac_m[2]; + *data_m = (*data_m << 8) | pr->dmac_m[3]; + break; + case TEMPLATE_FIELD_DMAC2: + *data = pr->dmac[0]; + *data = (*data << 8) | pr->dmac[1]; + *data_m = pr->dmac_m[0]; + *data_m = (*data_m << 8) | pr->dmac_m[1]; + break; + case TEMPLATE_FIELD_ETHERTYPE: + *data = pr->ethertype; + *data_m = pr->ethertype_m; + break; + case TEMPLATE_FIELD_ITAG: + *data = pr->itag; + *data_m = pr->itag_m; + break; + case TEMPLATE_FIELD_SIP0: + if (pr->is_ipv6) { + *data = pr->sip6.s6_addr16[7]; + *data_m = pr->sip6_m.s6_addr16[7]; + } else { + *data = pr->sip; + *data_m = pr->sip_m; + } + break; + case TEMPLATE_FIELD_SIP1: + if (pr->is_ipv6) { + *data = pr->sip6.s6_addr16[6]; + *data_m = pr->sip6_m.s6_addr16[6]; + } else { + *data = pr->sip >> 16; + *data_m = pr->sip_m >> 16; + } + break; + case TEMPLATE_FIELD_SIP2: + case TEMPLATE_FIELD_SIP3: + case TEMPLATE_FIELD_SIP4: + case TEMPLATE_FIELD_SIP5: + case TEMPLATE_FIELD_SIP6: + case TEMPLATE_FIELD_SIP7: + *data = pr->sip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + *data_m = pr->sip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_SIP2)]; + break; + + case TEMPLATE_FIELD_DIP0: + if (pr->is_ipv6) { + *data = pr->dip6.s6_addr16[7]; + *data_m = pr->dip6_m.s6_addr16[7]; + } else { + *data = pr->dip; + *data_m = pr->dip_m; + } + break; + case TEMPLATE_FIELD_DIP1: + if (pr->is_ipv6) { + *data = pr->dip6.s6_addr16[6]; + *data_m = pr->dip6_m.s6_addr16[6]; + } else { + *data = pr->dip >> 16; + *data_m = pr->dip_m >> 16; + } + break; + + case TEMPLATE_FIELD_DIP2: + case TEMPLATE_FIELD_DIP3: + case TEMPLATE_FIELD_DIP4: + case TEMPLATE_FIELD_DIP5: + case TEMPLATE_FIELD_DIP6: + case TEMPLATE_FIELD_DIP7: + *data = pr->dip6.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + *data_m = pr->dip6_m.s6_addr16[5 - (field_type - TEMPLATE_FIELD_DIP2)]; + break; + + case TEMPLATE_FIELD_IP_TOS_PROTO: + *data = pr->tos_proto; + *data_m = pr->tos_proto_m; + break; + case TEMPLATE_FIELD_L4_SPORT: + *data = pr->sport; + *data_m = pr->sport_m; + break; + case TEMPLATE_FIELD_L4_DPORT: + *data = pr->dport; + *data_m = pr->dport_m; + break; + case TEMPLATE_FIELD_DSAP_SSAP: + *data = pr->dsap_ssap; + *data_m = pr->dsap_ssap_m; + break; + case TEMPLATE_FIELD_TCP_INFO: + *data = pr->tcp_info; + *data_m = pr->tcp_info_m; + break; + case TEMPLATE_FIELD_RANGE_CHK: + pr_info("TEMPLATE_FIELD_RANGE_CHK: not configured\n"); + break; + default: + pr_info("%s: unknown field %d\n", __func__, field_type); + return -1; + } + + return 0; +} + +/* + * Reads the intermediate representation of the templated match-fields of the + * PIE rule in the pie_rule structure and fills in the raw data fields in the + * raw register space r[]. + * The register space configuration size is identical for the RTL8380/90 and RTL9300, + * however the RTL931X has 2 more registers / fields and the physical field-ids are different + * on all SoCs + * On the RTL9300 the mask fields are not word-aligend! + */ +static void rtl931x_write_pie_templated(u32 r[], struct pie_rule *pr, enum template_field_id t[]) +{ + int i; + u16 data, data_m; + + for (i = 0; i < N_FIXED_FIELDS; i++) { + rtl931x_pie_data_fill(t[i], pr, &data, &data_m); + + // On the RTL9300, the mask fields are not word aligned! + if (!(i % 2)) { + r[5 - i / 2] = data; + r[12 - i / 2] |= ((u32)data_m << 8); + } else { + r[5 - i / 2] |= ((u32)data) << 16; + r[12 - i / 2] |= ((u32)data_m) << 24; + r[11 - i / 2] |= ((u32)data_m) >> 8; + } + } +} + +static void rtl931x_read_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + pr->mgnt_vlan = r[7] & BIT(31); + if (pr->phase == PHASE_IACL) + pr->dmac_hit_sw = r[7] & BIT(30); + else // TODO: EACL/VACL phase handling + pr->content_too_deep = r[7] & BIT(30); + pr->not_first_frag = r[7] & BIT(29); + pr->frame_type_l4 = (r[7] >> 26) & 7; + pr->frame_type = (r[7] >> 24) & 3; + pr->otag_fmt = (r[7] >> 23) & 1; + pr->itag_fmt = (r[7] >> 22) & 1; + pr->otag_exist = (r[7] >> 21) & 1; + pr->itag_exist = (r[7] >> 20) & 1; + pr->frame_type_l2 = (r[7] >> 18) & 3; + pr->igr_normal_port = (r[7] >> 17) & 1; + pr->tid = (r[7] >> 16) & 1; + + pr->mgnt_vlan_m = r[14] & BIT(15); + if (pr->phase == PHASE_IACL) + pr->dmac_hit_sw_m = r[14] & BIT(14); + else + pr->content_too_deep_m = r[14] & BIT(14); + pr->not_first_frag_m = r[14] & BIT(13); + pr->frame_type_l4_m = (r[14] >> 10) & 7; + pr->frame_type_m = (r[14] >> 8) & 3; + pr->otag_fmt_m = r[14] & BIT(7); + pr->itag_fmt_m = r[14] & BIT(6); + pr->otag_exist_m = r[14] & BIT(5); + pr->itag_exist_m = r[14] & BIT (4); + pr->frame_type_l2_m = (r[14] >> 2) & 3; + pr->igr_normal_port_m = r[14] & BIT(1); + pr->tid_m = r[14] & 1; + + pr->valid = r[15] & BIT(31); + pr->cond_not = r[15] & BIT(30); + pr->cond_and1 = r[15] & BIT(29); + pr->cond_and2 = r[15] & BIT(28); +} + +static void rtl931x_write_pie_fixed_fields(u32 r[], struct pie_rule *pr) +{ + r[7] |= pr->mgnt_vlan ? BIT(31) : 0; + if (pr->phase == PHASE_IACL) + r[7] |= pr->dmac_hit_sw ? BIT(30) : 0; + else + r[7] |= pr->content_too_deep ? BIT(30) : 0; + r[7] |= pr->not_first_frag ? BIT(29) : 0; + r[7] |= ((u32) (pr->frame_type_l4 & 0x7)) << 26; + r[7] |= ((u32) (pr->frame_type & 0x3)) << 24; + r[7] |= pr->otag_fmt ? BIT(23) : 0; + r[7] |= pr->itag_fmt ? BIT(22) : 0; + r[7] |= pr->otag_exist ? BIT(21) : 0; + r[7] |= pr->itag_exist ? BIT(20) : 0; + r[7] |= ((u32) (pr->frame_type_l2 & 0x3)) << 18; + r[7] |= pr->igr_normal_port ? BIT(17) : 0; + r[7] |= ((u32) (pr->tid & 0x1)) << 16; + + r[14] |= pr->mgnt_vlan_m ? BIT(15) : 0; + if (pr->phase == PHASE_IACL) + r[14] |= pr->dmac_hit_sw_m ? BIT(14) : 0; + else + r[14] |= pr->content_too_deep_m ? BIT(14) : 0; + r[14] |= pr->not_first_frag_m ? BIT(13) : 0; + r[14] |= ((u32) (pr->frame_type_l4_m & 0x7)) << 10; + r[14] |= ((u32) (pr->frame_type_m & 0x3)) << 8; + r[14] |= pr->otag_fmt_m ? BIT(7) : 0; + r[14] |= pr->itag_fmt_m ? BIT(6) : 0; + r[14] |= pr->otag_exist_m ? BIT(5) : 0; + r[14] |= pr->itag_exist_m ? BIT(4) : 0; + r[14] |= ((u32) (pr->frame_type_l2_m & 0x3)) << 2; + r[14] |= pr->igr_normal_port_m ? BIT(1) : 0; + r[14] |= (u32) (pr->tid_m & 0x1); + + r[15] |= pr->valid ? BIT(31) : 0; + r[15] |= pr->cond_not ? BIT(30) : 0; + r[15] |= pr->cond_and1 ? BIT(29) : 0; + r[15] |= pr->cond_and2 ? BIT(28) : 0; +} + +static void rtl931x_write_pie_action(u32 r[], struct pie_rule *pr) +{ + // Either drop or forward + if (pr->drop) { + r[15] |= BIT(11) | BIT(12) | BIT(13); // Do Green, Yellow and Red drops + // Actually DROP, not PERMIT in Green / Yellow / Red + r[16] |= BIT(27) | BIT(28) | BIT(29); + } else { + r[15] |= pr->fwd_sel ? BIT(14) : 0; + r[16] |= pr->fwd_act << 24; + r[16] |= BIT(21); // We overwrite any drop + } + if (pr->phase == PHASE_VACL) + r[16] |= pr->fwd_sa_lrn ? BIT(22) : 0; + r[15] |= pr->bypass_sel ? BIT(10) : 0; + r[15] |= pr->nopri_sel ? BIT(21) : 0; + r[15] |= pr->tagst_sel ? BIT(20) : 0; + r[15] |= pr->ovid_sel ? BIT(18) : 0; + r[15] |= pr->ivid_sel ? BIT(16) : 0; + r[15] |= pr->meter_sel ? BIT(27) : 0; + r[15] |= pr->mir_sel ? BIT(15) : 0; + r[15] |= pr->log_sel ? BIT(26) : 0; + + r[16] |= ((u32)(pr->fwd_data & 0xfff)) << 9; +// r[15] |= pr->log_octets ? BIT(31) : 0; + r[15] |= (u32)(pr->meter_data) >> 2; + r[16] |= (((u32)(pr->meter_data) >> 7) & 0x3) << 29; + + r[16] |= ((u32)(pr->ivid_act & 0x3)) << 21; + r[15] |= ((u32)(pr->ivid_data & 0xfff)) << 9; + r[16] |= ((u32)(pr->ovid_act & 0x3)) << 30; + r[16] |= ((u32)(pr->ovid_data & 0xfff)) << 16; + r[16] |= ((u32)(pr->mir_data & 0x3)) << 6; + r[17] |= ((u32)(pr->tagst_data & 0xf)) << 28; + r[17] |= ((u32)(pr->nopri_data & 0x7)) << 25; + r[17] |= pr->bypass_ibc_sc ? BIT(16) : 0; +} + +void rtl931x_pie_rule_dump_raw(u32 r[]) +{ + pr_info("Raw IACL table entry:\n"); + pr_info("r 0 - 7: %08x %08x %08x %08x %08x %08x %08x %08x\n", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); + pr_info("r 8 - 15: %08x %08x %08x %08x %08x %08x %08x %08x\n", + r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]); + pr_info("r 16 - 18: %08x %08x %08x\n", r[16], r[17], r[18]); + pr_info("Match : %08x %08x %08x %08x %08x %08x\n", r[0], r[1], r[2], r[3], r[4], r[5]); + pr_info("Fixed : %06x\n", r[6] >> 8); + pr_info("Match M: %08x %08x %08x %08x %08x %08x\n", + (r[6] << 24) | (r[7] >> 8), (r[7] << 24) | (r[8] >> 8), (r[8] << 24) | (r[9] >> 8), + (r[9] << 24) | (r[10] >> 8), (r[10] << 24) | (r[11] >> 8), + (r[11] << 24) | (r[12] >> 8)); + pr_info("R[13]: %08x\n", r[13]); + pr_info("Fixed M: %06x\n", ((r[12] << 16) | (r[13] >> 16)) & 0xffffff); + pr_info("Valid / not / and1 / and2 : %1x\n", (r[13] >> 12) & 0xf); + pr_info("r 13-16: %08x %08x %08x %08x\n", r[13], r[14], r[15], r[16]); +} + +static int rtl931x_pie_rule_write(struct rtl838x_switch_priv *priv, int idx, struct pie_rule *pr) +{ + // Access IACL table (0) via register 1, the table size is 4096 + struct table_reg *q = rtl_table_get(RTL9310_TBL_1, 0); + u32 r[22]; + int i; + int block = idx / PIE_BLOCK_SIZE; + u32 t_select = sw_r32(RTL931X_PIE_BLK_TMPLTE_CTRL(block)); + + pr_info("%s: %d, t_select: %08x\n", __func__, idx, t_select); + + for (i = 0; i < 22; i++) + r[i] = 0; + + if (!pr->valid) { + rtl_table_write(q, idx); + rtl_table_release(q); + return 0; + } + rtl931x_write_pie_fixed_fields(r, pr); + + pr_info("%s: template %d\n", __func__, (t_select >> (pr->tid * 4)) & 0xf); + rtl931x_write_pie_templated(r, pr, fixed_templates[(t_select >> (pr->tid * 4)) & 0xf]); + + rtl931x_write_pie_action(r, pr); + + rtl931x_pie_rule_dump_raw(r); + + for (i = 0; i < 22; i++) + sw_w32(r[i], rtl_table_data(q, i)); + + rtl_table_write(q, idx); + rtl_table_release(q); + + return 0; +} + +static bool rtl931x_pie_templ_has(int t, enum template_field_id field_type) +{ + int i; + enum template_field_id ft; + + for (i = 0; i < N_FIXED_FIELDS_RTL931X; i++) { + ft = fixed_templates[t][i]; + if (field_type == ft) + return true; + } + + return false; +} + +/* + * Verify that the rule pr is compatible with a given template t in block block + * Note that this function is SoC specific since the values of e.g. TEMPLATE_FIELD_SIP0 + * depend on the SoC + */ +static int rtl931x_pie_verify_template(struct rtl838x_switch_priv *priv, + struct pie_rule *pr, int t, int block) +{ + int i; + + if (!pr->is_ipv6 && pr->sip_m && !rtl931x_pie_templ_has(t, TEMPLATE_FIELD_SIP0)) + return -1; + + if (!pr->is_ipv6 && pr->dip_m && !rtl931x_pie_templ_has(t, TEMPLATE_FIELD_DIP0)) + return -1; + + if (pr->is_ipv6) { + if ((pr->sip6_m.s6_addr32[0] || pr->sip6_m.s6_addr32[1] + || pr->sip6_m.s6_addr32[2] || pr->sip6_m.s6_addr32[3]) + && !rtl931x_pie_templ_has(t, TEMPLATE_FIELD_SIP2)) + return -1; + if ((pr->dip6_m.s6_addr32[0] || pr->dip6_m.s6_addr32[1] + || pr->dip6_m.s6_addr32[2] || pr->dip6_m.s6_addr32[3]) + && !rtl931x_pie_templ_has(t, TEMPLATE_FIELD_DIP2)) + return -1; + } + + if (ether_addr_to_u64(pr->smac) && !rtl931x_pie_templ_has(t, TEMPLATE_FIELD_SMAC0)) + return -1; + + if (ether_addr_to_u64(pr->dmac) && !rtl931x_pie_templ_has(t, TEMPLATE_FIELD_DMAC0)) + return -1; + + // TODO: Check more + + i = find_first_zero_bit(&priv->pie_use_bm[block * 4], PIE_BLOCK_SIZE); + + if (i >= PIE_BLOCK_SIZE) + return -1; + + return i + PIE_BLOCK_SIZE * block; +} + +static int rtl931x_pie_rule_add(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx, block, j, t; + int min_block = 0; + int max_block = priv->n_pie_blocks / 2; + + if (pr->is_egress) { + min_block = max_block; + max_block = priv->n_pie_blocks; + } + pr_info("In %s\n", __func__); + + mutex_lock(&priv->pie_mutex); + + for (block = min_block; block < max_block; block++) { + for (j = 0; j < 2; j++) { + t = (sw_r32(RTL931X_PIE_BLK_TMPLTE_CTRL(block)) >> (j * 4)) & 0xf; + pr_info("Testing block %d, template %d, template id %d\n", block, j, t); + pr_info("%s: %08x\n", + __func__, sw_r32(RTL931X_PIE_BLK_TMPLTE_CTRL(block))); + idx = rtl931x_pie_verify_template(priv, pr, t, block); + if (idx >= 0) + break; + } + if (j < 2) + break; + } + + if (block >= priv->n_pie_blocks) { + mutex_unlock(&priv->pie_mutex); + return -EOPNOTSUPP; + } + + pr_info("Using block: %d, index %d, template-id %d\n", block, idx, j); + set_bit(idx, priv->pie_use_bm); + + pr->valid = true; + pr->tid = j; // Mapped to template number + pr->tid_m = 0x1; + pr->id = idx; + + rtl931x_pie_lookup_enable(priv, idx); + rtl931x_pie_rule_write(priv, idx, pr); + + mutex_unlock(&priv->pie_mutex); + return 0; +} + +/* + * Delete a range of Packet Inspection Engine rules + */ +static int rtl931x_pie_rule_del(struct rtl838x_switch_priv *priv, int index_from, int index_to) +{ + u32 v = (index_from << 1)| (index_to << 13 ) | BIT(0); + + pr_info("%s: from %d to %d\n", __func__, index_from, index_to); + mutex_lock(&priv->reg_mutex); + + // Write from-to and execute bit into control register + sw_w32(v, RTL931X_PIE_CLR_CTRL); + + // Wait until command has completed + do { + } while (sw_r32(RTL931X_PIE_CLR_CTRL) & BIT(0)); + + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static void rtl931x_pie_rule_rm(struct rtl838x_switch_priv *priv, struct pie_rule *pr) +{ + int idx = pr->id; + + rtl931x_pie_rule_del(priv, idx, idx); + clear_bit(idx, priv->pie_use_bm); +} + +static void rtl931x_pie_init(struct rtl838x_switch_priv *priv) +{ + int i; + u32 template_selectors; + + mutex_init(&priv->pie_mutex); + + pr_info("%s\n", __func__); + // Enable ACL lookup on all ports, including CPU_PORT + for (i = 0; i <= priv->cpu_port; i++) + sw_w32(1, RTL931X_ACL_PORT_LOOKUP_CTRL(i)); + + // Include IPG in metering + sw_w32_mask(0, 1, RTL931X_METER_GLB_CTRL); + + // Delete all present rules, block size is 128 on all SoC families + rtl931x_pie_rule_del(priv, 0, priv->n_pie_blocks * 128 - 1); + + // Assign first half blocks 0-7 to VACL phase, second half to IACL + // 3 bits are used for each block, values for PIE blocks are + // 6: Disabled, 0: VACL, 1: IACL, 2: EACL + // And for OpenFlow Flow blocks: 3: Ingress Flow table 0, + // 4: Ingress Flow Table 3, 5: Egress flow table 0 + for (i = 0; i < priv->n_pie_blocks; i++) { + int pos = (i % 10) * 3; + u32 r = RTL931X_PIE_BLK_PHASE_CTRL + 4 * (i / 10); + + if (i < priv->n_pie_blocks / 2) + sw_w32_mask(0x7 << pos, 0, r); + else + sw_w32_mask(0x7 << pos, 1 << pos, r); + } + + // Enable predefined templates 0, 1 for first quarter of all blocks + template_selectors = 0 | (1 << 4); + for (i = 0; i < priv->n_pie_blocks / 4; i++) + sw_w32(template_selectors, RTL931X_PIE_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 2, 3 for second quarter of all blocks + template_selectors = 2 | (3 << 4); + for (i = priv->n_pie_blocks / 4; i < priv->n_pie_blocks / 2; i++) + sw_w32(template_selectors, RTL931X_PIE_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 0, 1 for third quater of all blocks + template_selectors = 0 | (1 << 4); + for (i = priv->n_pie_blocks / 2; i < priv->n_pie_blocks * 3 / 4; i++) + sw_w32(template_selectors, RTL931X_PIE_BLK_TMPLTE_CTRL(i)); + + // Enable predefined templates 2, 3 for fourth quater of all blocks + template_selectors = 2 | (3 << 4); + for (i = priv->n_pie_blocks * 3 / 4; i < priv->n_pie_blocks; i++) + sw_w32(template_selectors, RTL931X_PIE_BLK_TMPLTE_CTRL(i)); + +} + +int rtl931x_l3_setup(struct rtl838x_switch_priv *priv) +{ + return 0; +} + +void rtl931x_vlan_port_keep_tag_set(int port, bool keep_outer, bool keep_inner) +{ + sw_w32(FIELD_PREP(RTL931X_VLAN_PORT_TAG_EGR_OTAG_STS_MASK, + keep_outer ? RTL931X_VLAN_PORT_TAG_STS_TAGGED : RTL931X_VLAN_PORT_TAG_STS_UNTAG) | + FIELD_PREP(RTL931X_VLAN_PORT_TAG_EGR_ITAG_STS_MASK, + keep_inner ? RTL931X_VLAN_PORT_TAG_STS_TAGGED : RTL931X_VLAN_PORT_TAG_STS_UNTAG), + RTL931X_VLAN_PORT_TAG_CTRL(port)); +} + +void rtl931x_vlan_port_pvidmode_set(int port, enum pbvlan_type type, enum pbvlan_mode mode) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0x3 << 12, mode << 12, RTL931X_VLAN_PORT_IGR_CTRL + (port << 2)); + else + sw_w32_mask(0x3 << 26, mode << 26, RTL931X_VLAN_PORT_IGR_CTRL + (port << 2)); +} + +void rtl931x_vlan_port_pvid_set(int port, enum pbvlan_type type, int pvid) +{ + if (type == PBVLAN_TYPE_INNER) + sw_w32_mask(0xfff, pvid, RTL931X_VLAN_PORT_IGR_CTRL + (port << 2)); + else + sw_w32_mask(0xfff << 14, pvid << 14, RTL931X_VLAN_PORT_IGR_CTRL + (port << 2)); +} + +static void rtl931x_set_igr_filter(int port, enum igr_filter state) +{ + sw_w32_mask(0x3 << ((port & 0xf)<<1), state << ((port & 0xf)<<1), + RTL931X_VLAN_PORT_IGR_FLTR + (((port >> 4) << 2))); +} + +static void rtl931x_set_egr_filter(int port, enum egr_filter state) +{ + sw_w32_mask(0x1 << (port % 0x20), state << (port % 0x20), + RTL931X_VLAN_PORT_EGR_FLTR + (((port >> 5) << 2))); +} + +void rtl931x_set_distribution_algorithm(int group, int algoidx, u32 algomsk) +{ + u32 l3shift = 0; + u32 newmask = 0; + + /* TODO: for now we set algoidx to 0 */ + algoidx=0; + + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SIP_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SIP_BIT; + } + if (algomsk & TRUNK_DISTRIBUTION_ALGO_DIP_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_DIP_BIT; + } + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SRC_L4PORT_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SRC_L4PORT_BIT; + } + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SRC_L4PORT_BIT) { + l3shift = 4; + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SRC_L4PORT_BIT; + } + + if (l3shift == 4) { + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_SMAC_BIT; + if (algomsk & TRUNK_DISTRIBUTION_ALGO_DMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L3_DMAC_BIT; + } else { + if (algomsk & TRUNK_DISTRIBUTION_ALGO_SMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L2_SMAC_BIT; + if (algomsk & TRUNK_DISTRIBUTION_ALGO_DMAC_BIT) + newmask |= TRUNK_DISTRIBUTION_ALGO_L2_DMAC_BIT; + } + + sw_w32(newmask << l3shift, RTL931X_TRK_HASH_CTRL + (algoidx << 2)); +} + +static void rtl931x_led_init(struct rtl838x_switch_priv *priv) +{ + int i, pos; + u32 v, set; + u64 pm_copper = 0, pm_fiber = 0; + u32 setlen; + const __be32 *led_set; + char set_name[9]; + struct device_node *node; + + pr_info("%s called\n", __func__); + node = of_find_compatible_node(NULL, NULL, "realtek,rtl9300-leds"); + if (!node) { + pr_info("%s No compatible LED node found\n", __func__); + return; + } + + for (i= 0; i < priv->cpu_port; i++) { + pos = (i << 1) % 32; + sw_w32_mask(0x3 << pos, 0, RTL931X_LED_PORT_FIB_SET_SEL_CTRL(i)); + sw_w32_mask(0x3 << pos, 0, RTL931X_LED_PORT_COPR_SET_SEL_CTRL(i)); + + if (!priv->ports[i].phy) + continue; + + v = 0x1; // Found on the EdgeCore, but we do not have any HW description + sw_w32_mask(0x3 << pos, v << pos, RTL931X_LED_PORT_NUM_CTRL(i)); + + if (priv->ports[i].phy_is_integrated) + pm_fiber |= BIT_ULL(i); + else + pm_copper |= BIT_ULL(i); + + set = priv->ports[i].led_set; + sw_w32_mask(0, set << pos, RTL931X_LED_PORT_COPR_SET_SEL_CTRL(i)); + sw_w32_mask(0, set << pos, RTL931X_LED_PORT_FIB_SET_SEL_CTRL(i)); + } + + for (i = 0; i < 4; i++) { + sprintf(set_name, "led_set%d", i); + pr_info(">%s<\n", set_name); + led_set = of_get_property(node, set_name, &setlen); + if (!led_set || setlen != 16) + break; + v = be32_to_cpup(led_set) << 16 | be32_to_cpup(led_set + 1); + sw_w32(v, RTL931X_LED_SET0_0_CTRL - 4 - i * 8); + v = be32_to_cpup(led_set + 2) << 16 | be32_to_cpup(led_set + 3); + sw_w32(v, RTL931X_LED_SET0_0_CTRL - i * 8); + } + + // Set LED mode to serial (0x1) + sw_w32_mask(0x3, 0x1, RTL931X_LED_GLB_CTRL); + + rtl839x_set_port_reg_le(pm_copper, RTL931X_LED_PORT_COPR_MASK_CTRL); + rtl839x_set_port_reg_le(pm_fiber, RTL931X_LED_PORT_FIB_MASK_CTRL); + rtl839x_set_port_reg_le(pm_copper | pm_fiber, RTL931X_LED_PORT_COMBO_MASK_CTRL); + + for (i = 0; i < 32; i++) + pr_info("%s %08x: %08x\n",__func__, 0xbb000600 + i * 4, sw_r32(0x0600 + i * 4)); + +} + +const struct rtl838x_reg rtl931x_reg = { + .mask_port_reg_be = rtl839x_mask_port_reg_be, + .set_port_reg_be = rtl839x_set_port_reg_be, + .get_port_reg_be = rtl839x_get_port_reg_be, + .mask_port_reg_le = rtl839x_mask_port_reg_le, + .set_port_reg_le = rtl839x_set_port_reg_le, + .get_port_reg_le = rtl839x_get_port_reg_le, + .stat_port_rst = RTL931X_STAT_PORT_RST, + .stat_rst = RTL931X_STAT_RST, + .stat_port_std_mib = 0, // Not defined + .traffic_enable = rtl931x_traffic_enable, + .traffic_disable = rtl931x_traffic_disable, + .traffic_get = rtl931x_traffic_get, + .traffic_set = rtl931x_traffic_set, + .l2_ctrl_0 = RTL931X_L2_CTRL, + .l2_ctrl_1 = RTL931X_L2_AGE_CTRL, + .l2_port_aging_out = RTL931X_L2_PORT_AGE_CTRL, + .set_ageing_time = rtl931x_set_ageing_time, + // .smi_poll_ctrl does not exist + .l2_tbl_flush_ctrl = RTL931X_L2_TBL_FLUSH_CTRL, + .exec_tbl0_cmd = rtl931x_exec_tbl0_cmd, + .exec_tbl1_cmd = rtl931x_exec_tbl1_cmd, + .tbl_access_data_0 = rtl931x_tbl_access_data_0, + .isr_glb_src = RTL931X_ISR_GLB_SRC, + .isr_port_link_sts_chg = RTL931X_ISR_PORT_LINK_STS_CHG, + .imr_port_link_sts_chg = RTL931X_IMR_PORT_LINK_STS_CHG, + // imr_glb does not exist on RTL931X + .vlan_tables_read = rtl931x_vlan_tables_read, + .vlan_set_tagged = rtl931x_vlan_set_tagged, + .vlan_set_untagged = rtl931x_vlan_set_untagged, + .vlan_profile_dump = rtl931x_vlan_profile_dump, + .vlan_profile_setup = rtl931x_vlan_profile_setup, + .vlan_fwd_on_inner = rtl931x_vlan_fwd_on_inner, + .stp_get = rtl931x_stp_get, + .stp_set = rtl931x_stp_set, + .mac_force_mode_ctrl = rtl931x_mac_force_mode_ctrl, + .mac_port_ctrl = rtl931x_mac_port_ctrl, + .l2_port_new_salrn = rtl931x_l2_port_new_salrn, + .l2_port_new_sa_fwd = rtl931x_l2_port_new_sa_fwd, + .mir_ctrl = RTL931X_MIR_CTRL, + .mir_dpm = RTL931X_MIR_DPM_CTRL, + .mir_spm = RTL931X_MIR_SPM_CTRL, + .mac_link_sts = RTL931X_MAC_LINK_STS, + .mac_link_dup_sts = RTL931X_MAC_LINK_DUP_STS, + .mac_link_spd_sts = rtl931x_mac_link_spd_sts, + .mac_rx_pause_sts = RTL931X_MAC_RX_PAUSE_STS, + .mac_tx_pause_sts = RTL931X_MAC_TX_PAUSE_STS, + .read_l2_entry_using_hash = rtl931x_read_l2_entry_using_hash, + .write_l2_entry_using_hash = rtl931x_write_l2_entry_using_hash, + .read_cam = rtl931x_read_cam, + .write_cam = rtl931x_write_cam, + .vlan_port_keep_tag_set = rtl931x_vlan_port_keep_tag_set, + .vlan_port_pvidmode_set = rtl931x_vlan_port_pvidmode_set, + .vlan_port_pvid_set = rtl931x_vlan_port_pvid_set, + .trk_mbr_ctr = rtl931x_trk_mbr_ctr, + .set_vlan_igr_filter = rtl931x_set_igr_filter, + .set_vlan_egr_filter = rtl931x_set_egr_filter, + .set_distribution_algorithm = rtl931x_set_distribution_algorithm, + .l2_hash_key = rtl931x_l2_hash_key, + .read_mcast_pmask = rtl931x_read_mcast_pmask, + .write_mcast_pmask = rtl931x_write_mcast_pmask, + .pie_init = rtl931x_pie_init, + .pie_rule_write = rtl931x_pie_rule_write, + .pie_rule_add = rtl931x_pie_rule_add, + .pie_rule_rm = rtl931x_pie_rule_rm, + .l2_learning_setup = rtl931x_l2_learning_setup, + .l3_setup = rtl931x_l3_setup, + .led_init = rtl931x_led_init, +}; + diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/tc.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/tc.c new file mode 100644 index 00000000000..d0a8ee8cfe4 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/tc.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include +#include "rtl83xx.h" +#include "rtl838x.h" + +/* + * Parse the flow rule for the matching conditions + */ +static int rtl83xx_parse_flow_rule(struct rtl838x_switch_priv *priv, + struct flow_rule *rule, struct rtl83xx_flow *flow) +{ + struct flow_dissector *dissector = rule->match.dissector; + + pr_debug("In %s\n", __func__); + /* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */ + if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 || + (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) { + pr_err("Cannot form TC key: used_keys = 0x%x\n", dissector->used_keys); + return -EOPNOTSUPP; + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_match_basic match; + + pr_debug("%s: BASIC\n", __func__); + flow_rule_match_basic(rule, &match); + if (match.key->n_proto == htons(ETH_P_ARP)) + flow->rule.frame_type = 0; + if (match.key->n_proto == htons(ETH_P_IP)) + flow->rule.frame_type = 2; + if (match.key->n_proto == htons(ETH_P_IPV6)) + flow->rule.frame_type = 3; + if ((match.key->n_proto == htons(ETH_P_ARP)) || flow->rule.frame_type) + flow->rule.frame_type_m = 3; + if (flow->rule.frame_type >= 2) { + if (match.key->ip_proto == IPPROTO_UDP) + flow->rule.frame_type_l4 = 0; + if (match.key->ip_proto == IPPROTO_TCP) + flow->rule.frame_type_l4 = 1; + if (match.key->ip_proto == IPPROTO_ICMP + || match.key->ip_proto ==IPPROTO_ICMPV6) + flow->rule.frame_type_l4 = 2; + if (match.key->ip_proto == IPPROTO_TCP) + flow->rule.frame_type_l4 = 3; + if ((match.key->ip_proto == IPPROTO_UDP) || flow->rule.frame_type_l4) + flow->rule.frame_type_l4_m = 7; + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; + + pr_debug("%s: ETH_ADDR\n", __func__); + flow_rule_match_eth_addrs(rule, &match); + ether_addr_copy(flow->rule.dmac, match.key->dst); + ether_addr_copy(flow->rule.dmac_m, match.mask->dst); + ether_addr_copy(flow->rule.smac, match.key->src); + ether_addr_copy(flow->rule.smac_m, match.mask->src); + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_match_vlan match; + + pr_debug("%s: VLAN\n", __func__); + flow_rule_match_vlan(rule, &match); + flow->rule.itag = match.key->vlan_id; + flow->rule.itag_m = match.mask->vlan_id; + // TODO: What about match.key->vlan_priority ? + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + struct flow_match_ipv4_addrs match; + + pr_debug("%s: IPV4\n", __func__); + flow_rule_match_ipv4_addrs(rule, &match); + flow->rule.is_ipv6 = false; + flow->rule.dip = match.key->dst; + flow->rule.dip_m = match.mask->dst; + flow->rule.sip = match.key->src; + flow->rule.sip_m = match.mask->src; + } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { + struct flow_match_ipv6_addrs match; + + pr_debug("%s: IPV6\n", __func__); + flow->rule.is_ipv6 = true; + flow_rule_match_ipv6_addrs(rule, &match); + flow->rule.dip6 = match.key->dst; + flow->rule.dip6_m = match.mask->dst; + flow->rule.sip6 = match.key->src; + flow->rule.sip6_m = match.mask->src; + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports match; + + pr_debug("%s: PORTS\n", __func__); + flow_rule_match_ports(rule, &match); + flow->rule.dport = match.key->dst; + flow->rule.dport_m = match.mask->dst; + flow->rule.sport = match.key->src; + flow->rule.sport_m = match.mask->src; + } + + // TODO: ICMP + return 0; +} + +static void rtl83xx_flow_bypass_all(struct rtl83xx_flow *flow) +{ + flow->rule.bypass_sel = true; + flow->rule.bypass_all = true; + flow->rule.bypass_igr_stp = true; + flow->rule.bypass_ibc_sc = true; +} + +static int rtl83xx_parse_fwd(struct rtl838x_switch_priv *priv, + const struct flow_action_entry *act, struct rtl83xx_flow *flow) +{ + struct net_device *dev = act->dev; + int port; + + port = rtl83xx_port_is_under(dev, priv); + if (port < 0) { + netdev_info(dev, "%s: not a DSA device.\n", __func__); + return -EINVAL; + } + + flow->rule.fwd_sel = true; + flow->rule.fwd_data = port; + pr_debug("Using port index: %d\n", port); + rtl83xx_flow_bypass_all(flow); + + return 0; +} + +static int rtl83xx_add_flow(struct rtl838x_switch_priv *priv, struct flow_cls_offload *f, + struct rtl83xx_flow *flow) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(f); + const struct flow_action_entry *act; + int i, err; + + pr_debug("%s\n", __func__); + + rtl83xx_parse_flow_rule(priv, rule, flow); + + flow_action_for_each(i, act, &rule->action) { + switch (act->id) { + case FLOW_ACTION_DROP: + pr_debug("%s: DROP\n", __func__); + flow->rule.drop = true; + rtl83xx_flow_bypass_all(flow); + return 0; + + case FLOW_ACTION_TRAP: + pr_debug("%s: TRAP\n", __func__); + flow->rule.fwd_data = priv->cpu_port; + flow->rule.fwd_act = PIE_ACT_REDIRECT_TO_PORT; + rtl83xx_flow_bypass_all(flow); + break; + + case FLOW_ACTION_MANGLE: + pr_err("%s: FLOW_ACTION_MANGLE not supported\n", __func__); + return -EOPNOTSUPP; + + case FLOW_ACTION_ADD: + pr_err("%s: FLOW_ACTION_ADD not supported\n", __func__); + return -EOPNOTSUPP; + + case FLOW_ACTION_VLAN_PUSH: + pr_debug("%s: VLAN_PUSH\n", __func__); +// TODO: act->vlan.proto + flow->rule.ivid_act = PIE_ACT_VID_ASSIGN; + flow->rule.ivid_sel = true; + flow->rule.ivid_data = htons(act->vlan.vid); + flow->rule.ovid_act = PIE_ACT_VID_ASSIGN; + flow->rule.ovid_sel = true; + flow->rule.ovid_data = htons(act->vlan.vid); + flow->rule.fwd_mod_to_cpu = true; + break; + + case FLOW_ACTION_VLAN_POP: + pr_debug("%s: VLAN_POP\n", __func__); + flow->rule.ivid_act = PIE_ACT_VID_ASSIGN; + flow->rule.ivid_data = 0; + flow->rule.ivid_sel = true; + flow->rule.ovid_act = PIE_ACT_VID_ASSIGN; + flow->rule.ovid_data = 0; + flow->rule.ovid_sel = true; + flow->rule.fwd_mod_to_cpu = true; + break; + + case FLOW_ACTION_CSUM: + pr_err("%s: FLOW_ACTION_CSUM not supported\n", __func__); + return -EOPNOTSUPP; + + case FLOW_ACTION_REDIRECT: + pr_debug("%s: REDIRECT\n", __func__); + err = rtl83xx_parse_fwd(priv, act, flow); + if (err) + return err; + flow->rule.fwd_act = PIE_ACT_REDIRECT_TO_PORT; + break; + + case FLOW_ACTION_MIRRED: + pr_debug("%s: MIRRED\n", __func__); + err = rtl83xx_parse_fwd(priv, act, flow); + if (err) + return err; + flow->rule.fwd_act = PIE_ACT_COPY_TO_PORT; + break; + + default: + pr_err("%s: Flow action not supported: %d\n", __func__, act->id); + return -EOPNOTSUPP; + } + } + + return 0; +} + +static const struct rhashtable_params tc_ht_params = { + .head_offset = offsetof(struct rtl83xx_flow, node), + .key_offset = offsetof(struct rtl83xx_flow, cookie), + .key_len = sizeof(((struct rtl83xx_flow *)0)->cookie), + .automatic_shrinking = true, +}; + +static int rtl83xx_configure_flower(struct rtl838x_switch_priv *priv, + struct flow_cls_offload *f) +{ + struct rtl83xx_flow *flow; + int err = 0; + + pr_debug("In %s\n", __func__); + + rcu_read_lock(); + pr_debug("Cookie %08lx\n", f->cookie); + flow = rhashtable_lookup(&priv->tc_ht, &f->cookie, tc_ht_params); + if (flow) { + pr_info("%s: Got flow\n", __func__); + err = -EEXIST; + goto rcu_unlock; + } + +rcu_unlock: + rcu_read_unlock(); + if (flow) + goto out; + pr_debug("%s: New flow\n", __func__); + + flow = kzalloc(sizeof(*flow), GFP_KERNEL); + if (!flow) { + err = -ENOMEM; + goto out; + } + + flow->cookie = f->cookie; + flow->priv = priv; + + err = rhashtable_insert_fast(&priv->tc_ht, &flow->node, tc_ht_params); + if (err) { + pr_err("Could not insert add new rule\n"); + goto out_free; + } + + rtl83xx_add_flow(priv, f, flow); // TODO: check error + + // Add log action to flow + flow->rule.packet_cntr = rtl83xx_packet_cntr_alloc(priv); + if (flow->rule.packet_cntr >= 0) { + pr_debug("Using packet counter %d\n", flow->rule.packet_cntr); + flow->rule.log_sel = true; + flow->rule.log_data = flow->rule.packet_cntr; + } + + err = priv->r->pie_rule_add(priv, &flow->rule); + return err; + +out_free: + kfree(flow); +out: + pr_err("%s: error %d\n", __func__, err); + return err; +} + +static int rtl83xx_delete_flower(struct rtl838x_switch_priv *priv, + struct flow_cls_offload * cls_flower) +{ + struct rtl83xx_flow *flow; + + pr_debug("In %s\n", __func__); + rcu_read_lock(); + flow = rhashtable_lookup_fast(&priv->tc_ht, &cls_flower->cookie, tc_ht_params); + if (!flow) { + rcu_read_unlock(); + return -EINVAL; + } + + priv->r->pie_rule_rm(priv, &flow->rule); + + rhashtable_remove_fast(&priv->tc_ht, &flow->node, tc_ht_params); + + kfree_rcu(flow, rcu_head); + + rcu_read_unlock(); + return 0; +} + +static int rtl83xx_stats_flower(struct rtl838x_switch_priv *priv, + struct flow_cls_offload * cls_flower) +{ + struct rtl83xx_flow *flow; + unsigned long lastused = 0; + int total_packets, new_packets; + + pr_debug("%s: \n", __func__); + flow = rhashtable_lookup_fast(&priv->tc_ht, &cls_flower->cookie, tc_ht_params); + if (!flow) + return -1; + + if (flow->rule.packet_cntr >= 0) { + total_packets = priv->r->packet_cntr_read(flow->rule.packet_cntr); + pr_debug("Total packets: %d\n", total_packets); + new_packets = total_packets - flow->rule.last_packet_cnt; + flow->rule.last_packet_cnt = total_packets; + } + + // TODO: We need a second PIE rule to count the bytes + flow_stats_update(&cls_flower->stats, 100 * new_packets, new_packets, 0, lastused, + FLOW_ACTION_HW_STATS_IMMEDIATE); + return 0; +} + +static int rtl83xx_setup_tc_cls_flower(struct rtl838x_switch_priv *priv, + struct flow_cls_offload *cls_flower) +{ + pr_debug("%s: %d\n", __func__, cls_flower->command); + switch (cls_flower->command) { + case FLOW_CLS_REPLACE: + return rtl83xx_configure_flower(priv, cls_flower); + case FLOW_CLS_DESTROY: + return rtl83xx_delete_flower(priv, cls_flower); + case FLOW_CLS_STATS: + return rtl83xx_stats_flower(priv, cls_flower); + default: + return -EOPNOTSUPP; + } +} + + +static int rtl83xx_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv) +{ + struct rtl838x_switch_priv *priv = cb_priv; + + switch (type) { + case TC_SETUP_CLSFLOWER: + pr_debug("%s: TC_SETUP_CLSFLOWER\n", __func__); + return rtl83xx_setup_tc_cls_flower(priv, type_data); + default: + return -EOPNOTSUPP; + } +} + +static LIST_HEAD(rtl83xx_block_cb_list); + +int rtl83xx_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data) +{ + struct rtl838x_switch_priv *priv; + struct flow_block_offload *f = type_data; + static bool first_time = true; + int err; + + pr_debug("%s: %d\n", __func__, type); + + if(!netdev_uses_dsa(dev)) { + pr_err("%s: no DSA\n", __func__); + return 0; + } + priv = dev->dsa_ptr->ds->priv; + + switch (type) { + case TC_SETUP_BLOCK: + if (first_time) { + first_time = false; + err = rhashtable_init(&priv->tc_ht, &tc_ht_params); + if (err) + pr_err("%s: Could not initialize hash table\n", __func__); + } + + f->unlocked_driver_cb = true; + return flow_block_cb_setup_simple(type_data, + &rtl83xx_block_cb_list, + rtl83xx_setup_tc_block_cb, + priv, priv, true); + default: + return -EOPNOTSUPP; + } + + return 0; +} diff --git a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c new file mode 100644 index 00000000000..166e49e139d --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.c @@ -0,0 +1,2588 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/net/ethernet/rtl838x_eth.c + * Copyright (C) 2020 B. Koblitz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rtl838x_eth.h" + +extern struct rtl83xx_soc_info soc_info; + +/* + * Maximum number of RX rings is 8 on RTL83XX and 32 on the 93XX + * The ring is assigned by switch based on packet/port priortity + * Maximum number of TX rings is 2, Ring 2 being the high priority + * ring on the RTL93xx SoCs. MAX_RXLEN gives the maximum length + * for an RX ring, MAX_ENTRIES the maximum number of entries + * available in total for all queues. + */ +#define MAX_RXRINGS 32 +#define MAX_RXLEN 300 +#define MAX_ENTRIES (300 * 8) +#define TXRINGS 2 +#define TXRINGLEN 160 +#define NOTIFY_EVENTS 10 +#define NOTIFY_BLOCKS 10 +#define TX_EN 0x8 +#define RX_EN 0x4 +#define TX_EN_93XX 0x20 +#define RX_EN_93XX 0x10 +#define TX_DO 0x2 +#define WRAP 0x2 +#define MAX_PORTS 57 +#define MAX_SMI_BUSSES 4 + +#define RING_BUFFER 1600 + +struct p_hdr { + uint8_t *buf; + uint16_t reserved; + uint16_t size; /* buffer size */ + uint16_t offset; + uint16_t len; /* pkt len */ + /* cpu_tag[0] is a reserved uint16_t on RTL83xx */ + uint16_t cpu_tag[10]; +} __packed __aligned(1); + +struct n_event { + uint32_t type:2; + uint32_t fidVid:12; + uint64_t mac:48; + uint32_t slp:6; + uint32_t valid:1; + uint32_t reserved:27; +} __packed __aligned(1); + +struct ring_b { + uint32_t rx_r[MAX_RXRINGS][MAX_RXLEN]; + uint32_t tx_r[TXRINGS][TXRINGLEN]; + struct p_hdr rx_header[MAX_RXRINGS][MAX_RXLEN]; + struct p_hdr tx_header[TXRINGS][TXRINGLEN]; + uint32_t c_rx[MAX_RXRINGS]; + uint32_t c_tx[TXRINGS]; + uint8_t tx_space[TXRINGS * TXRINGLEN * RING_BUFFER]; + uint8_t *rx_space; +}; + +struct notify_block { + struct n_event events[NOTIFY_EVENTS]; +}; + +struct notify_b { + struct notify_block blocks[NOTIFY_BLOCKS]; + u32 reserved1[8]; + u32 ring[NOTIFY_BLOCKS]; + u32 reserved2[8]; +}; + +static void rtl838x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio) +{ + // cpu_tag[0] is reserved on the RTL83XX SoCs + h->cpu_tag[1] = 0x0401; // BIT 10: RTL8380_CPU_TAG, BIT0: L2LEARNING on + h->cpu_tag[2] = 0x0200; // Set only AS_DPM, to enable DPM settings below + h->cpu_tag[3] = 0x0000; + h->cpu_tag[4] = BIT(dest_port) >> 16; + h->cpu_tag[5] = BIT(dest_port) & 0xffff; + + /* Set internal priority (PRI) and enable (AS_PRI) */ + if (prio >= 0) + h->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 12; +} + +static void rtl839x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio) +{ + // cpu_tag[0] is reserved on the RTL83XX SoCs + h->cpu_tag[1] = 0x0100; // RTL8390_CPU_TAG marker + h->cpu_tag[2] = BIT(4) | BIT(7); /* AS_DPM (4) and L2LEARNING (7) flags */ + h->cpu_tag[3] = h->cpu_tag[4] = h->cpu_tag[5] = 0; + // h->cpu_tag[1] |= BIT(1) | BIT(0); // Bypass filter 1/2 + if (dest_port >= 32) { + dest_port -= 32; + h->cpu_tag[2] |= (BIT(dest_port) >> 16) & 0xf; + h->cpu_tag[3] = BIT(dest_port) & 0xffff; + } else { + h->cpu_tag[4] = BIT(dest_port) >> 16; + h->cpu_tag[5] = BIT(dest_port) & 0xffff; + } + + /* Set internal priority (PRI) and enable (AS_PRI) */ + if (prio >= 0) + h->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 8; +} + +static void rtl930x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio) +{ + h->cpu_tag[0] = 0x8000; // CPU tag marker + h->cpu_tag[1] = h->cpu_tag[2] = 0; + h->cpu_tag[3] = 0; + h->cpu_tag[4] = 0; + h->cpu_tag[5] = 0; + h->cpu_tag[6] = BIT(dest_port) >> 16; + h->cpu_tag[7] = BIT(dest_port) & 0xffff; + + /* Enable (AS_QID) and set priority queue (QID) */ + if (prio >= 0) + h->cpu_tag[2] = (BIT(5) | (prio & 0x1f)) << 8; +} + +static void rtl931x_create_tx_header(struct p_hdr *h, unsigned int dest_port, int prio) +{ + h->cpu_tag[0] = 0x8000; // CPU tag marker + h->cpu_tag[1] = h->cpu_tag[2] = 0; + h->cpu_tag[3] = 0; + h->cpu_tag[4] = h->cpu_tag[5] = h->cpu_tag[6] = h->cpu_tag[7] = 0; + if (dest_port >= 32) { + dest_port -= 32; + h->cpu_tag[4] = BIT(dest_port) >> 16; + h->cpu_tag[5] = BIT(dest_port) & 0xffff; + } else { + h->cpu_tag[6] = BIT(dest_port) >> 16; + h->cpu_tag[7] = BIT(dest_port) & 0xffff; + } + + /* Enable (AS_QID) and set priority queue (QID) */ + if (prio >= 0) + h->cpu_tag[2] = (BIT(5) | (prio & 0x1f)) << 8; +} + +static void rtl93xx_header_vlan_set(struct p_hdr *h, int vlan) +{ + h->cpu_tag[2] |= BIT(4); // Enable VLAN forwarding offload + h->cpu_tag[2] |= (vlan >> 8) & 0xf; + h->cpu_tag[3] |= (vlan & 0xff) << 8; +} + +struct rtl838x_rx_q { + int id; + struct rtl838x_eth_priv *priv; + struct napi_struct napi; +}; + +struct rtl838x_eth_priv { + struct net_device *netdev; + struct platform_device *pdev; + void *membase; + spinlock_t lock; + struct mii_bus *mii_bus; + struct rtl838x_rx_q rx_qs[MAX_RXRINGS]; + struct phylink *phylink; + struct phylink_config phylink_config; + u16 id; + u16 family_id; + const struct rtl838x_eth_reg *r; + u8 cpu_port; + u32 lastEvent; + u16 rxrings; + u16 rxringlen; + u8 smi_bus[MAX_PORTS]; + u8 smi_addr[MAX_PORTS]; + u32 sds_id[MAX_PORTS]; + bool smi_bus_isc45[MAX_SMI_BUSSES]; + bool phy_is_internal[MAX_PORTS]; + phy_interface_t interfaces[MAX_PORTS]; +}; + +extern int rtl838x_phy_init(struct rtl838x_eth_priv *priv); +extern int rtl838x_read_sds_phy(int phy_addr, int phy_reg); +extern int rtl839x_read_sds_phy(int phy_addr, int phy_reg); +extern int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v); +extern int rtl930x_read_sds_phy(int phy_addr, int page, int phy_reg); +extern int rtl930x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v); +extern int rtl931x_read_sds_phy(int phy_addr, int page, int phy_reg); +extern int rtl931x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v); +extern int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +extern int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val); +extern int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +extern int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val); + +/* + * On the RTL93XX, the RTL93XX_DMA_IF_RX_RING_CNTR track the fill level of + * the rings. Writing x into these registers substracts x from its content. + * When the content reaches the ring size, the ASIC no longer adds + * packets to this receive queue. + */ +void rtl838x_update_cntr(int r, int released) +{ + // This feature is not available on RTL838x SoCs +} + +void rtl839x_update_cntr(int r, int released) +{ + // This feature is not available on RTL839x SoCs +} + +void rtl930x_update_cntr(int r, int released) +{ + int pos = (r % 3) * 10; + u32 reg = RTL930X_DMA_IF_RX_RING_CNTR + ((r / 3) << 2); + u32 v = sw_r32(reg); + + v = (v >> pos) & 0x3ff; + pr_debug("RX: Work done %d, old value: %d, pos %d, reg %04x\n", released, v, pos, reg); + sw_w32_mask(0x3ff << pos, released << pos, reg); + sw_w32(v, reg); +} + +void rtl931x_update_cntr(int r, int released) +{ + int pos = (r % 3) * 10; + u32 reg = RTL931X_DMA_IF_RX_RING_CNTR + ((r / 3) << 2); + u32 v = sw_r32(reg); + + v = (v >> pos) & 0x3ff; + sw_w32_mask(0x3ff << pos, released << pos, reg); + sw_w32(v, reg); +} + +struct dsa_tag { + u8 reason; + u8 queue; + u16 port; + u8 l2_offloaded; + u8 prio; + bool crc_error; +}; + +bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + /* cpu_tag[0] is reserved. Fields are off-by-one */ + t->reason = h->cpu_tag[4] & 0xf; + t->queue = (h->cpu_tag[1] & 0xe0) >> 5; + t->port = h->cpu_tag[1] & 0x1f; + t->crc_error = t->reason == 13; + + pr_debug("Reason: %d\n", t->reason); + if (t->reason != 6) // NIC_RX_REASON_SPECIAL_TRAP + t->l2_offloaded = 1; + else + t->l2_offloaded = 0; + + return t->l2_offloaded; +} + +bool rtl839x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + /* cpu_tag[0] is reserved. Fields are off-by-one */ + t->reason = h->cpu_tag[5] & 0x1f; + t->queue = (h->cpu_tag[4] & 0xe000) >> 13; + t->port = h->cpu_tag[1] & 0x3f; + t->crc_error = h->cpu_tag[4] & BIT(6); + + pr_debug("Reason: %d\n", t->reason); + if ((t->reason >= 7 && t->reason <= 13) || // NIC_RX_REASON_RMA + (t->reason >= 23 && t->reason <= 25)) // NIC_RX_REASON_SPECIAL_TRAP + t->l2_offloaded = 0; + else + t->l2_offloaded = 1; + + return t->l2_offloaded; +} + +bool rtl930x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + t->reason = h->cpu_tag[7] & 0x3f; + t->queue = (h->cpu_tag[2] >> 11) & 0x1f; + t->port = (h->cpu_tag[0] >> 8) & 0x1f; + t->crc_error = h->cpu_tag[1] & BIT(6); + + pr_debug("Reason %d, port %d, queue %d\n", t->reason, t->port, t->queue); + if (t->reason >= 19 && t->reason <= 27) + t->l2_offloaded = 0; + else + t->l2_offloaded = 1; + + return t->l2_offloaded; +} + +bool rtl931x_decode_tag(struct p_hdr *h, struct dsa_tag *t) +{ + t->reason = h->cpu_tag[7] & 0x3f; + t->queue = (h->cpu_tag[2] >> 11) & 0x1f; + t->port = (h->cpu_tag[0] >> 8) & 0x3f; + t->crc_error = h->cpu_tag[1] & BIT(6); + + if (t->reason != 63) + pr_info("%s: Reason %d, port %d, queue %d\n", __func__, t->reason, t->port, t->queue); + if (t->reason >= 19 && t->reason <= 27) // NIC_RX_REASON_RMA + t->l2_offloaded = 0; + else + t->l2_offloaded = 1; + + return t->l2_offloaded; +} + +/* + * Discard the RX ring-buffers, called as part of the net-ISR + * when the buffer runs over + */ +static void rtl838x_rb_cleanup(struct rtl838x_eth_priv *priv, int status) +{ + int r; + u32 *last; + struct p_hdr *h; + struct ring_b *ring = priv->membase; + + for (r = 0; r < priv->rxrings; r++) { + pr_debug("In %s working on r: %d\n", __func__, r); + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4)); + do { + if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) + break; + pr_debug("Got something: %d\n", ring->c_rx[r]); + h = &ring->rx_header[r][ring->c_rx[r]]; + memset(h, 0, sizeof(struct p_hdr)); + h->buf = (u8 *)KSEG1ADDR(ring->rx_space + + r * priv->rxringlen * RING_BUFFER + + ring->c_rx[r] * RING_BUFFER); + h->size = RING_BUFFER; + /* make sure the header is visible to the ASIC */ + mb(); + + ring->rx_r[r][ring->c_rx[r]] = KSEG1ADDR(h) | 0x1 + | (ring->c_rx[r] == (priv->rxringlen - 1) ? WRAP : 0x1); + ring->c_rx[r] = (ring->c_rx[r] + 1) % priv->rxringlen; + } while (&ring->rx_r[r][ring->c_rx[r]] != last); + } +} + +struct fdb_update_work { + struct work_struct work; + struct net_device *ndev; + u64 macs[NOTIFY_EVENTS + 1]; +}; + +void rtl838x_fdb_sync(struct work_struct *work) +{ + const struct fdb_update_work *uw = + container_of(work, struct fdb_update_work, work); + struct switchdev_notifier_fdb_info info; + u8 addr[ETH_ALEN]; + int i = 0; + int action; + + while (uw->macs[i]) { + action = (uw->macs[i] & (1ULL << 63)) ? SWITCHDEV_FDB_ADD_TO_BRIDGE + : SWITCHDEV_FDB_DEL_TO_BRIDGE; + u64_to_ether_addr(uw->macs[i] & 0xffffffffffffULL, addr); + info.addr = &addr[0]; + info.vid = 0; + info.offloaded = 1; + pr_debug("FDB entry %d: %llx, action %d\n", i, uw->macs[0], action); + call_switchdev_notifiers(action, uw->ndev, &info.info, NULL); + i++; + } + kfree(work); +} + +static void rtl839x_l2_notification_handler(struct rtl838x_eth_priv *priv) +{ + struct notify_b *nb = priv->membase + sizeof(struct ring_b); + u32 e = priv->lastEvent; + struct n_event *event; + int i; + u64 mac; + struct fdb_update_work *w; + + while (!(nb->ring[e] & 1)) { + w = kzalloc(sizeof(*w), GFP_ATOMIC); + if (!w) { + pr_err("Out of memory: %s", __func__); + return; + } + INIT_WORK(&w->work, rtl838x_fdb_sync); + + for (i = 0; i < NOTIFY_EVENTS; i++) { + event = &nb->blocks[e].events[i]; + if (!event->valid) + continue; + mac = event->mac; + if (event->type) + mac |= 1ULL << 63; + w->ndev = priv->netdev; + w->macs[i] = mac; + } + + /* Hand the ring entry back to the switch */ + nb->ring[e] = nb->ring[e] | 1; + e = (e + 1) % NOTIFY_BLOCKS; + + w->macs[i] = 0ULL; + schedule_work(&w->work); + } + priv->lastEvent = e; +} + +static irqreturn_t rtl83xx_net_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct rtl838x_eth_priv *priv = netdev_priv(dev); + u32 status = sw_r32(priv->r->dma_if_intr_sts); + int i; + + pr_debug("IRQ: %08x\n", status); + + /* Ignore TX interrupt */ + if ((status & 0xf0000)) { + /* Clear ISR */ + sw_w32(0x000f0000, priv->r->dma_if_intr_sts); + } + + /* RX interrupt */ + if (status & 0x0ff00) { + /* ACK and disable RX interrupt for this ring */ + sw_w32_mask(0xff00 & status, 0, priv->r->dma_if_intr_msk); + sw_w32(0x0000ff00 & status, priv->r->dma_if_intr_sts); + for (i = 0; i < priv->rxrings; i++) { + if (status & BIT(i + 8)) { + pr_debug("Scheduling queue: %d\n", i); + napi_schedule(&priv->rx_qs[i].napi); + } + } + } + + /* RX buffer overrun */ + if (status & 0x000ff) { + pr_debug("RX buffer overrun: status %x, mask: %x\n", + status, sw_r32(priv->r->dma_if_intr_msk)); + sw_w32(status, priv->r->dma_if_intr_sts); + rtl838x_rb_cleanup(priv, status & 0xff); + } + + if (priv->family_id == RTL8390_FAMILY_ID && status & 0x00100000) { + sw_w32(0x00100000, priv->r->dma_if_intr_sts); + rtl839x_l2_notification_handler(priv); + } + + if (priv->family_id == RTL8390_FAMILY_ID && status & 0x00200000) { + sw_w32(0x00200000, priv->r->dma_if_intr_sts); + rtl839x_l2_notification_handler(priv); + } + + if (priv->family_id == RTL8390_FAMILY_ID && status & 0x00400000) { + sw_w32(0x00400000, priv->r->dma_if_intr_sts); + rtl839x_l2_notification_handler(priv); + } + + return IRQ_HANDLED; +} + +static irqreturn_t rtl93xx_net_irq(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct rtl838x_eth_priv *priv = netdev_priv(dev); + u32 status_rx_r = sw_r32(priv->r->dma_if_intr_rx_runout_sts); + u32 status_rx = sw_r32(priv->r->dma_if_intr_rx_done_sts); + u32 status_tx = sw_r32(priv->r->dma_if_intr_tx_done_sts); + int i; + + pr_debug("In %s, status_tx: %08x, status_rx: %08x, status_rx_r: %08x\n", + __func__, status_tx, status_rx, status_rx_r); + + /* Ignore TX interrupt */ + if (status_tx) { + /* Clear ISR */ + pr_debug("TX done\n"); + sw_w32(status_tx, priv->r->dma_if_intr_tx_done_sts); + } + + /* RX interrupt */ + if (status_rx) { + pr_debug("RX IRQ\n"); + /* ACK and disable RX interrupt for given rings */ + sw_w32(status_rx, priv->r->dma_if_intr_rx_done_sts); + sw_w32_mask(status_rx, 0, priv->r->dma_if_intr_rx_done_msk); + for (i = 0; i < priv->rxrings; i++) { + if (status_rx & BIT(i)) { + pr_debug("Scheduling queue: %d\n", i); + napi_schedule(&priv->rx_qs[i].napi); + } + } + } + + /* RX buffer overrun */ + if (status_rx_r) { + pr_debug("RX buffer overrun: status %x, mask: %x\n", + status_rx_r, sw_r32(priv->r->dma_if_intr_rx_runout_msk)); + sw_w32(status_rx_r, priv->r->dma_if_intr_rx_runout_sts); + rtl838x_rb_cleanup(priv, status_rx_r); + } + + return IRQ_HANDLED; +} + +static const struct rtl838x_eth_reg rtl838x_reg = { + .net_irq = rtl83xx_net_irq, + .mac_port_ctrl = rtl838x_mac_port_ctrl, + .dma_if_intr_sts = RTL838X_DMA_IF_INTR_STS, + .dma_if_intr_msk = RTL838X_DMA_IF_INTR_MSK, + .dma_if_ctrl = RTL838X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL838X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL838X_DMA_RX_BASE, + .dma_tx_base = RTL838X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl838x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl838x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL838X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0, + .get_mac_link_sts = rtl838x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl838x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl838x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl838x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl838x_get_mac_tx_pause_sts, + .mac = RTL838X_MAC, + .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl838x_update_cntr, + .create_tx_header = rtl838x_create_tx_header, + .decode_tag = rtl838x_decode_tag, +}; + +static const struct rtl838x_eth_reg rtl839x_reg = { + .net_irq = rtl83xx_net_irq, + .mac_port_ctrl = rtl839x_mac_port_ctrl, + .dma_if_intr_sts = RTL839X_DMA_IF_INTR_STS, + .dma_if_intr_msk = RTL839X_DMA_IF_INTR_MSK, + .dma_if_ctrl = RTL839X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL839X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL839X_DMA_RX_BASE, + .dma_tx_base = RTL839X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl839x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl839x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL839X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL839X_RST_GLB_CTRL, + .get_mac_link_sts = rtl839x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl839x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl839x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl839x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl839x_get_mac_tx_pause_sts, + .mac = RTL839X_MAC, + .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl839x_update_cntr, + .create_tx_header = rtl839x_create_tx_header, + .decode_tag = rtl839x_decode_tag, +}; + +static const struct rtl838x_eth_reg rtl930x_reg = { + .net_irq = rtl93xx_net_irq, + .mac_port_ctrl = rtl930x_mac_port_ctrl, + .dma_if_intr_rx_runout_sts = RTL930X_DMA_IF_INTR_RX_RUNOUT_STS, + .dma_if_intr_rx_done_sts = RTL930X_DMA_IF_INTR_RX_DONE_STS, + .dma_if_intr_tx_done_sts = RTL930X_DMA_IF_INTR_TX_DONE_STS, + .dma_if_intr_rx_runout_msk = RTL930X_DMA_IF_INTR_RX_RUNOUT_MSK, + .dma_if_intr_rx_done_msk = RTL930X_DMA_IF_INTR_RX_DONE_MSK, + .dma_if_intr_tx_done_msk = RTL930X_DMA_IF_INTR_TX_DONE_MSK, + .l2_ntfy_if_intr_sts = RTL930X_L2_NTFY_IF_INTR_STS, + .l2_ntfy_if_intr_msk = RTL930X_L2_NTFY_IF_INTR_MSK, + .dma_if_ctrl = RTL930X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL930X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL930X_DMA_RX_BASE, + .dma_tx_base = RTL930X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl930x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl930x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL930X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL930X_RST_GLB_CTRL_0, + .get_mac_link_sts = rtl930x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl930x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl930x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl930x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl930x_get_mac_tx_pause_sts, + .mac = RTL930X_MAC_L2_ADDR_CTRL, + .l2_tbl_flush_ctrl = RTL930X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl930x_update_cntr, + .create_tx_header = rtl930x_create_tx_header, + .decode_tag = rtl930x_decode_tag, +}; + +static const struct rtl838x_eth_reg rtl931x_reg = { + .net_irq = rtl93xx_net_irq, + .mac_port_ctrl = rtl931x_mac_port_ctrl, + .dma_if_intr_rx_runout_sts = RTL931X_DMA_IF_INTR_RX_RUNOUT_STS, + .dma_if_intr_rx_done_sts = RTL931X_DMA_IF_INTR_RX_DONE_STS, + .dma_if_intr_tx_done_sts = RTL931X_DMA_IF_INTR_TX_DONE_STS, + .dma_if_intr_rx_runout_msk = RTL931X_DMA_IF_INTR_RX_RUNOUT_MSK, + .dma_if_intr_rx_done_msk = RTL931X_DMA_IF_INTR_RX_DONE_MSK, + .dma_if_intr_tx_done_msk = RTL931X_DMA_IF_INTR_TX_DONE_MSK, + .l2_ntfy_if_intr_sts = RTL931X_L2_NTFY_IF_INTR_STS, + .l2_ntfy_if_intr_msk = RTL931X_L2_NTFY_IF_INTR_MSK, + .dma_if_ctrl = RTL931X_DMA_IF_CTRL, + .mac_force_mode_ctrl = RTL931X_MAC_FORCE_MODE_CTRL, + .dma_rx_base = RTL931X_DMA_RX_BASE, + .dma_tx_base = RTL931X_DMA_TX_BASE, + .dma_if_rx_ring_size = rtl931x_dma_if_rx_ring_size, + .dma_if_rx_ring_cntr = rtl931x_dma_if_rx_ring_cntr, + .dma_if_rx_cur = RTL931X_DMA_IF_RX_CUR, + .rst_glb_ctrl = RTL931X_RST_GLB_CTRL, + .get_mac_link_sts = rtl931x_get_mac_link_sts, + .get_mac_link_dup_sts = rtl931x_get_mac_link_dup_sts, + .get_mac_link_spd_sts = rtl931x_get_mac_link_spd_sts, + .get_mac_rx_pause_sts = rtl931x_get_mac_rx_pause_sts, + .get_mac_tx_pause_sts = rtl931x_get_mac_tx_pause_sts, + .mac = RTL931X_MAC_L2_ADDR_CTRL, + .l2_tbl_flush_ctrl = RTL931X_L2_TBL_FLUSH_CTRL, + .update_cntr = rtl931x_update_cntr, + .create_tx_header = rtl931x_create_tx_header, + .decode_tag = rtl931x_decode_tag, +}; + +static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv) +{ + u32 int_saved, nbuf; + u32 reset_mask; + int i, pos; + + pr_info("RESETTING %x, CPU_PORT %d\n", priv->family_id, priv->cpu_port); + sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(priv->cpu_port)); + mdelay(100); + + /* Disable and clear interrupts */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) { + sw_w32(0x00000000, priv->r->dma_if_intr_rx_runout_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_rx_done_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_tx_done_msk); + sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_sts); + } else { + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_sts); + } + + if (priv->family_id == RTL8390_FAMILY_ID) { + /* Preserve L2 notification and NBUF settings */ + int_saved = sw_r32(priv->r->dma_if_intr_msk); + nbuf = sw_r32(RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL); + + /* Disable link change interrupt on RTL839x */ + sw_w32(0, RTL839X_IMR_PORT_LINK_STS_CHG); + sw_w32(0, RTL839X_IMR_PORT_LINK_STS_CHG + 4); + + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_sts); + } + + /* Reset NIC (SW_NIC_RST) and queues (SW_Q_RST) */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) + reset_mask = 0x6; + else + reset_mask = 0xc; + + sw_w32(reset_mask, priv->r->rst_glb_ctrl); + + do { /* Wait for reset of NIC and Queues done */ + udelay(20); + } while (sw_r32(priv->r->rst_glb_ctrl) & reset_mask); + mdelay(100); + + /* Setup Head of Line */ + if (priv->family_id == RTL8380_FAMILY_ID) + sw_w32(0, RTL838X_DMA_IF_RX_RING_SIZE); // Disabled on RTL8380 + if (priv->family_id == RTL8390_FAMILY_ID) + sw_w32(0xffffffff, RTL839X_DMA_IF_RX_RING_CNTR); + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) { + for (i = 0; i < priv->rxrings; i++) { + pos = (i % 3) * 10; + sw_w32_mask(0x3ff << pos, 0, priv->r->dma_if_rx_ring_size(i)); + sw_w32_mask(0x3ff << pos, priv->rxringlen, + priv->r->dma_if_rx_ring_cntr(i)); + } + } + + /* Re-enable link change interrupt */ + if (priv->family_id == RTL8390_FAMILY_ID) { + sw_w32(0xffffffff, RTL839X_ISR_PORT_LINK_STS_CHG); + sw_w32(0xffffffff, RTL839X_ISR_PORT_LINK_STS_CHG + 4); + sw_w32(0xffffffff, RTL839X_IMR_PORT_LINK_STS_CHG); + sw_w32(0xffffffff, RTL839X_IMR_PORT_LINK_STS_CHG + 4); + + /* Restore notification settings: on RTL838x these bits are null */ + sw_w32_mask(7 << 20, int_saved & (7 << 20), priv->r->dma_if_intr_msk); + sw_w32(nbuf, RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL); + } +} + +static void rtl838x_hw_ring_setup(struct rtl838x_eth_priv *priv) +{ + int i; + struct ring_b *ring = priv->membase; + + for (i = 0; i < priv->rxrings; i++) + sw_w32(KSEG1ADDR(&ring->rx_r[i]), priv->r->dma_rx_base + i * 4); + + for (i = 0; i < TXRINGS; i++) + sw_w32(KSEG1ADDR(&ring->tx_r[i]), priv->r->dma_tx_base + i * 4); +} + +static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv) +{ + /* Disable Head of Line features for all RX rings */ + sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); + + /* Truncate RX buffer to 0x640 (1600) bytes, pad TX */ + sw_w32(0x06400020, priv->r->dma_if_ctrl); + + /* Enable RX done, RX overflow and TX done interrupts */ + sw_w32(0xfffff, priv->r->dma_if_intr_msk); + + /* Enable DMA, engine expects empty FCS field */ + sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); + + /* Restart TX/RX to CPU port */ + sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port)); + /* Set Speed, duplex, flow control + * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL + * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN + * | MEDIA_SEL + */ + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); + + /* Enable CRC checks on CPU-port */ + sw_w32_mask(0, BIT(3), priv->r->mac_port_ctrl(priv->cpu_port)); +} + +static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv) +{ + /* Setup CPU-Port: RX Buffer */ + sw_w32(0x0000c808, priv->r->dma_if_ctrl); + + /* Enable Notify, RX done, RX overflow and TX done interrupts */ + sw_w32(0x007fffff, priv->r->dma_if_intr_msk); // Notify IRQ! + + /* Enable DMA */ + sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl); + + /* Restart TX/RX to CPU port, enable CRC checking */ + sw_w32_mask(0x0, 0x3 | BIT(3), priv->r->mac_port_ctrl(priv->cpu_port)); + + /* CPU port joins Lookup Miss Flooding Portmask */ + // TODO: The code below should also work for the RTL838x + sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL); + sw_w32_mask(0, 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0)); + sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL); + + /* Force CPU port link up */ + sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); +} + +static void rtl93xx_hw_en_rxtx(struct rtl838x_eth_priv *priv) +{ + int i, pos; + u32 v; + + /* Setup CPU-Port: RX Buffer truncated at 1600 Bytes */ + sw_w32(0x06400040, priv->r->dma_if_ctrl); + + for (i = 0; i < priv->rxrings; i++) { + pos = (i % 3) * 10; + sw_w32_mask(0x3ff << pos, priv->rxringlen << pos, priv->r->dma_if_rx_ring_size(i)); + + // Some SoCs have issues with missing underflow protection + v = (sw_r32(priv->r->dma_if_rx_ring_cntr(i)) >> pos) & 0x3ff; + sw_w32_mask(0x3ff << pos, v, priv->r->dma_if_rx_ring_cntr(i)); + } + + /* Enable Notify, RX done, RX overflow and TX done interrupts */ + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_msk); + sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_msk); + + /* Enable DMA */ + sw_w32_mask(0, RX_EN_93XX | TX_EN_93XX, priv->r->dma_if_ctrl); + + /* Restart TX/RX to CPU port, enable CRC checking */ + sw_w32_mask(0x0, 0x3 | BIT(4), priv->r->mac_port_ctrl(priv->cpu_port)); + + if (priv->family_id == RTL9300_FAMILY_ID) + sw_w32_mask(0, BIT(priv->cpu_port), RTL930X_L2_UNKN_UC_FLD_PMSK); + else + sw_w32_mask(0, BIT(priv->cpu_port), RTL931X_L2_UNKN_UC_FLD_PMSK); + + if (priv->family_id == RTL9300_FAMILY_ID) + sw_w32(0x217, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); + else + sw_w32(0x2a1d, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); +} + +static void rtl838x_setup_ring_buffer(struct rtl838x_eth_priv *priv, struct ring_b *ring) +{ + int i, j; + + struct p_hdr *h; + + for (i = 0; i < priv->rxrings; i++) { + for (j = 0; j < priv->rxringlen; j++) { + h = &ring->rx_header[i][j]; + memset(h, 0, sizeof(struct p_hdr)); + h->buf = (u8 *)KSEG1ADDR(ring->rx_space + + i * priv->rxringlen * RING_BUFFER + + j * RING_BUFFER); + h->size = RING_BUFFER; + /* All rings owned by switch, last one wraps */ + ring->rx_r[i][j] = KSEG1ADDR(h) | 1 + | (j == (priv->rxringlen - 1) ? WRAP : 0); + } + ring->c_rx[i] = 0; + } + + for (i = 0; i < TXRINGS; i++) { + for (j = 0; j < TXRINGLEN; j++) { + h = &ring->tx_header[i][j]; + memset(h, 0, sizeof(struct p_hdr)); + h->buf = (u8 *)KSEG1ADDR(ring->tx_space + + i * TXRINGLEN * RING_BUFFER + + j * RING_BUFFER); + h->size = RING_BUFFER; + ring->tx_r[i][j] = KSEG1ADDR(&ring->tx_header[i][j]); + } + /* Last header is wrapping around */ + ring->tx_r[i][j-1] |= WRAP; + ring->c_tx[i] = 0; + } +} + +static void rtl839x_setup_notify_ring_buffer(struct rtl838x_eth_priv *priv) +{ + int i; + struct notify_b *b = priv->membase + sizeof(struct ring_b); + + for (i = 0; i < NOTIFY_BLOCKS; i++) + b->ring[i] = KSEG1ADDR(&b->blocks[i]) | 1 | (i == (NOTIFY_BLOCKS - 1) ? WRAP : 0); + + sw_w32((u32) b->ring, RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL); + sw_w32_mask(0x3ff << 2, 100 << 2, RTL839X_L2_NOTIFICATION_CTRL); + + /* Setup notification events */ + sw_w32_mask(0, 1 << 14, RTL839X_L2_CTRL_0); // RTL8390_L2_CTRL_0_FLUSH_NOTIFY_EN + sw_w32_mask(0, 1 << 12, RTL839X_L2_NOTIFICATION_CTRL); // SUSPEND_NOTIFICATION_EN + + /* Enable Notification */ + sw_w32_mask(0, 1 << 0, RTL839X_L2_NOTIFICATION_CTRL); + priv->lastEvent = 0; +} + +static int rtl838x_eth_open(struct net_device *ndev) +{ + unsigned long flags; + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + struct ring_b *ring = priv->membase; + int i; + + pr_debug("%s called: RX rings %d(length %d), TX rings %d(length %d)\n", + __func__, priv->rxrings, priv->rxringlen, TXRINGS, TXRINGLEN); + + spin_lock_irqsave(&priv->lock, flags); + rtl838x_hw_reset(priv); + rtl838x_setup_ring_buffer(priv, ring); + if (priv->family_id == RTL8390_FAMILY_ID) { + rtl839x_setup_notify_ring_buffer(priv); + /* Make sure the ring structure is visible to the ASIC */ + mb(); + flush_cache_all(); + } + + rtl838x_hw_ring_setup(priv); + phylink_start(priv->phylink); + + for (i = 0; i < priv->rxrings; i++) + napi_enable(&priv->rx_qs[i].napi); + + switch (priv->family_id) { + case RTL8380_FAMILY_ID: + rtl838x_hw_en_rxtx(priv); + /* Trap IGMP/MLD traffic to CPU-Port */ + sw_w32(0x3, RTL838X_SPCL_TRAP_IGMP_CTRL); + /* Flush learned FDB entries on link down of a port */ + sw_w32_mask(0, BIT(7), RTL838X_L2_CTRL_0); + break; + + case RTL8390_FAMILY_ID: + rtl839x_hw_en_rxtx(priv); + // Trap MLD and IGMP messages to CPU_PORT + sw_w32(0x3, RTL839X_SPCL_TRAP_IGMP_CTRL); + /* Flush learned FDB entries on link down of a port */ + sw_w32_mask(0, BIT(7), RTL839X_L2_CTRL_0); + break; + + case RTL9300_FAMILY_ID: + rtl93xx_hw_en_rxtx(priv); + /* Flush learned FDB entries on link down of a port */ + sw_w32_mask(0, BIT(7), RTL930X_L2_CTRL); + // Trap MLD and IGMP messages to CPU_PORT + sw_w32((0x2 << 3) | 0x2, RTL930X_VLAN_APP_PKT_CTRL); + break; + + case RTL9310_FAMILY_ID: + rtl93xx_hw_en_rxtx(priv); + + // Trap MLD and IGMP messages to CPU_PORT + sw_w32((0x2 << 3) | 0x2, RTL931X_VLAN_APP_PKT_CTRL); + + // Disable External CPU access to switch, clear EXT_CPU_EN + sw_w32_mask(BIT(2), 0, RTL931X_MAC_L2_GLOBAL_CTRL2); + + // Set PCIE_PWR_DOWN + sw_w32_mask(0, BIT(1), RTL931X_PS_SOC_CTRL); + break; + } + + netif_tx_start_all_queues(ndev); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv) +{ + u32 force_mac = priv->family_id == RTL8380_FAMILY_ID ? 0x6192C : 0x75; + u32 clear_irq = priv->family_id == RTL8380_FAMILY_ID ? 0x000fffff : 0x007fffff; + int i; + + // Disable RX/TX from/to CPU-port + sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(priv->cpu_port)); + + /* Disable traffic */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) + sw_w32_mask(RX_EN_93XX | TX_EN_93XX, 0, priv->r->dma_if_ctrl); + else + sw_w32_mask(RX_EN | TX_EN, 0, priv->r->dma_if_ctrl); + mdelay(200); // Test, whether this is needed + + /* Block all ports */ + if (priv->family_id == RTL8380_FAMILY_ID) { + sw_w32(0x03000000, RTL838X_TBL_ACCESS_DATA_0(0)); + sw_w32(0x00000000, RTL838X_TBL_ACCESS_DATA_0(1)); + sw_w32(1 << 15 | 2 << 12, RTL838X_TBL_ACCESS_CTRL_0); + } + + /* Flush L2 address cache */ + if (priv->family_id == RTL8380_FAMILY_ID) { + for (i = 0; i <= priv->cpu_port; i++) { + sw_w32(1 << 26 | 1 << 23 | i << 5, priv->r->l2_tbl_flush_ctrl); + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 26)); + } + } else if (priv->family_id == RTL8390_FAMILY_ID) { + for (i = 0; i <= priv->cpu_port; i++) { + sw_w32(1 << 28 | 1 << 25 | i << 5, priv->r->l2_tbl_flush_ctrl); + do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 28)); + } + } + // TODO: L2 flush register is 64 bit on RTL931X and 930X + + /* CPU-Port: Link down */ + if (priv->family_id == RTL8380_FAMILY_ID || priv->family_id == RTL8390_FAMILY_ID) + sw_w32(force_mac, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); + else if (priv->family_id == RTL9300_FAMILY_ID) + sw_w32_mask(0x3, 0, priv->r->mac_force_mode_ctrl + priv->cpu_port *4); + else if (priv->family_id == RTL9310_FAMILY_ID) + sw_w32_mask(BIT(0) | BIT(9), 0, priv->r->mac_force_mode_ctrl + priv->cpu_port *4); + mdelay(100); + + /* Disable all TX/RX interrupts */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) { + sw_w32(0x00000000, priv->r->dma_if_intr_rx_runout_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_rx_done_msk); + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_sts); + sw_w32(0x00000000, priv->r->dma_if_intr_tx_done_msk); + sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_sts); + } else { + sw_w32(0x00000000, priv->r->dma_if_intr_msk); + sw_w32(clear_irq, priv->r->dma_if_intr_sts); + } + + /* Disable TX/RX DMA */ + sw_w32(0x00000000, priv->r->dma_if_ctrl); + mdelay(200); +} + +static int rtl838x_eth_stop(struct net_device *ndev) +{ + unsigned long flags; + int i; + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_info("in %s\n", __func__); + + phylink_stop(priv->phylink); + rtl838x_hw_stop(priv); + + for (i = 0; i < priv->rxrings; i++) + napi_disable(&priv->rx_qs[i].napi); + + netif_tx_stop_all_queues(ndev); + + return 0; +} + +static void rtl838x_eth_set_multicast_list(struct net_device *ndev) +{ + /* + * Flood all classes of RMA addresses (01-80-C2-00-00-{01..2F}) + * CTRL_0_FULL = GENMASK(21, 0) = 0x3FFFFF + */ + if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + sw_w32(0x0, RTL838X_RMA_CTRL_0); + sw_w32(0x0, RTL838X_RMA_CTRL_1); + } + if (ndev->flags & IFF_ALLMULTI) + sw_w32(GENMASK(21, 0), RTL838X_RMA_CTRL_0); + if (ndev->flags & IFF_PROMISC) { + sw_w32(GENMASK(21, 0), RTL838X_RMA_CTRL_0); + sw_w32(0x7fff, RTL838X_RMA_CTRL_1); + } +} + +static void rtl839x_eth_set_multicast_list(struct net_device *ndev) +{ + /* + * Flood all classes of RMA addresses (01-80-C2-00-00-{01..2F}) + * CTRL_0_FULL = GENMASK(31, 2) = 0xFFFFFFFC + * Lower two bits are reserved, corresponding to RMA 01-80-C2-00-00-00 + * CTRL_1_FULL = CTRL_2_FULL = GENMASK(31, 0) + */ + if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { + sw_w32(0x0, RTL839X_RMA_CTRL_0); + sw_w32(0x0, RTL839X_RMA_CTRL_1); + sw_w32(0x0, RTL839X_RMA_CTRL_2); + sw_w32(0x0, RTL839X_RMA_CTRL_3); + } + if (ndev->flags & IFF_ALLMULTI) { + sw_w32(GENMASK(31, 2), RTL839X_RMA_CTRL_0); + sw_w32(GENMASK(31, 0), RTL839X_RMA_CTRL_1); + sw_w32(GENMASK(31, 0), RTL839X_RMA_CTRL_2); + } + if (ndev->flags & IFF_PROMISC) { + sw_w32(GENMASK(31, 2), RTL839X_RMA_CTRL_0); + sw_w32(GENMASK(31, 0), RTL839X_RMA_CTRL_1); + sw_w32(GENMASK(31, 0), RTL839X_RMA_CTRL_2); + sw_w32(0x3ff, RTL839X_RMA_CTRL_3); + } +} + +static void rtl930x_eth_set_multicast_list(struct net_device *ndev) +{ + /* + * Flood all classes of RMA addresses (01-80-C2-00-00-{01..2F}) + * CTRL_0_FULL = GENMASK(31, 2) = 0xFFFFFFFC + * Lower two bits are reserved, corresponding to RMA 01-80-C2-00-00-00 + * CTRL_1_FULL = CTRL_2_FULL = GENMASK(31, 0) + */ + if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC)) { + sw_w32(GENMASK(31, 2), RTL930X_RMA_CTRL_0); + sw_w32(GENMASK(31, 0), RTL930X_RMA_CTRL_1); + sw_w32(GENMASK(31, 0), RTL930X_RMA_CTRL_2); + } else { + sw_w32(0x0, RTL930X_RMA_CTRL_0); + sw_w32(0x0, RTL930X_RMA_CTRL_1); + sw_w32(0x0, RTL930X_RMA_CTRL_2); + } +} + +static void rtl931x_eth_set_multicast_list(struct net_device *ndev) +{ + /* + * Flood all classes of RMA addresses (01-80-C2-00-00-{01..2F}) + * CTRL_0_FULL = GENMASK(31, 2) = 0xFFFFFFFC + * Lower two bits are reserved, corresponding to RMA 01-80-C2-00-00-00. + * CTRL_1_FULL = CTRL_2_FULL = GENMASK(31, 0) + */ + if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC)) { + sw_w32(GENMASK(31, 2), RTL931X_RMA_CTRL_0); + sw_w32(GENMASK(31, 0), RTL931X_RMA_CTRL_1); + sw_w32(GENMASK(31, 0), RTL931X_RMA_CTRL_2); + } else { + sw_w32(0x0, RTL931X_RMA_CTRL_0); + sw_w32(0x0, RTL931X_RMA_CTRL_1); + sw_w32(0x0, RTL931X_RMA_CTRL_2); + } +} + +static void rtl838x_eth_tx_timeout(struct net_device *ndev, unsigned int txqueue) +{ + unsigned long flags; + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_warn("%s\n", __func__); + spin_lock_irqsave(&priv->lock, flags); + rtl838x_hw_stop(priv); + rtl838x_hw_ring_setup(priv); + rtl838x_hw_en_rxtx(priv); + netif_trans_update(ndev); + netif_start_queue(ndev); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev) +{ + int len, i; + struct rtl838x_eth_priv *priv = netdev_priv(dev); + struct ring_b *ring = priv->membase; + uint32_t val; + int ret; + unsigned long flags; + struct p_hdr *h; + int dest_port = -1; + int q = skb_get_queue_mapping(skb) % TXRINGS; + + if (q) // Check for high prio queue + pr_debug("SKB priority: %d\n", skb->priority); + + spin_lock_irqsave(&priv->lock, flags); + len = skb->len; + + /* Check for DSA tagging at the end of the buffer */ + if (netdev_uses_dsa(dev) && skb->data[len-4] == 0x80 + && skb->data[len-3] < priv->cpu_port + && skb->data[len-2] == 0x10 + && skb->data[len-1] == 0x00) { + /* Reuse tag space for CRC if possible */ + dest_port = skb->data[len-3]; + skb->data[len-4] = skb->data[len-3] = skb->data[len-2] = skb->data[len-1] = 0x00; + len -= 4; + } + + len += 4; // Add space for CRC + + if (skb_padto(skb, len)) { + ret = NETDEV_TX_OK; + goto txdone; + } + + /* We can send this packet if CPU owns the descriptor */ + if (!(ring->tx_r[q][ring->c_tx[q]] & 0x1)) { + + /* Set descriptor for tx */ + h = &ring->tx_header[q][ring->c_tx[q]]; + h->size = len; + h->len = len; + // On RTL8380 SoCs, small packet lengths being sent need adjustments + if (priv->family_id == RTL8380_FAMILY_ID) { + if (len < ETH_ZLEN - 4) + h->len -= 4; + } + + if (dest_port >= 0) + priv->r->create_tx_header(h, dest_port, skb->priority >> 1); + + /* Copy packet data to tx buffer */ + memcpy((void *)KSEG1ADDR(h->buf), skb->data, len); + /* Make sure packet data is visible to ASIC */ + wmb(); + + /* Hand over to switch */ + ring->tx_r[q][ring->c_tx[q]] |= 1; + + // Before starting TX, prevent a Lextra bus bug on RTL8380 SoCs + if (priv->family_id == RTL8380_FAMILY_ID) { + for (i = 0; i < 10; i++) { + val = sw_r32(priv->r->dma_if_ctrl); + if ((val & 0xc) == 0xc) + break; + } + } + + /* Tell switch to send data */ + if (priv->family_id == RTL9310_FAMILY_ID + || priv->family_id == RTL9300_FAMILY_ID) { + // Ring ID q == 0: Low priority, Ring ID = 1: High prio queue + if (!q) + sw_w32_mask(0, BIT(2), priv->r->dma_if_ctrl); + else + sw_w32_mask(0, BIT(3), priv->r->dma_if_ctrl); + } else { + sw_w32_mask(0, TX_DO, priv->r->dma_if_ctrl); + } + + dev->stats.tx_packets++; + dev->stats.tx_bytes += len; + dev_kfree_skb(skb); + ring->c_tx[q] = (ring->c_tx[q] + 1) % TXRINGLEN; + ret = NETDEV_TX_OK; + } else { + dev_warn(&priv->pdev->dev, "Data is owned by switch\n"); + ret = NETDEV_TX_BUSY; + } +txdone: + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + +/* + * Return queue number for TX. On the RTL83XX, these queues have equal priority + * so we do round-robin + */ +u16 rtl83xx_pick_tx_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + static u8 last = 0; + + last++; + return last % TXRINGS; +} + +/* + * Return queue number for TX. On the RTL93XX, queue 1 is the high priority queue + */ +u16 rtl93xx_pick_tx_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) +{ + if (skb->priority >= TC_PRIO_CONTROL) + return 1; + return 0; +} + +static int rtl838x_hw_receive(struct net_device *dev, int r, int budget) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + struct ring_b *ring = priv->membase; + struct sk_buff *skb; + LIST_HEAD(rx_list); + unsigned long flags; + int i, len, work_done = 0; + u8 *data, *skb_data; + unsigned int val; + u32 *last; + struct p_hdr *h; + bool dsa = netdev_uses_dsa(dev); + struct dsa_tag tag; + + pr_debug("---------------------------------------------------------- RX - %d\n", r); + spin_lock_irqsave(&priv->lock, flags); + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4)); + + do { + if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) { + if (&ring->rx_r[r][ring->c_rx[r]] != last) { + netdev_warn(dev, "Ring contention: r: %x, last %x, cur %x\n", + r, (uint32_t)last, (u32) &ring->rx_r[r][ring->c_rx[r]]); + } + break; + } + + h = &ring->rx_header[r][ring->c_rx[r]]; + data = (u8 *)KSEG1ADDR(h->buf); + len = h->len; + if (!len) + break; + work_done++; + + len -= 4; /* strip the CRC */ + /* Add 4 bytes for cpu_tag */ + if (dsa) + len += 4; + + skb = netdev_alloc_skb(dev, len + 4); + skb_reserve(skb, NET_IP_ALIGN); + + if (likely(skb)) { + /* BUG: Prevent bug on RTL838x SoCs*/ + if (priv->family_id == RTL8380_FAMILY_ID) { + sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0)); + for (i = 0; i < priv->rxrings; i++) { + /* Update each ring cnt */ + val = sw_r32(priv->r->dma_if_rx_ring_cntr(i)); + sw_w32(val, priv->r->dma_if_rx_ring_cntr(i)); + } + } + + skb_data = skb_put(skb, len); + /* Make sure data is visible */ + mb(); + memcpy(skb->data, (u8 *)KSEG1ADDR(data), len); + /* Overwrite CRC with cpu_tag */ + if (dsa) { + priv->r->decode_tag(h, &tag); + skb->data[len-4] = 0x80; + skb->data[len-3] = tag.port; + skb->data[len-2] = 0x10; + skb->data[len-1] = 0x00; + if (tag.l2_offloaded) + skb->data[len-3] |= 0x40; + } + + if (tag.queue >= 0) + pr_debug("Queue: %d, len: %d, reason %d port %d\n", + tag.queue, len, tag.reason, tag.port); + + skb->protocol = eth_type_trans(skb, dev); + if (dev->features & NETIF_F_RXCSUM) { + if (tag.crc_error) + skb_checksum_none_assert(skb); + else + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; + + list_add_tail(&skb->list, &rx_list); + } else { + if (net_ratelimit()) + dev_warn(&dev->dev, "low on memory - packet dropped\n"); + dev->stats.rx_dropped++; + } + + /* Reset header structure */ + memset(h, 0, sizeof(struct p_hdr)); + h->buf = data; + h->size = RING_BUFFER; + + ring->rx_r[r][ring->c_rx[r]] = KSEG1ADDR(h) | 0x1 + | (ring->c_rx[r] == (priv->rxringlen - 1) ? WRAP : 0x1); + ring->c_rx[r] = (ring->c_rx[r] + 1) % priv->rxringlen; + last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4)); + } while (&ring->rx_r[r][ring->c_rx[r]] != last && work_done < budget); + + netif_receive_skb_list(&rx_list); + + // Update counters + priv->r->update_cntr(r, 0); + + spin_unlock_irqrestore(&priv->lock, flags); + + return work_done; +} + +static int rtl838x_poll_rx(struct napi_struct *napi, int budget) +{ + struct rtl838x_rx_q *rx_q = container_of(napi, struct rtl838x_rx_q, napi); + struct rtl838x_eth_priv *priv = rx_q->priv; + int work_done = 0; + int r = rx_q->id; + int work; + + while (work_done < budget) { + work = rtl838x_hw_receive(priv->netdev, r, budget - work_done); + if (!work) + break; + work_done += work; + } + + if (work_done < budget) { + napi_complete_done(napi, work_done); + + /* Enable RX interrupt */ + if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) + sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_msk); + else + sw_w32_mask(0, 0xf00ff | BIT(r + 8), priv->r->dma_if_intr_msk); + } + return work_done; +} + + +static void rtl838x_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + + pr_debug("In %s\n", __func__); + + if (!phy_interface_mode_is_rgmii(state->interface) && + state->interface != PHY_INTERFACE_MODE_1000BASEX && + state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII && + state->interface != PHY_INTERFACE_MODE_GMII && + state->interface != PHY_INTERFACE_MODE_QSGMII && + state->interface != PHY_INTERFACE_MODE_INTERNAL && + state->interface != PHY_INTERFACE_MODE_SGMII) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + pr_err("Unsupported interface: %d\n", state->interface); + return; + } + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set_port_modes(mask); + phylink_set(mask, Pause); + phylink_set(mask, Asym_Pause); + + /* With the exclusion of MII and Reverse MII, we support Gigabit, + * including Half duplex + */ + if (state->interface != PHY_INTERFACE_MODE_MII && + state->interface != PHY_INTERFACE_MODE_REVMII) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseT_Half); + } + + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + + bitmap_and(supported, supported, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + + +static void rtl838x_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ + /* This is only being called for the master device, + * i.e. the CPU-Port. We don't need to do anything. + */ + + pr_info("In %s, mode %x\n", __func__, mode); +} + +static void rtl838x_mac_an_restart(struct phylink_config *config) +{ + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + /* This works only on RTL838x chips */ + if (priv->family_id != RTL8380_FAMILY_ID) + return; + + pr_debug("In %s\n", __func__); + /* Restart by disabling and re-enabling link */ + sw_w32(0x6192D, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); + mdelay(20); + sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4); +} + +static void rtl838x_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + u32 speed; + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + int port = priv->cpu_port; + + pr_info("In %s\n", __func__); + + state->link = priv->r->get_mac_link_sts(port) ? 1 : 0; + state->duplex = priv->r->get_mac_link_dup_sts(port) ? 1 : 0; + + pr_info("%s link status is %d\n", __func__, state->link); + speed = priv->r->get_mac_link_spd_sts(port); + switch (speed) { + case 0: + state->speed = SPEED_10; + break; + case 1: + state->speed = SPEED_100; + break; + case 2: + state->speed = SPEED_1000; + break; + case 5: + state->speed = SPEED_2500; + break; + case 6: + state->speed = SPEED_5000; + break; + case 4: + state->speed = SPEED_10000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } + + state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX); + if (priv->r->get_mac_rx_pause_sts(port)) + state->pause |= MLO_PAUSE_RX; + if (priv->r->get_mac_tx_pause_sts(port)) + state->pause |= MLO_PAUSE_TX; +} + +static void rtl838x_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + pr_debug("In %s\n", __func__); + /* Stop TX/RX to port */ + sw_w32_mask(0x03, 0, priv->r->mac_port_ctrl(priv->cpu_port)); +} + +static void rtl838x_mac_link_up(struct phylink_config *config, + struct phy_device *phy, unsigned int mode, + phy_interface_t interface, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct net_device *dev = container_of(config->dev, struct net_device, dev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + pr_debug("In %s\n", __func__); + /* Restart TX/RX to port */ + sw_w32_mask(0, 0x03, priv->r->mac_port_ctrl(priv->cpu_port)); +} + +static void rtl838x_set_mac_hw(struct net_device *dev, u8 *mac) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + pr_debug("In %s\n", __func__); + sw_w32((mac[0] << 8) | mac[1], priv->r->mac); + sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], priv->r->mac + 4); + + if (priv->family_id == RTL8380_FAMILY_ID) { + /* 2 more registers, ALE/MAC block */ + sw_w32((mac[0] << 8) | mac[1], RTL838X_MAC_ALE); + sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], + (RTL838X_MAC_ALE + 4)); + + sw_w32((mac[0] << 8) | mac[1], RTL838X_MAC2); + sw_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], + RTL838X_MAC2 + 4); + } + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int rtl838x_set_mac_address(struct net_device *dev, void *p) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + const struct sockaddr *addr = p; + u8 *mac = (u8 *) (addr->sa_data); + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + rtl838x_set_mac_hw(dev, mac); + + pr_info("Using MAC %08x%08x\n", sw_r32(priv->r->mac), sw_r32(priv->r->mac + 4)); + return 0; +} + +static int rtl8390_init_mac(struct rtl838x_eth_priv *priv) +{ + // We will need to set-up EEE and the egress-rate limitation + return 0; +} + +static int rtl8380_init_mac(struct rtl838x_eth_priv *priv) +{ + int i; + + if (priv->family_id == 0x8390) + return rtl8390_init_mac(priv); + + // At present we do not know how to set up EEE on any other SoC than RTL8380 + if (priv->family_id != 0x8380) + return 0; + + pr_info("%s\n", __func__); + /* fix timer for EEE */ + sw_w32(0x5001411, RTL838X_EEE_TX_TIMER_GIGA_CTRL); + sw_w32(0x5001417, RTL838X_EEE_TX_TIMER_GELITE_CTRL); + + /* Init VLAN. TODO: Understand what is being done, here */ + if (priv->id == 0x8382) { + for (i = 0; i <= 28; i++) + sw_w32(0, 0xd57c + i * 0x80); + } + if (priv->id == 0x8380) { + for (i = 8; i <= 28; i++) + sw_w32(0, 0xd57c + i * 0x80); + } + return 0; +} + +static int rtl838x_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *cmd) +{ + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_debug("%s called\n", __func__); + return phylink_ethtool_ksettings_get(priv->phylink, cmd); +} + +static int rtl838x_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct rtl838x_eth_priv *priv = netdev_priv(ndev); + + pr_debug("%s called\n", __func__); + return phylink_ethtool_ksettings_set(priv->phylink, cmd); +} + +static int rtl838x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) +{ + u32 val; + int err; + struct rtl838x_eth_priv *priv = bus->priv; + + if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) + return rtl838x_read_sds_phy(mii_id, regnum); + + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { + err = rtl838x_read_mmd_phy(mii_id, + mdiobus_c45_devad(regnum), + regnum, &val); + pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, + mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), + val, err); + } else { + pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); + err = rtl838x_read_phy(mii_id, page, regnum, &val); + } + if (err) + return err; + return val; +} + +static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + return rtl838x_mdio_read_paged(bus, mii_id, 0, regnum); +} + +static int rtl839x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) +{ + u32 val; + int err; + struct rtl838x_eth_priv *priv = bus->priv; + + if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) + return rtl839x_read_sds_phy(mii_id, regnum); + + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { + err = rtl839x_read_mmd_phy(mii_id, + mdiobus_c45_devad(regnum), + regnum, &val); + pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, + mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), + val, err); + } else { + err = rtl839x_read_phy(mii_id, page, regnum, &val); + pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); + } + if (err) + return err; + return val; +} + +static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + return rtl839x_mdio_read_paged(bus, mii_id, 0, regnum); +} + +static int rtl930x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) +{ + u32 val; + int err; + struct rtl838x_eth_priv *priv = bus->priv; + + if (priv->phy_is_internal[mii_id]) + return rtl930x_read_sds_phy(priv->sds_id[mii_id], page, regnum); + + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { + err = rtl930x_read_mmd_phy(mii_id, + mdiobus_c45_devad(regnum), + regnum, &val); + pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, + mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), + val, err); + } else { + err = rtl930x_read_phy(mii_id, page, regnum, &val); + pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); + } + if (err) + return err; + return val; +} + +static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + return rtl930x_mdio_read_paged(bus, mii_id, 0, regnum); +} + +static int rtl931x_mdio_read_paged(struct mii_bus *bus, int mii_id, u16 page, int regnum) +{ + u32 val; + int err, v; + struct rtl838x_eth_priv *priv = bus->priv; + + pr_debug("%s: In here, port %d\n", __func__, mii_id); + if (priv->phy_is_internal[mii_id]) { + v = rtl931x_read_sds_phy(priv->sds_id[mii_id], page, regnum); + if (v < 0) { + err = v; + } else { + err = 0; + val = v; + } + } else { + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { + err = rtl931x_read_mmd_phy(mii_id, + mdiobus_c45_devad(regnum), + regnum, &val); + pr_debug("MMD: %d dev %x register %x read %x, err %d\n", mii_id, + mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), + val, err); + } else { + err = rtl931x_read_phy(mii_id, page, regnum, &val); + pr_debug("PHY: %d register %x read %x, err %d\n", mii_id, regnum, val, err); + } + } + + if (err) + return err; + return val; +} + +static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + return rtl931x_mdio_read_paged(bus, mii_id, 0, regnum); +} + +static int rtl838x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, + int regnum, u16 value) +{ + u32 offset = 0; + struct rtl838x_eth_priv *priv = bus->priv; + int err; + + if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) { + if (mii_id == 26) + offset = 0x100; + sw_w32(value, RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2)); + return 0; + } + + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { + err = rtl838x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), + regnum, value); + pr_debug("MMD: %d dev %x register %x write %x, err %d\n", mii_id, + mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), + value, err); + + return err; + } + err = rtl838x_write_phy(mii_id, page, regnum, value); + pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); + return err; +} + +static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + return rtl838x_mdio_write_paged(bus, mii_id, 0, regnum, value); +} + +static int rtl839x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, + int regnum, u16 value) +{ + struct rtl838x_eth_priv *priv = bus->priv; + int err; + + if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393) + return rtl839x_write_sds_phy(mii_id, regnum, value); + + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { + err = rtl839x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), + regnum, value); + pr_debug("MMD: %d dev %x register %x write %x, err %d\n", mii_id, + mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), + value, err); + + return err; + } + + err = rtl839x_write_phy(mii_id, page, regnum, value); + pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); + return err; +} + +static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + return rtl839x_mdio_write_paged(bus, mii_id, 0, regnum, value); +} + +static int rtl930x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, + int regnum, u16 value) +{ + struct rtl838x_eth_priv *priv = bus->priv; + int err; + + if (priv->phy_is_internal[mii_id]) + return rtl930x_write_sds_phy(priv->sds_id[mii_id], page, regnum, value); + + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) + return rtl930x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), + regnum, value); + + err = rtl930x_write_phy(mii_id, page, regnum, value); + pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); + return err; +} + +static int rtl930x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + return rtl930x_mdio_write_paged(bus, mii_id, 0, regnum, value); +} + +static int rtl931x_mdio_write_paged(struct mii_bus *bus, int mii_id, u16 page, + int regnum, u16 value) +{ + struct rtl838x_eth_priv *priv = bus->priv; + int err; + + if (priv->phy_is_internal[mii_id]) + return rtl931x_write_sds_phy(priv->sds_id[mii_id], page, regnum, value); + + if (regnum & (MII_ADDR_C45 | MII_ADDR_C22_MMD)) { + err = rtl931x_write_mmd_phy(mii_id, mdiobus_c45_devad(regnum), + regnum, value); + pr_debug("MMD: %d dev %x register %x write %x, err %d\n", mii_id, + mdiobus_c45_devad(regnum), mdiobus_c45_regad(regnum), + value, err); + + return err; + } + + err = rtl931x_write_phy(mii_id, page, regnum, value); + pr_debug("PHY: %d register %x write %x, err %d\n", mii_id, regnum, value, err); + return err; +} + +static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + return rtl931x_mdio_write_paged(bus, mii_id, 0, regnum, value); +} + +static int rtl838x_mdio_reset(struct mii_bus *bus) +{ + pr_debug("%s called\n", __func__); + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0x00000000, RTL838X_SMI_POLL_CTRL); + + /* Enable PHY control via SoC */ + sw_w32_mask(0, 1 << 15, RTL838X_SMI_GLB_CTRL); + + // Probably should reset all PHYs here... + return 0; +} + +static int rtl839x_mdio_reset(struct mii_bus *bus) +{ + return 0; + + pr_debug("%s called\n", __func__); + /* BUG: The following does not work, but should! */ + /* Disable MAC polling the PHY so that we can start configuration */ + sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL); + sw_w32(0x00000000, RTL839X_SMI_PORT_POLLING_CTRL + 4); + /* Disable PHY polling via SoC */ + sw_w32_mask(1 << 7, 0, RTL839X_SMI_GLB_CTRL); + + // Probably should reset all PHYs here... + return 0; +} + +u8 mac_type_bit[RTL930X_CPU_PORT] = {0, 0, 0, 0, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, + 8, 8, 8, 8, 10, 10, 10, 10, 12, 15, 18, 21}; + +static int rtl930x_mdio_reset(struct mii_bus *bus) +{ + int i; + int pos; + struct rtl838x_eth_priv *priv = bus->priv; + u32 c45_mask = 0; + u32 poll_sel[2]; + u32 poll_ctrl = 0; + u32 private_poll_mask = 0; + u32 v; + bool uses_usxgmii = false; // For the Aquantia PHYs + bool uses_hisgmii = false; // For the RTL8221/8226 + + // Mapping of port to phy-addresses on an SMI bus + poll_sel[0] = poll_sel[1] = 0; + for (i = 0; i < RTL930X_CPU_PORT; i++) { + if (priv->smi_bus[i] > 3) + continue; + pos = (i % 6) * 5; + sw_w32_mask(0x1f << pos, priv->smi_addr[i] << pos, + RTL930X_SMI_PORT0_5_ADDR + (i / 6) * 4); + + pos = (i * 2) % 32; + poll_sel[i / 16] |= priv->smi_bus[i] << pos; + poll_ctrl |= BIT(20 + priv->smi_bus[i]); + } + + // Configure which SMI bus is behind which port number + sw_w32(poll_sel[0], RTL930X_SMI_PORT0_15_POLLING_SEL); + sw_w32(poll_sel[1], RTL930X_SMI_PORT16_27_POLLING_SEL); + + // Disable POLL_SEL for any SMI bus with a normal PHY (not RTL8295R for SFP+) + sw_w32_mask(poll_ctrl, 0, RTL930X_SMI_GLB_CTRL); + + // Configure which SMI busses are polled in c45 based on a c45 PHY being on that bus + for (i = 0; i < 4; i++) + if (priv->smi_bus_isc45[i]) + c45_mask |= BIT(i + 16); + + pr_info("c45_mask: %08x\n", c45_mask); + sw_w32_mask(0, c45_mask, RTL930X_SMI_GLB_CTRL); + + // Set the MAC type of each port according to the PHY-interface + // Values are FE: 2, GE: 3, XGE/2.5G: 0(SERDES) or 1(otherwise), SXGE: 0 + v = 0; + for (i = 0; i < RTL930X_CPU_PORT; i++) { + switch (priv->interfaces[i]) { + case PHY_INTERFACE_MODE_10GBASER: + break; // Serdes: Value = 0 + + case PHY_INTERFACE_MODE_HSGMII: + private_poll_mask |= BIT(i); + // fallthrough + case PHY_INTERFACE_MODE_USXGMII: + v |= BIT(mac_type_bit[i]); + uses_usxgmii = true; + break; + + case PHY_INTERFACE_MODE_QSGMII: + private_poll_mask |= BIT(i); + v |= 3 << mac_type_bit[i]; + break; + + default: + break; + } + } + sw_w32(v, RTL930X_SMI_MAC_TYPE_CTRL); + + // Set the private polling mask for all Realtek PHYs (i.e. not the 10GBit Aquantia ones) + sw_w32(private_poll_mask, RTL930X_SMI_PRVTE_POLLING_CTRL); + + /* The following magic values are found in the port configuration, they seem to + * define different ways of polling a PHY. The below is for the Aquantia PHYs of + * the XGS1250 and the RTL8226 of the XGS1210 */ + if (uses_usxgmii) { + sw_w32(0x01010000, RTL930X_SMI_10GPHY_POLLING_REG0_CFG); + sw_w32(0x01E7C400, RTL930X_SMI_10GPHY_POLLING_REG9_CFG); + sw_w32(0x01E7E820, RTL930X_SMI_10GPHY_POLLING_REG10_CFG); + } + if (uses_hisgmii) { + sw_w32(0x011FA400, RTL930X_SMI_10GPHY_POLLING_REG0_CFG); + sw_w32(0x013FA412, RTL930X_SMI_10GPHY_POLLING_REG9_CFG); + sw_w32(0x017FA414, RTL930X_SMI_10GPHY_POLLING_REG10_CFG); + } + + pr_debug("%s: RTL930X_SMI_GLB_CTRL %08x\n", __func__, + sw_r32(RTL930X_SMI_GLB_CTRL)); + pr_debug("%s: RTL930X_SMI_PORT0_15_POLLING_SEL %08x\n", __func__, + sw_r32(RTL930X_SMI_PORT0_15_POLLING_SEL)); + pr_debug("%s: RTL930X_SMI_PORT16_27_POLLING_SEL %08x\n", __func__, + sw_r32(RTL930X_SMI_PORT16_27_POLLING_SEL)); + pr_debug("%s: RTL930X_SMI_MAC_TYPE_CTRL %08x\n", __func__, + sw_r32(RTL930X_SMI_MAC_TYPE_CTRL)); + pr_debug("%s: RTL930X_SMI_10GPHY_POLLING_REG0_CFG %08x\n", __func__, + sw_r32(RTL930X_SMI_10GPHY_POLLING_REG0_CFG)); + pr_debug("%s: RTL930X_SMI_10GPHY_POLLING_REG9_CFG %08x\n", __func__, + sw_r32(RTL930X_SMI_10GPHY_POLLING_REG9_CFG)); + pr_debug("%s: RTL930X_SMI_10GPHY_POLLING_REG10_CFG %08x\n", __func__, + sw_r32(RTL930X_SMI_10GPHY_POLLING_REG10_CFG)); + pr_debug("%s: RTL930X_SMI_PRVTE_POLLING_CTRL %08x\n", __func__, + sw_r32(RTL930X_SMI_PRVTE_POLLING_CTRL)); + return 0; +} + +static int rtl931x_mdio_reset(struct mii_bus *bus) +{ + int i; + int pos; + struct rtl838x_eth_priv *priv = bus->priv; + u32 c45_mask = 0; + u32 poll_sel[4]; + u32 poll_ctrl = 0; + bool mdc_on[4]; + + pr_info("%s called\n", __func__); + // Disable port polling for configuration purposes + sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL); + sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL + 4); + msleep(100); + + mdc_on[0] = mdc_on[1] = mdc_on[2] = mdc_on[3] = false; + // Mapping of port to phy-addresses on an SMI bus + poll_sel[0] = poll_sel[1] = poll_sel[2] = poll_sel[3] = 0; + for (i = 0; i < 56; i++) { + pos = (i % 6) * 5; + sw_w32_mask(0x1f << pos, priv->smi_addr[i] << pos, RTL931X_SMI_PORT_ADDR + (i / 6) * 4); + pos = (i * 2) % 32; + poll_sel[i / 16] |= priv->smi_bus[i] << pos; + poll_ctrl |= BIT(20 + priv->smi_bus[i]); + mdc_on[priv->smi_bus[i]] = true; + } + + // Configure which SMI bus is behind which port number + for (i = 0; i < 4; i++) { + pr_info("poll sel %d, %08x\n", i, poll_sel[i]); + sw_w32(poll_sel[i], RTL931X_SMI_PORT_POLLING_SEL + (i * 4)); + } + + // Configure which SMI busses + pr_info("%s: WAS RTL931X_MAC_L2_GLOBAL_CTRL2 %08x\n", __func__, sw_r32(RTL931X_MAC_L2_GLOBAL_CTRL2)); + pr_info("c45_mask: %08x, RTL931X_SMI_GLB_CTRL0 was %X", c45_mask, sw_r32(RTL931X_SMI_GLB_CTRL0)); + for (i = 0; i < 4; i++) { + // bus is polled in c45 + if (priv->smi_bus_isc45[i]) + c45_mask |= 0x2 << (i * 2); // Std. C45, non-standard is 0x3 + // Enable bus access via MDC + if (mdc_on[i]) + sw_w32_mask(0, BIT(9 + i), RTL931X_MAC_L2_GLOBAL_CTRL2); + } + + pr_info("%s: RTL931X_MAC_L2_GLOBAL_CTRL2 %08x\n", __func__, sw_r32(RTL931X_MAC_L2_GLOBAL_CTRL2)); + pr_info("c45_mask: %08x, RTL931X_SMI_GLB_CTRL0 was %X", c45_mask, sw_r32(RTL931X_SMI_GLB_CTRL0)); + + /* We have a 10G PHY enable polling + sw_w32(0x01010000, RTL931X_SMI_10GPHY_POLLING_SEL2); + sw_w32(0x01E7C400, RTL931X_SMI_10GPHY_POLLING_SEL3); + sw_w32(0x01E7E820, RTL931X_SMI_10GPHY_POLLING_SEL4); +*/ + sw_w32_mask(0xff, c45_mask, RTL931X_SMI_GLB_CTRL1); + + return 0; +} + +static int rtl931x_chip_init(struct rtl838x_eth_priv *priv) +{ + pr_info("In %s\n", __func__); + + // Initialize Encapsulation memory and wait until finished + sw_w32(0x1, RTL931X_MEM_ENCAP_INIT); + do { } while (sw_r32(RTL931X_MEM_ENCAP_INIT) & 1); + pr_info("%s: init ENCAP done\n", __func__); + + // Initialize Managemen Information Base memory and wait until finished + sw_w32(0x1, RTL931X_MEM_MIB_INIT); + do { } while (sw_r32(RTL931X_MEM_MIB_INIT) & 1); + pr_info("%s: init MIB done\n", __func__); + + // Initialize ACL (PIE) memory and wait until finished + sw_w32(0x1, RTL931X_MEM_ACL_INIT); + do { } while (sw_r32(RTL931X_MEM_ACL_INIT) & 1); + pr_info("%s: init ACL done\n", __func__); + + // Initialize ALE memory and wait until finished + sw_w32(0xFFFFFFFF, RTL931X_MEM_ALE_INIT_0); + do { } while (sw_r32(RTL931X_MEM_ALE_INIT_0)); + sw_w32(0x7F, RTL931X_MEM_ALE_INIT_1); + sw_w32(0x7ff, RTL931X_MEM_ALE_INIT_2); + do { } while (sw_r32(RTL931X_MEM_ALE_INIT_2) & 0x7ff); + pr_info("%s: init ALE done\n", __func__); + + // Enable ESD auto recovery + sw_w32(0x1, RTL931X_MDX_CTRL_RSVD); + + // Init SPI, is this for thermal control or what? + sw_w32_mask(0x7 << 11, 0x2 << 11, RTL931X_SPI_CTRL0); + + return 0; +} + +static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv) +{ + struct device_node *mii_np, *dn; + u32 pn; + int ret; + + pr_debug("%s called\n", __func__); + mii_np = of_get_child_by_name(priv->pdev->dev.of_node, "mdio-bus"); + + if (!mii_np) { + dev_err(&priv->pdev->dev, "no %s child node found", "mdio-bus"); + return -ENODEV; + } + + if (!of_device_is_available(mii_np)) { + ret = -ENODEV; + goto err_put_node; + } + + priv->mii_bus = devm_mdiobus_alloc(&priv->pdev->dev); + if (!priv->mii_bus) { + ret = -ENOMEM; + goto err_put_node; + } + + switch(priv->family_id) { + case RTL8380_FAMILY_ID: + priv->mii_bus->name = "rtl838x-eth-mdio"; + priv->mii_bus->read = rtl838x_mdio_read; + priv->mii_bus->read_paged = rtl838x_mdio_read_paged; + priv->mii_bus->write = rtl838x_mdio_write; + priv->mii_bus->write_paged = rtl838x_mdio_write_paged; + priv->mii_bus->reset = rtl838x_mdio_reset; + break; + case RTL8390_FAMILY_ID: + priv->mii_bus->name = "rtl839x-eth-mdio"; + priv->mii_bus->read = rtl839x_mdio_read; + priv->mii_bus->read_paged = rtl839x_mdio_read_paged; + priv->mii_bus->write = rtl839x_mdio_write; + priv->mii_bus->write_paged = rtl839x_mdio_write_paged; + priv->mii_bus->reset = rtl839x_mdio_reset; + break; + case RTL9300_FAMILY_ID: + priv->mii_bus->name = "rtl930x-eth-mdio"; + priv->mii_bus->read = rtl930x_mdio_read; + priv->mii_bus->read_paged = rtl930x_mdio_read_paged; + priv->mii_bus->write = rtl930x_mdio_write; + priv->mii_bus->write_paged = rtl930x_mdio_write_paged; + priv->mii_bus->reset = rtl930x_mdio_reset; + priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45; + break; + case RTL9310_FAMILY_ID: + priv->mii_bus->name = "rtl931x-eth-mdio"; + priv->mii_bus->read = rtl931x_mdio_read; + priv->mii_bus->read_paged = rtl931x_mdio_read_paged; + priv->mii_bus->write = rtl931x_mdio_write; + priv->mii_bus->write_paged = rtl931x_mdio_write_paged; + priv->mii_bus->reset = rtl931x_mdio_reset; + priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45; + break; + } + priv->mii_bus->access_capabilities = MDIOBUS_ACCESS_C22_MMD; + priv->mii_bus->priv = priv; + priv->mii_bus->parent = &priv->pdev->dev; + + for_each_node_by_name(dn, "ethernet-phy") { + u32 smi_addr[2]; + + if (of_property_read_u32(dn, "reg", &pn)) + continue; + + if (of_property_read_u32_array(dn, "rtl9300,smi-address", &smi_addr[0], 2)) { + smi_addr[0] = 0; + smi_addr[1] = pn; + } + + if (of_property_read_u32(dn, "sds", &priv->sds_id[pn])) + priv->sds_id[pn] = -1; + else { + pr_info("set sds port %d to %d\n", pn, priv->sds_id[pn]); + } + + if (pn < MAX_PORTS) { + priv->smi_bus[pn] = smi_addr[0]; + priv->smi_addr[pn] = smi_addr[1]; + } else { + pr_err("%s: illegal port number %d\n", __func__, pn); + } + + if (of_device_is_compatible(dn, "ethernet-phy-ieee802.3-c45")) + priv->smi_bus_isc45[smi_addr[0]] = true; + + if (of_property_read_bool(dn, "phy-is-integrated")) { + priv->phy_is_internal[pn] = true; + } + } + + dn = of_find_compatible_node(NULL, NULL, "realtek,rtl83xx-switch"); + if (!dn) { + dev_err(&priv->pdev->dev, "No RTL switch node in DTS\n"); + return -ENODEV; + } + + for_each_node_by_name(dn, "port") { + if (of_property_read_u32(dn, "reg", &pn)) + continue; + pr_debug("%s Looking at port %d\n", __func__, pn); + if (pn > priv->cpu_port) + continue; + if (of_get_phy_mode(dn, &priv->interfaces[pn])) + priv->interfaces[pn] = PHY_INTERFACE_MODE_NA; + pr_debug("%s phy mode of port %d is %s\n", __func__, pn, phy_modes(priv->interfaces[pn])); + } + + snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np); + ret = of_mdiobus_register(priv->mii_bus, mii_np); + +err_put_node: + of_node_put(mii_np); + return ret; +} + +static int rtl838x_mdio_remove(struct rtl838x_eth_priv *priv) +{ + pr_debug("%s called\n", __func__); + if (!priv->mii_bus) + return 0; + + mdiobus_unregister(priv->mii_bus); + mdiobus_free(priv->mii_bus); + + return 0; +} + +static netdev_features_t rtl838x_fix_features(struct net_device *dev, + netdev_features_t features) +{ + return features; +} + +static int rtl83xx_set_features(struct net_device *dev, netdev_features_t features) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + if ((features ^ dev->features) & NETIF_F_RXCSUM) { + if (!(features & NETIF_F_RXCSUM)) + sw_w32_mask(BIT(3), 0, priv->r->mac_port_ctrl(priv->cpu_port)); + else + sw_w32_mask(0, BIT(3), priv->r->mac_port_ctrl(priv->cpu_port)); + } + + return 0; +} + +static int rtl93xx_set_features(struct net_device *dev, netdev_features_t features) +{ + struct rtl838x_eth_priv *priv = netdev_priv(dev); + + if ((features ^ dev->features) & NETIF_F_RXCSUM) { + if (!(features & NETIF_F_RXCSUM)) + sw_w32_mask(BIT(4), 0, priv->r->mac_port_ctrl(priv->cpu_port)); + else + sw_w32_mask(0, BIT(4), priv->r->mac_port_ctrl(priv->cpu_port)); + } + + return 0; +} + +static const struct net_device_ops rtl838x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl83xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl838x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl83xx_set_features, + .ndo_fix_features = rtl838x_fix_features, + .ndo_setup_tc = rtl83xx_setup_tc, +}; + +static const struct net_device_ops rtl839x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl83xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl839x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl83xx_set_features, + .ndo_fix_features = rtl838x_fix_features, + .ndo_setup_tc = rtl83xx_setup_tc, +}; + +static const struct net_device_ops rtl930x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl93xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl930x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl93xx_set_features, + .ndo_fix_features = rtl838x_fix_features, + .ndo_setup_tc = rtl83xx_setup_tc, +}; + +static const struct net_device_ops rtl931x_eth_netdev_ops = { + .ndo_open = rtl838x_eth_open, + .ndo_stop = rtl838x_eth_stop, + .ndo_start_xmit = rtl838x_eth_tx, + .ndo_select_queue = rtl93xx_pick_tx_queue, + .ndo_set_mac_address = rtl838x_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = rtl931x_eth_set_multicast_list, + .ndo_tx_timeout = rtl838x_eth_tx_timeout, + .ndo_set_features = rtl93xx_set_features, + .ndo_fix_features = rtl838x_fix_features, +}; + +static const struct phylink_mac_ops rtl838x_phylink_ops = { + .validate = rtl838x_validate, + .mac_pcs_get_state = rtl838x_mac_pcs_get_state, + .mac_an_restart = rtl838x_mac_an_restart, + .mac_config = rtl838x_mac_config, + .mac_link_down = rtl838x_mac_link_down, + .mac_link_up = rtl838x_mac_link_up, +}; + +static const struct ethtool_ops rtl838x_ethtool_ops = { + .get_link_ksettings = rtl838x_get_link_ksettings, + .set_link_ksettings = rtl838x_set_link_ksettings, +}; + +static int __init rtl838x_eth_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct device_node *dn = pdev->dev.of_node; + struct rtl838x_eth_priv *priv; + struct resource *res, *mem; + phy_interface_t phy_mode; + struct phylink *phylink; + int err = 0, i, rxrings, rxringlen; + struct ring_b *ring; + + pr_info("Probing RTL838X eth device pdev: %x, dev: %x\n", + (u32)pdev, (u32)(&(pdev->dev))); + + if (!dn) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + rxrings = (soc_info.family == RTL8380_FAMILY_ID + || soc_info.family == RTL8390_FAMILY_ID) ? 8 : 32; + rxrings = rxrings > MAX_RXRINGS ? MAX_RXRINGS : rxrings; + rxringlen = MAX_ENTRIES / rxrings; + rxringlen = rxringlen > MAX_RXLEN ? MAX_RXLEN : rxringlen; + + dev = alloc_etherdev_mqs(sizeof(struct rtl838x_eth_priv), TXRINGS, rxrings); + if (!dev) { + err = -ENOMEM; + goto err_free; + } + SET_NETDEV_DEV(dev, &pdev->dev); + priv = netdev_priv(dev); + + /* obtain buffer memory space */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) { + mem = devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), res->name); + if (!mem) { + dev_err(&pdev->dev, "cannot request memory space\n"); + err = -ENXIO; + goto err_free; + } + + dev->mem_start = mem->start; + dev->mem_end = mem->end; + } else { + dev_err(&pdev->dev, "cannot request IO resource\n"); + err = -ENXIO; + goto err_free; + } + + /* Allocate buffer memory */ + priv->membase = dmam_alloc_coherent(&pdev->dev, rxrings * rxringlen * RING_BUFFER + + sizeof(struct ring_b) + sizeof(struct notify_b), + (void *)&dev->mem_start, GFP_KERNEL); + if (!priv->membase) { + dev_err(&pdev->dev, "cannot allocate DMA buffer\n"); + err = -ENOMEM; + goto err_free; + } + + // Allocate ring-buffer space at the end of the allocated memory + ring = priv->membase; + ring->rx_space = priv->membase + sizeof(struct ring_b) + sizeof(struct notify_b); + + spin_lock_init(&priv->lock); + + dev->ethtool_ops = &rtl838x_ethtool_ops; + dev->min_mtu = ETH_ZLEN; + dev->max_mtu = 1536; + dev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM; + dev->hw_features = NETIF_F_RXCSUM; + + priv->id = soc_info.id; + priv->family_id = soc_info.family; + if (priv->id) { + pr_info("Found SoC ID: %4x: %s, family %x\n", + priv->id, soc_info.name, priv->family_id); + } else { + pr_err("Unknown chip id (%04x)\n", priv->id); + return -ENODEV; + } + + switch (priv->family_id) { + case RTL8380_FAMILY_ID: + priv->cpu_port = RTL838X_CPU_PORT; + priv->r = &rtl838x_reg; + dev->netdev_ops = &rtl838x_eth_netdev_ops; + break; + case RTL8390_FAMILY_ID: + priv->cpu_port = RTL839X_CPU_PORT; + priv->r = &rtl839x_reg; + dev->netdev_ops = &rtl839x_eth_netdev_ops; + break; + case RTL9300_FAMILY_ID: + priv->cpu_port = RTL930X_CPU_PORT; + priv->r = &rtl930x_reg; + dev->netdev_ops = &rtl930x_eth_netdev_ops; + break; + case RTL9310_FAMILY_ID: + priv->cpu_port = RTL931X_CPU_PORT; + priv->r = &rtl931x_reg; + dev->netdev_ops = &rtl931x_eth_netdev_ops; + rtl931x_chip_init(priv); + break; + default: + pr_err("Unknown SoC family\n"); + return -ENODEV; + } + priv->rxringlen = rxringlen; + priv->rxrings = rxrings; + + /* Obtain device IRQ number */ + dev->irq = platform_get_irq(pdev, 0); + if (dev->irq < 0) { + dev_err(&pdev->dev, "cannot obtain network-device IRQ\n"); + goto err_free; + } + + err = devm_request_irq(&pdev->dev, dev->irq, priv->r->net_irq, + IRQF_SHARED, dev->name, dev); + if (err) { + dev_err(&pdev->dev, "%s: could not acquire interrupt: %d\n", + __func__, err); + goto err_free; + } + + rtl8380_init_mac(priv); + + /* try to get mac address in the following order: + * 1) from device tree data + * 2) from internal registers set by bootloader + */ + of_get_mac_address(pdev->dev.of_node, dev->dev_addr); + if (is_valid_ether_addr(dev->dev_addr)) { + rtl838x_set_mac_hw(dev, (u8 *)dev->dev_addr); + } else { + dev->dev_addr[0] = (sw_r32(priv->r->mac) >> 8) & 0xff; + dev->dev_addr[1] = sw_r32(priv->r->mac) & 0xff; + dev->dev_addr[2] = (sw_r32(priv->r->mac + 4) >> 24) & 0xff; + dev->dev_addr[3] = (sw_r32(priv->r->mac + 4) >> 16) & 0xff; + dev->dev_addr[4] = (sw_r32(priv->r->mac + 4) >> 8) & 0xff; + dev->dev_addr[5] = sw_r32(priv->r->mac + 4) & 0xff; + } + /* if the address is invalid, use a random value */ + if (!is_valid_ether_addr(dev->dev_addr)) { + struct sockaddr sa = { AF_UNSPEC }; + + netdev_warn(dev, "Invalid MAC address, using random\n"); + eth_hw_addr_random(dev); + memcpy(sa.sa_data, dev->dev_addr, ETH_ALEN); + if (rtl838x_set_mac_address(dev, &sa)) + netdev_warn(dev, "Failed to set MAC address.\n"); + } + pr_info("Using MAC %08x%08x\n", sw_r32(priv->r->mac), + sw_r32(priv->r->mac + 4)); + strcpy(dev->name, "eth%d"); + priv->pdev = pdev; + priv->netdev = dev; + + err = rtl838x_mdio_init(priv); + if (err) + goto err_free; + + err = register_netdev(dev); + if (err) + goto err_free; + + for (i = 0; i < priv->rxrings; i++) { + priv->rx_qs[i].id = i; + priv->rx_qs[i].priv = priv; + netif_napi_add(dev, &priv->rx_qs[i].napi, rtl838x_poll_rx, 64); + } + + platform_set_drvdata(pdev, dev); + + phy_mode = PHY_INTERFACE_MODE_NA; + err = of_get_phy_mode(dn, &phy_mode); + if (err < 0) { + dev_err(&pdev->dev, "incorrect phy-mode\n"); + err = -EINVAL; + goto err_free; + } + priv->phylink_config.dev = &dev->dev; + priv->phylink_config.type = PHYLINK_NETDEV; + + phylink = phylink_create(&priv->phylink_config, pdev->dev.fwnode, + phy_mode, &rtl838x_phylink_ops); + + if (IS_ERR(phylink)) { + err = PTR_ERR(phylink); + goto err_free; + } + priv->phylink = phylink; + + return 0; + +err_free: + pr_err("Error setting up netdev, freeing it again.\n"); + free_netdev(dev); + return err; +} + +static int rtl838x_eth_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct rtl838x_eth_priv *priv = netdev_priv(dev); + int i; + + if (dev) { + pr_info("Removing platform driver for rtl838x-eth\n"); + rtl838x_mdio_remove(priv); + rtl838x_hw_stop(priv); + + netif_tx_stop_all_queues(dev); + + for (i = 0; i < priv->rxrings; i++) + netif_napi_del(&priv->rx_qs[i].napi); + + unregister_netdev(dev); + free_netdev(dev); + } + return 0; +} + +static const struct of_device_id rtl838x_eth_of_ids[] = { + { .compatible = "realtek,rtl838x-eth"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtl838x_eth_of_ids); + +static struct platform_driver rtl838x_eth_driver = { + .probe = rtl838x_eth_probe, + .remove = rtl838x_eth_remove, + .driver = { + .name = "rtl838x-eth", + .pm = NULL, + .of_match_table = rtl838x_eth_of_ids, + }, +}; + +module_platform_driver(rtl838x_eth_driver); + +MODULE_AUTHOR("B. Koblitz"); +MODULE_DESCRIPTION("RTL838X SoC Ethernet Driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h new file mode 100644 index 00000000000..d00d11d0c82 --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/ethernet/rtl838x_eth.h @@ -0,0 +1,457 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _RTL838X_ETH_H +#define _RTL838X_ETH_H + +/* + * Register definition + */ + +/* Per port MAC control */ +#define RTL838X_MAC_PORT_CTRL (0xd560) +#define RTL839X_MAC_PORT_CTRL (0x8004) +#define RTL930X_MAC_L2_PORT_CTRL (0x3268) +#define RTL930X_MAC_PORT_CTRL (0x3260) +#define RTL931X_MAC_L2_PORT_CTRL (0x6000) +#define RTL931X_MAC_PORT_CTRL (0x6004) + +/* DMA interrupt control and status registers */ +#define RTL838X_DMA_IF_CTRL (0x9f58) +#define RTL838X_DMA_IF_INTR_STS (0x9f54) +#define RTL838X_DMA_IF_INTR_MSK (0x9f50) + +#define RTL839X_DMA_IF_CTRL (0x786c) +#define RTL839X_DMA_IF_INTR_STS (0x7868) +#define RTL839X_DMA_IF_INTR_MSK (0x7864) + +#define RTL930X_DMA_IF_CTRL (0xe028) +#define RTL930X_DMA_IF_INTR_RX_RUNOUT_STS (0xe01C) +#define RTL930X_DMA_IF_INTR_RX_DONE_STS (0xe020) +#define RTL930X_DMA_IF_INTR_TX_DONE_STS (0xe024) +#define RTL930X_DMA_IF_INTR_RX_RUNOUT_MSK (0xe010) +#define RTL930X_DMA_IF_INTR_RX_DONE_MSK (0xe014) +#define RTL930X_DMA_IF_INTR_TX_DONE_MSK (0xe018) +#define RTL930X_L2_NTFY_IF_INTR_MSK (0xe04C) +#define RTL930X_L2_NTFY_IF_INTR_STS (0xe050) + +/* TODO: RTL931X_DMA_IF_CTRL has different bits meanings */ +#define RTL931X_DMA_IF_CTRL (0x0928) +#define RTL931X_DMA_IF_INTR_RX_RUNOUT_STS (0x091c) +#define RTL931X_DMA_IF_INTR_RX_DONE_STS (0x0920) +#define RTL931X_DMA_IF_INTR_TX_DONE_STS (0x0924) +#define RTL931X_DMA_IF_INTR_RX_RUNOUT_MSK (0x0910) +#define RTL931X_DMA_IF_INTR_RX_DONE_MSK (0x0914) +#define RTL931X_DMA_IF_INTR_TX_DONE_MSK (0x0918) +#define RTL931X_L2_NTFY_IF_INTR_MSK (0x09E4) +#define RTL931X_L2_NTFY_IF_INTR_STS (0x09E8) + +#define RTL838X_MAC_FORCE_MODE_CTRL (0xa104) +#define RTL839X_MAC_FORCE_MODE_CTRL (0x02bc) +#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C) +#define RTL931X_MAC_FORCE_MODE_CTRL (0x0ddc) + +/* MAC address settings */ +#define RTL838X_MAC (0xa9ec) +#define RTL839X_MAC (0x02b4) +#define RTL838X_MAC_ALE (0x6b04) +#define RTL838X_MAC2 (0xa320) +#define RTL930X_MAC_L2_ADDR_CTRL (0xC714) +#define RTL931X_MAC_L2_ADDR_CTRL (0x135c) + +/* Ringbuffer setup */ +#define RTL838X_DMA_RX_BASE (0x9f00) +#define RTL839X_DMA_RX_BASE (0x780c) +#define RTL930X_DMA_RX_BASE (0xdf00) +#define RTL931X_DMA_RX_BASE (0x0800) + +#define RTL838X_DMA_TX_BASE (0x9f40) +#define RTL839X_DMA_TX_BASE (0x784c) +#define RTL930X_DMA_TX_BASE (0xe000) +#define RTL931X_DMA_TX_BASE (0x0900) + +#define RTL838X_DMA_IF_RX_RING_SIZE (0xB7E4) +#define RTL839X_DMA_IF_RX_RING_SIZE (0x6038) +#define RTL930X_DMA_IF_RX_RING_SIZE (0x7C60) +#define RTL931X_DMA_IF_RX_RING_SIZE (0x2080) + +#define RTL838X_DMA_IF_RX_RING_CNTR (0xB7E8) +#define RTL839X_DMA_IF_RX_RING_CNTR (0x603c) +#define RTL930X_DMA_IF_RX_RING_CNTR (0x7C8C) +#define RTL931X_DMA_IF_RX_RING_CNTR (0x20AC) + +#define RTL838X_DMA_IF_RX_CUR (0x9F20) +#define RTL839X_DMA_IF_RX_CUR (0x782c) +#define RTL930X_DMA_IF_RX_CUR (0xdf80) +#define RTL931X_DMA_IF_RX_CUR (0x0880) + +#define RTL838X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0x9F48) +#define RTL930X_DMA_IF_TX_CUR_DESC_ADDR_CTRL (0xE008) + +#define RTL838X_DMY_REG31 (0x3b28) +#define RTL838X_SDS_MODE_SEL (0x0028) +#define RTL838X_SDS_CFG_REG (0x0034) +#define RTL838X_INT_MODE_CTRL (0x005c) +#define RTL838X_CHIP_INFO (0x00d8) +#define RTL838X_SDS4_REG28 (0xef80) +#define RTL838X_SDS4_DUMMY0 (0xef8c) +#define RTL838X_SDS5_EXT_REG6 (0xf18c) + +/* L2 features */ +#define RTL839X_TBL_ACCESS_L2_CTRL (0x1180) +#define RTL839X_TBL_ACCESS_L2_DATA(idx) (0x1184 + ((idx) << 2)) +#define RTL838X_TBL_ACCESS_CTRL_0 (0x6914) +#define RTL838X_TBL_ACCESS_DATA_0(idx) (0x6918 + ((idx) << 2)) + +/* MAC-side link state handling */ +#define RTL838X_MAC_LINK_STS (0xa188) +#define RTL839X_MAC_LINK_STS (0x0390) +#define RTL930X_MAC_LINK_STS (0xCB10) +#define RTL931X_MAC_LINK_STS (0x0ec0) + +#define RTL838X_MAC_LINK_SPD_STS (0xa190) +#define RTL839X_MAC_LINK_SPD_STS (0x03a0) +#define RTL930X_MAC_LINK_SPD_STS (0xCB18) +#define RTL931X_MAC_LINK_SPD_STS (0x0ed0) + +#define RTL838X_MAC_LINK_DUP_STS (0xa19c) +#define RTL839X_MAC_LINK_DUP_STS (0x03b0) +#define RTL930X_MAC_LINK_DUP_STS (0xCB28) +#define RTL931X_MAC_LINK_DUP_STS (0x0ef0) + +// TODO: RTL8390_MAC_LINK_MEDIA_STS_ADDR ??? + +#define RTL838X_MAC_TX_PAUSE_STS (0xa1a0) +#define RTL839X_MAC_TX_PAUSE_STS (0x03b8) +#define RTL930X_MAC_TX_PAUSE_STS (0xCB2C) +#define RTL931X_MAC_TX_PAUSE_STS (0x0ef8) + +#define RTL838X_MAC_RX_PAUSE_STS (0xa1a4) +#define RTL839X_MAC_RX_PAUSE_STS (0xCB30) +#define RTL930X_MAC_RX_PAUSE_STS (0xC2F8) +#define RTL931X_MAC_RX_PAUSE_STS (0x0f00) + +#define RTL838X_EEE_TX_TIMER_GIGA_CTRL (0xaa04) +#define RTL838X_EEE_TX_TIMER_GELITE_CTRL (0xaa08) + +#define RTL930X_L2_UNKN_UC_FLD_PMSK (0x9064) +#define RTL931X_L2_UNKN_UC_FLD_PMSK (0xC8F4) + +#define RTL839X_MAC_GLB_CTRL (0x02a8) +#define RTL839X_SCHED_LB_TICK_TKN_CTRL (0x60f8) + +#define RTL838X_L2_TBL_FLUSH_CTRL (0x3370) +#define RTL839X_L2_TBL_FLUSH_CTRL (0x3ba0) +#define RTL930X_L2_TBL_FLUSH_CTRL (0x9404) +#define RTL931X_L2_TBL_FLUSH_CTRL (0xCD9C) + +#define RTL930X_L2_PORT_SABLK_CTRL (0x905c) +#define RTL930X_L2_PORT_DABLK_CTRL (0x9060) + +/* MAC link state bits */ +#define FORCE_EN (1 << 0) +#define FORCE_LINK_EN (1 << 1) +#define NWAY_EN (1 << 2) +#define DUPLX_MODE (1 << 3) +#define TX_PAUSE_EN (1 << 6) +#define RX_PAUSE_EN (1 << 7) + +/* L2 Notification DMA interface */ +#define RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL (0x785C) +#define RTL839X_L2_NOTIFICATION_CTRL (0x7808) +#define RTL931X_L2_NTFY_RING_BASE_ADDR (0x09DC) +#define RTL931X_L2_NTFY_RING_CUR_ADDR (0x09E0) +#define RTL839X_L2_NOTIFICATION_CTRL (0x7808) +#define RTL931X_L2_NTFY_CTRL (0xCDC8) +#define RTL838X_L2_CTRL_0 (0x3200) +#define RTL839X_L2_CTRL_0 (0x3800) +#define RTL930X_L2_CTRL (0x8FD8) +#define RTL931X_L2_CTRL (0xC800) + +/* TRAPPING to CPU-PORT */ +#define RTL838X_SPCL_TRAP_IGMP_CTRL (0x6984) +#define RTL838X_RMA_CTRL_0 (0x4300) +#define RTL838X_RMA_CTRL_1 (0x4304) +#define RTL839X_RMA_CTRL_0 (0x1200) + +#define RTL839X_SPCL_TRAP_IGMP_CTRL (0x1058) +#define RTL839X_RMA_CTRL_1 (0x1204) +#define RTL839X_RMA_CTRL_2 (0x1208) +#define RTL839X_RMA_CTRL_3 (0x120C) + +#define RTL930X_VLAN_APP_PKT_CTRL (0xA23C) +#define RTL930X_RMA_CTRL_0 (0x9E60) +#define RTL930X_RMA_CTRL_1 (0x9E64) +#define RTL930X_RMA_CTRL_2 (0x9E68) + +#define RTL931X_VLAN_APP_PKT_CTRL (0x96b0) +#define RTL931X_RMA_CTRL_0 (0x8800) +#define RTL931X_RMA_CTRL_1 (0x8804) +#define RTL931X_RMA_CTRL_2 (0x8808) + +/* Advanced SMI control for clause 45 PHYs */ +#define RTL930X_SMI_MAC_TYPE_CTRL (0xCA04) +#define RTL930X_SMI_PORT24_27_ADDR_CTRL (0xCB90) +#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08) +#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C) + +#define RTL930X_SMI_10GPHY_POLLING_REG0_CFG (0xCBB4) +#define RTL930X_SMI_10GPHY_POLLING_REG9_CFG (0xCBB8) +#define RTL930X_SMI_10GPHY_POLLING_REG10_CFG (0xCBBC) +#define RTL930X_SMI_PRVTE_POLLING_CTRL (0xCA10) + +/* Registers of the internal Serdes of the 8390 */ +#define RTL839X_SDS12_13_XSG0 (0xB800) + +/* Chip configuration registers of the RTL9310 */ +#define RTL931X_MEM_ENCAP_INIT (0x4854) +#define RTL931X_MEM_MIB_INIT (0x7E18) +#define RTL931X_MEM_ACL_INIT (0x40BC) +#define RTL931X_MEM_ALE_INIT_0 (0x83F0) +#define RTL931X_MEM_ALE_INIT_1 (0x83F4) +#define RTL931X_MEM_ALE_INIT_2 (0x82E4) +#define RTL931X_MDX_CTRL_RSVD (0x0fcc) +#define RTL931X_PS_SOC_CTRL (0x13f8) +#define RTL931X_SMI_10GPHY_POLLING_SEL2 (0xCF8) +#define RTL931X_SMI_10GPHY_POLLING_SEL3 (0xCFC) +#define RTL931X_SMI_10GPHY_POLLING_SEL4 (0xD00) + +/* Registers of the internal Serdes of the 8380 */ +#define RTL838X_SDS4_FIB_REG0 (0xF800) + +inline int rtl838x_mac_port_ctrl(int p) +{ + return RTL838X_MAC_PORT_CTRL + (p << 7); +} + +inline int rtl839x_mac_port_ctrl(int p) +{ + return RTL839X_MAC_PORT_CTRL + (p << 7); +} + +/* On the RTL931XX, the functionality of the MAC port control register is split up + * into RTL931X_MAC_L2_PORT_CTRL and RTL931X_MAC_PORT_CTRL the functionality used + * by the Ethernet driver is in the same bits now in RTL931X_MAC_L2_PORT_CTRL + */ + +inline int rtl930x_mac_port_ctrl(int p) +{ + return RTL930X_MAC_L2_PORT_CTRL + (p << 6); +} + +inline int rtl931x_mac_port_ctrl(int p) +{ + return RTL931X_MAC_L2_PORT_CTRL + (p << 7); +} + +inline int rtl838x_dma_if_rx_ring_size(int i) +{ + return RTL838X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2); +} + +inline int rtl839x_dma_if_rx_ring_size(int i) +{ + return RTL839X_DMA_IF_RX_RING_SIZE + ((i >> 3) << 2); +} + +inline int rtl930x_dma_if_rx_ring_size(int i) +{ + return RTL930X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2); +} + +inline int rtl931x_dma_if_rx_ring_size(int i) +{ + return RTL931X_DMA_IF_RX_RING_SIZE + ((i / 3) << 2); +} + +inline int rtl838x_dma_if_rx_ring_cntr(int i) +{ + return RTL838X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2); +} + +inline int rtl839x_dma_if_rx_ring_cntr(int i) +{ + return RTL839X_DMA_IF_RX_RING_CNTR + ((i >> 3) << 2); +} + +inline int rtl930x_dma_if_rx_ring_cntr(int i) +{ + return RTL930X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2); +} + +inline int rtl931x_dma_if_rx_ring_cntr(int i) +{ + return RTL931X_DMA_IF_RX_RING_CNTR + ((i / 3) << 2); +} + +inline u32 rtl838x_get_mac_link_sts(int port) +{ + return (sw_r32(RTL838X_MAC_LINK_STS) & BIT(port)); +} + +inline u32 rtl839x_get_mac_link_sts(int p) +{ + return (sw_r32(RTL839X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl930x_get_mac_link_sts(int port) +{ + u32 link = sw_r32(RTL930X_MAC_LINK_STS); + + link = sw_r32(RTL930X_MAC_LINK_STS); + pr_info("%s link state is %08x\n", __func__, link); + return link & BIT(port); +} + +inline u32 rtl931x_get_mac_link_sts(int p) +{ + return (sw_r32(RTL931X_MAC_LINK_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl838x_get_mac_link_dup_sts(int port) +{ + return (sw_r32(RTL838X_MAC_LINK_DUP_STS) & BIT(port)); +} + +inline u32 rtl839x_get_mac_link_dup_sts(int p) +{ + return (sw_r32(RTL839X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl930x_get_mac_link_dup_sts(int port) +{ + return (sw_r32(RTL930X_MAC_LINK_DUP_STS) & BIT(port)); +} + +inline u32 rtl931x_get_mac_link_dup_sts(int p) +{ + return (sw_r32(RTL931X_MAC_LINK_DUP_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl838x_get_mac_link_spd_sts(int port) +{ + int r = RTL838X_MAC_LINK_SPD_STS + ((port >> 4) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 16) << 1; + return (speed & 0x3); +} + +inline u32 rtl839x_get_mac_link_spd_sts(int port) +{ + int r = RTL839X_MAC_LINK_SPD_STS + ((port >> 4) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 16) << 1; + return (speed & 0x3); +} + + +inline u32 rtl930x_get_mac_link_spd_sts(int port) +{ + int r = RTL930X_MAC_LINK_SPD_STS + ((port >> 3) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 8) << 2; + return (speed & 0xf); +} + +inline u32 rtl931x_get_mac_link_spd_sts(int port) +{ + int r = RTL931X_MAC_LINK_SPD_STS + ((port >> 3) << 2); + u32 speed = sw_r32(r); + + speed >>= (port % 8) << 2; + return (speed & 0xf); +} + +inline u32 rtl838x_get_mac_rx_pause_sts(int port) +{ + return (sw_r32(RTL838X_MAC_RX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl839x_get_mac_rx_pause_sts(int p) +{ + return (sw_r32(RTL839X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl930x_get_mac_rx_pause_sts(int port) +{ + return (sw_r32(RTL930X_MAC_RX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl931x_get_mac_rx_pause_sts(int p) +{ + return (sw_r32(RTL931X_MAC_RX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl838x_get_mac_tx_pause_sts(int port) +{ + return (sw_r32(RTL838X_MAC_TX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl839x_get_mac_tx_pause_sts(int p) +{ + return (sw_r32(RTL839X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +inline u32 rtl930x_get_mac_tx_pause_sts(int port) +{ + return (sw_r32(RTL930X_MAC_TX_PAUSE_STS) & (1 << port)); +} + +inline u32 rtl931x_get_mac_tx_pause_sts(int p) +{ + return (sw_r32(RTL931X_MAC_TX_PAUSE_STS + ((p >> 5) << 2)) & BIT(p % 32)); +} + +struct p_hdr; +struct dsa_tag; + +struct rtl838x_eth_reg { + irqreturn_t (*net_irq)(int irq, void *dev_id); + int (*mac_port_ctrl)(int port); + int dma_if_intr_sts; + int dma_if_intr_msk; + int dma_if_intr_rx_runout_sts; + int dma_if_intr_rx_done_sts; + int dma_if_intr_tx_done_sts; + int dma_if_intr_rx_runout_msk; + int dma_if_intr_rx_done_msk; + int dma_if_intr_tx_done_msk; + int l2_ntfy_if_intr_sts; + int l2_ntfy_if_intr_msk; + int dma_if_ctrl; + int mac_force_mode_ctrl; + int dma_rx_base; + int dma_tx_base; + int (*dma_if_rx_ring_size)(int ring); + int (*dma_if_rx_ring_cntr)(int ring); + int dma_if_rx_cur; + int rst_glb_ctrl; + u32 (*get_mac_link_sts)(int port); + u32 (*get_mac_link_dup_sts)(int port); + u32 (*get_mac_link_spd_sts)(int port); + u32 (*get_mac_rx_pause_sts)(int port); + u32 (*get_mac_tx_pause_sts)(int port); + int mac; + int l2_tbl_flush_ctrl; + void (*update_cntr)(int r, int work_done); + void (*create_tx_header)(struct p_hdr *h, unsigned int dest_port, int prio); + bool (*decode_tag)(struct p_hdr *h, struct dsa_tag *tag); +}; + +int rtl838x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl838x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val); +int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val); +int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); +int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val); +int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val); +int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val); +int rtl83xx_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data); + +#endif /* _RTL838X_ETH_H */ diff --git a/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c new file mode 100644 index 00000000000..491ceb48b6a --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.c @@ -0,0 +1,4018 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Realtek RTL838X Ethernet MDIO interface driver + * + * Copyright (C) 2020 B. Koblitz + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rtl83xx-phy.h" + +extern struct rtl83xx_soc_info soc_info; +extern struct mutex smi_lock; + +#define PHY_CTRL_REG 0 +#define PHY_POWER_BIT 11 + +#define PHY_PAGE_2 2 +#define PHY_PAGE_4 4 + +/* all Clause-22 RealTek MDIO PHYs use register 0x1f for page select */ +#define RTL8XXX_PAGE_SELECT 0x1f + +#define RTL8XXX_PAGE_MAIN 0x0000 +#define RTL821X_PAGE_PORT 0x0266 +#define RTL821X_PAGE_POWER 0x0a40 +#define RTL821X_PAGE_GPHY 0x0a42 +#define RTL821X_PAGE_MAC 0x0a43 +#define RTL821X_PAGE_STATE 0x0b80 +#define RTL821X_PAGE_PATCH 0x0b82 + +/* + * Using the special page 0xfff with the MDIO controller found in + * RealTek SoCs allows to access the PHY in RAW mode, ie. bypassing + * the cache and paging engine of the MDIO controller. + */ +#define RTL83XX_PAGE_RAW 0x0fff + +/* internal RTL821X PHY uses register 0x1d to select media page */ +#define RTL821XINT_MEDIA_PAGE_SELECT 0x1d +/* external RTL821X PHY uses register 0x1e to select media page */ +#define RTL821XEXT_MEDIA_PAGE_SELECT 0x1e + +#define RTL821X_MEDIA_PAGE_AUTO 0 +#define RTL821X_MEDIA_PAGE_COPPER 1 +#define RTL821X_MEDIA_PAGE_FIBRE 3 +#define RTL821X_MEDIA_PAGE_INTERNAL 8 + +#define RTL9300_PHY_ID_MASK 0xf0ffffff + +/* + * This lock protects the state of the SoC automatically polling the PHYs over the SMI + * bus to detect e.g. link and media changes. For operations on the PHYs such as + * patching or other configuration changes such as EEE, polling needs to be disabled + * since otherwise these operations may fails or lead to unpredictable results. + */ +DEFINE_MUTEX(poll_lock); + +static const struct firmware rtl838x_8380_fw; +static const struct firmware rtl838x_8214fc_fw; +static const struct firmware rtl838x_8218b_fw; + +static u64 disable_polling(int port) +{ + u64 saved_state; + + mutex_lock(&poll_lock); + + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + saved_state = sw_r32(RTL838X_SMI_POLL_CTRL); + sw_w32_mask(BIT(port), 0, RTL838X_SMI_POLL_CTRL); + break; + case RTL8390_FAMILY_ID: + saved_state = sw_r32(RTL839X_SMI_PORT_POLLING_CTRL + 4); + saved_state <<= 32; + saved_state |= sw_r32(RTL839X_SMI_PORT_POLLING_CTRL); + sw_w32_mask(BIT(port % 32), 0, + RTL839X_SMI_PORT_POLLING_CTRL + ((port >> 5) << 2)); + break; + case RTL9300_FAMILY_ID: + saved_state = sw_r32(RTL930X_SMI_POLL_CTRL); + sw_w32_mask(BIT(port), 0, RTL930X_SMI_POLL_CTRL); + break; + case RTL9310_FAMILY_ID: + pr_warn("%s not implemented for RTL931X\n", __func__); + break; + } + + mutex_unlock(&poll_lock); + + return saved_state; +} + +static int resume_polling(u64 saved_state) +{ + mutex_lock(&poll_lock); + + switch (soc_info.family) { + case RTL8380_FAMILY_ID: + sw_w32(saved_state, RTL838X_SMI_POLL_CTRL); + break; + case RTL8390_FAMILY_ID: + sw_w32(saved_state >> 32, RTL839X_SMI_PORT_POLLING_CTRL + 4); + sw_w32(saved_state, RTL839X_SMI_PORT_POLLING_CTRL); + break; + case RTL9300_FAMILY_ID: + sw_w32(saved_state, RTL930X_SMI_POLL_CTRL); + break; + case RTL9310_FAMILY_ID: + pr_warn("%s not implemented for RTL931X\n", __func__); + break; + } + + mutex_unlock(&poll_lock); + + return 0; +} + +static void rtl8380_int_phy_on_off(struct phy_device *phydev, bool on) +{ + phy_modify(phydev, 0, BIT(11), on?0:BIT(11)); +} + +static void rtl8380_rtl8214fc_on_off(struct phy_device *phydev, bool on) +{ + /* fiber ports */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_FIBRE); + phy_modify(phydev, 0x10, BIT(11), on?0:BIT(11)); + + /* copper ports */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + phy_modify_paged(phydev, RTL821X_PAGE_POWER, 0x10, BIT(11), on?0:BIT(11)); +} + +static void rtl8380_phy_reset(struct phy_device *phydev) +{ + phy_modify(phydev, 0, BIT(15), BIT(15)); +} + +// The access registers for SDS_MODE_SEL and the LSB for each SDS within +u16 rtl9300_sds_regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, 0x02a0, 0x02a0, 0x02a0, 0x02a0, + 0x02A4, 0x02A4, 0x0198, 0x0198 }; +u8 rtl9300_sds_lsb[] = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6}; + +/* + * Reset the SerDes by powering it off and set a new operations mode + * of the SerDes. 0x1f is off. Other modes are + * 0x02: SGMII 0x04: 1000BX_FIBER 0x05: FIBER100 + * 0x06: QSGMII 0x09: RSGMII 0x0d: USXGMII + * 0x10: XSGMII 0x12: HISGMII 0x16: 2500Base_X + * 0x17: RXAUI_LITE 0x19: RXAUI_PLUS 0x1a: 10G Base-R + * 0x1b: 10GR1000BX_AUTO 0x1f: OFF + */ +void rtl9300_sds_rst(int sds_num, u32 mode) +{ + pr_info("%s %d\n", __func__, mode); + if (sds_num < 0 || sds_num > 11) { + pr_err("Wrong SerDes number: %d\n", sds_num); + return; + } + + sw_w32_mask(0x1f << rtl9300_sds_lsb[sds_num], 0x1f << rtl9300_sds_lsb[sds_num], + rtl9300_sds_regs[sds_num]); + mdelay(10); + + sw_w32_mask(0x1f << rtl9300_sds_lsb[sds_num], mode << rtl9300_sds_lsb[sds_num], + rtl9300_sds_regs[sds_num]); + mdelay(10); + + pr_debug("%s: 194:%08x 198:%08x 2a0:%08x 2a4:%08x\n", __func__, + sw_r32(0x194), sw_r32(0x198), sw_r32(0x2a0), sw_r32(0x2a4)); +} + +void rtl9300_sds_set(int sds_num, u32 mode) +{ + pr_info("%s %d\n", __func__, mode); + if (sds_num < 0 || sds_num > 11) { + pr_err("Wrong SerDes number: %d\n", sds_num); + return; + } + + sw_w32_mask(0x1f << rtl9300_sds_lsb[sds_num], mode << rtl9300_sds_lsb[sds_num], + rtl9300_sds_regs[sds_num]); + mdelay(10); + + pr_debug("%s: 194:%08x 198:%08x 2a0:%08x 2a4:%08x\n", __func__, + sw_r32(0x194), sw_r32(0x198), sw_r32(0x2a0), sw_r32(0x2a4)); +} + +u32 rtl9300_sds_mode_get(int sds_num) +{ + u32 v; + + if (sds_num < 0 || sds_num > 11) { + pr_err("Wrong SerDes number: %d\n", sds_num); + return 0; + } + + v = sw_r32(rtl9300_sds_regs[sds_num]); + v >>= rtl9300_sds_lsb[sds_num]; + + return v & 0x1f; +} + +/* + * On the RTL839x family of SoCs with inbuilt SerDes, these SerDes are accessed through + * a 2048 bit register that holds the contents of the PHY being simulated by the SoC. + */ +int rtl839x_read_sds_phy(int phy_addr, int phy_reg) +{ + int offset = 0; + int reg; + u32 val; + + if (phy_addr == 49) + offset = 0x100; + + /* + * For the RTL8393 internal SerDes, we simulate a PHY ID in registers 2/3 + * which would otherwise read as 0. + */ + if (soc_info.id == 0x8393) { + if (phy_reg == 2) + return 0x1c; + if (phy_reg == 3) + return 0x8393; + } + + /* + * Register RTL839X_SDS12_13_XSG0 is 2048 bit broad, the MSB (bit 15) of the + * 0th PHY register is bit 1023 (in byte 0x80). Because PHY-registers are 16 + * bit broad, we offset by reg << 1. In the SoC 2 registers are stored in + * one 32 bit register. + */ + reg = (phy_reg << 1) & 0xfc; + val = sw_r32(RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); + + if (phy_reg & 1) + val = (val >> 16) & 0xffff; + else + val &= 0xffff; + return val; +} + +/* + * On the RTL930x family of SoCs, the internal SerDes are accessed through an IO + * register which simulates commands to an internal MDIO bus. + */ +int rtl930x_read_sds_phy(int phy_addr, int page, int phy_reg) +{ + int i; + u32 cmd = phy_addr << 2 | page << 7 | phy_reg << 13 | 1; + + sw_w32(cmd, RTL930X_SDS_INDACS_CMD); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTL930X_SDS_INDACS_CMD) & 0x1)) + break; + mdelay(1); + } + + if (i >= 100) + return -EIO; + + return sw_r32(RTL930X_SDS_INDACS_DATA) & 0xffff; +} + +int rtl930x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v) +{ + int i; + u32 cmd; + + sw_w32(v, RTL930X_SDS_INDACS_DATA); + cmd = phy_addr << 2 | page << 7 | phy_reg << 13 | 0x3; + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTL930X_SDS_INDACS_CMD) & 0x1)) + break; + mdelay(1); + } + + + if (i >= 100) { + pr_info("%s ERROR !!!!!!!!!!!!!!!!!!!!\n", __func__); + return -EIO; + } + + return 0; +} + +int rtl931x_read_sds_phy(int phy_addr, int page, int phy_reg) +{ + int i; + u32 cmd = phy_addr << 2 | page << 7 | phy_reg << 13 | 1; + + pr_debug("%s: phy_addr(SDS-ID) %d, phy_reg: %d\n", __func__, phy_addr, phy_reg); + sw_w32(cmd, RTL931X_SERDES_INDRT_ACCESS_CTRL); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTL931X_SERDES_INDRT_ACCESS_CTRL) & 0x1)) + break; + mdelay(1); + } + + if (i >= 100) + return -EIO; + + pr_debug("%s: returning %04x\n", __func__, sw_r32(RTL931X_SERDES_INDRT_DATA_CTRL) & 0xffff); + return sw_r32(RTL931X_SERDES_INDRT_DATA_CTRL) & 0xffff; +} + +int rtl931x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v) +{ + int i; + u32 cmd; + + cmd = phy_addr << 2 | page << 7 | phy_reg << 13; + sw_w32(cmd, RTL931X_SERDES_INDRT_ACCESS_CTRL); + + sw_w32(v, RTL931X_SERDES_INDRT_DATA_CTRL); + + cmd = sw_r32(RTL931X_SERDES_INDRT_ACCESS_CTRL) | 0x3; + sw_w32(cmd, RTL931X_SERDES_INDRT_ACCESS_CTRL); + + for (i = 0; i < 100; i++) { + if (!(sw_r32(RTL931X_SERDES_INDRT_ACCESS_CTRL) & 0x1)) + break; + mdelay(1); + } + + if (i >= 100) + return -EIO; + + return 0; +} + +/* + * On the RTL838x SoCs, the internal SerDes is accessed through direct access to + * standard PHY registers, where a 32 bit register holds a 16 bit word as found + * in a standard page 0 of a PHY + */ +int rtl838x_read_sds_phy(int phy_addr, int phy_reg) +{ + int offset = 0; + u32 val; + + if (phy_addr == 26) + offset = 0x100; + val = sw_r32(RTL838X_SDS4_FIB_REG0 + offset + (phy_reg << 2)) & 0xffff; + + return val; +} + +int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v) +{ + int offset = 0; + int reg; + u32 val; + + if (phy_addr == 49) + offset = 0x100; + + reg = (phy_reg << 1) & 0xfc; + val = v; + if (phy_reg & 1) { + val = val << 16; + sw_w32_mask(0xffff0000, val, + RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); + } else { + sw_w32_mask(0xffff, val, + RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg); + } + + return 0; +} + +/* Read the link and speed status of the 2 internal SGMII/1000Base-X + * ports of the RTL838x SoCs + */ +static int rtl8380_read_status(struct phy_device *phydev) +{ + int err; + + err = genphy_read_status(phydev); + + if (phydev->link) { + phydev->speed = SPEED_1000; + phydev->duplex = DUPLEX_FULL; + } + + return err; +} + +/* Read the link and speed status of the 2 internal SGMII/1000Base-X + * ports of the RTL8393 SoC + */ +static int rtl8393_read_status(struct phy_device *phydev) +{ + int offset = 0; + int err; + int phy_addr = phydev->mdio.addr; + u32 v; + + err = genphy_read_status(phydev); + if (phy_addr == 49) + offset = 0x100; + + if (phydev->link) { + phydev->speed = SPEED_100; + /* Read SPD_RD_00 (bit 13) and SPD_RD_01 (bit 6) out of the internal + * PHY registers + */ + v = sw_r32(RTL839X_SDS12_13_XSG0 + offset + 0x80); + if (!(v & (1 << 13)) && (v & (1 << 6))) + phydev->speed = SPEED_1000; + phydev->duplex = DUPLEX_FULL; + } + + return err; +} + +static int rtl8226_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, RTL8XXX_PAGE_SELECT); +} + +static int rtl8226_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, RTL8XXX_PAGE_SELECT, page); +} + +static int rtl8226_read_status(struct phy_device *phydev) +{ + int ret = 0, i; + u32 val; + +// TODO: ret = genphy_read_status(phydev); +// if (ret < 0) { +// pr_info("%s: genphy_read_status failed\n", __func__); +// return ret; +// } + + // Link status must be read twice + for (i = 0; i < 2; i++) { + val = phy_read_mmd(phydev, MMD_VEND2, 0xA402); + } + phydev->link = val & BIT(2) ? 1 : 0; + if (!phydev->link) + goto out; + + // Read duplex status + val = phy_read_mmd(phydev, MMD_VEND2, 0xA434); + if (val < 0) + goto out; + phydev->duplex = !!(val & BIT(3)); + + // Read speed + val = phy_read_mmd(phydev, MMD_VEND2, 0xA434); + switch (val & 0x0630) { + case 0x0000: + phydev->speed = SPEED_10; + break; + case 0x0010: + phydev->speed = SPEED_100; + break; + case 0x0020: + phydev->speed = SPEED_1000; + break; + case 0x0200: + phydev->speed = SPEED_10000; + break; + case 0x0210: + phydev->speed = SPEED_2500; + break; + case 0x0220: + phydev->speed = SPEED_5000; + break; + default: + break; + } +out: + return ret; +} + +static int rtl8226_advertise_aneg(struct phy_device *phydev) +{ + int ret = 0; + u32 v; + + pr_info("In %s\n", __func__); + + v = phy_read_mmd(phydev, MMD_AN, 16); + if (v < 0) + goto out; + + v |= BIT(5); // HD 10M + v |= BIT(6); // FD 10M + v |= BIT(7); // HD 100M + v |= BIT(8); // FD 100M + + ret = phy_write_mmd(phydev, MMD_AN, 16, v); + + // Allow 1GBit + v = phy_read_mmd(phydev, MMD_VEND2, 0xA412); + if (v < 0) + goto out; + v |= BIT(9); // FD 1000M + + ret = phy_write_mmd(phydev, MMD_VEND2, 0xA412, v); + if (ret < 0) + goto out; + + // Allow 2.5G + v = phy_read_mmd(phydev, MMD_AN, 32); + if (v < 0) + goto out; + + v |= BIT(7); + ret = phy_write_mmd(phydev, MMD_AN, 32, v); + +out: + return ret; +} + +static int rtl8226_config_aneg(struct phy_device *phydev) +{ + int ret = 0; + u32 v; + + pr_debug("In %s\n", __func__); + if (phydev->autoneg == AUTONEG_ENABLE) { + ret = rtl8226_advertise_aneg(phydev); + if (ret) + goto out; + // AutoNegotiationEnable + v = phy_read_mmd(phydev, MMD_AN, 0); + if (v < 0) + goto out; + + v |= BIT(12); // Enable AN + ret = phy_write_mmd(phydev, MMD_AN, 0, v); + if (ret < 0) + goto out; + + // RestartAutoNegotiation + v = phy_read_mmd(phydev, MMD_VEND2, 0xA400); + if (v < 0) + goto out; + v |= BIT(9); + + ret = phy_write_mmd(phydev, MMD_VEND2, 0xA400, v); + } + +// TODO: ret = __genphy_config_aneg(phydev, ret); + +out: + return ret; +} + +static int rtl8226_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 val; + int addr = phydev->mdio.addr; + + pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled); + + val = phy_read_mmd(phydev, MMD_AN, 60); + if (e->eee_enabled) { + e->eee_enabled = !!(val & BIT(1)); + if (!e->eee_enabled) { + val = phy_read_mmd(phydev, MMD_AN, 62); + e->eee_enabled = !!(val & BIT(0)); + } + } + pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled); + + return 0; +} + +static int rtl8226_set_eee(struct phy_device *phydev, struct ethtool_eee *e) +{ + int port = phydev->mdio.addr; + u64 poll_state; + bool an_enabled; + u32 val; + + pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled); + + poll_state = disable_polling(port); + + // Remember aneg state + val = phy_read_mmd(phydev, MMD_AN, 0); + an_enabled = !!(val & BIT(12)); + + // Setup 100/1000MBit + val = phy_read_mmd(phydev, MMD_AN, 60); + if (e->eee_enabled) + val |= 0x6; + else + val &= 0x6; + phy_write_mmd(phydev, MMD_AN, 60, val); + + // Setup 2.5GBit + val = phy_read_mmd(phydev, MMD_AN, 62); + if (e->eee_enabled) + val |= 0x1; + else + val &= 0x1; + phy_write_mmd(phydev, MMD_AN, 62, val); + + // RestartAutoNegotiation + val = phy_read_mmd(phydev, MMD_VEND2, 0xA400); + val |= BIT(9); + phy_write_mmd(phydev, MMD_VEND2, 0xA400, val); + + resume_polling(poll_state); + + return 0; +} + +static struct fw_header *rtl838x_request_fw(struct phy_device *phydev, + const struct firmware *fw, + const char *name) +{ + struct device *dev = &phydev->mdio.dev; + int err; + struct fw_header *h; + uint32_t checksum, my_checksum; + + err = request_firmware(&fw, name, dev); + if (err < 0) + goto out; + + if (fw->size < sizeof(struct fw_header)) { + pr_err("Firmware size too small.\n"); + err = -EINVAL; + goto out; + } + + h = (struct fw_header *) fw->data; + pr_info("Firmware loaded. Size %d, magic: %08x\n", fw->size, h->magic); + + if (h->magic != 0x83808380) { + pr_err("Wrong firmware file: MAGIC mismatch.\n"); + goto out; + } + + checksum = h->checksum; + h->checksum = 0; + my_checksum = ~crc32(0xFFFFFFFFU, fw->data, fw->size); + if (checksum != my_checksum) { + pr_err("Firmware checksum mismatch.\n"); + err = -EINVAL; + goto out; + } + h->checksum = checksum; + + return h; +out: + dev_err(dev, "Unable to load firmware %s (%d)\n", name, err); + return NULL; +} + +static void rtl821x_phy_setup_package_broadcast(struct phy_device *phydev, bool enable) +{ + int mac = phydev->mdio.addr; + + /* select main page 0 */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL8XXX_PAGE_MAIN); + /* write to 0x8 to register 0x1d on main page 0 */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_INTERNAL); + /* select page 0x266 */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL821X_PAGE_PORT); + /* set phy id and target broadcast bitmap in register 0x16 on page 0x266 */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, 0x16, (enable?0xff00:0x00) | mac); + /* return to main page 0 */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL8XXX_PAGE_MAIN); + /* write to 0x0 to register 0x1d on main page 0 */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + mdelay(1); +} + +static int rtl8390_configure_generic(struct phy_device *phydev) +{ + int mac = phydev->mdio.addr; + u32 val, phy_id; + + val = phy_read(phydev, 2); + phy_id = val << 16; + val = phy_read(phydev, 3); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY ID */ + phy_write_paged(phydev, 31, 27, 0x0002); + val = phy_read_paged(phydev, 31, 28); + + /* Internal RTL8218B, version 2 */ + phydev_info(phydev, "Detected unknown %x\n", val); + return 0; +} + +static int rtl8380_configure_int_rtl8218b(struct phy_device *phydev) +{ + u32 val, phy_id; + int i, p, ipd_flag; + int mac = phydev->mdio.addr; + struct fw_header *h; + u32 *rtl838x_6275B_intPhy_perport; + u32 *rtl8218b_6276B_hwEsd_perport; + + val = phy_read(phydev, 2); + phy_id = val << 16; + val = phy_read(phydev, 3); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY ID */ + phy_write_paged(phydev, 31, 27, 0x0002); + val = phy_read_paged(phydev, 31, 28); + if (val != 0x6275) { + phydev_err(phydev, "Expected internal RTL8218B, found PHY-ID %x\n", val); + return -1; + } + + /* Internal RTL8218B, version 2 */ + phydev_info(phydev, "Detected internal RTL8218B\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8380_fw, FIRMWARE_838X_8380_1); + if (!h) + return -1; + + if (h->phy != 0x83800000) { + phydev_err(phydev, "Wrong firmware file: PHY mismatch.\n"); + return -1; + } + + rtl838x_6275B_intPhy_perport = (void *)h + sizeof(struct fw_header) + + h->parts[8].start; + + rtl8218b_6276B_hwEsd_perport = (void *)h + sizeof(struct fw_header) + + h->parts[9].start; + + if (sw_r32(RTL838X_DMY_REG31) == 0x1) + ipd_flag = 1; + + val = phy_read(phydev, 0); + if (val & BIT(11)) + rtl8380_int_phy_on_off(phydev, true); + else + rtl8380_phy_reset(phydev); + msleep(100); + + /* Ready PHY for patch */ + for (p = 0; p < 8; p++) { + phy_package_port_write_paged(phydev, p, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL821X_PAGE_PATCH); + phy_package_port_write_paged(phydev, p, RTL83XX_PAGE_RAW, 0x10, 0x0010); + } + msleep(500); + for (p = 0; p < 8; p++) { + for (i = 0; i < 100 ; i++) { + val = phy_package_port_read_paged(phydev, p, RTL821X_PAGE_STATE, 0x10); + if (val & 0x40) + break; + } + if (i >= 100) { + phydev_err(phydev, + "ERROR: Port %d not ready for patch.\n", + mac + p); + return -1; + } + } + for (p = 0; p < 8; p++) { + i = 0; + while (rtl838x_6275B_intPhy_perport[i * 2]) { + phy_package_port_write_paged(phydev, p, RTL83XX_PAGE_RAW, + rtl838x_6275B_intPhy_perport[i * 2], + rtl838x_6275B_intPhy_perport[i * 2 + 1]); + i++; + } + i = 0; + while (rtl8218b_6276B_hwEsd_perport[i * 2]) { + phy_package_port_write_paged(phydev, p, RTL83XX_PAGE_RAW, + rtl8218b_6276B_hwEsd_perport[i * 2], + rtl8218b_6276B_hwEsd_perport[i * 2 + 1]); + i++; + } + } + return 0; +} + +static int rtl8380_configure_ext_rtl8218b(struct phy_device *phydev) +{ + u32 val, ipd, phy_id; + int i, l; + int mac = phydev->mdio.addr; + struct fw_header *h; + u32 *rtl8380_rtl8218b_perchip; + u32 *rtl8218B_6276B_rtl8380_perport; + u32 *rtl8380_rtl8218b_perport; + + if (soc_info.family == RTL8380_FAMILY_ID && mac != 0 && mac != 16) { + phydev_err(phydev, "External RTL8218B must have PHY-IDs 0 or 16!\n"); + return -1; + } + val = phy_read(phydev, 2); + phy_id = val << 16; + val = phy_read(phydev, 3); + phy_id |= val; + pr_info("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY ID */ + phy_write_paged(phydev, 31, 27, 0x0002); + val = phy_read_paged(phydev, 31, 28); + if (val != 0x6276) { + phydev_err(phydev, "Expected external RTL8218B, found PHY-ID %x\n", val); + return -1; + } + phydev_info(phydev, "Detected external RTL8218B\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8218b_fw, FIRMWARE_838X_8218b_1); + if (!h) + return -1; + + if (h->phy != 0x8218b000) { + phydev_err(phydev, "Wrong firmware file: PHY mismatch.\n"); + return -1; + } + + rtl8380_rtl8218b_perchip = (void *)h + sizeof(struct fw_header) + + h->parts[0].start; + + rtl8218B_6276B_rtl8380_perport = (void *)h + sizeof(struct fw_header) + + h->parts[1].start; + + rtl8380_rtl8218b_perport = (void *)h + sizeof(struct fw_header) + + h->parts[2].start; + + val = phy_read(phydev, 0); + if (val & (1 << 11)) + rtl8380_int_phy_on_off(phydev, true); + else + rtl8380_phy_reset(phydev); + + msleep(100); + + /* Get Chip revision */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL8XXX_PAGE_MAIN); + phy_write_paged(phydev, RTL83XX_PAGE_RAW, 0x1b, 0x4); + val = phy_read_paged(phydev, RTL83XX_PAGE_RAW, 0x1c); + + phydev_info(phydev, "Detected chip revision %04x\n", val); + + i = 0; + while (rtl8380_rtl8218b_perchip[i * 3] + && rtl8380_rtl8218b_perchip[i * 3 + 1]) { + phy_package_port_write_paged(phydev, rtl8380_rtl8218b_perchip[i * 3], + RTL83XX_PAGE_RAW, rtl8380_rtl8218b_perchip[i * 3 + 1], + rtl8380_rtl8218b_perchip[i * 3 + 2]); + i++; + } + + /* Enable PHY */ + for (i = 0; i < 8; i++) { + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL8XXX_PAGE_MAIN); + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, 0x00, 0x1140); + } + mdelay(100); + + /* Request patch */ + for (i = 0; i < 8; i++) { + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL821X_PAGE_PATCH); + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, 0x10, 0x0010); + } + + mdelay(300); + + /* Verify patch readiness */ + for (i = 0; i < 8; i++) { + for (l = 0; l < 100; l++) { + val = phy_package_port_read_paged(phydev, i, RTL821X_PAGE_STATE, 0x10); + if (val & 0x40) + break; + } + if (l >= 100) { + phydev_err(phydev, "Could not patch PHY\n"); + return -1; + } + } + + /* Use Broadcast ID method for patching */ + rtl821x_phy_setup_package_broadcast(phydev, true); + + phy_write_paged(phydev, RTL83XX_PAGE_RAW, 30, 8); + phy_write_paged(phydev, 0x26e, 17, 0xb); + phy_write_paged(phydev, 0x26e, 16, 0x2); + mdelay(1); + ipd = phy_read_paged(phydev, 0x26e, 19); + phy_write_paged(phydev, 0, 30, 0); + ipd = (ipd >> 4) & 0xf; /* unused ? */ + + i = 0; + while (rtl8218B_6276B_rtl8380_perport[i * 2]) { + phy_write_paged(phydev, RTL83XX_PAGE_RAW, rtl8218B_6276B_rtl8380_perport[i * 2], + rtl8218B_6276B_rtl8380_perport[i * 2 + 1]); + i++; + } + + /*Disable broadcast ID*/ + rtl821x_phy_setup_package_broadcast(phydev, false); + + return 0; +} + +static int rtl8218b_ext_match_phy_device(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + /* Both the RTL8214FC and the external RTL8218B have the same + * PHY ID. On the RTL838x, the RTL8218B can only be attached_dev + * at PHY IDs 0-7, while the RTL8214FC must be attached via + * the pair of SGMII/1000Base-X with higher PHY-IDs + */ + if (soc_info.family == RTL8380_FAMILY_ID) + return phydev->phy_id == PHY_ID_RTL8218B_E && addr < 8; + else + return phydev->phy_id == PHY_ID_RTL8218B_E; +} + +static bool rtl8214fc_media_is_fibre(struct phy_device *phydev) +{ + int mac = phydev->mdio.addr; + + static int reg[] = {16, 19, 20, 21}; + u32 val; + + phy_package_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_INTERNAL); + val = phy_package_read_paged(phydev, RTL821X_PAGE_PORT, reg[mac % 4]); + phy_package_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + + if (val & BIT(11)) + return false; + + return true; +} + +static void rtl8214fc_power_set(struct phy_device *phydev, int port, bool on) +{ + char *state = on ? "on" : "off"; + + if (port == PORT_FIBRE) { + pr_info("%s: Powering %s FIBRE (port %d)\n", __func__, state, phydev->mdio.addr); + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_FIBRE); + } else { + pr_info("%s: Powering %s COPPER (port %d)\n", __func__, state, phydev->mdio.addr); + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + } + + if (on) { + phy_modify_paged(phydev, RTL821X_PAGE_POWER, 0x10, BIT(11), 0); + } else { + phy_modify_paged(phydev, RTL821X_PAGE_POWER, 0x10, 0, BIT(11)); + } + + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); +} + +static int rtl8214fc_suspend(struct phy_device *phydev) +{ + rtl8214fc_power_set(phydev, PORT_MII, false); + rtl8214fc_power_set(phydev, PORT_FIBRE, false); + + return 0; +} + +static int rtl8214fc_resume(struct phy_device *phydev) +{ + if (rtl8214fc_media_is_fibre(phydev)) { + rtl8214fc_power_set(phydev, PORT_MII, false); + rtl8214fc_power_set(phydev, PORT_FIBRE, true); + } else { + rtl8214fc_power_set(phydev, PORT_FIBRE, false); + rtl8214fc_power_set(phydev, PORT_MII, true); + } + + return 0; +} + +static void rtl8214fc_media_set(struct phy_device *phydev, bool set_fibre) +{ + int mac = phydev->mdio.addr; + + static int reg[] = {16, 19, 20, 21}; + int val; + + pr_info("%s: port %d, set_fibre: %d\n", __func__, mac, set_fibre); + phy_package_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_INTERNAL); + val = phy_package_read_paged(phydev, RTL821X_PAGE_PORT, reg[mac % 4]); + + val |= BIT(10); + if (set_fibre) { + val &= ~BIT(11); + } else { + val |= BIT(11); + } + + phy_package_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_INTERNAL); + phy_package_write_paged(phydev, RTL821X_PAGE_PORT, reg[mac % 4], val); + phy_package_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + + if (!phydev->suspended) { + if (set_fibre) { + rtl8214fc_power_set(phydev, PORT_MII, false); + rtl8214fc_power_set(phydev, PORT_FIBRE, true); + } else { + rtl8214fc_power_set(phydev, PORT_FIBRE, false); + rtl8214fc_power_set(phydev, PORT_MII, true); + } + } +} + +static int rtl8214fc_set_port(struct phy_device *phydev, int port) +{ + bool is_fibre = (port == PORT_FIBRE ? true : false); + int addr = phydev->mdio.addr; + + pr_debug("%s port %d to %d\n", __func__, addr, port); + + rtl8214fc_media_set(phydev, is_fibre); + return 0; +} + +static int rtl8214fc_get_port(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + pr_debug("%s: port %d\n", __func__, addr); + if (rtl8214fc_media_is_fibre(phydev)) + return PORT_FIBRE; + return PORT_MII; +} + +/* + * Enable EEE on the RTL8218B PHYs + * The method used is not the preferred way (which would be based on the MAC-EEE state, + * but the only way that works since the kernel first enables EEE in the MAC + * and then sets up the PHY. The MAC-based approach would require the oppsite. + */ +void rtl8218d_eee_set(struct phy_device *phydev, bool enable) +{ + u32 val; + bool an_enabled; + + pr_debug("In %s %d, enable %d\n", __func__, phydev->mdio.addr, enable); + /* Set GPHY page to copper */ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + + val = phy_read(phydev, 0); + an_enabled = val & BIT(12); + + /* Enable 100M (bit 1) / 1000M (bit 2) EEE */ + val = phy_read_mmd(phydev, 7, 60); + val |= BIT(2) | BIT(1); + phy_write_mmd(phydev, 7, 60, enable ? 0x6 : 0); + + /* 500M EEE ability */ + val = phy_read_paged(phydev, RTL821X_PAGE_GPHY, 20); + if (enable) + val |= BIT(7); + else + val &= ~BIT(7); + phy_write_paged(phydev, RTL821X_PAGE_GPHY, 20, val); + + /* Restart AN if enabled */ + if (an_enabled) { + val = phy_read(phydev, 0); + val |= BIT(9); + phy_write(phydev, 0, val); + } + + /* GPHY page back to auto*/ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); +} + +static int rtl8218b_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 val; + int addr = phydev->mdio.addr; + + pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled); + + /* Set GPHY page to copper */ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + + val = phy_read_paged(phydev, 7, 60); + if (e->eee_enabled) { + // Verify vs MAC-based EEE + e->eee_enabled = !!(val & BIT(7)); + if (!e->eee_enabled) { + val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25); + e->eee_enabled = !!(val & BIT(4)); + } + } + pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled); + + /* GPHY page to auto */ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + + return 0; +} + +static int rtl8218d_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 val; + int addr = phydev->mdio.addr; + + pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled); + + /* Set GPHY page to copper */ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + + val = phy_read_paged(phydev, 7, 60); + if (e->eee_enabled) + e->eee_enabled = !!(val & BIT(7)); + pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled); + + /* GPHY page to auto */ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + + return 0; +} + +static int rtl8214fc_set_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + u32 poll_state; + int port = phydev->mdio.addr; + bool an_enabled; + u32 val; + + pr_debug("In %s port %d, enabled %d\n", __func__, port, e->eee_enabled); + + if (rtl8214fc_media_is_fibre(phydev)) { + netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", port); + return -ENOTSUPP; + } + + poll_state = disable_polling(port); + + /* Set GPHY page to copper */ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + + // Get auto-negotiation status + val = phy_read(phydev, 0); + an_enabled = val & BIT(12); + + pr_info("%s: aneg: %d\n", __func__, an_enabled); + val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25); + val &= ~BIT(5); // Use MAC-based EEE + phy_write_paged(phydev, RTL821X_PAGE_MAC, 25, val); + + /* Enable 100M (bit 1) / 1000M (bit 2) EEE */ + phy_write_paged(phydev, 7, 60, e->eee_enabled ? 0x6 : 0); + + /* 500M EEE ability */ + val = phy_read_paged(phydev, RTL821X_PAGE_GPHY, 20); + if (e->eee_enabled) + val |= BIT(7); + else + val &= ~BIT(7); + + phy_write_paged(phydev, RTL821X_PAGE_GPHY, 20, val); + + /* Restart AN if enabled */ + if (an_enabled) { + pr_info("%s: doing aneg\n", __func__); + val = phy_read(phydev, 0); + val |= BIT(9); + phy_write(phydev, 0, val); + } + + /* GPHY page back to auto*/ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + + resume_polling(poll_state); + + return 0; +} + +static int rtl8214fc_get_eee(struct phy_device *phydev, + struct ethtool_eee *e) +{ + int addr = phydev->mdio.addr; + + pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled); + if (rtl8214fc_media_is_fibre(phydev)) { + netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr); + return -ENOTSUPP; + } + + return rtl8218b_get_eee(phydev, e); +} + +static int rtl8218b_set_eee(struct phy_device *phydev, struct ethtool_eee *e) +{ + int port = phydev->mdio.addr; + u64 poll_state; + u32 val; + bool an_enabled; + + pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled); + + poll_state = disable_polling(port); + + /* Set GPHY page to copper */ + phy_write(phydev, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + val = phy_read(phydev, 0); + an_enabled = val & BIT(12); + + if (e->eee_enabled) { + /* 100/1000M EEE Capability */ + phy_write(phydev, 13, 0x0007); + phy_write(phydev, 14, 0x003C); + phy_write(phydev, 13, 0x4007); + phy_write(phydev, 14, 0x0006); + + val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25); + val |= BIT(4); + phy_write_paged(phydev, RTL821X_PAGE_MAC, 25, val); + } else { + /* 100/1000M EEE Capability */ + phy_write(phydev, 13, 0x0007); + phy_write(phydev, 14, 0x003C); + phy_write(phydev, 13, 0x0007); + phy_write(phydev, 14, 0x0000); + + val = phy_read_paged(phydev, RTL821X_PAGE_MAC, 25); + val &= ~BIT(4); + phy_write_paged(phydev, RTL821X_PAGE_MAC, 25, val); + } + + /* Restart AN if enabled */ + if (an_enabled) { + val = phy_read(phydev, 0); + val |= BIT(9); + phy_write(phydev, 0, val); + } + + /* GPHY page back to auto*/ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + + pr_info("%s done\n", __func__); + resume_polling(poll_state); + + return 0; +} + +static int rtl8218d_set_eee(struct phy_device *phydev, struct ethtool_eee *e) +{ + int addr = phydev->mdio.addr; + u64 poll_state; + + pr_info("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled); + + poll_state = disable_polling(addr); + + rtl8218d_eee_set(phydev, (bool) e->eee_enabled); + + resume_polling(poll_state); + + return 0; +} + +static int rtl8214c_match_phy_device(struct phy_device *phydev) +{ + return phydev->phy_id == PHY_ID_RTL8214C; +} + +static int rtl8380_configure_rtl8214c(struct phy_device *phydev) +{ + u32 phy_id, val; + int mac = phydev->mdio.addr; + + val = phy_read(phydev, 2); + phy_id = val << 16; + val = phy_read(phydev, 3); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + phydev_info(phydev, "Detected external RTL8214C\n"); + + /* GPHY auto conf */ + phy_write_paged(phydev, RTL821X_PAGE_GPHY, RTL821XINT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + return 0; +} + +static int rtl8380_configure_rtl8214fc(struct phy_device *phydev) +{ + u32 phy_id, val, page = 0; + int i, l; + int mac = phydev->mdio.addr; + struct fw_header *h; + u32 *rtl8380_rtl8214fc_perchip; + u32 *rtl8380_rtl8214fc_perport; + + val = phy_read(phydev, 2); + phy_id = val << 16; + val = phy_read(phydev, 3); + phy_id |= val; + pr_debug("Phy on MAC %d: %x\n", mac, phy_id); + + /* Read internal PHY id */ + phy_write_paged(phydev, 0, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + phy_write_paged(phydev, 0x1f, 0x1b, 0x0002); + val = phy_read_paged(phydev, 0x1f, 0x1c); + if (val != 0x6276) { + phydev_err(phydev, "Expected external RTL8214FC, found PHY-ID %x\n", val); + return -1; + } + phydev_info(phydev, "Detected external RTL8214FC\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8214fc_fw, FIRMWARE_838X_8214FC_1); + if (!h) + return -1; + + if (h->phy != 0x8214fc00) { + phydev_err(phydev, "Wrong firmware file: PHY mismatch.\n"); + return -1; + } + + rtl8380_rtl8214fc_perchip = (void *)h + sizeof(struct fw_header) + + h->parts[0].start; + + rtl8380_rtl8214fc_perport = (void *)h + sizeof(struct fw_header) + + h->parts[1].start; + + /* detect phy version */ + phy_write_paged(phydev, RTL83XX_PAGE_RAW, 27, 0x0004); + val = phy_read_paged(phydev, RTL83XX_PAGE_RAW, 28); + + val = phy_read(phydev, 16); + if (val & (1 << 11)) + rtl8380_rtl8214fc_on_off(phydev, true); + else + rtl8380_phy_reset(phydev); + + msleep(100); + phy_write_paged(phydev, 0, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + + i = 0; + while (rtl8380_rtl8214fc_perchip[i * 3] + && rtl8380_rtl8214fc_perchip[i * 3 + 1]) { + if (rtl8380_rtl8214fc_perchip[i * 3 + 1] == 0x1f) + page = rtl8380_rtl8214fc_perchip[i * 3 + 2]; + if (rtl8380_rtl8214fc_perchip[i * 3 + 1] == 0x13 && page == 0x260) { + val = phy_read_paged(phydev, 0x260, 13); + val = (val & 0x1f00) | (rtl8380_rtl8214fc_perchip[i * 3 + 2] + & 0xe0ff); + phy_write_paged(phydev, RTL83XX_PAGE_RAW, + rtl8380_rtl8214fc_perchip[i * 3 + 1], val); + } else { + phy_write_paged(phydev, RTL83XX_PAGE_RAW, + rtl8380_rtl8214fc_perchip[i * 3 + 1], + rtl8380_rtl8214fc_perchip[i * 3 + 2]); + } + i++; + } + + /* Force copper medium */ + for (i = 0; i < 4; i++) { + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL8XXX_PAGE_MAIN); + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_COPPER); + } + + /* Enable PHY */ + for (i = 0; i < 4; i++) { + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL8XXX_PAGE_MAIN); + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, 0x00, 0x1140); + } + mdelay(100); + + /* Disable Autosensing */ + for (i = 0; i < 4; i++) { + for (l = 0; l < 100; l++) { + val = phy_package_port_read_paged(phydev, i, RTL821X_PAGE_GPHY, 0x10); + if ((val & 0x7) >= 3) + break; + } + if (l >= 100) { + phydev_err(phydev, "Could not disable autosensing\n"); + return -1; + } + } + + /* Request patch */ + for (i = 0; i < 4; i++) { + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL821X_PAGE_PATCH); + phy_package_port_write_paged(phydev, i, RTL83XX_PAGE_RAW, 0x10, 0x0010); + } + mdelay(300); + + /* Verify patch readiness */ + for (i = 0; i < 4; i++) { + for (l = 0; l < 100; l++) { + val = phy_package_port_read_paged(phydev, i, RTL821X_PAGE_STATE, 0x10); + if (val & 0x40) + break; + } + if (l >= 100) { + phydev_err(phydev, "Could not patch PHY\n"); + return -1; + } + } + /* Use Broadcast ID method for patching */ + rtl821x_phy_setup_package_broadcast(phydev, true); + + i = 0; + while (rtl8380_rtl8214fc_perport[i * 2]) { + phy_write_paged(phydev, RTL83XX_PAGE_RAW, rtl8380_rtl8214fc_perport[i * 2], + rtl8380_rtl8214fc_perport[i * 2 + 1]); + i++; + } + + /*Disable broadcast ID*/ + rtl821x_phy_setup_package_broadcast(phydev, false); + + /* Auto medium selection */ + for (i = 0; i < 4; i++) { + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL8XXX_PAGE_SELECT, RTL8XXX_PAGE_MAIN); + phy_write_paged(phydev, RTL83XX_PAGE_RAW, RTL821XEXT_MEDIA_PAGE_SELECT, RTL821X_MEDIA_PAGE_AUTO); + } + + return 0; +} + +static int rtl8214fc_match_phy_device(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + return phydev->phy_id == PHY_ID_RTL8214FC && addr >= 24; +} + +static int rtl8380_configure_serdes(struct phy_device *phydev) +{ + u32 v; + u32 sds_conf_value; + int i; + struct fw_header *h; + u32 *rtl8380_sds_take_reset; + u32 *rtl8380_sds_common; + u32 *rtl8380_sds01_qsgmii_6275b; + u32 *rtl8380_sds23_qsgmii_6275b; + u32 *rtl8380_sds4_fiber_6275b; + u32 *rtl8380_sds5_fiber_6275b; + u32 *rtl8380_sds_reset; + u32 *rtl8380_sds_release_reset; + + phydev_info(phydev, "Detected internal RTL8380 SERDES\n"); + + h = rtl838x_request_fw(phydev, &rtl838x_8218b_fw, FIRMWARE_838X_8380_1); + if (!h) + return -1; + + if (h->magic != 0x83808380) { + phydev_err(phydev, "Wrong firmware file: magic number mismatch.\n"); + return -1; + } + + rtl8380_sds_take_reset = (void *)h + sizeof(struct fw_header) + + h->parts[0].start; + + rtl8380_sds_common = (void *)h + sizeof(struct fw_header) + + h->parts[1].start; + + rtl8380_sds01_qsgmii_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[2].start; + + rtl8380_sds23_qsgmii_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[3].start; + + rtl8380_sds4_fiber_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[4].start; + + rtl8380_sds5_fiber_6275b = (void *)h + sizeof(struct fw_header) + + h->parts[5].start; + + rtl8380_sds_reset = (void *)h + sizeof(struct fw_header) + + h->parts[6].start; + + rtl8380_sds_release_reset = (void *)h + sizeof(struct fw_header) + + h->parts[7].start; + + /* Back up serdes power off value */ + sds_conf_value = sw_r32(RTL838X_SDS_CFG_REG); + pr_info("SDS power down value: %x\n", sds_conf_value); + + /* take serdes into reset */ + i = 0; + while (rtl8380_sds_take_reset[2 * i]) { + sw_w32(rtl8380_sds_take_reset[2 * i + 1], rtl8380_sds_take_reset[2 * i]); + i++; + udelay(1000); + } + + /* apply common serdes patch */ + i = 0; + while (rtl8380_sds_common[2 * i]) { + sw_w32(rtl8380_sds_common[2 * i + 1], rtl8380_sds_common[2 * i]); + i++; + udelay(1000); + } + + /* internal R/W enable */ + sw_w32(3, RTL838X_INT_RW_CTRL); + + /* SerDes ports 4 and 5 are FIBRE ports */ + sw_w32_mask(0x7 | 0x38, 1 | (1 << 3), RTL838X_INT_MODE_CTRL); + + /* SerDes module settings, SerDes 0-3 are QSGMII */ + v = 0x6 << 25 | 0x6 << 20 | 0x6 << 15 | 0x6 << 10; + /* SerDes 4 and 5 are 1000BX FIBRE */ + v |= 0x4 << 5 | 0x4; + sw_w32(v, RTL838X_SDS_MODE_SEL); + + pr_info("PLL control register: %x\n", sw_r32(RTL838X_PLL_CML_CTRL)); + sw_w32_mask(0xfffffff0, 0xaaaaaaaf & 0xf, RTL838X_PLL_CML_CTRL); + i = 0; + while (rtl8380_sds01_qsgmii_6275b[2 * i]) { + sw_w32(rtl8380_sds01_qsgmii_6275b[2 * i + 1], + rtl8380_sds01_qsgmii_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds23_qsgmii_6275b[2 * i]) { + sw_w32(rtl8380_sds23_qsgmii_6275b[2 * i + 1], rtl8380_sds23_qsgmii_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds4_fiber_6275b[2 * i]) { + sw_w32(rtl8380_sds4_fiber_6275b[2 * i + 1], rtl8380_sds4_fiber_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds5_fiber_6275b[2 * i]) { + sw_w32(rtl8380_sds5_fiber_6275b[2 * i + 1], rtl8380_sds5_fiber_6275b[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds_reset[2 * i]) { + sw_w32(rtl8380_sds_reset[2 * i + 1], rtl8380_sds_reset[2 * i]); + i++; + } + + i = 0; + while (rtl8380_sds_release_reset[2 * i]) { + sw_w32(rtl8380_sds_release_reset[2 * i + 1], rtl8380_sds_release_reset[2 * i]); + i++; + } + + pr_info("SDS power down value now: %x\n", sw_r32(RTL838X_SDS_CFG_REG)); + sw_w32(sds_conf_value, RTL838X_SDS_CFG_REG); + + pr_info("Configuration of SERDES done\n"); + return 0; +} + +static int rtl8390_configure_serdes(struct phy_device *phydev) +{ + phydev_info(phydev, "Detected internal RTL8390 SERDES\n"); + + /* In autoneg state, force link, set SR4_CFG_EN_LINK_FIB1G */ + sw_w32_mask(0, 1 << 18, RTL839X_SDS12_13_XSG0 + 0x0a); + + /* Disable EEE: Clear FRE16_EEE_RSG_FIB1G, FRE16_EEE_STD_FIB1G, + * FRE16_C1_PWRSAV_EN_FIB1G, FRE16_C2_PWRSAV_EN_FIB1G + * and FRE16_EEE_QUIET_FIB1G + */ + sw_w32_mask(0x1f << 10, 0, RTL839X_SDS12_13_XSG0 + 0xe0); + + return 0; +} + +void rtl9300_sds_field_w(int sds, u32 page, u32 reg, int end_bit, int start_bit, u32 v) +{ + int l = end_bit - start_bit + 1; + u32 data = v; + + if (l < 32) { + u32 mask = BIT(l) - 1; + + data = rtl930x_read_sds_phy(sds, page, reg); + data &= ~(mask << start_bit); + data |= (v & mask) << start_bit; + } + + rtl930x_write_sds_phy(sds, page, reg, data); +} + +u32 rtl9300_sds_field_r(int sds, u32 page, u32 reg, int end_bit, int start_bit) +{ + int l = end_bit - start_bit + 1; + u32 v = rtl930x_read_sds_phy(sds, page, reg); + + if (l >= 32) + return v; + + return (v >> start_bit) & (BIT(l) - 1); +} + +/* Read the link and speed status of the internal SerDes of the RTL9300 + */ +static int rtl9300_read_status(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int phy_addr = phydev->mdio.addr; + struct device_node *dn; + u32 sds_num = 0, status, latch_status, mode; + + if (dev->of_node) { + dn = dev->of_node; + + if (of_property_read_u32(dn, "sds", &sds_num)) + sds_num = -1; + pr_info("%s: Port %d, SerDes is %d\n", __func__, phy_addr, sds_num); + } else { + dev_err(dev, "No DT node.\n"); + return -EINVAL; + } + + if (sds_num < 0) + return 0; + + mode = rtl9300_sds_mode_get(sds_num); + pr_info("%s got SDS mode %02x\n", __func__, mode); + if (mode == 0x1a) { // 10GR mode + status = rtl9300_sds_field_r(sds_num, 0x5, 0, 12, 12); + latch_status = rtl9300_sds_field_r(sds_num, 0x4, 1, 2, 2); + status |= rtl9300_sds_field_r(sds_num, 0x5, 0, 12, 12); + latch_status |= rtl9300_sds_field_r(sds_num, 0x4, 1, 2, 2); + } else { + status = rtl9300_sds_field_r(sds_num, 0x1, 29, 8, 0); + latch_status = rtl9300_sds_field_r(sds_num, 0x1, 30, 8, 0); + status |= rtl9300_sds_field_r(sds_num, 0x1, 29, 8, 0); + latch_status |= rtl9300_sds_field_r(sds_num, 0x1, 30, 8, 0); + } + + pr_info("%s link status: status: %d, latch %d\n", __func__, status, latch_status); + + if (latch_status) { + phydev->link = true; + if (mode == 0x1a) + phydev->speed = SPEED_10000; + else + phydev->speed = SPEED_1000; + + phydev->duplex = DUPLEX_FULL; + } + + return 0; +} + +void rtl930x_sds_rx_rst(int sds_num, phy_interface_t phy_if) +{ + int page = 0x2e; // 10GR and USXGMII + + if (phy_if == PHY_INTERFACE_MODE_1000BASEX) + page = 0x24; + + rtl9300_sds_field_w(sds_num, page, 0x15, 4, 4, 0x1); + mdelay(5); + rtl9300_sds_field_w(sds_num, page, 0x15, 4, 4, 0x0); +} + +/* + * Force PHY modes on 10GBit Serdes + */ +void rtl9300_force_sds_mode(int sds, phy_interface_t phy_if) +{ + int sds_mode; + bool lc_on; + int i, lc_value; + int lane_0 = (sds % 2) ? sds - 1 : sds; + u32 v, cr_0, cr_1, cr_2; + u32 m_bit, l_bit; + + pr_info("%s: SDS: %d, mode %d\n", __func__, sds, phy_if); + switch (phy_if) { + case PHY_INTERFACE_MODE_SGMII: + sds_mode = 0x2; + lc_on = false; + lc_value = 0x1; + break; + + case PHY_INTERFACE_MODE_HSGMII: + sds_mode = 0x12; + lc_value = 0x3; + // Configure LC + break; + + case PHY_INTERFACE_MODE_1000BASEX: + sds_mode = 0x04; + lc_on = false; + break; + + case PHY_INTERFACE_MODE_2500BASEX: + sds_mode = 0x16; + lc_value = 0x3; + // Configure LC + break; + + case PHY_INTERFACE_MODE_10GBASER: + sds_mode = 0x1a; + lc_on = true; + lc_value = 0x5; + break; + + case PHY_INTERFACE_MODE_NA: + // This will disable SerDes + sds_mode = 0x1f; + break; + + default: + pr_err("%s: unknown serdes mode: %s\n", + __func__, phy_modes(phy_if)); + return; + } + + pr_info("%s --------------------- serdes %d forcing to %x ...\n", __func__, sds, sds_mode); + // Power down SerDes + rtl9300_sds_field_w(sds, 0x20, 0, 7, 6, 0x3); + if (sds == 5) pr_info("%s after %x\n", __func__, rtl930x_read_sds_phy(sds, 0x20, 0)); + + if (sds == 5) pr_info("%s a %x\n", __func__, rtl930x_read_sds_phy(sds, 0x1f, 9)); + // Force mode enable + rtl9300_sds_field_w(sds, 0x1f, 9, 6, 6, 0x1); + if (sds == 5) pr_info("%s b %x\n", __func__, rtl930x_read_sds_phy(sds, 0x1f, 9)); + + /* SerDes off */ + rtl9300_sds_field_w(sds, 0x1f, 9, 11, 7, 0x1f); + + if (phy_if == PHY_INTERFACE_MODE_NA) + return; + + if (sds == 5) pr_info("%s c %x\n", __func__, rtl930x_read_sds_phy(sds, 0x20, 18)); + // Enable LC and ring + rtl9300_sds_field_w(lane_0, 0x20, 18, 3, 0, 0xf); + + if (sds == lane_0) + rtl9300_sds_field_w(lane_0, 0x20, 18, 5, 4, 0x1); + else + rtl9300_sds_field_w(lane_0, 0x20, 18, 7, 6, 0x1); + + rtl9300_sds_field_w(sds, 0x20, 0, 5, 4, 0x3); + + if (lc_on) + rtl9300_sds_field_w(lane_0, 0x20, 18, 11, 8, lc_value); + else + rtl9300_sds_field_w(lane_0, 0x20, 18, 15, 12, lc_value); + + // Force analog LC & ring on + rtl9300_sds_field_w(lane_0, 0x21, 11, 3, 0, 0xf); + + v = lc_on ? 0x3 : 0x1; + + if (sds == lane_0) + rtl9300_sds_field_w(lane_0, 0x20, 18, 5, 4, v); + else + rtl9300_sds_field_w(lane_0, 0x20, 18, 7, 6, v); + + // Force SerDes mode + rtl9300_sds_field_w(sds, 0x1f, 9, 6, 6, 1); + rtl9300_sds_field_w(sds, 0x1f, 9, 11, 7, sds_mode); + + // Toggle LC or Ring + for (i = 0; i < 20; i++) { + mdelay(200); + + rtl930x_write_sds_phy(lane_0, 0x1f, 2, 53); + + m_bit = (lane_0 == sds) ? (4) : (5); + l_bit = (lane_0 == sds) ? (4) : (5); + + cr_0 = rtl9300_sds_field_r(lane_0, 0x1f, 20, m_bit, l_bit); + mdelay(10); + cr_1 = rtl9300_sds_field_r(lane_0, 0x1f, 20, m_bit, l_bit); + mdelay(10); + cr_2 = rtl9300_sds_field_r(lane_0, 0x1f, 20, m_bit, l_bit); + + if (cr_0 && cr_1 && cr_2) { + u32 t; + if (phy_if != PHY_INTERFACE_MODE_10GBASER) + break; + + t = rtl9300_sds_field_r(sds, 0x6, 0x1, 2, 2); + rtl9300_sds_field_w(sds, 0x6, 0x1, 2, 2, 0x1); + + // Reset FSM + rtl9300_sds_field_w(sds, 0x6, 0x2, 12, 12, 0x1); + mdelay(10); + rtl9300_sds_field_w(sds, 0x6, 0x2, 12, 12, 0x0); + mdelay(10); + + // Need to read this twice + v = rtl9300_sds_field_r(sds, 0x5, 0, 12, 12); + v = rtl9300_sds_field_r(sds, 0x5, 0, 12, 12); + + rtl9300_sds_field_w(sds, 0x6, 0x1, 2, 2, t); + + // Reset FSM again + rtl9300_sds_field_w(sds, 0x6, 0x2, 12, 12, 0x1); + mdelay(10); + rtl9300_sds_field_w(sds, 0x6, 0x2, 12, 12, 0x0); + mdelay(10); + + if (v == 1) + break; + } + + m_bit = (phy_if == PHY_INTERFACE_MODE_10GBASER) ? 3 : 1; + l_bit = (phy_if == PHY_INTERFACE_MODE_10GBASER) ? 2 : 0; + + rtl9300_sds_field_w(lane_0, 0x21, 11, m_bit, l_bit, 0x2); + mdelay(10); + rtl9300_sds_field_w(lane_0, 0x21, 11, m_bit, l_bit, 0x3); + } + + rtl930x_sds_rx_rst(sds, phy_if); + + // Re-enable power + rtl9300_sds_field_w(sds, 0x20, 0, 7, 6, 0); + + pr_info("%s --------------------- serdes %d forced to %x DONE\n", __func__, sds, sds_mode); +} + +void rtl9300_sds_tx_config(int sds, phy_interface_t phy_if) +{ + // parameters: rtl9303_80G_txParam_s2 + int impedance = 0x8; + int pre_amp = 0x2; + int main_amp = 0x9; + int post_amp = 0x2; + int pre_en = 0x1; + int post_en = 0x1; + int page; + + switch(phy_if) { + case PHY_INTERFACE_MODE_1000BASEX: + page = 0x25; + break; + case PHY_INTERFACE_MODE_HSGMII: + case PHY_INTERFACE_MODE_2500BASEX: + page = 0x29; + break; + case PHY_INTERFACE_MODE_10GBASER: + page = 0x2f; + break; + default: + pr_err("%s: unsupported PHY mode\n", __func__); + return; + } + + rtl9300_sds_field_w(sds, page, 0x1, 15, 11, pre_amp); + rtl9300_sds_field_w(sds, page, 0x7, 0, 0, pre_en); + rtl9300_sds_field_w(sds, page, 0x7, 8, 4, main_amp); + rtl9300_sds_field_w(sds, page, 0x6, 4, 0, post_amp); + rtl9300_sds_field_w(sds, page, 0x7, 3, 3, post_en); + rtl9300_sds_field_w(sds, page, 0x18, 15, 12, impedance); +} + +/* + * Wait for clock ready, this assumes the SerDes is in XGMII mode + * timeout is in ms + */ +int rtl9300_sds_clock_wait(int timeout) +{ + u32 v; + unsigned long start = jiffies; + + do { + rtl9300_sds_field_w(2, 0x1f, 0x2, 15, 0, 53); + v = rtl9300_sds_field_r(2, 0x1f, 20, 5, 4); + if (v == 3) + return 0; + } while (jiffies < start + (HZ / 1000) * timeout); + + return 1; +} + +void rtl9300_serdes_mac_link_config(int sds, bool tx_normal, bool rx_normal) +{ + u32 v10, v1; + + v10 = rtl930x_read_sds_phy(sds, 6, 2); // 10GBit, page 6, reg 2 + v1 = rtl930x_read_sds_phy(sds, 0, 0); // 1GBit, page 0, reg 0 + pr_info("%s: registers before %08x %08x\n", __func__, v10, v1); + + v10 &= ~(BIT(13) | BIT(14)); + v1 &= ~(BIT(8) | BIT(9)); + + v10 |= rx_normal ? 0 : BIT(13); + v1 |= rx_normal ? 0 : BIT(9); + + v10 |= tx_normal ? 0 : BIT(14); + v1 |= tx_normal ? 0 : BIT(8); + + rtl930x_write_sds_phy(sds, 6, 2, v10); + rtl930x_write_sds_phy(sds, 0, 0, v1); + + v10 = rtl930x_read_sds_phy(sds, 6, 2); + v1 = rtl930x_read_sds_phy(sds, 0, 0); + pr_info("%s: registers after %08x %08x\n", __func__, v10, v1); +} + +void rtl9300_sds_rxcal_dcvs_manual(u32 sds_num, u32 dcvs_id, bool manual, u32 dvcs_list[]) +{ + if (manual) { + switch(dcvs_id) { + case 0: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 14, 14, 0x1); + rtl9300_sds_field_w(sds_num, 0x2f, 0x03, 5, 5, dvcs_list[0]); + rtl9300_sds_field_w(sds_num, 0x2f, 0x03, 4, 0, dvcs_list[1]); + break; + case 1: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 13, 13, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x1d, 15, 15, dvcs_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x1d, 14, 11, dvcs_list[1]); + break; + case 2: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 12, 12, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x1d, 10, 10, dvcs_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x1d, 9, 6, dvcs_list[1]); + break; + case 3: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 11, 11, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x1d, 5, 5, dvcs_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x1d, 4, 1, dvcs_list[1]); + break; + case 4: + rtl9300_sds_field_w(sds_num, 0x2e, 0x01, 15, 15, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x11, 10, 10, dvcs_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x11, 9, 6, dvcs_list[1]); + break; + case 5: + rtl9300_sds_field_w(sds_num, 0x2e, 0x02, 11, 11, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x11, 4, 4, dvcs_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x11, 3, 0, dvcs_list[1]); + break; + default: + break; + } + } else { + switch(dcvs_id) { + case 0: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 14, 14, 0x0); + break; + case 1: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 13, 13, 0x0); + break; + case 2: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 12, 12, 0x0); + break; + case 3: + rtl9300_sds_field_w(sds_num, 0x2e, 0x1e, 11, 11, 0x0); + break; + case 4: + rtl9300_sds_field_w(sds_num, 0x2e, 0x01, 15, 15, 0x0); + break; + case 5: + rtl9300_sds_field_w(sds_num, 0x2e, 0x02, 11, 11, 0x0); + break; + default: + break; + } + mdelay(1); + } +} + +void rtl9300_sds_rxcal_dcvs_get(u32 sds_num, u32 dcvs_id, u32 dcvs_list[]) +{ + u32 dcvs_sign_out = 0, dcvs_coef_bin = 0; + bool dcvs_manual; + + if (!(sds_num % 2)) + rtl930x_write_sds_phy(sds_num, 0x1f, 0x2, 0x2f); + else + rtl930x_write_sds_phy(sds_num - 1, 0x1f, 0x2, 0x31); + + // ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] + rtl9300_sds_field_w(sds_num, 0x2e, 0x15, 9, 9, 0x1); + + // ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] + rtl9300_sds_field_w(sds_num, 0x21, 0x06, 11, 6, 0x20); + + switch(dcvs_id) { + case 0: + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0x22); + mdelay(1); + + // ##DCVS0 Read Out + dcvs_sign_out = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtl9300_sds_field_r(sds_num, 0x2e, 0x1e, 14, 14); + break; + + case 1: + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0x23); + mdelay(1); + + // ##DCVS0 Read Out + dcvs_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtl9300_sds_field_r(sds_num, 0x2e, 0x1e, 13, 13); + break; + + case 2: + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0x24); + mdelay(1); + + // ##DCVS0 Read Out + dcvs_sign_out = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtl9300_sds_field_r(sds_num, 0x2e, 0x1e, 12, 12); + break; + case 3: + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0x25); + mdelay(1); + + // ##DCVS0 Read Out + dcvs_sign_out = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = rtl9300_sds_field_r(sds_num, 0x2e, 0x1e, 11, 11); + break; + + case 4: + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0x2c); + mdelay(1); + + // ##DCVS0 Read Out + dcvs_sign_out = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtl9300_sds_field_r(sds_num, 0x2e, 0x01, 15, 15); + break; + + case 5: + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0x2d); + mdelay(1); + + // ##DCVS0 Read Out + dcvs_sign_out = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = rtl9300_sds_field_r(sds_num, 0x2e, 0x02, 11, 11); + break; + + default: + break; + } + + if (dcvs_sign_out) + pr_info("%s DCVS %u Sign: -", __func__, dcvs_id); + else + pr_info("%s DCVS %u Sign: +", __func__, dcvs_id); + + pr_info("DCVS %u even coefficient = %u", dcvs_id, dcvs_coef_bin); + pr_info("DCVS %u manual = %u", dcvs_id, dcvs_manual); + + dcvs_list[0] = dcvs_sign_out; + dcvs_list[1] = dcvs_coef_bin; +} + +void rtl9300_sds_rxcal_leq_manual(u32 sds_num, bool manual, u32 leq_gray) +{ + if (manual) { + rtl9300_sds_field_w(sds_num, 0x2e, 0x18, 15, 15, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x16, 14, 10, leq_gray); + } else { + rtl9300_sds_field_w(sds_num, 0x2e, 0x18, 15, 15, 0x0); + mdelay(100); + } +} + +void rtl9300_sds_rxcal_leq_offset_manual(u32 sds_num, bool manual, u32 offset) +{ + if (manual) { + rtl9300_sds_field_w(sds_num, 0x2e, 0x17, 6, 2, offset); + } else { + rtl9300_sds_field_w(sds_num, 0x2e, 0x17, 6, 2, offset); + mdelay(1); + } +} + +#define GRAY_BITS 5 +u32 rtl9300_sds_rxcal_gray_to_binary(u32 gray_code) +{ + int i, j, m; + u32 g[GRAY_BITS]; + u32 c[GRAY_BITS]; + u32 leq_binary = 0; + + for(i = 0; i < GRAY_BITS; i++) + g[i] = (gray_code & BIT(i)) >> i; + + m = GRAY_BITS - 1; + + c[m] = g[m]; + + for(i = 0; i < m; i++) { + c[i] = g[i]; + for(j = i + 1; j < GRAY_BITS; j++) + c[i] = c[i] ^ g[j]; + } + + for(i = 0; i < GRAY_BITS; i++) + leq_binary += c[i] << i; + + return leq_binary; +} + +u32 rtl9300_sds_rxcal_leq_read(int sds_num) +{ + u32 leq_gray, leq_bin; + bool leq_manual; + + if (!(sds_num % 2)) + rtl930x_write_sds_phy(sds_num, 0x1f, 0x2, 0x2f); + else + rtl930x_write_sds_phy(sds_num - 1, 0x1f, 0x2, 0x31); + + // ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] + rtl9300_sds_field_w(sds_num, 0x2e, 0x15, 9, 9, 0x1); + + // ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[0 1 x x x x] + rtl9300_sds_field_w(sds_num, 0x21, 0x06, 11, 6, 0x10); + mdelay(1); + + // ##LEQ Read Out + leq_gray = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 7, 3); + leq_manual = !!rtl9300_sds_field_r(sds_num, 0x2e, 0x18, 15, 15); + leq_bin = rtl9300_sds_rxcal_gray_to_binary(leq_gray); + + pr_info("LEQ_gray: %u, LEQ_bin: %u", leq_gray, leq_bin); + pr_info("LEQ manual: %u", leq_manual); + + return leq_bin; +} + +void rtl9300_sds_rxcal_vth_manual(u32 sds_num, bool manual, u32 vth_list[]) +{ + if (manual) { + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, 13, 13, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x13, 5, 3, vth_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x13, 2, 0, vth_list[1]); + } else { + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, 13, 13, 0x0); + mdelay(10); + } +} + +void rtl9300_sds_rxcal_vth_get(u32 sds_num, u32 vth_list[]) +{ + u32 vth_manual; + + //##Page0x1F, Reg0x02[15 0], REG_DBGO_SEL=[0x002F]; //Lane0 + //##Page0x1F, Reg0x02[15 0], REG_DBGO_SEL=[0x0031]; //Lane1 + if (!(sds_num % 2)) + rtl930x_write_sds_phy(sds_num, 0x1f, 0x2, 0x2f); + else + rtl930x_write_sds_phy(sds_num - 1, 0x1f, 0x2, 0x31); + + //##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] + rtl9300_sds_field_w(sds_num, 0x2e, 0x15, 9, 9, 0x1); + //##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] + rtl9300_sds_field_w(sds_num, 0x21, 0x06, 11, 6, 0x20); + //##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 0 0] + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0xc); + + mdelay(1); + + //##VthP & VthN Read Out + vth_list[0] = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 2, 0); // v_thp set bin + vth_list[1] = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 5, 3); // v_thn set bin + + pr_info("vth_set_bin = %d", vth_list[0]); + pr_info("vth_set_bin = %d", vth_list[1]); + + vth_manual = !!rtl9300_sds_field_r(sds_num, 0x2e, 0x0f, 13, 13); + pr_info("Vth Maunal = %d", vth_manual); +} + +void rtl9300_sds_rxcal_tap_manual(u32 sds_num, int tap_id, bool manual, u32 tap_list[]) +{ + if (manual) { + switch(tap_id) { + case 0: + //##REG0_LOAD_IN_INIT[0]=1; REG0_TAP0_INIT[5:0]=Tap0_Value + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtl9300_sds_field_w(sds_num, 0x2f, 0x03, 5, 5, tap_list[0]); + rtl9300_sds_field_w(sds_num, 0x2f, 0x03, 4, 0, tap_list[1]); + break; + case 1: + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtl9300_sds_field_w(sds_num, 0x21, 0x07, 6, 6, tap_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x09, 11, 6, tap_list[1]); + rtl9300_sds_field_w(sds_num, 0x21, 0x07, 5, 5, tap_list[2]); + rtl9300_sds_field_w(sds_num, 0x2f, 0x12, 5, 0, tap_list[3]); + break; + case 2: + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x09, 5, 5, tap_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x09, 4, 0, tap_list[1]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x0a, 11, 11, tap_list[2]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x0a, 10, 6, tap_list[3]); + break; + case 3: + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtl9300_sds_field_w(sds_num, 0x2e, 0x0a, 5, 5, tap_list[0]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x0a, 4, 0, tap_list[1]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x06, 5, 5, tap_list[2]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x06, 4, 0, tap_list[3]); + break; + case 4: + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtl9300_sds_field_w(sds_num, 0x2f, 0x01, 5, 5, tap_list[0]); + rtl9300_sds_field_w(sds_num, 0x2f, 0x01, 4, 0, tap_list[1]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x06, 11, 11, tap_list[2]); + rtl9300_sds_field_w(sds_num, 0x2e, 0x06, 10, 6, tap_list[3]); + break; + default: + break; + } + } else { + rtl9300_sds_field_w(sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x0); + mdelay(10); + } +} + +void rtl9300_sds_rxcal_tap_get(u32 sds_num, u32 tap_id, u32 tap_list[]) +{ + u32 tap0_sign_out; + u32 tap0_coef_bin; + u32 tap_sign_out_even; + u32 tap_coef_bin_even; + u32 tap_sign_out_odd; + u32 tap_coef_bin_odd; + bool tap_manual; + + if (!(sds_num % 2)) + rtl930x_write_sds_phy(sds_num, 0x1f, 0x2, 0x2f); + else + rtl930x_write_sds_phy(sds_num - 1, 0x1f, 0x2, 0x31); + + //##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] + rtl9300_sds_field_w(sds_num, 0x2e, 0x15, 9, 9, 0x1); + //##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] + rtl9300_sds_field_w(sds_num, 0x21, 0x06, 11, 6, 0x20); + + if (!tap_id) { + //##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 0 0 1] + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0); + //##Tap1 Even Read Out + mdelay(1); + tap0_sign_out = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 5, 5); + tap0_coef_bin = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 0); + + if (tap0_sign_out == 1) + pr_info("Tap0 Sign : -"); + else + pr_info("Tap0 Sign : +"); + + pr_info("tap0_coef_bin = %d", tap0_coef_bin); + + tap_list[0] = tap0_sign_out; + tap_list[1] = tap0_coef_bin; + + tap_manual = !!rtl9300_sds_field_r(sds_num, 0x2e, 0x0f, 7, 7); + pr_info("tap0 manual = %u",tap_manual); + } else { + //##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 0 0 1] + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, tap_id); + mdelay(1); + //##Tap1 Even Read Out + tap_sign_out_even = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 5, 5); + tap_coef_bin_even = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 0); + + //##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 1 1 0] + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, (tap_id + 5)); + //##Tap1 Odd Read Out + tap_sign_out_odd = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 5, 5); + tap_coef_bin_odd = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 4, 0); + + if (tap_sign_out_even == 1) + pr_info("Tap %u even sign: -", tap_id); + else + pr_info("Tap %u even sign: +", tap_id); + + pr_info("Tap %u even coefficient = %u", tap_id, tap_coef_bin_even); + + if (tap_sign_out_odd == 1) + pr_info("Tap %u odd sign: -", tap_id); + else + pr_info("Tap %u odd sign: +", tap_id); + + pr_info("Tap %u odd coefficient = %u", tap_id,tap_coef_bin_odd); + + tap_list[0] = tap_sign_out_even; + tap_list[1] = tap_coef_bin_even; + tap_list[2] = tap_sign_out_odd; + tap_list[3] = tap_coef_bin_odd; + + tap_manual = rtl9300_sds_field_r(sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7); + pr_info("tap %u manual = %d",tap_id, tap_manual); + } +} + +void rtl9300_do_rx_calibration_1(int sds, phy_interface_t phy_mode) +{ + // From both rtl9300_rxCaliConf_serdes_myParam and rtl9300_rxCaliConf_phy_myParam + int tap0_init_val = 0x1f; // Initial Decision Fed Equalizer 0 tap + int vth_min = 0x0; + + pr_info("start_1.1.1 initial value for sds %d\n", sds); + rtl930x_write_sds_phy(sds, 6, 0, 0); + + // FGCAL + rtl9300_sds_field_w(sds, 0x2e, 0x01, 14, 14, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x1c, 10, 5, 0x20); + rtl9300_sds_field_w(sds, 0x2f, 0x02, 0, 0, 0x1); + + // DCVS + rtl9300_sds_field_w(sds, 0x2e, 0x1e, 14, 11, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x01, 15, 15, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x02, 11, 11, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x1c, 4, 0, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x1d, 15, 11, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x1d, 10, 6, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x1d, 5, 1, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x02, 10, 6, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x11, 4, 0, 0x0); + rtl9300_sds_field_w(sds, 0x2f, 0x00, 3, 0, 0xf); + rtl9300_sds_field_w(sds, 0x2e, 0x04, 6, 6, 0x1); + rtl9300_sds_field_w(sds, 0x2e, 0x04, 7, 7, 0x1); + + // LEQ (Long Term Equivalent signal level) + rtl9300_sds_field_w(sds, 0x2e, 0x16, 14, 8, 0x0); + + // DFE (Decision Fed Equalizer) + rtl9300_sds_field_w(sds, 0x2f, 0x03, 5, 0, tap0_init_val); + rtl9300_sds_field_w(sds, 0x2e, 0x09, 11, 6, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x09, 5, 0, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x0a, 5, 0, 0x0); + rtl9300_sds_field_w(sds, 0x2f, 0x01, 5, 0, 0x0); + rtl9300_sds_field_w(sds, 0x2f, 0x12, 5, 0, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x0a, 11, 6, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x06, 5, 0, 0x0); + rtl9300_sds_field_w(sds, 0x2f, 0x01, 5, 0, 0x0); + + // Vth + rtl9300_sds_field_w(sds, 0x2e, 0x13, 5, 3, 0x7); + rtl9300_sds_field_w(sds, 0x2e, 0x13, 2, 0, 0x7); + rtl9300_sds_field_w(sds, 0x2f, 0x0b, 5, 3, vth_min); + + pr_info("end_1.1.1 --\n"); + + pr_info("start_1.1.2 Load DFE init. value\n"); + + rtl9300_sds_field_w(sds, 0x2e, 0x0f, 13, 7, 0x7f); + + pr_info("end_1.1.2\n"); + + pr_info("start_1.1.3 disable LEQ training,enable DFE clock\n"); + + rtl9300_sds_field_w(sds, 0x2e, 0x17, 7, 7, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x17, 6, 2, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x0c, 8, 8, 0x0); + rtl9300_sds_field_w(sds, 0x2e, 0x0b, 4, 4, 0x1); + rtl9300_sds_field_w(sds, 0x2e, 0x12, 14, 14, 0x0); + rtl9300_sds_field_w(sds, 0x2f, 0x02, 15, 15, 0x0); + + pr_info("end_1.1.3 --\n"); + + pr_info("start_1.1.4 offset cali setting\n"); + + rtl9300_sds_field_w(sds, 0x2e, 0x0f, 15, 14, 0x3); + + pr_info("end_1.1.4\n"); + + pr_info("start_1.1.5 LEQ and DFE setting\n"); + + // TODO: make this work for DAC cables of different lengths + // For a 10GBit serdes wit Fibre, SDS 8 or 9 + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || PHY_INTERFACE_MODE_1000BASEX) + rtl9300_sds_field_w(sds, 0x2e, 0x16, 3, 2, 0x2); + else + pr_err("%s not PHY-based or SerDes, implement DAC!\n", __func__); + + // No serdes, check for Aquantia PHYs + rtl9300_sds_field_w(sds, 0x2e, 0x16, 3, 2, 0x2); + + rtl9300_sds_field_w(sds, 0x2e, 0x0f, 6, 0, 0x5f); + rtl9300_sds_field_w(sds, 0x2f, 0x05, 7, 2, 0x1f); + rtl9300_sds_field_w(sds, 0x2e, 0x19, 9, 5, 0x1f); + rtl9300_sds_field_w(sds, 0x2f, 0x0b, 15, 9, 0x3c); + rtl9300_sds_field_w(sds, 0x2e, 0x0b, 1, 0, 0x3); + + pr_info("end_1.1.5\n"); +} + +void rtl9300_do_rx_calibration_2_1(u32 sds_num) +{ + pr_info("start_1.2.1 ForegroundOffsetCal_Manual\n"); + + // Gray config endis to 1 + rtl9300_sds_field_w(sds_num, 0x2f, 0x02, 2, 2, 0x1); + + // ForegroundOffsetCal_Manual(auto mode) + rtl9300_sds_field_w(sds_num, 0x2e, 0x01, 14, 14, 0x0); + + pr_info("end_1.2.1"); +} + +void rtl9300_do_rx_calibration_2_2(int sds_num) +{ + //Force Rx-Run = 0 + rtl9300_sds_field_w(sds_num, 0x2e, 0x15, 8, 8, 0x0); + + rtl930x_sds_rx_rst(sds_num, PHY_INTERFACE_MODE_10GBASER); +} + +void rtl9300_do_rx_calibration_2_3(int sds_num) +{ + u32 fgcal_binary, fgcal_gray; + u32 offset_range; + + pr_info("start_1.2.3 Foreground Calibration\n"); + + while(1) { + if (!(sds_num % 2)) + rtl930x_write_sds_phy(sds_num, 0x1f, 0x2, 0x2f); + else + rtl930x_write_sds_phy(sds_num -1 , 0x1f, 0x2, 0x31); + + // ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] + rtl9300_sds_field_w(sds_num, 0x2e, 0x15, 9, 9, 0x1); + // ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] + rtl9300_sds_field_w(sds_num, 0x21, 0x06, 11, 6, 0x20); + // ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 1 1] + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0xf); + // ##FGCAL read gray + fgcal_gray = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 5, 0); + // ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 1 0] + rtl9300_sds_field_w(sds_num, 0x2f, 0x0c, 5, 0, 0xe); + // ##FGCAL read binary + fgcal_binary = rtl9300_sds_field_r(sds_num, 0x1f, 0x14, 5, 0); + + pr_info("%s: fgcal_gray: %d, fgcal_binary %d\n", + __func__, fgcal_gray, fgcal_binary); + + offset_range = rtl9300_sds_field_r(sds_num, 0x2e, 0x15, 15, 14); + + if (fgcal_binary > 60 || fgcal_binary < 3) { + if (offset_range == 3) { + pr_info("%s: Foreground Calibration result marginal!", __func__); + break; + } else { + offset_range++; + rtl9300_sds_field_w(sds_num, 0x2e, 0x15, 15, 14, offset_range); + rtl9300_do_rx_calibration_2_2(sds_num); + } + } else { + break; + } + } + pr_info("%s: end_1.2.3\n", __func__); +} + +void rtl9300_do_rx_calibration_2(int sds) +{ + rtl930x_sds_rx_rst(sds, PHY_INTERFACE_MODE_10GBASER); + rtl9300_do_rx_calibration_2_1(sds); + rtl9300_do_rx_calibration_2_2(sds); + rtl9300_do_rx_calibration_2_3(sds); +} + +void rtl9300_sds_rxcal_3_1(int sds_num, phy_interface_t phy_mode) +{ + pr_info("start_1.3.1"); + + // ##1.3.1 + if (phy_mode != PHY_INTERFACE_MODE_10GBASER && phy_mode != PHY_INTERFACE_MODE_1000BASEX) + rtl9300_sds_field_w(sds_num, 0x2e, 0xc, 8, 8, 0); + + rtl9300_sds_field_w(sds_num, 0x2e, 0x17, 7, 7, 0x0); + rtl9300_sds_rxcal_leq_manual(sds_num, false, 0); + + pr_info("end_1.3.1"); +} + +void rtl9300_sds_rxcal_3_2(int sds_num, phy_interface_t phy_mode) +{ + u32 sum10 = 0, avg10, int10; + int dac_long_cable_offset; + bool eq_hold_enabled; + int i; + + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || phy_mode == PHY_INTERFACE_MODE_1000BASEX) { + // rtl9300_rxCaliConf_serdes_myParam + dac_long_cable_offset = 3; + eq_hold_enabled = true; + } else { + // rtl9300_rxCaliConf_phy_myParam + dac_long_cable_offset = 0; + eq_hold_enabled = false; + } + + if (phy_mode == PHY_INTERFACE_MODE_1000BASEX) + pr_warn("%s: LEQ only valid for 10GR!\n", __func__); + + pr_info("start_1.3.2"); + + for(i = 0; i < 10; i++) { + sum10 += rtl9300_sds_rxcal_leq_read(sds_num); + mdelay(10); + } + + avg10 = (sum10 / 10) + (((sum10 % 10) >= 5) ? 1 : 0); + int10 = sum10 / 10; + + pr_info("sum10:%u, avg10:%u, int10:%u", sum10, avg10, int10); + + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || phy_mode == PHY_INTERFACE_MODE_1000BASEX) { + if (dac_long_cable_offset) { + rtl9300_sds_rxcal_leq_offset_manual(sds_num, 1, dac_long_cable_offset); + rtl9300_sds_field_w(sds_num, 0x2e, 0x17, 7, 7, eq_hold_enabled); + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) + rtl9300_sds_rxcal_leq_manual(sds_num, true, avg10); + } else { + if (sum10 >= 5) { + rtl9300_sds_rxcal_leq_offset_manual(sds_num, 1, 3); + rtl9300_sds_field_w(sds_num, 0x2e, 0x17, 7, 7, 0x1); + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) + rtl9300_sds_rxcal_leq_manual(sds_num, true, avg10); + } else { + rtl9300_sds_rxcal_leq_offset_manual(sds_num, 1, 0); + rtl9300_sds_field_w(sds_num, 0x2e, 0x17, 7, 7, 0x1); + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) + rtl9300_sds_rxcal_leq_manual(sds_num, true, avg10); + } + } + } + + pr_info("Sds:%u LEQ = %u",sds_num, rtl9300_sds_rxcal_leq_read(sds_num)); + + pr_info("end_1.3.2"); +} + +void rtl9300_do_rx_calibration_3(int sds_num, phy_interface_t phy_mode) +{ + rtl9300_sds_rxcal_3_1(sds_num, phy_mode); + + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || phy_mode == PHY_INTERFACE_MODE_1000BASEX) + rtl9300_sds_rxcal_3_2(sds_num, phy_mode); +} + +void rtl9300_do_rx_calibration_4_1(int sds_num) +{ + u32 vth_list[2] = {0, 0}; + u32 tap0_list[4] = {0, 0, 0, 0}; + + pr_info("start_1.4.1"); + + // ##1.4.1 + rtl9300_sds_rxcal_vth_manual(sds_num, false, vth_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 0, false, tap0_list); + mdelay(200); + + pr_info("end_1.4.1"); +} + +void rtl9300_do_rx_calibration_4_2(u32 sds_num) +{ + u32 vth_list[2]; + u32 tap_list[4]; + + pr_info("start_1.4.2"); + + rtl9300_sds_rxcal_vth_get(sds_num, vth_list); + rtl9300_sds_rxcal_vth_manual(sds_num, true, vth_list); + + mdelay(100); + + rtl9300_sds_rxcal_tap_get(sds_num, 0, tap_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 0, true, tap_list); + + pr_info("end_1.4.2"); +} + +void rtl9300_do_rx_calibration_4(u32 sds_num) +{ + rtl9300_do_rx_calibration_4_1(sds_num); + rtl9300_do_rx_calibration_4_2(sds_num); +} + +void rtl9300_do_rx_calibration_5_2(u32 sds_num) +{ + u32 tap1_list[4] = {0}; + u32 tap2_list[4] = {0}; + u32 tap3_list[4] = {0}; + u32 tap4_list[4] = {0}; + + pr_info("start_1.5.2"); + + rtl9300_sds_rxcal_tap_manual(sds_num, 1, false, tap1_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 2, false, tap2_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 3, false, tap3_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 4, false, tap4_list); + + mdelay(30); + + pr_info("end_1.5.2"); +} + +void rtl9300_do_rx_calibration_5(u32 sds_num, phy_interface_t phy_mode) +{ + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) // dfeTap1_4Enable true + rtl9300_do_rx_calibration_5_2(sds_num); +} + + +void rtl9300_do_rx_calibration_dfe_disable(u32 sds_num) +{ + u32 tap1_list[4] = {0}; + u32 tap2_list[4] = {0}; + u32 tap3_list[4] = {0}; + u32 tap4_list[4] = {0}; + + rtl9300_sds_rxcal_tap_manual(sds_num, 1, true, tap1_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 2, true, tap2_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 3, true, tap3_list); + rtl9300_sds_rxcal_tap_manual(sds_num, 4, true, tap4_list); + + mdelay(10); +} + +void rtl9300_do_rx_calibration(int sds, phy_interface_t phy_mode) +{ + u32 latch_sts; + + rtl9300_do_rx_calibration_1(sds, phy_mode); + rtl9300_do_rx_calibration_2(sds); + rtl9300_do_rx_calibration_4(sds); + rtl9300_do_rx_calibration_5(sds, phy_mode); + mdelay(20); + + // Do this only for 10GR mode, SDS active in mode 0x1a + if (rtl9300_sds_field_r(sds, 0x1f, 9, 11, 7) == 0x1a) { + pr_info("%s: SDS enabled\n", __func__); + latch_sts = rtl9300_sds_field_r(sds, 0x4, 1, 2, 2); + mdelay(1); + latch_sts = rtl9300_sds_field_r(sds, 0x4, 1, 2, 2); + if (latch_sts) { + rtl9300_do_rx_calibration_dfe_disable(sds); + rtl9300_do_rx_calibration_4(sds); + rtl9300_do_rx_calibration_5(sds, phy_mode); + } + } +} + +int rtl9300_sds_sym_err_reset(int sds_num, phy_interface_t phy_mode) +{ + switch (phy_mode) { + case PHY_INTERFACE_MODE_XGMII: + break; + + case PHY_INTERFACE_MODE_10GBASER: + // Read twice to clear + rtl930x_read_sds_phy(sds_num, 5, 1); + rtl930x_read_sds_phy(sds_num, 5, 1); + break; + + case PHY_INTERFACE_MODE_1000BASEX: + rtl9300_sds_field_w(sds_num, 0x1, 24, 2, 0, 0); + rtl9300_sds_field_w(sds_num, 0x1, 3, 15, 8, 0); + rtl9300_sds_field_w(sds_num, 0x1, 2, 15, 0, 0); + break; + + default: + pr_info("%s unsupported phy mode\n", __func__); + return -1; + } + + return 0; +} + +u32 rtl9300_sds_sym_err_get(int sds_num, phy_interface_t phy_mode) +{ + u32 v = 0; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_XGMII: + break; + + case PHY_INTERFACE_MODE_10GBASER: + v = rtl930x_read_sds_phy(sds_num, 5, 1); + return v & 0xff; + + default: + pr_info("%s unsupported PHY-mode\n", __func__); + } + + return v; +} + +int rtl9300_sds_check_calibration(int sds_num, phy_interface_t phy_mode) +{ + u32 errors1, errors2; + + rtl9300_sds_sym_err_reset(sds_num, phy_mode); + rtl9300_sds_sym_err_reset(sds_num, phy_mode); + + // Count errors during 1ms + errors1 = rtl9300_sds_sym_err_get(sds_num, phy_mode); + mdelay(1); + errors2 = rtl9300_sds_sym_err_get(sds_num, phy_mode); + + switch (phy_mode) { + case PHY_INTERFACE_MODE_XGMII: + + if ((errors2 - errors1 > 100) + || (errors1 >= 0xffff00) || (errors2 >= 0xffff00)) { + pr_info("%s XSGMII error rate too high\n", __func__); + return 1; + } + break; + case PHY_INTERFACE_MODE_10GBASER: + if (errors2 > 0) { + pr_info("%s 10GBASER error rate too high\n", __func__); + return 1; + } + break; + default: + return 1; + } + return 0; +} + +void rtl9300_phy_enable_10g_1g(int sds_num) +{ + u32 v; + + // Enable 1GBit PHY + v = rtl930x_read_sds_phy(sds_num, PHY_PAGE_2, PHY_CTRL_REG); + pr_info("%s 1gbit phy: %08x\n", __func__, v); + v &= ~BIT(PHY_POWER_BIT); + rtl930x_write_sds_phy(sds_num, PHY_PAGE_2, PHY_CTRL_REG, v); + pr_info("%s 1gbit phy enabled: %08x\n", __func__, v); + + // Enable 10GBit PHY + v = rtl930x_read_sds_phy(sds_num, PHY_PAGE_4, PHY_CTRL_REG); + pr_info("%s 10gbit phy: %08x\n", __func__, v); + v &= ~BIT(PHY_POWER_BIT); + rtl930x_write_sds_phy(sds_num, PHY_PAGE_4, PHY_CTRL_REG, v); + pr_info("%s 10gbit phy after: %08x\n", __func__, v); + + // dal_longan_construct_mac_default_10gmedia_fiber + v = rtl930x_read_sds_phy(sds_num, 0x1f, 11); + pr_info("%s set medium: %08x\n", __func__, v); + v |= BIT(1); + rtl930x_write_sds_phy(sds_num, 0x1f, 11, v); + pr_info("%s set medium after: %08x\n", __func__, v); +} + +#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C) +// phy_mode = PHY_INTERFACE_MODE_10GBASER, sds_mode = 0x1a +int rtl9300_serdes_setup(int sds_num, phy_interface_t phy_mode) +{ + int sds_mode; + int calib_tries = 0; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_HSGMII: + sds_mode = 0x12; + break; + case PHY_INTERFACE_MODE_1000BASEX: + sds_mode = 0x04; + break; + case PHY_INTERFACE_MODE_XGMII: + sds_mode = 0x10; + break; + case PHY_INTERFACE_MODE_10GBASER: + sds_mode = 0x1a; + break; + case PHY_INTERFACE_MODE_USXGMII: + sds_mode = 0x0d; + break; + default: + pr_err("%s: unknown serdes mode: %s\n", __func__, phy_modes(phy_mode)); + return -EINVAL; + } + + // Maybe use dal_longan_sds_init + + // dal_longan_construct_serdesConfig_init // Serdes Construct + rtl9300_phy_enable_10g_1g(sds_num); + + // Set Serdes Mode + rtl9300_sds_set(sds_num, 0x1a); // 0x1b: RTK_MII_10GR1000BX_AUTO + + // Do RX calibration + do { + rtl9300_do_rx_calibration(sds_num, phy_mode); + calib_tries++; + mdelay(50); + } while (rtl9300_sds_check_calibration(sds_num, phy_mode) && calib_tries < 3); + + + return 0; +} + +typedef struct { + u8 page; + u8 reg; + u16 data; +} sds_config; + +sds_config rtl9300_a_sds_10gr_lane0[] = +{ + /*1G*/ + {0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, {0x21, 0x03, 0x8206}, + {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, + {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, + {0x21, 0x0F, 0x0008}, {0x24, 0x00, 0x0668}, {0x24, 0x02, 0xD020}, + {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892}, {0x24, 0x0F, 0xFFDF}, + {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, {0x24, 0x14, 0x1311}, + {0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, {0x24, 0x1A, 0x0001}, + {0x24, 0x1C, 0x0400}, {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, + {0x25, 0x03, 0xFFDF}, {0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100}, + {0x25, 0x08, 0x0001}, {0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F}, + {0x25, 0x0E, 0x003F}, {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, + {0x25, 0x11, 0x8840}, {0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, + {0x2B, 0x19, 0x4902}, {0x2B, 0x1D, 0x2501}, {0x2D, 0x13, 0x0050}, + {0x2D, 0x18, 0x8E88}, {0x2D, 0x19, 0x4902}, {0x2D, 0x1D, 0x2641}, + {0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, {0x2F, 0x19, 0x4902}, + {0x2F, 0x1D, 0x66E1}, + /*3.125G*/ + {0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, + {0x28, 0x0B, 0x1892}, {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, + {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, {0x28, 0x16, 0x00C9}, + {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400}, + {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, {0x29, 0x03, 0xFFDF}, + {0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100}, {0x29, 0x08, 0x0001}, + {0x29, 0x09, 0xFFD4}, {0x29, 0x0A, 0x7C2F}, {0x29, 0x0E, 0x003F}, + {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840}, + /*10G*/ + {0x06, 0x0D, 0x0F00}, {0x06, 0x00, 0x0000}, {0x06, 0x01, 0xC800}, + {0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, + {0x21, 0x07, 0xF09F}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, + {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, {0x2E, 0x00, 0xA668}, + {0x2E, 0x02, 0xD020}, {0x2E, 0x06, 0xC000}, {0x2E, 0x0B, 0x1892}, + {0x2E, 0x0F, 0xFFDF}, {0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0044}, + {0x2E, 0x13, 0x027F}, {0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100}, + {0x2E, 0x1A, 0x0001}, {0x2E, 0x1C, 0x0400}, {0x2F, 0x01, 0x0300}, + {0x2F, 0x02, 0x1217}, {0x2F, 0x03, 0xFFDF}, {0x2F, 0x05, 0x7F7C}, + {0x2F, 0x07, 0x80C4}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4}, + {0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121}, + {0x2F, 0x10, 0x0020}, {0x2F, 0x11, 0x8840}, {0x2F, 0x14, 0xE008}, + {0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, {0x2B, 0x19, 0x4902}, + {0x2B, 0x1D, 0x2501}, {0x2D, 0x13, 0x0050}, {0x2D, 0x17, 0x4109}, + {0x2D, 0x18, 0x8E88}, {0x2D, 0x19, 0x4902}, {0x2D, 0x1C, 0x1109}, + {0x2D, 0x1D, 0x2641}, {0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, + {0x2F, 0x19, 0x4902}, {0x2F, 0x1D, 0x76E1}, +}; + +sds_config rtl9300_a_sds_10gr_lane1[] = +{ + /*1G*/ + {0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, {0x21, 0x03, 0x8206}, + {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003}, + {0x21, 0x0B, 0x0005}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, + {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, {0x24, 0x00, 0x0668}, + {0x24, 0x02, 0xD020}, {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892}, + {0x24, 0x0F, 0xFFDF}, {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, + {0x24, 0x14, 0x1311}, {0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, + {0x24, 0x1A, 0x0001}, {0x24, 0x1C, 0x0400}, {0x25, 0x00, 0x820F}, + {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, {0x25, 0x03, 0xFFDF}, + {0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100}, {0x25, 0x08, 0x0001}, + {0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F}, {0x25, 0x0E, 0x003F}, + {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, {0x25, 0x11, 0x8840}, + {0x2B, 0x13, 0x3D87}, {0x2B, 0x14, 0x3108}, {0x2D, 0x13, 0x3C87}, + {0x2D, 0x14, 0x1808}, + /*3.125G*/ + {0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, + {0x28, 0x0B, 0x1892}, {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, + {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, {0x28, 0x16, 0x00C9}, + {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400}, + {0x29, 0x00, 0x820F}, {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, + {0x29, 0x03, 0xFFDF}, {0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100}, + {0x29, 0x08, 0x0001}, {0x29, 0x0A, 0x7C2F}, {0x29, 0x0E, 0x003F}, + {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840}, + /*10G*/ + {0x06, 0x0D, 0x0F00}, {0x06, 0x00, 0x0000}, {0x06, 0x01, 0xC800}, + {0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, + {0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003}, {0x21, 0x0B, 0x0005}, + {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, + {0x21, 0x0F, 0x0008}, {0x2E, 0x00, 0xA668}, {0x2E, 0x02, 0xD020}, + {0x2E, 0x06, 0xC000}, {0x2E, 0x0B, 0x1892}, {0x2E, 0x0F, 0xFFDF}, + {0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0044}, {0x2E, 0x13, 0x027F}, + {0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100}, {0x2E, 0x1A, 0x0001}, + {0x2E, 0x1C, 0x0400}, {0x2F, 0x00, 0x820F}, {0x2F, 0x01, 0x0300}, + {0x2F, 0x02, 0x1217}, {0x2F, 0x03, 0xFFDF}, {0x2F, 0x05, 0x7F7C}, + {0x2F, 0x07, 0x80C4}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4}, + {0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121}, + {0x2F, 0x10, 0x0020}, {0x2F, 0x11, 0x8840}, {0x2B, 0x13, 0x3D87}, + {0x2B, 0x14, 0x3108}, {0x2D, 0x13, 0x3C87}, {0x2D, 0x14, 0x1808}, +}; + +int rtl9300_sds_cmu_band_get(int sds) +{ + u32 page; + u32 en; + u32 cmu_band; + +// page = rtl9300_sds_cmu_page_get(sds); + page = 0x25; // 10GR and 1000BX + sds = (sds % 2) ? (sds - 1) : (sds); + + rtl9300_sds_field_w(sds, page, 0x1c, 15, 15, 1); + rtl9300_sds_field_w(sds + 1, page, 0x1c, 15, 15, 1); + + en = rtl9300_sds_field_r(sds, page, 27, 1, 1); + if(!en) { // Auto mode + rtl930x_write_sds_phy(sds, 0x1f, 0x02, 31); + + cmu_band = rtl9300_sds_field_r(sds, 0x1f, 0x15, 5, 1); + } else { + cmu_band = rtl9300_sds_field_r(sds, page, 30, 4, 0); + } + + return cmu_band; +} + +int rtl9300_configure_serdes(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int phy_addr = phydev->mdio.addr; + struct device_node *dn; + u32 sds_num = 0; + int sds_mode, calib_tries = 0, phy_mode = PHY_INTERFACE_MODE_10GBASER, i; + + if (dev->of_node) { + dn = dev->of_node; + + if (of_property_read_u32(dn, "sds", &sds_num)) + sds_num = -1; + pr_info("%s: Port %d, SerDes is %d\n", __func__, phy_addr, sds_num); + } else { + dev_err(dev, "No DT node.\n"); + return -EINVAL; + } + + if (sds_num < 0) + return 0; + + if (phy_mode != PHY_INTERFACE_MODE_10GBASER) // TODO: for now we only patch 10GR SerDes + return 0; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_HSGMII: + sds_mode = 0x12; + break; + case PHY_INTERFACE_MODE_1000BASEX: + sds_mode = 0x04; + break; + case PHY_INTERFACE_MODE_XGMII: + sds_mode = 0x10; + break; + case PHY_INTERFACE_MODE_10GBASER: + sds_mode = 0x1a; + break; + case PHY_INTERFACE_MODE_USXGMII: + sds_mode = 0x0d; + break; + default: + pr_err("%s: unknown serdes mode: %s\n", __func__, phy_modes(phy_mode)); + return -EINVAL; + } + + pr_info("%s CMU BAND is %d\n", __func__, rtl9300_sds_cmu_band_get(sds_num)); + + // Turn Off Serdes + rtl9300_sds_rst(sds_num, 0x1f); + + pr_info("%s PATCHING SerDes %d\n", __func__, sds_num); + if (sds_num % 2) { + for (i = 0; i < sizeof(rtl9300_a_sds_10gr_lane1) / sizeof(sds_config); ++i) { + rtl930x_write_sds_phy(sds_num, rtl9300_a_sds_10gr_lane1[i].page, + rtl9300_a_sds_10gr_lane1[i].reg, + rtl9300_a_sds_10gr_lane1[i].data); + } + } else { + for (i = 0; i < sizeof(rtl9300_a_sds_10gr_lane0) / sizeof(sds_config); ++i) { + rtl930x_write_sds_phy(sds_num, rtl9300_a_sds_10gr_lane0[i].page, + rtl9300_a_sds_10gr_lane0[i].reg, + rtl9300_a_sds_10gr_lane0[i].data); + } + } + + rtl9300_phy_enable_10g_1g(sds_num); + + // Disable MAC + sw_w32_mask(0, 1, RTL930X_MAC_FORCE_MODE_CTRL); + mdelay(20); + + // ----> dal_longan_sds_mode_set + pr_info("%s: Configuring RTL9300 SERDES %d, mode %02x\n", __func__, sds_num, sds_mode); + + // Configure link to MAC + rtl9300_serdes_mac_link_config(sds_num, true, true); // MAC Construct + + // Disable MAC + sw_w32_mask(0, 1, RTL930X_MAC_FORCE_MODE_CTRL); + mdelay(20); + + rtl9300_force_sds_mode(sds_num, PHY_INTERFACE_MODE_NA); + + // Re-Enable MAC + sw_w32_mask(1, 0, RTL930X_MAC_FORCE_MODE_CTRL); + + rtl9300_force_sds_mode(sds_num, phy_mode); + + // Do RX calibration + do { + rtl9300_do_rx_calibration(sds_num, phy_mode); + calib_tries++; + mdelay(50); + } while (rtl9300_sds_check_calibration(sds_num, phy_mode) && calib_tries < 3); + + if (calib_tries >= 3) + pr_err("%s CALIBTRATION FAILED\n", __func__); + + rtl9300_sds_tx_config(sds_num, phy_mode); + + // The clock needs only to be configured on the FPGA implementation + + return 0; +} + +void rtl9310_sds_field_w(int sds, u32 page, u32 reg, int end_bit, int start_bit, u32 v) +{ + int l = end_bit - start_bit + 1; + u32 data = v; + + if (l < 32) { + u32 mask = BIT(l) - 1; + + data = rtl930x_read_sds_phy(sds, page, reg); + data &= ~(mask << start_bit); + data |= (v & mask) << start_bit; + } + + rtl931x_write_sds_phy(sds, page, reg, data); +} + + +u32 rtl9310_sds_field_r(int sds, u32 page, u32 reg, int end_bit, int start_bit) +{ + int l = end_bit - start_bit + 1; + u32 v = rtl931x_read_sds_phy(sds, page, reg); + + if (l >= 32) + return v; + + return (v >> start_bit) & (BIT(l) - 1); +} + +static void rtl931x_sds_rst(u32 sds) +{ + u32 o, v, o_mode; + int shift = ((sds & 0x3) << 3); + + // TODO: We need to lock this! + + o = sw_r32(RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR); + v = o | BIT(sds); + sw_w32(v, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR); + + o_mode = sw_r32(RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2)); + v = BIT(7) | 0x1F; + sw_w32_mask(0xff << shift, v << shift, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2)); + sw_w32(o_mode, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2)); + + sw_w32(o, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR); +} + +static void rtl931x_symerr_clear(u32 sds, phy_interface_t mode) +{ + u32 i; + u32 xsg_sdsid_0, xsg_sdsid_1; + + switch (mode) { + case PHY_INTERFACE_MODE_NA: + break; + case PHY_INTERFACE_MODE_XGMII: + if (sds < 2) + xsg_sdsid_0 = sds; + else + xsg_sdsid_0 = (sds - 1) * 2; + xsg_sdsid_1 = xsg_sdsid_0 + 1; + + for (i = 0; i < 4; ++i) { + rtl9310_sds_field_w(xsg_sdsid_0, 0x1, 24, 2, 0, i); + rtl9310_sds_field_w(xsg_sdsid_0, 0x1, 3, 15, 8, 0x0); + rtl9310_sds_field_w(xsg_sdsid_0, 0x1, 2, 15, 0, 0x0); + } + + for (i = 0; i < 4; ++i) { + rtl9310_sds_field_w(xsg_sdsid_1, 0x1, 24, 2, 0, i); + rtl9310_sds_field_w(xsg_sdsid_1, 0x1, 3, 15, 8, 0x0); + rtl9310_sds_field_w(xsg_sdsid_1, 0x1, 2, 15, 0, 0x0); + } + + rtl9310_sds_field_w(xsg_sdsid_0, 0x1, 0, 15, 0, 0x0); + rtl9310_sds_field_w(xsg_sdsid_0, 0x1, 1, 15, 8, 0x0); + rtl9310_sds_field_w(xsg_sdsid_1, 0x1, 0, 15, 0, 0x0); + rtl9310_sds_field_w(xsg_sdsid_1, 0x1, 1, 15, 8, 0x0); + break; + default: + break; + } + + return; +} + +static u32 rtl931x_get_analog_sds(u32 sds) +{ + u32 sds_map[] = { 0, 1, 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23 }; + + if (sds < 14) + return sds_map[sds]; + return sds; +} + +void rtl931x_sds_fiber_disable(u32 sds) +{ + u32 v = 0x3F; + u32 asds = rtl931x_get_analog_sds(sds); + + rtl9310_sds_field_w(asds, 0x1F, 0x9, 11, 6, v); +} + +static void rtl931x_sds_fiber_mode_set(u32 sds, phy_interface_t mode) +{ + u32 val, asds = rtl931x_get_analog_sds(sds); + + /* clear symbol error count before changing mode */ + rtl931x_symerr_clear(sds, mode); + + val = 0x9F; + sw_w32(val, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2)); + + switch (mode) { + case PHY_INTERFACE_MODE_SGMII: + val = 0x5; + break; + + case PHY_INTERFACE_MODE_1000BASEX: + /* serdes mode FIBER1G */ + val = 0x9; + break; + + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10GKR: + val = 0x35; + break; +/* case MII_10GR1000BX_AUTO: + val = 0x39; + break; */ + + + case PHY_INTERFACE_MODE_USXGMII: + val = 0x1B; + break; + default: + val = 0x25; + } + + pr_info("%s writing analog SerDes Mode value %02x\n", __func__, val); + rtl9310_sds_field_w(asds, 0x1F, 0x9, 11, 6, val); + + return; +} + +static int rtl931x_sds_cmu_page_get(phy_interface_t mode) +{ + switch (mode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: // MII_1000BX_FIBER / 100BX_FIBER / 1000BX100BX_AUTO + return 0x24; + case PHY_INTERFACE_MODE_HSGMII: + case PHY_INTERFACE_MODE_2500BASEX: // MII_2500Base_X: + return 0x28; +// case MII_HISGMII_5G: +// return 0x2a; + case PHY_INTERFACE_MODE_QSGMII: + return 0x2a; // Code also has 0x34 + case PHY_INTERFACE_MODE_XAUI: // MII_RXAUI_LITE: + return 0x2c; + case PHY_INTERFACE_MODE_XGMII: // MII_XSGMII + case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_10GBASER: // MII_10GR + return 0x2e; + default: + return -1; + } + return -1; +} + +static void rtl931x_cmu_type_set(u32 asds, phy_interface_t mode, int chiptype) +{ + int cmu_type = 0; // Clock Management Unit + u32 cmu_page = 0; + u32 frc_cmu_spd; + u32 evenSds; + u32 lane, frc_lc_mode_bitnum, frc_lc_mode_val_bitnum; + + switch (mode) { + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + return; + +/* case MII_10GR1000BX_AUTO: + if (chiptype) + rtl9310_sds_field_w(asds, 0x24, 0xd, 14, 14, 0); + return; */ + + case PHY_INTERFACE_MODE_QSGMII: + cmu_type = 1; + frc_cmu_spd = 0; + break; + + case PHY_INTERFACE_MODE_HSGMII: + cmu_type = 1; + frc_cmu_spd = 1; + break; + + case PHY_INTERFACE_MODE_1000BASEX: + cmu_type = 1; + frc_cmu_spd = 0; + break; + +/* case MII_1000BX100BX_AUTO: + cmu_type = 1; + frc_cmu_spd = 0; + break; */ + + case PHY_INTERFACE_MODE_SGMII: + cmu_type = 1; + frc_cmu_spd = 0; + break; + + case PHY_INTERFACE_MODE_2500BASEX: + cmu_type = 1; + frc_cmu_spd = 1; + break; + + default: + pr_info("SerDes %d mode is invalid\n", asds); + return; + } + + if (cmu_type == 1) + cmu_page = rtl931x_sds_cmu_page_get(mode); + + lane = asds % 2; + + if (!lane) { + frc_lc_mode_bitnum = 4; + frc_lc_mode_val_bitnum = 5; + } else { + frc_lc_mode_bitnum = 6; + frc_lc_mode_val_bitnum = 7; + } + + evenSds = asds - lane; + + pr_info("%s: cmu_type %0d cmu_page %x frc_cmu_spd %d lane %d asds %d\n", + __func__, cmu_type, cmu_page, frc_cmu_spd, lane, asds); + + if (cmu_type == 1) { + pr_info("%s A CMU page 0x28 0x7 %08x\n", __func__, rtl931x_read_sds_phy(asds, 0x28, 0x7)); + rtl9310_sds_field_w(asds, cmu_page, 0x7, 15, 15, 0); + pr_info("%s B CMU page 0x28 0x7 %08x\n", __func__, rtl931x_read_sds_phy(asds, 0x28, 0x7)); + if (chiptype) { + rtl9310_sds_field_w(asds, cmu_page, 0xd, 14, 14, 0); + } + + rtl9310_sds_field_w(evenSds, 0x20, 0x12, 3, 2, 0x3); + rtl9310_sds_field_w(evenSds, 0x20, 0x12, frc_lc_mode_bitnum, frc_lc_mode_bitnum, 1); + rtl9310_sds_field_w(evenSds, 0x20, 0x12, frc_lc_mode_val_bitnum, frc_lc_mode_val_bitnum, 0); + rtl9310_sds_field_w(evenSds, 0x20, 0x12, 12, 12, 1); + rtl9310_sds_field_w(evenSds, 0x20, 0x12, 15, 13, frc_cmu_spd); + } + + pr_info("%s CMU page 0x28 0x7 %08x\n", __func__, rtl931x_read_sds_phy(asds, 0x28, 0x7)); + return; +} + +static void rtl931x_sds_rx_rst(u32 sds) +{ + u32 asds = rtl931x_get_analog_sds(sds); + + if (sds < 2) + return; + + rtl931x_write_sds_phy(asds, 0x2e, 0x12, 0x2740); + rtl931x_write_sds_phy(asds, 0x2f, 0x0, 0x0); + rtl931x_write_sds_phy(asds, 0x2f, 0x2, 0x2010); + rtl931x_write_sds_phy(asds, 0x20, 0x0, 0xc10); + + rtl931x_write_sds_phy(asds, 0x2e, 0x12, 0x27c0); + rtl931x_write_sds_phy(asds, 0x2f, 0x0, 0xc000); + rtl931x_write_sds_phy(asds, 0x2f, 0x2, 0x6010); + rtl931x_write_sds_phy(asds, 0x20, 0x0, 0xc30); + + mdelay(50); +} + +static void rtl931x_sds_disable(u32 sds) +{ + u32 v = 0x1f; + + v |= BIT(7); + sw_w32(v, RTL931X_SERDES_MODE_CTRL + (sds >> 2) * 4); +} + +static void rtl931x_sds_mii_mode_set(u32 sds, phy_interface_t mode) +{ + u32 val; + + switch (mode) { + case PHY_INTERFACE_MODE_QSGMII: + val = 0x6; + break; + case PHY_INTERFACE_MODE_XGMII: + val = 0x10; // serdes mode XSGMII + break; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_2500BASEX: + val = 0xD; + break; + case PHY_INTERFACE_MODE_HSGMII: + val = 0x12; + break; + case PHY_INTERFACE_MODE_SGMII: + val = 0x2; + break; + default: + return; + } + + val |= (1 << 7); + + sw_w32(val, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2)); +} + +static sds_config sds_config_10p3125g_type1[] = { + { 0x2E, 0x00, 0x0107 }, { 0x2E, 0x01, 0x01A3 }, { 0x2E, 0x02, 0x6A24 }, + { 0x2E, 0x03, 0xD10D }, { 0x2E, 0x04, 0x8000 }, { 0x2E, 0x05, 0xA17E }, + { 0x2E, 0x06, 0xE31D }, { 0x2E, 0x07, 0x800E }, { 0x2E, 0x08, 0x0294 }, + { 0x2E, 0x09, 0x0CE4 }, { 0x2E, 0x0A, 0x7FC8 }, { 0x2E, 0x0B, 0xE0E7 }, + { 0x2E, 0x0C, 0x0200 }, { 0x2E, 0x0D, 0xDF80 }, { 0x2E, 0x0E, 0x0000 }, + { 0x2E, 0x0F, 0x1FC2 }, { 0x2E, 0x10, 0x0C3F }, { 0x2E, 0x11, 0x0000 }, + { 0x2E, 0x12, 0x27C0 }, { 0x2E, 0x13, 0x7E1D }, { 0x2E, 0x14, 0x1300 }, + { 0x2E, 0x15, 0x003F }, { 0x2E, 0x16, 0xBE7F }, { 0x2E, 0x17, 0x0090 }, + { 0x2E, 0x18, 0x0000 }, { 0x2E, 0x19, 0x4000 }, { 0x2E, 0x1A, 0x0000 }, + { 0x2E, 0x1B, 0x8000 }, { 0x2E, 0x1C, 0x011F }, { 0x2E, 0x1D, 0x0000 }, + { 0x2E, 0x1E, 0xC8FF }, { 0x2E, 0x1F, 0x0000 }, { 0x2F, 0x00, 0xC000 }, + { 0x2F, 0x01, 0xF000 }, { 0x2F, 0x02, 0x6010 }, { 0x2F, 0x12, 0x0EE7 }, + { 0x2F, 0x13, 0x0000 } +}; + +static sds_config sds_config_10p3125g_cmu_type1[] = { + { 0x2F, 0x03, 0x4210 }, { 0x2F, 0x04, 0x0000 }, { 0x2F, 0x05, 0x0019 }, + { 0x2F, 0x06, 0x18A6 }, { 0x2F, 0x07, 0x2990 }, { 0x2F, 0x08, 0xFFF4 }, + { 0x2F, 0x09, 0x1F08 }, { 0x2F, 0x0A, 0x0000 }, { 0x2F, 0x0B, 0x8000 }, + { 0x2F, 0x0C, 0x4224 }, { 0x2F, 0x0D, 0x0000 }, { 0x2F, 0x0E, 0x0000 }, + { 0x2F, 0x0F, 0xA470 }, { 0x2F, 0x10, 0x8000 }, { 0x2F, 0x11, 0x037B } +}; + +void rtl931x_sds_init(u32 sds, phy_interface_t mode) +{ + + u32 board_sds_tx_type1[] = { 0x1C3, 0x1C3, 0x1C3, 0x1A3, 0x1A3, + 0x1A3, 0x143, 0x143, 0x143, 0x143, 0x163, 0x163 + }; + + u32 board_sds_tx[] = { 0x1A00, 0x1A00, 0x200, 0x200, 0x200, + 0x200, 0x1A3, 0x1A3, 0x1A3, 0x1A3, 0x1E3, 0x1E3 + }; + + u32 board_sds_tx2[] = { 0xDC0, 0x1C0, 0x200, 0x180, 0x160, + 0x123, 0x123, 0x163, 0x1A3, 0x1A0, 0x1C3, 0x9C3 + }; + + u32 asds, dSds, ori, model_info, val; + int chiptype = 0; + + asds = rtl931x_get_analog_sds(sds); + + if (sds > 13) + return; + + pr_info("%s: set sds %d to mode %d\n", __func__, sds, mode); + val = rtl9310_sds_field_r(asds, 0x1F, 0x9, 11, 6); + + pr_info("%s: fibermode %08X stored mode 0x%x analog SDS %d", __func__, + rtl931x_read_sds_phy(asds, 0x1f, 0x9), val, asds); + pr_info("%s: SGMII mode %08X in 0x24 0x9 analog SDS %d", __func__, + rtl931x_read_sds_phy(asds, 0x24, 0x9), asds); + pr_info("%s: CMU mode %08X stored even SDS %d", __func__, + rtl931x_read_sds_phy(asds & ~1, 0x20, 0x12), asds & ~1); + pr_info("%s: serdes_mode_ctrl %08X", __func__, RTL931X_SERDES_MODE_CTRL + 4 * (sds >> 2)); + pr_info("%s CMU page 0x24 0x7 %08x\n", __func__, rtl931x_read_sds_phy(asds, 0x24, 0x7)); + pr_info("%s CMU page 0x26 0x7 %08x\n", __func__, rtl931x_read_sds_phy(asds, 0x26, 0x7)); + pr_info("%s CMU page 0x28 0x7 %08x\n", __func__, rtl931x_read_sds_phy(asds, 0x28, 0x7)); + pr_info("%s XSG page 0x0 0xe %08x\n", __func__, rtl931x_read_sds_phy(dSds, 0x0, 0xe)); + pr_info("%s XSG2 page 0x0 0xe %08x\n", __func__, rtl931x_read_sds_phy(dSds + 1, 0x0, 0xe)); + + model_info = sw_r32(RTL93XX_MODEL_NAME_INFO); + if ((model_info >> 4) & 0x1) { + pr_info("detected chiptype 1\n"); + chiptype = 1; + } else { + pr_info("detected chiptype 0\n"); + } + + if (sds < 2) + dSds = sds; + else + dSds = (sds - 1) * 2; + + pr_info("%s: 2.5gbit %08X dsds %d", __func__, + rtl931x_read_sds_phy(dSds, 0x1, 0x14), dSds); + + pr_info("%s: RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR 0x%08X\n", __func__, sw_r32(RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR)); + ori = sw_r32(RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR); + val = ori | (1 << sds); + sw_w32(val, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR); + + switch (mode) { + case PHY_INTERFACE_MODE_NA: + break; + + case PHY_INTERFACE_MODE_XGMII: // MII_XSGMII + + if (chiptype) { + u32 xsg_sdsid_1; + xsg_sdsid_1 = dSds + 1; + //fifo inv clk + rtl9310_sds_field_w(dSds, 0x1, 0x1, 7, 4, 0xf); + rtl9310_sds_field_w(dSds, 0x1, 0x1, 3, 0, 0xf); + + rtl9310_sds_field_w(xsg_sdsid_1, 0x1, 0x1, 7, 4, 0xf); + rtl9310_sds_field_w(xsg_sdsid_1, 0x1, 0x1, 3, 0, 0xf); + + } + + rtl9310_sds_field_w(dSds, 0x0, 0xE, 12, 12, 1); + rtl9310_sds_field_w(dSds + 1, 0x0, 0xE, 12, 12, 1); + break; + + case PHY_INTERFACE_MODE_USXGMII: // MII_USXGMII_10GSXGMII/10GDXGMII/10GQXGMII: + u32 i, evenSds; + u32 op_code = 0x6003; + + if (chiptype) { + rtl9310_sds_field_w(asds, 0x6, 0x2, 12, 12, 1); + + for (i = 0; i < sizeof(sds_config_10p3125g_type1) / sizeof(sds_config); ++i) { + rtl931x_write_sds_phy(asds, sds_config_10p3125g_type1[i].page - 0x4, sds_config_10p3125g_type1[i].reg, sds_config_10p3125g_type1[i].data); + } + + evenSds = asds - (asds % 2); + + for (i = 0; i < sizeof(sds_config_10p3125g_cmu_type1) / sizeof(sds_config); ++i) { + rtl931x_write_sds_phy(evenSds, + sds_config_10p3125g_cmu_type1[i].page - 0x4, sds_config_10p3125g_cmu_type1[i].reg, sds_config_10p3125g_cmu_type1[i].data); + } + + rtl9310_sds_field_w(asds, 0x6, 0x2, 12, 12, 0); + } else { + + rtl9310_sds_field_w(asds, 0x2e, 0xd, 6, 0, 0x0); + rtl9310_sds_field_w(asds, 0x2e, 0xd, 7, 7, 0x1); + + rtl9310_sds_field_w(asds, 0x2e, 0x1c, 5, 0, 0x1E); + rtl9310_sds_field_w(asds, 0x2e, 0x1d, 11, 0, 0x00); + rtl9310_sds_field_w(asds, 0x2e, 0x1f, 11, 0, 0x00); + rtl9310_sds_field_w(asds, 0x2f, 0x0, 11, 0, 0x00); + rtl9310_sds_field_w(asds, 0x2f, 0x1, 11, 0, 0x00); + + rtl9310_sds_field_w(asds, 0x2e, 0xf, 12, 6, 0x7F); + rtl931x_write_sds_phy(asds, 0x2f, 0x12, 0xaaa); + + rtl931x_sds_rx_rst(sds); + + rtl931x_write_sds_phy(asds, 0x7, 0x10, op_code); + rtl931x_write_sds_phy(asds, 0x6, 0x1d, 0x0480); + rtl931x_write_sds_phy(asds, 0x6, 0xe, 0x0400); + } + break; + + case PHY_INTERFACE_MODE_10GBASER: // MII_10GR / MII_10GR1000BX_AUTO: + // configure 10GR fiber mode=1 + rtl9310_sds_field_w(asds, 0x1f, 0xb, 1, 1, 1); + + // init fiber_1g + rtl9310_sds_field_w(dSds, 0x3, 0x13, 15, 14, 0); + + rtl9310_sds_field_w(dSds, 0x2, 0x0, 12, 12, 1); + rtl9310_sds_field_w(dSds, 0x2, 0x0, 6, 6, 1); + rtl9310_sds_field_w(dSds, 0x2, 0x0, 13, 13, 0); + + // init auto + rtl9310_sds_field_w(asds, 0x1f, 13, 15, 0, 0x109e); + rtl9310_sds_field_w(asds, 0x1f, 0x6, 14, 10, 0x8); + rtl9310_sds_field_w(asds, 0x1f, 0x7, 10, 4, 0x7f); + break; + + case PHY_INTERFACE_MODE_HSGMII: + rtl9310_sds_field_w(dSds, 0x1, 0x14, 8, 8, 1); + break; + + case PHY_INTERFACE_MODE_1000BASEX: // MII_1000BX_FIBER + rtl9310_sds_field_w(dSds, 0x3, 0x13, 15, 14, 0); + + rtl9310_sds_field_w(dSds, 0x2, 0x0, 12, 12, 1); + rtl9310_sds_field_w(dSds, 0x2, 0x0, 6, 6, 1); + rtl9310_sds_field_w(dSds, 0x2, 0x0, 13, 13, 0); + break; + + case PHY_INTERFACE_MODE_SGMII: + rtl9310_sds_field_w(asds, 0x24, 0x9, 15, 15, 0); + break; + + case PHY_INTERFACE_MODE_2500BASEX: + rtl9310_sds_field_w(dSds, 0x1, 0x14, 8, 8, 1); + break; + + case PHY_INTERFACE_MODE_QSGMII: + default: + pr_info("%s: PHY mode %s not supported by SerDes %d\n", + __func__, phy_modes(mode), sds); + return; + } + + rtl931x_cmu_type_set(asds, mode, chiptype); + + if (sds >= 2 && sds <= 13) { + if (chiptype) + rtl931x_write_sds_phy(asds, 0x2E, 0x1, board_sds_tx_type1[sds - 2]); + else { + val = 0xa0000; + sw_w32(val, RTL931X_CHIP_INFO_ADDR); + val = sw_r32(RTL931X_CHIP_INFO_ADDR); + if (val & BIT(28)) // consider 9311 etc. RTL9313_CHIP_ID == HWP_CHIP_ID(unit)) + { + rtl931x_write_sds_phy(asds, 0x2E, 0x1, board_sds_tx2[sds - 2]); + } else { + rtl931x_write_sds_phy(asds, 0x2E, 0x1, board_sds_tx[sds - 2]); + } + val = 0; + sw_w32(val, RTL931X_CHIP_INFO_ADDR); + } + } + + val = ori & ~BIT(sds); + sw_w32(val, RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR); + pr_debug("%s: RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR 0x%08X\n", __func__, sw_r32(RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR)); + + if (mode == PHY_INTERFACE_MODE_XGMII || mode == PHY_INTERFACE_MODE_QSGMII + || mode == PHY_INTERFACE_MODE_HSGMII || mode == PHY_INTERFACE_MODE_SGMII + || mode == PHY_INTERFACE_MODE_USXGMII) { + if (mode == PHY_INTERFACE_MODE_XGMII) + rtl931x_sds_mii_mode_set(sds, mode); + else + rtl931x_sds_fiber_mode_set(sds, mode); + } +} + +int rtl931x_sds_cmu_band_set(int sds, bool enable, u32 band, phy_interface_t mode) +{ + u32 asds; + int page = rtl931x_sds_cmu_page_get(mode); + + sds -= (sds % 2); + sds = sds & ~1; + asds = rtl931x_get_analog_sds(sds); + page += 1; + + if (enable) { + rtl9310_sds_field_w(asds, page, 0x7, 13, 13, 0); + rtl9310_sds_field_w(asds, page, 0x7, 11, 11, 0); + } else { + rtl9310_sds_field_w(asds, page, 0x7, 13, 13, 0); + rtl9310_sds_field_w(asds, page, 0x7, 11, 11, 0); + } + + rtl9310_sds_field_w(asds, page, 0x7, 4, 0, band); + + rtl931x_sds_rst(sds); + + return 0; +} + +int rtl931x_sds_cmu_band_get(int sds, phy_interface_t mode) +{ + int page = rtl931x_sds_cmu_page_get(mode); + u32 asds, band; + + sds -= (sds % 2); + asds = rtl931x_get_analog_sds(sds); + page += 1; + rtl931x_write_sds_phy(asds, 0x1f, 0x02, 73); + + rtl9310_sds_field_w(asds, page, 0x5, 15, 15, 1); + band = rtl9310_sds_field_r(asds, 0x1f, 0x15, 8, 3); + pr_info("%s band is: %d\n", __func__, band); + + return band; +} + + +int rtl931x_link_sts_get(u32 sds) +{ + u32 sts, sts1, latch_sts, latch_sts1; + if (0){ + u32 xsg_sdsid_0, xsg_sdsid_1; + + xsg_sdsid_0 = sds < 2 ? sds : (sds - 1) * 2; + xsg_sdsid_1 = xsg_sdsid_0 + 1; + + sts = rtl9310_sds_field_r(xsg_sdsid_0, 0x1, 29, 8, 0); + sts1 = rtl9310_sds_field_r(xsg_sdsid_1, 0x1, 29, 8, 0); + latch_sts = rtl9310_sds_field_r(xsg_sdsid_0, 0x1, 30, 8, 0); + latch_sts1 = rtl9310_sds_field_r(xsg_sdsid_1, 0x1, 30, 8, 0); + } else { + u32 asds, dsds; + + asds = rtl931x_get_analog_sds(sds); + sts = rtl9310_sds_field_r(asds, 0x5, 0, 12, 12); + latch_sts = rtl9310_sds_field_r(asds, 0x4, 1, 2, 2); + + dsds = sds < 2 ? sds : (sds - 1) * 2; + latch_sts1 = rtl9310_sds_field_r(dsds, 0x2, 1, 2, 2); + sts1 = rtl9310_sds_field_r(dsds, 0x2, 1, 2, 2); + } + + pr_info("%s: serdes %d sts %d, sts1 %d, latch_sts %d, latch_sts1 %d\n", __func__, + sds, sts, sts1, latch_sts, latch_sts1); + return sts1; +} + +static int rtl8214fc_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + + rtl8214fc_media_set(phydev, true); + + return 0; +} + +static void rtl8214fc_sfp_remove(void *upstream) +{ + struct phy_device *phydev = upstream; + + rtl8214fc_media_set(phydev, false); +} + +static const struct sfp_upstream_ops rtl8214fc_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, + .module_insert = rtl8214fc_sfp_insert, + .module_remove = rtl8214fc_sfp_remove, +}; + +static int rtl8214fc_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int addr = phydev->mdio.addr; + int ret = 0; + + /* 839x has internal SerDes */ + if (soc_info.id == 0x8393) + return -ENODEV; + + /* All base addresses of the PHYs start at multiples of 8 */ + devm_phy_package_join(dev, phydev, addr & (~7), + sizeof(struct rtl83xx_shared_private)); + + if (!(addr % 8)) { + struct rtl83xx_shared_private *shared = phydev->shared->priv; + shared->name = "RTL8214FC"; + /* Configuration must be done while patching still possible */ + ret = rtl8380_configure_rtl8214fc(phydev); + if (ret) + return ret; + } + + return phy_sfp_probe(phydev, &rtl8214fc_sfp_ops); +} + +static int rtl8214c_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int addr = phydev->mdio.addr; + + /* All base addresses of the PHYs start at multiples of 8 */ + devm_phy_package_join(dev, phydev, addr & (~7), + sizeof(struct rtl83xx_shared_private)); + + if (!(addr % 8)) { + struct rtl83xx_shared_private *shared = phydev->shared->priv; + shared->name = "RTL8214C"; + /* Configuration must be done whil patching still possible */ + return rtl8380_configure_rtl8214c(phydev); + } + return 0; +} + +static int rtl8218b_ext_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int addr = phydev->mdio.addr; + + /* All base addresses of the PHYs start at multiples of 8 */ + devm_phy_package_join(dev, phydev, addr & (~7), + sizeof(struct rtl83xx_shared_private)); + + if (!(addr % 8)) { + struct rtl83xx_shared_private *shared = phydev->shared->priv; + shared->name = "RTL8218B (external)"; + if (soc_info.family == RTL8380_FAMILY_ID) { + /* Configuration must be done while patching still possible */ + return rtl8380_configure_ext_rtl8218b(phydev); + } + } + + return 0; +} + +static int rtl8218b_int_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int addr = phydev->mdio.addr; + + if (soc_info.family != RTL8380_FAMILY_ID) + return -ENODEV; + if (addr >= 24) + return -ENODEV; + + pr_debug("%s: id: %d\n", __func__, addr); + /* All base addresses of the PHYs start at multiples of 8 */ + devm_phy_package_join(dev, phydev, addr & (~7), + sizeof(struct rtl83xx_shared_private)); + + if (!(addr % 8)) { + struct rtl83xx_shared_private *shared = phydev->shared->priv; + shared->name = "RTL8218B (internal)"; + /* Configuration must be done while patching still possible */ + return rtl8380_configure_int_rtl8218b(phydev); + } + + return 0; +} + +static int rtl8218d_phy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + int addr = phydev->mdio.addr; + + pr_debug("%s: id: %d\n", __func__, addr); + /* All base addresses of the PHYs start at multiples of 8 */ + devm_phy_package_join(dev, phydev, addr & (~7), + sizeof(struct rtl83xx_shared_private)); + + /* All base addresses of the PHYs start at multiples of 8 */ + if (!(addr % 8)) { + struct rtl83xx_shared_private *shared = phydev->shared->priv; + shared->name = "RTL8218D"; + /* Configuration must be done while patching still possible */ +// TODO: return configure_rtl8218d(phydev); + } + return 0; +} + +static int rtl838x_serdes_probe(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + if (soc_info.family != RTL8380_FAMILY_ID) + return -ENODEV; + if (addr < 24) + return -ENODEV; + + /* On the RTL8380M, PHYs 24-27 connect to the internal SerDes */ + if (soc_info.id == 0x8380) { + if (addr == 24) + return rtl8380_configure_serdes(phydev); + return 0; + } + return -ENODEV; +} + +static int rtl8393_serdes_probe(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + pr_info("%s: id: %d\n", __func__, addr); + if (soc_info.family != RTL8390_FAMILY_ID) + return -ENODEV; + + if (addr < 24) + return -ENODEV; + + return rtl8390_configure_serdes(phydev); +} + +static int rtl8390_serdes_probe(struct phy_device *phydev) +{ + int addr = phydev->mdio.addr; + + if (soc_info.family != RTL8390_FAMILY_ID) + return -ENODEV; + + if (addr < 24) + return -ENODEV; + + return rtl8390_configure_generic(phydev); +} + +static int rtl9300_serdes_probe(struct phy_device *phydev) +{ + if (soc_info.family != RTL9300_FAMILY_ID) + return -ENODEV; + + phydev_info(phydev, "Detected internal RTL9300 Serdes\n"); + + return rtl9300_configure_serdes(phydev); +} + +static struct phy_driver rtl83xx_phy_driver[] = { + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8214C), + .name = "Realtek RTL8214C", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .match_phy_device = rtl8214c_match_phy_device, + .probe = rtl8214c_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC), + .name = "Realtek RTL8214FC", + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .match_phy_device = rtl8214fc_match_phy_device, + .probe = rtl8214fc_phy_probe, + .suspend = rtl8214fc_suspend, + .resume = rtl8214fc_resume, + .set_loopback = genphy_loopback, + .set_port = rtl8214fc_set_port, + .get_port = rtl8214fc_get_port, + .set_eee = rtl8214fc_set_eee, + .get_eee = rtl8214fc_get_eee, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_E), + .name = "Realtek RTL8218B (external)", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .match_phy_device = rtl8218b_ext_match_phy_device, + .probe = rtl8218b_ext_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .set_eee = rtl8218b_set_eee, + .get_eee = rtl8218b_get_eee, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218D), + .name = "REALTEK RTL8218D", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .probe = rtl8218d_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .set_eee = rtl8218d_set_eee, + .get_eee = rtl8218d_get_eee, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8221B), + .name = "REALTEK RTL8221B", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_page = rtl8226_read_page, + .write_page = rtl8226_write_page, + .read_status = rtl8226_read_status, + .config_aneg = rtl8226_config_aneg, + .set_eee = rtl8226_set_eee, + .get_eee = rtl8226_get_eee, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8226), + .name = "REALTEK RTL8226", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_page = rtl8226_read_page, + .write_page = rtl8226_write_page, + .read_status = rtl8226_read_status, + .config_aneg = rtl8226_config_aneg, + .set_eee = rtl8226_set_eee, + .get_eee = rtl8226_get_eee, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), + .name = "Realtek RTL8218B (internal)", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .probe = rtl8218b_int_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .set_eee = rtl8218b_set_eee, + .get_eee = rtl8218b_get_eee, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), + .name = "Realtek RTL8380 SERDES", + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .probe = rtl838x_serdes_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_status = rtl8380_read_status, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8393_I), + .name = "Realtek RTL8393 SERDES", + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .probe = rtl8393_serdes_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_status = rtl8393_read_status, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL8390_GENERIC), + .name = "Realtek RTL8390 Generic", + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .probe = rtl8390_serdes_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + }, + { + PHY_ID_MATCH_MODEL(PHY_ID_RTL9300_I), + .name = "REALTEK RTL9300 SERDES", + .features = PHY_GBIT_FIBRE_FEATURES, + .flags = PHY_HAS_REALTEK_PAGES, + .probe = rtl9300_serdes_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .set_loopback = genphy_loopback, + .read_status = rtl9300_read_status, + }, +}; + +module_phy_driver(rtl83xx_phy_driver); + +static struct mdio_device_id __maybe_unused rtl83xx_tbl[] = { + { PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC) }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, rtl83xx_tbl); + +MODULE_AUTHOR("B. Koblitz"); +MODULE_DESCRIPTION("RTL83xx PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.h b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.h new file mode 100644 index 00000000000..553d9a1575e --- /dev/null +++ b/target/linux/realtek/files-5.15/drivers/net/phy/rtl83xx-phy.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-only + +struct rtl83xx_shared_private { + char *name; +}; + +struct __attribute__ ((__packed__)) part { + uint16_t start; + uint8_t wordsize; + uint8_t words; +}; + +struct __attribute__ ((__packed__)) fw_header { + uint32_t magic; + uint32_t phy; + uint32_t checksum; + uint32_t version; + struct part parts[10]; +}; + +// TODO: fixed path? +#define FIRMWARE_838X_8380_1 "rtl838x_phy/rtl838x_8380.fw" +#define FIRMWARE_838X_8214FC_1 "rtl838x_phy/rtl838x_8214fc.fw" +#define FIRMWARE_838X_8218b_1 "rtl838x_phy/rtl838x_8218b.fw" + +/* External RTL8218B and RTL8214FC IDs are identical */ +#define PHY_ID_RTL8214C 0x001cc942 +#define PHY_ID_RTL8214FC 0x001cc981 +#define PHY_ID_RTL8218B_E 0x001cc981 +#define PHY_ID_RTL8218D 0x001cc983 +#define PHY_ID_RTL8218B_I 0x001cca40 +#define PHY_ID_RTL8221B 0x001cc849 +#define PHY_ID_RTL8226 0x001cc838 +#define PHY_ID_RTL8390_GENERIC 0x001ccab0 +#define PHY_ID_RTL8393_I 0x001c8393 +#define PHY_ID_RTL9300_I 0x70d03106 + +// PHY MMD devices +#define MMD_AN 7 +#define MMD_VEND2 31 + +/* Registers of the internal Serdes of the 8380 */ +#define RTL838X_SDS_MODE_SEL (0x0028) +#define RTL838X_SDS_CFG_REG (0x0034) +#define RTL838X_INT_MODE_CTRL (0x005c) +#define RTL838X_DMY_REG31 (0x3b28) + +#define RTL8380_SDS4_FIB_REG0 (0xF800) +#define RTL838X_SDS4_REG28 (0xef80) +#define RTL838X_SDS4_DUMMY0 (0xef8c) +#define RTL838X_SDS5_EXT_REG6 (0xf18c) +#define RTL838X_SDS4_FIB_REG0 (RTL838X_SDS4_REG28 + 0x880) +#define RTL838X_SDS5_FIB_REG0 (RTL838X_SDS4_REG28 + 0x980) + +/* Registers of the internal SerDes of the RTL8390 */ +#define RTL839X_SDS12_13_XSG0 (0xB800) + +/* Registers of the internal Serdes of the 9300 */ +#define RTL930X_SDS_INDACS_CMD (0x03B0) +#define RTL930X_SDS_INDACS_DATA (0x03B4) +#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C) + +/*Registers of the internal SerDes of the 9310 */ +#define RTL931X_SERDES_INDRT_ACCESS_CTRL (0x5638) +#define RTL931X_SERDES_INDRT_DATA_CTRL (0x563C) +#define RTL931X_SERDES_MODE_CTRL (0x13cc) +#define RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR (0x13F4) +#define RTL931X_MAC_SERDES_MODE_CTRL(sds) (0x136C + (((sds) << 2))) diff --git a/target/linux/realtek/files-5.15/include/dt-bindings/clock/rtl83xx-clk.h b/target/linux/realtek/files-5.15/include/dt-bindings/clock/rtl83xx-clk.h new file mode 100644 index 00000000000..3937052cc50 --- /dev/null +++ b/target/linux/realtek/files-5.15/include/dt-bindings/clock/rtl83xx-clk.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 Markus Stockhausen + * + * RTL83XX clock indices + */ +#ifndef __DT_BINDINGS_CLOCK_RTL83XX_H +#define __DT_BINDINGS_CLOCK_RTL83XX_H + +#define CLK_CPU 0 +#define CLK_MEM 1 +#define CLK_LXB 2 +#define CLK_COUNT 3 + +#endif /* __DT_BINDINGS_CLOCK_RTL83XX_H */ diff --git a/target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch b/target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch new file mode 100644 index 00000000000..80b806d89ff --- /dev/null +++ b/target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch @@ -0,0 +1,103 @@ +From a362c0ce64866939c3daa17c76943cfed555b065 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Tue, 30 Mar 2021 19:48:42 +0200 +Subject: dt-bindings: gpio: Binding for Realtek Otto GPIO + +Add a binding description for Realtek's GPIO controller found on several +of their MIPS-based SoCs (codenamed Otto), such as the RTL838x and +RTL839x series of switch SoCs. + +A fallback binding 'realtek,otto-gpio' is provided for cases where the +actual port ordering is not known yet, and enabling the interrupt +controller may result in uncaught interrupts. + +Signed-off-by: Sander Vanheule +Reviewed-by: Linus Walleij +Reviewed-by: Rob Herring +Signed-off-by: Bartosz Golaszewski +--- + .../bindings/gpio/realtek,otto-gpio.yaml | 78 ++++++++++++++++++++++ + 1 file changed, 78 insertions(+) + create mode 100644 Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml +@@ -0,0 +1,78 @@ ++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/gpio/realtek,otto-gpio.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Realtek Otto GPIO controller ++ ++maintainers: ++ - Sander Vanheule ++ - Bert Vermeulen ++ ++description: | ++ Realtek's GPIO controller on their MIPS switch SoCs (Otto platform) consists ++ of two banks of 32 GPIOs. These GPIOs can generate edge-triggered interrupts. ++ Each bank's interrupts are cascased into one interrupt line on the parent ++ interrupt controller, if provided. ++ This binding allows defining a single bank in the devicetree. The interrupt ++ controller is not supported on the fallback compatible name, which only ++ allows for GPIO port use. ++ ++properties: ++ $nodename: ++ pattern: "^gpio@[0-9a-f]+$" ++ ++ compatible: ++ items: ++ - enum: ++ - realtek,rtl8380-gpio ++ - realtek,rtl8390-gpio ++ - const: realtek,otto-gpio ++ ++ reg: ++ maxItems: 1 ++ ++ "#gpio-cells": ++ const: 2 ++ ++ gpio-controller: true ++ ++ ngpios: ++ minimum: 1 ++ maximum: 32 ++ ++ interrupt-controller: true ++ ++ "#interrupt-cells": ++ const: 2 ++ ++ interrupts: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ - "#gpio-cells" ++ - gpio-controller ++ ++additionalProperties: false ++ ++dependencies: ++ interrupt-controller: [ interrupts ] ++ ++examples: ++ - | ++ gpio@3500 { ++ compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio"; ++ reg = <0x3500 0x1c>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpios = <24>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupt-parent = <&rtlintc>; ++ interrupts = <23>; ++ }; ++ ++... diff --git a/target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch b/target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch new file mode 100644 index 00000000000..62e4338cbb2 --- /dev/null +++ b/target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch @@ -0,0 +1,394 @@ +From f0f7d662e8514169c90d3d84cd6df773b2983088 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Tue, 30 Mar 2021 19:48:43 +0200 +Subject: gpio: Add Realtek Otto GPIO support + +Realtek MIPS SoCs (platform name Otto) have GPIO controllers with up to +64 GPIOs, divided over two banks. Each bank has a set of registers for +32 GPIOs, with support for edge-triggered interrupts. + +Each GPIO bank consists of four 8-bit GPIO ports (ABCD and EFGH). Most +registers pack one bit per GPIO, except for the IMR register, which +packs two bits per GPIO (AB-CD). + +Although the byte order is currently assumed to have port A..D at offset +0x0..0x3, this has been observed to be reversed on other, Lexra-based, +SoCs (e.g. RTL8196E/97D/97F). + +Interrupt support is disabled for the fallback devicetree-compatible +'realtek,otto-gpio'. This allows for quick support of GPIO banks in +which the byte order would be unknown. In this case, the port ordering +in the IMR registers may not match the reversed order in the other +registers (DCBA, and BA-DC or DC-BA). + +Signed-off-by: Sander Vanheule +Reviewed-by: Linus Walleij +Reviewed-by: Andy Shevchenko +Signed-off-by: Bartosz Golaszewski +--- + drivers/gpio/Kconfig | 13 ++ + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-realtek-otto.c | 325 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 339 insertions(+) + create mode 100644 drivers/gpio/gpio-realtek-otto.c + +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -489,6 +489,19 @@ config GPIO_RDA + help + Say Y here to support RDA Micro GPIO controller. + ++config GPIO_REALTEK_OTTO ++ tristate "Realtek Otto GPIO support" ++ depends on MACH_REALTEK_RTL ++ default MACH_REALTEK_RTL ++ select GPIO_GENERIC ++ select GPIOLIB_IRQCHIP ++ help ++ The GPIO controller on the Otto MIPS platform supports up to two ++ banks of 32 GPIOs, with edge triggered interrupts. The 32 GPIOs ++ are grouped in four 8-bit wide ports. ++ ++ When built as a module, the module will be called realtek_otto_gpio. ++ + config GPIO_REG + bool + help +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -125,6 +125,7 @@ obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t + obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o + obj-$(CONFIG_GPIO_RDA) += gpio-rda.o + obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o ++obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o + obj-$(CONFIG_GPIO_REG) += gpio-reg.o + obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o + obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o +--- /dev/null ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -0,0 +1,325 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Total register block size is 0x1C for one bank of four ports (A, B, C, D). ++ * An optional second bank, with ports E, F, G, and H, may be present, starting ++ * at register offset 0x1C. ++ */ ++ ++/* ++ * Pin select: (0) "normal", (1) "dedicate peripheral" ++ * Not used on RTL8380/RTL8390, peripheral selection is managed by control bits ++ * in the peripheral registers. ++ */ ++#define REALTEK_GPIO_REG_CNR 0x00 ++/* Clear bit (0) for input, set bit (1) for output */ ++#define REALTEK_GPIO_REG_DIR 0x08 ++#define REALTEK_GPIO_REG_DATA 0x0C ++/* Read bit for IRQ status, write 1 to clear IRQ */ ++#define REALTEK_GPIO_REG_ISR 0x10 ++/* Two bits per GPIO in IMR registers */ ++#define REALTEK_GPIO_REG_IMR 0x14 ++#define REALTEK_GPIO_REG_IMR_AB 0x14 ++#define REALTEK_GPIO_REG_IMR_CD 0x18 ++#define REALTEK_GPIO_IMR_LINE_MASK GENMASK(1, 0) ++#define REALTEK_GPIO_IRQ_EDGE_FALLING 1 ++#define REALTEK_GPIO_IRQ_EDGE_RISING 2 ++#define REALTEK_GPIO_IRQ_EDGE_BOTH 3 ++ ++#define REALTEK_GPIO_MAX 32 ++#define REALTEK_GPIO_PORTS_PER_BANK 4 ++ ++/** ++ * realtek_gpio_ctrl - Realtek Otto GPIO driver data ++ * ++ * @gc: Associated gpio_chip instance ++ * @base: Base address of the register block for a GPIO bank ++ * @lock: Lock for accessing the IRQ registers and values ++ * @intr_mask: Mask for interrupts lines ++ * @intr_type: Interrupt type selection ++ * ++ * Because the interrupt mask register (IMR) combines the function of IRQ type ++ * selection and masking, two extra values are stored. @intr_mask is used to ++ * mask/unmask the interrupts for a GPIO port, and @intr_type is used to store ++ * the selected interrupt types. The logical AND of these values is written to ++ * IMR on changes. ++ */ ++struct realtek_gpio_ctrl { ++ struct gpio_chip gc; ++ void __iomem *base; ++ raw_spinlock_t lock; ++ u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK]; ++ u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK]; ++}; ++ ++/* Expand with more flags as devices with other quirks are added */ ++enum realtek_gpio_flags { ++ /* ++ * Allow disabling interrupts, for cases where the port order is ++ * unknown. This may result in a port mismatch between ISR and IMR. ++ * An interrupt would appear to come from a different line than the ++ * line the IRQ handler was assigned to, causing uncaught interrupts. ++ */ ++ GPIO_INTERRUPTS_DISABLED = BIT(0), ++}; ++ ++static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(data); ++ ++ return container_of(gc, struct realtek_gpio_ctrl, gc); ++} ++ ++/* ++ * Normal port order register access ++ * ++ * Port information is stored with the first port at offset 0, followed by the ++ * second, etc. Most registers store one bit per GPIO and use a u8 value per ++ * port. The two interrupt mask registers store two bits per GPIO, so use u16 ++ * values. ++ */ ++static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl, ++ unsigned int port, u16 irq_type, u16 irq_mask) ++{ ++ iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port); ++} ++ ++static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, ++ unsigned int port, u8 mask) ++{ ++ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port); ++} ++ ++static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port) ++{ ++ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port); ++} ++ ++/* Set the rising and falling edge mask bits for a GPIO port pin */ ++static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value) ++{ ++ return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin; ++} ++ ++static void realtek_gpio_irq_ack(struct irq_data *data) ++{ ++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); ++ irq_hw_number_t line = irqd_to_hwirq(data); ++ unsigned int port = line / 8; ++ unsigned int port_pin = line % 8; ++ ++ realtek_gpio_clear_isr(ctrl, port, BIT(port_pin)); ++} ++ ++static void realtek_gpio_irq_unmask(struct irq_data *data) ++{ ++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); ++ unsigned int line = irqd_to_hwirq(data); ++ unsigned int port = line / 8; ++ unsigned int port_pin = line % 8; ++ unsigned long flags; ++ u16 m; ++ ++ raw_spin_lock_irqsave(&ctrl->lock, flags); ++ m = ctrl->intr_mask[port]; ++ m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); ++ ctrl->intr_mask[port] = m; ++ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); ++ raw_spin_unlock_irqrestore(&ctrl->lock, flags); ++} ++ ++static void realtek_gpio_irq_mask(struct irq_data *data) ++{ ++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); ++ unsigned int line = irqd_to_hwirq(data); ++ unsigned int port = line / 8; ++ unsigned int port_pin = line % 8; ++ unsigned long flags; ++ u16 m; ++ ++ raw_spin_lock_irqsave(&ctrl->lock, flags); ++ m = ctrl->intr_mask[port]; ++ m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); ++ ctrl->intr_mask[port] = m; ++ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); ++ raw_spin_unlock_irqrestore(&ctrl->lock, flags); ++} ++ ++static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) ++{ ++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); ++ unsigned int line = irqd_to_hwirq(data); ++ unsigned int port = line / 8; ++ unsigned int port_pin = line % 8; ++ unsigned long flags; ++ u16 type, t; ++ ++ switch (flow_type & IRQ_TYPE_SENSE_MASK) { ++ case IRQ_TYPE_EDGE_FALLING: ++ type = REALTEK_GPIO_IRQ_EDGE_FALLING; ++ break; ++ case IRQ_TYPE_EDGE_RISING: ++ type = REALTEK_GPIO_IRQ_EDGE_RISING; ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ type = REALTEK_GPIO_IRQ_EDGE_BOTH; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ irq_set_handler_locked(data, handle_edge_irq); ++ ++ raw_spin_lock_irqsave(&ctrl->lock, flags); ++ t = ctrl->intr_type[port]; ++ t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); ++ t |= realtek_gpio_imr_bits(port_pin, type); ++ ctrl->intr_type[port] = t; ++ realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]); ++ raw_spin_unlock_irqrestore(&ctrl->lock, flags); ++ ++ return 0; ++} ++ ++static void realtek_gpio_irq_handler(struct irq_desc *desc) ++{ ++ struct gpio_chip *gc = irq_desc_get_handler_data(desc); ++ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); ++ struct irq_chip *irq_chip = irq_desc_get_chip(desc); ++ unsigned int lines_done; ++ unsigned int port_pin_count; ++ unsigned int irq; ++ unsigned long status; ++ int offset; ++ ++ chained_irq_enter(irq_chip, desc); ++ ++ for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) { ++ status = realtek_gpio_read_isr(ctrl, lines_done / 8); ++ port_pin_count = min(gc->ngpio - lines_done, 8U); ++ for_each_set_bit(offset, &status, port_pin_count) { ++ irq = irq_find_mapping(gc->irq.domain, offset); ++ generic_handle_irq(irq); ++ } ++ } ++ ++ chained_irq_exit(irq_chip, desc); ++} ++ ++static int realtek_gpio_irq_init(struct gpio_chip *gc) ++{ ++ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); ++ unsigned int port; ++ ++ for (port = 0; (port * 8) < gc->ngpio; port++) { ++ realtek_gpio_write_imr(ctrl, port, 0, 0); ++ realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0)); ++ } ++ ++ return 0; ++} ++ ++static struct irq_chip realtek_gpio_irq_chip = { ++ .name = "realtek-otto-gpio", ++ .irq_ack = realtek_gpio_irq_ack, ++ .irq_mask = realtek_gpio_irq_mask, ++ .irq_unmask = realtek_gpio_irq_unmask, ++ .irq_set_type = realtek_gpio_irq_set_type, ++}; ++ ++static const struct of_device_id realtek_gpio_of_match[] = { ++ { ++ .compatible = "realtek,otto-gpio", ++ .data = (void *)GPIO_INTERRUPTS_DISABLED, ++ }, ++ { ++ .compatible = "realtek,rtl8380-gpio", ++ }, ++ { ++ .compatible = "realtek,rtl8390-gpio", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, realtek_gpio_of_match); ++ ++static int realtek_gpio_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ unsigned int dev_flags; ++ struct gpio_irq_chip *girq; ++ struct realtek_gpio_ctrl *ctrl; ++ u32 ngpios; ++ int err, irq; ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ dev_flags = (unsigned int) device_get_match_data(dev); ++ ++ ngpios = REALTEK_GPIO_MAX; ++ device_property_read_u32(dev, "ngpios", &ngpios); ++ ++ if (ngpios > REALTEK_GPIO_MAX) { ++ dev_err(&pdev->dev, "invalid ngpios (max. %d)\n", ++ REALTEK_GPIO_MAX); ++ return -EINVAL; ++ } ++ ++ ctrl->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(ctrl->base)) ++ return PTR_ERR(ctrl->base); ++ ++ raw_spin_lock_init(&ctrl->lock); ++ ++ err = bgpio_init(&ctrl->gc, dev, 4, ++ ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL, ++ ctrl->base + REALTEK_GPIO_REG_DIR, NULL, ++ BGPIOF_BIG_ENDIAN_BYTE_ORDER); ++ if (err) { ++ dev_err(dev, "unable to init generic GPIO"); ++ return err; ++ } ++ ++ ctrl->gc.ngpio = ngpios; ++ ctrl->gc.owner = THIS_MODULE; ++ ++ irq = platform_get_irq_optional(pdev, 0); ++ if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) { ++ girq = &ctrl->gc.irq; ++ girq->chip = &realtek_gpio_irq_chip; ++ girq->default_type = IRQ_TYPE_NONE; ++ girq->handler = handle_bad_irq; ++ girq->parent_handler = realtek_gpio_irq_handler; ++ girq->num_parents = 1; ++ girq->parents = devm_kcalloc(dev, girq->num_parents, ++ sizeof(*girq->parents), GFP_KERNEL); ++ if (!girq->parents) ++ return -ENOMEM; ++ girq->parents[0] = irq; ++ girq->init_hw = realtek_gpio_irq_init; ++ } ++ ++ return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); ++} ++ ++static struct platform_driver realtek_gpio_driver = { ++ .driver = { ++ .name = "realtek-otto-gpio", ++ .of_match_table = realtek_gpio_of_match, ++ }, ++ .probe = realtek_gpio_probe, ++}; ++module_platform_driver(realtek_gpio_driver); ++ ++MODULE_DESCRIPTION("Realtek Otto GPIO support"); ++MODULE_AUTHOR("Sander Vanheule "); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch b/target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch new file mode 100644 index 00000000000..325e904994a --- /dev/null +++ b/target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch @@ -0,0 +1,57 @@ +From 6acbd614c2c8d3b8de5fb7605d6e24b9b3a8a17b Mon Sep 17 00:00:00 2001 +From: Bert Vermeulen +Date: Wed, 20 Jan 2021 14:59:27 +0100 +Subject: spi: Realtek RTL838x/RTL839x SPI controller + +Signed-off-by: Bert Vermeulen +Link: https://lore.kernel.org/r/20210120135928.246054-2-bert@biot.com +Signed-off-by: Mark Brown +--- + .../devicetree/bindings/spi/realtek,rtl-spi.yaml | 41 ++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + create mode 100644 Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml +@@ -0,0 +1,41 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/spi/realtek,rtl-spi.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Realtek RTL838x/RTL839x SPI controller ++ ++maintainers: ++ - Bert Vermeulen ++ - Birger Koblitz ++ ++allOf: ++ - $ref: "spi-controller.yaml#" ++ ++properties: ++ compatible: ++ oneOf: ++ - const: realtek,rtl8380-spi ++ - const: realtek,rtl8382-spi ++ - const: realtek,rtl8391-spi ++ - const: realtek,rtl8392-spi ++ - const: realtek,rtl8393-spi ++ ++ reg: ++ maxItems: 1 ++ ++required: ++ - compatible ++ - reg ++ ++unevaluatedProperties: false ++ ++examples: ++ - | ++ spi: spi@1200 { ++ compatible = "realtek,rtl8382-spi"; ++ reg = <0x1200 0x100>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; diff --git a/target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch b/target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch new file mode 100644 index 00000000000..090847baad7 --- /dev/null +++ b/target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch @@ -0,0 +1,248 @@ +From a8af5cc2ff1e804694629a8ef320935629dd15ba Mon Sep 17 00:00:00 2001 +From: Bert Vermeulen +Date: Wed, 20 Jan 2021 14:59:28 +0100 +Subject: spi: realtek-rtl: Add support for Realtek RTL838x/RTL839x SPI + controllers + +This driver likely also supports earlier (RTL8196) and later (RTL93xx) +SoCs. + +The SPI hardware in these SoCs is specifically intended for connecting NOR +bootflash chips, and only used for that in dozens of examined devices. +However boiled down to basics, it's really just a half-duplex SPI +controller. + +The hardware appears to have a vestigial second chip-select control, but +it hasn't been seen in the wild and is thus not supported. + +Signed-off-by: Bert Vermeulen +Link: https://lore.kernel.org/r/20210120135928.246054-3-bert@biot.com +Signed-off-by: Mark Brown +--- + drivers/spi/Makefile | 1 + + drivers/spi/spi-realtek-rtl.c | 209 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 210 insertions(+) + create mode 100644 drivers/spi/spi-realtek-rtl.c + +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -94,6 +94,7 @@ obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom + obj-$(CONFIG_SPI_QUP) += spi-qup.o + obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o + obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o ++obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o + obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o + obj-$(CONFIG_SPI_RSPI) += spi-rspi.o + obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o +--- /dev/null ++++ b/drivers/spi/spi-realtek-rtl.c +@@ -0,0 +1,209 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++#include ++#include ++#include ++#include ++ ++struct rtspi { ++ void __iomem *base; ++}; ++ ++/* SPI Flash Configuration Register */ ++#define RTL_SPI_SFCR 0x00 ++#define RTL_SPI_SFCR_RBO BIT(28) ++#define RTL_SPI_SFCR_WBO BIT(27) ++ ++/* SPI Flash Control and Status Register */ ++#define RTL_SPI_SFCSR 0x08 ++#define RTL_SPI_SFCSR_CSB0 BIT(31) ++#define RTL_SPI_SFCSR_CSB1 BIT(30) ++#define RTL_SPI_SFCSR_RDY BIT(27) ++#define RTL_SPI_SFCSR_CS BIT(24) ++#define RTL_SPI_SFCSR_LEN_MASK ~(0x03 << 28) ++#define RTL_SPI_SFCSR_LEN1 (0x00 << 28) ++#define RTL_SPI_SFCSR_LEN4 (0x03 << 28) ++ ++/* SPI Flash Data Register */ ++#define RTL_SPI_SFDR 0x0c ++ ++#define REG(x) (rtspi->base + x) ++ ++ ++static void rt_set_cs(struct spi_device *spi, bool active) ++{ ++ struct rtspi *rtspi = spi_controller_get_devdata(spi->controller); ++ u32 value; ++ ++ /* CS0 bit is active low */ ++ value = readl(REG(RTL_SPI_SFCSR)); ++ if (active) ++ value |= RTL_SPI_SFCSR_CSB0; ++ else ++ value &= ~RTL_SPI_SFCSR_CSB0; ++ writel(value, REG(RTL_SPI_SFCSR)); ++} ++ ++static void set_size(struct rtspi *rtspi, int size) ++{ ++ u32 value; ++ ++ value = readl(REG(RTL_SPI_SFCSR)); ++ value &= RTL_SPI_SFCSR_LEN_MASK; ++ if (size == 4) ++ value |= RTL_SPI_SFCSR_LEN4; ++ else if (size == 1) ++ value |= RTL_SPI_SFCSR_LEN1; ++ writel(value, REG(RTL_SPI_SFCSR)); ++} ++ ++static inline void wait_ready(struct rtspi *rtspi) ++{ ++ while (!(readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY)) ++ cpu_relax(); ++} ++static void send4(struct rtspi *rtspi, const u32 *buf) ++{ ++ wait_ready(rtspi); ++ set_size(rtspi, 4); ++ writel(*buf, REG(RTL_SPI_SFDR)); ++} ++ ++static void send1(struct rtspi *rtspi, const u8 *buf) ++{ ++ wait_ready(rtspi); ++ set_size(rtspi, 1); ++ writel(buf[0] << 24, REG(RTL_SPI_SFDR)); ++} ++ ++static void rcv4(struct rtspi *rtspi, u32 *buf) ++{ ++ wait_ready(rtspi); ++ set_size(rtspi, 4); ++ *buf = readl(REG(RTL_SPI_SFDR)); ++} ++ ++static void rcv1(struct rtspi *rtspi, u8 *buf) ++{ ++ wait_ready(rtspi); ++ set_size(rtspi, 1); ++ *buf = readl(REG(RTL_SPI_SFDR)) >> 24; ++} ++ ++static int transfer_one(struct spi_controller *ctrl, struct spi_device *spi, ++ struct spi_transfer *xfer) ++{ ++ struct rtspi *rtspi = spi_controller_get_devdata(ctrl); ++ void *rx_buf; ++ const void *tx_buf; ++ int cnt; ++ ++ tx_buf = xfer->tx_buf; ++ rx_buf = xfer->rx_buf; ++ cnt = xfer->len; ++ if (tx_buf) { ++ while (cnt >= 4) { ++ send4(rtspi, tx_buf); ++ tx_buf += 4; ++ cnt -= 4; ++ } ++ while (cnt) { ++ send1(rtspi, tx_buf); ++ tx_buf++; ++ cnt--; ++ } ++ } else if (rx_buf) { ++ while (cnt >= 4) { ++ rcv4(rtspi, rx_buf); ++ rx_buf += 4; ++ cnt -= 4; ++ } ++ while (cnt) { ++ rcv1(rtspi, rx_buf); ++ rx_buf++; ++ cnt--; ++ } ++ } ++ ++ spi_finalize_current_transfer(ctrl); ++ ++ return 0; ++} ++ ++static void init_hw(struct rtspi *rtspi) ++{ ++ u32 value; ++ ++ /* Turn on big-endian byte ordering */ ++ value = readl(REG(RTL_SPI_SFCR)); ++ value |= RTL_SPI_SFCR_RBO | RTL_SPI_SFCR_WBO; ++ writel(value, REG(RTL_SPI_SFCR)); ++ ++ value = readl(REG(RTL_SPI_SFCSR)); ++ /* Permanently disable CS1, since it's never used */ ++ value |= RTL_SPI_SFCSR_CSB1; ++ /* Select CS0 for use */ ++ value &= RTL_SPI_SFCSR_CS; ++ writel(value, REG(RTL_SPI_SFCSR)); ++} ++ ++static int realtek_rtl_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_controller *ctrl; ++ struct rtspi *rtspi; ++ int err; ++ ++ ctrl = devm_spi_alloc_master(&pdev->dev, sizeof(*rtspi)); ++ if (!ctrl) { ++ dev_err(&pdev->dev, "Error allocating SPI controller\n"); ++ return -ENOMEM; ++ } ++ platform_set_drvdata(pdev, ctrl); ++ rtspi = spi_controller_get_devdata(ctrl); ++ ++ rtspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); ++ if (IS_ERR(rtspi->base)) { ++ dev_err(&pdev->dev, "Could not map SPI register address"); ++ return -ENOMEM; ++ } ++ ++ init_hw(rtspi); ++ ++ ctrl->dev.of_node = pdev->dev.of_node; ++ ctrl->flags = SPI_CONTROLLER_HALF_DUPLEX; ++ ctrl->set_cs = rt_set_cs; ++ ctrl->transfer_one = transfer_one; ++ ++ err = devm_spi_register_controller(&pdev->dev, ctrl); ++ if (err) { ++ dev_err(&pdev->dev, "Could not register SPI controller\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++ ++static const struct of_device_id realtek_rtl_spi_of_ids[] = { ++ { .compatible = "realtek,rtl8380-spi" }, ++ { .compatible = "realtek,rtl8382-spi" }, ++ { .compatible = "realtek,rtl8391-spi" }, ++ { .compatible = "realtek,rtl8392-spi" }, ++ { .compatible = "realtek,rtl8393-spi" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, realtek_rtl_spi_of_ids); ++ ++static struct platform_driver realtek_rtl_spi_driver = { ++ .probe = realtek_rtl_spi_probe, ++ .driver = { ++ .name = "realtek-rtl-spi", ++ .of_match_table = realtek_rtl_spi_of_ids, ++ }, ++}; ++ ++module_platform_driver(realtek_rtl_spi_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Bert Vermeulen "); ++MODULE_DESCRIPTION("Realtek RTL SPI driver"); diff --git a/target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch b/target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch new file mode 100644 index 00000000000..165545a2207 --- /dev/null +++ b/target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch @@ -0,0 +1,78 @@ +From 4a2b92a5d3519fc2c1edda4d4aa0e05bff41e8de Mon Sep 17 00:00:00 2001 +From: Bert Vermeulen +Date: Fri, 22 Jan 2021 21:42:23 +0100 +Subject: dt-bindings: interrupt-controller: Add Realtek RTL838x/RTL839x + support + +Document the binding for the Realtek RTL838x/RTL839x interrupt controller. + +Reviewed-by: Rob Herring +Signed-off-by: Bert Vermeulen +[maz: Add a commit message, as the author couldn't be bothered...] +Signed-off-by: Marc Zyngier +Link: https://lore.kernel.org/r/20210122204224.509124-2-bert@biot.com +--- + .../interrupt-controller/realtek,rtl-intc.yaml | 57 ++++++++++++++++++++++ + 1 file changed, 57 insertions(+) + create mode 100644 Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml + +--- /dev/null ++++ b/Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml +@@ -0,0 +1,57 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/interrupt-controller/realtek,rtl-intc.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Realtek RTL SoC interrupt controller devicetree bindings ++ ++maintainers: ++ - Birger Koblitz ++ - Bert Vermeulen ++ - John Crispin ++ ++properties: ++ compatible: ++ const: realtek,rtl-intc ++ ++ "#interrupt-cells": ++ const: 1 ++ ++ reg: ++ maxItems: 1 ++ ++ interrupts: ++ maxItems: 1 ++ ++ interrupt-controller: true ++ ++ "#address-cells": ++ const: 0 ++ ++ interrupt-map: ++ description: Describes mapping from SoC interrupts to CPU interrupts ++ ++required: ++ - compatible ++ - reg ++ - "#interrupt-cells" ++ - interrupt-controller ++ - "#address-cells" ++ - interrupt-map ++ ++additionalProperties: false ++ ++examples: ++ - | ++ intc: interrupt-controller@3000 { ++ compatible = "realtek,rtl-intc"; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ reg = <0x3000 0x20>; ++ #address-cells = <0>; ++ interrupt-map = ++ <31 &cpuintc 2>, ++ <30 &cpuintc 1>, ++ <29 &cpuintc 5>; ++ }; diff --git a/target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch b/target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch new file mode 100644 index 00000000000..753c41eb2f9 --- /dev/null +++ b/target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch @@ -0,0 +1,211 @@ +From 9f3a0f34b84ad1b9a8f2bdae44b66f16685b2143 Mon Sep 17 00:00:00 2001 +From: Bert Vermeulen +Date: Fri, 22 Jan 2021 21:42:24 +0100 +Subject: irqchip: Add support for Realtek RTL838x/RTL839x interrupt controller + +This is a standard IRQ driver with only status and mask registers. + +The mapping from SoC interrupts (18-31) to MIPS core interrupts is +done via an interrupt-map in device tree. + +Signed-off-by: Bert Vermeulen +Signed-off-by: Birger Koblitz +Acked-by: John Crispin +Signed-off-by: Marc Zyngier +Link: https://lore.kernel.org/r/20210122204224.509124-3-bert@biot.com +--- + drivers/irqchip/Makefile | 1 + + drivers/irqchip/irq-realtek-rtl.c | 180 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 181 insertions(+) + create mode 100644 drivers/irqchip/irq-realtek-rtl.c + +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -114,3 +114,4 @@ obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-l + obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o + obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o + obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o ++obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o +--- /dev/null ++++ b/drivers/irqchip/irq-realtek-rtl.c +@@ -0,0 +1,180 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2020 Birger Koblitz ++ * Copyright (C) 2020 Bert Vermeulen ++ * Copyright (C) 2020 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* Global Interrupt Mask Register */ ++#define RTL_ICTL_GIMR 0x00 ++/* Global Interrupt Status Register */ ++#define RTL_ICTL_GISR 0x04 ++/* Interrupt Routing Registers */ ++#define RTL_ICTL_IRR0 0x08 ++#define RTL_ICTL_IRR1 0x0c ++#define RTL_ICTL_IRR2 0x10 ++#define RTL_ICTL_IRR3 0x14 ++ ++#define REG(x) (realtek_ictl_base + x) ++ ++static DEFINE_RAW_SPINLOCK(irq_lock); ++static void __iomem *realtek_ictl_base; ++ ++static void realtek_ictl_unmask_irq(struct irq_data *i) ++{ ++ unsigned long flags; ++ u32 value; ++ ++ raw_spin_lock_irqsave(&irq_lock, flags); ++ ++ value = readl(REG(RTL_ICTL_GIMR)); ++ value |= BIT(i->hwirq); ++ writel(value, REG(RTL_ICTL_GIMR)); ++ ++ raw_spin_unlock_irqrestore(&irq_lock, flags); ++} ++ ++static void realtek_ictl_mask_irq(struct irq_data *i) ++{ ++ unsigned long flags; ++ u32 value; ++ ++ raw_spin_lock_irqsave(&irq_lock, flags); ++ ++ value = readl(REG(RTL_ICTL_GIMR)); ++ value &= ~BIT(i->hwirq); ++ writel(value, REG(RTL_ICTL_GIMR)); ++ ++ raw_spin_unlock_irqrestore(&irq_lock, flags); ++} ++ ++static struct irq_chip realtek_ictl_irq = { ++ .name = "realtek-rtl-intc", ++ .irq_mask = realtek_ictl_mask_irq, ++ .irq_unmask = realtek_ictl_unmask_irq, ++}; ++ ++static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) ++{ ++ irq_set_chip_and_handler(hw, &realtek_ictl_irq, handle_level_irq); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops irq_domain_ops = { ++ .map = intc_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ ++static void realtek_irq_dispatch(struct irq_desc *desc) ++{ ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct irq_domain *domain; ++ unsigned int pending; ++ ++ chained_irq_enter(chip, desc); ++ pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); ++ if (unlikely(!pending)) { ++ spurious_interrupt(); ++ goto out; ++ } ++ domain = irq_desc_get_handler_data(desc); ++ generic_handle_irq(irq_find_mapping(domain, __ffs(pending))); ++ ++out: ++ chained_irq_exit(chip, desc); ++} ++ ++/* ++ * SoC interrupts are cascaded to MIPS CPU interrupts according to the ++ * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for ++ * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts ++ * thus go into 4 IRRs. ++ */ ++static int __init map_interrupts(struct device_node *node, struct irq_domain *domain) ++{ ++ struct device_node *cpu_ictl; ++ const __be32 *imap; ++ u32 imaplen, soc_int, cpu_int, tmp, regs[4]; ++ int ret, i, irr_regs[] = { ++ RTL_ICTL_IRR3, ++ RTL_ICTL_IRR2, ++ RTL_ICTL_IRR1, ++ RTL_ICTL_IRR0, ++ }; ++ u8 mips_irqs_set; ++ ++ ret = of_property_read_u32(node, "#address-cells", &tmp); ++ if (ret || tmp) ++ return -EINVAL; ++ ++ imap = of_get_property(node, "interrupt-map", &imaplen); ++ if (!imap || imaplen % 3) ++ return -EINVAL; ++ ++ mips_irqs_set = 0; ++ memset(regs, 0, sizeof(regs)); ++ for (i = 0; i < imaplen; i += 3 * sizeof(u32)) { ++ soc_int = be32_to_cpup(imap); ++ if (soc_int > 31) ++ return -EINVAL; ++ ++ cpu_ictl = of_find_node_by_phandle(be32_to_cpup(imap + 1)); ++ if (!cpu_ictl) ++ return -EINVAL; ++ ret = of_property_read_u32(cpu_ictl, "#interrupt-cells", &tmp); ++ if (ret || tmp != 1) ++ return -EINVAL; ++ of_node_put(cpu_ictl); ++ ++ cpu_int = be32_to_cpup(imap + 2); ++ if (cpu_int > 7) ++ return -EINVAL; ++ ++ if (!(mips_irqs_set & BIT(cpu_int))) { ++ irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch, ++ domain); ++ mips_irqs_set |= BIT(cpu_int); ++ } ++ ++ regs[(soc_int * 4) / 32] |= cpu_int << (soc_int * 4) % 32; ++ imap += 3; ++ } ++ ++ for (i = 0; i < 4; i++) ++ writel(regs[i], REG(irr_regs[i])); ++ ++ return 0; ++} ++ ++static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent) ++{ ++ struct irq_domain *domain; ++ int ret; ++ ++ realtek_ictl_base = of_iomap(node, 0); ++ if (!realtek_ictl_base) ++ return -ENXIO; ++ ++ /* Disable all cascaded interrupts */ ++ writel(0, REG(RTL_ICTL_GIMR)); ++ ++ domain = irq_domain_add_simple(node, 32, 0, ++ &irq_domain_ops, NULL); ++ ++ ret = map_interrupts(node, domain); ++ if (ret) { ++ pr_err("invalid interrupt map\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init); diff --git a/target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch b/target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch new file mode 100644 index 00000000000..9935c572573 --- /dev/null +++ b/target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch @@ -0,0 +1,22 @@ +From: Sander Vanheule +Subject: gpio: realtek-otto: fix GPIO line IRQ offset + +The irqchip uses one domain for all GPIO lines, so th line offset should be +determined w.r.t. the first line of the first port, not the first line of the +triggered port. + +Fixes: 0d82fb1127fb ("gpio: Add Realtek Otto GPIO support") +Signed-off-by: Sander Vanheule +Link: https://lore.kernel.org/linux-gpio/20211028085243.34360-1-sander@svanheule.net/ + +--- a/drivers/gpio/gpio-realtek-otto.c ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -206,7 +206,7 @@ static void realtek_gpio_irq_handler(str + status = realtek_gpio_read_isr(ctrl, lines_done / 8); + port_pin_count = min(gc->ngpio - lines_done, 8U); + for_each_set_bit(offset, &status, port_pin_count) { +- irq = irq_find_mapping(gc->irq.domain, offset); ++ irq = irq_find_mapping(gc->irq.domain, offset + lines_done); + generic_handle_irq(irq); + } + } diff --git a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch new file mode 100644 index 00000000000..845441b4257 --- /dev/null +++ b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch @@ -0,0 +1,467 @@ +From 293903b9dfe43520f01374dc1661be11d6838c49 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Thu, 18 Nov 2021 17:29:52 +0100 +Subject: watchdog: Add Realtek Otto watchdog timer + +Realtek MIPS SoCs (platform name Otto) have a watchdog timer with +pretimeout notifitication support. The WDT can (partially) hard reset, +or soft reset the SoC. + +This driver implements all features as described in the devicetree +binding, except the phase2 interrupt, and also functions as a restart +handler. The cpu reset mode is considered to be a "warm" restart, since +this mode does not reset all peripherals. Being an embedded system +though, the "cpu" and "software" modes will still cause the bootloader +to run on restart. + +It is not known how a forced system reset can be disabled on the +supported platforms. This means that the phase2 interrupt will only fire +at the same time as reset, so implementing phase2 is of little use. + +Signed-off-by: Sander Vanheule +Reviewed-by: Guenter Roeck +Link: https://lore.kernel.org/r/6d060bccbdcc709cfa79203485db85aad3c3beb5.1637252610.git.sander@svanheule.net +Signed-off-by: Guenter Roeck +--- + MAINTAINERS | 7 + + drivers/watchdog/Kconfig | 13 ++ + drivers/watchdog/Makefile | 1 + + drivers/watchdog/realtek_otto_wdt.c | 384 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 405 insertions(+) + create mode 100644 drivers/watchdog/realtek_otto_wdt.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -14816,6 +14816,13 @@ S: Maintained + F: include/sound/rt*.h + F: sound/soc/codecs/rt* + ++REALTEK OTTO WATCHDOG ++M: Sander Vanheule ++L: linux-watchdog@vger.kernel.org ++S: Maintained ++F: Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml ++F: driver/watchdog/realtek_otto_wdt.c ++ + REALTEK RTL83xx SMI DSA ROUTER CHIPS + M: Linus Walleij + S: Maintained +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -995,6 +995,19 @@ config RTD119X_WATCHDOG + Say Y here to include support for the watchdog timer in + Realtek RTD1295 SoCs. + ++config REALTEK_OTTO_WDT ++ tristate "Realtek Otto MIPS watchdog support" ++ depends on MACH_REALTEK_RTL || COMPILE_TEST ++ depends on COMMON_CLK ++ select WATCHDOG_CORE ++ default MACH_REALTEK_RTL ++ help ++ Say Y here to include support for the watchdog timer on Realtek ++ RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout ++ notifications and system reset on timeout. ++ ++ When built as a module this will be called realtek_otto_wdt. ++ + config SPRD_WATCHDOG + tristate "Spreadtrum watchdog support" + depends on ARCH_SPRD || COMPILE_TEST +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -174,6 +174,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o + obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o + obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o + obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o ++obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o + + # PARISC Architecture + +--- /dev/null ++++ b/drivers/watchdog/realtek_otto_wdt.c +@@ -0,0 +1,384 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++ ++/* ++ * Realtek Otto MIPS platform watchdog ++ * ++ * Watchdog timer that will reset the system after timeout, using the selected ++ * reset mode. ++ * ++ * Counter scaling and timeouts: ++ * - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz ++ * - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8} ++ * - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0 ++ * Generates an interrupt, WDT cannot be stopped after phase 1 ++ * - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0 ++ * Resets the system according to RST_MODE ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define OTTO_WDT_REG_CNTR 0x0 ++#define OTTO_WDT_CNTR_PING BIT(31) ++ ++#define OTTO_WDT_REG_INTR 0x4 ++#define OTTO_WDT_INTR_PHASE_1 BIT(31) ++#define OTTO_WDT_INTR_PHASE_2 BIT(30) ++ ++#define OTTO_WDT_REG_CTRL 0x8 ++#define OTTO_WDT_CTRL_ENABLE BIT(31) ++#define OTTO_WDT_CTRL_PRESCALE GENMASK(30, 29) ++#define OTTO_WDT_CTRL_PHASE1 GENMASK(26, 22) ++#define OTTO_WDT_CTRL_PHASE2 GENMASK(19, 15) ++#define OTTO_WDT_CTRL_RST_MODE GENMASK(1, 0) ++#define OTTO_WDT_MODE_SOC 0 ++#define OTTO_WDT_MODE_CPU 1 ++#define OTTO_WDT_MODE_SOFTWARE 2 ++#define OTTO_WDT_CTRL_DEFAULT OTTO_WDT_MODE_CPU ++ ++#define OTTO_WDT_PRESCALE_MAX 3 ++ ++/* ++ * One higher than the max values contained in PHASE{1,2}, since a value of 0 ++ * corresponds to one tick. ++ */ ++#define OTTO_WDT_PHASE_TICKS_MAX 32 ++ ++/* ++ * The maximum reset delay is actually 2×32 ticks, but that would require large ++ * pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout ++ * to 32 + 1 to ensure small pretimeout values can be configured as expected. ++ */ ++#define OTTO_WDT_TIMEOUT_TICKS_MAX (OTTO_WDT_PHASE_TICKS_MAX + 1) ++ ++struct otto_wdt_ctrl { ++ struct watchdog_device wdev; ++ struct device *dev; ++ void __iomem *base; ++ unsigned int clk_rate_khz; ++ int irq_phase1; ++}; ++ ++static int otto_wdt_start(struct watchdog_device *wdev) ++{ ++ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); ++ u32 v; ++ ++ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); ++ v |= OTTO_WDT_CTRL_ENABLE; ++ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); ++ ++ return 0; ++} ++ ++static int otto_wdt_stop(struct watchdog_device *wdev) ++{ ++ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); ++ u32 v; ++ ++ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); ++ v &= ~OTTO_WDT_CTRL_ENABLE; ++ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); ++ ++ return 0; ++} ++ ++static int otto_wdt_ping(struct watchdog_device *wdev) ++{ ++ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); ++ ++ iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR); ++ ++ return 0; ++} ++ ++static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale) ++{ ++ return DIV_ROUND_CLOSEST(1 << (25 + prescale), ctrl->clk_rate_khz); ++} ++ ++/* ++ * The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks exceeds ++ * the value stored in those fields. This means each phase will run for at least ++ * one tick, so small values need to be clamped to correctly reflect the timeout. ++ */ ++static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration, ++ unsigned int min_ticks) ++{ ++ return max(min_ticks, DIV_ROUND_UP(val, tick_duration)); ++} ++ ++static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout, ++ unsigned int pretimeout) ++{ ++ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); ++ unsigned int pretimeout_ms = pretimeout * 1000; ++ unsigned int timeout_ms = timeout * 1000; ++ unsigned int prescale_next = 0; ++ unsigned int phase1_ticks; ++ unsigned int phase2_ticks; ++ unsigned int total_ticks; ++ unsigned int prescale; ++ unsigned int tick_ms; ++ u32 v; ++ ++ do { ++ prescale = prescale_next; ++ if (prescale > OTTO_WDT_PRESCALE_MAX) ++ return -EINVAL; ++ ++ tick_ms = otto_wdt_tick_ms(ctrl, prescale); ++ total_ticks = div_round_ticks(timeout_ms, tick_ms, 2); ++ phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1); ++ phase2_ticks = total_ticks - phase1_ticks; ++ ++ prescale_next++; ++ } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX ++ || phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX); ++ ++ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); ++ ++ v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2); ++ v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1); ++ v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1); ++ v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale); ++ ++ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); ++ ++ timeout_ms = total_ticks * tick_ms; ++ ctrl->wdev.timeout = timeout_ms / 1000; ++ ++ pretimeout_ms = phase2_ticks * tick_ms; ++ ctrl->wdev.pretimeout = pretimeout_ms / 1000; ++ ++ return 0; ++} ++ ++static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val) ++{ ++ return otto_wdt_determine_timeouts(wdev, val, min(wdev->pretimeout, val - 1)); ++} ++ ++static int otto_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int val) ++{ ++ return otto_wdt_determine_timeouts(wdev, wdev->timeout, val); ++} ++ ++static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_mode, ++ void *data) ++{ ++ struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev); ++ u32 reset_mode; ++ u32 v; ++ ++ disable_irq(ctrl->irq_phase1); ++ ++ switch (reboot_mode) { ++ case REBOOT_SOFT: ++ reset_mode = OTTO_WDT_MODE_SOFTWARE; ++ break; ++ case REBOOT_WARM: ++ reset_mode = OTTO_WDT_MODE_CPU; ++ break; ++ default: ++ reset_mode = OTTO_WDT_MODE_SOC; ++ break; ++ } ++ ++ /* Configure for shortest timeout and wait for reset to occur */ ++ v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE; ++ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); ++ ++ mdelay(3 * otto_wdt_tick_ms(ctrl, 0)); ++ ++ return 0; ++} ++ ++static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id) ++{ ++ struct otto_wdt_ctrl *ctrl = dev_id; ++ ++ iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR); ++ dev_crit(ctrl->dev, "phase 1 timeout\n"); ++ watchdog_notify_pretimeout(&ctrl->wdev); ++ ++ return IRQ_HANDLED; ++} ++ ++static const struct watchdog_ops otto_wdt_ops = { ++ .owner = THIS_MODULE, ++ .start = otto_wdt_start, ++ .stop = otto_wdt_stop, ++ .ping = otto_wdt_ping, ++ .set_timeout = otto_wdt_set_timeout, ++ .set_pretimeout = otto_wdt_set_pretimeout, ++ .restart = otto_wdt_restart, ++}; ++ ++static const struct watchdog_info otto_wdt_info = { ++ .identity = "Realtek Otto watchdog timer", ++ .options = WDIOF_KEEPALIVEPING | ++ WDIOF_MAGICCLOSE | ++ WDIOF_SETTIMEOUT | ++ WDIOF_PRETIMEOUT, ++}; ++ ++static void otto_wdt_clock_action(void *data) ++{ ++ clk_disable_unprepare(data); ++} ++ ++static int otto_wdt_probe_clk(struct otto_wdt_ctrl *ctrl) ++{ ++ struct clk *clk = devm_clk_get(ctrl->dev, NULL); ++ int ret; ++ ++ if (IS_ERR(clk)) ++ return dev_err_probe(ctrl->dev, PTR_ERR(clk), "Failed to get clock\n"); ++ ++ ret = clk_prepare_enable(clk); ++ if (ret) ++ return dev_err_probe(ctrl->dev, ret, "Failed to enable clock\n"); ++ ++ ret = devm_add_action_or_reset(ctrl->dev, otto_wdt_clock_action, clk); ++ if (ret) ++ return ret; ++ ++ ctrl->clk_rate_khz = clk_get_rate(clk) / 1000; ++ if (ctrl->clk_rate_khz == 0) ++ return dev_err_probe(ctrl->dev, -ENXIO, "Failed to get clock rate\n"); ++ ++ return 0; ++} ++ ++static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl) ++{ ++ static const char *mode_property = "realtek,reset-mode"; ++ const struct fwnode_handle *node = ctrl->dev->fwnode; ++ int mode_count; ++ u32 mode; ++ u32 v; ++ ++ if (!node) ++ return -ENXIO; ++ ++ mode_count = fwnode_property_string_array_count(node, mode_property); ++ if (mode_count < 0) ++ return mode_count; ++ else if (mode_count == 0) ++ return 0; ++ else if (mode_count != 1) ++ return -EINVAL; ++ ++ if (fwnode_property_match_string(node, mode_property, "soc") == 0) ++ mode = OTTO_WDT_MODE_SOC; ++ else if (fwnode_property_match_string(node, mode_property, "cpu") == 0) ++ mode = OTTO_WDT_MODE_CPU; ++ else if (fwnode_property_match_string(node, mode_property, "software") == 0) ++ mode = OTTO_WDT_MODE_SOFTWARE; ++ else ++ return -EINVAL; ++ ++ v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); ++ v &= ~OTTO_WDT_CTRL_RST_MODE; ++ v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode); ++ iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL); ++ ++ return 0; ++} ++ ++static int otto_wdt_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct otto_wdt_ctrl *ctrl; ++ unsigned int max_tick_ms; ++ int ret; ++ ++ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ ctrl->dev = dev; ++ ctrl->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(ctrl->base)) ++ return PTR_ERR(ctrl->base); ++ ++ /* Clear any old interrupts and reset initial state */ ++ iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2, ++ ctrl->base + OTTO_WDT_REG_INTR); ++ iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL); ++ ++ ret = otto_wdt_probe_clk(ctrl); ++ if (ret) ++ return ret; ++ ++ ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1"); ++ if (ctrl->irq_phase1 < 0) ++ return ctrl->irq_phase1; ++ ++ ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0, ++ "realtek-otto-wdt", ctrl); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n"); ++ ++ ret = otto_wdt_probe_reset_mode(ctrl); ++ if (ret) ++ return dev_err_probe(dev, ret, "Invalid reset mode specified\n"); ++ ++ ctrl->wdev.parent = dev; ++ ctrl->wdev.info = &otto_wdt_info; ++ ctrl->wdev.ops = &otto_wdt_ops; ++ ++ /* ++ * Since pretimeout cannot be disabled, min. timeout is twice the ++ * subsystem resolution. Max. timeout is ca. 43s at a bus clock of 200MHz. ++ */ ++ ctrl->wdev.min_timeout = 2; ++ max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX); ++ ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX; ++ ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000); ++ ++ watchdog_set_drvdata(&ctrl->wdev, ctrl); ++ watchdog_init_timeout(&ctrl->wdev, 0, dev); ++ watchdog_stop_on_reboot(&ctrl->wdev); ++ watchdog_set_restart_priority(&ctrl->wdev, 128); ++ ++ ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl->wdev.timeout, 1); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to set timeout\n"); ++ ++ return devm_watchdog_register_device(dev, &ctrl->wdev); ++} ++ ++static const struct of_device_id otto_wdt_ids[] = { ++ { .compatible = "realtek,rtl8380-wdt" }, ++ { .compatible = "realtek,rtl8390-wdt" }, ++ { .compatible = "realtek,rtl9300-wdt" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, otto_wdt_ids); ++ ++static struct platform_driver otto_wdt_driver = { ++ .probe = otto_wdt_probe, ++ .driver = { ++ .name = "realtek-otto-watchdog", ++ .of_match_table = otto_wdt_ids, ++ }, ++}; ++module_platform_driver(otto_wdt_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Sander Vanheule "); ++MODULE_DESCRIPTION("Realtek Otto watchdog timer driver"); diff --git a/target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch b/target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch new file mode 100644 index 00000000000..ef2e225717e --- /dev/null +++ b/target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch @@ -0,0 +1,53 @@ +From c6af53f038aa32cec12e8a305ba07c7ef168f1b0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Tue, 4 Jan 2022 12:07:00 +0000 +Subject: [PATCH 2/3] net: mdio: add helpers to extract clause 45 regad and + devad fields + +Add a couple of helpers and definitions to extract the clause 45 regad +and devad fields from the regnum passed into MDIO drivers. + +Tested-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: Russell King (Oracle) +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + include/linux/mdio.h | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/include/linux/mdio.h ++++ b/include/linux/mdio.h +@@ -7,6 +7,7 @@ + #define __LINUX_MDIO_H__ + + #include ++#include + #include + + /* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit +@@ -14,6 +15,7 @@ + */ + #define MII_ADDR_C45 (1<<30) + #define MII_DEVADDR_C45_SHIFT 16 ++#define MII_DEVADDR_C45_MASK GENMASK(20, 16) + #define MII_REGADDR_C45_MASK GENMASK(15, 0) + + struct gpio_desc; +@@ -342,6 +344,16 @@ static inline u32 mdiobus_c45_addr(int d + return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum; + } + ++static inline u16 mdiobus_c45_regad(u32 regnum) ++{ ++ return FIELD_GET(MII_REGADDR_C45_MASK, regnum); ++} ++ ++static inline u16 mdiobus_c45_devad(u32 regnum) ++{ ++ return FIELD_GET(MII_DEVADDR_C45_MASK, regnum); ++} ++ + static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad, + u16 regnum) + { diff --git a/target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch b/target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch new file mode 100644 index 00000000000..8f353514388 --- /dev/null +++ b/target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch @@ -0,0 +1,123 @@ +From 512c5be35223d9baa2629efa1084cf5210eaee80 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sat, 9 Apr 2022 21:55:47 +0200 +Subject: [PATCH 2/6] gpio: realtek-otto: Support reversed port layouts + +The GPIO port layout on the RTL930x SoC series is reversed compared to +the RTL838x and RTL839x SoC series. Add new port offset calculator +functions to ensure the correct order is used when reading port IRQ +data, and ensure bgpio uses the right byte ordering. + +Signed-off-by: Sander Vanheule +Signed-off-by: Bartosz Golaszewski +--- + drivers/gpio/gpio-realtek-otto.c | 55 +++++++++++++++++++++++++++++--- + 1 file changed, 51 insertions(+), 4 deletions(-) + +--- a/drivers/gpio/gpio-realtek-otto.c ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -58,6 +58,8 @@ struct realtek_gpio_ctrl { + raw_spinlock_t lock; + u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK]; + u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK]; ++ unsigned int (*port_offset_u8)(unsigned int port); ++ unsigned int (*port_offset_u16)(unsigned int port); + }; + + /* Expand with more flags as devices with other quirks are added */ +@@ -69,6 +71,11 @@ enum realtek_gpio_flags { + * line the IRQ handler was assigned to, causing uncaught interrupts. + */ + GPIO_INTERRUPTS_DISABLED = BIT(0), ++ /* ++ * Port order is reversed, meaning DCBA register layout for 1-bit ++ * fields, and [BA, DC] for 2-bit fields. ++ */ ++ GPIO_PORTS_REVERSED = BIT(1), + }; + + static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data) +@@ -86,21 +93,50 @@ static struct realtek_gpio_ctrl *irq_dat + * port. The two interrupt mask registers store two bits per GPIO, so use u16 + * values. + */ ++static unsigned int realtek_gpio_port_offset_u8(unsigned int port) ++{ ++ return port; ++} ++ ++static unsigned int realtek_gpio_port_offset_u16(unsigned int port) ++{ ++ return 2 * port; ++} ++ ++/* ++ * Reversed port order register access ++ * ++ * For registers with one bit per GPIO, all ports are stored as u8-s in one ++ * register in reversed order. The two interrupt mask registers store two bits ++ * per GPIO, so use u16 values. The first register contains ports 1 and 0, the ++ * second ports 3 and 2. ++ */ ++static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port) ++{ ++ return 3 - port; ++} ++ ++static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port) ++{ ++ return 2 * (port ^ 1); ++} ++ + static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl, + unsigned int port, u16 irq_type, u16 irq_mask) + { +- iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port); ++ iowrite16(irq_type & irq_mask, ++ ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port)); + } + + static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, + unsigned int port, u8 mask) + { +- iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port); ++ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port)); + } + + static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port) + { +- return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port); ++ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port)); + } + + /* Set the rising and falling edge mask bits for a GPIO port pin */ +@@ -253,6 +289,7 @@ MODULE_DEVICE_TABLE(of, realtek_gpio_of_ + static int realtek_gpio_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; ++ unsigned long bgpio_flags; + unsigned int dev_flags; + struct gpio_irq_chip *girq; + struct realtek_gpio_ctrl *ctrl; +@@ -280,10 +317,20 @@ static int realtek_gpio_probe(struct pla + + raw_spin_lock_init(&ctrl->lock); + ++ if (dev_flags & GPIO_PORTS_REVERSED) { ++ bgpio_flags = 0; ++ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev; ++ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev; ++ } else { ++ bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; ++ ctrl->port_offset_u8 = realtek_gpio_port_offset_u8; ++ ctrl->port_offset_u16 = realtek_gpio_port_offset_u16; ++ } ++ + err = bgpio_init(&ctrl->gc, dev, 4, + ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL, + ctrl->base + REALTEK_GPIO_REG_DIR, NULL, +- BGPIOF_BIG_ENDIAN_BYTE_ORDER); ++ bgpio_flags); + if (err) { + dev_err(dev, "unable to init generic GPIO"); + return err; diff --git a/target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch b/target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch new file mode 100644 index 00000000000..8f5d571ea2f --- /dev/null +++ b/target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch @@ -0,0 +1,153 @@ +From 95fa6dbe58f286a8f87cb37b7516232eb678de2d Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sat, 9 Apr 2022 21:55:48 +0200 +Subject: [PATCH 3/6] gpio: realtek-otto: Support per-cpu interrupts + +On SoCs with multiple cores, it is possible that the GPIO interrupt +controller supports assigning specific pins to one or more cores. + +IRQ balancing can be performed on a line-by-line basis if the parent +interrupt is routed to all available cores, which is the default upon +initialisation. + +Signed-off-by: Sander Vanheule +Signed-off-by: Bartosz Golaszewski +--- + drivers/gpio/gpio-realtek-otto.c | 75 +++++++++++++++++++++++++++++++- + 1 file changed, 74 insertions(+), 1 deletion(-) + +--- a/drivers/gpio/gpio-realtek-otto.c ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + + #include ++#include + #include + #include + #include +@@ -55,6 +56,8 @@ + struct realtek_gpio_ctrl { + struct gpio_chip gc; + void __iomem *base; ++ void __iomem *cpumask_base; ++ struct cpumask cpu_irq_maskable; + raw_spinlock_t lock; + u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK]; + u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK]; +@@ -76,6 +79,11 @@ enum realtek_gpio_flags { + * fields, and [BA, DC] for 2-bit fields. + */ + GPIO_PORTS_REVERSED = BIT(1), ++ /* ++ * Interrupts can be enabled per cpu. This requires a secondary IO ++ * range, where the per-cpu enable masks are located. ++ */ ++ GPIO_INTERRUPTS_PER_CPU = BIT(2), + }; + + static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data) +@@ -250,14 +258,61 @@ static void realtek_gpio_irq_handler(str + chained_irq_exit(irq_chip, desc); + } + ++static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl, ++ unsigned int port, int cpu) ++{ ++ return ctrl->cpumask_base + ctrl->port_offset_u8(port) + ++ REALTEK_GPIO_PORTS_PER_BANK * cpu; ++} ++ ++static int realtek_gpio_irq_set_affinity(struct irq_data *data, ++ const struct cpumask *dest, bool force) ++{ ++ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); ++ unsigned int line = irqd_to_hwirq(data); ++ unsigned int port = line / 8; ++ unsigned int port_pin = line % 8; ++ void __iomem *irq_cpu_mask; ++ unsigned long flags; ++ int cpu; ++ u8 v; ++ ++ if (!ctrl->cpumask_base) ++ return -ENXIO; ++ ++ raw_spin_lock_irqsave(&ctrl->lock, flags); ++ ++ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) { ++ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu); ++ v = ioread8(irq_cpu_mask); ++ ++ if (cpumask_test_cpu(cpu, dest)) ++ v |= BIT(port_pin); ++ else ++ v &= ~BIT(port_pin); ++ ++ iowrite8(v, irq_cpu_mask); ++ } ++ ++ raw_spin_unlock_irqrestore(&ctrl->lock, flags); ++ ++ irq_data_update_effective_affinity(data, dest); ++ ++ return 0; ++} ++ + static int realtek_gpio_irq_init(struct gpio_chip *gc) + { + struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); + unsigned int port; ++ int cpu; + + for (port = 0; (port * 8) < gc->ngpio; port++) { + realtek_gpio_write_imr(ctrl, port, 0, 0); + realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0)); ++ ++ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) ++ iowrite8(GENMASK(7, 0), realtek_gpio_irq_cpu_mask(ctrl, port, cpu)); + } + + return 0; +@@ -269,6 +324,7 @@ static struct irq_chip realtek_gpio_irq_ + .irq_mask = realtek_gpio_irq_mask, + .irq_unmask = realtek_gpio_irq_unmask, + .irq_set_type = realtek_gpio_irq_set_type, ++ .irq_set_affinity = realtek_gpio_irq_set_affinity, + }; + + static const struct of_device_id realtek_gpio_of_match[] = { +@@ -293,8 +349,10 @@ static int realtek_gpio_probe(struct pla + unsigned int dev_flags; + struct gpio_irq_chip *girq; + struct realtek_gpio_ctrl *ctrl; ++ struct resource *res; + u32 ngpios; +- int err, irq; ++ unsigned int nr_cpus; ++ int cpu, err, irq; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) +@@ -355,6 +413,21 @@ static int realtek_gpio_probe(struct pla + girq->init_hw = realtek_gpio_irq_init; + } + ++ cpumask_clear(&ctrl->cpu_irq_maskable); ++ ++ if ((dev_flags & GPIO_INTERRUPTS_PER_CPU) && irq > 0) { ++ ctrl->cpumask_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); ++ if (IS_ERR(ctrl->cpumask_base)) ++ return dev_err_probe(dev, PTR_ERR(ctrl->cpumask_base), ++ "missing CPU IRQ mask registers"); ++ ++ nr_cpus = resource_size(res) / REALTEK_GPIO_PORTS_PER_BANK; ++ nr_cpus = min(nr_cpus, num_present_cpus()); ++ ++ for (cpu = 0; cpu < nr_cpus; cpu++) ++ cpumask_set_cpu(cpu, &ctrl->cpu_irq_maskable); ++ } ++ + return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); + } + diff --git a/target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch b/target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch new file mode 100644 index 00000000000..536c85740a6 --- /dev/null +++ b/target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch @@ -0,0 +1,29 @@ +From deaf1cecdeb052cdb5e92fd642016198724b44a4 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sat, 9 Apr 2022 21:55:49 +0200 +Subject: [PATCH 4/6] gpio: realtek-otto: Add RTL930x support + +The RTL930x SoC series has support for 24 GPIOs, with the port order +reversed compared to RTL838x and RTL839x. The RTL930x series also has +two CPUs (VPEs) and can distribute individual GPIO interrupts between +them. + +Signed-off-by: Sander Vanheule +Signed-off-by: Bartosz Golaszewski +--- + drivers/gpio/gpio-realtek-otto.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/gpio/gpio-realtek-otto.c ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -338,6 +338,10 @@ static const struct of_device_id realtek + { + .compatible = "realtek,rtl8390-gpio", + }, ++ { ++ .compatible = "realtek,rtl9300-gpio", ++ .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU) ++ }, + {} + }; + MODULE_DEVICE_TABLE(of, realtek_gpio_of_match); diff --git a/target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch b/target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch new file mode 100644 index 00000000000..16990ce053e --- /dev/null +++ b/target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch @@ -0,0 +1,30 @@ +From d3bf3dc4bbbf6109bd9b4bd60089d36205ec4a37 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sat, 9 Apr 2022 21:55:51 +0200 +Subject: [PATCH 6/6] gpio: realtek-otto: Add RTL931x support + +The RTL931x SoC series has support for 32 GPIOs, although not all lines +may be broken out to a physical pad. + +The GPIO bank's parent interrupt can be routed to either or both of the +SoC's CPU cores by the GIC. Line-by-line IRQ balancing is not possible +on these SoCs. + +Signed-off-by: Sander Vanheule +Signed-off-by: Bartosz Golaszewski +--- + drivers/gpio/gpio-realtek-otto.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/gpio/gpio-realtek-otto.c ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -342,6 +342,9 @@ static const struct of_device_id realtek + .compatible = "realtek,rtl9300-gpio", + .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU) + }, ++ { ++ .compatible = "realtek,rtl9310-gpio", ++ }, + {} + }; + MODULE_DEVICE_TABLE(of, realtek_gpio_of_match); diff --git a/target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch b/target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch new file mode 100644 index 00000000000..3834ba7c619 --- /dev/null +++ b/target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch @@ -0,0 +1,96 @@ +From fce11f68491b46b93df69de0630cd9edb90bc772 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Wed, 29 Dec 2021 21:54:21 +0100 +Subject: [PATCH] realtek: Create 4 different Realtek Platforms + +Creates RTL83XX as a basic kernel config parameter for the +RTL838X, RTL839x, RTL930X and RTL931X platforms with respective +configurations for the SoCs, which are introduced in addition. + +Submitted-by: Birger Koblitz +--- + arch/mips/Kbuild.platforms | 1 + + arch/mips/Kconfig | 57 ++++++++++++++ + 2 files changed, 58 insertions(+) + +--- a/arch/mips/Kbuild.platforms ++++ b/arch/mips/Kbuild.platforms +@@ -23,6 +23,7 @@ platform-$(CONFIG_PIC32MZDA) += pic32/ + platform-$(CONFIG_MACH_PISTACHIO) += pistachio/ + platform-$(CONFIG_RALINK) += ralink/ + platform-$(CONFIG_MIKROTIK_RB532) += rb532/ ++platform-$(CONFIG_RTL83XX) += rtl838x/ + platform-$(CONFIG_SGI_IP22) += sgi-ip22/ + platform-$(CONFIG_SGI_IP27) += sgi-ip27/ + platform-$(CONFIG_SGI_IP28) += sgi-ip22/ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1037,8 +1037,58 @@ config NLM_XLP_BOARD + This board is based on Netlogic XLP Processor. + Say Y here if you have a XLP based board. + ++config RTL83XX ++ bool "Realtek based platforms" ++ select DMA_NONCOHERENT ++ select IRQ_MIPS_CPU ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_HAS_CPU_MIPS32_R2 ++ select SYS_SUPPORTS_BIG_ENDIAN ++ select SYS_SUPPORTS_HIGHMEM ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SYS_SUPPORTS_MIPS16 ++ select SYS_HAS_EARLY_PRINTK ++ select SYS_HAS_EARLY_PRINTK_8250 ++ select USE_GENERIC_EARLY_PRINTK_8250 ++ select BOOT_RAW ++ select PINCTRL ++ select ARCH_HAS_RESET_CONTROLLER ++ select RESET_CONTROLLER ++ select USE_OF ++ + endchoice + ++config RTL838X ++ bool "Realtek RTL838X based platforms" ++ depends on RTL83XX ++ select CPU_SUPPORTS_CPUFREQ ++ select MIPS_EXTERNAL_TIMER ++ ++config RTL839X ++ bool "Realtek RTL839X based platforms" ++ depends on RTL83XX ++ select CPU_SUPPORTS_CPUFREQ ++ select MIPS_EXTERNAL_TIMER ++ select SYS_SUPPORTS_MULTITHREADING ++ ++config RTL930X ++ bool "Realtek RTL930X based platforms" ++ depends on RTL83XX ++ select MIPS_CPU_SCACHE ++ select CSRC_R4K ++ select CEVT_RTL9300 ++ select SYS_SUPPORTS_MULTITHREADING ++ ++config RTL931X ++ bool "Realtek RTL931X based platforms" ++ depends on RTL930X ++ select MIPS_GIC ++ select COMMON_CLK ++ select CLKSRC_MIPS_GIC ++ select SYS_SUPPORTS_VPE_LOADER ++ select SYS_SUPPORTS_SMP ++ select SYS_SUPPORTS_MIPS_CPS ++ + source "arch/mips/alchemy/Kconfig" + source "arch/mips/ath25/Kconfig" + source "arch/mips/ath79/Kconfig" +@@ -1097,6 +1147,9 @@ config CEVT_GT641XX + config CEVT_R4K + bool + ++config CEVT_RTL9300 ++ bool ++ + config CEVT_SB1250 + bool + diff --git a/target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch b/target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch new file mode 100644 index 00000000000..27256512438 --- /dev/null +++ b/target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch @@ -0,0 +1,50 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: [PATCH] realtek: update the tree to the latest refactored version +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + drivers/gpio/Kconfig | 6 ++++++ + drivers/gpio/Makefile | 1 + + 2 files changed, 7 insertions(+) + +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -508,6 +508,12 @@ config GPIO_REG + A 32-bit single register GPIO fixed in/out implementation. This + can be used to represent any register as a set of GPIO signals. + ++config GPIO_RTL8231 ++ tristate "RTL8231 GPIO" ++ depends on RTL83XX ++ help ++ Say yes here to support Realtek RTL8231 GPIO expansion chips. ++ + config GPIO_SAMA5D2_PIOBU + tristate "SAMA5D2 PIOBU GPIO support" + depends on MFD_SYSCON +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_RDA) += gpio-rda.o + obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o + obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o + obj-$(CONFIG_GPIO_REG) += gpio-reg.o ++obj-$(CONFIG_GPIO_RTL8231) += gpio-rtl8231.o + obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o + obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o + obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o diff --git a/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch b/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch new file mode 100644 index 00000000000..8b17ddaef00 --- /dev/null +++ b/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch @@ -0,0 +1,40 @@ +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -127,6 +127,17 @@ config RDA_TIMER + help + Enables the support for the RDA Micro timer driver. + ++config REALTEK_OTTO_TIMER ++ bool "Clocksource/timer for the Realtek Otto platform" ++ select COMMON_CLK ++ select TIMER_OF ++ help ++ This driver adds support for the timers found in the Realtek RTL83xx ++ and RTL93xx SoCs series. This includes chips such as RTL8380, RTL8381 ++ and RTL832, as well as chips from the RTL839x series, such as RTL8390 ++ RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series ++ such as RTL9301, RTL9302 or RTL9303. ++ + config SUN4I_TIMER + bool "Sun4i timer driver" if COMPILE_TEST + depends on HAS_IOMEM +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -63,6 +63,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-mi + obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o + obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o + obj-$(CONFIG_RDA_TIMER) += timer-rda.o ++obj-$(CONFIG_REALTEK_OTTO_TIMER) += timer-rtl-otto.o + + obj-$(CONFIG_ARC_TIMERS) += arc_timer.o + obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o +--- a/include/linux/cpuhotplug.h ++++ b/include/linux/cpuhotplug.h +@@ -133,6 +133,7 @@ enum cpuhp_state { + CPUHP_AP_MARCO_TIMER_STARTING, + CPUHP_AP_MIPS_GIC_TIMER_STARTING, + CPUHP_AP_ARC_TIMER_STARTING, ++ CPUHP_AP_REALTEK_TIMER_STARTING, + CPUHP_AP_RISCV_TIMER_STARTING, + CPUHP_AP_CLINT_TIMER_STARTING, + CPUHP_AP_CSKY_TIMER_STARTING, diff --git a/target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch b/target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch new file mode 100644 index 00000000000..5f0986b9051 --- /dev/null +++ b/target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch @@ -0,0 +1,26 @@ +From 9bac1c20b8f39f2e0e342b087add5093b94feaed Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +Date: Wed, 5 May 2021 22:05:39 +0900 +Subject: realtek: backport gpio-realtek-otto driver from 5.13 to 5.10 + +This patch backports "gpio-realtek-otto" driver to Kernel 5.10. +"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X" +is used in OpenWrt, so update the dependency by the additional patch. + +Submitted-by: INAGAKI Hiroshi +--- + drivers/gpio/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -491,8 +491,8 @@ config GPIO_RDA + + config GPIO_REALTEK_OTTO + tristate "Realtek Otto GPIO support" +- depends on MACH_REALTEK_RTL +- default MACH_REALTEK_RTL ++ depends on RTL83XX ++ default RTL838X + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help diff --git a/target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch b/target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch new file mode 100644 index 00000000000..23c5448aa2e --- /dev/null +++ b/target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch @@ -0,0 +1,25 @@ +From 0b000cbfe0aa0323bffa855ef8449c0687a4c071 Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +Date: Thu, 6 May 2021 19:30:58 +0900 +Subject: realtek: backport spi-realtek-rtl driver from 5.12 to 5.10 + +This patch backports "spi-realtek-rtl" driver to Kernel 5.10 from 5.12. +"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X" +is used in OpenWrt, so update the dependency by the additional patch. + +Submitted-by: INAGAKI Hiroshi +--- + drivers/spi/Makefile | 2 +- + 1 files changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -94,7 +94,7 @@ obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom + obj-$(CONFIG_SPI_QUP) += spi-qup.o + obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o + obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o +-obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o ++obj-$(CONFIG_RTL83XX) += spi-realtek-rtl.o + obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o + obj-$(CONFIG_SPI_RSPI) += spi-rspi.o + obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o diff --git a/target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch b/target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch new file mode 100644 index 00000000000..0c1cc4235e6 --- /dev/null +++ b/target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch @@ -0,0 +1,22 @@ +From 2cd00b51470a30198b048a5fca48a04db77e29cc Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +Date: Fri, 21 May 2021 23:16:37 +0900 +Subject: [PATCH] realtek: backport irq-realtek-rtl driver from 5.12 to 5.10 + +This patch backports "irq-realtek-rtl" driver to Kernel 5.10 from 5.12. +"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X" +is used in OpenWrt, so update the dependency by the additional patch. + +Submitted-by: INAGAKI Hiroshi +--- + drivers/irqchip/Makefile | 2 +- + 1 files changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -114,4 +114,4 @@ obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-l + obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o + obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o + obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o +-obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o ++obj-$(CONFIG_RTL83XX) += irq-realtek-rtl.o diff --git a/target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch b/target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch new file mode 100644 index 00000000000..f92981340bb --- /dev/null +++ b/target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch @@ -0,0 +1,32 @@ +From b8fc5eecdc5d33cf261986436597b5482ab856da Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sun, 14 Nov 2021 19:45:32 +0100 +Subject: [PATCH] realtek: Backport Realtek Otto WDT driver + +Add patch submitted upstream to linux-watchdog and replace the MIPS +architecture symbols. Requires one extra patch for the DIV_ROUND_* +macros, which have moved to a different header since 5.10. + +Submitted-by: Sander Vanheule +Tested-by: Stijn Segers +Tested-by: Paul Fertser +Tested-by: Stijn Tintel +--- + drivers/watchdog/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -997,10 +997,10 @@ config RTD119X_WATCHDOG + + config REALTEK_OTTO_WDT + tristate "Realtek Otto MIPS watchdog support" +- depends on MACH_REALTEK_RTL || COMPILE_TEST ++ depends on RTL83XX + depends on COMMON_CLK + select WATCHDOG_CORE +- default MACH_REALTEK_RTL ++ default RTL83XX + help + Say Y here to include support for the watchdog timer on Realtek + RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout diff --git a/target/linux/realtek/patches-5.15/308-otto-wdt-fix-missing-math-header.patch b/target/linux/realtek/patches-5.15/308-otto-wdt-fix-missing-math-header.patch new file mode 100644 index 00000000000..78b145617fa --- /dev/null +++ b/target/linux/realtek/patches-5.15/308-otto-wdt-fix-missing-math-header.patch @@ -0,0 +1,28 @@ +From b8fc5eecdc5d33cf261986436597b5482ab856da Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sun, 14 Nov 2021 19:45:32 +0100 +Subject: [PATCH] realtek: Backport Realtek Otto WDT driver + +Add patch submitted upstream to linux-watchdog and replace the MIPS +architecture symbols. Requires one extra patch for the DIV_ROUND_* +macros, which have moved to a different header since 5.10. + +Submitted-by: Sander Vanheule +Tested-by: Stijn Segers +Tested-by: Paul Fertser +Tested-by: Stijn Tintel +--- + drivers/watchdog/realtek_otto_wdt.c | 2 +- + 1 files changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/watchdog/realtek_otto_wdt.c ++++ b/drivers/watchdog/realtek_otto_wdt.c +@@ -21,7 +21,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include diff --git a/target/linux/realtek/patches-5.15/309-cevt-rtl9300-support.patch b/target/linux/realtek/patches-5.15/309-cevt-rtl9300-support.patch new file mode 100644 index 00000000000..93d69c07cf1 --- /dev/null +++ b/target/linux/realtek/patches-5.15/309-cevt-rtl9300-support.patch @@ -0,0 +1,54 @@ +From 775d903216a08c2a8009863d2f9c33f62657ba94 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Thu, 6 Jan 2022 20:27:01 +0100 +Subject: [PATCH] realtek: Replace the RTL9300 generic timer with a CEVT timer + +The RTL9300 has a broken R4K MIPS timer interrupt, however, the +R4K clocksource works. We replace the RTL9300 timer with a +Clock Event Timer (CEVT), which is VSMP aware and can be instantiated +as part of brining a VSMTP cpu up instead of the R4K CEVT source. +For this we place the RTL9300 CEVT timer in arch/mips/kernel +together with other MIPS CEVT timers, initialize the SoC IRQs +from a modified smp-mt.c and instantiate each timer as part +of the MIPS time setup in arch/mips/include/asm/time.h instead +of the R4K CEVT, similarly as is done by other MIPS CEVT timers. + +Submitted-by: Birger Koblitz +--- + arch/mips/kernel/Makefile | 1 + + arch/mips/include/asm/time.h | 7 +++++++ + 2 files changed, 8 insertions(+) + +--- a/arch/mips/kernel/Makefile ++++ b/arch/mips/kernel/Makefile +@@ -27,6 +27,7 @@ obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm14 + obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o + obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o + obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o ++obj-$(CONFIG_CEVT_RTL9300) += cevt-rtl9300.o + obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o + obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o + obj-$(CONFIG_CSRC_BCM1480) += csrc-bcm1480.o +--- a/arch/mips/include/asm/time.h ++++ b/arch/mips/include/asm/time.h +@@ -15,6 +15,8 @@ + #include + #include + ++extern void rtl9300_clockevent_init(void); ++ + extern spinlock_t rtc_lock; + + /* +@@ -43,6 +45,11 @@ extern int r4k_clockevent_init(void); + + static inline int mips_clockevent_init(void) + { ++#ifdef CONFIG_CEVT_RTL9300 ++ rtl9300_clockevent_init(); ++ return 0; ++#endif ++ + #ifdef CONFIG_CEVT_R4K + return r4k_clockevent_init(); + #else diff --git a/target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch b/target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch new file mode 100644 index 00000000000..4df9bf043b1 --- /dev/null +++ b/target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch @@ -0,0 +1,46 @@ +From 63a0a4d85bc900464c5b046b13808a582345f8c8 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Sat, 11 Dec 2021 20:14:47 +0100 +Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C controller + +This adds support for the RTL9300 and RTL9310 I2C controller. +The controller implements the SMBus protocol for SMBus transfers +over an I2C bus. The driver supports selecting one of the 2 possible +SCL pins and any of the 8 possible SDA pins. Bus speeds of +100kHz (standard speed) and 400kHz (high speed I2C) are supported. + +Submitted-by: Birger Koblitz +--- + drivers/i2c/busses/Kconfig | 10 +++++++++ + drivers/i2c/busses/Makefile | 1 + + 2 files changed, 11 insertions(+) + +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -954,6 +954,16 @@ config I2C_RK3X + This driver can also be built as a module. If so, the module will + be called i2c-rk3x. + ++config I2C_RTL9300 ++ tristate "Realtek RTL9300 I2C adapter" ++ depends on OF ++ help ++ Say Y here to include support for the I2C adapter in Realtek RTL9300 ++ and RTL9310 SoCs. ++ ++ This driver can also be built as a module. If so, the module will ++ be called i2c-rtl9300. ++ + config HAVE_S3C2410_I2C + bool + help +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -95,6 +95,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom- + obj-$(CONFIG_I2C_QUP) += i2c-qup.o + obj-$(CONFIG_I2C_RIIC) += i2c-riic.o + obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o ++obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o + obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o + obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o + obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o diff --git a/target/linux/realtek/patches-5.15/311-add-i2c-mux-rtl9300-support.patch b/target/linux/realtek/patches-5.15/311-add-i2c-mux-rtl9300-support.patch new file mode 100644 index 00000000000..d0bfba45381 --- /dev/null +++ b/target/linux/realtek/patches-5.15/311-add-i2c-mux-rtl9300-support.patch @@ -0,0 +1,46 @@ +From f4bdb7fdccdfe3fa382abe77f72a16c2f2e6add0 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Sat, 11 Dec 2021 20:25:37 +0100 +Subject: [PATCH] realtek: Add support for RTL9300/RTL9310 I2C multiplexing + +The RTL9300/RTL9310 I2C controllers have support for 2 independent I2C +masters, each with a fixed SCL pin, that cannot be changed. Each of these +masters can use 8 (RTL9300) or 16 (RTL9310) different pins for SDA. +This multiplexer directly controls the two masters and their shared +IO configuration registers to allow multiplexing between any of these +busses. The two masters cannot be used in parallel as the multiplex +is protected by a standard multiplex lock. + +Submitted-by: Birger Koblitz +--- + drivers/i2c/muxes/Kconfig | 9 +++++++ + drivers/i2c/muxes/Makefile | 1 + + 2 files changed, 10 insertions(+) + +--- a/drivers/i2c/muxes/Kconfig ++++ b/drivers/i2c/muxes/Kconfig +@@ -99,6 +99,15 @@ config I2C_MUX_REG + This driver can also be built as a module. If so, the module + will be called i2c-mux-reg. + ++config I2C_MUX_RTL9300 ++ tristate "RTL9300 based I2C multiplexer" ++ help ++ If you say yes to this option, support will be included for a ++ RTL9300 based I2C multiplexer. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-mux-reg. ++ + config I2C_DEMUX_PINCTRL + tristate "pinctrl-based I2C demultiplexer" + depends on PINCTRL && OF +--- a/drivers/i2c/muxes/Makefile ++++ b/drivers/i2c/muxes/Makefile +@@ -14,5 +14,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux + obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o + obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o + obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o ++obj-$(CONFIG_I2C_MUX_RTL9300) += i2c-mux-rtl9300.o + + ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git a/target/linux/realtek/patches-5.15/312-rt9313-support.patch b/target/linux/realtek/patches-5.15/312-rt9313-support.patch new file mode 100644 index 00000000000..516bca2b6ef --- /dev/null +++ b/target/linux/realtek/patches-5.15/312-rt9313-support.patch @@ -0,0 +1,76 @@ +From 0b8dfe085180b58b81d2657c76b080168e3bc8df Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Wed, 19 Jan 2022 18:14:02 +0100 +Subject: [PATCH] realtek: Add RTL931X sub-target + +We add the RTL931X sub-target with kernel configuration for +a dual core MIPS InterAptive CPU. + +Submitted-by: Sebastian Gottschall +Submitted-by: Birger Koblitz +--- + arch/mips/Makefile | 10 +++++++++++++-- + arch/mips/kernel/head.S | 4 ++++ + aarch/mips/kernel/vmlinux.lds.S | 4 ++++ + 3 files changed, 18 insertions(+), 2 deletions(-) + +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -307,14 +307,24 @@ endif + + KBUILD_AFLAGS += $(cflags-y) + KBUILD_CFLAGS += $(cflags-y) ++ifdef CONFIG_931X ++KBUILD_CPPFLAGS += -DVMLINUX_LOAD_ADDRESS=$(load-y) ++bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) \ ++ VMLINUX_ENTRY_ADDRESS=$(entry-y) \ ++ PLATFORM="$(platform-y)" \ ++ ITS_INPUTS="$(its-y)" ++else + KBUILD_CPPFLAGS += -DVMLINUX_LOAD_ADDRESS=$(load-y) -DLINKER_LOAD_ADDRESS=$(load-ld) +-KBUILD_CPPFLAGS += -DDATAOFFSET=$(if $(dataoffset-y),$(dataoffset-y),0) +- + bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) \ + LINKER_LOAD_ADDRESS=$(load-ld) \ + VMLINUX_ENTRY_ADDRESS=$(entry-y) \ + PLATFORM="$(platform-y)" \ + ITS_INPUTS="$(its-y)" ++endif ++KBUILD_CPPFLAGS += -DDATAOFFSET=$(if $(dataoffset-y),$(dataoffset-y),0) ++ ++ ++ + ifdef CONFIG_32BIT + bootvars-y += ADDR_BITS=32 + endif +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -60,12 +60,14 @@ + .endm + + #ifndef CONFIG_NO_EXCEPT_FILL ++#ifndef CONFIG_RTL931X + /* + * Reserved space for exception handlers. + * Necessary for machines which link their kernels at KSEG0. + */ + .fill 0x400 + #endif ++#endif + + EXPORT(_stext) + +--- a/arch/mips/kernel/vmlinux.lds.S ++++ b/arch/mips/kernel/vmlinux.lds.S +@@ -55,7 +55,11 @@ SECTIONS + /* . = 0xa800000000300000; */ + . = 0xffffffff80300000; + #endif ++#ifdef CONFIG_RTL931X ++ . = 0x80220000; ++#else + . = LINKER_LOAD_ADDRESS; ++#endif + /* read-only */ + _text = .; /* Text and read-only data */ + .text : { diff --git a/target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch b/target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch new file mode 100644 index 00000000000..26c36302c28 --- /dev/null +++ b/target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch @@ -0,0 +1,407 @@ +From 6c18e9c491959ac0674ebe36b09f9ddc3f2c9bce Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Fri, 31 Dec 2021 11:56:49 +0100 +Subject: [PATCH] realtek: Add VPE support for the IRQ driver + +In order to support VSMP, enable support for both VPEs +of the RTL839X and RTL930X SoCs in the irq-realtek-rtl +driver. Add support for IRQ affinity setting. + +Submitted-by: Birger Koblitz +--- + drivers/irqchip/irq-realtek-rtl.c | 152 +++++++++++++++--- + 1 file changed, 73 insertions(+), 76 deletions(-) + +--- a/drivers/irqchip/irq-realtek-rtl.c ++++ b/drivers/irqchip/irq-realtek-rtl.c +@@ -21,21 +21,63 @@ + #define RTL_ICTL_IRR2 0x10 + #define RTL_ICTL_IRR3 0x14 + +-#define REG(x) (realtek_ictl_base + x) ++#define RTL_ICTL_NUM_INPUTS 32 ++#define RTL_ICTL_NUM_OUTPUTS 15 + + static DEFINE_RAW_SPINLOCK(irq_lock); +-static void __iomem *realtek_ictl_base; ++ ++#define REG(offset, cpu) (realtek_ictl_base[cpu] + offset) ++ ++static void __iomem *realtek_ictl_base[NR_CPUS]; ++static cpumask_t realtek_ictl_cpu_configurable; ++ ++struct realtek_ictl_output { ++ /* IRQ controller data */ ++ struct fwnode_handle *fwnode; ++ /* Output specific data */ ++ unsigned int output_index; ++ struct irq_domain *domain; ++ u32 child_mask; ++}; ++ ++/* ++ * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering, ++ * placing IRQ 31 in the first four bits. A routing value of '0' means the ++ * interrupt is left disconnected. Routing values {1..15} connect to output ++ * lines {0..14}. ++ */ ++#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32)) ++#define IRR_SHIFT(idx) ((idx * 4) % 32) ++ ++static inline u32 read_irr(void __iomem *irr0, int idx) ++{ ++ return (readl(irr0 + IRR_OFFSET(idx)) >> IRR_SHIFT(idx)) & 0xf; ++} ++ ++static inline void write_irr(void __iomem *irr0, int idx, u32 value) ++{ ++ unsigned int offset = IRR_OFFSET(idx); ++ unsigned int shift = IRR_SHIFT(idx); ++ u32 irr; ++ ++ irr = readl(irr0 + offset) & ~(0xf << shift); ++ irr |= (value & 0xf) << shift; ++ writel(irr, irr0 + offset); ++} + + static void realtek_ictl_unmask_irq(struct irq_data *i) + { + unsigned long flags; + u32 value; ++ int cpu; + + raw_spin_lock_irqsave(&irq_lock, flags); + +- value = readl(REG(RTL_ICTL_GIMR)); +- value |= BIT(i->hwirq); +- writel(value, REG(RTL_ICTL_GIMR)); ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) { ++ value = readl(REG(RTL_ICTL_GIMR, cpu)); ++ value |= BIT(i->hwirq); ++ writel(value, REG(RTL_ICTL_GIMR, cpu)); ++ } + + raw_spin_unlock_irqrestore(&irq_lock, flags); + } +@@ -44,137 +86,247 @@ static void realtek_ictl_mask_irq(struct + { + unsigned long flags; + u32 value; ++ int cpu; + + raw_spin_lock_irqsave(&irq_lock, flags); + +- value = readl(REG(RTL_ICTL_GIMR)); +- value &= ~BIT(i->hwirq); +- writel(value, REG(RTL_ICTL_GIMR)); ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) { ++ value = readl(REG(RTL_ICTL_GIMR, cpu)); ++ value &= ~BIT(i->hwirq); ++ writel(value, REG(RTL_ICTL_GIMR, cpu)); ++ } + + raw_spin_unlock_irqrestore(&irq_lock, flags); + } + ++static int __maybe_unused realtek_ictl_irq_affinity(struct irq_data *i, ++ const struct cpumask *dest, bool force) ++{ ++ struct realtek_ictl_output *output = i->domain->host_data; ++ cpumask_t cpu_configure; ++ cpumask_t cpu_disable; ++ cpumask_t cpu_enable; ++ unsigned long flags; ++ int cpu; ++ ++ raw_spin_lock_irqsave(&irq_lock, flags); ++ ++ cpumask_and(&cpu_configure, cpu_present_mask, &realtek_ictl_cpu_configurable); ++ ++ cpumask_and(&cpu_enable, &cpu_configure, dest); ++ cpumask_andnot(&cpu_disable, &cpu_configure, dest); ++ ++ for_each_cpu(cpu, &cpu_disable) ++ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0); ++ ++ for_each_cpu(cpu, &cpu_enable) ++ write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1); ++ ++ irq_data_update_effective_affinity(i, &cpu_enable); ++ ++ raw_spin_unlock_irqrestore(&irq_lock, flags); ++ ++ return IRQ_SET_MASK_OK; ++} ++ + static struct irq_chip realtek_ictl_irq = { + .name = "realtek-rtl-intc", + .irq_mask = realtek_ictl_mask_irq, + .irq_unmask = realtek_ictl_unmask_irq, ++#ifdef CONFIG_SMP ++ .irq_set_affinity = realtek_ictl_irq_affinity, ++#endif + }; + + static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) + { +- irq_set_chip_and_handler(hw, &realtek_ictl_irq, handle_level_irq); ++ struct realtek_ictl_output *output = d->host_data; ++ unsigned long flags; ++ ++ irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq); ++ ++ raw_spin_lock_irqsave(&irq_lock, flags); ++ ++ output->child_mask |= BIT(hw); ++ write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1); ++ ++ raw_spin_unlock_irqrestore(&irq_lock, flags); + + return 0; + } + ++static int intc_select(struct irq_domain *d, struct irq_fwspec *fwspec, ++ enum irq_domain_bus_token bus_token) ++{ ++ struct realtek_ictl_output *output = d->host_data; ++ bool routed_elsewhere; ++ unsigned long flags; ++ u32 routing_old; ++ int cpu; ++ ++ if (fwspec->fwnode != output->fwnode) ++ return false; ++ ++ /* Original specifiers had only one parameter */ ++ if (fwspec->param_count < 2) ++ return true; ++ ++ raw_spin_lock_irqsave(&irq_lock, flags); ++ ++ /* ++ * Inputs can only be routed to one output, so they shouldn't be ++ * allowed to end up in multiple domains. ++ */ ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) { ++ routing_old = read_irr(REG(RTL_ICTL_IRR0, cpu), fwspec->param[0]); ++ routed_elsewhere = routing_old && fwspec->param[1] != routing_old - 1; ++ if (routed_elsewhere) { ++ pr_warn("soc int %d already routed to output %d\n", ++ fwspec->param[0], routing_old - 1); ++ break; ++ } ++ } ++ ++ raw_spin_unlock_irqrestore(&irq_lock, flags); ++ ++ return !routed_elsewhere && fwspec->param[1] == output->output_index; ++} ++ + static const struct irq_domain_ops irq_domain_ops = { + .map = intc_map, ++ .select = intc_select, + .xlate = irq_domain_xlate_onecell, + }; + + static void realtek_irq_dispatch(struct irq_desc *desc) + { ++ struct realtek_ictl_output *output = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); +- struct irq_domain *domain; +- unsigned int pending; ++ int cpu = smp_processor_id(); ++ unsigned long pending; ++ unsigned int soc_int; + + chained_irq_enter(chip, desc); +- pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); ++ pending = readl(REG(RTL_ICTL_GIMR, cpu)) & readl(REG(RTL_ICTL_GISR, cpu)) ++ & output->child_mask; ++ + if (unlikely(!pending)) { + spurious_interrupt(); + goto out; + } +- domain = irq_desc_get_handler_data(desc); +- generic_handle_irq(irq_find_mapping(domain, __ffs(pending))); ++ ++ for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS) ++ generic_handle_irq(irq_find_mapping(output->domain, soc_int)); ++// generic_handle_domain_irq(output->domain, soc_int); + + out: + chained_irq_exit(chip, desc); + } + +-/* +- * SoC interrupts are cascaded to MIPS CPU interrupts according to the +- * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for +- * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts +- * thus go into 4 IRRs. +- */ +-static int __init map_interrupts(struct device_node *node, struct irq_domain *domain) ++static int __init setup_parent_interrupts(struct device_node *node, int *parents, ++ unsigned int num_parents) + { +- struct device_node *cpu_ictl; +- const __be32 *imap; +- u32 imaplen, soc_int, cpu_int, tmp, regs[4]; +- int ret, i, irr_regs[] = { +- RTL_ICTL_IRR3, +- RTL_ICTL_IRR2, +- RTL_ICTL_IRR1, +- RTL_ICTL_IRR0, +- }; +- u8 mips_irqs_set; ++ struct realtek_ictl_output *outputs; ++ struct realtek_ictl_output *output; ++ struct irq_domain *domain; ++ unsigned int p; + +- ret = of_property_read_u32(node, "#address-cells", &tmp); +- if (ret || tmp) +- return -EINVAL; ++ outputs = kcalloc(num_parents, sizeof(*outputs), GFP_KERNEL); ++ if (!outputs) ++ return -ENOMEM; + +- imap = of_get_property(node, "interrupt-map", &imaplen); +- if (!imap || imaplen % 3) +- return -EINVAL; ++ for (p = 0; p < num_parents; p++) { ++ output = outputs + p; + +- mips_irqs_set = 0; +- memset(regs, 0, sizeof(regs)); +- for (i = 0; i < imaplen; i += 3 * sizeof(u32)) { +- soc_int = be32_to_cpup(imap); +- if (soc_int > 31) +- return -EINVAL; +- +- cpu_ictl = of_find_node_by_phandle(be32_to_cpup(imap + 1)); +- if (!cpu_ictl) +- return -EINVAL; +- ret = of_property_read_u32(cpu_ictl, "#interrupt-cells", &tmp); +- if (ret || tmp != 1) +- return -EINVAL; +- of_node_put(cpu_ictl); +- +- cpu_int = be32_to_cpup(imap + 2); +- if (cpu_int > 7) +- return -EINVAL; +- +- if (!(mips_irqs_set & BIT(cpu_int))) { +- irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch, +- domain); +- mips_irqs_set |= BIT(cpu_int); +- } ++ domain = irq_domain_add_linear(node, RTL_ICTL_NUM_INPUTS, &irq_domain_ops, output); ++ if (!domain) ++ goto domain_err; + +- regs[(soc_int * 4) / 32] |= cpu_int << (soc_int * 4) % 32; +- imap += 3; +- } ++ output->fwnode = of_node_to_fwnode(node); ++ output->output_index = p; ++ output->domain = domain; + +- for (i = 0; i < 4; i++) +- writel(regs[i], REG(irr_regs[i])); ++ irq_set_chained_handler_and_data(parents[p], realtek_irq_dispatch, output); ++ } + + return 0; ++ ++domain_err: ++ while (p--) { ++ irq_set_chained_handler_and_data(parents[p], NULL, NULL); ++ irq_domain_remove(outputs[p].domain); ++ } ++ ++ kfree(outputs); ++ ++ return -ENOMEM; + } + + static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent) + { +- struct irq_domain *domain; +- int ret; ++ int parent_irqs[RTL_ICTL_NUM_OUTPUTS]; ++ struct of_phandle_args oirq; ++ unsigned int num_parents; ++ unsigned int soc_irq; ++ unsigned int p; ++ int cpu; ++ ++ cpumask_clear(&realtek_ictl_cpu_configurable); ++ ++ for (cpu = 0; cpu < NR_CPUS; cpu++) { ++ realtek_ictl_base[cpu] = of_iomap(node, cpu); ++ if (realtek_ictl_base[cpu]) { ++ cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable); ++ ++ /* Disable all cascaded interrupts and clear routing */ ++ writel(0, REG(RTL_ICTL_GIMR, cpu)); ++ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) ++ write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0); ++ } ++ } + +- realtek_ictl_base = of_iomap(node, 0); +- if (!realtek_ictl_base) ++ if (cpumask_empty(&realtek_ictl_cpu_configurable)) + return -ENXIO; + +- /* Disable all cascaded interrupts */ +- writel(0, REG(RTL_ICTL_GIMR)); ++ num_parents = of_irq_count(node); ++ if (num_parents > RTL_ICTL_NUM_OUTPUTS) { ++ pr_err("too many parent interrupts\n"); ++ return -EINVAL; ++ } + +- domain = irq_domain_add_simple(node, 32, 0, +- &irq_domain_ops, NULL); ++ for (p = 0; p < num_parents; p++) ++ parent_irqs[p] = of_irq_get(node, p); + +- ret = map_interrupts(node, domain); +- if (ret) { +- pr_err("invalid interrupt map\n"); +- return ret; ++ if (WARN_ON(!num_parents)) { ++ /* ++ * If DT contains no parent interrupts, assume MIPS CPU IRQ 2 ++ * (HW0) is connected to the first output. This is the case for ++ * all known hardware anyway. "interrupt-map" is deprecated, so ++ * don't bother trying to parse that. ++ * Since this is to account for old devicetrees with one-cell ++ * interrupt specifiers, only one output domain is needed. ++ */ ++ oirq.np = of_find_compatible_node(NULL, NULL, "mti,cpu-interrupt-controller"); ++ if (oirq.np) { ++ oirq.args_count = 1; ++ oirq.args[0] = 2; ++ ++ parent_irqs[0] = irq_create_of_mapping(&oirq); ++ num_parents = 1; ++ } ++ ++ of_node_put(oirq.np); + } + +- return 0; ++ /* Ensure we haven't collected any errors before proceeding */ ++ for (p = 0; p < num_parents; p++) { ++ if (parent_irqs[p] < 0) ++ return parent_irqs[p]; ++ if (!parent_irqs[p]) ++ return -ENODEV; ++ } ++ ++ return setup_parent_interrupts(node, &parent_irqs[0], num_parents); + } + + IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init); diff --git a/target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch b/target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch new file mode 100644 index 00000000000..3980875dd4b --- /dev/null +++ b/target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch @@ -0,0 +1,51 @@ +From bde6311569ef25a00c3beaeabfd6b78b19651872 Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sun, 29 May 2022 19:38:09 +0200 +Subject: [PATCH] realtek: don't unmask non-maskable GPIO IRQs + +On uniprocessor builds, for_each_cpu(cpu, mask) will assume 'mask' +always contains exactly one CPU, and ignore the actual mask contents. +This causes the loop to run, even when it shouldn't on an empty mask, +and tries to access an uninitialised pointer. + +Fix this by wrapping the loop in a cpumask_empty() check, to ensure it +will not run on uniprocessor builds if the CPU mask is empty. + +Fixes: af6cd37f42f3 ("realtek: replace RTL93xx GPIO patches") +Reported-by: INAGAKI Hiroshi +Reported-by: Robert Marko +Tested-by: Robert Marko +Submitted-by: Sander Vanheule +--- + drivers/gpio/gpio-realtek-otto.c | 9 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/drivers/gpio/gpio-realtek-otto.c ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -304,6 +304,7 @@ static int realtek_gpio_irq_set_affinity + static int realtek_gpio_irq_init(struct gpio_chip *gc) + { + struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); ++ void __iomem *irq_cpu_mask; + unsigned int port; + int cpu; + +@@ -311,8 +312,16 @@ static int realtek_gpio_irq_init(struct + realtek_gpio_write_imr(ctrl, port, 0, 0); + realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0)); + +- for_each_cpu(cpu, &ctrl->cpu_irq_maskable) +- iowrite8(GENMASK(7, 0), realtek_gpio_irq_cpu_mask(ctrl, port, cpu)); ++ /* ++ * Uniprocessor builds assume a mask always contains one CPU, ++ * so only start the loop if we have at least one maskable CPU. ++ */ ++ if(!cpumask_empty(&ctrl->cpu_irq_maskable)) { ++ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) { ++ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu); ++ iowrite8(GENMASK(7, 0), irq_cpu_mask); ++ } ++ } + } + + return 0; diff --git a/target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch b/target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch new file mode 100644 index 00000000000..9c043b71269 --- /dev/null +++ b/target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch @@ -0,0 +1,373 @@ +From ee0175b3b44288c74d5292c2a9c2c154f6c0317e Mon Sep 17 00:00:00 2001 +From: Sander Vanheule +Date: Sun, 7 Aug 2022 21:21:15 +0200 +Subject: [PATCH] gpio: realtek-otto: switch to 32-bit I/O + +By using 16-bit I/O on the GPIO peripheral, which is apparently not safe +on MIPS, the IMR can end up containing garbage. This then results in +interrupt triggers for lines that don't have an interrupt handler +associated. The irq_desc lookup fails, and the ISR will not be cleared, +keeping the CPU busy until reboot, or until another IMR operation +restores the correct value. This situation appears to happen very +rarely, for < 0.5% of IMR writes. + +Instead of using 8-bit or 16-bit I/O operations on the 32-bit memory +mapped peripheral registers, switch to using 32-bit I/O only, operating +on the entire bank for all single bit line settings. For 2-bit line +settings, with 16-bit port values, stick to manual (un)packing. + +This issue has been seen on RTL8382M (HPE 1920-16G), RTL8391M (Netgear +GS728TP v2), and RTL8393M (D-Link DGS-1210-52 F3, Zyxel GS1900-48). + +Reported-by: Luiz Angelo Daros de Luca # DGS-1210-52 +Reported-by: Birger Koblitz # GS728TP +Reported-by: Jan Hoffmann # 1920-16G +Fixes: 0d82fb1127fb ("gpio: Add Realtek Otto GPIO support") +Signed-off-by: Sander Vanheule +Cc: Paul Cercueil +Reviewed-by: Linus Walleij +Signed-off-by: Bartosz Golaszewski + +Update patch for missing upstream changes: + - commit a01a40e33499 ("gpio: realtek-otto: Make the irqchip immutable") + - commit dbd1c54fc820 ("gpio: Bulk conversion to generic_handle_domain_irq()") +Signed-off-by: Sander Vanheule + +--- + drivers/gpio/gpio-realtek-otto.c | 166 ++++++++++++++++--------------- + 1 file changed, 85 insertions(+), 81 deletions(-) + +--- a/drivers/gpio/gpio-realtek-otto.c ++++ b/drivers/gpio/gpio-realtek-otto.c +@@ -46,10 +46,20 @@ + * @lock: Lock for accessing the IRQ registers and values + * @intr_mask: Mask for interrupts lines + * @intr_type: Interrupt type selection ++ * @bank_read: Read a bank setting as a single 32-bit value ++ * @bank_write: Write a bank setting as a single 32-bit value ++ * @imr_line_pos: Bit shift of an IRQ line's IMR value. ++ * ++ * The DIR, DATA, and ISR registers consist of four 8-bit port values, packed ++ * into a single 32-bit register. Use @bank_read (@bank_write) to get (assign) ++ * a value from (to) these registers. The IMR register consists of four 16-bit ++ * port values, packed into two 32-bit registers. Use @imr_line_pos to get the ++ * bit shift of the 2-bit field for a line's IMR settings. Shifts larger than ++ * 32 overflow into the second register. + * + * Because the interrupt mask register (IMR) combines the function of IRQ type + * selection and masking, two extra values are stored. @intr_mask is used to +- * mask/unmask the interrupts for a GPIO port, and @intr_type is used to store ++ * mask/unmask the interrupts for a GPIO line, and @intr_type is used to store + * the selected interrupt types. The logical AND of these values is written to + * IMR on changes. + */ +@@ -59,10 +69,11 @@ struct realtek_gpio_ctrl { + void __iomem *cpumask_base; + struct cpumask cpu_irq_maskable; + raw_spinlock_t lock; +- u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK]; +- u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK]; +- unsigned int (*port_offset_u8)(unsigned int port); +- unsigned int (*port_offset_u16)(unsigned int port); ++ u8 intr_mask[REALTEK_GPIO_MAX]; ++ u8 intr_type[REALTEK_GPIO_MAX]; ++ u32 (*bank_read)(void __iomem *reg); ++ void (*bank_write)(void __iomem *reg, u32 value); ++ unsigned int (*line_imr_pos)(unsigned int line); + }; + + /* Expand with more flags as devices with other quirks are added */ +@@ -101,14 +112,22 @@ static struct realtek_gpio_ctrl *irq_dat + * port. The two interrupt mask registers store two bits per GPIO, so use u16 + * values. + */ +-static unsigned int realtek_gpio_port_offset_u8(unsigned int port) ++static u32 realtek_gpio_bank_read_swapped(void __iomem *reg) ++{ ++ return ioread32be(reg); ++} ++ ++static void realtek_gpio_bank_write_swapped(void __iomem *reg, u32 value) + { +- return port; ++ iowrite32be(value, reg); + } + +-static unsigned int realtek_gpio_port_offset_u16(unsigned int port) ++static unsigned int realtek_gpio_line_imr_pos_swapped(unsigned int line) + { +- return 2 * port; ++ unsigned int port_pin = line % 8; ++ unsigned int port = line / 8; ++ ++ return 2 * (8 * (port ^ 1) + port_pin); + } + + /* +@@ -119,64 +138,65 @@ static unsigned int realtek_gpio_port_of + * per GPIO, so use u16 values. The first register contains ports 1 and 0, the + * second ports 3 and 2. + */ +-static unsigned int realtek_gpio_port_offset_u8_rev(unsigned int port) ++static u32 realtek_gpio_bank_read(void __iomem *reg) + { +- return 3 - port; ++ return ioread32(reg); + } + +-static unsigned int realtek_gpio_port_offset_u16_rev(unsigned int port) ++static void realtek_gpio_bank_write(void __iomem *reg, u32 value) + { +- return 2 * (port ^ 1); ++ iowrite32(value, reg); + } + +-static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl, +- unsigned int port, u16 irq_type, u16 irq_mask) ++static unsigned int realtek_gpio_line_imr_pos(unsigned int line) + { +- iowrite16(irq_type & irq_mask, +- ctrl->base + REALTEK_GPIO_REG_IMR + ctrl->port_offset_u16(port)); ++ return 2 * line; + } + +-static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, +- unsigned int port, u8 mask) ++static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, u32 mask) + { +- iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port)); ++ ctrl->bank_write(ctrl->base + REALTEK_GPIO_REG_ISR, mask); + } + +-static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port) ++static u32 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl) + { +- return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + ctrl->port_offset_u8(port)); ++ return ctrl->bank_read(ctrl->base + REALTEK_GPIO_REG_ISR); + } + +-/* Set the rising and falling edge mask bits for a GPIO port pin */ +-static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value) ++/* Set the rising and falling edge mask bits for a GPIO pin */ ++static void realtek_gpio_update_line_imr(struct realtek_gpio_ctrl *ctrl, unsigned int line) + { +- return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin; ++ void __iomem *reg = ctrl->base + REALTEK_GPIO_REG_IMR; ++ unsigned int line_shift = ctrl->line_imr_pos(line); ++ unsigned int shift = line_shift % 32; ++ u32 irq_type = ctrl->intr_type[line]; ++ u32 irq_mask = ctrl->intr_mask[line]; ++ u32 reg_val; ++ ++ reg += 4 * (line_shift / 32); ++ reg_val = ioread32(reg); ++ reg_val &= ~(REALTEK_GPIO_IMR_LINE_MASK << shift); ++ reg_val |= (irq_type & irq_mask & REALTEK_GPIO_IMR_LINE_MASK) << shift; ++ iowrite32(reg_val, reg); + } + + static void realtek_gpio_irq_ack(struct irq_data *data) + { + struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); + irq_hw_number_t line = irqd_to_hwirq(data); +- unsigned int port = line / 8; +- unsigned int port_pin = line % 8; + +- realtek_gpio_clear_isr(ctrl, port, BIT(port_pin)); ++ realtek_gpio_clear_isr(ctrl, BIT(line)); + } + + static void realtek_gpio_irq_unmask(struct irq_data *data) + { + struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); + unsigned int line = irqd_to_hwirq(data); +- unsigned int port = line / 8; +- unsigned int port_pin = line % 8; + unsigned long flags; +- u16 m; + + raw_spin_lock_irqsave(&ctrl->lock, flags); +- m = ctrl->intr_mask[port]; +- m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); +- ctrl->intr_mask[port] = m; +- realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); ++ ctrl->intr_mask[line] = REALTEK_GPIO_IMR_LINE_MASK; ++ realtek_gpio_update_line_imr(ctrl, line); + raw_spin_unlock_irqrestore(&ctrl->lock, flags); + } + +@@ -184,16 +204,11 @@ static void realtek_gpio_irq_mask(struct + { + struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); + unsigned int line = irqd_to_hwirq(data); +- unsigned int port = line / 8; +- unsigned int port_pin = line % 8; + unsigned long flags; +- u16 m; + + raw_spin_lock_irqsave(&ctrl->lock, flags); +- m = ctrl->intr_mask[port]; +- m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); +- ctrl->intr_mask[port] = m; +- realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); ++ ctrl->intr_mask[line] = 0; ++ realtek_gpio_update_line_imr(ctrl, line); + raw_spin_unlock_irqrestore(&ctrl->lock, flags); + } + +@@ -201,10 +216,8 @@ static int realtek_gpio_irq_set_type(str + { + struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); + unsigned int line = irqd_to_hwirq(data); +- unsigned int port = line / 8; +- unsigned int port_pin = line % 8; + unsigned long flags; +- u16 type, t; ++ u8 type; + + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_FALLING: +@@ -223,11 +236,8 @@ static int realtek_gpio_irq_set_type(str + irq_set_handler_locked(data, handle_edge_irq); + + raw_spin_lock_irqsave(&ctrl->lock, flags); +- t = ctrl->intr_type[port]; +- t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); +- t |= realtek_gpio_imr_bits(port_pin, type); +- ctrl->intr_type[port] = t; +- realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]); ++ ctrl->intr_type[line] = type; ++ realtek_gpio_update_line_imr(ctrl, line); + raw_spin_unlock_irqrestore(&ctrl->lock, flags); + + return 0; +@@ -238,31 +248,24 @@ static void realtek_gpio_irq_handler(str + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); +- unsigned int lines_done; +- unsigned int port_pin_count; + unsigned int irq; + unsigned long status; + int offset; + + chained_irq_enter(irq_chip, desc); + +- for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) { +- status = realtek_gpio_read_isr(ctrl, lines_done / 8); +- port_pin_count = min(gc->ngpio - lines_done, 8U); +- for_each_set_bit(offset, &status, port_pin_count) { +- irq = irq_find_mapping(gc->irq.domain, offset + lines_done); +- generic_handle_irq(irq); +- } ++ status = realtek_gpio_read_isr(ctrl); ++ for_each_set_bit(offset, &status, gc->ngpio) { ++ irq = irq_find_mapping(gc->irq.domain, offset); ++ generic_handle_irq(irq); + } + + chained_irq_exit(irq_chip, desc); + } + +-static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl, +- unsigned int port, int cpu) ++static inline void __iomem *realtek_gpio_irq_cpu_mask(struct realtek_gpio_ctrl *ctrl, int cpu) + { +- return ctrl->cpumask_base + ctrl->port_offset_u8(port) + +- REALTEK_GPIO_PORTS_PER_BANK * cpu; ++ return ctrl->cpumask_base + REALTEK_GPIO_PORTS_PER_BANK * cpu; + } + + static int realtek_gpio_irq_set_affinity(struct irq_data *data, +@@ -270,12 +273,10 @@ static int realtek_gpio_irq_set_affinity + { + struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); + unsigned int line = irqd_to_hwirq(data); +- unsigned int port = line / 8; +- unsigned int port_pin = line % 8; + void __iomem *irq_cpu_mask; + unsigned long flags; + int cpu; +- u8 v; ++ u32 v; + + if (!ctrl->cpumask_base) + return -ENXIO; +@@ -283,15 +284,15 @@ static int realtek_gpio_irq_set_affinity + raw_spin_lock_irqsave(&ctrl->lock, flags); + + for_each_cpu(cpu, &ctrl->cpu_irq_maskable) { +- irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu); +- v = ioread8(irq_cpu_mask); ++ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, cpu); ++ v = ctrl->bank_read(irq_cpu_mask); + + if (cpumask_test_cpu(cpu, dest)) +- v |= BIT(port_pin); ++ v |= BIT(line); + else +- v &= ~BIT(port_pin); ++ v &= ~BIT(line); + +- iowrite8(v, irq_cpu_mask); ++ ctrl->bank_write(irq_cpu_mask, v); + } + + raw_spin_unlock_irqrestore(&ctrl->lock, flags); +@@ -305,22 +306,23 @@ static int realtek_gpio_irq_init(struct + { + struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); + void __iomem *irq_cpu_mask; +- unsigned int port; ++ u32 mask_all = GENMASK(gc->ngpio - 1, 0); ++ unsigned int line; + int cpu; + +- for (port = 0; (port * 8) < gc->ngpio; port++) { +- realtek_gpio_write_imr(ctrl, port, 0, 0); +- realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0)); +- +- /* +- * Uniprocessor builds assume a mask always contains one CPU, +- * so only start the loop if we have at least one maskable CPU. +- */ +- if(!cpumask_empty(&ctrl->cpu_irq_maskable)) { +- for_each_cpu(cpu, &ctrl->cpu_irq_maskable) { +- irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, port, cpu); +- iowrite8(GENMASK(7, 0), irq_cpu_mask); +- } ++ for (line = 0; line < gc->ngpio; line++) ++ realtek_gpio_update_line_imr(ctrl, line); ++ ++ realtek_gpio_clear_isr(ctrl, mask_all); ++ ++ /* ++ * Uniprocessor builds assume a mask always contains one CPU, ++ * so only start the loop if we have at least one maskable CPU. ++ */ ++ if(!cpumask_empty(&ctrl->cpu_irq_maskable)) { ++ for_each_cpu(cpu, &ctrl->cpu_irq_maskable) { ++ irq_cpu_mask = realtek_gpio_irq_cpu_mask(ctrl, cpu); ++ ctrl->bank_write(irq_cpu_mask, mask_all); + } + } + +@@ -393,12 +395,14 @@ static int realtek_gpio_probe(struct pla + + if (dev_flags & GPIO_PORTS_REVERSED) { + bgpio_flags = 0; +- ctrl->port_offset_u8 = realtek_gpio_port_offset_u8_rev; +- ctrl->port_offset_u16 = realtek_gpio_port_offset_u16_rev; ++ ctrl->bank_read = realtek_gpio_bank_read; ++ ctrl->bank_write = realtek_gpio_bank_write; ++ ctrl->line_imr_pos = realtek_gpio_line_imr_pos; + } else { + bgpio_flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; +- ctrl->port_offset_u8 = realtek_gpio_port_offset_u8; +- ctrl->port_offset_u16 = realtek_gpio_port_offset_u16; ++ ctrl->bank_read = realtek_gpio_bank_read_swapped; ++ ctrl->bank_write = realtek_gpio_bank_write_swapped; ++ ctrl->line_imr_pos = realtek_gpio_line_imr_pos_swapped; + } + + err = bgpio_init(&ctrl->gc, dev, 4, diff --git a/target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch b/target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch new file mode 100644 index 00000000000..94c42308356 --- /dev/null +++ b/target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch @@ -0,0 +1,33 @@ +From 800d5fb3c6a16661932c932bacd660e38d06b727 Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Thu, 25 Aug 2022 08:22:36 +0200 +Subject: [PATCH] realtek: add patch to enable new clock driver in kernel + +Allow building the clock driver with kernel config options. + +Submitted-by: Markus Stockhausen +--- + drivers/clk/Kconfig | 1 + + drivers/clk/Makefile | 1 + + 2 files changed, 2 insertions(+) + +--- a/drivers/clk/Kconfig ++++ b/drivers/clk/Kconfig +@@ -372,6 +372,7 @@ source "drivers/clk/mediatek/Kconfig" + source "drivers/clk/meson/Kconfig" + source "drivers/clk/mvebu/Kconfig" + source "drivers/clk/qcom/Kconfig" ++source "drivers/clk/realtek/Kconfig" + source "drivers/clk/renesas/Kconfig" + source "drivers/clk/rockchip/Kconfig" + source "drivers/clk/samsung/Kconfig" +--- a/drivers/clk/Makefile ++++ b/drivers/clk/Makefile +@@ -100,6 +100,7 @@ obj-$(CONFIG_COMMON_CLK_NXP) += nxp/ + obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ + obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ + obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ ++obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/ + obj-y += renesas/ + obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ + obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ diff --git a/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch b/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch new file mode 100644 index 00000000000..bb95e7103a3 --- /dev/null +++ b/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch @@ -0,0 +1,159 @@ +From 2cd00b51470a30198b048a5fca48a04db77e29cc Mon Sep 17 00:00:00 2001 +From: INAGAKI Hiroshi +Date: Fri, 21 May 2021 23:16:37 +0900 +Subject: [PATCH] realtek: backport irq-realtek-rtl driver from 5.12 to 5.10 + +This patch backports "irq-realtek-rtl" driver to Kernel 5.10 from 5.12. +"MACH_REALTEK_RTL" is used as a platform name in upstream, but "RTL838X" +is used in OpenWrt, so update the dependency by the additional patch. + +Submitted-by: INAGAKI Hiroshi +--- + drivers/irqchip/irq-realtek-rtl.c | 38 +++++++++++------ + 1 files changed, 58 insertions(+), 20 deletions(-) + +--- a/drivers/irqchip/irq-realtek-rtl.c ++++ b/drivers/irqchip/irq-realtek-rtl.c +@@ -28,6 +28,7 @@ static DEFINE_RAW_SPINLOCK(irq_lock); + + #define REG(offset, cpu) (realtek_ictl_base[cpu] + offset) + ++static u32 realtek_ictl_unmask[NR_CPUS]; + static void __iomem *realtek_ictl_base[NR_CPUS]; + static cpumask_t realtek_ictl_cpu_configurable; + +@@ -41,11 +42,29 @@ struct realtek_ictl_output { + }; + + /* +- * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering, +- * placing IRQ 31 in the first four bits. A routing value of '0' means the +- * interrupt is left disconnected. Routing values {1..15} connect to output +- * lines {0..14}. ++ * Per CPU we have a set of 5 registers that determine interrupt handling for ++ * 32 external interrupts. GIMR (enable/disable interrupt) plus IRR0-IRR3 that ++ * contain "routing" or "priority" values. GIMR uses one bit for each interrupt ++ * and IRRx store 4 bits per interrupt. Realtek uses inverted numbering, ++ * placing IRQ 31 in the first four bits. The register combinations give the ++ * following results for a single interrupt in the wild: ++ * ++ * a) GIMR = 0 / IRRx > 0 -> no interrupts ++ * b) GIMR = 0 / IRRx = 0 -> no interrupts ++ * c) GIMR = 1 / IRRx > 0 -> interrupts ++ * d) GIMR = 1 / IRRx = 0 -> rare interrupts in SMP environment ++ * ++ * Combination d) seems to trigger interrupts only on a VPE if the other VPE ++ * has GIMR = 0 and IRRx > 0. E.g. busy without interrupts allowed. To provide ++ * IRQ balancing features in SMP this driver will handle the registers as ++ * follows: ++ * ++ * 1) set IRRx > 0 for VPE where the interrupt is desired ++ * 2) set IRRx = 0 for VPE where the interrupt is not desired ++ * 3) set both GIMR = 0 to mask (disabled) interrupt ++ * 4) set GIMR = 1 to unmask (enable) interrupt but only for VPE where IRRx > 0 + */ ++ + #define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32)) + #define IRR_SHIFT(idx) ((idx * 4) % 32) + +@@ -65,19 +84,33 @@ static inline void write_irr(void __iome + writel(irr, irr0 + offset); + } + ++static inline void enable_gimr(int hwirq, int cpu) ++{ ++ u32 value; ++ ++ value = readl(REG(RTL_ICTL_GIMR, cpu)); ++ value |= (BIT(hwirq) & realtek_ictl_unmask[cpu]); ++ writel(value, REG(RTL_ICTL_GIMR, cpu)); ++} ++ ++static inline void disable_gimr(int hwirq, int cpu) ++{ ++ u32 value; ++ ++ value = readl(REG(RTL_ICTL_GIMR, cpu)); ++ value &= ~BIT(hwirq); ++ writel(value, REG(RTL_ICTL_GIMR, cpu)); ++} ++ + static void realtek_ictl_unmask_irq(struct irq_data *i) + { + unsigned long flags; +- u32 value; + int cpu; + + raw_spin_lock_irqsave(&irq_lock, flags); + +- for_each_cpu(cpu, &realtek_ictl_cpu_configurable) { +- value = readl(REG(RTL_ICTL_GIMR, cpu)); +- value |= BIT(i->hwirq); +- writel(value, REG(RTL_ICTL_GIMR, cpu)); +- } ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) ++ enable_gimr(i->hwirq, cpu); + + raw_spin_unlock_irqrestore(&irq_lock, flags); + } +@@ -85,16 +118,12 @@ static void realtek_ictl_unmask_irq(stru + static void realtek_ictl_mask_irq(struct irq_data *i) + { + unsigned long flags; +- u32 value; + int cpu; + + raw_spin_lock_irqsave(&irq_lock, flags); + +- for_each_cpu(cpu, &realtek_ictl_cpu_configurable) { +- value = readl(REG(RTL_ICTL_GIMR, cpu)); +- value &= ~BIT(i->hwirq); +- writel(value, REG(RTL_ICTL_GIMR, cpu)); +- } ++ for_each_cpu(cpu, &realtek_ictl_cpu_configurable) ++ disable_gimr(i->hwirq, cpu); + + raw_spin_unlock_irqrestore(&irq_lock, flags); + } +@@ -116,11 +145,17 @@ static int __maybe_unused realtek_ictl_i + cpumask_and(&cpu_enable, &cpu_configure, dest); + cpumask_andnot(&cpu_disable, &cpu_configure, dest); + +- for_each_cpu(cpu, &cpu_disable) ++ for_each_cpu(cpu, &cpu_disable) { + write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, 0); ++ realtek_ictl_unmask[cpu] &= ~BIT(i->hwirq); ++ disable_gimr(i->hwirq, cpu); ++ } + +- for_each_cpu(cpu, &cpu_enable) ++ for_each_cpu(cpu, &cpu_enable) { + write_irr(REG(RTL_ICTL_IRR0, cpu), i->hwirq, output->output_index + 1); ++ realtek_ictl_unmask[cpu] |= BIT(i->hwirq); ++ enable_gimr(i->hwirq, cpu); ++ } + + irq_data_update_effective_affinity(i, &cpu_enable); + +@@ -149,6 +184,7 @@ static int intc_map(struct irq_domain *d + + output->child_mask |= BIT(hw); + write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1); ++ realtek_ictl_unmask[0] |= BIT(hw); + + raw_spin_unlock_irqrestore(&irq_lock, flags); + +@@ -279,9 +315,11 @@ static int __init realtek_rtl_of_init(st + cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable); + + /* Disable all cascaded interrupts and clear routing */ +- writel(0, REG(RTL_ICTL_GIMR, cpu)); +- for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) ++ for (soc_irq = 0; soc_irq < RTL_ICTL_NUM_INPUTS; soc_irq++) { + write_irr(REG(RTL_ICTL_IRR0, cpu), soc_irq, 0); ++ realtek_ictl_unmask[cpu] &= ~BIT(soc_irq); ++ disable_gimr(soc_irq, cpu); ++ } + } + } + diff --git a/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch b/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch new file mode 100644 index 00000000000..f97ea94d9b8 --- /dev/null +++ b/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch @@ -0,0 +1,40 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: dsa: Add support for rtl838x switch + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + drivers/net/dsa/rtl83xx/Kconfig | 2 ++ + drivers/net/dsa/rtl83xx/Makefile | 1 + + 2 files changed, 3 insertions(+) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -68,6 +68,8 @@ config NET_DSA_QCA8K + This enables support for the Qualcomm Atheros QCA8K Ethernet + switch chips. + ++source "drivers/net/dsa/rtl83xx/Kconfig" ++ + config NET_DSA_REALTEK_SMI + tristate "Realtek SMI Ethernet switch family support" + depends on NET_DSA +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -23,3 +23,4 @@ obj-y += mv88e6xxx/ + obj-y += ocelot/ + obj-y += qca/ + obj-y += sja1105/ ++obj-y += rtl83xx/ diff --git a/target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch b/target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch new file mode 100644 index 00000000000..27299f17dea --- /dev/null +++ b/target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch @@ -0,0 +1,61 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: dsa: Add rtl838x support for tag trailer + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + net/dsa/tag_trailer.c | 16 +++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +--- a/net/dsa/tag_trailer.c ++++ b/net/dsa/tag_trailer.c +@@ -17,7 +17,12 @@ static struct sk_buff *trailer_xmit(stru + + trailer = skb_put(skb, 4); + trailer[0] = 0x80; ++ ++#ifdef CONFIG_NET_DSA_RTL83XX ++ trailer[1] = dp->index; ++#else + trailer[1] = 1 << dp->index; ++#endif /* CONFIG_NET_DSA_RTL838X */ + trailer[2] = 0x10; + trailer[3] = 0x00; + +@@ -34,12 +39,23 @@ static struct sk_buff *trailer_rcv(struc + return NULL; + + trailer = skb_tail_pointer(skb) - 4; ++ ++#ifdef CONFIG_NET_DSA_RTL83XX ++ if (trailer[0] != 0x80 || (trailer[1] & 0x80) != 0x00 || ++ (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) ++ return NULL; ++ ++ if (trailer[1] & 0x40) ++ skb->offload_fwd_mark = 1; ++ ++ source_port = trailer[1] & 0x3f; ++#else + if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || + (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) + return NULL; + + source_port = trailer[1] & 7; +- ++#endif + skb->dev = dsa_master_find_slave(dev, 0, source_port); + if (!skb->dev) + return NULL; diff --git a/target/linux/realtek/patches-5.15/702-net-dsa-increase-dsa-max-ports-for-rtl838x.patch b/target/linux/realtek/patches-5.15/702-net-dsa-increase-dsa-max-ports-for-rtl838x.patch new file mode 100644 index 00000000000..63991d373cd --- /dev/null +++ b/target/linux/realtek/patches-5.15/702-net-dsa-increase-dsa-max-ports-for-rtl838x.patch @@ -0,0 +1,32 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: dsa: Increase max ports for rtl838x + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + include/linux/platform_data/dsa.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/platform_data/dsa.h ++++ b/include/linux/platform_data/dsa.h +@@ -6,7 +6,7 @@ struct device; + struct net_device; + + #define DSA_MAX_SWITCHES 4 +-#define DSA_MAX_PORTS 12 ++#define DSA_MAX_PORTS 54 + #define DSA_RTABLE_NONE -1 + + struct dsa_chip_data { diff --git a/target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch b/target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch new file mode 100644 index 00000000000..9f50e8b5648 --- /dev/null +++ b/target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch @@ -0,0 +1,48 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: ethernet: Add support for RTL838x ethernet + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + drivers/net/ethernet/Kconfig | 7 +- + drivers/net/ethernet/Makefile | 1 + + 2 files changed, 8 insertions(+) + +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -163,6 +163,13 @@ source "drivers/net/ethernet/rdc/Kconfig + source "drivers/net/ethernet/realtek/Kconfig" + source "drivers/net/ethernet/renesas/Kconfig" + source "drivers/net/ethernet/rocker/Kconfig" ++ ++config NET_RTL838X ++ tristate "Realtek rtl838x Ethernet MAC support" ++ depends on RTL83XX ++ help ++ Say Y here if you want to use the Realtek rtl838x Gbps Ethernet MAC. ++ + source "drivers/net/ethernet/samsung/Kconfig" + source "drivers/net/ethernet/seeq/Kconfig" + source "drivers/net/ethernet/sfc/Kconfig" +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -75,6 +75,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real + obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/ + obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ + obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ ++obj-$(CONFIG_NET_RTL838X) += rtl838x_eth.o + obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/ + obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/ + obj-$(CONFIG_NET_VENDOR_SILAN) += silan/ diff --git a/target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch new file mode 100644 index 00000000000..8f68dd4e884 --- /dev/null +++ b/target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch @@ -0,0 +1,34 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: phy: Add PHY ops for rtl838x EEE + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + include/linux/phy.h | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -885,6 +885,10 @@ struct phy_driver { + int (*get_sqi)(struct phy_device *dev); + /** @get_sqi_max: Get the maximum signal quality indication */ + int (*get_sqi_max)(struct phy_device *dev); ++ int (*get_port)(struct phy_device *dev); ++ int (*set_port)(struct phy_device *dev, int port); ++ int (*get_eee)(struct phy_device *dev, struct ethtool_eee *e); ++ int (*set_eee)(struct phy_device *dev, struct ethtool_eee *e); + }; + #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) diff --git a/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch b/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch new file mode 100644 index 00000000000..c46be91fe01 --- /dev/null +++ b/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch @@ -0,0 +1,61 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: net: phy: EEE support for rtl838x + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + drivers/net/phy/phylink. | 14 +++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -1449,6 +1449,11 @@ int phylink_ethtool_ksettings_set(struct + * the presence of a PHY, this should not be changed as that + * should be determined from the media side advertisement. + */ ++ if (pl->phydev->drv->get_port && pl->phydev->drv->set_port) { ++ if(pl->phydev->drv->get_port(pl->phydev) != kset->base.port) { ++ pl->phydev->drv->set_port(pl->phydev, kset->base.port); ++ } ++ } + return phy_ethtool_ksettings_set(pl->phydev, kset); + } + +@@ -1750,8 +1755,11 @@ int phylink_ethtool_get_eee(struct phyli + + ASSERT_RTNL(); + +- if (pl->phydev) ++ if (pl->phydev) { ++ if (pl->phydev->drv->get_eee) ++ return pl->phydev->drv->get_eee(pl->phydev, eee); + ret = phy_ethtool_get_eee(pl->phydev, eee); ++ } + + return ret; + } +@@ -1768,8 +1776,11 @@ int phylink_ethtool_set_eee(struct phyli + + ASSERT_RTNL(); + +- if (pl->phydev) ++ if (pl->phydev) { ++ if (pl->phydev->drv->set_eee) ++ return pl->phydev->drv->set_eee(pl->phydev, eee); + ret = phy_ethtool_set_eee(pl->phydev, eee); ++ } + + return ret; + } diff --git a/target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch b/target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch new file mode 100644 index 00000000000..442bff82fae --- /dev/null +++ b/target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch @@ -0,0 +1,33 @@ +From 9d9bf16aa8d966834ac1280f96c37d22552c33d1 Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Wed, 8 Sep 2021 16:13:18 +0200 +Subject: phy: Add PHY hsgmii mode + +This adds RTL93xx-specific MAC configuration routines that allow also configuration +of 10GBit links for phylink. There is support for the Realtek-specific HISGMI +protocol. + +Submitted-by: Birger Koblitz +--- + include/linux/phy.h | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -134,6 +134,7 @@ typedef enum { + PHY_INTERFACE_MODE_XGMII, + PHY_INTERFACE_MODE_XLGMII, + PHY_INTERFACE_MODE_MOCA, ++ PHY_INTERFACE_MODE_HSGMII, + PHY_INTERFACE_MODE_QSGMII, + PHY_INTERFACE_MODE_TRGMII, + PHY_INTERFACE_MODE_100BASEX, +@@ -201,6 +202,8 @@ static inline const char *phy_modes(phy_ + return "xlgmii"; + case PHY_INTERFACE_MODE_MOCA: + return "moca"; ++ case PHY_INTERFACE_MODE_HSGMII: ++ return "hsgmii"; + case PHY_INTERFACE_MODE_QSGMII: + return "qsgmii"; + case PHY_INTERFACE_MODE_TRGMII: diff --git a/target/linux/realtek/patches-5.15/705-add-rtl-phy.patch b/target/linux/realtek/patches-5.15/705-add-rtl-phy.patch new file mode 100644 index 00000000000..47041262fc3 --- /dev/null +++ b/target/linux/realtek/patches-5.15/705-add-rtl-phy.patch @@ -0,0 +1,39 @@ +From 89f71ebb355c624320c2b0ace8ae9488ff53cbeb Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Tue, 5 Jan 2021 20:40:52 +0100 +Subject: PHY: Add realtek PHY + +This fixes the build problems for the REALTEK target by adding a proper +configuration option for the phy module. + +Submitted-by: Birger Koblitz +--- + drivers/net/phy/Kconfig | 6 ++++++ + drivers/net/phy/Makefile | 1 + + 2 files changed, 7 insertions(+) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -330,6 +330,12 @@ config REALTEK_PHY + help + Supports the Realtek 821x PHY. + ++config REALTEK_SOC_PHY ++ tristate "Realtek SoC PHYs" ++ depends on RTL83XX ++ help ++ Supports the PHYs found in combination with Realtek Switch SoCs ++ + config RENESAS_PHY + tristate "Renesas PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -89,6 +89,7 @@ obj-$(CONFIG_NATIONAL_PHY) += national.o + obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o + obj-$(CONFIG_QSEMI_PHY) += qsemi.o + obj-$(CONFIG_REALTEK_PHY) += realtek.o ++obj-$(CONFIG_REALTEK_SOC_PHY) += rtl83xx-phy.o + obj-$(CONFIG_RENESAS_PHY) += uPD60620.o + obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o + obj-$(CONFIG_SMSC_PHY) += smsc.o diff --git a/target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch b/target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch new file mode 100644 index 00000000000..bba6fd71828 --- /dev/null +++ b/target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch @@ -0,0 +1,32 @@ +From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 26 Nov 2020 12:02:21 +0100 +Subject: PHY: Increase max PHY adddress number + +* rename the target to realtek +* add refactored DSA driver +* add latest gpio driver +* lots of arch cleanups +* new irq driver +* additional boards + +Submitted-by: Bert Vermeulen +Submitted-by: Birger Koblitz +Submitted-by: Sander Vanheule +Submitted-by: Bjørn Mork +Submitted-by: John Crispin +--- + include/linux/phy.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -233,7 +233,7 @@ static inline const char *phy_modes(phy_ + #define PHY_INIT_TIMEOUT 100000 + #define PHY_FORCE_TIMEOUT 10 + +-#define PHY_MAX_ADDR 32 ++#define PHY_MAX_ADDR 64 + + /* Used when trying to connect to a specific phy (mii bus id:phy device id) */ + #define PHY_ID_FMT "%s:%02x" diff --git a/target/linux/realtek/patches-5.15/708-brflood-api.patch b/target/linux/realtek/patches-5.15/708-brflood-api.patch new file mode 100644 index 00000000000..2716933ae70 --- /dev/null +++ b/target/linux/realtek/patches-5.15/708-brflood-api.patch @@ -0,0 +1,162 @@ +From afa3ab54c03d5126b14651f367b38165fab5b3cc Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Tue, 18 Jan 2022 17:18:43 +0100 +Subject: net: brflood API + +Adds the DSA API for bridge configuration (flooding, L2 learning, +and aging) offload as found in Linux 5.12 so that we can implement +it in our drivver. + +Submitted-by: Sebastian Gottschall +Submitted-by: Birger Koblitz +--- + include/net/dsa.h | 6 +++++++-- + net/dsa/dsa_priv.h | 6 +++--- + net/dsa/port.c | 28 ++++++++---- + net/dsa/slave.c | 6 +++--- + 4 file changed, 29 insertions(+), 13 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -552,8 +552,14 @@ struct dsa_switch_ops { + void (*port_stp_state_set)(struct dsa_switch *ds, int port, + u8 state); + void (*port_fast_age)(struct dsa_switch *ds, int port); +- int (*port_egress_floods)(struct dsa_switch *ds, int port, +- bool unicast, bool multicast); ++ int (*port_pre_bridge_flags)(struct dsa_switch *ds, int port, ++ unsigned long flags, ++ struct netlink_ext_ack *extack); ++ int (*port_bridge_flags)(struct dsa_switch *ds, int port, ++ unsigned long flags, ++ struct netlink_ext_ack *extack); ++ int (*port_set_mrouter)(struct dsa_switch *ds, int port, bool mrouter, ++ struct netlink_ext_ack *extack); + + /* + * VLAN support +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -167,11 +167,11 @@ int dsa_port_mdb_add(const struct dsa_po + int dsa_port_mdb_del(const struct dsa_port *dp, + const struct switchdev_obj_port_mdb *mdb); + int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, +- struct switchdev_trans *trans); ++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); + int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, +- struct switchdev_trans *trans); ++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); + int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, +- struct switchdev_trans *trans); ++ struct switchdev_trans *trans, struct netlink_ext_ack *extack); + int dsa_port_vlan_add(struct dsa_port *dp, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans); +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -145,7 +145,7 @@ int dsa_port_bridge_join(struct dsa_port + int err; + + /* Set the flooding mode before joining the port in the switch */ +- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL); ++ err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL); + if (err) + return err; + +@@ -158,7 +158,7 @@ int dsa_port_bridge_join(struct dsa_port + + /* The bridging is rolled back on error */ + if (err) { +- dsa_port_bridge_flags(dp, 0, NULL); ++ dsa_port_bridge_flags(dp, 0, NULL, NULL); + dp->bridge_dev = NULL; + } + +@@ -185,7 +185,7 @@ void dsa_port_bridge_leave(struct dsa_po + pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); + + /* Port is leaving the bridge, disable flooding */ +- dsa_port_bridge_flags(dp, 0, NULL); ++ dsa_port_bridge_flags(dp, 0, NULL, NULL); + + /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, + * so allow it to be in BR_STATE_FORWARDING to be kept functional +@@ -333,44 +333,44 @@ int dsa_port_ageing_time(struct dsa_port + } + + int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, +- struct switchdev_trans *trans) ++ struct switchdev_trans *trans, struct netlink_ext_ack *extack) + { + struct dsa_switch *ds = dp->ds; + +- if (!ds->ops->port_egress_floods || +- (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) ++ if (!ds->ops->port_pre_bridge_flags) + return -EINVAL; + +- return 0; ++ return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); + } + + int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, +- struct switchdev_trans *trans) ++ struct switchdev_trans *trans, struct netlink_ext_ack *extack) + { + struct dsa_switch *ds = dp->ds; +- int port = dp->index; +- int err = 0; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + +- if (ds->ops->port_egress_floods) +- err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, +- flags & BR_MCAST_FLOOD); ++ if (!ds->ops->port_bridge_flags) ++ return -EINVAL; ++ ++ return ds->ops->port_bridge_flags(ds, dp->index, flags, extack); + +- return err; + } + + int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, +- struct switchdev_trans *trans) ++ struct switchdev_trans *trans, ++ struct netlink_ext_ack *extack) + { + struct dsa_switch *ds = dp->ds; +- int port = dp->index; + + if (switchdev_trans_ph_prepare(trans)) +- return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP; ++ return ds->ops->port_set_mrouter ? 0 : -EOPNOTSUPP; ++ ++ if (!ds->ops->port_set_mrouter) ++ return -EOPNOTSUPP; + +- return ds->ops->port_egress_floods(ds, port, true, mrouter); ++ return ds->ops->port_set_mrouter(ds, dp->index, mrouter, extack); + } + + int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -290,13 +290,13 @@ static int dsa_slave_port_attr_set(struc + break; + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: + ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags, +- trans); ++ trans, NULL); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: +- ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans); ++ ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans, NULL); + break; + case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: +- ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans); ++ ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans, NULL); + break; + default: + ret = -EOPNOTSUPP; diff --git a/target/linux/realtek/patches-5.15/709-lag-offloading.patch b/target/linux/realtek/patches-5.15/709-lag-offloading.patch new file mode 100644 index 00000000000..f84687ff4ac --- /dev/null +++ b/target/linux/realtek/patches-5.15/709-lag-offloading.patch @@ -0,0 +1,781 @@ +From afa3ab54c03d5126b14651f367b38165fab5b3cc Mon Sep 17 00:00:00 2001 +From: Birger Koblitz +Date: Tue, 18 Jan 2022 17:18:43 +0100 +Subject: [PATCH] realtek: Backport bridge configuration for DSA + +Adds the DSA API for bridge configuration (flooding, L2 learning, +and aging) offload as found in Linux 5.12 so that we can implement +it in our drivver. + +Submitted-by: Sebastian Gottschall +Submitted-by: Birger Koblitz +--- + drivers/net/bonding/bond_main.c | 2 ++ + include/net/dsa.h | 79 ++++++++++++++++- + net/dsa/dsa2.c | 88 +++++++++++++++++++ + net/dsa/dsa_priv.h | 74 ++++++++++++++ + net/dsa/port.c | 92 ++++++++++++++++++++ + net/dsa/slave.c | 88 ++++++++++++++++--- + net/dsa/switch.c | 49 ++++++++++ + net/sda/tag_dsa.c | 13 +++++- + 8 file changed, 460 insertions(+), 25 deletions(-) + +--- a/drivers/net/bonding/bond_main.c ++++ b/drivers/net/bonding/bond_main.c +@@ -2045,6 +2045,8 @@ int bond_enslave(struct net_device *bond + goto err_unregister; + } + ++ bond_lower_state_changed(new_slave); ++ + res = bond_sysfs_slave_add(new_slave); + if (res) { + slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res); +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -149,8 +149,41 @@ struct dsa_switch_tree { + + /* List of DSA links composing the routing table */ + struct list_head rtable; ++ ++ /* Maps offloaded LAG netdevs to a zero-based linear ID for ++ * drivers that need it. ++ */ ++ struct net_device **lags; ++ unsigned int lags_len; + }; + ++#define dsa_lags_foreach_id(_id, _dst) \ ++ for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++) \ ++ if ((_dst)->lags[(_id)]) ++ ++#define dsa_lag_foreach_port(_dp, _dst, _lag) \ ++ list_for_each_entry((_dp), &(_dst)->ports, list) \ ++ if ((_dp)->lag_dev == (_lag)) ++ ++static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst, ++ unsigned int id) ++{ ++ return dst->lags[id]; ++} ++ ++static inline int dsa_lag_id(struct dsa_switch_tree *dst, ++ struct net_device *lag) ++{ ++ unsigned int id; ++ ++ dsa_lags_foreach_id(id, dst) { ++ if (dsa_lag_dev(dst, id) == lag) ++ return id; ++ } ++ ++ return -ENODEV; ++} ++ + /* TC matchall action types */ + enum dsa_port_mall_action_type { + DSA_PORT_MALL_MIRROR, +@@ -220,6 +253,8 @@ struct dsa_port { + bool devlink_port_setup; + struct phylink *pl; + struct phylink_config pl_config; ++ struct net_device *lag_dev; ++ bool lag_tx_enabled; + + struct list_head list; + +@@ -340,6 +375,14 @@ struct dsa_switch { + */ + bool mtu_enforcement_ingress; + ++ /* Drivers that benefit from having an ID associated with each ++ * offloaded LAG should set this to the maximum number of ++ * supported IDs. DSA will then maintain a mapping of _at ++ * least_ these many IDs, accessible to drivers via ++ * dsa_lag_id(). ++ */ ++ unsigned int num_lag_ids; ++ + size_t num_ports; + }; + +@@ -432,6 +475,18 @@ static inline bool dsa_port_is_vlan_filt + return dp->vlan_filtering; + } + ++static inline ++struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp) ++{ ++ if (!dp->bridge_dev) ++ return NULL; ++ ++ if (dp->lag_dev) ++ return dp->lag_dev; ++ ++ return dp->slave; ++} ++ + typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid, + bool is_static, void *data); + struct dsa_switch_ops { +@@ -629,6 +684,13 @@ struct dsa_switch_ops { + void (*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index, + int sw_index, int port, + struct net_device *br); ++ int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index, ++ int port); ++ int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index, ++ int port, struct net_device *lag, ++ struct netdev_lag_upper_info *info); ++ int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index, ++ int port, struct net_device *lag); + + /* + * PTP functionality +@@ -660,6 +722,16 @@ struct dsa_switch_ops { + int (*port_change_mtu)(struct dsa_switch *ds, int port, + int new_mtu); + int (*port_max_mtu)(struct dsa_switch *ds, int port); ++ ++ /* ++ * LAG integration ++ */ ++ int (*port_lag_change)(struct dsa_switch *ds, int port); ++ int (*port_lag_join)(struct dsa_switch *ds, int port, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info); ++ int (*port_lag_leave)(struct dsa_switch *ds, int port, ++ struct net_device *lag); + }; + + #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \ +--- a/net/dsa/dsa.c ++++ b/net/dsa/dsa.c +@@ -220,11 +220,21 @@ static int dsa_switch_rcv(struct sk_buff + } + + skb = nskb; +- p = netdev_priv(skb->dev); + skb_push(skb, ETH_HLEN); + skb->pkt_type = PACKET_HOST; + skb->protocol = eth_type_trans(skb, skb->dev); + ++ if (unlikely(!dsa_slave_dev_check(skb->dev))) { ++ /* Packet is to be injected directly on an upper ++ * device, e.g. a team/bond, so skip all DSA-port ++ * specific actions. ++ */ ++ netif_rx(skb); ++ return 0; ++ } ++ ++ p = netdev_priv(skb->dev); ++ + if (unlikely(cpu_dp->ds->untag_bridge_pvid)) { + nskb = dsa_untag_bridge_pvid(skb); + if (!nskb) { +--- a/net/dsa/dsa2.c ++++ b/net/dsa/dsa2.c +@@ -21,6 +21,65 @@ + static DEFINE_MUTEX(dsa2_mutex); + LIST_HEAD(dsa_tree_list); + ++/** ++ * dsa_lag_map() - Map LAG netdev to a linear LAG ID ++ * @dst: Tree in which to record the mapping. ++ * @lag: Netdev that is to be mapped to an ID. ++ * ++ * dsa_lag_id/dsa_lag_dev can then be used to translate between the ++ * two spaces. The size of the mapping space is determined by the ++ * driver by setting ds->num_lag_ids. It is perfectly legal to leave ++ * it unset if it is not needed, in which case these functions become ++ * no-ops. ++ */ ++void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag) ++{ ++ unsigned int id; ++ ++ if (dsa_lag_id(dst, lag) >= 0) ++ /* Already mapped */ ++ return; ++ ++ for (id = 0; id < dst->lags_len; id++) { ++ if (!dsa_lag_dev(dst, id)) { ++ dst->lags[id] = lag; ++ return; ++ } ++ } ++ ++ /* No IDs left, which is OK. Some drivers do not need it. The ++ * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id ++ * returns an error for this device when joining the LAG. The ++ * driver can then return -EOPNOTSUPP back to DSA, which will ++ * fall back to a software LAG. ++ */ ++} ++ ++/** ++ * dsa_lag_unmap() - Remove a LAG ID mapping ++ * @dst: Tree in which the mapping is recorded. ++ * @lag: Netdev that was mapped. ++ * ++ * As there may be multiple users of the mapping, it is only removed ++ * if there are no other references to it. ++ */ ++void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag) ++{ ++ struct dsa_port *dp; ++ unsigned int id; ++ ++ dsa_lag_foreach_port(dp, dst, lag) ++ /* There are remaining users of this mapping */ ++ return; ++ ++ dsa_lags_foreach_id(id, dst) { ++ if (dsa_lag_dev(dst, id) == lag) { ++ dst->lags[id] = NULL; ++ break; ++ } ++ } ++} ++ + struct dsa_switch *dsa_switch_find(int tree_index, int sw_index) + { + struct dsa_switch_tree *dst; +@@ -597,6 +656,32 @@ static void dsa_tree_teardown_master(str + dsa_master_teardown(dp->master); + } + ++static int dsa_tree_setup_lags(struct dsa_switch_tree *dst) ++{ ++ unsigned int len = 0; ++ struct dsa_port *dp; ++ ++ list_for_each_entry(dp, &dst->ports, list) { ++ if (dp->ds->num_lag_ids > len) ++ len = dp->ds->num_lag_ids; ++ } ++ ++ if (!len) ++ return 0; ++ ++ dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL); ++ if (!dst->lags) ++ return -ENOMEM; ++ ++ dst->lags_len = len; ++ return 0; ++} ++ ++static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst) ++{ ++ kfree(dst->lags); ++} ++ + static int dsa_tree_setup(struct dsa_switch_tree *dst) + { + bool complete; +@@ -624,12 +709,18 @@ static int dsa_tree_setup(struct dsa_swi + if (err) + goto teardown_switches; + ++ err = dsa_tree_setup_lags(dst); ++ if (err) ++ goto teardown_master; ++ + dst->setup = true; + + pr_info("DSA: tree %d setup\n", dst->index); + + return 0; + ++teardown_master: ++ dsa_tree_teardown_master(dst); + teardown_switches: + dsa_tree_teardown_switches(dst); + teardown_default_cpu: +@@ -645,6 +736,8 @@ static void dsa_tree_teardown(struct dsa + if (!dst->setup) + return; + ++ dsa_tree_teardown_lags(dst); ++ + dsa_tree_teardown_master(dst); + + dsa_tree_teardown_switches(dst); +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -20,6 +20,9 @@ enum { + DSA_NOTIFIER_BRIDGE_LEAVE, + DSA_NOTIFIER_FDB_ADD, + DSA_NOTIFIER_FDB_DEL, ++ DSA_NOTIFIER_LAG_CHANGE, ++ DSA_NOTIFIER_LAG_JOIN, ++ DSA_NOTIFIER_LAG_LEAVE, + DSA_NOTIFIER_MDB_ADD, + DSA_NOTIFIER_MDB_DEL, + DSA_NOTIFIER_VLAN_ADD, +@@ -57,6 +60,15 @@ struct dsa_notifier_mdb_info { + int port; + }; + ++/* DSA_NOTIFIER_LAG_* */ ++struct dsa_notifier_lag_info { ++ struct net_device *lag; ++ int sw_index; ++ int port; ++ ++ struct netdev_lag_upper_info *info; ++}; ++ + /* DSA_NOTIFIER_VLAN_* */ + struct dsa_notifier_vlan_info { + const struct switchdev_obj_port_vlan *vlan; +@@ -149,6 +161,11 @@ void dsa_port_disable_rt(struct dsa_port + void dsa_port_disable(struct dsa_port *dp); + int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br); + void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); ++int dsa_port_lag_change(struct dsa_port *dp, ++ struct netdev_lag_lower_state_info *linfo); ++int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, ++ struct netdev_lag_upper_info *uinfo); ++void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); + int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, + struct switchdev_trans *trans); + bool dsa_port_skip_vlan_configuration(struct dsa_port *dp); +@@ -181,6 +198,71 @@ int dsa_port_link_register_of(struct dsa + void dsa_port_link_unregister_of(struct dsa_port *dp); + extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; + ++static inline bool dsa_port_offloads_netdev(struct dsa_port *dp, ++ struct net_device *dev) ++{ ++ /* Switchdev offloading can be configured on: */ ++ ++ if (dev == dp->slave) ++ /* DSA ports directly connected to a bridge, and event ++ * was emitted for the ports themselves. ++ */ ++ return true; ++ ++ if (dp->bridge_dev == dev) ++ /* DSA ports connected to a bridge, and event was emitted ++ * for the bridge. ++ */ ++ return true; ++ ++ if (dp->lag_dev == dev) ++ /* DSA ports connected to a bridge via a LAG */ ++ return true; ++ ++ return false; ++} ++ ++static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp, ++ struct net_device *dev) ++{ ++ return dsa_port_to_bridge_port(dp) == dev; ++} ++ ++static inline bool dsa_port_offloads_bridge(struct dsa_port *dp, ++ struct net_device *bridge_dev) ++{ ++ /* DSA ports connected to a bridge, and event was emitted ++ * for the bridge. ++ */ ++ return dp->bridge_dev == bridge_dev; ++} ++ ++/* Returns true if any port of this tree offloads the given net_device */ ++static inline bool dsa_tree_offloads_bridge_port(struct dsa_switch_tree *dst, ++ struct net_device *dev) ++{ ++ struct dsa_port *dp; ++ ++ list_for_each_entry(dp, &dst->ports, list) ++ if (dsa_port_offloads_bridge_port(dp, dev)) ++ return true; ++ ++ return false; ++} ++ ++/* Returns true if any port of this tree offloads the given net_device */ ++static inline bool dsa_tree_offloads_netdev(struct dsa_switch_tree *dst, ++ struct net_device *dev) ++{ ++ struct dsa_port *dp; ++ ++ list_for_each_entry(dp, &dst->ports, list) ++ if (dsa_port_offloads_netdev(dp, dev)) ++ return true; ++ ++ return false; ++} ++ + /* slave.c */ + extern const struct dsa_device_ops notag_netdev_ops; + void dsa_slave_mii_bus_init(struct dsa_switch *ds); +@@ -285,6 +367,9 @@ int dsa_switch_register_notifier(struct + void dsa_switch_unregister_notifier(struct dsa_switch *ds); + + /* dsa2.c */ ++void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag); ++void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag); ++ + extern struct list_head dsa_tree_list; + + #endif +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -193,6 +193,99 @@ void dsa_port_bridge_leave(struct dsa_po + dsa_port_set_state_now(dp, BR_STATE_FORWARDING); + } + ++int dsa_port_lag_change(struct dsa_port *dp, ++ struct netdev_lag_lower_state_info *linfo) ++{ ++ struct dsa_notifier_lag_info info = { ++ .sw_index = dp->ds->index, ++ .port = dp->index, ++ }; ++ bool tx_enabled; ++ ++ if (!dp->lag_dev) ++ return 0; ++ ++ /* On statically configured aggregates (e.g. loadbalance ++ * without LACP) ports will always be tx_enabled, even if the ++ * link is down. Thus we require both link_up and tx_enabled ++ * in order to include it in the tx set. ++ */ ++ tx_enabled = linfo->link_up && linfo->tx_enabled; ++ ++ if (tx_enabled == dp->lag_tx_enabled) ++ return 0; ++ ++ dp->lag_tx_enabled = tx_enabled; ++ ++ return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); ++} ++ ++int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, ++ struct netdev_lag_upper_info *uinfo) ++{ ++ struct dsa_notifier_lag_info info = { ++ .sw_index = dp->ds->index, ++ .port = dp->index, ++ .lag = lag, ++ .info = uinfo, ++ }; ++ struct net_device *bridge_dev; ++ int err; ++ ++ dsa_lag_map(dp->ds->dst, lag); ++ dp->lag_dev = lag; ++ ++ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); ++ if (err) ++ goto err_lag_join; ++ ++ bridge_dev = netdev_master_upper_dev_get(lag); ++ if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) ++ return 0; ++ ++ err = dsa_port_bridge_join(dp, bridge_dev); ++ if (err) ++ goto err_bridge_join; ++ ++ return 0; ++ ++err_bridge_join: ++ dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); ++err_lag_join: ++ dp->lag_dev = NULL; ++ dsa_lag_unmap(dp->ds->dst, lag); ++ return err; ++} ++ ++void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) ++{ ++ struct dsa_notifier_lag_info info = { ++ .sw_index = dp->ds->index, ++ .port = dp->index, ++ .lag = lag, ++ }; ++ int err; ++ ++ if (!dp->lag_dev) ++ return; ++ ++ /* Port might have been part of a LAG that in turn was ++ * attached to a bridge. ++ */ ++ if (dp->bridge_dev) ++ dsa_port_bridge_leave(dp, dp->bridge_dev); ++ ++ dp->lag_tx_enabled = false; ++ dp->lag_dev = NULL; ++ ++ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); ++ if (err) ++ pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n", ++ err); ++ ++ dsa_lag_unmap(dp->ds->dst, lag); ++} ++ + /* Must be called under rcu_read_lock() */ + static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, + bool vlan_filtering) +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -337,9 +337,6 @@ static int dsa_slave_vlan_add(struct net + struct switchdev_obj_port_vlan vlan; + int vid, err; + +- if (obj->orig_dev != dev) +- return -EOPNOTSUPP; +- + if (dsa_port_skip_vlan_configuration(dp)) + return 0; + +@@ -394,11 +391,13 @@ static int dsa_slave_port_obj_add(struct + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_MDB: +- if (obj->orig_dev != dev) ++ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) + return -EOPNOTSUPP; + err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); + break; + case SWITCHDEV_OBJ_ID_HOST_MDB: ++ if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; + /* DSA can directly translate this to a normal MDB add, + * but on the CPU port. + */ +@@ -406,6 +405,9 @@ static int dsa_slave_port_obj_add(struct + trans); + break; + case SWITCHDEV_OBJ_ID_PORT_VLAN: ++ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; ++ + err = dsa_slave_vlan_add(dev, obj, trans); + break; + default: +@@ -424,9 +426,6 @@ static int dsa_slave_vlan_del(struct net + struct switchdev_obj_port_vlan *vlan; + int vid, err; + +- if (obj->orig_dev != dev) +- return -EOPNOTSUPP; +- + if (dsa_port_skip_vlan_configuration(dp)) + return 0; + +@@ -453,17 +452,22 @@ static int dsa_slave_port_obj_del(struct + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_MDB: +- if (obj->orig_dev != dev) ++ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) + return -EOPNOTSUPP; + err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); + break; + case SWITCHDEV_OBJ_ID_HOST_MDB: ++ if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; + /* DSA can directly translate this to a normal MDB add, + * but on the CPU port. + */ + err = dsa_port_mdb_del(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj)); + break; + case SWITCHDEV_OBJ_ID_PORT_VLAN: ++ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) ++ return -EOPNOTSUPP; ++ + err = dsa_slave_vlan_del(dev, obj); + break; + default: +@@ -1993,6 +1997,46 @@ static int dsa_slave_changeupper(struct + dsa_port_bridge_leave(dp, info->upper_dev); + err = NOTIFY_OK; + } ++ } else if (netif_is_lag_master(info->upper_dev)) { ++ if (info->linking) { ++ err = dsa_port_lag_join(dp, info->upper_dev, ++ info->upper_info); ++ if (err == -EOPNOTSUPP) { ++ NL_SET_ERR_MSG_MOD(info->info.extack, ++ "Offloading not supported"); ++ err = 0; ++ } ++ err = notifier_from_errno(err); ++ } else { ++ dsa_port_lag_leave(dp, info->upper_dev); ++ err = NOTIFY_OK; ++ } ++ } ++ ++ return err; ++} ++ ++static int ++dsa_slave_lag_changeupper(struct net_device *dev, ++ struct netdev_notifier_changeupper_info *info) ++{ ++ struct net_device *lower; ++ struct list_head *iter; ++ int err = NOTIFY_DONE; ++ struct dsa_port *dp; ++ ++ netdev_for_each_lower_dev(dev, lower, iter) { ++ if (!dsa_slave_dev_check(lower)) ++ continue; ++ ++ dp = dsa_slave_to_port(lower); ++ if (!dp->lag_dev) ++ /* Software LAG */ ++ continue; ++ ++ err = dsa_slave_changeupper(lower, info); ++ if (notifier_to_errno(err)) ++ break; + } + + return err; +@@ -2078,10 +2122,26 @@ static int dsa_slave_netdevice_event(str + break; + } + case NETDEV_CHANGEUPPER: ++ if (dsa_slave_dev_check(dev)) ++ return dsa_slave_changeupper(dev, ptr); ++ ++ if (netif_is_lag_master(dev)) ++ return dsa_slave_lag_changeupper(dev, ptr); ++ ++ break; ++ case NETDEV_CHANGELOWERSTATE: { ++ struct netdev_notifier_changelowerstate_info *info = ptr; ++ struct dsa_port *dp; ++ int err; ++ + if (!dsa_slave_dev_check(dev)) +- return NOTIFY_DONE; ++ break; + +- return dsa_slave_changeupper(dev, ptr); ++ dp = dsa_slave_to_port(dev); ++ ++ err = dsa_port_lag_change(dp, info->lower_state_info); ++ return notifier_from_errno(err); ++ } + } + + return NOTIFY_DONE; +@@ -2229,6 +2289,15 @@ static int dsa_slave_switchdev_event(str + if (!fdb_info->added_by_user && + !dp->ds->assisted_learning_on_cpu_port) + return NOTIFY_DONE; ++ ++ /* When the bridge learns an address on an offloaded ++ * LAG we don't want to send traffic to the CPU, the ++ * other ports bridged with the LAG should be able to ++ * autonomously forward towards it. ++ */ ++ if (dsa_tree_offloads_netdev(dp->ds->dst, dev)) ++ return NOTIFY_DONE; ++ + } + + if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del) +--- a/net/dsa/switch.c ++++ b/net/dsa/switch.c +@@ -193,6 +193,47 @@ static int dsa_switch_fdb_del(struct dsa + return ds->ops->port_fdb_del(ds, port, info->addr, info->vid); + } + ++static int dsa_switch_lag_change(struct dsa_switch *ds, ++ struct dsa_notifier_lag_info *info) ++{ ++ if (ds->index == info->sw_index && ds->ops->port_lag_change) ++ return ds->ops->port_lag_change(ds, info->port); ++ ++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) ++ return ds->ops->crosschip_lag_change(ds, info->sw_index, ++ info->port); ++ ++ return 0; ++} ++ ++static int dsa_switch_lag_join(struct dsa_switch *ds, ++ struct dsa_notifier_lag_info *info) ++{ ++ if (ds->index == info->sw_index && ds->ops->port_lag_join) ++ return ds->ops->port_lag_join(ds, info->port, info->lag, ++ info->info); ++ ++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) ++ return ds->ops->crosschip_lag_join(ds, info->sw_index, ++ info->port, info->lag, ++ info->info); ++ ++ return -EOPNOTSUPP; ++} ++ ++static int dsa_switch_lag_leave(struct dsa_switch *ds, ++ struct dsa_notifier_lag_info *info) ++{ ++ if (ds->index == info->sw_index && ds->ops->port_lag_leave) ++ return ds->ops->port_lag_leave(ds, info->port, info->lag); ++ ++ if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) ++ return ds->ops->crosschip_lag_leave(ds, info->sw_index, ++ info->port, info->lag); ++ ++ return -EOPNOTSUPP; ++} ++ + static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port, + struct dsa_notifier_mdb_info *info) + { +@@ -340,6 +381,15 @@ static int dsa_switch_event(struct notif + case DSA_NOTIFIER_FDB_DEL: + err = dsa_switch_fdb_del(ds, info); + break; ++ case DSA_NOTIFIER_LAG_CHANGE: ++ err = dsa_switch_lag_change(ds, info); ++ break; ++ case DSA_NOTIFIER_LAG_JOIN: ++ err = dsa_switch_lag_join(ds, info); ++ break; ++ case DSA_NOTIFIER_LAG_LEAVE: ++ err = dsa_switch_lag_leave(ds, info); ++ break; + case DSA_NOTIFIER_MDB_ADD: + err = dsa_switch_mdb_add(ds, info); + break; +--- a/net/dsa/tag_dsa.c ++++ b/net/dsa/tag_dsa.c +@@ -82,7 +82,19 @@ static struct sk_buff *dsa_rcv(struct sk + source_device = dsa_header[0] & 0x1f; + source_port = (dsa_header[1] >> 3) & 0x1f; + +- skb->dev = dsa_master_find_slave(dev, source_device, source_port); ++ if (trunk) { ++ struct dsa_port *cpu_dp = dev->dsa_ptr; ++ ++ /* The exact source port is not available in the tag, ++ * so we inject the frame directly on the upper ++ * team/bond. ++ */ ++ skb->dev = dsa_lag_dev(cpu_dp->dst, source_port); ++ } else { ++ skb->dev = dsa_master_find_slave(dev, source_device, ++ source_port); ++ } ++ + if (!skb->dev) + return NULL; + diff --git a/target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch b/target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch new file mode 100644 index 00000000000..248abee39be --- /dev/null +++ b/target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch @@ -0,0 +1,26 @@ +From a381ac0aa281fdb0b41a39d8a2bc08fd88f6db92 Mon Sep 17 00:00:00 2001 +From: Antoine Tenart +Date: Tue, 25 Feb 2020 16:32:37 +0100 +Subject: [PATCH 1/3] net: phy: sfp: re-probe modules on DEV_UP event + +Signed-off-by: Antoine Tenart +--- + drivers/net/phy/sfp.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -1966,6 +1966,13 @@ static void sfp_sm_module(struct sfp *sf + return; + } + ++ /* Re-probe the SFP modules when an interface is brought up, as the MAC ++ * do not report its link status (This means Phylink wouldn't be ++ * triggered if the PHY had a link before a MAC is brought up). ++ */ ++ if (event == SFP_E_DEV_UP && sfp->sm_mod_state == SFP_MOD_PRESENT) ++ sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL); ++ + switch (sfp->sm_mod_state) { + default: + if (event == SFP_E_INSERT) { diff --git a/target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch b/target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch new file mode 100644 index 00000000000..5e1053ca6ca --- /dev/null +++ b/target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch @@ -0,0 +1,168 @@ +From d585c55b9f70cf9e8c66820d7efe7130c683f19e Mon Sep 17 00:00:00 2001 +From: Antoine Tenart +Date: Fri, 21 Feb 2020 11:51:27 +0100 +Subject: [PATCH 2/3] net: phy: add an MDIO SMBus library + +Signed-off-by: Antoine Tenart +--- + drivers/net/mdio/Kconfig | 11 +++++++ + drivers/net/mdio/Makefile | 1 + + drivers/net/mdio/mdio-smbus.c | 62 +++++++++++++++++++++++++++++++++++ + drivers/net/phy/Kconfig | 1 + + include/linux/mdio/mdio-i2c.h | 16 +++++++++ + 5 files changed, 91 insertions(+) + create mode 100644 drivers/net/mdio/mdio-smbus.c + +--- a/drivers/net/mdio/Kconfig ++++ b/drivers/net/mdio/Kconfig +@@ -40,6 +40,17 @@ config MDIO_SUN4I + interface units of the Allwinner SoC that have an EMAC (A10, + A12, A10s, etc.) + ++config MDIO_SMBUS ++ tristate ++ depends on I2C_SMBUS ++ help ++ Support SMBus based PHYs. This provides a MDIO bus bridged ++ to SMBus to allow PHYs connected in SMBus mode to be accessed ++ using the existing infrastructure. ++ ++ This is library mode. ++ ++ + config MDIO_XGENE + tristate "APM X-Gene SoC MDIO bus controller" + depends on ARCH_XGENE || COMPILE_TEST +--- a/drivers/net/mdio/Makefile ++++ b/drivers/net/mdio/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxar + obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o + obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o + obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o ++obj-$(CONFIG_MDIO_SMBUS) += mdio-smbus.o + obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o + obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o + obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +--- /dev/null ++++ b/drivers/net/mdio/mdio-smbus.c +@@ -0,0 +1,62 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * MDIO SMBus bridge ++ * ++ * Copyright (C) 2020 Antoine Tenart ++ * ++ * Network PHYs can appear on SMBus when they are part of SFP modules. ++ */ ++#include ++#include ++#include ++ ++static int smbus_mii_read(struct mii_bus *mii, int phy_id, int reg) ++{ ++ struct i2c_adapter *i2c = mii->priv; ++ union i2c_smbus_data data; ++ int ret; ++ ++ ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0, I2C_SMBUS_READ, ++ reg, I2C_SMBUS_BYTE_DATA, &data); ++ if (ret < 0) ++ return 0xff; ++ ++ return data.byte; ++} ++ ++static int smbus_mii_write(struct mii_bus *mii, int phy_id, int reg, u16 val) ++{ ++ struct i2c_adapter *i2c = mii->priv; ++ union i2c_smbus_data data; ++ int ret; ++ ++ data.byte = val; ++ ++ ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0, I2C_SMBUS_WRITE, ++ reg, I2C_SMBUS_BYTE_DATA, &data); ++ return ret < 0 ? ret : 0; ++} ++ ++struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c) ++{ ++ struct mii_bus *mii; ++ ++ if (!i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return ERR_PTR(-EINVAL); ++ ++ mii = mdiobus_alloc(); ++ if (!mii) ++ return ERR_PTR(-ENOMEM); ++ ++ snprintf(mii->id, MII_BUS_ID_SIZE, "smbus:%s", dev_name(parent)); ++ mii->parent = parent; ++ mii->read = smbus_mii_read; ++ mii->write = smbus_mii_write; ++ mii->priv = i2c; ++ ++ return mii; ++} ++ ++MODULE_AUTHOR("Antoine Tenart"); ++MODULE_DESCRIPTION("MDIO SMBus bridge library"); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -60,6 +60,7 @@ config SFP + depends on I2C && PHYLINK + depends on HWMON || HWMON=n + select MDIO_I2C ++ select MDIO_SMBUS + + comment "Switch configuration API + drivers" + +--- a/include/linux/mdio/mdio-i2c.h ++++ b/include/linux/mdio/mdio-i2c.h +@@ -12,5 +12,21 @@ struct i2c_adapter; + struct mii_bus; + + struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c); ++struct mii_bus *mdio_smbus_alloc(struct device *parent, struct i2c_adapter *i2c); ++ ++/* ++ * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is ++ * specified to be present in SFP modules. These correspond with PHY ++ * addresses 16 and 17. Disallow access to these "phy" addresses. ++ */ ++static bool i2c_mii_valid_phy_id(int phy_id) ++{ ++ return phy_id != 0x10 && phy_id != 0x11; ++} ++ ++static unsigned int i2c_mii_phy_addr(int phy_id) ++{ ++ return phy_id + 0x40; ++} + + #endif +--- a/drivers/net/mdio/mdio-i2c.c ++++ b/drivers/net/mdio/mdio-i2c.c +@@ -13,21 +13,6 @@ + #include + #include + +-/* +- * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is +- * specified to be present in SFP modules. These correspond with PHY +- * addresses 16 and 17. Disallow access to these "phy" addresses. +- */ +-static bool i2c_mii_valid_phy_id(int phy_id) +-{ +- return phy_id != 0x10 && phy_id != 0x11; +-} +- +-static unsigned int i2c_mii_phy_addr(int phy_id) +-{ +- return phy_id + 0x40; +-} +- + static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) + { + struct i2c_adapter *i2c = bus->priv; diff --git a/target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch b/target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch new file mode 100644 index 00000000000..c4bacc0ea9f --- /dev/null +++ b/target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch @@ -0,0 +1,99 @@ +From 3cb0bde365d913c484d20224367a54a0eac780a7 Mon Sep 17 00:00:00 2001 +From: Antoine Tenart +Date: Fri, 21 Feb 2020 11:55:29 +0100 +Subject: [PATCH 3/3] net: phy: sfp: add support for SMBus + +Signed-off-by: Antoine Tenart +--- + drivers/net/phy/sfp.c | 68 ++++++++++++++++++++++++++++++++++--------- + 1 file changed, 54 insertions(+), 14 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -413,32 +413,72 @@ static int sfp_i2c_write(struct sfp *sfp + return ret == ARRAY_SIZE(msgs) ? len : 0; + } + ++static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, ++ size_t len) ++{ ++ u8 bus_addr = a2 ? 0x51 : 0x50, *val = buf; ++ ++ bus_addr -= 0x40; ++ ++ while (len > 0) { ++ *val = sfp->i2c_mii->read(sfp->i2c_mii, bus_addr, dev_addr); ++ ++ val++; ++ dev_addr++; ++ len--; ++ } ++ ++ return val - (u8 *)buf; ++} ++ ++static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, ++ size_t len) ++{ ++ u8 bus_addr = a2 ? 0x51 : 0x50; ++ u16 val; ++ ++ memcpy(&val, buf, len); ++ ++ return sfp->i2c_mii->write(sfp->i2c_mii, bus_addr, dev_addr, val); ++} ++ + static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) + { +- struct mii_bus *i2c_mii; ++ struct mii_bus *mii; + int ret; + +- if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) +- return -EINVAL; +- + sfp->i2c = i2c; +- sfp->read = sfp_i2c_read; +- sfp->write = sfp_i2c_write; + +- i2c_mii = mdio_i2c_alloc(sfp->dev, i2c); +- if (IS_ERR(i2c_mii)) +- return PTR_ERR(i2c_mii); ++ if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) { ++ sfp->read = sfp_i2c_read; ++ sfp->write = sfp_i2c_write; ++ ++ mii = mdio_i2c_alloc(sfp->dev, i2c); ++ if (IS_ERR(mii)) ++ return PTR_ERR(mii); ++ ++ mii->name = "SFP I2C Bus"; ++ } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ sfp->read = sfp_smbus_read; ++ sfp->write = sfp_smbus_write; ++ ++ mii = mdio_smbus_alloc(sfp->dev, i2c); ++ if (IS_ERR(mii)) ++ return PTR_ERR(mii); + +- i2c_mii->name = "SFP I2C Bus"; +- i2c_mii->phy_mask = ~0; ++ mii->name = "SFP SMBus"; ++ } else { ++ return -EINVAL; ++ } + +- ret = mdiobus_register(i2c_mii); ++ mii->phy_mask = ~0; ++ ret = mdiobus_register(mii); + if (ret < 0) { +- mdiobus_free(i2c_mii); ++ mdiobus_free(mii); + return ret; + } + +- sfp->i2c_mii = i2c_mii; ++ sfp->i2c_mii = mii; + + return 0; + } diff --git a/target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch b/target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch new file mode 100644 index 00000000000..a3bfec59ab7 --- /dev/null +++ b/target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch @@ -0,0 +1,148 @@ +From: Vladimir Oltean +Date: Fri, 12 Feb 2021 17:15:54 +0200 +Subject: [PATCH] net: dsa: configure better brport flags when ports leave the + bridge + +Bugfixed version of upstream commit 5e38c15856e9 ("net: dsa: configure +better brport flags when ports leave the bridge") + +For a DSA switch port operating in standalone mode, address learning +doesn't make much sense since that is a bridge function. In fact, +address learning even breaks setups such as this one: + + +---------------------------------------------+ + | | + | +-------------------+ | + | | br0 | send receive | + | +--------+-+--------+ +--------+ +--------+ | + | | | | | | | | | | + | | swp0 | | swp1 | | swp2 | | swp3 | | + | | | | | | | | | | + +-+--------+-+--------+-+--------+-+--------+-+ + | ^ | ^ + | | | | + | +-----------+ | + | | + +--------------------------------+ + +because if the switch has a single FDB (can offload a single bridge) +then source address learning on swp3 can "steal" the source MAC address +of swp2 from br0's FDB, because learning frames coming from swp2 will be +done twice: first on the swp1 ingress port, second on the swp3 ingress +port. So the hardware FDB will become out of sync with the software +bridge, and when swp2 tries to send one more packet towards swp1, the +ASIC will attempt to short-circuit the forwarding path and send it +directly to swp3 (since that's the last port it learned that address on), +which it obviously can't, because swp3 operates in standalone mode. + +So DSA drivers operating in standalone mode should still configure a +list of bridge port flags even when they are standalone. Currently DSA +attempts to call dsa_port_bridge_flags with 0, which disables egress +flooding of unknown unicast and multicast, something which doesn't make +much sense. For the switches that implement .port_egress_floods - b53 +and mv88e6xxx, it probably doesn't matter too much either, since they +can possibly inject traffic from the CPU into a standalone port, +regardless of MAC DA, even if egress flooding is turned off for that +port, but certainly not all DSA switches can do that - sja1105, for +example, can't. So it makes sense to use a better common default there, +such as "flood everything". + +It should also be noted that what DSA calls "dsa_port_bridge_flags()" +is a degenerate name for just calling .port_egress_floods(), since +nothing else is implemented - not learning, in particular. But disabling +address learning, something that this driver is also coding up for, will +be supported by individual drivers once .port_egress_floods is replaced +with a more generic .port_bridge_flags. + +Previous attempts to code up this logic have been in the common bridge +layer, but as pointed out by Ido Schimmel, there are corner cases that +are missed when doing that: +https://patchwork.kernel.org/project/netdevbpf/patch/20210209151936.97382-5-olteanv@gmail.com/ + +So, at least for now, let's leave DSA in charge of setting port flags +before and after the bridge join and leave. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +[ backport and bugfix: break dsa_port_bridge_flags() out of loop ] +Signed-off-by: Bjørn Mork +--- + net/dsa/port.c | 45 ++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 38 insertions(+), 7 deletions(-) + +--- a/net/dsa/port.c ++++ b/net/dsa/port.c +@@ -134,6 +134,27 @@ void dsa_port_disable(struct dsa_port *d + rtnl_unlock(); + } + ++static void dsa_port_change_brport_flags(struct dsa_port *dp, ++ bool bridge_offload) ++{ ++ unsigned long mask, flags; ++ int flag, err; ++ ++ mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; ++ if (bridge_offload) ++ flags = mask; ++ else ++ flags = mask & ~BR_LEARNING; ++ ++ for_each_set_bit(flag, &mask, 32) { ++ err = dsa_port_pre_bridge_flags(dp, BIT(flag), NULL, NULL); ++ if (err) ++ flags &= ~BIT(flag); ++ } ++ ++ dsa_port_bridge_flags(dp, flags, NULL, NULL); ++} ++ + int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) + { + struct dsa_notifier_bridge_info info = { +@@ -144,10 +165,10 @@ int dsa_port_bridge_join(struct dsa_port + }; + int err; + +- /* Set the flooding mode before joining the port in the switch */ +- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL); +- if (err) +- return err; ++ /* Notify the port driver to set its configurable flags in a way that ++ * matches the initial settings of a bridge port. ++ */ ++ dsa_port_change_brport_flags(dp, true); + + /* Here the interface is already bridged. Reflect the current + * configuration so that drivers can program their chips accordingly. +@@ -158,7 +179,7 @@ int dsa_port_bridge_join(struct dsa_port + + /* The bridging is rolled back on error */ + if (err) { +- dsa_port_bridge_flags(dp, 0, NULL, NULL); ++ dsa_port_change_brport_flags(dp, false); + dp->bridge_dev = NULL; + } + +@@ -184,8 +205,18 @@ void dsa_port_bridge_leave(struct dsa_po + if (err) + pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); + +- /* Port is leaving the bridge, disable flooding */ +- dsa_port_bridge_flags(dp, 0, NULL, NULL); ++ /* Configure the port for standalone mode (no address learning, ++ * flood everything). ++ * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events ++ * when the user requests it through netlink or sysfs, but not ++ * automatically at port join or leave, so we need to handle resetting ++ * the brport flags ourselves. But we even prefer it that way, because ++ * otherwise, some setups might never get the notification they need, ++ * for example, when a port leaves a LAG that offloads the bridge, ++ * it becomes standalone, but as far as the bridge is concerned, no ++ * port ever left. ++ */ ++ dsa_port_change_brport_flags(dp, false); + + /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, + * so allow it to be in BR_STATE_FORWARDING to be kept functional diff --git a/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch b/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch new file mode 100644 index 00000000000..eeb26fa1994 --- /dev/null +++ b/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch @@ -0,0 +1,840 @@ +From 5d84f16b0036b33487b94abef15ad3c224c81ee9 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Thu, 3 Feb 2022 16:38:50 +0000 +Subject: [PATCH] net: mdio: support hardware-assisted indirect access + +MDIO controllers found in Switch-SoCs can offload some MDIO operations +to the hardware: + * MMD register access via Clause-22 + Instead of using multiple operations to access MMD registers via + MII register MII_MMD_CTRL and MII_MMD_DATA some controllers + allow transparent access to MMD PHY registers. + + * paged MII register access + Some PHYs (namely RealTek and Vitesse) use vendor-defined MII + register 0x1f for paged access. Some MDIO host controllers support + transparent paged access when used with such PHYs. + + * add convenience accessors to fully support paged access also on + multi-PHY packages (like the embedded PHYs in RTL83xx): + phy_package_read_paged and phy_package_write_paged + phy_package_port_read and phy_package_port_write + phy_package_port_read_paged and phy_package_port_write_paged + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/mdio_bus.c | 335 ++++++++++++++++++++++++++++++++++++- + drivers/net/phy/phy-core.c | 66 +++++++- + include/linux/mdio.h | 59 +++++++ + include/linux/phy.h | 129 ++++++++++++++ + include/uapi/linux/mii.h | 1 + + 5 files changed, 580 insertions(+), 10 deletions(-) + +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -734,6 +734,32 @@ out: + } + + /** ++ * __mdiobus_select_page - Unlocked version of the mdiobus_select_page function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: register page to select ++ * ++ * Selects a MDIO bus register page. Caller must hold the mdio bus lock. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page) ++{ ++ lockdep_assert_held_once(&bus->mdio_lock); ++ ++ if (bus->selected_page[addr] == page) ++ return 0; ++ ++ bus->selected_page[addr] = page; ++ if (bus->read_paged) ++ return 0; ++ ++ return bus->write(bus, addr, MII_MAINPAGE, page); ++ ++} ++EXPORT_SYMBOL(__mdiobus_select_page); ++ ++/** + * __mdiobus_read - Unlocked version of the mdiobus_read function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -749,7 +775,10 @@ int __mdiobus_read(struct mii_bus *bus, + + WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock)); + +- retval = bus->read(bus, addr, regnum); ++ if (bus->read_paged) ++ retval = bus->read_paged(bus, addr, bus->selected_page[addr], regnum); ++ else ++ retval = bus->read(bus, addr, regnum); + + trace_mdio_access(bus, 1, addr, regnum, retval, retval); + mdiobus_stats_acct(&bus->stats[addr], true, retval); +@@ -759,6 +788,40 @@ int __mdiobus_read(struct mii_bus *bus, + EXPORT_SYMBOL(__mdiobus_read); + + /** ++ * __mdiobus_read_paged - Unlocked version of the mdiobus_read_paged function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: the register page to access ++ * @regnum: register number to read ++ * ++ * Read a MDIO bus register. Caller must hold the mdio bus lock. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum) ++{ ++ int retval; ++ int oldpage; ++ ++ lockdep_assert_held_once(&bus->mdio_lock); ++ ++ if (bus->read_paged) { ++ retval = bus->read_paged(bus, addr, page, regnum); ++ } else { ++ oldpage = bus->selected_page[addr]; ++ __mdiobus_select_page(bus, addr, page); ++ retval = bus->read(bus, addr, regnum); ++ __mdiobus_select_page(bus, addr, oldpage); ++ } ++ ++ trace_mdio_access(bus, 1, addr, regnum, retval, retval); ++ mdiobus_stats_acct(&bus->stats[addr], true, retval); ++ ++ return retval; ++} ++EXPORT_SYMBOL(__mdiobus_read_paged); ++ ++/** + * __mdiobus_write - Unlocked version of the mdiobus_write function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -775,7 +838,10 @@ int __mdiobus_write(struct mii_bus *bus, + + WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock)); + +- err = bus->write(bus, addr, regnum, val); ++ if (bus->write_paged) ++ err = bus->write_paged(bus, addr, bus->selected_page[addr], regnum, val); ++ else ++ err = bus->write(bus, addr, regnum, val); + + trace_mdio_access(bus, 0, addr, regnum, val, err); + mdiobus_stats_acct(&bus->stats[addr], false, err); +@@ -785,6 +851,39 @@ int __mdiobus_write(struct mii_bus *bus, + EXPORT_SYMBOL(__mdiobus_write); + + /** ++ * __mdiobus_write_paged - Unlocked version of the mdiobus_write_paged function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: the register page to access ++ * @regnum: register number to write ++ * @val: value to write to @regnum ++ * ++ * Write a MDIO bus register. Caller must hold the mdio bus lock. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val) ++{ ++ int err, oldpage; ++ ++ lockdep_assert_held_once(&bus->mdio_lock); ++ ++ if (bus->write_paged) { ++ err = bus->write_paged(bus, addr, page, regnum, val); ++ } else { ++ oldpage = bus->selected_page[addr]; ++ __mdiobus_select_page(bus, addr, page); ++ err = bus->write(bus, addr, regnum, val); ++ __mdiobus_select_page(bus, addr, oldpage); ++ } ++ trace_mdio_access(bus, 0, addr, regnum, val, err); ++ mdiobus_stats_acct(&bus->stats[addr], false, err); ++ return err; ++} ++EXPORT_SYMBOL(__mdiobus_write_paged); ++ ++ ++/** + * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -817,6 +916,43 @@ int __mdiobus_modify_changed(struct mii_ + EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); + + /** ++ * __mdiobus_modify_changed_paged - Unlocked version of the mdiobus_modify_paged function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @regnum: register number to modify ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ * ++ * Read, modify, and if any change, write the register value back to the ++ * device. Any error returns a negative number. ++ * ++ * NOTE: MUST NOT be called from interrupt context. ++ */ ++int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page, ++ u16 mask, u16 set) ++{ ++ int new, ret, oldpage; ++ ++ oldpage = bus->selected_page[addr]; ++ __mdiobus_select_page(bus, addr, page); ++ ++ ret = __mdiobus_read_paged(bus, addr, page, regnum); ++ if (ret < 0) ++ return ret; ++ ++ new = (ret & ~mask) | set; ++ if (new == ret) ++ return 0; ++ ++ ret = __mdiobus_write_paged(bus, addr, page, regnum, new); ++ ++ __mdiobus_select_page(bus, addr, oldpage); ++ ++ return ret < 0 ? ret : 1; ++} ++EXPORT_SYMBOL_GPL(__mdiobus_modify_changed_paged); ++ ++/** + * mdiobus_read_nested - Nested version of the mdiobus_read function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -842,6 +978,79 @@ int mdiobus_read_nested(struct mii_bus * + EXPORT_SYMBOL(mdiobus_read_nested); + + /** ++ * mdiobus_select_page_nested - Nested version of the mdiobus_select_page function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: register page to access ++ * ++ * In case of nested MDIO bus access avoid lockdep false positives by ++ * using mutex_lock_nested(). ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page) ++{ ++ int retval; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ retval = __mdiobus_select_page(bus, addr, page); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return retval; ++} ++EXPORT_SYMBOL(mdiobus_select_page_nested); ++ ++/** ++ * mdiobus_read_paged_nested - Nested version of the mdiobus_read_paged function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: register page to access ++ * @regnum: register number to read ++ * ++ * In case of nested MDIO bus access avoid lockdep false positives by ++ * using mutex_lock_nested(). ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_read_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum) ++{ ++ int retval; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ retval = __mdiobus_read_paged(bus, addr, page, regnum); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return retval; ++} ++EXPORT_SYMBOL(mdiobus_read_paged_nested); ++ ++/** ++ * mdiobus_select_page - Convenience function for setting the MII register page ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: the register page to set ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page) ++{ ++ int retval; ++ ++ mutex_lock(&bus->mdio_lock); ++ retval = __mdiobus_select_page(bus, addr, page); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return retval; ++} ++EXPORT_SYMBOL(mdiobus_select_page); ++ ++/** + * mdiobus_read - Convenience function for reading a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address +@@ -864,6 +1073,29 @@ int mdiobus_read(struct mii_bus *bus, in + EXPORT_SYMBOL(mdiobus_read); + + /** ++ * mdiobus_read_paged - Convenience function for reading a given paged MII mgmt register ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: register page to access ++ * @regnum: register number to read ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum) ++{ ++ int retval; ++ ++ mutex_lock(&bus->mdio_lock); ++ retval = __mdiobus_read_paged(bus, addr, page, regnum); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return retval; ++} ++EXPORT_SYMBOL(mdiobus_read_paged); ++ ++/** + * mdiobus_write_nested - Nested version of the mdiobus_write function + * @bus: the mii_bus struct + * @addr: the phy address +@@ -890,6 +1122,33 @@ int mdiobus_write_nested(struct mii_bus + EXPORT_SYMBOL(mdiobus_write_nested); + + /** ++ * mdiobus_write_paged_nested - Nested version of the mdiobus_write_aged function ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: the register page to access ++ * @regnum: register number to write ++ * @val: value to write to @regnum ++ * ++ * In case of nested MDIO bus access avoid lockdep false positives by ++ * using mutex_lock_nested(). ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_write_paged_nested(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val) ++{ ++ int err; ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ err = __mdiobus_write_paged(bus, addr, page, regnum, val); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err; ++} ++EXPORT_SYMBOL(mdiobus_write_paged_nested); ++ ++/** + * mdiobus_write - Convenience function for writing a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address +@@ -913,6 +1172,30 @@ int mdiobus_write(struct mii_bus *bus, i + EXPORT_SYMBOL(mdiobus_write); + + /** ++ * mdiobus_write_paged - Convenience function for writing a given paged MII mgmt register ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: the register page to access ++ * @regnum: register number to write ++ * @val: value to write to @regnum ++ * ++ * NOTE: MUST NOT be called from interrupt context, ++ * because the bus read/write functions may wait for an interrupt ++ * to conclude the operation. ++ */ ++int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val) ++{ ++ int err; ++ ++ mutex_lock(&bus->mdio_lock); ++ err = __mdiobus_write_paged(bus, addr, page, regnum, val); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err; ++} ++EXPORT_SYMBOL(mdiobus_write_paged); ++ ++/** + * mdiobus_modify - Convenience function for modifying a given mdio device + * register + * @bus: the mii_bus struct +@@ -934,6 +1217,51 @@ int mdiobus_modify(struct mii_bus *bus, + EXPORT_SYMBOL_GPL(mdiobus_modify); + + /** ++ * mdiobus_modify_paged - Convenience function for modifying a given mdio device ++ * register ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: the register page to access ++ * @regnum: register number to write ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ */ ++int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask, u16 set) ++{ ++ int err; ++ ++ mutex_lock(&bus->mdio_lock); ++ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err < 0 ? err : 0; ++} ++EXPORT_SYMBOL_GPL(mdiobus_modify_paged); ++ ++/** ++ * mdiobus_modify_changed_paged - Convenience function for modifying a given paged ++ * mdio device register and returning if it changed ++ * @bus: the mii_bus struct ++ * @addr: the phy address ++ * @page: the register page to access ++ * @regnum: register number to write ++ * @mask: bit mask of bits to clear ++ * @set: bit mask of bits to set ++ */ ++int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, ++ u16 mask, u16 set) ++{ ++ int err; ++ ++ mutex_lock(&bus->mdio_lock); ++ err = __mdiobus_modify_changed_paged(bus, addr, page, regnum, mask, set); ++ mutex_unlock(&bus->mdio_lock); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(mdiobus_modify_changed_paged); ++ ++/** + * mdio_bus_match - determine if given MDIO driver supports the given + * MDIO device + * @dev: target MDIO device +--- a/drivers/net/phy/phy-core.c ++++ b/drivers/net/phy/phy-core.c +@@ -481,10 +481,16 @@ int __phy_read_mmd(struct phy_device *ph + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; + +- mmd_phy_indirect(bus, phy_addr, devad, regnum); +- +- /* Read the content of the MMD's selected register */ +- val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); ++ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) { ++ val = __mdiobus_c22_mmd_read(phydev->mdio.bus, ++ phydev->mdio.addr, ++ devad, regnum); ++ } else { ++ mmd_phy_indirect(bus, phy_addr, devad, regnum); ++ ++ /* Read the content of the MMD's selected register */ ++ val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); ++ } + } + return val; + } +@@ -537,12 +543,18 @@ int __phy_write_mmd(struct phy_device *p + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; + +- mmd_phy_indirect(bus, phy_addr, devad, regnum); ++ if (bus->access_capabilities & MDIOBUS_ACCESS_C22_MMD) { ++ ret = __mdiobus_c22_mmd_write(phydev->mdio.bus, ++ phydev->mdio.addr, ++ devad, regnum, val); ++ } else { ++ mmd_phy_indirect(bus, phy_addr, devad, regnum); + +- /* Write the data into MMD's selected register */ +- __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); ++ /* Write the data into MMD's selected register */ ++ __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); + +- ret = 0; ++ ret = 0; ++ } + } + return ret; + } +@@ -748,6 +760,13 @@ EXPORT_SYMBOL_GPL(phy_modify_mmd); + + static int __phy_read_page(struct phy_device *phydev) + { ++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { ++ struct mii_bus *bus = phydev->mdio.bus; ++ int phy_addr = phydev->mdio.addr; ++ ++ return bus->selected_page[phy_addr]; ++ } ++ + if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n")) + return -EOPNOTSUPP; + +@@ -756,6 +775,13 @@ static int __phy_read_page(struct phy_de + + static int __phy_write_page(struct phy_device *phydev, int page) + { ++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { ++ struct mii_bus *bus = phydev->mdio.bus; ++ int phy_addr = phydev->mdio.addr; ++ ++ return __mdiobus_select_page(bus, phy_addr, page); ++ } ++ + if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n")) + return -EOPNOTSUPP; + +@@ -857,6 +883,18 @@ int phy_read_paged(struct phy_device *ph + { + int ret = 0, oldpage; + ++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { ++ struct mii_bus *bus = phydev->mdio.bus; ++ int phy_addr = phydev->mdio.addr; ++ ++ if (bus->read_paged) { ++ phy_lock_mdio_bus(phydev); ++ ret = bus->read_paged(bus, phy_addr, page, regnum); ++ phy_unlock_mdio_bus(phydev); ++ return ret; ++ } ++ } ++ + oldpage = phy_select_page(phydev, page); + if (oldpage >= 0) + ret = __phy_read(phydev, regnum); +@@ -878,6 +916,18 @@ int phy_write_paged(struct phy_device *p + { + int ret = 0, oldpage; + ++ if (phydev->drv && phydev->drv->flags & PHY_HAS_REALTEK_PAGES) { ++ struct mii_bus *bus = phydev->mdio.bus; ++ int phy_addr = phydev->mdio.addr; ++ ++ if (bus->write_paged) { ++ phy_lock_mdio_bus(phydev); ++ ret = bus->write_paged(bus, phy_addr, page, regnum, val); ++ phy_unlock_mdio_bus(phydev); ++ return ret; ++ } ++ } ++ + oldpage = phy_select_page(phydev, page); + if (oldpage >= 0) + ret = __phy_write(phydev, regnum, val); +--- a/include/linux/mdio.h ++++ b/include/linux/mdio.h +@@ -14,6 +14,7 @@ + * IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. + */ + #define MII_ADDR_C45 (1<<30) ++#define MII_ADDR_C22_MMD (1<<29) + #define MII_DEVADDR_C45_SHIFT 16 + #define MII_DEVADDR_C45_MASK GENMASK(20, 16) + #define MII_REGADDR_C45_MASK GENMASK(15, 0) +@@ -327,11 +328,19 @@ static inline void mii_10gbt_stat_mod_li + advertising, lpa & MDIO_AN_10GBT_STAT_LP10G); + } + ++int __mdiobus_select_page(struct mii_bus *bus, int addr, u16 page); + int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); + int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); + int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum, + u16 mask, u16 set); + ++int __mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum); ++int __mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val); ++int __mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u32 regnum, u16 page, ++ u16 mask, u16 set); ++ ++int mdiobus_select_page(struct mii_bus *bus, int addr, u16 page); ++int mdiobus_select_page_nested(struct mii_bus *bus, int addr, u16 page); + int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); + int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum); + int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); +@@ -339,11 +348,51 @@ int mdiobus_write_nested(struct mii_bus + int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, + u16 set); + ++int mdiobus_read_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum); ++int mdiobus_read_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum); ++int mdiobus_write_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val); ++int mdiobus_write_nested_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 val); ++int mdiobus_modify_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, u16 mask, ++ u16 set); ++int mdiobus_modify_changed_paged(struct mii_bus *bus, int addr, u16 page, u32 regnum, ++ u16 mask, u16 set); ++ ++static inline int mdiodev_read_paged(struct mdio_device *mdiodev, u16 page, ++ u32 regnum) ++{ ++ return mdiobus_read_paged(mdiodev->bus, mdiodev->addr, page, regnum); ++} ++ ++static inline int mdiodev_write_paged(struct mdio_device *mdiodev, u16 page, ++ u32 regnum, u16 val) ++{ ++ return mdiobus_write_paged(mdiodev->bus, mdiodev->addr, page, regnum, val); ++} ++ ++static inline int mdiodev_modify_paged(struct mdio_device *mdiodev, u16 page, ++ u32 regnum, u16 mask, u16 set) ++{ ++ return mdiobus_modify_paged(mdiodev->bus, mdiodev->addr, page, regnum, ++ mask, set); ++} ++ ++static inline int mdiodev_modify_changed_paged(struct mdio_device *mdiodev, u16 page, ++ u32 regnum, u16 mask, u16 set) ++{ ++ return mdiobus_modify_changed_paged(mdiodev->bus, mdiodev->addr, page, regnum, ++ mask, set); ++} ++ + static inline u32 mdiobus_c45_addr(int devad, u16 regnum) + { + return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum; + } + ++static inline u32 mdiobus_c22_mmd_addr(int devad, u16 regnum) ++{ ++ return MII_ADDR_C22_MMD | devad << MII_DEVADDR_C45_SHIFT | regnum; ++} ++ + static inline u16 mdiobus_c45_regad(u32 regnum) + { + return FIELD_GET(MII_REGADDR_C45_MASK, regnum); +@@ -367,6 +416,19 @@ static inline int __mdiobus_c45_write(st + val); + } + ++static inline int __mdiobus_c22_mmd_read(struct mii_bus *bus, int prtad, ++ int devad, u16 regnum) ++{ ++ return __mdiobus_read(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum)); ++} ++ ++static inline int __mdiobus_c22_mmd_write(struct mii_bus *bus, int prtad, ++ int devad, u16 regnum, u16 val) ++{ ++ return __mdiobus_write(bus, prtad, mdiobus_c22_mmd_addr(devad, regnum), ++ val); ++} ++ + static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad, + u16 regnum) + { +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -80,6 +80,7 @@ extern const int phy_10gbit_features_arr + #define PHY_IS_INTERNAL 0x00000001 + #define PHY_RST_AFTER_CLK_EN 0x00000002 + #define PHY_POLL_CABLE_TEST 0x00000004 ++#define PHY_HAS_REALTEK_PAGES 0x00000010 + #define MDIO_DEVICE_IS_PHY 0x80000000 + + /** +@@ -374,6 +375,22 @@ struct mii_bus { + + /** @shared: shared state across different PHYs */ + struct phy_package_shared *shared[PHY_MAX_ADDR]; ++ ++ /** @access_capabilities: hardware-assisted access capabilties */ ++ enum { ++ MDIOBUS_ACCESS_SOFTWARE_ONLY = 0, ++ MDIOBUS_ACCESS_C22_MMD = 0x1, ++ } access_capabilities; ++ ++ /** @read: Perform a read transfer on the bus, offloading page access */ ++ int (*read_paged)(struct mii_bus *bus, int addr, u16 page, int regnum); ++ /** @write: Perform a write transfer on the bus, offloading page access */ ++ int (*write_paged)(struct mii_bus *bus, int addr, u16 page, int regnum, u16 val); ++ /** currently selected page when page access is offloaded ++ * array should be PHY_MAX_ADDR+1size, but current design of the MDIO driver ++ * uses port addresses as phy addresses and they are up to 6 bit. ++ */ ++ u16 selected_page[64]; + }; + #define to_mii_bus(d) container_of(d, struct mii_bus, dev) + +@@ -1651,6 +1668,66 @@ static inline int __phy_package_read(str + return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); + } + ++static inline int phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum); ++} ++ ++static inline int __phy_package_read_port(struct phy_device *phydev, u16 port, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_read(phydev->mdio.bus, shared->addr + port, regnum); ++} ++ ++static inline int phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum); ++} ++ ++static inline int __phy_package_read_paged(struct phy_device *phydev, u16 page, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr, page, regnum); ++} ++ ++static inline int phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum); ++} ++ ++static inline int __phy_package_port_read_paged(struct phy_device *phydev, u16 port, u16 page, u32 regnum) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_read_paged(phydev->mdio.bus, shared->addr + port, page, regnum); ++} ++ + static inline int phy_package_write(struct phy_device *phydev, + u32 regnum, u16 val) + { +@@ -1673,6 +1750,72 @@ static inline int __phy_package_write(st + return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); + } + ++static inline int phy_package_port_write(struct phy_device *phydev, ++ u16 port, u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val); ++} ++ ++static inline int __phy_package_port_write(struct phy_device *phydev, ++ u16 port, u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_write(phydev->mdio.bus, shared->addr + port, regnum, val); ++} ++ ++static inline int phy_package_port_write_paged(struct phy_device *phydev, ++ u16 port, u16 page, u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val); ++} ++ ++static inline int __phy_package_port_write_paged(struct phy_device *phydev, ++ u16 port, u16 page, u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr + port, page, regnum, val); ++} ++ ++static inline int phy_package_write_paged(struct phy_device *phydev, ++ u16 page, u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val); ++} ++ ++static inline int __phy_package_write_paged(struct phy_device *phydev, ++ u16 page, u32 regnum, u16 val) ++{ ++ struct phy_package_shared *shared = phydev->shared; ++ ++ if (!shared) ++ return -EIO; ++ ++ return __mdiobus_write_paged(phydev->mdio.bus, shared->addr, page, regnum, val); ++} ++ + static inline bool __phy_package_set_once(struct phy_device *phydev, + unsigned int b) + { +--- a/include/uapi/linux/mii.h ++++ b/include/uapi/linux/mii.h +@@ -36,6 +36,7 @@ + #define MII_RESV2 0x1a /* Reserved... */ + #define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ + #define MII_NCONFIG 0x1c /* Network interface config */ ++#define MII_MAINPAGE 0x1f /* Page register */ + + /* Basic mode control register. */ + #define BMCR_RESV 0x003f /* Unused... */ diff --git a/target/linux/realtek/rtl838x/config-5.15 b/target/linux/realtek/rtl838x/config-5.15 new file mode 100644 index 00000000000..d69345e3012 --- /dev/null +++ b/target/linux/realtek/rtl838x/config-5.15 @@ -0,0 +1,228 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BMIPS_CPUFREQ is not set +# CONFIG_CEVT_R4K is not set +# CONFIG_CEVT_RTL9300 is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_BOSTON=y +CONFIG_COMMON_CLK_REALTEK=y +CONFIG_COMMON_CLK_RTL83XX=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_DIEI=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CPU_SUPPORTS_MSA=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_RNG2=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_8250=y +CONFIG_EXTRA_FIRMWARE="rtl838x_phy/rtl838x_8214fc.fw rtl838x_phy/rtl838x_8218b.fw rtl838x_phy/rtl838x_8380.fw" +CONFIG_EXTRA_FIRMWARE_DIR="firmware" +CONFIG_FIXED_PHY=y +CONFIG_FORCE_MAX_ZONEORDER=13 +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_REALTEK_OTTO=y +CONFIG_GPIO_RTL8231=y +CONFIG_GPIO_WATCHDOG=y +# CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HWMON=y +CONFIG_HZ_PERIODIC=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_MUX=y +# CONFIG_I2C_RTL9300 is not set +# CONFIG_I2C_MUX_RTL9300 is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +CONFIG_JFFS2_ZLIB=y +CONFIG_LEDS_GPIO=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_LIBFDT=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_MARVELL_PHY=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MDIO_I2C=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CBPF_JIT=y +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=5 +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MIPS_SPRAM=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BRNIMAGE_FW=y +CONFIG_MTD_SPLIT_EVA_FW=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_H3C_VFS=y +CONFIG_MTD_SPLIT_TPLINK_FW=y +CONFIG_MTD_SPLIT_UIMAGE_FW=y +CONFIG_MTD_VIRT_CONCAT=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_RTL83XX=y +CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_RTL838X=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PINCTRL=y +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_RATIONAL=y +CONFIG_REALTEK_OTTO_TIMER=y +CONFIG_REALTEK_OTTO_WDT=y +CONFIG_REALTEK_PHY=y +CONFIG_REALTEK_SOC_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RTL83XX=y +CONFIG_RTL838X=y +# CONFIG_RTL839X is not set +# CONFIG_RTL930X is not set +# CONFIG_RTL931X is not set +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SFP=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRAM=y +CONFIG_SRCU=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_TARGET_ISA_REV=2 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TINY_SRCU=y +CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y +CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/realtek/rtl839x/config-5.15 b/target/linux/realtek/rtl839x/config-5.15 new file mode 100644 index 00000000000..57eded97fcd --- /dev/null +++ b/target/linux/realtek/rtl839x/config-5.15 @@ -0,0 +1,223 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BMIPS_CPUFREQ is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_BOSTON=y +CONFIG_COMMON_CLK_REALTEK=y +CONFIG_COMMON_CLK_RTL83XX=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +CONFIG_CPUFREQ_DT=y +CONFIG_CPUFREQ_DT_PLATDEV=y +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ATTR_SET=y +CONFIG_CPU_FREQ_GOV_COMMON=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_DIEI=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_CPUFREQ=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CPU_SUPPORTS_MSA=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_8250=y +CONFIG_FIXED_PHY=y +CONFIG_FORCE_MAX_ZONEORDER=13 +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_REALTEK_OTTO=y +CONFIG_GPIO_RTL8231=y +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HWMON=y +CONFIG_HZ_PERIODIC=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_GPIO=y +# CONFIG_I2C_RTL9300 is not set +# CONFIG_I2C_MUX_RTL9300 is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +CONFIG_JFFS2_ZLIB=y +CONFIG_LEDS_GPIO=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_LIBFDT=y +CONFIG_LLD_VERSION=0 +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_MARVELL_PHY=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MDIO_I2C=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CBPF_JIT=y +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_EXTERNAL_TIMER=y +CONFIG_MIPS_L1_CACHE_SHIFT=5 +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_MT_SMP=y +# CONFIG_MIPS_MT_FPAFF is not set +CONFIG_NR_CPUS=2 +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MIPS_SPRAM=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BRNIMAGE_FW=y +CONFIG_MTD_SPLIT_EVA_FW=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_TPLINK_FW=y +CONFIG_MTD_SPLIT_UIMAGE_FW=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_RTL83XX=y +CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_RTL838X=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PINCTRL=y +CONFIG_PM_OPP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_RATIONAL=y +CONFIG_REALTEK_OTTO_TIMER=y +CONFIG_REALTEK_OTTO_WDT=y +CONFIG_REALTEK_PHY=y +CONFIG_REALTEK_SOC_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RTL83XX=y +# CONFIG_RTL838X is not set +CONFIG_RTL839X=y +# CONFIG_RTL930X is not set +# CONFIG_RTL931X is not set +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SFP=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRAM=y +CONFIG_SRCU=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_TARGET_ISA_REV=2 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TINY_SRCU=y +CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y +CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/realtek/rtl930x/config-5.15 b/target/linux/realtek/rtl930x/config-5.15 new file mode 100644 index 00000000000..3b2b327f08f --- /dev/null +++ b/target/linux/realtek/rtl930x/config-5.15 @@ -0,0 +1,211 @@ +CONFIG_AQUANTIA_PHY=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_CEVT_RTL9300=y +# CONFIG_CEVT_R4K is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_BOSTON=y +# CONFIG_COMMON_CLK_REALTEK is not set +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_DIEI=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CPU_SUPPORTS_MSA=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_8250=y +CONFIG_FIXED_PHY=y +CONFIG_FORCE_MAX_ZONEORDER=13 +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_REALTEK_OTTO=y +CONFIG_GPIO_RTL8231=y +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HWMON=y +CONFIG_HZ_PERIODIC=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_RTL9300=y +CONFIG_I2C_RTL9300=y +CONFIG_I2C_SMBUS=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +CONFIG_JFFS2_ZLIB=y +CONFIG_LEDS_GPIO=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_LIBFDT=y +CONFIG_LLD_VERSION=0 +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_MARVELL_PHY=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MDIO_I2C=y +CONFIG_MDIO_SMBUS=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CBPF_JIT=y +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +# CONFIG_MIPS_MT_SMP is not set +# CONFIG_MIPS_MT_FPAFF is not set +CONFIG_NR_CPUS=2 +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MIPS_SPRAM=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BRNIMAGE_FW=y +CONFIG_MTD_SPLIT_EVA_FW=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_TPLINK_FW=y +CONFIG_MTD_SPLIT_UIMAGE_FW=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_RTL83XX=y +CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_RTL838X=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PINCTRL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_RATIONAL=y +# CONFIG_REALTEK_OTTO_TIMER is not set +CONFIG_REALTEK_OTTO_WDT=y +CONFIG_REALTEK_PHY=y +CONFIG_REALTEK_SOC_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RTL83XX=y +# CONFIG_RTL838X is not set +# CONFIG_RTL839X is not set +CONFIG_RTL930X=y +# CONFIG_RTL931X is not set +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SFP=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_TARGET_ISA_REV=2 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TINY_SRCU=y +CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y +CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/realtek/rtl931x/config-5.15 b/target/linux/realtek/rtl931x/config-5.15 new file mode 100644 index 00000000000..567421c8d4a --- /dev/null +++ b/target/linux/realtek/rtl931x/config-5.15 @@ -0,0 +1,225 @@ +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_MMAP_RND_BITS_MAX=15 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_BOSTON=y +# CONFIG_COMMON_CLK_REALTEK is not set +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=15 +CONFIG_CPU_BIG_ENDIAN=y +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_DIEI=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_RIXI=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CPU_SUPPORTS_MSA=y +CONFIG_MIPS_MT=y +CONFIG_MIPS_MT_FPAFF=y +CONFIG_MIPS_MT_SMP=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=4 +CONFIG_HIGHMEM=y +CONFIG_CRYPTO_BLAKE2S=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 +CONFIG_CRYPTO_NULL2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CSRC_R4K=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_FIXED_PHY=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_GETTIMEOFDAY=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_LIB_ASHLDI3=y +CONFIG_GENERIC_LIB_ASHRDI3=y +CONFIG_GENERIC_LIB_CMPDI2=y +CONFIG_GENERIC_LIB_LSHRDI3=y +CONFIG_GENERIC_LIB_UCMPDI2=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_PINCTRL_GROUPS=y +CONFIG_GENERIC_PINMUX_FUNCTIONS=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_REALTEK_OTTO=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_RTL8231=y +CONFIG_GRO_CELLS=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_HWMON=y +CONFIG_HZ_PERIODIC=y +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_RTL9300=y +CONFIG_I2C_RTL9300=y +CONFIG_I2C_SMBUS=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MIPS_CPU=y +CONFIG_IRQ_WORK=y +CONFIG_JFFS2_ZLIB=y +CONFIG_LEDS_GPIO=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_LIBFDT=y +CONFIG_LLD_VERSION=0 +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_MARVELL_PHY=y +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MDIO_DEVRES=y +CONFIG_MDIO_I2C=y +CONFIG_MDIO_SMBUS=y +CONFIG_MEMFD_CREATE=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MIPS=y +CONFIG_MIPS_ASID_BITS=8 +CONFIG_MIPS_ASID_SHIFT=0 +CONFIG_MIPS_CBPF_JIT=y +CONFIG_MIPS_CLOCK_VSYSCALL=y +# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_MIPS_CMDLINE_FROM_DTB=y +# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +CONFIG_MIPS_LD_CAN_LINK_VDSO=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_RAW_APPENDED_DTB=y +CONFIG_MIPS_SPRAM=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_SPLIT_BRNIMAGE_FW=y +CONFIG_MTD_SPLIT_EVA_FW=y +CONFIG_MTD_SPLIT_FIRMWARE=y +CONFIG_MTD_SPLIT_TPLINK_FW=y +CONFIG_MTD_SPLIT_UIMAGE_FW=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_DEVLINK=y +CONFIG_NET_DSA=y +CONFIG_NET_DSA_RTL83XX=y +CONFIG_NET_DSA_TAG_RTL83XX=y +CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_RTL838X=y +CONFIG_NET_SWITCHDEV=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NVMEM=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_PCI_DRIVERS_LEGACY=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PINCTRL=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_RATIONAL=y +# CONFIG_REALTEK_OTTO_TIMER is not set +# CONFIG_REALTEK_PHY is not set +CONFIG_REALTEK_SOC_PHY=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RTL838X=y +# CONFIG_RTL9300_TIMER is not set +CONFIG_SENSORS_GPIO_FAN=y +CONFIG_SENSORS_LM75=y +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SFP=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_MEM=y +CONFIG_SRCU=y +CONFIG_SWPHY=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_TARGET_ISA_REV=2 +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TINY_SRCU=y +# CONFIG_USE_GENERIC_EARLY_PRINTK_8250 is not set +CONFIG_USE_OF=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_FORCE_MAX_ZONEORDER=13 +# CONFIG_MIPS_VPE_LOADER is not set +CONFIG_MIPS_CPU_SCACHE=y +CONFIG_SPI_RTL838X=y +CONFIG_MIPS_MT_SMP=y +CONFIG_MIPS_CPC=y +CONFIG_MIPS_CPS=y +CONFIG_MIPS_PM=y +# CONFIG_MIPS_CMP is not set +CONFIG_MIPS_CPS_CPUIDLE=y +# CONFIG_MIPS_CPS_NS16550_BOOL is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=2 +CONFIG_RTL83XX=y +# CONFIG_RTL838X is not set +# CONFIG_RTL839X is not set +CONFIG_RTL930X=y +CONFIG_RTL931X=y +CONFIG_REALTEK_OTTO_WDT=y From a9d5a8bc79b1246c7ae39f2fb6e9ed30bc3dd781 Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Fri, 22 Jul 2022 22:05:51 +0900 Subject: [PATCH 044/110] realtek: drop patches of upstreamed drivers from 5.15 The following drivers were upstreamed and available on 5.15, so drop from OpenWrt tree. - realtek-otto-gpio (5.13) - realtek-rtl-spi (5.12) - realtek-rtl-intc (5.12) Signed-off-by: INAGAKI Hiroshi --- ...s-gpio-binding-for-realtek-otto-gpio.patch | 103 ----- ...3-gpio-add-realtek-otto-gpio-support.patch | 394 ------------------ ...altek-rtl838x-rtl839x-spi-controller.patch | 57 --- ...ltek-rtl838x-rtl839x-spi-controllers.patch | 248 ----------- ...-add-realtek-rtl838x-rtl839x-support.patch | 78 ---- ...rtl838x-rtl839x-interrupt-controller.patch | 211 ---------- 6 files changed, 1091 deletions(-) delete mode 100644 target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch delete mode 100644 target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch delete mode 100644 target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch delete mode 100644 target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch delete mode 100644 target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch delete mode 100644 target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch diff --git a/target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch b/target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch deleted file mode 100644 index 80b806d89ff..00000000000 --- a/target/linux/realtek/patches-5.15/001-5.13-dt-bindings-gpio-binding-for-realtek-otto-gpio.patch +++ /dev/null @@ -1,103 +0,0 @@ -From a362c0ce64866939c3daa17c76943cfed555b065 Mon Sep 17 00:00:00 2001 -From: Sander Vanheule -Date: Tue, 30 Mar 2021 19:48:42 +0200 -Subject: dt-bindings: gpio: Binding for Realtek Otto GPIO - -Add a binding description for Realtek's GPIO controller found on several -of their MIPS-based SoCs (codenamed Otto), such as the RTL838x and -RTL839x series of switch SoCs. - -A fallback binding 'realtek,otto-gpio' is provided for cases where the -actual port ordering is not known yet, and enabling the interrupt -controller may result in uncaught interrupts. - -Signed-off-by: Sander Vanheule -Reviewed-by: Linus Walleij -Reviewed-by: Rob Herring -Signed-off-by: Bartosz Golaszewski ---- - .../bindings/gpio/realtek,otto-gpio.yaml | 78 ++++++++++++++++++++++ - 1 file changed, 78 insertions(+) - create mode 100644 Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml - ---- /dev/null -+++ b/Documentation/devicetree/bindings/gpio/realtek,otto-gpio.yaml -@@ -0,0 +1,78 @@ -+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/gpio/realtek,otto-gpio.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: Realtek Otto GPIO controller -+ -+maintainers: -+ - Sander Vanheule -+ - Bert Vermeulen -+ -+description: | -+ Realtek's GPIO controller on their MIPS switch SoCs (Otto platform) consists -+ of two banks of 32 GPIOs. These GPIOs can generate edge-triggered interrupts. -+ Each bank's interrupts are cascased into one interrupt line on the parent -+ interrupt controller, if provided. -+ This binding allows defining a single bank in the devicetree. The interrupt -+ controller is not supported on the fallback compatible name, which only -+ allows for GPIO port use. -+ -+properties: -+ $nodename: -+ pattern: "^gpio@[0-9a-f]+$" -+ -+ compatible: -+ items: -+ - enum: -+ - realtek,rtl8380-gpio -+ - realtek,rtl8390-gpio -+ - const: realtek,otto-gpio -+ -+ reg: -+ maxItems: 1 -+ -+ "#gpio-cells": -+ const: 2 -+ -+ gpio-controller: true -+ -+ ngpios: -+ minimum: 1 -+ maximum: 32 -+ -+ interrupt-controller: true -+ -+ "#interrupt-cells": -+ const: 2 -+ -+ interrupts: -+ maxItems: 1 -+ -+required: -+ - compatible -+ - reg -+ - "#gpio-cells" -+ - gpio-controller -+ -+additionalProperties: false -+ -+dependencies: -+ interrupt-controller: [ interrupts ] -+ -+examples: -+ - | -+ gpio@3500 { -+ compatible = "realtek,rtl8380-gpio", "realtek,otto-gpio"; -+ reg = <0x3500 0x1c>; -+ gpio-controller; -+ #gpio-cells = <2>; -+ ngpios = <24>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ interrupt-parent = <&rtlintc>; -+ interrupts = <23>; -+ }; -+ -+... diff --git a/target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch b/target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch deleted file mode 100644 index 62e4338cbb2..00000000000 --- a/target/linux/realtek/patches-5.15/002-5.13-gpio-add-realtek-otto-gpio-support.patch +++ /dev/null @@ -1,394 +0,0 @@ -From f0f7d662e8514169c90d3d84cd6df773b2983088 Mon Sep 17 00:00:00 2001 -From: Sander Vanheule -Date: Tue, 30 Mar 2021 19:48:43 +0200 -Subject: gpio: Add Realtek Otto GPIO support - -Realtek MIPS SoCs (platform name Otto) have GPIO controllers with up to -64 GPIOs, divided over two banks. Each bank has a set of registers for -32 GPIOs, with support for edge-triggered interrupts. - -Each GPIO bank consists of four 8-bit GPIO ports (ABCD and EFGH). Most -registers pack one bit per GPIO, except for the IMR register, which -packs two bits per GPIO (AB-CD). - -Although the byte order is currently assumed to have port A..D at offset -0x0..0x3, this has been observed to be reversed on other, Lexra-based, -SoCs (e.g. RTL8196E/97D/97F). - -Interrupt support is disabled for the fallback devicetree-compatible -'realtek,otto-gpio'. This allows for quick support of GPIO banks in -which the byte order would be unknown. In this case, the port ordering -in the IMR registers may not match the reversed order in the other -registers (DCBA, and BA-DC or DC-BA). - -Signed-off-by: Sander Vanheule -Reviewed-by: Linus Walleij -Reviewed-by: Andy Shevchenko -Signed-off-by: Bartosz Golaszewski ---- - drivers/gpio/Kconfig | 13 ++ - drivers/gpio/Makefile | 1 + - drivers/gpio/gpio-realtek-otto.c | 325 +++++++++++++++++++++++++++++++++++++++ - 3 files changed, 339 insertions(+) - create mode 100644 drivers/gpio/gpio-realtek-otto.c - ---- a/drivers/gpio/Kconfig -+++ b/drivers/gpio/Kconfig -@@ -489,6 +489,19 @@ config GPIO_RDA - help - Say Y here to support RDA Micro GPIO controller. - -+config GPIO_REALTEK_OTTO -+ tristate "Realtek Otto GPIO support" -+ depends on MACH_REALTEK_RTL -+ default MACH_REALTEK_RTL -+ select GPIO_GENERIC -+ select GPIOLIB_IRQCHIP -+ help -+ The GPIO controller on the Otto MIPS platform supports up to two -+ banks of 32 GPIOs, with edge triggered interrupts. The 32 GPIOs -+ are grouped in four 8-bit wide ports. -+ -+ When built as a module, the module will be called realtek_otto_gpio. -+ - config GPIO_REG - bool - help ---- a/drivers/gpio/Makefile -+++ b/drivers/gpio/Makefile -@@ -125,6 +125,7 @@ obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t - obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o - obj-$(CONFIG_GPIO_RDA) += gpio-rda.o - obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o -+obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o - obj-$(CONFIG_GPIO_REG) += gpio-reg.o - obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o - obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o ---- /dev/null -+++ b/drivers/gpio/gpio-realtek-otto.c -@@ -0,0 +1,325 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* -+ * Total register block size is 0x1C for one bank of four ports (A, B, C, D). -+ * An optional second bank, with ports E, F, G, and H, may be present, starting -+ * at register offset 0x1C. -+ */ -+ -+/* -+ * Pin select: (0) "normal", (1) "dedicate peripheral" -+ * Not used on RTL8380/RTL8390, peripheral selection is managed by control bits -+ * in the peripheral registers. -+ */ -+#define REALTEK_GPIO_REG_CNR 0x00 -+/* Clear bit (0) for input, set bit (1) for output */ -+#define REALTEK_GPIO_REG_DIR 0x08 -+#define REALTEK_GPIO_REG_DATA 0x0C -+/* Read bit for IRQ status, write 1 to clear IRQ */ -+#define REALTEK_GPIO_REG_ISR 0x10 -+/* Two bits per GPIO in IMR registers */ -+#define REALTEK_GPIO_REG_IMR 0x14 -+#define REALTEK_GPIO_REG_IMR_AB 0x14 -+#define REALTEK_GPIO_REG_IMR_CD 0x18 -+#define REALTEK_GPIO_IMR_LINE_MASK GENMASK(1, 0) -+#define REALTEK_GPIO_IRQ_EDGE_FALLING 1 -+#define REALTEK_GPIO_IRQ_EDGE_RISING 2 -+#define REALTEK_GPIO_IRQ_EDGE_BOTH 3 -+ -+#define REALTEK_GPIO_MAX 32 -+#define REALTEK_GPIO_PORTS_PER_BANK 4 -+ -+/** -+ * realtek_gpio_ctrl - Realtek Otto GPIO driver data -+ * -+ * @gc: Associated gpio_chip instance -+ * @base: Base address of the register block for a GPIO bank -+ * @lock: Lock for accessing the IRQ registers and values -+ * @intr_mask: Mask for interrupts lines -+ * @intr_type: Interrupt type selection -+ * -+ * Because the interrupt mask register (IMR) combines the function of IRQ type -+ * selection and masking, two extra values are stored. @intr_mask is used to -+ * mask/unmask the interrupts for a GPIO port, and @intr_type is used to store -+ * the selected interrupt types. The logical AND of these values is written to -+ * IMR on changes. -+ */ -+struct realtek_gpio_ctrl { -+ struct gpio_chip gc; -+ void __iomem *base; -+ raw_spinlock_t lock; -+ u16 intr_mask[REALTEK_GPIO_PORTS_PER_BANK]; -+ u16 intr_type[REALTEK_GPIO_PORTS_PER_BANK]; -+}; -+ -+/* Expand with more flags as devices with other quirks are added */ -+enum realtek_gpio_flags { -+ /* -+ * Allow disabling interrupts, for cases where the port order is -+ * unknown. This may result in a port mismatch between ISR and IMR. -+ * An interrupt would appear to come from a different line than the -+ * line the IRQ handler was assigned to, causing uncaught interrupts. -+ */ -+ GPIO_INTERRUPTS_DISABLED = BIT(0), -+}; -+ -+static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data) -+{ -+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data); -+ -+ return container_of(gc, struct realtek_gpio_ctrl, gc); -+} -+ -+/* -+ * Normal port order register access -+ * -+ * Port information is stored with the first port at offset 0, followed by the -+ * second, etc. Most registers store one bit per GPIO and use a u8 value per -+ * port. The two interrupt mask registers store two bits per GPIO, so use u16 -+ * values. -+ */ -+static void realtek_gpio_write_imr(struct realtek_gpio_ctrl *ctrl, -+ unsigned int port, u16 irq_type, u16 irq_mask) -+{ -+ iowrite16(irq_type & irq_mask, ctrl->base + REALTEK_GPIO_REG_IMR + 2 * port); -+} -+ -+static void realtek_gpio_clear_isr(struct realtek_gpio_ctrl *ctrl, -+ unsigned int port, u8 mask) -+{ -+ iowrite8(mask, ctrl->base + REALTEK_GPIO_REG_ISR + port); -+} -+ -+static u8 realtek_gpio_read_isr(struct realtek_gpio_ctrl *ctrl, unsigned int port) -+{ -+ return ioread8(ctrl->base + REALTEK_GPIO_REG_ISR + port); -+} -+ -+/* Set the rising and falling edge mask bits for a GPIO port pin */ -+static u16 realtek_gpio_imr_bits(unsigned int pin, u16 value) -+{ -+ return (value & REALTEK_GPIO_IMR_LINE_MASK) << 2 * pin; -+} -+ -+static void realtek_gpio_irq_ack(struct irq_data *data) -+{ -+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); -+ irq_hw_number_t line = irqd_to_hwirq(data); -+ unsigned int port = line / 8; -+ unsigned int port_pin = line % 8; -+ -+ realtek_gpio_clear_isr(ctrl, port, BIT(port_pin)); -+} -+ -+static void realtek_gpio_irq_unmask(struct irq_data *data) -+{ -+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); -+ unsigned int line = irqd_to_hwirq(data); -+ unsigned int port = line / 8; -+ unsigned int port_pin = line % 8; -+ unsigned long flags; -+ u16 m; -+ -+ raw_spin_lock_irqsave(&ctrl->lock, flags); -+ m = ctrl->intr_mask[port]; -+ m |= realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); -+ ctrl->intr_mask[port] = m; -+ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); -+ raw_spin_unlock_irqrestore(&ctrl->lock, flags); -+} -+ -+static void realtek_gpio_irq_mask(struct irq_data *data) -+{ -+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); -+ unsigned int line = irqd_to_hwirq(data); -+ unsigned int port = line / 8; -+ unsigned int port_pin = line % 8; -+ unsigned long flags; -+ u16 m; -+ -+ raw_spin_lock_irqsave(&ctrl->lock, flags); -+ m = ctrl->intr_mask[port]; -+ m &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); -+ ctrl->intr_mask[port] = m; -+ realtek_gpio_write_imr(ctrl, port, ctrl->intr_type[port], m); -+ raw_spin_unlock_irqrestore(&ctrl->lock, flags); -+} -+ -+static int realtek_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) -+{ -+ struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); -+ unsigned int line = irqd_to_hwirq(data); -+ unsigned int port = line / 8; -+ unsigned int port_pin = line % 8; -+ unsigned long flags; -+ u16 type, t; -+ -+ switch (flow_type & IRQ_TYPE_SENSE_MASK) { -+ case IRQ_TYPE_EDGE_FALLING: -+ type = REALTEK_GPIO_IRQ_EDGE_FALLING; -+ break; -+ case IRQ_TYPE_EDGE_RISING: -+ type = REALTEK_GPIO_IRQ_EDGE_RISING; -+ break; -+ case IRQ_TYPE_EDGE_BOTH: -+ type = REALTEK_GPIO_IRQ_EDGE_BOTH; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ irq_set_handler_locked(data, handle_edge_irq); -+ -+ raw_spin_lock_irqsave(&ctrl->lock, flags); -+ t = ctrl->intr_type[port]; -+ t &= ~realtek_gpio_imr_bits(port_pin, REALTEK_GPIO_IMR_LINE_MASK); -+ t |= realtek_gpio_imr_bits(port_pin, type); -+ ctrl->intr_type[port] = t; -+ realtek_gpio_write_imr(ctrl, port, t, ctrl->intr_mask[port]); -+ raw_spin_unlock_irqrestore(&ctrl->lock, flags); -+ -+ return 0; -+} -+ -+static void realtek_gpio_irq_handler(struct irq_desc *desc) -+{ -+ struct gpio_chip *gc = irq_desc_get_handler_data(desc); -+ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); -+ struct irq_chip *irq_chip = irq_desc_get_chip(desc); -+ unsigned int lines_done; -+ unsigned int port_pin_count; -+ unsigned int irq; -+ unsigned long status; -+ int offset; -+ -+ chained_irq_enter(irq_chip, desc); -+ -+ for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) { -+ status = realtek_gpio_read_isr(ctrl, lines_done / 8); -+ port_pin_count = min(gc->ngpio - lines_done, 8U); -+ for_each_set_bit(offset, &status, port_pin_count) { -+ irq = irq_find_mapping(gc->irq.domain, offset); -+ generic_handle_irq(irq); -+ } -+ } -+ -+ chained_irq_exit(irq_chip, desc); -+} -+ -+static int realtek_gpio_irq_init(struct gpio_chip *gc) -+{ -+ struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); -+ unsigned int port; -+ -+ for (port = 0; (port * 8) < gc->ngpio; port++) { -+ realtek_gpio_write_imr(ctrl, port, 0, 0); -+ realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0)); -+ } -+ -+ return 0; -+} -+ -+static struct irq_chip realtek_gpio_irq_chip = { -+ .name = "realtek-otto-gpio", -+ .irq_ack = realtek_gpio_irq_ack, -+ .irq_mask = realtek_gpio_irq_mask, -+ .irq_unmask = realtek_gpio_irq_unmask, -+ .irq_set_type = realtek_gpio_irq_set_type, -+}; -+ -+static const struct of_device_id realtek_gpio_of_match[] = { -+ { -+ .compatible = "realtek,otto-gpio", -+ .data = (void *)GPIO_INTERRUPTS_DISABLED, -+ }, -+ { -+ .compatible = "realtek,rtl8380-gpio", -+ }, -+ { -+ .compatible = "realtek,rtl8390-gpio", -+ }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, realtek_gpio_of_match); -+ -+static int realtek_gpio_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ unsigned int dev_flags; -+ struct gpio_irq_chip *girq; -+ struct realtek_gpio_ctrl *ctrl; -+ u32 ngpios; -+ int err, irq; -+ -+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); -+ if (!ctrl) -+ return -ENOMEM; -+ -+ dev_flags = (unsigned int) device_get_match_data(dev); -+ -+ ngpios = REALTEK_GPIO_MAX; -+ device_property_read_u32(dev, "ngpios", &ngpios); -+ -+ if (ngpios > REALTEK_GPIO_MAX) { -+ dev_err(&pdev->dev, "invalid ngpios (max. %d)\n", -+ REALTEK_GPIO_MAX); -+ return -EINVAL; -+ } -+ -+ ctrl->base = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(ctrl->base)) -+ return PTR_ERR(ctrl->base); -+ -+ raw_spin_lock_init(&ctrl->lock); -+ -+ err = bgpio_init(&ctrl->gc, dev, 4, -+ ctrl->base + REALTEK_GPIO_REG_DATA, NULL, NULL, -+ ctrl->base + REALTEK_GPIO_REG_DIR, NULL, -+ BGPIOF_BIG_ENDIAN_BYTE_ORDER); -+ if (err) { -+ dev_err(dev, "unable to init generic GPIO"); -+ return err; -+ } -+ -+ ctrl->gc.ngpio = ngpios; -+ ctrl->gc.owner = THIS_MODULE; -+ -+ irq = platform_get_irq_optional(pdev, 0); -+ if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) { -+ girq = &ctrl->gc.irq; -+ girq->chip = &realtek_gpio_irq_chip; -+ girq->default_type = IRQ_TYPE_NONE; -+ girq->handler = handle_bad_irq; -+ girq->parent_handler = realtek_gpio_irq_handler; -+ girq->num_parents = 1; -+ girq->parents = devm_kcalloc(dev, girq->num_parents, -+ sizeof(*girq->parents), GFP_KERNEL); -+ if (!girq->parents) -+ return -ENOMEM; -+ girq->parents[0] = irq; -+ girq->init_hw = realtek_gpio_irq_init; -+ } -+ -+ return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); -+} -+ -+static struct platform_driver realtek_gpio_driver = { -+ .driver = { -+ .name = "realtek-otto-gpio", -+ .of_match_table = realtek_gpio_of_match, -+ }, -+ .probe = realtek_gpio_probe, -+}; -+module_platform_driver(realtek_gpio_driver); -+ -+MODULE_DESCRIPTION("Realtek Otto GPIO support"); -+MODULE_AUTHOR("Sander Vanheule "); -+MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch b/target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch deleted file mode 100644 index 325e904994a..00000000000 --- a/target/linux/realtek/patches-5.15/003-5.12-spi-realtek-rtl838x-rtl839x-spi-controller.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 6acbd614c2c8d3b8de5fb7605d6e24b9b3a8a17b Mon Sep 17 00:00:00 2001 -From: Bert Vermeulen -Date: Wed, 20 Jan 2021 14:59:27 +0100 -Subject: spi: Realtek RTL838x/RTL839x SPI controller - -Signed-off-by: Bert Vermeulen -Link: https://lore.kernel.org/r/20210120135928.246054-2-bert@biot.com -Signed-off-by: Mark Brown ---- - .../devicetree/bindings/spi/realtek,rtl-spi.yaml | 41 ++++++++++++++++++++++ - 1 file changed, 41 insertions(+) - create mode 100644 Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml - ---- /dev/null -+++ b/Documentation/devicetree/bindings/spi/realtek,rtl-spi.yaml -@@ -0,0 +1,41 @@ -+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/spi/realtek,rtl-spi.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: Realtek RTL838x/RTL839x SPI controller -+ -+maintainers: -+ - Bert Vermeulen -+ - Birger Koblitz -+ -+allOf: -+ - $ref: "spi-controller.yaml#" -+ -+properties: -+ compatible: -+ oneOf: -+ - const: realtek,rtl8380-spi -+ - const: realtek,rtl8382-spi -+ - const: realtek,rtl8391-spi -+ - const: realtek,rtl8392-spi -+ - const: realtek,rtl8393-spi -+ -+ reg: -+ maxItems: 1 -+ -+required: -+ - compatible -+ - reg -+ -+unevaluatedProperties: false -+ -+examples: -+ - | -+ spi: spi@1200 { -+ compatible = "realtek,rtl8382-spi"; -+ reg = <0x1200 0x100>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; diff --git a/target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch b/target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch deleted file mode 100644 index 090847baad7..00000000000 --- a/target/linux/realtek/patches-5.15/004-5.12-spi-realtek-rtl-add-support-for-realtek-rtl838x-rtl839x-spi-controllers.patch +++ /dev/null @@ -1,248 +0,0 @@ -From a8af5cc2ff1e804694629a8ef320935629dd15ba Mon Sep 17 00:00:00 2001 -From: Bert Vermeulen -Date: Wed, 20 Jan 2021 14:59:28 +0100 -Subject: spi: realtek-rtl: Add support for Realtek RTL838x/RTL839x SPI - controllers - -This driver likely also supports earlier (RTL8196) and later (RTL93xx) -SoCs. - -The SPI hardware in these SoCs is specifically intended for connecting NOR -bootflash chips, and only used for that in dozens of examined devices. -However boiled down to basics, it's really just a half-duplex SPI -controller. - -The hardware appears to have a vestigial second chip-select control, but -it hasn't been seen in the wild and is thus not supported. - -Signed-off-by: Bert Vermeulen -Link: https://lore.kernel.org/r/20210120135928.246054-3-bert@biot.com -Signed-off-by: Mark Brown ---- - drivers/spi/Makefile | 1 + - drivers/spi/spi-realtek-rtl.c | 209 ++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 210 insertions(+) - create mode 100644 drivers/spi/spi-realtek-rtl.c - ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -94,6 +94,7 @@ obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom - obj-$(CONFIG_SPI_QUP) += spi-qup.o - obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o - obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o -+obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o - obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o - obj-$(CONFIG_SPI_RSPI) += spi-rspi.o - obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o ---- /dev/null -+++ b/drivers/spi/spi-realtek-rtl.c -@@ -0,0 +1,209 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+ -+#include -+#include -+#include -+#include -+ -+struct rtspi { -+ void __iomem *base; -+}; -+ -+/* SPI Flash Configuration Register */ -+#define RTL_SPI_SFCR 0x00 -+#define RTL_SPI_SFCR_RBO BIT(28) -+#define RTL_SPI_SFCR_WBO BIT(27) -+ -+/* SPI Flash Control and Status Register */ -+#define RTL_SPI_SFCSR 0x08 -+#define RTL_SPI_SFCSR_CSB0 BIT(31) -+#define RTL_SPI_SFCSR_CSB1 BIT(30) -+#define RTL_SPI_SFCSR_RDY BIT(27) -+#define RTL_SPI_SFCSR_CS BIT(24) -+#define RTL_SPI_SFCSR_LEN_MASK ~(0x03 << 28) -+#define RTL_SPI_SFCSR_LEN1 (0x00 << 28) -+#define RTL_SPI_SFCSR_LEN4 (0x03 << 28) -+ -+/* SPI Flash Data Register */ -+#define RTL_SPI_SFDR 0x0c -+ -+#define REG(x) (rtspi->base + x) -+ -+ -+static void rt_set_cs(struct spi_device *spi, bool active) -+{ -+ struct rtspi *rtspi = spi_controller_get_devdata(spi->controller); -+ u32 value; -+ -+ /* CS0 bit is active low */ -+ value = readl(REG(RTL_SPI_SFCSR)); -+ if (active) -+ value |= RTL_SPI_SFCSR_CSB0; -+ else -+ value &= ~RTL_SPI_SFCSR_CSB0; -+ writel(value, REG(RTL_SPI_SFCSR)); -+} -+ -+static void set_size(struct rtspi *rtspi, int size) -+{ -+ u32 value; -+ -+ value = readl(REG(RTL_SPI_SFCSR)); -+ value &= RTL_SPI_SFCSR_LEN_MASK; -+ if (size == 4) -+ value |= RTL_SPI_SFCSR_LEN4; -+ else if (size == 1) -+ value |= RTL_SPI_SFCSR_LEN1; -+ writel(value, REG(RTL_SPI_SFCSR)); -+} -+ -+static inline void wait_ready(struct rtspi *rtspi) -+{ -+ while (!(readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY)) -+ cpu_relax(); -+} -+static void send4(struct rtspi *rtspi, const u32 *buf) -+{ -+ wait_ready(rtspi); -+ set_size(rtspi, 4); -+ writel(*buf, REG(RTL_SPI_SFDR)); -+} -+ -+static void send1(struct rtspi *rtspi, const u8 *buf) -+{ -+ wait_ready(rtspi); -+ set_size(rtspi, 1); -+ writel(buf[0] << 24, REG(RTL_SPI_SFDR)); -+} -+ -+static void rcv4(struct rtspi *rtspi, u32 *buf) -+{ -+ wait_ready(rtspi); -+ set_size(rtspi, 4); -+ *buf = readl(REG(RTL_SPI_SFDR)); -+} -+ -+static void rcv1(struct rtspi *rtspi, u8 *buf) -+{ -+ wait_ready(rtspi); -+ set_size(rtspi, 1); -+ *buf = readl(REG(RTL_SPI_SFDR)) >> 24; -+} -+ -+static int transfer_one(struct spi_controller *ctrl, struct spi_device *spi, -+ struct spi_transfer *xfer) -+{ -+ struct rtspi *rtspi = spi_controller_get_devdata(ctrl); -+ void *rx_buf; -+ const void *tx_buf; -+ int cnt; -+ -+ tx_buf = xfer->tx_buf; -+ rx_buf = xfer->rx_buf; -+ cnt = xfer->len; -+ if (tx_buf) { -+ while (cnt >= 4) { -+ send4(rtspi, tx_buf); -+ tx_buf += 4; -+ cnt -= 4; -+ } -+ while (cnt) { -+ send1(rtspi, tx_buf); -+ tx_buf++; -+ cnt--; -+ } -+ } else if (rx_buf) { -+ while (cnt >= 4) { -+ rcv4(rtspi, rx_buf); -+ rx_buf += 4; -+ cnt -= 4; -+ } -+ while (cnt) { -+ rcv1(rtspi, rx_buf); -+ rx_buf++; -+ cnt--; -+ } -+ } -+ -+ spi_finalize_current_transfer(ctrl); -+ -+ return 0; -+} -+ -+static void init_hw(struct rtspi *rtspi) -+{ -+ u32 value; -+ -+ /* Turn on big-endian byte ordering */ -+ value = readl(REG(RTL_SPI_SFCR)); -+ value |= RTL_SPI_SFCR_RBO | RTL_SPI_SFCR_WBO; -+ writel(value, REG(RTL_SPI_SFCR)); -+ -+ value = readl(REG(RTL_SPI_SFCSR)); -+ /* Permanently disable CS1, since it's never used */ -+ value |= RTL_SPI_SFCSR_CSB1; -+ /* Select CS0 for use */ -+ value &= RTL_SPI_SFCSR_CS; -+ writel(value, REG(RTL_SPI_SFCSR)); -+} -+ -+static int realtek_rtl_spi_probe(struct platform_device *pdev) -+{ -+ struct spi_controller *ctrl; -+ struct rtspi *rtspi; -+ int err; -+ -+ ctrl = devm_spi_alloc_master(&pdev->dev, sizeof(*rtspi)); -+ if (!ctrl) { -+ dev_err(&pdev->dev, "Error allocating SPI controller\n"); -+ return -ENOMEM; -+ } -+ platform_set_drvdata(pdev, ctrl); -+ rtspi = spi_controller_get_devdata(ctrl); -+ -+ rtspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); -+ if (IS_ERR(rtspi->base)) { -+ dev_err(&pdev->dev, "Could not map SPI register address"); -+ return -ENOMEM; -+ } -+ -+ init_hw(rtspi); -+ -+ ctrl->dev.of_node = pdev->dev.of_node; -+ ctrl->flags = SPI_CONTROLLER_HALF_DUPLEX; -+ ctrl->set_cs = rt_set_cs; -+ ctrl->transfer_one = transfer_one; -+ -+ err = devm_spi_register_controller(&pdev->dev, ctrl); -+ if (err) { -+ dev_err(&pdev->dev, "Could not register SPI controller\n"); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+ -+static const struct of_device_id realtek_rtl_spi_of_ids[] = { -+ { .compatible = "realtek,rtl8380-spi" }, -+ { .compatible = "realtek,rtl8382-spi" }, -+ { .compatible = "realtek,rtl8391-spi" }, -+ { .compatible = "realtek,rtl8392-spi" }, -+ { .compatible = "realtek,rtl8393-spi" }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, realtek_rtl_spi_of_ids); -+ -+static struct platform_driver realtek_rtl_spi_driver = { -+ .probe = realtek_rtl_spi_probe, -+ .driver = { -+ .name = "realtek-rtl-spi", -+ .of_match_table = realtek_rtl_spi_of_ids, -+ }, -+}; -+ -+module_platform_driver(realtek_rtl_spi_driver); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Bert Vermeulen "); -+MODULE_DESCRIPTION("Realtek RTL SPI driver"); diff --git a/target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch b/target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch deleted file mode 100644 index 165545a2207..00000000000 --- a/target/linux/realtek/patches-5.15/005-5.12-dt-bindings-interrupt-controller-add-realtek-rtl838x-rtl839x-support.patch +++ /dev/null @@ -1,78 +0,0 @@ -From 4a2b92a5d3519fc2c1edda4d4aa0e05bff41e8de Mon Sep 17 00:00:00 2001 -From: Bert Vermeulen -Date: Fri, 22 Jan 2021 21:42:23 +0100 -Subject: dt-bindings: interrupt-controller: Add Realtek RTL838x/RTL839x - support - -Document the binding for the Realtek RTL838x/RTL839x interrupt controller. - -Reviewed-by: Rob Herring -Signed-off-by: Bert Vermeulen -[maz: Add a commit message, as the author couldn't be bothered...] -Signed-off-by: Marc Zyngier -Link: https://lore.kernel.org/r/20210122204224.509124-2-bert@biot.com ---- - .../interrupt-controller/realtek,rtl-intc.yaml | 57 ++++++++++++++++++++++ - 1 file changed, 57 insertions(+) - create mode 100644 Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml - ---- /dev/null -+++ b/Documentation/devicetree/bindings/interrupt-controller/realtek,rtl-intc.yaml -@@ -0,0 +1,57 @@ -+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+%YAML 1.2 -+--- -+$id: http://devicetree.org/schemas/interrupt-controller/realtek,rtl-intc.yaml# -+$schema: http://devicetree.org/meta-schemas/core.yaml# -+ -+title: Realtek RTL SoC interrupt controller devicetree bindings -+ -+maintainers: -+ - Birger Koblitz -+ - Bert Vermeulen -+ - John Crispin -+ -+properties: -+ compatible: -+ const: realtek,rtl-intc -+ -+ "#interrupt-cells": -+ const: 1 -+ -+ reg: -+ maxItems: 1 -+ -+ interrupts: -+ maxItems: 1 -+ -+ interrupt-controller: true -+ -+ "#address-cells": -+ const: 0 -+ -+ interrupt-map: -+ description: Describes mapping from SoC interrupts to CPU interrupts -+ -+required: -+ - compatible -+ - reg -+ - "#interrupt-cells" -+ - interrupt-controller -+ - "#address-cells" -+ - interrupt-map -+ -+additionalProperties: false -+ -+examples: -+ - | -+ intc: interrupt-controller@3000 { -+ compatible = "realtek,rtl-intc"; -+ #interrupt-cells = <1>; -+ interrupt-controller; -+ reg = <0x3000 0x20>; -+ #address-cells = <0>; -+ interrupt-map = -+ <31 &cpuintc 2>, -+ <30 &cpuintc 1>, -+ <29 &cpuintc 5>; -+ }; diff --git a/target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch b/target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch deleted file mode 100644 index 753c41eb2f9..00000000000 --- a/target/linux/realtek/patches-5.15/006-5.12-irqchip-add-support-for-realtek-rtl838x-rtl839x-interrupt-controller.patch +++ /dev/null @@ -1,211 +0,0 @@ -From 9f3a0f34b84ad1b9a8f2bdae44b66f16685b2143 Mon Sep 17 00:00:00 2001 -From: Bert Vermeulen -Date: Fri, 22 Jan 2021 21:42:24 +0100 -Subject: irqchip: Add support for Realtek RTL838x/RTL839x interrupt controller - -This is a standard IRQ driver with only status and mask registers. - -The mapping from SoC interrupts (18-31) to MIPS core interrupts is -done via an interrupt-map in device tree. - -Signed-off-by: Bert Vermeulen -Signed-off-by: Birger Koblitz -Acked-by: John Crispin -Signed-off-by: Marc Zyngier -Link: https://lore.kernel.org/r/20210122204224.509124-3-bert@biot.com ---- - drivers/irqchip/Makefile | 1 + - drivers/irqchip/irq-realtek-rtl.c | 180 ++++++++++++++++++++++++++++++++++++++ - 2 files changed, 181 insertions(+) - create mode 100644 drivers/irqchip/irq-realtek-rtl.c - ---- a/drivers/irqchip/Makefile -+++ b/drivers/irqchip/Makefile -@@ -114,3 +114,4 @@ obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-l - obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o - obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o - obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o -+obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o ---- /dev/null -+++ b/drivers/irqchip/irq-realtek-rtl.c -@@ -0,0 +1,180 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2020 Birger Koblitz -+ * Copyright (C) 2020 Bert Vermeulen -+ * Copyright (C) 2020 John Crispin -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+/* Global Interrupt Mask Register */ -+#define RTL_ICTL_GIMR 0x00 -+/* Global Interrupt Status Register */ -+#define RTL_ICTL_GISR 0x04 -+/* Interrupt Routing Registers */ -+#define RTL_ICTL_IRR0 0x08 -+#define RTL_ICTL_IRR1 0x0c -+#define RTL_ICTL_IRR2 0x10 -+#define RTL_ICTL_IRR3 0x14 -+ -+#define REG(x) (realtek_ictl_base + x) -+ -+static DEFINE_RAW_SPINLOCK(irq_lock); -+static void __iomem *realtek_ictl_base; -+ -+static void realtek_ictl_unmask_irq(struct irq_data *i) -+{ -+ unsigned long flags; -+ u32 value; -+ -+ raw_spin_lock_irqsave(&irq_lock, flags); -+ -+ value = readl(REG(RTL_ICTL_GIMR)); -+ value |= BIT(i->hwirq); -+ writel(value, REG(RTL_ICTL_GIMR)); -+ -+ raw_spin_unlock_irqrestore(&irq_lock, flags); -+} -+ -+static void realtek_ictl_mask_irq(struct irq_data *i) -+{ -+ unsigned long flags; -+ u32 value; -+ -+ raw_spin_lock_irqsave(&irq_lock, flags); -+ -+ value = readl(REG(RTL_ICTL_GIMR)); -+ value &= ~BIT(i->hwirq); -+ writel(value, REG(RTL_ICTL_GIMR)); -+ -+ raw_spin_unlock_irqrestore(&irq_lock, flags); -+} -+ -+static struct irq_chip realtek_ictl_irq = { -+ .name = "realtek-rtl-intc", -+ .irq_mask = realtek_ictl_mask_irq, -+ .irq_unmask = realtek_ictl_unmask_irq, -+}; -+ -+static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) -+{ -+ irq_set_chip_and_handler(hw, &realtek_ictl_irq, handle_level_irq); -+ -+ return 0; -+} -+ -+static const struct irq_domain_ops irq_domain_ops = { -+ .map = intc_map, -+ .xlate = irq_domain_xlate_onecell, -+}; -+ -+static void realtek_irq_dispatch(struct irq_desc *desc) -+{ -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ struct irq_domain *domain; -+ unsigned int pending; -+ -+ chained_irq_enter(chip, desc); -+ pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); -+ if (unlikely(!pending)) { -+ spurious_interrupt(); -+ goto out; -+ } -+ domain = irq_desc_get_handler_data(desc); -+ generic_handle_irq(irq_find_mapping(domain, __ffs(pending))); -+ -+out: -+ chained_irq_exit(chip, desc); -+} -+ -+/* -+ * SoC interrupts are cascaded to MIPS CPU interrupts according to the -+ * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for -+ * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts -+ * thus go into 4 IRRs. -+ */ -+static int __init map_interrupts(struct device_node *node, struct irq_domain *domain) -+{ -+ struct device_node *cpu_ictl; -+ const __be32 *imap; -+ u32 imaplen, soc_int, cpu_int, tmp, regs[4]; -+ int ret, i, irr_regs[] = { -+ RTL_ICTL_IRR3, -+ RTL_ICTL_IRR2, -+ RTL_ICTL_IRR1, -+ RTL_ICTL_IRR0, -+ }; -+ u8 mips_irqs_set; -+ -+ ret = of_property_read_u32(node, "#address-cells", &tmp); -+ if (ret || tmp) -+ return -EINVAL; -+ -+ imap = of_get_property(node, "interrupt-map", &imaplen); -+ if (!imap || imaplen % 3) -+ return -EINVAL; -+ -+ mips_irqs_set = 0; -+ memset(regs, 0, sizeof(regs)); -+ for (i = 0; i < imaplen; i += 3 * sizeof(u32)) { -+ soc_int = be32_to_cpup(imap); -+ if (soc_int > 31) -+ return -EINVAL; -+ -+ cpu_ictl = of_find_node_by_phandle(be32_to_cpup(imap + 1)); -+ if (!cpu_ictl) -+ return -EINVAL; -+ ret = of_property_read_u32(cpu_ictl, "#interrupt-cells", &tmp); -+ if (ret || tmp != 1) -+ return -EINVAL; -+ of_node_put(cpu_ictl); -+ -+ cpu_int = be32_to_cpup(imap + 2); -+ if (cpu_int > 7) -+ return -EINVAL; -+ -+ if (!(mips_irqs_set & BIT(cpu_int))) { -+ irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch, -+ domain); -+ mips_irqs_set |= BIT(cpu_int); -+ } -+ -+ regs[(soc_int * 4) / 32] |= cpu_int << (soc_int * 4) % 32; -+ imap += 3; -+ } -+ -+ for (i = 0; i < 4; i++) -+ writel(regs[i], REG(irr_regs[i])); -+ -+ return 0; -+} -+ -+static int __init realtek_rtl_of_init(struct device_node *node, struct device_node *parent) -+{ -+ struct irq_domain *domain; -+ int ret; -+ -+ realtek_ictl_base = of_iomap(node, 0); -+ if (!realtek_ictl_base) -+ return -ENXIO; -+ -+ /* Disable all cascaded interrupts */ -+ writel(0, REG(RTL_ICTL_GIMR)); -+ -+ domain = irq_domain_add_simple(node, 32, 0, -+ &irq_domain_ops, NULL); -+ -+ ret = map_interrupts(node, domain); -+ if (ret) { -+ pr_err("invalid interrupt map\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+IRQCHIP_DECLARE(realtek_rtl_intc, "realtek,rtl-intc", realtek_rtl_of_init); From 23881c91e584f758af52c231cd2aaac3e751535b Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Tue, 29 Mar 2022 15:15:07 +0900 Subject: [PATCH 045/110] realtek: drop patches of upstreamed fix and changes from 5.15 - 007-5.16-gpio-realtek...: upstreamed on 5.16 and backported to 5.15.3 - 708-brflood-spi.patch : upstreamed - 709-lag-offloading.patch: upstreamed - 713-v5.12-net-dsa-... : upstreamed and some implementations are replaced Signed-off-by: INAGAKI Hiroshi --- ...ealtek-otto-fix-gpio-line-irq-offset.patch | 22 - .../patches-5.15/708-brflood-api.patch | 162 ---- .../patches-5.15/709-lag-offloading.patch | 781 ------------------ ...e-better-brport-flags-when-ports-lea.patch | 148 ---- 4 files changed, 1113 deletions(-) delete mode 100644 target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch delete mode 100644 target/linux/realtek/patches-5.15/708-brflood-api.patch delete mode 100644 target/linux/realtek/patches-5.15/709-lag-offloading.patch delete mode 100644 target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch diff --git a/target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch b/target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch deleted file mode 100644 index 9935c572573..00000000000 --- a/target/linux/realtek/patches-5.15/007-5.16-gpio-realtek-realtek-otto-fix-gpio-line-irq-offset.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: Sander Vanheule -Subject: gpio: realtek-otto: fix GPIO line IRQ offset - -The irqchip uses one domain for all GPIO lines, so th line offset should be -determined w.r.t. the first line of the first port, not the first line of the -triggered port. - -Fixes: 0d82fb1127fb ("gpio: Add Realtek Otto GPIO support") -Signed-off-by: Sander Vanheule -Link: https://lore.kernel.org/linux-gpio/20211028085243.34360-1-sander@svanheule.net/ - ---- a/drivers/gpio/gpio-realtek-otto.c -+++ b/drivers/gpio/gpio-realtek-otto.c -@@ -206,7 +206,7 @@ static void realtek_gpio_irq_handler(str - status = realtek_gpio_read_isr(ctrl, lines_done / 8); - port_pin_count = min(gc->ngpio - lines_done, 8U); - for_each_set_bit(offset, &status, port_pin_count) { -- irq = irq_find_mapping(gc->irq.domain, offset); -+ irq = irq_find_mapping(gc->irq.domain, offset + lines_done); - generic_handle_irq(irq); - } - } diff --git a/target/linux/realtek/patches-5.15/708-brflood-api.patch b/target/linux/realtek/patches-5.15/708-brflood-api.patch deleted file mode 100644 index 2716933ae70..00000000000 --- a/target/linux/realtek/patches-5.15/708-brflood-api.patch +++ /dev/null @@ -1,162 +0,0 @@ -From afa3ab54c03d5126b14651f367b38165fab5b3cc Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Tue, 18 Jan 2022 17:18:43 +0100 -Subject: net: brflood API - -Adds the DSA API for bridge configuration (flooding, L2 learning, -and aging) offload as found in Linux 5.12 so that we can implement -it in our drivver. - -Submitted-by: Sebastian Gottschall -Submitted-by: Birger Koblitz ---- - include/net/dsa.h | 6 +++++++-- - net/dsa/dsa_priv.h | 6 +++--- - net/dsa/port.c | 28 ++++++++---- - net/dsa/slave.c | 6 +++--- - 4 file changed, 29 insertions(+), 13 deletions(-) - ---- a/include/net/dsa.h -+++ b/include/net/dsa.h -@@ -552,8 +552,14 @@ struct dsa_switch_ops { - void (*port_stp_state_set)(struct dsa_switch *ds, int port, - u8 state); - void (*port_fast_age)(struct dsa_switch *ds, int port); -- int (*port_egress_floods)(struct dsa_switch *ds, int port, -- bool unicast, bool multicast); -+ int (*port_pre_bridge_flags)(struct dsa_switch *ds, int port, -+ unsigned long flags, -+ struct netlink_ext_ack *extack); -+ int (*port_bridge_flags)(struct dsa_switch *ds, int port, -+ unsigned long flags, -+ struct netlink_ext_ack *extack); -+ int (*port_set_mrouter)(struct dsa_switch *ds, int port, bool mrouter, -+ struct netlink_ext_ack *extack); - - /* - * VLAN support ---- a/net/dsa/dsa_priv.h -+++ b/net/dsa/dsa_priv.h -@@ -167,11 +167,11 @@ int dsa_port_mdb_add(const struct dsa_po - int dsa_port_mdb_del(const struct dsa_port *dp, - const struct switchdev_obj_port_mdb *mdb); - int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, -- struct switchdev_trans *trans); -+ struct switchdev_trans *trans, struct netlink_ext_ack *extack); - int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, -- struct switchdev_trans *trans); -+ struct switchdev_trans *trans, struct netlink_ext_ack *extack); - int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, -- struct switchdev_trans *trans); -+ struct switchdev_trans *trans, struct netlink_ext_ack *extack); - int dsa_port_vlan_add(struct dsa_port *dp, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans); ---- a/net/dsa/port.c -+++ b/net/dsa/port.c -@@ -145,7 +145,7 @@ int dsa_port_bridge_join(struct dsa_port - int err; - - /* Set the flooding mode before joining the port in the switch */ -- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL); -+ err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL); - if (err) - return err; - -@@ -158,7 +158,7 @@ int dsa_port_bridge_join(struct dsa_port - - /* The bridging is rolled back on error */ - if (err) { -- dsa_port_bridge_flags(dp, 0, NULL); -+ dsa_port_bridge_flags(dp, 0, NULL, NULL); - dp->bridge_dev = NULL; - } - -@@ -185,7 +185,7 @@ void dsa_port_bridge_leave(struct dsa_po - pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); - - /* Port is leaving the bridge, disable flooding */ -- dsa_port_bridge_flags(dp, 0, NULL); -+ dsa_port_bridge_flags(dp, 0, NULL, NULL); - - /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, - * so allow it to be in BR_STATE_FORWARDING to be kept functional -@@ -333,44 +333,44 @@ int dsa_port_ageing_time(struct dsa_port - } - - int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, -- struct switchdev_trans *trans) -+ struct switchdev_trans *trans, struct netlink_ext_ack *extack) - { - struct dsa_switch *ds = dp->ds; - -- if (!ds->ops->port_egress_floods || -- (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) -+ if (!ds->ops->port_pre_bridge_flags) - return -EINVAL; - -- return 0; -+ return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); - } - - int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, -- struct switchdev_trans *trans) -+ struct switchdev_trans *trans, struct netlink_ext_ack *extack) - { - struct dsa_switch *ds = dp->ds; -- int port = dp->index; -- int err = 0; - - if (switchdev_trans_ph_prepare(trans)) - return 0; - -- if (ds->ops->port_egress_floods) -- err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, -- flags & BR_MCAST_FLOOD); -+ if (!ds->ops->port_bridge_flags) -+ return -EINVAL; -+ -+ return ds->ops->port_bridge_flags(ds, dp->index, flags, extack); - -- return err; - } - - int dsa_port_mrouter(struct dsa_port *dp, bool mrouter, -- struct switchdev_trans *trans) -+ struct switchdev_trans *trans, -+ struct netlink_ext_ack *extack) - { - struct dsa_switch *ds = dp->ds; -- int port = dp->index; - - if (switchdev_trans_ph_prepare(trans)) -- return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP; -+ return ds->ops->port_set_mrouter ? 0 : -EOPNOTSUPP; -+ -+ if (!ds->ops->port_set_mrouter) -+ return -EOPNOTSUPP; - -- return ds->ops->port_egress_floods(ds, port, true, mrouter); -+ return ds->ops->port_set_mrouter(ds, dp->index, mrouter, extack); - } - - int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, ---- a/net/dsa/slave.c -+++ b/net/dsa/slave.c -@@ -290,13 +290,13 @@ static int dsa_slave_port_attr_set(struc - break; - case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: - ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags, -- trans); -+ trans, NULL); - break; - case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: -- ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans); -+ ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans, NULL); - break; - case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER: -- ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans); -+ ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans, NULL); - break; - default: - ret = -EOPNOTSUPP; diff --git a/target/linux/realtek/patches-5.15/709-lag-offloading.patch b/target/linux/realtek/patches-5.15/709-lag-offloading.patch deleted file mode 100644 index f84687ff4ac..00000000000 --- a/target/linux/realtek/patches-5.15/709-lag-offloading.patch +++ /dev/null @@ -1,781 +0,0 @@ -From afa3ab54c03d5126b14651f367b38165fab5b3cc Mon Sep 17 00:00:00 2001 -From: Birger Koblitz -Date: Tue, 18 Jan 2022 17:18:43 +0100 -Subject: [PATCH] realtek: Backport bridge configuration for DSA - -Adds the DSA API for bridge configuration (flooding, L2 learning, -and aging) offload as found in Linux 5.12 so that we can implement -it in our drivver. - -Submitted-by: Sebastian Gottschall -Submitted-by: Birger Koblitz ---- - drivers/net/bonding/bond_main.c | 2 ++ - include/net/dsa.h | 79 ++++++++++++++++- - net/dsa/dsa2.c | 88 +++++++++++++++++++ - net/dsa/dsa_priv.h | 74 ++++++++++++++ - net/dsa/port.c | 92 ++++++++++++++++++++ - net/dsa/slave.c | 88 ++++++++++++++++--- - net/dsa/switch.c | 49 ++++++++++ - net/sda/tag_dsa.c | 13 +++++- - 8 file changed, 460 insertions(+), 25 deletions(-) - ---- a/drivers/net/bonding/bond_main.c -+++ b/drivers/net/bonding/bond_main.c -@@ -2045,6 +2045,8 @@ int bond_enslave(struct net_device *bond - goto err_unregister; - } - -+ bond_lower_state_changed(new_slave); -+ - res = bond_sysfs_slave_add(new_slave); - if (res) { - slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res); ---- a/include/net/dsa.h -+++ b/include/net/dsa.h -@@ -149,8 +149,41 @@ struct dsa_switch_tree { - - /* List of DSA links composing the routing table */ - struct list_head rtable; -+ -+ /* Maps offloaded LAG netdevs to a zero-based linear ID for -+ * drivers that need it. -+ */ -+ struct net_device **lags; -+ unsigned int lags_len; - }; - -+#define dsa_lags_foreach_id(_id, _dst) \ -+ for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++) \ -+ if ((_dst)->lags[(_id)]) -+ -+#define dsa_lag_foreach_port(_dp, _dst, _lag) \ -+ list_for_each_entry((_dp), &(_dst)->ports, list) \ -+ if ((_dp)->lag_dev == (_lag)) -+ -+static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst, -+ unsigned int id) -+{ -+ return dst->lags[id]; -+} -+ -+static inline int dsa_lag_id(struct dsa_switch_tree *dst, -+ struct net_device *lag) -+{ -+ unsigned int id; -+ -+ dsa_lags_foreach_id(id, dst) { -+ if (dsa_lag_dev(dst, id) == lag) -+ return id; -+ } -+ -+ return -ENODEV; -+} -+ - /* TC matchall action types */ - enum dsa_port_mall_action_type { - DSA_PORT_MALL_MIRROR, -@@ -220,6 +253,8 @@ struct dsa_port { - bool devlink_port_setup; - struct phylink *pl; - struct phylink_config pl_config; -+ struct net_device *lag_dev; -+ bool lag_tx_enabled; - - struct list_head list; - -@@ -340,6 +375,14 @@ struct dsa_switch { - */ - bool mtu_enforcement_ingress; - -+ /* Drivers that benefit from having an ID associated with each -+ * offloaded LAG should set this to the maximum number of -+ * supported IDs. DSA will then maintain a mapping of _at -+ * least_ these many IDs, accessible to drivers via -+ * dsa_lag_id(). -+ */ -+ unsigned int num_lag_ids; -+ - size_t num_ports; - }; - -@@ -432,6 +475,18 @@ static inline bool dsa_port_is_vlan_filt - return dp->vlan_filtering; - } - -+static inline -+struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp) -+{ -+ if (!dp->bridge_dev) -+ return NULL; -+ -+ if (dp->lag_dev) -+ return dp->lag_dev; -+ -+ return dp->slave; -+} -+ - typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid, - bool is_static, void *data); - struct dsa_switch_ops { -@@ -629,6 +684,13 @@ struct dsa_switch_ops { - void (*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index, - int sw_index, int port, - struct net_device *br); -+ int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index, -+ int port); -+ int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index, -+ int port, struct net_device *lag, -+ struct netdev_lag_upper_info *info); -+ int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index, -+ int port, struct net_device *lag); - - /* - * PTP functionality -@@ -660,6 +722,16 @@ struct dsa_switch_ops { - int (*port_change_mtu)(struct dsa_switch *ds, int port, - int new_mtu); - int (*port_max_mtu)(struct dsa_switch *ds, int port); -+ -+ /* -+ * LAG integration -+ */ -+ int (*port_lag_change)(struct dsa_switch *ds, int port); -+ int (*port_lag_join)(struct dsa_switch *ds, int port, -+ struct net_device *lag, -+ struct netdev_lag_upper_info *info); -+ int (*port_lag_leave)(struct dsa_switch *ds, int port, -+ struct net_device *lag); - }; - - #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \ ---- a/net/dsa/dsa.c -+++ b/net/dsa/dsa.c -@@ -220,11 +220,21 @@ static int dsa_switch_rcv(struct sk_buff - } - - skb = nskb; -- p = netdev_priv(skb->dev); - skb_push(skb, ETH_HLEN); - skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, skb->dev); - -+ if (unlikely(!dsa_slave_dev_check(skb->dev))) { -+ /* Packet is to be injected directly on an upper -+ * device, e.g. a team/bond, so skip all DSA-port -+ * specific actions. -+ */ -+ netif_rx(skb); -+ return 0; -+ } -+ -+ p = netdev_priv(skb->dev); -+ - if (unlikely(cpu_dp->ds->untag_bridge_pvid)) { - nskb = dsa_untag_bridge_pvid(skb); - if (!nskb) { ---- a/net/dsa/dsa2.c -+++ b/net/dsa/dsa2.c -@@ -21,6 +21,65 @@ - static DEFINE_MUTEX(dsa2_mutex); - LIST_HEAD(dsa_tree_list); - -+/** -+ * dsa_lag_map() - Map LAG netdev to a linear LAG ID -+ * @dst: Tree in which to record the mapping. -+ * @lag: Netdev that is to be mapped to an ID. -+ * -+ * dsa_lag_id/dsa_lag_dev can then be used to translate between the -+ * two spaces. The size of the mapping space is determined by the -+ * driver by setting ds->num_lag_ids. It is perfectly legal to leave -+ * it unset if it is not needed, in which case these functions become -+ * no-ops. -+ */ -+void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag) -+{ -+ unsigned int id; -+ -+ if (dsa_lag_id(dst, lag) >= 0) -+ /* Already mapped */ -+ return; -+ -+ for (id = 0; id < dst->lags_len; id++) { -+ if (!dsa_lag_dev(dst, id)) { -+ dst->lags[id] = lag; -+ return; -+ } -+ } -+ -+ /* No IDs left, which is OK. Some drivers do not need it. The -+ * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id -+ * returns an error for this device when joining the LAG. The -+ * driver can then return -EOPNOTSUPP back to DSA, which will -+ * fall back to a software LAG. -+ */ -+} -+ -+/** -+ * dsa_lag_unmap() - Remove a LAG ID mapping -+ * @dst: Tree in which the mapping is recorded. -+ * @lag: Netdev that was mapped. -+ * -+ * As there may be multiple users of the mapping, it is only removed -+ * if there are no other references to it. -+ */ -+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag) -+{ -+ struct dsa_port *dp; -+ unsigned int id; -+ -+ dsa_lag_foreach_port(dp, dst, lag) -+ /* There are remaining users of this mapping */ -+ return; -+ -+ dsa_lags_foreach_id(id, dst) { -+ if (dsa_lag_dev(dst, id) == lag) { -+ dst->lags[id] = NULL; -+ break; -+ } -+ } -+} -+ - struct dsa_switch *dsa_switch_find(int tree_index, int sw_index) - { - struct dsa_switch_tree *dst; -@@ -597,6 +656,32 @@ static void dsa_tree_teardown_master(str - dsa_master_teardown(dp->master); - } - -+static int dsa_tree_setup_lags(struct dsa_switch_tree *dst) -+{ -+ unsigned int len = 0; -+ struct dsa_port *dp; -+ -+ list_for_each_entry(dp, &dst->ports, list) { -+ if (dp->ds->num_lag_ids > len) -+ len = dp->ds->num_lag_ids; -+ } -+ -+ if (!len) -+ return 0; -+ -+ dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL); -+ if (!dst->lags) -+ return -ENOMEM; -+ -+ dst->lags_len = len; -+ return 0; -+} -+ -+static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst) -+{ -+ kfree(dst->lags); -+} -+ - static int dsa_tree_setup(struct dsa_switch_tree *dst) - { - bool complete; -@@ -624,12 +709,18 @@ static int dsa_tree_setup(struct dsa_swi - if (err) - goto teardown_switches; - -+ err = dsa_tree_setup_lags(dst); -+ if (err) -+ goto teardown_master; -+ - dst->setup = true; - - pr_info("DSA: tree %d setup\n", dst->index); - - return 0; - -+teardown_master: -+ dsa_tree_teardown_master(dst); - teardown_switches: - dsa_tree_teardown_switches(dst); - teardown_default_cpu: -@@ -645,6 +736,8 @@ static void dsa_tree_teardown(struct dsa - if (!dst->setup) - return; - -+ dsa_tree_teardown_lags(dst); -+ - dsa_tree_teardown_master(dst); - - dsa_tree_teardown_switches(dst); ---- a/net/dsa/dsa_priv.h -+++ b/net/dsa/dsa_priv.h -@@ -20,6 +20,9 @@ enum { - DSA_NOTIFIER_BRIDGE_LEAVE, - DSA_NOTIFIER_FDB_ADD, - DSA_NOTIFIER_FDB_DEL, -+ DSA_NOTIFIER_LAG_CHANGE, -+ DSA_NOTIFIER_LAG_JOIN, -+ DSA_NOTIFIER_LAG_LEAVE, - DSA_NOTIFIER_MDB_ADD, - DSA_NOTIFIER_MDB_DEL, - DSA_NOTIFIER_VLAN_ADD, -@@ -57,6 +60,15 @@ struct dsa_notifier_mdb_info { - int port; - }; - -+/* DSA_NOTIFIER_LAG_* */ -+struct dsa_notifier_lag_info { -+ struct net_device *lag; -+ int sw_index; -+ int port; -+ -+ struct netdev_lag_upper_info *info; -+}; -+ - /* DSA_NOTIFIER_VLAN_* */ - struct dsa_notifier_vlan_info { - const struct switchdev_obj_port_vlan *vlan; -@@ -149,6 +161,11 @@ void dsa_port_disable_rt(struct dsa_port - void dsa_port_disable(struct dsa_port *dp); - int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br); - void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br); -+int dsa_port_lag_change(struct dsa_port *dp, -+ struct netdev_lag_lower_state_info *linfo); -+int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev, -+ struct netdev_lag_upper_info *uinfo); -+void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev); - int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, - struct switchdev_trans *trans); - bool dsa_port_skip_vlan_configuration(struct dsa_port *dp); -@@ -181,6 +198,71 @@ int dsa_port_link_register_of(struct dsa - void dsa_port_link_unregister_of(struct dsa_port *dp); - extern const struct phylink_mac_ops dsa_port_phylink_mac_ops; - -+static inline bool dsa_port_offloads_netdev(struct dsa_port *dp, -+ struct net_device *dev) -+{ -+ /* Switchdev offloading can be configured on: */ -+ -+ if (dev == dp->slave) -+ /* DSA ports directly connected to a bridge, and event -+ * was emitted for the ports themselves. -+ */ -+ return true; -+ -+ if (dp->bridge_dev == dev) -+ /* DSA ports connected to a bridge, and event was emitted -+ * for the bridge. -+ */ -+ return true; -+ -+ if (dp->lag_dev == dev) -+ /* DSA ports connected to a bridge via a LAG */ -+ return true; -+ -+ return false; -+} -+ -+static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp, -+ struct net_device *dev) -+{ -+ return dsa_port_to_bridge_port(dp) == dev; -+} -+ -+static inline bool dsa_port_offloads_bridge(struct dsa_port *dp, -+ struct net_device *bridge_dev) -+{ -+ /* DSA ports connected to a bridge, and event was emitted -+ * for the bridge. -+ */ -+ return dp->bridge_dev == bridge_dev; -+} -+ -+/* Returns true if any port of this tree offloads the given net_device */ -+static inline bool dsa_tree_offloads_bridge_port(struct dsa_switch_tree *dst, -+ struct net_device *dev) -+{ -+ struct dsa_port *dp; -+ -+ list_for_each_entry(dp, &dst->ports, list) -+ if (dsa_port_offloads_bridge_port(dp, dev)) -+ return true; -+ -+ return false; -+} -+ -+/* Returns true if any port of this tree offloads the given net_device */ -+static inline bool dsa_tree_offloads_netdev(struct dsa_switch_tree *dst, -+ struct net_device *dev) -+{ -+ struct dsa_port *dp; -+ -+ list_for_each_entry(dp, &dst->ports, list) -+ if (dsa_port_offloads_netdev(dp, dev)) -+ return true; -+ -+ return false; -+} -+ - /* slave.c */ - extern const struct dsa_device_ops notag_netdev_ops; - void dsa_slave_mii_bus_init(struct dsa_switch *ds); -@@ -285,6 +367,9 @@ int dsa_switch_register_notifier(struct - void dsa_switch_unregister_notifier(struct dsa_switch *ds); - - /* dsa2.c */ -+void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag); -+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag); -+ - extern struct list_head dsa_tree_list; - - #endif ---- a/net/dsa/port.c -+++ b/net/dsa/port.c -@@ -193,6 +193,99 @@ void dsa_port_bridge_leave(struct dsa_po - dsa_port_set_state_now(dp, BR_STATE_FORWARDING); - } - -+int dsa_port_lag_change(struct dsa_port *dp, -+ struct netdev_lag_lower_state_info *linfo) -+{ -+ struct dsa_notifier_lag_info info = { -+ .sw_index = dp->ds->index, -+ .port = dp->index, -+ }; -+ bool tx_enabled; -+ -+ if (!dp->lag_dev) -+ return 0; -+ -+ /* On statically configured aggregates (e.g. loadbalance -+ * without LACP) ports will always be tx_enabled, even if the -+ * link is down. Thus we require both link_up and tx_enabled -+ * in order to include it in the tx set. -+ */ -+ tx_enabled = linfo->link_up && linfo->tx_enabled; -+ -+ if (tx_enabled == dp->lag_tx_enabled) -+ return 0; -+ -+ dp->lag_tx_enabled = tx_enabled; -+ -+ return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info); -+} -+ -+int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag, -+ struct netdev_lag_upper_info *uinfo) -+{ -+ struct dsa_notifier_lag_info info = { -+ .sw_index = dp->ds->index, -+ .port = dp->index, -+ .lag = lag, -+ .info = uinfo, -+ }; -+ struct net_device *bridge_dev; -+ int err; -+ -+ dsa_lag_map(dp->ds->dst, lag); -+ dp->lag_dev = lag; -+ -+ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info); -+ if (err) -+ goto err_lag_join; -+ -+ bridge_dev = netdev_master_upper_dev_get(lag); -+ if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) -+ return 0; -+ -+ err = dsa_port_bridge_join(dp, bridge_dev); -+ if (err) -+ goto err_bridge_join; -+ -+ return 0; -+ -+err_bridge_join: -+ dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); -+err_lag_join: -+ dp->lag_dev = NULL; -+ dsa_lag_unmap(dp->ds->dst, lag); -+ return err; -+} -+ -+void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag) -+{ -+ struct dsa_notifier_lag_info info = { -+ .sw_index = dp->ds->index, -+ .port = dp->index, -+ .lag = lag, -+ }; -+ int err; -+ -+ if (!dp->lag_dev) -+ return; -+ -+ /* Port might have been part of a LAG that in turn was -+ * attached to a bridge. -+ */ -+ if (dp->bridge_dev) -+ dsa_port_bridge_leave(dp, dp->bridge_dev); -+ -+ dp->lag_tx_enabled = false; -+ dp->lag_dev = NULL; -+ -+ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info); -+ if (err) -+ pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n", -+ err); -+ -+ dsa_lag_unmap(dp->ds->dst, lag); -+} -+ - /* Must be called under rcu_read_lock() */ - static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, - bool vlan_filtering) ---- a/net/dsa/slave.c -+++ b/net/dsa/slave.c -@@ -337,9 +337,6 @@ static int dsa_slave_vlan_add(struct net - struct switchdev_obj_port_vlan vlan; - int vid, err; - -- if (obj->orig_dev != dev) -- return -EOPNOTSUPP; -- - if (dsa_port_skip_vlan_configuration(dp)) - return 0; - -@@ -394,11 +391,13 @@ static int dsa_slave_port_obj_add(struct - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_MDB: -- if (obj->orig_dev != dev) -+ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) - return -EOPNOTSUPP; - err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); - break; - case SWITCHDEV_OBJ_ID_HOST_MDB: -+ if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; - /* DSA can directly translate this to a normal MDB add, - * but on the CPU port. - */ -@@ -406,6 +405,9 @@ static int dsa_slave_port_obj_add(struct - trans); - break; - case SWITCHDEV_OBJ_ID_PORT_VLAN: -+ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; -+ - err = dsa_slave_vlan_add(dev, obj, trans); - break; - default: -@@ -424,9 +426,6 @@ static int dsa_slave_vlan_del(struct net - struct switchdev_obj_port_vlan *vlan; - int vid, err; - -- if (obj->orig_dev != dev) -- return -EOPNOTSUPP; -- - if (dsa_port_skip_vlan_configuration(dp)) - return 0; - -@@ -453,17 +452,22 @@ static int dsa_slave_port_obj_del(struct - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_MDB: -- if (obj->orig_dev != dev) -+ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) - return -EOPNOTSUPP; - err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); - break; - case SWITCHDEV_OBJ_ID_HOST_MDB: -+ if (!dsa_port_offloads_bridge(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; - /* DSA can directly translate this to a normal MDB add, - * but on the CPU port. - */ - err = dsa_port_mdb_del(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj)); - break; - case SWITCHDEV_OBJ_ID_PORT_VLAN: -+ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev)) -+ return -EOPNOTSUPP; -+ - err = dsa_slave_vlan_del(dev, obj); - break; - default: -@@ -1993,6 +1997,46 @@ static int dsa_slave_changeupper(struct - dsa_port_bridge_leave(dp, info->upper_dev); - err = NOTIFY_OK; - } -+ } else if (netif_is_lag_master(info->upper_dev)) { -+ if (info->linking) { -+ err = dsa_port_lag_join(dp, info->upper_dev, -+ info->upper_info); -+ if (err == -EOPNOTSUPP) { -+ NL_SET_ERR_MSG_MOD(info->info.extack, -+ "Offloading not supported"); -+ err = 0; -+ } -+ err = notifier_from_errno(err); -+ } else { -+ dsa_port_lag_leave(dp, info->upper_dev); -+ err = NOTIFY_OK; -+ } -+ } -+ -+ return err; -+} -+ -+static int -+dsa_slave_lag_changeupper(struct net_device *dev, -+ struct netdev_notifier_changeupper_info *info) -+{ -+ struct net_device *lower; -+ struct list_head *iter; -+ int err = NOTIFY_DONE; -+ struct dsa_port *dp; -+ -+ netdev_for_each_lower_dev(dev, lower, iter) { -+ if (!dsa_slave_dev_check(lower)) -+ continue; -+ -+ dp = dsa_slave_to_port(lower); -+ if (!dp->lag_dev) -+ /* Software LAG */ -+ continue; -+ -+ err = dsa_slave_changeupper(lower, info); -+ if (notifier_to_errno(err)) -+ break; - } - - return err; -@@ -2078,10 +2122,26 @@ static int dsa_slave_netdevice_event(str - break; - } - case NETDEV_CHANGEUPPER: -+ if (dsa_slave_dev_check(dev)) -+ return dsa_slave_changeupper(dev, ptr); -+ -+ if (netif_is_lag_master(dev)) -+ return dsa_slave_lag_changeupper(dev, ptr); -+ -+ break; -+ case NETDEV_CHANGELOWERSTATE: { -+ struct netdev_notifier_changelowerstate_info *info = ptr; -+ struct dsa_port *dp; -+ int err; -+ - if (!dsa_slave_dev_check(dev)) -- return NOTIFY_DONE; -+ break; - -- return dsa_slave_changeupper(dev, ptr); -+ dp = dsa_slave_to_port(dev); -+ -+ err = dsa_port_lag_change(dp, info->lower_state_info); -+ return notifier_from_errno(err); -+ } - } - - return NOTIFY_DONE; -@@ -2229,6 +2289,15 @@ static int dsa_slave_switchdev_event(str - if (!fdb_info->added_by_user && - !dp->ds->assisted_learning_on_cpu_port) - return NOTIFY_DONE; -+ -+ /* When the bridge learns an address on an offloaded -+ * LAG we don't want to send traffic to the CPU, the -+ * other ports bridged with the LAG should be able to -+ * autonomously forward towards it. -+ */ -+ if (dsa_tree_offloads_netdev(dp->ds->dst, dev)) -+ return NOTIFY_DONE; -+ - } - - if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del) ---- a/net/dsa/switch.c -+++ b/net/dsa/switch.c -@@ -193,6 +193,47 @@ static int dsa_switch_fdb_del(struct dsa - return ds->ops->port_fdb_del(ds, port, info->addr, info->vid); - } - -+static int dsa_switch_lag_change(struct dsa_switch *ds, -+ struct dsa_notifier_lag_info *info) -+{ -+ if (ds->index == info->sw_index && ds->ops->port_lag_change) -+ return ds->ops->port_lag_change(ds, info->port); -+ -+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_change) -+ return ds->ops->crosschip_lag_change(ds, info->sw_index, -+ info->port); -+ -+ return 0; -+} -+ -+static int dsa_switch_lag_join(struct dsa_switch *ds, -+ struct dsa_notifier_lag_info *info) -+{ -+ if (ds->index == info->sw_index && ds->ops->port_lag_join) -+ return ds->ops->port_lag_join(ds, info->port, info->lag, -+ info->info); -+ -+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_join) -+ return ds->ops->crosschip_lag_join(ds, info->sw_index, -+ info->port, info->lag, -+ info->info); -+ -+ return -EOPNOTSUPP; -+} -+ -+static int dsa_switch_lag_leave(struct dsa_switch *ds, -+ struct dsa_notifier_lag_info *info) -+{ -+ if (ds->index == info->sw_index && ds->ops->port_lag_leave) -+ return ds->ops->port_lag_leave(ds, info->port, info->lag); -+ -+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave) -+ return ds->ops->crosschip_lag_leave(ds, info->sw_index, -+ info->port, info->lag); -+ -+ return -EOPNOTSUPP; -+} -+ - static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port, - struct dsa_notifier_mdb_info *info) - { -@@ -340,6 +381,15 @@ static int dsa_switch_event(struct notif - case DSA_NOTIFIER_FDB_DEL: - err = dsa_switch_fdb_del(ds, info); - break; -+ case DSA_NOTIFIER_LAG_CHANGE: -+ err = dsa_switch_lag_change(ds, info); -+ break; -+ case DSA_NOTIFIER_LAG_JOIN: -+ err = dsa_switch_lag_join(ds, info); -+ break; -+ case DSA_NOTIFIER_LAG_LEAVE: -+ err = dsa_switch_lag_leave(ds, info); -+ break; - case DSA_NOTIFIER_MDB_ADD: - err = dsa_switch_mdb_add(ds, info); - break; ---- a/net/dsa/tag_dsa.c -+++ b/net/dsa/tag_dsa.c -@@ -82,7 +82,19 @@ static struct sk_buff *dsa_rcv(struct sk - source_device = dsa_header[0] & 0x1f; - source_port = (dsa_header[1] >> 3) & 0x1f; - -- skb->dev = dsa_master_find_slave(dev, source_device, source_port); -+ if (trunk) { -+ struct dsa_port *cpu_dp = dev->dsa_ptr; -+ -+ /* The exact source port is not available in the tag, -+ * so we inject the frame directly on the upper -+ * team/bond. -+ */ -+ skb->dev = dsa_lag_dev(cpu_dp->dst, source_port); -+ } else { -+ skb->dev = dsa_master_find_slave(dev, source_device, -+ source_port); -+ } -+ - if (!skb->dev) - return NULL; - diff --git a/target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch b/target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch deleted file mode 100644 index a3bfec59ab7..00000000000 --- a/target/linux/realtek/patches-5.15/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch +++ /dev/null @@ -1,148 +0,0 @@ -From: Vladimir Oltean -Date: Fri, 12 Feb 2021 17:15:54 +0200 -Subject: [PATCH] net: dsa: configure better brport flags when ports leave the - bridge - -Bugfixed version of upstream commit 5e38c15856e9 ("net: dsa: configure -better brport flags when ports leave the bridge") - -For a DSA switch port operating in standalone mode, address learning -doesn't make much sense since that is a bridge function. In fact, -address learning even breaks setups such as this one: - - +---------------------------------------------+ - | | - | +-------------------+ | - | | br0 | send receive | - | +--------+-+--------+ +--------+ +--------+ | - | | | | | | | | | | - | | swp0 | | swp1 | | swp2 | | swp3 | | - | | | | | | | | | | - +-+--------+-+--------+-+--------+-+--------+-+ - | ^ | ^ - | | | | - | +-----------+ | - | | - +--------------------------------+ - -because if the switch has a single FDB (can offload a single bridge) -then source address learning on swp3 can "steal" the source MAC address -of swp2 from br0's FDB, because learning frames coming from swp2 will be -done twice: first on the swp1 ingress port, second on the swp3 ingress -port. So the hardware FDB will become out of sync with the software -bridge, and when swp2 tries to send one more packet towards swp1, the -ASIC will attempt to short-circuit the forwarding path and send it -directly to swp3 (since that's the last port it learned that address on), -which it obviously can't, because swp3 operates in standalone mode. - -So DSA drivers operating in standalone mode should still configure a -list of bridge port flags even when they are standalone. Currently DSA -attempts to call dsa_port_bridge_flags with 0, which disables egress -flooding of unknown unicast and multicast, something which doesn't make -much sense. For the switches that implement .port_egress_floods - b53 -and mv88e6xxx, it probably doesn't matter too much either, since they -can possibly inject traffic from the CPU into a standalone port, -regardless of MAC DA, even if egress flooding is turned off for that -port, but certainly not all DSA switches can do that - sja1105, for -example, can't. So it makes sense to use a better common default there, -such as "flood everything". - -It should also be noted that what DSA calls "dsa_port_bridge_flags()" -is a degenerate name for just calling .port_egress_floods(), since -nothing else is implemented - not learning, in particular. But disabling -address learning, something that this driver is also coding up for, will -be supported by individual drivers once .port_egress_floods is replaced -with a more generic .port_bridge_flags. - -Previous attempts to code up this logic have been in the common bridge -layer, but as pointed out by Ido Schimmel, there are corner cases that -are missed when doing that: -https://patchwork.kernel.org/project/netdevbpf/patch/20210209151936.97382-5-olteanv@gmail.com/ - -So, at least for now, let's leave DSA in charge of setting port flags -before and after the bridge join and leave. - -Signed-off-by: Vladimir Oltean -Reviewed-by: Florian Fainelli -Signed-off-by: David S. Miller -[ backport and bugfix: break dsa_port_bridge_flags() out of loop ] -Signed-off-by: Bjørn Mork ---- - net/dsa/port.c | 45 ++++++++++++++++++++++++++++++++++++++------- - 1 file changed, 38 insertions(+), 7 deletions(-) - ---- a/net/dsa/port.c -+++ b/net/dsa/port.c -@@ -134,6 +134,27 @@ void dsa_port_disable(struct dsa_port *d - rtnl_unlock(); - } - -+static void dsa_port_change_brport_flags(struct dsa_port *dp, -+ bool bridge_offload) -+{ -+ unsigned long mask, flags; -+ int flag, err; -+ -+ mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; -+ if (bridge_offload) -+ flags = mask; -+ else -+ flags = mask & ~BR_LEARNING; -+ -+ for_each_set_bit(flag, &mask, 32) { -+ err = dsa_port_pre_bridge_flags(dp, BIT(flag), NULL, NULL); -+ if (err) -+ flags &= ~BIT(flag); -+ } -+ -+ dsa_port_bridge_flags(dp, flags, NULL, NULL); -+} -+ - int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) - { - struct dsa_notifier_bridge_info info = { -@@ -144,10 +165,10 @@ int dsa_port_bridge_join(struct dsa_port - }; - int err; - -- /* Set the flooding mode before joining the port in the switch */ -- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL); -- if (err) -- return err; -+ /* Notify the port driver to set its configurable flags in a way that -+ * matches the initial settings of a bridge port. -+ */ -+ dsa_port_change_brport_flags(dp, true); - - /* Here the interface is already bridged. Reflect the current - * configuration so that drivers can program their chips accordingly. -@@ -158,7 +179,7 @@ int dsa_port_bridge_join(struct dsa_port - - /* The bridging is rolled back on error */ - if (err) { -- dsa_port_bridge_flags(dp, 0, NULL, NULL); -+ dsa_port_change_brport_flags(dp, false); - dp->bridge_dev = NULL; - } - -@@ -184,8 +205,18 @@ void dsa_port_bridge_leave(struct dsa_po - if (err) - pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); - -- /* Port is leaving the bridge, disable flooding */ -- dsa_port_bridge_flags(dp, 0, NULL, NULL); -+ /* Configure the port for standalone mode (no address learning, -+ * flood everything). -+ * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events -+ * when the user requests it through netlink or sysfs, but not -+ * automatically at port join or leave, so we need to handle resetting -+ * the brport flags ourselves. But we even prefer it that way, because -+ * otherwise, some setups might never get the notification they need, -+ * for example, when a port leaves a LAG that offloads the bridge, -+ * it becomes standalone, but as far as the bridge is concerned, no -+ * port ever left. -+ */ -+ dsa_port_change_brport_flags(dp, false); - - /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, - * so allow it to be in BR_STATE_FORWARDING to be kept functional From aa528eec7317cd43bf6a8b71d38df8494926798f Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Sat, 10 Sep 2022 01:15:22 +0900 Subject: [PATCH 046/110] realtek: refresh patches in 5.15 Adjust patches for kernel 5.15. Signed-off-by: INAGAKI Hiroshi --- ...hdog-add-realtek-otto-watchdog-timer.patch | 6 +-- ...pers-to-extract-clause-45-regad-and-.patch | 2 +- ...k-otto-Support-reversed-port-layouts.patch | 4 +- ...ltek-otto-Support-per-cpu-interrupts.patch | 8 +-- ...pio-realtek-otto-Add-RTL930x-support.patch | 2 +- ...pio-realtek-otto-Add-RTL931x-support.patch | 2 +- .../300-mips-add-rtl838x-platform.patch | 8 +-- .../301-gpio-add-rtl8231-driver.patch | 10 ++-- .../302-clocksource-add-otto-driver.patch | 4 +- ...e-dependencies-for-gpio-realtek-otto.patch | 2 +- ...pdate-dependency-for-spi-realtek-rtl.patch | 4 +- ...pdate-dependency-for-irq-realtek-rtl.patch | 5 +- ...date-dependency-for-realtek-otto-wdt.patch | 2 +- .../310-add-i2c-rtl9300-support.patch | 4 +- .../patches-5.15/312-rt9313-support.patch | 2 +- ...chip-irq-realtek-rtl-add-VPE-support.patch | 43 +++++++--------- .../316-otto-gpio-uniprocessor-irq-mask.patch | 4 +- ...io-realtek-otto-switch-to-32-bit-I-O.patch | 25 ++++------ .../318-add-rtl83xx-clk-support.patch | 10 ++-- ...hip-irq-realtek-rtl-fix-VPE-affinity.patch | 2 +- ...t-dsa-add-support-for-rtl838x-switch.patch | 14 +++--- ...-add-rtl838x-support-for-tag-trailer.patch | 2 +- ...net-add-support-for-rtl838x-ethernet.patch | 6 +-- ...nclude-linux-add-phy-ops-for-rtl838x.patch | 2 +- ...vers-net-phy-eee-support-for-rtl838x.patch | 6 +-- ...04-include-linux-add-phy-hsgmii-mode.patch | 4 +- .../patches-5.15/705-add-rtl-phy.patch | 4 +- ...rease-phy-address-number-for-rtl839x.patch | 2 +- ...sfp-re-probe-modules-on-DEV_UP-event.patch | 2 +- ...11-net-phy-add-an-MDIO-SMBus-library.patch | 4 +- ...12-net-phy-sfp-add-support-for-SMBus.patch | 2 +- ...rt-hardware-assisted-indirect-access.patch | 50 +++++++++---------- 32 files changed, 121 insertions(+), 126 deletions(-) diff --git a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch index 845441b4257..d5737d17367 100644 --- a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch +++ b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch @@ -32,7 +32,7 @@ Signed-off-by: Guenter Roeck --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -14816,6 +14816,13 @@ S: Maintained +@@ -15887,6 +15887,13 @@ S: Maintained F: include/sound/rt*.h F: sound/soc/codecs/rt* @@ -48,7 +48,7 @@ Signed-off-by: Guenter Roeck S: Maintained --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig -@@ -995,6 +995,19 @@ config RTD119X_WATCHDOG +@@ -954,6 +954,19 @@ config RTD119X_WATCHDOG Say Y here to include support for the watchdog timer in Realtek RTD1295 SoCs. @@ -70,7 +70,7 @@ Signed-off-by: Guenter Roeck depends on ARCH_SPRD || COMPILE_TEST --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile -@@ -174,6 +174,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o +@@ -171,6 +171,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o diff --git a/target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch b/target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch index ef2e225717e..da33aaa72f3 100644 --- a/target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch +++ b/target/linux/realtek/patches-5.15/020-v5.17-net-mdio-add-helpers-to-extract-clause-45-regad-and-.patch @@ -34,7 +34,7 @@ Signed-off-by: David S. Miller #define MII_REGADDR_C45_MASK GENMASK(15, 0) struct gpio_desc; -@@ -342,6 +344,16 @@ static inline u32 mdiobus_c45_addr(int d +@@ -355,6 +357,16 @@ static inline u32 mdiobus_c45_addr(int d return MII_ADDR_C45 | devad << MII_DEVADDR_C45_SHIFT | regnum; } diff --git a/target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch b/target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch index 8f353514388..437e7862d91 100644 --- a/target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch +++ b/target/linux/realtek/patches-5.15/021-v5.19-02-gpio-realtek-otto-Support-reversed-port-layouts.patch @@ -91,7 +91,7 @@ Signed-off-by: Bartosz Golaszewski } /* Set the rising and falling edge mask bits for a GPIO port pin */ -@@ -253,6 +289,7 @@ MODULE_DEVICE_TABLE(of, realtek_gpio_of_ +@@ -250,6 +286,7 @@ MODULE_DEVICE_TABLE(of, realtek_gpio_of_ static int realtek_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -99,7 +99,7 @@ Signed-off-by: Bartosz Golaszewski unsigned int dev_flags; struct gpio_irq_chip *girq; struct realtek_gpio_ctrl *ctrl; -@@ -280,10 +317,20 @@ static int realtek_gpio_probe(struct pla +@@ -277,10 +314,20 @@ static int realtek_gpio_probe(struct pla raw_spin_lock_init(&ctrl->lock); diff --git a/target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch b/target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch index 8f5d571ea2f..b632095c362 100644 --- a/target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch +++ b/target/linux/realtek/patches-5.15/021-v5.19-03-gpio-realtek-otto-Support-per-cpu-interrupts.patch @@ -47,7 +47,7 @@ Signed-off-by: Bartosz Golaszewski }; static struct realtek_gpio_ctrl *irq_data_to_ctrl(struct irq_data *data) -@@ -250,14 +258,61 @@ static void realtek_gpio_irq_handler(str +@@ -247,14 +255,61 @@ static void realtek_gpio_irq_handler(str chained_irq_exit(irq_chip, desc); } @@ -109,7 +109,7 @@ Signed-off-by: Bartosz Golaszewski } return 0; -@@ -269,6 +324,7 @@ static struct irq_chip realtek_gpio_irq_ +@@ -266,6 +321,7 @@ static struct irq_chip realtek_gpio_irq_ .irq_mask = realtek_gpio_irq_mask, .irq_unmask = realtek_gpio_irq_unmask, .irq_set_type = realtek_gpio_irq_set_type, @@ -117,7 +117,7 @@ Signed-off-by: Bartosz Golaszewski }; static const struct of_device_id realtek_gpio_of_match[] = { -@@ -293,8 +349,10 @@ static int realtek_gpio_probe(struct pla +@@ -290,8 +346,10 @@ static int realtek_gpio_probe(struct pla unsigned int dev_flags; struct gpio_irq_chip *girq; struct realtek_gpio_ctrl *ctrl; @@ -129,7 +129,7 @@ Signed-off-by: Bartosz Golaszewski ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); if (!ctrl) -@@ -355,6 +413,21 @@ static int realtek_gpio_probe(struct pla +@@ -352,6 +410,21 @@ static int realtek_gpio_probe(struct pla girq->init_hw = realtek_gpio_irq_init; } diff --git a/target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch b/target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch index 536c85740a6..9b258031389 100644 --- a/target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch +++ b/target/linux/realtek/patches-5.15/021-v5.19-04-gpio-realtek-otto-Add-RTL930x-support.patch @@ -16,7 +16,7 @@ Signed-off-by: Bartosz Golaszewski --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c -@@ -338,6 +338,10 @@ static const struct of_device_id realtek +@@ -335,6 +335,10 @@ static const struct of_device_id realtek { .compatible = "realtek,rtl8390-gpio", }, diff --git a/target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch b/target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch index 16990ce053e..810856eb891 100644 --- a/target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch +++ b/target/linux/realtek/patches-5.15/021-v5.19-06-gpio-realtek-otto-Add-RTL931x-support.patch @@ -18,7 +18,7 @@ Signed-off-by: Bartosz Golaszewski --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c -@@ -342,6 +342,9 @@ static const struct of_device_id realtek +@@ -339,6 +339,9 @@ static const struct of_device_id realtek .compatible = "realtek,rtl9300-gpio", .data = (void *)(GPIO_PORTS_REVERSED | GPIO_INTERRUPTS_PER_CPU) }, diff --git a/target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch b/target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch index 3834ba7c619..3929096888b 100644 --- a/target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch +++ b/target/linux/realtek/patches-5.15/300-mips-add-rtl838x-platform.patch @@ -15,8 +15,8 @@ Submitted-by: Birger Koblitz --- a/arch/mips/Kbuild.platforms +++ b/arch/mips/Kbuild.platforms -@@ -23,6 +23,7 @@ platform-$(CONFIG_PIC32MZDA) += pic32/ - platform-$(CONFIG_MACH_PISTACHIO) += pistachio/ +@@ -23,6 +23,7 @@ platform-$(CONFIG_NLM_COMMON) += netlog + platform-$(CONFIG_PIC32MZDA) += pic32/ platform-$(CONFIG_RALINK) += ralink/ platform-$(CONFIG_MIKROTIK_RB532) += rb532/ +platform-$(CONFIG_RTL83XX) += rtl838x/ @@ -25,7 +25,7 @@ Submitted-by: Birger Koblitz platform-$(CONFIG_SGI_IP28) += sgi-ip22/ --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig -@@ -1037,8 +1037,58 @@ config NLM_XLP_BOARD +@@ -1053,8 +1053,58 @@ config NLM_XLP_BOARD This board is based on Netlogic XLP Processor. Say Y here if you have a XLP based board. @@ -84,7 +84,7 @@ Submitted-by: Birger Koblitz source "arch/mips/alchemy/Kconfig" source "arch/mips/ath25/Kconfig" source "arch/mips/ath79/Kconfig" -@@ -1097,6 +1147,9 @@ config CEVT_GT641XX +@@ -1112,6 +1162,9 @@ config CEVT_GT641XX config CEVT_R4K bool diff --git a/target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch b/target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch index 27256512438..7603a037442 100644 --- a/target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch +++ b/target/linux/realtek/patches-5.15/301-gpio-add-rtl8231-driver.patch @@ -25,9 +25,9 @@ Submitted-by: John Crispin --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig -@@ -508,6 +508,12 @@ config GPIO_REG - A 32-bit single register GPIO fixed in/out implementation. This - can be used to represent any register as a set of GPIO signals. +@@ -529,6 +529,12 @@ config GPIO_ROCKCHIP + help + Say yes here to support GPIO on Rockchip SoCs. +config GPIO_RTL8231 + tristate "RTL8231 GPIO" @@ -40,10 +40,10 @@ Submitted-by: John Crispin depends on MFD_SYSCON --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile -@@ -127,6 +127,7 @@ obj-$(CONFIG_GPIO_RDA) += gpio-rda.o - obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o +@@ -129,6 +129,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc3 obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o obj-$(CONFIG_GPIO_REG) += gpio-reg.o + obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o +obj-$(CONFIG_GPIO_RTL8231) += gpio-rtl8231.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o diff --git a/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch b/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch index 8b17ddaef00..fb1f4643561 100644 --- a/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch +++ b/target/linux/realtek/patches-5.15/302-clocksource-add-otto-driver.patch @@ -20,7 +20,7 @@ depends on HAS_IOMEM --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile -@@ -63,6 +63,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-mi +@@ -58,6 +58,7 @@ obj-$(CONFIG_MILBEAUT_TIMER) += timer-mi obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o obj-$(CONFIG_NPCM7XX_TIMER) += timer-npcm7xx.o obj-$(CONFIG_RDA_TIMER) += timer-rda.o @@ -30,7 +30,7 @@ obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h -@@ -133,6 +133,7 @@ enum cpuhp_state { +@@ -177,6 +177,7 @@ enum cpuhp_state { CPUHP_AP_MARCO_TIMER_STARTING, CPUHP_AP_MIPS_GIC_TIMER_STARTING, CPUHP_AP_ARC_TIMER_STARTING, diff --git a/target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch b/target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch index 5f0986b9051..beaddcb58e6 100644 --- a/target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch +++ b/target/linux/realtek/patches-5.15/303-gpio-update-dependencies-for-gpio-realtek-otto.patch @@ -13,7 +13,7 @@ Submitted-by: INAGAKI Hiroshi 1 file changed, 2 insertions(+), 2 deletions(-) --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig -@@ -491,8 +491,8 @@ config GPIO_RDA +@@ -503,8 +503,8 @@ config GPIO_RDA config GPIO_REALTEK_OTTO tristate "Realtek Otto GPIO support" diff --git a/target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch b/target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch index 23c5448aa2e..f2b57bacde9 100644 --- a/target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch +++ b/target/linux/realtek/patches-5.15/304-spi-update-dependency-for-spi-realtek-rtl.patch @@ -14,9 +14,9 @@ Submitted-by: INAGAKI Hiroshi --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile -@@ -94,7 +94,7 @@ obj-$(CONFIG_SPI_QCOM_QSPI) += spi-qcom - obj-$(CONFIG_SPI_QUP) += spi-qup.o +@@ -97,7 +97,7 @@ obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o + obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o -obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o +obj-$(CONFIG_RTL83XX) += spi-realtek-rtl.o diff --git a/target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch b/target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch index 0c1cc4235e6..a94a3ff8ac7 100644 --- a/target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch +++ b/target/linux/realtek/patches-5.15/305-irqchip-update-dependency-for-irq-realtek-rtl.patch @@ -14,9 +14,12 @@ Submitted-by: INAGAKI Hiroshi --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile -@@ -114,4 +114,4 @@ obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-l +@@ -112,7 +112,7 @@ obj-$(CONFIG_LOONGSON_PCH_PIC) += irq-l obj-$(CONFIG_LOONGSON_PCH_MSI) += irq-loongson-pch-msi.o obj-$(CONFIG_MST_IRQ) += irq-mst-intc.o obj-$(CONFIG_SL28CPLD_INTC) += irq-sl28cpld.o -obj-$(CONFIG_MACH_REALTEK_RTL) += irq-realtek-rtl.o +obj-$(CONFIG_RTL83XX) += irq-realtek-rtl.o + obj-$(CONFIG_WPCM450_AIC) += irq-wpcm450-aic.o + obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o + obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o diff --git a/target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch b/target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch index f92981340bb..b44aebea129 100644 --- a/target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch +++ b/target/linux/realtek/patches-5.15/307-wdt-update-dependency-for-realtek-otto-wdt.patch @@ -17,7 +17,7 @@ Tested-by: Stijn Tintel --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig -@@ -997,10 +997,10 @@ config RTD119X_WATCHDOG +@@ -956,10 +956,10 @@ config RTD119X_WATCHDOG config REALTEK_OTTO_WDT tristate "Realtek Otto MIPS watchdog support" diff --git a/target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch b/target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch index 4df9bf043b1..2125aea77f0 100644 --- a/target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch +++ b/target/linux/realtek/patches-5.15/310-add-i2c-rtl9300-support.patch @@ -17,7 +17,7 @@ Submitted-by: Birger Koblitz --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig -@@ -954,6 +954,16 @@ config I2C_RK3X +@@ -949,6 +949,16 @@ config I2C_RK3X This driver can also be built as a module. If so, the module will be called i2c-rk3x. @@ -36,7 +36,7 @@ Submitted-by: Birger Koblitz help --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile -@@ -95,6 +95,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom- +@@ -94,6 +94,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom- obj-$(CONFIG_I2C_QUP) += i2c-qup.o obj-$(CONFIG_I2C_RIIC) += i2c-riic.o obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o diff --git a/target/linux/realtek/patches-5.15/312-rt9313-support.patch b/target/linux/realtek/patches-5.15/312-rt9313-support.patch index 516bca2b6ef..67f5580cd64 100644 --- a/target/linux/realtek/patches-5.15/312-rt9313-support.patch +++ b/target/linux/realtek/patches-5.15/312-rt9313-support.patch @@ -16,7 +16,7 @@ Submitted-by: Birger Koblitz --- a/arch/mips/Makefile +++ b/arch/mips/Makefile -@@ -307,14 +307,24 @@ endif +@@ -310,14 +310,24 @@ endif KBUILD_AFLAGS += $(cflags-y) KBUILD_CFLAGS += $(cflags-y) diff --git a/target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch b/target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch index 26c36302c28..102c9850ce4 100644 --- a/target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch +++ b/target/linux/realtek/patches-5.15/315-irqchip-irq-realtek-rtl-add-VPE-support.patch @@ -83,7 +83,7 @@ Submitted-by: Birger Koblitz raw_spin_unlock_irqrestore(&irq_lock, flags); } -@@ -44,137 +86,247 @@ static void realtek_ictl_mask_irq(struct +@@ -44,52 +86,137 @@ static void realtek_ictl_mask_irq(struct { unsigned long flags; u32 value; @@ -144,19 +144,18 @@ Submitted-by: Birger Koblitz static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { -- irq_set_chip_and_handler(hw, &realtek_ictl_irq, handle_level_irq); + struct realtek_ictl_output *output = d->host_data; + unsigned long flags; + -+ irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq); -+ + irq_set_chip_and_handler(irq, &realtek_ictl_irq, handle_level_irq); + + raw_spin_lock_irqsave(&irq_lock, flags); + + output->child_mask |= BIT(hw); + write_irr(REG(RTL_ICTL_IRR0, 0), hw, output->output_index + 1); + + raw_spin_unlock_irqrestore(&irq_lock, flags); - ++ return 0; } @@ -208,37 +207,32 @@ Submitted-by: Birger Koblitz + struct realtek_ictl_output *output = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); - struct irq_domain *domain; -- unsigned int pending; + int cpu = smp_processor_id(); -+ unsigned long pending; -+ unsigned int soc_int; + unsigned long pending; + unsigned int soc_int; chained_irq_enter(chip, desc); - pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR)); + pending = readl(REG(RTL_ICTL_GIMR, cpu)) & readl(REG(RTL_ICTL_GISR, cpu)) + & output->child_mask; -+ + if (unlikely(!pending)) { spurious_interrupt(); goto out; } + - domain = irq_desc_get_handler_data(desc); -- generic_handle_irq(irq_find_mapping(domain, __ffs(pending))); -+ +- for_each_set_bit(soc_int, &pending, 32) +- generic_handle_domain_irq(domain, soc_int); + for_each_set_bit(soc_int, &pending, RTL_ICTL_NUM_INPUTS) -+ generic_handle_irq(irq_find_mapping(output->domain, soc_int)); -+// generic_handle_domain_irq(output->domain, soc_int); ++ generic_handle_domain_irq(output->domain, soc_int); out: chained_irq_exit(chip, desc); - } - --/* -- * SoC interrupts are cascaded to MIPS CPU interrupts according to the -- * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for -- * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts -- * thus go into 4 IRRs. -- */ +@@ -102,85 +229,110 @@ out: + * thus go into 4 IRRs. A routing value of '0' means the interrupt is left + * disconnected. Routing values {1..15} connect to output lines {0..14}. + */ -static int __init map_interrupts(struct device_node *node, struct irq_domain *domain) +static int __init setup_parent_interrupts(struct device_node *node, int *parents, + unsigned int num_parents) @@ -282,12 +276,12 @@ Submitted-by: Birger Koblitz - if (!cpu_ictl) - return -EINVAL; - ret = of_property_read_u32(cpu_ictl, "#interrupt-cells", &tmp); +- of_node_put(cpu_ictl); - if (ret || tmp != 1) - return -EINVAL; -- of_node_put(cpu_ictl); - - cpu_int = be32_to_cpup(imap + 2); -- if (cpu_int > 7) +- if (cpu_int > 7 || cpu_int < 2) - return -EINVAL; - - if (!(mips_irqs_set & BIT(cpu_int))) { @@ -299,7 +293,8 @@ Submitted-by: Birger Koblitz + if (!domain) + goto domain_err; -- regs[(soc_int * 4) / 32] |= cpu_int << (soc_int * 4) % 32; +- /* Use routing values (1..6) for CPU interrupts (2..7) */ +- regs[(soc_int * 4) / 32] |= (cpu_int - 1) << (soc_int * 4) % 32; - imap += 3; - } + output->fwnode = of_node_to_fwnode(node); diff --git a/target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch b/target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch index 3980875dd4b..a13d70d6335 100644 --- a/target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch +++ b/target/linux/realtek/patches-5.15/316-otto-gpio-uniprocessor-irq-mask.patch @@ -22,7 +22,7 @@ Submitted-by: Sander Vanheule --- a/drivers/gpio/gpio-realtek-otto.c +++ b/drivers/gpio/gpio-realtek-otto.c -@@ -304,6 +304,7 @@ static int realtek_gpio_irq_set_affinity +@@ -301,6 +301,7 @@ static int realtek_gpio_irq_set_affinity static int realtek_gpio_irq_init(struct gpio_chip *gc) { struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); @@ -30,7 +30,7 @@ Submitted-by: Sander Vanheule unsigned int port; int cpu; -@@ -311,8 +312,16 @@ static int realtek_gpio_irq_init(struct +@@ -308,8 +309,16 @@ static int realtek_gpio_irq_init(struct realtek_gpio_write_imr(ctrl, port, 0, 0); realtek_gpio_clear_isr(ctrl, port, GENMASK(7, 0)); diff --git a/target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch b/target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch index 9c043b71269..9f2c3b50b61 100644 --- a/target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch +++ b/target/linux/realtek/patches-5.15/317-gpio-realtek-otto-switch-to-32-bit-I-O.patch @@ -30,7 +30,6 @@ Signed-off-by: Bartosz Golaszewski Update patch for missing upstream changes: - commit a01a40e33499 ("gpio: realtek-otto: Make the irqchip immutable") - - commit dbd1c54fc820 ("gpio: Bulk conversion to generic_handle_domain_irq()") Signed-off-by: Sander Vanheule --- @@ -241,13 +240,12 @@ Signed-off-by: Sander Vanheule raw_spin_unlock_irqrestore(&ctrl->lock, flags); return 0; -@@ -238,31 +248,24 @@ static void realtek_gpio_irq_handler(str +@@ -238,28 +248,21 @@ static void realtek_gpio_irq_handler(str struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); struct irq_chip *irq_chip = irq_desc_get_chip(desc); - unsigned int lines_done; - unsigned int port_pin_count; - unsigned int irq; unsigned long status; int offset; @@ -256,15 +254,12 @@ Signed-off-by: Sander Vanheule - for (lines_done = 0; lines_done < gc->ngpio; lines_done += 8) { - status = realtek_gpio_read_isr(ctrl, lines_done / 8); - port_pin_count = min(gc->ngpio - lines_done, 8U); -- for_each_set_bit(offset, &status, port_pin_count) { -- irq = irq_find_mapping(gc->irq.domain, offset + lines_done); -- generic_handle_irq(irq); -- } +- for_each_set_bit(offset, &status, port_pin_count) +- generic_handle_domain_irq(gc->irq.domain, offset + lines_done); +- } + status = realtek_gpio_read_isr(ctrl); -+ for_each_set_bit(offset, &status, gc->ngpio) { -+ irq = irq_find_mapping(gc->irq.domain, offset); -+ generic_handle_irq(irq); - } ++ for_each_set_bit(offset, &status, gc->ngpio) ++ generic_handle_domain_irq(gc->irq.domain, offset); chained_irq_exit(irq_chip, desc); } @@ -279,7 +274,7 @@ Signed-off-by: Sander Vanheule } static int realtek_gpio_irq_set_affinity(struct irq_data *data, -@@ -270,12 +273,10 @@ static int realtek_gpio_irq_set_affinity +@@ -267,12 +270,10 @@ static int realtek_gpio_irq_set_affinity { struct realtek_gpio_ctrl *ctrl = irq_data_to_ctrl(data); unsigned int line = irqd_to_hwirq(data); @@ -293,7 +288,7 @@ Signed-off-by: Sander Vanheule if (!ctrl->cpumask_base) return -ENXIO; -@@ -283,15 +284,15 @@ static int realtek_gpio_irq_set_affinity +@@ -280,15 +281,15 @@ static int realtek_gpio_irq_set_affinity raw_spin_lock_irqsave(&ctrl->lock, flags); for_each_cpu(cpu, &ctrl->cpu_irq_maskable) { @@ -314,7 +309,7 @@ Signed-off-by: Sander Vanheule } raw_spin_unlock_irqrestore(&ctrl->lock, flags); -@@ -305,22 +306,23 @@ static int realtek_gpio_irq_init(struct +@@ -302,22 +303,23 @@ static int realtek_gpio_irq_init(struct { struct realtek_gpio_ctrl *ctrl = gpiochip_get_data(gc); void __iomem *irq_cpu_mask; @@ -352,7 +347,7 @@ Signed-off-by: Sander Vanheule } } -@@ -393,12 +395,14 @@ static int realtek_gpio_probe(struct pla +@@ -390,12 +392,14 @@ static int realtek_gpio_probe(struct pla if (dev_flags & GPIO_PORTS_REVERSED) { bgpio_flags = 0; diff --git a/target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch b/target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch index 94c42308356..c63c165cd28 100644 --- a/target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch +++ b/target/linux/realtek/patches-5.15/318-add-rtl83xx-clk-support.patch @@ -13,20 +13,20 @@ Submitted-by: Markus Stockhausen --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig -@@ -372,6 +372,7 @@ source "drivers/clk/mediatek/Kconfig" - source "drivers/clk/meson/Kconfig" +@@ -405,6 +405,7 @@ source "drivers/clk/mstar/Kconfig" source "drivers/clk/mvebu/Kconfig" + source "drivers/clk/pistachio/Kconfig" source "drivers/clk/qcom/Kconfig" +source "drivers/clk/realtek/Kconfig" + source "drivers/clk/ralink/Kconfig" source "drivers/clk/renesas/Kconfig" source "drivers/clk/rockchip/Kconfig" - source "drivers/clk/samsung/Kconfig" --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile -@@ -100,6 +100,7 @@ obj-$(CONFIG_COMMON_CLK_NXP) += nxp/ - obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ +@@ -101,6 +101,7 @@ obj-$(CONFIG_COMMON_CLK_PISTACHIO) += pi obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ + obj-y += ralink/ +obj-$(CONFIG_COMMON_CLK_REALTEK) += realtek/ obj-y += renesas/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ diff --git a/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch b/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch index bb95e7103a3..0c29b7739e1 100644 --- a/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch +++ b/target/linux/realtek/patches-5.15/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch @@ -143,7 +143,7 @@ Submitted-by: INAGAKI Hiroshi raw_spin_unlock_irqrestore(&irq_lock, flags); -@@ -279,9 +315,11 @@ static int __init realtek_rtl_of_init(st +@@ -285,9 +321,11 @@ static int __init realtek_rtl_of_init(st cpumask_set_cpu(cpu, &realtek_ictl_cpu_configurable); /* Disable all cascaded interrupts and clear routing */ diff --git a/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch b/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch index f97ea94d9b8..71724034d4c 100644 --- a/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch +++ b/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch @@ -22,19 +22,21 @@ Submitted-by: John Crispin --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig -@@ -68,6 +68,8 @@ config NET_DSA_QCA8K - This enables support for the Qualcomm Atheros QCA8K Ethernet - switch chips. +@@ -60,6 +60,8 @@ source "drivers/net/dsa/sja1105/Kconfig" + + source "drivers/net/dsa/xrs700x/Kconfig" +source "drivers/net/dsa/rtl83xx/Kconfig" + config NET_DSA_REALTEK_SMI tristate "Realtek SMI Ethernet switch family support" - depends on NET_DSA + select NET_DSA_TAG_RTL4_A --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile -@@ -23,3 +23,4 @@ obj-y += mv88e6xxx/ +@@ -22,5 +22,6 @@ obj-y += microchip/ + obj-y += mv88e6xxx/ obj-y += ocelot/ obj-y += qca/ - obj-y += sja1105/ +obj-y += rtl83xx/ + obj-y += sja1105/ + obj-y += xrs700x/ diff --git a/target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch b/target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch index 27299f17dea..0cb784fcbfa 100644 --- a/target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch +++ b/target/linux/realtek/patches-5.15/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch @@ -34,7 +34,7 @@ Submitted-by: John Crispin trailer[2] = 0x10; trailer[3] = 0x00; -@@ -34,12 +39,23 @@ static struct sk_buff *trailer_rcv(struc +@@ -33,12 +38,23 @@ static struct sk_buff *trailer_rcv(struc return NULL; trailer = skb_tail_pointer(skb) - 4; diff --git a/target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch b/target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch index 9f50e8b5648..1c25c42b911 100644 --- a/target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch +++ b/target/linux/realtek/patches-5.15/702-net-ethernet-add-support-for-rtl838x-ethernet.patch @@ -22,7 +22,7 @@ Submitted-by: John Crispin --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig -@@ -163,6 +163,13 @@ source "drivers/net/ethernet/rdc/Kconfig +@@ -166,6 +166,13 @@ source "drivers/net/ethernet/rdc/Kconfig source "drivers/net/ethernet/realtek/Kconfig" source "drivers/net/ethernet/renesas/Kconfig" source "drivers/net/ethernet/rocker/Kconfig" @@ -35,10 +35,10 @@ Submitted-by: John Crispin + source "drivers/net/ethernet/samsung/Kconfig" source "drivers/net/ethernet/seeq/Kconfig" - source "drivers/net/ethernet/sfc/Kconfig" + source "drivers/net/ethernet/sgi/Kconfig" --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile -@@ -75,6 +75,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real +@@ -77,6 +77,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += real obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/ obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ diff --git a/target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch index 8f68dd4e884..c4594980a90 100644 --- a/target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch +++ b/target/linux/realtek/patches-5.15/703-include-linux-add-phy-ops-for-rtl838x.patch @@ -21,7 +21,7 @@ Submitted-by: John Crispin --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -885,6 +885,10 @@ struct phy_driver { +@@ -943,6 +943,10 @@ struct phy_driver { int (*get_sqi)(struct phy_device *dev); /** @get_sqi_max: Get the maximum signal quality indication */ int (*get_sqi_max)(struct phy_device *dev); diff --git a/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch b/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch index c46be91fe01..d886ba224c8 100644 --- a/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch +++ b/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch @@ -21,7 +21,7 @@ Submitted-by: John Crispin --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c -@@ -1449,6 +1449,11 @@ int phylink_ethtool_ksettings_set(struct +@@ -1944,6 +1944,11 @@ int phylink_ethtool_ksettings_set(struct * the presence of a PHY, this should not be changed as that * should be determined from the media side advertisement. */ @@ -33,7 +33,7 @@ Submitted-by: John Crispin return phy_ethtool_ksettings_set(pl->phydev, kset); } -@@ -1750,8 +1755,11 @@ int phylink_ethtool_get_eee(struct phyli +@@ -2247,8 +2252,11 @@ int phylink_ethtool_get_eee(struct phyli ASSERT_RTNL(); @@ -46,7 +46,7 @@ Submitted-by: John Crispin return ret; } -@@ -1768,8 +1776,11 @@ int phylink_ethtool_set_eee(struct phyli +@@ -2265,8 +2273,11 @@ int phylink_ethtool_set_eee(struct phyli ASSERT_RTNL(); diff --git a/target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch b/target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch index 442bff82fae..8109abd264c 100644 --- a/target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch +++ b/target/linux/realtek/patches-5.15/704-include-linux-add-phy-hsgmii-mode.patch @@ -14,7 +14,7 @@ Submitted-by: Birger Koblitz --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -134,6 +134,7 @@ typedef enum { +@@ -138,6 +138,7 @@ typedef enum { PHY_INTERFACE_MODE_XGMII, PHY_INTERFACE_MODE_XLGMII, PHY_INTERFACE_MODE_MOCA, @@ -22,7 +22,7 @@ Submitted-by: Birger Koblitz PHY_INTERFACE_MODE_QSGMII, PHY_INTERFACE_MODE_TRGMII, PHY_INTERFACE_MODE_100BASEX, -@@ -201,6 +202,8 @@ static inline const char *phy_modes(phy_ +@@ -243,6 +244,8 @@ static inline const char *phy_modes(phy_ return "xlgmii"; case PHY_INTERFACE_MODE_MOCA: return "moca"; diff --git a/target/linux/realtek/patches-5.15/705-add-rtl-phy.patch b/target/linux/realtek/patches-5.15/705-add-rtl-phy.patch index 47041262fc3..cb80a72fe25 100644 --- a/target/linux/realtek/patches-5.15/705-add-rtl-phy.patch +++ b/target/linux/realtek/patches-5.15/705-add-rtl-phy.patch @@ -14,7 +14,7 @@ Submitted-by: Birger Koblitz --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -330,6 +330,12 @@ config REALTEK_PHY +@@ -356,6 +356,12 @@ config REALTEK_PHY help Supports the Realtek 821x PHY. @@ -29,7 +29,7 @@ Submitted-by: Birger Koblitz help --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -89,6 +89,7 @@ obj-$(CONFIG_NATIONAL_PHY) += national.o +@@ -93,6 +93,7 @@ obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek.o diff --git a/target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch b/target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch index bba6fd71828..3c273dca66d 100644 --- a/target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch +++ b/target/linux/realtek/patches-5.15/705-include-linux-phy-increase-phy-address-number-for-rtl839x.patch @@ -21,7 +21,7 @@ Submitted-by: John Crispin --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -233,7 +233,7 @@ static inline const char *phy_modes(phy_ +@@ -279,7 +279,7 @@ static inline const char *phy_modes(phy_ #define PHY_INIT_TIMEOUT 100000 #define PHY_FORCE_TIMEOUT 10 diff --git a/target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch b/target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch index 248abee39be..b269f7e1040 100644 --- a/target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch +++ b/target/linux/realtek/patches-5.15/710-net-phy-sfp-re-probe-modules-on-DEV_UP-event.patch @@ -10,7 +10,7 @@ Signed-off-by: Antoine Tenart --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c -@@ -1966,6 +1966,13 @@ static void sfp_sm_module(struct sfp *sf +@@ -2147,6 +2147,13 @@ static void sfp_sm_module(struct sfp *sf return; } diff --git a/target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch b/target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch index 5e1053ca6ca..a80482d94ab 100644 --- a/target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch +++ b/target/linux/realtek/patches-5.15/711-net-phy-add-an-MDIO-SMBus-library.patch @@ -15,7 +15,7 @@ Signed-off-by: Antoine Tenart --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig -@@ -40,6 +40,17 @@ config MDIO_SUN4I +@@ -54,6 +54,17 @@ config MDIO_SUN4I interface units of the Allwinner SoC that have an EMAC (A10, A12, A10s, etc.) @@ -35,7 +35,7 @@ Signed-off-by: Antoine Tenart depends on ARCH_XGENE || COMPILE_TEST --- a/drivers/net/mdio/Makefile +++ b/drivers/net/mdio/Makefile -@@ -17,6 +17,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxar +@@ -19,6 +19,7 @@ obj-$(CONFIG_MDIO_MOXART) += mdio-moxar obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o obj-$(CONFIG_MDIO_MVUSB) += mdio-mvusb.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o diff --git a/target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch b/target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch index c4bacc0ea9f..1f0b74a17c9 100644 --- a/target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch +++ b/target/linux/realtek/patches-5.15/712-net-phy-sfp-add-support-for-SMBus.patch @@ -10,7 +10,7 @@ Signed-off-by: Antoine Tenart --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c -@@ -413,32 +413,72 @@ static int sfp_i2c_write(struct sfp *sfp +@@ -539,32 +539,72 @@ static int sfp_i2c_write(struct sfp *sfp return ret == ARRAY_SIZE(msgs) ? len : 0; } diff --git a/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch b/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch index eeb26fa1994..48674886421 100644 --- a/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch +++ b/target/linux/realtek/patches-5.15/800-net-mdio-support-hardware-assisted-indirect-access.patch @@ -32,7 +32,7 @@ Signed-off-by: Daniel Golle --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c -@@ -734,6 +734,32 @@ out: +@@ -737,6 +737,32 @@ out: } /** @@ -65,9 +65,9 @@ Signed-off-by: Daniel Golle * __mdiobus_read - Unlocked version of the mdiobus_read function * @bus: the mii_bus struct * @addr: the phy address -@@ -749,7 +775,10 @@ int __mdiobus_read(struct mii_bus *bus, +@@ -752,7 +778,10 @@ int __mdiobus_read(struct mii_bus *bus, - WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock)); + lockdep_assert_held_once(&bus->mdio_lock); - retval = bus->read(bus, addr, regnum); + if (bus->read_paged) @@ -77,7 +77,7 @@ Signed-off-by: Daniel Golle trace_mdio_access(bus, 1, addr, regnum, retval, retval); mdiobus_stats_acct(&bus->stats[addr], true, retval); -@@ -759,6 +788,40 @@ int __mdiobus_read(struct mii_bus *bus, +@@ -762,6 +791,40 @@ int __mdiobus_read(struct mii_bus *bus, EXPORT_SYMBOL(__mdiobus_read); /** @@ -118,9 +118,9 @@ Signed-off-by: Daniel Golle * __mdiobus_write - Unlocked version of the mdiobus_write function * @bus: the mii_bus struct * @addr: the phy address -@@ -775,7 +838,10 @@ int __mdiobus_write(struct mii_bus *bus, +@@ -778,7 +841,10 @@ int __mdiobus_write(struct mii_bus *bus, - WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock)); + lockdep_assert_held_once(&bus->mdio_lock); - err = bus->write(bus, addr, regnum, val); + if (bus->write_paged) @@ -130,7 +130,7 @@ Signed-off-by: Daniel Golle trace_mdio_access(bus, 0, addr, regnum, val, err); mdiobus_stats_acct(&bus->stats[addr], false, err); -@@ -785,6 +851,39 @@ int __mdiobus_write(struct mii_bus *bus, +@@ -788,6 +854,39 @@ int __mdiobus_write(struct mii_bus *bus, EXPORT_SYMBOL(__mdiobus_write); /** @@ -170,7 +170,7 @@ Signed-off-by: Daniel Golle * __mdiobus_modify_changed - Unlocked version of the mdiobus_modify function * @bus: the mii_bus struct * @addr: the phy address -@@ -817,6 +916,43 @@ int __mdiobus_modify_changed(struct mii_ +@@ -820,6 +919,43 @@ int __mdiobus_modify_changed(struct mii_ EXPORT_SYMBOL_GPL(__mdiobus_modify_changed); /** @@ -214,7 +214,7 @@ Signed-off-by: Daniel Golle * mdiobus_read_nested - Nested version of the mdiobus_read function * @bus: the mii_bus struct * @addr: the phy address -@@ -842,6 +978,79 @@ int mdiobus_read_nested(struct mii_bus * +@@ -845,6 +981,79 @@ int mdiobus_read_nested(struct mii_bus * EXPORT_SYMBOL(mdiobus_read_nested); /** @@ -294,7 +294,7 @@ Signed-off-by: Daniel Golle * mdiobus_read - Convenience function for reading a given MII mgmt register * @bus: the mii_bus struct * @addr: the phy address -@@ -864,6 +1073,29 @@ int mdiobus_read(struct mii_bus *bus, in +@@ -867,6 +1076,29 @@ int mdiobus_read(struct mii_bus *bus, in EXPORT_SYMBOL(mdiobus_read); /** @@ -324,7 +324,7 @@ Signed-off-by: Daniel Golle * mdiobus_write_nested - Nested version of the mdiobus_write function * @bus: the mii_bus struct * @addr: the phy address -@@ -890,6 +1122,33 @@ int mdiobus_write_nested(struct mii_bus +@@ -893,6 +1125,33 @@ int mdiobus_write_nested(struct mii_bus EXPORT_SYMBOL(mdiobus_write_nested); /** @@ -358,7 +358,7 @@ Signed-off-by: Daniel Golle * mdiobus_write - Convenience function for writing a given MII mgmt register * @bus: the mii_bus struct * @addr: the phy address -@@ -913,6 +1172,30 @@ int mdiobus_write(struct mii_bus *bus, i +@@ -916,6 +1175,30 @@ int mdiobus_write(struct mii_bus *bus, i EXPORT_SYMBOL(mdiobus_write); /** @@ -389,7 +389,7 @@ Signed-off-by: Daniel Golle * mdiobus_modify - Convenience function for modifying a given mdio device * register * @bus: the mii_bus struct -@@ -934,6 +1217,51 @@ int mdiobus_modify(struct mii_bus *bus, +@@ -937,6 +1220,51 @@ int mdiobus_modify(struct mii_bus *bus, EXPORT_SYMBOL_GPL(mdiobus_modify); /** @@ -443,7 +443,7 @@ Signed-off-by: Daniel Golle * @dev: target MDIO device --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c -@@ -481,10 +481,16 @@ int __phy_read_mmd(struct phy_device *ph +@@ -482,10 +482,16 @@ int __phy_read_mmd(struct phy_device *ph struct mii_bus *bus = phydev->mdio.bus; int phy_addr = phydev->mdio.addr; @@ -464,7 +464,7 @@ Signed-off-by: Daniel Golle } return val; } -@@ -537,12 +543,18 @@ int __phy_write_mmd(struct phy_device *p +@@ -538,12 +544,18 @@ int __phy_write_mmd(struct phy_device *p struct mii_bus *bus = phydev->mdio.bus; int phy_addr = phydev->mdio.addr; @@ -487,7 +487,7 @@ Signed-off-by: Daniel Golle } return ret; } -@@ -748,6 +760,13 @@ EXPORT_SYMBOL_GPL(phy_modify_mmd); +@@ -749,6 +761,13 @@ EXPORT_SYMBOL_GPL(phy_modify_mmd); static int __phy_read_page(struct phy_device *phydev) { @@ -501,7 +501,7 @@ Signed-off-by: Daniel Golle if (WARN_ONCE(!phydev->drv->read_page, "read_page callback not available, PHY driver not loaded?\n")) return -EOPNOTSUPP; -@@ -756,6 +775,13 @@ static int __phy_read_page(struct phy_de +@@ -757,6 +776,13 @@ static int __phy_read_page(struct phy_de static int __phy_write_page(struct phy_device *phydev, int page) { @@ -515,7 +515,7 @@ Signed-off-by: Daniel Golle if (WARN_ONCE(!phydev->drv->write_page, "write_page callback not available, PHY driver not loaded?\n")) return -EOPNOTSUPP; -@@ -857,6 +883,18 @@ int phy_read_paged(struct phy_device *ph +@@ -858,6 +884,18 @@ int phy_read_paged(struct phy_device *ph { int ret = 0, oldpage; @@ -534,7 +534,7 @@ Signed-off-by: Daniel Golle oldpage = phy_select_page(phydev, page); if (oldpage >= 0) ret = __phy_read(phydev, regnum); -@@ -878,6 +916,18 @@ int phy_write_paged(struct phy_device *p +@@ -879,6 +917,18 @@ int phy_write_paged(struct phy_device *p { int ret = 0, oldpage; @@ -563,7 +563,7 @@ Signed-off-by: Daniel Golle #define MII_DEVADDR_C45_SHIFT 16 #define MII_DEVADDR_C45_MASK GENMASK(20, 16) #define MII_REGADDR_C45_MASK GENMASK(15, 0) -@@ -327,11 +328,19 @@ static inline void mii_10gbt_stat_mod_li +@@ -340,11 +341,19 @@ static inline void mii_10gbt_stat_mod_li advertising, lpa & MDIO_AN_10GBT_STAT_LP10G); } @@ -583,7 +583,7 @@ Signed-off-by: Daniel Golle int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum); int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum); int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); -@@ -339,11 +348,51 @@ int mdiobus_write_nested(struct mii_bus +@@ -352,11 +361,51 @@ int mdiobus_write_nested(struct mii_bus int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set); @@ -635,7 +635,7 @@ Signed-off-by: Daniel Golle static inline u16 mdiobus_c45_regad(u32 regnum) { return FIELD_GET(MII_REGADDR_C45_MASK, regnum); -@@ -367,6 +416,19 @@ static inline int __mdiobus_c45_write(st +@@ -380,6 +429,19 @@ static inline int __mdiobus_c45_write(st val); } @@ -665,7 +665,7 @@ Signed-off-by: Daniel Golle #define MDIO_DEVICE_IS_PHY 0x80000000 /** -@@ -374,6 +375,22 @@ struct mii_bus { +@@ -420,6 +421,22 @@ struct mii_bus { /** @shared: shared state across different PHYs */ struct phy_package_shared *shared[PHY_MAX_ADDR]; @@ -688,7 +688,7 @@ Signed-off-by: Daniel Golle }; #define to_mii_bus(d) container_of(d, struct mii_bus, dev) -@@ -1651,6 +1668,66 @@ static inline int __phy_package_read(str +@@ -1754,6 +1771,66 @@ static inline int __phy_package_read(str return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); } @@ -755,7 +755,7 @@ Signed-off-by: Daniel Golle static inline int phy_package_write(struct phy_device *phydev, u32 regnum, u16 val) { -@@ -1673,6 +1750,72 @@ static inline int __phy_package_write(st +@@ -1776,6 +1853,72 @@ static inline int __phy_package_write(st return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); } From f3a9975549959e06822de04d9a7b9c17a9a1806e Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Sat, 10 Sep 2022 01:40:43 +0900 Subject: [PATCH 047/110] realtek: refresh config-5.15 in all subtargets Refresh config-5.15 in all subtargets by kernel_menuconfig. Signed-off-by: INAGAKI Hiroshi --- target/linux/realtek/rtl838x/config-5.15 | 26 +++--- target/linux/realtek/rtl839x/config-5.15 | 51 +++++++---- target/linux/realtek/rtl930x/config-5.15 | 32 +++---- target/linux/realtek/rtl931x/config-5.15 | 106 +++++++++++++---------- 4 files changed, 123 insertions(+), 92 deletions(-) diff --git a/target/linux/realtek/rtl838x/config-5.15 b/target/linux/realtek/rtl838x/config-5.15 index d69345e3012..41560725169 100644 --- a/target/linux/realtek/rtl838x/config-5.15 +++ b/target/linux/realtek/rtl838x/config-5.15 @@ -1,5 +1,6 @@ CONFIG_ARCH_32BIT_OFF_T=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_ARCH_MMAP_RND_BITS_MAX=15 CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 CONFIG_ARCH_SUSPEND_POSSIBLE=y @@ -7,10 +8,6 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 # CONFIG_BMIPS_CPUFREQ is not set -# CONFIG_CEVT_R4K is not set -# CONFIG_CEVT_RTL9300 is not set -CONFIG_CLKDEV_LOOKUP=y -CONFIG_CLKSRC_MMIO=y CONFIG_CLONE_BACKWARDS=y CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK_BOSTON=y @@ -47,7 +44,6 @@ CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y CONFIG_CPU_SUPPORTS_CPUFREQ=y CONFIG_CPU_SUPPORTS_HIGHMEM=y CONFIG_CPU_SUPPORTS_MSA=y -CONFIG_CRYPTO_BLAKE2S=y CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 CONFIG_CRYPTO_RNG2=y @@ -60,11 +56,14 @@ CONFIG_EXTRA_FIRMWARE="rtl838x_phy/rtl838x_8214fc.fw rtl838x_phy/rtl838x_8218b.f CONFIG_EXTRA_FIRMWARE_DIR="firmware" CONFIG_FIXED_PHY=y CONFIG_FORCE_MAX_ZONEORDER=13 +CONFIG_FWNODE_MDIO=y CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y CONFIG_GENERIC_ATOMIC64=y CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_GENERIC_CMOS_UPDATE=y CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_FIND_FIRST_BIT=y CONFIG_GENERIC_GETTIMEOFDAY=y CONFIG_GENERIC_IOMAP=y CONFIG_GENERIC_IRQ_CHIP=y @@ -84,6 +83,7 @@ CONFIG_GENERIC_SCHED_CLOCK=y CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_GENERIC_TIME_VSYSCALL=y CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y @@ -97,7 +97,6 @@ CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT_MAP=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_HWMON=y CONFIG_HZ_PERIODIC=y CONFIG_I2C=y @@ -106,8 +105,8 @@ CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y CONFIG_I2C_MUX=y -# CONFIG_I2C_RTL9300 is not set # CONFIG_I2C_MUX_RTL9300 is not set +# CONFIG_I2C_RTL9300 is not set CONFIG_INITRAMFS_SOURCE="" CONFIG_IRQCHIP=y CONFIG_IRQ_DOMAIN=y @@ -125,18 +124,16 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_SMBUS=y CONFIG_MEMFD_CREATE=y CONFIG_MFD_SYSCON=y CONFIG_MIGRATION=y CONFIG_MIPS=y CONFIG_MIPS_ASID_BITS=8 CONFIG_MIPS_ASID_SHIFT=0 -CONFIG_MIPS_CBPF_JIT=y -CONFIG_MIPS_CLOCK_VSYSCALL=y -# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set # CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set CONFIG_MIPS_CMDLINE_FROM_DTB=y -# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_EBPF_JIT=y CONFIG_MIPS_EXTERNAL_TIMER=y CONFIG_MIPS_L1_CACHE_SHIFT=5 CONFIG_MIPS_LD_CAN_LINK_VDSO=y @@ -163,6 +160,7 @@ CONFIG_NET_DSA=y CONFIG_NET_DSA_RTL83XX=y CONFIG_NET_DSA_TAG_TRAILER=y CONFIG_NET_RTL838X=y +CONFIG_NET_SELFTESTS=y CONFIG_NET_SWITCHDEV=y CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y CONFIG_NVMEM=y @@ -174,7 +172,6 @@ CONFIG_OF_GPIO=y CONFIG_OF_IRQ=y CONFIG_OF_KOBJ=y CONFIG_OF_MDIO=y -CONFIG_OF_NET=y CONFIG_PCI_DRIVERS_LEGACY=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 @@ -185,6 +182,7 @@ CONFIG_PM_OPP=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_POWER_RESET_SYSCON=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_RATIONAL=y CONFIG_REALTEK_OTTO_TIMER=y CONFIG_REALTEK_OTTO_WDT=y @@ -194,11 +192,10 @@ CONFIG_REGMAP=y CONFIG_REGMAP_I2C=y CONFIG_REGMAP_MMIO=y CONFIG_RESET_CONTROLLER=y -CONFIG_RTL83XX=y CONFIG_RTL838X=y # CONFIG_RTL839X is not set +CONFIG_RTL83XX=y # CONFIG_RTL930X is not set -# CONFIG_RTL931X is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SFP=y @@ -215,6 +212,7 @@ CONFIG_SYS_HAS_EARLY_PRINTK=y CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y CONFIG_SYS_SUPPORTS_ARBIT_HZ=y CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HIGHMEM=y CONFIG_SYS_SUPPORTS_MIPS16=y CONFIG_TARGET_ISA_REV=2 CONFIG_TICK_CPU_ACCOUNTING=y diff --git a/target/linux/realtek/rtl839x/config-5.15 b/target/linux/realtek/rtl839x/config-5.15 index 57eded97fcd..5dfbec7a68f 100644 --- a/target/linux/realtek/rtl839x/config-5.15 +++ b/target/linux/realtek/rtl839x/config-5.15 @@ -1,8 +1,7 @@ CONFIG_ARCH_32BIT_OFF_T=y -CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_ARCH_MMAP_RND_BITS_MAX=15 CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 -CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 @@ -38,17 +37,17 @@ CONFIG_CPU_MIPS32=y # CONFIG_CPU_MIPS32_R1 is not set CONFIG_CPU_MIPS32_R2=y CONFIG_CPU_MIPSR2=y +CONFIG_CPU_MIPSR2_IRQ_EI=y +CONFIG_CPU_MIPSR2_IRQ_VI=y CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y CONFIG_CPU_SUPPORTS_CPUFREQ=y CONFIG_CPU_SUPPORTS_HIGHMEM=y CONFIG_CPU_SUPPORTS_MSA=y -CONFIG_CRYPTO_BLAKE2S=y -CONFIG_CRYPTO_GF128MUL=y CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 -CONFIG_CRYPTO_NULL2=y CONFIG_CRYPTO_RNG2=y CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_DMA_NONCOHERENT=y @@ -57,11 +56,14 @@ CONFIG_EARLY_PRINTK=y CONFIG_EARLY_PRINTK_8250=y CONFIG_FIXED_PHY=y CONFIG_FORCE_MAX_ZONEORDER=13 +CONFIG_FWNODE_MDIO=y CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y CONFIG_GENERIC_ATOMIC64=y CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_GENERIC_CMOS_UPDATE=y CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_FIND_FIRST_BIT=y CONFIG_GENERIC_GETTIMEOFDAY=y CONFIG_GENERIC_IOMAP=y CONFIG_GENERIC_IRQ_CHIP=y @@ -81,6 +83,7 @@ CONFIG_GENERIC_SCHED_CLOCK=y CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_GENERIC_TIME_VSYSCALL=y CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y @@ -92,7 +95,6 @@ CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT_MAP=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_HWMON=y CONFIG_HZ_PERIODIC=y CONFIG_I2C=y @@ -104,6 +106,7 @@ CONFIG_I2C_GPIO=y CONFIG_INITRAMFS_SOURCE="" CONFIG_IRQCHIP=y CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y CONFIG_IRQ_FORCED_THREADING=y CONFIG_IRQ_MIPS_CPU=y CONFIG_IRQ_WORK=y @@ -112,32 +115,33 @@ CONFIG_LEDS_GPIO=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 CONFIG_LIBFDT=y -CONFIG_LLD_VERSION=0 CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_MARVELL_PHY=y CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_I2C=y +CONFIG_MDIO_SMBUS=y CONFIG_MEMFD_CREATE=y CONFIG_MFD_SYSCON=y CONFIG_MIGRATION=y CONFIG_MIPS=y CONFIG_MIPS_ASID_BITS=8 CONFIG_MIPS_ASID_SHIFT=0 -CONFIG_MIPS_CBPF_JIT=y CONFIG_MIPS_CLOCK_VSYSCALL=y # CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set # CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set CONFIG_MIPS_CMDLINE_FROM_DTB=y -# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_EBPF_JIT=y CONFIG_MIPS_EXTERNAL_TIMER=y CONFIG_MIPS_L1_CACHE_SHIFT=5 CONFIG_MIPS_LD_CAN_LINK_VDSO=y -# CONFIG_MIPS_NO_APPENDED_DTB is not set -CONFIG_MIPS_MT_SMP=y +CONFIG_MIPS_MT=y # CONFIG_MIPS_MT_FPAFF is not set -CONFIG_NR_CPUS=2 +CONFIG_MIPS_MT_SMP=y +# CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_PERF_SHARED_TC_COUNTERS=y CONFIG_MIPS_RAW_APPENDED_DTB=y CONFIG_MIPS_SPRAM=y CONFIG_MODULES_USE_ELF_REL=y @@ -152,14 +156,16 @@ CONFIG_MTD_SPLIT_FIRMWARE=y CONFIG_MTD_SPLIT_TPLINK_FW=y CONFIG_MTD_SPLIT_UIMAGE_FW=y CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NEED_PER_CPU_KM=y CONFIG_NET_DEVLINK=y CONFIG_NET_DSA=y CONFIG_NET_DSA_RTL83XX=y CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_FLOW_LIMIT=y CONFIG_NET_RTL838X=y +CONFIG_NET_SELFTESTS=y CONFIG_NET_SWITCHDEV=y CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 CONFIG_NVMEM=y CONFIG_OF=y CONFIG_OF_ADDRESS=y @@ -169,7 +175,7 @@ CONFIG_OF_GPIO=y CONFIG_OF_IRQ=y CONFIG_OF_KOBJ=y CONFIG_OF_MDIO=y -CONFIG_OF_NET=y +CONFIG_PADATA=y CONFIG_PCI_DRIVERS_LEGACY=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 @@ -180,6 +186,9 @@ CONFIG_PM_OPP=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_POWER_RESET_SYSCON=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y CONFIG_RATIONAL=y CONFIG_REALTEK_OTTO_TIMER=y CONFIG_REALTEK_OTTO_WDT=y @@ -189,20 +198,25 @@ CONFIG_REGMAP=y CONFIG_REGMAP_I2C=y CONFIG_REGMAP_MMIO=y CONFIG_RESET_CONTROLLER=y -CONFIG_RTL83XX=y +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y # CONFIG_RTL838X is not set CONFIG_RTL839X=y +CONFIG_RTL83XX=y # CONFIG_RTL930X is not set -# CONFIG_RTL931X is not set CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SFP=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y CONFIG_SPI=y CONFIG_SPI_MASTER=y CONFIG_SPI_MEM=y CONFIG_SRAM=y CONFIG_SRCU=y CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y CONFIG_SYSCTL_EXCEPTION_TRACE=y CONFIG_SYS_HAS_CPU_MIPS32_R1=y CONFIG_SYS_HAS_CPU_MIPS32_R2=y @@ -210,7 +224,11 @@ CONFIG_SYS_HAS_EARLY_PRINTK=y CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y CONFIG_SYS_SUPPORTS_ARBIT_HZ=y CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HIGHMEM=y CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_SYS_SUPPORTS_MULTITHREADING=y +CONFIG_SYS_SUPPORTS_SCHED_SMT=y +CONFIG_SYS_SUPPORTS_SMP=y CONFIG_TARGET_ISA_REV=2 CONFIG_TICK_CPU_ACCOUNTING=y CONFIG_TIMER_OF=y @@ -219,5 +237,6 @@ CONFIG_TINY_SRCU=y CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y CONFIG_USE_OF=y CONFIG_WATCHDOG_CORE=y +CONFIG_XPS=y CONFIG_ZLIB_DEFLATE=y CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/realtek/rtl930x/config-5.15 b/target/linux/realtek/rtl930x/config-5.15 index 3b2b327f08f..462cdae5417 100644 --- a/target/linux/realtek/rtl930x/config-5.15 +++ b/target/linux/realtek/rtl930x/config-5.15 @@ -1,16 +1,15 @@ CONFIG_AQUANTIA_PHY=y CONFIG_ARCH_32BIT_OFF_T=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_ARCH_MMAP_RND_BITS_MAX=15 CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BOARD_SCACHE=y CONFIG_CEVT_RTL9300=y -# CONFIG_CEVT_R4K is not set -CONFIG_CLKDEV_LOOKUP=y -CONFIG_CLKSRC_MMIO=y CONFIG_CLONE_BACKWARDS=y CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK_BOSTON=y @@ -32,11 +31,8 @@ CONFIG_CPU_R4K_CACHE_TLB=y CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y CONFIG_CPU_SUPPORTS_HIGHMEM=y CONFIG_CPU_SUPPORTS_MSA=y -CONFIG_CRYPTO_BLAKE2S=y -CONFIG_CRYPTO_GF128MUL=y CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 -CONFIG_CRYPTO_NULL2=y CONFIG_CRYPTO_RNG2=y CONFIG_CSRC_R4K=y CONFIG_DEBUG_SECTION_MISMATCH=y @@ -46,11 +42,13 @@ CONFIG_EARLY_PRINTK=y CONFIG_EARLY_PRINTK_8250=y CONFIG_FIXED_PHY=y CONFIG_FORCE_MAX_ZONEORDER=13 +CONFIG_FWNODE_MDIO=y CONFIG_FW_LOADER_PAGED_BUF=y CONFIG_GENERIC_ATOMIC64=y CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_GENERIC_CMOS_UPDATE=y CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_FIND_FIRST_BIT=y CONFIG_GENERIC_GETTIMEOFDAY=y CONFIG_GENERIC_IOMAP=y CONFIG_GENERIC_IRQ_CHIP=y @@ -70,6 +68,7 @@ CONFIG_GENERIC_SCHED_CLOCK=y CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_GENERIC_TIME_VSYSCALL=y CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_REALTEK_OTTO=y @@ -80,7 +79,6 @@ CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT_MAP=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_HWMON=y CONFIG_HZ_PERIODIC=y CONFIG_I2C=y @@ -103,7 +101,6 @@ CONFIG_LEDS_GPIO=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 CONFIG_LIBFDT=y -CONFIG_LLD_VERSION=0 CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_MARVELL_PHY=y CONFIG_MDIO_BUS=y @@ -117,18 +114,15 @@ CONFIG_MIGRATION=y CONFIG_MIPS=y CONFIG_MIPS_ASID_BITS=8 CONFIG_MIPS_ASID_SHIFT=0 -CONFIG_MIPS_CBPF_JIT=y CONFIG_MIPS_CLOCK_VSYSCALL=y -# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set # CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set CONFIG_MIPS_CMDLINE_FROM_DTB=y -# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_CPU_SCACHE=y +CONFIG_MIPS_EBPF_JIT=y CONFIG_MIPS_L1_CACHE_SHIFT=5 CONFIG_MIPS_LD_CAN_LINK_VDSO=y -# CONFIG_MIPS_NO_APPENDED_DTB is not set # CONFIG_MIPS_MT_SMP is not set -# CONFIG_MIPS_MT_FPAFF is not set -CONFIG_NR_CPUS=2 +# CONFIG_MIPS_NO_APPENDED_DTB is not set CONFIG_MIPS_RAW_APPENDED_DTB=y CONFIG_MIPS_SPRAM=y CONFIG_MODULES_USE_ELF_REL=y @@ -149,6 +143,7 @@ CONFIG_NET_DSA=y CONFIG_NET_DSA_RTL83XX=y CONFIG_NET_DSA_TAG_TRAILER=y CONFIG_NET_RTL838X=y +CONFIG_NET_SELFTESTS=y CONFIG_NET_SWITCHDEV=y CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y CONFIG_NVMEM=y @@ -160,7 +155,6 @@ CONFIG_OF_GPIO=y CONFIG_OF_IRQ=y CONFIG_OF_KOBJ=y CONFIG_OF_MDIO=y -CONFIG_OF_NET=y CONFIG_PCI_DRIVERS_LEGACY=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 @@ -170,17 +164,19 @@ CONFIG_PINCTRL=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_POWER_RESET_SYSCON=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_RATIONAL=y # CONFIG_REALTEK_OTTO_TIMER is not set CONFIG_REALTEK_OTTO_WDT=y CONFIG_REALTEK_PHY=y CONFIG_REALTEK_SOC_PHY=y CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y CONFIG_REGMAP_MMIO=y CONFIG_RESET_CONTROLLER=y -CONFIG_RTL83XX=y # CONFIG_RTL838X is not set # CONFIG_RTL839X is not set +CONFIG_RTL83XX=y CONFIG_RTL930X=y # CONFIG_RTL931X is not set CONFIG_SERIAL_MCTRL_GPIO=y @@ -198,11 +194,11 @@ CONFIG_SYS_HAS_EARLY_PRINTK=y CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y CONFIG_SYS_SUPPORTS_ARBIT_HZ=y CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HIGHMEM=y CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_SYS_SUPPORTS_MULTITHREADING=y CONFIG_TARGET_ISA_REV=2 CONFIG_TICK_CPU_ACCOUNTING=y -CONFIG_TIMER_OF=y -CONFIG_TIMER_PROBE=y CONFIG_TINY_SRCU=y CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y CONFIG_USE_OF=y diff --git a/target/linux/realtek/rtl931x/config-5.15 b/target/linux/realtek/rtl931x/config-5.15 index 567421c8d4a..e1798174b16 100644 --- a/target/linux/realtek/rtl931x/config-5.15 +++ b/target/linux/realtek/rtl931x/config-5.15 @@ -1,14 +1,16 @@ CONFIG_ARCH_32BIT_OFF_T=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y CONFIG_ARCH_MMAP_RND_BITS_MAX=15 CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15 CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 -CONFIG_CEVT_R4K=y -CONFIG_CLKDEV_LOOKUP=y -CONFIG_CLKSRC_MMIO=y +CONFIG_BOARD_SCACHE=y +CONFIG_CEVT_RTL9300=y +CONFIG_CLKSRC_MIPS_GIC=y +CONFIG_CLOCKSOURCE_WATCHDOG=y CONFIG_CLONE_BACKWARDS=y CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK_BOSTON=y @@ -25,34 +27,32 @@ CONFIG_CPU_MIPS32=y # CONFIG_CPU_MIPS32_R1 is not set CONFIG_CPU_MIPS32_R2=y CONFIG_CPU_MIPSR2=y +CONFIG_CPU_MIPSR2_IRQ_EI=y +CONFIG_CPU_MIPSR2_IRQ_VI=y CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_RMAP=y CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y CONFIG_CPU_SUPPORTS_HIGHMEM=y CONFIG_CPU_SUPPORTS_MSA=y -CONFIG_MIPS_MT=y -CONFIG_MIPS_MT_FPAFF=y -CONFIG_MIPS_MT_SMP=y -# CONFIG_MIPS_NO_APPENDED_DTB is not set -CONFIG_MIPS_NR_CPU_NR_MAP=4 -CONFIG_HIGHMEM=y -CONFIG_CRYPTO_BLAKE2S=y -CONFIG_CRYPTO_GF128MUL=y CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_POLY1305_RSIZE=2 -CONFIG_CRYPTO_NULL2=y CONFIG_CRYPTO_RNG2=y CONFIG_CSRC_R4K=y CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_DMA_NONCOHERENT=y CONFIG_DTC=y CONFIG_EARLY_PRINTK=y +CONFIG_EARLY_PRINTK_8250=y CONFIG_FIXED_PHY=y +CONFIG_FORCE_MAX_ZONEORDER=13 +CONFIG_FWNODE_MDIO=y CONFIG_FW_LOADER_PAGED_BUF=y CONFIG_GENERIC_ATOMIC64=y CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_GENERIC_CMOS_UPDATE=y CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_FIND_FIRST_BIT=y CONFIG_GENERIC_GETTIMEOFDAY=y CONFIG_GENERIC_IOMAP=y CONFIG_GENERIC_IRQ_CHIP=y @@ -72,8 +72,9 @@ CONFIG_GENERIC_SCHED_CLOCK=y CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_GENERIC_TIME_VSYSCALL=y CONFIG_GPIOLIB_IRQCHIP=y -CONFIG_GPIO_REALTEK_OTTO=y +CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_REALTEK_OTTO=y CONFIG_GPIO_RTL8231=y CONFIG_GRO_CELLS=y CONFIG_HANDLE_DOMAIN_IRQ=y @@ -81,7 +82,7 @@ CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT_MAP=y -CONFIG_HIGH_RES_TIMERS=y +CONFIG_HIGHMEM=y CONFIG_HWMON=y CONFIG_HZ_PERIODIC=y CONFIG_I2C=y @@ -96,15 +97,16 @@ CONFIG_I2C_SMBUS=y CONFIG_INITRAMFS_SOURCE="" CONFIG_IRQCHIP=y CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y CONFIG_IRQ_FORCED_THREADING=y CONFIG_IRQ_MIPS_CPU=y CONFIG_IRQ_WORK=y CONFIG_JFFS2_ZLIB=y +CONFIG_KMAP_LOCAL=y CONFIG_LEDS_GPIO=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 CONFIG_LIBFDT=y -CONFIG_LLD_VERSION=0 CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_MARVELL_PHY=y CONFIG_MDIO_BUS=y @@ -118,15 +120,24 @@ CONFIG_MIGRATION=y CONFIG_MIPS=y CONFIG_MIPS_ASID_BITS=8 CONFIG_MIPS_ASID_SHIFT=0 -CONFIG_MIPS_CBPF_JIT=y CONFIG_MIPS_CLOCK_VSYSCALL=y -# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set +CONFIG_MIPS_CM=y # CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set CONFIG_MIPS_CMDLINE_FROM_DTB=y -# CONFIG_MIPS_ELF_APPENDED_DTB is not set +CONFIG_MIPS_CPC=y +CONFIG_MIPS_CPS=y +# CONFIG_MIPS_CPS_NS16550_BOOL is not set +CONFIG_MIPS_CPU_SCACHE=y +CONFIG_MIPS_EBPF_JIT=y +CONFIG_MIPS_GIC=y CONFIG_MIPS_L1_CACHE_SHIFT=5 CONFIG_MIPS_LD_CAN_LINK_VDSO=y +CONFIG_MIPS_MT=y +CONFIG_MIPS_MT_FPAFF=y +CONFIG_MIPS_MT_SMP=y # CONFIG_MIPS_NO_APPENDED_DTB is not set +CONFIG_MIPS_NR_CPU_NR_MAP=2 +CONFIG_MIPS_PERF_SHARED_TC_COUNTERS=y CONFIG_MIPS_RAW_APPENDED_DTB=y CONFIG_MIPS_SPRAM=y CONFIG_MODULES_USE_ELF_REL=y @@ -141,15 +152,16 @@ CONFIG_MTD_SPLIT_FIRMWARE=y CONFIG_MTD_SPLIT_TPLINK_FW=y CONFIG_MTD_SPLIT_UIMAGE_FW=y CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NEED_PER_CPU_KM=y CONFIG_NET_DEVLINK=y CONFIG_NET_DSA=y CONFIG_NET_DSA_RTL83XX=y -CONFIG_NET_DSA_TAG_RTL83XX=y CONFIG_NET_DSA_TAG_TRAILER=y +CONFIG_NET_FLOW_LIMIT=y CONFIG_NET_RTL838X=y +CONFIG_NET_SELFTESTS=y CONFIG_NET_SWITCHDEV=y CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_NR_CPUS=2 CONFIG_NVMEM=y CONFIG_OF=y CONFIG_OF_ADDRESS=y @@ -159,7 +171,7 @@ CONFIG_OF_GPIO=y CONFIG_OF_IRQ=y CONFIG_OF_KOBJ=y CONFIG_OF_MDIO=y -CONFIG_OF_NET=y +CONFIG_PADATA=y CONFIG_PCI_DRIVERS_LEGACY=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 @@ -168,25 +180,39 @@ CONFIG_PHYLINK=y CONFIG_PINCTRL=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_SYSCON=y +CONFIG_PTP_1588_CLOCK_OPTIONAL=y +CONFIG_QUEUED_RWLOCKS=y +CONFIG_QUEUED_SPINLOCKS=y CONFIG_RATIONAL=y # CONFIG_REALTEK_OTTO_TIMER is not set +CONFIG_REALTEK_OTTO_WDT=y # CONFIG_REALTEK_PHY is not set CONFIG_REALTEK_SOC_PHY=y CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y CONFIG_REGMAP_MMIO=y CONFIG_RESET_CONTROLLER=y -CONFIG_RTL838X=y -# CONFIG_RTL9300_TIMER is not set +CONFIG_RFS_ACCEL=y +CONFIG_RPS=y +# CONFIG_RTL838X is not set +# CONFIG_RTL839X is not set +CONFIG_RTL83XX=y +CONFIG_RTL930X=y +CONFIG_RTL931X=y CONFIG_SENSORS_GPIO_FAN=y CONFIG_SENSORS_LM75=y CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SFP=y +CONFIG_SMP=y +CONFIG_SMP_UP=y +CONFIG_SOCK_RX_QUEUE_MAPPING=y CONFIG_SPI=y CONFIG_SPI_MASTER=y CONFIG_SPI_MEM=y CONFIG_SRCU=y CONFIG_SWPHY=y +CONFIG_SYNC_R4K=y CONFIG_SYSCTL_EXCEPTION_TRACE=y CONFIG_SYS_HAS_CPU_MIPS32_R1=y CONFIG_SYS_HAS_CPU_MIPS32_R2=y @@ -194,32 +220,24 @@ CONFIG_SYS_HAS_EARLY_PRINTK=y CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y CONFIG_SYS_SUPPORTS_ARBIT_HZ=y CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y +CONFIG_SYS_SUPPORTS_HIGHMEM=y +CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y CONFIG_SYS_SUPPORTS_MIPS16=y +CONFIG_SYS_SUPPORTS_MIPS_CPS=y +CONFIG_SYS_SUPPORTS_MULTITHREADING=y +CONFIG_SYS_SUPPORTS_SCHED_SMT=y +CONFIG_SYS_SUPPORTS_SMP=y +CONFIG_SYS_SUPPORTS_VPE_LOADER=y CONFIG_TARGET_ISA_REV=2 CONFIG_TICK_CPU_ACCOUNTING=y CONFIG_TIMER_OF=y CONFIG_TIMER_PROBE=y -CONFIG_TINY_SRCU=y -# CONFIG_USE_GENERIC_EARLY_PRINTK_8250 is not set +CONFIG_TREE_RCU=y +CONFIG_TREE_SRCU=y +CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WEAK_ORDERING=y +CONFIG_XPS=y CONFIG_ZLIB_DEFLATE=y CONFIG_ZLIB_INFLATE=y -CONFIG_FORCE_MAX_ZONEORDER=13 -# CONFIG_MIPS_VPE_LOADER is not set -CONFIG_MIPS_CPU_SCACHE=y -CONFIG_SPI_RTL838X=y -CONFIG_MIPS_MT_SMP=y -CONFIG_MIPS_CPC=y -CONFIG_MIPS_CPS=y -CONFIG_MIPS_PM=y -# CONFIG_MIPS_CMP is not set -CONFIG_MIPS_CPS_CPUIDLE=y -# CONFIG_MIPS_CPS_NS16550_BOOL is not set -CONFIG_SMP=y -CONFIG_NR_CPUS=2 -CONFIG_RTL83XX=y -# CONFIG_RTL838X is not set -# CONFIG_RTL839X is not set -CONFIG_RTL930X=y -CONFIG_RTL931X=y -CONFIG_REALTEK_OTTO_WDT=y From 1f153558a30a067d83178442e87bca4b67bd120d Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Wed, 1 Dec 2021 12:25:54 +0900 Subject: [PATCH 048/110] realtek: update platform support for 5.15 - fw_passed_dtb and others were replaced to get_fdt() function[1] - __appended_dtb defined by asm/bootinfo.h[2] [1]: https://www.spinics.net/lists/linux-mips/msg03332.html [2]: https://www.spinics.net/lists/linux-mips/msg03332.html Signed-off-by: INAGAKI Hiroshi --- target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c | 1 - target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c b/target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c index dd1b2b170d3..abf594aa040 100644 --- a/target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c +++ b/target/linux/realtek/files-5.15/arch/mips/rtl838x/prom.c @@ -25,7 +25,6 @@ #include extern char arcs_cmdline[]; -extern const char __appended_dtb; struct rtl83xx_soc_info soc_info; const void *fdt; diff --git a/target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c b/target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c index b4d415ab442..546b2fa2f81 100644 --- a/target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c +++ b/target/linux/realtek/files-5.15/arch/mips/rtl838x/setup.c @@ -35,11 +35,8 @@ void __init plat_mem_setup(void) set_io_port_base(KSEG1); - if (fw_passed_dtb) /* UHI interface */ - dtb = (void *)fw_passed_dtb; - else if (&__dtb_start[0] != &__dtb_end[0]) - dtb = (void *)__dtb_start; - else + dtb = get_fdt(); + if (!dtb) panic("no dtb found"); /* From 109962d8bfa77abbe70d3774309c28689290a0f7 Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Wed, 1 Dec 2021 18:38:11 +0900 Subject: [PATCH 049/110] realtek: update dsa.c of DSA driver for 5.15 - rtl83xx_vlan_filtering() "struct switchdev_trans *trans" parameter was removed[1] and "struct netlink_ext_ack *extack" was added[2]. [1]: https://www.spinics.net/lists/netdev/msg712250.html [2]: https://www.spinics.net/lists/netdev/msg722496.html - rtl83xx_vlan_add/del() vlan->vid_begin and vlan->vid_end were removed and vlan->vid was added[3]. [3]: https://www.spinics.net/lists/netdev/msg712248.html - rtl83xx_vlan_prepare() "port_vlan_prepare" member was removed from "dsa_switch_ops" struct in dsa.h[4] and vlan_prepare function should be called from vlan_add function. Also, change return type of vlan_add function to int. [4]: https://www.spinics.net/lists/netdev/msg712252.html - rtl83xx_port_mdb_add() "port_mdb_prepare" member in "dsa_switch_ops" struct was removed and preparation need to be done in the function of "port_mdb_add" member instead. And also, int type need to be returned on "port_mdb_add" member[5]. [5]: https://www.spinics.net/lists/netdev/msg712251.html - rtl83xx_port_pre_bridge_flags(), rtl83xx_port_bridge_flags() The current "port_pre_bridge_flags" member and "port_bridge_flags" member in "dsa_switch_ops" in dsa.h has flags of "struct switchdev_brport_flags" type instead[6], so adjust to it. And, the changed features are passed by flags.mask[7] in rtl83xx_port_bridge_flags(), so check it before calling function to enable/disable fieature. [6]: https://lore.kernel.org/lkml/20210212151600.3357121-7-olteanv@gmail.com/ [7]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e18f4c18ab5b0dd47caaf8377c2e36d66f632a8c Suggested-by: Markus Stockhausen Signed-off-by: INAGAKI Hiroshi [shorten final return statement of rtl83xx_port_mdb_add()] Signed-off-by: Sander Vanheule --- .../files-5.15/drivers/net/dsa/rtl83xx/dsa.c | 197 ++++++++---------- 1 file changed, 92 insertions(+), 105 deletions(-) diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c index 6eea0dc9367..9281e08d33e 100644 --- a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c @@ -1377,7 +1377,7 @@ void rtl930x_fast_age(struct dsa_switch *ds, int port) static int rtl83xx_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, - struct switchdev_trans *trans) + struct netlink_ext_ack *extack) { struct rtl838x_switch_priv *priv = ds->priv; @@ -1436,68 +1436,68 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port, return 0; } -static void rtl83xx_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int rtl83xx_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) { struct rtl838x_vlan_info info; struct rtl838x_switch_priv *priv = ds->priv; - int v; + int err; - pr_debug("%s port %d, vid_begin %d, vid_end %d, flags %x\n", __func__, - port, vlan->vid_begin, vlan->vid_end, vlan->flags); + pr_debug("%s port %d, vid %d, flags %x\n", + __func__, port, vlan->vid, vlan->flags); - if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { - dev_err(priv->dev, "VLAN out of range: %d - %d", - vlan->vid_begin, vlan->vid_end); - return; + if (vlan->vid > 4095) { + dev_err(priv->dev, "VLAN out of range: %d", vlan->vid); + return -ENOTSUPP; } + err = rtl83xx_vlan_prepare(ds, port, vlan); + if (err) + return err; + mutex_lock(&priv->reg_mutex); - if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { - for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { - if (!v) - continue; - /* Set both inner and outer PVID of the port */ - priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, v); - priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, v); - priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER, - PBVLAN_MODE_UNTAG_AND_PRITAG); - priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER, - PBVLAN_MODE_UNTAG_AND_PRITAG); + if (vlan->flags & BRIDGE_VLAN_INFO_PVID && vlan->vid) { + /* Set both inner and outer PVID of the port */ + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, vlan->vid); + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, vlan->vid); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER, + PBVLAN_MODE_UNTAG_AND_PRITAG); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER, + PBVLAN_MODE_UNTAG_AND_PRITAG); - priv->ports[port].pvid = vlan->vid_end; - } + priv->ports[port].pvid = vlan->vid; } - for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { - /* Get port memberships of this vlan */ - priv->r->vlan_tables_read(v, &info); + /* Get port memberships of this vlan */ + priv->r->vlan_tables_read(vlan->vid, &info); - /* new VLAN? */ - if (!info.tagged_ports) { - info.fid = 0; - info.hash_mc_fid = false; - info.hash_uc_fid = false; - info.profile_id = 0; - } - - /* sanitize untagged_ports - must be a subset */ - if (info.untagged_ports & ~info.tagged_ports) - info.untagged_ports = 0; - - info.tagged_ports |= BIT_ULL(port); - if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED) - info.untagged_ports |= BIT_ULL(port); - - priv->r->vlan_set_untagged(v, info.untagged_ports); - pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); - - priv->r->vlan_set_tagged(v, &info); - pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + /* new VLAN? */ + if (!info.tagged_ports) { + info.fid = 0; + info.hash_mc_fid = false; + info.hash_uc_fid = false; + info.profile_id = 0; } + /* sanitize untagged_ports - must be a subset */ + if (info.untagged_ports & ~info.tagged_ports) + info.untagged_ports = 0; + + info.tagged_ports |= BIT_ULL(port); + if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED) + info.untagged_ports |= BIT_ULL(port); + + priv->r->vlan_set_untagged(vlan->vid, info.untagged_ports); + pr_debug("Untagged ports, VLAN %d: %llx\n", vlan->vid, info.untagged_ports); + + priv->r->vlan_set_tagged(vlan->vid, &info); + pr_debug("Tagged ports, VLAN %d: %llx\n", vlan->vid, info.tagged_ports); + mutex_unlock(&priv->reg_mutex); + + return 0; } static int rtl83xx_vlan_del(struct dsa_switch *ds, int port, @@ -1505,44 +1505,41 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port, { struct rtl838x_vlan_info info; struct rtl838x_switch_priv *priv = ds->priv; - int v; u16 pvid; - pr_debug("%s: port %d, vid_begin %d, vid_end %d, flags %x\n", __func__, - port, vlan->vid_begin, vlan->vid_end, vlan->flags); + pr_debug("%s: port %d, vid %d, flags %x\n", + __func__, port, vlan->vid, vlan->flags); - if (vlan->vid_begin > 4095 || vlan->vid_end > 4095) { - dev_err(priv->dev, "VLAN out of range: %d - %d", - vlan->vid_begin, vlan->vid_end); + if (vlan->vid > 4095) { + dev_err(priv->dev, "VLAN out of range: %d", vlan->vid); return -ENOTSUPP; } mutex_lock(&priv->reg_mutex); pvid = priv->ports[port].pvid; - for (v = vlan->vid_begin; v <= vlan->vid_end; v++) { - /* Reset to default if removing the current PVID */ - if (v == pvid) { - priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, 0); - priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, 0); - priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER, - PBVLAN_MODE_UNTAG_AND_PRITAG); - priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER, - PBVLAN_MODE_UNTAG_AND_PRITAG); - } - /* Get port memberships of this vlan */ - priv->r->vlan_tables_read(v, &info); - - /* remove port from both tables */ - info.untagged_ports &= (~BIT_ULL(port)); - info.tagged_ports &= (~BIT_ULL(port)); - - priv->r->vlan_set_untagged(v, info.untagged_ports); - pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports); - - priv->r->vlan_set_tagged(v, &info); - pr_debug("Tagged ports, VLAN %d: %llx\n", v, info.tagged_ports); + /* Reset to default if removing the current PVID */ + if (vlan->vid == pvid) { + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, 0); + priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, 0); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER, + PBVLAN_MODE_UNTAG_AND_PRITAG); + priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER, + PBVLAN_MODE_UNTAG_AND_PRITAG); } + /* Get port memberships of this vlan */ + priv->r->vlan_tables_read(vlan->vid, &info); + + /* remove port from both tables */ + info.untagged_ports &= (~BIT_ULL(port)); + info.tagged_ports &= (~BIT_ULL(port)); + + priv->r->vlan_set_untagged(vlan->vid, info.untagged_ports); + pr_debug("Untagged ports, VLAN %d: %llx\n", vlan->vid, info.untagged_ports); + + priv->r->vlan_set_tagged(vlan->vid, &info); + pr_debug("Tagged ports, VLAN %d: %llx\n", vlan->vid, info.tagged_ports); + mutex_unlock(&priv->reg_mutex); return 0; @@ -1744,18 +1741,7 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port, return 0; } -static int rtl83xx_port_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) -{ - struct rtl838x_switch_priv *priv = ds->priv; - - if (priv->id >= 0x9300) - return -EOPNOTSUPP; - - return 0; -} - -static void rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, +static int rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb) { struct rtl838x_switch_priv *priv = ds->priv; @@ -1766,11 +1752,14 @@ static void rtl83xx_port_mdb_add(struct dsa_switch *ds, int port, u64 seed = priv->r->l2_hash_seed(mac, vid); int mc_group; + if (priv->id >= 0x9300) + return -EOPNOTSUPP; + pr_debug("In %s port %d, mac %llx, vid: %d\n", __func__, port, mac, vid); if (priv->is_lagmember[port]) { pr_debug("%s: %d is lag slave. ignore\n", __func__, port); - return; + return -EINVAL; } mutex_lock(&priv->reg_mutex); @@ -1822,6 +1811,8 @@ out: mutex_unlock(&priv->reg_mutex); if (err) dev_err(ds->dev, "failed to add MDB entry\n"); + + return err; } int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port, @@ -1968,11 +1959,11 @@ static void rtl83xx_port_mirror_del(struct dsa_switch *ds, int port, mutex_unlock(&priv->reg_mutex); } -static int rtl83xx_port_pre_bridge_flags(struct dsa_switch *ds, int port, unsigned long flags, struct netlink_ext_ack *extack) +static int rtl83xx_port_pre_bridge_flags(struct dsa_switch *ds, int port, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) { struct rtl838x_switch_priv *priv = ds->priv; unsigned long features = 0; - pr_debug("%s: %d %lX\n", __func__, port, flags); + pr_debug("%s: %d %lX\n", __func__, port, flags.val); if (priv->r->enable_learning) features |= BR_LEARNING; if (priv->r->enable_flood) @@ -1981,28 +1972,28 @@ static int rtl83xx_port_pre_bridge_flags(struct dsa_switch *ds, int port, unsign features |= BR_MCAST_FLOOD; if (priv->r->enable_bcast_flood) features |= BR_BCAST_FLOOD; - if (flags & ~(features)) + if (flags.mask & ~(features)) return -EINVAL; return 0; } -static int rtl83xx_port_bridge_flags(struct dsa_switch *ds, int port, unsigned long flags, struct netlink_ext_ack *extack) +static int rtl83xx_port_bridge_flags(struct dsa_switch *ds, int port, struct switchdev_brport_flags flags, struct netlink_ext_ack *extack) { struct rtl838x_switch_priv *priv = ds->priv; - pr_debug("%s: %d %lX\n", __func__, port, flags); - if (priv->r->enable_learning) - priv->r->enable_learning(port, !!(flags & BR_LEARNING)); + pr_debug("%s: %d %lX\n", __func__, port, flags.val); + if (priv->r->enable_learning && (flags.mask & BR_LEARNING)) + priv->r->enable_learning(port, !!(flags.val & BR_LEARNING)); - if (priv->r->enable_flood) - priv->r->enable_flood(port, !!(flags & BR_FLOOD)); + if (priv->r->enable_flood && (flags.mask & BR_FLOOD)) + priv->r->enable_flood(port, !!(flags.val & BR_FLOOD)); - if (priv->r->enable_mcast_flood) - priv->r->enable_mcast_flood(port, !!(flags & BR_MCAST_FLOOD)); + if (priv->r->enable_mcast_flood && (flags.mask & BR_MCAST_FLOOD)) + priv->r->enable_mcast_flood(port, !!(flags.val & BR_MCAST_FLOOD)); - if (priv->r->enable_bcast_flood) - priv->r->enable_bcast_flood(port, !!(flags & BR_BCAST_FLOOD)); + if (priv->r->enable_bcast_flood && (flags.mask & BR_BCAST_FLOOD)) + priv->r->enable_bcast_flood(port, !!(flags.val & BR_BCAST_FLOOD)); return 0; } @@ -2185,7 +2176,6 @@ const struct dsa_switch_ops rtl83xx_switch_ops = { .port_fast_age = rtl83xx_fast_age, .port_vlan_filtering = rtl83xx_vlan_filtering, - .port_vlan_prepare = rtl83xx_vlan_prepare, .port_vlan_add = rtl83xx_vlan_add, .port_vlan_del = rtl83xx_vlan_del, @@ -2193,7 +2183,6 @@ const struct dsa_switch_ops rtl83xx_switch_ops = { .port_fdb_del = rtl83xx_port_fdb_del, .port_fdb_dump = rtl83xx_port_fdb_dump, - .port_mdb_prepare = rtl83xx_port_mdb_prepare, .port_mdb_add = rtl83xx_port_mdb_add, .port_mdb_del = rtl83xx_port_mdb_del, @@ -2238,7 +2227,6 @@ const struct dsa_switch_ops rtl930x_switch_ops = { .port_fast_age = rtl930x_fast_age, .port_vlan_filtering = rtl83xx_vlan_filtering, - .port_vlan_prepare = rtl83xx_vlan_prepare, .port_vlan_add = rtl83xx_vlan_add, .port_vlan_del = rtl83xx_vlan_del, @@ -2246,7 +2234,6 @@ const struct dsa_switch_ops rtl930x_switch_ops = { .port_fdb_del = rtl83xx_port_fdb_del, .port_fdb_dump = rtl83xx_port_fdb_dump, - .port_mdb_prepare = rtl83xx_port_mdb_prepare, .port_mdb_add = rtl83xx_port_mdb_add, .port_mdb_del = rtl83xx_port_mdb_del, From 69055a54121a95c4e0e9dd409d8b78d19743c221 Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Mon, 4 Apr 2022 21:25:20 +0900 Subject: [PATCH 050/110] realtek: enable needs_standalone_vlan_filtering on DSA driver in 5.15 To configure VLAN 0, enable needs_standalone_vlan_filtering option of dsa_switch struct. Signed-off-by: INAGAKI Hiroshi --- target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c index e86ff9ccdfe..b5afed0ef55 100644 --- a/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c +++ b/target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/common.c @@ -1466,6 +1466,7 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev) priv->ds->dev = dev; priv->ds->priv = priv; priv->ds->ops = &rtl83xx_switch_ops; + priv->ds->needs_standalone_vlan_filtering = true; priv->dev = dev; mutex_init(&priv->reg_mutex); From 88db7461cf2bea1e21bdaee2091908cd38e00bc3 Mon Sep 17 00:00:00 2001 From: INAGAKI Hiroshi Date: Tue, 29 Mar 2022 14:50:13 +0900 Subject: [PATCH 051/110] realtek: add Linux Kernel 5.15 as testing version Add Linux Kernel 5.15 support for testing. Signed-off-by: INAGAKI Hiroshi [APRESIA ApresiaLightGS120GT-SS, Panasonic Switch-M24eG PN28240K, Switch-M48eG PN28480K] Tested-by: INAGAKI Hiroshi TP-Link TL-SG2008P Tested-by: Alexandru Gagniuc [D-Link DGS-1210-20, DGS-1210-52, Zyxel XGS1010-12] Tested-by: Markus Stockhausen [Zyxel XGS1250-12] Tested-by: Lucian Cristian # Zyxel [HPE 1920-8G, 1920-48G] Tested-by: Jan Hoffmann --- target/linux/realtek/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/target/linux/realtek/Makefile b/target/linux/realtek/Makefile index 037ba7ac7db..797ae60274e 100644 --- a/target/linux/realtek/Makefile +++ b/target/linux/realtek/Makefile @@ -10,6 +10,7 @@ FEATURES:=ramdisk squashfs SUBTARGETS:=rtl838x rtl839x rtl930x rtl931x KERNEL_PATCHVER:=5.10 +KERNEL_TESTING_PATCHVER:=5.15 define Target/Description Build firmware images for Realtek RTL83xx based boards. From 4a4d0bf78ddbbf17508891c5c837e5eb00420b5c Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Fri, 16 Dec 2022 00:18:08 +0100 Subject: [PATCH 052/110] iwinfo: update to latest Git HEAD 8d15809 cli: print current HT mode 8f86dd6 cli: use IWINFO_HTMODE_COUNT f36b72b cli: use IWINFO_KMGMT_NAMES 91be7e0 cli: use IWINFO_CIPHER_NAMES 49b6ec9 cli: fix printing the scan channel width b1c8873 cli: fix marking the active channel 9e14e64 utils: add iwinfo_band2ghz() and iwinfo_ghz2band() helpers e084781 utils: add helper functions to get names by values d09a77a utils: add iwinfo_htmode_is_{ht|vht|he} helpers 8752977 utils: add and use iwinfo_format_hwmodes() 02f433e lib: add IWINFO_80211_COUNT and IWINFO_80211_NAMES 1d30df1 lib: add IWINFO_BAND_COUNT and IWINFO_BAND_NAMES aefd0ef lib: use common IWINFO_CIPHER_NAMES strings a5b30de lib: add IWINFO_OPMODE_COUNT and use it for IWINFO_OPMODE_NAMES 9f29e79 lib: constify and fixup the string array definitions fddc015 nl80211: mark frequencies where HE operation in not allowed 6d50a7c nl80211: add support for HE htmodes 4ba5713 nl80211: properly get available bands for the hwmode 91b2ada nl80211: update the kernel header nl80211.h 3f619a5 nl80211: fix frequency/channel conversion for the 6G band a77d915 nl80211: don't guess if a name is an ifname c27ce71 devices: add usb device MediaTek MT7921AU 14f864e nl80211: add ability to describe USB devices a5a75fd nl80211: remove ancient wpa_supplicant ctrl socket path dd4e1ff nl80211: fix wpa supplicant ctrl socket permissions d638163 fix -Wdangling-else warnings 4aa6c5a fix -Wreturn-type warning 3112726 fix -Wpointer-sign warning ebd5f84 fix -Wmaybe-uninitialized warning 5469898 fix -Wunused-variable warnings 462b679 fix -Wduplicate-decl-specifier warnings ccaabb4 fix -Wformat-truncation warnings 50380db enable useful compiler warnings via -Wall Fixes: https://github.com/openwrt/openwrt/issues/10158 Fixes: https://github.com/openwrt/openwrt/issues/10687 Signed-off-by: Jo-Philipp Wich --- package/network/utils/iwinfo/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/network/utils/iwinfo/Makefile b/package/network/utils/iwinfo/Makefile index 590ee05f393..6263e47fc9e 100644 --- a/package/network/utils/iwinfo/Makefile +++ b/package/network/utils/iwinfo/Makefile @@ -11,9 +11,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/iwinfo.git -PKG_SOURCE_DATE:=2022-11-01 -PKG_SOURCE_VERSION:=00aab871c50d944fd83269e460e69124cb11adb7 -PKG_MIRROR_HASH:=e915d53da916db333d62c396154612ed7557fbb27ed8884e70c19c321c08b12c +PKG_SOURCE_DATE:=2022-12-15 +PKG_SOURCE_VERSION:=8d158096a9882d3090c7e180a296ca7b035b4865 +PKG_MIRROR_HASH:=c376d3f2794fcef2956c038a16b4a1a4d30082ca4f2d2b955bd191d06e78f6ec PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=GPL-2.0 From d15b1fbed7abb6d4d536c32551ce6d73a309889b Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Fri, 16 Dec 2022 00:27:02 +0100 Subject: [PATCH 053/110] rpcd: update to latest Git HEAD 7de4820 iwinfo: add "hwmodes_text" to the info output b3f530b iwinfo: clean up rpc_iwinfo_call_hw_ht_mode() c46ad61 iwinfo: reuse infos provided by libiwinfo 6c5e900 iwinfo: constify string map arg for rpc_iwinfo_call_int() Signed-off-by: Jo-Philipp Wich --- package/system/rpcd/Makefile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/package/system/rpcd/Makefile b/package/system/rpcd/Makefile index c63b8acda5a..ab4589f88c1 100644 --- a/package/system/rpcd/Makefile +++ b/package/system/rpcd/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/rpcd.git -PKG_MIRROR_HASH:=18137fa6904b1a7aec03a16a4c25fd9b9689b24ee14d431bacdda9f093339071 -PKG_SOURCE_DATE:=2022-09-21 -PKG_SOURCE_VERSION:=8c852b656bf1622dee1ae2cfa4c083f730c1c539 +PKG_MIRROR_HASH:=87dab115512070d3ab566ebe47859bad3c148986271cdb2ac05a0952be262519 +PKG_SOURCE_DATE:=2022-12-15 +PKG_SOURCE_VERSION:=7de4820c87437033f6b7716018f3bfa60a3f7b12 PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=ISC @@ -68,8 +68,9 @@ endef # 1: plugin name -# 2: extra dependencies +# 2: additional dependencies # 3: plugin title/description +# 4: extra dependencies define BuildPlugin PKG_CONFIG_DEPENDS += CONFIG_PACKAGE_rpcd-mod-$(1) @@ -78,6 +79,7 @@ define BuildPlugin $(Package/rpcd/default) TITLE+= ($(1) plugin) DEPENDS+=rpcd $(2) + EXTRA_DEPENDS:=$(4) endef define Package/rpcd-mod-$(1)/description @@ -101,5 +103,5 @@ endef $(eval $(call BuildPackage,rpcd)) $(eval $(call BuildPlugin,file,,Provides ubus calls for file and directory operations.)) $(eval $(call BuildPlugin,rpcsys,,Provides ubus calls for sysupgrade and password changing.)) -$(eval $(call BuildPlugin,iwinfo,+libiwinfo,Provides ubus calls for accessing iwinfo data.)) +$(eval $(call BuildPlugin,iwinfo,+libiwinfo,Provides ubus calls for accessing iwinfo data.,libiwinfo (>= 2022-12-15))) $(eval $(call BuildPlugin,ucode,+libucode,Allows implementing plugins using ucode scripts.)) From 2fb38b77a23735fe55324f51437a1f83d853bafa Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 15 Dec 2022 13:57:04 +0100 Subject: [PATCH 054/110] hostapd: add support for automatically setting RADIUS own-ip dynamically Some servers use the NAS-IP-Address attribute as a destination address Signed-off-by: Felix Fietkau --- .../network/services/hostapd/files/hostapd.sh | 11 +- .../hostapd/patches/760-dynamic_own_ip.patch | 109 ++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 package/network/services/hostapd/patches/760-dynamic_own_ip.patch diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh index 55cc766b58c..eabfb21a9f1 100644 --- a/package/network/services/hostapd/files/hostapd.sh +++ b/package/network/services/hostapd/files/hostapd.sh @@ -687,7 +687,7 @@ hostapd_set_bss_options() { json_get_vars \ auth_server auth_secret auth_port \ dae_client dae_secret dae_port \ - ownip radius_client_addr \ + dynamic_ownip ownip radius_client_addr \ eap_reauth_period request_cui \ erp_domain mobility_domain \ fils_realm fils_dhcp @@ -695,6 +695,8 @@ hostapd_set_bss_options() { # radius can provide VLAN ID for clients vlan_possible=1 + set_default dynamic_ownip 1 + # legacy compatibility [ -n "$auth_server" ] || json_get_var auth_server server [ -n "$auth_port" ] || json_get_var auth_port port @@ -743,7 +745,12 @@ hostapd_set_bss_options() { } json_for_each_item append_radius_auth_req_attr radius_auth_req_attr - [ -n "$ownip" ] && append bss_conf "own_ip_addr=$ownip" "$N" + if [ -n "$ownip" ]; then + append bss_conf "own_ip_addr=$ownip" "$N" + elif [ "$dynamic_ownip" -gt 0 ]; then + append bss_conf "dynamic_own_ip_addr=$dynamic_ownip" "$N" + fi + [ -n "$radius_client_addr" ] && append bss_conf "radius_client_addr=$radius_client_addr" "$N" append bss_conf "eapol_key_index_workaround=1" "$N" append bss_conf "ieee8021x=1" "$N" diff --git a/package/network/services/hostapd/patches/760-dynamic_own_ip.patch b/package/network/services/hostapd/patches/760-dynamic_own_ip.patch new file mode 100644 index 00000000000..3d2b59e8cab --- /dev/null +++ b/package/network/services/hostapd/patches/760-dynamic_own_ip.patch @@ -0,0 +1,109 @@ +--- a/src/ap/ap_config.h ++++ b/src/ap/ap_config.h +@@ -311,6 +311,7 @@ struct hostapd_bss_config { + unsigned int eap_sim_db_timeout; + int eap_server_erp; /* Whether ERP is enabled on internal EAP server */ + struct hostapd_ip_addr own_ip_addr; ++ int dynamic_own_ip_addr; + char *nas_identifier; + struct hostapd_radius_servers *radius; + int acct_interim_interval; +--- a/src/radius/radius_client.c ++++ b/src/radius/radius_client.c +@@ -163,6 +163,8 @@ struct radius_client_data { + */ + void *ctx; + ++ struct hostapd_ip_addr local_ip; ++ + /** + * conf - RADIUS client configuration (list of RADIUS servers to use) + */ +@@ -720,6 +722,30 @@ static void radius_client_list_add(struc + + + /** ++ * radius_client_send - Get local address for the RADIUS auth socket ++ * @radius: RADIUS client context from radius_client_init() ++ * @addr: pointer to store the address ++ * ++ * This function returns the local address for the connection to the RADIUS ++ * auth server. It also opens the socket if it's not available yet. ++ */ ++int radius_client_get_local_addr(struct radius_client_data *radius, ++ struct hostapd_ip_addr *addr) ++{ ++ struct hostapd_radius_servers *conf = radius->conf; ++ ++ if (conf->auth_server && radius->auth_sock < 0) ++ radius_client_init_auth(radius); ++ ++ if (radius->auth_sock < 0) ++ return -1; ++ ++ memcpy(addr, &radius->local_ip, sizeof(*addr)); ++ ++ return 0; ++} ++ ++/** + * radius_client_send - Send a RADIUS request + * @radius: RADIUS client context from radius_client_init() + * @msg: RADIUS message to be sent +@@ -1238,6 +1264,10 @@ radius_change_server(struct radius_clien + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntoa(claddr.sin_addr), + ntohs(claddr.sin_port)); ++ if (auth) { ++ radius->local_ip.af = AF_INET; ++ radius->local_ip.u.v4 = claddr.sin_addr; ++ } + } + break; + #ifdef CONFIG_IPV6 +@@ -1249,6 +1279,10 @@ radius_change_server(struct radius_clien + inet_ntop(AF_INET6, &claddr6.sin6_addr, + abuf, sizeof(abuf)), + ntohs(claddr6.sin6_port)); ++ if (auth) { ++ radius->local_ip.af = AF_INET6; ++ radius->local_ip.u.v6 = claddr6.sin6_addr; ++ } + } + break; + } +--- a/src/radius/radius_client.h ++++ b/src/radius/radius_client.h +@@ -249,6 +249,8 @@ int radius_client_register(struct radius + void radius_client_set_interim_error_cb(struct radius_client_data *radius, + void (*cb)(const u8 *addr, void *ctx), + void *ctx); ++int radius_client_get_local_addr(struct radius_client_data *radius, ++ struct hostapd_ip_addr * addr); + int radius_client_send(struct radius_client_data *radius, + struct radius_msg *msg, + RadiusType msg_type, const u8 *addr); +--- a/src/ap/ieee802_1x.c ++++ b/src/ap/ieee802_1x.c +@@ -535,6 +535,10 @@ int add_common_radius_attr(struct hostap + struct hostapd_radius_attr *attr; + int len; + ++ if (hapd->conf->dynamic_own_ip_addr) ++ radius_client_get_local_addr(hapd->radius, ++ &hapd->conf->own_ip_addr); ++ + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IP_ADDRESS) && + hapd->conf->own_ip_addr.af == AF_INET && +--- a/hostapd/config_file.c ++++ b/hostapd/config_file.c +@@ -2681,6 +2681,8 @@ static int hostapd_config_fill(struct ho + } else if (os_strcmp(buf, "iapp_interface") == 0) { + wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used"); + #endif /* CONFIG_IAPP */ ++ } else if (os_strcmp(buf, "dynamic_own_ip_addr") == 0) { ++ bss->dynamic_own_ip_addr = atoi(pos); + } else if (os_strcmp(buf, "own_ip_addr") == 0) { + if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) { + wpa_printf(MSG_ERROR, From 92acd9f3e8312883c3cbc7b3885c969d72c36f77 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 16 Dec 2022 11:12:51 +0100 Subject: [PATCH 055/110] mac80211: fix uninitialized variable in rx->link/link_sta initialization fix Signed-off-by: Felix Fietkau --- ...x-initialization-of-rx-link-and-rx-l.patch | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch b/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch index 5652c14c9b8..8c6823d221d 100644 --- a/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch +++ b/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch @@ -186,15 +186,18 @@ Signed-off-by: Felix Fietkau stats->dropped++; return true; -@@ -4787,7 +4813,6 @@ static bool ieee80211_prepare_and_rx_han +@@ -4787,8 +4813,8 @@ static bool ieee80211_prepare_and_rx_han struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_hdr *hdr = (void *)skb->data; - struct link_sta_info *link_sta = NULL; - struct ieee80211_link_data *link; +- struct ieee80211_link_data *link; ++ struct link_sta_info *link_sta = rx->link_sta; ++ struct ieee80211_link_data *link = rx->link; rx->skb = skb; -@@ -4810,35 +4835,6 @@ static bool ieee80211_prepare_and_rx_han + +@@ -4810,35 +4836,6 @@ static bool ieee80211_prepare_and_rx_han if (!ieee80211_accept_frame(rx)) return false; @@ -230,7 +233,7 @@ Signed-off-by: Felix Fietkau if (!consume) { struct skb_shared_hwtstamps *shwt; -@@ -4858,16 +4854,16 @@ static bool ieee80211_prepare_and_rx_han +@@ -4858,7 +4855,7 @@ static bool ieee80211_prepare_and_rx_han shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp; } @@ -239,18 +242,7 @@ Signed-off-by: Felix Fietkau /* translate to MLD addresses */ if (ether_addr_equal(link->conf->addr, hdr->addr1)) ether_addr_copy(hdr->addr1, rx->sdata->vif.addr); -- if (ether_addr_equal(link_sta->addr, hdr->addr2)) -+ if (ether_addr_equal(rx->link_sta->addr, hdr->addr2)) - ether_addr_copy(hdr->addr2, rx->sta->addr); - /* translate A3 only if it's the BSSID */ - if (!ieee80211_has_tods(hdr->frame_control) && - !ieee80211_has_fromds(hdr->frame_control)) { -- if (ether_addr_equal(link_sta->addr, hdr->addr3)) -+ if (ether_addr_equal(rx->link_sta->addr, hdr->addr3)) - ether_addr_copy(hdr->addr3, rx->sta->addr); - else if (ether_addr_equal(link->conf->addr, hdr->addr3)) - ether_addr_copy(hdr->addr3, rx->sdata->vif.addr); -@@ -4888,6 +4884,7 @@ static void __ieee80211_rx_handle_8023(s +@@ -4888,6 +4885,7 @@ static void __ieee80211_rx_handle_8023(s struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_fast_rx *fast_rx; struct ieee80211_rx_data rx; @@ -258,7 +250,7 @@ Signed-off-by: Felix Fietkau memset(&rx, 0, sizeof(rx)); rx.skb = skb; -@@ -4904,12 +4901,8 @@ static void __ieee80211_rx_handle_8023(s +@@ -4904,12 +4902,8 @@ static void __ieee80211_rx_handle_8023(s if (!pubsta) goto drop; @@ -273,7 +265,7 @@ Signed-off-by: Felix Fietkau /* * TODO: Should the frame be dropped if the right link_id is not -@@ -4918,19 +4911,8 @@ static void __ieee80211_rx_handle_8023(s +@@ -4918,19 +4912,8 @@ static void __ieee80211_rx_handle_8023(s * link_id is used only for stats purpose and updating the stats on * the deflink is fine? */ @@ -295,7 +287,7 @@ Signed-off-by: Felix Fietkau fast_rx = rcu_dereference(rx.sta->fast_rx); if (!fast_rx) -@@ -4948,6 +4930,8 @@ static bool ieee80211_rx_for_interface(s +@@ -4948,6 +4931,8 @@ static bool ieee80211_rx_for_interface(s { struct link_sta_info *link_sta; struct ieee80211_hdr *hdr = (void *)skb->data; @@ -304,7 +296,7 @@ Signed-off-by: Felix Fietkau /* * Look up link station first, in case there's a -@@ -4957,24 +4941,19 @@ static bool ieee80211_rx_for_interface(s +@@ -4957,24 +4942,19 @@ static bool ieee80211_rx_for_interface(s */ link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2); if (link_sta) { @@ -337,7 +329,7 @@ Signed-off-by: Felix Fietkau return ieee80211_prepare_and_rx_handle(rx, skb, consume); } -@@ -5033,19 +5012,15 @@ static void __ieee80211_rx_handle_packet +@@ -5033,19 +5013,15 @@ static void __ieee80211_rx_handle_packet if (ieee80211_is_data(fc)) { struct sta_info *sta, *prev_sta; @@ -362,7 +354,7 @@ Signed-off-by: Felix Fietkau /* * In MLO connection, fetch the link_id using addr2 * when the driver does not pass link_id in status. -@@ -5063,7 +5038,7 @@ static void __ieee80211_rx_handle_packet +@@ -5063,7 +5039,7 @@ static void __ieee80211_rx_handle_packet if (!link_sta) goto out; @@ -371,7 +363,7 @@ Signed-off-by: Felix Fietkau } if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) -@@ -5079,30 +5054,25 @@ static void __ieee80211_rx_handle_packet +@@ -5079,30 +5055,25 @@ static void __ieee80211_rx_handle_packet continue; } From 133a1e2f3787ec995561398bda7571180598454f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 16 Dec 2022 11:37:30 +0100 Subject: [PATCH 056/110] mac80211: work around a race condition on detecting a phy while bringing it up When reloading modules and running wifi, a phy can sometimes be renamed while in the middle of a hotplug call that tries to detect new phys This can lead to bogus wifi-device sections being created Signed-off-by: Felix Fietkau --- package/kernel/mac80211/files/lib/wifi/mac80211.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package/kernel/mac80211/files/lib/wifi/mac80211.sh b/package/kernel/mac80211/files/lib/wifi/mac80211.sh index 3ecd932228f..e24a2a634ea 100644 --- a/package/kernel/mac80211/files/lib/wifi/mac80211.sh +++ b/package/kernel/mac80211/files/lib/wifi/mac80211.sh @@ -167,6 +167,10 @@ detect_mac80211() { path="$(iwinfo nl80211 path "$dev")" macaddr="$(cat /sys/class/ieee80211/${dev}/macaddress)" + + # work around phy rename related race condition + [ -n "$path" -o -n "$macaddr" ] || continue + board_dev= fallback_board_dev= json_for_each_item check_board_phy wlan From c2fde432b3de2d0e153e436f1b7e3f64542e5edc Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 16 Dec 2022 12:53:05 +0100 Subject: [PATCH 057/110] hostapd: always set a default for the nas identifier It is used for both 802.11r and WPA enterprise. Setting it when not needed is harmless Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package/network/services/hostapd/files/hostapd.sh b/package/network/services/hostapd/files/hostapd.sh index eabfb21a9f1..b165995d9f9 100644 --- a/package/network/services/hostapd/files/hostapd.sh +++ b/package/network/services/hostapd/files/hostapd.sh @@ -611,7 +611,9 @@ hostapd_set_bss_options() { [ -n "$wpa_strict_rekey" ] && append bss_conf "wpa_strict_rekey=$wpa_strict_rekey" "$N" } - [ -n "$nasid" ] && append bss_conf "nas_identifier=$nasid" "$N" + set_default nasid "${macaddr//\:}" + append bss_conf "nas_identifier=$nasid" "$N" + [ -n "$acct_server" ] && { append bss_conf "acct_server_addr=$acct_server" "$N" append bss_conf "acct_server_port=$acct_port" "$N" @@ -912,7 +914,6 @@ hostapd_set_bss_options() { append bss_conf "ft_psk_generate_local=$ft_psk_generate_local" "$N" append bss_conf "ft_over_ds=$ft_over_ds" "$N" append bss_conf "reassociation_deadline=$reassociation_deadline" "$N" - [ -n "$nasid" ] || append bss_conf "nas_identifier=${macaddr//\:}" "$N" if [ "$ft_psk_generate_local" -eq "0" ]; then json_get_vars r0_key_lifetime r1_key_holder pmk_r1_push From 581f2b15b2126e93575ac9cca2f7f02f913dc333 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 16 Dec 2022 13:09:36 +0100 Subject: [PATCH 058/110] hostapd: enable coredumps Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/wpad.init | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/network/services/hostapd/files/wpad.init b/package/network/services/hostapd/files/wpad.init index 79c5bf1075c..65d46df982a 100644 --- a/package/network/services/hostapd/files/wpad.init +++ b/package/network/services/hostapd/files/wpad.init @@ -13,6 +13,7 @@ start_service() { procd_open_instance hostapd procd_set_param command /usr/sbin/hostapd -s -g /var/run/hostapd/global procd_set_param respawn 3600 1 0 + procd_set_param limits core="unlimited" [ -x /sbin/ujail -a -e /etc/capabilities/wpad.json ] && { procd_add_jail hostapd procd_set_param capabilities /etc/capabilities/wpad.json @@ -29,6 +30,7 @@ start_service() { procd_open_instance supplicant procd_set_param command /usr/sbin/wpa_supplicant -n -s -g /var/run/wpa_supplicant/global procd_set_param respawn 3600 1 0 + procd_set_param limits core="unlimited" [ -x /sbin/ujail -a -e /etc/capabilities/wpad.json ] && { procd_add_jail wpa_supplicant procd_set_param capabilities /etc/capabilities/wpad.json From d07d47d0e3146a4d332c48fadbcff2e2806e1d27 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 16 Dec 2022 14:31:55 +0100 Subject: [PATCH 059/110] mac80211: fix a regression in processing management frames for AP VLAN interfaces Fixes re-assoc of WDS stations Signed-off-by: Felix Fietkau --- ...x-initialization-of-rx-link-and-rx-l.patch | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch b/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch index 8c6823d221d..857c1c84470 100644 --- a/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch +++ b/package/kernel/mac80211/patches/subsys/307-wifi-mac80211-fix-initialization-of-rx-link-and-rx-l.patch @@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c -@@ -4067,6 +4067,55 @@ static void ieee80211_invoke_rx_handlers +@@ -4067,6 +4067,56 @@ static void ieee80211_invoke_rx_handlers #undef CALL_RXH } @@ -59,7 +59,8 @@ Signed-off-by: Felix Fietkau + + if (sta) { + rx->local = sta->sdata->local; -+ rx->sdata = sta->sdata; ++ if (!rx->sdata) ++ rx->sdata = sta->sdata; + rx->link_sta = &sta->deflink; + + if (link_id >= 0 && @@ -76,7 +77,7 @@ Signed-off-by: Felix Fietkau /* * This function makes calls into the RX path, therefore * it has to be invoked under RCU read lock. -@@ -4075,16 +4124,19 @@ void ieee80211_release_reorder_timeout(s +@@ -4075,16 +4125,19 @@ void ieee80211_release_reorder_timeout(s { struct sk_buff_head frames; struct ieee80211_rx_data rx = { @@ -101,7 +102,7 @@ Signed-off-by: Felix Fietkau tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); if (!tid_agg_rx) -@@ -4104,10 +4156,6 @@ void ieee80211_release_reorder_timeout(s +@@ -4104,10 +4157,6 @@ void ieee80211_release_reorder_timeout(s }; drv_event_callback(rx.local, rx.sdata, &event); } @@ -112,7 +113,7 @@ Signed-off-by: Felix Fietkau ieee80211_rx_handlers(&rx, &frames); } -@@ -4123,7 +4171,6 @@ void ieee80211_mark_rx_ba_filtered_frame +@@ -4123,7 +4172,6 @@ void ieee80211_mark_rx_ba_filtered_frame /* This is OK -- must be QoS data frame */ .security_idx = tid, .seqno_idx = tid, @@ -120,7 +121,7 @@ Signed-off-by: Felix Fietkau }; int i, diff; -@@ -4134,10 +4181,8 @@ void ieee80211_mark_rx_ba_filtered_frame +@@ -4134,10 +4182,8 @@ void ieee80211_mark_rx_ba_filtered_frame sta = container_of(pubsta, struct sta_info, sta); @@ -133,7 +134,7 @@ Signed-off-by: Felix Fietkau rcu_read_lock(); tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); -@@ -4524,15 +4569,6 @@ void ieee80211_check_fast_rx_iface(struc +@@ -4524,15 +4570,6 @@ void ieee80211_check_fast_rx_iface(struc mutex_unlock(&local->sta_mtx); } @@ -149,7 +150,7 @@ Signed-off-by: Felix Fietkau static void ieee80211_rx_8023(struct ieee80211_rx_data *rx, struct ieee80211_fast_rx *fast_rx, int orig_len) -@@ -4643,7 +4679,6 @@ static bool ieee80211_invoke_fast_rx(str +@@ -4643,7 +4680,6 @@ static bool ieee80211_invoke_fast_rx(str struct sk_buff *skb = rx->skb; struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -157,7 +158,7 @@ Signed-off-by: Felix Fietkau int orig_len = skb->len; int hdrlen = ieee80211_hdrlen(hdr->frame_control); int snap_offs = hdrlen; -@@ -4655,7 +4690,6 @@ static bool ieee80211_invoke_fast_rx(str +@@ -4655,7 +4691,6 @@ static bool ieee80211_invoke_fast_rx(str u8 da[ETH_ALEN]; u8 sa[ETH_ALEN]; } addrs __aligned(2); @@ -165,7 +166,7 @@ Signed-off-by: Felix Fietkau struct ieee80211_sta_rx_stats *stats; /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write -@@ -4758,18 +4792,10 @@ static bool ieee80211_invoke_fast_rx(str +@@ -4758,18 +4793,10 @@ static bool ieee80211_invoke_fast_rx(str drop: dev_kfree_skb(skb); @@ -186,7 +187,7 @@ Signed-off-by: Felix Fietkau stats->dropped++; return true; -@@ -4787,8 +4813,8 @@ static bool ieee80211_prepare_and_rx_han +@@ -4787,8 +4814,8 @@ static bool ieee80211_prepare_and_rx_han struct ieee80211_local *local = rx->local; struct ieee80211_sub_if_data *sdata = rx->sdata; struct ieee80211_hdr *hdr = (void *)skb->data; @@ -197,7 +198,7 @@ Signed-off-by: Felix Fietkau rx->skb = skb; -@@ -4810,35 +4836,6 @@ static bool ieee80211_prepare_and_rx_han +@@ -4810,35 +4837,6 @@ static bool ieee80211_prepare_and_rx_han if (!ieee80211_accept_frame(rx)) return false; @@ -233,7 +234,7 @@ Signed-off-by: Felix Fietkau if (!consume) { struct skb_shared_hwtstamps *shwt; -@@ -4858,7 +4855,7 @@ static bool ieee80211_prepare_and_rx_han +@@ -4858,7 +4856,7 @@ static bool ieee80211_prepare_and_rx_han shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp; } @@ -242,7 +243,7 @@ Signed-off-by: Felix Fietkau /* translate to MLD addresses */ if (ether_addr_equal(link->conf->addr, hdr->addr1)) ether_addr_copy(hdr->addr1, rx->sdata->vif.addr); -@@ -4888,6 +4885,7 @@ static void __ieee80211_rx_handle_8023(s +@@ -4888,6 +4886,7 @@ static void __ieee80211_rx_handle_8023(s struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_fast_rx *fast_rx; struct ieee80211_rx_data rx; @@ -250,7 +251,7 @@ Signed-off-by: Felix Fietkau memset(&rx, 0, sizeof(rx)); rx.skb = skb; -@@ -4904,12 +4902,8 @@ static void __ieee80211_rx_handle_8023(s +@@ -4904,12 +4903,8 @@ static void __ieee80211_rx_handle_8023(s if (!pubsta) goto drop; @@ -265,7 +266,7 @@ Signed-off-by: Felix Fietkau /* * TODO: Should the frame be dropped if the right link_id is not -@@ -4918,19 +4912,8 @@ static void __ieee80211_rx_handle_8023(s +@@ -4918,19 +4913,8 @@ static void __ieee80211_rx_handle_8023(s * link_id is used only for stats purpose and updating the stats on * the deflink is fine? */ @@ -287,7 +288,7 @@ Signed-off-by: Felix Fietkau fast_rx = rcu_dereference(rx.sta->fast_rx); if (!fast_rx) -@@ -4948,6 +4931,8 @@ static bool ieee80211_rx_for_interface(s +@@ -4948,6 +4932,8 @@ static bool ieee80211_rx_for_interface(s { struct link_sta_info *link_sta; struct ieee80211_hdr *hdr = (void *)skb->data; @@ -296,7 +297,7 @@ Signed-off-by: Felix Fietkau /* * Look up link station first, in case there's a -@@ -4957,24 +4942,19 @@ static bool ieee80211_rx_for_interface(s +@@ -4957,24 +4943,19 @@ static bool ieee80211_rx_for_interface(s */ link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2); if (link_sta) { @@ -329,7 +330,7 @@ Signed-off-by: Felix Fietkau return ieee80211_prepare_and_rx_handle(rx, skb, consume); } -@@ -5033,19 +5013,15 @@ static void __ieee80211_rx_handle_packet +@@ -5033,19 +5014,15 @@ static void __ieee80211_rx_handle_packet if (ieee80211_is_data(fc)) { struct sta_info *sta, *prev_sta; @@ -354,7 +355,7 @@ Signed-off-by: Felix Fietkau /* * In MLO connection, fetch the link_id using addr2 * when the driver does not pass link_id in status. -@@ -5063,7 +5039,7 @@ static void __ieee80211_rx_handle_packet +@@ -5063,7 +5040,7 @@ static void __ieee80211_rx_handle_packet if (!link_sta) goto out; @@ -363,7 +364,7 @@ Signed-off-by: Felix Fietkau } if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) -@@ -5079,30 +5055,25 @@ static void __ieee80211_rx_handle_packet +@@ -5079,30 +5056,25 @@ static void __ieee80211_rx_handle_packet continue; } From 9e2c01e4a6a5c9b7c77fde90306af5f441ef2cb0 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Fri, 16 Dec 2022 20:41:33 +0800 Subject: [PATCH 060/110] kernel: add driver package for WS2812B LEDs Two WS2812B (NeoPixel) clones are used in Xiaomi Redmi AX6000 as indicator lights. Add a driver for controlling it using SPI MOSI. Signed-off-by: Chuanhong Guo --- package/kernel/leds-ws2812b/Makefile | 34 +++ package/kernel/leds-ws2812b/src/Makefile | 1 + .../kernel/leds-ws2812b/src/leds-ws2812b.c | 233 ++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 package/kernel/leds-ws2812b/Makefile create mode 100644 package/kernel/leds-ws2812b/src/Makefile create mode 100644 package/kernel/leds-ws2812b/src/leds-ws2812b.c diff --git a/package/kernel/leds-ws2812b/Makefile b/package/kernel/leds-ws2812b/Makefile new file mode 100644 index 00000000000..453d2590f65 --- /dev/null +++ b/package/kernel/leds-ws2812b/Makefile @@ -0,0 +1,34 @@ +# +# Copyright (C) 2008-2010 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=leds-ws2812b +PKG_RELEASE:=1 +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/leds-ws2812b + SUBMENU:=LED modules + TITLE:=Worldsemi WS2812B (NeoPixel) LED support + FILES:= \ + $(PKG_BUILD_DIR)/leds-ws2812b.ko + AUTOLOAD:=$(call AutoProbe,leds-ws2812b,1) + DEPENDS:=@TARGET_mediatek_filogic +endef + +define KernelPackage/leds-ws2812b/description + LED support for driving WS2812B (NeoPixel) using SPI MOSI. +endef + +define Build/Compile + $(KERNEL_MAKE) M="$(PKG_BUILD_DIR)" modules +endef + +$(eval $(call KernelPackage,leds-ws2812b)) diff --git a/package/kernel/leds-ws2812b/src/Makefile b/package/kernel/leds-ws2812b/src/Makefile new file mode 100644 index 00000000000..718a9f708de --- /dev/null +++ b/package/kernel/leds-ws2812b/src/Makefile @@ -0,0 +1 @@ +obj-m := leds-ws2812b.o diff --git a/package/kernel/leds-ws2812b/src/leds-ws2812b.c b/package/kernel/leds-ws2812b/src/leds-ws2812b.c new file mode 100644 index 00000000000..b0d13f52427 --- /dev/null +++ b/package/kernel/leds-ws2812b/src/leds-ws2812b.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * WorldSemi WS2812B individually-addressable LED driver using SPI + * + * Copyright 2022 Chuanhong Guo + * + * This driver simulates WS2812B protocol using SPI MOSI pin. A one pulse + * is transferred as 3'b110 and a zero pulse is 3'b100. For this driver to + * work properly, the SPI frequency should be 2.105MHz~2.85MHz and it needs + * to transfer all the bytes continuously. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define WS2812B_BYTES_PER_COLOR 3 +#define WS2812B_NUM_COLORS 3 +/* A continuous 0 for 50us+ as the 'reset' signal */ +#define WS2812B_RESET_LEN 18 + +struct ws2812b_led { + struct led_classdev_mc mc_cdev; + struct mc_subled subled[WS2812B_NUM_COLORS]; + int cascade; +}; + +struct ws2812b_priv { + struct led_classdev ldev; + struct spi_device *spi; + struct mutex mutex; + int num_leds; + size_t data_len; + u8 *data_buf; + struct ws2812b_led leds[]; +}; + +/** + * ws2812b_set_byte - convert a byte of data to 3-byte SPI data for pulses + * @priv: pointer to the private data structure + * @offset: offset of the target byte in the data stream + * @val: 1-byte data to be set + * + * WS2812B receives a stream of bytes from DI, takes the first 3 byte as LED + * brightness and pases the rest to the next LED through the DO pin. + * This function assembles a single byte of data to the LED: + * A bit is represented with a pulse of specific length. A long pulse is a 1 + * and a short pulse is a 0. + * SPI transfers data continuously, MSB first. We can send 3'b100 to create a + * 0 pulse and 3'b110 for a 1 pulse. In this way, a byte of data takes up 3 + * bytes in a SPI transfer: + * 1x0 1x0 1x0 1x0 1x0 1x0 1x0 1x0 + * Let's rearrange it in 8 bits: + * 1x01x01x 01x01x01 x01x01x0 + * The higher 3 bits, middle 2 bits and lower 3 bits are represented with the + * 1st, 2nd and 3rd byte in the SPI transfer respectively. + * There are only 8 combinations for 3 bits and 4 for 2 bits, so we can create + * a lookup table for the 3 bytes. + * e.g. For 0x6b -> 2'b01101011: + * Bit 7-5: 3'b011 -> 10011011 -> 0x9b + * Bit 4-3: 2'b01 -> 01001101 -> 0x4d + * Bit 2-0: 3'b011 -> 00110110 -> 0x36 + */ +static void ws2812b_set_byte(struct ws2812b_priv *priv, size_t offset, u8 val) +{ + /* The lookup table for Bit 7-5 4-3 2-0 */ + const u8 h3b[] = { 0x92, 0x93, 0x9a, 0x9b, 0xd2, 0xd3, 0xda, 0xdb }; + const u8 m2b[] = { 0x49, 0x4d, 0x69, 0x6d }; + const u8 l3b[] = { 0x24, 0x26, 0x34, 0x36, 0xa4, 0xa6, 0xb4, 0xb6 }; + u8 *p = priv->data_buf + WS2812B_RESET_LEN + (offset * WS2812B_BYTES_PER_COLOR); + + p[0] = h3b[val >> 5]; /* Bit 7-5 */ + p[1] = m2b[(val >> 3) & 0x3]; /* Bit 4-3 */ + p[2] = l3b[val & 0x7]; /* Bit 2-0 */ +} + +static int ws2812b_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct ws2812b_led *led = + container_of(mc_cdev, struct ws2812b_led, mc_cdev); + struct ws2812b_priv *priv = dev_get_drvdata(cdev->dev->parent); + int ret; + int i; + + led_mc_calc_color_components(mc_cdev, brightness); + + mutex_lock(&priv->mutex); + for (i = 0; i < WS2812B_NUM_COLORS; i++) + ws2812b_set_byte(priv, led->cascade * WS2812B_NUM_COLORS + i, + led->subled[i].brightness); + ret = spi_write(priv->spi, priv->data_buf, priv->data_len); + mutex_unlock(&priv->mutex); + + return ret; +} + +static int ws2812b_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + int cur_led = 0; + struct ws2812b_priv *priv; + struct fwnode_handle *led_node; + int num_leds, i, cnt, ret; + + num_leds = device_get_child_node_count(dev); + + priv = devm_kzalloc(dev, struct_size(priv, leds, num_leds), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->data_len = + num_leds * WS2812B_BYTES_PER_COLOR * WS2812B_NUM_COLORS + + WS2812B_RESET_LEN; + priv->data_buf = kzalloc(priv->data_len, GFP_KERNEL); + if (!priv->data_buf) + return -ENOMEM; + + for (i = 0; i < num_leds * WS2812B_NUM_COLORS; i++) + ws2812b_set_byte(priv, i, 0); + + mutex_init(&priv->mutex); + priv->num_leds = num_leds; + priv->spi = spi; + + device_for_each_child_node(dev, led_node) { + struct led_init_data init_data = { + .fwnode = led_node, + }; + /* WS2812B LEDs usually come with GRB color */ + u32 color_idx[WS2812B_NUM_COLORS] = { + LED_COLOR_ID_GREEN, + LED_COLOR_ID_RED, + LED_COLOR_ID_BLUE, + }; + u32 cascade; + + ret = fwnode_property_read_u32(led_node, "reg", &cascade); + if (ret) { + dev_err(dev, "failed to obtain numerical LED index for %s", + fwnode_get_name(led_node)); + goto ERR_UNREG_LEDS; + } + if (cascade >= num_leds) { + dev_err(dev, "LED index of %s is larger than the number of LEDs.", + fwnode_get_name(led_node)); + ret = -EINVAL; + goto ERR_UNREG_LEDS; + } + + cnt = fwnode_property_count_u32(led_node, "color-index"); + if (cnt > 0 && cnt <= WS2812B_NUM_COLORS) + fwnode_property_read_u32_array(led_node, "color-index", + color_idx, (size_t)cnt); + + priv->leds[cur_led].mc_cdev.subled_info = + priv->leds[cur_led].subled; + priv->leds[cur_led].mc_cdev.num_colors = WS2812B_NUM_COLORS; + priv->leds[cur_led].mc_cdev.led_cdev.max_brightness = 255; + priv->leds[cur_led].mc_cdev.led_cdev.brightness_set_blocking = ws2812b_set; + + for (i = 0; i < WS2812B_NUM_COLORS; i++) { + priv->leds[cur_led].subled[i].color_index = color_idx[i]; + priv->leds[cur_led].subled[i].intensity = 255; + } + + priv->leds[cur_led].cascade = cascade; + + ret = led_classdev_multicolor_register_ext( + dev, &priv->leds[cur_led].mc_cdev, &init_data); + if (ret) { + dev_err(dev, "registration of %s failed.", + fwnode_get_name(led_node)); + goto ERR_UNREG_LEDS; + } + cur_led++; + } + + spi_set_drvdata(spi, priv); + + return 0; +ERR_UNREG_LEDS: + for (; cur_led >= 0; cur_led--) + led_classdev_multicolor_unregister(&priv->leds[cur_led].mc_cdev); + mutex_destroy(&priv->mutex); + kfree(priv->data_buf); + return ret; +} + +static int ws2812b_remove(struct spi_device *spi) +{ + struct ws2812b_priv *priv = spi_get_drvdata(spi); + int cur_led; + + for (cur_led = priv->num_leds - 1; cur_led >= 0; cur_led--) + led_classdev_multicolor_unregister(&priv->leds[cur_led].mc_cdev); + kfree(priv->data_buf); + mutex_destroy(&priv->mutex); + + return 0; +} + +static const struct spi_device_id ws2812b_spi_ids[] = { + { "ws2812b" }, + {}, +}; +MODULE_DEVICE_TABLE(spi, ws2812b_spi_ids); + +static const struct of_device_id ws2812b_dt_ids[] = { + { .compatible = "worldsemi,ws2812b" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ws2812b_dt_ids); + +static struct spi_driver ws2812b_driver = { + .probe = ws2812b_probe, + .remove = ws2812b_remove, + .id_table = ws2812b_spi_ids, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ws2812b_dt_ids, + }, +}; + +module_spi_driver(ws2812b_driver); + +MODULE_AUTHOR("Chuanhong Guo "); +MODULE_DESCRIPTION("WS2812B LED driver using SPI"); +MODULE_LICENSE("GPL"); From 335c1e7cfdfd477a1106ca05c96ff8949ecd953f Mon Sep 17 00:00:00 2001 From: Furong Xu Date: Fri, 16 Dec 2022 21:13:37 +0800 Subject: [PATCH 061/110] mediatek: enable sel_clk for spi-mt65xx Without explicitly enabling sel_clk, clk_disable_unused() will disable it when boot is done, causing CPU hang on SPI1 register access on MT7986. Explicitly enable sel_clk to make SPI1 functional. Signed-off-by: Furong Xu Signed-off-by: Chuanhong Guo --- .../930-spi-mt65xx-enable-sel-clk.patch | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch diff --git a/target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch b/target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch new file mode 100644 index 00000000000..904f2ef3a2f --- /dev/null +++ b/target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch @@ -0,0 +1,29 @@ +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -1223,10 +1223,16 @@ static int mtk_spi_probe(struct platform + goto err_disable_spi_hclk; + } + ++ ret = clk_prepare_enable(mdata->sel_clk); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "failed to enable sel_clk (%d)\n", ret); ++ goto err_disable_spi_clk; ++ } ++ + ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); +- goto err_disable_spi_clk; ++ goto err_disable_spi_sel_clk; + } + + mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk); +@@ -1277,6 +1283,8 @@ static int mtk_spi_probe(struct platform + + err_disable_runtime_pm: + pm_runtime_disable(&pdev->dev); ++err_disable_spi_sel_clk: ++ clk_disable_unprepare(mdata->sel_clk); + err_disable_spi_clk: + clk_disable_unprepare(mdata->spi_clk); + err_disable_spi_hclk: From d9e13625810edf7ca8353ec5c23b47fb59606a3d Mon Sep 17 00:00:00 2001 From: Furong Xu Date: Fri, 16 Dec 2022 21:21:33 +0800 Subject: [PATCH 062/110] mediatek: add LED support for Xiaomi Redmi AX6000 add DT nodes and default package for the LEDs on Redmi AX6000 Signed-off-by: Furong Xu Signed-off-by: Chuanhong Guo --- .../mt7986a-xiaomi-redmi-router-ax6000.dts | 38 +++++++++++++++++++ .../filogic/base-files/etc/board.d/01_leds | 16 ++++++++ target/linux/mediatek/image/filogic.mk | 1 + 3 files changed, 55 insertions(+) create mode 100644 target/linux/mediatek/filogic/base-files/etc/board.d/01_leds diff --git a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts index 7c590d22e22..31112664c2e 100644 --- a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts +++ b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts @@ -3,6 +3,7 @@ /dts-v1/; #include #include +#include #include "mt7986a.dtsi" @@ -12,6 +13,10 @@ aliases { serial0 = &uart0; + led-boot = &led_status_rgb; + led-failsafe = &led_status_rgb; + led-running = &led_status_rgb; + led-upgrade = &led_status_rgb; }; chosen { @@ -95,6 +100,13 @@ }; }; + spi_led_pins: spic-pins-29-to-32 { + mux { + function = "spi"; + groups = "spi1_2"; + }; + }; + wf_2g_5g_pins: wf_2g_5g-pins { mux { function = "wifi"; @@ -202,6 +214,32 @@ }; }; +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_led_pins>; + status = "okay"; + + ws2812b@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "worldsemi,ws2812b"; + reg = <0>; + spi-max-frequency = <3000000>; + + led_status_rgb: led@0 { + reg = <0>; + label = "rgb:status"; + color-index = ; + }; + + led_network_rgb: led@1 { + reg = <1>; + label = "rgb:network"; + color-index = ; + }; + }; +}; + &switch { ports { #address-cells = <1>; diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/01_leds b/target/linux/mediatek/filogic/base-files/etc/board.d/01_leds new file mode 100644 index 00000000000..f851c7ed54b --- /dev/null +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/01_leds @@ -0,0 +1,16 @@ +. /lib/functions/leds.sh +. /lib/functions/uci-defaults.sh + +board=$(board_name) + +board_config_update + +case $board in +xiaomi,redmi-router-ax6000) + ucidef_set_led_netdev "wan" "wan" "rgb:network" "wan" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/mediatek/image/filogic.mk b/target/linux/mediatek/image/filogic.mk index 1ece1465c23..5dbeedb64c7 100644 --- a/target/linux/mediatek/image/filogic.mk +++ b/target/linux/mediatek/image/filogic.mk @@ -130,6 +130,7 @@ define Device/xiaomi_redmi-router-ax6000 DEVICE_MODEL := Redmi Router AX6000 DEVICE_DTS := mt7986a-xiaomi-redmi-router-ax6000 DEVICE_DTS_DIR := ../dts + DEVICE_PACKAGES := kmod-leds-ws2812b KERNEL_LOADADDR := 0x48000000 UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k From 819b676af05f74dc2a1e432c3972bae63d7c4495 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 8 Dec 2022 14:59:40 +0100 Subject: [PATCH 063/110] CI: improve build naming for shared workflow Impove build naming for build shared workflow to better understand what is being test. Signed-off-by: Christian Marangi --- .github/workflows/build.yml | 4 ++-- .github/workflows/packages.yml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1c665cb8698..783943badd9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ permissions: jobs: setup_build: - name: Setup build + name: Setup build ${{ inputs.target }} runs-on: ubuntu-latest outputs: owner_lc: ${{ steps.lower_owner.outputs.owner_lc }} @@ -80,7 +80,7 @@ jobs: echo "container_tag=$CONTAINER_TAG" >> $GITHUB_OUTPUT build: - name: Build with external toolchain + name: Build ${{ inputs.target }} needs: setup_build runs-on: ubuntu-latest diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 94982ba65bf..0711a986772 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -25,6 +25,7 @@ permissions: jobs: build: + name: Build Packages with external toolchain permissions: contents: read packages: read From dcdb0b064a5165ef1958a121228da2e02df3e576 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 7 Dec 2022 14:44:34 +0100 Subject: [PATCH 064/110] CI: build: make kernel build configurable Make kernel build configurable to permit to introduce toolchain testing. Signed-off-by: Christian Marangi --- .github/workflows/build.yml | 4 ++++ .github/workflows/kernel.yml | 1 + .github/workflows/packages.yml | 1 + 3 files changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 783943badd9..42db3669ace 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,8 @@ on: type: boolean build_full: type: boolean + build_kernel: + type: boolean build_all_modules: type: boolean build_all_kmods: @@ -338,11 +340,13 @@ jobs: run: make toolchain/install -j$(nproc) BUILD_LOG=1 || ret=$? .github/workflows/scripts/show_build_failures.sh - name: Build Kernel + if: inputs.build_kernel == true shell: su buildbot -c "sh -e {0}" working-directory: openwrt run: make target/compile -j$(nproc) BUILD_LOG=1 || ret=$? .github/workflows/scripts/show_build_failures.sh - name: Build Kernel Kmods + if: inputs.build_kernel == true shell: su buildbot -c "sh -e {0}" working-directory: openwrt run: make package/linux/compile -j$(nproc) BUILD_LOG=1 || ret=$? .github/workflows/scripts/show_build_failures.sh diff --git a/.github/workflows/kernel.yml b/.github/workflows/kernel.yml index 0fd03749f3a..d886002ed16 100644 --- a/.github/workflows/kernel.yml +++ b/.github/workflows/kernel.yml @@ -67,6 +67,7 @@ jobs: uses: ./.github/workflows/build.yml with: target: ${{ matrix.target }} + build_kernel: true build_all_kmods: true check-kernel-patches: diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 0711a986772..7bcaa2b3d33 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -38,6 +38,7 @@ jobs: uses: ./.github/workflows/build.yml with: target: ${{ matrix.target }} + build_kernel: true build_all_kmods: true build_all_modules: true build_full: true From 1b8c8864c5e27c5abeaa87bd7cfc24cd468bc71b Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 7 Dec 2022 00:52:41 +0100 Subject: [PATCH 065/110] CI: introduce target toolchain test Add test to build test toolchain for each target. Signed-off-by: Christian Marangi --- .github/workflows/toolchain.yml | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/toolchain.yml diff --git a/.github/workflows/toolchain.yml b/.github/workflows/toolchain.yml new file mode 100644 index 00000000000..d6abab4dec4 --- /dev/null +++ b/.github/workflows/toolchain.yml @@ -0,0 +1,64 @@ +name: Build Toolchains + +on: + pull_request: + paths: + - '.github/workflows/build.yml' + - '.github/workflows/toolchain.yml' + - 'toolchain/**' + push: + paths: + - '.github/workflows/build.yml' + - '.github/workflows/toolchain.yml' + - 'toolchain/**' + +permissions: + contents: read + +jobs: + determine_targets: + name: Set targets + runs-on: ubuntu-latest + outputs: + target: ${{ steps.find_targets.outputs.target }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set targets + id: find_targets + run: | + export TARGETS="$(perl ./scripts/dump-target-info.pl targets 2>/dev/null \ + | sort -u -t '/' -k1,1 \ + | awk '{ print $1 }')" + + JSON='[' + FIRST=1 + for TARGET in $TARGETS; do + [[ $FIRST -ne 1 ]] && JSON="$JSON"',' + JSON="$JSON"'"'"${TARGET}"'"' + FIRST=0 + done + JSON="$JSON"']' + + echo -e "\n---- targets ----\n" + echo "$JSON" + echo -e "\n---- targets ----\n" + + echo "target=$JSON" >> $GITHUB_OUTPUT + + build: + name: Build Target Toolchain + needs: determine_targets + permissions: + contents: read + packages: read + strategy: + fail-fast: False + matrix: + target: ${{fromJson(needs.determine_targets.outputs.target)}} + uses: ./.github/workflows/build.yml + with: + target: ${{ matrix.target }} + build_toolchain: true From d7876daf6552a9f39bd5e0bf50b554e9406ec275 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 14 Oct 2022 21:00:39 +0200 Subject: [PATCH 066/110] generic: add support for EON EN25QX128A spi nor flash Add support for EON EN25QX128A spi nor flash with no flags as it does support SFDP parsing. Fixes: #9442 Signed-off-by: Christian Marangi Tested-by: Szabolcs Hubai [ramips/mt7621: xiaomi_mi-router-4a-gigabit] --- .../477-mtd-spi-nor-add-eon-en25qx128a.patch | 21 +++++++++++++++++++ .../477-mtd-spi-nor-add-eon-en25qx128a.patch | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 target/linux/generic/pending-5.10/477-mtd-spi-nor-add-eon-en25qx128a.patch create mode 100644 target/linux/generic/pending-5.15/477-mtd-spi-nor-add-eon-en25qx128a.patch diff --git a/target/linux/generic/pending-5.10/477-mtd-spi-nor-add-eon-en25qx128a.patch b/target/linux/generic/pending-5.10/477-mtd-spi-nor-add-eon-en25qx128a.patch new file mode 100644 index 00000000000..adddf82ac8f --- /dev/null +++ b/target/linux/generic/pending-5.10/477-mtd-spi-nor-add-eon-en25qx128a.patch @@ -0,0 +1,21 @@ +From: Christian Marangi +Subject: kernel/mtd: add support for EON EN25QX128A + +Add support for EON EN25QX128A with no flags as it does +support SFDP parsing. + +Signed-off-by: Christian Marangi +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/eon.c ++++ b/drivers/mtd/spi-nor/eon.c +@@ -16,6 +16,7 @@ static const struct flash_info eon_parts + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256, SECT_4K) }, ++ { "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256, 0) }, + { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32, diff --git a/target/linux/generic/pending-5.15/477-mtd-spi-nor-add-eon-en25qx128a.patch b/target/linux/generic/pending-5.15/477-mtd-spi-nor-add-eon-en25qx128a.patch new file mode 100644 index 00000000000..adddf82ac8f --- /dev/null +++ b/target/linux/generic/pending-5.15/477-mtd-spi-nor-add-eon-en25qx128a.patch @@ -0,0 +1,21 @@ +From: Christian Marangi +Subject: kernel/mtd: add support for EON EN25QX128A + +Add support for EON EN25QX128A with no flags as it does +support SFDP parsing. + +Signed-off-by: Christian Marangi +--- + drivers/mtd/spi-nor/spi-nor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/mtd/spi-nor/eon.c ++++ b/drivers/mtd/spi-nor/eon.c +@@ -16,6 +16,7 @@ static const struct flash_info eon_parts + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256, SECT_4K) }, ++ { "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256, 0) }, + { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32, From c48e511fef37c42820c02119dab68c2b6cde3374 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 11 Dec 2022 18:25:42 +0800 Subject: [PATCH 067/110] base-files: split ubi attaching out of nand_upgrade_prepare_ubi split ubi reformat/attach into nand_attach_ubi in preparation for reusing this code in other functions. Signed-off-by: Chuanhong Guo --- package/base-files/files/lib/upgrade/nand.sh | 65 ++++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/package/base-files/files/lib/upgrade/nand.sh b/package/base-files/files/lib/upgrade/nand.sh index 1019b9927c0..258483fbf45 100644 --- a/package/base-files/files/lib/upgrade/nand.sh +++ b/package/base-files/files/lib/upgrade/nand.sh @@ -146,6 +146,42 @@ nand_remove_ubiblock() { fi } +nand_attach_ubi() { + local ubipart="$1" + local has_env="${2:-0}" + + local mtdnum="$( find_mtd_index "$ubipart" )" + if [ ! "$mtdnum" ]; then + >&2 echo "cannot find ubi mtd partition $ubipart" + return 1 + fi + + local ubidev="$( nand_find_ubi "$ubipart" )" + if [ ! "$ubidev" ]; then + >&2 ubiattach -m "$mtdnum" + ubidev="$( nand_find_ubi "$ubipart" )" + + if [ ! "$ubidev" ]; then + >&2 ubiformat /dev/mtd$mtdnum -y + >&2 ubiattach -m "$mtdnum" + ubidev="$( nand_find_ubi "$ubipart" )" + + if [ ! "$ubidev" ]; then + >&2 echo "cannot attach ubi mtd partition $ubipart" + return 1 + fi + + if [ "$has_env" -gt 0 ]; then + >&2 ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB + >&2 ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB + fi + fi + fi + + echo "$ubidev" + return 0 +} + nand_detach_ubi() { local ubipart="$1" @@ -180,33 +216,8 @@ nand_upgrade_prepare_ubi() { [ -n "$rootfs_length" -o -n "$kernel_length" ] || return 1 - local mtdnum="$( find_mtd_index "$CI_UBIPART" )" - if [ ! "$mtdnum" ]; then - echo "cannot find ubi mtd partition $CI_UBIPART" - return 1 - fi - - local ubidev="$( nand_find_ubi "$CI_UBIPART" )" - if [ ! "$ubidev" ]; then - ubiattach -m "$mtdnum" - ubidev="$( nand_find_ubi "$CI_UBIPART" )" - - if [ ! "$ubidev" ]; then - ubiformat /dev/mtd$mtdnum -y - ubiattach -m "$mtdnum" - ubidev="$( nand_find_ubi "$CI_UBIPART" )" - - if [ ! "$ubidev" ]; then - echo "cannot attach ubi mtd partition $CI_UBIPART" - return 1 - fi - - if [ "$has_env" -gt 0 ]; then - ubimkvol /dev/$ubidev -n 0 -N ubootenv -s 1MiB - ubimkvol /dev/$ubidev -n 1 -N ubootenv2 -s 1MiB - fi - fi - fi + local ubidev="$( nand_attach_ubi "$CI_UBIPART" "$has_env" )" + [ -n "$ubidev" ] || return 1 local kern_ubivol="$( nand_find_volume $ubidev "$CI_KERNPART" )" local root_ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )" From 189637c96488e253c782ce0b9fd7f4a30c645df1 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 11 Dec 2022 19:56:13 +0800 Subject: [PATCH 068/110] base-files: sysupgrade-tar: allow separated kernel/rootfs ubi There are some devices putting kernel and rootfs on separated ubi volumes. To make OpenWrt compatible with their bootloader, we need to put kernel and rootfs into separated ubi volumes. Add support for CI_KERN_UBIPART and CI_ROOT_UBIPART for this situation. Signed-off-by: Chuanhong Guo --- package/base-files/files/lib/upgrade/nand.sh | 41 +++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/package/base-files/files/lib/upgrade/nand.sh b/package/base-files/files/lib/upgrade/nand.sh index 258483fbf45..a8e3cab0b8b 100644 --- a/package/base-files/files/lib/upgrade/nand.sh +++ b/package/base-files/files/lib/upgrade/nand.sh @@ -7,6 +7,8 @@ CI_KERNPART="${CI_KERNPART:-kernel}" # 'ubi' partition on NAND contains UBI +# There are also CI_KERN_UBIPART and CI_ROOT_UBIPART if kernel +# and rootfs are on separated UBIs. CI_UBIPART="${CI_UBIPART:-ubi}" # 'rootfs' UBI volume on NAND contains the rootfs @@ -104,7 +106,7 @@ identify_if_gzip() { } nand_restore_config() { - local ubidev=$( nand_find_ubi "$CI_UBIPART" ) + local ubidev=$( nand_find_ubi "${CI_ROOT_UBIPART:-$CI_UBIPART}" ) local ubivol="$( nand_find_volume $ubidev rootfs_data )" if [ ! "$ubivol" ]; then ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )" @@ -213,15 +215,25 @@ nand_upgrade_prepare_ubi() { local kernel_length="$3" local has_env="${4:-0}" + local kern_ubidev + local root_ubidev [ -n "$rootfs_length" -o -n "$kernel_length" ] || return 1 - local ubidev="$( nand_attach_ubi "$CI_UBIPART" "$has_env" )" - [ -n "$ubidev" ] || return 1 + if [ -n "$CI_KERN_UBIPART" -a -n "$CI_ROOT_UBIPART" ]; then + kern_ubidev="$( nand_attach_ubi "$CI_KERN_UBIPART" "$has_env" )" + [ -n "$kern_ubidev" ] || return 1 + root_ubidev="$( nand_attach_ubi "$CI_ROOT_UBIPART" )" + [ -n "$root_ubidev" ] || return 1 + else + kern_ubidev="$( nand_attach_ubi "$CI_UBIPART" "$has_env" )" + [ -n "$kern_ubidev" ] || return 1 + root_ubidev="$kern_ubidev" + fi - local kern_ubivol="$( nand_find_volume $ubidev "$CI_KERNPART" )" - local root_ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )" - local data_ubivol="$( nand_find_volume $ubidev rootfs_data )" + local kern_ubivol="$( nand_find_volume $kern_ubidev "$CI_KERNPART" )" + local root_ubivol="$( nand_find_volume $root_ubidev "$CI_ROOTPART" )" + local data_ubivol="$( nand_find_volume $root_ubidev rootfs_data )" [ "$root_ubivol" = "$kern_ubivol" ] && root_ubivol= # remove ubiblocks @@ -230,13 +242,13 @@ nand_upgrade_prepare_ubi() { [ "$data_ubivol" ] && { nand_remove_ubiblock $data_ubivol || return 1; } # kill volumes - [ "$kern_ubivol" ] && ubirmvol /dev/$ubidev -N "$CI_KERNPART" || : - [ "$root_ubivol" ] && ubirmvol /dev/$ubidev -N "$CI_ROOTPART" || : - [ "$data_ubivol" ] && ubirmvol /dev/$ubidev -N rootfs_data || : + [ "$kern_ubivol" ] && ubirmvol /dev/$kern_ubidev -N "$CI_KERNPART" || : + [ "$root_ubivol" ] && ubirmvol /dev/$root_ubidev -N "$CI_ROOTPART" || : + [ "$data_ubivol" ] && ubirmvol /dev/$root_ubidev -N rootfs_data || : # create kernel vol if [ -n "$kernel_length" ]; then - if ! ubimkvol /dev/$ubidev -N "$CI_KERNPART" -s $kernel_length; then + if ! ubimkvol /dev/$kern_ubidev -N "$CI_KERNPART" -s $kernel_length; then echo "cannot create kernel volume" return 1; fi @@ -250,7 +262,7 @@ nand_upgrade_prepare_ubi() { else rootfs_size_param="-s $rootfs_length" fi - if ! ubimkvol /dev/$ubidev -N "$CI_ROOTPART" $rootfs_size_param; then + if ! ubimkvol /dev/$root_ubidev -N "$CI_ROOTPART" $rootfs_size_param; then echo "cannot create rootfs volume" return 1; fi @@ -262,8 +274,8 @@ nand_upgrade_prepare_ubi() { if [ -n "$rootfs_data_max" ]; then rootfs_data_size_param="-s $rootfs_data_max" fi - if ! ubimkvol /dev/$ubidev -N rootfs_data $rootfs_data_size_param; then - if ! ubimkvol /dev/$ubidev -N rootfs_data -m; then + if ! ubimkvol /dev/$root_ubidev -N rootfs_data $rootfs_data_size_param; then + if ! ubimkvol /dev/$root_ubidev -N rootfs_data -m; then echo "cannot initialize rootfs_data volume" return 1 fi @@ -347,8 +359,8 @@ nand_upgrade_tar() { local has_env=0 nand_upgrade_prepare_ubi "$rootfs_length" "$rootfs_type" "$ubi_kernel_length" "$has_env" || return 1 - local ubidev="$( nand_find_ubi "$CI_UBIPART" )" if [ "$rootfs_length" ]; then + local ubidev="$( nand_find_ubi "${CI_ROOT_UBIPART:-$CI_UBIPART}" )" local root_ubivol="$( nand_find_volume $ubidev "$CI_ROOTPART" )" tar xO${gz}f "$tar_file" "$board_dir/root" | \ ubiupdatevol /dev/$root_ubivol -s "$rootfs_length" - @@ -358,6 +370,7 @@ nand_upgrade_tar() { tar xO${gz}f "$tar_file" "$board_dir/kernel" | \ mtd write - "$CI_KERNPART" else + local ubidev="$( nand_find_ubi "${CI_KERN_UBIPART:-$CI_UBIPART}" )" local kern_ubivol="$( nand_find_volume $ubidev "$CI_KERNPART" )" tar xO${gz}f "$tar_file" "$board_dir/kernel" | \ ubiupdatevol /dev/$kern_ubivol -s "$kernel_length" - From 18bea173a646518a64ca05ea26f87eab0540feb9 Mon Sep 17 00:00:00 2001 From: Chen Minqiang Date: Tue, 13 Sep 2022 13:46:20 +0800 Subject: [PATCH 069/110] mediatek: add alternative stock layout for Xiaomi Redmi Router AX6000 In this implementation, the flash partition layout is adjusted to avoid modifying the uboot environment of mtdparts. This ensures that the 30M ubi_kernel partition remains aligned with the stock ubi partition, and the kernel volume is placed in it. This allows the stock uboot to boot from it without changing the mtdparts, which is useful for reverting back to the stock firmware using Xiaomi Firmware Tools. In actual testing, modifying mtdparts has been found to break Xiaomi Firmware Tools. 1. use ARTIFACTS to generate initramfs-factory.ubi for easy installation. 2. The NAND flash layout is changed to allow for reverting back to the stock firmware. 3. Before performing sysupgrade, do some cleanup in platform_pre_upgrade to ensure a clean installation of OpenWRT. 4. Setup the uboot env to ensure that the system always boot, which can be helpful for users who may forget to do this before sysupgrade in the initramfs. New flash instructions: 1. Gain ssh access. Please refer to: https://openwrt.org/toh/xiaomi/redmi_ax6000#installation) 2. Check which system current u-boot is loading from: COMMAND: `cat /proc/cmdline` sample OUTPUT: `console=ttyS0,115200n1 loglevel=8 firmware=1 uart_en=1` if firmware=1, current system is ubi1 if firmware=0, current system is ubi0 3. Setup nvram and write the firmware: If the current system is ubi1, please set it up so that the next time it will boot from ubi, and write the firmware to ubi: ``` nvram set boot_wait=on nvram set uart_en=1 nvram set flag_boot_rootfs=0 nvram set flag_last_success=0 nvram set flag_boot_success=1 nvram set flag_try_sys1_failed=0 nvram set flag_try_sys2_failed=0 nvram commit ubiformat /dev/mtd8 -y -f /tmp/initramfs-factory.ubi ``` If the current system is ubi, please set it up so that the next time it will boot from ubi1, and write the firmware to ubi1: ``` nvram set boot_wait=on nvram set uart_en=1 nvram set flag_boot_rootfs=1 nvram set flag_last_success=1 nvram set flag_boot_success=1 nvram set flag_try_sys1_failed=0 nvram set flag_try_sys2_failed=0 nvram commit ubiformat /dev/mtd9 -y -f /tmp/initramfs-factory.ubi ``` 4. After rebooting, the system should now boot into the openwrt initramfs. Flash the squashfs-sysupgrade.bin via using ssh or luci. ``` sysupgrade -n /tmp/squashfs-sysupgrade.bin ``` Done. For existing users of the Redmi AX6000 running OpenWrt, here are the steps to switch to this new layout: 1. Flash initramfs-factory.ubi ``` mtd -r -e ubi write /tmp/initramfs-factory.ubi ubi ``` 2. After rebooting, the system will boot into the new openwrt-initramfs. Log in and perform a sysupgrade to complete the process. ``` sysupgrade -n /tmp/squashfs-sysupgrade.bin ``` Signed-off-by: Chen Minqiang Signed-off-by: Chuanhong Guo --- .../uboot-envtools/files/mediatek_filogic | 3 +- ...7986a-xiaomi-redmi-router-ax6000-stock.dts | 21 ++ .../mt7986a-xiaomi-redmi-router-ax6000.dts | 293 +----------------- .../mt7986a-xiaomi-redmi-router-ax6000.dtsi | 277 +++++++++++++++++ .../filogic/base-files/etc/board.d/01_leds | 3 +- .../filogic/base-files/etc/board.d/02_network | 6 +- .../base-files/lib/upgrade/platform.sh | 53 ++++ target/linux/mediatek/image/filogic.mk | 18 ++ 8 files changed, 389 insertions(+), 285 deletions(-) create mode 100644 target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000-stock.dts create mode 100644 target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi diff --git a/package/boot/uboot-envtools/files/mediatek_filogic b/package/boot/uboot-envtools/files/mediatek_filogic index fe986b2e4ea..f73ea80109a 100644 --- a/package/boot/uboot-envtools/files/mediatek_filogic +++ b/package/boot/uboot-envtools/files/mediatek_filogic @@ -34,7 +34,8 @@ bananapi,bpi-r3) ;; esac ;; -xiaomi,redmi-router-ax6000) +xiaomi,redmi-router-ax6000|\ +xiaomi,redmi-router-ax6000-stock) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x10000" "0x20000" ubootenv_add_uci_sys_config "/dev/mtd2" "0x0" "0x10000" "0x20000" ;; diff --git a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000-stock.dts b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000-stock.dts new file mode 100644 index 00000000000..64342677b38 --- /dev/null +++ b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000-stock.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/dts-v1/; +#include "mt7986a-xiaomi-redmi-router-ax6000.dtsi" + +/ { + model = "Xiaomi Redmi Router AX6000 (stock layout)"; + compatible = "xiaomi,redmi-router-ax6000-stock", "mediatek,mt7986a"; +}; + +&partitions { + partition@600000 { + label = "ubi_kernel"; + reg = <0x600000 0x1e00000>; + }; + + partition@2400000 { + label = "ubi"; + reg = <0x2400000 0x5000000>; + }; +}; diff --git a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts index 31112664c2e..a8df7866320 100644 --- a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts +++ b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dts @@ -1,293 +1,24 @@ // SPDX-License-Identifier: (GPL-2.0 OR MIT) /dts-v1/; -#include -#include -#include - -#include "mt7986a.dtsi" +#include "mt7986a-xiaomi-redmi-router-ax6000.dtsi" / { model = "Xiaomi Redmi Router AX6000"; compatible = "xiaomi,redmi-router-ax6000", "mediatek,mt7986a"; - - aliases { - serial0 = &uart0; - led-boot = &led_status_rgb; - led-failsafe = &led_status_rgb; - led-running = &led_status_rgb; - led-upgrade = &led_status_rgb; - }; - - chosen { - stdout-path = "serial0:115200n8"; - }; - - memory { - reg = <0 0x40000000 0 0x20000000>; - }; - - keys { - compatible = "gpio-keys"; - - reset { - label = "reset"; - gpios = <&pio 9 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - - mesh { - label = "mesh"; - gpios = <&pio 10 GPIO_ACTIVE_LOW>; - linux,code = ; - linux,input-type = ; - }; - }; }; -ð { - status = "okay"; - - gmac0: mac@0 { - compatible = "mediatek,eth-mac"; - reg = <0>; - phy-mode = "2500base-x"; - - nvmem-cells = <&macaddr_factory_4>; - nvmem-cell-names = "mac-address"; - mac-address-increment = <(-1)>; - - fixed-link { - speed = <2500>; - full-duplex; - pause; - }; +&partitions { + /* ubi partition is the result of squashing + * consecutive stock partitions: + * - ubi + * - ubi1 + * - overlay + */ + partition@600000 { + label = "ubi"; + reg = <0x600000 0x6e00000>; }; - mdio: mdio-bus { - #address-cells = <1>; - #size-cells = <0>; - }; -}; - -&mdio { - switch: switch@0 { - compatible = "mediatek,mt7531"; - reg = <31>; - reset-gpios = <&pio 5 GPIO_ACTIVE_HIGH>; - interrupt-controller; - #interrupt-cells = <1>; - interrupt-parent = <&pio>; - interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; - }; -}; - -&pio { - spi_flash_pins: spi-flash-pins-33-to-38 { - mux { - function = "spi"; - groups = "spi0", "spi0_wp_hold"; - }; - conf-pu { - pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; - drive-strength = <8>; - mediatek,pull-up-adv = <0>; /* bias-disable */ - }; - conf-pd { - pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; - drive-strength = <8>; - mediatek,pull-down-adv = <0>; /* bias-disable */ - }; - }; - - spi_led_pins: spic-pins-29-to-32 { - mux { - function = "spi"; - groups = "spi1_2"; - }; - }; - - wf_2g_5g_pins: wf_2g_5g-pins { - mux { - function = "wifi"; - groups = "wf_2g", "wf_5g"; - }; - conf { - pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", - "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", - "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", - "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", - "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", - "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", - "WF1_TOP_CLK", "WF1_TOP_DATA"; - drive-strength = <4>; - }; - }; -}; - -&spi0 { - pinctrl-names = "default"; - pinctrl-0 = <&spi_flash_pins>; - cs-gpios = <0>, <0>; - status = "okay"; - - flash@0 { - compatible = "spi-nand"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - - mediatek,nmbm; - mediatek,bmt-max-ratio = <1>; - mediatek,bmt-max-reserved-blocks = <64>; - - spi-max-frequency = <20000000>; - spi-tx-buswidth = <4>; - spi-rx-buswidth = <4>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "BL2"; - reg = <0x0 0x100000>; - read-only; - }; - - partition@100000 { - label = "Nvram"; - reg = <0x100000 0x40000>; - }; - - partition@140000 { - label = "Bdata"; - reg = <0x140000 0x40000>; - }; - - factory: partition@180000 { - label = "Factory"; - reg = <0x180000 0x200000>; - read-only; - - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_factory_4: macaddr@4 { - reg = <0x4 0x6>; - }; - }; - - partition@380000 { - label = "FIP"; - reg = <0x380000 0x200000>; - read-only; - }; - - partition@580000 { - label = "crash"; - reg = <0x580000 0x40000>; - read-only; - }; - - partition@5c0000 { - label = "crash_log"; - reg = <0x5c0000 0x40000>; - read-only; - }; - - /* ubi partition is the result of squashing - * consecutive stock partitions: - * - ubi - * - ubi1 - * - overlay - */ - partition@600000 { - label = "ubi"; - reg = <0x600000 0x6e00000>; - }; - - /* last 12 MiB is reserved for NMBM bad block table */ - }; - }; -}; - -&spi1 { - pinctrl-names = "default"; - pinctrl-0 = <&spi_led_pins>; - status = "okay"; - - ws2812b@0 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "worldsemi,ws2812b"; - reg = <0>; - spi-max-frequency = <3000000>; - - led_status_rgb: led@0 { - reg = <0>; - label = "rgb:status"; - color-index = ; - }; - - led_network_rgb: led@1 { - reg = <1>; - label = "rgb:network"; - color-index = ; - }; - }; -}; - -&switch { - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - reg = <1>; - label = "lan4"; - }; - - port@2 { - reg = <2>; - label = "lan3"; - }; - - port@3 { - reg = <3>; - label = "lan2"; - }; - - port@4 { - reg = <4>; - label = "wan"; - }; - - port@6 { - reg = <6>; - label = "cpu"; - ethernet = <&gmac0>; - phy-mode = "2500base-x"; - - fixed-link { - speed = <2500>; - full-duplex; - pause; - }; - }; - }; -}; - -&wmac { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&wf_2g_5g_pins>; - - mediatek,mtd-eeprom = <&factory 0x0>; -}; - -&uart0 { - status = "okay"; + /* last 12 MiB is reserved for NMBM bad block table */ }; diff --git a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi new file mode 100644 index 00000000000..4b2754b0012 --- /dev/null +++ b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/dts-v1/; +#include +#include +#include + +#include "mt7986a.dtsi" + +/ { + aliases { + serial0 = &uart0; + led-boot = &led_status_rgb; + led-failsafe = &led_status_rgb; + led-running = &led_status_rgb; + led-upgrade = &led_status_rgb; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + memory { + reg = <0 0x40000000 0 0x20000000>; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&pio 9 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + mesh { + label = "mesh"; + gpios = <&pio 10 GPIO_ACTIVE_LOW>; + linux,code = ; + linux,input-type = ; + }; + }; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + nvmem-cells = <&macaddr_factory_4>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <(-1)>; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; +}; + +&mdio { + switch: switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + reset-gpios = <&pio 5 GPIO_ACTIVE_HIGH>; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&pio>; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; + }; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = <8>; + mediatek,pull-up-adv = <0>; /* bias-disable */ + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = <8>; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + }; + + spi_led_pins: spic-pins-29-to-32 { + mux { + function = "spi"; + groups = "spi1_2"; + }; + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = <4>; + }; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + cs-gpios = <0>, <0>; + status = "okay"; + + flash@0 { + compatible = "spi-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + + mediatek,nmbm; + mediatek,bmt-max-ratio = <1>; + mediatek,bmt-max-reserved-blocks = <64>; + + spi-max-frequency = <20000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + + partitions: partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "BL2"; + reg = <0x0 0x100000>; + read-only; + }; + + partition@100000 { + label = "Nvram"; + reg = <0x100000 0x40000>; + }; + + partition@140000 { + label = "Bdata"; + reg = <0x140000 0x40000>; + }; + + factory: partition@180000 { + label = "Factory"; + reg = <0x180000 0x200000>; + read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_factory_4: macaddr@4 { + reg = <0x4 0x6>; + }; + }; + + partition@380000 { + label = "FIP"; + reg = <0x380000 0x200000>; + read-only; + }; + + partition@580000 { + label = "crash"; + reg = <0x580000 0x40000>; + read-only; + }; + + partition@5c0000 { + label = "crash_log"; + reg = <0x5c0000 0x40000>; + read-only; + }; + }; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_led_pins>; + status = "okay"; + + ws2812b@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "worldsemi,ws2812b"; + reg = <0>; + spi-max-frequency = <3000000>; + + led_status_rgb: led@0 { + reg = <0>; + label = "rgb:status"; + color-index = ; + }; + + led_network_rgb: led@1 { + reg = <1>; + label = "rgb:network"; + color-index = ; + }; + }; +}; + +&switch { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + label = "lan4"; + }; + + port@2 { + reg = <2>; + label = "lan3"; + }; + + port@3 { + reg = <3>; + label = "lan2"; + }; + + port@4 { + reg = <4>; + label = "wan"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; +}; + +&wmac { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&wf_2g_5g_pins>; + + mediatek,mtd-eeprom = <&factory 0x0>; +}; + +&uart0 { + status = "okay"; +}; diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/01_leds b/target/linux/mediatek/filogic/base-files/etc/board.d/01_leds index f851c7ed54b..8cfed15544d 100644 --- a/target/linux/mediatek/filogic/base-files/etc/board.d/01_leds +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/01_leds @@ -6,7 +6,8 @@ board=$(board_name) board_config_update case $board in -xiaomi,redmi-router-ax6000) +xiaomi,redmi-router-ax6000|\ +xiaomi,redmi-router-ax6000-stock) ucidef_set_led_netdev "wan" "wan" "rgb:network" "wan" ;; esac diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network index 0f0a0fc86b1..1c98dcc7cb3 100644 --- a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network @@ -16,7 +16,8 @@ mediatek_setup_interfaces() ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4 sfp2" "eth1 wan" ucidef_set_interface_macaddr "wan" "$(macaddr_add $(cat /sys/class/net/eth0/address) 1)" ;; - xiaomi,redmi-router-ax6000) + xiaomi,redmi-router-ax6000|\ + xiaomi,redmi-router-ax6000-stock) ucidef_set_interfaces_lan_wan "lan2 lan3 lan4" wan ;; *) @@ -33,7 +34,8 @@ mediatek_setup_macs() local label_mac="" case $board in - xiaomi,redmi-router-ax6000) + xiaomi,redmi-router-ax6000|\ + xiaomi,redmi-router-ax6000-stock) wan_mac=$(mtd_get_mac_ascii Bdata ethaddr_wan) label_mac=$wan_mac ;; diff --git a/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh index a9327ae282a..29c7a1475ed 100755 --- a/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh +++ b/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh @@ -1,5 +1,43 @@ REQUIRE_IMAGE_METADATA=1 +redmi_ax6000_initial_setup() +{ + # initialize UBI and setup uboot-env if it's running on initramfs + [ "$(rootfs_type)" = "tmpfs" ] || return 0 + + local mtdnum="$( find_mtd_index ubi )" + if [ ! "$mtdnum" ]; then + echo "unable to find mtd partition ubi" + return 1 + fi + + local kern_mtdnum="$( find_mtd_index ubi_kernel )" + if [ ! "$kern_mtdnum" ]; then + echo "unable to find mtd partition ubi_kernel" + return 1 + fi + + ubidetach -m "$mtdnum" + ubiformat /dev/mtd$mtdnum -y + + ubidetach -m "$kern_mtdnum" + ubiformat /dev/mtd$kern_mtdnum -y + + if ! fw_printenv -n flag_try_sys2_failed &>/dev/null; then + echo "failed to access u-boot-env. skip env setup." + return 0 + fi + + fw_setenv boot_wait on + fw_setenv uart_en 1 + fw_setenv flag_boot_rootfs 0 + fw_setenv flag_last_success 1 + fw_setenv flag_boot_success 1 + fw_setenv flag_try_sys1_failed 8 + fw_setenv flag_try_sys2_failed 8 + fw_setenv mtdparts "nmbm0:1024k(bl2),256k(Nvram),256k(Bdata),2048k(factory),2048k(fip),256k(crash),256k(crash_log),30720k(ubi),30720k(ubi1),51200k(overlay)" +} + platform_do_upgrade() { local board=$(board_name) @@ -21,6 +59,11 @@ platform_do_upgrade() { ;; esac ;; + xiaomi,redmi-router-ax6000-stock) + CI_KERN_UBIPART=ubi_kernel + CI_ROOT_UBIPART=ubi + nand_do_upgrade "$1" + ;; *) nand_do_upgrade "$1" ;; @@ -63,3 +106,13 @@ platform_copy_config() { ;; esac } + +platform_pre_upgrade() { + local board=$(board_name) + + case "$board" in + xiaomi,redmi-router-ax6000-stock) + redmi_ax6000_initial_setup + ;; + esac +} diff --git a/target/linux/mediatek/image/filogic.mk b/target/linux/mediatek/image/filogic.mk index 5dbeedb64c7..4e3399d120b 100644 --- a/target/linux/mediatek/image/filogic.mk +++ b/target/linux/mediatek/image/filogic.mk @@ -139,3 +139,21 @@ define Device/xiaomi_redmi-router-ax6000 IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata endef TARGET_DEVICES += xiaomi_redmi-router-ax6000 + +define Device/xiaomi_redmi-router-ax6000-stock + DEVICE_VENDOR := Xiaomi + DEVICE_MODEL := Redmi Router AX6000 (stock layout) + DEVICE_DTS := mt7986a-xiaomi-redmi-router-ax6000-stock + DEVICE_DTS_DIR := ../dts + DEVICE_PACKAGES := kmod-leds-ws2812b + KERNEL_LOADADDR := 0x48000000 + UBINIZE_OPTS := -E 5 + BLOCKSIZE := 128k + PAGESIZE := 2048 +ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) + ARTIFACTS := initramfs-factory.ubi + ARTIFACT/initramfs-factory.ubi := append-image-stage initramfs-kernel.bin | ubinize-kernel +endif + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += xiaomi_redmi-router-ax6000-stock From 7fb32285d543fa93a7998625edc3faabb06ae49f Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sat, 17 Dec 2022 15:07:54 +0800 Subject: [PATCH 070/110] mediatek: redmi-ax6000: drop cs-gpios 'cs-gpios = <0>, <0>' is a hack in ath79 to override the incorrectly specified maximum number of chipselects available in spi-ath79.c. It's not needed here and must have been copied here by mistake. Signed-off-by: Chuanhong Guo --- .../linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi | 1 - 1 file changed, 1 deletion(-) diff --git a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi index 4b2754b0012..1a80649721c 100644 --- a/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi +++ b/target/linux/mediatek/dts/mt7986a-xiaomi-redmi-router-ax6000.dtsi @@ -125,7 +125,6 @@ &spi0 { pinctrl-names = "default"; pinctrl-0 = <&spi_flash_pins>; - cs-gpios = <0>, <0>; status = "okay"; flash@0 { From 4e88a009509ef1278ceb91450f5f9f61dcc089c6 Mon Sep 17 00:00:00 2001 From: Linhui Liu Date: Thu, 15 Dec 2022 13:12:08 +0800 Subject: [PATCH 071/110] tools/ccache: update to 4.7.4 Release Notes: https://ccache.dev/releasenotes.html#_ccache_4_7_3 https://ccache.dev/releasenotes.html#_ccache_4_7_4 Signed-off-by: Linhui Liu --- tools/ccache/Makefile | 4 ++-- tools/ccache/patches/100-honour-copts.patch | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/ccache/Makefile b/tools/ccache/Makefile index 6d92c2f1413..25ab4a46eab 100644 --- a/tools/ccache/Makefile +++ b/tools/ccache/Makefile @@ -8,11 +8,11 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/target.mk PKG_NAME:=ccache -PKG_VERSION:=4.7.2 +PKG_VERSION:=4.7.4 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=https://github.com/ccache/ccache/releases/download/v$(PKG_VERSION) -PKG_HASH:=17ca75a577d49c1e4f2ac86d53126859de52b789cfe85dd532758518db114eaf +PKG_HASH:=df0c64d15d3efaf0b4f6837dd6b1467e40eeaaa807db25ce79c3a08a46a84e36 include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/cmake.mk diff --git a/tools/ccache/patches/100-honour-copts.patch b/tools/ccache/patches/100-honour-copts.patch index b4c1be4df3f..5358d425c35 100644 --- a/tools/ccache/patches/100-honour-copts.patch +++ b/tools/ccache/patches/100-honour-copts.patch @@ -1,6 +1,6 @@ --- a/src/ccache.cpp +++ b/src/ccache.cpp -@@ -1762,6 +1762,7 @@ get_manifest_key(Context& ctx, Hash& hash) +@@ -1779,6 +1779,7 @@ get_manifest_key(Context& ctx, Hash& has "CPLUS_INCLUDE_PATH", "OBJC_INCLUDE_PATH", "OBJCPLUS_INCLUDE_PATH", // clang From 4609228432583c9e9fb306c5a69c6fc21fd79cb7 Mon Sep 17 00:00:00 2001 From: Linhui Liu Date: Fri, 16 Dec 2022 10:32:44 +0800 Subject: [PATCH 072/110] tools/mpc: update to 1.3.1 Version 1.3.1 new features: Bug fix: It is again possible to include mpc.h without including stdio.h. Version 1.3.0 new features: New function: mpc_agm New rounding modes "away from zero", indicated by the letter "A" and corresponding to MPFR_RNDA on the designated real or imaginary part. New experimental ball arithmetic. New experimental function: mpc_eta_fund Bug fixes: mpc_asin for asin(z) with small |Re(z)| and tiny |Im(z)| mpc_pow_fr: sign of zero part of result when the base has up to sign the same real and imaginary part, and the exponent is an even positive integer mpc_fma: the returned int value was incorrect in some cases (indicating whether the rounded real/imaginary parts were smaller/equal/greater than the exact values), but the computed complex value was correct. Remove the unmaintained Makefile.vc; build files for Visual Studio are maintained independently by Brian Gladman. Signed-off-by: Linhui Liu --- tools/mpc/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/mpc/Makefile b/tools/mpc/Makefile index b170043c1a6..5c196a27ef7 100644 --- a/tools/mpc/Makefile +++ b/tools/mpc/Makefile @@ -7,11 +7,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mpc -PKG_VERSION:=1.2.1 +PKG_VERSION:=1.3.1 PKG_SOURCE_URL:=@GNU/mpc/ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz -PKG_HASH:=17503d2c395dfcf106b622dc142683c1199431d095367c6aacba6eec30340459 +PKG_HASH:=ab642492f5cf882b74aa0cb730cd410a81edcdbec895183ce930e706c1c759b8 HOST_BUILD_PARALLEL:=1 From 06ad3adeecc27859313e60c173c435d45ac2b345 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 14 Dec 2022 16:29:16 +0100 Subject: [PATCH 073/110] kernel: Make KERNEL_MAKEOPTS recursively expanded KERNEL_MAKEOPTS will get expanded when it is used and not when it is defined in the kernel.mk file now. This fixes problems finding dependent kernel modules when it is used by a kernel module package. Without this change the build of packages which depend on other out of tree modules failed when they used KERNEL_MAKE because some symbols could not be found. This happened because KERNEL_MAKE_FLAGS which contains a "if $(__package_mk)" was evaluated where KERNEL_MAKEOPTS was defined and not when the KERNEL_MAKE was used. For packages which included kernel.mk before package.mk we saw this problem. One workaround was to use the correct include order and the other one was to not use KERNEL_MAKE_FLAGS, but copy its content. Signed-off-by: Hauke Mehrtens --- include/kernel.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/kernel.mk b/include/kernel.mk index 2efa3bf8f8e..c657bf5d82e 100644 --- a/include/kernel.mk +++ b/include/kernel.mk @@ -130,7 +130,7 @@ ifneq ($(HOST_OS),Linux) export SKIP_STACK_VALIDATION:=1 endif -KERNEL_MAKEOPTS := -C $(LINUX_DIR) $(KERNEL_MAKE_FLAGS) +KERNEL_MAKEOPTS = -C $(LINUX_DIR) $(KERNEL_MAKE_FLAGS) ifdef CONFIG_USE_SPARSE KERNEL_MAKEOPTS += C=1 CHECK=$(STAGING_DIR_HOST)/bin/sparse From 243a7256c154a912d9716d98dc33e9b0dc856cb5 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 14 Dec 2022 22:34:19 +0100 Subject: [PATCH 074/110] kernel: Make use of KERNEL_MAKE Make use of KERNEL_MAKE in kernel packages were easily possible. This moves some more code to common places and reduces the number of lines. It is defined like this: KERNEL_MAKE = $(MAKE) $(KERNEL_MAKEOPTS) KERNEL_MAKEOPTS = -C $(LINUX_DIR) $(KERNEL_MAKE_FLAGS) Signed-off-by: Hauke Mehrtens --- package/kernel/ath10k-ct/Makefile | 3 +-- package/kernel/broadcom-wl/Makefile | 3 +-- package/kernel/gpio-button-hotplug/Makefile | 8 +------- package/kernel/gpio-nct5104d/Makefile | 12 ++++-------- package/kernel/hwmon-gsc/Makefile | 3 +-- package/kernel/lantiq/ltq-vectoring/Makefile | 3 +-- package/kernel/mt76/Makefile | 3 +-- package/kernel/mwlwifi/Makefile | 3 +-- package/kernel/rtc-rv5c386a/Makefile | 3 +-- package/kernel/rtl8812au-ct/Makefile | 3 +-- 10 files changed, 13 insertions(+), 31 deletions(-) diff --git a/package/kernel/ath10k-ct/Makefile b/package/kernel/ath10k-ct/Makefile index 0c1334cc46e..2a8c0a421c6 100644 --- a/package/kernel/ath10k-ct/Makefile +++ b/package/kernel/ath10k-ct/Makefile @@ -111,8 +111,7 @@ ifneq ($(findstring c,$(OPENWRT_VERBOSE)),) endif define Build/Compile - +$(MAKE) $(CT_MAKEDEFS) $(PKG_JOBS) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ + +$(KERNEL_MAKE) $(CT_MAKEDEFS) $(PKG_JOBS) \ M="$(PKG_BUILD_DIR)/ath10k$(CT_KVER)" \ NOSTDINC_FLAGS="$(NOSTDINC_FLAGS)" \ modules diff --git a/package/kernel/broadcom-wl/Makefile b/package/kernel/broadcom-wl/Makefile index dec5bcb98d2..137e547bee7 100644 --- a/package/kernel/broadcom-wl/Makefile +++ b/package/kernel/broadcom-wl/Makefile @@ -99,8 +99,7 @@ define Package/nas/description proprietary Broadcom wl driver. endef -MAKE_KMOD := $(MAKE) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ +MAKE_KMOD := $(KERNEL_MAKE) \ PATH="$(TARGET_PATH)" \ M="$(PKG_BUILD_DIR)/kmod" \ diff --git a/package/kernel/gpio-button-hotplug/Makefile b/package/kernel/gpio-button-hotplug/Makefile index 09f9c00e990..7ca6195a03e 100644 --- a/package/kernel/gpio-button-hotplug/Makefile +++ b/package/kernel/gpio-button-hotplug/Makefile @@ -32,14 +32,8 @@ define KernelPackage/gpio-button-hotplug/description an overkill for OpenWrt simple needs. endef -MAKE_OPTS:= \ - $(KERNEL_MAKE_FLAGS) \ - M="$(PKG_BUILD_DIR)" - define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(MAKE_OPTS) \ - modules + $(KERNEL_MAKE) M="$(PKG_BUILD_DIR)" modules endef $(eval $(call KernelPackage,gpio-button-hotplug)) diff --git a/package/kernel/gpio-nct5104d/Makefile b/package/kernel/gpio-nct5104d/Makefile index e85639c94af..72b1d58e62e 100644 --- a/package/kernel/gpio-nct5104d/Makefile +++ b/package/kernel/gpio-nct5104d/Makefile @@ -36,15 +36,11 @@ EXTRA_CFLAGS:= \ $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \ $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \ -MAKE_OPTS:= \ - $(KERNEL_MAKE_FLAGS) \ - M="$(PKG_BUILD_DIR)" \ - EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ - $(EXTRA_KCONFIG) - define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(MAKE_OPTS) \ + $(KERNEL_MAKE) \ + M="$(PKG_BUILD_DIR)" \ + EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ + $(EXTRA_KCONFIG) \ modules endef diff --git a/package/kernel/hwmon-gsc/Makefile b/package/kernel/hwmon-gsc/Makefile index 34c33dc581f..cd7d4efb107 100644 --- a/package/kernel/hwmon-gsc/Makefile +++ b/package/kernel/hwmon-gsc/Makefile @@ -19,8 +19,7 @@ define KernelPackage/hwmon-gsc/description endef define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ + $(KERNEL_MAKE) \ M="$(PKG_BUILD_DIR)" \ EXTRA_CFLAGS="$(BUILDFLAGS)" \ modules diff --git a/package/kernel/lantiq/ltq-vectoring/Makefile b/package/kernel/lantiq/ltq-vectoring/Makefile index b7b1b83a078..335cfaa1d5f 100644 --- a/package/kernel/lantiq/ltq-vectoring/Makefile +++ b/package/kernel/lantiq/ltq-vectoring/Makefile @@ -51,8 +51,7 @@ define Build/Configure endef define Build/Compile - +$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ + +$(KERNEL_MAKE) $(PKG_JOBS) \ M="$(PKG_BUILD_DIR)/$(MAKE_PATH)" \ modules endef diff --git a/package/kernel/mt76/Makefile b/package/kernel/mt76/Makefile index a086fed1f93..c71c7c52ded 100644 --- a/package/kernel/mt76/Makefile +++ b/package/kernel/mt76/Makefile @@ -379,8 +379,7 @@ ifdef CONFIG_PACKAGE_kmod-mt7921e endif define Build/Compile - +$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ + +$(KERNEL_MAKE) $(PKG_JOBS) \ $(PKG_MAKE_FLAGS) \ M="$(PKG_BUILD_DIR)" \ NOSTDINC_FLAGS="$(NOSTDINC_FLAGS)" \ diff --git a/package/kernel/mwlwifi/Makefile b/package/kernel/mwlwifi/Makefile index bb472b2a54a..c806f917aaa 100644 --- a/package/kernel/mwlwifi/Makefile +++ b/package/kernel/mwlwifi/Makefile @@ -45,8 +45,7 @@ NOSTDINC_FLAGS := \ -Wno-unused-result define Build/Compile - +$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ + +$(KERNEL_MAKE) $(PKG_JOBS) \ M="$(PKG_BUILD_DIR)" \ NOSTDINC_FLAGS="$(NOSTDINC_FLAGS)" \ modules diff --git a/package/kernel/rtc-rv5c386a/Makefile b/package/kernel/rtc-rv5c386a/Makefile index 0cca78548a2..7c18942ffec 100644 --- a/package/kernel/rtc-rv5c386a/Makefile +++ b/package/kernel/rtc-rv5c386a/Makefile @@ -22,8 +22,7 @@ define KernelPackage/rtc-rv5c386a endef define Build/Compile - $(MAKE) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ + $(KERNEL_MAKE) \ M="$(PKG_BUILD_DIR)" \ EXTRA_CFLAGS="$(BUILDFLAGS)" \ modules diff --git a/package/kernel/rtl8812au-ct/Makefile b/package/kernel/rtl8812au-ct/Makefile index eb2aaa6bf50..c8dd4170673 100644 --- a/package/kernel/rtl8812au-ct/Makefile +++ b/package/kernel/rtl8812au-ct/Makefile @@ -44,8 +44,7 @@ NOSTDINC_FLAGS := \ NOSTDINC_FLAGS+=-DCONFIG_IOCTL_CFG80211 -DRTW_USE_CFG80211_STA_EVENT -DBUILD_OPENWRT define Build/Compile - +$(MAKE) $(PKG_JOBS) -C "$(LINUX_DIR)" \ - $(KERNEL_MAKE_FLAGS) \ + +$(KERNEL_MAKE) $(PKG_JOBS) \ M="$(PKG_BUILD_DIR)" \ NOSTDINC_FLAGS="$(NOSTDINC_FLAGS)" \ modules From b01f2d9f38bb5a903f016d301ca349dad29aad6b Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 14:38:50 +0100 Subject: [PATCH 075/110] libtraceevent: update to 1.7.0 Update to latest release. Signed-off-by: Nick Hainke --- package/libs/libtraceevent/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/libs/libtraceevent/Makefile b/package/libs/libtraceevent/Makefile index c09197cc78a..63f954ba0ea 100644 --- a/package/libs/libtraceevent/Makefile +++ b/package/libs/libtraceevent/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=libtraceevent -PKG_VERSION:=1.6.3 +PKG_VERSION:=1.7.0 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/snapshot/ -PKG_HASH:=031e4428586788bb835d894f7f11c54e482764063d96ead303c7f208fb59e353 +PKG_HASH:=3e902184f743c955b183b45f25ea163a3d41c9f287fdcfc95cd9cca748c563c8 PKG_MAINTAINER:=Nick Hainke From b5d317f47e9eacd091ecae4ead3d9bcc1adc36f0 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 14:40:55 +0100 Subject: [PATCH 076/110] libtracefs: update to 1.6.3 Update to latest release. Signed-off-by: Nick Hainke --- package/libs/libtracefs/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/libs/libtracefs/Makefile b/package/libs/libtracefs/Makefile index 2647f377c3d..a3d08d2986b 100644 --- a/package/libs/libtracefs/Makefile +++ b/package/libs/libtracefs/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=libtracefs -PKG_VERSION:=1.6.2 +PKG_VERSION:=1.6.3 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/snapshot/ -PKG_HASH:=3598ead36660b03cec097bd7e45d900e4c3f7673bba673f5508884477d2d982c +PKG_HASH:=de307faaa54ac52cf0ce3ff19b32b51af24897e3440e643068ac82d31e197e92 PKG_MAINTAINER:=Nick Hainke From 95ed519b25b0e235d2fa00e172f6ca18e50b6241 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 14:42:35 +0100 Subject: [PATCH 077/110] trace-cmd: update to v3.1.5 Update to latest release. Signed-off-by: Nick Hainke --- package/devel/trace-cmd/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/devel/trace-cmd/Makefile b/package/devel/trace-cmd/Makefile index b6a28ab93a6..47929491ee5 100644 --- a/package/devel/trace-cmd/Makefile +++ b/package/devel/trace-cmd/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=trace-cmd -PKG_VERSION:=v3.1.4 +PKG_VERSION:=v3.1.5 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/snapshot/ -PKG_HASH:=447e095dbdfb0d362ab8c2086d62d80c5a2ecf67aef09b8f6b0cc064c0e1bfb5 +PKG_HASH:=9af1ea00e312d03639470e126fa9c786789f03c16df93a57c0bc90eeffbc7d50 PKG_LICENSE:=GPL-2.0-only PKG_LICENSE_FILES:=COPYING From fc47874ef4787c255833c1af5e6f32292a898290 Mon Sep 17 00:00:00 2001 From: Linhui Liu Date: Thu, 15 Dec 2022 20:11:16 +0800 Subject: [PATCH 078/110] tools/bash: update to 5.2.15 Update to the latest released version. Signed-off-by: Linhui Liu --- tools/bash/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/bash/Makefile b/tools/bash/Makefile index 11b38da6a1d..f4bfc07397d 100644 --- a/tools/bash/Makefile +++ b/tools/bash/Makefile @@ -8,11 +8,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=bash PKG_CPE_ID:=cpe:/a:gnu:bash -PKG_VERSION:=5.2.9 +PKG_VERSION:=5.2.15 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=@GNU/bash -PKG_HASH:=68d978264253bc933d692f1de195e2e5b463a3984dfb4e5504b076865f16b6dd +PKG_HASH:=13720965b5f4fc3a0d4b61dd37e7565c741da9a5be24edc2ae00182fc1b3588c HOST_BUILD_PARALLEL := 1 From 74b7d6960158422b8fb0ccaa4bb52f6838990e23 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Tue, 13 Dec 2022 20:05:07 +0100 Subject: [PATCH 079/110] strace: update to 6.1 Release Notes: https://github.com/strace/strace/releases/tag/v6.1 Signed-off-by: Nick Hainke --- package/devel/strace/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/devel/strace/Makefile b/package/devel/strace/Makefile index 7500c373167..2a4516f4bd3 100644 --- a/package/devel/strace/Makefile +++ b/package/devel/strace/Makefile @@ -9,12 +9,12 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=strace -PKG_VERSION:=6.0 +PKG_VERSION:=6.1 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=https://strace.io/files/$(PKG_VERSION) -PKG_HASH:=92d720a666855e9f1c6a11512fd6e99674a82bbfe1442557815f2ce8e1293338 +PKG_HASH:=2579e9cec37dbb786f6ea0bebd15f40dd561ef2bde2a2a2ecdce5963b01859fd PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=LGPL-2.1-or-later From 46077860c2be38164aa41f00790c2cd9384d4023 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 18:09:21 +0100 Subject: [PATCH 080/110] ath79: convert boards based on ar9344_openmesh_mr600.dtsi to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Merge art into partition node. Signed-off-by: Nick Hainke --- .../ath79/dts/ar9344_openmesh_mr600.dtsi | 38 ++++++++++--------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 2 - 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_openmesh_mr600.dtsi b/target/linux/ath79/dts/ar9344_openmesh_mr600.dtsi index 5a6ef3b0fce..b35d699bdc1 100644 --- a/target/linux/ath79/dts/ar9344_openmesh_mr600.dtsi +++ b/target/linux/ath79/dts/ar9344_openmesh_mr600.dtsi @@ -70,10 +70,26 @@ reg = <0x850000 0x7a0000>; }; - art: partition@ff0000 { + partition@ff0000 { label = "ART"; reg = <0xff0000 0x010000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; }; }; @@ -111,9 +127,8 @@ &wmac { status = "okay"; - mtd-cal-data = <&art 0x1000>; - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <1>; }; @@ -123,22 +138,11 @@ ath9k: wifi@0,0 { compatible = "pci168c,0030"; reg = <0x0000 0 0 0 0>; - qca,no-eeprom; - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <8>; gpio-controller; #gpio-cells = <2>; }; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 9640baac172..98d760fa428 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -112,8 +112,6 @@ case "$FIRMWARE" in tplink,tl-wr842n-v1) caldata_extract "art" 0x1000 0x1000 ;; - openmesh,mr600-v1|\ - openmesh,mr600-v2|\ openmesh,om5p-an) caldata_extract "ART" 0x5000 0x440 ;; From f6ca84bf02a816fa66bc656f0f7d3c233207d65c Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 18:13:55 +0100 Subject: [PATCH 081/110] ath79: convert OpenMesh OM5P-AN WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Merge art into partition node. Signed-off-by: Nick Hainke --- .../ath79/dts/ar9344_openmesh_om5p-an.dts | 38 ++++++++++--------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 3 -- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/target/linux/ath79/dts/ar9344_openmesh_om5p-an.dts b/target/linux/ath79/dts/ar9344_openmesh_om5p-an.dts index 1925031e0b0..6fab2b3219b 100644 --- a/target/linux/ath79/dts/ar9344_openmesh_om5p-an.dts +++ b/target/linux/ath79/dts/ar9344_openmesh_om5p-an.dts @@ -148,10 +148,26 @@ reg = <0x850000 0x7a0000>; }; - art: partition@ff0000 { + partition@ff0000 { label = "ART"; reg = <0xff0000 0x010000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; + + calibration_art_5000: calibration@5000 { + reg = <0x5000 0x440>; + }; }; }; }; @@ -198,9 +214,8 @@ &wmac { status = "okay"; - mtd-cal-data = <&art 0x1000>; - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <2>; }; @@ -210,19 +225,8 @@ wifi@0,0 { compatible = "pci168c,0030"; reg = <0x0000 0 0 0 0>; - qca,no-eeprom; - nvmem-cells = <&macaddr_art_0>; - nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_art_0>, <&calibration_art_5000>; + nvmem-cell-names = "mac-address", "calibration"; mac-address-increment = <16>; }; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 98d760fa428..aa76d1bc9f8 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -112,9 +112,6 @@ case "$FIRMWARE" in tplink,tl-wr842n-v1) caldata_extract "art" 0x1000 0x1000 ;; - openmesh,om5p-an) - caldata_extract "ART" 0x5000 0x440 - ;; openmesh,om2p-v1) caldata_extract "ART" 0x1000 0x440 ;; From d4ec4f9d0b83fbb79bbcd14e1022069e0024e46b Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 18:18:16 +0100 Subject: [PATCH 082/110] ath79: convert OpenMesh OM2P v1 WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Merge art into partition node. Signed-off-by: Nick Hainke --- .../ath79/dts/ar7240_openmesh_om2p-v1.dts | 35 ++++++++++--------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 3 -- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/target/linux/ath79/dts/ar7240_openmesh_om2p-v1.dts b/target/linux/ath79/dts/ar7240_openmesh_om2p-v1.dts index 456724a750a..a66d9140150 100644 --- a/target/linux/ath79/dts/ar7240_openmesh_om2p-v1.dts +++ b/target/linux/ath79/dts/ar7240_openmesh_om2p-v1.dts @@ -122,10 +122,26 @@ reg = <0x8c0000 0x700000>; }; - art: partition@fc0000 { + partition@fc0000 { label = "ART"; reg = <0xfc0000 0x040000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_art_6: macaddr@6 { + reg = <0x6 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; }; }; }; @@ -149,22 +165,9 @@ wifi@0,0 { compatible = "pci168c,002a"; reg = <0x0000 0 0 0 0>; - qca,no-eeprom; + nvmem-cells = <&calibration_art_1000>; + nvmem-cell-names = "calibration"; #gpio-cells = <2>; gpio-controller; }; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; - - macaddr_art_6: macaddr@6 { - reg = <0x6 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index aa76d1bc9f8..ebcac0b6b29 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -112,9 +112,6 @@ case "$FIRMWARE" in tplink,tl-wr842n-v1) caldata_extract "art" 0x1000 0x1000 ;; - openmesh,om2p-v1) - caldata_extract "ART" 0x1000 0x440 - ;; wd,mynet-n600|\ wd,mynet-n750) caldata_extract "art" 0x5000 0x440 From b7ad3c5c5d16de37f393fbee2233e1aa1eb7f3b3 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 15:17:26 +0100 Subject: [PATCH 083/110] ath79: convert Buffalo WZR-HP-G302H A1A0 WiFis to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. Merge art into partition node. Signed-off-by: Nick Hainke --- .../dts/ar7242_buffalo_wzr-hp-g302h-a1a0.dts | 29 ++++++++++--------- .../etc/hotplug.d/firmware/10-ath9k-eeprom | 1 - 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/target/linux/ath79/dts/ar7242_buffalo_wzr-hp-g302h-a1a0.dts b/target/linux/ath79/dts/ar7242_buffalo_wzr-hp-g302h-a1a0.dts index c6fd220ed14..4a2f749cc45 100644 --- a/target/linux/ath79/dts/ar7242_buffalo_wzr-hp-g302h-a1a0.dts +++ b/target/linux/ath79/dts/ar7242_buffalo_wzr-hp-g302h-a1a0.dts @@ -133,10 +133,22 @@ label = "u-boot-env"; }; - art: partition@50000 { + partition@50000 { reg = <0x50000 0x10000>; label = "art"; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_120c: macaddr@120c { + reg = <0x120c 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0xeb8>; + }; }; partition@60000 { @@ -197,9 +209,8 @@ ath9k: wifi@0,0 { compatible = "pci168c,002a"; reg = <0x0000 0 0 0 0>; - nvmem-cells = <&macaddr_art_120c>; - nvmem-cell-names = "mac-address"; - qca,no-eeprom; + nvmem-cells = <&macaddr_art_120c>, <&calibration_art_1000>; + nvmem-cell-names = "mac-address", "calibration"; #gpio-cells = <2>; gpio-controller; }; @@ -212,13 +223,3 @@ &usb { status = "okay"; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_120c: macaddr@120c { - reg = <0x120c 0x6>; - }; -}; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index ebcac0b6b29..5fcfcaf64fa 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -86,7 +86,6 @@ case "$FIRMWARE" in avm,fritz300e) caldata_extract_reverse "urloader" 0x1541 0x440 ;; - buffalo,wzr-hp-g302h-a1a0|\ ubnt,unifi-ap-outdoor-plus) caldata_extract "art" 0x1000 0xeb8 ;; From af5306ba70733f7e736b0aef582fb93519fccde9 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Thu, 15 Dec 2022 17:04:14 +0100 Subject: [PATCH 084/110] ath79: convert WiFis based on ar7241_ubnt_unifi.dtsi to nvmem-cells Pull the calibration data from the nvmem subsystem. This allows us to move userspace caldata extraction into the device-tree definition. While working on it remove stale uboot partition label and merge art into partition node. Signed-off-by: Nick Hainke --- .../dts/ar7241_ubnt_unifi-ap-outdoor-plus.dts | 36 ++++++++++--------- .../linux/ath79/dts/ar7241_ubnt_unifi-ap.dtsi | 29 +++++++++------ target/linux/ath79/dts/ar7241_ubnt_unifi.dtsi | 1 - .../etc/hotplug.d/firmware/10-ath9k-eeprom | 6 +--- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/target/linux/ath79/dts/ar7241_ubnt_unifi-ap-outdoor-plus.dts b/target/linux/ath79/dts/ar7241_ubnt_unifi-ap-outdoor-plus.dts index 9ccbdbd450d..3f965ec9dea 100644 --- a/target/linux/ath79/dts/ar7241_ubnt_unifi-ap-outdoor-plus.dts +++ b/target/linux/ath79/dts/ar7241_ubnt_unifi-ap-outdoor-plus.dts @@ -41,7 +41,7 @@ #address-cells = <1>; #size-cells = <1>; - uboot: partition@0 { + partition@0 { label = "u-boot"; reg = <0x0 0x40000>; read-only; @@ -82,10 +82,26 @@ read-only; }; - art: partition@ff0000 { + partition@ff0000 { label = "art"; reg = <0xff0000 0x10000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_art_6: macaddr@6 { + reg = <0x6 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0xeb8>; + }; }; }; }; @@ -105,18 +121,6 @@ &wifi { ubnt,hsr; -}; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; - - macaddr_art_6: macaddr@6 { - reg = <0x6 0x6>; - }; + nvmem-cells = <&calibration_art_1000>; + nvmem-cell-names = "calibration"; }; diff --git a/target/linux/ath79/dts/ar7241_ubnt_unifi-ap.dtsi b/target/linux/ath79/dts/ar7241_ubnt_unifi-ap.dtsi index c70a1c02dba..02166a26eb8 100644 --- a/target/linux/ath79/dts/ar7241_ubnt_unifi-ap.dtsi +++ b/target/linux/ath79/dts/ar7241_ubnt_unifi-ap.dtsi @@ -68,15 +68,32 @@ read-only; }; - art: partition@7f0000 { + partition@7f0000 { label = "art"; reg = <0x7f0000 0x010000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + calibration_art_1000: calibration@1000 { + reg = <0x1000 0x440>; + }; }; }; }; }; +&wifi { + nvmem-cells = <&calibration_art_1000>; + nvmem-cell-names = "calibration"; +}; + ð0 { nvmem-cells = <&macaddr_art_0>; nvmem-cell-names = "mac-address"; @@ -85,13 +102,3 @@ ð1 { compatible = "syscon", "simple-mfd"; }; - -&art { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_art_0: macaddr@0 { - reg = <0x0 0x6>; - }; -}; diff --git a/target/linux/ath79/dts/ar7241_ubnt_unifi.dtsi b/target/linux/ath79/dts/ar7241_ubnt_unifi.dtsi index ec8c6381e71..fa6c2337333 100644 --- a/target/linux/ath79/dts/ar7241_ubnt_unifi.dtsi +++ b/target/linux/ath79/dts/ar7241_ubnt_unifi.dtsi @@ -26,7 +26,6 @@ wifi: wifi@0,0 { reg = <0x0000 0 0 0 0>; - qca,no-eeprom; }; }; diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 5fcfcaf64fa..4f107bfe881 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -86,11 +86,7 @@ case "$FIRMWARE" in avm,fritz300e) caldata_extract_reverse "urloader" 0x1541 0x440 ;; - ubnt,unifi-ap-outdoor-plus) - caldata_extract "art" 0x1000 0xeb8 - ;; - buffalo,wzr-hp-g450h|\ - ubnt,unifi) + buffalo,wzr-hp-g450h) caldata_extract "art" 0x1000 0x440 ;; dlink,dir-825-c1|\ From 7bcf6b54fc819c26fc2cea32c464e87614cc2d26 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Sun, 9 Oct 2022 23:15:28 +0800 Subject: [PATCH 085/110] sunxi: fix typo in device packages for MarsBoard A10 The kmod prefix for sound-soc-sunxi is missing, fix it. Also add kmod-sound-core as dependence. Fixes: 6a35659 ("sunxi: Added profile for HAOYU Electronics Marsboard A10") Signed-off-by: Chukun Pan --- target/linux/sunxi/image/cortexa8.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/sunxi/image/cortexa8.mk b/target/linux/sunxi/image/cortexa8.mk index ceb15a0fd8d..7f50659bdde 100644 --- a/target/linux/sunxi/image/cortexa8.mk +++ b/target/linux/sunxi/image/cortexa8.mk @@ -22,8 +22,8 @@ TARGET_DEVICES += linksprite_a10-pcduino define Device/marsboard_a10-marsboard DEVICE_VENDOR := HAOYU Electronics DEVICE_MODEL := MarsBoard A10 - DEVICE_PACKAGES:=kmod-ata-core kmod-ata-sunxi kmod-sun4i-emac kmod-rtc-sunxi \ - sound-soc-sunxi + DEVICE_PACKAGES:=kmod-ata-core kmod-ata-sunxi kmod-sun4i-emac \ + kmod-rtc-sunxi kmod-sound-core kmod-sound-soc-sunxi SOC := sun4i endef TARGET_DEVICES += marsboard_a10-marsboard From 53123b93b0a14deb43274c44ea473ea9b360acf3 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Mon, 10 Oct 2022 23:08:27 +0800 Subject: [PATCH 086/110] sunxi: fix board_name for MarsBoard A10 The compatible in the device tree is "haoyu,a10-marsboard", modify the board_name to keep it consistent. Signed-off-by: Chukun Pan --- package/boot/uboot-sunxi/Makefile | 4 ++-- target/linux/sunxi/image/cortexa8.mk | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/package/boot/uboot-sunxi/Makefile b/package/boot/uboot-sunxi/Makefile index 5c27407d155..75301d69beb 100644 --- a/package/boot/uboot-sunxi/Makefile +++ b/package/boot/uboot-sunxi/Makefile @@ -116,8 +116,8 @@ endef define U-Boot/Marsboard_A10 BUILD_SUBTARGET:=cortexa8 - NAME:=Marsboard - BUILD_DEVICES:=marsboard_a10-marsboard + NAME:=HAOYU Marsboard A10 + BUILD_DEVICES:=haoyu_a10-marsboard endef define U-Boot/Mele_M9 diff --git a/target/linux/sunxi/image/cortexa8.mk b/target/linux/sunxi/image/cortexa8.mk index 7f50659bdde..eafc2187e84 100644 --- a/target/linux/sunxi/image/cortexa8.mk +++ b/target/linux/sunxi/image/cortexa8.mk @@ -11,6 +11,16 @@ define Device/cubietech_a10-cubieboard endef TARGET_DEVICES += cubietech_a10-cubieboard +define Device/haoyu_a10-marsboard + DEVICE_VENDOR := HAOYU Electronics + DEVICE_MODEL := MarsBoard A10 + DEVICE_PACKAGES:=kmod-ata-core kmod-ata-sunxi kmod-sun4i-emac \ + kmod-rtc-sunxi kmod-sound-core kmod-sound-soc-sunxi + SUPPORTED_DEVICES += marsboard,a10-marsboard + SOC := sun4i +endef +TARGET_DEVICES += haoyu_a10-marsboard + define Device/linksprite_a10-pcduino DEVICE_VENDOR := LinkSprite DEVICE_MODEL := pcDuino @@ -19,15 +29,6 @@ define Device/linksprite_a10-pcduino endef TARGET_DEVICES += linksprite_a10-pcduino -define Device/marsboard_a10-marsboard - DEVICE_VENDOR := HAOYU Electronics - DEVICE_MODEL := MarsBoard A10 - DEVICE_PACKAGES:=kmod-ata-core kmod-ata-sunxi kmod-sun4i-emac \ - kmod-rtc-sunxi kmod-sound-core kmod-sound-soc-sunxi - SOC := sun4i -endef -TARGET_DEVICES += marsboard_a10-marsboard - define Device/olimex_a10-olinuxino-lime DEVICE_VENDOR := Olimex DEVICE_MODEL := A10-OLinuXino-LIME From a7d8b54f86f572409b40c7ab4ad3982a1eabfdfe Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Mon, 12 Dec 2022 11:18:41 +0000 Subject: [PATCH 087/110] ramips: improve support for H3C TX180x series devices 1. Explicitly declare gpio pin groups to ensure that gpio works properly. 2. Override bootargs in device tree to avoid modifying u-boot envs during initial installation. Tested on H3C TX1801 Plus Signed-off-by: Shiji Yang --- target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi b/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi index 61f2f8cc98c..54d7908e406 100644 --- a/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi +++ b/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi @@ -16,6 +16,7 @@ chosen { bootargs = "console=ttyS0,115200"; + bootargs-override = "console=ttyS0,115200"; }; keys { @@ -141,6 +142,13 @@ status = "disabled"; }; +&state_default { + gpio { + groups = "jtag"; + function = "gpio"; + }; +}; + &switch0 { ports { port@1 { From 6361a952afa4827eb5e9d5221b9bb4b54a2019d5 Mon Sep 17 00:00:00 2001 From: Shiji Yang Date: Mon, 12 Dec 2022 11:25:45 +0000 Subject: [PATCH 088/110] ramips: improve support for SIM SIMAX1800T and Haier HAR-20S2U1 1. Use ARTIFACTS to build factory image. This change allows users to generate initramfs factory image using OpenWrt ImageBuilder. 2. Override the default bootargs property defined in "mt7621.dtsi". Although we use the "bootargs-override" property to set bootargs, the default "bootargs" property will still be written into the device tree, so it is better to override it. Tested on SIM SIMAX1800T Signed-off-by: Shiji Yang --- .../ramips/dts/mt7621_haier-sim_wr1800k.dtsi | 1 + target/linux/ramips/image/mt7621.mk | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi b/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi index 32d42fe813a..a272d1ad755 100644 --- a/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi +++ b/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi @@ -16,6 +16,7 @@ }; chosen { + bootargs = "console=ttyS0,115200"; bootargs-override = "console=ttyS0,115200"; }; diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk index fd99fa22c21..75022f97083 100644 --- a/target/linux/ramips/image/mt7621.mk +++ b/target/linux/ramips/image/mt7621.mk @@ -38,15 +38,13 @@ define Build/h3c-blank-header endef define Build/haier-sim_wr1800k-factory - -[ -e $(KDIR)/tmp/$(KERNEL_INITRAMFS_IMAGE) ] && \ - mkdir -p "$(1).tmp" && \ - $(CP) $(KDIR)/tmp/$(KERNEL_INITRAMFS_IMAGE) "$(1).tmp/UploadBrush-bin.img" && \ - $(MKHASH) md5 "$(1).tmp/UploadBrush-bin.img" | head -c32 > "$(1).tmp/check_MD5.txt" && \ - $(TAR) -czf $(1).tmp.tgz -C "$(1).tmp" UploadBrush-bin.img check_MD5.txt && \ - $(STAGING_DIR_HOST)/bin/openssl aes-256-cbc -e -salt -in $(1).tmp.tgz -out "$(1)" -k QiLunSmartWL && \ - printf %32s "$(DEVICE_MODEL)" >> "$(1)" && \ - rm -rf "$(1).tmp" $(1).tmp.tgz && \ - $(CP) $(1) $(BIN_DIR)/ + mkdir -p "$@.tmp" + mv "$@" "$@.tmp/UploadBrush-bin.img" + $(MKHASH) md5 "$@.tmp/UploadBrush-bin.img" | head -c32 > "$@.tmp/check_MD5.txt" + $(TAR) -czf "$@.tmp.tgz" -C "$@.tmp" UploadBrush-bin.img check_MD5.txt + $(STAGING_DIR_HOST)/bin/openssl aes-256-cbc -e -salt -in "$@.tmp.tgz" -out "$@" -k QiLunSmartWL + printf %32s $(DEVICE_MODEL) >> "$@" + rm -rf "$@.tmp" "$@.tmp.tgz" endef define Build/iodata-mstc-header @@ -904,8 +902,11 @@ define Device/haier-sim_wr1800k KERNEL_LOADADDR := 0x82000000 KERNEL := kernel-bin | relocate-kernel 0x80001000 | lzma | \ fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb - KERNEL_INITRAMFS := $$(KERNEL) | \ - haier-sim_wr1800k-factory $(KDIR)/tmp/$$(KERNEL_INITRAMFS_PREFIX)-factory.bin +ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),) + ARTIFACTS := initramfs-factory.bin + ARTIFACT/initramfs-factory.bin := append-image-stage initramfs-kernel.bin | \ + haier-sim_wr1800k-factory +endif IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata DEVICE_PACKAGES := kmod-mt7915e uboot-envtools endef From d9566d059c41e4ff491cfdda6721223f69821340 Mon Sep 17 00:00:00 2001 From: Davide Fioravanti Date: Tue, 13 Dec 2022 23:28:08 +0100 Subject: [PATCH 089/110] ath79: add support for KuWFi C910 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KuWFi C910 is an 802.11n (300N) indoor router with LTE support. I can't find anywhere the OEM firmware. So if you want to restore the original firmware you must do a dump before the OpenWrt flash. According to the U-Boot, the board name is Iyunlink MINI_V2. Hardware -------- SoC: Qualcomm QCA9533 650/400/200/25/25 MHz (CPU/RAM/AHB/SPI/REF) RAM: 128 MB DDR2 16-bit CL3-4-4-10 (Nanya NT5TU64M16HG-AC) FLASH: 16 MB Winbond W25Q128 ETH: - 2x 100M LAN (QCA9533 internal AR8229 switch, eth0) - 1x 100M WAN (QCA9533 internal PHY, eth1) WIFI: - 2.4GHz: 1x QCA9533 2T2R (b/g/n) - 2 external non detachable antennas (near the power barrel side) LTE: - Quectel EC200T-EU (or -CN or -AU depending on markets) - 2 external non detachable antennas (near the sim slot side) BTN: - 1x Reset button LEDS: - 5x White leds (Power, Wifi, Wan, Lan1, Lan2) - 1x RGB led (Internet) UART: 115200-8-N-1 (Starting from lan ports in order: GND, RX, TX, VCC) Everything works correctly. MAC Addresses ------------- LAN XX:XX:XX:XX:XX:48 (art@0x1002) WAN XX:XX:XX:XX:XX:49 (art@0x1002 + 1) WIFI XX:XX:XX:XX:XX:48 LABEL XX:XX:XX:XX:XX:48 Installation ------------ Turn the router on while pressing the reset button for 4 seconds. You can simply count the flashes of the first lan led. (See notes) If done correctly you should see the first lan led glowing slowly and you should be able to enter the U-Boot web interface. Click on the second tab ("固件") and select the -factory.bin firmware then click "Update firmware". A screen "Update in progress" should appear. After few minutes the flash should be completed. This procedure can be used also to recover the router in case of soft brick. Backup the original firmware ---------------------------- The following steps are intended for a linux pc. However using the right software this guide should also work for Windows and MacOS. 1) Install a tftp server on your pc. For example tftpd-hpa. 2) Create two empty files in your tftp folder called: kuwfi_c910_all_nor.bin kuwfi_c910_firmware_only.bin 3) Give global write permissions to these files: chmod 666 kuwfi_c910_all_nor.bin chmod 666 kuwfi_c910_firmware_only.bin 4) Start a netcat session on your pc with this command: nc -u -p 6666 192.168.1.1 6666 5) Set the static address on your pc: 192.168.1.2. Connect the router to your pc. 6) Turn the router on while pressing the reset button for 8-9 seconds. You can simply count the flashes of the first lan led. If you press the reset button for too many seconds it will continue the normal boot, so you have to restart the router. (See notes) 7) If done correctly you should see the U-Boot network console and you should see the following lines on the netcat session: Version and build date: U-Boot 1.1.4-55f1bca8-dirty, 2020-05-07 Modification by: Piotr Dymacz https://github.com/pepe2k/u-boot_mod u-boot> 8) Start the transfer of the whole NOR: tftpput 0x9f000000 0x1000000 kuwfi_c910_all_nor.bin 9) The router should start the transfer and it should end with a message like this (pay attention to the bytes transferred): TFTP transfer complete! Bytes transferred: 16777216 (0x1000000) 10) Repeat the same transfer for the firmware: tftpput 0x9f050000 0xfa0000 kuwfi_c910_firmware_only.bin 11) The router should start the transfer and it should end with a message like this (pay attention to the bytes transferred): TFTP transfer complete! Bytes transferred: 16384000 (0xfa0000) 12) Now you have the backup for the whole nor and for the firmware partition. If you want to restore the OEM firmware from OpenWrt you have to flash the kuwfi_c910_firmware_only.bin from the U-Boot web interface. WARNING: Don't use the kuwfi_c910_all_nor.bin file. This file is only useful if you manage to hard brick the router or you damage the art partition (ask on the forum) Notes ----- This router (or at least my unit) has the pepe2k's U-Boot. It's a modded U-Boot version with a lot of cool features. You can read more here: https://github.com/pepe2k/u-boot_mod With this version of U-Boot, pushing the reset button while turning on the router starts different tools: - 3-5 seconds: U-Boot web interface that can be used to replace the firmware, the art or the U-Boot itself - 5-7 seconds: U-Boot uart console - 7-10 seconds: U-Boot network console - 11+ seconds: Normal boot The LTE modem can be used in cdc_ether (ECM) or RNDIS mode. The default mode is ECM and in this commit only the ECM software is included. In order to set RNDIS mode you must use this AT command: AT+QCFG="usbnet",3 In order to use again the ECM mode you must use this AT command: AT+QCFG="usbnet",1 Look for "Quectel_EC200T_Linux_USB_Driver_User_Guide_V1.0.pdf" for other AT commands Signed-off-by: Davide Fioravanti --- target/linux/ath79/dts/qca9533_kuwfi_c910.dts | 194 ++++++++++++++++++ .../generic/base-files/etc/board.d/01_leds | 5 + .../generic/base-files/etc/board.d/02_network | 5 + target/linux/ath79/image/generic.mk | 16 ++ 4 files changed, 220 insertions(+) create mode 100644 target/linux/ath79/dts/qca9533_kuwfi_c910.dts diff --git a/target/linux/ath79/dts/qca9533_kuwfi_c910.dts b/target/linux/ath79/dts/qca9533_kuwfi_c910.dts new file mode 100644 index 00000000000..a705aa8686b --- /dev/null +++ b/target/linux/ath79/dts/qca9533_kuwfi_c910.dts @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "qca953x.dtsi" + +#include +#include +#include + +/ { + compatible = "kuwfi,c910", "qca,qca9533"; + model = "KuWFi C910"; + + aliases { + label-mac-device = ð1; + led-boot = &internet_red; + led-failsafe = &internet_red; + led-upgrade = &internet_red; + }; + + virtual_flash { + compatible = "mtd-concat"; + devices = <&fwconcat0 &fwconcat1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + reg = <0x0 0x0>; + label = "firmware"; + compatible = "openwrt,uimage", "denx,uimage"; + openwrt,ih-magic = ; + }; + }; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + linux,code = ; + gpios = <&gpio 17 GPIO_ACTIVE_LOW>; + debounce-interval = <60>; + }; + }; + + leds { + compatible = "gpio-leds"; + + internet_blue { + label = "blue:internet"; + gpios = <&gpio 1 GPIO_ACTIVE_LOW>; + }; + + wifi { + label = "white:wifi"; + gpios = <&gpio 2 GPIO_ACTIVE_LOW>; + linux,default-trigger = "phy0tpt"; + }; + + internet_green { + label = "green:internet"; + gpios = <&gpio 3 GPIO_ACTIVE_LOW>; + }; + + wan { + label = "white:wan"; + gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + }; + + lan2 { + label = "white:lan2"; + gpios = <&gpio 11 GPIO_ACTIVE_LOW>; + }; + + // gpio 12 enables or disables LTE (Quectel EC200T) + + internet_red: internet_red { + label = "red:internet"; + gpios = <&gpio 14 GPIO_ACTIVE_LOW>; + }; + + lan1 { + label = "white:lan1"; + gpios = <&gpio 15 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&spi { + status = "okay"; + + flash@0 { + compatible = "winbond,w25q128", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <25000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x000000 0x020000>; + read-only; + }; + + partition@20000 { + label = "u-boot-env"; + reg = <0x020000 0x010000>; + read-only; + }; + + partition@30000 { + label = "config"; + reg = <0x030000 0x020000>; + read-only; + }; + + fwconcat0: partition@50000 { + label = "fwconcat0"; + reg = <0x050000 0xe30000>; + }; + + partition@e80000 { + label = "loader"; + reg = <0xe80000 0x10000>; + }; + + fwconcat1: partition@e90000 { + label = "fwconcat1"; + reg = <0xe90000 0x160000>; + }; + + partition@ff0000 { + label = "art"; + reg = <0xff0000 0x010000>; + read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + calibration_art_1000: macaddr@1000 { + reg = <0x1000 0x440>; + }; + + macaddr_art_1002: macaddr@1002 { + reg = <0x1002 0x6>; + }; + }; + }; + }; +}; + +&usb0 { + status = "okay"; +}; + +&usb_phy { + status = "okay"; +}; + +ð0 { + status = "okay"; + + phy-handle = <&swphy0>; + + nvmem-cells = <&macaddr_art_1002>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <1>; +}; + +ð1 { + phy-handle = <&swphy4>; + + nvmem-cells = <&macaddr_art_1002>; + nvmem-cell-names = "mac-address"; + + gmac-config { + device = <&gmac>; + switch-phy-swap = <1>; + }; +}; + +&wmac { + status = "okay"; + + nvmem-cells = <&calibration_art_1000>; + nvmem-cell-names = "calibration"; +}; diff --git a/target/linux/ath79/generic/base-files/etc/board.d/01_leds b/target/linux/ath79/generic/base-files/etc/board.d/01_leds index f0610f39e91..5855d2a9287 100644 --- a/target/linux/ath79/generic/base-files/etc/board.d/01_leds +++ b/target/linux/ath79/generic/base-files/etc/board.d/01_leds @@ -268,6 +268,11 @@ hiwifi,hc6361) ucidef_set_led_netdev "wan" "WAN" "blue:wan" "eth1" ucidef_set_led_wlan "wlan" "WLAN" "blue:wlan" "phy0tpt" ;; +kuwfi,c910) + ucidef_set_led_netdev "wan" "WAN" "white:wan" "eth1" + ucidef_set_led_switch "lan1" "LAN1" "white:lan1" "switch0" "0x10" + ucidef_set_led_switch "lan2" "LAN2" "white:lan2" "switch0" "0x02" + ;; meraki,mr12|\ tplink,cpe210-v2|\ tplink,cpe210-v3) diff --git a/target/linux/ath79/generic/base-files/etc/board.d/02_network b/target/linux/ath79/generic/base-files/etc/board.d/02_network index 529d33782fa..92541cd9f8b 100644 --- a/target/linux/ath79/generic/base-files/etc/board.d/02_network +++ b/target/linux/ath79/generic/base-files/etc/board.d/02_network @@ -347,6 +347,11 @@ ath79_setup_interfaces() ucidef_add_switch "switch0" \ "0@eth0" "2:lan:3" "3:lan:2" "4:lan:1" ;; + kuwfi,c910) + ucidef_set_interface_wan "eth1" + ucidef_add_switch "switch0" \ + "0@eth0" "1:lan:2" "4:lan:1" + ;; letv,lba-047-ch) ucidef_set_interface_wan "eth0" ucidef_add_switch "switch0" \ diff --git a/target/linux/ath79/image/generic.mk b/target/linux/ath79/image/generic.mk index 9c209b041f1..d66684a124e 100644 --- a/target/linux/ath79/image/generic.mk +++ b/target/linux/ath79/image/generic.mk @@ -1583,6 +1583,22 @@ define Device/joyit_jt-or750i endef TARGET_DEVICES += joyit_jt-or750i +define Device/kuwfi_c910 + $(Device/loader-okli-uimage) + SOC := qca9533 + DEVICE_VENDOR := KuWFi + DEVICE_MODEL := C910 + DEVICE_PACKAGES := kmod-usb2 kmod-usb-net-cdc-ether comgt-ncm + LOADER_FLASH_OFFS := 0x50000 + KERNEL := kernel-bin | append-dtb | lzma | uImage lzma -M 0x4f4b4c49 + IMAGE_SIZE := 15936k + IMAGES += factory.bin + IMAGE/factory.bin := append-kernel | pad-to $$$$(BLOCKSIZE) | \ + append-rootfs | pad-rootfs | check-size | pad-to 14528k | \ + append-loader-okli-uimage $(1) | pad-to 64k +endef +TARGET_DEVICES += kuwfi_c910 + define Device/letv_lba-047-ch $(Device/loader-okli-uimage) SOC := qca9531 From a442974cfa89c7182c37b3b422b2d49319e2b339 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 17 Dec 2022 22:10:39 +0100 Subject: [PATCH 090/110] gdb: Do not link against xxhash libxxhash is now available in the OpenWrt package feed and gdb will link against it if gdb finds this library. Explicitly deactivate the usage of xxhash. This should fix the build of gdb in build bots. Signed-off-by: Hauke Mehrtens --- package/devel/gdb/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/package/devel/gdb/Makefile b/package/devel/gdb/Makefile index 7acf26d60b9..1b059dcc50b 100644 --- a/package/devel/gdb/Makefile +++ b/package/devel/gdb/Makefile @@ -66,6 +66,7 @@ CONFIGURE_ARGS+= \ --without-mpc \ --without-mpfr \ --without-isl \ + --without-xxhash \ --with-libgmp-prefix=$(STAGING_DIR)/usr CONFIGURE_VARS+= \ From 3f201d1f8ece4ed363428f353558d44f9dbe98a5 Mon Sep 17 00:00:00 2001 From: Alexey Bartenev <41exey@proton.me> Date: Mon, 24 Oct 2022 22:50:33 +0700 Subject: [PATCH 091/110] ramips: add support for SNR-CPE-W4N-MT router MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit General specification: - SoC Type: MediaTek MT7620N (580MHz) - ROM: 8 MB SPI-NOR (W25Q64FV) - RAM: 64 MB DDR (M13S5121632A) - Switch: MediaTek MT7530 - Ethernet: 5 ports - 5×100MbE (WAN, LAN1-4) - Wireless 2.4 GHz: b/g/n - Buttons: 1 button (RESET) - Bootloader: U-Boot 1.1.3, MediaTek U-Boot: 5.0.0.5 - Power: 12 VDC, 1.0 A Flash by the native uploader in 2 stages: 1. Use the native uploader to flash an initramfs image. Choose openwrt-ramips-mt7620-snr_cpe-w4n-mt-initramfs-kernel.bin file by "Administration/Management/Firmware update/Choose File" in vendor's web interface (ip: 192.168.1.10, login: Admin, password: Admin). Wait ~160 seconds. 2. Flash a sysupgrade image via the initramfs image. Choose openwrt-ramips-mt7620-snr_cpe-w4n-mt-squashfs-sysupgrade.bin file by "System/Backup/Flash Firmware/Flash image..." in LuCI web interface (ip: 192.168.1.1, login: root, no password). Wait ~240 seconds. Flash by U-Boot TFTP method: 1. Configure your PC with IP 192.168.1.131 2. Set up TFTP server and put the openwrt-ramips-mt7620-snr_cpe-w4n-mt-squashfs-sysupgrade.bin image on your PC 3. Connect serial port (57600 8N1) and turn on the router. Then interrupt "U-Boot Boot Menu" by hitting 2 key (select "2: Load system code then write to Flash via TFTP."). Press Y key when show "Warning!! Erase Linux in Flash then burn new one. Are you sure? (Y/N)" Input device IP (192.168.1.1) ==:192.168.1.1 Input server IP (192.168.1.131) ==:192.168.1.131 Input Linux Kernel filename () ==: openwrt-ramips-mt7620-snr_cpe-w4n-mt-squashfs-sysupgrade.bin 3. Wait ~120 seconds to complete flashing Signed-off-by: Alexey Bartenev <41exey@proton.me> --- package/boot/uboot-envtools/files/ramips | 5 + .../ramips/dts/mt7620n_snr_cpe-w4n-mt.dts | 122 ++++++++++++++++++ target/linux/ramips/image/mt7620.mk | 11 ++ .../mt7620/base-files/etc/board.d/01_leds | 5 + .../mt7620/base-files/etc/board.d/02_network | 6 + 5 files changed, 149 insertions(+) create mode 100644 target/linux/ramips/dts/mt7620n_snr_cpe-w4n-mt.dts diff --git a/package/boot/uboot-envtools/files/ramips b/package/boot/uboot-envtools/files/ramips index 62c0908a6fe..9cb47cd774d 100644 --- a/package/boot/uboot-envtools/files/ramips +++ b/package/boot/uboot-envtools/files/ramips @@ -79,6 +79,11 @@ linksys,ea8100-v2|\ mts,wg430223) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x1000" "0x20000" ;; +snr,cpe-w4n-mt) + idx="$(find_mtd_index uboot-env)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x1000" "0x1000" + ;; xiaomi,mi-router-3g-v2|\ xiaomi,mi-router-4a-gigabit|\ xiaomi,miwifi-3c) diff --git a/target/linux/ramips/dts/mt7620n_snr_cpe-w4n-mt.dts b/target/linux/ramips/dts/mt7620n_snr_cpe-w4n-mt.dts new file mode 100644 index 00000000000..97e60436429 --- /dev/null +++ b/target/linux/ramips/dts/mt7620n_snr_cpe-w4n-mt.dts @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7620n.dtsi" + +#include +#include +#include + +/ { + compatible = "snr,cpe-w4n-mt", "ralink,mt7620n-soc"; + model = "SNR-CPE-W4N (rev.M)"; + + aliases { + led-boot = &led_wps; + led-failsafe = &led_sys; + led-running = &led_wps; + led-upgrade = &led_sys; + }; + + leds { + compatible = "gpio-leds"; + + led_sys: sys { + label = "green:sys"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; // GPIO#38 + panic-indicator; + }; + + led_wps: wps { + label = "green:wps"; + color = ; + function = LED_FUNCTION_WPS; + gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; // GPIO#39 + }; + + wlan { + label = "green:wlan"; + color = ; + function = LED_FUNCTION_WLAN; + gpios = <&gpio3 0 GPIO_ACTIVE_LOW>; // GPIO#72 + }; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "Reset Button"; + gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>; // GPIO#1 + linux,code = ; + }; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <48000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "Bootloader"; + reg = <0x0 0x30000>; + read-only; + }; + + partition@30000 { + label = "Config"; + reg = <0x30000 0x10000>; + }; + + factory: partition@40000 { + label = "Factory"; + reg = <0x40000 0x100000>; + read-only; + }; + + partition@50000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x50000 0x7b0000>; + }; + + partition@30000_1 { + label = "uboot-env"; + reg = <0x30000 0x1000>; + }; + }; + }; +}; + +&state_default { + gpio { + groups = "i2c", "spi refclk"; + function = "gpio"; + }; +}; + +&wmac { + ralink,mtd-eeprom = <&factory 0x0>; +}; diff --git a/target/linux/ramips/image/mt7620.mk b/target/linux/ramips/image/mt7620.mk index 7fff06665ef..445d3dec342 100644 --- a/target/linux/ramips/image/mt7620.mk +++ b/target/linux/ramips/image/mt7620.mk @@ -1104,6 +1104,17 @@ define Device/sitecom_wlr-4100-v1-002 endef TARGET_DEVICES += sitecom_wlr-4100-v1-002 +define Device/snr_cpe-w4n-mt + $(Device/uimage-lzma-loader) + SOC := mt7620n + IMAGE_SIZE := 7360k + DEVICE_VENDOR := SNR + DEVICE_MODEL := CPE-W4N + DEVICE_VARIANT := MT + UIMAGE_NAME := SNR-CPE-W4N-MT +endef +TARGET_DEVICES += snr_cpe-w4n-mt + define Device/tplink_archer-c20i $(Device/tplink-v2) SOC := mt7620a diff --git a/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds b/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds index d52beefdccf..36e3045a38a 100644 --- a/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds +++ b/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds @@ -151,6 +151,11 @@ kingston,mlwg2|\ sanlinking,d240) ucidef_set_led_netdev "wifi_led" "wifi" "blue:wifi" "wlan0" ;; +snr,cpe-w4n-mt) + ucidef_set_led_heartbeat "wps" "WPS" "green:wps" + ucidef_set_led_timer "sys" "System" "green:sys" "500" "500" + ucidef_set_led_wlan "wlan" "Wi-Fi" "green:wlan" "phy0tpt" + ;; lenovo,newifi-y1) ucidef_set_led_netdev "wifi" "WIFI" "blue:wifi" "wlan1" ucidef_set_led_netdev "wifi5g" "WIFI5G" "blue:wifi5g" "wlan0" diff --git a/target/linux/ramips/mt7620/base-files/etc/board.d/02_network b/target/linux/ramips/mt7620/base-files/etc/board.d/02_network index 13f9453442c..424e3709969 100644 --- a/target/linux/ramips/mt7620/base-files/etc/board.d/02_network +++ b/target/linux/ramips/mt7620/base-files/etc/board.d/02_network @@ -86,6 +86,7 @@ ramips_setup_interfaces() glinet,gl-mt300n|\ glinet,gl-mt750|\ hiwifi,hc5661|\ + snr,cpe-w4n-mt|\ wrtnode,wrtnode|\ zbtlink,zbt-wa05|\ zyxel,keenetic-omni-ii) @@ -381,6 +382,11 @@ ramips_setup_macs() linksys,e1700) wan_mac=$(mtd_get_mac_ascii config WAN_MAC_ADDR) ;; + snr,cpe-w4n-mt) + lan_mac=$(mtd_get_mac_binary Factory 0x28) + wan_mac=$(mtd_get_mac_binary Factory 0x2e) + label_mac=$wan_mac + ;; tplink,archer-c2-v1|\ tplink,archer-c20-v1|\ tplink,archer-c20i|\ From 47df168dd279d52127f6bbc623e79bdeeb6c8fd4 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 14 Dec 2022 11:39:17 +0100 Subject: [PATCH 092/110] image-commands.mk: Be consistent in command invocation Most/all other tools use the staging dir prefix, gzip should as well. Signed-off-by: Olliver Schinagl Acked-by: Christian Marangi --- include/image-commands.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/image-commands.mk b/include/image-commands.mk index 41e1c969485..074e40e4e83 100644 --- a/include/image-commands.mk +++ b/include/image-commands.mk @@ -307,7 +307,7 @@ define Build/fit endef define Build/gzip - gzip -f -9n -c $@ $(1) > $@.new + $(STAGING_DIR_HOST)/bin/gzip -f -9n -c $@ $(1) > $@.new @mv $@.new $@ endef From eb7ffeafbfa78235b05abb4ae479376150d7a814 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Sat, 17 Dec 2022 16:39:00 +0100 Subject: [PATCH 093/110] rules: fix broken commitcount on alpine system To generate commitcount we use grep --max-count. This is not present on alpine grep and cause wrong generation. Use -m as it's just the short version of --max-count and more portable. Fixes: #11200 Signed-off-by: Christian Marangi --- rules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules.mk b/rules.mk index 2de43d490d6..e17852e0217 100644 --- a/rules.mk +++ b/rules.mk @@ -409,7 +409,7 @@ $(shell \ if git log -1 >/dev/null 2>/dev/null; then \ if [ -n "$(1)" ]; then \ last_bump="$$(git log --pretty=format:'%h %s' . | \ - grep --max-count=1 -e ': [uU]pdate to ' -e ': [bB]ump to ' | \ + grep -m 1 -e ': [uU]pdate to ' -e ': [bB]ump to ' | \ cut -f 1 -d ' ')"; \ fi; \ if [ -n "$$last_bump" ]; then \ From 8ed53e0928b9453e5c65311f6281475fb24df51f Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Tue, 13 Dec 2022 20:17:58 +0100 Subject: [PATCH 094/110] iproute2: update to 6.1.0 Announcement: https://lore.kernel.org/netdev/20221214094130.7b11ec2e@hermes.local/T/#t Refresh patch: - 170-ip_tiny.patch Signed-off-by: Nick Hainke --- package/network/utils/iproute2/Makefile | 4 ++-- package/network/utils/iproute2/patches/170-ip_tiny.patch | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package/network/utils/iproute2/Makefile b/package/network/utils/iproute2/Makefile index 6ef0b1a96ac..39c94bd6e20 100644 --- a/package/network/utils/iproute2/Makefile +++ b/package/network/utils/iproute2/Makefile @@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=iproute2 -PKG_VERSION:=6.0.0 +PKG_VERSION:=6.1.0 PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=@KERNEL/linux/utils/net/iproute2 -PKG_HASH:=523139e9e72aec996374fa2de74be4c53d2dd05589488934d21ff97bae19580a +PKG_HASH:=5ce12a0fec6b212725ef218735941b2dab76244db7e72646a76021b0537b43ab PKG_BUILD_PARALLEL:=1 PKG_BUILD_DEPENDS:=iptables PKG_LICENSE:=GPL-2.0 diff --git a/package/network/utils/iproute2/patches/170-ip_tiny.patch b/package/network/utils/iproute2/patches/170-ip_tiny.patch index 9e95bcc2d62..eef5e38229b 100644 --- a/package/network/utils/iproute2/patches/170-ip_tiny.patch +++ b/package/network/utils/iproute2/patches/170-ip_tiny.patch @@ -25,7 +25,7 @@ sed -n '/'$$s'[^ ]* =/{s:.* \([^ ]*'$$s'[^ ]*\) .*:extern char \1[] __attribute__((weak)); if (!strcmp(sym, "\1")) return \1;:;p}' $$files ; \ --- a/ip/ip.c +++ b/ip/ip.c -@@ -64,11 +64,17 @@ static void usage(void) +@@ -65,11 +65,17 @@ static void usage(void) fprintf(stderr, "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" " ip [ -force ] -batch filename\n" @@ -43,7 +43,7 @@ " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -h[uman-readable] | -iec | -j[son] | -p[retty] |\n" " -f[amily] { inet | inet6 | mpls | bridge | link } |\n" -@@ -91,37 +97,49 @@ static const struct cmd { +@@ -92,37 +98,49 @@ static const struct cmd { int (*func)(int argc, char **argv); } cmds[] = { { "address", do_ipaddr }, From ee0cade0141beb0e24c9734a4305161d570a59b2 Mon Sep 17 00:00:00 2001 From: Linhui Liu Date: Thu, 15 Dec 2022 15:29:38 +0800 Subject: [PATCH 095/110] tools/libressl: update to 3.7.0 Release notes: https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.6.0-relnotes.txt https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.6.1-relnotes.txt https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.7.0-relnotes.txt Signed-off-by: Linhui Liu --- tools/libressl/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/libressl/Makefile b/tools/libressl/Makefile index fe73e7cde23..f687f6b413d 100644 --- a/tools/libressl/Makefile +++ b/tools/libressl/Makefile @@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=libressl -PKG_VERSION:=3.5.3 -PKG_HASH:=3ab5e5eaef69ce20c6b170ee64d785b42235f48f2e62b095fca5d7b6672b8b28 +PKG_VERSION:=3.7.0 +PKG_HASH:=3fc1290f4007ec75f6e9acecbb25512630d1b9ab8c53ba79844e395868c3e006 PKG_RELEASE:=1 PKG_CPE_ID:=cpe:/a:openbsd:libressl From 8be6350f6646b37929f40a9a482343f22af2065b Mon Sep 17 00:00:00 2001 From: "Johnny S. Lee" Date: Thu, 15 Dec 2022 18:57:31 +0800 Subject: [PATCH 096/110] generic: 5.15: allow MV88E6xxx built-in when PTP support disabled As described in commit 97c77fff28cf001399f33c7bc1ec6687ba18450b MV88E6xxx driver (NET_DSA_MV88E6XXX) cannot be built-in when PTP (PTP_1588_CLOCK) is a module in Linux 5.15. But actually it should be allowed to be built-in when its PTP support (NET_DSA_MV88E6XXX_PTP) is disabled. This adds a patch to fix that. Signed-off-by: Johnny S. Lee --- ...v88e6xxx-depend-on-PTP-conditionally.patch | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 target/linux/generic/hack-5.15/290-net-dsa-mv88e6xxx-depend-on-PTP-conditionally.patch diff --git a/target/linux/generic/hack-5.15/290-net-dsa-mv88e6xxx-depend-on-PTP-conditionally.patch b/target/linux/generic/hack-5.15/290-net-dsa-mv88e6xxx-depend-on-PTP-conditionally.patch new file mode 100644 index 00000000000..86b03d156ec --- /dev/null +++ b/target/linux/generic/hack-5.15/290-net-dsa-mv88e6xxx-depend-on-PTP-conditionally.patch @@ -0,0 +1,44 @@ +From e6866ed4219b8c7754dcd3eb1a654f6f524b0e56 Mon Sep 17 00:00:00 2001 +From: "Johnny S. Lee" +Date: Thu, 15 Dec 2022 17:49:04 +0800 +Subject: [PATCH] net: dsa: mv88e6xxx: depend on PTP conditionally + +PTP hardware timestamping related objects are not linked when PTP +support for MV88E6xxx (NET_DSA_MV88E6XXX_PTP) is disabled, therefore +NET_DSA_MV88E6XXX should not depend on PTP_1588_CLOCK_OPTIONAL +regardless of NET_DSA_MV88E6XXX_PTP. + +Instead, condition more strictly on how NET_DSA_MV88E6XXX_PTP's +dependencies are met, making sure that it cannot be enabled when +NET_DSA_MV88E6XXX=y and PTP_1588_CLOCK=m. + +In other words, this commit allows NET_DSA_MV88E6XXX to be built-in +while PTP_1588_CLOCK is a module, as long as NET_DSA_MV88E6XXX_PTP is +prevented from being enabled. + +Fixes: e5f31552674e ("ethernet: fix PTP_1588_CLOCK dependencies") +Signed-off-by: Johnny S. Lee +--- + drivers/net/dsa/mv88e6xxx/Kconfig | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/mv88e6xxx/Kconfig ++++ b/drivers/net/dsa/mv88e6xxx/Kconfig +@@ -2,7 +2,6 @@ + config NET_DSA_MV88E6XXX + tristate "Marvell 88E6xxx Ethernet switch fabric support" + depends on NET_DSA +- depends on PTP_1588_CLOCK_OPTIONAL + select IRQ_DOMAIN + select NET_DSA_TAG_EDSA + select NET_DSA_TAG_DSA +@@ -13,7 +12,8 @@ config NET_DSA_MV88E6XXX + config NET_DSA_MV88E6XXX_PTP + bool "PTP support for Marvell 88E6xxx" + default n +- depends on NET_DSA_MV88E6XXX && PTP_1588_CLOCK ++ depends on (NET_DSA_MV88E6XXX = y && PTP_1588_CLOCK = y) || \ ++ (NET_DSA_MV88E6XXX = m && PTP_1588_CLOCK) + help + Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch + chips that support it. From b893aa7992087c105ea205088f8d9787200ed6f8 Mon Sep 17 00:00:00 2001 From: Julien Dusser Date: Sun, 20 Mar 2022 10:11:15 +0100 Subject: [PATCH 097/110] ramips: add support for Linksys E5400 and clones Linksys E5400 is a 2.4/5GHz dual band AC router, based on MediaTek MT7628AN. This device is also sold as Linksys E2500v4, E5300 and E5350 with the same hardware, but software speed limitations in vendor firmwares. Specification: * SoC: MT7628AN (580 MHz) * RAM: DDR2 64 MiB * Flash: 16 MiB NOR (W25Q128BV) * Wi-Fi: * 2.4GHz: SoC Built-in * 5GHz: MT7612EN * Ethernet: 5x 100Mbps * Switch: SoC built-in * UART: * 115200, 8N1, 3.3V (real u-boot speed: 119050) * Pinout JB4: 1:[3V3] (TXD) (RXD) [NC] (GND) Flash Layout: 0x0000000-0x0030000 : "Bootloader" 0x0030000-0x0040000 : "Config" 0x0040000-0x0050000 : "Factory" 0x0050000-0x0ff0000 : "Kernel" 0x0ff0000-0x1000000 : "CBTinfo" MAC address: LAN: factory 0x28 WAN: LAN + 1 2.4G: LAN + 2 5G: LAN + 3 Installation: 1. Connect to UART, use LF as EOL (not CRLF) 2. Set IP address - Press 4 during boot - setenv serverip - setenv ipaddr - setenv bootfile openwrt-ramips-mt76x8-linksys_e5400-initramfs-kernel.bin - saveenv - reset 3. Load Openwrt Kernel image from TFTP: - Press 1 during boot - IP and filename should be already set 4. Boot into OpenWrt and perform sysupgrade with sysupgrade image. Signed-off-by: Julien Dusser --- .../ramips/dts/mt7628an_linksys_e5400.dts | 163 ++++++++++++++++++ target/linux/ramips/image/mt76x8.mk | 15 ++ .../mt76x8/base-files/etc/board.d/02_network | 7 + 3 files changed, 185 insertions(+) create mode 100644 target/linux/ramips/dts/mt7628an_linksys_e5400.dts diff --git a/target/linux/ramips/dts/mt7628an_linksys_e5400.dts b/target/linux/ramips/dts/mt7628an_linksys_e5400.dts new file mode 100644 index 00000000000..3a6403fe7f8 --- /dev/null +++ b/target/linux/ramips/dts/mt7628an_linksys_e5400.dts @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7628an.dtsi" + +#include +#include + +/ { + compatible = "linksys,e5400", "mediatek,mt7628an-soc"; + model = "Linksys E5400"; + + aliases { + led-boot = &led_wps; + led-failsafe = &led_wps; + led-running = &led_wps; + led-upgrade = &led_wps; + label-mac-device = ðernet; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x4000000>; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio 11 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&gpio 38 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_wps: wps { + label = "green:wps"; + gpios = <&gpio 37 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <40000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x30000>; + read-only; + }; + + partition@30000 { + label = "u-boot-env"; + reg = <0x30000 0x10000>; + read-only; + }; + + factory: partition@40000 { + label = "factory"; + reg = <0x40000 0x10000>; + read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_factory_28: macaddr@28 { + reg = <0x28 0x6>; + }; + }; + + partition@50000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x50000 0xfa0000>; + }; + + partition@ff0000 { + label = "cbtinfo"; + reg = <0xff0000 0x10000>; + read-only; + }; + }; + }; +}; + +&ehci { + status = "disabled"; +}; + +&esw { + mediatek,portmap = <0x2f>; + mediatek,portdisable = <0x20>; +}; + +ðernet { + nvmem-cells = <&macaddr_factory_28>; + nvmem-cell-names = "mac-address"; +}; + +&ohci { + status = "disabled"; +}; + +&pcie { + status = "okay"; +}; + +&pcie0 { + wifi5: wifi@0,0 { + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + mediatek,mtd-eeprom = <&factory 0x8000>; + ieee80211-freq-limit = <5000000 6000000>; + + nvmem-cells = <&macaddr_factory_28>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <3>; + }; +}; + +&state_default { + gpio { + group = "gpio", "i2c", "i2s", "refclk", "uart1", "wdt", "wled_an"; + function = "gpio"; + }; +}; + +&usbphy { + status = "disabled"; +}; + +&wmac { + status = "okay"; + + mediatek,mtd-eeprom = <&factory 0x0>; + nvmem-cells = <&macaddr_factory_28>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <2>; +}; + diff --git a/target/linux/ramips/image/mt76x8.mk b/target/linux/ramips/image/mt76x8.mk index 1b7e786b52b..a77f1c97e1e 100644 --- a/target/linux/ramips/image/mt76x8.mk +++ b/target/linux/ramips/image/mt76x8.mk @@ -342,6 +342,21 @@ define Device/kroks_kndrt31r19 endef TARGET_DEVICES += kroks_kndrt31r19 +define Device/linksys_e5400 + IMAGE_SIZE := 16000k + DEVICE_VENDOR := Linksys + DEVICE_MODEL := E5400 + DEVICE_ALT0_VENDOR := Linksys + DEVICE_ALT0_MODEL := E2500 + DEVICE_ALT0_VARIANT := v4 + DEVICE_ALT1_VENDOR := Linksys + DEVICE_ALT1_MODEL := E5300 + DEVICE_ALT2_VENDOR := Linksys + DEVICE_ALT2_MODEL := E5350 + DEVICE_PACKAGES := kmod-mt76x2 +endef +TARGET_DEVICES += linksys_e5400 + define Device/mediatek_linkit-smart-7688 IMAGE_SIZE := 32448k DEVICE_VENDOR := MediaTek diff --git a/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network b/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network index d85cdf3db7f..26415e0fa65 100644 --- a/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network +++ b/target/linux/ramips/mt76x8/base-files/etc/board.d/02_network @@ -125,6 +125,10 @@ ramips_setup_interfaces() ucidef_set_interface_lan "eth0" ucidef_set_interface "wan" device "/dev/cdc-wdm0" protocol "qmi" ;; + linksys,e5400) + ucidef_add_switch "switch0" \ + "0:lan:1" "1:lan:2" "2:lan:3" "3:lan:4" "4:wan" "6@eth0" + ;; motorola,mwr03) ucidef_add_switch "switch0" \ "1:lan" "2:lan" "3:lan" "0:wan" "6@eth0" @@ -248,6 +252,9 @@ ramips_setup_macs() totolink,lr1200) wan_mac=$(mtd_get_mac_binary factory 0x2e) ;; + linksys,e5400) + wan_mac=$(mtd_get_mac_binary factory 0x22) + ;; mercury,mac1200r-v2) wan_mac=$(macaddr_add "$(mtd_get_mac_binary factory_info 0xd)" 1) ;; From 963c77158c61950c46f52de0f87c90cae21f6652 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sun, 18 Dec 2022 03:09:22 +0100 Subject: [PATCH 098/110] tools/sed: Fix handling of symlinks of 128 chars If the absolute path a symlink is pointing to is 128 bytes long sed failed with an error message like this: "/sedstbU8O: Not a directory" This fixes a problem building python seen in the build bot. This patch is on its way into upstream sed. Signed-off-by: Hauke Mehrtens --- ...-of-symlinks-pointing-to-path-with-1.patch | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tools/sed/patches/001-sed-fix-handling-of-symlinks-pointing-to-path-with-1.patch diff --git a/tools/sed/patches/001-sed-fix-handling-of-symlinks-pointing-to-path-with-1.patch b/tools/sed/patches/001-sed-fix-handling-of-symlinks-pointing-to-path-with-1.patch new file mode 100644 index 00000000000..96ae5bdbb98 --- /dev/null +++ b/tools/sed/patches/001-sed-fix-handling-of-symlinks-pointing-to-path-with-1.patch @@ -0,0 +1,47 @@ +From 8f600f2df293d539e9e9137f6f82faa1633b97c1 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 17 Dec 2022 20:56:29 -0800 +Subject: [PATCH] sed: fix symlink bufsize readlink check + +Problem reported by Hauke Mehrtens. +* sed/utils.c (follow_symlink): Fix typo when checking size of +second and later symlink, when that symlink is so large that it +does not fit into the buffer. Although the bug is not a buffer +overflow, it does cause sed to mishandle the symlink. +* testsuite/follow-symlinks.sh: Test for the bug. +--- + sed/utils.c | 2 +- + testsuite/follow-symlinks.sh | 13 +++++++++++++ + 3 files changed, 18 insertions(+), 1 deletion(-) + +--- a/sed/utils.c ++++ b/sed/utils.c +@@ -345,7 +345,7 @@ follow_symlink (const char *fname) + while ((linklen = (buf_used < buf_size + ? readlink (fn, buf + buf_used, buf_size - buf_used) + : 0)) +- == buf_size) ++ == buf_size - buf_used) + { + buf = xpalloc (buf, &buf_size, 1, SSIZE_IDX_MAX, 1); + if (num_links) +--- a/testsuite/follow-symlinks.sh ++++ b/testsuite/follow-symlinks.sh +@@ -73,4 +73,17 @@ compare_ exp-la-abs out-la-abs || fail=1 + ln -s la-loop la-loop || framework_failure_ + sed --follow-symlinks -i s/a/b/ la-loop && fail=1 + ++# symlink of length 128 ++long=d/ ++for i in 2 3 4 5 6 7; do ++ long=$long$long ++done ++dir=${long%/d/} ++file=$dir/xx ++mkdir -p $dir && ++echo x >$file && ++ln -s $file yy && ++ln -s yy xx || framework_failure_ ++sed -i --follow-symlinks s/x/y/ xx || fail=1 ++ + Exit $fail From 1e0709ea39e888510a847ce626cd871752b17aab Mon Sep 17 00:00:00 2001 From: Linhui Liu Date: Fri, 16 Dec 2022 14:40:49 +0800 Subject: [PATCH 099/110] tools/patchelf: update to 0.17.0 Update to the latest released version. Signed-off-by: Linhui Liu --- tools/patchelf/Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/patchelf/Makefile b/tools/patchelf/Makefile index 4cf039c4072..1271a393c0d 100644 --- a/tools/patchelf/Makefile +++ b/tools/patchelf/Makefile @@ -7,12 +7,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=patchelf +PKG_VERSION:=0.17.0 -PKG_SOURCE_PROTO:=git -PKG_SOURCE_URL:=https://github.com/NixOS/patchelf.git -PKG_SOURCE_VERSION:=f34751b88bd07d7f44f5cd3200fb4122bf916c7e -PKG_SOURCE_DATE:=2020-12-07 -PKG_MIRROR_HASH:=ac746930b919b97da40f259cfc9ab7bbd48a0c9cbf2eebd8cee5ae19a94356fd +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 +PKG_SOURCE_URL:=https://github.com/NixOS/patchelf/releases/download/$(PKG_VERSION) +PKG_HASH:=45d76f4a31688a523718ec512f31650b1f35d1affec3eafeb3feeb5448d341e1 HOST_BUILD_PARALLEL:=1 HOST_FIXUP:=autoreconf From 5968290a21e0b5d191234e07928214b0f0872bbe Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 21 Dec 2021 13:47:03 +0100 Subject: [PATCH 100/110] kernel: package QRTR support QRTR is Qualcomm IPC router protocol and ath11k requires it for both AHB and PCI support, so package it as a kernel module so it can be added as a dependency to the ath11k module. Only kernel 5.15 is currently supported due to various things missing in 5.10 whose backporting is out of scope for this patch. SMD, TUN and MHI variants are packaged. SMD variant depends on the ipq807x target as it has dependency on the RPMSG drivers which are Qualcomm and SoC specific anyway. Signed-off-by: Robert Marko --- package/kernel/linux/modules/netsupport.mk | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/package/kernel/linux/modules/netsupport.mk b/package/kernel/linux/modules/netsupport.mk index a8c343f44b1..22c39e86189 100644 --- a/package/kernel/linux/modules/netsupport.mk +++ b/package/kernel/linux/modules/netsupport.mk @@ -1438,3 +1438,67 @@ define KernelPackage/netconsole/description endef $(eval $(call KernelPackage,netconsole)) + + +define KernelPackage/qrtr + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=Qualcomm IPC Router support + HIDDEN:=1 + DEPENDS:=@!LINUX_5_10 + KCONFIG:=CONFIG_QRTR + FILES:= \ + $(LINUX_DIR)/net/qrtr/qrtr.ko \ + $(LINUX_DIR)/net/qrtr/ns.ko + AUTOLOAD:=$(call AutoProbe,qrtr) +endef + +define KernelPackage/qrtr/description + Qualcomm IPC Router support +endef + +$(eval $(call KernelPackage,qrtr)) + +define KernelPackage/qrtr-tun + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=TUN device for Qualcomm IPC Router + DEPENDS:=+kmod-qrtr + KCONFIG:=CONFIG_QRTR_TUN + FILES:= $(LINUX_DIR)/net/qrtr/qrtr-tun.ko + AUTOLOAD:=$(call AutoProbe,qrtr-tun) +endef + +define KernelPackage/qrtr-tun/description + TUN device for Qualcomm IPC Router +endef + +$(eval $(call KernelPackage,qrtr-tun)) + +define KernelPackage/qrtr-smd + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=SMD IPC Router channels + DEPENDS:=+kmod-qrtr @TARGET_ipq807x + KCONFIG:=CONFIG_QRTR_SMD + FILES:= $(LINUX_DIR)/net/qrtr/qrtr-smd.ko + AUTOLOAD:=$(call AutoProbe,qrtr-smd) +endef + +define KernelPackage/qrtr-smd/description + SMD IPC Router channels +endef + +$(eval $(call KernelPackage,qrtr-smd)) + +define KernelPackage/qrtr-mhi + SUBMENU:=$(NETWORK_SUPPORT_MENU) + TITLE:=MHI IPC Router channels + DEPENDS:=+kmod-mhi-bus +kmod-qrtr + KCONFIG:=CONFIG_QRTR_MHI + FILES:= $(LINUX_DIR)/net/qrtr/qrtr-mhi.ko + AUTOLOAD:=$(call AutoProbe,qrtr-mhi) +endef + +define KernelPackage/qrtr-mhi/description + MHI IPC Router channels +endef + +$(eval $(call KernelPackage,qrtr-mhi)) From b4d3694f81f423089ac5cc8d8e7b6af62428da3a Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 13 Dec 2022 23:01:20 +0100 Subject: [PATCH 101/110] linux-firmware: package ath11k consumer cards firmware Package firmware for ath11k supported QCA consumer cards from linux-firmware. Signed-off-by: Robert Marko --- package/firmware/linux-firmware/Makefile | 2 +- package/firmware/linux-firmware/qca_ath11k.mk | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 package/firmware/linux-firmware/qca_ath11k.mk diff --git a/package/firmware/linux-firmware/Makefile b/package/firmware/linux-firmware/Makefile index 4a665ad7242..69e3a948ad5 100644 --- a/package/firmware/linux-firmware/Makefile +++ b/package/firmware/linux-firmware/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=linux-firmware PKG_VERSION:=20221109 -PKG_RELEASE:=4 +PKG_RELEASE:=5 PKG_SOURCE_URL:=@KERNEL/linux/kernel/firmware PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz diff --git a/package/firmware/linux-firmware/qca_ath11k.mk b/package/firmware/linux-firmware/qca_ath11k.mk new file mode 100644 index 00000000000..3e997bc3f4a --- /dev/null +++ b/package/firmware/linux-firmware/qca_ath11k.mk @@ -0,0 +1,23 @@ +Package/ath11k-firmware-qca6390 = $(call Package/firmware-default,QCA6390 ath11k firmware) +define Package/ath11k-firmware-qca6390/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/QCA6390/hw2.0 + $(INSTALL_DATA) \ + $(PKG_BUILD_DIR)/ath11k/QCA6390/hw2.0/* $(1)/lib/firmware/ath11k/QCA6390/hw2.0/ +endef +$(eval $(call BuildPackage,ath11k-firmware-qca6390)) + +Package/ath11k-firmware-wcn6750 = $(call Package/firmware-default,WCN6750 ath11k firmware) +define Package/ath11k-firmware-wcn6750/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/WCN6750/hw1.0 + $(INSTALL_DATA) \ + $(PKG_BUILD_DIR)/ath11k/WCN6750/hw1.0/* $(1)/lib/firmware/ath11k/WCN6750/hw1.0/ +endef +$(eval $(call BuildPackage,ath11k-firmware-wcn6750)) + +Package/ath11k-firmware-wcn6855 = $(call Package/firmware-default,WCN6855 ath11k firmware) +define Package/ath11k-firmware-wcn6855/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/WCN6855/hw2.0 + $(INSTALL_DATA) \ + $(PKG_BUILD_DIR)/ath11k/WCN6855/hw2.0/* $(1)/lib/firmware/ath11k/WCN6855/hw2.0/ +endef +$(eval $(call BuildPackage,ath11k-firmware-wcn6855)) From 93ae4353cdf651ef0b5d8a4d40a6c29d8d3f655b Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Sun, 6 Feb 2022 15:50:24 +0100 Subject: [PATCH 102/110] mac80211: add ath11k PCI support ath11k is the upstream driver for Qualcomm 802.11ax radios, both for the internal AHB and PCI based cards. This commit does however only provide PCI support while AHB will follow but its SoC specific so it will require an OpenWrt target first. It differs a bit from ath10k as it requires stuff like QRTR, MHI and QMI helpers. PCI variant requires qrtr-mhi and mhi-bus which backports do provide, however we are dropping those in a patch as they will conflict with support for the AHB variant as that one requires qrtr-smd which in turn requires RPMSG and GLINK and its not feasable to provide those in backports as they are really SoC specific. QRTR and MHI in kernel 5.10 are not usable and backporting the changes is not easy as they have changed drastically from 5.10 to 5.15 ath11k will only be available on targets that use kernel 5.15. Signed-off-by: Robert Marko --- package/kernel/mac80211/Makefile | 3 +- package/kernel/mac80211/ath.mk | 49 ++- ...-tx-queues-immediately-upon-firmware.patch | 78 ++++ ...-ath11k-Don-t-exit-on-wakeup-failure.patch | 45 +++ ...warning-in-dma_free_coherent-of-memo.patch | 139 +++++++ ...-Fix-spelling-mistake-chnange-change.patch | 25 ++ ...firmware-assert-during-bandwidth-cha.patch | 225 ++++++++++++ ...-ath11k-suppress-add-interface-error.patch | 52 +++ ...support-to-configure-channel-dwell-t.patch | 102 ++++++ ...-PME-message-during-wakeup-from-D3co.patch | 39 ++ ...firmware-crash-on-vdev-delete-race-c.patch | 116 ++++++ ...monitor-vdev-creation-with-firmware-.patch | 40 ++ ...qmi_msg_handler-data-structure-initi.patch | 33 ++ ...hronize-ath11k_mac_he_gi_to_nl80211_.patch | 42 +++ ...-ath11k-Make-QMI-message-rules-const.patch | 341 ++++++++++++++++++ ...ger-sta-disconnect-on-hardware-resta.patch | 119 ++++++ ...race-condition-with-struct-htt_ppdu_.patch | 103 ++++++ ...k-control-thermal-support-via-symbol.patch | 66 ++++ ...ci-fix-compilation-in-5.16-and-older.patch | 29 ++ .../100-backports-drop-QRTR-and-MHI.patch | 76 ++++ 20 files changed, 1718 insertions(+), 4 deletions(-) create mode 100644 package/kernel/mac80211/patches/ath11k/0001-wifi-ath11k-stop-tx-queues-immediately-upon-firmware.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0002-wifi-ath11k-Don-t-exit-on-wakeup-failure.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0003-wifi-ath11k-fix-warning-in-dma_free_coherent-of-memo.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0005-wifi-ath11k-Fix-spelling-mistake-chnange-change.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0006-wifi-ath11k-fix-firmware-assert-during-bandwidth-cha.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0007-wifi-ath11k-suppress-add-interface-error.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0008-wifi-ath11k-add-support-to-configure-channel-dwell-t.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0009-wifi-ath11k-Send-PME-message-during-wakeup-from-D3co.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0010-wifi-ath11k-Fix-firmware-crash-on-vdev-delete-race-c.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0011-wifi-ath11k-fix-monitor-vdev-creation-with-firmware-.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0012-wifi-ath11k-Fix-qmi_msg_handler-data-structure-initi.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0013-wifi-ath11k-synchronize-ath11k_mac_he_gi_to_nl80211_.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0016-wifi-ath11k-Make-QMI-message-rules-const.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0017-wifi-ath11k-Trigger-sta-disconnect-on-hardware-resta.patch create mode 100644 package/kernel/mac80211/patches/ath11k/0018-wifi-ath11k-Fix-race-condition-with-struct-htt_ppdu_.patch create mode 100644 package/kernel/mac80211/patches/ath11k/900-ath11k-control-thermal-support-via-symbol.patch create mode 100644 package/kernel/mac80211/patches/ath11k/901-wifi-ath11k-pci-fix-compilation-in-5.16-and-older.patch create mode 100644 package/kernel/mac80211/patches/build/100-backports-drop-QRTR-and-MHI.patch diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile index 58f07e1eeb2..1344b9f8e2c 100644 --- a/package/kernel/mac80211/Makefile +++ b/package/kernel/mac80211/Makefile @@ -11,7 +11,7 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=mac80211 PKG_VERSION:=6.1-rc8 -PKG_RELEASE:=1 +PKG_RELEASE:=2 # PKG_SOURCE_URL:=@KERNEL/linux/kernel/projects/backports/stable/v5.15.58/ PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources/ PKG_HASH:=7f3d96c2573183cd79d6a3ebe5e1b7b73c19d1326d443c85b69c4181f14e6e2b @@ -307,6 +307,7 @@ define Build/Prepare $(PKG_BUILD_DIR)/include/linux/crc8.h \ $(PKG_BUILD_DIR)/include/linux/eeprom_93cx6.h \ $(PKG_BUILD_DIR)/include/linux/wl12xx.h \ + $(PKG_BUILD_DIR)/include/linux/mhi.h \ $(PKG_BUILD_DIR)/include/net/ieee80211.h \ $(PKG_BUILD_DIR)/backport-include/linux/bcm47xx_nvram.h diff --git a/package/kernel/mac80211/ath.mk b/package/kernel/mac80211/ath.mk index af8c3b93d3c..83228311cc4 100644 --- a/package/kernel/mac80211/ath.mk +++ b/package/kernel/mac80211/ath.mk @@ -1,6 +1,6 @@ PKG_DRIVERS += \ ath ath5k ath6kl ath6kl-sdio ath6kl-usb ath9k ath9k-common ath9k-htc ath10k ath10k-smallbuffers \ - carl9170 owl-loader ar5523 wil6210 + ath11k ath11k-pci carl9170 owl-loader ar5523 wil6210 PKG_CONFIG_DEPENDS += \ CONFIG_PACKAGE_ATH_DEBUG \ @@ -12,6 +12,7 @@ PKG_CONFIG_DEPENDS += \ CONFIG_ATH9K_TX99 \ CONFIG_ATH10K_LEDS \ CONFIG_ATH10K_THERMAL \ + CONFIG_ATH11K_THERMAL \ CONFIG_ATH_USER_REGD ifdef CONFIG_PACKAGE_MAC80211_DEBUGFS @@ -19,6 +20,7 @@ ifdef CONFIG_PACKAGE_MAC80211_DEBUGFS ATH9K_DEBUGFS \ ATH9K_HTC_DEBUGFS \ ATH10K_DEBUGFS \ + ATH11K_DEBUGFS \ CARL9170_DEBUGFS \ ATH5K_DEBUG \ ATH6KL_DEBUG \ @@ -28,6 +30,7 @@ endif ifdef CONFIG_PACKAGE_MAC80211_TRACING config-y += \ ATH10K_TRACING \ + ATH11K_TRACING \ ATH6KL_TRACING \ ATH_TRACEPOINTS \ ATH5K_TRACER \ @@ -35,9 +38,9 @@ ifdef CONFIG_PACKAGE_MAC80211_TRACING endif config-$(call config_package,ath) += ATH_CARDS ATH_COMMON -config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG ATH9K_STATION_STATISTICS +config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG ATH11K_DEBUG ATH9K_STATION_STATISTICS config-$(CONFIG_PACKAGE_ATH_DFS) += ATH9K_DFS_CERTIFIED ATH10K_DFS_CERTIFIED -config-$(CONFIG_PACKAGE_ATH_SPECTRAL) += ATH9K_COMMON_SPECTRAL ATH10K_SPECTRAL +config-$(CONFIG_PACKAGE_ATH_SPECTRAL) += ATH9K_COMMON_SPECTRAL ATH10K_SPECTRAL ATH11K_SPECTRAL config-$(CONFIG_PACKAGE_ATH_DYNACK) += ATH9K_DYNACK config-$(call config_package,ath9k) += ATH9K config-$(call config_package,ath9k-common) += ATH9K_COMMON @@ -52,10 +55,13 @@ config-$(CONFIG_ATH9K_TX99) += ATH9K_TX99 config-$(CONFIG_ATH9K_UBNTHSR) += ATH9K_UBNTHSR config-$(CONFIG_ATH10K_LEDS) += ATH10K_LEDS config-$(CONFIG_ATH10K_THERMAL) += ATH10K_THERMAL +config-$(CONFIG_ATH11K_THERMAL) += ATH11K_THERMAL config-$(call config_package,ath9k-htc) += ATH9K_HTC config-$(call config_package,ath10k) += ATH10K ATH10K_PCI config-$(call config_package,ath10k-smallbuffers) += ATH10K ATH10K_PCI ATH10K_SMALLBUFFERS +config-$(call config_package,ath11k) += ATH11K +config-$(call config_package,ath11k-pci) += ATH11K_PCI config-$(call config_package,ath5k) += ATH5K ifdef CONFIG_TARGET_ath25 @@ -290,6 +296,43 @@ define KernelPackage/ath10k-smallbuffers VARIANT:=smallbuffers endef +define KernelPackage/ath11k + $(call KernelPackage/mac80211/Default) + TITLE:=Qualcomm 802.11ax wireless chipset support (common code) + URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath11k + DEPENDS+= +kmod-ath +@DRIVER_11AC_SUPPORT +@DRIVER_11AX_SUPPORT \ + +kmod-crypto-michael-mic +ATH11K_THERMAL:kmod-hwmon-core +ATH11K_THERMAL:kmod-thermal + FILES:=$(PKG_BUILD_DIR)/drivers/soc/qcom/qmi_helpers.ko \ + $(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k.ko +endef + +define KernelPackage/ath11k/description +This module adds support for Qualcomm Technologies 802.11ax family of +chipsets. +endef + +define KernelPackage/ath11k/config + + config ATH11K_THERMAL + bool "Enable thermal sensors and throttling support" + depends on PACKAGE_kmod-ath11k + +endef + +define KernelPackage/ath11k-pci + $(call KernelPackage/mac80211/Default) + TITLE:=Qualcomm 802.11ax PCI wireless chipset support + URL:=https://wireless.wiki.kernel.org/en/users/drivers/ath11k + DEPENDS+= @PCI_SUPPORT +kmod-qrtr-mhi +kmod-ath11k + FILES:=$(PKG_BUILD_DIR)/drivers/net/wireless/ath/ath11k/ath11k_pci.ko + AUTOLOAD:=$(call AutoProbe,ath11k_pci) +endef + +define KernelPackage/ath11k-pci/description +This module adds support for Qualcomm Technologies 802.11ax family of +chipsets with PCI bus. +endef + define KernelPackage/carl9170 $(call KernelPackage/mac80211/Default) TITLE:=Driver for Atheros AR9170 USB sticks diff --git a/package/kernel/mac80211/patches/ath11k/0001-wifi-ath11k-stop-tx-queues-immediately-upon-firmware.patch b/package/kernel/mac80211/patches/ath11k/0001-wifi-ath11k-stop-tx-queues-immediately-upon-firmware.patch new file mode 100644 index 00000000000..ae8920c62f2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0001-wifi-ath11k-stop-tx-queues-immediately-upon-firmware.patch @@ -0,0 +1,78 @@ +From 81e60b2dfb2744ab6642c4aa62534b4f711fdc5d Mon Sep 17 00:00:00 2001 +From: Aditya Kumar Singh +Date: Tue, 27 Sep 2022 09:18:54 +0300 +Subject: [PATCH] wifi: ath11k: stop tx queues immediately upon firmware exit + +Currently, recovery flag is set immediately upon firmware +exit but tx queues are stopped once firmware arrives back +and is ready which is during ath11k_core_restart. Once +ieee80211 hw restart is completed, tx queues are resumed. +If during the time delta between firmware exit and firmware +ready, mac80211 send packets, currently ath11k will drop it +since recovery flag will be set. But warning prints will +come - + "ath11k c000000.wifi: failed to transmit frame -108" + +If more tx packets are there, this could lead to flooding +of above print. + +However, actually tx queues should be stopped immediately +when firmware leaves. This will prevent packets to get +dropped when firmware is recovering. + +Add fix to stop tx queues immediately after firmware exit. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Aditya Kumar Singh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220923170235.18873-1-quic_adisi@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 5 +---- + drivers/net/wireless/ath/ath11k/core.h | 1 + + drivers/net/wireless/ath/ath11k/qmi.c | 3 +++ + 3 files changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1641,7 +1641,7 @@ static void ath11k_update_11d(struct wor + } + } + +-static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) ++void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) + { + struct ath11k *ar; + struct ath11k_pdev *pdev; +@@ -1730,9 +1730,6 @@ static void ath11k_core_restart(struct w + struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); + int ret; + +- if (!ab->is_reset) +- ath11k_core_pre_reconfigure_recovery(ab); +- + ret = ath11k_core_reconfigure_on_crash(ab); + if (ret) { + ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -1157,6 +1157,7 @@ int ath11k_core_check_smbios(struct ath1 + void ath11k_core_halt(struct ath11k *ar); + int ath11k_core_resume(struct ath11k_base *ab); + int ath11k_core_suspend(struct ath11k_base *ab); ++void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab); + + const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, + const char *filename); +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -3158,6 +3158,9 @@ static void ath11k_qmi_driver_event_work + case ATH11K_QMI_EVENT_SERVER_EXIT: + set_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); + set_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); ++ ++ if (!ab->is_reset) ++ ath11k_core_pre_reconfigure_recovery(ab); + break; + case ATH11K_QMI_EVENT_REQUEST_MEM: + ret = ath11k_qmi_event_mem_request(qmi); diff --git a/package/kernel/mac80211/patches/ath11k/0002-wifi-ath11k-Don-t-exit-on-wakeup-failure.patch b/package/kernel/mac80211/patches/ath11k/0002-wifi-ath11k-Don-t-exit-on-wakeup-failure.patch new file mode 100644 index 00000000000..47385e0458e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0002-wifi-ath11k-Don-t-exit-on-wakeup-failure.patch @@ -0,0 +1,45 @@ +From 45d2e268369b0c768d5a644f319758bcfd370521 Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Wed, 28 Sep 2022 09:51:40 +0800 +Subject: [PATCH] wifi: ath11k: Don't exit on wakeup failure + +Currently, ath11k_pcic_read() returns an error if wakeup() +fails, this makes firmware crash debug quite hard because we can +get nothing. + +Change to go ahead on wakeup failure, in that case we still may +get something valid to check. There should be no mislead due +to incorrect content because we are aware of the failure with the +log printed. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220928015140.5431-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/pcic.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -218,9 +218,16 @@ int ath11k_pcic_read(struct ath11k_base + if (wakeup_required && ab->pci.ops->wakeup) { + ret = ab->pci.ops->wakeup(ab); + if (ret) { +- ath11k_warn(ab, "failed to wakeup for read from 0x%x: %d\n", +- start, ret); +- return ret; ++ ath11k_warn(ab, ++ "wakeup failed, data may be invalid: %d", ++ ret); ++ /* Even though wakeup() failed, continue processing rather ++ * than returning because some parts of the data may still ++ * be valid and useful in some cases, e.g. could give us ++ * some clues on firmware crash. ++ * Mislead due to invalid data could be avoided because we ++ * are aware of the wakeup failure. ++ */ + } + } + diff --git a/package/kernel/mac80211/patches/ath11k/0003-wifi-ath11k-fix-warning-in-dma_free_coherent-of-memo.patch b/package/kernel/mac80211/patches/ath11k/0003-wifi-ath11k-fix-warning-in-dma_free_coherent-of-memo.patch new file mode 100644 index 00000000000..661f4bbfbf1 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0003-wifi-ath11k-fix-warning-in-dma_free_coherent-of-memo.patch @@ -0,0 +1,139 @@ +From f74878433d5ade360447da5d92e9c2e535780d80 Mon Sep 17 00:00:00 2001 +From: Wen Gong +Date: Wed, 28 Sep 2022 03:38:32 -0400 +Subject: [PATCH] wifi: ath11k: fix warning in dma_free_coherent() of memory + chunks while recovery + +Commit 26f3a021b37c ("ath11k: allocate smaller chunks of memory for +firmware") and commit f6f92968e1e5 ("ath11k: qmi: try to allocate a +big block of DMA memory first") change ath11k to allocate the memory +chunks for target twice while wlan load. It fails for the 1st time +because of large memory and then changed to allocate many small chunks +for the 2nd time sometimes as below log. + +1st time failed: +[10411.640620] ath11k_pci 0000:05:00.0: qmi firmware request memory request +[10411.640625] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 6881280 +[10411.640630] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 3784704 +[10411.640658] ath11k_pci 0000:05:00.0: qmi dma allocation failed (6881280 B type 1), will try later with small size +[10411.640671] ath11k_pci 0000:05:00.0: qmi delays mem_request 2 +[10411.640677] ath11k_pci 0000:05:00.0: qmi respond memory request delayed 1 +2nd time success: +[10411.642004] ath11k_pci 0000:05:00.0: qmi firmware request memory request +[10411.642008] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642012] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642014] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642016] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642018] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642020] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642022] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642024] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642027] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642029] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 +[10411.642031] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 458752 +[10411.642033] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 131072 +[10411.642035] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 +[10411.642037] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 +[10411.642039] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 +[10411.642041] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 +[10411.642043] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 +[10411.642045] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 524288 +[10411.642047] ath11k_pci 0000:05:00.0: qmi mem seg type 4 size 491520 +[10411.642049] ath11k_pci 0000:05:00.0: qmi mem seg type 1 size 524288 + +And then commit 5962f370ce41 ("ath11k: Reuse the available memory after +firmware reload") skip the ath11k_qmi_free_resource() which frees the +memory chunks while recovery, after that, when run recovery test on +WCN6855, a warning happened every time as below and finally leads fail +for recovery. + +[ 159.570318] BUG: Bad page state in process kworker/u16:5 pfn:33300 +[ 159.570320] page:0000000096ffdbb9 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x33300 +[ 159.570324] flags: 0xfffffc0000000(node=0|zone=1|lastcpupid=0x1fffff) +[ 159.570329] raw: 000fffffc0000000 0000000000000000 dead000000000122 0000000000000000 +[ 159.570332] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 +[ 159.570334] page dumped because: nonzero _refcount +[ 159.570440] firewire_ohci syscopyarea sysfillrect psmouse sdhci_pci ahci sysimgblt firewire_core fb_sys_fops libahci crc_itu_t cqhci drm sdhci e1000e wmi video +[ 159.570460] CPU: 2 PID: 217 Comm: kworker/u16:5 Kdump: loaded Tainted: G B 5.19.0-rc1-wt-ath+ #3 +[ 159.570465] Hardware name: LENOVO 418065C/418065C, BIOS 83ET63WW (1.33 ) 07/29/2011 +[ 159.570467] Workqueue: qmi_msg_handler qmi_data_ready_work [qmi_helpers] +[ 159.570475] Call Trace: +[ 159.570476] +[ 159.570478] dump_stack_lvl+0x49/0x5f +[ 159.570486] dump_stack+0x10/0x12 +[ 159.570493] bad_page+0xab/0xf0 +[ 159.570502] check_free_page_bad+0x66/0x70 +[ 159.570511] __free_pages_ok+0x530/0x9a0 +[ 159.570517] ? __dev_printk+0x58/0x6b +[ 159.570525] ? _dev_printk+0x56/0x72 +[ 159.570534] ? qmi_decode+0x119/0x470 [qmi_helpers] +[ 159.570543] __free_pages+0x91/0xd0 +[ 159.570548] dma_free_contiguous+0x50/0x60 +[ 159.570556] dma_direct_free+0xe5/0x140 +[ 159.570564] dma_free_attrs+0x35/0x50 +[ 159.570570] ath11k_qmi_msg_mem_request_cb+0x2ae/0x3c0 [ath11k] +[ 159.570620] qmi_invoke_handler+0xac/0xe0 [qmi_helpers] +[ 159.570630] qmi_handle_message+0x6d/0x180 [qmi_helpers] +[ 159.570643] qmi_data_ready_work+0x2ca/0x440 [qmi_helpers] +[ 159.570656] process_one_work+0x227/0x440 +[ 159.570667] worker_thread+0x31/0x3d0 +[ 159.570676] ? process_one_work+0x440/0x440 +[ 159.570685] kthread+0xfe/0x130 +[ 159.570692] ? kthread_complete_and_exit+0x20/0x20 +[ 159.570701] ret_from_fork+0x22/0x30 +[ 159.570712] + +The reason is because when wlan start to recovery, the type, size and +count is not same for the 1st and 2nd QMI_WLFW_REQUEST_MEM_IND message, +Then it leads the parameter size is not correct for the dma_free_coherent(). +For the chunk[1], the actual dma size is 524288 which allocate in the +2nd time of the initial wlan load phase, and the size which pass to +dma_free_coherent() is 3784704 which is got in the 1st time of recovery +phase, then warning above happened. + +Change to use prev_size of struct target_mem_chunk for the paramter of +dma_free_coherent() since prev_size is the real size of last load/recovery. +Also change to check both type and size of struct target_mem_chunk to +reuse the memory to avoid mismatch buffer size for target. Then the +warning disappear and recovery success. When the 1st QMI_WLFW_REQUEST_MEM_IND +for recovery arrived, the trunk[0] is freed in ath11k_qmi_alloc_target_mem_chunk() +and then dma_alloc_coherent() failed caused by large size, and then +trunk[1] is freed in ath11k_qmi_free_target_mem_chunk(), the left 18 +trunks will be reuse for the 2nd QMI_WLFW_REQUEST_MEM_IND message. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Fixes: 5962f370ce41 ("ath11k: Reuse the available memory after firmware reload") +Signed-off-by: Wen Gong +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220928073832.16251-1-quic_wgong@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -1961,7 +1961,7 @@ static void ath11k_qmi_free_target_mem_c + continue; + + dma_free_coherent(ab->dev, +- ab->qmi.target_mem[i].size, ++ ab->qmi.target_mem[i].prev_size, + ab->qmi.target_mem[i].vaddr, + ab->qmi.target_mem[i].paddr); + ab->qmi.target_mem[i].vaddr = NULL; +@@ -1982,12 +1982,12 @@ static int ath11k_qmi_alloc_target_mem_c + * in such case, no need to allocate memory for FW again. + */ + if (chunk->vaddr) { +- if (chunk->prev_type == chunk->type || ++ if (chunk->prev_type == chunk->type && + chunk->prev_size == chunk->size) + continue; + + /* cannot reuse the existing chunk */ +- dma_free_coherent(ab->dev, chunk->size, ++ dma_free_coherent(ab->dev, chunk->prev_size, + chunk->vaddr, chunk->paddr); + chunk->vaddr = NULL; + } diff --git a/package/kernel/mac80211/patches/ath11k/0005-wifi-ath11k-Fix-spelling-mistake-chnange-change.patch b/package/kernel/mac80211/patches/ath11k/0005-wifi-ath11k-Fix-spelling-mistake-chnange-change.patch new file mode 100644 index 00000000000..4b52252ef34 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0005-wifi-ath11k-Fix-spelling-mistake-chnange-change.patch @@ -0,0 +1,25 @@ +From a797f479bf3e02c6d179c2e6aeace7f9b22b0acd Mon Sep 17 00:00:00 2001 +From: Colin Ian King +Date: Wed, 28 Sep 2022 15:38:34 +0100 +Subject: [PATCH] wifi: ath11k: Fix spelling mistake "chnange" -> "change" + +There is a spelling mistake in an ath11k_dbg debug message. Fix it. + +Signed-off-by: Colin Ian King +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220928143834.35189-1-colin.i.king@gmail.com +--- + drivers/net/wireless/ath/ath11k/wmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/wmi.c ++++ b/drivers/net/wireless/ath/ath11k/wmi.c +@@ -6829,7 +6829,7 @@ static void ath11k_wmi_event_peer_sta_ps + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, +- "peer sta ps chnange ev addr %pM state %u sup_bitmap %x ps_valid %u ts %u\n", ++ "peer sta ps change ev addr %pM state %u sup_bitmap %x ps_valid %u ts %u\n", + ev->peer_macaddr.addr, ev->peer_ps_state, + ev->ps_supported_bitmap, ev->peer_ps_valid, + ev->peer_ps_timestamp); diff --git a/package/kernel/mac80211/patches/ath11k/0006-wifi-ath11k-fix-firmware-assert-during-bandwidth-cha.patch b/package/kernel/mac80211/patches/ath11k/0006-wifi-ath11k-fix-firmware-assert-during-bandwidth-cha.patch new file mode 100644 index 00000000000..f4fedb46ccb --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0006-wifi-ath11k-fix-firmware-assert-during-bandwidth-cha.patch @@ -0,0 +1,225 @@ +From 3ff51d7416ee1ea2d771051a0ffa1ec8be054768 Mon Sep 17 00:00:00 2001 +From: Aditya Kumar Singh +Date: Wed, 5 Oct 2022 15:24:30 +0530 +Subject: [PATCH 6/9] wifi: ath11k: fix firmware assert during bandwidth change + for peer sta + +Currently, ath11k sends peer assoc command for each peer to +firmware when bandwidth changes. Peer assoc command is a +bulky command and if many clients are connected, this could +lead to firmware buffer getting overflowed leading to a firmware +assert. + +However, during bandwidth change, only phymode and bandwidth +also can be updated by WMI set peer param command. This makes +the overall command light when compared to peer assoc and for +multi-client cases, firmware buffer overflow also does not +occur. + +Remove sending peer assoc command during sta bandwidth change +and instead add sending WMI set peer param command for phymode +and bandwidth. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Fixes: f187fe8e3bc65 ("ath11k: fix firmware crash during channel switch") +Signed-off-by: Aditya Kumar Singh +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221005095430.19890-1-quic_adisi@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.h | 2 + + drivers/net/wireless/ath/ath11k/mac.c | 122 +++++++++++++++++-------- + 2 files changed, 87 insertions(+), 37 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -505,6 +505,8 @@ struct ath11k_sta { + u64 ps_start_jiffies; + u64 ps_total_duration; + bool peer_current_ps_valid; ++ ++ u32 bw_prev; + }; + + #define ATH11K_MIN_5G_FREQ 4150 +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -4215,10 +4215,11 @@ static void ath11k_sta_rc_update_wk(stru + const u8 *ht_mcs_mask; + const u16 *vht_mcs_mask; + const u16 *he_mcs_mask; +- u32 changed, bw, nss, smps; ++ u32 changed, bw, nss, smps, bw_prev; + int err, num_vht_rates, num_he_rates; + const struct cfg80211_bitrate_mask *mask; + struct peer_assoc_params peer_arg; ++ enum wmi_phy_mode peer_phymode; + + arsta = container_of(wk, struct ath11k_sta, update_wk); + sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); +@@ -4239,6 +4240,7 @@ static void ath11k_sta_rc_update_wk(stru + arsta->changed = 0; + + bw = arsta->bw; ++ bw_prev = arsta->bw_prev; + nss = arsta->nss; + smps = arsta->smps; + +@@ -4252,26 +4254,57 @@ static void ath11k_sta_rc_update_wk(stru + ath11k_mac_max_he_nss(he_mcs_mask))); + + if (changed & IEEE80211_RC_BW_CHANGED) { +- /* Send peer assoc command before set peer bandwidth param to +- * avoid the mismatch between the peer phymode and the peer +- * bandwidth. +- */ +- ath11k_peer_assoc_prepare(ar, arvif->vif, sta, &peer_arg, true); ++ /* Get the peer phymode */ ++ ath11k_peer_assoc_h_phymode(ar, arvif->vif, sta, &peer_arg); ++ peer_phymode = peer_arg.peer_phymode; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac update sta %pM peer bw %d phymode %d\n", ++ sta->addr, bw, peer_phymode); ++ ++ if (bw > bw_prev) { ++ /* BW is upgraded. In this case we send WMI_PEER_PHYMODE ++ * followed by WMI_PEER_CHWIDTH ++ */ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW upgrade for sta %pM new BW %d, old BW %d\n", ++ sta->addr, bw, bw_prev); ++ ++ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, ++ WMI_PEER_PHYMODE, peer_phymode); ++ ++ if (err) { ++ ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n", ++ sta->addr, peer_phymode, err); ++ goto err_rc_bw_changed; ++ } + +- peer_arg.is_assoc = false; +- err = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); +- if (err) { +- ath11k_warn(ar->ab, "failed to send peer assoc for STA %pM vdev %i: %d\n", +- sta->addr, arvif->vdev_id, err); +- } else if (wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, + WMI_PEER_CHWIDTH, bw); ++ + if (err) + ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", + sta->addr, bw, err); + } else { +- ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", +- sta->addr, arvif->vdev_id); ++ /* BW is downgraded. In this case we send WMI_PEER_CHWIDTH ++ * followed by WMI_PEER_PHYMODE ++ */ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW downgrade for sta %pM new BW %d,old BW %d\n", ++ sta->addr, bw, bw_prev); ++ ++ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, ++ WMI_PEER_CHWIDTH, bw); ++ ++ if (err) { ++ ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", ++ sta->addr, bw, err); ++ goto err_rc_bw_changed; ++ } ++ ++ err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, ++ WMI_PEER_PHYMODE, peer_phymode); ++ ++ if (err) ++ ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n", ++ sta->addr, peer_phymode, err); + } + } + +@@ -4352,6 +4385,7 @@ static void ath11k_sta_rc_update_wk(stru + } + } + ++err_rc_bw_changed: + mutex_unlock(&ar->conf_mutex); + } + +@@ -4505,6 +4539,34 @@ exit: + return ret; + } + ++static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar, ++ struct ieee80211_sta *sta) ++{ ++ u32 bw = WMI_PEER_CHWIDTH_20MHZ; ++ ++ switch (sta->deflink.bandwidth) { ++ case IEEE80211_STA_RX_BW_20: ++ bw = WMI_PEER_CHWIDTH_20MHZ; ++ break; ++ case IEEE80211_STA_RX_BW_40: ++ bw = WMI_PEER_CHWIDTH_40MHZ; ++ break; ++ case IEEE80211_STA_RX_BW_80: ++ bw = WMI_PEER_CHWIDTH_80MHZ; ++ break; ++ case IEEE80211_STA_RX_BW_160: ++ bw = WMI_PEER_CHWIDTH_160MHZ; ++ break; ++ default: ++ ath11k_warn(ar->ab, "Invalid bandwidth %d for %pM\n", ++ sta->deflink.bandwidth, sta->addr); ++ bw = WMI_PEER_CHWIDTH_20MHZ; ++ break; ++ } ++ ++ return bw; ++} ++ + static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, +@@ -4590,6 +4652,12 @@ static int ath11k_mac_op_sta_state(struc + if (ret) + ath11k_warn(ar->ab, "Failed to associate station: %pM\n", + sta->addr); ++ ++ spin_lock_bh(&ar->data_lock); ++ /* Set arsta bw and prev bw */ ++ arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); ++ arsta->bw_prev = arsta->bw; ++ spin_unlock_bh(&ar->data_lock); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) { + spin_lock_bh(&ar->ab->base_lock); +@@ -4713,28 +4781,8 @@ static void ath11k_mac_op_sta_rc_update( + spin_lock_bh(&ar->data_lock); + + if (changed & IEEE80211_RC_BW_CHANGED) { +- bw = WMI_PEER_CHWIDTH_20MHZ; +- +- switch (sta->deflink.bandwidth) { +- case IEEE80211_STA_RX_BW_20: +- bw = WMI_PEER_CHWIDTH_20MHZ; +- break; +- case IEEE80211_STA_RX_BW_40: +- bw = WMI_PEER_CHWIDTH_40MHZ; +- break; +- case IEEE80211_STA_RX_BW_80: +- bw = WMI_PEER_CHWIDTH_80MHZ; +- break; +- case IEEE80211_STA_RX_BW_160: +- bw = WMI_PEER_CHWIDTH_160MHZ; +- break; +- default: +- ath11k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", +- sta->deflink.bandwidth, sta->addr); +- bw = WMI_PEER_CHWIDTH_20MHZ; +- break; +- } +- ++ bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); ++ arsta->bw_prev = arsta->bw; + arsta->bw = bw; + } + diff --git a/package/kernel/mac80211/patches/ath11k/0007-wifi-ath11k-suppress-add-interface-error.patch b/package/kernel/mac80211/patches/ath11k/0007-wifi-ath11k-suppress-add-interface-error.patch new file mode 100644 index 00000000000..fbef0abb8db --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0007-wifi-ath11k-suppress-add-interface-error.patch @@ -0,0 +1,52 @@ +From 638b26652b0438563a76ec90014c8cba34db982b Mon Sep 17 00:00:00 2001 +From: Karthikeyan Periyasamy +Date: Thu, 6 Oct 2022 06:28:42 +0530 +Subject: [PATCH 7/9] wifi: ath11k: suppress add interface error + +In the VIF (other than monitor type) creation request, we should not +throw the error code when the monitor VIF creation fails, since the +actual VIF creation succeeds. If we throw the error code from driver +then the actual VIF creation get fail. So suppress the monitor VIF +creation error by throwing warning message instead of error code. + +Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.6.0.1-00760-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Karthikeyan Periyasamy +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221006005842.8599-1-quic_periyasa@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6421,18 +6421,16 @@ static int ath11k_mac_op_add_interface(s + + ath11k_dp_vdev_tx_attach(ar, arvif); + ++ ath11k_debugfs_add_interface(arvif); ++ + if (vif->type != NL80211_IFTYPE_MONITOR && + test_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags)) { + ret = ath11k_mac_monitor_vdev_create(ar); +- if (ret) { ++ if (ret) + ath11k_warn(ar->ab, "failed to create monitor vdev during add interface: %d", + ret); +- goto err_peer_del; +- } + } + +- ath11k_debugfs_add_interface(arvif); +- + mutex_unlock(&ar->conf_mutex); + + return 0; +@@ -6457,7 +6455,6 @@ err_vdev_del: + spin_unlock_bh(&ar->data_lock); + + err: +- ath11k_debugfs_remove_interface(arvif); + mutex_unlock(&ar->conf_mutex); + + return ret; diff --git a/package/kernel/mac80211/patches/ath11k/0008-wifi-ath11k-add-support-to-configure-channel-dwell-t.patch b/package/kernel/mac80211/patches/ath11k/0008-wifi-ath11k-add-support-to-configure-channel-dwell-t.patch new file mode 100644 index 00000000000..d0b19fe59f5 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0008-wifi-ath11k-add-support-to-configure-channel-dwell-t.patch @@ -0,0 +1,102 @@ +From c362daa213cdeb0a9e7c2ed84849544c24505720 Mon Sep 17 00:00:00 2001 +From: Manikanta Pubbisetty +Date: Fri, 7 Oct 2022 10:41:30 +0530 +Subject: [PATCH 8/9] wifi: ath11k: add support to configure channel dwell time + +Add support to configure channel dwell time during scan. +Dwell time help to stay on the channel for a specified duration +during scan and aid userspace in finding WiFi networks. Very +useful in passive scans where longer dwell times are needed +to find the WiFi networks. + +Configure channel dwell time from duration of the scan request +received from mac80211 when the duration is non-zero. When the +scan request does not have duration value, use the default ones, +the current implementation. + +Advertise corresponding feature flag NL80211_EXT_FEATURE_SET_SCAN_DWELL +to enable the feature. + +Change is applicable for all ath11k hardware. + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Signed-off-by: Manikanta Pubbisetty +Reviewed-by: Jeff Johnson +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221007051130.6067-1-quic_mpubbise@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 33 +++++++++++++++++++++++---- + 1 file changed, 29 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -241,7 +241,10 @@ const struct htt_rx_ring_tlv_filter ath1 + #define ath11k_a_rates (ath11k_legacy_rates + 4) + #define ath11k_a_rates_size (ARRAY_SIZE(ath11k_legacy_rates) - 4) + +-#define ATH11K_MAC_SCAN_TIMEOUT_MSECS 200 /* in msecs */ ++#define ATH11K_MAC_SCAN_CMD_EVT_OVERHEAD 200 /* in msecs */ ++ ++/* Overhead due to the processing of channel switch events from FW */ ++#define ATH11K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD 10 /* in msecs */ + + static const u32 ath11k_smps_map[] = { + [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC, +@@ -3612,6 +3615,7 @@ static int ath11k_mac_op_hw_scan(struct + struct scan_req_params arg; + int ret = 0; + int i; ++ u32 scan_timeout; + + mutex_lock(&ar->conf_mutex); + +@@ -3681,6 +3685,26 @@ static int ath11k_mac_op_hw_scan(struct + ether_addr_copy(arg.mac_mask.addr, req->mac_addr_mask); + } + ++ /* if duration is set, default dwell times will be overwritten */ ++ if (req->duration) { ++ arg.dwell_time_active = req->duration; ++ arg.dwell_time_active_2g = req->duration; ++ arg.dwell_time_active_6g = req->duration; ++ arg.dwell_time_passive = req->duration; ++ arg.dwell_time_passive_6g = req->duration; ++ arg.burst_duration = req->duration; ++ ++ scan_timeout = min_t(u32, arg.max_rest_time * ++ (arg.num_chan - 1) + (req->duration + ++ ATH11K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD) * ++ arg.num_chan, arg.max_scan_time); ++ } else { ++ scan_timeout = arg.max_scan_time; ++ } ++ ++ /* Add a margin to account for event/command processing */ ++ scan_timeout += ATH11K_MAC_SCAN_CMD_EVT_OVERHEAD; ++ + ret = ath11k_start_scan(ar, &arg); + if (ret) { + ath11k_warn(ar->ab, "failed to start hw scan: %d\n", ret); +@@ -3689,10 +3713,8 @@ static int ath11k_mac_op_hw_scan(struct + spin_unlock_bh(&ar->data_lock); + } + +- /* Add a 200ms margin to account for event/command processing */ + ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, +- msecs_to_jiffies(arg.max_scan_time + +- ATH11K_MAC_SCAN_TIMEOUT_MSECS)); ++ msecs_to_jiffies(scan_timeout)); + + exit: + kfree(arg.chan_list); +@@ -9060,6 +9082,9 @@ static int __ath11k_mac_register(struct + NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); + } + ++ wiphy_ext_feature_set(ar->hw->wiphy, ++ NL80211_EXT_FEATURE_SET_SCAN_DWELL); ++ + ath11k_reg_init(ar); + + if (!test_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags)) { diff --git a/package/kernel/mac80211/patches/ath11k/0009-wifi-ath11k-Send-PME-message-during-wakeup-from-D3co.patch b/package/kernel/mac80211/patches/ath11k/0009-wifi-ath11k-Send-PME-message-during-wakeup-from-D3co.patch new file mode 100644 index 00000000000..1e04c974fe3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0009-wifi-ath11k-Send-PME-message-during-wakeup-from-D3co.patch @@ -0,0 +1,39 @@ +From 3f9b09ccf7d5f23066b02881a737bee42def9d1a Mon Sep 17 00:00:00 2001 +From: Baochen Qiang +Date: Mon, 10 Oct 2022 11:32:37 +0800 +Subject: [PATCH 9/9] wifi: ath11k: Send PME message during wakeup from D3cold + +We are seeing system stuck on some specific platforms due to +WLAN chip fails to wakeup from D3cold state. + +With this flag, firmware will send PME message during wakeup +and this issue is gone. + +Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3 + +Signed-off-by: Baochen Qiang +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221010033237.415478-1-quic_bqiang@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -19,6 +19,7 @@ + #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 + #define HOST_CSTATE_BIT 0x04 + #define PLATFORM_CAP_PCIE_GLOBAL_RESET 0x08 ++#define PLATFORM_CAP_PCIE_PME_D3COLD 0x10 + + #define FW_BUILD_ID_MASK "QC_IMAGE_VERSION_STRING=" + +@@ -1752,6 +1753,8 @@ static int ath11k_qmi_host_cap_send(stru + if (ab->hw_params.global_reset) + req.nm_modem |= PLATFORM_CAP_PCIE_GLOBAL_RESET; + ++ req.nm_modem |= PLATFORM_CAP_PCIE_PME_D3COLD; ++ + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi host cap request\n"); + + ret = qmi_txn_init(&ab->qmi.handle, &txn, diff --git a/package/kernel/mac80211/patches/ath11k/0010-wifi-ath11k-Fix-firmware-crash-on-vdev-delete-race-c.patch b/package/kernel/mac80211/patches/ath11k/0010-wifi-ath11k-Fix-firmware-crash-on-vdev-delete-race-c.patch new file mode 100644 index 00000000000..7275af06ea2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0010-wifi-ath11k-Fix-firmware-crash-on-vdev-delete-race-c.patch @@ -0,0 +1,116 @@ +From 3811fa1f231f1a3e29759efef4992116604aab8b Mon Sep 17 00:00:00 2001 +From: Sowmiya Sree Elavalagan +Date: Tue, 11 Oct 2022 15:23:46 +0530 +Subject: [PATCH] wifi: ath11k: Fix firmware crash on vdev delete race + condition + +Current code does not wait for vdev delete completion on vdev create +failures and tries to send another vdev create followed by vdev set +param to firmware with same vdev id. This causes firmware crash. +Fix this crash by waiting for vdev delete completion on vdev +create failures. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.6.0.1-00905-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Sowmiya Sree Elavalagan +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221011095346.3901-1-quic_ssreeela@quicinc.com +--- + drivers/net/wireless/ath/ath11k/mac.c | 60 +++++++++++++++++---------- + 1 file changed, 37 insertions(+), 23 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -6233,6 +6233,40 @@ void ath11k_mac_11d_scan_stop_all(struct + } + } + ++static int ath11k_mac_vdev_delete(struct ath11k *ar, struct ath11k_vif *arvif) ++{ ++ unsigned long time_left; ++ struct ieee80211_vif *vif = arvif->vif; ++ int ret = 0; ++ ++ lockdep_assert_held(&ar->conf_mutex); ++ ++ reinit_completion(&ar->vdev_delete_done); ++ ++ ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); ++ if (ret) { ++ ath11k_warn(ar->ab, "failed to delete WMI vdev %d: %d\n", ++ arvif->vdev_id, ret); ++ return ret; ++ } ++ ++ time_left = wait_for_completion_timeout(&ar->vdev_delete_done, ++ ATH11K_VDEV_DELETE_TIMEOUT_HZ); ++ if (time_left == 0) { ++ ath11k_warn(ar->ab, "Timeout in receiving vdev delete response\n"); ++ return -ETIMEDOUT; ++ } ++ ++ ar->ab->free_vdev_map |= 1LL << (arvif->vdev_id); ++ ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); ++ ar->num_created_vdevs--; ++ ++ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", ++ vif->addr, arvif->vdev_id); ++ ++ return ret; ++} ++ + static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) + { +@@ -6468,10 +6502,7 @@ err_peer_del: + } + + err_vdev_del: +- ath11k_wmi_vdev_delete(ar, arvif->vdev_id); +- ar->num_created_vdevs--; +- ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); +- ab->free_vdev_map |= 1LL << arvif->vdev_id; ++ ath11k_mac_vdev_delete(ar, arvif); + spin_lock_bh(&ar->data_lock); + list_del(&arvif->list); + spin_unlock_bh(&ar->data_lock); +@@ -6499,7 +6530,6 @@ static void ath11k_mac_op_remove_interfa + struct ath11k *ar = hw->priv; + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k_base *ab = ar->ab; +- unsigned long time_left; + int ret; + int i; + +@@ -6520,29 +6550,13 @@ static void ath11k_mac_op_remove_interfa + arvif->vdev_id, ret); + } + +- reinit_completion(&ar->vdev_delete_done); +- +- ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); ++ ret = ath11k_mac_vdev_delete(ar, arvif); + if (ret) { +- ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n", ++ ath11k_warn(ab, "failed to delete vdev %d: %d\n", + arvif->vdev_id, ret); + goto err_vdev_del; + } + +- time_left = wait_for_completion_timeout(&ar->vdev_delete_done, +- ATH11K_VDEV_DELETE_TIMEOUT_HZ); +- if (time_left == 0) { +- ath11k_warn(ab, "Timeout in receiving vdev delete response\n"); +- goto err_vdev_del; +- } +- +- ab->free_vdev_map |= 1LL << (arvif->vdev_id); +- ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); +- ar->num_created_vdevs--; +- +- ath11k_dbg(ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", +- vif->addr, arvif->vdev_id); +- + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); + ar->monitor_vdev_id = -1; diff --git a/package/kernel/mac80211/patches/ath11k/0011-wifi-ath11k-fix-monitor-vdev-creation-with-firmware-.patch b/package/kernel/mac80211/patches/ath11k/0011-wifi-ath11k-fix-monitor-vdev-creation-with-firmware-.patch new file mode 100644 index 00000000000..2f066d0a565 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0011-wifi-ath11k-fix-monitor-vdev-creation-with-firmware-.patch @@ -0,0 +1,40 @@ +From f3ca72b0327101a074a871539e61775d43908ca4 Mon Sep 17 00:00:00 2001 +From: Nagarajan Maran +Date: Fri, 14 Oct 2022 21:20:54 +0530 +Subject: [PATCH] wifi: ath11k: fix monitor vdev creation with firmware + recovery + +During firmware recovery, the monitor interface is not +getting created in the driver and firmware since +the respective flags are not updated properly. + +So after firmware recovery is successful, when monitor +interface is brought down manually, firmware assertion +is observed, since we are trying to bring down the +interface which is not yet created in the firmware. + +Fix this by updating the monitor flags properly per +phy#, during firmware recovery. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Nagarajan Maran +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221014155054.11471-1-quic_nmaran@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -1677,6 +1677,10 @@ void ath11k_core_pre_reconfigure_recover + ath11k_mac_tx_mgmt_pending_free, ar); + idr_destroy(&ar->txmgmt_idr); + wake_up(&ar->txmgmt_empty_waitq); ++ ++ ar->monitor_vdev_id = -1; ++ clear_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags); ++ clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); + } + + wake_up(&ab->wmi_ab.tx_credits_wq); diff --git a/package/kernel/mac80211/patches/ath11k/0012-wifi-ath11k-Fix-qmi_msg_handler-data-structure-initi.patch b/package/kernel/mac80211/patches/ath11k/0012-wifi-ath11k-Fix-qmi_msg_handler-data-structure-initi.patch new file mode 100644 index 00000000000..1f7418ff89f --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0012-wifi-ath11k-Fix-qmi_msg_handler-data-structure-initi.patch @@ -0,0 +1,33 @@ +From ed3725e15a154ebebf44e0c34806c57525483f92 Mon Sep 17 00:00:00 2001 +From: Rahul Bhattacharjee +Date: Fri, 21 Oct 2022 14:31:26 +0530 +Subject: [PATCH] wifi: ath11k: Fix qmi_msg_handler data structure + initialization + +qmi_msg_handler is required to be null terminated by QMI module. +There might be a case where a handler for a msg id is not present in the +handlers array which can lead to infinite loop while searching the handler +and therefore out of bound access in qmi_invoke_handler(). +Hence update the initialization in qmi_msg_handler data structure. + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Rahul Bhattacharjee +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221021090126.28626-1-quic_rbhattac@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -3090,6 +3090,9 @@ static const struct qmi_msg_handler ath1 + sizeof(struct qmi_wlfw_fw_init_done_ind_msg_v01), + .fn = ath11k_qmi_msg_fw_init_done_cb, + }, ++ ++ /* end of list */ ++ {}, + }; + + static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, diff --git a/package/kernel/mac80211/patches/ath11k/0013-wifi-ath11k-synchronize-ath11k_mac_he_gi_to_nl80211_.patch b/package/kernel/mac80211/patches/ath11k/0013-wifi-ath11k-synchronize-ath11k_mac_he_gi_to_nl80211_.patch new file mode 100644 index 00000000000..1e89b4d4f21 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0013-wifi-ath11k-synchronize-ath11k_mac_he_gi_to_nl80211_.patch @@ -0,0 +1,42 @@ +From dd1c2322694522f674c874f5fa02ac5ae39135dd Mon Sep 17 00:00:00 2001 +From: "Jiri Slaby (SUSE)" +Date: Mon, 31 Oct 2022 12:43:41 +0100 +Subject: [PATCH] wifi: ath11k: synchronize + ath11k_mac_he_gi_to_nl80211_he_gi()'s return type + +ath11k_mac_he_gi_to_nl80211_he_gi() generates a valid warning with gcc-13: + drivers/net/wireless/ath/ath11k/mac.c:321:20: error: conflicting types for 'ath11k_mac_he_gi_to_nl80211_he_gi' due to enum/integer mismatch; have 'enum nl80211_he_gi(u8)' + drivers/net/wireless/ath/ath11k/mac.h:166:5: note: previous declaration of 'ath11k_mac_he_gi_to_nl80211_he_gi' with type 'u32(u8)' + +I.e. the type of the return value ath11k_mac_he_gi_to_nl80211_he_gi() in +the declaration is u32, while the definition spells enum nl80211_he_gi. +Synchronize them to the latter. + +Cc: Martin Liska +Cc: Kalle Valo +Cc: "David S. Miller" +Cc: Eric Dumazet +Cc: Jakub Kicinski +Cc: Paolo Abeni +Cc: ath11k@lists.infradead.org +Cc: linux-wireless@vger.kernel.org +Cc: netdev@vger.kernel.org +Signed-off-by: Jiri Slaby (SUSE) +Reviewed-by: Jeff Johnson +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221031114341.10377-1-jirislaby@kernel.org +--- + drivers/net/wireless/ath/ath11k/mac.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath11k/mac.h ++++ b/drivers/net/wireless/ath/ath11k/mac.h +@@ -163,7 +163,7 @@ void ath11k_mac_drain_tx(struct ath11k * + void ath11k_mac_peer_cleanup_all(struct ath11k *ar); + int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); + u8 ath11k_mac_bw_to_mac80211_bw(u8 bw); +-u32 ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); ++enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi); + enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy); + enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones); + enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw); diff --git a/package/kernel/mac80211/patches/ath11k/0016-wifi-ath11k-Make-QMI-message-rules-const.patch b/package/kernel/mac80211/patches/ath11k/0016-wifi-ath11k-Make-QMI-message-rules-const.patch new file mode 100644 index 00000000000..1f48df73f75 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0016-wifi-ath11k-Make-QMI-message-rules-const.patch @@ -0,0 +1,341 @@ +From 93c1592889fca46d09d833455628bab05516cdbf Mon Sep 17 00:00:00 2001 +From: Jeff Johnson +Date: Wed, 14 Sep 2022 17:23:03 -0700 +Subject: [PATCH] wifi: ath11k: Make QMI message rules const + +Commit ff6d365898d4 ("soc: qcom: qmi: use const for struct +qmi_elem_info") allows QMI message encoding/decoding rules to be +const, so do that for ath11k. + +Compile tested only. + +Signed-off-by: Jeff Johnson +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220915002303.12206-1-quic_jjohnson@quicinc.com +--- + drivers/net/wireless/ath/ath11k/qmi.c | 72 +++++++++++++-------------- + 1 file changed, 36 insertions(+), 36 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -29,7 +29,7 @@ module_param_named(cold_boot_cal, ath11k + MODULE_PARM_DESC(cold_boot_cal, + "Decrease the channel switch time but increase the driver load time (Default: true)"); + +-static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, +@@ -280,7 +280,7 @@ static struct qmi_elem_info qmi_wlanfw_h + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -297,7 +297,7 @@ static struct qmi_elem_info qmi_wlanfw_h + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, +@@ -522,7 +522,7 @@ static struct qmi_elem_info qmi_wlanfw_i + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -558,7 +558,7 @@ static struct qmi_elem_info qmi_wlanfw_i + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, +@@ -590,7 +590,7 @@ static struct qmi_elem_info qmi_wlanfw_m + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -632,7 +632,7 @@ static struct qmi_elem_info qmi_wlanfw_m + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, +@@ -659,7 +659,7 @@ static struct qmi_elem_info qmi_wlanfw_r + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, +@@ -699,7 +699,7 @@ static struct qmi_elem_info qmi_wlanfw_m + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, +@@ -726,7 +726,7 @@ static struct qmi_elem_info qmi_wlanfw_r + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -744,7 +744,7 @@ static struct qmi_elem_info qmi_wlanfw_r + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, +@@ -752,7 +752,7 @@ static struct qmi_elem_info qmi_wlanfw_c + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, +@@ -760,7 +760,7 @@ static struct qmi_elem_info qmi_wlanfw_d + }, + }; + +-static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -814,7 +814,7 @@ static struct qmi_elem_info qmi_wlfw_dev + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -840,7 +840,7 @@ static struct qmi_elem_info qmi_wlanfw_r + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -857,7 +857,7 @@ static struct qmi_elem_info qmi_wlanfw_r + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -873,7 +873,7 @@ static struct qmi_elem_info qmi_wlanfw_s + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -899,7 +899,7 @@ static struct qmi_elem_info qmi_wlanfw_f + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -1100,7 +1100,7 @@ static struct qmi_elem_info qmi_wlanfw_c + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, +@@ -1235,7 +1235,7 @@ static struct qmi_elem_info qmi_wlanfw_b + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -1253,7 +1253,7 @@ static struct qmi_elem_info qmi_wlanfw_b + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, +@@ -1277,7 +1277,7 @@ static struct qmi_elem_info qmi_wlanfw_m + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -1294,7 +1294,7 @@ static struct qmi_elem_info qmi_wlanfw_m + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -1347,7 +1347,7 @@ static struct qmi_elem_info qmi_wlanfw_c + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -1382,7 +1382,7 @@ static struct qmi_elem_info qmi_wlanfw_c + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, +@@ -1406,7 +1406,7 @@ static struct qmi_elem_info qmi_wlanfw_s + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -1423,7 +1423,7 @@ static struct qmi_elem_info qmi_wlanfw_s + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, +@@ -1458,7 +1458,7 @@ static struct qmi_elem_info qmi_wlanfw_w + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -1476,7 +1476,7 @@ static struct qmi_elem_info qmi_wlanfw_w + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, +@@ -1615,7 +1615,7 @@ static struct qmi_elem_info qmi_wlanfw_w + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -1632,28 +1632,28 @@ static struct qmi_elem_info qmi_wlanfw_w + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, +@@ -1679,7 +1679,7 @@ static struct qmi_elem_info qmi_wlanfw_w + }, + }; + +-static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, +@@ -1697,7 +1697,7 @@ static struct qmi_elem_info qmi_wlanfw_w + }, + }; + +-static struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { ++static const struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, diff --git a/package/kernel/mac80211/patches/ath11k/0017-wifi-ath11k-Trigger-sta-disconnect-on-hardware-resta.patch b/package/kernel/mac80211/patches/ath11k/0017-wifi-ath11k-Trigger-sta-disconnect-on-hardware-resta.patch new file mode 100644 index 00000000000..f95e5027b25 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0017-wifi-ath11k-Trigger-sta-disconnect-on-hardware-resta.patch @@ -0,0 +1,119 @@ +From a018750a2cceaf4427c4ee3d9ce3e83a171d5bd6 Mon Sep 17 00:00:00 2001 +From: Youghandhar Chintala +Date: Fri, 4 Nov 2022 14:24:03 +0530 +Subject: [PATCH] wifi: ath11k: Trigger sta disconnect on hardware restart + +Currently after the hardware restart triggered from the driver, the +station interface connection remains intact, since a disconnect trigger +is not sent to userspace. This can lead to a problem in targets where +the wifi mac sequence is added by the firmware. + +After the target restart, its wifi mac sequence number gets reset to +zero. Hence AP to which our device is connected will receive frames with +a wifi mac sequence number jump to the past, thereby resulting in the +AP dropping all these frames, until the frame arrives with a wifi mac +sequence number which AP was expecting. + +To avoid such frame drops, its better to trigger a station disconnect +upon target hardware restart which can be done with API +ieee80211_reconfig_disconnect exposed to mac80211. + +The other targets are not affected by this change, since the hardware +params flag is not set. + +Reported-by: kernel test robot + +Tested-on: WCN6750 hw1.0 AHB WLAN.MSL.1.0.1-00887-QCAMSLSWPLZ-1 + +Signed-off-by: Youghandhar Chintala +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221104085403.11025-1-quic_youghand@quicinc.com +--- + drivers/net/wireless/ath/ath11k/core.c | 6 ++++++ + drivers/net/wireless/ath/ath11k/hw.h | 1 + + drivers/net/wireless/ath/ath11k/mac.c | 7 +++++++ + 3 files changed, 14 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -195,6 +195,7 @@ static const struct ath11k_hw_params ath + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = false, + }, + { + .name = "qca6390 hw2.0", +@@ -277,6 +278,7 @@ static const struct ath11k_hw_params ath + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = true, + }, + { + .name = "qcn9074 hw1.0", +@@ -356,6 +358,7 @@ static const struct ath11k_hw_params ath + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = false, + }, + { + .name = "wcn6855 hw2.0", +@@ -438,6 +441,7 @@ static const struct ath11k_hw_params ath + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = true, + }, + { + .name = "wcn6855 hw2.1", +@@ -519,6 +523,7 @@ static const struct ath11k_hw_params ath + .tcl_ring_retry = true, + .tx_ring_size = DP_TCL_DATA_RING_SIZE, + .smp2p_wow_exit = false, ++ .support_fw_mac_sequence = true, + }, + { + .name = "wcn6750 hw1.0", +@@ -597,6 +602,7 @@ static const struct ath11k_hw_params ath + .tcl_ring_retry = false, + .tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750, + .smp2p_wow_exit = true, ++ .support_fw_mac_sequence = true, + }, + }; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -219,6 +219,7 @@ struct ath11k_hw_params { + bool tcl_ring_retry; + u32 tx_ring_size; + bool smp2p_wow_exit; ++ bool support_fw_mac_sequence; + }; + + struct ath11k_hw_ops { +--- a/drivers/net/wireless/ath/ath11k/mac.c ++++ b/drivers/net/wireless/ath/ath11k/mac.c +@@ -8010,6 +8010,7 @@ ath11k_mac_op_reconfig_complete(struct i + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + int recovery_count; ++ struct ath11k_vif *arvif; + + if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) + return; +@@ -8045,6 +8046,12 @@ ath11k_mac_op_reconfig_complete(struct i + ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n"); + } + } ++ if (ar->ab->hw_params.support_fw_mac_sequence) { ++ list_for_each_entry(arvif, &ar->arvifs, list) { ++ if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA) ++ ieee80211_hw_restart_disconnect(arvif->vif); ++ } ++ } + } + + mutex_unlock(&ar->conf_mutex); diff --git a/package/kernel/mac80211/patches/ath11k/0018-wifi-ath11k-Fix-race-condition-with-struct-htt_ppdu_.patch b/package/kernel/mac80211/patches/ath11k/0018-wifi-ath11k-Fix-race-condition-with-struct-htt_ppdu_.patch new file mode 100644 index 00000000000..cef61ee3448 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/0018-wifi-ath11k-Fix-race-condition-with-struct-htt_ppdu_.patch @@ -0,0 +1,103 @@ +From e44de90453bb2b46a523df78c39eb896bab35dcd Mon Sep 17 00:00:00 2001 +From: Govindaraj Saminathan +Date: Tue, 29 Nov 2022 13:04:02 +0200 +Subject: [PATCH] wifi: ath11k: Fix race condition with struct + htt_ppdu_stats_info + +A crash happens when running the traffic with multiple clients: + +Crash Signature : Unable to handle kernel paging request at +virtual address ffffffd700970918 During the crash, PC points to +"ieee80211_tx_rate_update+0x30/0x68 [mac80211]" +LR points to "ath11k_dp_htt_htc_t2h_msg_handler+0x5a8/0x8a0 [ath11k]". + +Struct ppdu_stats_info is allocated and accessed from event callback via copy +engine tasklet, this has a problem when freeing it from ath11k_mac_op_stop(). + +Use data_lock during entire ath11k_dp_htt_get_ppdu_desc() call to protect +struct htt_ppdu_stats_info access and to avoid race condition when accessing it +from ath11k_mac_op_stop(). + +Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 + +Signed-off-by: Govindaraj Saminathan +Co-developed-by: Karthikeyan Kathirvel +Signed-off-by: Karthikeyan Kathirvel +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221124071104.22506-1-quic_kathirve@quicinc.com +--- + drivers/net/wireless/ath/ath11k/dp_rx.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/dp_rx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_rx.c +@@ -1535,13 +1535,12 @@ struct htt_ppdu_stats_info *ath11k_dp_ht + { + struct htt_ppdu_stats_info *ppdu_info; + +- spin_lock_bh(&ar->data_lock); ++ lockdep_assert_held(&ar->data_lock); ++ + if (!list_empty(&ar->ppdu_stats_info)) { + list_for_each_entry(ppdu_info, &ar->ppdu_stats_info, list) { +- if (ppdu_info->ppdu_id == ppdu_id) { +- spin_unlock_bh(&ar->data_lock); ++ if (ppdu_info->ppdu_id == ppdu_id) + return ppdu_info; +- } + } + + if (ar->ppdu_stat_list_depth > HTT_PPDU_DESC_MAX_DEPTH) { +@@ -1553,16 +1552,13 @@ struct htt_ppdu_stats_info *ath11k_dp_ht + kfree(ppdu_info); + } + } +- spin_unlock_bh(&ar->data_lock); + + ppdu_info = kzalloc(sizeof(*ppdu_info), GFP_ATOMIC); + if (!ppdu_info) + return NULL; + +- spin_lock_bh(&ar->data_lock); + list_add_tail(&ppdu_info->list, &ar->ppdu_stats_info); + ar->ppdu_stat_list_depth++; +- spin_unlock_bh(&ar->data_lock); + + return ppdu_info; + } +@@ -1586,16 +1582,17 @@ static int ath11k_htt_pull_ppdu_stats(st + ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ret = -EINVAL; +- goto exit; ++ goto out; + } + + if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar)) + trace_ath11k_htt_ppdu_stats(ar, skb->data, len); + ++ spin_lock_bh(&ar->data_lock); + ppdu_info = ath11k_dp_htt_get_ppdu_desc(ar, ppdu_id); + if (!ppdu_info) { + ret = -EINVAL; +- goto exit; ++ goto out_unlock_data; + } + + ppdu_info->ppdu_id = ppdu_id; +@@ -1604,10 +1601,13 @@ static int ath11k_htt_pull_ppdu_stats(st + (void *)ppdu_info); + if (ret) { + ath11k_warn(ab, "Failed to parse tlv %d\n", ret); +- goto exit; ++ goto out_unlock_data; + } + +-exit: ++out_unlock_data: ++ spin_unlock_bh(&ar->data_lock); ++ ++out: + rcu_read_unlock(); + + return ret; diff --git a/package/kernel/mac80211/patches/ath11k/900-ath11k-control-thermal-support-via-symbol.patch b/package/kernel/mac80211/patches/ath11k/900-ath11k-control-thermal-support-via-symbol.patch new file mode 100644 index 00000000000..60720a721e3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/900-ath11k-control-thermal-support-via-symbol.patch @@ -0,0 +1,66 @@ +From 703d6551f71e7290619d6effe2a25a64e10538b7 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Thu, 15 Dec 2022 12:20:52 +0100 +Subject: [PATCH] ath11k: control thermal support via symbol + +Currently, thermal support will get built if CONFIG_THERMAL is reachable, +however this is not suitable for OpenWrt as with ALL_KMODS being set to y +ATH11K_THERMAL wont get selected and so hwmon and thermal kmods wont get +pulled in resulting in a build-failure. + +So, to avoid that, lets do what is already done for ath10k and add a +config symbol into backports for enabling thermal support. + +Signed-off-by: Robert Marko +--- + drivers/net/wireless/ath/ath11k/Kconfig | 7 +++++++ + drivers/net/wireless/ath/ath11k/Makefile | 2 +- + drivers/net/wireless/ath/ath11k/thermal.h | 2 +- + local-symbols | 1 + + 4 files changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/Kconfig ++++ b/drivers/net/wireless/ath/ath11k/Kconfig +@@ -61,3 +61,10 @@ config ATH11K_SPECTRAL + Enable ath11k spectral scan support + + Say Y to enable access to the FFT/spectral data via debugfs. ++ ++config ATH11K_THERMAL ++ bool "ath11k thermal sensors and throttling support" ++ depends on ATH11K ++ depends on THERMAL ++ help ++ Enable ath11k thermal sensors and throttling support. +--- a/drivers/net/wireless/ath/ath11k/Makefile ++++ b/drivers/net/wireless/ath/ath11k/Makefile +@@ -22,7 +22,7 @@ ath11k-y += core.o \ + ath11k-$(CPTCFG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o + ath11k-$(CPTCFG_NL80211_TESTMODE) += testmode.o + ath11k-$(CPTCFG_ATH11K_TRACING) += trace.o +-ath11k-$(CONFIG_THERMAL) += thermal.o ++ath11k-$(CPTCFG_ATH11K_THERMAL) += thermal.o + ath11k-$(CPTCFG_ATH11K_SPECTRAL) += spectral.o + ath11k-$(CONFIG_PM) += wow.o + +--- a/drivers/net/wireless/ath/ath11k/thermal.h ++++ b/drivers/net/wireless/ath/ath11k/thermal.h +@@ -25,7 +25,7 @@ struct ath11k_thermal { + int temperature; + }; + +-#if IS_REACHABLE(CONFIG_THERMAL) ++#if IS_REACHABLE(CPTCFG_ATH11K_THERMAL) + int ath11k_thermal_register(struct ath11k_base *sc); + void ath11k_thermal_unregister(struct ath11k_base *sc); + int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state); +--- a/local-symbols ++++ b/local-symbols +@@ -174,6 +174,7 @@ ATH11K_DEBUG= + ATH11K_DEBUGFS= + ATH11K_TRACING= + ATH11K_SPECTRAL= ++ATH11K_THERMAL= + WLAN_VENDOR_ATMEL= + ATMEL= + PCI_ATMEL= diff --git a/package/kernel/mac80211/patches/ath11k/901-wifi-ath11k-pci-fix-compilation-in-5.16-and-older.patch b/package/kernel/mac80211/patches/ath11k/901-wifi-ath11k-pci-fix-compilation-in-5.16-and-older.patch new file mode 100644 index 00000000000..2b6c18d6dd9 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/901-wifi-ath11k-pci-fix-compilation-in-5.16-and-older.patch @@ -0,0 +1,29 @@ +From 04178918e7f6b5f34dde81ec79ee8a1ccace3be3 Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 17 Oct 2022 11:45:03 +0200 +Subject: [PATCH] wifi: ath11k: pci: fix compilation in 5.16 and older + +Commit ("genirq/msi, treewide: Use a named struct for PCI/MSI attributes") +changed the msi_desc structure a bit, however that is only available in +kernels 5.17 and newer, so check for kernel version to allow compilation +in 5.16 and older. + +Signed-off-by: Robert Marko +--- + drivers/net/wireless/ath/ath11k/pci.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -451,7 +451,11 @@ static int ath11k_pci_alloc_msi(struct a + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, + &ab->pci.msi.addr_lo); + ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 17, 0)) + if (msi_desc->pci.msi_attrib.is_64) { ++#else ++ if (msi_desc->msi_attrib.is_64) { ++#endif + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, + &ab->pci.msi.addr_hi); + } else { diff --git a/package/kernel/mac80211/patches/build/100-backports-drop-QRTR-and-MHI.patch b/package/kernel/mac80211/patches/build/100-backports-drop-QRTR-and-MHI.patch new file mode 100644 index 00000000000..b017a0ce146 --- /dev/null +++ b/package/kernel/mac80211/patches/build/100-backports-drop-QRTR-and-MHI.patch @@ -0,0 +1,76 @@ +From 54e0f9aaf340377fb76acdffee9ec7372c4b70ae Mon Sep 17 00:00:00 2001 +From: Robert Marko +Date: Mon, 17 Oct 2022 11:35:36 +0200 +Subject: [PATCH] backports: drop QRTR and MHI + +Backports currently include QRTR and MHI due to ath11k-pci requiring them, +however this at the same time prevents us from adding ath11k-ahb as it +also requires QRTR however its AHB variant from the kernel will conflict +with the core provided by backports. + +Since MHI also conflicts with existing OpenWrt kmods providing MHI drop +both from backports and use the ones provided by OpenWrt kernel. + +Signed-off-by: Robert Marko +--- + Kconfig.sources | 2 -- + Makefile.kernel | 2 -- + drivers/net/wireless/ath/ath11k/Kconfig | 6 +++--- + local-symbols | 8 -------- + 4 files changed, 3 insertions(+), 15 deletions(-) + +--- a/Kconfig.sources ++++ b/Kconfig.sources +@@ -4,8 +4,6 @@ source "$BACKPORT_DIR/compat/Kconfig" + # these are copied from the kernel + source "$BACKPORT_DIR/net/wireless/Kconfig" + source "$BACKPORT_DIR/net/mac80211/Kconfig" +-source "$BACKPORT_DIR/net/qrtr/Kconfig" +-source "$BACKPORT_DIR/drivers/bus/mhi/Kconfig" + source "$BACKPORT_DIR/drivers/soc/qcom/Kconfig" + source "$BACKPORT_DIR/drivers/net/wireless/Kconfig" + source "$BACKPORT_DIR/drivers/net/usb/Kconfig" +--- a/Makefile.kernel ++++ b/Makefile.kernel +@@ -39,9 +39,7 @@ obj-y += compat/ + + obj-$(CPTCFG_CFG80211) += net/wireless/ + obj-$(CPTCFG_MAC80211) += net/mac80211/ +-obj-$(CPTCFG_QRTR) += net/qrtr/ + obj-$(CPTCFG_QCOM_QMI_HELPERS) += drivers/soc/qcom/ +-obj-$(CPTCFG_MHI_BUS) += drivers/bus/mhi/ + obj-$(CPTCFG_WLAN) += drivers/net/wireless/ + obj-$(CPTCFG_USB_NET_RNDIS_WLAN) += drivers/net/usb/ + +--- a/drivers/net/wireless/ath/ath11k/Kconfig ++++ b/drivers/net/wireless/ath/ath11k/Kconfig +@@ -25,9 +25,9 @@ config ATH11K_PCI + tristate "Atheros ath11k PCI support" + depends on m + depends on ATH11K && PCI +- select MHI_BUS +- select QRTR +- select QRTR_MHI ++ depends on MHI_BUS ++ depends on QRTR ++ depends on QRTR_MHI + help + This module adds support for PCIE bus + +--- a/local-symbols ++++ b/local-symbols +@@ -65,14 +65,6 @@ MAC80211_MESH_PS_DEBUG= + MAC80211_TDLS_DEBUG= + MAC80211_DEBUG_COUNTERS= + MAC80211_STA_HASH_MAX_SIZE= +-QRTR= +-QRTR_SMD= +-QRTR_TUN= +-QRTR_MHI= +-MHI_BUS= +-MHI_BUS_DEBUG= +-MHI_BUS_PCI_GENERIC= +-MHI_BUS_EP= + QCOM_AOSS_QMP= + QCOM_COMMAND_DB= + QCOM_CPR= From 1bff0752bd5e7feb0f311224a09b3bf217a9aeb3 Mon Sep 17 00:00:00 2001 From: Michael Pratt Date: Tue, 22 Nov 2022 00:37:39 +0000 Subject: [PATCH 103/110] image: fix device profile specific COMPILE targets Commit a01d23e75 ("image: always rebuild kernel loaders") is a step in the right direction, but exposed some issues and regressions in the makefile. Some of the files made by device specific COMPILE targets start with an "append" command (i.e. >> instead of > redirection) and if the file already exists, the target file is the input to itself before the first recipe-specified input. Fixes: a01d23e75 ("image: always rebuild kernel loaders") Fixes: a7fb589e8 ("image: always rebuild kernel loaders") Signed-off-by: Michael Pratt --- include/image.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/include/image.mk b/include/image.mk index e9dc53f82ea..5d9d4acb414 100644 --- a/include/image.mk +++ b/include/image.mk @@ -520,6 +520,7 @@ define Device/Build/compile $$(_COMPILE_TARGET): $(KDIR)/$(1) $(eval $(call Device/Export,$(KDIR)/$(1))) $(KDIR)/$(1): FORCE + rm -f $(KDIR)/$(1) $$(call concat_cmd,$(COMPILE/$(1))) endef From 17c6fb1054e3dde8fa573195acaac42a5edf0942 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Sat, 3 Dec 2022 15:25:12 +0100 Subject: [PATCH 104/110] ath79: image: don't depend on other COMPILE targets A device COMPILE target should not depend on another COMPILE. Otherwise race condition may happen. The loader is very small. Compiling it twice shouldn't have a huge impact. Signed-off-by: Alexander Couzens --- target/linux/ath79/image/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/ath79/image/Makefile b/target/linux/ath79/image/Makefile index 712ab941e18..8328be7667c 100644 --- a/target/linux/ath79/image/Makefile +++ b/target/linux/ath79/image/Makefile @@ -83,7 +83,7 @@ define Device/loader-okli-uimage LOADER_TYPE := bin COMPILE := loader-$(1).bin loader-$(1).uImage COMPILE/loader-$(1).bin := loader-okli-compile - COMPILE/loader-$(1).uImage := append-loader-okli $(1) | pad-to 64k | \ + COMPILE/loader-$(1).uImage := loader-okli-compile | pad-to 64k | \ lzma | uImage lzma endef From 6d1df35747d7f02f52973ba5604642dcc17ddf5f Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 15 Sep 2022 00:48:45 -0700 Subject: [PATCH 105/110] hostapd: add mbedtls variant This adds the current WIP mbedtls patches for hostapd. The motivation here is to reduce size. Signed-off-by: Rosen Penev --- package/network/services/hostapd/Config.in | 18 +- package/network/services/hostapd/Makefile | 121 +- ...edtls-TLS-crypto-option-initial-port.patch | 8051 +++++++++++++++++ .../patches/120-mbedtls-fips186_2_prf.patch | 114 + ...otate-with-TEST_FAIL-for-hwsim-tests.patch | 421 + ...efile-make-run-tests-with-CONFIG_TLS.patch | 1358 +++ ...hecks-encountered-during-tests-hwsim.patch | 45 + ...-dpp_pkex-EC-point-mul-w-value-prime.patch | 26 + ...70-DPP-fix-memleak-of-intro.peer_key.patch | 32 + .../hostapd/patches/200-multicall.patch | 8 +- .../patches/500-lto-jobserver-support.patch | 6 +- 11 files changed, 10188 insertions(+), 12 deletions(-) create mode 100644 package/network/services/hostapd/patches/110-mbedtls-TLS-crypto-option-initial-port.patch create mode 100644 package/network/services/hostapd/patches/120-mbedtls-fips186_2_prf.patch create mode 100644 package/network/services/hostapd/patches/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch create mode 100644 package/network/services/hostapd/patches/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch create mode 100644 package/network/services/hostapd/patches/150-add-NULL-checks-encountered-during-tests-hwsim.patch create mode 100644 package/network/services/hostapd/patches/160-dpp_pkex-EC-point-mul-w-value-prime.patch create mode 100644 package/network/services/hostapd/patches/170-DPP-fix-memleak-of-intro.peer_key.patch diff --git a/package/network/services/hostapd/Config.in b/package/network/services/hostapd/Config.in index dd8b9b4de71..8f28eb2bd4f 100644 --- a/package/network/services/hostapd/Config.in +++ b/package/network/services/hostapd/Config.in @@ -4,20 +4,25 @@ config WPA_RFKILL_SUPPORT depends on PACKAGE_wpa-supplicant || \ PACKAGE_wpa-supplicant-openssl || \ PACKAGE_wpa-supplicant-wolfssl || \ + PACKAGE_wpa-supplicant-mbedtls || \ PACKAGE_wpa-supplicant-mesh-openssl || \ PACKAGE_wpa-supplicant-mesh-wolfssl || \ + PACKAGE_wpa-supplicant-mesh-mbedtls || \ PACKAGE_wpa-supplicant-basic || \ PACKAGE_wpa-supplicant-mini || \ PACKAGE_wpa-supplicant-p2p || \ PACKAGE_wpad || \ PACKAGE_wpad-openssl || \ PACKAGE_wpad-wolfssl || \ + PACKAGE_wpad-mbedtls || \ PACKAGE_wpad-basic || \ PACKAGE_wpad-basic-openssl || \ PACKAGE_wpad-basic-wolfssl || \ + PACKAGE_wpad-basic-mbedtls || \ PACKAGE_wpad-mini || \ PACKAGE_wpad-mesh-openssl || \ - PACKAGE_wpad-mesh-wolfssl + PACKAGE_wpad-mesh-wolfssl || \ + PACKAGE_wpad-mesh-mbedtls default n config WPA_MSG_MIN_PRIORITY @@ -25,20 +30,25 @@ config WPA_MSG_MIN_PRIORITY depends on PACKAGE_wpa-supplicant || \ PACKAGE_wpa-supplicant-openssl || \ PACKAGE_wpa-supplicant-wolfssl || \ + PACKAGE_wpa-supplicant-mbedtls || \ PACKAGE_wpa-supplicant-mesh-openssl || \ PACKAGE_wpa-supplicant-mesh-wolfssl || \ + PACKAGE_wpa-supplicant-mesh-mbedtls || \ PACKAGE_wpa-supplicant-basic || \ PACKAGE_wpa-supplicant-mini || \ PACKAGE_wpa-supplicant-p2p || \ PACKAGE_wpad || \ PACKAGE_wpad-openssl || \ PACKAGE_wpad-wolfssl || \ + PACKAGE_wpad-mbedtls || \ PACKAGE_wpad-basic || \ PACKAGE_wpad-basic-openssl || \ PACKAGE_wpad-basic-wolfssl || \ + PACKAGE_wpad-basic-mbedtls || \ PACKAGE_wpad-mini || \ PACKAGE_wpad-mesh-openssl || \ - PACKAGE_wpad-mesh-wolfssl + PACKAGE_wpad-mesh-wolfssl || \ + PACKAGE_wpad-mesh-mbedtls default 3 help Useful values are: @@ -91,9 +101,11 @@ config WPA_MBO_SUPPORT default PACKAGE_wpa-supplicant || \ PACKAGE_wpa-supplicant-openssl || \ PACKAGE_wpa-supplicant-wolfssl || \ + PACKAGE_wpa-supplicant-mbedtls || \ PACKAGE_wpad || \ PACKAGE_wpad-openssl || \ - PACKAGE_wpad-wolfssl + PACKAGE_wpad-wolfssl || \ + PACKAGE_wpad-mbedtls help Multi Band Operation aka (Agile Multiband) enables features that facilitate efficient use of multiple frequency bands. diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile index 4fa9a6ae46f..829879f7630 100644 --- a/package/network/services/hostapd/Makefile +++ b/package/network/services/hostapd/Makefile @@ -52,14 +52,16 @@ LOCAL_AND_LIB_VARIANT=$(patsubst hostapd-%,%,\ LOCAL_VARIANT=$(patsubst %-internal,%,\ $(patsubst %-openssl,%,\ $(patsubst %-wolfssl,%,\ + $(patsubst %-mbedtls,%,\ $(LOCAL_AND_LIB_VARIANT)\ - ))) + )))) SSL_VARIANT=$(strip \ $(if $(findstring openssl,$(LOCAL_AND_LIB_VARIANT)),openssl,\ $(if $(findstring wolfssl,$(LOCAL_AND_LIB_VARIANT)),wolfssl,\ + $(if $(findstring mbedtls,$(LOCAL_AND_LIB_VARIANT)),mbedtls,\ internal\ - ))) + )))) CONFIG_VARIANT:=$(LOCAL_VARIANT) ifeq ($(LOCAL_VARIANT),mesh) @@ -116,6 +118,21 @@ ifeq ($(SSL_VARIANT),wolfssl) endif endif +ifeq ($(SSL_VARIANT),mbedtls) + DRIVER_MAKEOPTS += CONFIG_TLS=mbedtls CONFIG_SAE=y + TARGET_LDFLAGS += -lmbedcrypto -lmbedx509 -lmbedtls + + ifeq ($(LOCAL_VARIANT),basic) + DRIVER_MAKEOPTS += CONFIG_OWE=y + endif + ifeq ($(LOCAL_VARIANT),mesh) + DRIVER_MAKEOPTS += CONFIG_AP=y CONFIG_MESH=y CONFIG_WPS_NFC=1 + endif + ifeq ($(LOCAL_VARIANT),full) + DRIVER_MAKEOPTS += CONFIG_OWE=y CONFIG_SUITEB192=y CONFIG_AP=y CONFIG_MESH=y CONFIG_WPS_NFC=1 + endif +endif + ifneq ($(LOCAL_TYPE),hostapd) ifdef CONFIG_WPA_RFKILL_SUPPORT DRIVER_MAKEOPTS += NEED_RFKILL=y @@ -173,6 +190,15 @@ endef Package/hostapd-wolfssl/description = $(Package/hostapd/description) +define Package/hostapd-mbedtls +$(call Package/hostapd/Default,$(1)) + TITLE+= (mbedTLS full) + VARIANT:=full-mbedtls + DEPENDS+=+PACKAGE_hostapd-mbedtls:libmbedtls +endef + +Package/hostapd-mbedtls/description = $(Package/hostapd/description) + define Package/hostapd-basic $(call Package/hostapd/Default,$(1)) TITLE+= (WPA-PSK, 11r, 11w) @@ -205,6 +231,17 @@ define Package/hostapd-basic-wolfssl/description This package contains a basic IEEE 802.1x/WPA Authenticator with WPA-PSK, 802.11r and 802.11w support. endef +define Package/hostapd-basic-mbedtls +$(call Package/hostapd/Default,$(1)) + TITLE+= (WPA-PSK, 11r and 11w) + VARIANT:=basic-mbedtls + DEPENDS+=+PACKAGE_hostapd-basic-mbedtls:libmbedtls +endef + +define Package/hostapd-basic-mbedtls/description + This package contains a basic IEEE 802.1x/WPA Authenticator with WPA-PSK, 802.11r and 802.11w support. +endef + define Package/hostapd-mini $(call Package/hostapd/Default,$(1)) TITLE+= (WPA-PSK only) @@ -260,6 +297,15 @@ endef Package/wpad-wolfssl/description = $(Package/wpad/description) +define Package/wpad-mbedtls +$(call Package/wpad/Default,$(1)) + TITLE+= (mbedTLS full) + VARIANT:=wpad-full-mbedtls + DEPENDS+=+PACKAGE_wpad-mbedtls:libmbedtls +endef + +Package/wpad-mbedtls/description = $(Package/wpad/description) + define Package/wpad-basic $(call Package/wpad/Default,$(1)) TITLE+= (WPA-PSK, 11r, 11w) @@ -292,6 +338,17 @@ define Package/wpad-basic-wolfssl/description This package contains a basic IEEE 802.1x/WPA Authenticator and Supplicant with WPA-PSK, SAE (WPA3-Personal), 802.11r and 802.11w support. endef +define Package/wpad-basic-mbedtls +$(call Package/wpad/Default,$(1)) + TITLE+= (mbedTLS, 11r, 11w) + VARIANT:=wpad-basic-mbedtls + DEPENDS+=+PACKAGE_wpad-basic-mbedtls:libmbedtls +endef + +define Package/wpad-basic-mbedtls/description + This package contains a basic IEEE 802.1x/WPA Authenticator and Supplicant with WPA-PSK, SAE (WPA3-Personal), 802.11r and 802.11w support. +endef + define Package/wpad-mini $(call Package/wpad/Default,$(1)) TITLE+= (WPA-PSK only) @@ -330,6 +387,15 @@ endef Package/wpad-mesh-wolfssl/description = $(Package/wpad-mesh/description) +define Package/wpad-mesh-mbedtls +$(call Package/wpad-mesh,$(1)) + TITLE+= (mbedTLS, 11s, SAE) + DEPENDS+=+PACKAGE_wpad-mesh-mbedtls:libmbedtls + VARIANT:=wpad-mesh-mbedtls +endef + +Package/wpad-mesh-mbedtls/description = $(Package/wpad-mesh/description) + define Package/wpa-supplicant/Default SECTION:=net @@ -365,6 +431,13 @@ $(call Package/wpa-supplicant/Default,$(1)) DEPENDS+=+PACKAGE_wpa-supplicant-wolfssl:libwolfssl endef +define Package/wpa-supplicant-mbedtls +$(call Package/wpa-supplicant/Default,$(1)) + TITLE+= (mbedTLS full) + VARIANT:=supplicant-full-mbedtls + DEPENDS+=+PACKAGE_wpa-supplicant-mbedtls:libmbedtls +endef + define Package/wpa-supplicant/config source "$(SOURCE)/Config.in" endef @@ -396,6 +469,13 @@ $(call Package/wpa-supplicant-mesh/Default,$(1)) DEPENDS+=+PACKAGE_wpa-supplicant-mesh-wolfssl:libwolfssl endef +define Package/wpa-supplicant-mesh-mbedtls +$(call Package/wpa-supplicant-mesh/Default,$(1)) + TITLE+= (mbedTLS, 11s, SAE) + VARIANT:=supplicant-mesh-mbedtls + DEPENDS+=+PACKAGE_wpa-supplicant-mesh-mbedtls:libmbedtls +endef + define Package/wpa-supplicant-basic $(call Package/wpa-supplicant/Default,$(1)) TITLE+= (11r, 11w) @@ -472,6 +552,15 @@ define Package/eapol-test-wolfssl PROVIDES:=eapol-test endef +define Package/eapol-test-mbedtls + $(call Package/eapol-test/Default,$(1)) + TITLE+= (mbedTLS full) + VARIANT:=supplicant-full-mbedtls + CONFLICTS:=$(filter-out eapol-test-openssl ,$(filter-out eapol-test-mbedtls ,$(EAPOL_TEST_PROVIDERS))) + DEPENDS+=+PACKAGE_eapol-test-mbedtls:libmbedtls + PROVIDES:=eapol-test +endef + ifneq ($(wildcard $(PKG_BUILD_DIR)/.config_*),$(subst .configured_,.config_,$(STAMP_CONFIGURED))) define Build/Configure/rebuild @@ -580,6 +669,12 @@ define Build/Compile/supplicant-full-wolfssl ) endef +define Build/Compile/supplicant-full-mbedtls + +$(call Build/RunMake,wpa_supplicant, \ + eapol_test \ + ) +endef + define Build/Compile $(Build/Compile/$(LOCAL_TYPE)) $(Build/Compile/$(BUILD_VARIANT)) @@ -610,9 +705,11 @@ endef Package/hostapd-basic/install = $(Package/hostapd/install) Package/hostapd-basic-openssl/install = $(Package/hostapd/install) Package/hostapd-basic-wolfssl/install = $(Package/hostapd/install) +Package/hostapd-basic-mbedtls/install = $(Package/hostapd/install) Package/hostapd-mini/install = $(Package/hostapd/install) Package/hostapd-openssl/install = $(Package/hostapd/install) Package/hostapd-wolfssl/install = $(Package/hostapd/install) +Package/hostapd-mbedtls/install = $(Package/hostapd/install) ifneq ($(LOCAL_TYPE),supplicant) define Package/hostapd-utils/install @@ -631,11 +728,14 @@ endef Package/wpad-basic/install = $(Package/wpad/install) Package/wpad-basic-openssl/install = $(Package/wpad/install) Package/wpad-basic-wolfssl/install = $(Package/wpad/install) +Package/wpad-basic-mbedtls/install = $(Package/wpad/install) Package/wpad-mini/install = $(Package/wpad/install) Package/wpad-openssl/install = $(Package/wpad/install) Package/wpad-wolfssl/install = $(Package/wpad/install) +Package/wpad-mbedtls/install = $(Package/wpad/install) Package/wpad-mesh-openssl/install = $(Package/wpad/install) Package/wpad-mesh-wolfssl/install = $(Package/wpad/install) +Package/wpad-mesh-mbedtls/install = $(Package/wpad/install) define Package/wpa-supplicant/install $(call Install/supplicant,$(1)) @@ -646,8 +746,10 @@ Package/wpa-supplicant-mini/install = $(Package/wpa-supplicant/install) Package/wpa-supplicant-p2p/install = $(Package/wpa-supplicant/install) Package/wpa-supplicant-openssl/install = $(Package/wpa-supplicant/install) Package/wpa-supplicant-wolfssl/install = $(Package/wpa-supplicant/install) +Package/wpa-supplicant-mbedtls/install = $(Package/wpa-supplicant/install) Package/wpa-supplicant-mesh-openssl/install = $(Package/wpa-supplicant/install) Package/wpa-supplicant-mesh-wolfssl/install = $(Package/wpa-supplicant/install) +Package/wpa-supplicant-mesh-mbedtls/install = $(Package/wpa-supplicant/install) ifneq ($(LOCAL_TYPE),hostapd) define Package/wpa-cli/install @@ -677,6 +779,13 @@ ifeq ($(BUILD_VARIANT),supplicant-full-wolfssl) endef endif +ifeq ($(BUILD_VARIANT),supplicant-full-mbedtls) + define Package/eapol-test-mbedtls/install + $(INSTALL_DIR) $(1)/usr/sbin + $(CP) $(PKG_BUILD_DIR)/wpa_supplicant/eapol_test $(1)/usr/sbin/ + endef +endif + # Build hostapd-common before its dependents, to avoid # spurious rebuilds when building multiple variants. $(eval $(call BuildPackage,hostapd-common)) @@ -684,28 +793,36 @@ $(eval $(call BuildPackage,hostapd)) $(eval $(call BuildPackage,hostapd-basic)) $(eval $(call BuildPackage,hostapd-basic-openssl)) $(eval $(call BuildPackage,hostapd-basic-wolfssl)) +$(eval $(call BuildPackage,hostapd-basic-mbedtls)) $(eval $(call BuildPackage,hostapd-mini)) $(eval $(call BuildPackage,hostapd-openssl)) $(eval $(call BuildPackage,hostapd-wolfssl)) +$(eval $(call BuildPackage,hostapd-mbedtls)) $(eval $(call BuildPackage,wpad)) $(eval $(call BuildPackage,wpad-mesh-openssl)) $(eval $(call BuildPackage,wpad-mesh-wolfssl)) +$(eval $(call BuildPackage,wpad-mesh-mbedtls)) $(eval $(call BuildPackage,wpad-basic)) $(eval $(call BuildPackage,wpad-basic-openssl)) $(eval $(call BuildPackage,wpad-basic-wolfssl)) +$(eval $(call BuildPackage,wpad-basic-mbedtls)) $(eval $(call BuildPackage,wpad-mini)) $(eval $(call BuildPackage,wpad-openssl)) $(eval $(call BuildPackage,wpad-wolfssl)) +$(eval $(call BuildPackage,wpad-mbedtls)) $(eval $(call BuildPackage,wpa-supplicant)) $(eval $(call BuildPackage,wpa-supplicant-mesh-openssl)) $(eval $(call BuildPackage,wpa-supplicant-mesh-wolfssl)) +$(eval $(call BuildPackage,wpa-supplicant-mesh-mbedtls)) $(eval $(call BuildPackage,wpa-supplicant-basic)) $(eval $(call BuildPackage,wpa-supplicant-mini)) $(eval $(call BuildPackage,wpa-supplicant-p2p)) $(eval $(call BuildPackage,wpa-supplicant-openssl)) $(eval $(call BuildPackage,wpa-supplicant-wolfssl)) +$(eval $(call BuildPackage,wpa-supplicant-mbedtls)) $(eval $(call BuildPackage,wpa-cli)) $(eval $(call BuildPackage,hostapd-utils)) $(eval $(call BuildPackage,eapol-test)) $(eval $(call BuildPackage,eapol-test-openssl)) $(eval $(call BuildPackage,eapol-test-wolfssl)) +$(eval $(call BuildPackage,eapol-test-mbedtls)) diff --git a/package/network/services/hostapd/patches/110-mbedtls-TLS-crypto-option-initial-port.patch b/package/network/services/hostapd/patches/110-mbedtls-TLS-crypto-option-initial-port.patch new file mode 100644 index 00000000000..01af14b71eb --- /dev/null +++ b/package/network/services/hostapd/patches/110-mbedtls-TLS-crypto-option-initial-port.patch @@ -0,0 +1,8051 @@ +From e16f200dc1d2f69efc78c7c55af0d7b410a981f9 Mon Sep 17 00:00:00 2001 +From: Glenn Strauss +Date: Tue, 5 Jul 2022 02:49:50 -0400 +Subject: [PATCH 1/7] mbedtls: TLS/crypto option (initial port) + +Signed-off-by: Glenn Strauss +--- + hostapd/Makefile | 91 + + hostapd/defconfig | 15 +- + src/crypto/crypto_mbedtls.c | 4043 +++++++++++++++++ + src/crypto/tls_mbedtls.c | 3313 ++++++++++++++ + .../build/build-wpa_supplicant-mbedtls.config | 24 + + tests/hwsim/example-hostapd.config | 4 + + tests/hwsim/example-wpa_supplicant.config | 4 + + wpa_supplicant/Makefile | 74 + + wpa_supplicant/defconfig | 6 +- + 9 files changed, 7571 insertions(+), 3 deletions(-) + create mode 100644 src/crypto/crypto_mbedtls.c + create mode 100644 src/crypto/tls_mbedtls.c + create mode 100644 tests/build/build-wpa_supplicant-mbedtls.config + +--- a/hostapd/Makefile ++++ b/hostapd/Makefile +@@ -745,6 +745,40 @@ endif + CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\" + endif + ++ifeq ($(CONFIG_TLS), mbedtls) ++ifndef CONFIG_CRYPTO ++CONFIG_CRYPTO=mbedtls ++endif ++ifdef TLS_FUNCS ++OBJS += ../src/crypto/tls_mbedtls.o ++LIBS += -lmbedtls ++ifndef CONFIG_DPP ++LIBS += -lmbedx509 ++endif ++endif ++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ++HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ++SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ++ifdef NEED_FIPS186_2_PRF ++OBJS += ../src/crypto/fips_prf_internal.o ++SHA1OBJS += ../src/crypto/sha1-internal.o ++endif ++ifeq ($(CONFIG_CRYPTO), mbedtls) ++ifdef CONFIG_DPP ++LIBS += -lmbedx509 ++LIBS_h += -lmbedx509 ++LIBS_n += -lmbedx509 ++LIBS_s += -lmbedx509 ++endif ++LIBS += -lmbedcrypto ++LIBS_h += -lmbedcrypto ++LIBS_n += -lmbedcrypto ++LIBS_s += -lmbedcrypto ++# XXX: create a config option? ++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 ++endif ++endif ++ + ifeq ($(CONFIG_TLS), gnutls) + ifndef CONFIG_CRYPTO + # default to libgcrypt +@@ -924,9 +958,11 @@ endif + + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-wrap.o + endif + endif ++endif + ifdef NEED_AES_EAX + AESOBJS += ../src/crypto/aes-eax.o + NEED_AES_CTR=y +@@ -936,38 +972,48 @@ AESOBJS += ../src/crypto/aes-siv.o + NEED_AES_CTR=y + endif + ifdef NEED_AES_CTR ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-ctr.o + endif ++endif + ifdef NEED_AES_ENCBLOCK ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-encblock.o + endif ++endif + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-omac1.o + endif + endif + endif ++endif + ifdef NEED_AES_UNWRAP + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + NEED_AES_DEC=y + AESOBJS += ../src/crypto/aes-unwrap.o + endif + endif + endif + endif ++endif + ifdef NEED_AES_CBC + NEED_AES_DEC=y + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-cbc.o + endif + endif + endif + endif ++endif + ifdef NEED_AES_DEC + ifdef CONFIG_INTERNAL_AES + AESOBJS += ../src/crypto/aes-internal-dec.o +@@ -982,12 +1028,16 @@ ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1.o + endif + endif + endif + endif ++endif ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-prf.o ++endif + ifdef CONFIG_INTERNAL_SHA1 + SHA1OBJS += ../src/crypto/sha1-internal.o + ifdef NEED_FIPS186_2_PRF +@@ -996,16 +1046,22 @@ endif + endif + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-pbkdf2.o + endif + endif ++endif + ifdef NEED_T_PRF ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-tprf.o + endif ++endif + ifdef NEED_TLS_PRF ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-tlsprf.o + endif + endif ++endif + + ifdef NEED_SHA1 + OBJS += $(SHA1OBJS) +@@ -1015,11 +1071,13 @@ ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/md5.o + endif + endif + endif + endif ++endif + + ifdef NEED_MD5 + ifdef CONFIG_INTERNAL_MD5 +@@ -1058,56 +1116,81 @@ ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha256.o + endif + endif + endif + endif ++endif ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha256-prf.o ++endif + ifdef CONFIG_INTERNAL_SHA256 + OBJS += ../src/crypto/sha256-internal.o + endif + ifdef NEED_TLS_PRF_SHA256 ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha256-tlsprf.o + endif ++endif + ifdef NEED_TLS_PRF_SHA384 ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha384-tlsprf.o + endif ++endif + ifdef NEED_HMAC_SHA256_KDF ++CFLAGS += -DCONFIG_HMAC_SHA256_KDF ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha256-kdf.o + endif ++endif + ifdef NEED_HMAC_SHA384_KDF ++CFLAGS += -DCONFIG_HMAC_SHA384_KDF ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha384-kdf.o + endif ++endif + ifdef NEED_HMAC_SHA512_KDF ++CFLAGS += -DCONFIG_HMAC_SHA512_KDF ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha512-kdf.o + endif ++endif + ifdef NEED_SHA384 + CFLAGS += -DCONFIG_SHA384 + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha384.o + endif + endif + endif + endif ++endif ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha384-prf.o + endif ++endif + ifdef NEED_SHA512 + CFLAGS += -DCONFIG_SHA512 + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha512.o + endif + endif + endif + endif ++endif ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha512-prf.o + endif ++endif + + ifdef CONFIG_INTERNAL_SHA384 + CFLAGS += -DCONFIG_INTERNAL_SHA384 +@@ -1152,11 +1235,13 @@ HOBJS += $(SHA1OBJS) + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + HOBJS += ../src/crypto/md5.o + endif + endif + endif + endif ++endif + + ifdef CONFIG_RADIUS_SERVER + CFLAGS += -DRADIUS_SERVER +@@ -1327,7 +1412,9 @@ NOBJS += ../src/utils/trace.o + endif + + HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o ++ifneq ($(CONFIG_TLS), mbedtls) + HOBJS += ../src/crypto/aes-encblock.o ++endif + ifdef CONFIG_INTERNAL_AES + HOBJS += ../src/crypto/aes-internal.o + HOBJS += ../src/crypto/aes-internal-enc.o +@@ -1350,13 +1437,17 @@ SOBJS += ../src/common/sae.o + SOBJS += ../src/common/sae_pk.o + SOBJS += ../src/common/dragonfly.o + SOBJS += $(AESOBJS) ++ifneq ($(CONFIG_TLS), mbedtls) + SOBJS += ../src/crypto/sha256-prf.o + SOBJS += ../src/crypto/sha384-prf.o + SOBJS += ../src/crypto/sha512-prf.o ++endif + SOBJS += ../src/crypto/dh_groups.o ++ifneq ($(CONFIG_TLS), mbedtls) + SOBJS += ../src/crypto/sha256-kdf.o + SOBJS += ../src/crypto/sha384-kdf.o + SOBJS += ../src/crypto/sha512-kdf.o ++endif + + _OBJS_VAR := NOBJS + include ../src/objs.mk +--- a/hostapd/defconfig ++++ b/hostapd/defconfig +@@ -6,9 +6,21 @@ + # just setting VARIABLE=n is not disabling that variable. + # + # This file is included in Makefile, so variables like CFLAGS and LIBS can also +-# be modified from here. In most cass, these lines should use += in order not ++# be modified from here. In most cases, these lines should use += in order not + # to override previous values of the variables. + ++ ++# Uncomment following two lines and fix the paths if you have installed TLS ++# libraries in a non-default location ++#CFLAGS += -I/usr/local/openssl/include ++#LIBS += -L/usr/local/openssl/lib ++ ++# Some Red Hat versions seem to include kerberos header files from OpenSSL, but ++# the kerberos files are not in the default include path. Following line can be ++# used to fix build issues on such systems (krb5.h not found). ++#CFLAGS += -I/usr/include/kerberos ++ ++ + # Driver interface for Host AP driver + CONFIG_DRIVER_HOSTAP=y + +@@ -278,6 +290,7 @@ CONFIG_IPV6=y + # openssl = OpenSSL (default) + # gnutls = GnuTLS + # internal = Internal TLSv1 implementation (experimental) ++# mbedtls = mbed TLS + # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental) + # none = Empty template + #CONFIG_TLS=openssl +--- /dev/null ++++ b/src/crypto/crypto_mbedtls.c +@@ -0,0 +1,4043 @@ ++/* ++ * crypto wrapper functions for mbed TLS ++ * ++ * SPDX-FileCopyrightText: 2022 Glenn Strauss ++ * SPDX-License-Identifier: BSD-3-Clause ++ */ ++ ++#include "utils/includes.h" ++#include "utils/common.h" ++ ++#include ++#include ++#include ++#include /* mbedtls_platform_zeroize() */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef MBEDTLS_PRIVATE ++#define MBEDTLS_PRIVATE(x) x ++#endif ++ ++/* hostapd/wpa_supplicant provides forced_memzero(), ++ * but prefer mbedtls_platform_zeroize() */ ++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz) ++ ++#ifndef __has_attribute ++#define __has_attribute(x) 0 ++#endif ++ ++#ifndef __GNUC_PREREQ ++#define __GNUC_PREREQ(maj,min) 0 ++#endif ++ ++#ifndef __attribute_cold__ ++#if __has_attribute(cold) \ ++ || __GNUC_PREREQ(4,3) ++#define __attribute_cold__ __attribute__((__cold__)) ++#else ++#define __attribute_cold__ ++#endif ++#endif ++ ++#ifndef __attribute_noinline__ ++#if __has_attribute(noinline) \ ++ || __GNUC_PREREQ(3,1) ++#define __attribute_noinline__ __attribute__((__noinline__)) ++#else ++#define __attribute_noinline__ ++#endif ++#endif ++ ++#include "crypto.h" ++#include "aes_wrap.h" ++#include "aes.h" ++#include "md5.h" ++#include "sha1.h" ++#include "sha256.h" ++#include "sha384.h" ++#include "sha512.h" ++ ++ ++/* ++ * selective code inclusion based on preprocessor defines ++ * ++ * future: additional code could be wrapped with preprocessor checks if ++ * wpa_supplicant/Makefile and hostap/Makefile were more consistent with ++ * setting preprocessor defines for named groups of functionality ++ */ ++ ++#if defined(CONFIG_FIPS) ++#undef MBEDTLS_MD4_C /* omit md4_vector() */ ++#undef MBEDTLS_MD5_C /* omit md5_vector() hmac_md5_vector() hmac_md5() */ ++#undef MBEDTLS_DES_C /* omit des_encrypt() */ ++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */ ++#define CRYPTO_MBEDTLS_CONFIG_FIPS ++#endif ++ ++#if !defined(CONFIG_FIPS) ++#if defined(EAP_PWD) \ ++ || defined(EAP_LEAP) || defined(EAP_LEAP_DYNAMIC) \ ++ || defined(EAP_TTLS) || defined(EAP_TTLS_DYNAMIC) \ ++ || defined(EAP_MSCHAPv2) || defined(EAP_MSCHAPv2_DYNAMIC) \ ++ || defined(EAP_SERVER_MSCHAPV2) ++#ifndef MBEDTLS_MD4_C /* (MD4 not in mbedtls 3.x) */ ++#include "md4-internal.c"/* pull in hostap local implementation */ ++#endif /* md4_vector() */ ++#else ++#undef MBEDTLS_MD4_C /* omit md4_vector() */ ++#endif ++#endif ++ ++#if !defined(CONFIG_NO_RC4) && !defined(CONFIG_NO_WPA) ++#ifndef MBEDTLS_ARC4_C /* (RC4 not in mbedtls 3.x) */ ++#include "rc4.c" /* pull in hostap local implementation */ ++#endif /* rc4_skip() */ ++#else ++#undef MBEDTLS_ARC4_C /* omit rc4_skip() */ ++#endif ++ ++#if defined(CONFIG_MACSEC) \ ++ || defined(CONFIG_NO_RADIUS) \ ++ || defined(CONFIG_IEEE80211R) \ ++ || defined(EAP_SERVER_FAST) \ ++ || defined(EAP_SERVER_TEAP) \ ++ || !defined(CONFIG_NO_WPA) ++ /* aes_wrap() aes_unwrap() */ ++#else ++#undef MBEDTLS_NIST_KW_C /* omit aes_wrap() aes_unwrap() */ ++#endif ++ ++#if !defined(CONFIG_SHA256) ++#undef MBEDTLS_SHA256_C ++#endif ++ ++#if !defined(CONFIG_SHA384) && !defined(CONFIG_SHA512) ++#undef MBEDTLS_SHA512_C ++#endif ++ ++#if defined(CONFIG_HMAC_SHA256_KDF) ++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA256 ++#endif ++#if defined(CONFIG_HMAC_SHA384_KDF) ++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA384 ++#endif ++#if defined(CONFIG_HMAC_SHA512_KDF) ++#define CRYPTO_MBEDTLS_HMAC_KDF_SHA512 ++#endif ++ ++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \ ++ || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST) ++#define CRYPTO_MBEDTLS_SHA1_T_PRF ++#endif ++ ++#if defined(CONFIG_DES) ++#define CRYPTO_MBEDTLS_DES_ENCRYPT ++#endif /* des_encrypt() */ ++ ++#if !defined(CONFIG_NO_PBKDF2) ++#define CRYPTO_MBEDTLS_PBKDF2_SHA1 ++#endif /* pbkdf2_sha1() */ ++ ++#if defined(EAP_IKEV2) \ ++ || defined(EAP_IKEV2_DYNAMIC) \ ++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */ ++#define CRYPTO_MBEDTLS_CRYPTO_CIPHER ++#endif /* crypto_cipher_*() */ ++ ++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ ++#define CRYPTO_MBEDTLS_CRYPTO_HASH ++#endif /* crypto_hash_*() */ ++ ++#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) /* CONFIG_EAP_PWD=y */ \ ++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */ ++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM ++#endif /* crypto_bignum_*() */ ++ ++#if defined(EAP_PWD) /* CONFIG_EAP_PWD=y */ \ ++ || defined(EAP_EKE) /* CONFIG_EAP_EKE=y */ \ ++ || defined(EAP_EKE_DYNAMIC) /* CONFIG_EAP_EKE=y */ \ ++ || defined(EAP_SERVER_EKE) /* CONFIG_EAP_EKE=y */ \ ++ || defined(EAP_IKEV2) /* CONFIG_EAP_IKEV2y */ \ ++ || defined(EAP_IKEV2_DYNAMIC)/* CONFIG_EAP_IKEV2=y */ \ ++ || defined(EAP_SERVER_IKEV2) /* CONFIG_EAP_IKEV2=y */ \ ++ || defined(CONFIG_SAE) /* CONFIG_SAE=y */ \ ++ || defined(CONFIG_WPS) /* CONFIG_WPS=y */ ++#define CRYPTO_MBEDTLS_CRYPTO_DH ++#if defined(CONFIG_WPS_NFC) ++#define CRYPTO_MBEDTLS_DH5_INIT_FIXED ++#endif /* dh5_init_fixed() */ ++#endif /* crypto_dh_*() */ ++ ++#if !defined(CONFIG_NO_WPA) /* CONFIG_NO_WPA= */ ++#define CRYPTO_MBEDTLS_CRYPTO_ECDH ++#endif /* crypto_ecdh_*() */ ++ ++#if defined(CONFIG_ECC) ++#define CRYPTO_MBEDTLS_CRYPTO_BIGNUM ++#define CRYPTO_MBEDTLS_CRYPTO_EC ++#endif /* crypto_ec_*() crypto_ec_key_*() */ ++ ++#if defined(CONFIG_DPP) /* CONFIG_DPP=y */ ++#define CRYPTO_MBEDTLS_CRYPTO_EC_DPP /* extra for DPP */ ++#define CRYPTO_MBEDTLS_CRYPTO_CSR ++#endif /* crypto_csr_*() */ ++ ++#if defined(CONFIG_DPP3) /* CONFIG_DPP3=y */ ++#define CRYPTO_MBEDTLS_CRYPTO_HPKE ++#endif ++ ++#if defined(CONFIG_DPP2) /* CONFIG_DPP2=y */ ++#define CRYPTO_MBEDTLS_CRYPTO_PKCS7 ++#endif /* crypto_pkcs7_*() */ ++ ++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \ ++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) \ ++ || defined(CONFIG_AP) || defined(HOSTAPD) ++/* CONFIG_EAP_SIM=y CONFIG_EAP_AKA=y CONFIG_AP=y HOSTAPD */ ++#if defined(CRYPTO_RSA_OAEP_SHA256) ++#define CRYPTO_MBEDTLS_CRYPTO_RSA ++#endif ++#endif /* crypto_rsa_*() */ ++ ++ ++static int ctr_drbg_init_state; ++static mbedtls_ctr_drbg_context ctr_drbg; ++static mbedtls_entropy_context entropy; ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM ++#include ++static mbedtls_mpi mpi_sw_A; ++#endif ++ ++__attribute_cold__ ++__attribute_noinline__ ++static mbedtls_ctr_drbg_context * ctr_drbg_init(void) ++{ ++ mbedtls_ctr_drbg_init(&ctr_drbg); ++ mbedtls_entropy_init(&entropy); ++ if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, ++ NULL, 0)) { ++ wpa_printf(MSG_ERROR, "Init of random number generator failed"); ++ /* XXX: abort? */ ++ } ++ else ++ ctr_drbg_init_state = 1; ++ ++ return &ctr_drbg; ++} ++ ++__attribute_cold__ ++void crypto_unload(void) ++{ ++ if (ctr_drbg_init_state) { ++ mbedtls_ctr_drbg_free(&ctr_drbg); ++ mbedtls_entropy_free(&entropy); ++ #ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM ++ mbedtls_mpi_free(&mpi_sw_A); ++ #endif ++ ctr_drbg_init_state = 0; ++ } ++} ++ ++/* init ctr_drbg on first use ++ * crypto_global_init() and crypto_global_deinit() are not available here ++ * (available only when CONFIG_TLS=internal, which is not CONFIG_TLS=mbedtls) */ ++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/ ++inline ++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void) ++{ ++ return ctr_drbg_init_state ? &ctr_drbg : ctr_drbg_init(); ++} ++ ++#ifdef CRYPTO_MBEDTLS_CONFIG_FIPS ++int crypto_get_random(void *buf, size_t len) ++{ ++ return mbedtls_ctr_drbg_random(crypto_mbedtls_ctr_drbg(),buf,len) ? -1 : 0; ++} ++#endif ++ ++ ++#if 1 ++ ++/* tradeoff: slightly smaller code size here at cost of slight increase ++ * in instructions and function calls at runtime versus the expanded ++ * per-message-digest code that follows in #else (~0.5 kib .text larger) */ ++ ++__attribute_noinline__ ++static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len, ++ u8 *mac, mbedtls_md_type_t md_type) ++{ ++ mbedtls_md_context_t ctx; ++ mbedtls_md_init(&ctx); ++ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){ ++ mbedtls_md_free(&ctx); ++ return -1; ++ } ++ mbedtls_md_starts(&ctx); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_md_update(&ctx, addr[i], len[i]); ++ mbedtls_md_finish(&ctx, mac); ++ mbedtls_md_free(&ctx); ++ return 0; ++} ++ ++#ifdef MBEDTLS_SHA512_C ++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA512); ++} ++ ++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA384); ++} ++#endif ++ ++#ifdef MBEDTLS_SHA256_C ++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA256); ++} ++#endif ++ ++#ifdef MBEDTLS_SHA1_C ++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_SHA1); ++} ++#endif ++ ++#ifdef MBEDTLS_MD5_C ++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD5); ++} ++#endif ++ ++#ifdef MBEDTLS_MD4_C ++#include ++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return md_vector(num_elem, addr, len, mac, MBEDTLS_MD_MD4); ++} ++#endif ++ ++#else /* expanded per-message-digest functions */ ++ ++#ifdef MBEDTLS_SHA512_C ++#include ++__attribute_noinline__ ++static int sha384_512_vector(size_t num_elem, const u8 *addr[], ++ const size_t *len, u8 *mac, int is384) ++{ ++ struct mbedtls_sha512_context ctx; ++ mbedtls_sha512_init(&ctx); ++ #if MBEDTLS_VERSION_MAJOR >= 3 ++ mbedtls_sha512_starts(&ctx, is384); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_sha512_update(&ctx, addr[i], len[i]); ++ mbedtls_sha512_finish(&ctx, mac); ++ #else ++ mbedtls_sha512_starts_ret(&ctx, is384); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_sha512_update_ret(&ctx, addr[i], len[i]); ++ mbedtls_sha512_finish_ret(&ctx, mac); ++ #endif ++ mbedtls_sha512_free(&ctx); ++ return 0; ++} ++ ++int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return sha384_512_vector(num_elem, addr, len, mac, 0); ++} ++ ++int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return sha384_512_vector(num_elem, addr, len, mac, 1); ++} ++#endif ++ ++#ifdef MBEDTLS_SHA256_C ++#include ++int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ struct mbedtls_sha256_context ctx; ++ mbedtls_sha256_init(&ctx); ++ #if MBEDTLS_VERSION_MAJOR >= 3 ++ mbedtls_sha256_starts(&ctx, 0); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_sha256_update(&ctx, addr[i], len[i]); ++ mbedtls_sha256_finish(&ctx, mac); ++ #else ++ mbedtls_sha256_starts_ret(&ctx, 0); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_sha256_update_ret(&ctx, addr[i], len[i]); ++ mbedtls_sha256_finish_ret(&ctx, mac); ++ #endif ++ mbedtls_sha256_free(&ctx); ++ return 0; ++} ++#endif ++ ++#ifdef MBEDTLS_SHA1_C ++#include ++int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ struct mbedtls_sha1_context ctx; ++ mbedtls_sha1_init(&ctx); ++ #if MBEDTLS_VERSION_MAJOR >= 3 ++ mbedtls_sha1_starts(&ctx); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_sha1_update(&ctx, addr[i], len[i]); ++ mbedtls_sha1_finish(&ctx, mac); ++ #else ++ mbedtls_sha1_starts_ret(&ctx); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_sha1_update_ret(&ctx, addr[i], len[i]); ++ mbedtls_sha1_finish_ret(&ctx, mac); ++ #endif ++ mbedtls_sha1_free(&ctx); ++ return 0; ++} ++#endif ++ ++#ifdef MBEDTLS_MD5_C ++#include ++int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ struct mbedtls_md5_context ctx; ++ mbedtls_md5_init(&ctx); ++ #if MBEDTLS_VERSION_MAJOR >= 3 ++ mbedtls_md5_starts(&ctx); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_md5_update(&ctx, addr[i], len[i]); ++ mbedtls_md5_finish(&ctx, mac); ++ #else ++ mbedtls_md5_starts_ret(&ctx); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_md5_update_ret(&ctx, addr[i], len[i]); ++ mbedtls_md5_finish_ret(&ctx, mac); ++ #endif ++ mbedtls_md5_free(&ctx); ++ return 0; ++} ++#endif ++ ++#ifdef MBEDTLS_MD4_C ++#include ++int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ struct mbedtls_md4_context ctx; ++ mbedtls_md4_init(&ctx); ++ mbedtls_md4_starts_ret(&ctx); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_md4_update_ret(&ctx, addr[i], len[i]); ++ mbedtls_md4_finish_ret(&ctx, mac); ++ mbedtls_md4_free(&ctx); ++ return 0; ++} ++#endif ++ ++#endif /* expanded per-message-digest functions */ ++ ++ ++__attribute_noinline__ ++static int hmac_vector(const u8 *key, size_t key_len, size_t num_elem, ++ const u8 *addr[], const size_t *len, u8 *mac, ++ mbedtls_md_type_t md_type) ++{ ++ mbedtls_md_context_t ctx; ++ mbedtls_md_init(&ctx); ++ if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){ ++ mbedtls_md_free(&ctx); ++ return -1; ++ } ++ mbedtls_md_hmac_starts(&ctx, key, key_len); ++ for (size_t i = 0; i < num_elem; ++i) ++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]); ++ mbedtls_md_hmac_finish(&ctx, mac); ++ mbedtls_md_free(&ctx); ++ return 0; ++} ++ ++#ifdef MBEDTLS_SHA512_C ++int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, ++ const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return hmac_vector(key, key_len, num_elem, addr, len, mac, ++ MBEDTLS_MD_SHA512); ++} ++ ++int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, size_t data_len, ++ u8 *mac) ++{ ++ return hmac_vector(key, key_len, 1, &data, &data_len, mac, ++ MBEDTLS_MD_SHA512); ++} ++ ++int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, ++ const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return hmac_vector(key, key_len, num_elem, addr, len, mac, ++ MBEDTLS_MD_SHA384); ++} ++ ++int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len, ++ u8 *mac) ++{ ++ return hmac_vector(key, key_len, 1, &data, &data_len, mac, ++ MBEDTLS_MD_SHA384); ++} ++#endif ++ ++#ifdef MBEDTLS_SHA256_C ++int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, ++ const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return hmac_vector(key, key_len, num_elem, addr, len, mac, ++ MBEDTLS_MD_SHA256); ++} ++ ++int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, ++ u8 *mac) ++{ ++ return hmac_vector(key, key_len, 1, &data, &data_len, mac, ++ MBEDTLS_MD_SHA256); ++} ++#endif ++ ++#ifdef MBEDTLS_SHA1_C ++int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, ++ const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return hmac_vector(key, key_len, num_elem, addr, len, mac, ++ MBEDTLS_MD_SHA1); ++} ++ ++int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, ++ u8 *mac) ++{ ++ return hmac_vector(key, key_len, 1, &data, &data_len, mac, ++ MBEDTLS_MD_SHA1); ++} ++#endif ++ ++#ifdef MBEDTLS_MD5_C ++int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, ++ const u8 *addr[], const size_t *len, u8 *mac) ++{ ++ return hmac_vector(key, key_len, num_elem, addr, len, mac, ++ MBEDTLS_MD_MD5); ++} ++ ++int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, ++ u8 *mac) ++{ ++ return hmac_vector(key, key_len, 1, &data, &data_len, mac, ++ MBEDTLS_MD_MD5); ++} ++#endif ++ ++ ++#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C) ++ ++#if defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA256) \ ++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA384) \ ++ || defined(CRYPTO_MBEDTLS_HMAC_KDF_SHA512) ++ ++#include ++ ++/* sha256-kdf.c sha384-kdf.c sha512-kdf.c */ ++ ++/* HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) */ ++/* HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) */ ++/* HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) */ ++__attribute_noinline__ ++static int hmac_kdf_expand(const u8 *prk, size_t prk_len, ++ const char *label, const u8 *info, size_t info_len, ++ u8 *okm, size_t okm_len, mbedtls_md_type_t md_type) ++{ ++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); ++ #ifdef MBEDTLS_HKDF_C ++ if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */ ++ return mbedtls_hkdf_expand(md_info, prk, prk_len, info, ++ info_len, okm, okm_len) ? -1 : 0; ++ #endif ++ ++ const size_t mac_len = mbedtls_md_get_size(md_info); ++ /* okm_len must not exceed 255 times hash len (RFC 5869 Section 2.3) */ ++ if (okm_len > ((mac_len << 8) - mac_len)) ++ return -1; ++ ++ mbedtls_md_context_t ctx; ++ mbedtls_md_init(&ctx); ++ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) { ++ mbedtls_md_free(&ctx); ++ return -1; ++ } ++ mbedtls_md_hmac_starts(&ctx, prk, prk_len); ++ ++ u8 iter = 1; ++ const u8 *addr[4] = { okm, (const u8 *)label, info, &iter }; ++ size_t len[4] = { 0, label ? os_strlen(label)+1 : 0, info_len, 1 }; ++ ++ for (; okm_len >= mac_len; okm_len -= mac_len, ++iter) { ++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i) ++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]); ++ mbedtls_md_hmac_finish(&ctx, okm); ++ mbedtls_md_hmac_reset(&ctx); ++ addr[0] = okm; ++ okm += mac_len; ++ len[0] = mac_len; /*(include digest in subsequent rounds)*/ ++ } ++ ++ if (okm_len) { ++ u8 hash[MBEDTLS_MD_MAX_SIZE]; ++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i) ++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]); ++ mbedtls_md_hmac_finish(&ctx, hash); ++ os_memcpy(okm, hash, okm_len); ++ forced_memzero(hash, mac_len); ++ } ++ ++ mbedtls_md_free(&ctx); ++ return 0; ++} ++ ++#ifdef MBEDTLS_SHA512_C ++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA512 ++int hmac_sha512_kdf(const u8 *secret, size_t secret_len, ++ const char *label, const u8 *seed, size_t seed_len, ++ u8 *out, size_t outlen) ++{ ++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len, ++ out, outlen, MBEDTLS_MD_SHA512); ++} ++#endif ++ ++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA384 ++int hmac_sha384_kdf(const u8 *secret, size_t secret_len, ++ const char *label, const u8 *seed, size_t seed_len, ++ u8 *out, size_t outlen) ++{ ++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len, ++ out, outlen, MBEDTLS_MD_SHA384); ++} ++#endif ++#endif ++ ++#ifdef MBEDTLS_SHA256_C ++#ifdef CRYPTO_MBEDTLS_HMAC_KDF_SHA256 ++int hmac_sha256_kdf(const u8 *secret, size_t secret_len, ++ const char *label, const u8 *seed, size_t seed_len, ++ u8 *out, size_t outlen) ++{ ++ return hmac_kdf_expand(secret, secret_len, label, seed, seed_len, ++ out, outlen, MBEDTLS_MD_SHA256); ++} ++#endif ++#endif ++ ++#endif /* CRYPTO_MBEDTLS_HMAC_KDF_* */ ++ ++ ++/* sha256-prf.c sha384-prf.c sha512-prf.c */ ++ ++/* hmac_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function */ ++__attribute_noinline__ ++static int hmac_prf_bits(const u8 *key, size_t key_len, const char *label, ++ const u8 *data, size_t data_len, u8 *buf, ++ size_t buf_len_bits, mbedtls_md_type_t md_type) ++{ ++ mbedtls_md_context_t ctx; ++ mbedtls_md_init(&ctx); ++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); ++ if (mbedtls_md_setup(&ctx, md_info, 1) != 0) { ++ mbedtls_md_free(&ctx); ++ return -1; ++ } ++ mbedtls_md_hmac_starts(&ctx, key, key_len); ++ ++ u16 ctr, n_le = host_to_le16(buf_len_bits); ++ const u8 * const addr[] = { (u8 *)&ctr,(u8 *)label,data,(u8 *)&n_le }; ++ const size_t len[] = { 2, os_strlen(label), data_len, 2 }; ++ const size_t mac_len = mbedtls_md_get_size(md_info); ++ size_t buf_len = (buf_len_bits + 7) / 8; ++ for (ctr = 1; buf_len >= mac_len; buf_len -= mac_len, ++ctr) { ++ #if __BYTE_ORDER == __BIG_ENDIAN ++ ctr = host_to_le16(ctr); ++ #endif ++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i) ++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]); ++ mbedtls_md_hmac_finish(&ctx, buf); ++ mbedtls_md_hmac_reset(&ctx); ++ buf += mac_len; ++ #if __BYTE_ORDER == __BIG_ENDIAN ++ ctr = le_to_host16(ctr); ++ #endif ++ } ++ ++ if (buf_len) { ++ u8 hash[MBEDTLS_MD_MAX_SIZE]; ++ #if __BYTE_ORDER == __BIG_ENDIAN ++ ctr = host_to_le16(ctr); ++ #endif ++ for (size_t i = 0; i < ARRAY_SIZE(addr); ++i) ++ mbedtls_md_hmac_update(&ctx, addr[i], len[i]); ++ mbedtls_md_hmac_finish(&ctx, hash); ++ os_memcpy(buf, hash, buf_len); ++ buf += buf_len; ++ forced_memzero(hash, mac_len); ++ } ++ ++ /* Mask out unused bits in last octet if it does not use all the bits */ ++ if ((buf_len_bits &= 0x7)) ++ buf[-1] &= (u8)(0xff << (8 - buf_len_bits)); ++ ++ mbedtls_md_free(&ctx); ++ return 0; ++} ++ ++#ifdef MBEDTLS_SHA512_C ++int sha512_prf(const u8 *key, size_t key_len, const char *label, ++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len) ++{ ++ return hmac_prf_bits(key, key_len, label, data, data_len, buf, ++ buf_len * 8, MBEDTLS_MD_SHA512); ++} ++ ++int sha384_prf(const u8 *key, size_t key_len, const char *label, ++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len) ++{ ++ return hmac_prf_bits(key, key_len, label, data, data_len, buf, ++ buf_len * 8, MBEDTLS_MD_SHA384); ++} ++#endif ++ ++#ifdef MBEDTLS_SHA256_C ++int sha256_prf(const u8 *key, size_t key_len, const char *label, ++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len) ++{ ++ return hmac_prf_bits(key, key_len, label, data, data_len, buf, ++ buf_len * 8, MBEDTLS_MD_SHA256); ++} ++ ++int sha256_prf_bits(const u8 *key, size_t key_len, const char *label, ++ const u8 *data, size_t data_len, u8 *buf, ++ size_t buf_len_bits) ++{ ++ return hmac_prf_bits(key, key_len, label, data, data_len, buf, ++ buf_len_bits, MBEDTLS_MD_SHA256); ++} ++#endif ++ ++#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA512_C */ ++ ++ ++#ifdef MBEDTLS_SHA1_C ++ ++/* sha1-prf.c */ ++ ++/* sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) */ ++ ++int sha1_prf(const u8 *key, size_t key_len, const char *label, ++ const u8 *data, size_t data_len, u8 *buf, size_t buf_len) ++{ ++ /*(note: algorithm differs from hmac_prf_bits() */ ++ /*(note: smaller code size instead of expanding hmac_sha1_vector() ++ * as is done in hmac_prf_bits(); not expecting large num of loops) */ ++ u8 counter = 0; ++ const u8 *addr[] = { (u8 *)label, data, &counter }; ++ const size_t len[] = { os_strlen(label)+1, data_len, 1 }; ++ ++ for (; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++counter) { ++ if (hmac_sha1_vector(key, key_len, 3, addr, len, buf)) ++ return -1; ++ buf += SHA1_MAC_LEN; ++ } ++ ++ if (buf_len) { ++ u8 hash[SHA1_MAC_LEN]; ++ if (hmac_sha1_vector(key, key_len, 3, addr, len, hash)) ++ return -1; ++ os_memcpy(buf, hash, buf_len); ++ forced_memzero(hash, sizeof(hash)); ++ } ++ ++ return 0; ++} ++ ++#ifdef CRYPTO_MBEDTLS_SHA1_T_PRF ++ ++/* sha1-tprf.c */ ++ ++/* sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) (RFC 4851,Section 5.5)*/ ++ ++int sha1_t_prf(const u8 *key, size_t key_len, const char *label, ++ const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) ++{ ++ /*(note: algorithm differs from hmac_prf_bits() and hmac_kdf() above)*/ ++ /*(note: smaller code size instead of expanding hmac_sha1_vector() ++ * as is done in hmac_prf_bits(); not expecting large num of loops) */ ++ u8 ctr; ++ u16 olen = host_to_be16(buf_len); ++ const u8 *addr[] = { buf, (u8 *)label, seed, (u8 *)&olen, &ctr }; ++ size_t len[] = { 0, os_strlen(label)+1, seed_len, 2, 1 }; ++ ++ for (ctr = 1; buf_len >= SHA1_MAC_LEN; buf_len -= SHA1_MAC_LEN, ++ctr) { ++ if (hmac_sha1_vector(key, key_len, 5, addr, len, buf)) ++ return -1; ++ addr[0] = buf; ++ buf += SHA1_MAC_LEN; ++ len[0] = SHA1_MAC_LEN; /*(include digest in subsequent rounds)*/ ++ } ++ ++ if (buf_len) { ++ u8 hash[SHA1_MAC_LEN]; ++ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash)) ++ return -1; ++ os_memcpy(buf, hash, buf_len); ++ forced_memzero(hash, sizeof(hash)); ++ } ++ ++ return 0; ++} ++ ++#endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */ ++ ++#endif /* MBEDTLS_SHA1_C */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_DES_ENCRYPT ++#ifdef MBEDTLS_DES_C ++#include ++int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) ++{ ++ u8 pkey[8], next, tmp; ++ int i; ++ ++ /* Add parity bits to the key */ ++ next = 0; ++ for (i = 0; i < 7; i++) { ++ tmp = key[i]; ++ pkey[i] = (tmp >> i) | next | 1; ++ next = tmp << (7 - i); ++ } ++ pkey[i] = next | 1; ++ ++ mbedtls_des_context des; ++ mbedtls_des_init(&des); ++ int ret = mbedtls_des_setkey_enc(&des, pkey) ++ || mbedtls_des_crypt_ecb(&des, clear, cypher) ? -1 : 0; ++ mbedtls_des_free(&des); ++ return ret; ++} ++#else ++#include "des-internal.c"/* pull in hostap local implementation */ ++#endif ++#endif ++ ++ ++#ifdef CRYPTO_MBEDTLS_PBKDF2_SHA1 ++/* sha1-pbkdf2.c */ ++#include ++int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, ++ int iterations, u8 *buf, size_t buflen) ++{ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03020200 /* mbedtls 3.2.2 */ ++ return mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, ++ (const u8 *)passphrase, os_strlen(passphrase), ++ ssid, ssid_len, iterations, 32, buf) ? -1 : 0; ++ #else ++ const mbedtls_md_info_t *md_info; ++ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); ++ if (md_info == NULL) ++ return -1; ++ mbedtls_md_context_t ctx; ++ mbedtls_md_init(&ctx); ++ int ret = mbedtls_md_setup(&ctx, md_info, 1) ++ || mbedtls_pkcs5_pbkdf2_hmac(&ctx, ++ (const u8 *)passphrase, os_strlen(passphrase), ++ ssid, ssid_len, iterations, 32, buf) ? -1 : 0; ++ mbedtls_md_free(&ctx); ++ return ret; ++ #endif ++} ++#endif ++ ++ ++/*#include "aes.h"*/ /* prototypes also included in "crypto.h" */ ++ ++static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode) ++{ ++ mbedtls_aes_context *aes = os_malloc(sizeof(*aes)); ++ if (!aes) ++ return NULL; ++ ++ mbedtls_aes_init(aes); ++ if ((mode == MBEDTLS_AES_ENCRYPT ++ ? mbedtls_aes_setkey_enc(aes, key, len * 8) ++ : mbedtls_aes_setkey_dec(aes, key, len * 8)) == 0) ++ return aes; ++ ++ mbedtls_aes_free(aes); ++ os_free(aes); ++ return NULL; ++} ++ ++void *aes_encrypt_init(const u8 *key, size_t len) ++{ ++ return aes_crypt_init_mode(key, len, MBEDTLS_AES_ENCRYPT); ++} ++ ++int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) ++{ ++ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, plain, crypt); ++} ++ ++void aes_encrypt_deinit(void *ctx) ++{ ++ mbedtls_aes_free(ctx); ++ os_free(ctx); ++} ++ ++void *aes_decrypt_init(const u8 *key, size_t len) ++{ ++ return aes_crypt_init_mode(key, len, MBEDTLS_AES_DECRYPT); ++} ++ ++int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) ++{ ++ return mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_DECRYPT, crypt, plain); ++} ++ ++void aes_decrypt_deinit(void *ctx) ++{ ++ mbedtls_aes_free(ctx); ++ os_free(ctx); ++} ++ ++ ++#include "aes_wrap.h" ++ ++ ++#ifdef MBEDTLS_NIST_KW_C ++ ++#include ++ ++/* aes-wrap.c */ ++int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) ++{ ++ mbedtls_nist_kw_context ctx; ++ mbedtls_nist_kw_init(&ctx); ++ size_t olen; ++ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, ++ kek, kek_len*8, 1) ++ || mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, plain, n*8, ++ cipher, &olen, (n+1)*8) ? -1 : 0; ++ mbedtls_nist_kw_free(&ctx); ++ return ret; ++} ++ ++/* aes-unwrap.c */ ++int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain) ++{ ++ mbedtls_nist_kw_context ctx; ++ mbedtls_nist_kw_init(&ctx); ++ size_t olen; ++ int ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, ++ kek, kek_len*8, 0) ++ || mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, cipher, ++ (n+1)*8, plain, &olen, n*8) ? -1 : 0; ++ mbedtls_nist_kw_free(&ctx); ++ return ret; ++} ++ ++#else ++ ++#ifndef CRYPTO_MBEDTLS_CONFIG_FIPS ++#include "aes-wrap.c" /* pull in hostap local implementation */ ++#include "aes-unwrap.c" /* pull in hostap local implementation */ ++#endif ++ ++#endif /* MBEDTLS_NIST_KW_C */ ++ ++ ++#ifdef MBEDTLS_CMAC_C ++ ++/* aes-omac1.c */ ++ ++#include ++ ++int omac1_aes_vector( ++ const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], ++ const size_t *len, u8 *mac) ++{ ++ mbedtls_cipher_type_t cipher_type; ++ switch (key_len) { ++ case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break; ++ case 24: cipher_type = MBEDTLS_CIPHER_AES_192_ECB; break; ++ case 32: cipher_type = MBEDTLS_CIPHER_AES_256_ECB; break; ++ default: return -1; ++ } ++ const mbedtls_cipher_info_t *cipher_info; ++ cipher_info = mbedtls_cipher_info_from_type(cipher_type); ++ if (cipher_info == NULL) ++ return -1; ++ ++ mbedtls_cipher_context_t ctx; ++ mbedtls_cipher_init(&ctx); ++ int ret = -1; ++ if (mbedtls_cipher_setup(&ctx, cipher_info) == 0 ++ && mbedtls_cipher_cmac_starts(&ctx, key, key_len*8) == 0) { ++ ret = 0; ++ for (size_t i = 0; i < num_elem && ret == 0; ++i) ++ ret = mbedtls_cipher_cmac_update(&ctx, addr[i], len[i]); ++ } ++ if (ret == 0) ++ ret = mbedtls_cipher_cmac_finish(&ctx, mac); ++ mbedtls_cipher_free(&ctx); ++ return ret ? -1 : 0; ++} ++ ++int omac1_aes_128_vector(const u8 *key, size_t num_elem, ++ const u8 *addr[], const size_t *len, ++ u8 *mac) ++{ ++ return omac1_aes_vector(key, 16, num_elem, addr, len, mac); ++} ++ ++int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) ++{ ++ return omac1_aes_vector(key, 16, 1, &data, &data_len, mac); ++} ++ ++int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) ++{ ++ return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); ++} ++ ++#else ++ ++#include "aes-omac1.c" /* pull in hostap local implementation */ ++ ++#ifndef MBEDTLS_AES_BLOCK_SIZE ++#define MBEDTLS_AES_BLOCK_SIZE 16 ++#endif ++ ++#endif /* MBEDTLS_CMAC_C */ ++ ++ ++/* These interfaces can be inefficient when used in loops, as the overhead of ++ * initialization each call is large for each block input (e.g. 16 bytes) */ ++ ++ ++/* aes-encblock.c */ ++int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) ++{ ++ mbedtls_aes_context aes; ++ mbedtls_aes_init(&aes); ++ int ret = mbedtls_aes_setkey_enc(&aes, key, 128) ++ || mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, in, out) ++ ? -1 ++ : 0; ++ mbedtls_aes_free(&aes); ++ return ret; ++} ++ ++ ++/* aes-ctr.c */ ++int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, ++ u8 *data, size_t data_len) ++{ ++ unsigned char counter[MBEDTLS_AES_BLOCK_SIZE]; ++ unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE]; ++ os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/ ++ ++ mbedtls_aes_context ctx; ++ mbedtls_aes_init(&ctx); ++ size_t nc_off = 0; ++ int ret = mbedtls_aes_setkey_enc(&ctx, key, key_len*8) ++ || mbedtls_aes_crypt_ctr(&ctx, data_len, &nc_off, ++ counter, stream_block, ++ data, data) ? -1 : 0; ++ forced_memzero(stream_block, sizeof(stream_block)); ++ mbedtls_aes_free(&ctx); ++ return ret; ++} ++ ++int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, ++ u8 *data, size_t data_len) ++{ ++ return aes_ctr_encrypt(key, 16, nonce, data, data_len); ++} ++ ++ ++/* aes-cbc.c */ ++static int aes_128_cbc_oper(const u8 *key, const u8 *iv, ++ u8 *data, size_t data_len, int mode) ++{ ++ unsigned char ivec[MBEDTLS_AES_BLOCK_SIZE]; ++ os_memcpy(ivec, iv, MBEDTLS_AES_BLOCK_SIZE); /*(must be writable)*/ ++ ++ mbedtls_aes_context ctx; ++ mbedtls_aes_init(&ctx); ++ int ret = (mode == MBEDTLS_AES_ENCRYPT ++ ? mbedtls_aes_setkey_enc(&ctx, key, 128) ++ : mbedtls_aes_setkey_dec(&ctx, key, 128)) ++ || mbedtls_aes_crypt_cbc(&ctx, mode, data_len, ivec, data, data); ++ mbedtls_aes_free(&ctx); ++ return ret ? -1 : 0; ++} ++ ++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) ++{ ++ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT); ++} ++ ++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) ++{ ++ return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT); ++} ++ ++ ++/* ++ * Much of the following is documented in crypto.h as for CONFIG_TLS=internal ++ * but such comments are not accurate: ++ * ++ * "This function is only used with internal TLSv1 implementation ++ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need ++ * to implement this." ++ */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_CIPHER ++ ++#include ++ ++struct crypto_cipher ++{ ++ mbedtls_cipher_context_t ctx_enc; ++ mbedtls_cipher_context_t ctx_dec; ++}; ++ ++struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, ++ const u8 *iv, const u8 *key, ++ size_t key_len) ++{ ++ /* IKEv2 src/eap_common/ikev2_common.c:ikev2_{encr,decr}_encrypt() ++ * uses one of CRYPTO_CIPHER_ALG_AES or CRYPTO_CIPHER_ALG_3DES */ ++ ++ mbedtls_cipher_type_t cipher_type; ++ size_t iv_len; ++ switch (alg) { ++ #ifdef MBEDTLS_ARC4_C ++ #if 0 ++ case CRYPTO_CIPHER_ALG_RC4: ++ cipher_type = MBEDTLS_CIPHER_ARC4_128; ++ iv_len = 0; ++ break; ++ #endif ++ #endif ++ #ifdef MBEDTLS_AES_C ++ case CRYPTO_CIPHER_ALG_AES: ++ if (key_len == 16) cipher_type = MBEDTLS_CIPHER_AES_128_CTR; ++ if (key_len == 24) cipher_type = MBEDTLS_CIPHER_AES_192_CTR; ++ if (key_len == 32) cipher_type = MBEDTLS_CIPHER_AES_256_CTR; ++ iv_len = 16; ++ break; ++ #endif ++ #ifdef MBEDTLS_DES_C ++ case CRYPTO_CIPHER_ALG_3DES: ++ cipher_type = MBEDTLS_CIPHER_DES_EDE3_CBC; ++ iv_len = 8; ++ break; ++ #if 0 ++ case CRYPTO_CIPHER_ALG_DES: ++ cipher_type = MBEDTLS_CIPHER_DES_CBC; ++ iv_len = 8; ++ break; ++ #endif ++ #endif ++ default: ++ return NULL; ++ } ++ ++ const mbedtls_cipher_info_t *cipher_info; ++ cipher_info = mbedtls_cipher_info_from_type(cipher_type); ++ if (cipher_info == NULL) ++ return NULL; ++ ++ key_len *= 8; /* key_bitlen */ ++ #if 0 /*(were key_bitlen not already available)*/ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */ ++ key_len = mbedtls_cipher_info_get_key_bitlen(cipher_info); ++ #else ++ key_len = cipher_info->MBEDTLS_PRIVATE(key_bitlen); ++ #endif ++ #endif ++ ++ #if 0 /*(were iv_len not known above, would need MBEDTLS_PRIVATE(iv_size))*/ ++ iv_len = cipher_info->MBEDTLS_PRIVATE(iv_size); ++ #endif ++ ++ struct crypto_cipher *ctx = os_malloc(sizeof(*ctx)); ++ if (!ctx) ++ return NULL; ++ ++ mbedtls_cipher_init(&ctx->ctx_enc); ++ mbedtls_cipher_init(&ctx->ctx_dec); ++ if ( mbedtls_cipher_setup(&ctx->ctx_enc,cipher_info) == 0 ++ && mbedtls_cipher_setup(&ctx->ctx_dec,cipher_info) == 0 ++ && mbedtls_cipher_setkey(&ctx->ctx_enc,key,key_len,MBEDTLS_ENCRYPT) == 0 ++ && mbedtls_cipher_setkey(&ctx->ctx_dec,key,key_len,MBEDTLS_DECRYPT) == 0 ++ && mbedtls_cipher_set_iv(&ctx->ctx_enc,iv,iv_len) == 0 ++ && mbedtls_cipher_set_iv(&ctx->ctx_dec,iv,iv_len) == 0 ++ && mbedtls_cipher_reset(&ctx->ctx_enc) == 0 ++ && mbedtls_cipher_reset(&ctx->ctx_dec) == 0) { ++ return ctx; ++ } ++ ++ mbedtls_cipher_free(&ctx->ctx_enc); ++ mbedtls_cipher_free(&ctx->ctx_dec); ++ os_free(ctx); ++ return NULL; ++} ++ ++int crypto_cipher_encrypt(struct crypto_cipher *ctx, ++ const u8 *plain, u8 *crypt, size_t len) ++{ ++ size_t olen = 0; /*(poor interface above; unknown size of u8 *crypt)*/ ++ return (mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen) ++ || mbedtls_cipher_finish(&ctx->ctx_enc, crypt + olen, &olen)) ? -1 : 0; ++} ++ ++int crypto_cipher_decrypt(struct crypto_cipher *ctx, ++ const u8 *crypt, u8 *plain, size_t len) ++{ ++ size_t olen = 0; /*(poor interface above; unknown size of u8 *plain)*/ ++ return (mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen) ++ || mbedtls_cipher_finish(&ctx->ctx_dec, plain + olen, &olen)) ? -1 : 0; ++} ++ ++void crypto_cipher_deinit(struct crypto_cipher *ctx) ++{ ++ mbedtls_cipher_free(&ctx->ctx_enc); ++ mbedtls_cipher_free(&ctx->ctx_dec); ++ os_free(ctx); ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_CIPHER */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_HASH ++ ++struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ++ size_t key_len) ++{ ++ mbedtls_md_type_t md_type; ++ int is_hmac = 0; ++ ++ switch (alg) { ++ #ifdef MBEDTLS_MD5_C ++ case CRYPTO_HASH_ALG_MD5: ++ md_type = MBEDTLS_MD_MD5; ++ break; ++ #endif ++ #ifdef MBEDTLS_SHA1_C ++ case CRYPTO_HASH_ALG_SHA1: ++ md_type = MBEDTLS_MD_SHA1; ++ break; ++ #endif ++ #ifdef MBEDTLS_MD5_C ++ case CRYPTO_HASH_ALG_HMAC_MD5: ++ md_type = MBEDTLS_MD_MD5; ++ is_hmac = 1; ++ break; ++ #endif ++ #ifdef MBEDTLS_SHA1_C ++ case CRYPTO_HASH_ALG_HMAC_SHA1: ++ md_type = MBEDTLS_MD_SHA1; ++ is_hmac = 1; ++ break; ++ #endif ++ #ifdef MBEDTLS_SHA256_C ++ case CRYPTO_HASH_ALG_SHA256: ++ md_type = MBEDTLS_MD_SHA256; ++ break; ++ case CRYPTO_HASH_ALG_HMAC_SHA256: ++ md_type = MBEDTLS_MD_SHA256; ++ is_hmac = 1; ++ break; ++ #endif ++ #ifdef MBEDTLS_SHA512_C ++ case CRYPTO_HASH_ALG_SHA384: ++ md_type = MBEDTLS_MD_SHA384; ++ break; ++ case CRYPTO_HASH_ALG_SHA512: ++ md_type = MBEDTLS_MD_SHA512; ++ break; ++ #endif ++ default: ++ return NULL; ++ } ++ ++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); ++ if (!md_info) ++ return NULL; ++ ++ mbedtls_md_context_t *mctx = os_malloc(sizeof(*mctx)); ++ if (mctx == NULL) ++ return NULL; ++ ++ mbedtls_md_init(mctx); ++ if (mbedtls_md_setup(mctx, md_info, is_hmac) != 0) { ++ os_free(mctx); ++ return NULL; ++ } ++ ++ if (is_hmac) ++ mbedtls_md_hmac_starts(mctx, key, key_len); ++ else ++ mbedtls_md_starts(mctx); ++ return (struct crypto_hash *)((uintptr_t)mctx | is_hmac); ++} ++ ++void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) ++{ ++ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL); ++ #if 0 ++ /*(mbedtls_md_hmac_update() and mbedtls_md_update() ++ * make same modifications under the hood in mbedtls)*/ ++ if ((uintptr_t)ctx & 1uL) ++ mbedtls_md_hmac_update(mctx, data, len); ++ else ++ #endif ++ mbedtls_md_update(mctx, data, len); ++} ++ ++int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) ++{ ++ mbedtls_md_context_t *mctx = (mbedtls_md_context_t*)((uintptr_t)ctx & ~1uL); ++ if (mac != NULL && len != NULL) { /*(NULL if caller just freeing context)*/ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */ ++ const mbedtls_md_info_t *md_info = mbedtls_md_info_from_ctx(mctx); ++ #else ++ const mbedtls_md_info_t *md_info = mctx->MBEDTLS_PRIVATE(md_info); ++ #endif ++ size_t maclen = mbedtls_md_get_size(md_info); ++ if (*len < maclen) { ++ *len = maclen; ++ /*(note: ctx not freed; can call again with larger *len)*/ ++ return -1; ++ } ++ *len = maclen; ++ if ((uintptr_t)ctx & 1uL) ++ mbedtls_md_hmac_finish(mctx, mac); ++ else ++ mbedtls_md_finish(mctx, mac); ++ } ++ mbedtls_md_free(mctx); ++ os_free(mctx); ++ return 0; ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_HASH */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_BIGNUM ++ ++#include ++ ++/* crypto.h bignum interfaces */ ++ ++struct crypto_bignum *crypto_bignum_init(void) ++{ ++ mbedtls_mpi *bn = os_malloc(sizeof(*bn)); ++ if (bn) ++ mbedtls_mpi_init(bn); ++ return (struct crypto_bignum *)bn; ++} ++ ++struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len) ++{ ++ mbedtls_mpi *bn = os_malloc(sizeof(*bn)); ++ if (bn) { ++ mbedtls_mpi_init(bn); ++ if (mbedtls_mpi_read_binary(bn, buf, len) == 0) ++ return (struct crypto_bignum *)bn; ++ } ++ ++ os_free(bn); ++ return NULL; ++} ++ ++struct crypto_bignum *crypto_bignum_init_uint(unsigned int val) ++{ ++ #if 0 /*(hostap use of this interface passes int, not uint)*/ ++ val = host_to_be32(val); ++ return crypto_bignum_init_set((const u8 *)&val, sizeof(val)); ++ #else ++ mbedtls_mpi *bn = os_malloc(sizeof(*bn)); ++ if (bn) { ++ mbedtls_mpi_init(bn); ++ if (mbedtls_mpi_lset(bn, (int)val) == 0) ++ return (struct crypto_bignum *)bn; ++ } ++ ++ os_free(bn); ++ return NULL; ++ #endif ++} ++ ++void crypto_bignum_deinit(struct crypto_bignum *n, int clear) ++{ ++ mbedtls_mpi_free((mbedtls_mpi *)n); ++ os_free(n); ++} ++ ++int crypto_bignum_to_bin(const struct crypto_bignum *a, ++ u8 *buf, size_t buflen, size_t padlen) ++{ ++ size_t n = mbedtls_mpi_size((mbedtls_mpi *)a); ++ if (n < padlen) ++ n = padlen; ++ return n > buflen || mbedtls_mpi_write_binary((mbedtls_mpi *)a, buf, n) ++ ? -1 ++ : (int)(n); ++} ++ ++int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) ++{ ++ /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/ ++ #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */ ++ return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) ? -1 : 0; ++ #else ++ /* (needed by EAP_PWD, SAE, DPP) */ ++ wpa_printf(MSG_ERROR, ++ "mbedtls 2.27.0 or later required for mbedtls_mpi_random()"); ++ return -1; ++ #endif ++} ++ ++int crypto_bignum_add(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ struct crypto_bignum *c) ++{ ++ return mbedtls_mpi_add_mpi((mbedtls_mpi *)c, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b) ? -1 : 0; ++} ++ ++int crypto_bignum_mod(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ struct crypto_bignum *c) ++{ ++ return mbedtls_mpi_mod_mpi((mbedtls_mpi *)c, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b) ? -1 : 0; ++} ++ ++int crypto_bignum_exptmod(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ const struct crypto_bignum *c, ++ struct crypto_bignum *d) ++{ ++ /* (check if input params match d; d is the result) */ ++ /* (a == d) is ok in current mbedtls implementation */ ++ if (b == d || c == d) { /*(not ok; store result in intermediate)*/ ++ mbedtls_mpi R; ++ mbedtls_mpi_init(&R); ++ int rc = mbedtls_mpi_exp_mod(&R, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b, ++ (const mbedtls_mpi *)c, ++ NULL) ++ || mbedtls_mpi_copy((mbedtls_mpi *)d, &R) ? -1 : 0; ++ mbedtls_mpi_free(&R); ++ return rc; ++ } ++ else { ++ return mbedtls_mpi_exp_mod((mbedtls_mpi *)d, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b, ++ (const mbedtls_mpi *)c, ++ NULL) ? -1 : 0; ++ } ++} ++ ++int crypto_bignum_inverse(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ struct crypto_bignum *c) ++{ ++ return mbedtls_mpi_inv_mod((mbedtls_mpi *)c, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b) ? -1 : 0; ++} ++ ++int crypto_bignum_sub(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ struct crypto_bignum *c) ++{ ++ return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b) ? -1 : 0; ++} ++ ++int crypto_bignum_div(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ struct crypto_bignum *c) ++{ ++ /*(most current use of this crypto.h interface has a == c (result), ++ * so store result in an intermediate to avoid overwritten input)*/ ++ mbedtls_mpi R; ++ mbedtls_mpi_init(&R); ++ int rc = mbedtls_mpi_div_mpi(&R, NULL, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b) ++ || mbedtls_mpi_copy((mbedtls_mpi *)c, &R) ? -1 : 0; ++ mbedtls_mpi_free(&R); ++ return rc; ++} ++ ++int crypto_bignum_addmod(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ const struct crypto_bignum *c, ++ struct crypto_bignum *d) ++{ ++ return mbedtls_mpi_add_mpi((mbedtls_mpi *)d, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b) ++ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d, ++ (mbedtls_mpi *)d, ++ (const mbedtls_mpi *)c) ? -1 : 0; ++} ++ ++int crypto_bignum_mulmod(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ const struct crypto_bignum *c, ++ struct crypto_bignum *d) ++{ ++ return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d, ++ (const mbedtls_mpi *)a, ++ (const mbedtls_mpi *)b) ++ || mbedtls_mpi_mod_mpi((mbedtls_mpi *)d, ++ (mbedtls_mpi *)d, ++ (const mbedtls_mpi *)c) ? -1 : 0; ++} ++ ++int crypto_bignum_sqrmod(const struct crypto_bignum *a, ++ const struct crypto_bignum *b, ++ struct crypto_bignum *c) ++{ ++ #if 1 ++ return crypto_bignum_mulmod(a, a, b, c); ++ #else ++ mbedtls_mpi bn; ++ mbedtls_mpi_init(&bn); ++ if (mbedtls_mpi_lset(&bn, 2)) /* alt?: mbedtls_mpi_set_bit(&bn, 1) */ ++ return -1; ++ int ret = mbedtls_mpi_exp_mod((mbedtls_mpi *)c, ++ (const mbedtls_mpi *)a, &bn, ++ (const mbedtls_mpi *)b, NULL) ? -1 : 0; ++ mbedtls_mpi_free(&bn); ++ return ret; ++ #endif ++} ++ ++int crypto_bignum_rshift(const struct crypto_bignum *a, int n, ++ struct crypto_bignum *r) ++{ ++ return mbedtls_mpi_copy((mbedtls_mpi *)r, (const mbedtls_mpi *)a) ++ || mbedtls_mpi_shift_r((mbedtls_mpi *)r, n) ? -1 : 0; ++} ++ ++int crypto_bignum_cmp(const struct crypto_bignum *a, ++ const struct crypto_bignum *b) ++{ ++ return mbedtls_mpi_cmp_mpi((const mbedtls_mpi *)a, (const mbedtls_mpi *)b); ++} ++ ++int crypto_bignum_is_zero(const struct crypto_bignum *a) ++{ ++ /* XXX: src/common/sae.c:sswu() contains comment: ++ * "TODO: Make sure crypto_bignum_is_zero() is constant time" ++ * Note: mbedtls_mpi_cmp_int() *is not* constant time */ ++ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 0) == 0); ++} ++ ++int crypto_bignum_is_one(const struct crypto_bignum *a) ++{ ++ return (mbedtls_mpi_cmp_int((const mbedtls_mpi *)a, 1) == 0); ++} ++ ++int crypto_bignum_is_odd(const struct crypto_bignum *a) ++{ ++ return mbedtls_mpi_get_bit((const mbedtls_mpi *)a, 0); ++} ++ ++#include "utils/const_time.h" ++int crypto_bignum_legendre(const struct crypto_bignum *a, ++ const struct crypto_bignum *p) ++{ ++ /* Security Note: ++ * mbedtls_mpi_exp_mod() is not documented to run in constant time, ++ * though mbedtls/library/bignum.c uses constant_time_internal.h funcs. ++ * Compare to crypto_openssl.c:crypto_bignum_legendre() ++ * which uses openssl BN_mod_exp_mont_consttime() ++ * mbedtls/library/ecp.c has further countermeasures to timing attacks, ++ * (but ecp.c funcs are not used here) */ ++ ++ mbedtls_mpi exp, tmp; ++ mbedtls_mpi_init(&exp); ++ mbedtls_mpi_init(&tmp); ++ ++ /* exp = (p-1) / 2 */ ++ int res; ++ if (mbedtls_mpi_sub_int(&exp, (const mbedtls_mpi *)p, 1) == 0 ++ && mbedtls_mpi_shift_r(&exp, 1) == 0 ++ && mbedtls_mpi_exp_mod(&tmp, (const mbedtls_mpi *)a, &exp, ++ (const mbedtls_mpi *)p, NULL) == 0) { ++ /*(modified from crypto_openssl.c:crypto_bignum_legendre())*/ ++ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need ++ * to use constant time selection to avoid branches here. */ ++ unsigned int mask; ++ res = -1; ++ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 1) == 0), 1); ++ res = const_time_select_int(mask, 1, res); ++ mask = const_time_eq((mbedtls_mpi_cmp_int(&tmp, 0) == 0), 1); ++ res = const_time_select_int(mask, 0, res); ++ } else { ++ res = -2; ++ } ++ ++ mbedtls_mpi_free(&tmp); ++ mbedtls_mpi_free(&exp); ++ return res; ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_BIGNUM */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_DH ++ ++/* crypto_internal-modexp.c */ ++ ++#include ++#include ++ ++#if 0 /* crypto_dh_init() and crypto_dh_derive_secret() prefer to use mbedtls */ ++int crypto_mod_exp(const u8 *base, size_t base_len, ++ const u8 *power, size_t power_len, ++ const u8 *modulus, size_t modulus_len, ++ u8 *result, size_t *result_len) ++{ ++ mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result; ++ mbedtls_mpi_init(&bn_base); ++ mbedtls_mpi_init(&bn_exp); ++ mbedtls_mpi_init(&bn_modulus); ++ mbedtls_mpi_init(&bn_result); ++ ++ size_t len; ++ int ret = mbedtls_mpi_read_binary(&bn_base, base, base_len) ++ || mbedtls_mpi_read_binary(&bn_exp, power, power_len) ++ || mbedtls_mpi_read_binary(&bn_modulus, modulus, modulus_len) ++ || mbedtls_mpi_exp_mod(&bn_result,&bn_base,&bn_exp,&bn_modulus,NULL) ++ || (len = mbedtls_mpi_size(&bn_result)) > *result_len ++ || mbedtls_mpi_write_binary(&bn_result, result, (*result_len = len)) ++ ? -1 ++ : 0; ++ ++ mbedtls_mpi_free(&bn_base); ++ mbedtls_mpi_free(&bn_exp); ++ mbedtls_mpi_free(&bn_modulus); ++ mbedtls_mpi_free(&bn_result); ++ return ret; ++} ++#endif ++ ++static int crypto_mbedtls_dh_set_bin_pg(mbedtls_dhm_context *ctx, u8 generator, ++ const u8 *prime, size_t prime_len) ++{ ++ /*(could set these directly in MBEDTLS_PRIVATE members)*/ ++ mbedtls_mpi P, G; ++ mbedtls_mpi_init(&P); ++ mbedtls_mpi_init(&G); ++ int ret = mbedtls_mpi_lset(&G, generator) ++ || mbedtls_mpi_read_binary(&P, prime, prime_len) ++ || mbedtls_dhm_set_group(ctx, &P, &G); ++ mbedtls_mpi_free(&P); ++ mbedtls_mpi_free(&G); ++ return ret; ++} ++ ++__attribute_noinline__ ++static int crypto_mbedtls_dh_init_public(mbedtls_dhm_context *ctx, u8 generator, ++ const u8 *prime, size_t prime_len, ++ u8 *privkey, u8 *pubkey) ++{ ++ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len) ++ || mbedtls_dhm_make_public(ctx, (int)prime_len, pubkey, prime_len, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg())) ++ return -1; ++ ++ /*(enable later when upstream mbedtls interface changes require)*/ ++ #if 0 && MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ mbedtls_mpi X; ++ mbedtls_mpi_init(&X); ++ int ret = mbedtls_dhm_get_value(ctx, MBEDTLS_DHM_PARAM_X, &X) ++ || mbedtls_mpi_write_binary(&X, privkey, prime_len) ? -1 : 0; ++ mbedtls_mpi_free(&X); ++ return ret; ++ #else ++ return mbedtls_mpi_write_binary(&ctx->MBEDTLS_PRIVATE(X), ++ privkey, prime_len) ? -1 : 0; ++ #endif ++} ++ ++int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, ++ u8 *pubkey) ++{ ++ #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/ ++ size_t pubkey_len, pad; ++ ++ if (os_get_random(privkey, prime_len) < 0) ++ return -1; ++ if (os_memcmp(privkey, prime, prime_len) > 0) { ++ /* Make sure private value is smaller than prime */ ++ privkey[0] = 0; ++ } ++ ++ pubkey_len = prime_len; ++ if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, ++ pubkey, &pubkey_len) < 0) ++ return -1; ++ if (pubkey_len < prime_len) { ++ pad = prime_len - pubkey_len; ++ os_memmove(pubkey + pad, pubkey, pubkey_len); ++ os_memset(pubkey, 0, pad); ++ } ++ ++ return 0; ++ #else ++ /* Prefer to use mbedtls to derive our public/private key, as doing so ++ * leverages mbedtls to properly format output and to perform blinding*/ ++ mbedtls_dhm_context ctx; ++ mbedtls_dhm_init(&ctx); ++ int ret = crypto_mbedtls_dh_init_public(&ctx, generator, prime, ++ prime_len, privkey, pubkey); ++ mbedtls_dhm_free(&ctx); ++ return ret; ++ #endif ++} ++ ++/*(crypto_dh_derive_secret() could be implemented using crypto.h APIs ++ * instead of being reimplemented in each crypto_*.c)*/ ++int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, ++ const u8 *order, size_t order_len, ++ const u8 *privkey, size_t privkey_len, ++ const u8 *pubkey, size_t pubkey_len, ++ u8 *secret, size_t *len) ++{ ++ #if 0 ++ if (pubkey_len > prime_len || ++ (pubkey_len == prime_len && ++ os_memcmp(pubkey, prime, prime_len) >= 0)) ++ return -1; ++ ++ int res = 0; ++ mbedtls_mpi pub; ++ mbedtls_mpi_init(&pub); ++ if (mbedtls_mpi_read_binary(&pub, pubkey, pubkey_len) ++ || mbedtls_mpi_cmp_int(&pub, 1) <= 0) { ++ res = -1; ++ } else if (order) { ++ mbedtls_mpi p, q, tmp; ++ mbedtls_mpi_init(&p); ++ mbedtls_mpi_init(&q); ++ mbedtls_mpi_init(&tmp); ++ ++ /* verify: pubkey^q == 1 mod p */ ++ res = (mbedtls_mpi_read_binary(&p, prime, prime_len) ++ || mbedtls_mpi_read_binary(&q, order, order_len) ++ || mbedtls_mpi_exp_mod(&tmp, &pub, &q, &p, NULL) ++ || mbedtls_mpi_cmp_int(&tmp, 1) != 0); ++ ++ mbedtls_mpi_free(&p); ++ mbedtls_mpi_free(&q); ++ mbedtls_mpi_free(&tmp); ++ } ++ mbedtls_mpi_free(&pub); ++ ++ return (res == 0) ++ ? crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, ++ prime, prime_len, secret, len) ++ : -1; ++ #else ++ /* Prefer to use mbedtls to derive DH shared secret, as doing so ++ * leverages mbedtls to validate params and to perform blinding. ++ * ++ * Attempt to reconstitute DH context to derive shared secret ++ * (due to limitations of the interface, which ought to pass context). ++ * Force provided G (our private key) into context without validation. ++ * Regenerating GX (our public key) not needed to derive shared secret. ++ */ ++ /*(older compilers might not support VLAs)*/ ++ /*unsigned char buf[2+prime_len+2+1+2+pubkey_len];*/ ++ unsigned char buf[2+MBEDTLS_MPI_MAX_SIZE+2+1+2+MBEDTLS_MPI_MAX_SIZE]; ++ unsigned char *p = buf + 2 + prime_len; ++ if (2+prime_len+2+1+2+pubkey_len > sizeof(buf)) ++ return -1; ++ WPA_PUT_BE16(buf, prime_len); /*(2-byte big-endian size of prime)*/ ++ p[0] = 0; /*(2-byte big-endian size of generator)*/ ++ p[1] = 1; ++ p[2] = generator; ++ WPA_PUT_BE16(p+3, pubkey_len); /*(2-byte big-endian size of pubkey)*/ ++ os_memcpy(p+5, pubkey, pubkey_len); ++ os_memcpy(buf+2, prime, prime_len); ++ ++ mbedtls_dhm_context ctx; ++ mbedtls_dhm_init(&ctx); ++ p = buf; ++ int ret = mbedtls_dhm_read_params(&ctx, &p, p+2+prime_len+5+pubkey_len) ++ || mbedtls_mpi_read_binary(&ctx.MBEDTLS_PRIVATE(X), ++ privkey, privkey_len) ++ || mbedtls_dhm_calc_secret(&ctx, secret, *len, len, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) ? -1 : 0; ++ mbedtls_dhm_free(&ctx); ++ return ret; ++ #endif ++} ++ ++/* dh_group5.c */ ++ ++#include "dh_group5.h" ++ ++/* RFC3526_PRIME_1536[] and RFC3526_GENERATOR_1536[] from crypto_wolfssl.c */ ++ ++static const unsigned char RFC3526_PRIME_1536[] = { ++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, ++ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, ++ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, ++ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, ++ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, ++ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, ++ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, ++ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, ++ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, ++ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, ++ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, ++ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, ++ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, ++ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, ++ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, ++ 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ++}; ++ ++static const unsigned char RFC3526_GENERATOR_1536[] = { ++ 0x02 ++}; ++ ++void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) ++{ ++ const unsigned char * const prime = RFC3526_PRIME_1536; ++ const size_t prime_len = sizeof(RFC3526_PRIME_1536); ++ const u8 generator = *RFC3526_GENERATOR_1536; ++ struct wpabuf *wpubl = NULL, *wpriv = NULL; ++ ++ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ mbedtls_dhm_init(ctx); ++ ++ if ( (wpubl = wpabuf_alloc(prime_len)) ++ && (wpriv = wpabuf_alloc(prime_len)) ++ && crypto_mbedtls_dh_init_public(ctx, generator, prime, prime_len, ++ wpabuf_put(wpriv, prime_len), ++ wpabuf_put(wpubl, prime_len))==0) { ++ wpabuf_free(*publ); ++ wpabuf_clear_free(*priv); ++ *publ = wpubl; ++ *priv = wpriv; ++ return ctx; ++ } ++ ++ wpabuf_clear_free(wpriv); ++ wpabuf_free(wpubl); ++ mbedtls_dhm_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++ ++#ifdef CRYPTO_MBEDTLS_DH5_INIT_FIXED ++void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) ++{ ++ const unsigned char * const prime = RFC3526_PRIME_1536; ++ const size_t prime_len = sizeof(RFC3526_PRIME_1536); ++ const u8 generator = *RFC3526_GENERATOR_1536; ++ ++ mbedtls_dhm_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ mbedtls_dhm_init(ctx); ++ ++ if (crypto_mbedtls_dh_set_bin_pg(ctx, generator, prime, prime_len)==0 ++ #if 0 /*(ignore; not required to derive shared secret)*/ ++ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(GX), ++ wpabuf_head(publ),wpabuf_len(publ))==0 ++ #endif ++ && mbedtls_mpi_read_binary(&ctx->MBEDTLS_PRIVATE(X), ++ wpabuf_head(priv),wpabuf_len(priv))==0) { ++ return ctx; ++ } ++ ++ mbedtls_dhm_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++#endif ++ ++struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, ++ const struct wpabuf *own_private) ++{ ++ /*((mbedtls_dhm_context *)ctx must already contain own_private)*/ ++ /* mbedtls 2.x: prime_len = ctx->len; */ ++ /* mbedtls 3.x: prime_len = mbedtls_dhm_get_len(ctx); */ ++ size_t olen = sizeof(RFC3526_PRIME_1536); /*(sizeof(); prime known)*/ ++ struct wpabuf *buf = wpabuf_alloc(olen); ++ if (buf == NULL) ++ return NULL; ++ if (mbedtls_dhm_read_public((mbedtls_dhm_context *)ctx, ++ wpabuf_head(peer_public), ++ wpabuf_len(peer_public)) == 0 ++ && mbedtls_dhm_calc_secret(ctx, wpabuf_mhead(buf), olen, &olen, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) == 0) { ++ wpabuf_put(buf, olen); ++ return buf; ++ } ++ ++ wpabuf_free(buf); ++ return NULL; ++} ++ ++void dh5_free(void *ctx) ++{ ++ mbedtls_dhm_free(ctx); ++ os_free(ctx); ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_DH */ ++ ++ ++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC) ++ ++#include ++ ++#define CRYPTO_EC_pbits(e) (((mbedtls_ecp_group *)(e))->pbits) ++#define CRYPTO_EC_plen(e) ((((mbedtls_ecp_group *)(e))->pbits+7)>>3) ++#define CRYPTO_EC_P(e) (&((mbedtls_ecp_group *)(e))->P) ++#define CRYPTO_EC_N(e) (&((mbedtls_ecp_group *)(e))->N) ++#define CRYPTO_EC_A(e) (&((mbedtls_ecp_group *)(e))->A) ++#define CRYPTO_EC_B(e) (&((mbedtls_ecp_group *)(e))->B) ++#define CRYPTO_EC_G(e) (&((mbedtls_ecp_group *)(e))->G) ++ ++static mbedtls_ecp_group_id crypto_mbedtls_ecp_group_id_from_ike_id(int group) ++{ ++ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */ ++ switch (group) { ++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED ++ case 19: return MBEDTLS_ECP_DP_SECP256R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED ++ case 20: return MBEDTLS_ECP_DP_SECP384R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED ++ case 21: return MBEDTLS_ECP_DP_SECP521R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED ++ case 25: return MBEDTLS_ECP_DP_SECP192R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED ++ case 26: return MBEDTLS_ECP_DP_SECP224R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED ++ case 28: return MBEDTLS_ECP_DP_BP256R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED ++ case 29: return MBEDTLS_ECP_DP_BP384R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED ++ case 30: return MBEDTLS_ECP_DP_BP512R1; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED ++ case 31: return MBEDTLS_ECP_DP_CURVE25519; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED ++ case 32: return MBEDTLS_ECP_DP_CURVE448; ++ #endif ++ default: return MBEDTLS_ECP_DP_NONE; ++ } ++} ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC ++static int crypto_mbedtls_ike_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id) ++{ ++ /* https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml */ ++ /*(for crypto_ec_key_group())*/ ++ switch (grp_id) { ++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP256R1: return 19; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP384R1: return 20; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP521R1: return 21; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP192R1: return 25; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP224R1: return 26; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED ++ case MBEDTLS_ECP_DP_BP256R1: return 28; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED ++ case MBEDTLS_ECP_DP_BP384R1: return 29; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED ++ case MBEDTLS_ECP_DP_BP512R1: return 30; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED ++ case MBEDTLS_ECP_DP_CURVE25519: return 31; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED ++ case MBEDTLS_ECP_DP_CURVE448: return 32; ++ #endif ++ default: return -1; ++ } ++} ++#endif ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH || CRYPTO_MBEDTLS_CRYPTO_EC */ ++ ++ ++#if defined(CRYPTO_MBEDTLS_CRYPTO_ECDH) || defined(CRYPTO_MBEDTLS_CRYPTO_EC_DPP) ++ ++#include ++#include ++ ++static int crypto_mbedtls_keypair_gen(int group, mbedtls_pk_context *pk) ++{ ++ mbedtls_ecp_group_id grp_id = ++ crypto_mbedtls_ecp_group_id_from_ike_id(group); ++ if (grp_id == MBEDTLS_ECP_DP_NONE) ++ return -1; ++ const mbedtls_pk_info_t *pk_info = ++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); ++ if (pk_info == NULL) ++ return -1; ++ return mbedtls_pk_setup(pk, pk_info) ++ || mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(*pk), ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) ? -1 : 0; ++} ++ ++#endif ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_ECDH ++ ++#include ++#include ++#include ++#include ++ ++/* wrap mbedtls_ecdh_context for more future-proof direct access to components ++ * (mbedtls_ecdh_context internal implementation may change between releases) ++ * ++ * If mbedtls_pk_context -- specifically underlying mbedtls_ecp_keypair -- ++ * lifetime were guaranteed to be longer than that of mbedtls_ecdh_context, ++ * then mbedtls_pk_context or mbedtls_ecp_keypair could be stored in crypto_ecdh ++ * (or crypto_ec_key could be stored in crypto_ecdh, and crypto_ec_key could ++ * wrap mbedtls_ecp_keypair and components, to avoid MBEDTLS_PRIVATE access) */ ++struct crypto_ecdh { ++ mbedtls_ecdh_context ctx; ++ mbedtls_ecp_group grp; ++ mbedtls_ecp_point Q; ++}; ++ ++struct crypto_ecdh * crypto_ecdh_init(int group) ++{ ++ mbedtls_pk_context pk; ++ mbedtls_pk_init(&pk); ++ struct crypto_ecdh *ecdh = crypto_mbedtls_keypair_gen(group, &pk) == 0 ++ ? crypto_ecdh_init2(group, (struct crypto_ec_key *)&pk) ++ : NULL; ++ mbedtls_pk_free(&pk); ++ return ecdh; ++} ++ ++struct crypto_ecdh * crypto_ecdh_init2(int group, ++ struct crypto_ec_key *own_key) ++{ ++ mbedtls_ecp_group_id grp_id = ++ crypto_mbedtls_ecp_group_id_from_ike_id(group); ++ if (grp_id == MBEDTLS_ECP_DP_NONE) ++ return NULL; ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)own_key); ++ struct crypto_ecdh *ecdh = os_malloc(sizeof(*ecdh)); ++ if (ecdh == NULL) ++ return NULL; ++ mbedtls_ecdh_init(&ecdh->ctx); ++ mbedtls_ecp_group_init(&ecdh->grp); ++ mbedtls_ecp_point_init(&ecdh->Q); ++ if (mbedtls_ecdh_setup(&ecdh->ctx, grp_id) == 0 ++ && mbedtls_ecdh_get_params(&ecdh->ctx,ecp_kp,MBEDTLS_ECDH_OURS) == 0) { ++ /* copy grp and Q for later use ++ * (retrieving this info later is more convoluted ++ * even if mbedtls_ecdh_make_public() is considered)*/ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */ ++ mbedtls_mpi d; ++ mbedtls_mpi_init(&d); ++ if (mbedtls_ecp_export(ecp_kp, &ecdh->grp, &d, &ecdh->Q) == 0) { ++ mbedtls_mpi_free(&d); ++ return ecdh; ++ } ++ mbedtls_mpi_free(&d); ++ #else ++ if (mbedtls_ecp_group_load(&ecdh->grp, grp_id) == 0 ++ && mbedtls_ecp_copy(&ecdh->Q, &ecp_kp->MBEDTLS_PRIVATE(Q)) == 0) ++ return ecdh; ++ #endif ++ } ++ ++ mbedtls_ecp_point_free(&ecdh->Q); ++ mbedtls_ecp_group_free(&ecdh->grp); ++ mbedtls_ecdh_free(&ecdh->ctx); ++ os_free(ecdh); ++ return NULL; ++} ++ ++struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) ++{ ++ mbedtls_ecp_group *grp = &ecdh->grp; ++ size_t len = CRYPTO_EC_plen(grp); ++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED ++ /* len */ ++ #endif ++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED ++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) ++ len = inc_y ? len*2+1 : len+1; ++ #endif ++ struct wpabuf *buf = wpabuf_alloc(len); ++ if (buf == NULL) ++ return NULL; ++ inc_y = inc_y ? MBEDTLS_ECP_PF_UNCOMPRESSED : MBEDTLS_ECP_PF_COMPRESSED; ++ if (mbedtls_ecp_point_write_binary(grp, &ecdh->Q, inc_y, &len, ++ wpabuf_mhead_u8(buf), len) == 0) { ++ wpabuf_put(buf, len); ++ return buf; ++ } ++ ++ wpabuf_free(buf); ++ return NULL; ++} ++ ++#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) ++static int crypto_mbedtls_short_weierstrass_derive_y(mbedtls_ecp_group *grp, ++ mbedtls_mpi *bn, ++ int parity_bit) ++{ ++ /* y^2 = x^3 + ax + b ++ * sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) */ ++ mbedtls_mpi *cy2 = (mbedtls_mpi *) ++ crypto_ec_point_compute_y_sqr((struct crypto_ec *)grp, ++ (const struct crypto_bignum *)bn); /*x*/ ++ if (cy2 == NULL) ++ return -1; ++ ++ /*mbedtls_mpi_free(bn);*/ ++ /*(reuse bn to store result (y))*/ ++ ++ mbedtls_mpi exp; ++ mbedtls_mpi_init(&exp); ++ int ret = mbedtls_mpi_get_bit(&grp->P, 0) != 1 /*(p = 3 mod 4)*/ ++ || mbedtls_mpi_get_bit(&grp->P, 1) != 1 /*(p = 3 mod 4)*/ ++ || mbedtls_mpi_add_int(&exp, &grp->P, 1) ++ || mbedtls_mpi_shift_r(&exp, 2) ++ || mbedtls_mpi_exp_mod(bn, cy2, &exp, &grp->P, NULL) ++ || (mbedtls_mpi_get_bit(bn, 0) != parity_bit ++ && mbedtls_mpi_sub_mpi(bn, &grp->P, bn)); ++ mbedtls_mpi_free(&exp); ++ mbedtls_mpi_free(cy2); ++ os_free(cy2); ++ return ret; ++} ++#endif ++ ++struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, ++ const u8 *key, size_t len) ++{ ++ if (len == 0) /*(invalid peer key)*/ ++ return NULL; ++ ++ mbedtls_ecp_group *grp = &ecdh->grp; ++ ++ #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) ++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { ++ /* add header for mbedtls_ecdh_read_public() */ ++ u8 buf[256]; ++ if (sizeof(buf)-1 < len) ++ return NULL; ++ buf[0] = (u8)(len); ++ os_memcpy(buf+1, key, len); ++ ++ if (inc_y) { ++ if (!(len & 1)) { /*(dpp code/tests does not include tag?!?)*/ ++ if (sizeof(buf)-2 < len) ++ return NULL; ++ buf[0] = (u8)(1+len); ++ buf[1] = 0x04; ++ os_memcpy(buf+2, key, len); ++ } ++ len >>= 1; /*(repurpose len to prime_len)*/ ++ } ++ else if (key[0] == 0x02 || key[0] == 0x03) { /* (inc_y == 0) */ ++ --len; /*(repurpose len to prime_len)*/ ++ ++ /* mbedtls_ecp_point_read_binary() does not currently support ++ * MBEDTLS_ECP_PF_COMPRESSED format (buf[1] = 0x02 or 0x03) ++ * (returns MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) */ ++ ++ /* derive y, amend buf[] with y for UNCOMPRESSED format */ ++ if (sizeof(buf)-2 < len*2 || len == 0) ++ return NULL; ++ buf[0] = (u8)(1+len*2); ++ buf[1] = 0x04; ++ mbedtls_mpi bn; ++ mbedtls_mpi_init(&bn); ++ int ret = mbedtls_mpi_read_binary(&bn, key+1, len) ++ || crypto_mbedtls_short_weierstrass_derive_y(grp, &bn, ++ key[0] & 1) ++ || mbedtls_mpi_write_binary(&bn, buf+2+len, len); ++ mbedtls_mpi_free(&bn); ++ if (ret != 0) ++ return NULL; ++ } ++ ++ if (key[0] == 0) /*(repurpose len to prime_len)*/ ++ len = CRYPTO_EC_plen(grp); ++ ++ if (mbedtls_ecdh_read_public(&ecdh->ctx, buf, buf[0]+1)) ++ return NULL; ++ } ++ #endif ++ #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) ++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { ++ if (mbedtls_ecdh_read_public(&ecdh->ctx, key, len)) ++ return NULL; ++ } ++ #endif ++ ++ struct wpabuf *buf = wpabuf_alloc(len); ++ if (buf == NULL) ++ return NULL; ++ ++ if (mbedtls_ecdh_calc_secret(&ecdh->ctx, &len, ++ wpabuf_mhead(buf), len, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) == 0) { ++ wpabuf_put(buf, len); ++ return buf; ++ } ++ ++ wpabuf_clear_free(buf); ++ return NULL; ++} ++ ++void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) ++{ ++ if (ecdh == NULL) ++ return; ++ mbedtls_ecp_point_free(&ecdh->Q); ++ mbedtls_ecp_group_free(&ecdh->grp); ++ mbedtls_ecdh_free(&ecdh->ctx); ++ os_free(ecdh); ++} ++ ++size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh) ++{ ++ return CRYPTO_EC_plen(&ecdh->grp); ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_ECDH */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC ++ ++#include ++ ++struct crypto_ec *crypto_ec_init(int group) ++{ ++ mbedtls_ecp_group_id grp_id = ++ crypto_mbedtls_ecp_group_id_from_ike_id(group); ++ if (grp_id == MBEDTLS_ECP_DP_NONE) ++ return NULL; ++ mbedtls_ecp_group *e = os_malloc(sizeof(*e)); ++ if (e == NULL) ++ return NULL; ++ mbedtls_ecp_group_init(e); ++ if (mbedtls_ecp_group_load(e, grp_id) == 0) ++ return (struct crypto_ec *)e; ++ ++ mbedtls_ecp_group_free(e); ++ os_free(e); ++ return NULL; ++} ++ ++void crypto_ec_deinit(struct crypto_ec *e) ++{ ++ mbedtls_ecp_group_free((mbedtls_ecp_group *)e); ++ os_free(e); ++} ++ ++size_t crypto_ec_prime_len(struct crypto_ec *e) ++{ ++ return CRYPTO_EC_plen(e); ++} ++ ++size_t crypto_ec_prime_len_bits(struct crypto_ec *e) ++{ ++ return CRYPTO_EC_pbits(e); ++} ++ ++size_t crypto_ec_order_len(struct crypto_ec *e) ++{ ++ return (mbedtls_mpi_bitlen(CRYPTO_EC_N(e)) + 7) / 8; ++} ++ ++const struct crypto_bignum *crypto_ec_get_prime(struct crypto_ec *e) ++{ ++ return (const struct crypto_bignum *)CRYPTO_EC_P(e); ++} ++ ++const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e) ++{ ++ return (const struct crypto_bignum *)CRYPTO_EC_N(e); ++} ++ ++const struct crypto_bignum *crypto_ec_get_a(struct crypto_ec *e) ++{ ++ static const uint8_t secp256r1_a[] = ++ {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc}; ++ static const uint8_t secp384r1_a[] = ++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfc}; ++ static const uint8_t secp521r1_a[] = ++ {0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xfc}; ++ static const uint8_t secp192r1_a[] = ++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc}; ++ static const uint8_t secp224r1_a[] = ++ {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xfe}; ++ ++ const uint8_t *bin = NULL; ++ size_t len = 0; ++ ++ /* (mbedtls groups matching supported sswu_curve_param() IKE groups) */ ++ switch (((mbedtls_ecp_group *)e)->id) { ++ #ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP256R1: ++ bin = secp256r1_a; ++ len = sizeof(secp256r1_a); ++ break; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP384R1: ++ bin = secp384r1_a; ++ len = sizeof(secp384r1_a); ++ break; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP521R1: ++ bin = secp521r1_a; ++ len = sizeof(secp521r1_a); ++ break; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP192R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP192R1: ++ bin = secp192r1_a; ++ len = sizeof(secp192r1_a); ++ break; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_SECP224R1_ENABLED ++ case MBEDTLS_ECP_DP_SECP224R1: ++ bin = secp224r1_a; ++ len = sizeof(secp224r1_a); ++ break; ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP256R1_ENABLED ++ case MBEDTLS_ECP_DP_BP256R1: ++ return (const struct crypto_bignum *)CRYPTO_EC_A(e); ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP384R1_ENABLED ++ case MBEDTLS_ECP_DP_BP384R1: ++ return (const struct crypto_bignum *)CRYPTO_EC_A(e); ++ #endif ++ #ifdef MBEDTLS_ECP_DP_BP512R1_ENABLED ++ case MBEDTLS_ECP_DP_BP512R1: ++ return (const struct crypto_bignum *)CRYPTO_EC_A(e); ++ #endif ++ #ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED ++ case MBEDTLS_ECP_DP_CURVE25519: ++ return (const struct crypto_bignum *)CRYPTO_EC_A(e); ++ #endif ++ #ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED ++ case MBEDTLS_ECP_DP_CURVE448: ++ return (const struct crypto_bignum *)CRYPTO_EC_A(e); ++ #endif ++ default: ++ return NULL; ++ } ++ ++ /*(note: not thread-safe; returns file-scoped static storage)*/ ++ if (mbedtls_mpi_read_binary(&mpi_sw_A, bin, len) == 0) ++ return (const struct crypto_bignum *)&mpi_sw_A; ++ return NULL; ++} ++ ++const struct crypto_bignum *crypto_ec_get_b(struct crypto_ec *e) ++{ ++ return (const struct crypto_bignum *)CRYPTO_EC_B(e); ++} ++ ++const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e) ++{ ++ return (const struct crypto_ec_point *)CRYPTO_EC_G(e); ++} ++ ++struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e) ++{ ++ mbedtls_ecp_point *p = os_malloc(sizeof(*p)); ++ if (p != NULL) ++ mbedtls_ecp_point_init(p); ++ return (struct crypto_ec_point *)p; ++} ++ ++void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) ++{ ++ mbedtls_ecp_point_free((mbedtls_ecp_point *)p); ++ os_free(p); ++} ++ ++int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, ++ struct crypto_bignum *x) ++{ ++ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X); ++ return mbedtls_mpi_copy((mbedtls_mpi *)x, px) ++ ? -1 ++ : 0; ++} ++ ++int crypto_ec_point_to_bin(struct crypto_ec *e, ++ const struct crypto_ec_point *point, u8 *x, u8 *y) ++{ ++ /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */ ++ size_t len = CRYPTO_EC_plen(e); ++ if (x) { ++ mbedtls_mpi *px = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(X); ++ if (mbedtls_mpi_write_binary(px, x, len)) ++ return -1; ++ } ++ if (y) { ++ #if 0 /*(should not be necessary; py mpi should be in initial state)*/ ++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED ++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e) ++ == MBEDTLS_ECP_TYPE_MONTGOMERY) { ++ os_memset(y, 0, len); ++ return 0; ++ } ++ #endif ++ #endif ++ mbedtls_mpi *py = &((mbedtls_ecp_point *)point)->MBEDTLS_PRIVATE(Y); ++ if (mbedtls_mpi_write_binary(py, y, len)) ++ return -1; ++ } ++ return 0; ++} ++ ++struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, ++ const u8 *val) ++{ ++ size_t len = CRYPTO_EC_plen(e); ++ mbedtls_ecp_point *p = os_malloc(sizeof(*p)); ++ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2]; ++ if (p == NULL) ++ return NULL; ++ mbedtls_ecp_point_init(p); ++ ++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED ++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e) ++ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { ++ #if 0 /* prefer alternative to MBEDTLS_PRIVATE() access */ ++ mbedtls_mpi *px = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X); ++ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y); ++ mbedtls_mpi *pz = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Z); ++ ++ if (mbedtls_mpi_read_binary(px, val, len) == 0 ++ && mbedtls_mpi_read_binary(py, val + len, len) == 0 ++ && mbedtls_mpi_lset(pz, 1) == 0) ++ return (struct crypto_ec_point *)p; ++ #else ++ buf[0] = 0x04; ++ os_memcpy(buf+1, val, len*2); ++ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p, ++ buf, 1+len*2) == 0) ++ return (struct crypto_ec_point *)p; ++ #endif ++ } ++ #endif ++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED ++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e) ++ == MBEDTLS_ECP_TYPE_MONTGOMERY) { ++ /* crypto.h interface documents crypto_ec_point_from_bin() ++ * val is length: prime_len * 2 and is big-endian ++ * (Short Weierstrass is assumed by hostap) ++ * Reverse to little-endian format for Montgomery */ ++ for (unsigned int i = 0; i < len; ++i) ++ buf[i] = val[len-1-i]; ++ if (mbedtls_ecp_point_read_binary((mbedtls_ecp_group *)e, p, ++ buf, len) == 0) ++ return (struct crypto_ec_point *)p; ++ } ++ #endif ++ ++ mbedtls_ecp_point_free(p); ++ os_free(p); ++ return NULL; ++} ++ ++int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, ++ const struct crypto_ec_point *b, ++ struct crypto_ec_point *c) ++{ ++ /* mbedtls does not provide an mbedtls_ecp_point add function */ ++ mbedtls_mpi one; ++ mbedtls_mpi_init(&one); ++ int ret = mbedtls_mpi_lset(&one, 1) ++ || mbedtls_ecp_muladd( ++ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)c, ++ &one, (const mbedtls_ecp_point *)a, ++ &one, (const mbedtls_ecp_point *)b) ? -1 : 0; ++ mbedtls_mpi_free(&one); ++ return ret; ++} ++ ++int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, ++ const struct crypto_bignum *b, ++ struct crypto_ec_point *res) ++{ ++ return mbedtls_ecp_mul( ++ (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res, ++ (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p, ++ mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) ? -1 : 0; ++} ++ ++int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) ++{ ++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e) ++ == MBEDTLS_ECP_TYPE_MONTGOMERY) { ++ /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */ ++ wpa_printf(MSG_ERROR, ++ "%s not implemented for Montgomery curves",__func__); ++ return -1; ++ } ++ ++ /* mbedtls does not provide an mbedtls_ecp_point invert function */ ++ /* below works for Short Weierstrass; incorrect for Montgomery curves */ ++ mbedtls_mpi *py = &((mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y); ++ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p) /*point at infinity*/ ++ || mbedtls_mpi_cmp_int(py, 0) == 0 /*point is its own inverse*/ ++ || mbedtls_mpi_sub_abs(py, CRYPTO_EC_P(e), py) == 0 ? 0 : -1; ++} ++ ++#ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED ++static int ++crypto_ec_point_y_sqr_weierstrass(mbedtls_ecp_group *e, const mbedtls_mpi *x, ++ mbedtls_mpi *y2) ++{ ++ /* MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS y^2 = x^3 + a x + b */ ++ ++ /* Short Weierstrass elliptic curve group w/o A set treated as A = -3 */ ++ /* Attempt to match mbedtls/library/ecp.c:ecp_check_pubkey_sw() behavior ++ * and elsewhere in mbedtls/library/ecp.c where if A is not set, it is ++ * treated as if A = -3. */ ++ ++ #if 0 ++ /* y^2 = x^3 + ax + b */ ++ mbedtls_mpi *A = &e->A; ++ mbedtls_mpi t, A_neg3; ++ if (&e->A.p == NULL) { ++ mbedtls_mpi_init(&A_neg3); ++ if (mbedtls_mpi_lset(&A_neg3, -3) != 0) { ++ mbedtls_mpi_free(&A_neg3); ++ return -1; ++ } ++ A = &A_neg3; ++ } ++ mbedtls_mpi_init(&t); ++ int ret = /* x^3 */ ++ mbedtls_mpi_lset(&t, 3) ++ || mbedtls_mpi_exp_mod(y2, x, &t, &e->P, NULL) ++ /* ax */ ++ || mbedtls_mpi_mul_mpi(y2, y2, A) ++ || mbedtls_mpi_mod_mpi(&t, &t, &e->P) ++ /* ax + b */ ++ || mbedtls_mpi_add_mpi(&t, &t, &e->B) ++ || mbedtls_mpi_mod_mpi(&t, &t, &e->P) ++ /* x^3 + ax + b */ ++ || mbedtls_mpi_add_mpi(&t, &t, y2) /* ax + b + x^3 */ ++ || mbedtls_mpi_mod_mpi(y2, &t, &e->P); ++ mbedtls_mpi_free(&t); ++ if (A == &A_neg3) ++ mbedtls_mpi_free(&A_neg3); ++ return ret; /* 0: success, non-zero: failure */ ++ #else ++ /* y^2 = x^3 + ax + b = (x^2 + a)x + b */ ++ return /* x^2 */ ++ mbedtls_mpi_mul_mpi(y2, x, x) ++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P) ++ /* x^2 + a */ ++ || (e->A.MBEDTLS_PRIVATE(p) ++ ? mbedtls_mpi_add_mpi(y2, y2, &e->A) ++ : mbedtls_mpi_sub_int(y2, y2, 3)) ++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P) ++ /* (x^2 + a)x */ ++ || mbedtls_mpi_mul_mpi(y2, y2, x) ++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P) ++ /* (x^2 + a)x + b */ ++ || mbedtls_mpi_add_mpi(y2, y2, &e->B) ++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P); ++ #endif ++} ++#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ ++ ++#if 0 /* not used by hostap */ ++#ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED ++static int ++crypto_ec_point_y_sqr_montgomery(mbedtls_ecp_group *e, const mbedtls_mpi *x, ++ mbedtls_mpi *y2) ++{ ++ /* XXX: !!! must be reviewed and audited for correctness !!! */ ++ ++ /* MBEDTLS_ECP_TYPE_MONTGOMERY y^2 = x^3 + a x^2 + x */ ++ ++ /* y^2 = x^3 + a x^2 + x = (x + a)x^2 + x */ ++ mbedtls_mpi x2; ++ mbedtls_mpi_init(&x2); ++ int ret = /* x^2 */ ++ mbedtls_mpi_mul_mpi(&x2, x, x) ++ || mbedtls_mpi_mod_mpi(&x2, &x2, &e->P) ++ /* x + a */ ++ || mbedtls_mpi_add_mpi(y2, x, &e->A) ++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P) ++ /* (x + a)x^2 */ ++ || mbedtls_mpi_mul_mpi(y2, y2, &x2) ++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P) ++ /* (x + a)x^2 + x */ ++ || mbedtls_mpi_add_mpi(y2, y2, x) ++ || mbedtls_mpi_mod_mpi(y2, y2, &e->P); ++ mbedtls_mpi_free(&x2); ++ return ret; /* 0: success, non-zero: failure */ ++} ++#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ ++#endif ++ ++struct crypto_bignum * ++crypto_ec_point_compute_y_sqr(struct crypto_ec *e, ++ const struct crypto_bignum *x) ++{ ++ mbedtls_mpi *y2 = os_malloc(sizeof(*y2)); ++ if (y2 == NULL) ++ return NULL; ++ mbedtls_mpi_init(y2); ++ ++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED ++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e) ++ == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS ++ && crypto_ec_point_y_sqr_weierstrass((mbedtls_ecp_group *)e, ++ (const mbedtls_mpi *)x, ++ y2) == 0) ++ return (struct crypto_bignum *)y2; ++ #endif ++ #if 0 /* not used by hostap */ ++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED ++ if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e) ++ == MBEDTLS_ECP_TYPE_MONTGOMERY ++ && crypto_ec_point_y_sqr_montgomery((mbedtls_ecp_group *)e, ++ (const mbedtls_mpi *)x, ++ y2) == 0) ++ return (struct crypto_bignum *)y2; ++ #endif ++ #endif ++ ++ mbedtls_mpi_free(y2); ++ os_free(y2); ++ return NULL; ++} ++ ++int crypto_ec_point_is_at_infinity(struct crypto_ec *e, ++ const struct crypto_ec_point *p) ++{ ++ return mbedtls_ecp_is_zero((mbedtls_ecp_point *)p); ++} ++ ++int crypto_ec_point_is_on_curve(struct crypto_ec *e, ++ const struct crypto_ec_point *p) ++{ ++ #if 1 ++ return mbedtls_ecp_check_pubkey((const mbedtls_ecp_group *)e, ++ (const mbedtls_ecp_point *)p) == 0; ++ #else ++ /* compute y^2 mod P and compare to y^2 mod P */ ++ /*(ref: src/eap_common/eap_pwd_common.c:compute_password_element())*/ ++ const mbedtls_mpi *px = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(X); ++ mbedtls_mpi *cy2 = (mbedtls_mpi *) ++ crypto_ec_point_compute_y_sqr(e, (const struct crypto_bignum *)px); ++ if (cy2 == NULL) ++ return 0; ++ ++ mbedtls_mpi y2; ++ mbedtls_mpi_init(&y2); ++ const mbedtls_mpi *py = &((const mbedtls_ecp_point *)p)->MBEDTLS_PRIVATE(Y); ++ int is_on_curve = mbedtls_mpi_mul_mpi(&y2, py, py) /* y^2 mod P */ ++ || mbedtls_mpi_mod_mpi(&y2, &y2, CRYPTO_EC_P(e)) ++ || mbedtls_mpi_cmp_mpi(&y2, cy2) != 0 ? 0 : 1; ++ ++ mbedtls_mpi_free(&y2); ++ mbedtls_mpi_free(cy2); ++ os_free(cy2); ++ return is_on_curve; ++ #endif ++} ++ ++int crypto_ec_point_cmp(const struct crypto_ec *e, ++ const struct crypto_ec_point *a, ++ const struct crypto_ec_point *b) ++{ ++ return mbedtls_ecp_point_cmp((const mbedtls_ecp_point *)a, ++ (const mbedtls_ecp_point *)b); ++} ++ ++#if !defined(CONFIG_NO_STDOUT_DEBUG) ++void crypto_ec_point_debug_print(const struct crypto_ec *e, ++ const struct crypto_ec_point *p, ++ const char *title) ++{ ++ u8 x[MBEDTLS_MPI_MAX_SIZE]; ++ u8 y[MBEDTLS_MPI_MAX_SIZE]; ++ size_t len = CRYPTO_EC_plen(e); ++ /* crypto_ec_point_to_bin ought to take (const struct crypto_ec *e) */ ++ struct crypto_ec *ee; ++ *(const struct crypto_ec **)&ee = e; /*(cast away const)*/ ++ if (crypto_ec_point_to_bin(ee, p, x, y) == 0) { ++ if (title) ++ wpa_printf(MSG_DEBUG, "%s", title); ++ wpa_hexdump(MSG_DEBUG, "x:", x, len); ++ wpa_hexdump(MSG_DEBUG, "y:", y, len); ++ } ++} ++#endif ++ ++ ++struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len) ++{ ++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ mbedtls_pk_init(ctx); ++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */ ++ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0) == 0) ++ #else ++ if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) == 0) ++ #endif ++ return (struct crypto_ec_key *)ctx; ++ ++ mbedtls_pk_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE ++#ifdef CONFIG_MODULE_TESTS ++/*(for crypto_module_tests.c)*/ ++struct crypto_ec_key * crypto_ec_key_set_priv(int group, ++ const u8 *raw, size_t raw_len) ++{ ++ mbedtls_ecp_group_id grp_id = ++ crypto_mbedtls_ecp_group_id_from_ike_id(group); ++ if (grp_id == MBEDTLS_ECP_DP_NONE) ++ return NULL; ++ const mbedtls_pk_info_t *pk_info = ++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); ++ if (pk_info == NULL) ++ return NULL; ++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ mbedtls_pk_init(ctx); ++ if (mbedtls_pk_setup(ctx, pk_info) == 0 ++ && mbedtls_ecp_read_key(grp_id,mbedtls_pk_ec(*ctx),raw,raw_len) == 0) { ++ return (struct crypto_ec_key *)ctx; ++ } ++ ++ mbedtls_pk_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++#endif ++#endif ++ ++#include ++#include ++static int crypto_mbedtls_pk_parse_subpubkey_compressed(mbedtls_pk_context *ctx, const u8 *der, size_t der_len) ++{ ++ /* The following is modified from: ++ * mbedtls/library/pkparse.c:mbedtls_pk_parse_subpubkey() ++ * mbedtls/library/pkparse.c:pk_get_pk_alg() ++ * mbedtls/library/pkparse.c:pk_use_ecparams() ++ */ ++ mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE; ++ const mbedtls_pk_info_t *pk_info; ++ int ret; ++ size_t len; ++ const unsigned char *end = der+der_len; ++ unsigned char *p; ++ *(const unsigned char **)&p = der; ++ ++ if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) ++ { ++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret ) ); ++ } ++ ++ end = p + len; ++ ++ /* ++ if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, &alg_params ) ) != 0 ) ++ return( ret ); ++ */ ++ mbedtls_asn1_buf alg_oid, params; ++ memset( ¶ms, 0, sizeof(mbedtls_asn1_buf) ); ++ if( ( ret = mbedtls_asn1_get_alg( &p, end, &alg_oid, ¶ms ) ) != 0 ) ++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_ALG, ret ) ); ++ if( mbedtls_oid_get_pk_alg( &alg_oid, &pk_alg ) != 0 ) ++ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); ++ ++ if( ( ret = mbedtls_asn1_get_bitstring_null( &p, end, &len ) ) != 0 ) ++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ret ) ); ++ ++ if( p + len != end ) ++ return( MBEDTLS_ERROR_ADD( MBEDTLS_ERR_PK_INVALID_PUBKEY, ++ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ) ); ++ ++ if( ( pk_info = mbedtls_pk_info_from_type( pk_alg ) ) == NULL ) ++ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); ++ ++ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 ) ++ return( ret ); ++ ++ /* assume mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx) ++ * has already run with ctx initialized up to pk_get_ecpubkey(), ++ * and pk_get_ecpubkey() has returned MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE ++ * ++ * mbedtls mbedtls_ecp_point_read_binary() ++ * does not handle point in COMPRESSED format ++ * ++ * (validate assumption that algorithm is EC) */ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx); ++ if (ecp_kp == NULL) ++ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE ); ++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp); ++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q); ++ mbedtls_ecp_group_id grp_id; ++ ++ ++ /* mbedtls/library/pkparse.c:pk_use_ecparams() */ ++ ++ if( params.tag == MBEDTLS_ASN1_OID ) ++ { ++ if( mbedtls_oid_get_ec_grp( ¶ms, &grp_id ) != 0 ) ++ return( MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE ); ++ } ++ else ++ { ++#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) ++ /*(large code block not copied from mbedtls; unsupported)*/ ++ #if 0 ++ if( ( ret = pk_group_id_from_specified( ¶ms, &grp_id ) ) != 0 ) ++ return( ret ); ++ #endif ++#endif ++ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT ); ++ } ++ ++ /* ++ * grp may already be initialized; if so, make sure IDs match ++ */ ++ if( ecp_kp_grp->id != MBEDTLS_ECP_DP_NONE && ecp_kp_grp->id != grp_id ) ++ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT ); ++ ++ if( ( ret = mbedtls_ecp_group_load( ecp_kp_grp, grp_id ) ) != 0 ) ++ return( ret ); ++ ++ ++ /* (validate assumption that EC point is in COMPRESSED format) */ ++ len = CRYPTO_EC_plen(ecp_kp_grp); ++ if( mbedtls_ecp_get_type(ecp_kp_grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS ++ || (end - p) != 1+len ++ || (*p != 0x02 && *p != 0x03) ) ++ return( MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE ); ++ ++ /* Instead of calling mbedtls/library/pkparse.c:pk_get_ecpubkey() to call ++ * mbedtls_ecp_point_read_binary(), manually parse point into ecp_kp_Q */ ++ mbedtls_mpi *X = &ecp_kp_Q->MBEDTLS_PRIVATE(X); ++ mbedtls_mpi *Y = &ecp_kp_Q->MBEDTLS_PRIVATE(Y); ++ mbedtls_mpi *Z = &ecp_kp_Q->MBEDTLS_PRIVATE(Z); ++ ret = mbedtls_mpi_lset(Z, 1); ++ if (ret != 0) ++ return( ret ); ++ ret = mbedtls_mpi_read_binary(X, p+1, len); ++ if (ret != 0) ++ return( ret ); ++ /* derive Y ++ * (similar derivation of Y in crypto_mbedtls.c:crypto_ecdh_set_peerkey())*/ ++ ret = mbedtls_mpi_copy(Y, X) /*(Y is used as input and output obj below)*/ ++ || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1)); ++ if (ret != 0) ++ return( ret ); ++ ++ return mbedtls_ecp_check_pubkey( ecp_kp_grp, ecp_kp_Q ); ++} ++ ++struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len) ++{ ++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ mbedtls_pk_init(ctx); ++ /*int rc = mbedtls_pk_parse_subpubkey(&der, der+der_len, ctx);*/ ++ int rc = mbedtls_pk_parse_public_key(ctx, der, der_len); ++ if (rc == 0) ++ return (struct crypto_ec_key *)ctx; ++ else if (rc == MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) { ++ /* mbedtls mbedtls_ecp_point_read_binary() ++ * does not handle point in COMPRESSED format; parse internally */ ++ rc = crypto_mbedtls_pk_parse_subpubkey_compressed(ctx,der,der_len); ++ if (rc == 0) ++ return (struct crypto_ec_key *)ctx; ++ } ++ ++ mbedtls_pk_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP ++ ++static struct crypto_ec_key * ++crypto_ec_key_set_pub_point_for_group(mbedtls_ecp_group_id grp_id, ++ const mbedtls_ecp_point *pub, ++ const u8 *buf, size_t len) ++{ ++ const mbedtls_pk_info_t *pk_info = ++ mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); ++ if (pk_info == NULL) ++ return NULL; ++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ mbedtls_pk_init(ctx); ++ if (mbedtls_pk_setup(ctx, pk_info) == 0) { ++ /* (Is private key generation necessary for callers?) ++ * alt: gen key then overwrite Q ++ * mbedtls_ecp_gen_key(grp_id, ecp_kp, ++ * mbedtls_ctr_drbg_random, ++ * crypto_mbedtls_ctr_drbg()) == 0 ++ */ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*ctx); ++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp); ++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q); ++ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d); ++ if (mbedtls_ecp_group_load(ecp_kp_grp, grp_id) == 0 ++ && (pub ++ ? mbedtls_ecp_copy(ecp_kp_Q, pub) == 0 ++ : mbedtls_ecp_point_read_binary(ecp_kp_grp, ecp_kp_Q, ++ buf, len) == 0) ++ && mbedtls_ecp_gen_privkey(ecp_kp_grp, ecp_kp_d, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) == 0){ ++ return (struct crypto_ec_key *)ctx; ++ } ++ } ++ ++ mbedtls_pk_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++ ++struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x, ++ const u8 *y, size_t len) ++{ ++ mbedtls_ecp_group_id grp_id = ++ crypto_mbedtls_ecp_group_id_from_ike_id(group); ++ if (grp_id == MBEDTLS_ECP_DP_NONE) ++ return NULL; ++ if (len > MBEDTLS_MPI_MAX_SIZE) ++ return NULL; ++ u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2]; ++ buf[0] = 0x04; /* assume x,y for Short Weierstrass */ ++ os_memcpy(buf+1, x, len); ++ os_memcpy(buf+1+len, y, len); ++ ++ return crypto_ec_key_set_pub_point_for_group(grp_id,NULL,buf,1+len*2); ++} ++ ++struct crypto_ec_key * ++crypto_ec_key_set_pub_point(struct crypto_ec *e, ++ const struct crypto_ec_point *pub) ++{ ++ mbedtls_ecp_group_id grp_id = ((mbedtls_ecp_group *)e)->id; ++ mbedtls_ecp_point *p = (mbedtls_ecp_point *)pub; ++ return crypto_ec_key_set_pub_point_for_group(grp_id, p, NULL, 0); ++} ++ ++ ++struct crypto_ec_key * crypto_ec_key_gen(int group) ++{ ++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) ++ return NULL; ++ mbedtls_pk_init(ctx); ++ if (crypto_mbedtls_keypair_gen(group, ctx) == 0) ++ return (struct crypto_ec_key *)ctx; ++ mbedtls_pk_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */ ++ ++void crypto_ec_key_deinit(struct crypto_ec_key *key) ++{ ++ mbedtls_pk_free((mbedtls_pk_context *)key); ++ os_free(key); ++} ++ ++struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key) ++{ ++ /* (similar to crypto_ec_key_get_pubkey_point(), ++ * but compressed point format and ASN.1 DER wrapping)*/ ++#ifndef MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/ ++#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ( 30 + 2 * MBEDTLS_ECP_MAX_BYTES ) ++#endif ++ unsigned char buf[MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES]; ++ int len = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)key, ++ buf, sizeof(buf)); ++ if (len < 0) ++ return NULL; ++ /* Note: data is written at the end of the buffer! Use the ++ * return value to determine where you should start ++ * using the buffer */ ++ unsigned char *p = buf+sizeof(buf)-len; ++ ++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key); ++ if (ecp_kp == NULL) ++ return NULL; ++ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp); ++ /* Note: sae_pk.c expects pubkey point in compressed format, ++ * but mbedtls_pk_write_pubkey_der() writes uncompressed format. ++ * Manually translate format and update lengths in DER format */ ++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { ++ unsigned char *end = buf+sizeof(buf); ++ size_t n; ++ /* SubjectPublicKeyInfo SEQUENCE */ ++ mbedtls_asn1_get_tag(&p, end, &n, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ++ /* algorithm AlgorithmIdentifier */ ++ unsigned char *a = p; ++ size_t alen; ++ mbedtls_asn1_get_tag(&p, end, &alen, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ++ p += alen; ++ alen = (size_t)(p - a); ++ /* subjectPublicKey BIT STRING */ ++ mbedtls_asn1_get_tag(&p, end, &n, MBEDTLS_ASN1_BIT_STRING); ++ /* rewrite into compressed point format and rebuild ASN.1 */ ++ p[1] = (buf[sizeof(buf)-1] & 1) ? 0x03 : 0x02; ++ n = 1 + 1 + (n-2)/2; ++ len = mbedtls_asn1_write_len(&p, buf, n) + (int)n; ++ len += mbedtls_asn1_write_tag(&p, buf, MBEDTLS_ASN1_BIT_STRING); ++ os_memmove(p-alen, a, alen); ++ len += alen; ++ p -= alen; ++ len += mbedtls_asn1_write_len(&p, buf, (size_t)len); ++ len += mbedtls_asn1_write_tag(&p, buf, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ++ } ++ #endif ++ return wpabuf_alloc_copy(p, (size_t)len); ++} ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP ++ ++struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key, ++ bool include_pub) ++{ ++#ifndef MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES /*(mbedtls/library/pkwrite.h)*/ ++#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES ( 29 + 3 * MBEDTLS_ECP_MAX_BYTES ) ++#endif ++ unsigned char priv[MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES]; ++ int privlen = mbedtls_pk_write_key_der((mbedtls_pk_context *)key, ++ priv, sizeof(priv)); ++ if (privlen < 0) ++ return NULL; ++ ++ struct wpabuf *wbuf; ++ ++ /* Note: data is written at the end of the buffer! Use the ++ * return value to determine where you should start ++ * using the buffer */ ++ /* mbedtls_pk_write_key_der() includes publicKey in DER */ ++ if (include_pub) ++ wbuf = wpabuf_alloc_copy(priv+sizeof(priv)-privlen, privlen); ++ else { ++ /* calculate publicKey offset and skip from end of buffer */ ++ unsigned char *p = priv+sizeof(priv)-privlen; ++ unsigned char *end = priv+sizeof(priv); ++ size_t len; ++ /* ECPrivateKey SEQUENCE */ ++ mbedtls_asn1_get_tag(&p, end, &len, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ++ /* version INTEGER */ ++ unsigned char *v = p; ++ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_INTEGER); ++ p += len; ++ /* privateKey OCTET STRING */ ++ mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); ++ p += len; ++ /* parameters ECParameters */ ++ mbedtls_asn1_get_tag(&p, end, &len, ++ MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED); ++ p += len; ++ ++ /* write new SEQUENCE header (we know that it fits in priv[]) */ ++ len = (size_t)(p - v); ++ p = v; ++ len += mbedtls_asn1_write_len(&p, priv, len); ++ len += mbedtls_asn1_write_tag(&p, priv, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ++ wbuf = wpabuf_alloc_copy(p, len); ++ } ++ ++ forced_memzero(priv, sizeof(priv)); ++ return wbuf; ++} ++ ++struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key, ++ int prefix) ++{ ++ /*(similarities to crypto_ecdh_get_pubkey(), but different struct)*/ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key); ++ if (ecp_kp == NULL) ++ return NULL; ++ mbedtls_ecp_group *grp = &ecp_kp->MBEDTLS_PRIVATE(grp); ++ size_t len = CRYPTO_EC_plen(grp); ++ #ifdef MBEDTLS_ECP_MONTGOMERY_ENABLED ++ /* len */ ++ #endif ++ #ifdef MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED ++ if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) ++ len = len*2+1; ++ #endif ++ struct wpabuf *buf = wpabuf_alloc(len); ++ if (buf == NULL) ++ return NULL; ++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q); ++ if (mbedtls_ecp_point_write_binary(grp, ecp_kp_Q, ++ MBEDTLS_ECP_PF_UNCOMPRESSED, &len, ++ wpabuf_mhead_u8(buf), len) == 0) { ++ if (!prefix) /* Remove 0x04 prefix if requested */ ++ os_memmove(wpabuf_mhead(buf),wpabuf_mhead(buf)+1,--len); ++ wpabuf_put(buf, len); ++ return buf; ++ } ++ ++ wpabuf_free(buf); ++ return NULL; ++} ++ ++struct crypto_ec_point * ++crypto_ec_key_get_public_key(struct crypto_ec_key *key) ++{ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key); ++ if (ecp_kp == NULL) ++ return NULL; ++ mbedtls_ecp_point *p = os_malloc(sizeof(*p)); ++ if (p != NULL) { ++ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/ ++ mbedtls_ecp_point_init(p); ++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q); ++ if (mbedtls_ecp_copy(p, ecp_kp_Q)) { ++ mbedtls_ecp_point_free(p); ++ os_free(p); ++ p = NULL; ++ } ++ } ++ return (struct crypto_ec_point *)p; ++} ++ ++struct crypto_bignum * ++crypto_ec_key_get_private_key(struct crypto_ec_key *key) ++{ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key); ++ if (ecp_kp == NULL) ++ return NULL; ++ mbedtls_mpi *bn = os_malloc(sizeof(*bn)); ++ if (bn) { ++ /*(mbedtls_ecp_export() uses &ecp_kp->MBEDTLS_PRIVATE(grp))*/ ++ mbedtls_mpi_init(bn); ++ mbedtls_mpi *ecp_kp_d = &ecp_kp->MBEDTLS_PRIVATE(d); ++ if (mbedtls_mpi_copy(bn, ecp_kp_d)) { ++ mbedtls_mpi_free(bn); ++ os_free(bn); ++ bn = NULL; ++ } ++ } ++ return (struct crypto_bignum *)bn; ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */ ++ ++static mbedtls_md_type_t crypto_ec_key_sign_md(size_t len) ++{ ++ /* get mbedtls_md_type_t from length of hash data to be signed */ ++ switch (len) { ++ case 64: return MBEDTLS_MD_SHA512; ++ case 48: return MBEDTLS_MD_SHA384; ++ case 32: return MBEDTLS_MD_SHA256; ++ case 20: return MBEDTLS_MD_SHA1; ++ case 16: return MBEDTLS_MD_MD5; ++ default: return MBEDTLS_MD_NONE; ++ } ++} ++ ++struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data, ++ size_t len) ++{ ++ #ifndef MBEDTLS_PK_SIGNATURE_MAX_SIZE /*(defined since mbedtls 2.20.0)*/ ++ #if MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_MPI_MAX_SIZE ++ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN ++ #else ++ #define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE ++ #endif ++ #endif ++ size_t sig_len = MBEDTLS_PK_SIGNATURE_MAX_SIZE; ++ struct wpabuf *buf = wpabuf_alloc(sig_len); ++ if (buf == NULL) ++ return NULL; ++ if (mbedtls_pk_sign((mbedtls_pk_context *)key, ++ crypto_ec_key_sign_md(len), data, len, ++ wpabuf_mhead_u8(buf), ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ sig_len, ++ #endif ++ &sig_len, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) == 0) { ++ wpabuf_put(buf, sig_len); ++ return buf; ++ } ++ ++ wpabuf_free(buf); ++ return NULL; ++} ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP ++struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key, ++ const u8 *data, size_t len) ++{ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key); ++ if (ecp_kp == NULL) ++ return NULL; ++ ++ size_t sig_len = MBEDTLS_ECDSA_MAX_LEN; ++ u8 buf[MBEDTLS_ECDSA_MAX_LEN]; ++ if (mbedtls_ecdsa_write_signature(ecp_kp, crypto_ec_key_sign_md(len), ++ data, len, buf, ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ sig_len, ++ #endif ++ &sig_len, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg())) { ++ return NULL; ++ } ++ ++ /*(mbedtls_ecdsa_write_signature() writes signature in ASN.1)*/ ++ /* parse ASN.1 to get r and s and lengths */ ++ u8 *p = buf, *r, *s; ++ u8 *end = p + sig_len; ++ size_t rlen, slen; ++ mbedtls_asn1_get_tag(&p, end, &rlen, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ++ mbedtls_asn1_get_tag(&p, end, &rlen, MBEDTLS_ASN1_INTEGER); ++ r = p; ++ p += rlen; ++ mbedtls_asn1_get_tag(&p, end, &slen, MBEDTLS_ASN1_INTEGER); ++ s = p; ++ ++ /* write raw r and s into out ++ * (including removal of leading 0 if added for ASN.1 integer) ++ * note: DPP caller expects raw r, s each padded to prime len */ ++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp); ++ size_t plen = CRYPTO_EC_plen(ecp_kp_grp); ++ if (rlen > plen) { ++ r += (rlen - plen); ++ rlen = plen; ++ } ++ if (slen > plen) { ++ s += (slen - plen); ++ slen = plen; ++ } ++ struct wpabuf *out = wpabuf_alloc(plen*2); ++ if (out) { ++ wpabuf_put(out, plen*2); ++ p = wpabuf_mhead_u8(out); ++ os_memset(p, 0, plen*2); ++ os_memcpy(p+plen*1-rlen, r, rlen); ++ os_memcpy(p+plen*2-slen, s, slen); ++ } ++ return out; ++} ++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */ ++ ++int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data, ++ size_t len, const u8 *sig, size_t sig_len) ++{ ++ switch (mbedtls_pk_verify((mbedtls_pk_context *)key, ++ crypto_ec_key_sign_md(len), data, len, ++ sig, sig_len)) { ++ case 0: ++ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */ ++ return 1; ++ case MBEDTLS_ERR_ECP_VERIFY_FAILED: ++ return 0; ++ default: ++ return -1; ++ } ++} ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP ++int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key, ++ const u8 *data, size_t len, ++ const u8 *r, size_t r_len, ++ const u8 *s, size_t s_len) ++{ ++ /* reimplement mbedtls_ecdsa_read_signature() without encoding r and s ++ * into ASN.1 just for mbedtls_ecdsa_read_signature() to decode ASN.1 */ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key); ++ if (ecp_kp == NULL) ++ return -1; ++ mbedtls_ecp_group *ecp_kp_grp = &ecp_kp->MBEDTLS_PRIVATE(grp); ++ mbedtls_ecp_point *ecp_kp_Q = &ecp_kp->MBEDTLS_PRIVATE(Q); ++ ++ mbedtls_mpi mpi_r; ++ mbedtls_mpi mpi_s; ++ mbedtls_mpi_init(&mpi_r); ++ mbedtls_mpi_init(&mpi_s); ++ int ret = mbedtls_mpi_read_binary(&mpi_r, r, r_len) ++ || mbedtls_mpi_read_binary(&mpi_s, s, s_len) ? -1 : 0; ++ if (ret == 0) { ++ ret = mbedtls_ecdsa_verify(ecp_kp_grp, data, len, ++ ecp_kp_Q, &mpi_r, &mpi_s); ++ ret = ret ? ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA ? 0 : -1 : 1; ++ } ++ mbedtls_mpi_free(&mpi_r); ++ mbedtls_mpi_free(&mpi_s); ++ return ret; ++} ++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */ ++ ++int crypto_ec_key_group(struct crypto_ec_key *key) ++{ ++ mbedtls_ecp_keypair *ecp_kp = mbedtls_pk_ec(*(mbedtls_pk_context *)key); ++ if (ecp_kp == NULL) ++ return -1; ++ mbedtls_ecp_group *ecp_group = &ecp_kp->MBEDTLS_PRIVATE(grp); ++ return crypto_mbedtls_ike_id_from_ecp_group_id(ecp_group->id); ++} ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_EC_DPP ++ ++int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2) ++{ ++#if 0 /*(DPP is passing two public keys; unable to use pk_check_pair())*/ ++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */ ++ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1, ++ (const mbedtls_pk_context *)key2) ? -1 : 0; ++ #else ++ return mbedtls_pk_check_pair((const mbedtls_pk_context *)key1, ++ (const mbedtls_pk_context *)key2, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()) ? -1 : 0; ++ #endif ++#else ++ mbedtls_ecp_keypair *ecp_kp1=mbedtls_pk_ec(*(mbedtls_pk_context *)key1); ++ mbedtls_ecp_keypair *ecp_kp2=mbedtls_pk_ec(*(mbedtls_pk_context *)key2); ++ if (ecp_kp1 == NULL || ecp_kp2 == NULL) ++ return -1; ++ mbedtls_ecp_group *ecp_kp1_grp = &ecp_kp1->MBEDTLS_PRIVATE(grp); ++ mbedtls_ecp_group *ecp_kp2_grp = &ecp_kp2->MBEDTLS_PRIVATE(grp); ++ mbedtls_ecp_point *ecp_kp1_Q = &ecp_kp1->MBEDTLS_PRIVATE(Q); ++ mbedtls_ecp_point *ecp_kp2_Q = &ecp_kp2->MBEDTLS_PRIVATE(Q); ++ return ecp_kp1_grp->id != ecp_kp2_grp->id ++ || mbedtls_ecp_point_cmp(ecp_kp1_Q, ecp_kp2_Q) ? -1 : 0; ++#endif ++} ++ ++void crypto_ec_key_debug_print(const struct crypto_ec_key *key, ++ const char *title) ++{ ++ /* TBD: what info is desirable here and in what human readable format?*/ ++ /*(crypto_openssl.c prints a human-readably public key and attributes)*/ ++ #if 0 ++ struct mbedtls_pk_debug_item debug_item; ++ if (mbedtls_pk_debug((const mbedtls_pk_context *)key, &debug_item)) ++ return; ++ /* ... */ ++ #endif ++ wpa_printf(MSG_DEBUG, "%s: %s not implemented", title, __func__); ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC_DPP */ ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_EC */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_CSR ++ ++#include ++#include ++ ++struct crypto_csr * crypto_csr_init(void) ++{ ++ mbedtls_x509write_csr *csr = os_malloc(sizeof(*csr)); ++ if (csr != NULL) ++ mbedtls_x509write_csr_init(csr); ++ return (struct crypto_csr *)csr; ++} ++ ++struct crypto_csr * crypto_csr_verify(const struct wpabuf *req) ++{ ++ /* future: look for alternatives to MBEDTLS_PRIVATE() access */ ++ ++ /* sole caller src/common/dpp_crypto.c:dpp_validate_csr() ++ * uses (mbedtls_x509_csr *) to obtain CSR_ATTR_CHALLENGE_PASSWORD ++ * so allocate different object (mbedtls_x509_csr *) and special-case ++ * object when used in crypto_csr_get_attribute() and when free()d in ++ * crypto_csr_deinit(). */ ++ ++ mbedtls_x509_csr *csr = os_malloc(sizeof(*csr)); ++ if (csr == NULL) ++ return NULL; ++ mbedtls_x509_csr_init(csr); ++ const mbedtls_md_info_t *md_info; ++ unsigned char digest[MBEDTLS_MD_MAX_SIZE]; ++ if (mbedtls_x509_csr_parse_der(csr,wpabuf_head(req),wpabuf_len(req))==0 ++ && (md_info=mbedtls_md_info_from_type(csr->MBEDTLS_PRIVATE(sig_md))) ++ != NULL ++ && mbedtls_md(md_info, csr->cri.p, csr->cri.len, digest) == 0) { ++ switch (mbedtls_pk_verify(&csr->pk,csr->MBEDTLS_PRIVATE(sig_md), ++ digest, mbedtls_md_get_size(md_info), ++ csr->MBEDTLS_PRIVATE(sig).p, ++ csr->MBEDTLS_PRIVATE(sig).len)) { ++ case 0: ++ /*case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:*//* XXX: allow? */ ++ return (struct crypto_csr *)((uintptr_t)csr | 1uL); ++ default: ++ break; ++ } ++ } ++ ++ mbedtls_x509_csr_free(csr); ++ os_free(csr); ++ return NULL; ++} ++ ++void crypto_csr_deinit(struct crypto_csr *csr) ++{ ++ if ((uintptr_t)csr & 1uL) { ++ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL); ++ mbedtls_x509_csr_free((mbedtls_x509_csr *)csr); ++ } ++ else ++ mbedtls_x509write_csr_free((mbedtls_x509write_csr *)csr); ++ os_free(csr); ++} ++ ++int crypto_csr_set_ec_public_key(struct crypto_csr *csr, ++ struct crypto_ec_key *key) ++{ ++ mbedtls_x509write_csr_set_key((mbedtls_x509write_csr *)csr, ++ (mbedtls_pk_context *)key); ++ return 0; ++} ++ ++int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type, ++ const char *name) ++{ ++ /* specialized for src/common/dpp_crypto.c */ ++ ++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() ++ * calls this function only once, using type == CSR_NAME_CN ++ * (If called more than once, this code would need to append ++ * components to the subject name, which we could do by ++ * appending to (mbedtls_x509write_csr *) private member ++ * mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject)) */ ++ ++ const char *label; ++ switch (type) { ++ case CSR_NAME_CN: label = "CN="; break; ++ case CSR_NAME_SN: label = "SN="; break; ++ case CSR_NAME_C: label = "C="; break; ++ case CSR_NAME_O: label = "O="; break; ++ case CSR_NAME_OU: label = "OU="; break; ++ default: return -1; ++ } ++ ++ size_t len = strlen(name); ++ struct wpabuf *buf = wpabuf_alloc(3+len+1); ++ if (buf == NULL) ++ return -1; ++ wpabuf_put_data(buf, label, strlen(label)); ++ wpabuf_put_data(buf, name, len+1); /*(include trailing '\0')*/ ++ /* Note: 'name' provided is set as given and should be backslash-escaped ++ * by caller when necessary, e.g. literal ',' which are not separating ++ * components should be backslash-escaped */ ++ ++ int ret = ++ mbedtls_x509write_csr_set_subject_name((mbedtls_x509write_csr *)csr, ++ wpabuf_head(buf)) ? -1 : 0; ++ wpabuf_free(buf); ++ return ret; ++} ++ ++/* OBJ_pkcs9_challengePassword 1 2 840 113549 1 9 7 */ ++static const char OBJ_pkcs9_challengePassword[] = MBEDTLS_OID_PKCS9 "\x07"; ++ ++int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr, ++ int attr_type, const u8 *value, size_t len) ++{ ++ /* specialized for src/common/dpp_crypto.c */ ++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes ++ * attr == CSR_ATTR_CHALLENGE_PASSWORD ++ * attr_type == ASN1_TAG_UTF8STRING */ ++ ++ const char *oid; ++ size_t oid_len; ++ switch (attr) { ++ case CSR_ATTR_CHALLENGE_PASSWORD: ++ oid = OBJ_pkcs9_challengePassword; ++ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1; ++ break; ++ default: ++ return -1; ++ } ++ ++ #if 0 /*(incorrect; sets an extension, not an attribute)*/ ++ return mbedtls_x509write_csr_set_extension((mbedtls_x509write_csr *)csr, ++ oid, oid_len, ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ 0, /*(critical flag)*/ ++ #endif ++ value, len) ? -1 : 0; ++ #else ++ (void)oid; ++ (void)oid_len; ++ #endif ++ ++ /* mbedtls does not currently provide way to set an attribute in a CSR: ++ * https://github.com/Mbed-TLS/mbedtls/issues/4886 */ ++ wpa_printf(MSG_ERROR, ++ "mbedtls does not currently support setting challengePassword " ++ "attribute in CSR"); ++ return -1; ++} ++ ++const u8 * mbedtls_x509_csr_attr_oid_value(mbedtls_x509_csr *csr, ++ const char *oid, size_t oid_len, ++ size_t *vlen, int *vtype) ++{ ++ /* Note: mbedtls_x509_csr_parse_der() has parsed and validated CSR, ++ * so validation checks are not repeated here ++ * ++ * It would be nicer if (mbedtls_x509_csr *) had an mbedtls_x509_buf of ++ * Attributes (or at least a pointer) since mbedtls_x509_csr_parse_der() ++ * already parsed the rest of CertificationRequestInfo, some of which is ++ * repeated here to step to Attributes. Since csr->subject_raw.p points ++ * into csr->cri.p, which points into csr->raw.p, step over version and ++ * subject of CertificationRequestInfo (SEQUENCE) */ ++ unsigned char *p = csr->subject_raw.p + csr->subject_raw.len; ++ unsigned char *end = csr->cri.p + csr->cri.len, *ext; ++ size_t len; ++ ++ /* step over SubjectPublicKeyInfo */ ++ mbedtls_asn1_get_tag(&p, end, &len, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ++ p += len; ++ ++ /* Attributes ++ * { ATTRIBUTE:IOSet } ::= SET OF { SEQUENCE { OID, value } } ++ */ ++ if (mbedtls_asn1_get_tag(&p, end, &len, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) { ++ return NULL; ++ } ++ while (p < end) { ++ if (mbedtls_asn1_get_tag(&p, end, &len, ++ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { ++ return NULL; ++ } ++ ext = p; ++ p += len; ++ ++ if (mbedtls_asn1_get_tag(&ext,end,&len,MBEDTLS_ASN1_OID) != 0) ++ return NULL; ++ if (oid_len != len || 0 != memcmp(ext, oid, oid_len)) ++ continue; ++ ++ /* found oid; return value */ ++ *vtype = *ext++; /* tag */ ++ return (mbedtls_asn1_get_len(&ext,end,vlen) == 0) ? ext : NULL; ++ } ++ ++ return NULL; ++} ++ ++const u8 * crypto_csr_get_attribute(struct crypto_csr *csr, ++ enum crypto_csr_attr attr, ++ size_t *len, int *type) ++{ ++ /* specialized for src/common/dpp_crypto.c */ ++ /* sole caller src/common/dpp_crypto.c:dpp_build_csr() passes ++ * attr == CSR_ATTR_CHALLENGE_PASSWORD */ ++ ++ const char *oid; ++ size_t oid_len; ++ switch (attr) { ++ case CSR_ATTR_CHALLENGE_PASSWORD: ++ oid = OBJ_pkcs9_challengePassword; ++ oid_len = sizeof(OBJ_pkcs9_challengePassword)-1; ++ break; ++ default: ++ return NULL; ++ } ++ ++ /* see crypto_csr_verify(); expecting (mbedtls_x509_csr *) tagged |=1 */ ++ if (!((uintptr_t)csr & 1uL)) ++ return NULL; ++ csr = (struct crypto_csr *)((uintptr_t)csr & ~1uL); ++ ++ return mbedtls_x509_csr_attr_oid_value((mbedtls_x509_csr *)csr, ++ oid, oid_len, len, type); ++} ++ ++struct wpabuf * crypto_csr_sign(struct crypto_csr *csr, ++ struct crypto_ec_key *key, ++ enum crypto_hash_alg algo) ++{ ++ mbedtls_md_type_t sig_md; ++ switch (algo) { ++ #ifdef MBEDTLS_SHA256_C ++ case CRYPTO_HASH_ALG_SHA256: sig_md = MBEDTLS_MD_SHA256; break; ++ #endif ++ #ifdef MBEDTLS_SHA512_C ++ case CRYPTO_HASH_ALG_SHA384: sig_md = MBEDTLS_MD_SHA384; break; ++ case CRYPTO_HASH_ALG_SHA512: sig_md = MBEDTLS_MD_SHA512; break; ++ #endif ++ default: ++ return NULL; ++ } ++ mbedtls_x509write_csr_set_md_alg((mbedtls_x509write_csr *)csr, sig_md); ++ ++ #if 0 ++ unsigned char key_usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE ++ | MBEDTLS_X509_KU_KEY_CERT_SIGN; ++ if (mbedtls_x509write_csr_set_key_usage((mbedtls_x509write_csr *)csr, ++ key_usage)) ++ return NULL; ++ #endif ++ ++ #if 0 ++ unsigned char ns_cert_type = MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT ++ | MBEDTLS_X509_NS_CERT_TYPE_EMAIL; ++ if (mbedtls_x509write_csr_set_ns_cert_type((mbedtls_x509write_csr *)csr, ++ ns_cert_type)) ++ return NULL; ++ #endif ++ ++ #if 0 ++ /* mbedtls does not currently provide way to set an attribute in a CSR: ++ * https://github.com/Mbed-TLS/mbedtls/issues/4886 ++ * XXX: hwsim dpp_enterprise test fails due to this limitation. ++ * ++ * Current usage of this function is solely by dpp_build_csr(), ++ * so as a kludge, might consider custom (struct crypto_csr *) ++ * containing (mbedtls_x509write_csr *) and a list of attributes ++ * (i.e. challengePassword). Might have to totally reimplement ++ * mbedtls_x509write_csr_der(); underlying x509write_csr_der_internal() ++ * handles signing the CSR. (This is more work that appending an ++ * Attributes section to end of CSR and adjusting ASN.1 length of CSR.) ++ */ ++ #endif ++ ++ unsigned char buf[4096]; /* XXX: large enough? too large? */ ++ int len = mbedtls_x509write_csr_der((mbedtls_x509write_csr *)csr, ++ buf, sizeof(buf), ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg()); ++ if (len < 0) ++ return NULL; ++ /* Note: data is written at the end of the buffer! Use the ++ * return value to determine where you should start ++ * using the buffer */ ++ return wpabuf_alloc_copy(buf+sizeof(buf)-len, (size_t)len); ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_CSR */ ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_PKCS7 ++ ++#if 0 ++#include /* PKCS7 is not currently supported in mbedtls */ ++#include ++#endif ++ ++struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7) ++{ ++ /* PKCS7 is not currently supported in mbedtls */ ++ return NULL; ++ ++#if 0 ++ /* https://github.com/naynajain/mbedtls-1 branch: development-pkcs7 ++ * (??? potential future contribution to mbedtls ???) */ ++ ++ /* Note: PKCS7 signature *is not* verified by this function. ++ * The function interface does not provide for passing a certificate */ ++ ++ mbedtls_pkcs7 mpkcs7; ++ mbedtls_pkcs7_init(&mpkcs7); ++ int pkcs7_type = mbedtls_pkcs7_parse_der(wpabuf_head(pkcs7), ++ wpabuf_len(pkcs7), ++ &mpkcs7); ++ wpabuf *buf = NULL; ++ do { ++ if (pkcs7_type < 0) ++ break; ++ ++ /* src/common/dpp.c:dpp_parse_cred_dot1x() interested in certs ++ * for wpa_supplicant/dpp_supplicant.c:wpas_dpp_add_network() ++ * (? are adding certificate headers and footers desired ?) */ ++ ++ /* development-pkcs7 branch does not currently provide ++ * additional interfaces to retrieve the parsed data */ ++ ++ mbedtls_x509_crt *certs = ++ &mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(certs); ++ int ncerts = ++ mpkcs7.MBEDTLS_PRIVATE(signed_data).MBEDTLS_PRIVATE(no_of_certs); ++ ++ /* allocate buffer for PEM (base64-encoded DER) ++ * plus header, footer, newlines, and some extra */ ++ buf = wpabuf_alloc((wpabuf_len(pkcs7)+2)/3*4 + ncerts*64); ++ if (buf == NULL) ++ break; ++ ++ #define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" ++ #define PEM_END_CRT "-----END CERTIFICATE-----\n" ++ size_t olen; ++ for (int i = 0; i < ncerts; ++i) { ++ int ret = mbedtls_pem_write_buffer( ++ PEM_BEGIN_CRT, PEM_END_CRT, ++ certs[i].raw.p, certs[i].raw.len, ++ wpabuf_mhead(buf, 0), wpabuf_tailroom(buf), ++ &olen)); ++ if (ret == 0) ++ wpabuf_put(buf, olen); ++ } else { ++ if (ret == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) ++ ret = wpabuf_resize( ++ &buf,olen-wpabuf_tailroom(buf)); ++ if (ret == 0) { ++ --i;/*(adjust loop iterator for retry)*/ ++ continue; ++ } ++ wpabuf_free(buf); ++ buf = NULL; ++ break; ++ } ++ } ++ } while (0); ++ ++ mbedtls_pkcs7_free(&mpkcs7); ++ return buf; ++#endif ++} ++ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_PKCS7 */ ++ ++ ++#ifdef MBEDTLS_ARC4_C ++#include ++int rc4_skip(const u8 *key, size_t keylen, size_t skip, ++ u8 *data, size_t data_len) ++{ ++ mbedtls_arc4_context ctx; ++ mbedtls_arc4_init(&ctx); ++ mbedtls_arc4_setup(&ctx, key, keylen); ++ ++ if (skip) { ++ /*(prefer [16] on ancient hardware with smaller cache lines)*/ ++ unsigned char skip_buf[64]; /*('skip' is generally small)*/ ++ /*os_memset(skip_buf, 0, sizeof(skip_buf));*/ /*(necessary?)*/ ++ size_t len; ++ do { ++ len = skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip; ++ mbedtls_arc4_crypt(&ctx, len, skip_buf, skip_buf); ++ } while ((skip -= len)); ++ } ++ ++ int ret = mbedtls_arc4_crypt(&ctx, data_len, data, data); ++ mbedtls_arc4_free(&ctx); ++ return ret; ++} ++#endif ++ ++ ++/* duplicated in tls_mbedtls.c:tls_mbedtls_readfile()*/ ++__attribute_noinline__ ++static int crypto_mbedtls_readfile(const char *path, u8 **buf, size_t *n) ++{ ++ #if 0 /* #ifdef MBEDTLS_FS_IO */ ++ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/ ++ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) { ++ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path); ++ return -1; ++ } ++ #else ++ /*(use os_readfile() so that we can use os_free() ++ *(if we use mbedtls_pk_load_file() above, macros prevent calling free() ++ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free() ++ * on buf aborts in tests if buf not allocated via os_malloc())*/ ++ *buf = (u8 *)os_readfile(path, n); ++ if (!*buf) { ++ wpa_printf(MSG_ERROR, "error: os_readfile %s", path); ++ return -1; ++ } ++ u8 *buf0 = os_realloc(*buf, *n+1); ++ if (!buf0) { ++ bin_clear_free(*buf, *n); ++ *buf = NULL; ++ return -1; ++ } ++ buf0[(*n)++] = '\0'; ++ *buf = buf0; ++ #endif ++ return 0; ++} ++ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_RSA ++#ifdef MBEDTLS_RSA_C ++ ++#include ++#include ++ ++struct crypto_rsa_key * crypto_rsa_key_read(const char *file, bool private_key) ++{ ++ /* mbedtls_pk_parse_keyfile() and mbedtls_pk_parse_public_keyfile() ++ * require #ifdef MBEDTLS_FS_IO in mbedtls library. Prefer to use ++ * crypto_mbedtls_readfile(), which wraps os_readfile() */ ++ u8 *data; ++ size_t len; ++ if (crypto_mbedtls_readfile(file, &data, &len) != 0) ++ return NULL; ++ ++ mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx)); ++ if (ctx == NULL) { ++ bin_clear_free(data, len); ++ return NULL; ++ } ++ mbedtls_pk_init(ctx); ++ ++ int rc; ++ rc = (private_key ++ ? mbedtls_pk_parse_key(ctx, data, len, NULL, 0 ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ ,mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg() ++ #endif ++ ) ++ : mbedtls_pk_parse_public_key(ctx, data, len)) == 0 ++ && mbedtls_pk_can_do(ctx, MBEDTLS_PK_RSA); ++ ++ bin_clear_free(data, len); ++ ++ if (rc) { ++ /* use MBEDTLS_RSA_PKCS_V21 padding for RSAES-OAEP */ ++ /* use MBEDTLS_MD_SHA256 for these hostap interfaces */ ++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */ ++ /*(no return value in mbedtls 2.x)*/ ++ mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx), ++ MBEDTLS_RSA_PKCS_V21, ++ MBEDTLS_MD_SHA256); ++ #else ++ if (mbedtls_rsa_set_padding(mbedtls_pk_rsa(*ctx), ++ MBEDTLS_RSA_PKCS_V21, ++ MBEDTLS_MD_SHA256) == 0) ++ #endif ++ return (struct crypto_rsa_key *)ctx; ++ } ++ ++ mbedtls_pk_free(ctx); ++ os_free(ctx); ++ return NULL; ++} ++ ++struct wpabuf * crypto_rsa_oaep_sha256_encrypt(struct crypto_rsa_key *key, ++ const struct wpabuf *in) ++{ ++ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key); ++ size_t olen = mbedtls_rsa_get_len(pk_rsa); ++ struct wpabuf *buf = wpabuf_alloc(olen); ++ if (buf == NULL) ++ return NULL; ++ ++ /* mbedtls_pk_encrypt() takes a few more hops to get to same func */ ++ if (mbedtls_rsa_rsaes_oaep_encrypt(pk_rsa, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg(), ++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */ ++ MBEDTLS_RSA_PRIVATE, ++ #endif ++ NULL, 0, ++ wpabuf_len(in), wpabuf_head(in), ++ wpabuf_put(buf, olen)) == 0) { ++ return buf; ++ } ++ ++ wpabuf_clear_free(buf); ++ return NULL; ++} ++ ++struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key, ++ const struct wpabuf *in) ++{ ++ mbedtls_rsa_context *pk_rsa = mbedtls_pk_rsa(*(mbedtls_pk_context*)key); ++ size_t olen = mbedtls_rsa_get_len(pk_rsa); ++ struct wpabuf *buf = wpabuf_alloc(olen); ++ if (buf == NULL) ++ return NULL; ++ ++ /* mbedtls_pk_decrypt() takes a few more hops to get to same func */ ++ if (mbedtls_rsa_rsaes_oaep_decrypt(pk_rsa, ++ mbedtls_ctr_drbg_random, ++ crypto_mbedtls_ctr_drbg(), ++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */ ++ MBEDTLS_RSA_PUBLIC, ++ #endif ++ NULL, 0, &olen, wpabuf_head(in), ++ wpabuf_mhead(buf), olen) == 0) { ++ wpabuf_put(buf, olen); ++ return buf; ++ } ++ ++ wpabuf_clear_free(buf); ++ return NULL; ++} ++ ++void crypto_rsa_key_free(struct crypto_rsa_key *key) ++{ ++ mbedtls_pk_free((mbedtls_pk_context *)key); ++ os_free(key); ++} ++ ++#endif /* MBEDTLS_RSA_C */ ++#endif /* CRYPTO_MBEDTLS_CRYPTO_RSA */ ++ ++#ifdef CRYPTO_MBEDTLS_CRYPTO_HPKE ++ ++struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id, ++ enum hpke_kdf_id kdf_id, ++ enum hpke_aead_id aead_id, ++ struct crypto_ec_key *peer_pub, ++ const u8 *info, size_t info_len, ++ const u8 *aad, size_t aad_len, ++ const u8 *pt, size_t pt_len) ++{ ++ /* not yet implemented */ ++ return NULL; ++} ++ ++struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id, ++ enum hpke_kdf_id kdf_id, ++ enum hpke_aead_id aead_id, ++ struct crypto_ec_key *own_priv, ++ const u8 *info, size_t info_len, ++ const u8 *aad, size_t aad_len, ++ const u8 *enc_ct, size_t enc_ct_len) ++{ ++ /* not yet implemented */ ++ return NULL; ++} ++ ++#endif +--- /dev/null ++++ b/src/crypto/tls_mbedtls.c +@@ -0,0 +1,3313 @@ ++/* ++ * SSL/TLS interface functions for mbed TLS ++ * ++ * SPDX-FileCopyrightText: 2022 Glenn Strauss ++ * SPDX-License-Identifier: BSD-3-Clause ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ * ++ * template: src/crypto/tls_none.c ++ * reference: src/crypto/tls_*.c ++ * ++ * Known Limitations: ++ * - no TLSv1.3 (not available in mbedtls 2.x; experimental in mbedtls 3.x) ++ * - no OCSP (not yet available in mbedtls) ++ * - mbedtls does not support all certificate encodings used by hwsim tests ++ * PCKS#5 v1.5 ++ * PCKS#12 ++ * DH DSA ++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c ++ * - mbedtls does not currently provide way to set an attribute in a CSR ++ * https://github.com/Mbed-TLS/mbedtls/issues/4886 ++ * so tests/hwsim dpp_enterprise tests fail ++ * - DPP2 not supported ++ * PKCS#7 parsing is not supported in mbedtls ++ * See crypto_mbedtls.c:crypto_pkcs7_get_certificates() comments ++ * - DPP3 not supported ++ * hpke_base_seal() and hpke_base_seal() not implemented in crypto_mbedtls.c ++ * ++ * Status: ++ * - code written to be compatible with mbedtls 2.x and mbedtls 3.x ++ * (currently requires mbedtls >= 2.27.0 for mbedtls_mpi_random()) ++ * (currently requires mbedtls >= 2.18.0 for mbedtls_ssl_tls_prf()) ++ * - builds with tests/build/build-wpa_supplicant-mbedtls.config ++ * - passes all tests/ crypto module tests (incomplete coverage) ++ * ($ cd tests; make clean; make -j 4 run-tests CONFIG_TLS=mbedtls) ++ * - passes almost all tests/hwsim tests ++ * (hwsim tests skipped for missing features) ++ * ++ * RFE: ++ * - EAP-FAST, EAP-TEAP session ticket support not implemented in tls_mbedtls.c ++ * - client/server session resumption, and/or save client session ticket ++ */ ++ ++#include "includes.h" ++#include "common.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include /* mbedtls_calloc() mbedtls_free() */ ++#include /* mbedtls_platform_zeroize() */ ++#include ++#include ++#include ++#include ++ ++#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.4.0 */ ++#include ++#else ++#include ++#endif ++ ++#ifndef MBEDTLS_PRIVATE ++#define MBEDTLS_PRIVATE(x) x ++#endif ++ ++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */ ++#define mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl) \ ++ ((ssl)->MBEDTLS_PRIVATE(session) \ ++ ?(ssl)->MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(ciphersuite) \ ++ : 0) ++#define mbedtls_ssl_ciphersuite_get_name(info) \ ++ (info)->MBEDTLS_PRIVATE(name) ++#endif ++ ++#include "crypto.h" /* sha256_vector() */ ++#include "tls.h" ++ ++#ifndef SHA256_DIGEST_LENGTH ++#define SHA256_DIGEST_LENGTH 32 ++#endif ++ ++#ifndef MBEDTLS_EXPKEY_FIXED_SECRET_LEN ++#define MBEDTLS_EXPKEY_FIXED_SECRET_LEN 48 ++#endif ++ ++#ifndef MBEDTLS_EXPKEY_RAND_LEN ++#define MBEDTLS_EXPKEY_RAND_LEN 32 ++#endif ++ ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++static mbedtls_ssl_export_keys_t tls_connection_export_keys_cb; ++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ ++static mbedtls_ssl_export_keys_ext_t tls_connection_export_keys_cb; ++#else /*(not implemented; return error)*/ ++#define mbedtls_ssl_tls_prf(a,b,c,d,e,f,g,h) (-1) ++typedef mbedtls_tls_prf_types int; ++#endif ++ ++ ++/* hostapd/wpa_supplicant provides forced_memzero(), ++ * but prefer mbedtls_platform_zeroize() */ ++#define forced_memzero(ptr,sz) mbedtls_platform_zeroize(ptr,sz) ++ ++ ++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \ ++ || defined(EAP_TEAP) || defined(EAP_SERVER_TEAP) ++#ifdef MBEDTLS_SSL_SESSION_TICKETS ++#ifdef MBEDTLS_SSL_TICKET_C ++#define TLS_MBEDTLS_SESSION_TICKETS ++#if defined(EAP_TEAP) || defined(EAP_SERVER_TEAP) ++#define TLS_MBEDTLS_EAP_TEAP ++#endif ++#if !defined(CONFIG_FIPS) /* EAP-FAST keys cannot be exported in FIPS mode */ ++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) ++#define TLS_MBEDTLS_EAP_FAST ++#endif ++#endif ++#endif ++#endif ++#endif ++ ++ ++struct tls_conf { ++ mbedtls_ssl_config conf; ++ ++ unsigned int verify_peer:1; ++ unsigned int verify_depth0_only:1; ++ unsigned int check_crl:2; /*(needs :2 bits for 0, 1, 2)*/ ++ unsigned int check_crl_strict:1; /*(needs :1 bit for 0, 1)*/ ++ unsigned int ca_cert_probe:1; ++ unsigned int has_ca_cert:1; ++ unsigned int has_client_cert:1; ++ unsigned int has_private_key:1; ++ unsigned int suiteb128:1; ++ unsigned int suiteb192:1; ++ mbedtls_x509_crl *crl; ++ mbedtls_x509_crt ca_cert; ++ mbedtls_x509_crt client_cert; ++ mbedtls_pk_context private_key; ++ ++ uint32_t refcnt; ++ ++ unsigned int flags; ++ char *subject_match; ++ char *altsubject_match; ++ char *suffix_match; ++ char *domain_match; ++ char *check_cert_subject; ++ u8 ca_cert_hash[SHA256_DIGEST_LENGTH]; ++ ++ int *ciphersuites; /* list of ciphersuite ids for mbedtls_ssl_config */ ++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */ ++ mbedtls_ecp_group_id *curves; ++#else ++ uint16_t *curves; /* list of curve ids for mbedtls_ssl_config */ ++#endif ++}; ++ ++ ++struct tls_global { ++ struct tls_conf *tls_conf; ++ char *ocsp_stapling_response; ++ mbedtls_ctr_drbg_context *ctr_drbg; /*(see crypto_mbedtls.c)*/ ++ #ifdef MBEDTLS_SSL_SESSION_TICKETS ++ mbedtls_ssl_ticket_context ticket_ctx; ++ #endif ++ char *ca_cert_file; ++ struct os_reltime crl_reload_previous; ++ unsigned int crl_reload_interval; ++ uint32_t refcnt; ++ struct tls_config init_conf; ++}; ++ ++static struct tls_global tls_ctx_global; ++ ++ ++struct tls_connection { ++ struct tls_conf *tls_conf; ++ struct wpabuf *push_buf; ++ struct wpabuf *pull_buf; ++ size_t pull_buf_offset; ++ ++ unsigned int established:1; ++ unsigned int resumed:1; ++ unsigned int verify_peer:1; ++ unsigned int is_server:1; ++ ++ mbedtls_ssl_context ssl; ++ ++ mbedtls_tls_prf_types tls_prf_type; ++ size_t expkey_keyblock_size; ++ size_t expkey_secret_len; ++ #if MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */ ++ unsigned char expkey_secret[MBEDTLS_EXPKEY_FIXED_SECRET_LEN]; ++ #else ++ unsigned char expkey_secret[MBEDTLS_MD_MAX_SIZE]; ++ #endif ++ unsigned char expkey_randbytes[MBEDTLS_EXPKEY_RAND_LEN*2]; ++ ++ int read_alerts, write_alerts, failed; ++ ++ #ifdef TLS_MBEDTLS_SESSION_TICKETS ++ tls_session_ticket_cb session_ticket_cb; ++ void *session_ticket_cb_ctx; ++ unsigned char *clienthello_session_ticket; ++ size_t clienthello_session_ticket_len; ++ #endif ++ char *peer_subject; /* peer subject info for authenticated peer */ ++ struct wpabuf *success_data; ++}; ++ ++ ++#ifndef __has_attribute ++#define __has_attribute(x) 0 ++#endif ++ ++#ifndef __GNUC_PREREQ ++#define __GNUC_PREREQ(maj,min) 0 ++#endif ++ ++#ifndef __attribute_cold__ ++#if __has_attribute(cold) \ ++ || __GNUC_PREREQ(4,3) ++#define __attribute_cold__ __attribute__((__cold__)) ++#else ++#define __attribute_cold__ ++#endif ++#endif ++ ++#ifndef __attribute_noinline__ ++#if __has_attribute(noinline) \ ++ || __GNUC_PREREQ(3,1) ++#define __attribute_noinline__ __attribute__((__noinline__)) ++#else ++#define __attribute_noinline__ ++#endif ++#endif ++ ++ ++__attribute_cold__ ++__attribute_noinline__ ++static void emsg(int level, const char * const msg) ++{ ++ wpa_printf(level, "MTLS: %s", msg); ++} ++ ++ ++__attribute_cold__ ++__attribute_noinline__ ++static void emsgrc(int level, const char * const msg, int rc) ++{ ++ #ifdef MBEDTLS_ERROR_C ++ /* error logging convenience function that decodes mbedtls result codes */ ++ char buf[256]; ++ mbedtls_strerror(rc, buf, sizeof(buf)); ++ wpa_printf(level, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc); ++ #else ++ wpa_printf(level, "MTLS: %s: (-0x%04x)", msg, -rc); ++ #endif ++} ++ ++ ++#define elog(rc, msg) emsgrc(MSG_ERROR, (msg), (rc)) ++#define ilog(rc, msg) emsgrc(MSG_INFO, (msg), (rc)) ++ ++ ++struct tls_conf * tls_conf_init(void *tls_ctx) ++{ ++ struct tls_conf *tls_conf = os_zalloc(sizeof(*tls_conf)); ++ if (tls_conf == NULL) ++ return NULL; ++ tls_conf->refcnt = 1; ++ ++ mbedtls_ssl_config_init(&tls_conf->conf); ++ mbedtls_ssl_conf_rng(&tls_conf->conf, ++ mbedtls_ctr_drbg_random, tls_ctx_global.ctr_drbg); ++ mbedtls_x509_crt_init(&tls_conf->ca_cert); ++ mbedtls_x509_crt_init(&tls_conf->client_cert); ++ mbedtls_pk_init(&tls_conf->private_key); ++ ++ return tls_conf; ++} ++ ++ ++void tls_conf_deinit(struct tls_conf *tls_conf) ++{ ++ if (tls_conf == NULL || --tls_conf->refcnt != 0) ++ return; ++ ++ mbedtls_x509_crt_free(&tls_conf->ca_cert); ++ mbedtls_x509_crt_free(&tls_conf->client_cert); ++ if (tls_conf->crl) { ++ mbedtls_x509_crl_free(tls_conf->crl); ++ os_free(tls_conf->crl); ++ } ++ mbedtls_pk_free(&tls_conf->private_key); ++ mbedtls_ssl_config_free(&tls_conf->conf); ++ os_free(tls_conf->curves); ++ os_free(tls_conf->ciphersuites); ++ os_free(tls_conf->subject_match); ++ os_free(tls_conf->altsubject_match); ++ os_free(tls_conf->suffix_match); ++ os_free(tls_conf->domain_match); ++ os_free(tls_conf->check_cert_subject); ++ os_free(tls_conf); ++} ++ ++ ++mbedtls_ctr_drbg_context * crypto_mbedtls_ctr_drbg(void); /*(not in header)*/ ++ ++__attribute_cold__ ++void * tls_init(const struct tls_config *conf) ++{ ++ /* RFE: review struct tls_config *conf (different from tls_conf) */ ++ ++ if (++tls_ctx_global.refcnt > 1) ++ return &tls_ctx_global; ++ ++ tls_ctx_global.ctr_drbg = crypto_mbedtls_ctr_drbg(); ++ #ifdef MBEDTLS_SSL_SESSION_TICKETS ++ mbedtls_ssl_ticket_init(&tls_ctx_global.ticket_ctx); ++ mbedtls_ssl_ticket_setup(&tls_ctx_global.ticket_ctx, ++ mbedtls_ctr_drbg_random, ++ tls_ctx_global.ctr_drbg, ++ MBEDTLS_CIPHER_AES_256_GCM, ++ 43200); /* ticket timeout: 12 hours */ ++ #endif ++ /* copy struct for future use */ ++ tls_ctx_global.init_conf = *conf; ++ if (conf->openssl_ciphers) ++ tls_ctx_global.init_conf.openssl_ciphers = ++ os_strdup(conf->openssl_ciphers); ++ ++ tls_ctx_global.crl_reload_interval = conf->crl_reload_interval; ++ os_get_reltime(&tls_ctx_global.crl_reload_previous); ++ ++ return &tls_ctx_global; ++} ++ ++ ++__attribute_cold__ ++void tls_deinit(void *tls_ctx) ++{ ++ if (tls_ctx == NULL || --tls_ctx_global.refcnt != 0) ++ return; ++ ++ tls_conf_deinit(tls_ctx_global.tls_conf); ++ os_free(tls_ctx_global.ca_cert_file); ++ os_free(tls_ctx_global.ocsp_stapling_response); ++ char *openssl_ciphers; /*(allocated in tls_init())*/ ++ *(const char **)&openssl_ciphers = ++ tls_ctx_global.init_conf.openssl_ciphers; ++ os_free(openssl_ciphers); ++ #ifdef MBEDTLS_SSL_SESSION_TICKETS ++ mbedtls_ssl_ticket_free(&tls_ctx_global.ticket_ctx); ++ #endif ++ os_memset(&tls_ctx_global, 0, sizeof(tls_ctx_global)); ++} ++ ++ ++int tls_get_errors(void *tls_ctx) ++{ ++ return 0; ++} ++ ++ ++static void tls_connection_deinit_expkey(struct tls_connection *conn) ++{ ++ conn->tls_prf_type = 0; /* MBEDTLS_SSL_TLS_PRF_NONE; */ ++ conn->expkey_keyblock_size = 0; ++ conn->expkey_secret_len = 0; ++ forced_memzero(conn->expkey_secret, sizeof(conn->expkey_secret)); ++ forced_memzero(conn->expkey_randbytes, sizeof(conn->expkey_randbytes)); ++} ++ ++ ++#ifdef TLS_MBEDTLS_SESSION_TICKETS ++void tls_connection_deinit_clienthello_session_ticket(struct tls_connection *conn) ++{ ++ if (conn->clienthello_session_ticket) { ++ mbedtls_platform_zeroize(conn->clienthello_session_ticket, ++ conn->clienthello_session_ticket_len); ++ mbedtls_free(conn->clienthello_session_ticket); ++ conn->clienthello_session_ticket = NULL; ++ conn->clienthello_session_ticket_len = 0; ++ } ++} ++#endif ++ ++ ++void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) ++{ ++ if (conn == NULL) ++ return; ++ ++ #if 0 /*(good intention, but never sent since we destroy self below)*/ ++ if (conn->established) ++ mbedtls_ssl_close_notify(&conn->ssl); ++ #endif ++ ++ if (conn->tls_prf_type) ++ tls_connection_deinit_expkey(conn); ++ ++ #ifdef TLS_MBEDTLS_SESSION_TICKETS ++ if (conn->clienthello_session_ticket) ++ tls_connection_deinit_clienthello_session_ticket(conn); ++ #endif ++ ++ os_free(conn->peer_subject); ++ wpabuf_free(conn->success_data); ++ wpabuf_free(conn->push_buf); ++ wpabuf_free(conn->pull_buf); ++ mbedtls_ssl_free(&conn->ssl); ++ tls_conf_deinit(conn->tls_conf); ++ os_free(conn); ++} ++ ++ ++static void tls_mbedtls_refresh_crl(void); ++static int tls_mbedtls_ssl_setup(struct tls_connection *conn); ++ ++struct tls_connection * tls_connection_init(void *tls_ctx) ++{ ++ struct tls_connection *conn = os_zalloc(sizeof(*conn)); ++ if (conn == NULL) ++ return NULL; ++ ++ mbedtls_ssl_init(&conn->ssl); ++ ++ conn->tls_conf = tls_ctx_global.tls_conf; /*(inherit global conf, if set)*/ ++ if (conn->tls_conf) { ++ ++conn->tls_conf->refcnt; ++ /* check for CRL refresh if inheriting from global config */ ++ tls_mbedtls_refresh_crl(); ++ ++ conn->verify_peer = conn->tls_conf->verify_peer; ++ if (tls_mbedtls_ssl_setup(conn) != 0) { ++ tls_connection_deinit(&tls_ctx_global, conn); ++ return NULL; ++ } ++ } ++ ++ return conn; ++} ++ ++ ++int tls_connection_established(void *tls_ctx, struct tls_connection *conn) ++{ ++ return conn ? conn->established : 0; ++} ++ ++ ++__attribute_noinline__ ++char * tls_mbedtls_peer_serial_num(const mbedtls_x509_crt *crt, char *serial_num, size_t len) ++{ ++ /* mbedtls_x509_serial_gets() inefficiently formats to hex separated by ++ * colons, so generate the hex serial number here. The func ++ * wpa_snprintf_hex_uppercase() is similarly inefficient. */ ++ size_t i = 0; /* skip leading 0's per Distinguished Encoding Rules (DER) */ ++ while (i < crt->serial.len && crt->serial.p[i] == 0) ++i; ++ if (i == crt->serial.len) --i; ++ ++ const unsigned char *s = crt->serial.p + i; ++ const size_t e = (crt->serial.len - i) * 2; ++ if (e >= len) ++ return NULL; ++ #if 0 ++ wpa_snprintf_hex_uppercase(serial_num, len, s, crt->serial.len-i); ++ #else ++ for (i = 0; i < e; i+=2, ++s) { ++ serial_num[i+0] = "0123456789ABCDEF"[(*s >> 4)]; ++ serial_num[i+1] = "0123456789ABCDEF"[(*s & 0xF)]; ++ } ++ serial_num[e] = '\0'; ++ #endif ++ return serial_num; ++} ++ ++ ++char * tls_connection_peer_serial_num(void *tls_ctx, ++ struct tls_connection *conn) ++{ ++ const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&conn->ssl); ++ if (crt == NULL) ++ return NULL; ++ size_t len = crt->serial.len * 2 + 1; ++ char *serial_num = os_malloc(len); ++ if (!serial_num) ++ return NULL; ++ return tls_mbedtls_peer_serial_num(crt, serial_num, len); ++} ++ ++ ++static void tls_pull_buf_reset(struct tls_connection *conn); ++ ++int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) ++{ ++ /* Note: this function called from eap_peer_tls_reauth_init() ++ * for session resumption, not for connection shutdown */ ++ ++ if (conn == NULL) ++ return -1; ++ ++ tls_pull_buf_reset(conn); ++ wpabuf_free(conn->push_buf); ++ conn->push_buf = NULL; ++ conn->established = 0; ++ conn->resumed = 0; ++ if (conn->tls_prf_type) ++ tls_connection_deinit_expkey(conn); ++ ++ /* RFE: prepare for session resumption? (see doc in crypto/tls.h) */ ++ ++ return mbedtls_ssl_session_reset(&conn->ssl); ++} ++ ++ ++static int tls_wpabuf_resize_put_data(struct wpabuf **buf, ++ const unsigned char *data, size_t dlen) ++{ ++ if (wpabuf_resize(buf, dlen) < 0) ++ return 0; ++ wpabuf_put_data(*buf, data, dlen); ++ return 1; ++} ++ ++ ++static int tls_pull_buf_append(struct tls_connection *conn, ++ const struct wpabuf *in_data) ++{ ++ /*(interface does not lend itself to move semantics)*/ ++ return tls_wpabuf_resize_put_data(&conn->pull_buf, ++ wpabuf_head(in_data), ++ wpabuf_len(in_data)); ++} ++ ++ ++static void tls_pull_buf_reset(struct tls_connection *conn) ++{ ++ /*(future: might consider reusing conn->pull_buf)*/ ++ wpabuf_free(conn->pull_buf); ++ conn->pull_buf = NULL; ++ conn->pull_buf_offset = 0; ++} ++ ++ ++__attribute_cold__ ++static void tls_pull_buf_discard(struct tls_connection *conn, const char *func) ++{ ++ size_t discard = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset; ++ if (discard) ++ wpa_printf(MSG_DEBUG, ++ "%s - %zu bytes remaining in pull_buf; discarding", ++ func, discard); ++ tls_pull_buf_reset(conn); ++} ++ ++ ++static int tls_pull_func(void *ptr, unsigned char *buf, size_t len) ++{ ++ struct tls_connection *conn = (struct tls_connection *) ptr; ++ if (conn->pull_buf == NULL) ++ return MBEDTLS_ERR_SSL_WANT_READ; ++ const size_t dlen = wpabuf_len(conn->pull_buf) - conn->pull_buf_offset; ++ if (dlen == 0) ++ return MBEDTLS_ERR_SSL_WANT_READ; ++ ++ if (len > dlen) ++ len = dlen; ++ os_memcpy(buf, wpabuf_head(conn->pull_buf)+conn->pull_buf_offset, len); ++ ++ if (len == dlen) { ++ tls_pull_buf_reset(conn); ++ /*wpa_printf(MSG_DEBUG, "%s - emptied pull_buf", __func__);*/ ++ } ++ else { ++ conn->pull_buf_offset += len; ++ /*wpa_printf(MSG_DEBUG, "%s - %zu bytes remaining in pull_buf", ++ __func__, dlen - len);*/ ++ } ++ return (int)len; ++} ++ ++ ++static int tls_push_func(void *ptr, const unsigned char *buf, size_t len) ++{ ++ struct tls_connection *conn = (struct tls_connection *) ptr; ++ return tls_wpabuf_resize_put_data(&conn->push_buf, buf, len) ++ ? (int)len ++ : MBEDTLS_ERR_SSL_ALLOC_FAILED; ++} ++ ++ ++static int ++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags); ++ ++ ++static int tls_mbedtls_ssl_setup(struct tls_connection *conn) ++{ ++ #if 0 ++ /* mbedtls_ssl_setup() must be called only once */ ++ /* If this func might be called multiple times (e.g. via set_params), ++ * then we should set a flag in conn that ssl was initialized */ ++ if (conn->ssl_is_init) { ++ mbedtls_ssl_free(&conn->ssl); ++ mbedtls_ssl_init(&conn->ssl); ++ } ++ #endif ++ ++ int ret = mbedtls_ssl_setup(&conn->ssl, &conn->tls_conf->conf); ++ if (ret != 0) { ++ elog(ret, "mbedtls_ssl_setup"); ++ return -1; ++ } ++ ++ mbedtls_ssl_set_bio(&conn->ssl, conn, tls_push_func, tls_pull_func, NULL); ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ mbedtls_ssl_set_export_keys_cb( ++ &conn->ssl, tls_connection_export_keys_cb, conn); ++ #elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ ++ mbedtls_ssl_conf_export_keys_ext_cb( ++ &conn->tls_conf->conf, tls_connection_export_keys_cb, conn); ++ #endif ++ if (conn->verify_peer) ++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn); ++ ++ return 0; ++} ++ ++ ++static int tls_mbedtls_data_is_pem(const u8 *data) ++{ ++ return (NULL != os_strstr((char *)data, "-----")); ++} ++ ++ ++static void tls_mbedtls_set_allowed_tls_vers(struct tls_conf *tls_conf, ++ mbedtls_ssl_config *conf) ++{ ++ #if !defined(MBEDTLS_SSL_PROTO_TLS1_3) ++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_3; ++ #endif ++ ++ /* unconditionally require TLSv1.2+ for TLS_CONN_SUITEB */ ++ if (tls_conf->flags & TLS_CONN_SUITEB) { ++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_0; ++ tls_conf->flags |= TLS_CONN_DISABLE_TLSv1_1; ++ } ++ ++ const unsigned int flags = tls_conf->flags; ++ ++ /* attempt to map flags to min and max TLS protocol version */ ++ ++ int min = (flags & TLS_CONN_DISABLE_TLSv1_0) ++ ? (flags & TLS_CONN_DISABLE_TLSv1_1) ++ ? (flags & TLS_CONN_DISABLE_TLSv1_2) ++ ? (flags & TLS_CONN_DISABLE_TLSv1_3) ++ ? 4 ++ : 3 ++ : 2 ++ : 1 ++ : 0; ++ ++ int max = (flags & TLS_CONN_DISABLE_TLSv1_3) ++ ? (flags & TLS_CONN_DISABLE_TLSv1_2) ++ ? (flags & TLS_CONN_DISABLE_TLSv1_1) ++ ? (flags & TLS_CONN_DISABLE_TLSv1_0) ++ ? -1 ++ : 0 ++ : 1 ++ : 2 ++ : 3; ++ ++ if ((flags & TLS_CONN_ENABLE_TLSv1_2) && min > 2) min = 2; ++ if ((flags & TLS_CONN_ENABLE_TLSv1_1) && min > 1) min = 1; ++ if ((flags & TLS_CONN_ENABLE_TLSv1_0) && min > 0) min = 0; ++ if (max < min) { ++ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring"); ++ return; ++ } ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ /* mbed TLS 3.0.0 removes support for protocols < TLSv1.2 */ ++ if (min < 2 || max < 2) { ++ emsg(MSG_ERROR, "invalid tls_disable_tlsv* params; ignoring"); ++ if (min < 2) min = 2; ++ if (max < 2) max = 2; ++ } ++ #endif ++ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */ ++ /* MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303 *//*!< (D)TLS 1.2 */ ++ /* MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 *//*!< (D)TLS 1.3 */ ++ min = (min == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3; ++ max = (max == 2) ? MBEDTLS_SSL_VERSION_TLS1_2 : MBEDTLS_SSL_VERSION_TLS1_3; ++ mbedtls_ssl_conf_min_tls_version(conf, min); ++ mbedtls_ssl_conf_max_tls_version(conf, max); ++ #else ++ #ifndef MBEDTLS_SSL_MINOR_VERSION_4 ++ if (min == 3) min = 2; ++ if (max == 3) max = 2; ++ #endif ++ /* MBEDTLS_SSL_MINOR_VERSION_0 0 *//*!< SSL v3.0 */ ++ /* MBEDTLS_SSL_MINOR_VERSION_1 1 *//*!< TLS v1.0 */ ++ /* MBEDTLS_SSL_MINOR_VERSION_2 2 *//*!< TLS v1.1 */ ++ /* MBEDTLS_SSL_MINOR_VERSION_3 3 *//*!< TLS v1.2 */ ++ /* MBEDTLS_SSL_MINOR_VERSION_4 4 *//*!< TLS v1.3 */ ++ mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, min+1); ++ mbedtls_ssl_conf_max_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, max+1); ++ #endif ++} ++ ++ ++__attribute_noinline__ ++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n); ++ ++ ++static int ++tls_mbedtls_set_dhparams(struct tls_conf *tls_conf, const char *dh_file) ++{ ++ size_t len; ++ u8 *data; ++ if (tls_mbedtls_readfile(dh_file, &data, &len)) ++ return 0; ++ ++ /* parse only if DH parameters if in PEM format */ ++ if (tls_mbedtls_data_is_pem(data) ++ && NULL == os_strstr((char *)data, "-----BEGIN DH PARAMETERS-----")) { ++ if (os_strstr((char *)data, "-----BEGIN DSA PARAMETERS-----")) ++ wpa_printf(MSG_WARNING, "DSA parameters not handled (%s)", dh_file); ++ else ++ wpa_printf(MSG_WARNING, "unexpected DH param content (%s)",dh_file); ++ forced_memzero(data, len); ++ os_free(data); ++ return 0; ++ } ++ ++ /* mbedtls_dhm_parse_dhm() expects "-----BEGIN DH PARAMETERS-----" if PEM */ ++ mbedtls_dhm_context dhm; ++ mbedtls_dhm_init(&dhm); ++ int rc = mbedtls_dhm_parse_dhm(&dhm, data, len); ++ if (0 == rc) ++ rc = mbedtls_ssl_conf_dh_param_ctx(&tls_conf->conf, &dhm); ++ if (0 != rc) ++ elog(rc, dh_file); ++ mbedtls_dhm_free(&dhm); ++ ++ forced_memzero(data, len); ++ os_free(data); ++ return (0 == rc); ++} ++ ++ ++/* reference: lighttpd src/mod_mbedtls.c:mod_mbedtls_ssl_append_curve() ++ * (same author: gstrauss@gluelogic.com; same license: BSD-3-Clause) */ ++#if MBEDTLS_VERSION_NUMBER < 0x03010000 /* mbedtls 3.1.0 */ ++static int ++tls_mbedtls_append_curve (mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id) ++{ ++ if (1 >= idsz - (nids + 1)) { ++ emsg(MSG_ERROR, "error: too many curves during list expand"); ++ return -1; ++ } ++ ids[++nids] = id; ++ return nids; ++} ++ ++ ++static int ++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist) ++{ ++ mbedtls_ecp_group_id ids[512]; ++ int nids = -1; ++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1); ++ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list(); ++ ++ for (const char *e = curvelist-1; e; ) { ++ const char * const n = e+1; ++ e = os_strchr(n, ':'); ++ size_t len = e ? (size_t)(e - n) : os_strlen(n); ++ mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE; ++ switch (len) { ++ case 5: ++ if (0 == os_memcmp("P-521", n, 5)) ++ grp_id = MBEDTLS_ECP_DP_SECP521R1; ++ else if (0 == os_memcmp("P-384", n, 5)) ++ grp_id = MBEDTLS_ECP_DP_SECP384R1; ++ else if (0 == os_memcmp("P-256", n, 5)) ++ grp_id = MBEDTLS_ECP_DP_SECP256R1; ++ break; ++ case 6: ++ if (0 == os_memcmp("BP-521", n, 6)) ++ grp_id = MBEDTLS_ECP_DP_BP512R1; ++ else if (0 == os_memcmp("BP-384", n, 6)) ++ grp_id = MBEDTLS_ECP_DP_BP384R1; ++ else if (0 == os_memcmp("BP-256", n, 6)) ++ grp_id = MBEDTLS_ECP_DP_BP256R1; ++ break; ++ default: ++ break; ++ } ++ if (grp_id != MBEDTLS_ECP_DP_NONE) { ++ nids = tls_mbedtls_append_curve(ids, nids, idsz, grp_id); ++ if (-1 == nids) return 0; ++ continue; ++ } ++ /* similar to mbedtls_ecp_curve_info_from_name() */ ++ const mbedtls_ecp_curve_info *info; ++ for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) { ++ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0') ++ break; ++ } ++ if (info->grp_id == MBEDTLS_ECP_DP_NONE) { ++ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n); ++ return 0; ++ } ++ ++ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->grp_id); ++ if (-1 == nids) return 0; ++ } ++ ++ /* mod_openssl configures "prime256v1" if curve list not specified, ++ * but mbedtls provides a list of supported curves if not explicitly set */ ++ if (-1 == nids) return 1; /* empty list; no-op */ ++ ++ ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */ ++ ++nids; ++ ++ /* curves list must be persistent for lifetime of mbedtls_ssl_config */ ++ tls_conf->curves = os_malloc(nids * sizeof(mbedtls_ecp_group_id)); ++ if (tls_conf->curves == NULL) ++ return 0; ++ os_memcpy(tls_conf->curves, ids, nids * sizeof(mbedtls_ecp_group_id)); ++ ++ mbedtls_ssl_conf_curves(&tls_conf->conf, tls_conf->curves); ++ return 1; ++} ++#else ++static int ++tls_mbedtls_append_curve (uint16_t *ids, int nids, int idsz, const uint16_t id) ++{ ++ if (1 >= idsz - (nids + 1)) { ++ emsg(MSG_ERROR, "error: too many curves during list expand"); ++ return -1; ++ } ++ ids[++nids] = id; ++ return nids; ++} ++ ++ ++static int ++tls_mbedtls_set_curves(struct tls_conf *tls_conf, const char *curvelist) ++{ ++ /* TLS Supported Groups (renamed from "EC Named Curve Registry") ++ * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 ++ */ ++ uint16_t ids[512]; ++ int nids = -1; ++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1); ++ const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list(); ++ ++ for (const char *e = curvelist-1; e; ) { ++ const char * const n = e+1; ++ e = os_strchr(n, ':'); ++ size_t len = e ? (size_t)(e - n) : os_strlen(n); ++ uint16_t tls_id = 0; ++ switch (len) { ++ case 5: ++ if (0 == os_memcmp("P-521", n, 5)) ++ tls_id = 25; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP521R1 */ ++ else if (0 == os_memcmp("P-384", n, 5)) ++ tls_id = 24; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP384R1 */ ++ else if (0 == os_memcmp("P-256", n, 5)) ++ tls_id = 23; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_SECP256R1 */ ++ break; ++ case 6: ++ if (0 == os_memcmp("BP-521", n, 6)) ++ tls_id = 28; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP512R1 */ ++ else if (0 == os_memcmp("BP-384", n, 6)) ++ tls_id = 27; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP384R1 */ ++ else if (0 == os_memcmp("BP-256", n, 6)) ++ tls_id = 26; /* mbedtls_ecp_group_id MBEDTLS_ECP_DP_BP256R1 */ ++ break; ++ default: ++ break; ++ } ++ if (tls_id != 0) { ++ nids = tls_mbedtls_append_curve(ids, nids, idsz, tls_id); ++ if (-1 == nids) return 0; ++ continue; ++ } ++ /* similar to mbedtls_ecp_curve_info_from_name() */ ++ const mbedtls_ecp_curve_info *info; ++ for (info = curve_info; info->tls_id != 0; ++info) { ++ if (0 == os_strncmp(info->name, n, len) && info->name[len] == '\0') ++ break; ++ } ++ if (info->tls_id == 0) { ++ wpa_printf(MSG_ERROR, "MTLS: unrecognized curve: %.*s",(int)len,n); ++ return 0; ++ } ++ ++ nids = tls_mbedtls_append_curve(ids, nids, idsz, info->tls_id); ++ if (-1 == nids) return 0; ++ } ++ ++ /* mod_openssl configures "prime256v1" if curve list not specified, ++ * but mbedtls provides a list of supported curves if not explicitly set */ ++ if (-1 == nids) return 1; /* empty list; no-op */ ++ ++ ids[++nids] = 0; /* terminate list */ ++ ++nids; ++ ++ /* curves list must be persistent for lifetime of mbedtls_ssl_config */ ++ tls_conf->curves = os_malloc(nids * sizeof(uint16_t)); ++ if (tls_conf->curves == NULL) ++ return 0; ++ os_memcpy(tls_conf->curves, ids, nids * sizeof(uint16_t)); ++ ++ mbedtls_ssl_conf_groups(&tls_conf->conf, tls_conf->curves); ++ return 1; ++} ++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03010000 */ /* mbedtls 3.1.0 */ ++ ++ ++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */ ++static const int suite_AES_256_ephemeral[] = { ++ /* All AES-256 ephemeral suites */ ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8 ++}; ++ ++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */ ++static const int suite_AES_128_ephemeral[] = { ++ /* All AES-128 ephemeral suites */ ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8 ++}; ++ ++/* data copied from lighttpd src/mod_mbedtls.c (BSD-3-Clause) */ ++/* HIGH cipher list (mapped from openssl list to mbedtls) */ ++static const int suite_HIGH[] = { ++ MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, ++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, ++ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, ++ MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, ++ MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, ++ MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, ++ MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, ++ MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM, ++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, ++ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8, ++ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM, ++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8, ++ MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, ++ MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_RSA_WITH_AES_256_CCM, ++ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256, ++ MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8, ++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, ++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, ++ MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384, ++ MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_RSA_WITH_AES_128_CCM, ++ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8, ++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, ++ MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256, ++ MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, ++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, ++ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, ++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, ++ MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, ++ MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, ++ MBEDTLS_TLS_PSK_WITH_AES_256_CCM, ++ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, ++ MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, ++ MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, ++ MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, ++ MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384, ++ MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, ++ MBEDTLS_TLS_PSK_WITH_AES_128_CCM, ++ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, ++ MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, ++ MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, ++ MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8, ++ MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256 ++}; ++ ++ ++__attribute_noinline__ ++static int ++tls_mbedtls_append_ciphersuite (int *ids, int nids, int idsz, const int *x, int xsz) ++{ ++ if (xsz >= idsz - (nids + 1)) { ++ emsg(MSG_ERROR, "error: too many ciphers during list expand"); ++ return -1; ++ } ++ ++ for (int i = 0; i < xsz; ++i) ++ ids[++nids] = x[i]; ++ ++ return nids; ++} ++ ++ ++static int ++tls_mbedtls_translate_ciphername(int id, char *buf, size_t buflen) ++{ ++ const mbedtls_ssl_ciphersuite_t *info = ++ mbedtls_ssl_ciphersuite_from_id(id); ++ if (info == NULL) ++ return 0; ++ const char *name = mbedtls_ssl_ciphersuite_get_name(info); ++ const size_t len = os_strlen(name); ++ if (len == 7 && 0 == os_memcmp(name, "unknown", 7)) ++ return 0; ++ if (len >= buflen) ++ return 0; ++ os_strlcpy(buf, name, buflen); ++ ++ /* attempt to translate mbedtls string to openssl string ++ * (some heuristics; incomplete) */ ++ size_t i = 0, j = 0; ++ if (buf[0] == 'T') { ++ if (os_strncmp(buf, "TLS1-3-", 7) == 0) { ++ buf[3] = '-'; ++ j = 4; /* remove "1-3" from "TLS1-3-" prefix */ ++ i = 7; ++ } ++ else if (os_strncmp(buf, "TLS-", 4) == 0) ++ i = 4; /* remove "TLS-" prefix */ ++ } ++ for (; buf[i]; ++i) { ++ if (buf[i] == '-') { ++ if (i >= 3) { ++ if (0 == os_memcmp(buf+i-3, "AES", 3)) ++ continue; /* "AES-" -> "AES" */ ++ } ++ if (i >= 4) { ++ if (0 == os_memcmp(buf+i-4, "WITH", 4)) { ++ j -= 4; /* remove "WITH-" */ ++ continue; ++ } ++ } ++ } ++ buf[j++] = buf[i]; ++ } ++ buf[j] = '\0'; ++ ++ return j; ++} ++ ++ ++__attribute_noinline__ ++static int ++tls_mbedtls_set_ciphersuites(struct tls_conf *tls_conf, int *ids, int nids) ++{ ++ /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/ ++ os_free(tls_conf->ciphersuites); ++ tls_conf->ciphersuites = os_malloc(nids * sizeof(int)); ++ if (tls_conf->ciphersuites == NULL) ++ return 0; ++ os_memcpy(tls_conf->ciphersuites, ids, nids * sizeof(int)); ++ mbedtls_ssl_conf_ciphersuites(&tls_conf->conf, tls_conf->ciphersuites); ++ return 1; ++} ++ ++ ++static int ++tls_mbedtls_set_ciphers(struct tls_conf *tls_conf, const char *ciphers) ++{ ++ char buf[64]; ++ int ids[512]; ++ int nids = -1; ++ const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1); ++ const char *next; ++ size_t blen, clen; ++ do { ++ next = os_strchr(ciphers, ':'); ++ clen = next ? (size_t)(next - ciphers) : os_strlen(ciphers); ++ if (!clen) ++ continue; ++ ++ /* special-case a select set of openssl group names for hwsim tests */ ++ /* (review; remove excess code if tests are not run for non-OpenSSL?) */ ++ if (clen == 9 && os_memcmp(ciphers, "SUITEB192", 9) == 0) { ++ static int ssl_preset_suiteb192_ciphersuites[] = { ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, ++ 0 ++ }; ++ return tls_mbedtls_set_ciphersuites(tls_conf, ++ ssl_preset_suiteb192_ciphersuites, ++ 2); ++ } ++ if (clen == 9 && os_memcmp(ciphers, "SUITEB128", 9) == 0) { ++ static int ssl_preset_suiteb128_ciphersuites[] = { ++ MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, ++ 0 ++ }; ++ return tls_mbedtls_set_ciphersuites(tls_conf, ++ ssl_preset_suiteb128_ciphersuites, ++ 2); ++ } ++ if (clen == 7 && os_memcmp(ciphers, "DEFAULT", 7) == 0) ++ continue; ++ if (clen == 6 && os_memcmp(ciphers, "AES128", 6) == 0) { ++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, ++ suite_AES_128_ephemeral, ++ (int)ARRAY_SIZE(suite_AES_128_ephemeral)); ++ if (nids == -1) ++ return 0; ++ continue; ++ } ++ if (clen == 6 && os_memcmp(ciphers, "AES256", 6) == 0) { ++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, ++ suite_AES_256_ephemeral, ++ (int)ARRAY_SIZE(suite_AES_256_ephemeral)); ++ if (nids == -1) ++ return 0; ++ continue; ++ } ++ if (clen == 4 && os_memcmp(ciphers, "HIGH", 4) == 0) { ++ nids = tls_mbedtls_append_ciphersuite(ids, nids, idsz, suite_HIGH, ++ (int)ARRAY_SIZE(suite_HIGH)); ++ if (nids == -1) ++ return 0; ++ continue; ++ } ++ /* ignore anonymous cipher group names (?not supported by mbedtls?) */ ++ if (clen == 4 && os_memcmp(ciphers, "!ADH", 4) == 0) ++ continue; ++ if (clen == 6 && os_memcmp(ciphers, "-aECDH", 6) == 0) ++ continue; ++ if (clen == 7 && os_memcmp(ciphers, "-aECDSA", 7) == 0) ++ continue; ++ ++ /* attempt to match mbedtls cipher names ++ * nb: does not support openssl group names or list manipulation syntax ++ * (alt: could copy almost 1200 lines (!!!) of lighttpd mod_mbedtls.c ++ * mod_mbedtls_ssl_conf_ciphersuites() to translate strings) ++ * note: not efficient to rewrite list for each ciphers entry, ++ * but this code is expected to run only at startup ++ */ ++ const int *list = mbedtls_ssl_list_ciphersuites(); ++ for (; *list; ++list) { ++ blen = tls_mbedtls_translate_ciphername(*list,buf,sizeof(buf)); ++ if (!blen) ++ continue; ++ ++ /* matching heuristics additional to translate_ciphername above */ ++ if (blen == clen+4) { ++ char *cbc = os_strstr(buf, "CBC-"); ++ if (cbc) { ++ os_memmove(cbc, cbc+4, blen-(cbc+4-buf)+1); /*(w/ '\0')*/ ++ blen -= 4; ++ } ++ } ++ if (blen >= clen && os_memcmp(ciphers, buf, clen) == 0 ++ && (blen == clen ++ || (blen == clen+7 && os_memcmp(buf+clen, "-SHA256", 7)))) { ++ if (1 >= idsz - (nids + 1)) { ++ emsg(MSG_ERROR, ++ "error: too many ciphers during list expand"); ++ return 0; ++ } ++ ids[++nids] = *list; ++ break; ++ } ++ } ++ if (*list == 0) { ++ wpa_printf(MSG_ERROR, ++ "MTLS: unrecognized cipher: %.*s", (int)clen, ciphers); ++ return 0; ++ } ++ } while ((ciphers = next ? next+1 : NULL)); ++ ++ if (-1 == nids) return 1; /* empty list; no-op */ ++ ++ ids[++nids] = 0; /* terminate list */ ++ ++nids; ++ ++ return tls_mbedtls_set_ciphersuites(tls_conf, ids, nids); ++} ++ ++ ++__attribute_noinline__ ++static int tls_mbedtls_set_item(char **config_item, const char *item) ++{ ++ os_free(*config_item); ++ *config_item = NULL; ++ return item ? (*config_item = os_strdup(item)) != NULL : 1; ++} ++ ++ ++static int tls_connection_set_subject_match(struct tls_conf *tls_conf, ++ const struct tls_connection_params *params) ++{ ++ int rc = 1; ++ rc &= tls_mbedtls_set_item(&tls_conf->subject_match, ++ params->subject_match); ++ rc &= tls_mbedtls_set_item(&tls_conf->altsubject_match, ++ params->altsubject_match); ++ rc &= tls_mbedtls_set_item(&tls_conf->suffix_match, ++ params->suffix_match); ++ rc &= tls_mbedtls_set_item(&tls_conf->domain_match, ++ params->domain_match); ++ rc &= tls_mbedtls_set_item(&tls_conf->check_cert_subject, ++ params->check_cert_subject); ++ return rc; ++} ++ ++ ++/* duplicated in crypto_mbedtls.c:crypto_mbedtls_readfile()*/ ++__attribute_noinline__ ++static int tls_mbedtls_readfile(const char *path, u8 **buf, size_t *n) ++{ ++ #if 0 /* #ifdef MBEDTLS_FS_IO */ ++ /*(includes +1 for '\0' needed by mbedtls PEM parsing funcs)*/ ++ if (mbedtls_pk_load_file(path, (unsigned char **)buf, n) != 0) { ++ wpa_printf(MSG_ERROR, "error: mbedtls_pk_load_file %s", path); ++ return -1; ++ } ++ #else ++ /*(use os_readfile() so that we can use os_free() ++ *(if we use mbedtls_pk_load_file() above, macros prevent calling free() ++ * directly #if defined(OS_REJECT_C_LIB_FUNCTIONS) and calling os_free() ++ * on buf aborts in tests if buf not allocated via os_malloc())*/ ++ *buf = (u8 *)os_readfile(path, n); ++ if (!*buf) { ++ wpa_printf(MSG_ERROR, "error: os_readfile %s", path); ++ return -1; ++ } ++ u8 *buf0 = os_realloc(*buf, *n+1); ++ if (!buf0) { ++ bin_clear_free(*buf, *n); ++ *buf = NULL; ++ return -1; ++ } ++ buf0[(*n)++] = '\0'; ++ *buf = buf0; ++ #endif ++ return 0; ++} ++ ++ ++static int tls_mbedtls_set_crl(struct tls_conf *tls_conf, const u8 *data, size_t len) ++{ ++ /* do not use mbedtls_x509_crl_parse() on PEM unless it contains CRL */ ++ if (len && data[len-1] == '\0' ++ && NULL == os_strstr((const char *)data,"-----BEGIN X509 CRL-----") ++ && tls_mbedtls_data_is_pem(data)) ++ return 0; ++ ++ mbedtls_x509_crl crl; ++ mbedtls_x509_crl_init(&crl); ++ int rc = mbedtls_x509_crl_parse(&crl, data, len); ++ if (rc < 0) { ++ mbedtls_x509_crl_free(&crl); ++ return rc == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT ? 0 : rc; ++ } ++ ++ mbedtls_x509_crl *crl_new = os_malloc(sizeof(crl)); ++ if (crl_new == NULL) { ++ mbedtls_x509_crl_free(&crl); ++ return MBEDTLS_ERR_X509_ALLOC_FAILED; ++ } ++ os_memcpy(crl_new, &crl, sizeof(crl)); ++ ++ mbedtls_x509_crl *crl_old = tls_conf->crl; ++ tls_conf->crl = crl_new; ++ if (crl_old) { ++ mbedtls_x509_crl_free(crl_old); ++ os_free(crl_old); ++ } ++ return 0; ++} ++ ++ ++static int tls_mbedtls_set_ca(struct tls_conf *tls_conf, u8 *data, size_t len) ++{ ++ /* load crt struct onto stack and then copy into tls_conf in ++ * order to preserve existing tls_conf value if error occurs ++ * ++ * hostapd is not threaded, or else should allocate memory and swap in ++ * pointer reduce race condition. (If threaded, would also need to ++ * keep reference count of use to avoid freeing while still in use.) */ ++ ++ mbedtls_x509_crt crt; ++ mbedtls_x509_crt_init(&crt); ++ int rc = mbedtls_x509_crt_parse(&crt, data, len); ++ if (rc < 0) { ++ mbedtls_x509_crt_free(&crt); ++ return rc; ++ } ++ ++ mbedtls_x509_crt_free(&tls_conf->ca_cert); ++ os_memcpy(&tls_conf->ca_cert, &crt, sizeof(crt)); ++ return 0; ++} ++ ++ ++static int tls_mbedtls_set_ca_and_crl(struct tls_conf *tls_conf, const char *ca_cert_file) ++{ ++ size_t len; ++ u8 *data; ++ if (tls_mbedtls_readfile(ca_cert_file, &data, &len)) ++ return -1; ++ ++ int rc; ++ if (0 == (rc = tls_mbedtls_set_ca(tls_conf, data, len)) ++ && (!tls_mbedtls_data_is_pem(data) /*skip parse for CRL if not PEM*/ ++ || 0 == (rc = tls_mbedtls_set_crl(tls_conf, data, len)))) { ++ mbedtls_ssl_conf_ca_chain(&tls_conf->conf, ++ &tls_conf->ca_cert, ++ tls_conf->crl); ++ } ++ else { ++ elog(rc, __func__); ++ emsg(MSG_ERROR, ca_cert_file); ++ } ++ ++ forced_memzero(data, len); ++ os_free(data); ++ return rc; ++} ++ ++ ++static void tls_mbedtls_refresh_crl(void) ++{ ++ /* check for CRL refresh ++ * continue even if error occurs; continue with previous cert, CRL */ ++ unsigned int crl_reload_interval = tls_ctx_global.crl_reload_interval; ++ const char *ca_cert_file = tls_ctx_global.ca_cert_file; ++ if (!crl_reload_interval || !ca_cert_file) ++ return; ++ ++ struct os_reltime *previous = &tls_ctx_global.crl_reload_previous; ++ struct os_reltime now; ++ if (os_get_reltime(&now) != 0 ++ || !os_reltime_expired(&now, previous, crl_reload_interval)) ++ return; ++ ++ /* Note: modifying global state is not thread-safe ++ * if in use by existing connections ++ * ++ * src/utils/os.h does not provide a portable stat() ++ * or else it would be a good idea to check mtime and size, ++ * and avoid reloading if file has not changed */ ++ ++ if (tls_mbedtls_set_ca_and_crl(tls_ctx_global.tls_conf, ca_cert_file) == 0) ++ *previous = now; ++} ++ ++ ++static int tls_mbedtls_set_ca_cert(struct tls_conf *tls_conf, ++ const struct tls_connection_params *params) ++{ ++ if (params->ca_cert) { ++ if (os_strncmp(params->ca_cert, "probe://", 8) == 0) { ++ tls_conf->ca_cert_probe = 1; ++ tls_conf->has_ca_cert = 1; ++ return 0; ++ } ++ ++ if (os_strncmp(params->ca_cert, "hash://", 7) == 0) { ++ const char *pos = params->ca_cert + 7; ++ if (os_strncmp(pos, "server/sha256/", 14) != 0) { ++ emsg(MSG_ERROR, "unsupported ca_cert hash value"); ++ return -1; ++ } ++ pos += 14; ++ if (os_strlen(pos) != SHA256_DIGEST_LENGTH*2) { ++ emsg(MSG_ERROR, "unexpected ca_cert hash length"); ++ return -1; ++ } ++ if (hexstr2bin(pos, tls_conf->ca_cert_hash, ++ SHA256_DIGEST_LENGTH) < 0) { ++ emsg(MSG_ERROR, "invalid ca_cert hash value"); ++ return -1; ++ } ++ emsg(MSG_DEBUG, "checking only server certificate match"); ++ tls_conf->verify_depth0_only = 1; ++ tls_conf->has_ca_cert = 1; ++ return 0; ++ } ++ ++ if (tls_mbedtls_set_ca_and_crl(tls_conf, params->ca_cert) != 0) ++ return -1; ++ } ++ if (params->ca_cert_blob) { ++ size_t len = params->ca_cert_blob_len; ++ int is_pem = tls_mbedtls_data_is_pem(params->ca_cert_blob); ++ if (len && params->ca_cert_blob[len-1] != '\0' && is_pem) ++ ++len; /*(include '\0' in len for PEM)*/ ++ int ret = mbedtls_x509_crt_parse(&tls_conf->ca_cert, ++ params->ca_cert_blob, len); ++ if (ret != 0) { ++ elog(ret, "mbedtls_x509_crt_parse"); ++ return -1; ++ } ++ if (is_pem) { /*(ca_cert_blob in DER format contains ca cert only)*/ ++ ret = tls_mbedtls_set_crl(tls_conf, params->ca_cert_blob, len); ++ if (ret != 0) { ++ elog(ret, "mbedtls_x509_crl_parse"); ++ return -1; ++ } ++ } ++ } ++ ++ if (mbedtls_x509_time_is_future(&tls_conf->ca_cert.valid_from) ++ || mbedtls_x509_time_is_past(&tls_conf->ca_cert.valid_to)) { ++ emsg(MSG_WARNING, "ca_cert expired or not yet valid"); ++ if (params->ca_cert) ++ emsg(MSG_WARNING, params->ca_cert); ++ } ++ ++ tls_conf->has_ca_cert = 1; ++ return 0; ++} ++ ++ ++static int tls_mbedtls_set_certs(struct tls_conf *tls_conf, ++ const struct tls_connection_params *params) ++{ ++ int ret; ++ ++ if (params->ca_cert || params->ca_cert_blob) { ++ if (tls_mbedtls_set_ca_cert(tls_conf, params) != 0) ++ return -1; ++ } ++ else if (params->ca_path) { ++ emsg(MSG_INFO, "ca_path support not implemented"); ++ return -1; ++ } ++ ++ if (!tls_conf->has_ca_cert) ++ mbedtls_ssl_conf_authmode(&tls_conf->conf, MBEDTLS_SSL_VERIFY_NONE); ++ else { ++ /* Initial setting: REQUIRED for client, OPTIONAL for server ++ * (see also tls_connection_set_verify()) */ ++ tls_conf->verify_peer = (tls_ctx_global.tls_conf == NULL); ++ int authmode = tls_conf->verify_peer ++ ? MBEDTLS_SSL_VERIFY_REQUIRED ++ : MBEDTLS_SSL_VERIFY_OPTIONAL; ++ mbedtls_ssl_conf_authmode(&tls_conf->conf, authmode); ++ mbedtls_ssl_conf_ca_chain(&tls_conf->conf, ++ &tls_conf->ca_cert, ++ tls_conf->crl); ++ ++ if (!tls_connection_set_subject_match(tls_conf, params)) ++ return -1; ++ } ++ ++ if (params->client_cert2) /*(yes, server_cert2 in msg below)*/ ++ emsg(MSG_INFO, "server_cert2 support not implemented"); ++ ++ if (params->client_cert) { ++ size_t len; ++ u8 *data; ++ if (tls_mbedtls_readfile(params->client_cert, &data, &len)) ++ return -1; ++ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, data, len); ++ forced_memzero(data, len); ++ os_free(data); ++ } ++ if (params->client_cert_blob) { ++ size_t len = params->client_cert_blob_len; ++ if (len && params->client_cert_blob[len-1] != '\0' ++ && tls_mbedtls_data_is_pem(params->client_cert_blob)) ++ ++len; /*(include '\0' in len for PEM)*/ ++ ret = mbedtls_x509_crt_parse(&tls_conf->client_cert, ++ params->client_cert_blob, len); ++ } ++ if (params->client_cert || params->client_cert_blob) { ++ if (ret < 0) { ++ elog(ret, "mbedtls_x509_crt_parse"); ++ if (params->client_cert) ++ emsg(MSG_ERROR, params->client_cert); ++ return -1; ++ } ++ if (mbedtls_x509_time_is_future(&tls_conf->client_cert.valid_from) ++ || mbedtls_x509_time_is_past(&tls_conf->client_cert.valid_to)) { ++ emsg(MSG_WARNING, "cert expired or not yet valid"); ++ if (params->client_cert) ++ emsg(MSG_WARNING, params->client_cert); ++ } ++ tls_conf->has_client_cert = 1; ++ } ++ ++ if (params->private_key || params->private_key_blob) { ++ size_t len = params->private_key_blob_len; ++ u8 *data; ++ *(const u8 **)&data = params->private_key_blob; ++ if (len && data[len-1] != '\0' && tls_mbedtls_data_is_pem(data)) ++ ++len; /*(include '\0' in len for PEM)*/ ++ if (params->private_key ++ && tls_mbedtls_readfile(params->private_key, &data, &len)) { ++ return -1; ++ } ++ const char *pwd = params->private_key_passwd; ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ ret = mbedtls_pk_parse_key(&tls_conf->private_key, ++ data, len, ++ (const unsigned char *)pwd, ++ pwd ? os_strlen(pwd) : 0, ++ mbedtls_ctr_drbg_random, ++ tls_ctx_global.ctr_drbg); ++ #else ++ ret = mbedtls_pk_parse_key(&tls_conf->private_key, ++ data, len, ++ (const unsigned char *)pwd, ++ pwd ? os_strlen(pwd) : 0); ++ #endif ++ if (params->private_key) { ++ forced_memzero(data, len); ++ os_free(data); ++ } ++ if (ret < 0) { ++ elog(ret, "mbedtls_pk_parse_key"); ++ return -1; ++ } ++ tls_conf->has_private_key = 1; ++ } ++ ++ if (tls_conf->has_client_cert && tls_conf->has_private_key) { ++ ret = mbedtls_ssl_conf_own_cert( ++ &tls_conf->conf, &tls_conf->client_cert, &tls_conf->private_key); ++ if (ret < 0) { ++ elog(ret, "mbedtls_ssl_conf_own_cert"); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++/* mbedtls_x509_crt_profile_suiteb plus rsa_min_bitlen 2048 */ ++/* (reference: see also mbedtls_x509_crt_profile_next) */ ++/* ??? should permit SHA-512, too, and additional curves ??? */ ++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb128 = ++{ ++ /* Only SHA-256 and 384 */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) | ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ), ++ /* Only ECDSA */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) | ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ), ++#if defined(MBEDTLS_ECP_C) ++ /* Only NIST P-256 and P-384 */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) | ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ), ++#else ++ 0, ++#endif ++ 2048, ++}; ++ ++ ++/* stricter than mbedtls_x509_crt_profile_suiteb */ ++/* (reference: see also mbedtls_x509_crt_profile_next) */ ++/* ??? should permit SHA-512, too, and additional curves ??? */ ++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192 = ++{ ++ /* Only SHA-384 */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ), ++ /* Only ECDSA */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) | ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ), ++#if defined(MBEDTLS_ECP_C) ++ /* Only NIST P-384 */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ), ++#else ++ 0, ++#endif ++ 3072, ++}; ++ ++ ++/* stricter than mbedtls_x509_crt_profile_suiteb except allow any PK alg */ ++/* (reference: see also mbedtls_x509_crt_profile_next) */ ++/* ??? should permit SHA-512, too, and additional curves ??? */ ++static const mbedtls_x509_crt_profile tls_mbedtls_crt_profile_suiteb192_anypk = ++{ ++ /* Only SHA-384 */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ), ++ 0xFFFFFFF, /* Any PK alg */ ++#if defined(MBEDTLS_ECP_C) ++ /* Only NIST P-384 */ ++ MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP384R1 ), ++#else ++ 0, ++#endif ++ 3072, ++}; ++ ++ ++static int tls_mbedtls_set_params(struct tls_conf *tls_conf, ++ const struct tls_connection_params *params) ++{ ++ tls_conf->flags = params->flags; ++ ++ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP_ALL) { ++ emsg(MSG_INFO, "ocsp=3 not supported"); ++ return -1; ++ } ++ ++ if (tls_conf->flags & TLS_CONN_REQUIRE_OCSP) { ++ emsg(MSG_INFO, "ocsp not supported"); ++ return -1; ++ } ++ ++ int suiteb128 = 0; ++ int suiteb192 = 0; ++ if (params->openssl_ciphers) { ++ if (os_strcmp(params->openssl_ciphers, "SUITEB192") == 0) { ++ suiteb192 = 1; ++ tls_conf->flags |= TLS_CONN_SUITEB; ++ } ++ if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) { ++ suiteb128 = 1; ++ tls_conf->flags |= TLS_CONN_SUITEB; ++ } ++ } ++ ++ int ret = mbedtls_ssl_config_defaults( ++ &tls_conf->conf, tls_ctx_global.tls_conf ? MBEDTLS_SSL_IS_SERVER ++ : MBEDTLS_SSL_IS_CLIENT, ++ MBEDTLS_SSL_TRANSPORT_STREAM, ++ (tls_conf->flags & TLS_CONN_SUITEB) ? MBEDTLS_SSL_PRESET_SUITEB ++ : MBEDTLS_SSL_PRESET_DEFAULT); ++ if (ret != 0) { ++ elog(ret, "mbedtls_ssl_config_defaults"); ++ return -1; ++ } ++ ++ if (suiteb128) { ++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf, ++ &tls_mbedtls_crt_profile_suiteb128); ++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 2048); ++ } ++ else if (suiteb192) { ++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf, ++ &tls_mbedtls_crt_profile_suiteb192); ++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072); ++ } ++ else if (tls_conf->flags & TLS_CONN_SUITEB) { ++ /* treat as suiteb192 while allowing any PK algorithm */ ++ mbedtls_ssl_conf_cert_profile(&tls_conf->conf, ++ &tls_mbedtls_crt_profile_suiteb192_anypk); ++ mbedtls_ssl_conf_dhm_min_bitlen(&tls_conf->conf, 3072); ++ } ++ ++ tls_mbedtls_set_allowed_tls_vers(tls_conf, &tls_conf->conf); ++ ret = tls_mbedtls_set_certs(tls_conf, params); ++ if (ret != 0) ++ return -1; ++ ++ if (params->dh_file ++ && !tls_mbedtls_set_dhparams(tls_conf, params->dh_file)) { ++ return -1; ++ } ++ ++ if (params->openssl_ecdh_curves ++ && !tls_mbedtls_set_curves(tls_conf, params->openssl_ecdh_curves)) { ++ return -1; ++ } ++ ++ if (params->openssl_ciphers) { ++ if (!tls_mbedtls_set_ciphers(tls_conf, params->openssl_ciphers)) ++ return -1; ++ } ++ else if (tls_conf->flags & TLS_CONN_SUITEB) { ++ /* special-case a select set of ciphers for hwsim tests */ ++ if (!tls_mbedtls_set_ciphers(tls_conf, ++ (tls_conf->flags & TLS_CONN_SUITEB_NO_ECDH) ++ ? "DHE-RSA-AES256-GCM-SHA384" ++ : "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384")) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, ++ const struct tls_connection_params *params) ++{ ++ if (conn == NULL || params == NULL) ++ return -1; ++ ++ tls_conf_deinit(conn->tls_conf); ++ struct tls_conf *tls_conf = conn->tls_conf = tls_conf_init(tls_ctx); ++ if (tls_conf == NULL) ++ return -1; ++ ++ if (tls_ctx_global.tls_conf) { ++ tls_conf->check_crl = tls_ctx_global.tls_conf->check_crl; ++ tls_conf->check_crl_strict = tls_ctx_global.tls_conf->check_crl_strict; ++ /*(tls_openssl.c inherits check_cert_subject from global conf)*/ ++ if (tls_ctx_global.tls_conf->check_cert_subject) { ++ tls_conf->check_cert_subject = ++ os_strdup(tls_ctx_global.tls_conf->check_cert_subject); ++ if (tls_conf->check_cert_subject == NULL) ++ return -1; ++ } ++ } ++ ++ if (tls_mbedtls_set_params(tls_conf, params) != 0) ++ return -1; ++ conn->verify_peer = tls_conf->verify_peer; ++ ++ return tls_mbedtls_ssl_setup(conn); ++} ++ ++ ++#ifdef TLS_MBEDTLS_SESSION_TICKETS ++ ++static int tls_mbedtls_clienthello_session_ticket_prep (struct tls_connection *conn, ++ const u8 *data, size_t len) ++{ ++ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET) ++ return -1; ++ if (conn->clienthello_session_ticket) ++ tls_connection_deinit_clienthello_session_ticket(conn); ++ if (len) { ++ conn->clienthello_session_ticket = mbedtls_calloc(1, len); ++ if (conn->clienthello_session_ticket == NULL) ++ return -1; ++ conn->clienthello_session_ticket_len = len; ++ os_memcpy(conn->clienthello_session_ticket, data, len); ++ } ++ return 0; ++} ++ ++ ++static void tls_mbedtls_clienthello_session_ticket_set (struct tls_connection *conn) ++{ ++ mbedtls_ssl_session *sess = conn->ssl.MBEDTLS_PRIVATE(session_negotiate); ++ if (sess->MBEDTLS_PRIVATE(ticket)) { ++ mbedtls_platform_zeroize(sess->MBEDTLS_PRIVATE(ticket), ++ sess->MBEDTLS_PRIVATE(ticket_len)); ++ mbedtls_free(sess->MBEDTLS_PRIVATE(ticket)); ++ } ++ sess->MBEDTLS_PRIVATE(ticket) = conn->clienthello_session_ticket; ++ sess->MBEDTLS_PRIVATE(ticket_len) = conn->clienthello_session_ticket_len; ++ sess->MBEDTLS_PRIVATE(ticket_lifetime) = 86400;/* XXX: can hint be 0? */ ++ ++ conn->clienthello_session_ticket = NULL; ++ conn->clienthello_session_ticket_len = 0; ++} ++ ++ ++static int tls_mbedtls_ssl_ticket_write(void *p_ticket, ++ const mbedtls_ssl_session *session, ++ unsigned char *start, ++ const unsigned char *end, ++ size_t *tlen, ++ uint32_t *lifetime) ++{ ++ struct tls_connection *conn = p_ticket; ++ if (conn && conn->session_ticket_cb) { ++ /* see tls_mbedtls_clienthello_session_ticket_prep() */ ++ /* see tls_mbedtls_clienthello_session_ticket_set() */ ++ return 0; ++ } ++ ++ return mbedtls_ssl_ticket_write(&tls_ctx_global.ticket_ctx, ++ session, start, end, tlen, lifetime); ++} ++ ++ ++static int tls_mbedtls_ssl_ticket_parse(void *p_ticket, ++ mbedtls_ssl_session *session, ++ unsigned char *buf, ++ size_t len) ++{ ++ /* XXX: TODO: not implemented in client; ++ * mbedtls_ssl_conf_session_tickets_cb() callbacks only for TLS server*/ ++ ++ if (len == 0) ++ return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; ++ ++ struct tls_connection *conn = p_ticket; ++ if (conn && conn->session_ticket_cb) { ++ /* XXX: have random and secret been initialized yet? ++ * or must keys first be exported? ++ * EAP-FAST uses all args, EAP-TEAP only uses secret */ ++ struct tls_random data; ++ if (tls_connection_get_random(NULL, conn, &data) != 0) ++ return MBEDTLS_ERR_SSL_INTERNAL_ERROR; ++ int ret = ++ conn->session_ticket_cb(conn->session_ticket_cb_ctx, ++ buf, len, ++ data.client_random, ++ data.server_random, ++ conn->expkey_secret); ++ if (ret == 1) { ++ conn->resumed = 1; ++ return 0; ++ } ++ emsg(MSG_ERROR, "EAP session ticket ext not implemented"); ++ return MBEDTLS_ERR_SSL_INVALID_MAC; ++ /*(non-zero return used for mbedtls debug logging)*/ ++ } ++ ++ /* XXX: TODO always use tls_mbedtls_ssl_ticket_parse() for callback? */ ++ int rc = mbedtls_ssl_ticket_parse(&tls_ctx_global.ticket_ctx, ++ session, buf, len); ++ if (conn) ++ conn->resumed = (rc == 0); ++ return rc; ++} ++ ++#endif /* TLS_MBEDTLS_SESSION_TICKETS */ ++ ++ ++__attribute_cold__ ++int tls_global_set_params(void *tls_ctx, ++ const struct tls_connection_params *params) ++{ ++ /* XXX: why might global_set_params be called more than once? */ ++ if (tls_ctx_global.tls_conf) ++ tls_conf_deinit(tls_ctx_global.tls_conf); ++ tls_ctx_global.tls_conf = tls_conf_init(tls_ctx); ++ if (tls_ctx_global.tls_conf == NULL) ++ return -1; ++ ++ #ifdef MBEDTLS_SSL_SESSION_TICKETS ++ #ifdef MBEDTLS_SSL_TICKET_C ++ if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) ++ #ifdef TLS_MBEDTLS_SESSION_TICKETS ++ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf, ++ tls_mbedtls_ssl_ticket_write, ++ tls_mbedtls_ssl_ticket_parse, ++ NULL); ++ #else ++ mbedtls_ssl_conf_session_tickets_cb(&tls_ctx_global.tls_conf->conf, ++ mbedtls_ssl_ticket_write, ++ mbedtls_ssl_ticket_parse, ++ &tls_ctx_global.ticket_ctx); ++ #endif ++ #endif ++ #endif ++ ++ os_free(tls_ctx_global.ocsp_stapling_response); ++ tls_ctx_global.ocsp_stapling_response = NULL; ++ if (params->ocsp_stapling_response) ++ tls_ctx_global.ocsp_stapling_response = ++ os_strdup(params->ocsp_stapling_response); ++ ++ os_free(tls_ctx_global.ca_cert_file); ++ tls_ctx_global.ca_cert_file = NULL; ++ if (params->ca_cert) ++ tls_ctx_global.ca_cert_file = os_strdup(params->ca_cert); ++ return tls_mbedtls_set_params(tls_ctx_global.tls_conf, params); ++} ++ ++ ++int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) ++{ ++ tls_ctx_global.tls_conf->check_crl = check_crl; ++ tls_ctx_global.tls_conf->check_crl_strict = strict; /*(time checks)*/ ++ return 0; ++} ++ ++ ++int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, ++ int verify_peer, unsigned int flags, ++ const u8 *session_ctx, size_t session_ctx_len) ++{ ++ /*(EAP server-side calls this from eap_server_tls_ssl_init())*/ ++ if (conn == NULL) ++ return -1; ++ ++ conn->tls_conf->flags |= flags;/* TODO: reprocess flags, if necessary */ ++ ++ int authmode; ++ switch (verify_peer) { ++ case 2: authmode = MBEDTLS_SSL_VERIFY_OPTIONAL; break;/*(eap_teap_init())*/ ++ case 1: authmode = MBEDTLS_SSL_VERIFY_REQUIRED; break; ++ default: authmode = MBEDTLS_SSL_VERIFY_NONE; break; ++ } ++ mbedtls_ssl_set_hs_authmode(&conn->ssl, authmode); ++ ++ if ((conn->verify_peer = (authmode != MBEDTLS_SSL_VERIFY_NONE))) ++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn); ++ else ++ mbedtls_ssl_set_verify(&conn->ssl, NULL, NULL); ++ ++ return 0; ++} ++ ++ ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++static void tls_connection_export_keys_cb( ++ void *p_expkey, mbedtls_ssl_key_export_type secret_type, ++ const unsigned char *secret, size_t secret_len, ++ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN], ++ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN], ++ mbedtls_tls_prf_types tls_prf_type) ++{ ++ struct tls_connection *conn = p_expkey; ++ conn->tls_prf_type = tls_prf_type; ++ if (!tls_prf_type) ++ return; ++ if (secret_len > sizeof(conn->expkey_secret)) { ++ emsg(MSG_ERROR, "tls_connection_export_keys_cb secret too long"); ++ conn->tls_prf_type = MBEDTLS_SSL_TLS_PRF_NONE; /* 0 */ ++ return; ++ } ++ conn->expkey_secret_len = secret_len; ++ os_memcpy(conn->expkey_secret, secret, secret_len); ++ os_memcpy(conn->expkey_randbytes, ++ client_random, MBEDTLS_EXPKEY_RAND_LEN); ++ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN, ++ server_random, MBEDTLS_EXPKEY_RAND_LEN); ++} ++#elif MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ ++static int tls_connection_export_keys_cb( ++ void *p_expkey, ++ const unsigned char *ms, ++ const unsigned char *kb, ++ size_t maclen, ++ size_t keylen, ++ size_t ivlen, ++ const unsigned char client_random[MBEDTLS_EXPKEY_RAND_LEN], ++ const unsigned char server_random[MBEDTLS_EXPKEY_RAND_LEN], ++ mbedtls_tls_prf_types tls_prf_type ) ++{ ++ struct tls_connection *conn = p_expkey; ++ conn->tls_prf_type = tls_prf_type; ++ if (!tls_prf_type) ++ return -1; /*(return value ignored by mbedtls)*/ ++ conn->expkey_keyblock_size = maclen + keylen + ivlen; ++ conn->expkey_secret_len = MBEDTLS_EXPKEY_FIXED_SECRET_LEN; ++ os_memcpy(conn->expkey_secret, ms, MBEDTLS_EXPKEY_FIXED_SECRET_LEN); ++ os_memcpy(conn->expkey_randbytes, ++ client_random, MBEDTLS_EXPKEY_RAND_LEN); ++ os_memcpy(conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN, ++ server_random, MBEDTLS_EXPKEY_RAND_LEN); ++ return 0; ++} ++#endif ++ ++ ++int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, ++ struct tls_random *data) ++{ ++ if (!conn || !conn->tls_prf_type) ++ return -1; ++ data->client_random = conn->expkey_randbytes; ++ data->client_random_len = MBEDTLS_EXPKEY_RAND_LEN; ++ data->server_random = conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN; ++ data->server_random_len = MBEDTLS_EXPKEY_RAND_LEN; ++ return 0; ++} ++ ++ ++int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, ++ const char *label, const u8 *context, ++ size_t context_len, u8 *out, size_t out_len) ++{ ++ /* (EAP-PEAP EAP-TLS EAP-TTLS) */ ++ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ ++ return (conn && conn->established && conn->tls_prf_type) ++ ? mbedtls_ssl_tls_prf(conn->tls_prf_type, ++ conn->expkey_secret, conn->expkey_secret_len, label, ++ conn->expkey_randbytes, ++ sizeof(conn->expkey_randbytes), out, out_len) ++ : -1; ++ #else ++ /* not implemented here for mbedtls < 2.18.0 */ ++ return -1; ++ #endif ++} ++ ++ ++#ifdef TLS_MBEDTLS_EAP_FAST ++ ++#if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++/* keyblock size info is not exposed in mbed TLS 3.0.0 */ ++/* extracted from mbedtls library/ssl_tls.c:ssl_tls12_populate_transform() */ ++#include ++#include ++static size_t tls_mbedtls_ssl_keyblock_size (mbedtls_ssl_context *ssl) ++{ ++ #if !defined(MBEDTLS_USE_PSA_CRYPTO) /* XXX: (not extracted for PSA crypto) */ ++ #if defined(MBEDTLS_SSL_PROTO_TLS1_3) ++ if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3) ++ return 0; /* (calculation not extracted) */ ++ #endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ ++ ++ int ciphersuite = mbedtls_ssl_get_ciphersuite_id_from_ssl(ssl); ++ const mbedtls_ssl_ciphersuite_t *ciphersuite_info = ++ mbedtls_ssl_ciphersuite_from_id(ciphersuite); ++ if (ciphersuite_info == NULL) ++ return 0; ++ ++ const mbedtls_cipher_info_t *cipher_info = ++ mbedtls_cipher_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(cipher)); ++ if (cipher_info == NULL) ++ return 0; ++ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03010000 /* mbedtls 3.1.0 */ ++ size_t keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8; ++ mbedtls_cipher_mode_t mode = mbedtls_cipher_info_get_mode(cipher_info); ++ #else ++ size_t keylen = cipher_info->MBEDTLS_PRIVATE(key_bitlen) / 8; ++ mbedtls_cipher_mode_t mode = cipher_info->MBEDTLS_PRIVATE(mode); ++ #endif ++ #if defined(MBEDTLS_GCM_C) || \ ++ defined(MBEDTLS_CCM_C) || \ ++ defined(MBEDTLS_CHACHAPOLY_C) ++ if (mode == MBEDTLS_MODE_GCM || mode == MBEDTLS_MODE_CCM) ++ return keylen + 4; ++ else if (mode == MBEDTLS_MODE_CHACHAPOLY) ++ return keylen + 12; ++ else ++ #endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */ ++ #if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) ++ { ++ const mbedtls_md_info_t *md_info = ++ mbedtls_md_info_from_type(ciphersuite_info->MBEDTLS_PRIVATE(mac)); ++ if (md_info == NULL) ++ return 0; ++ size_t mac_key_len = mbedtls_md_get_size(md_info); ++ size_t ivlen = mbedtls_cipher_info_get_iv_size(cipher_info); ++ return keylen + mac_key_len + ivlen; ++ } ++ #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ ++ #endif /* !MBEDTLS_USE_PSA_CRYPTO *//* (not extracted for PSA crypto) */ ++ return 0; ++} ++#endif /* MBEDTLS_VERSION_NUMBER >= 0x03000000 *//* mbedtls 3.0.0 */ ++ ++ ++int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, ++ u8 *out, size_t out_len) ++{ ++ /* XXX: has export keys callback been run? */ ++ if (!conn || !conn->tls_prf_type) ++ return -1; ++ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ conn->expkey_keyblock_size = tls_mbedtls_ssl_keyblock_size(&conn->ssl); ++ if (conn->expkey_keyblock_size == 0) ++ return -1; ++ #endif ++ size_t skip = conn->expkey_keyblock_size * 2; ++ unsigned char *tmp_out = os_malloc(skip + out_len); ++ if (!tmp_out) ++ return -1; ++ ++ /* server_random and then client_random */ ++ unsigned char seed[MBEDTLS_EXPKEY_RAND_LEN*2]; ++ os_memcpy(seed, conn->expkey_randbytes + MBEDTLS_EXPKEY_RAND_LEN, ++ MBEDTLS_EXPKEY_RAND_LEN); ++ os_memcpy(seed + MBEDTLS_EXPKEY_RAND_LEN, conn->expkey_randbytes, ++ MBEDTLS_EXPKEY_RAND_LEN); ++ ++ #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ ++ int ret = mbedtls_ssl_tls_prf(conn->tls_prf_type, ++ conn->expkey_secret, conn->expkey_secret_len, ++ "key expansion", seed, sizeof(seed), ++ tmp_out, skip + out_len); ++ if (ret == 0) ++ os_memcpy(out, tmp_out + skip, out_len); ++ #else ++ int ret = -1; /*(not reached if not impl; return -1 at top of func)*/ ++ #endif ++ ++ bin_clear_free(tmp_out, skip + out_len); ++ forced_memzero(seed, sizeof(seed)); ++ return ret; ++} ++ ++#endif /* TLS_MBEDTLS_EAP_FAST */ ++ ++ ++__attribute_cold__ ++static void tls_mbedtls_suiteb_handshake_alert (struct tls_connection *conn) ++{ ++ /* tests/hwsim/test_suite_b.py test_suite_b_192_rsa_insufficient_dh */ ++ if (!(conn->tls_conf->flags & TLS_CONN_SUITEB)) ++ return; ++ if (tls_ctx_global.tls_conf) /*(is server; want issue event on client)*/ ++ return; ++ #if 0 ++ /*(info not available on client; ++ * mbed TLS library enforces dhm min bitlen in ServerKeyExchange)*/ ++ if (MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 == ++ #if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */ ++ mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl) ++ #else ++ mbedtls_ssl_get_ciphersuite_id( ++ mbedtls_ssl_get_ciphersuite(&conn->ssl)) ++ #endif ++ && mbedtls_mpi_size(&conn->tls_conf->conf.MBEDTLS_PRIVATE(dhm_P)) ++ < 384 /*(3072/8)*/) ++ #endif ++ { ++ struct tls_config *init_conf = &tls_ctx_global.init_conf; ++ if (init_conf->event_cb) { ++ union tls_event_data ev; ++ os_memset(&ev, 0, sizeof(ev)); ++ ev.alert.is_local = 1; ++ ev.alert.type = "fatal"; ++ /*"internal error" string for tests/hwsim/test_suiteb.py */ ++ ev.alert.description = "internal error: handshake failure"; ++ /*ev.alert.description = "insufficient security";*/ ++ init_conf->event_cb(init_conf->cb_ctx, TLS_ALERT, &ev); ++ } ++ } ++} ++ ++ ++struct wpabuf * tls_connection_handshake(void *tls_ctx, ++ struct tls_connection *conn, ++ const struct wpabuf *in_data, ++ struct wpabuf **appl_data) ++{ ++ if (appl_data) ++ *appl_data = NULL; ++ ++ if (in_data && wpabuf_len(in_data)) { ++ /*(unsure why tls_gnutls.c discards buffer contents; skip here)*/ ++ if (conn->pull_buf && 0) /* disable; appears unwise */ ++ tls_pull_buf_discard(conn, __func__); ++ if (!tls_pull_buf_append(conn, in_data)) ++ return NULL; ++ } ++ ++ if (conn->tls_conf == NULL) { ++ struct tls_connection_params params; ++ os_memset(¶ms, 0, sizeof(params)); ++ params.openssl_ciphers = ++ tls_ctx_global.init_conf.openssl_ciphers; ++ params.flags = tls_ctx_global.tls_conf->flags; ++ if (tls_connection_set_params(tls_ctx, conn, ¶ms) != 0) ++ return NULL; ++ } ++ ++ if (conn->verify_peer) /*(call here might be redundant; nbd)*/ ++ mbedtls_ssl_set_verify(&conn->ssl, tls_mbedtls_verify_cb, conn); ++ ++ #ifdef TLS_MBEDTLS_SESSION_TICKETS ++ if (conn->clienthello_session_ticket) ++ /*(starting handshake for EAP-FAST and EAP-TEAP)*/ ++ tls_mbedtls_clienthello_session_ticket_set(conn); ++ ++ /* (not thread-safe due to need to set userdata 'conn' for callback) */ ++ /* (unable to use mbedtls_ssl_set_user_data_p() with mbedtls 3.2.0+ ++ * since ticket write and parse callbacks take (mbedtls_ssl_session *) ++ * param instead of (mbedtls_ssl_context *) param) */ ++ if (conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET) ++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf, ++ NULL, NULL, NULL); ++ else ++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf, ++ tls_mbedtls_ssl_ticket_write, ++ tls_mbedtls_ssl_ticket_parse, ++ conn); ++ #endif ++ ++ #if MBEDTLS_VERSION_NUMBER >= 0x03020000 /* mbedtls 3.2.0 */ ++ int ret = mbedtls_ssl_handshake(&conn->ssl); ++ #else ++ int ret = 0; ++ while (conn->ssl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER) { ++ ret = mbedtls_ssl_handshake_step(&conn->ssl); ++ if (ret != 0) ++ break; ++ } ++ #endif ++ ++ #ifdef TLS_MBEDTLS_SESSION_TICKETS ++ mbedtls_ssl_conf_session_tickets_cb(&conn->tls_conf->conf, ++ tls_mbedtls_ssl_ticket_write, ++ tls_mbedtls_ssl_ticket_parse, ++ NULL); ++ #endif ++ ++ switch (ret) { ++ case 0: ++ conn->established = 1; ++ if (conn->push_buf == NULL) ++ /* Need to return something to get final TLS ACK. */ ++ conn->push_buf = wpabuf_alloc(0); ++ ++ if (appl_data /*&& conn->pull_buf && wpabuf_len(conn->pull_buf)*/) ++ *appl_data = NULL; /* RFE: check for application data */ ++ break; ++ case MBEDTLS_ERR_SSL_WANT_WRITE: ++ case MBEDTLS_ERR_SSL_WANT_READ: ++ case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: ++ case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: ++ if (tls_ctx_global.tls_conf /*(is server)*/ ++ && conn->established && conn->push_buf == NULL) ++ /* Need to return something to trigger completion of EAP-TLS. */ ++ conn->push_buf = wpabuf_alloc(0); ++ break; ++ default: ++ ++conn->failed; ++ switch (ret) { ++ case MBEDTLS_ERR_SSL_CLIENT_RECONNECT: ++ case MBEDTLS_ERR_NET_CONN_RESET: ++ case MBEDTLS_ERR_NET_SEND_FAILED: ++ ++conn->write_alerts; ++ break; ++ #if MBEDTLS_VERSION_NUMBER >= 0x03000000 /* mbedtls 3.0.0 */ ++ case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE: ++ #else ++ case MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE: ++ #endif ++ tls_mbedtls_suiteb_handshake_alert(conn); ++ /* fall through */ ++ case MBEDTLS_ERR_NET_RECV_FAILED: ++ case MBEDTLS_ERR_SSL_CONN_EOF: ++ case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: ++ case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: ++ ++conn->read_alerts; ++ break; ++ default: ++ break; ++ } ++ ++ ilog(ret, "mbedtls_ssl_handshake"); ++ break; ++ } ++ ++ struct wpabuf *out_data = conn->push_buf; ++ conn->push_buf = NULL; ++ return out_data; ++} ++ ++ ++struct wpabuf * tls_connection_server_handshake(void *tls_ctx, ++ struct tls_connection *conn, ++ const struct wpabuf *in_data, ++ struct wpabuf **appl_data) ++{ ++ conn->is_server = 1; ++ return tls_connection_handshake(tls_ctx, conn, in_data, appl_data); ++} ++ ++ ++struct wpabuf * tls_connection_encrypt(void *tls_ctx, ++ struct tls_connection *conn, ++ const struct wpabuf *in_data) ++{ ++ int res = mbedtls_ssl_write(&conn->ssl, ++ wpabuf_head_u8(in_data), wpabuf_len(in_data)); ++ if (res < 0) { ++ elog(res, "mbedtls_ssl_write"); ++ return NULL; ++ } ++ ++ struct wpabuf *buf = conn->push_buf; ++ conn->push_buf = NULL; ++ return buf; ++} ++ ++ ++struct wpabuf * tls_connection_decrypt(void *tls_ctx, ++ struct tls_connection *conn, ++ const struct wpabuf *in_data) ++{ ++ int res; ++ struct wpabuf *out; ++ ++ /*assert(in_data != NULL);*/ ++ if (!tls_pull_buf_append(conn, in_data)) ++ return NULL; ++ ++ #if defined(MBEDTLS_ZLIB_SUPPORT) /* removed in mbedtls 3.x */ ++ /* Add extra buffer space to handle the possibility of decrypted ++ * data being longer than input data due to TLS compression. */ ++ out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); ++ #else /* TLS compression is disabled in mbedtls 3.x */ ++ out = wpabuf_alloc(wpabuf_len(in_data)); ++ #endif ++ if (out == NULL) ++ return NULL; ++ ++ res = mbedtls_ssl_read(&conn->ssl, wpabuf_mhead(out), wpabuf_size(out)); ++ if (res < 0) { ++ #if 1 /*(seems like a different error if wpabuf_len(in_data) == 0)*/ ++ if (res == MBEDTLS_ERR_SSL_WANT_READ) ++ return out; ++ #endif ++ elog(res, "mbedtls_ssl_read"); ++ wpabuf_free(out); ++ return NULL; ++ } ++ wpabuf_put(out, res); ++ ++ return out; ++} ++ ++ ++int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) ++{ ++ /* XXX: might need to detect if session resumed from TLS session ticket ++ * even if not special session ticket handling for EAP-FAST, EAP-TEAP */ ++ /* (?ssl->handshake->resume during session ticket validation?) */ ++ return conn && conn->resumed; ++} ++ ++ ++#ifdef TLS_MBEDTLS_EAP_FAST ++int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, ++ u8 *ciphers) ++{ ++ /* ciphers is list of TLS_CIPHER_* from hostap/src/crypto/tls.h */ ++ int ids[7]; ++ const int idsz = (int)sizeof(ids); ++ int nids = -1, id; ++ for ( ; *ciphers != TLS_CIPHER_NONE; ++ciphers) { ++ switch (*ciphers) { ++ case TLS_CIPHER_RC4_SHA: ++ #ifdef MBEDTLS_TLS_RSA_WITH_RC4_128_SHA ++ id = MBEDTLS_TLS_RSA_WITH_RC4_128_SHA; ++ break; ++ #else ++ continue; /*(not supported in mbedtls 3.x; ignore)*/ ++ #endif ++ case TLS_CIPHER_AES128_SHA: ++ id = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA; ++ break; ++ case TLS_CIPHER_RSA_DHE_AES128_SHA: ++ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA; ++ break; ++ case TLS_CIPHER_ANON_DH_AES128_SHA: ++ continue; /*(not supported in mbedtls; ignore)*/ ++ case TLS_CIPHER_RSA_DHE_AES256_SHA: ++ id = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA; ++ break; ++ case TLS_CIPHER_AES256_SHA: ++ id = MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA; ++ break; ++ default: ++ return -1; /* should not happen */ ++ } ++ if (++nids == idsz) ++ return -1; /* should not happen */ ++ ids[nids] = id; ++ } ++ if (nids < 0) ++ return 0; /* nothing to do */ ++ if (++nids == idsz) ++ return -1; /* should not happen */ ++ ids[nids] = 0; /* terminate list */ ++ ++nids; ++ ++ return tls_mbedtls_set_ciphersuites(conn->tls_conf, ids, nids) ? 0 : -1; ++} ++#endif ++ ++ ++int tls_get_version(void *ssl_ctx, struct tls_connection *conn, ++ char *buf, size_t buflen) ++{ ++ if (conn == NULL) ++ return -1; ++ os_strlcpy(buf, mbedtls_ssl_get_version(&conn->ssl), buflen); ++ return buf[0] != 'u' ? 0 : -1; /*(-1 if "unknown")*/ ++} ++ ++ ++#ifdef TLS_MBEDTLS_EAP_TEAP ++u16 tls_connection_get_cipher_suite(struct tls_connection *conn) ++{ ++ if (conn == NULL) ++ return 0; ++ return (u16)mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl); ++} ++#endif ++ ++ ++int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, ++ char *buf, size_t buflen) ++{ ++ if (conn == NULL) ++ return -1; ++ const int id = mbedtls_ssl_get_ciphersuite_id_from_ssl(&conn->ssl); ++ return tls_mbedtls_translate_ciphername(id, buf, buflen) ? 0 : -1; ++} ++ ++ ++#ifdef TLS_MBEDTLS_SESSION_TICKETS ++ ++int tls_connection_enable_workaround(void *tls_ctx, ++ struct tls_connection *conn) ++{ ++ /* (see comment in src/eap_peer/eap_fast.c:eap_fast_init()) */ ++ /* XXX: is there a relevant setting for this in mbed TLS? */ ++ /* (do we even care that much about older CBC ciphers?) */ ++ return 0; ++} ++ ++ ++int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, ++ int ext_type, const u8 *data, ++ size_t data_len) ++{ ++ /* (EAP-FAST and EAP-TEAP) */ ++ if (ext_type == MBEDTLS_TLS_EXT_SESSION_TICKET) /*(ext_type == 35)*/ ++ return tls_mbedtls_clienthello_session_ticket_prep(conn, data, ++ data_len); ++ ++ return -1; ++} ++ ++#endif /* TLS_MBEDTLS_SESSION_TICKETS */ ++ ++ ++int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) ++{ ++ return conn ? conn->failed : -1; ++} ++ ++ ++int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) ++{ ++ return conn ? conn->read_alerts : -1; ++} ++ ++ ++int tls_connection_get_write_alerts(void *tls_ctx, ++ struct tls_connection *conn) ++{ ++ return conn ? conn->write_alerts : -1; ++} ++ ++ ++#ifdef TLS_MBEDTLS_SESSION_TICKETS ++int tls_connection_set_session_ticket_cb( ++ void *tls_ctx, struct tls_connection *conn, ++ tls_session_ticket_cb cb, void *ctx) ++{ ++ if (!(conn->tls_conf->flags & TLS_CONN_DISABLE_SESSION_TICKET)) { ++ /* (EAP-FAST and EAP-TEAP) */ ++ conn->session_ticket_cb = cb; ++ conn->session_ticket_cb_ctx = ctx; ++ return 0; ++ } ++ return -1; ++} ++#endif ++ ++ ++int tls_get_library_version(char *buf, size_t buf_len) ++{ ++ #ifndef MBEDTLS_VERSION_C ++ const char * const ver = "n/a"; ++ #else ++ char ver[9]; ++ mbedtls_version_get_string(ver); ++ #endif ++ return os_snprintf(buf, buf_len, ++ "mbed TLS build=" MBEDTLS_VERSION_STRING " run=%s", ver); ++} ++ ++ ++void tls_connection_set_success_data(struct tls_connection *conn, ++ struct wpabuf *data) ++{ ++ wpabuf_free(conn->success_data); ++ conn->success_data = data; ++} ++ ++ ++void tls_connection_set_success_data_resumed(struct tls_connection *conn) ++{ ++} ++ ++ ++const struct wpabuf * ++tls_connection_get_success_data(struct tls_connection *conn) ++{ ++ return conn->success_data; ++} ++ ++ ++void tls_connection_remove_session(struct tls_connection *conn) ++{ ++} ++ ++ ++#ifdef TLS_MBEDTLS_EAP_TEAP ++int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len) ++{ ++ #if defined(MBEDTLS_SSL_RENEGOTIATION) /* XXX: renegotiation or resumption? */ ++ /* data from TLS handshake Finished message */ ++ size_t verify_len = conn->ssl.MBEDTLS_PRIVATE(verify_data_len); ++ char *verify_data = (conn->is_server ^ conn->resumed) ++ ? conn->ssl.MBEDTLS_PRIVATE(peer_verify_data) ++ : conn->ssl.MBEDTLS_PRIVATE(own_verify_data); ++ if (verify_len && verify_len <= max_len) { ++ os_memcpy(buf, verify_data, verify_len); ++ return (int)verify_len; ++ } ++ #endif ++ return -1; ++} ++#endif ++ ++ ++__attribute_noinline__ ++static void tls_mbedtls_set_peer_subject(struct tls_connection *conn, const mbedtls_x509_crt *crt) ++{ ++ if (conn->peer_subject) ++ return; ++ char buf[MBEDTLS_X509_MAX_DN_NAME_SIZE*2]; ++ int buflen = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject); ++ if (buflen >= 0 && (conn->peer_subject = os_malloc((size_t)buflen+1))) ++ os_memcpy(conn->peer_subject, buf, (size_t)buflen+1); ++} ++ ++ ++#ifdef TLS_MBEDTLS_EAP_TEAP ++const char * tls_connection_get_peer_subject(struct tls_connection *conn) ++{ ++ if (!conn) ++ return NULL; ++ if (!conn->peer_subject) { /*(if not set during cert verify)*/ ++ const mbedtls_x509_crt *peer_cert = ++ mbedtls_ssl_get_peer_cert(&conn->ssl); ++ if (peer_cert) ++ tls_mbedtls_set_peer_subject(conn, peer_cert); ++ } ++ return conn->peer_subject; ++} ++#endif ++ ++ ++#ifdef TLS_MBEDTLS_EAP_TEAP ++bool tls_connection_get_own_cert_used(struct tls_connection *conn) ++{ ++ /* XXX: availability of cert does not necessary mean that client ++ * received certificate request from server and then sent cert. ++ * ? step handshake in tls_connection_handshake() looking for ++ * MBEDTLS_SSL_CERTIFICATE_REQUEST ? */ ++ const struct tls_conf * const tls_conf = conn->tls_conf; ++ return (tls_conf->has_client_cert && tls_conf->has_private_key); ++} ++#endif ++ ++ ++#if defined(CONFIG_FIPS) ++#define TLS_MBEDTLS_CONFIG_FIPS ++#endif ++ ++#if defined(CONFIG_SHA256) ++#define TLS_MBEDTLS_TLS_PRF_SHA256 ++#endif ++ ++#if defined(CONFIG_SHA384) ++#define TLS_MBEDTLS_TLS_PRF_SHA384 ++#endif ++ ++ ++#ifndef TLS_MBEDTLS_CONFIG_FIPS ++#if defined(CONFIG_MODULE_TESTS) ++/* unused with CONFIG_TLS=mbedtls except in crypto_module_tests.c */ ++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ \ ++ && MBEDTLS_VERSION_NUMBER < 0x03000000 /* mbedtls 3.0.0 */ ++/* sha1-tlsprf.c */ ++#include "sha1.h" ++int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, ++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen) ++{ ++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_TLS1, ++ secret, secret_len, label, ++ seed, seed_len, out, outlen) ? -1 : 0; ++} ++#else ++#include "sha1-tlsprf.c" /* pull in hostap local implementation */ ++#endif ++#endif ++#endif ++ ++#ifdef TLS_MBEDTLS_TLS_PRF_SHA256 ++/* sha256-tlsprf.c */ ++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ ++#include "sha256.h" ++int tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, ++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen) ++{ ++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA256, ++ secret, secret_len, label, ++ seed, seed_len, out, outlen) ? -1 : 0; ++} ++#else ++#include "sha256-tlsprf.c" /* pull in hostap local implementation */ ++#endif ++#endif ++ ++#ifdef TLS_MBEDTLS_TLS_PRF_SHA384 ++/* sha384-tlsprf.c */ ++#if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ ++#include "sha384.h" ++int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label, ++ const u8 *seed, size_t seed_len, u8 *out, size_t outlen) ++{ ++ return mbedtls_ssl_tls_prf(MBEDTLS_SSL_TLS_PRF_SHA384, ++ secret, secret_len, label, ++ seed, seed_len, out, outlen) ? -1 : 0; ++} ++#else ++#include "sha384-tlsprf.c" /* pull in hostap local implementation */ ++#endif ++#endif ++ ++ ++#if MBEDTLS_VERSION_NUMBER < 0x03020000 /* mbedtls 3.2.0 */ ++#define mbedtls_x509_crt_has_ext_type(crt, ext_type) \ ++ ((crt)->MBEDTLS_PRIVATE(ext_types) & (ext_type)) ++#endif ++ ++struct mlist { const char *p; size_t n; }; ++ ++ ++static int ++tls_mbedtls_match_altsubject(mbedtls_x509_crt *crt, const char *match) ++{ ++ /* RFE: this could be pre-parsed into structured data at config time */ ++ struct mlist list[256]; /*(much larger than expected)*/ ++ int nlist = 0; ++ if ( os_strncmp(match, "EMAIL:", 6) != 0 ++ && os_strncmp(match, "DNS:", 4) != 0 ++ && os_strncmp(match, "URI:", 4) != 0 ) { ++ wpa_printf(MSG_INFO, "MTLS: Invalid altSubjectName match '%s'", match); ++ return 0; ++ } ++ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") { ++ do { } while ((tok = os_strchr(s, ';')) ++ && os_strncmp(tok+1, "EMAIL:", 6) != 0 ++ && os_strncmp(tok+1, "DNS:", 4) != 0 ++ && os_strncmp(tok+1, "URI:", 4) != 0); ++ list[nlist].p = s; ++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s); ++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) { ++ wpa_printf(MSG_INFO, "MTLS: excessive altSubjectName match '%s'", ++ match); ++ break; /* truncate huge list and continue */ ++ } ++ } ++ ++ if (!mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) ++ return 0; ++ ++ const mbedtls_x509_sequence *cur = &crt->subject_alt_names; ++ for (; cur != NULL; cur = cur->next) { ++ const unsigned char san_type = (unsigned char)cur->buf.tag ++ & MBEDTLS_ASN1_TAG_VALUE_MASK; ++ char t; ++ size_t step = 4; ++ switch (san_type) { /* "EMAIL:" or "DNS:" or "URI:" */ ++ case MBEDTLS_X509_SAN_RFC822_NAME: step = 6; t = 'E'; break; ++ case MBEDTLS_X509_SAN_DNS_NAME: t = 'D'; break; ++ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: t = 'U'; break; ++ default: continue; ++ } ++ ++ for (int i = 0; i < nlist; ++i) { ++ /* step over "EMAIL:" or "DNS:" or "URI:" in list[i].p */ ++ /* Note: v is not '\0'-terminated, but is a known length vlen, ++ * so okay to pass to os_strncasecmp() even though not z-string */ ++ if (cur->buf.len == list[i].n - step && t == *list[i].p ++ && 0 == os_strncasecmp((char *)cur->buf.p, ++ list[i].p+step, cur->buf.len)) { ++ return 1; /* match */ ++ } ++ } ++ } ++ return 0; /* no match */ ++} ++ ++ ++static int ++tls_mbedtls_match_suffix(const char *v, size_t vlen, ++ const struct mlist *list, int nlist, int full) ++{ ++ /* Note: v is not '\0'-terminated, but is a known length vlen, ++ * so okay to pass to os_strncasecmp() even though not z-string */ ++ for (int i = 0; i < nlist; ++i) { ++ size_t n = list[i].n; ++ if ((n == vlen || (n < vlen && v[vlen-n-1] == '.' && !full)) ++ && 0 == os_strncasecmp(v+vlen-n, list[i].p, n)) ++ return 1; /* match */ ++ } ++ return 0; /* no match */ ++} ++ ++ ++static int ++tls_mbedtls_match_suffixes(mbedtls_x509_crt *crt, const char *match, int full) ++{ ++ /* RFE: this could be pre-parsed into structured data at config time */ ++ struct mlist list[256]; /*(much larger than expected)*/ ++ int nlist = 0; ++ for (const char *s = match, *tok; *s; s = tok ? tok+1 : "") { ++ tok = os_strchr(s, ';'); ++ list[nlist].p = s; ++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s); ++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) { ++ wpa_printf(MSG_INFO, "MTLS: excessive suffix match '%s'", match); ++ break; /* truncate huge list and continue */ ++ } ++ } ++ ++ /* check subjectAltNames */ ++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) { ++ const mbedtls_x509_sequence *cur = &crt->subject_alt_names; ++ for (; cur != NULL; cur = cur->next) { ++ const unsigned char san_type = (unsigned char)cur->buf.tag ++ & MBEDTLS_ASN1_TAG_VALUE_MASK; ++ if (san_type == MBEDTLS_X509_SAN_DNS_NAME ++ && tls_mbedtls_match_suffix((char *)cur->buf.p, ++ cur->buf.len, ++ list, nlist, full)) { ++ return 1; /* match */ ++ } ++ } ++ } ++ ++ /* check subject CN */ ++ const mbedtls_x509_name *name = &crt->subject; ++ for (; name != NULL; name = name->next) { ++ if (name->oid.p && MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0) ++ break; ++ } ++ if (name && tls_mbedtls_match_suffix((char *)name->val.p, name->val.len, ++ list, nlist, full)) { ++ return 1; /* match */ ++ } ++ ++ return 0; /* no match */ ++} ++ ++ ++static int ++tls_mbedtls_match_dn_field(mbedtls_x509_crt *crt, const char *match) ++{ ++ /* RFE: this could be pre-parsed into structured data at config time */ ++ struct mlistoid { const char *p; size_t n; ++ const char *oid; size_t olen; ++ int prefix; }; ++ struct mlistoid list[32]; /*(much larger than expected)*/ ++ int nlist = 0; ++ for (const char *s = match, *tok, *e; *s; s = tok ? tok+1 : "") { ++ tok = os_strchr(s, '/'); ++ list[nlist].oid = NULL; ++ list[nlist].olen = 0; ++ list[nlist].n = tok ? (size_t)(tok - s) : os_strlen(s); ++ e = memchr(s, '=', list[nlist].n); ++ if (e == NULL) { ++ if (list[nlist].n == 0) ++ continue; /* skip consecutive, repeated '/' */ ++ if (list[nlist].n == 1 && *s == '*') { ++ /* special-case "*" to match any OID and value */ ++ s = e = "=*"; ++ list[nlist].n = 2; ++ list[nlist].oid = ""; ++ } ++ else { ++ wpa_printf(MSG_INFO, ++ "MTLS: invalid check_cert_subject '%s' missing '='", ++ match); ++ return 0; ++ } ++ } ++ switch (e - s) { ++ case 1: ++ if (*s == 'C') { ++ list[nlist].oid = MBEDTLS_OID_AT_COUNTRY; ++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_COUNTRY)-1; ++ } ++ else if (*s == 'L') { ++ list[nlist].oid = MBEDTLS_OID_AT_LOCALITY; ++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_LOCALITY)-1; ++ } ++ else if (*s == 'O') { ++ list[nlist].oid = MBEDTLS_OID_AT_ORGANIZATION; ++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORGANIZATION)-1; ++ } ++ break; ++ case 2: ++ if (s[0] == 'C' && s[1] == 'N') { ++ list[nlist].oid = MBEDTLS_OID_AT_CN; ++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_CN)-1; ++ } ++ else if (s[0] == 'S' && s[1] == 'T') { ++ list[nlist].oid = MBEDTLS_OID_AT_STATE; ++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_STATE)-1; ++ } ++ else if (s[0] == 'O' && s[1] == 'U') { ++ list[nlist].oid = MBEDTLS_OID_AT_ORG_UNIT; ++ list[nlist].olen = sizeof(MBEDTLS_OID_AT_ORG_UNIT)-1; ++ } ++ break; ++ case 12: ++ if (os_memcmp(s, "emailAddress", 12) == 0) { ++ list[nlist].oid = MBEDTLS_OID_PKCS9_EMAIL; ++ list[nlist].olen = sizeof(MBEDTLS_OID_PKCS9_EMAIL)-1; ++ } ++ break; ++ default: ++ break; ++ } ++ if (list[nlist].oid == NULL) { ++ wpa_printf(MSG_INFO, ++ "MTLS: Unknown field in check_cert_subject '%s'", ++ match); ++ return 0; ++ } ++ list[nlist].n -= (size_t)(++e - s); ++ list[nlist].p = e; ++ if (list[nlist].n && e[list[nlist].n-1] == '*') { ++ --list[nlist].n; ++ list[nlist].prefix = 1; ++ } ++ /*(could easily add support for suffix matches if value begins with '*', ++ * but suffix match is not currently supported by other TLS modules)*/ ++ ++ if (list[nlist].n && ++nlist == sizeof(list)/sizeof(*list)) { ++ wpa_printf(MSG_INFO, ++ "MTLS: excessive check_cert_subject match '%s'", ++ match); ++ break; /* truncate huge list and continue */ ++ } ++ } ++ ++ /* each component in match string must match cert Subject in order listed ++ * The behavior below preserves ordering but is slightly different than ++ * the grossly inefficient contortions implemented in tls_openssl.c */ ++ const mbedtls_x509_name *name = &crt->subject; ++ for (int i = 0; i < nlist; ++i) { ++ int found = 0; ++ for (; name != NULL && !found; name = name->next) { ++ if (!name->oid.p) ++ continue; ++ /* special-case "*" to match any OID and value */ ++ if (list[i].olen == 0) { ++ found = 1; ++ continue; ++ } ++ /* perform equalent of !MBEDTLS_OID_CMP() with oid ptr and len */ ++ if (list[i].olen != name->oid.len ++ || os_memcmp(list[i].oid, name->oid.p, name->oid.len) != 0) ++ continue; ++ /* Note: v is not '\0'-terminated, but is a known length vlen, ++ * so okay to pass to os_strncasecmp() even though not z-string */ ++ if ((list[i].prefix ++ ? list[i].n <= name->val.len /* prefix match */ ++ : list[i].n == name->val.len) /* full match */ ++ && 0 == os_strncasecmp((char *)name->val.p, ++ list[i].p, list[i].n)) { ++ found = 1; ++ continue; ++ } ++ } ++ if (!found) ++ return 0; /* no match */ ++ } ++ return 1; /* match */ ++} ++ ++ ++__attribute_cold__ ++static void ++tls_mbedtls_verify_fail_event (mbedtls_x509_crt *crt, int depth, ++ const char *errmsg, enum tls_fail_reason reason) ++{ ++ struct tls_config *init_conf = &tls_ctx_global.init_conf; ++ if (init_conf->event_cb == NULL) ++ return; ++ ++ struct wpabuf *certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len); ++ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2]; ++ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0) ++ subject[0] = '\0'; ++ union tls_event_data ev; ++ os_memset(&ev, 0, sizeof(ev)); ++ ev.cert_fail.reason = reason; ++ ev.cert_fail.depth = depth; ++ ev.cert_fail.subject = subject; ++ ev.cert_fail.reason_txt = errmsg; ++ ev.cert_fail.cert = certbuf; ++ ++ init_conf->event_cb(init_conf->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); ++ ++ wpabuf_free(certbuf); ++} ++ ++ ++__attribute_noinline__ ++static void ++tls_mbedtls_verify_cert_event (struct tls_connection *conn, ++ mbedtls_x509_crt *crt, int depth) ++{ ++ struct tls_config *init_conf = &tls_ctx_global.init_conf; ++ if (init_conf->event_cb == NULL) ++ return; ++ ++ struct wpabuf *certbuf = NULL; ++ union tls_event_data ev; ++ os_memset(&ev, 0, sizeof(ev)); ++ ++ #ifdef MBEDTLS_SHA256_C ++ u8 hash[SHA256_DIGEST_LENGTH]; ++ const u8 *addr[] = { (u8 *)crt->raw.p }; ++ if (sha256_vector(1, addr, &crt->raw.len, hash) == 0) { ++ ev.peer_cert.hash = hash; ++ ev.peer_cert.hash_len = sizeof(hash); ++ } ++ #endif ++ ev.peer_cert.depth = depth; ++ char subject[MBEDTLS_X509_MAX_DN_NAME_SIZE*2]; ++ if (depth == 0) ++ ev.peer_cert.subject = conn->peer_subject; ++ if (ev.peer_cert.subject == NULL) { ++ ev.peer_cert.subject = subject; ++ if (mbedtls_x509_dn_gets(subject, sizeof(subject), &crt->subject) < 0) ++ subject[0] = '\0'; ++ } ++ ++ char serial_num[128+1]; ++ ev.peer_cert.serial_num = ++ tls_mbedtls_peer_serial_num(crt, serial_num, sizeof(serial_num)); ++ ++ const mbedtls_x509_sequence *cur; ++ ++ cur = NULL; ++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_SUBJECT_ALT_NAME)) ++ cur = &crt->subject_alt_names; ++ for (; cur != NULL; cur = cur->next) { ++ const unsigned char san_type = (unsigned char)cur->buf.tag ++ & MBEDTLS_ASN1_TAG_VALUE_MASK; ++ size_t prelen = 4; ++ const char *pre; ++ switch (san_type) { ++ case MBEDTLS_X509_SAN_RFC822_NAME: prelen = 6; pre = "EMAIL:";break; ++ case MBEDTLS_X509_SAN_DNS_NAME: pre = "DNS:"; break; ++ case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: pre = "URI:"; break; ++ default: continue; ++ } ++ ++ char *pos = os_malloc(prelen + cur->buf.len + 1); ++ if (pos == NULL) ++ break; ++ ev.peer_cert.altsubject[ev.peer_cert.num_altsubject] = pos; ++ os_memcpy(pos, pre, prelen); ++ /* data should be properly backslash-escaped if needed, ++ * so code below does not re-escape, but does replace CTLs */ ++ /*os_memcpy(pos+prelen, cur->buf.p, cur->buf.len);*/ ++ /*pos[prelen+cur->buf.len] = '\0';*/ ++ pos += prelen; ++ for (size_t i = 0; i < cur->buf.len; ++i) { ++ unsigned char c = cur->buf.p[i]; ++ *pos++ = (c >= 32 && c != 127) ? c : '?'; ++ } ++ *pos = '\0'; ++ ++ if (++ev.peer_cert.num_altsubject == TLS_MAX_ALT_SUBJECT) ++ break; ++ } ++ ++ cur = NULL; ++ if (mbedtls_x509_crt_has_ext_type(crt, MBEDTLS_X509_EXT_CERTIFICATE_POLICIES)) ++ cur = &crt->certificate_policies; ++ for (; cur != NULL; cur = cur->next) { ++ if (cur->buf.len != 11) /* len of OID_TOD_STRICT or OID_TOD_TOFU */ ++ continue; ++ /* TOD-STRICT "1.3.6.1.4.1.40808.1.3.1" */ ++ /* TOD-TOFU "1.3.6.1.4.1.40808.1.3.2" */ ++ #define OID_TOD_STRICT "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x01" ++ #define OID_TOD_TOFU "\x2b\x06\x01\x04\x01\x82\xbe\x68\x01\x03\x02" ++ if (os_memcmp(cur->buf.p, ++ OID_TOD_STRICT, sizeof(OID_TOD_STRICT)-1) == 0) { ++ ev.peer_cert.tod = 1; /* TOD-STRICT */ ++ break; ++ } ++ if (os_memcmp(cur->buf.p, ++ OID_TOD_TOFU, sizeof(OID_TOD_TOFU)-1) == 0) { ++ ev.peer_cert.tod = 2; /* TOD-TOFU */ ++ break; ++ } ++ } ++ ++ struct tls_conf *tls_conf = conn->tls_conf; ++ if (tls_conf->ca_cert_probe || (tls_conf->flags & TLS_CONN_EXT_CERT_CHECK) ++ || init_conf->cert_in_cb) { ++ certbuf = wpabuf_alloc_copy(crt->raw.p, crt->raw.len); ++ ev.peer_cert.cert = certbuf; ++ } ++ ++ init_conf->event_cb(init_conf->cb_ctx, TLS_PEER_CERTIFICATE, &ev); ++ ++ wpabuf_free(certbuf); ++ char **altsubject; ++ *(const char ***)&altsubject = ev.peer_cert.altsubject; ++ for (size_t i = 0; i < ev.peer_cert.num_altsubject; ++i) ++ os_free(altsubject[i]); ++} ++ ++ ++static int ++tls_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags) ++{ ++ /* XXX: N.B. verify code not carefully tested besides hwsim tests ++ * ++ * RFE: mbedtls_x509_crt_verify_info() and enhance log trace messages ++ * RFE: review and add support for additional TLS_CONN_* flags ++ * not handling OCSP (not available in mbedtls) ++ * ... */ ++ ++ struct tls_connection *conn = (struct tls_connection *)arg; ++ struct tls_conf *tls_conf = conn->tls_conf; ++ uint32_t flags_in = *flags; ++ ++ if (depth > 8) { /*(depth 8 picked as arbitrary limit)*/ ++ emsg(MSG_WARNING, "client cert chain too long"); ++ *flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */ ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "client cert chain too long", ++ TLS_FAIL_BAD_CERTIFICATE); ++ } ++ else if (tls_conf->verify_depth0_only) { ++ if (depth > 0) ++ *flags = 0; ++ else { ++ #ifdef MBEDTLS_SHA256_C ++ u8 hash[SHA256_DIGEST_LENGTH]; ++ const u8 *addr[] = { (u8 *)crt->raw.p }; ++ if (sha256_vector(1, addr, &crt->raw.len, hash) < 0 ++ || os_memcmp(tls_conf->ca_cert_hash, hash, sizeof(hash)) != 0) { ++ *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "cert hash mismatch", ++ TLS_FAIL_UNTRUSTED); ++ } ++ else /* hash matches; ignore other issues *except* if revoked)*/ ++ *flags &= MBEDTLS_X509_BADCERT_REVOKED; ++ #endif ++ } ++ } ++ else if (depth == 0) { ++ if (!conn->peer_subject) ++ tls_mbedtls_set_peer_subject(conn, crt); ++ /*(use same labels to tls_mbedtls_verify_fail_event() as used in ++ * other TLS modules so that hwsim tests find exact string match)*/ ++ if (!conn->peer_subject) { /* error copying subject string */ ++ *flags |= MBEDTLS_X509_BADCERT_OTHER; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "internal error", ++ TLS_FAIL_UNSPECIFIED); ++ } ++ /*(use os_strstr() for subject match as is done in tls_mbedtls.c ++ * to follow the same behavior, even though a suffix match would ++ * make more sense. Also, note that strstr match does not ++ * normalize whitespace (between components) for comparison)*/ ++ else if (tls_conf->subject_match ++ && os_strstr(conn->peer_subject, ++ tls_conf->subject_match) == NULL) { ++ wpa_printf(MSG_WARNING, ++ "MTLS: Subject '%s' did not match with '%s'", ++ conn->peer_subject, tls_conf->subject_match); ++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "Subject mismatch", ++ TLS_FAIL_SUBJECT_MISMATCH); ++ } ++ if (tls_conf->altsubject_match ++ && !tls_mbedtls_match_altsubject(crt, tls_conf->altsubject_match)) { ++ wpa_printf(MSG_WARNING, ++ "MTLS: altSubjectName match '%s' not found", ++ tls_conf->altsubject_match); ++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "AltSubject mismatch", ++ TLS_FAIL_ALTSUBJECT_MISMATCH); ++ } ++ if (tls_conf->suffix_match ++ && !tls_mbedtls_match_suffixes(crt, tls_conf->suffix_match, 0)) { ++ wpa_printf(MSG_WARNING, ++ "MTLS: Domain suffix match '%s' not found", ++ tls_conf->suffix_match); ++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "Domain suffix mismatch", ++ TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); ++ } ++ if (tls_conf->domain_match ++ && !tls_mbedtls_match_suffixes(crt, tls_conf->domain_match, 1)) { ++ wpa_printf(MSG_WARNING, ++ "MTLS: Domain match '%s' not found", ++ tls_conf->domain_match); ++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "Domain mismatch", ++ TLS_FAIL_DOMAIN_MISMATCH); ++ } ++ if (tls_conf->check_cert_subject ++ && !tls_mbedtls_match_dn_field(crt, tls_conf->check_cert_subject)) { ++ *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "Distinguished Name", ++ TLS_FAIL_DN_MISMATCH); ++ } ++ if (tls_conf->flags & TLS_CONN_SUITEB) { ++ /* check RSA modulus size (public key bitlen) */ ++ const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(&crt->pk); ++ if ((pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS) ++ && mbedtls_pk_get_bitlen(&crt->pk) < 3072) { ++ /* hwsim suite_b RSA tests expect 3072 ++ * suite_b_192_rsa_ecdhe_radius_rsa2048_client ++ * suite_b_192_rsa_dhe_radius_rsa2048_client */ ++ *flags |= MBEDTLS_X509_BADCERT_BAD_KEY; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "Insufficient RSA modulus size", ++ TLS_FAIL_INSUFFICIENT_KEY_LEN); ++ } ++ } ++ if (tls_conf->check_crl && tls_conf->crl == NULL) { ++ /* see tests/hwsim test_ap_eap.py ap_wpa2_eap_tls_check_crl */ ++ emsg(MSG_WARNING, "check_crl set but no CRL loaded; reject all?"); ++ *flags |= MBEDTLS_X509_BADCERT_OTHER; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "check_crl set but no CRL loaded; " ++ "reject all?", ++ TLS_FAIL_BAD_CERTIFICATE); ++ } ++ } ++ else { ++ if (tls_conf->check_crl != 2) /* 2 == verify CRLs for all certs */ ++ *flags &= ~MBEDTLS_X509_BADCERT_REVOKED; ++ } ++ ++ if (!tls_conf->check_crl_strict) { ++ *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED; ++ *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; ++ } ++ ++ if (tls_conf->flags & TLS_CONN_DISABLE_TIME_CHECKS) { ++ *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED; ++ *flags &= ~MBEDTLS_X509_BADCERT_FUTURE; ++ } ++ ++ tls_mbedtls_verify_cert_event(conn, crt, depth); ++ ++ if (*flags) { ++ if (*flags & (MBEDTLS_X509_BADCERT_NOT_TRUSTED ++ |MBEDTLS_X509_BADCERT_CN_MISMATCH ++ |MBEDTLS_X509_BADCERT_REVOKED)) { ++ emsg(MSG_WARNING, "client cert not trusted"); ++ } ++ /* report event if flags set but no additional flags set above */ ++ /* (could translate flags to more detailed TLS_FAIL_* if needed) */ ++ if (!(*flags & ~flags_in)) { ++ enum tls_fail_reason reason = TLS_FAIL_UNSPECIFIED; ++ const char *errmsg = "cert verify fail unspecified"; ++ if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { ++ reason = TLS_FAIL_UNTRUSTED; ++ errmsg = "certificate not trusted"; ++ } ++ if (*flags & MBEDTLS_X509_BADCERT_REVOKED) { ++ reason = TLS_FAIL_REVOKED; ++ errmsg = "certificate has been revoked"; ++ } ++ if (*flags & MBEDTLS_X509_BADCERT_FUTURE) { ++ reason = TLS_FAIL_NOT_YET_VALID; ++ errmsg = "certificate not yet valid"; ++ } ++ if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) { ++ reason = TLS_FAIL_EXPIRED; ++ errmsg = "certificate has expired"; ++ } ++ if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) { ++ reason = TLS_FAIL_BAD_CERTIFICATE; ++ errmsg = "certificate uses insecure algorithm"; ++ } ++ tls_mbedtls_verify_fail_event(crt, depth, errmsg, reason); ++ } ++ #if 0 ++ /* ??? send (again) cert events for all certs in chain ??? ++ * (should already have been called for greater depths) */ ++ /* tls_openssl.c:tls_verify_cb() sends cert events for all certs ++ * in chain if certificate validation fails, but sends all events ++ * with depth set to 0 (might be a bug) */ ++ if (depth > 0) { ++ int pdepth = depth + 1; ++ for (mbedtls_x509_crt *pcrt; (pcrt = crt->next); ++pdepth) { ++ tls_mbedtls_verify_cert_event(conn, pcrt, pdepth); ++ } ++ } ++ #endif ++ /*(do not preserve subject if verification failed but was optional)*/ ++ if (depth == 0 && conn->peer_subject) { ++ os_free(conn->peer_subject); ++ conn->peer_subject = NULL; ++ } ++ } ++ else if (depth == 0) { ++ struct tls_config *init_conf = &tls_ctx_global.init_conf; ++ if (tls_conf->ca_cert_probe) { ++ /* reject server certificate on probe-only run */ ++ *flags |= MBEDTLS_X509_BADCERT_OTHER; ++ tls_mbedtls_verify_fail_event(crt, depth, ++ "server chain probe", ++ TLS_FAIL_SERVER_CHAIN_PROBE); ++ } ++ else if (init_conf->event_cb) { ++ /* ??? send event as soon as depth == 0 is verified ??? ++ * What about rest of chain? ++ * Follows tls_mbedtls.c behavior: */ ++ init_conf->event_cb(init_conf->cb_ctx, ++ TLS_CERT_CHAIN_SUCCESS, NULL); ++ } ++ } ++ ++ return 0; ++} +--- /dev/null ++++ b/tests/build/build-wpa_supplicant-mbedtls.config +@@ -0,0 +1,24 @@ ++CONFIG_TLS=mbedtls ++ ++CONFIG_WPS=y ++CONFIG_EAP_TLS=y ++CONFIG_EAP_MSCHAPV2=y ++ ++CONFIG_EAP_PSK=y ++CONFIG_EAP_GPSK=y ++CONFIG_EAP_AKA=y ++CONFIG_EAP_SIM=y ++CONFIG_EAP_SAKE=y ++CONFIG_EAP_PAX=y ++CONFIG_EAP_FAST=y ++CONFIG_EAP_IKEV2=y ++ ++CONFIG_SAE=y ++CONFIG_FILS=y ++CONFIG_FILS_SK_PFS=y ++CONFIG_OWE=y ++CONFIG_DPP=y ++CONFIG_SUITEB=y ++CONFIG_SUITEB192=y ++ ++CFLAGS += -Werror +--- a/tests/hwsim/example-hostapd.config ++++ b/tests/hwsim/example-hostapd.config +@@ -4,6 +4,7 @@ CONFIG_DRIVER_NONE=y + CONFIG_DRIVER_NL80211=y + CONFIG_RSN_PREAUTH=y + ++#CONFIG_TLS=mbedtls + #CONFIG_TLS=internal + #CONFIG_INTERNAL_LIBTOMMATH=y + #CONFIG_INTERNAL_LIBTOMMATH_FAST=y +@@ -39,6 +40,9 @@ endif + ifeq ($(CONFIG_TLS), wolfssl) + CONFIG_EAP_PWD=y + endif ++ifeq ($(CONFIG_TLS), mbedtls) ++CONFIG_EAP_PWD=y ++endif + CONFIG_EAP_EKE=y + CONFIG_PKCS12=y + CONFIG_RADIUS_SERVER=y +--- a/tests/hwsim/example-wpa_supplicant.config ++++ b/tests/hwsim/example-wpa_supplicant.config +@@ -2,6 +2,7 @@ + + CONFIG_TLS=openssl + #CONFIG_TLS=wolfssl ++#CONFIG_TLS=mbedtls + #CONFIG_TLS=internal + #CONFIG_INTERNAL_LIBTOMMATH=y + #CONFIG_INTERNAL_LIBTOMMATH_FAST=y +@@ -41,6 +42,9 @@ endif + ifeq ($(CONFIG_TLS), wolfssl) + CONFIG_EAP_PWD=y + endif ++ifeq ($(CONFIG_TLS), mbedtls) ++CONFIG_EAP_PWD=y ++endif + + CONFIG_USIM_SIMULATOR=y + CONFIG_SIM_SIMULATOR=y +--- a/wpa_supplicant/Makefile ++++ b/wpa_supplicant/Makefile +@@ -1149,6 +1149,29 @@ endif + CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\" + endif + ++ifeq ($(CONFIG_TLS), mbedtls) ++ifndef CONFIG_CRYPTO ++CONFIG_CRYPTO=mbedtls ++endif ++ifdef TLS_FUNCS ++OBJS += ../src/crypto/tls_mbedtls.o ++LIBS += -lmbedtls -lmbedx509 ++endif ++OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ++OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ++OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ++ifdef NEED_FIPS186_2_PRF ++OBJS += ../src/crypto/fips_prf_internal.o ++SHA1OBJS += ../src/crypto/sha1-internal.o ++endif ++ifeq ($(CONFIG_CRYPTO), mbedtls) ++LIBS += -lmbedcrypto ++LIBS_p += -lmbedcrypto ++# XXX: create a config option? ++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 ++endif ++endif ++ + ifeq ($(CONFIG_TLS), gnutls) + ifndef CONFIG_CRYPTO + # default to libgcrypt +@@ -1341,9 +1364,11 @@ endif + + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + NEED_INTERNAL_AES_WRAP=y + endif + endif ++endif + ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP + # Seems to be needed at least with BoringSSL + NEED_INTERNAL_AES_WRAP=y +@@ -1357,9 +1382,11 @@ endif + + ifdef NEED_INTERNAL_AES_WRAP + ifneq ($(CONFIG_TLS), linux) ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-unwrap.o + endif + endif ++endif + ifdef NEED_AES_EAX + AESOBJS += ../src/crypto/aes-eax.o + NEED_AES_CTR=y +@@ -1369,35 +1396,45 @@ AESOBJS += ../src/crypto/aes-siv.o + NEED_AES_CTR=y + endif + ifdef NEED_AES_CTR ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-ctr.o + endif ++endif + ifdef NEED_AES_ENCBLOCK ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-encblock.o + endif ++endif + NEED_AES_ENC=y + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-omac1.o + endif + endif + endif ++endif + ifdef NEED_AES_WRAP + NEED_AES_ENC=y + ifdef NEED_INTERNAL_AES_WRAP ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-wrap.o + endif + endif ++endif + ifdef NEED_AES_CBC + NEED_AES_ENC=y + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + AESOBJS += ../src/crypto/aes-cbc.o + endif + endif + endif + endif ++endif + ifdef NEED_AES_ENC + ifdef CONFIG_INTERNAL_AES + AESOBJS += ../src/crypto/aes-internal-enc.o +@@ -1412,12 +1449,16 @@ ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1.o + endif + endif + endif + endif ++endif ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-prf.o ++endif + ifdef CONFIG_INTERNAL_SHA1 + SHA1OBJS += ../src/crypto/sha1-internal.o + ifdef NEED_FIPS186_2_PRF +@@ -1429,29 +1470,37 @@ CFLAGS += -DCONFIG_NO_PBKDF2 + else + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-pbkdf2.o + endif + endif + endif ++endif + ifdef NEED_T_PRF ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-tprf.o + endif ++endif + ifdef NEED_TLS_PRF ++ifneq ($(CONFIG_TLS), mbedtls) + SHA1OBJS += ../src/crypto/sha1-tlsprf.o + endif + endif ++endif + + ifndef CONFIG_FIPS + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + MD5OBJS += ../src/crypto/md5.o + endif + endif + endif + endif + endif ++endif + ifdef NEED_MD5 + ifdef CONFIG_INTERNAL_MD5 + MD5OBJS += ../src/crypto/md5-internal.o +@@ -1506,12 +1555,17 @@ ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + SHA256OBJS += ../src/crypto/sha256.o + endif + endif + endif + endif ++endif ++ ++ifneq ($(CONFIG_TLS), mbedtls) + SHA256OBJS += ../src/crypto/sha256-prf.o ++endif + ifdef CONFIG_INTERNAL_SHA256 + SHA256OBJS += ../src/crypto/sha256-internal.o + endif +@@ -1524,50 +1578,68 @@ CFLAGS += -DCONFIG_INTERNAL_SHA512 + SHA256OBJS += ../src/crypto/sha512-internal.o + endif + ifdef NEED_TLS_PRF_SHA256 ++ifneq ($(CONFIG_TLS), mbedtls) + SHA256OBJS += ../src/crypto/sha256-tlsprf.o + endif ++endif + ifdef NEED_TLS_PRF_SHA384 ++ifneq ($(CONFIG_TLS), mbedtls) + SHA256OBJS += ../src/crypto/sha384-tlsprf.o + endif ++endif + ifdef NEED_HMAC_SHA256_KDF + CFLAGS += -DCONFIG_HMAC_SHA256_KDF ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha256-kdf.o + endif ++endif + ifdef NEED_HMAC_SHA384_KDF + CFLAGS += -DCONFIG_HMAC_SHA384_KDF ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha384-kdf.o + endif ++endif + ifdef NEED_HMAC_SHA512_KDF + CFLAGS += -DCONFIG_HMAC_SHA512_KDF ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha512-kdf.o + endif ++endif + OBJS += $(SHA256OBJS) + ifdef NEED_SHA384 + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha384.o + endif + endif + endif + endif ++endif + CFLAGS += -DCONFIG_SHA384 ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha384-prf.o + endif ++endif + ifdef NEED_SHA512 + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), linux) + ifneq ($(CONFIG_TLS), gnutls) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha512.o + endif + endif + endif + endif ++endif + CFLAGS += -DCONFIG_SHA512 ++ifneq ($(CONFIG_TLS), mbedtls) + OBJS += ../src/crypto/sha512-prf.o + endif ++endif + + ifdef NEED_ASN1 + OBJS += ../src/tls/asn1.o +@@ -1742,10 +1814,12 @@ ifdef CONFIG_FIPS + CFLAGS += -DCONFIG_FIPS + ifneq ($(CONFIG_TLS), openssl) + ifneq ($(CONFIG_TLS), wolfssl) ++ifneq ($(CONFIG_TLS), mbedtls) + $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl) + endif + endif + endif ++endif + + OBJS += $(SHA1OBJS) $(DESOBJS) + +--- a/wpa_supplicant/defconfig ++++ b/wpa_supplicant/defconfig +@@ -10,8 +10,8 @@ + # to override previous values of the variables. + + +-# Uncomment following two lines and fix the paths if you have installed OpenSSL +-# or GnuTLS in non-default location ++# Uncomment following two lines and fix the paths if you have installed TLS ++# libraries in a non-default location + #CFLAGS += -I/usr/local/openssl/include + #LIBS += -L/usr/local/openssl/lib + +@@ -20,6 +20,7 @@ + # used to fix build issues on such systems (krb5.h not found). + #CFLAGS += -I/usr/include/kerberos + ++ + # Driver interface for generic Linux wireless extensions + # Note: WEXT is deprecated in the current Linux kernel version and no new + # functionality is added to it. nl80211-based interface is the new +@@ -326,6 +327,7 @@ CONFIG_BACKEND=file + # openssl = OpenSSL (default) + # gnutls = GnuTLS + # internal = Internal TLSv1 implementation (experimental) ++# mbedtls = mbed TLS + # linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental) + # none = Empty template + #CONFIG_TLS=openssl diff --git a/package/network/services/hostapd/patches/120-mbedtls-fips186_2_prf.patch b/package/network/services/hostapd/patches/120-mbedtls-fips186_2_prf.patch new file mode 100644 index 00000000000..a0d287086b2 --- /dev/null +++ b/package/network/services/hostapd/patches/120-mbedtls-fips186_2_prf.patch @@ -0,0 +1,114 @@ +From c8dba4bd750269bcc80fed3d546e2077cb4cdf0e Mon Sep 17 00:00:00 2001 +From: Glenn Strauss +Date: Tue, 19 Jul 2022 20:02:21 -0400 +Subject: [PATCH 2/7] mbedtls: fips186_2_prf() + +Signed-off-by: Glenn Strauss +--- + hostapd/Makefile | 4 --- + src/crypto/crypto_mbedtls.c | 60 +++++++++++++++++++++++++++++++++++++ + wpa_supplicant/Makefile | 4 --- + 3 files changed, 60 insertions(+), 8 deletions(-) + +--- a/hostapd/Makefile ++++ b/hostapd/Makefile +@@ -759,10 +759,6 @@ endif + OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o + HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o + SOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o +-ifdef NEED_FIPS186_2_PRF +-OBJS += ../src/crypto/fips_prf_internal.o +-SHA1OBJS += ../src/crypto/sha1-internal.o +-endif + ifeq ($(CONFIG_CRYPTO), mbedtls) + ifdef CONFIG_DPP + LIBS += -lmbedx509 +--- a/src/crypto/crypto_mbedtls.c ++++ b/src/crypto/crypto_mbedtls.c +@@ -132,6 +132,12 @@ + #define CRYPTO_MBEDTLS_HMAC_KDF_SHA512 + #endif + ++#if defined(EAP_SIM) || defined(EAP_SIM_DYNAMIC) || defined(EAP_SERVER_SIM) \ ++ || defined(EAP_AKA) || defined(EAP_AKA_DYNAMIC) || defined(EAP_SERVER_AKA) ++/* EAP_SIM=y EAP_AKA=y */ ++#define CRYPTO_MBEDTLS_FIPS186_2_PRF ++#endif ++ + #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) \ + || defined(EAP_TEAP) || defined(EAP_TEAP_DYNAMIC) || defined(EAP_SERVER_FAST) + #define CRYPTO_MBEDTLS_SHA1_T_PRF +@@ -813,6 +819,60 @@ int sha1_t_prf(const u8 *key, size_t key + + #endif /* CRYPTO_MBEDTLS_SHA1_T_PRF */ + ++#ifdef CRYPTO_MBEDTLS_FIPS186_2_PRF ++ ++/* fips_prf_internal.c sha1-internal.c */ ++ ++/* used only by src/eap_common/eap_sim_common.c:eap_sim_prf() ++ * for eap_sim_derive_keys() and eap_sim_derive_keys_reauth() ++ * where xlen is 160 */ ++ ++int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) ++{ ++ /* FIPS 186-2 + change notice 1 */ ++ ++ mbedtls_sha1_context ctx; ++ u8 * const xkey = ctx.MBEDTLS_PRIVATE(buffer); ++ u32 * const xstate = ctx.MBEDTLS_PRIVATE(state); ++ const u32 xstate_init[] = ++ { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; ++ ++ mbedtls_sha1_init(&ctx); ++ os_memcpy(xkey, seed, seed_len < 64 ? seed_len : 64); ++ ++ /* note: does not fill extra bytes if (xlen % 20) (SHA1_MAC_LEN) */ ++ for (; xlen >= 20; xlen -= 20) { ++ /* XSEED_j = 0 */ ++ /* XVAL = (XKEY + XSEED_j) mod 2^b */ ++ ++ /* w_i = G(t, XVAL) */ ++ os_memcpy(xstate, xstate_init, sizeof(xstate_init)); ++ mbedtls_internal_sha1_process(&ctx, xkey); ++ ++ #if __BYTE_ORDER == __LITTLE_ENDIAN ++ xstate[0] = host_to_be32(xstate[0]); ++ xstate[1] = host_to_be32(xstate[1]); ++ xstate[2] = host_to_be32(xstate[2]); ++ xstate[3] = host_to_be32(xstate[3]); ++ xstate[4] = host_to_be32(xstate[4]); ++ #endif ++ os_memcpy(x, xstate, 20); ++ if (xlen == 20) /*(done; skip prep for next loop)*/ ++ break; ++ ++ /* XKEY = (1 + XKEY + w_i) mod 2^b */ ++ for (u32 carry = 1, k = 20; k-- > 0; carry >>= 8) ++ xkey[k] = (carry += xkey[k] + x[k]) & 0xff; ++ x += 20; ++ /* x_j = w_0|w_1 (each pair of iterations through loop)*/ ++ } ++ ++ mbedtls_sha1_free(&ctx); ++ return 0; ++} ++ ++#endif /* CRYPTO_MBEDTLS_FIPS186_2_PRF */ ++ + #endif /* MBEDTLS_SHA1_C */ + + +--- a/wpa_supplicant/Makefile ++++ b/wpa_supplicant/Makefile +@@ -1160,10 +1160,6 @@ endif + OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o + OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o + OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o +-ifdef NEED_FIPS186_2_PRF +-OBJS += ../src/crypto/fips_prf_internal.o +-SHA1OBJS += ../src/crypto/sha1-internal.o +-endif + ifeq ($(CONFIG_CRYPTO), mbedtls) + LIBS += -lmbedcrypto + LIBS_p += -lmbedcrypto diff --git a/package/network/services/hostapd/patches/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch b/package/network/services/hostapd/patches/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch new file mode 100644 index 00000000000..ae7620b90c1 --- /dev/null +++ b/package/network/services/hostapd/patches/130-mbedtls-annotate-with-TEST_FAIL-for-hwsim-tests.patch @@ -0,0 +1,421 @@ +From 31bd19e0e0254b910cccfd3ddc6a6a9222bbcfc0 Mon Sep 17 00:00:00 2001 +From: Glenn Strauss +Date: Sun, 9 Oct 2022 05:12:17 -0400 +Subject: [PATCH 3/7] mbedtls: annotate with TEST_FAIL() for hwsim tests + +Signed-off-by: Glenn Strauss +--- + src/crypto/crypto_mbedtls.c | 124 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 124 insertions(+) + +--- a/src/crypto/crypto_mbedtls.c ++++ b/src/crypto/crypto_mbedtls.c +@@ -280,6 +280,9 @@ __attribute_noinline__ + static int md_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac, mbedtls_md_type_t md_type) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0) != 0){ +@@ -343,6 +346,9 @@ __attribute_noinline__ + static int sha384_512_vector(size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac, int is384) + { ++ if (TEST_FAIL()) ++ return -1; ++ + struct mbedtls_sha512_context ctx; + mbedtls_sha512_init(&ctx); + #if MBEDTLS_VERSION_MAJOR >= 3 +@@ -375,6 +381,9 @@ int sha384_vector(size_t num_elem, const + #include + int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) + { ++ if (TEST_FAIL()) ++ return -1; ++ + struct mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + #if MBEDTLS_VERSION_MAJOR >= 3 +@@ -397,6 +406,9 @@ int sha256_vector(size_t num_elem, const + #include + int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) + { ++ if (TEST_FAIL()) ++ return -1; ++ + struct mbedtls_sha1_context ctx; + mbedtls_sha1_init(&ctx); + #if MBEDTLS_VERSION_MAJOR >= 3 +@@ -419,6 +431,9 @@ int sha1_vector(size_t num_elem, const u + #include + int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) + { ++ if (TEST_FAIL()) ++ return -1; ++ + struct mbedtls_md5_context ctx; + mbedtls_md5_init(&ctx); + #if MBEDTLS_VERSION_MAJOR >= 3 +@@ -441,6 +456,9 @@ int md5_vector(size_t num_elem, const u8 + #include + int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) + { ++ if (TEST_FAIL()) ++ return -1; ++ + struct mbedtls_md4_context ctx; + mbedtls_md4_init(&ctx); + mbedtls_md4_starts_ret(&ctx); +@@ -460,6 +478,9 @@ static int hmac_vector(const u8 *key, si + const u8 *addr[], const size_t *len, u8 *mac, + mbedtls_md_type_t md_type) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + if (mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1) != 0){ +@@ -571,6 +592,9 @@ static int hmac_kdf_expand(const u8 *prk + const char *label, const u8 *info, size_t info_len, + u8 *okm, size_t okm_len, mbedtls_md_type_t md_type) + { ++ if (TEST_FAIL()) ++ return -1; ++ + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); + #ifdef MBEDTLS_HKDF_C + if (label == NULL) /* RFC 5869 HKDF-Expand when (label == NULL) */ +@@ -663,6 +687,9 @@ static int hmac_prf_bits(const u8 *key, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits, mbedtls_md_type_t md_type) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_md_context_t ctx; + mbedtls_md_init(&ctx); + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); +@@ -938,6 +965,9 @@ int pbkdf2_sha1(const char *passphrase, + + static void *aes_crypt_init_mode(const u8 *key, size_t len, int mode) + { ++ if (TEST_FAIL()) ++ return NULL; ++ + mbedtls_aes_context *aes = os_malloc(sizeof(*aes)); + if (!aes) + return NULL; +@@ -996,6 +1026,9 @@ void aes_decrypt_deinit(void *ctx) + /* aes-wrap.c */ + int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_nist_kw_context ctx; + mbedtls_nist_kw_init(&ctx); + size_t olen; +@@ -1010,6 +1043,9 @@ int aes_wrap(const u8 *kek, size_t kek_l + /* aes-unwrap.c */ + int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, u8 *plain) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_nist_kw_context ctx; + mbedtls_nist_kw_init(&ctx); + size_t olen; +@@ -1041,6 +1077,9 @@ int omac1_aes_vector( + const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_cipher_type_t cipher_type; + switch (key_len) { + case 16: cipher_type = MBEDTLS_CIPHER_AES_128_ECB; break; +@@ -1103,6 +1142,9 @@ int omac1_aes_256(const u8 *key, const u + /* aes-encblock.c */ + int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_aes_context aes; + mbedtls_aes_init(&aes); + int ret = mbedtls_aes_setkey_enc(&aes, key, 128) +@@ -1118,6 +1160,9 @@ int aes_128_encrypt_block(const u8 *key, + int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, + u8 *data, size_t data_len) + { ++ if (TEST_FAIL()) ++ return -1; ++ + unsigned char counter[MBEDTLS_AES_BLOCK_SIZE]; + unsigned char stream_block[MBEDTLS_AES_BLOCK_SIZE]; + os_memcpy(counter, nonce, MBEDTLS_AES_BLOCK_SIZE);/*(must be writable)*/ +@@ -1160,11 +1205,17 @@ static int aes_128_cbc_oper(const u8 *ke + + int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) + { ++ if (TEST_FAIL()) ++ return -1; ++ + return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_ENCRYPT); + } + + int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) + { ++ if (TEST_FAIL()) ++ return -1; ++ + return aes_128_cbc_oper(key, iv, data, data_len, MBEDTLS_AES_DECRYPT); + } + +@@ -1407,6 +1458,10 @@ int crypto_hash_finish(struct crypto_has + } + mbedtls_md_free(mctx); + os_free(mctx); ++ ++ if (TEST_FAIL()) ++ return -1; ++ + return 0; + } + +@@ -1421,6 +1476,9 @@ int crypto_hash_finish(struct crypto_has + + struct crypto_bignum *crypto_bignum_init(void) + { ++ if (TEST_FAIL()) ++ return NULL; ++ + mbedtls_mpi *bn = os_malloc(sizeof(*bn)); + if (bn) + mbedtls_mpi_init(bn); +@@ -1429,6 +1487,9 @@ struct crypto_bignum *crypto_bignum_init + + struct crypto_bignum *crypto_bignum_init_set(const u8 *buf, size_t len) + { ++ if (TEST_FAIL()) ++ return NULL; ++ + mbedtls_mpi *bn = os_malloc(sizeof(*bn)); + if (bn) { + mbedtls_mpi_init(bn); +@@ -1442,6 +1503,9 @@ struct crypto_bignum *crypto_bignum_init + + struct crypto_bignum *crypto_bignum_init_uint(unsigned int val) + { ++ if (TEST_FAIL()) ++ return NULL; ++ + #if 0 /*(hostap use of this interface passes int, not uint)*/ + val = host_to_be32(val); + return crypto_bignum_init_set((const u8 *)&val, sizeof(val)); +@@ -1467,6 +1531,9 @@ void crypto_bignum_deinit(struct crypto_ + int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) + { ++ if (TEST_FAIL()) ++ return -1; ++ + size_t n = mbedtls_mpi_size((mbedtls_mpi *)a); + if (n < padlen) + n = padlen; +@@ -1477,6 +1544,9 @@ int crypto_bignum_to_bin(const struct cr + + int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) + { ++ if (TEST_FAIL()) ++ return -1; ++ + /*assert(r != m);*//* r must not be same as m for mbedtls_mpi_random()*/ + #if MBEDTLS_VERSION_NUMBER >= 0x021B0000 /* mbedtls 2.27.0 */ + return mbedtls_mpi_random((mbedtls_mpi *)r, 0, (mbedtls_mpi *)m, +@@ -1513,6 +1583,9 @@ int crypto_bignum_exptmod(const struct c + const struct crypto_bignum *c, + struct crypto_bignum *d) + { ++ if (TEST_FAIL()) ++ return -1; ++ + /* (check if input params match d; d is the result) */ + /* (a == d) is ok in current mbedtls implementation */ + if (b == d || c == d) { /*(not ok; store result in intermediate)*/ +@@ -1540,6 +1613,9 @@ int crypto_bignum_inverse(const struct c + const struct crypto_bignum *b, + struct crypto_bignum *c) + { ++ if (TEST_FAIL()) ++ return -1; ++ + return mbedtls_mpi_inv_mod((mbedtls_mpi *)c, + (const mbedtls_mpi *)a, + (const mbedtls_mpi *)b) ? -1 : 0; +@@ -1549,6 +1625,9 @@ int crypto_bignum_sub(const struct crypt + const struct crypto_bignum *b, + struct crypto_bignum *c) + { ++ if (TEST_FAIL()) ++ return -1; ++ + return mbedtls_mpi_sub_mpi((mbedtls_mpi *)c, + (const mbedtls_mpi *)a, + (const mbedtls_mpi *)b) ? -1 : 0; +@@ -1558,6 +1637,9 @@ int crypto_bignum_div(const struct crypt + const struct crypto_bignum *b, + struct crypto_bignum *c) + { ++ if (TEST_FAIL()) ++ return -1; ++ + /*(most current use of this crypto.h interface has a == c (result), + * so store result in an intermediate to avoid overwritten input)*/ + mbedtls_mpi R; +@@ -1575,6 +1657,9 @@ int crypto_bignum_addmod(const struct cr + const struct crypto_bignum *c, + struct crypto_bignum *d) + { ++ if (TEST_FAIL()) ++ return -1; ++ + return mbedtls_mpi_add_mpi((mbedtls_mpi *)d, + (const mbedtls_mpi *)a, + (const mbedtls_mpi *)b) +@@ -1588,6 +1673,9 @@ int crypto_bignum_mulmod(const struct cr + const struct crypto_bignum *c, + struct crypto_bignum *d) + { ++ if (TEST_FAIL()) ++ return -1; ++ + return mbedtls_mpi_mul_mpi((mbedtls_mpi *)d, + (const mbedtls_mpi *)a, + (const mbedtls_mpi *)b) +@@ -1600,6 +1688,9 @@ int crypto_bignum_sqrmod(const struct cr + const struct crypto_bignum *b, + struct crypto_bignum *c) + { ++ if (TEST_FAIL()) ++ return -1; ++ + #if 1 + return crypto_bignum_mulmod(a, a, b, c); + #else +@@ -1650,6 +1741,9 @@ int crypto_bignum_is_odd(const struct cr + int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) + { ++ if (TEST_FAIL()) ++ return -2; ++ + /* Security Note: + * mbedtls_mpi_exp_mod() is not documented to run in constant time, + * though mbedtls/library/bignum.c uses constant_time_internal.h funcs. +@@ -1702,6 +1796,9 @@ int crypto_mod_exp(const u8 *base, size_ + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) + { ++ if (TEST_FAIL()) ++ return -1; ++ + mbedtls_mpi bn_base, bn_exp, bn_modulus, bn_result; + mbedtls_mpi_init(&bn_base); + mbedtls_mpi_init(&bn_exp); +@@ -1769,6 +1866,9 @@ static int crypto_mbedtls_dh_init_public + int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) + { ++ if (TEST_FAIL()) ++ return -1; ++ + #if 0 /*(crypto_dh_init() duplicated (and identical) in crypto_*.c modules)*/ + size_t pubkey_len, pad; + +@@ -1810,6 +1910,9 @@ int crypto_dh_derive_secret(u8 generator + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) + { ++ if (TEST_FAIL()) ++ return -1; ++ + #if 0 + if (pubkey_len > prime_len || + (pubkey_len == prime_len && +@@ -2512,6 +2615,9 @@ const struct crypto_ec_point * crypto_ec + + struct crypto_ec_point *crypto_ec_point_init(struct crypto_ec *e) + { ++ if (TEST_FAIL()) ++ return NULL; ++ + mbedtls_ecp_point *p = os_malloc(sizeof(*p)); + if (p != NULL) + mbedtls_ecp_point_init(p); +@@ -2536,6 +2642,9 @@ int crypto_ec_point_x(struct crypto_ec * + int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) + { ++ if (TEST_FAIL()) ++ return -1; ++ + /* crypto.h documents crypto_ec_point_to_bin() output is big-endian */ + size_t len = CRYPTO_EC_plen(e); + if (x) { +@@ -2563,6 +2672,9 @@ int crypto_ec_point_to_bin(struct crypto + struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) + { ++ if (TEST_FAIL()) ++ return NULL; ++ + size_t len = CRYPTO_EC_plen(e); + mbedtls_ecp_point *p = os_malloc(sizeof(*p)); + u8 buf[1+MBEDTLS_MPI_MAX_SIZE*2]; +@@ -2615,6 +2727,9 @@ int crypto_ec_point_add(struct crypto_ec + const struct crypto_ec_point *b, + struct crypto_ec_point *c) + { ++ if (TEST_FAIL()) ++ return -1; ++ + /* mbedtls does not provide an mbedtls_ecp_point add function */ + mbedtls_mpi one; + mbedtls_mpi_init(&one); +@@ -2631,6 +2746,9 @@ int crypto_ec_point_mul(struct crypto_ec + const struct crypto_bignum *b, + struct crypto_ec_point *res) + { ++ if (TEST_FAIL()) ++ return -1; ++ + return mbedtls_ecp_mul( + (mbedtls_ecp_group *)e, (mbedtls_ecp_point *)res, + (const mbedtls_mpi *)b, (const mbedtls_ecp_point *)p, +@@ -2639,6 +2757,9 @@ int crypto_ec_point_mul(struct crypto_ec + + int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) + { ++ if (TEST_FAIL()) ++ return -1; ++ + if (mbedtls_ecp_get_type((mbedtls_ecp_group *)e) + == MBEDTLS_ECP_TYPE_MONTGOMERY) { + /* e.g. MBEDTLS_ECP_DP_CURVE25519 and MBEDTLS_ECP_DP_CURVE448 */ +@@ -2751,6 +2872,9 @@ struct crypto_bignum * + crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) + { ++ if (TEST_FAIL()) ++ return NULL; ++ + mbedtls_mpi *y2 = os_malloc(sizeof(*y2)); + if (y2 == NULL) + return NULL; diff --git a/package/network/services/hostapd/patches/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch b/package/network/services/hostapd/patches/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch new file mode 100644 index 00000000000..9e9e88c1ebe --- /dev/null +++ b/package/network/services/hostapd/patches/140-tests-Makefile-make-run-tests-with-CONFIG_TLS.patch @@ -0,0 +1,1358 @@ +From f24933dc175e0faf44a3cce3330c256a59649ca6 Mon Sep 17 00:00:00 2001 +From: Glenn Strauss +Date: Tue, 19 Jul 2022 23:01:17 -0400 +Subject: [PATCH 4/7] tests/Makefile make run-tests with CONFIG_TLS=... + +add test-crypto_module.c to run crypto_module_tests() + +adjust some tests/hwsim/*.py for mbed TLS (work in progress) + +option to build and run-tests with CONFIG_TLS=internal # (default) +$ cd tests; make clean +$ make run-tests + +option to build and run-tests with CONFIG_TLS=gnutls +$ cd tests; make clean CONFIG_TLS=gnutls +$ make run-tests CONFIG_TLS=gnutls + +option to build and run-tests with CONFIG_TLS=mbedtls +$ cd tests; make clean CONFIG_TLS=mbedtls +$ make run-tests CONFIG_TLS=mbedtls + +option to build and run-tests with CONFIG_TLS=openssl +$ cd tests; make clean CONFIG_TLS=openssl +$ make run-tests CONFIG_TLS=openssl + +option to build and run-tests with CONFIG_TLS=wolfssl +$ cd tests; make clean CONFIG_TLS=wolfssl +$ make run-tests CONFIG_TLS=wolfssl + +RFE: Makefile logic for crypto objects should be centralized + instead of being duplicated in hostapd/Makefile, + wpa_supplicant/Makefile, src/crypto/Makefile, + tests/Makefile, ... + +Signed-off-by: Glenn Strauss +--- + hostapd/Makefile | 6 + + src/crypto/Makefile | 129 ++++++++++++++++++++- + src/crypto/crypto_module_tests.c | 134 ++++++++++++++++++++++ + src/tls/Makefile | 11 ++ + tests/Makefile | 75 +++++++++--- + tests/hwsim/example-hostapd.config | 11 +- + tests/hwsim/example-wpa_supplicant.config | 12 +- + tests/hwsim/test_ap_eap.py | 114 +++++++++++++----- + tests/hwsim/test_ap_ft.py | 4 +- + tests/hwsim/test_authsrv.py | 9 +- + tests/hwsim/test_dpp.py | 19 ++- + tests/hwsim/test_erp.py | 16 +-- + tests/hwsim/test_fils.py | 5 +- + tests/hwsim/test_pmksa_cache.py | 4 +- + tests/hwsim/test_sae.py | 7 ++ + tests/hwsim/test_suite_b.py | 3 + + tests/hwsim/test_wpas_ctrl.py | 2 +- + tests/hwsim/utils.py | 8 +- + tests/test-crypto_module.c | 16 +++ + tests/test-https.c | 12 +- + tests/test-https_server.c | 12 +- + wpa_supplicant/Makefile | 6 + + 22 files changed, 524 insertions(+), 91 deletions(-) + create mode 100644 tests/test-crypto_module.c + +--- a/hostapd/Makefile ++++ b/hostapd/Makefile +@@ -696,6 +696,7 @@ CFLAGS += -DCONFIG_TLSV12 + endif + + ifeq ($(CONFIG_TLS), wolfssl) ++CFLAGS += -DCONFIG_TLS_WOLFSSL + CONFIG_CRYPTO=wolfssl + ifdef TLS_FUNCS + OBJS += ../src/crypto/tls_wolfssl.o +@@ -716,6 +717,7 @@ endif + endif + + ifeq ($(CONFIG_TLS), openssl) ++CFLAGS += -DCONFIG_TLS_OPENSSL + CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 + CONFIG_CRYPTO=openssl + ifdef TLS_FUNCS +@@ -746,6 +748,7 @@ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONF + endif + + ifeq ($(CONFIG_TLS), mbedtls) ++CFLAGS += -DCONFIG_TLS_MBEDTLS + ifndef CONFIG_CRYPTO + CONFIG_CRYPTO=mbedtls + endif +@@ -776,6 +779,7 @@ endif + endif + + ifeq ($(CONFIG_TLS), gnutls) ++CFLAGS += -DCONFIG_TLS_GNUTLS + ifndef CONFIG_CRYPTO + # default to libgcrypt + CONFIG_CRYPTO=gnutls +@@ -806,6 +810,7 @@ endif + endif + + ifeq ($(CONFIG_TLS), internal) ++CFLAGS += -DCONFIG_TLS_INTERNAL + ifndef CONFIG_CRYPTO + CONFIG_CRYPTO=internal + endif +@@ -884,6 +889,7 @@ endif + endif + + ifeq ($(CONFIG_TLS), linux) ++CFLAGS += -DCONFIG_TLS_INTERNAL + OBJS += ../src/crypto/crypto_linux.o + ifdef TLS_FUNCS + OBJS += ../src/crypto/crypto_internal-rsa.o +--- a/src/crypto/Makefile ++++ b/src/crypto/Makefile +@@ -1,10 +1,121 @@ +-CFLAGS += -DCONFIG_CRYPTO_INTERNAL +-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +-CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER + #CFLAGS += -DALL_DH_GROUPS + CFLAGS += -DCONFIG_SHA256 + CFLAGS += -DCONFIG_SHA384 ++CFLAGS += -DCONFIG_HMAC_SHA256_KDF + CFLAGS += -DCONFIG_HMAC_SHA384_KDF ++ ++# crypto_module_tests.c ++CFLAGS += -DCONFIG_MODULE_TESTS ++CFLAGS += -DCONFIG_DPP ++#CFLAGS += -DCONFIG_DPP2 ++#CFLAGS += -DCONFIG_DPP3 ++CFLAGS += -DCONFIG_ECC ++CFLAGS += -DCONFIG_MESH ++CFLAGS += -DEAP_PSK ++CFLAGS += -DEAP_FAST ++ ++ifeq ($(CONFIG_TLS),mbedtls) ++ ++# (enable features for 'cd tests; make run-tests CONFIG_TLS=mbedtls') ++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 ++CFLAGS += -DCONFIG_DES ++CFLAGS += -DEAP_IKEV2 ++CFLAGS += -DEAP_MSCHAPv2 ++CFLAGS += -DEAP_SIM ++ ++LIB_OBJS = tls_mbedtls.o crypto_mbedtls.o ++LIB_OBJS+= \ ++ aes-eax.o \ ++ aes-siv.o \ ++ dh_groups.o \ ++ milenage.o \ ++ ms_funcs.o ++ ++else ++ifeq ($(CONFIG_TLS),openssl) ++ ++# (enable features for 'cd tests; make run-tests CONFIG_TLS=openssl') ++ifndef CONFIG_TLS_DEFAULT_CIPHERS ++CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW" ++endif ++CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\" ++CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 ++CFLAGS += -DEAP_TLS_OPENSSL ++ ++LIB_OBJS = tls_openssl.o fips_prf_openssl.o crypto_openssl.o ++LIB_OBJS+= \ ++ aes-ctr.o \ ++ aes-eax.o \ ++ aes-encblock.o \ ++ aes-siv.o \ ++ dh_groups.o \ ++ milenage.o \ ++ ms_funcs.o \ ++ sha1-prf.o \ ++ sha1-tlsprf.o \ ++ sha1-tprf.o \ ++ sha256-kdf.o \ ++ sha256-prf.o \ ++ sha256-tlsprf.o ++ ++else ++ifeq ($(CONFIG_TLS),wolfssl) ++ ++# (wolfssl libraries must be built with ./configure --enable-wpas) ++# (enable features for 'cd tests; make run-tests CONFIG_TLS=wolfssl') ++CFLAGS += -DWOLFSSL_DER_LOAD ++CFLAGS += -DCONFIG_DES ++ ++LIB_OBJS = tls_wolfssl.o fips_prf_wolfssl.o crypto_wolfssl.o ++LIB_OBJS+= \ ++ aes-ctr.o \ ++ aes-eax.o \ ++ aes-encblock.o \ ++ aes-siv.o \ ++ dh_groups.o \ ++ milenage.o \ ++ ms_funcs.o \ ++ sha1-prf.o \ ++ sha1-tlsprf.o \ ++ sha1-tprf.o \ ++ sha256-kdf.o \ ++ sha256-prf.o \ ++ sha256-tlsprf.o ++ ++else ++ifeq ($(CONFIG_TLS),gnutls) ++ ++# (enable features for 'cd tests; make run-tests CONFIG_TLS=gnutls') ++LIB_OBJS = tls_gnutls.o crypto_gnutls.o ++LIB_OBJS+= \ ++ aes-cbc.o \ ++ aes-ctr.o \ ++ aes-eax.o \ ++ aes-encblock.o \ ++ aes-omac1.o \ ++ aes-siv.o \ ++ aes-unwrap.o \ ++ aes-wrap.o \ ++ dh_group5.o \ ++ dh_groups.o \ ++ milenage.o \ ++ ms_funcs.o \ ++ rc4.o \ ++ sha1-pbkdf2.o \ ++ sha1-prf.o \ ++ fips_prf_internal.o \ ++ sha1-internal.o \ ++ sha1-tlsprf.o \ ++ sha1-tprf.o \ ++ sha256-kdf.o \ ++ sha256-prf.o \ ++ sha256-tlsprf.o ++ ++else ++ ++CFLAGS += -DCONFIG_CRYPTO_INTERNAL ++CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT ++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER + CFLAGS += -DCONFIG_INTERNAL_SHA384 + + LIB_OBJS= \ +@@ -13,7 +124,6 @@ LIB_OBJS= \ + aes-ctr.o \ + aes-eax.o \ + aes-encblock.o \ +- aes-gcm.o \ + aes-internal.o \ + aes-internal-dec.o \ + aes-internal-enc.o \ +@@ -37,6 +147,7 @@ LIB_OBJS= \ + sha1-tlsprf.o \ + sha1-tprf.o \ + sha256.o \ ++ sha256-kdf.o \ + sha256-prf.o \ + sha256-tlsprf.o \ + sha256-internal.o \ +@@ -53,6 +164,16 @@ LIB_OBJS += crypto_internal-modexp.o + LIB_OBJS += crypto_internal-rsa.o + LIB_OBJS += tls_internal.o + LIB_OBJS += fips_prf_internal.o ++ ++endif ++endif ++endif ++endif ++ ++ ++# (used by wlantest/{bip,gcmp,rx_mgmt}.c and tests/test-aes.c) ++LIB_OBJS += aes-gcm.o ++ + ifndef TEST_FUZZ + LIB_OBJS += random.o + endif +--- a/src/crypto/crypto_module_tests.c ++++ b/src/crypto/crypto_module_tests.c +@@ -2469,6 +2469,139 @@ static int test_hpke(void) + } + + ++static int test_ecc(void) ++{ ++#ifdef CONFIG_ECC ++#ifndef CONFIG_TLS_INTERNAL ++#ifndef CONFIG_TLS_GNUTLS ++#if defined(CONFIG_TLS_MBEDTLS) \ ++ || defined(CONFIG_TLS_OPENSSL) \ ++ || defined(CONFIG_TLS_WOLFSSL) ++ wpa_printf(MSG_INFO, "Testing ECC"); ++ /* Note: some tests below are valid on supported Short Weierstrass ++ * curves, but not on Montgomery curves (e.g. IKE groups 31 and 32) ++ * (e.g. deriving and comparing y^2 test below not valid on Montgomery) ++ */ ++#ifdef CONFIG_TLS_MBEDTLS ++ const int grps[] = {19, 20, 21, 25, 26, 28}; ++#endif ++#ifdef CONFIG_TLS_OPENSSL ++ const int grps[] = {19, 20, 21, 26}; ++#endif ++#ifdef CONFIG_TLS_WOLFSSL ++ const int grps[] = {19, 20, 21, 26}; ++#endif ++ uint32_t i; ++ struct crypto_ec *e = NULL; ++ struct crypto_ec_point *p = NULL, *q = NULL; ++ struct crypto_bignum *x = NULL, *y = NULL; ++#ifdef CONFIG_DPP ++ u8 bin[4096]; ++#endif ++ for (i = 0; i < ARRAY_SIZE(grps); ++i) { ++ e = crypto_ec_init(grps[i]); ++ if (e == NULL ++ || crypto_ec_prime_len(e) == 0 ++ || crypto_ec_prime_len_bits(e) == 0 ++ || crypto_ec_order_len(e) == 0 ++ || crypto_ec_get_prime(e) == NULL ++ || crypto_ec_get_order(e) == NULL ++ || crypto_ec_get_a(e) == NULL ++ || crypto_ec_get_b(e) == NULL ++ || crypto_ec_get_generator(e) == NULL) { ++ break; ++ } ++#ifdef CONFIG_DPP ++ struct crypto_ec_key *key = crypto_ec_key_gen(grps[i]); ++ if (key == NULL) ++ break; ++ p = crypto_ec_key_get_public_key(key); ++ q = crypto_ec_key_get_public_key(key); ++ crypto_ec_key_deinit(key); ++ if (p == NULL || q == NULL) ++ break; ++ if (!crypto_ec_point_is_on_curve(e, p)) ++ break; ++ ++ /* inverted point should not match original; ++ * double-invert should match */ ++ if (crypto_ec_point_invert(e, q) != 0 ++ || crypto_ec_point_cmp(e, p, q) == 0 ++ || crypto_ec_point_invert(e, q) != 0 ++ || crypto_ec_point_cmp(e, p, q) != 0) { ++ break; ++ } ++ ++ /* crypto_ec_point_to_bin() and crypto_ec_point_from_bin() ++ * imbalanced interfaces? */ ++ size_t prime_len = crypto_ec_prime_len(e); ++ if (prime_len * 2 > sizeof(bin)) ++ break; ++ if (crypto_ec_point_to_bin(e, p, bin, bin+prime_len) != 0) ++ break; ++ struct crypto_ec_point *tmp = crypto_ec_point_from_bin(e, bin); ++ if (tmp == NULL) ++ break; ++ if (crypto_ec_point_cmp(e, p, tmp) != 0) { ++ crypto_ec_point_deinit(tmp, 0); ++ break; ++ } ++ crypto_ec_point_deinit(tmp, 0); ++ ++ x = crypto_bignum_init(); ++ y = crypto_bignum_init_set(bin+prime_len, prime_len); ++ if (x == NULL || y == NULL || crypto_ec_point_x(e, p, x) != 0) ++ break; ++ struct crypto_bignum *y2 = crypto_ec_point_compute_y_sqr(e, x); ++ if (y2 == NULL) ++ break; ++ if (crypto_bignum_sqrmod(y, crypto_ec_get_prime(e), y) != 0 ++ || crypto_bignum_cmp(y, y2) != 0) { ++ crypto_bignum_deinit(y2, 0); ++ break; ++ } ++ crypto_bignum_deinit(y2, 0); ++ crypto_bignum_deinit(x, 0); ++ crypto_bignum_deinit(y, 0); ++ x = NULL; ++ y = NULL; ++ ++ x = crypto_bignum_init(); ++ if (x == NULL) ++ break; ++ if (crypto_bignum_rand(x, crypto_ec_get_prime(e)) != 0) ++ break; ++ crypto_bignum_deinit(x, 0); ++ x = NULL; ++ ++ crypto_ec_point_deinit(p, 0); ++ p = NULL; ++ crypto_ec_point_deinit(q, 0); ++ q = NULL; ++#endif /* CONFIG_DPP */ ++ crypto_ec_deinit(e); ++ e = NULL; ++ } ++ if (i != ARRAY_SIZE(grps)) { ++ crypto_bignum_deinit(x, 0); ++ crypto_bignum_deinit(y, 0); ++ crypto_ec_point_deinit(p, 0); ++ crypto_ec_point_deinit(q, 0); ++ crypto_ec_deinit(e); ++ wpa_printf(MSG_INFO, ++ "ECC test case failed tls_id:%d", grps[i]); ++ return -1; ++ } ++ ++ wpa_printf(MSG_INFO, "ECC test cases passed"); ++#endif ++#endif /* !CONFIG_TLS_GNUTLS */ ++#endif /* !CONFIG_TLS_INTERNAL */ ++#endif /* CONFIG_ECC */ ++ return 0; ++} ++ ++ + static int test_ms_funcs(void) + { + #ifndef CONFIG_FIPS +@@ -2590,6 +2723,7 @@ int crypto_module_tests(void) + test_fips186_2_prf() || + test_extract_expand_hkdf() || + test_hpke() || ++ test_ecc() || + test_ms_funcs()) + ret = -1; + +--- a/src/tls/Makefile ++++ b/src/tls/Makefile +@@ -1,3 +1,10 @@ ++LIB_OBJS= asn1.o ++ ++ifneq ($(CONFIG_TLS),gnutls) ++ifneq ($(CONFIG_TLS),mbedtls) ++ifneq ($(CONFIG_TLS),openssl) ++ifneq ($(CONFIG_TLS),wolfssl) ++ + CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH + CFLAGS += -DCONFIG_CRYPTO_INTERNAL + CFLAGS += -DCONFIG_TLSV11 +@@ -21,5 +28,9 @@ LIB_OBJS= \ + tlsv1_server_read.o \ + tlsv1_server_write.o \ + x509v3.o ++endif ++endif ++endif ++endif + + include ../lib.rules +--- a/tests/Makefile ++++ b/tests/Makefile +@@ -1,8 +1,10 @@ +-ALL=test-base64 test-md4 test-milenage \ +- test-rsa-sig-ver \ +- test-sha1 \ +- test-https test-https_server \ +- test-sha256 test-aes test-x509v3 test-list test-rc4 ++RUN_TESTS= \ ++ test-list \ ++ test-md4 test-rc4 test-sha1 test-sha256 \ ++ test-milenage test-aes \ ++ test-crypto_module ++ ++ALL=$(RUN_TESTS) test-base64 test-https test-https_server + + include ../src/build.rules + +@@ -24,13 +26,27 @@ CFLAGS += -DCONFIG_IEEE80211R_AP + CFLAGS += -DCONFIG_IEEE80211R + CFLAGS += -DCONFIG_TDLS + ++# test-crypto_module ++CFLAGS += -DCONFIG_MODULE_TESTS ++CFLAGS += -DCONFIG_DPP ++#CFLAGS += -DCONFIG_DPP2 ++#CFLAGS += -DCONFIG_DPP3 ++CFLAGS += -DCONFIG_ECC ++CFLAGS += -DCONFIG_HMAC_SHA256_KDF ++CFLAGS += -DCONFIG_HMAC_SHA384_KDF ++CFLAGS += -DCONFIG_MESH ++CFLAGS += -DCONFIG_SHA256 ++CFLAGS += -DCONFIG_SHA384 ++CFLAGS += -DEAP_PSK ++CFLAGS += -DEAP_FAST ++ + CFLAGS += -I../src + CFLAGS += -I../src/utils + + SLIBS = ../src/utils/libutils.a + +-DLIBS = ../src/crypto/libcrypto.a \ +- ../src/tls/libtls.a ++DLIBS = ../src/tls/libtls.a \ ++ ../src/crypto/libcrypto.a + + _OBJS_VAR := LLIBS + include ../src/objs.mk +@@ -42,12 +58,43 @@ include ../src/objs.mk + LIBS = $(SLIBS) $(DLIBS) + LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS) + ++ifeq ($(CONFIG_TLS),mbedtls) ++CFLAGS += -DCONFIG_TLS_MBEDTLS ++LLIBS += -lmbedtls -lmbedx509 -lmbedcrypto ++else ++ifeq ($(CONFIG_TLS),openssl) ++CFLAGS += -DCONFIG_TLS_OPENSSL ++LLIBS += -lssl -lcrypto ++else ++ifeq ($(CONFIG_TLS),gnutls) ++CFLAGS += -DCONFIG_TLS_GNUTLS ++LLIBS += -lgnutls -lgpg-error -lgcrypt ++else ++ifeq ($(CONFIG_TLS),wolfssl) ++CFLAGS += -DCONFIG_TLS_WOLFSSL ++LLIBS += -lwolfssl -lm ++else ++CFLAGS += -DCONFIG_TLS_INTERNAL ++CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER ++ALL += test-rsa-sig-ver ++ALL += test-x509v3 ++clean-config_tls_internal: ++ rm -f test_x509v3_nist.out.* ++ rm -f test_x509v3_nist2.out.* ++endif ++endif ++endif ++endif ++ + # glibc < 2.17 needs -lrt for clock_gettime() + LLIBS += -lrt + + test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS) + $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS) + ++test-crypto_module: $(call BUILDOBJ,test-crypto_module.o) $(LIBS) ++ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS) ++ + test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS) + $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS) + +@@ -83,17 +130,11 @@ test-x509v3: $(call BUILDOBJ,test-x509v3 + + + run-tests: $(ALL) +- ./test-aes +- ./test-list +- ./test-md4 +- ./test-milenage +- ./test-rsa-sig-ver +- ./test-sha1 +- ./test-sha256 ++ @set -ex; for i in $(RUN_TESTS); do ./$$i; done + @echo + @echo All tests completed successfully. + +-clean: common-clean ++clean: common-clean clean-config_tls_internal + rm -f *~ +- rm -f test_x509v3_nist.out.* +- rm -f test_x509v3_nist2.out.* ++ ++.PHONY: run-tests clean-config_tls_internal +--- a/tests/hwsim/example-hostapd.config ++++ b/tests/hwsim/example-hostapd.config +@@ -34,15 +34,7 @@ CONFIG_EAP_TNC=y + CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\" + LIBS += -rdynamic + CONFIG_EAP_UNAUTH_TLS=y +-ifeq ($(CONFIG_TLS), openssl) +-CONFIG_EAP_PWD=y +-endif +-ifeq ($(CONFIG_TLS), wolfssl) +-CONFIG_EAP_PWD=y +-endif +-ifeq ($(CONFIG_TLS), mbedtls) +-CONFIG_EAP_PWD=y +-endif ++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,) + CONFIG_EAP_EKE=y + CONFIG_PKCS12=y + CONFIG_RADIUS_SERVER=y +@@ -89,6 +81,7 @@ CFLAGS += -DCONFIG_RADIUS_TEST + CONFIG_MODULE_TESTS=y + + CONFIG_SUITEB=y ++CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,) + + # AddressSanitizer (ASan) can be enabled by uncommenting the following lines. + # This can be used as a more efficient memory error detector than valgrind +--- a/tests/hwsim/example-wpa_supplicant.config ++++ b/tests/hwsim/example-wpa_supplicant.config +@@ -35,16 +35,7 @@ LIBS += -rdynamic + CONFIG_EAP_FAST=y + CONFIG_EAP_TEAP=y + CONFIG_EAP_IKEV2=y +- +-ifeq ($(CONFIG_TLS), openssl) +-CONFIG_EAP_PWD=y +-endif +-ifeq ($(CONFIG_TLS), wolfssl) +-CONFIG_EAP_PWD=y +-endif +-ifeq ($(CONFIG_TLS), mbedtls) +-CONFIG_EAP_PWD=y +-endif ++CONFIG_EAP_PWD=$(if $(filter openssl wolfssl mbedtls,$(CONFIG_TLS)),y,) + + CONFIG_USIM_SIMULATOR=y + CONFIG_SIM_SIMULATOR=y +@@ -137,6 +128,7 @@ CONFIG_TESTING_OPTIONS=y + CONFIG_MODULE_TESTS=y + + CONFIG_SUITEB=y ++CONFIG_SUITEB192=$(if $(filter openssl mbedtls,$(CONFIG_TLS)),y,) + + # AddressSanitizer (ASan) can be enabled by uncommenting the following lines. + # This can be used as a more efficient memory error detector than valgrind +--- a/tests/hwsim/test_ap_eap.py ++++ b/tests/hwsim/test_ap_eap.py +@@ -42,20 +42,42 @@ def check_eap_capa(dev, method): + res = dev.get_capability("eap") + if method not in res: + raise HwsimSkip("EAP method %s not supported in the build" % method) ++ if method == "FAST" or method == "TEAP": ++ tls = dev.request("GET tls_library") ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("EAP-%s not supported with this TLS library: " % method + tls) + + def check_subject_match_support(dev): + tls = dev.request("GET tls_library") +- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"): ++ if tls.startswith("OpenSSL"): ++ return ++ elif tls.startswith("wolfSSL"): ++ return ++ elif tls.startswith("mbed TLS"): ++ return ++ else: + raise HwsimSkip("subject_match not supported with this TLS library: " + tls) + + def check_check_cert_subject_support(dev): + tls = dev.request("GET tls_library") +- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"): ++ if tls.startswith("OpenSSL"): ++ return ++ elif tls.startswith("wolfSSL"): ++ return ++ elif tls.startswith("mbed TLS"): ++ return ++ else: + raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls) + + def check_altsubject_match_support(dev): + tls = dev.request("GET tls_library") +- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"): ++ if tls.startswith("OpenSSL"): ++ return ++ elif tls.startswith("wolfSSL"): ++ return ++ elif tls.startswith("mbed TLS"): ++ return ++ else: + raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls) + + def check_domain_match(dev): +@@ -70,7 +92,13 @@ def check_domain_suffix_match(dev): + + def check_domain_match_full(dev): + tls = dev.request("GET tls_library") +- if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"): ++ if tls.startswith("OpenSSL"): ++ return ++ elif tls.startswith("wolfSSL"): ++ return ++ elif tls.startswith("mbed TLS"): ++ return ++ else: + raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls) + + def check_cert_probe_support(dev): +@@ -79,8 +107,15 @@ def check_cert_probe_support(dev): + raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls) + + def check_ext_cert_check_support(dev): ++ if not openssl_imported: ++ raise HwsimSkip("OpenSSL python method not available") ++ + tls = dev.request("GET tls_library") +- if not tls.startswith("OpenSSL"): ++ if tls.startswith("OpenSSL"): ++ return ++ elif tls.startswith("mbed TLS"): ++ return ++ else: + raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls) + + def check_ocsp_support(dev): +@@ -91,14 +126,18 @@ def check_ocsp_support(dev): + # raise HwsimSkip("OCSP not supported with this TLS library: " + tls) + #if tls.startswith("wolfSSL"): + # raise HwsimSkip("OCSP not supported with this TLS library: " + tls) ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("OCSP not supported with this TLS library: " + tls) + + def check_pkcs5_v15_support(dev): + tls = dev.request("GET tls_library") +- if "BoringSSL" in tls or "GnuTLS" in tls: ++ if "BoringSSL" in tls or "GnuTLS" in tls or "mbed TLS" in tls: + raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls) + + def check_tls13_support(dev): + tls = dev.request("GET tls_library") ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("TLS v1.3 not supported") + if "run=OpenSSL 1.1.1" not in tls and "run=OpenSSL 3.0" not in tls and "wolfSSL" not in tls: + raise HwsimSkip("TLS v1.3 not supported") + +@@ -118,11 +157,15 @@ def check_pkcs12_support(dev): + # raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls) + if tls.startswith("wolfSSL"): + raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls) ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls) + + def check_dh_dsa_support(dev): + tls = dev.request("GET tls_library") + if tls.startswith("internal"): + raise HwsimSkip("DH DSA not supported with this TLS library: " + tls) ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls) + + def check_ec_support(dev): + tls = dev.request("GET tls_library") +@@ -1625,7 +1668,7 @@ def test_ap_wpa2_eap_ttls_pap_subject_ma + eap_connect(dev[0], hapd, "TTLS", "pap user", + anonymous_identity="ttls", password="password", + ca_cert="auth_serv/ca.pem", phase2="auth=PAP", +- subject_match="/C=FI/O=w1.fi/CN=server.w1.fi", ++ check_cert_subject="/C=FI/O=w1.fi/CN=server.w1.fi", + altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/") + eap_reauth(dev[0], "TTLS") + +@@ -2860,6 +2903,7 @@ def test_ap_wpa2_eap_tls_neg_domain_matc + + def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev): + """WPA2-Enterprise negative test - subject mismatch""" ++ check_subject_match_support(dev[0]) + params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") + hostapd.add_ap(apdev[0], params) + dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS", +@@ -2920,6 +2964,7 @@ def test_ap_wpa2_eap_tls_neg_subject_mat + + def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev): + """WPA2-Enterprise negative test - altsubject mismatch""" ++ check_altsubject_match_support(dev[0]) + params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") + hostapd.add_ap(apdev[0], params) + +@@ -3460,7 +3505,7 @@ def test_ap_wpa2_eap_ikev2_oom(dev, apde + dev[0].request("REMOVE_NETWORK all") + + tls = dev[0].request("GET tls_library") +- if not tls.startswith("wolfSSL"): ++ if not tls.startswith("wolfSSL") and not tls.startswith("mbed TLS"): + tests = [(1, "os_get_random;dh_init")] + else: + tests = [(1, "crypto_dh_init;dh_init")] +@@ -4774,7 +4819,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca + params["private_key"] = "auth_serv/iCA-server/server.key" + hostapd.add_ap(apdev[0], params) + tls = dev[0].request("GET tls_library") +- if "GnuTLS" in tls or "wolfSSL" in tls: ++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls: + ca_cert = "auth_serv/iCA-user/ca-and-root.pem" + client_cert = "auth_serv/iCA-user/user_and_ica.pem" + else: +@@ -4840,6 +4885,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca + run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1") + + def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md): ++ check_ocsp_support(dev[0]) + params = int_eap_server_params() + params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem" + params["server_cert"] = "auth_serv/iCA-server/server.pem" +@@ -4849,7 +4895,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ + try: + hostapd.add_ap(apdev[0], params) + tls = dev[0].request("GET tls_library") +- if "GnuTLS" in tls or "wolfSSL" in tls: ++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls: + ca_cert = "auth_serv/iCA-user/ca-and-root.pem" + client_cert = "auth_serv/iCA-user/user_and_ica.pem" + else: +@@ -4885,7 +4931,7 @@ def run_ap_wpa2_eap_tls_intermediate_ca_ + try: + hostapd.add_ap(apdev[0], params) + tls = dev[0].request("GET tls_library") +- if "GnuTLS" in tls or "wolfSSL" in tls: ++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls: + ca_cert = "auth_serv/iCA-user/ca-and-root.pem" + client_cert = "auth_serv/iCA-user/user_and_ica.pem" + else: +@@ -4935,7 +4981,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca + try: + hostapd.add_ap(apdev[0], params) + tls = dev[0].request("GET tls_library") +- if "GnuTLS" in tls or "wolfSSL" in tls: ++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls: + ca_cert = "auth_serv/iCA-user/ca-and-root.pem" + client_cert = "auth_serv/iCA-user/user_and_ica.pem" + else: +@@ -5002,7 +5048,7 @@ def test_ap_wpa2_eap_tls_intermediate_ca + + hostapd.add_ap(apdev[0], params) + tls = dev[0].request("GET tls_library") +- if "GnuTLS" in tls or "wolfSSL" in tls: ++ if "GnuTLS" in tls or "wolfSSL" in tls or "mbed TLS" in tls: + ca_cert = "auth_serv/iCA-user/ca-and-root.pem" + client_cert = "auth_serv/iCA-user/user_and_ica.pem" + else: +@@ -5260,6 +5306,7 @@ def test_ap_wpa2_eap_ttls_server_cert_ek + + def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev): + """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file""" ++ check_pkcs12_support(dev[0]) + skip_with_fips(dev[0]) + params = int_eap_server_params() + del params["server_cert"] +@@ -5272,6 +5319,7 @@ def test_ap_wpa2_eap_ttls_server_pkcs12( + + def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev): + """EAP-TTLS and server PKCS#12 file with extra certs""" ++ check_pkcs12_support(dev[0]) + skip_with_fips(dev[0]) + params = int_eap_server_params() + del params["server_cert"] +@@ -5294,6 +5342,7 @@ def test_ap_wpa2_eap_ttls_dh_params_serv + + def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev): + """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)""" ++ check_dh_dsa_support(dev[0]) + params = int_eap_server_params() + params["dh_file"] = "auth_serv/dsaparam.pem" + hapd = hostapd.add_ap(apdev[0], params) +@@ -5605,8 +5654,8 @@ def test_ap_wpa2_eap_non_ascii_identity2 + def test_openssl_cipher_suite_config_wpas(dev, apdev): + """OpenSSL cipher suite configuration on wpa_supplicant""" + tls = dev[0].request("GET tls_library") +- if not tls.startswith("OpenSSL"): +- raise HwsimSkip("TLS library is not OpenSSL: " + tls) ++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"): ++ raise HwsimSkip("TLS library is not OpenSSL or mbed TLS: " + tls) + params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") + hapd = hostapd.add_ap(apdev[0], params) + eap_connect(dev[0], hapd, "TTLS", "pap user", +@@ -5632,14 +5681,14 @@ def test_openssl_cipher_suite_config_wpa + def test_openssl_cipher_suite_config_hapd(dev, apdev): + """OpenSSL cipher suite configuration on hostapd""" + tls = dev[0].request("GET tls_library") +- if not tls.startswith("OpenSSL"): +- raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls) ++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"): ++ raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL or mbed TLS: " + tls) + params = int_eap_server_params() + params['openssl_ciphers'] = "AES256" + hapd = hostapd.add_ap(apdev[0], params) + tls = hapd.request("GET tls_library") +- if not tls.startswith("OpenSSL"): +- raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls) ++ if not tls.startswith("OpenSSL") and not tls.startswith("mbed TLS"): ++ raise HwsimSkip("hostapd TLS library is not OpenSSL or mbed TLS: " + tls) + eap_connect(dev[0], hapd, "TTLS", "pap user", + anonymous_identity="ttls", password="password", + ca_cert="auth_serv/ca.pem", phase2="auth=PAP") +@@ -6081,13 +6130,17 @@ def test_ap_wpa2_eap_tls_versions(dev, a + check_tls_ver(dev[0], hapd, + "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", + "TLSv1.2") +- elif tls.startswith("internal"): ++ elif tls.startswith("internal") or tls.startswith("mbed TLS"): + check_tls_ver(dev[0], hapd, + "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2") +- check_tls_ver(dev[1], hapd, +- "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1") +- check_tls_ver(dev[2], hapd, +- "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1") ++ if tls.startswith("mbed TLS"): ++ check_tls_ver(dev[2], hapd, ++ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1.0") ++ else: ++ check_tls_ver(dev[1], hapd, ++ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1") ++ check_tls_ver(dev[2], hapd, ++ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1") + if "run=OpenSSL 1.1.1" in tls or "run=OpenSSL 3.0" in tls: + check_tls_ver(dev[0], hapd, + "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3") +@@ -6109,6 +6162,11 @@ def test_ap_wpa2_eap_tls_versions_server + tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"), + ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"), + ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")] ++ tls = dev[0].request("GET tls_library") ++ if tls.startswith("mbed TLS"): ++ tests = [#("TLSv1.0", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"), ++ #("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"), ++ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")] + for exp, flags in tests: + hapd.disable() + hapd.set("tls_flags", flags) +@@ -7145,6 +7203,7 @@ def test_ap_wpa2_eap_assoc_rsn(dev, apde + def test_eap_tls_ext_cert_check(dev, apdev): + """EAP-TLS and external server certification validation""" + # With internal server certificate chain validation ++ check_ext_cert_check_support(dev[0]) + id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS", + identity="tls user", + ca_cert="auth_serv/ca.pem", +@@ -7157,6 +7216,7 @@ def test_eap_tls_ext_cert_check(dev, apd + def test_eap_ttls_ext_cert_check(dev, apdev): + """EAP-TTLS and external server certification validation""" + # Without internal server certificate chain validation ++ check_ext_cert_check_support(dev[0]) + id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS", + identity="pap user", anonymous_identity="ttls", + password="password", phase2="auth=PAP", +@@ -7167,6 +7227,7 @@ def test_eap_ttls_ext_cert_check(dev, ap + def test_eap_peap_ext_cert_check(dev, apdev): + """EAP-PEAP and external server certification validation""" + # With internal server certificate chain validation ++ check_ext_cert_check_support(dev[0]) + id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP", + identity="user", anonymous_identity="peap", + ca_cert="auth_serv/ca.pem", +@@ -7177,6 +7238,7 @@ def test_eap_peap_ext_cert_check(dev, ap + + def test_eap_fast_ext_cert_check(dev, apdev): + """EAP-FAST and external server certification validation""" ++ check_ext_cert_check_support(dev[0]) + check_eap_capa(dev[0], "FAST") + # With internal server certificate chain validation + dev[0].request("SET blob fast_pac_auth_ext ") +@@ -7191,10 +7253,6 @@ def test_eap_fast_ext_cert_check(dev, ap + run_ext_cert_check(dev, apdev, id) + + def run_ext_cert_check(dev, apdev, net_id): +- check_ext_cert_check_support(dev[0]) +- if not openssl_imported: +- raise HwsimSkip("OpenSSL python method not available") +- + params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap") + hapd = hostapd.add_ap(apdev[0], params) + +--- a/tests/hwsim/test_ap_ft.py ++++ b/tests/hwsim/test_ap_ft.py +@@ -2347,11 +2347,11 @@ def test_ap_ft_ap_oom5(dev, apdev): + # This will fail to roam + dev[0].roam(bssid1, check_bssid=False) + +- with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"): ++ with fail_test(hapd1, 1, "sha256_prf;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"): + # This will fail to roam + dev[0].roam(bssid1, check_bssid=False) + +- with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"): ++ with fail_test(hapd1, 2, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"): + # This will fail to roam + dev[0].roam(bssid1, check_bssid=False) + +--- a/tests/hwsim/test_authsrv.py ++++ b/tests/hwsim/test_authsrv.py +@@ -156,9 +156,12 @@ def test_authsrv_oom(dev, apdev): + if "FAIL" not in authsrv.request("ENABLE"): + raise Exception("ENABLE succeeded during OOM") + +- with alloc_fail(authsrv, 1, "tls_init;authsrv_init"): +- if "FAIL" not in authsrv.request("ENABLE"): +- raise Exception("ENABLE succeeded during OOM") ++ # tls_mbedtls.c:tls_init() does not alloc memory (no alloc fail trigger) ++ tls = dev[0].request("GET tls_library") ++ if not tls.startswith("mbed TLS"): ++ with alloc_fail(authsrv, 1, "tls_init;authsrv_init"): ++ if "FAIL" not in authsrv.request("ENABLE"): ++ raise Exception("ENABLE succeeded during OOM") + + for count in range(1, 3): + with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"): +--- a/tests/hwsim/test_dpp.py ++++ b/tests/hwsim/test_dpp.py +@@ -39,7 +39,8 @@ def check_dpp_capab(dev, brainpool=False + raise HwsimSkip("DPP not supported") + if brainpool: + tls = dev.request("GET tls_library") +- if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL"): ++ if (not tls.startswith("OpenSSL") or "run=BoringSSL" in tls) and not tls.startswith("wolfSSL") \ ++ and not tls.startswith("mbed TLS"): + raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls) + capa = dev.request("GET_CAPABILITY dpp") + ver = 1 +@@ -3621,6 +3622,9 @@ def test_dpp_proto_auth_req_no_i_proto_k + + def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev): + """DPP protocol testing - invalid I-proto key in Auth Req""" ++ tls = dev[0].request("GET tls_library") ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response") + run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key") + + def test_dpp_proto_auth_req_no_i_nonce(dev, apdev): +@@ -3716,7 +3720,12 @@ def test_dpp_proto_auth_resp_no_r_proto_ + + def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev): + """DPP protocol testing - invalid R-Proto Key in Auth Resp""" +- run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key") ++ tls = dev[0].request("GET tls_library") ++ if tls.startswith("mbed TLS"): ++ # mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key ++ run_dpp_proto_auth_resp_missing(dev, 67, "Failed to derive ECDH shared secret") ++ else: ++ run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key") + + def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev): + """DPP protocol testing - no R-nonce in Auth Resp""" +@@ -4078,11 +4087,17 @@ def test_dpp_proto_pkex_exchange_resp_in + + def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev): + """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request""" ++ tls = dev[0].request("GET tls_library") ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response") + run_dpp_proto_pkex_req_missing(dev, 47, + "Peer bootstrapping key is invalid") + + def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev): + """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response""" ++ tls = dev[0].request("GET tls_library") ++ if tls.startswith("mbed TLS"): ++ raise HwsimSkip("mbed TLS crypto_ecdh_set_peerkey() properly detects invalid key; no response") + run_dpp_proto_pkex_resp_missing(dev, 48, + "Peer bootstrapping key is invalid") + +--- a/tests/hwsim/test_erp.py ++++ b/tests/hwsim/test_erp.py +@@ -12,7 +12,7 @@ import time + + import hostapd + from utils import * +-from test_ap_eap import int_eap_server_params, check_tls13_support ++from test_ap_eap import int_eap_server_params, check_tls13_support, check_eap_capa + from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations + + def test_erp_initiate_reauth_start(dev, apdev): +@@ -276,6 +276,7 @@ def test_erp_radius_eap_methods(dev, apd + params['erp_domain'] = 'example.com' + params['disable_pmksa_caching'] = '1' + hapd = hostapd.add_ap(apdev[0], params) ++ tls = dev[0].request("GET tls_library") + + erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com", + password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123") +@@ -289,7 +290,7 @@ def test_erp_radius_eap_methods(dev, apd + password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123") + erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com", + password="hello") +- if "FAST" in eap_methods: ++ if "FAST" in eap_methods and check_eap_capa(dev[0], "FAST"): + erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com", + password="password", ca_cert="auth_serv/ca.pem", + phase2="auth=GTC", +@@ -301,13 +302,14 @@ def test_erp_radius_eap_methods(dev, apd + password="password") + erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com", + password_hex="0123456789abcdef0123456789abcdef") +- if "MSCHAPV2" in eap_methods: ++ if "MSCHAPV2" in eap_methods and check_eap_capa(dev[0], "MSCHAPV2"): + erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com", + password="password", ca_cert="auth_serv/ca.pem", + phase2="auth=MSCHAPV2") +- erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com", +- password="password", ca_cert="auth_serv/ca.pem", +- phase2="auth=MSCHAPV2", pac_file="blob://teap_pac") ++ if check_eap_capa(dev[0], "TEAP"): ++ erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com", ++ password="password", ca_cert="auth_serv/ca.pem", ++ phase2="auth=MSCHAPV2", pac_file="blob://teap_pac") + erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com", + password_hex="0123456789abcdef0123456789abcdef") + if "PWD" in eap_methods: +@@ -640,7 +642,7 @@ def test_erp_local_errors(dev, apdev): + dev[0].request("REMOVE_NETWORK all") + dev[0].wait_disconnected() + +- for count in range(1, 6): ++ for count in range(1, 4): + dev[0].request("ERP_FLUSH") + with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"): + dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS", +--- a/tests/hwsim/test_fils.py ++++ b/tests/hwsim/test_fils.py +@@ -1422,7 +1422,10 @@ def run_fils_sk_pfs(dev, apdev, group, p + check_erp_capa(dev[0]) + + tls = dev[0].request("GET tls_library") +- if not tls.startswith("wolfSSL"): ++ if tls.startswith("mbed TLS"): ++ if int(group) == 27: ++ raise HwsimSkip("Brainpool EC group 27 not supported by mbed TLS") ++ elif not tls.startswith("wolfSSL"): + if int(group) in [25]: + if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls or "build=OpenSSL 3.0" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls or "run=OpenSSL 3.0" in tls)): + raise HwsimSkip("EC group not supported") +--- a/tests/hwsim/test_pmksa_cache.py ++++ b/tests/hwsim/test_pmksa_cache.py +@@ -954,7 +954,7 @@ def test_pmksa_cache_preauth_wpas_oom(de + eap_connect(dev[0], hapd, "PAX", "pax.user@example.com", + password_hex="0123456789abcdef0123456789abcdef", + bssid=apdev[0]['bssid']) +- for i in range(1, 11): ++ for i in range(1, 10): + with alloc_fail(dev[0], i, "rsn_preauth_init"): + res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip() + logger.info("Iteration %d - PREAUTH command results: %s" % (i, res)) +@@ -962,7 +962,7 @@ def test_pmksa_cache_preauth_wpas_oom(de + state = dev[0].request('GET_ALLOC_FAIL') + if state.startswith('0:'): + break +- time.sleep(0.05) ++ time.sleep(0.10) + + def test_pmksa_cache_ctrl(dev, apdev): + """PMKSA cache control interface operations""" +--- a/tests/hwsim/test_sae.py ++++ b/tests/hwsim/test_sae.py +@@ -177,6 +177,11 @@ def test_sae_groups(dev, apdev): + if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls: + logger.info("Add Brainpool EC groups since OpenSSL is new enough") + sae_groups += [27, 28, 29, 30] ++ if tls.startswith("mbed TLS"): ++ # secp224k1 and secp224r1 (26) have prime p = 1 mod 4, and mbedtls ++ # does not have code to derive y from compressed format for those curves ++ sae_groups = [19, 25, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24] ++ sae_groups += [27, 28, 29, 30] + heavy_groups = [14, 15, 16] + suitable_groups = [15, 16, 17, 18, 19, 20, 21] + groups = [str(g) for g in sae_groups] +@@ -2122,6 +2127,8 @@ def run_sae_pwe_group(dev, apdev, group) + logger.info("Add Brainpool EC groups since OpenSSL is new enough") + elif tls.startswith("wolfSSL"): + logger.info("Make sure Brainpool EC groups were enabled when compiling wolfSSL") ++ elif tls.startswith("mbed TLS"): ++ logger.info("Make sure Brainpool EC groups were enabled when compiling mbed TLS") + else: + raise HwsimSkip("Brainpool curve not supported") + start_sae_pwe_ap(apdev[0], group, 2) +--- a/tests/hwsim/test_suite_b.py ++++ b/tests/hwsim/test_suite_b.py +@@ -26,6 +26,8 @@ def check_suite_b_tls_lib(dev, dhe=False + return + if tls.startswith("wolfSSL"): + return ++ if tls.startswith("mbed TLS"): ++ return + if not tls.startswith("OpenSSL"): + raise HwsimSkip("TLS library not supported for Suite B: " + tls) + supported = False +@@ -499,6 +501,7 @@ def test_suite_b_192_rsa_insufficient_dh + + dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192", + ieee80211w="2", ++ openssl_ciphers="DHE-RSA-AES256-GCM-SHA384", + phase1="tls_suiteb=1", + eap="TLS", identity="tls user", + ca_cert="auth_serv/rsa3072-ca.pem", +--- a/tests/hwsim/test_wpas_ctrl.py ++++ b/tests/hwsim/test_wpas_ctrl.py +@@ -1834,7 +1834,7 @@ def _test_wpas_ctrl_oom(dev): + tls = dev[0].request("GET tls_library") + if not tls.startswith("internal"): + tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL', +- 4, 'wpas_ctrl_nfc_get_handover_sel_p2p')) ++ 3, 'wpas_ctrl_nfc_get_handover_sel_p2p')) + for cmd, exp, count, func in tests: + with alloc_fail(dev[0], count, func): + res = dev[0].request(cmd) +--- a/tests/hwsim/utils.py ++++ b/tests/hwsim/utils.py +@@ -135,7 +135,13 @@ def check_fils_sk_pfs_capa(dev): + + def check_tls_tod(dev): + tls = dev.request("GET tls_library") +- if not tls.startswith("OpenSSL") and not tls.startswith("internal"): ++ if tls.startswith("OpenSSL"): ++ return ++ elif tls.startswith("internal"): ++ return ++ elif tls.startswith("mbed TLS"): ++ return ++ else: + raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls) + + def vht_supported(): +--- /dev/null ++++ b/tests/test-crypto_module.c +@@ -0,0 +1,16 @@ ++/* ++ * crypto module tests - test program ++ * Copyright (c) 2022, Glenn Strauss ++ * ++ * This software may be distributed under the terms of the BSD license. ++ * See README for more details. ++ */ ++ ++#include "utils/includes.h" ++#include "utils/module_tests.h" ++#include "crypto/crypto_module_tests.c" ++ ++int main(int argc, char *argv[]) ++{ ++ return crypto_module_tests(); ++} +--- a/tests/test-https.c ++++ b/tests/test-https.c +@@ -75,7 +75,7 @@ static int https_client(int s, const cha + struct tls_connection *conn; + struct wpabuf *in, *out, *appl; + int res = -1; +- int need_more_data; ++ int need_more_data = 0; + + os_memset(&conf, 0, sizeof(conf)); + conf.event_cb = https_tls_event_cb; +@@ -93,8 +93,12 @@ static int https_client(int s, const cha + + for (;;) { + appl = NULL; ++#ifdef CONFIG_TLS_INTERNAL_SERVER + out = tls_connection_handshake2(tls, conn, in, &appl, + &need_more_data); ++#else ++ out = tls_connection_handshake(tls, conn, in, &appl); ++#endif + wpabuf_free(in); + in = NULL; + if (out == NULL) { +@@ -152,11 +156,15 @@ static int https_client(int s, const cha + + wpa_printf(MSG_INFO, "Reading HTTP response"); + for (;;) { +- int need_more_data; ++ int need_more_data = 0; + in = https_recv(s); + if (in == NULL) + goto done; ++#ifdef CONFIG_TLS_INTERNAL_SERVER + out = tls_connection_decrypt2(tls, conn, in, &need_more_data); ++#else ++ out = tls_connection_decrypt(tls, conn, in); ++#endif + if (need_more_data) + wpa_printf(MSG_DEBUG, "HTTP: Need more data"); + wpabuf_free(in); +--- a/tests/test-https_server.c ++++ b/tests/test-https_server.c +@@ -67,10 +67,12 @@ static struct wpabuf * https_recv(int s, + } + + ++#ifdef CONFIG_TLS_INTERNAL_SERVER + static void https_tls_log_cb(void *ctx, const char *msg) + { + wpa_printf(MSG_DEBUG, "TLS: %s", msg); + } ++#endif + + + static int https_server(int s) +@@ -79,7 +81,7 @@ static int https_server(int s) + void *tls; + struct tls_connection_params params; + struct tls_connection *conn; +- struct wpabuf *in, *out, *appl; ++ struct wpabuf *in = NULL, *out = NULL, *appl = NULL; + int res = -1; + + os_memset(&conf, 0, sizeof(conf)); +@@ -106,7 +108,9 @@ static int https_server(int s) + return -1; + } + ++#ifdef CONFIG_TLS_INTERNAL_SERVER + tls_connection_set_log_cb(conn, https_tls_log_cb, NULL); ++#endif + + for (;;) { + in = https_recv(s, 5000); +@@ -147,12 +151,16 @@ static int https_server(int s) + + wpa_printf(MSG_INFO, "Reading HTTP request"); + for (;;) { +- int need_more_data; ++ int need_more_data = 0; + + in = https_recv(s, 5000); + if (!in) + goto done; ++#ifdef CONFIG_TLS_INTERNAL_SERVER + out = tls_connection_decrypt2(tls, conn, in, &need_more_data); ++#else ++ out = tls_connection_decrypt(tls, conn, in); ++#endif + wpabuf_free(in); + in = NULL; + if (need_more_data) { +--- a/wpa_supplicant/Makefile ++++ b/wpa_supplicant/Makefile +@@ -1108,6 +1108,7 @@ CFLAGS += -DCONFIG_TLSV12 + endif + + ifeq ($(CONFIG_TLS), wolfssl) ++CFLAGS += -DCONFIG_TLS_WOLFSSL + ifdef TLS_FUNCS + CFLAGS += -DWOLFSSL_DER_LOAD + OBJS += ../src/crypto/tls_wolfssl.o +@@ -1123,6 +1124,7 @@ LIBS_p += -lwolfssl -lm + endif + + ifeq ($(CONFIG_TLS), openssl) ++CFLAGS += -DCONFIG_TLS_OPENSSL + CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 + ifdef TLS_FUNCS + CFLAGS += -DEAP_TLS_OPENSSL +@@ -1150,6 +1152,7 @@ CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONF + endif + + ifeq ($(CONFIG_TLS), mbedtls) ++CFLAGS += -DCONFIG_TLS_MBEDTLS + ifndef CONFIG_CRYPTO + CONFIG_CRYPTO=mbedtls + endif +@@ -1169,6 +1172,7 @@ endif + endif + + ifeq ($(CONFIG_TLS), gnutls) ++CFLAGS += -DCONFIG_TLS_GNUTLS + ifndef CONFIG_CRYPTO + # default to libgcrypt + CONFIG_CRYPTO=gnutls +@@ -1199,6 +1203,7 @@ endif + endif + + ifeq ($(CONFIG_TLS), internal) ++CFLAGS += -DCONFIG_TLS_INTERNAL + ifndef CONFIG_CRYPTO + CONFIG_CRYPTO=internal + endif +@@ -1279,6 +1284,7 @@ endif + endif + + ifeq ($(CONFIG_TLS), linux) ++CFLAGS += -DCONFIG_TLS_INTERNAL + OBJS += ../src/crypto/crypto_linux.o + OBJS_p += ../src/crypto/crypto_linux.o + ifdef TLS_FUNCS diff --git a/package/network/services/hostapd/patches/150-add-NULL-checks-encountered-during-tests-hwsim.patch b/package/network/services/hostapd/patches/150-add-NULL-checks-encountered-during-tests-hwsim.patch new file mode 100644 index 00000000000..361f7261469 --- /dev/null +++ b/package/network/services/hostapd/patches/150-add-NULL-checks-encountered-during-tests-hwsim.patch @@ -0,0 +1,45 @@ +From 33afce36c54b0cad38643629ded10ff5d727f077 Mon Sep 17 00:00:00 2001 +From: Glenn Strauss +Date: Fri, 12 Aug 2022 05:34:47 -0400 +Subject: [PATCH 5/7] add NULL checks (encountered during tests/hwsim) + +sae_derive_commit_element_ecc NULL pwe_ecc check +dpp_gen_keypair() NULL curve check + +Signed-off-by: Glenn Strauss +--- + src/common/dpp_crypto.c | 6 ++++++ + src/common/sae.c | 7 +++++++ + 2 files changed, 13 insertions(+) + +--- a/src/common/dpp_crypto.c ++++ b/src/common/dpp_crypto.c +@@ -248,6 +248,12 @@ struct crypto_ec_key * dpp_set_pubkey_po + + struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve) + { ++ if (curve == NULL) { ++ wpa_printf(MSG_DEBUG, ++ "DPP: %s curve must be initialized", __func__); ++ return NULL; ++ } ++ + struct crypto_ec_key *key; + + wpa_printf(MSG_DEBUG, "DPP: Generating a keypair"); +--- a/src/common/sae.c ++++ b/src/common/sae.c +@@ -1278,6 +1278,13 @@ void sae_deinit_pt(struct sae_pt *pt) + static int sae_derive_commit_element_ecc(struct sae_data *sae, + struct crypto_bignum *mask) + { ++ if (sae->tmp->pwe_ecc == NULL) { ++ wpa_printf(MSG_DEBUG, ++ "SAE: %s sae->tmp->pwe_ecc must be initialized", ++ __func__); ++ return -1; ++ } ++ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ecc) { + sae->tmp->own_commit_element_ecc = diff --git a/package/network/services/hostapd/patches/160-dpp_pkex-EC-point-mul-w-value-prime.patch b/package/network/services/hostapd/patches/160-dpp_pkex-EC-point-mul-w-value-prime.patch new file mode 100644 index 00000000000..56ae8d0b867 --- /dev/null +++ b/package/network/services/hostapd/patches/160-dpp_pkex-EC-point-mul-w-value-prime.patch @@ -0,0 +1,26 @@ +From 54211caa2e0e5163aefef390daf88a971367a702 Mon Sep 17 00:00:00 2001 +From: Glenn Strauss +Date: Tue, 4 Oct 2022 17:09:24 -0400 +Subject: [PATCH 6/7] dpp_pkex: EC point mul w/ value < prime + +crypto_ec_point_mul() with mbedtls requires point +be multiplied by a multiplicand with value < prime + +Signed-off-by: Glenn Strauss +--- + src/common/dpp_crypto.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/src/common/dpp_crypto.c ++++ b/src/common/dpp_crypto.c +@@ -1567,7 +1567,9 @@ dpp_pkex_derive_Qr(const struct dpp_curv + Pr = crypto_ec_key_get_public_key(Pr_key); + Qr = crypto_ec_point_init(ec); + hash_bn = crypto_bignum_init_set(hash, curve->hash_len); +- if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr)) ++ if (!Pr || !Qr || !hash_bn || ++ crypto_bignum_mod(hash_bn, crypto_ec_get_prime(ec), hash_bn) || ++ crypto_ec_point_mul(ec, Pr, hash_bn, Qr)) + goto fail; + + if (crypto_ec_point_is_at_infinity(ec, Qr)) { diff --git a/package/network/services/hostapd/patches/170-DPP-fix-memleak-of-intro.peer_key.patch b/package/network/services/hostapd/patches/170-DPP-fix-memleak-of-intro.peer_key.patch new file mode 100644 index 00000000000..157347d6d76 --- /dev/null +++ b/package/network/services/hostapd/patches/170-DPP-fix-memleak-of-intro.peer_key.patch @@ -0,0 +1,32 @@ +From 639bb1bb912029ec4ff110c3ed807b62f583d6bf Mon Sep 17 00:00:00 2001 +From: Glenn Strauss +Date: Sun, 9 Oct 2022 04:02:44 -0400 +Subject: [PATCH 7/7] DPP: fix memleak of intro.peer_key + +fix memleak of intro.peer_key in wpas_dpp_rx_peer_disc_resp() + +Signed-off-by: Glenn Strauss +--- + wpa_supplicant/dpp_supplicant.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/wpa_supplicant/dpp_supplicant.c ++++ b/wpa_supplicant/dpp_supplicant.c +@@ -2610,6 +2610,8 @@ static void wpas_dpp_rx_peer_disc_resp(s + return; + } + ++ os_memset(&intro, 0, sizeof(intro)); ++ + trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID, + &trans_id_len); + if (!trans_id || trans_id_len != 1) { +@@ -2720,7 +2722,7 @@ static void wpas_dpp_rx_peer_disc_resp(s + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + fail: +- os_memset(&intro, 0, sizeof(intro)); ++ dpp_peer_intro_deinit(&intro); + } + + diff --git a/package/network/services/hostapd/patches/200-multicall.patch b/package/network/services/hostapd/patches/200-multicall.patch index 576c6711554..0db8caa20e1 100644 --- a/package/network/services/hostapd/patches/200-multicall.patch +++ b/package/network/services/hostapd/patches/200-multicall.patch @@ -36,7 +36,7 @@ LIBS += $(DRV_AP_LIBS) ifdef CONFIG_L2_PACKET -@@ -1291,6 +1297,12 @@ install: $(addprefix $(DESTDIR)$(BINDIR) +@@ -1378,6 +1384,12 @@ install: $(addprefix $(DESTDIR)$(BINDIR) _OBJS_VAR := OBJS include ../src/objs.mk @@ -49,7 +49,7 @@ hostapd: $(OBJS) $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS) @$(E) " LD " $@ -@@ -1365,6 +1377,12 @@ include ../src/objs.mk +@@ -1458,6 +1470,12 @@ include ../src/objs.mk _OBJS_VAR := SOBJS include ../src/objs.mk @@ -128,7 +128,7 @@ NEED_AES_WRAP=y OBJS += ../src/ap/wpa_auth.o OBJS += ../src/ap/wpa_auth_ie.o -@@ -1920,6 +1937,12 @@ wpa_priv: $(BCHECK) $(OBJS_priv) +@@ -1996,6 +2013,12 @@ wpa_priv: $(BCHECK) $(OBJS_priv) _OBJS_VAR := OBJS include ../src/objs.mk @@ -141,7 +141,7 @@ wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs) $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) @$(E) " LD " $@ -@@ -2052,6 +2075,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK) +@@ -2128,6 +2151,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK) $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ @$(E) " sed" $< diff --git a/package/network/services/hostapd/patches/500-lto-jobserver-support.patch b/package/network/services/hostapd/patches/500-lto-jobserver-support.patch index e0458b20548..1a0ce1256ab 100644 --- a/package/network/services/hostapd/patches/500-lto-jobserver-support.patch +++ b/package/network/services/hostapd/patches/500-lto-jobserver-support.patch @@ -1,6 +1,6 @@ --- a/hostapd/Makefile +++ b/hostapd/Makefile -@@ -1307,7 +1307,7 @@ hostapd_multi.a: $(BCHECK) $(OBJS) +@@ -1394,7 +1394,7 @@ hostapd_multi.a: $(BCHECK) $(OBJS) @$(AR) cr $@ hostapd_multi.o $(OBJS) hostapd: $(OBJS) @@ -9,7 +9,7 @@ @$(E) " LD " $@ ifdef CONFIG_WPA_TRACE -@@ -1318,7 +1318,7 @@ _OBJS_VAR := OBJS_c +@@ -1405,7 +1405,7 @@ _OBJS_VAR := OBJS_c include ../src/objs.mk hostapd_cli: $(OBJS_c) @@ -20,7 +20,7 @@ NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS) --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile -@@ -1949,31 +1949,31 @@ wpa_supplicant_multi.a: .config $(BCHECK +@@ -2025,31 +2025,31 @@ wpa_supplicant_multi.a: .config $(BCHECK @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS) wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs) From a0bf926a461484cf7b9b56768e14212994c88d84 Mon Sep 17 00:00:00 2001 From: Nick Hainke Date: Wed, 14 Dec 2022 11:00:07 +0100 Subject: [PATCH 106/110] tools/xz: update to 5.2.10 Update to latest version. Signed-off-by: Nick Hainke --- tools/xz/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/xz/Makefile b/tools/xz/Makefile index 7d3392fefe5..6e287a61b0b 100644 --- a/tools/xz/Makefile +++ b/tools/xz/Makefile @@ -7,12 +7,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=xz -PKG_VERSION:=5.2.9 +PKG_VERSION:=5.2.10 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:=@SF/lzmautils \ http://tukaani.org/xz -PKG_HASH:=b194507fba3a462a753c553149ccdaa168337bcb7deefddd067ba987c83dfce6 +PKG_HASH:=01b71df61521d9da698ce3c33148bff06a131628ff037398c09482f3a26e5408 PKG_CPE_ID:=cpe:/a:tukaani:xz HOST_BUILD_PARALLEL:=1 From e9008223268c5023e8148a723f629b7102546f47 Mon Sep 17 00:00:00 2001 From: John Audia Date: Mon, 19 Dec 2022 09:17:10 -0500 Subject: [PATCH 107/110] kernel: bump 5.15 to 5.15.84 All patches automatically rebased Build system: x86_64 Build-tested: bcm2711/RPi4B Run-tested: bcm2711/RPi4B Signed-off-by: John Audia --- include/kernel-5.15 | 4 ++-- .../008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/kernel-5.15 b/include/kernel-5.15 index 9a619067df0..5013ce44e23 100644 --- a/include/kernel-5.15 +++ b/include/kernel-5.15 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.15 = .83 -LINUX_KERNEL_HASH-5.15.83 = 40590843c04c85789105157f69efbd71a4efe87ae2568e40d1b7258c3f747ff3 +LINUX_VERSION-5.15 = .84 +LINUX_KERNEL_HASH-5.15.84 = 318dc30cb059c2e35b59652b166b39804bb3a941f11878aae6119019a04b8217 diff --git a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch index d5737d17367..78b050df185 100644 --- a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch +++ b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch @@ -32,7 +32,7 @@ Signed-off-by: Guenter Roeck --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -15887,6 +15887,13 @@ S: Maintained +@@ -15882,6 +15882,13 @@ S: Maintained F: include/sound/rt*.h F: sound/soc/codecs/rt* From 1e375c8fbd6a3259ffbbbde13eb0c106bedbcd1c Mon Sep 17 00:00:00 2001 From: John Audia Date: Mon, 19 Dec 2022 09:18:02 -0500 Subject: [PATCH 108/110] kernel: bump 5.10 to 5.10.160 No patches affected by this update. Build system: x86_64 Build-tested: ramips/tplink_archer-a6-v3 Run-tested: ramips/tplink_archer-a6-v3 Signed-off-by: John Audia --- include/kernel-5.10 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/kernel-5.10 b/include/kernel-5.10 index bd40dc0f407..abfb0422d12 100644 --- a/include/kernel-5.10 +++ b/include/kernel-5.10 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.10 = .159 -LINUX_KERNEL_HASH-5.10.159 = 1ba9bf57b6bf36d76447d5044b80b746cb5fd61d981c811603dc763b7789cea7 +LINUX_VERSION-5.10 = .160 +LINUX_KERNEL_HASH-5.10.160 = 30d5076acae863941045880c4c5c5109d26a54a932168fa1324237e8aeaa840b From 5f973c1eefa91050f2659c29a276284a7793985b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0tetiar?= Date: Mon, 19 Dec 2022 11:14:41 +0100 Subject: [PATCH 109/110] Revert "Revert "mvebu: switch default kernel to 5.15"" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 97c77fff28cf001399f33c7bc1ec6687ba18450b as commit 8be6350f6646 ("generic: 5.15: allow MV88E6xxx built-in when PTP support disabled") contains the fix, so lets enable kernel 5.15 on mvebu again. Signed-off-by: Petr Štetiar --- target/linux/mvebu/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/linux/mvebu/Makefile b/target/linux/mvebu/Makefile index 6a1e0f63f70..2971f3fcaf5 100644 --- a/target/linux/mvebu/Makefile +++ b/target/linux/mvebu/Makefile @@ -9,8 +9,7 @@ BOARDNAME:=Marvell EBU Armada FEATURES:=fpu usb pci pcie gpio nand squashfs ramdisk boot-part rootfs-part legacy-sdcard targz SUBTARGETS:=cortexa9 cortexa53 cortexa72 -KERNEL_PATCHVER:=5.10 -KERNEL_TESTING_PATCHVER:=5.15 +KERNEL_PATCHVER:=5.15 include $(INCLUDE_DIR)/target.mk From 6675a9aaf4bc0afc3dccad8f9925b03b6e42993e Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 19 Dec 2022 18:58:42 +0000 Subject: [PATCH 110/110] xdp-tools: update to version 1.2.9 Changes since v1.2.8: 32aaf32 libxdp: Fix incorrect rx_ring_setup_done 6049671 headers: add bpf_endian.h for parsing_helpers.h 2682c1c export-man: Ignore errors when executing git shell command 8afda7a xdp-loader/README: Mention lack of support for HW mode in most cards dc69919 libxdp: fix prog_fd checks for fd >= 0 3d7c22a libxdp: Allow falling back to single-program attachment for loaded programs af00429 libxdp: Fix check in xdp_program__attach_single() 41703d2 libxdp: Make sure to set the the program autoload when loading a program b1fd2e5 test-xdpdump: Only run tshark attribute test on newer versions of tshark 5dfe342 libxdp: Convert xdp-dispatcher to use strict section names 929a22e configure: Try to auto-detect versioned clang binaries 074fcfb libxdp: Check program name when determining if a program is a dispatcher e13a191 Bump TOOLS_VERSION to 1.2.9 Signed-off-by: Daniel Golle --- package/network/utils/xdp-tools/Makefile | 4 ++-- .../xdp-tools/patches/010-configure-respect-LDFLAGS.patch | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package/network/utils/xdp-tools/Makefile b/package/network/utils/xdp-tools/Makefile index 20a9139946f..f46170c6ca6 100644 --- a/package/network/utils/xdp-tools/Makefile +++ b/package/network/utils/xdp-tools/Makefile @@ -2,8 +2,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=xdp-tools PKG_RELEASE:=$(AUTORELEASE) -PKG_VERSION:=1.2.8 -PKG_HASH:=2c575e5242e60055b0e7fc720f5b6ea87d74911f967dde3d50046d751f35bff0 +PKG_VERSION:=1.2.9 +PKG_HASH:=159ed8d3c8195d812ec3cde83bd736245a72743af372998320d39c2ba69ab142 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/xdp-project/xdp-tools/tar.gz/v$(PKG_VERSION)? diff --git a/package/network/utils/xdp-tools/patches/010-configure-respect-LDFLAGS.patch b/package/network/utils/xdp-tools/patches/010-configure-respect-LDFLAGS.patch index b3454548f83..e2fbfa57dca 100644 --- a/package/network/utils/xdp-tools/patches/010-configure-respect-LDFLAGS.patch +++ b/package/network/utils/xdp-tools/patches/010-configure-respect-LDFLAGS.patch @@ -1,6 +1,6 @@ --- a/configure +++ b/configure -@@ -138,7 +138,7 @@ int main(int argc, char **argv) { +@@ -174,7 +174,7 @@ int main(int argc, char **argv) { return 0; } EOF @@ -9,7 +9,7 @@ if [ "$?" -eq "0" ]; then echo "HAVE_PCAP:=y" >>$CONFIG [ -n "$LIBPCAP_CFLAGS" ] && echo 'CFLAGS += ' $LIBPCAP_CFLAGS >> $CONFIG -@@ -186,7 +186,7 @@ int main(int argc, char **argv) { +@@ -222,7 +222,7 @@ int main(int argc, char **argv) { return 0; } EOF @@ -18,7 +18,7 @@ if [ "$?" -eq "0" ]; then echo "HAVE_FEATURES+=${config_var}" >>"$CONFIG" echo "yes" -@@ -253,7 +253,7 @@ int main(int argc, char **argv) { +@@ -289,7 +289,7 @@ int main(int argc, char **argv) { } EOF