diff --git a/package/firmware/linux-firmware/aeonsemi.mk b/package/firmware/linux-firmware/aeonsemi.mk new file mode 100644 index 00000000000000..d2b4e50c5fdb15 --- /dev/null +++ b/package/firmware/linux-firmware/aeonsemi.mk @@ -0,0 +1,9 @@ +Package/aeonsemi-as21xxx-firmware = $(call Package/firmware-default,Aeonsemi AS21xxx Ethernet PHY firmware,,LICENSE.aeonsemi) +define Package/aeonsemi-as21xxx-firmware/install + $(INSTALL_DIR) $(1)/lib/firmware/aeonsemi + $(CP) \ + $(PKG_BUILD_DIR)/aeonsemi/as21x1x_fw.bin \ + $(1)/lib/firmware +endef + +$(eval $(call BuildPackage,aeonsemi-as21xxx-firmware)) diff --git a/package/firmware/linux-firmware/airoha.mk b/package/firmware/linux-firmware/airoha.mk index 4d46ac54472818..a89a1966e45233 100644 --- a/package/firmware/linux-firmware/airoha.mk +++ b/package/firmware/linux-firmware/airoha.mk @@ -15,3 +15,14 @@ endif endef $(eval $(call BuildPackage,airoha-en8811h-firmware)) + +Package/airoha-en7581-npu-firmware = $(call Package/firmware-default,Airoha EN7581 NPU firmware,,LICENSE.airoha) +define Package/airoha-en7581-npu-firmware/install + $(INSTALL_DIR) $(1)/lib/firmware/airoha + $(CP) \ + $(PKG_BUILD_DIR)/airoha/en7581_npu_data.bin \ + $(PKG_BUILD_DIR)/airoha/en7581_npu_rv32.bin \ + $(1)/lib/firmware/airoha +endef + +$(eval $(call BuildPackage,airoha-en7581-npu-firmware)) diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index 9ae3958dfed882..f27624d4ccbf26 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -557,6 +557,23 @@ endef $(eval $(call KernelPackage,phy-vitesse)) +define KernelPackage/phy-aeonsemi-as21xxx + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Aeonsemi AS21xxx 10G Ethernet PHY + DEPENDS:=+aeonsemi-as21xxx-firmware +kmod-libphy + KCONFIG:=CONFIG_AS21XXX_PHY + FILES:= \ + $(LINUX_DIR)/drivers/net/phy/as21xxx.ko + AUTOLOAD:=$(call AutoLoad,18,as21xxx) +endef + +define KernelPackage/phy-aeonsemi-as21x1x/description + Kernel modules for Aeonsemi AS21x1x 10G Ethernet PHY +endef + +$(eval $(call KernelPackage,phy-aeonsemi-as21xxx)) + + define KernelPackage/phy-airoha-en8811h SUBMENU:=$(NETWORK_DEVICES_MENU) TITLE:=Airoha EN8811H 2.5G Ethernet PHY diff --git a/target/linux/airoha/dts/an7581.dtsi b/target/linux/airoha/dts/an7581.dtsi index 086cae977f74a5..6d18d199266add 100644 --- a/target/linux/airoha/dts/an7581.dtsi +++ b/target/linux/airoha/dts/an7581.dtsi @@ -17,29 +17,39 @@ #size-cells = <2>; ranges; - npu-binary@84000000 { + atf@80000000 { + no-map; + reg = <0x0 0x80000000 0x0 0x200000>; + }; + + npu_binary: npu-binary@84000000 { no-map; reg = <0x0 0x84000000 0x0 0xa00000>; }; - npu-flag@84b0000 { + qdma0_buf: qdma0-buf@87000000 { + no-map; + reg = <0x0 0x87000000 0x0 0x2000000>; + }; + + qdma1_buf: qdma1-buf@89000000 { no-map; - reg = <0x0 0x84b00000 0x0 0x100000>; + reg = <0x0 0x89000000 0x0 0x1000000>; }; - npu-pkt@85000000 { + npu_pkt: npu-pkt@8a000000 { no-map; - reg = <0x0 0x85000000 0x0 0x1a00000>; + reg = <0x0 0x8a000000 0x0 0x2c00000>; }; - npu-phyaddr@86b00000 { + npu_txpkt: npu-txpkt@8cc00000 { no-map; - reg = <0x0 0x86b00000 0x0 0x100000>; + reg = <0x0 0x8cc00000 0x0 0x4000000>; }; - npu-rxdesc@86d00000 { + npu_txbufid: npu-txbufid@90c00000 { no-map; - reg = <0x0 0x86d00000 0x0 0x100000>; + reg = <0x0 0x90c00000 0x0 0x6800>; }; }; @@ -666,6 +676,31 @@ }; }; + npu: npu@1e900000 { + compatible = "airoha,en7581-npu"; + reg = <0x0 0x1e900000 0x0 0x313000>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + memory-region = <&npu_binary>, <&npu_pkt>, <&npu_txpkt>, + <&npu_txbufid>; + memory-region-names = "binary", "pkt", "tx-pkt", + "tx-bufid"; + status = "disabled"; + }; + eth: ethernet@1fb50000 { compatible = "airoha,en7581-eth"; reg = <0 0x1fb50000 0 0x2600>, @@ -696,6 +731,11 @@ , ; + memory-region = <&qdma0_buf>, <&qdma1_buf>; + memory-region-names = "qdma0-buf", "qdma1-buf"; + + airoha,npu = <&npu>; + status = "disabled"; #address-cells = <1>; diff --git a/target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch b/target/linux/airoha/patches-6.6/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch similarity index 84% rename from target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch rename to target/linux/airoha/patches-6.6/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch index ffacbba69374fb..e168cda3249523 100644 --- a/target/linux/airoha/patches-6.6/101-02-thermal-Add-support-for-Airoha-EN7581-thermal-sensor.patch +++ b/target/linux/airoha/patches-6.6/049-01-v6.16-thermal-drivers-Add-support-for-Airoha-EN7581-therma.patch @@ -1,7 +1,7 @@ -From bc6a6a4ec6c28467683121cc165e5681b4acdf8d Mon Sep 17 00:00:00 2001 +From 42de37f40e1bc818df216dfa0918c114cfb5941d Mon Sep 17 00:00:00 2001 From: Christian Marangi -Date: Tue, 27 Aug 2024 23:04:53 +0200 -Subject: [PATCH 3/3] thermal: Add support for Airoha EN7581 thermal sensor +Date: Sun, 11 May 2025 20:49:55 +0200 +Subject: [PATCH] thermal/drivers: Add support for Airoha EN7581 thermal sensor Add support for Airoha EN7581 thermal sensor. This provide support for reading the CPU or SoC Package sensor and to setup trip points for hot @@ -12,12 +12,20 @@ The thermal regs provide a way to read the ADC value from an external register placed in the Chip SCU regs. Monitor will read this value and fire an interrupt if the trip condition configured is reached. +The Thermal Trip and Interrupt logic is conceptually similar to Mediatek +LVTS Thermal but differ in register mapping and actual function/bug +workaround. The implementation only share some register names but from +functionality observation it's very different and used only for the +basic function of periodically poll the temp and trip the interrupt. + Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20250511185003.3754495-2-ansuelsmth@gmail.com +Signed-off-by: Daniel Lezcano --- drivers/thermal/Kconfig | 9 + drivers/thermal/Makefile | 1 + - drivers/thermal/airoha_thermal.c | 482 +++++++++++++++++++++++++++++++ - 3 files changed, 492 insertions(+) + drivers/thermal/airoha_thermal.c | 489 +++++++++++++++++++++++++++++++ + 3 files changed, 499 insertions(+) create mode 100644 drivers/thermal/airoha_thermal.c --- a/drivers/thermal/Kconfig @@ -50,7 +58,7 @@ Signed-off-by: Christian Marangi obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o --- /dev/null +++ b/drivers/thermal/airoha_thermal.c -@@ -0,0 +1,482 @@ +@@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include @@ -235,15 +243,17 @@ Signed-off-by: Christian Marangi +#define EN7581_SCU_THERMAL_MUX_DIODE1 0x7 + +/* Convert temp to raw value as read from ADC ((((temp / 100) - init) * slope) / 1000) + offset */ -+#define TEMP_TO_RAW(priv, tz, temp) ((((((temp) / 100) - (priv)->init_temp) * \ -+ thermal_zone_get_slope(tz)) / 1000) + \ -+ thermal_zone_get_offset(tz)) ++#define TEMP_TO_RAW(priv, temp) ((((((temp) / 100) - (priv)->init_temp) * \ ++ (priv)->default_slope) / 1000) + \ ++ (priv)->default_offset) + +/* Convert raw to temp ((((temp - offset) * 1000) / slope + init) * 100) */ -+#define RAW_TO_TEMP(priv, tz, raw) (((((raw) - thermal_zone_get_offset(tz)) * 1000) / \ -+ thermal_zone_get_slope(tz) + \ ++#define RAW_TO_TEMP(priv, raw) (((((raw) - (priv)->default_offset) * 1000) / \ ++ (priv)->default_slope + \ + (priv)->init_temp) * 100) + ++#define AIROHA_MAX_SAMPLES 6 ++ +struct airoha_thermal_priv { + void __iomem *base; + struct regmap *chip_scu; @@ -251,6 +261,8 @@ Signed-off-by: Christian Marangi + + struct thermal_zone_device *tz; + int init_temp; ++ int default_slope; ++ int default_offset; +}; + +static int airoha_get_thermal_ADC(struct airoha_thermal_priv *priv) @@ -280,28 +292,25 @@ Signed-off-by: Christian Marangi +static int airoha_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); -+ int min, max, avg_temp, temp_adc; ++ int min_value, max_value, avg_value, value; + int i; + -+ /* Get the starting temp */ -+ temp_adc = airoha_get_thermal_ADC(priv); -+ min = temp_adc; -+ max = temp_adc; -+ avg_temp = temp_adc; -+ -+ /* Make 5 more measurement and average the temp ADC difference */ -+ for (i = 0; i < 5; i++) { -+ temp_adc = airoha_get_thermal_ADC(priv); -+ avg_temp += temp_adc; -+ if (temp_adc > max) -+ max = temp_adc; -+ if (temp_adc < min) -+ min = temp_adc; ++ avg_value = 0; ++ min_value = INT_MAX; ++ max_value = INT_MIN; ++ ++ for (i = 0; i < AIROHA_MAX_SAMPLES; i++) { ++ value = airoha_get_thermal_ADC(priv); ++ min_value = min(value, min_value); ++ max_value = max(value, max_value); ++ avg_value += value; + } -+ avg_temp = avg_temp - max - min; -+ avg_temp /= 4; + -+ *temp = RAW_TO_TEMP(priv, tz, avg_temp); ++ /* Drop min and max and average for the remaining sample */ ++ avg_value -= (min_value + max_value); ++ avg_value /= AIROHA_MAX_SAMPLES - 2; ++ ++ *temp = RAW_TO_TEMP(priv, avg_value); + return 0; +} + @@ -309,29 +318,35 @@ Signed-off-by: Christian Marangi + int high) +{ + struct airoha_thermal_priv *priv = thermal_zone_device_priv(tz); ++ bool enable_monitor = false; + + if (high != INT_MAX) { -+ /* Validate high and clamp them a sane value */ -+ if (high > RAW_TO_TEMP(priv, tz, FIELD_MAX(EN7581_DOUT_TADC_MASK))) -+ high = 110000; ++ /* Validate high and clamp it a supported value */ ++ high = clamp_t(int, high, RAW_TO_TEMP(priv, 0), ++ RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK))); + + /* We offset the high temp of 1°C to trigger correct event */ -+ writel(TEMP_TO_RAW(priv, tz, high) >> 4, ++ writel(TEMP_TO_RAW(priv, high) >> 4, + priv->base + EN7581_TEMPOFFSETH); ++ ++ enable_monitor = true; + } + + if (low != -INT_MAX) { -+ /* Validate low and clamp them to a sane value */ -+ if (low < RAW_TO_TEMP(priv, tz, 0)) -+ low = -33000; ++ /* Validate low and clamp it to a supported value */ ++ low = clamp_t(int, high, RAW_TO_TEMP(priv, 0), ++ RAW_TO_TEMP(priv, FIELD_MAX(EN7581_DOUT_TADC_MASK))); + + /* We offset the low temp of 1°C to trigger correct event */ -+ writel(TEMP_TO_RAW(priv, tz, low) >> 4, ++ writel(TEMP_TO_RAW(priv, low) >> 4, + priv->base + EN7581_TEMPOFFSETL); ++ ++ enable_monitor = true; + } + -+ /* Enable sensor 0 monitor */ -+ writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0); ++ /* Enable sensor 0 monitor after trip are set */ ++ if (enable_monitor) ++ writel(EN7581_SENSE0_EN, priv->base + EN7581_TEMPMONCTL0); + + return 0; +} @@ -345,32 +360,35 @@ Signed-off-by: Christian Marangi +{ + struct airoha_thermal_priv *priv = data; + enum thermal_notify_event event; ++ bool update = false; + u32 status; + + status = readl(priv->base + EN7581_TEMPMONINTSTS); + switch (status & (EN7581_HOFSINTSTS0 | EN7581_LOFSINTSTS0)) { + case EN7581_HOFSINTSTS0: + event = THERMAL_TRIP_VIOLATED; ++ update = true; + break; + case EN7581_LOFSINTSTS0: + event = THERMAL_EVENT_UNSPECIFIED; ++ update = true; + break; + default: -+ goto exit; ++ /* Should be impossible as we enable only these Interrupt */ ++ break; + } + -+ thermal_zone_device_update(priv->tz, event); -+ -+exit: -+ /* reset interrupt */ ++ /* Reset Interrupt */ + writel(status, priv->base + EN7581_TEMPMONINTSTS); + ++ if (update) ++ thermal_zone_device_update(priv->tz, event); ++ + return IRQ_HANDLED; +} + +static void airoha_thermal_setup_adc_val(struct device *dev, -+ struct airoha_thermal_priv *priv, -+ struct thermal_zone_params *tzp) ++ struct airoha_thermal_priv *priv) +{ + u32 efuse_calib_info, cpu_sensor; + @@ -381,19 +399,19 @@ Signed-off-by: Christian Marangi + + efuse_calib_info = readl(priv->base + EN7581_EFUSE_TEMP_OFFSET_REG); + if (efuse_calib_info) { -+ tzp->offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info); ++ priv->default_offset = FIELD_GET(EN7581_EFUSE_TEMP_OFFSET, efuse_calib_info); + /* Different slope are applied if the sensor is used for CPU or for package */ + cpu_sensor = readl(priv->base + EN7581_EFUSE_TEMP_CPU_SENSOR_REG); + if (cpu_sensor) { -+ tzp->slope = EN7581_SLOPE_X100_DIO_DEFAULT; ++ priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT; + priv->init_temp = EN7581_INIT_TEMP_FTK_X10; + } else { -+ tzp->slope = EN7581_SLOPE_X100_DIO_AVS; ++ priv->default_slope = EN7581_SLOPE_X100_DIO_AVS; + priv->init_temp = EN7581_INIT_TEMP_CPK_X10; + } + } else { -+ tzp->offset = airoha_get_thermal_ADC(priv); -+ tzp->slope = EN7581_SLOPE_X100_DIO_DEFAULT; ++ priv->default_offset = airoha_get_thermal_ADC(priv); ++ priv->default_slope = EN7581_SLOPE_X100_DIO_DEFAULT; + priv->init_temp = EN7581_INIT_TEMP_NONK_X10; + dev_info(dev, "missing thermal calibrarion EFUSE, using non calibrated value\n"); + } @@ -456,7 +474,6 @@ Signed-off-by: Christian Marangi + +static int airoha_thermal_probe(struct platform_device *pdev) +{ -+ struct thermal_zone_params tzp = { }; + struct airoha_thermal_priv *priv; + struct device_node *chip_scu_np; + struct device *dev = &pdev->dev; @@ -487,19 +504,17 @@ Signed-off-by: Christian Marangi + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + airoha_thermal_irq, IRQF_ONESHOT, -+ pdev->name, (void *)priv); ++ pdev->name, priv); + if (ret) { + dev_err(dev, "Can't get interrupt working.\n"); + return ret; + } + + airoha_thermal_setup_monitor(priv); -+ airoha_thermal_setup_adc_val(dev, priv, &tzp); ++ airoha_thermal_setup_adc_val(dev, priv); + + /* register of thermal sensor and get info from DT */ -+ priv->tz = devm_thermal_of_zone_register_with_params(dev, 0, priv, -+ &thdev_ops, -+ &tzp); ++ priv->tz = devm_thermal_of_zone_register(dev, 0, priv, &thdev_ops); + if (IS_ERR(priv->tz)) { + dev_err(dev, "register thermal zone sensor failed\n"); + return PTR_ERR(priv->tz); diff --git a/target/linux/airoha/patches-6.6/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch b/target/linux/airoha/patches-6.6/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch new file mode 100644 index 00000000000000..7b1b9478c37fbc --- /dev/null +++ b/target/linux/airoha/patches-6.6/049-02-v6.16-thermal-drivers-airoha-Fix-spelling-mistake.patch @@ -0,0 +1,44 @@ +From e23cba0ab49a9cf95e9bc3a86cfbf336b0e285f6 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Wed, 14 May 2025 23:39:12 +0200 +Subject: [PATCH] thermal/drivers/airoha: Fix spelling mistake + +Fix various spelling mistake in airoha_thermal_setup_monitor() and +define. + +Reported-by: Alok Tiwari +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20250514213919.2321490-1-ansuelsmth@gmail.com +Signed-off-by: Daniel Lezcano +--- + drivers/thermal/airoha_thermal.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/thermal/airoha_thermal.c ++++ b/drivers/thermal/airoha_thermal.c +@@ -155,7 +155,7 @@ + * Can operate in: + * - 1 sample + * - 2 sample and make average of them +- * - 4,6,10,16 sample, drop max and min and make avgerage of them ++ * - 4,6,10,16 sample, drop max and min and make average of them + */ + #define EN7581_MSRCTL_1SAMPLE 0x0 + #define EN7581_MSRCTL_AVG2SAMPLE 0x1 +@@ -365,12 +365,12 @@ static void airoha_thermal_setup_monitor + /* + * Configure ADC valid reading addr + * The AHB temp monitor system doesn't have direct access to the +- * thermal sensor. It does instead work by providing all kind of +- * address to configure how to access and setup an ADC for the ++ * thermal sensor. It does instead work by providing various ++ * addresses to configure how to access and setup an ADC for the + * sensor. EN7581 supports only one sensor hence the + * implementation is greatly simplified but the AHB supports +- * up to 4 different sensor from the same ADC that can be +- * switched by tuning the ADC mux or wiriting address. ++ * up to 4 different sensors from the same ADC that can be ++ * switched by tuning the ADC mux or writing address. + * + * We set valid instead of volt as we don't enable valid/volt + * split reading and AHB read valid addr in such case. diff --git a/target/linux/airoha/patches-6.6/051-v6.15-pinctrl-airoha-fix-wrong-PHY-LED-mapping-and-PHY2-LE.patch b/target/linux/airoha/patches-6.6/051-v6.15-pinctrl-airoha-fix-wrong-PHY-LED-mapping-and-PHY2-LE.patch new file mode 100644 index 00000000000000..386532781ffd31 --- /dev/null +++ b/target/linux/airoha/patches-6.6/051-v6.15-pinctrl-airoha-fix-wrong-PHY-LED-mapping-and-PHY2-LE.patch @@ -0,0 +1,435 @@ +From 457d9772e8a5cdae64f66b5f7d5b0247365191ec Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Tue, 1 Apr 2025 15:50:21 +0200 +Subject: [PATCH] pinctrl: airoha: fix wrong PHY LED mapping and PHY2 LED + defines + +The current PHY2 LED define are wrong and actually set BITs outside the +related mask. Fix it and set the correct value. While at it, also use +FIELD_PREP_CONST macro to make it simple to understand what values are +actually applied for the mask. + +Also fix wrong PHY LED mapping. The SoC Switch supports up to 4 port but +the register define mapping for 5 PHY port, starting from 0. The mapping +was wrongly defined starting from PHY1. Reorder the function group to +start from PHY0. PHY4 is actually never supported as we don't have a +GPIO pin to assign. + +Cc: stable@vger.kernel.org +Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") +Reviewed-by: Benjamin Larsson +Signed-off-by: Christian Marangi +Acked-by: Lorenzo Bianconi +Link: https://lore.kernel.org/20250401135026.18018-1-ansuelsmth@gmail.com +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 159 ++++++++++------------ + 1 file changed, 70 insertions(+), 89 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -106,39 +107,19 @@ + #define REG_LAN_LED1_MAPPING 0x0280 + + #define LAN4_LED_MAPPING_MASK GENMASK(18, 16) +-#define LAN4_PHY4_LED_MAP BIT(18) +-#define LAN4_PHY2_LED_MAP BIT(17) +-#define LAN4_PHY1_LED_MAP BIT(16) +-#define LAN4_PHY0_LED_MAP 0 +-#define LAN4_PHY3_LED_MAP GENMASK(17, 16) ++#define LAN4_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN4_LED_MAPPING_MASK, (_n)) + + #define LAN3_LED_MAPPING_MASK GENMASK(14, 12) +-#define LAN3_PHY4_LED_MAP BIT(14) +-#define LAN3_PHY2_LED_MAP BIT(13) +-#define LAN3_PHY1_LED_MAP BIT(12) +-#define LAN3_PHY0_LED_MAP 0 +-#define LAN3_PHY3_LED_MAP GENMASK(13, 12) ++#define LAN3_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN3_LED_MAPPING_MASK, (_n)) + + #define LAN2_LED_MAPPING_MASK GENMASK(10, 8) +-#define LAN2_PHY4_LED_MAP BIT(12) +-#define LAN2_PHY2_LED_MAP BIT(11) +-#define LAN2_PHY1_LED_MAP BIT(10) +-#define LAN2_PHY0_LED_MAP 0 +-#define LAN2_PHY3_LED_MAP GENMASK(11, 10) ++#define LAN2_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN2_LED_MAPPING_MASK, (_n)) + + #define LAN1_LED_MAPPING_MASK GENMASK(6, 4) +-#define LAN1_PHY4_LED_MAP BIT(6) +-#define LAN1_PHY2_LED_MAP BIT(5) +-#define LAN1_PHY1_LED_MAP BIT(4) +-#define LAN1_PHY0_LED_MAP 0 +-#define LAN1_PHY3_LED_MAP GENMASK(5, 4) ++#define LAN1_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN1_LED_MAPPING_MASK, (_n)) + + #define LAN0_LED_MAPPING_MASK GENMASK(2, 0) +-#define LAN0_PHY4_LED_MAP BIT(3) +-#define LAN0_PHY2_LED_MAP BIT(2) +-#define LAN0_PHY1_LED_MAP BIT(1) +-#define LAN0_PHY0_LED_MAP 0 +-#define LAN0_PHY3_LED_MAP GENMASK(2, 1) ++#define LAN0_PHY_LED_MAP(_n) FIELD_PREP_CONST(LAN0_LED_MAPPING_MASK, (_n)) + + /* CONF */ + #define REG_I2C_SDA_E2 0x001c +@@ -1470,8 +1451,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY1_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1485,8 +1466,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY1_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1500,8 +1481,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY1_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1515,8 +1496,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY1_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, +@@ -1534,8 +1515,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY2_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1549,8 +1530,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY2_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1564,8 +1545,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY2_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1579,8 +1560,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY2_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, +@@ -1598,8 +1579,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY3_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1613,8 +1594,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY3_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1628,8 +1609,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY3_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1643,8 +1624,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY3_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, +@@ -1662,8 +1643,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY4_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1677,8 +1658,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY4_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1692,8 +1673,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY4_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1707,8 +1688,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED0_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY4_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, +@@ -1726,8 +1707,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY1_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1741,8 +1722,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY1_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1756,8 +1737,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY1_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, { +@@ -1771,8 +1752,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY1_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(0) + }, + .regmap_size = 2, + }, +@@ -1790,8 +1771,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY2_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1805,8 +1786,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY2_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1820,8 +1801,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY2_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, { +@@ -1835,8 +1816,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY2_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(1) + }, + .regmap_size = 2, + }, +@@ -1854,8 +1835,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY3_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1869,8 +1850,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY3_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1884,8 +1865,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY3_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, { +@@ -1899,8 +1880,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY3_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(2) + }, + .regmap_size = 2, + }, +@@ -1918,8 +1899,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN1_LED_MAPPING_MASK, +- LAN1_PHY4_LED_MAP ++ LAN0_LED_MAPPING_MASK, ++ LAN0_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1933,8 +1914,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN2_LED_MAPPING_MASK, +- LAN2_PHY4_LED_MAP ++ LAN1_LED_MAPPING_MASK, ++ LAN1_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1948,8 +1929,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN3_LED_MAPPING_MASK, +- LAN3_PHY4_LED_MAP ++ LAN2_LED_MAPPING_MASK, ++ LAN2_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, { +@@ -1963,8 +1944,8 @@ static const struct airoha_pinctrl_func_ + .regmap[1] = { + AIROHA_FUNC_MUX, + REG_LAN_LED1_MAPPING, +- LAN4_LED_MAPPING_MASK, +- LAN4_PHY4_LED_MAP ++ LAN3_LED_MAPPING_MASK, ++ LAN3_PHY_LED_MAP(3) + }, + .regmap_size = 2, + }, diff --git a/target/linux/airoha/patches-6.6/060-v6.16-02-net-phy-mediatek-add-Airoha-PHY-ID-to-SoC-driver.patch b/target/linux/airoha/patches-6.6/060-v6.16-02-net-phy-mediatek-add-Airoha-PHY-ID-to-SoC-driver.patch new file mode 100644 index 00000000000000..68d21dd4c57c6f --- /dev/null +++ b/target/linux/airoha/patches-6.6/060-v6.16-02-net-phy-mediatek-add-Airoha-PHY-ID-to-SoC-driver.patch @@ -0,0 +1,129 @@ +From 6a325aed130bb68790e765f923e76ec5669d2da7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 10 Apr 2025 12:04:04 +0200 +Subject: [PATCH 2/2] net: phy: mediatek: add Airoha PHY ID to SoC driver + +Airoha AN7581 SoC ship with a Switch based on the MT753x Switch embedded +in other SoC like the MT7581 and the MT7988. Similar to these they +require configuring some pin to enable LED PHYs. + +Add support for the PHY ID for the Airoha embedded Switch and define a +simple probe function to toggle these pins. Also fill the LED functions +and add dedicated function to define LED polarity. + +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250410100410.348-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/Kconfig | 4 +- + drivers/net/phy/mediatek/mtk-ge-soc.c | 62 +++++++++++++++++++++++++++ + 2 files changed, 65 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/mediatek/Kconfig ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -15,7 +15,9 @@ config MEDIATEK_GE_PHY + + config MEDIATEK_GE_SOC_PHY + tristate "MediaTek SoC Ethernet PHYs" +- depends on (ARM64 && ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || COMPILE_TEST ++ depends on ARM64 || COMPILE_TEST ++ depends on ARCH_AIROHA || (ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || \ ++ COMPILE_TEST + select MTK_NET_PHYLIB + help + Supports MediaTek SoC built-in Gigabit Ethernet PHYs. +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -10,8 +10,11 @@ + + #include "mtk.h" + ++#define MTK_PHY_MAX_LEDS 2 ++ + #define MTK_GPHY_ID_MT7981 0x03a29461 + #define MTK_GPHY_ID_MT7988 0x03a29481 ++#define MTK_GPHY_ID_AN7581 0x03a294c1 + + #define MTK_EXT_PAGE_ACCESS 0x1f + #define MTK_PHY_PAGE_STANDARD 0x0000 +@@ -1405,6 +1408,53 @@ static int mt7981_phy_probe(struct phy_d + return mt798x_phy_calibration(phydev); + } + ++static int an7581_phy_probe(struct phy_device *phydev) ++{ ++ struct mtk_socphy_priv *priv; ++ struct pinctrl *pinctrl; ++ ++ /* Toggle pinctrl to enable PHY LED */ ++ pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); ++ if (IS_ERR(pinctrl)) ++ dev_err(&phydev->mdio.bus->dev, ++ "Failed to setup PHY LED pinctrl\n"); ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ phydev->priv = priv; ++ ++ return 0; ++} ++ ++static int an7581_phy_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ u32 mode; ++ u16 val; ++ ++ if (index >= MTK_PHY_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ val = MTK_PHY_LED_ON_POLARITY; ++ break; ++ case PHY_LED_ACTIVE_HIGH: ++ val = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_POLARITY, val); ++} ++ + static struct phy_driver mtk_socphy_driver[] = { + { + PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), +@@ -1440,6 +1490,17 @@ static struct phy_driver mtk_socphy_driv + .led_hw_control_set = mt798x_phy_led_hw_control_set, + .led_hw_control_get = mt798x_phy_led_hw_control_get, + }, ++ { ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_AN7581), ++ .name = "Airoha AN7581 PHY", ++ .probe = an7581_phy_probe, ++ .led_blink_set = mt798x_phy_led_blink_set, ++ .led_brightness_set = mt798x_phy_led_brightness_set, ++ .led_hw_is_supported = mt798x_phy_led_hw_is_supported, ++ .led_hw_control_set = mt798x_phy_led_hw_control_set, ++ .led_hw_control_get = mt798x_phy_led_hw_control_get, ++ .led_polarity_set = an7581_phy_led_polarity_set, ++ }, + }; + + module_phy_driver(mtk_socphy_driver); +@@ -1447,6 +1508,7 @@ module_phy_driver(mtk_socphy_driver); + static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_AN7581) }, + { } + }; + diff --git a/target/linux/airoha/patches-6.6/063-01-v6.15-net-airoha-Move-min-max-packet-len-configuration-in-.patch b/target/linux/airoha/patches-6.6/063-01-v6.15-net-airoha-Move-min-max-packet-len-configuration-in-.patch new file mode 100644 index 00000000000000..03654f4450645a --- /dev/null +++ b/target/linux/airoha/patches-6.6/063-01-v6.15-net-airoha-Move-min-max-packet-len-configuration-in-.patch @@ -0,0 +1,59 @@ +From 54d989d58d2ac87c8504c2306ba8b4957c60e8dc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:08 +0100 +Subject: [PATCH 1/6] net: airoha: Move min/max packet len configuration in + airoha_dev_open() + +In order to align max allowed packet size to the configured mtu, move +REG_GDM_LEN_CFG configuration in airoha_dev_open routine. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-1-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -138,15 +138,10 @@ static void airoha_fe_maccr_init(struct + { + int p; + +- for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { ++ for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) + airoha_fe_set(eth, REG_GDM_FWD_CFG(p), + GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM | + GDM_DROP_CRC_ERR); +- airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), +- GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, +- FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | +- FIELD_PREP(GDM_LONG_LEN_MASK, 4004)); +- } + + airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK, + FIELD_PREP(CDM1_VLAN_MASK, 0x8100)); +@@ -1521,9 +1516,9 @@ static void airoha_update_hw_stats(struc + + static int airoha_dev_open(struct net_device *dev) + { ++ int err, len = ETH_HLEN + dev->mtu + ETH_FCS_LEN; + struct airoha_gdm_port *port = netdev_priv(dev); + struct airoha_qdma *qdma = port->qdma; +- int err; + + netif_tx_start_all_queues(dev); + err = airoha_set_vip_for_gdm_port(port, true); +@@ -1537,6 +1532,11 @@ static int airoha_dev_open(struct net_de + airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), + GDM_STAG_EN_MASK); + ++ airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), ++ GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | ++ FIELD_PREP(GDM_LONG_LEN_MASK, len)); ++ + airoha_qdma_set(qdma, REG_QDMA_GLOBAL_CFG, + GLOBAL_CFG_TX_DMA_EN_MASK | + GLOBAL_CFG_RX_DMA_EN_MASK); diff --git a/target/linux/airoha/patches-6.6/063-02-v6.15-net-airoha-Enable-Rx-Scatter-Gather.patch b/target/linux/airoha/patches-6.6/063-02-v6.15-net-airoha-Enable-Rx-Scatter-Gather.patch new file mode 100644 index 00000000000000..cea179c274de82 --- /dev/null +++ b/target/linux/airoha/patches-6.6/063-02-v6.15-net-airoha-Enable-Rx-Scatter-Gather.patch @@ -0,0 +1,170 @@ +From e12182ddb6e712951d21a50e2c8ccd700e41a40c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:09 +0100 +Subject: [PATCH 2/6] net: airoha: Enable Rx Scatter-Gather + +EN7581 SoC can receive 9k frames. Enable the reception of Scatter-Gather +(SG) frames. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-2-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 68 ++++++++++++++--------- + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + drivers/net/ethernet/airoha/airoha_regs.h | 5 ++ + 3 files changed, 48 insertions(+), 26 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -615,10 +615,10 @@ static int airoha_qdma_rx_process(struct + struct airoha_qdma_desc *desc = &q->desc[q->tail]; + u32 hash, reason, msg1 = le32_to_cpu(desc->msg1); + dma_addr_t dma_addr = le32_to_cpu(desc->addr); ++ struct page *page = virt_to_head_page(e->buf); + u32 desc_ctrl = le32_to_cpu(desc->ctrl); + struct airoha_gdm_port *port; +- struct sk_buff *skb; +- int len, p; ++ int data_len, len, p; + + if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) + break; +@@ -636,30 +636,41 @@ static int airoha_qdma_rx_process(struct + dma_sync_single_for_cpu(eth->dev, dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), dir); + ++ data_len = q->skb ? q->buf_size ++ : SKB_WITH_OVERHEAD(q->buf_size); ++ if (data_len < len) ++ goto free_frag; ++ + p = airoha_qdma_get_gdm_port(eth, desc); +- if (p < 0 || !eth->ports[p]) { +- page_pool_put_full_page(q->page_pool, +- virt_to_head_page(e->buf), +- true); +- continue; +- } ++ if (p < 0 || !eth->ports[p]) ++ goto free_frag; + + port = eth->ports[p]; +- skb = napi_build_skb(e->buf, q->buf_size); +- if (!skb) { +- page_pool_put_full_page(q->page_pool, +- virt_to_head_page(e->buf), +- true); +- break; ++ if (!q->skb) { /* first buffer */ ++ q->skb = napi_build_skb(e->buf, q->buf_size); ++ if (!q->skb) ++ goto free_frag; ++ ++ __skb_put(q->skb, len); ++ skb_mark_for_recycle(q->skb); ++ q->skb->dev = port->dev; ++ q->skb->protocol = eth_type_trans(q->skb, port->dev); ++ q->skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb_record_rx_queue(q->skb, qid); ++ } else { /* scattered frame */ ++ struct skb_shared_info *shinfo = skb_shinfo(q->skb); ++ int nr_frags = shinfo->nr_frags; ++ ++ if (nr_frags >= ARRAY_SIZE(shinfo->frags)) ++ goto free_frag; ++ ++ skb_add_rx_frag(q->skb, nr_frags, page, ++ e->buf - page_address(page), len, ++ q->buf_size); + } + +- skb_reserve(skb, 2); +- __skb_put(skb, len); +- skb_mark_for_recycle(skb); +- skb->dev = port->dev; +- skb->protocol = eth_type_trans(skb, skb->dev); +- skb->ip_summed = CHECKSUM_UNNECESSARY; +- skb_record_rx_queue(skb, qid); ++ if (FIELD_GET(QDMA_DESC_MORE_MASK, desc_ctrl)) ++ continue; + + if (netdev_uses_dsa(port->dev)) { + /* PPE module requires untagged packets to work +@@ -672,22 +683,27 @@ static int airoha_qdma_rx_process(struct + + if (sptag < ARRAY_SIZE(port->dsa_meta) && + port->dsa_meta[sptag]) +- skb_dst_set_noref(skb, ++ skb_dst_set_noref(q->skb, + &port->dsa_meta[sptag]->dst); + } + + hash = FIELD_GET(AIROHA_RXD4_FOE_ENTRY, msg1); + if (hash != AIROHA_RXD4_FOE_ENTRY) +- skb_set_hash(skb, jhash_1word(hash, 0), ++ skb_set_hash(q->skb, jhash_1word(hash, 0), + PKT_HASH_TYPE_L4); + + reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); + if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) + airoha_ppe_check_skb(eth->ppe, hash); + +- napi_gro_receive(&q->napi, skb); +- + done++; ++ napi_gro_receive(&q->napi, q->skb); ++ q->skb = NULL; ++ continue; ++free_frag: ++ page_pool_put_full_page(q->page_pool, page, true); ++ dev_kfree_skb(q->skb); ++ q->skb = NULL; + } + airoha_qdma_fill_rx_queue(q); + +@@ -763,6 +779,7 @@ static int airoha_qdma_init_rx_queue(str + FIELD_PREP(RX_RING_THR_MASK, thr)); + airoha_qdma_rmw(qdma, REG_RX_DMA_IDX(qid), RX_RING_DMA_IDX_MASK, + FIELD_PREP(RX_RING_DMA_IDX_MASK, q->head)); ++ airoha_qdma_set(qdma, REG_RX_SCATTER_CFG(qid), RX_RING_SG_EN_MASK); + + airoha_qdma_fill_rx_queue(q); + +@@ -1162,7 +1179,6 @@ static int airoha_qdma_hw_init(struct ai + } + + airoha_qdma_wr(qdma, REG_QDMA_GLOBAL_CFG, +- GLOBAL_CFG_RX_2B_OFFSET_MASK | + FIELD_PREP(GLOBAL_CFG_DMA_PREFERENCE_MASK, 3) | + GLOBAL_CFG_CPU_TXR_RR_MASK | + GLOBAL_CFG_PAYLOAD_BYTE_SWAP_MASK | +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -176,6 +176,7 @@ struct airoha_queue { + + struct napi_struct napi; + struct page_pool *page_pool; ++ struct sk_buff *skb; + }; + + struct airoha_tx_irq_queue { +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -626,10 +626,15 @@ + #define REG_RX_DELAY_INT_IDX(_n) \ + (((_n) < 16) ? 0x0210 + ((_n) << 5) : 0x0e10 + (((_n) - 16) << 5)) + ++#define REG_RX_SCATTER_CFG(_n) \ ++ (((_n) < 16) ? 0x0214 + ((_n) << 5) : 0x0e14 + (((_n) - 16) << 5)) ++ + #define RX_DELAY_INT_MASK GENMASK(15, 0) + + #define RX_RING_DMA_IDX_MASK GENMASK(15, 0) + ++#define RX_RING_SG_EN_MASK BIT(0) ++ + #define REG_INGRESS_TRTCM_CFG 0x0070 + #define INGRESS_TRTCM_EN_MASK BIT(31) + #define INGRESS_TRTCM_MODE_MASK BIT(30) diff --git a/target/linux/airoha/patches-6.6/063-03-v6.15-net-airoha-Introduce-airoha_dev_change_mtu-callback.patch b/target/linux/airoha/patches-6.6/063-03-v6.15-net-airoha-Introduce-airoha_dev_change_mtu-callback.patch new file mode 100644 index 00000000000000..2a4aa0888081f1 --- /dev/null +++ b/target/linux/airoha/patches-6.6/063-03-v6.15-net-airoha-Introduce-airoha_dev_change_mtu-callback.patch @@ -0,0 +1,47 @@ +From 03b1b69f0662c46f258a45e4a7d7837351c11692 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:10 +0100 +Subject: [PATCH 3/6] net: airoha: Introduce airoha_dev_change_mtu callback + +Add airoha_dev_change_mtu callback to update the MTU of a running +device. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-3-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1706,6 +1706,20 @@ static void airoha_dev_get_stats64(struc + } while (u64_stats_fetch_retry(&port->stats.syncp, start)); + } + ++static int airoha_dev_change_mtu(struct net_device *dev, int mtu) ++{ ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->qdma->eth; ++ u32 len = ETH_HLEN + mtu + ETH_FCS_LEN; ++ ++ airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), ++ GDM_LONG_LEN_MASK, ++ FIELD_PREP(GDM_LONG_LEN_MASK, len)); ++ WRITE_ONCE(dev->mtu, mtu); ++ ++ return 0; ++} ++ + static u16 airoha_dev_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev) + { +@@ -2398,6 +2412,7 @@ static const struct net_device_ops airoh + .ndo_init = airoha_dev_init, + .ndo_open = airoha_dev_open, + .ndo_stop = airoha_dev_stop, ++ .ndo_change_mtu = airoha_dev_change_mtu, + .ndo_select_queue = airoha_dev_select_queue, + .ndo_start_xmit = airoha_dev_xmit, + .ndo_get_stats64 = airoha_dev_get_stats64, diff --git a/target/linux/airoha/patches-6.6/063-04-v6.15-net-airoha-Increase-max-mtu-to-9k.patch b/target/linux/airoha/patches-6.6/063-04-v6.15-net-airoha-Increase-max-mtu-to-9k.patch new file mode 100644 index 00000000000000..8771ff22db5ceb --- /dev/null +++ b/target/linux/airoha/patches-6.6/063-04-v6.15-net-airoha-Increase-max-mtu-to-9k.patch @@ -0,0 +1,26 @@ +From 168ef0c1dee83c401896a0bca680e9f97b1ebd64 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:21:11 +0100 +Subject: [PATCH 4/6] net: airoha: Increase max mtu to 9k + +EN7581 SoC supports 9k maximum MTU. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-rx-sg-v1-4-283ebc61120e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -20,7 +20,7 @@ + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 +-#define AIROHA_MAX_MTU 2000 ++#define AIROHA_MAX_MTU 9216 + #define AIROHA_MAX_PACKET_SIZE 2048 + #define AIROHA_NUM_QOS_CHANNELS 4 + #define AIROHA_NUM_QOS_QUEUES 8 diff --git a/target/linux/airoha/patches-6.6/063-05-v6.15-net-airoha-Fix-lan4-support-in-airoha_qdma_get_gdm_p.patch b/target/linux/airoha/patches-6.6/063-05-v6.15-net-airoha-Fix-lan4-support-in-airoha_qdma_get_gdm_p.patch new file mode 100644 index 00000000000000..1c3030afd0c4a9 --- /dev/null +++ b/target/linux/airoha/patches-6.6/063-05-v6.15-net-airoha-Fix-lan4-support-in-airoha_qdma_get_gdm_p.patch @@ -0,0 +1,29 @@ +From 35ea4f06fd33fc32f556a0c26d1d8340497fa7f8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 15:38:05 +0100 +Subject: [PATCH 5/6] net: airoha: Fix lan4 support in + airoha_qdma_get_gdm_port() + +EN7581 SoC supports lan{1,4} ports on MT7530 DSA switch. Fix lan4 +reported value in airoha_qdma_get_gdm_port routine. + +Fixes: 23020f0493270 ("net: airoha: Introduce ethernet support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-airoha-eth-fix-lan4-v1-1-832417da4bb5@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -589,7 +589,7 @@ static int airoha_qdma_get_gdm_port(stru + + sport = FIELD_GET(QDMA_ETH_RXMSG_SPORT_MASK, msg1); + switch (sport) { +- case 0x10 ... 0x13: ++ case 0x10 ... 0x14: + port = 0; + break; + case 0x2 ... 0x4: diff --git a/target/linux/airoha/patches-6.6/063-06-v6.15-net-airoha-Enable-TSO-Scatter-Gather-for-LAN-port.patch b/target/linux/airoha/patches-6.6/063-06-v6.15-net-airoha-Enable-TSO-Scatter-Gather-for-LAN-port.patch new file mode 100644 index 00000000000000..28a85e67ad06f4 --- /dev/null +++ b/target/linux/airoha/patches-6.6/063-06-v6.15-net-airoha-Enable-TSO-Scatter-Gather-for-LAN-port.patch @@ -0,0 +1,27 @@ +From a202dfe31cae2f2120297a7142385d80a5577d42 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 4 Mar 2025 16:46:40 +0100 +Subject: [PATCH 6/6] net: airoha: Enable TSO/Scatter Gather for LAN port + +Set net_device vlan_features in order to enable TSO and Scatter Gather +for DSA user ports. + +Reviewed-by: Mateusz Polchlopek +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250304-lan-enable-tso-v1-1-b398eb9976ba@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2503,6 +2503,7 @@ static int airoha_alloc_gdm_port(struct + NETIF_F_SG | NETIF_F_TSO | + NETIF_F_HW_TC; + dev->features |= dev->hw_features; ++ dev->vlan_features = dev->hw_features; + dev->dev.of_node = np; + dev->irq = qdma->irq; + SET_NETDEV_DEV(dev, eth->dev); diff --git a/target/linux/airoha/patches-6.6/064-v6.15-net-airoha-Fix-dev-dsa_ptr-check-in-airoha_get_dsa_t.patch b/target/linux/airoha/patches-6.6/064-v6.15-net-airoha-Fix-dev-dsa_ptr-check-in-airoha_get_dsa_t.patch new file mode 100644 index 00000000000000..7134de91731092 --- /dev/null +++ b/target/linux/airoha/patches-6.6/064-v6.15-net-airoha-Fix-dev-dsa_ptr-check-in-airoha_get_dsa_t.patch @@ -0,0 +1,47 @@ +From e368d2a1e8b6f0926e4e76a56b484249905192f5 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 6 Mar 2025 11:52:20 +0100 +Subject: [PATCH] net: airoha: Fix dev->dsa_ptr check in airoha_get_dsa_tag() + +Fix the following warning reported by Smatch static checker in +airoha_get_dsa_tag routine: + +drivers/net/ethernet/airoha/airoha_eth.c:1722 airoha_get_dsa_tag() +warn: 'dp' isn't an ERR_PTR + +dev->dsa_ptr can't be set to an error pointer, it can just be NULL. +Remove this check since it is already performed in netdev_uses_dsa(). + +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/netdev/Z8l3E0lGOcrel07C@lore-desk/T/#m54adc113fcdd8c5e6c5f65ffd60d8e8b1d483d90 +Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250306-airoha-flowtable-fixes-v1-1-68d3c1296cdd@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1742,18 +1742,13 @@ static u32 airoha_get_dsa_tag(struct sk_ + { + #if IS_ENABLED(CONFIG_NET_DSA) + struct ethhdr *ehdr; +- struct dsa_port *dp; + u8 xmit_tpid; + u16 tag; + + if (!netdev_uses_dsa(dev)) + return 0; + +- dp = dev->dsa_ptr; +- if (IS_ERR(dp)) +- return 0; +- +- if (dp->tag_ops->proto != DSA_TAG_PROTO_MTK) ++ if (dev->dsa_ptr->tag_ops->proto != DSA_TAG_PROTO_MTK) + return 0; + + if (skb_cow_head(skb, 0)) diff --git a/target/linux/airoha/patches-6.6/065-v6.15-net-airoha-fix-CONFIG_DEBUG_FS-check.patch b/target/linux/airoha/patches-6.6/065-v6.15-net-airoha-fix-CONFIG_DEBUG_FS-check.patch new file mode 100644 index 00000000000000..a8467408ed590c --- /dev/null +++ b/target/linux/airoha/patches-6.6/065-v6.15-net-airoha-fix-CONFIG_DEBUG_FS-check.patch @@ -0,0 +1,35 @@ +From 08d0185e36ad8bb5902a73711bf114765d282161 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Fri, 14 Mar 2025 16:49:59 +0100 +Subject: [PATCH] net: airoha: fix CONFIG_DEBUG_FS check + +The #if check causes a build failure when CONFIG_DEBUG_FS is turned +off: + +In file included from drivers/net/ethernet/airoha/airoha_eth.c:17: +drivers/net/ethernet/airoha/airoha_eth.h:543:5: error: "CONFIG_DEBUG_FS" is not defined, evaluates to 0 [-Werror=undef] + 543 | #if CONFIG_DEBUG_FS + | ^~~~~~~~~~~~~~~ + +Replace it with the correct #ifdef. + +Fixes: 3fe15c640f38 ("net: airoha: Introduce PPE debugfs support") +Signed-off-by: Arnd Bergmann +Acked-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250314155009.4114308-1-arnd@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -540,7 +540,7 @@ void airoha_ppe_deinit(struct airoha_eth + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); + +-#if CONFIG_DEBUG_FS ++#ifdef CONFIG_DEBUG_FS + int airoha_ppe_debugfs_init(struct airoha_ppe *ppe); + #else + static inline int airoha_ppe_debugfs_init(struct airoha_ppe *ppe) diff --git a/target/linux/airoha/patches-6.6/066-01-v6.15-net-airoha-Fix-qid-report-in-airoha_tc_get_htb_get_l.patch b/target/linux/airoha/patches-6.6/066-01-v6.15-net-airoha-Fix-qid-report-in-airoha_tc_get_htb_get_l.patch new file mode 100644 index 00000000000000..0a815c17b654da --- /dev/null +++ b/target/linux/airoha/patches-6.6/066-01-v6.15-net-airoha-Fix-qid-report-in-airoha_tc_get_htb_get_l.patch @@ -0,0 +1,77 @@ +From 57b290d97c6150774bf929117ca737a26d8fc33d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 31 Mar 2025 08:52:53 +0200 +Subject: [PATCH 1/2] net: airoha: Fix qid report in + airoha_tc_get_htb_get_leaf_queue() + +Fix the following kernel warning deleting HTB offloaded leafs and/or root +HTB qdisc in airoha_eth driver properly reporting qid in +airoha_tc_get_htb_get_leaf_queue routine. + +$tc qdisc replace dev eth1 root handle 10: htb offload +$tc class add dev eth1 arent 10: classid 10:4 htb rate 100mbit ceil 100mbit +$tc qdisc replace dev eth1 parent 10:4 handle 4: ets bands 8 \ + quanta 1514 3028 4542 6056 7570 9084 10598 12112 +$tc qdisc del dev eth1 root + +[ 55.827864] ------------[ cut here ]------------ +[ 55.832493] WARNING: CPU: 3 PID: 2678 at 0xffffffc0798695a4 +[ 55.956510] CPU: 3 PID: 2678 Comm: tc Tainted: G O 6.6.71 #0 +[ 55.963557] Hardware name: Airoha AN7581 Evaluation Board (DT) +[ 55.969383] pstate: 20400005 (nzCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) +[ 55.976344] pc : 0xffffffc0798695a4 +[ 55.979851] lr : 0xffffffc079869a20 +[ 55.983358] sp : ffffffc0850536a0 +[ 55.986665] x29: ffffffc0850536a0 x28: 0000000000000024 x27: 0000000000000001 +[ 55.993800] x26: 0000000000000000 x25: ffffff8008b19000 x24: ffffff800222e800 +[ 56.000935] x23: 0000000000000001 x22: 0000000000000000 x21: ffffff8008b19000 +[ 56.008071] x20: ffffff8002225800 x19: ffffff800379d000 x18: 0000000000000000 +[ 56.015206] x17: ffffffbf9ea59000 x16: ffffffc080018000 x15: 0000000000000000 +[ 56.022342] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000001 +[ 56.029478] x11: ffffffc081471008 x10: ffffffc081575a98 x9 : 0000000000000000 +[ 56.036614] x8 : ffffffc08167fd40 x7 : ffffffc08069e104 x6 : ffffff8007f86000 +[ 56.043748] x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000001 +[ 56.050884] x2 : 0000000000000000 x1 : 0000000000000250 x0 : ffffff800222c000 +[ 56.058020] Call trace: +[ 56.060459] 0xffffffc0798695a4 +[ 56.063618] 0xffffffc079869a20 +[ 56.066777] __qdisc_destroy+0x40/0xa0 +[ 56.070528] qdisc_put+0x54/0x6c +[ 56.073748] qdisc_graft+0x41c/0x648 +[ 56.077324] tc_get_qdisc+0x168/0x2f8 +[ 56.080978] rtnetlink_rcv_msg+0x230/0x330 +[ 56.085076] netlink_rcv_skb+0x5c/0x128 +[ 56.088913] rtnetlink_rcv+0x14/0x1c +[ 56.092490] netlink_unicast+0x1e0/0x2c8 +[ 56.096413] netlink_sendmsg+0x198/0x3c8 +[ 56.100337] ____sys_sendmsg+0x1c4/0x274 +[ 56.104261] ___sys_sendmsg+0x7c/0xc0 +[ 56.107924] __sys_sendmsg+0x44/0x98 +[ 56.111492] __arm64_sys_sendmsg+0x20/0x28 +[ 56.115580] invoke_syscall.constprop.0+0x58/0xfc +[ 56.120285] do_el0_svc+0x3c/0xbc +[ 56.123592] el0_svc+0x18/0x4c +[ 56.126647] el0t_64_sync_handler+0x118/0x124 +[ 56.131005] el0t_64_sync+0x150/0x154 +[ 56.134660] ---[ end trace 0000000000000000 ]--- + +Fixes: ef1ca9271313b ("net: airoha: Add sched HTB offload support") +Signed-off-by: Lorenzo Bianconi +Acked-by: Paolo Abeni +Link: https://patch.msgid.link/20250331-airoha-htb-qdisc-offload-del-fix-v1-1-4ea429c2c968@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2356,7 +2356,7 @@ static int airoha_tc_get_htb_get_leaf_qu + return -EINVAL; + } + +- opt->qid = channel; ++ opt->qid = AIROHA_NUM_TX_RING + channel; + + return 0; + } diff --git a/target/linux/airoha/patches-6.6/066-02-v6.15-net-airoha-Fix-ETS-priomap-validation.patch b/target/linux/airoha/patches-6.6/066-02-v6.15-net-airoha-Fix-ETS-priomap-validation.patch new file mode 100644 index 00000000000000..118047e43d6246 --- /dev/null +++ b/target/linux/airoha/patches-6.6/066-02-v6.15-net-airoha-Fix-ETS-priomap-validation.patch @@ -0,0 +1,58 @@ +From 367579274f60cb23c570ae5348966ab51e1509a4 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 31 Mar 2025 18:17:31 +0200 +Subject: [PATCH 2/2] net: airoha: Fix ETS priomap validation + +ETS Qdisc schedules SP bands in a priority order assigning band-0 the +highest priority (band-0 > band-1 > .. > band-n) while EN7581 arranges +SP bands in a priority order assigning band-7 the highest priority +(band-7 > band-6, .. > band-n). +Fix priomap check in airoha_qdma_set_tx_ets_sched routine in order to +align ETS Qdisc and airoha_eth driver SP priority ordering. + +Fixes: b56e4d660a96 ("net: airoha: Enforce ETS Qdisc priomap") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Reviewed-by: Davide Caratti +Link: https://patch.msgid.link/20250331-airoha-ets-validate-priomap-v1-1-60a524488672@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2029,7 +2029,7 @@ static int airoha_qdma_set_tx_ets_sched( + struct tc_ets_qopt_offload_replace_params *p = &opt->replace_params; + enum tx_sched_mode mode = TC_SCH_SP; + u16 w[AIROHA_NUM_QOS_QUEUES] = {}; +- int i, nstrict = 0, nwrr, qidx; ++ int i, nstrict = 0; + + if (p->bands > AIROHA_NUM_QOS_QUEUES) + return -EINVAL; +@@ -2047,17 +2047,17 @@ static int airoha_qdma_set_tx_ets_sched( + * lowest priorities with respect to SP ones. + * e.g: WRR0, WRR1, .., WRRm, SP0, SP1, .., SPn + */ +- nwrr = p->bands - nstrict; +- qidx = nstrict && nwrr ? nstrict : 0; +- for (i = 1; i <= p->bands; i++) { +- if (p->priomap[i % AIROHA_NUM_QOS_QUEUES] != qidx) ++ for (i = 0; i < nstrict; i++) { ++ if (p->priomap[p->bands - i - 1] != i) + return -EINVAL; +- +- qidx = i == nwrr ? 0 : qidx + 1; + } + +- for (i = 0; i < nwrr; i++) ++ for (i = 0; i < p->bands - nstrict; i++) { ++ if (p->priomap[i] != nstrict + i) ++ return -EINVAL; ++ + w[i] = p->weights[nstrict + i]; ++ } + + if (!nstrict) + mode = TC_SCH_WRR8; diff --git a/target/linux/airoha/patches-6.6/067-v6.15-net-airoha-Validate-egress-gdm-port-in-airoha_ppe_fo.patch b/target/linux/airoha/patches-6.6/067-v6.15-net-airoha-Validate-egress-gdm-port-in-airoha_ppe_fo.patch new file mode 100644 index 00000000000000..c6ddbde692ff1b --- /dev/null +++ b/target/linux/airoha/patches-6.6/067-v6.15-net-airoha-Validate-egress-gdm-port-in-airoha_ppe_fo.patch @@ -0,0 +1,100 @@ +From 09bccf56db36501ccb1935d921dc24451e9f57dd Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 1 Apr 2025 11:42:30 +0200 +Subject: [PATCH] net: airoha: Validate egress gdm port in + airoha_ppe_foe_entry_prepare() + +Dev pointer in airoha_ppe_foe_entry_prepare routine is not strictly +a device allocated by airoha_eth driver since it is an egress device +and the flowtable can contain even wlan, pppoe or vlan devices. E.g: + +flowtable ft { + hook ingress priority filter + devices = { eth1, lan1, lan2, lan3, lan4, wlan0 } + flags offload ^ + | + "not allocated by airoha_eth" -- +} + +In this case airoha_get_dsa_port() will just return the original device +pointer and we can't assume netdev priv pointer points to an +airoha_gdm_port struct. +Fix the issue validating egress gdm port in airoha_ppe_foe_entry_prepare +routine before accessing net_device priv pointer. + +Fixes: 00a7678310fe ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250401-airoha-validate-egress-gdm-port-v4-1-c7315d33ce10@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 13 +++++++++++++ + drivers/net/ethernet/airoha/airoha_eth.h | 3 +++ + drivers/net/ethernet/airoha/airoha_ppe.c | 8 ++++++-- + 3 files changed, 22 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2452,6 +2452,19 @@ static void airoha_metadata_dst_free(str + } + } + ++bool airoha_is_valid_gdm_port(struct airoha_eth *eth, ++ struct airoha_gdm_port *port) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { ++ if (eth->ports[i] == port) ++ return true; ++ } ++ ++ return false; ++} ++ + static int airoha_alloc_gdm_port(struct airoha_eth *eth, + struct device_node *np, int index) + { +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -532,6 +532,9 @@ u32 airoha_rmw(void __iomem *base, u32 o + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + ++bool airoha_is_valid_gdm_port(struct airoha_eth *eth, ++ struct airoha_gdm_port *port); ++ + void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash); + int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -197,7 +197,8 @@ static int airoha_get_dsa_port(struct ne + #endif + } + +-static int airoha_ppe_foe_entry_prepare(struct airoha_foe_entry *hwe, ++static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, ++ struct airoha_foe_entry *hwe, + struct net_device *dev, int type, + struct airoha_flow_data *data, + int l4proto) +@@ -225,6 +226,9 @@ static int airoha_ppe_foe_entry_prepare( + struct airoha_gdm_port *port = netdev_priv(dev); + u8 pse_port; + ++ if (!airoha_is_valid_gdm_port(eth, port)) ++ return -EINVAL; ++ + if (dsa_port >= 0) + pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; + else +@@ -633,7 +637,7 @@ static int airoha_ppe_flow_offload_repla + !is_valid_ether_addr(data.eth.h_dest)) + return -EINVAL; + +- err = airoha_ppe_foe_entry_prepare(&hwe, odev, offload_type, ++ err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type, + &data, l4proto); + if (err) + return err; diff --git a/target/linux/airoha/patches-6.6/068-01-v6.16-net-airoha-Add-l2_flows-rhashtable.patch b/target/linux/airoha/patches-6.6/068-01-v6.16-net-airoha-Add-l2_flows-rhashtable.patch new file mode 100644 index 00000000000000..95f83f53bdb6dc --- /dev/null +++ b/target/linux/airoha/patches-6.6/068-01-v6.16-net-airoha-Add-l2_flows-rhashtable.patch @@ -0,0 +1,207 @@ +From b4916f67902e2ae1dc8e37dfa45e8894ad2f8921 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 9 Apr 2025 11:47:14 +0200 +Subject: [PATCH 1/2] net: airoha: Add l2_flows rhashtable + +Introduce l2_flows rhashtable in airoha_ppe struct in order to +store L2 flows committed by upper layers of the kernel. This is a +preliminary patch in order to offload L2 traffic rules. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Michal Kubiak +Link: https://patch.msgid.link/20250409-airoha-flowtable-l2b-v2-1-4a1e3935ea92@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.h | 15 +++- + drivers/net/ethernet/airoha/airoha_ppe.c | 103 ++++++++++++++++++----- + 2 files changed, 98 insertions(+), 20 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -422,12 +422,23 @@ struct airoha_flow_data { + } pppoe; + }; + ++enum airoha_flow_entry_type { ++ FLOW_TYPE_L4, ++ FLOW_TYPE_L2, ++ FLOW_TYPE_L2_SUBFLOW, ++}; ++ + struct airoha_flow_table_entry { +- struct hlist_node list; ++ union { ++ struct hlist_node list; /* PPE L3 flow entry */ ++ struct rhash_head l2_node; /* L2 flow entry */ ++ }; + + struct airoha_foe_entry data; + u32 hash; + ++ enum airoha_flow_entry_type type; ++ + struct rhash_head node; + unsigned long cookie; + }; +@@ -480,6 +491,8 @@ struct airoha_ppe { + void *foe; + dma_addr_t foe_dma; + ++ struct rhashtable l2_flows; ++ + struct hlist_head *foe_flow; + u16 foe_check_time[PPE_NUM_ENTRIES]; + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -24,6 +24,13 @@ static const struct rhashtable_params ai + .automatic_shrinking = true, + }; + ++static const struct rhashtable_params airoha_l2_flow_table_params = { ++ .head_offset = offsetof(struct airoha_flow_table_entry, l2_node), ++ .key_offset = offsetof(struct airoha_flow_table_entry, data.bridge), ++ .key_len = 2 * ETH_ALEN, ++ .automatic_shrinking = true, ++}; ++ + static bool airoha_ppe2_is_enabled(struct airoha_eth *eth) + { + return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK; +@@ -476,6 +483,43 @@ static int airoha_ppe_foe_commit_entry(s + return 0; + } + ++static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ lockdep_assert_held(&ppe_lock); ++ ++ hlist_del_init(&e->list); ++ if (e->hash != 0xffff) { ++ e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; ++ e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, ++ AIROHA_FOE_STATE_INVALID); ++ airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); ++ e->hash = 0xffff; ++ } ++} ++ ++static void airoha_ppe_foe_remove_l2_flow(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ lockdep_assert_held(&ppe_lock); ++ ++ rhashtable_remove_fast(&ppe->l2_flows, &e->l2_node, ++ airoha_l2_flow_table_params); ++} ++ ++static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ spin_lock_bh(&ppe_lock); ++ ++ if (e->type == FLOW_TYPE_L2) ++ airoha_ppe_foe_remove_l2_flow(ppe, e); ++ else ++ airoha_ppe_foe_remove_flow(ppe, e); ++ ++ spin_unlock_bh(&ppe_lock); ++} ++ + static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, u32 hash) + { + struct airoha_flow_table_entry *e; +@@ -505,11 +549,37 @@ unlock: + spin_unlock_bh(&ppe_lock); + } + ++static int ++airoha_ppe_foe_l2_flow_commit_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ struct airoha_flow_table_entry *prev; ++ ++ e->type = FLOW_TYPE_L2; ++ prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &e->l2_node, ++ airoha_l2_flow_table_params); ++ if (!prev) ++ return 0; ++ ++ if (IS_ERR(prev)) ++ return PTR_ERR(prev); ++ ++ return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node, ++ &e->l2_node, ++ airoha_l2_flow_table_params); ++} ++ + static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe, + struct airoha_flow_table_entry *e) + { +- u32 hash = airoha_ppe_foe_get_entry_hash(&e->data); ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, e->data.ib1); ++ u32 hash; + ++ if (type == PPE_PKT_TYPE_BRIDGE) ++ return airoha_ppe_foe_l2_flow_commit_entry(ppe, e); ++ ++ hash = airoha_ppe_foe_get_entry_hash(&e->data); ++ e->type = FLOW_TYPE_L4; + e->hash = 0xffff; + + spin_lock_bh(&ppe_lock); +@@ -519,23 +589,6 @@ static int airoha_ppe_foe_flow_commit_en + return 0; + } + +-static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, +- struct airoha_flow_table_entry *e) +-{ +- spin_lock_bh(&ppe_lock); +- +- hlist_del_init(&e->list); +- if (e->hash != 0xffff) { +- e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; +- e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, +- AIROHA_FOE_STATE_INVALID); +- airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); +- e->hash = 0xffff; +- } +- +- spin_unlock_bh(&ppe_lock); +-} +- + static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, + struct flow_cls_offload *f) + { +@@ -890,9 +943,20 @@ int airoha_ppe_init(struct airoha_eth *e + if (err) + return err; + ++ err = rhashtable_init(&ppe->l2_flows, &airoha_l2_flow_table_params); ++ if (err) ++ goto error_flow_table_destroy; ++ + err = airoha_ppe_debugfs_init(ppe); + if (err) +- rhashtable_destroy(ð->flow_table); ++ goto error_l2_flow_table_destroy; ++ ++ return 0; ++ ++error_l2_flow_table_destroy: ++ rhashtable_destroy(&ppe->l2_flows); ++error_flow_table_destroy: ++ rhashtable_destroy(ð->flow_table); + + return err; + } +@@ -909,6 +973,7 @@ void airoha_ppe_deinit(struct airoha_eth + } + rcu_read_unlock(); + ++ rhashtable_destroy(ð->ppe->l2_flows); + rhashtable_destroy(ð->flow_table); + debugfs_remove(eth->ppe->debugfs_dir); + } diff --git a/target/linux/airoha/patches-6.6/068-02-v6.16-net-airoha-Add-L2-hw-acceleration-support.patch b/target/linux/airoha/patches-6.6/068-02-v6.16-net-airoha-Add-L2-hw-acceleration-support.patch new file mode 100644 index 00000000000000..2375962338b90a --- /dev/null +++ b/target/linux/airoha/patches-6.6/068-02-v6.16-net-airoha-Add-L2-hw-acceleration-support.patch @@ -0,0 +1,253 @@ +From cd53f622611f9a6dd83b858c85448dd3568b67ec Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 9 Apr 2025 11:47:15 +0200 +Subject: [PATCH 2/2] net: airoha: Add L2 hw acceleration support + +Similar to mtk driver, introduce the capability to offload L2 traffic +defining flower rules in the PSE/PPE engine available on EN7581 SoC. +Since the hw always reports L2/L3/L4 flower rules, link all L2 rules +sharing the same L2 info (with different L3/L4 info) in the L2 subflows +list of a given L2 PPE entry. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Michal Kubiak +Link: https://patch.msgid.link/20250409-airoha-flowtable-l2b-v2-2-4a1e3935ea92@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 +- + drivers/net/ethernet/airoha/airoha_eth.h | 9 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 121 ++++++++++++++++++++--- + 3 files changed, 115 insertions(+), 17 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -694,7 +694,7 @@ static int airoha_qdma_rx_process(struct + + reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); + if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) +- airoha_ppe_check_skb(eth->ppe, hash); ++ airoha_ppe_check_skb(eth->ppe, q->skb, hash); + + done++; + napi_gro_receive(&q->napi, q->skb); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -431,10 +431,14 @@ enum airoha_flow_entry_type { + struct airoha_flow_table_entry { + union { + struct hlist_node list; /* PPE L3 flow entry */ +- struct rhash_head l2_node; /* L2 flow entry */ ++ struct { ++ struct rhash_head l2_node; /* L2 flow entry */ ++ struct hlist_head l2_flows; /* PPE L2 subflows list */ ++ }; + }; + + struct airoha_foe_entry data; ++ struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */ + u32 hash; + + enum airoha_flow_entry_type type; +@@ -548,7 +552,8 @@ u32 airoha_rmw(void __iomem *base, u32 o + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash); ++void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, ++ u16 hash); + int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, + void *cb_priv); + int airoha_ppe_init(struct airoha_eth *eth); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -204,6 +204,15 @@ static int airoha_get_dsa_port(struct ne + #endif + } + ++static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br, ++ struct ethhdr *eh) ++{ ++ br->dest_mac_hi = get_unaligned_be32(eh->h_dest); ++ br->dest_mac_lo = get_unaligned_be16(eh->h_dest + 4); ++ br->src_mac_hi = get_unaligned_be16(eh->h_source); ++ br->src_mac_lo = get_unaligned_be32(eh->h_source + 2); ++} ++ + static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, + struct airoha_foe_entry *hwe, + struct net_device *dev, int type, +@@ -254,13 +263,7 @@ static int airoha_ppe_foe_entry_prepare( + + qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f); + if (type == PPE_PKT_TYPE_BRIDGE) { +- hwe->bridge.dest_mac_hi = get_unaligned_be32(data->eth.h_dest); +- hwe->bridge.dest_mac_lo = +- get_unaligned_be16(data->eth.h_dest + 4); +- hwe->bridge.src_mac_hi = +- get_unaligned_be16(data->eth.h_source); +- hwe->bridge.src_mac_lo = +- get_unaligned_be32(data->eth.h_source + 2); ++ airoha_ppe_foe_set_bridge_addrs(&hwe->bridge, &data->eth); + hwe->bridge.data = qdata; + hwe->bridge.ib2 = val; + l2 = &hwe->bridge.l2.common; +@@ -385,6 +388,19 @@ static u32 airoha_ppe_foe_get_entry_hash + hv3 = hwe->ipv6.src_ip[1] ^ hwe->ipv6.dest_ip[1]; + hv3 ^= hwe->ipv6.src_ip[0]; + break; ++ case PPE_PKT_TYPE_BRIDGE: { ++ struct airoha_foe_mac_info *l2 = &hwe->bridge.l2; ++ ++ hv1 = l2->common.src_mac_hi & 0xffff; ++ hv1 = hv1 << 16 | l2->src_mac_lo; ++ ++ hv2 = l2->common.dest_mac_lo; ++ hv2 = hv2 << 16; ++ hv2 = hv2 | ((l2->common.src_mac_hi & 0xffff0000) >> 16); ++ ++ hv3 = l2->common.dest_mac_hi; ++ break; ++ } + case PPE_PKT_TYPE_IPV4_DSLITE: + case PPE_PKT_TYPE_IPV6_6RD: + default: +@@ -496,15 +512,24 @@ static void airoha_ppe_foe_remove_flow(s + airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); + e->hash = 0xffff; + } ++ if (e->type == FLOW_TYPE_L2_SUBFLOW) { ++ hlist_del_init(&e->l2_subflow_node); ++ kfree(e); ++ } + } + + static void airoha_ppe_foe_remove_l2_flow(struct airoha_ppe *ppe, + struct airoha_flow_table_entry *e) + { ++ struct hlist_head *head = &e->l2_flows; ++ struct hlist_node *n; ++ + lockdep_assert_held(&ppe_lock); + + rhashtable_remove_fast(&ppe->l2_flows, &e->l2_node, + airoha_l2_flow_table_params); ++ hlist_for_each_entry_safe(e, n, head, l2_subflow_node) ++ airoha_ppe_foe_remove_flow(ppe, e); + } + + static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, +@@ -520,10 +545,56 @@ static void airoha_ppe_foe_flow_remove_e + spin_unlock_bh(&ppe_lock); + } + +-static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, u32 hash) ++static int ++airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e, ++ u32 hash) ++{ ++ u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; ++ struct airoha_foe_entry *hwe_p, hwe; ++ struct airoha_flow_table_entry *f; ++ struct airoha_foe_mac_info *l2; ++ int type; ++ ++ hwe_p = airoha_ppe_foe_get_entry(ppe, hash); ++ if (!hwe_p) ++ return -EINVAL; ++ ++ f = kzalloc(sizeof(*f), GFP_ATOMIC); ++ if (!f) ++ return -ENOMEM; ++ ++ hlist_add_head(&f->l2_subflow_node, &e->l2_flows); ++ f->type = FLOW_TYPE_L2_SUBFLOW; ++ f->hash = hash; ++ ++ memcpy(&hwe, hwe_p, sizeof(*hwe_p)); ++ hwe.ib1 = (hwe.ib1 & mask) | (e->data.ib1 & ~mask); ++ l2 = &hwe.bridge.l2; ++ memcpy(l2, &e->data.bridge.l2, sizeof(*l2)); ++ ++ type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe.ib1); ++ if (type == PPE_PKT_TYPE_IPV4_HNAPT) ++ memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, ++ sizeof(hwe.ipv4.new_tuple)); ++ else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T && ++ l2->common.etype == ETH_P_IP) ++ l2->common.etype = ETH_P_IPV6; ++ ++ hwe.bridge.ib2 = e->data.bridge.ib2; ++ airoha_ppe_foe_commit_entry(ppe, &hwe, hash); ++ ++ return 0; ++} ++ ++static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, ++ struct sk_buff *skb, ++ u32 hash) + { + struct airoha_flow_table_entry *e; ++ struct airoha_foe_bridge br = {}; + struct airoha_foe_entry *hwe; ++ bool commit_done = false; + struct hlist_node *n; + u32 index, state; + +@@ -539,12 +610,33 @@ static void airoha_ppe_foe_insert_entry( + + index = airoha_ppe_foe_get_entry_hash(hwe); + hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) { +- if (airoha_ppe_foe_compare_entry(e, hwe)) { +- airoha_ppe_foe_commit_entry(ppe, &e->data, hash); +- e->hash = hash; +- break; ++ if (e->type == FLOW_TYPE_L2_SUBFLOW) { ++ state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); ++ if (state != AIROHA_FOE_STATE_BIND) { ++ e->hash = 0xffff; ++ airoha_ppe_foe_remove_flow(ppe, e); ++ } ++ continue; ++ } ++ ++ if (commit_done || !airoha_ppe_foe_compare_entry(e, hwe)) { ++ e->hash = 0xffff; ++ continue; + } ++ ++ airoha_ppe_foe_commit_entry(ppe, &e->data, hash); ++ commit_done = true; ++ e->hash = hash; + } ++ ++ if (commit_done) ++ goto unlock; ++ ++ airoha_ppe_foe_set_bridge_addrs(&br, eth_hdr(skb)); ++ e = rhashtable_lookup_fast(&ppe->l2_flows, &br, ++ airoha_l2_flow_table_params); ++ if (e) ++ airoha_ppe_foe_commit_subflow_entry(ppe, e, hash); + unlock: + spin_unlock_bh(&ppe_lock); + } +@@ -899,7 +991,8 @@ int airoha_ppe_setup_tc_block_cb(enum tc + return err; + } + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash) ++void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, ++ u16 hash) + { + u16 now, diff; + +@@ -912,7 +1005,7 @@ void airoha_ppe_check_skb(struct airoha_ + return; + + ppe->foe_check_time[hash] = now; +- airoha_ppe_foe_insert_entry(ppe, hash); ++ airoha_ppe_foe_insert_entry(ppe, skb, hash); + } + + int airoha_ppe_init(struct airoha_eth *eth) diff --git a/target/linux/airoha/patches-6.6/069-v6.16-net-airoha-Add-matchall-filter-offload-support.patch b/target/linux/airoha/patches-6.6/069-v6.16-net-airoha-Add-matchall-filter-offload-support.patch new file mode 100644 index 00000000000000..ba2a2bf2d5f02a --- /dev/null +++ b/target/linux/airoha/patches-6.6/069-v6.16-net-airoha-Add-matchall-filter-offload-support.patch @@ -0,0 +1,405 @@ +From df8398fb7bb7a0e509200af56b79343aa133b7d6 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 15 Apr 2025 09:14:34 +0200 +Subject: [PATCH] net: airoha: Add matchall filter offload support + +Introduce tc matchall filter offload support in airoha_eth driver. +Matchall hw filter is used to implement hw rate policing via tc action +police: + +$tc qdisc add dev eth0 handle ffff: ingress +$tc filter add dev eth0 parent ffff: matchall action police \ + rate 100mbit burst 1000k drop + +The current implementation supports just drop/accept as exceed/notexceed +actions. Moreover, rate and burst are the only supported configuration +parameters. + +Reviewed-by: Davide Caratti +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250415-airoha-hw-rx-ratelimit-v4-1-03458784fbc3@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 273 +++++++++++++++++++++- + drivers/net/ethernet/airoha/airoha_eth.h | 8 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 9 +- + drivers/net/ethernet/airoha/airoha_regs.h | 7 + + 4 files changed, 286 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -527,6 +527,25 @@ static int airoha_fe_init(struct airoha_ + /* disable IFC by default */ + airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK); + ++ airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), ++ FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM1) | ++ FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM1)); ++ airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(1), ++ FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM2) | ++ FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM2)); ++ + /* enable 1:N vlan action, init vlan table */ + airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK); + +@@ -1632,7 +1651,6 @@ static void airhoha_set_gdm2_loopback(st + + if (port->id == 3) { + /* FIXME: handle XSI_PCE1_PORT */ +- airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0), 0x5500); + airoha_fe_rmw(eth, REG_FE_WAN_PORT, + WAN1_EN_MASK | WAN1_MASK | WAN0_MASK, + FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT)); +@@ -2107,6 +2125,125 @@ static int airoha_tc_setup_qdisc_ets(str + } + } + ++static int airoha_qdma_get_rl_param(struct airoha_qdma *qdma, int queue_id, ++ u32 addr, enum trtcm_param_type param, ++ u32 *val_low, u32 *val_high) ++{ ++ u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); ++ u32 val, config = FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | ++ FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ if (read_poll_timeout(airoha_qdma_rr, val, ++ val & RATE_LIMIT_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, qdma, ++ REG_TRTCM_CFG_PARAM(addr))) ++ return -ETIMEDOUT; ++ ++ *val_low = airoha_qdma_rr(qdma, REG_TRTCM_DATA_LOW(addr)); ++ if (val_high) ++ *val_high = airoha_qdma_rr(qdma, REG_TRTCM_DATA_HIGH(addr)); ++ ++ return 0; ++} ++ ++static int airoha_qdma_set_rl_param(struct airoha_qdma *qdma, int queue_id, ++ u32 addr, enum trtcm_param_type param, ++ u32 val) ++{ ++ u32 idx = QDMA_METER_IDX(queue_id), group = QDMA_METER_GROUP(queue_id); ++ u32 config = RATE_LIMIT_PARAM_RW_MASK | ++ FIELD_PREP(RATE_LIMIT_PARAM_TYPE_MASK, param) | ++ FIELD_PREP(RATE_LIMIT_METER_GROUP_MASK, group) | ++ FIELD_PREP(RATE_LIMIT_PARAM_INDEX_MASK, idx); ++ ++ airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val); ++ airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config); ++ ++ return read_poll_timeout(airoha_qdma_rr, val, ++ val & RATE_LIMIT_PARAM_RW_DONE_MASK, ++ USEC_PER_MSEC, 10 * USEC_PER_MSEC, true, ++ qdma, REG_TRTCM_CFG_PARAM(addr)); ++} ++ ++static int airoha_qdma_set_rl_config(struct airoha_qdma *qdma, int queue_id, ++ u32 addr, bool enable, u32 enable_mask) ++{ ++ u32 val; ++ int err; ++ ++ err = airoha_qdma_get_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, ++ &val, NULL); ++ if (err) ++ return err; ++ ++ val = enable ? val | enable_mask : val & ~enable_mask; ++ ++ return airoha_qdma_set_rl_param(qdma, queue_id, addr, TRTCM_MISC_MODE, ++ val); ++} ++ ++static int airoha_qdma_set_rl_token_bucket(struct airoha_qdma *qdma, ++ int queue_id, u32 rate_val, ++ u32 bucket_size) ++{ ++ u32 val, config, tick, unit, rate, rate_frac; ++ int err; ++ ++ err = airoha_qdma_get_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ TRTCM_MISC_MODE, &config, NULL); ++ if (err) ++ return err; ++ ++ val = airoha_qdma_rr(qdma, REG_INGRESS_TRTCM_CFG); ++ tick = FIELD_GET(INGRESS_FAST_TICK_MASK, val); ++ if (config & TRTCM_TICK_SEL) ++ tick *= FIELD_GET(INGRESS_SLOW_TICK_RATIO_MASK, val); ++ if (!tick) ++ return -EINVAL; ++ ++ unit = (config & TRTCM_PKT_MODE) ? 1000000 / tick : 8000 / tick; ++ if (!unit) ++ return -EINVAL; ++ ++ rate = rate_val / unit; ++ rate_frac = rate_val % unit; ++ rate_frac = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate_frac) / unit; ++ rate = FIELD_PREP(TRTCM_TOKEN_RATE_MASK, rate) | ++ FIELD_PREP(TRTCM_TOKEN_RATE_FRACTION_MASK, rate_frac); ++ ++ err = airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ TRTCM_TOKEN_RATE_MODE, rate); ++ if (err) ++ return err; ++ ++ val = bucket_size; ++ if (!(config & TRTCM_PKT_MODE)) ++ val = max_t(u32, val, MIN_TOKEN_SIZE); ++ val = min_t(u32, __fls(val), MAX_TOKEN_SIZE_OFFSET); ++ ++ return airoha_qdma_set_rl_param(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ TRTCM_BUCKETSIZE_SHIFT_MODE, val); ++} ++ ++static int airoha_qdma_init_rl_config(struct airoha_qdma *qdma, int queue_id, ++ bool enable, enum trtcm_unit_type unit) ++{ ++ bool tick_sel = queue_id == 0 || queue_id == 2 || queue_id == 8; ++ enum trtcm_param mode = TRTCM_METER_MODE; ++ int err; ++ ++ mode |= unit == TRTCM_PACKET_UNIT ? TRTCM_PKT_MODE : 0; ++ err = airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ enable, mode); ++ if (err) ++ return err; ++ ++ return airoha_qdma_set_rl_config(qdma, queue_id, REG_INGRESS_TRTCM_CFG, ++ tick_sel, TRTCM_TICK_SEL); ++} ++ + static int airoha_qdma_get_trtcm_param(struct airoha_qdma *qdma, int channel, + u32 addr, enum trtcm_param_type param, + enum trtcm_mode_type mode, +@@ -2271,10 +2408,142 @@ static int airoha_tc_htb_alloc_leaf_queu + return 0; + } + ++static int airoha_qdma_set_rx_meter(struct airoha_gdm_port *port, ++ u32 rate, u32 bucket_size, ++ enum trtcm_unit_type unit_type) ++{ ++ struct airoha_qdma *qdma = port->qdma; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { ++ int err; ++ ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ err = airoha_qdma_init_rl_config(qdma, i, !!rate, unit_type); ++ if (err) ++ return err; ++ ++ err = airoha_qdma_set_rl_token_bucket(qdma, i, rate, ++ bucket_size); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int airoha_tc_matchall_act_validate(struct tc_cls_matchall_offload *f) ++{ ++ const struct flow_action *actions = &f->rule->action; ++ const struct flow_action_entry *act; ++ ++ if (!flow_action_has_entries(actions)) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "filter run with no actions"); ++ return -EINVAL; ++ } ++ ++ if (!flow_offload_has_one_action(actions)) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "only once action per filter is supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ act = &actions->entries[0]; ++ if (act->id != FLOW_ACTION_POLICE) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, "unsupported action"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.exceed.act_id != FLOW_ACTION_DROP) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "invalid exceed action id"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "invalid notexceed action id"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && ++ !flow_action_is_last_entry(actions, act)) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "action accept must be last"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (act->police.peakrate_bytes_ps || act->police.avrate || ++ act->police.overhead || act->police.mtu) { ++ NL_SET_ERR_MSG_MOD(f->common.extack, ++ "peakrate/avrate/overhead/mtu unsupported"); ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int airoha_dev_tc_matchall(struct net_device *dev, ++ struct tc_cls_matchall_offload *f) ++{ ++ enum trtcm_unit_type unit_type = TRTCM_BYTE_UNIT; ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ u32 rate = 0, bucket_size = 0; ++ ++ switch (f->command) { ++ case TC_CLSMATCHALL_REPLACE: { ++ const struct flow_action_entry *act; ++ int err; ++ ++ err = airoha_tc_matchall_act_validate(f); ++ if (err) ++ return err; ++ ++ act = &f->rule->action.entries[0]; ++ if (act->police.rate_pkt_ps) { ++ rate = act->police.rate_pkt_ps; ++ bucket_size = act->police.burst_pkt; ++ unit_type = TRTCM_PACKET_UNIT; ++ } else { ++ rate = div_u64(act->police.rate_bytes_ps, 1000); ++ rate = rate << 3; /* Kbps */ ++ bucket_size = act->police.burst; ++ } ++ fallthrough; ++ } ++ case TC_CLSMATCHALL_DESTROY: ++ return airoha_qdma_set_rx_meter(port, rate, bucket_size, ++ unit_type); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++static int airoha_dev_setup_tc_block_cb(enum tc_setup_type type, ++ void *type_data, void *cb_priv) ++{ ++ struct net_device *dev = cb_priv; ++ ++ if (!tc_can_offload(dev)) ++ return -EOPNOTSUPP; ++ ++ switch (type) { ++ case TC_SETUP_CLSFLOWER: ++ return airoha_ppe_setup_tc_block_cb(dev, type_data); ++ case TC_SETUP_CLSMATCHALL: ++ return airoha_dev_tc_matchall(dev, type_data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ + static int airoha_dev_setup_tc_block(struct airoha_gdm_port *port, + struct flow_block_offload *f) + { +- flow_setup_cb_t *cb = airoha_ppe_setup_tc_block_cb; ++ flow_setup_cb_t *cb = airoha_dev_setup_tc_block_cb; + static LIST_HEAD(block_cb_list); + struct flow_block_cb *block_cb; + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -127,6 +127,11 @@ enum tx_sched_mode { + TC_SCH_WRR2, + }; + ++enum trtcm_unit_type { ++ TRTCM_BYTE_UNIT, ++ TRTCM_PACKET_UNIT, ++}; ++ + enum trtcm_param_type { + TRTCM_MISC_MODE, /* meter_en, pps_mode, tick_sel */ + TRTCM_TOKEN_RATE_MODE, +@@ -554,8 +559,7 @@ bool airoha_is_valid_gdm_port(struct air + + void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, + u16 hash); +-int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, +- void *cb_priv); ++int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -967,18 +967,13 @@ error_npu_put: + return err; + } + +-int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, +- void *cb_priv) ++int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) + { +- struct flow_cls_offload *cls = type_data; +- struct net_device *dev = cb_priv; + struct airoha_gdm_port *port = netdev_priv(dev); ++ struct flow_cls_offload *cls = type_data; + struct airoha_eth *eth = port->qdma->eth; + int err = 0; + +- if (!tc_can_offload(dev) || type != TC_SETUP_CLSFLOWER) +- return -EOPNOTSUPP; +- + mutex_lock(&flow_offload_mutex); + + if (!eth->npu) +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -283,6 +283,7 @@ + #define PPE_HASH_SEED 0x12345678 + + #define REG_PPE_DFT_CPORT0(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x248) ++#define DFT_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2)) + + #define REG_PPE_DFT_CPORT1(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x24c) + +@@ -691,6 +692,12 @@ + #define REG_TRTCM_DATA_LOW(_n) ((_n) + 0x8) + #define REG_TRTCM_DATA_HIGH(_n) ((_n) + 0xc) + ++#define RATE_LIMIT_PARAM_RW_MASK BIT(31) ++#define RATE_LIMIT_PARAM_RW_DONE_MASK BIT(30) ++#define RATE_LIMIT_PARAM_TYPE_MASK GENMASK(29, 28) ++#define RATE_LIMIT_METER_GROUP_MASK GENMASK(27, 26) ++#define RATE_LIMIT_PARAM_INDEX_MASK GENMASK(23, 16) ++ + #define REG_TXWRR_MODE_CFG 0x1020 + #define TWRR_WEIGHT_SCALE_MASK BIT(31) + #define TWRR_WEIGHT_BASE_MASK BIT(3) diff --git a/target/linux/airoha/patches-6.6/070-01-v6.16-net-airoha-Introduce-airoha_irq_bank-struct.patch b/target/linux/airoha/patches-6.6/070-01-v6.16-net-airoha-Introduce-airoha_irq_bank-struct.patch new file mode 100644 index 00000000000000..dd5dc164580415 --- /dev/null +++ b/target/linux/airoha/patches-6.6/070-01-v6.16-net-airoha-Introduce-airoha_irq_bank-struct.patch @@ -0,0 +1,292 @@ +From 9439db26d3ee4a897e5cd108864172531f31ce07 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 18 Apr 2025 12:40:49 +0200 +Subject: [PATCH 1/2] net: airoha: Introduce airoha_irq_bank struct + +EN7581 ethernet SoC supports 4 programmable IRQ lines each one composed +by 4 IRQ configuration registers. Add airoha_irq_bank struct as a +container for independent IRQ lines info (e.g. IRQ number, enabled source +interrupts, ecc). This is a preliminary patch to support multiple IRQ lines +in airoha_eth driver. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250418-airoha-eth-multi-irq-v1-1-1ab0083ca3c1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 106 ++++++++++++++-------- + drivers/net/ethernet/airoha/airoha_eth.h | 13 ++- + drivers/net/ethernet/airoha/airoha_regs.h | 11 ++- + 3 files changed, 86 insertions(+), 44 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -34,37 +34,40 @@ u32 airoha_rmw(void __iomem *base, u32 o + return val; + } + +-static void airoha_qdma_set_irqmask(struct airoha_qdma *qdma, int index, +- u32 clear, u32 set) ++static void airoha_qdma_set_irqmask(struct airoha_irq_bank *irq_bank, ++ int index, u32 clear, u32 set) + { ++ struct airoha_qdma *qdma = irq_bank->qdma; ++ int bank = irq_bank - &qdma->irq_banks[0]; + unsigned long flags; + +- if (WARN_ON_ONCE(index >= ARRAY_SIZE(qdma->irqmask))) ++ if (WARN_ON_ONCE(index >= ARRAY_SIZE(irq_bank->irqmask))) + return; + +- spin_lock_irqsave(&qdma->irq_lock, flags); ++ spin_lock_irqsave(&irq_bank->irq_lock, flags); + +- qdma->irqmask[index] &= ~clear; +- qdma->irqmask[index] |= set; +- airoha_qdma_wr(qdma, REG_INT_ENABLE(index), qdma->irqmask[index]); ++ irq_bank->irqmask[index] &= ~clear; ++ irq_bank->irqmask[index] |= set; ++ airoha_qdma_wr(qdma, REG_INT_ENABLE(bank, index), ++ irq_bank->irqmask[index]); + /* Read irq_enable register in order to guarantee the update above + * completes in the spinlock critical section. + */ +- airoha_qdma_rr(qdma, REG_INT_ENABLE(index)); ++ airoha_qdma_rr(qdma, REG_INT_ENABLE(bank, index)); + +- spin_unlock_irqrestore(&qdma->irq_lock, flags); ++ spin_unlock_irqrestore(&irq_bank->irq_lock, flags); + } + +-static void airoha_qdma_irq_enable(struct airoha_qdma *qdma, int index, +- u32 mask) ++static void airoha_qdma_irq_enable(struct airoha_irq_bank *irq_bank, ++ int index, u32 mask) + { +- airoha_qdma_set_irqmask(qdma, index, 0, mask); ++ airoha_qdma_set_irqmask(irq_bank, index, 0, mask); + } + +-static void airoha_qdma_irq_disable(struct airoha_qdma *qdma, int index, +- u32 mask) ++static void airoha_qdma_irq_disable(struct airoha_irq_bank *irq_bank, ++ int index, u32 mask) + { +- airoha_qdma_set_irqmask(qdma, index, mask, 0); ++ airoha_qdma_set_irqmask(irq_bank, index, mask, 0); + } + + static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) +@@ -732,6 +735,7 @@ free_frag: + static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); ++ struct airoha_irq_bank *irq_bank = &q->qdma->irq_banks[0]; + int cur, done = 0; + + do { +@@ -740,7 +744,7 @@ static int airoha_qdma_rx_napi_poll(stru + } while (cur && done < budget); + + if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(q->qdma, QDMA_INT_REG_IDX1, ++ airoha_qdma_irq_enable(irq_bank, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + + return done; +@@ -945,7 +949,7 @@ unlock: + } + + if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, + TX_DONE_INT_MASK(id)); + + return done; +@@ -1176,13 +1180,16 @@ static int airoha_qdma_hw_init(struct ai + int i; + + /* clear pending irqs */ +- for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks[0].irqmask); i++) + airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); + + /* setup irqs */ +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX0, INT_IDX0_MASK); +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX1, INT_IDX1_MASK); +- airoha_qdma_irq_enable(qdma, QDMA_INT_REG_IDX4, INT_IDX4_MASK); ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, ++ INT_IDX0_MASK); ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX1, ++ INT_IDX1_MASK); ++ airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX4, ++ INT_IDX4_MASK); + + /* setup irq binding */ + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +@@ -1227,13 +1234,14 @@ static int airoha_qdma_hw_init(struct ai + + static irqreturn_t airoha_irq_handler(int irq, void *dev_instance) + { +- struct airoha_qdma *qdma = dev_instance; +- u32 intr[ARRAY_SIZE(qdma->irqmask)]; ++ struct airoha_irq_bank *irq_bank = dev_instance; ++ struct airoha_qdma *qdma = irq_bank->qdma; ++ u32 intr[ARRAY_SIZE(irq_bank->irqmask)]; + int i; + +- for (i = 0; i < ARRAY_SIZE(qdma->irqmask); i++) { ++ for (i = 0; i < ARRAY_SIZE(intr); i++) { + intr[i] = airoha_qdma_rr(qdma, REG_INT_STATUS(i)); +- intr[i] &= qdma->irqmask[i]; ++ intr[i] &= irq_bank->irqmask[i]; + airoha_qdma_wr(qdma, REG_INT_STATUS(i), intr[i]); + } + +@@ -1241,7 +1249,7 @@ static irqreturn_t airoha_irq_handler(in + return IRQ_NONE; + + if (intr[1] & RX_DONE_INT_MASK) { +- airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX1, ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, + RX_DONE_INT_MASK); + + for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +@@ -1258,7 +1266,7 @@ static irqreturn_t airoha_irq_handler(in + if (!(intr[0] & TX_DONE_INT_MASK(i))) + continue; + +- airoha_qdma_irq_disable(qdma, QDMA_INT_REG_IDX0, ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX0, + TX_DONE_INT_MASK(i)); + napi_schedule(&qdma->q_tx_irq[i].napi); + } +@@ -1267,6 +1275,39 @@ static irqreturn_t airoha_irq_handler(in + return IRQ_HANDLED; + } + ++static int airoha_qdma_init_irq_banks(struct platform_device *pdev, ++ struct airoha_qdma *qdma) ++{ ++ struct airoha_eth *eth = qdma->eth; ++ int i, id = qdma - ð->qdma[0]; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { ++ struct airoha_irq_bank *irq_bank = &qdma->irq_banks[i]; ++ int err, irq_index = 4 * id + i; ++ const char *name; ++ ++ spin_lock_init(&irq_bank->irq_lock); ++ irq_bank->qdma = qdma; ++ ++ irq_bank->irq = platform_get_irq(pdev, irq_index); ++ if (irq_bank->irq < 0) ++ return irq_bank->irq; ++ ++ name = devm_kasprintf(eth->dev, GFP_KERNEL, ++ KBUILD_MODNAME ".%d", irq_index); ++ if (!name) ++ return -ENOMEM; ++ ++ err = devm_request_irq(eth->dev, irq_bank->irq, ++ airoha_irq_handler, IRQF_SHARED, name, ++ irq_bank); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ + static int airoha_qdma_init(struct platform_device *pdev, + struct airoha_eth *eth, + struct airoha_qdma *qdma) +@@ -1274,9 +1315,7 @@ static int airoha_qdma_init(struct platf + int err, id = qdma - ð->qdma[0]; + const char *res; + +- spin_lock_init(&qdma->irq_lock); + qdma->eth = eth; +- + res = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d", id); + if (!res) + return -ENOMEM; +@@ -1286,12 +1325,7 @@ static int airoha_qdma_init(struct platf + return dev_err_probe(eth->dev, PTR_ERR(qdma->regs), + "failed to iomap qdma%d regs\n", id); + +- qdma->irq = platform_get_irq(pdev, 4 * id); +- if (qdma->irq < 0) +- return qdma->irq; +- +- err = devm_request_irq(eth->dev, qdma->irq, airoha_irq_handler, +- IRQF_SHARED, KBUILD_MODNAME, qdma); ++ err = airoha_qdma_init_irq_banks(pdev, qdma); + if (err) + return err; + +@@ -2782,7 +2816,7 @@ static int airoha_alloc_gdm_port(struct + dev->features |= dev->hw_features; + dev->vlan_features = dev->hw_features; + dev->dev.of_node = np; +- dev->irq = qdma->irq; ++ dev->irq = qdma->irq_banks[0].irq; + SET_NETDEV_DEV(dev, eth->dev); + + /* reserve hw queues for HTB offloading */ +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -17,6 +17,7 @@ + + #define AIROHA_MAX_NUM_GDM_PORTS 4 + #define AIROHA_MAX_NUM_QDMA 2 ++#define AIROHA_MAX_NUM_IRQ_BANKS 1 + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 +@@ -452,17 +453,23 @@ struct airoha_flow_table_entry { + unsigned long cookie; + }; + +-struct airoha_qdma { +- struct airoha_eth *eth; +- void __iomem *regs; ++struct airoha_irq_bank { ++ struct airoha_qdma *qdma; + + /* protect concurrent irqmask accesses */ + spinlock_t irq_lock; + u32 irqmask[QDMA_INT_REG_MAX]; + int irq; ++}; ++ ++struct airoha_qdma { ++ struct airoha_eth *eth; ++ void __iomem *regs; + + atomic_t users; + ++ struct airoha_irq_bank irq_banks[AIROHA_MAX_NUM_IRQ_BANKS]; ++ + struct airoha_tx_irq_queue q_tx_irq[AIROHA_NUM_TX_IRQ]; + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -423,11 +423,12 @@ + ((_n) == 2) ? 0x0720 : \ + ((_n) == 1) ? 0x0024 : 0x0020) + +-#define REG_INT_ENABLE(_n) \ +- (((_n) == 4) ? 0x0750 : \ +- ((_n) == 3) ? 0x0744 : \ +- ((_n) == 2) ? 0x0740 : \ +- ((_n) == 1) ? 0x002c : 0x0028) ++#define REG_INT_ENABLE(_b, _n) \ ++ (((_n) == 4) ? 0x0750 + ((_b) << 5) : \ ++ ((_n) == 3) ? 0x0744 + ((_b) << 5) : \ ++ ((_n) == 2) ? 0x0740 + ((_b) << 5) : \ ++ ((_n) == 1) ? 0x002c + ((_b) << 3) : \ ++ 0x0028 + ((_b) << 3)) + + /* QDMA_CSR_INT_ENABLE1 */ + #define RX15_COHERENT_INT_MASK BIT(31) diff --git a/target/linux/airoha/patches-6.6/070-02-v6.16-net-airoha-Enable-multiple-IRQ-lines-support-in-airo.patch b/target/linux/airoha/patches-6.6/070-02-v6.16-net-airoha-Enable-multiple-IRQ-lines-support-in-airo.patch new file mode 100644 index 00000000000000..db4494e12f2a9a --- /dev/null +++ b/target/linux/airoha/patches-6.6/070-02-v6.16-net-airoha-Enable-multiple-IRQ-lines-support-in-airo.patch @@ -0,0 +1,379 @@ +From f252493e1835366fc25ce631c3056f900977dd11 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 18 Apr 2025 12:40:50 +0200 +Subject: [PATCH 2/2] net: airoha: Enable multiple IRQ lines support in + airoha_eth driver. + +EN7581 ethernet SoC supports 4 programmable IRQ lines for Tx and Rx +interrupts. Enable multiple IRQ lines support. Map Rx/Tx queues to the +available IRQ lines using the default scheme used in the vendor SDK: + +- IRQ0: rx queues [0-4],[7-9],15 +- IRQ1: rx queues [21-30] +- IRQ2: rx queues 5 +- IRQ3: rx queues 6 + +Tx queues interrupts are managed by IRQ0. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250418-airoha-eth-multi-irq-v1-2-1ab0083ca3c1@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 67 +++++--- + drivers/net/ethernet/airoha/airoha_eth.h | 13 +- + drivers/net/ethernet/airoha/airoha_regs.h | 185 +++++++++++++++++----- + 3 files changed, 206 insertions(+), 59 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -735,7 +735,6 @@ free_frag: + static int airoha_qdma_rx_napi_poll(struct napi_struct *napi, int budget) + { + struct airoha_queue *q = container_of(napi, struct airoha_queue, napi); +- struct airoha_irq_bank *irq_bank = &q->qdma->irq_banks[0]; + int cur, done = 0; + + do { +@@ -743,9 +742,20 @@ static int airoha_qdma_rx_napi_poll(stru + done += cur; + } while (cur && done < budget); + +- if (done < budget && napi_complete(napi)) +- airoha_qdma_irq_enable(irq_bank, QDMA_INT_REG_IDX1, +- RX_DONE_INT_MASK); ++ if (done < budget && napi_complete(napi)) { ++ struct airoha_qdma *qdma = q->qdma; ++ int i, qid = q - &qdma->q_rx[0]; ++ int intr_reg = qid < RX_DONE_HIGH_OFFSET ? QDMA_INT_REG_IDX1 ++ : QDMA_INT_REG_IDX2; ++ ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { ++ if (!(BIT(qid) & RX_IRQ_BANK_PIN_MASK(i))) ++ continue; ++ ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], intr_reg, ++ BIT(qid % RX_DONE_HIGH_OFFSET)); ++ } ++ } + + return done; + } +@@ -1179,17 +1189,24 @@ static int airoha_qdma_hw_init(struct ai + { + int i; + +- /* clear pending irqs */ +- for (i = 0; i < ARRAY_SIZE(qdma->irq_banks[0].irqmask); i++) ++ for (i = 0; i < ARRAY_SIZE(qdma->irq_banks); i++) { ++ /* clear pending irqs */ + airoha_qdma_wr(qdma, REG_INT_STATUS(i), 0xffffffff); +- +- /* setup irqs */ ++ /* setup rx irqs */ ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX0, ++ INT_RX0_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX1, ++ INT_RX1_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX2, ++ INT_RX2_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ airoha_qdma_irq_enable(&qdma->irq_banks[i], QDMA_INT_REG_IDX3, ++ INT_RX3_MASK(RX_IRQ_BANK_PIN_MASK(i))); ++ } ++ /* setup tx irqs */ + airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX0, +- INT_IDX0_MASK); +- airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX1, +- INT_IDX1_MASK); ++ TX_COHERENT_LOW_INT_MASK | INT_TX_MASK); + airoha_qdma_irq_enable(&qdma->irq_banks[0], QDMA_INT_REG_IDX4, +- INT_IDX4_MASK); ++ TX_COHERENT_HIGH_INT_MASK); + + /* setup irq binding */ + for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { +@@ -1236,6 +1253,7 @@ static irqreturn_t airoha_irq_handler(in + { + struct airoha_irq_bank *irq_bank = dev_instance; + struct airoha_qdma *qdma = irq_bank->qdma; ++ u32 rx_intr_mask = 0, rx_intr1, rx_intr2; + u32 intr[ARRAY_SIZE(irq_bank->irqmask)]; + int i; + +@@ -1248,17 +1266,24 @@ static irqreturn_t airoha_irq_handler(in + if (!test_bit(DEV_STATE_INITIALIZED, &qdma->eth->state)) + return IRQ_NONE; + +- if (intr[1] & RX_DONE_INT_MASK) { +- airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, +- RX_DONE_INT_MASK); ++ rx_intr1 = intr[1] & RX_DONE_LOW_INT_MASK; ++ if (rx_intr1) { ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX1, rx_intr1); ++ rx_intr_mask |= rx_intr1; ++ } + +- for (i = 0; i < ARRAY_SIZE(qdma->q_rx); i++) { +- if (!qdma->q_rx[i].ndesc) +- continue; ++ rx_intr2 = intr[2] & RX_DONE_HIGH_INT_MASK; ++ if (rx_intr2) { ++ airoha_qdma_irq_disable(irq_bank, QDMA_INT_REG_IDX2, rx_intr2); ++ rx_intr_mask |= (rx_intr2 << 16); ++ } + +- if (intr[1] & BIT(i)) +- napi_schedule(&qdma->q_rx[i].napi); +- } ++ for (i = 0; rx_intr_mask && i < ARRAY_SIZE(qdma->q_rx); i++) { ++ if (!qdma->q_rx[i].ndesc) ++ continue; ++ ++ if (rx_intr_mask & BIT(i)) ++ napi_schedule(&qdma->q_rx[i].napi); + } + + if (intr[0] & INT_TX_MASK) { +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -17,7 +17,7 @@ + + #define AIROHA_MAX_NUM_GDM_PORTS 4 + #define AIROHA_MAX_NUM_QDMA 2 +-#define AIROHA_MAX_NUM_IRQ_BANKS 1 ++#define AIROHA_MAX_NUM_IRQ_BANKS 4 + #define AIROHA_MAX_DSA_PORTS 7 + #define AIROHA_MAX_NUM_RSTS 3 + #define AIROHA_MAX_NUM_XSI_RSTS 5 +@@ -453,6 +453,17 @@ struct airoha_flow_table_entry { + unsigned long cookie; + }; + ++/* RX queue to IRQ mapping: BIT(q) in IRQ(n) */ ++#define RX_IRQ0_BANK_PIN_MASK 0x839f ++#define RX_IRQ1_BANK_PIN_MASK 0x7fe00000 ++#define RX_IRQ2_BANK_PIN_MASK 0x20 ++#define RX_IRQ3_BANK_PIN_MASK 0x40 ++#define RX_IRQ_BANK_PIN_MASK(_n) \ ++ (((_n) == 3) ? RX_IRQ3_BANK_PIN_MASK : \ ++ ((_n) == 2) ? RX_IRQ2_BANK_PIN_MASK : \ ++ ((_n) == 1) ? RX_IRQ1_BANK_PIN_MASK : \ ++ RX_IRQ0_BANK_PIN_MASK) ++ + struct airoha_irq_bank { + struct airoha_qdma *qdma; + +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -463,6 +463,26 @@ + #define IRQ0_FULL_INT_MASK BIT(1) + #define IRQ0_INT_MASK BIT(0) + ++#define RX_COHERENT_LOW_INT_MASK \ ++ (RX15_COHERENT_INT_MASK | RX14_COHERENT_INT_MASK | \ ++ RX13_COHERENT_INT_MASK | RX12_COHERENT_INT_MASK | \ ++ RX11_COHERENT_INT_MASK | RX10_COHERENT_INT_MASK | \ ++ RX9_COHERENT_INT_MASK | RX8_COHERENT_INT_MASK | \ ++ RX7_COHERENT_INT_MASK | RX6_COHERENT_INT_MASK | \ ++ RX5_COHERENT_INT_MASK | RX4_COHERENT_INT_MASK | \ ++ RX3_COHERENT_INT_MASK | RX2_COHERENT_INT_MASK | \ ++ RX1_COHERENT_INT_MASK | RX0_COHERENT_INT_MASK) ++ ++#define RX_COHERENT_LOW_OFFSET __ffs(RX_COHERENT_LOW_INT_MASK) ++#define INT_RX0_MASK(_n) \ ++ (((_n) << RX_COHERENT_LOW_OFFSET) & RX_COHERENT_LOW_INT_MASK) ++ ++#define TX_COHERENT_LOW_INT_MASK \ ++ (TX7_COHERENT_INT_MASK | TX6_COHERENT_INT_MASK | \ ++ TX5_COHERENT_INT_MASK | TX4_COHERENT_INT_MASK | \ ++ TX3_COHERENT_INT_MASK | TX2_COHERENT_INT_MASK | \ ++ TX1_COHERENT_INT_MASK | TX0_COHERENT_INT_MASK) ++ + #define TX_DONE_INT_MASK(_n) \ + ((_n) ? IRQ1_INT_MASK | IRQ1_FULL_INT_MASK \ + : IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) +@@ -471,17 +491,6 @@ + (IRQ1_INT_MASK | IRQ1_FULL_INT_MASK | \ + IRQ0_INT_MASK | IRQ0_FULL_INT_MASK) + +-#define INT_IDX0_MASK \ +- (TX0_COHERENT_INT_MASK | TX1_COHERENT_INT_MASK | \ +- TX2_COHERENT_INT_MASK | TX3_COHERENT_INT_MASK | \ +- TX4_COHERENT_INT_MASK | TX5_COHERENT_INT_MASK | \ +- TX6_COHERENT_INT_MASK | TX7_COHERENT_INT_MASK | \ +- RX0_COHERENT_INT_MASK | RX1_COHERENT_INT_MASK | \ +- RX2_COHERENT_INT_MASK | RX3_COHERENT_INT_MASK | \ +- RX4_COHERENT_INT_MASK | RX7_COHERENT_INT_MASK | \ +- RX8_COHERENT_INT_MASK | RX9_COHERENT_INT_MASK | \ +- RX15_COHERENT_INT_MASK | INT_TX_MASK) +- + /* QDMA_CSR_INT_ENABLE2 */ + #define RX15_NO_CPU_DSCP_INT_MASK BIT(31) + #define RX14_NO_CPU_DSCP_INT_MASK BIT(30) +@@ -516,19 +525,121 @@ + #define RX1_DONE_INT_MASK BIT(1) + #define RX0_DONE_INT_MASK BIT(0) + +-#define RX_DONE_INT_MASK \ +- (RX0_DONE_INT_MASK | RX1_DONE_INT_MASK | \ +- RX2_DONE_INT_MASK | RX3_DONE_INT_MASK | \ +- RX4_DONE_INT_MASK | RX7_DONE_INT_MASK | \ +- RX8_DONE_INT_MASK | RX9_DONE_INT_MASK | \ +- RX15_DONE_INT_MASK) +-#define INT_IDX1_MASK \ +- (RX_DONE_INT_MASK | \ +- RX0_NO_CPU_DSCP_INT_MASK | RX1_NO_CPU_DSCP_INT_MASK | \ +- RX2_NO_CPU_DSCP_INT_MASK | RX3_NO_CPU_DSCP_INT_MASK | \ +- RX4_NO_CPU_DSCP_INT_MASK | RX7_NO_CPU_DSCP_INT_MASK | \ +- RX8_NO_CPU_DSCP_INT_MASK | RX9_NO_CPU_DSCP_INT_MASK | \ +- RX15_NO_CPU_DSCP_INT_MASK) ++#define RX_NO_CPU_DSCP_LOW_INT_MASK \ ++ (RX15_NO_CPU_DSCP_INT_MASK | RX14_NO_CPU_DSCP_INT_MASK | \ ++ RX13_NO_CPU_DSCP_INT_MASK | RX12_NO_CPU_DSCP_INT_MASK | \ ++ RX11_NO_CPU_DSCP_INT_MASK | RX10_NO_CPU_DSCP_INT_MASK | \ ++ RX9_NO_CPU_DSCP_INT_MASK | RX8_NO_CPU_DSCP_INT_MASK | \ ++ RX7_NO_CPU_DSCP_INT_MASK | RX6_NO_CPU_DSCP_INT_MASK | \ ++ RX5_NO_CPU_DSCP_INT_MASK | RX4_NO_CPU_DSCP_INT_MASK | \ ++ RX3_NO_CPU_DSCP_INT_MASK | RX2_NO_CPU_DSCP_INT_MASK | \ ++ RX1_NO_CPU_DSCP_INT_MASK | RX0_NO_CPU_DSCP_INT_MASK) ++ ++#define RX_DONE_LOW_INT_MASK \ ++ (RX15_DONE_INT_MASK | RX14_DONE_INT_MASK | \ ++ RX13_DONE_INT_MASK | RX12_DONE_INT_MASK | \ ++ RX11_DONE_INT_MASK | RX10_DONE_INT_MASK | \ ++ RX9_DONE_INT_MASK | RX8_DONE_INT_MASK | \ ++ RX7_DONE_INT_MASK | RX6_DONE_INT_MASK | \ ++ RX5_DONE_INT_MASK | RX4_DONE_INT_MASK | \ ++ RX3_DONE_INT_MASK | RX2_DONE_INT_MASK | \ ++ RX1_DONE_INT_MASK | RX0_DONE_INT_MASK) ++ ++#define RX_NO_CPU_DSCP_LOW_OFFSET __ffs(RX_NO_CPU_DSCP_LOW_INT_MASK) ++#define INT_RX1_MASK(_n) \ ++ ((((_n) << RX_NO_CPU_DSCP_LOW_OFFSET) & RX_NO_CPU_DSCP_LOW_INT_MASK) | \ ++ (RX_DONE_LOW_INT_MASK & (_n))) ++ ++/* QDMA_CSR_INT_ENABLE3 */ ++#define RX31_NO_CPU_DSCP_INT_MASK BIT(31) ++#define RX30_NO_CPU_DSCP_INT_MASK BIT(30) ++#define RX29_NO_CPU_DSCP_INT_MASK BIT(29) ++#define RX28_NO_CPU_DSCP_INT_MASK BIT(28) ++#define RX27_NO_CPU_DSCP_INT_MASK BIT(27) ++#define RX26_NO_CPU_DSCP_INT_MASK BIT(26) ++#define RX25_NO_CPU_DSCP_INT_MASK BIT(25) ++#define RX24_NO_CPU_DSCP_INT_MASK BIT(24) ++#define RX23_NO_CPU_DSCP_INT_MASK BIT(23) ++#define RX22_NO_CPU_DSCP_INT_MASK BIT(22) ++#define RX21_NO_CPU_DSCP_INT_MASK BIT(21) ++#define RX20_NO_CPU_DSCP_INT_MASK BIT(20) ++#define RX19_NO_CPU_DSCP_INT_MASK BIT(19) ++#define RX18_NO_CPU_DSCP_INT_MASK BIT(18) ++#define RX17_NO_CPU_DSCP_INT_MASK BIT(17) ++#define RX16_NO_CPU_DSCP_INT_MASK BIT(16) ++#define RX31_DONE_INT_MASK BIT(15) ++#define RX30_DONE_INT_MASK BIT(14) ++#define RX29_DONE_INT_MASK BIT(13) ++#define RX28_DONE_INT_MASK BIT(12) ++#define RX27_DONE_INT_MASK BIT(11) ++#define RX26_DONE_INT_MASK BIT(10) ++#define RX25_DONE_INT_MASK BIT(9) ++#define RX24_DONE_INT_MASK BIT(8) ++#define RX23_DONE_INT_MASK BIT(7) ++#define RX22_DONE_INT_MASK BIT(6) ++#define RX21_DONE_INT_MASK BIT(5) ++#define RX20_DONE_INT_MASK BIT(4) ++#define RX19_DONE_INT_MASK BIT(3) ++#define RX18_DONE_INT_MASK BIT(2) ++#define RX17_DONE_INT_MASK BIT(1) ++#define RX16_DONE_INT_MASK BIT(0) ++ ++#define RX_NO_CPU_DSCP_HIGH_INT_MASK \ ++ (RX31_NO_CPU_DSCP_INT_MASK | RX30_NO_CPU_DSCP_INT_MASK | \ ++ RX29_NO_CPU_DSCP_INT_MASK | RX28_NO_CPU_DSCP_INT_MASK | \ ++ RX27_NO_CPU_DSCP_INT_MASK | RX26_NO_CPU_DSCP_INT_MASK | \ ++ RX25_NO_CPU_DSCP_INT_MASK | RX24_NO_CPU_DSCP_INT_MASK | \ ++ RX23_NO_CPU_DSCP_INT_MASK | RX22_NO_CPU_DSCP_INT_MASK | \ ++ RX21_NO_CPU_DSCP_INT_MASK | RX20_NO_CPU_DSCP_INT_MASK | \ ++ RX19_NO_CPU_DSCP_INT_MASK | RX18_NO_CPU_DSCP_INT_MASK | \ ++ RX17_NO_CPU_DSCP_INT_MASK | RX16_NO_CPU_DSCP_INT_MASK) ++ ++#define RX_DONE_HIGH_INT_MASK \ ++ (RX31_DONE_INT_MASK | RX30_DONE_INT_MASK | \ ++ RX29_DONE_INT_MASK | RX28_DONE_INT_MASK | \ ++ RX27_DONE_INT_MASK | RX26_DONE_INT_MASK | \ ++ RX25_DONE_INT_MASK | RX24_DONE_INT_MASK | \ ++ RX23_DONE_INT_MASK | RX22_DONE_INT_MASK | \ ++ RX21_DONE_INT_MASK | RX20_DONE_INT_MASK | \ ++ RX19_DONE_INT_MASK | RX18_DONE_INT_MASK | \ ++ RX17_DONE_INT_MASK | RX16_DONE_INT_MASK) ++ ++#define RX_DONE_INT_MASK (RX_DONE_HIGH_INT_MASK | RX_DONE_LOW_INT_MASK) ++#define RX_DONE_HIGH_OFFSET fls(RX_DONE_HIGH_INT_MASK) ++ ++#define INT_RX2_MASK(_n) \ ++ ((RX_NO_CPU_DSCP_HIGH_INT_MASK & (_n)) | \ ++ (((_n) >> RX_DONE_HIGH_OFFSET) & RX_DONE_HIGH_INT_MASK)) ++ ++/* QDMA_CSR_INT_ENABLE4 */ ++#define RX31_COHERENT_INT_MASK BIT(31) ++#define RX30_COHERENT_INT_MASK BIT(30) ++#define RX29_COHERENT_INT_MASK BIT(29) ++#define RX28_COHERENT_INT_MASK BIT(28) ++#define RX27_COHERENT_INT_MASK BIT(27) ++#define RX26_COHERENT_INT_MASK BIT(26) ++#define RX25_COHERENT_INT_MASK BIT(25) ++#define RX24_COHERENT_INT_MASK BIT(24) ++#define RX23_COHERENT_INT_MASK BIT(23) ++#define RX22_COHERENT_INT_MASK BIT(22) ++#define RX21_COHERENT_INT_MASK BIT(21) ++#define RX20_COHERENT_INT_MASK BIT(20) ++#define RX19_COHERENT_INT_MASK BIT(19) ++#define RX18_COHERENT_INT_MASK BIT(18) ++#define RX17_COHERENT_INT_MASK BIT(17) ++#define RX16_COHERENT_INT_MASK BIT(16) ++ ++#define RX_COHERENT_HIGH_INT_MASK \ ++ (RX31_COHERENT_INT_MASK | RX30_COHERENT_INT_MASK | \ ++ RX29_COHERENT_INT_MASK | RX28_COHERENT_INT_MASK | \ ++ RX27_COHERENT_INT_MASK | RX26_COHERENT_INT_MASK | \ ++ RX25_COHERENT_INT_MASK | RX24_COHERENT_INT_MASK | \ ++ RX23_COHERENT_INT_MASK | RX22_COHERENT_INT_MASK | \ ++ RX21_COHERENT_INT_MASK | RX20_COHERENT_INT_MASK | \ ++ RX19_COHERENT_INT_MASK | RX18_COHERENT_INT_MASK | \ ++ RX17_COHERENT_INT_MASK | RX16_COHERENT_INT_MASK) ++ ++#define INT_RX3_MASK(_n) (RX_COHERENT_HIGH_INT_MASK & (_n)) + + /* QDMA_CSR_INT_ENABLE5 */ + #define TX31_COHERENT_INT_MASK BIT(31) +@@ -556,19 +667,19 @@ + #define TX9_COHERENT_INT_MASK BIT(9) + #define TX8_COHERENT_INT_MASK BIT(8) + +-#define INT_IDX4_MASK \ +- (TX8_COHERENT_INT_MASK | TX9_COHERENT_INT_MASK | \ +- TX10_COHERENT_INT_MASK | TX11_COHERENT_INT_MASK | \ +- TX12_COHERENT_INT_MASK | TX13_COHERENT_INT_MASK | \ +- TX14_COHERENT_INT_MASK | TX15_COHERENT_INT_MASK | \ +- TX16_COHERENT_INT_MASK | TX17_COHERENT_INT_MASK | \ +- TX18_COHERENT_INT_MASK | TX19_COHERENT_INT_MASK | \ +- TX20_COHERENT_INT_MASK | TX21_COHERENT_INT_MASK | \ +- TX22_COHERENT_INT_MASK | TX23_COHERENT_INT_MASK | \ +- TX24_COHERENT_INT_MASK | TX25_COHERENT_INT_MASK | \ +- TX26_COHERENT_INT_MASK | TX27_COHERENT_INT_MASK | \ +- TX28_COHERENT_INT_MASK | TX29_COHERENT_INT_MASK | \ +- TX30_COHERENT_INT_MASK | TX31_COHERENT_INT_MASK) ++#define TX_COHERENT_HIGH_INT_MASK \ ++ (TX31_COHERENT_INT_MASK | TX30_COHERENT_INT_MASK | \ ++ TX29_COHERENT_INT_MASK | TX28_COHERENT_INT_MASK | \ ++ TX27_COHERENT_INT_MASK | TX26_COHERENT_INT_MASK | \ ++ TX25_COHERENT_INT_MASK | TX24_COHERENT_INT_MASK | \ ++ TX23_COHERENT_INT_MASK | TX22_COHERENT_INT_MASK | \ ++ TX21_COHERENT_INT_MASK | TX20_COHERENT_INT_MASK | \ ++ TX19_COHERENT_INT_MASK | TX18_COHERENT_INT_MASK | \ ++ TX17_COHERENT_INT_MASK | TX16_COHERENT_INT_MASK | \ ++ TX15_COHERENT_INT_MASK | TX14_COHERENT_INT_MASK | \ ++ TX13_COHERENT_INT_MASK | TX12_COHERENT_INT_MASK | \ ++ TX11_COHERENT_INT_MASK | TX10_COHERENT_INT_MASK | \ ++ TX9_COHERENT_INT_MASK | TX8_COHERENT_INT_MASK) + + #define REG_TX_IRQ_BASE(_n) ((_n) ? 0x0048 : 0x0050) + diff --git a/target/linux/airoha/patches-6.6/071-v6.15-net-airoha-Add-missing-field-to-ppe_mbox_data-struct.patch b/target/linux/airoha/patches-6.6/071-v6.15-net-airoha-Add-missing-field-to-ppe_mbox_data-struct.patch new file mode 100644 index 00000000000000..2fb90b6c3b79a4 --- /dev/null +++ b/target/linux/airoha/patches-6.6/071-v6.15-net-airoha-Add-missing-field-to-ppe_mbox_data-struct.patch @@ -0,0 +1,48 @@ +From 4a7843cc8a41b9612becccc07715ed017770eb89 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 6 May 2025 18:56:47 +0200 +Subject: [PATCH] net: airoha: Add missing field to ppe_mbox_data struct + +The official Airoha EN7581 firmware requires adding max_packet field in +ppe_mbox_data struct while the unofficial one used to develop the Airoha +EN7581 flowtable support does not require this field. +This patch does not introduce any real backwards compatible issue since +EN7581 fw is not publicly available in linux-firmware or other +repositories (e.g. OpenWrt) yet and the official fw version will use this +new layout. For this reason this change needs to be backported. +Moreover, make explicit the padding added by the compiler introducing +the rsv array in init_info struct. +At the same time use u32 instead of int for init_info and set_info +struct definitions in ppe_mbox_data struct. + +Fixes: 23290c7bc190d ("net: airoha: Introduce Airoha NPU support") +Reviewed-by: Simon Horman +Reviewed-by: Jacob Keller +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250506-airoha-en7581-fix-ppe_mbox_data-v5-1-29cabed6864d@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -104,12 +104,14 @@ struct ppe_mbox_data { + u8 xpon_hal_api; + u8 wan_xsi; + u8 ct_joyme4; +- int ppe_type; +- int wan_mode; +- int wan_sel; ++ u8 max_packet; ++ u8 rsv[3]; ++ u32 ppe_type; ++ u32 wan_mode; ++ u32 wan_sel; + } init_info; + struct { +- int func_id; ++ u32 func_id; + u32 size; + u32 data; + } set_info; diff --git a/target/linux/airoha/patches-6.6/072-v6.15-net-airoha-Fix-page-recycling-in-airoha_qdma_rx_proc.patch b/target/linux/airoha/patches-6.6/072-v6.15-net-airoha-Fix-page-recycling-in-airoha_qdma_rx_proc.patch new file mode 100644 index 00000000000000..bcf60ce8dd8617 --- /dev/null +++ b/target/linux/airoha/patches-6.6/072-v6.15-net-airoha-Fix-page-recycling-in-airoha_qdma_rx_proc.patch @@ -0,0 +1,72 @@ +From d6d2b0e1538d5c381ec0ca95afaf772c096ea5dc Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 15 May 2025 08:33:06 +0200 +Subject: [PATCH] net: airoha: Fix page recycling in airoha_qdma_rx_process() + +Do not recycle the page twice in airoha_qdma_rx_process routine in case +of error. Just run dev_kfree_skb() if the skb has been allocated and marked +for recycling. Run page_pool_put_full_page() directly if the skb has not +been allocated yet. +Moreover, rely on DMA address from queue entry element instead of reading +it from the DMA descriptor for DMA syncing in airoha_qdma_rx_process(). + +Fixes: e12182ddb6e71 ("net: airoha: Enable Rx Scatter-Gather") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250515-airoha-fix-rx-process-error-condition-v2-1-657e92c894b9@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -636,7 +636,6 @@ static int airoha_qdma_rx_process(struct + struct airoha_queue_entry *e = &q->entry[q->tail]; + struct airoha_qdma_desc *desc = &q->desc[q->tail]; + u32 hash, reason, msg1 = le32_to_cpu(desc->msg1); +- dma_addr_t dma_addr = le32_to_cpu(desc->addr); + struct page *page = virt_to_head_page(e->buf); + u32 desc_ctrl = le32_to_cpu(desc->ctrl); + struct airoha_gdm_port *port; +@@ -645,22 +644,16 @@ static int airoha_qdma_rx_process(struct + if (!(desc_ctrl & QDMA_DESC_DONE_MASK)) + break; + +- if (!dma_addr) +- break; +- +- len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); +- if (!len) +- break; +- + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + +- dma_sync_single_for_cpu(eth->dev, dma_addr, ++ dma_sync_single_for_cpu(eth->dev, e->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), dir); + ++ len = FIELD_GET(QDMA_DESC_LEN_MASK, desc_ctrl); + data_len = q->skb ? q->buf_size + : SKB_WITH_OVERHEAD(q->buf_size); +- if (data_len < len) ++ if (!len || data_len < len) + goto free_frag; + + p = airoha_qdma_get_gdm_port(eth, desc); +@@ -723,9 +716,12 @@ static int airoha_qdma_rx_process(struct + q->skb = NULL; + continue; + free_frag: +- page_pool_put_full_page(q->page_pool, page, true); +- dev_kfree_skb(q->skb); +- q->skb = NULL; ++ if (q->skb) { ++ dev_kfree_skb(q->skb); ++ q->skb = NULL; ++ } else { ++ page_pool_put_full_page(q->page_pool, page, true); ++ } + } + airoha_qdma_fill_rx_queue(q); + diff --git a/target/linux/airoha/patches-6.6/073-01-v6.16-net-airoha-npu-Move-memory-allocation-in-airoha_npu_.patch b/target/linux/airoha/patches-6.6/073-01-v6.16-net-airoha-npu-Move-memory-allocation-in-airoha_npu_.patch new file mode 100644 index 00000000000000..f0c41d5dc4e919 --- /dev/null +++ b/target/linux/airoha/patches-6.6/073-01-v6.16-net-airoha-npu-Move-memory-allocation-in-airoha_npu_.patch @@ -0,0 +1,196 @@ +From c52918744ee1e49cea86622a2633b9782446428f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 16 May 2025 09:59:59 +0200 +Subject: [PATCH 1/3] net: airoha: npu: Move memory allocation in + airoha_npu_send_msg() caller + +Move ppe_mbox_data struct memory allocation from airoha_npu_send_msg +routine to the caller one. This is a preliminary patch to enable wlan NPU +offloading and flow counter stats support. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-1-06d5fbf28984@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 126 +++++++++++++---------- + 1 file changed, 72 insertions(+), 54 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -124,17 +124,12 @@ static int airoha_npu_send_msg(struct ai + u16 core = 0; /* FIXME */ + u32 val, offset = core << 4; + dma_addr_t dma_addr; +- void *addr; + int ret; + +- addr = kmemdup(p, size, GFP_ATOMIC); +- if (!addr) +- return -ENOMEM; +- +- dma_addr = dma_map_single(npu->dev, addr, size, DMA_TO_DEVICE); ++ dma_addr = dma_map_single(npu->dev, p, size, DMA_TO_DEVICE); + ret = dma_mapping_error(npu->dev, dma_addr); + if (ret) +- goto out; ++ return ret; + + spin_lock_bh(&npu->cores[core].lock); + +@@ -155,8 +150,6 @@ static int airoha_npu_send_msg(struct ai + spin_unlock_bh(&npu->cores[core].lock); + + dma_unmap_single(npu->dev, dma_addr, size, DMA_TO_DEVICE); +-out: +- kfree(addr); + + return ret; + } +@@ -261,76 +254,101 @@ static irqreturn_t airoha_npu_wdt_handle + + static int airoha_npu_ppe_init(struct airoha_npu *npu) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT, +- .init_info = { +- .ppe_type = PPE_TYPE_L2B_IPV4_IPV6, +- .wan_mode = QDMA_WAN_ETHER, +- }, +- }; ++ struct ppe_mbox_data *ppe_data; ++ int err; + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_INIT; ++ ppe_data->init_info.ppe_type = PPE_TYPE_L2B_IPV4_IPV6; ++ ppe_data->init_info.wan_mode = QDMA_WAN_ETHER; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ kfree(ppe_data); ++ ++ return err; + } + + static int airoha_npu_ppe_deinit(struct airoha_npu *npu) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT, +- }; ++ struct ppe_mbox_data *ppe_data; ++ int err; ++ ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); ++ if (!ppe_data) ++ return -ENOMEM; + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_HWNAT_DEINIT; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ kfree(ppe_data); ++ ++ return err; + } + + static int airoha_npu_ppe_flush_sram_entries(struct airoha_npu *npu, + dma_addr_t foe_addr, + int sram_num_entries) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_API, +- .set_info = { +- .func_id = PPE_SRAM_RESET_VAL, +- .data = foe_addr, +- .size = sram_num_entries, +- }, +- }; ++ struct ppe_mbox_data *ppe_data; ++ int err; ++ ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_KERNEL); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_API; ++ ppe_data->set_info.func_id = PPE_SRAM_RESET_VAL; ++ ppe_data->set_info.data = foe_addr; ++ ppe_data->set_info.size = sram_num_entries; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ kfree(ppe_data); + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ return err; + } + + static int airoha_npu_foe_commit_entry(struct airoha_npu *npu, + dma_addr_t foe_addr, + u32 entry_size, u32 hash, bool ppe2) + { +- struct ppe_mbox_data ppe_data = { +- .func_type = NPU_OP_SET, +- .func_id = PPE_FUNC_SET_WAIT_API, +- .set_info = { +- .data = foe_addr, +- .size = entry_size, +- }, +- }; ++ struct ppe_mbox_data *ppe_data; + int err; + +- ppe_data.set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY +- : PPE_SRAM_SET_ENTRY; ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_API; ++ ppe_data->set_info.data = foe_addr; ++ ppe_data->set_info.size = entry_size; ++ ppe_data->set_info.func_id = ppe2 ? PPE2_SRAM_SET_ENTRY ++ : PPE_SRAM_SET_ENTRY; + +- err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); + if (err) +- return err; ++ goto out; + +- ppe_data.set_info.func_id = PPE_SRAM_SET_VAL; +- ppe_data.set_info.data = hash; +- ppe_data.set_info.size = sizeof(u32); ++ ppe_data->set_info.func_id = PPE_SRAM_SET_VAL; ++ ppe_data->set_info.data = hash; ++ ppe_data->set_info.size = sizeof(u32); ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++out: ++ kfree(ppe_data); + +- return airoha_npu_send_msg(npu, NPU_FUNC_PPE, &ppe_data, +- sizeof(struct ppe_mbox_data)); ++ return err; + } + + struct airoha_npu *airoha_npu_get(struct device *dev) diff --git a/target/linux/airoha/patches-6.6/073-02-v6.16-net-airoha-Add-FLOW_CLS_STATS-callback-support.patch b/target/linux/airoha/patches-6.6/073-02-v6.16-net-airoha-Add-FLOW_CLS_STATS-callback-support.patch new file mode 100644 index 00000000000000..584ddb1da8f076 --- /dev/null +++ b/target/linux/airoha/patches-6.6/073-02-v6.16-net-airoha-Add-FLOW_CLS_STATS-callback-support.patch @@ -0,0 +1,633 @@ +From b81e0f2b58be37628b2e12f8dffdd63c84573e75 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 16 May 2025 10:00:00 +0200 +Subject: [PATCH 2/3] net: airoha: Add FLOW_CLS_STATS callback support + +Introduce per-flow stats accounting to the flowtable hw offload in +the airoha_eth driver. Flow stats are split in the PPE and NPU modules: +- PPE: accounts for high 32bit of per-flow stats +- NPU: accounts for low 32bit of per-flow stats + +FLOW_CLS_STATS can be enabled or disabled at compile time. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-2-06d5fbf28984@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/Kconfig | 7 + + drivers/net/ethernet/airoha/airoha_eth.h | 33 +++ + drivers/net/ethernet/airoha/airoha_npu.c | 52 +++- + drivers/net/ethernet/airoha/airoha_npu.h | 4 +- + drivers/net/ethernet/airoha/airoha_ppe.c | 269 ++++++++++++++++-- + .../net/ethernet/airoha/airoha_ppe_debugfs.c | 9 +- + 6 files changed, 354 insertions(+), 20 deletions(-) + +--- a/drivers/net/ethernet/airoha/Kconfig ++++ b/drivers/net/ethernet/airoha/Kconfig +@@ -24,4 +24,11 @@ config NET_AIROHA + This driver supports the gigabit ethernet MACs in the + Airoha SoC family. + ++config NET_AIROHA_FLOW_STATS ++ default y ++ bool "Airoha flow stats" ++ depends on NET_AIROHA && NET_AIROHA_NPU ++ help ++ Enable Aiorha flowtable statistic counters. ++ + endif #NET_VENDOR_AIROHA +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -50,6 +50,14 @@ + #define PPE_NUM 2 + #define PPE1_SRAM_NUM_ENTRIES (8 * 1024) + #define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES) ++#ifdef CONFIG_NET_AIROHA_FLOW_STATS ++#define PPE1_STATS_NUM_ENTRIES (4 * 1024) ++#else ++#define PPE1_STATS_NUM_ENTRIES 0 ++#endif /* CONFIG_NET_AIROHA_FLOW_STATS */ ++#define PPE_STATS_NUM_ENTRIES (2 * PPE1_STATS_NUM_ENTRIES) ++#define PPE1_SRAM_NUM_DATA_ENTRIES (PPE1_SRAM_NUM_ENTRIES - PPE1_STATS_NUM_ENTRIES) ++#define PPE_SRAM_NUM_DATA_ENTRIES (2 * PPE1_SRAM_NUM_DATA_ENTRIES) + #define PPE_DRAM_NUM_ENTRIES (16 * 1024) + #define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES) + #define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1) +@@ -261,6 +269,8 @@ struct airoha_foe_mac_info { + + u16 pppoe_id; + u16 src_mac_lo; ++ ++ u32 meter; + }; + + #define AIROHA_FOE_IB1_UNBIND_PREBIND BIT(24) +@@ -296,6 +306,11 @@ struct airoha_foe_mac_info { + #define AIROHA_FOE_TUNNEL BIT(6) + #define AIROHA_FOE_TUNNEL_ID GENMASK(5, 0) + ++#define AIROHA_FOE_TUNNEL_MTU GENMASK(31, 16) ++#define AIROHA_FOE_ACNT_GRP3 GENMASK(15, 9) ++#define AIROHA_FOE_METER_GRP3 GENMASK(8, 5) ++#define AIROHA_FOE_METER_GRP2 GENMASK(4, 0) ++ + struct airoha_foe_bridge { + u32 dest_mac_hi; + +@@ -379,6 +394,8 @@ struct airoha_foe_ipv6 { + u32 ib2; + + struct airoha_foe_mac_info_common l2; ++ ++ u32 meter; + }; + + struct airoha_foe_entry { +@@ -397,6 +414,16 @@ struct airoha_foe_entry { + }; + }; + ++struct airoha_foe_stats { ++ u32 bytes; ++ u32 packets; ++}; ++ ++struct airoha_foe_stats64 { ++ u64 bytes; ++ u64 packets; ++}; ++ + struct airoha_flow_data { + struct ethhdr eth; + +@@ -447,6 +474,7 @@ struct airoha_flow_table_entry { + struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */ + u32 hash; + ++ struct airoha_foe_stats64 stats; + enum airoha_flow_entry_type type; + + struct rhash_head node; +@@ -523,6 +551,9 @@ struct airoha_ppe { + struct hlist_head *foe_flow; + u16 foe_check_time[PPE_NUM_ENTRIES]; + ++ struct airoha_foe_stats *foe_stats; ++ dma_addr_t foe_stats_dma; ++ + struct dentry *debugfs_dir; + }; + +@@ -582,6 +613,8 @@ int airoha_ppe_init(struct airoha_eth *e + void airoha_ppe_deinit(struct airoha_eth *eth); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); ++void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, ++ struct airoha_foe_stats64 *stats); + + #ifdef CONFIG_DEBUG_FS + int airoha_ppe_debugfs_init(struct airoha_ppe *ppe); +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -12,6 +12,7 @@ + #include + #include + ++#include "airoha_eth.h" + #include "airoha_npu.h" + + #define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" +@@ -72,6 +73,7 @@ enum { + PPE_FUNC_SET_WAIT_HWNAT_INIT, + PPE_FUNC_SET_WAIT_HWNAT_DEINIT, + PPE_FUNC_SET_WAIT_API, ++ PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP, + }; + + enum { +@@ -115,6 +117,10 @@ struct ppe_mbox_data { + u32 size; + u32 data; + } set_info; ++ struct { ++ u32 npu_stats_addr; ++ u32 foe_stats_addr; ++ } stats_info; + }; + }; + +@@ -351,7 +357,40 @@ out: + return err; + } + +-struct airoha_npu *airoha_npu_get(struct device *dev) ++static int airoha_npu_stats_setup(struct airoha_npu *npu, ++ dma_addr_t foe_stats_addr) ++{ ++ int err, size = PPE_STATS_NUM_ENTRIES * sizeof(*npu->stats); ++ struct ppe_mbox_data *ppe_data; ++ ++ if (!size) /* flow stats are disabled */ ++ return 0; ++ ++ ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC); ++ if (!ppe_data) ++ return -ENOMEM; ++ ++ ppe_data->func_type = NPU_OP_SET; ++ ppe_data->func_id = PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP; ++ ppe_data->stats_info.foe_stats_addr = foe_stats_addr; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data, ++ sizeof(*ppe_data)); ++ if (err) ++ goto out; ++ ++ npu->stats = devm_ioremap(npu->dev, ++ ppe_data->stats_info.npu_stats_addr, ++ size); ++ if (!npu->stats) ++ err = -ENOMEM; ++out: ++ kfree(ppe_data); ++ ++ return err; ++} ++ ++struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; + struct device_node *np; +@@ -389,6 +428,17 @@ struct airoha_npu *airoha_npu_get(struct + goto error_module_put; + } + ++ if (stats_addr) { ++ int err; ++ ++ err = airoha_npu_stats_setup(npu, *stats_addr); ++ if (err) { ++ dev_err(dev, "failed to allocate npu stats buffer\n"); ++ npu = ERR_PTR(err); ++ goto error_module_put; ++ } ++ } ++ + return npu; + + error_module_put: +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -17,6 +17,8 @@ struct airoha_npu { + struct work_struct wdt_work; + } cores[NPU_NUM_CORES]; + ++ struct airoha_foe_stats __iomem *stats; ++ + struct { + int (*ppe_init)(struct airoha_npu *npu); + int (*ppe_deinit)(struct airoha_npu *npu); +@@ -30,5 +32,5 @@ struct airoha_npu { + } ops; + }; + +-struct airoha_npu *airoha_npu_get(struct device *dev); ++struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); + void airoha_npu_put(struct airoha_npu *npu); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -102,7 +102,7 @@ static void airoha_ppe_hw_init(struct ai + + if (airoha_ppe2_is_enabled(eth)) { + sram_num_entries = +- PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_ENTRIES); ++ PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES); + airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), + PPE_SRAM_TB_NUM_ENTRY_MASK | + PPE_DRAM_TB_NUM_ENTRY_MASK, +@@ -119,7 +119,7 @@ static void airoha_ppe_hw_init(struct ai + dram_num_entries)); + } else { + sram_num_entries = +- PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_ENTRIES); ++ PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES); + airoha_fe_rmw(eth, REG_PPE_TB_CFG(0), + PPE_SRAM_TB_NUM_ENTRY_MASK | + PPE_DRAM_TB_NUM_ENTRY_MASK, +@@ -417,6 +417,77 @@ static u32 airoha_ppe_foe_get_entry_hash + return hash; + } + ++static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash) ++{ ++ if (!airoha_ppe2_is_enabled(ppe->eth)) ++ return hash; ++ ++ return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES ++ : hash; ++} ++ ++static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe, ++ struct airoha_npu *npu, ++ int index) ++{ ++ memset_io(&npu->stats[index], 0, sizeof(*npu->stats)); ++ memset(&ppe->foe_stats[index], 0, sizeof(*ppe->foe_stats)); ++} ++ ++static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe, ++ struct airoha_npu *npu) ++{ ++ int i; ++ ++ for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++) ++ airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i); ++} ++ ++static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe, ++ struct airoha_npu *npu, ++ struct airoha_foe_entry *hwe, ++ u32 hash) ++{ ++ int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1); ++ u32 index, pse_port, val, *data, *ib2, *meter; ++ u8 nbq; ++ ++ index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); ++ if (index >= PPE_STATS_NUM_ENTRIES) ++ return; ++ ++ if (type == PPE_PKT_TYPE_BRIDGE) { ++ data = &hwe->bridge.data; ++ ib2 = &hwe->bridge.ib2; ++ meter = &hwe->bridge.l2.meter; ++ } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { ++ data = &hwe->ipv6.data; ++ ib2 = &hwe->ipv6.ib2; ++ meter = &hwe->ipv6.meter; ++ } else { ++ data = &hwe->ipv4.data; ++ ib2 = &hwe->ipv4.ib2; ++ meter = &hwe->ipv4.l2.meter; ++ } ++ ++ airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, index); ++ ++ val = FIELD_GET(AIROHA_FOE_CHANNEL | AIROHA_FOE_QID, *data); ++ *data = (*data & ~AIROHA_FOE_ACTDP) | ++ FIELD_PREP(AIROHA_FOE_ACTDP, val); ++ ++ val = *ib2 & (AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | ++ AIROHA_FOE_IB2_PSE_QOS | AIROHA_FOE_IB2_FAST_PATH); ++ *meter |= FIELD_PREP(AIROHA_FOE_TUNNEL_MTU, val); ++ ++ pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); ++ nbq = pse_port == 1 ? 6 : 5; ++ *ib2 &= ~(AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | ++ AIROHA_FOE_IB2_PSE_QOS); ++ *ib2 |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, 6) | ++ FIELD_PREP(AIROHA_FOE_IB2_NBQ, nbq); ++} ++ + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash) + { +@@ -470,6 +541,8 @@ static int airoha_ppe_foe_commit_entry(s + struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); + u32 ts = airoha_ppe_get_timestamp(ppe); + struct airoha_eth *eth = ppe->eth; ++ struct airoha_npu *npu; ++ int err = 0; + + memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1)); + wmb(); +@@ -478,25 +551,28 @@ static int airoha_ppe_foe_commit_entry(s + e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts); + hwe->ib1 = e->ib1; + ++ rcu_read_lock(); ++ ++ npu = rcu_dereference(eth->npu); ++ if (!npu) { ++ err = -ENODEV; ++ goto unlock; ++ } ++ ++ airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); ++ + if (hash < PPE_SRAM_NUM_ENTRIES) { + dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); + bool ppe2 = airoha_ppe2_is_enabled(eth) && + hash >= PPE1_SRAM_NUM_ENTRIES; +- struct airoha_npu *npu; +- int err = -ENODEV; +- +- rcu_read_lock(); +- npu = rcu_dereference(eth->npu); +- if (npu) +- err = npu->ops.ppe_foe_commit_entry(npu, addr, +- sizeof(*hwe), hash, +- ppe2); +- rcu_read_unlock(); + +- return err; ++ err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe), ++ hash, ppe2); + } ++unlock: ++ rcu_read_unlock(); + +- return 0; ++ return err; + } + + static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe, +@@ -582,6 +658,7 @@ airoha_ppe_foe_commit_subflow_entry(stru + l2->common.etype = ETH_P_IPV6; + + hwe.bridge.ib2 = e->data.bridge.ib2; ++ hwe.bridge.data = e->data.bridge.data; + airoha_ppe_foe_commit_entry(ppe, &hwe, hash); + + return 0; +@@ -681,6 +758,98 @@ static int airoha_ppe_foe_flow_commit_en + return 0; + } + ++static int airoha_ppe_get_entry_idle_time(struct airoha_ppe *ppe, u32 ib1) ++{ ++ u32 state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); ++ u32 ts, ts_mask, now = airoha_ppe_get_timestamp(ppe); ++ int idle; ++ ++ if (state == AIROHA_FOE_STATE_BIND) { ++ ts = FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, ib1); ++ ts_mask = AIROHA_FOE_IB1_BIND_TIMESTAMP; ++ } else { ++ ts = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, ib1); ++ now = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, now); ++ ts_mask = AIROHA_FOE_IB1_UNBIND_TIMESTAMP; ++ } ++ idle = now - ts; ++ ++ return idle < 0 ? idle + ts_mask + 1 : idle; ++} ++ ++static void ++airoha_ppe_foe_flow_l2_entry_update(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ int min_idle = airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); ++ struct airoha_flow_table_entry *iter; ++ struct hlist_node *n; ++ ++ lockdep_assert_held(&ppe_lock); ++ ++ hlist_for_each_entry_safe(iter, n, &e->l2_flows, l2_subflow_node) { ++ struct airoha_foe_entry *hwe; ++ u32 ib1, state; ++ int idle; ++ ++ hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); ++ ib1 = READ_ONCE(hwe->ib1); ++ ++ state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); ++ if (state != AIROHA_FOE_STATE_BIND) { ++ iter->hash = 0xffff; ++ airoha_ppe_foe_remove_flow(ppe, iter); ++ continue; ++ } ++ ++ idle = airoha_ppe_get_entry_idle_time(ppe, ib1); ++ if (idle >= min_idle) ++ continue; ++ ++ min_idle = idle; ++ e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP; ++ e->data.ib1 |= ib1 & AIROHA_FOE_IB1_BIND_TIMESTAMP; ++ } ++} ++ ++static void airoha_ppe_foe_flow_entry_update(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ struct airoha_foe_entry *hwe_p, hwe = {}; ++ ++ spin_lock_bh(&ppe_lock); ++ ++ if (e->type == FLOW_TYPE_L2) { ++ airoha_ppe_foe_flow_l2_entry_update(ppe, e); ++ goto unlock; ++ } ++ ++ if (e->hash == 0xffff) ++ goto unlock; ++ ++ hwe_p = airoha_ppe_foe_get_entry(ppe, e->hash); ++ if (!hwe_p) ++ goto unlock; ++ ++ memcpy(&hwe, hwe_p, sizeof(*hwe_p)); ++ if (!airoha_ppe_foe_compare_entry(e, &hwe)) { ++ e->hash = 0xffff; ++ goto unlock; ++ } ++ ++ e->data.ib1 = hwe.ib1; ++unlock: ++ spin_unlock_bh(&ppe_lock); ++} ++ ++static int airoha_ppe_entry_idle_time(struct airoha_ppe *ppe, ++ struct airoha_flow_table_entry *e) ++{ ++ airoha_ppe_foe_flow_entry_update(ppe, e); ++ ++ return airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); ++} ++ + static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, + struct flow_cls_offload *f) + { +@@ -896,6 +1065,60 @@ static int airoha_ppe_flow_offload_destr + return 0; + } + ++void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, ++ struct airoha_foe_stats64 *stats) ++{ ++ u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash); ++ struct airoha_eth *eth = ppe->eth; ++ struct airoha_npu *npu; ++ ++ if (index >= PPE_STATS_NUM_ENTRIES) ++ return; ++ ++ rcu_read_lock(); ++ ++ npu = rcu_dereference(eth->npu); ++ if (npu) { ++ u64 packets = ppe->foe_stats[index].packets; ++ u64 bytes = ppe->foe_stats[index].bytes; ++ struct airoha_foe_stats npu_stats; ++ ++ memcpy_fromio(&npu_stats, &npu->stats[index], ++ sizeof(*npu->stats)); ++ stats->packets = packets << 32 | npu_stats.packets; ++ stats->bytes = bytes << 32 | npu_stats.bytes; ++ } ++ ++ rcu_read_unlock(); ++} ++ ++static int airoha_ppe_flow_offload_stats(struct airoha_gdm_port *port, ++ struct flow_cls_offload *f) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_flow_table_entry *e; ++ u32 idle; ++ ++ e = rhashtable_lookup(ð->flow_table, &f->cookie, ++ airoha_flow_table_params); ++ if (!e) ++ return -ENOENT; ++ ++ idle = airoha_ppe_entry_idle_time(eth->ppe, e); ++ f->stats.lastused = jiffies - idle * HZ; ++ ++ if (e->hash != 0xffff) { ++ struct airoha_foe_stats64 stats = {}; ++ ++ airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats); ++ f->stats.pkts += (stats.packets - e->stats.packets); ++ f->stats.bytes += (stats.bytes - e->stats.bytes); ++ e->stats = stats; ++ } ++ ++ return 0; ++} ++ + static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port, + struct flow_cls_offload *f) + { +@@ -904,6 +1127,8 @@ static int airoha_ppe_flow_offload_cmd(s + return airoha_ppe_flow_offload_replace(port, f); + case FLOW_CLS_DESTROY: + return airoha_ppe_flow_offload_destroy(port, f); ++ case FLOW_CLS_STATS: ++ return airoha_ppe_flow_offload_stats(port, f); + default: + break; + } +@@ -929,11 +1154,12 @@ static int airoha_ppe_flush_sram_entries + + static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) + { +- struct airoha_npu *npu = airoha_npu_get(eth->dev); ++ struct airoha_npu *npu = airoha_npu_get(eth->dev, ++ ð->ppe->foe_stats_dma); + + if (IS_ERR(npu)) { + request_module("airoha-npu"); +- npu = airoha_npu_get(eth->dev); ++ npu = airoha_npu_get(eth->dev, ð->ppe->foe_stats_dma); + } + + return npu; +@@ -956,6 +1182,8 @@ static int airoha_ppe_offload_setup(stru + if (err) + goto error_npu_put; + ++ airoha_ppe_foe_flow_stats_reset(eth->ppe, npu); ++ + rcu_assign_pointer(eth->npu, npu); + synchronize_rcu(); + +@@ -1027,6 +1255,15 @@ int airoha_ppe_init(struct airoha_eth *e + if (!ppe->foe_flow) + return -ENOMEM; + ++ foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats); ++ if (foe_size) { ++ ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size, ++ &ppe->foe_stats_dma, ++ GFP_KERNEL); ++ if (!ppe->foe_stats) ++ return -ENOMEM; ++ } ++ + err = rhashtable_init(ð->flow_table, &airoha_flow_table_params); + if (err) + return err; +--- a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c +@@ -61,6 +61,7 @@ static int airoha_ppe_debugfs_foe_show(s + u16 *src_port = NULL, *dest_port = NULL; + struct airoha_foe_mac_info_common *l2; + unsigned char h_source[ETH_ALEN] = {}; ++ struct airoha_foe_stats64 stats = {}; + unsigned char h_dest[ETH_ALEN]; + struct airoha_foe_entry *hwe; + u32 type, state, ib2, data; +@@ -144,14 +145,18 @@ static int airoha_ppe_debugfs_foe_show(s + cpu_to_be16(hwe->ipv4.l2.src_mac_lo); + } + ++ airoha_ppe_foe_entry_get_stats(ppe, i, &stats); ++ + *((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi); + *((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo); + *((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi); + + seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x" +- " vlan=%d,%d ib1=%08x ib2=%08x\n", ++ " vlan=%d,%d ib1=%08x ib2=%08x" ++ " packets=%llu bytes=%llu\n", + h_source, h_dest, l2->etype, data, +- l2->vlan1, l2->vlan2, hwe->ib1, ib2); ++ l2->vlan1, l2->vlan2, hwe->ib1, ib2, ++ stats.packets, stats.bytes); + } + + return 0; diff --git a/target/linux/airoha/patches-6.6/073-03-v6.16-net-airoha-ppe-Disable-packet-keepalive.patch b/target/linux/airoha/patches-6.6/073-03-v6.16-net-airoha-ppe-Disable-packet-keepalive.patch new file mode 100644 index 00000000000000..30c74ddec64eeb --- /dev/null +++ b/target/linux/airoha/patches-6.6/073-03-v6.16-net-airoha-ppe-Disable-packet-keepalive.patch @@ -0,0 +1,28 @@ +From a98326c151ea3d92e9496858cc2dacccd0870941 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 16 May 2025 10:00:01 +0200 +Subject: [PATCH 3/3] net: airoha: ppe: Disable packet keepalive + +Since netfilter flowtable entries are now refreshed by flow-stats +polling, we can disable hw packet keepalive used to periodically send +packets belonging to offloaded flows to the kernel in order to refresh +flowtable entries. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-3-06d5fbf28984@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -84,6 +84,7 @@ static void airoha_ppe_hw_init(struct ai + + airoha_fe_rmw(eth, REG_PPE_TB_CFG(i), + PPE_TB_CFG_SEARCH_MISS_MASK | ++ PPE_TB_CFG_KEEPALIVE_MASK | + PPE_TB_ENTRY_SIZE_MASK, + FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) | + FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0)); diff --git a/target/linux/airoha/patches-6.6/074-01-v6.16-net-airoha-Do-not-store-hfwd-references-in-airoha_qd.patch b/target/linux/airoha/patches-6.6/074-01-v6.16-net-airoha-Do-not-store-hfwd-references-in-airoha_qd.patch new file mode 100644 index 00000000000000..81d708f8ce8bc5 --- /dev/null +++ b/target/linux/airoha/patches-6.6/074-01-v6.16-net-airoha-Do-not-store-hfwd-references-in-airoha_qd.patch @@ -0,0 +1,57 @@ +From 09aa788f98da3e2f41ce158cc691d6d52e808bc9 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 21 May 2025 09:16:37 +0200 +Subject: [PATCH 1/3] net: airoha: Do not store hfwd references in airoha_qdma + struct + +Since hfwd descriptor and buffer queues are allocated via +dmam_alloc_coherent() we do not need to store their references +in airoha_qdma struct. This patch does not introduce any logical changes, +just code clean-up. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-2-a6e9b085b4f0@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 8 ++------ + drivers/net/ethernet/airoha/airoha_eth.h | 6 ------ + 2 files changed, 2 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1078,17 +1078,13 @@ static int airoha_qdma_init_hfwd_queues( + int size; + + size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); +- qdma->hfwd.desc = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!qdma->hfwd.desc) ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + + size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; +- qdma->hfwd.q = dmam_alloc_coherent(eth->dev, size, &dma_addr, +- GFP_KERNEL); +- if (!qdma->hfwd.q) ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -513,12 +513,6 @@ struct airoha_qdma { + + struct airoha_queue q_tx[AIROHA_NUM_TX_RING]; + struct airoha_queue q_rx[AIROHA_NUM_RX_RING]; +- +- /* descriptor and packet buffers for qdma hw forward */ +- struct { +- void *desc; +- void *q; +- } hfwd; + }; + + struct airoha_gdm_port { diff --git a/target/linux/airoha/patches-6.6/074-02-v6.16-net-airoha-Add-the-capability-to-allocate-hwfd-buffe.patch b/target/linux/airoha/patches-6.6/074-02-v6.16-net-airoha-Add-the-capability-to-allocate-hwfd-buffe.patch new file mode 100644 index 00000000000000..d6f3e94f099422 --- /dev/null +++ b/target/linux/airoha/patches-6.6/074-02-v6.16-net-airoha-Add-the-capability-to-allocate-hwfd-buffe.patch @@ -0,0 +1,79 @@ +From 3a1ce9e3d01bbf3912c3e3f81cb554d558eb715b Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 21 May 2025 09:16:38 +0200 +Subject: [PATCH 2/3] net: airoha: Add the capability to allocate hwfd buffers + via reserved-memory + +In some configurations QDMA blocks require a contiguous block of +system memory for hwfd buffers queue. Introduce the capability to allocate +hw buffers forwarding queue via the reserved-memory DTS property instead of +running dmam_alloc_coherent(). + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-3-a6e9b085b4f0@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 33 +++++++++++++++++++++--- + 1 file changed, 30 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -5,6 +5,7 @@ + */ + #include + #include ++#include + #include + #include + #include +@@ -1073,9 +1074,11 @@ static void airoha_qdma_cleanup_tx_queue + static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) + { + struct airoha_eth *eth = qdma->eth; ++ int id = qdma - ð->qdma[0]; + dma_addr_t dma_addr; ++ const char *name; ++ int size, index; + u32 status; +- int size; + + size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) +@@ -1083,10 +1086,34 @@ static int airoha_qdma_init_hfwd_queues( + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + +- size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; +- if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) ++ name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); ++ if (!name) + return -ENOMEM; + ++ index = of_property_match_string(eth->dev->of_node, ++ "memory-region-names", name); ++ if (index >= 0) { ++ struct reserved_mem *rmem; ++ struct device_node *np; ++ ++ /* Consume reserved memory for hw forwarding buffers queue if ++ * available in the DTS ++ */ ++ np = of_parse_phandle(eth->dev->of_node, "memory-region", ++ index); ++ if (!np) ++ return -ENODEV; ++ ++ rmem = of_reserved_mem_lookup(np); ++ of_node_put(np); ++ dma_addr = rmem->base; ++ } else { ++ size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, ++ GFP_KERNEL)) ++ return -ENOMEM; ++ } ++ + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); + + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, diff --git a/target/linux/airoha/patches-6.6/074-03-v6.16-net-airoha-Add-the-capability-to-allocate-hfwd-descr.patch b/target/linux/airoha/patches-6.6/074-03-v6.16-net-airoha-Add-the-capability-to-allocate-hfwd-descr.patch new file mode 100644 index 00000000000000..a380adf3b73a87 --- /dev/null +++ b/target/linux/airoha/patches-6.6/074-03-v6.16-net-airoha-Add-the-capability-to-allocate-hfwd-descr.patch @@ -0,0 +1,82 @@ +From c683e378c0907e66cee939145edf936c254ff1e3 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 21 May 2025 09:16:39 +0200 +Subject: [PATCH 3/3] net: airoha: Add the capability to allocate hfwd + descriptors in SRAM + +In order to improve packet processing and packet forwarding +performances, EN7581 SoC supports consuming SRAM instead of DRAM for +hw forwarding descriptors queue. +For downlink hw accelerated traffic request to consume SRAM memory +for hw forwarding descriptors queue. + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250521-airopha-desc-sram-v3-4-a6e9b085b4f0@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 11 +---------- + drivers/net/ethernet/airoha/airoha_eth.h | 9 +++++++++ + drivers/net/ethernet/airoha/airoha_ppe.c | 6 ++++++ + 3 files changed, 16 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -71,15 +71,6 @@ static void airoha_qdma_irq_disable(stru + airoha_qdma_set_irqmask(irq_bank, index, mask, 0); + } + +-static bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) +-{ +- /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. +- * GDM{2,3,4} can be used as wan port connected to an external +- * phy module. +- */ +- return port->id == 1; +-} +- + static void airoha_set_macaddr(struct airoha_gdm_port *port, const u8 *addr) + { + struct airoha_eth *eth = port->qdma->eth; +@@ -1125,7 +1116,7 @@ static int airoha_qdma_init_hfwd_queues( + LMGR_INIT_START | LMGR_SRAM_MODE_MASK | + HW_FWD_DESC_NUM_MASK, + FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | +- LMGR_INIT_START); ++ LMGR_INIT_START | LMGR_SRAM_MODE_MASK); + + return read_poll_timeout(airoha_qdma_rr, status, + !(status & LMGR_INIT_START), USEC_PER_MSEC, +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -597,6 +597,15 @@ u32 airoha_rmw(void __iomem *base, u32 o + #define airoha_qdma_clear(qdma, offset, val) \ + airoha_rmw((qdma)->regs, (offset), (val), 0) + ++static inline bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port) ++{ ++ /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. ++ * GDM{2,3,4} can be used as wan port connected to an external ++ * phy module. ++ */ ++ return port->id == 1; ++} ++ + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -251,6 +251,12 @@ static int airoha_ppe_foe_entry_prepare( + else + pse_port = 2; /* uplink relies on GDM2 loopback */ + val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port); ++ ++ /* For downlink traffic consume SRAM memory for hw forwarding ++ * descriptors queue. ++ */ ++ if (airhoa_is_lan_gdm_port(port)) ++ val |= AIROHA_FOE_IB2_FAST_PATH; + } + + if (is_multicast_ether_addr(data->eth.h_dest)) diff --git a/target/linux/airoha/patches-6.6/075-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_allo.patch b/target/linux/airoha/patches-6.6/075-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_allo.patch new file mode 100644 index 00000000000000..8606cfff583106 --- /dev/null +++ b/target/linux/airoha/patches-6.6/075-v6.16-net-airoha-Fix-an-error-handling-path-in-airoha_allo.patch @@ -0,0 +1,42 @@ +From c59783780c8ad66f6076a9a7c74df3e006e29519 Mon Sep 17 00:00:00 2001 +From: Christophe JAILLET +Date: Sat, 24 May 2025 09:29:11 +0200 +Subject: [PATCH] net: airoha: Fix an error handling path in + airoha_alloc_gdm_port() + +If register_netdev() fails, the error handling path of the probe will not +free the memory allocated by the previous airoha_metadata_dst_alloc() call +because port->dev->reg_state will not be NETREG_REGISTERED. + +So, an explicit airoha_metadata_dst_free() call is needed in this case to +avoid a memory leak. + +Fixes: af3cf757d5c9 ("net: airoha: Move DSA tag in DMA descriptor") +Signed-off-by: Christophe JAILLET +Acked-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/1b94b91345017429ed653e2f05d25620dc2823f9.1746715755.git.christophe.jaillet@wanadoo.fr +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2881,7 +2881,15 @@ static int airoha_alloc_gdm_port(struct + if (err) + return err; + +- return register_netdev(dev); ++ err = register_netdev(dev); ++ if (err) ++ goto free_metadata_dst; ++ ++ return 0; ++ ++free_metadata_dst: ++ airoha_metadata_dst_free(port); ++ return err; + } + + static int airoha_probe(struct platform_device *pdev) diff --git a/target/linux/airoha/patches-6.6/076-01-v6.16-net-airoha-Initialize-PPE-UPDMEM-source-mac-table.patch b/target/linux/airoha/patches-6.6/076-01-v6.16-net-airoha-Initialize-PPE-UPDMEM-source-mac-table.patch new file mode 100644 index 00000000000000..334661dd964fd0 --- /dev/null +++ b/target/linux/airoha/patches-6.6/076-01-v6.16-net-airoha-Initialize-PPE-UPDMEM-source-mac-table.patch @@ -0,0 +1,122 @@ +From a869d3a5eb011a9cf9bd864f31f5cf27362de8c7 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 2 Jun 2025 12:55:37 +0200 +Subject: [PATCH 1/3] net: airoha: Initialize PPE UPDMEM source-mac table + +UPDMEM source-mac table is a key-value map used to store devices mac +addresses according to the port identifier. UPDMEM source mac table is +used during IPv6 traffic hw acceleration since PPE entries, for space +constraints, do not contain the full source mac address but just the +identifier in the UPDMEM source-mac table. +Configure UPDMEM source-mac table with device mac addresses and set +the source-mac ID field for PPE IPv6 entries in order to select the +proper device mac address as source mac for L3 IPv6 hw accelerated traffic. + +Fixes: 00a7678310fe ("net: airoha: Introduce flowtable offload support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250602-airoha-flowtable-ipv6-fix-v2-1-3287f8b55214@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.c | 2 ++ + drivers/net/ethernet/airoha/airoha_eth.h | 1 + + drivers/net/ethernet/airoha/airoha_ppe.c | 26 ++++++++++++++++++++++- + drivers/net/ethernet/airoha/airoha_regs.h | 10 +++++++++ + 4 files changed, 38 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -84,6 +84,8 @@ static void airoha_set_macaddr(struct ai + val = (addr[3] << 16) | (addr[4] << 8) | addr[5]; + airoha_fe_wr(eth, REG_FE_MAC_LMIN(reg), val); + airoha_fe_wr(eth, REG_FE_MAC_LMAX(reg), val); ++ ++ airoha_ppe_init_upd_mem(port); + } + + static void airoha_set_gdm_port_fwd_cfg(struct airoha_eth *eth, u32 addr, +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -614,6 +614,7 @@ void airoha_ppe_check_skb(struct airoha_ + int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); ++void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port); + struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, + u32 hash); + void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash, +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -223,6 +223,7 @@ static int airoha_ppe_foe_entry_prepare( + int dsa_port = airoha_get_dsa_port(&dev); + struct airoha_foe_mac_info_common *l2; + u32 qdata, ports_pad, val; ++ u8 smac_id = 0xf; + + memset(hwe, 0, sizeof(*hwe)); + +@@ -257,6 +258,8 @@ static int airoha_ppe_foe_entry_prepare( + */ + if (airhoa_is_lan_gdm_port(port)) + val |= AIROHA_FOE_IB2_FAST_PATH; ++ ++ smac_id = port->id; + } + + if (is_multicast_ether_addr(data->eth.h_dest)) +@@ -291,7 +294,7 @@ static int airoha_ppe_foe_entry_prepare( + hwe->ipv4.l2.src_mac_lo = + get_unaligned_be16(data->eth.h_source + 4); + } else { +- l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, 0xf); ++ l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, smac_id); + } + + if (data->vlan.num) { +@@ -1238,6 +1241,27 @@ void airoha_ppe_check_skb(struct airoha_ + airoha_ppe_foe_insert_entry(ppe, skb, hash); + } + ++void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) ++{ ++ struct airoha_eth *eth = port->qdma->eth; ++ struct net_device *dev = port->dev; ++ const u8 *addr = dev->dev_addr; ++ u32 val; ++ ++ val = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; ++ airoha_fe_wr(eth, REG_UPDMEM_DATA(0), val); ++ airoha_fe_wr(eth, REG_UPDMEM_CTRL(0), ++ FIELD_PREP(PPE_UPDMEM_ADDR_MASK, port->id) | ++ PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); ++ ++ val = (addr[0] << 8) | addr[1]; ++ airoha_fe_wr(eth, REG_UPDMEM_DATA(0), val); ++ airoha_fe_wr(eth, REG_UPDMEM_CTRL(0), ++ FIELD_PREP(PPE_UPDMEM_ADDR_MASK, port->id) | ++ FIELD_PREP(PPE_UPDMEM_OFFSET_MASK, 1) | ++ PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); ++} ++ + int airoha_ppe_init(struct airoha_eth *eth) + { + struct airoha_ppe *ppe; +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -313,6 +313,16 @@ + #define REG_PPE_RAM_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x320) + #define REG_PPE_RAM_ENTRY(_m, _n) (REG_PPE_RAM_BASE(_m) + ((_n) << 2)) + ++#define REG_UPDMEM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x370) ++#define PPE_UPDMEM_ACK_MASK BIT(31) ++#define PPE_UPDMEM_ADDR_MASK GENMASK(11, 8) ++#define PPE_UPDMEM_OFFSET_MASK GENMASK(7, 4) ++#define PPE_UPDMEM_SEL_MASK GENMASK(3, 2) ++#define PPE_UPDMEM_WR_MASK BIT(1) ++#define PPE_UPDMEM_REQ_MASK BIT(0) ++ ++#define REG_UPDMEM_DATA(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x374) ++ + #define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280) + #define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284) + #define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288) diff --git a/target/linux/airoha/patches-6.6/076-02-v6.16-net-airoha-Fix-IPv6-hw-acceleration-in-bridge-mode.patch b/target/linux/airoha/patches-6.6/076-02-v6.16-net-airoha-Fix-IPv6-hw-acceleration-in-bridge-mode.patch new file mode 100644 index 00000000000000..faa7669e0d474c --- /dev/null +++ b/target/linux/airoha/patches-6.6/076-02-v6.16-net-airoha-Fix-IPv6-hw-acceleration-in-bridge-mode.patch @@ -0,0 +1,64 @@ +From 504a577c9b000f9e0e99e1b28616fb4eb369e1ef Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 2 Jun 2025 12:55:38 +0200 +Subject: [PATCH 2/3] net: airoha: Fix IPv6 hw acceleration in bridge mode + +ib2 and airoha_foe_mac_info_common have not the same offsets in +airoha_foe_bridge and airoha_foe_ipv6 structures. Current codebase does +not accelerate IPv6 traffic in bridge mode since ib2 and l2 info are not +set properly copying airoha_foe_bridge struct into airoha_foe_ipv6 one +in airoha_ppe_foe_commit_subflow_entry routine. +Fix IPv6 hw acceleration in bridge mode resolving ib2 and +airoha_foe_mac_info_common overwrite in +airoha_ppe_foe_commit_subflow_entry() and configuring them with proper +values. + +Fixes: cd53f622611f ("net: airoha: Add L2 hw acceleration support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250602-airoha-flowtable-ipv6-fix-v2-2-3287f8b55214@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 23 ++++++++++++----------- + 1 file changed, 12 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -639,7 +639,6 @@ airoha_ppe_foe_commit_subflow_entry(stru + u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; + struct airoha_foe_entry *hwe_p, hwe; + struct airoha_flow_table_entry *f; +- struct airoha_foe_mac_info *l2; + int type; + + hwe_p = airoha_ppe_foe_get_entry(ppe, hash); +@@ -656,18 +655,20 @@ airoha_ppe_foe_commit_subflow_entry(stru + + memcpy(&hwe, hwe_p, sizeof(*hwe_p)); + hwe.ib1 = (hwe.ib1 & mask) | (e->data.ib1 & ~mask); +- l2 = &hwe.bridge.l2; +- memcpy(l2, &e->data.bridge.l2, sizeof(*l2)); + + type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe.ib1); +- if (type == PPE_PKT_TYPE_IPV4_HNAPT) +- memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, +- sizeof(hwe.ipv4.new_tuple)); +- else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T && +- l2->common.etype == ETH_P_IP) +- l2->common.etype = ETH_P_IPV6; ++ if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { ++ memcpy(&hwe.ipv6.l2, &e->data.bridge.l2, sizeof(hwe.ipv6.l2)); ++ hwe.ipv6.ib2 = e->data.bridge.ib2; ++ } else { ++ memcpy(&hwe.bridge.l2, &e->data.bridge.l2, ++ sizeof(hwe.bridge.l2)); ++ hwe.bridge.ib2 = e->data.bridge.ib2; ++ if (type == PPE_PKT_TYPE_IPV4_HNAPT) ++ memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, ++ sizeof(hwe.ipv4.new_tuple)); ++ } + +- hwe.bridge.ib2 = e->data.bridge.ib2; + hwe.bridge.data = e->data.bridge.data; + airoha_ppe_foe_commit_entry(ppe, &hwe, hash); + diff --git a/target/linux/airoha/patches-6.6/076-03-v6.16-net-airoha-Fix-smac_id-configuration-in-bridge-mode.patch b/target/linux/airoha/patches-6.6/076-03-v6.16-net-airoha-Fix-smac_id-configuration-in-bridge-mode.patch new file mode 100644 index 00000000000000..f790d9d148c43c --- /dev/null +++ b/target/linux/airoha/patches-6.6/076-03-v6.16-net-airoha-Fix-smac_id-configuration-in-bridge-mode.patch @@ -0,0 +1,32 @@ +From c86fac5365d3a068422beeb508f2741f1a2d734d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 2 Jun 2025 12:55:39 +0200 +Subject: [PATCH 3/3] net: airoha: Fix smac_id configuration in bridge mode + +Set PPE entry smac_id field to 0xf in airoha_ppe_foe_commit_subflow_entry +routine for IPv6 traffic in order to instruct the hw to keep original +source mac address for IPv6 hw accelerated traffic in bridge mode. + +Fixes: cd53f622611f ("net: airoha: Add L2 hw acceleration support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250602-airoha-flowtable-ipv6-fix-v2-3-3287f8b55214@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -660,6 +660,11 @@ airoha_ppe_foe_commit_subflow_entry(stru + if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { + memcpy(&hwe.ipv6.l2, &e->data.bridge.l2, sizeof(hwe.ipv6.l2)); + hwe.ipv6.ib2 = e->data.bridge.ib2; ++ /* setting smac_id to 0xf instruct the hw to keep original ++ * source mac address ++ */ ++ hwe.ipv6.l2.src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, ++ 0xf); + } else { + memcpy(&hwe.bridge.l2, &e->data.bridge.l2, + sizeof(hwe.bridge.l2)); diff --git a/target/linux/airoha/patches-6.6/077-v6.17-net-airoha-Add-PPPoE-offload-support.patch b/target/linux/airoha/patches-6.6/077-v6.17-net-airoha-Add-PPPoE-offload-support.patch new file mode 100644 index 00000000000000..6245f0d218d787 --- /dev/null +++ b/target/linux/airoha/patches-6.6/077-v6.17-net-airoha-Add-PPPoE-offload-support.patch @@ -0,0 +1,87 @@ +From 0097c4195b1d0ca57d15979626c769c74747b5a0 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 9 Jun 2025 22:28:40 +0200 +Subject: [PATCH] net: airoha: Add PPPoE offload support + +Introduce flowtable hw acceleration for PPPoE traffic. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250609-b4-airoha-flowtable-pppoe-v1-1-1520fa7711b4@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 31 ++++++++++++++++++------ + 1 file changed, 23 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -232,6 +232,7 @@ static int airoha_ppe_foe_entry_prepare( + FIELD_PREP(AIROHA_FOE_IB1_BIND_UDP, l4proto == IPPROTO_UDP) | + FIELD_PREP(AIROHA_FOE_IB1_BIND_VLAN_LAYER, data->vlan.num) | + FIELD_PREP(AIROHA_FOE_IB1_BIND_VPM, data->vlan.num) | ++ FIELD_PREP(AIROHA_FOE_IB1_BIND_PPPOE, data->pppoe.num) | + AIROHA_FOE_IB1_BIND_TTL; + hwe->ib1 = val; + +@@ -281,33 +282,42 @@ static int airoha_ppe_foe_entry_prepare( + hwe->ipv6.data = qdata; + hwe->ipv6.ib2 = val; + l2 = &hwe->ipv6.l2; ++ l2->etype = ETH_P_IPV6; + } else { + hwe->ipv4.data = qdata; + hwe->ipv4.ib2 = val; + l2 = &hwe->ipv4.l2.common; ++ l2->etype = ETH_P_IP; + } + + l2->dest_mac_hi = get_unaligned_be32(data->eth.h_dest); + l2->dest_mac_lo = get_unaligned_be16(data->eth.h_dest + 4); + if (type <= PPE_PKT_TYPE_IPV4_DSLITE) { ++ struct airoha_foe_mac_info *mac_info; ++ + l2->src_mac_hi = get_unaligned_be32(data->eth.h_source); + hwe->ipv4.l2.src_mac_lo = + get_unaligned_be16(data->eth.h_source + 4); ++ ++ mac_info = (struct airoha_foe_mac_info *)l2; ++ mac_info->pppoe_id = data->pppoe.sid; + } else { +- l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, smac_id); ++ l2->src_mac_hi = FIELD_PREP(AIROHA_FOE_MAC_SMAC_ID, smac_id) | ++ FIELD_PREP(AIROHA_FOE_MAC_PPPOE_ID, ++ data->pppoe.sid); + } + + if (data->vlan.num) { +- l2->etype = dsa_port >= 0 ? BIT(dsa_port) : 0; + l2->vlan1 = data->vlan.hdr[0].id; + if (data->vlan.num == 2) + l2->vlan2 = data->vlan.hdr[1].id; +- } else if (dsa_port >= 0) { +- l2->etype = BIT(15) | BIT(dsa_port); +- } else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) { +- l2->etype = ETH_P_IPV6; +- } else { +- l2->etype = ETH_P_IP; ++ } ++ ++ if (dsa_port >= 0) { ++ l2->etype = BIT(dsa_port); ++ l2->etype |= !data->vlan.num ? BIT(15) : 0; ++ } else if (data->pppoe.num) { ++ l2->etype = ETH_P_PPP_SES; + } + + return 0; +@@ -957,6 +967,11 @@ static int airoha_ppe_flow_offload_repla + case FLOW_ACTION_VLAN_POP: + break; + case FLOW_ACTION_PPPOE_PUSH: ++ if (data.pppoe.num == 1 || data.vlan.num == 2) ++ return -EOPNOTSUPP; ++ ++ data.pppoe.sid = act->pppoe.sid; ++ data.pppoe.num++; + break; + default: + return -EOPNOTSUPP; diff --git a/target/linux/airoha/patches-6.6/078-v6.16-net-airoha-Enable-RX-queues-16-31.patch b/target/linux/airoha/patches-6.6/078-v6.16-net-airoha-Enable-RX-queues-16-31.patch new file mode 100644 index 00000000000000..1550c5926127fb --- /dev/null +++ b/target/linux/airoha/patches-6.6/078-v6.16-net-airoha-Enable-RX-queues-16-31.patch @@ -0,0 +1,28 @@ +From f478d68b653323b691280b40fbd3b8ca1ac75aa2 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 9 Jun 2025 22:40:35 +0200 +Subject: [PATCH] net: airoha: Enable RX queues 16-31 + +Fix RX_DONE_INT_MASK definition in order to enable RX queues 16-31. + +Fixes: f252493e18353 ("net: airoha: Enable multiple IRQ lines support in airoha_eth driver.") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250609-aioha-fix-rx-queue-mask-v1-1-f33706a06fa2@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_regs.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_regs.h ++++ b/drivers/net/ethernet/airoha/airoha_regs.h +@@ -614,8 +614,9 @@ + RX19_DONE_INT_MASK | RX18_DONE_INT_MASK | \ + RX17_DONE_INT_MASK | RX16_DONE_INT_MASK) + +-#define RX_DONE_INT_MASK (RX_DONE_HIGH_INT_MASK | RX_DONE_LOW_INT_MASK) + #define RX_DONE_HIGH_OFFSET fls(RX_DONE_HIGH_INT_MASK) ++#define RX_DONE_INT_MASK \ ++ ((RX_DONE_HIGH_INT_MASK << RX_DONE_HIGH_OFFSET) | RX_DONE_LOW_INT_MASK) + + #define INT_RX2_MASK(_n) \ + ((RX_NO_CPU_DSCP_HIGH_INT_MASK & (_n)) | \ diff --git a/target/linux/airoha/patches-6.6/079-v6.16-net-airoha-Always-check-return-value-from-airoha_ppe.patch b/target/linux/airoha/patches-6.6/079-v6.16-net-airoha-Always-check-return-value-from-airoha_ppe.patch new file mode 100644 index 00000000000000..551e8e3c9d821d --- /dev/null +++ b/target/linux/airoha/patches-6.6/079-v6.16-net-airoha-Always-check-return-value-from-airoha_ppe.patch @@ -0,0 +1,32 @@ +From 78bd03ee1f20a267d2c218884b66041b3508ac9c Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Wed, 18 Jun 2025 09:37:40 +0200 +Subject: [PATCH] net: airoha: Always check return value from + airoha_ppe_foe_get_entry() + +airoha_ppe_foe_get_entry routine can return NULL, so check the returned +pointer is not NULL in airoha_ppe_foe_flow_l2_entry_update() + +Fixes: b81e0f2b58be3 ("net: airoha: Add FLOW_CLS_STATS callback support") +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250618-check-ret-from-airoha_ppe_foe_get_entry-v2-1-068dcea3cc66@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -819,8 +819,10 @@ airoha_ppe_foe_flow_l2_entry_update(stru + int idle; + + hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); +- ib1 = READ_ONCE(hwe->ib1); ++ if (!hwe) ++ continue; + ++ ib1 = READ_ONCE(hwe->ib1); + state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1); + if (state != AIROHA_FOE_STATE_BIND) { + iter->hash = 0xffff; diff --git a/target/linux/airoha/patches-6.6/080-01-v6.16-net-airoha-Compute-number-of-descriptors-according-t.patch b/target/linux/airoha/patches-6.6/080-01-v6.16-net-airoha-Compute-number-of-descriptors-according-t.patch new file mode 100644 index 00000000000000..9d419c33db03a7 --- /dev/null +++ b/target/linux/airoha/patches-6.6/080-01-v6.16-net-airoha-Compute-number-of-descriptors-according-t.patch @@ -0,0 +1,77 @@ +From edf8afeecfbb0b8c1a2edb8c8892d2f759d35321 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 19 Jun 2025 09:07:24 +0200 +Subject: [PATCH 1/2] net: airoha: Compute number of descriptors according to + reserved memory size + +In order to not exceed the reserved memory size for hwfd buffers, +compute the number of hwfd buffers/descriptors according to the +reserved memory size and the size of each hwfd buffer (2KB). + +Fixes: 3a1ce9e3d01b ("net: airoha: Add the capability to allocate hwfd buffers via reserved-memory") +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250619-airoha-hw-num-desc-v4-1-49600a9b319a@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1066,19 +1066,13 @@ static void airoha_qdma_cleanup_tx_queue + + static int airoha_qdma_init_hfwd_queues(struct airoha_qdma *qdma) + { ++ int size, index, num_desc = HW_DSCP_NUM; + struct airoha_eth *eth = qdma->eth; + int id = qdma - ð->qdma[0]; + dma_addr_t dma_addr; + const char *name; +- int size, index; + u32 status; + +- size = HW_DSCP_NUM * sizeof(struct airoha_qdma_fwd_desc); +- if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) +- return -ENOMEM; +- +- airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); +- + name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); + if (!name) + return -ENOMEM; +@@ -1100,8 +1094,12 @@ static int airoha_qdma_init_hfwd_queues( + rmem = of_reserved_mem_lookup(np); + of_node_put(np); + dma_addr = rmem->base; ++ /* Compute the number of hw descriptors according to the ++ * reserved memory size and the payload buffer size ++ */ ++ num_desc = rmem->size / AIROHA_MAX_PACKET_SIZE; + } else { +- size = AIROHA_MAX_PACKET_SIZE * HW_DSCP_NUM; ++ size = AIROHA_MAX_PACKET_SIZE * num_desc; + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, + GFP_KERNEL)) + return -ENOMEM; +@@ -1109,6 +1107,11 @@ static int airoha_qdma_init_hfwd_queues( + + airoha_qdma_wr(qdma, REG_FWD_BUF_BASE, dma_addr); + ++ size = num_desc * sizeof(struct airoha_qdma_fwd_desc); ++ if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, GFP_KERNEL)) ++ return -ENOMEM; ++ ++ airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, + HW_FWD_DSCP_PAYLOAD_SIZE_MASK, + FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); +@@ -1117,7 +1120,7 @@ static int airoha_qdma_init_hfwd_queues( + airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, + LMGR_INIT_START | LMGR_SRAM_MODE_MASK | + HW_FWD_DESC_NUM_MASK, +- FIELD_PREP(HW_FWD_DESC_NUM_MASK, HW_DSCP_NUM) | ++ FIELD_PREP(HW_FWD_DESC_NUM_MASK, num_desc) | + LMGR_INIT_START | LMGR_SRAM_MODE_MASK); + + return read_poll_timeout(airoha_qdma_rr, status, diff --git a/target/linux/airoha/patches-6.6/080-02-v6.16-net-airoha-Differentiate-hwfd-buffer-size-for-QDMA0-.patch b/target/linux/airoha/patches-6.6/080-02-v6.16-net-airoha-Differentiate-hwfd-buffer-size-for-QDMA0-.patch new file mode 100644 index 00000000000000..d47fc434132f3a --- /dev/null +++ b/target/linux/airoha/patches-6.6/080-02-v6.16-net-airoha-Differentiate-hwfd-buffer-size-for-QDMA0-.patch @@ -0,0 +1,64 @@ +From 7b46bdaec00a675f6fac9d0b01a2105b5746ebe9 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 19 Jun 2025 09:07:25 +0200 +Subject: [PATCH 2/2] net: airoha: Differentiate hwfd buffer size for QDMA0 and + QDMA1 + +EN7581 SoC allows configuring the size and the number of buffers in +hwfd payload queue for both QDMA0 and QDMA1. +In order to reduce the required DRAM used for hwfd buffers queues and +decrease the memory footprint, differentiate hwfd buffer size for QDMA0 +and QDMA1 and reduce hwfd buffer size to 1KB for QDMA1 (WAN) while +maintaining 2KB for QDMA0 (LAN). + +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250619-airoha-hw-num-desc-v4-2-49600a9b319a@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -1069,14 +1069,15 @@ static int airoha_qdma_init_hfwd_queues( + int size, index, num_desc = HW_DSCP_NUM; + struct airoha_eth *eth = qdma->eth; + int id = qdma - ð->qdma[0]; ++ u32 status, buf_size; + dma_addr_t dma_addr; + const char *name; +- u32 status; + + name = devm_kasprintf(eth->dev, GFP_KERNEL, "qdma%d-buf", id); + if (!name) + return -ENOMEM; + ++ buf_size = id ? AIROHA_MAX_PACKET_SIZE / 2 : AIROHA_MAX_PACKET_SIZE; + index = of_property_match_string(eth->dev->of_node, + "memory-region-names", name); + if (index >= 0) { +@@ -1097,9 +1098,9 @@ static int airoha_qdma_init_hfwd_queues( + /* Compute the number of hw descriptors according to the + * reserved memory size and the payload buffer size + */ +- num_desc = rmem->size / AIROHA_MAX_PACKET_SIZE; ++ num_desc = div_u64(rmem->size, buf_size); + } else { +- size = AIROHA_MAX_PACKET_SIZE * num_desc; ++ size = buf_size * num_desc; + if (!dmam_alloc_coherent(eth->dev, size, &dma_addr, + GFP_KERNEL)) + return -ENOMEM; +@@ -1112,9 +1113,10 @@ static int airoha_qdma_init_hfwd_queues( + return -ENOMEM; + + airoha_qdma_wr(qdma, REG_FWD_DSCP_BASE, dma_addr); ++ /* QDMA0: 2KB. QDMA1: 1KB */ + airoha_qdma_rmw(qdma, REG_HW_FWD_DSCP_CFG, + HW_FWD_DSCP_PAYLOAD_SIZE_MASK, +- FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, 0)); ++ FIELD_PREP(HW_FWD_DSCP_PAYLOAD_SIZE_MASK, !!id)); + airoha_qdma_rmw(qdma, REG_FWD_DSCP_LOW_THR, FWD_DSCP_LOW_THR_MASK, + FIELD_PREP(FWD_DSCP_LOW_THR_MASK, 128)); + airoha_qdma_rmw(qdma, REG_LMGR_INIT_CFG, diff --git a/target/linux/airoha/patches-6.6/081-v6.17-net-airoha-Fix-PPE-table-access-in-airoha_ppe_debugf.patch b/target/linux/airoha/patches-6.6/081-v6.17-net-airoha-Fix-PPE-table-access-in-airoha_ppe_debugf.patch new file mode 100644 index 00000000000000..919b6b44da059b --- /dev/null +++ b/target/linux/airoha/patches-6.6/081-v6.17-net-airoha-Fix-PPE-table-access-in-airoha_ppe_debugf.patch @@ -0,0 +1,92 @@ +From 38358fa3cc8e16c6862a3e5c5c233f9f652e3a6d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 31 Jul 2025 12:29:08 +0200 +Subject: [PATCH] net: airoha: Fix PPE table access in + airoha_ppe_debugfs_foe_show() + +In order to avoid any possible race we need to hold the ppe_lock +spinlock accessing the hw PPE table. airoha_ppe_foe_get_entry routine is +always executed holding ppe_lock except in airoha_ppe_debugfs_foe_show +routine. Fix the problem introducing airoha_ppe_foe_get_entry_locked +routine. + +Fixes: 3fe15c640f380 ("net: airoha: Introduce PPE debugfs support") +Reviewed-by: Dawid Osuchowski +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250731-airoha_ppe_foe_get_entry_locked-v2-1-50efbd8c0fd6@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 26 ++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -508,9 +508,11 @@ static void airoha_ppe_foe_flow_stats_up + FIELD_PREP(AIROHA_FOE_IB2_NBQ, nbq); + } + +-struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, +- u32 hash) ++static struct airoha_foe_entry * ++airoha_ppe_foe_get_entry_locked(struct airoha_ppe *ppe, u32 hash) + { ++ lockdep_assert_held(&ppe_lock); ++ + if (hash < PPE_SRAM_NUM_ENTRIES) { + u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry); + struct airoha_eth *eth = ppe->eth; +@@ -537,6 +539,18 @@ struct airoha_foe_entry *airoha_ppe_foe_ + return ppe->foe + hash * sizeof(struct airoha_foe_entry); + } + ++struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe, ++ u32 hash) ++{ ++ struct airoha_foe_entry *hwe; ++ ++ spin_lock_bh(&ppe_lock); ++ hwe = airoha_ppe_foe_get_entry_locked(ppe, hash); ++ spin_unlock_bh(&ppe_lock); ++ ++ return hwe; ++} ++ + static bool airoha_ppe_foe_compare_entry(struct airoha_flow_table_entry *e, + struct airoha_foe_entry *hwe) + { +@@ -651,7 +665,7 @@ airoha_ppe_foe_commit_subflow_entry(stru + struct airoha_flow_table_entry *f; + int type; + +- hwe_p = airoha_ppe_foe_get_entry(ppe, hash); ++ hwe_p = airoha_ppe_foe_get_entry_locked(ppe, hash); + if (!hwe_p) + return -EINVAL; + +@@ -703,7 +717,7 @@ static void airoha_ppe_foe_insert_entry( + + spin_lock_bh(&ppe_lock); + +- hwe = airoha_ppe_foe_get_entry(ppe, hash); ++ hwe = airoha_ppe_foe_get_entry_locked(ppe, hash); + if (!hwe) + goto unlock; + +@@ -818,7 +832,7 @@ airoha_ppe_foe_flow_l2_entry_update(stru + u32 ib1, state; + int idle; + +- hwe = airoha_ppe_foe_get_entry(ppe, iter->hash); ++ hwe = airoha_ppe_foe_get_entry_locked(ppe, iter->hash); + if (!hwe) + continue; + +@@ -855,7 +869,7 @@ static void airoha_ppe_foe_flow_entry_up + if (e->hash == 0xffff) + goto unlock; + +- hwe_p = airoha_ppe_foe_get_entry(ppe, e->hash); ++ hwe_p = airoha_ppe_foe_get_entry_locked(ppe, e->hash); + if (!hwe_p) + goto unlock; + diff --git a/target/linux/airoha/patches-6.6/082-v6.17-net-airoha-ppe-Do-not-invalid-PPE-entries-in-case-of.patch b/target/linux/airoha/patches-6.6/082-v6.17-net-airoha-ppe-Do-not-invalid-PPE-entries-in-case-of.patch new file mode 100644 index 00000000000000..eda914aab719f0 --- /dev/null +++ b/target/linux/airoha/patches-6.6/082-v6.17-net-airoha-ppe-Do-not-invalid-PPE-entries-in-case-of.patch @@ -0,0 +1,42 @@ +From 9f6b606b6b37e61427412708411e8e04b1a858e8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 18 Aug 2025 11:58:25 +0200 +Subject: [PATCH] net: airoha: ppe: Do not invalid PPE entries in case of SW + hash collision + +SW hash computed by airoha_ppe_foe_get_entry_hash routine (used for +foe_flow hlist) can theoretically produce collisions between two +different HW PPE entries. +In airoha_ppe_foe_insert_entry() if the collision occurs we will mark +the second PPE entry in the list as stale (setting the hw hash to 0xffff). +Stale entries are no more updated in airoha_ppe_foe_flow_entry_update +routine and so they are removed by Netfilter. +Fix the problem not marking the second entry as stale in +airoha_ppe_foe_insert_entry routine if we have already inserted the +brand new entry in the PPE table and let Netfilter remove real stale +entries according to their timestamp. +Please note this is just a theoretical issue spotted reviewing the code +and not faced running the system. + +Fixes: cd53f622611f9 ("net: airoha: Add L2 hw acceleration support") +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250818-airoha-en7581-hash-collision-fix-v1-1-d190c4b53d1c@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -736,10 +736,8 @@ static void airoha_ppe_foe_insert_entry( + continue; + } + +- if (commit_done || !airoha_ppe_foe_compare_entry(e, hwe)) { +- e->hash = 0xffff; ++ if (!airoha_ppe_foe_compare_entry(e, hwe)) + continue; +- } + + airoha_ppe_foe_commit_entry(ppe, &e->data, hash); + commit_done = true; diff --git a/target/linux/airoha/patches-6.6/083-01-v6.13-resource-Add-resource-set-range-and-size-helpers.patch b/target/linux/airoha/patches-6.6/083-01-v6.13-resource-Add-resource-set-range-and-size-helpers.patch new file mode 100644 index 00000000000000..36a5300ad0a4bf --- /dev/null +++ b/target/linux/airoha/patches-6.6/083-01-v6.13-resource-Add-resource-set-range-and-size-helpers.patch @@ -0,0 +1,73 @@ +From 9fb6fef0fb49124291837af1da5028f79d53f98e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= +Date: Fri, 14 Jun 2024 13:06:03 +0300 +Subject: [PATCH] resource: Add resource set range and size helpers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Setting the end address for a resource with a given size lacks a helper and +is therefore coded manually unlike the getter side which has a helper for +resource size calculation. Also, almost all callsites that calculate the +end address for a resource also set the start address right before it like +this: + + res->start = start_addr; + res->end = res->start + size - 1; + +Add resource_set_range(res, start_addr, size) that sets the start address +and calculates the end address to simplify this often repeated fragment. + +Also add resource_set_size() for the cases where setting the start address +of the resource is not necessary but mention in its kerneldoc that +resource_set_range() is preferred when setting both addresses. + +Link: https://lore.kernel.org/r/20240614100606.15830-2-ilpo.jarvinen@linux.intel.com +Signed-off-by: Ilpo Järvinen +Signed-off-by: Bjorn Helgaas +Reviewed-by: Jonathan Cameron +--- + include/linux/ioport.h | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +--- a/include/linux/ioport.h ++++ b/include/linux/ioport.h +@@ -216,6 +216,38 @@ struct resource *lookup_resource(struct + int adjust_resource(struct resource *res, resource_size_t start, + resource_size_t size); + resource_size_t resource_alignment(struct resource *res); ++ ++/** ++ * resource_set_size - Calculate resource end address from size and start ++ * @res: Resource descriptor ++ * @size: Size of the resource ++ * ++ * Calculate the end address for @res based on @size. ++ * ++ * Note: The start address of @res must be set when calling this function. ++ * Prefer resource_set_range() if setting both the start address and @size. ++ */ ++static inline void resource_set_size(struct resource *res, resource_size_t size) ++{ ++ res->end = res->start + size - 1; ++} ++ ++/** ++ * resource_set_range - Set resource start and end addresses ++ * @res: Resource descriptor ++ * @start: Start address for the resource ++ * @size: Size of the resource ++ * ++ * Set @res start address and calculate the end address based on @size. ++ */ ++static inline void resource_set_range(struct resource *res, ++ resource_size_t start, ++ resource_size_t size) ++{ ++ res->start = start; ++ resource_set_size(res, size); ++} ++ + static inline resource_size_t resource_size(const struct resource *res) + { + return res->end - res->start + 1; diff --git a/target/linux/airoha/patches-6.6/083-02-v6.16-of-reserved_mem-Add-functions-to-parse-memory-region.patch b/target/linux/airoha/patches-6.6/083-02-v6.16-of-reserved_mem-Add-functions-to-parse-memory-region.patch new file mode 100644 index 00000000000000..cbfec9a391d830 --- /dev/null +++ b/target/linux/airoha/patches-6.6/083-02-v6.16-of-reserved_mem-Add-functions-to-parse-memory-region.patch @@ -0,0 +1,163 @@ +From f4fcfdda2fd8834c62dcb9bfddcf1f89d190b70e Mon Sep 17 00:00:00 2001 +From: "Rob Herring (Arm)" +Date: Wed, 23 Apr 2025 14:42:13 -0500 +Subject: [PATCH] of: reserved_mem: Add functions to parse "memory-region" + +Drivers with "memory-region" properties currently have to do their own +parsing of "memory-region" properties. The result is all the drivers +have similar patterns of a call to parse "memory-region" and then get +the region's address and size. As this is a standard property, it should +have common functions for drivers to use. Add new functions to count the +number of regions and retrieve the region's address as a resource. + +Reviewed-by: Daniel Baluta +Acked-by: Arnaud Pouliquen +Link: https://lore.kernel.org/r/20250423-dt-memory-region-v2-v2-1-2fbd6ebd3c88@kernel.org +Signed-off-by: Rob Herring (Arm) +--- + drivers/of/of_reserved_mem.c | 80 +++++++++++++++++++++++++++++++++ + include/linux/of_reserved_mem.h | 26 +++++++++++ + 2 files changed, 106 insertions(+) + +--- a/drivers/of/of_reserved_mem.c ++++ b/drivers/of/of_reserved_mem.c +@@ -12,6 +12,7 @@ + #define pr_fmt(fmt) "OF: reserved mem: " fmt + + #include ++#include + #include + #include + #include +@@ -514,3 +515,82 @@ struct reserved_mem *of_reserved_mem_loo + return NULL; + } + EXPORT_SYMBOL_GPL(of_reserved_mem_lookup); ++ ++/** ++ * of_reserved_mem_region_to_resource() - Get a reserved memory region as a resource ++ * @np: node containing 'memory-region' property ++ * @idx: index of 'memory-region' property to lookup ++ * @res: Pointer to a struct resource to fill in with reserved region ++ * ++ * This function allows drivers to lookup a node's 'memory-region' property ++ * entries by index and return a struct resource for the entry. ++ * ++ * Returns 0 on success with @res filled in. Returns -ENODEV if 'memory-region' ++ * is missing or unavailable, -EINVAL for any other error. ++ */ ++int of_reserved_mem_region_to_resource(const struct device_node *np, ++ unsigned int idx, struct resource *res) ++{ ++ struct reserved_mem *rmem; ++ ++ if (!np) ++ return -EINVAL; ++ ++ struct device_node __free(device_node) *target = of_parse_phandle(np, "memory-region", idx); ++ if (!target || !of_device_is_available(target)) ++ return -ENODEV; ++ ++ rmem = of_reserved_mem_lookup(target); ++ if (!rmem) ++ return -EINVAL; ++ ++ resource_set_range(res, rmem->base, rmem->size); ++ res->name = rmem->name; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource); ++ ++/** ++ * of_reserved_mem_region_to_resource_byname() - Get a reserved memory region as a resource ++ * @np: node containing 'memory-region' property ++ * @name: name of 'memory-region' property entry to lookup ++ * @res: Pointer to a struct resource to fill in with reserved region ++ * ++ * This function allows drivers to lookup a node's 'memory-region' property ++ * entries by name and return a struct resource for the entry. ++ * ++ * Returns 0 on success with @res filled in, or a negative error-code on ++ * failure. ++ */ ++int of_reserved_mem_region_to_resource_byname(const struct device_node *np, ++ const char *name, ++ struct resource *res) ++{ ++ int idx; ++ ++ if (!name) ++ return -EINVAL; ++ ++ idx = of_property_match_string(np, "memory-region-names", name); ++ if (idx < 0) ++ return idx; ++ ++ return of_reserved_mem_region_to_resource(np, idx, res); ++} ++EXPORT_SYMBOL_GPL(of_reserved_mem_region_to_resource_byname); ++ ++/** ++ * of_reserved_mem_region_count() - Return the number of 'memory-region' entries ++ * @np: node containing 'memory-region' property ++ * ++ * This function allows drivers to retrieve the number of entries for a node's ++ * 'memory-region' property. ++ * ++ * Returns the number of entries on success, or negative error code on a ++ * malformed property. ++ */ ++int of_reserved_mem_region_count(const struct device_node *np) ++{ ++ return of_count_phandle_with_args(np, "memory-region", NULL); ++} ++EXPORT_SYMBOL_GPL(of_reserved_mem_region_count); +--- a/include/linux/of_reserved_mem.h ++++ b/include/linux/of_reserved_mem.h +@@ -7,6 +7,7 @@ + + struct of_phandle_args; + struct reserved_mem_ops; ++struct resource; + + struct reserved_mem { + const char *name; +@@ -40,6 +41,12 @@ int of_reserved_mem_device_init_by_name( + void of_reserved_mem_device_release(struct device *dev); + + struct reserved_mem *of_reserved_mem_lookup(struct device_node *np); ++int of_reserved_mem_region_to_resource(const struct device_node *np, ++ unsigned int idx, struct resource *res); ++int of_reserved_mem_region_to_resource_byname(const struct device_node *np, ++ const char *name, struct resource *res); ++int of_reserved_mem_region_count(const struct device_node *np); ++ + #else + + #define RESERVEDMEM_OF_DECLARE(name, compat, init) \ +@@ -64,6 +71,25 @@ static inline struct reserved_mem *of_re + { + return NULL; + } ++ ++static inline int of_reserved_mem_region_to_resource(const struct device_node *np, ++ unsigned int idx, ++ struct resource *res) ++{ ++ return -ENOSYS; ++} ++ ++static inline int of_reserved_mem_region_to_resource_byname(const struct device_node *np, ++ const char *name, ++ struct resource *res) ++{ ++ return -ENOSYS; ++} ++ ++static inline int of_reserved_mem_region_count(const struct device_node *np) ++{ ++ return 0; ++} + #endif + + /** diff --git a/target/linux/airoha/patches-6.6/084-01-v6.18-net-airoha-npu-Add-NPU-wlan-memory-initialization-co.patch b/target/linux/airoha/patches-6.6/084-01-v6.18-net-airoha-npu-Add-NPU-wlan-memory-initialization-co.patch new file mode 100644 index 00000000000000..7e9e9423ec2d5e --- /dev/null +++ b/target/linux/airoha/patches-6.6/084-01-v6.18-net-airoha-npu-Add-NPU-wlan-memory-initialization-co.patch @@ -0,0 +1,179 @@ +From 564923b02c1d2fe02ee789f9849ff79979b63b9f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:37 +0200 +Subject: [PATCH 1/6] net: airoha: npu: Add NPU wlan memory initialization + commands + +Introduce wlan_init_reserved_memory callback used by MT76 driver during +NPU wlan offloading setup. +This is a preliminary patch to enable wlan flowtable offload for EN7581 +SoC with MT76 driver. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-2-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 82 ++++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 38 +++++++++++ + 2 files changed, 120 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -124,6 +124,13 @@ struct ppe_mbox_data { + }; + }; + ++struct wlan_mbox_data { ++ u32 ifindex:4; ++ u32 func_type:4; ++ u32 func_id; ++ DECLARE_FLEX_ARRAY(u8, d); ++}; ++ + static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id, + void *p, int size) + { +@@ -390,6 +397,80 @@ out: + return err; + } + ++static int airoha_npu_wlan_msg_send(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_set_cmd func_id, ++ void *data, int data_len, gfp_t gfp) ++{ ++ struct wlan_mbox_data *wlan_data; ++ int err, len; ++ ++ len = sizeof(*wlan_data) + data_len; ++ wlan_data = kzalloc(len, gfp); ++ if (!wlan_data) ++ return -ENOMEM; ++ ++ wlan_data->ifindex = ifindex; ++ wlan_data->func_type = NPU_OP_SET; ++ wlan_data->func_id = func_id; ++ memcpy(wlan_data->d, data, data_len); ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_WIFI, wlan_data, len); ++ kfree(wlan_data); ++ ++ return err; ++} ++ ++static int ++airoha_npu_wlan_set_reserved_memory(struct airoha_npu *npu, ++ int ifindex, const char *name, ++ enum airoha_npu_wlan_set_cmd func_id) ++{ ++ struct device *dev = npu->dev; ++ struct resource res; ++ int err; ++ u32 val; ++ ++ err = of_reserved_mem_region_to_resource_byname(dev->of_node, name, ++ &res); ++ if (err) ++ return err; ++ ++ val = res.start; ++ return airoha_npu_wlan_msg_send(npu, ifindex, func_id, &val, ++ sizeof(val), GFP_KERNEL); ++} ++ ++static int airoha_npu_wlan_init_memory(struct airoha_npu *npu) ++{ ++ enum airoha_npu_wlan_set_cmd cmd = WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU; ++ u32 val = 0; ++ int err; ++ ++ err = airoha_npu_wlan_msg_send(npu, 1, cmd, &val, sizeof(val), ++ GFP_KERNEL); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR; ++ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "tx-bufid", cmd); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR; ++ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "pkt", cmd); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR; ++ err = airoha_npu_wlan_set_reserved_memory(npu, 0, "tx-pkt", cmd); ++ if (err) ++ return err; ++ ++ cmd = WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU; ++ return airoha_npu_wlan_msg_send(npu, 0, cmd, &val, sizeof(val), ++ GFP_KERNEL); ++} ++ + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; +@@ -493,6 +574,7 @@ static int airoha_npu_probe(struct platf + npu->ops.ppe_deinit = airoha_npu_ppe_deinit; + npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries; + npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry; ++ npu->ops.wlan_init_reserved_memory = airoha_npu_wlan_init_memory; + + npu->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(npu->regmap)) +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -6,6 +6,43 @@ + + #define NPU_NUM_CORES 8 + ++enum airoha_npu_wlan_set_cmd { ++ WLAN_FUNC_SET_WAIT_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_DESC, ++ WLAN_FUNC_SET_WAIT_NPU_INIT_DONE, ++ WLAN_FUNC_SET_WAIT_TRAN_TO_CPU, ++ WLAN_FUNC_SET_WAIT_BA_WIN_SIZE, ++ WLAN_FUNC_SET_WAIT_DRIVER_MODEL, ++ WLAN_FUNC_SET_WAIT_DEL_STA, ++ WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR, ++ WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_IS_TEST_NOBA, ++ WLAN_FUNC_SET_WAIT_FLUSHONE_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_FLUSHALL_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU, ++ WLAN_FUNC_SET_WAIT_PCIE_STATE, ++ WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, ++ WLAN_FUNC_SET_WAIT_ERROR_RETRY_TIMES, ++ WLAN_FUNC_SET_WAIT_BAR_INFO, ++ WLAN_FUNC_SET_WAIT_FAST_FLAG, ++ WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU, ++ WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_TX_DESC_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_DEBUG_FLAG, ++ WLAN_FUNC_SET_WAIT_INODE_HW_CFG_INFO, ++ WLAN_FUNC_SET_WAIT_INODE_STOP_ACTION, ++ WLAN_FUNC_SET_WAIT_INODE_PCIE_SWAP, ++ WLAN_FUNC_SET_WAIT_RATELIMIT_CTRL, ++ WLAN_FUNC_SET_WAIT_HWNAT_INIT, ++ WLAN_FUNC_SET_WAIT_ARHT_CHIP_INFO, ++ WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR, ++ WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, ++}; ++ + struct airoha_npu { + struct device *dev; + struct regmap *regmap; +@@ -29,6 +66,7 @@ struct airoha_npu { + dma_addr_t foe_addr, + u32 entry_size, u32 hash, + bool ppe2); ++ int (*wlan_init_reserved_memory)(struct airoha_npu *npu); + } ops; + }; + diff --git a/target/linux/airoha/patches-6.6/084-02-v6.18-net-airoha-npu-Add-wlan_-send-get-_msg-NPU-callbacks.patch b/target/linux/airoha/patches-6.6/084-02-v6.18-net-airoha-npu-Add-wlan_-send-get-_msg-NPU-callbacks.patch new file mode 100644 index 00000000000000..5ff820d0be9116 --- /dev/null +++ b/target/linux/airoha/patches-6.6/084-02-v6.18-net-airoha-npu-Add-wlan_-send-get-_msg-NPU-callbacks.patch @@ -0,0 +1,139 @@ +From f97fc66185b2004ad5f393f78b3e645009ddd1d0 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:38 +0200 +Subject: [PATCH 2/6] net: airoha: npu: Add wlan_{send,get}_msg NPU callbacks + +Introduce wlan_send_msg() and wlan_get_msg() NPU wlan callbacks used +by the wlan driver (MT76) to initialize NPU module registers in order to +offload wireless-wired traffic. +This is a preliminary patch to enable wlan flowtable offload for EN7581 +SoC with MT76 driver. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-3-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 52 ++++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 22 ++++++++++ + 2 files changed, 74 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -42,6 +42,22 @@ + #define REG_CR_MBQ8_CTRL(_n) (NPU_MBOX_BASE_ADDR + 0x0b0 + ((_n) << 2)) + #define REG_CR_NPU_MIB(_n) (NPU_MBOX_BASE_ADDR + 0x140 + ((_n) << 2)) + ++#define NPU_WLAN_BASE_ADDR 0x30d000 ++ ++#define REG_IRQ_STATUS (NPU_WLAN_BASE_ADDR + 0x030) ++#define REG_IRQ_RXDONE(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 2) + 0x034) ++#define NPU_IRQ_RX_MASK(_n) ((_n) == 1 ? BIT(17) : BIT(16)) ++ ++#define REG_TX_BASE(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x080) ++#define REG_TX_DSCP_NUM(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x084) ++#define REG_TX_CPU_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x088) ++#define REG_TX_DMA_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x08c) ++ ++#define REG_RX_BASE(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x180) ++#define REG_RX_DSCP_NUM(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x184) ++#define REG_RX_CPU_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x188) ++#define REG_RX_DMA_IDX(_n) (NPU_WLAN_BASE_ADDR + ((_n) << 4) + 0x18c) ++ + #define NPU_TIMER_BASE_ADDR 0x310100 + #define REG_WDT_TIMER_CTRL(_n) (NPU_TIMER_BASE_ADDR + ((_n) * 0x100)) + #define WDT_EN_MASK BIT(25) +@@ -420,6 +436,30 @@ static int airoha_npu_wlan_msg_send(stru + return err; + } + ++static int airoha_npu_wlan_msg_get(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd func_id, ++ void *data, int data_len, gfp_t gfp) ++{ ++ struct wlan_mbox_data *wlan_data; ++ int err, len; ++ ++ len = sizeof(*wlan_data) + data_len; ++ wlan_data = kzalloc(len, gfp); ++ if (!wlan_data) ++ return -ENOMEM; ++ ++ wlan_data->ifindex = ifindex; ++ wlan_data->func_type = NPU_OP_GET; ++ wlan_data->func_id = func_id; ++ ++ err = airoha_npu_send_msg(npu, NPU_FUNC_WIFI, wlan_data, len); ++ if (!err) ++ memcpy(data, wlan_data->d, data_len); ++ kfree(wlan_data); ++ ++ return err; ++} ++ + static int + airoha_npu_wlan_set_reserved_memory(struct airoha_npu *npu, + int ifindex, const char *name, +@@ -471,6 +511,15 @@ static int airoha_npu_wlan_init_memory(s + GFP_KERNEL); + } + ++static u32 airoha_npu_wlan_queue_addr_get(struct airoha_npu *npu, int qid, ++ bool xmit) ++{ ++ if (xmit) ++ return REG_TX_BASE(qid + 2); ++ ++ return REG_RX_BASE(qid); ++} ++ + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; +@@ -575,6 +624,9 @@ static int airoha_npu_probe(struct platf + npu->ops.ppe_flush_sram_entries = airoha_npu_ppe_flush_sram_entries; + npu->ops.ppe_foe_commit_entry = airoha_npu_foe_commit_entry; + npu->ops.wlan_init_reserved_memory = airoha_npu_wlan_init_memory; ++ npu->ops.wlan_send_msg = airoha_npu_wlan_msg_send; ++ npu->ops.wlan_get_msg = airoha_npu_wlan_msg_get; ++ npu->ops.wlan_get_queue_addr = airoha_npu_wlan_queue_addr_get; + + npu->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(npu->regmap)) +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -43,6 +43,20 @@ enum airoha_npu_wlan_set_cmd { + WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, + }; + ++enum airoha_npu_wlan_get_cmd { ++ WLAN_FUNC_GET_WAIT_NPU_INFO, ++ WLAN_FUNC_GET_WAIT_LAST_RATE, ++ WLAN_FUNC_GET_WAIT_COUNTER, ++ WLAN_FUNC_GET_WAIT_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_RXDESC_BASE, ++ WLAN_FUNC_GET_WAIT_WCID_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_DMA_ADDR, ++ WLAN_FUNC_GET_WAIT_RING_SIZE, ++ WLAN_FUNC_GET_WAIT_NPU_SUPPORT_MAP, ++ WLAN_FUNC_GET_WAIT_MDC_LOCK_ADDRESS, ++ WLAN_FUNC_GET_WAIT_NPU_VERSION, ++}; ++ + struct airoha_npu { + struct device *dev; + struct regmap *regmap; +@@ -67,6 +81,14 @@ struct airoha_npu { + u32 entry_size, u32 hash, + bool ppe2); + int (*wlan_init_reserved_memory)(struct airoha_npu *npu); ++ int (*wlan_send_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_set_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ int (*wlan_get_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, ++ bool xmit); + } ops; + }; + diff --git a/target/linux/airoha/patches-6.6/084-03-v6.18-net-airoha-npu-Add-wlan-irq-management-callbacks.patch b/target/linux/airoha/patches-6.6/084-03-v6.18-net-airoha-npu-Add-wlan-irq-management-callbacks.patch new file mode 100644 index 00000000000000..f05b947cecd17d --- /dev/null +++ b/target/linux/airoha/patches-6.6/084-03-v6.18-net-airoha-npu-Add-wlan-irq-management-callbacks.patch @@ -0,0 +1,74 @@ +From 03b7ca3ee5e1b700c462aed5b6cb88f616d6ba7f Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:39 +0200 +Subject: [PATCH 3/6] net: airoha: npu: Add wlan irq management callbacks + +Introduce callbacks used by the MT76 driver to configure NPU SoC +interrupts. This is a preliminary patch to enable wlan flowtable +offload for EN7581 SoC with MT76 driver. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-4-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 27 ++++++++++++++++++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 4 ++++ + 2 files changed, 31 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -520,6 +520,29 @@ static u32 airoha_npu_wlan_queue_addr_ge + return REG_RX_BASE(qid); + } + ++static void airoha_npu_wlan_irq_status_set(struct airoha_npu *npu, u32 val) ++{ ++ regmap_write(npu->regmap, REG_IRQ_STATUS, val); ++} ++ ++static u32 airoha_npu_wlan_irq_status_get(struct airoha_npu *npu, int q) ++{ ++ u32 val; ++ ++ regmap_read(npu->regmap, REG_IRQ_STATUS, &val); ++ return val; ++} ++ ++static void airoha_npu_wlan_irq_enable(struct airoha_npu *npu, int q) ++{ ++ regmap_set_bits(npu->regmap, REG_IRQ_RXDONE(q), NPU_IRQ_RX_MASK(q)); ++} ++ ++static void airoha_npu_wlan_irq_disable(struct airoha_npu *npu, int q) ++{ ++ regmap_clear_bits(npu->regmap, REG_IRQ_RXDONE(q), NPU_IRQ_RX_MASK(q)); ++} ++ + struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr) + { + struct platform_device *pdev; +@@ -627,6 +650,10 @@ static int airoha_npu_probe(struct platf + npu->ops.wlan_send_msg = airoha_npu_wlan_msg_send; + npu->ops.wlan_get_msg = airoha_npu_wlan_msg_get; + npu->ops.wlan_get_queue_addr = airoha_npu_wlan_queue_addr_get; ++ npu->ops.wlan_set_irq_status = airoha_npu_wlan_irq_status_set; ++ npu->ops.wlan_get_irq_status = airoha_npu_wlan_irq_status_get; ++ npu->ops.wlan_enable_irq = airoha_npu_wlan_irq_enable; ++ npu->ops.wlan_disable_irq = airoha_npu_wlan_irq_disable; + + npu->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(npu->regmap)) +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -89,6 +89,10 @@ struct airoha_npu { + void *data, int data_len, gfp_t gfp); + u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, + bool xmit); ++ void (*wlan_set_irq_status)(struct airoha_npu *npu, u32 val); ++ u32 (*wlan_get_irq_status)(struct airoha_npu *npu, int q); ++ void (*wlan_enable_irq)(struct airoha_npu *npu, int q); ++ void (*wlan_disable_irq)(struct airoha_npu *npu, int q); + } ops; + }; + diff --git a/target/linux/airoha/patches-6.6/084-04-v6.18-net-airoha-npu-Read-NPU-wlan-interrupt-lines-from-th.patch b/target/linux/airoha/patches-6.6/084-04-v6.18-net-airoha-npu-Read-NPU-wlan-interrupt-lines-from-th.patch new file mode 100644 index 00000000000000..234dc8b99ae3b3 --- /dev/null +++ b/target/linux/airoha/patches-6.6/084-04-v6.18-net-airoha-npu-Read-NPU-wlan-interrupt-lines-from-th.patch @@ -0,0 +1,58 @@ +From a1740b16c83729d908c760eaa821f27b51e58a13 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:40 +0200 +Subject: [PATCH 4/6] net: airoha: npu: Read NPU wlan interrupt lines from the + DTS + +Read all NPU wlan IRQ lines from the NPU device-tree node. +NPU module fires wlan irq lines when the traffic to/from the WiFi NIC is +not hw accelerated (these interrupts will be consumed by the MT76 driver +in subsequent patches). +This is a preliminary patch to enable wlan flowtable offload for EN7581 +SoC. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-5-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 9 +++++++++ + drivers/net/ethernet/airoha/airoha_npu.h | 3 +++ + 2 files changed, 12 insertions(+) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -696,6 +696,15 @@ static int airoha_npu_probe(struct platf + INIT_WORK(&core->wdt_work, airoha_npu_wdt_work); + } + ++ /* wlan IRQ lines */ ++ for (i = 0; i < ARRAY_SIZE(npu->irqs); i++) { ++ irq = platform_get_irq(pdev, i + ARRAY_SIZE(npu->cores) + 1); ++ if (irq < 0) ++ return irq; ++ ++ npu->irqs[i] = irq; ++ } ++ + err = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (err) + return err; +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ b/drivers/net/ethernet/airoha/airoha_npu.h +@@ -5,6 +5,7 @@ + */ + + #define NPU_NUM_CORES 8 ++#define NPU_NUM_IRQ 6 + + enum airoha_npu_wlan_set_cmd { + WLAN_FUNC_SET_WAIT_PCIE_ADDR, +@@ -68,6 +69,8 @@ struct airoha_npu { + struct work_struct wdt_work; + } cores[NPU_NUM_CORES]; + ++ int irqs[NPU_NUM_IRQ]; ++ + struct airoha_foe_stats __iomem *stats; + + struct { diff --git a/target/linux/airoha/patches-6.6/084-05-v6.18-net-airoha-npu-Enable-core-3-for-WiFi-offloading.patch b/target/linux/airoha/patches-6.6/084-05-v6.18-net-airoha-npu-Enable-core-3-for-WiFi-offloading.patch new file mode 100644 index 00000000000000..c285af23c328a3 --- /dev/null +++ b/target/linux/airoha/patches-6.6/084-05-v6.18-net-airoha-npu-Enable-core-3-for-WiFi-offloading.patch @@ -0,0 +1,28 @@ +From 29c4a3ce508961a02d185ead2d52699b16d82c6d Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:41 +0200 +Subject: [PATCH 5/6] net: airoha: npu: Enable core 3 for WiFi offloading + +NPU core 3 is responsible for WiFi offloading so enable it during NPU +probe. + +Reviewed-by: Simon Horman +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-6-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -726,8 +726,7 @@ static int airoha_npu_probe(struct platf + usleep_range(1000, 2000); + + /* enable NPU cores */ +- /* do not start core3 since it is used for WiFi offloading */ +- regmap_write(npu->regmap, REG_CR_BOOT_CONFIG, 0xf7); ++ regmap_write(npu->regmap, REG_CR_BOOT_CONFIG, 0xff); + regmap_write(npu->regmap, REG_CR_BOOT_TRIGGER, 0x1); + msleep(100); + diff --git a/target/linux/airoha/patches-6.6/084-06-v6.18-net-airoha-Add-airoha_offload.h-header.patch b/target/linux/airoha/patches-6.6/084-06-v6.18-net-airoha-Add-airoha_offload.h-header.patch new file mode 100644 index 00000000000000..ef98c85c3667f4 --- /dev/null +++ b/target/linux/airoha/patches-6.6/084-06-v6.18-net-airoha-Add-airoha_offload.h-header.patch @@ -0,0 +1,416 @@ +From b3ef7bdec66fb1813e865fd39d179a93cefd2015 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Mon, 11 Aug 2025 17:31:42 +0200 +Subject: [PATCH 6/6] net: airoha: Add airoha_offload.h header + +Move NPU definitions to airoha_offload.h in include/linux/soc/airoha/ in +order to allow the MT76 driver to access the callback definitions. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250811-airoha-en7581-wlan-offlaod-v7-7-58823603bb4e@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_npu.c | 2 +- + drivers/net/ethernet/airoha/airoha_npu.h | 103 --------- + drivers/net/ethernet/airoha/airoha_ppe.c | 2 +- + include/linux/soc/airoha/airoha_offload.h | 260 ++++++++++++++++++++++ + 4 files changed, 262 insertions(+), 105 deletions(-) + delete mode 100644 drivers/net/ethernet/airoha/airoha_npu.h + create mode 100644 include/linux/soc/airoha/airoha_offload.h + +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -11,9 +11,9 @@ + #include + #include + #include ++#include + + #include "airoha_eth.h" +-#include "airoha_npu.h" + + #define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin" + #define NPU_EN7581_FIRMWARE_RV32 "airoha/en7581_npu_rv32.bin" +--- a/drivers/net/ethernet/airoha/airoha_npu.h ++++ /dev/null +@@ -1,103 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * Copyright (c) 2025 AIROHA Inc +- * Author: Lorenzo Bianconi +- */ +- +-#define NPU_NUM_CORES 8 +-#define NPU_NUM_IRQ 6 +- +-enum airoha_npu_wlan_set_cmd { +- WLAN_FUNC_SET_WAIT_PCIE_ADDR, +- WLAN_FUNC_SET_WAIT_DESC, +- WLAN_FUNC_SET_WAIT_NPU_INIT_DONE, +- WLAN_FUNC_SET_WAIT_TRAN_TO_CPU, +- WLAN_FUNC_SET_WAIT_BA_WIN_SIZE, +- WLAN_FUNC_SET_WAIT_DRIVER_MODEL, +- WLAN_FUNC_SET_WAIT_DEL_STA, +- WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR, +- WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR, +- WLAN_FUNC_SET_WAIT_IS_TEST_NOBA, +- WLAN_FUNC_SET_WAIT_FLUSHONE_TIMEOUT, +- WLAN_FUNC_SET_WAIT_FLUSHALL_TIMEOUT, +- WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU, +- WLAN_FUNC_SET_WAIT_PCIE_STATE, +- WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, +- WLAN_FUNC_SET_WAIT_ERROR_RETRY_TIMES, +- WLAN_FUNC_SET_WAIT_BAR_INFO, +- WLAN_FUNC_SET_WAIT_FAST_FLAG, +- WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU, +- WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, +- WLAN_FUNC_SET_WAIT_TX_DESC_HW_BASE, +- WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, +- WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE, +- WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR, +- WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, +- WLAN_FUNC_SET_WAIT_INODE_DEBUG_FLAG, +- WLAN_FUNC_SET_WAIT_INODE_HW_CFG_INFO, +- WLAN_FUNC_SET_WAIT_INODE_STOP_ACTION, +- WLAN_FUNC_SET_WAIT_INODE_PCIE_SWAP, +- WLAN_FUNC_SET_WAIT_RATELIMIT_CTRL, +- WLAN_FUNC_SET_WAIT_HWNAT_INIT, +- WLAN_FUNC_SET_WAIT_ARHT_CHIP_INFO, +- WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR, +- WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, +-}; +- +-enum airoha_npu_wlan_get_cmd { +- WLAN_FUNC_GET_WAIT_NPU_INFO, +- WLAN_FUNC_GET_WAIT_LAST_RATE, +- WLAN_FUNC_GET_WAIT_COUNTER, +- WLAN_FUNC_GET_WAIT_DBG_COUNTER, +- WLAN_FUNC_GET_WAIT_RXDESC_BASE, +- WLAN_FUNC_GET_WAIT_WCID_DBG_COUNTER, +- WLAN_FUNC_GET_WAIT_DMA_ADDR, +- WLAN_FUNC_GET_WAIT_RING_SIZE, +- WLAN_FUNC_GET_WAIT_NPU_SUPPORT_MAP, +- WLAN_FUNC_GET_WAIT_MDC_LOCK_ADDRESS, +- WLAN_FUNC_GET_WAIT_NPU_VERSION, +-}; +- +-struct airoha_npu { +- struct device *dev; +- struct regmap *regmap; +- +- struct airoha_npu_core { +- struct airoha_npu *npu; +- /* protect concurrent npu memory accesses */ +- spinlock_t lock; +- struct work_struct wdt_work; +- } cores[NPU_NUM_CORES]; +- +- int irqs[NPU_NUM_IRQ]; +- +- struct airoha_foe_stats __iomem *stats; +- +- struct { +- int (*ppe_init)(struct airoha_npu *npu); +- int (*ppe_deinit)(struct airoha_npu *npu); +- int (*ppe_flush_sram_entries)(struct airoha_npu *npu, +- dma_addr_t foe_addr, +- int sram_num_entries); +- int (*ppe_foe_commit_entry)(struct airoha_npu *npu, +- dma_addr_t foe_addr, +- u32 entry_size, u32 hash, +- bool ppe2); +- int (*wlan_init_reserved_memory)(struct airoha_npu *npu); +- int (*wlan_send_msg)(struct airoha_npu *npu, int ifindex, +- enum airoha_npu_wlan_set_cmd func_id, +- void *data, int data_len, gfp_t gfp); +- int (*wlan_get_msg)(struct airoha_npu *npu, int ifindex, +- enum airoha_npu_wlan_get_cmd func_id, +- void *data, int data_len, gfp_t gfp); +- u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, +- bool xmit); +- void (*wlan_set_irq_status)(struct airoha_npu *npu, u32 val); +- u32 (*wlan_get_irq_status)(struct airoha_npu *npu, int q); +- void (*wlan_enable_irq)(struct airoha_npu *npu, int q); +- void (*wlan_disable_irq)(struct airoha_npu *npu, int q); +- } ops; +-}; +- +-struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); +-void airoha_npu_put(struct airoha_npu *npu); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -7,10 +7,10 @@ + #include + #include + #include ++#include + #include + #include + +-#include "airoha_npu.h" + #include "airoha_regs.h" + #include "airoha_eth.h" + +--- /dev/null ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -0,0 +1,260 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (c) 2025 AIROHA Inc ++ * Author: Lorenzo Bianconi ++ */ ++#ifndef AIROHA_OFFLOAD_H ++#define AIROHA_OFFLOAD_H ++ ++#include ++#include ++ ++#define NPU_NUM_CORES 8 ++#define NPU_NUM_IRQ 6 ++#define NPU_RX0_DESC_NUM 512 ++#define NPU_RX1_DESC_NUM 512 ++ ++/* CTRL */ ++#define NPU_RX_DMA_DESC_LAST_MASK BIT(29) ++#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(28, 15) ++#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(14, 1) ++#define NPU_RX_DMA_DESC_DONE_MASK BIT(0) ++/* INFO */ ++#define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 28) ++#define NPU_RX_DMA_PKT_ID_MASK GENMASK(28, 26) ++#define NPU_RX_DMA_SRC_PORT_MASK GENMASK(25, 21) ++#define NPU_RX_DMA_CRSN_MASK GENMASK(20, 16) ++#define NPU_RX_DMA_FOE_ID_MASK GENMASK(15, 0) ++/* DATA */ ++#define NPU_RX_DMA_SID_MASK GENMASK(31, 16) ++#define NPU_RX_DMA_FRAG_TYPE_MASK GENMASK(15, 14) ++#define NPU_RX_DMA_PRIORITY_MASK GENMASK(13, 10) ++#define NPU_RX_DMA_RADIO_ID_MASK GENMASK(9, 6) ++#define NPU_RX_DMA_VAP_ID_MASK GENMASK(5, 2) ++#define NPU_RX_DMA_FRAME_TYPE_MASK GENMASK(1, 0) ++ ++struct airoha_npu_rx_dma_desc { ++ u32 ctrl; ++ u32 info; ++ u32 data; ++ u32 addr; ++ u64 rsv; ++} __packed; ++ ++/* CTRL */ ++#define NPU_TX_DMA_DESC_SCHED_MASK BIT(31) ++#define NPU_TX_DMA_DESC_LEN_MASK GENMASK(30, 18) ++#define NPU_TX_DMA_DESC_VEND_LEN_MASK GENMASK(17, 1) ++#define NPU_TX_DMA_DESC_DONE_MASK BIT(0) ++ ++#define NPU_TXWI_LEN 192 ++ ++struct airoha_npu_tx_dma_desc { ++ u32 ctrl; ++ u32 addr; ++ u64 rsv; ++ u8 txwi[NPU_TXWI_LEN]; ++} __packed; ++ ++enum airoha_npu_wlan_set_cmd { ++ WLAN_FUNC_SET_WAIT_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_DESC, ++ WLAN_FUNC_SET_WAIT_NPU_INIT_DONE, ++ WLAN_FUNC_SET_WAIT_TRAN_TO_CPU, ++ WLAN_FUNC_SET_WAIT_BA_WIN_SIZE, ++ WLAN_FUNC_SET_WAIT_DRIVER_MODEL, ++ WLAN_FUNC_SET_WAIT_DEL_STA, ++ WLAN_FUNC_SET_WAIT_DRAM_BA_NODE_ADDR, ++ WLAN_FUNC_SET_WAIT_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_IS_TEST_NOBA, ++ WLAN_FUNC_SET_WAIT_FLUSHONE_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_FLUSHALL_TIMEOUT, ++ WLAN_FUNC_SET_WAIT_IS_FORCE_TO_CPU, ++ WLAN_FUNC_SET_WAIT_PCIE_STATE, ++ WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, ++ WLAN_FUNC_SET_WAIT_ERROR_RETRY_TIMES, ++ WLAN_FUNC_SET_WAIT_BAR_INFO, ++ WLAN_FUNC_SET_WAIT_FAST_FLAG, ++ WLAN_FUNC_SET_WAIT_NPU_BAND0_ONCPU, ++ WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, ++ WLAN_FUNC_SET_WAIT_TX_DESC_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE, ++ WLAN_FUNC_SET_WAIT_TX_PKT_BUF_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, ++ WLAN_FUNC_SET_WAIT_INODE_DEBUG_FLAG, ++ WLAN_FUNC_SET_WAIT_INODE_HW_CFG_INFO, ++ WLAN_FUNC_SET_WAIT_INODE_STOP_ACTION, ++ WLAN_FUNC_SET_WAIT_INODE_PCIE_SWAP, ++ WLAN_FUNC_SET_WAIT_RATELIMIT_CTRL, ++ WLAN_FUNC_SET_WAIT_HWNAT_INIT, ++ WLAN_FUNC_SET_WAIT_ARHT_CHIP_INFO, ++ WLAN_FUNC_SET_WAIT_TX_BUF_CHECK_ADDR, ++ WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, ++}; ++ ++enum airoha_npu_wlan_get_cmd { ++ WLAN_FUNC_GET_WAIT_NPU_INFO, ++ WLAN_FUNC_GET_WAIT_LAST_RATE, ++ WLAN_FUNC_GET_WAIT_COUNTER, ++ WLAN_FUNC_GET_WAIT_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_RXDESC_BASE, ++ WLAN_FUNC_GET_WAIT_WCID_DBG_COUNTER, ++ WLAN_FUNC_GET_WAIT_DMA_ADDR, ++ WLAN_FUNC_GET_WAIT_RING_SIZE, ++ WLAN_FUNC_GET_WAIT_NPU_SUPPORT_MAP, ++ WLAN_FUNC_GET_WAIT_MDC_LOCK_ADDRESS, ++ WLAN_FUNC_GET_WAIT_NPU_VERSION, ++}; ++ ++struct airoha_npu { ++#if (IS_BUILTIN(CONFIG_NET_AIROHA_NPU) || IS_MODULE(CONFIG_NET_AIROHA_NPU)) ++ struct device *dev; ++ struct regmap *regmap; ++ ++ struct airoha_npu_core { ++ struct airoha_npu *npu; ++ /* protect concurrent npu memory accesses */ ++ spinlock_t lock; ++ struct work_struct wdt_work; ++ } cores[NPU_NUM_CORES]; ++ ++ int irqs[NPU_NUM_IRQ]; ++ ++ struct airoha_foe_stats __iomem *stats; ++ ++ struct { ++ int (*ppe_init)(struct airoha_npu *npu); ++ int (*ppe_deinit)(struct airoha_npu *npu); ++ int (*ppe_flush_sram_entries)(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ int sram_num_entries); ++ int (*ppe_foe_commit_entry)(struct airoha_npu *npu, ++ dma_addr_t foe_addr, ++ u32 entry_size, u32 hash, ++ bool ppe2); ++ int (*wlan_init_reserved_memory)(struct airoha_npu *npu); ++ int (*wlan_send_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_set_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ int (*wlan_get_msg)(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd func_id, ++ void *data, int data_len, gfp_t gfp); ++ u32 (*wlan_get_queue_addr)(struct airoha_npu *npu, int qid, ++ bool xmit); ++ void (*wlan_set_irq_status)(struct airoha_npu *npu, u32 val); ++ u32 (*wlan_get_irq_status)(struct airoha_npu *npu, int q); ++ void (*wlan_enable_irq)(struct airoha_npu *npu, int q); ++ void (*wlan_disable_irq)(struct airoha_npu *npu, int q); ++ } ops; ++#endif ++}; ++ ++#if (IS_BUILTIN(CONFIG_NET_AIROHA_NPU) || IS_MODULE(CONFIG_NET_AIROHA_NPU)) ++struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr); ++void airoha_npu_put(struct airoha_npu *npu); ++ ++static inline int airoha_npu_wlan_init_reserved_memory(struct airoha_npu *npu) ++{ ++ return npu->ops.wlan_init_reserved_memory(npu); ++} ++ ++static inline int airoha_npu_wlan_send_msg(struct airoha_npu *npu, ++ int ifindex, ++ enum airoha_npu_wlan_set_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return npu->ops.wlan_send_msg(npu, ifindex, cmd, data, data_len, gfp); ++} ++ ++static inline int airoha_npu_wlan_get_msg(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return npu->ops.wlan_get_msg(npu, ifindex, cmd, data, data_len, gfp); ++} ++ ++static inline u32 airoha_npu_wlan_get_queue_addr(struct airoha_npu *npu, ++ int qid, bool xmit) ++{ ++ return npu->ops.wlan_get_queue_addr(npu, qid, xmit); ++} ++ ++static inline void airoha_npu_wlan_set_irq_status(struct airoha_npu *npu, ++ u32 val) ++{ ++ npu->ops.wlan_set_irq_status(npu, val); ++} ++ ++static inline u32 airoha_npu_wlan_get_irq_status(struct airoha_npu *npu, int q) ++{ ++ return npu->ops.wlan_get_irq_status(npu, q); ++} ++ ++static inline void airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q) ++{ ++ npu->ops.wlan_enable_irq(npu, q); ++} ++ ++static inline void airoha_npu_wlan_disable_irq(struct airoha_npu *npu, int q) ++{ ++ npu->ops.wlan_disable_irq(npu, q); ++} ++#else ++static inline struct airoha_npu *airoha_npu_get(struct device *dev, ++ dma_addr_t *foe_stats_addr) ++{ ++ return NULL; ++} ++ ++static inline void airoha_npu_put(struct airoha_npu *npu) ++{ ++} ++ ++static inline int airoha_npu_wlan_init_reserved_memory(struct airoha_npu *npu) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int airoha_npu_wlan_send_msg(struct airoha_npu *npu, ++ int ifindex, ++ enum airoha_npu_wlan_set_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline int airoha_npu_wlan_get_msg(struct airoha_npu *npu, int ifindex, ++ enum airoha_npu_wlan_get_cmd cmd, ++ void *data, int data_len, gfp_t gfp) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static inline u32 airoha_npu_wlan_get_queue_addr(struct airoha_npu *npu, ++ int qid, bool xmit) ++{ ++ return 0; ++} ++ ++static inline void airoha_npu_wlan_set_irq_status(struct airoha_npu *npu, ++ u32 val) ++{ ++} ++ ++static inline u32 airoha_npu_wlan_get_irq_status(struct airoha_npu *npu, ++ int q) ++{ ++ return 0; ++} ++ ++static inline void airoha_npu_wlan_enable_irq(struct airoha_npu *npu, int q) ++{ ++} ++ ++static inline void airoha_npu_wlan_disable_irq(struct airoha_npu *npu, int q) ++{ ++} ++#endif ++ ++#endif /* AIROHA_OFFLOAD_H */ diff --git a/target/linux/airoha/patches-6.6/085-v6.18-net-airoha-Add-wlan-flowtable-TX-offload.patch b/target/linux/airoha/patches-6.6/085-v6.18-net-airoha-Add-wlan-flowtable-TX-offload.patch new file mode 100644 index 00000000000000..ab9a3761a50329 --- /dev/null +++ b/target/linux/airoha/patches-6.6/085-v6.18-net-airoha-Add-wlan-flowtable-TX-offload.patch @@ -0,0 +1,198 @@ +From a8bdd935d1ddb7186358fb60ffe84253e85340c8 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Thu, 14 Aug 2025 09:51:16 +0200 +Subject: [PATCH] net: airoha: Add wlan flowtable TX offload + +Introduce support to offload the traffic received on the ethernet NIC +and forwarded to the wireless one using HW Packet Processor Engine (PPE) +capabilities. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250814-airoha-en7581-wlan-tx-offload-v1-1-72e0a312003e@kernel.org +Signed-off-by: Paolo Abeni +--- + drivers/net/ethernet/airoha/airoha_eth.h | 11 +++ + drivers/net/ethernet/airoha/airoha_ppe.c | 103 ++++++++++++++++------- + 2 files changed, 85 insertions(+), 29 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -252,6 +252,10 @@ enum { + #define AIROHA_FOE_MAC_SMAC_ID GENMASK(20, 16) + #define AIROHA_FOE_MAC_PPPOE_ID GENMASK(15, 0) + ++#define AIROHA_FOE_MAC_WDMA_QOS GENMASK(15, 12) ++#define AIROHA_FOE_MAC_WDMA_BAND BIT(11) ++#define AIROHA_FOE_MAC_WDMA_WCID GENMASK(10, 0) ++ + struct airoha_foe_mac_info_common { + u16 vlan1; + u16 etype; +@@ -481,6 +485,13 @@ struct airoha_flow_table_entry { + unsigned long cookie; + }; + ++struct airoha_wdma_info { ++ u8 idx; ++ u8 queue; ++ u16 wcid; ++ u8 bss; ++}; ++ + /* RX queue to IRQ mapping: BIT(q) in IRQ(n) */ + #define RX_IRQ0_BANK_PIN_MASK 0x839f + #define RX_IRQ1_BANK_PIN_MASK 0x7fe00000 +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -190,6 +190,31 @@ static int airoha_ppe_flow_mangle_ipv4(c + return 0; + } + ++static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr, ++ struct airoha_wdma_info *info) ++{ ++ struct net_device_path_stack stack; ++ struct net_device_path *path; ++ int err; ++ ++ if (!dev) ++ return -ENODEV; ++ ++ err = dev_fill_forward_path(dev, addr, &stack); ++ if (err) ++ return err; ++ ++ path = &stack.path[stack.num_paths - 1]; ++ if (path->type != DEV_PATH_MTK_WDMA) ++ return -1; ++ ++ info->idx = path->mtk_wdma.wdma_idx; ++ info->bss = path->mtk_wdma.bss; ++ info->wcid = path->mtk_wdma.wcid; ++ ++ return 0; ++} ++ + static int airoha_get_dsa_port(struct net_device **dev) + { + #if IS_ENABLED(CONFIG_NET_DSA) +@@ -220,9 +245,9 @@ static int airoha_ppe_foe_entry_prepare( + struct airoha_flow_data *data, + int l4proto) + { +- int dsa_port = airoha_get_dsa_port(&dev); ++ u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val; ++ int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&dev); + struct airoha_foe_mac_info_common *l2; +- u32 qdata, ports_pad, val; + u8 smac_id = 0xf; + + memset(hwe, 0, sizeof(*hwe)); +@@ -236,31 +261,47 @@ static int airoha_ppe_foe_entry_prepare( + AIROHA_FOE_IB1_BIND_TTL; + hwe->ib1 = val; + +- val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f) | +- AIROHA_FOE_IB2_PSE_QOS; +- if (dsa_port >= 0) +- val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, dsa_port); +- ++ val = FIELD_PREP(AIROHA_FOE_IB2_PORT_AG, 0x1f); + if (dev) { +- struct airoha_gdm_port *port = netdev_priv(dev); +- u8 pse_port; +- +- if (!airoha_is_valid_gdm_port(eth, port)) +- return -EINVAL; ++ struct airoha_wdma_info info = {}; + +- if (dsa_port >= 0) +- pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id; +- else +- pse_port = 2; /* uplink relies on GDM2 loopback */ +- val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port); +- +- /* For downlink traffic consume SRAM memory for hw forwarding +- * descriptors queue. +- */ +- if (airhoa_is_lan_gdm_port(port)) +- val |= AIROHA_FOE_IB2_FAST_PATH; ++ if (!airoha_ppe_get_wdma_info(dev, data->eth.h_dest, &info)) { ++ val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) | ++ FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, ++ FE_PSE_PORT_CDM4); ++ qdata |= FIELD_PREP(AIROHA_FOE_ACTDP, info.bss); ++ wlan_etype = FIELD_PREP(AIROHA_FOE_MAC_WDMA_BAND, ++ info.idx) | ++ FIELD_PREP(AIROHA_FOE_MAC_WDMA_WCID, ++ info.wcid); ++ } else { ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ u8 pse_port; ++ ++ if (!airoha_is_valid_gdm_port(eth, port)) ++ return -EINVAL; ++ ++ if (dsa_port >= 0) ++ pse_port = port->id == 4 ? FE_PSE_PORT_GDM4 ++ : port->id; ++ else ++ pse_port = 2; /* uplink relies on GDM2 ++ * loopback ++ */ ++ ++ val |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, pse_port) | ++ AIROHA_FOE_IB2_PSE_QOS; ++ /* For downlink traffic consume SRAM memory for hw ++ * forwarding descriptors queue. ++ */ ++ if (airhoa_is_lan_gdm_port(port)) ++ val |= AIROHA_FOE_IB2_FAST_PATH; ++ if (dsa_port >= 0) ++ val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, ++ dsa_port); + +- smac_id = port->id; ++ smac_id = port->id; ++ } + } + + if (is_multicast_ether_addr(data->eth.h_dest)) +@@ -272,7 +313,6 @@ static int airoha_ppe_foe_entry_prepare( + if (type == PPE_PKT_TYPE_IPV6_ROUTE_3T) + hwe->ipv6.ports = ports_pad; + +- qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f); + if (type == PPE_PKT_TYPE_BRIDGE) { + airoha_ppe_foe_set_bridge_addrs(&hwe->bridge, &data->eth); + hwe->bridge.data = qdata; +@@ -313,7 +353,9 @@ static int airoha_ppe_foe_entry_prepare( + l2->vlan2 = data->vlan.hdr[1].id; + } + +- if (dsa_port >= 0) { ++ if (wlan_etype >= 0) { ++ l2->etype = wlan_etype; ++ } else if (dsa_port >= 0) { + l2->etype = BIT(dsa_port); + l2->etype |= !data->vlan.num ? BIT(15) : 0; + } else if (data->pppoe.num) { +@@ -490,6 +532,10 @@ static void airoha_ppe_foe_flow_stats_up + meter = &hwe->ipv4.l2.meter; + } + ++ pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); ++ if (pse_port == FE_PSE_PORT_CDM4) ++ return; ++ + airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, index); + + val = FIELD_GET(AIROHA_FOE_CHANNEL | AIROHA_FOE_QID, *data); +@@ -500,7 +546,6 @@ static void airoha_ppe_foe_flow_stats_up + AIROHA_FOE_IB2_PSE_QOS | AIROHA_FOE_IB2_FAST_PATH); + *meter |= FIELD_PREP(AIROHA_FOE_TUNNEL_MTU, val); + +- pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2); + nbq = pse_port == 1 ? 6 : 5; + *ib2 &= ~(AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT | + AIROHA_FOE_IB2_PSE_QOS); diff --git a/target/linux/airoha/patches-6.6/086-01-v6.18-net-airoha-Rely-on-airoha_eth-struct-in-airoha_ppe_f.patch b/target/linux/airoha/patches-6.6/086-01-v6.18-net-airoha-Rely-on-airoha_eth-struct-in-airoha_ppe_f.patch new file mode 100644 index 00000000000000..cef2922869d3e8 --- /dev/null +++ b/target/linux/airoha/patches-6.6/086-01-v6.18-net-airoha-Rely-on-airoha_eth-struct-in-airoha_ppe_f.patch @@ -0,0 +1,95 @@ +From 524a43c3a0c17fa0a1223eea36751dcba55e5530 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 23 Aug 2025 09:56:02 +0200 +Subject: [PATCH 1/3] net: airoha: Rely on airoha_eth struct in + airoha_ppe_flow_offload_cmd signature + +Rely on airoha_eth struct in airoha_ppe_flow_offload_cmd routine +signature and in all the called subroutines. +This is a preliminary patch to introduce flowtable offload for traffic +received by the wlan NIC and forwarded to the ethernet one. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250823-airoha-en7581-wlan-rx-offload-v3-1-f78600ec3ed8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_ppe.c | 20 ++++++++------------ + 1 file changed, 8 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -935,11 +935,10 @@ static int airoha_ppe_entry_idle_time(st + return airoha_ppe_get_entry_idle_time(ppe, e->data.ib1); + } + +-static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_replace(struct airoha_eth *eth, + struct flow_cls_offload *f) + { + struct flow_rule *rule = flow_cls_offload_flow_rule(f); +- struct airoha_eth *eth = port->qdma->eth; + struct airoha_flow_table_entry *e; + struct airoha_flow_data data = {}; + struct net_device *odev = NULL; +@@ -1136,10 +1135,9 @@ free_entry: + return err; + } + +-static int airoha_ppe_flow_offload_destroy(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth, + struct flow_cls_offload *f) + { +- struct airoha_eth *eth = port->qdma->eth; + struct airoha_flow_table_entry *e; + + e = rhashtable_lookup(ð->flow_table, &f->cookie, +@@ -1182,10 +1180,9 @@ void airoha_ppe_foe_entry_get_stats(stru + rcu_read_unlock(); + } + +-static int airoha_ppe_flow_offload_stats(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_stats(struct airoha_eth *eth, + struct flow_cls_offload *f) + { +- struct airoha_eth *eth = port->qdma->eth; + struct airoha_flow_table_entry *e; + u32 idle; + +@@ -1209,16 +1206,16 @@ static int airoha_ppe_flow_offload_stats + return 0; + } + +-static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port, ++static int airoha_ppe_flow_offload_cmd(struct airoha_eth *eth, + struct flow_cls_offload *f) + { + switch (f->command) { + case FLOW_CLS_REPLACE: +- return airoha_ppe_flow_offload_replace(port, f); ++ return airoha_ppe_flow_offload_replace(eth, f); + case FLOW_CLS_DESTROY: +- return airoha_ppe_flow_offload_destroy(port, f); ++ return airoha_ppe_flow_offload_destroy(eth, f); + case FLOW_CLS_STATS: +- return airoha_ppe_flow_offload_stats(port, f); ++ return airoha_ppe_flow_offload_stats(eth, f); + default: + break; + } +@@ -1288,7 +1285,6 @@ error_npu_put: + int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) + { + struct airoha_gdm_port *port = netdev_priv(dev); +- struct flow_cls_offload *cls = type_data; + struct airoha_eth *eth = port->qdma->eth; + int err = 0; + +@@ -1297,7 +1293,7 @@ int airoha_ppe_setup_tc_block_cb(struct + if (!eth->npu) + err = airoha_ppe_offload_setup(eth); + if (!err) +- err = airoha_ppe_flow_offload_cmd(port, cls); ++ err = airoha_ppe_flow_offload_cmd(eth, type_data); + + mutex_unlock(&flow_offload_mutex); + diff --git a/target/linux/airoha/patches-6.6/086-02-v6.18-net-airoha-Add-airoha_ppe_dev-struct-definition.patch b/target/linux/airoha/patches-6.6/086-02-v6.18-net-airoha-Add-airoha_ppe_dev-struct-definition.patch new file mode 100644 index 00000000000000..7fa5f9bddd9452 --- /dev/null +++ b/target/linux/airoha/patches-6.6/086-02-v6.18-net-airoha-Add-airoha_ppe_dev-struct-definition.patch @@ -0,0 +1,223 @@ +From f45fc18b6de04483643e8aa2ab97737abfe03d59 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 23 Aug 2025 09:56:03 +0200 +Subject: [PATCH 2/3] net: airoha: Add airoha_ppe_dev struct definition + +Introduce airoha_ppe_dev struct as container for PPE offload callbacks +consumed by the MT76 driver during flowtable offload for traffic +received by the wlan NIC and forwarded to the wired one. +Add airoha_ppe_setup_tc_block_cb routine to PPE offload ops for MT76 +driver. +Rely on airoha_ppe_dev pointer in airoha_ppe_setup_tc_block_cb +signature. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250823-airoha-en7581-wlan-rx-offload-v3-2-f78600ec3ed8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 4 +- + drivers/net/ethernet/airoha/airoha_eth.h | 4 +- + drivers/net/ethernet/airoha/airoha_npu.c | 1 - + drivers/net/ethernet/airoha/airoha_ppe.c | 67 +++++++++++++++++++++-- + include/linux/soc/airoha/airoha_offload.h | 35 ++++++++++++ + 5 files changed, 104 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2602,13 +2602,15 @@ static int airoha_dev_setup_tc_block_cb( + void *type_data, void *cb_priv) + { + struct net_device *dev = cb_priv; ++ struct airoha_gdm_port *port = netdev_priv(dev); ++ struct airoha_eth *eth = port->qdma->eth; + + if (!tc_can_offload(dev)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_CLSFLOWER: +- return airoha_ppe_setup_tc_block_cb(dev, type_data); ++ return airoha_ppe_setup_tc_block_cb(ð->ppe->dev, type_data); + case TC_SETUP_CLSMATCHALL: + return airoha_dev_tc_matchall(dev, type_data); + default: +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + + #define AIROHA_MAX_NUM_GDM_PORTS 4 +@@ -546,6 +547,7 @@ struct airoha_gdm_port { + #define AIROHA_RXD4_FOE_ENTRY GENMASK(15, 0) + + struct airoha_ppe { ++ struct airoha_ppe_dev dev; + struct airoha_eth *eth; + + void *foe; +@@ -622,7 +624,7 @@ bool airoha_is_valid_gdm_port(struct air + + void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, + u16 hash); +-int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data); ++int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); + void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port); +--- a/drivers/net/ethernet/airoha/airoha_npu.c ++++ b/drivers/net/ethernet/airoha/airoha_npu.c +@@ -11,7 +11,6 @@ + #include + #include + #include +-#include + + #include "airoha_eth.h" + +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -6,8 +6,9 @@ + + #include + #include ++#include ++#include + #include +-#include + #include + #include + +@@ -1282,10 +1283,10 @@ error_npu_put: + return err; + } + +-int airoha_ppe_setup_tc_block_cb(struct net_device *dev, void *type_data) ++int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data) + { +- struct airoha_gdm_port *port = netdev_priv(dev); +- struct airoha_eth *eth = port->qdma->eth; ++ struct airoha_ppe *ppe = dev->priv; ++ struct airoha_eth *eth = ppe->eth; + int err = 0; + + mutex_lock(&flow_offload_mutex); +@@ -1338,6 +1339,61 @@ void airoha_ppe_init_upd_mem(struct airo + PPE_UPDMEM_WR_MASK | PPE_UPDMEM_REQ_MASK); + } + ++struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev) ++{ ++ struct platform_device *pdev; ++ struct device_node *np; ++ struct airoha_eth *eth; ++ ++ np = of_parse_phandle(dev->of_node, "airoha,eth", 0); ++ if (!np) ++ return ERR_PTR(-ENODEV); ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev) { ++ dev_err(dev, "cannot find device node %s\n", np->name); ++ of_node_put(np); ++ return ERR_PTR(-ENODEV); ++ } ++ of_node_put(np); ++ ++ if (!try_module_get(THIS_MODULE)) { ++ dev_err(dev, "failed to get the device driver module\n"); ++ goto error_pdev_put; ++ } ++ ++ eth = platform_get_drvdata(pdev); ++ if (!eth) ++ goto error_module_put; ++ ++ if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER)) { ++ dev_err(&pdev->dev, ++ "failed to create device link to consumer %s\n", ++ dev_name(dev)); ++ goto error_module_put; ++ } ++ ++ return ð->ppe->dev; ++ ++error_module_put: ++ module_put(THIS_MODULE); ++error_pdev_put: ++ platform_device_put(pdev); ++ ++ return ERR_PTR(-ENODEV); ++} ++EXPORT_SYMBOL_GPL(airoha_ppe_get_dev); ++ ++void airoha_ppe_put_dev(struct airoha_ppe_dev *dev) ++{ ++ struct airoha_ppe *ppe = dev->priv; ++ struct airoha_eth *eth = ppe->eth; ++ ++ module_put(THIS_MODULE); ++ put_device(eth->dev); ++} ++EXPORT_SYMBOL_GPL(airoha_ppe_put_dev); ++ + int airoha_ppe_init(struct airoha_eth *eth) + { + struct airoha_ppe *ppe; +@@ -1347,6 +1403,9 @@ int airoha_ppe_init(struct airoha_eth *e + if (!ppe) + return -ENOMEM; + ++ ppe->dev.ops.setup_tc_block_cb = airoha_ppe_setup_tc_block_cb; ++ ppe->dev.priv = ppe; ++ + foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry); + ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma, + GFP_KERNEL); +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -9,6 +9,41 @@ + #include + #include + ++struct airoha_ppe_dev { ++ struct { ++ int (*setup_tc_block_cb)(struct airoha_ppe_dev *dev, ++ void *type_data); ++ } ops; ++ ++ void *priv; ++}; ++ ++#if (IS_BUILTIN(CONFIG_NET_AIROHA) || IS_MODULE(CONFIG_NET_AIROHA)) ++struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev); ++void airoha_ppe_put_dev(struct airoha_ppe_dev *dev); ++ ++static inline int airoha_ppe_dev_setup_tc_block_cb(struct airoha_ppe_dev *dev, ++ void *type_data) ++{ ++ return dev->ops.setup_tc_block_cb(dev, type_data); ++} ++#else ++static inline struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev) ++{ ++ return NULL; ++} ++ ++static inline void airoha_ppe_put_dev(struct airoha_ppe_dev *dev) ++{ ++} ++ ++static inline int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, ++ void *type_data) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ + #define NPU_NUM_CORES 8 + #define NPU_NUM_IRQ 6 + #define NPU_RX0_DESC_NUM 512 diff --git a/target/linux/airoha/patches-6.6/086-03-v6.18-net-airoha-Introduce-check_skb-callback-in-ppe_dev-o.patch b/target/linux/airoha/patches-6.6/086-03-v6.18-net-airoha-Introduce-check_skb-callback-in-ppe_dev-o.patch new file mode 100644 index 00000000000000..1edc2aa54c4f48 --- /dev/null +++ b/target/linux/airoha/patches-6.6/086-03-v6.18-net-airoha-Introduce-check_skb-callback-in-ppe_dev-o.patch @@ -0,0 +1,207 @@ +From a7cc1aa151e3a9c0314b995f06102f7763d3bd71 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Sat, 23 Aug 2025 09:56:04 +0200 +Subject: [PATCH 3/3] net: airoha: Introduce check_skb callback in ppe_dev ops + +Export airoha_ppe_check_skb routine in ppe_dev ops. check_skb callback +will be used by the MT76 driver in order to offload the traffic received +by the wlan NIC and forwarded to the ethernet one. +Add rx_wlan parameter to airoha_ppe_check_skb routine signature. + +Signed-off-by: Lorenzo Bianconi +Link: https://patch.msgid.link/20250823-airoha-en7581-wlan-rx-offload-v3-3-f78600ec3ed8@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 3 ++- + drivers/net/ethernet/airoha/airoha_eth.h | 8 ++------ + drivers/net/ethernet/airoha/airoha_ppe.c | 25 +++++++++++++---------- + include/linux/soc/airoha/airoha_offload.h | 20 ++++++++++++++++++ + 4 files changed, 38 insertions(+), 18 deletions(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -703,7 +703,8 @@ static int airoha_qdma_rx_process(struct + + reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); + if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) +- airoha_ppe_check_skb(eth->ppe, q->skb, hash); ++ airoha_ppe_check_skb(ð->ppe->dev, q->skb, hash, ++ false); + + done++; + napi_gro_receive(&q->napi, q->skb); +--- a/drivers/net/ethernet/airoha/airoha_eth.h ++++ b/drivers/net/ethernet/airoha/airoha_eth.h +@@ -230,10 +230,6 @@ struct airoha_hw_stats { + }; + + enum { +- PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f, +-}; +- +-enum { + AIROHA_FOE_STATE_INVALID, + AIROHA_FOE_STATE_UNBIND, + AIROHA_FOE_STATE_BIND, +@@ -622,8 +618,8 @@ static inline bool airhoa_is_lan_gdm_por + bool airoha_is_valid_gdm_port(struct airoha_eth *eth, + struct airoha_gdm_port *port); + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, +- u16 hash); ++void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, ++ u16 hash, bool rx_wlan); + int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data); + int airoha_ppe_init(struct airoha_eth *eth); + void airoha_ppe_deinit(struct airoha_eth *eth); +--- a/drivers/net/ethernet/airoha/airoha_ppe.c ++++ b/drivers/net/ethernet/airoha/airoha_ppe.c +@@ -616,7 +616,7 @@ static bool airoha_ppe_foe_compare_entry + + static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe, + struct airoha_foe_entry *e, +- u32 hash) ++ u32 hash, bool rx_wlan) + { + struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe); + u32 ts = airoha_ppe_get_timestamp(ppe); +@@ -639,7 +639,8 @@ static int airoha_ppe_foe_commit_entry(s + goto unlock; + } + +- airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); ++ if (!rx_wlan) ++ airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash); + + if (hash < PPE_SRAM_NUM_ENTRIES) { + dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe); +@@ -665,7 +666,7 @@ static void airoha_ppe_foe_remove_flow(s + e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; + e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, + AIROHA_FOE_STATE_INVALID); +- airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); ++ airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash, false); + e->hash = 0xffff; + } + if (e->type == FLOW_TYPE_L2_SUBFLOW) { +@@ -704,7 +705,7 @@ static void airoha_ppe_foe_flow_remove_e + static int + airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, + struct airoha_flow_table_entry *e, +- u32 hash) ++ u32 hash, bool rx_wlan) + { + u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; + struct airoha_foe_entry *hwe_p, hwe; +@@ -745,14 +746,14 @@ airoha_ppe_foe_commit_subflow_entry(stru + } + + hwe.bridge.data = e->data.bridge.data; +- airoha_ppe_foe_commit_entry(ppe, &hwe, hash); ++ airoha_ppe_foe_commit_entry(ppe, &hwe, hash, rx_wlan); + + return 0; + } + + static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, + struct sk_buff *skb, +- u32 hash) ++ u32 hash, bool rx_wlan) + { + struct airoha_flow_table_entry *e; + struct airoha_foe_bridge br = {}; +@@ -785,7 +786,7 @@ static void airoha_ppe_foe_insert_entry( + if (!airoha_ppe_foe_compare_entry(e, hwe)) + continue; + +- airoha_ppe_foe_commit_entry(ppe, &e->data, hash); ++ airoha_ppe_foe_commit_entry(ppe, &e->data, hash, rx_wlan); + commit_done = true; + e->hash = hash; + } +@@ -797,7 +798,7 @@ static void airoha_ppe_foe_insert_entry( + e = rhashtable_lookup_fast(&ppe->l2_flows, &br, + airoha_l2_flow_table_params); + if (e) +- airoha_ppe_foe_commit_subflow_entry(ppe, e, hash); ++ airoha_ppe_foe_commit_subflow_entry(ppe, e, hash, rx_wlan); + unlock: + spin_unlock_bh(&ppe_lock); + } +@@ -1301,9 +1302,10 @@ int airoha_ppe_setup_tc_block_cb(struct + return err; + } + +-void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, +- u16 hash) ++void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, ++ u16 hash, bool rx_wlan) + { ++ struct airoha_ppe *ppe = dev->priv; + u16 now, diff; + + if (hash > PPE_HASH_MASK) +@@ -1315,7 +1317,7 @@ void airoha_ppe_check_skb(struct airoha_ + return; + + ppe->foe_check_time[hash] = now; +- airoha_ppe_foe_insert_entry(ppe, skb, hash); ++ airoha_ppe_foe_insert_entry(ppe, skb, hash, rx_wlan); + } + + void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port) +@@ -1404,6 +1406,7 @@ int airoha_ppe_init(struct airoha_eth *e + return -ENOMEM; + + ppe->dev.ops.setup_tc_block_cb = airoha_ppe_setup_tc_block_cb; ++ ppe->dev.ops.check_skb = airoha_ppe_check_skb; + ppe->dev.priv = ppe; + + foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry); +--- a/include/linux/soc/airoha/airoha_offload.h ++++ b/include/linux/soc/airoha/airoha_offload.h +@@ -9,10 +9,17 @@ + #include + #include + ++enum { ++ PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f, ++}; ++ + struct airoha_ppe_dev { + struct { + int (*setup_tc_block_cb)(struct airoha_ppe_dev *dev, + void *type_data); ++ void (*check_skb)(struct airoha_ppe_dev *dev, ++ struct sk_buff *skb, u16 hash, ++ bool rx_wlan); + } ops; + + void *priv; +@@ -27,6 +34,13 @@ static inline int airoha_ppe_dev_setup_t + { + return dev->ops.setup_tc_block_cb(dev, type_data); + } ++ ++static inline void airoha_ppe_dev_check_skb(struct airoha_ppe_dev *dev, ++ struct sk_buff *skb, ++ u16 hash, bool rx_wlan) ++{ ++ dev->ops.check_skb(dev, skb, hash, rx_wlan); ++} + #else + static inline struct airoha_ppe_dev *airoha_ppe_get_dev(struct device *dev) + { +@@ -42,6 +56,12 @@ static inline int airoha_ppe_setup_tc_bl + { + return -EOPNOTSUPP; + } ++ ++static inline void airoha_ppe_dev_check_skb(struct airoha_ppe_dev *dev, ++ struct sk_buff *skb, u16 hash, ++ bool rx_wlan) ++{ ++} + #endif + + #define NPU_NUM_CORES 8 diff --git a/target/linux/airoha/patches-6.6/087-v6.17-pinctrl-airoha-Fix-return-value-in-pinconf-callbacks.patch b/target/linux/airoha/patches-6.6/087-v6.17-pinctrl-airoha-Fix-return-value-in-pinconf-callbacks.patch new file mode 100644 index 00000000000000..f12b941e74dc14 --- /dev/null +++ b/target/linux/airoha/patches-6.6/087-v6.17-pinctrl-airoha-Fix-return-value-in-pinconf-callbacks.patch @@ -0,0 +1,50 @@ +From 563fcd6475931c5c8c652a4dd548256314cc87ed Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Fri, 22 Aug 2025 14:14:18 +0200 +Subject: [PATCH] pinctrl: airoha: Fix return value in pinconf callbacks + +Pinctrl stack requires ENOTSUPP error code if the parameter is not +supported by the pinctrl driver. Fix the returned error code in pinconf +callbacks if the operation is not supported. + +Fixes: 1c8ace2d0725 ("pinctrl: airoha: Add support for EN7581 SoC") +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/20250822-airoha-pinconf-err-val-fix-v1-1-87b4f264ced2@kernel.org +Signed-off-by: Linus Walleij +--- + drivers/pinctrl/mediatek/pinctrl-airoha.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/pinctrl/mediatek/pinctrl-airoha.c ++++ b/drivers/pinctrl/mediatek/pinctrl-airoha.c +@@ -2696,7 +2696,7 @@ static int airoha_pinconf_get(struct pin + arg = 1; + break; + default: +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); +@@ -2790,7 +2790,7 @@ static int airoha_pinconf_set(struct pin + break; + } + default: +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + } + } + +@@ -2807,10 +2807,10 @@ static int airoha_pinconf_group_get(stru + if (airoha_pinconf_get(pctrl_dev, + airoha_pinctrl_groups[group].pins[i], + config)) +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + + if (i && cur_config != *config) +- return -EOPNOTSUPP; ++ return -ENOTSUPP; + + cur_config = *config; + } diff --git a/target/linux/airoha/patches-6.6/089-v6.14-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch b/target/linux/airoha/patches-6.6/089-v6.14-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch new file mode 100644 index 00000000000000..41f7570e32b6c7 --- /dev/null +++ b/target/linux/airoha/patches-6.6/089-v6.14-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch @@ -0,0 +1,36 @@ +From 7d0da8f862340c5f42f0062b8560b8d0971a6ac4 Mon Sep 17 00:00:00 2001 +From: Lorenzo Bianconi +Date: Tue, 7 Jan 2025 23:26:28 +0100 +Subject: [PATCH] net: airoha: Fix channel configuration for ETS Qdisc + +Limit ETS QoS channel to AIROHA_NUM_QOS_CHANNELS in +airoha_tc_setup_qdisc_ets() in order to align the configured channel to +the value set in airoha_dev_select_queue(). + +Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support") +Signed-off-by: Lorenzo Bianconi +Reviewed-by: Michal Swiatkowski +Link: https://patch.msgid.link/20250107-airoha-ets-fix-chan-v1-1-97f66ed3a068@kernel.org +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/airoha/airoha_eth.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/airoha/airoha_eth.c ++++ b/drivers/net/ethernet/airoha/airoha_eth.c +@@ -2184,11 +2184,14 @@ static int airoha_qdma_get_tx_ets_stats( + static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, + struct tc_ets_qopt_offload *opt) + { +- int channel = TC_H_MAJ(opt->handle) >> 16; ++ int channel; + + if (opt->parent == TC_H_ROOT) + return -EINVAL; + ++ channel = TC_H_MAJ(opt->handle) >> 16; ++ channel = channel % AIROHA_NUM_QOS_CHANNELS; ++ + switch (opt->command) { + case TC_ETS_REPLACE: + return airoha_qdma_set_tx_ets_sched(port, channel, opt); diff --git a/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch b/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch deleted file mode 100644 index d646e05170368a..00000000000000 --- a/target/linux/airoha/patches-6.6/101-01-thermal-of-Add-devm_thermal_of_zone_register_with_pa.patch +++ /dev/null @@ -1,210 +0,0 @@ -From 1f194995c3648e20da53137d4c9110b39e779f41 Mon Sep 17 00:00:00 2001 -From: Christian Marangi -Date: Fri, 18 Oct 2024 11:34:35 +0200 -Subject: [PATCH 2/3] thermal: of: Add - devm_thermal_of_zone_register_with_params() variant - -Commit b1ae92dcfa8e ("thermal: core: Make struct thermal_zone_device -definition internal") moved the thermal_zone_device struct from global -thermal.h to internal thermal_core.h making the internal variables of -the struct not accessible from the user drivers (without inclusing -thermal_core.h). - -One case where the internal variables might be needed is for the -thermal_zone_params in the context of OF probe. - -In such case a thermal driver might have default params that can only be -parsed at runtime (example present in EFUSE or derived from other values) -and wants to update the values in the thermal_zone_params for the -thermal device. (to use the helper like get_slope() or get_offset()) - -To account for this scenario, introduce a variant of -devm_thermal_of_zone_register(), -devm_thermal_of_zone_register_with_params(), that takes and additional -variable and permits to register the thermal device with default -thermal_zone_params. - -To follow OF implementation, these params are only treated as default -params and are ignored if a related one is defined in DT. (example a -slope or offset value defined in DT have priority to the default one -passed in a thermal_device_params struct) - -This permits to support both implementation, use the helpers and expose -these values in sysfs. - -Signed-off-by: Christian Marangi ---- - drivers/thermal/thermal_of.c | 67 +++++++++++++++++++++++++++++------- - include/linux/thermal.h | 13 +++++++ - 2 files changed, 68 insertions(+), 12 deletions(-) - ---- a/drivers/thermal/thermal_of.c -+++ b/drivers/thermal/thermal_of.c -@@ -246,7 +246,7 @@ static void thermal_of_parameters_init(s - { - int coef[2]; - int ncoef = ARRAY_SIZE(coef); -- int prop, ret; -+ int prop; - - tzp->no_hwmon = true; - -@@ -258,14 +258,11 @@ static void thermal_of_parameters_init(s - * thermal zone. Thus, we are considering only the first two - * values as slope and offset. - */ -- ret = of_property_read_u32_array(np, "coefficients", coef, ncoef); -- if (ret) { -- coef[0] = 1; -- coef[1] = 0; -+ if (!of_property_read_u32_array(np, "coefficients", coef, ncoef)) { -+ tzp->slope = coef[0]; -+ tzp->offset = coef[1]; - } - -- tzp->slope = coef[0]; -- tzp->offset = coef[1]; - } - - static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_device *tz) -@@ -459,10 +456,15 @@ static void thermal_of_zone_unregister(s - * zone properties and registers new thermal zone with those - * properties. - * -+ * The passed thermal zone params are treated as default values and ignored if -+ * the related property is found in DT. (DT params have priority to -+ * default values) -+ * - * @sensor: A device node pointer corresponding to the sensor in the device tree - * @id: An integer as sensor identifier - * @data: A private data to be stored in the thermal zone dedicated private area - * @ops: A set of thermal sensor ops -+ * @tzp: a pointer to the default thermal zone params structure associated with the sensor - * - * Return: a valid thermal zone structure pointer on success. - * - EINVAL: if the device tree thermal description is malformed -@@ -470,11 +472,11 @@ static void thermal_of_zone_unregister(s - * - Other negative errors are returned by the underlying called functions - */ - static struct thermal_zone_device *thermal_of_zone_register(struct device_node *sensor, int id, void *data, -- const struct thermal_zone_device_ops *ops) -+ const struct thermal_zone_device_ops *ops, -+ struct thermal_zone_params *tzp) - { - struct thermal_zone_device *tz; - struct thermal_trip *trips; -- struct thermal_zone_params tzp = {}; - struct thermal_zone_device_ops *of_ops; - struct device_node *np; - int delay, pdelay; -@@ -509,7 +511,7 @@ static struct thermal_zone_device *therm - goto out_kfree_trips; - } - -- thermal_of_parameters_init(np, &tzp); -+ thermal_of_parameters_init(np, tzp); - - of_ops->bind = thermal_of_bind; - of_ops->unbind = thermal_of_unbind; -@@ -517,7 +519,7 @@ static struct thermal_zone_device *therm - mask = GENMASK_ULL((ntrips) - 1, 0); - - tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips, -- mask, data, of_ops, &tzp, -+ mask, data, of_ops, tzp, - pdelay, delay); - if (IS_ERR(tz)) { - ret = PTR_ERR(tz); -@@ -572,6 +574,7 @@ static int devm_thermal_of_zone_match(st - struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, int sensor_id, void *data, - const struct thermal_zone_device_ops *ops) - { -+ struct thermal_zone_params tzp = { .slope = 1 }; - struct thermal_zone_device **ptr, *tzd; - - ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr), -@@ -579,7 +582,7 @@ struct thermal_zone_device *devm_thermal - if (!ptr) - return ERR_PTR(-ENOMEM); - -- tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops); -+ tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops, &tzp); - if (IS_ERR(tzd)) { - devres_free(ptr); - return tzd; -@@ -593,6 +596,46 @@ struct thermal_zone_device *devm_thermal - EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register); - - /** -+ * devm_thermal_of_zone_register_with_params - register a thermal tied with the sensor life cycle -+ * with default params -+ * -+ * This function is the device version of the thermal_of_zone_register() function. -+ * -+ * @dev: a device structure pointer to sensor to be tied with the thermal zone OF life cycle -+ * @sensor_id: the sensor identifier -+ * @data: a pointer to a private data to be stored in the thermal zone 'devdata' field -+ * @ops: a pointer to the ops structure associated with the sensor -+ * @tzp: a pointer to the default thermal zone params structure associated with the sensor -+ * -+ * The thermal zone params are treated as default values and ignored if the related property is -+ * found in DT. (DT params have priority to default values) -+ */ -+struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int sensor_id, -+ void *data, -+ const struct thermal_zone_device_ops *ops, -+ struct thermal_zone_params *tzp) -+{ -+ struct thermal_zone_device **ptr, *tzd; -+ -+ ptr = devres_alloc(devm_thermal_of_zone_release, sizeof(*ptr), -+ GFP_KERNEL); -+ if (!ptr) -+ return ERR_PTR(-ENOMEM); -+ -+ tzd = thermal_of_zone_register(dev->of_node, sensor_id, data, ops, tzp); -+ if (IS_ERR(tzd)) { -+ devres_free(ptr); -+ return tzd; -+ } -+ -+ *ptr = tzd; -+ devres_add(dev, ptr); -+ -+ return tzd; -+} -+EXPORT_SYMBOL_GPL(devm_thermal_of_zone_register_with_params); -+ -+/** - * devm_thermal_of_zone_unregister - Resource managed version of - * thermal_of_zone_unregister(). - * @dev: Device for which which resource was allocated. ---- a/include/linux/thermal.h -+++ b/include/linux/thermal.h -@@ -263,6 +263,10 @@ struct thermal_zone_params { - #ifdef CONFIG_THERMAL_OF - struct thermal_zone_device *devm_thermal_of_zone_register(struct device *dev, int id, void *data, - const struct thermal_zone_device_ops *ops); -+struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int id, -+ void *data, -+ const struct thermal_zone_device_ops *ops, -+ struct thermal_zone_params *tzp); - - void devm_thermal_of_zone_unregister(struct device *dev, struct thermal_zone_device *tz); - -@@ -274,6 +278,15 @@ struct thermal_zone_device *devm_thermal - { - return ERR_PTR(-ENOTSUPP); - } -+ -+static inline -+struct thermal_zone_device *devm_thermal_of_zone_register_with_params(struct device *dev, int id, -+ void *data, -+ const struct thermal_zone_device_ops *ops, -+ struct thermal_zone_params *tzp) -+{ -+ return ERR_PTR(-ENOTSUPP); -+} - - static inline void devm_thermal_of_zone_unregister(struct device *dev, - struct thermal_zone_device *tz) diff --git a/target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch b/target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch deleted file mode 100644 index 21896d7fa998a5..00000000000000 --- a/target/linux/airoha/patches-6.6/113-net-airoha-Fix-channel-configuration-for-ETS-Qdisc.patch +++ /dev/null @@ -1,104 +0,0 @@ -From patchwork Tue Jan 7 22:26:28 2025 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Patchwork-Submitter: Lorenzo Bianconi -X-Patchwork-Id: 13929634 -X-Patchwork-Delegate: kuba@kernel.org -Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org - [10.30.226.201]) - (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) - (No client certificate requested) - by smtp.subspace.kernel.org (Postfix) with ESMTPS id A82271A3035 - for ; Tue, 7 Jan 2025 22:27:02 +0000 (UTC) -Authentication-Results: smtp.subspace.kernel.org; - arc=none smtp.client-ip=10.30.226.201 -ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; - t=1736288822; cv=none; - b=XZhiaYPBxLiUvOxWeE7zfuFI3fOmu5SLoHdLPFXNXBtvmZWWIohKA8AeGI37v/l+0Du9JwGRKMkb19v/IxDJtMXkyTJXHHKYhXWaNFpj/pFRk9C4WsIa29OCqanfA+yXUQLJyGVopMLsxfcbzznozIANWbaO0NVBHyZZSH9eaYU= -ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; - s=arc-20240116; t=1736288822; c=relaxed/simple; - bh=/BuvRwLGk+7by7QeOu7n+QgJ5Sk03TO9WCsGbgTs3sE=; - h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; - b=jHXwJfD+6o5WvM5xaeL35BI6hshOViNtg+mSqf5q8jH9l3k6FctngCkEYdxJzcYaw9aEEigC8/FZiHoPrIXGyJA29kTWkYSjj7rtagL1aSIWPGAuSJaaAUv2Bj8jxUmIlJxb23wTleEv/Pwnz+1oSf3yZ7g46h9gv4RZaU8yySg= -ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b=Gx3FMrdJ; arc=none smtp.client-ip=10.30.226.201 -Authentication-Results: smtp.subspace.kernel.org; - dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org - header.b="Gx3FMrdJ" -Received: by smtp.kernel.org (Postfix) with ESMTPSA id E1A57C4CED6; - Tue, 7 Jan 2025 22:27:01 +0000 (UTC) -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; - s=k20201202; t=1736288822; - bh=/BuvRwLGk+7by7QeOu7n+QgJ5Sk03TO9WCsGbgTs3sE=; - h=From:Date:Subject:To:Cc:From; - b=Gx3FMrdJH+xaen2jSRnu523A40ZOBBFaj896IwBv1PeosUm+eiUCx+K3Qz9CAisX0 - Bj4ohheTiHZDQHZelhKF3ZFTfVQUyYiLiard4x5QdylW2YZA0cpwJe64TMf7CsHbrT - NHCF7nrJPJUwOhDoS/YVdeTw/bb9DlM95aKGSfyH0cy7Kdmjz55No3Im9bCSKcgyaX - Y/lcRZglFjbLyiC3LS06AtM0KOyhUxQKrH+ZWpx5E/sdOEj3SRTJ/I+K8o3m75Kzsn - wKRft5pBwfhGEIrJXrFR4f73QwnxJ6eSUrfjYV8k4mFQpH3nB0hKLi2DpvYPim5dj/ - ADsdcP6QPwokg== -From: Lorenzo Bianconi -Date: Tue, 07 Jan 2025 23:26:28 +0100 -Subject: [PATCH net-next] net: airoha: Fix channel configuration for ETS - Qdisc -Precedence: bulk -X-Mailing-List: netdev@vger.kernel.org -List-Id: -List-Subscribe: -List-Unsubscribe: -MIME-Version: 1.0 -Message-Id: <20250107-airoha-ets-fix-chan-v1-1-97f66ed3a068@kernel.org> -X-B4-Tracking: v=1; b=H4sIABOqfWcC/x2MSwqAMAwFryJZG6ifVvAq4qLUaLNRaUSE0rsbX - Q5v3mQQSkwCY5Uh0c3Cx67Q1BWE6PeNkBdlaE1rTWMG9JyO6JEuwZUf/CSkzobgw+AW14M+z0S - 6/dVpLuUFNrlCSGUAAAA= -X-Change-ID: 20250107-airoha-ets-fix-chan-e35ccac76d64 -To: Felix Fietkau , Sean Wang , - Mark Lee , Andrew Lunn , - "David S. Miller" , Eric Dumazet , - Jakub Kicinski , Paolo Abeni , - Matthias Brugger , - AngeloGioacchino Del Regno -Cc: linux-arm-kernel@lists.infradead.org, - linux-mediatek@lists.infradead.org, netdev@vger.kernel.org, - Lorenzo Bianconi -X-Mailer: b4 0.14.2 -X-Patchwork-Delegate: kuba@kernel.org - -Limit ETS QoS channel to AIROHA_NUM_QOS_CHANNELS in -airoha_tc_setup_qdisc_ets() in order to align the configured channel to -the value set in airoha_dev_select_queue(). - -Fixes: 20bf7d07c956 ("net: airoha: Add sched ETS offload support") -Signed-off-by: Lorenzo Bianconi -Reviewed-by: Michal Swiatkowski ---- - drivers/net/ethernet/mediatek/airoha_eth.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - - ---- -base-commit: a1942da8a38717ddd9b4c132f59e1657c85c1432 -change-id: 20250107-airoha-ets-fix-chan-e35ccac76d64 - -Best regards, - ---- a/drivers/net/ethernet/airoha/airoha_eth.c -+++ b/drivers/net/ethernet/airoha/airoha_eth.c -@@ -2064,11 +2064,14 @@ static int airoha_qdma_get_tx_ets_stats( - static int airoha_tc_setup_qdisc_ets(struct airoha_gdm_port *port, - struct tc_ets_qopt_offload *opt) - { -- int channel = TC_H_MAJ(opt->handle) >> 16; -+ int channel; - - if (opt->parent == TC_H_ROOT) - return -EINVAL; - -+ channel = TC_H_MAJ(opt->handle) >> 16; -+ channel = channel % AIROHA_NUM_QOS_CHANNELS; -+ - switch (opt->command) { - case TC_ETS_REPLACE: - return airoha_qdma_set_tx_ets_sched(port, channel, opt); diff --git a/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch new file mode 100644 index 00000000000000..cda52269042b1f --- /dev/null +++ b/target/linux/generic/backport-6.12/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch @@ -0,0 +1,303 @@ +From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:45 +0200 +Subject: [PATCH] net: phy: pass PHY driver to .match_phy_device OP + +Pass PHY driver pointer to .match_phy_device OP in addition to phydev. +Having access to the PHY driver struct might be useful to check the +PHY ID of the driver is being matched for in case the PHY ID scanned in +the phydev is not consistent. + +A scenario for this is a PHY that change PHY ID after a firmware is +loaded, in such case, the PHY ID stored in PHY device struct is not +valid anymore and PHY will manually scan the ID in the match_phy_device +function. + +Having the PHY driver info is also useful for those PHY driver that +implement multiple simple .match_phy_device OP to match specific MMD PHY +ID. With this extra info if the parsing logic is the same, the matching +function can be generalized by using the phy_id in the PHY driver +instead of hardcoding. + +Rust wrapper callback is updated to align to the new match_phy_device +arguments. + +Suggested-by: Russell King (Oracle) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Reviewed-by: Benno Lossin # for Rust +Reviewed-by: FUJITA Tomonori +Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/bcm87xx.c | 6 ++++-- + drivers/net/phy/icplus.c | 6 ++++-- + drivers/net/phy/marvell10g.c | 12 ++++++++---- + drivers/net/phy/micrel.c | 6 ++++-- + drivers/net/phy/nxp-c45-tja11xx.c | 12 ++++++++---- + drivers/net/phy/nxp-tja11xx.c | 6 ++++-- + drivers/net/phy/phy_device.c | 2 +- + drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++--------- + drivers/net/phy/teranetics.c | 3 ++- + include/linux/phy.h | 3 ++- + rust/kernel/net/phy.rs | 1 + + 11 files changed, 56 insertions(+), 28 deletions(-) + +--- a/drivers/net/phy/bcm87xx.c ++++ b/drivers/net/phy/bcm87xx.c +@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr + return IRQ_HANDLED; + } + +-static int bcm8706_match_phy_device(struct phy_device *phydev) ++static int bcm8706_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706; + } + +-static int bcm8727_match_phy_device(struct phy_device *phydev) ++static int bcm8727_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727; + } +--- a/drivers/net/phy/icplus.c ++++ b/drivers/net/phy/icplus.c +@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str + return ip101a == !ret; + } + +-static int ip101a_match_phy_device(struct phy_device *phydev) ++static int ip101a_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, true); + } + +-static int ip101g_match_phy_device(struct phy_device *phydev) ++static int ip101g_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -1284,7 +1284,8 @@ static int mv3310_get_number_of_ports(st + return ret + 1; + } + +-static int mv3310_match_phy_device(struct phy_device *phydev) ++static int mv3310_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1293,7 +1294,8 @@ static int mv3310_match_phy_device(struc + return mv3310_get_number_of_ports(phydev) == 1; + } + +-static int mv3340_match_phy_device(struct phy_device *phydev) ++static int mv3340_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1317,12 +1319,14 @@ static int mv211x_match_phy_device(struc + return !!(val & MDIO_PCS_SPEED_5G) == has_5g; + } + +-static int mv2110_match_phy_device(struct phy_device *phydev) ++static int mv2110_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, true); + } + +-static int mv2111_match_phy_device(struct phy_device *phydev) ++static int mv2111_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -768,7 +768,8 @@ static int ksz8051_ksz8795_match_phy_dev + return !ret; + } + +-static int ksz8051_match_phy_device(struct phy_device *phydev) ++static int ksz8051_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, true); + } +@@ -888,7 +889,8 @@ static int ksz8061_config_init(struct ph + return kszphy_config_init(phydev); + } + +-static int ksz8795_match_phy_device(struct phy_device *phydev) ++static int ksz8795_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/nxp-c45-tja11xx.c ++++ b/drivers/net/phy/nxp-c45-tja11xx.c +@@ -1944,13 +1944,15 @@ static int nxp_c45_macsec_ability(struct + return macsec_ability; + } + +-static int tja1103_match_phy_device(struct phy_device *phydev) ++static int tja1103_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) && + !nxp_c45_macsec_ability(phydev); + } + +-static int tja1104_match_phy_device(struct phy_device *phydev) ++static int tja1104_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phy_id_compare(phydev->phy_id, PHY_ID_TJA_1103, PHY_ID_MASK) && + nxp_c45_macsec_ability(phydev); +--- a/drivers/net/phy/nxp-tja11xx.c ++++ b/drivers/net/phy/nxp-tja11xx.c +@@ -646,12 +646,14 @@ static int tja1102_match_phy_device(stru + return !ret; + } + +-static int tja1102_p0_match_phy_device(struct phy_device *phydev) ++static int tja1102_p0_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, true); + } + +-static int tja1102_p1_match_phy_device(struct phy_device *phydev) ++static int tja1102_p1_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -600,7 +600,7 @@ static int phy_bus_match(struct device * + return 0; + + if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev); ++ return phydrv->match_phy_device(phydev, phydrv); + + if (phydev->is_c45) { + for (i = 1; i < num_ids; i++) { +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1343,13 +1343,15 @@ static bool rtlgen_supports_mmd(struct p + return val > 0; + } + +-static int rtlgen_match_phy_device(struct phy_device *phydev) ++static int rtlgen_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + !rtlgen_supports_2_5gbps(phydev); + } + +-static int rtl8226_match_phy_device(struct phy_device *phydev) ++static int rtl8226_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + rtlgen_supports_2_5gbps(phydev) && +@@ -1365,32 +1367,38 @@ static int rtlgen_is_c45_match(struct ph + return !is_c45 && (id == phydev->phy_id); + } + +-static int rtl8221b_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); + } + +-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); + } + +-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); + } + +-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); + } + +-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); + } + +-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) ++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if (phydev->is_c45) + return false; +@@ -1409,7 +1417,8 @@ static int rtl_internal_nbaset_match_phy + return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); + } + +-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8251B, true); + } +--- a/drivers/net/phy/teranetics.c ++++ b/drivers/net/phy/teranetics.c +@@ -67,7 +67,8 @@ static int teranetics_read_status(struct + return 0; + } + +-static int teranetics_match_phy_device(struct phy_device *phydev) ++static int teranetics_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1004,7 +1004,8 @@ struct phy_driver { + * driver for the given phydev. If NULL, matching is based on + * phy_id and phy_id_mask. + */ +- int (*match_phy_device)(struct phy_device *phydev); ++ int (*match_phy_device)(struct phy_device *phydev, ++ const struct phy_driver *phydrv); + + /** + * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY +--- a/rust/kernel/net/phy.rs ++++ b/rust/kernel/net/phy.rs +@@ -421,6 +421,7 @@ impl Adapter { + /// `phydev` must be passed by the corresponding callback in `phy_driver`. + unsafe extern "C" fn match_phy_device_callback( + phydev: *mut bindings::phy_device, ++ _phydrv: *const bindings::phy_driver, + ) -> crate::ffi::c_int { + // SAFETY: This callback is called only in contexts + // where we hold `phy_device->lock`, so the accessors on diff --git a/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch new file mode 100644 index 00000000000000..9e28011b0cc3e6 --- /dev/null +++ b/target/linux/generic/backport-6.12/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch @@ -0,0 +1,109 @@ +From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:48 +0200 +Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device() + +Introduce new API, genphy_match_phy_device(), to provide a way to check +to match a PHY driver for a PHY device based on the info stored in the +PHY device struct. + +The function generalize the logic used in phy_bus_match() to check the +PHY ID whether if C45 or C22 ID should be used for matching. + +This is useful for custom .match_phy_device function that wants to use +the generic logic under some condition. (example a PHY is already setup +and provide the correct PHY ID) + +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++----------- + include/linux/phy.h | 3 +++ + 2 files changed, 40 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -589,20 +589,26 @@ static int phy_scan_fixups(struct phy_de + return 0; + } + +-static int phy_bus_match(struct device *dev, const struct device_driver *drv) ++/** ++ * genphy_match_phy_device - match a PHY device with a PHY driver ++ * @phydev: target phy_device struct ++ * @phydrv: target phy_driver struct ++ * ++ * Description: Checks whether the given PHY device matches the specified ++ * PHY driver. For Clause 45 PHYs, iterates over the available device ++ * identifiers and compares them against the driver's expected PHY ID, ++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison ++ * is performed. ++ * ++ * Return: 1 if the PHY device matches the driver, 0 otherwise. ++ */ ++int genphy_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { +- struct phy_device *phydev = to_phy_device(dev); +- const struct phy_driver *phydrv = to_phy_driver(drv); +- const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); +- int i; +- +- if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) +- return 0; +- +- if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev, phydrv); +- + if (phydev->is_c45) { ++ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); ++ int i; ++ + for (i = 1; i < num_ids; i++) { + if (phydev->c45_ids.device_ids[i] == 0xffffffff) + continue; +@@ -611,11 +617,27 @@ static int phy_bus_match(struct device * + phydrv->phy_id, phydrv->phy_id_mask)) + return 1; + } ++ + return 0; +- } else { +- return phy_id_compare(phydev->phy_id, phydrv->phy_id, +- phydrv->phy_id_mask); + } ++ ++ return phy_id_compare(phydev->phy_id, phydrv->phy_id, ++ phydrv->phy_id_mask); ++} ++EXPORT_SYMBOL_GPL(genphy_match_phy_device); ++ ++static int phy_bus_match(struct device *dev, const struct device_driver *drv) ++{ ++ struct phy_device *phydev = to_phy_device(dev); ++ const struct phy_driver *phydrv = to_phy_driver(drv); ++ ++ if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) ++ return 0; ++ ++ if (phydrv->match_phy_device) ++ return phydrv->match_phy_device(phydev, phydrv); ++ ++ return genphy_match_phy_device(phydev, phydrv); + } + + static ssize_t +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1906,6 +1906,9 @@ char *phy_attached_info_irq(struct phy_d + __malloc; + void phy_attached_info(struct phy_device *phydev); + ++int genphy_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv); ++ + /* Clause 22 PHY */ + int genphy_read_abilities(struct phy_device *phydev); + int genphy_setup_forced(struct phy_device *phydev); diff --git a/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch new file mode 100644 index 00000000000000..bcf9b1a6e5bf1d --- /dev/null +++ b/target/linux/generic/backport-6.12/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch @@ -0,0 +1,1165 @@ +From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:49 +0200 +Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs + +Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate +an IPC to setup some configuration and require special handling to +sync with the parity bit. The parity bit is a way the IPC use to +follow correct order of command sent. + +Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, +AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, +AS21210PB1 that all register with the PHY ID 0x7500 0x7510 +before the firmware is loaded. + +They all support up to 5 LEDs with various HW mode supported. + +While implementing it was found some strange coincidence with using the +same logic for implementing C22 in MMD regs in Broadcom PHYs. + +For reference here the AS21xxx PHY name logic: + +AS21x1xxB1 + ^ ^^ + | |J: Supports SyncE/PTP + | |P: No SyncE/PTP support + | 1: Supports 2nd Serdes + | 2: Not 2nd Serdes support + 0: 10G, 5G, 2.5G + 5: 5G, 2.5G + 2: 2.5G + +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 6 + + drivers/net/phy/Kconfig | 12 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 1106 insertions(+) + create mode 100644 drivers/net/phy/as21xxx.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -79,6 +79,18 @@ config SFP + + comment "MII PHY device drivers" + ++config AS21XXX_PHY ++ tristate "Aeonsemi AS21xxx PHYs" ++ help ++ Currently supports the Aeonsemi AS21xxx PHY. ++ ++ These are C45 PHYs 10G that require all a generic firmware. ++ ++ Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, ++ AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, ++ AS21210PB1 that all register with the PHY ID 0x7500 0x7500 ++ before the firmware is loaded. ++ + config AIR_EN8811H_PHY + tristate "Airoha EN8811H 2.5 Gigabit PHY" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_AIR_EN8811H_PHY) += air_e + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AMCC_QT2025_PHY) += qt2025.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ ++obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o + ifdef CONFIG_AX88796B_RUST_PHY + obj-$(CONFIG_AX88796B_PHY) += ax88796b_rust.o + else +--- /dev/null ++++ b/drivers/net/phy/as21xxx.c +@@ -0,0 +1,1087 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Aeonsemi AS21XXxX PHY Driver ++ * ++ * Author: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3 ++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4 ++ ++#define VEND1_GLB_REG_CPU_CTRL 0xe ++#define VEND1_GLB_CPU_CTRL_MASK GENMASK(4, 0) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \ ++ BIT(_n)) ++ ++#define VEND1_FW_START_ADDR 0x100 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101 ++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103 ++ ++#define VEND1_PTP_CLK 0x142 ++#define VEND1_PTP_CLK_EN BIT(6) ++ ++/* 5 LED at step of 0x20 ++ * FE: Fast-Ethernet (10/100) ++ * GE: Gigabit-Ethernet (1000) ++ * NG: New-Generation (2500/5000/10000) ++ */ ++#define VEND1_LED_REG(_n) (0x1800 + ((_n) * 0x10)) ++#define VEND1_LED_REG_A_EVENT GENMASK(15, 11) ++#define VEND1_LED_CONF 0x1881 ++#define VEND1_LED_CONFG_BLINK GENMASK(7, 0) ++ ++#define VEND1_SPEED_STATUS 0x4002 ++#define VEND1_SPEED_MASK GENMASK(7, 0) ++#define VEND1_SPEED_10000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3) ++#define VEND1_SPEED_5000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5) ++#define VEND1_SPEED_2500 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9) ++#define VEND1_SPEED_1000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10) ++#define VEND1_SPEED_100 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20) ++#define VEND1_SPEED_10 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0) ++ ++#define VEND1_IPC_CMD 0x5801 ++#define AEON_IPC_CMD_PARITY BIT(15) ++#define AEON_IPC_CMD_SIZE GENMASK(10, 6) ++#define AEON_IPC_CMD_OPCODE GENMASK(5, 0) ++ ++#define IPC_CMD_NOOP 0x0 /* Do nothing */ ++#define IPC_CMD_INFO 0x1 /* Get Firmware Version */ ++#define IPC_CMD_SYS_CPU 0x2 /* SYS_CPU */ ++#define IPC_CMD_BULK_DATA 0xa /* Pass bulk data in ipc registers. */ ++#define IPC_CMD_BULK_WRITE 0xc /* Write bulk data to memory */ ++#define IPC_CMD_CFG_PARAM 0x1a /* Write config parameters to memory */ ++#define IPC_CMD_NG_TESTMODE 0x1b /* Set NG test mode and tone */ ++#define IPC_CMD_TEMP_MON 0x15 /* Temperature monitoring function */ ++#define IPC_CMD_SET_LED 0x23 /* Set led */ ++ ++#define VEND1_IPC_STS 0x5802 ++#define AEON_IPC_STS_PARITY BIT(15) ++#define AEON_IPC_STS_SIZE GENMASK(14, 10) ++#define AEON_IPC_STS_OPCODE GENMASK(9, 4) ++#define AEON_IPC_STS_STATUS GENMASK(3, 0) ++#define AEON_IPC_STS_STATUS_RCVD FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1) ++#define AEON_IPC_STS_STATUS_PROCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2) ++#define AEON_IPC_STS_STATUS_SUCCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4) ++#define AEON_IPC_STS_STATUS_ERROR FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8) ++#define AEON_IPC_STS_STATUS_BUSY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe) ++#define AEON_IPC_STS_STATUS_READY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf) ++ ++#define VEND1_IPC_DATA0 0x5808 ++#define VEND1_IPC_DATA1 0x5809 ++#define VEND1_IPC_DATA2 0x580a ++#define VEND1_IPC_DATA3 0x580b ++#define VEND1_IPC_DATA4 0x580c ++#define VEND1_IPC_DATA5 0x580d ++#define VEND1_IPC_DATA6 0x580e ++#define VEND1_IPC_DATA7 0x580f ++#define VEND1_IPC_DATA(_n) (VEND1_IPC_DATA0 + (_n)) ++ ++/* Sub command of CMD_INFO */ ++#define IPC_INFO_VERSION 0x1 ++ ++/* Sub command of CMD_SYS_CPU */ ++#define IPC_SYS_CPU_REBOOT 0x3 ++#define IPC_SYS_CPU_IMAGE_OFST 0x4 ++#define IPC_SYS_CPU_IMAGE_CHECK 0x5 ++#define IPC_SYS_CPU_PHY_ENABLE 0x6 ++ ++/* Sub command of CMD_CFG_PARAM */ ++#define IPC_CFG_PARAM_DIRECT 0x4 ++ ++/* CFG DIRECT sub command */ ++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL 0x1 ++#define IPC_CFG_PARAM_DIRECT_CU_AN 0x2 ++#define IPC_CFG_PARAM_DIRECT_SDS_PCS 0x3 ++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE 0x4 ++#define IPC_CFG_PARAM_DIRECT_SDS_PMA 0x5 ++#define IPC_CFG_PARAM_DIRECT_DPC_RA 0x6 ++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7 ++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8 ++#define IPC_CFG_PARAM_DIRECT_WDT 0x9 ++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10 ++#define IPC_CFG_PARAM_DIRECT_TEMP_MON 0x11 ++#define IPC_CFG_PARAM_DIRECT_WOL 0x12 ++ ++/* Sub command of CMD_TEMP_MON */ ++#define IPC_CMD_TEMP_MON_GET 0x4 ++ ++#define AS21XXX_MDIO_AN_C22 0xffe0 ++ ++#define PHY_ID_AS21XXX 0x75009410 ++/* AS21xxx ID Legend ++ * AS21x1xxB1 ++ * ^ ^^ ++ * | |J: Supports SyncE/PTP ++ * | |P: No SyncE/PTP support ++ * | 1: Supports 2nd Serdes ++ * | 2: Not 2nd Serdes support ++ * 0: 10G, 5G, 2.5G ++ * 5: 5G, 2.5G ++ * 2: 2.5G ++ */ ++#define PHY_ID_AS21011JB1 0x75009402 ++#define PHY_ID_AS21011PB1 0x75009412 ++#define PHY_ID_AS21010JB1 0x75009422 ++#define PHY_ID_AS21010PB1 0x75009432 ++#define PHY_ID_AS21511JB1 0x75009442 ++#define PHY_ID_AS21511PB1 0x75009452 ++#define PHY_ID_AS21510JB1 0x75009462 ++#define PHY_ID_AS21510PB1 0x75009472 ++#define PHY_ID_AS21210JB1 0x75009482 ++#define PHY_ID_AS21210PB1 0x75009492 ++#define PHY_VENDOR_AEONSEMI 0x75009400 ++ ++#define AEON_MAX_LEDS 5 ++#define AEON_IPC_DELAY 10000 ++#define AEON_IPC_TIMEOUT (AEON_IPC_DELAY * 100) ++#define AEON_IPC_DATA_NUM_REGISTERS 8 ++#define AEON_IPC_DATA_MAX (AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16)) ++ ++#define AEON_BOOT_ADDR 0x1000 ++#define AEON_CPU_BOOT_ADDR 0x2000 ++#define AEON_CPU_CTRL_FW_LOAD (BIT(4) | BIT(2) | BIT(1) | BIT(0)) ++#define AEON_CPU_CTRL_FW_START BIT(0) ++ ++enum as21xxx_led_event { ++ VEND1_LED_REG_A_EVENT_ON_10 = 0x0, ++ VEND1_LED_REG_A_EVENT_ON_100, ++ VEND1_LED_REG_A_EVENT_ON_1000, ++ VEND1_LED_REG_A_EVENT_ON_2500, ++ VEND1_LED_REG_A_EVENT_ON_5000, ++ VEND1_LED_REG_A_EVENT_ON_10000, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_NG, ++ VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX, ++ VEND1_LED_REG_A_EVENT_ON_COLLISION, ++ VEND1_LED_REG_A_EVENT_BLINK_TX, ++ VEND1_LED_REG_A_EVENT_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION, ++ VEND1_LED_REG_A_EVENT_ON, ++ VEND1_LED_REG_A_EVENT_OFF, ++}; ++ ++struct as21xxx_led_pattern_info { ++ unsigned int pattern; ++ u16 val; ++}; ++ ++struct as21xxx_priv { ++ bool parity_status; ++ /* Protect concurrent IPC access */ ++ struct mutex ipc_lock; ++}; ++ ++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = { ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10), ++ .val = VEND1_LED_REG_A_EVENT_ON_10 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_100), ++ .val = VEND1_LED_REG_A_EVENT_ON_100 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_1000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500), ++ .val = VEND1_LED_REG_A_EVENT_ON_2500 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_5000), ++ .val = VEND1_LED_REG_A_EVENT_ON_5000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_10000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_TX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT ++ } ++}; ++ ++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data, ++ size_t size) ++{ ++ int i, ret; ++ u16 val; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR, ++ AEON_BOOT_ADDR); ++ if (ret) ++ return ret; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD, ++ 0x3ffc, 0xc000); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_STATUS); ++ if (val > 1) { ++ phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val); ++ return -EINVAL; ++ } ++ ++ /* Firmware is always aligned to u16 */ ++ for (i = 0; i < size; i += 2) { ++ val = data[i + 1] << 8 | data[i]; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val); ++ if (ret) ++ return ret; ++ } ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR, ++ lower_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR, ++ upper_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START); ++} ++ ++static int aeon_firmware_load(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ const char *fw_name; ++ int ret; ++ ++ ret = of_property_read_string(dev->of_node, "firmware-name", ++ &fw_name); ++ if (ret) ++ return ret; ++ ++ ret = request_firmware(&fw, fw_name, dev); ++ if (ret) { ++ phydev_err(phydev, "failed to find FW file %s (%d)\n", ++ fw_name, ret); ++ return ret; ++ } ++ ++ ret = aeon_firmware_boot(phydev, fw->data, fw->size); ++ ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++static bool aeon_ipc_ready(u16 val, bool parity_status) ++{ ++ u16 status; ++ ++ if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status) ++ return false; ++ ++ status = val & AEON_IPC_STS_STATUS; ++ ++ return status != AEON_IPC_STS_STATUS_RCVD && ++ status != AEON_IPC_STS_STATUS_PROCESS && ++ status != AEON_IPC_STS_STATUS_BUSY; ++} ++ ++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status) ++{ ++ u16 val; ++ ++ /* Exit condition logic: ++ * - Wait for parity bit equal ++ * - Wait for status success, error OR ready ++ */ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val, ++ aeon_ipc_ready(val, parity_status), ++ AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false); ++} ++ ++static int aeon_ipc_send_cmd(struct phy_device *phydev, ++ struct as21xxx_priv *priv, ++ u16 cmd, u16 *ret_sts) ++{ ++ bool curr_parity; ++ int ret; ++ ++ /* The IPC sync by using a single parity bit. ++ * Each CMD have alternately this bit set or clear ++ * to understand correct flow and packet order. ++ */ ++ curr_parity = priv->parity_status; ++ if (priv->parity_status) ++ cmd |= AEON_IPC_CMD_PARITY; ++ ++ /* Always update parity for next packet */ ++ priv->parity_status = !priv->parity_status; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd); ++ if (ret) ++ return ret; ++ ++ /* Wait for packet to be processed */ ++ usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000); ++ ++ /* With no ret_sts, ignore waiting for packet completion ++ * (ipc parity bit sync) ++ */ ++ if (!ret_sts) ++ return 0; ++ ++ ret = aeon_ipc_wait_cmd(phydev, curr_parity); ++ if (ret) ++ return ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS); ++ if (ret < 0) ++ return ret; ++ ++ *ret_sts = ret; ++ if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* If data is NULL, return 0 or negative error. ++ * If data not NULL, return number of Bytes received from IPC or ++ * a negative error. ++ */ ++static int aeon_ipc_send_msg(struct phy_device *phydev, ++ u16 opcode, u16 *data, unsigned int data_len, ++ u16 *ret_data) ++{ ++ struct as21xxx_priv *priv = phydev->priv; ++ unsigned int ret_size; ++ u16 cmd, ret_sts; ++ int ret; ++ int i; ++ ++ /* IPC have a max of 8 register to transfer data, ++ * make sure we never exceed this. ++ */ ++ if (data_len > AEON_IPC_DATA_MAX) ++ return -EINVAL; ++ ++ for (i = 0; i < data_len / sizeof(u16); i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i), ++ data[i]); ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode); ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts); ++ if (ret) { ++ phydev_err(phydev, "failed to send ipc msg for %x: %d\n", ++ opcode, ret); ++ goto out; ++ } ++ ++ if (!data) ++ goto out; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Prevent IPC from stack smashing the kernel. ++ * We can't trust IPC to return a good value and we always ++ * preallocate space for 16 Bytes. ++ */ ++ ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts); ++ if (ret_size > AEON_IPC_DATA_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Read data from IPC data register for ret_size value from IPC */ ++ for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) { ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i)); ++ if (ret < 0) ++ goto out; ++ ++ ret_data[i] = ret; ++ } ++ ++ ret = ret_size; ++ ++out: ++ mutex_unlock(&priv->ipc_lock); ++ ++ return ret; ++} ++ ++static int aeon_ipc_noop(struct phy_device *phydev, ++ struct as21xxx_priv *priv, u16 *ret_sts) ++{ ++ u16 cmd; ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP); ++ ++ return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts); ++} ++ ++/* Logic to sync parity bit with IPC. ++ * We send 2 NOP cmd with same partity and we wait for IPC ++ * to handle the packet only for the second one. This way ++ * we make sure we are sync for every next cmd. ++ */ ++static int aeon_ipc_sync_parity(struct phy_device *phydev, ++ struct as21xxx_priv *priv) ++{ ++ u16 ret_sts; ++ int ret; ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ /* Send NOP with no parity */ ++ aeon_ipc_noop(phydev, priv, NULL); ++ ++ /* Reset packet parity */ ++ priv->parity_status = false; ++ ++ /* Send second NOP with no parity */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ ++ mutex_unlock(&priv->ipc_lock); ++ ++ /* We expect to return -EINVAL */ ++ if (ret != -EINVAL) ++ return ret; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) { ++ phydev_err(phydev, "Invalid IPC status on sync parity: %x\n", ++ ret_sts); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aeon_ipc_get_fw_version(struct phy_device *phydev) ++{ ++ u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1]; ++ char fw_version[AEON_IPC_DATA_MAX + 1]; ++ int ret; ++ ++ data[0] = IPC_INFO_VERSION; ++ ++ ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data, ++ sizeof(data), ret_data); ++ if (ret < 0) ++ return ret; ++ ++ /* Make sure FW version is NULL terminated */ ++ memcpy(fw_version, ret_data, ret); ++ fw_version[ret] = '\0'; ++ ++ phydev_info(phydev, "Firmware Version: %s\n", fw_version); ++ ++ return 0; ++} ++ ++static int aeon_dpc_ra_enable(struct phy_device *phydev) ++{ ++ u16 data[2]; ++ ++ data[0] = IPC_CFG_PARAM_DIRECT; ++ data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA; ++ ++ return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data, ++ sizeof(data), NULL); ++} ++ ++static int as21xxx_probe(struct phy_device *phydev) ++{ ++ struct as21xxx_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, ++ sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = devm_mutex_init(&phydev->mdio.dev, ++ &priv->ipc_lock); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_get_fw_version(phydev); ++ if (ret) ++ return ret; ++ ++ /* Enable PTP clk if not already Enabled */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK, ++ VEND1_PTP_CLK_EN); ++ if (ret) ++ return ret; ++ ++ return aeon_dpc_ra_enable(phydev); ++} ++ ++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr) ++{ ++ int status; ++ ++ /* Normal C22 BMCR report inconsistent data, use ++ * the mapped C22 in C45 to have more consistent link info. ++ */ ++ *bmcr = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_BMCR); ++ if (*bmcr < 0) ++ return *bmcr; ++ ++ /* Autoneg is being started, therefore disregard current ++ * link status and report link as down. ++ */ ++ if (*bmcr & BMCR_ANRESTART) { ++ phydev->link = 0; ++ return 0; ++ } ++ ++ status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ if (status < 0) ++ return status; ++ ++ phydev->link = !!(status & MDIO_STAT1_LSTATUS); ++ ++ return 0; ++} ++ ++static int as21xxx_read_c22_lpa(struct phy_device *phydev) ++{ ++ int lpagb; ++ ++ /* MII_STAT1000 are only filled in the mapped C22 ++ * in C45, use that to fill lpagb values and check. ++ */ ++ lpagb = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_STAT1000); ++ if (lpagb < 0) ++ return lpagb; ++ ++ if (lpagb & LPA_1000MSFAIL) { ++ int adv = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_CTRL1000); ++ ++ if (adv < 0) ++ return adv; ++ ++ if (adv & CTL1000_ENABLE_MASTER) ++ phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); ++ else ++ phydev_err(phydev, "Master/Slave resolution failed\n"); ++ return -ENOLINK; ++ } ++ ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ++ lpagb); ++ ++ return 0; ++} ++ ++static int as21xxx_read_status(struct phy_device *phydev) ++{ ++ int bmcr, old_link = phydev->link; ++ int ret; ++ ++ ret = as21xxx_read_link(phydev, &bmcr); ++ if (ret) ++ return ret; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ ret = genphy_c45_read_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ ret = as21xxx_read_c22_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ phy_resolve_aneg_linkmode(phydev); ++ } else { ++ int speed; ++ ++ linkmode_zero(phydev->lp_advertising); ++ ++ speed = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_SPEED_STATUS); ++ if (speed < 0) ++ return speed; ++ ++ switch (speed & VEND1_SPEED_STATUS) { ++ case VEND1_SPEED_10000: ++ phydev->speed = SPEED_10000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_5000: ++ phydev->speed = SPEED_5000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ if (bmcr & BMCR_FULLDPLX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ break; ++ case VEND1_SPEED_100: ++ phydev->speed = SPEED_100; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_10: ++ phydev->speed = SPEED_10; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int as21xxx_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 val = VEND1_LED_REG_A_EVENT_OFF; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ if (value) ++ val = VEND1_LED_REG_A_EVENT_ON; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) ++ return 0; ++ ++ return -EOPNOTSUPP; ++} ++ ++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int i, val; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index)); ++ if (val < 0) ++ return val; ++ ++ val = FIELD_GET(VEND1_LED_REG_A_EVENT, val); ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (val == as21xxx_led_supported_pattern[i].val) { ++ *rules = as21xxx_led_supported_pattern[i].pattern; ++ return 0; ++ } ++ ++ /* Should be impossible */ ++ return -EINVAL; ++} ++ ++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 val = 0; ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) { ++ val = as21xxx_led_supported_pattern[i].val; ++ break; ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ bool led_active_low = false; ++ u16 mask, val = 0; ++ u32 mode; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ led_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: /* default mode */ ++ led_active_low = false; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ if (led_active_low) ++ val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_CTRL, ++ mask, val); ++} ++ ++static int as21xxx_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ struct as21xxx_priv *priv; ++ u16 ret_sts; ++ u32 phy_id; ++ int ret; ++ ++ /* Skip PHY that are not AS21xxx or already have firmware loaded */ ++ if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX) ++ return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv); ++ ++ /* Read PHY ID to handle firmware just loaded */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1); ++ if (ret < 0) ++ return ret; ++ phy_id = ret << 16; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2); ++ if (ret < 0) ++ return ret; ++ phy_id |= ret; ++ ++ /* With PHY ID not the generic AS21xxx one assume ++ * the firmware just loaded ++ */ ++ if (phy_id != PHY_ID_AS21XXX) ++ return phy_id == phydrv->phy_id; ++ ++ /* Allocate temp priv and load the firmware */ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->ipc_lock); ++ ++ ret = aeon_firmware_load(phydev); ++ if (ret) ++ goto out; ++ ++ /* Sync parity... */ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ goto out; ++ ++ /* ...and send a third NOOP cmd to wait for firmware finish loading */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ if (ret) ++ goto out; ++ ++out: ++ mutex_destroy(&priv->ipc_lock); ++ kfree(priv); ++ ++ /* Return can either be 0 or a negative error code. ++ * Returning 0 here means THIS is NOT a suitable PHY. ++ * ++ * For the specific case of the generic Aeonsemi PHY ID that ++ * needs the firmware the be loaded first to have a correct PHY ID, ++ * this is OK as a matching PHY ID will be found right after. ++ * This relies on the driver probe order where the first PHY driver ++ * probed is the generic one. ++ */ ++ return ret; ++} ++ ++static struct phy_driver as21xxx_drivers[] = { ++ { ++ /* PHY expose in C45 as 0x7500 0x9410 ++ * before firmware is loaded. ++ * This driver entry must be attempted first to load ++ * the firmware and thus update the ID registers. ++ */ ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX), ++ .name = "Aeonsemi AS21xxx", ++ .match_phy_device = as21xxx_match_phy_device, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1), ++ .name = "Aeonsemi AS21011JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1), ++ .name = "Aeonsemi AS21011PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1), ++ .name = "Aeonsemi AS21010PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1), ++ .name = "Aeonsemi AS21010JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1), ++ .name = "Aeonsemi AS21210PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1), ++ .name = "Aeonsemi AS21510JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1), ++ .name = "Aeonsemi AS21510PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1), ++ .name = "Aeonsemi AS21511JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1), ++ .name = "Aeonsemi AS21210JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1), ++ .name = "Aeonsemi AS21511PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++}; ++module_phy_driver(as21xxx_drivers); ++ ++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = { ++ { PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl); ++ ++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/741-v6.13-01-net-phy-mediatek-ge-soc-Fix-coding-style.patch b/target/linux/generic/backport-6.6/741-v6.13-01-net-phy-mediatek-ge-soc-Fix-coding-style.patch new file mode 100644 index 00000000000000..9e75b1871c739e --- /dev/null +++ b/target/linux/generic/backport-6.6/741-v6.13-01-net-phy-mediatek-ge-soc-Fix-coding-style.patch @@ -0,0 +1,88 @@ +From b544223bec9f710256543eadc1b8b8344f80d665 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 17 Oct 2024 11:22:11 +0800 +Subject: [PATCH 1/3] net: phy: mediatek-ge-soc: Fix coding style + +This patch fixes spelling errors, re-arrange vars with +reverse Xmas tree and remove unnecessary parens in +mediatek-ge-soc.c. + +Signed-off-by: SkyLake.Huang +Reviewed-by: Simon Horman +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/mediatek-ge-soc.c | 36 ++++++++++++++++--------------- + 1 file changed, 19 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ b/drivers/net/phy/mediatek-ge-soc.c +@@ -408,16 +408,17 @@ static int tx_offset_cal_efuse(struct ph + + static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) + { +- int i; +- int bias[16] = {}; +- const int vals_9461[16] = { 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7 }; + const int vals_9481[16] = { 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10 }; ++ const int vals_9461[16] = { 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7 }; ++ int bias[16] = {}; ++ int i; ++ + switch (phydev->drv->phy_id) { + case MTK_GPHY_ID_MT7981: + /* We add some calibration to efuse values +@@ -1069,10 +1070,10 @@ static int start_cal(struct phy_device * + + static int mt798x_phy_calibration(struct phy_device *phydev) + { ++ struct nvmem_cell *cell; + int ret = 0; +- u32 *buf; + size_t len; +- struct nvmem_cell *cell; ++ u32 *buf; + + cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); + if (IS_ERR(cell)) { +@@ -1210,14 +1211,15 @@ static int mt798x_phy_led_brightness_set + return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); + } + +-static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_FULL_DUPLEX) | +- BIT(TRIGGER_NETDEV_HALF_DUPLEX) | +- BIT(TRIGGER_NETDEV_LINK) | +- BIT(TRIGGER_NETDEV_LINK_10) | +- BIT(TRIGGER_NETDEV_LINK_100) | +- BIT(TRIGGER_NETDEV_LINK_1000) | +- BIT(TRIGGER_NETDEV_RX) | +- BIT(TRIGGER_NETDEV_TX)); ++static const unsigned long supported_triggers = ++ BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_HALF_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); + + static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +@@ -1415,7 +1417,7 @@ static int mt7988_phy_probe_shared(struc + * LED_C and LED_D respectively. At the same time those pins are used to + * bootstrap configuration of the reference clock source (LED_A), + * DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D). +- * In practise this is done using a LED and a resistor pulling the pin ++ * In practice this is done using a LED and a resistor pulling the pin + * either to GND or to VIO. + * The detected value at boot time is accessible at run-time using the + * TPBANK0 register located in the gpio base of the pinctrl, in order diff --git a/target/linux/generic/backport-6.6/741-v6.13-02-net-phy-mediatek-ge-soc-Shrink-line-wrapping-to-80-c.patch b/target/linux/generic/backport-6.6/741-v6.13-02-net-phy-mediatek-ge-soc-Shrink-line-wrapping-to-80-c.patch new file mode 100644 index 00000000000000..f8bebc410d817e --- /dev/null +++ b/target/linux/generic/backport-6.6/741-v6.13-02-net-phy-mediatek-ge-soc-Shrink-line-wrapping-to-80-c.patch @@ -0,0 +1,271 @@ +From b0f90a863ca5030fd074426b2b5095ef93f2c5bf Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 17 Oct 2024 11:22:12 +0800 +Subject: [PATCH 2/3] net: phy: mediatek-ge-soc: Shrink line wrapping to 80 + characters + +This patch shrinks line wrapping to 80 chars. Also, in +tx_amp_fill_result(), use FIELD_PREP() to prettify code. + +Signed-off-by: SkyLake.Huang +Reviewed-by: Simon Horman +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/mediatek-ge-soc.c | 125 +++++++++++++++++++++--------- + 1 file changed, 88 insertions(+), 37 deletions(-) + +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ b/drivers/net/phy/mediatek-ge-soc.c +@@ -342,7 +342,8 @@ static int cal_cycle(struct phy_device * + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_AD_CAL_CLK, reg_val, + reg_val & MTK_PHY_DA_CAL_CLK, 500, +- ANALOG_INTERNAL_OPERATION_MAX_US, false); ++ ANALOG_INTERNAL_OPERATION_MAX_US, ++ false); + if (ret) { + phydev_err(phydev, "Calibration cycle timeout\n"); + return ret; +@@ -441,40 +442,72 @@ static int tx_amp_fill_result(struct phy + } + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10); ++ MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ buf[0] + bias[0])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]); ++ MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ buf[0] + bias[1])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10); ++ MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ buf[0] + bias[2])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]); ++ MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ buf[0] + bias[3])); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8); ++ MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ buf[1] + bias[4])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]); ++ MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ buf[1] + bias[5])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8); ++ MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ buf[1] + bias[6])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]); ++ MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ buf[1] + bias[7])); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8); ++ MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ buf[2] + bias[8])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]); ++ MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ buf[2] + bias[9])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8); ++ MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ buf[2] + bias[10])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]); ++ MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ buf[2] + bias[11])); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8); ++ MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ buf[3] + bias[12])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]); ++ MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ buf[3] + bias[13])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8); ++ MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ buf[3] + bias[14])); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]); ++ MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ buf[3] + bias[15])); + + return 0; + } +@@ -663,7 +696,8 @@ static int tx_vcm_cal_sw(struct phy_devi + goto restore; + + /* We calibrate TX-VCM in different logic. Check upper index and then +- * lower index. If this calibration is valid, apply lower index's result. ++ * lower index. If this calibration is valid, apply lower index's ++ * result. + */ + ret = upper_ret - lower_ret; + if (ret == 1) { +@@ -692,7 +726,8 @@ static int tx_vcm_cal_sw(struct phy_devi + } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && + lower_ret == 0) { + ret = 0; +- phydev_warn(phydev, "TX-VCM SW cal result at high margin 0x%x\n", ++ phydev_warn(phydev, ++ "TX-VCM SW cal result at high margin 0x%x\n", + upper_idx); + } else { + ret = -EINVAL; +@@ -796,7 +831,8 @@ static void mt7981_phy_finetune(struct p + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); + + /* rg_tr_lpf_cnt_val = 512 */ +@@ -865,7 +901,8 @@ static void mt7988_phy_finetune(struct p + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); + + /* rg_tr_lpf_cnt_val = 1023 */ +@@ -977,7 +1014,8 @@ static void mt798x_phy_eee(struct phy_de + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); +- __phy_modify(phydev, MTK_PHY_LPI_REG_14, MTK_PHY_LPI_WAKE_TIMER_1000_MASK, ++ __phy_modify(phydev, MTK_PHY_LPI_REG_14, ++ MTK_PHY_LPI_WAKE_TIMER_1000_MASK, + FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); + + __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, +@@ -987,7 +1025,8 @@ static void mt798x_phy_eee(struct phy_de + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, + MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, 0xff)); ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ 0xff)); + } + + static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, +@@ -1147,7 +1186,8 @@ static int mt798x_phy_hw_led_on_set(stru + (index ? 16 : 0), &priv->led_state); + if (changed) + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED_ON_MASK, + on ? MTK_PHY_LED_ON_FORCE_ON : 0); + else +@@ -1157,7 +1197,8 @@ static int mt798x_phy_hw_led_on_set(stru + static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, + bool blinking) + { +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + (index ? 16 : 0); ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); + struct mtk_socphy_priv *priv = phydev->priv; + bool changed; + +@@ -1170,8 +1211,10 @@ static int mt798x_phy_hw_led_blink_set(s + (index ? 16 : 0), &priv->led_state); + if (changed) + return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : MTK_PHY_LED0_BLINK_CTRL, +- blinking ? MTK_PHY_LED_BLINK_FORCE_BLINK : 0); ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, ++ blinking ? ++ MTK_PHY_LED_BLINK_FORCE_BLINK : 0); + else + return 0; + } +@@ -1237,7 +1280,8 @@ static int mt798x_phy_led_hw_is_supporte + static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) + { +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + (index ? 16 : 0); ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); + unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); + unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); + struct mtk_socphy_priv *priv = phydev->priv; +@@ -1258,8 +1302,8 @@ static int mt798x_phy_led_hw_control_get + if (blink < 0) + return -EIO; + +- if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | +- MTK_PHY_LED_ON_LINKDOWN)) || ++ if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || + (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) + set_bit(bit_netdev, &priv->led_state); + else +@@ -1333,17 +1377,23 @@ static int mt798x_phy_led_hw_control_set + + if (rules & BIT(TRIGGER_NETDEV_RX)) { + blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000RX : 0)) : ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000RX : 0)) : + MTK_PHY_LED_BLINK_RX; + } + + if (rules & BIT(TRIGGER_NETDEV_TX)) { + blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? MTK_PHY_LED_BLINK_10TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? MTK_PHY_LED_BLINK_100TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? MTK_PHY_LED_BLINK_1000TX : 0)) : ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000TX : 0)) : + MTK_PHY_LED_BLINK_TX; + } + +@@ -1400,7 +1450,8 @@ static int mt7988_phy_fix_leds_polaritie + /* Only now setup pinctrl to avoid bogus blinking */ + pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); + if (IS_ERR(pinctrl)) +- dev_err(&phydev->mdio.bus->dev, "Failed to setup PHY LED pinctrl\n"); ++ dev_err(&phydev->mdio.bus->dev, ++ "Failed to setup PHY LED pinctrl\n"); + + return 0; + } diff --git a/target/linux/generic/backport-6.6/741-v6.13-03-net-phy-mediatek-ge-soc-Propagate-error-code-correct.patch b/target/linux/generic/backport-6.6/741-v6.13-03-net-phy-mediatek-ge-soc-Propagate-error-code-correct.patch new file mode 100644 index 00000000000000..f66e6ae0f2fa35 --- /dev/null +++ b/target/linux/generic/backport-6.6/741-v6.13-03-net-phy-mediatek-ge-soc-Propagate-error-code-correct.patch @@ -0,0 +1,40 @@ +From 93a610c00ffd8b9c7c108039d894ddd5193b147e Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 17 Oct 2024 11:22:13 +0800 +Subject: [PATCH 3/3] net: phy: mediatek-ge-soc: Propagate error code correctly + in cal_cycle() + +This patch propagates error code correctly in cal_cycle() +and improve with FIELD_GET(). + +Signed-off-by: SkyLake.Huang +Reviewed-by: Simon Horman +Signed-off-by: Andrew Lunn +--- + drivers/net/phy/mediatek-ge-soc.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ b/drivers/net/phy/mediatek-ge-soc.c +@@ -110,7 +110,7 @@ + #define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) + + #define MTK_PHY_RG_AD_CAL_COMP 0x17a +-#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8) ++#define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8) + + #define MTK_PHY_RG_AD_CAL_CLK 0x17b + #define MTK_PHY_DA_CAL_CLK BIT(0) +@@ -351,8 +351,10 @@ static int cal_cycle(struct phy_device * + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, + MTK_PHY_DA_CALIN_FLAG); +- ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >> +- MTK_PHY_AD_CAL_COMP_OUT_SHIFT; ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP); ++ if (ret < 0) ++ return ret; ++ ret = FIELD_GET(MTK_PHY_AD_CAL_COMP_OUT_MASK, ret); + phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); + + return ret; diff --git a/target/linux/generic/backport-6.6/742-v6.13-net-phy-mediatek-Re-organize-MediaTek-ethernet-phy-d.patch b/target/linux/generic/backport-6.6/742-v6.13-net-phy-mediatek-Re-organize-MediaTek-ethernet-phy-d.patch new file mode 100644 index 00000000000000..8715d3ef63a6bc --- /dev/null +++ b/target/linux/generic/backport-6.6/742-v6.13-net-phy-mediatek-Re-organize-MediaTek-ethernet-phy-d.patch @@ -0,0 +1,3557 @@ +From 4c452f7ea86212934ae896842847d1671e13a18b Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:51 +0800 +Subject: [PATCH] net: phy: mediatek: Re-organize MediaTek ethernet phy drivers + +Re-organize MediaTek ethernet phy driver files and get ready to integrate +some common functions and add new 2.5G phy driver. +mtk-ge.c: MT7530 Gphy on MT7621 & MT7531 Gphy +mtk-ge-soc.c: Built-in Gphy on MT7981 & Built-in switch Gphy on MT7988 +mtk-2p5ge.c: Planned for built-in 2.5G phy on MT7988 + +Reviewed-by: Andrew Lunn +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + MAINTAINERS | 4 ++-- + drivers/net/phy/Kconfig | 17 +------------- + drivers/net/phy/Makefile | 3 +-- + drivers/net/phy/mediatek/Kconfig | 22 +++++++++++++++++++ + drivers/net/phy/mediatek/Makefile | 3 +++ + .../mtk-ge-soc.c} | 0 + .../phy/{mediatek-ge.c => mediatek/mtk-ge.c} | 0 + 7 files changed, 29 insertions(+), 20 deletions(-) + create mode 100644 drivers/net/phy/mediatek/Kconfig + create mode 100644 drivers/net/phy/mediatek/Makefile + rename drivers/net/phy/{mediatek-ge-soc.c => mediatek/mtk-ge-soc.c} (100%) + rename drivers/net/phy/{mediatek-ge.c => mediatek/mtk-ge.c} (100%) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -234,22 +234,7 @@ config MAXLINEAR_GPHY + Support for the Maxlinear GPY115, GPY211, GPY212, GPY215, + GPY241, GPY245 PHYs. + +-config MEDIATEK_GE_PHY +- tristate "MediaTek Gigabit Ethernet PHYs" +- help +- Supports the MediaTek Gigabit Ethernet PHYs. +- +-config MEDIATEK_GE_SOC_PHY +- tristate "MediaTek SoC Ethernet PHYs" +- depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST +- depends on NVMEM_MTK_EFUSE +- help +- Supports MediaTek SoC built-in Gigabit Ethernet PHYs. +- +- Include support for built-in Ethernet PHYs which are present in +- the MT7981 and MT7988 SoCs. These PHYs need calibration data +- present in the SoCs efuse and will dynamically calibrate VCM +- (common-mode voltage) during startup. ++source "drivers/net/phy/mediatek/Kconfig" + + config MICREL_PHY + tristate "Micrel PHYs" +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -66,8 +66,7 @@ obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_MARVELL_88Q2XXX_PHY) += marvell-88q2xxx.o + obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o + obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o +-obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o +-obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o ++obj-y += mediatek/ + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o + obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o +--- /dev/null ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -0,0 +1,22 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++config MEDIATEK_GE_PHY ++ tristate "MediaTek Gigabit Ethernet PHYs" ++ help ++ Supports the MediaTek non-built-in Gigabit Ethernet PHYs. ++ ++ Non-built-in Gigabit Ethernet PHYs include mt7530/mt7531. ++ You may find mt7530 inside mt7621. This driver shares some ++ common operations with MediaTek SoC built-in Gigabit ++ Ethernet PHYs. ++ ++config MEDIATEK_GE_SOC_PHY ++ tristate "MediaTek SoC Ethernet PHYs" ++ depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST ++ depends on NVMEM_MTK_EFUSE ++ help ++ Supports MediaTek SoC built-in Gigabit Ethernet PHYs. ++ ++ Include support for built-in Ethernet PHYs which are present in ++ the MT7981 and MT7988 SoCs. These PHYs need calibration data ++ present in the SoCs efuse and will dynamically calibrate VCM ++ (common-mode voltage) during startup. +--- /dev/null ++++ b/drivers/net/phy/mediatek/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o ++obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mtk-ge-soc.o +--- a/drivers/net/phy/mediatek-ge-soc.c ++++ /dev/null +@@ -1,1610 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define MTK_GPHY_ID_MT7981 0x03a29461 +-#define MTK_GPHY_ID_MT7988 0x03a29481 +- +-#define MTK_EXT_PAGE_ACCESS 0x1f +-#define MTK_PHY_PAGE_STANDARD 0x0000 +-#define MTK_PHY_PAGE_EXTENDED_3 0x0003 +- +-#define MTK_PHY_LPI_REG_14 0x14 +-#define MTK_PHY_LPI_WAKE_TIMER_1000_MASK GENMASK(8, 0) +- +-#define MTK_PHY_LPI_REG_1c 0x1c +-#define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) +- +-#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 +- +-#define ANALOG_INTERNAL_OPERATION_MAX_US 20 +-#define TXRESERVE_MIN 0 +-#define TXRESERVE_MAX 7 +- +-#define MTK_PHY_ANARG_RG 0x10 +-#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) +- +-/* Registers on MDIO_MMD_VEND1 */ +-#define MTK_PHY_TXVLD_DA_RG 0x12 +-#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) +-#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 0x16 +-#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) +-#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 0x17 +-#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 0x18 +-#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 0x19 +-#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 0x20 +-#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 0x21 +-#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) +- +-#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 0x22 +-#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) +-#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) +- +-#define MTK_PHY_RXADC_CTRL_RG7 0xc6 +-#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) +- +-#define MTK_PHY_RXADC_CTRL_RG9 0xc8 +-#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) +-#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) +-#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) +-#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) +- +-#define MTK_PHY_LDO_OUTPUT_V 0xd7 +- +-#define MTK_PHY_RG_ANA_CAL_RG0 0xdb +-#define MTK_PHY_RG_CAL_CKINV BIT(12) +-#define MTK_PHY_RG_ANA_CALEN BIT(8) +-#define MTK_PHY_RG_ZCALEN_A BIT(0) +- +-#define MTK_PHY_RG_ANA_CAL_RG1 0xdc +-#define MTK_PHY_RG_ZCALEN_B BIT(12) +-#define MTK_PHY_RG_ZCALEN_C BIT(8) +-#define MTK_PHY_RG_ZCALEN_D BIT(4) +-#define MTK_PHY_RG_TXVOS_CALEN BIT(0) +- +-#define MTK_PHY_RG_ANA_CAL_RG5 0xe0 +-#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) +- +-#define MTK_PHY_RG_TX_FILTER 0xfe +- +-#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120 0x120 +-#define MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK GENMASK(12, 8) +-#define MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK GENMASK(4, 0) +- +-#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122 0x122 +-#define MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK GENMASK(7, 0) +- +-#define MTK_PHY_RG_TESTMUX_ADC_CTRL 0x144 +-#define MTK_PHY_RG_TXEN_DIG_MASK GENMASK(5, 5) +- +-#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B 0x172 +-#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) +-#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) +- +-#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D 0x173 +-#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) +-#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) +- +-#define MTK_PHY_RG_AD_CAL_COMP 0x17a +-#define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8) +- +-#define MTK_PHY_RG_AD_CAL_CLK 0x17b +-#define MTK_PHY_DA_CAL_CLK BIT(0) +- +-#define MTK_PHY_RG_AD_CALIN 0x17c +-#define MTK_PHY_DA_CALIN_FLAG BIT(0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_A 0x17d +-#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_B 0x17e +-#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_C 0x17f +-#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN0_D 0x180 +-#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_A 0x181 +-#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_B 0x182 +-#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_C 0x183 +-#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DASN_DAC_IN1_D 0x184 +-#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) +- +-#define MTK_PHY_RG_DEV1E_REG19b 0x19b +-#define MTK_PHY_BYPASS_DSP_LPI_READY BIT(8) +- +-#define MTK_PHY_RG_LP_IIR2_K1_L 0x22a +-#define MTK_PHY_RG_LP_IIR2_K1_U 0x22b +-#define MTK_PHY_RG_LP_IIR2_K2_L 0x22c +-#define MTK_PHY_RG_LP_IIR2_K2_U 0x22d +-#define MTK_PHY_RG_LP_IIR2_K3_L 0x22e +-#define MTK_PHY_RG_LP_IIR2_K3_U 0x22f +-#define MTK_PHY_RG_LP_IIR2_K4_L 0x230 +-#define MTK_PHY_RG_LP_IIR2_K4_U 0x231 +-#define MTK_PHY_RG_LP_IIR2_K5_L 0x232 +-#define MTK_PHY_RG_LP_IIR2_K5_U 0x233 +- +-#define MTK_PHY_RG_DEV1E_REG234 0x234 +-#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) +-#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) +-#define MTK_PHY_TR_LP_IIR_EEE_EN BIT(12) +- +-#define MTK_PHY_RG_LPF_CNT_VAL 0x235 +- +-#define MTK_PHY_RG_DEV1E_REG238 0x238 +-#define MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK GENMASK(8, 0) +-#define MTK_PHY_LPI_SLV_SEND_TX_EN BIT(12) +- +-#define MTK_PHY_RG_DEV1E_REG239 0x239 +-#define MTK_PHY_LPI_SEND_LOC_TIMER_MASK GENMASK(8, 0) +-#define MTK_PHY_LPI_TXPCS_LOC_RCV BIT(12) +- +-#define MTK_PHY_RG_DEV1E_REG27C 0x27c +-#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) +-#define MTK_PHY_RG_DEV1E_REG27D 0x27d +-#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) +- +-#define MTK_PHY_RG_DEV1E_REG2C7 0x2c7 +-#define MTK_PHY_MAX_GAIN_MASK GENMASK(4, 0) +-#define MTK_PHY_MIN_GAIN_MASK GENMASK(12, 8) +- +-#define MTK_PHY_RG_DEV1E_REG2D1 0x2d1 +-#define MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK GENMASK(7, 0) +-#define MTK_PHY_LPI_SKIP_SD_SLV_TR BIT(8) +-#define MTK_PHY_LPI_TR_READY BIT(9) +-#define MTK_PHY_LPI_VCO_EEE_STG0_EN BIT(10) +- +-#define MTK_PHY_RG_DEV1E_REG323 0x323 +-#define MTK_PHY_EEE_WAKE_MAS_INT_DC BIT(0) +-#define MTK_PHY_EEE_WAKE_SLV_INT_DC BIT(4) +- +-#define MTK_PHY_RG_DEV1E_REG324 0x324 +-#define MTK_PHY_SMI_DETCNT_MAX_MASK GENMASK(5, 0) +-#define MTK_PHY_SMI_DET_MAX_EN BIT(8) +- +-#define MTK_PHY_RG_DEV1E_REG326 0x326 +-#define MTK_PHY_LPI_MODE_SD_ON BIT(0) +-#define MTK_PHY_RESET_RANDUPD_CNT BIT(1) +-#define MTK_PHY_TREC_UPDATE_ENAB_CLR BIT(2) +-#define MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF BIT(4) +-#define MTK_PHY_TR_READY_SKIP_AFE_WAKEUP BIT(5) +- +-#define MTK_PHY_LDO_PUMP_EN_PAIRAB 0x502 +-#define MTK_PHY_LDO_PUMP_EN_PAIRCD 0x503 +- +-#define MTK_PHY_DA_TX_R50_PAIR_A 0x53d +-#define MTK_PHY_DA_TX_R50_PAIR_B 0x53e +-#define MTK_PHY_DA_TX_R50_PAIR_C 0x53f +-#define MTK_PHY_DA_TX_R50_PAIR_D 0x540 +- +-/* Registers on MDIO_MMD_VEND2 */ +-#define MTK_PHY_LED0_ON_CTRL 0x24 +-#define MTK_PHY_LED1_ON_CTRL 0x26 +-#define MTK_PHY_LED_ON_MASK GENMASK(6, 0) +-#define MTK_PHY_LED_ON_LINK1000 BIT(0) +-#define MTK_PHY_LED_ON_LINK100 BIT(1) +-#define MTK_PHY_LED_ON_LINK10 BIT(2) +-#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\ +- MTK_PHY_LED_ON_LINK100 |\ +- MTK_PHY_LED_ON_LINK1000) +-#define MTK_PHY_LED_ON_LINKDOWN BIT(3) +-#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +-#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +-#define MTK_PHY_LED_ON_FORCE_ON BIT(6) +-#define MTK_PHY_LED_ON_POLARITY BIT(14) +-#define MTK_PHY_LED_ON_ENABLE BIT(15) +- +-#define MTK_PHY_LED0_BLINK_CTRL 0x25 +-#define MTK_PHY_LED1_BLINK_CTRL 0x27 +-#define MTK_PHY_LED_BLINK_1000TX BIT(0) +-#define MTK_PHY_LED_BLINK_1000RX BIT(1) +-#define MTK_PHY_LED_BLINK_100TX BIT(2) +-#define MTK_PHY_LED_BLINK_100RX BIT(3) +-#define MTK_PHY_LED_BLINK_10TX BIT(4) +-#define MTK_PHY_LED_BLINK_10RX BIT(5) +-#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\ +- MTK_PHY_LED_BLINK_100RX |\ +- MTK_PHY_LED_BLINK_1000RX) +-#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\ +- MTK_PHY_LED_BLINK_100TX |\ +- MTK_PHY_LED_BLINK_1000TX) +-#define MTK_PHY_LED_BLINK_COLLISION BIT(6) +-#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +-#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +-#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) +- +-#define MTK_PHY_LED1_DEFAULT_POLARITIES BIT(1) +- +-#define MTK_PHY_RG_BG_RASEL 0x115 +-#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) +- +-/* 'boottrap' register reflecting the configuration of the 4 PHY LEDs */ +-#define RG_GPIO_MISC_TPBANK0 0x6f0 +-#define RG_GPIO_MISC_TPBANK0_BOOTMODE GENMASK(11, 8) +- +-/* These macro privides efuse parsing for internal phy. */ +-#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) +-#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) +-#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) +-#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) +-#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) +- +-#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) +-#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) +-#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) +-#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) +-#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) +- +-#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) +-#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) +- +-#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) +-#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) +- +-enum { +- NO_PAIR, +- PAIR_A, +- PAIR_B, +- PAIR_C, +- PAIR_D, +-}; +- +-enum calibration_mode { +- EFUSE_K, +- SW_K +-}; +- +-enum CAL_ITEM { +- REXT, +- TX_OFFSET, +- TX_AMP, +- TX_R50, +- TX_VCM +-}; +- +-enum CAL_MODE { +- EFUSE_M, +- SW_M +-}; +- +-#define MTK_PHY_LED_STATE_FORCE_ON 0 +-#define MTK_PHY_LED_STATE_FORCE_BLINK 1 +-#define MTK_PHY_LED_STATE_NETDEV 2 +- +-struct mtk_socphy_priv { +- unsigned long led_state; +-}; +- +-struct mtk_socphy_shared { +- u32 boottrap; +- struct mtk_socphy_priv priv[4]; +-}; +- +-static int mtk_socphy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_socphy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- +-/* One calibration cycle consists of: +- * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high +- * until AD_CAL_COMP is ready to output calibration result. +- * 2.Wait until DA_CAL_CLK is available. +- * 3.Fetch AD_CAL_COMP_OUT. +- */ +-static int cal_cycle(struct phy_device *phydev, int devad, +- u32 regnum, u16 mask, u16 cal_val) +-{ +- int reg_val; +- int ret; +- +- phy_modify_mmd(phydev, devad, regnum, +- mask, cal_val); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, +- MTK_PHY_DA_CALIN_FLAG); +- +- ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_AD_CAL_CLK, reg_val, +- reg_val & MTK_PHY_DA_CAL_CLK, 500, +- ANALOG_INTERNAL_OPERATION_MAX_US, +- false); +- if (ret) { +- phydev_err(phydev, "Calibration cycle timeout\n"); +- return ret; +- } +- +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, +- MTK_PHY_DA_CALIN_FLAG); +- ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP); +- if (ret < 0) +- return ret; +- ret = FIELD_GET(MTK_PHY_AD_CAL_COMP_OUT_MASK, ret); +- phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); +- +- return ret; +-} +- +-static int rext_fill_result(struct phy_device *phydev, u16 *buf) +-{ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, +- MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, +- MTK_PHY_RG_BG_RASEL_MASK, buf[1]); +- +- return 0; +-} +- +-static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) +-{ +- u16 rext_cal_val[2]; +- +- rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); +- rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); +- rext_fill_result(phydev, rext_cal_val); +- +- return 0; +-} +- +-static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) +-{ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, +- MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, +- MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, +- MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, +- MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); +- +- return 0; +-} +- +-static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) +-{ +- u16 tx_offset_cal_val[4]; +- +- tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); +- tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); +- tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); +- tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); +- +- tx_offset_fill_result(phydev, tx_offset_cal_val); +- +- return 0; +-} +- +-static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) +-{ +- const int vals_9481[16] = { 10, 6, 6, 10, +- 10, 6, 6, 10, +- 10, 6, 6, 10, +- 10, 6, 6, 10 }; +- const int vals_9461[16] = { 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7, +- 7, 1, 4, 7 }; +- int bias[16] = {}; +- int i; +- +- switch (phydev->drv->phy_id) { +- case MTK_GPHY_ID_MT7981: +- /* We add some calibration to efuse values +- * due to board level influence. +- * GBE: +7, TBT: +1, HBT: +4, TST: +7 +- */ +- memcpy(bias, (const void *)vals_9461, sizeof(bias)); +- break; +- case MTK_GPHY_ID_MT7988: +- memcpy(bias, (const void *)vals_9481, sizeof(bias)); +- break; +- } +- +- /* Prevent overflow */ +- for (i = 0; i < 12; i++) { +- if (buf[i >> 2] + bias[i] > 63) { +- buf[i >> 2] = 63; +- bias[i] = 0; +- } +- } +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, +- buf[0] + bias[0])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, +- MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, +- buf[0] + bias[1])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, +- buf[0] + bias[2])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, +- MTK_PHY_DA_TX_I2MPB_A_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TST_MASK, +- buf[0] + bias[3])); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, +- buf[1] + bias[4])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, +- MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, +- buf[1] + bias[5])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, +- buf[1] + bias[6])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, +- MTK_PHY_DA_TX_I2MPB_B_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TST_MASK, +- buf[1] + bias[7])); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, +- buf[2] + bias[8])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, +- MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, +- buf[2] + bias[9])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, +- buf[2] + bias[10])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, +- MTK_PHY_DA_TX_I2MPB_C_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TST_MASK, +- buf[2] + bias[11])); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, +- buf[3] + bias[12])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, +- MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, +- buf[3] + bias[13])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, +- buf[3] + bias[14])); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, +- MTK_PHY_DA_TX_I2MPB_D_TST_MASK, +- FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TST_MASK, +- buf[3] + bias[15])); +- +- return 0; +-} +- +-static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) +-{ +- u16 tx_amp_cal_val[4]; +- +- tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); +- tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); +- tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); +- tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); +- tx_amp_fill_result(phydev, tx_amp_cal_val); +- +- return 0; +-} +- +-static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, +- u8 txg_calen_x) +-{ +- int bias = 0; +- u16 reg, val; +- +- if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) +- bias = -1; +- +- val = clamp_val(bias + tx_r50_cal_val, 0, 63); +- +- switch (txg_calen_x) { +- case PAIR_A: +- reg = MTK_PHY_DA_TX_R50_PAIR_A; +- break; +- case PAIR_B: +- reg = MTK_PHY_DA_TX_R50_PAIR_B; +- break; +- case PAIR_C: +- reg = MTK_PHY_DA_TX_R50_PAIR_C; +- break; +- case PAIR_D: +- reg = MTK_PHY_DA_TX_R50_PAIR_D; +- break; +- default: +- return -EINVAL; +- } +- +- phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); +- +- return 0; +-} +- +-static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, +- u8 txg_calen_x) +-{ +- u16 tx_r50_cal_val; +- +- switch (txg_calen_x) { +- case PAIR_A: +- tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); +- break; +- case PAIR_B: +- tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); +- break; +- case PAIR_C: +- tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); +- break; +- case PAIR_D: +- tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); +- break; +- default: +- return -EINVAL; +- } +- tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); +- +- return 0; +-} +- +-static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) +-{ +- u8 lower_idx, upper_idx, txreserve_val; +- u8 lower_ret, upper_ret; +- int ret; +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ANA_CALEN); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_CAL_CKINV); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_TXVOS_CALEN); +- +- switch (rg_txreserve_x) { +- case PAIR_A: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_A, +- MTK_PHY_DASN_DAC_IN0_A_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_A, +- MTK_PHY_DASN_DAC_IN1_A_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ZCALEN_A); +- break; +- case PAIR_B: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_B, +- MTK_PHY_DASN_DAC_IN0_B_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_B, +- MTK_PHY_DASN_DAC_IN1_B_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_B); +- break; +- case PAIR_C: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_C, +- MTK_PHY_DASN_DAC_IN0_C_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_C, +- MTK_PHY_DASN_DAC_IN1_C_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_C); +- break; +- case PAIR_D: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN0_D, +- MTK_PHY_DASN_DAC_IN0_D_MASK); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DASN_DAC_IN1_D, +- MTK_PHY_DASN_DAC_IN1_D_MASK); +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_D); +- break; +- default: +- ret = -EINVAL; +- goto restore; +- } +- +- lower_idx = TXRESERVE_MIN; +- upper_idx = TXRESERVE_MAX; +- +- phydev_dbg(phydev, "Start TX-VCM SW cal.\n"); +- while ((upper_idx - lower_idx) > 1) { +- txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); +- ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- txreserve_val << 12 | txreserve_val << 8 | +- txreserve_val << 4 | txreserve_val); +- if (ret == 1) { +- upper_idx = txreserve_val; +- upper_ret = ret; +- } else if (ret == 0) { +- lower_idx = txreserve_val; +- lower_ret = ret; +- } else { +- goto restore; +- } +- } +- +- if (lower_idx == TXRESERVE_MIN) { +- lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- lower_idx << 12 | lower_idx << 8 | +- lower_idx << 4 | lower_idx); +- ret = lower_ret; +- } else if (upper_idx == TXRESERVE_MAX) { +- upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- upper_idx << 12 | upper_idx << 8 | +- upper_idx << 4 | upper_idx); +- ret = upper_ret; +- } +- if (ret < 0) +- goto restore; +- +- /* We calibrate TX-VCM in different logic. Check upper index and then +- * lower index. If this calibration is valid, apply lower index's +- * result. +- */ +- ret = upper_ret - lower_ret; +- if (ret == 1) { +- ret = 0; +- /* Make sure we use upper_idx in our calibration system */ +- cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- upper_idx << 12 | upper_idx << 8 | +- upper_idx << 4 | upper_idx); +- phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx); +- } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && +- lower_ret == 1) { +- ret = 0; +- cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, +- MTK_PHY_DA_RX_PSBN_TBT_MASK | +- MTK_PHY_DA_RX_PSBN_HBT_MASK | +- MTK_PHY_DA_RX_PSBN_GBE_MASK | +- MTK_PHY_DA_RX_PSBN_LP_MASK, +- lower_idx << 12 | lower_idx << 8 | +- lower_idx << 4 | lower_idx); +- phydev_warn(phydev, "TX-VCM SW cal result at low margin 0x%x\n", +- lower_idx); +- } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && +- lower_ret == 0) { +- ret = 0; +- phydev_warn(phydev, +- "TX-VCM SW cal result at high margin 0x%x\n", +- upper_idx); +- } else { +- ret = -EINVAL; +- } +- +-restore: +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ANA_CALEN); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_TXVOS_CALEN); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, +- MTK_PHY_RG_ZCALEN_A); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, +- MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | +- MTK_PHY_RG_ZCALEN_D); +- +- return ret; +-} +- +-static void mt798x_phy_common_finetune(struct phy_device *phydev) +-{ +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ +- __phy_write(phydev, 0x11, 0xc71); +- __phy_write(phydev, 0x12, 0xc); +- __phy_write(phydev, 0x10, 0x8fae); +- +- /* EnabRandUpdTrig = 1 */ +- __phy_write(phydev, 0x11, 0x2f00); +- __phy_write(phydev, 0x12, 0xe); +- __phy_write(phydev, 0x10, 0x8fb0); +- +- /* NormMseLoThresh = 85 */ +- __phy_write(phydev, 0x11, 0x55a0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x83aa); +- +- /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ +- __phy_write(phydev, 0x11, 0x240); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9680); +- +- /* TrFreeze = 0 (mt7988 default) */ +- __phy_write(phydev, 0x11, 0x0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9686); +- +- /* SSTrKp100 = 5 */ +- /* SSTrKf100 = 6 */ +- /* SSTrKp1000Mas = 5 */ +- /* SSTrKf1000Mas = 6 */ +- /* SSTrKp1000Slv = 5 */ +- /* SSTrKf1000Slv = 6 */ +- __phy_write(phydev, 0x11, 0xbaef); +- __phy_write(phydev, 0x12, 0x2e); +- __phy_write(phydev, 0x10, 0x968c); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +-} +- +-static void mt7981_phy_finetune(struct phy_device *phydev) +-{ +- u16 val[8] = { 0x01ce, 0x01c1, +- 0x020f, 0x0202, +- 0x03d0, 0x03c0, +- 0x0013, 0x0005 }; +- int i, k; +- +- /* 100M eye finetune: +- * Keep middle level of TX MLT3 shapper as default. +- * Only change TX MLT3 overshoot level here. +- */ +- for (k = 0, i = 1; i < 12; i++) { +- if (i % 3 == 0) +- continue; +- phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); +- } +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 6 */ +- __phy_write(phydev, 0x11, 0x600); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); +- +- /* VgaDecRate = 1 */ +- __phy_write(phydev, 0x11, 0x4c2a); +- __phy_write(phydev, 0x12, 0x3e); +- __phy_write(phydev, 0x10, 0x8fa4); +- +- /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, +- * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 +- */ +- __phy_write(phydev, 0x11, 0xd10a); +- __phy_write(phydev, 0x12, 0x34); +- __phy_write(phydev, 0x10, 0x8f82); +- +- /* VcoSlicerThreshBitsHigh */ +- __phy_write(phydev, 0x11, 0x5555); +- __phy_write(phydev, 0x12, 0x55); +- __phy_write(phydev, 0x10, 0x8ec0); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | +- MTK_PHY_LPF_X_AVERAGE_MASK, +- BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); +- +- /* rg_tr_lpf_cnt_val = 512 */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); +- +- /* IIR2 related */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); +- +- /* FFE peaking */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, +- MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, +- MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); +- +- /* Disable LDO pump */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); +- /* Adjust LDO output voltage */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); +-} +- +-static void mt7988_phy_finetune(struct phy_device *phydev) +-{ +- u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, +- 0x020d, 0x0206, 0x0384, 0x03d0, +- 0x03c6, 0x030a, 0x0011, 0x0005 }; +- int i; +- +- /* Set default MLT3 shaper first */ +- for (i = 0; i < 12; i++) +- phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); +- +- /* TCT finetune */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 5 */ +- __phy_write(phydev, 0x11, 0x500); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); +- +- /* VgaDecRate is 1 at default on mt7988 */ +- +- /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, +- * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 +- */ +- __phy_write(phydev, 0x11, 0xb90a); +- __phy_write(phydev, 0x12, 0x6f); +- __phy_write(phydev, 0x10, 0x8f82); +- +- /* RemAckCntLimitCtrl = 1 */ +- __phy_write(phydev, 0x11, 0xfbba); +- __phy_write(phydev, 0x12, 0xc3); +- __phy_write(phydev, 0x10, 0x87f8); +- +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, +- MTK_PHY_TR_OPEN_LOOP_EN_MASK | +- MTK_PHY_LPF_X_AVERAGE_MASK, +- BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); +- +- /* rg_tr_lpf_cnt_val = 1023 */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x3ff); +-} +- +-static void mt798x_phy_eee(struct phy_device *phydev) +-{ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120, +- MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK | +- MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK, 0x0) | +- FIELD_PREP(MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, 0x14)); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, +- MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- 0xff)); +- +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_TESTMUX_ADC_CTRL, +- MTK_PHY_RG_TXEN_DIG_MASK); +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DEV1E_REG19b, MTK_PHY_BYPASS_DSP_LPI_READY); +- +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_LP_IIR_EEE_EN); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG238, +- MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK | +- MTK_PHY_LPI_SLV_SEND_TX_EN, +- FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); +- +- /* Keep MTK_PHY_LPI_SEND_LOC_TIMER as 375 */ +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, +- MTK_PHY_LPI_TXPCS_LOC_RCV); +- +- /* This also fixes some IoT issues, such as CH340 */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, +- MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, +- FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | +- FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13)); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2D1, +- MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, +- FIELD_PREP(MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, +- 0x33) | +- MTK_PHY_LPI_SKIP_SD_SLV_TR | MTK_PHY_LPI_TR_READY | +- MTK_PHY_LPI_VCO_EEE_STG0_EN); +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG323, +- MTK_PHY_EEE_WAKE_MAS_INT_DC | +- MTK_PHY_EEE_WAKE_SLV_INT_DC); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG324, +- MTK_PHY_SMI_DETCNT_MAX_MASK, +- FIELD_PREP(MTK_PHY_SMI_DETCNT_MAX_MASK, 0x3f) | +- MTK_PHY_SMI_DET_MAX_EN); +- +- phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG326, +- MTK_PHY_LPI_MODE_SD_ON | MTK_PHY_RESET_RANDUPD_CNT | +- MTK_PHY_TREC_UPDATE_ENAB_CLR | +- MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF | +- MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* Regsigdet_sel_1000 = 0 */ +- __phy_write(phydev, 0x11, 0xb); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9690); +- +- /* REG_EEE_st2TrKf1000 = 2 */ +- __phy_write(phydev, 0x11, 0x114f); +- __phy_write(phydev, 0x12, 0x2); +- __phy_write(phydev, 0x10, 0x969a); +- +- /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ +- __phy_write(phydev, 0x11, 0x3028); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x969e); +- +- /* RegEEE_slv_wake_int_timer_tar = 8 */ +- __phy_write(phydev, 0x11, 0x5010); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a0); +- +- /* RegEEE_trfreeze_timer2 = 586 */ +- __phy_write(phydev, 0x11, 0x24a); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a8); +- +- /* RegEEE100Stg1_tar = 16 */ +- __phy_write(phydev, 0x11, 0x3210); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96b8); +- +- /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ +- __phy_write(phydev, 0x11, 0x1463); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96ca); +- +- /* DfeTailEnableVgaThresh1000 = 27 */ +- __phy_write(phydev, 0x11, 0x36); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8f80); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); +- __phy_modify(phydev, MTK_PHY_LPI_REG_14, +- MTK_PHY_LPI_WAKE_TIMER_1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); +- +- __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, +- FIELD_PREP(MTK_PHY_SMI_DET_ON_THRESH_MASK, 0xc)); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, +- MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, +- MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, +- 0xff)); +-} +- +-static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, +- u8 start_pair, u8 end_pair) +-{ +- u8 pair_n; +- int ret; +- +- for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { +- /* TX_OFFSET & TX_AMP have no SW calibration. */ +- switch (cal_item) { +- case TX_VCM: +- ret = tx_vcm_cal_sw(phydev, pair_n); +- break; +- default: +- return -EINVAL; +- } +- if (ret) +- return ret; +- } +- return 0; +-} +- +-static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, +- u8 start_pair, u8 end_pair, u32 *buf) +-{ +- u8 pair_n; +- int ret; +- +- for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { +- /* TX_VCM has no efuse calibration. */ +- switch (cal_item) { +- case REXT: +- ret = rext_cal_efuse(phydev, buf); +- break; +- case TX_OFFSET: +- ret = tx_offset_cal_efuse(phydev, buf); +- break; +- case TX_AMP: +- ret = tx_amp_cal_efuse(phydev, buf); +- break; +- case TX_R50: +- ret = tx_r50_cal_efuse(phydev, buf, pair_n); +- break; +- default: +- return -EINVAL; +- } +- if (ret) +- return ret; +- } +- +- return 0; +-} +- +-static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, +- enum CAL_MODE cal_mode, u8 start_pair, +- u8 end_pair, u32 *buf) +-{ +- int ret; +- +- switch (cal_mode) { +- case EFUSE_M: +- ret = cal_efuse(phydev, cal_item, start_pair, +- end_pair, buf); +- break; +- case SW_M: +- ret = cal_sw(phydev, cal_item, start_pair, end_pair); +- break; +- default: +- return -EINVAL; +- } +- +- if (ret) { +- phydev_err(phydev, "cal %d failed\n", cal_item); +- return -EIO; +- } +- +- return 0; +-} +- +-static int mt798x_phy_calibration(struct phy_device *phydev) +-{ +- struct nvmem_cell *cell; +- int ret = 0; +- size_t len; +- u32 *buf; +- +- cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); +- if (IS_ERR(cell)) { +- if (PTR_ERR(cell) == -EPROBE_DEFER) +- return PTR_ERR(cell); +- return 0; +- } +- +- buf = (u32 *)nvmem_cell_read(cell, &len); +- if (IS_ERR(buf)) +- return PTR_ERR(buf); +- nvmem_cell_put(cell); +- +- if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) { +- phydev_err(phydev, "invalid efuse data\n"); +- ret = -EINVAL; +- goto out; +- } +- +- ret = start_cal(phydev, REXT, EFUSE_M, NO_PAIR, NO_PAIR, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_OFFSET, EFUSE_M, NO_PAIR, NO_PAIR, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_AMP, EFUSE_M, NO_PAIR, NO_PAIR, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_R50, EFUSE_M, PAIR_A, PAIR_D, buf); +- if (ret) +- goto out; +- ret = start_cal(phydev, TX_VCM, SW_M, PAIR_A, PAIR_A, buf); +- if (ret) +- goto out; +- +-out: +- kfree(buf); +- return ret; +-} +- +-static int mt798x_phy_config_init(struct phy_device *phydev) +-{ +- switch (phydev->drv->phy_id) { +- case MTK_GPHY_ID_MT7981: +- mt7981_phy_finetune(phydev); +- break; +- case MTK_GPHY_ID_MT7988: +- mt7988_phy_finetune(phydev); +- break; +- } +- +- mt798x_phy_common_finetune(phydev); +- mt798x_phy_eee(phydev); +- +- return mt798x_phy_calibration(phydev); +-} +- +-static int mt798x_phy_hw_led_on_set(struct phy_device *phydev, u8 index, +- bool on) +-{ +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (on) +- changed = !test_and_set_bit(bit_on, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_on, &priv->led_state); +- +- changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_MASK, +- on ? MTK_PHY_LED_ON_FORCE_ON : 0); +- else +- return 0; +-} +- +-static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, +- bool blinking) +-{ +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (blinking) +- changed = !test_and_set_bit(bit_blink, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_blink, &priv->led_state); +- +- changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, +- blinking ? +- MTK_PHY_LED_BLINK_FORCE_BLINK : 0); +- else +- return 0; +-} +- +-static int mt798x_phy_led_blink_set(struct phy_device *phydev, u8 index, +- unsigned long *delay_on, +- unsigned long *delay_off) +-{ +- bool blinking = false; +- int err = 0; +- +- if (index > 1) +- return -EINVAL; +- +- if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { +- blinking = true; +- *delay_on = 50; +- *delay_off = 50; +- } +- +- err = mt798x_phy_hw_led_blink_set(phydev, index, blinking); +- if (err) +- return err; +- +- return mt798x_phy_hw_led_on_set(phydev, index, false); +-} +- +-static int mt798x_phy_led_brightness_set(struct phy_device *phydev, +- u8 index, enum led_brightness value) +-{ +- int err; +- +- err = mt798x_phy_hw_led_blink_set(phydev, index, false); +- if (err) +- return err; +- +- return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); +-} +- +-static const unsigned long supported_triggers = +- BIT(TRIGGER_NETDEV_FULL_DUPLEX) | +- BIT(TRIGGER_NETDEV_HALF_DUPLEX) | +- BIT(TRIGGER_NETDEV_LINK) | +- BIT(TRIGGER_NETDEV_LINK_10) | +- BIT(TRIGGER_NETDEV_LINK_100) | +- BIT(TRIGGER_NETDEV_LINK_1000) | +- BIT(TRIGGER_NETDEV_RX) | +- BIT(TRIGGER_NETDEV_TX); +- +-static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- if (index > 1) +- return -EINVAL; +- +- /* All combinations of the supported triggers are allowed */ +- if (rules & ~supported_triggers) +- return -EOPNOTSUPP; +- +- return 0; +-}; +- +-static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, +- unsigned long *rules) +-{ +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- int on, blink; +- +- if (index > 1) +- return -EINVAL; +- +- on = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); +- +- if (on < 0) +- return -EIO; +- +- blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL); +- if (blink < 0) +- return -EIO; +- +- if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || +- (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- if (on & MTK_PHY_LED_ON_FORCE_ON) +- set_bit(bit_on, &priv->led_state); +- else +- clear_bit(bit_on, &priv->led_state); +- +- if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) +- set_bit(bit_blink, &priv->led_state); +- else +- clear_bit(bit_blink, &priv->led_state); +- +- if (!rules) +- return 0; +- +- if (on & MTK_PHY_LED_ON_LINK) +- *rules |= BIT(TRIGGER_NETDEV_LINK); +- +- if (on & MTK_PHY_LED_ON_LINK10) +- *rules |= BIT(TRIGGER_NETDEV_LINK_10); +- +- if (on & MTK_PHY_LED_ON_LINK100) +- *rules |= BIT(TRIGGER_NETDEV_LINK_100); +- +- if (on & MTK_PHY_LED_ON_LINK1000) +- *rules |= BIT(TRIGGER_NETDEV_LINK_1000); +- +- if (on & MTK_PHY_LED_ON_FDX) +- *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); +- +- if (on & MTK_PHY_LED_ON_HDX) +- *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); +- +- if (blink & MTK_PHY_LED_BLINK_RX) +- *rules |= BIT(TRIGGER_NETDEV_RX); +- +- if (blink & MTK_PHY_LED_BLINK_TX) +- *rules |= BIT(TRIGGER_NETDEV_TX); +- +- return 0; +-}; +- +-static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, +- unsigned long rules) +-{ +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- u16 on = 0, blink = 0; +- int ret; +- +- if (index > 1) +- return -EINVAL; +- +- if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) +- on |= MTK_PHY_LED_ON_FDX; +- +- if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) +- on |= MTK_PHY_LED_ON_HDX; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK10; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK100; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK1000; +- +- if (rules & BIT(TRIGGER_NETDEV_RX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000RX : 0)) : +- MTK_PHY_LED_BLINK_RX; +- } +- +- if (rules & BIT(TRIGGER_NETDEV_TX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000TX : 0)) : +- MTK_PHY_LED_BLINK_TX; +- } +- +- if (blink || on) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | +- MTK_PHY_LED_ON_LINK, +- on); +- +- if (ret) +- return ret; +- +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, blink); +-}; +- +-static bool mt7988_phy_led_get_polarity(struct phy_device *phydev, int led_num) +-{ +- struct mtk_socphy_shared *priv = phydev->shared->priv; +- u32 polarities; +- +- if (led_num == 0) +- polarities = ~(priv->boottrap); +- else +- polarities = MTK_PHY_LED1_DEFAULT_POLARITIES; +- +- if (polarities & BIT(phydev->mdio.addr)) +- return true; +- +- return false; +-} +- +-static int mt7988_phy_fix_leds_polarities(struct phy_device *phydev) +-{ +- struct pinctrl *pinctrl; +- int index; +- +- /* Setup LED polarity according to bootstrap use of LED pins */ +- for (index = 0; index < 2; ++index) +- phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_POLARITY, +- mt7988_phy_led_get_polarity(phydev, index) ? +- MTK_PHY_LED_ON_POLARITY : 0); +- +- /* Only now setup pinctrl to avoid bogus blinking */ +- pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); +- if (IS_ERR(pinctrl)) +- dev_err(&phydev->mdio.bus->dev, +- "Failed to setup PHY LED pinctrl\n"); +- +- return 0; +-} +- +-static int mt7988_phy_probe_shared(struct phy_device *phydev) +-{ +- struct device_node *np = dev_of_node(&phydev->mdio.bus->dev); +- struct mtk_socphy_shared *shared = phydev->shared->priv; +- struct regmap *regmap; +- u32 reg; +- int ret; +- +- /* The LED0 of the 4 PHYs in MT7988 are wired to SoC pins LED_A, LED_B, +- * LED_C and LED_D respectively. At the same time those pins are used to +- * bootstrap configuration of the reference clock source (LED_A), +- * DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D). +- * In practice this is done using a LED and a resistor pulling the pin +- * either to GND or to VIO. +- * The detected value at boot time is accessible at run-time using the +- * TPBANK0 register located in the gpio base of the pinctrl, in order +- * to read it here it needs to be referenced by a phandle called +- * 'mediatek,pio' in the MDIO bus hosting the PHY. +- * The 4 bits in TPBANK0 are kept as package shared data and are used to +- * set LED polarity for each of the LED0. +- */ +- regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,pio"); +- if (IS_ERR(regmap)) +- return PTR_ERR(regmap); +- +- ret = regmap_read(regmap, RG_GPIO_MISC_TPBANK0, ®); +- if (ret) +- return ret; +- +- shared->boottrap = FIELD_GET(RG_GPIO_MISC_TPBANK0_BOOTMODE, reg); +- +- return 0; +-} +- +-static void mt798x_phy_leds_state_init(struct phy_device *phydev) +-{ +- int i; +- +- for (i = 0; i < 2; ++i) +- mt798x_phy_led_hw_control_get(phydev, i, NULL); +-} +- +-static int mt7988_phy_probe(struct phy_device *phydev) +-{ +- struct mtk_socphy_shared *shared; +- struct mtk_socphy_priv *priv; +- int err; +- +- if (phydev->mdio.addr > 3) +- return -EINVAL; +- +- err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0, +- sizeof(struct mtk_socphy_shared)); +- if (err) +- return err; +- +- if (phy_package_probe_once(phydev)) { +- err = mt7988_phy_probe_shared(phydev); +- if (err) +- return err; +- } +- +- shared = phydev->shared->priv; +- priv = &shared->priv[phydev->mdio.addr]; +- +- phydev->priv = priv; +- +- mt798x_phy_leds_state_init(phydev); +- +- err = mt7988_phy_fix_leds_polarities(phydev); +- if (err) +- return err; +- +- /* Disable TX power saving at probing to: +- * 1. Meet common mode compliance test criteria +- * 2. Make sure that TX-VCM calibration works fine +- */ +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, +- MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); +- +- return mt798x_phy_calibration(phydev); +-} +- +-static int mt7981_phy_probe(struct phy_device *phydev) +-{ +- struct mtk_socphy_priv *priv; +- +- priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct mtk_socphy_priv), +- GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- phydev->priv = priv; +- +- mt798x_phy_leds_state_init(phydev); +- +- return mt798x_phy_calibration(phydev); +-} +- +-static struct phy_driver mtk_socphy_driver[] = { +- { +- PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), +- .name = "MediaTek MT7981 PHY", +- .config_init = mt798x_phy_config_init, +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .probe = mt7981_phy_probe, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, +- .led_blink_set = mt798x_phy_led_blink_set, +- .led_brightness_set = mt798x_phy_led_brightness_set, +- .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +- .led_hw_control_set = mt798x_phy_led_hw_control_set, +- .led_hw_control_get = mt798x_phy_led_hw_control_get, +- }, +- { +- PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988), +- .name = "MediaTek MT7988 PHY", +- .config_init = mt798x_phy_config_init, +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .probe = mt7988_phy_probe, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, +- .led_blink_set = mt798x_phy_led_blink_set, +- .led_brightness_set = mt798x_phy_led_brightness_set, +- .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +- .led_hw_control_set = mt798x_phy_led_hw_control_set, +- .led_hw_control_get = mt798x_phy_led_hw_control_get, +- }, +-}; +- +-module_phy_driver(mtk_socphy_driver); +- +-static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { +- { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, +- { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, +- { } +-}; +- +-MODULE_DESCRIPTION("MediaTek SoC Gigabit Ethernet PHY driver"); +-MODULE_AUTHOR("Daniel Golle "); +-MODULE_AUTHOR("SkyLake Huang "); +-MODULE_LICENSE("GPL"); +- +-MODULE_DEVICE_TABLE(mdio, mtk_socphy_tbl); +--- a/drivers/net/phy/mediatek-ge.c ++++ /dev/null +@@ -1,114 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0+ +-#include +-#include +-#include +- +-#define MTK_EXT_PAGE_ACCESS 0x1f +-#define MTK_PHY_PAGE_STANDARD 0x0000 +-#define MTK_PHY_PAGE_EXTENDED 0x0001 +-#define MTK_PHY_PAGE_EXTENDED_2 0x0002 +-#define MTK_PHY_PAGE_EXTENDED_3 0x0003 +-#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 +- +-static int mtk_gephy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_gephy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- +-static void mtk_gephy_config_init(struct phy_device *phydev) +-{ +- /* Disable EEE */ +- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); +- +- /* Enable HW auto downshift */ +- phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); +- +- /* Increase SlvDPSready time */ +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- __phy_write(phydev, 0x10, 0xafae); +- __phy_write(phydev, 0x12, 0x2f); +- __phy_write(phydev, 0x10, 0x8fae); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +- +- /* Adjust 100_mse_threshold */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); +- +- /* Disable mcc */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); +-} +- +-static int mt7530_phy_config_init(struct phy_device *phydev) +-{ +- mtk_gephy_config_init(phydev); +- +- /* Increase post_update_timer */ +- phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); +- +- return 0; +-} +- +-static int mt7531_phy_config_init(struct phy_device *phydev) +-{ +- mtk_gephy_config_init(phydev); +- +- /* PHY link down power saving enable */ +- phy_set_bits(phydev, 0x17, BIT(4)); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); +- +- /* Set TX Pair delay selection */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); +- +- return 0; +-} +- +-static struct phy_driver mtk_gephy_driver[] = { +- { +- PHY_ID_MATCH_EXACT(0x03a29412), +- .name = "MediaTek MT7530 PHY", +- .config_init = mt7530_phy_config_init, +- /* Interrupts are handled by the switch, not the PHY +- * itself. +- */ +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, +- }, +- { +- PHY_ID_MATCH_EXACT(0x03a29441), +- .name = "MediaTek MT7531 PHY", +- .config_init = mt7531_phy_config_init, +- /* Interrupts are handled by the switch, not the PHY +- * itself. +- */ +- .config_intr = genphy_no_config_intr, +- .handle_interrupt = genphy_handle_interrupt_no_ack, +- .suspend = genphy_suspend, +- .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, +- }, +-}; +- +-module_phy_driver(mtk_gephy_driver); +- +-static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { +- { PHY_ID_MATCH_EXACT(0x03a29441) }, +- { PHY_ID_MATCH_EXACT(0x03a29412) }, +- { } +-}; +- +-MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); +-MODULE_AUTHOR("DENG, Qingfang "); +-MODULE_LICENSE("GPL"); +- +-MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl); +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -0,0 +1,1610 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MTK_GPHY_ID_MT7981 0x03a29461 ++#define MTK_GPHY_ID_MT7988 0x03a29481 ++ ++#define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_STANDARD 0x0000 ++#define MTK_PHY_PAGE_EXTENDED_3 0x0003 ++ ++#define MTK_PHY_LPI_REG_14 0x14 ++#define MTK_PHY_LPI_WAKE_TIMER_1000_MASK GENMASK(8, 0) ++ ++#define MTK_PHY_LPI_REG_1c 0x1c ++#define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) ++ ++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 ++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 ++ ++#define ANALOG_INTERNAL_OPERATION_MAX_US 20 ++#define TXRESERVE_MIN 0 ++#define TXRESERVE_MAX 7 ++ ++#define MTK_PHY_ANARG_RG 0x10 ++#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) ++ ++/* Registers on MDIO_MMD_VEND1 */ ++#define MTK_PHY_TXVLD_DA_RG 0x12 ++#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) ++#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 0x16 ++#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) ++#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 0x17 ++#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 0x18 ++#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 0x19 ++#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 0x20 ++#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 0x21 ++#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 0x22 ++#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_RXADC_CTRL_RG7 0xc6 ++#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) ++ ++#define MTK_PHY_RXADC_CTRL_RG9 0xc8 ++#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) ++#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) ++#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) ++#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) ++ ++#define MTK_PHY_LDO_OUTPUT_V 0xd7 ++ ++#define MTK_PHY_RG_ANA_CAL_RG0 0xdb ++#define MTK_PHY_RG_CAL_CKINV BIT(12) ++#define MTK_PHY_RG_ANA_CALEN BIT(8) ++#define MTK_PHY_RG_ZCALEN_A BIT(0) ++ ++#define MTK_PHY_RG_ANA_CAL_RG1 0xdc ++#define MTK_PHY_RG_ZCALEN_B BIT(12) ++#define MTK_PHY_RG_ZCALEN_C BIT(8) ++#define MTK_PHY_RG_ZCALEN_D BIT(4) ++#define MTK_PHY_RG_TXVOS_CALEN BIT(0) ++ ++#define MTK_PHY_RG_ANA_CAL_RG5 0xe0 ++#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) ++ ++#define MTK_PHY_RG_TX_FILTER 0xfe ++ ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120 0x120 ++#define MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK GENMASK(12, 8) ++#define MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK GENMASK(4, 0) ++ ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122 0x122 ++#define MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK GENMASK(7, 0) ++ ++#define MTK_PHY_RG_TESTMUX_ADC_CTRL 0x144 ++#define MTK_PHY_RG_TXEN_DIG_MASK GENMASK(5, 5) ++ ++#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B 0x172 ++#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) ++#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) ++ ++#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D 0x173 ++#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) ++#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) ++ ++#define MTK_PHY_RG_AD_CAL_COMP 0x17a ++#define MTK_PHY_AD_CAL_COMP_OUT_MASK GENMASK(8, 8) ++ ++#define MTK_PHY_RG_AD_CAL_CLK 0x17b ++#define MTK_PHY_DA_CAL_CLK BIT(0) ++ ++#define MTK_PHY_RG_AD_CALIN 0x17c ++#define MTK_PHY_DA_CALIN_FLAG BIT(0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_A 0x17d ++#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_B 0x17e ++#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_C 0x17f ++#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_D 0x180 ++#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_A 0x181 ++#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_B 0x182 ++#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_C 0x183 ++#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_D 0x184 ++#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DEV1E_REG19b 0x19b ++#define MTK_PHY_BYPASS_DSP_LPI_READY BIT(8) ++ ++#define MTK_PHY_RG_LP_IIR2_K1_L 0x22a ++#define MTK_PHY_RG_LP_IIR2_K1_U 0x22b ++#define MTK_PHY_RG_LP_IIR2_K2_L 0x22c ++#define MTK_PHY_RG_LP_IIR2_K2_U 0x22d ++#define MTK_PHY_RG_LP_IIR2_K3_L 0x22e ++#define MTK_PHY_RG_LP_IIR2_K3_U 0x22f ++#define MTK_PHY_RG_LP_IIR2_K4_L 0x230 ++#define MTK_PHY_RG_LP_IIR2_K4_U 0x231 ++#define MTK_PHY_RG_LP_IIR2_K5_L 0x232 ++#define MTK_PHY_RG_LP_IIR2_K5_U 0x233 ++ ++#define MTK_PHY_RG_DEV1E_REG234 0x234 ++#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) ++#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) ++#define MTK_PHY_TR_LP_IIR_EEE_EN BIT(12) ++ ++#define MTK_PHY_RG_LPF_CNT_VAL 0x235 ++ ++#define MTK_PHY_RG_DEV1E_REG238 0x238 ++#define MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK GENMASK(8, 0) ++#define MTK_PHY_LPI_SLV_SEND_TX_EN BIT(12) ++ ++#define MTK_PHY_RG_DEV1E_REG239 0x239 ++#define MTK_PHY_LPI_SEND_LOC_TIMER_MASK GENMASK(8, 0) ++#define MTK_PHY_LPI_TXPCS_LOC_RCV BIT(12) ++ ++#define MTK_PHY_RG_DEV1E_REG27C 0x27c ++#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) ++#define MTK_PHY_RG_DEV1E_REG27D 0x27d ++#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) ++ ++#define MTK_PHY_RG_DEV1E_REG2C7 0x2c7 ++#define MTK_PHY_MAX_GAIN_MASK GENMASK(4, 0) ++#define MTK_PHY_MIN_GAIN_MASK GENMASK(12, 8) ++ ++#define MTK_PHY_RG_DEV1E_REG2D1 0x2d1 ++#define MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK GENMASK(7, 0) ++#define MTK_PHY_LPI_SKIP_SD_SLV_TR BIT(8) ++#define MTK_PHY_LPI_TR_READY BIT(9) ++#define MTK_PHY_LPI_VCO_EEE_STG0_EN BIT(10) ++ ++#define MTK_PHY_RG_DEV1E_REG323 0x323 ++#define MTK_PHY_EEE_WAKE_MAS_INT_DC BIT(0) ++#define MTK_PHY_EEE_WAKE_SLV_INT_DC BIT(4) ++ ++#define MTK_PHY_RG_DEV1E_REG324 0x324 ++#define MTK_PHY_SMI_DETCNT_MAX_MASK GENMASK(5, 0) ++#define MTK_PHY_SMI_DET_MAX_EN BIT(8) ++ ++#define MTK_PHY_RG_DEV1E_REG326 0x326 ++#define MTK_PHY_LPI_MODE_SD_ON BIT(0) ++#define MTK_PHY_RESET_RANDUPD_CNT BIT(1) ++#define MTK_PHY_TREC_UPDATE_ENAB_CLR BIT(2) ++#define MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF BIT(4) ++#define MTK_PHY_TR_READY_SKIP_AFE_WAKEUP BIT(5) ++ ++#define MTK_PHY_LDO_PUMP_EN_PAIRAB 0x502 ++#define MTK_PHY_LDO_PUMP_EN_PAIRCD 0x503 ++ ++#define MTK_PHY_DA_TX_R50_PAIR_A 0x53d ++#define MTK_PHY_DA_TX_R50_PAIR_B 0x53e ++#define MTK_PHY_DA_TX_R50_PAIR_C 0x53f ++#define MTK_PHY_DA_TX_R50_PAIR_D 0x540 ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define MTK_PHY_LED0_ON_CTRL 0x24 ++#define MTK_PHY_LED1_ON_CTRL 0x26 ++#define MTK_PHY_LED_ON_MASK GENMASK(6, 0) ++#define MTK_PHY_LED_ON_LINK1000 BIT(0) ++#define MTK_PHY_LED_ON_LINK100 BIT(1) ++#define MTK_PHY_LED_ON_LINK10 BIT(2) ++#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\ ++ MTK_PHY_LED_ON_LINK100 |\ ++ MTK_PHY_LED_ON_LINK1000) ++#define MTK_PHY_LED_ON_LINKDOWN BIT(3) ++#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ ++#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ ++#define MTK_PHY_LED_ON_FORCE_ON BIT(6) ++#define MTK_PHY_LED_ON_POLARITY BIT(14) ++#define MTK_PHY_LED_ON_ENABLE BIT(15) ++ ++#define MTK_PHY_LED0_BLINK_CTRL 0x25 ++#define MTK_PHY_LED1_BLINK_CTRL 0x27 ++#define MTK_PHY_LED_BLINK_1000TX BIT(0) ++#define MTK_PHY_LED_BLINK_1000RX BIT(1) ++#define MTK_PHY_LED_BLINK_100TX BIT(2) ++#define MTK_PHY_LED_BLINK_100RX BIT(3) ++#define MTK_PHY_LED_BLINK_10TX BIT(4) ++#define MTK_PHY_LED_BLINK_10RX BIT(5) ++#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\ ++ MTK_PHY_LED_BLINK_100RX |\ ++ MTK_PHY_LED_BLINK_1000RX) ++#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\ ++ MTK_PHY_LED_BLINK_100TX |\ ++ MTK_PHY_LED_BLINK_1000TX) ++#define MTK_PHY_LED_BLINK_COLLISION BIT(6) ++#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) ++#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) ++#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) ++ ++#define MTK_PHY_LED1_DEFAULT_POLARITIES BIT(1) ++ ++#define MTK_PHY_RG_BG_RASEL 0x115 ++#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) ++ ++/* 'boottrap' register reflecting the configuration of the 4 PHY LEDs */ ++#define RG_GPIO_MISC_TPBANK0 0x6f0 ++#define RG_GPIO_MISC_TPBANK0_BOOTMODE GENMASK(11, 8) ++ ++/* These macro privides efuse parsing for internal phy. */ ++#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) ++ ++#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) ++ ++#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) ++ ++#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) ++#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) ++ ++enum { ++ NO_PAIR, ++ PAIR_A, ++ PAIR_B, ++ PAIR_C, ++ PAIR_D, ++}; ++ ++enum calibration_mode { ++ EFUSE_K, ++ SW_K ++}; ++ ++enum CAL_ITEM { ++ REXT, ++ TX_OFFSET, ++ TX_AMP, ++ TX_R50, ++ TX_VCM ++}; ++ ++enum CAL_MODE { ++ EFUSE_M, ++ SW_M ++}; ++ ++#define MTK_PHY_LED_STATE_FORCE_ON 0 ++#define MTK_PHY_LED_STATE_FORCE_BLINK 1 ++#define MTK_PHY_LED_STATE_NETDEV 2 ++ ++struct mtk_socphy_priv { ++ unsigned long led_state; ++}; ++ ++struct mtk_socphy_shared { ++ u32 boottrap; ++ struct mtk_socphy_priv priv[4]; ++}; ++ ++static int mtk_socphy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); ++} ++ ++static int mtk_socphy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); ++} ++ ++/* One calibration cycle consists of: ++ * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high ++ * until AD_CAL_COMP is ready to output calibration result. ++ * 2.Wait until DA_CAL_CLK is available. ++ * 3.Fetch AD_CAL_COMP_OUT. ++ */ ++static int cal_cycle(struct phy_device *phydev, int devad, ++ u32 regnum, u16 mask, u16 cal_val) ++{ ++ int reg_val; ++ int ret; ++ ++ phy_modify_mmd(phydev, devad, regnum, ++ mask, cal_val); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, ++ MTK_PHY_DA_CALIN_FLAG); ++ ++ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_AD_CAL_CLK, reg_val, ++ reg_val & MTK_PHY_DA_CAL_CLK, 500, ++ ANALOG_INTERNAL_OPERATION_MAX_US, ++ false); ++ if (ret) { ++ phydev_err(phydev, "Calibration cycle timeout\n"); ++ return ret; ++ } ++ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, ++ MTK_PHY_DA_CALIN_FLAG); ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP); ++ if (ret < 0) ++ return ret; ++ ret = FIELD_GET(MTK_PHY_AD_CAL_COMP_OUT_MASK, ret); ++ phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); ++ ++ return ret; ++} ++ ++static int rext_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, ++ MTK_PHY_RG_BG_RASEL_MASK, buf[1]); ++ ++ return 0; ++} ++ ++static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 rext_cal_val[2]; ++ ++ rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); ++ rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); ++ rext_fill_result(phydev, rext_cal_val); ++ ++ return 0; ++} ++ ++static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, ++ MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, ++ MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, ++ MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, ++ MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); ++ ++ return 0; ++} ++ ++static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 tx_offset_cal_val[4]; ++ ++ tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); ++ tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); ++ tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); ++ tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); ++ ++ tx_offset_fill_result(phydev, tx_offset_cal_val); ++ ++ return 0; ++} ++ ++static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ const int vals_9481[16] = { 10, 6, 6, 10, ++ 10, 6, 6, 10, ++ 10, 6, 6, 10, ++ 10, 6, 6, 10 }; ++ const int vals_9461[16] = { 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7 }; ++ int bias[16] = {}; ++ int i; ++ ++ switch (phydev->drv->phy_id) { ++ case MTK_GPHY_ID_MT7981: ++ /* We add some calibration to efuse values ++ * due to board level influence. ++ * GBE: +7, TBT: +1, HBT: +4, TST: +7 ++ */ ++ memcpy(bias, (const void *)vals_9461, sizeof(bias)); ++ break; ++ case MTK_GPHY_ID_MT7988: ++ memcpy(bias, (const void *)vals_9481, sizeof(bias)); ++ break; ++ } ++ ++ /* Prevent overflow */ ++ for (i = 0; i < 12; i++) { ++ if (buf[i >> 2] + bias[i] > 63) { ++ buf[i >> 2] = 63; ++ bias[i] = 0; ++ } ++ } ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, ++ MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, ++ buf[0] + bias[0])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, ++ MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, ++ buf[0] + bias[1])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, ++ MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, ++ buf[0] + bias[2])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, ++ MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_A_TST_MASK, ++ buf[0] + bias[3])); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, ++ MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, ++ buf[1] + bias[4])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, ++ MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, ++ buf[1] + bias[5])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, ++ MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, ++ buf[1] + bias[6])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, ++ MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_B_TST_MASK, ++ buf[1] + bias[7])); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, ++ MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, ++ buf[2] + bias[8])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, ++ MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, ++ buf[2] + bias[9])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, ++ MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, ++ buf[2] + bias[10])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, ++ MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_C_TST_MASK, ++ buf[2] + bias[11])); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, ++ MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, ++ buf[3] + bias[12])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, ++ MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, ++ buf[3] + bias[13])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, ++ MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, ++ buf[3] + bias[14])); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, ++ MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ FIELD_PREP(MTK_PHY_DA_TX_I2MPB_D_TST_MASK, ++ buf[3] + bias[15])); ++ ++ return 0; ++} ++ ++static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 tx_amp_cal_val[4]; ++ ++ tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); ++ tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); ++ tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); ++ tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); ++ tx_amp_fill_result(phydev, tx_amp_cal_val); ++ ++ return 0; ++} ++ ++static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, ++ u8 txg_calen_x) ++{ ++ int bias = 0; ++ u16 reg, val; ++ ++ if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) ++ bias = -1; ++ ++ val = clamp_val(bias + tx_r50_cal_val, 0, 63); ++ ++ switch (txg_calen_x) { ++ case PAIR_A: ++ reg = MTK_PHY_DA_TX_R50_PAIR_A; ++ break; ++ case PAIR_B: ++ reg = MTK_PHY_DA_TX_R50_PAIR_B; ++ break; ++ case PAIR_C: ++ reg = MTK_PHY_DA_TX_R50_PAIR_C; ++ break; ++ case PAIR_D: ++ reg = MTK_PHY_DA_TX_R50_PAIR_D; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); ++ ++ return 0; ++} ++ ++static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, ++ u8 txg_calen_x) ++{ ++ u16 tx_r50_cal_val; ++ ++ switch (txg_calen_x) { ++ case PAIR_A: ++ tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); ++ break; ++ case PAIR_B: ++ tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); ++ break; ++ case PAIR_C: ++ tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); ++ break; ++ case PAIR_D: ++ tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); ++ break; ++ default: ++ return -EINVAL; ++ } ++ tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); ++ ++ return 0; ++} ++ ++static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) ++{ ++ u8 lower_idx, upper_idx, txreserve_val; ++ u8 lower_ret, upper_ret; ++ int ret; ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ANA_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_CAL_CKINV); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ ++ switch (rg_txreserve_x) { ++ case PAIR_A: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_A, ++ MTK_PHY_DASN_DAC_IN0_A_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_A, ++ MTK_PHY_DASN_DAC_IN1_A_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ZCALEN_A); ++ break; ++ case PAIR_B: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_B, ++ MTK_PHY_DASN_DAC_IN0_B_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_B, ++ MTK_PHY_DASN_DAC_IN1_B_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_B); ++ break; ++ case PAIR_C: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_C, ++ MTK_PHY_DASN_DAC_IN0_C_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_C, ++ MTK_PHY_DASN_DAC_IN1_C_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_C); ++ break; ++ case PAIR_D: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_D, ++ MTK_PHY_DASN_DAC_IN0_D_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_D, ++ MTK_PHY_DASN_DAC_IN1_D_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_D); ++ break; ++ default: ++ ret = -EINVAL; ++ goto restore; ++ } ++ ++ lower_idx = TXRESERVE_MIN; ++ upper_idx = TXRESERVE_MAX; ++ ++ phydev_dbg(phydev, "Start TX-VCM SW cal.\n"); ++ while ((upper_idx - lower_idx) > 1) { ++ txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); ++ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ txreserve_val << 12 | txreserve_val << 8 | ++ txreserve_val << 4 | txreserve_val); ++ if (ret == 1) { ++ upper_idx = txreserve_val; ++ upper_ret = ret; ++ } else if (ret == 0) { ++ lower_idx = txreserve_val; ++ lower_ret = ret; ++ } else { ++ goto restore; ++ } ++ } ++ ++ if (lower_idx == TXRESERVE_MIN) { ++ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ lower_idx << 12 | lower_idx << 8 | ++ lower_idx << 4 | lower_idx); ++ ret = lower_ret; ++ } else if (upper_idx == TXRESERVE_MAX) { ++ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ upper_idx << 12 | upper_idx << 8 | ++ upper_idx << 4 | upper_idx); ++ ret = upper_ret; ++ } ++ if (ret < 0) ++ goto restore; ++ ++ /* We calibrate TX-VCM in different logic. Check upper index and then ++ * lower index. If this calibration is valid, apply lower index's ++ * result. ++ */ ++ ret = upper_ret - lower_ret; ++ if (ret == 1) { ++ ret = 0; ++ /* Make sure we use upper_idx in our calibration system */ ++ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ upper_idx << 12 | upper_idx << 8 | ++ upper_idx << 4 | upper_idx); ++ phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx); ++ } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && ++ lower_ret == 1) { ++ ret = 0; ++ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ lower_idx << 12 | lower_idx << 8 | ++ lower_idx << 4 | lower_idx); ++ phydev_warn(phydev, "TX-VCM SW cal result at low margin 0x%x\n", ++ lower_idx); ++ } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && ++ lower_ret == 0) { ++ ret = 0; ++ phydev_warn(phydev, ++ "TX-VCM SW cal result at high margin 0x%x\n", ++ upper_idx); ++ } else { ++ ret = -EINVAL; ++ } ++ ++restore: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ANA_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ZCALEN_A); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | ++ MTK_PHY_RG_ZCALEN_D); ++ ++ return ret; ++} ++ ++static void mt798x_phy_common_finetune(struct phy_device *phydev) ++{ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ ++ __phy_write(phydev, 0x11, 0xc71); ++ __phy_write(phydev, 0x12, 0xc); ++ __phy_write(phydev, 0x10, 0x8fae); ++ ++ /* EnabRandUpdTrig = 1 */ ++ __phy_write(phydev, 0x11, 0x2f00); ++ __phy_write(phydev, 0x12, 0xe); ++ __phy_write(phydev, 0x10, 0x8fb0); ++ ++ /* NormMseLoThresh = 85 */ ++ __phy_write(phydev, 0x11, 0x55a0); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x83aa); ++ ++ /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ ++ __phy_write(phydev, 0x11, 0x240); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x9680); ++ ++ /* TrFreeze = 0 (mt7988 default) */ ++ __phy_write(phydev, 0x11, 0x0); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x9686); ++ ++ /* SSTrKp100 = 5 */ ++ /* SSTrKf100 = 6 */ ++ /* SSTrKp1000Mas = 5 */ ++ /* SSTrKf1000Mas = 6 */ ++ /* SSTrKp1000Slv = 5 */ ++ /* SSTrKf1000Slv = 6 */ ++ __phy_write(phydev, 0x11, 0xbaef); ++ __phy_write(phydev, 0x12, 0x2e); ++ __phy_write(phydev, 0x10, 0x968c); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++} ++ ++static void mt7981_phy_finetune(struct phy_device *phydev) ++{ ++ u16 val[8] = { 0x01ce, 0x01c1, ++ 0x020f, 0x0202, ++ 0x03d0, 0x03c0, ++ 0x0013, 0x0005 }; ++ int i, k; ++ ++ /* 100M eye finetune: ++ * Keep middle level of TX MLT3 shapper as default. ++ * Only change TX MLT3 overshoot level here. ++ */ ++ for (k = 0, i = 1; i < 12; i++) { ++ if (i % 3 == 0) ++ continue; ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); ++ } ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* ResetSyncOffset = 6 */ ++ __phy_write(phydev, 0x11, 0x600); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8fc0); ++ ++ /* VgaDecRate = 1 */ ++ __phy_write(phydev, 0x11, 0x4c2a); ++ __phy_write(phydev, 0x12, 0x3e); ++ __phy_write(phydev, 0x10, 0x8fa4); ++ ++ /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, ++ * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 ++ */ ++ __phy_write(phydev, 0x11, 0xd10a); ++ __phy_write(phydev, 0x12, 0x34); ++ __phy_write(phydev, 0x10, 0x8f82); ++ ++ /* VcoSlicerThreshBitsHigh */ ++ __phy_write(phydev, 0x11, 0x5555); ++ __phy_write(phydev, 0x12, 0x55); ++ __phy_write(phydev, 0x10, 0x8ec0); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, ++ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); ++ ++ /* rg_tr_lpf_cnt_val = 512 */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); ++ ++ /* IIR2 related */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); ++ ++ /* FFE peaking */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, ++ MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, ++ MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); ++ ++ /* Disable LDO pump */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); ++ /* Adjust LDO output voltage */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); ++} ++ ++static void mt7988_phy_finetune(struct phy_device *phydev) ++{ ++ u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, ++ 0x020d, 0x0206, 0x0384, 0x03d0, ++ 0x03c6, 0x030a, 0x0011, 0x0005 }; ++ int i; ++ ++ /* Set default MLT3 shaper first */ ++ for (i = 0; i < 12; i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); ++ ++ /* TCT finetune */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* ResetSyncOffset = 5 */ ++ __phy_write(phydev, 0x11, 0x500); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8fc0); ++ ++ /* VgaDecRate is 1 at default on mt7988 */ ++ ++ /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, ++ * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 ++ */ ++ __phy_write(phydev, 0x11, 0xb90a); ++ __phy_write(phydev, 0x12, 0x6f); ++ __phy_write(phydev, 0x10, 0x8f82); ++ ++ /* RemAckCntLimitCtrl = 1 */ ++ __phy_write(phydev, 0x11, 0xfbba); ++ __phy_write(phydev, 0x12, 0xc3); ++ __phy_write(phydev, 0x10, 0x87f8); ++ ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | ++ MTK_PHY_LPF_X_AVERAGE_MASK, ++ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0xa)); ++ ++ /* rg_tr_lpf_cnt_val = 1023 */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x3ff); ++} ++ ++static void mt798x_phy_eee(struct phy_device *phydev) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120, ++ MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK | ++ MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK, 0x0) | ++ FIELD_PREP(MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, 0x14)); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, ++ MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ 0xff)); ++ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_TESTMUX_ADC_CTRL, ++ MTK_PHY_RG_TXEN_DIG_MASK); ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DEV1E_REG19b, MTK_PHY_BYPASS_DSP_LPI_READY); ++ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_LP_IIR_EEE_EN); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG238, ++ MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK | ++ MTK_PHY_LPI_SLV_SEND_TX_EN, ++ FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); ++ ++ /* Keep MTK_PHY_LPI_SEND_LOC_TIMER as 375 */ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, ++ MTK_PHY_LPI_TXPCS_LOC_RCV); ++ ++ /* This also fixes some IoT issues, such as CH340 */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, ++ MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, ++ FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | ++ FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13)); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2D1, ++ MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, ++ FIELD_PREP(MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, ++ 0x33) | ++ MTK_PHY_LPI_SKIP_SD_SLV_TR | MTK_PHY_LPI_TR_READY | ++ MTK_PHY_LPI_VCO_EEE_STG0_EN); ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG323, ++ MTK_PHY_EEE_WAKE_MAS_INT_DC | ++ MTK_PHY_EEE_WAKE_SLV_INT_DC); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG324, ++ MTK_PHY_SMI_DETCNT_MAX_MASK, ++ FIELD_PREP(MTK_PHY_SMI_DETCNT_MAX_MASK, 0x3f) | ++ MTK_PHY_SMI_DET_MAX_EN); ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG326, ++ MTK_PHY_LPI_MODE_SD_ON | MTK_PHY_RESET_RANDUPD_CNT | ++ MTK_PHY_TREC_UPDATE_ENAB_CLR | ++ MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF | ++ MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* Regsigdet_sel_1000 = 0 */ ++ __phy_write(phydev, 0x11, 0xb); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x9690); ++ ++ /* REG_EEE_st2TrKf1000 = 2 */ ++ __phy_write(phydev, 0x11, 0x114f); ++ __phy_write(phydev, 0x12, 0x2); ++ __phy_write(phydev, 0x10, 0x969a); ++ ++ /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ ++ __phy_write(phydev, 0x11, 0x3028); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x969e); ++ ++ /* RegEEE_slv_wake_int_timer_tar = 8 */ ++ __phy_write(phydev, 0x11, 0x5010); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96a0); ++ ++ /* RegEEE_trfreeze_timer2 = 586 */ ++ __phy_write(phydev, 0x11, 0x24a); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96a8); ++ ++ /* RegEEE100Stg1_tar = 16 */ ++ __phy_write(phydev, 0x11, 0x3210); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96b8); ++ ++ /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ ++ __phy_write(phydev, 0x11, 0x1463); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x96ca); ++ ++ /* DfeTailEnableVgaThresh1000 = 27 */ ++ __phy_write(phydev, 0x11, 0x36); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8f80); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); ++ __phy_modify(phydev, MTK_PHY_LPI_REG_14, ++ MTK_PHY_LPI_WAKE_TIMER_1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); ++ ++ __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, ++ FIELD_PREP(MTK_PHY_SMI_DET_ON_THRESH_MASK, 0xc)); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, ++ MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, ++ 0xff)); ++} ++ ++static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ u8 start_pair, u8 end_pair) ++{ ++ u8 pair_n; ++ int ret; ++ ++ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { ++ /* TX_OFFSET & TX_AMP have no SW calibration. */ ++ switch (cal_item) { ++ case TX_VCM: ++ ret = tx_vcm_cal_sw(phydev, pair_n); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ u8 start_pair, u8 end_pair, u32 *buf) ++{ ++ u8 pair_n; ++ int ret; ++ ++ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { ++ /* TX_VCM has no efuse calibration. */ ++ switch (cal_item) { ++ case REXT: ++ ret = rext_cal_efuse(phydev, buf); ++ break; ++ case TX_OFFSET: ++ ret = tx_offset_cal_efuse(phydev, buf); ++ break; ++ case TX_AMP: ++ ret = tx_amp_cal_efuse(phydev, buf); ++ break; ++ case TX_R50: ++ ret = tx_r50_cal_efuse(phydev, buf, pair_n); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ enum CAL_MODE cal_mode, u8 start_pair, ++ u8 end_pair, u32 *buf) ++{ ++ int ret; ++ ++ switch (cal_mode) { ++ case EFUSE_M: ++ ret = cal_efuse(phydev, cal_item, start_pair, ++ end_pair, buf); ++ break; ++ case SW_M: ++ ret = cal_sw(phydev, cal_item, start_pair, end_pair); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (ret) { ++ phydev_err(phydev, "cal %d failed\n", cal_item); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mt798x_phy_calibration(struct phy_device *phydev) ++{ ++ struct nvmem_cell *cell; ++ int ret = 0; ++ size_t len; ++ u32 *buf; ++ ++ cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); ++ if (IS_ERR(cell)) { ++ if (PTR_ERR(cell) == -EPROBE_DEFER) ++ return PTR_ERR(cell); ++ return 0; ++ } ++ ++ buf = (u32 *)nvmem_cell_read(cell, &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ nvmem_cell_put(cell); ++ ++ if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) { ++ phydev_err(phydev, "invalid efuse data\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = start_cal(phydev, REXT, EFUSE_M, NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_OFFSET, EFUSE_M, NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_AMP, EFUSE_M, NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_R50, EFUSE_M, PAIR_A, PAIR_D, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_VCM, SW_M, PAIR_A, PAIR_A, buf); ++ if (ret) ++ goto out; ++ ++out: ++ kfree(buf); ++ return ret; ++} ++ ++static int mt798x_phy_config_init(struct phy_device *phydev) ++{ ++ switch (phydev->drv->phy_id) { ++ case MTK_GPHY_ID_MT7981: ++ mt7981_phy_finetune(phydev); ++ break; ++ case MTK_GPHY_ID_MT7988: ++ mt7988_phy_finetune(phydev); ++ break; ++ } ++ ++ mt798x_phy_common_finetune(phydev); ++ mt798x_phy_eee(phydev); ++ ++ return mt798x_phy_calibration(phydev); ++} ++ ++static int mt798x_phy_hw_led_on_set(struct phy_device *phydev, u8 index, ++ bool on) ++{ ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (on) ++ changed = !test_and_set_bit(bit_on, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_on, &priv->led_state); ++ ++ changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_MASK, ++ on ? MTK_PHY_LED_ON_FORCE_ON : 0); ++ else ++ return 0; ++} ++ ++static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, ++ bool blinking) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (blinking) ++ changed = !test_and_set_bit(bit_blink, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_blink, &priv->led_state); ++ ++ changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, ++ blinking ? ++ MTK_PHY_LED_BLINK_FORCE_BLINK : 0); ++ else ++ return 0; ++} ++ ++static int mt798x_phy_led_blink_set(struct phy_device *phydev, u8 index, ++ unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ bool blinking = false; ++ int err = 0; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { ++ blinking = true; ++ *delay_on = 50; ++ *delay_off = 50; ++ } ++ ++ err = mt798x_phy_hw_led_blink_set(phydev, index, blinking); ++ if (err) ++ return err; ++ ++ return mt798x_phy_hw_led_on_set(phydev, index, false); ++} ++ ++static int mt798x_phy_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ int err; ++ ++ err = mt798x_phy_hw_led_blink_set(phydev, index, false); ++ if (err) ++ return err; ++ ++ return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); ++} ++ ++static const unsigned long supported_triggers = ++ BIT(TRIGGER_NETDEV_FULL_DUPLEX) | ++ BIT(TRIGGER_NETDEV_HALF_DUPLEX) | ++ BIT(TRIGGER_NETDEV_LINK) | ++ BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_RX) | ++ BIT(TRIGGER_NETDEV_TX); ++ ++static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* All combinations of the supported triggers are allowed */ ++ if (rules & ~supported_triggers) ++ return -EOPNOTSUPP; ++ ++ return 0; ++}; ++ ++static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ int on, blink; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ on = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); ++ ++ if (on < 0) ++ return -EIO; ++ ++ blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL); ++ if (blink < 0) ++ return -EIO; ++ ++ if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || ++ (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ if (on & MTK_PHY_LED_ON_FORCE_ON) ++ set_bit(bit_on, &priv->led_state); ++ else ++ clear_bit(bit_on, &priv->led_state); ++ ++ if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) ++ set_bit(bit_blink, &priv->led_state); ++ else ++ clear_bit(bit_blink, &priv->led_state); ++ ++ if (!rules) ++ return 0; ++ ++ if (on & MTK_PHY_LED_ON_LINK) ++ *rules |= BIT(TRIGGER_NETDEV_LINK); ++ ++ if (on & MTK_PHY_LED_ON_LINK10) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_10); ++ ++ if (on & MTK_PHY_LED_ON_LINK100) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_100); ++ ++ if (on & MTK_PHY_LED_ON_LINK1000) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_1000); ++ ++ if (on & MTK_PHY_LED_ON_FDX) ++ *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); ++ ++ if (on & MTK_PHY_LED_ON_HDX) ++ *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); ++ ++ if (blink & MTK_PHY_LED_BLINK_RX) ++ *rules |= BIT(TRIGGER_NETDEV_RX); ++ ++ if (blink & MTK_PHY_LED_BLINK_TX) ++ *rules |= BIT(TRIGGER_NETDEV_TX); ++ ++ return 0; ++}; ++ ++static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ u16 on = 0, blink = 0; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) ++ on |= MTK_PHY_LED_ON_FDX; ++ ++ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) ++ on |= MTK_PHY_LED_ON_HDX; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK1000; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= (on & MTK_PHY_LED_ON_LINK) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000RX : 0)) : ++ MTK_PHY_LED_BLINK_RX; ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= (on & MTK_PHY_LED_ON_LINK) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000TX : 0)) : ++ MTK_PHY_LED_BLINK_TX; ++ } ++ ++ if (blink || on) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | ++ MTK_PHY_LED_ON_LINK, ++ on); ++ ++ if (ret) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, blink); ++}; ++ ++static bool mt7988_phy_led_get_polarity(struct phy_device *phydev, int led_num) ++{ ++ struct mtk_socphy_shared *priv = phydev->shared->priv; ++ u32 polarities; ++ ++ if (led_num == 0) ++ polarities = ~(priv->boottrap); ++ else ++ polarities = MTK_PHY_LED1_DEFAULT_POLARITIES; ++ ++ if (polarities & BIT(phydev->mdio.addr)) ++ return true; ++ ++ return false; ++} ++ ++static int mt7988_phy_fix_leds_polarities(struct phy_device *phydev) ++{ ++ struct pinctrl *pinctrl; ++ int index; ++ ++ /* Setup LED polarity according to bootstrap use of LED pins */ ++ for (index = 0; index < 2; ++index) ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_POLARITY, ++ mt7988_phy_led_get_polarity(phydev, index) ? ++ MTK_PHY_LED_ON_POLARITY : 0); ++ ++ /* Only now setup pinctrl to avoid bogus blinking */ ++ pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); ++ if (IS_ERR(pinctrl)) ++ dev_err(&phydev->mdio.bus->dev, ++ "Failed to setup PHY LED pinctrl\n"); ++ ++ return 0; ++} ++ ++static int mt7988_phy_probe_shared(struct phy_device *phydev) ++{ ++ struct device_node *np = dev_of_node(&phydev->mdio.bus->dev); ++ struct mtk_socphy_shared *shared = phydev->shared->priv; ++ struct regmap *regmap; ++ u32 reg; ++ int ret; ++ ++ /* The LED0 of the 4 PHYs in MT7988 are wired to SoC pins LED_A, LED_B, ++ * LED_C and LED_D respectively. At the same time those pins are used to ++ * bootstrap configuration of the reference clock source (LED_A), ++ * DRAM DDRx16b x2/x1 (LED_B) and boot device (LED_C, LED_D). ++ * In practice this is done using a LED and a resistor pulling the pin ++ * either to GND or to VIO. ++ * The detected value at boot time is accessible at run-time using the ++ * TPBANK0 register located in the gpio base of the pinctrl, in order ++ * to read it here it needs to be referenced by a phandle called ++ * 'mediatek,pio' in the MDIO bus hosting the PHY. ++ * The 4 bits in TPBANK0 are kept as package shared data and are used to ++ * set LED polarity for each of the LED0. ++ */ ++ regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,pio"); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ ret = regmap_read(regmap, RG_GPIO_MISC_TPBANK0, ®); ++ if (ret) ++ return ret; ++ ++ shared->boottrap = FIELD_GET(RG_GPIO_MISC_TPBANK0_BOOTMODE, reg); ++ ++ return 0; ++} ++ ++static void mt798x_phy_leds_state_init(struct phy_device *phydev) ++{ ++ int i; ++ ++ for (i = 0; i < 2; ++i) ++ mt798x_phy_led_hw_control_get(phydev, i, NULL); ++} ++ ++static int mt7988_phy_probe(struct phy_device *phydev) ++{ ++ struct mtk_socphy_shared *shared; ++ struct mtk_socphy_priv *priv; ++ int err; ++ ++ if (phydev->mdio.addr > 3) ++ return -EINVAL; ++ ++ err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0, ++ sizeof(struct mtk_socphy_shared)); ++ if (err) ++ return err; ++ ++ if (phy_package_probe_once(phydev)) { ++ err = mt7988_phy_probe_shared(phydev); ++ if (err) ++ return err; ++ } ++ ++ shared = phydev->shared->priv; ++ priv = &shared->priv[phydev->mdio.addr]; ++ ++ phydev->priv = priv; ++ ++ mt798x_phy_leds_state_init(phydev); ++ ++ err = mt7988_phy_fix_leds_polarities(phydev); ++ if (err) ++ return err; ++ ++ /* Disable TX power saving at probing to: ++ * 1. Meet common mode compliance test criteria ++ * 2. Make sure that TX-VCM calibration works fine ++ */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, ++ MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); ++ ++ return mt798x_phy_calibration(phydev); ++} ++ ++static int mt7981_phy_probe(struct phy_device *phydev) ++{ ++ struct mtk_socphy_priv *priv; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct mtk_socphy_priv), ++ GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ phydev->priv = priv; ++ ++ mt798x_phy_leds_state_init(phydev); ++ ++ return mt798x_phy_calibration(phydev); ++} ++ ++static struct phy_driver mtk_socphy_driver[] = { ++ { ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), ++ .name = "MediaTek MT7981 PHY", ++ .config_init = mt798x_phy_config_init, ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .probe = mt7981_phy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_socphy_read_page, ++ .write_page = mtk_socphy_write_page, ++ .led_blink_set = mt798x_phy_led_blink_set, ++ .led_brightness_set = mt798x_phy_led_brightness_set, ++ .led_hw_is_supported = mt798x_phy_led_hw_is_supported, ++ .led_hw_control_set = mt798x_phy_led_hw_control_set, ++ .led_hw_control_get = mt798x_phy_led_hw_control_get, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988), ++ .name = "MediaTek MT7988 PHY", ++ .config_init = mt798x_phy_config_init, ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .probe = mt7988_phy_probe, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_socphy_read_page, ++ .write_page = mtk_socphy_write_page, ++ .led_blink_set = mt798x_phy_led_blink_set, ++ .led_brightness_set = mt798x_phy_led_brightness_set, ++ .led_hw_is_supported = mt798x_phy_led_hw_is_supported, ++ .led_hw_control_set = mt798x_phy_led_hw_control_set, ++ .led_hw_control_get = mt798x_phy_led_hw_control_get, ++ }, ++}; ++ ++module_phy_driver(mtk_socphy_driver); ++ ++static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, ++ { } ++}; ++ ++MODULE_DESCRIPTION("MediaTek SoC Gigabit Ethernet PHY driver"); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_AUTHOR("SkyLake Huang "); ++MODULE_LICENSE("GPL"); ++ ++MODULE_DEVICE_TABLE(mdio, mtk_socphy_tbl); +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -0,0 +1,114 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++#include ++#include ++#include ++ ++#define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_STANDARD 0x0000 ++#define MTK_PHY_PAGE_EXTENDED 0x0001 ++#define MTK_PHY_PAGE_EXTENDED_2 0x0002 ++#define MTK_PHY_PAGE_EXTENDED_3 0x0003 ++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 ++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 ++ ++static int mtk_gephy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); ++} ++ ++static int mtk_gephy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); ++} ++ ++static void mtk_gephy_config_init(struct phy_device *phydev) ++{ ++ /* Disable EEE */ ++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); ++ ++ /* Enable HW auto downshift */ ++ phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); ++ ++ /* Increase SlvDPSready time */ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ __phy_write(phydev, 0x10, 0xafae); ++ __phy_write(phydev, 0x12, 0x2f); ++ __phy_write(phydev, 0x10, 0x8fae); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* Adjust 100_mse_threshold */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); ++ ++ /* Disable mcc */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); ++} ++ ++static int mt7530_phy_config_init(struct phy_device *phydev) ++{ ++ mtk_gephy_config_init(phydev); ++ ++ /* Increase post_update_timer */ ++ phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); ++ ++ return 0; ++} ++ ++static int mt7531_phy_config_init(struct phy_device *phydev) ++{ ++ mtk_gephy_config_init(phydev); ++ ++ /* PHY link down power saving enable */ ++ phy_set_bits(phydev, 0x17, BIT(4)); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); ++ ++ /* Set TX Pair delay selection */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); ++ ++ return 0; ++} ++ ++static struct phy_driver mtk_gephy_driver[] = { ++ { ++ PHY_ID_MATCH_EXACT(0x03a29412), ++ .name = "MediaTek MT7530 PHY", ++ .config_init = mt7530_phy_config_init, ++ /* Interrupts are handled by the switch, not the PHY ++ * itself. ++ */ ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(0x03a29441), ++ .name = "MediaTek MT7531 PHY", ++ .config_init = mt7531_phy_config_init, ++ /* Interrupts are handled by the switch, not the PHY ++ * itself. ++ */ ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++}; ++ ++module_phy_driver(mtk_gephy_driver); ++ ++static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { ++ { PHY_ID_MATCH_EXACT(0x03a29441) }, ++ { PHY_ID_MATCH_EXACT(0x03a29412) }, ++ { } ++}; ++ ++MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); ++MODULE_AUTHOR("DENG, Qingfang "); ++MODULE_LICENSE("GPL"); ++ ++MODULE_DEVICE_TABLE(mdio, mtk_gephy_tbl); diff --git a/target/linux/generic/backport-6.6/743-v6.13-net-phy-mediatek-Move-LED-helper-functions-into-mtk-.patch b/target/linux/generic/backport-6.6/743-v6.13-net-phy-mediatek-Move-LED-helper-functions-into-mtk-.patch new file mode 100644 index 00000000000000..583836facd0e1c --- /dev/null +++ b/target/linux/generic/backport-6.6/743-v6.13-net-phy-mediatek-Move-LED-helper-functions-into-mtk-.patch @@ -0,0 +1,762 @@ +From 7f9c320c98dbca18934afa80306015eb71c05a6c Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:52 +0800 +Subject: [PATCH] net: phy: mediatek: Move LED helper functions into mtk phy + lib + +This patch creates mtk-phy-lib.c & mtk-phy.h and integrates mtk-ge-soc.c's +LED helper functions so that we can use those helper functions in other +MTK's ethernet phy driver. + +Reviewed-by: Andrew Lunn +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + MAINTAINERS | 2 + + drivers/net/phy/mediatek/Kconfig | 4 + + drivers/net/phy/mediatek/Makefile | 1 + + drivers/net/phy/mediatek/mtk-ge-soc.c | 280 +++---------------------- + drivers/net/phy/mediatek/mtk-phy-lib.c | 254 ++++++++++++++++++++++ + drivers/net/phy/mediatek/mtk.h | 86 ++++++++ + 6 files changed, 372 insertions(+), 255 deletions(-) + create mode 100644 drivers/net/phy/mediatek/mtk-phy-lib.c + create mode 100644 drivers/net/phy/mediatek/mtk.h + +--- a/drivers/net/phy/mediatek/Kconfig ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -1,4 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only ++config MTK_NET_PHYLIB ++ tristate ++ + config MEDIATEK_GE_PHY + tristate "MediaTek Gigabit Ethernet PHYs" + help +@@ -13,6 +16,7 @@ config MEDIATEK_GE_SOC_PHY + tristate "MediaTek SoC Ethernet PHYs" + depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST + depends on NVMEM_MTK_EFUSE ++ select MTK_NET_PHYLIB + help + Supports MediaTek SoC built-in Gigabit Ethernet PHYs. + +--- a/drivers/net/phy/mediatek/Makefile ++++ b/drivers/net/phy/mediatek/Makefile +@@ -1,3 +1,4 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_MTK_NET_PHYLIB) += mtk-phy-lib.o + obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o + obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mtk-ge-soc.o +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -8,6 +8,8 @@ + #include + #include + ++#include "mtk.h" ++ + #define MTK_GPHY_ID_MT7981 0x03a29461 + #define MTK_GPHY_ID_MT7988 0x03a29481 + +@@ -210,41 +212,6 @@ + #define MTK_PHY_DA_TX_R50_PAIR_D 0x540 + + /* Registers on MDIO_MMD_VEND2 */ +-#define MTK_PHY_LED0_ON_CTRL 0x24 +-#define MTK_PHY_LED1_ON_CTRL 0x26 +-#define MTK_PHY_LED_ON_MASK GENMASK(6, 0) +-#define MTK_PHY_LED_ON_LINK1000 BIT(0) +-#define MTK_PHY_LED_ON_LINK100 BIT(1) +-#define MTK_PHY_LED_ON_LINK10 BIT(2) +-#define MTK_PHY_LED_ON_LINK (MTK_PHY_LED_ON_LINK10 |\ +- MTK_PHY_LED_ON_LINK100 |\ +- MTK_PHY_LED_ON_LINK1000) +-#define MTK_PHY_LED_ON_LINKDOWN BIT(3) +-#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ +-#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ +-#define MTK_PHY_LED_ON_FORCE_ON BIT(6) +-#define MTK_PHY_LED_ON_POLARITY BIT(14) +-#define MTK_PHY_LED_ON_ENABLE BIT(15) +- +-#define MTK_PHY_LED0_BLINK_CTRL 0x25 +-#define MTK_PHY_LED1_BLINK_CTRL 0x27 +-#define MTK_PHY_LED_BLINK_1000TX BIT(0) +-#define MTK_PHY_LED_BLINK_1000RX BIT(1) +-#define MTK_PHY_LED_BLINK_100TX BIT(2) +-#define MTK_PHY_LED_BLINK_100RX BIT(3) +-#define MTK_PHY_LED_BLINK_10TX BIT(4) +-#define MTK_PHY_LED_BLINK_10RX BIT(5) +-#define MTK_PHY_LED_BLINK_RX (MTK_PHY_LED_BLINK_10RX |\ +- MTK_PHY_LED_BLINK_100RX |\ +- MTK_PHY_LED_BLINK_1000RX) +-#define MTK_PHY_LED_BLINK_TX (MTK_PHY_LED_BLINK_10TX |\ +- MTK_PHY_LED_BLINK_100TX |\ +- MTK_PHY_LED_BLINK_1000TX) +-#define MTK_PHY_LED_BLINK_COLLISION BIT(6) +-#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) +-#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) +-#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) +- + #define MTK_PHY_LED1_DEFAULT_POLARITIES BIT(1) + + #define MTK_PHY_RG_BG_RASEL 0x115 +@@ -299,14 +266,6 @@ enum CAL_MODE { + SW_M + }; + +-#define MTK_PHY_LED_STATE_FORCE_ON 0 +-#define MTK_PHY_LED_STATE_FORCE_BLINK 1 +-#define MTK_PHY_LED_STATE_NETDEV 2 +- +-struct mtk_socphy_priv { +- unsigned long led_state; +-}; +- + struct mtk_socphy_shared { + u32 boottrap; + struct mtk_socphy_priv priv[4]; +@@ -1172,76 +1131,23 @@ static int mt798x_phy_config_init(struct + return mt798x_phy_calibration(phydev); + } + +-static int mt798x_phy_hw_led_on_set(struct phy_device *phydev, u8 index, +- bool on) +-{ +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (on) +- changed = !test_and_set_bit(bit_on, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_on, &priv->led_state); +- +- changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_MASK, +- on ? MTK_PHY_LED_ON_FORCE_ON : 0); +- else +- return 0; +-} +- +-static int mt798x_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, +- bool blinking) +-{ +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- bool changed; +- +- if (blinking) +- changed = !test_and_set_bit(bit_blink, &priv->led_state); +- else +- changed = !!test_and_clear_bit(bit_blink, &priv->led_state); +- +- changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + +- (index ? 16 : 0), &priv->led_state); +- if (changed) +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, +- blinking ? +- MTK_PHY_LED_BLINK_FORCE_BLINK : 0); +- else +- return 0; +-} +- + static int mt798x_phy_led_blink_set(struct phy_device *phydev, u8 index, + unsigned long *delay_on, + unsigned long *delay_off) + { + bool blinking = false; +- int err = 0; +- +- if (index > 1) +- return -EINVAL; ++ int err; + +- if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { +- blinking = true; +- *delay_on = 50; +- *delay_off = 50; +- } ++ err = mtk_phy_led_num_dly_cfg(index, delay_on, delay_off, &blinking); ++ if (err < 0) ++ return err; + +- err = mt798x_phy_hw_led_blink_set(phydev, index, blinking); ++ err = mtk_phy_hw_led_blink_set(phydev, index, blinking); + if (err) + return err; + +- return mt798x_phy_hw_led_on_set(phydev, index, false); ++ return mtk_phy_hw_led_on_set(phydev, index, MTK_GPHY_LED_ON_MASK, ++ false); + } + + static int mt798x_phy_led_brightness_set(struct phy_device *phydev, +@@ -1249,11 +1155,12 @@ static int mt798x_phy_led_brightness_set + { + int err; + +- err = mt798x_phy_hw_led_blink_set(phydev, index, false); ++ err = mtk_phy_hw_led_blink_set(phydev, index, false); + if (err) + return err; + +- return mt798x_phy_hw_led_on_set(phydev, index, (value != LED_OFF)); ++ return mtk_phy_hw_led_on_set(phydev, index, MTK_GPHY_LED_ON_MASK, ++ (value != LED_OFF)); + } + + static const unsigned long supported_triggers = +@@ -1269,155 +1176,26 @@ static const unsigned long supported_tri + static int mt798x_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) + { +- if (index > 1) +- return -EINVAL; +- +- /* All combinations of the supported triggers are allowed */ +- if (rules & ~supported_triggers) +- return -EOPNOTSUPP; +- +- return 0; +-}; ++ return mtk_phy_led_hw_is_supported(phydev, index, rules, ++ supported_triggers); ++} + + static int mt798x_phy_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) + { +- unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + +- (index ? 16 : 0); +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- int on, blink; +- +- if (index > 1) +- return -EINVAL; +- +- on = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); +- +- if (on < 0) +- return -EIO; +- +- blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, +- index ? MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL); +- if (blink < 0) +- return -EIO; +- +- if ((on & (MTK_PHY_LED_ON_LINK | MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || +- (blink & (MTK_PHY_LED_BLINK_RX | MTK_PHY_LED_BLINK_TX))) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- if (on & MTK_PHY_LED_ON_FORCE_ON) +- set_bit(bit_on, &priv->led_state); +- else +- clear_bit(bit_on, &priv->led_state); +- +- if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) +- set_bit(bit_blink, &priv->led_state); +- else +- clear_bit(bit_blink, &priv->led_state); +- +- if (!rules) +- return 0; +- +- if (on & MTK_PHY_LED_ON_LINK) +- *rules |= BIT(TRIGGER_NETDEV_LINK); +- +- if (on & MTK_PHY_LED_ON_LINK10) +- *rules |= BIT(TRIGGER_NETDEV_LINK_10); +- +- if (on & MTK_PHY_LED_ON_LINK100) +- *rules |= BIT(TRIGGER_NETDEV_LINK_100); +- +- if (on & MTK_PHY_LED_ON_LINK1000) +- *rules |= BIT(TRIGGER_NETDEV_LINK_1000); +- +- if (on & MTK_PHY_LED_ON_FDX) +- *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); +- +- if (on & MTK_PHY_LED_ON_HDX) +- *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); +- +- if (blink & MTK_PHY_LED_BLINK_RX) +- *rules |= BIT(TRIGGER_NETDEV_RX); +- +- if (blink & MTK_PHY_LED_BLINK_TX) +- *rules |= BIT(TRIGGER_NETDEV_TX); +- +- return 0; ++ return mtk_phy_led_hw_ctrl_get(phydev, index, rules, ++ MTK_GPHY_LED_ON_SET, ++ MTK_GPHY_LED_RX_BLINK_SET, ++ MTK_GPHY_LED_TX_BLINK_SET); + }; + + static int mt798x_phy_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) + { +- unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); +- struct mtk_socphy_priv *priv = phydev->priv; +- u16 on = 0, blink = 0; +- int ret; +- +- if (index > 1) +- return -EINVAL; +- +- if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) +- on |= MTK_PHY_LED_ON_FDX; +- +- if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) +- on |= MTK_PHY_LED_ON_HDX; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK10; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK100; +- +- if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) +- on |= MTK_PHY_LED_ON_LINK1000; +- +- if (rules & BIT(TRIGGER_NETDEV_RX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100RX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000RX : 0)) : +- MTK_PHY_LED_BLINK_RX; +- } +- +- if (rules & BIT(TRIGGER_NETDEV_TX)) { +- blink |= (on & MTK_PHY_LED_ON_LINK) ? +- (((on & MTK_PHY_LED_ON_LINK10) ? +- MTK_PHY_LED_BLINK_10TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK100) ? +- MTK_PHY_LED_BLINK_100TX : 0) | +- ((on & MTK_PHY_LED_ON_LINK1000) ? +- MTK_PHY_LED_BLINK_1000TX : 0)) : +- MTK_PHY_LED_BLINK_TX; +- } +- +- if (blink || on) +- set_bit(bit_netdev, &priv->led_state); +- else +- clear_bit(bit_netdev, &priv->led_state); +- +- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_ON_CTRL : +- MTK_PHY_LED0_ON_CTRL, +- MTK_PHY_LED_ON_FDX | +- MTK_PHY_LED_ON_HDX | +- MTK_PHY_LED_ON_LINK, +- on); +- +- if (ret) +- return ret; +- +- return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? +- MTK_PHY_LED1_BLINK_CTRL : +- MTK_PHY_LED0_BLINK_CTRL, blink); ++ return mtk_phy_led_hw_ctrl_set(phydev, index, rules, ++ MTK_GPHY_LED_ON_SET, ++ MTK_GPHY_LED_RX_BLINK_SET, ++ MTK_GPHY_LED_TX_BLINK_SET); + }; + + static bool mt7988_phy_led_get_polarity(struct phy_device *phydev, int led_num) +@@ -1492,14 +1270,6 @@ static int mt7988_phy_probe_shared(struc + return 0; + } + +-static void mt798x_phy_leds_state_init(struct phy_device *phydev) +-{ +- int i; +- +- for (i = 0; i < 2; ++i) +- mt798x_phy_led_hw_control_get(phydev, i, NULL); +-} +- + static int mt7988_phy_probe(struct phy_device *phydev) + { + struct mtk_socphy_shared *shared; +@@ -1525,7 +1295,7 @@ static int mt7988_phy_probe(struct phy_d + + phydev->priv = priv; + +- mt798x_phy_leds_state_init(phydev); ++ mtk_phy_leds_state_init(phydev); + + err = mt7988_phy_fix_leds_polarities(phydev); + if (err) +@@ -1552,7 +1322,7 @@ static int mt7981_phy_probe(struct phy_d + + phydev->priv = priv; + +- mt798x_phy_leds_state_init(phydev); ++ mtk_phy_leds_state_init(phydev); + + return mt798x_phy_calibration(phydev); + } +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -0,0 +1,254 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include ++#include ++ ++#include ++ ++#include "mtk.h" ++ ++int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules, ++ unsigned long supported_triggers) ++{ ++ if (index > 1) ++ return -EINVAL; ++ ++ /* All combinations of the supported triggers are allowed */ ++ if (rules & ~supported_triggers) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_hw_is_supported); ++ ++int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ int on, blink; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ on = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL); ++ ++ if (on < 0) ++ return -EIO; ++ ++ blink = phy_read_mmd(phydev, MDIO_MMD_VEND2, ++ index ? MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL); ++ if (blink < 0) ++ return -EIO; ++ ++ if ((on & (on_set | MTK_PHY_LED_ON_FDX | ++ MTK_PHY_LED_ON_HDX | MTK_PHY_LED_ON_LINKDOWN)) || ++ (blink & (rx_blink_set | tx_blink_set))) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ if (on & MTK_PHY_LED_ON_FORCE_ON) ++ set_bit(bit_on, &priv->led_state); ++ else ++ clear_bit(bit_on, &priv->led_state); ++ ++ if (blink & MTK_PHY_LED_BLINK_FORCE_BLINK) ++ set_bit(bit_blink, &priv->led_state); ++ else ++ clear_bit(bit_blink, &priv->led_state); ++ ++ if (!rules) ++ return 0; ++ ++ if (on & on_set) ++ *rules |= BIT(TRIGGER_NETDEV_LINK); ++ ++ if (on & MTK_PHY_LED_ON_LINK10) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_10); ++ ++ if (on & MTK_PHY_LED_ON_LINK100) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_100); ++ ++ if (on & MTK_PHY_LED_ON_LINK1000) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_1000); ++ ++ if (on & MTK_PHY_LED_ON_LINK2500) ++ *rules |= BIT(TRIGGER_NETDEV_LINK_2500); ++ ++ if (on & MTK_PHY_LED_ON_FDX) ++ *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX); ++ ++ if (on & MTK_PHY_LED_ON_HDX) ++ *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX); ++ ++ if (blink & rx_blink_set) ++ *rules |= BIT(TRIGGER_NETDEV_RX); ++ ++ if (blink & tx_blink_set) ++ *rules |= BIT(TRIGGER_NETDEV_TX); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_get); ++ ++int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index, ++ unsigned long rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set) ++{ ++ unsigned int bit_netdev = MTK_PHY_LED_STATE_NETDEV + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ u16 on = 0, blink = 0; ++ int ret; ++ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) ++ on |= MTK_PHY_LED_ON_FDX; ++ ++ if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) ++ on |= MTK_PHY_LED_ON_HDX; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK10; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK100; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK1000; ++ ++ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) ++ on |= MTK_PHY_LED_ON_LINK2500; ++ ++ if (rules & BIT(TRIGGER_NETDEV_RX)) { ++ blink |= (on & on_set) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000RX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK2500) ? ++ MTK_PHY_LED_BLINK_2500RX : 0)) : ++ rx_blink_set; ++ } ++ ++ if (rules & BIT(TRIGGER_NETDEV_TX)) { ++ blink |= (on & on_set) ? ++ (((on & MTK_PHY_LED_ON_LINK10) ? ++ MTK_PHY_LED_BLINK_10TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK100) ? ++ MTK_PHY_LED_BLINK_100TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK1000) ? ++ MTK_PHY_LED_BLINK_1000TX : 0) | ++ ((on & MTK_PHY_LED_ON_LINK2500) ? ++ MTK_PHY_LED_BLINK_2500TX : 0)) : ++ tx_blink_set; ++ } ++ ++ if (blink || on) ++ set_bit(bit_netdev, &priv->led_state); ++ else ++ clear_bit(bit_netdev, &priv->led_state); ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED_ON_FDX | MTK_PHY_LED_ON_HDX | on_set, ++ on); ++ ++ if (ret) ++ return ret; ++ ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, blink); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_hw_ctrl_set); ++ ++int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on, ++ unsigned long *delay_off, bool *blinking) ++{ ++ if (index > 1) ++ return -EINVAL; ++ ++ if (delay_on && delay_off && (*delay_on > 0) && (*delay_off > 0)) { ++ *blinking = true; ++ *delay_on = 50; ++ *delay_off = 50; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_led_num_dly_cfg); ++ ++int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index, ++ u16 led_on_mask, bool on) ++{ ++ unsigned int bit_on = MTK_PHY_LED_STATE_FORCE_ON + (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (on) ++ changed = !test_and_set_bit(bit_on, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_on, &priv->led_state); ++ ++ changed |= !!test_and_clear_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_ON_CTRL : ++ MTK_PHY_LED0_ON_CTRL, ++ led_on_mask, ++ on ? MTK_PHY_LED_ON_FORCE_ON : 0); ++ else ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_hw_led_on_set); ++ ++int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, bool blinking) ++{ ++ unsigned int bit_blink = MTK_PHY_LED_STATE_FORCE_BLINK + ++ (index ? 16 : 0); ++ struct mtk_socphy_priv *priv = phydev->priv; ++ bool changed; ++ ++ if (blinking) ++ changed = !test_and_set_bit(bit_blink, &priv->led_state); ++ else ++ changed = !!test_and_clear_bit(bit_blink, &priv->led_state); ++ ++ changed |= !!test_bit(MTK_PHY_LED_STATE_NETDEV + ++ (index ? 16 : 0), &priv->led_state); ++ if (changed) ++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, index ? ++ MTK_PHY_LED1_BLINK_CTRL : ++ MTK_PHY_LED0_BLINK_CTRL, ++ blinking ? ++ MTK_PHY_LED_BLINK_FORCE_BLINK : 0); ++ else ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mtk_phy_hw_led_blink_set); ++ ++void mtk_phy_leds_state_init(struct phy_device *phydev) ++{ ++ int i; ++ ++ for (i = 0; i < 2; ++i) ++ phydev->drv->led_hw_control_get(phydev, i, NULL); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_leds_state_init); ++ ++MODULE_DESCRIPTION("MediaTek Ethernet PHY driver common"); ++MODULE_AUTHOR("Sky Huang "); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -0,0 +1,86 @@ ++/* SPDX-License-Identifier: GPL-2.0 ++ * ++ * Common definition for Mediatek Ethernet PHYs ++ * Author: SkyLake Huang ++ * Copyright (c) 2024 MediaTek Inc. ++ */ ++ ++#ifndef _MTK_EPHY_H_ ++#define _MTK_EPHY_H_ ++ ++#define MTK_EXT_PAGE_ACCESS 0x1f ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define MTK_PHY_LED0_ON_CTRL 0x24 ++#define MTK_PHY_LED1_ON_CTRL 0x26 ++#define MTK_GPHY_LED_ON_MASK GENMASK(6, 0) ++#define MTK_2P5GPHY_LED_ON_MASK GENMASK(7, 0) ++#define MTK_PHY_LED_ON_LINK1000 BIT(0) ++#define MTK_PHY_LED_ON_LINK100 BIT(1) ++#define MTK_PHY_LED_ON_LINK10 BIT(2) ++#define MTK_PHY_LED_ON_LINKDOWN BIT(3) ++#define MTK_PHY_LED_ON_FDX BIT(4) /* Full duplex */ ++#define MTK_PHY_LED_ON_HDX BIT(5) /* Half duplex */ ++#define MTK_PHY_LED_ON_FORCE_ON BIT(6) ++#define MTK_PHY_LED_ON_LINK2500 BIT(7) ++#define MTK_PHY_LED_ON_POLARITY BIT(14) ++#define MTK_PHY_LED_ON_ENABLE BIT(15) ++ ++#define MTK_PHY_LED0_BLINK_CTRL 0x25 ++#define MTK_PHY_LED1_BLINK_CTRL 0x27 ++#define MTK_PHY_LED_BLINK_1000TX BIT(0) ++#define MTK_PHY_LED_BLINK_1000RX BIT(1) ++#define MTK_PHY_LED_BLINK_100TX BIT(2) ++#define MTK_PHY_LED_BLINK_100RX BIT(3) ++#define MTK_PHY_LED_BLINK_10TX BIT(4) ++#define MTK_PHY_LED_BLINK_10RX BIT(5) ++#define MTK_PHY_LED_BLINK_COLLISION BIT(6) ++#define MTK_PHY_LED_BLINK_RX_CRC_ERR BIT(7) ++#define MTK_PHY_LED_BLINK_RX_IDLE_ERR BIT(8) ++#define MTK_PHY_LED_BLINK_FORCE_BLINK BIT(9) ++#define MTK_PHY_LED_BLINK_2500TX BIT(10) ++#define MTK_PHY_LED_BLINK_2500RX BIT(11) ++ ++#define MTK_GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK1000 | \ ++ MTK_PHY_LED_ON_LINK100 | \ ++ MTK_PHY_LED_ON_LINK10) ++#define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ ++ MTK_PHY_LED_BLINK_100RX | \ ++ MTK_PHY_LED_BLINK_10RX) ++#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ ++ MTK_PHY_LED_BLINK_100RX | \ ++ MTK_PHY_LED_BLINK_10RX) ++ ++#define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \ ++ MTK_GPHY_LED_ON_SET) ++#define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ ++ MTK_GPHY_LED_RX_BLINK_SET) ++#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ ++ MTK_GPHY_LED_TX_BLINK_SET) ++ ++#define MTK_PHY_LED_STATE_FORCE_ON 0 ++#define MTK_PHY_LED_STATE_FORCE_BLINK 1 ++#define MTK_PHY_LED_STATE_NETDEV 2 ++ ++struct mtk_socphy_priv { ++ unsigned long led_state; ++}; ++ ++int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules, ++ unsigned long supported_triggers); ++int mtk_phy_led_hw_ctrl_set(struct phy_device *phydev, u8 index, ++ unsigned long rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set); ++int mtk_phy_led_hw_ctrl_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules, u16 on_set, ++ u16 rx_blink_set, u16 tx_blink_set); ++int mtk_phy_led_num_dly_cfg(u8 index, unsigned long *delay_on, ++ unsigned long *delay_off, bool *blinking); ++int mtk_phy_hw_led_on_set(struct phy_device *phydev, u8 index, ++ u16 led_on_mask, bool on); ++int mtk_phy_hw_led_blink_set(struct phy_device *phydev, u8 index, ++ bool blinking); ++void mtk_phy_leds_state_init(struct phy_device *phydev); ++ ++#endif /* _MTK_EPHY_H_ */ diff --git a/target/linux/generic/backport-6.6/744-v6.13-net-phy-mediatek-Integrate-read-write-page-helper-fu.patch b/target/linux/generic/backport-6.6/744-v6.13-net-phy-mediatek-Integrate-read-write-page-helper-fu.patch new file mode 100644 index 00000000000000..f04f8044e469e4 --- /dev/null +++ b/target/linux/generic/backport-6.6/744-v6.13-net-phy-mediatek-Integrate-read-write-page-helper-fu.patch @@ -0,0 +1,153 @@ +From 3cb1a3c9cbaaadaf91437374b96ec72256c40db2 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:54 +0800 +Subject: [PATCH] net: phy: mediatek: Integrate read/write page helper + functions + +This patch integrates read/write page helper functions as MTK phy lib. +They are basically the same in mtk-ge.c & mtk-ge-soc.c. + +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + drivers/net/phy/mediatek/Kconfig | 1 + + drivers/net/phy/mediatek/mtk-ge-soc.c | 18 ++++-------------- + drivers/net/phy/mediatek/mtk-ge.c | 20 ++++++-------------- + drivers/net/phy/mediatek/mtk-phy-lib.c | 12 ++++++++++++ + drivers/net/phy/mediatek/mtk.h | 3 +++ + 5 files changed, 26 insertions(+), 28 deletions(-) + +--- a/drivers/net/phy/mediatek/Kconfig ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -4,6 +4,7 @@ config MTK_NET_PHYLIB + + config MEDIATEK_GE_PHY + tristate "MediaTek Gigabit Ethernet PHYs" ++ select MTK_NET_PHYLIB + help + Supports the MediaTek non-built-in Gigabit Ethernet PHYs. + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -271,16 +271,6 @@ struct mtk_socphy_shared { + struct mtk_socphy_priv priv[4]; + }; + +-static int mtk_socphy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_socphy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- + /* One calibration cycle consists of: + * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high + * until AD_CAL_COMP is ready to output calibration result. +@@ -1337,8 +1327,8 @@ static struct phy_driver mtk_socphy_driv + .probe = mt7981_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + .led_blink_set = mt798x_phy_led_blink_set, + .led_brightness_set = mt798x_phy_led_brightness_set, + .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +@@ -1354,8 +1344,8 @@ static struct phy_driver mtk_socphy_driv + .probe = mt7988_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_socphy_read_page, +- .write_page = mtk_socphy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + .led_blink_set = mt798x_phy_led_blink_set, + .led_brightness_set = mt798x_phy_led_brightness_set, + .led_hw_is_supported = mt798x_phy_led_hw_is_supported, +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -3,6 +3,8 @@ + #include + #include + ++#include "mtk.h" ++ + #define MTK_EXT_PAGE_ACCESS 0x1f + #define MTK_PHY_PAGE_STANDARD 0x0000 + #define MTK_PHY_PAGE_EXTENDED 0x0001 +@@ -11,16 +13,6 @@ + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 + #define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + +-static int mtk_gephy_read_page(struct phy_device *phydev) +-{ +- return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +-} +- +-static int mtk_gephy_write_page(struct phy_device *phydev, int page) +-{ +- return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +-} +- + static void mtk_gephy_config_init(struct phy_device *phydev) + { + /* Disable EEE */ +@@ -80,8 +72,8 @@ static struct phy_driver mtk_gephy_drive + .handle_interrupt = genphy_handle_interrupt_no_ack, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + }, + { + PHY_ID_MATCH_EXACT(0x03a29441), +@@ -94,8 +86,8 @@ static struct phy_driver mtk_gephy_drive + .handle_interrupt = genphy_handle_interrupt_no_ack, + .suspend = genphy_suspend, + .resume = genphy_resume, +- .read_page = mtk_gephy_read_page, +- .write_page = mtk_gephy_write_page, ++ .read_page = mtk_phy_read_page, ++ .write_page = mtk_phy_write_page, + }, + }; + +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -6,6 +6,18 @@ + + #include "mtk.h" + ++int mtk_phy_read_page(struct phy_device *phydev) ++{ ++ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_read_page); ++ ++int mtk_phy_write_page(struct phy_device *phydev, int page) ++{ ++ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); ++} ++EXPORT_SYMBOL_GPL(mtk_phy_write_page); ++ + int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules, + unsigned long supported_triggers) +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -66,6 +66,9 @@ struct mtk_socphy_priv { + unsigned long led_state; + }; + ++int mtk_phy_read_page(struct phy_device *phydev); ++int mtk_phy_write_page(struct phy_device *phydev, int page); ++ + int mtk_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules, + unsigned long supported_triggers); diff --git a/target/linux/generic/backport-6.6/745-v6.13-net-phy-mediatek-add-MT7530-MT7531-s-PHY-ID-macros.patch b/target/linux/generic/backport-6.6/745-v6.13-net-phy-mediatek-add-MT7530-MT7531-s-PHY-ID-macros.patch new file mode 100644 index 00000000000000..a07b3a1a586cae --- /dev/null +++ b/target/linux/generic/backport-6.6/745-v6.13-net-phy-mediatek-add-MT7530-MT7531-s-PHY-ID-macros.patch @@ -0,0 +1,56 @@ +From 219cecbb3e864685a1f08bcb79ce07d9bc4f506e Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Sat, 9 Nov 2024 00:34:55 +0800 +Subject: [PATCH] net: phy: mediatek: add MT7530 & MT7531's PHY ID macros + +This patch adds MT7530 & MT7531's PHY ID macros in mtk-ge.c so that +it follows the same rule of mtk-ge-soc.c. + +Reviewed-by: Andrew Lunn +Signed-off-by: SkyLake.Huang +Signed-off-by: David S. Miller +--- + drivers/net/phy/mediatek/mtk-ge.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -5,6 +5,9 @@ + + #include "mtk.h" + ++#define MTK_GPHY_ID_MT7530 0x03a29412 ++#define MTK_GPHY_ID_MT7531 0x03a29441 ++ + #define MTK_EXT_PAGE_ACCESS 0x1f + #define MTK_PHY_PAGE_STANDARD 0x0000 + #define MTK_PHY_PAGE_EXTENDED 0x0001 +@@ -62,7 +65,7 @@ static int mt7531_phy_config_init(struct + + static struct phy_driver mtk_gephy_driver[] = { + { +- PHY_ID_MATCH_EXACT(0x03a29412), ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530), + .name = "MediaTek MT7530 PHY", + .config_init = mt7530_phy_config_init, + /* Interrupts are handled by the switch, not the PHY +@@ -76,7 +79,7 @@ static struct phy_driver mtk_gephy_drive + .write_page = mtk_phy_write_page, + }, + { +- PHY_ID_MATCH_EXACT(0x03a29441), ++ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531), + .name = "MediaTek MT7531 PHY", + .config_init = mt7531_phy_config_init, + /* Interrupts are handled by the switch, not the PHY +@@ -94,8 +97,8 @@ static struct phy_driver mtk_gephy_drive + module_phy_driver(mtk_gephy_driver); + + static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { +- { PHY_ID_MATCH_EXACT(0x03a29441) }, +- { PHY_ID_MATCH_EXACT(0x03a29412) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7530) }, ++ { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7531) }, + { } + }; + diff --git a/target/linux/generic/backport-6.6/746-v6.15-01-net-phy-mediatek-Change-to-more-meaningful-macros.patch b/target/linux/generic/backport-6.6/746-v6.15-01-net-phy-mediatek-Change-to-more-meaningful-macros.patch new file mode 100644 index 00000000000000..90b5ff9cd69fb4 --- /dev/null +++ b/target/linux/generic/backport-6.6/746-v6.15-01-net-phy-mediatek-Change-to-more-meaningful-macros.patch @@ -0,0 +1,149 @@ +From 2f435137a0484f11b47554281091ef4908f8cb31 Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:49 +0800 +Subject: [PATCH 1/5] net: phy: mediatek: Change to more meaningful macros + +Replace magic number with more meaningful macros in mtk-ge.c. +Also, move some common macros into mtk-phy-lib.c. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-2-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 1 - + drivers/net/phy/mediatek/mtk-ge.c | 71 +++++++++++++++++++++------ + drivers/net/phy/mediatek/mtk.h | 2 + + 3 files changed, 57 insertions(+), 17 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -24,7 +24,6 @@ + #define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) + + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + + #define ANALOG_INTERNAL_OPERATION_MAX_US 20 + #define TXRESERVE_MIN 0 +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -8,13 +8,31 @@ + #define MTK_GPHY_ID_MT7530 0x03a29412 + #define MTK_GPHY_ID_MT7531 0x03a29441 + +-#define MTK_EXT_PAGE_ACCESS 0x1f +-#define MTK_PHY_PAGE_STANDARD 0x0000 +-#define MTK_PHY_PAGE_EXTENDED 0x0001 +-#define MTK_PHY_PAGE_EXTENDED_2 0x0002 +-#define MTK_PHY_PAGE_EXTENDED_3 0x0003 +-#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +-#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 ++#define MTK_PHY_PAGE_EXTENDED_1 0x0001 ++#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 ++#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) ++ ++#define MTK_PHY_PAGE_EXTENDED_2 0x0002 ++#define MTK_PHY_PAGE_EXTENDED_3 0x0003 ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11 0x11 ++ ++#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 ++ ++/* Registers on MDIO_MMD_VEND1 */ ++#define MTK_PHY_GBE_MODE_TX_DELAY_SEL 0x13 ++#define MTK_PHY_TEST_MODE_TX_DELAY_SEL 0x14 ++#define MTK_TX_DELAY_PAIR_B_MASK GENMASK(10, 8) ++#define MTK_TX_DELAY_PAIR_D_MASK GENMASK(2, 0) ++ ++#define MTK_PHY_MCC_CTRL_AND_TX_POWER_CTRL 0xa6 ++#define MTK_MCC_NEARECHO_OFFSET_MASK GENMASK(15, 8) ++ ++#define MTK_PHY_RXADC_CTRL_RG7 0xc6 ++#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) ++ ++#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG123 0x123 ++#define MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK GENMASK(15, 8) ++#define MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK GENMASK(7, 0) + + static void mtk_gephy_config_init(struct phy_device *phydev) + { +@@ -22,7 +40,9 @@ static void mtk_gephy_config_init(struct + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0); + + /* Enable HW auto downshift */ +- phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED, 0x14, 0, BIT(4)); ++ phy_modify_paged(phydev, MTK_PHY_PAGE_EXTENDED_1, ++ MTK_PHY_AUX_CTRL_AND_STATUS, ++ 0, MTK_PHY_ENABLE_DOWNSHIFT); + + /* Increase SlvDPSready time */ + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +@@ -32,10 +52,20 @@ static void mtk_gephy_config_init(struct + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* Adjust 100_mse_threshold */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x123, 0xffff); +- +- /* Disable mcc */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0xa6, 0x300); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG123, ++ MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK | ++ MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK, ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_LO_THRESH100_MASK, ++ 0xff) | ++ FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH100_MASK, ++ 0xff)); ++ ++ /* If echo time is narrower than 0x3, it will be regarded as noise */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_MCC_CTRL_AND_TX_POWER_CTRL, ++ MTK_MCC_NEARECHO_OFFSET_MASK, ++ FIELD_PREP(MTK_MCC_NEARECHO_OFFSET_MASK, 0x3)); + } + + static int mt7530_phy_config_init(struct phy_device *phydev) +@@ -43,7 +73,8 @@ static int mt7530_phy_config_init(struct + mtk_gephy_config_init(phydev); + + /* Increase post_update_timer */ +- phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, 0x11, 0x4b); ++ phy_write_paged(phydev, MTK_PHY_PAGE_EXTENDED_3, ++ MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11, 0x4b); + + return 0; + } +@@ -54,11 +85,19 @@ static int mt7531_phy_config_init(struct + + /* PHY link down power saving enable */ + phy_set_bits(phydev, 0x17, BIT(4)); +- phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, 0xc6, 0x300); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, ++ MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, ++ FIELD_PREP(MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3)); + + /* Set TX Pair delay selection */ +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); +- phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_GBE_MODE_TX_DELAY_SEL, ++ MTK_TX_DELAY_PAIR_B_MASK | MTK_TX_DELAY_PAIR_D_MASK, ++ FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) | ++ FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4)); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TEST_MODE_TX_DELAY_SEL, ++ MTK_TX_DELAY_PAIR_B_MASK | MTK_TX_DELAY_PAIR_D_MASK, ++ FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) | ++ FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4)); + + return 0; + } +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -9,6 +9,8 @@ + #define _MTK_EPHY_H_ + + #define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_STANDARD 0x0000 ++#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + + /* Registers on MDIO_MMD_VEND2 */ + #define MTK_PHY_LED0_ON_CTRL 0x24 diff --git a/target/linux/generic/backport-6.6/746-v6.15-02-net-phy-mediatek-Add-token-ring-access-helper-functi.patch b/target/linux/generic/backport-6.6/746-v6.15-02-net-phy-mediatek-Add-token-ring-access-helper-functi.patch new file mode 100644 index 00000000000000..383b580fc9285b --- /dev/null +++ b/target/linux/generic/backport-6.6/746-v6.15-02-net-phy-mediatek-Add-token-ring-access-helper-functi.patch @@ -0,0 +1,448 @@ +From afa08fde7c4780ea5556d9d4df6c1daa38ba0d6b Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:50 +0800 +Subject: [PATCH 2/5] net: phy: mediatek: Add token ring access helper + functions in mtk-phy-lib + +This patch adds TR(token ring) manipulations and adds correct +macro names for those magic numbers. TR is a way to access +proprietary registers on page 52b5. Use these helper functions +so we can see which fields we're going to modify/set/clear. + +TR functions with __* prefix mean that the operations inside +aren't wrapped by page select/restore functions. + +This patch doesn't really change registers' settings but just +enhances readability and maintainability. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-3-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 231 +++++++++++++++++-------- + drivers/net/phy/mediatek/mtk-ge.c | 11 +- + drivers/net/phy/mediatek/mtk-phy-lib.c | 63 +++++++ + drivers/net/phy/mediatek/mtk.h | 5 + + 4 files changed, 230 insertions(+), 80 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -25,6 +25,90 @@ + + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 + ++/* Registers on Token Ring debug nodes */ ++/* ch_addr = 0x0, node_addr = 0x7, data_addr = 0x15 */ ++/* NormMseLoThresh */ ++#define NORMAL_MSE_LO_THRESH_MASK GENMASK(15, 8) ++ ++/* ch_addr = 0x0, node_addr = 0xf, data_addr = 0x3c */ ++/* RemAckCntLimitCtrl */ ++#define REMOTE_ACK_COUNT_LIMIT_CTRL_MASK GENMASK(2, 1) ++ ++/* ch_addr = 0x1, node_addr = 0xd, data_addr = 0x20 */ ++/* VcoSlicerThreshBitsHigh */ ++#define VCO_SLICER_THRESH_HIGH_MASK GENMASK(23, 0) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x0 */ ++/* DfeTailEnableVgaThresh1000 */ ++#define DFE_TAIL_EANBLE_VGA_TRHESH_1000 GENMASK(5, 1) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x1 */ ++/* MrvlTrFix100Kp */ ++#define MRVL_TR_FIX_100KP_MASK GENMASK(22, 20) ++/* MrvlTrFix100Kf */ ++#define MRVL_TR_FIX_100KF_MASK GENMASK(19, 17) ++/* MrvlTrFix1000Kp */ ++#define MRVL_TR_FIX_1000KP_MASK GENMASK(16, 14) ++/* MrvlTrFix1000Kf */ ++#define MRVL_TR_FIX_1000KF_MASK GENMASK(13, 11) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x12 */ ++/* VgaDecRate */ ++#define VGA_DECIMATION_RATE_MASK GENMASK(8, 5) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x17 */ ++/* SlvDSPreadyTime */ ++#define SLAVE_DSP_READY_TIME_MASK GENMASK(22, 15) ++/* MasDSPreadyTime */ ++#define MASTER_DSP_READY_TIME_MASK GENMASK(14, 7) ++ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x20 */ ++/* ResetSyncOffset */ ++#define RESET_SYNC_OFFSET_MASK GENMASK(11, 8) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x0 */ ++/* FfeUpdGainForceVal */ ++#define FFE_UPDATE_GAIN_FORCE_VAL_MASK GENMASK(9, 7) ++/* FfeUpdGainForce */ ++#define FFE_UPDATE_GAIN_FORCE BIT(6) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x6 */ ++/* SS: Steady-state, KP: Proportional Gain */ ++/* SSTrKp100 */ ++#define SS_TR_KP100_MASK GENMASK(21, 19) ++/* SSTrKf100 */ ++#define SS_TR_KF100_MASK GENMASK(18, 16) ++/* SSTrKp1000Mas */ ++#define SS_TR_KP1000_MASTER_MASK GENMASK(15, 13) ++/* SSTrKf1000Mas */ ++#define SS_TR_KF1000_MASTER_MASK GENMASK(12, 10) ++/* SSTrKp1000Slv */ ++#define SS_TR_KP1000_SLAVE_MASK GENMASK(9, 7) ++/* SSTrKf1000Slv */ ++#define SS_TR_KF1000_SLAVE_MASK GENMASK(6, 4) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xd */ ++/* RegEEE_st2TrKf1000 */ ++#define EEE1000_STAGE2_TR_KF_MASK GENMASK(13, 11) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xf */ ++/* RegEEE_slv_waketr_timer_tar */ ++#define SLAVE_WAKETR_TIMER_MASK GENMASK(20, 11) ++/* RegEEE_slv_remtx_timer_tar */ ++#define SLAVE_REMTX_TIMER_MASK GENMASK(10, 1) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x10 */ ++/* RegEEE_slv_wake_int_timer_tar */ ++#define SLAVE_WAKEINT_TIMER_MASK GENMASK(10, 1) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x14 */ ++/* RegEEE_trfreeze_timer2 */ ++#define TR_FREEZE_TIMER2_MASK GENMASK(9, 0) ++ ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x1c */ ++/* RegEEE100Stg1_tar */ ++#define EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK GENMASK(8, 0) ++ + #define ANALOG_INTERNAL_OPERATION_MAX_US 20 + #define TXRESERVE_MIN 0 + #define TXRESERVE_MAX 7 +@@ -700,40 +784,41 @@ restore: + static void mt798x_phy_common_finetune(struct phy_device *phydev) + { + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ +- __phy_write(phydev, 0x11, 0xc71); +- __phy_write(phydev, 0x12, 0xc); +- __phy_write(phydev, 0x10, 0x8fae); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x17, ++ SLAVE_DSP_READY_TIME_MASK | MASTER_DSP_READY_TIME_MASK, ++ FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x18) | ++ FIELD_PREP(MASTER_DSP_READY_TIME_MASK, 0x18)); + + /* EnabRandUpdTrig = 1 */ + __phy_write(phydev, 0x11, 0x2f00); + __phy_write(phydev, 0x12, 0xe); + __phy_write(phydev, 0x10, 0x8fb0); + +- /* NormMseLoThresh = 85 */ +- __phy_write(phydev, 0x11, 0x55a0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x83aa); +- +- /* FfeUpdGainForce = 1(Enable), FfeUpdGainForceVal = 4 */ +- __phy_write(phydev, 0x11, 0x240); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9680); ++ __mtk_tr_modify(phydev, 0x0, 0x7, 0x15, ++ NORMAL_MSE_LO_THRESH_MASK, ++ FIELD_PREP(NORMAL_MSE_LO_THRESH_MASK, 0x55)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x0, ++ FFE_UPDATE_GAIN_FORCE_VAL_MASK, ++ FIELD_PREP(FFE_UPDATE_GAIN_FORCE_VAL_MASK, 0x4) | ++ FFE_UPDATE_GAIN_FORCE); + + /* TrFreeze = 0 (mt7988 default) */ + __phy_write(phydev, 0x11, 0x0); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9686); + +- /* SSTrKp100 = 5 */ +- /* SSTrKf100 = 6 */ +- /* SSTrKp1000Mas = 5 */ +- /* SSTrKf1000Mas = 6 */ +- /* SSTrKp1000Slv = 5 */ +- /* SSTrKf1000Slv = 6 */ +- __phy_write(phydev, 0x11, 0xbaef); +- __phy_write(phydev, 0x12, 0x2e); +- __phy_write(phydev, 0x10, 0x968c); ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x6, ++ SS_TR_KP100_MASK | SS_TR_KF100_MASK | ++ SS_TR_KP1000_MASTER_MASK | SS_TR_KF1000_MASTER_MASK | ++ SS_TR_KP1000_SLAVE_MASK | SS_TR_KF1000_SLAVE_MASK, ++ FIELD_PREP(SS_TR_KP100_MASK, 0x5) | ++ FIELD_PREP(SS_TR_KF100_MASK, 0x6) | ++ FIELD_PREP(SS_TR_KP1000_MASTER_MASK, 0x5) | ++ FIELD_PREP(SS_TR_KF1000_MASTER_MASK, 0x6) | ++ FIELD_PREP(SS_TR_KP1000_SLAVE_MASK, 0x5) | ++ FIELD_PREP(SS_TR_KF1000_SLAVE_MASK, 0x6)); ++ + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + } + +@@ -756,27 +841,29 @@ static void mt7981_phy_finetune(struct p + } + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 6 */ +- __phy_write(phydev, 0x11, 0x600); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); +- +- /* VgaDecRate = 1 */ +- __phy_write(phydev, 0x11, 0x4c2a); +- __phy_write(phydev, 0x12, 0x3e); +- __phy_write(phydev, 0x10, 0x8fa4); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x20, ++ RESET_SYNC_OFFSET_MASK, ++ FIELD_PREP(RESET_SYNC_OFFSET_MASK, 0x6)); ++ ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x12, ++ VGA_DECIMATION_RATE_MASK, ++ FIELD_PREP(VGA_DECIMATION_RATE_MASK, 0x1)); + + /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, + * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 + */ +- __phy_write(phydev, 0x11, 0xd10a); +- __phy_write(phydev, 0x12, 0x34); +- __phy_write(phydev, 0x10, 0x8f82); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x1, ++ MRVL_TR_FIX_100KP_MASK | MRVL_TR_FIX_100KF_MASK | ++ MRVL_TR_FIX_1000KP_MASK | MRVL_TR_FIX_1000KF_MASK, ++ FIELD_PREP(MRVL_TR_FIX_100KP_MASK, 0x3) | ++ FIELD_PREP(MRVL_TR_FIX_100KF_MASK, 0x2) | ++ FIELD_PREP(MRVL_TR_FIX_1000KP_MASK, 0x3) | ++ FIELD_PREP(MRVL_TR_FIX_1000KF_MASK, 0x2)); + + /* VcoSlicerThreshBitsHigh */ +- __phy_write(phydev, 0x11, 0x5555); +- __phy_write(phydev, 0x12, 0x55); +- __phy_write(phydev, 0x10, 0x8ec0); ++ __mtk_tr_modify(phydev, 0x1, 0xd, 0x20, ++ VCO_SLICER_THRESH_HIGH_MASK, ++ FIELD_PREP(VCO_SLICER_THRESH_HIGH_MASK, 0x555555)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9 */ +@@ -828,25 +915,23 @@ static void mt7988_phy_finetune(struct p + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* ResetSyncOffset = 5 */ +- __phy_write(phydev, 0x11, 0x500); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8fc0); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x20, ++ RESET_SYNC_OFFSET_MASK, ++ FIELD_PREP(RESET_SYNC_OFFSET_MASK, 0x5)); + + /* VgaDecRate is 1 at default on mt7988 */ + +- /* MrvlTrFix100Kp = 6, MrvlTrFix100Kf = 7, +- * MrvlTrFix1000Kp = 6, MrvlTrFix1000Kf = 7 +- */ +- __phy_write(phydev, 0x11, 0xb90a); +- __phy_write(phydev, 0x12, 0x6f); +- __phy_write(phydev, 0x10, 0x8f82); +- +- /* RemAckCntLimitCtrl = 1 */ +- __phy_write(phydev, 0x11, 0xfbba); +- __phy_write(phydev, 0x12, 0xc3); +- __phy_write(phydev, 0x10, 0x87f8); +- ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x1, ++ MRVL_TR_FIX_100KP_MASK | MRVL_TR_FIX_100KF_MASK | ++ MRVL_TR_FIX_1000KP_MASK | MRVL_TR_FIX_1000KF_MASK, ++ FIELD_PREP(MRVL_TR_FIX_100KP_MASK, 0x6) | ++ FIELD_PREP(MRVL_TR_FIX_100KF_MASK, 0x7) | ++ FIELD_PREP(MRVL_TR_FIX_1000KP_MASK, 0x6) | ++ FIELD_PREP(MRVL_TR_FIX_1000KF_MASK, 0x7)); ++ ++ __mtk_tr_modify(phydev, 0x0, 0xf, 0x3c, ++ REMOTE_ACK_COUNT_LIMIT_CTRL_MASK, ++ FIELD_PREP(REMOTE_ACK_COUNT_LIMIT_CTRL_MASK, 0x1)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 10 */ +@@ -927,40 +1012,36 @@ static void mt798x_phy_eee(struct phy_de + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9690); + +- /* REG_EEE_st2TrKf1000 = 2 */ +- __phy_write(phydev, 0x11, 0x114f); +- __phy_write(phydev, 0x12, 0x2); +- __phy_write(phydev, 0x10, 0x969a); +- +- /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ +- __phy_write(phydev, 0x11, 0x3028); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x969e); +- +- /* RegEEE_slv_wake_int_timer_tar = 8 */ +- __phy_write(phydev, 0x11, 0x5010); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a0); +- +- /* RegEEE_trfreeze_timer2 = 586 */ +- __phy_write(phydev, 0x11, 0x24a); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96a8); +- +- /* RegEEE100Stg1_tar = 16 */ +- __phy_write(phydev, 0x11, 0x3210); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96b8); ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0xd, ++ EEE1000_STAGE2_TR_KF_MASK, ++ FIELD_PREP(EEE1000_STAGE2_TR_KF_MASK, 0x2)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0xf, ++ SLAVE_WAKETR_TIMER_MASK | SLAVE_REMTX_TIMER_MASK, ++ FIELD_PREP(SLAVE_WAKETR_TIMER_MASK, 0x6) | ++ FIELD_PREP(SLAVE_REMTX_TIMER_MASK, 0x14)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x10, ++ SLAVE_WAKEINT_TIMER_MASK, ++ FIELD_PREP(SLAVE_WAKEINT_TIMER_MASK, 0x8)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x14, ++ TR_FREEZE_TIMER2_MASK, ++ FIELD_PREP(TR_FREEZE_TIMER2_MASK, 0x24a)); ++ ++ __mtk_tr_modify(phydev, 0x2, 0xd, 0x1c, ++ EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK, ++ FIELD_PREP(EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK, ++ 0x10)); + + /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ + __phy_write(phydev, 0x11, 0x1463); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96ca); + +- /* DfeTailEnableVgaThresh1000 = 27 */ +- __phy_write(phydev, 0x11, 0x36); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x8f80); ++ __mtk_tr_modify(phydev, 0x1, 0xf, 0x0, ++ DFE_TAIL_EANBLE_VGA_TRHESH_1000, ++ FIELD_PREP(DFE_TAIL_EANBLE_VGA_TRHESH_1000, 0x1b)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -18,6 +18,10 @@ + + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 + ++/* Registers on Token Ring debug nodes */ ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x17 */ ++#define SLAVE_DSP_READY_TIME_MASK GENMASK(22, 15) ++ + /* Registers on MDIO_MMD_VEND1 */ + #define MTK_PHY_GBE_MODE_TX_DELAY_SEL 0x13 + #define MTK_PHY_TEST_MODE_TX_DELAY_SEL 0x14 +@@ -45,11 +49,8 @@ static void mtk_gephy_config_init(struct + 0, MTK_PHY_ENABLE_DOWNSHIFT); + + /* Increase SlvDPSready time */ +- phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- __phy_write(phydev, 0x10, 0xafae); +- __phy_write(phydev, 0x12, 0x2f); +- __phy_write(phydev, 0x10, 0x8fae); +- phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ mtk_tr_modify(phydev, 0x1, 0xf, 0x17, SLAVE_DSP_READY_TIME_MASK, ++ FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x5e)); + + /* Adjust 100_mse_threshold */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -6,6 +6,69 @@ + + #include "mtk.h" + ++/* Difference between functions with mtk_tr* and __mtk_tr* prefixes is ++ * mtk_tr* functions: wrapped by page switching operations ++ * __mtk_tr* functions: no page switching operations ++ */ ++ ++static void __mtk_tr_access(struct phy_device *phydev, bool read, u8 ch_addr, ++ u8 node_addr, u8 data_addr) ++{ ++ u16 tr_cmd = BIT(15); /* bit 14 & 0 are reserved */ ++ ++ if (read) ++ tr_cmd |= BIT(13); ++ ++ tr_cmd |= (((ch_addr & 0x3) << 11) | ++ ((node_addr & 0xf) << 7) | ++ ((data_addr & 0x3f) << 1)); ++ dev_dbg(&phydev->mdio.dev, "tr_cmd: 0x%x\n", tr_cmd); ++ __phy_write(phydev, 0x10, tr_cmd); ++} ++ ++static void __mtk_tr_read(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u16 *tr_high, u16 *tr_low) ++{ ++ __mtk_tr_access(phydev, true, ch_addr, node_addr, data_addr); ++ *tr_low = __phy_read(phydev, 0x11); ++ *tr_high = __phy_read(phydev, 0x12); ++ dev_dbg(&phydev->mdio.dev, "tr_high read: 0x%x, tr_low read: 0x%x\n", ++ *tr_high, *tr_low); ++} ++ ++static void __mtk_tr_write(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 tr_data) ++{ ++ __phy_write(phydev, 0x11, tr_data & 0xffff); ++ __phy_write(phydev, 0x12, tr_data >> 16); ++ dev_dbg(&phydev->mdio.dev, "tr_high write: 0x%x, tr_low write: 0x%x\n", ++ tr_data >> 16, tr_data & 0xffff); ++ __mtk_tr_access(phydev, false, ch_addr, node_addr, data_addr); ++} ++ ++void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set) ++{ ++ u32 tr_data; ++ u16 tr_high; ++ u16 tr_low; ++ ++ __mtk_tr_read(phydev, ch_addr, node_addr, data_addr, &tr_high, &tr_low); ++ tr_data = (tr_high << 16) | tr_low; ++ tr_data = (tr_data & ~mask) | set; ++ __mtk_tr_write(phydev, ch_addr, node_addr, data_addr, tr_data); ++} ++EXPORT_SYMBOL_GPL(__mtk_tr_modify); ++ ++void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set) ++{ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, mask, set); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++} ++EXPORT_SYMBOL_GPL(mtk_tr_modify); ++ + int mtk_phy_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -68,6 +68,11 @@ struct mtk_socphy_priv { + unsigned long led_state; + }; + ++void __mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set); ++void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 mask, u32 set); ++ + int mtk_phy_read_page(struct phy_device *phydev); + int mtk_phy_write_page(struct phy_device *phydev, int page); + diff --git a/target/linux/generic/backport-6.6/746-v6.15-03-net-phy-mediatek-Add-token-ring-set-bit-operation-su.patch b/target/linux/generic/backport-6.6/746-v6.15-03-net-phy-mediatek-Add-token-ring-set-bit-operation-su.patch new file mode 100644 index 00000000000000..d4c2b39592b9ee --- /dev/null +++ b/target/linux/generic/backport-6.6/746-v6.15-03-net-phy-mediatek-Add-token-ring-set-bit-operation-su.patch @@ -0,0 +1,73 @@ +From 40d33d6d3c90eb104c66e05cdc00db61268c93f9 Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:51 +0800 +Subject: [PATCH 3/5] net: phy: mediatek: Add token ring set bit operation + support + +Previously in mtk-ge-soc.c, we set some register bits via token +ring, which were implemented in three __phy_write(). +Now we can do the same thing via __mtk_tr_set_bits() helper. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-4-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 10 ++++++---- + drivers/net/phy/mediatek/mtk-phy-lib.c | 7 +++++++ + drivers/net/phy/mediatek/mtk.h | 2 ++ + 3 files changed, 15 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -62,6 +62,10 @@ + /* MasDSPreadyTime */ + #define MASTER_DSP_READY_TIME_MASK GENMASK(14, 7) + ++/* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x18 */ ++/* EnabRandUpdTrig */ ++#define ENABLE_RANDOM_UPDOWN_COUNTER_TRIGGER BIT(8) ++ + /* ch_addr = 0x1, node_addr = 0xf, data_addr = 0x20 */ + /* ResetSyncOffset */ + #define RESET_SYNC_OFFSET_MASK GENMASK(11, 8) +@@ -789,10 +793,8 @@ static void mt798x_phy_common_finetune(s + FIELD_PREP(SLAVE_DSP_READY_TIME_MASK, 0x18) | + FIELD_PREP(MASTER_DSP_READY_TIME_MASK, 0x18)); + +- /* EnabRandUpdTrig = 1 */ +- __phy_write(phydev, 0x11, 0x2f00); +- __phy_write(phydev, 0x12, 0xe); +- __phy_write(phydev, 0x10, 0x8fb0); ++ __mtk_tr_set_bits(phydev, 0x1, 0xf, 0x18, ++ ENABLE_RANDOM_UPDOWN_COUNTER_TRIGGER); + + __mtk_tr_modify(phydev, 0x0, 0x7, 0x15, + NORMAL_MSE_LO_THRESH_MASK, +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -69,6 +69,13 @@ void mtk_tr_modify(struct phy_device *ph + } + EXPORT_SYMBOL_GPL(mtk_tr_modify); + ++void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 set) ++{ ++ __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, 0, set); ++} ++EXPORT_SYMBOL_GPL(__mtk_tr_set_bits); ++ + int mtk_phy_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -72,6 +72,8 @@ void __mtk_tr_modify(struct phy_device * + u8 data_addr, u32 mask, u32 set); + void mtk_tr_modify(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 mask, u32 set); ++void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 set); + + int mtk_phy_read_page(struct phy_device *phydev); + int mtk_phy_write_page(struct phy_device *phydev, int page); diff --git a/target/linux/generic/backport-6.6/746-v6.15-04-net-phy-mediatek-Add-token-ring-clear-bit-operation-.patch b/target/linux/generic/backport-6.6/746-v6.15-04-net-phy-mediatek-Add-token-ring-clear-bit-operation-.patch new file mode 100644 index 00000000000000..c5a14137a62eda --- /dev/null +++ b/target/linux/generic/backport-6.6/746-v6.15-04-net-phy-mediatek-Add-token-ring-clear-bit-operation-.patch @@ -0,0 +1,122 @@ +From 4786eff288bcc77a5dbc2be2308f46f70e58600d Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:52 +0800 +Subject: [PATCH 4/5] net: phy: mediatek: Add token ring clear bit operation + support + +Similar to __mtk_tr_set_bits() support. Previously in mtk-ge-soc.c, +we clear some register bits via token ring, which were also implemented +in three __phy_write(). Now we can do the same thing via +__mtk_tr_clr_bits() helper. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-5-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge-soc.c | 30 +++++++++++++++----------- + drivers/net/phy/mediatek/mtk-phy-lib.c | 7 ++++++ + drivers/net/phy/mediatek/mtk.h | 2 ++ + 3 files changed, 27 insertions(+), 12 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge-soc.c ++++ b/drivers/net/phy/mediatek/mtk-ge-soc.c +@@ -76,6 +76,10 @@ + /* FfeUpdGainForce */ + #define FFE_UPDATE_GAIN_FORCE BIT(6) + ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x3 */ ++/* TrFreeze */ ++#define TR_FREEZE_MASK GENMASK(11, 0) ++ + /* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x6 */ + /* SS: Steady-state, KP: Proportional Gain */ + /* SSTrKp100 */ +@@ -91,6 +95,11 @@ + /* SSTrKf1000Slv */ + #define SS_TR_KF1000_SLAVE_MASK GENMASK(6, 4) + ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x8 */ ++/* clear this bit if wanna select from AFE */ ++/* Regsigdet_sel_1000 */ ++#define EEE1000_SELECT_SIGNAL_DETECTION_FROM_DFE BIT(4) ++ + /* ch_addr = 0x2, node_addr = 0xd, data_addr = 0xd */ + /* RegEEE_st2TrKf1000 */ + #define EEE1000_STAGE2_TR_KF_MASK GENMASK(13, 11) +@@ -113,6 +122,10 @@ + /* RegEEE100Stg1_tar */ + #define EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK GENMASK(8, 0) + ++/* ch_addr = 0x2, node_addr = 0xd, data_addr = 0x25 */ ++/* REGEEE_wake_slv_tr_wait_dfesigdet_en */ ++#define WAKE_SLAVE_TR_WAIT_DFE_DETECTION_EN BIT(11) ++ + #define ANALOG_INTERNAL_OPERATION_MAX_US 20 + #define TXRESERVE_MIN 0 + #define TXRESERVE_MAX 7 +@@ -805,10 +818,7 @@ static void mt798x_phy_common_finetune(s + FIELD_PREP(FFE_UPDATE_GAIN_FORCE_VAL_MASK, 0x4) | + FFE_UPDATE_GAIN_FORCE); + +- /* TrFreeze = 0 (mt7988 default) */ +- __phy_write(phydev, 0x11, 0x0); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9686); ++ __mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x3, TR_FREEZE_MASK); + + __mtk_tr_modify(phydev, 0x2, 0xd, 0x6, + SS_TR_KP100_MASK | SS_TR_KF100_MASK | +@@ -1009,10 +1019,8 @@ static void mt798x_phy_eee(struct phy_de + MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); +- /* Regsigdet_sel_1000 = 0 */ +- __phy_write(phydev, 0x11, 0xb); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x9690); ++ __mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x8, ++ EEE1000_SELECT_SIGNAL_DETECTION_FROM_DFE); + + __mtk_tr_modify(phydev, 0x2, 0xd, 0xd, + EEE1000_STAGE2_TR_KF_MASK, +@@ -1036,10 +1044,8 @@ static void mt798x_phy_eee(struct phy_de + FIELD_PREP(EEE100_LPSYNC_STAGE1_UPDATE_TIMER_MASK, + 0x10)); + +- /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 0 */ +- __phy_write(phydev, 0x11, 0x1463); +- __phy_write(phydev, 0x12, 0x0); +- __phy_write(phydev, 0x10, 0x96ca); ++ __mtk_tr_clr_bits(phydev, 0x2, 0xd, 0x25, ++ WAKE_SLAVE_TR_WAIT_DFE_DETECTION_EN); + + __mtk_tr_modify(phydev, 0x1, 0xf, 0x0, + DFE_TAIL_EANBLE_VGA_TRHESH_1000, +--- a/drivers/net/phy/mediatek/mtk-phy-lib.c ++++ b/drivers/net/phy/mediatek/mtk-phy-lib.c +@@ -76,6 +76,13 @@ void __mtk_tr_set_bits(struct phy_device + } + EXPORT_SYMBOL_GPL(__mtk_tr_set_bits); + ++void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 clr) ++{ ++ __mtk_tr_modify(phydev, ch_addr, node_addr, data_addr, clr, 0); ++} ++EXPORT_SYMBOL_GPL(__mtk_tr_clr_bits); ++ + int mtk_phy_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -74,6 +74,8 @@ void mtk_tr_modify(struct phy_device *ph + u8 data_addr, u32 mask, u32 set); + void __mtk_tr_set_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, + u8 data_addr, u32 set); ++void __mtk_tr_clr_bits(struct phy_device *phydev, u8 ch_addr, u8 node_addr, ++ u8 data_addr, u32 clr); + + int mtk_phy_read_page(struct phy_device *phydev); + int mtk_phy_write_page(struct phy_device *phydev, int page); diff --git a/target/linux/generic/backport-6.6/746-v6.15-05-net-phy-mediatek-Move-some-macros-to-phy-lib-for-lat.patch b/target/linux/generic/backport-6.6/746-v6.15-05-net-phy-mediatek-Move-some-macros-to-phy-lib-for-lat.patch new file mode 100644 index 00000000000000..dd7543d6f97dad --- /dev/null +++ b/target/linux/generic/backport-6.6/746-v6.15-05-net-phy-mediatek-Move-some-macros-to-phy-lib-for-lat.patch @@ -0,0 +1,45 @@ +From be378ebd6cfb8e369d4aa03a551e594d00debda5 Mon Sep 17 00:00:00 2001 +From: Sky Huang +Date: Thu, 13 Feb 2025 16:05:53 +0800 +Subject: [PATCH 5/5] net: phy: mediatek: Move some macros to phy-lib for later + use + +Move some macros to phy-lib because MediaTek's 2.5G built-in +ethernet PHY will also use them. + +Signed-off-by: Sky Huang +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250213080553.921434-6-SkyLake.Huang@mediatek.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/mtk-ge.c | 4 ---- + drivers/net/phy/mediatek/mtk.h | 4 ++++ + 2 files changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c +@@ -8,10 +8,6 @@ + #define MTK_GPHY_ID_MT7530 0x03a29412 + #define MTK_GPHY_ID_MT7531 0x03a29441 + +-#define MTK_PHY_PAGE_EXTENDED_1 0x0001 +-#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 +-#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) +- + #define MTK_PHY_PAGE_EXTENDED_2 0x0002 + #define MTK_PHY_PAGE_EXTENDED_3 0x0003 + #define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG11 0x11 +--- a/drivers/net/phy/mediatek/mtk.h ++++ b/drivers/net/phy/mediatek/mtk.h +@@ -8,7 +8,11 @@ + #ifndef _MTK_EPHY_H_ + #define _MTK_EPHY_H_ + ++#define MTK_PHY_AUX_CTRL_AND_STATUS 0x14 ++#define MTK_PHY_ENABLE_DOWNSHIFT BIT(4) ++ + #define MTK_EXT_PAGE_ACCESS 0x1f ++#define MTK_PHY_PAGE_EXTENDED_1 0x0001 + #define MTK_PHY_PAGE_STANDARD 0x0000 + #define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + diff --git a/target/linux/generic/backport-6.6/747-v6.16-01-net-phy-mediatek-permit-to-compile-test-GE-SOC-PHY-d.patch b/target/linux/generic/backport-6.6/747-v6.16-01-net-phy-mediatek-permit-to-compile-test-GE-SOC-PHY-d.patch new file mode 100644 index 00000000000000..938217ed733da8 --- /dev/null +++ b/target/linux/generic/backport-6.6/747-v6.16-01-net-phy-mediatek-permit-to-compile-test-GE-SOC-PHY-d.patch @@ -0,0 +1,38 @@ +From e5566162af8b9690e096d2e6089e4ed955a0d13d Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Thu, 10 Apr 2025 12:04:03 +0200 +Subject: [PATCH 1/2] net: phy: mediatek: permit to compile test GE SOC PHY + driver + +When commit 462a3daad679 ("net: phy: mediatek: fix compile-test +dependencies") fixed the dependency, it should have also introduced +an or on COMPILE_TEST to permit this driver to be compile-tested even if +NVMEM_MTK_EFUSE wasn't selected. The driver makes use of NVMEM API that +are always compiled (return error) so the driver can actually be +compiled even without that config. + +Fix and simplify the dependency condition of this kernel config. + +Fixes: 462a3daad679 ("net: phy: mediatek: fix compile-test dependencies") +Acked-by: Daniel Golle +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Acked-by: Arnd Bergmann +Link: https://patch.msgid.link/20250410100410.348-1-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/mediatek/Kconfig | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/drivers/net/phy/mediatek/Kconfig ++++ b/drivers/net/phy/mediatek/Kconfig +@@ -15,8 +15,7 @@ config MEDIATEK_GE_PHY + + config MEDIATEK_GE_SOC_PHY + tristate "MediaTek SoC Ethernet PHYs" +- depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST +- depends on NVMEM_MTK_EFUSE ++ depends on (ARM64 && ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || COMPILE_TEST + select MTK_NET_PHYLIB + help + Supports MediaTek SoC built-in Gigabit Ethernet PHYs. diff --git a/target/linux/generic/backport-6.6/781-24-v6.14-net-phy-move-realtek-PHY-driver-to-its-own-subdirect.patch b/target/linux/generic/backport-6.6/781-24-v6.14-net-phy-move-realtek-PHY-driver-to-its-own-subdirect.patch index e66d724a981f66..f810f718e09fd3 100644 --- a/target/linux/generic/backport-6.6/781-24-v6.14-net-phy-move-realtek-PHY-driver-to-its-own-subdirect.patch +++ b/target/linux/generic/backport-6.6/781-24-v6.14-net-phy-move-realtek-PHY-driver-to-its-own-subdirect.patch @@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -325,10 +325,7 @@ config QSEMI_PHY +@@ -310,10 +310,7 @@ config QSEMI_PHY help Currently supports the qs6612 @@ -38,7 +38,7 @@ Signed-off-by: Jakub Kicinski tristate "Renesas PHYs" --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -83,7 +83,7 @@ obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o +@@ -82,7 +82,7 @@ obj-$(CONFIG_NXP_CBTX_PHY) += nxp-cbtx.o obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o obj-y += qcom/ obj-$(CONFIG_QSEMI_PHY) += qsemi.o diff --git a/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch b/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch new file mode 100644 index 00000000000000..b8460a2b5e2d8b --- /dev/null +++ b/target/linux/generic/backport-6.6/782-01-v6.16-net-phy-pass-PHY-driver-to-.match_phy_device-OP.patch @@ -0,0 +1,273 @@ +From 31afd6bc55cc0093c3e5b0a368319e423d4de8ea Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:45 +0200 +Subject: [PATCH 1/5] net: phy: pass PHY driver to .match_phy_device OP + +Pass PHY driver pointer to .match_phy_device OP in addition to phydev. +Having access to the PHY driver struct might be useful to check the +PHY ID of the driver is being matched for in case the PHY ID scanned in +the phydev is not consistent. + +A scenario for this is a PHY that change PHY ID after a firmware is +loaded, in such case, the PHY ID stored in PHY device struct is not +valid anymore and PHY will manually scan the ID in the match_phy_device +function. + +Having the PHY driver info is also useful for those PHY driver that +implement multiple simple .match_phy_device OP to match specific MMD PHY +ID. With this extra info if the parsing logic is the same, the matching +function can be generalized by using the phy_id in the PHY driver +instead of hardcoding. + +Rust wrapper callback is updated to align to the new match_phy_device +arguments. + +Suggested-by: Russell King (Oracle) +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Reviewed-by: Benno Lossin # for Rust +Reviewed-by: FUJITA Tomonori +Link: https://patch.msgid.link/20250517201353.5137-2-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/bcm87xx.c | 6 ++++-- + drivers/net/phy/icplus.c | 6 ++++-- + drivers/net/phy/marvell10g.c | 12 ++++++++---- + drivers/net/phy/micrel.c | 6 ++++-- + drivers/net/phy/nxp-c45-tja11xx.c | 12 ++++++++---- + drivers/net/phy/nxp-tja11xx.c | 6 ++++-- + drivers/net/phy/phy_device.c | 2 +- + drivers/net/phy/realtek/realtek_main.c | 27 +++++++++++++++++--------- + drivers/net/phy/teranetics.c | 3 ++- + include/linux/phy.h | 3 ++- + rust/kernel/net/phy.rs | 1 + + 11 files changed, 56 insertions(+), 28 deletions(-) + +--- a/drivers/net/phy/bcm87xx.c ++++ b/drivers/net/phy/bcm87xx.c +@@ -185,12 +185,14 @@ static irqreturn_t bcm87xx_handle_interr + return IRQ_HANDLED; + } + +-static int bcm8706_match_phy_device(struct phy_device *phydev) ++static int bcm8706_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706; + } + +-static int bcm8727_match_phy_device(struct phy_device *phydev) ++static int bcm8727_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727; + } +--- a/drivers/net/phy/icplus.c ++++ b/drivers/net/phy/icplus.c +@@ -520,12 +520,14 @@ static int ip101a_g_match_phy_device(str + return ip101a == !ret; + } + +-static int ip101a_match_phy_device(struct phy_device *phydev) ++static int ip101a_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, true); + } + +-static int ip101g_match_phy_device(struct phy_device *phydev) ++static int ip101g_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ip101a_g_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/marvell10g.c ++++ b/drivers/net/phy/marvell10g.c +@@ -1221,7 +1221,8 @@ static int mv3310_get_number_of_ports(st + return ret + 1; + } + +-static int mv3310_match_phy_device(struct phy_device *phydev) ++static int mv3310_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1230,7 +1231,8 @@ static int mv3310_match_phy_device(struc + return mv3310_get_number_of_ports(phydev) == 1; + } + +-static int mv3340_match_phy_device(struct phy_device *phydev) ++static int mv3340_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if ((phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] & + MARVELL_PHY_ID_MASK) != MARVELL_PHY_ID_88X3310) +@@ -1254,12 +1256,14 @@ static int mv211x_match_phy_device(struc + return !!(val & MDIO_PCS_SPEED_5G) == has_5g; + } + +-static int mv2110_match_phy_device(struct phy_device *phydev) ++static int mv2110_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, true); + } + +-static int mv2111_match_phy_device(struct phy_device *phydev) ++static int mv2111_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return mv211x_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/micrel.c ++++ b/drivers/net/phy/micrel.c +@@ -670,7 +670,8 @@ static int ksz8051_ksz8795_match_phy_dev + return !ret; + } + +-static int ksz8051_match_phy_device(struct phy_device *phydev) ++static int ksz8051_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, true); + } +@@ -790,7 +791,8 @@ static int ksz8061_config_init(struct ph + return kszphy_config_init(phydev); + } + +-static int ksz8795_match_phy_device(struct phy_device *phydev) ++static int ksz8795_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return ksz8051_ksz8795_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/nxp-tja11xx.c ++++ b/drivers/net/phy/nxp-tja11xx.c +@@ -648,12 +648,14 @@ static int tja1102_match_phy_device(stru + return !ret; + } + +-static int tja1102_p0_match_phy_device(struct phy_device *phydev) ++static int tja1102_p0_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, true); + } + +-static int tja1102_p1_match_phy_device(struct phy_device *phydev) ++static int tja1102_p1_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return tja1102_match_phy_device(phydev, false); + } +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -533,7 +533,7 @@ static int phy_bus_match(struct device * + return 0; + + if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev); ++ return phydrv->match_phy_device(phydev, phydrv); + + if (phydev->is_c45) { + for (i = 1; i < num_ids; i++) { +--- a/drivers/net/phy/realtek/realtek_main.c ++++ b/drivers/net/phy/realtek/realtek_main.c +@@ -1315,13 +1315,15 @@ static bool rtlgen_supports_mmd(struct p + return val > 0; + } + +-static int rtlgen_match_phy_device(struct phy_device *phydev) ++static int rtlgen_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + !rtlgen_supports_2_5gbps(phydev); + } + +-static int rtl8226_match_phy_device(struct phy_device *phydev) ++static int rtl8226_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_GENERIC_PHYID && + rtlgen_supports_2_5gbps(phydev) && +@@ -1337,32 +1339,38 @@ static int rtlgen_is_c45_match(struct ph + return !is_c45 && (id == phydev->phy_id); + } + +-static int rtl8221b_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); + } + +-static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); + } + +-static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true); + } + +-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false); + } + +-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); + } + +-static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) ++static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + if (phydev->is_c45) + return false; +@@ -1381,7 +1389,8 @@ static int rtl_internal_nbaset_match_phy + return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); + } + +-static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) ++static int rtl8251b_c45_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return rtlgen_is_c45_match(phydev, RTL_8251B, true); + } +--- a/drivers/net/phy/teranetics.c ++++ b/drivers/net/phy/teranetics.c +@@ -67,7 +67,8 @@ static int teranetics_read_status(struct + return 0; + } + +-static int teranetics_match_phy_device(struct phy_device *phydev) ++static int teranetics_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) + { + return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -972,7 +972,8 @@ struct phy_driver { + * driver for the given phydev. If NULL, matching is based on + * phy_id and phy_id_mask. + */ +- int (*match_phy_device)(struct phy_device *phydev); ++ int (*match_phy_device)(struct phy_device *phydev, ++ const struct phy_driver *phydrv); + + /** + * @set_wol: Some devices (e.g. qnap TS-119P II) require PHY diff --git a/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch b/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch new file mode 100644 index 00000000000000..1c0b5d836db2a7 --- /dev/null +++ b/target/linux/generic/backport-6.6/782-04-v6.16-net-phy-introduce-genphy_match_phy_device.patch @@ -0,0 +1,109 @@ +From d6c45707ac84c2d9f274ece1cea4dddb97996bde Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:48 +0200 +Subject: [PATCH 4/5] net: phy: introduce genphy_match_phy_device() + +Introduce new API, genphy_match_phy_device(), to provide a way to check +to match a PHY driver for a PHY device based on the info stored in the +PHY device struct. + +The function generalize the logic used in phy_bus_match() to check the +PHY ID whether if C45 or C22 ID should be used for matching. + +This is useful for custom .match_phy_device function that wants to use +the generic logic under some condition. (example a PHY is already setup +and provide the correct PHY ID) + +Reviewed-by: Russell King (Oracle) +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-5-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/phy_device.c | 52 +++++++++++++++++++++++++----------- + include/linux/phy.h | 3 +++ + 2 files changed, 40 insertions(+), 15 deletions(-) + +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -522,20 +522,26 @@ static int phy_scan_fixups(struct phy_de + return 0; + } + +-static int phy_bus_match(struct device *dev, struct device_driver *drv) ++/** ++ * genphy_match_phy_device - match a PHY device with a PHY driver ++ * @phydev: target phy_device struct ++ * @phydrv: target phy_driver struct ++ * ++ * Description: Checks whether the given PHY device matches the specified ++ * PHY driver. For Clause 45 PHYs, iterates over the available device ++ * identifiers and compares them against the driver's expected PHY ID, ++ * applying the provided mask. For Clause 22 PHYs, a direct ID comparison ++ * is performed. ++ * ++ * Return: 1 if the PHY device matches the driver, 0 otherwise. ++ */ ++int genphy_match_phy_device(struct phy_device *phydev, ++ struct phy_driver *phydrv) + { +- struct phy_device *phydev = to_phy_device(dev); +- struct phy_driver *phydrv = to_phy_driver(drv); +- const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); +- int i; +- +- if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) +- return 0; +- +- if (phydrv->match_phy_device) +- return phydrv->match_phy_device(phydev, phydrv); +- + if (phydev->is_c45) { ++ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); ++ int i; ++ + for (i = 1; i < num_ids; i++) { + if (phydev->c45_ids.device_ids[i] == 0xffffffff) + continue; +@@ -544,11 +550,27 @@ static int phy_bus_match(struct device * + phydrv->phy_id, phydrv->phy_id_mask)) + return 1; + } ++ + return 0; +- } else { +- return phy_id_compare(phydev->phy_id, phydrv->phy_id, +- phydrv->phy_id_mask); + } ++ ++ return phy_id_compare(phydev->phy_id, phydrv->phy_id, ++ phydrv->phy_id_mask); ++} ++EXPORT_SYMBOL_GPL(genphy_match_phy_device); ++ ++static int phy_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ struct phy_device *phydev = to_phy_device(dev); ++ struct phy_driver *phydrv = to_phy_driver(drv); ++ ++ if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) ++ return 0; ++ ++ if (phydrv->match_phy_device) ++ return phydrv->match_phy_device(phydev, phydrv); ++ ++ return genphy_match_phy_device(phydev, phydrv); + } + + static ssize_t +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1812,6 +1812,9 @@ char *phy_attached_info_irq(struct phy_d + __malloc; + void phy_attached_info(struct phy_device *phydev); + ++int genphy_match_phy_device(struct phy_device *phydev, ++ struct phy_driver *phydrv); ++ + /* Clause 22 PHY */ + int genphy_read_abilities(struct phy_device *phydev); + int genphy_setup_forced(struct phy_device *phydev); diff --git a/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch b/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch new file mode 100644 index 00000000000000..cf33b7bfdf53d3 --- /dev/null +++ b/target/linux/generic/backport-6.6/782-05-v6.16-net-phy-Add-support-for-Aeonsemi-AS21xxx-PHYs.patch @@ -0,0 +1,1165 @@ +From 830877d89edcd834e4b4d0fcc021ff619d89505e Mon Sep 17 00:00:00 2001 +From: Christian Marangi +Date: Sat, 17 May 2025 22:13:49 +0200 +Subject: [PATCH 5/5] net: phy: Add support for Aeonsemi AS21xxx PHYs + +Add support for Aeonsemi AS21xxx 10G C45 PHYs. These PHYs integrate +an IPC to setup some configuration and require special handling to +sync with the parity bit. The parity bit is a way the IPC use to +follow correct order of command sent. + +Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, +AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, +AS21210PB1 that all register with the PHY ID 0x7500 0x7510 +before the firmware is loaded. + +They all support up to 5 LEDs with various HW mode supported. + +While implementing it was found some strange coincidence with using the +same logic for implementing C22 in MMD regs in Broadcom PHYs. + +For reference here the AS21xxx PHY name logic: + +AS21x1xxB1 + ^ ^^ + | |J: Supports SyncE/PTP + | |P: No SyncE/PTP support + | 1: Supports 2nd Serdes + | 2: Not 2nd Serdes support + 0: 10G, 5G, 2.5G + 5: 5G, 2.5G + 2: 2.5G + +Reviewed-by: Andrew Lunn +Signed-off-by: Christian Marangi +Link: https://patch.msgid.link/20250517201353.5137-6-ansuelsmth@gmail.com +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 6 + + drivers/net/phy/Kconfig | 12 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/as21xxx.c | 1087 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 1106 insertions(+) + create mode 100644 drivers/net/phy/as21xxx.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -68,6 +68,18 @@ config SFP + + comment "MII PHY device drivers" + ++config AS21XXX_PHY ++ tristate "Aeonsemi AS21xxx PHYs" ++ help ++ Currently supports the Aeonsemi AS21xxx PHY. ++ ++ These are C45 PHYs 10G that require all a generic firmware. ++ ++ Supported PHYs AS21011JB1, AS21011PB1, AS21010JB1, AS21010PB1, ++ AS21511JB1, AS21511PB1, AS21510JB1, AS21510PB1, AS21210JB1, ++ AS21210PB1 that all register with the PHY ID 0x7500 0x7500 ++ before the firmware is loaded. ++ + config AMD_PHY + tristate "AMD PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -36,6 +36,7 @@ obj-$(CONFIG_ADIN_PHY) += adin.o + obj-$(CONFIG_ADIN1100_PHY) += adin1100.o + obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ ++obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o + obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_BCM54140_PHY) += bcm54140.o + obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o +--- /dev/null ++++ b/drivers/net/phy/as21xxx.c +@@ -0,0 +1,1087 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Aeonsemi AS21XXxX PHY Driver ++ * ++ * Author: Christian Marangi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR 0x3 ++#define VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR 0x4 ++ ++#define VEND1_GLB_REG_CPU_CTRL 0xe ++#define VEND1_GLB_CPU_CTRL_MASK GENMASK(4, 0) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK GENMASK(12, 8) ++#define VEND1_GLB_CPU_CTRL_LED_POLARITY(_n) FIELD_PREP(VEND1_GLB_CPU_CTRL_LED_POLARITY_MASK, \ ++ BIT(_n)) ++ ++#define VEND1_FW_START_ADDR 0x100 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD 0x101 ++#define VEND1_GLB_REG_MDIO_INDIRECT_LOAD 0x102 ++ ++#define VEND1_GLB_REG_MDIO_INDIRECT_STATUS 0x103 ++ ++#define VEND1_PTP_CLK 0x142 ++#define VEND1_PTP_CLK_EN BIT(6) ++ ++/* 5 LED at step of 0x20 ++ * FE: Fast-Ethernet (10/100) ++ * GE: Gigabit-Ethernet (1000) ++ * NG: New-Generation (2500/5000/10000) ++ */ ++#define VEND1_LED_REG(_n) (0x1800 + ((_n) * 0x10)) ++#define VEND1_LED_REG_A_EVENT GENMASK(15, 11) ++#define VEND1_LED_CONF 0x1881 ++#define VEND1_LED_CONFG_BLINK GENMASK(7, 0) ++ ++#define VEND1_SPEED_STATUS 0x4002 ++#define VEND1_SPEED_MASK GENMASK(7, 0) ++#define VEND1_SPEED_10000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x3) ++#define VEND1_SPEED_5000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x5) ++#define VEND1_SPEED_2500 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x9) ++#define VEND1_SPEED_1000 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x10) ++#define VEND1_SPEED_100 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x20) ++#define VEND1_SPEED_10 FIELD_PREP_CONST(VEND1_SPEED_MASK, 0x0) ++ ++#define VEND1_IPC_CMD 0x5801 ++#define AEON_IPC_CMD_PARITY BIT(15) ++#define AEON_IPC_CMD_SIZE GENMASK(10, 6) ++#define AEON_IPC_CMD_OPCODE GENMASK(5, 0) ++ ++#define IPC_CMD_NOOP 0x0 /* Do nothing */ ++#define IPC_CMD_INFO 0x1 /* Get Firmware Version */ ++#define IPC_CMD_SYS_CPU 0x2 /* SYS_CPU */ ++#define IPC_CMD_BULK_DATA 0xa /* Pass bulk data in ipc registers. */ ++#define IPC_CMD_BULK_WRITE 0xc /* Write bulk data to memory */ ++#define IPC_CMD_CFG_PARAM 0x1a /* Write config parameters to memory */ ++#define IPC_CMD_NG_TESTMODE 0x1b /* Set NG test mode and tone */ ++#define IPC_CMD_TEMP_MON 0x15 /* Temperature monitoring function */ ++#define IPC_CMD_SET_LED 0x23 /* Set led */ ++ ++#define VEND1_IPC_STS 0x5802 ++#define AEON_IPC_STS_PARITY BIT(15) ++#define AEON_IPC_STS_SIZE GENMASK(14, 10) ++#define AEON_IPC_STS_OPCODE GENMASK(9, 4) ++#define AEON_IPC_STS_STATUS GENMASK(3, 0) ++#define AEON_IPC_STS_STATUS_RCVD FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x1) ++#define AEON_IPC_STS_STATUS_PROCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x2) ++#define AEON_IPC_STS_STATUS_SUCCESS FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x4) ++#define AEON_IPC_STS_STATUS_ERROR FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0x8) ++#define AEON_IPC_STS_STATUS_BUSY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xe) ++#define AEON_IPC_STS_STATUS_READY FIELD_PREP_CONST(AEON_IPC_STS_STATUS, 0xf) ++ ++#define VEND1_IPC_DATA0 0x5808 ++#define VEND1_IPC_DATA1 0x5809 ++#define VEND1_IPC_DATA2 0x580a ++#define VEND1_IPC_DATA3 0x580b ++#define VEND1_IPC_DATA4 0x580c ++#define VEND1_IPC_DATA5 0x580d ++#define VEND1_IPC_DATA6 0x580e ++#define VEND1_IPC_DATA7 0x580f ++#define VEND1_IPC_DATA(_n) (VEND1_IPC_DATA0 + (_n)) ++ ++/* Sub command of CMD_INFO */ ++#define IPC_INFO_VERSION 0x1 ++ ++/* Sub command of CMD_SYS_CPU */ ++#define IPC_SYS_CPU_REBOOT 0x3 ++#define IPC_SYS_CPU_IMAGE_OFST 0x4 ++#define IPC_SYS_CPU_IMAGE_CHECK 0x5 ++#define IPC_SYS_CPU_PHY_ENABLE 0x6 ++ ++/* Sub command of CMD_CFG_PARAM */ ++#define IPC_CFG_PARAM_DIRECT 0x4 ++ ++/* CFG DIRECT sub command */ ++#define IPC_CFG_PARAM_DIRECT_NG_PHYCTRL 0x1 ++#define IPC_CFG_PARAM_DIRECT_CU_AN 0x2 ++#define IPC_CFG_PARAM_DIRECT_SDS_PCS 0x3 ++#define IPC_CFG_PARAM_DIRECT_AUTO_EEE 0x4 ++#define IPC_CFG_PARAM_DIRECT_SDS_PMA 0x5 ++#define IPC_CFG_PARAM_DIRECT_DPC_RA 0x6 ++#define IPC_CFG_PARAM_DIRECT_DPC_PKT_CHK 0x7 ++#define IPC_CFG_PARAM_DIRECT_DPC_SDS_WAIT_ETH 0x8 ++#define IPC_CFG_PARAM_DIRECT_WDT 0x9 ++#define IPC_CFG_PARAM_DIRECT_SDS_RESTART_AN 0x10 ++#define IPC_CFG_PARAM_DIRECT_TEMP_MON 0x11 ++#define IPC_CFG_PARAM_DIRECT_WOL 0x12 ++ ++/* Sub command of CMD_TEMP_MON */ ++#define IPC_CMD_TEMP_MON_GET 0x4 ++ ++#define AS21XXX_MDIO_AN_C22 0xffe0 ++ ++#define PHY_ID_AS21XXX 0x75009410 ++/* AS21xxx ID Legend ++ * AS21x1xxB1 ++ * ^ ^^ ++ * | |J: Supports SyncE/PTP ++ * | |P: No SyncE/PTP support ++ * | 1: Supports 2nd Serdes ++ * | 2: Not 2nd Serdes support ++ * 0: 10G, 5G, 2.5G ++ * 5: 5G, 2.5G ++ * 2: 2.5G ++ */ ++#define PHY_ID_AS21011JB1 0x75009402 ++#define PHY_ID_AS21011PB1 0x75009412 ++#define PHY_ID_AS21010JB1 0x75009422 ++#define PHY_ID_AS21010PB1 0x75009432 ++#define PHY_ID_AS21511JB1 0x75009442 ++#define PHY_ID_AS21511PB1 0x75009452 ++#define PHY_ID_AS21510JB1 0x75009462 ++#define PHY_ID_AS21510PB1 0x75009472 ++#define PHY_ID_AS21210JB1 0x75009482 ++#define PHY_ID_AS21210PB1 0x75009492 ++#define PHY_VENDOR_AEONSEMI 0x75009400 ++ ++#define AEON_MAX_LEDS 5 ++#define AEON_IPC_DELAY 10000 ++#define AEON_IPC_TIMEOUT (AEON_IPC_DELAY * 100) ++#define AEON_IPC_DATA_NUM_REGISTERS 8 ++#define AEON_IPC_DATA_MAX (AEON_IPC_DATA_NUM_REGISTERS * sizeof(u16)) ++ ++#define AEON_BOOT_ADDR 0x1000 ++#define AEON_CPU_BOOT_ADDR 0x2000 ++#define AEON_CPU_CTRL_FW_LOAD (BIT(4) | BIT(2) | BIT(1) | BIT(0)) ++#define AEON_CPU_CTRL_FW_START BIT(0) ++ ++enum as21xxx_led_event { ++ VEND1_LED_REG_A_EVENT_ON_10 = 0x0, ++ VEND1_LED_REG_A_EVENT_ON_100, ++ VEND1_LED_REG_A_EVENT_ON_1000, ++ VEND1_LED_REG_A_EVENT_ON_2500, ++ VEND1_LED_REG_A_EVENT_ON_5000, ++ VEND1_LED_REG_A_EVENT_ON_10000, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_NG, ++ VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX, ++ VEND1_LED_REG_A_EVENT_ON_COLLISION, ++ VEND1_LED_REG_A_EVENT_BLINK_TX, ++ VEND1_LED_REG_A_EVENT_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX, ++ VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT, ++ VEND1_LED_REG_A_EVENT_ON_NG_BLINK_FE_GE, ++ VEND1_LED_REG_A_EVENT_ON_FD_BLINK_COLLISION, ++ VEND1_LED_REG_A_EVENT_ON, ++ VEND1_LED_REG_A_EVENT_OFF, ++}; ++ ++struct as21xxx_led_pattern_info { ++ unsigned int pattern; ++ u16 val; ++}; ++ ++struct as21xxx_priv { ++ bool parity_status; ++ /* Protect concurrent IPC access */ ++ struct mutex ipc_lock; ++}; ++ ++static struct as21xxx_led_pattern_info as21xxx_led_supported_pattern[] = { ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10), ++ .val = VEND1_LED_REG_A_EVENT_ON_10 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_100), ++ .val = VEND1_LED_REG_A_EVENT_ON_100 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_1000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500), ++ .val = VEND1_LED_REG_A_EVENT_ON_2500 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_5000), ++ .val = VEND1_LED_REG_A_EVENT_ON_5000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_10000 ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_FULL_DUPLEX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FULL_DUPLEX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_TX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_LINK_BLINK_RX ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_10) | ++ BIT(TRIGGER_NETDEV_LINK_100) | ++ BIT(TRIGGER_NETDEV_LINK_1000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_FE_GE_BLINK_ACT ++ }, ++ { ++ .pattern = BIT(TRIGGER_NETDEV_LINK_2500) | ++ BIT(TRIGGER_NETDEV_LINK_5000) | ++ BIT(TRIGGER_NETDEV_LINK_10000) | ++ BIT(TRIGGER_NETDEV_TX) | ++ BIT(TRIGGER_NETDEV_RX), ++ .val = VEND1_LED_REG_A_EVENT_ON_NG_BLINK_ACT ++ } ++}; ++ ++static int aeon_firmware_boot(struct phy_device *phydev, const u8 *data, ++ size_t size) ++{ ++ int i, ret; ++ u16 val; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_LOAD); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_FW_START_ADDR, ++ AEON_BOOT_ADDR); ++ if (ret) ++ return ret; ++ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_ADDRCMD, ++ 0x3ffc, 0xc000); ++ if (ret) ++ return ret; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_STATUS); ++ if (val > 1) { ++ phydev_err(phydev, "wrong origin mdio_indirect_status: %x\n", val); ++ return -EINVAL; ++ } ++ ++ /* Firmware is always aligned to u16 */ ++ for (i = 0; i < size; i += 2) { ++ val = data[i + 1] << 8 | data[i]; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_MDIO_INDIRECT_LOAD, val); ++ if (ret) ++ return ret; ++ } ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_LO_BASEADDR, ++ lower_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_RESET_ADDR_HI_BASEADDR, ++ upper_16_bits(AEON_CPU_BOOT_ADDR)); ++ if (ret) ++ return ret; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLB_REG_CPU_CTRL, ++ VEND1_GLB_CPU_CTRL_MASK, AEON_CPU_CTRL_FW_START); ++} ++ ++static int aeon_firmware_load(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ const struct firmware *fw; ++ const char *fw_name; ++ int ret; ++ ++ ret = of_property_read_string(dev->of_node, "firmware-name", ++ &fw_name); ++ if (ret) ++ return ret; ++ ++ ret = request_firmware(&fw, fw_name, dev); ++ if (ret) { ++ phydev_err(phydev, "failed to find FW file %s (%d)\n", ++ fw_name, ret); ++ return ret; ++ } ++ ++ ret = aeon_firmware_boot(phydev, fw->data, fw->size); ++ ++ release_firmware(fw); ++ ++ return ret; ++} ++ ++static bool aeon_ipc_ready(u16 val, bool parity_status) ++{ ++ u16 status; ++ ++ if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status) ++ return false; ++ ++ status = val & AEON_IPC_STS_STATUS; ++ ++ return status != AEON_IPC_STS_STATUS_RCVD && ++ status != AEON_IPC_STS_STATUS_PROCESS && ++ status != AEON_IPC_STS_STATUS_BUSY; ++} ++ ++static int aeon_ipc_wait_cmd(struct phy_device *phydev, bool parity_status) ++{ ++ u16 val; ++ ++ /* Exit condition logic: ++ * - Wait for parity bit equal ++ * - Wait for status success, error OR ready ++ */ ++ return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val, ++ aeon_ipc_ready(val, parity_status), ++ AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false); ++} ++ ++static int aeon_ipc_send_cmd(struct phy_device *phydev, ++ struct as21xxx_priv *priv, ++ u16 cmd, u16 *ret_sts) ++{ ++ bool curr_parity; ++ int ret; ++ ++ /* The IPC sync by using a single parity bit. ++ * Each CMD have alternately this bit set or clear ++ * to understand correct flow and packet order. ++ */ ++ curr_parity = priv->parity_status; ++ if (priv->parity_status) ++ cmd |= AEON_IPC_CMD_PARITY; ++ ++ /* Always update parity for next packet */ ++ priv->parity_status = !priv->parity_status; ++ ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd); ++ if (ret) ++ return ret; ++ ++ /* Wait for packet to be processed */ ++ usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000); ++ ++ /* With no ret_sts, ignore waiting for packet completion ++ * (ipc parity bit sync) ++ */ ++ if (!ret_sts) ++ return 0; ++ ++ ret = aeon_ipc_wait_cmd(phydev, curr_parity); ++ if (ret) ++ return ret; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS); ++ if (ret < 0) ++ return ret; ++ ++ *ret_sts = ret; ++ if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* If data is NULL, return 0 or negative error. ++ * If data not NULL, return number of Bytes received from IPC or ++ * a negative error. ++ */ ++static int aeon_ipc_send_msg(struct phy_device *phydev, ++ u16 opcode, u16 *data, unsigned int data_len, ++ u16 *ret_data) ++{ ++ struct as21xxx_priv *priv = phydev->priv; ++ unsigned int ret_size; ++ u16 cmd, ret_sts; ++ int ret; ++ int i; ++ ++ /* IPC have a max of 8 register to transfer data, ++ * make sure we never exceed this. ++ */ ++ if (data_len > AEON_IPC_DATA_MAX) ++ return -EINVAL; ++ ++ for (i = 0; i < data_len / sizeof(u16); i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i), ++ data[i]); ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode); ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ ret = aeon_ipc_send_cmd(phydev, priv, cmd, &ret_sts); ++ if (ret) { ++ phydev_err(phydev, "failed to send ipc msg for %x: %d\n", ++ opcode, ret); ++ goto out; ++ } ++ ++ if (!data) ++ goto out; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Prevent IPC from stack smashing the kernel. ++ * We can't trust IPC to return a good value and we always ++ * preallocate space for 16 Bytes. ++ */ ++ ret_size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts); ++ if (ret_size > AEON_IPC_DATA_MAX) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* Read data from IPC data register for ret_size value from IPC */ ++ for (i = 0; i < DIV_ROUND_UP(ret_size, sizeof(u16)); i++) { ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i)); ++ if (ret < 0) ++ goto out; ++ ++ ret_data[i] = ret; ++ } ++ ++ ret = ret_size; ++ ++out: ++ mutex_unlock(&priv->ipc_lock); ++ ++ return ret; ++} ++ ++static int aeon_ipc_noop(struct phy_device *phydev, ++ struct as21xxx_priv *priv, u16 *ret_sts) ++{ ++ u16 cmd; ++ ++ cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, 0) | ++ FIELD_PREP(AEON_IPC_CMD_OPCODE, IPC_CMD_NOOP); ++ ++ return aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts); ++} ++ ++/* Logic to sync parity bit with IPC. ++ * We send 2 NOP cmd with same partity and we wait for IPC ++ * to handle the packet only for the second one. This way ++ * we make sure we are sync for every next cmd. ++ */ ++static int aeon_ipc_sync_parity(struct phy_device *phydev, ++ struct as21xxx_priv *priv) ++{ ++ u16 ret_sts; ++ int ret; ++ ++ mutex_lock(&priv->ipc_lock); ++ ++ /* Send NOP with no parity */ ++ aeon_ipc_noop(phydev, priv, NULL); ++ ++ /* Reset packet parity */ ++ priv->parity_status = false; ++ ++ /* Send second NOP with no parity */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ ++ mutex_unlock(&priv->ipc_lock); ++ ++ /* We expect to return -EINVAL */ ++ if (ret != -EINVAL) ++ return ret; ++ ++ if ((ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_READY) { ++ phydev_err(phydev, "Invalid IPC status on sync parity: %x\n", ++ ret_sts); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int aeon_ipc_get_fw_version(struct phy_device *phydev) ++{ ++ u16 ret_data[AEON_IPC_DATA_NUM_REGISTERS], data[1]; ++ char fw_version[AEON_IPC_DATA_MAX + 1]; ++ int ret; ++ ++ data[0] = IPC_INFO_VERSION; ++ ++ ret = aeon_ipc_send_msg(phydev, IPC_CMD_INFO, data, ++ sizeof(data), ret_data); ++ if (ret < 0) ++ return ret; ++ ++ /* Make sure FW version is NULL terminated */ ++ memcpy(fw_version, ret_data, ret); ++ fw_version[ret] = '\0'; ++ ++ phydev_info(phydev, "Firmware Version: %s\n", fw_version); ++ ++ return 0; ++} ++ ++static int aeon_dpc_ra_enable(struct phy_device *phydev) ++{ ++ u16 data[2]; ++ ++ data[0] = IPC_CFG_PARAM_DIRECT; ++ data[1] = IPC_CFG_PARAM_DIRECT_DPC_RA; ++ ++ return aeon_ipc_send_msg(phydev, IPC_CMD_CFG_PARAM, data, ++ sizeof(data), NULL); ++} ++ ++static int as21xxx_probe(struct phy_device *phydev) ++{ ++ struct as21xxx_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(&phydev->mdio.dev, ++ sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ phydev->priv = priv; ++ ++ ret = devm_mutex_init(&phydev->mdio.dev, ++ &priv->ipc_lock); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ return ret; ++ ++ ret = aeon_ipc_get_fw_version(phydev); ++ if (ret) ++ return ret; ++ ++ /* Enable PTP clk if not already Enabled */ ++ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PTP_CLK, ++ VEND1_PTP_CLK_EN); ++ if (ret) ++ return ret; ++ ++ return aeon_dpc_ra_enable(phydev); ++} ++ ++static int as21xxx_read_link(struct phy_device *phydev, int *bmcr) ++{ ++ int status; ++ ++ /* Normal C22 BMCR report inconsistent data, use ++ * the mapped C22 in C45 to have more consistent link info. ++ */ ++ *bmcr = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_BMCR); ++ if (*bmcr < 0) ++ return *bmcr; ++ ++ /* Autoneg is being started, therefore disregard current ++ * link status and report link as down. ++ */ ++ if (*bmcr & BMCR_ANRESTART) { ++ phydev->link = 0; ++ return 0; ++ } ++ ++ status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); ++ if (status < 0) ++ return status; ++ ++ phydev->link = !!(status & MDIO_STAT1_LSTATUS); ++ ++ return 0; ++} ++ ++static int as21xxx_read_c22_lpa(struct phy_device *phydev) ++{ ++ int lpagb; ++ ++ /* MII_STAT1000 are only filled in the mapped C22 ++ * in C45, use that to fill lpagb values and check. ++ */ ++ lpagb = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_STAT1000); ++ if (lpagb < 0) ++ return lpagb; ++ ++ if (lpagb & LPA_1000MSFAIL) { ++ int adv = phy_read_mmd(phydev, MDIO_MMD_AN, ++ AS21XXX_MDIO_AN_C22 + MII_CTRL1000); ++ ++ if (adv < 0) ++ return adv; ++ ++ if (adv & CTL1000_ENABLE_MASTER) ++ phydev_err(phydev, "Master/Slave resolution failed, maybe conflicting manual settings?\n"); ++ else ++ phydev_err(phydev, "Master/Slave resolution failed\n"); ++ return -ENOLINK; ++ } ++ ++ mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ++ lpagb); ++ ++ return 0; ++} ++ ++static int as21xxx_read_status(struct phy_device *phydev) ++{ ++ int bmcr, old_link = phydev->link; ++ int ret; ++ ++ ret = as21xxx_read_link(phydev, &bmcr); ++ if (ret) ++ return ret; ++ ++ /* why bother the PHY if nothing can have changed */ ++ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) ++ return 0; ++ ++ phydev->speed = SPEED_UNKNOWN; ++ phydev->duplex = DUPLEX_UNKNOWN; ++ phydev->pause = 0; ++ phydev->asym_pause = 0; ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ ret = genphy_c45_read_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ ret = as21xxx_read_c22_lpa(phydev); ++ if (ret) ++ return ret; ++ ++ phy_resolve_aneg_linkmode(phydev); ++ } else { ++ int speed; ++ ++ linkmode_zero(phydev->lp_advertising); ++ ++ speed = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_SPEED_STATUS); ++ if (speed < 0) ++ return speed; ++ ++ switch (speed & VEND1_SPEED_STATUS) { ++ case VEND1_SPEED_10000: ++ phydev->speed = SPEED_10000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_5000: ++ phydev->speed = SPEED_5000; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_2500: ++ phydev->speed = SPEED_2500; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_1000: ++ phydev->speed = SPEED_1000; ++ if (bmcr & BMCR_FULLDPLX) ++ phydev->duplex = DUPLEX_FULL; ++ else ++ phydev->duplex = DUPLEX_HALF; ++ break; ++ case VEND1_SPEED_100: ++ phydev->speed = SPEED_100; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ case VEND1_SPEED_10: ++ phydev->speed = SPEED_10; ++ phydev->duplex = DUPLEX_FULL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int as21xxx_led_brightness_set(struct phy_device *phydev, ++ u8 index, enum led_brightness value) ++{ ++ u16 val = VEND1_LED_REG_A_EVENT_OFF; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ if (value) ++ val = VEND1_LED_REG_A_EVENT_ON; ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_hw_is_supported(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) ++ return 0; ++ ++ return -EOPNOTSUPP; ++} ++ ++static int as21xxx_led_hw_control_get(struct phy_device *phydev, u8 index, ++ unsigned long *rules) ++{ ++ int i, val; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_LED_REG(index)); ++ if (val < 0) ++ return val; ++ ++ val = FIELD_GET(VEND1_LED_REG_A_EVENT, val); ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (val == as21xxx_led_supported_pattern[i].val) { ++ *rules = as21xxx_led_supported_pattern[i].pattern; ++ return 0; ++ } ++ ++ /* Should be impossible */ ++ return -EINVAL; ++} ++ ++static int as21xxx_led_hw_control_set(struct phy_device *phydev, u8 index, ++ unsigned long rules) ++{ ++ u16 val = 0; ++ int i; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(as21xxx_led_supported_pattern); i++) ++ if (rules == as21xxx_led_supported_pattern[i].pattern) { ++ val = as21xxx_led_supported_pattern[i].val; ++ break; ++ } ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_LED_REG(index), ++ VEND1_LED_REG_A_EVENT, ++ FIELD_PREP(VEND1_LED_REG_A_EVENT, val)); ++} ++ ++static int as21xxx_led_polarity_set(struct phy_device *phydev, int index, ++ unsigned long modes) ++{ ++ bool led_active_low = false; ++ u16 mask, val = 0; ++ u32 mode; ++ ++ if (index > AEON_MAX_LEDS) ++ return -EINVAL; ++ ++ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { ++ switch (mode) { ++ case PHY_LED_ACTIVE_LOW: ++ led_active_low = true; ++ break; ++ case PHY_LED_ACTIVE_HIGH: /* default mode */ ++ led_active_low = false; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ mask = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ if (led_active_low) ++ val = VEND1_GLB_CPU_CTRL_LED_POLARITY(index); ++ ++ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ VEND1_GLB_REG_CPU_CTRL, ++ mask, val); ++} ++ ++static int as21xxx_match_phy_device(struct phy_device *phydev, ++ const struct phy_driver *phydrv) ++{ ++ struct as21xxx_priv *priv; ++ u16 ret_sts; ++ u32 phy_id; ++ int ret; ++ ++ /* Skip PHY that are not AS21xxx or already have firmware loaded */ ++ if (phydev->c45_ids.device_ids[MDIO_MMD_PCS] != PHY_ID_AS21XXX) ++ return genphy_match_phy_device(phydev, (struct phy_driver *)phydrv); ++ ++ /* Read PHY ID to handle firmware just loaded */ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID1); ++ if (ret < 0) ++ return ret; ++ phy_id = ret << 16; ++ ++ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_PHYSID2); ++ if (ret < 0) ++ return ret; ++ phy_id |= ret; ++ ++ /* With PHY ID not the generic AS21xxx one assume ++ * the firmware just loaded ++ */ ++ if (phy_id != PHY_ID_AS21XXX) ++ return phy_id == phydrv->phy_id; ++ ++ /* Allocate temp priv and load the firmware */ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->ipc_lock); ++ ++ ret = aeon_firmware_load(phydev); ++ if (ret) ++ goto out; ++ ++ /* Sync parity... */ ++ ret = aeon_ipc_sync_parity(phydev, priv); ++ if (ret) ++ goto out; ++ ++ /* ...and send a third NOOP cmd to wait for firmware finish loading */ ++ ret = aeon_ipc_noop(phydev, priv, &ret_sts); ++ if (ret) ++ goto out; ++ ++out: ++ mutex_destroy(&priv->ipc_lock); ++ kfree(priv); ++ ++ /* Return can either be 0 or a negative error code. ++ * Returning 0 here means THIS is NOT a suitable PHY. ++ * ++ * For the specific case of the generic Aeonsemi PHY ID that ++ * needs the firmware the be loaded first to have a correct PHY ID, ++ * this is OK as a matching PHY ID will be found right after. ++ * This relies on the driver probe order where the first PHY driver ++ * probed is the generic one. ++ */ ++ return ret; ++} ++ ++static struct phy_driver as21xxx_drivers[] = { ++ { ++ /* PHY expose in C45 as 0x7500 0x9410 ++ * before firmware is loaded. ++ * This driver entry must be attempted first to load ++ * the firmware and thus update the ID registers. ++ */ ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21XXX), ++ .name = "Aeonsemi AS21xxx", ++ .match_phy_device = as21xxx_match_phy_device, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011JB1), ++ .name = "Aeonsemi AS21011JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21011PB1), ++ .name = "Aeonsemi AS21011PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010PB1), ++ .name = "Aeonsemi AS21010PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21010JB1), ++ .name = "Aeonsemi AS21010JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210PB1), ++ .name = "Aeonsemi AS21210PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510JB1), ++ .name = "Aeonsemi AS21510JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21510PB1), ++ .name = "Aeonsemi AS21510PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511JB1), ++ .name = "Aeonsemi AS21511JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21210JB1), ++ .name = "Aeonsemi AS21210JB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(PHY_ID_AS21511PB1), ++ .name = "Aeonsemi AS21511PB1", ++ .probe = as21xxx_probe, ++ .match_phy_device = as21xxx_match_phy_device, ++ .read_status = as21xxx_read_status, ++ .led_brightness_set = as21xxx_led_brightness_set, ++ .led_hw_is_supported = as21xxx_led_hw_is_supported, ++ .led_hw_control_set = as21xxx_led_hw_control_set, ++ .led_hw_control_get = as21xxx_led_hw_control_get, ++ .led_polarity_set = as21xxx_led_polarity_set, ++ }, ++}; ++module_phy_driver(as21xxx_drivers); ++ ++static struct mdio_device_id __maybe_unused as21xxx_tbl[] = { ++ { PHY_ID_MATCH_VENDOR(PHY_VENDOR_AEONSEMI) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mdio, as21xxx_tbl); ++ ++MODULE_DESCRIPTION("Aeonsemi AS21xxx PHY driver"); ++MODULE_AUTHOR("Christian Marangi "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch b/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch index 5b627cf44975c2..ecea0e987d5b12 100644 --- a/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch +++ b/target/linux/generic/backport-6.6/798-v6.10-01-net-phy-air_en8811h-Add-the-Airoha-EN8811H-PHY-drive.patch @@ -27,9 +27,9 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -68,6 +68,11 @@ config SFP - - comment "MII PHY device drivers" +@@ -80,6 +80,11 @@ config AS21XXX_PHY + AS21210PB1 that all register with the PHY ID 0x7500 0x7500 + before the firmware is loaded. +config AIR_EN8811H_PHY + tristate "Airoha EN8811H 2.5 Gigabit PHY" @@ -48,7 +48,7 @@ Signed-off-by: Jakub Kicinski +obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o obj-$(CONFIG_AMD_PHY) += amd.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia/ - obj-$(CONFIG_AX88796B_PHY) += ax88796b.o + obj-$(CONFIG_AS21XXX_PHY) += as21xxx.o --- /dev/null +++ b/drivers/net/phy/air_en8811h.c @@ -0,0 +1,1086 @@ diff --git a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch index 07287206f698b3..1ed1008ee53755 100644 --- a/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch +++ b/target/linux/generic/backport-6.6/832-v6.7-net-phy-amd-Support-the-Altima-AMI101L.patch @@ -16,7 +16,7 @@ Signed-off-by: Linus Walleij --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -74,9 +74,9 @@ config AIR_EN8811H_PHY +@@ -86,9 +86,9 @@ config AIR_EN8811H_PHY Currently supports the Airoha EN8811H PHY. config AMD_PHY diff --git a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch index 54932436c747c4..4d8742f0e3329e 100644 --- a/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch +++ b/target/linux/generic/backport-6.6/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch @@ -28,7 +28,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -3204,6 +3204,7 @@ static int of_phy_led(struct phy_device +@@ -3226,6 +3226,7 @@ static int of_phy_led(struct phy_device struct device *dev = &phydev->mdio.dev; struct led_init_data init_data = {}; struct led_classdev *cdev; @@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski struct phy_led *phyled; u32 index; int err; -@@ -3221,6 +3222,21 @@ static int of_phy_led(struct phy_device +@@ -3243,6 +3244,21 @@ static int of_phy_led(struct phy_device if (index > U8_MAX) return -EINVAL; @@ -76,7 +76,7 @@ Signed-off-by: Jakub Kicinski /** * struct phy_driver - Driver structure for a particular PHY type * -@@ -1143,6 +1152,19 @@ struct phy_driver { +@@ -1144,6 +1153,19 @@ struct phy_driver { int (*led_hw_control_get)(struct phy_device *dev, u8 index, unsigned long *rules); diff --git a/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch b/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch index 3d8a15bd1e0869..1b979f8662a484 100644 --- a/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch +++ b/target/linux/generic/backport-6.6/841-v6.13-net-phy-support-active-high-property-for-PHY-LEDs.patch @@ -19,7 +19,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -3222,11 +3222,17 @@ static int of_phy_led(struct phy_device +@@ -3244,11 +3244,17 @@ static int of_phy_led(struct phy_device if (index > U8_MAX) return -EINVAL; diff --git a/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch b/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch index b86dbea898524c..43e2c92ef009da 100644 --- a/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch +++ b/target/linux/generic/backport-6.6/895-01-v6.8-net-phy-add-possible-interfaces.patch @@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -1247,6 +1247,8 @@ int phy_init_hw(struct phy_device *phyde +@@ -1269,6 +1269,8 @@ int phy_init_hw(struct phy_device *phyde if (ret < 0) return ret; diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12 index 261166c6c3228c..dc836ccb106e0e 100644 --- a/target/linux/generic/config-6.12 +++ b/target/linux/generic/config-6.12 @@ -458,6 +458,7 @@ CONFIG_ARM_MODULE_PLTS=y # CONFIG_ARM_TIMER_SP804 is not set # CONFIG_ARM_UNWIND is not set # CONFIG_ARM_VIRT_EXT is not set +# CONFIG_AS21XXX_PHY is not set # CONFIG_AS3935 is not set # CONFIG_AS73211 is not set # CONFIG_ASM9260_TIMER is not set diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6 index a7d06efed3d923..be73510dd02e19 100644 --- a/target/linux/generic/config-6.6 +++ b/target/linux/generic/config-6.6 @@ -440,6 +440,7 @@ CONFIG_ARM_MODULE_PLTS=y # CONFIG_ARM_TIMER_SP804 is not set # CONFIG_ARM_UNWIND is not set # CONFIG_ARM_VIRT_EXT is not set +# CONFIG_AS21XXX_PHY is not set # CONFIG_AS3935 is not set # CONFIG_AS73211 is not set # CONFIG_ASM9260_TIMER is not set diff --git a/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch index 4591a42f782714..9b1f53af3e4a07 100644 --- a/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch +++ b/target/linux/generic/hack-6.12/700-swconfig_switch_drivers.patch @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau + comment "MII PHY device drivers" - config AIR_EN8811H_PHY + config AS21XXX_PHY --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -27,6 +27,21 @@ libphy-$(CONFIG_OPEN_ALLIANCE_HELPERS) + diff --git a/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch index 616db258906a5f..cfcc72066218ca 100644 --- a/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch +++ b/target/linux/generic/hack-6.12/735-net-phy-realtek-rtl8261n.patch @@ -1,6 +1,6 @@ --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -419,6 +419,8 @@ config QSEMI_PHY +@@ -431,6 +431,8 @@ config QSEMI_PHY source "drivers/net/phy/realtek/Kconfig" @@ -11,7 +11,7 @@ help --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -110,6 +110,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja +@@ -111,6 +111,7 @@ obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja obj-y += qcom/ obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek/ diff --git a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch index 4428ebbb5adda8..59cc16cb753dce 100644 --- a/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch +++ b/target/linux/generic/hack-6.6/700-swconfig_switch_drivers.patch @@ -92,7 +92,7 @@ Signed-off-by: Felix Fietkau + comment "MII PHY device drivers" - config AIR_EN8811H_PHY + config AS21XXX_PHY --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -26,6 +26,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_ diff --git a/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch b/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch index 946869c3037377..06f508d95b9dbe 100644 --- a/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch +++ b/target/linux/generic/hack-6.6/735-net-phy-realtek-rtl8261n.patch @@ -1,6 +1,6 @@ --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -406,6 +406,8 @@ config QSEMI_PHY +@@ -403,6 +403,8 @@ config QSEMI_PHY source "drivers/net/phy/realtek/Kconfig" diff --git a/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch b/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch index 3405d5c535b6c9..7d8b21f508fdd6 100644 --- a/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch +++ b/target/linux/generic/hack-6.6/766-net-phy-mediatek-ge-add-LED-configuration-interface.patch @@ -12,18 +12,18 @@ plans on integrating their own framework for handling these LEDs. Signed-off-by: David Bauer --- - drivers/net/phy/mediatek-ge.c | 33 +++++++++++++++++++++++++++++++++ + drivers/net/phy/mediatek/mtk-ge.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) ---- a/drivers/net/phy/mediatek-ge.c -+++ b/drivers/net/phy/mediatek-ge.c +--- a/drivers/net/phy/mediatek/mtk-ge.c ++++ b/drivers/net/phy/mediatek/mtk-ge.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0+ +#include #include #include #include -@@ -53,6 +54,36 @@ static int mt7530_phy_config_init(struct +@@ -76,6 +77,36 @@ static int mt7530_phy_config_init(struct return 0; } @@ -60,9 +60,9 @@ Signed-off-by: David Bauer static int mt7531_phy_config_init(struct phy_device *phydev) { mtk_gephy_config_init(phydev); -@@ -65,6 +96,9 @@ static int mt7531_phy_config_init(struct - phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x13, 0x404); - phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x14, 0x404); +@@ -96,6 +127,9 @@ static int mt7531_phy_config_init(struct + FIELD_PREP(MTK_TX_DELAY_PAIR_B_MASK, 0x4) | + FIELD_PREP(MTK_TX_DELAY_PAIR_D_MASK, 0x4)); + /* LED Config*/ + mt7530_led_config_of(phydev); diff --git a/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch index b8d20d86104a01..ed6c9070b24bca 100644 --- a/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch +++ b/target/linux/generic/pending-6.12/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -2015,6 +2015,9 @@ void phy_detach(struct phy_device *phyde +@@ -2037,6 +2037,9 @@ void phy_detach(struct phy_device *phyde phydev->devlink = NULL; } diff --git a/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch index 9bb5737b396757..a4cb71b83b2b0f 100644 --- a/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch +++ b/target/linux/generic/pending-6.12/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch @@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -1666,6 +1666,7 @@ static struct phy_driver realtek_drvs[] +@@ -1675,6 +1675,7 @@ static struct phy_driver realtek_drvs[] }, { .name = "RTL8226 2.5Gbps PHY", .match_phy_device = rtl8226_match_phy_device, @@ -23,7 +23,7 @@ Signed-off-by: Daniel Golle .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .read_status = rtl822x_read_status, -@@ -1676,6 +1677,7 @@ static struct phy_driver realtek_drvs[] +@@ -1685,6 +1686,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_match_phy_device, .name = "RTL8226B_RTL8221B 2.5Gbps PHY", @@ -31,7 +31,7 @@ Signed-off-by: Daniel Golle .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .config_init = rtl822xb_config_init, -@@ -1698,6 +1700,7 @@ static struct phy_driver realtek_drvs[] +@@ -1707,6 +1709,7 @@ static struct phy_driver realtek_drvs[] }, { PHY_ID_MATCH_EXACT(0x001cc848), .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", @@ -39,7 +39,7 @@ Signed-off-by: Daniel Golle .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .config_init = rtl822xb_config_init, -@@ -1710,6 +1713,7 @@ static struct phy_driver realtek_drvs[] +@@ -1719,6 +1722,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", @@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle .probe = rtl822x_probe, .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, -@@ -1723,6 +1727,7 @@ static struct phy_driver realtek_drvs[] +@@ -1732,6 +1736,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", @@ -55,7 +55,7 @@ Signed-off-by: Daniel Golle .probe = rtl822x_probe, .config_init = rtl822xb_config_init, .get_rate_matching = rtl822xb_get_rate_matching, -@@ -1734,6 +1739,7 @@ static struct phy_driver realtek_drvs[] +@@ -1743,6 +1748,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", @@ -63,7 +63,7 @@ Signed-off-by: Daniel Golle .probe = rtl822x_probe, .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, -@@ -1747,6 +1753,7 @@ static struct phy_driver realtek_drvs[] +@@ -1756,6 +1762,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", diff --git a/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch index 4e07882b1ec70a..186b3ff2b9ac4a 100644 --- a/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.12/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle Signed-off-by: Mieczyslaw Nalewaj --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -1398,10 +1398,32 @@ static int rtl8226_match_phy_device(stru +@@ -1400,10 +1400,32 @@ static int rtl8226_match_phy_device(stru static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, bool is_c45) { @@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj + } } - static int rtl8221b_match_phy_device(struct phy_device *phydev) + static int rtl8221b_match_phy_device(struct phy_device *phydev, diff --git a/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch index 855ea41c8cebf9..e908af055f39ae 100644 --- a/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.12/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch @@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -1610,6 +1610,51 @@ static irqreturn_t rtl9000a_handle_inter +@@ -1619,6 +1619,51 @@ static irqreturn_t rtl9000a_handle_inter return IRQ_HANDLED; } @@ -64,7 +64,7 @@ Signed-off-by: Jianhui Zhao static struct phy_driver realtek_drvs[] = { { PHY_ID_MATCH_EXACT(0x00008201), -@@ -1774,6 +1819,8 @@ static struct phy_driver realtek_drvs[] +@@ -1783,6 +1828,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", @@ -73,7 +73,7 @@ Signed-off-by: Jianhui Zhao .soft_reset = genphy_soft_reset, .probe = rtl822x_probe, .get_features = rtl822x_get_features, -@@ -1788,6 +1835,8 @@ static struct phy_driver realtek_drvs[] +@@ -1797,6 +1844,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", @@ -82,7 +82,7 @@ Signed-off-by: Jianhui Zhao .soft_reset = rtl822x_c45_soft_reset, .probe = rtl822x_probe, .config_init = rtl822xb_config_init, -@@ -1800,6 +1849,8 @@ static struct phy_driver realtek_drvs[] +@@ -1809,6 +1858,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", @@ -91,7 +91,7 @@ Signed-off-by: Jianhui Zhao .soft_reset = genphy_soft_reset, .probe = rtl822x_probe, .get_features = rtl822x_get_features, -@@ -1814,6 +1865,8 @@ static struct phy_driver realtek_drvs[] +@@ -1823,6 +1874,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", diff --git a/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch index e451d13bd8fdb5..d4920b5c97a848 100644 --- a/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch +++ b/target/linux/generic/pending-6.12/720-08-net-phy-realtek-work-around-broken-serdes.patch @@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle static int rtl822xb_get_rate_matching(struct phy_device *phydev, phy_interface_t iface) { -@@ -1842,7 +1858,7 @@ static struct phy_driver realtek_drvs[] +@@ -1851,7 +1867,7 @@ static struct phy_driver realtek_drvs[] .handle_interrupt = rtl8221b_handle_interrupt, .soft_reset = rtl822x_c45_soft_reset, .probe = rtl822x_probe, @@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle .get_rate_matching = rtl822xb_get_rate_matching, .get_features = rtl822x_c45_get_features, .config_aneg = rtl822x_c45_config_aneg, -@@ -1872,7 +1888,7 @@ static struct phy_driver realtek_drvs[] +@@ -1881,7 +1897,7 @@ static struct phy_driver realtek_drvs[] .handle_interrupt = rtl8221b_handle_interrupt, .soft_reset = rtl822x_c45_soft_reset, .probe = rtl822x_probe, diff --git a/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch index f5621e34ee874e..7a52bd552132ba 100644 --- a/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch +++ b/target/linux/generic/pending-6.12/741-net-phy-broadcom-update-dependency-condition.patch @@ -24,7 +24,7 @@ Signed-off-by: David Bauer --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -139,7 +139,7 @@ config BROADCOM_PHY +@@ -151,7 +151,7 @@ config BROADCOM_PHY tristate "Broadcom 54XX PHYs" select BCM_NET_PHYLIB select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING diff --git a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch index 453abe65c64014..efa76572f86e74 100644 --- a/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch +++ b/target/linux/generic/pending-6.6/703-phy-add-detach-callback-to-struct-phy_driver.patch @@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -1912,6 +1912,9 @@ void phy_detach(struct phy_device *phyde +@@ -1934,6 +1934,9 @@ void phy_detach(struct phy_device *phyde phydev->devlink = NULL; } diff --git a/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch b/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch index 1becba6da6f73a..deb6506186e0bb 100644 --- a/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch +++ b/target/linux/generic/pending-6.6/720-01-net-phy-realtek-use-genphy_soft_reset-for-2.5G-PHYs.patch @@ -15,7 +15,7 @@ Signed-off-by: Daniel Golle --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -1638,6 +1638,7 @@ static struct phy_driver realtek_drvs[] +@@ -1647,6 +1647,7 @@ static struct phy_driver realtek_drvs[] }, { .name = "RTL8226 2.5Gbps PHY", .match_phy_device = rtl8226_match_phy_device, @@ -23,7 +23,7 @@ Signed-off-by: Daniel Golle .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .read_status = rtl822x_read_status, -@@ -1648,6 +1649,7 @@ static struct phy_driver realtek_drvs[] +@@ -1657,6 +1658,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_match_phy_device, .name = "RTL8226B_RTL8221B 2.5Gbps PHY", @@ -31,7 +31,7 @@ Signed-off-by: Daniel Golle .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .config_init = rtl822xb_config_init, -@@ -1660,6 +1662,7 @@ static struct phy_driver realtek_drvs[] +@@ -1669,6 +1671,7 @@ static struct phy_driver realtek_drvs[] }, { PHY_ID_MATCH_EXACT(0x001cc838), .name = "RTL8226-CG 2.5Gbps PHY", @@ -39,7 +39,7 @@ Signed-off-by: Daniel Golle .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .read_status = rtl822x_read_status, -@@ -1670,6 +1673,7 @@ static struct phy_driver realtek_drvs[] +@@ -1679,6 +1682,7 @@ static struct phy_driver realtek_drvs[] }, { PHY_ID_MATCH_EXACT(0x001cc848), .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", @@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, .config_init = rtl822xb_config_init, -@@ -1682,6 +1686,7 @@ static struct phy_driver realtek_drvs[] +@@ -1691,6 +1695,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", @@ -55,7 +55,7 @@ Signed-off-by: Daniel Golle .probe = rtl822x_probe, .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, -@@ -1695,6 +1700,7 @@ static struct phy_driver realtek_drvs[] +@@ -1704,6 +1709,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", @@ -63,7 +63,7 @@ Signed-off-by: Daniel Golle .probe = rtl822x_probe, .config_init = rtl822xb_config_init, .get_rate_matching = rtl822xb_get_rate_matching, -@@ -1706,6 +1712,7 @@ static struct phy_driver realtek_drvs[] +@@ -1715,6 +1721,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", @@ -71,7 +71,7 @@ Signed-off-by: Daniel Golle .probe = rtl822x_probe, .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, -@@ -1719,6 +1726,7 @@ static struct phy_driver realtek_drvs[] +@@ -1728,6 +1735,7 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", diff --git a/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch index 0918794d87e265..2886babe57e89c 100644 --- a/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.6/720-05-net-phy-realtek-detect-early-version-of-RTL8221B.patch @@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle Signed-off-by: Mieczyslaw Nalewaj --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -1368,10 +1368,32 @@ static int rtl8226_match_phy_device(stru +@@ -1370,10 +1370,32 @@ static int rtl8226_match_phy_device(stru static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, bool is_c45) { @@ -49,4 +49,4 @@ Signed-off-by: Mieczyslaw Nalewaj + } } - static int rtl8221b_match_phy_device(struct phy_device *phydev) + static int rtl8221b_match_phy_device(struct phy_device *phydev, diff --git a/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch b/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch index 9afe8baca6614a..29610d2767b3db 100644 --- a/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch +++ b/target/linux/generic/pending-6.6/720-06-net-phy-realtek-support-interrupt-of-RTL8221B.patch @@ -12,7 +12,7 @@ Signed-off-by: Jianhui Zhao --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c -@@ -1580,6 +1580,51 @@ static irqreturn_t rtl9000a_handle_inter +@@ -1589,6 +1589,51 @@ static irqreturn_t rtl9000a_handle_inter return IRQ_HANDLED; } @@ -64,7 +64,7 @@ Signed-off-by: Jianhui Zhao static struct phy_driver realtek_drvs[] = { { PHY_ID_MATCH_EXACT(0x00008201), -@@ -1745,6 +1790,8 @@ static struct phy_driver realtek_drvs[] +@@ -1754,6 +1799,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", @@ -73,7 +73,7 @@ Signed-off-by: Jianhui Zhao .soft_reset = genphy_soft_reset, .probe = rtl822x_probe, .get_features = rtl822x_get_features, -@@ -1759,6 +1806,8 @@ static struct phy_driver realtek_drvs[] +@@ -1768,6 +1815,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", @@ -82,7 +82,7 @@ Signed-off-by: Jianhui Zhao .soft_reset = genphy_soft_reset, .probe = rtl822x_probe, .config_init = rtl822xb_config_init, -@@ -1771,6 +1820,8 @@ static struct phy_driver realtek_drvs[] +@@ -1780,6 +1829,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device, .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", @@ -91,7 +91,7 @@ Signed-off-by: Jianhui Zhao .soft_reset = genphy_soft_reset, .probe = rtl822x_probe, .get_features = rtl822x_get_features, -@@ -1785,6 +1836,8 @@ static struct phy_driver realtek_drvs[] +@@ -1794,6 +1845,8 @@ static struct phy_driver realtek_drvs[] }, { .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device, .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)", diff --git a/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch b/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch index 1749a74e361443..1b6978547db8e2 100644 --- a/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch +++ b/target/linux/generic/pending-6.6/720-08-net-phy-realtek-work-around-broken-serdes.patch @@ -38,7 +38,7 @@ Signed-off-by: Daniel Golle static int rtl822xb_get_rate_matching(struct phy_device *phydev, phy_interface_t iface) { -@@ -1813,7 +1829,7 @@ static struct phy_driver realtek_drvs[] +@@ -1822,7 +1838,7 @@ static struct phy_driver realtek_drvs[] .handle_interrupt = rtl8221b_handle_interrupt, .soft_reset = genphy_soft_reset, .probe = rtl822x_probe, @@ -47,7 +47,7 @@ Signed-off-by: Daniel Golle .get_rate_matching = rtl822xb_get_rate_matching, .get_features = rtl822x_c45_get_features, .config_aneg = rtl822x_c45_config_aneg, -@@ -1843,7 +1859,7 @@ static struct phy_driver realtek_drvs[] +@@ -1852,7 +1868,7 @@ static struct phy_driver realtek_drvs[] .handle_interrupt = rtl8221b_handle_interrupt, .soft_reset = genphy_soft_reset, .probe = rtl822x_probe, diff --git a/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch b/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch index 80b69920e9d46f..449b27e617a4f3 100644 --- a/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch +++ b/target/linux/generic/pending-6.6/741-net-phy-broadcom-update-dependency-condition.patch @@ -24,7 +24,7 @@ Signed-off-by: David Bauer --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -113,7 +113,7 @@ config BROADCOM_PHY +@@ -125,7 +125,7 @@ config BROADCOM_PHY tristate "Broadcom 54XX PHYs" select BCM_NET_PHYLIB select BCM_NET_PHYPTP if NETWORK_PHY_TIMESTAMPING diff --git a/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch b/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch index 72a1464966d3cd..8f911f6fd8f115 100644 --- a/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch +++ b/target/linux/mediatek/patches-6.12/500-gsw-rtl8367s-mt7622-support.patch @@ -1,6 +1,6 @@ --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -431,6 +431,12 @@ config ROCKCHIP_PHY +@@ -443,6 +443,12 @@ config ROCKCHIP_PHY help Currently supports the integrated Ethernet PHY. @@ -15,7 +15,7 @@ select CRC16 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -113,6 +113,7 @@ obj-$(CONFIG_REALTEK_PHY) += realtek/ +@@ -114,6 +114,7 @@ obj-$(CONFIG_REALTEK_PHY) += realtek/ obj-y += rtl8261n/ obj-$(CONFIG_RENESAS_PHY) += uPD60620.o obj-$(CONFIG_ROCKCHIP_PHY) += rockchip.o diff --git a/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch b/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch index 079351b7a22ccf..4ebaffd1dd1dfa 100644 --- a/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch +++ b/target/linux/mediatek/patches-6.12/734-net-phy-add-Airoha-EN8801SC-PHY.patch @@ -14,9 +14,9 @@ Signed-off-by: Robert Marko --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -153,6 +153,11 @@ endif # RTL8366_SMI - - comment "MII PHY device drivers" +@@ -165,6 +165,11 @@ config AS21XXX_PHY + AS21210PB1 that all register with the PHY ID 0x7500 0x7500 + before the firmware is loaded. +config AIROHA_EN8801SC_PHY + tristate "Airoha EN8801SC Gigabit PHY" diff --git a/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch b/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch index b0adf04a5bdc75..d31215ec27640b 100644 --- a/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch +++ b/target/linux/mediatek/patches-6.12/737-net-dsa-add-Airoha-AN8855.patch @@ -254,7 +254,7 @@ Christian Marangi (9): obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -158,6 +158,11 @@ config AIROHA_EN8801SC_PHY +@@ -170,6 +170,11 @@ config AIROHA_EN8801SC_PHY help Currently supports the Airoha EN8801SC PHY. diff --git a/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch b/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch index 632aad0ed29449..12638978b3e5cd 100644 --- a/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch +++ b/target/linux/mediatek/patches-6.12/739-net-add-negotiation-of-in-band-capabilities.patch @@ -880,7 +880,7 @@ publishing the in-band capabilities from the BCM84881 PHY driver. * @get_rate_matching: Get the supported type of rate matching for a * particular phy interface. This is used by phy consumers to determine * whether to advertise lower-speed modes for that interface. It is -@@ -1839,6 +1870,9 @@ int phy_config_aneg(struct phy_device *p +@@ -1840,6 +1871,9 @@ int phy_config_aneg(struct phy_device *p int _phy_start_aneg(struct phy_device *phydev); int phy_start_aneg(struct phy_device *phydev); int phy_aneg_done(struct phy_device *phydev); diff --git a/target/linux/realtek/base-files/etc/board.d/02_network b/target/linux/realtek/base-files/etc/board.d/02_network index dd2456d9dc10bc..0586e9e3960b3d 100644 --- a/target/linux/realtek/base-files/etc/board.d/02_network +++ b/target/linux/realtek/base-files/etc/board.d/02_network @@ -56,7 +56,7 @@ tplink,t1600g-28ts-v3) label_mac=$(get_mac_label) lan_mac="$label_mac" ;; -tplink,tl-st1008f,v2) +tplink,tl-st1008f-v2) lan_mac=$(mtd_get_mac_ascii u-boot-env ethaddr) [ -z "$lan_mac" ] || [ "$lan_mac" = "00:e0:4c:00:00:00" ] && lan_mac=$(macaddr_random) ;; diff --git a/target/linux/realtek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr b/target/linux/realtek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr index adb54da9566f45..c2684223895ff7 100644 --- a/target/linux/realtek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr +++ b/target/linux/realtek/base-files/etc/uci-defaults/99_fwenv-store-ethaddr @@ -10,7 +10,7 @@ BOARD_CFG=/etc/board.json [ "$(rootfs_type)" = "tmpfs" ] && exit 0 case "$(board_name)" in -tplink,tl-st1008f,v2) +tplink,tl-st1008f-v2) env_ethaddr=$(macaddr_canonicalize "$(fw_printenv -n ethaddr 2>/dev/null)") # This device ships with a dummy ethaddr because it's an unmanaged switch. diff --git a/target/linux/realtek/dts/rtl9303_tplink_tl-st1008f_v2.dts b/target/linux/realtek/dts/rtl9303_tplink_tl-st1008f-v2.dts similarity index 99% rename from target/linux/realtek/dts/rtl9303_tplink_tl-st1008f_v2.dts rename to target/linux/realtek/dts/rtl9303_tplink_tl-st1008f-v2.dts index 49d7b348d552fe..83f999879504bb 100644 --- a/target/linux/realtek/dts/rtl9303_tplink_tl-st1008f_v2.dts +++ b/target/linux/realtek/dts/rtl9303_tplink_tl-st1008f-v2.dts @@ -7,7 +7,7 @@ #include / { - compatible = "tplink,tl-st1008f,v2", "realtek,rtl930x-soc"; + compatible = "tplink,tl-st1008f-v2", "realtek,rtl930x-soc"; model = "TP-Link TL-ST1008F v2.0"; memory@0 { diff --git a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c index 3b14e9bcfe2ddc..7d38070091b391 100644 --- a/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c +++ b/target/linux/realtek/files-6.12/drivers/net/phy/rtl83xx-phy.c @@ -223,12 +223,14 @@ static int rtl821x_match_phy_device(struct phy_device *phydev) return PHY_IS_RTL8214FB; } -static int rtl8218b_ext_match_phy_device(struct phy_device *phydev) +static int rtl8218b_ext_match_phy_device(struct phy_device *phydev, + const struct phy_driver *phydrv) { return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8218B_E; } -static int rtl8214fc_match_phy_device(struct phy_device *phydev) +static int rtl8214fc_match_phy_device(struct phy_device *phydev, + const struct phy_driver *phydrv) { return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8214FC; } diff --git a/target/linux/realtek/image/rtl930x.mk b/target/linux/realtek/image/rtl930x.mk index 8f53855b5bccfe..cf7333e3d4c428 100644 --- a/target/linux/realtek/image/rtl930x.mk +++ b/target/linux/realtek/image/rtl930x.mk @@ -40,17 +40,18 @@ define Device/plasmacloud_psx10 endef TARGET_DEVICES += plasmacloud_psx10 -define Device/tplink_tl-st1008f_v2 +define Device/tplink_tl-st1008f-v2 SOC := rtl9303 UIMAGE_MAGIC := 0x93030000 DEVICE_VENDOR := TP-Link DEVICE_MODEL := TL-ST1008F DEVICE_VARIANT := v2.0 DEVICE_PACKAGES := kmod-gpio-pca953x + SUPPORTED_DEVICES += tplink,tl-st1008f,v2 IMAGE_SIZE := 31808k $(Device/kernel-lzma) endef -TARGET_DEVICES += tplink_tl-st1008f_v2 +TARGET_DEVICES += tplink_tl-st1008f-v2 define Device/vimin_vm-s100-0800ms SOC := rtl9303 diff --git a/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch index 9b054f96a8c2d3..ca411d69549e7f 100644 --- a/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch +++ b/target/linux/realtek/patches-6.12/706-include-linux-add-phy-ops-for-rtl838x.patch @@ -21,7 +21,7 @@ Submitted-by: John Crispin --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -1226,6 +1226,8 @@ struct phy_driver { +@@ -1227,6 +1227,8 @@ struct phy_driver { */ int (*led_polarity_set)(struct phy_device *dev, int index, unsigned long modes); diff --git a/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch index 3be218635e8243..4d1efd76b78564 100644 --- a/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch +++ b/target/linux/realtek/patches-6.12/720-add-rtl-phy.patch @@ -14,7 +14,7 @@ Submitted-by: Birger Koblitz --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -422,6 +422,12 @@ source "drivers/net/phy/realtek/Kconfig" +@@ -434,6 +434,12 @@ source "drivers/net/phy/realtek/Kconfig" source "drivers/net/phy/rtl8261n/Kconfig" @@ -29,7 +29,7 @@ Submitted-by: Birger Koblitz help --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -111,6 +111,7 @@ obj-y += qcom/ +@@ -112,6 +112,7 @@ obj-y += qcom/ obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_REALTEK_PHY) += realtek/ obj-y += rtl8261n/ diff --git a/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch b/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch index 55532bb5e520ab..98a6f07de80d9f 100644 --- a/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch +++ b/target/linux/siflower/patches-6.6/001-net-phy-c45-add-genphy_c45_pma_read_ext_abilities-fu.patch @@ -170,7 +170,7 @@ Signed-off-by: Jakub Kicinski /* This is optional functionality. If not supported, we may get an error --- a/include/linux/phy.h +++ b/include/linux/phy.h -@@ -1893,6 +1893,7 @@ int genphy_c45_an_config_aneg(struct phy +@@ -1897,6 +1897,7 @@ int genphy_c45_an_config_aneg(struct phy int genphy_c45_an_disable_aneg(struct phy_device *phydev); int genphy_c45_read_mdix(struct phy_device *phydev); int genphy_c45_pma_read_abilities(struct phy_device *phydev); diff --git a/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch b/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch index 4113d823263658..745d2e8fb5d445 100644 --- a/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch +++ b/target/linux/siflower/patches-6.6/019-net-phy-add-support-for-Siflower-SF23P1211-SF23P1240.patch @@ -11,7 +11,7 @@ Signed-off-by: haoming.chen --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -484,3 +484,8 @@ endif # PHYLIB +@@ -481,3 +481,8 @@ endif # PHYLIB config MICREL_KS8995MA tristate "Micrel KS8995MA 5-ports 10/100 managed Ethernet switch" depends on SPI