diff --git a/include/kernel-5.10 b/include/kernel-5.10 index e52f4b1c48..f5dff37bda 100644 --- a/include/kernel-5.10 +++ b/include/kernel-5.10 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.10 = .142 -LINUX_KERNEL_HASH-5.10.142 = 3f47ebdb9afe152a0c32c1157336ef13fa5cc08ac6d884dfc1f6ddc2b7dba268 +LINUX_VERSION-5.10 = .143 +LINUX_KERNEL_HASH-5.10.143 = fa2c9edef272d39dca52e057e1d41433cf1b6ab6a00d24a00333c0b735054e91 diff --git a/include/kernel-5.15 b/include/kernel-5.15 index 8ebccec7fa..8aaf057ce4 100644 --- a/include/kernel-5.15 +++ b/include/kernel-5.15 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.15 = .67 -LINUX_KERNEL_HASH-5.15.67 = da47d9a80b694548835ccb553b6eb1a1f3f5d5cddd9e2bd6f4886b99ca14f940 +LINUX_VERSION-5.15 = .68 +LINUX_KERNEL_HASH-5.15.68 = 17bbb3cb5c9ba18583b6679cc28f828aec49c72abbfc6fbde310b0cb17218b7e diff --git a/package/firmware/linux-firmware/Makefile b/package/firmware/linux-firmware/Makefile index c64dec83f0..f383acf85a 100644 --- a/package/firmware/linux-firmware/Makefile +++ b/package/firmware/linux-firmware/Makefile @@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=linux-firmware -PKG_VERSION:=20220708 +PKG_VERSION:=20220913 PKG_RELEASE:=1 PKG_SOURCE_URL:=@KERNEL/linux/kernel/firmware PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz -PKG_HASH:=0abec827a035c82bdcabdf82aa37ded247bc682ef05861bd409ea6f477bab81d +PKG_HASH:=26fd00f2d8e96c4af6f44269a6b893eb857253044f75ad28ef6706a2250cd8e9 PKG_MAINTAINER:=Felix Fietkau diff --git a/package/firmware/wireless-regdb/Makefile b/package/firmware/wireless-regdb/Makefile index 006974b3d1..73ea40133f 100644 --- a/package/firmware/wireless-regdb/Makefile +++ b/package/firmware/wireless-regdb/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=wireless-regdb -PKG_VERSION:=2022.06.06 +PKG_VERSION:=2022.08.12 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=@KERNEL/software/network/wireless-regdb/ -PKG_HASH:=ac00f97efecce5046ed069d1d93f3365fdf994c7c7854a8fc50831e959537230 +PKG_HASH:=59c8f7d17966db71b27f90e735ee8f5b42ca3527694a8c5e6e9b56bd379c3b84 PKG_MAINTAINER:=Felix Fietkau diff --git a/package/kernel/lantiq/ltq-vdsl-mei/Makefile b/package/kernel/lantiq/ltq-vdsl-vr9-mei/Makefile similarity index 89% rename from package/kernel/lantiq/ltq-vdsl-mei/Makefile rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/Makefile index b7060eb2b4..e9c5a681fd 100644 --- a/package/kernel/lantiq/ltq-vdsl-mei/Makefile +++ b/package/kernel/lantiq/ltq-vdsl-vr9-mei/Makefile @@ -39,7 +39,7 @@ define KernelPackage/ltq-vdsl-vr9-mei/description endef -define Package/ltq-vdsl-mei-test +define Package/ltq-vdsl-vr9-mei-test SECTION:=net CATEGORY:=Network TITLE:=Lantiq mei driver test tool @@ -47,7 +47,7 @@ define Package/ltq-vdsl-mei-test DEPENDS:=@TARGET_lantiq_xrx200 endef -define Package/ltq-vdsl-mei-test/description +define Package/ltq-vdsl-vr9-mei-test/description Userland tool to directly control the mei driver, this is only needed for test and development purposes. endef @@ -56,10 +56,10 @@ MAKE_FLAGS += \ $(KERNEL_MAKE_FLAGS) \ SHELL="$(BASH)" -# ltq-vdsl-app uses a header provided by the MEI driver which has some +# ltq-vdsl-vr9-app uses a header provided by the MEI driver which has some # conditionals. # Define the conditionals here to have the same view on both sides. If you -# change them, you need to change them for the ltq-vdsl-app as well +# change them, you need to change them for the ltq-vdsl-vr9-app as well MEI_DRV_CFLAGS = \ -DMEI_DRV_ATM_PTM_INTERFACE_ENABLE=1 \ -DMEI_SUPPORT_DEBUG_STREAMS=1 \ @@ -91,9 +91,9 @@ endef $(eval $(call KernelPackage,ltq-vdsl-vr9-mei)) -define Package/ltq-vdsl-mei-test/install +define Package/ltq-vdsl-vr9-mei-test/install $(INSTALL_DIR) $(1)/bin $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/mei_cpe_drv_test $(1)/bin endef -$(eval $(call BuildPackage,ltq-vdsl-mei-test)) +$(eval $(call BuildPackage,ltq-vdsl-vr9-mei-test)) diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/001-fix-compile.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/001-fix-compile.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/001-fix-compile.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/001-fix-compile.patch diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/010-warnings.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/010-warnings.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/010-warnings.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/010-warnings.patch diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/020-not-leak-cflags.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/020-not-leak-cflags.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/020-not-leak-cflags.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/020-not-leak-cflags.patch diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/030-no-static-linking.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/030-no-static-linking.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/030-no-static-linking.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/030-no-static-linking.patch diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/100-compat.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/100-compat.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/100-compat.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/100-compat.patch diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/101_no-date-time.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/101_no-date-time.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/101_no-date-time.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/101_no-date-time.patch diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/110-reset-g_tx_link_rate-on-showtime-exit.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/110-reset-g_tx_link_rate-on-showtime-exit.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/110-reset-g_tx_link_rate-on-showtime-exit.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/110-reset-g_tx_link_rate-on-showtime-exit.patch diff --git a/package/kernel/lantiq/ltq-vdsl-mei/patches/200-interrupt-lock.patch b/package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/200-interrupt-lock.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl-mei/patches/200-interrupt-lock.patch rename to package/kernel/lantiq/ltq-vdsl-vr9-mei/patches/200-interrupt-lock.patch diff --git a/package/kernel/lantiq/ltq-vdsl/Makefile b/package/kernel/lantiq/ltq-vdsl-vr9/Makefile similarity index 100% rename from package/kernel/lantiq/ltq-vdsl/Makefile rename to package/kernel/lantiq/ltq-vdsl-vr9/Makefile diff --git a/package/kernel/lantiq/ltq-vdsl/patches/001-fix-compile.patch b/package/kernel/lantiq/ltq-vdsl-vr9/patches/001-fix-compile.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl/patches/001-fix-compile.patch rename to package/kernel/lantiq/ltq-vdsl-vr9/patches/001-fix-compile.patch diff --git a/package/kernel/lantiq/ltq-vdsl/patches/020-not-leak-cflags.patch b/package/kernel/lantiq/ltq-vdsl-vr9/patches/020-not-leak-cflags.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl/patches/020-not-leak-cflags.patch rename to package/kernel/lantiq/ltq-vdsl-vr9/patches/020-not-leak-cflags.patch diff --git a/package/kernel/lantiq/ltq-vdsl/patches/100-compat.patch b/package/kernel/lantiq/ltq-vdsl-vr9/patches/100-compat.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl/patches/100-compat.patch rename to package/kernel/lantiq/ltq-vdsl-vr9/patches/100-compat.patch diff --git a/package/kernel/lantiq/ltq-vdsl/patches/110-semaphore-lock.patch b/package/kernel/lantiq/ltq-vdsl-vr9/patches/110-semaphore-lock.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl/patches/110-semaphore-lock.patch rename to package/kernel/lantiq/ltq-vdsl-vr9/patches/110-semaphore-lock.patch diff --git a/package/kernel/lantiq/ltq-vdsl/patches/200-fix-elapsed-time.patch b/package/kernel/lantiq/ltq-vdsl-vr9/patches/200-fix-elapsed-time.patch similarity index 100% rename from package/kernel/lantiq/ltq-vdsl/patches/200-fix-elapsed-time.patch rename to package/kernel/lantiq/ltq-vdsl-vr9/patches/200-fix-elapsed-time.patch diff --git a/package/kernel/linux/Makefile b/package/kernel/linux/Makefile index 9fa68d78f7..ad68bde9b3 100644 --- a/package/kernel/linux/Makefile +++ b/package/kernel/linux/Makefile @@ -12,7 +12,7 @@ PKG_NAME:=kernel PKG_FLAGS:=hold PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/packages -SCAN_DEPS=modules/*.mk $(TOPDIR)/target/linux/*/modules.mk $(TOPDIR)/include/netfilter.mk +SCAN_DEPS=modules/*.mk $(TOPDIR)/target/linux/*/modules.mk $(TOPDIR)/target/linux/*/*/modules.mk $(TOPDIR)/include/netfilter.mk PKG_LICENSE:=GPL-2.0 PKG_LICENSE_FILES:= @@ -64,3 +64,4 @@ $(eval $(if $(DUMP),,$(call BuildPackage,kernel))) include $(sort $(wildcard ./modules/*.mk)) -include $(TOPDIR)/target/linux/*/modules.mk +-include $(TOPDIR)/target/linux/*/*/modules.mk diff --git a/package/kernel/mac80211/patches/rt2x00/001-rt2x00-define-RF5592-in-init_eeprom-routine.patch b/package/kernel/mac80211/patches/rt2x00/001-rt2x00-define-RF5592-in-init_eeprom-routine.patch new file mode 100644 index 0000000000..351e24a4d5 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/001-rt2x00-define-RF5592-in-init_eeprom-routine.patch @@ -0,0 +1,52 @@ +From patchwork Sat Sep 17 20:26:27 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979242 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:26:27 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 01/16] rt2x00: define RF5592 in init_eeprom routine +Message-ID: + +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +From: Tomislav Požega + +Fix incorrect RF value encoded in EEPROM on devices with Ralink Rt5592 +PCIe radio (a single chip 2T2R 802.11abgn solution). + +Signed-off-by: Tomislav Požega +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -9435,6 +9435,8 @@ static int rt2800_init_eeprom(struct rt2 + rf = RF3853; + else if (rt2x00_rt(rt2x00dev, RT5350)) + rf = RF5350; ++ else if (rt2x00_rt(rt2x00dev, RT5592)) ++ rf = RF5592; + else + rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); + diff --git a/package/kernel/mac80211/patches/rt2x00/002-rt2x00-add-throughput-LED-trigger.patch b/package/kernel/mac80211/patches/rt2x00/002-rt2x00-add-throughput-LED-trigger.patch new file mode 100644 index 0000000000..02d1f7a2e5 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/002-rt2x00-add-throughput-LED-trigger.patch @@ -0,0 +1,76 @@ +From patchwork Sat Sep 17 20:26:40 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979243 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:26:40 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 02/16] rt2x00: add throughput LED trigger +Message-ID: + <73f5ba4134e621462a26186449400cf0c1ac1730.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +From: David Bauer + +This adds a (currently missing) throughput LED trigger for the rt2x00 +driver. Previously, LED triggers had to be assigned to the netdev, which +was limited to a single VAP. + +Tested-by: Christoph Krapp +Signed-off-by: David Bauer +Acked-by: Stanislaw Gruszka +--- + drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +@@ -1093,6 +1093,19 @@ static void rt2x00lib_remove_hw(struct r + kfree(rt2x00dev->spec.channels_info); + } + ++static const struct ieee80211_tpt_blink rt2x00_tpt_blink[] = { ++ { .throughput = 0 * 1024, .blink_time = 334 }, ++ { .throughput = 1 * 1024, .blink_time = 260 }, ++ { .throughput = 2 * 1024, .blink_time = 220 }, ++ { .throughput = 5 * 1024, .blink_time = 190 }, ++ { .throughput = 10 * 1024, .blink_time = 170 }, ++ { .throughput = 25 * 1024, .blink_time = 150 }, ++ { .throughput = 54 * 1024, .blink_time = 130 }, ++ { .throughput = 120 * 1024, .blink_time = 110 }, ++ { .throughput = 265 * 1024, .blink_time = 80 }, ++ { .throughput = 586 * 1024, .blink_time = 50 }, ++}; ++ + static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) + { + struct hw_mode_spec *spec = &rt2x00dev->spec; +@@ -1174,6 +1187,11 @@ static int rt2x00lib_probe_hw(struct rt2 + + #undef RT2X00_TASKLET_INIT + ++ ieee80211_create_tpt_led_trigger(rt2x00dev->hw, ++ IEEE80211_TPT_LEDTRIG_FL_RADIO, ++ rt2x00_tpt_blink, ++ ARRAY_SIZE(rt2x00_tpt_blink)); ++ + /* + * Register HW. + */ diff --git a/package/kernel/mac80211/patches/rt2x00/002-rt2x00-define-RF5592-in-init_eeprom-routine.patch b/package/kernel/mac80211/patches/rt2x00/002-rt2x00-define-RF5592-in-init_eeprom-routine.patch deleted file mode 100644 index 96eeb37dc6..0000000000 --- a/package/kernel/mac80211/patches/rt2x00/002-rt2x00-define-RF5592-in-init_eeprom-routine.patch +++ /dev/null @@ -1,51 +0,0 @@ -From patchwork Thu Dec 27 14:05:26 2018 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: Tom Psyborg -X-Patchwork-Id: 10743707 -X-Patchwork-Delegate: kvalo@adurom.com -From: =?utf-8?q?Tomislav_Po=C5=BEega?= -To: linux-wireless@vger.kernel.org -Cc: kvalo@codeaurora.org, hauke@hauke-m.de, nbd@nbd.name, - john@phrozen.org, sgruszka@redhat.com, daniel@makrotopia.org -Subject: [PATCH 2/2] rt2x00: define RF5592 in init_eeprom routine -Date: Thu, 27 Dec 2018 15:05:26 +0100 -Message-Id: <1545919526-4074-2-git-send-email-pozega.tomislav@gmail.com> -X-Mailer: git-send-email 1.7.0.4 -In-Reply-To: <1545919526-4074-1-git-send-email-pozega.tomislav@gmail.com> -References: <1545919526-4074-1-git-send-email-pozega.tomislav@gmail.com> -MIME-Version: 1.0 -Sender: linux-wireless-owner@vger.kernel.org -Precedence: bulk -List-ID: -X-Mailing-List: linux-wireless@vger.kernel.org -X-Virus-Scanned: ClamAV using ClamSMTP - -This patch fixes following crash on Linksys EA2750 during 5GHz wifi -init: - -[ 7.955153] rt2800pci 0000:01:00.0: card - bus=0x1, slot = 0x0 irq=4 -[ 7.962259] rt2800pci 0000:01:00.0: loaded eeprom from mtd device "Factory" -[ 7.969435] ieee80211 phy0: rt2x00_set_rt: Info - RT chipset 5592, rev 0222 detected -[ 7.977348] ieee80211 phy0: rt2800_init_eeprom: Error - Invalid RF chipset 0x0000 detected -[ 7.985793] ieee80211 phy0: rt2x00lib_probe_dev: Error - Failed to allocate device -[ 7.993569] CPU 0 Unable to handle kernel paging request at virtual address 00000024, epc == 800c8f54, ra == 80249ff8 -[ 8.004408] Oops[#1]: - -Signed-off-by: Tomislav Požega ---- - drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 ++ - 1 files changed, 2 insertions(+), 0 deletions(-) - ---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -9435,6 +9435,8 @@ static int rt2800_init_eeprom(struct rt2 - rf = RF3853; - else if (rt2x00_rt(rt2x00dev, RT5350)) - rf = RF5350; -+ else if (rt2x00_rt(rt2x00dev, RT5592)) -+ rf = RF5592; - else - rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); - diff --git a/package/kernel/mac80211/patches/rt2x00/650-rt2x00-add-support-for-external-PA-on-MT7620.patch b/package/kernel/mac80211/patches/rt2x00/003-rt2x00-add-support-for-external-PA-on-MT7620.patch similarity index 58% rename from package/kernel/mac80211/patches/rt2x00/650-rt2x00-add-support-for-external-PA-on-MT7620.patch rename to package/kernel/mac80211/patches/rt2x00/003-rt2x00-add-support-for-external-PA-on-MT7620.patch index 8814c02532..216f583063 100644 --- a/package/kernel/mac80211/patches/rt2x00/650-rt2x00-add-support-for-external-PA-on-MT7620.patch +++ b/package/kernel/mac80211/patches/rt2x00/003-rt2x00-add-support-for-external-PA-on-MT7620.patch @@ -1,22 +1,42 @@ -From 9782a7f7488443568fa4d6088b73c9aff7eb8510 Mon Sep 17 00:00:00 2001 +From patchwork Sat Sep 17 20:26:55 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979244 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:26:55 +0100 From: Daniel Golle -Date: Wed, 19 Apr 2017 16:14:53 +0200 -Subject: [PATCH] rt2x00: add support for external PA on MT7620 -To: Stanislaw Gruszka -Cc: Helmut Schaa , - linux-wireless@vger.kernel.org, - Kalle Valo -Content-Type: text/plain; charset="UTF-8" -Content-Transfer-Encoding: quoted-printable +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 03/16] rt2x00: add support for external PA on MT7620 +Message-ID: + +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org -Signed-off-by: Daniel Golle -Signed-off-by: Tomislav Po=C5=BEega +Implement support for external PA connected to MT7620A. + +Signed-off-by: Tomislav Požega [pozega.tomislav@gmail.com: use chanreg and dccal helpers.] - +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka --- - drivers/net/wireless/ralink/rt2x00/rt2800.h | 1 + - drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 70 +++++++++++++++++++++++++- - 2 files changed, 70 insertions(+), 1 deletion(-) + drivers/net/wireless/ralink/rt2x00/rt2800.h | 1 + + .../net/wireless/ralink/rt2x00/rt2800lib.c | 52 ++++++++++++++++++- + 2 files changed, 52 insertions(+), 1 deletion(-) --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h @@ -24,21 +44,19 @@ Signed-off-by: Tomislav Po=C5=BEega #define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) #define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) #define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) -+#define EEPROM_NIC_CONF2_EXTERNAL_PA FIELD16(0xc000) ++#define EEPROM_NIC_CONF2_EXTERNAL_PA FIELD16(0x8000) /* * EEPROM LNA --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -4369,6 +4369,45 @@ static void rt2800_config_channel(struct +@@ -4368,6 +4368,43 @@ static void rt2800_config_channel(struct rt2800_iq_calibrate(rt2x00dev, rf->channel); } + if (rt2x00_rt(rt2x00dev, RT6352)) { + if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, + &rt2x00dev->cap_flags)) { -+ rt2x00_warn(rt2x00dev, "Using incomplete support for " \ -+ "external PA\n"); + reg = rt2800_register_read(rt2x00dev, RF_CONTROL3); + reg |= 0x00000101; + rt2800_register_write(rt2x00dev, RF_CONTROL3, reg); @@ -76,7 +94,7 @@ Signed-off-by: Tomislav Po=C5=BEega bbp = rt2800_bbp_read(rt2x00dev, 4); rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); rt2800_bbp_write(rt2x00dev, 4, bbp); -@@ -9578,7 +9617,8 @@ static int rt2800_init_eeprom(struct rt2 +@@ -9566,7 +9603,8 @@ static int rt2800_init_eeprom(struct rt2 */ eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1); @@ -86,19 +104,19 @@ Signed-off-by: Tomislav Po=C5=BEega if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX0_PA_3352)) __set_bit(CAPABILITY_EXTERNAL_PA_TX0, -@@ -9589,6 +9629,18 @@ static int rt2800_init_eeprom(struct rt2 +@@ -9577,6 +9615,18 @@ static int rt2800_init_eeprom(struct rt2 &rt2x00dev->cap_flags); } + eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF2); + + if (rt2x00_rt(rt2x00dev, RT6352) && eeprom != 0 && eeprom != 0xffff) { -+ if (rt2x00_get_field16(eeprom, -+ EEPROM_NIC_CONF2_EXTERNAL_PA)) { -+ __set_bit(CAPABILITY_EXTERNAL_PA_TX0, -+ &rt2x00dev->cap_flags); -+ __set_bit(CAPABILITY_EXTERNAL_PA_TX1, -+ &rt2x00dev->cap_flags); ++ if (!rt2x00_get_field16(eeprom, ++ EEPROM_NIC_CONF2_EXTERNAL_PA)) { ++ __clear_bit(CAPABILITY_EXTERNAL_PA_TX0, ++ &rt2x00dev->cap_flags); ++ __clear_bit(CAPABILITY_EXTERNAL_PA_TX1, ++ &rt2x00dev->cap_flags); + } + } + diff --git a/package/kernel/mac80211/patches/rt2x00/004-rt2x00-move-up-and-reuse-busy-wait-functions.patch b/package/kernel/mac80211/patches/rt2x00/004-rt2x00-move-up-and-reuse-busy-wait-functions.patch new file mode 100644 index 0000000000..da453074a1 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/004-rt2x00-move-up-and-reuse-busy-wait-functions.patch @@ -0,0 +1,178 @@ +From patchwork Sat Sep 17 20:27:10 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979245 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:27:10 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 04/16] rt2x00: move up and reuse busy wait functions +Message-ID: + <3fdb9dc15e76a9f9c1948b4a3a1308a7a5677bb8.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +Move bbp_ready and rf_ready busy wait functions up in the code so they +can more easily be used. Allow specifying register mask in rf_ready +function which is useful for calibration routines which will be added +in follow-up commits. + +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + .../net/wireless/ralink/rt2x00/rt2800lib.c | 99 +++++++++---------- + 1 file changed, 46 insertions(+), 53 deletions(-) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -2143,6 +2143,48 @@ void rt2800_config_erp(struct rt2x00_dev + } + EXPORT_SYMBOL_GPL(rt2800_config_erp); + ++static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev, ++ const struct rt2x00_field32 mask) ++{ ++ unsigned int i; ++ u32 reg; ++ ++ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { ++ reg = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); ++ if (!rt2x00_get_field32(reg, mask)) ++ return 0; ++ ++ udelay(REGISTER_BUSY_DELAY); ++ } ++ ++ rt2x00_err(rt2x00dev, "BBP/RF register access failed, aborting\n"); ++ return -EACCES; ++} ++ ++static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) ++{ ++ unsigned int i; ++ u8 value; ++ ++ /* ++ * BBP was enabled after firmware was loaded, ++ * but we need to reactivate it now. ++ */ ++ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); ++ rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); ++ msleep(1); ++ ++ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { ++ value = rt2800_bbp_read(rt2x00dev, 0); ++ if ((value != 0xff) && (value != 0x00)) ++ return 0; ++ udelay(REGISTER_BUSY_DELAY); ++ } ++ ++ rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); ++ return -EACCES; ++} ++ + static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev) + { + u32 reg; +@@ -3799,10 +3841,9 @@ static void rt2800_config_alc(struct rt2 + struct ieee80211_channel *chan, + int power_level) { + u16 eeprom, target_power, max_power; +- u32 mac_sys_ctrl, mac_status; ++ u32 mac_sys_ctrl; + u32 reg; + u8 bbp; +- int i; + + /* hardware unit is 0.5dBm, limited to 23.5dBm */ + power_level *= 2; +@@ -3838,16 +3879,8 @@ static void rt2800_config_alc(struct rt2 + /* Disable Tx/Rx */ + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0); + /* Check MAC Tx/Rx idle */ +- for (i = 0; i < 10000; i++) { +- mac_status = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); +- if (mac_status & 0x3) +- usleep_range(50, 200); +- else +- break; +- } +- +- if (i == 10000) +- rt2x00_warn(rt2x00dev, "Wait MAC Status to MAX !!!\n"); ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY))) ++ rt2x00_warn(rt2x00dev, "RF busy while configuring ALC\n"); + + if (chan->center_freq > 2457) { + bbp = rt2800_bbp_read(rt2x00dev, 30); +@@ -6249,46 +6282,6 @@ static int rt2800_init_registers(struct + return 0; + } + +-static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev) +-{ +- unsigned int i; +- u32 reg; +- +- for (i = 0; i < REGISTER_BUSY_COUNT; i++) { +- reg = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); +- if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY)) +- return 0; +- +- udelay(REGISTER_BUSY_DELAY); +- } +- +- rt2x00_err(rt2x00dev, "BBP/RF register access failed, aborting\n"); +- return -EACCES; +-} +- +-static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +-{ +- unsigned int i; +- u8 value; +- +- /* +- * BBP was enabled after firmware was loaded, +- * but we need to reactivate it now. +- */ +- rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); +- rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); +- msleep(1); +- +- for (i = 0; i < REGISTER_BUSY_COUNT; i++) { +- value = rt2800_bbp_read(rt2x00dev, 0); +- if ((value != 0xff) && (value != 0x00)) +- return 0; +- udelay(REGISTER_BUSY_DELAY); +- } +- +- rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); +- return -EACCES; +-} + + static void rt2800_bbp4_mac_if_ctrl(struct rt2x00_dev *rt2x00dev) + { +@@ -9110,7 +9103,7 @@ int rt2800_enable_radio(struct rt2x00_de + /* + * Wait BBP/RF to wake up. + */ +- if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev))) ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY))) + return -EIO; + + /* diff --git a/package/kernel/mac80211/patches/rt2x00/982-rt2x00-add-RF-self-TXDC-calibration.patch b/package/kernel/mac80211/patches/rt2x00/005-rt2x00-add-RF-self-TXDC-calibration-for-MT7620.patch similarity index 55% rename from package/kernel/mac80211/patches/rt2x00/982-rt2x00-add-RF-self-TXDC-calibration.patch rename to package/kernel/mac80211/patches/rt2x00/005-rt2x00-add-RF-self-TXDC-calibration-for-MT7620.patch index b74bea2020..4e98d6a375 100644 --- a/package/kernel/mac80211/patches/rt2x00/982-rt2x00-add-RF-self-TXDC-calibration.patch +++ b/package/kernel/mac80211/patches/rt2x00/005-rt2x00-add-RF-self-TXDC-calibration-for-MT7620.patch @@ -1,20 +1,48 @@ -From: =?UTF-8?q?Tomislav=20Po=C5=BEega?= -Date: Mon, 8 Jan 2018 13:42:27 +0100 -Subject: [PATCH] rt2x00: add RF self TXDC calibration +From patchwork Sat Sep 17 20:27:26 2022 +Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979246 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:27:26 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 05/16] rt2x00: add RF self TXDC calibration for MT7620 +Message-ID: + +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +From: Tomislav Požega Add TX self calibration based on mtk driver. Signed-off-by: Tomislav Požega +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka --- - .../net/wireless/ralink/rt2x00/rt2800lib.c | 51 +++++++++++++++++++ - 1 file changed, 51 insertions(+) +v2: use ++i instead of i = i + 1 in loops + + .../net/wireless/ralink/rt2x00/rt2800lib.c | 48 +++++++++++++++++++ + 1 file changed, 48 insertions(+) --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -8438,6 +8438,56 @@ static void rt2800_init_rfcsr_5592(struc +@@ -8428,6 +8428,53 @@ static void rt2800_init_rfcsr_5592(struc rt2800_led_open_drain_enable(rt2x00dev); } @@ -24,7 +52,6 @@ Signed-off-by: Tomislav Požega + u32 mac0518, mac051c, mac0528, mac052c; + u8 i; + -+ rt2x00_info(rt2x00dev, "RF Tx self calibration start\n"); + mac0518 = rt2800_register_read(rt2x00dev, RF_CONTROL0); + mac051c = rt2800_register_read(rt2x00dev, RF_BYPASS0); + mac0528 = rt2800_register_read(rt2x00dev, RF_CONTROL2); @@ -41,19 +68,19 @@ Signed-off-by: Tomislav Požega + rfb7r1_org = rt2800_rfcsr_read_bank(rt2x00dev, 7, 1); + + rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, 0x4); -+ for (i = 0; i < 100; i = i + 1) { -+ udelay(50); ++ for (i = 0; i < 100; ++i) { ++ usleep_range(50, 100); + rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 5, 1); -+ if((rfvalue & 0x04) != 0x4) ++ if ((rfvalue & 0x04) != 0x4) + break; + } + rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, rfb5r1_org); + + rt2800_rfcsr_write_bank(rt2x00dev, 7, 1, 0x4); -+ for (i = 0; i < 100; i = i + 1) { -+ udelay(50); ++ for (i = 0; i < 100; ++i) { ++ usleep_range(50, 100); + rfvalue = rt2800_rfcsr_read_bank(rt2x00dev, 7, 1); -+ if((rfvalue & 0x04) != 0x4) ++ if ((rfvalue & 0x04) != 0x4) + break; + } + rt2800_rfcsr_write_bank(rt2x00dev, 7, 1, rfb7r1_org); @@ -64,14 +91,12 @@ Signed-off-by: Tomislav Požega + rt2800_register_write(rt2x00dev, RF_BYPASS0, mac051c); + rt2800_register_write(rt2x00dev, RF_CONTROL2, mac0528); + rt2800_register_write(rt2x00dev, RF_BYPASS2, mac052c); -+ -+ rt2x00_info(rt2x00dev, "RF Tx self calibration end\n"); +} + static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev, bool set_bw, bool is_ht40) { -@@ -9045,6 +9095,7 @@ static void rt2800_init_rfcsr_6352(struc +@@ -9035,6 +9082,7 @@ static void rt2800_init_rfcsr_6352(struc rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00); rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C); diff --git a/package/kernel/mac80211/patches/rt2x00/983-rt2x00-add-r-calibration.patch b/package/kernel/mac80211/patches/rt2x00/006-rt2x00-add-r-calibration-for-MT7620.patch similarity index 68% rename from package/kernel/mac80211/patches/rt2x00/983-rt2x00-add-r-calibration.patch rename to package/kernel/mac80211/patches/rt2x00/006-rt2x00-add-r-calibration-for-MT7620.patch index 04ea802117..7c69970166 100644 --- a/package/kernel/mac80211/patches/rt2x00/983-rt2x00-add-r-calibration.patch +++ b/package/kernel/mac80211/patches/rt2x00/006-rt2x00-add-r-calibration-for-MT7620.patch @@ -1,28 +1,68 @@ -From: =?UTF-8?q?Tomislav=20Po=C5=BEega?= -Date: Mon, 8 Jan 2018 13:42:58 +0100 -Subject: [PATCH] rt2x00: add r calibration +From patchwork Sat Sep 17 20:27:41 2022 +Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979247 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:27:41 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 06/16] rt2x00: add r calibration for MT7620 +Message-ID: + +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +From: Tomislav Požega Add r calibration code as found in mtk driver. Signed-off-by: Tomislav Požega +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka --- - .../net/wireless/ralink/rt2x00/rt2800lib.c | 150 ++++++++++++++++++ - 1 file changed, 150 insertions(+) +v2: use rt2800_wait_bbp_rf_ready() + drivers/net/wireless/ralink/rt2x00/rt2800.h | 2 + + .../net/wireless/ralink/rt2x00/rt2800lib.c | 133 ++++++++++++++++++ + 2 files changed, 135 insertions(+) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h +@@ -1016,6 +1016,8 @@ + */ + #define MAC_STATUS_CFG 0x1200 + #define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003) ++#define MAC_STATUS_CFG_BBP_RF_BUSY_TX FIELD32(0x00000001) ++#define MAC_STATUS_CFG_BBP_RF_BUSY_RX FIELD32(0x00000002) + + /* + * PWR_PIN_CFG: --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -8488,6 +8488,155 @@ static void rt2800_rf_self_txdc_cal(stru - rt2x00_info(rt2x00dev, "RF Tx self calibration end\n"); +@@ -8475,6 +8475,138 @@ static void rt2800_rf_self_txdc_cal(stru + rt2800_register_write(rt2x00dev, RF_BYPASS2, mac052c); } +static int rt2800_calcrcalibrationcode(struct rt2x00_dev *rt2x00dev, int d1, int d2) +{ -+ int calcode; -+ calcode = ((d2 - d1) * 1000) / 43; -+ if ((calcode%10) >= 5) ++ int calcode = ((d2 - d1) * 1000) / 43; ++ ++ if ((calcode % 10) >= 5) + calcode += 10; + calcode = (calcode / 10); + @@ -42,8 +82,7 @@ Signed-off-by: Tomislav Požega + char d1 = 0, d2 = 0; + u8 rfvalue; + u32 MAC_RF_BYPASS0, MAC_RF_CONTROL0, MAC_PWR_PIN_CFG; -+ u32 maccfg, macstatus; -+ int i; ++ u32 maccfg; + + saverfb0r1 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 1); + saverfb0r34 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 34); @@ -67,30 +106,14 @@ Signed-off-by: Tomislav Požega + maccfg &= (~0x04); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, maccfg); + -+ for (i = 0; i < 10000; i++) { -+ macstatus = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macstatus & 0x1) -+ udelay(50); -+ else -+ break; -+ } -+ -+ if (i == 10000) ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX))) + rt2x00_warn(rt2x00dev, "Wait MAC Tx Status to MAX !!!\n"); + + maccfg = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + maccfg &= (~0x04); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, maccfg); + -+ for (i = 0; i < 10000; i++) { -+ macstatus = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macstatus & 0x2) -+ udelay(50); -+ else -+ break; -+ } -+ -+ if (i == 10000) ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_RX))) + rt2x00_warn(rt2x00dev, "Wait MAC Rx Status to MAX !!!\n"); + + rfvalue = (MAC_RF_BYPASS0 | 0x3004); @@ -112,7 +135,7 @@ Signed-off-by: Tomislav Požega + + rt2800_bbp_write(rt2x00dev, 47, 0x04); + rt2800_bbp_write(rt2x00dev, 22, 0x80); -+ udelay(100); ++ usleep_range(100, 200); + bytevalue = rt2800_bbp_read(rt2x00dev, 49); + if (bytevalue > 128) + d1 = bytevalue - 256; @@ -122,7 +145,7 @@ Signed-off-by: Tomislav Požega + rt2800_rfcsr_write_bank(rt2x00dev, 0, 35, 0x01); + + rt2800_bbp_write(rt2x00dev, 22, 0x80); -+ udelay(100); ++ usleep_range(100, 200); + bytevalue = rt2800_bbp_read(rt2x00dev, 49); + if (bytevalue > 128) + d2 = bytevalue - 256; @@ -170,7 +193,7 @@ Signed-off-by: Tomislav Požega static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev, bool set_bw, bool is_ht40) { -@@ -9095,6 +9244,7 @@ static void rt2800_init_rfcsr_6352(struc +@@ -9082,6 +9214,7 @@ static void rt2800_init_rfcsr_6352(struc rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00); rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C); diff --git a/package/kernel/mac80211/patches/rt2x00/984-rt2x00-add-RXDCOC-calibration.patch b/package/kernel/mac80211/patches/rt2x00/007-rt2x00-add-RXDCOC-calibration-for-MT7620.patch similarity index 54% rename from package/kernel/mac80211/patches/rt2x00/984-rt2x00-add-RXDCOC-calibration.patch rename to package/kernel/mac80211/patches/rt2x00/007-rt2x00-add-RXDCOC-calibration-for-MT7620.patch index 821af87ec1..8da41e875a 100644 --- a/package/kernel/mac80211/patches/rt2x00/984-rt2x00-add-RXDCOC-calibration.patch +++ b/package/kernel/mac80211/patches/rt2x00/007-rt2x00-add-RXDCOC-calibration-for-MT7620.patch @@ -1,27 +1,54 @@ -From: =?UTF-8?q?Tomislav=20Po=C5=BEega?= -Date: Mon, 8 Jan 2018 13:43:37 +0100 -Subject: [PATCH] rt2x00: add RXDCOC calibration +From patchwork Sat Sep 17 20:27:56 2022 +Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979248 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:27:56 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 07/16] rt2x00: add RXDCOC calibration for MT7620 +Message-ID: + <850b30f652e88de30d79e968af4eb47aa5bc2511.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +From: Tomislav Požega Add RXDCOC calibration code from mtk driver. Signed-off-by: Tomislav Požega +[fixed typo reported by Serge Vasilugin ] +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka --- - .../net/wireless/ralink/rt2x00/rt2800lib.c | 65 +++++++++++++++++++ - 1 file changed, 65 insertions(+) + .../net/wireless/ralink/rt2x00/rt2800lib.c | 60 +++++++++++++++++++ + 1 file changed, 60 insertions(+) --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -8637,6 +8637,70 @@ static void rt2800_r_calibration(struct +@@ -8607,6 +8607,65 @@ static void rt2800_r_calibration(struct rt2800_register_write(rt2x00dev, PWR_PIN_CFG, MAC_PWR_PIN_CFG); } +static void rt2800_rxdcoc_calibration(struct rt2x00_dev *rt2x00dev) +{ + u8 bbpreg = 0; -+ u32 macvalue = 0, macvalue1 = 0; ++ u32 macvalue = 0; + u8 saverfb0r2, saverfb5r4, saverfb7r4, rfvalue; + int i; + @@ -38,15 +65,10 @@ Signed-off-by: Tomislav Požega + macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x8); + -+ for (i = 0; i < 10000; i++) { -+ macvalue1 = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macvalue1 & 0x1) -+ udelay(50); -+ else -+ break; -+ } ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX))) ++ rt2x00_warn(rt2x00dev, "RF TX busy in RX RXDCOC calibration\n"); + -+ saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 0); ++ saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4); + saverfb7r4 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4); + saverfb5r4 = saverfb5r4 & (~0x40); + saverfb7r4 = saverfb7r4 & (~0x40); @@ -63,9 +85,9 @@ Signed-off-by: Tomislav Požega + + for (i = 0; i < 10000; i++) { + bbpreg = rt2800_bbp_read(rt2x00dev, 159); -+ if ((bbpreg & 0x40)==0) ++ if ((bbpreg & 0x40) == 0) + break; -+ udelay(50); ++ usleep_range(50, 100); + } + + bbpreg = rt2800_bbp_read(rt2x00dev, 159); @@ -85,7 +107,7 @@ Signed-off-by: Tomislav Požega static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev, bool set_bw, bool is_ht40) { -@@ -9246,6 +9310,7 @@ static void rt2800_init_rfcsr_6352(struc +@@ -9216,6 +9275,7 @@ static void rt2800_init_rfcsr_6352(struc rt2800_r_calibration(rt2x00dev); rt2800_rf_self_txdc_cal(rt2x00dev); diff --git a/package/kernel/mac80211/patches/rt2x00/985-rt2x00-add-RXIQ-calibration.patch b/package/kernel/mac80211/patches/rt2x00/008-rt2x00-add-RXIQ-calibration-for-MT7620.patch similarity index 64% rename from package/kernel/mac80211/patches/rt2x00/985-rt2x00-add-RXIQ-calibration.patch rename to package/kernel/mac80211/patches/rt2x00/008-rt2x00-add-RXIQ-calibration-for-MT7620.patch index 1c07cc7520..dc516da43a 100644 --- a/package/kernel/mac80211/patches/rt2x00/985-rt2x00-add-RXIQ-calibration.patch +++ b/package/kernel/mac80211/patches/rt2x00/008-rt2x00-add-RXIQ-calibration-for-MT7620.patch @@ -1,32 +1,62 @@ -From: =?UTF-8?q?Tomislav=20Po=C5=BEega?= -Date: Mon, 8 Jan 2018 13:43:56 +0100 -Subject: [PATCH] rt2x00: add RXIQ calibration +From patchwork Sat Sep 17 20:28:10 2022 +Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979249 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:28:10 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 08/16] rt2x00: add RXIQ calibration for MT7620 +Message-ID: + <033a39a697d51f6df258acea4c33608e0944fe4c.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +From: Tomislav Požega Add RXIQ calibration found in mtk driver. With old openwrt builds this gets us ~8Mbps more of RX bandwidth (test with iPA/eLNA layout). Signed-off-by: Tomislav Požega +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka --- - .../net/wireless/ralink/rt2x00/rt2800lib.c | 379 ++++++++++++++++++ - 1 file changed, 379 insertions(+) +v2: use rt2800_wait_bbp_rf_ready(), fix indentation + + .../net/wireless/ralink/rt2x00/rt2800lib.c | 375 ++++++++++++++++++ + 1 file changed, 375 insertions(+) --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -8701,6 +8701,384 @@ static void rt2800_rxdcoc_calibration(st +@@ -8666,6 +8666,380 @@ static void rt2800_rxdcoc_calibration(st rt2800_rfcsr_write_bank(rt2x00dev, 0, 2, saverfb0r2); } -+static u32 rt2800_do_sqrt_accumulation(u32 si) { ++static u32 rt2800_do_sqrt_accumulation(u32 si) ++{ + u32 root, root_pre, bit; + char i; ++ + bit = 1 << 15; + root = 0; + for (i = 15; i >= 0; i = i - 1) { + root_pre = root + bit; -+ if ((root_pre*root_pre) <= si) ++ if ((root_pre * root_pre) <= si) + root = root_pre; + bit = bit >> 1; + } @@ -34,7 +64,8 @@ Signed-off-by: Tomislav Požega + return root; +} + -+static void rt2800_rxiq_calibration(struct rt2x00_dev *rt2x00dev) { ++static void rt2800_rxiq_calibration(struct rt2x00_dev *rt2x00dev) ++{ + u8 rfb0r1, rfb0r2, rfb0r42; + u8 rfb4r0, rfb4r19; + u8 rfb5r3, rfb5r4, rfb5r17, rfb5r18, rfb5r19, rfb5r20; @@ -58,8 +89,8 @@ Signed-off-by: Tomislav Požega + u32 orig_RF_BYPASS1 = 0; + u32 orig_RF_CONTROL3 = 0; + u32 orig_RF_BYPASS3 = 0; -+ u32 macstatus, bbpval1 = 0; -+ u8 rf_vga_table[] = {0x20, 0x21, 0x22, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}; ++ u32 bbpval1 = 0; ++ static const u8 rf_vga_table[] = {0x20, 0x21, 0x22, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}; + + savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + orig_RF_CONTROL0 = rt2800_register_read(rt2x00dev, RF_CONTROL0); @@ -74,16 +105,8 @@ Signed-off-by: Tomislav Požega + + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x0); + -+ for (i = 0; i < 10000; i++) { -+ macstatus = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macstatus & 0x3) -+ udelay(50); -+ else -+ break; -+ } -+ -+ if (i == 10000) -+ rt2x00_warn(rt2x00dev, "Wait MAC Status to MAX !!!\n"); ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY))) ++ rt2x00_warn(rt2x00dev, "Timeout waiting for MAC status in RXIQ calibration\n"); + + bbpval = bbp4 & (~0x18); + bbpval = bbp4 | 0x00; @@ -180,7 +203,7 @@ Signed-off-by: Tomislav Požega + rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001006); + udelay(1); + -+ bbpval = bbp1 & (~ 0x18); ++ bbpval = bbp1 & (~0x18); + bbpval = bbpval | 0x00; + rt2800_bbp_write(rt2x00dev, 1, bbpval); + @@ -199,132 +222,133 @@ Signed-off-by: Tomislav Požega + rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002006); + udelay(1); + -+ bbpval = bbp1 & (~ 0x18); ++ bbpval = bbp1 & (~0x18); + bbpval = bbpval | 0x08; + rt2800_bbp_write(rt2x00dev, 1, bbpval); + + rt2800_bbp_dcoc_write(rt2x00dev, 1, 0x01); + } -+ udelay(500); ++ usleep_range(500, 1500); + + vga_idx = 0; -+ while (vga_idx < 11) { -+ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rf_vga_table[vga_idx]); -+ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rf_vga_table[vga_idx]); ++ while (vga_idx < 11) { ++ rt2800_rfcsr_write_dccal(rt2x00dev, 3, rf_vga_table[vga_idx]); ++ rt2800_rfcsr_write_dccal(rt2x00dev, 4, rf_vga_table[vga_idx]); + -+ rt2800_bbp_dcoc_write(rt2x00dev, 0, 0x93); ++ rt2800_bbp_dcoc_write(rt2x00dev, 0, 0x93); + -+ for (i = 0; i < 10000; i++) { -+ bbpval = rt2800_bbp_read(rt2x00dev, 159); -+ if ((bbpval & 0xff) == 0x93) -+ udelay(50); -+ else -+ break; -+ } -+ -+ if ((bbpval & 0xff) == 0x93) { -+ rt2x00_warn(rt2x00dev, "Fatal Error: Calibration doesn't finish"); -+ goto restore_value; -+ } -+ -+ for (i = 0; i < 5; i++) { -+ u32 bbptemp = 0; -+ u8 value = 0; -+ int result = 0; -+ -+ rt2800_bbp_write(rt2x00dev, 158, 0x1e); -+ rt2800_bbp_write(rt2x00dev, 159, i); -+ rt2800_bbp_write(rt2x00dev, 158, 0x22); -+ value = rt2800_bbp_read(rt2x00dev, 159); -+ bbptemp = bbptemp + (value << 24); -+ rt2800_bbp_write(rt2x00dev, 158, 0x21); -+ value = rt2800_bbp_read(rt2x00dev, 159); -+ bbptemp = bbptemp + (value << 16); -+ rt2800_bbp_write(rt2x00dev, 158, 0x20); -+ value = rt2800_bbp_read(rt2x00dev, 159); -+ bbptemp = bbptemp + (value << 8); -+ rt2800_bbp_write(rt2x00dev, 158, 0x1f); -+ value = rt2800_bbp_read(rt2x00dev, 159); -+ bbptemp = bbptemp + value; -+ -+ if ((i < 2) && (bbptemp & 0x800000)) -+ result = (bbptemp & 0xffffff) - 0x1000000; -+ else if (i == 4) -+ result = bbptemp; -+ else -+ result = bbptemp; -+ -+ if (i == 0) -+ mi = result/4096; -+ else if (i == 1) -+ mq = result/4096; -+ else if (i == 2) -+ si = bbptemp/4096; -+ else if (i == 3) -+ sq = bbptemp/4096; -+ else -+ riq = result/4096; -+ } -+ -+ bbpval1 = si - mi*mi; -+ rt2x00_dbg(rt2x00dev, "RXIQ si=%d, sq=%d, riq=%d, bbpval %d, vga_idx %d", si, sq, riq, bbpval1, vga_idx); -+ -+ if (bbpval1 >= (100*100)) -+ break; -+ -+ if (bbpval1 <= 100) -+ vga_idx = vga_idx + 9; -+ else if (bbpval1 <= 158) -+ vga_idx = vga_idx + 8; -+ else if (bbpval1 <= 251) -+ vga_idx = vga_idx + 7; -+ else if (bbpval1 <= 398) -+ vga_idx = vga_idx + 6; -+ else if (bbpval1 <= 630) -+ vga_idx = vga_idx + 5; -+ else if (bbpval1 <= 1000) -+ vga_idx = vga_idx + 4; -+ else if (bbpval1 <= 1584) -+ vga_idx = vga_idx + 3; -+ else if (bbpval1 <= 2511) -+ vga_idx = vga_idx + 2; ++ for (i = 0; i < 10000; i++) { ++ bbpval = rt2800_bbp_read(rt2x00dev, 159); ++ if ((bbpval & 0xff) == 0x93) ++ usleep_range(50, 100); + else -+ vga_idx = vga_idx + 1; ++ break; ++ } ++ ++ if ((bbpval & 0xff) == 0x93) { ++ rt2x00_warn(rt2x00dev, "Fatal Error: Calibration doesn't finish"); ++ goto restore_value; ++ } ++ for (i = 0; i < 5; i++) { ++ u32 bbptemp = 0; ++ u8 value = 0; ++ int result = 0; ++ ++ rt2800_bbp_write(rt2x00dev, 158, 0x1e); ++ rt2800_bbp_write(rt2x00dev, 159, i); ++ rt2800_bbp_write(rt2x00dev, 158, 0x22); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + (value << 24); ++ rt2800_bbp_write(rt2x00dev, 158, 0x21); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + (value << 16); ++ rt2800_bbp_write(rt2x00dev, 158, 0x20); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + (value << 8); ++ rt2800_bbp_write(rt2x00dev, 158, 0x1f); ++ value = rt2800_bbp_read(rt2x00dev, 159); ++ bbptemp = bbptemp + value; ++ ++ if (i < 2 && (bbptemp & 0x800000)) ++ result = (bbptemp & 0xffffff) - 0x1000000; ++ else if (i == 4) ++ result = bbptemp; ++ else ++ result = bbptemp; ++ ++ if (i == 0) ++ mi = result / 4096; ++ else if (i == 1) ++ mq = result / 4096; ++ else if (i == 2) ++ si = bbptemp / 4096; ++ else if (i == 3) ++ sq = bbptemp / 4096; ++ else ++ riq = result / 4096; + } + -+ sigma_i = rt2800_do_sqrt_accumulation(100*(si - mi*mi)); -+ sigma_q = rt2800_do_sqrt_accumulation(100*(sq - mq*mq)); -+ r_iq = 10*(riq-(mi*mq)); ++ bbpval1 = si - mi * mi; ++ rt2x00_dbg(rt2x00dev, ++ "RXIQ si=%d, sq=%d, riq=%d, bbpval %d, vga_idx %d", ++ si, sq, riq, bbpval1, vga_idx); ++ ++ if (bbpval1 >= (100 * 100)) ++ break; ++ ++ if (bbpval1 <= 100) ++ vga_idx = vga_idx + 9; ++ else if (bbpval1 <= 158) ++ vga_idx = vga_idx + 8; ++ else if (bbpval1 <= 251) ++ vga_idx = vga_idx + 7; ++ else if (bbpval1 <= 398) ++ vga_idx = vga_idx + 6; ++ else if (bbpval1 <= 630) ++ vga_idx = vga_idx + 5; ++ else if (bbpval1 <= 1000) ++ vga_idx = vga_idx + 4; ++ else if (bbpval1 <= 1584) ++ vga_idx = vga_idx + 3; ++ else if (bbpval1 <= 2511) ++ vga_idx = vga_idx + 2; ++ else ++ vga_idx = vga_idx + 1; ++ } ++ ++ sigma_i = rt2800_do_sqrt_accumulation(100 * (si - mi * mi)); ++ sigma_q = rt2800_do_sqrt_accumulation(100 * (sq - mq * mq)); ++ r_iq = 10 * (riq - (mi * mq)); + + rt2x00_dbg(rt2x00dev, "Sigma_i=%d, Sigma_q=%d, R_iq=%d", sigma_i, sigma_q, r_iq); + -+ if (((sigma_i <= 1400 ) && (sigma_i >= 1000)) -+ && ((sigma_i - sigma_q) <= 112) -+ && ((sigma_i - sigma_q) >= -112) -+ && ((mi <= 32) && (mi >= -32)) -+ && ((mq <= 32) && (mq >= -32))) { -+ r_iq = 10*(riq-(mi*mq)); -+ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n", sigma_i, sigma_q, r_iq); ++ if (sigma_i <= 1400 && sigma_i >= 1000 && ++ (sigma_i - sigma_q) <= 112 && ++ (sigma_i - sigma_q) >= -112 && ++ mi <= 32 && mi >= -32 && ++ mq <= 32 && mq >= -32) { ++ r_iq = 10 * (riq - (mi * mq)); ++ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n", ++ sigma_i, sigma_q, r_iq); + -+ g_rx = (1000 * sigma_q) / sigma_i; -+ g_imb = ((-2) * 128 * (1000 - g_rx)) / (1000 + g_rx); -+ ph_rx = (r_iq * 2292) / (sigma_i * sigma_q); -+ rt2x00_info(rt2x00dev, "RXIQ G_imb=%d, Ph_rx=%d\n", g_imb, ph_rx); ++ g_rx = (1000 * sigma_q) / sigma_i; ++ g_imb = ((-2) * 128 * (1000 - g_rx)) / (1000 + g_rx); ++ ph_rx = (r_iq * 2292) / (sigma_i * sigma_q); + -+ if ((ph_rx > 20) || (ph_rx < -20)) { -+ ph_rx = 0; -+ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); -+ } -+ -+ if ((g_imb > 12) || (g_imb < -12)) { -+ g_imb = 0; -+ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); -+ } ++ if (ph_rx > 20 || ph_rx < -20) { ++ ph_rx = 0; ++ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); + } -+ else { ++ ++ if (g_imb > 12 || g_imb < -12) { ++ g_imb = 0; ++ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); ++ } ++ } else { + g_imb = 0; + ph_rx = 0; -+ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n", sigma_i, sigma_q, r_iq); ++ rt2x00_dbg(rt2x00dev, "RXIQ Sigma_i=%d, Sigma_q=%d, R_iq=%d\n", ++ sigma_i, sigma_q, r_iq); + rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); + } + @@ -400,7 +424,7 @@ Signed-off-by: Tomislav Požega static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev, bool set_bw, bool is_ht40) { -@@ -9313,6 +9691,7 @@ static void rt2800_init_rfcsr_6352(struc +@@ -9278,6 +9652,7 @@ static void rt2800_init_rfcsr_6352(struc rt2800_rxdcoc_calibration(rt2x00dev); rt2800_bw_filter_calibration(rt2x00dev, true); rt2800_bw_filter_calibration(rt2x00dev, false); diff --git a/package/kernel/mac80211/patches/rt2x00/009-rt2x00-don-t-run-Rt5592-IQ-calibration-on-MT7620.patch b/package/kernel/mac80211/patches/rt2x00/009-rt2x00-don-t-run-Rt5592-IQ-calibration-on-MT7620.patch new file mode 100644 index 0000000000..6b27f6a705 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/009-rt2x00-don-t-run-Rt5592-IQ-calibration-on-MT7620.patch @@ -0,0 +1,52 @@ +From patchwork Sat Sep 17 20:28:29 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979250 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:28:29 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 09/16] rt2x00: don't run Rt5592 IQ calibration on MT7620 +Message-ID: + <31a1c34ddbd296b82f38c18c9ae7339059215fdc.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +The function rt2800_iq_calibrate is intended for Rt5592 only. +Don't call it for MT7620 which has it's own calibration functions. + +Reported-by: Serge Vasilugin +Signed-off-by: Daniel Golle +--- +v2: test for RT5592 instead of !RT6352 + + drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -4398,7 +4398,8 @@ static void rt2800_config_channel(struct + reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2*rt2x00dev->lna_gain; + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + +- rt2800_iq_calibrate(rt2x00dev, rf->channel); ++ if (rt2x00_rt(rt2x00dev, RT5592)) ++ rt2800_iq_calibrate(rt2x00dev, rf->channel); + } + + if (rt2x00_rt(rt2x00dev, RT6352)) { diff --git a/package/kernel/mac80211/patches/rt2x00/986-rt2x00-add-TX-LOFT-calibration.patch b/package/kernel/mac80211/patches/rt2x00/010-rt2x00-add-TX-LOFT-calibration-for-MT7620.patch similarity index 84% rename from package/kernel/mac80211/patches/rt2x00/986-rt2x00-add-TX-LOFT-calibration.patch rename to package/kernel/mac80211/patches/rt2x00/010-rt2x00-add-TX-LOFT-calibration-for-MT7620.patch index a816aaf52a..bb01ff1dca 100644 --- a/package/kernel/mac80211/patches/rt2x00/986-rt2x00-add-TX-LOFT-calibration.patch +++ b/package/kernel/mac80211/patches/rt2x00/010-rt2x00-add-TX-LOFT-calibration-for-MT7620.patch @@ -1,25 +1,56 @@ -From: =?UTF-8?q?Tomislav=20Po=C5=BEega?= -Date: Thu, 11 Jan 2018 19:53:49 +0100 -Subject: [PATCH] rt2x00: add TX LOFT calibration +From patchwork Sat Sep 17 20:28:43 2022 +Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979251 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:28:43 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 10/16] rt2x00: add TX LOFT calibration for MT7620 +Message-ID: + +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +From: Tomislav Požega Add TX LOFT calibration from mtk driver. Signed-off-by: Tomislav Požega +Signed-off-by: Daniel Golle --- - .../net/wireless/ralink/rt2x00/rt2800lib.c | 938 ++++++++++++++++++ +v2: use helper functions, make tables static const, remove useless + debug prints +v3: don't export function not used anywhere else +Reported-by: kernel test robot + + .../net/wireless/ralink/rt2x00/rt2800lib.c | 902 ++++++++++++++++++ .../net/wireless/ralink/rt2x00/rt2800lib.h | 10 + - 2 files changed, 948 insertions(+) + 2 files changed, 912 insertions(+) --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -9079,6 +9079,943 @@ restore_value: +@@ -9041,6 +9041,907 @@ restore_value: rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl); } -+static void rt2800_rf_configstore(struct rt2x00_dev *rt2x00dev, rf_reg_pair rf_reg_record[][13], u8 chain) ++static void rt2800_rf_configstore(struct rt2x00_dev *rt2x00dev, ++ struct rf_reg_pair rf_reg_record[][13], u8 chain) +{ + u8 rfvalue = 0; + @@ -131,13 +162,11 @@ Signed-off-by: Tomislav Požega + rf_reg_record[CHAIN_1][12].value = rfvalue; + } else { + rt2x00_warn(rt2x00dev, "Unknown chain = %u\n", chain); -+ return; + } -+ -+ return; +} + -+static void rt2800_rf_configrecover(struct rt2x00_dev *rt2x00dev, rf_reg_pair rf_record[][13]) ++static void rt2800_rf_configrecover(struct rt2x00_dev *rt2x00dev, ++ struct rf_reg_pair rf_record[][13]) +{ + u8 chain_index = 0, record_index = 0; + u8 bank = 0, rf_register = 0, value = 0; @@ -148,11 +177,10 @@ Signed-off-by: Tomislav Požega + rf_register = rf_record[chain_index][record_index].reg; + value = rf_record[chain_index][record_index].value; + rt2800_rfcsr_write_bank(rt2x00dev, bank, rf_register, value); -+ rt2x00_dbg(rt2x00dev, "bank: %d, rf_register: %d, value: %x\n", bank, rf_register, value); ++ rt2x00_dbg(rt2x00dev, "bank: %d, rf_register: %d, value: %x\n", ++ bank, rf_register, value); + } + } -+ -+ return; +} + +static void rt2800_setbbptonegenerator(struct rt2x00_dev *rt2x00dev) @@ -170,15 +198,13 @@ Signed-off-by: Tomislav Požega + rt2800_bbp_write(rt2x00dev, 159, 0x3F); + + rt2800_bbp_write(rt2x00dev, 244, 0x40); -+ -+ return; +} + +static u32 rt2800_do_fft_accumulation(struct rt2x00_dev *rt2x00dev, u8 tidx, u8 read_neg) +{ + u32 macvalue = 0; + int fftout_i = 0, fftout_q = 0; -+ u32 ptmp=0, pint = 0; ++ u32 ptmp = 0, pint = 0; + u8 bbp = 0; + u8 tidxi; + @@ -188,7 +214,7 @@ Signed-off-by: Tomislav Požega + bbp = 0x9b; + + while (bbp == 0x9b) { -+ udelay(10); ++ usleep_range(10, 50); + bbp = rt2800_bbp_read(rt2x00dev, 159); + bbp = bbp & 0xff; + } @@ -233,10 +259,11 @@ Signed-off-by: Tomislav Požega + return pint; +} + -+static u32 rt2800_read_fft_accumulation(struct rt2x00_dev *rt2x00dev, u8 tidx) { ++static u32 rt2800_read_fft_accumulation(struct rt2x00_dev *rt2x00dev, u8 tidx) ++{ + u32 macvalue = 0; + int fftout_i = 0, fftout_q = 0; -+ u32 ptmp=0, pint = 0; ++ u32 ptmp = 0, pint = 0; + + rt2800_bbp_write(rt2x00dev, 158, 0xBA); + rt2800_bbp_write(rt2x00dev, 159, tidx); @@ -252,7 +279,6 @@ Signed-off-by: Tomislav Požega + ptmp = (fftout_i * fftout_i); + ptmp = ptmp + (fftout_q * fftout_q); + pint = ptmp; -+ rt2x00_info(rt2x00dev, "I = %d, Q = %d, power = %x\n", fftout_i, fftout_q, pint); + + return pint; +} @@ -266,18 +292,17 @@ Signed-off-by: Tomislav Požega + rt2800_bbp_write(rt2x00dev, 159, bbp); + + if (ch_idx == 0) -+ bbp = (iorq == 0) ? 0xb1: 0xb2; ++ bbp = (iorq == 0) ? 0xb1 : 0xb2; + else -+ bbp = (iorq == 0) ? 0xb8: 0xb9; ++ bbp = (iorq == 0) ? 0xb8 : 0xb9; + + rt2800_bbp_write(rt2x00dev, 158, bbp); + bbp = dc; + rt2800_bbp_write(rt2x00dev, 159, bbp); -+ -+ return; +} + -+static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 alc_idx, u8 dc_result[][RF_ALC_NUM][2]) ++static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, ++ u8 alc_idx, u8 dc_result[][RF_ALC_NUM][2]) +{ + u32 p0 = 0, p1 = 0, pf = 0; + char idx0 = 0, idx1 = 0; @@ -291,8 +316,6 @@ Signed-off-by: Tomislav Požega + + for (bidx = 5; bidx >= 0; bidx--) { + for (iorq = 0; iorq <= 1; iorq++) { -+ rt2x00_dbg(rt2x00dev, "\n========================================================\n"); -+ + if (idxf[iorq] == 0x20) { + idx0 = 0x20; + p0 = pf; @@ -303,16 +326,17 @@ Signed-off-by: Tomislav Požega + p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0); + } + -+ idx1 = idxf[iorq] + ((bidx == 5) ? 0 : ibit); ++ idx1 = idxf[iorq] + (bidx == 5 ? 0 : ibit); + idx1 = idx1 & 0x3F; + rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idx1); + p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0); + -+ rt2x00_dbg(rt2x00dev, "alc=%u, IorQ=%u, idx_final=%2x\n", alc_idx, iorq, idxf[iorq]); -+ rt2x00_dbg(rt2x00dev, "p0=%x, p1=%x, pf=%x, idx_0=%x, idx_1=%x, ibit=%x !\n", p0, p1, pf, idx0, idx1, ibit); ++ rt2x00_dbg(rt2x00dev, "alc=%u, IorQ=%u, idx_final=%2x\n", ++ alc_idx, iorq, idxf[iorq]); ++ rt2x00_dbg(rt2x00dev, "p0=%x, p1=%x, pf=%x, idx_0=%x, idx_1=%x, ibit=%x\n", ++ p0, p1, pf, idx0, idx1, ibit); + -+ if ((bidx != 5) && (pf <= p0) && (pf < p1)) { -+ pf = pf; ++ if (bidx != 5 && pf <= p0 && pf < p1) { + idxf[iorq] = idxf[iorq]; + } else if (p0 < p1) { + pf = p0; @@ -321,17 +345,15 @@ Signed-off-by: Tomislav Požega + pf = p1; + idxf[iorq] = idx1 & 0x3F; + } -+ rt2x00_dbg(rt2x00dev, "IorQ=%u, idx_final[%u]:%x, pf:%8x\n", iorq, iorq, idxf[iorq], pf); ++ rt2x00_dbg(rt2x00dev, "IorQ=%u, idx_final[%u]:%x, pf:%8x\n", ++ iorq, iorq, idxf[iorq], pf); + + rt2800_write_dc(rt2x00dev, ch_idx, 0, iorq, idxf[iorq]); -+ + } + ibit = ibit >> 1; + } + dc_result[ch_idx][alc_idx][0] = idxf[0]; + dc_result[ch_idx][alc_idx][1] = idxf[1]; -+ -+ return; +} + +static void rt2800_iq_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, u8 *ges, u8 *pes) @@ -349,26 +371,25 @@ Signed-off-by: Tomislav Požega + u8 bbp = 0; + char bidx; + -+ rt2x00_info(rt2x00dev, "IQCalibration Start!\n"); + for (bidx = 5; bidx >= 1; bidx--) { + for (gop = 0; gop < 2; gop++) { -+ rt2x00_dbg(rt2x00dev, "\n========================================================\n"); -+ -+ if ((gop == 1) || (bidx < 4)) { ++ if (gop == 1 || bidx < 4) { + if (gop == 0) + iq_err = gerr; + else + iq_err = perr; + + first_search = (gop == 0) ? (bidx == 3) : (bidx == 5); -+ touch_neg_max = (gop) ? ((iq_err & 0x0F) == 0x08) : ((iq_err & 0x3F) == 0x20); ++ touch_neg_max = (gop) ? ((iq_err & 0x0F) == 0x08) : ++ ((iq_err & 0x3F) == 0x20); + + if (touch_neg_max) { + p0 = pf; + idx0 = iq_err; + } else { + idx0 = iq_err - ibit; -+ bbp = (ch_idx == 0) ? ((gop == 0) ? 0x28 : 0x29): ((gop == 0) ? 0x46 : 0x47); ++ bbp = (ch_idx == 0) ? ((gop == 0) ? 0x28 : 0x29) : ++ ((gop == 0) ? 0x46 : 0x47); + + rt2800_bbp_write(rt2x00dev, 158, bbp); + rt2800_bbp_write(rt2x00dev, 159, idx0); @@ -379,26 +400,30 @@ Signed-off-by: Tomislav Požega + idx1 = iq_err + (first_search ? 0 : ibit); + idx1 = (gop == 0) ? (idx1 & 0x0F) : (idx1 & 0x3F); + -+ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 : (gop == 0) ? 0x46 : 0x47; ++ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 : ++ (gop == 0) ? 0x46 : 0x47; + + rt2800_bbp_write(rt2x00dev, 158, bbp); + rt2800_bbp_write(rt2x00dev, 159, idx1); + + p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1); + -+ rt2x00_dbg(rt2x00dev, "p0=%x, p1=%x, pwer_final=%x, idx0=%x, idx1=%x, iq_err=%x, gop=%d, ibit=%x !\n", p0, p1, pf, idx0, idx1, iq_err, gop, ibit); ++ rt2x00_dbg(rt2x00dev, ++ "p0=%x, p1=%x, pwer_final=%x, idx0=%x, idx1=%x, iq_err=%x, gop=%d, ibit=%x\n", ++ p0, p1, pf, idx0, idx1, iq_err, gop, ibit); + -+ if ((!first_search) && (pf <= p0) && (pf < p1)) { -+ pf = pf; -+ } else if (p0 < p1) { -+ pf = p0; -+ iq_err = idx0; -+ } else { -+ pf = p1; -+ iq_err = idx1; ++ if (!(!first_search && pf <= p0 && pf < p1)) { ++ if (p0 < p1) { ++ pf = p0; ++ iq_err = idx0; ++ } else { ++ pf = p1; ++ iq_err = idx1; ++ } + } + -+ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 : (gop == 0) ? 0x46 : 0x47; ++ bbp = (ch_idx == 0) ? (gop == 0) ? 0x28 : 0x29 : ++ (gop == 0) ? 0x46 : 0x47; + + rt2800_bbp_write(rt2x00dev, 158, bbp); + rt2800_bbp_write(rt2x00dev, 159, iq_err); @@ -408,8 +433,8 @@ Signed-off-by: Tomislav Požega + else + perr = iq_err; + -+ rt2x00_dbg(rt2x00dev, "IQCalibration pf=%8x (%2x, %2x) !\n", pf, gerr & 0x0F, perr & 0x3F); -+ ++ rt2x00_dbg(rt2x00dev, "IQCalibration pf=%8x (%2x, %2x) !\n", ++ pf, gerr & 0x0F, perr & 0x3F); + } + } + @@ -438,25 +463,21 @@ Signed-off-by: Tomislav Požega + rt2800_bbp_write(rt2x00dev, 159, pef & 0x3F); + + p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 1); -+ if ((gef == gsta) && (pef == psta)) { ++ if (gef == gsta && pef == psta) { ++ pf = p1; ++ gerr = gef; ++ perr = pef; ++ } else if (pf > p1) { + pf = p1; + gerr = gef; + perr = pef; + } -+ else if (pf > p1){ -+ pf = p1; -+ gerr = gef; -+ perr = pef; -+ } -+ rt2x00_dbg(rt2x00dev, "Fine IQCalibration p1=%8x pf=%8x (%2x, %2x) !\n", p1, pf, gef & 0x0F, pef & 0x3F); ++ rt2x00_dbg(rt2x00dev, "Fine IQCalibration p1=%8x pf=%8x (%2x, %2x) !\n", ++ p1, pf, gef & 0x0F, pef & 0x3F); + } + + ges[ch_idx] = gerr & 0x0F; + pes[ch_idx] = perr & 0x3F; -+ -+ rt2x00_info(rt2x00dev, "IQCalibration Done! CH = %u, (gain=%2x, phase=%2x)\n", ch_idx, gerr & 0x0F, perr & 0x3F); -+ -+ return; +} + +static void rt2800_rf_aux_tx0_loopback(struct rt2x00_dev *rt2x00dev) @@ -493,9 +514,9 @@ Signed-off-by: Tomislav Požega + rt2800_rfcsr_write_bank(rt2x00dev, 7, 20, 0x20); +} + -+void rt2800_loft_iq_calibration(struct rt2x00_dev *rt2x00dev) ++static void rt2800_loft_iq_calibration(struct rt2x00_dev *rt2x00dev) +{ -+ rf_reg_pair rf_store[CHAIN_NUM][13]; ++ struct rf_reg_pair rf_store[CHAIN_NUM][13]; + u32 macorg1 = 0; + u32 macorg2 = 0; + u32 macorg3 = 0; @@ -504,7 +525,7 @@ Signed-off-by: Tomislav Požega + u32 orig528 = 0; + u32 orig52c = 0; + -+ u32 savemacsysctrl = 0, mtxcycle = 0; ++ u32 savemacsysctrl = 0; + u32 macvalue = 0; + u32 mac13b8 = 0; + u32 p0 = 0, p1 = 0; @@ -513,11 +534,8 @@ Signed-off-by: Tomislav Požega + u8 rfvalue; + u8 loft_dc_search_result[CHAIN_NUM][RF_ALC_NUM][2]; + u8 ger[CHAIN_NUM], per[CHAIN_NUM]; -+ u8 rf_gain[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x0c}; -+ u8 rfvga_gain_table[] = {0x24, 0x25, 0x26, 0x27, 0x28, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3F}; + + u8 vga_gain[] = {14, 14}; -+ u8 bbp_2324gain[] = {0x16, 0x14, 0x12, 0x10, 0x0c, 0x08}; + u8 bbp = 0, ch_idx = 0, rf_alc_idx = 0, idx = 0; + u8 bbpr30, rfb0r39, rfb0r42; + u8 bbpr1; @@ -525,6 +543,11 @@ Signed-off-by: Tomislav Požega + u8 bbpr241, bbpr242; + u8 count_step; + ++ static const u8 rf_gain[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x0c}; ++ static const u8 rfvga_gain_table[] = {0x24, 0x25, 0x26, 0x27, 0x28, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, ++ 0x31, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3F}; ++ static const u8 bbp_2324gain[] = {0x16, 0x14, 0x12, 0x10, 0x0c, 0x08}; ++ + savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + macorg1 = rt2800_register_read(rt2x00dev, TX_PIN_CFG); + macorg2 = rt2800_register_read(rt2x00dev, RF_CONTROL0); @@ -539,29 +562,18 @@ Signed-off-by: Tomislav Požega + macvalue &= (~0x04); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); + -+ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { -+ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macvalue & 0x01) -+ udelay(50); -+ else -+ break; -+ } ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX))) ++ rt2x00_warn(rt2x00dev, "RF TX busy in LOFT IQ calibration\n"); + + macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + macvalue &= (~0x08); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); + -+ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { -+ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macvalue & 0x02) -+ udelay(50); -+ else -+ break; -+ } ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_RX))) ++ rt2x00_warn(rt2x00dev, "RF RX busy in LOFT IQ calibration\n"); + -+ for (ch_idx = 0; ch_idx < 2; ch_idx++) { ++ for (ch_idx = 0; ch_idx < 2; ch_idx++) + rt2800_rf_configstore(rt2x00dev, rf_store, ch_idx); -+ } + + bbpr30 = rt2800_bbp_read(rt2x00dev, 30); + rfb0r39 = rt2800_rfcsr_read_bank(rt2x00dev, 0, 39); @@ -576,7 +588,7 @@ Signed-off-by: Tomislav Požega + + rt2800_setbbptonegenerator(rt2x00dev); + -+ for (ch_idx = 0; ch_idx < 2; ch_idx ++) { ++ for (ch_idx = 0; ch_idx < 2; ch_idx++) { + rt2800_bbp_write(rt2x00dev, 23, 0x00); + rt2800_bbp_write(rt2x00dev, 24, 0x00); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00); @@ -586,18 +598,17 @@ Signed-off-by: Tomislav Požega + rt2800_register_write(rt2x00dev, 0x13b8, 0x10); + udelay(1); + -+ if (ch_idx == 0) { ++ if (ch_idx == 0) + rt2800_rf_aux_tx0_loopback(rt2x00dev); -+ } else { ++ else + rt2800_rf_aux_tx1_loopback(rt2x00dev); -+ } ++ + udelay(1); + -+ if (ch_idx == 0) { ++ if (ch_idx == 0) + rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00001004); -+ } else { ++ else + rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x00002004); -+ } + + rt2800_bbp_write(rt2x00dev, 158, 0x05); + rt2800_bbp_write(rt2x00dev, 159, 0x00); @@ -623,7 +634,8 @@ Signed-off-by: Tomislav Požega + + if (rf_alc_idx == 0) { + rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x21); -+ for (;vga_gain[ch_idx] > 0;vga_gain[ch_idx] = vga_gain[ch_idx] - 2) { ++ for (; vga_gain[ch_idx] > 0; ++ vga_gain[ch_idx] = vga_gain[ch_idx] - 2) { + rfvalue = rfvga_gain_table[vga_gain[ch_idx]]; + rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue); + rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue); @@ -633,15 +645,15 @@ Signed-off-by: Tomislav Požega + rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x21); + p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x0A, 0); + rt2x00_dbg(rt2x00dev, "LOFT AGC %d %d\n", p0, p1); -+ if ((p0 < 7000*7000) && (p1 < (7000*7000))) { ++ if ((p0 < 7000 * 7000) && (p1 < (7000 * 7000))) + break; -+ } + } + + rt2800_write_dc(rt2x00dev, ch_idx, 0, 0, 0x00); + rt2800_write_dc(rt2x00dev, ch_idx, 0, 1, 0x00); + -+ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n",vga_gain[ch_idx], rfvga_gain_table[vga_gain[ch_idx]]); ++ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n", vga_gain[ch_idx], ++ rfvga_gain_table[vga_gain[ch_idx]]); + + if (vga_gain[ch_idx] < 0) + vga_gain[ch_idx] = 0; @@ -659,7 +671,7 @@ Signed-off-by: Tomislav Požega + for (rf_alc_idx = 0; rf_alc_idx < 3; rf_alc_idx++) { + for (idx = 0; idx < 4; idx++) { + rt2800_bbp_write(rt2x00dev, 158, 0xB0); -+ bbp = (idx<<2) + rf_alc_idx; ++ bbp = (idx << 2) + rf_alc_idx; + rt2800_bbp_write(rt2x00dev, 159, bbp); + rt2x00_dbg(rt2x00dev, " ALC %2x,", bbp); + @@ -720,8 +732,6 @@ Signed-off-by: Tomislav Požega + rt2800_register_write(rt2x00dev, RF_BYPASS2, orig52c); + rt2800_register_write(rt2x00dev, 0x13b8, mac13b8); + -+ rt2x00_info(rt2x00dev, "LOFT Calibration Done!\n"); -+ + savemacsysctrl = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + macorg1 = rt2800_register_read(rt2x00dev, TX_PIN_CFG); + macorg2 = rt2800_register_read(rt2x00dev, RF_CONTROL0); @@ -738,24 +748,16 @@ Signed-off-by: Tomislav Požega + macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + macvalue &= (~0x04); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); -+ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { -+ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macvalue & 0x01) -+ udelay(50); -+ else -+ break; -+ } ++ ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_TX))) ++ rt2x00_warn(rt2x00dev, "RF TX busy in LOFT IQ calibration\n"); + + macvalue = rt2800_register_read(rt2x00dev, MAC_SYS_CTRL); + macvalue &= (~0x08); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, macvalue); -+ for (mtxcycle = 0; mtxcycle < 10000; mtxcycle++) { -+ macvalue = rt2800_register_read(rt2x00dev, MAC_STATUS_CFG); -+ if (macvalue & 0x02) -+ udelay(50); -+ else -+ break; -+ } ++ ++ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY_RX))) ++ rt2x00_warn(rt2x00dev, "RF RX busy in LOFT IQ calibration\n"); + + if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { + rt2800_register_write(rt2x00dev, RF_CONTROL3, 0x00000101); @@ -791,9 +793,8 @@ Signed-off-by: Tomislav Požega + + rt2800_register_write(rt2x00dev, 0x13b8, 0x00000010); + -+ for (ch_idx = 0; ch_idx < 2; ch_idx++) { ++ for (ch_idx = 0; ch_idx < 2; ch_idx++) + rt2800_rf_configstore(rt2x00dev, rf_store, ch_idx); -+ } + + rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x3B); + rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x3B); @@ -803,7 +804,7 @@ Signed-off-by: Tomislav Požega + rt2800_bbp_write(rt2x00dev, 158, 0xB0); + rt2800_bbp_write(rt2x00dev, 159, 0x80); + -+ for (ch_idx = 0; ch_idx < 2; ch_idx ++) { ++ for (ch_idx = 0; ch_idx < 2; ch_idx++) { + rt2800_bbp_write(rt2x00dev, 23, 0x00); + rt2800_bbp_write(rt2x00dev, 24, 0x00); + @@ -846,7 +847,7 @@ Signed-off-by: Tomislav Požega + count_step = 2; + } + -+ for (;vga_gain[ch_idx] < 19; vga_gain[ch_idx]=(vga_gain[ch_idx] + count_step)) { ++ for (; vga_gain[ch_idx] < 19; vga_gain[ch_idx] = (vga_gain[ch_idx] + count_step)) { + rfvalue = rfvga_gain_table[vga_gain[ch_idx]]; + rt2800_rfcsr_write_dccal(rt2x00dev, 3, rfvalue); + rt2800_rfcsr_write_dccal(rt2x00dev, 4, rfvalue); @@ -855,37 +856,35 @@ Signed-off-by: Tomislav Požega + rt2800_bbp_write(rt2x00dev, 158, bbp); + rt2800_bbp_write(rt2x00dev, 159, 0x00); + p0 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 0); -+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) + p0_idx10 = rt2800_read_fft_accumulation(rt2x00dev, 0x0A); -+ } + + bbp = (ch_idx == 0) ? 0x29 : 0x47; + rt2800_bbp_write(rt2x00dev, 158, bbp); + rt2800_bbp_write(rt2x00dev, 159, 0x21); + p1 = rt2800_do_fft_accumulation(rt2x00dev, 0x14, 0); -+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags)) { ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags)) + p1_idx10 = rt2800_read_fft_accumulation(rt2x00dev, 0x0A); -+ } + + rt2x00_dbg(rt2x00dev, "IQ AGC %d %d\n", p0, p1); + + if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { + rt2x00_dbg(rt2x00dev, "IQ AGC IDX 10 %d %d\n", p0_idx10, p1_idx10); -+ if ((p0_idx10 > 7000*7000) || (p1_idx10 > 7000*7000)) { -+ if (vga_gain[ch_idx]!=0) -+ vga_gain[ch_idx] = vga_gain[ch_idx]-1; ++ if ((p0_idx10 > 7000 * 7000) || (p1_idx10 > 7000 * 7000)) { ++ if (vga_gain[ch_idx] != 0) ++ vga_gain[ch_idx] = vga_gain[ch_idx] - 1; + break; + } + } + -+ if ((p0 > 2500*2500) || (p1 > 2500*2500)) { ++ if ((p0 > 2500 * 2500) || (p1 > 2500 * 2500)) + break; -+ } + } + + if (vga_gain[ch_idx] > 18) + vga_gain[ch_idx] = 18; -+ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n",vga_gain[ch_idx], rfvga_gain_table[vga_gain[ch_idx]]); ++ rt2x00_dbg(rt2x00dev, "Used VGA %d %x\n", vga_gain[ch_idx], ++ rfvga_gain_table[vga_gain[ch_idx]]); + + bbp = (ch_idx == 0) ? 0x29 : 0x47; + rt2800_bbp_write(rt2x00dev, 158, bbp); @@ -930,9 +929,8 @@ Signed-off-by: Tomislav Požega + rt2800_rfcsr_write_bank(rt2x00dev, 0, 39, rfb0r39); + rt2800_rfcsr_write_bank(rt2x00dev, 0, 42, rfb0r42); + -+ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) { ++ if (test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags)) + rt2800_bbp_write(rt2x00dev, 4, bbpr4); -+ } + + rt2800_bbp_write(rt2x00dev, 21, 0x01); + udelay(1); @@ -950,16 +948,12 @@ Signed-off-by: Tomislav Požega + rt2800_register_write(rt2x00dev, RF_BYPASS3, macorg5); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, savemacsysctrl); + rt2800_register_write(rt2x00dev, 0x13b8, mac13b8); -+ -+ rt2x00_info(rt2x00dev, "TX IQ Calibration Done!\n"); -+ -+ return; +} + static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev, bool set_bw, bool is_ht40) { -@@ -9691,6 +10628,7 @@ static void rt2800_init_rfcsr_6352(struc +@@ -9653,6 +10554,7 @@ static void rt2800_init_rfcsr_6352(struc rt2800_rxdcoc_calibration(rt2x00dev); rt2800_bw_filter_calibration(rt2x00dev, true); rt2800_bw_filter_calibration(rt2x00dev, false); @@ -978,11 +972,11 @@ Signed-off-by: Tomislav Požega +#define RF_ALC_NUM 6 +#define CHAIN_NUM 2 + -+typedef struct rf_reg_pair { ++struct rf_reg_pair { + u8 bank; + u8 reg; + u8 value; -+} rf_reg_pair; ++}; /* RT2800 driver data structure */ struct rt2800_drv_data { diff --git a/package/kernel/mac80211/patches/rt2x00/011-rt2x00-move-helper-functions-up-in-file.patch b/package/kernel/mac80211/patches/rt2x00/011-rt2x00-move-helper-functions-up-in-file.patch new file mode 100644 index 0000000000..07a32b17be --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/011-rt2x00-move-helper-functions-up-in-file.patch @@ -0,0 +1,94 @@ +From patchwork Sat Sep 17 20:28:58 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979252 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:28:58 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 11/16] rt2x00: move helper functions up in file +Message-ID: + +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +Move register access helper functions up to the head of the file so +they can be used in all functions. + +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + .../net/wireless/ralink/rt2x00/rt2800lib.c | 40 +++++++++---------- + 1 file changed, 20 insertions(+), 20 deletions(-) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -198,6 +198,26 @@ static void rt2800_rfcsr_write_dccal(str + rt2800_rfcsr_write_bank(rt2x00dev, 7, reg, value); + } + ++static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev, ++ const u8 reg, const u8 value) ++{ ++ rt2800_bbp_write(rt2x00dev, 158, reg); ++ rt2800_bbp_write(rt2x00dev, 159, value); ++} ++ ++static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg) ++{ ++ rt2800_bbp_write(rt2x00dev, 158, reg); ++ return rt2800_bbp_read(rt2x00dev, 159); ++} ++ ++static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev, ++ const u8 reg, const u8 value) ++{ ++ rt2800_bbp_write(rt2x00dev, 195, reg); ++ rt2800_bbp_write(rt2x00dev, 196, value); ++} ++ + static u8 rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word) + { +@@ -6947,26 +6967,6 @@ static void rt2800_init_bbp_5592(struct + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + } + +-static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev, +- const u8 reg, const u8 value) +-{ +- rt2800_bbp_write(rt2x00dev, 195, reg); +- rt2800_bbp_write(rt2x00dev, 196, value); +-} +- +-static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev, +- const u8 reg, const u8 value) +-{ +- rt2800_bbp_write(rt2x00dev, 158, reg); +- rt2800_bbp_write(rt2x00dev, 159, value); +-} +- +-static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg) +-{ +- rt2800_bbp_write(rt2x00dev, 158, reg); +- return rt2800_bbp_read(rt2x00dev, 159); +-} +- + static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev) + { + u8 bbp; diff --git a/package/kernel/mac80211/patches/rt2x00/012-rt2x00-fix-HT20-HT40-bandwidth-switch-on-MT7620.patch b/package/kernel/mac80211/patches/rt2x00/012-rt2x00-fix-HT20-HT40-bandwidth-switch-on-MT7620.patch new file mode 100644 index 0000000000..e989205ba2 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/012-rt2x00-fix-HT20-HT40-bandwidth-switch-on-MT7620.patch @@ -0,0 +1,56 @@ +From patchwork Sat Sep 17 20:29:13 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979253 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:29:13 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 12/16] rt2x00: fix HT20/HT40 bandwidth switch on MT7620 +Message-ID: + <1664d89ba149f7b0bcec18b2a2abaedf49654507.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +Add missing configuration of the channel bandwidth filter to the +channel setup function for MT7620. + +Reported-by: Serge Vasilugin +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -3855,6 +3855,14 @@ static void rt2800_config_channel_rf7620 + rfcsr |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 7, 59, rfcsr); + } ++ ++ if (conf_is_ht40(conf)) { ++ rt2800_bbp_glrt_write(rt2x00dev, 141, 0x10); ++ rt2800_bbp_glrt_write(rt2x00dev, 157, 0x2f); ++ } else { ++ rt2800_bbp_glrt_write(rt2x00dev, 141, 0x1a); ++ rt2800_bbp_glrt_write(rt2x00dev, 157, 0x40); ++ } + } + + static void rt2800_config_alc(struct rt2x00_dev *rt2x00dev, diff --git a/package/kernel/mac80211/patches/rt2x00/013-rt2x00-set-correct-TX_SW_CFG1-MAC-register-for-MT7620.patch b/package/kernel/mac80211/patches/rt2x00/013-rt2x00-set-correct-TX_SW_CFG1-MAC-register-for-MT7620.patch new file mode 100644 index 0000000000..3d7af17b16 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/013-rt2x00-set-correct-TX_SW_CFG1-MAC-register-for-MT7620.patch @@ -0,0 +1,52 @@ +From patchwork Sat Sep 17 20:29:26 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979254 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:29:26 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 13/16] rt2x00: set correct TX_SW_CFG1 MAC register for + MT7620 +Message-ID: + <4be38975ce600a34249e12d09a3cb758c6e71071.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +Set correct TX_SW_CFG1 MAC register as it is done also in v3 of the +vendor driver[1]. + +[1]: https://gitlab.com/dm38/padavan-ng/-/blob/master/trunk/proprietary/rt_wifi/rtpci/3.0.X.X/mt76x2/chips/rt6352.c#L531 +Reported-by: Serge Vasilugin +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -5966,7 +5966,7 @@ static int rt2800_init_registers(struct + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); + } else if (rt2x00_rt(rt2x00dev, RT6352)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401); +- rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0000); ++ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000); + rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0); diff --git a/package/kernel/mac80211/patches/rt2x00/014-rt2x00-set-VGC-gain-for-both-chains-of-MT7620.patch b/package/kernel/mac80211/patches/rt2x00/014-rt2x00-set-VGC-gain-for-both-chains-of-MT7620.patch new file mode 100644 index 0000000000..27b8d92cd2 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/014-rt2x00-set-VGC-gain-for-both-chains-of-MT7620.patch @@ -0,0 +1,50 @@ +From patchwork Sat Sep 17 20:29:40 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979255 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:29:40 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 14/16] rt2x00: set VGC gain for both chains of MT7620 +Message-ID: + <29e161397e5c9d9399da0fe87d44458aa2b90a78.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +Set bbp66 for all chains of the MT7620. + +Reported-by: Serge Vasilugin +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -5743,7 +5743,8 @@ static inline void rt2800_set_vgc(struct + if (qual->vgc_level != vgc_level) { + if (rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || +- rt2x00_rt(rt2x00dev, RT3883)) { ++ rt2x00_rt(rt2x00dev, RT3883) || ++ rt2x00_rt(rt2x00dev, RT6352)) { + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, + vgc_level); + } else if (rt2x00_rt(rt2x00dev, RT5592)) { diff --git a/package/kernel/mac80211/patches/rt2x00/015-rt2x00-set-SoC-wmac-clock-register.patch b/package/kernel/mac80211/patches/rt2x00/015-rt2x00-set-SoC-wmac-clock-register.patch new file mode 100644 index 0000000000..3cadc91da6 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/015-rt2x00-set-SoC-wmac-clock-register.patch @@ -0,0 +1,70 @@ +From patchwork Sat Sep 17 20:29:55 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979256 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +Date: Sat, 17 Sep 2022 21:29:55 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 15/16] rt2x00: set SoC wmac clock register +Message-ID: + <3e275d259f476f597dab91a9c395015ef3fe3284.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +Instead of using the default value 33 (pci), set US_CYC_CNT init based +on Programming guide: +If available, set chipset bus clock with fallback to cpu clock/3. + +Reported-by: Serge Vasilugin +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + .../net/wireless/ralink/rt2x00/rt2800lib.c | 21 +++++++++++++++++++ + 1 file changed, 21 insertions(+) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -6229,6 +6229,27 @@ static int rt2800_init_registers(struct + reg = rt2800_register_read(rt2x00dev, US_CYC_CNT); + rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 125); + rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); ++ } else if (rt2x00_is_soc(rt2x00dev)) { ++ struct clk *clk = clk_get_sys("bus", NULL); ++ int rate; ++ ++ if (IS_ERR(clk)) { ++ clk = clk_get_sys("cpu", NULL); ++ ++ if (IS_ERR(clk)) { ++ rate = 125; ++ } else { ++ rate = clk_get_rate(clk) / 3000000; ++ clk_put(clk); ++ } ++ } else { ++ rate = clk_get_rate(clk) / 1000000; ++ clk_put(clk); ++ } ++ ++ reg = rt2800_register_read(rt2x00dev, US_CYC_CNT); ++ rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, rate); ++ rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); + } + + reg = rt2800_register_read(rt2x00dev, HT_FBK_CFG0); diff --git a/package/kernel/mac80211/patches/rt2x00/016-rt2x00-correctly-set-BBP-register-86-for-MT7620.patch b/package/kernel/mac80211/patches/rt2x00/016-rt2x00-correctly-set-BBP-register-86-for-MT7620.patch new file mode 100644 index 0000000000..5ddbbf1dd6 --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/016-rt2x00-correctly-set-BBP-register-86-for-MT7620.patch @@ -0,0 +1,79 @@ +From patchwork Sat Sep 17 20:30:09 2022 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 12979257 +X-Patchwork-Delegate: kvalo@adurom.com +Return-Path: +X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on + aws-us-west-2-korg-lkml-1.web.codeaurora.org +Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) + by smtp.lore.kernel.org (Postfix) with ESMTP id E9118ECAAA1 + for ; + Sat, 17 Sep 2022 20:30:22 +0000 (UTC) +Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand + id S229639AbiIQUaV (ORCPT + ); + Sat, 17 Sep 2022 16:30:21 -0400 +Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53912 "EHLO + lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S229628AbiIQUaT (ORCPT + ); + Sat, 17 Sep 2022 16:30:19 -0400 +Received: from fudo.makrotopia.org (fudo.makrotopia.org + [IPv6:2a07:2ec0:3002::71]) + by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1AEA822B27 + for ; + Sat, 17 Sep 2022 13:30:16 -0700 (PDT) +Received: from local + by fudo.makrotopia.org with esmtpsa + (TLS1.3:TLS_AES_256_GCM_SHA384:256) + (Exim 4.96) + (envelope-from ) + id 1oZeS7-0003ra-0k; + Sat, 17 Sep 2022 22:30:15 +0200 +Date: Sat, 17 Sep 2022 21:30:09 +0100 +From: Daniel Golle +To: linux-wireless@vger.kernel.org, Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + "David S. Miller" , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg +Subject: [PATCH v3 16/16] rt2x00: correctly set BBP register 86 for MT7620 +Message-ID: + <257267247ee4fa7ebc6a5d0c4948b3f8119c0d77.1663445157.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: linux-wireless@vger.kernel.org + +Instead of 0 set the correct value for BBP register 86 for MT7620. + +Reported-by: Serge Vasilugin +Signed-off-by: Daniel Golle +Acked-by: Stanislaw Gruszka +--- + drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -4225,7 +4225,10 @@ static void rt2800_config_channel(struct + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); +- rt2800_bbp_write(rt2x00dev, 86, 0); ++ if (rt2x00_rt(rt2x00dev, RT6352)) ++ rt2800_bbp_write(rt2x00dev, 86, 0x38); ++ else ++ rt2800_bbp_write(rt2x00dev, 86, 0); + } + + if (rf->channel <= 14) { diff --git a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch index bd33b2efbd..ba16e85510 100644 --- a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch +++ b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch @@ -48,7 +48,7 @@ obj-$(CPTCFG_RT2X00_LIB_MMIO) += rt2x00mmio.o --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h -@@ -37,6 +37,8 @@ struct rt2800_drv_data { +@@ -47,6 +47,8 @@ struct rt2800_drv_data { struct ieee80211_sta *wcid_to_sta[STA_IDS_SIZE]; }; @@ -57,7 +57,7 @@ struct rt2800_ops { u32 (*register_read)(struct rt2x00_dev *rt2x00dev, const unsigned int offset); -@@ -135,6 +137,15 @@ static inline int rt2800_read_eeprom(str +@@ -145,6 +147,15 @@ static inline int rt2800_read_eeprom(str { const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; @@ -127,7 +127,7 @@ DECLARE_KFIFO_PTR(txstatus_fifo, u32); --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c -@@ -1401,6 +1401,10 @@ int rt2x00lib_probe_dev(struct rt2x00_de +@@ -1419,6 +1419,10 @@ int rt2x00lib_probe_dev(struct rt2x00_de INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep); @@ -138,7 +138,7 @@ /* * Let the driver probe the device to detect the capabilities. */ -@@ -1541,6 +1545,11 @@ void rt2x00lib_remove_dev(struct rt2x00_ +@@ -1559,6 +1563,11 @@ void rt2x00lib_remove_dev(struct rt2x00_ * Free the driver data. */ kfree(rt2x00dev->drv_data); @@ -193,7 +193,7 @@ + if (pdata && pdata->eeprom_file_name) + return pdata->eeprom_file_name; + -+ return NULL ++ return NULL; +} + +static int rt2x00lib_request_eeprom_file(struct rt2x00_dev *rt2x00dev) diff --git a/package/kernel/mac80211/patches/rt2x00/603-rt2x00-of_load_eeprom_filename.patch b/package/kernel/mac80211/patches/rt2x00/603-rt2x00-of_load_eeprom_filename.patch index 9dffef1812..431e090237 100644 --- a/package/kernel/mac80211/patches/rt2x00/603-rt2x00-of_load_eeprom_filename.patch +++ b/package/kernel/mac80211/patches/rt2x00/603-rt2x00-of_load_eeprom_filename.patch @@ -8,7 +8,7 @@ #include "rt2x00.h" #include "rt2x00lib.h" -@@ -34,11 +35,21 @@ static const char * +@@ -34,10 +35,20 @@ static const char * rt2x00lib_get_eeprom_file_name(struct rt2x00_dev *rt2x00dev) { struct rt2x00_platform_data *pdata = rt2x00dev->dev->platform_data; @@ -20,14 +20,12 @@ if (pdata && pdata->eeprom_file_name) return pdata->eeprom_file_name; -- return NULL +#ifdef CONFIG_OF + np = rt2x00dev->dev->of_node; + if (np && of_property_read_string(np, "ralink,eeprom", &eep) == 0) + return eep; +#endif + -+ return NULL; + return NULL; } - static int rt2x00lib_request_eeprom_file(struct rt2x00_dev *rt2x00dev) diff --git a/package/kernel/mac80211/patches/rt2x00/610-rt2x00-change-led-polarity-from-OF.patch b/package/kernel/mac80211/patches/rt2x00/610-rt2x00-change-led-polarity-from-OF.patch index d78b76d7f5..acc8a8edb8 100644 --- a/package/kernel/mac80211/patches/rt2x00/610-rt2x00-change-led-polarity-from-OF.patch +++ b/package/kernel/mac80211/patches/rt2x00/610-rt2x00-change-led-polarity-from-OF.patch @@ -8,7 +8,7 @@ #include "rt2x00.h" #include "rt2800lib.h" -@@ -9549,6 +9550,17 @@ static int rt2800_init_eeprom(struct rt2 +@@ -11131,6 +11132,17 @@ static int rt2800_init_eeprom(struct rt2 rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); diff --git a/package/kernel/mac80211/patches/rt2x00/611-rt2x00-add-AP+STA-support.patch b/package/kernel/mac80211/patches/rt2x00/611-rt2x00-add-AP+STA-support.patch index 0da9356e0c..5ef5fc8def 100644 --- a/package/kernel/mac80211/patches/rt2x00/611-rt2x00-add-AP+STA-support.patch +++ b/package/kernel/mac80211/patches/rt2x00/611-rt2x00-add-AP+STA-support.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c -@@ -1340,7 +1340,7 @@ static inline void rt2x00lib_set_if_comb +@@ -1358,7 +1358,7 @@ static inline void rt2x00lib_set_if_comb */ if_limit = &rt2x00dev->if_limits_ap; if_limit->max = rt2x00dev->ops->max_ap_intf; diff --git a/package/kernel/mac80211/patches/rt2x00/612-rt2x00-add-throughput-LED-trigger.patch b/package/kernel/mac80211/patches/rt2x00/612-rt2x00-add-throughput-LED-trigger.patch deleted file mode 100644 index f27873acc0..0000000000 --- a/package/kernel/mac80211/patches/rt2x00/612-rt2x00-add-throughput-LED-trigger.patch +++ /dev/null @@ -1,47 +0,0 @@ -From: David Bauer -Date: Mon, 16 Dec 2019 20:47:06 +0100 -Subject: [PATCH] rt2x00: add throughput LED trigger - -This adds a (currently missing) throughput LED trigger for the rt2x00 -driver. Previously, LED triggers had to be assigned to the netdev, which -was limited to a single VAP. - -Signed-off-by: David Bauer -Tested-by: Christoph Krapp ---- - drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - ---- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c -+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c -@@ -1125,6 +1125,19 @@ static void rt2x00lib_remove_hw(struct r - kfree(rt2x00dev->spec.channels_info); - } - -+static const struct ieee80211_tpt_blink rt2x00_tpt_blink[] = { -+ { .throughput = 0 * 1024, .blink_time = 334 }, -+ { .throughput = 1 * 1024, .blink_time = 260 }, -+ { .throughput = 2 * 1024, .blink_time = 220 }, -+ { .throughput = 5 * 1024, .blink_time = 190 }, -+ { .throughput = 10 * 1024, .blink_time = 170 }, -+ { .throughput = 25 * 1024, .blink_time = 150 }, -+ { .throughput = 54 * 1024, .blink_time = 130 }, -+ { .throughput = 120 * 1024, .blink_time = 110 }, -+ { .throughput = 265 * 1024, .blink_time = 80 }, -+ { .throughput = 586 * 1024, .blink_time = 50 }, -+}; -+ - static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) - { - struct hw_mode_spec *spec = &rt2x00dev->spec; -@@ -1206,6 +1219,10 @@ static int rt2x00lib_probe_hw(struct rt2 - - #undef RT2X00_TASKLET_INIT - -+ ieee80211_create_tpt_led_trigger(rt2x00dev->hw, -+ IEEE80211_TPT_LEDTRIG_FL_RADIO, rt2x00_tpt_blink, -+ ARRAY_SIZE(rt2x00_tpt_blink)); -+ - /* - * Register HW. - */ diff --git a/package/kernel/mac80211/patches/rt2x00/992-rt2x00-more-fixes.patch b/package/kernel/mac80211/patches/rt2x00/992-rt2x00-more-fixes.patch deleted file mode 100644 index 2ad6219014..0000000000 --- a/package/kernel/mac80211/patches/rt2x00/992-rt2x00-more-fixes.patch +++ /dev/null @@ -1,423 +0,0 @@ -From: Daniel Golle -Date: Mon, 12 Sep 2022 21:33:13 +0100 -Subject: [PATCH] rt2x00: various experimental fixes for MT7620 - -Serge Vasilugin reports: - -To improve mt7620 built-in wifi performance some changes: -1. Correct BW20/BW40 switching (see comments with mark see commets with mark (1)) -2. Correct TX_SW_CFG1 MAC reg from v3 of vendor driver see - https://gitlab.com/dm38/padavan-ng/-/blob/master/trunk/proprietary/rt_wifi/rtpci/3.0.X.X/mt76x2/chips/rt6352.c#L531 -3. Set bbp66 for all chains. -4. US_CYC_CNT init based on Programming guide, default value was 33 (pci), - set chipset bus clock with fallback to cpu clock/3. -5. Don't overwrite default values for mt7620. -6. Correct some typos. -7. Add support for external LNA: - a) RF and BBP regs never be corrected for this mode - b) eLNA is driven the same way as ePA with mt7620's pin PA - but vendor driver explicitly pin PA to gpio mode (for forrect calibration?) - so I'm not sure that request for pa_pin in dts-file will be enough - -First 5 changes (really 2) improve performance for boards w/o eLNA/ePA. -Changes 7 add support for eLNA - -Configuration w/o eLAN/ePA and with eLNA show results -tx/rx (from router point of view) for each stream: - 35-40/30-35 Mbps for HT20 - 65-70/60-65 Mbps for HT40 - -Yes. Max results for 2T2R client is 140-145/135-140 -with peaks 160/150, It correspond to mediatek driver results. -Boards with ePA untested. - -Reported-by: Serge Vasilugin -Signed-off-by: Daniel Golle ---- ---- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -137,6 +137,26 @@ static u8 rt2800_bbp_read(struct rt2x00_ - - return value; - } -+//serge: move here for use in test -+static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev, -+ const u8 reg, const u8 value) -+{ -+ rt2800_bbp_write(rt2x00dev, 195, reg); -+ rt2800_bbp_write(rt2x00dev, 196, value); -+} -+ -+static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev, -+ const u8 reg, const u8 value) -+{ -+ rt2800_bbp_write(rt2x00dev, 158, reg); -+ rt2800_bbp_write(rt2x00dev, 159, value); -+} -+ -+static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg) -+{ -+ rt2800_bbp_write(rt2x00dev, 158, reg); -+ return rt2800_bbp_read(rt2x00dev, 159); -+} - - static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -@@ -284,6 +304,28 @@ static void rt2800_rf_write(struct rt2x0 - mutex_unlock(&rt2x00dev->csr_mutex); - } - -+void rt6352_enable_pa_pin(struct rt2x00_dev *rt2x00dev, int enable) -+{ -+ if (!rt2x00dev->pinctrl) -+ return; -+ -+ if (enable) { -+ if (!rt2x00dev->pins_default) { -+ rt2x00_warn(rt2x00dev, "cannot enable PA pin! no default pinctrl\n"); -+ return; -+ } -+ -+ pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_default); -+ } else { -+ if (!rt2x00dev->pins_pa_gpio) { -+ rt2x00_warn(rt2x00dev, "cannot disable PA pin! no pa_gpio pinctrl\n"); -+ return; -+ } -+ -+ pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_pa_gpio); -+ } -+} -+ - static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = { - [EEPROM_CHIP_ID] = 0x0000, - [EEPROM_VERSION] = 0x0001, -@@ -3801,6 +3843,20 @@ static void rt2800_config_channel_rf7620 - rfcsr |= tx_agc_fc; - rt2800_rfcsr_write_bank(rt2x00dev, 7, 59, rfcsr); - } -+ -+ if (conf_is_ht40(conf)) {//serge:skipped this step (1) -+ rt2800_bbp_write(rt2x00dev, 195, 141); -+ rt2800_bbp_write(rt2x00dev, 196, 0x10); -+ rt2800_bbp_write(rt2x00dev, 195, 157); -+ rt2800_bbp_write(rt2x00dev, 196, 0x2f); -+ //rt2800_bbp_write(rt2x00dev, 105, 0x3C); -+ } else { -+ rt2800_bbp_write(rt2x00dev, 195, 141); -+ rt2800_bbp_write(rt2x00dev, 196, 0x1a); -+ rt2800_bbp_write(rt2x00dev, 195, 157); -+ rt2800_bbp_write(rt2x00dev, 196, 0x40); -+ //rt2800_bbp_write(rt2x00dev, 105, 0x1C); -+ } - } - - static void rt2800_config_alc(struct rt2x00_dev *rt2x00dev, -@@ -4172,6 +4228,11 @@ static void rt2800_config_channel(struct - rt2800_bbp_write(rt2x00dev, 86, 0x46); - else - rt2800_bbp_write(rt2x00dev, 86, 0); -+ } else if (rt2x00_rt(rt2x00dev, RT6352)) {//serge: don't overwite bbp r86 (5) -+ rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); -+ rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); -+ rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); -+ rt2800_bbp_write(rt2x00dev, 86, 0x38); - } else { - rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); -@@ -4377,7 +4438,8 @@ static void rt2800_config_channel(struct - reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2*rt2x00dev->lna_gain; - rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); - -- rt2800_iq_calibrate(rt2x00dev, rf->channel); -+ if (!rt2x00_rt(rt2x00dev, RT6352))//serge: this function for rt5592 only, for rt6352 it switch off compensations (5) -+ rt2800_iq_calibrate(rt2x00dev, rf->channel); - } - - if (rt2x00_rt(rt2x00dev, RT6352)) { -@@ -4417,6 +4479,31 @@ static void rt2800_config_channel(struct - rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN, - 0x6C6C6B6C); - } -+ -+ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {//serge: for support eLNA (7a) -+ rt2x00_warn(rt2x00dev, "Correct RF/BBP for eLNA!\n"); -+ reg = rt2800_register_read(rt2x00dev, RF_CONTROL3); -+ reg |= 0x00000101; -+ rt2800_register_write(rt2x00dev, RF_CONTROL3, reg); -+ -+ reg = rt2800_register_read(rt2x00dev, RF_BYPASS3); -+ reg |= 0x00000101; -+ rt2800_register_write(rt2x00dev, RF_BYPASS3, reg); -+ -+ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66); -+ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20); -+ rt2800_rfcsr_write_chanreg(rt2x00dev, 18, 0x42); -+ rt2800_bbp_write(rt2x00dev, 75, 0x68);//serge: move bbp eLNA init here? -+ rt2800_bbp_write(rt2x00dev, 76, 0x4C); -+ rt2800_bbp_write(rt2x00dev, 79, 0x1C); -+ rt2800_bbp_write(rt2x00dev, 80, 0x0C); -+ rt2800_bbp_write(rt2x00dev, 82, 0xB6); -+ /* bank 0 RF reg 42 and glrt BBP reg 141 -+ will be set in config channel function -+ in dependence of channel and HT20/HT40 -+ so don't touch it -+ */ -+ } - } - - bbp = rt2800_bbp_read(rt2x00dev, 4); -@@ -4457,6 +4544,9 @@ static void rt2800_config_channel(struct - rt2x00_set_field8(&bbp, BBP49_UPDATE_FLAG, 0); - rt2800_bbp_write(rt2x00dev, 49, bbp); - } -+//serge:just print results after config channel - don't forget to remove nahren (c) <- this is copyright, not ref to comments :) -+ bbp = rt2800_bbp_dcoc_read(rt2x00dev, 0x03); -+ pr_info("BBP tx/rx compensation control=0x%02x\n", bbp); - } - - static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) -@@ -5527,7 +5617,7 @@ void rt2800_vco_calibration(struct rt2x0 - } - rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); - -- if (rt2x00_rt(rt2x00dev, RT6352)) { -+ if (rt2x00_rt(rt2x00dev, RT6352)) {//serge:remark - move all this code to rfcsr_6352 init? - if (rt2x00dev->default_ant.rx_chain_num == 1) { - rt2800_bbp_write(rt2x00dev, 91, 0x07); - rt2800_bbp_write(rt2x00dev, 95, 0x1A); -@@ -5695,7 +5785,8 @@ static inline void rt2800_set_vgc(struct - if (qual->vgc_level != vgc_level) { - if (rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT3593) || -- rt2x00_rt(rt2x00dev, RT3883)) { -+ rt2x00_rt(rt2x00dev, RT3883) || -+ rt2x00_rt(rt2x00dev, RT6352)) {//serge: rt6352 too (3) - rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, - vgc_level); - } else if (rt2x00_rt(rt2x00dev, RT5592)) { -@@ -5930,7 +6021,7 @@ static int rt2800_init_registers(struct - 0x00550055); - } else { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401); -- rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0000); -+ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001);//serge:was 0x000C0000 (2) - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); - rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000); - rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0); -@@ -6195,6 +6286,29 @@ static int rt2800_init_registers(struct - reg = rt2800_register_read(rt2x00dev, US_CYC_CNT); - rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 125); - rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); -+ } else if (rt2x00_is_soc(rt2x00dev)) {//serge:which value correct? (4) -+ struct clk *clk = clk_get_sys("bus", NULL); -+ int rate; -+ -+ if (IS_ERR(clk)) { -+ rt2x00_warn(rt2x00dev, "system bus clock undefined\n"); -+ clk = clk_get_sys("cpu", NULL); -+ -+ if (IS_ERR(clk)) -+ rate = 125; -+ else { -+ rate = clk_get_rate(clk) / 3000000; -+ clk_put(clk); -+ } -+ } else { -+ rate = clk_get_rate(clk) / 1000000; -+ clk_put(clk); -+ } -+ -+ rt2x00_info(rt2x00dev, "set US_CYC=%dMHz\n", rate); -+ reg = rt2800_register_read(rt2x00dev, US_CYC_CNT); -+ rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, rate); -+ rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); - } - - reg = rt2800_register_read(rt2x00dev, HT_FBK_CFG0); -@@ -6981,26 +7095,7 @@ static void rt2800_init_bbp_5592(struct - if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - } -- --static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev, -- const u8 reg, const u8 value) --{ -- rt2800_bbp_write(rt2x00dev, 195, reg); -- rt2800_bbp_write(rt2x00dev, 196, value); --} -- --static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev, -- const u8 reg, const u8 value) --{ -- rt2800_bbp_write(rt2x00dev, 158, reg); -- rt2800_bbp_write(rt2x00dev, 159, value); --} -- --static u8 rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, const u8 reg) --{ -- rt2800_bbp_write(rt2x00dev, 158, reg); -- return rt2800_bbp_read(rt2x00dev, 159); --} -+//serge: move these function upper - - static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev) - { -@@ -8635,7 +8730,7 @@ static void rt2800_r_calibration(struct - r_cal_code = (u8)rcalcode; - - rt2800_rfcsr_write_bank(rt2x00dev, 0, 7, r_cal_code); -- -+ pr_info("RF bank 0 reg 5=0x%02x\n", r_cal_code);//serge: just for info to compare with vendor driver - rt2800_bbp_write(rt2x00dev, 22, 0x0); - - bytevalue = rt2800_bbp_read(rt2x00dev, 21); -@@ -8693,7 +8788,7 @@ static void rt2800_rxdcoc_calibration(st - break; - } - -- saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 0); -+ saverfb5r4 = rt2800_rfcsr_read_bank(rt2x00dev, 5, 4);//serge: was 0 - typo? (6) - saverfb7r4 = rt2800_rfcsr_read_bank(rt2x00dev, 7, 4); - saverfb5r4 = saverfb5r4 & (~0x40); - saverfb7r4 = saverfb7r4 & (~0x40); -@@ -9022,13 +9117,15 @@ static void rt2800_rxiq_calibration(stru - rt2x00_info(rt2x00dev, "RXIQ G_imb=%d, Ph_rx=%d\n", g_imb, ph_rx); - - if ((ph_rx > 20) || (ph_rx < -20)) { -+ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL(ph_rx=%d out of [-20..20]", ph_rx);//serge:just to see value - ph_rx = 0; -- rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); -+ //rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); - } - - if ((g_imb > 12) || (g_imb < -12)) { -+ rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL(g_imb=%d out of (-12..12]", g_imb);//serge:just to see the reason - g_imb = 0; -- rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); -+ //rt2x00_warn(rt2x00dev, "RXIQ calibration FAIL"); - } - } - else { -@@ -9039,11 +9136,21 @@ static void rt2800_rxiq_calibration(stru - } - - if (ch_idx == 0) { -+ //serge: just to see values -+ pr_info("RXIQ RX0 g_imb (0x37, %2x) ph_rx (0x35, %2x)\n", -+ g_imb & 0x3f, -+ ph_rx & 0x3f -+ ); - rt2800_bbp_write(rt2x00dev, 158, 0x37); - rt2800_bbp_write(rt2x00dev, 159, g_imb & 0x3f); - rt2800_bbp_write(rt2x00dev, 158, 0x35); - rt2800_bbp_write(rt2x00dev, 159, ph_rx & 0x3f); - } else { -+ //serge: just to see values -+ pr_info("RXIQ RX1 g_imb (0x55, %2x) ph_rx (0x53, %2x)\n", -+ g_imb & 0x3f, -+ ph_rx & 0x3f -+ ); - rt2800_bbp_write(rt2x00dev, 158, 0x55); - rt2800_bbp_write(rt2x00dev, 159, g_imb & 0x3f); - rt2800_bbp_write(rt2x00dev, 158, 0x53); -@@ -9745,6 +9852,15 @@ void rt2800_loft_iq_calibration(struct r - } - - for (rf_alc_idx = 0; rf_alc_idx < 3; rf_alc_idx++) { -+ //serge: just to see values -+ pr_info("LOFT ALC (0xb0, %2x) I0 (0xb1, %2x) Q0 (0xb2, %2x) I1 (0xb8, %2x) Q1 (0xb9, %2x)\n", -+ rf_alc_idx, -+ loft_dc_search_result[CHAIN_0][rf_alc_idx][0x00] & 0x3F, -+ loft_dc_search_result[CHAIN_0][rf_alc_idx][0x01] & 0x3F, -+ loft_dc_search_result[CHAIN_1][rf_alc_idx][0x00] & 0x3F, -+ loft_dc_search_result[CHAIN_1][rf_alc_idx][0x01] & 0x3F -+ ); -+ - for (idx = 0; idx < 4; idx++) { - rt2800_bbp_write(rt2x00dev, 158, 0xB0); - bbp = (idx<<2) + rf_alc_idx; -@@ -10669,6 +10785,7 @@ static void rt2800_init_rfcsr_6352(struc - rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C); - } - -+ rt6352_enable_pa_pin(rt2x00dev, 0);//serge: vendor driver do it before calibration (7b) - rt2800_r_calibration(rt2x00dev); - rt2800_rf_self_txdc_cal(rt2x00dev); - rt2800_rxdcoc_calibration(rt2x00dev); -@@ -10676,6 +10793,29 @@ static void rt2800_init_rfcsr_6352(struc - rt2800_bw_filter_calibration(rt2x00dev, false); - rt2800_loft_iq_calibration(rt2x00dev); - rt2800_rxiq_calibration(rt2x00dev); -+ rt6352_enable_pa_pin(rt2x00dev, 1);//serge: vendor driver do it after calibration (7b) -+ /* Vendor driver restore iLNA/iPA before -+ recalibration and set correct values after. -+ Openwrt driver init iLNA and iPA but restore only -+ ePA values after recalibration. -+ So set eLNA values only -+ */ -+ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {//serge: rf regs never corrected for eLNA (7a) -+ rt2x00_info(rt2x00dev, "Correct RF/BBP for eLNA!\n"); -+ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66); -+ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20); -+ rt2800_rfcsr_write_chanreg(rt2x00dev, 18, 0x42); -+ rt2800_bbp_write(rt2x00dev, 75, 0x68);//serge: move bbp eLNA init here? -+ rt2800_bbp_write(rt2x00dev, 76, 0x4C); -+ rt2800_bbp_write(rt2x00dev, 79, 0x1C); -+ rt2800_bbp_write(rt2x00dev, 80, 0x0C); -+ rt2800_bbp_write(rt2x00dev, 82, 0xB6); -+ /* bank 0 RF reg 42 and glrt BBP reg 141 -+ will be set in config channel function -+ in dependence of channel and HT20/HT40 -+ so don't touch it -+ */ -+ } - } - - static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) ---- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h -+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h -@@ -28,6 +28,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -1029,6 +1030,11 @@ struct rt2x00_dev { - - /* Clock for System On Chip devices. */ - struct clk *clk; -+ -+ /* pinctrl and states for System On Chip devices with PA/LNA. */ -+ struct pinctrl *pinctrl; -+ struct pinctrl_state *pins_default; -+ struct pinctrl_state *pins_pa_gpio; - }; - - struct rt2x00_bar_list_entry { ---- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c -+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c -@@ -97,6 +97,21 @@ int rt2x00soc_probe(struct platform_devi - if (retval) - goto exit_free_reg; - -+ rt2x00dev->pinctrl = devm_pinctrl_get(&pdev->dev); -+ if (IS_ERR(rt2x00dev->pinctrl)) { -+ rt2x00dev->pinctrl = NULL; -+ rt2x00dev->pins_default = NULL; -+ rt2x00dev->pins_pa_gpio = NULL; -+ } else { -+ rt2x00dev->pins_default = pinctrl_lookup_state(rt2x00dev->pinctrl, "default"); -+ if (IS_ERR(rt2x00dev->pins_default)) -+ rt2x00dev->pins_default = NULL; -+ -+ rt2x00dev->pins_pa_gpio = pinctrl_lookup_state(rt2x00dev->pinctrl, "pa_gpio"); -+ if (IS_ERR(rt2x00dev->pins_pa_gpio)) -+ rt2x00dev->pins_pa_gpio = NULL; -+ } -+ - return 0; - - exit_free_reg: diff --git a/package/kernel/mac80211/patches/rt2x00/994-rt2x00-import-support-for-external-LNA-on-MT7620.patch b/package/kernel/mac80211/patches/rt2x00/994-rt2x00-import-support-for-external-LNA-on-MT7620.patch new file mode 100644 index 0000000000..deaa03be6c --- /dev/null +++ b/package/kernel/mac80211/patches/rt2x00/994-rt2x00-import-support-for-external-LNA-on-MT7620.patch @@ -0,0 +1,161 @@ +From 0fce1109f894ec7fcd72cb098843a1eff786716a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 16 Sep 2022 20:49:42 +0100 +Subject: [PATCH 16/16] rt2x00: import support for external LNA on MT7620 +To: linux-wireless@vger.kernel.org, + Stanislaw Gruszka , + Helmut Schaa +Cc: Kalle Valo , + David S. Miller , + Eric Dumazet , + Jakub Kicinski , + Paolo Abeni , + Johannes Berg + +In order to carry out calibration on boards with ePA or eLNA the PA pin +needs to be switch to GPIO mode on MT7620. Implement that by selecting +pinctrl state "pa_gpio" which should be defined for MT7620 boards with +eLNA or ePA beside the "default" state. + +Reported-by: Serge Vasilugin +Signed-off-by: Daniel Golle +--- + .../net/wireless/ralink/rt2x00/rt2800lib.c | 58 +++++++++++++++++++ + drivers/net/wireless/ralink/rt2x00/rt2x00.h | 5 ++ + .../net/wireless/ralink/rt2x00/rt2x00soc.c | 15 +++++ + 3 files changed, 78 insertions(+) + +--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +@@ -304,6 +304,24 @@ static void rt2800_rf_write(struct rt2x0 + mutex_unlock(&rt2x00dev->csr_mutex); + } + ++void rt6352_enable_pa_pin(struct rt2x00_dev *rt2x00dev, int enable) ++{ ++ if (!rt2x00dev->pinctrl) ++ return; ++ ++ if (enable) { ++ if (!rt2x00dev->pins_default) ++ return; ++ ++ pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_default); ++ } else { ++ if (!rt2x00dev->pins_pa_gpio) ++ return; ++ ++ pinctrl_select_state(rt2x00dev->pinctrl, rt2x00dev->pins_pa_gpio); ++ } ++} ++ + static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = { + [EEPROM_CHIP_ID] = 0x0000, + [EEPROM_VERSION] = 0x0001, +@@ -4469,6 +4487,29 @@ static void rt2800_config_channel(struct + rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN, + 0x6C6C6B6C); + } ++ ++ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { ++ reg = rt2800_register_read(rt2x00dev, RF_CONTROL3); ++ reg |= 0x00000101; ++ rt2800_register_write(rt2x00dev, RF_CONTROL3, reg); ++ ++ reg = rt2800_register_read(rt2x00dev, RF_BYPASS3); ++ reg |= 0x00000101; ++ rt2800_register_write(rt2x00dev, RF_BYPASS3, reg); ++ ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 18, 0x42); ++ rt2800_bbp_write(rt2x00dev, 75, 0x68); ++ rt2800_bbp_write(rt2x00dev, 76, 0x4C); ++ rt2800_bbp_write(rt2x00dev, 79, 0x1C); ++ rt2800_bbp_write(rt2x00dev, 80, 0x0C); ++ rt2800_bbp_write(rt2x00dev, 82, 0xB6); ++ /* bank 0 RF reg 42 and glrt BBP reg 141 will be set in ++ * config channel function in dependence of channel and ++ * HT20/HT40 so don't touch it ++ */ ++ } + } + + bbp = rt2800_bbp_read(rt2x00dev, 4); +@@ -10583,6 +10624,7 @@ static void rt2800_init_rfcsr_6352(struc + rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C); + ++ rt6352_enable_pa_pin(rt2x00dev, 0); + rt2800_r_calibration(rt2x00dev); + rt2800_rf_self_txdc_cal(rt2x00dev); + rt2800_rxdcoc_calibration(rt2x00dev); +@@ -10590,6 +10632,22 @@ static void rt2800_init_rfcsr_6352(struc + rt2800_bw_filter_calibration(rt2x00dev, false); + rt2800_loft_iq_calibration(rt2x00dev); + rt2800_rxiq_calibration(rt2x00dev); ++ rt6352_enable_pa_pin(rt2x00dev, 1); ++ ++ if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x66); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x20); ++ rt2800_rfcsr_write_chanreg(rt2x00dev, 18, 0x42); ++ rt2800_bbp_write(rt2x00dev, 75, 0x68); ++ rt2800_bbp_write(rt2x00dev, 76, 0x4C); ++ rt2800_bbp_write(rt2x00dev, 79, 0x1C); ++ rt2800_bbp_write(rt2x00dev, 80, 0x0C); ++ rt2800_bbp_write(rt2x00dev, 82, 0xB6); ++ /* bank 0 RF reg 42 and glrt BBP reg 141 will be set in config ++ * channel function in dependence of channel and HT20/HT40, ++ * so don't touch them here. ++ */ ++ } + } + + static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -1029,6 +1030,11 @@ struct rt2x00_dev { + + /* Clock for System On Chip devices. */ + struct clk *clk; ++ ++ /* pinctrl and states for System On Chip devices with PA/LNA. */ ++ struct pinctrl *pinctrl; ++ struct pinctrl_state *pins_default; ++ struct pinctrl_state *pins_pa_gpio; + }; + + struct rt2x00_bar_list_entry { +--- a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c ++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c +@@ -97,6 +97,21 @@ int rt2x00soc_probe(struct platform_devi + if (retval) + goto exit_free_reg; + ++ rt2x00dev->pinctrl = devm_pinctrl_get(&pdev->dev); ++ if (IS_ERR(rt2x00dev->pinctrl)) { ++ rt2x00dev->pinctrl = NULL; ++ rt2x00dev->pins_default = NULL; ++ rt2x00dev->pins_pa_gpio = NULL; ++ } else { ++ rt2x00dev->pins_default = pinctrl_lookup_state(rt2x00dev->pinctrl, "default"); ++ if (IS_ERR(rt2x00dev->pins_default)) ++ rt2x00dev->pins_default = NULL; ++ ++ rt2x00dev->pins_pa_gpio = pinctrl_lookup_state(rt2x00dev->pinctrl, "pa_gpio"); ++ if (IS_ERR(rt2x00dev->pins_pa_gpio)) ++ rt2x00dev->pins_pa_gpio = NULL; ++ } ++ + return 0; + + exit_free_reg: diff --git a/package/kernel/mac80211/patches/rt2x00/990-rt2x00-mt7620-introduce-accessors-for-CHIP_VER-register.patch b/package/kernel/mac80211/patches/rt2x00/995-rt2x00-mt7620-introduce-accessors-for-CHIP_VER-register.patch similarity index 100% rename from package/kernel/mac80211/patches/rt2x00/990-rt2x00-mt7620-introduce-accessors-for-CHIP_VER-register.patch rename to package/kernel/mac80211/patches/rt2x00/995-rt2x00-mt7620-introduce-accessors-for-CHIP_VER-register.patch diff --git a/package/kernel/mac80211/patches/rt2x00/991-rt2x00-mt7620-differentiate-based-on-SoC-CHIP_VER.patch b/package/kernel/mac80211/patches/rt2x00/996-rt2x00-mt7620-differentiate-based-on-SoC-CHIP_VER.patch similarity index 95% rename from package/kernel/mac80211/patches/rt2x00/991-rt2x00-mt7620-differentiate-based-on-SoC-CHIP_VER.patch rename to package/kernel/mac80211/patches/rt2x00/996-rt2x00-mt7620-differentiate-based-on-SoC-CHIP_VER.patch index 40b20ec594..dab6e05ffd 100644 --- a/package/kernel/mac80211/patches/rt2x00/991-rt2x00-mt7620-differentiate-based-on-SoC-CHIP_VER.patch +++ b/package/kernel/mac80211/patches/rt2x00/996-rt2x00-mt7620-differentiate-based-on-SoC-CHIP_VER.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h -@@ -1042,6 +1042,11 @@ +@@ -1044,6 +1044,11 @@ #define MIMO_PS_CFG_RX_STBY_POL FIELD32(0x00000010) #define MIMO_PS_CFG_RX_RX_STBY0 FIELD32(0x00000020) @@ -14,7 +14,7 @@ */ --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c -@@ -3698,14 +3698,16 @@ static void rt2800_config_channel_rf7620 +@@ -3778,14 +3778,16 @@ static void rt2800_config_channel_rf7620 rt2x00_set_field8(&rfcsr, RFCSR19_K, rf->rf4); rt2800_rfcsr_write(rt2x00dev, 19, rfcsr); @@ -39,7 +39,7 @@ rfcsr = rt2800_rfcsr_read(rt2x00dev, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX2_EN_MT7620, -@@ -3739,18 +3741,23 @@ static void rt2800_config_channel_rf7620 +@@ -3819,18 +3821,23 @@ static void rt2800_config_channel_rf7620 rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x20); } @@ -73,9 +73,9 @@ if (!test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) { if (conf_is_ht40(conf)) { -@@ -3850,25 +3857,29 @@ static void rt2800_config_alc(struct rt2 - if (i == 10000) - rt2x00_warn(rt2x00dev, "Wait MAC Status to MAX !!!\n"); +@@ -3929,25 +3936,29 @@ static void rt2800_config_alc(struct rt2 + if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev, MAC_STATUS_CFG_BBP_RF_BUSY))) + rt2x00_warn(rt2x00dev, "RF busy while configuring ALC\n"); - if (chan->center_freq > 2457) { - bbp = rt2800_bbp_read(rt2x00dev, 30); @@ -121,12 +121,12 @@ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, mac_sys_ctrl); rt2800_vco_calibration(rt2x00dev); -@@ -5906,18 +5917,33 @@ static int rt2800_init_registers(struct +@@ -6011,18 +6022,33 @@ static int rt2800_init_registers(struct } else if (rt2x00_rt(rt2x00dev, RT5350)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); } else if (rt2x00_rt(rt2x00dev, RT6352)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401); -- rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0000); +- rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001); - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); - rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000); - rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0); @@ -150,7 +150,7 @@ + 0x00550055); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401); -+ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0000); ++ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0001); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x00000000); + rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0); @@ -167,7 +167,7 @@ reg = rt2800_register_read(rt2x00dev, TX_ALC_CFG_1); rt2x00_set_field32(®, TX_ALC_CFG_1_ROS_BUSY_EN, 0); rt2800_register_write(rt2x00dev, TX_ALC_CFG_1, reg); -@@ -7061,14 +7087,16 @@ static void rt2800_init_bbp_6352(struct +@@ -7127,14 +7153,16 @@ static void rt2800_init_bbp_6352(struct rt2800_bbp_write(rt2x00dev, 188, 0x00); rt2800_bbp_write(rt2x00dev, 189, 0x00); @@ -192,7 +192,7 @@ /* BBP for G band GLRT function (BBP_128 ~ BBP_221) */ rt2800_bbp_glrt_write(rt2x00dev, 0, 0x00); -@@ -10407,31 +10435,36 @@ static void rt2800_init_rfcsr_6352(struc +@@ -10408,31 +10436,36 @@ static void rt2800_init_rfcsr_6352(struc rt2800_rfcsr_write(rt2x00dev, 42, 0x5B); rt2800_rfcsr_write(rt2x00dev, 43, 0x00); @@ -254,7 +254,7 @@ /* Initialize RF channel register to default value */ rt2800_rfcsr_write_chanreg(rt2x00dev, 0, 0x03); -@@ -10497,63 +10530,71 @@ static void rt2800_init_rfcsr_6352(struc +@@ -10498,63 +10531,71 @@ static void rt2800_init_rfcsr_6352(struc rt2800_rfcsr_write_bank(rt2x00dev, 6, 45, 0xC5); @@ -383,7 +383,7 @@ /* Initialize RF DC calibration register to default value */ rt2800_rfcsr_write_dccal(rt2x00dev, 0, 0x47); -@@ -10616,12 +10657,17 @@ static void rt2800_init_rfcsr_6352(struc +@@ -10617,12 +10658,17 @@ static void rt2800_init_rfcsr_6352(struc rt2800_rfcsr_write_dccal(rt2x00dev, 62, 0x00); rt2800_rfcsr_write_dccal(rt2x00dev, 63, 0x00); @@ -404,5 +404,5 @@ + rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C); + } + rt6352_enable_pa_pin(rt2x00dev, 0); rt2800_r_calibration(rt2x00dev); - rt2800_rf_self_txdc_cal(rt2x00dev); diff --git a/package/libs/libusb/Makefile b/package/libs/libusb/Makefile index 5d89568937..65c624409d 100644 --- a/package/libs/libusb/Makefile +++ b/package/libs/libusb/Makefile @@ -41,10 +41,26 @@ define Package/libusb-1.0/description many different operating systems. endef +define Package/fxload + SECTION:=utils + CATEGORY:=Utilities + TITLE:=fxload firmware loader + URL:=http://linux-hotplug.sourceforge.net + DEPENDS:=+libusb-1.0 +endef + +define Package/fxload/description +This program is conveniently able to download firmware into FX, FX2, +and FX2LP EZ-USB devices, as well as the original AnchorChips EZ-USB. +It is intended to be invoked by hotplug scripts when the unprogrammed +device appears on the bus. +endef + TARGET_CFLAGS += $(FPIC) CONFIGURE_ARGS += \ - --disable-udev \ - --disable-log + --enable-examples-build \ + --disable-log \ + --disable-udev define Build/InstallDev $(INSTALL_DIR) $(1)/usr/include/libusb-1.0 @@ -60,4 +76,10 @@ define Package/libusb-1.0/install $(CP) $(PKG_INSTALL_DIR)/usr/lib/libusb-1.0.so* $(1)/usr/lib/ endef +define Package/fxload/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/examples/.libs/fxload $(1)/usr/sbin +endef + $(eval $(call BuildPackage,libusb-1.0)) +$(eval $(call BuildPackage,fxload)) diff --git a/package/libs/wolfssl/Config.in b/package/libs/wolfssl/Config.in index 4b9db4d107..af9ba923fe 100644 --- a/package/libs/wolfssl/Config.in +++ b/package/libs/wolfssl/Config.in @@ -1,4 +1,4 @@ -if PACKAGE_libwolfssl +menu "wolfSSL Library Configuration" config WOLFSSL_HAS_AES_CCM bool "Include AES-CCM support" @@ -66,38 +66,31 @@ config WOLFSSL_ALT_NAMES config WOLFSSL_HAS_DEVCRYPTO bool -config WOLFSSL_ASM_CAPABLE - bool - default x86_64 || (aarch64 && !TARGET_bcm27xx) +if PACKAGE_libwolfssl + if PACKAGE_libwolfssl-cpu-crypto + comment "Hardware Acceleration does not apply to libwolfssl-cpu-crypto" + endif + choice + prompt "Hardware Acceleration" + default WOLFSSL_HAS_NO_HW -choice - prompt "Hardware Acceleration" - default WOLFSSL_HAS_NO_HW + config WOLFSSL_HAS_NO_HW + bool "None" - config WOLFSSL_HAS_NO_HW - bool "None" + config WOLFSSL_HAS_AFALG + bool "AF_ALG" - config WOLFSSL_HAS_CPU_CRYPTO - bool "Use CPU crypto instructions" - depends on WOLFSSL_ASM_CAPABLE - help - This will use Intel AESNI insturctions or armv8 Crypto Extensions. - Either of them should easily outperform hardware crypto in WolfSSL. + config WOLFSSL_HAS_DEVCRYPTO_CBC + bool "/dev/crytpo - AES-CBC-only" + select WOLFSSL_HAS_DEVCRYPTO - config WOLFSSL_HAS_AFALG - bool "AF_ALG" - - config WOLFSSL_HAS_DEVCRYPTO_CBC - bool "/dev/crytpo - AES-CBC-only" - select WOLFSSL_HAS_DEVCRYPTO - - config WOLFSSL_HAS_DEVCRYPTO_AES - bool "/dev/crypto - AES-only (all supported modes)" - select WOLFSSL_HAS_DEVCRYPTO - - config WOLFSSL_HAS_DEVCRYPTO_FULL - bool "/dev/crypto - full" - select WOLFSSL_HAS_DEVCRYPTO -endchoice + config WOLFSSL_HAS_DEVCRYPTO_AES + bool "/dev/crypto - AES-only (all supported modes)" + select WOLFSSL_HAS_DEVCRYPTO + config WOLFSSL_HAS_DEVCRYPTO_FULL + bool "/dev/crypto - full" + select WOLFSSL_HAS_DEVCRYPTO + endchoice endif +endmenu diff --git a/package/libs/wolfssl/Makefile b/package/libs/wolfssl/Makefile index ee07081cfd..def3f657c1 100644 --- a/package/libs/wolfssl/Makefile +++ b/package/libs/wolfssl/Makefile @@ -45,7 +45,6 @@ PKG_ABI_VERSION:=$(patsubst %-stable,%,$(PKG_VERSION)).$(call version_abbrev,$(c PKG_CONFIG_DEPENDS+=\ CONFIG_WOLFSSL_HAS_AFALG \ - CONFIG_WOLFSSL_HAS_CPU_CRYPTO \ CONFIG_WOLFSSL_HAS_DEVCRYPTO_AES \ CONFIG_WOLFSSL_HAS_DEVCRYPTO_CBC \ CONFIG_WOLFSSL_HAS_DEVCRYPTO_FULL @@ -66,6 +65,9 @@ $(call Package/libwolfssl/Default) PROVIDES:=libcyassl DEPENDS:=+WOLFSSL_HAS_DEVCRYPTO:kmod-cryptodev +WOLFSSL_HAS_AFALG:kmod-crypto-user ABI_VERSION:=$(PKG_ABI_VERSION) + VARIANT:=regular + DEFAULT_VARIANT:=1 + CONFLICTS:=libwolfssl-cpu-crypto endef define Package/libwolfssl/description @@ -77,12 +79,38 @@ define Package/libwolfssl/config source "$(SOURCE)/Config.in" endef +define Package/libwolfssl-cpu-crypto +$(call Package/libwolfssl/Default) + TITLE:=wolfSSL library with AES CPU instructions + PROVIDES:=libwolfssl libcyassl + DEPENDS:=@((aarch64||x86_64)&&(m||!TARGET_bcm27xx)) + ABI_VERSION:=$(PKG_ABI_VERSION) + VARIANT:=cpu-crypto +endef + define Package/libwolfssl-benchmark $(call Package/libwolfssl/Default) TITLE:=wolfSSL Benchmark Utility DEPENDS:=libwolfssl endef +define Package/libwolfssl-cpu-crypto/description +$(call Package/libwolfssl/description) +This variant uses AES CPU instructions (Intel AESNI or ARMv8 Crypto Extension) +endef + +define Package/libwolfssl-cpu-crypto/config + if TARGET_armvirt && PACKAGE_libwolfssl-cpu-crypto = y + comment "You are about to build libwolfssl-cpu-crypto into an armvirt_64 image." + comment "Ensure all of your installation targets support the Crypto Extension. " + comment "Look for the 'aes' feature in /proc/cpuinfo. This library does not do " + comment "run-time detection and will crash if the CPU does not support it. " + endif + if TARGET_bcm27xx && PACKAGE_libwolfssl-cpu-crypto + comment "Beware that libwolfssl-cpu-crypto will not run in a bcm27xx target. " + endif +endef + define Package/libwolfssl-benchmark/description This is the wolfssl benchmark utility. endef @@ -121,11 +149,23 @@ CONFIGURE_ARGS += \ --$(if $(CONFIG_WOLFSSL_HAS_DTLS),enable,disable)-dtls \ --$(if $(CONFIG_WOLFSSL_HAS_ECC25519),enable,disable)-curve25519 \ --$(if $(CONFIG_WOLFSSL_HAS_ECC448),enable,disable)-curve448 \ + --$(if $(CONFIG_WOLFSSL_HAS_OPENVPN),enable,disable)-openvpn + +ifeq ($(BUILD_VARIANT),regular) +CONFIGURE_ARGS += \ --$(if $(CONFIG_WOLFSSL_HAS_AFALG),enable,disable)-afalg \ - --$(if $(CONFIG_WOLFSSL_HAS_OPENVPN),enable,disable)-openvpn \ --enable-devcrypto=$(if $(CONFIG_WOLFSSL_HAS_DEVCRYPTO_CBC),cbc\ ,$(if $(CONFIG_WOLFSSL_HAS_DEVCRYPTO_AES),aes\ ,$(if $(CONFIG_WOLFSSL_HAS_DEVCRYPTO_FULL),yes,no))) +else ifdef CONFIG_aarch64 + CONFIGURE_ARGS += --enable-armasm + TARGET_CFLAGS:=$(TARGET_CFLAGS:-mcpu%=-mcpu%+crypto) + WOLFSSL_NOASM_REGEX:=^bcm27xx/.* + Package/libwolfssl-cpu-crypto/preinst=\ + $(subst @@WOLFSSL_NOASM_REGEX@@,$(WOLFSSL_NOASM_REGEX),$(file &2 +printf "[libwolfssl-cpu-crypto] Checking for Arm v8-A Cryptographic Extension support: " +if [ -n "${IPKG_INSTROOT}" ]; then + printf "...[offline]... " + eval "$(grep '^DISTRIB_TARGET=' "${IPKG_INSTROOT}/etc/openwrt_release")" + ### @@WOLFSSL_NOASM_REGEX@@ is expanded from WOLFSSL_NOASM_REGEX in the Makefile + echo "${DISTRIB_TARGET}" | grep '@@WOLFSSL_NOASM_REGEX@@' > /dev/null && { + echo "not supported" + echo "Error: Target ${DISTRIB_TARGET} does not support Arm Cryptographic Extension." + echo "Install the regular libwolfssl package instead of libwolfssl-cpu-crypto." + exit 1 + } +else + grep -q '^Features.*\baes\b' /proc/cpuinfo || { + echo "not supported" + echo "Error: Arm v8-A Cryptographic Extension not supported." + echo "Install the regular libwolfssl package instead of libwolfssl-cpu-crypto." + echo "Contents of /proc/cpuinfo:" + cat /proc/cpuinfo + exit 1 + } +fi +echo OK +exit 0 diff --git a/package/network/config/ltq-adsl-app/Makefile b/package/network/config/ltq-adsl-app/Makefile index 2e4792266c..c4ddbf7089 100644 --- a/package/network/config/ltq-adsl-app/Makefile +++ b/package/network/config/ltq-adsl-app/Makefile @@ -36,6 +36,7 @@ define Package/ltq-adsl-app TITLE:=Lantiq DSL userland tool URL:=http://www.lantiq.com/ DEPENDS:=@(TARGET_lantiq_xway||TARGET_lantiq_xway_legacy||TARGET_lantiq_ase) +libpthread +ltq-dsl-base +libubox +libubus + PROVIDES:=ltq-dsl-app MENU:=1 endef @@ -77,7 +78,7 @@ TARGET_CFLAGS += -I$(LINUX_DIR)/include define Build/Prepare $(call Build/Prepare/Default) - $(CP) ../ltq-vdsl-app/src/src/dsl_cpe_ubus.c $(PKG_BUILD_DIR)/src/ + $(CP) ../ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c $(PKG_BUILD_DIR)/src/ endef define Package/ltq-adsl-app/install diff --git a/package/network/config/ltq-adsl-app/patches/100-add-more-script-notifications.patch b/package/network/config/ltq-adsl-app/patches/100-add-more-script-notifications.patch index 9d611c1caf..875b7ecbf5 100644 --- a/package/network/config/ltq-adsl-app/patches/100-add-more-script-notifications.patch +++ b/package/network/config/ltq-adsl-app/patches/100-add-more-script-notifications.patch @@ -3,7 +3,7 @@ From: Mathias Kresin Date: Mon, 16 Oct 2017 21:08:26 +0200 Subject: ltq-adsl-app: add more script notifications -Backport HANDSHAKE and TRAINING notification from ltq-vdsl-app. It +Backport HANDSHAKE and TRAINING notification from ltq-vdsl-vr9-app. It unifies the dsl led blinking pattern accross all subtargets and allows to get the current line status from the dsl led. diff --git a/package/network/config/ltq-vdsl-app/Makefile b/package/network/config/ltq-vdsl-vr9-app/Makefile similarity index 84% rename from package/network/config/ltq-vdsl-app/Makefile rename to package/network/config/ltq-vdsl-vr9-app/Makefile index 0051f39b31..85d332e5b1 100644 --- a/package/network/config/ltq-vdsl-app/Makefile +++ b/package/network/config/ltq-vdsl-vr9-app/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk -PKG_NAME:=ltq-vdsl-app +PKG_NAME:=ltq-vdsl-vr9-app PKG_VERSION:=4.17.18.6 PKG_RELEASE:=$(AUTORELEASE) PKG_BASE_NAME:=dsl_cpe_control @@ -17,32 +17,33 @@ PKG_HASH:=da8bb929526a61aea0e153ef524331fcd472a1ebbc6d88ca017735a4f82ece02 PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_BASE_NAME)-$(PKG_VERSION) PKG_LICENSE:=BSD-2-Clause -PKG_BUILD_DEPENDS:=ltq-vdsl +PKG_BUILD_DEPENDS:=ltq-vdsl-vr9 PKG_FLAGS:=nonshared PKG_FIXUP:=autoreconf include $(INCLUDE_DIR)/package.mk -define Package/ltq-vdsl-app +define Package/ltq-vdsl-vr9-app SECTION:=net CATEGORY:=Network TITLE:=Lantiq VDSL userland tool URL:=http://www.lantiq.com/ DEPENDS:=@TARGET_lantiq_xrx200 +libpthread +librt +ltq-dsl-base +libubox +libubus + PROVIDES:=ltq-dsl-app endef -define Package/ltq-vdsl-app/description +define Package/ltq-vdsl-vr9-app/description Userland tool needed to control Lantiq VDSL CPE endef -# ltq-vdsl-app uses a header provided by the MEI driver which has some +# ltq-vdsl-vr9-app uses a header provided by the MEI driver which has some # conditionals. # # Define them here with the default values they would get in the MEI driver, # have the same view on both sides. # -# If you change them, you need to change them for the ltq-vdsl-app as well +# If you change them, you need to change them for the ltq-vdsl-vr9-app as well VDSL_APP_CFLAGS = \ -DMAX_CLI_PIPES=1 \ -DMEI_SUPPORT_DEBUG_STREAMS=1 \ @@ -69,7 +70,7 @@ CONFIGURE_ARGS += \ #CONFIGURE_ARGS += --enable-model=typical #CONFIGURE_ARGS += --enable-model=debug -define Package/ltq-vdsl-app/install +define Package/ltq-vdsl-vr9-app/install $(INSTALL_DIR) $(1)/etc/init.d $(1)/sbin $(1)/etc/hotplug.d/dsl $(INSTALL_BIN) ./files/dsl_control $(1)/etc/init.d/ $(INSTALL_BIN) ./files/10_atm.sh $(1)/etc/hotplug.d/dsl @@ -79,4 +80,4 @@ define Package/ltq-vdsl-app/install $(INSTALL_BIN) ./files/dsl_cpe_pipe.sh $(1)/sbin/ endef -$(eval $(call BuildPackage,ltq-vdsl-app)) +$(eval $(call BuildPackage,ltq-vdsl-vr9-app)) diff --git a/package/network/config/ltq-vdsl-app/files/10_atm.sh b/package/network/config/ltq-vdsl-vr9-app/files/10_atm.sh similarity index 100% rename from package/network/config/ltq-vdsl-app/files/10_atm.sh rename to package/network/config/ltq-vdsl-vr9-app/files/10_atm.sh diff --git a/package/network/config/ltq-vdsl-app/files/10_ptm.sh b/package/network/config/ltq-vdsl-vr9-app/files/10_ptm.sh similarity index 100% rename from package/network/config/ltq-vdsl-app/files/10_ptm.sh rename to package/network/config/ltq-vdsl-vr9-app/files/10_ptm.sh diff --git a/package/network/config/ltq-vdsl-app/files/dsl_control b/package/network/config/ltq-vdsl-vr9-app/files/dsl_control similarity index 100% rename from package/network/config/ltq-vdsl-app/files/dsl_control rename to package/network/config/ltq-vdsl-vr9-app/files/dsl_control diff --git a/package/network/config/ltq-vdsl-app/files/dsl_cpe_pipe.sh b/package/network/config/ltq-vdsl-vr9-app/files/dsl_cpe_pipe.sh similarity index 100% rename from package/network/config/ltq-vdsl-app/files/dsl_cpe_pipe.sh rename to package/network/config/ltq-vdsl-vr9-app/files/dsl_cpe_pipe.sh diff --git a/package/network/config/ltq-vdsl-app/patches/100-compat.patch b/package/network/config/ltq-vdsl-vr9-app/patches/100-compat.patch similarity index 100% rename from package/network/config/ltq-vdsl-app/patches/100-compat.patch rename to package/network/config/ltq-vdsl-vr9-app/patches/100-compat.patch diff --git a/package/network/config/ltq-vdsl-app/patches/101-musl.patch b/package/network/config/ltq-vdsl-vr9-app/patches/101-musl.patch similarity index 100% rename from package/network/config/ltq-vdsl-app/patches/101-musl.patch rename to package/network/config/ltq-vdsl-vr9-app/patches/101-musl.patch diff --git a/package/network/config/ltq-vdsl-app/patches/200-autoboot.patch b/package/network/config/ltq-vdsl-vr9-app/patches/200-autoboot.patch similarity index 100% rename from package/network/config/ltq-vdsl-app/patches/200-autoboot.patch rename to package/network/config/ltq-vdsl-vr9-app/patches/200-autoboot.patch diff --git a/package/network/config/ltq-vdsl-app/patches/201-sigterm.patch b/package/network/config/ltq-vdsl-vr9-app/patches/201-sigterm.patch similarity index 100% rename from package/network/config/ltq-vdsl-app/patches/201-sigterm.patch rename to package/network/config/ltq-vdsl-vr9-app/patches/201-sigterm.patch diff --git a/package/network/config/ltq-vdsl-app/patches/300-ubus.patch b/package/network/config/ltq-vdsl-vr9-app/patches/300-ubus.patch similarity index 100% rename from package/network/config/ltq-vdsl-app/patches/300-ubus.patch rename to package/network/config/ltq-vdsl-vr9-app/patches/300-ubus.patch diff --git a/package/network/config/ltq-vdsl-app/src/src/dsl_cpe_ubus.c b/package/network/config/ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c similarity index 100% rename from package/network/config/ltq-vdsl-app/src/src/dsl_cpe_ubus.c rename to package/network/config/ltq-vdsl-vr9-app/src/src/dsl_cpe_ubus.c diff --git a/package/network/services/hostapd/Makefile b/package/network/services/hostapd/Makefile index c4aa6d04f3..e479496879 100644 --- a/package/network/services/hostapd/Makefile +++ b/package/network/services/hostapd/Makefile @@ -9,9 +9,9 @@ PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE_URL:=http://w1.fi/hostap.git PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2022-06-02 -PKG_SOURCE_VERSION:=4383528e01955d995d3b3db201e4c0f9840e8236 -PKG_MIRROR_HASH:=1b533287661d4877d56aca8a5b2a53d84d2a90eebe246d8d9b3adf943da7f0a1 +PKG_SOURCE_DATE:=2022-07-29 +PKG_SOURCE_VERSION:=b704dc72ef824dfdd96674b90179b274d1d38105 +PKG_MIRROR_HASH:=6c9dd359ef5a4595b6576e07928566d6864957c4af6466d641d6c3f7717f4689 PKG_MAINTAINER:=Felix Fietkau PKG_LICENSE:=BSD-3-Clause diff --git a/package/network/services/hostapd/patches/001-wolfssl-init-RNG-with-ECC-key.patch b/package/network/services/hostapd/patches/001-wolfssl-init-RNG-with-ECC-key.patch index 994aa30626..269dcaac75 100644 --- a/package/network/services/hostapd/patches/001-wolfssl-init-RNG-with-ECC-key.patch +++ b/package/network/services/hostapd/patches/001-wolfssl-init-RNG-with-ECC-key.patch @@ -16,7 +16,7 @@ Signed-off-by: David Bauer --- a/src/crypto/crypto_wolfssl.c +++ b/src/crypto/crypto_wolfssl.c -@@ -1307,6 +1307,7 @@ int ecc_projective_add_point(ecc_point * +@@ -1340,6 +1340,7 @@ int ecc_projective_add_point(ecc_point * struct crypto_ec { ecc_key key; @@ -24,7 +24,7 @@ Signed-off-by: David Bauer mp_int a; mp_int prime; mp_int order; -@@ -1361,6 +1362,8 @@ struct crypto_ec * crypto_ec_init(int gr +@@ -1394,6 +1395,8 @@ struct crypto_ec * crypto_ec_init(int gr return NULL; if (wc_ecc_init(&e->key) != 0 || @@ -33,7 +33,7 @@ Signed-off-by: David Bauer wc_ecc_set_curve(&e->key, 0, curve_id) != 0 || mp_init(&e->a) != MP_OKAY || mp_init(&e->prime) != MP_OKAY || -@@ -1392,6 +1395,7 @@ void crypto_ec_deinit(struct crypto_ec* +@@ -1425,6 +1428,7 @@ void crypto_ec_deinit(struct crypto_ec* mp_clear(&e->order); mp_clear(&e->prime); mp_clear(&e->a); diff --git a/package/network/services/hostapd/patches/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch b/package/network/services/hostapd/patches/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch index 16d24d1000..6bc48abfb9 100644 --- a/package/network/services/hostapd/patches/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch +++ b/package/network/services/hostapd/patches/010-mesh-Allow-DFS-channels-to-be-selected-if-dfs-is-ena.patch @@ -14,7 +14,7 @@ Signed-off-by: Peter Oh --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -2409,7 +2409,7 @@ static int drv_supports_vht(struct wpa_s +@@ -2436,7 +2436,7 @@ static int drv_supports_vht(struct wpa_s } @@ -23,7 +23,7 @@ Signed-off-by: Peter Oh { int i; -@@ -2418,7 +2418,10 @@ static bool ibss_mesh_is_80mhz_avail(int +@@ -2445,7 +2445,10 @@ static bool ibss_mesh_is_80mhz_avail(int chan = hw_get_channel_chan(mode, i, NULL); if (!chan || @@ -35,7 +35,7 @@ Signed-off-by: Peter Oh return false; } -@@ -2447,6 +2450,8 @@ void ibss_mesh_setup_freq(struct wpa_sup +@@ -2474,6 +2477,8 @@ void ibss_mesh_setup_freq(struct wpa_sup int chwidth, seg0, seg1; u32 vht_caps = 0; bool is_24ghz, is_6ghz; @@ -44,7 +44,7 @@ Signed-off-by: Peter Oh freq->freq = ssid->frequency; -@@ -2543,8 +2548,11 @@ void ibss_mesh_setup_freq(struct wpa_sup +@@ -2570,8 +2575,11 @@ void ibss_mesh_setup_freq(struct wpa_sup return; /* Check primary channel flags */ @@ -57,7 +57,7 @@ Signed-off-by: Peter Oh freq->channel = pri_chan->chan; -@@ -2577,8 +2585,11 @@ void ibss_mesh_setup_freq(struct wpa_sup +@@ -2604,8 +2612,11 @@ void ibss_mesh_setup_freq(struct wpa_sup return; /* Check secondary channel flags */ @@ -70,7 +70,7 @@ Signed-off-by: Peter Oh if (ht40 == -1) { if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) -@@ -2667,7 +2678,7 @@ skip_to_6ghz: +@@ -2694,7 +2705,7 @@ skip_to_6ghz: return; /* Back to HT configuration if channel not usable */ @@ -78,8 +78,8 @@ Signed-off-by: Peter Oh + if (!ibss_mesh_is_80mhz_avail(channel, mode, dfs_enabled)) return; - chwidth = CHANWIDTH_80MHZ; -@@ -2681,7 +2692,7 @@ skip_to_6ghz: + chwidth = CONF_OPER_CHWIDTH_80MHZ; +@@ -2708,7 +2719,7 @@ skip_to_6ghz: * above; check the remaining four 20 MHz channels for the total * of 160 MHz bandwidth. */ @@ -88,7 +88,7 @@ Signed-off-by: Peter Oh return; for (j = 0; j < ARRAY_SIZE(bw160); j++) { -@@ -2711,10 +2722,12 @@ skip_to_6ghz: +@@ -2738,10 +2749,12 @@ skip_to_6ghz: if (!chan) continue; @@ -103,4 +103,4 @@ Signed-off-by: Peter Oh + continue; /* Found a suitable second segment for 80+80 */ - chwidth = CHANWIDTH_80P80MHZ; + chwidth = CONF_OPER_CHWIDTH_80P80MHZ; diff --git a/package/network/services/hostapd/patches/011-mesh-use-deterministic-channel-on-channel-switch.patch b/package/network/services/hostapd/patches/011-mesh-use-deterministic-channel-on-channel-switch.patch index 1faeacf766..32a447914c 100644 --- a/package/network/services/hostapd/patches/011-mesh-use-deterministic-channel-on-channel-switch.patch +++ b/package/network/services/hostapd/patches/011-mesh-use-deterministic-channel-on-channel-switch.patch @@ -28,8 +28,8 @@ Signed-off-by: Markus Theil +#include "crypto/crypto.h" - static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) -@@ -483,9 +484,14 @@ dfs_get_valid_channel(struct hostapd_ifa + enum dfs_channel_type { +@@ -515,9 +516,14 @@ dfs_get_valid_channel(struct hostapd_ifa int num_available_chandefs; int chan_idx, chan_idx2; int sec_chan_idx_80p80 = -1; @@ -44,7 +44,7 @@ Signed-off-by: Markus Theil wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); *secondary_channel = 0; *oper_centr_freq_seg0_idx = 0; -@@ -505,8 +511,20 @@ dfs_get_valid_channel(struct hostapd_ifa +@@ -537,8 +543,20 @@ dfs_get_valid_channel(struct hostapd_ifa if (num_available_chandefs == 0) return NULL; @@ -64,11 +64,11 @@ Signed-off-by: Markus Theil return NULL; + chan_idx = _rand % num_available_chandefs; - dfs_find_channel(iface, &chan, chan_idx, skip_radar); + dfs_find_channel(iface, &chan, chan_idx, type); if (!chan) { --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c -@@ -9895,6 +9895,10 @@ static int nl80211_switch_channel(void * +@@ -9948,6 +9948,10 @@ static int nl80211_switch_channel(void * if (ret) goto error; diff --git a/package/network/services/hostapd/patches/021-fix-sta-add-after-previous-connection.patch b/package/network/services/hostapd/patches/021-fix-sta-add-after-previous-connection.patch index ac02ec5ab7..80b23bdfc9 100644 --- a/package/network/services/hostapd/patches/021-fix-sta-add-after-previous-connection.patch +++ b/package/network/services/hostapd/patches/021-fix-sta-add-after-previous-connection.patch @@ -1,6 +1,6 @@ --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c -@@ -4944,6 +4944,13 @@ static int add_associated_sta(struct hos +@@ -4963,6 +4963,13 @@ static int add_associated_sta(struct hos * drivers to accept the STA parameter configuration. Since this is * after a new FT-over-DS exchange, a new TK has been derived, so key * reinstallation is not a concern for this case. @@ -14,7 +14,7 @@ */ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)", -@@ -4957,7 +4964,8 @@ static int add_associated_sta(struct hos +@@ -4976,7 +4983,8 @@ static int add_associated_sta(struct hos (!(sta->flags & WLAN_STA_AUTHORIZED) || (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) || (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && diff --git a/package/network/services/hostapd/patches/022-hostapd-fix-use-of-uninitialized-stack-variables.patch b/package/network/services/hostapd/patches/022-hostapd-fix-use-of-uninitialized-stack-variables.patch index c7da33f029..25801da334 100644 --- a/package/network/services/hostapd/patches/022-hostapd-fix-use-of-uninitialized-stack-variables.patch +++ b/package/network/services/hostapd/patches/022-hostapd-fix-use-of-uninitialized-stack-variables.patch @@ -14,7 +14,7 @@ Signed-off-by: Felix Fietkau --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c -@@ -3431,7 +3431,7 @@ static int hostapd_change_config_freq(st +@@ -3453,7 +3453,7 @@ static int hostapd_change_config_freq(st struct hostapd_freq_params *old_params) { int channel; diff --git a/package/network/services/hostapd/patches/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch b/package/network/services/hostapd/patches/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch index ade0b11311..988fbbc6f6 100644 --- a/package/network/services/hostapd/patches/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch +++ b/package/network/services/hostapd/patches/030-driver_nl80211-rewrite-neigh-code-to-not-depend-on-l.patch @@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau #include #include #include -@@ -5300,26 +5297,29 @@ fail: +@@ -5344,26 +5341,29 @@ fail: static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr) { @@ -64,7 +64,7 @@ Signed-off-by: Felix Fietkau if (err < 0) { wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for " MACSTR " ifindex=%d failed: %s", MAC2STR(addr), -@@ -5329,9 +5329,8 @@ static void rtnl_neigh_delete_fdb_entry( +@@ -5373,9 +5373,8 @@ static void rtnl_neigh_delete_fdb_entry( MACSTR, MAC2STR(addr)); } @@ -76,7 +76,7 @@ Signed-off-by: Felix Fietkau } -@@ -7714,7 +7713,6 @@ static void *i802_init(struct hostapd_da +@@ -7763,7 +7762,6 @@ static void *i802_init(struct hostapd_da (params->num_bridge == 0 || !params->bridge[0])) add_ifidx(drv, br_ifindex, drv->ifindex); @@ -84,7 +84,7 @@ Signed-off-by: Felix Fietkau if (bss->added_if_into_bridge || bss->already_in_bridge) { int err; -@@ -7731,7 +7729,6 @@ static void *i802_init(struct hostapd_da +@@ -7780,7 +7778,6 @@ static void *i802_init(struct hostapd_da goto failed; } } @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) { wpa_printf(MSG_DEBUG, -@@ -10678,13 +10675,14 @@ static int wpa_driver_br_add_ip_neigh(vo +@@ -10813,13 +10810,14 @@ static int wpa_driver_br_add_ip_neigh(vo const u8 *ipaddr, int prefixlen, const u8 *addr) { @@ -112,7 +112,7 @@ Signed-off-by: Felix Fietkau int res; if (!ipaddr || prefixlen == 0 || !addr) -@@ -10703,85 +10701,66 @@ static int wpa_driver_br_add_ip_neigh(vo +@@ -10838,85 +10836,66 @@ static int wpa_driver_br_add_ip_neigh(vo } if (version == 4) { @@ -220,7 +220,7 @@ Signed-off-by: Felix Fietkau addrsize = 16; } else { return -EINVAL; -@@ -10799,41 +10778,30 @@ static int wpa_driver_br_delete_ip_neigh +@@ -10934,41 +10913,30 @@ static int wpa_driver_br_delete_ip_neigh return -1; } diff --git a/package/network/services/hostapd/patches/040-mesh-allow-processing-authentication-frames-in-block.patch b/package/network/services/hostapd/patches/040-mesh-allow-processing-authentication-frames-in-block.patch index 6d9fd81acf..6b34cd4350 100644 --- a/package/network/services/hostapd/patches/040-mesh-allow-processing-authentication-frames-in-block.patch +++ b/package/network/services/hostapd/patches/040-mesh-allow-processing-authentication-frames-in-block.patch @@ -16,7 +16,7 @@ Signed-off-by: Felix Fietkau --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c -@@ -3761,15 +3761,6 @@ static void handle_auth(struct hostapd_d +@@ -3781,15 +3781,6 @@ static void handle_auth(struct hostapd_d seq_ctrl); return; } diff --git a/package/network/services/hostapd/patches/050-build_fix.patch b/package/network/services/hostapd/patches/050-build_fix.patch index 2652a83316..c9268f59e3 100644 --- a/package/network/services/hostapd/patches/050-build_fix.patch +++ b/package/network/services/hostapd/patches/050-build_fix.patch @@ -1,6 +1,6 @@ --- a/hostapd/Makefile +++ b/hostapd/Makefile -@@ -323,6 +323,7 @@ ifdef CONFIG_FILS +@@ -324,6 +324,7 @@ ifdef CONFIG_FILS CFLAGS += -DCONFIG_FILS OBJS += ../src/ap/fils_hlp.o NEED_SHA384=y @@ -10,7 +10,7 @@ CFLAGS += -DCONFIG_FILS_SK_PFS --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile -@@ -312,6 +312,7 @@ endif +@@ -320,6 +320,7 @@ endif ifdef CONFIG_FILS CFLAGS += -DCONFIG_FILS NEED_SHA384=y diff --git a/package/network/services/hostapd/patches/200-multicall.patch b/package/network/services/hostapd/patches/200-multicall.patch index ad82e020f6..576c671155 100644 --- a/package/network/services/hostapd/patches/200-multicall.patch +++ b/package/network/services/hostapd/patches/200-multicall.patch @@ -18,7 +18,7 @@ OBJS += ../src/ap/vlan_init.o OBJS += ../src/ap/vlan_ifconfig.o OBJS += ../src/ap/vlan.o -@@ -350,10 +352,14 @@ CFLAGS += -DCONFIG_MBO +@@ -357,10 +359,14 @@ CFLAGS += -DCONFIG_MBO OBJS += ../src/ap/mbo_ap.o endif @@ -36,7 +36,7 @@ LIBS += $(DRV_AP_LIBS) ifdef CONFIG_L2_PACKET -@@ -1281,6 +1287,12 @@ install: $(addprefix $(DESTDIR)$(BINDIR) +@@ -1291,6 +1297,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 " $@ -@@ -1355,6 +1367,12 @@ include ../src/objs.mk +@@ -1365,6 +1377,12 @@ include ../src/objs.mk _OBJS_VAR := SOBJS include ../src/objs.mk @@ -64,15 +64,15 @@ @$(E) " LD " $@ --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile -@@ -17,6 +17,7 @@ endif +@@ -10,6 +10,7 @@ ALL += dbus/fi.w1.wpa_supplicant1.servic EXTRA_TARGETS=dynamic_eap_methods CONFIG_FILE=.config +-include $(if $(MULTICALL),../hostapd/.config) include ../src/build.rules - ifdef LIBS -@@ -363,7 +364,9 @@ endif + ifdef CONFIG_BUILD_WPA_CLIENT_SO +@@ -371,7 +372,9 @@ endif ifdef CONFIG_IBSS_RSN NEED_RSN_AUTHENTICATOR=y CFLAGS += -DCONFIG_IBSS_RSN @@ -82,7 +82,7 @@ OBJS += ibss_rsn.o endif -@@ -900,6 +903,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS +@@ -912,6 +915,10 @@ ifdef CONFIG_DYNAMIC_EAP_METHODS CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS LIBS += -ldl -rdynamic endif @@ -93,7 +93,7 @@ endif ifdef CONFIG_AP -@@ -907,9 +914,11 @@ NEED_EAP_COMMON=y +@@ -919,9 +926,11 @@ NEED_EAP_COMMON=y NEED_RSN_AUTHENTICATOR=y CFLAGS += -DCONFIG_AP OBJS += ap.o @@ -105,7 +105,7 @@ OBJS += ../src/ap/hostapd.o OBJS += ../src/ap/wpa_auth_glue.o OBJS += ../src/ap/utils.o -@@ -989,6 +998,12 @@ endif +@@ -1008,6 +1017,12 @@ endif ifdef CONFIG_HS20 OBJS += ../src/ap/hs20.o endif @@ -118,7 +118,7 @@ endif ifdef CONFIG_MBO -@@ -997,7 +1012,9 @@ CFLAGS += -DCONFIG_MBO +@@ -1016,7 +1031,9 @@ CFLAGS += -DCONFIG_MBO endif ifdef NEED_RSN_AUTHENTICATOR @@ -128,7 +128,7 @@ NEED_AES_WRAP=y OBJS += ../src/ap/wpa_auth.o OBJS += ../src/ap/wpa_auth_ie.o -@@ -1891,6 +1908,12 @@ wpa_priv: $(BCHECK) $(OBJS_priv) +@@ -1920,6 +1937,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 " $@ -@@ -2023,6 +2046,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK) +@@ -2052,6 +2075,12 @@ eap_gpsk.so: $(SRC_EAP_GPSK) $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ @$(E) " sed" $< @@ -156,7 +156,7 @@ wpa_cli.exe: wpa_cli --- a/src/drivers/driver.h +++ b/src/drivers/driver.h -@@ -6033,8 +6033,8 @@ union wpa_event_data { +@@ -6171,8 +6171,8 @@ union wpa_event_data { * Driver wrapper code should call this function whenever an event is received * from the driver. */ @@ -167,7 +167,7 @@ /** * wpa_supplicant_event_global - Report a driver event for wpa_supplicant -@@ -6046,7 +6046,7 @@ void wpa_supplicant_event(void *ctx, enu +@@ -6184,7 +6184,7 @@ void wpa_supplicant_event(void *ctx, enu * Same as wpa_supplicant_event(), but we search for the interface in * wpa_global. */ @@ -178,7 +178,7 @@ /* --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c -@@ -1842,8 +1842,8 @@ err: +@@ -1872,8 +1872,8 @@ err: #endif /* CONFIG_OWE */ @@ -189,7 +189,7 @@ { struct hostapd_data *hapd = ctx; #ifndef CONFIG_NO_STDOUT_DEBUG -@@ -2088,7 +2088,7 @@ void wpa_supplicant_event(void *ctx, enu +@@ -2145,7 +2145,7 @@ void wpa_supplicant_event(void *ctx, enu } @@ -220,7 +220,7 @@ union wpa_event_data *data) { struct wpa_priv_global *global = ctx; -@@ -1215,6 +1215,8 @@ int main(int argc, char *argv[]) +@@ -1216,6 +1216,8 @@ int main(int argc, char *argv[]) if (os_program_init()) return -1; @@ -231,7 +231,7 @@ os_memset(&global, 0, sizeof(global)); --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c -@@ -4891,8 +4891,8 @@ static void wpas_event_unprot_beacon(str +@@ -4953,8 +4953,8 @@ static void wpas_event_unprot_beacon(str } @@ -242,7 +242,7 @@ { struct wpa_supplicant *wpa_s = ctx; int resched; -@@ -5745,7 +5745,7 @@ void wpa_supplicant_event(void *ctx, enu +@@ -5813,7 +5813,7 @@ void wpa_supplicant_event(void *ctx, enu } @@ -253,7 +253,7 @@ struct wpa_supplicant *wpa_s; --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -7043,7 +7043,6 @@ struct wpa_interface * wpa_supplicant_ma +@@ -7087,7 +7087,6 @@ struct wpa_interface * wpa_supplicant_ma return NULL; } @@ -261,7 +261,7 @@ /** * wpa_supplicant_match_existing - Match existing interfaces * @global: Pointer to global data from wpa_supplicant_init() -@@ -7078,6 +7077,11 @@ static int wpa_supplicant_match_existing +@@ -7122,6 +7121,11 @@ static int wpa_supplicant_match_existing #endif /* CONFIG_MATCH_IFACE */ @@ -273,7 +273,7 @@ /** * wpa_supplicant_add_iface - Add a new network interface -@@ -7334,6 +7338,8 @@ struct wpa_global * wpa_supplicant_init( +@@ -7378,6 +7382,8 @@ struct wpa_global * wpa_supplicant_init( #ifndef CONFIG_NO_WPA_MSG wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb); #endif /* CONFIG_NO_WPA_MSG */ @@ -284,7 +284,7 @@ wpa_debug_open_file(params->wpa_debug_file_path); --- a/hostapd/main.c +++ b/hostapd/main.c -@@ -590,6 +590,11 @@ fail: +@@ -591,6 +591,11 @@ fail: return -1; } @@ -296,14 +296,14 @@ #ifdef CONFIG_WPS static int gen_uuid(const char *txt_addr) -@@ -683,6 +688,8 @@ int main(int argc, char *argv[]) +@@ -684,6 +689,8 @@ int main(int argc, char *argv[]) return -1; #endif /* CONFIG_DPP */ + wpa_supplicant_event = hostapd_wpa_event; + wpa_supplicant_event_global = hostapd_wpa_event_global; for (;;) { - c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:"); + c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q"); if (c < 0) --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -320,7 +320,7 @@ { --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c -@@ -30,7 +30,12 @@ +@@ -31,7 +31,12 @@ #include "ctrl_iface.h" #include "pcsc_funcs.h" #include "wpas_glue.h" @@ -333,7 +333,7 @@ const struct wpa_driver_ops *const wpa_drivers[] = { NULL }; -@@ -1291,6 +1296,10 @@ static void usage(void) +@@ -1303,6 +1308,10 @@ static void usage(void) "option several times.\n"); } @@ -344,7 +344,7 @@ int main(int argc, char *argv[]) { -@@ -1311,6 +1320,8 @@ int main(int argc, char *argv[]) +@@ -1323,6 +1332,8 @@ int main(int argc, char *argv[]) if (os_program_init()) return -1; diff --git a/package/network/services/hostapd/patches/300-noscan.patch b/package/network/services/hostapd/patches/300-noscan.patch index 01a33d0d03..a0e00c4d56 100644 --- a/package/network/services/hostapd/patches/300-noscan.patch +++ b/package/network/services/hostapd/patches/300-noscan.patch @@ -1,6 +1,6 @@ --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -3474,6 +3474,10 @@ static int hostapd_config_fill(struct ho +@@ -3439,6 +3439,10 @@ static int hostapd_config_fill(struct ho if (bss->ocv && !bss->ieee80211w) bss->ieee80211w = 1; #endif /* CONFIG_OCV */ @@ -13,7 +13,7 @@ } else if (os_strcmp(buf, "ht_capab") == 0) { --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h -@@ -1014,6 +1014,8 @@ struct hostapd_config { +@@ -1043,6 +1043,8 @@ struct hostapd_config { int ht_op_mode_fixed; u16 ht_capab; diff --git a/package/network/services/hostapd/patches/301-mesh-noscan.patch b/package/network/services/hostapd/patches/301-mesh-noscan.patch index e682efb543..998540198e 100644 --- a/package/network/services/hostapd/patches/301-mesh-noscan.patch +++ b/package/network/services/hostapd/patches/301-mesh-noscan.patch @@ -1,6 +1,6 @@ --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c -@@ -2532,6 +2532,7 @@ static const struct parse_data ssid_fiel +@@ -2555,6 +2555,7 @@ static const struct parse_data ssid_fiel #else /* CONFIG_MESH */ { INT_RANGE(mode, 0, 4) }, #endif /* CONFIG_MESH */ @@ -10,7 +10,7 @@ { STR(id_str) }, --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c -@@ -769,6 +769,7 @@ static void wpa_config_write_network(FIL +@@ -766,6 +766,7 @@ static void wpa_config_write_network(FIL #endif /* IEEE8021X_EAPOL */ INT(mode); INT(no_auto_peer); @@ -20,7 +20,7 @@ INT(enable_edmg); --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c -@@ -505,6 +505,8 @@ static int wpa_supplicant_mesh_init(stru +@@ -506,6 +506,8 @@ static int wpa_supplicant_mesh_init(stru frequency); goto out_free; } @@ -31,7 +31,7 @@ /* --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -2436,7 +2436,7 @@ void ibss_mesh_setup_freq(struct wpa_sup +@@ -2463,7 +2463,7 @@ void ibss_mesh_setup_freq(struct wpa_sup int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode); enum hostapd_hw_mode hw_mode; struct hostapd_hw_modes *mode = NULL; @@ -40,7 +40,7 @@ 184, 192 }; int bw80[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035, 6115, 6195, 6275, 6355, 6435, 6515, -@@ -2444,7 +2444,7 @@ void ibss_mesh_setup_freq(struct wpa_sup +@@ -2471,7 +2471,7 @@ void ibss_mesh_setup_freq(struct wpa_sup int bw160[] = { 5955, 6115, 6275, 6435, 6595, 6755, 6915 }; struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; u8 channel; @@ -49,7 +49,7 @@ unsigned int j, k; struct hostapd_freq_params vht_freq; int chwidth, seg0, seg1; -@@ -2535,7 +2535,7 @@ void ibss_mesh_setup_freq(struct wpa_sup +@@ -2562,7 +2562,7 @@ void ibss_mesh_setup_freq(struct wpa_sup #endif /* CONFIG_HE_OVERRIDES */ /* Setup higher BW only for 5 GHz */ @@ -60,7 +60,7 @@ for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) { --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h -@@ -974,6 +974,8 @@ struct wpa_ssid { +@@ -981,6 +981,8 @@ struct wpa_ssid { */ int no_auto_peer; diff --git a/package/network/services/hostapd/patches/310-rescan_immediately.patch b/package/network/services/hostapd/patches/310-rescan_immediately.patch index b0c1cb8354..2c2541947c 100644 --- a/package/network/services/hostapd/patches/310-rescan_immediately.patch +++ b/package/network/services/hostapd/patches/310-rescan_immediately.patch @@ -1,6 +1,6 @@ --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -5377,7 +5377,7 @@ wpa_supplicant_alloc(struct wpa_supplica +@@ -5419,7 +5419,7 @@ wpa_supplicant_alloc(struct wpa_supplica if (wpa_s == NULL) return NULL; wpa_s->scan_req = INITIAL_SCAN_REQ; diff --git a/package/network/services/hostapd/patches/330-nl80211_fix_set_freq.patch b/package/network/services/hostapd/patches/330-nl80211_fix_set_freq.patch index 37033c3035..8218a439ac 100644 --- a/package/network/services/hostapd/patches/330-nl80211_fix_set_freq.patch +++ b/package/network/services/hostapd/patches/330-nl80211_fix_set_freq.patch @@ -1,8 +1,8 @@ --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c -@@ -4986,7 +4986,7 @@ static int nl80211_set_channel(struct i8 - freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled, - freq->bandwidth, freq->center_freq1, freq->center_freq2); +@@ -5022,7 +5022,7 @@ static int nl80211_set_channel(struct i8 + freq->he_enabled, freq->eht_enabled, freq->bandwidth, + freq->center_freq1, freq->center_freq2); - msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL : + msg = nl80211_bss_msg(bss, 0, set_chan ? NL80211_CMD_SET_CHANNEL : diff --git a/package/network/services/hostapd/patches/340-reload_freq_change.patch b/package/network/services/hostapd/patches/340-reload_freq_change.patch index 89259f2f44..b591074634 100644 --- a/package/network/services/hostapd/patches/340-reload_freq_change.patch +++ b/package/network/services/hostapd/patches/340-reload_freq_change.patch @@ -1,6 +1,6 @@ --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c -@@ -115,6 +115,29 @@ static void hostapd_reload_bss(struct ho +@@ -119,6 +119,29 @@ static void hostapd_reload_bss(struct ho #endif /* CONFIG_NO_RADIUS */ ssid = &hapd->conf->ssid; @@ -30,7 +30,7 @@ if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next && ssid->wpa_passphrase_set && ssid->wpa_passphrase) { /* -@@ -216,6 +238,7 @@ int hostapd_reload_config(struct hostapd +@@ -220,6 +243,7 @@ int hostapd_reload_config(struct hostapd struct hostapd_data *hapd = iface->bss[0]; struct hostapd_config *newconf, *oldconf; size_t j; @@ -38,7 +38,7 @@ if (iface->config_fname == NULL) { /* Only in-memory config in use - assume it has been updated */ -@@ -266,24 +289,20 @@ int hostapd_reload_config(struct hostapd +@@ -270,24 +294,20 @@ int hostapd_reload_config(struct hostapd } iface->conf = newconf; diff --git a/package/network/services/hostapd/patches/341-mesh-ctrl-iface-channel-switch.patch b/package/network/services/hostapd/patches/341-mesh-ctrl-iface-channel-switch.patch index b13dcb0673..29a3799b5e 100644 --- a/package/network/services/hostapd/patches/341-mesh-ctrl-iface-channel-switch.patch +++ b/package/network/services/hostapd/patches/341-mesh-ctrl-iface-channel-switch.patch @@ -1,6 +1,6 @@ --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c -@@ -1611,15 +1611,35 @@ int ap_switch_channel(struct wpa_supplic +@@ -1803,15 +1803,35 @@ int ap_switch_channel(struct wpa_supplic #ifdef CONFIG_CTRL_IFACE 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 8a2beb312f..85298df7ea 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 @@ -1,6 +1,6 @@ --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c -@@ -2932,11 +2932,11 @@ static int wpa_driver_nl80211_del_beacon +@@ -2938,11 +2938,11 @@ static int wpa_driver_nl80211_del_beacon struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", @@ -14,7 +14,7 @@ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); } -@@ -5650,7 +5650,7 @@ static void nl80211_teardown_ap(struct i +@@ -5661,7 +5661,7 @@ static void nl80211_teardown_ap(struct i nl80211_mgmt_unsubscribe(bss, "AP teardown"); nl80211_put_wiphy_data_ap(bss); @@ -23,7 +23,7 @@ } -@@ -8104,8 +8104,6 @@ static int wpa_driver_nl80211_if_remove( +@@ -8120,8 +8120,6 @@ static int wpa_driver_nl80211_if_remove( } else { wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); nl80211_teardown_ap(bss); diff --git a/package/network/services/hostapd/patches/360-ctrl_iface_reload.patch b/package/network/services/hostapd/patches/360-ctrl_iface_reload.patch index 7f3aa91889..7699541fd4 100644 --- a/package/network/services/hostapd/patches/360-ctrl_iface_reload.patch +++ b/package/network/services/hostapd/patches/360-ctrl_iface_reload.patch @@ -78,7 +78,7 @@ #ifdef NEED_AP_MLME static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, -@@ -3771,6 +3828,8 @@ static int hostapd_ctrl_iface_receive_pr +@@ -3449,6 +3506,8 @@ static int hostapd_ctrl_iface_receive_pr } else if (os_strncmp(buf, "VENDOR ", 7) == 0) { reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply, reply_size); @@ -89,7 +89,7 @@ #ifdef RADIUS_SERVER --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c -@@ -927,7 +927,13 @@ int hostapd_parse_csa_settings(const cha +@@ -945,7 +945,13 @@ int hostapd_parse_csa_settings(const cha int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd) { diff --git a/package/network/services/hostapd/patches/370-ap_sta_support.patch b/package/network/services/hostapd/patches/370-ap_sta_support.patch index c81c841a43..6faaffcf72 100644 --- a/package/network/services/hostapd/patches/370-ap_sta_support.patch +++ b/package/network/services/hostapd/patches/370-ap_sta_support.patch @@ -1,6 +1,6 @@ --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile -@@ -108,6 +108,8 @@ OBJS_c += ../src/utils/common.o +@@ -115,6 +115,8 @@ OBJS_c += ../src/utils/common.o OBJS_c += ../src/common/cli.o OBJS += wmm_ac.o @@ -30,7 +30,7 @@ dst->flags = src->flags; os_memcpy(dst->bssid, src->bssid, ETH_ALEN); dst->freq = src->freq; -@@ -294,6 +299,15 @@ static void wpa_bss_copy_res(struct wpa_ +@@ -295,6 +300,15 @@ static void wpa_bss_copy_res(struct wpa_ dst->est_throughput = src->est_throughput; dst->snr = src->snr; @@ -61,7 +61,7 @@ /** Beacon interval in TUs (host byte order) */ --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c -@@ -34,7 +34,7 @@ static void usage(void) +@@ -35,7 +35,7 @@ static void usage(void) "vW] [-P] " "[-g] \\\n" " [-G] \\\n" @@ -70,7 +70,7 @@ "[-p] \\\n" " [-b] [-e]" #ifdef CONFIG_DEBUG_FILE -@@ -74,6 +74,7 @@ static void usage(void) +@@ -75,6 +75,7 @@ static void usage(void) " -g = global ctrl_interface\n" " -G = global ctrl_interface group\n" " -h = show this help text\n" @@ -78,7 +78,7 @@ " -i = interface name\n" " -I = additional configuration file\n" " -K = include keys (passwords, etc.) in debug output\n" -@@ -201,7 +202,7 @@ int main(int argc, char *argv[]) +@@ -202,7 +203,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, @@ -87,7 +87,7 @@ if (c < 0) break; switch (c) { -@@ -248,6 +249,9 @@ int main(int argc, char *argv[]) +@@ -249,6 +250,9 @@ int main(int argc, char *argv[]) usage(); exitcode = 0; goto out; @@ -99,7 +99,7 @@ break; --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -130,6 +130,54 @@ static void wpas_update_fils_connect_par +@@ -131,6 +131,54 @@ static void wpas_update_fils_connect_par static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s); #endif /* CONFIG_OWE */ @@ -154,7 +154,7 @@ #ifdef CONFIG_WEP /* Configure default/group WEP keys for static WEP */ -@@ -1015,6 +1063,8 @@ void wpa_supplicant_set_state(struct wpa +@@ -1016,6 +1064,8 @@ void wpa_supplicant_set_state(struct wpa sme_sched_obss_scan(wpa_s, 1); @@ -163,7 +163,7 @@ #if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) if (!fils_hlp_sent && ssid && ssid->eap.erp) update_fils_connect_params = true; -@@ -1025,6 +1075,8 @@ void wpa_supplicant_set_state(struct wpa +@@ -1026,6 +1076,8 @@ void wpa_supplicant_set_state(struct wpa #endif /* CONFIG_OWE */ } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { @@ -172,7 +172,7 @@ wpa_s->new_connection = 1; wpa_drv_set_operstate(wpa_s, 0); #ifndef IEEE8021X_EAPOL -@@ -2308,6 +2360,8 @@ void wpa_supplicant_associate(struct wpa +@@ -2335,6 +2387,8 @@ void wpa_supplicant_associate(struct wpa return; } wpa_s->current_bss = bss; @@ -181,7 +181,7 @@ #else /* CONFIG_MESH */ wpa_msg(wpa_s, MSG_ERROR, "mesh mode support not included in the build"); -@@ -6650,6 +6704,16 @@ static int wpa_supplicant_init_iface(str +@@ -6693,6 +6747,16 @@ static int wpa_supplicant_init_iface(str sizeof(wpa_s->bridge_ifname)); } @@ -198,7 +198,7 @@ /* RSNA Supplicant Key Management - INITIALIZE */ eapol_sm_notify_portEnabled(wpa_s->eapol, false); eapol_sm_notify_portValid(wpa_s->eapol, false); -@@ -6987,6 +7051,11 @@ static void wpa_supplicant_deinit_iface( +@@ -7031,6 +7095,11 @@ static void wpa_supplicant_deinit_iface( if (terminate) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING); @@ -212,7 +212,7 @@ --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h -@@ -104,6 +104,11 @@ struct wpa_interface { +@@ -105,6 +105,11 @@ struct wpa_interface { const char *ifname; /** @@ -224,7 +224,7 @@ * bridge_ifname - Optional bridge interface name * * If the driver interface (ifname) is included in a Linux bridge -@@ -718,6 +723,8 @@ struct wpa_supplicant { +@@ -717,6 +722,8 @@ struct wpa_supplicant { #endif /* CONFIG_CTRL_IFACE_BINDER */ char bridge_ifname[16]; @@ -235,7 +235,7 @@ --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c -@@ -2889,6 +2889,12 @@ static int hostapd_ctrl_iface_chan_switc +@@ -2641,6 +2641,12 @@ static int hostapd_ctrl_iface_chan_switc return 0; } @@ -247,10 +247,10 @@ + for (i = 0; i < iface->num_bss; i++) { - /* Save CHAN_SWITCH VHT and HE config */ + /* Save CHAN_SWITCH VHT, HE, and EHT config */ --- a/src/ap/beacon.c +++ b/src/ap/beacon.c -@@ -1791,11 +1791,6 @@ static int __ieee802_11_set_beacon(struc +@@ -1903,11 +1903,6 @@ static int __ieee802_11_set_beacon(struc return -1; } @@ -264,7 +264,7 @@ if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c -@@ -4891,6 +4891,60 @@ static void wpas_event_unprot_beacon(str +@@ -4953,6 +4953,60 @@ static void wpas_event_unprot_beacon(str } @@ -325,7 +325,7 @@ void supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { -@@ -5206,8 +5260,10 @@ void supplicant_event(void *ctx, enum wp +@@ -5268,8 +5322,10 @@ void supplicant_event(void *ctx, enum wp channel_width_to_string(data->ch_switch.ch_width), data->ch_switch.cf1, data->ch_switch.cf2); @@ -339,7 +339,7 @@ wpa_s->current_ssid->frequency = data->ch_switch.freq; --- a/src/drivers/driver.h +++ b/src/drivers/driver.h -@@ -5837,6 +5837,7 @@ union wpa_event_data { +@@ -5968,6 +5968,7 @@ union wpa_event_data { /** * struct ch_switch @@ -347,7 +347,7 @@ * @freq: Frequency of new channel in MHz * @ht_enabled: Whether this is an HT channel * @ch_offset: Secondary channel offset -@@ -5845,6 +5846,7 @@ union wpa_event_data { +@@ -5976,6 +5977,7 @@ union wpa_event_data { * @cf2: Center frequency 2 */ struct ch_switch { @@ -357,7 +357,7 @@ int ch_offset; --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c -@@ -684,7 +684,7 @@ static void mlme_event_ch_switch(struct +@@ -694,7 +694,7 @@ static void mlme_event_ch_switch(struct struct nlattr *ifindex, struct nlattr *freq, struct nlattr *type, struct nlattr *bw, struct nlattr *cf1, struct nlattr *cf2, @@ -366,7 +366,7 @@ { struct i802_bss *bss; union wpa_event_data data; -@@ -745,6 +745,8 @@ static void mlme_event_ch_switch(struct +@@ -755,6 +755,8 @@ static void mlme_event_ch_switch(struct data.ch_switch.cf1 = nla_get_u32(cf1); if (cf2) data.ch_switch.cf2 = nla_get_u32(cf2); @@ -375,7 +375,7 @@ if (finished) bss->freq = data.ch_switch.freq; -@@ -3003,6 +3005,7 @@ static void do_process_drv_event(struct +@@ -3113,6 +3115,7 @@ static void do_process_drv_event(struct tb[NL80211_ATTR_CHANNEL_WIDTH], tb[NL80211_ATTR_CENTER_FREQ1], tb[NL80211_ATTR_CENTER_FREQ2], @@ -383,7 +383,7 @@ 0); break; case NL80211_CMD_CH_SWITCH_NOTIFY: -@@ -3013,6 +3016,7 @@ static void do_process_drv_event(struct +@@ -3123,6 +3126,7 @@ static void do_process_drv_event(struct tb[NL80211_ATTR_CHANNEL_WIDTH], tb[NL80211_ATTR_CENTER_FREQ1], tb[NL80211_ATTR_CENTER_FREQ2], diff --git a/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch b/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch index 92b52a6d37..1f78c42dee 100644 --- a/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch +++ b/package/network/services/hostapd/patches/380-disable_ctrl_iface_mib.patch @@ -12,7 +12,7 @@ else --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c -@@ -3587,6 +3587,7 @@ static int hostapd_ctrl_iface_receive_pr +@@ -3265,6 +3265,7 @@ static int hostapd_ctrl_iface_receive_pr reply_size); } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) { reply_len = hostapd_drv_status(hapd, reply, reply_size); @@ -20,7 +20,7 @@ } else if (os_strcmp(buf, "MIB") == 0) { reply_len = ieee802_11_get_mib(hapd, reply, reply_size); if (reply_len >= 0) { -@@ -3628,6 +3629,7 @@ static int hostapd_ctrl_iface_receive_pr +@@ -3306,6 +3307,7 @@ static int hostapd_ctrl_iface_receive_pr } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, reply_size); @@ -30,7 +30,7 @@ reply_len = -1; --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile -@@ -958,6 +958,9 @@ ifdef CONFIG_FILS +@@ -973,6 +973,9 @@ ifdef CONFIG_FILS OBJS += ../src/ap/fils_hlp.o endif ifdef CONFIG_CTRL_IFACE @@ -42,7 +42,7 @@ --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c -@@ -2314,7 +2314,7 @@ static int wpa_supplicant_ctrl_iface_sta +@@ -2325,7 +2325,7 @@ static int wpa_supplicant_ctrl_iface_sta pos += ret; } @@ -51,7 +51,7 @@ if (wpa_s->ap_iface) { pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos, end - pos, -@@ -11494,6 +11494,7 @@ char * wpa_supplicant_ctrl_iface_process +@@ -11565,6 +11565,7 @@ char * wpa_supplicant_ctrl_iface_process reply_len = -1; } else if (os_strncmp(buf, "NOTE ", 5) == 0) { wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); @@ -59,7 +59,7 @@ } else if (os_strcmp(buf, "MIB") == 0) { reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); if (reply_len >= 0) { -@@ -11506,6 +11507,7 @@ char * wpa_supplicant_ctrl_iface_process +@@ -11577,6 +11578,7 @@ char * wpa_supplicant_ctrl_iface_process reply_size - reply_len); #endif /* CONFIG_MACSEC */ } @@ -67,7 +67,7 @@ } else if (os_strncmp(buf, "STATUS", 6) == 0) { reply_len = wpa_supplicant_ctrl_iface_status( wpa_s, buf + 6, reply, reply_size); -@@ -11994,6 +11996,7 @@ char * wpa_supplicant_ctrl_iface_process +@@ -12065,6 +12067,7 @@ char * wpa_supplicant_ctrl_iface_process reply_len = wpa_supplicant_ctrl_iface_bss( wpa_s, buf + 4, reply, reply_size); #ifdef CONFIG_AP @@ -75,7 +75,7 @@ } else if (os_strcmp(buf, "STA-FIRST") == 0) { reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size); } else if (os_strncmp(buf, "STA ", 4) == 0) { -@@ -12002,12 +12005,15 @@ char * wpa_supplicant_ctrl_iface_process +@@ -12073,12 +12076,15 @@ char * wpa_supplicant_ctrl_iface_process } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply, reply_size); @@ -93,15 +93,15 @@ reply_len = -1; --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c -@@ -25,6 +25,7 @@ - #include "mbo_ap.h" +@@ -26,6 +26,7 @@ #include "taxonomy.h" + #include "wnm_ap.h" +#ifdef CONFIG_CTRL_IFACE_MIB static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen, size_t curr_len, const u8 *mcs_set) -@@ -459,6 +460,7 @@ int hostapd_ctrl_iface_sta_next(struct h +@@ -460,6 +461,7 @@ int hostapd_ctrl_iface_sta_next(struct h return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); } @@ -109,7 +109,7 @@ #ifdef CONFIG_P2P_MANAGER static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, -@@ -815,12 +817,12 @@ int hostapd_ctrl_iface_status(struct hos +@@ -832,12 +834,12 @@ int hostapd_ctrl_iface_status(struct hos return len; len += ret; } @@ -126,7 +126,7 @@ if (os_snprintf_error(buflen - len, ret)) --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c -@@ -2712,6 +2712,7 @@ static const char * bool_txt(bool val) +@@ -2740,6 +2740,7 @@ static const char * bool_txt(bool val) return val ? "TRUE" : "FALSE"; } @@ -134,7 +134,7 @@ int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) { -@@ -2898,6 +2899,7 @@ int ieee802_1x_get_mib_sta(struct hostap +@@ -2926,6 +2927,7 @@ int ieee802_1x_get_mib_sta(struct hostap return len; } @@ -144,7 +144,7 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c -@@ -4519,6 +4519,7 @@ static const char * wpa_bool_txt(int val +@@ -4559,6 +4559,7 @@ static const char * wpa_bool_txt(int val return val ? "TRUE" : "FALSE"; } @@ -152,7 +152,7 @@ #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ -@@ -4669,7 +4670,7 @@ int wpa_get_mib_sta(struct wpa_state_mac +@@ -4709,7 +4710,7 @@ int wpa_get_mib_sta(struct wpa_state_mac return len; } @@ -163,7 +163,7 @@ { --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c -@@ -2777,6 +2777,8 @@ static u32 wpa_key_mgmt_suite(struct wpa +@@ -2802,6 +2802,8 @@ static u32 wpa_key_mgmt_suite(struct wpa } @@ -172,7 +172,7 @@ #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) \ ((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff -@@ -2858,6 +2860,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, ch +@@ -2883,6 +2885,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, ch return (int) len; } @@ -182,7 +182,7 @@ --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c -@@ -1462,7 +1462,7 @@ int wpas_ap_wps_nfc_report_handover(stru +@@ -1477,7 +1477,7 @@ int wpas_ap_wps_nfc_report_handover(stru #endif /* CONFIG_WPS */ diff --git a/package/network/services/hostapd/patches/390-wpa_ie_cap_workaround.patch b/package/network/services/hostapd/patches/390-wpa_ie_cap_workaround.patch index 65a8b07e65..bf481c3bab 100644 --- a/package/network/services/hostapd/patches/390-wpa_ie_cap_workaround.patch +++ b/package/network/services/hostapd/patches/390-wpa_ie_cap_workaround.patch @@ -1,6 +1,6 @@ --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c -@@ -2444,6 +2444,31 @@ u32 wpa_akm_to_suite(int akm) +@@ -2529,6 +2529,31 @@ u32 wpa_akm_to_suite(int akm) } @@ -32,7 +32,7 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, const u8 *ie2, size_t ie2len) -@@ -2451,8 +2476,19 @@ int wpa_compare_rsn_ie(int ft_initial_as +@@ -2536,8 +2561,19 @@ int wpa_compare_rsn_ie(int ft_initial_as if (ie1 == NULL || ie2 == NULL) return -1; diff --git a/package/network/services/hostapd/patches/400-wps_single_auth_enc_type.patch b/package/network/services/hostapd/patches/400-wps_single_auth_enc_type.patch index f708bf39ba..edcd985257 100644 --- a/package/network/services/hostapd/patches/400-wps_single_auth_enc_type.patch +++ b/package/network/services/hostapd/patches/400-wps_single_auth_enc_type.patch @@ -11,7 +11,7 @@ bss->wpa_pairwise |= WPA_CIPHER_TKIP; #endif /* CONFIG_NO_TKIP */ bss->rsn_pairwise = bss->wpa_pairwise; -@@ -1180,8 +1179,7 @@ int hostapd_init_wps(struct hostapd_data +@@ -1181,8 +1180,7 @@ int hostapd_init_wps(struct hostapd_data WPA_CIPHER_GCMP_256)) { wps->encr_types |= WPS_ENCR_AES; wps->encr_types_rsn |= WPS_ENCR_AES; diff --git a/package/network/services/hostapd/patches/430-hostapd_cli_ifdef.patch b/package/network/services/hostapd/patches/430-hostapd_cli_ifdef.patch index dc1fa3d29d..e52420953a 100644 --- a/package/network/services/hostapd/patches/430-hostapd_cli_ifdef.patch +++ b/package/network/services/hostapd/patches/430-hostapd_cli_ifdef.patch @@ -32,7 +32,7 @@ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc, -@@ -1579,13 +1575,10 @@ static const struct hostapd_cli_cmd host +@@ -1588,13 +1584,10 @@ static const struct hostapd_cli_cmd host { "disassociate", hostapd_cli_cmd_disassociate, hostapd_complete_stations, " = disassociate a station" }, @@ -46,7 +46,7 @@ { "wps_pin", hostapd_cli_cmd_wps_pin, NULL, " [timeout] [addr] = add WPS Enrollee PIN" }, { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL, -@@ -1610,7 +1603,6 @@ static const struct hostapd_cli_cmd host +@@ -1619,7 +1612,6 @@ static const struct hostapd_cli_cmd host " = configure AP" }, { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, "= show current WPS status" }, diff --git a/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch b/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch index 28f07c7dc2..38ff663173 100644 --- a/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch +++ b/package/network/services/hostapd/patches/460-wpa_supplicant-add-new-config-params-to-be-used-with.patch @@ -22,7 +22,7 @@ Signed-hostap: Antonio Quartulli #include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" -@@ -857,6 +858,9 @@ struct wpa_driver_associate_params { +@@ -894,6 +895,9 @@ struct wpa_driver_associate_params { * responsible for selecting with which BSS to associate. */ const u8 *bssid; @@ -42,7 +42,7 @@ Signed-hostap: Antonio Quartulli #include "config.h" -@@ -2321,6 +2322,97 @@ static char * wpa_config_write_peerkey(c +@@ -2345,6 +2346,97 @@ static char * wpa_config_write_peerkey(c #endif /* NO_CONFIG_WRITE */ @@ -140,7 +140,7 @@ Signed-hostap: Antonio Quartulli /* Helper macros for network block parser */ #ifdef OFFSET -@@ -2606,6 +2698,8 @@ static const struct parse_data ssid_fiel +@@ -2629,6 +2721,8 @@ static const struct parse_data ssid_fiel { INT(ap_max_inactivity) }, { INT(dtim_period) }, { INT(beacon_int) }, @@ -174,7 +174,7 @@ Signed-hostap: Antonio Quartulli * macsec_policy - Determines the policy for MACsec secure session --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -3865,6 +3865,12 @@ static void wpas_start_assoc_cb(struct w +@@ -3899,6 +3899,12 @@ static void wpas_start_assoc_cb(struct w params.beacon_int = ssid->beacon_int; else params.beacon_int = wpa_s->conf->beacon_int; diff --git a/package/network/services/hostapd/patches/461-driver_nl80211-use-new-parameters-during-ibss-join.patch b/package/network/services/hostapd/patches/461-driver_nl80211-use-new-parameters-during-ibss-join.patch index 0be77f9845..65d67b8b05 100644 --- a/package/network/services/hostapd/patches/461-driver_nl80211-use-new-parameters-during-ibss-join.patch +++ b/package/network/services/hostapd/patches/461-driver_nl80211-use-new-parameters-during-ibss-join.patch @@ -10,7 +10,7 @@ Signed-hostap: Antonio Quartulli --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c -@@ -5966,7 +5966,7 @@ static int wpa_driver_nl80211_ibss(struc +@@ -6005,7 +6005,7 @@ static int wpa_driver_nl80211_ibss(struc struct wpa_driver_associate_params *params) { struct nl_msg *msg; @@ -19,7 +19,7 @@ Signed-hostap: Antonio Quartulli int count = 0; wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex); -@@ -5993,6 +5993,37 @@ retry: +@@ -6032,6 +6032,37 @@ retry: nl80211_put_beacon_int(msg, params->beacon_int)) goto fail; diff --git a/package/network/services/hostapd/patches/463-add-mcast_rate-to-11s.patch b/package/network/services/hostapd/patches/463-add-mcast_rate-to-11s.patch index bd1d4d7565..5dc19fedee 100644 --- a/package/network/services/hostapd/patches/463-add-mcast_rate-to-11s.patch +++ b/package/network/services/hostapd/patches/463-add-mcast_rate-to-11s.patch @@ -19,7 +19,7 @@ Tested-by: Simon Wunderlich --- a/src/drivers/driver.h +++ b/src/drivers/driver.h -@@ -1624,6 +1624,7 @@ struct wpa_driver_mesh_join_params { +@@ -1661,6 +1661,7 @@ struct wpa_driver_mesh_join_params { #define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008 unsigned int flags; bool handle_dfs; @@ -29,7 +29,7 @@ Tested-by: Simon Wunderlich struct wpa_driver_set_key_params { --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c -@@ -10496,6 +10496,18 @@ static int nl80211_put_mesh_id(struct nl +@@ -10627,6 +10627,18 @@ static int nl80211_put_mesh_id(struct nl } @@ -48,7 +48,7 @@ Tested-by: Simon Wunderlich static int nl80211_put_mesh_config(struct nl_msg *msg, struct wpa_driver_mesh_bss_params *params) { -@@ -10557,6 +10569,7 @@ static int nl80211_join_mesh(struct i802 +@@ -10688,6 +10700,7 @@ static int nl80211_join_mesh(struct i802 nl80211_put_basic_rates(msg, params->basic_rates) || nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) || nl80211_put_beacon_int(msg, params->beacon_int) || @@ -58,7 +58,7 @@ Tested-by: Simon Wunderlich --- a/wpa_supplicant/mesh.c +++ b/wpa_supplicant/mesh.c -@@ -631,6 +631,7 @@ int wpa_supplicant_join_mesh(struct wpa_ +@@ -632,6 +632,7 @@ int wpa_supplicant_join_mesh(struct wpa_ params->meshid = ssid->ssid; params->meshid_len = ssid->ssid_len; diff --git a/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch b/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch index 4807727e0e..48086ea0e3 100644 --- a/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch +++ b/package/network/services/hostapd/patches/464-fix-mesh-obss-check.patch @@ -1,6 +1,6 @@ --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -2512,11 +2512,13 @@ void ibss_mesh_setup_freq(struct wpa_sup +@@ -2539,11 +2539,13 @@ void ibss_mesh_setup_freq(struct wpa_sup for (j = 0; j < wpa_s->last_scan_res_used; j++) { struct wpa_bss *bss = wpa_s->last_scan_res[j]; diff --git a/package/network/services/hostapd/patches/465-hostapd-config-support-random-BSS-color.patch b/package/network/services/hostapd/patches/465-hostapd-config-support-random-BSS-color.patch index c0b01196c4..6810b797c9 100644 --- a/package/network/services/hostapd/patches/465-hostapd-config-support-random-BSS-color.patch +++ b/package/network/services/hostapd/patches/465-hostapd-config-support-random-BSS-color.patch @@ -13,7 +13,7 @@ Signed-off-by: David Bauer --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -3485,6 +3485,8 @@ static int hostapd_config_fill(struct ho +@@ -3489,6 +3489,8 @@ static int hostapd_config_fill(struct ho } else if (os_strcmp(buf, "he_bss_color") == 0) { conf->he_op.he_bss_color = atoi(pos) & 0x3f; conf->he_op.he_bss_color_disabled = 0; 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 c51db01fec..e0458b2054 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 -@@ -1297,7 +1297,7 @@ hostapd_multi.a: $(BCHECK) $(OBJS) +@@ -1307,7 +1307,7 @@ hostapd_multi.a: $(BCHECK) $(OBJS) @$(AR) cr $@ hostapd_multi.o $(OBJS) hostapd: $(OBJS) @@ -9,7 +9,7 @@ @$(E) " LD " $@ ifdef CONFIG_WPA_TRACE -@@ -1308,7 +1308,7 @@ _OBJS_VAR := OBJS_c +@@ -1318,7 +1318,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 -@@ -1920,31 +1920,31 @@ wpa_supplicant_multi.a: .config $(BCHECK +@@ -1949,31 +1949,31 @@ wpa_supplicant_multi.a: .config $(BCHECK @$(AR) cr $@ wpa_supplicant_multi.o $(OBJS) wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs) diff --git a/package/network/services/hostapd/patches/590-rrm-wnm-statistics.patch b/package/network/services/hostapd/patches/590-rrm-wnm-statistics.patch index ee3ab7938a..98b8820cd7 100644 --- a/package/network/services/hostapd/patches/590-rrm-wnm-statistics.patch +++ b/package/network/services/hostapd/patches/590-rrm-wnm-statistics.patch @@ -1,6 +1,6 @@ --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h -@@ -150,6 +150,21 @@ struct hostapd_sae_commit_queue { +@@ -162,6 +162,21 @@ struct hostapd_sae_commit_queue { }; /** @@ -22,7 +22,7 @@ * struct hostapd_data - hostapd per-BSS data structure */ struct hostapd_data { -@@ -163,6 +178,9 @@ struct hostapd_data { +@@ -175,6 +190,9 @@ struct hostapd_data { u8 own_addr[ETH_ALEN]; @@ -42,7 +42,7 @@ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " "validity_interval=%u", -@@ -646,10 +647,12 @@ int ieee802_11_rx_wnm_action_ap(struct h +@@ -659,10 +660,12 @@ int ieee802_11_rx_wnm_action_ap(struct h switch (action) { case WNM_BSS_TRANS_MGMT_QUERY: @@ -55,7 +55,7 @@ ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload, plen); return 0; -@@ -696,6 +699,7 @@ int wnm_send_disassoc_imminent(struct ho +@@ -709,6 +712,7 @@ int wnm_send_disassoc_imminent(struct ho pos = mgmt->u.action.u.bss_tm_req.variable; @@ -63,7 +63,7 @@ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to " MACSTR, disassoc_timer, MAC2STR(sta->addr)); if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) { -@@ -777,6 +781,7 @@ int wnm_send_ess_disassoc_imminent(struc +@@ -790,6 +794,7 @@ int wnm_send_ess_disassoc_imminent(struc return -1; } @@ -71,7 +71,7 @@ if (disassoc_timer) { /* send disassociation frame after time-out */ set_disassoc_timer(hapd, sta, disassoc_timer); -@@ -857,6 +862,7 @@ int wnm_send_bss_tm_req(struct hostapd_d +@@ -870,6 +875,7 @@ int wnm_send_bss_tm_req(struct hostapd_d } os_free(buf); diff --git a/package/network/services/hostapd/patches/600-ubus_support.patch b/package/network/services/hostapd/patches/600-ubus_support.patch index 4abb6887f6..7c6c5e3814 100644 --- a/package/network/services/hostapd/patches/600-ubus_support.patch +++ b/package/network/services/hostapd/patches/600-ubus_support.patch @@ -14,7 +14,7 @@ CFLAGS += -O0 -fprofile-arcs -ftest-coverage --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h -@@ -17,6 +17,7 @@ +@@ -18,6 +18,7 @@ #include "utils/list.h" #include "ap_config.h" #include "drivers/driver.h" @@ -22,7 +22,7 @@ #define OCE_STA_CFON_ENABLED(hapd) \ ((hapd->conf->oce & OCE_STA_CFON) && \ -@@ -80,7 +81,7 @@ struct hapd_interfaces { +@@ -92,7 +93,7 @@ struct hapd_interfaces { #ifdef CONFIG_CTRL_IFACE_UDP unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN]; #endif /* CONFIG_CTRL_IFACE_UDP */ @@ -31,7 +31,7 @@ }; enum hostapd_chan_status { -@@ -171,6 +172,7 @@ struct hostapd_data { +@@ -183,6 +184,7 @@ struct hostapd_data { struct hostapd_iface *iface; struct hostapd_config *iconf; struct hostapd_bss_config *conf; @@ -39,7 +39,7 @@ int interface_added; /* virtual interface added for this BSS */ unsigned int started:1; unsigned int disabled:1; -@@ -630,6 +632,7 @@ hostapd_alloc_bss_data(struct hostapd_if +@@ -673,6 +675,7 @@ hostapd_alloc_bss_data(struct hostapd_if struct hostapd_bss_config *bss); int hostapd_setup_interface(struct hostapd_iface *iface); int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); @@ -49,7 +49,7 @@ struct hostapd_iface * hostapd_alloc_iface(void); --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c -@@ -396,6 +396,7 @@ void hostapd_free_hapd_data(struct hosta +@@ -401,6 +401,7 @@ void hostapd_free_hapd_data(struct hosta hapd->beacon_set_done = 0; wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); @@ -57,7 +57,7 @@ accounting_deinit(hapd); hostapd_deinit_wpa(hapd); vlan_deinit(hapd); -@@ -1422,6 +1423,8 @@ static int hostapd_setup_bss(struct host +@@ -1431,6 +1432,8 @@ static int hostapd_setup_bss(struct host if (hapd->driver && hapd->driver->set_operstate) hapd->driver->set_operstate(hapd->drv_priv, 1); @@ -66,7 +66,7 @@ return 0; } -@@ -2028,6 +2031,7 @@ static int hostapd_setup_interface_compl +@@ -2050,6 +2053,7 @@ static int hostapd_setup_interface_compl if (err) goto fail; @@ -74,7 +74,7 @@ wpa_printf(MSG_DEBUG, "Completing interface initialization"); if (iface->freq) { #ifdef NEED_AP_MLME -@@ -2225,6 +2229,7 @@ dfs_offload: +@@ -2248,6 +2252,7 @@ dfs_offload: fail: wpa_printf(MSG_ERROR, "Interface initialization failed"); @@ -82,7 +82,7 @@ hostapd_set_state(iface, HAPD_IFACE_DISABLED); wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); #ifdef CONFIG_FST -@@ -2700,6 +2705,7 @@ void hostapd_interface_deinit_free(struc +@@ -2723,6 +2728,7 @@ void hostapd_interface_deinit_free(struc (unsigned int) iface->conf->num_bss); driver = iface->bss[0]->driver; drv_priv = iface->bss[0]->drv_priv; @@ -92,7 +92,7 @@ __func__, driver, drv_priv); --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c -@@ -3553,13 +3553,18 @@ static void handle_auth(struct hostapd_d +@@ -3573,13 +3573,18 @@ static void handle_auth(struct hostapd_d u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; struct sta_info *sta = NULL; @@ -112,7 +112,7 @@ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", -@@ -3727,6 +3732,13 @@ static void handle_auth(struct hostapd_d +@@ -3747,6 +3752,13 @@ static void handle_auth(struct hostapd_d resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -126,7 +126,7 @@ if (res == HOSTAPD_ACL_PENDING) return; -@@ -5447,7 +5459,7 @@ static void handle_assoc(struct hostapd_ +@@ -5488,7 +5500,7 @@ static void handle_assoc(struct hostapd_ int resp = WLAN_STATUS_SUCCESS; u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE; const u8 *pos; @@ -135,7 +135,7 @@ struct sta_info *sta; u8 *tmp = NULL; #ifdef CONFIG_FILS -@@ -5660,6 +5672,11 @@ static void handle_assoc(struct hostapd_ +@@ -5701,6 +5713,11 @@ static void handle_assoc(struct hostapd_ left = res; } #endif /* CONFIG_FILS */ @@ -147,7 +147,7 @@ /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ -@@ -5758,6 +5775,13 @@ static void handle_assoc(struct hostapd_ +@@ -5799,6 +5816,13 @@ static void handle_assoc(struct hostapd_ } #endif /* CONFIG_FILS */ @@ -161,7 +161,7 @@ fail: /* -@@ -5851,6 +5875,7 @@ static void handle_disassoc(struct hosta +@@ -5892,6 +5916,7 @@ static void handle_disassoc(struct hosta wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", MAC2STR(mgmt->sa), le_to_host16(mgmt->u.disassoc.reason_code)); @@ -169,7 +169,7 @@ sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { -@@ -5920,6 +5945,8 @@ static void handle_deauth(struct hostapd +@@ -5961,6 +5986,8 @@ static void handle_deauth(struct hostapd /* Clear the PTKSA cache entries for PASN */ ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE); @@ -180,7 +180,7 @@ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " --- a/src/ap/beacon.c +++ b/src/ap/beacon.c -@@ -852,6 +852,12 @@ void handle_probe_req(struct hostapd_dat +@@ -919,6 +919,12 @@ void handle_probe_req(struct hostapd_dat u16 csa_offs[2]; size_t csa_offs_len; struct radius_sta rad_info; @@ -193,7 +193,7 @@ if (hapd->iconf->rssi_ignore_probe_request && ssi_signal && ssi_signal < hapd->iconf->rssi_ignore_probe_request) -@@ -1038,6 +1044,12 @@ void handle_probe_req(struct hostapd_dat +@@ -1105,6 +1111,12 @@ void handle_probe_req(struct hostapd_dat } #endif /* CONFIG_P2P */ @@ -234,7 +234,7 @@ wpabuf_free(sta->p2p_ie); --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c -@@ -458,6 +458,7 @@ void ap_handle_timer(void *eloop_ctx, vo +@@ -460,6 +460,7 @@ void ap_handle_timer(void *eloop_ctx, vo hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " "local deauth request"); @@ -242,7 +242,7 @@ ap_free_sta(hapd, sta); return; } -@@ -613,6 +614,7 @@ skip_poll: +@@ -615,6 +616,7 @@ skip_poll: mlme_deauthenticate_indication( hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -260,7 +260,7 @@ hapd->msg_ctx_parent != hapd->msg_ctx) --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c -@@ -265,6 +265,7 @@ static void hostapd_wpa_auth_psk_failure +@@ -268,6 +268,7 @@ static void hostapd_wpa_auth_psk_failure struct hostapd_data *hapd = ctx; wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR, MAC2STR(addr)); @@ -270,7 +270,7 @@ --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile -@@ -176,6 +176,12 @@ ifdef CONFIG_EAPOL_TEST +@@ -183,6 +183,12 @@ ifdef CONFIG_EAPOL_TEST CFLAGS += -Werror -DEAPOL_TEST endif @@ -283,7 +283,7 @@ ifdef CONFIG_CODE_COVERAGE CFLAGS += -O0 -fprofile-arcs -ftest-coverage LIBS += -lgcov -@@ -962,6 +968,9 @@ ifdef CONFIG_CTRL_IFACE_MIB +@@ -977,6 +983,9 @@ ifdef CONFIG_CTRL_IFACE_MIB CFLAGS += -DCONFIG_CTRL_IFACE_MIB endif OBJS += ../src/ap/ctrl_iface_ap.o @@ -295,7 +295,7 @@ CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c -@@ -7241,6 +7241,8 @@ struct wpa_supplicant * wpa_supplicant_a +@@ -7285,6 +7285,8 @@ struct wpa_supplicant * wpa_supplicant_a } #endif /* CONFIG_P2P */ @@ -304,7 +304,7 @@ return wpa_s; } -@@ -7267,6 +7269,8 @@ int wpa_supplicant_remove_iface(struct w +@@ -7311,6 +7313,8 @@ int wpa_supplicant_remove_iface(struct w struct wpa_supplicant *parent = wpa_s->parent; #endif /* CONFIG_MESH */ @@ -313,7 +313,7 @@ /* Remove interface from the global list of interfaces */ prev = global->ifaces; if (prev == wpa_s) { -@@ -7570,8 +7574,12 @@ int wpa_supplicant_run(struct wpa_global +@@ -7614,8 +7618,12 @@ int wpa_supplicant_run(struct wpa_global eloop_register_signal_terminate(wpa_supplicant_terminate, global); eloop_register_signal_reconfig(wpa_supplicant_reconfig, global); @@ -328,7 +328,7 @@ --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h -@@ -19,6 +19,7 @@ +@@ -20,6 +20,7 @@ #include "wps/wps_defs.h" #include "config_ssid.h" #include "wmm_ac.h" @@ -336,7 +336,7 @@ extern const char *const wpa_supplicant_version; extern const char *const wpa_supplicant_license; -@@ -322,6 +323,8 @@ struct wpa_global { +@@ -323,6 +324,8 @@ struct wpa_global { #endif /* CONFIG_WIFI_DISPLAY */ struct psk_list_entry *add_psk; /* From group formation */ @@ -345,7 +345,7 @@ }; -@@ -708,6 +711,7 @@ struct wpa_supplicant { +@@ -707,6 +710,7 @@ struct wpa_supplicant { unsigned char own_addr[ETH_ALEN]; unsigned char perm_addr[ETH_ALEN]; char ifname[100]; @@ -363,7 +363,7 @@ #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG -@@ -393,6 +394,8 @@ static int wpa_supplicant_wps_cred(void +@@ -391,6 +392,8 @@ static int wpa_supplicant_wps_cred(void wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", cred->cred_attr, cred->cred_attr_len); @@ -374,7 +374,7 @@ --- a/hostapd/main.c +++ b/hostapd/main.c -@@ -895,6 +895,7 @@ int main(int argc, char *argv[]) +@@ -897,6 +897,7 @@ int main(int argc, char *argv[]) } hostapd_global_ctrl_iface_init(&interfaces); @@ -382,7 +382,7 @@ if (hostapd_global_run(&interfaces, daemonize, pid_file)) { wpa_printf(MSG_ERROR, "Failed to start eloop"); -@@ -904,6 +905,7 @@ int main(int argc, char *argv[]) +@@ -906,6 +907,7 @@ int main(int argc, char *argv[]) ret = 0; out: @@ -392,7 +392,7 @@ for (i = 0; i < interfaces.count; i++) { --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c -@@ -203,7 +203,7 @@ int main(int argc, char *argv[]) +@@ -204,7 +204,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, @@ -401,7 +401,7 @@ if (c < 0) break; switch (c) { -@@ -271,6 +271,9 @@ int main(int argc, char *argv[]) +@@ -272,6 +272,9 @@ int main(int argc, char *argv[]) params.conf_p2p_dev = optarg; break; #endif /* CONFIG_P2P */ @@ -473,7 +473,7 @@ --- a/src/ap/dfs.c +++ b/src/ap/dfs.c -@@ -1196,6 +1196,8 @@ int hostapd_dfs_radar_detected(struct ho +@@ -1203,6 +1203,8 @@ int hostapd_dfs_pre_cac_expired(struct h "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", freq, ht_enabled, chan_offset, chan_width, cf1, cf2); @@ -515,7 +515,7 @@ } --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h -@@ -324,6 +324,7 @@ struct sta_info { +@@ -328,6 +328,7 @@ struct sta_info { #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_AIRTIME_POLICY unsigned int airtime_weight; @@ -525,9 +525,9 @@ --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c -@@ -442,7 +442,8 @@ static void ieee802_11_rx_bss_trans_mgmt - wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", - pos, end - pos); +@@ -455,7 +455,8 @@ static void ieee802_11_rx_bss_trans_mgmt + MAC2STR(addr), reason, hex ? " neighbor=" : "", hex); + os_free(hex); - ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token); + if (!hostapd_ubus_notify_bss_transition_query(hapd, addr, dialog_token, reason, pos, end - pos)) @@ -535,7 +535,7 @@ } -@@ -464,7 +465,7 @@ static void ieee802_11_rx_bss_trans_mgmt +@@ -477,7 +478,7 @@ static void ieee802_11_rx_bss_trans_mgmt size_t len) { u8 dialog_token, status_code, bss_termination_delay; @@ -544,7 +544,7 @@ int enabled = hapd->conf->bss_transition; struct sta_info *sta; -@@ -511,6 +512,7 @@ static void ieee802_11_rx_bss_trans_mgmt +@@ -524,6 +525,7 @@ static void ieee802_11_rx_bss_trans_mgmt wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); return; } @@ -552,7 +552,7 @@ sta->agreed_to_steer = 1; eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer, -@@ -530,6 +532,10 @@ static void ieee802_11_rx_bss_trans_mgmt +@@ -543,6 +545,10 @@ static void ieee802_11_rx_bss_trans_mgmt MAC2STR(addr), status_code, bss_termination_delay); } diff --git a/package/network/services/hostapd/patches/700-wifi-reload.patch b/package/network/services/hostapd/patches/700-wifi-reload.patch index e6d7c2f673..174127df6e 100644 --- a/package/network/services/hostapd/patches/700-wifi-reload.patch +++ b/package/network/services/hostapd/patches/700-wifi-reload.patch @@ -1,6 +1,6 @@ --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -2458,6 +2458,8 @@ static int hostapd_config_fill(struct ho +@@ -2416,6 +2416,8 @@ static int hostapd_config_fill(struct ho bss->isolate = atoi(pos); } else if (os_strcmp(buf, "ap_max_inactivity") == 0) { bss->ap_max_inactivity = atoi(pos); @@ -9,7 +9,7 @@ } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) { bss->skip_inactivity_poll = atoi(pos); } else if (os_strcmp(buf, "country_code") == 0) { -@@ -3158,6 +3160,8 @@ static int hostapd_config_fill(struct ho +@@ -3121,6 +3123,8 @@ static int hostapd_config_fill(struct ho } } else if (os_strcmp(buf, "acs_exclude_dfs") == 0) { conf->acs_exclude_dfs = atoi(pos); @@ -20,7 +20,7 @@ } else if (os_strcmp(buf, "channel") == 0) { --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c -@@ -792,6 +792,7 @@ void hostapd_config_free_bss(struct host +@@ -796,6 +796,7 @@ void hostapd_config_free_bss(struct host os_free(conf->radius_req_attr_sqlite); os_free(conf->rsn_preauth_interfaces); os_free(conf->ctrl_interface); @@ -28,7 +28,7 @@ os_free(conf->ca_cert); os_free(conf->server_cert); os_free(conf->server_cert2); -@@ -988,6 +989,7 @@ void hostapd_config_free(struct hostapd_ +@@ -995,6 +996,7 @@ void hostapd_config_free(struct hostapd_ for (i = 0; i < conf->num_bss; i++) hostapd_config_free_bss(conf->bss[i]); @@ -38,7 +38,7 @@ os_free(conf->basic_rates); --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h -@@ -279,6 +279,8 @@ struct hostapd_bss_config { +@@ -285,6 +285,8 @@ struct hostapd_bss_config { char vlan_bridge[IFNAMSIZ + 1]; char wds_bridge[IFNAMSIZ + 1]; @@ -47,7 +47,7 @@ enum hostapd_logger_level logger_syslog_level, logger_stdout_level; unsigned int logger_syslog; /* module bitfield */ -@@ -942,6 +944,7 @@ struct spatial_reuse { +@@ -969,6 +971,7 @@ struct eht_phy_capabilities_info { struct hostapd_config { struct hostapd_bss_config **bss, *last_bss; size_t num_bss; @@ -57,7 +57,7 @@ int rts_threshold; --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c -@@ -219,6 +219,10 @@ static int hostapd_iface_conf_changed(st +@@ -224,6 +224,10 @@ static int hostapd_iface_conf_changed(st { size_t i; @@ -68,7 +68,7 @@ if (newconf->num_bss != oldconf->num_bss) return 1; -@@ -232,7 +236,7 @@ static int hostapd_iface_conf_changed(st +@@ -237,7 +241,7 @@ static int hostapd_iface_conf_changed(st } @@ -77,7 +77,7 @@ { struct hapd_interfaces *interfaces = iface->interfaces; struct hostapd_data *hapd = iface->bss[0]; -@@ -255,13 +259,16 @@ int hostapd_reload_config(struct hostapd +@@ -260,13 +264,16 @@ int hostapd_reload_config(struct hostapd if (newconf == NULL) return -1; @@ -96,7 +96,7 @@ wpa_printf(MSG_DEBUG, "Configuration changes include interface/BSS modification - force full disable+enable sequence"); fname = os_strdup(iface->config_fname); -@@ -286,6 +293,24 @@ int hostapd_reload_config(struct hostapd +@@ -291,6 +298,24 @@ int hostapd_reload_config(struct hostapd wpa_printf(MSG_ERROR, "Failed to enable interface on config reload"); return res; @@ -121,7 +121,7 @@ } iface->conf = newconf; -@@ -302,6 +327,12 @@ int hostapd_reload_config(struct hostapd +@@ -307,6 +332,12 @@ int hostapd_reload_config(struct hostapd for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; @@ -134,7 +134,7 @@ hapd->iconf = newconf; hapd->conf = newconf->bss[j]; hostapd_reload_bss(hapd); -@@ -2397,6 +2428,10 @@ hostapd_alloc_bss_data(struct hostapd_if +@@ -2420,6 +2451,10 @@ hostapd_alloc_bss_data(struct hostapd_if hapd->iconf = conf; hapd->conf = bss; hapd->iface = hapd_iface; @@ -147,7 +147,7 @@ hapd->ctrl_sock = -1; --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h -@@ -46,7 +46,7 @@ struct mesh_conf; +@@ -47,7 +47,7 @@ struct mesh_conf; struct hostapd_iface; struct hapd_interfaces { @@ -156,7 +156,7 @@ struct hostapd_config * (*config_read_cb)(const char *config_fname); int (*ctrl_iface_init)(struct hostapd_data *hapd); void (*ctrl_iface_deinit)(struct hostapd_data *hapd); -@@ -173,6 +173,7 @@ struct hostapd_data { +@@ -185,6 +185,7 @@ struct hostapd_data { struct hostapd_config *iconf; struct hostapd_bss_config *conf; struct hostapd_ubus_bss ubus; @@ -164,7 +164,7 @@ int interface_added; /* virtual interface added for this BSS */ unsigned int started:1; unsigned int disabled:1; -@@ -624,7 +625,7 @@ struct hostapd_iface { +@@ -667,7 +668,7 @@ struct hostapd_iface { int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); @@ -175,7 +175,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c -@@ -4833,6 +4833,9 @@ static int wpa_driver_nl80211_set_ap(voi +@@ -4852,6 +4852,9 @@ static int wpa_driver_nl80211_set_ap(voi if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", ret, strerror(-ret)); diff --git a/package/network/services/hostapd/patches/710-vlan_no_bridge.patch b/package/network/services/hostapd/patches/710-vlan_no_bridge.patch index 856dc8ba88..b06ef8f68b 100644 --- a/package/network/services/hostapd/patches/710-vlan_no_bridge.patch +++ b/package/network/services/hostapd/patches/710-vlan_no_bridge.patch @@ -1,6 +1,6 @@ --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h -@@ -115,6 +115,7 @@ struct hostapd_ssid { +@@ -121,6 +121,7 @@ struct hostapd_ssid { #define DYNAMIC_VLAN_OPTIONAL 1 #define DYNAMIC_VLAN_REQUIRED 2 int dynamic_vlan; @@ -30,7 +30,7 @@ --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -3381,6 +3381,8 @@ static int hostapd_config_fill(struct ho +@@ -3346,6 +3346,8 @@ static int hostapd_config_fill(struct ho #ifndef CONFIG_NO_VLAN } else if (os_strcmp(buf, "dynamic_vlan") == 0) { bss->ssid.dynamic_vlan = atoi(pos); diff --git a/package/network/services/hostapd/patches/711-wds_bridge_force.patch b/package/network/services/hostapd/patches/711-wds_bridge_force.patch index a22580c357..169807c61e 100644 --- a/package/network/services/hostapd/patches/711-wds_bridge_force.patch +++ b/package/network/services/hostapd/patches/711-wds_bridge_force.patch @@ -1,6 +1,6 @@ --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -2358,6 +2358,8 @@ static int hostapd_config_fill(struct ho +@@ -2316,6 +2316,8 @@ static int hostapd_config_fill(struct ho sizeof(conf->bss[0]->iface)); } else if (os_strcmp(buf, "bridge") == 0) { os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); diff --git a/package/network/services/hostapd/patches/720-iface_max_num_sta.patch b/package/network/services/hostapd/patches/720-iface_max_num_sta.patch index 106f9d7407..ed76d22dd0 100644 --- a/package/network/services/hostapd/patches/720-iface_max_num_sta.patch +++ b/package/network/services/hostapd/patches/720-iface_max_num_sta.patch @@ -1,6 +1,6 @@ --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -2880,6 +2880,14 @@ static int hostapd_config_fill(struct ho +@@ -2841,6 +2841,14 @@ static int hostapd_config_fill(struct ho line, bss->max_num_sta, MAX_STA_COUNT); return 1; } @@ -17,17 +17,17 @@ } else if (os_strcmp(buf, "extended_key_id") == 0) { --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h -@@ -668,6 +668,7 @@ void hostapd_cleanup_cs_params(struct ho +@@ -711,6 +711,7 @@ void hostapd_cleanup_cs_params(struct ho void hostapd_periodic_iface(struct hostapd_iface *iface); int hostapd_owe_trans_get_info(struct hostapd_data *hapd); void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx); +int hostapd_check_max_sta(struct hostapd_data *hapd); - /* utils.c */ - int hostapd_register_probereq_cb(struct hostapd_data *hapd, + void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap); + void hostapd_cleanup_cca_params(struct hostapd_data *hapd); --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c -@@ -236,6 +236,30 @@ static int hostapd_iface_conf_changed(st +@@ -241,6 +241,30 @@ static int hostapd_iface_conf_changed(st } @@ -60,7 +60,7 @@ struct hapd_interfaces *interfaces = iface->interfaces; --- a/src/ap/beacon.c +++ b/src/ap/beacon.c -@@ -1068,7 +1068,7 @@ void handle_probe_req(struct hostapd_dat +@@ -1135,7 +1135,7 @@ void handle_probe_req(struct hostapd_dat if (hapd->conf->no_probe_resp_if_max_sta && is_multicast_ether_addr(mgmt->da) && is_multicast_ether_addr(mgmt->bssid) && @@ -71,7 +71,7 @@ " since no room for additional STA", --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h -@@ -981,6 +981,8 @@ struct hostapd_config { +@@ -1010,6 +1010,8 @@ struct hostapd_config { unsigned int track_sta_max_num; unsigned int track_sta_max_age; diff --git a/package/network/services/hostapd/patches/730-ft_iface.patch b/package/network/services/hostapd/patches/730-ft_iface.patch index b5809222d7..d9a4f15f03 100644 --- a/package/network/services/hostapd/patches/730-ft_iface.patch +++ b/package/network/services/hostapd/patches/730-ft_iface.patch @@ -1,6 +1,6 @@ --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -3038,6 +3038,8 @@ static int hostapd_config_fill(struct ho +@@ -3000,6 +3000,8 @@ static int hostapd_config_fill(struct ho wpa_printf(MSG_INFO, "Line %d: Obsolete peerkey parameter ignored", line); #ifdef CONFIG_IEEE80211R_AP @@ -11,7 +11,7 @@ hexstr2bin(pos, bss->mobility_domain, --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h -@@ -277,6 +277,7 @@ struct airtime_sta_weight { +@@ -283,6 +283,7 @@ struct airtime_sta_weight { struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; @@ -21,7 +21,7 @@ --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c -@@ -1566,8 +1566,12 @@ int hostapd_setup_wpa(struct hostapd_dat +@@ -1595,8 +1595,12 @@ int hostapd_setup_wpa(struct hostapd_dat wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { const char *ft_iface; diff --git a/package/network/services/hostapd/patches/740-snoop_iface.patch b/package/network/services/hostapd/patches/740-snoop_iface.patch index 2ed73750ca..608f15a256 100644 --- a/package/network/services/hostapd/patches/740-snoop_iface.patch +++ b/package/network/services/hostapd/patches/740-snoop_iface.patch @@ -1,6 +1,6 @@ --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h -@@ -278,6 +278,7 @@ struct hostapd_bss_config { +@@ -284,6 +284,7 @@ struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; char ft_iface[IFNAMSIZ + 1]; @@ -10,9 +10,9 @@ --- a/src/ap/x_snoop.c +++ b/src/ap/x_snoop.c -@@ -31,14 +31,16 @@ int x_snoop_init(struct hostapd_data *ha - return -1; - } +@@ -33,14 +33,16 @@ int x_snoop_init(struct hostapd_data *ha + + hapd->x_snoop_initialized = true; - if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, + if (!conf->snoop_iface[0] && @@ -29,7 +29,7 @@ wpa_printf(MSG_DEBUG, "x_snoop: Failed to enable proxyarp on the bridge port"); return -1; -@@ -52,7 +54,8 @@ int x_snoop_init(struct hostapd_data *ha +@@ -54,7 +56,8 @@ int x_snoop_init(struct hostapd_data *ha } #ifdef CONFIG_IPV6 @@ -39,7 +39,7 @@ wpa_printf(MSG_DEBUG, "x_snoop: Failed to enable multicast snooping on the bridge"); return -1; -@@ -71,8 +74,12 @@ x_snoop_get_l2_packet(struct hostapd_dat +@@ -73,8 +76,12 @@ x_snoop_get_l2_packet(struct hostapd_dat { struct hostapd_bss_config *conf = hapd->conf; struct l2_packet_data *l2; @@ -55,7 +55,7 @@ "x_snoop: Failed to initialize L2 packet processing %s", --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -2360,6 +2360,8 @@ static int hostapd_config_fill(struct ho +@@ -2318,6 +2318,8 @@ static int hostapd_config_fill(struct ho os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); if (!bss->wds_bridge[0]) os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); diff --git a/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch b/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch index ff1d076b37..479d561555 100644 --- a/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch +++ b/package/network/services/hostapd/patches/750-qos_map_set_without_interworking.patch @@ -1,6 +1,6 @@ --- a/hostapd/config_file.c +++ b/hostapd/config_file.c -@@ -1598,6 +1598,8 @@ static int parse_anqp_elem(struct hostap +@@ -1602,6 +1602,8 @@ static int parse_anqp_elem(struct hostap return 0; } @@ -9,7 +9,7 @@ static int parse_qos_map_set(struct hostapd_bss_config *bss, char *buf, int line) -@@ -1639,8 +1641,6 @@ static int parse_qos_map_set(struct host +@@ -1643,8 +1645,6 @@ static int parse_qos_map_set(struct host return 0; } @@ -18,7 +18,7 @@ #ifdef CONFIG_HS20 static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf, -@@ -4042,10 +4042,10 @@ static int hostapd_config_fill(struct ho +@@ -4046,10 +4046,10 @@ static int hostapd_config_fill(struct ho bss->gas_frag_limit = val; } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { bss->gas_comeback_delay = atoi(pos); @@ -32,7 +32,7 @@ os_free(bss->dump_msk_file); --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c -@@ -1423,6 +1423,7 @@ static int hostapd_setup_bss(struct host +@@ -1424,6 +1424,7 @@ static int hostapd_setup_bss(struct host wpa_printf(MSG_ERROR, "GAS server initialization failed"); return -1; } @@ -40,7 +40,7 @@ if (conf->qos_map_set_len && hostapd_drv_set_qos_map(hapd, conf->qos_map_set, -@@ -1430,7 +1431,6 @@ static int hostapd_setup_bss(struct host +@@ -1431,7 +1432,6 @@ static int hostapd_setup_bss(struct host wpa_printf(MSG_ERROR, "Failed to initialize QoS Map"); return -1; } @@ -81,7 +81,7 @@ data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP)) --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c -@@ -1098,13 +1098,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_da +@@ -1100,13 +1100,11 @@ u8 * hostapd_eid_rsnxe(struct hostapd_da u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ext_capab_ie, size_t ext_capab_ie_len) { diff --git a/package/network/services/hostapd/patches/751-qos_map_ignore_when_unsupported.patch b/package/network/services/hostapd/patches/751-qos_map_ignore_when_unsupported.patch index 8af5a0a046..d90a275233 100644 --- a/package/network/services/hostapd/patches/751-qos_map_ignore_when_unsupported.patch +++ b/package/network/services/hostapd/patches/751-qos_map_ignore_when_unsupported.patch @@ -1,6 +1,6 @@ --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c -@@ -850,7 +850,8 @@ int hostapd_start_dfs_cac(struct hostapd +@@ -864,7 +864,8 @@ int hostapd_start_dfs_cac(struct hostapd int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, u8 qos_map_set_len) { diff --git a/package/network/services/hostapd/patches/800-acs-don-t-select-indoor-channel-on-outdoor-operation.patch b/package/network/services/hostapd/patches/800-acs-don-t-select-indoor-channel-on-outdoor-operation.patch new file mode 100644 index 0000000000..1d9e9564ec --- /dev/null +++ b/package/network/services/hostapd/patches/800-acs-don-t-select-indoor-channel-on-outdoor-operation.patch @@ -0,0 +1,58 @@ +From 37528a5205cb0b9e2238b7d97fb2ff5457448f1c Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Thu, 8 Sep 2022 01:45:41 +0200 +Subject: [PATCH] acs: don't select indoor channel on outdoor operation + +Don't select channels designated for exclusive-indoor use when the +country3 element is set on outdoor operation. + +Signed-off-by: David Bauer +--- + src/ap/acs.c | 9 +++++++++ + src/ap/dfs.c | 3 +++ + 2 files changed, 12 insertions(+) + +--- a/src/ap/acs.c ++++ b/src/ap/acs.c +@@ -552,6 +552,9 @@ static void acs_survey_mode_interference + if (chan->max_tx_power < iface->conf->min_tx_power) + continue; + ++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) ++ continue; ++ + wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", + chan->chan, chan->freq); + +@@ -686,6 +689,9 @@ acs_find_ideal_chan_mode(struct hostapd_ + if (chan->max_tx_power < iface->conf->min_tx_power) + continue; + ++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) ++ continue; ++ + if (!chan_bw_allowed(chan, bw, 1, 1)) { + wpa_printf(MSG_DEBUG, + "ACS: Channel %d: BW %u is not supported", +@@ -1067,6 +1073,9 @@ static int * acs_request_scan_add_freqs( + if (chan->max_tx_power < iface->conf->min_tx_power) + continue; + ++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) ++ continue; ++ + *freq++ = chan->freq; + } + +--- a/src/ap/dfs.c ++++ b/src/ap/dfs.c +@@ -282,6 +282,9 @@ static int dfs_find_channel(struct hosta + if (chan->max_tx_power < iface->conf->min_tx_power) + continue; + ++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) ++ continue; ++ + if (ret_chan && idx == channel_idx) { + wpa_printf(MSG_DEBUG, "Selected channel %d (%d)", + chan->freq, chan->chan); diff --git a/package/network/services/hostapd/patches/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch b/package/network/services/hostapd/patches/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch index 3665c6c33f..e78a4ef5cf 100644 --- a/package/network/services/hostapd/patches/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch +++ b/package/network/services/hostapd/patches/990-ctrl-make-WNM_AP-functions-dependant-on-CONFIG_AP.patch @@ -11,11 +11,9 @@ Signed-off-by: David Bauer wpa_supplicant/ctrl_iface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) -diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c -index ac337e0f5..6e23114e6 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c -@@ -12185,7 +12185,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, +@@ -12241,7 +12241,7 @@ char * wpa_supplicant_ctrl_iface_process if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18)) reply_len = -1; #endif /* CONFIG_WNM */ @@ -24,7 +22,7 @@ index ac337e0f5..6e23114e6 100644 } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { if (ap_ctrl_iface_disassoc_imminent(wpa_s, buf + 18)) reply_len = -1; -@@ -12195,7 +12195,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, +@@ -12251,7 +12251,7 @@ char * wpa_supplicant_ctrl_iface_process } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) { if (ap_ctrl_iface_bss_tm_req(wpa_s, buf + 11)) reply_len = -1; @@ -33,6 +31,3 @@ index ac337e0f5..6e23114e6 100644 } else if (os_strcmp(buf, "FLUSH") == 0) { wpa_supplicant_ctrl_iface_flush(wpa_s); } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) { --- -2.35.1 - diff --git a/package/network/services/hostapd/patches/991-fix-compile.patch b/package/network/services/hostapd/patches/991-fix-compile.patch deleted file mode 100644 index d08efa08bd..0000000000 --- a/package/network/services/hostapd/patches/991-fix-compile.patch +++ /dev/null @@ -1,19 +0,0 @@ ---- a/wpa_supplicant/wpa_supplicant.c -+++ b/wpa_supplicant/wpa_supplicant.c -@@ -8104,6 +8104,7 @@ int wpas_network_disabled(struct wpa_sup - !ssid->mem_only_psk) - return 1; - -+#ifdef CONFIG_IEEE8021X_EAPOL - #ifdef CRYPTO_RSA_OAEP_SHA256 - if (ssid->eap.imsi_privacy_cert) { - struct crypto_rsa_key *key; -@@ -8121,7 +8122,7 @@ int wpas_network_disabled(struct wpa_sup - } - } - #endif /* CRYPTO_RSA_OAEP_SHA256 */ -- -+#endif /* CONFIG_IEEE8021X_EAPOL */ - return 0; - } - diff --git a/package/network/services/unetd/Makefile b/package/network/services/unetd/Makefile index c645ee57d8..d2cd4a6183 100644 --- a/package/network/services/unetd/Makefile +++ b/package/network/services/unetd/Makefile @@ -10,9 +10,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=unetd PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/unetd.git -PKG_SOURCE_DATE:=2022-09-01 -PKG_SOURCE_VERSION:=d7fb9e5b065bf9eecb5bcbcf741b5f89695c5dcc -PKG_MIRROR_HASH:=a693c2b4b4bda5e1e44b493019e8e6e6d39c4048f417b581c801a9931e6b9b39 +PKG_SOURCE_DATE:=2022-09-16.1 +PKG_SOURCE_VERSION:=6c888f897862b152e2cfae735faafc9cdcb07336 +PKG_MIRROR_HASH:=35df8c822c671495146c3d5d1c193c1d1a44721c274aee2035943eb5e8c04768 PKG_LICENSE:=GPL-2.0 PKG_MAINTAINER:=Felix Fietkau @@ -44,10 +44,17 @@ define Package/unetd/config endef +define Package/unet-dht + SECTION:=net + CATEGORY:=Network + DEPENDS:=unetd + TITLE:=unetd DHT discovery support +endef + define Package/unet-cli SECTION:=net CATEGORY:=Network - DEPENDS:=unetd +ucode +ucode-mod-fs + DEPENDS:=+unetd +ucode +ucode-mod-fs TITLE:=unetd administration command line utility endef @@ -88,10 +95,19 @@ define Package/unetd/install $(INSTALL_BIN) ./files/unetd.sh $(1)/lib/netifd/proto endef +define Package/unet-dht/install + $(INSTALL_DIR) \ + $(1)/etc/init.d \ + $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/unet-dht $(1)/usr/sbin + $(INSTALL_BIN) ./files/unet-dht.init $(1)/etc/init.d/unet-dht +endef + define Package/unet-cli/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) $(PKG_BUILD_DIR)/scripts/unet-cli $(1)/usr/sbin endef $(eval $(call BuildPackage,unetd)) +$(eval $(call BuildPackage,unet-dht)) $(eval $(call BuildPackage,unet-cli)) diff --git a/package/network/services/unetd/files/unet-dht.init b/package/network/services/unetd/files/unet-dht.init new file mode 100644 index 0000000000..272626a933 --- /dev/null +++ b/package/network/services/unetd/files/unet-dht.init @@ -0,0 +1,24 @@ +#!/bin/sh /etc/rc.common +# Copyright (c) 2022 OpenWrt.org + +START=19 + +USE_PROCD=1 +PROG=/usr/sbin/unet-dht + +unet_dht_id() { + cat \ + /sys/class/net/eth?/address \ + /sys/class/ieee80211/phy*/macaddress \ + /etc/board.json | md5sum | awk '{ print $1 }' +} + +start_service() { + mkdir -p /var/run/unetd /etc/unetd + + procd_open_instance + procd_set_param command "$PROG" -u /var/run/unetd/socket -n /var/run/unetd/nodes.dat $(unet_dht_id) + procd_set_param respawn + procd_set_param limits core="unlimited" + procd_close_instance +} diff --git a/package/network/services/unetd/files/unetd.init b/package/network/services/unetd/files/unetd.init index 0ec48b2b92..c1124821ee 100644 --- a/package/network/services/unetd/files/unetd.init +++ b/package/network/services/unetd/files/unetd.init @@ -10,7 +10,7 @@ start_service() { mkdir -p /var/run/unetd /etc/unetd procd_open_instance - procd_set_param command "$PROG" -h /var/run/unetd/hosts + procd_set_param command "$PROG" -h /var/run/unetd/hosts -u /var/run/unetd/socket procd_set_param respawn procd_set_param limits core="unlimited" procd_close_instance diff --git a/package/network/services/unetd/files/unetd.sh b/package/network/services/unetd/files/unetd.sh index 70a28f6482..2f0f0c478c 100644 --- a/package/network/services/unetd/files/unetd.sh +++ b/package/network/services/unetd/files/unetd.sh @@ -16,6 +16,7 @@ proto_unet_init_config() { proto_config_add_string file proto_config_add_int keepalive proto_config_add_string domain + proto_config_add_boolean dht proto_config_add_array "tunnels:list(string)" proto_config_add_array "connect:list(string)" proto_config_add_array "peer_data:list(string)" @@ -28,7 +29,7 @@ proto_unet_setup() { local config="$1" local device type key file keepalive domain tunnels - json_get_vars device type auth_key key file keepalive domain + json_get_vars device type auth_key key file keepalive domain dht json_get_values tunnels tunnels json_get_values connect connect json_get_values peer_data peer_data @@ -45,6 +46,7 @@ proto_unet_setup() { json_add_string key "$key" json_add_string file "$file" [ -n "$keepalive" ] && json_add_int keepalive "$keepalive" + [ -n "$dht" ] && json_add_boolean dht "$dht" json_add_string domain "$domain" json_add_object tunnels diff --git a/package/network/utils/xdp-tools/Makefile b/package/network/utils/xdp-tools/Makefile index 42a8a0695b..20a9139946 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.6 -PKG_HASH:=e1bead15014adf404c1ae93b5bb24e6625840b4aadef6c1acfb47e0b99039f52 +PKG_VERSION:=1.2.8 +PKG_HASH:=2c575e5242e60055b0e7fc720f5b6ea87d74911f967dde3d50046d751f35bff0 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/system/rpcd/Makefile b/package/system/rpcd/Makefile index 6571ca9c55..7719da9101 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:=52ca9beab4b18afec9717a5f05c5232ef6160c3ece82a020851ac3aeaa52a29a -PKG_SOURCE_DATE:=2022-08-24 -PKG_SOURCE_VERSION:=82904bd4f92e5928d047db6396cc14ca2b07d89f +PKG_MIRROR_HASH:=90ce347d391694e6207ae3c57aaf8b80b7cfe043823f3610244c8f47e9017935 +PKG_SOURCE_DATE:=2022-09-12 +PKG_SOURCE_VERSION:=e80d0b2d77f8ed163872971200d24c423db60bb8 PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=ISC diff --git a/package/utils/ucode/Makefile b/package/utils/ucode/Makefile index 025f997387..185133c554 100644 --- a/package/utils/ucode/Makefile +++ b/package/utils/ucode/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=https://github.com/jow-/ucode.git -PKG_SOURCE_DATE:=2022-08-29 -PKG_SOURCE_VERSION:=344fa9e69da43ecdc4d8f7768d85d42639352405 -PKG_MIRROR_HASH:=94e1ae6779536b2bd8957db8c2922ec32dcef75258401707505b1d34b6f16d0b +PKG_SOURCE_DATE:=2022-09-09 +PKG_SOURCE_VERSION:=7fa59ce44b9347528b0e4e44ebcfb04a08479f3f +PKG_MIRROR_HASH:=abef8939104db9d08d18fc65941d9357b8fc1b0f3b511e361388789972d0a44b PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=ISC diff --git a/target/linux/airoha/patches-5.15/0001-add-airoha-platform.patch b/target/linux/airoha/patches-5.15/0001-add-airoha-platform.patch index b1f88a6ac7..bb1104206e 100644 --- a/target/linux/airoha/patches-5.15/0001-add-airoha-platform.patch +++ b/target/linux/airoha/patches-5.15/0001-add-airoha-platform.patch @@ -1,11 +1,9 @@ -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 66f5d6c3..05cd3385 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -571,6 +571,18 @@ config ARCH_VIRT - select HAVE_ARM_ARCH_TIMER - select ARCH_SUPPORTS_BIG_ENDIAN - + select HAVE_ARM_ARCH_TIMER + select ARCH_SUPPORTS_BIG_ENDIAN + +config ARCH_AIROHA + bool "Airoha SoC Support" + depends on ARCH_MULTI_V7 @@ -21,11 +19,9 @@ index 66f5d6c3..05cd3385 100644 # # This is sorted alphabetically by mach-* pathname. However, plat-* # Kconfigs may be included either alphabetically (according to the -diff --git a/arch/arm/Makefile b/arch/arm/Makefile -index fa45837b..c34f7463 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile -@@ -156,6 +156,7 @@ textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000 +@@ -156,6 +156,7 @@ textofs-$(CONFIG_ARCH_AXXIA) := 0x003080 # Machine directory name. This list is sorted alphanumerically # by CONFIG_* macro name. machine-$(CONFIG_ARCH_ACTIONS) += actions diff --git a/target/linux/airoha/patches-5.15/0002-add-airoha-en7523-clk-driver.patch b/target/linux/airoha/patches-5.15/0002-add-airoha-en7523-clk-driver.patch index 676e0f40bb..bae5cacc5e 100644 --- a/target/linux/airoha/patches-5.15/0002-add-airoha-en7523-clk-driver.patch +++ b/target/linux/airoha/patches-5.15/0002-add-airoha-en7523-clk-driver.patch @@ -1,11 +1,9 @@ -diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig -index c5b3dc97..c973ac1a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -192,6 +192,15 @@ config COMMON_CLK_CS2000_CP - help - If you say yes here you get support for the CS2000 clock multiplier. - + help + If you say yes here you get support for the CS2000 clock multiplier. + +config COMMON_CLK_EN7523 + bool "Clock driver for Airoha EN7523 SoC system clocks" + depends on OF @@ -16,13 +14,11 @@ index c5b3dc97..c973ac1a 100644 + ARM silicon. + config COMMON_CLK_FSL_FLEXSPI - tristate "Clock driver for FlexSPI on Layerscape SoCs" - depends on ARCH_LAYERSCAPE || COMPILE_TEST -diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile -index e4231212..be11d88c 100644 + tristate "Clock driver for FlexSPI on Layerscape SoCs" + depends on ARCH_LAYERSCAPE || COMPILE_TEST --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile -@@ -27,6 +27,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o +@@ -27,6 +27,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk- obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o diff --git a/target/linux/airoha/patches-5.15/0003-add-airoha-en7523-gpio-driver.patch b/target/linux/airoha/patches-5.15/0003-add-airoha-en7523-gpio-driver.patch index 1d95e6b2c8..319336257b 100644 --- a/target/linux/airoha/patches-5.15/0003-add-airoha-en7523-gpio-driver.patch +++ b/target/linux/airoha/patches-5.15/0003-add-airoha-en7523-gpio-driver.patch @@ -1,11 +1,9 @@ -diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig -index cbfb6f13..b3106df6 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -247,6 +247,16 @@ config GPIO_EM - help - Say yes here to support GPIO on Renesas Emma Mobile SoCs. - + help + Say yes here to support GPIO on Renesas Emma Mobile SoCs. + +config GPIO_EN7523 + tristate "Airoha GPIO support" + depends on ARCH_AIROHA @@ -17,10 +15,8 @@ index cbfb6f13..b3106df6 100644 + Airoha EN7523 SoC. It supports two banks of 32 GPIOs. + config GPIO_EP93XX - def_bool y - depends on ARCH_EP93XX -diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile -index 61202717..4c73ce82 100644 + def_bool y + depends on ARCH_EP93XX --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o diff --git a/target/linux/airoha/patches-5.15/0004-ARM-9124-1-uncompress-Parse-linux-usable-memory-rang.patch b/target/linux/airoha/patches-5.15/0004-ARM-9124-1-uncompress-Parse-linux-usable-memory-rang.patch index f13ab2b0ed..0f0cbaa10f 100644 --- a/target/linux/airoha/patches-5.15/0004-ARM-9124-1-uncompress-Parse-linux-usable-memory-rang.patch +++ b/target/linux/airoha/patches-5.15/0004-ARM-9124-1-uncompress-Parse-linux-usable-memory-rang.patch @@ -21,11 +21,9 @@ Signed-off-by: Daniel Danzberger .../arm/boot/compressed/fdt_check_mem_start.c | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) -diff --git a/arch/arm/boot/compressed/fdt_check_mem_start.c b/arch/arm/boot/compressed/fdt_check_mem_start.c -index 62450d824c3c..9291a2661bdf 100644 --- a/arch/arm/boot/compressed/fdt_check_mem_start.c +++ b/arch/arm/boot/compressed/fdt_check_mem_start.c -@@ -55,16 +55,17 @@ static uint64_t get_val(const fdt32_t *cells, uint32_t ncells) +@@ -55,16 +55,17 @@ static uint64_t get_val(const fdt32_t *c * DTB, and, if out-of-range, replace it by the real start address. * To preserve backwards compatibility (systems reserving a block of memory * at the start of physical memory, kdump, ...), the traditional method is @@ -39,18 +37,18 @@ index 62450d824c3c..9291a2661bdf 100644 { - uint32_t addr_cells, size_cells, base; + uint32_t addr_cells, size_cells, usable_base, base; - uint32_t fdt_mem_start = 0xffffffff; + uint32_t fdt_mem_start = 0xffffffff; - const fdt32_t *reg, *endp; - uint64_t size, end; + const fdt32_t *usable, *reg, *endp; + uint64_t size, usable_end, end; - const char *type; - int offset, len; - -@@ -80,6 +81,27 @@ uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt) - if (addr_cells > 2 || size_cells > 2) - return mem_start; - + const char *type; + int offset, len; + +@@ -80,6 +81,27 @@ uint32_t fdt_check_mem_start(uint32_t me + if (addr_cells > 2 || size_cells > 2) + return mem_start; + + /* + * Usable memory in case of a crash dump kernel + * This property describes a limitation: memory within this range is @@ -72,13 +70,13 @@ index 62450d824c3c..9291a2661bdf 100644 + usable_end = usable_base + size; + } + - /* Walk all memory nodes and regions */ - for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0; - offset = fdt_next_node(fdt, offset, NULL)) { -@@ -107,7 +129,20 @@ uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt) - - base = fdt32_ld(reg + addr_cells - 1); - end = base + size; + /* Walk all memory nodes and regions */ + for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { +@@ -107,7 +129,20 @@ uint32_t fdt_check_mem_start(uint32_t me + + base = fdt32_ld(reg + addr_cells - 1); + end = base + size; - if (mem_start >= base && mem_start < end) { + if (usable) { + /* @@ -94,18 +92,16 @@ index 62450d824c3c..9291a2661bdf 100644 + if (end <= base) + continue; + } else if (mem_start >= base && mem_start < end) { - /* Calculated address is valid, use it */ - return mem_start; - } -@@ -123,7 +158,8 @@ uint32_t fdt_check_mem_start(uint32_t mem_start, const void *fdt) - } - - /* + /* Calculated address is valid, use it */ + return mem_start; + } +@@ -123,7 +158,8 @@ uint32_t fdt_check_mem_start(uint32_t me + } + + /* - * The calculated address is not usable. + * The calculated address is not usable, or was overridden by the + * "linux,usable-memory-range" property. - * Use the lowest usable physical memory address from the DTB instead, - * and make sure this is a multiple of 2 MiB for phys/virt patching. - */ --- -2.35.1 + * Use the lowest usable physical memory address from the DTB instead, + * and make sure this is a multiple of 2 MiB for phys/virt patching. + */ diff --git a/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch b/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch index e368acc0cf..2c590f7fab 100644 --- a/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch +++ b/target/linux/airoha/patches-5.15/0005-spi-Add-support-for-the-Airoha-EN7523-SoC-SPI-contro.patch @@ -1,11 +1,9 @@ -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index 83e352b0..5f7defe4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -308,6 +308,12 @@ config SPI_DLN2 - This driver can also be built as a module. If so, the module - will be called spi-dln2. - + This driver can also be built as a module. If so, the module + will be called spi-dln2. + +config SPI_AIROHA_EN7523 + bool "Airoha EN7523 SPI controller support" + depends on ARCH_AIROHA @@ -13,13 +11,11 @@ index 83e352b0..5f7defe4 100644 + This enables SPI controller support for the Airoha EN7523 SoC. + config SPI_EP93XX - tristate "Cirrus Logic EP93xx SPI controller" - depends on ARCH_EP93XX || COMPILE_TEST -diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile -index 699db95c..6c9460f7 100644 + tristate "Cirrus Logic EP93xx SPI controller" + depends on ARCH_EP93XX || COMPILE_TEST --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile -@@ -45,6 +45,7 @@ obj-$(CONFIG_SPI_DW_BT1) += spi-dw-bt1.o +@@ -45,6 +45,7 @@ obj-$(CONFIG_SPI_DW_BT1) += spi-dw-bt1. obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o @@ -27,9 +23,6 @@ index 699db95c..6c9460f7 100644 obj-$(CONFIG_SPI_FALCON) += spi-falcon.o obj-$(CONFIG_SPI_FSI) += spi-fsi.o obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o -diff --git a/drivers/spi/spi-en7523.c b/drivers/spi/spi-en7523.c -new file mode 100644 -index 00000000..322bf2eb --- /dev/null +++ b/drivers/spi/spi-en7523.c @@ -0,0 +1,311 @@ diff --git a/target/linux/airoha/patches-5.15/0006-PCI-mediatek-Allow-building-for-ARCH_AIROHA.patch b/target/linux/airoha/patches-5.15/0006-PCI-mediatek-Allow-building-for-ARCH_AIROHA.patch index 9f51f281bc..4659809074 100644 --- a/target/linux/airoha/patches-5.15/0006-PCI-mediatek-Allow-building-for-ARCH_AIROHA.patch +++ b/target/linux/airoha/patches-5.15/0006-PCI-mediatek-Allow-building-for-ARCH_AIROHA.patch @@ -18,18 +18,14 @@ Signed-off-by: Daniel Danzberger drivers/pci/controller/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig -index b8d96d38064d..2f6806dc2a20 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig -@@ -237,7 +237,7 @@ config PCIE_ROCKCHIP_EP - +@@ -233,7 +233,7 @@ config PCIE_ROCKCHIP_EP + config PCIE_MEDIATEK - tristate "MediaTek PCIe controller" + tristate "MediaTek PCIe controller" - depends on ARCH_MEDIATEK || COMPILE_TEST + depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST - depends on OF - depends on PCI_MSI_IRQ_DOMAIN - help --- -2.35.1 + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + help diff --git a/target/linux/at91/patches-5.10/127-regulator-core-validate-selector-against-linear_min_.patch b/target/linux/at91/patches-5.10/127-regulator-core-validate-selector-against-linear_min_.patch index 712cdac5dd..14eedb094c 100644 --- a/target/linux/at91/patches-5.10/127-regulator-core-validate-selector-against-linear_min_.patch +++ b/target/linux/at91/patches-5.10/127-regulator-core-validate-selector-against-linear_min_.patch @@ -20,7 +20,7 @@ Signed-off-by: Mark Brown --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c -@@ -2987,7 +2987,8 @@ static int _regulator_list_voltage(struc +@@ -2992,7 +2992,8 @@ static int _regulator_list_voltage(struc return rdev->desc->fixed_uV; if (ops->list_voltage) { @@ -30,7 +30,7 @@ Signed-off-by: Mark Brown return -EINVAL; if (lock) regulator_lock(rdev); -@@ -3138,7 +3139,8 @@ int regulator_list_hardware_vsel(struct +@@ -3143,7 +3144,8 @@ int regulator_list_hardware_vsel(struct struct regulator_dev *rdev = regulator->rdev; const struct regulator_ops *ops = rdev->desc->ops; @@ -40,7 +40,7 @@ Signed-off-by: Mark Brown return -EINVAL; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; -@@ -4061,6 +4063,9 @@ int regulator_set_voltage_time(struct re +@@ -4066,6 +4068,9 @@ int regulator_set_voltage_time(struct re for (i = 0; i < rdev->desc->n_voltages; i++) { /* We only look for exact voltage matches here */ diff --git a/target/linux/at91/patches-5.10/128-regulator-core-do-not-continue-if-selector-match.patch b/target/linux/at91/patches-5.10/128-regulator-core-do-not-continue-if-selector-match.patch index f4f09f888f..2eed3a0aee 100644 --- a/target/linux/at91/patches-5.10/128-regulator-core-do-not-continue-if-selector-match.patch +++ b/target/linux/at91/patches-5.10/128-regulator-core-do-not-continue-if-selector-match.patch @@ -14,7 +14,7 @@ Signed-off-by: Mark Brown --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c -@@ -4066,6 +4066,9 @@ int regulator_set_voltage_time(struct re +@@ -4071,6 +4071,9 @@ int regulator_set_voltage_time(struct re if (i < rdev->desc->linear_min_sel) continue; diff --git a/target/linux/at91/patches-5.10/129-regulator-core-return-zero-for-selectors-lower-than-.patch b/target/linux/at91/patches-5.10/129-regulator-core-return-zero-for-selectors-lower-than-.patch index c093b2b2d3..9af0e0f53c 100644 --- a/target/linux/at91/patches-5.10/129-regulator-core-return-zero-for-selectors-lower-than-.patch +++ b/target/linux/at91/patches-5.10/129-regulator-core-return-zero-for-selectors-lower-than-.patch @@ -21,7 +21,7 @@ Signed-off-by: Mark Brown --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c -@@ -2987,9 +2987,10 @@ static int _regulator_list_voltage(struc +@@ -2992,9 +2992,10 @@ static int _regulator_list_voltage(struc return rdev->desc->fixed_uV; if (ops->list_voltage) { @@ -34,7 +34,7 @@ Signed-off-by: Mark Brown if (lock) regulator_lock(rdev); ret = ops->list_voltage(rdev, selector); -@@ -3139,9 +3140,10 @@ int regulator_list_hardware_vsel(struct +@@ -3144,9 +3145,10 @@ int regulator_list_hardware_vsel(struct struct regulator_dev *rdev = regulator->rdev; const struct regulator_ops *ops = rdev->desc->ops; diff --git a/target/linux/ath79/image/generic.mk b/target/linux/ath79/image/generic.mk index 7b26c5693d..1df0b4ac0f 100644 --- a/target/linux/ath79/image/generic.mk +++ b/target/linux/ath79/image/generic.mk @@ -402,6 +402,8 @@ define Device/asus_rp-ac51 DEVICE_MODEL := RP-AC51 IMAGE_SIZE := 16000k IMAGES += factory.bin + IMAGE/factory.bin := append-kernel | pad-to $$$$(BLOCKSIZE) | \ + append-rootfs | pad-rootfs DEVICE_PACKAGES := kmod-ath10k-ct ath10k-firmware-qca9888-ct \ -swconfig endef diff --git a/target/linux/ath79/patches-5.10/910-unaligned_access_hacks.patch b/target/linux/ath79/patches-5.10/910-unaligned_access_hacks.patch index cd335fbbc8..07cfb6f4e6 100644 --- a/target/linux/ath79/patches-5.10/910-unaligned_access_hacks.patch +++ b/target/linux/ath79/patches-5.10/910-unaligned_access_hacks.patch @@ -706,7 +706,7 @@ EXPORT_SYMBOL(xfrm_parse_spi); --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c -@@ -4116,14 +4116,16 @@ static bool tcp_parse_aligned_timestamp( +@@ -4127,14 +4127,16 @@ static bool tcp_parse_aligned_timestamp( { const __be32 *ptr = (const __be32 *)(th + 1); diff --git a/target/linux/ath79/patches-5.15/910-unaligned_access_hacks.patch b/target/linux/ath79/patches-5.15/910-unaligned_access_hacks.patch index 1eaeefef00..d166702047 100644 --- a/target/linux/ath79/patches-5.15/910-unaligned_access_hacks.patch +++ b/target/linux/ath79/patches-5.15/910-unaligned_access_hacks.patch @@ -706,7 +706,7 @@ EXPORT_SYMBOL(xfrm_parse_spi); --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c -@@ -4140,14 +4140,16 @@ static bool tcp_parse_aligned_timestamp( +@@ -4151,14 +4151,16 @@ static bool tcp_parse_aligned_timestamp( { const __be32 *ptr = (const __be32 *)(th + 1); diff --git a/target/linux/bcm27xx/patches-5.15/950-0035-cgroup-Disable-cgroup-memory-by-default.patch b/target/linux/bcm27xx/patches-5.15/950-0035-cgroup-Disable-cgroup-memory-by-default.patch index a2f7e21708..a836952eeb 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0035-cgroup-Disable-cgroup-memory-by-default.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0035-cgroup-Disable-cgroup-memory-by-default.patch @@ -17,7 +17,7 @@ Signed-off-by: Phil Elwell --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c -@@ -5834,6 +5834,9 @@ int __init cgroup_init_early(void) +@@ -5875,6 +5875,9 @@ int __init cgroup_init_early(void) return 0; } @@ -27,7 +27,7 @@ Signed-off-by: Phil Elwell /** * cgroup_init - cgroup initialization * -@@ -5872,6 +5875,12 @@ int __init cgroup_init(void) +@@ -5913,6 +5916,12 @@ int __init cgroup_init(void) mutex_unlock(&cgroup_mutex); @@ -40,7 +40,7 @@ Signed-off-by: Phil Elwell for_each_subsys(ss, ssid) { if (ss->early_init) { struct cgroup_subsys_state *css = -@@ -6456,6 +6465,10 @@ static int __init cgroup_disable(char *s +@@ -6497,6 +6506,10 @@ static int __init cgroup_disable(char *s strcmp(token, ss->legacy_name)) continue; @@ -51,7 +51,7 @@ Signed-off-by: Phil Elwell static_branch_disable(cgroup_subsys_enabled_key[i]); pr_info("Disabling %s control group subsystem\n", ss->name); -@@ -6474,6 +6487,31 @@ static int __init cgroup_disable(char *s +@@ -6515,6 +6528,31 @@ static int __init cgroup_disable(char *s } __setup("cgroup_disable=", cgroup_disable); diff --git a/target/linux/generic/backport-5.10/610-v5.13-10-netfilter-nftables-update-table-flags-from-the-commi.patch b/target/linux/generic/backport-5.10/610-v5.13-10-netfilter-nftables-update-table-flags-from-the-commi.patch index f9bb0e2cc3..352c6094da 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-10-netfilter-nftables-update-table-flags-from-the-commi.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-10-netfilter-nftables-update-table-flags-from-the-commi.patch @@ -70,7 +70,7 @@ Signed-off-by: Pablo Neira Ayuso nft_trans_table_update(trans) = true; list_add_tail(&trans->list, &ctx->net->nft.commit_list); return 0; -@@ -7919,11 +7923,10 @@ static int nf_tables_commit(struct net * +@@ -7921,11 +7925,10 @@ static int nf_tables_commit(struct net * switch (trans->msg_type) { case NFT_MSG_NEWTABLE: if (nft_trans_table_update(trans)) { @@ -86,7 +86,7 @@ Signed-off-by: Pablo Neira Ayuso } else { nft_clear(net, trans->ctx.table); } -@@ -8137,11 +8140,9 @@ static int __nf_tables_abort(struct net +@@ -8139,11 +8142,9 @@ static int __nf_tables_abort(struct net switch (trans->msg_type) { case NFT_MSG_NEWTABLE: if (nft_trans_table_update(trans)) { diff --git a/target/linux/generic/backport-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch b/target/linux/generic/backport-5.15/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch similarity index 100% rename from target/linux/generic/backport-5.15/081-net-next-regmap-allow-to-define-reg_update_bits-for-no-bus.patch rename to target/linux/generic/backport-5.15/081-v5.17-regmap-allow-to-define-reg_update_bits-for-no-bus.patch diff --git a/target/linux/generic/backport-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch b/target/linux/generic/backport-5.15/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch similarity index 100% rename from target/linux/generic/backport-5.15/700-net-next-net-dsa-introduce-tagger-owned-storage-for-private.patch rename to target/linux/generic/backport-5.15/700-v5.17-net-dsa-introduce-tagger-owned-storage-for-private.patch diff --git a/target/linux/generic/backport-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch b/target/linux/generic/backport-5.15/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch similarity index 100% rename from target/linux/generic/backport-5.15/701-net-dsa-make-tagging-protocols-connect-to-individual-switches.patch rename to target/linux/generic/backport-5.15/701-v5.17-dsa-make-tagging-protocols-connect-to-individual-switches.patch diff --git a/target/linux/generic/backport-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch b/target/linux/generic/backport-5.15/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch similarity index 100% rename from target/linux/generic/backport-5.15/753-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch rename to target/linux/generic/backport-5.15/753-v5.17-net-next-net-dsa-qca8k-remove-redundant-check-in-parse_port_config.patch diff --git a/target/linux/generic/backport-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch b/target/linux/generic/backport-5.15/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch similarity index 100% rename from target/linux/generic/backport-5.15/754-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch rename to target/linux/generic/backport-5.15/754-v5.17-net-next-net-dsa-qca8k-convert-to-GENMASK_FIELD_PREP_FIELD_GET.patch diff --git a/target/linux/generic/backport-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch b/target/linux/generic/backport-5.15/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch similarity index 100% rename from target/linux/generic/backport-5.15/755-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch rename to target/linux/generic/backport-5.15/755-v5.17-net-next-net-dsa-qca8k-remove-extra-mutex_init-in-qca8k_setup.patch diff --git a/target/linux/generic/backport-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch b/target/linux/generic/backport-5.15/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch similarity index 100% rename from target/linux/generic/backport-5.15/756-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch rename to target/linux/generic/backport-5.15/756-v5.17-net-next-net-dsa-qca8k-move-regmap-init-in-probe-and-set-it.patch diff --git a/target/linux/generic/backport-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch b/target/linux/generic/backport-5.15/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch similarity index 100% rename from target/linux/generic/backport-5.15/757-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch rename to target/linux/generic/backport-5.15/757-v5.17-net-next-net-dsa-qca8k-initial-conversion-to-regmap-heper.patch diff --git a/target/linux/generic/backport-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch b/target/linux/generic/backport-5.15/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch similarity index 100% rename from target/linux/generic/backport-5.15/758-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch rename to target/linux/generic/backport-5.15/758-v5.17-net-next-net-dsa-qca8k-add-additional-MIB-counter-and-.patch diff --git a/target/linux/generic/backport-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch b/target/linux/generic/backport-5.15/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch similarity index 100% rename from target/linux/generic/backport-5.15/759-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch rename to target/linux/generic/backport-5.15/759-v5.17-net-next-net-dsa-qca8k-add-support-for-port-fast-aging.patch diff --git a/target/linux/generic/backport-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch b/target/linux/generic/backport-5.15/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch similarity index 100% rename from target/linux/generic/backport-5.15/760-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch rename to target/linux/generic/backport-5.15/760-v5.17-net-next-net-dsa-qca8k-add-set_ageing_time-support.patch diff --git a/target/linux/generic/backport-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch b/target/linux/generic/backport-5.15/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch similarity index 100% rename from target/linux/generic/backport-5.15/761-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch rename to target/linux/generic/backport-5.15/761-v5.17-net-next-net-dsa-qca8k-add-support-for-mdb_add-del.patch diff --git a/target/linux/generic/backport-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch b/target/linux/generic/backport-5.15/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch similarity index 100% rename from target/linux/generic/backport-5.15/762-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch rename to target/linux/generic/backport-5.15/762-v5.17-net-next-net-dsa-qca8k-add-support-for-mirror-mode.patch diff --git a/target/linux/generic/backport-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch b/target/linux/generic/backport-5.15/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch similarity index 100% rename from target/linux/generic/backport-5.15/763-net-next-net-dsa-qca8k-add-LAG-support.patch rename to target/linux/generic/backport-5.15/763-v5.17-net-next-net-dsa-qca8k-add-LAG-support.patch diff --git a/target/linux/generic/backport-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch b/target/linux/generic/backport-5.15/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch similarity index 100% rename from target/linux/generic/backport-5.15/764-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch rename to target/linux/generic/backport-5.15/764-v5.17-net-next-net-dsa-qca8k-fix-warning-in-LAG-feature.patch diff --git a/target/linux/generic/backport-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch b/target/linux/generic/backport-5.15/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch similarity index 92% rename from target/linux/generic/backport-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch rename to target/linux/generic/backport-5.15/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch index 77cf63b809..1786bf0345 100644 --- a/target/linux/generic/backport-5.15/765-1-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch +++ b/target/linux/generic/backport-5.15/765-v5.17-01-net-next-net-dsa-reorder-PHY-initialization-with-MTU-setup-in.patch @@ -22,7 +22,7 @@ Signed-off-by: David S. Miller --- a/net/dsa/slave.c +++ b/net/dsa/slave.c -@@ -1977,13 +1977,6 @@ int dsa_slave_create(struct dsa_port *po +@@ -1986,13 +1986,6 @@ int dsa_slave_create(struct dsa_port *po port->slave = slave_dev; dsa_slave_setup_tagger(slave_dev); @@ -36,7 +36,7 @@ Signed-off-by: David S. Miller netif_carrier_off(slave_dev); ret = dsa_slave_phy_setup(slave_dev); -@@ -1995,6 +1988,13 @@ int dsa_slave_create(struct dsa_port *po +@@ -2004,6 +1997,13 @@ int dsa_slave_create(struct dsa_port *po } rtnl_lock(); diff --git a/target/linux/generic/backport-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch b/target/linux/generic/backport-5.15/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch similarity index 94% rename from target/linux/generic/backport-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch rename to target/linux/generic/backport-5.15/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch index 50aa5d8f0d..c2493a08fd 100644 --- a/target/linux/generic/backport-5.15/765-2-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch +++ b/target/linux/generic/backport-5.15/765-v5.17-02-net-next-net-dsa-merge-rtnl_lock-sections-in-dsa_slave_create.patch @@ -16,7 +16,7 @@ Signed-off-by: David S. Miller --- a/net/dsa/slave.c +++ b/net/dsa/slave.c -@@ -1988,14 +1988,12 @@ int dsa_slave_create(struct dsa_port *po +@@ -1997,14 +1997,12 @@ int dsa_slave_create(struct dsa_port *po } rtnl_lock(); diff --git a/target/linux/generic/backport-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch b/target/linux/generic/backport-5.15/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch similarity index 100% rename from target/linux/generic/backport-5.15/765-3-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch rename to target/linux/generic/backport-5.15/765-v5.17-03-net-next-net-dsa-stop-updating-master-MTU-from-master.c.patch diff --git a/target/linux/generic/backport-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch b/target/linux/generic/backport-5.15/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch similarity index 100% rename from target/linux/generic/backport-5.15/765-4-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch rename to target/linux/generic/backport-5.15/765-v5.17-04-net-next-net-dsa-hold-rtnl_mutex-when-calling-dsa_master_-set.patch diff --git a/target/linux/generic/backport-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch b/target/linux/generic/backport-5.15/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch similarity index 100% rename from target/linux/generic/backport-5.15/765-5-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch rename to target/linux/generic/backport-5.15/765-v5.17-05-net-next-net-dsa-first-set-up-shared-ports-then-non-shared-po.patch diff --git a/target/linux/generic/backport-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch b/target/linux/generic/backport-5.15/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch similarity index 100% rename from target/linux/generic/backport-5.15/765-6-net-next-net-dsa-setup-master-before-ports.patch rename to target/linux/generic/backport-5.15/765-v5.17-06-net-next-net-dsa-setup-master-before-ports.patch diff --git a/target/linux/generic/backport-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch b/target/linux/generic/backport-5.15/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch similarity index 97% rename from target/linux/generic/backport-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch rename to target/linux/generic/backport-5.15/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch index 7c6a3a3f8d..d73b745586 100644 --- a/target/linux/generic/backport-5.15/766-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch +++ b/target/linux/generic/backport-5.15/766-v5.18-01-net-dsa-provide-switch-operations-for-tracking-the-m.patch @@ -68,7 +68,7 @@ Signed-off-by: David S. Miller static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) { return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED; -@@ -949,6 +959,13 @@ struct dsa_switch_ops { +@@ -916,6 +926,13 @@ struct dsa_switch_ops { int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid, u16 flags); int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid); @@ -175,7 +175,7 @@ Signed-off-by: David S. Miller --- a/net/dsa/slave.c +++ b/net/dsa/slave.c -@@ -2311,6 +2311,36 @@ static int dsa_slave_netdevice_event(str +@@ -2320,6 +2320,36 @@ static int dsa_slave_netdevice_event(str err = dsa_port_lag_change(dp, info->lower_state_info); return notifier_from_errno(err); } @@ -212,7 +212,7 @@ Signed-off-by: David S. Miller case NETDEV_GOING_DOWN: { struct dsa_port *dp, *cpu_dp; struct dsa_switch_tree *dst; -@@ -2322,6 +2352,8 @@ static int dsa_slave_netdevice_event(str +@@ -2331,6 +2361,8 @@ static int dsa_slave_netdevice_event(str cpu_dp = dev->dsa_ptr; dst = cpu_dp->ds->dst; diff --git a/target/linux/generic/backport-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch b/target/linux/generic/backport-5.15/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch rename to target/linux/generic/backport-5.15/766-v5.18-02-net-dsa-replay-master-state-events-in-dsa_tree_-setu.patch diff --git a/target/linux/generic/backport-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch b/target/linux/generic/backport-5.15/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch rename to target/linux/generic/backport-5.15/766-v5.18-03-net-dsa-tag_qca-convert-to-FIELD-macro.patch diff --git a/target/linux/generic/backport-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch b/target/linux/generic/backport-5.15/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch rename to target/linux/generic/backport-5.15/766-v5.18-04-net-dsa-tag_qca-move-define-to-include-linux-dsa.patch diff --git a/target/linux/generic/backport-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch b/target/linux/generic/backport-5.15/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch rename to target/linux/generic/backport-5.15/766-v5.18-05-net-dsa-tag_qca-enable-promisc_on_master-flag.patch diff --git a/target/linux/generic/backport-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch b/target/linux/generic/backport-5.15/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch rename to target/linux/generic/backport-5.15/766-v5.18-06-net-dsa-tag_qca-add-define-for-handling-mgmt-Etherne.patch diff --git a/target/linux/generic/backport-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch b/target/linux/generic/backport-5.15/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch rename to target/linux/generic/backport-5.15/766-v5.18-07-net-dsa-tag_qca-add-define-for-handling-MIB-packet.patch diff --git a/target/linux/generic/backport-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch b/target/linux/generic/backport-5.15/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch rename to target/linux/generic/backport-5.15/766-v5.18-08-net-dsa-tag_qca-add-support-for-handling-mgmt-and-MI.patch diff --git a/target/linux/generic/backport-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch b/target/linux/generic/backport-5.15/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch rename to target/linux/generic/backport-5.15/766-v5.18-09-net-dsa-qca8k-add-tracking-state-of-master-port.patch diff --git a/target/linux/generic/backport-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch b/target/linux/generic/backport-5.15/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch rename to target/linux/generic/backport-5.15/766-v5.18-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch diff --git a/target/linux/generic/backport-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch b/target/linux/generic/backport-5.15/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch rename to target/linux/generic/backport-5.15/766-v5.18-11-net-dsa-qca8k-add-support-for-mib-autocast-in-Ethern.patch diff --git a/target/linux/generic/backport-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch b/target/linux/generic/backport-5.15/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch rename to target/linux/generic/backport-5.15/766-v5.18-12-net-dsa-qca8k-add-support-for-phy-read-write-with-mg.patch diff --git a/target/linux/generic/backport-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch b/target/linux/generic/backport-5.15/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch rename to target/linux/generic/backport-5.15/766-v5.18-13-net-dsa-qca8k-move-page-cache-to-driver-priv.patch diff --git a/target/linux/generic/backport-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch b/target/linux/generic/backport-5.15/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch rename to target/linux/generic/backport-5.15/766-v5.18-14-net-dsa-qca8k-cache-lo-and-hi-for-mdio-write.patch diff --git a/target/linux/generic/backport-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch b/target/linux/generic/backport-5.15/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch rename to target/linux/generic/backport-5.15/766-v5.18-15-net-dsa-qca8k-add-support-for-larger-read-write-size.patch diff --git a/target/linux/generic/backport-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch b/target/linux/generic/backport-5.15/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch similarity index 100% rename from target/linux/generic/backport-5.15/766-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch rename to target/linux/generic/backport-5.15/766-v5.18-16-net-dsa-qca8k-introduce-qca8k_bulk_read-write-functi.patch diff --git a/target/linux/generic/backport-5.15/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch b/target/linux/generic/backport-5.15/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch new file mode 100644 index 0000000000..34607c223c --- /dev/null +++ b/target/linux/generic/backport-5.15/767-v5.18-net-dsa-qca8k-check-correct-variable-in-qca8k_phy_et.patch @@ -0,0 +1,28 @@ +From c3664d913dc115cab4a5fdb5634df4887048000e Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Fri, 4 Feb 2022 13:03:36 +0300 +Subject: [PATCH 1/1] net: dsa: qca8k: check correct variable in + qca8k_phy_eth_command() + +This is a copy and paste bug. It was supposed to check "clear_skb" +instead of "write_skb". + +Fixes: 2cd548566384 ("net: dsa: qca8k: add support for phy read/write with mgmt Ethernet") +Signed-off-by: Dan Carpenter +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1018,7 +1018,7 @@ qca8k_phy_eth_command(struct qca8k_priv + + clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, + QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); +- if (!write_skb) { ++ if (!clear_skb) { + ret = -ENOMEM; + goto err_clear_skb; + } diff --git a/target/linux/generic/backport-5.15/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch b/target/linux/generic/backport-5.15/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch new file mode 100644 index 0000000000..d8cf266309 --- /dev/null +++ b/target/linux/generic/backport-5.15/768-v5.18-net-dsa-qca8k-fix-noderef.cocci-warnings.patch @@ -0,0 +1,34 @@ +From 4f5e483b8c7a644733db941a1ae00173baa7b463 Mon Sep 17 00:00:00 2001 +From: kernel test robot +Date: Thu, 10 Feb 2022 06:13:04 +0800 +Subject: [PATCH 1/1] net: dsa: qca8k: fix noderef.cocci warnings + +drivers/net/dsa/qca8k.c:422:37-43: ERROR: application of sizeof to pointer + + sizeof when applied to a pointer typed expression gives the size of + the pointer + +Generated by: scripts/coccinelle/misc/noderef.cocci + +Fixes: 90386223f44e ("net: dsa: qca8k: add support for larger read/write size with mgmt Ethernet") +CC: Ansuel Smith +Reported-by: kernel test robot +Signed-off-by: kernel test robot +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20220209221304.GA17529@d2214a582157 +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca8k.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -456,7 +456,7 @@ qca8k_regmap_read(void *ctx, uint32_t re + u16 r1, r2, page; + int ret; + +- if (!qca8k_read_eth(priv, reg, val, sizeof(val))) ++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) + return 0; + + qca8k_split_addr(reg, &r1, &r2, &page); diff --git a/target/linux/generic/backport-5.15/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch b/target/linux/generic/backport-5.15/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch new file mode 100644 index 0000000000..57df4c126e --- /dev/null +++ b/target/linux/generic/backport-5.15/769-v5.19-01-net-dsa-qca8k-drop-MTU-tracking-from-qca8k_priv.patch @@ -0,0 +1,79 @@ +From 69fd055957a02309ffdc23d887a01988b6e5bab1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:12 +0200 +Subject: [PATCH 1/6] net: dsa: qca8k: drop MTU tracking from qca8k_priv + +DSA set the CPU port based on the largest MTU of all the slave ports. +Based on this we can drop the MTU array from qca8k_priv and set the +port_change_mtu logic on DSA changing MTU of the CPU port as the switch +have a global MTU settingfor each port. + +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 26 +++++++++----------------- + drivers/net/dsa/qca8k.h | 1 - + 2 files changed, 9 insertions(+), 18 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1803,16 +1803,6 @@ qca8k_setup(struct dsa_switch *ds) + QCA8K_PORT_HOL_CTRL1_WRED_EN, + mask); + } +- +- /* Set initial MTU for every port. +- * We have only have a general MTU setting. So track +- * every port and set the max across all port. +- * Set per port MTU to 1500 as the MTU change function +- * will add the overhead and if its set to 1518 then it +- * will apply the overhead again and we will end up with +- * MTU of 1536 instead of 1518 +- */ +- priv->port_mtu[i] = ETH_DATA_LEN; + } + + /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ +@@ -2525,13 +2515,16 @@ static int + qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) + { + struct qca8k_priv *priv = ds->priv; +- int ret, i, mtu = 0; +- +- priv->port_mtu[port] = new_mtu; ++ int ret; + +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- if (priv->port_mtu[i] > mtu) +- mtu = priv->port_mtu[i]; ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; + + /* To change the MAX_FRAME_SIZE the cpu ports must be off or + * the switch panics. +@@ -2545,7 +2538,7 @@ qca8k_port_change_mtu(struct dsa_switch + qca8k_port_set_status(priv, 6, 0); + + /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, mtu + ETH_HLEN + ETH_FCS_LEN); ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); + + if (priv->port_sts[0].enabled) + qca8k_port_set_status(priv, 0, 1); +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -392,7 +392,6 @@ struct qca8k_priv { + struct device *dev; + struct dsa_switch_ops ops; + struct gpio_desc *reset_gpio; +- unsigned int port_mtu[QCA8K_NUM_PORTS]; + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + struct qca8k_mgmt_eth_data mgmt_eth_data; + struct qca8k_mib_eth_data mib_eth_data; diff --git a/target/linux/generic/backport-5.15/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch b/target/linux/generic/backport-5.15/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch new file mode 100644 index 0000000000..3cacd7e4fd --- /dev/null +++ b/target/linux/generic/backport-5.15/769-v5.19-02-net-dsa-qca8k-drop-port_sts-from-qca8k_priv.patch @@ -0,0 +1,116 @@ +From 2b8fd87af7f156942971789abac8ee2bb60c03bc Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:13 +0200 +Subject: [PATCH 2/6] net: dsa: qca8k: drop port_sts from qca8k_priv + +Port_sts is a thing of the past for this driver. It was something +present on the initial implementation of this driver and parts of the +original struct were dropped over time. Using an array of int to store if +a port is enabled or not to handle PM operation seems overkill. Switch +and use a simple u8 to store the port status where each bit correspond +to a port. (bit is set port is enabled, bit is not set, port is disabled) +Also add some comments to better describe why we need to track port +status. + +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 15 +++++++++------ + drivers/net/dsa/qca8k.h | 9 ++++----- + 2 files changed, 13 insertions(+), 11 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -2494,7 +2494,7 @@ qca8k_port_enable(struct dsa_switch *ds, + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + + qca8k_port_set_status(priv, port, 1); +- priv->port_sts[port].enabled = 1; ++ priv->port_enabled_map |= BIT(port); + + if (dsa_is_user_port(ds, port)) + phy_support_asym_pause(phy); +@@ -2508,7 +2508,7 @@ qca8k_port_disable(struct dsa_switch *ds + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; + + qca8k_port_set_status(priv, port, 0); +- priv->port_sts[port].enabled = 0; ++ priv->port_enabled_map &= ~BIT(port); + } + + static int +@@ -2531,19 +2531,19 @@ qca8k_port_change_mtu(struct dsa_switch + * Turn off both cpu ports before applying the new value to prevent + * this. + */ +- if (priv->port_sts[0].enabled) ++ if (priv->port_enabled_map & BIT(0)) + qca8k_port_set_status(priv, 0, 0); + +- if (priv->port_sts[6].enabled) ++ if (priv->port_enabled_map & BIT(6)) + qca8k_port_set_status(priv, 6, 0); + + /* Include L2 header / FCS length */ + ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); + +- if (priv->port_sts[0].enabled) ++ if (priv->port_enabled_map & BIT(0)) + qca8k_port_set_status(priv, 0, 1); + +- if (priv->port_sts[6].enabled) ++ if (priv->port_enabled_map & BIT(6)) + qca8k_port_set_status(priv, 6, 1); + + return ret; +@@ -3199,13 +3199,16 @@ static void qca8k_sw_shutdown(struct mdi + static void + qca8k_set_pm(struct qca8k_priv *priv, int enable) + { +- int i; ++ int port; + +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (!priv->port_sts[i].enabled) ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Do not enable on resume if the port was ++ * disabled before. ++ */ ++ if (!(priv->port_enabled_map & BIT(port))) + continue; + +- qca8k_port_set_status(priv, i, enable); ++ qca8k_port_set_status(priv, port, enable); + } + } + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -324,10 +324,6 @@ enum qca8k_mid_cmd { + QCA8K_MIB_CAST = 3, + }; + +-struct ar8xxx_port_status { +- int enabled; +-}; +- + struct qca8k_match_data { + u8 id; + bool reduced_package; +@@ -382,11 +378,14 @@ struct qca8k_priv { + u8 mirror_rx; + u8 mirror_tx; + u8 lag_hash_mode; ++ /* Each bit correspond to a port. This switch can support a max of 7 port. ++ * Bit 1: port enabled. Bit 0: port disabled. ++ */ ++ u8 port_enabled_map; + bool legacy_phy_port_mapping; + struct qca8k_ports_config ports_config; + struct regmap *regmap; + struct mii_bus *bus; +- struct ar8xxx_port_status port_sts[QCA8K_NUM_PORTS]; + struct dsa_switch *ds; + struct mutex reg_mutex; + struct device *dev; diff --git a/target/linux/generic/backport-5.15/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch b/target/linux/generic/backport-5.15/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch new file mode 100644 index 0000000000..12c3221077 --- /dev/null +++ b/target/linux/generic/backport-5.15/769-v5.19-03-net-dsa-qca8k-rework-and-simplify-mdiobus-logic.patch @@ -0,0 +1,173 @@ +From 8255212e4130bd2dc1463286a3dddb74797bbdc1 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:14 +0200 +Subject: [PATCH 3/6] net: dsa: qca8k: rework and simplify mdiobus logic + +In an attempt to reduce qca8k_priv space, rework and simplify mdiobus +logic. +We now declare a mdiobus instead of relying on DSA phy_read/write even +if a mdio node is not present. This is all to make the qca8k ops static +and not switch specific. With a legacy implementation where port doesn't +have a phy map declared in the dts with a mdio node, we declare a +'qca8k-legacy' mdiobus. The conversion logic is used as legacy read and +write ops are used instead of the internal one. +Also drop the legacy_phy_port_mapping as we now declare mdiobus with ops +that already address the workaround. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 95 +++++++++++++---------------------------- + drivers/net/dsa/qca8k.h | 1 - + 2 files changed, 29 insertions(+), 67 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1291,83 +1291,63 @@ qca8k_internal_mdio_read(struct mii_bus + } + + static int +-qca8k_phy_write(struct dsa_switch *ds, int port, int regnum, u16 data) ++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) + { +- struct qca8k_priv *priv = ds->priv; +- int ret; ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + +- /* Check if the legacy mapping should be used and the +- * port is not correctly mapped to the right PHY in the +- * devicetree +- */ +- if (priv->legacy_phy_port_mapping) +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- /* Use mdio Ethernet when available, fallback to legacy one on error */ +- ret = qca8k_phy_eth_command(priv, false, port, regnum, 0); +- if (!ret) +- return ret; +- +- return qca8k_mdio_write(priv, port, regnum, data); ++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data); + } + + static int +-qca8k_phy_read(struct dsa_switch *ds, int port, int regnum) ++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) + { +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- /* Check if the legacy mapping should be used and the +- * port is not correctly mapped to the right PHY in the +- * devicetree +- */ +- if (priv->legacy_phy_port_mapping) +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- /* Use mdio Ethernet when available, fallback to legacy one on error */ +- ret = qca8k_phy_eth_command(priv, true, port, regnum, 0); +- if (ret >= 0) +- return ret; +- +- ret = qca8k_mdio_read(priv, port, regnum); +- +- if (ret < 0) +- return 0xffff; ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; + +- return ret; ++ return qca8k_internal_mdio_read(slave_bus, port, regnum); + } + + static int +-qca8k_mdio_register(struct qca8k_priv *priv, struct device_node *mdio) ++qca8k_mdio_register(struct qca8k_priv *priv) + { + struct dsa_switch *ds = priv->ds; ++ struct device_node *mdio; + struct mii_bus *bus; + + bus = devm_mdiobus_alloc(ds->dev); +- + if (!bus) + return -ENOMEM; + + bus->priv = (void *)priv; +- bus->name = "qca8k slave mii"; +- bus->read = qca8k_internal_mdio_read; +- bus->write = qca8k_internal_mdio_write; +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", +- ds->index); +- + bus->parent = ds->dev; + bus->phy_mask = ~ds->phys_mii_mask; +- + ds->slave_mii_bus = bus; + +- return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ /* Check if the devicetree declare the port:phy mapping */ ++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (of_device_is_available(mdio)) { ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index); ++ bus->name = "qca8k slave mii"; ++ bus->read = qca8k_internal_mdio_read; ++ bus->write = qca8k_internal_mdio_write; ++ return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ } ++ ++ /* If a mapping can't be found the legacy mapping is used, ++ * using the qca8k_port_to_phy function ++ */ ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); ++ bus->name = "qca8k-legacy slave mii"; ++ bus->read = qca8k_legacy_mdio_read; ++ bus->write = qca8k_legacy_mdio_write; ++ return devm_mdiobus_register(priv->dev, bus); + } + + static int + qca8k_setup_mdio_bus(struct qca8k_priv *priv) + { + u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; +- struct device_node *ports, *port, *mdio; ++ struct device_node *ports, *port; + phy_interface_t mode; + int err; + +@@ -1429,24 +1409,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv * + QCA8K_MDIO_MASTER_EN); + } + +- /* Check if the devicetree declare the port:phy mapping */ +- mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (of_device_is_available(mdio)) { +- err = qca8k_mdio_register(priv, mdio); +- if (err) +- of_node_put(mdio); +- +- return err; +- } +- +- /* If a mapping can't be found the legacy mapping is used, +- * using the qca8k_port_to_phy function +- */ +- priv->legacy_phy_port_mapping = true; +- priv->ops.phy_read = qca8k_phy_read; +- priv->ops.phy_write = qca8k_phy_write; +- +- return 0; ++ return qca8k_mdio_register(priv); + } + + static int +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -382,7 +382,6 @@ struct qca8k_priv { + * Bit 1: port enabled. Bit 0: port disabled. + */ + u8 port_enabled_map; +- bool legacy_phy_port_mapping; + struct qca8k_ports_config ports_config; + struct regmap *regmap; + struct mii_bus *bus; diff --git a/target/linux/generic/backport-5.15/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch b/target/linux/generic/backport-5.15/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch new file mode 100644 index 0000000000..8641000abb --- /dev/null +++ b/target/linux/generic/backport-5.15/769-v5.19-04-net-dsa-qca8k-drop-dsa_switch_ops-from-qca8k_priv.patch @@ -0,0 +1,39 @@ +From 2349b83a2486c55b9dd225326f0172a84a43c5e4 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:15 +0200 +Subject: [PATCH 4/6] net: dsa: qca8k: drop dsa_switch_ops from qca8k_priv + +Now that dsa_switch_ops is not switch specific anymore, we can drop it +from qca8k_priv and use the static ops directly for the dsa_switch +pointer. + +Signed-off-by: Ansuel Smith +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 3 +-- + drivers/net/dsa/qca8k.h | 1 - + 2 files changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -3121,8 +3121,7 @@ qca8k_sw_probe(struct mdio_device *mdiod + priv->ds->dev = &mdiodev->dev; + priv->ds->num_ports = QCA8K_NUM_PORTS; + priv->ds->priv = priv; +- priv->ops = qca8k_switch_ops; +- priv->ds->ops = &priv->ops; ++ priv->ds->ops = &qca8k_switch_ops; + mutex_init(&priv->reg_mutex); + dev_set_drvdata(&mdiodev->dev, priv); + +--- a/drivers/net/dsa/qca8k.h ++++ b/drivers/net/dsa/qca8k.h +@@ -388,7 +388,6 @@ struct qca8k_priv { + struct dsa_switch *ds; + struct mutex reg_mutex; + struct device *dev; +- struct dsa_switch_ops ops; + struct gpio_desc *reset_gpio; + struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ + struct qca8k_mgmt_eth_data mgmt_eth_data; diff --git a/target/linux/generic/backport-5.15/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch b/target/linux/generic/backport-5.15/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch new file mode 100644 index 0000000000..b14b22091b --- /dev/null +++ b/target/linux/generic/backport-5.15/769-v5.19-05-net-dsa-qca8k-correctly-handle-mdio-read-error.patch @@ -0,0 +1,33 @@ +From 6cfc03b602200c5cbbd8d906fd905547814e83df Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:16 +0200 +Subject: [PATCH 5/6] net: dsa: qca8k: correctly handle mdio read error + +Restore original way to handle mdio read error by returning 0xffff. +This was wrongly changed when the internal_mdio_read was introduced, +now that both legacy and internal use the same function, make sure that +they behave the same way. + +Fixes: ce062a0adbfe ("net: dsa: qca8k: fix kernel panic with legacy mdio mapping") +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1287,7 +1287,12 @@ qca8k_internal_mdio_read(struct mii_bus + if (ret >= 0) + return ret; + +- return qca8k_mdio_read(priv, phy, regnum); ++ ret = qca8k_mdio_read(priv, phy, regnum); ++ ++ if (ret < 0) ++ return 0xffff; ++ ++ return ret; + } + + static int diff --git a/target/linux/generic/backport-5.15/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch b/target/linux/generic/backport-5.15/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch new file mode 100644 index 0000000000..094686f11b --- /dev/null +++ b/target/linux/generic/backport-5.15/769-v5.19-06-net-dsa-qca8k-unify-bus-id-naming-with-legacy-and-OF.patch @@ -0,0 +1,44 @@ +From 8d1af50842bf2774f4edc57054206e909117469b Mon Sep 17 00:00:00 2001 +From: Ansuel Smith +Date: Sat, 16 Apr 2022 01:30:17 +0200 +Subject: [PATCH 6/6] net: dsa: qca8k: unify bus id naming with legacy and OF + mdio bus + +Add support for multiple switch with OF mdio bus declaration. +Unify the bus id naming and use the same logic for both legacy and OF +mdio bus. + +Signed-off-by: Ansuel Smith +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca8k.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/net/dsa/qca8k.c ++++ b/drivers/net/dsa/qca8k.c +@@ -1323,6 +1323,8 @@ qca8k_mdio_register(struct qca8k_priv *p + return -ENOMEM; + + bus->priv = (void *)priv; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); + bus->parent = ds->dev; + bus->phy_mask = ~ds->phys_mii_mask; + ds->slave_mii_bus = bus; +@@ -1330,7 +1332,6 @@ qca8k_mdio_register(struct qca8k_priv *p + /* Check if the devicetree declare the port:phy mapping */ + mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (of_device_is_available(mdio)) { +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d", ds->index); + bus->name = "qca8k slave mii"; + bus->read = qca8k_internal_mdio_read; + bus->write = qca8k_internal_mdio_write; +@@ -1340,8 +1341,6 @@ qca8k_mdio_register(struct qca8k_priv *p + /* If a mapping can't be found the legacy mapping is used, + * using the qca8k_port_to_phy function + */ +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", +- ds->dst->index, ds->index); + bus->name = "qca8k-legacy slave mii"; + bus->read = qca8k_legacy_mdio_read; + bus->write = qca8k_legacy_mdio_write; diff --git a/target/linux/generic/backport-5.15/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch b/target/linux/generic/backport-5.15/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch new file mode 100644 index 0000000000..1534113f1d --- /dev/null +++ b/target/linux/generic/backport-5.15/770-v6.0-net-dsa-qca8k-move-driver-to-qca-dir.patch @@ -0,0 +1,7389 @@ +From 4bbaf764e1e1786eb937fdb62172f656f512e116 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 13 Jul 2022 22:53:50 +0200 +Subject: [PATCH 1/1] net: dsa: qca8k: move driver to qca dir + +Move qca8k driver to qca dir in preparation for code split and +introduction of ipq4019 switch based on qca8k. + +Signed-off-by: Christian Marangi +Reviewed-by: Florian Fainelli +Signed-off-by: David S. Miller +--- + drivers/net/dsa/Kconfig | 8 -------- + drivers/net/dsa/Makefile | 1 - + drivers/net/dsa/qca/Kconfig | 8 ++++++++ + drivers/net/dsa/qca/Makefile | 1 + + drivers/net/dsa/{ => qca}/qca8k.c | 0 + drivers/net/dsa/{ => qca}/qca8k.h | 0 + 6 files changed, 9 insertions(+), 9 deletions(-) + rename drivers/net/dsa/{ => qca}/qca8k.c (100%) + rename drivers/net/dsa/{ => qca}/qca8k.h (100%) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -60,14 +60,6 @@ source "drivers/net/dsa/sja1105/Kconfig" + + source "drivers/net/dsa/xrs700x/Kconfig" + +-config NET_DSA_QCA8K +- tristate "Qualcomm Atheros QCA8K Ethernet switch family support" +- select NET_DSA_TAG_QCA +- select REGMAP +- help +- This enables support for the Qualcomm Atheros QCA8K Ethernet +- switch chips. +- + config NET_DSA_REALTEK_SMI + tristate "Realtek SMI Ethernet switch family support" + select NET_DSA_TAG_RTL4_A +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -8,7 +8,6 @@ endif + obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o + obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o + obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o +-obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o + obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o + realtek-smi-objs := realtek-smi-core.o rtl8366.o rtl8366rb.o + obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o +--- a/drivers/net/dsa/qca/Kconfig ++++ b/drivers/net/dsa/qca/Kconfig +@@ -7,3 +7,11 @@ config NET_DSA_AR9331 + help + This enables support for the Qualcomm Atheros AR9331 built-in Ethernet + switch. ++ ++config NET_DSA_QCA8K ++ tristate "Qualcomm Atheros QCA8K Ethernet switch family support" ++ select NET_DSA_TAG_QCA ++ select REGMAP ++ help ++ This enables support for the Qualcomm Atheros QCA8K Ethernet ++ switch chips. +--- a/drivers/net/dsa/qca/Makefile ++++ b/drivers/net/dsa/qca/Makefile +@@ -1,2 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o ++obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k.c +@@ -0,0 +1,3243 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2016 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qca8k.h" ++ ++#define MIB_DESC(_s, _o, _n) \ ++ { \ ++ .size = (_s), \ ++ .offset = (_o), \ ++ .name = (_n), \ ++ } ++ ++static const struct qca8k_mib_desc ar8327_mib[] = { ++ MIB_DESC(1, 0x00, "RxBroad"), ++ MIB_DESC(1, 0x04, "RxPause"), ++ MIB_DESC(1, 0x08, "RxMulti"), ++ MIB_DESC(1, 0x0c, "RxFcsErr"), ++ MIB_DESC(1, 0x10, "RxAlignErr"), ++ MIB_DESC(1, 0x14, "RxRunt"), ++ MIB_DESC(1, 0x18, "RxFragment"), ++ MIB_DESC(1, 0x1c, "Rx64Byte"), ++ MIB_DESC(1, 0x20, "Rx128Byte"), ++ MIB_DESC(1, 0x24, "Rx256Byte"), ++ MIB_DESC(1, 0x28, "Rx512Byte"), ++ MIB_DESC(1, 0x2c, "Rx1024Byte"), ++ MIB_DESC(1, 0x30, "Rx1518Byte"), ++ MIB_DESC(1, 0x34, "RxMaxByte"), ++ MIB_DESC(1, 0x38, "RxTooLong"), ++ MIB_DESC(2, 0x3c, "RxGoodByte"), ++ MIB_DESC(2, 0x44, "RxBadByte"), ++ MIB_DESC(1, 0x4c, "RxOverFlow"), ++ MIB_DESC(1, 0x50, "Filtered"), ++ MIB_DESC(1, 0x54, "TxBroad"), ++ MIB_DESC(1, 0x58, "TxPause"), ++ MIB_DESC(1, 0x5c, "TxMulti"), ++ MIB_DESC(1, 0x60, "TxUnderRun"), ++ MIB_DESC(1, 0x64, "Tx64Byte"), ++ MIB_DESC(1, 0x68, "Tx128Byte"), ++ MIB_DESC(1, 0x6c, "Tx256Byte"), ++ MIB_DESC(1, 0x70, "Tx512Byte"), ++ MIB_DESC(1, 0x74, "Tx1024Byte"), ++ MIB_DESC(1, 0x78, "Tx1518Byte"), ++ MIB_DESC(1, 0x7c, "TxMaxByte"), ++ MIB_DESC(1, 0x80, "TxOverSize"), ++ MIB_DESC(2, 0x84, "TxByte"), ++ MIB_DESC(1, 0x8c, "TxCollision"), ++ MIB_DESC(1, 0x90, "TxAbortCol"), ++ MIB_DESC(1, 0x94, "TxMultiCol"), ++ MIB_DESC(1, 0x98, "TxSingleCol"), ++ MIB_DESC(1, 0x9c, "TxExcDefer"), ++ MIB_DESC(1, 0xa0, "TxDefer"), ++ MIB_DESC(1, 0xa4, "TxLateCol"), ++ MIB_DESC(1, 0xa8, "RXUnicast"), ++ MIB_DESC(1, 0xac, "TXUnicast"), ++}; ++ ++static void ++qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) ++{ ++ regaddr >>= 1; ++ *r1 = regaddr & 0x1e; ++ ++ regaddr >>= 5; ++ *r2 = regaddr & 0x7; ++ ++ regaddr >>= 3; ++ *page = regaddr & 0x3ff; ++} ++ ++static int ++qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo) ++{ ++ u16 *cached_lo = &priv->mdio_cache.lo; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (lo == *cached_lo) ++ return 0; ++ ++ ret = bus->write(bus, phy_id, regnum, lo); ++ if (ret < 0) ++ dev_err_ratelimited(&bus->dev, ++ "failed to write qca8k 32bit lo register\n"); ++ ++ *cached_lo = lo; ++ return 0; ++} ++ ++static int ++qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi) ++{ ++ u16 *cached_hi = &priv->mdio_cache.hi; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (hi == *cached_hi) ++ return 0; ++ ++ ret = bus->write(bus, phy_id, regnum, hi); ++ if (ret < 0) ++ dev_err_ratelimited(&bus->dev, ++ "failed to write qca8k 32bit hi register\n"); ++ ++ *cached_hi = hi; ++ return 0; ++} ++ ++static int ++qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) ++{ ++ int ret; ++ ++ ret = bus->read(bus, phy_id, regnum); ++ if (ret >= 0) { ++ *val = ret; ++ ret = bus->read(bus, phy_id, regnum + 1); ++ *val |= ret << 16; ++ } ++ ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to read qca8k 32bit register\n"); ++ *val = 0; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) ++{ ++ u16 lo, hi; ++ int ret; ++ ++ lo = val & 0xffff; ++ hi = (u16)(val >> 16); ++ ++ ret = qca8k_set_lo(priv, phy_id, regnum, lo); ++ if (ret >= 0) ++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); ++} ++ ++static int ++qca8k_set_page(struct qca8k_priv *priv, u16 page) ++{ ++ u16 *cached_page = &priv->mdio_cache.page; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (page == *cached_page) ++ return 0; ++ ++ ret = bus->write(bus, 0x18, 0, page); ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to set qca8k page\n"); ++ return ret; ++ } ++ ++ *cached_page = page; ++ usleep_range(1000, 2000); ++ return 0; ++} ++ ++static int ++qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) ++{ ++ return regmap_read(priv->regmap, reg, val); ++} ++ ++static int ++qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) ++{ ++ return regmap_write(priv->regmap, reg, val); ++} ++ ++static int ++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++{ ++ return regmap_update_bits(priv->regmap, reg, mask, write_val); ++} ++ ++static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ u8 len, cmd; ++ ++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); ++ mgmt_eth_data = &priv->mgmt_eth_data; ++ ++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); ++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); ++ ++ /* Make sure the seq match the requested packet */ ++ if (mgmt_ethhdr->seq == mgmt_eth_data->seq) ++ mgmt_eth_data->ack = true; ++ ++ if (cmd == MDIO_READ) { ++ mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; ++ ++ /* Get the rest of the 12 byte of data. ++ * The read/write function will extract the requested data. ++ */ ++ if (len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(mgmt_eth_data->data + 1, skb->data, ++ QCA_HDR_MGMT_DATA2_LEN); ++ } ++ ++ complete(&mgmt_eth_data->rw_done); ++} ++ ++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, ++ int priority, unsigned int len) ++{ ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ unsigned int real_len; ++ struct sk_buff *skb; ++ u32 *data2; ++ u16 hdr; ++ ++ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); ++ if (!skb) ++ return NULL; ++ ++ /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte ++ * Actually for some reason the steps are: ++ * 0: nothing ++ * 1-4: first 4 byte ++ * 5-6: first 12 byte ++ * 7-15: all 16 byte ++ */ ++ if (len == 16) ++ real_len = 15; ++ else ++ real_len = len; ++ ++ skb_reset_mac_header(skb); ++ skb_set_network_header(skb, skb->len); ++ ++ mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); ++ ++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority); ++ hdr |= QCA_HDR_XMIT_FROM_CPU; ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); ++ ++ mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, ++ QCA_HDR_MGMT_CHECK_CODE_VAL); ++ ++ if (cmd == MDIO_WRITE) ++ mgmt_ethhdr->mdio_data = *val; ++ ++ mgmt_ethhdr->hdr = htons(hdr); ++ ++ data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); ++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); ++ ++ return skb; ++} ++ ++static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) ++{ ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ ++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; ++ mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); ++} ++ ++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; ++ struct sk_buff *skb; ++ bool ack; ++ int ret; ++ ++ skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check mgmt_master if is operational */ ++ if (!priv->mgmt_master) { ++ kfree_skb(skb); ++ mutex_unlock(&mgmt_eth_data->mutex); ++ return -EINVAL; ++ } ++ ++ skb->dev = priv->mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the mdio pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); ++ ++ *val = mgmt_eth_data->data[0]; ++ if (len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN); ++ ++ ack = mgmt_eth_data->ack; ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; ++ struct sk_buff *skb; ++ bool ack; ++ int ret; ++ ++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val, ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check mgmt_master if is operational */ ++ if (!priv->mgmt_master) { ++ kfree_skb(skb); ++ mutex_unlock(&mgmt_eth_data->mutex); ++ return -EINVAL; ++ } ++ ++ skb->dev = priv->mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the mdio pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); ++ ++ ack = mgmt_eth_data->ack; ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ++qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++{ ++ u32 val = 0; ++ int ret; ++ ++ ret = qca8k_read_eth(priv, reg, &val, sizeof(val)); ++ if (ret) ++ return ret; ++ ++ val &= ~mask; ++ val |= write_val; ++ ++ return qca8k_write_eth(priv, reg, &val, sizeof(val)); ++} ++ ++static int ++qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ ++ if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ u32 tmp; ++ ++ if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ tmp = val[i]; ++ ++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ int ret; ++ ++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ int ret; ++ ++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ if (ret < 0) ++ goto exit; ++ ++ val &= ~mask; ++ val |= write_val; ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static const struct regmap_range qca8k_readable_ranges[] = { ++ regmap_reg_range(0x0000, 0x00e4), /* Global control */ ++ regmap_reg_range(0x0100, 0x0168), /* EEE control */ ++ regmap_reg_range(0x0200, 0x0270), /* Parser control */ ++ regmap_reg_range(0x0400, 0x0454), /* ACL */ ++ regmap_reg_range(0x0600, 0x0718), /* Lookup */ ++ regmap_reg_range(0x0800, 0x0b70), /* QM */ ++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */ ++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */ ++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ ++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ ++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ ++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ ++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ ++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ ++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ ++ ++}; ++ ++static const struct regmap_access_table qca8k_readable_table = { ++ .yes_ranges = qca8k_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), ++}; ++ ++static struct regmap_config qca8k_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x16ac, /* end MIB - Port6 range */ ++ .reg_read = qca8k_regmap_read, ++ .reg_write = qca8k_regmap_write, ++ .reg_update_bits = qca8k_regmap_update_bits, ++ .rd_table = &qca8k_readable_table, ++ .disable_locking = true, /* Locking is handled by qca8k read/write */ ++ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */ ++}; ++ ++static int ++qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++{ ++ u32 val; ++ ++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); ++} ++ ++static int ++qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) ++{ ++ u32 reg[3]; ++ int ret; ++ ++ /* load the ARL table into an array */ ++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++ if (ret) ++ return ret; ++ ++ /* vid - 83:72 */ ++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); ++ /* aging - 67:64 */ ++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); ++ /* portmask - 54:48 */ ++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); ++ /* mac - 47:0 */ ++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); ++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); ++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); ++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); ++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); ++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); ++ ++ return 0; ++} ++ ++static void ++qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, ++ u8 aging) ++{ ++ u32 reg[3] = { 0 }; ++ ++ /* vid - 83:72 */ ++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); ++ /* aging - 67:64 */ ++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); ++ /* portmask - 54:48 */ ++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); ++ /* mac - 47:0 */ ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); ++ ++ /* load the array into the ARL table */ ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++} ++ ++static int ++qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and FDB index */ ++ reg = QCA8K_ATU_FUNC_BUSY; ++ reg |= cmd; ++ if (port >= 0) { ++ reg |= QCA8K_ATU_FUNC_PORT_EN; ++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); ++ } ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_FDB_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_ATU_FUNC_FULL) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) ++{ ++ int ret; ++ ++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); ++ if (ret < 0) ++ return ret; ++ ++ return qca8k_fdb_read(priv, fdb); ++} ++ ++static int ++qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, ++ u16 vid, u8 aging) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static void ++qca8k_fdb_flush(struct qca8k_priv *priv) ++{ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int ++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_fdb_read(priv, &fdb); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule exist. Delete first */ ++ if (!fdb.aging) { ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ } ++ ++ /* Add port to fdb portmask */ ++ fdb.port_mask |= port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule doesn't exist. Why delete? */ ++ if (!fdb.aging) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ ++ /* Only port in the rule is this port. Don't re insert */ ++ if (fdb.port_mask == port_mask) ++ goto exit; ++ ++ /* Remove port from port mask */ ++ fdb.port_mask &= ~port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and VLAN index */ ++ reg = QCA8K_VTU_FUNC1_BUSY; ++ reg |= cmd; ++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_VLAN_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_VTU_FUNC1_FULL) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) ++{ ++ u32 reg; ++ int ret; ++ ++ /* ++ We do the right thing with VLAN 0 and treat it as untagged while ++ preserving the tag on egress. ++ */ ++ if (vid == 0) ++ return 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ if (untagged) ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); ++ else ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); ++ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) ++{ ++ u32 reg, mask; ++ int ret, i; ++ bool del; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); ++ ++ /* Check if we're the last member to be removed */ ++ del = true; ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); ++ ++ if ((reg & mask) != mask) { ++ del = false; ++ break; ++ } ++ } ++ ++ if (del) { ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); ++ } else { ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ } ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_mib_init(struct qca8k_priv *priv) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, ++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, ++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | ++ QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static void ++qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) ++{ ++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ /* Port 0 and 6 have no internal PHY */ ++ if (port > 0 && port < 6) ++ mask |= QCA8K_PORT_STATUS_LINK_AUTO; ++ ++ if (enable) ++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++ else ++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++} ++ ++static int ++qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, ++ struct sk_buff *read_skb, u32 *val) ++{ ++ struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL); ++ bool ack; ++ int ret; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the copy pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ *val = mgmt_eth_data->data[0]; ++ ++ return 0; ++} ++ ++static int ++qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, ++ int regnum, u16 data) ++{ ++ struct sk_buff *write_skb, *clear_skb, *read_skb; ++ struct qca8k_mgmt_eth_data *mgmt_eth_data; ++ u32 write_val, clear_val = 0, val; ++ struct net_device *mgmt_master; ++ int ret, ret1; ++ bool ack; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ mgmt_eth_data = &priv->mgmt_eth_data; ++ ++ write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum); ++ ++ if (read) { ++ write_val |= QCA8K_MDIO_MASTER_READ; ++ } else { ++ write_val |= QCA8K_MDIO_MASTER_WRITE; ++ write_val |= QCA8K_MDIO_MASTER_DATA(data); ++ } ++ ++ /* Prealloc all the needed skb before the lock */ ++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val)); ++ if (!write_skb) ++ return -ENOMEM; ++ ++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); ++ if (!clear_skb) { ++ ret = -ENOMEM; ++ goto err_clear_skb; ++ } ++ ++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); ++ if (!read_skb) { ++ ret = -ENOMEM; ++ goto err_read_skb; ++ } ++ ++ /* Actually start the request: ++ * 1. Send mdio master packet ++ * 2. Busy Wait for mdio master command ++ * 3. Get the data if we are reading ++ * 4. Reset the mdio master (even with error) ++ */ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check if mgmt_master is operational */ ++ mgmt_master = priv->mgmt_master; ++ if (!mgmt_master) { ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ret = -EINVAL; ++ goto err_mgmt_master; ++ } ++ ++ read_skb->dev = mgmt_master; ++ clear_skb->dev = mgmt_master; ++ write_skb->dev = mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the write pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(write_skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ kfree_skb(read_skb); ++ goto exit; ++ } ++ ++ if (!ack) { ++ ret = -EINVAL; ++ kfree_skb(read_skb); ++ goto exit; ++ } ++ ++ ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, ++ !(val & QCA8K_MDIO_MASTER_BUSY), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ mgmt_eth_data, read_skb, &val); ++ ++ if (ret < 0 && ret1 < 0) { ++ ret = ret1; ++ goto exit; ++ } ++ ++ if (read) { ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the read pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(read_skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ goto exit; ++ } ++ ++ if (!ack) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK; ++ } else { ++ kfree_skb(read_skb); ++ } ++exit: ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the clear pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(clear_skb); ++ ++ wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ return ret; ++ ++ /* Error handling before lock */ ++err_mgmt_master: ++ kfree_skb(read_skb); ++err_read_skb: ++ kfree_skb(clear_skb); ++err_clear_skb: ++ kfree_skb(write_skb); ++ ++ return ret; ++} ++ ++static u32 ++qca8k_port_to_phy(int port) ++{ ++ /* From Andrew Lunn: ++ * Port 0 has no internal phy. ++ * Port 1 has an internal PHY at MDIO address 0. ++ * Port 2 has an internal PHY at MDIO address 1. ++ * ... ++ * Port 5 has an internal PHY at MDIO address 4. ++ * Port 6 has no internal PHY. ++ */ ++ ++ return port - 1; ++} ++ ++static int ++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) ++{ ++ u16 r1, r2, page; ++ u32 val; ++ int ret, ret1; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ bus, 0x10 | r2, r1, &val); ++ ++ /* Check if qca8k_read has failed for a different reason ++ * before returnting -ETIMEDOUT ++ */ ++ if (ret < 0 && ret1 < 0) ++ return ret1; ++ ++ return ret; ++} ++ ++static int ++qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) ++{ ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum) | ++ QCA8K_MDIO_MASTER_DATA(data); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ ++exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static int ++qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) ++{ ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ ++exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ if (ret >= 0) ++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK; ++ ++ return ret; ++} ++ ++static int ++qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) ++{ ++ struct qca8k_priv *priv = slave_bus->priv; ++ int ret; ++ ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, false, phy, regnum, data); ++ if (!ret) ++ return 0; ++ ++ return qca8k_mdio_write(priv, phy, regnum, data); ++} ++ ++static int ++qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) ++{ ++ struct qca8k_priv *priv = slave_bus->priv; ++ int ret; ++ ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0); ++ if (ret >= 0) ++ return ret; ++ ++ ret = qca8k_mdio_read(priv, phy, regnum); ++ ++ if (ret < 0) ++ return 0xffff; ++ ++ return ret; ++} ++ ++static int ++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data); ++} ++ ++static int ++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_read(slave_bus, port, regnum); ++} ++ ++static int ++qca8k_mdio_register(struct qca8k_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct device_node *mdio; ++ struct mii_bus *bus; ++ ++ bus = devm_mdiobus_alloc(ds->dev); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->priv = (void *)priv; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); ++ bus->parent = ds->dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ds->slave_mii_bus = bus; ++ ++ /* Check if the devicetree declare the port:phy mapping */ ++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (of_device_is_available(mdio)) { ++ bus->name = "qca8k slave mii"; ++ bus->read = qca8k_internal_mdio_read; ++ bus->write = qca8k_internal_mdio_write; ++ return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ } ++ ++ /* If a mapping can't be found the legacy mapping is used, ++ * using the qca8k_port_to_phy function ++ */ ++ bus->name = "qca8k-legacy slave mii"; ++ bus->read = qca8k_legacy_mdio_read; ++ bus->write = qca8k_legacy_mdio_write; ++ return devm_mdiobus_register(priv->dev, bus); ++} ++ ++static int ++qca8k_setup_mdio_bus(struct qca8k_priv *priv) ++{ ++ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; ++ struct device_node *ports, *port; ++ phy_interface_t mode; ++ int err; ++ ++ ports = of_get_child_by_name(priv->dev->of_node, "ports"); ++ if (!ports) ++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); ++ ++ if (!ports) ++ return -EINVAL; ++ ++ for_each_available_child_of_node(ports, port) { ++ err = of_property_read_u32(port, "reg", ®); ++ if (err) { ++ of_node_put(port); ++ of_node_put(ports); ++ return err; ++ } ++ ++ if (!dsa_is_user_port(priv->ds, reg)) ++ continue; ++ ++ of_get_phy_mode(port, &mode); ++ ++ if (of_property_read_bool(port, "phy-handle") && ++ mode != PHY_INTERFACE_MODE_INTERNAL) ++ external_mdio_mask |= BIT(reg); ++ else ++ internal_mdio_mask |= BIT(reg); ++ } ++ ++ of_node_put(ports); ++ if (!external_mdio_mask && !internal_mdio_mask) { ++ dev_err(priv->dev, "no PHYs are defined.\n"); ++ return -EINVAL; ++ } ++ ++ /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through ++ * the MDIO_MASTER register also _disconnects_ the external MDC ++ * passthrough to the internal PHYs. It's not possible to use both ++ * configurations at the same time! ++ * ++ * Because this came up during the review process: ++ * If the external mdio-bus driver is capable magically disabling ++ * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's ++ * accessors for the time being, it would be possible to pull this ++ * off. ++ */ ++ if (!!external_mdio_mask && !!internal_mdio_mask) { ++ dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); ++ return -EINVAL; ++ } ++ ++ if (external_mdio_mask) { ++ /* Make sure to disable the internal mdio bus in cases ++ * a dt-overlay and driver reload changed the configuration ++ */ ++ ++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ } ++ ++ return qca8k_mdio_register(priv); ++} ++ ++static int ++qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv) ++{ ++ u32 mask = 0; ++ int ret = 0; ++ ++ /* SoC specific settings for ipq8064. ++ * If more device require this consider adding ++ * a dedicated binding. ++ */ ++ if (of_machine_is_compatible("qcom,ipq8064")) ++ mask |= QCA8K_MAC_PWR_RGMII0_1_8V; ++ ++ /* SoC specific settings for ipq8065 */ ++ if (of_machine_is_compatible("qcom,ipq8065")) ++ mask |= QCA8K_MAC_PWR_RGMII1_1_8V; ++ ++ if (mask) { ++ ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL, ++ QCA8K_MAC_PWR_RGMII0_1_8V | ++ QCA8K_MAC_PWR_RGMII1_1_8V, ++ mask); ++ } ++ ++ return ret; ++} ++ ++static int qca8k_find_cpu_port(struct dsa_switch *ds) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Find the connected cpu port. Valid port are 0 or 6 */ ++ if (dsa_is_cpu_port(ds, 0)) ++ return 0; ++ ++ dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6"); ++ ++ if (dsa_is_cpu_port(ds, 6)) ++ return 6; ++ ++ return -EINVAL; ++} ++ ++static int ++qca8k_setup_of_pws_reg(struct qca8k_priv *priv) ++{ ++ struct device_node *node = priv->dev->of_node; ++ const struct qca8k_match_data *data; ++ u32 val = 0; ++ int ret; ++ ++ /* QCA8327 require to set to the correct mode. ++ * His bigger brother QCA8328 have the 172 pin layout. ++ * Should be applied by default but we set this just to make sure. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ data = of_device_get_match_data(priv->dev); ++ ++ /* Set the correct package of 148 pin for QCA8327 */ ++ if (data->reduced_package) ++ val |= QCA8327_PWS_PACKAGE148_EN; ++ ++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN, ++ val); ++ if (ret) ++ return ret; ++ } ++ ++ if (of_property_read_bool(node, "qca,ignore-power-on-sel")) ++ val |= QCA8K_PWS_POWER_ON_SEL; ++ ++ if (of_property_read_bool(node, "qca,led-open-drain")) { ++ if (!(val & QCA8K_PWS_POWER_ON_SEL)) { ++ dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set."); ++ return -EINVAL; ++ } ++ ++ val |= QCA8K_PWS_LED_OPEN_EN_CSR; ++ } ++ ++ return qca8k_rmw(priv, QCA8K_REG_PWS, ++ QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL, ++ val); ++} ++ ++static int ++qca8k_parse_port_config(struct qca8k_priv *priv) ++{ ++ int port, cpu_port_index = -1, ret; ++ struct device_node *port_dn; ++ phy_interface_t mode; ++ struct dsa_port *dp; ++ u32 delay; ++ ++ /* We have 2 CPU port. Check them */ ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Skip every other port */ ++ if (port != 0 && port != 6) ++ continue; ++ ++ dp = dsa_to_port(priv->ds, port); ++ port_dn = dp->dn; ++ cpu_port_index++; ++ ++ if (!of_device_is_available(port_dn)) ++ continue; ++ ++ ret = of_get_phy_mode(port_dn, &mode); ++ if (ret) ++ continue; ++ ++ switch (mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_SGMII: ++ delay = 0; ++ ++ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay)) ++ /* Switch regs accept value in ns, convert ps to ns */ ++ delay = delay / 1000; ++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_TXID) ++ delay = 1; ++ ++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) { ++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); ++ delay = 3; ++ } ++ ++ priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay; ++ ++ delay = 0; ++ ++ if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay)) ++ /* Switch regs accept value in ns, convert ps to ns */ ++ delay = delay / 1000; ++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_RXID) ++ delay = 2; ++ ++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) { ++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); ++ delay = 3; ++ } ++ ++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; ++ ++ /* Skip sgmii parsing for rgmii* mode */ ++ if (mode == PHY_INTERFACE_MODE_RGMII || ++ mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_TXID || ++ mode == PHY_INTERFACE_MODE_RGMII_RXID) ++ break; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) ++ priv->ports_config.sgmii_tx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) ++ priv->ports_config.sgmii_rx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { ++ priv->ports_config.sgmii_enable_pll = true; ++ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling"); ++ priv->ports_config.sgmii_enable_pll = false; ++ } ++ ++ if (priv->switch_revision < 2) ++ dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more."); ++ } ++ ++ break; ++ default: ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_setup(struct dsa_switch *ds) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int cpu_port, ret, i; ++ u32 mask; ++ ++ cpu_port = qca8k_find_cpu_port(ds); ++ if (cpu_port < 0) { ++ dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); ++ return cpu_port; ++ } ++ ++ /* Parse CPU port config to be later used in phy_link mac_config */ ++ ret = qca8k_parse_port_config(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_mdio_bus(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_of_pws_reg(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_mac_pwr_sel(priv); ++ if (ret) ++ return ret; ++ ++ /* Make sure MAC06 is disabled */ ++ ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, ++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); ++ if (ret) { ++ dev_err(priv->dev, "failed disabling MAC06 exchange"); ++ return ret; ++ } ++ ++ /* Enable CPU Port */ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); ++ if (ret) { ++ dev_err(priv->dev, "failed enabling CPU port"); ++ return ret; ++ } ++ ++ /* Enable MIB counters */ ++ ret = qca8k_mib_init(priv); ++ if (ret) ++ dev_warn(priv->dev, "mib init failed"); ++ ++ /* Initial setup of all ports */ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ /* Disable forwarding by default on all ports */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, 0); ++ if (ret) ++ return ret; ++ ++ /* Enable QCA header mode on all cpu ports */ ++ if (dsa_is_cpu_port(ds, i)) { ++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), ++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | ++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); ++ if (ret) { ++ dev_err(priv->dev, "failed enabling QCA header mode"); ++ return ret; ++ } ++ } ++ ++ /* Disable MAC by default on all user ports */ ++ if (dsa_is_user_port(ds, i)) ++ qca8k_port_set_status(priv, i, 0); ++ } ++ ++ /* Forward all unknown frames to CPU port for Linux processing ++ * Notice that in multi-cpu config only one port should be set ++ * for igmp, unknown, multicast and broadcast packet ++ */ ++ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); ++ if (ret) ++ return ret; ++ ++ /* Setup connection between CPU port & user ports ++ * Configure specific switch configuration for ports ++ */ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ /* CPU port gets connected to all user ports of the switch */ ++ if (dsa_is_cpu_port(ds, i)) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ if (ret) ++ return ret; ++ } ++ ++ /* Individual user ports get connected to CPU port only */ ++ if (dsa_is_user_port(ds, i)) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ BIT(cpu_port)); ++ if (ret) ++ return ret; ++ ++ /* Enable ARP Auto-learning by default */ ++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; ++ ++ /* For port based vlans to work we need to set the ++ * default egress vid ++ */ ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), ++ QCA8K_EGREES_VLAN_PORT_MASK(i), ++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), ++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | ++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; ++ } ++ ++ /* The port 5 of the qca8337 have some problem in flood condition. The ++ * original legacy driver had some specific buffer and priority settings ++ * for the different port suggested by the QCA switch team. Add this ++ * missing settings to improve switch stability under load condition. ++ * This problem is limited to qca8337 and other qca8k switch are not affected. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) { ++ switch (i) { ++ /* The 2 CPU port and port 5 requires some different ++ * priority than any other ports. ++ */ ++ case 0: ++ case 5: ++ case 6: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); ++ break; ++ default: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); ++ } ++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); ++ ++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN; ++ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), ++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN, ++ mask); ++ } ++ } ++ ++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); ++ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, ++ QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, ++ mask); ++ } ++ ++ /* Setup our port MTUs to match power on defaults */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); ++ if (ret) ++ dev_warn(priv->dev, "failed setting MTU settings"); ++ ++ /* Flush the FDB table */ ++ qca8k_fdb_flush(priv); ++ ++ /* We don't have interrupts for link changes, so we need to poll */ ++ ds->pcs_poll = true; ++ ++ /* Set min a max ageing value supported */ ++ ds->ageing_time_min = 7000; ++ ds->ageing_time_max = 458745000; ++ ++ /* Set max number of LAGs supported */ ++ ds->num_lag_ids = QCA8K_NUM_LAGS; ++ ++ return 0; ++} ++ ++static void ++qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index, ++ u32 reg) ++{ ++ u32 delay, val = 0; ++ int ret; ++ ++ /* Delay can be declared in 3 different way. ++ * Mode to rgmii and internal-delay standard binding defined ++ * rgmii-id or rgmii-tx/rx phy mode set. ++ * The parse logic set a delay different than 0 only when one ++ * of the 3 different way is used. In all other case delay is ++ * not enabled. With ID or TX/RXID delay is enabled and set ++ * to the default and recommended value. ++ */ ++ if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) { ++ delay = priv->ports_config.rgmii_tx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; ++ } ++ ++ if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) { ++ delay = priv->ports_config.rgmii_rx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; ++ } ++ ++ /* Set RGMII delay based on the selected values */ ++ ret = qca8k_rmw(priv, reg, ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN, ++ val); ++ if (ret) ++ dev_err(priv->dev, "Failed to set internal delay for CPU port%d", ++ cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6); ++} ++ ++static void ++qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int cpu_port_index, ret; ++ u32 reg, val; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ return; ++ ++ reg = QCA8K_REG_PORT0_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT0; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY, nothing to do */ ++ return; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ return; ++ ++ reg = QCA8K_REG_PORT6_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT6; ++ break; ++ default: ++ dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); ++ return; ++ } ++ ++ if (port != 6 && phylink_autoneg_inband(mode)) { ++ dev_err(ds->dev, "%s: in-band negotiation unsupported\n", ++ __func__); ++ return; ++ } ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); ++ ++ /* Configure rgmii delay */ ++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); ++ ++ /* QCA8337 requires to set rgmii rx delay for all ports. ++ * This is enabled through PORT5_PAD_CTRL for all ports, ++ * rather than individual port registers. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) ++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* Enable SGMII on the port */ ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); ++ ++ /* Enable/disable SerDes auto-negotiation as necessary */ ++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val); ++ if (ret) ++ return; ++ if (phylink_autoneg_inband(mode)) ++ val &= ~QCA8K_PWS_SERDES_AEN_DIS; ++ else ++ val |= QCA8K_PWS_SERDES_AEN_DIS; ++ qca8k_write(priv, QCA8K_REG_PWS, val); ++ ++ /* Configure the SGMII parameters */ ++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); ++ if (ret) ++ return; ++ ++ val |= QCA8K_SGMII_EN_SD; ++ ++ if (priv->ports_config.sgmii_enable_pll) ++ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | ++ QCA8K_SGMII_EN_TX; ++ ++ if (dsa_is_cpu_port(ds, port)) { ++ /* CPU port, we're talking to the CPU MAC, be a PHY */ ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_PHY; ++ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_MAC; ++ } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_BASEX; ++ } ++ ++ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); ++ ++ /* From original code is reported port instability as SGMII also ++ * require delay set. Apply advised values here or take them from DT. ++ */ ++ if (state->interface == PHY_INTERFACE_MODE_SGMII) ++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); ++ ++ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and ++ * falling edge is set writing in the PORT0 PAD reg ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8327 || ++ priv->switch_id == QCA8K_ID_QCA8337) ++ reg = QCA8K_REG_PORT0_PAD_CTRL; ++ ++ val = 0; ++ ++ /* SGMII Clock phase configuration */ ++ if (priv->ports_config.sgmii_rx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; ++ ++ if (priv->ports_config.sgmii_tx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; ++ ++ if (val) ++ ret = qca8k_rmw(priv, reg, ++ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | ++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, ++ val); ++ ++ break; ++ default: ++ dev_err(ds->dev, "xMII mode %s not supported for port %d\n", ++ phy_modes(state->interface), port); ++ return; ++ } ++} ++ ++static void ++qca8k_phylink_validate(struct dsa_switch *ds, int port, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ goto unsupported; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL) ++ goto unsupported; ++ break; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ goto unsupported; ++ break; ++ default: ++unsupported: ++ linkmode_zero(supported); ++ return; ++ } ++ ++ phylink_set_port_modes(mask); ++ phylink_set(mask, Autoneg); ++ ++ phylink_set(mask, 1000baseT_Full); ++ phylink_set(mask, 10baseT_Half); ++ phylink_set(mask, 10baseT_Full); ++ phylink_set(mask, 100baseT_Half); ++ phylink_set(mask, 100baseT_Full); ++ ++ if (state->interface == PHY_INTERFACE_MODE_1000BASEX) ++ phylink_set(mask, 1000baseX_Full); ++ ++ phylink_set(mask, Pause); ++ phylink_set(mask, Asym_Pause); ++ ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); ++} ++ ++static int ++qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, ++ struct phylink_link_state *state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ int ret; ++ ++ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); ++ if (ret < 0) ++ return ret; ++ ++ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); ++ state->an_complete = state->link; ++ state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); ++ state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : ++ DUPLEX_HALF; ++ ++ switch (reg & QCA8K_PORT_STATUS_SPEED) { ++ case QCA8K_PORT_STATUS_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ break; ++ } ++ ++ state->pause = MLO_PAUSE_NONE; ++ if (reg & QCA8K_PORT_STATUS_RXFLOW) ++ state->pause |= MLO_PAUSE_RX; ++ if (reg & QCA8K_PORT_STATUS_TXFLOW) ++ state->pause |= MLO_PAUSE_TX; ++ ++ return 1; ++} ++ ++static void ++qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++} ++ ++static void ++qca8k_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 qca8k_priv *priv = ds->priv; ++ u32 reg; ++ ++ if (phylink_autoneg_inband(mode)) { ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ } else { ++ switch (speed) { ++ case SPEED_10: ++ reg = QCA8K_PORT_STATUS_SPEED_10; ++ break; ++ case SPEED_100: ++ reg = QCA8K_PORT_STATUS_SPEED_100; ++ break; ++ case SPEED_1000: ++ reg = QCA8K_PORT_STATUS_SPEED_1000; ++ break; ++ default: ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ break; ++ } ++ ++ if (duplex == DUPLEX_FULL) ++ reg |= QCA8K_PORT_STATUS_DUPLEX; ++ ++ if (rx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_RXFLOW; ++ ++ if (tx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_TXFLOW; ++ } ++ ++ reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); ++} ++ ++static void ++qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) ++{ ++ const struct qca8k_match_data *match_data; ++ struct qca8k_priv *priv = ds->priv; ++ int i; ++ ++ if (stringset != ETH_SS_STATS) ++ return; ++ ++ match_data = of_device_get_match_data(priv->dev); ++ ++ for (i = 0; i < match_data->mib_count; i++) ++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) ++{ ++ const struct qca8k_match_data *match_data; ++ struct qca8k_mib_eth_data *mib_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ const struct qca8k_mib_desc *mib; ++ struct mib_ethhdr *mib_ethhdr; ++ int i, mib_len, offset = 0; ++ u64 *data; ++ u8 port; ++ ++ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); ++ mib_eth_data = &priv->mib_eth_data; ++ ++ /* The switch autocast every port. Ignore other packet and ++ * parse only the requested one. ++ */ ++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr)); ++ if (port != mib_eth_data->req_port) ++ goto exit; ++ ++ match_data = device_get_match_data(priv->dev); ++ data = mib_eth_data->data; ++ ++ for (i = 0; i < match_data->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ ++ /* First 3 mib are present in the skb head */ ++ if (i < 3) { ++ data[i] = mib_ethhdr->data[i]; ++ continue; ++ } ++ ++ mib_len = sizeof(uint32_t); ++ ++ /* Some mib are 64 bit wide */ ++ if (mib->size == 2) ++ mib_len = sizeof(uint64_t); ++ ++ /* Copy the mib value from packet to the */ ++ memcpy(data + i, skb->data + offset, mib_len); ++ ++ /* Set the offset for the next mib */ ++ offset += mib_len; ++ } ++ ++exit: ++ /* Complete on receiving all the mib packet */ ++ if (refcount_dec_and_test(&mib_eth_data->port_parsed)) ++ complete(&mib_eth_data->rw_done); ++} ++ ++static int ++qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct qca8k_mib_eth_data *mib_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ mib_eth_data = &priv->mib_eth_data; ++ ++ mutex_lock(&mib_eth_data->mutex); ++ ++ reinit_completion(&mib_eth_data->rw_done); ++ ++ mib_eth_data->req_port = dp->index; ++ mib_eth_data->data = data; ++ refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS); ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ /* Send mib autocast request */ ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, ++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, ++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) | ++ QCA8K_MIB_BUSY); ++ ++ mutex_unlock(&priv->reg_mutex); ++ ++ if (ret) ++ goto exit; ++ ++ ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT); ++ ++exit: ++ mutex_unlock(&mib_eth_data->mutex); ++ ++ return ret; ++} ++ ++static void ++qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, ++ uint64_t *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ const struct qca8k_match_data *match_data; ++ const struct qca8k_mib_desc *mib; ++ u32 reg, i, val; ++ u32 hi = 0; ++ int ret; ++ ++ if (priv->mgmt_master && ++ qca8k_get_ethtool_stats_eth(ds, port, data) > 0) ++ return; ++ ++ match_data = of_device_get_match_data(priv->dev); ++ ++ for (i = 0; i < match_data->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ ret = qca8k_read(priv, reg, &val); ++ if (ret < 0) ++ continue; ++ ++ if (mib->size == 2) { ++ ret = qca8k_read(priv, reg + 4, &hi); ++ if (ret < 0) ++ continue; ++ } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= (u64)hi << 32; ++ } ++} ++ ++static int ++qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) ++{ ++ const struct qca8k_match_data *match_data; ++ struct qca8k_priv *priv = ds->priv; ++ ++ if (sset != ETH_SS_STATS) ++ return 0; ++ ++ match_data = of_device_get_match_data(priv->dev); ++ ++ return match_data->mib_count; ++} ++ ++static int ++qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ u32 reg; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); ++ if (ret < 0) ++ goto exit; ++ ++ if (eee->eee_enabled) ++ reg |= lpi_en; ++ else ++ reg &= ~lpi_en; ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) ++{ ++ /* Nothing to do on the port's MAC */ ++ return 0; ++} ++ ++static void ++qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 stp_state; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; ++ break; ++ case BR_STATE_LISTENING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; ++ break; ++ case BR_STATE_LEARNING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ default: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; ++ break; ++ } ++ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); ++} ++ ++static int ++qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int port_mask, cpu_port; ++ int i, ret; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ port_mask = BIT(cpu_port); ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; ++ if (dsa_to_port(ds, i)->bridge_dev != br) ++ continue; ++ /* Add this port to the portvlan mask of the other ports ++ * in the bridge ++ */ ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ if (ret) ++ return ret; ++ if (i != port) ++ port_mask |= BIT(i); ++ } ++ ++ /* Add all other ports to this ports portvlan mask */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ++ return ret; ++} ++ ++static void ++qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int cpu_port, i; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; ++ if (dsa_to_port(ds, i)->bridge_dev != br) ++ continue; ++ /* Remove this port to the portvlan mask of the other ports ++ * in the bridge ++ */ ++ regmap_clear_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ } ++ ++ /* Set the cpu port to be the only one in the portvlan mask of ++ * this port ++ */ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); ++} ++ ++static void ++qca8k_port_fast_age(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int ++qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ unsigned int secs = msecs / 1000; ++ u32 val; ++ ++ /* AGE_TIME reg is set in 7s step */ ++ val = secs / 7; ++ ++ /* Handle case with 0 as val to NOT disable ++ * learning ++ */ ++ if (!val) ++ val = 1; ++ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, ++ QCA8K_ATU_AGE_TIME(val)); ++} ++ ++static int ++qca8k_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 1); ++ priv->port_enabled_map |= BIT(port); ++ ++ if (dsa_is_user_port(ds, port)) ++ phy_support_asym_pause(phy); ++ ++ return 0; ++} ++ ++static void ++qca8k_port_disable(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++ priv->port_enabled_map &= ~BIT(port); ++} ++ ++static int ++qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or ++ * the switch panics. ++ * Turn off both cpu ports before applying the new value to prevent ++ * this. ++ */ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 0); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 0); ++ ++ /* Include L2 header / FCS length */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); ++ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 1); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 1); ++ ++ return ret; ++} ++ ++static int ++qca8k_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return QCA8K_MAX_MTU; ++} ++ ++static int ++qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid) ++{ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_add(priv, addr, port_mask, vid, ++ QCA8K_ATU_STATUS_STATIC); ++} ++ ++static int ++qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct qca8k_fdb _fdb = { 0 }; ++ int cnt = QCA8K_NUM_FDB_RECORDS; ++ bool is_static; ++ int ret = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { ++ if (!_fdb.aging) ++ break; ++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); ++ ret = cb(_fdb.mac, _fdb.vid, is_static, data); ++ if (ret) ++ break; ++ } ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++static int ++qca8k_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); ++} ++ ++static int ++qca8k_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); ++} ++ ++static int ++qca8k_port_mirror_add(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror, ++ bool ingress) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int monitor_port, ret; ++ u32 reg, val; ++ ++ /* Check for existent entry */ ++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) ++ return -EEXIST; ++ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* QCA83xx can have only one port set to mirror mode. ++ * Check that the correct port is requested and return error otherwise. ++ * When no mirror port is set, the values is set to 0xF ++ */ ++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port) ++ return -EEXIST; ++ ++ /* Set the monitor port */ ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, ++ mirror->to_local_port); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ return ret; ++ ++ if (ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_update_bits(priv->regmap, reg, val, val); ++ if (ret) ++ return ret; ++ ++ /* Track mirror port for tx and rx to decide when the ++ * mirror port has to be disabled. ++ */ ++ if (ingress) ++ priv->mirror_rx |= BIT(port); ++ else ++ priv->mirror_tx |= BIT(port); ++ ++ return 0; ++} ++ ++static void ++qca8k_port_mirror_del(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg, val; ++ int ret; ++ ++ if (mirror->ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_clear_bits(priv->regmap, reg, val); ++ if (ret) ++ goto err; ++ ++ if (mirror->ingress) ++ priv->mirror_rx &= ~BIT(port); ++ else ++ priv->mirror_tx &= ~BIT(port); ++ ++ /* No port set to send packet to mirror port. Disable mirror port */ ++ if (!priv->mirror_rx && !priv->mirror_tx) { ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ goto err; ++ } ++err: ++ dev_err(priv->dev, "Failed to del mirror port from %d", port); ++} ++ ++static int ++qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, ++ struct netlink_ext_ack *extack) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ if (vlan_filtering) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); ++ } else { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; ++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); ++ if (ret) { ++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); ++ return ret; ++ } ++ ++ if (pvid) { ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), ++ QCA8K_PORT_VLAN_CVID(vlan->vid) | ++ QCA8K_PORT_VLAN_SVID(vlan->vid)); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_del(priv, port, vlan->vid); ++ if (ret) ++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); ++ ++ return ret; ++} ++ ++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Communicate to the phy internal driver the switch revision. ++ * Based on the switch revision different values needs to be ++ * set to the dbg and mmd reg on the phy. ++ * The first 2 bit are used to communicate the switch revision ++ * to the phy driver. ++ */ ++ if (port > 0 && port < 6) ++ return priv->switch_revision; ++ ++ return 0; ++} ++ ++static enum dsa_tag_protocol ++qca8k_get_tag_protocol(struct dsa_switch *ds, int port, ++ enum dsa_tag_protocol mp) ++{ ++ return DSA_TAG_PROTO_QCA; ++} ++ ++static bool ++qca8k_lag_can_offload(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct dsa_port *dp; ++ int id, members = 0; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ if (id < 0 || id >= ds->num_lag_ids) ++ return false; ++ ++ dsa_lag_foreach_port(dp, ds->dst, lag) ++ /* Includes the port joining the LAG */ ++ members++; ++ ++ if (members > QCA8K_NUM_PORTS_FOR_LAG) ++ 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 ++qca8k_lag_setup_hash(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ bool unique_lag = true; ++ u32 hash = 0; ++ int i, id; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ switch (info->hash_type) { ++ case NETDEV_LAG_HASH_L23: ++ hash |= QCA8K_TRUNK_HASH_SIP_EN; ++ hash |= QCA8K_TRUNK_HASH_DIP_EN; ++ fallthrough; ++ case NETDEV_LAG_HASH_L2: ++ hash |= QCA8K_TRUNK_HASH_SA_EN; ++ hash |= QCA8K_TRUNK_HASH_DA_EN; ++ break; ++ default: /* We should NEVER reach this */ ++ return -EOPNOTSUPP; ++ } ++ ++ /* Check if we are the unique configured LAG */ ++ dsa_lags_foreach_id(i, ds->dst) ++ if (i != id && dsa_lag_dev(ds->dst, i)) { ++ unique_lag = false; ++ break; ++ } ++ ++ /* Hash Mode is global. Make sure the same Hash Mode ++ * is set to all the 4 possible lag. ++ * If we are the unique LAG we can set whatever hash ++ * mode we want. ++ * To change hash mode it's needed to remove all LAG ++ * and change the mode with the latest. ++ */ ++ if (unique_lag) { ++ priv->lag_hash_mode = hash; ++ } else if (priv->lag_hash_mode != hash) { ++ netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, ++ QCA8K_TRUNK_HASH_MASK, hash); ++} ++ ++static int ++qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, ++ struct net_device *lag, bool delete) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret, id, i; ++ u32 val; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ /* Read current port member */ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* Shift val to the correct trunk */ ++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); ++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; ++ if (delete) ++ val &= ~BIT(port); ++ else ++ val |= BIT(port); ++ ++ /* Update port member. With empty portmap disable trunk */ ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, ++ QCA8K_REG_GOL_TRUNK_MEMBER(id) | ++ QCA8K_REG_GOL_TRUNK_EN(id), ++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | ++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); ++ ++ /* Search empty member if adding or port on deleting */ ++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); ++ if (ret) ++ return ret; ++ ++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; ++ ++ if (delete) { ++ /* If port flagged to be disabled assume this member is ++ * empty ++ */ ++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; ++ if (val != port) ++ continue; ++ } else { ++ /* If port flagged to be enabled assume this member is ++ * already set ++ */ ++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ } ++ ++ /* We have found the member to add/remove */ ++ break; ++ } ++ ++ /* Set port in the correct port mask or disable port if in delete mode */ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), ++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | ++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); ++} ++ ++static int ++qca8k_port_lag_join(struct dsa_switch *ds, int port, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ int ret; ++ ++ if (!qca8k_lag_can_offload(ds, lag, info)) ++ return -EOPNOTSUPP; ++ ++ ret = qca8k_lag_setup_hash(ds, lag, info); ++ if (ret) ++ return ret; ++ ++ return qca8k_lag_refresh_portmap(ds, port, lag, false); ++} ++ ++static int ++qca8k_port_lag_leave(struct dsa_switch *ds, int port, ++ struct net_device *lag) ++{ ++ return qca8k_lag_refresh_portmap(ds, port, lag, true); ++} ++ ++static void ++qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, ++ bool operational) ++{ ++ struct dsa_port *dp = master->dsa_ptr; ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Ethernet MIB/MDIO is only supported for CPU port 0 */ ++ if (dp->index != 0) ++ return; ++ ++ mutex_lock(&priv->mgmt_eth_data.mutex); ++ mutex_lock(&priv->mib_eth_data.mutex); ++ ++ priv->mgmt_master = operational ? (struct net_device *)master : NULL; ++ ++ mutex_unlock(&priv->mib_eth_data.mutex); ++ mutex_unlock(&priv->mgmt_eth_data.mutex); ++} ++ ++static int qca8k_connect_tag_protocol(struct dsa_switch *ds, ++ enum dsa_tag_protocol proto) ++{ ++ struct qca_tagger_data *tagger_data; ++ ++ switch (proto) { ++ case DSA_TAG_PROTO_QCA: ++ tagger_data = ds->tagger_data; ++ ++ tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; ++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; ++ ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static const struct dsa_switch_ops qca8k_switch_ops = { ++ .get_tag_protocol = qca8k_get_tag_protocol, ++ .setup = qca8k_setup, ++ .get_strings = qca8k_get_strings, ++ .get_ethtool_stats = qca8k_get_ethtool_stats, ++ .get_sset_count = qca8k_get_sset_count, ++ .set_ageing_time = qca8k_set_ageing_time, ++ .get_mac_eee = qca8k_get_mac_eee, ++ .set_mac_eee = qca8k_set_mac_eee, ++ .port_enable = qca8k_port_enable, ++ .port_disable = qca8k_port_disable, ++ .port_change_mtu = qca8k_port_change_mtu, ++ .port_max_mtu = qca8k_port_max_mtu, ++ .port_stp_state_set = qca8k_port_stp_state_set, ++ .port_bridge_join = qca8k_port_bridge_join, ++ .port_bridge_leave = qca8k_port_bridge_leave, ++ .port_fast_age = qca8k_port_fast_age, ++ .port_fdb_add = qca8k_port_fdb_add, ++ .port_fdb_del = qca8k_port_fdb_del, ++ .port_fdb_dump = qca8k_port_fdb_dump, ++ .port_mdb_add = qca8k_port_mdb_add, ++ .port_mdb_del = qca8k_port_mdb_del, ++ .port_mirror_add = qca8k_port_mirror_add, ++ .port_mirror_del = qca8k_port_mirror_del, ++ .port_vlan_filtering = qca8k_port_vlan_filtering, ++ .port_vlan_add = qca8k_port_vlan_add, ++ .port_vlan_del = qca8k_port_vlan_del, ++ .phylink_validate = qca8k_phylink_validate, ++ .phylink_mac_link_state = qca8k_phylink_mac_link_state, ++ .phylink_mac_config = qca8k_phylink_mac_config, ++ .phylink_mac_link_down = qca8k_phylink_mac_link_down, ++ .phylink_mac_link_up = qca8k_phylink_mac_link_up, ++ .get_phy_flags = qca8k_get_phy_flags, ++ .port_lag_join = qca8k_port_lag_join, ++ .port_lag_leave = qca8k_port_lag_leave, ++ .master_state_change = qca8k_master_change, ++ .connect_tag_protocol = qca8k_connect_tag_protocol, ++}; ++ ++static int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ const struct qca8k_match_data *data; ++ u32 val; ++ u8 id; ++ int ret; ++ ++ /* get the switches ID from the compatible */ ++ data = of_device_get_match_data(priv->dev); ++ if (!data) ++ return -ENODEV; ++ ++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); ++ if (ret < 0) ++ return -ENODEV; ++ ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val); ++ if (id != data->id) { ++ dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id); ++ return -ENODEV; ++ } ++ ++ priv->switch_id = id; ++ ++ /* Save revision to communicate to the internal PHY driver */ ++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); ++ ++ return 0; ++} ++ ++static int ++qca8k_sw_probe(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv; ++ int ret; ++ ++ /* allocate the private data struct so that we can probe the switches ++ * ID register ++ */ ++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->bus = mdiodev->bus; ++ priv->dev = &mdiodev->dev; ++ ++ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", ++ GPIOD_ASIS); ++ if (IS_ERR(priv->reset_gpio)) ++ return PTR_ERR(priv->reset_gpio); ++ ++ if (priv->reset_gpio) { ++ gpiod_set_value_cansleep(priv->reset_gpio, 1); ++ /* The active low duration must be greater than 10 ms ++ * and checkpatch.pl wants 20 ms. ++ */ ++ msleep(20); ++ gpiod_set_value_cansleep(priv->reset_gpio, 0); ++ } ++ ++ /* Start by setting up the register mapping */ ++ priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv, ++ &qca8k_regmap_config); ++ if (IS_ERR(priv->regmap)) { ++ dev_err(priv->dev, "regmap initialization failed"); ++ return PTR_ERR(priv->regmap); ++ } ++ ++ priv->mdio_cache.page = 0xffff; ++ priv->mdio_cache.lo = 0xffff; ++ priv->mdio_cache.hi = 0xffff; ++ ++ /* Check the detected switch id */ ++ ret = qca8k_read_switch_id(priv); ++ if (ret) ++ return ret; ++ ++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); ++ if (!priv->ds) ++ return -ENOMEM; ++ ++ mutex_init(&priv->mgmt_eth_data.mutex); ++ init_completion(&priv->mgmt_eth_data.rw_done); ++ ++ mutex_init(&priv->mib_eth_data.mutex); ++ init_completion(&priv->mib_eth_data.rw_done); ++ ++ priv->ds->dev = &mdiodev->dev; ++ priv->ds->num_ports = QCA8K_NUM_PORTS; ++ priv->ds->priv = priv; ++ priv->ds->ops = &qca8k_switch_ops; ++ mutex_init(&priv->reg_mutex); ++ dev_set_drvdata(&mdiodev->dev, priv); ++ ++ return dsa_register_switch(priv->ds); ++} ++ ++static void ++qca8k_sw_remove(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ int i; ++ ++ if (!priv) ++ return; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) ++ qca8k_port_set_status(priv, i, 0); ++ ++ dsa_unregister_switch(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++static void qca8k_sw_shutdown(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ ++ if (!priv) ++ return; ++ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static void ++qca8k_set_pm(struct qca8k_priv *priv, int enable) ++{ ++ int port; ++ ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Do not enable on resume if the port was ++ * disabled before. ++ */ ++ if (!(priv->port_enabled_map & BIT(port))) ++ continue; ++ ++ qca8k_port_set_status(priv, port, enable); ++ } ++} ++ ++static int qca8k_suspend(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 0); ++ ++ return dsa_switch_suspend(priv->ds); ++} ++ ++static int qca8k_resume(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 1); ++ ++ return dsa_switch_resume(priv->ds); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, ++ qca8k_suspend, qca8k_resume); ++ ++static const struct qca8k_match_data qca8327 = { ++ .id = QCA8K_ID_QCA8327, ++ .reduced_package = true, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, ++}; ++ ++static const struct qca8k_match_data qca8328 = { ++ .id = QCA8K_ID_QCA8327, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, ++}; ++ ++static const struct qca8k_match_data qca833x = { ++ .id = QCA8K_ID_QCA8337, ++ .mib_count = QCA8K_QCA833X_MIB_COUNT, ++}; ++ ++static const struct of_device_id qca8k_of_match[] = { ++ { .compatible = "qca,qca8327", .data = &qca8327 }, ++ { .compatible = "qca,qca8328", .data = &qca8328 }, ++ { .compatible = "qca,qca8334", .data = &qca833x }, ++ { .compatible = "qca,qca8337", .data = &qca833x }, ++ { /* sentinel */ }, ++}; ++ ++static struct mdio_driver qca8kmdio_driver = { ++ .probe = qca8k_sw_probe, ++ .remove = qca8k_sw_remove, ++ .shutdown = qca8k_sw_shutdown, ++ .mdiodrv.driver = { ++ .name = "qca8k", ++ .of_match_table = qca8k_of_match, ++ .pm = &qca8k_pm_ops, ++ }, ++}; ++ ++mdio_module_driver(qca8kmdio_driver); ++ ++MODULE_AUTHOR("Mathieu Olivari, John Crispin "); ++MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qca8k"); +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -0,0 +1,411 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, The Linux Foundation. All rights reserved. ++ */ ++ ++#ifndef __QCA8K_H ++#define __QCA8K_H ++ ++#include ++#include ++#include ++#include ++ ++#define QCA8K_ETHERNET_MDIO_PRIORITY 7 ++#define QCA8K_ETHERNET_PHY_PRIORITY 6 ++#define QCA8K_ETHERNET_TIMEOUT 100 ++ ++#define QCA8K_NUM_PORTS 7 ++#define QCA8K_NUM_CPU_PORTS 2 ++#define QCA8K_MAX_MTU 9000 ++#define QCA8K_NUM_LAGS 4 ++#define QCA8K_NUM_PORTS_FOR_LAG 4 ++ ++#define PHY_ID_QCA8327 0x004dd034 ++#define QCA8K_ID_QCA8327 0x12 ++#define PHY_ID_QCA8337 0x004dd036 ++#define QCA8K_ID_QCA8337 0x13 ++ ++#define QCA8K_QCA832X_MIB_COUNT 39 ++#define QCA8K_QCA833X_MIB_COUNT 41 ++ ++#define QCA8K_BUSY_WAIT_TIMEOUT 2000 ++ ++#define QCA8K_NUM_FDB_RECORDS 2048 ++ ++#define QCA8K_PORT_VID_DEF 1 ++ ++/* Global control registers */ ++#define QCA8K_REG_MASK_CTRL 0x000 ++#define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0) ++#define QCA8K_MASK_CTRL_REV_ID(x) FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x) ++#define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8) ++#define QCA8K_MASK_CTRL_DEVICE_ID(x) FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x) ++#define QCA8K_REG_PORT0_PAD_CTRL 0x004 ++#define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31) ++#define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19) ++#define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18) ++#define QCA8K_REG_PORT5_PAD_CTRL 0x008 ++#define QCA8K_REG_PORT6_PAD_CTRL 0x00c ++#define QCA8K_PORT_PAD_RGMII_EN BIT(26) ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22) ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x) ++#define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20) ++#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x) ++#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25) ++#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) ++#define QCA8K_PORT_PAD_SGMII_EN BIT(7) ++#define QCA8K_REG_PWS 0x010 ++#define QCA8K_PWS_POWER_ON_SEL BIT(31) ++/* This reg is only valid for QCA832x and toggle the package ++ * type from 176 pin (by default) to 148 pin used on QCA8327 ++ */ ++#define QCA8327_PWS_PACKAGE148_EN BIT(30) ++#define QCA8K_PWS_LED_OPEN_EN_CSR BIT(24) ++#define QCA8K_PWS_SERDES_AEN_DIS BIT(7) ++#define QCA8K_REG_MODULE_EN 0x030 ++#define QCA8K_MODULE_EN_MIB BIT(0) ++#define QCA8K_REG_MIB 0x034 ++#define QCA8K_MIB_FUNC GENMASK(26, 24) ++#define QCA8K_MIB_CPU_KEEP BIT(20) ++#define QCA8K_MIB_BUSY BIT(17) ++#define QCA8K_MDIO_MASTER_CTRL 0x3c ++#define QCA8K_MDIO_MASTER_BUSY BIT(31) ++#define QCA8K_MDIO_MASTER_EN BIT(30) ++#define QCA8K_MDIO_MASTER_READ BIT(27) ++#define QCA8K_MDIO_MASTER_WRITE 0 ++#define QCA8K_MDIO_MASTER_SUP_PRE BIT(26) ++#define QCA8K_MDIO_MASTER_PHY_ADDR_MASK GENMASK(25, 21) ++#define QCA8K_MDIO_MASTER_PHY_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x) ++#define QCA8K_MDIO_MASTER_REG_ADDR_MASK GENMASK(20, 16) ++#define QCA8K_MDIO_MASTER_REG_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x) ++#define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0) ++#define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x) ++#define QCA8K_MDIO_MASTER_MAX_PORTS 5 ++#define QCA8K_MDIO_MASTER_MAX_REG 32 ++#define QCA8K_GOL_MAC_ADDR0 0x60 ++#define QCA8K_GOL_MAC_ADDR1 0x64 ++#define QCA8K_MAX_FRAME_SIZE 0x78 ++#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) ++#define QCA8K_PORT_STATUS_SPEED GENMASK(1, 0) ++#define QCA8K_PORT_STATUS_SPEED_10 0 ++#define QCA8K_PORT_STATUS_SPEED_100 0x1 ++#define QCA8K_PORT_STATUS_SPEED_1000 0x2 ++#define QCA8K_PORT_STATUS_TXMAC BIT(2) ++#define QCA8K_PORT_STATUS_RXMAC BIT(3) ++#define QCA8K_PORT_STATUS_TXFLOW BIT(4) ++#define QCA8K_PORT_STATUS_RXFLOW BIT(5) ++#define QCA8K_PORT_STATUS_DUPLEX BIT(6) ++#define QCA8K_PORT_STATUS_LINK_UP BIT(8) ++#define QCA8K_PORT_STATUS_LINK_AUTO BIT(9) ++#define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10) ++#define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12) ++#define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4)) ++#define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2) ++#define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0) ++#define QCA8K_PORT_HDR_CTRL_ALL 2 ++#define QCA8K_PORT_HDR_CTRL_MGMT 1 ++#define QCA8K_PORT_HDR_CTRL_NONE 0 ++#define QCA8K_REG_SGMII_CTRL 0x0e0 ++#define QCA8K_SGMII_EN_PLL BIT(1) ++#define QCA8K_SGMII_EN_RX BIT(2) ++#define QCA8K_SGMII_EN_TX BIT(3) ++#define QCA8K_SGMII_EN_SD BIT(4) ++#define QCA8K_SGMII_CLK125M_DELAY BIT(7) ++#define QCA8K_SGMII_MODE_CTRL_MASK GENMASK(23, 22) ++#define QCA8K_SGMII_MODE_CTRL(x) FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x) ++#define QCA8K_SGMII_MODE_CTRL_BASEX QCA8K_SGMII_MODE_CTRL(0x0) ++#define QCA8K_SGMII_MODE_CTRL_PHY QCA8K_SGMII_MODE_CTRL(0x1) ++#define QCA8K_SGMII_MODE_CTRL_MAC QCA8K_SGMII_MODE_CTRL(0x2) ++ ++/* MAC_PWR_SEL registers */ ++#define QCA8K_REG_MAC_PWR_SEL 0x0e4 ++#define QCA8K_MAC_PWR_RGMII1_1_8V BIT(18) ++#define QCA8K_MAC_PWR_RGMII0_1_8V BIT(19) ++ ++/* EEE control registers */ ++#define QCA8K_REG_EEE_CTRL 0x100 ++#define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2) ++ ++/* TRUNK_HASH_EN registers */ ++#define QCA8K_TRUNK_HASH_EN_CTRL 0x270 ++#define QCA8K_TRUNK_HASH_SIP_EN BIT(3) ++#define QCA8K_TRUNK_HASH_DIP_EN BIT(2) ++#define QCA8K_TRUNK_HASH_SA_EN BIT(1) ++#define QCA8K_TRUNK_HASH_DA_EN BIT(0) ++#define QCA8K_TRUNK_HASH_MASK GENMASK(3, 0) ++ ++/* ACL registers */ ++#define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8)) ++#define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16) ++#define QCA8K_PORT_VLAN_CVID(x) FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x) ++#define QCA8K_PORT_VLAN_SVID_MASK GENMASK(11, 0) ++#define QCA8K_PORT_VLAN_SVID(x) FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x) ++#define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8)) ++#define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470 ++#define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474 ++ ++/* Lookup registers */ ++#define QCA8K_REG_ATU_DATA0 0x600 ++#define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24) ++#define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16) ++#define QCA8K_ATU_ADDR4_MASK GENMASK(15, 8) ++#define QCA8K_ATU_ADDR5_MASK GENMASK(7, 0) ++#define QCA8K_REG_ATU_DATA1 0x604 ++#define QCA8K_ATU_PORT_MASK GENMASK(22, 16) ++#define QCA8K_ATU_ADDR0_MASK GENMASK(15, 8) ++#define QCA8K_ATU_ADDR1_MASK GENMASK(7, 0) ++#define QCA8K_REG_ATU_DATA2 0x608 ++#define QCA8K_ATU_VID_MASK GENMASK(19, 8) ++#define QCA8K_ATU_STATUS_MASK GENMASK(3, 0) ++#define QCA8K_ATU_STATUS_STATIC 0xf ++#define QCA8K_REG_ATU_FUNC 0x60c ++#define QCA8K_ATU_FUNC_BUSY BIT(31) ++#define QCA8K_ATU_FUNC_PORT_EN BIT(14) ++#define QCA8K_ATU_FUNC_MULTI_EN BIT(13) ++#define QCA8K_ATU_FUNC_FULL BIT(12) ++#define QCA8K_ATU_FUNC_PORT_MASK GENMASK(11, 8) ++#define QCA8K_REG_VTU_FUNC0 0x610 ++#define QCA8K_VTU_FUNC0_VALID BIT(20) ++#define QCA8K_VTU_FUNC0_IVL_EN BIT(19) ++/* QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(17, 4) ++ * It does contain VLAN_MODE for each port [5:4] for port0, ++ * [7:6] for port1 ... [17:16] for port6. Use virtual port ++ * define to handle this. ++ */ ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i) (4 + (_i) * 2) ++#define QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(1, 0) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i) (GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_TAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_VTU_FUNC0_EG_MODE_NOT FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3) ++#define QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i) (QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) ++#define QCA8K_REG_VTU_FUNC1 0x614 ++#define QCA8K_VTU_FUNC1_BUSY BIT(31) ++#define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16) ++#define QCA8K_VTU_FUNC1_FULL BIT(4) ++#define QCA8K_REG_ATU_CTRL 0x618 ++#define QCA8K_ATU_AGE_TIME_MASK GENMASK(15, 0) ++#define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x)) ++#define QCA8K_REG_GLOBAL_FW_CTRL0 0x620 ++#define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) ++#define QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM GENMASK(7, 4) ++#define QCA8K_REG_GLOBAL_FW_CTRL1 0x624 ++#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24) ++#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16) ++#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8) ++#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0) ++#define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc) ++#define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_MASK GENMASK(9, 8) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE QCA8K_PORT_LOOKUP_VLAN_MODE(0x0) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK QCA8K_PORT_LOOKUP_VLAN_MODE(0x1) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK QCA8K_PORT_LOOKUP_VLAN_MODE(0x2) ++#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE QCA8K_PORT_LOOKUP_VLAN_MODE(0x3) ++#define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16) ++#define QCA8K_PORT_LOOKUP_STATE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x) ++#define QCA8K_PORT_LOOKUP_STATE_DISABLED QCA8K_PORT_LOOKUP_STATE(0x0) ++#define QCA8K_PORT_LOOKUP_STATE_BLOCKING QCA8K_PORT_LOOKUP_STATE(0x1) ++#define QCA8K_PORT_LOOKUP_STATE_LISTENING QCA8K_PORT_LOOKUP_STATE(0x2) ++#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3) ++#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4) ++#define QCA8K_PORT_LOOKUP_LEARN BIT(20) ++#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25) ++ ++#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700 ++/* 4 max trunk first ++ * first 6 bit for member bitmap ++ * 7th bit is to enable trunk port ++ */ ++#define QCA8K_REG_GOL_TRUNK_SHIFT(_i) ((_i) * 8) ++#define QCA8K_REG_GOL_TRUNK_EN_MASK BIT(7) ++#define QCA8K_REG_GOL_TRUNK_EN(_i) (QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) ++#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK GENMASK(6, 0) ++#define QCA8K_REG_GOL_TRUNK_MEMBER(_i) (QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) ++/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */ ++#define QCA8K_REG_GOL_TRUNK_CTRL(_i) (0x704 + (((_i) / 2) * 4)) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK GENMASK(3, 0) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK BIT(3) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK GENMASK(2, 0) ++#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i) (((_i) / 2) * 16) ++#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i) ((_i) * 4) ++/* Complex shift: FIRST shift for port THEN shift for trunk */ ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j) (QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i)) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)) ++#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)) ++ ++#define QCA8K_REG_GLOBAL_FC_THRESH 0x800 ++#define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16) ++#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x) ++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK GENMASK(8, 0) ++#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x) ++ ++#define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK GENMASK(3, 0) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK GENMASK(7, 4) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK GENMASK(11, 8) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK GENMASK(15, 12) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK GENMASK(19, 16) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK GENMASK(23, 20) ++#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK GENMASK(29, 24) ++#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x) ++ ++#define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8) ++#define QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK GENMASK(3, 0) ++#define QCA8K_PORT_HOL_CTRL1_ING(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x) ++#define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6) ++#define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7) ++#define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8) ++#define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16) ++ ++/* Pkt edit registers */ ++#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i) (16 * ((_i) % 2)) ++#define QCA8K_EGREES_VLAN_PORT_MASK(_i) (GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i)) ++#define QCA8K_EGREES_VLAN_PORT(_i, x) ((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i)) ++#define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2))) ++ ++/* L3 registers */ ++#define QCA8K_HROUTER_CONTROL 0xe00 ++#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_M GENMASK(17, 16) ++#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_S 16 ++#define QCA8K_HROUTER_CONTROL_ARP_AGE_MODE 1 ++#define QCA8K_HROUTER_PBASED_CONTROL1 0xe08 ++#define QCA8K_HROUTER_PBASED_CONTROL2 0xe0c ++#define QCA8K_HNAT_CONTROL 0xe38 ++ ++/* MIB registers */ ++#define QCA8K_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100) ++ ++/* QCA specific MII registers */ ++#define MII_ATH_MMD_ADDR 0x0d ++#define MII_ATH_MMD_DATA 0x0e ++ ++enum { ++ QCA8K_PORT_SPEED_10M = 0, ++ QCA8K_PORT_SPEED_100M = 1, ++ QCA8K_PORT_SPEED_1000M = 2, ++ QCA8K_PORT_SPEED_ERR = 3, ++}; ++ ++enum qca8k_fdb_cmd { ++ QCA8K_FDB_FLUSH = 1, ++ QCA8K_FDB_LOAD = 2, ++ QCA8K_FDB_PURGE = 3, ++ QCA8K_FDB_FLUSH_PORT = 5, ++ QCA8K_FDB_NEXT = 6, ++ QCA8K_FDB_SEARCH = 7, ++}; ++ ++enum qca8k_vlan_cmd { ++ QCA8K_VLAN_FLUSH = 1, ++ QCA8K_VLAN_LOAD = 2, ++ QCA8K_VLAN_PURGE = 3, ++ QCA8K_VLAN_REMOVE_PORT = 4, ++ QCA8K_VLAN_NEXT = 5, ++ QCA8K_VLAN_READ = 6, ++}; ++ ++enum qca8k_mid_cmd { ++ QCA8K_MIB_FLUSH = 1, ++ QCA8K_MIB_FLUSH_PORT = 2, ++ QCA8K_MIB_CAST = 3, ++}; ++ ++struct qca8k_match_data { ++ u8 id; ++ bool reduced_package; ++ u8 mib_count; ++}; ++ ++enum { ++ QCA8K_CPU_PORT0, ++ QCA8K_CPU_PORT6, ++}; ++ ++struct qca8k_mgmt_eth_data { ++ struct completion rw_done; ++ struct mutex mutex; /* Enforce one mdio read/write at time */ ++ bool ack; ++ u32 seq; ++ u32 data[4]; ++}; ++ ++struct qca8k_mib_eth_data { ++ struct completion rw_done; ++ struct mutex mutex; /* Process one command at time */ ++ refcount_t port_parsed; /* Counter to track parsed port */ ++ u8 req_port; ++ u64 *data; /* pointer to ethtool data */ ++}; ++ ++struct qca8k_ports_config { ++ bool sgmii_rx_clk_falling_edge; ++ bool sgmii_tx_clk_falling_edge; ++ bool sgmii_enable_pll; ++ u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ ++ u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ ++}; ++ ++struct qca8k_mdio_cache { ++/* The 32bit switch registers are accessed indirectly. To achieve this we need ++ * to set the page of the register. Track the last page that was set to reduce ++ * mdio writes ++ */ ++ u16 page; ++/* lo and hi can also be cached and from Documentation we can skip one ++ * extra mdio write if lo or hi is didn't change. ++ */ ++ u16 lo; ++ u16 hi; ++}; ++ ++struct qca8k_priv { ++ u8 switch_id; ++ u8 switch_revision; ++ u8 mirror_rx; ++ u8 mirror_tx; ++ u8 lag_hash_mode; ++ /* Each bit correspond to a port. This switch can support a max of 7 port. ++ * Bit 1: port enabled. Bit 0: port disabled. ++ */ ++ u8 port_enabled_map; ++ struct qca8k_ports_config ports_config; ++ struct regmap *regmap; ++ struct mii_bus *bus; ++ struct dsa_switch *ds; ++ struct mutex reg_mutex; ++ struct device *dev; ++ struct gpio_desc *reset_gpio; ++ struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ ++ struct qca8k_mgmt_eth_data mgmt_eth_data; ++ struct qca8k_mib_eth_data mib_eth_data; ++ struct qca8k_mdio_cache mdio_cache; ++}; ++ ++struct qca8k_mib_desc { ++ unsigned int size; ++ unsigned int offset; ++ const char *name; ++}; ++ ++struct qca8k_fdb { ++ u16 vid; ++ u8 port_mask; ++ u8 aging; ++ u8 mac[6]; ++}; ++ ++#endif /* __QCA8K_H */ +--- a/drivers/net/dsa/qca8k.c ++++ /dev/null +@@ -1,3243 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Copyright (C) 2009 Felix Fietkau +- * Copyright (C) 2011-2012 Gabor Juhos +- * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. +- * Copyright (c) 2016 John Crispin +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "qca8k.h" +- +-#define MIB_DESC(_s, _o, _n) \ +- { \ +- .size = (_s), \ +- .offset = (_o), \ +- .name = (_n), \ +- } +- +-static const struct qca8k_mib_desc ar8327_mib[] = { +- MIB_DESC(1, 0x00, "RxBroad"), +- MIB_DESC(1, 0x04, "RxPause"), +- MIB_DESC(1, 0x08, "RxMulti"), +- MIB_DESC(1, 0x0c, "RxFcsErr"), +- MIB_DESC(1, 0x10, "RxAlignErr"), +- MIB_DESC(1, 0x14, "RxRunt"), +- MIB_DESC(1, 0x18, "RxFragment"), +- MIB_DESC(1, 0x1c, "Rx64Byte"), +- MIB_DESC(1, 0x20, "Rx128Byte"), +- MIB_DESC(1, 0x24, "Rx256Byte"), +- MIB_DESC(1, 0x28, "Rx512Byte"), +- MIB_DESC(1, 0x2c, "Rx1024Byte"), +- MIB_DESC(1, 0x30, "Rx1518Byte"), +- MIB_DESC(1, 0x34, "RxMaxByte"), +- MIB_DESC(1, 0x38, "RxTooLong"), +- MIB_DESC(2, 0x3c, "RxGoodByte"), +- MIB_DESC(2, 0x44, "RxBadByte"), +- MIB_DESC(1, 0x4c, "RxOverFlow"), +- MIB_DESC(1, 0x50, "Filtered"), +- MIB_DESC(1, 0x54, "TxBroad"), +- MIB_DESC(1, 0x58, "TxPause"), +- MIB_DESC(1, 0x5c, "TxMulti"), +- MIB_DESC(1, 0x60, "TxUnderRun"), +- MIB_DESC(1, 0x64, "Tx64Byte"), +- MIB_DESC(1, 0x68, "Tx128Byte"), +- MIB_DESC(1, 0x6c, "Tx256Byte"), +- MIB_DESC(1, 0x70, "Tx512Byte"), +- MIB_DESC(1, 0x74, "Tx1024Byte"), +- MIB_DESC(1, 0x78, "Tx1518Byte"), +- MIB_DESC(1, 0x7c, "TxMaxByte"), +- MIB_DESC(1, 0x80, "TxOverSize"), +- MIB_DESC(2, 0x84, "TxByte"), +- MIB_DESC(1, 0x8c, "TxCollision"), +- MIB_DESC(1, 0x90, "TxAbortCol"), +- MIB_DESC(1, 0x94, "TxMultiCol"), +- MIB_DESC(1, 0x98, "TxSingleCol"), +- MIB_DESC(1, 0x9c, "TxExcDefer"), +- MIB_DESC(1, 0xa0, "TxDefer"), +- MIB_DESC(1, 0xa4, "TxLateCol"), +- MIB_DESC(1, 0xa8, "RXUnicast"), +- MIB_DESC(1, 0xac, "TXUnicast"), +-}; +- +-static void +-qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) +-{ +- regaddr >>= 1; +- *r1 = regaddr & 0x1e; +- +- regaddr >>= 5; +- *r2 = regaddr & 0x7; +- +- regaddr >>= 3; +- *page = regaddr & 0x3ff; +-} +- +-static int +-qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo) +-{ +- u16 *cached_lo = &priv->mdio_cache.lo; +- struct mii_bus *bus = priv->bus; +- int ret; +- +- if (lo == *cached_lo) +- return 0; +- +- ret = bus->write(bus, phy_id, regnum, lo); +- if (ret < 0) +- dev_err_ratelimited(&bus->dev, +- "failed to write qca8k 32bit lo register\n"); +- +- *cached_lo = lo; +- return 0; +-} +- +-static int +-qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi) +-{ +- u16 *cached_hi = &priv->mdio_cache.hi; +- struct mii_bus *bus = priv->bus; +- int ret; +- +- if (hi == *cached_hi) +- return 0; +- +- ret = bus->write(bus, phy_id, regnum, hi); +- if (ret < 0) +- dev_err_ratelimited(&bus->dev, +- "failed to write qca8k 32bit hi register\n"); +- +- *cached_hi = hi; +- return 0; +-} +- +-static int +-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) +-{ +- int ret; +- +- ret = bus->read(bus, phy_id, regnum); +- if (ret >= 0) { +- *val = ret; +- ret = bus->read(bus, phy_id, regnum + 1); +- *val |= ret << 16; +- } +- +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to read qca8k 32bit register\n"); +- *val = 0; +- return ret; +- } +- +- return 0; +-} +- +-static void +-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) +-{ +- u16 lo, hi; +- int ret; +- +- lo = val & 0xffff; +- hi = (u16)(val >> 16); +- +- ret = qca8k_set_lo(priv, phy_id, regnum, lo); +- if (ret >= 0) +- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); +-} +- +-static int +-qca8k_set_page(struct qca8k_priv *priv, u16 page) +-{ +- u16 *cached_page = &priv->mdio_cache.page; +- struct mii_bus *bus = priv->bus; +- int ret; +- +- if (page == *cached_page) +- return 0; +- +- ret = bus->write(bus, 0x18, 0, page); +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to set qca8k page\n"); +- return ret; +- } +- +- *cached_page = page; +- usleep_range(1000, 2000); +- return 0; +-} +- +-static int +-qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) +-{ +- return regmap_read(priv->regmap, reg, val); +-} +- +-static int +-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) +-{ +- return regmap_write(priv->regmap, reg, val); +-} +- +-static int +-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +-{ +- return regmap_update_bits(priv->regmap, reg, mask, write_val); +-} +- +-static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) +-{ +- struct qca8k_mgmt_eth_data *mgmt_eth_data; +- struct qca8k_priv *priv = ds->priv; +- struct qca_mgmt_ethhdr *mgmt_ethhdr; +- u8 len, cmd; +- +- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); +- mgmt_eth_data = &priv->mgmt_eth_data; +- +- cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); +- len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); +- +- /* Make sure the seq match the requested packet */ +- if (mgmt_ethhdr->seq == mgmt_eth_data->seq) +- mgmt_eth_data->ack = true; +- +- if (cmd == MDIO_READ) { +- mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; +- +- /* Get the rest of the 12 byte of data. +- * The read/write function will extract the requested data. +- */ +- if (len > QCA_HDR_MGMT_DATA1_LEN) +- memcpy(mgmt_eth_data->data + 1, skb->data, +- QCA_HDR_MGMT_DATA2_LEN); +- } +- +- complete(&mgmt_eth_data->rw_done); +-} +- +-static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, +- int priority, unsigned int len) +-{ +- struct qca_mgmt_ethhdr *mgmt_ethhdr; +- unsigned int real_len; +- struct sk_buff *skb; +- u32 *data2; +- u16 hdr; +- +- skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); +- if (!skb) +- return NULL; +- +- /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte +- * Actually for some reason the steps are: +- * 0: nothing +- * 1-4: first 4 byte +- * 5-6: first 12 byte +- * 7-15: all 16 byte +- */ +- if (len == 16) +- real_len = 15; +- else +- real_len = len; +- +- skb_reset_mac_header(skb); +- skb_set_network_header(skb, skb->len); +- +- mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); +- +- hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); +- hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority); +- hdr |= QCA_HDR_XMIT_FROM_CPU; +- hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); +- hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); +- +- mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, +- QCA_HDR_MGMT_CHECK_CODE_VAL); +- +- if (cmd == MDIO_WRITE) +- mgmt_ethhdr->mdio_data = *val; +- +- mgmt_ethhdr->hdr = htons(hdr); +- +- data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); +- if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) +- memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); +- +- return skb; +-} +- +-static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) +-{ +- struct qca_mgmt_ethhdr *mgmt_ethhdr; +- +- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; +- mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); +-} +- +-static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; +- struct sk_buff *skb; +- bool ack; +- int ret; +- +- skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, +- QCA8K_ETHERNET_MDIO_PRIORITY, len); +- if (!skb) +- return -ENOMEM; +- +- mutex_lock(&mgmt_eth_data->mutex); +- +- /* Check mgmt_master if is operational */ +- if (!priv->mgmt_master) { +- kfree_skb(skb); +- mutex_unlock(&mgmt_eth_data->mutex); +- return -EINVAL; +- } +- +- skb->dev = priv->mgmt_master; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the mdio pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); +- +- *val = mgmt_eth_data->data[0]; +- if (len > QCA_HDR_MGMT_DATA1_LEN) +- memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN); +- +- ack = mgmt_eth_data->ack; +- +- mutex_unlock(&mgmt_eth_data->mutex); +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- return 0; +-} +- +-static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; +- struct sk_buff *skb; +- bool ack; +- int ret; +- +- skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val, +- QCA8K_ETHERNET_MDIO_PRIORITY, len); +- if (!skb) +- return -ENOMEM; +- +- mutex_lock(&mgmt_eth_data->mutex); +- +- /* Check mgmt_master if is operational */ +- if (!priv->mgmt_master) { +- kfree_skb(skb); +- mutex_unlock(&mgmt_eth_data->mutex); +- return -EINVAL; +- } +- +- skb->dev = priv->mgmt_master; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the mdio pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); +- +- ack = mgmt_eth_data->ack; +- +- mutex_unlock(&mgmt_eth_data->mutex); +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- return 0; +-} +- +-static int +-qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +-{ +- u32 val = 0; +- int ret; +- +- ret = qca8k_read_eth(priv, reg, &val, sizeof(val)); +- if (ret) +- return ret; +- +- val &= ~mask; +- val |= write_val; +- +- return qca8k_write_eth(priv, reg, &val, sizeof(val)); +-} +- +-static int +-qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- +- if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- ret = regmap_read(priv->regmap, reg + (i * 4), val + i); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int +-qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- u32 tmp; +- +- if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- tmp = val[i]; +- +- ret = regmap_write(priv->regmap, reg + (i * 4), tmp); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int +-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- int ret; +- +- if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- int ret; +- +- if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- u32 val; +- int ret; +- +- if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- if (ret < 0) +- goto exit; +- +- val &= ~mask; +- val |= write_val; +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static const struct regmap_range qca8k_readable_ranges[] = { +- regmap_reg_range(0x0000, 0x00e4), /* Global control */ +- regmap_reg_range(0x0100, 0x0168), /* EEE control */ +- regmap_reg_range(0x0200, 0x0270), /* Parser control */ +- regmap_reg_range(0x0400, 0x0454), /* ACL */ +- regmap_reg_range(0x0600, 0x0718), /* Lookup */ +- regmap_reg_range(0x0800, 0x0b70), /* QM */ +- regmap_reg_range(0x0c00, 0x0c80), /* PKT */ +- regmap_reg_range(0x0e00, 0x0e98), /* L3 */ +- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ +- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ +- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ +- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ +- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ +- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ +- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ +- +-}; +- +-static const struct regmap_access_table qca8k_readable_table = { +- .yes_ranges = qca8k_readable_ranges, +- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), +-}; +- +-static struct regmap_config qca8k_regmap_config = { +- .reg_bits = 16, +- .val_bits = 32, +- .reg_stride = 4, +- .max_register = 0x16ac, /* end MIB - Port6 range */ +- .reg_read = qca8k_regmap_read, +- .reg_write = qca8k_regmap_write, +- .reg_update_bits = qca8k_regmap_update_bits, +- .rd_table = &qca8k_readable_table, +- .disable_locking = true, /* Locking is handled by qca8k read/write */ +- .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */ +-}; +- +-static int +-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) +-{ +- u32 val; +- +- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); +-} +- +-static int +-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) +-{ +- u32 reg[3]; +- int ret; +- +- /* load the ARL table into an array */ +- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +- if (ret) +- return ret; +- +- /* vid - 83:72 */ +- fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); +- /* aging - 67:64 */ +- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); +- /* portmask - 54:48 */ +- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); +- /* mac - 47:0 */ +- fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); +- fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); +- fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); +- fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); +- fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); +- fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); +- +- return 0; +-} +- +-static void +-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, +- u8 aging) +-{ +- u32 reg[3] = { 0 }; +- +- /* vid - 83:72 */ +- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +- /* aging - 67:64 */ +- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); +- /* portmask - 54:48 */ +- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); +- /* mac - 47:0 */ +- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); +- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); +- +- /* load the array into the ARL table */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +-} +- +-static int +-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and FDB index */ +- reg = QCA8K_ATU_FUNC_BUSY; +- reg |= cmd; +- if (port >= 0) { +- reg |= QCA8K_ATU_FUNC_PORT_EN; +- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); +- } +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_FDB_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_ATU_FUNC_FULL) +- return -1; +- } +- +- return 0; +-} +- +-static int +-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) +-{ +- int ret; +- +- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); +- if (ret < 0) +- return ret; +- +- return qca8k_fdb_read(priv, fdb); +-} +- +-static int +-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, +- u16 vid, u8 aging) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static void +-qca8k_fdb_flush(struct qca8k_priv *priv) +-{ +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); +- mutex_unlock(&priv->reg_mutex); +-} +- +-static int +-qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, +- const u8 *mac, u16 vid) +-{ +- struct qca8k_fdb fdb = { 0 }; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- +- qca8k_fdb_write(priv, vid, 0, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_fdb_read(priv, &fdb); +- if (ret < 0) +- goto exit; +- +- /* Rule exist. Delete first */ +- if (!fdb.aging) { +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- if (ret) +- goto exit; +- } +- +- /* Add port to fdb portmask */ +- fdb.port_mask |= port_mask; +- +- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, +- const u8 *mac, u16 vid) +-{ +- struct qca8k_fdb fdb = { 0 }; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- +- qca8k_fdb_write(priv, vid, 0, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +- if (ret < 0) +- goto exit; +- +- /* Rule doesn't exist. Why delete? */ +- if (!fdb.aging) { +- ret = -EINVAL; +- goto exit; +- } +- +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- if (ret) +- goto exit; +- +- /* Only port in the rule is this port. Don't re insert */ +- if (fdb.port_mask == port_mask) +- goto exit; +- +- /* Remove port from port mask */ +- fdb.port_mask &= ~port_mask; +- +- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and VLAN index */ +- reg = QCA8K_VTU_FUNC1_BUSY; +- reg |= cmd; +- reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_VLAN_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_VTU_FUNC1_FULL) +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static int +-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +-{ +- u32 reg; +- int ret; +- +- /* +- We do the right thing with VLAN 0 and treat it as untagged while +- preserving the tag on egress. +- */ +- if (vid == 0) +- return 0; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- if (untagged) +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); +- else +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); +- +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) +-{ +- u32 reg, mask; +- int ret, i; +- bool del; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); +- +- /* Check if we're the last member to be removed */ +- del = true; +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); +- +- if ((reg & mask) != mask) { +- del = false; +- break; +- } +- } +- +- if (del) { +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); +- } else { +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- } +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_mib_init(struct qca8k_priv *priv) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, +- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, +- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | +- QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- if (ret) +- goto exit; +- +- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static void +-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +-{ +- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- /* Port 0 and 6 have no internal PHY */ +- if (port > 0 && port < 6) +- mask |= QCA8K_PORT_STATUS_LINK_AUTO; +- +- if (enable) +- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +- else +- regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +-} +- +-static int +-qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, +- struct sk_buff *read_skb, u32 *val) +-{ +- struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL); +- bool ack; +- int ret; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the copy pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- ack = mgmt_eth_data->ack; +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- *val = mgmt_eth_data->data[0]; +- +- return 0; +-} +- +-static int +-qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, +- int regnum, u16 data) +-{ +- struct sk_buff *write_skb, *clear_skb, *read_skb; +- struct qca8k_mgmt_eth_data *mgmt_eth_data; +- u32 write_val, clear_val = 0, val; +- struct net_device *mgmt_master; +- int ret, ret1; +- bool ack; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- mgmt_eth_data = &priv->mgmt_eth_data; +- +- write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum); +- +- if (read) { +- write_val |= QCA8K_MDIO_MASTER_READ; +- } else { +- write_val |= QCA8K_MDIO_MASTER_WRITE; +- write_val |= QCA8K_MDIO_MASTER_DATA(data); +- } +- +- /* Prealloc all the needed skb before the lock */ +- write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val, +- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val)); +- if (!write_skb) +- return -ENOMEM; +- +- clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, +- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); +- if (!clear_skb) { +- ret = -ENOMEM; +- goto err_clear_skb; +- } +- +- read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val, +- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); +- if (!read_skb) { +- ret = -ENOMEM; +- goto err_read_skb; +- } +- +- /* Actually start the request: +- * 1. Send mdio master packet +- * 2. Busy Wait for mdio master command +- * 3. Get the data if we are reading +- * 4. Reset the mdio master (even with error) +- */ +- mutex_lock(&mgmt_eth_data->mutex); +- +- /* Check if mgmt_master is operational */ +- mgmt_master = priv->mgmt_master; +- if (!mgmt_master) { +- mutex_unlock(&mgmt_eth_data->mutex); +- ret = -EINVAL; +- goto err_mgmt_master; +- } +- +- read_skb->dev = mgmt_master; +- clear_skb->dev = mgmt_master; +- write_skb->dev = mgmt_master; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the write pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(write_skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- ack = mgmt_eth_data->ack; +- +- if (ret <= 0) { +- ret = -ETIMEDOUT; +- kfree_skb(read_skb); +- goto exit; +- } +- +- if (!ack) { +- ret = -EINVAL; +- kfree_skb(read_skb); +- goto exit; +- } +- +- ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, +- !(val & QCA8K_MDIO_MASTER_BUSY), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- mgmt_eth_data, read_skb, &val); +- +- if (ret < 0 && ret1 < 0) { +- ret = ret1; +- goto exit; +- } +- +- if (read) { +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the read pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(read_skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- ack = mgmt_eth_data->ack; +- +- if (ret <= 0) { +- ret = -ETIMEDOUT; +- goto exit; +- } +- +- if (!ack) { +- ret = -EINVAL; +- goto exit; +- } +- +- ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK; +- } else { +- kfree_skb(read_skb); +- } +-exit: +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the clear pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(clear_skb); +- +- wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- mutex_unlock(&mgmt_eth_data->mutex); +- +- return ret; +- +- /* Error handling before lock */ +-err_mgmt_master: +- kfree_skb(read_skb); +-err_read_skb: +- kfree_skb(clear_skb); +-err_clear_skb: +- kfree_skb(write_skb); +- +- return ret; +-} +- +-static u32 +-qca8k_port_to_phy(int port) +-{ +- /* From Andrew Lunn: +- * Port 0 has no internal phy. +- * Port 1 has an internal PHY at MDIO address 0. +- * Port 2 has an internal PHY at MDIO address 1. +- * ... +- * Port 5 has an internal PHY at MDIO address 4. +- * Port 6 has no internal PHY. +- */ +- +- return port - 1; +-} +- +-static int +-qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) +-{ +- u16 r1, r2, page; +- u32 val; +- int ret, ret1; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- bus, 0x10 | r2, r1, &val); +- +- /* Check if qca8k_read has failed for a different reason +- * before returnting -ETIMEDOUT +- */ +- if (ret < 0 && ret1 < 0) +- return ret1; +- +- return ret; +-} +- +-static int +-qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) +-{ +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- u32 val; +- int ret; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum) | +- QCA8K_MDIO_MASTER_DATA(data); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); +- +-exit: +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static int +-qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) +-{ +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- u32 val; +- int ret; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); +- if (ret) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- +-exit: +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- if (ret >= 0) +- ret = val & QCA8K_MDIO_MASTER_DATA_MASK; +- +- return ret; +-} +- +-static int +-qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) +-{ +- struct qca8k_priv *priv = slave_bus->priv; +- int ret; +- +- /* Use mdio Ethernet when available, fallback to legacy one on error */ +- ret = qca8k_phy_eth_command(priv, false, phy, regnum, data); +- if (!ret) +- return 0; +- +- return qca8k_mdio_write(priv, phy, regnum, data); +-} +- +-static int +-qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) +-{ +- struct qca8k_priv *priv = slave_bus->priv; +- int ret; +- +- /* Use mdio Ethernet when available, fallback to legacy one on error */ +- ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0); +- if (ret >= 0) +- return ret; +- +- ret = qca8k_mdio_read(priv, phy, regnum); +- +- if (ret < 0) +- return 0xffff; +- +- return ret; +-} +- +-static int +-qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_write(slave_bus, port, regnum, data); +-} +- +-static int +-qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_read(slave_bus, port, regnum); +-} +- +-static int +-qca8k_mdio_register(struct qca8k_priv *priv) +-{ +- struct dsa_switch *ds = priv->ds; +- struct device_node *mdio; +- struct mii_bus *bus; +- +- bus = devm_mdiobus_alloc(ds->dev); +- if (!bus) +- return -ENOMEM; +- +- bus->priv = (void *)priv; +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", +- ds->dst->index, ds->index); +- bus->parent = ds->dev; +- bus->phy_mask = ~ds->phys_mii_mask; +- ds->slave_mii_bus = bus; +- +- /* Check if the devicetree declare the port:phy mapping */ +- mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (of_device_is_available(mdio)) { +- bus->name = "qca8k slave mii"; +- bus->read = qca8k_internal_mdio_read; +- bus->write = qca8k_internal_mdio_write; +- return devm_of_mdiobus_register(priv->dev, bus, mdio); +- } +- +- /* If a mapping can't be found the legacy mapping is used, +- * using the qca8k_port_to_phy function +- */ +- bus->name = "qca8k-legacy slave mii"; +- bus->read = qca8k_legacy_mdio_read; +- bus->write = qca8k_legacy_mdio_write; +- return devm_mdiobus_register(priv->dev, bus); +-} +- +-static int +-qca8k_setup_mdio_bus(struct qca8k_priv *priv) +-{ +- u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; +- struct device_node *ports, *port; +- phy_interface_t mode; +- int err; +- +- ports = of_get_child_by_name(priv->dev->of_node, "ports"); +- if (!ports) +- ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); +- +- if (!ports) +- return -EINVAL; +- +- for_each_available_child_of_node(ports, port) { +- err = of_property_read_u32(port, "reg", ®); +- if (err) { +- of_node_put(port); +- of_node_put(ports); +- return err; +- } +- +- if (!dsa_is_user_port(priv->ds, reg)) +- continue; +- +- of_get_phy_mode(port, &mode); +- +- if (of_property_read_bool(port, "phy-handle") && +- mode != PHY_INTERFACE_MODE_INTERNAL) +- external_mdio_mask |= BIT(reg); +- else +- internal_mdio_mask |= BIT(reg); +- } +- +- of_node_put(ports); +- if (!external_mdio_mask && !internal_mdio_mask) { +- dev_err(priv->dev, "no PHYs are defined.\n"); +- return -EINVAL; +- } +- +- /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through +- * the MDIO_MASTER register also _disconnects_ the external MDC +- * passthrough to the internal PHYs. It's not possible to use both +- * configurations at the same time! +- * +- * Because this came up during the review process: +- * If the external mdio-bus driver is capable magically disabling +- * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's +- * accessors for the time being, it would be possible to pull this +- * off. +- */ +- if (!!external_mdio_mask && !!internal_mdio_mask) { +- dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); +- return -EINVAL; +- } +- +- if (external_mdio_mask) { +- /* Make sure to disable the internal mdio bus in cases +- * a dt-overlay and driver reload changed the configuration +- */ +- +- return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- } +- +- return qca8k_mdio_register(priv); +-} +- +-static int +-qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv) +-{ +- u32 mask = 0; +- int ret = 0; +- +- /* SoC specific settings for ipq8064. +- * If more device require this consider adding +- * a dedicated binding. +- */ +- if (of_machine_is_compatible("qcom,ipq8064")) +- mask |= QCA8K_MAC_PWR_RGMII0_1_8V; +- +- /* SoC specific settings for ipq8065 */ +- if (of_machine_is_compatible("qcom,ipq8065")) +- mask |= QCA8K_MAC_PWR_RGMII1_1_8V; +- +- if (mask) { +- ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL, +- QCA8K_MAC_PWR_RGMII0_1_8V | +- QCA8K_MAC_PWR_RGMII1_1_8V, +- mask); +- } +- +- return ret; +-} +- +-static int qca8k_find_cpu_port(struct dsa_switch *ds) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- /* Find the connected cpu port. Valid port are 0 or 6 */ +- if (dsa_is_cpu_port(ds, 0)) +- return 0; +- +- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6"); +- +- if (dsa_is_cpu_port(ds, 6)) +- return 6; +- +- return -EINVAL; +-} +- +-static int +-qca8k_setup_of_pws_reg(struct qca8k_priv *priv) +-{ +- struct device_node *node = priv->dev->of_node; +- const struct qca8k_match_data *data; +- u32 val = 0; +- int ret; +- +- /* QCA8327 require to set to the correct mode. +- * His bigger brother QCA8328 have the 172 pin layout. +- * Should be applied by default but we set this just to make sure. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- data = of_device_get_match_data(priv->dev); +- +- /* Set the correct package of 148 pin for QCA8327 */ +- if (data->reduced_package) +- val |= QCA8327_PWS_PACKAGE148_EN; +- +- ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN, +- val); +- if (ret) +- return ret; +- } +- +- if (of_property_read_bool(node, "qca,ignore-power-on-sel")) +- val |= QCA8K_PWS_POWER_ON_SEL; +- +- if (of_property_read_bool(node, "qca,led-open-drain")) { +- if (!(val & QCA8K_PWS_POWER_ON_SEL)) { +- dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set."); +- return -EINVAL; +- } +- +- val |= QCA8K_PWS_LED_OPEN_EN_CSR; +- } +- +- return qca8k_rmw(priv, QCA8K_REG_PWS, +- QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL, +- val); +-} +- +-static int +-qca8k_parse_port_config(struct qca8k_priv *priv) +-{ +- int port, cpu_port_index = -1, ret; +- struct device_node *port_dn; +- phy_interface_t mode; +- struct dsa_port *dp; +- u32 delay; +- +- /* We have 2 CPU port. Check them */ +- for (port = 0; port < QCA8K_NUM_PORTS; port++) { +- /* Skip every other port */ +- if (port != 0 && port != 6) +- continue; +- +- dp = dsa_to_port(priv->ds, port); +- port_dn = dp->dn; +- cpu_port_index++; +- +- if (!of_device_is_available(port_dn)) +- continue; +- +- ret = of_get_phy_mode(port_dn, &mode); +- if (ret) +- continue; +- +- switch (mode) { +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- case PHY_INTERFACE_MODE_SGMII: +- delay = 0; +- +- if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay)) +- /* Switch regs accept value in ns, convert ps to ns */ +- delay = delay / 1000; +- else if (mode == PHY_INTERFACE_MODE_RGMII_ID || +- mode == PHY_INTERFACE_MODE_RGMII_TXID) +- delay = 1; +- +- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) { +- dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); +- delay = 3; +- } +- +- priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay; +- +- delay = 0; +- +- if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay)) +- /* Switch regs accept value in ns, convert ps to ns */ +- delay = delay / 1000; +- else if (mode == PHY_INTERFACE_MODE_RGMII_ID || +- mode == PHY_INTERFACE_MODE_RGMII_RXID) +- delay = 2; +- +- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) { +- dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); +- delay = 3; +- } +- +- priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; +- +- /* Skip sgmii parsing for rgmii* mode */ +- if (mode == PHY_INTERFACE_MODE_RGMII || +- mode == PHY_INTERFACE_MODE_RGMII_ID || +- mode == PHY_INTERFACE_MODE_RGMII_TXID || +- mode == PHY_INTERFACE_MODE_RGMII_RXID) +- break; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) +- priv->ports_config.sgmii_tx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) +- priv->ports_config.sgmii_rx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { +- priv->ports_config.sgmii_enable_pll = true; +- +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling"); +- priv->ports_config.sgmii_enable_pll = false; +- } +- +- if (priv->switch_revision < 2) +- dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more."); +- } +- +- break; +- default: +- continue; +- } +- } +- +- return 0; +-} +- +-static int +-qca8k_setup(struct dsa_switch *ds) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, ret, i; +- u32 mask; +- +- cpu_port = qca8k_find_cpu_port(ds); +- if (cpu_port < 0) { +- dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); +- return cpu_port; +- } +- +- /* Parse CPU port config to be later used in phy_link mac_config */ +- ret = qca8k_parse_port_config(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_mdio_bus(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_of_pws_reg(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_mac_pwr_sel(priv); +- if (ret) +- return ret; +- +- /* Make sure MAC06 is disabled */ +- ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, +- QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); +- if (ret) { +- dev_err(priv->dev, "failed disabling MAC06 exchange"); +- return ret; +- } +- +- /* Enable CPU Port */ +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); +- if (ret) { +- dev_err(priv->dev, "failed enabling CPU port"); +- return ret; +- } +- +- /* Enable MIB counters */ +- ret = qca8k_mib_init(priv); +- if (ret) +- dev_warn(priv->dev, "mib init failed"); +- +- /* Initial setup of all ports */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- /* Disable forwarding by default on all ports */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, 0); +- if (ret) +- return ret; +- +- /* Enable QCA header mode on all cpu ports */ +- if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), +- FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | +- FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); +- if (ret) { +- dev_err(priv->dev, "failed enabling QCA header mode"); +- return ret; +- } +- } +- +- /* Disable MAC by default on all user ports */ +- if (dsa_is_user_port(ds, i)) +- qca8k_port_set_status(priv, i, 0); +- } +- +- /* Forward all unknown frames to CPU port for Linux processing +- * Notice that in multi-cpu config only one port should be set +- * for igmp, unknown, multicast and broadcast packet +- */ +- ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); +- if (ret) +- return ret; +- +- /* Setup connection between CPU port & user ports +- * Configure specific switch configuration for ports +- */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- /* CPU port gets connected to all user ports of the switch */ +- if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); +- if (ret) +- return ret; +- } +- +- /* Individual user ports get connected to CPU port only */ +- if (dsa_is_user_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, +- BIT(cpu_port)); +- if (ret) +- return ret; +- +- /* Enable ARP Auto-learning by default */ +- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); +- if (ret) +- return ret; +- +- /* For port based vlans to work we need to set the +- * default egress vid +- */ +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- QCA8K_EGREES_VLAN_PORT_MASK(i), +- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), +- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | +- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- } +- +- /* The port 5 of the qca8337 have some problem in flood condition. The +- * original legacy driver had some specific buffer and priority settings +- * for the different port suggested by the QCA switch team. Add this +- * missing settings to improve switch stability under load condition. +- * This problem is limited to qca8337 and other qca8k switch are not affected. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8337) { +- switch (i) { +- /* The 2 CPU port and port 5 requires some different +- * priority than any other ports. +- */ +- case 0: +- case 5: +- case 6: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); +- break; +- default: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); +- } +- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); +- +- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN; +- qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), +- QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN, +- mask); +- } +- } +- +- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | +- QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); +- qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, +- QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | +- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, +- mask); +- } +- +- /* Setup our port MTUs to match power on defaults */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); +- if (ret) +- dev_warn(priv->dev, "failed setting MTU settings"); +- +- /* Flush the FDB table */ +- qca8k_fdb_flush(priv); +- +- /* We don't have interrupts for link changes, so we need to poll */ +- ds->pcs_poll = true; +- +- /* Set min a max ageing value supported */ +- ds->ageing_time_min = 7000; +- ds->ageing_time_max = 458745000; +- +- /* Set max number of LAGs supported */ +- ds->num_lag_ids = QCA8K_NUM_LAGS; +- +- return 0; +-} +- +-static void +-qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index, +- u32 reg) +-{ +- u32 delay, val = 0; +- int ret; +- +- /* Delay can be declared in 3 different way. +- * Mode to rgmii and internal-delay standard binding defined +- * rgmii-id or rgmii-tx/rx phy mode set. +- * The parse logic set a delay different than 0 only when one +- * of the 3 different way is used. In all other case delay is +- * not enabled. With ID or TX/RXID delay is enabled and set +- * to the default and recommended value. +- */ +- if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) { +- delay = priv->ports_config.rgmii_tx_delay[cpu_port_index]; +- +- val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | +- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; +- } +- +- if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) { +- delay = priv->ports_config.rgmii_rx_delay[cpu_port_index]; +- +- val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; +- } +- +- /* Set RGMII delay based on the selected values */ +- ret = qca8k_rmw(priv, reg, +- QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK | +- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN, +- val); +- if (ret) +- dev_err(priv->dev, "Failed to set internal delay for CPU port%d", +- cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6); +-} +- +-static void +-qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +- const struct phylink_link_state *state) +-{ +- struct qca8k_priv *priv = ds->priv; +- int cpu_port_index, ret; +- u32 reg, val; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- return; +- +- reg = QCA8K_REG_PORT0_PAD_CTRL; +- cpu_port_index = QCA8K_CPU_PORT0; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY, nothing to do */ +- return; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- return; +- +- reg = QCA8K_REG_PORT6_PAD_CTRL; +- cpu_port_index = QCA8K_CPU_PORT6; +- break; +- default: +- dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); +- return; +- } +- +- if (port != 6 && phylink_autoneg_inband(mode)) { +- dev_err(ds->dev, "%s: in-band negotiation unsupported\n", +- __func__); +- return; +- } +- +- switch (state->interface) { +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); +- +- /* Configure rgmii delay */ +- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); +- +- /* QCA8337 requires to set rgmii rx delay for all ports. +- * This is enabled through PORT5_PAD_CTRL for all ports, +- * rather than individual port registers. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8337) +- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); +- break; +- case PHY_INTERFACE_MODE_SGMII: +- case PHY_INTERFACE_MODE_1000BASEX: +- /* Enable SGMII on the port */ +- qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); +- +- /* Enable/disable SerDes auto-negotiation as necessary */ +- ret = qca8k_read(priv, QCA8K_REG_PWS, &val); +- if (ret) +- return; +- if (phylink_autoneg_inband(mode)) +- val &= ~QCA8K_PWS_SERDES_AEN_DIS; +- else +- val |= QCA8K_PWS_SERDES_AEN_DIS; +- qca8k_write(priv, QCA8K_REG_PWS, val); +- +- /* Configure the SGMII parameters */ +- ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); +- if (ret) +- return; +- +- val |= QCA8K_SGMII_EN_SD; +- +- if (priv->ports_config.sgmii_enable_pll) +- val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | +- QCA8K_SGMII_EN_TX; +- +- if (dsa_is_cpu_port(ds, port)) { +- /* CPU port, we're talking to the CPU MAC, be a PHY */ +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_PHY; +- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_MAC; +- } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_BASEX; +- } +- +- qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); +- +- /* From original code is reported port instability as SGMII also +- * require delay set. Apply advised values here or take them from DT. +- */ +- if (state->interface == PHY_INTERFACE_MODE_SGMII) +- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); +- +- /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and +- * falling edge is set writing in the PORT0 PAD reg +- */ +- if (priv->switch_id == QCA8K_ID_QCA8327 || +- priv->switch_id == QCA8K_ID_QCA8337) +- reg = QCA8K_REG_PORT0_PAD_CTRL; +- +- val = 0; +- +- /* SGMII Clock phase configuration */ +- if (priv->ports_config.sgmii_rx_clk_falling_edge) +- val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; +- +- if (priv->ports_config.sgmii_tx_clk_falling_edge) +- val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; +- +- if (val) +- ret = qca8k_rmw(priv, reg, +- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | +- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, +- val); +- +- break; +- default: +- dev_err(ds->dev, "xMII mode %s not supported for port %d\n", +- phy_modes(state->interface), port); +- return; +- } +-} +- +-static void +-qca8k_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- goto unsupported; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_GMII && +- state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; +- break; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- goto unsupported; +- break; +- default: +-unsupported: +- linkmode_zero(supported); +- return; +- } +- +- phylink_set_port_modes(mask); +- phylink_set(mask, Autoneg); +- +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- +- if (state->interface == PHY_INTERFACE_MODE_1000BASEX) +- phylink_set(mask, 1000baseX_Full); +- +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); +-} +- +-static int +-qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, +- struct phylink_link_state *state) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg; +- int ret; +- +- ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); +- if (ret < 0) +- return ret; +- +- state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); +- state->an_complete = state->link; +- state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); +- state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : +- DUPLEX_HALF; +- +- switch (reg & QCA8K_PORT_STATUS_SPEED) { +- case QCA8K_PORT_STATUS_SPEED_10: +- state->speed = SPEED_10; +- break; +- case QCA8K_PORT_STATUS_SPEED_100: +- state->speed = SPEED_100; +- break; +- case QCA8K_PORT_STATUS_SPEED_1000: +- state->speed = SPEED_1000; +- break; +- default: +- state->speed = SPEED_UNKNOWN; +- break; +- } +- +- state->pause = MLO_PAUSE_NONE; +- if (reg & QCA8K_PORT_STATUS_RXFLOW) +- state->pause |= MLO_PAUSE_RX; +- if (reg & QCA8K_PORT_STATUS_TXFLOW) +- state->pause |= MLO_PAUSE_TX; +- +- return 1; +-} +- +-static void +-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, +- phy_interface_t interface) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +-} +- +-static void +-qca8k_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 qca8k_priv *priv = ds->priv; +- u32 reg; +- +- if (phylink_autoneg_inband(mode)) { +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- } else { +- switch (speed) { +- case SPEED_10: +- reg = QCA8K_PORT_STATUS_SPEED_10; +- break; +- case SPEED_100: +- reg = QCA8K_PORT_STATUS_SPEED_100; +- break; +- case SPEED_1000: +- reg = QCA8K_PORT_STATUS_SPEED_1000; +- break; +- default: +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- break; +- } +- +- if (duplex == DUPLEX_FULL) +- reg |= QCA8K_PORT_STATUS_DUPLEX; +- +- if (rx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_RXFLOW; +- +- if (tx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_TXFLOW; +- } +- +- reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); +-} +- +-static void +-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) +-{ +- const struct qca8k_match_data *match_data; +- struct qca8k_priv *priv = ds->priv; +- int i; +- +- if (stringset != ETH_SS_STATS) +- return; +- +- match_data = of_device_get_match_data(priv->dev); +- +- for (i = 0; i < match_data->mib_count; i++) +- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, +- ETH_GSTRING_LEN); +-} +- +-static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) +-{ +- const struct qca8k_match_data *match_data; +- struct qca8k_mib_eth_data *mib_eth_data; +- struct qca8k_priv *priv = ds->priv; +- const struct qca8k_mib_desc *mib; +- struct mib_ethhdr *mib_ethhdr; +- int i, mib_len, offset = 0; +- u64 *data; +- u8 port; +- +- mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); +- mib_eth_data = &priv->mib_eth_data; +- +- /* The switch autocast every port. Ignore other packet and +- * parse only the requested one. +- */ +- port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr)); +- if (port != mib_eth_data->req_port) +- goto exit; +- +- match_data = device_get_match_data(priv->dev); +- data = mib_eth_data->data; +- +- for (i = 0; i < match_data->mib_count; i++) { +- mib = &ar8327_mib[i]; +- +- /* First 3 mib are present in the skb head */ +- if (i < 3) { +- data[i] = mib_ethhdr->data[i]; +- continue; +- } +- +- mib_len = sizeof(uint32_t); +- +- /* Some mib are 64 bit wide */ +- if (mib->size == 2) +- mib_len = sizeof(uint64_t); +- +- /* Copy the mib value from packet to the */ +- memcpy(data + i, skb->data + offset, mib_len); +- +- /* Set the offset for the next mib */ +- offset += mib_len; +- } +- +-exit: +- /* Complete on receiving all the mib packet */ +- if (refcount_dec_and_test(&mib_eth_data->port_parsed)) +- complete(&mib_eth_data->rw_done); +-} +- +-static int +-qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data) +-{ +- struct dsa_port *dp = dsa_to_port(ds, port); +- struct qca8k_mib_eth_data *mib_eth_data; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- mib_eth_data = &priv->mib_eth_data; +- +- mutex_lock(&mib_eth_data->mutex); +- +- reinit_completion(&mib_eth_data->rw_done); +- +- mib_eth_data->req_port = dp->index; +- mib_eth_data->data = data; +- refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS); +- +- mutex_lock(&priv->reg_mutex); +- +- /* Send mib autocast request */ +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, +- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, +- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) | +- QCA8K_MIB_BUSY); +- +- mutex_unlock(&priv->reg_mutex); +- +- if (ret) +- goto exit; +- +- ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT); +- +-exit: +- mutex_unlock(&mib_eth_data->mutex); +- +- return ret; +-} +- +-static void +-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, +- uint64_t *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- const struct qca8k_match_data *match_data; +- const struct qca8k_mib_desc *mib; +- u32 reg, i, val; +- u32 hi = 0; +- int ret; +- +- if (priv->mgmt_master && +- qca8k_get_ethtool_stats_eth(ds, port, data) > 0) +- return; +- +- match_data = of_device_get_match_data(priv->dev); +- +- for (i = 0; i < match_data->mib_count; i++) { +- mib = &ar8327_mib[i]; +- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; +- +- ret = qca8k_read(priv, reg, &val); +- if (ret < 0) +- continue; +- +- if (mib->size == 2) { +- ret = qca8k_read(priv, reg + 4, &hi); +- if (ret < 0) +- continue; +- } +- +- data[i] = val; +- if (mib->size == 2) +- data[i] |= (u64)hi << 32; +- } +-} +- +-static int +-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) +-{ +- const struct qca8k_match_data *match_data; +- struct qca8k_priv *priv = ds->priv; +- +- if (sset != ETH_SS_STATS) +- return 0; +- +- match_data = of_device_get_match_data(priv->dev); +- +- return match_data->mib_count; +-} +- +-static int +-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- u32 reg; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); +- if (ret < 0) +- goto exit; +- +- if (eee->eee_enabled) +- reg |= lpi_en; +- else +- reg &= ~lpi_en; +- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +-{ +- /* Nothing to do on the port's MAC */ +- return 0; +-} +- +-static void +-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 stp_state; +- +- switch (state) { +- case BR_STATE_DISABLED: +- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; +- break; +- case BR_STATE_BLOCKING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; +- break; +- case BR_STATE_LISTENING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; +- break; +- case BR_STATE_LEARNING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; +- break; +- case BR_STATE_FORWARDING: +- default: +- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; +- break; +- } +- +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); +-} +- +-static int +-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int port_mask, cpu_port; +- int i, ret; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- port_mask = BIT(cpu_port); +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (dsa_to_port(ds, i)->bridge_dev != br) +- continue; +- /* Add this port to the portvlan mask of the other ports +- * in the bridge +- */ +- ret = regmap_set_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- if (ret) +- return ret; +- if (i != port) +- port_mask |= BIT(i); +- } +- +- /* Add all other ports to this ports portvlan mask */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); +- +- return ret; +-} +- +-static void +-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, i; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (dsa_to_port(ds, i)->bridge_dev != br) +- continue; +- /* Remove this port to the portvlan mask of the other ports +- * in the bridge +- */ +- regmap_clear_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- } +- +- /* Set the cpu port to be the only one in the portvlan mask of +- * this port +- */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); +-} +- +-static void +-qca8k_port_fast_age(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); +- mutex_unlock(&priv->reg_mutex); +-} +- +-static int +-qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +-{ +- struct qca8k_priv *priv = ds->priv; +- unsigned int secs = msecs / 1000; +- u32 val; +- +- /* AGE_TIME reg is set in 7s step */ +- val = secs / 7; +- +- /* Handle case with 0 as val to NOT disable +- * learning +- */ +- if (!val) +- val = 1; +- +- return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, +- QCA8K_ATU_AGE_TIME(val)); +-} +- +-static int +-qca8k_port_enable(struct dsa_switch *ds, int port, +- struct phy_device *phy) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 1); +- priv->port_enabled_map |= BIT(port); +- +- if (dsa_is_user_port(ds, port)) +- phy_support_asym_pause(phy); +- +- return 0; +-} +- +-static void +-qca8k_port_disable(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +- priv->port_enabled_map &= ~BIT(port); +-} +- +-static int +-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- /* We have only have a general MTU setting. +- * DSA always set the CPU port's MTU to the largest MTU of the slave +- * ports. +- * Setting MTU just for the CPU port is sufficient to correctly set a +- * value for every port. +- */ +- if (!dsa_is_cpu_port(ds, port)) +- return 0; +- +- /* To change the MAX_FRAME_SIZE the cpu ports must be off or +- * the switch panics. +- * Turn off both cpu ports before applying the new value to prevent +- * this. +- */ +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 0); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 0); +- +- /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); +- +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 1); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 1); +- +- return ret; +-} +- +-static int +-qca8k_port_max_mtu(struct dsa_switch *ds, int port) +-{ +- return QCA8K_MAX_MTU; +-} +- +-static int +-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, +- u16 port_mask, u16 vid) +-{ +- /* Set the vid to the port vlan id if no vid is set */ +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_add(priv, addr, port_mask, vid, +- QCA8K_ATU_STATUS_STATIC); +-} +- +-static int +-qca8k_port_fdb_add(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- return qca8k_port_fdb_insert(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_del(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_del(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_dump(struct dsa_switch *ds, int port, +- dsa_fdb_dump_cb_t *cb, void *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- struct qca8k_fdb _fdb = { 0 }; +- int cnt = QCA8K_NUM_FDB_RECORDS; +- bool is_static; +- int ret = 0; +- +- mutex_lock(&priv->reg_mutex); +- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { +- if (!_fdb.aging) +- break; +- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); +- ret = cb(_fdb.mac, _fdb.vid, is_static, data); +- if (ret) +- break; +- } +- mutex_unlock(&priv->reg_mutex); +- +- return 0; +-} +- +-static int +-qca8k_port_mdb_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_mdb *mdb) +-{ +- struct qca8k_priv *priv = ds->priv; +- const u8 *addr = mdb->addr; +- u16 vid = mdb->vid; +- +- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); +-} +- +-static int +-qca8k_port_mdb_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_mdb *mdb) +-{ +- struct qca8k_priv *priv = ds->priv; +- const u8 *addr = mdb->addr; +- u16 vid = mdb->vid; +- +- return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); +-} +- +-static int +-qca8k_port_mirror_add(struct dsa_switch *ds, int port, +- struct dsa_mall_mirror_tc_entry *mirror, +- bool ingress) +-{ +- struct qca8k_priv *priv = ds->priv; +- int monitor_port, ret; +- u32 reg, val; +- +- /* Check for existent entry */ +- if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) +- return -EEXIST; +- +- ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); +- if (ret) +- return ret; +- +- /* QCA83xx can have only one port set to mirror mode. +- * Check that the correct port is requested and return error otherwise. +- * When no mirror port is set, the values is set to 0xF +- */ +- monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (monitor_port != 0xF && monitor_port != mirror->to_local_port) +- return -EEXIST; +- +- /* Set the monitor port */ +- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, +- mirror->to_local_port); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (ret) +- return ret; +- +- if (ingress) { +- reg = QCA8K_PORT_LOOKUP_CTRL(port); +- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +- } else { +- reg = QCA8K_REG_PORT_HOL_CTRL1(port); +- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +- } +- +- ret = regmap_update_bits(priv->regmap, reg, val, val); +- if (ret) +- return ret; +- +- /* Track mirror port for tx and rx to decide when the +- * mirror port has to be disabled. +- */ +- if (ingress) +- priv->mirror_rx |= BIT(port); +- else +- priv->mirror_tx |= BIT(port); +- +- return 0; +-} +- +-static void +-qca8k_port_mirror_del(struct dsa_switch *ds, int port, +- struct dsa_mall_mirror_tc_entry *mirror) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg, val; +- int ret; +- +- if (mirror->ingress) { +- reg = QCA8K_PORT_LOOKUP_CTRL(port); +- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +- } else { +- reg = QCA8K_REG_PORT_HOL_CTRL1(port); +- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +- } +- +- ret = regmap_clear_bits(priv->regmap, reg, val); +- if (ret) +- goto err; +- +- if (mirror->ingress) +- priv->mirror_rx &= ~BIT(port); +- else +- priv->mirror_tx &= ~BIT(port); +- +- /* No port set to send packet to mirror port. Disable mirror port */ +- if (!priv->mirror_rx && !priv->mirror_tx) { +- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (ret) +- goto err; +- } +-err: +- dev_err(priv->dev, "Failed to del mirror port from %d", port); +-} +- +-static int +-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +- struct netlink_ext_ack *extack) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- if (vlan_filtering) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); +- } else { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan, +- struct netlink_ext_ack *extack) +-{ +- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); +- if (ret) { +- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); +- return ret; +- } +- +- if (pvid) { +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- QCA8K_EGREES_VLAN_PORT_MASK(port), +- QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), +- QCA8K_PORT_VLAN_CVID(vlan->vid) | +- QCA8K_PORT_VLAN_SVID(vlan->vid)); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_del(priv, port, vlan->vid); +- if (ret) +- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); +- +- return ret; +-} +- +-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- /* Communicate to the phy internal driver the switch revision. +- * Based on the switch revision different values needs to be +- * set to the dbg and mmd reg on the phy. +- * The first 2 bit are used to communicate the switch revision +- * to the phy driver. +- */ +- if (port > 0 && port < 6) +- return priv->switch_revision; +- +- return 0; +-} +- +-static enum dsa_tag_protocol +-qca8k_get_tag_protocol(struct dsa_switch *ds, int port, +- enum dsa_tag_protocol mp) +-{ +- return DSA_TAG_PROTO_QCA; +-} +- +-static bool +-qca8k_lag_can_offload(struct dsa_switch *ds, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- struct dsa_port *dp; +- int id, members = 0; +- +- id = dsa_lag_id(ds->dst, lag); +- if (id < 0 || id >= ds->num_lag_ids) +- return false; +- +- dsa_lag_foreach_port(dp, ds->dst, lag) +- /* Includes the port joining the LAG */ +- members++; +- +- if (members > QCA8K_NUM_PORTS_FOR_LAG) +- 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 +-qca8k_lag_setup_hash(struct dsa_switch *ds, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- struct qca8k_priv *priv = ds->priv; +- bool unique_lag = true; +- u32 hash = 0; +- int i, id; +- +- id = dsa_lag_id(ds->dst, lag); +- +- switch (info->hash_type) { +- case NETDEV_LAG_HASH_L23: +- hash |= QCA8K_TRUNK_HASH_SIP_EN; +- hash |= QCA8K_TRUNK_HASH_DIP_EN; +- fallthrough; +- case NETDEV_LAG_HASH_L2: +- hash |= QCA8K_TRUNK_HASH_SA_EN; +- hash |= QCA8K_TRUNK_HASH_DA_EN; +- break; +- default: /* We should NEVER reach this */ +- return -EOPNOTSUPP; +- } +- +- /* Check if we are the unique configured LAG */ +- dsa_lags_foreach_id(i, ds->dst) +- if (i != id && dsa_lag_dev(ds->dst, i)) { +- unique_lag = false; +- break; +- } +- +- /* Hash Mode is global. Make sure the same Hash Mode +- * is set to all the 4 possible lag. +- * If we are the unique LAG we can set whatever hash +- * mode we want. +- * To change hash mode it's needed to remove all LAG +- * and change the mode with the latest. +- */ +- if (unique_lag) { +- priv->lag_hash_mode = hash; +- } else if (priv->lag_hash_mode != hash) { +- netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n"); +- return -EOPNOTSUPP; +- } +- +- return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, +- QCA8K_TRUNK_HASH_MASK, hash); +-} +- +-static int +-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, +- struct net_device *lag, bool delete) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret, id, i; +- u32 val; +- +- id = dsa_lag_id(ds->dst, lag); +- +- /* Read current port member */ +- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); +- if (ret) +- return ret; +- +- /* Shift val to the correct trunk */ +- val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); +- val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; +- if (delete) +- val &= ~BIT(port); +- else +- val |= BIT(port); +- +- /* Update port member. With empty portmap disable trunk */ +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, +- QCA8K_REG_GOL_TRUNK_MEMBER(id) | +- QCA8K_REG_GOL_TRUNK_EN(id), +- !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | +- val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); +- +- /* Search empty member if adding or port on deleting */ +- for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { +- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); +- if (ret) +- return ret; +- +- val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); +- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; +- +- if (delete) { +- /* If port flagged to be disabled assume this member is +- * empty +- */ +- if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +- continue; +- +- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; +- if (val != port) +- continue; +- } else { +- /* If port flagged to be enabled assume this member is +- * already set +- */ +- if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +- continue; +- } +- +- /* We have found the member to add/remove */ +- break; +- } +- +- /* Set port in the correct port mask or disable port if in delete mode */ +- return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), +- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | +- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), +- !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | +- port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); +-} +- +-static int +-qca8k_port_lag_join(struct dsa_switch *ds, int port, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- int ret; +- +- if (!qca8k_lag_can_offload(ds, lag, info)) +- return -EOPNOTSUPP; +- +- ret = qca8k_lag_setup_hash(ds, lag, info); +- if (ret) +- return ret; +- +- return qca8k_lag_refresh_portmap(ds, port, lag, false); +-} +- +-static int +-qca8k_port_lag_leave(struct dsa_switch *ds, int port, +- struct net_device *lag) +-{ +- return qca8k_lag_refresh_portmap(ds, port, lag, true); +-} +- +-static void +-qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, +- bool operational) +-{ +- struct dsa_port *dp = master->dsa_ptr; +- struct qca8k_priv *priv = ds->priv; +- +- /* Ethernet MIB/MDIO is only supported for CPU port 0 */ +- if (dp->index != 0) +- return; +- +- mutex_lock(&priv->mgmt_eth_data.mutex); +- mutex_lock(&priv->mib_eth_data.mutex); +- +- priv->mgmt_master = operational ? (struct net_device *)master : NULL; +- +- mutex_unlock(&priv->mib_eth_data.mutex); +- mutex_unlock(&priv->mgmt_eth_data.mutex); +-} +- +-static int qca8k_connect_tag_protocol(struct dsa_switch *ds, +- enum dsa_tag_protocol proto) +-{ +- struct qca_tagger_data *tagger_data; +- +- switch (proto) { +- case DSA_TAG_PROTO_QCA: +- tagger_data = ds->tagger_data; +- +- tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; +- tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; +- +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- return 0; +-} +- +-static const struct dsa_switch_ops qca8k_switch_ops = { +- .get_tag_protocol = qca8k_get_tag_protocol, +- .setup = qca8k_setup, +- .get_strings = qca8k_get_strings, +- .get_ethtool_stats = qca8k_get_ethtool_stats, +- .get_sset_count = qca8k_get_sset_count, +- .set_ageing_time = qca8k_set_ageing_time, +- .get_mac_eee = qca8k_get_mac_eee, +- .set_mac_eee = qca8k_set_mac_eee, +- .port_enable = qca8k_port_enable, +- .port_disable = qca8k_port_disable, +- .port_change_mtu = qca8k_port_change_mtu, +- .port_max_mtu = qca8k_port_max_mtu, +- .port_stp_state_set = qca8k_port_stp_state_set, +- .port_bridge_join = qca8k_port_bridge_join, +- .port_bridge_leave = qca8k_port_bridge_leave, +- .port_fast_age = qca8k_port_fast_age, +- .port_fdb_add = qca8k_port_fdb_add, +- .port_fdb_del = qca8k_port_fdb_del, +- .port_fdb_dump = qca8k_port_fdb_dump, +- .port_mdb_add = qca8k_port_mdb_add, +- .port_mdb_del = qca8k_port_mdb_del, +- .port_mirror_add = qca8k_port_mirror_add, +- .port_mirror_del = qca8k_port_mirror_del, +- .port_vlan_filtering = qca8k_port_vlan_filtering, +- .port_vlan_add = qca8k_port_vlan_add, +- .port_vlan_del = qca8k_port_vlan_del, +- .phylink_validate = qca8k_phylink_validate, +- .phylink_mac_link_state = qca8k_phylink_mac_link_state, +- .phylink_mac_config = qca8k_phylink_mac_config, +- .phylink_mac_link_down = qca8k_phylink_mac_link_down, +- .phylink_mac_link_up = qca8k_phylink_mac_link_up, +- .get_phy_flags = qca8k_get_phy_flags, +- .port_lag_join = qca8k_port_lag_join, +- .port_lag_leave = qca8k_port_lag_leave, +- .master_state_change = qca8k_master_change, +- .connect_tag_protocol = qca8k_connect_tag_protocol, +-}; +- +-static int qca8k_read_switch_id(struct qca8k_priv *priv) +-{ +- const struct qca8k_match_data *data; +- u32 val; +- u8 id; +- int ret; +- +- /* get the switches ID from the compatible */ +- data = of_device_get_match_data(priv->dev); +- if (!data) +- return -ENODEV; +- +- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +- if (ret < 0) +- return -ENODEV; +- +- id = QCA8K_MASK_CTRL_DEVICE_ID(val); +- if (id != data->id) { +- dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id); +- return -ENODEV; +- } +- +- priv->switch_id = id; +- +- /* Save revision to communicate to the internal PHY driver */ +- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); +- +- return 0; +-} +- +-static int +-qca8k_sw_probe(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv; +- int ret; +- +- /* allocate the private data struct so that we can probe the switches +- * ID register +- */ +- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- +- priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", +- GPIOD_ASIS); +- if (IS_ERR(priv->reset_gpio)) +- return PTR_ERR(priv->reset_gpio); +- +- if (priv->reset_gpio) { +- gpiod_set_value_cansleep(priv->reset_gpio, 1); +- /* The active low duration must be greater than 10 ms +- * and checkpatch.pl wants 20 ms. +- */ +- msleep(20); +- gpiod_set_value_cansleep(priv->reset_gpio, 0); +- } +- +- /* Start by setting up the register mapping */ +- priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv, +- &qca8k_regmap_config); +- if (IS_ERR(priv->regmap)) { +- dev_err(priv->dev, "regmap initialization failed"); +- return PTR_ERR(priv->regmap); +- } +- +- priv->mdio_cache.page = 0xffff; +- priv->mdio_cache.lo = 0xffff; +- priv->mdio_cache.hi = 0xffff; +- +- /* Check the detected switch id */ +- ret = qca8k_read_switch_id(priv); +- if (ret) +- return ret; +- +- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- mutex_init(&priv->mgmt_eth_data.mutex); +- init_completion(&priv->mgmt_eth_data.rw_done); +- +- mutex_init(&priv->mib_eth_data.mutex); +- init_completion(&priv->mib_eth_data.rw_done); +- +- priv->ds->dev = &mdiodev->dev; +- priv->ds->num_ports = QCA8K_NUM_PORTS; +- priv->ds->priv = priv; +- priv->ds->ops = &qca8k_switch_ops; +- mutex_init(&priv->reg_mutex); +- dev_set_drvdata(&mdiodev->dev, priv); +- +- return dsa_register_switch(priv->ds); +-} +- +-static void +-qca8k_sw_remove(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- int i; +- +- if (!priv) +- return; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- qca8k_port_set_status(priv, i, 0); +- +- dsa_unregister_switch(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-static void qca8k_sw_shutdown(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- +- if (!priv) +- return; +- +- dsa_switch_shutdown(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-#ifdef CONFIG_PM_SLEEP +-static void +-qca8k_set_pm(struct qca8k_priv *priv, int enable) +-{ +- int port; +- +- for (port = 0; port < QCA8K_NUM_PORTS; port++) { +- /* Do not enable on resume if the port was +- * disabled before. +- */ +- if (!(priv->port_enabled_map & BIT(port))) +- continue; +- +- qca8k_port_set_status(priv, port, enable); +- } +-} +- +-static int qca8k_suspend(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 0); +- +- return dsa_switch_suspend(priv->ds); +-} +- +-static int qca8k_resume(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 1); +- +- return dsa_switch_resume(priv->ds); +-} +-#endif /* CONFIG_PM_SLEEP */ +- +-static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, +- qca8k_suspend, qca8k_resume); +- +-static const struct qca8k_match_data qca8327 = { +- .id = QCA8K_ID_QCA8327, +- .reduced_package = true, +- .mib_count = QCA8K_QCA832X_MIB_COUNT, +-}; +- +-static const struct qca8k_match_data qca8328 = { +- .id = QCA8K_ID_QCA8327, +- .mib_count = QCA8K_QCA832X_MIB_COUNT, +-}; +- +-static const struct qca8k_match_data qca833x = { +- .id = QCA8K_ID_QCA8337, +- .mib_count = QCA8K_QCA833X_MIB_COUNT, +-}; +- +-static const struct of_device_id qca8k_of_match[] = { +- { .compatible = "qca,qca8327", .data = &qca8327 }, +- { .compatible = "qca,qca8328", .data = &qca8328 }, +- { .compatible = "qca,qca8334", .data = &qca833x }, +- { .compatible = "qca,qca8337", .data = &qca833x }, +- { /* sentinel */ }, +-}; +- +-static struct mdio_driver qca8kmdio_driver = { +- .probe = qca8k_sw_probe, +- .remove = qca8k_sw_remove, +- .shutdown = qca8k_sw_shutdown, +- .mdiodrv.driver = { +- .name = "qca8k", +- .of_match_table = qca8k_of_match, +- .pm = &qca8k_pm_ops, +- }, +-}; +- +-mdio_module_driver(qca8kmdio_driver); +- +-MODULE_AUTHOR("Mathieu Olivari, John Crispin "); +-MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:qca8k"); +--- a/drivers/net/dsa/qca8k.h ++++ /dev/null +@@ -1,411 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * Copyright (C) 2009 Felix Fietkau +- * Copyright (C) 2011-2012 Gabor Juhos +- * Copyright (c) 2015, The Linux Foundation. All rights reserved. +- */ +- +-#ifndef __QCA8K_H +-#define __QCA8K_H +- +-#include +-#include +-#include +-#include +- +-#define QCA8K_ETHERNET_MDIO_PRIORITY 7 +-#define QCA8K_ETHERNET_PHY_PRIORITY 6 +-#define QCA8K_ETHERNET_TIMEOUT 100 +- +-#define QCA8K_NUM_PORTS 7 +-#define QCA8K_NUM_CPU_PORTS 2 +-#define QCA8K_MAX_MTU 9000 +-#define QCA8K_NUM_LAGS 4 +-#define QCA8K_NUM_PORTS_FOR_LAG 4 +- +-#define PHY_ID_QCA8327 0x004dd034 +-#define QCA8K_ID_QCA8327 0x12 +-#define PHY_ID_QCA8337 0x004dd036 +-#define QCA8K_ID_QCA8337 0x13 +- +-#define QCA8K_QCA832X_MIB_COUNT 39 +-#define QCA8K_QCA833X_MIB_COUNT 41 +- +-#define QCA8K_BUSY_WAIT_TIMEOUT 2000 +- +-#define QCA8K_NUM_FDB_RECORDS 2048 +- +-#define QCA8K_PORT_VID_DEF 1 +- +-/* Global control registers */ +-#define QCA8K_REG_MASK_CTRL 0x000 +-#define QCA8K_MASK_CTRL_REV_ID_MASK GENMASK(7, 0) +-#define QCA8K_MASK_CTRL_REV_ID(x) FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x) +-#define QCA8K_MASK_CTRL_DEVICE_ID_MASK GENMASK(15, 8) +-#define QCA8K_MASK_CTRL_DEVICE_ID(x) FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x) +-#define QCA8K_REG_PORT0_PAD_CTRL 0x004 +-#define QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN BIT(31) +-#define QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE BIT(19) +-#define QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE BIT(18) +-#define QCA8K_REG_PORT5_PAD_CTRL 0x008 +-#define QCA8K_REG_PORT6_PAD_CTRL 0x00c +-#define QCA8K_PORT_PAD_RGMII_EN BIT(26) +-#define QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK GENMASK(23, 22) +-#define QCA8K_PORT_PAD_RGMII_TX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x) +-#define QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK GENMASK(21, 20) +-#define QCA8K_PORT_PAD_RGMII_RX_DELAY(x) FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x) +-#define QCA8K_PORT_PAD_RGMII_TX_DELAY_EN BIT(25) +-#define QCA8K_PORT_PAD_RGMII_RX_DELAY_EN BIT(24) +-#define QCA8K_PORT_PAD_SGMII_EN BIT(7) +-#define QCA8K_REG_PWS 0x010 +-#define QCA8K_PWS_POWER_ON_SEL BIT(31) +-/* This reg is only valid for QCA832x and toggle the package +- * type from 176 pin (by default) to 148 pin used on QCA8327 +- */ +-#define QCA8327_PWS_PACKAGE148_EN BIT(30) +-#define QCA8K_PWS_LED_OPEN_EN_CSR BIT(24) +-#define QCA8K_PWS_SERDES_AEN_DIS BIT(7) +-#define QCA8K_REG_MODULE_EN 0x030 +-#define QCA8K_MODULE_EN_MIB BIT(0) +-#define QCA8K_REG_MIB 0x034 +-#define QCA8K_MIB_FUNC GENMASK(26, 24) +-#define QCA8K_MIB_CPU_KEEP BIT(20) +-#define QCA8K_MIB_BUSY BIT(17) +-#define QCA8K_MDIO_MASTER_CTRL 0x3c +-#define QCA8K_MDIO_MASTER_BUSY BIT(31) +-#define QCA8K_MDIO_MASTER_EN BIT(30) +-#define QCA8K_MDIO_MASTER_READ BIT(27) +-#define QCA8K_MDIO_MASTER_WRITE 0 +-#define QCA8K_MDIO_MASTER_SUP_PRE BIT(26) +-#define QCA8K_MDIO_MASTER_PHY_ADDR_MASK GENMASK(25, 21) +-#define QCA8K_MDIO_MASTER_PHY_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x) +-#define QCA8K_MDIO_MASTER_REG_ADDR_MASK GENMASK(20, 16) +-#define QCA8K_MDIO_MASTER_REG_ADDR(x) FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x) +-#define QCA8K_MDIO_MASTER_DATA_MASK GENMASK(15, 0) +-#define QCA8K_MDIO_MASTER_DATA(x) FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x) +-#define QCA8K_MDIO_MASTER_MAX_PORTS 5 +-#define QCA8K_MDIO_MASTER_MAX_REG 32 +-#define QCA8K_GOL_MAC_ADDR0 0x60 +-#define QCA8K_GOL_MAC_ADDR1 0x64 +-#define QCA8K_MAX_FRAME_SIZE 0x78 +-#define QCA8K_REG_PORT_STATUS(_i) (0x07c + (_i) * 4) +-#define QCA8K_PORT_STATUS_SPEED GENMASK(1, 0) +-#define QCA8K_PORT_STATUS_SPEED_10 0 +-#define QCA8K_PORT_STATUS_SPEED_100 0x1 +-#define QCA8K_PORT_STATUS_SPEED_1000 0x2 +-#define QCA8K_PORT_STATUS_TXMAC BIT(2) +-#define QCA8K_PORT_STATUS_RXMAC BIT(3) +-#define QCA8K_PORT_STATUS_TXFLOW BIT(4) +-#define QCA8K_PORT_STATUS_RXFLOW BIT(5) +-#define QCA8K_PORT_STATUS_DUPLEX BIT(6) +-#define QCA8K_PORT_STATUS_LINK_UP BIT(8) +-#define QCA8K_PORT_STATUS_LINK_AUTO BIT(9) +-#define QCA8K_PORT_STATUS_LINK_PAUSE BIT(10) +-#define QCA8K_PORT_STATUS_FLOW_AUTO BIT(12) +-#define QCA8K_REG_PORT_HDR_CTRL(_i) (0x9c + (_i * 4)) +-#define QCA8K_PORT_HDR_CTRL_RX_MASK GENMASK(3, 2) +-#define QCA8K_PORT_HDR_CTRL_TX_MASK GENMASK(1, 0) +-#define QCA8K_PORT_HDR_CTRL_ALL 2 +-#define QCA8K_PORT_HDR_CTRL_MGMT 1 +-#define QCA8K_PORT_HDR_CTRL_NONE 0 +-#define QCA8K_REG_SGMII_CTRL 0x0e0 +-#define QCA8K_SGMII_EN_PLL BIT(1) +-#define QCA8K_SGMII_EN_RX BIT(2) +-#define QCA8K_SGMII_EN_TX BIT(3) +-#define QCA8K_SGMII_EN_SD BIT(4) +-#define QCA8K_SGMII_CLK125M_DELAY BIT(7) +-#define QCA8K_SGMII_MODE_CTRL_MASK GENMASK(23, 22) +-#define QCA8K_SGMII_MODE_CTRL(x) FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x) +-#define QCA8K_SGMII_MODE_CTRL_BASEX QCA8K_SGMII_MODE_CTRL(0x0) +-#define QCA8K_SGMII_MODE_CTRL_PHY QCA8K_SGMII_MODE_CTRL(0x1) +-#define QCA8K_SGMII_MODE_CTRL_MAC QCA8K_SGMII_MODE_CTRL(0x2) +- +-/* MAC_PWR_SEL registers */ +-#define QCA8K_REG_MAC_PWR_SEL 0x0e4 +-#define QCA8K_MAC_PWR_RGMII1_1_8V BIT(18) +-#define QCA8K_MAC_PWR_RGMII0_1_8V BIT(19) +- +-/* EEE control registers */ +-#define QCA8K_REG_EEE_CTRL 0x100 +-#define QCA8K_REG_EEE_CTRL_LPI_EN(_i) ((_i + 1) * 2) +- +-/* TRUNK_HASH_EN registers */ +-#define QCA8K_TRUNK_HASH_EN_CTRL 0x270 +-#define QCA8K_TRUNK_HASH_SIP_EN BIT(3) +-#define QCA8K_TRUNK_HASH_DIP_EN BIT(2) +-#define QCA8K_TRUNK_HASH_SA_EN BIT(1) +-#define QCA8K_TRUNK_HASH_DA_EN BIT(0) +-#define QCA8K_TRUNK_HASH_MASK GENMASK(3, 0) +- +-/* ACL registers */ +-#define QCA8K_REG_PORT_VLAN_CTRL0(_i) (0x420 + (_i * 8)) +-#define QCA8K_PORT_VLAN_CVID_MASK GENMASK(27, 16) +-#define QCA8K_PORT_VLAN_CVID(x) FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x) +-#define QCA8K_PORT_VLAN_SVID_MASK GENMASK(11, 0) +-#define QCA8K_PORT_VLAN_SVID(x) FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x) +-#define QCA8K_REG_PORT_VLAN_CTRL1(_i) (0x424 + (_i * 8)) +-#define QCA8K_REG_IPV4_PRI_BASE_ADDR 0x470 +-#define QCA8K_REG_IPV4_PRI_ADDR_MASK 0x474 +- +-/* Lookup registers */ +-#define QCA8K_REG_ATU_DATA0 0x600 +-#define QCA8K_ATU_ADDR2_MASK GENMASK(31, 24) +-#define QCA8K_ATU_ADDR3_MASK GENMASK(23, 16) +-#define QCA8K_ATU_ADDR4_MASK GENMASK(15, 8) +-#define QCA8K_ATU_ADDR5_MASK GENMASK(7, 0) +-#define QCA8K_REG_ATU_DATA1 0x604 +-#define QCA8K_ATU_PORT_MASK GENMASK(22, 16) +-#define QCA8K_ATU_ADDR0_MASK GENMASK(15, 8) +-#define QCA8K_ATU_ADDR1_MASK GENMASK(7, 0) +-#define QCA8K_REG_ATU_DATA2 0x608 +-#define QCA8K_ATU_VID_MASK GENMASK(19, 8) +-#define QCA8K_ATU_STATUS_MASK GENMASK(3, 0) +-#define QCA8K_ATU_STATUS_STATIC 0xf +-#define QCA8K_REG_ATU_FUNC 0x60c +-#define QCA8K_ATU_FUNC_BUSY BIT(31) +-#define QCA8K_ATU_FUNC_PORT_EN BIT(14) +-#define QCA8K_ATU_FUNC_MULTI_EN BIT(13) +-#define QCA8K_ATU_FUNC_FULL BIT(12) +-#define QCA8K_ATU_FUNC_PORT_MASK GENMASK(11, 8) +-#define QCA8K_REG_VTU_FUNC0 0x610 +-#define QCA8K_VTU_FUNC0_VALID BIT(20) +-#define QCA8K_VTU_FUNC0_IVL_EN BIT(19) +-/* QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(17, 4) +- * It does contain VLAN_MODE for each port [5:4] for port0, +- * [7:6] for port1 ... [17:16] for port6. Use virtual port +- * define to handle this. +- */ +-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i) (4 + (_i) * 2) +-#define QCA8K_VTU_FUNC0_EG_MODE_MASK GENMASK(1, 0) +-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i) (GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +-#define QCA8K_VTU_FUNC0_EG_MODE_UNMOD FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0) +-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +-#define QCA8K_VTU_FUNC0_EG_MODE_UNTAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1) +-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +-#define QCA8K_VTU_FUNC0_EG_MODE_TAG FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2) +-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i) (QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +-#define QCA8K_VTU_FUNC0_EG_MODE_NOT FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3) +-#define QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i) (QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +-#define QCA8K_REG_VTU_FUNC1 0x614 +-#define QCA8K_VTU_FUNC1_BUSY BIT(31) +-#define QCA8K_VTU_FUNC1_VID_MASK GENMASK(27, 16) +-#define QCA8K_VTU_FUNC1_FULL BIT(4) +-#define QCA8K_REG_ATU_CTRL 0x618 +-#define QCA8K_ATU_AGE_TIME_MASK GENMASK(15, 0) +-#define QCA8K_ATU_AGE_TIME(x) FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x)) +-#define QCA8K_REG_GLOBAL_FW_CTRL0 0x620 +-#define QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN BIT(10) +-#define QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM GENMASK(7, 4) +-#define QCA8K_REG_GLOBAL_FW_CTRL1 0x624 +-#define QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK GENMASK(30, 24) +-#define QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK GENMASK(22, 16) +-#define QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK GENMASK(14, 8) +-#define QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK GENMASK(6, 0) +-#define QCA8K_PORT_LOOKUP_CTRL(_i) (0x660 + (_i) * 0xc) +-#define QCA8K_PORT_LOOKUP_MEMBER GENMASK(6, 0) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_MASK GENMASK(9, 8) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_NONE QCA8K_PORT_LOOKUP_VLAN_MODE(0x0) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK QCA8K_PORT_LOOKUP_VLAN_MODE(0x1) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK QCA8K_PORT_LOOKUP_VLAN_MODE(0x2) +-#define QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE QCA8K_PORT_LOOKUP_VLAN_MODE(0x3) +-#define QCA8K_PORT_LOOKUP_STATE_MASK GENMASK(18, 16) +-#define QCA8K_PORT_LOOKUP_STATE(x) FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x) +-#define QCA8K_PORT_LOOKUP_STATE_DISABLED QCA8K_PORT_LOOKUP_STATE(0x0) +-#define QCA8K_PORT_LOOKUP_STATE_BLOCKING QCA8K_PORT_LOOKUP_STATE(0x1) +-#define QCA8K_PORT_LOOKUP_STATE_LISTENING QCA8K_PORT_LOOKUP_STATE(0x2) +-#define QCA8K_PORT_LOOKUP_STATE_LEARNING QCA8K_PORT_LOOKUP_STATE(0x3) +-#define QCA8K_PORT_LOOKUP_STATE_FORWARD QCA8K_PORT_LOOKUP_STATE(0x4) +-#define QCA8K_PORT_LOOKUP_LEARN BIT(20) +-#define QCA8K_PORT_LOOKUP_ING_MIRROR_EN BIT(25) +- +-#define QCA8K_REG_GOL_TRUNK_CTRL0 0x700 +-/* 4 max trunk first +- * first 6 bit for member bitmap +- * 7th bit is to enable trunk port +- */ +-#define QCA8K_REG_GOL_TRUNK_SHIFT(_i) ((_i) * 8) +-#define QCA8K_REG_GOL_TRUNK_EN_MASK BIT(7) +-#define QCA8K_REG_GOL_TRUNK_EN(_i) (QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) +-#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK GENMASK(6, 0) +-#define QCA8K_REG_GOL_TRUNK_MEMBER(_i) (QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) +-/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */ +-#define QCA8K_REG_GOL_TRUNK_CTRL(_i) (0x704 + (((_i) / 2) * 4)) +-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK GENMASK(3, 0) +-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK BIT(3) +-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK GENMASK(2, 0) +-#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i) (((_i) / 2) * 16) +-#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i) ((_i) * 4) +-/* Complex shift: FIRST shift for port THEN shift for trunk */ +-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j) (QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i)) +-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)) +-#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j) (QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)) +- +-#define QCA8K_REG_GLOBAL_FC_THRESH 0x800 +-#define QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK GENMASK(24, 16) +-#define QCA8K_GLOBAL_FC_GOL_XON_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x) +-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK GENMASK(8, 0) +-#define QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x) FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x) +- +-#define QCA8K_REG_PORT_HOL_CTRL0(_i) (0x970 + (_i) * 0x8) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK GENMASK(3, 0) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI0(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK GENMASK(7, 4) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI1(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK GENMASK(11, 8) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI2(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK GENMASK(15, 12) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI3(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK GENMASK(19, 16) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI4(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK GENMASK(23, 20) +-#define QCA8K_PORT_HOL_CTRL0_EG_PRI5(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x) +-#define QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK GENMASK(29, 24) +-#define QCA8K_PORT_HOL_CTRL0_EG_PORT(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x) +- +-#define QCA8K_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8) +-#define QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK GENMASK(3, 0) +-#define QCA8K_PORT_HOL_CTRL1_ING(x) FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x) +-#define QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN BIT(6) +-#define QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN BIT(7) +-#define QCA8K_PORT_HOL_CTRL1_WRED_EN BIT(8) +-#define QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16) +- +-/* Pkt edit registers */ +-#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i) (16 * ((_i) % 2)) +-#define QCA8K_EGREES_VLAN_PORT_MASK(_i) (GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i)) +-#define QCA8K_EGREES_VLAN_PORT(_i, x) ((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i)) +-#define QCA8K_EGRESS_VLAN(x) (0x0c70 + (4 * (x / 2))) +- +-/* L3 registers */ +-#define QCA8K_HROUTER_CONTROL 0xe00 +-#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_M GENMASK(17, 16) +-#define QCA8K_HROUTER_CONTROL_GLB_LOCKTIME_S 16 +-#define QCA8K_HROUTER_CONTROL_ARP_AGE_MODE 1 +-#define QCA8K_HROUTER_PBASED_CONTROL1 0xe08 +-#define QCA8K_HROUTER_PBASED_CONTROL2 0xe0c +-#define QCA8K_HNAT_CONTROL 0xe38 +- +-/* MIB registers */ +-#define QCA8K_PORT_MIB_COUNTER(_i) (0x1000 + (_i) * 0x100) +- +-/* QCA specific MII registers */ +-#define MII_ATH_MMD_ADDR 0x0d +-#define MII_ATH_MMD_DATA 0x0e +- +-enum { +- QCA8K_PORT_SPEED_10M = 0, +- QCA8K_PORT_SPEED_100M = 1, +- QCA8K_PORT_SPEED_1000M = 2, +- QCA8K_PORT_SPEED_ERR = 3, +-}; +- +-enum qca8k_fdb_cmd { +- QCA8K_FDB_FLUSH = 1, +- QCA8K_FDB_LOAD = 2, +- QCA8K_FDB_PURGE = 3, +- QCA8K_FDB_FLUSH_PORT = 5, +- QCA8K_FDB_NEXT = 6, +- QCA8K_FDB_SEARCH = 7, +-}; +- +-enum qca8k_vlan_cmd { +- QCA8K_VLAN_FLUSH = 1, +- QCA8K_VLAN_LOAD = 2, +- QCA8K_VLAN_PURGE = 3, +- QCA8K_VLAN_REMOVE_PORT = 4, +- QCA8K_VLAN_NEXT = 5, +- QCA8K_VLAN_READ = 6, +-}; +- +-enum qca8k_mid_cmd { +- QCA8K_MIB_FLUSH = 1, +- QCA8K_MIB_FLUSH_PORT = 2, +- QCA8K_MIB_CAST = 3, +-}; +- +-struct qca8k_match_data { +- u8 id; +- bool reduced_package; +- u8 mib_count; +-}; +- +-enum { +- QCA8K_CPU_PORT0, +- QCA8K_CPU_PORT6, +-}; +- +-struct qca8k_mgmt_eth_data { +- struct completion rw_done; +- struct mutex mutex; /* Enforce one mdio read/write at time */ +- bool ack; +- u32 seq; +- u32 data[4]; +-}; +- +-struct qca8k_mib_eth_data { +- struct completion rw_done; +- struct mutex mutex; /* Process one command at time */ +- refcount_t port_parsed; /* Counter to track parsed port */ +- u8 req_port; +- u64 *data; /* pointer to ethtool data */ +-}; +- +-struct qca8k_ports_config { +- bool sgmii_rx_clk_falling_edge; +- bool sgmii_tx_clk_falling_edge; +- bool sgmii_enable_pll; +- u8 rgmii_rx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ +- u8 rgmii_tx_delay[QCA8K_NUM_CPU_PORTS]; /* 0: CPU port0, 1: CPU port6 */ +-}; +- +-struct qca8k_mdio_cache { +-/* The 32bit switch registers are accessed indirectly. To achieve this we need +- * to set the page of the register. Track the last page that was set to reduce +- * mdio writes +- */ +- u16 page; +-/* lo and hi can also be cached and from Documentation we can skip one +- * extra mdio write if lo or hi is didn't change. +- */ +- u16 lo; +- u16 hi; +-}; +- +-struct qca8k_priv { +- u8 switch_id; +- u8 switch_revision; +- u8 mirror_rx; +- u8 mirror_tx; +- u8 lag_hash_mode; +- /* Each bit correspond to a port. This switch can support a max of 7 port. +- * Bit 1: port enabled. Bit 0: port disabled. +- */ +- u8 port_enabled_map; +- struct qca8k_ports_config ports_config; +- struct regmap *regmap; +- struct mii_bus *bus; +- struct dsa_switch *ds; +- struct mutex reg_mutex; +- struct device *dev; +- struct gpio_desc *reset_gpio; +- struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */ +- struct qca8k_mgmt_eth_data mgmt_eth_data; +- struct qca8k_mib_eth_data mib_eth_data; +- struct qca8k_mdio_cache mdio_cache; +-}; +- +-struct qca8k_mib_desc { +- unsigned int size; +- unsigned int offset; +- const char *name; +-}; +- +-struct qca8k_fdb { +- u16 vid; +- u8 port_mask; +- u8 aging; +- u8 mac[6]; +-}; +- +-#endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch b/target/linux/generic/backport-5.15/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch new file mode 100644 index 0000000000..77fe64632f --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-01-net-dsa-qca8k-cache-match-data-to-speed-up-access.patch @@ -0,0 +1,157 @@ +From 3bb0844e7bcd0fb0bcfab6202b5edd349ef5250a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:10 +0200 +Subject: [PATCH 01/14] net: dsa: qca8k: cache match data to speed up access + +Using of_device_get_match_data is expensive. Cache match data to speed +up access and rework user of match data to use the new cached value. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k.c | 35 +++++++++++------------------------ + drivers/net/dsa/qca/qca8k.h | 1 + + 2 files changed, 12 insertions(+), 24 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k.c ++++ b/drivers/net/dsa/qca/qca8k.c +@@ -1462,8 +1462,8 @@ static int qca8k_find_cpu_port(struct ds + static int + qca8k_setup_of_pws_reg(struct qca8k_priv *priv) + { ++ const struct qca8k_match_data *data = priv->info; + struct device_node *node = priv->dev->of_node; +- const struct qca8k_match_data *data; + u32 val = 0; + int ret; + +@@ -1472,8 +1472,6 @@ qca8k_setup_of_pws_reg(struct qca8k_priv + * Should be applied by default but we set this just to make sure. + */ + if (priv->switch_id == QCA8K_ID_QCA8327) { +- data = of_device_get_match_data(priv->dev); +- + /* Set the correct package of 148 pin for QCA8327 */ + if (data->reduced_package) + val |= QCA8327_PWS_PACKAGE148_EN; +@@ -2146,23 +2144,19 @@ qca8k_phylink_mac_link_up(struct dsa_swi + static void + qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) + { +- const struct qca8k_match_data *match_data; + struct qca8k_priv *priv = ds->priv; + int i; + + if (stringset != ETH_SS_STATS) + return; + +- match_data = of_device_get_match_data(priv->dev); +- +- for (i = 0; i < match_data->mib_count; i++) ++ for (i = 0; i < priv->info->mib_count; i++) + strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, + ETH_GSTRING_LEN); + } + + static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) + { +- const struct qca8k_match_data *match_data; + struct qca8k_mib_eth_data *mib_eth_data; + struct qca8k_priv *priv = ds->priv; + const struct qca8k_mib_desc *mib; +@@ -2181,10 +2175,9 @@ static void qca8k_mib_autocast_handler(s + if (port != mib_eth_data->req_port) + goto exit; + +- match_data = device_get_match_data(priv->dev); + data = mib_eth_data->data; + +- for (i = 0; i < match_data->mib_count; i++) { ++ for (i = 0; i < priv->info->mib_count; i++) { + mib = &ar8327_mib[i]; + + /* First 3 mib are present in the skb head */ +@@ -2256,7 +2249,6 @@ qca8k_get_ethtool_stats(struct dsa_switc + uint64_t *data) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- const struct qca8k_match_data *match_data; + const struct qca8k_mib_desc *mib; + u32 reg, i, val; + u32 hi = 0; +@@ -2266,9 +2258,7 @@ qca8k_get_ethtool_stats(struct dsa_switc + qca8k_get_ethtool_stats_eth(ds, port, data) > 0) + return; + +- match_data = of_device_get_match_data(priv->dev); +- +- for (i = 0; i < match_data->mib_count; i++) { ++ for (i = 0; i < priv->info->mib_count; i++) { + mib = &ar8327_mib[i]; + reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; + +@@ -2291,15 +2281,12 @@ qca8k_get_ethtool_stats(struct dsa_switc + static int + qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) + { +- const struct qca8k_match_data *match_data; + struct qca8k_priv *priv = ds->priv; + + if (sset != ETH_SS_STATS) + return 0; + +- match_data = of_device_get_match_data(priv->dev); +- +- return match_data->mib_count; ++ return priv->info->mib_count; + } + + static int +@@ -3037,14 +3024,11 @@ static const struct dsa_switch_ops qca8k + + static int qca8k_read_switch_id(struct qca8k_priv *priv) + { +- const struct qca8k_match_data *data; + u32 val; + u8 id; + int ret; + +- /* get the switches ID from the compatible */ +- data = of_device_get_match_data(priv->dev); +- if (!data) ++ if (!priv->info) + return -ENODEV; + + ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +@@ -3052,8 +3036,10 @@ static int qca8k_read_switch_id(struct q + return -ENODEV; + + id = QCA8K_MASK_CTRL_DEVICE_ID(val); +- if (id != data->id) { +- dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id); ++ if (id != priv->info->id) { ++ dev_err(priv->dev, ++ "Switch id detected %x but expected %x", ++ id, priv->info->id); + return -ENODEV; + } + +@@ -3078,6 +3064,7 @@ qca8k_sw_probe(struct mdio_device *mdiod + if (!priv) + return -ENOMEM; + ++ priv->info = of_device_get_match_data(priv->dev); + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; + +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -393,6 +393,7 @@ struct qca8k_priv { + struct qca8k_mgmt_eth_data mgmt_eth_data; + struct qca8k_mib_eth_data mib_eth_data; + struct qca8k_mdio_cache mdio_cache; ++ const struct qca8k_match_data *info; + }; + + struct qca8k_mib_desc { diff --git a/target/linux/generic/backport-5.15/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch b/target/linux/generic/backport-5.15/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch new file mode 100644 index 0000000000..5b2dce4c55 --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-02-net-dsa-qca8k-make-mib-autocast-feature-optional.patch @@ -0,0 +1,77 @@ +From 533c64bca62a8654f00698bc893f639013e38c7b Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:11 +0200 +Subject: [PATCH 02/14] net: dsa: qca8k: make mib autocast feature optional + +Some switch may not support mib autocast feature and require the legacy +way of reading the regs directly. +Make the mib autocast feature optional and permit to declare support for +it using match_data struct in a dedicated qca8k_info_ops struct. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k.c | 11 +++++++++-- + drivers/net/dsa/qca/qca8k.h | 5 +++++ + 2 files changed, 14 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k.c ++++ b/drivers/net/dsa/qca/qca8k.c +@@ -2254,8 +2254,8 @@ qca8k_get_ethtool_stats(struct dsa_switc + u32 hi = 0; + int ret; + +- if (priv->mgmt_master && +- qca8k_get_ethtool_stats_eth(ds, port, data) > 0) ++ if (priv->mgmt_master && priv->info->ops->autocast_mib && ++ priv->info->ops->autocast_mib(ds, port, data) > 0) + return; + + for (i = 0; i < priv->info->mib_count; i++) { +@@ -3187,20 +3187,27 @@ static int qca8k_resume(struct device *d + static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + qca8k_suspend, qca8k_resume); + ++static const struct qca8k_info_ops qca8xxx_ops = { ++ .autocast_mib = qca8k_get_ethtool_stats_eth, ++}; ++ + static const struct qca8k_match_data qca8327 = { + .id = QCA8K_ID_QCA8327, + .reduced_package = true, + .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, + }; + + static const struct qca8k_match_data qca8328 = { + .id = QCA8K_ID_QCA8327, + .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, + }; + + static const struct qca8k_match_data qca833x = { + .id = QCA8K_ID_QCA8337, + .mib_count = QCA8K_QCA833X_MIB_COUNT, ++ .ops = &qca8xxx_ops, + }; + + static const struct of_device_id qca8k_of_match[] = { +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -324,10 +324,15 @@ enum qca8k_mid_cmd { + QCA8K_MIB_CAST = 3, + }; + ++struct qca8k_info_ops { ++ int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); ++}; ++ + struct qca8k_match_data { + u8 id; + bool reduced_package; + u8 mib_count; ++ const struct qca8k_info_ops *ops; + }; + + enum { diff --git a/target/linux/generic/backport-5.15/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch b/target/linux/generic/backport-5.15/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch new file mode 100644 index 0000000000..afa466693a --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-03-net-dsa-qca8k-move-mib-struct-to-common-code.patch @@ -0,0 +1,6532 @@ +From 027152b830434e3632ad5dd678cc5d4740358dbb Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:12 +0200 +Subject: [PATCH 03/14] net: dsa: qca8k: move mib struct to common code + +The same MIB struct is used by drivers based on qca8k family switch. Move +it to common code to make it accessible also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/Makefile | 1 + + drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} | 51 --------------- + drivers/net/dsa/qca/qca8k-common.c | 63 +++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 3 + + 4 files changed, 67 insertions(+), 51 deletions(-) + rename drivers/net/dsa/qca/{qca8k.c => qca8k-8xxx.c} (98%) + create mode 100644 drivers/net/dsa/qca/qca8k-common.c + +--- a/drivers/net/dsa/qca/Makefile ++++ b/drivers/net/dsa/qca/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_NET_DSA_AR9331) += ar9331.o + obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o ++qca8k-y += qca8k-common.o qca8k-8xxx.o +--- a/drivers/net/dsa/qca/qca8k.c ++++ /dev/null +@@ -1,3237 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Copyright (C) 2009 Felix Fietkau +- * Copyright (C) 2011-2012 Gabor Juhos +- * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. +- * Copyright (c) 2016 John Crispin +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "qca8k.h" +- +-#define MIB_DESC(_s, _o, _n) \ +- { \ +- .size = (_s), \ +- .offset = (_o), \ +- .name = (_n), \ +- } +- +-static const struct qca8k_mib_desc ar8327_mib[] = { +- MIB_DESC(1, 0x00, "RxBroad"), +- MIB_DESC(1, 0x04, "RxPause"), +- MIB_DESC(1, 0x08, "RxMulti"), +- MIB_DESC(1, 0x0c, "RxFcsErr"), +- MIB_DESC(1, 0x10, "RxAlignErr"), +- MIB_DESC(1, 0x14, "RxRunt"), +- MIB_DESC(1, 0x18, "RxFragment"), +- MIB_DESC(1, 0x1c, "Rx64Byte"), +- MIB_DESC(1, 0x20, "Rx128Byte"), +- MIB_DESC(1, 0x24, "Rx256Byte"), +- MIB_DESC(1, 0x28, "Rx512Byte"), +- MIB_DESC(1, 0x2c, "Rx1024Byte"), +- MIB_DESC(1, 0x30, "Rx1518Byte"), +- MIB_DESC(1, 0x34, "RxMaxByte"), +- MIB_DESC(1, 0x38, "RxTooLong"), +- MIB_DESC(2, 0x3c, "RxGoodByte"), +- MIB_DESC(2, 0x44, "RxBadByte"), +- MIB_DESC(1, 0x4c, "RxOverFlow"), +- MIB_DESC(1, 0x50, "Filtered"), +- MIB_DESC(1, 0x54, "TxBroad"), +- MIB_DESC(1, 0x58, "TxPause"), +- MIB_DESC(1, 0x5c, "TxMulti"), +- MIB_DESC(1, 0x60, "TxUnderRun"), +- MIB_DESC(1, 0x64, "Tx64Byte"), +- MIB_DESC(1, 0x68, "Tx128Byte"), +- MIB_DESC(1, 0x6c, "Tx256Byte"), +- MIB_DESC(1, 0x70, "Tx512Byte"), +- MIB_DESC(1, 0x74, "Tx1024Byte"), +- MIB_DESC(1, 0x78, "Tx1518Byte"), +- MIB_DESC(1, 0x7c, "TxMaxByte"), +- MIB_DESC(1, 0x80, "TxOverSize"), +- MIB_DESC(2, 0x84, "TxByte"), +- MIB_DESC(1, 0x8c, "TxCollision"), +- MIB_DESC(1, 0x90, "TxAbortCol"), +- MIB_DESC(1, 0x94, "TxMultiCol"), +- MIB_DESC(1, 0x98, "TxSingleCol"), +- MIB_DESC(1, 0x9c, "TxExcDefer"), +- MIB_DESC(1, 0xa0, "TxDefer"), +- MIB_DESC(1, 0xa4, "TxLateCol"), +- MIB_DESC(1, 0xa8, "RXUnicast"), +- MIB_DESC(1, 0xac, "TXUnicast"), +-}; +- +-static void +-qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) +-{ +- regaddr >>= 1; +- *r1 = regaddr & 0x1e; +- +- regaddr >>= 5; +- *r2 = regaddr & 0x7; +- +- regaddr >>= 3; +- *page = regaddr & 0x3ff; +-} +- +-static int +-qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo) +-{ +- u16 *cached_lo = &priv->mdio_cache.lo; +- struct mii_bus *bus = priv->bus; +- int ret; +- +- if (lo == *cached_lo) +- return 0; +- +- ret = bus->write(bus, phy_id, regnum, lo); +- if (ret < 0) +- dev_err_ratelimited(&bus->dev, +- "failed to write qca8k 32bit lo register\n"); +- +- *cached_lo = lo; +- return 0; +-} +- +-static int +-qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi) +-{ +- u16 *cached_hi = &priv->mdio_cache.hi; +- struct mii_bus *bus = priv->bus; +- int ret; +- +- if (hi == *cached_hi) +- return 0; +- +- ret = bus->write(bus, phy_id, regnum, hi); +- if (ret < 0) +- dev_err_ratelimited(&bus->dev, +- "failed to write qca8k 32bit hi register\n"); +- +- *cached_hi = hi; +- return 0; +-} +- +-static int +-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) +-{ +- int ret; +- +- ret = bus->read(bus, phy_id, regnum); +- if (ret >= 0) { +- *val = ret; +- ret = bus->read(bus, phy_id, regnum + 1); +- *val |= ret << 16; +- } +- +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to read qca8k 32bit register\n"); +- *val = 0; +- return ret; +- } +- +- return 0; +-} +- +-static void +-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) +-{ +- u16 lo, hi; +- int ret; +- +- lo = val & 0xffff; +- hi = (u16)(val >> 16); +- +- ret = qca8k_set_lo(priv, phy_id, regnum, lo); +- if (ret >= 0) +- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); +-} +- +-static int +-qca8k_set_page(struct qca8k_priv *priv, u16 page) +-{ +- u16 *cached_page = &priv->mdio_cache.page; +- struct mii_bus *bus = priv->bus; +- int ret; +- +- if (page == *cached_page) +- return 0; +- +- ret = bus->write(bus, 0x18, 0, page); +- if (ret < 0) { +- dev_err_ratelimited(&bus->dev, +- "failed to set qca8k page\n"); +- return ret; +- } +- +- *cached_page = page; +- usleep_range(1000, 2000); +- return 0; +-} +- +-static int +-qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) +-{ +- return regmap_read(priv->regmap, reg, val); +-} +- +-static int +-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) +-{ +- return regmap_write(priv->regmap, reg, val); +-} +- +-static int +-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +-{ +- return regmap_update_bits(priv->regmap, reg, mask, write_val); +-} +- +-static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) +-{ +- struct qca8k_mgmt_eth_data *mgmt_eth_data; +- struct qca8k_priv *priv = ds->priv; +- struct qca_mgmt_ethhdr *mgmt_ethhdr; +- u8 len, cmd; +- +- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); +- mgmt_eth_data = &priv->mgmt_eth_data; +- +- cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); +- len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); +- +- /* Make sure the seq match the requested packet */ +- if (mgmt_ethhdr->seq == mgmt_eth_data->seq) +- mgmt_eth_data->ack = true; +- +- if (cmd == MDIO_READ) { +- mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; +- +- /* Get the rest of the 12 byte of data. +- * The read/write function will extract the requested data. +- */ +- if (len > QCA_HDR_MGMT_DATA1_LEN) +- memcpy(mgmt_eth_data->data + 1, skb->data, +- QCA_HDR_MGMT_DATA2_LEN); +- } +- +- complete(&mgmt_eth_data->rw_done); +-} +- +-static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, +- int priority, unsigned int len) +-{ +- struct qca_mgmt_ethhdr *mgmt_ethhdr; +- unsigned int real_len; +- struct sk_buff *skb; +- u32 *data2; +- u16 hdr; +- +- skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); +- if (!skb) +- return NULL; +- +- /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte +- * Actually for some reason the steps are: +- * 0: nothing +- * 1-4: first 4 byte +- * 5-6: first 12 byte +- * 7-15: all 16 byte +- */ +- if (len == 16) +- real_len = 15; +- else +- real_len = len; +- +- skb_reset_mac_header(skb); +- skb_set_network_header(skb, skb->len); +- +- mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); +- +- hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); +- hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority); +- hdr |= QCA_HDR_XMIT_FROM_CPU; +- hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); +- hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); +- +- mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); +- mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, +- QCA_HDR_MGMT_CHECK_CODE_VAL); +- +- if (cmd == MDIO_WRITE) +- mgmt_ethhdr->mdio_data = *val; +- +- mgmt_ethhdr->hdr = htons(hdr); +- +- data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); +- if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) +- memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); +- +- return skb; +-} +- +-static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) +-{ +- struct qca_mgmt_ethhdr *mgmt_ethhdr; +- +- mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; +- mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); +-} +- +-static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; +- struct sk_buff *skb; +- bool ack; +- int ret; +- +- skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, +- QCA8K_ETHERNET_MDIO_PRIORITY, len); +- if (!skb) +- return -ENOMEM; +- +- mutex_lock(&mgmt_eth_data->mutex); +- +- /* Check mgmt_master if is operational */ +- if (!priv->mgmt_master) { +- kfree_skb(skb); +- mutex_unlock(&mgmt_eth_data->mutex); +- return -EINVAL; +- } +- +- skb->dev = priv->mgmt_master; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the mdio pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); +- +- *val = mgmt_eth_data->data[0]; +- if (len > QCA_HDR_MGMT_DATA1_LEN) +- memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN); +- +- ack = mgmt_eth_data->ack; +- +- mutex_unlock(&mgmt_eth_data->mutex); +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- return 0; +-} +- +-static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; +- struct sk_buff *skb; +- bool ack; +- int ret; +- +- skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val, +- QCA8K_ETHERNET_MDIO_PRIORITY, len); +- if (!skb) +- return -ENOMEM; +- +- mutex_lock(&mgmt_eth_data->mutex); +- +- /* Check mgmt_master if is operational */ +- if (!priv->mgmt_master) { +- kfree_skb(skb); +- mutex_unlock(&mgmt_eth_data->mutex); +- return -EINVAL; +- } +- +- skb->dev = priv->mgmt_master; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the mdio pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); +- +- ack = mgmt_eth_data->ack; +- +- mutex_unlock(&mgmt_eth_data->mutex); +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- return 0; +-} +- +-static int +-qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +-{ +- u32 val = 0; +- int ret; +- +- ret = qca8k_read_eth(priv, reg, &val, sizeof(val)); +- if (ret) +- return ret; +- +- val &= ~mask; +- val |= write_val; +- +- return qca8k_write_eth(priv, reg, &val, sizeof(val)); +-} +- +-static int +-qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- +- if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- ret = regmap_read(priv->regmap, reg + (i * 4), val + i); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int +-qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- u32 tmp; +- +- if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- tmp = val[i]; +- +- ret = regmap_write(priv->regmap, reg + (i * 4), tmp); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int +-qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- int ret; +- +- if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- int ret; +- +- if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- return ret; +-} +- +-static int +-qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- u32 val; +- int ret; +- +- if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) +- return 0; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- if (ret < 0) +- goto exit; +- +- val &= ~mask; +- val |= write_val; +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +-exit: +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static const struct regmap_range qca8k_readable_ranges[] = { +- regmap_reg_range(0x0000, 0x00e4), /* Global control */ +- regmap_reg_range(0x0100, 0x0168), /* EEE control */ +- regmap_reg_range(0x0200, 0x0270), /* Parser control */ +- regmap_reg_range(0x0400, 0x0454), /* ACL */ +- regmap_reg_range(0x0600, 0x0718), /* Lookup */ +- regmap_reg_range(0x0800, 0x0b70), /* QM */ +- regmap_reg_range(0x0c00, 0x0c80), /* PKT */ +- regmap_reg_range(0x0e00, 0x0e98), /* L3 */ +- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ +- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ +- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ +- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ +- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ +- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ +- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ +- +-}; +- +-static const struct regmap_access_table qca8k_readable_table = { +- .yes_ranges = qca8k_readable_ranges, +- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), +-}; +- +-static struct regmap_config qca8k_regmap_config = { +- .reg_bits = 16, +- .val_bits = 32, +- .reg_stride = 4, +- .max_register = 0x16ac, /* end MIB - Port6 range */ +- .reg_read = qca8k_regmap_read, +- .reg_write = qca8k_regmap_write, +- .reg_update_bits = qca8k_regmap_update_bits, +- .rd_table = &qca8k_readable_table, +- .disable_locking = true, /* Locking is handled by qca8k read/write */ +- .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */ +-}; +- +-static int +-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) +-{ +- u32 val; +- +- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); +-} +- +-static int +-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) +-{ +- u32 reg[3]; +- int ret; +- +- /* load the ARL table into an array */ +- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +- if (ret) +- return ret; +- +- /* vid - 83:72 */ +- fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); +- /* aging - 67:64 */ +- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); +- /* portmask - 54:48 */ +- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); +- /* mac - 47:0 */ +- fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); +- fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); +- fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); +- fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); +- fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); +- fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); +- +- return 0; +-} +- +-static void +-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, +- u8 aging) +-{ +- u32 reg[3] = { 0 }; +- +- /* vid - 83:72 */ +- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +- /* aging - 67:64 */ +- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); +- /* portmask - 54:48 */ +- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); +- /* mac - 47:0 */ +- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); +- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); +- +- /* load the array into the ARL table */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +-} +- +-static int +-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and FDB index */ +- reg = QCA8K_ATU_FUNC_BUSY; +- reg |= cmd; +- if (port >= 0) { +- reg |= QCA8K_ATU_FUNC_PORT_EN; +- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); +- } +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_FDB_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_ATU_FUNC_FULL) +- return -1; +- } +- +- return 0; +-} +- +-static int +-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) +-{ +- int ret; +- +- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); +- if (ret < 0) +- return ret; +- +- return qca8k_fdb_read(priv, fdb); +-} +- +-static int +-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, +- u16 vid, u8 aging) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static void +-qca8k_fdb_flush(struct qca8k_priv *priv) +-{ +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); +- mutex_unlock(&priv->reg_mutex); +-} +- +-static int +-qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, +- const u8 *mac, u16 vid) +-{ +- struct qca8k_fdb fdb = { 0 }; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- +- qca8k_fdb_write(priv, vid, 0, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_fdb_read(priv, &fdb); +- if (ret < 0) +- goto exit; +- +- /* Rule exist. Delete first */ +- if (!fdb.aging) { +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- if (ret) +- goto exit; +- } +- +- /* Add port to fdb portmask */ +- fdb.port_mask |= port_mask; +- +- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, +- const u8 *mac, u16 vid) +-{ +- struct qca8k_fdb fdb = { 0 }; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- +- qca8k_fdb_write(priv, vid, 0, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +- if (ret < 0) +- goto exit; +- +- /* Rule doesn't exist. Why delete? */ +- if (!fdb.aging) { +- ret = -EINVAL; +- goto exit; +- } +- +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- if (ret) +- goto exit; +- +- /* Only port in the rule is this port. Don't re insert */ +- if (fdb.port_mask == port_mask) +- goto exit; +- +- /* Remove port from port mask */ +- fdb.port_mask &= ~port_mask; +- +- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and VLAN index */ +- reg = QCA8K_VTU_FUNC1_BUSY; +- reg |= cmd; +- reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_VLAN_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_VTU_FUNC1_FULL) +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static int +-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +-{ +- u32 reg; +- int ret; +- +- /* +- We do the right thing with VLAN 0 and treat it as untagged while +- preserving the tag on egress. +- */ +- if (vid == 0) +- return 0; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- if (untagged) +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); +- else +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); +- +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) +-{ +- u32 reg, mask; +- int ret, i; +- bool del; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); +- +- /* Check if we're the last member to be removed */ +- del = true; +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); +- +- if ((reg & mask) != mask) { +- del = false; +- break; +- } +- } +- +- if (del) { +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); +- } else { +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- } +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_mib_init(struct qca8k_priv *priv) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, +- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, +- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | +- QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- if (ret) +- goto exit; +- +- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static void +-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +-{ +- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- /* Port 0 and 6 have no internal PHY */ +- if (port > 0 && port < 6) +- mask |= QCA8K_PORT_STATUS_LINK_AUTO; +- +- if (enable) +- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +- else +- regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +-} +- +-static int +-qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, +- struct sk_buff *read_skb, u32 *val) +-{ +- struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL); +- bool ack; +- int ret; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the copy pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- ack = mgmt_eth_data->ack; +- +- if (ret <= 0) +- return -ETIMEDOUT; +- +- if (!ack) +- return -EINVAL; +- +- *val = mgmt_eth_data->data[0]; +- +- return 0; +-} +- +-static int +-qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, +- int regnum, u16 data) +-{ +- struct sk_buff *write_skb, *clear_skb, *read_skb; +- struct qca8k_mgmt_eth_data *mgmt_eth_data; +- u32 write_val, clear_val = 0, val; +- struct net_device *mgmt_master; +- int ret, ret1; +- bool ack; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- mgmt_eth_data = &priv->mgmt_eth_data; +- +- write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum); +- +- if (read) { +- write_val |= QCA8K_MDIO_MASTER_READ; +- } else { +- write_val |= QCA8K_MDIO_MASTER_WRITE; +- write_val |= QCA8K_MDIO_MASTER_DATA(data); +- } +- +- /* Prealloc all the needed skb before the lock */ +- write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val, +- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val)); +- if (!write_skb) +- return -ENOMEM; +- +- clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, +- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); +- if (!clear_skb) { +- ret = -ENOMEM; +- goto err_clear_skb; +- } +- +- read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val, +- QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); +- if (!read_skb) { +- ret = -ENOMEM; +- goto err_read_skb; +- } +- +- /* Actually start the request: +- * 1. Send mdio master packet +- * 2. Busy Wait for mdio master command +- * 3. Get the data if we are reading +- * 4. Reset the mdio master (even with error) +- */ +- mutex_lock(&mgmt_eth_data->mutex); +- +- /* Check if mgmt_master is operational */ +- mgmt_master = priv->mgmt_master; +- if (!mgmt_master) { +- mutex_unlock(&mgmt_eth_data->mutex); +- ret = -EINVAL; +- goto err_mgmt_master; +- } +- +- read_skb->dev = mgmt_master; +- clear_skb->dev = mgmt_master; +- write_skb->dev = mgmt_master; +- +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the write pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(write_skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- ack = mgmt_eth_data->ack; +- +- if (ret <= 0) { +- ret = -ETIMEDOUT; +- kfree_skb(read_skb); +- goto exit; +- } +- +- if (!ack) { +- ret = -EINVAL; +- kfree_skb(read_skb); +- goto exit; +- } +- +- ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, +- !(val & QCA8K_MDIO_MASTER_BUSY), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- mgmt_eth_data, read_skb, &val); +- +- if (ret < 0 && ret1 < 0) { +- ret = ret1; +- goto exit; +- } +- +- if (read) { +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the read pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(read_skb); +- +- ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- ack = mgmt_eth_data->ack; +- +- if (ret <= 0) { +- ret = -ETIMEDOUT; +- goto exit; +- } +- +- if (!ack) { +- ret = -EINVAL; +- goto exit; +- } +- +- ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK; +- } else { +- kfree_skb(read_skb); +- } +-exit: +- reinit_completion(&mgmt_eth_data->rw_done); +- +- /* Increment seq_num and set it in the clear pkt */ +- mgmt_eth_data->seq++; +- qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq); +- mgmt_eth_data->ack = false; +- +- dev_queue_xmit(clear_skb); +- +- wait_for_completion_timeout(&mgmt_eth_data->rw_done, +- QCA8K_ETHERNET_TIMEOUT); +- +- mutex_unlock(&mgmt_eth_data->mutex); +- +- return ret; +- +- /* Error handling before lock */ +-err_mgmt_master: +- kfree_skb(read_skb); +-err_read_skb: +- kfree_skb(clear_skb); +-err_clear_skb: +- kfree_skb(write_skb); +- +- return ret; +-} +- +-static u32 +-qca8k_port_to_phy(int port) +-{ +- /* From Andrew Lunn: +- * Port 0 has no internal phy. +- * Port 1 has an internal PHY at MDIO address 0. +- * Port 2 has an internal PHY at MDIO address 1. +- * ... +- * Port 5 has an internal PHY at MDIO address 4. +- * Port 6 has no internal PHY. +- */ +- +- return port - 1; +-} +- +-static int +-qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) +-{ +- u16 r1, r2, page; +- u32 val; +- int ret, ret1; +- +- qca8k_split_addr(reg, &r1, &r2, &page); +- +- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, +- bus, 0x10 | r2, r1, &val); +- +- /* Check if qca8k_read has failed for a different reason +- * before returnting -ETIMEDOUT +- */ +- if (ret < 0 && ret1 < 0) +- return ret1; +- +- return ret; +-} +- +-static int +-qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) +-{ +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- u32 val; +- int ret; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum) | +- QCA8K_MDIO_MASTER_DATA(data); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); +- +-exit: +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- return ret; +-} +- +-static int +-qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) +-{ +- struct mii_bus *bus = priv->bus; +- u16 r1, r2, page; +- u32 val; +- int ret; +- +- if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) +- return -EINVAL; +- +- val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | +- QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | +- QCA8K_MDIO_MASTER_REG_ADDR(regnum); +- +- qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); +- +- mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); +- +- ret = qca8k_set_page(priv, page); +- if (ret) +- goto exit; +- +- qca8k_mii_write32(priv, 0x10 | r2, r1, val); +- +- ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_BUSY); +- if (ret) +- goto exit; +- +- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); +- +-exit: +- /* even if the busy_wait timeouts try to clear the MASTER_EN */ +- qca8k_mii_write32(priv, 0x10 | r2, r1, 0); +- +- mutex_unlock(&bus->mdio_lock); +- +- if (ret >= 0) +- ret = val & QCA8K_MDIO_MASTER_DATA_MASK; +- +- return ret; +-} +- +-static int +-qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) +-{ +- struct qca8k_priv *priv = slave_bus->priv; +- int ret; +- +- /* Use mdio Ethernet when available, fallback to legacy one on error */ +- ret = qca8k_phy_eth_command(priv, false, phy, regnum, data); +- if (!ret) +- return 0; +- +- return qca8k_mdio_write(priv, phy, regnum, data); +-} +- +-static int +-qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) +-{ +- struct qca8k_priv *priv = slave_bus->priv; +- int ret; +- +- /* Use mdio Ethernet when available, fallback to legacy one on error */ +- ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0); +- if (ret >= 0) +- return ret; +- +- ret = qca8k_mdio_read(priv, phy, regnum); +- +- if (ret < 0) +- return 0xffff; +- +- return ret; +-} +- +-static int +-qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_write(slave_bus, port, regnum, data); +-} +- +-static int +-qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) +-{ +- port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; +- +- return qca8k_internal_mdio_read(slave_bus, port, regnum); +-} +- +-static int +-qca8k_mdio_register(struct qca8k_priv *priv) +-{ +- struct dsa_switch *ds = priv->ds; +- struct device_node *mdio; +- struct mii_bus *bus; +- +- bus = devm_mdiobus_alloc(ds->dev); +- if (!bus) +- return -ENOMEM; +- +- bus->priv = (void *)priv; +- snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", +- ds->dst->index, ds->index); +- bus->parent = ds->dev; +- bus->phy_mask = ~ds->phys_mii_mask; +- ds->slave_mii_bus = bus; +- +- /* Check if the devicetree declare the port:phy mapping */ +- mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (of_device_is_available(mdio)) { +- bus->name = "qca8k slave mii"; +- bus->read = qca8k_internal_mdio_read; +- bus->write = qca8k_internal_mdio_write; +- return devm_of_mdiobus_register(priv->dev, bus, mdio); +- } +- +- /* If a mapping can't be found the legacy mapping is used, +- * using the qca8k_port_to_phy function +- */ +- bus->name = "qca8k-legacy slave mii"; +- bus->read = qca8k_legacy_mdio_read; +- bus->write = qca8k_legacy_mdio_write; +- return devm_mdiobus_register(priv->dev, bus); +-} +- +-static int +-qca8k_setup_mdio_bus(struct qca8k_priv *priv) +-{ +- u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; +- struct device_node *ports, *port; +- phy_interface_t mode; +- int err; +- +- ports = of_get_child_by_name(priv->dev->of_node, "ports"); +- if (!ports) +- ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); +- +- if (!ports) +- return -EINVAL; +- +- for_each_available_child_of_node(ports, port) { +- err = of_property_read_u32(port, "reg", ®); +- if (err) { +- of_node_put(port); +- of_node_put(ports); +- return err; +- } +- +- if (!dsa_is_user_port(priv->ds, reg)) +- continue; +- +- of_get_phy_mode(port, &mode); +- +- if (of_property_read_bool(port, "phy-handle") && +- mode != PHY_INTERFACE_MODE_INTERNAL) +- external_mdio_mask |= BIT(reg); +- else +- internal_mdio_mask |= BIT(reg); +- } +- +- of_node_put(ports); +- if (!external_mdio_mask && !internal_mdio_mask) { +- dev_err(priv->dev, "no PHYs are defined.\n"); +- return -EINVAL; +- } +- +- /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through +- * the MDIO_MASTER register also _disconnects_ the external MDC +- * passthrough to the internal PHYs. It's not possible to use both +- * configurations at the same time! +- * +- * Because this came up during the review process: +- * If the external mdio-bus driver is capable magically disabling +- * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's +- * accessors for the time being, it would be possible to pull this +- * off. +- */ +- if (!!external_mdio_mask && !!internal_mdio_mask) { +- dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); +- return -EINVAL; +- } +- +- if (external_mdio_mask) { +- /* Make sure to disable the internal mdio bus in cases +- * a dt-overlay and driver reload changed the configuration +- */ +- +- return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, +- QCA8K_MDIO_MASTER_EN); +- } +- +- return qca8k_mdio_register(priv); +-} +- +-static int +-qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv) +-{ +- u32 mask = 0; +- int ret = 0; +- +- /* SoC specific settings for ipq8064. +- * If more device require this consider adding +- * a dedicated binding. +- */ +- if (of_machine_is_compatible("qcom,ipq8064")) +- mask |= QCA8K_MAC_PWR_RGMII0_1_8V; +- +- /* SoC specific settings for ipq8065 */ +- if (of_machine_is_compatible("qcom,ipq8065")) +- mask |= QCA8K_MAC_PWR_RGMII1_1_8V; +- +- if (mask) { +- ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL, +- QCA8K_MAC_PWR_RGMII0_1_8V | +- QCA8K_MAC_PWR_RGMII1_1_8V, +- mask); +- } +- +- return ret; +-} +- +-static int qca8k_find_cpu_port(struct dsa_switch *ds) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- /* Find the connected cpu port. Valid port are 0 or 6 */ +- if (dsa_is_cpu_port(ds, 0)) +- return 0; +- +- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6"); +- +- if (dsa_is_cpu_port(ds, 6)) +- return 6; +- +- return -EINVAL; +-} +- +-static int +-qca8k_setup_of_pws_reg(struct qca8k_priv *priv) +-{ +- const struct qca8k_match_data *data = priv->info; +- struct device_node *node = priv->dev->of_node; +- u32 val = 0; +- int ret; +- +- /* QCA8327 require to set to the correct mode. +- * His bigger brother QCA8328 have the 172 pin layout. +- * Should be applied by default but we set this just to make sure. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- /* Set the correct package of 148 pin for QCA8327 */ +- if (data->reduced_package) +- val |= QCA8327_PWS_PACKAGE148_EN; +- +- ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN, +- val); +- if (ret) +- return ret; +- } +- +- if (of_property_read_bool(node, "qca,ignore-power-on-sel")) +- val |= QCA8K_PWS_POWER_ON_SEL; +- +- if (of_property_read_bool(node, "qca,led-open-drain")) { +- if (!(val & QCA8K_PWS_POWER_ON_SEL)) { +- dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set."); +- return -EINVAL; +- } +- +- val |= QCA8K_PWS_LED_OPEN_EN_CSR; +- } +- +- return qca8k_rmw(priv, QCA8K_REG_PWS, +- QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL, +- val); +-} +- +-static int +-qca8k_parse_port_config(struct qca8k_priv *priv) +-{ +- int port, cpu_port_index = -1, ret; +- struct device_node *port_dn; +- phy_interface_t mode; +- struct dsa_port *dp; +- u32 delay; +- +- /* We have 2 CPU port. Check them */ +- for (port = 0; port < QCA8K_NUM_PORTS; port++) { +- /* Skip every other port */ +- if (port != 0 && port != 6) +- continue; +- +- dp = dsa_to_port(priv->ds, port); +- port_dn = dp->dn; +- cpu_port_index++; +- +- if (!of_device_is_available(port_dn)) +- continue; +- +- ret = of_get_phy_mode(port_dn, &mode); +- if (ret) +- continue; +- +- switch (mode) { +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- case PHY_INTERFACE_MODE_SGMII: +- delay = 0; +- +- if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay)) +- /* Switch regs accept value in ns, convert ps to ns */ +- delay = delay / 1000; +- else if (mode == PHY_INTERFACE_MODE_RGMII_ID || +- mode == PHY_INTERFACE_MODE_RGMII_TXID) +- delay = 1; +- +- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) { +- dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); +- delay = 3; +- } +- +- priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay; +- +- delay = 0; +- +- if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay)) +- /* Switch regs accept value in ns, convert ps to ns */ +- delay = delay / 1000; +- else if (mode == PHY_INTERFACE_MODE_RGMII_ID || +- mode == PHY_INTERFACE_MODE_RGMII_RXID) +- delay = 2; +- +- if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) { +- dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); +- delay = 3; +- } +- +- priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; +- +- /* Skip sgmii parsing for rgmii* mode */ +- if (mode == PHY_INTERFACE_MODE_RGMII || +- mode == PHY_INTERFACE_MODE_RGMII_ID || +- mode == PHY_INTERFACE_MODE_RGMII_TXID || +- mode == PHY_INTERFACE_MODE_RGMII_RXID) +- break; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) +- priv->ports_config.sgmii_tx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) +- priv->ports_config.sgmii_rx_clk_falling_edge = true; +- +- if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { +- priv->ports_config.sgmii_enable_pll = true; +- +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling"); +- priv->ports_config.sgmii_enable_pll = false; +- } +- +- if (priv->switch_revision < 2) +- dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more."); +- } +- +- break; +- default: +- continue; +- } +- } +- +- return 0; +-} +- +-static int +-qca8k_setup(struct dsa_switch *ds) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, ret, i; +- u32 mask; +- +- cpu_port = qca8k_find_cpu_port(ds); +- if (cpu_port < 0) { +- dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); +- return cpu_port; +- } +- +- /* Parse CPU port config to be later used in phy_link mac_config */ +- ret = qca8k_parse_port_config(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_mdio_bus(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_of_pws_reg(priv); +- if (ret) +- return ret; +- +- ret = qca8k_setup_mac_pwr_sel(priv); +- if (ret) +- return ret; +- +- /* Make sure MAC06 is disabled */ +- ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, +- QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); +- if (ret) { +- dev_err(priv->dev, "failed disabling MAC06 exchange"); +- return ret; +- } +- +- /* Enable CPU Port */ +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); +- if (ret) { +- dev_err(priv->dev, "failed enabling CPU port"); +- return ret; +- } +- +- /* Enable MIB counters */ +- ret = qca8k_mib_init(priv); +- if (ret) +- dev_warn(priv->dev, "mib init failed"); +- +- /* Initial setup of all ports */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- /* Disable forwarding by default on all ports */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, 0); +- if (ret) +- return ret; +- +- /* Enable QCA header mode on all cpu ports */ +- if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), +- FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | +- FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); +- if (ret) { +- dev_err(priv->dev, "failed enabling QCA header mode"); +- return ret; +- } +- } +- +- /* Disable MAC by default on all user ports */ +- if (dsa_is_user_port(ds, i)) +- qca8k_port_set_status(priv, i, 0); +- } +- +- /* Forward all unknown frames to CPU port for Linux processing +- * Notice that in multi-cpu config only one port should be set +- * for igmp, unknown, multicast and broadcast packet +- */ +- ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | +- FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); +- if (ret) +- return ret; +- +- /* Setup connection between CPU port & user ports +- * Configure specific switch configuration for ports +- */ +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- /* CPU port gets connected to all user ports of the switch */ +- if (dsa_is_cpu_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); +- if (ret) +- return ret; +- } +- +- /* Individual user ports get connected to CPU port only */ +- if (dsa_is_user_port(ds, i)) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_MEMBER, +- BIT(cpu_port)); +- if (ret) +- return ret; +- +- /* Enable ARP Auto-learning by default */ +- ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), +- QCA8K_PORT_LOOKUP_LEARN); +- if (ret) +- return ret; +- +- /* For port based vlans to work we need to set the +- * default egress vid +- */ +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), +- QCA8K_EGREES_VLAN_PORT_MASK(i), +- QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), +- QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | +- QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); +- if (ret) +- return ret; +- } +- +- /* The port 5 of the qca8337 have some problem in flood condition. The +- * original legacy driver had some specific buffer and priority settings +- * for the different port suggested by the QCA switch team. Add this +- * missing settings to improve switch stability under load condition. +- * This problem is limited to qca8337 and other qca8k switch are not affected. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8337) { +- switch (i) { +- /* The 2 CPU port and port 5 requires some different +- * priority than any other ports. +- */ +- case 0: +- case 5: +- case 6: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); +- break; +- default: +- mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | +- QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | +- QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); +- } +- qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); +- +- mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN; +- qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), +- QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | +- QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | +- QCA8K_PORT_HOL_CTRL1_WRED_EN, +- mask); +- } +- } +- +- /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ +- if (priv->switch_id == QCA8K_ID_QCA8327) { +- mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | +- QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); +- qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, +- QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | +- QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, +- mask); +- } +- +- /* Setup our port MTUs to match power on defaults */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); +- if (ret) +- dev_warn(priv->dev, "failed setting MTU settings"); +- +- /* Flush the FDB table */ +- qca8k_fdb_flush(priv); +- +- /* We don't have interrupts for link changes, so we need to poll */ +- ds->pcs_poll = true; +- +- /* Set min a max ageing value supported */ +- ds->ageing_time_min = 7000; +- ds->ageing_time_max = 458745000; +- +- /* Set max number of LAGs supported */ +- ds->num_lag_ids = QCA8K_NUM_LAGS; +- +- return 0; +-} +- +-static void +-qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index, +- u32 reg) +-{ +- u32 delay, val = 0; +- int ret; +- +- /* Delay can be declared in 3 different way. +- * Mode to rgmii and internal-delay standard binding defined +- * rgmii-id or rgmii-tx/rx phy mode set. +- * The parse logic set a delay different than 0 only when one +- * of the 3 different way is used. In all other case delay is +- * not enabled. With ID or TX/RXID delay is enabled and set +- * to the default and recommended value. +- */ +- if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) { +- delay = priv->ports_config.rgmii_tx_delay[cpu_port_index]; +- +- val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | +- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; +- } +- +- if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) { +- delay = priv->ports_config.rgmii_rx_delay[cpu_port_index]; +- +- val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; +- } +- +- /* Set RGMII delay based on the selected values */ +- ret = qca8k_rmw(priv, reg, +- QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK | +- QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN, +- val); +- if (ret) +- dev_err(priv->dev, "Failed to set internal delay for CPU port%d", +- cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6); +-} +- +-static void +-qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +- const struct phylink_link_state *state) +-{ +- struct qca8k_priv *priv = ds->priv; +- int cpu_port_index, ret; +- u32 reg, val; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- return; +- +- reg = QCA8K_REG_PORT0_PAD_CTRL; +- cpu_port_index = QCA8K_CPU_PORT0; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY, nothing to do */ +- return; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- return; +- +- reg = QCA8K_REG_PORT6_PAD_CTRL; +- cpu_port_index = QCA8K_CPU_PORT6; +- break; +- default: +- dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); +- return; +- } +- +- if (port != 6 && phylink_autoneg_inband(mode)) { +- dev_err(ds->dev, "%s: in-band negotiation unsupported\n", +- __func__); +- return; +- } +- +- switch (state->interface) { +- case PHY_INTERFACE_MODE_RGMII: +- case PHY_INTERFACE_MODE_RGMII_ID: +- case PHY_INTERFACE_MODE_RGMII_TXID: +- case PHY_INTERFACE_MODE_RGMII_RXID: +- qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); +- +- /* Configure rgmii delay */ +- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); +- +- /* QCA8337 requires to set rgmii rx delay for all ports. +- * This is enabled through PORT5_PAD_CTRL for all ports, +- * rather than individual port registers. +- */ +- if (priv->switch_id == QCA8K_ID_QCA8337) +- qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, +- QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); +- break; +- case PHY_INTERFACE_MODE_SGMII: +- case PHY_INTERFACE_MODE_1000BASEX: +- /* Enable SGMII on the port */ +- qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); +- +- /* Enable/disable SerDes auto-negotiation as necessary */ +- ret = qca8k_read(priv, QCA8K_REG_PWS, &val); +- if (ret) +- return; +- if (phylink_autoneg_inband(mode)) +- val &= ~QCA8K_PWS_SERDES_AEN_DIS; +- else +- val |= QCA8K_PWS_SERDES_AEN_DIS; +- qca8k_write(priv, QCA8K_REG_PWS, val); +- +- /* Configure the SGMII parameters */ +- ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); +- if (ret) +- return; +- +- val |= QCA8K_SGMII_EN_SD; +- +- if (priv->ports_config.sgmii_enable_pll) +- val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | +- QCA8K_SGMII_EN_TX; +- +- if (dsa_is_cpu_port(ds, port)) { +- /* CPU port, we're talking to the CPU MAC, be a PHY */ +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_PHY; +- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_MAC; +- } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { +- val &= ~QCA8K_SGMII_MODE_CTRL_MASK; +- val |= QCA8K_SGMII_MODE_CTRL_BASEX; +- } +- +- qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); +- +- /* From original code is reported port instability as SGMII also +- * require delay set. Apply advised values here or take them from DT. +- */ +- if (state->interface == PHY_INTERFACE_MODE_SGMII) +- qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); +- +- /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and +- * falling edge is set writing in the PORT0 PAD reg +- */ +- if (priv->switch_id == QCA8K_ID_QCA8327 || +- priv->switch_id == QCA8K_ID_QCA8337) +- reg = QCA8K_REG_PORT0_PAD_CTRL; +- +- val = 0; +- +- /* SGMII Clock phase configuration */ +- if (priv->ports_config.sgmii_rx_clk_falling_edge) +- val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; +- +- if (priv->ports_config.sgmii_tx_clk_falling_edge) +- val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; +- +- if (val) +- ret = qca8k_rmw(priv, reg, +- QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | +- QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, +- val); +- +- break; +- default: +- dev_err(ds->dev, "xMII mode %s not supported for port %d\n", +- phy_modes(state->interface), port); +- return; +- } +-} +- +-static void +-qca8k_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) +-{ +- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +- +- switch (port) { +- case 0: /* 1st CPU port */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII) +- goto unsupported; +- break; +- case 1: +- case 2: +- case 3: +- case 4: +- case 5: +- /* Internal PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_GMII && +- state->interface != PHY_INTERFACE_MODE_INTERNAL) +- goto unsupported; +- break; +- case 6: /* 2nd CPU port / external PHY */ +- if (state->interface != PHY_INTERFACE_MODE_NA && +- state->interface != PHY_INTERFACE_MODE_RGMII && +- state->interface != PHY_INTERFACE_MODE_RGMII_ID && +- state->interface != PHY_INTERFACE_MODE_RGMII_TXID && +- state->interface != PHY_INTERFACE_MODE_RGMII_RXID && +- state->interface != PHY_INTERFACE_MODE_SGMII && +- state->interface != PHY_INTERFACE_MODE_1000BASEX) +- goto unsupported; +- break; +- default: +-unsupported: +- linkmode_zero(supported); +- return; +- } +- +- phylink_set_port_modes(mask); +- phylink_set(mask, Autoneg); +- +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); +- +- if (state->interface == PHY_INTERFACE_MODE_1000BASEX) +- phylink_set(mask, 1000baseX_Full); +- +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); +- +- linkmode_and(supported, supported, mask); +- linkmode_and(state->advertising, state->advertising, mask); +-} +- +-static int +-qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, +- struct phylink_link_state *state) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg; +- int ret; +- +- ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); +- if (ret < 0) +- return ret; +- +- state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); +- state->an_complete = state->link; +- state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); +- state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : +- DUPLEX_HALF; +- +- switch (reg & QCA8K_PORT_STATUS_SPEED) { +- case QCA8K_PORT_STATUS_SPEED_10: +- state->speed = SPEED_10; +- break; +- case QCA8K_PORT_STATUS_SPEED_100: +- state->speed = SPEED_100; +- break; +- case QCA8K_PORT_STATUS_SPEED_1000: +- state->speed = SPEED_1000; +- break; +- default: +- state->speed = SPEED_UNKNOWN; +- break; +- } +- +- state->pause = MLO_PAUSE_NONE; +- if (reg & QCA8K_PORT_STATUS_RXFLOW) +- state->pause |= MLO_PAUSE_RX; +- if (reg & QCA8K_PORT_STATUS_TXFLOW) +- state->pause |= MLO_PAUSE_TX; +- +- return 1; +-} +- +-static void +-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, +- phy_interface_t interface) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +-} +- +-static void +-qca8k_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 qca8k_priv *priv = ds->priv; +- u32 reg; +- +- if (phylink_autoneg_inband(mode)) { +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- } else { +- switch (speed) { +- case SPEED_10: +- reg = QCA8K_PORT_STATUS_SPEED_10; +- break; +- case SPEED_100: +- reg = QCA8K_PORT_STATUS_SPEED_100; +- break; +- case SPEED_1000: +- reg = QCA8K_PORT_STATUS_SPEED_1000; +- break; +- default: +- reg = QCA8K_PORT_STATUS_LINK_AUTO; +- break; +- } +- +- if (duplex == DUPLEX_FULL) +- reg |= QCA8K_PORT_STATUS_DUPLEX; +- +- if (rx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_RXFLOW; +- +- if (tx_pause || dsa_is_cpu_port(ds, port)) +- reg |= QCA8K_PORT_STATUS_TXFLOW; +- } +- +- reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); +-} +- +-static void +-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) +-{ +- struct qca8k_priv *priv = ds->priv; +- int i; +- +- if (stringset != ETH_SS_STATS) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) +- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, +- ETH_GSTRING_LEN); +-} +- +-static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) +-{ +- struct qca8k_mib_eth_data *mib_eth_data; +- struct qca8k_priv *priv = ds->priv; +- const struct qca8k_mib_desc *mib; +- struct mib_ethhdr *mib_ethhdr; +- int i, mib_len, offset = 0; +- u64 *data; +- u8 port; +- +- mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); +- mib_eth_data = &priv->mib_eth_data; +- +- /* The switch autocast every port. Ignore other packet and +- * parse only the requested one. +- */ +- port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr)); +- if (port != mib_eth_data->req_port) +- goto exit; +- +- data = mib_eth_data->data; +- +- for (i = 0; i < priv->info->mib_count; i++) { +- mib = &ar8327_mib[i]; +- +- /* First 3 mib are present in the skb head */ +- if (i < 3) { +- data[i] = mib_ethhdr->data[i]; +- continue; +- } +- +- mib_len = sizeof(uint32_t); +- +- /* Some mib are 64 bit wide */ +- if (mib->size == 2) +- mib_len = sizeof(uint64_t); +- +- /* Copy the mib value from packet to the */ +- memcpy(data + i, skb->data + offset, mib_len); +- +- /* Set the offset for the next mib */ +- offset += mib_len; +- } +- +-exit: +- /* Complete on receiving all the mib packet */ +- if (refcount_dec_and_test(&mib_eth_data->port_parsed)) +- complete(&mib_eth_data->rw_done); +-} +- +-static int +-qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data) +-{ +- struct dsa_port *dp = dsa_to_port(ds, port); +- struct qca8k_mib_eth_data *mib_eth_data; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- mib_eth_data = &priv->mib_eth_data; +- +- mutex_lock(&mib_eth_data->mutex); +- +- reinit_completion(&mib_eth_data->rw_done); +- +- mib_eth_data->req_port = dp->index; +- mib_eth_data->data = data; +- refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS); +- +- mutex_lock(&priv->reg_mutex); +- +- /* Send mib autocast request */ +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, +- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, +- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) | +- QCA8K_MIB_BUSY); +- +- mutex_unlock(&priv->reg_mutex); +- +- if (ret) +- goto exit; +- +- ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT); +- +-exit: +- mutex_unlock(&mib_eth_data->mutex); +- +- return ret; +-} +- +-static void +-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, +- uint64_t *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- const struct qca8k_mib_desc *mib; +- u32 reg, i, val; +- u32 hi = 0; +- int ret; +- +- if (priv->mgmt_master && priv->info->ops->autocast_mib && +- priv->info->ops->autocast_mib(ds, port, data) > 0) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) { +- mib = &ar8327_mib[i]; +- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; +- +- ret = qca8k_read(priv, reg, &val); +- if (ret < 0) +- continue; +- +- if (mib->size == 2) { +- ret = qca8k_read(priv, reg + 4, &hi); +- if (ret < 0) +- continue; +- } +- +- data[i] = val; +- if (mib->size == 2) +- data[i] |= (u64)hi << 32; +- } +-} +- +-static int +-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- if (sset != ETH_SS_STATS) +- return 0; +- +- return priv->info->mib_count; +-} +- +-static int +-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- u32 reg; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); +- if (ret < 0) +- goto exit; +- +- if (eee->eee_enabled) +- reg |= lpi_en; +- else +- reg &= ~lpi_en; +- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +-{ +- /* Nothing to do on the port's MAC */ +- return 0; +-} +- +-static void +-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 stp_state; +- +- switch (state) { +- case BR_STATE_DISABLED: +- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; +- break; +- case BR_STATE_BLOCKING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; +- break; +- case BR_STATE_LISTENING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; +- break; +- case BR_STATE_LEARNING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; +- break; +- case BR_STATE_FORWARDING: +- default: +- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; +- break; +- } +- +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); +-} +- +-static int +-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int port_mask, cpu_port; +- int i, ret; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- port_mask = BIT(cpu_port); +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (dsa_to_port(ds, i)->bridge_dev != br) +- continue; +- /* Add this port to the portvlan mask of the other ports +- * in the bridge +- */ +- ret = regmap_set_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- if (ret) +- return ret; +- if (i != port) +- port_mask |= BIT(i); +- } +- +- /* Add all other ports to this ports portvlan mask */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); +- +- return ret; +-} +- +-static void +-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, i; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (dsa_to_port(ds, i)->bridge_dev != br) +- continue; +- /* Remove this port to the portvlan mask of the other ports +- * in the bridge +- */ +- regmap_clear_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- } +- +- /* Set the cpu port to be the only one in the portvlan mask of +- * this port +- */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); +-} +- +-static void +-qca8k_port_fast_age(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); +- mutex_unlock(&priv->reg_mutex); +-} +- +-static int +-qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +-{ +- struct qca8k_priv *priv = ds->priv; +- unsigned int secs = msecs / 1000; +- u32 val; +- +- /* AGE_TIME reg is set in 7s step */ +- val = secs / 7; +- +- /* Handle case with 0 as val to NOT disable +- * learning +- */ +- if (!val) +- val = 1; +- +- return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, +- QCA8K_ATU_AGE_TIME(val)); +-} +- +-static int +-qca8k_port_enable(struct dsa_switch *ds, int port, +- struct phy_device *phy) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 1); +- priv->port_enabled_map |= BIT(port); +- +- if (dsa_is_user_port(ds, port)) +- phy_support_asym_pause(phy); +- +- return 0; +-} +- +-static void +-qca8k_port_disable(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +- priv->port_enabled_map &= ~BIT(port); +-} +- +-static int +-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- /* We have only have a general MTU setting. +- * DSA always set the CPU port's MTU to the largest MTU of the slave +- * ports. +- * Setting MTU just for the CPU port is sufficient to correctly set a +- * value for every port. +- */ +- if (!dsa_is_cpu_port(ds, port)) +- return 0; +- +- /* To change the MAX_FRAME_SIZE the cpu ports must be off or +- * the switch panics. +- * Turn off both cpu ports before applying the new value to prevent +- * this. +- */ +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 0); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 0); +- +- /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); +- +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 1); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 1); +- +- return ret; +-} +- +-static int +-qca8k_port_max_mtu(struct dsa_switch *ds, int port) +-{ +- return QCA8K_MAX_MTU; +-} +- +-static int +-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, +- u16 port_mask, u16 vid) +-{ +- /* Set the vid to the port vlan id if no vid is set */ +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_add(priv, addr, port_mask, vid, +- QCA8K_ATU_STATUS_STATIC); +-} +- +-static int +-qca8k_port_fdb_add(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- return qca8k_port_fdb_insert(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_del(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_del(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_dump(struct dsa_switch *ds, int port, +- dsa_fdb_dump_cb_t *cb, void *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- struct qca8k_fdb _fdb = { 0 }; +- int cnt = QCA8K_NUM_FDB_RECORDS; +- bool is_static; +- int ret = 0; +- +- mutex_lock(&priv->reg_mutex); +- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { +- if (!_fdb.aging) +- break; +- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); +- ret = cb(_fdb.mac, _fdb.vid, is_static, data); +- if (ret) +- break; +- } +- mutex_unlock(&priv->reg_mutex); +- +- return 0; +-} +- +-static int +-qca8k_port_mdb_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_mdb *mdb) +-{ +- struct qca8k_priv *priv = ds->priv; +- const u8 *addr = mdb->addr; +- u16 vid = mdb->vid; +- +- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); +-} +- +-static int +-qca8k_port_mdb_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_mdb *mdb) +-{ +- struct qca8k_priv *priv = ds->priv; +- const u8 *addr = mdb->addr; +- u16 vid = mdb->vid; +- +- return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); +-} +- +-static int +-qca8k_port_mirror_add(struct dsa_switch *ds, int port, +- struct dsa_mall_mirror_tc_entry *mirror, +- bool ingress) +-{ +- struct qca8k_priv *priv = ds->priv; +- int monitor_port, ret; +- u32 reg, val; +- +- /* Check for existent entry */ +- if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) +- return -EEXIST; +- +- ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); +- if (ret) +- return ret; +- +- /* QCA83xx can have only one port set to mirror mode. +- * Check that the correct port is requested and return error otherwise. +- * When no mirror port is set, the values is set to 0xF +- */ +- monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (monitor_port != 0xF && monitor_port != mirror->to_local_port) +- return -EEXIST; +- +- /* Set the monitor port */ +- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, +- mirror->to_local_port); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (ret) +- return ret; +- +- if (ingress) { +- reg = QCA8K_PORT_LOOKUP_CTRL(port); +- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +- } else { +- reg = QCA8K_REG_PORT_HOL_CTRL1(port); +- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +- } +- +- ret = regmap_update_bits(priv->regmap, reg, val, val); +- if (ret) +- return ret; +- +- /* Track mirror port for tx and rx to decide when the +- * mirror port has to be disabled. +- */ +- if (ingress) +- priv->mirror_rx |= BIT(port); +- else +- priv->mirror_tx |= BIT(port); +- +- return 0; +-} +- +-static void +-qca8k_port_mirror_del(struct dsa_switch *ds, int port, +- struct dsa_mall_mirror_tc_entry *mirror) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg, val; +- int ret; +- +- if (mirror->ingress) { +- reg = QCA8K_PORT_LOOKUP_CTRL(port); +- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +- } else { +- reg = QCA8K_REG_PORT_HOL_CTRL1(port); +- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +- } +- +- ret = regmap_clear_bits(priv->regmap, reg, val); +- if (ret) +- goto err; +- +- if (mirror->ingress) +- priv->mirror_rx &= ~BIT(port); +- else +- priv->mirror_tx &= ~BIT(port); +- +- /* No port set to send packet to mirror port. Disable mirror port */ +- if (!priv->mirror_rx && !priv->mirror_tx) { +- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (ret) +- goto err; +- } +-err: +- dev_err(priv->dev, "Failed to del mirror port from %d", port); +-} +- +-static int +-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +- struct netlink_ext_ack *extack) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- if (vlan_filtering) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); +- } else { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan, +- struct netlink_ext_ack *extack) +-{ +- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); +- if (ret) { +- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); +- return ret; +- } +- +- if (pvid) { +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- QCA8K_EGREES_VLAN_PORT_MASK(port), +- QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), +- QCA8K_PORT_VLAN_CVID(vlan->vid) | +- QCA8K_PORT_VLAN_SVID(vlan->vid)); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_del(priv, port, vlan->vid); +- if (ret) +- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); +- +- return ret; +-} +- +-static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- /* Communicate to the phy internal driver the switch revision. +- * Based on the switch revision different values needs to be +- * set to the dbg and mmd reg on the phy. +- * The first 2 bit are used to communicate the switch revision +- * to the phy driver. +- */ +- if (port > 0 && port < 6) +- return priv->switch_revision; +- +- return 0; +-} +- +-static enum dsa_tag_protocol +-qca8k_get_tag_protocol(struct dsa_switch *ds, int port, +- enum dsa_tag_protocol mp) +-{ +- return DSA_TAG_PROTO_QCA; +-} +- +-static bool +-qca8k_lag_can_offload(struct dsa_switch *ds, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- struct dsa_port *dp; +- int id, members = 0; +- +- id = dsa_lag_id(ds->dst, lag); +- if (id < 0 || id >= ds->num_lag_ids) +- return false; +- +- dsa_lag_foreach_port(dp, ds->dst, lag) +- /* Includes the port joining the LAG */ +- members++; +- +- if (members > QCA8K_NUM_PORTS_FOR_LAG) +- 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 +-qca8k_lag_setup_hash(struct dsa_switch *ds, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- struct qca8k_priv *priv = ds->priv; +- bool unique_lag = true; +- u32 hash = 0; +- int i, id; +- +- id = dsa_lag_id(ds->dst, lag); +- +- switch (info->hash_type) { +- case NETDEV_LAG_HASH_L23: +- hash |= QCA8K_TRUNK_HASH_SIP_EN; +- hash |= QCA8K_TRUNK_HASH_DIP_EN; +- fallthrough; +- case NETDEV_LAG_HASH_L2: +- hash |= QCA8K_TRUNK_HASH_SA_EN; +- hash |= QCA8K_TRUNK_HASH_DA_EN; +- break; +- default: /* We should NEVER reach this */ +- return -EOPNOTSUPP; +- } +- +- /* Check if we are the unique configured LAG */ +- dsa_lags_foreach_id(i, ds->dst) +- if (i != id && dsa_lag_dev(ds->dst, i)) { +- unique_lag = false; +- break; +- } +- +- /* Hash Mode is global. Make sure the same Hash Mode +- * is set to all the 4 possible lag. +- * If we are the unique LAG we can set whatever hash +- * mode we want. +- * To change hash mode it's needed to remove all LAG +- * and change the mode with the latest. +- */ +- if (unique_lag) { +- priv->lag_hash_mode = hash; +- } else if (priv->lag_hash_mode != hash) { +- netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n"); +- return -EOPNOTSUPP; +- } +- +- return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, +- QCA8K_TRUNK_HASH_MASK, hash); +-} +- +-static int +-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, +- struct net_device *lag, bool delete) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret, id, i; +- u32 val; +- +- id = dsa_lag_id(ds->dst, lag); +- +- /* Read current port member */ +- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); +- if (ret) +- return ret; +- +- /* Shift val to the correct trunk */ +- val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); +- val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; +- if (delete) +- val &= ~BIT(port); +- else +- val |= BIT(port); +- +- /* Update port member. With empty portmap disable trunk */ +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, +- QCA8K_REG_GOL_TRUNK_MEMBER(id) | +- QCA8K_REG_GOL_TRUNK_EN(id), +- !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | +- val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); +- +- /* Search empty member if adding or port on deleting */ +- for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { +- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); +- if (ret) +- return ret; +- +- val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); +- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; +- +- if (delete) { +- /* If port flagged to be disabled assume this member is +- * empty +- */ +- if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +- continue; +- +- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; +- if (val != port) +- continue; +- } else { +- /* If port flagged to be enabled assume this member is +- * already set +- */ +- if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +- continue; +- } +- +- /* We have found the member to add/remove */ +- break; +- } +- +- /* Set port in the correct port mask or disable port if in delete mode */ +- return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), +- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | +- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), +- !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | +- port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); +-} +- +-static int +-qca8k_port_lag_join(struct dsa_switch *ds, int port, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- int ret; +- +- if (!qca8k_lag_can_offload(ds, lag, info)) +- return -EOPNOTSUPP; +- +- ret = qca8k_lag_setup_hash(ds, lag, info); +- if (ret) +- return ret; +- +- return qca8k_lag_refresh_portmap(ds, port, lag, false); +-} +- +-static int +-qca8k_port_lag_leave(struct dsa_switch *ds, int port, +- struct net_device *lag) +-{ +- return qca8k_lag_refresh_portmap(ds, port, lag, true); +-} +- +-static void +-qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, +- bool operational) +-{ +- struct dsa_port *dp = master->dsa_ptr; +- struct qca8k_priv *priv = ds->priv; +- +- /* Ethernet MIB/MDIO is only supported for CPU port 0 */ +- if (dp->index != 0) +- return; +- +- mutex_lock(&priv->mgmt_eth_data.mutex); +- mutex_lock(&priv->mib_eth_data.mutex); +- +- priv->mgmt_master = operational ? (struct net_device *)master : NULL; +- +- mutex_unlock(&priv->mib_eth_data.mutex); +- mutex_unlock(&priv->mgmt_eth_data.mutex); +-} +- +-static int qca8k_connect_tag_protocol(struct dsa_switch *ds, +- enum dsa_tag_protocol proto) +-{ +- struct qca_tagger_data *tagger_data; +- +- switch (proto) { +- case DSA_TAG_PROTO_QCA: +- tagger_data = ds->tagger_data; +- +- tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; +- tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; +- +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- return 0; +-} +- +-static const struct dsa_switch_ops qca8k_switch_ops = { +- .get_tag_protocol = qca8k_get_tag_protocol, +- .setup = qca8k_setup, +- .get_strings = qca8k_get_strings, +- .get_ethtool_stats = qca8k_get_ethtool_stats, +- .get_sset_count = qca8k_get_sset_count, +- .set_ageing_time = qca8k_set_ageing_time, +- .get_mac_eee = qca8k_get_mac_eee, +- .set_mac_eee = qca8k_set_mac_eee, +- .port_enable = qca8k_port_enable, +- .port_disable = qca8k_port_disable, +- .port_change_mtu = qca8k_port_change_mtu, +- .port_max_mtu = qca8k_port_max_mtu, +- .port_stp_state_set = qca8k_port_stp_state_set, +- .port_bridge_join = qca8k_port_bridge_join, +- .port_bridge_leave = qca8k_port_bridge_leave, +- .port_fast_age = qca8k_port_fast_age, +- .port_fdb_add = qca8k_port_fdb_add, +- .port_fdb_del = qca8k_port_fdb_del, +- .port_fdb_dump = qca8k_port_fdb_dump, +- .port_mdb_add = qca8k_port_mdb_add, +- .port_mdb_del = qca8k_port_mdb_del, +- .port_mirror_add = qca8k_port_mirror_add, +- .port_mirror_del = qca8k_port_mirror_del, +- .port_vlan_filtering = qca8k_port_vlan_filtering, +- .port_vlan_add = qca8k_port_vlan_add, +- .port_vlan_del = qca8k_port_vlan_del, +- .phylink_validate = qca8k_phylink_validate, +- .phylink_mac_link_state = qca8k_phylink_mac_link_state, +- .phylink_mac_config = qca8k_phylink_mac_config, +- .phylink_mac_link_down = qca8k_phylink_mac_link_down, +- .phylink_mac_link_up = qca8k_phylink_mac_link_up, +- .get_phy_flags = qca8k_get_phy_flags, +- .port_lag_join = qca8k_port_lag_join, +- .port_lag_leave = qca8k_port_lag_leave, +- .master_state_change = qca8k_master_change, +- .connect_tag_protocol = qca8k_connect_tag_protocol, +-}; +- +-static int qca8k_read_switch_id(struct qca8k_priv *priv) +-{ +- u32 val; +- u8 id; +- int ret; +- +- if (!priv->info) +- return -ENODEV; +- +- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +- if (ret < 0) +- return -ENODEV; +- +- id = QCA8K_MASK_CTRL_DEVICE_ID(val); +- if (id != priv->info->id) { +- dev_err(priv->dev, +- "Switch id detected %x but expected %x", +- id, priv->info->id); +- return -ENODEV; +- } +- +- priv->switch_id = id; +- +- /* Save revision to communicate to the internal PHY driver */ +- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); +- +- return 0; +-} +- +-static int +-qca8k_sw_probe(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv; +- int ret; +- +- /* allocate the private data struct so that we can probe the switches +- * ID register +- */ +- priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->info = of_device_get_match_data(priv->dev); +- priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- +- priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", +- GPIOD_ASIS); +- if (IS_ERR(priv->reset_gpio)) +- return PTR_ERR(priv->reset_gpio); +- +- if (priv->reset_gpio) { +- gpiod_set_value_cansleep(priv->reset_gpio, 1); +- /* The active low duration must be greater than 10 ms +- * and checkpatch.pl wants 20 ms. +- */ +- msleep(20); +- gpiod_set_value_cansleep(priv->reset_gpio, 0); +- } +- +- /* Start by setting up the register mapping */ +- priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv, +- &qca8k_regmap_config); +- if (IS_ERR(priv->regmap)) { +- dev_err(priv->dev, "regmap initialization failed"); +- return PTR_ERR(priv->regmap); +- } +- +- priv->mdio_cache.page = 0xffff; +- priv->mdio_cache.lo = 0xffff; +- priv->mdio_cache.hi = 0xffff; +- +- /* Check the detected switch id */ +- ret = qca8k_read_switch_id(priv); +- if (ret) +- return ret; +- +- priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- mutex_init(&priv->mgmt_eth_data.mutex); +- init_completion(&priv->mgmt_eth_data.rw_done); +- +- mutex_init(&priv->mib_eth_data.mutex); +- init_completion(&priv->mib_eth_data.rw_done); +- +- priv->ds->dev = &mdiodev->dev; +- priv->ds->num_ports = QCA8K_NUM_PORTS; +- priv->ds->priv = priv; +- priv->ds->ops = &qca8k_switch_ops; +- mutex_init(&priv->reg_mutex); +- dev_set_drvdata(&mdiodev->dev, priv); +- +- return dsa_register_switch(priv->ds); +-} +- +-static void +-qca8k_sw_remove(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- int i; +- +- if (!priv) +- return; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) +- qca8k_port_set_status(priv, i, 0); +- +- dsa_unregister_switch(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-static void qca8k_sw_shutdown(struct mdio_device *mdiodev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); +- +- if (!priv) +- return; +- +- dsa_switch_shutdown(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); +-} +- +-#ifdef CONFIG_PM_SLEEP +-static void +-qca8k_set_pm(struct qca8k_priv *priv, int enable) +-{ +- int port; +- +- for (port = 0; port < QCA8K_NUM_PORTS; port++) { +- /* Do not enable on resume if the port was +- * disabled before. +- */ +- if (!(priv->port_enabled_map & BIT(port))) +- continue; +- +- qca8k_port_set_status(priv, port, enable); +- } +-} +- +-static int qca8k_suspend(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 0); +- +- return dsa_switch_suspend(priv->ds); +-} +- +-static int qca8k_resume(struct device *dev) +-{ +- struct qca8k_priv *priv = dev_get_drvdata(dev); +- +- qca8k_set_pm(priv, 1); +- +- return dsa_switch_resume(priv->ds); +-} +-#endif /* CONFIG_PM_SLEEP */ +- +-static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, +- qca8k_suspend, qca8k_resume); +- +-static const struct qca8k_info_ops qca8xxx_ops = { +- .autocast_mib = qca8k_get_ethtool_stats_eth, +-}; +- +-static const struct qca8k_match_data qca8327 = { +- .id = QCA8K_ID_QCA8327, +- .reduced_package = true, +- .mib_count = QCA8K_QCA832X_MIB_COUNT, +- .ops = &qca8xxx_ops, +-}; +- +-static const struct qca8k_match_data qca8328 = { +- .id = QCA8K_ID_QCA8327, +- .mib_count = QCA8K_QCA832X_MIB_COUNT, +- .ops = &qca8xxx_ops, +-}; +- +-static const struct qca8k_match_data qca833x = { +- .id = QCA8K_ID_QCA8337, +- .mib_count = QCA8K_QCA833X_MIB_COUNT, +- .ops = &qca8xxx_ops, +-}; +- +-static const struct of_device_id qca8k_of_match[] = { +- { .compatible = "qca,qca8327", .data = &qca8327 }, +- { .compatible = "qca,qca8328", .data = &qca8328 }, +- { .compatible = "qca,qca8334", .data = &qca833x }, +- { .compatible = "qca,qca8337", .data = &qca833x }, +- { /* sentinel */ }, +-}; +- +-static struct mdio_driver qca8kmdio_driver = { +- .probe = qca8k_sw_probe, +- .remove = qca8k_sw_remove, +- .shutdown = qca8k_sw_shutdown, +- .mdiodrv.driver = { +- .name = "qca8k", +- .of_match_table = qca8k_of_match, +- .pm = &qca8k_pm_ops, +- }, +-}; +- +-mdio_module_driver(qca8kmdio_driver); +- +-MODULE_AUTHOR("Mathieu Olivari, John Crispin "); +-MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:qca8k"); +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -0,0 +1,3186 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2016 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qca8k.h" ++ ++static void ++qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) ++{ ++ regaddr >>= 1; ++ *r1 = regaddr & 0x1e; ++ ++ regaddr >>= 5; ++ *r2 = regaddr & 0x7; ++ ++ regaddr >>= 3; ++ *page = regaddr & 0x3ff; ++} ++ ++static int ++qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo) ++{ ++ u16 *cached_lo = &priv->mdio_cache.lo; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (lo == *cached_lo) ++ return 0; ++ ++ ret = bus->write(bus, phy_id, regnum, lo); ++ if (ret < 0) ++ dev_err_ratelimited(&bus->dev, ++ "failed to write qca8k 32bit lo register\n"); ++ ++ *cached_lo = lo; ++ return 0; ++} ++ ++static int ++qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi) ++{ ++ u16 *cached_hi = &priv->mdio_cache.hi; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (hi == *cached_hi) ++ return 0; ++ ++ ret = bus->write(bus, phy_id, regnum, hi); ++ if (ret < 0) ++ dev_err_ratelimited(&bus->dev, ++ "failed to write qca8k 32bit hi register\n"); ++ ++ *cached_hi = hi; ++ return 0; ++} ++ ++static int ++qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val) ++{ ++ int ret; ++ ++ ret = bus->read(bus, phy_id, regnum); ++ if (ret >= 0) { ++ *val = ret; ++ ret = bus->read(bus, phy_id, regnum + 1); ++ *val |= ret << 16; ++ } ++ ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to read qca8k 32bit register\n"); ++ *val = 0; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val) ++{ ++ u16 lo, hi; ++ int ret; ++ ++ lo = val & 0xffff; ++ hi = (u16)(val >> 16); ++ ++ ret = qca8k_set_lo(priv, phy_id, regnum, lo); ++ if (ret >= 0) ++ ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi); ++} ++ ++static int ++qca8k_set_page(struct qca8k_priv *priv, u16 page) ++{ ++ u16 *cached_page = &priv->mdio_cache.page; ++ struct mii_bus *bus = priv->bus; ++ int ret; ++ ++ if (page == *cached_page) ++ return 0; ++ ++ ret = bus->write(bus, 0x18, 0, page); ++ if (ret < 0) { ++ dev_err_ratelimited(&bus->dev, ++ "failed to set qca8k page\n"); ++ return ret; ++ } ++ ++ *cached_page = page; ++ usleep_range(1000, 2000); ++ return 0; ++} ++ ++static int ++qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) ++{ ++ return regmap_read(priv->regmap, reg, val); ++} ++ ++static int ++qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) ++{ ++ return regmap_write(priv->regmap, reg, val); ++} ++ ++static int ++qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++{ ++ return regmap_update_bits(priv->regmap, reg, mask, write_val); ++} ++ ++static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ u8 len, cmd; ++ ++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); ++ mgmt_eth_data = &priv->mgmt_eth_data; ++ ++ cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); ++ len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); ++ ++ /* Make sure the seq match the requested packet */ ++ if (mgmt_ethhdr->seq == mgmt_eth_data->seq) ++ mgmt_eth_data->ack = true; ++ ++ if (cmd == MDIO_READ) { ++ mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; ++ ++ /* Get the rest of the 12 byte of data. ++ * The read/write function will extract the requested data. ++ */ ++ if (len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(mgmt_eth_data->data + 1, skb->data, ++ QCA_HDR_MGMT_DATA2_LEN); ++ } ++ ++ complete(&mgmt_eth_data->rw_done); ++} ++ ++static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val, ++ int priority, unsigned int len) ++{ ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ unsigned int real_len; ++ struct sk_buff *skb; ++ u32 *data2; ++ u16 hdr; ++ ++ skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); ++ if (!skb) ++ return NULL; ++ ++ /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte ++ * Actually for some reason the steps are: ++ * 0: nothing ++ * 1-4: first 4 byte ++ * 5-6: first 12 byte ++ * 7-15: all 16 byte ++ */ ++ if (len == 16) ++ real_len = 15; ++ else ++ real_len = len; ++ ++ skb_reset_mac_header(skb); ++ skb_set_network_header(skb, skb->len); ++ ++ mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN); ++ ++ hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION); ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority); ++ hdr |= QCA_HDR_XMIT_FROM_CPU; ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); ++ hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); ++ ++ mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); ++ mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, ++ QCA_HDR_MGMT_CHECK_CODE_VAL); ++ ++ if (cmd == MDIO_WRITE) ++ mgmt_ethhdr->mdio_data = *val; ++ ++ mgmt_ethhdr->hdr = htons(hdr); ++ ++ data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); ++ if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); ++ ++ return skb; ++} ++ ++static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) ++{ ++ struct qca_mgmt_ethhdr *mgmt_ethhdr; ++ ++ mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; ++ mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); ++} ++ ++static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; ++ struct sk_buff *skb; ++ bool ack; ++ int ret; ++ ++ skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL, ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check mgmt_master if is operational */ ++ if (!priv->mgmt_master) { ++ kfree_skb(skb); ++ mutex_unlock(&mgmt_eth_data->mutex); ++ return -EINVAL; ++ } ++ ++ skb->dev = priv->mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the mdio pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); ++ ++ *val = mgmt_eth_data->data[0]; ++ if (len > QCA_HDR_MGMT_DATA1_LEN) ++ memcpy(val + 1, mgmt_eth_data->data + 1, len - QCA_HDR_MGMT_DATA1_LEN); ++ ++ ack = mgmt_eth_data->ack; ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data; ++ struct sk_buff *skb; ++ bool ack; ++ int ret; ++ ++ skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, val, ++ QCA8K_ETHERNET_MDIO_PRIORITY, len); ++ if (!skb) ++ return -ENOMEM; ++ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check mgmt_master if is operational */ ++ if (!priv->mgmt_master) { ++ kfree_skb(skb); ++ mutex_unlock(&mgmt_eth_data->mutex); ++ return -EINVAL; ++ } ++ ++ skb->dev = priv->mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the mdio pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT)); ++ ++ ack = mgmt_eth_data->ack; ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int ++qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++{ ++ u32 val = 0; ++ int ret; ++ ++ ret = qca8k_read_eth(priv, reg, &val, sizeof(val)); ++ if (ret) ++ return ret; ++ ++ val &= ~mask; ++ val |= write_val; ++ ++ return qca8k_write_eth(priv, reg, &val, sizeof(val)); ++} ++ ++static int ++qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ ++ if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ u32 tmp; ++ ++ if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ tmp = val[i]; ++ ++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ int ret; ++ ++ if (!qca8k_read_eth(priv, reg, val, sizeof(*val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ int ret; ++ ++ if (!qca8k_write_eth(priv, reg, &val, sizeof(val))) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ return ret; ++} ++ ++static int ++qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ctx; ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val)) ++ return 0; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ if (ret < 0) ++ goto exit; ++ ++ val &= ~mask; ++ val |= write_val; ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++exit: ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static const struct regmap_range qca8k_readable_ranges[] = { ++ regmap_reg_range(0x0000, 0x00e4), /* Global control */ ++ regmap_reg_range(0x0100, 0x0168), /* EEE control */ ++ regmap_reg_range(0x0200, 0x0270), /* Parser control */ ++ regmap_reg_range(0x0400, 0x0454), /* ACL */ ++ regmap_reg_range(0x0600, 0x0718), /* Lookup */ ++ regmap_reg_range(0x0800, 0x0b70), /* QM */ ++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */ ++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */ ++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ ++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ ++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ ++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ ++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ ++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ ++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ ++ ++}; ++ ++static const struct regmap_access_table qca8k_readable_table = { ++ .yes_ranges = qca8k_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), ++}; ++ ++static struct regmap_config qca8k_regmap_config = { ++ .reg_bits = 16, ++ .val_bits = 32, ++ .reg_stride = 4, ++ .max_register = 0x16ac, /* end MIB - Port6 range */ ++ .reg_read = qca8k_regmap_read, ++ .reg_write = qca8k_regmap_write, ++ .reg_update_bits = qca8k_regmap_update_bits, ++ .rd_table = &qca8k_readable_table, ++ .disable_locking = true, /* Locking is handled by qca8k read/write */ ++ .cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */ ++}; ++ ++static int ++qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++{ ++ u32 val; ++ ++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); ++} ++ ++static int ++qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) ++{ ++ u32 reg[3]; ++ int ret; ++ ++ /* load the ARL table into an array */ ++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++ if (ret) ++ return ret; ++ ++ /* vid - 83:72 */ ++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); ++ /* aging - 67:64 */ ++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); ++ /* portmask - 54:48 */ ++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); ++ /* mac - 47:0 */ ++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); ++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); ++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); ++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); ++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); ++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); ++ ++ return 0; ++} ++ ++static void ++qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, ++ u8 aging) ++{ ++ u32 reg[3] = { 0 }; ++ ++ /* vid - 83:72 */ ++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); ++ /* aging - 67:64 */ ++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); ++ /* portmask - 54:48 */ ++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); ++ /* mac - 47:0 */ ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); ++ ++ /* load the array into the ARL table */ ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++} ++ ++static int ++qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and FDB index */ ++ reg = QCA8K_ATU_FUNC_BUSY; ++ reg |= cmd; ++ if (port >= 0) { ++ reg |= QCA8K_ATU_FUNC_PORT_EN; ++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); ++ } ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_FDB_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_ATU_FUNC_FULL) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) ++{ ++ int ret; ++ ++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); ++ if (ret < 0) ++ return ret; ++ ++ return qca8k_fdb_read(priv, fdb); ++} ++ ++static int ++qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, ++ u16 vid, u8 aging) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static void ++qca8k_fdb_flush(struct qca8k_priv *priv) ++{ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int ++qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_fdb_read(priv, &fdb); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule exist. Delete first */ ++ if (!fdb.aging) { ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ } ++ ++ /* Add port to fdb portmask */ ++ fdb.port_mask |= port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule doesn't exist. Why delete? */ ++ if (!fdb.aging) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ ++ /* Only port in the rule is this port. Don't re insert */ ++ if (fdb.port_mask == port_mask) ++ goto exit; ++ ++ /* Remove port from port mask */ ++ fdb.port_mask &= ~port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and VLAN index */ ++ reg = QCA8K_VTU_FUNC1_BUSY; ++ reg |= cmd; ++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_VLAN_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_VTU_FUNC1_FULL) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) ++{ ++ u32 reg; ++ int ret; ++ ++ /* ++ We do the right thing with VLAN 0 and treat it as untagged while ++ preserving the tag on egress. ++ */ ++ if (vid == 0) ++ return 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ if (untagged) ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); ++ else ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); ++ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) ++{ ++ u32 reg, mask; ++ int ret, i; ++ bool del; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); ++ ++ /* Check if we're the last member to be removed */ ++ del = true; ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); ++ ++ if ((reg & mask) != mask) { ++ del = false; ++ break; ++ } ++ } ++ ++ if (del) { ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); ++ } else { ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ } ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int ++qca8k_mib_init(struct qca8k_priv *priv) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, ++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, ++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | ++ QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static void ++qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) ++{ ++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ /* Port 0 and 6 have no internal PHY */ ++ if (port > 0 && port < 6) ++ mask |= QCA8K_PORT_STATUS_LINK_AUTO; ++ ++ if (enable) ++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++ else ++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++} ++ ++static int ++qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, ++ struct sk_buff *read_skb, u32 *val) ++{ ++ struct sk_buff *skb = skb_copy(read_skb, GFP_KERNEL); ++ bool ack; ++ int ret; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the copy pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) ++ return -ETIMEDOUT; ++ ++ if (!ack) ++ return -EINVAL; ++ ++ *val = mgmt_eth_data->data[0]; ++ ++ return 0; ++} ++ ++static int ++qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, ++ int regnum, u16 data) ++{ ++ struct sk_buff *write_skb, *clear_skb, *read_skb; ++ struct qca8k_mgmt_eth_data *mgmt_eth_data; ++ u32 write_val, clear_val = 0, val; ++ struct net_device *mgmt_master; ++ int ret, ret1; ++ bool ack; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ mgmt_eth_data = &priv->mgmt_eth_data; ++ ++ write_val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum); ++ ++ if (read) { ++ write_val |= QCA8K_MDIO_MASTER_READ; ++ } else { ++ write_val |= QCA8K_MDIO_MASTER_WRITE; ++ write_val |= QCA8K_MDIO_MASTER_DATA(data); ++ } ++ ++ /* Prealloc all the needed skb before the lock */ ++ write_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &write_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(write_val)); ++ if (!write_skb) ++ return -ENOMEM; ++ ++ clear_skb = qca8k_alloc_mdio_header(MDIO_WRITE, QCA8K_MDIO_MASTER_CTRL, &clear_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); ++ if (!clear_skb) { ++ ret = -ENOMEM; ++ goto err_clear_skb; ++ } ++ ++ read_skb = qca8k_alloc_mdio_header(MDIO_READ, QCA8K_MDIO_MASTER_CTRL, &clear_val, ++ QCA8K_ETHERNET_PHY_PRIORITY, sizeof(clear_val)); ++ if (!read_skb) { ++ ret = -ENOMEM; ++ goto err_read_skb; ++ } ++ ++ /* Actually start the request: ++ * 1. Send mdio master packet ++ * 2. Busy Wait for mdio master command ++ * 3. Get the data if we are reading ++ * 4. Reset the mdio master (even with error) ++ */ ++ mutex_lock(&mgmt_eth_data->mutex); ++ ++ /* Check if mgmt_master is operational */ ++ mgmt_master = priv->mgmt_master; ++ if (!mgmt_master) { ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ret = -EINVAL; ++ goto err_mgmt_master; ++ } ++ ++ read_skb->dev = mgmt_master; ++ clear_skb->dev = mgmt_master; ++ write_skb->dev = mgmt_master; ++ ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the write pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(write_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(write_skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ kfree_skb(read_skb); ++ goto exit; ++ } ++ ++ if (!ack) { ++ ret = -EINVAL; ++ kfree_skb(read_skb); ++ goto exit; ++ } ++ ++ ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, ++ !(val & QCA8K_MDIO_MASTER_BUSY), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ mgmt_eth_data, read_skb, &val); ++ ++ if (ret < 0 && ret1 < 0) { ++ ret = ret1; ++ goto exit; ++ } ++ ++ if (read) { ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the read pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(read_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(read_skb); ++ ++ ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ ack = mgmt_eth_data->ack; ++ ++ if (ret <= 0) { ++ ret = -ETIMEDOUT; ++ goto exit; ++ } ++ ++ if (!ack) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = mgmt_eth_data->data[0] & QCA8K_MDIO_MASTER_DATA_MASK; ++ } else { ++ kfree_skb(read_skb); ++ } ++exit: ++ reinit_completion(&mgmt_eth_data->rw_done); ++ ++ /* Increment seq_num and set it in the clear pkt */ ++ mgmt_eth_data->seq++; ++ qca8k_mdio_header_fill_seq_num(clear_skb, mgmt_eth_data->seq); ++ mgmt_eth_data->ack = false; ++ ++ dev_queue_xmit(clear_skb); ++ ++ wait_for_completion_timeout(&mgmt_eth_data->rw_done, ++ QCA8K_ETHERNET_TIMEOUT); ++ ++ mutex_unlock(&mgmt_eth_data->mutex); ++ ++ return ret; ++ ++ /* Error handling before lock */ ++err_mgmt_master: ++ kfree_skb(read_skb); ++err_read_skb: ++ kfree_skb(clear_skb); ++err_clear_skb: ++ kfree_skb(write_skb); ++ ++ return ret; ++} ++ ++static u32 ++qca8k_port_to_phy(int port) ++{ ++ /* From Andrew Lunn: ++ * Port 0 has no internal phy. ++ * Port 1 has an internal PHY at MDIO address 0. ++ * Port 2 has an internal PHY at MDIO address 1. ++ * ... ++ * Port 5 has an internal PHY at MDIO address 4. ++ * Port 6 has no internal PHY. ++ */ ++ ++ return port - 1; ++} ++ ++static int ++qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) ++{ ++ u16 r1, r2, page; ++ u32 val; ++ int ret, ret1; ++ ++ qca8k_split_addr(reg, &r1, &r2, &page); ++ ++ ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, ++ bus, 0x10 | r2, r1, &val); ++ ++ /* Check if qca8k_read has failed for a different reason ++ * before returnting -ETIMEDOUT ++ */ ++ if (ret < 0 && ret1 < 0) ++ return ret1; ++ ++ return ret; ++} ++ ++static int ++qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) ++{ ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_WRITE | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum) | ++ QCA8K_MDIO_MASTER_DATA(data); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ ++exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ return ret; ++} ++ ++static int ++qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) ++{ ++ struct mii_bus *bus = priv->bus; ++ u16 r1, r2, page; ++ u32 val; ++ int ret; ++ ++ if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) ++ return -EINVAL; ++ ++ val = QCA8K_MDIO_MASTER_BUSY | QCA8K_MDIO_MASTER_EN | ++ QCA8K_MDIO_MASTER_READ | QCA8K_MDIO_MASTER_PHY_ADDR(phy) | ++ QCA8K_MDIO_MASTER_REG_ADDR(regnum); ++ ++ qca8k_split_addr(QCA8K_MDIO_MASTER_CTRL, &r1, &r2, &page); ++ ++ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); ++ ++ ret = qca8k_set_page(priv, page); ++ if (ret) ++ goto exit; ++ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, val); ++ ++ ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val); ++ ++exit: ++ /* even if the busy_wait timeouts try to clear the MASTER_EN */ ++ qca8k_mii_write32(priv, 0x10 | r2, r1, 0); ++ ++ mutex_unlock(&bus->mdio_lock); ++ ++ if (ret >= 0) ++ ret = val & QCA8K_MDIO_MASTER_DATA_MASK; ++ ++ return ret; ++} ++ ++static int ++qca8k_internal_mdio_write(struct mii_bus *slave_bus, int phy, int regnum, u16 data) ++{ ++ struct qca8k_priv *priv = slave_bus->priv; ++ int ret; ++ ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, false, phy, regnum, data); ++ if (!ret) ++ return 0; ++ ++ return qca8k_mdio_write(priv, phy, regnum, data); ++} ++ ++static int ++qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) ++{ ++ struct qca8k_priv *priv = slave_bus->priv; ++ int ret; ++ ++ /* Use mdio Ethernet when available, fallback to legacy one on error */ ++ ret = qca8k_phy_eth_command(priv, true, phy, regnum, 0); ++ if (ret >= 0) ++ return ret; ++ ++ ret = qca8k_mdio_read(priv, phy, regnum); ++ ++ if (ret < 0) ++ return 0xffff; ++ ++ return ret; ++} ++ ++static int ++qca8k_legacy_mdio_write(struct mii_bus *slave_bus, int port, int regnum, u16 data) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_write(slave_bus, port, regnum, data); ++} ++ ++static int ++qca8k_legacy_mdio_read(struct mii_bus *slave_bus, int port, int regnum) ++{ ++ port = qca8k_port_to_phy(port) % PHY_MAX_ADDR; ++ ++ return qca8k_internal_mdio_read(slave_bus, port, regnum); ++} ++ ++static int ++qca8k_mdio_register(struct qca8k_priv *priv) ++{ ++ struct dsa_switch *ds = priv->ds; ++ struct device_node *mdio; ++ struct mii_bus *bus; ++ ++ bus = devm_mdiobus_alloc(ds->dev); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->priv = (void *)priv; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "qca8k-%d.%d", ++ ds->dst->index, ds->index); ++ bus->parent = ds->dev; ++ bus->phy_mask = ~ds->phys_mii_mask; ++ ds->slave_mii_bus = bus; ++ ++ /* Check if the devicetree declare the port:phy mapping */ ++ mdio = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (of_device_is_available(mdio)) { ++ bus->name = "qca8k slave mii"; ++ bus->read = qca8k_internal_mdio_read; ++ bus->write = qca8k_internal_mdio_write; ++ return devm_of_mdiobus_register(priv->dev, bus, mdio); ++ } ++ ++ /* If a mapping can't be found the legacy mapping is used, ++ * using the qca8k_port_to_phy function ++ */ ++ bus->name = "qca8k-legacy slave mii"; ++ bus->read = qca8k_legacy_mdio_read; ++ bus->write = qca8k_legacy_mdio_write; ++ return devm_mdiobus_register(priv->dev, bus); ++} ++ ++static int ++qca8k_setup_mdio_bus(struct qca8k_priv *priv) ++{ ++ u32 internal_mdio_mask = 0, external_mdio_mask = 0, reg; ++ struct device_node *ports, *port; ++ phy_interface_t mode; ++ int err; ++ ++ ports = of_get_child_by_name(priv->dev->of_node, "ports"); ++ if (!ports) ++ ports = of_get_child_by_name(priv->dev->of_node, "ethernet-ports"); ++ ++ if (!ports) ++ return -EINVAL; ++ ++ for_each_available_child_of_node(ports, port) { ++ err = of_property_read_u32(port, "reg", ®); ++ if (err) { ++ of_node_put(port); ++ of_node_put(ports); ++ return err; ++ } ++ ++ if (!dsa_is_user_port(priv->ds, reg)) ++ continue; ++ ++ of_get_phy_mode(port, &mode); ++ ++ if (of_property_read_bool(port, "phy-handle") && ++ mode != PHY_INTERFACE_MODE_INTERNAL) ++ external_mdio_mask |= BIT(reg); ++ else ++ internal_mdio_mask |= BIT(reg); ++ } ++ ++ of_node_put(ports); ++ if (!external_mdio_mask && !internal_mdio_mask) { ++ dev_err(priv->dev, "no PHYs are defined.\n"); ++ return -EINVAL; ++ } ++ ++ /* The QCA8K_MDIO_MASTER_EN Bit, which grants access to PHYs through ++ * the MDIO_MASTER register also _disconnects_ the external MDC ++ * passthrough to the internal PHYs. It's not possible to use both ++ * configurations at the same time! ++ * ++ * Because this came up during the review process: ++ * If the external mdio-bus driver is capable magically disabling ++ * the QCA8K_MDIO_MASTER_EN and mutex/spin-locking out the qca8k's ++ * accessors for the time being, it would be possible to pull this ++ * off. ++ */ ++ if (!!external_mdio_mask && !!internal_mdio_mask) { ++ dev_err(priv->dev, "either internal or external mdio bus configuration is supported.\n"); ++ return -EINVAL; ++ } ++ ++ if (external_mdio_mask) { ++ /* Make sure to disable the internal mdio bus in cases ++ * a dt-overlay and driver reload changed the configuration ++ */ ++ ++ return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, ++ QCA8K_MDIO_MASTER_EN); ++ } ++ ++ return qca8k_mdio_register(priv); ++} ++ ++static int ++qca8k_setup_mac_pwr_sel(struct qca8k_priv *priv) ++{ ++ u32 mask = 0; ++ int ret = 0; ++ ++ /* SoC specific settings for ipq8064. ++ * If more device require this consider adding ++ * a dedicated binding. ++ */ ++ if (of_machine_is_compatible("qcom,ipq8064")) ++ mask |= QCA8K_MAC_PWR_RGMII0_1_8V; ++ ++ /* SoC specific settings for ipq8065 */ ++ if (of_machine_is_compatible("qcom,ipq8065")) ++ mask |= QCA8K_MAC_PWR_RGMII1_1_8V; ++ ++ if (mask) { ++ ret = qca8k_rmw(priv, QCA8K_REG_MAC_PWR_SEL, ++ QCA8K_MAC_PWR_RGMII0_1_8V | ++ QCA8K_MAC_PWR_RGMII1_1_8V, ++ mask); ++ } ++ ++ return ret; ++} ++ ++static int qca8k_find_cpu_port(struct dsa_switch *ds) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Find the connected cpu port. Valid port are 0 or 6 */ ++ if (dsa_is_cpu_port(ds, 0)) ++ return 0; ++ ++ dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6"); ++ ++ if (dsa_is_cpu_port(ds, 6)) ++ return 6; ++ ++ return -EINVAL; ++} ++ ++static int ++qca8k_setup_of_pws_reg(struct qca8k_priv *priv) ++{ ++ const struct qca8k_match_data *data = priv->info; ++ struct device_node *node = priv->dev->of_node; ++ u32 val = 0; ++ int ret; ++ ++ /* QCA8327 require to set to the correct mode. ++ * His bigger brother QCA8328 have the 172 pin layout. ++ * Should be applied by default but we set this just to make sure. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ /* Set the correct package of 148 pin for QCA8327 */ ++ if (data->reduced_package) ++ val |= QCA8327_PWS_PACKAGE148_EN; ++ ++ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8327_PWS_PACKAGE148_EN, ++ val); ++ if (ret) ++ return ret; ++ } ++ ++ if (of_property_read_bool(node, "qca,ignore-power-on-sel")) ++ val |= QCA8K_PWS_POWER_ON_SEL; ++ ++ if (of_property_read_bool(node, "qca,led-open-drain")) { ++ if (!(val & QCA8K_PWS_POWER_ON_SEL)) { ++ dev_err(priv->dev, "qca,led-open-drain require qca,ignore-power-on-sel to be set."); ++ return -EINVAL; ++ } ++ ++ val |= QCA8K_PWS_LED_OPEN_EN_CSR; ++ } ++ ++ return qca8k_rmw(priv, QCA8K_REG_PWS, ++ QCA8K_PWS_LED_OPEN_EN_CSR | QCA8K_PWS_POWER_ON_SEL, ++ val); ++} ++ ++static int ++qca8k_parse_port_config(struct qca8k_priv *priv) ++{ ++ int port, cpu_port_index = -1, ret; ++ struct device_node *port_dn; ++ phy_interface_t mode; ++ struct dsa_port *dp; ++ u32 delay; ++ ++ /* We have 2 CPU port. Check them */ ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Skip every other port */ ++ if (port != 0 && port != 6) ++ continue; ++ ++ dp = dsa_to_port(priv->ds, port); ++ port_dn = dp->dn; ++ cpu_port_index++; ++ ++ if (!of_device_is_available(port_dn)) ++ continue; ++ ++ ret = of_get_phy_mode(port_dn, &mode); ++ if (ret) ++ continue; ++ ++ switch (mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_SGMII: ++ delay = 0; ++ ++ if (!of_property_read_u32(port_dn, "tx-internal-delay-ps", &delay)) ++ /* Switch regs accept value in ns, convert ps to ns */ ++ delay = delay / 1000; ++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_TXID) ++ delay = 1; ++ ++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) { ++ dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value"); ++ delay = 3; ++ } ++ ++ priv->ports_config.rgmii_tx_delay[cpu_port_index] = delay; ++ ++ delay = 0; ++ ++ if (!of_property_read_u32(port_dn, "rx-internal-delay-ps", &delay)) ++ /* Switch regs accept value in ns, convert ps to ns */ ++ delay = delay / 1000; ++ else if (mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_RXID) ++ delay = 2; ++ ++ if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) { ++ dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value"); ++ delay = 3; ++ } ++ ++ priv->ports_config.rgmii_rx_delay[cpu_port_index] = delay; ++ ++ /* Skip sgmii parsing for rgmii* mode */ ++ if (mode == PHY_INTERFACE_MODE_RGMII || ++ mode == PHY_INTERFACE_MODE_RGMII_ID || ++ mode == PHY_INTERFACE_MODE_RGMII_TXID || ++ mode == PHY_INTERFACE_MODE_RGMII_RXID) ++ break; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-txclk-falling-edge")) ++ priv->ports_config.sgmii_tx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-rxclk-falling-edge")) ++ priv->ports_config.sgmii_rx_clk_falling_edge = true; ++ ++ if (of_property_read_bool(port_dn, "qca,sgmii-enable-pll")) { ++ priv->ports_config.sgmii_enable_pll = true; ++ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ dev_err(priv->dev, "SGMII PLL should NOT be enabled for qca8327. Aborting enabling"); ++ priv->ports_config.sgmii_enable_pll = false; ++ } ++ ++ if (priv->switch_revision < 2) ++ dev_warn(priv->dev, "SGMII PLL should NOT be enabled for qca8337 with revision 2 or more."); ++ } ++ ++ break; ++ default: ++ continue; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++qca8k_setup(struct dsa_switch *ds) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int cpu_port, ret, i; ++ u32 mask; ++ ++ cpu_port = qca8k_find_cpu_port(ds); ++ if (cpu_port < 0) { ++ dev_err(priv->dev, "No cpu port configured in both cpu port0 and port6"); ++ return cpu_port; ++ } ++ ++ /* Parse CPU port config to be later used in phy_link mac_config */ ++ ret = qca8k_parse_port_config(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_mdio_bus(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_of_pws_reg(priv); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_setup_mac_pwr_sel(priv); ++ if (ret) ++ return ret; ++ ++ /* Make sure MAC06 is disabled */ ++ ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, ++ QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); ++ if (ret) { ++ dev_err(priv->dev, "failed disabling MAC06 exchange"); ++ return ret; ++ } ++ ++ /* Enable CPU Port */ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); ++ if (ret) { ++ dev_err(priv->dev, "failed enabling CPU port"); ++ return ret; ++ } ++ ++ /* Enable MIB counters */ ++ ret = qca8k_mib_init(priv); ++ if (ret) ++ dev_warn(priv->dev, "mib init failed"); ++ ++ /* Initial setup of all ports */ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ /* Disable forwarding by default on all ports */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, 0); ++ if (ret) ++ return ret; ++ ++ /* Enable QCA header mode on all cpu ports */ ++ if (dsa_is_cpu_port(ds, i)) { ++ ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), ++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | ++ FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL)); ++ if (ret) { ++ dev_err(priv->dev, "failed enabling QCA header mode"); ++ return ret; ++ } ++ } ++ ++ /* Disable MAC by default on all user ports */ ++ if (dsa_is_user_port(ds, i)) ++ qca8k_port_set_status(priv, i, 0); ++ } ++ ++ /* Forward all unknown frames to CPU port for Linux processing ++ * Notice that in multi-cpu config only one port should be set ++ * for igmp, unknown, multicast and broadcast packet ++ */ ++ ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | ++ FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port))); ++ if (ret) ++ return ret; ++ ++ /* Setup connection between CPU port & user ports ++ * Configure specific switch configuration for ports ++ */ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ /* CPU port gets connected to all user ports of the switch */ ++ if (dsa_is_cpu_port(ds, i)) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); ++ if (ret) ++ return ret; ++ } ++ ++ /* Individual user ports get connected to CPU port only */ ++ if (dsa_is_user_port(ds, i)) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_MEMBER, ++ BIT(cpu_port)); ++ if (ret) ++ return ret; ++ ++ /* Enable ARP Auto-learning by default */ ++ ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), ++ QCA8K_PORT_LOOKUP_LEARN); ++ if (ret) ++ return ret; ++ ++ /* For port based vlans to work we need to set the ++ * default egress vid ++ */ ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), ++ QCA8K_EGREES_VLAN_PORT_MASK(i), ++ QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(i), ++ QCA8K_PORT_VLAN_CVID(QCA8K_PORT_VID_DEF) | ++ QCA8K_PORT_VLAN_SVID(QCA8K_PORT_VID_DEF)); ++ if (ret) ++ return ret; ++ } ++ ++ /* The port 5 of the qca8337 have some problem in flood condition. The ++ * original legacy driver had some specific buffer and priority settings ++ * for the different port suggested by the QCA switch team. Add this ++ * missing settings to improve switch stability under load condition. ++ * This problem is limited to qca8337 and other qca8k switch are not affected. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) { ++ switch (i) { ++ /* The 2 CPU port and port 5 requires some different ++ * priority than any other ports. ++ */ ++ case 0: ++ case 5: ++ case 6: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI4(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI5(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x1e); ++ break; ++ default: ++ mask = QCA8K_PORT_HOL_CTRL0_EG_PRI0(0x3) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI1(0x4) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI2(0x6) | ++ QCA8K_PORT_HOL_CTRL0_EG_PRI3(0x8) | ++ QCA8K_PORT_HOL_CTRL0_EG_PORT(0x19); ++ } ++ qca8k_write(priv, QCA8K_REG_PORT_HOL_CTRL0(i), mask); ++ ++ mask = QCA8K_PORT_HOL_CTRL1_ING(0x6) | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN; ++ qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), ++ QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK | ++ QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN | ++ QCA8K_PORT_HOL_CTRL1_WRED_EN, ++ mask); ++ } ++ } ++ ++ /* Special GLOBAL_FC_THRESH value are needed for ar8327 switch */ ++ if (priv->switch_id == QCA8K_ID_QCA8327) { ++ mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496); ++ qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, ++ QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | ++ QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, ++ mask); ++ } ++ ++ /* Setup our port MTUs to match power on defaults */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, ETH_FRAME_LEN + ETH_FCS_LEN); ++ if (ret) ++ dev_warn(priv->dev, "failed setting MTU settings"); ++ ++ /* Flush the FDB table */ ++ qca8k_fdb_flush(priv); ++ ++ /* We don't have interrupts for link changes, so we need to poll */ ++ ds->pcs_poll = true; ++ ++ /* Set min a max ageing value supported */ ++ ds->ageing_time_min = 7000; ++ ds->ageing_time_max = 458745000; ++ ++ /* Set max number of LAGs supported */ ++ ds->num_lag_ids = QCA8K_NUM_LAGS; ++ ++ return 0; ++} ++ ++static void ++qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_index, ++ u32 reg) ++{ ++ u32 delay, val = 0; ++ int ret; ++ ++ /* Delay can be declared in 3 different way. ++ * Mode to rgmii and internal-delay standard binding defined ++ * rgmii-id or rgmii-tx/rx phy mode set. ++ * The parse logic set a delay different than 0 only when one ++ * of the 3 different way is used. In all other case delay is ++ * not enabled. With ID or TX/RXID delay is enabled and set ++ * to the default and recommended value. ++ */ ++ if (priv->ports_config.rgmii_tx_delay[cpu_port_index]) { ++ delay = priv->ports_config.rgmii_tx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_TX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN; ++ } ++ ++ if (priv->ports_config.rgmii_rx_delay[cpu_port_index]) { ++ delay = priv->ports_config.rgmii_rx_delay[cpu_port_index]; ++ ++ val |= QCA8K_PORT_PAD_RGMII_RX_DELAY(delay) | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN; ++ } ++ ++ /* Set RGMII delay based on the selected values */ ++ ret = qca8k_rmw(priv, reg, ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK | ++ QCA8K_PORT_PAD_RGMII_TX_DELAY_EN | ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN, ++ val); ++ if (ret) ++ dev_err(priv->dev, "Failed to set internal delay for CPU port%d", ++ cpu_port_index == QCA8K_CPU_PORT0 ? 0 : 6); ++} ++ ++static void ++qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int cpu_port_index, ret; ++ u32 reg, val; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ return; ++ ++ reg = QCA8K_REG_PORT0_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT0; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY, nothing to do */ ++ return; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ return; ++ ++ reg = QCA8K_REG_PORT6_PAD_CTRL; ++ cpu_port_index = QCA8K_CPU_PORT6; ++ break; ++ default: ++ dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); ++ return; ++ } ++ ++ if (port != 6 && phylink_autoneg_inband(mode)) { ++ dev_err(ds->dev, "%s: in-band negotiation unsupported\n", ++ __func__); ++ return; ++ } ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_RGMII_EN); ++ ++ /* Configure rgmii delay */ ++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); ++ ++ /* QCA8337 requires to set rgmii rx delay for all ports. ++ * This is enabled through PORT5_PAD_CTRL for all ports, ++ * rather than individual port registers. ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8337) ++ qca8k_write(priv, QCA8K_REG_PORT5_PAD_CTRL, ++ QCA8K_PORT_PAD_RGMII_RX_DELAY_EN); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* Enable SGMII on the port */ ++ qca8k_write(priv, reg, QCA8K_PORT_PAD_SGMII_EN); ++ ++ /* Enable/disable SerDes auto-negotiation as necessary */ ++ ret = qca8k_read(priv, QCA8K_REG_PWS, &val); ++ if (ret) ++ return; ++ if (phylink_autoneg_inband(mode)) ++ val &= ~QCA8K_PWS_SERDES_AEN_DIS; ++ else ++ val |= QCA8K_PWS_SERDES_AEN_DIS; ++ qca8k_write(priv, QCA8K_REG_PWS, val); ++ ++ /* Configure the SGMII parameters */ ++ ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val); ++ if (ret) ++ return; ++ ++ val |= QCA8K_SGMII_EN_SD; ++ ++ if (priv->ports_config.sgmii_enable_pll) ++ val |= QCA8K_SGMII_EN_PLL | QCA8K_SGMII_EN_RX | ++ QCA8K_SGMII_EN_TX; ++ ++ if (dsa_is_cpu_port(ds, port)) { ++ /* CPU port, we're talking to the CPU MAC, be a PHY */ ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_PHY; ++ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_MAC; ++ } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { ++ val &= ~QCA8K_SGMII_MODE_CTRL_MASK; ++ val |= QCA8K_SGMII_MODE_CTRL_BASEX; ++ } ++ ++ qca8k_write(priv, QCA8K_REG_SGMII_CTRL, val); ++ ++ /* From original code is reported port instability as SGMII also ++ * require delay set. Apply advised values here or take them from DT. ++ */ ++ if (state->interface == PHY_INTERFACE_MODE_SGMII) ++ qca8k_mac_config_setup_internal_delay(priv, cpu_port_index, reg); ++ ++ /* For qca8327/qca8328/qca8334/qca8338 sgmii is unique and ++ * falling edge is set writing in the PORT0 PAD reg ++ */ ++ if (priv->switch_id == QCA8K_ID_QCA8327 || ++ priv->switch_id == QCA8K_ID_QCA8337) ++ reg = QCA8K_REG_PORT0_PAD_CTRL; ++ ++ val = 0; ++ ++ /* SGMII Clock phase configuration */ ++ if (priv->ports_config.sgmii_rx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE; ++ ++ if (priv->ports_config.sgmii_tx_clk_falling_edge) ++ val |= QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE; ++ ++ if (val) ++ ret = qca8k_rmw(priv, reg, ++ QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE | ++ QCA8K_PORT0_PAD_SGMII_TXCLK_FALLING_EDGE, ++ val); ++ ++ break; ++ default: ++ dev_err(ds->dev, "xMII mode %s not supported for port %d\n", ++ phy_modes(state->interface), port); ++ return; ++ } ++} ++ ++static void ++qca8k_phylink_validate(struct dsa_switch *ds, int port, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; ++ ++ switch (port) { ++ case 0: /* 1st CPU port */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII) ++ goto unsupported; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ /* Internal PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL) ++ goto unsupported; ++ break; ++ case 6: /* 2nd CPU port / external PHY */ ++ if (state->interface != PHY_INTERFACE_MODE_NA && ++ state->interface != PHY_INTERFACE_MODE_RGMII && ++ state->interface != PHY_INTERFACE_MODE_RGMII_ID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_TXID && ++ state->interface != PHY_INTERFACE_MODE_RGMII_RXID && ++ state->interface != PHY_INTERFACE_MODE_SGMII && ++ state->interface != PHY_INTERFACE_MODE_1000BASEX) ++ goto unsupported; ++ break; ++ default: ++unsupported: ++ linkmode_zero(supported); ++ return; ++ } ++ ++ phylink_set_port_modes(mask); ++ phylink_set(mask, Autoneg); ++ ++ phylink_set(mask, 1000baseT_Full); ++ phylink_set(mask, 10baseT_Half); ++ phylink_set(mask, 10baseT_Full); ++ phylink_set(mask, 100baseT_Half); ++ phylink_set(mask, 100baseT_Full); ++ ++ if (state->interface == PHY_INTERFACE_MODE_1000BASEX) ++ phylink_set(mask, 1000baseX_Full); ++ ++ phylink_set(mask, Pause); ++ phylink_set(mask, Asym_Pause); ++ ++ linkmode_and(supported, supported, mask); ++ linkmode_and(state->advertising, state->advertising, mask); ++} ++ ++static int ++qca8k_phylink_mac_link_state(struct dsa_switch *ds, int port, ++ struct phylink_link_state *state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ int ret; ++ ++ ret = qca8k_read(priv, QCA8K_REG_PORT_STATUS(port), ®); ++ if (ret < 0) ++ return ret; ++ ++ state->link = !!(reg & QCA8K_PORT_STATUS_LINK_UP); ++ state->an_complete = state->link; ++ state->an_enabled = !!(reg & QCA8K_PORT_STATUS_LINK_AUTO); ++ state->duplex = (reg & QCA8K_PORT_STATUS_DUPLEX) ? DUPLEX_FULL : ++ DUPLEX_HALF; ++ ++ switch (reg & QCA8K_PORT_STATUS_SPEED) { ++ case QCA8K_PORT_STATUS_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case QCA8K_PORT_STATUS_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ default: ++ state->speed = SPEED_UNKNOWN; ++ break; ++ } ++ ++ state->pause = MLO_PAUSE_NONE; ++ if (reg & QCA8K_PORT_STATUS_RXFLOW) ++ state->pause |= MLO_PAUSE_RX; ++ if (reg & QCA8K_PORT_STATUS_TXFLOW) ++ state->pause |= MLO_PAUSE_TX; ++ ++ return 1; ++} ++ ++static void ++qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, ++ phy_interface_t interface) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++} ++ ++static void ++qca8k_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 qca8k_priv *priv = ds->priv; ++ u32 reg; ++ ++ if (phylink_autoneg_inband(mode)) { ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ } else { ++ switch (speed) { ++ case SPEED_10: ++ reg = QCA8K_PORT_STATUS_SPEED_10; ++ break; ++ case SPEED_100: ++ reg = QCA8K_PORT_STATUS_SPEED_100; ++ break; ++ case SPEED_1000: ++ reg = QCA8K_PORT_STATUS_SPEED_1000; ++ break; ++ default: ++ reg = QCA8K_PORT_STATUS_LINK_AUTO; ++ break; ++ } ++ ++ if (duplex == DUPLEX_FULL) ++ reg |= QCA8K_PORT_STATUS_DUPLEX; ++ ++ if (rx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_RXFLOW; ++ ++ if (tx_pause || dsa_is_cpu_port(ds, port)) ++ reg |= QCA8K_PORT_STATUS_TXFLOW; ++ } ++ ++ reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); ++} ++ ++static void ++qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int i; ++ ++ if (stringset != ETH_SS_STATS) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) ++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) ++{ ++ struct qca8k_mib_eth_data *mib_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ const struct qca8k_mib_desc *mib; ++ struct mib_ethhdr *mib_ethhdr; ++ int i, mib_len, offset = 0; ++ u64 *data; ++ u8 port; ++ ++ mib_ethhdr = (struct mib_ethhdr *)skb_mac_header(skb); ++ mib_eth_data = &priv->mib_eth_data; ++ ++ /* The switch autocast every port. Ignore other packet and ++ * parse only the requested one. ++ */ ++ port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, ntohs(mib_ethhdr->hdr)); ++ if (port != mib_eth_data->req_port) ++ goto exit; ++ ++ data = mib_eth_data->data; ++ ++ for (i = 0; i < priv->info->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ ++ /* First 3 mib are present in the skb head */ ++ if (i < 3) { ++ data[i] = mib_ethhdr->data[i]; ++ continue; ++ } ++ ++ mib_len = sizeof(uint32_t); ++ ++ /* Some mib are 64 bit wide */ ++ if (mib->size == 2) ++ mib_len = sizeof(uint64_t); ++ ++ /* Copy the mib value from packet to the */ ++ memcpy(data + i, skb->data + offset, mib_len); ++ ++ /* Set the offset for the next mib */ ++ offset += mib_len; ++ } ++ ++exit: ++ /* Complete on receiving all the mib packet */ ++ if (refcount_dec_and_test(&mib_eth_data->port_parsed)) ++ complete(&mib_eth_data->rw_done); ++} ++ ++static int ++qca8k_get_ethtool_stats_eth(struct dsa_switch *ds, int port, u64 *data) ++{ ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct qca8k_mib_eth_data *mib_eth_data; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ mib_eth_data = &priv->mib_eth_data; ++ ++ mutex_lock(&mib_eth_data->mutex); ++ ++ reinit_completion(&mib_eth_data->rw_done); ++ ++ mib_eth_data->req_port = dp->index; ++ mib_eth_data->data = data; ++ refcount_set(&mib_eth_data->port_parsed, QCA8K_NUM_PORTS); ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ /* Send mib autocast request */ ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, ++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, ++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_CAST) | ++ QCA8K_MIB_BUSY); ++ ++ mutex_unlock(&priv->reg_mutex); ++ ++ if (ret) ++ goto exit; ++ ++ ret = wait_for_completion_timeout(&mib_eth_data->rw_done, QCA8K_ETHERNET_TIMEOUT); ++ ++exit: ++ mutex_unlock(&mib_eth_data->mutex); ++ ++ return ret; ++} ++ ++static void ++qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, ++ uint64_t *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ const struct qca8k_mib_desc *mib; ++ u32 reg, i, val; ++ u32 hi = 0; ++ int ret; ++ ++ if (priv->mgmt_master && priv->info->ops->autocast_mib && ++ priv->info->ops->autocast_mib(ds, port, data) > 0) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ ret = qca8k_read(priv, reg, &val); ++ if (ret < 0) ++ continue; ++ ++ if (mib->size == 2) { ++ ret = qca8k_read(priv, reg + 4, &hi); ++ if (ret < 0) ++ continue; ++ } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= (u64)hi << 32; ++ } ++} ++ ++static int ++qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ if (sset != ETH_SS_STATS) ++ return 0; ++ ++ return priv->info->mib_count; ++} ++ ++static int ++qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ u32 reg; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); ++ if (ret < 0) ++ goto exit; ++ ++ if (eee->eee_enabled) ++ reg |= lpi_en; ++ else ++ reg &= ~lpi_en; ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int ++qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) ++{ ++ /* Nothing to do on the port's MAC */ ++ return 0; ++} ++ ++static void ++qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u32 stp_state; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; ++ break; ++ case BR_STATE_LISTENING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; ++ break; ++ case BR_STATE_LEARNING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ default: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; ++ break; ++ } ++ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); ++} ++ ++static int ++qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int port_mask, cpu_port; ++ int i, ret; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ port_mask = BIT(cpu_port); ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; ++ if (dsa_to_port(ds, i)->bridge_dev != br) ++ continue; ++ /* Add this port to the portvlan mask of the other ports ++ * in the bridge ++ */ ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ if (ret) ++ return ret; ++ if (i != port) ++ port_mask |= BIT(i); ++ } ++ ++ /* Add all other ports to this ports portvlan mask */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ++ return ret; ++} ++ ++static void ++qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ int cpu_port, i; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; ++ if (dsa_to_port(ds, i)->bridge_dev != br) ++ continue; ++ /* Remove this port to the portvlan mask of the other ports ++ * in the bridge ++ */ ++ regmap_clear_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ } ++ ++ /* Set the cpu port to be the only one in the portvlan mask of ++ * this port ++ */ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); ++} ++ ++static void ++qca8k_port_fast_age(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int ++qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ unsigned int secs = msecs / 1000; ++ u32 val; ++ ++ /* AGE_TIME reg is set in 7s step */ ++ val = secs / 7; ++ ++ /* Handle case with 0 as val to NOT disable ++ * learning ++ */ ++ if (!val) ++ val = 1; ++ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, ++ QCA8K_ATU_AGE_TIME(val)); ++} ++ ++static int ++qca8k_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 1); ++ priv->port_enabled_map |= BIT(port); ++ ++ if (dsa_is_user_port(ds, port)) ++ phy_support_asym_pause(phy); ++ ++ return 0; ++} ++ ++static void ++qca8k_port_disable(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++ priv->port_enabled_map &= ~BIT(port); ++} ++ ++static int ++qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or ++ * the switch panics. ++ * Turn off both cpu ports before applying the new value to prevent ++ * this. ++ */ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 0); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 0); ++ ++ /* Include L2 header / FCS length */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); ++ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 1); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 1); ++ ++ return ret; ++} ++ ++static int ++qca8k_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return QCA8K_MAX_MTU; ++} ++ ++static int ++qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid) ++{ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_add(priv, addr, port_mask, vid, ++ QCA8K_ATU_STATUS_STATIC); ++} ++ ++static int ++qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ ++static int ++qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct qca8k_fdb _fdb = { 0 }; ++ int cnt = QCA8K_NUM_FDB_RECORDS; ++ bool is_static; ++ int ret = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { ++ if (!_fdb.aging) ++ break; ++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); ++ ret = cb(_fdb.mac, _fdb.vid, is_static, data); ++ if (ret) ++ break; ++ } ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++static int ++qca8k_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); ++} ++ ++static int ++qca8k_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); ++} ++ ++static int ++qca8k_port_mirror_add(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror, ++ bool ingress) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int monitor_port, ret; ++ u32 reg, val; ++ ++ /* Check for existent entry */ ++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) ++ return -EEXIST; ++ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* QCA83xx can have only one port set to mirror mode. ++ * Check that the correct port is requested and return error otherwise. ++ * When no mirror port is set, the values is set to 0xF ++ */ ++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port) ++ return -EEXIST; ++ ++ /* Set the monitor port */ ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, ++ mirror->to_local_port); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ return ret; ++ ++ if (ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_update_bits(priv->regmap, reg, val, val); ++ if (ret) ++ return ret; ++ ++ /* Track mirror port for tx and rx to decide when the ++ * mirror port has to be disabled. ++ */ ++ if (ingress) ++ priv->mirror_rx |= BIT(port); ++ else ++ priv->mirror_tx |= BIT(port); ++ ++ return 0; ++} ++ ++static void ++qca8k_port_mirror_del(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg, val; ++ int ret; ++ ++ if (mirror->ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_clear_bits(priv->regmap, reg, val); ++ if (ret) ++ goto err; ++ ++ if (mirror->ingress) ++ priv->mirror_rx &= ~BIT(port); ++ else ++ priv->mirror_tx &= ~BIT(port); ++ ++ /* No port set to send packet to mirror port. Disable mirror port */ ++ if (!priv->mirror_rx && !priv->mirror_tx) { ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ goto err; ++ } ++err: ++ dev_err(priv->dev, "Failed to del mirror port from %d", port); ++} ++ ++static int ++qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, ++ struct netlink_ext_ack *extack) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ if (vlan_filtering) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); ++ } else { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; ++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); ++ if (ret) { ++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); ++ return ret; ++ } ++ ++ if (pvid) { ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), ++ QCA8K_PORT_VLAN_CVID(vlan->vid) | ++ QCA8K_PORT_VLAN_SVID(vlan->vid)); ++ } ++ ++ return ret; ++} ++ ++static int ++qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_del(priv, port, vlan->vid); ++ if (ret) ++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); ++ ++ return ret; ++} ++ ++static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Communicate to the phy internal driver the switch revision. ++ * Based on the switch revision different values needs to be ++ * set to the dbg and mmd reg on the phy. ++ * The first 2 bit are used to communicate the switch revision ++ * to the phy driver. ++ */ ++ if (port > 0 && port < 6) ++ return priv->switch_revision; ++ ++ return 0; ++} ++ ++static enum dsa_tag_protocol ++qca8k_get_tag_protocol(struct dsa_switch *ds, int port, ++ enum dsa_tag_protocol mp) ++{ ++ return DSA_TAG_PROTO_QCA; ++} ++ ++static bool ++qca8k_lag_can_offload(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct dsa_port *dp; ++ int id, members = 0; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ if (id < 0 || id >= ds->num_lag_ids) ++ return false; ++ ++ dsa_lag_foreach_port(dp, ds->dst, lag) ++ /* Includes the port joining the LAG */ ++ members++; ++ ++ if (members > QCA8K_NUM_PORTS_FOR_LAG) ++ 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 ++qca8k_lag_setup_hash(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ bool unique_lag = true; ++ u32 hash = 0; ++ int i, id; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ switch (info->hash_type) { ++ case NETDEV_LAG_HASH_L23: ++ hash |= QCA8K_TRUNK_HASH_SIP_EN; ++ hash |= QCA8K_TRUNK_HASH_DIP_EN; ++ fallthrough; ++ case NETDEV_LAG_HASH_L2: ++ hash |= QCA8K_TRUNK_HASH_SA_EN; ++ hash |= QCA8K_TRUNK_HASH_DA_EN; ++ break; ++ default: /* We should NEVER reach this */ ++ return -EOPNOTSUPP; ++ } ++ ++ /* Check if we are the unique configured LAG */ ++ dsa_lags_foreach_id(i, ds->dst) ++ if (i != id && dsa_lag_dev(ds->dst, i)) { ++ unique_lag = false; ++ break; ++ } ++ ++ /* Hash Mode is global. Make sure the same Hash Mode ++ * is set to all the 4 possible lag. ++ * If we are the unique LAG we can set whatever hash ++ * mode we want. ++ * To change hash mode it's needed to remove all LAG ++ * and change the mode with the latest. ++ */ ++ if (unique_lag) { ++ priv->lag_hash_mode = hash; ++ } else if (priv->lag_hash_mode != hash) { ++ netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, ++ QCA8K_TRUNK_HASH_MASK, hash); ++} ++ ++static int ++qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, ++ struct net_device *lag, bool delete) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret, id, i; ++ u32 val; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ /* Read current port member */ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* Shift val to the correct trunk */ ++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); ++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; ++ if (delete) ++ val &= ~BIT(port); ++ else ++ val |= BIT(port); ++ ++ /* Update port member. With empty portmap disable trunk */ ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, ++ QCA8K_REG_GOL_TRUNK_MEMBER(id) | ++ QCA8K_REG_GOL_TRUNK_EN(id), ++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | ++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); ++ ++ /* Search empty member if adding or port on deleting */ ++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); ++ if (ret) ++ return ret; ++ ++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; ++ ++ if (delete) { ++ /* If port flagged to be disabled assume this member is ++ * empty ++ */ ++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; ++ if (val != port) ++ continue; ++ } else { ++ /* If port flagged to be enabled assume this member is ++ * already set ++ */ ++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ } ++ ++ /* We have found the member to add/remove */ ++ break; ++ } ++ ++ /* Set port in the correct port mask or disable port if in delete mode */ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), ++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | ++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); ++} ++ ++static int ++qca8k_port_lag_join(struct dsa_switch *ds, int port, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ int ret; ++ ++ if (!qca8k_lag_can_offload(ds, lag, info)) ++ return -EOPNOTSUPP; ++ ++ ret = qca8k_lag_setup_hash(ds, lag, info); ++ if (ret) ++ return ret; ++ ++ return qca8k_lag_refresh_portmap(ds, port, lag, false); ++} ++ ++static int ++qca8k_port_lag_leave(struct dsa_switch *ds, int port, ++ struct net_device *lag) ++{ ++ return qca8k_lag_refresh_portmap(ds, port, lag, true); ++} ++ ++static void ++qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, ++ bool operational) ++{ ++ struct dsa_port *dp = master->dsa_ptr; ++ struct qca8k_priv *priv = ds->priv; ++ ++ /* Ethernet MIB/MDIO is only supported for CPU port 0 */ ++ if (dp->index != 0) ++ return; ++ ++ mutex_lock(&priv->mgmt_eth_data.mutex); ++ mutex_lock(&priv->mib_eth_data.mutex); ++ ++ priv->mgmt_master = operational ? (struct net_device *)master : NULL; ++ ++ mutex_unlock(&priv->mib_eth_data.mutex); ++ mutex_unlock(&priv->mgmt_eth_data.mutex); ++} ++ ++static int qca8k_connect_tag_protocol(struct dsa_switch *ds, ++ enum dsa_tag_protocol proto) ++{ ++ struct qca_tagger_data *tagger_data; ++ ++ switch (proto) { ++ case DSA_TAG_PROTO_QCA: ++ tagger_data = ds->tagger_data; ++ ++ tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler; ++ tagger_data->mib_autocast_handler = qca8k_mib_autocast_handler; ++ ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static const struct dsa_switch_ops qca8k_switch_ops = { ++ .get_tag_protocol = qca8k_get_tag_protocol, ++ .setup = qca8k_setup, ++ .get_strings = qca8k_get_strings, ++ .get_ethtool_stats = qca8k_get_ethtool_stats, ++ .get_sset_count = qca8k_get_sset_count, ++ .set_ageing_time = qca8k_set_ageing_time, ++ .get_mac_eee = qca8k_get_mac_eee, ++ .set_mac_eee = qca8k_set_mac_eee, ++ .port_enable = qca8k_port_enable, ++ .port_disable = qca8k_port_disable, ++ .port_change_mtu = qca8k_port_change_mtu, ++ .port_max_mtu = qca8k_port_max_mtu, ++ .port_stp_state_set = qca8k_port_stp_state_set, ++ .port_bridge_join = qca8k_port_bridge_join, ++ .port_bridge_leave = qca8k_port_bridge_leave, ++ .port_fast_age = qca8k_port_fast_age, ++ .port_fdb_add = qca8k_port_fdb_add, ++ .port_fdb_del = qca8k_port_fdb_del, ++ .port_fdb_dump = qca8k_port_fdb_dump, ++ .port_mdb_add = qca8k_port_mdb_add, ++ .port_mdb_del = qca8k_port_mdb_del, ++ .port_mirror_add = qca8k_port_mirror_add, ++ .port_mirror_del = qca8k_port_mirror_del, ++ .port_vlan_filtering = qca8k_port_vlan_filtering, ++ .port_vlan_add = qca8k_port_vlan_add, ++ .port_vlan_del = qca8k_port_vlan_del, ++ .phylink_validate = qca8k_phylink_validate, ++ .phylink_mac_link_state = qca8k_phylink_mac_link_state, ++ .phylink_mac_config = qca8k_phylink_mac_config, ++ .phylink_mac_link_down = qca8k_phylink_mac_link_down, ++ .phylink_mac_link_up = qca8k_phylink_mac_link_up, ++ .get_phy_flags = qca8k_get_phy_flags, ++ .port_lag_join = qca8k_port_lag_join, ++ .port_lag_leave = qca8k_port_lag_leave, ++ .master_state_change = qca8k_master_change, ++ .connect_tag_protocol = qca8k_connect_tag_protocol, ++}; ++ ++static int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ u32 val; ++ u8 id; ++ int ret; ++ ++ if (!priv->info) ++ return -ENODEV; ++ ++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); ++ if (ret < 0) ++ return -ENODEV; ++ ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val); ++ if (id != priv->info->id) { ++ dev_err(priv->dev, ++ "Switch id detected %x but expected %x", ++ id, priv->info->id); ++ return -ENODEV; ++ } ++ ++ priv->switch_id = id; ++ ++ /* Save revision to communicate to the internal PHY driver */ ++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); ++ ++ return 0; ++} ++ ++static int ++qca8k_sw_probe(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv; ++ int ret; ++ ++ /* allocate the private data struct so that we can probe the switches ++ * ID register ++ */ ++ priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->info = of_device_get_match_data(priv->dev); ++ priv->bus = mdiodev->bus; ++ priv->dev = &mdiodev->dev; ++ ++ priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", ++ GPIOD_ASIS); ++ if (IS_ERR(priv->reset_gpio)) ++ return PTR_ERR(priv->reset_gpio); ++ ++ if (priv->reset_gpio) { ++ gpiod_set_value_cansleep(priv->reset_gpio, 1); ++ /* The active low duration must be greater than 10 ms ++ * and checkpatch.pl wants 20 ms. ++ */ ++ msleep(20); ++ gpiod_set_value_cansleep(priv->reset_gpio, 0); ++ } ++ ++ /* Start by setting up the register mapping */ ++ priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv, ++ &qca8k_regmap_config); ++ if (IS_ERR(priv->regmap)) { ++ dev_err(priv->dev, "regmap initialization failed"); ++ return PTR_ERR(priv->regmap); ++ } ++ ++ priv->mdio_cache.page = 0xffff; ++ priv->mdio_cache.lo = 0xffff; ++ priv->mdio_cache.hi = 0xffff; ++ ++ /* Check the detected switch id */ ++ ret = qca8k_read_switch_id(priv); ++ if (ret) ++ return ret; ++ ++ priv->ds = devm_kzalloc(&mdiodev->dev, sizeof(*priv->ds), GFP_KERNEL); ++ if (!priv->ds) ++ return -ENOMEM; ++ ++ mutex_init(&priv->mgmt_eth_data.mutex); ++ init_completion(&priv->mgmt_eth_data.rw_done); ++ ++ mutex_init(&priv->mib_eth_data.mutex); ++ init_completion(&priv->mib_eth_data.rw_done); ++ ++ priv->ds->dev = &mdiodev->dev; ++ priv->ds->num_ports = QCA8K_NUM_PORTS; ++ priv->ds->priv = priv; ++ priv->ds->ops = &qca8k_switch_ops; ++ mutex_init(&priv->reg_mutex); ++ dev_set_drvdata(&mdiodev->dev, priv); ++ ++ return dsa_register_switch(priv->ds); ++} ++ ++static void ++qca8k_sw_remove(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ int i; ++ ++ if (!priv) ++ return; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) ++ qca8k_port_set_status(priv, i, 0); ++ ++ dsa_unregister_switch(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++static void qca8k_sw_shutdown(struct mdio_device *mdiodev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); ++ ++ if (!priv) ++ return; ++ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(&mdiodev->dev, NULL); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static void ++qca8k_set_pm(struct qca8k_priv *priv, int enable) ++{ ++ int port; ++ ++ for (port = 0; port < QCA8K_NUM_PORTS; port++) { ++ /* Do not enable on resume if the port was ++ * disabled before. ++ */ ++ if (!(priv->port_enabled_map & BIT(port))) ++ continue; ++ ++ qca8k_port_set_status(priv, port, enable); ++ } ++} ++ ++static int qca8k_suspend(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 0); ++ ++ return dsa_switch_suspend(priv->ds); ++} ++ ++static int qca8k_resume(struct device *dev) ++{ ++ struct qca8k_priv *priv = dev_get_drvdata(dev); ++ ++ qca8k_set_pm(priv, 1); ++ ++ return dsa_switch_resume(priv->ds); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, ++ qca8k_suspend, qca8k_resume); ++ ++static const struct qca8k_info_ops qca8xxx_ops = { ++ .autocast_mib = qca8k_get_ethtool_stats_eth, ++}; ++ ++static const struct qca8k_match_data qca8327 = { ++ .id = QCA8K_ID_QCA8327, ++ .reduced_package = true, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, ++}; ++ ++static const struct qca8k_match_data qca8328 = { ++ .id = QCA8K_ID_QCA8327, ++ .mib_count = QCA8K_QCA832X_MIB_COUNT, ++ .ops = &qca8xxx_ops, ++}; ++ ++static const struct qca8k_match_data qca833x = { ++ .id = QCA8K_ID_QCA8337, ++ .mib_count = QCA8K_QCA833X_MIB_COUNT, ++ .ops = &qca8xxx_ops, ++}; ++ ++static const struct of_device_id qca8k_of_match[] = { ++ { .compatible = "qca,qca8327", .data = &qca8327 }, ++ { .compatible = "qca,qca8328", .data = &qca8328 }, ++ { .compatible = "qca,qca8334", .data = &qca833x }, ++ { .compatible = "qca,qca8337", .data = &qca833x }, ++ { /* sentinel */ }, ++}; ++ ++static struct mdio_driver qca8kmdio_driver = { ++ .probe = qca8k_sw_probe, ++ .remove = qca8k_sw_remove, ++ .shutdown = qca8k_sw_shutdown, ++ .mdiodrv.driver = { ++ .name = "qca8k", ++ .of_match_table = qca8k_of_match, ++ .pm = &qca8k_pm_ops, ++ }, ++}; ++ ++mdio_module_driver(qca8kmdio_driver); ++ ++MODULE_AUTHOR("Mathieu Olivari, John Crispin "); ++MODULE_DESCRIPTION("Driver for QCA8K ethernet switch family"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qca8k"); +--- /dev/null ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -0,0 +1,63 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2009 Felix Fietkau ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. ++ * Copyright (c) 2016 John Crispin ++ */ ++ ++#include ++#include ++ ++#include "qca8k.h" ++ ++#define MIB_DESC(_s, _o, _n) \ ++ { \ ++ .size = (_s), \ ++ .offset = (_o), \ ++ .name = (_n), \ ++ } ++ ++const struct qca8k_mib_desc ar8327_mib[] = { ++ MIB_DESC(1, 0x00, "RxBroad"), ++ MIB_DESC(1, 0x04, "RxPause"), ++ MIB_DESC(1, 0x08, "RxMulti"), ++ MIB_DESC(1, 0x0c, "RxFcsErr"), ++ MIB_DESC(1, 0x10, "RxAlignErr"), ++ MIB_DESC(1, 0x14, "RxRunt"), ++ MIB_DESC(1, 0x18, "RxFragment"), ++ MIB_DESC(1, 0x1c, "Rx64Byte"), ++ MIB_DESC(1, 0x20, "Rx128Byte"), ++ MIB_DESC(1, 0x24, "Rx256Byte"), ++ MIB_DESC(1, 0x28, "Rx512Byte"), ++ MIB_DESC(1, 0x2c, "Rx1024Byte"), ++ MIB_DESC(1, 0x30, "Rx1518Byte"), ++ MIB_DESC(1, 0x34, "RxMaxByte"), ++ MIB_DESC(1, 0x38, "RxTooLong"), ++ MIB_DESC(2, 0x3c, "RxGoodByte"), ++ MIB_DESC(2, 0x44, "RxBadByte"), ++ MIB_DESC(1, 0x4c, "RxOverFlow"), ++ MIB_DESC(1, 0x50, "Filtered"), ++ MIB_DESC(1, 0x54, "TxBroad"), ++ MIB_DESC(1, 0x58, "TxPause"), ++ MIB_DESC(1, 0x5c, "TxMulti"), ++ MIB_DESC(1, 0x60, "TxUnderRun"), ++ MIB_DESC(1, 0x64, "Tx64Byte"), ++ MIB_DESC(1, 0x68, "Tx128Byte"), ++ MIB_DESC(1, 0x6c, "Tx256Byte"), ++ MIB_DESC(1, 0x70, "Tx512Byte"), ++ MIB_DESC(1, 0x74, "Tx1024Byte"), ++ MIB_DESC(1, 0x78, "Tx1518Byte"), ++ MIB_DESC(1, 0x7c, "TxMaxByte"), ++ MIB_DESC(1, 0x80, "TxOverSize"), ++ MIB_DESC(2, 0x84, "TxByte"), ++ MIB_DESC(1, 0x8c, "TxCollision"), ++ MIB_DESC(1, 0x90, "TxAbortCol"), ++ MIB_DESC(1, 0x94, "TxMultiCol"), ++ MIB_DESC(1, 0x98, "TxSingleCol"), ++ MIB_DESC(1, 0x9c, "TxExcDefer"), ++ MIB_DESC(1, 0xa0, "TxDefer"), ++ MIB_DESC(1, 0xa4, "TxLateCol"), ++ MIB_DESC(1, 0xa8, "RXUnicast"), ++ MIB_DESC(1, 0xac, "TXUnicast"), ++}; +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -414,4 +414,7 @@ struct qca8k_fdb { + u8 mac[6]; + }; + ++/* Common setup function */ ++extern const struct qca8k_mib_desc ar8327_mib[]; ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch b/target/linux/generic/backport-5.15/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch new file mode 100644 index 0000000000..012ab85474 --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-04-net-dsa-qca8k-move-qca8k-read-write-rmw-and-reg-tabl.patch @@ -0,0 +1,135 @@ +From d5f901eab2e9dfed1095995dfc98f231f4fd2971 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:13 +0200 +Subject: [PATCH 04/14] net: dsa: qca8k: move qca8k read/write/rmw and reg + table to common code + +The same reg table and read/write/rmw function are used by drivers +based on qca8k family switch. +Move them to common code to make it accessible also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 42 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 38 +++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 +++++ + 3 files changed, 44 insertions(+), 42 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -133,24 +133,6 @@ qca8k_set_page(struct qca8k_priv *priv, + return 0; + } + +-static int +-qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) +-{ +- return regmap_read(priv->regmap, reg, val); +-} +- +-static int +-qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) +-{ +- return regmap_write(priv->regmap, reg, val); +-} +- +-static int +-qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +-{ +- return regmap_update_bits(priv->regmap, reg, mask, write_val); +-} +- + static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) + { + struct qca8k_mgmt_eth_data *mgmt_eth_data; +@@ -483,30 +465,6 @@ exit: + return ret; + } + +-static const struct regmap_range qca8k_readable_ranges[] = { +- regmap_reg_range(0x0000, 0x00e4), /* Global control */ +- regmap_reg_range(0x0100, 0x0168), /* EEE control */ +- regmap_reg_range(0x0200, 0x0270), /* Parser control */ +- regmap_reg_range(0x0400, 0x0454), /* ACL */ +- regmap_reg_range(0x0600, 0x0718), /* Lookup */ +- regmap_reg_range(0x0800, 0x0b70), /* QM */ +- regmap_reg_range(0x0c00, 0x0c80), /* PKT */ +- regmap_reg_range(0x0e00, 0x0e98), /* L3 */ +- regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ +- regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ +- regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ +- regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ +- regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ +- regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ +- regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ +- +-}; +- +-static const struct regmap_access_table qca8k_readable_table = { +- .yes_ranges = qca8k_readable_ranges, +- .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), +-}; +- + static struct regmap_config qca8k_regmap_config = { + .reg_bits = 16, + .val_bits = 32, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -61,3 +61,41 @@ const struct qca8k_mib_desc ar8327_mib[] + MIB_DESC(1, 0xa8, "RXUnicast"), + MIB_DESC(1, 0xac, "TXUnicast"), + }; ++ ++int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) ++{ ++ return regmap_read(priv->regmap, reg, val); ++} ++ ++int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) ++{ ++ return regmap_write(priv->regmap, reg, val); ++} ++ ++int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) ++{ ++ return regmap_update_bits(priv->regmap, reg, mask, write_val); ++} ++ ++static const struct regmap_range qca8k_readable_ranges[] = { ++ regmap_reg_range(0x0000, 0x00e4), /* Global control */ ++ regmap_reg_range(0x0100, 0x0168), /* EEE control */ ++ regmap_reg_range(0x0200, 0x0270), /* Parser control */ ++ regmap_reg_range(0x0400, 0x0454), /* ACL */ ++ regmap_reg_range(0x0600, 0x0718), /* Lookup */ ++ regmap_reg_range(0x0800, 0x0b70), /* QM */ ++ regmap_reg_range(0x0c00, 0x0c80), /* PKT */ ++ regmap_reg_range(0x0e00, 0x0e98), /* L3 */ ++ regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ ++ regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ ++ regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ ++ regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ ++ regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ ++ regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ ++ regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ ++}; ++ ++const struct regmap_access_table qca8k_readable_table = { ++ .yes_ranges = qca8k_readable_ranges, ++ .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), ++}; +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -416,5 +416,11 @@ struct qca8k_fdb { + + /* Common setup function */ + extern const struct qca8k_mib_desc ar8327_mib[]; ++extern const struct regmap_access_table qca8k_readable_table; ++ ++/* Common read/write/rmw function */ ++int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); ++int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); ++int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch b/target/linux/generic/backport-5.15/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch new file mode 100644 index 0000000000..0ed7ed41fb --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-05-net-dsa-qca8k-move-qca8k-bulk-read-write-helper-to-c.patch @@ -0,0 +1,145 @@ +From 910746444313dc463396cd63024cdf54ef04ef39 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:14 +0200 +Subject: [PATCH 05/14] net: dsa: qca8k: move qca8k bulk read/write helper to + common code + +The same ATU function are used by drivers based on qca8k family switch. +Move the bulk read/write helper to common code to declare these shared +ATU functions in common code. +These helper will be dropped when regmap correctly support bulk +read/write. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 39 ++---------------------------- + drivers/net/dsa/qca/qca8k-common.c | 39 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 8 ++++++ + 3 files changed, 49 insertions(+), 37 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -343,43 +343,6 @@ qca8k_regmap_update_bits_eth(struct qca8 + } + + static int +-qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- +- if (priv->mgmt_master && !qca8k_read_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- ret = regmap_read(priv->regmap, reg + (i * 4), val + i); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int +-qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) +-{ +- int i, count = len / sizeof(u32), ret; +- u32 tmp; +- +- if (priv->mgmt_master && !qca8k_write_eth(priv, reg, val, len)) +- return 0; +- +- for (i = 0; i < count; i++) { +- tmp = val[i]; +- +- ret = regmap_write(priv->regmap, reg + (i * 4), tmp); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int + qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ctx; +@@ -3096,6 +3059,8 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops, + + static const struct qca8k_info_ops qca8xxx_ops = { + .autocast_mib = qca8k_get_ethtool_stats_eth, ++ .read_eth = qca8k_read_eth, ++ .write_eth = qca8k_write_eth, + }; + + static const struct qca8k_match_data qca8327 = { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -99,3 +99,42 @@ const struct regmap_access_table qca8k_r + .yes_ranges = qca8k_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), + }; ++ ++/* TODO: remove these extra ops when we can support regmap bulk read/write */ ++int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ ++ if (priv->mgmt_master && priv->info->ops->read_eth && ++ !priv->info->ops->read_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ ret = regmap_read(priv->regmap, reg + (i * 4), val + i); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* TODO: remove these extra ops when we can support regmap bulk read/write */ ++int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++{ ++ int i, count = len / sizeof(u32), ret; ++ u32 tmp; ++ ++ if (priv->mgmt_master && priv->info->ops->write_eth && ++ !priv->info->ops->write_eth(priv, reg, val, len)) ++ return 0; ++ ++ for (i = 0; i < count; i++) { ++ tmp = val[i]; ++ ++ ret = regmap_write(priv->regmap, reg + (i * 4), tmp); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -324,8 +324,13 @@ enum qca8k_mid_cmd { + QCA8K_MIB_CAST = 3, + }; + ++struct qca8k_priv; ++ + struct qca8k_info_ops { + int (*autocast_mib)(struct dsa_switch *ds, int port, u64 *data); ++ /* TODO: remove these extra ops when we can support regmap bulk read/write */ ++ int (*read_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); ++ int (*write_eth)(struct qca8k_priv *priv, u32 reg, u32 *val, int len); + }; + + struct qca8k_match_data { +@@ -423,4 +428,7 @@ int qca8k_read(struct qca8k_priv *priv, + int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); + int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + ++int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); ++int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch b/target/linux/generic/backport-5.15/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch new file mode 100644 index 0000000000..a39a55b89b --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-06-net-dsa-qca8k-move-mib-init-function-to-common-code.patch @@ -0,0 +1,137 @@ +From fce1ec0c4e2d03d9c62ffc615a42bdba78eb4c14 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:15 +0200 +Subject: [PATCH 06/14] net: dsa: qca8k: move mib init function to common code + +The same mib function is used by drivers based on qca8k family switch. +Move it to common code to make it accessible also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 37 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 35 ++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 4 ++++ + 3 files changed, 39 insertions(+), 37 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -442,15 +442,6 @@ static struct regmap_config qca8k_regmap + }; + + static int +-qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) +-{ +- u32 val; +- +- return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, +- QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); +-} +- +-static int + qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) + { + u32 reg[3]; +@@ -777,34 +768,6 @@ out: + return ret; + } + +-static int +-qca8k_mib_init(struct qca8k_priv *priv) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, +- QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, +- FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | +- QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); +- if (ret) +- goto exit; +- +- ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +- if (ret) +- goto exit; +- +- ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- + static void + qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -7,6 +7,7 @@ + */ + + #include ++#include + #include + + #include "qca8k.h" +@@ -138,3 +139,38 @@ int qca8k_bulk_write(struct qca8k_priv * + + return 0; + } ++ ++int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++{ ++ u32 val; ++ ++ return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, ++ QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); ++} ++ ++int qca8k_mib_init(struct qca8k_priv *priv) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, ++ QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, ++ FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | ++ QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); ++ if (ret) ++ goto exit; ++ ++ ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); ++ if (ret) ++ goto exit; ++ ++ ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -422,6 +422,7 @@ struct qca8k_fdb { + /* Common setup function */ + extern const struct qca8k_mib_desc ar8327_mib[]; + extern const struct regmap_access_table qca8k_readable_table; ++int qca8k_mib_init(struct qca8k_priv *priv); + + /* Common read/write/rmw function */ + int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); +@@ -431,4 +432,7 @@ int qca8k_rmw(struct qca8k_priv *priv, u + int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); + int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); + ++/* Common ops function */ ++int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch b/target/linux/generic/backport-5.15/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch new file mode 100644 index 0000000000..6fd1c66b0a --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-07-net-dsa-qca8k-move-port-set-status-eee-ethtool-stats.patch @@ -0,0 +1,281 @@ +From 472fcea160f27a5d9b7526093d9d8d89ba0b6137 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:16 +0200 +Subject: [PATCH 07/14] net: dsa: qca8k: move port set status/eee/ethtool stats + function to common code + +The same logic to disable/enable port, set eee and get ethtool stats is +used by drivers based on qca8k family switch. +Move it to common code to make it accessible also by other drivers. +While at it also drop unnecessary qca8k_priv cast for void pointers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 105 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 102 ++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 11 +++ + 3 files changed, 113 insertions(+), 105 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -768,21 +768,6 @@ out: + return ret; + } + +-static void +-qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +-{ +- u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; +- +- /* Port 0 and 6 have no internal PHY */ +- if (port > 0 && port < 6) +- mask |= QCA8K_PORT_STATUS_LINK_AUTO; +- +- if (enable) +- regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +- else +- regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); +-} +- + static int + qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, + struct sk_buff *read_skb, u32 *val) +@@ -1974,20 +1959,6 @@ qca8k_phylink_mac_link_up(struct dsa_swi + qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); + } + +-static void +-qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) +-{ +- struct qca8k_priv *priv = ds->priv; +- int i; +- +- if (stringset != ETH_SS_STATS) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) +- strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, +- ETH_GSTRING_LEN); +-} +- + static void qca8k_mib_autocast_handler(struct dsa_switch *ds, struct sk_buff *skb) + { + struct qca8k_mib_eth_data *mib_eth_data; +@@ -2078,82 +2049,6 @@ exit: + } + + static void +-qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, +- uint64_t *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- const struct qca8k_mib_desc *mib; +- u32 reg, i, val; +- u32 hi = 0; +- int ret; +- +- if (priv->mgmt_master && priv->info->ops->autocast_mib && +- priv->info->ops->autocast_mib(ds, port, data) > 0) +- return; +- +- for (i = 0; i < priv->info->mib_count; i++) { +- mib = &ar8327_mib[i]; +- reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; +- +- ret = qca8k_read(priv, reg, &val); +- if (ret < 0) +- continue; +- +- if (mib->size == 2) { +- ret = qca8k_read(priv, reg + 4, &hi); +- if (ret < 0) +- continue; +- } +- +- data[i] = val; +- if (mib->size == 2) +- data[i] |= (u64)hi << 32; +- } +-} +- +-static int +-qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- if (sset != ETH_SS_STATS) +- return 0; +- +- return priv->info->mib_count; +-} +- +-static int +-qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); +- u32 reg; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); +- if (ret < 0) +- goto exit; +- +- if (eee->eee_enabled) +- reg |= lpi_en; +- else +- reg &= ~lpi_en; +- ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +-{ +- /* Nothing to do on the port's MAC */ +- return 0; +-} +- +-static void + qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) + { + struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -174,3 +174,105 @@ exit: + mutex_unlock(&priv->reg_mutex); + return ret; + } ++ ++void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) ++{ ++ u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; ++ ++ /* Port 0 and 6 have no internal PHY */ ++ if (port > 0 && port < 6) ++ mask |= QCA8K_PORT_STATUS_LINK_AUTO; ++ ++ if (enable) ++ regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++ else ++ regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); ++} ++ ++void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, ++ uint8_t *data) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int i; ++ ++ if (stringset != ETH_SS_STATS) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) ++ strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, ++ ETH_GSTRING_LEN); ++} ++ ++void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, ++ uint64_t *data) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const struct qca8k_mib_desc *mib; ++ u32 reg, i, val; ++ u32 hi = 0; ++ int ret; ++ ++ if (priv->mgmt_master && priv->info->ops->autocast_mib && ++ priv->info->ops->autocast_mib(ds, port, data) > 0) ++ return; ++ ++ for (i = 0; i < priv->info->mib_count; i++) { ++ mib = &ar8327_mib[i]; ++ reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; ++ ++ ret = qca8k_read(priv, reg, &val); ++ if (ret < 0) ++ continue; ++ ++ if (mib->size == 2) { ++ ret = qca8k_read(priv, reg + 4, &hi); ++ if (ret < 0) ++ continue; ++ } ++ ++ data[i] = val; ++ if (mib->size == 2) ++ data[i] |= (u64)hi << 32; ++ } ++} ++ ++int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ if (sset != ETH_SS_STATS) ++ return 0; ++ ++ return priv->info->mib_count; ++} ++ ++int qca8k_set_mac_eee(struct dsa_switch *ds, int port, ++ struct ethtool_eee *eee) ++{ ++ u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); ++ if (ret < 0) ++ goto exit; ++ ++ if (eee->eee_enabled) ++ reg |= lpi_en; ++ else ++ reg &= ~lpi_en; ++ ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++int qca8k_get_mac_eee(struct dsa_switch *ds, int port, ++ struct ethtool_eee *e) ++{ ++ /* Nothing to do on the port's MAC */ ++ return 0; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -423,6 +423,7 @@ struct qca8k_fdb { + extern const struct qca8k_mib_desc ar8327_mib[]; + extern const struct regmap_access_table qca8k_readable_table; + int qca8k_mib_init(struct qca8k_priv *priv); ++void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable); + + /* Common read/write/rmw function */ + int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); +@@ -435,4 +436,14 @@ int qca8k_bulk_write(struct qca8k_priv * + /* Common ops function */ + int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); + ++/* Common ethtool stats function */ ++void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data); ++void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, ++ uint64_t *data); ++int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset); ++ ++/* Common eee function */ ++int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee); ++int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch b/target/linux/generic/backport-5.15/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch new file mode 100644 index 0000000000..3ca682d72c --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-08-net-dsa-qca8k-move-bridge-functions-to-common-code.patch @@ -0,0 +1,237 @@ +From fd3cae2f3ac190d06e48f43739237e02f9dc51ff Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:17 +0200 +Subject: [PATCH 08/14] net: dsa: qca8k: move bridge functions to common code + +The same bridge functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. +While at it also drop unnecessary qca8k_priv cast for void pointers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 93 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 93 ++++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 9 +++ + 3 files changed, 102 insertions(+), 93 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2049,97 +2049,6 @@ exit: + } + + static void +-qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u32 stp_state; +- +- switch (state) { +- case BR_STATE_DISABLED: +- stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; +- break; +- case BR_STATE_BLOCKING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; +- break; +- case BR_STATE_LISTENING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; +- break; +- case BR_STATE_LEARNING: +- stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; +- break; +- case BR_STATE_FORWARDING: +- default: +- stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; +- break; +- } +- +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); +-} +- +-static int +-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int port_mask, cpu_port; +- int i, ret; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- port_mask = BIT(cpu_port); +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (dsa_to_port(ds, i)->bridge_dev != br) +- continue; +- /* Add this port to the portvlan mask of the other ports +- * in the bridge +- */ +- ret = regmap_set_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- if (ret) +- return ret; +- if (i != port) +- port_mask |= BIT(i); +- } +- +- /* Add all other ports to this ports portvlan mask */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); +- +- return ret; +-} +- +-static void +-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- int cpu_port, i; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (dsa_to_port(ds, i)->bridge_dev != br) +- continue; +- /* Remove this port to the portvlan mask of the other ports +- * in the bridge +- */ +- regmap_clear_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- } +- +- /* Set the cpu port to be the only one in the portvlan mask of +- * this port +- */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); +-} +- +-static void + qca8k_port_fast_age(struct dsa_switch *ds, int port) + { + struct qca8k_priv *priv = ds->priv; +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + + #include "qca8k.h" + +@@ -276,3 +277,93 @@ int qca8k_get_mac_eee(struct dsa_switch + /* Nothing to do on the port's MAC */ + return 0; + } ++ ++void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 stp_state; ++ ++ switch (state) { ++ case BR_STATE_DISABLED: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; ++ break; ++ case BR_STATE_BLOCKING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; ++ break; ++ case BR_STATE_LISTENING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; ++ break; ++ case BR_STATE_LEARNING: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; ++ break; ++ case BR_STATE_FORWARDING: ++ default: ++ stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; ++ break; ++ } ++ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); ++} ++ ++int qca8k_port_bridge_join(struct dsa_switch *ds, int port, ++ struct net_device *br) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int port_mask, cpu_port; ++ int i, ret; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ port_mask = BIT(cpu_port); ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; ++ if (dsa_to_port(ds, i)->bridge_dev != br) ++ continue; ++ /* Add this port to the portvlan mask of the other ports ++ * in the bridge ++ */ ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ if (ret) ++ return ret; ++ if (i != port) ++ port_mask |= BIT(i); ++ } ++ ++ /* Add all other ports to this ports portvlan mask */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ++ return ret; ++} ++ ++void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, ++ struct net_device *br) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int cpu_port, i; ++ ++ cpu_port = dsa_to_port(ds, port)->cpu_dp->index; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (dsa_is_cpu_port(ds, i)) ++ continue; ++ if (dsa_to_port(ds, i)->bridge_dev != br) ++ continue; ++ /* Remove this port to the portvlan mask of the other ports ++ * in the bridge ++ */ ++ regmap_clear_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ } ++ ++ /* Set the cpu port to be the only one in the portvlan mask of ++ * this port ++ */ ++ qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -446,4 +446,11 @@ int qca8k_get_sset_count(struct dsa_swit + int qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee); + int qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); + ++/* Common bridge function */ ++void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); ++int qca8k_port_bridge_join(struct dsa_switch *ds, int port, ++ struct net_device *br); ++void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, ++ struct net_device *br); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch b/target/linux/generic/backport-5.15/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch new file mode 100644 index 0000000000..e3414408d6 --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-09-net-dsa-qca8k-move-set-age-MTU-port-enable-disable-f.patch @@ -0,0 +1,227 @@ +From b3a302b171f73425b41de8d3357fae3fa7057322 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:18 +0200 +Subject: [PATCH 09/14] net: dsa: qca8k: move set age/MTU/port enable/disable + functions to common code + +The same set age, MTU and port enable/disable function are used by +driver based on qca8k family switch. +Move them to common code to make them accessible also by other drivers. +While at it also drop unnecessary qca8k_priv cast for void pointers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 88 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 85 +++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 12 ++++ + 3 files changed, 97 insertions(+), 88 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -2059,94 +2059,6 @@ qca8k_port_fast_age(struct dsa_switch *d + } + + static int +-qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +-{ +- struct qca8k_priv *priv = ds->priv; +- unsigned int secs = msecs / 1000; +- u32 val; +- +- /* AGE_TIME reg is set in 7s step */ +- val = secs / 7; +- +- /* Handle case with 0 as val to NOT disable +- * learning +- */ +- if (!val) +- val = 1; +- +- return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, +- QCA8K_ATU_AGE_TIME(val)); +-} +- +-static int +-qca8k_port_enable(struct dsa_switch *ds, int port, +- struct phy_device *phy) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 1); +- priv->port_enabled_map |= BIT(port); +- +- if (dsa_is_user_port(ds, port)) +- phy_support_asym_pause(phy); +- +- return 0; +-} +- +-static void +-qca8k_port_disable(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- +- qca8k_port_set_status(priv, port, 0); +- priv->port_enabled_map &= ~BIT(port); +-} +- +-static int +-qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- /* We have only have a general MTU setting. +- * DSA always set the CPU port's MTU to the largest MTU of the slave +- * ports. +- * Setting MTU just for the CPU port is sufficient to correctly set a +- * value for every port. +- */ +- if (!dsa_is_cpu_port(ds, port)) +- return 0; +- +- /* To change the MAX_FRAME_SIZE the cpu ports must be off or +- * the switch panics. +- * Turn off both cpu ports before applying the new value to prevent +- * this. +- */ +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 0); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 0); +- +- /* Include L2 header / FCS length */ +- ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); +- +- if (priv->port_enabled_map & BIT(0)) +- qca8k_port_set_status(priv, 0, 1); +- +- if (priv->port_enabled_map & BIT(6)) +- qca8k_port_set_status(priv, 6, 1); +- +- return ret; +-} +- +-static int +-qca8k_port_max_mtu(struct dsa_switch *ds, int port) +-{ +- return QCA8K_MAX_MTU; +-} +- +-static int + qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, + u16 port_mask, u16 vid) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -367,3 +367,88 @@ void qca8k_port_bridge_leave(struct dsa_ + qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); + } ++ ++int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ unsigned int secs = msecs / 1000; ++ u32 val; ++ ++ /* AGE_TIME reg is set in 7s step */ ++ val = secs / 7; ++ ++ /* Handle case with 0 as val to NOT disable ++ * learning ++ */ ++ if (!val) ++ val = 1; ++ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, ++ QCA8K_ATU_AGE_TIME_MASK, ++ QCA8K_ATU_AGE_TIME(val)); ++} ++ ++int qca8k_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 1); ++ priv->port_enabled_map |= BIT(port); ++ ++ if (dsa_is_user_port(ds, port)) ++ phy_support_asym_pause(phy); ++ ++ return 0; ++} ++ ++void qca8k_port_disable(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ qca8k_port_set_status(priv, port, 0); ++ priv->port_enabled_map &= ~BIT(port); ++} ++ ++int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ /* We have only have a general MTU setting. ++ * DSA always set the CPU port's MTU to the largest MTU of the slave ++ * ports. ++ * Setting MTU just for the CPU port is sufficient to correctly set a ++ * value for every port. ++ */ ++ if (!dsa_is_cpu_port(ds, port)) ++ return 0; ++ ++ /* To change the MAX_FRAME_SIZE the cpu ports must be off or ++ * the switch panics. ++ * Turn off both cpu ports before applying the new value to prevent ++ * this. ++ */ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 0); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 0); ++ ++ /* Include L2 header / FCS length */ ++ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ++ ETH_HLEN + ETH_FCS_LEN); ++ ++ if (priv->port_enabled_map & BIT(0)) ++ qca8k_port_set_status(priv, 0, 1); ++ ++ if (priv->port_enabled_map & BIT(6)) ++ qca8k_port_set_status(priv, 6, 1); ++ ++ return ret; ++} ++ ++int qca8k_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ return QCA8K_MAX_MTU; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -453,4 +453,16 @@ int qca8k_port_bridge_join(struct dsa_sw + void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *br); + ++/* Common port enable/disable function */ ++int qca8k_port_enable(struct dsa_switch *ds, int port, ++ struct phy_device *phy); ++void qca8k_port_disable(struct dsa_switch *ds, int port); ++ ++/* Common MTU function */ ++int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu); ++int qca8k_port_max_mtu(struct dsa_switch *ds, int port); ++ ++/* Common fast age function */ ++int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch b/target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch new file mode 100644 index 0000000000..96468ae74e --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-10-net-dsa-qca8k-move-port-FDB-MDB-function-to-common-c.patch @@ -0,0 +1,704 @@ +From 2e5bd96eea86a246b4de3bf756f7a11b43e6187d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:19 +0200 +Subject: [PATCH 10/14] net: dsa: qca8k: move port FDB/MDB function to common + code + +The same port FDB/MDB function are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. +Also drop bulk read/write functions and make them static + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 306 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 297 +++++++++++++++++++++++++++- + drivers/net/dsa/qca/qca8k.h | 25 ++- + 3 files changed, 317 insertions(+), 311 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -442,217 +442,6 @@ static struct regmap_config qca8k_regmap + }; + + static int +-qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) +-{ +- u32 reg[3]; +- int ret; +- +- /* load the ARL table into an array */ +- ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +- if (ret) +- return ret; +- +- /* vid - 83:72 */ +- fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); +- /* aging - 67:64 */ +- fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); +- /* portmask - 54:48 */ +- fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); +- /* mac - 47:0 */ +- fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); +- fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); +- fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); +- fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); +- fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); +- fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); +- +- return 0; +-} +- +-static void +-qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac, +- u8 aging) +-{ +- u32 reg[3] = { 0 }; +- +- /* vid - 83:72 */ +- reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); +- /* aging - 67:64 */ +- reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); +- /* portmask - 54:48 */ +- reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); +- /* mac - 47:0 */ +- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); +- reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); +- reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); +- +- /* load the array into the ARL table */ +- qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); +-} +- +-static int +-qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and FDB index */ +- reg = QCA8K_ATU_FUNC_BUSY; +- reg |= cmd; +- if (port >= 0) { +- reg |= QCA8K_ATU_FUNC_PORT_EN; +- reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); +- } +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_FDB_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_ATU_FUNC_FULL) +- return -1; +- } +- +- return 0; +-} +- +-static int +-qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port) +-{ +- int ret; +- +- qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); +- if (ret < 0) +- return ret; +- +- return qca8k_fdb_read(priv, fdb); +-} +- +-static int +-qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, +- u16 vid, u8 aging) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, u16 port_mask, u16 vid) +-{ +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_write(priv, vid, port_mask, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static void +-qca8k_fdb_flush(struct qca8k_priv *priv) +-{ +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); +- mutex_unlock(&priv->reg_mutex); +-} +- +-static int +-qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, +- const u8 *mac, u16 vid) +-{ +- struct qca8k_fdb fdb = { 0 }; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- +- qca8k_fdb_write(priv, vid, 0, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +- if (ret < 0) +- goto exit; +- +- ret = qca8k_fdb_read(priv, &fdb); +- if (ret < 0) +- goto exit; +- +- /* Rule exist. Delete first */ +- if (!fdb.aging) { +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- if (ret) +- goto exit; +- } +- +- /* Add port to fdb portmask */ +- fdb.port_mask |= port_mask; +- +- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int +-qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, +- const u8 *mac, u16 vid) +-{ +- struct qca8k_fdb fdb = { 0 }; +- int ret; +- +- mutex_lock(&priv->reg_mutex); +- +- qca8k_fdb_write(priv, vid, 0, mac, 0); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +- if (ret < 0) +- goto exit; +- +- /* Rule doesn't exist. Why delete? */ +- if (!fdb.aging) { +- ret = -EINVAL; +- goto exit; +- } +- +- ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +- if (ret) +- goto exit; +- +- /* Only port in the rule is this port. Don't re insert */ +- if (fdb.port_mask == port_mask) +- goto exit; +- +- /* Remove port from port mask */ +- fdb.port_mask &= ~port_mask; +- +- qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +- ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); +- +-exit: +- mutex_unlock(&priv->reg_mutex); +- return ret; +-} +- +-static int + qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) + { + u32 reg; +@@ -2048,97 +1837,6 @@ exit: + return ret; + } + +-static void +-qca8k_port_fast_age(struct dsa_switch *ds, int port) +-{ +- struct qca8k_priv *priv = ds->priv; +- +- mutex_lock(&priv->reg_mutex); +- qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); +- mutex_unlock(&priv->reg_mutex); +-} +- +-static int +-qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, +- u16 port_mask, u16 vid) +-{ +- /* Set the vid to the port vlan id if no vid is set */ +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_add(priv, addr, port_mask, vid, +- QCA8K_ATU_STATUS_STATIC); +-} +- +-static int +-qca8k_port_fdb_add(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- return qca8k_port_fdb_insert(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_del(struct dsa_switch *ds, int port, +- const unsigned char *addr, u16 vid) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- u16 port_mask = BIT(port); +- +- if (!vid) +- vid = QCA8K_PORT_VID_DEF; +- +- return qca8k_fdb_del(priv, addr, port_mask, vid); +-} +- +-static int +-qca8k_port_fdb_dump(struct dsa_switch *ds, int port, +- dsa_fdb_dump_cb_t *cb, void *data) +-{ +- struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +- struct qca8k_fdb _fdb = { 0 }; +- int cnt = QCA8K_NUM_FDB_RECORDS; +- bool is_static; +- int ret = 0; +- +- mutex_lock(&priv->reg_mutex); +- while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { +- if (!_fdb.aging) +- break; +- is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); +- ret = cb(_fdb.mac, _fdb.vid, is_static, data); +- if (ret) +- break; +- } +- mutex_unlock(&priv->reg_mutex); +- +- return 0; +-} +- +-static int +-qca8k_port_mdb_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_mdb *mdb) +-{ +- struct qca8k_priv *priv = ds->priv; +- const u8 *addr = mdb->addr; +- u16 vid = mdb->vid; +- +- return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); +-} +- +-static int +-qca8k_port_mdb_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_mdb *mdb) +-{ +- struct qca8k_priv *priv = ds->priv; +- const u8 *addr = mdb->addr; +- u16 vid = mdb->vid; +- +- return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); +-} +- + static int + qca8k_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -103,7 +103,7 @@ const struct regmap_access_table qca8k_r + }; + + /* TODO: remove these extra ops when we can support regmap bulk read/write */ +-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++static int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) + { + int i, count = len / sizeof(u32), ret; + +@@ -121,7 +121,7 @@ int qca8k_bulk_read(struct qca8k_priv *p + } + + /* TODO: remove these extra ops when we can support regmap bulk read/write */ +-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) ++static int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) + { + int i, count = len / sizeof(u32), ret; + u32 tmp; +@@ -149,6 +149,211 @@ int qca8k_busy_wait(struct qca8k_priv *p + QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); + } + ++static int qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb) ++{ ++ u32 reg[3]; ++ int ret; ++ ++ /* load the ARL table into an array */ ++ ret = qca8k_bulk_read(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++ if (ret) ++ return ret; ++ ++ /* vid - 83:72 */ ++ fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]); ++ /* aging - 67:64 */ ++ fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]); ++ /* portmask - 54:48 */ ++ fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]); ++ /* mac - 47:0 */ ++ fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); ++ fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); ++ fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); ++ fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); ++ fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); ++ fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]); ++ ++ return 0; ++} ++ ++static void qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, ++ const u8 *mac, u8 aging) ++{ ++ u32 reg[3] = { 0 }; ++ ++ /* vid - 83:72 */ ++ reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid); ++ /* aging - 67:64 */ ++ reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging); ++ /* portmask - 54:48 */ ++ reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask); ++ /* mac - 47:0 */ ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); ++ reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); ++ reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]); ++ ++ /* load the array into the ARL table */ ++ qca8k_bulk_write(priv, QCA8K_REG_ATU_DATA0, reg, sizeof(reg)); ++} ++ ++static int qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, ++ int port) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and FDB index */ ++ reg = QCA8K_ATU_FUNC_BUSY; ++ reg |= cmd; ++ if (port >= 0) { ++ reg |= QCA8K_ATU_FUNC_PORT_EN; ++ reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port); ++ } ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_ATU_FUNC, QCA8K_ATU_FUNC_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_FDB_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_ATU_FUNC, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_ATU_FUNC_FULL) ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, ++ int port) ++{ ++ int ret; ++ ++ qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port); ++ if (ret < 0) ++ return ret; ++ ++ return qca8k_fdb_read(priv, fdb); ++} ++ ++static int qca8k_fdb_add(struct qca8k_priv *priv, const u8 *mac, ++ u16 port_mask, u16 vid, u8 aging) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int qca8k_fdb_del(struct qca8k_priv *priv, const u8 *mac, ++ u16 port_mask, u16 vid) ++{ ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_write(priv, vid, port_mask, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++void qca8k_fdb_flush(struct qca8k_priv *priv) ++{ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH, -1); ++ mutex_unlock(&priv->reg_mutex); ++} ++ ++static int qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ ret = qca8k_fdb_read(priv, &fdb); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule exist. Delete first */ ++ if (!fdb.aging) { ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ } ++ ++ /* Add port to fdb portmask */ ++ fdb.port_mask |= port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ ++static int qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, ++ const u8 *mac, u16 vid) ++{ ++ struct qca8k_fdb fdb = { 0 }; ++ int ret; ++ ++ mutex_lock(&priv->reg_mutex); ++ ++ qca8k_fdb_write(priv, vid, 0, mac, 0); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); ++ if (ret < 0) ++ goto exit; ++ ++ /* Rule doesn't exist. Why delete? */ ++ if (!fdb.aging) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); ++ if (ret) ++ goto exit; ++ ++ /* Only port in the rule is this port. Don't re insert */ ++ if (fdb.port_mask == port_mask) ++ goto exit; ++ ++ /* Remove port from port mask */ ++ fdb.port_mask &= ~port_mask; ++ ++ qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); ++ ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); ++ ++exit: ++ mutex_unlock(&priv->reg_mutex); ++ return ret; ++} ++ + int qca8k_mib_init(struct qca8k_priv *priv) + { + int ret; +@@ -368,6 +573,15 @@ void qca8k_port_bridge_leave(struct dsa_ + QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); + } + ++void qca8k_port_fast_age(struct dsa_switch *ds, int port) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ ++ mutex_lock(&priv->reg_mutex); ++ qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); ++ mutex_unlock(&priv->reg_mutex); ++} ++ + int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) + { + struct qca8k_priv *priv = ds->priv; +@@ -452,3 +666,78 @@ int qca8k_port_max_mtu(struct dsa_switch + { + return QCA8K_MAX_MTU; + } ++ ++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid) ++{ ++ /* Set the vid to the port vlan id if no vid is set */ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_add(priv, addr, port_mask, vid, ++ QCA8K_ATU_STATUS_STATIC); ++} ++ ++int qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ return qca8k_port_fdb_insert(priv, addr, port_mask, vid); ++} ++ ++int qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ u16 port_mask = BIT(port); ++ ++ if (!vid) ++ vid = QCA8K_PORT_VID_DEF; ++ ++ return qca8k_fdb_del(priv, addr, port_mask, vid); ++} ++ ++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data) ++{ ++ struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; ++ struct qca8k_fdb _fdb = { 0 }; ++ int cnt = QCA8K_NUM_FDB_RECORDS; ++ bool is_static; ++ int ret = 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { ++ if (!_fdb.aging) ++ break; ++ is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); ++ ret = cb(_fdb.mac, _fdb.vid, is_static, data); ++ if (ret) ++ break; ++ } ++ mutex_unlock(&priv->reg_mutex); ++ ++ return 0; ++} ++ ++int qca8k_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); ++} ++ ++int qca8k_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ const u8 *addr = mdb->addr; ++ u16 vid = mdb->vid; ++ ++ return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -430,11 +430,9 @@ int qca8k_read(struct qca8k_priv *priv, + int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val); + int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + +-int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len); +-int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len); +- + /* Common ops function */ + int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); ++void qca8k_fdb_flush(struct qca8k_priv *priv); + + /* Common ethtool stats function */ + void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data); +@@ -463,6 +461,23 @@ int qca8k_port_change_mtu(struct dsa_swi + int qca8k_port_max_mtu(struct dsa_switch *ds, int port); + + /* Common fast age function */ ++void qca8k_port_fast_age(struct dsa_switch *ds, int port); + int qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs); + ++/* Common FDB function */ ++int qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, ++ u16 port_mask, u16 vid); ++int qca8k_port_fdb_add(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid); ++int qca8k_port_fdb_del(struct dsa_switch *ds, int port, ++ const unsigned char *addr, u16 vid); ++int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, ++ dsa_fdb_dump_cb_t *cb, void *data); ++ ++/* Common MDB function */ ++int qca8k_port_mdb_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb); ++int qca8k_port_mdb_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_mdb *mdb); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch b/target/linux/generic/backport-5.15/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch new file mode 100644 index 0000000000..c1336d4a92 --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-11-net-dsa-qca8k-move-port-mirror-functions-to-common-c.patch @@ -0,0 +1,232 @@ +From 742d37a84d3f7bb60d9b2d9ada9ad4e599f65ebf Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:20 +0200 +Subject: [PATCH 11/14] net: dsa: qca8k: move port mirror functions to common + code + +The same port mirror functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 93 ------------------------------ + drivers/net/dsa/qca/qca8k-common.c | 91 +++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 7 +++ + 3 files changed, 98 insertions(+), 93 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1838,99 +1838,6 @@ exit: + } + + static int +-qca8k_port_mirror_add(struct dsa_switch *ds, int port, +- struct dsa_mall_mirror_tc_entry *mirror, +- bool ingress) +-{ +- struct qca8k_priv *priv = ds->priv; +- int monitor_port, ret; +- u32 reg, val; +- +- /* Check for existent entry */ +- if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) +- return -EEXIST; +- +- ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); +- if (ret) +- return ret; +- +- /* QCA83xx can have only one port set to mirror mode. +- * Check that the correct port is requested and return error otherwise. +- * When no mirror port is set, the values is set to 0xF +- */ +- monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (monitor_port != 0xF && monitor_port != mirror->to_local_port) +- return -EEXIST; +- +- /* Set the monitor port */ +- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, +- mirror->to_local_port); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (ret) +- return ret; +- +- if (ingress) { +- reg = QCA8K_PORT_LOOKUP_CTRL(port); +- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +- } else { +- reg = QCA8K_REG_PORT_HOL_CTRL1(port); +- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +- } +- +- ret = regmap_update_bits(priv->regmap, reg, val, val); +- if (ret) +- return ret; +- +- /* Track mirror port for tx and rx to decide when the +- * mirror port has to be disabled. +- */ +- if (ingress) +- priv->mirror_rx |= BIT(port); +- else +- priv->mirror_tx |= BIT(port); +- +- return 0; +-} +- +-static void +-qca8k_port_mirror_del(struct dsa_switch *ds, int port, +- struct dsa_mall_mirror_tc_entry *mirror) +-{ +- struct qca8k_priv *priv = ds->priv; +- u32 reg, val; +- int ret; +- +- if (mirror->ingress) { +- reg = QCA8K_PORT_LOOKUP_CTRL(port); +- val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +- } else { +- reg = QCA8K_REG_PORT_HOL_CTRL1(port); +- val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +- } +- +- ret = regmap_clear_bits(priv->regmap, reg, val); +- if (ret) +- goto err; +- +- if (mirror->ingress) +- priv->mirror_rx &= ~BIT(port); +- else +- priv->mirror_tx &= ~BIT(port); +- +- /* No port set to send packet to mirror port. Disable mirror port */ +- if (!priv->mirror_rx && !priv->mirror_tx) { +- val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +- QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +- if (ret) +- goto err; +- } +-err: +- dev_err(priv->dev, "Failed to del mirror port from %d", port); +-} +- +-static int + qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, + struct netlink_ext_ack *extack) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -741,3 +741,94 @@ int qca8k_port_mdb_del(struct dsa_switch + + return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); + } ++ ++int qca8k_port_mirror_add(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror, ++ bool ingress) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int monitor_port, ret; ++ u32 reg, val; ++ ++ /* Check for existent entry */ ++ if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) ++ return -EEXIST; ++ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* QCA83xx can have only one port set to mirror mode. ++ * Check that the correct port is requested and return error otherwise. ++ * When no mirror port is set, the values is set to 0xF ++ */ ++ monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (monitor_port != 0xF && monitor_port != mirror->to_local_port) ++ return -EEXIST; ++ ++ /* Set the monitor port */ ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, ++ mirror->to_local_port); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ return ret; ++ ++ if (ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_update_bits(priv->regmap, reg, val, val); ++ if (ret) ++ return ret; ++ ++ /* Track mirror port for tx and rx to decide when the ++ * mirror port has to be disabled. ++ */ ++ if (ingress) ++ priv->mirror_rx |= BIT(port); ++ else ++ priv->mirror_tx |= BIT(port); ++ ++ return 0; ++} ++ ++void qca8k_port_mirror_del(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ u32 reg, val; ++ int ret; ++ ++ if (mirror->ingress) { ++ reg = QCA8K_PORT_LOOKUP_CTRL(port); ++ val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; ++ } else { ++ reg = QCA8K_REG_PORT_HOL_CTRL1(port); ++ val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; ++ } ++ ++ ret = regmap_clear_bits(priv->regmap, reg, val); ++ if (ret) ++ goto err; ++ ++ if (mirror->ingress) ++ priv->mirror_rx &= ~BIT(port); ++ else ++ priv->mirror_tx &= ~BIT(port); ++ ++ /* No port set to send packet to mirror port. Disable mirror port */ ++ if (!priv->mirror_rx && !priv->mirror_tx) { ++ val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, ++ QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); ++ if (ret) ++ goto err; ++ } ++err: ++ dev_err(priv->dev, "Failed to del mirror port from %d", port); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -480,4 +480,11 @@ int qca8k_port_mdb_add(struct dsa_switch + int qca8k_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb); + ++/* Common port mirror function */ ++int qca8k_port_mirror_add(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror, ++ bool ingress); ++void qca8k_port_mirror_del(struct dsa_switch *ds, int port, ++ struct dsa_mall_mirror_tc_entry *mirror); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch b/target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch new file mode 100644 index 0000000000..898010f950 --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-12-net-dsa-qca8k-move-port-VLAN-functions-to-common-cod.patch @@ -0,0 +1,448 @@ +From c5290f636624b98e76a82bd63ffec0a8a9daa620 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:21 +0200 +Subject: [PATCH 12/14] net: dsa: qca8k: move port VLAN functions to common + code + +The same port VLAN functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. +Also drop exposing busy_wait and make it static. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 182 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 179 +++++++++++++++++++++++++++- + drivers/net/dsa/qca/qca8k.h | 10 +- + 3 files changed, 187 insertions(+), 184 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -15,7 +15,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -442,122 +441,6 @@ static struct regmap_config qca8k_regmap + }; + + static int +-qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid) +-{ +- u32 reg; +- int ret; +- +- /* Set the command and VLAN index */ +- reg = QCA8K_VTU_FUNC1_BUSY; +- reg |= cmd; +- reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); +- +- /* Write the function register triggering the table access */ +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); +- if (ret) +- return ret; +- +- /* wait for completion */ +- ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); +- if (ret) +- return ret; +- +- /* Check for table full violation when adding an entry */ +- if (cmd == QCA8K_VLAN_LOAD) { +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); +- if (ret < 0) +- return ret; +- if (reg & QCA8K_VTU_FUNC1_FULL) +- return -ENOMEM; +- } +- +- return 0; +-} +- +-static int +-qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged) +-{ +- u32 reg; +- int ret; +- +- /* +- We do the right thing with VLAN 0 and treat it as untagged while +- preserving the tag on egress. +- */ +- if (vid == 0) +- return 0; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- if (untagged) +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); +- else +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); +- +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int +-qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) +-{ +- u32 reg, mask; +- int ret, i; +- bool del; +- +- mutex_lock(&priv->reg_mutex); +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); +- if (ret < 0) +- goto out; +- +- ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); +- if (ret < 0) +- goto out; +- reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +- reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); +- +- /* Check if we're the last member to be removed */ +- del = true; +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); +- +- if ((reg & mask) != mask) { +- del = false; +- break; +- } +- } +- +- if (del) { +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); +- } else { +- ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); +- if (ret) +- goto out; +- ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); +- } +- +-out: +- mutex_unlock(&priv->reg_mutex); +- +- return ret; +-} +- +-static int + qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, + struct sk_buff *read_skb, u32 *val) + { +@@ -1836,71 +1719,6 @@ exit: + + return ret; + } +- +-static int +-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +- struct netlink_ext_ack *extack) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- if (vlan_filtering) { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); +- } else { +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, +- QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_add(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan, +- struct netlink_ext_ack *extack) +-{ +- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); +- if (ret) { +- dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); +- return ret; +- } +- +- if (pvid) { +- ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), +- QCA8K_EGREES_VLAN_PORT_MASK(port), +- QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); +- if (ret) +- return ret; +- +- ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), +- QCA8K_PORT_VLAN_CVID(vlan->vid) | +- QCA8K_PORT_VLAN_SVID(vlan->vid)); +- } +- +- return ret; +-} +- +-static int +-qca8k_port_vlan_del(struct dsa_switch *ds, int port, +- const struct switchdev_obj_port_vlan *vlan) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret; +- +- ret = qca8k_vlan_del(priv, port, vlan->vid); +- if (ret) +- dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); +- +- return ret; +-} + + static u32 qca8k_get_phy_flags(struct dsa_switch *ds, int port) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -141,7 +141,7 @@ static int qca8k_bulk_write(struct qca8k + return 0; + } + +-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) ++static int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) + { + u32 val; + +@@ -354,6 +354,120 @@ exit: + return ret; + } + ++static int qca8k_vlan_access(struct qca8k_priv *priv, ++ enum qca8k_vlan_cmd cmd, u16 vid) ++{ ++ u32 reg; ++ int ret; ++ ++ /* Set the command and VLAN index */ ++ reg = QCA8K_VTU_FUNC1_BUSY; ++ reg |= cmd; ++ reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid); ++ ++ /* Write the function register triggering the table access */ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); ++ if (ret) ++ return ret; ++ ++ /* wait for completion */ ++ ret = qca8k_busy_wait(priv, QCA8K_REG_VTU_FUNC1, QCA8K_VTU_FUNC1_BUSY); ++ if (ret) ++ return ret; ++ ++ /* Check for table full violation when adding an entry */ ++ if (cmd == QCA8K_VLAN_LOAD) { ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC1, ®); ++ if (ret < 0) ++ return ret; ++ if (reg & QCA8K_VTU_FUNC1_FULL) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, ++ bool untagged) ++{ ++ u32 reg; ++ int ret; ++ ++ /* We do the right thing with VLAN 0 and treat it as untagged while ++ * preserving the tag on egress. ++ */ ++ if (vid == 0) ++ return 0; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ if (untagged) ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port); ++ else ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port); ++ ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ ++static int qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid) ++{ ++ u32 reg, mask; ++ int ret, i; ++ bool del; ++ ++ mutex_lock(&priv->reg_mutex); ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_READ, vid); ++ if (ret < 0) ++ goto out; ++ ++ ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®); ++ if (ret < 0) ++ goto out; ++ reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); ++ reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port); ++ ++ /* Check if we're the last member to be removed */ ++ del = true; ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); ++ ++ if ((reg & mask) != mask) { ++ del = false; ++ break; ++ } ++ } ++ ++ if (del) { ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_PURGE, vid); ++ } else { ++ ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg); ++ if (ret) ++ goto out; ++ ret = qca8k_vlan_access(priv, QCA8K_VLAN_LOAD, vid); ++ } ++ ++out: ++ mutex_unlock(&priv->reg_mutex); ++ ++ return ret; ++} ++ + int qca8k_mib_init(struct qca8k_priv *priv) + { + int ret; +@@ -832,3 +946,66 @@ void qca8k_port_mirror_del(struct dsa_sw + err: + dev_err(priv->dev, "Failed to del mirror port from %d", port); + } ++ ++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, ++ bool vlan_filtering, ++ struct netlink_ext_ack *extack) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ if (vlan_filtering) { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE); ++ } else { ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, ++ QCA8K_PORT_LOOKUP_VLAN_MODE_NONE); ++ } ++ ++ return ret; ++} ++ ++int qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack) ++{ ++ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; ++ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_add(priv, port, vlan->vid, untagged); ++ if (ret) { ++ dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret); ++ return ret; ++ } ++ ++ if (pvid) { ++ ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), ++ QCA8K_EGREES_VLAN_PORT_MASK(port), ++ QCA8K_EGREES_VLAN_PORT(port, vlan->vid)); ++ if (ret) ++ return ret; ++ ++ ret = qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port), ++ QCA8K_PORT_VLAN_CVID(vlan->vid) | ++ QCA8K_PORT_VLAN_SVID(vlan->vid)); ++ } ++ ++ return ret; ++} ++ ++int qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret; ++ ++ ret = qca8k_vlan_del(priv, port, vlan->vid); ++ if (ret) ++ dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret); ++ ++ return ret; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -431,7 +431,6 @@ int qca8k_write(struct qca8k_priv *priv, + int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val); + + /* Common ops function */ +-int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask); + void qca8k_fdb_flush(struct qca8k_priv *priv); + + /* Common ethtool stats function */ +@@ -487,4 +486,13 @@ int qca8k_port_mirror_add(struct dsa_swi + void qca8k_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror); + ++/* Common port VLAN function */ ++int qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, ++ struct netlink_ext_ack *extack); ++int qca8k_port_vlan_add(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan, ++ struct netlink_ext_ack *extack); ++int qca8k_port_vlan_del(struct dsa_switch *ds, int port, ++ const struct switchdev_obj_port_vlan *vlan); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch b/target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch new file mode 100644 index 0000000000..1802b17eaa --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-13-net-dsa-qca8k-move-port-LAG-functions-to-common-code.patch @@ -0,0 +1,384 @@ +From e9bbf019af44b204b71ef8edf224002550aab641 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:22 +0200 +Subject: [PATCH 13/14] net: dsa: qca8k: move port LAG functions to common code + +The same port LAG functions are used by drivers based on qca8k family +switch. Move them to common code to make them accessible also by other +drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 168 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 165 ++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 6 ++ + 3 files changed, 171 insertions(+), 168 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1743,178 +1743,6 @@ qca8k_get_tag_protocol(struct dsa_switch + return DSA_TAG_PROTO_QCA; + } + +-static bool +-qca8k_lag_can_offload(struct dsa_switch *ds, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- struct dsa_port *dp; +- int id, members = 0; +- +- id = dsa_lag_id(ds->dst, lag); +- if (id < 0 || id >= ds->num_lag_ids) +- return false; +- +- dsa_lag_foreach_port(dp, ds->dst, lag) +- /* Includes the port joining the LAG */ +- members++; +- +- if (members > QCA8K_NUM_PORTS_FOR_LAG) +- 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 +-qca8k_lag_setup_hash(struct dsa_switch *ds, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- struct qca8k_priv *priv = ds->priv; +- bool unique_lag = true; +- u32 hash = 0; +- int i, id; +- +- id = dsa_lag_id(ds->dst, lag); +- +- switch (info->hash_type) { +- case NETDEV_LAG_HASH_L23: +- hash |= QCA8K_TRUNK_HASH_SIP_EN; +- hash |= QCA8K_TRUNK_HASH_DIP_EN; +- fallthrough; +- case NETDEV_LAG_HASH_L2: +- hash |= QCA8K_TRUNK_HASH_SA_EN; +- hash |= QCA8K_TRUNK_HASH_DA_EN; +- break; +- default: /* We should NEVER reach this */ +- return -EOPNOTSUPP; +- } +- +- /* Check if we are the unique configured LAG */ +- dsa_lags_foreach_id(i, ds->dst) +- if (i != id && dsa_lag_dev(ds->dst, i)) { +- unique_lag = false; +- break; +- } +- +- /* Hash Mode is global. Make sure the same Hash Mode +- * is set to all the 4 possible lag. +- * If we are the unique LAG we can set whatever hash +- * mode we want. +- * To change hash mode it's needed to remove all LAG +- * and change the mode with the latest. +- */ +- if (unique_lag) { +- priv->lag_hash_mode = hash; +- } else if (priv->lag_hash_mode != hash) { +- netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n"); +- return -EOPNOTSUPP; +- } +- +- return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, +- QCA8K_TRUNK_HASH_MASK, hash); +-} +- +-static int +-qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, +- struct net_device *lag, bool delete) +-{ +- struct qca8k_priv *priv = ds->priv; +- int ret, id, i; +- u32 val; +- +- id = dsa_lag_id(ds->dst, lag); +- +- /* Read current port member */ +- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); +- if (ret) +- return ret; +- +- /* Shift val to the correct trunk */ +- val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); +- val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; +- if (delete) +- val &= ~BIT(port); +- else +- val |= BIT(port); +- +- /* Update port member. With empty portmap disable trunk */ +- ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, +- QCA8K_REG_GOL_TRUNK_MEMBER(id) | +- QCA8K_REG_GOL_TRUNK_EN(id), +- !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | +- val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); +- +- /* Search empty member if adding or port on deleting */ +- for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { +- ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); +- if (ret) +- return ret; +- +- val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); +- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; +- +- if (delete) { +- /* If port flagged to be disabled assume this member is +- * empty +- */ +- if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +- continue; +- +- val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; +- if (val != port) +- continue; +- } else { +- /* If port flagged to be enabled assume this member is +- * already set +- */ +- if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +- continue; +- } +- +- /* We have found the member to add/remove */ +- break; +- } +- +- /* Set port in the correct port mask or disable port if in delete mode */ +- return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), +- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | +- QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), +- !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | +- port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); +-} +- +-static int +-qca8k_port_lag_join(struct dsa_switch *ds, int port, +- struct net_device *lag, +- struct netdev_lag_upper_info *info) +-{ +- int ret; +- +- if (!qca8k_lag_can_offload(ds, lag, info)) +- return -EOPNOTSUPP; +- +- ret = qca8k_lag_setup_hash(ds, lag, info); +- if (ret) +- return ret; +- +- return qca8k_lag_refresh_portmap(ds, port, lag, false); +-} +- +-static int +-qca8k_port_lag_leave(struct dsa_switch *ds, int port, +- struct net_device *lag) +-{ +- return qca8k_lag_refresh_portmap(ds, port, lag, true); +-} +- + static void + qca8k_master_change(struct dsa_switch *ds, const struct net_device *master, + bool operational) +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -1009,3 +1009,169 @@ int qca8k_port_vlan_del(struct dsa_switc + + return ret; + } ++ ++static bool qca8k_lag_can_offload(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct dsa_port *dp; ++ int id, members = 0; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ if (id < 0 || id >= ds->num_lag_ids) ++ return false; ++ ++ dsa_lag_foreach_port(dp, ds->dst, lag) ++ /* Includes the port joining the LAG */ ++ members++; ++ ++ if (members > QCA8K_NUM_PORTS_FOR_LAG) ++ 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 qca8k_lag_setup_hash(struct dsa_switch *ds, ++ struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ bool unique_lag = true; ++ u32 hash = 0; ++ int i, id; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ switch (info->hash_type) { ++ case NETDEV_LAG_HASH_L23: ++ hash |= QCA8K_TRUNK_HASH_SIP_EN; ++ hash |= QCA8K_TRUNK_HASH_DIP_EN; ++ fallthrough; ++ case NETDEV_LAG_HASH_L2: ++ hash |= QCA8K_TRUNK_HASH_SA_EN; ++ hash |= QCA8K_TRUNK_HASH_DA_EN; ++ break; ++ default: /* We should NEVER reach this */ ++ return -EOPNOTSUPP; ++ } ++ ++ /* Check if we are the unique configured LAG */ ++ dsa_lags_foreach_id(i, ds->dst) ++ if (i != id && dsa_lag_dev(ds->dst, i)) { ++ unique_lag = false; ++ break; ++ } ++ ++ /* Hash Mode is global. Make sure the same Hash Mode ++ * is set to all the 4 possible lag. ++ * If we are the unique LAG we can set whatever hash ++ * mode we want. ++ * To change hash mode it's needed to remove all LAG ++ * and change the mode with the latest. ++ */ ++ if (unique_lag) { ++ priv->lag_hash_mode = hash; ++ } else if (priv->lag_hash_mode != hash) { ++ netdev_err(lag, "Error: Mismatched Hash Mode across different lag is not supported\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, ++ QCA8K_TRUNK_HASH_MASK, hash); ++} ++ ++static int qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, ++ struct net_device *lag, bool delete) ++{ ++ struct qca8k_priv *priv = ds->priv; ++ int ret, id, i; ++ u32 val; ++ ++ id = dsa_lag_id(ds->dst, lag); ++ ++ /* Read current port member */ ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); ++ if (ret) ++ return ret; ++ ++ /* Shift val to the correct trunk */ ++ val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); ++ val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; ++ if (delete) ++ val &= ~BIT(port); ++ else ++ val |= BIT(port); ++ ++ /* Update port member. With empty portmap disable trunk */ ++ ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, ++ QCA8K_REG_GOL_TRUNK_MEMBER(id) | ++ QCA8K_REG_GOL_TRUNK_EN(id), ++ !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | ++ val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); ++ ++ /* Search empty member if adding or port on deleting */ ++ for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { ++ ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); ++ if (ret) ++ return ret; ++ ++ val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; ++ ++ if (delete) { ++ /* If port flagged to be disabled assume this member is ++ * empty ++ */ ++ if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ ++ val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; ++ if (val != port) ++ continue; ++ } else { ++ /* If port flagged to be enabled assume this member is ++ * already set ++ */ ++ if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) ++ continue; ++ } ++ ++ /* We have found the member to add/remove */ ++ break; ++ } ++ ++ /* Set port in the correct port mask or disable port if in delete mode */ ++ return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | ++ QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), ++ !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | ++ port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); ++} ++ ++int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag, ++ struct netdev_lag_upper_info *info) ++{ ++ int ret; ++ ++ if (!qca8k_lag_can_offload(ds, lag, info)) ++ return -EOPNOTSUPP; ++ ++ ret = qca8k_lag_setup_hash(ds, lag, info); ++ if (ret) ++ return ret; ++ ++ return qca8k_lag_refresh_portmap(ds, port, lag, false); ++} ++ ++int qca8k_port_lag_leave(struct dsa_switch *ds, int port, ++ struct net_device *lag) ++{ ++ return qca8k_lag_refresh_portmap(ds, port, lag, true); ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -495,4 +495,10 @@ int qca8k_port_vlan_add(struct dsa_switc + int qca8k_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); + ++/* Common port LAG function */ ++int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag, ++ struct netdev_lag_upper_info *info); ++int qca8k_port_lag_leave(struct dsa_switch *ds, int port, ++ struct net_device *lag); ++ + #endif /* __QCA8K_H */ diff --git a/target/linux/generic/backport-5.15/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch b/target/linux/generic/backport-5.15/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch new file mode 100644 index 0000000000..d6ec8b77e0 --- /dev/null +++ b/target/linux/generic/backport-5.15/771-v6.0-14-net-dsa-qca8k-move-read_switch_id-function-to-common.patch @@ -0,0 +1,102 @@ +From 9d1bcb1f293f1391302a109c9819c3705c804700 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 27 Jul 2022 13:35:23 +0200 +Subject: [PATCH 14/14] net: dsa: qca8k: move read_switch_id function to common + code + +The same function to read the switch id is used by drivers based on +qca8k family switch. Move them to common code to make them accessible +also by other drivers. + +Signed-off-by: Christian Marangi +Reviewed-by: Vladimir Oltean +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 29 ----------------------------- + drivers/net/dsa/qca/qca8k-common.c | 29 +++++++++++++++++++++++++++++ + drivers/net/dsa/qca/qca8k.h | 1 + + 3 files changed, 30 insertions(+), 29 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1822,35 +1822,6 @@ static const struct dsa_switch_ops qca8k + .connect_tag_protocol = qca8k_connect_tag_protocol, + }; + +-static int qca8k_read_switch_id(struct qca8k_priv *priv) +-{ +- u32 val; +- u8 id; +- int ret; +- +- if (!priv->info) +- return -ENODEV; +- +- ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); +- if (ret < 0) +- return -ENODEV; +- +- id = QCA8K_MASK_CTRL_DEVICE_ID(val); +- if (id != priv->info->id) { +- dev_err(priv->dev, +- "Switch id detected %x but expected %x", +- id, priv->info->id); +- return -ENODEV; +- } +- +- priv->switch_id = id; +- +- /* Save revision to communicate to the internal PHY driver */ +- priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); +- +- return 0; +-} +- + static int + qca8k_sw_probe(struct mdio_device *mdiodev) + { +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -1175,3 +1175,32 @@ int qca8k_port_lag_leave(struct dsa_swit + { + return qca8k_lag_refresh_portmap(ds, port, lag, true); + } ++ ++int qca8k_read_switch_id(struct qca8k_priv *priv) ++{ ++ u32 val; ++ u8 id; ++ int ret; ++ ++ if (!priv->info) ++ return -ENODEV; ++ ++ ret = qca8k_read(priv, QCA8K_REG_MASK_CTRL, &val); ++ if (ret < 0) ++ return -ENODEV; ++ ++ id = QCA8K_MASK_CTRL_DEVICE_ID(val); ++ if (id != priv->info->id) { ++ dev_err(priv->dev, ++ "Switch id detected %x but expected %x", ++ id, priv->info->id); ++ return -ENODEV; ++ } ++ ++ priv->switch_id = id; ++ ++ /* Save revision to communicate to the internal PHY driver */ ++ priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val); ++ ++ return 0; ++} +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -424,6 +424,7 @@ extern const struct qca8k_mib_desc ar832 + extern const struct regmap_access_table qca8k_readable_table; + int qca8k_mib_init(struct qca8k_priv *priv); + void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable); ++int qca8k_read_switch_id(struct qca8k_priv *priv); + + /* Common read/write/rmw function */ + int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val); diff --git a/target/linux/generic/backport-5.15/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch b/target/linux/generic/backport-5.15/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch new file mode 100644 index 0000000000..0cca2788f6 --- /dev/null +++ b/target/linux/generic/backport-5.15/772-v6.0-net-dsa-qca8k-fix-NULL-pointer-dereference-for-of_de.patch @@ -0,0 +1,29 @@ +From 057bcf15db8e625276ddf02b2b7c668a3cb43f81 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sun, 4 Sep 2022 23:46:24 +0200 +Subject: [net PATCH] net: dsa: qca8k: fix NULL pointer dereference for + of_device_get_match_data + +of_device_get_match_data is called on priv->dev before priv->dev is +actually set. Move of_device_get_match_data after priv->dev is correctly +set to fix this kernel panic. + +Fixes: 3bb0844e7bcd ("net: dsa: qca8k: cache match data to speed up access") +Signed-off-by: Christian Marangi +--- + drivers/net/dsa/qca/qca8k-8xxx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/qca/qca8k-8xxx.c ++++ b/drivers/net/dsa/qca/qca8k-8xxx.c +@@ -1835,9 +1835,9 @@ qca8k_sw_probe(struct mdio_device *mdiod + if (!priv) + return -ENOMEM; + +- priv->info = of_device_get_match_data(priv->dev); + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; ++ priv->info = of_device_get_match_data(priv->dev); + + priv->reset_gpio = devm_gpiod_get_optional(priv->dev, "reset", + GPIOD_ASIS); diff --git a/target/linux/generic/backport-5.15/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch b/target/linux/generic/backport-5.15/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch new file mode 100644 index 0000000000..c93bc3b77d --- /dev/null +++ b/target/linux/generic/backport-5.15/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch @@ -0,0 +1,142 @@ +From f9ec5723c3dbfcede9c7b0dcdf85e401ce16316c Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:29 +0200 +Subject: [PATCH 1/5] net: ethernet: stmicro: stmmac: move queue reset to + dedicated functions + +Move queue reset to dedicated functions. This aside from a simple +cleanup is also required to allocate a dma conf without resetting the tx +queue while the device is temporarily detached as now the reset is not +part of the dma init function and can be done later in the code flow. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 59 ++++++++++--------- + 1 file changed, 31 insertions(+), 28 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -130,6 +130,9 @@ static irqreturn_t stmmac_mac_interrupt( + static irqreturn_t stmmac_safety_interrupt(int irq, void *dev_id); + static irqreturn_t stmmac_msi_intr_tx(int irq, void *data); + static irqreturn_t stmmac_msi_intr_rx(int irq, void *data); ++static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue); ++static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue); ++static void stmmac_reset_queues_param(struct stmmac_priv *priv); + static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue); + static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue); + +@@ -1696,9 +1699,6 @@ static int __init_dma_rx_desc_rings(stru + return -ENOMEM; + } + +- rx_q->cur_rx = 0; +- rx_q->dirty_rx = 0; +- + /* Setup the chained descriptor addresses */ + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) +@@ -1804,12 +1804,6 @@ static int __init_dma_tx_desc_rings(stru + tx_q->tx_skbuff[i] = NULL; + } + +- tx_q->dirty_tx = 0; +- tx_q->cur_tx = 0; +- tx_q->mss = 0; +- +- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); +- + return 0; + } + +@@ -2678,10 +2672,7 @@ static void stmmac_tx_err(struct stmmac_ + stmmac_stop_tx_dma(priv, chan); + dma_free_tx_skbufs(priv, chan); + stmmac_clear_tx_descriptors(priv, chan); +- tx_q->dirty_tx = 0; +- tx_q->cur_tx = 0; +- tx_q->mss = 0; +- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, chan)); ++ stmmac_reset_tx_queue(priv, chan); + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); + stmmac_start_tx_dma(priv, chan); +@@ -3756,6 +3747,8 @@ static int stmmac_open(struct net_device + goto init_error; + } + ++ stmmac_reset_queues_param(priv); ++ + ret = stmmac_hw_setup(dev, true); + if (ret < 0) { + netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); +@@ -6395,6 +6388,7 @@ void stmmac_enable_rx_queue(struct stmma + return; + } + ++ stmmac_reset_rx_queue(priv, queue); + stmmac_clear_rx_descriptors(priv, queue); + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, +@@ -6456,6 +6450,7 @@ void stmmac_enable_tx_queue(struct stmma + return; + } + ++ stmmac_reset_tx_queue(priv, queue); + stmmac_clear_tx_descriptors(priv, queue); + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, +@@ -7383,6 +7378,25 @@ int stmmac_suspend(struct device *dev) + } + EXPORT_SYMBOL_GPL(stmmac_suspend); + ++static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) ++{ ++ struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ ++ rx_q->cur_rx = 0; ++ rx_q->dirty_rx = 0; ++} ++ ++static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue) ++{ ++ struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ ++ tx_q->cur_tx = 0; ++ tx_q->dirty_tx = 0; ++ tx_q->mss = 0; ++ ++ netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); ++} ++ + /** + * stmmac_reset_queues_param - reset queue parameters + * @priv: device pointer +@@ -7393,22 +7407,11 @@ static void stmmac_reset_queues_param(st + u32 tx_cnt = priv->plat->tx_queues_to_use; + u32 queue; + +- for (queue = 0; queue < rx_cnt; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ for (queue = 0; queue < rx_cnt; queue++) ++ stmmac_reset_rx_queue(priv, queue); + +- rx_q->cur_rx = 0; +- rx_q->dirty_rx = 0; +- } +- +- for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; +- +- tx_q->cur_tx = 0; +- tx_q->dirty_tx = 0; +- tx_q->mss = 0; +- +- netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); +- } ++ for (queue = 0; queue < tx_cnt; queue++) ++ stmmac_reset_tx_queue(priv, queue); + } + + /** diff --git a/target/linux/generic/backport-5.15/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch b/target/linux/generic/backport-5.15/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch new file mode 100644 index 0000000000..e73422636c --- /dev/null +++ b/target/linux/generic/backport-5.15/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch @@ -0,0 +1,37 @@ +From 7028471edb646bfc532fec0973e50e784cdcb7c6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:30 +0200 +Subject: [PATCH 2/5] net: ethernet: stmicro: stmmac: first disable all queues + and disconnect in release + +Disable all queues and disconnect before tx_disable in stmmac_release to +prevent a corner case where packet may be still queued at the same time +tx_disable is called resulting in kernel panic if some packet still has +to be processed. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -3808,8 +3808,6 @@ static int stmmac_release(struct net_dev + struct stmmac_priv *priv = netdev_priv(dev); + u32 chan; + +- netif_tx_disable(dev); +- + if (device_may_wakeup(priv->device)) + phylink_speed_down(priv->phylink, false); + /* Stop and disconnect the PHY */ +@@ -3821,6 +3819,8 @@ static int stmmac_release(struct net_dev + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) + hrtimer_cancel(&priv->tx_queue[chan].txtimer); + ++ netif_tx_disable(dev); ++ + /* Free the IRQ lines */ + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); + diff --git a/target/linux/generic/backport-5.15/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch b/target/linux/generic/backport-5.15/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch new file mode 100644 index 0000000000..5cb2663c82 --- /dev/null +++ b/target/linux/generic/backport-5.15/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch @@ -0,0 +1,1289 @@ +From 8531c80800c10e8ef7952022326c2f983e1314bf Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:31 +0200 +Subject: [PATCH 3/5] net: ethernet: stmicro: stmmac: move dma conf to + dedicated struct + +Move dma buf conf to dedicated struct. This in preparation for code +rework that will permit to allocate separate dma_conf without affecting +the priv struct. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/chain_mode.c | 6 +- + .../net/ethernet/stmicro/stmmac/ring_mode.c | 4 +- + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 21 +- + .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 4 +- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 286 +++++++++--------- + .../stmicro/stmmac/stmmac_selftests.c | 8 +- + .../net/ethernet/stmicro/stmmac/stmmac_tc.c | 6 +- + 7 files changed, 172 insertions(+), 163 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c ++++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +@@ -46,7 +46,7 @@ static int jumbo_frm(void *p, struct sk_ + + while (len != 0) { + tx_q->tx_skbuff[entry] = NULL; +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + desc = tx_q->dma_tx + entry; + + if (len > bmax) { +@@ -137,7 +137,7 @@ static void refill_desc3(void *priv_ptr, + */ + p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy + + (((rx_q->dirty_rx) + 1) % +- priv->dma_rx_size) * ++ priv->dma_conf.dma_rx_size) * + sizeof(struct dma_desc))); + } + +@@ -155,7 +155,7 @@ static void clean_desc3(void *priv_ptr, + */ + p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy + + ((tx_q->dirty_tx + 1) % +- priv->dma_tx_size)) ++ priv->dma_conf.dma_tx_size)) + * sizeof(struct dma_desc))); + } + +--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c ++++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +@@ -51,7 +51,7 @@ static int jumbo_frm(void *p, struct sk_ + stmmac_prepare_tx_desc(priv, desc, 1, bmax, csum, + STMMAC_RING_MODE, 0, false, skb->len); + tx_q->tx_skbuff[entry] = NULL; +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + + if (priv->extend_desc) + desc = (struct dma_desc *)(tx_q->dma_etx + entry); +@@ -107,7 +107,7 @@ static void refill_desc3(void *priv_ptr, + struct stmmac_priv *priv = rx_q->priv_data; + + /* Fill DES3 in case of RING mode */ +- if (priv->dma_buf_sz == BUF_SIZE_16KiB) ++ if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB) + p->des3 = cpu_to_le32(le32_to_cpu(p->des2) + BUF_SIZE_8KiB); + } + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -185,6 +185,18 @@ struct stmmac_rfs_entry { + int tc; + }; + ++struct stmmac_dma_conf { ++ unsigned int dma_buf_sz; ++ ++ /* RX Queue */ ++ struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; ++ unsigned int dma_rx_size; ++ ++ /* TX Queue */ ++ struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES]; ++ unsigned int dma_tx_size; ++}; ++ + struct stmmac_priv { + /* Frequently used values are kept adjacent for cache effect */ + u32 tx_coal_frames[MTL_MAX_TX_QUEUES]; +@@ -199,7 +211,6 @@ struct stmmac_priv { + int sph_cap; + u32 sarc_type; + +- unsigned int dma_buf_sz; + unsigned int rx_copybreak; + u32 rx_riwt[MTL_MAX_TX_QUEUES]; + int hwts_rx_en; +@@ -211,13 +222,7 @@ struct stmmac_priv { + int (*hwif_quirks)(struct stmmac_priv *priv); + struct mutex lock; + +- /* RX Queue */ +- struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; +- unsigned int dma_rx_size; +- +- /* TX Queue */ +- struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES]; +- unsigned int dma_tx_size; ++ struct stmmac_dma_conf dma_conf; + + /* Generic channel for NAPI */ + struct stmmac_channel channel[STMMAC_CH_MAX]; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +@@ -484,8 +484,8 @@ static void stmmac_get_ringparam(struct + + ring->rx_max_pending = DMA_MAX_RX_SIZE; + ring->tx_max_pending = DMA_MAX_TX_SIZE; +- ring->rx_pending = priv->dma_rx_size; +- ring->tx_pending = priv->dma_tx_size; ++ ring->rx_pending = priv->dma_conf.dma_rx_size; ++ ring->tx_pending = priv->dma_conf.dma_tx_size; + } + + static int stmmac_set_ringparam(struct net_device *netdev, +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -74,8 +74,8 @@ static int phyaddr = -1; + module_param(phyaddr, int, 0444); + MODULE_PARM_DESC(phyaddr, "Physical device address"); + +-#define STMMAC_TX_THRESH(x) ((x)->dma_tx_size / 4) +-#define STMMAC_RX_THRESH(x) ((x)->dma_rx_size / 4) ++#define STMMAC_TX_THRESH(x) ((x)->dma_conf.dma_tx_size / 4) ++#define STMMAC_RX_THRESH(x) ((x)->dma_conf.dma_rx_size / 4) + + /* Limit to make sure XDP TX and slow path can coexist */ + #define STMMAC_XSK_TX_BUDGET_MAX 256 +@@ -232,7 +232,7 @@ static void stmmac_disable_all_queues(st + + /* synchronize_rcu() needed for pending XDP buffers to drain */ + for (queue = 0; queue < rx_queues_cnt; queue++) { +- rx_q = &priv->rx_queue[queue]; ++ rx_q = &priv->dma_conf.rx_queue[queue]; + if (rx_q->xsk_pool) { + synchronize_rcu(); + break; +@@ -358,13 +358,13 @@ static void print_pkt(unsigned char *buf + + static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + u32 avail; + + if (tx_q->dirty_tx > tx_q->cur_tx) + avail = tx_q->dirty_tx - tx_q->cur_tx - 1; + else +- avail = priv->dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1; ++ avail = priv->dma_conf.dma_tx_size - tx_q->cur_tx + tx_q->dirty_tx - 1; + + return avail; + } +@@ -376,13 +376,13 @@ static inline u32 stmmac_tx_avail(struct + */ + static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + u32 dirty; + + if (rx_q->dirty_rx <= rx_q->cur_rx) + dirty = rx_q->cur_rx - rx_q->dirty_rx; + else +- dirty = priv->dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx; ++ dirty = priv->dma_conf.dma_rx_size - rx_q->dirty_rx + rx_q->cur_rx; + + return dirty; + } +@@ -410,7 +410,7 @@ static int stmmac_enable_eee_mode(struct + + /* check if all TX queues have the work finished */ + for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + if (tx_q->dirty_tx != tx_q->cur_tx) + return -EBUSY; /* still unfinished work */ +@@ -1293,7 +1293,7 @@ static void stmmac_display_rx_rings(stru + + /* Display RX rings */ + for (queue = 0; queue < rx_cnt; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + pr_info("\tRX Queue %u rings\n", queue); + +@@ -1306,7 +1306,7 @@ static void stmmac_display_rx_rings(stru + } + + /* Display RX ring */ +- stmmac_display_ring(priv, head_rx, priv->dma_rx_size, true, ++ stmmac_display_ring(priv, head_rx, priv->dma_conf.dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + } +@@ -1320,7 +1320,7 @@ static void stmmac_display_tx_rings(stru + + /* Display TX rings */ + for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + pr_info("\tTX Queue %d rings\n", queue); + +@@ -1335,7 +1335,7 @@ static void stmmac_display_tx_rings(stru + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, head_tx, priv->dma_tx_size, false, ++ stmmac_display_ring(priv, head_tx, priv->dma_conf.dma_tx_size, false, + tx_q->dma_tx_phy, desc_size); + } + } +@@ -1376,21 +1376,21 @@ static int stmmac_set_bfsize(int mtu, in + */ + static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + + /* Clear the RX descriptors */ +- for (i = 0; i < priv->dma_rx_size; i++) ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) + if (priv->extend_desc) + stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic, + priv->use_riwt, priv->mode, +- (i == priv->dma_rx_size - 1), +- priv->dma_buf_sz); ++ (i == priv->dma_conf.dma_rx_size - 1), ++ priv->dma_conf.dma_buf_sz); + else + stmmac_init_rx_desc(priv, &rx_q->dma_rx[i], + priv->use_riwt, priv->mode, +- (i == priv->dma_rx_size - 1), +- priv->dma_buf_sz); ++ (i == priv->dma_conf.dma_rx_size - 1), ++ priv->dma_conf.dma_buf_sz); + } + + /** +@@ -1402,12 +1402,12 @@ static void stmmac_clear_rx_descriptors( + */ + static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int i; + + /* Clear the TX descriptors */ +- for (i = 0; i < priv->dma_tx_size; i++) { +- int last = (i == (priv->dma_tx_size - 1)); ++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { ++ int last = (i == (priv->dma_conf.dma_tx_size - 1)); + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1455,7 +1455,7 @@ static void stmmac_clear_descriptors(str + static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, + int i, gfp_t flags, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->page) { +@@ -1480,7 +1480,7 @@ static int stmmac_init_rx_buffers(struct + buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; + + stmmac_set_desc_addr(priv, p, buf->addr); +- if (priv->dma_buf_sz == BUF_SIZE_16KiB) ++ if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB) + stmmac_init_desc3(priv, p); + + return 0; +@@ -1494,7 +1494,7 @@ static int stmmac_init_rx_buffers(struct + */ + static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (buf->page) +@@ -1514,7 +1514,7 @@ static void stmmac_free_rx_buffer(struct + */ + static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + if (tx_q->tx_skbuff_dma[i].buf && + tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) { +@@ -1559,17 +1559,17 @@ static void dma_free_rx_skbufs(struct st + { + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) + stmmac_free_rx_buffer(priv, queue, i); + } + + static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, u32 queue, + gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { + struct dma_desc *p; + int ret; + +@@ -1596,10 +1596,10 @@ static int stmmac_alloc_rx_buffers(struc + */ + static void dma_free_rx_xskbufs(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->xdp) +@@ -1612,10 +1612,10 @@ static void dma_free_rx_xskbufs(struct s + + static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_rx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { + struct stmmac_rx_buffer *buf; + dma_addr_t dma_addr; + struct dma_desc *p; +@@ -1658,7 +1658,7 @@ static struct xsk_buff_pool *stmmac_get_ + */ + static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, u32 queue, gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int ret; + + netif_dbg(priv, probe, priv->dev, +@@ -1704,11 +1704,11 @@ static int __init_dma_rx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, rx_q->dma_erx, + rx_q->dma_rx_phy, +- priv->dma_rx_size, 1); ++ priv->dma_conf.dma_rx_size, 1); + else + stmmac_mode_init(priv, rx_q->dma_rx, + rx_q->dma_rx_phy, +- priv->dma_rx_size, 0); ++ priv->dma_conf.dma_rx_size, 0); + } + + return 0; +@@ -1735,7 +1735,7 @@ static int init_dma_rx_desc_rings(struct + + err_init_rx_buffers: + while (queue >= 0) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + if (rx_q->xsk_pool) + dma_free_rx_xskbufs(priv, queue); +@@ -1764,7 +1764,7 @@ err_init_rx_buffers: + */ + static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int i; + + netif_dbg(priv, probe, priv->dev, +@@ -1776,16 +1776,16 @@ static int __init_dma_tx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, tx_q->dma_etx, + tx_q->dma_tx_phy, +- priv->dma_tx_size, 1); ++ priv->dma_conf.dma_tx_size, 1); + else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) + stmmac_mode_init(priv, tx_q->dma_tx, + tx_q->dma_tx_phy, +- priv->dma_tx_size, 0); ++ priv->dma_conf.dma_tx_size, 0); + } + + tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue); + +- for (i = 0; i < priv->dma_tx_size; i++) { ++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1855,12 +1855,12 @@ static int init_dma_desc_rings(struct ne + */ + static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int i; + + tx_q->xsk_frames_done = 0; + +- for (i = 0; i < priv->dma_tx_size; i++) ++ for (i = 0; i < priv->dma_conf.dma_tx_size; i++) + stmmac_free_tx_buffer(priv, queue, i); + + if (tx_q->xsk_pool && tx_q->xsk_frames_done) { +@@ -1890,7 +1890,7 @@ static void stmmac_free_tx_skbufs(struct + */ + static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + /* Release the DMA RX socket buffers */ + if (rx_q->xsk_pool) +@@ -1903,11 +1903,11 @@ static void __free_dma_rx_desc_resources + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) +- dma_free_coherent(priv->device, priv->dma_rx_size * ++ dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * + sizeof(struct dma_desc), + rx_q->dma_rx, rx_q->dma_rx_phy); + else +- dma_free_coherent(priv->device, priv->dma_rx_size * ++ dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * + sizeof(struct dma_extended_desc), + rx_q->dma_erx, rx_q->dma_rx_phy); + +@@ -1936,7 +1936,7 @@ static void free_dma_rx_desc_resources(s + */ + static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + size_t size; + void *addr; + +@@ -1954,7 +1954,7 @@ static void __free_dma_tx_desc_resources + addr = tx_q->dma_tx; + } + +- size *= priv->dma_tx_size; ++ size *= priv->dma_conf.dma_tx_size; + + dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); + +@@ -1983,7 +1983,7 @@ static void free_dma_tx_desc_resources(s + */ + static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + bool xdp_prog = stmmac_xdp_is_enabled(priv); + struct page_pool_params pp_params = { 0 }; +@@ -1995,8 +1995,8 @@ static int __alloc_dma_rx_desc_resources + rx_q->priv_data = priv; + + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; +- pp_params.pool_size = priv->dma_rx_size; +- num_pages = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE); ++ pp_params.pool_size = priv->dma_conf.dma_rx_size; ++ num_pages = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE); + pp_params.order = ilog2(num_pages); + pp_params.nid = dev_to_node(priv->device); + pp_params.dev = priv->device; +@@ -2011,7 +2011,7 @@ static int __alloc_dma_rx_desc_resources + return ret; + } + +- rx_q->buf_pool = kcalloc(priv->dma_rx_size, ++ rx_q->buf_pool = kcalloc(priv->dma_conf.dma_rx_size, + sizeof(*rx_q->buf_pool), + GFP_KERNEL); + if (!rx_q->buf_pool) +@@ -2019,7 +2019,7 @@ static int __alloc_dma_rx_desc_resources + + if (priv->extend_desc) { + rx_q->dma_erx = dma_alloc_coherent(priv->device, +- priv->dma_rx_size * ++ priv->dma_conf.dma_rx_size * + sizeof(struct dma_extended_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2028,7 +2028,7 @@ static int __alloc_dma_rx_desc_resources + + } else { + rx_q->dma_rx = dma_alloc_coherent(priv->device, +- priv->dma_rx_size * ++ priv->dma_conf.dma_rx_size * + sizeof(struct dma_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2085,20 +2085,20 @@ err_dma: + */ + static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + size_t size; + void *addr; + + tx_q->queue_index = queue; + tx_q->priv_data = priv; + +- tx_q->tx_skbuff_dma = kcalloc(priv->dma_tx_size, ++ tx_q->tx_skbuff_dma = kcalloc(priv->dma_conf.dma_tx_size, + sizeof(*tx_q->tx_skbuff_dma), + GFP_KERNEL); + if (!tx_q->tx_skbuff_dma) + return -ENOMEM; + +- tx_q->tx_skbuff = kcalloc(priv->dma_tx_size, ++ tx_q->tx_skbuff = kcalloc(priv->dma_conf.dma_tx_size, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!tx_q->tx_skbuff) +@@ -2111,7 +2111,7 @@ static int __alloc_dma_tx_desc_resources + else + size = sizeof(struct dma_desc); + +- size *= priv->dma_tx_size; ++ size *= priv->dma_conf.dma_tx_size; + + addr = dma_alloc_coherent(priv->device, size, + &tx_q->dma_tx_phy, GFP_KERNEL); +@@ -2355,7 +2355,7 @@ static void stmmac_dma_operation_mode(st + + /* configure all channels */ + for (chan = 0; chan < rx_channels_count; chan++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; + u32 buf_size; + + qmode = priv->plat->rx_queues_cfg[chan].mode_to_use; +@@ -2370,7 +2370,7 @@ static void stmmac_dma_operation_mode(st + chan); + } else { + stmmac_set_dma_bfsize(priv, priv->ioaddr, +- priv->dma_buf_sz, ++ priv->dma_conf.dma_buf_sz, + chan); + } + } +@@ -2386,7 +2386,7 @@ static void stmmac_dma_operation_mode(st + static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) + { + struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue); +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct xsk_buff_pool *pool = tx_q->xsk_pool; + unsigned int entry = tx_q->cur_tx; + struct dma_desc *tx_desc = NULL; +@@ -2461,7 +2461,7 @@ static bool stmmac_xdp_xmit_zc(struct st + + stmmac_enable_dma_transmission(priv, priv->ioaddr); + +- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size); ++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + entry = tx_q->cur_tx; + } + +@@ -2487,7 +2487,7 @@ static bool stmmac_xdp_xmit_zc(struct st + */ + static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + unsigned int bytes_compl = 0, pkts_compl = 0; + unsigned int entry, xmits = 0, count = 0; + +@@ -2500,7 +2500,7 @@ static int stmmac_tx_clean(struct stmmac + entry = tx_q->dirty_tx; + + /* Try to clean all TX complete frame in 1 shot */ +- while ((entry != tx_q->cur_tx) && count < priv->dma_tx_size) { ++ while ((entry != tx_q->cur_tx) && count < priv->dma_conf.dma_tx_size) { + struct xdp_frame *xdpf; + struct sk_buff *skb; + struct dma_desc *p; +@@ -2600,7 +2600,7 @@ static int stmmac_tx_clean(struct stmmac + + stmmac_release_tx_desc(priv, p, priv->mode); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + } + tx_q->dirty_tx = entry; + +@@ -2665,7 +2665,7 @@ static int stmmac_tx_clean(struct stmmac + */ + static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); + +@@ -2732,8 +2732,8 @@ static int stmmac_napi_check(struct stmm + { + int status = stmmac_dma_interrupt_status(priv, priv->ioaddr, + &priv->xstats, chan, dir); +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_channel *ch = &priv->channel[chan]; + struct napi_struct *rx_napi; + struct napi_struct *tx_napi; +@@ -2909,7 +2909,7 @@ static int stmmac_init_dma_engine(struct + + /* DMA RX Channel Configuration */ + for (chan = 0; chan < rx_channels_count; chan++) { +- rx_q = &priv->rx_queue[chan]; ++ rx_q = &priv->dma_conf.rx_queue[chan]; + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + rx_q->dma_rx_phy, chan); +@@ -2923,7 +2923,7 @@ static int stmmac_init_dma_engine(struct + + /* DMA TX Channel Configuration */ + for (chan = 0; chan < tx_channels_count; chan++) { +- tx_q = &priv->tx_queue[chan]; ++ tx_q = &priv->dma_conf.tx_queue[chan]; + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); +@@ -2938,7 +2938,7 @@ static int stmmac_init_dma_engine(struct + + static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + hrtimer_start(&tx_q->txtimer, + STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]), +@@ -2988,7 +2988,7 @@ static void stmmac_init_coalesce(struct + u32 chan; + + for (chan = 0; chan < tx_channel_count; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + + priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES; + priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER; +@@ -3010,12 +3010,12 @@ static void stmmac_set_rings_length(stru + /* set TX ring length */ + for (chan = 0; chan < tx_channels_count; chan++) + stmmac_set_tx_ring_len(priv, priv->ioaddr, +- (priv->dma_tx_size - 1), chan); ++ (priv->dma_conf.dma_tx_size - 1), chan); + + /* set RX ring length */ + for (chan = 0; chan < rx_channels_count; chan++) + stmmac_set_rx_ring_len(priv, priv->ioaddr, +- (priv->dma_rx_size - 1), chan); ++ (priv->dma_conf.dma_rx_size - 1), chan); + } + + /** +@@ -3350,7 +3350,7 @@ static int stmmac_hw_setup(struct net_de + /* Enable TSO */ + if (priv->tso) { + for (chan = 0; chan < tx_cnt; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + + /* TSO and TBS cannot co-exist */ + if (tx_q->tbs & STMMAC_TBS_AVAIL) +@@ -3372,7 +3372,7 @@ static int stmmac_hw_setup(struct net_de + + /* TBS */ + for (chan = 0; chan < tx_cnt; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + int enable = tx_q->tbs & STMMAC_TBS_AVAIL; + + stmmac_enable_tbs(priv, priv->ioaddr, enable, chan); +@@ -3416,7 +3416,7 @@ static void stmmac_free_irq(struct net_d + for (j = irq_idx - 1; j >= 0; j--) { + if (priv->tx_irq[j] > 0) { + irq_set_affinity_hint(priv->tx_irq[j], NULL); +- free_irq(priv->tx_irq[j], &priv->tx_queue[j]); ++ free_irq(priv->tx_irq[j], &priv->dma_conf.tx_queue[j]); + } + } + irq_idx = priv->plat->rx_queues_to_use; +@@ -3425,7 +3425,7 @@ static void stmmac_free_irq(struct net_d + for (j = irq_idx - 1; j >= 0; j--) { + if (priv->rx_irq[j] > 0) { + irq_set_affinity_hint(priv->rx_irq[j], NULL); +- free_irq(priv->rx_irq[j], &priv->rx_queue[j]); ++ free_irq(priv->rx_irq[j], &priv->dma_conf.rx_queue[j]); + } + } + +@@ -3558,7 +3558,7 @@ static int stmmac_request_irq_multi_msi( + sprintf(int_name, "%s:%s-%d", dev->name, "rx", i); + ret = request_irq(priv->rx_irq[i], + stmmac_msi_intr_rx, +- 0, int_name, &priv->rx_queue[i]); ++ 0, int_name, &priv->dma_conf.rx_queue[i]); + if (unlikely(ret < 0)) { + netdev_err(priv->dev, + "%s: alloc rx-%d MSI %d (error: %d)\n", +@@ -3581,7 +3581,7 @@ static int stmmac_request_irq_multi_msi( + sprintf(int_name, "%s:%s-%d", dev->name, "tx", i); + ret = request_irq(priv->tx_irq[i], + stmmac_msi_intr_tx, +- 0, int_name, &priv->tx_queue[i]); ++ 0, int_name, &priv->dma_conf.tx_queue[i]); + if (unlikely(ret < 0)) { + netdev_err(priv->dev, + "%s: alloc tx-%d MSI %d (error: %d)\n", +@@ -3712,21 +3712,21 @@ static int stmmac_open(struct net_device + bfsize = 0; + + if (bfsize < BUF_SIZE_16KiB) +- bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); ++ bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_conf.dma_buf_sz); + +- priv->dma_buf_sz = bfsize; ++ priv->dma_conf.dma_buf_sz = bfsize; + buf_sz = bfsize; + + priv->rx_copybreak = STMMAC_RX_COPYBREAK; + +- if (!priv->dma_tx_size) +- priv->dma_tx_size = DMA_DEFAULT_TX_SIZE; +- if (!priv->dma_rx_size) +- priv->dma_rx_size = DMA_DEFAULT_RX_SIZE; ++ if (!priv->dma_conf.dma_tx_size) ++ priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE; ++ if (!priv->dma_conf.dma_rx_size) ++ priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE; + + /* Earlier check for TBS */ + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en; + + /* Setup per-TXQ tbs flag before TX descriptor alloc */ +@@ -3775,7 +3775,7 @@ irq_error: + phylink_stop(priv->phylink); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + stmmac_hw_teardown(dev); + init_error: +@@ -3817,7 +3817,7 @@ static int stmmac_release(struct net_dev + stmmac_disable_all_queues(priv); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + netif_tx_disable(dev); + +@@ -3877,7 +3877,7 @@ static bool stmmac_vlan_insert(struct st + return false; + + stmmac_set_tx_owner(priv, p); +- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size); ++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + return true; + } + +@@ -3895,7 +3895,7 @@ static bool stmmac_vlan_insert(struct st + static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des, + int total_len, bool last_segment, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct dma_desc *desc; + u32 buff_size; + int tmp_len; +@@ -3906,7 +3906,7 @@ static void stmmac_tso_allocator(struct + dma_addr_t curr_addr; + + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, +- priv->dma_tx_size); ++ priv->dma_conf.dma_tx_size); + WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); + + if (tx_q->tbs & STMMAC_TBS_AVAIL) +@@ -3934,7 +3934,7 @@ static void stmmac_tso_allocator(struct + + static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + int desc_size; + + if (likely(priv->extend_desc)) +@@ -3996,7 +3996,7 @@ static netdev_tx_t stmmac_tso_xmit(struc + dma_addr_t des; + int i; + +- tx_q = &priv->tx_queue[queue]; ++ tx_q = &priv->dma_conf.tx_queue[queue]; + first_tx = tx_q->cur_tx; + + /* Compute header lengths */ +@@ -4036,7 +4036,7 @@ static netdev_tx_t stmmac_tso_xmit(struc + stmmac_set_mss(priv, mss_desc, mss); + tx_q->mss = mss; + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, +- priv->dma_tx_size); ++ priv->dma_conf.dma_tx_size); + WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); + } + +@@ -4148,7 +4148,7 @@ static netdev_tx_t stmmac_tso_xmit(struc + * ndo_start_xmit will fill this descriptor the next time it's + * called and stmmac_tx_clean may clean up to this descriptor. + */ +- tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_tx_size); ++ tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); + + if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { + netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", +@@ -4236,7 +4236,7 @@ static netdev_tx_t stmmac_xmit(struct sk + int entry, first_tx; + dma_addr_t des; + +- tx_q = &priv->tx_queue[queue]; ++ tx_q = &priv->dma_conf.tx_queue[queue]; + first_tx = tx_q->cur_tx; + + if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en) +@@ -4299,7 +4299,7 @@ static netdev_tx_t stmmac_xmit(struct sk + int len = skb_frag_size(frag); + bool last_segment = (i == (nfrags - 1)); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + WARN_ON(tx_q->tx_skbuff[entry]); + + if (likely(priv->extend_desc)) +@@ -4370,7 +4370,7 @@ static netdev_tx_t stmmac_xmit(struct sk + * ndo_start_xmit will fill this descriptor the next time it's + * called and stmmac_tx_clean may clean up to this descriptor. + */ +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + tx_q->cur_tx = entry; + + if (netif_msg_pktdata(priv)) { +@@ -4485,7 +4485,7 @@ static void stmmac_rx_vlan(struct net_de + */ + static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + int dirty = stmmac_rx_dirty(priv, queue); + unsigned int entry = rx_q->dirty_rx; + +@@ -4535,7 +4535,7 @@ static inline void stmmac_rx_refill(stru + dma_wmb(); + stmmac_set_rx_owner(priv, p, use_rx_wd); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_rx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size); + } + rx_q->dirty_rx = entry; + rx_q->rx_tail_addr = rx_q->dma_rx_phy + +@@ -4563,12 +4563,12 @@ static unsigned int stmmac_rx_buf1_len(s + + /* First descriptor, not last descriptor and not split header */ + if (status & rx_not_ls) +- return priv->dma_buf_sz; ++ return priv->dma_conf.dma_buf_sz; + + plen = stmmac_get_rx_frame_len(priv, p, coe); + + /* First descriptor and last descriptor and not split header */ +- return min_t(unsigned int, priv->dma_buf_sz, plen); ++ return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen); + } + + static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, +@@ -4584,7 +4584,7 @@ static unsigned int stmmac_rx_buf2_len(s + + /* Not last descriptor */ + if (status & rx_not_ls) +- return priv->dma_buf_sz; ++ return priv->dma_conf.dma_buf_sz; + + plen = stmmac_get_rx_frame_len(priv, p, coe); + +@@ -4595,7 +4595,7 @@ static unsigned int stmmac_rx_buf2_len(s + static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, + struct xdp_frame *xdpf, bool dma_map) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + unsigned int entry = tx_q->cur_tx; + struct dma_desc *tx_desc; + dma_addr_t dma_addr; +@@ -4658,7 +4658,7 @@ static int stmmac_xdp_xmit_xdpf(struct s + + stmmac_enable_dma_transmission(priv, priv->ioaddr); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_tx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_tx_size); + tx_q->cur_tx = entry; + + return STMMAC_XDP_TX; +@@ -4832,7 +4832,7 @@ static void stmmac_dispatch_skb_zc(struc + + static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + unsigned int entry = rx_q->dirty_rx; + struct dma_desc *rx_desc = NULL; + bool ret = true; +@@ -4875,7 +4875,7 @@ static bool stmmac_rx_refill_zc(struct s + dma_wmb(); + stmmac_set_rx_owner(priv, rx_desc, use_rx_wd); + +- entry = STMMAC_GET_ENTRY(entry, priv->dma_rx_size); ++ entry = STMMAC_GET_ENTRY(entry, priv->dma_conf.dma_rx_size); + } + + if (rx_desc) { +@@ -4890,7 +4890,7 @@ static bool stmmac_rx_refill_zc(struct s + + static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + unsigned int count = 0, error = 0, len = 0; + int dirty = stmmac_rx_dirty(priv, queue); + unsigned int next_entry = rx_q->cur_rx; +@@ -4912,7 +4912,7 @@ static int stmmac_rx_zc(struct stmmac_pr + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, rx_head, priv->dma_rx_size, true, ++ stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + while (count < limit) { +@@ -4959,7 +4959,7 @@ read_again: + + /* Prefetch the next RX descriptor */ + rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, +- priv->dma_rx_size); ++ priv->dma_conf.dma_rx_size); + next_entry = rx_q->cur_rx; + + if (priv->extend_desc) +@@ -5080,7 +5080,7 @@ read_again: + */ + static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + unsigned int count = 0, error = 0, len = 0; + int status = 0, coe = priv->hw->rx_csum; +@@ -5093,7 +5093,7 @@ static int stmmac_rx(struct stmmac_priv + int buf_sz; + + dma_dir = page_pool_get_dma_dir(rx_q->page_pool); +- buf_sz = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; ++ buf_sz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; + + if (netif_msg_rx_status(priv)) { + void *rx_head; +@@ -5107,7 +5107,7 @@ static int stmmac_rx(struct stmmac_priv + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, rx_head, priv->dma_rx_size, true, ++ stmmac_display_ring(priv, rx_head, priv->dma_conf.dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + while (count < limit) { +@@ -5151,7 +5151,7 @@ read_again: + break; + + rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, +- priv->dma_rx_size); ++ priv->dma_conf.dma_rx_size); + next_entry = rx_q->cur_rx; + + if (priv->extend_desc) +@@ -5285,7 +5285,7 @@ read_again: + buf1_len, dma_dir); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + buf->page, buf->page_offset, buf1_len, +- priv->dma_buf_sz); ++ priv->dma_conf.dma_buf_sz); + + /* Data payload appended into SKB */ + page_pool_release_page(rx_q->page_pool, buf->page); +@@ -5297,7 +5297,7 @@ read_again: + buf2_len, dma_dir); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + buf->sec_page, 0, buf2_len, +- priv->dma_buf_sz); ++ priv->dma_conf.dma_buf_sz); + + /* Data payload appended into SKB */ + page_pool_release_page(rx_q->page_pool, buf->sec_page); +@@ -5739,11 +5739,13 @@ static irqreturn_t stmmac_safety_interru + static irqreturn_t stmmac_msi_intr_tx(int irq, void *data) + { + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data; ++ struct stmmac_dma_conf *dma_conf; + int chan = tx_q->queue_index; + struct stmmac_priv *priv; + int status; + +- priv = container_of(tx_q, struct stmmac_priv, tx_queue[chan]); ++ dma_conf = container_of(tx_q, struct stmmac_dma_conf, tx_queue[chan]); ++ priv = container_of(dma_conf, struct stmmac_priv, dma_conf); + + if (unlikely(!data)) { + netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__); +@@ -5783,10 +5785,12 @@ static irqreturn_t stmmac_msi_intr_tx(in + static irqreturn_t stmmac_msi_intr_rx(int irq, void *data) + { + struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data; ++ struct stmmac_dma_conf *dma_conf; + int chan = rx_q->queue_index; + struct stmmac_priv *priv; + +- priv = container_of(rx_q, struct stmmac_priv, rx_queue[chan]); ++ dma_conf = container_of(rx_q, struct stmmac_dma_conf, rx_queue[chan]); ++ priv = container_of(dma_conf, struct stmmac_priv, dma_conf); + + if (unlikely(!data)) { + netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__); +@@ -5817,10 +5821,10 @@ static void stmmac_poll_controller(struc + + if (priv->plat->multi_msi_en) { + for (i = 0; i < priv->plat->rx_queues_to_use; i++) +- stmmac_msi_intr_rx(0, &priv->rx_queue[i]); ++ stmmac_msi_intr_rx(0, &priv->dma_conf.rx_queue[i]); + + for (i = 0; i < priv->plat->tx_queues_to_use; i++) +- stmmac_msi_intr_tx(0, &priv->tx_queue[i]); ++ stmmac_msi_intr_tx(0, &priv->dma_conf.tx_queue[i]); + } else { + disable_irq(dev->irq); + stmmac_interrupt(dev->irq, dev); +@@ -6001,34 +6005,34 @@ static int stmmac_rings_status_show(stru + return 0; + + for (queue = 0; queue < rx_count; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + seq_printf(seq, "RX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_erx, +- priv->dma_rx_size, 1, seq, rx_q->dma_rx_phy); ++ priv->dma_conf.dma_rx_size, 1, seq, rx_q->dma_rx_phy); + } else { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_rx, +- priv->dma_rx_size, 0, seq, rx_q->dma_rx_phy); ++ priv->dma_conf.dma_rx_size, 0, seq, rx_q->dma_rx_phy); + } + } + + for (queue = 0; queue < tx_count; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + seq_printf(seq, "TX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_etx, +- priv->dma_tx_size, 1, seq, tx_q->dma_tx_phy); ++ priv->dma_conf.dma_tx_size, 1, seq, tx_q->dma_tx_phy); + } else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_tx, +- priv->dma_tx_size, 0, seq, tx_q->dma_tx_phy); ++ priv->dma_conf.dma_tx_size, 0, seq, tx_q->dma_tx_phy); + } + } + +@@ -6369,7 +6373,7 @@ void stmmac_disable_rx_queue(struct stmm + + void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + unsigned long flags; + u32 buf_size; +@@ -6406,7 +6410,7 @@ void stmmac_enable_rx_queue(struct stmma + rx_q->queue_index); + } else { + stmmac_set_dma_bfsize(priv, priv->ioaddr, +- priv->dma_buf_sz, ++ priv->dma_conf.dma_buf_sz, + rx_q->queue_index); + } + +@@ -6432,7 +6436,7 @@ void stmmac_disable_tx_queue(struct stmm + + void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + unsigned long flags; + int ret; +@@ -6479,7 +6483,7 @@ void stmmac_xdp_release(struct net_devic + stmmac_disable_all_queues(priv); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + /* Free the IRQ lines */ + stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); +@@ -6538,7 +6542,7 @@ int stmmac_xdp_open(struct net_device *d + + /* DMA RX Channel Configuration */ + for (chan = 0; chan < rx_cnt; chan++) { +- rx_q = &priv->rx_queue[chan]; ++ rx_q = &priv->dma_conf.rx_queue[chan]; + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + rx_q->dma_rx_phy, chan); +@@ -6556,7 +6560,7 @@ int stmmac_xdp_open(struct net_device *d + rx_q->queue_index); + } else { + stmmac_set_dma_bfsize(priv, priv->ioaddr, +- priv->dma_buf_sz, ++ priv->dma_conf.dma_buf_sz, + rx_q->queue_index); + } + +@@ -6565,7 +6569,7 @@ int stmmac_xdp_open(struct net_device *d + + /* DMA TX Channel Configuration */ + for (chan = 0; chan < tx_cnt; chan++) { +- tx_q = &priv->tx_queue[chan]; ++ tx_q = &priv->dma_conf.tx_queue[chan]; + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); +@@ -6598,7 +6602,7 @@ int stmmac_xdp_open(struct net_device *d + + irq_error: + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + stmmac_hw_teardown(dev); + init_error: +@@ -6625,8 +6629,8 @@ int stmmac_xsk_wakeup(struct net_device + queue >= priv->plat->tx_queues_to_use) + return -EINVAL; + +- rx_q = &priv->rx_queue[queue]; +- tx_q = &priv->tx_queue[queue]; ++ rx_q = &priv->dma_conf.rx_queue[queue]; ++ tx_q = &priv->dma_conf.tx_queue[queue]; + ch = &priv->channel[queue]; + + if (!rx_q->xsk_pool && !tx_q->xsk_pool) +@@ -6882,8 +6886,8 @@ int stmmac_reinit_ringparam(struct net_d + if (netif_running(dev)) + stmmac_release(dev); + +- priv->dma_rx_size = rx_size; +- priv->dma_tx_size = tx_size; ++ priv->dma_conf.dma_rx_size = rx_size; ++ priv->dma_conf.dma_tx_size = tx_size; + + if (netif_running(dev)) + ret = stmmac_open(dev); +@@ -7329,7 +7333,7 @@ int stmmac_suspend(struct device *dev) + stmmac_disable_all_queues(priv); + + for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) +- hrtimer_cancel(&priv->tx_queue[chan].txtimer); ++ hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer); + + if (priv->eee_enabled) { + priv->tx_path_in_lpi_mode = false; +@@ -7380,7 +7384,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend); + + static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + + rx_q->cur_rx = 0; + rx_q->dirty_rx = 0; +@@ -7388,7 +7392,7 @@ static void stmmac_reset_rx_queue(struct + + static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + + tx_q->cur_tx = 0; + tx_q->dirty_tx = 0; +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +@@ -795,8 +795,8 @@ static int stmmac_test_flowctrl(struct s + struct stmmac_channel *ch = &priv->channel[i]; + u32 tail; + +- tail = priv->rx_queue[i].dma_rx_phy + +- (priv->dma_rx_size * sizeof(struct dma_desc)); ++ tail = priv->dma_conf.rx_queue[i].dma_rx_phy + ++ (priv->dma_conf.dma_rx_size * sizeof(struct dma_desc)); + + stmmac_set_rx_tail_ptr(priv, priv->ioaddr, tail, i); + stmmac_start_rx(priv, priv->ioaddr, i); +@@ -1680,7 +1680,7 @@ cleanup: + static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue) + { + struct stmmac_packet_attrs attr = { }; +- int size = priv->dma_buf_sz; ++ int size = priv->dma_conf.dma_buf_sz; + + attr.dst = priv->dev->dev_addr; + attr.max_size = size - ETH_FCS_LEN; +@@ -1763,7 +1763,7 @@ static int stmmac_test_tbs(struct stmmac + + /* Find first TBS enabled Queue, if any */ + for (i = 0; i < priv->plat->tx_queues_to_use; i++) +- if (priv->tx_queue[i].tbs & STMMAC_TBS_AVAIL) ++ if (priv->dma_conf.tx_queue[i].tbs & STMMAC_TBS_AVAIL) + break; + + if (i >= priv->plat->tx_queues_to_use) +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +@@ -970,13 +970,13 @@ static int tc_setup_etf(struct stmmac_pr + return -EOPNOTSUPP; + if (qopt->queue >= priv->plat->tx_queues_to_use) + return -EINVAL; +- if (!(priv->tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL)) ++ if (!(priv->dma_conf.tx_queue[qopt->queue].tbs & STMMAC_TBS_AVAIL)) + return -EINVAL; + + if (qopt->enable) +- priv->tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN; ++ priv->dma_conf.tx_queue[qopt->queue].tbs |= STMMAC_TBS_EN; + else +- priv->tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN; ++ priv->dma_conf.tx_queue[qopt->queue].tbs &= ~STMMAC_TBS_EN; + + netdev_info(priv->dev, "%s ETF for Queue %d\n", + qopt->enable ? "enabled" : "disabled", qopt->queue); diff --git a/target/linux/generic/backport-5.15/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch b/target/linux/generic/backport-5.15/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch new file mode 100644 index 0000000000..c8544fcda3 --- /dev/null +++ b/target/linux/generic/backport-5.15/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch @@ -0,0 +1,1161 @@ +From ba39b344e9240a4a5fd4ab8178200b85cd1809da Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:32 +0200 +Subject: [PATCH 4/5] net: ethernet: stmicro: stmmac: generate stmmac dma conf + before open + +Rework the driver to generate the stmmac dma_conf before stmmac_open. +This permits a function to first check if it's possible to allocate a +new dma_config and then pass it directly to __stmmac_open and "open" the +interface with the new configuration. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 462 +++++++++++------- + 1 file changed, 289 insertions(+), 173 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1284,7 +1284,8 @@ static int stmmac_phy_setup(struct stmma + return 0; + } + +-static void stmmac_display_rx_rings(struct stmmac_priv *priv) ++static void stmmac_display_rx_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_cnt = priv->plat->rx_queues_to_use; + unsigned int desc_size; +@@ -1293,7 +1294,7 @@ static void stmmac_display_rx_rings(stru + + /* Display RX rings */ + for (queue = 0; queue < rx_cnt; queue++) { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + pr_info("\tRX Queue %u rings\n", queue); + +@@ -1306,12 +1307,13 @@ static void stmmac_display_rx_rings(stru + } + + /* Display RX ring */ +- stmmac_display_ring(priv, head_rx, priv->dma_conf.dma_rx_size, true, ++ stmmac_display_ring(priv, head_rx, dma_conf->dma_rx_size, true, + rx_q->dma_rx_phy, desc_size); + } + } + +-static void stmmac_display_tx_rings(struct stmmac_priv *priv) ++static void stmmac_display_tx_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 tx_cnt = priv->plat->tx_queues_to_use; + unsigned int desc_size; +@@ -1320,7 +1322,7 @@ static void stmmac_display_tx_rings(stru + + /* Display TX rings */ + for (queue = 0; queue < tx_cnt; queue++) { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + + pr_info("\tTX Queue %d rings\n", queue); + +@@ -1335,18 +1337,19 @@ static void stmmac_display_tx_rings(stru + desc_size = sizeof(struct dma_desc); + } + +- stmmac_display_ring(priv, head_tx, priv->dma_conf.dma_tx_size, false, ++ stmmac_display_ring(priv, head_tx, dma_conf->dma_tx_size, false, + tx_q->dma_tx_phy, desc_size); + } + } + +-static void stmmac_display_rings(struct stmmac_priv *priv) ++static void stmmac_display_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + /* Display RX ring */ +- stmmac_display_rx_rings(priv); ++ stmmac_display_rx_rings(priv, dma_conf); + + /* Display TX ring */ +- stmmac_display_tx_rings(priv); ++ stmmac_display_tx_rings(priv, dma_conf); + } + + static int stmmac_set_bfsize(int mtu, int bufsize) +@@ -1370,44 +1373,50 @@ static int stmmac_set_bfsize(int mtu, in + /** + * stmmac_clear_rx_descriptors - clear RX descriptors + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * Description: this function is called to clear the RX descriptors + * in case of both basic and extended descriptors are used. + */ +-static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) ++static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + + /* Clear the RX descriptors */ +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) ++ for (i = 0; i < dma_conf->dma_rx_size; i++) + if (priv->extend_desc) + stmmac_init_rx_desc(priv, &rx_q->dma_erx[i].basic, + priv->use_riwt, priv->mode, +- (i == priv->dma_conf.dma_rx_size - 1), +- priv->dma_conf.dma_buf_sz); ++ (i == dma_conf->dma_rx_size - 1), ++ dma_conf->dma_buf_sz); + else + stmmac_init_rx_desc(priv, &rx_q->dma_rx[i], + priv->use_riwt, priv->mode, +- (i == priv->dma_conf.dma_rx_size - 1), +- priv->dma_conf.dma_buf_sz); ++ (i == dma_conf->dma_rx_size - 1), ++ dma_conf->dma_buf_sz); + } + + /** + * stmmac_clear_tx_descriptors - clear tx descriptors + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index. + * Description: this function is called to clear the TX descriptors + * in case of both basic and extended descriptors are used. + */ +-static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) ++static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + int i; + + /* Clear the TX descriptors */ +- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { +- int last = (i == (priv->dma_conf.dma_tx_size - 1)); ++ for (i = 0; i < dma_conf->dma_tx_size; i++) { ++ int last = (i == (dma_conf->dma_tx_size - 1)); + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1424,10 +1433,12 @@ static void stmmac_clear_tx_descriptors( + /** + * stmmac_clear_descriptors - clear descriptors + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * Description: this function is called to clear the TX and RX descriptors + * in case of both basic and extended descriptors are used. + */ +-static void stmmac_clear_descriptors(struct stmmac_priv *priv) ++static void stmmac_clear_descriptors(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_queue_cnt = priv->plat->rx_queues_to_use; + u32 tx_queue_cnt = priv->plat->tx_queues_to_use; +@@ -1435,16 +1446,17 @@ static void stmmac_clear_descriptors(str + + /* Clear the RX descriptors */ + for (queue = 0; queue < rx_queue_cnt; queue++) +- stmmac_clear_rx_descriptors(priv, queue); ++ stmmac_clear_rx_descriptors(priv, dma_conf, queue); + + /* Clear the TX descriptors */ + for (queue = 0; queue < tx_queue_cnt; queue++) +- stmmac_clear_tx_descriptors(priv, queue); ++ stmmac_clear_tx_descriptors(priv, dma_conf, queue); + } + + /** + * stmmac_init_rx_buffers - init the RX descriptor buffer. + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @p: descriptor pointer + * @i: descriptor index + * @flags: gfp flag +@@ -1452,10 +1464,12 @@ static void stmmac_clear_descriptors(str + * Description: this function is called to allocate a receive buffer, perform + * the DMA mapping and init the descriptor. + */ +-static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, ++static int stmmac_init_rx_buffers(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ struct dma_desc *p, + int i, gfp_t flags, u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->page) { +@@ -1480,7 +1494,7 @@ static int stmmac_init_rx_buffers(struct + buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; + + stmmac_set_desc_addr(priv, p, buf->addr); +- if (priv->dma_conf.dma_buf_sz == BUF_SIZE_16KiB) ++ if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB) + stmmac_init_desc3(priv, p); + + return 0; +@@ -1489,12 +1503,13 @@ static int stmmac_init_rx_buffers(struct + /** + * stmmac_free_rx_buffer - free RX dma buffers + * @priv: private structure +- * @queue: RX queue index ++ * @rx_q: RX queue + * @i: buffer index. + */ +-static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) ++static void stmmac_free_rx_buffer(struct stmmac_priv *priv, ++ struct stmmac_rx_queue *rx_q, ++ int i) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (buf->page) +@@ -1509,12 +1524,15 @@ static void stmmac_free_rx_buffer(struct + /** + * stmmac_free_tx_buffer - free RX dma buffers + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * @i: buffer index. + */ +-static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i) ++static void stmmac_free_tx_buffer(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue, int i) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + + if (tx_q->tx_skbuff_dma[i].buf && + tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) { +@@ -1553,23 +1571,28 @@ static void stmmac_free_tx_buffer(struct + /** + * dma_free_rx_skbufs - free RX dma buffers + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + */ +-static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue) ++static void dma_free_rx_skbufs(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) +- stmmac_free_rx_buffer(priv, queue, i); ++ for (i = 0; i < dma_conf->dma_rx_size; i++) ++ stmmac_free_rx_buffer(priv, rx_q, i); + } + +-static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, u32 queue, +- gfp_t flags) ++static int stmmac_alloc_rx_buffers(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue, gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { ++ for (i = 0; i < dma_conf->dma_rx_size; i++) { + struct dma_desc *p; + int ret; + +@@ -1578,7 +1601,7 @@ static int stmmac_alloc_rx_buffers(struc + else + p = rx_q->dma_rx + i; + +- ret = stmmac_init_rx_buffers(priv, p, i, flags, ++ ret = stmmac_init_rx_buffers(priv, dma_conf, p, i, flags, + queue); + if (ret) + return ret; +@@ -1592,14 +1615,17 @@ static int stmmac_alloc_rx_buffers(struc + /** + * dma_free_rx_xskbufs - free RX dma buffers from XSK pool + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + */ +-static void dma_free_rx_xskbufs(struct stmmac_priv *priv, u32 queue) ++static void dma_free_rx_xskbufs(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { ++ for (i = 0; i < dma_conf->dma_rx_size; i++) { + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + + if (!buf->xdp) +@@ -1610,12 +1636,14 @@ static void dma_free_rx_xskbufs(struct s + } + } + +-static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, u32 queue) ++static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int i; + +- for (i = 0; i < priv->dma_conf.dma_rx_size; i++) { ++ for (i = 0; i < dma_conf->dma_rx_size; i++) { + struct stmmac_rx_buffer *buf; + dma_addr_t dma_addr; + struct dma_desc *p; +@@ -1650,22 +1678,25 @@ static struct xsk_buff_pool *stmmac_get_ + /** + * __init_dma_rx_desc_rings - init the RX descriptor ring (per queue) + * @priv: driver private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * @flags: gfp flag. + * Description: this function initializes the DMA RX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +-static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, u32 queue, gfp_t flags) ++static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue, gfp_t flags) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + int ret; + + netif_dbg(priv, probe, priv->dev, + "(%s) dma_rx_phy=0x%08x\n", __func__, + (u32)rx_q->dma_rx_phy); + +- stmmac_clear_rx_descriptors(priv, queue); ++ stmmac_clear_rx_descriptors(priv, dma_conf, queue); + + xdp_rxq_info_unreg_mem_model(&rx_q->xdp_rxq); + +@@ -1692,9 +1723,9 @@ static int __init_dma_rx_desc_rings(stru + /* RX XDP ZC buffer pool may not be populated, e.g. + * xdpsock TX-only. + */ +- stmmac_alloc_rx_buffers_zc(priv, queue); ++ stmmac_alloc_rx_buffers_zc(priv, dma_conf, queue); + } else { +- ret = stmmac_alloc_rx_buffers(priv, queue, flags); ++ ret = stmmac_alloc_rx_buffers(priv, dma_conf, queue, flags); + if (ret < 0) + return -ENOMEM; + } +@@ -1704,17 +1735,19 @@ static int __init_dma_rx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, rx_q->dma_erx, + rx_q->dma_rx_phy, +- priv->dma_conf.dma_rx_size, 1); ++ dma_conf->dma_rx_size, 1); + else + stmmac_mode_init(priv, rx_q->dma_rx, + rx_q->dma_rx_phy, +- priv->dma_conf.dma_rx_size, 0); ++ dma_conf->dma_rx_size, 0); + } + + return 0; + } + +-static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) ++static int init_dma_rx_desc_rings(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf, ++ gfp_t flags) + { + struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_count = priv->plat->rx_queues_to_use; +@@ -1726,7 +1759,7 @@ static int init_dma_rx_desc_rings(struct + "SKB addresses:\nskb\t\tskb data\tdma data\n"); + + for (queue = 0; queue < rx_count; queue++) { +- ret = __init_dma_rx_desc_rings(priv, queue, flags); ++ ret = __init_dma_rx_desc_rings(priv, dma_conf, queue, flags); + if (ret) + goto err_init_rx_buffers; + } +@@ -1735,12 +1768,12 @@ static int init_dma_rx_desc_rings(struct + + err_init_rx_buffers: + while (queue >= 0) { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + if (rx_q->xsk_pool) +- dma_free_rx_xskbufs(priv, queue); ++ dma_free_rx_xskbufs(priv, dma_conf, queue); + else +- dma_free_rx_skbufs(priv, queue); ++ dma_free_rx_skbufs(priv, dma_conf, queue); + + rx_q->buf_alloc_num = 0; + rx_q->xsk_pool = NULL; +@@ -1757,14 +1790,17 @@ err_init_rx_buffers: + /** + * __init_dma_tx_desc_rings - init the TX descriptor ring (per queue) + * @priv: driver private structure +- * @queue : TX queue index ++ * @dma_conf: structure to take the dma data ++ * @queue: TX queue index + * Description: this function initializes the DMA TX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +-static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, u32 queue) ++static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + int i; + + netif_dbg(priv, probe, priv->dev, +@@ -1776,16 +1812,16 @@ static int __init_dma_tx_desc_rings(stru + if (priv->extend_desc) + stmmac_mode_init(priv, tx_q->dma_etx, + tx_q->dma_tx_phy, +- priv->dma_conf.dma_tx_size, 1); ++ dma_conf->dma_tx_size, 1); + else if (!(tx_q->tbs & STMMAC_TBS_AVAIL)) + stmmac_mode_init(priv, tx_q->dma_tx, + tx_q->dma_tx_phy, +- priv->dma_conf.dma_tx_size, 0); ++ dma_conf->dma_tx_size, 0); + } + + tx_q->xsk_pool = stmmac_get_xsk_pool(priv, queue); + +- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) { ++ for (i = 0; i < dma_conf->dma_tx_size; i++) { + struct dma_desc *p; + + if (priv->extend_desc) +@@ -1807,7 +1843,8 @@ static int __init_dma_tx_desc_rings(stru + return 0; + } + +-static int init_dma_tx_desc_rings(struct net_device *dev) ++static int init_dma_tx_desc_rings(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf) + { + struct stmmac_priv *priv = netdev_priv(dev); + u32 tx_queue_cnt; +@@ -1816,7 +1853,7 @@ static int init_dma_tx_desc_rings(struct + tx_queue_cnt = priv->plat->tx_queues_to_use; + + for (queue = 0; queue < tx_queue_cnt; queue++) +- __init_dma_tx_desc_rings(priv, queue); ++ __init_dma_tx_desc_rings(priv, dma_conf, queue); + + return 0; + } +@@ -1824,26 +1861,29 @@ static int init_dma_tx_desc_rings(struct + /** + * init_dma_desc_rings - init the RX/TX descriptor rings + * @dev: net device structure ++ * @dma_conf: structure to take the dma data + * @flags: gfp flag. + * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +-static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) ++static int init_dma_desc_rings(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf, ++ gfp_t flags) + { + struct stmmac_priv *priv = netdev_priv(dev); + int ret; + +- ret = init_dma_rx_desc_rings(dev, flags); ++ ret = init_dma_rx_desc_rings(dev, dma_conf, flags); + if (ret) + return ret; + +- ret = init_dma_tx_desc_rings(dev); ++ ret = init_dma_tx_desc_rings(dev, dma_conf); + +- stmmac_clear_descriptors(priv); ++ stmmac_clear_descriptors(priv, dma_conf); + + if (netif_msg_hw(priv)) +- stmmac_display_rings(priv); ++ stmmac_display_rings(priv, dma_conf); + + return ret; + } +@@ -1851,17 +1891,20 @@ static int init_dma_desc_rings(struct ne + /** + * dma_free_tx_skbufs - free TX dma buffers + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index + */ +-static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) ++static void dma_free_tx_skbufs(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + int i; + + tx_q->xsk_frames_done = 0; + +- for (i = 0; i < priv->dma_conf.dma_tx_size; i++) +- stmmac_free_tx_buffer(priv, queue, i); ++ for (i = 0; i < dma_conf->dma_tx_size; i++) ++ stmmac_free_tx_buffer(priv, dma_conf, queue, i); + + if (tx_q->xsk_pool && tx_q->xsk_frames_done) { + xsk_tx_completed(tx_q->xsk_pool, tx_q->xsk_frames_done); +@@ -1880,34 +1923,37 @@ static void stmmac_free_tx_skbufs(struct + u32 queue; + + for (queue = 0; queue < tx_queue_cnt; queue++) +- dma_free_tx_skbufs(priv, queue); ++ dma_free_tx_skbufs(priv, &priv->dma_conf, queue); + } + + /** + * __free_dma_rx_desc_resources - free RX dma desc resources (per queue) + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + */ +-static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + + /* Release the DMA RX socket buffers */ + if (rx_q->xsk_pool) +- dma_free_rx_xskbufs(priv, queue); ++ dma_free_rx_xskbufs(priv, dma_conf, queue); + else +- dma_free_rx_skbufs(priv, queue); ++ dma_free_rx_skbufs(priv, dma_conf, queue); + + rx_q->buf_alloc_num = 0; + rx_q->xsk_pool = NULL; + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) +- dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * ++ dma_free_coherent(priv->device, dma_conf->dma_rx_size * + sizeof(struct dma_desc), + rx_q->dma_rx, rx_q->dma_rx_phy); + else +- dma_free_coherent(priv->device, priv->dma_conf.dma_rx_size * ++ dma_free_coherent(priv->device, dma_conf->dma_rx_size * + sizeof(struct dma_extended_desc), + rx_q->dma_erx, rx_q->dma_rx_phy); + +@@ -1919,29 +1965,33 @@ static void __free_dma_rx_desc_resources + page_pool_destroy(rx_q->page_pool); + } + +-static void free_dma_rx_desc_resources(struct stmmac_priv *priv) ++static void free_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_count = priv->plat->rx_queues_to_use; + u32 queue; + + /* Free RX queue resources */ + for (queue = 0; queue < rx_count; queue++) +- __free_dma_rx_desc_resources(priv, queue); ++ __free_dma_rx_desc_resources(priv, dma_conf, queue); + } + + /** + * __free_dma_tx_desc_resources - free TX dma desc resources (per queue) + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index + */ +-static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + size_t size; + void *addr; + + /* Release the DMA TX socket buffers */ +- dma_free_tx_skbufs(priv, queue); ++ dma_free_tx_skbufs(priv, dma_conf, queue); + + if (priv->extend_desc) { + size = sizeof(struct dma_extended_desc); +@@ -1954,7 +2004,7 @@ static void __free_dma_tx_desc_resources + addr = tx_q->dma_tx; + } + +- size *= priv->dma_conf.dma_tx_size; ++ size *= dma_conf->dma_tx_size; + + dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); + +@@ -1962,28 +2012,32 @@ static void __free_dma_tx_desc_resources + kfree(tx_q->tx_skbuff); + } + +-static void free_dma_tx_desc_resources(struct stmmac_priv *priv) ++static void free_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 tx_count = priv->plat->tx_queues_to_use; + u32 queue; + + /* Free TX queue resources */ + for (queue = 0; queue < tx_count; queue++) +- __free_dma_tx_desc_resources(priv, queue); ++ __free_dma_tx_desc_resources(priv, dma_conf, queue); + } + + /** + * __alloc_dma_rx_desc_resources - alloc RX resources (per queue). + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: RX queue index + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +-static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; ++ struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; + bool xdp_prog = stmmac_xdp_is_enabled(priv); + struct page_pool_params pp_params = { 0 }; +@@ -1995,8 +2049,8 @@ static int __alloc_dma_rx_desc_resources + rx_q->priv_data = priv; + + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; +- pp_params.pool_size = priv->dma_conf.dma_rx_size; +- num_pages = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE); ++ pp_params.pool_size = dma_conf->dma_rx_size; ++ num_pages = DIV_ROUND_UP(dma_conf->dma_buf_sz, PAGE_SIZE); + pp_params.order = ilog2(num_pages); + pp_params.nid = dev_to_node(priv->device); + pp_params.dev = priv->device; +@@ -2011,7 +2065,7 @@ static int __alloc_dma_rx_desc_resources + return ret; + } + +- rx_q->buf_pool = kcalloc(priv->dma_conf.dma_rx_size, ++ rx_q->buf_pool = kcalloc(dma_conf->dma_rx_size, + sizeof(*rx_q->buf_pool), + GFP_KERNEL); + if (!rx_q->buf_pool) +@@ -2019,7 +2073,7 @@ static int __alloc_dma_rx_desc_resources + + if (priv->extend_desc) { + rx_q->dma_erx = dma_alloc_coherent(priv->device, +- priv->dma_conf.dma_rx_size * ++ dma_conf->dma_rx_size * + sizeof(struct dma_extended_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2028,7 +2082,7 @@ static int __alloc_dma_rx_desc_resources + + } else { + rx_q->dma_rx = dma_alloc_coherent(priv->device, +- priv->dma_conf.dma_rx_size * ++ dma_conf->dma_rx_size * + sizeof(struct dma_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); +@@ -2053,7 +2107,8 @@ static int __alloc_dma_rx_desc_resources + return 0; + } + +-static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv) ++static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 rx_count = priv->plat->rx_queues_to_use; + u32 queue; +@@ -2061,7 +2116,7 @@ static int alloc_dma_rx_desc_resources(s + + /* RX queues buffers and DMA */ + for (queue = 0; queue < rx_count; queue++) { +- ret = __alloc_dma_rx_desc_resources(priv, queue); ++ ret = __alloc_dma_rx_desc_resources(priv, dma_conf, queue); + if (ret) + goto err_dma; + } +@@ -2069,7 +2124,7 @@ static int alloc_dma_rx_desc_resources(s + return 0; + + err_dma: +- free_dma_rx_desc_resources(priv); ++ free_dma_rx_desc_resources(priv, dma_conf); + + return ret; + } +@@ -2077,28 +2132,31 @@ err_dma: + /** + * __alloc_dma_tx_desc_resources - alloc TX resources (per queue). + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * @queue: TX queue index + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +-static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) ++static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf, ++ u32 queue) + { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[queue]; + size_t size; + void *addr; + + tx_q->queue_index = queue; + tx_q->priv_data = priv; + +- tx_q->tx_skbuff_dma = kcalloc(priv->dma_conf.dma_tx_size, ++ tx_q->tx_skbuff_dma = kcalloc(dma_conf->dma_tx_size, + sizeof(*tx_q->tx_skbuff_dma), + GFP_KERNEL); + if (!tx_q->tx_skbuff_dma) + return -ENOMEM; + +- tx_q->tx_skbuff = kcalloc(priv->dma_conf.dma_tx_size, ++ tx_q->tx_skbuff = kcalloc(dma_conf->dma_tx_size, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!tx_q->tx_skbuff) +@@ -2111,7 +2169,7 @@ static int __alloc_dma_tx_desc_resources + else + size = sizeof(struct dma_desc); + +- size *= priv->dma_conf.dma_tx_size; ++ size *= dma_conf->dma_tx_size; + + addr = dma_alloc_coherent(priv->device, size, + &tx_q->dma_tx_phy, GFP_KERNEL); +@@ -2128,7 +2186,8 @@ static int __alloc_dma_tx_desc_resources + return 0; + } + +-static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv) ++static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + u32 tx_count = priv->plat->tx_queues_to_use; + u32 queue; +@@ -2136,7 +2195,7 @@ static int alloc_dma_tx_desc_resources(s + + /* TX queues buffers and DMA */ + for (queue = 0; queue < tx_count; queue++) { +- ret = __alloc_dma_tx_desc_resources(priv, queue); ++ ret = __alloc_dma_tx_desc_resources(priv, dma_conf, queue); + if (ret) + goto err_dma; + } +@@ -2144,27 +2203,29 @@ static int alloc_dma_tx_desc_resources(s + return 0; + + err_dma: +- free_dma_tx_desc_resources(priv); ++ free_dma_tx_desc_resources(priv, dma_conf); + return ret; + } + + /** + * alloc_dma_desc_resources - alloc TX/RX resources. + * @priv: private structure ++ * @dma_conf: structure to take the dma data + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +-static int alloc_dma_desc_resources(struct stmmac_priv *priv) ++static int alloc_dma_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + /* RX Allocation */ +- int ret = alloc_dma_rx_desc_resources(priv); ++ int ret = alloc_dma_rx_desc_resources(priv, dma_conf); + + if (ret) + return ret; + +- ret = alloc_dma_tx_desc_resources(priv); ++ ret = alloc_dma_tx_desc_resources(priv, dma_conf); + + return ret; + } +@@ -2172,16 +2233,18 @@ static int alloc_dma_desc_resources(stru + /** + * free_dma_desc_resources - free dma desc resources + * @priv: private structure ++ * @dma_conf: structure to take the dma data + */ +-static void free_dma_desc_resources(struct stmmac_priv *priv) ++static void free_dma_desc_resources(struct stmmac_priv *priv, ++ struct stmmac_dma_conf *dma_conf) + { + /* Release the DMA TX socket buffers */ +- free_dma_tx_desc_resources(priv); ++ free_dma_tx_desc_resources(priv, dma_conf); + + /* Release the DMA RX socket buffers later + * to ensure all pending XDP_TX buffers are returned. + */ +- free_dma_rx_desc_resources(priv); ++ free_dma_rx_desc_resources(priv, dma_conf); + } + + /** +@@ -2670,8 +2733,8 @@ static void stmmac_tx_err(struct stmmac_ + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); + + stmmac_stop_tx_dma(priv, chan); +- dma_free_tx_skbufs(priv, chan); +- stmmac_clear_tx_descriptors(priv, chan); ++ dma_free_tx_skbufs(priv, &priv->dma_conf, chan); ++ stmmac_clear_tx_descriptors(priv, &priv->dma_conf, chan); + stmmac_reset_tx_queue(priv, chan); + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, chan); +@@ -3668,19 +3731,93 @@ static int stmmac_request_irq(struct net + } + + /** +- * stmmac_open - open entry point of the driver ++ * stmmac_setup_dma_desc - Generate a dma_conf and allocate DMA queue ++ * @priv: driver private structure ++ * @mtu: MTU to setup the dma queue and buf with ++ * Description: Allocate and generate a dma_conf based on the provided MTU. ++ * Allocate the Tx/Rx DMA queue and init them. ++ * Return value: ++ * the dma_conf allocated struct on success and an appropriate ERR_PTR on failure. ++ */ ++static struct stmmac_dma_conf * ++stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu) ++{ ++ struct stmmac_dma_conf *dma_conf; ++ int chan, bfsize, ret; ++ ++ dma_conf = kzalloc(sizeof(*dma_conf), GFP_KERNEL); ++ if (!dma_conf) { ++ netdev_err(priv->dev, "%s: DMA conf allocation failed\n", ++ __func__); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ bfsize = stmmac_set_16kib_bfsize(priv, mtu); ++ if (bfsize < 0) ++ bfsize = 0; ++ ++ if (bfsize < BUF_SIZE_16KiB) ++ bfsize = stmmac_set_bfsize(mtu, 0); ++ ++ dma_conf->dma_buf_sz = bfsize; ++ /* Chose the tx/rx size from the already defined one in the ++ * priv struct. (if defined) ++ */ ++ dma_conf->dma_tx_size = priv->dma_conf.dma_tx_size; ++ dma_conf->dma_rx_size = priv->dma_conf.dma_rx_size; ++ ++ if (!dma_conf->dma_tx_size) ++ dma_conf->dma_tx_size = DMA_DEFAULT_TX_SIZE; ++ if (!dma_conf->dma_rx_size) ++ dma_conf->dma_rx_size = DMA_DEFAULT_RX_SIZE; ++ ++ /* Earlier check for TBS */ ++ for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) { ++ struct stmmac_tx_queue *tx_q = &dma_conf->tx_queue[chan]; ++ int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en; ++ ++ /* Setup per-TXQ tbs flag before TX descriptor alloc */ ++ tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0; ++ } ++ ++ ret = alloc_dma_desc_resources(priv, dma_conf); ++ if (ret < 0) { ++ netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", ++ __func__); ++ goto alloc_error; ++ } ++ ++ ret = init_dma_desc_rings(priv->dev, dma_conf, GFP_KERNEL); ++ if (ret < 0) { ++ netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", ++ __func__); ++ goto init_error; ++ } ++ ++ return dma_conf; ++ ++init_error: ++ free_dma_desc_resources(priv, dma_conf); ++alloc_error: ++ kfree(dma_conf); ++ return ERR_PTR(ret); ++} ++ ++/** ++ * __stmmac_open - open entry point of the driver + * @dev : pointer to the device structure. ++ * @dma_conf : structure to take the dma data + * Description: + * This function is the open entry point of the driver. + * Return value: + * 0 on success and an appropriate (-)ve integer as defined in errno.h + * file on failure. + */ +-static int stmmac_open(struct net_device *dev) ++static int __stmmac_open(struct net_device *dev, ++ struct stmmac_dma_conf *dma_conf) + { + struct stmmac_priv *priv = netdev_priv(dev); + int mode = priv->plat->phy_interface; +- int bfsize = 0; + u32 chan; + int ret; + +@@ -3707,45 +3844,10 @@ static int stmmac_open(struct net_device + memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); + priv->xstats.threshold = tc; + +- bfsize = stmmac_set_16kib_bfsize(priv, dev->mtu); +- if (bfsize < 0) +- bfsize = 0; +- +- if (bfsize < BUF_SIZE_16KiB) +- bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_conf.dma_buf_sz); +- +- priv->dma_conf.dma_buf_sz = bfsize; +- buf_sz = bfsize; +- + priv->rx_copybreak = STMMAC_RX_COPYBREAK; + +- if (!priv->dma_conf.dma_tx_size) +- priv->dma_conf.dma_tx_size = DMA_DEFAULT_TX_SIZE; +- if (!priv->dma_conf.dma_rx_size) +- priv->dma_conf.dma_rx_size = DMA_DEFAULT_RX_SIZE; +- +- /* Earlier check for TBS */ +- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) { +- struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; +- int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en; +- +- /* Setup per-TXQ tbs flag before TX descriptor alloc */ +- tx_q->tbs |= tbs_en ? STMMAC_TBS_AVAIL : 0; +- } +- +- ret = alloc_dma_desc_resources(priv); +- if (ret < 0) { +- netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", +- __func__); +- goto dma_desc_error; +- } +- +- ret = init_dma_desc_rings(dev, GFP_KERNEL); +- if (ret < 0) { +- netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", +- __func__); +- goto init_error; +- } ++ buf_sz = dma_conf->dma_buf_sz; ++ memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); + + stmmac_reset_queues_param(priv); + +@@ -3779,14 +3881,28 @@ irq_error: + + stmmac_hw_teardown(dev); + init_error: +- free_dma_desc_resources(priv); +-dma_desc_error: ++ free_dma_desc_resources(priv, &priv->dma_conf); + phylink_disconnect_phy(priv->phylink); + init_phy_error: + pm_runtime_put(priv->device); + return ret; + } + ++static int stmmac_open(struct net_device *dev) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ struct stmmac_dma_conf *dma_conf; ++ int ret; ++ ++ dma_conf = stmmac_setup_dma_desc(priv, dev->mtu); ++ if (IS_ERR(dma_conf)) ++ return PTR_ERR(dma_conf); ++ ++ ret = __stmmac_open(dev, dma_conf); ++ kfree(dma_conf); ++ return ret; ++} ++ + static void stmmac_fpe_stop_wq(struct stmmac_priv *priv) + { + set_bit(__FPE_REMOVING, &priv->fpe_task_state); +@@ -3833,7 +3949,7 @@ static int stmmac_release(struct net_dev + stmmac_stop_all_dma(priv); + + /* Release and free the Rx/Tx resources */ +- free_dma_desc_resources(priv); ++ free_dma_desc_resources(priv, &priv->dma_conf); + + /* Disable the MAC Rx/Tx */ + stmmac_mac_set(priv, priv->ioaddr, false); +@@ -6368,7 +6484,7 @@ void stmmac_disable_rx_queue(struct stmm + spin_unlock_irqrestore(&ch->lock, flags); + + stmmac_stop_rx_dma(priv, queue); +- __free_dma_rx_desc_resources(priv, queue); ++ __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + } + + void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) +@@ -6379,21 +6495,21 @@ void stmmac_enable_rx_queue(struct stmma + u32 buf_size; + int ret; + +- ret = __alloc_dma_rx_desc_resources(priv, queue); ++ ret = __alloc_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + if (ret) { + netdev_err(priv->dev, "Failed to alloc RX desc.\n"); + return; + } + +- ret = __init_dma_rx_desc_rings(priv, queue, GFP_KERNEL); ++ ret = __init_dma_rx_desc_rings(priv, &priv->dma_conf, queue, GFP_KERNEL); + if (ret) { +- __free_dma_rx_desc_resources(priv, queue); ++ __free_dma_rx_desc_resources(priv, &priv->dma_conf, queue); + netdev_err(priv->dev, "Failed to init RX desc.\n"); + return; + } + + stmmac_reset_rx_queue(priv, queue); +- stmmac_clear_rx_descriptors(priv, queue); ++ stmmac_clear_rx_descriptors(priv, &priv->dma_conf, queue); + + stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + rx_q->dma_rx_phy, rx_q->queue_index); +@@ -6431,7 +6547,7 @@ void stmmac_disable_tx_queue(struct stmm + spin_unlock_irqrestore(&ch->lock, flags); + + stmmac_stop_tx_dma(priv, queue); +- __free_dma_tx_desc_resources(priv, queue); ++ __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + } + + void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) +@@ -6441,21 +6557,21 @@ void stmmac_enable_tx_queue(struct stmma + unsigned long flags; + int ret; + +- ret = __alloc_dma_tx_desc_resources(priv, queue); ++ ret = __alloc_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + if (ret) { + netdev_err(priv->dev, "Failed to alloc TX desc.\n"); + return; + } + +- ret = __init_dma_tx_desc_rings(priv, queue); ++ ret = __init_dma_tx_desc_rings(priv, &priv->dma_conf, queue); + if (ret) { +- __free_dma_tx_desc_resources(priv, queue); ++ __free_dma_tx_desc_resources(priv, &priv->dma_conf, queue); + netdev_err(priv->dev, "Failed to init TX desc.\n"); + return; + } + + stmmac_reset_tx_queue(priv, queue); +- stmmac_clear_tx_descriptors(priv, queue); ++ stmmac_clear_tx_descriptors(priv, &priv->dma_conf, queue); + + stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, + tx_q->dma_tx_phy, tx_q->queue_index); +@@ -6492,7 +6608,7 @@ void stmmac_xdp_release(struct net_devic + stmmac_stop_all_dma(priv); + + /* Release and free the Rx/Tx resources */ +- free_dma_desc_resources(priv); ++ free_dma_desc_resources(priv, &priv->dma_conf); + + /* Disable the MAC Rx/Tx */ + stmmac_mac_set(priv, priv->ioaddr, false); +@@ -6517,14 +6633,14 @@ int stmmac_xdp_open(struct net_device *d + u32 chan; + int ret; + +- ret = alloc_dma_desc_resources(priv); ++ ret = alloc_dma_desc_resources(priv, &priv->dma_conf); + if (ret < 0) { + netdev_err(dev, "%s: DMA descriptors allocation failed\n", + __func__); + goto dma_desc_error; + } + +- ret = init_dma_desc_rings(dev, GFP_KERNEL); ++ ret = init_dma_desc_rings(dev, &priv->dma_conf, GFP_KERNEL); + if (ret < 0) { + netdev_err(dev, "%s: DMA descriptors initialization failed\n", + __func__); +@@ -6606,7 +6722,7 @@ irq_error: + + stmmac_hw_teardown(dev); + init_error: +- free_dma_desc_resources(priv); ++ free_dma_desc_resources(priv, &priv->dma_conf); + dma_desc_error: + return ret; + } +@@ -7475,7 +7591,7 @@ int stmmac_resume(struct device *dev) + stmmac_reset_queues_param(priv); + + stmmac_free_tx_skbufs(priv); +- stmmac_clear_descriptors(priv); ++ stmmac_clear_descriptors(priv, &priv->dma_conf); + + stmmac_hw_setup(ndev, false); + stmmac_init_coalesce(priv); diff --git a/target/linux/generic/backport-5.15/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch b/target/linux/generic/backport-5.15/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch new file mode 100644 index 0000000000..c5b332878e --- /dev/null +++ b/target/linux/generic/backport-5.15/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch @@ -0,0 +1,73 @@ +From 3470079687448abac42deb62774253be1d6bdef3 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 23 Jul 2022 16:29:33 +0200 +Subject: [PATCH 5/5] net: ethernet: stmicro: stmmac: permit MTU change with + interface up + +Remove the limitation where the interface needs to be down to change +MTU by releasing and opening the stmmac driver to set the new MTU. +Also call the set_filter function to correctly init the port. +This permits to remove the EBUSY error while the ethernet port is +running permitting a correct MTU change if for example a DSA request +a MTU change for a switch CPU port. + +Signed-off-by: Christian Marangi +Signed-off-by: Jakub Kicinski +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 30 +++++++++++++++---- + 1 file changed, 24 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -5597,18 +5597,15 @@ static int stmmac_change_mtu(struct net_ + { + struct stmmac_priv *priv = netdev_priv(dev); + int txfifosz = priv->plat->tx_fifo_size; ++ struct stmmac_dma_conf *dma_conf; + const int mtu = new_mtu; ++ int ret; + + if (txfifosz == 0) + txfifosz = priv->dma_cap.tx_fifo_size; + + txfifosz /= priv->plat->tx_queues_to_use; + +- if (netif_running(dev)) { +- netdev_err(priv->dev, "must be stopped to change its MTU\n"); +- return -EBUSY; +- } +- + if (stmmac_xdp_is_enabled(priv) && new_mtu > ETH_DATA_LEN) { + netdev_dbg(priv->dev, "Jumbo frames not supported for XDP\n"); + return -EINVAL; +@@ -5620,8 +5617,29 @@ static int stmmac_change_mtu(struct net_ + if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB)) + return -EINVAL; + +- dev->mtu = mtu; ++ if (netif_running(dev)) { ++ netdev_dbg(priv->dev, "restarting interface to change its MTU\n"); ++ /* Try to allocate the new DMA conf with the new mtu */ ++ dma_conf = stmmac_setup_dma_desc(priv, mtu); ++ if (IS_ERR(dma_conf)) { ++ netdev_err(priv->dev, "failed allocating new dma conf for new MTU %d\n", ++ mtu); ++ return PTR_ERR(dma_conf); ++ } ++ ++ stmmac_release(dev); ++ ++ ret = __stmmac_open(dev, dma_conf); ++ kfree(dma_conf); ++ if (ret) { ++ netdev_err(priv->dev, "failed reopening the interface after MTU change\n"); ++ return ret; ++ } ++ ++ stmmac_set_rx_mode(dev); ++ } + ++ dev->mtu = mtu; + netdev_update_features(dev); + + return 0; diff --git a/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch index 53f6d33bd9..237933a4df 100644 --- a/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch +++ b/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch @@ -71,7 +71,7 @@ Signed-off-by: Felix Fietkau */ --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -2824,6 +2824,10 @@ static inline int pskb_trim(struct sk_bu +@@ -2851,6 +2851,10 @@ static inline int pskb_trim(struct sk_bu return (len < skb->len) ? __pskb_trim(skb, len) : 0; } @@ -82,7 +82,7 @@ Signed-off-by: Felix Fietkau /** * pskb_trim_unique - remove end from a paged unique (not cloned) buffer * @skb: buffer to alter -@@ -2974,16 +2978,6 @@ static inline struct sk_buff *dev_alloc_ +@@ -3001,16 +3005,6 @@ static inline struct sk_buff *dev_alloc_ } diff --git a/target/linux/generic/pending-5.15/020-00-mm-x86-arm64-add-arch_has_hw_pte_young.patch b/target/linux/generic/pending-5.15/020-00-mm-x86-arm64-add-arch_has_hw_pte_young.patch index 548d8e61b2..48bcaf3e3e 100644 --- a/target/linux/generic/pending-5.15/020-00-mm-x86-arm64-add-arch_has_hw_pte_young.patch +++ b/target/linux/generic/pending-5.15/020-00-mm-x86-arm64-add-arch_has_hw_pte_young.patch @@ -72,7 +72,7 @@ Change-Id: Ib49b44fb56df3333a2ff1fcc496fb1980b976e7a --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c -@@ -2184,6 +2184,16 @@ static const struct arm64_cpu_capabiliti +@@ -2187,6 +2187,16 @@ static const struct arm64_cpu_capabiliti .matches = has_hw_dbm, .cpu_enable = cpu_enable_hw_dbm, }, diff --git a/target/linux/generic/pending-5.15/020-04-mm-multigenerational-lru-mm_struct-list.patch b/target/linux/generic/pending-5.15/020-04-mm-multigenerational-lru-mm_struct-list.patch index 75fd39d99d..2592f18e06 100644 --- a/target/linux/generic/pending-5.15/020-04-mm-multigenerational-lru-mm_struct-list.patch +++ b/target/linux/generic/pending-5.15/020-04-mm-multigenerational-lru-mm_struct-list.patch @@ -313,7 +313,7 @@ Change-Id: I25d9eda8c6bdc7c3653b9f210a159d6c247c81e8 mmdrop(mm); } -@@ -2616,6 +2618,13 @@ pid_t kernel_clone(struct kernel_clone_a +@@ -2617,6 +2619,13 @@ pid_t kernel_clone(struct kernel_clone_a get_task_struct(p); } diff --git a/target/linux/generic/pending-5.15/613-netfilter_optional_tcp_window_check.patch b/target/linux/generic/pending-5.15/613-netfilter_optional_tcp_window_check.patch index fd9e63a613..93c52e8842 100644 --- a/target/linux/generic/pending-5.15/613-netfilter_optional_tcp_window_check.patch +++ b/target/linux/generic/pending-5.15/613-netfilter_optional_tcp_window_check.patch @@ -19,7 +19,7 @@ Signed-off-by: Christian 'Ansuel' Marangi /* * Get the required data from the packet. */ -@@ -1160,7 +1163,7 @@ int nf_conntrack_tcp_packet(struct nf_co +@@ -1191,7 +1194,7 @@ int nf_conntrack_tcp_packet(struct nf_co IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) timeout = timeouts[TCP_CONNTRACK_UNACK]; @@ -28,7 +28,7 @@ Signed-off-by: Christian 'Ansuel' Marangi timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) timeout = timeouts[TCP_CONNTRACK_RETRANS]; else -@@ -1476,6 +1479,9 @@ void nf_conntrack_tcp_init_net(struct ne +@@ -1507,6 +1510,9 @@ void nf_conntrack_tcp_init_net(struct ne */ tn->tcp_be_liberal = 0; diff --git a/target/linux/generic/pending-5.15/655-increase_skb_pad.patch b/target/linux/generic/pending-5.15/655-increase_skb_pad.patch index e3dd249d11..bb8b7d982d 100644 --- a/target/linux/generic/pending-5.15/655-increase_skb_pad.patch +++ b/target/linux/generic/pending-5.15/655-increase_skb_pad.patch @@ -9,7 +9,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h -@@ -2790,7 +2790,7 @@ static inline int pskb_network_may_pull( +@@ -2817,7 +2817,7 @@ static inline int pskb_network_may_pull( * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) */ #ifndef NET_SKB_PAD diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-wac510.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-wac510.dts index d7972cd53d..d6c0ff0938 100644 --- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-wac510.dts +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-wac510.dts @@ -114,7 +114,7 @@ led_power_green: led-1 { label = "green:power"; - color = ; + color = ; function = LED_FUNCTION_POWER; gpios = <&ssr 5 GPIO_ACTIVE_LOW>; }; diff --git a/target/linux/ipq40xx/image/generic.mk b/target/linux/ipq40xx/image/generic.mk index 8e15f7ea20..dfb235c8d5 100644 --- a/target/linux/ipq40xx/image/generic.mk +++ b/target/linux/ipq40xx/image/generic.mk @@ -640,6 +640,7 @@ define Device/linksys_ea6350v3 UBINIZE_OPTS := -E 5 IMAGES += factory.bin IMAGE/factory.bin := append-kernel | append-uImage-fakehdr filesystem | pad-to $$$$(KERNEL_SIZE) | append-ubi | linksys-image type=EA6350v3 + DEFAULT := n endef TARGET_DEVICES += linksys_ea6350v3 @@ -656,6 +657,7 @@ define Device/linksys_ea8300 IMAGES += factory.bin IMAGE/factory.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-ubi | linksys-image type=EA8300 DEVICE_PACKAGES := ath10k-firmware-qca9888-ct ipq-wifi-linksys_ea8300 kmod-usb-ledtrig-usbport + DEFAULT := n endef TARGET_DEVICES += linksys_ea8300 @@ -672,6 +674,7 @@ define Device/linksys_mr8300 IMAGES += factory.bin IMAGE/factory.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-ubi | linksys-image type=MR8300 DEVICE_PACKAGES := ath10k-firmware-qca9888-ct kmod-usb-ledtrig-usbport + DEFAULT := n endef TARGET_DEVICES += linksys_mr8300 diff --git a/target/linux/lantiq/xrx200/target.mk b/target/linux/lantiq/xrx200/target.mk index 8813de8d04..0f119edd6f 100644 --- a/target/linux/lantiq/xrx200/target.mk +++ b/target/linux/lantiq/xrx200/target.mk @@ -12,7 +12,7 @@ DEFAULT_PACKAGES+=kmod-leds-gpio \ kmod-ltq-atm-vr9 \ kmod-ltq-deu-vr9 \ kmod-ltq-ptm-vr9 \ - ltq-vdsl-app \ + ltq-vdsl-vr9-app \ dsl-vrx200-firmware-xdsl-a \ dsl-vrx200-firmware-xdsl-b-patch \ ppp-mod-pppoa diff --git a/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch b/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch index a11219d353..acbb190030 100644 --- a/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch +++ b/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch @@ -1,6 +1,6 @@ --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig -@@ -1794,6 +1794,14 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGL +@@ -1729,6 +1729,14 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN endchoice @@ -17,7 +17,7 @@ default "" --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c -@@ -1059,6 +1059,17 @@ int __init early_init_dt_scan_chosen(uns +@@ -1162,6 +1162,17 @@ int __init early_init_dt_scan_chosen(uns if (p != NULL && l > 0) strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE)); diff --git a/target/linux/mpc85xx/patches-5.15/100-powerpc-85xx-tl-wdr4900-v1-support.patch b/target/linux/mpc85xx/patches-5.15/100-powerpc-85xx-tl-wdr4900-v1-support.patch index 76fd6af010..e981ac85e4 100644 --- a/target/linux/mpc85xx/patches-5.15/100-powerpc-85xx-tl-wdr4900-v1-support.patch +++ b/target/linux/mpc85xx/patches-5.15/100-powerpc-85xx-tl-wdr4900-v1-support.patch @@ -17,10 +17,8 @@ Signed-off-by: Pawel Dembicki arch/powerpc/platforms/85xx/Makefile | 1 + 4 files changed, 20 insertions(+), 1 deletion(-) -Index: linux-5.15.67/arch/powerpc/boot/Makefile -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/boot/Makefile -+++ linux-5.15.67/arch/powerpc/boot/Makefile +--- a/arch/powerpc/boot/Makefile ++++ b/arch/powerpc/boot/Makefile @@ -162,6 +162,7 @@ src-plat-$(CONFIG_PPC_PSERIES) += pserie src-plat-$(CONFIG_PPC_POWERNV) += pseries-head.S src-plat-$(CONFIG_PPC_IBM_CELL_BLADE) += pseries-head.S @@ -38,10 +36,8 @@ Index: linux-5.15.67/arch/powerpc/boot/Makefile # Board ports in arch/powerpc/platform/86xx/Kconfig image-$(CONFIG_MVME7100) += dtbImage.mvme7100 -Index: linux-5.15.67/arch/powerpc/boot/wrapper -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/boot/wrapper -+++ linux-5.15.67/arch/powerpc/boot/wrapper +--- a/arch/powerpc/boot/wrapper ++++ b/arch/powerpc/boot/wrapper @@ -326,6 +326,11 @@ adder875-redboot) platformo="$object/fixed-head.o $object/redboot-8xx.o" binary=y @@ -54,10 +50,8 @@ Index: linux-5.15.67/arch/powerpc/boot/wrapper simpleboot-*) platformo="$object/fixed-head.o $object/simpleboot.o" binary=y -Index: linux-5.15.67/arch/powerpc/platforms/85xx/Kconfig -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/platforms/85xx/Kconfig -+++ linux-5.15.67/arch/powerpc/platforms/85xx/Kconfig +--- a/arch/powerpc/platforms/85xx/Kconfig ++++ b/arch/powerpc/platforms/85xx/Kconfig @@ -170,6 +170,18 @@ config STX_GP3 select CPM2 select DEFAULT_UIMAGE @@ -77,10 +71,8 @@ Index: linux-5.15.67/arch/powerpc/platforms/85xx/Kconfig config TQM8540 bool "TQ Components TQM8540" help -Index: linux-5.15.67/arch/powerpc/platforms/85xx/Makefile -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/platforms/85xx/Makefile -+++ linux-5.15.67/arch/powerpc/platforms/85xx/Makefile +--- a/arch/powerpc/platforms/85xx/Makefile ++++ b/arch/powerpc/platforms/85xx/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_TWR_P102x) += twr_p102x.o obj-$(CONFIG_CORENET_GENERIC) += corenet_generic.o obj-$(CONFIG_FB_FSL_DIU) += t1042rdb_diu.o diff --git a/target/linux/mpc85xx/patches-5.15/102-powerpc-add-cmdline-override.patch b/target/linux/mpc85xx/patches-5.15/102-powerpc-add-cmdline-override.patch index c8803d8428..0d26dc8501 100644 --- a/target/linux/mpc85xx/patches-5.15/102-powerpc-add-cmdline-override.patch +++ b/target/linux/mpc85xx/patches-5.15/102-powerpc-add-cmdline-override.patch @@ -1,7 +1,5 @@ -Index: linux-5.15.67/arch/powerpc/Kconfig -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/Kconfig -+++ linux-5.15.67/arch/powerpc/Kconfig +--- a/arch/powerpc/Kconfig ++++ b/arch/powerpc/Kconfig @@ -915,6 +915,14 @@ config CMDLINE_FORCE endchoice @@ -17,10 +15,8 @@ Index: linux-5.15.67/arch/powerpc/Kconfig config EXTRA_TARGETS string "Additional default image types" help -Index: linux-5.15.67/drivers/of/fdt.c -=================================================================== ---- linux-5.15.67.orig/drivers/of/fdt.c -+++ linux-5.15.67/drivers/of/fdt.c +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c @@ -1162,6 +1162,17 @@ int __init early_init_dt_scan_chosen(uns if (p != NULL && l > 0) strlcat(data, p, min_t(int, strlen(data) + (int)l, COMMAND_LINE_SIZE)); diff --git a/target/linux/mpc85xx/patches-5.15/103-powerpc-85xx-red-15w-rev1.patch b/target/linux/mpc85xx/patches-5.15/103-powerpc-85xx-red-15w-rev1.patch index 574d16d96c..66044878eb 100644 --- a/target/linux/mpc85xx/patches-5.15/103-powerpc-85xx-red-15w-rev1.patch +++ b/target/linux/mpc85xx/patches-5.15/103-powerpc-85xx-red-15w-rev1.patch @@ -1,7 +1,5 @@ -Index: linux-5.15.67/arch/powerpc/platforms/85xx/Kconfig -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/platforms/85xx/Kconfig -+++ linux-5.15.67/arch/powerpc/platforms/85xx/Kconfig +--- a/arch/powerpc/platforms/85xx/Kconfig ++++ b/arch/powerpc/platforms/85xx/Kconfig @@ -173,6 +173,16 @@ config XES_MPC85xx Manufacturer: Extreme Engineering Solutions, Inc. URL: @@ -19,10 +17,8 @@ Index: linux-5.15.67/arch/powerpc/platforms/85xx/Kconfig config STX_GP3 bool "Silicon Turnkey Express GP3" help -Index: linux-5.15.67/arch/powerpc/platforms/85xx/Makefile -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/platforms/85xx/Makefile -+++ linux-5.15.67/arch/powerpc/platforms/85xx/Makefile +--- a/arch/powerpc/platforms/85xx/Makefile ++++ b/arch/powerpc/platforms/85xx/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_P1023_RDB) += p1023_rdb.o obj-$(CONFIG_TWR_P102x) += twr_p102x.o obj-$(CONFIG_CORENET_GENERIC) += corenet_generic.o diff --git a/target/linux/mpc85xx/patches-5.15/110-gpio-mpc8xxx-Fix-support-for-IRQ_TYPE_LEVEL_LOW-flow.patch b/target/linux/mpc85xx/patches-5.15/110-gpio-mpc8xxx-Fix-support-for-IRQ_TYPE_LEVEL_LOW-flow.patch index 0592da1763..c31649d207 100644 --- a/target/linux/mpc85xx/patches-5.15/110-gpio-mpc8xxx-Fix-support-for-IRQ_TYPE_LEVEL_LOW-flow.patch +++ b/target/linux/mpc85xx/patches-5.15/110-gpio-mpc8xxx-Fix-support-for-IRQ_TYPE_LEVEL_LOW-flow.patch @@ -25,10 +25,8 @@ Signed-off-by: Pali Rohár drivers/gpio/gpio-mpc8xxx.c | 1 + 1 file changed, 1 insertion(+) -Index: linux-5.15.67/drivers/gpio/gpio-mpc8xxx.c -=================================================================== ---- linux-5.15.67.orig/drivers/gpio/gpio-mpc8xxx.c -+++ linux-5.15.67/drivers/gpio/gpio-mpc8xxx.c +--- a/drivers/gpio/gpio-mpc8xxx.c ++++ b/drivers/gpio/gpio-mpc8xxx.c @@ -172,6 +172,7 @@ static int mpc8xxx_irq_set_type(struct i switch (flow_type) { diff --git a/target/linux/mpc85xx/patches-5.15/900-powerpc-bootwrapper-disable-uImage-generation.patch b/target/linux/mpc85xx/patches-5.15/900-powerpc-bootwrapper-disable-uImage-generation.patch index a021db000d..c81ab7fcc2 100644 --- a/target/linux/mpc85xx/patches-5.15/900-powerpc-bootwrapper-disable-uImage-generation.patch +++ b/target/linux/mpc85xx/patches-5.15/900-powerpc-bootwrapper-disable-uImage-generation.patch @@ -14,10 +14,8 @@ Signed-off-by: David Bauer arch/powerpc/boot/Makefile | 9 --------- 1 file changed, 9 deletions(-) -Index: linux-5.15.67/arch/powerpc/boot/Makefile -=================================================================== ---- linux-5.15.67.orig/arch/powerpc/boot/Makefile -+++ linux-5.15.67/arch/powerpc/boot/Makefile +--- a/arch/powerpc/boot/Makefile ++++ b/arch/powerpc/boot/Makefile @@ -271,7 +271,6 @@ image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp image-$(CONFIG_PPC_PMAC) += zImage.pmac diff --git a/target/linux/ramips/dts/mt7621.dtsi b/target/linux/ramips/dts/mt7621.dtsi index a3735c05db..46bc82149a 100644 --- a/target/linux/ramips/dts/mt7621.dtsi +++ b/target/linux/ramips/dts/mt7621.dtsi @@ -513,7 +513,7 @@ compatible = "mediatek,eth-mac"; reg = <1>; status = "disabled"; - phy-mode = "rgmii-rxid"; + phy-mode = "rgmii"; }; mdio: mdio-bus { diff --git a/target/linux/ramips/dts/mt7621_gnubee_gb-pc1.dts b/target/linux/ramips/dts/mt7621_gnubee_gb-pc1.dts index 29f9f09ee5..809df6dde3 100644 --- a/target/linux/ramips/dts/mt7621_gnubee_gb-pc1.dts +++ b/target/linux/ramips/dts/mt7621_gnubee_gb-pc1.dts @@ -27,16 +27,6 @@ leds { compatible = "gpio-leds"; - ethblack_act { - label = "green:ethblack_act"; - gpios = <&gpio 3 GPIO_ACTIVE_LOW>; - }; - - ethblue_act { - label = "green:ethblue_act"; - gpios = <&gpio 4 GPIO_ACTIVE_LOW>; - }; - power { label = "green:power"; gpios = <&gpio 6 GPIO_ACTIVE_LOW>; diff --git a/target/linux/ramips/dts/mt7621_gnubee_gb-pc2.dts b/target/linux/ramips/dts/mt7621_gnubee_gb-pc2.dts index cd72ea1d62..6199511cff 100644 --- a/target/linux/ramips/dts/mt7621_gnubee_gb-pc2.dts +++ b/target/linux/ramips/dts/mt7621_gnubee_gb-pc2.dts @@ -27,16 +27,26 @@ leds { compatible = "gpio-leds"; - ethblack_act { - label = "green:ethblack_act"; + ethblack-green { + label = "green:ethblack"; gpios = <&gpio 3 GPIO_ACTIVE_LOW>; }; - ethblue_act { - label = "green:ethblue_act"; + ethblue-green { + label = "green:ethblue"; gpios = <&gpio 4 GPIO_ACTIVE_LOW>; }; + ethyellow-green { + label = "green:ethyellow"; + gpios = <&gpio 15 GPIO_ACTIVE_LOW>; + }; + + ethyellow-orange { + label = "orange:ethyellow"; + gpios = <&gpio 13 GPIO_ACTIVE_LOW>; + }; + power { label = "green:power"; gpios = <&gpio 6 GPIO_ACTIVE_LOW>; diff --git a/target/linux/ramips/mt7620/config-5.15 b/target/linux/ramips/mt7620/config-5.15 index 5c6464d4a2..d6c6995efe 100644 --- a/target/linux/ramips/mt7620/config-5.15 +++ b/target/linux/ramips/mt7620/config-5.15 @@ -64,6 +64,7 @@ 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_SCHED_CLOCK=y CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_GENERIC_TIME_VSYSCALL=y @@ -150,8 +151,9 @@ CONFIG_PHYLIB=y # CONFIG_PHY_MT7621_PCI is not set CONFIG_PHY_RALINK_USB=y CONFIG_PINCTRL=y +# CONFIG_PINCTRL_AW9523 is not set CONFIG_PINCTRL_MT7620=y -# CONFIG_PINCTRL_RALINK is not set +CONFIG_PINCTRL_RALINK=y # CONFIG_PINCTRL_SINGLE is not set CONFIG_PTP_1588_CLOCK_OPTIONAL=y CONFIG_RALINK=y 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 0ac67e9670..5ffa4ecb3a 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 @@ -67,10 +67,8 @@ dlink,dir-882-a1|\ dlink,dir-882-r1) ucidef_set_led_netdev "wan" "wan" "green:net" "wan" ;; -gnubee,gb-pc1|\ gnubee,gb-pc2) - ucidef_set_led_netdev "ethblack_act" "ethblack act" "green:ethblack_act" "ethblack" "tx rx" - ucidef_set_led_netdev "ethblue_act" "ethblue act" "green:ethblue_act" "ethblue" "tx rx" + ucidef_set_led_netdev "ethyellow" "ethyellow" "orange:ethyellow" "ethyellow" "link tx rx" ;; linksys,e5600) ucidef_set_led_netdev "wan" "wan link" "blue:wan" "wan" "link" diff --git a/target/linux/ramips/mt7621/config-5.15 b/target/linux/ramips/mt7621/config-5.15 index 53127a5760..79f0a9bc15 100644 --- a/target/linux/ramips/mt7621/config-5.15 +++ b/target/linux/ramips/mt7621/config-5.15 @@ -11,7 +11,6 @@ CONFIG_BOARD_SCACHE=y CONFIG_BOUNCE=y CONFIG_CEVT_R4K=y CONFIG_CLKSRC_MIPS_GIC=y -# CONFIG_CLKSRC_PISTACHIO is not set CONFIG_CLK_MT7621=y CONFIG_CLOCKSOURCE_WATCHDOG=y CONFIG_CLONE_BACKWARDS=y @@ -19,7 +18,6 @@ CONFIG_CMDLINE="rootfstype=squashfs,jffs2" CONFIG_CMDLINE_BOOL=y # CONFIG_CMDLINE_OVERRIDE is not set CONFIG_COMMON_CLK=y -# CONFIG_COMMON_CLK_PISTACHIO is not set CONFIG_COMPAT_32BIT_TIME=y CONFIG_CPU_GENERIC_DUMP_TLB=y CONFIG_CPU_HAS_DIEI=y @@ -42,7 +40,6 @@ CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y CONFIG_CPU_SUPPORTS_HIGHMEM=y CONFIG_CPU_SUPPORTS_MSA=y CONFIG_CRC16=y -CONFIG_CRYPTO_BLAKE2S=y CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_HASH_INFO=y CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y @@ -106,7 +103,6 @@ CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y CONFIG_I2C_MT7621=y CONFIG_ICPLUS_PHY=y -# CONFIG_INGENIC_CGU_JZ4760 is not set CONFIG_INITRAMFS_SOURCE="" CONFIG_IRQCHIP=y CONFIG_IRQ_DOMAIN=y @@ -115,14 +111,12 @@ CONFIG_IRQ_FORCED_THREADING=y CONFIG_IRQ_MIPS_CPU=y CONFIG_IRQ_WORK=y CONFIG_KMAP_LOCAL=y -CONFIG_LED_TRIGGER_PHY=y CONFIG_LEDS_UBNT_LEDBAR=y +CONFIG_LED_TRIGGER_PHY=y CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_LZO_COMPRESS=y CONFIG_LZO_DECOMPRESS=y -# CONFIG_MACH_NINTENDO64 is not set -# CONFIG_MACH_REALTEK_RTL is not set CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y @@ -219,16 +213,12 @@ CONFIG_PERF_USE_VMALLOC=y CONFIG_PGTABLE_LEVELS=2 CONFIG_PHYLIB=y CONFIG_PHYLINK=y -# CONFIG_PHY_INGENIC_USB is not set CONFIG_PHY_MT7621_PCI=y -# CONFIG_PHY_PISTACHIO_USB is not set # CONFIG_PHY_RALINK_USB is not set CONFIG_PINCTRL=y CONFIG_PINCTRL_AW9523=y CONFIG_PINCTRL_MT7621=y -# CONFIG_PINCTRL_PISTACHIO is not set CONFIG_PINCTRL_RALINK=y -CONFIG_PINCTRL_RT2880=y # CONFIG_PINCTRL_SINGLE is not set CONFIG_PINCTRL_SX150X=y CONFIG_POWER_RESET=y diff --git a/target/linux/ramips/mt76x8/config-5.15 b/target/linux/ramips/mt76x8/config-5.15 index b16a9a5cde..555b57fe60 100644 --- a/target/linux/ramips/mt76x8/config-5.15 +++ b/target/linux/ramips/mt76x8/config-5.15 @@ -144,6 +144,7 @@ CONFIG_PHYLIB=y # CONFIG_PHY_MT7621_PCI is not set CONFIG_PHY_RALINK_USB=y CONFIG_PINCTRL=y +# CONFIG_PINCTRL_AW9523 is not set CONFIG_PINCTRL_MT7620=y CONFIG_PINCTRL_RALINK=y # CONFIG_PINCTRL_SINGLE is not set diff --git a/target/linux/ramips/rt288x/config-5.15 b/target/linux/ramips/rt288x/config-5.15 index 07e839999e..1cf653163b 100644 --- a/target/linux/ramips/rt288x/config-5.15 +++ b/target/linux/ramips/rt288x/config-5.15 @@ -133,6 +133,7 @@ CONFIG_PHYLIB=y # CONFIG_PHY_MT7621_PCI is not set # CONFIG_PHY_RALINK_USB is not set CONFIG_PINCTRL=y +# CONFIG_PINCTRL_AW9523 is not set CONFIG_PINCTRL_RALINK=y CONFIG_PINCTRL_RT288X=y # CONFIG_PINCTRL_SINGLE is not set diff --git a/target/linux/ramips/rt305x/config-5.15 b/target/linux/ramips/rt305x/config-5.15 index d4833b61b0..69835c8b54 100644 --- a/target/linux/ramips/rt305x/config-5.15 +++ b/target/linux/ramips/rt305x/config-5.15 @@ -133,6 +133,7 @@ CONFIG_PHYLIB=y # CONFIG_PHY_MT7621_PCI is not set CONFIG_PHY_RALINK_USB=y CONFIG_PINCTRL=y +# CONFIG_PINCTRL_AW9523 is not set CONFIG_PINCTRL_RALINK=y CONFIG_PINCTRL_RT305X=y # CONFIG_PINCTRL_SINGLE is not set diff --git a/target/linux/ramips/rt3883/config-5.15 b/target/linux/ramips/rt3883/config-5.15 index 8637885254..4dd839be72 100644 --- a/target/linux/ramips/rt3883/config-5.15 +++ b/target/linux/ramips/rt3883/config-5.15 @@ -133,6 +133,7 @@ CONFIG_PHYLIB=y # CONFIG_PHY_MT7621_PCI is not set CONFIG_PHY_RALINK_USB=y CONFIG_PINCTRL=y +# CONFIG_PINCTRL_AW9523 is not set CONFIG_PINCTRL_RALINK=y CONFIG_PINCTRL_RT3883=y # CONFIG_PINCTRL_SINGLE is not set diff --git a/target/linux/realtek/dts-5.10/rtl8380_d-link_dgs-1210-10mp-f.dts b/target/linux/realtek/dts-5.10/rtl8380_d-link_dgs-1210-10mp-f.dts index 3e8e97986c..e7db688ad5 100644 --- a/target/linux/realtek/dts-5.10/rtl8380_d-link_dgs-1210-10mp-f.dts +++ b/target/linux/realtek/dts-5.10/rtl8380_d-link_dgs-1210-10mp-f.dts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT -#include "rtl838x_d-link_dgs-1210_common.dtsi" +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" #include "rtl83xx_d-link_dgs-1210_gpio.dtsi" / { diff --git a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-10p.dts b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-10p.dts index e57197d55c..7ab37aaa9f 100644 --- a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-10p.dts +++ b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-10p.dts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT -#include "rtl838x_d-link_dgs-1210_common.dtsi" +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" / { compatible = "d-link,dgs-1210-10p", "realtek,rtl838x-soc"; diff --git a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-16.dts b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-16.dts index 28c64c2ab4..35f41b9e4d 100644 --- a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-16.dts +++ b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-16.dts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT -#include "rtl838x_d-link_dgs-1210_common.dtsi" +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" / { compatible = "d-link,dgs-1210-16", "realtek,rtl838x-soc"; diff --git a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-20.dts b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-20.dts index f96f08db29..dacc50676d 100644 --- a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-20.dts +++ b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-20.dts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT -#include "rtl838x_d-link_dgs-1210_common.dtsi" +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" #include "rtl83xx_d-link_dgs-1210_gpio.dtsi" / { diff --git a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-28.dts b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-28.dts index a4a450cc09..cf7b888fc9 100644 --- a/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-28.dts +++ b/target/linux/realtek/dts-5.10/rtl8382_d-link_dgs-1210-28.dts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT -#include "rtl838x_d-link_dgs-1210_common.dtsi" +#include "rtl838x.dtsi" +#include "rtl83xx_d-link_dgs-1210_common.dtsi" #include "rtl83xx_d-link_dgs-1210_gpio.dtsi" / { diff --git a/target/linux/realtek/dts-5.10/rtl8393_d-link_dgs-1210-52.dts b/target/linux/realtek/dts-5.10/rtl8393_d-link_dgs-1210-52.dts new file mode 100644 index 0000000000..5b876e7c43 --- /dev/null +++ b/target/linux/realtek/dts-5.10/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.10/rtl839x.dtsi b/target/linux/realtek/dts-5.10/rtl839x.dtsi index 2091fe0ba3..78e2454b7e 100644 --- a/target/linux/realtek/dts-5.10/rtl839x.dtsi +++ b/target/linux/realtek/dts-5.10/rtl839x.dtsi @@ -29,6 +29,13 @@ 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>; \ diff --git a/target/linux/realtek/dts-5.10/rtl839x_d-link_dgs-1210_gpio.dtsi b/target/linux/realtek/dts-5.10/rtl839x_d-link_dgs-1210_gpio.dtsi new file mode 100644 index 0000000000..260ab67ef3 --- /dev/null +++ b/target/linux/realtek/dts-5.10/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.10/rtl838x_d-link_dgs-1210_common.dtsi b/target/linux/realtek/dts-5.10/rtl83xx_d-link_dgs-1210_common.dtsi similarity index 98% rename from target/linux/realtek/dts-5.10/rtl838x_d-link_dgs-1210_common.dtsi rename to target/linux/realtek/dts-5.10/rtl83xx_d-link_dgs-1210_common.dtsi index c1deff4bba..1e3cafa938 100644 --- a/target/linux/realtek/dts-5.10/rtl838x_d-link_dgs-1210_common.dtsi +++ b/target/linux/realtek/dts-5.10/rtl83xx_d-link_dgs-1210_common.dtsi @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later OR MIT -#include "rtl838x.dtsi" - #include #include diff --git a/target/linux/realtek/files-5.10/arch/mips/rtl838x/prom.c b/target/linux/realtek/files-5.10/arch/mips/rtl838x/prom.c index ad8c2d75f2..ae768f9338 100644 --- a/target/linux/realtek/files-5.10/arch/mips/rtl838x/prom.c +++ b/target/linux/realtek/files-5.10/arch/mips/rtl838x/prom.c @@ -29,6 +29,28 @@ 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 void rtl_init_secondary(void) +{ +/* + * MIPS timer interrupt might fire like crazy if not used or initialized + * properly. Silence it by setting the maximum possible interval. + */ + write_c0_compare(0); +/* + * 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 + const char *get_system_type(void) { return soc_info.name; @@ -193,15 +215,19 @@ void __init prom_init(void) prom_init_cmdline(); -#ifdef CONFIG_MIPS_CPS - if (!register_cps_smp_ops()) { + if (!register_cps_smp_ops()) return; - } -#endif + #ifdef CONFIG_MIPS_MT_SMP - if (!register_vsmp_smp_ops()) { + if (cpu_has_mipsmt) { + struct plat_smp_ops rtl_smp_ops; + + 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/image/Makefile b/target/linux/realtek/image/Makefile index 6165d99bfc..6c7be4aa90 100644 --- a/target/linux/realtek/image/Makefile +++ b/target/linux/realtek/image/Makefile @@ -90,27 +90,6 @@ define Device/Default check-size | append-metadata endef -define Device/hpe_1920 - DEVICE_VENDOR := HPE - IMAGE_SIZE := 29632k - BLOCKSIZE := 64k - H3C_PRODUCT_ID := 0x3c010501 - KERNEL := kernel-bin | append-dtb | relocate-kernel | 7z | h3c-image | h3c-vfs - KERNEL_INITRAMFS := kernel-bin | append-dtb | relocate-kernel | 7z | h3c-image - IMAGE/sysupgrade.bin := append-kernel | pad-to $$$$(BLOCKSIZE) | append-rootfs | \ - pad-rootfs | check-size | append-metadata -endef - -# "NGE" refers to the uImage magic -define Device/netgear_nge - KERNEL := kernel-bin | append-dtb | lzma | uImage lzma - KERNEL_INITRAMFS := kernel-bin | append-dtb | lzma | uImage lzma - SOC := rtl8380 - IMAGE_SIZE := 14848k - UIMAGE_MAGIC := 0x4e474520 - DEVICE_VENDOR := NETGEAR -endef - include $(SUBTARGET).mk $(eval $(call BuildImage)) diff --git a/target/linux/realtek/image/common.mk b/target/linux/realtek/image/common.mk new file mode 100644 index 0000000000..9d84ec08f5 --- /dev/null +++ b/target/linux/realtek/image/common.mk @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only + +define Device/d-link_dgs-1210 + IMAGE_SIZE := 13824k + DEVICE_VENDOR := D-Link + DLINK_KERNEL_PART_SIZE := 1572864 + KERNEL := kernel-bin | append-dtb | gzip | uImage gzip | dlink-cameo + CAMEO_KERNEL_PART := 2 + CAMEO_ROOTFS_PART := 3 + CAMEO_CUSTOMER_SIGNATURE := 2 + CAMEO_BOARD_VERSION := 32 + IMAGES += factory_image1.bin + IMAGE/factory_image1.bin := append-kernel | pad-to 64k | \ + append-rootfs | pad-rootfs | pad-to 16 | check-size | \ + dlink-version | dlink-headers +endef + +define Device/hpe_1920 + DEVICE_VENDOR := HPE + IMAGE_SIZE := 29632k + BLOCKSIZE := 64k + H3C_PRODUCT_ID := 0x3c010501 + KERNEL := kernel-bin | append-dtb | relocate-kernel | 7z | h3c-image | h3c-vfs + KERNEL_INITRAMFS := kernel-bin | append-dtb | relocate-kernel | 7z | h3c-image + IMAGE/sysupgrade.bin := append-kernel | pad-to $$$$(BLOCKSIZE) | append-rootfs | \ + pad-rootfs | check-size | append-metadata +endef diff --git a/target/linux/realtek/image/rtl838x.mk b/target/linux/realtek/image/rtl838x.mk index 36b19b3cbb..c33ac7c5c4 100644 --- a/target/linux/realtek/image/rtl838x.mk +++ b/target/linux/realtek/image/rtl838x.mk @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +include ./common.mk define Device/allnet_all-sg8208m SOC := rtl8382 @@ -11,22 +12,6 @@ define Device/allnet_all-sg8208m endef TARGET_DEVICES += allnet_all-sg8208m -define Device/d-link_dgs-1210 - SOC := rtl8382 - IMAGE_SIZE := 13824k - DEVICE_VENDOR := D-Link - DLINK_KERNEL_PART_SIZE := 1572864 - KERNEL := kernel-bin | append-dtb | gzip | uImage gzip | dlink-cameo - CAMEO_KERNEL_PART := 2 - CAMEO_ROOTFS_PART := 3 - CAMEO_CUSTOMER_SIGNATURE := 2 - CAMEO_BOARD_VERSION := 32 - IMAGES += factory_image1.bin - IMAGE/factory_image1.bin := append-kernel | pad-to 64k | \ - append-rootfs | pad-rootfs | pad-to 16 | check-size | \ - dlink-version | dlink-headers -endef - define Device/d-link_dgs-1210-10mp-f $(Device/d-link_dgs-1210) SOC := rtl8380 @@ -38,6 +23,7 @@ TARGET_DEVICES += d-link_dgs-1210-10mp-f define Device/d-link_dgs-1210-10p $(Device/d-link_dgs-1210) + SOC := rtl8382 DEVICE_MODEL := DGS-1210-10P DEVICE_PACKAGES += lua-rs232 endef @@ -45,18 +31,21 @@ TARGET_DEVICES += d-link_dgs-1210-10p define Device/d-link_dgs-1210-16 $(Device/d-link_dgs-1210) + SOC := rtl8382 DEVICE_MODEL := DGS-1210-16 endef TARGET_DEVICES += d-link_dgs-1210-16 define Device/d-link_dgs-1210-20 $(Device/d-link_dgs-1210) + SOC := rtl8382 DEVICE_MODEL := DGS-1210-20 endef TARGET_DEVICES += d-link_dgs-1210-20 define Device/d-link_dgs-1210-28 $(Device/d-link_dgs-1210) + SOC := rtl8382 DEVICE_MODEL := DGS-1210-28 endef TARGET_DEVICES += d-link_dgs-1210-28 @@ -116,6 +105,16 @@ define Device/iodata_bsh-g24mb endef TARGET_DEVICES += iodata_bsh-g24mb +# "NGE" refers to the uImage magic +define Device/netgear_nge + KERNEL := kernel-bin | append-dtb | lzma | uImage lzma + KERNEL_INITRAMFS := kernel-bin | append-dtb | lzma | uImage lzma + SOC := rtl8380 + IMAGE_SIZE := 14848k + UIMAGE_MAGIC := 0x4e474520 + DEVICE_VENDOR := NETGEAR +endef + define Device/netgear_gs108t-v3 $(Device/netgear_nge) DEVICE_MODEL := GS108T diff --git a/target/linux/realtek/image/rtl839x.mk b/target/linux/realtek/image/rtl839x.mk index b47c409244..74ae95ccd7 100644 --- a/target/linux/realtek/image/rtl839x.mk +++ b/target/linux/realtek/image/rtl839x.mk @@ -1,5 +1,14 @@ # SPDX-License-Identifier: GPL-2.0-only +include ./common.mk + +define Device/d-link_dgs-1210-52 + $(Device/d-link_dgs-1210) + SOC := rtl8393 + DEVICE_MODEL := DGS-1210-52 +endef +TARGET_DEVICES += d-link_dgs-1210-52 + define Device/panasonic_m48eg-pn28480k SOC := rtl8393 IMAGE_SIZE := 16384k diff --git a/target/linux/realtek/patches-5.10/309-cevt-rtl9300-support.patch b/target/linux/realtek/patches-5.10/309-cevt-rtl9300-support.patch index 6a0038d86b..2b9102633e 100644 --- a/target/linux/realtek/patches-5.10/309-cevt-rtl9300-support.patch +++ b/target/linux/realtek/patches-5.10/309-cevt-rtl9300-support.patch @@ -31,24 +31,3 @@ #ifdef CONFIG_CEVT_R4K return r4k_clockevent_init(); #else ---- a/arch/mips/kernel/smp-mt.c -+++ b/arch/mips/kernel/smp-mt.c -@@ -108,12 +108,18 @@ static void __init smvp_tc_init(unsigned - static void vsmp_init_secondary(void) - { - /* This is Malta specific: IPI,performance and timer interrupts */ -+ -+ /* RTL9300 Clear internal timer interrupt */ -+ write_c0_compare(0); -+ - if (mips_gic_present()) - change_c0_status(ST0_IM, STATUSF_IP2 | STATUSF_IP3 | - STATUSF_IP4 | STATUSF_IP5 | - STATUSF_IP6 | STATUSF_IP7); - else - change_c0_status(ST0_IM, STATUSF_IP0 | STATUSF_IP1 | -+ STATUSF_IP2 | STATUSF_IP3 | -+ STATUSF_IP4 | STATUSF_IP5 | - STATUSF_IP6 | STATUSF_IP7); - } - diff --git a/target/linux/realtek/patches-5.10/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch b/target/linux/realtek/patches-5.10/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch new file mode 100644 index 0000000000..0f4ab38d4d --- /dev/null +++ b/target/linux/realtek/patches-5.10/319-irqchip-irq-realtek-rtl-fix-VPE-affinity.patch @@ -0,0 +1,145 @@ +--- 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 __iomem *irr0, int idx, u32 value) + 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(struct irq_data *i) + 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_irq_affinity(struct irq_data *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, unsigned int irq, irq_hw_number_t hw) + + 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(struct device_node *node, struct device_no + 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/x86/Makefile b/target/linux/x86/Makefile index 38ed576fc0..5631be78ee 100644 --- a/target/linux/x86/Makefile +++ b/target/linux/x86/Makefile @@ -23,7 +23,7 @@ DEFAULT_PACKAGES += alsa-utils partx-utils mkf2fs fdisk e2fsprogs kmod-button-ho kmod-ac97 kmod-sound-hda-core kmod-sound-hda-codec-realtek kmod-sound-hda-codec-via \ kmod-sound-via82xx kmod-sound-hda-intel kmod-sound-hda-codec-hdmi kmod-sound-i8x0 \ kmod-usb-audio kmod-usb-net kmod-usb-net-asix kmod-usb-net-asix-ax88179 \ - kmod-usb-net-rtl8150 kmod-usb-net-rtl8152-vendor automount + kmod-usb-net-rtl8150 kmod-usb-net-rtl8152-vendor grub2-bios-setup automount $(eval $(call BuildTarget)) diff --git a/tools/cmake/Makefile b/tools/cmake/Makefile index 9650755ba0..a0695c7d62 100644 --- a/tools/cmake/Makefile +++ b/tools/cmake/Makefile @@ -8,12 +8,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=cmake PKG_VERSION:=3.24.1 +PKG_VERSION_MAJOR:=$(word 1,$(subst ., ,$(PKG_VERSION))).$(word 2,$(subst ., ,$(PKG_VERSION))) PKG_RELEASE:=1 PKG_CPE_ID:=cpe:/a:kitware:cmake PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/Kitware/CMake/releases/download/v$(PKG_VERSION)/ \ - https://cmake.org/files/v3.24/ + https://cmake.org/files/v$(PKG_VERSION_MAJOR)/ PKG_HASH:=4931e277a4db1a805f13baa7013a7757a0cbfe5b7932882925c7061d9d1fa82b HOST_BUILD_PARALLEL:=1