From 0368dc896a2f66f2a409fbc14b68528467449c23 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Wed, 15 Jul 2026 10:55:24 +0400 Subject: [PATCH 001/152] qualcommax: backport upstreamed patches for IPQ PWM support Backport upstreamed patches for IPQ PWM support and refresh following patches. Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/24238 Signed-off-by: Robert Marko --- ...dt-bindings-pwm-add-IPQ6018-binding.patch} | 32 ++- ...iver-for-qualcomm-ipq6018-pwm-block.patch} | 257 +++++++++--------- ...-arm64-dts-qcom-ipq6018-add-pwm-node.patch | 44 +++ ...-arm64-dts-qcom-ipq5018-add-pwm-node.patch | 41 +++ ...-arm64-dts-qcom-ipq6018-add-pwm-node.patch | 45 --- ...qcom-ipq5018-add-nodes-to-bringup-q6.patch | 4 +- ...-bindings-pwm-add-IPQ5018-compatible.patch | 22 -- ...-arm64-dts-qcom-ipq5018-Add-PWM-node.patch | 27 -- ...arm64-dts-qcom-ipq6018-add-wifi-node.patch | 2 +- ...07-soc-qcom-fix-smp2p-ack-on-ipq6018.patch | 2 +- ...pq6018-assign-QDSS_AT-clock-to-wifi-.patch | 2 +- 11 files changed, 247 insertions(+), 231 deletions(-) rename target/linux/qualcommax/patches-6.12/{0140-dt-bindings-pwm-add-IPQ6018-binding.patch => 0076-v7.2-dt-bindings-pwm-add-IPQ6018-binding.patch} (58%) rename target/linux/qualcommax/patches-6.12/{0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch => 0077-v7.2-pwm-Driver-for-qualcomm-ipq6018-pwm-block.patch} (57%) create mode 100644 target/linux/qualcommax/patches-6.12/0078-v7.3-arm64-dts-qcom-ipq6018-add-pwm-node.patch create mode 100644 target/linux/qualcommax/patches-6.12/0079-v7.3-arm64-dts-qcom-ipq5018-add-pwm-node.patch delete mode 100644 target/linux/qualcommax/patches-6.12/0138-arm64-dts-qcom-ipq6018-add-pwm-node.patch delete mode 100644 target/linux/qualcommax/patches-6.12/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch delete mode 100644 target/linux/qualcommax/patches-6.12/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch diff --git a/target/linux/qualcommax/patches-6.12/0140-dt-bindings-pwm-add-IPQ6018-binding.patch b/target/linux/qualcommax/patches-6.12/0076-v7.2-dt-bindings-pwm-add-IPQ6018-binding.patch similarity index 58% rename from target/linux/qualcommax/patches-6.12/0140-dt-bindings-pwm-add-IPQ6018-binding.patch rename to target/linux/qualcommax/patches-6.12/0076-v7.2-dt-bindings-pwm-add-IPQ6018-binding.patch index bba8b3457a..cdf9772410 100644 --- a/target/linux/qualcommax/patches-6.12/0140-dt-bindings-pwm-add-IPQ6018-binding.patch +++ b/target/linux/qualcommax/patches-6.12/0076-v7.2-dt-bindings-pwm-add-IPQ6018-binding.patch @@ -1,5 +1,6 @@ +From a8cd1a1baadbfed43ac214f0bf3e26c90e3b521e Mon Sep 17 00:00:00 2001 From: Devi Priya -Date: Thu, 5 Oct 2023 21:35:48 +0530 +Date: Mon, 6 Apr 2026 22:24:38 +0200 Subject: [PATCH] dt-bindings: pwm: add IPQ6018 binding DT binding for the PWM block in Qualcomm IPQ6018 SoC. @@ -9,10 +10,17 @@ Reviewed-by: Krzysztof Kozlowski Co-developed-by: Baruch Siach Signed-off-by: Baruch Siach Signed-off-by: Devi Priya +Signed-off-by: George Moussalem +Link: https://patch.msgid.link/20260406-ipq-pwm-v21-1-6ed1e868e4c2@outlook.com +Signed-off-by: Rob Herring (Arm) --- + .../bindings/pwm/qcom,ipq6018-pwm.yaml | 51 +++++++++++++++++++ + 1 file changed, 51 insertions(+) + create mode 100644 Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml + --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml -@@ -0,0 +1,45 @@ +@@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- @@ -22,21 +30,27 @@ Signed-off-by: Devi Priya +title: Qualcomm IPQ6018 PWM controller + +maintainers: -+ - Baruch Siach ++ - George Moussalem + +properties: + compatible: -+ const: qcom,ipq6018-pwm ++ oneOf: ++ - items: ++ - enum: ++ - qcom,ipq5018-pwm ++ - qcom,ipq5332-pwm ++ - qcom,ipq9574-pwm ++ - const: qcom,ipq6018-pwm ++ - const: qcom,ipq6018-pwm + + reg: -+ description: Offset of PWM register in the TCSR block. + maxItems: 1 + + clocks: + maxItems: 1 + + "#pwm-cells": -+ const: 2 ++ const: 3 + +required: + - compatible @@ -50,11 +64,11 @@ Signed-off-by: Devi Priya + - | + #include + -+ pwm: pwm@a010 { ++ pwm: pwm@1941010 { + compatible = "qcom,ipq6018-pwm"; -+ reg = <0xa010 0x20>; ++ reg = <0x01941010 0x20>; + clocks = <&gcc GCC_ADSS_PWM_CLK>; + assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; + assigned-clock-rates = <100000000>; -+ #pwm-cells = <2>; ++ #pwm-cells = <3>; + }; diff --git a/target/linux/qualcommax/patches-6.12/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch b/target/linux/qualcommax/patches-6.12/0077-v7.2-pwm-Driver-for-qualcomm-ipq6018-pwm-block.patch similarity index 57% rename from target/linux/qualcommax/patches-6.12/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch rename to target/linux/qualcommax/patches-6.12/0077-v7.2-pwm-Driver-for-qualcomm-ipq6018-pwm-block.patch index c01997a8e1..ba0a65fcbe 100644 --- a/target/linux/qualcommax/patches-6.12/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch +++ b/target/linux/qualcommax/patches-6.12/0077-v7.2-pwm-Driver-for-qualcomm-ipq6018-pwm-block.patch @@ -1,21 +1,30 @@ +From c436e3e9c265a1f012008ef5da6fd28863f8025c Mon Sep 17 00:00:00 2001 From: Devi Priya -Date: Thu, 5 Oct 2023 21:35:47 +0530 -Subject: [PATCH] pwm: driver for qualcomm ipq6018 pwm block +Date: Mon, 6 Apr 2026 22:24:39 +0200 +Subject: [PATCH] pwm: Driver for qualcomm ipq6018 pwm block +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit Driver for the PWM block in Qualcomm IPQ6018 line of SoCs. Based on driver from downstream Codeaurora kernel tree. Removed support for older (V1) variants because I have no access to that hardware. -Tested on IPQ6010 based hardware. +Tested on IPQ5018 and IPQ6010 based hardware. Co-developed-by: Baruch Siach Signed-off-by: Baruch Siach Signed-off-by: Devi Priya +Reviewed-by: Bjorn Andersson +Signed-off-by: George Moussalem +Link: https://patch.msgid.link/20260406-ipq-pwm-v21-2-6ed1e868e4c2@outlook.com +[ukleinek: Fixed a few nitpicks as agreed on the mailing list] +Signed-off-by: Uwe Kleine-König --- drivers/pwm/Kconfig | 12 ++ drivers/pwm/Makefile | 1 + - drivers/pwm/pwm-ipq.c | 282 ++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 295 insertions(+) + drivers/pwm/pwm-ipq.c | 263 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 276 insertions(+) create mode 100644 drivers/pwm/pwm-ipq.c --- a/drivers/pwm/Kconfig @@ -51,10 +60,26 @@ Signed-off-by: Devi Priya obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o --- /dev/null +++ b/drivers/pwm/pwm-ipq.c -@@ -0,0 +1,261 @@ +@@ -0,0 +1,263 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. ++ * ++ * Limitations: ++ * - The PWM controller has no publicly available datasheet. ++ * - Each of the four channels is programmed via two 32-bit registers ++ * (REG0 and REG1 at 8-byte stride). ++ * - Period and duty-cycle reconfiguration is fully atomic: new divider, ++ * pre-divider, and high-duration values are latched by setting the ++ * UPDATE bit (bit 30 in REG1). The hardware applies the new settings ++ * at the beginning of the next period without disabling the output, ++ * so the currently running period is always completed. ++ * - On disable (clearing the ENABLE bit 31 in REG1), the hardware ++ * finishes the current period before stopping the output. The pin ++ * is then driven to the inactive (low) level. ++ * - Upon disabling, the hardware resets the pre-divider (PRE_DIV) and divider ++ * fields (PWM_DIV) in REG0 and REG1 to 0x0000 and 0x0001 respectively. ++ * - Only normal polarity is supported. + */ + +#include @@ -68,14 +93,9 @@ Signed-off-by: Devi Priya +#include +#include + -+/* The frequency range supported is 1 Hz to clock rate */ ++/* The frequency range supported is 1 Hz to 100 Mhz (clock rate) */ +#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC) -+ -+/* -+ * The max value specified for each field is based on the number of bits -+ * in the pwm control register for that field -+ */ -+#define IPQ_PWM_MAX_DIV 0xFFFF ++#define IPQ_PWM_MIN_PERIOD_NS 10 + +/* + * Two 32-bit registers for each PWM: REG0, and REG1. @@ -89,15 +109,21 @@ Signed-off-by: Devi Priya +#define IPQ_PWM_REG1_PRE_DIV GENMASK(15, 0) +/* + * Enable bit is set to enable output toggling in pwm device. -+ * Update bit is set to reflect the changed divider and high duration -+ * values in register. ++ * Update bit is set to trigger the change and is unset automatically ++ * to reflect the changed divider and high duration values in register. + */ +#define IPQ_PWM_REG1_UPDATE BIT(30) +#define IPQ_PWM_REG1_ENABLE BIT(31) + ++/* ++ * The max value specified for each field is based on the number of bits ++ * in the pwm control register for that field (16-bit) ++ */ ++#define IPQ_PWM_MAX_DIV FIELD_MAX(IPQ_PWM_REG0_PWM_DIV) ++ +struct ipq_pwm_chip { -+ struct clk *clk; + void __iomem *mem; ++ unsigned long clk_rate; +}; + +static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip) @@ -122,18 +148,62 @@ Signed-off-by: Devi Priya + writel(val, ipq_chip->mem + off); +} + -+static void config_div_and_duty(struct pwm_device *pwm, unsigned int pre_div, -+ unsigned int pwm_div, unsigned long rate, u64 duty_ns, -+ bool enable) ++static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ const struct pwm_state *state) +{ -+ unsigned long hi_dur; ++ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); ++ unsigned int pre_div, pwm_div; ++ u64 period_ns, duty_ns; + unsigned long val = 0; ++ unsigned long hi_dur; ++ ++ if (!state->enabled) { ++ /* clear IPQ_PWM_REG1_ENABLE */ ++ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, IPQ_PWM_REG1_UPDATE); ++ return 0; ++ } ++ ++ if (state->polarity != PWM_POLARITY_NORMAL) ++ return -EINVAL; + + /* -+ * high duration = pwm duty * (pwm div + 1) -+ * pwm duty = duty_ns / period_ns ++ * Check the upper and lower bounds for the period as per ++ * hardware limits + */ -+ hi_dur = div64_u64(duty_ns * rate, (pre_div + 1) * NSEC_PER_SEC); ++ if (state->period < IPQ_PWM_MIN_PERIOD_NS) ++ return -ERANGE; ++ period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS); ++ duty_ns = min(state->duty_cycle, period_ns); ++ ++ /* ++ * Pick the maximal value for PWM_DIV that still allows a ++ * 100% relative duty cycle. This allows a fine grained ++ * selection of duty cycles. ++ */ ++ pwm_div = IPQ_PWM_MAX_DIV - 1; ++ ++ /* ++ * although mul_u64_u64_div_u64 returns a u64, in practice it ++ * won't overflow due to above constraints. Take the max period ++ * of 10^9 (NSEC_PER_SEC) and the pwm_div + 1 (IPQ_PWM_MAX_DIV) ++ * 10^9 * 10^8 ++ * ------------- => which fits well into a 32-bit unsigned int. ++ * 10^9 * 65,535 ++ */ ++ pre_div = mul_u64_u64_div_u64(period_ns, ipq_chip->clk_rate, ++ (u64)NSEC_PER_SEC * (pwm_div + 1)); ++ ++ if (!pre_div) ++ return -ERANGE; ++ ++ pre_div -= 1; ++ ++ if (pre_div > IPQ_PWM_MAX_DIV) ++ pre_div = IPQ_PWM_MAX_DIV; ++ ++ /* pwm duty = HI_DUR * (PRE_DIV + 1) / clk_rate */ ++ hi_dur = mul_u64_u64_div_u64(duty_ns, ipq_chip->clk_rate, ++ (u64)NSEC_PER_SEC * (pre_div + 1)); + + val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) | + FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div); @@ -143,90 +213,8 @@ Signed-off-by: Devi Priya + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); + + /* PWM enable toggle needs a separate write to REG1 */ -+ val |= IPQ_PWM_REG1_UPDATE; -+ if (enable) -+ val |= IPQ_PWM_REG1_ENABLE; ++ val |= IPQ_PWM_REG1_UPDATE | IPQ_PWM_REG1_ENABLE; + ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val); -+} -+ -+static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, -+ const struct pwm_state *state) -+{ -+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); -+ unsigned int pre_div, pwm_div, best_pre_div, best_pwm_div; -+ unsigned long rate = clk_get_rate(ipq_chip->clk); -+ u64 period_ns, duty_ns, period_rate; -+ u64 min_diff; -+ -+ if (state->polarity != PWM_POLARITY_NORMAL) -+ return -EINVAL; -+ -+ if (state->period < DIV64_U64_ROUND_UP(NSEC_PER_SEC, rate)) -+ return -ERANGE; -+ -+ period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS); -+ duty_ns = min(state->duty_cycle, period_ns); -+ -+ /* -+ * period_ns is 1G or less. As long as rate is less than 16 GHz, -+ * period_rate does not overflow. Make that explicit. -+ */ -+ if ((unsigned long long)rate > 16ULL * GIGA) -+ return -EINVAL; -+ period_rate = period_ns * rate; -+ best_pre_div = IPQ_PWM_MAX_DIV; -+ best_pwm_div = IPQ_PWM_MAX_DIV; -+ /* -+ * We don't need to consider pre_div values smaller than -+ * -+ * period_rate -+ * pre_div_min := ------------------------------------ -+ * NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1) -+ * -+ * because pre_div = pre_div_min results in a better -+ * approximation. -+ */ -+ pre_div = div64_u64(period_rate, -+ (u64)NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1)); -+ min_diff = period_rate; -+ -+ for (; pre_div <= IPQ_PWM_MAX_DIV; pre_div++) { -+ u64 remainder; -+ -+ pwm_div = div64_u64_rem(period_rate, -+ (u64)NSEC_PER_SEC * (pre_div + 1), &remainder); -+ /* pwm_div is unsigned; the check below catches underflow */ -+ pwm_div--; -+ -+ /* -+ * Swapping values for pre_div and pwm_div produces the same -+ * period length. So we can skip all settings with pre_div > -+ * pwm_div which results in bigger constraints for selecting -+ * the duty_cycle than with the two values swapped. -+ */ -+ if (pre_div > pwm_div) -+ break; -+ -+ /* -+ * Make sure we can do 100% duty cycle where -+ * hi_dur == pwm_div + 1 -+ */ -+ if (pwm_div > IPQ_PWM_MAX_DIV - 1) -+ continue; -+ -+ if (remainder < min_diff) { -+ best_pre_div = pre_div; -+ best_pwm_div = pwm_div; -+ min_diff = remainder; -+ -+ if (min_diff == 0) /* bingo */ -+ break; -+ } -+ } -+ -+ /* config divider values for the closest possible frequency */ -+ config_div_and_duty(pwm, best_pre_div, best_pwm_div, -+ rate, duty_ns, state->enabled); + + return 0; +} @@ -235,27 +223,41 @@ Signed-off-by: Devi Priya + struct pwm_state *state) +{ + struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip); -+ unsigned long rate = clk_get_rate(ipq_chip->clk); + unsigned int pre_div, pwm_div, hi_dur; + u64 effective_div, hi_div; + u32 reg0, reg1; + -+ reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0); + reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1); ++ state->enabled = reg1 & IPQ_PWM_REG1_ENABLE; ++ ++ if (!state->enabled) ++ return 0; ++ ++ reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0); + + state->polarity = PWM_POLARITY_NORMAL; -+ state->enabled = reg1 & IPQ_PWM_REG1_ENABLE; + + pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0); + hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0); + pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1); + -+ /* No overflow here, both pre_div and pwm_div <= 0xffff */ -+ effective_div = (u64)(pre_div + 1) * (pwm_div + 1); -+ state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, rate); ++ effective_div = (u64)(pwm_div + 1) * (pre_div + 1); ++ ++ /* ++ * effective_div <= 0x100000000, so the multiplication doesn't overflow. ++ */ ++ state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, ++ ipq_chip->clk_rate); + + hi_div = hi_dur * (pre_div + 1); -+ state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, rate); ++ state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, ++ ipq_chip->clk_rate); ++ ++ /* ++ * ensure a valid config is passed back to PWM core in case duty_cycle ++ * is > period (>100%) ++ */ ++ state->duty_cycle = min(state->duty_cycle, state->period); + + return 0; +} @@ -267,9 +269,10 @@ Signed-off-by: Devi Priya + +static int ipq_pwm_probe(struct platform_device *pdev) +{ -+ struct pwm_chip *chip; -+ struct ipq_pwm_chip *pwm; + struct device *dev = &pdev->dev; ++ struct ipq_pwm_chip *pwm; ++ struct pwm_chip *chip; ++ struct clk *clk; + int ret; + + chip = devm_pwmchip_alloc(dev, 4, sizeof(*pwm)); @@ -280,20 +283,28 @@ Signed-off-by: Devi Priya + pwm->mem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pwm->mem)) + return dev_err_probe(dev, PTR_ERR(pwm->mem), -+ "regs map failed"); ++ "Failed to acquire resource\n"); + -+ pwm->clk = devm_clk_get_enabled(dev, NULL); -+ if (IS_ERR(pwm->clk)) -+ return dev_err_probe(dev, PTR_ERR(pwm->clk), -+ "failed to get clock"); ++ clk = devm_clk_get_enabled(dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), ++ "Failed to get clock\n"); ++ ++ ret = devm_clk_rate_exclusive_get(dev, clk); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to lock clock rate\n"); ++ ++ pwm->clk_rate = clk_get_rate(clk); ++ if (!pwm->clk_rate) ++ return dev_err_probe(dev, -EINVAL, "Failed due to clock rate being zero\n"); + + chip->ops = &ipq_pwm_ops; + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) -+ dev_err_probe(dev, ret, "devm_pwmchip_add() failed\n"); ++ return dev_err_probe(dev, ret, "Failed to add pwm chip\n"); + -+ return ret; ++ return 0; +} + +static const struct of_device_id pwm_ipq_dt_match[] = { @@ -312,4 +323,4 @@ Signed-off-by: Devi Priya + +module_platform_driver(ipq_pwm_driver); + -+MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/qualcommax/patches-6.12/0078-v7.3-arm64-dts-qcom-ipq6018-add-pwm-node.patch b/target/linux/qualcommax/patches-6.12/0078-v7.3-arm64-dts-qcom-ipq6018-add-pwm-node.patch new file mode 100644 index 0000000000..4ee9086b89 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0078-v7.3-arm64-dts-qcom-ipq6018-add-pwm-node.patch @@ -0,0 +1,44 @@ +From 6cc812b7435a64cd9d66af107c6dfefb2e77a7cb Mon Sep 17 00:00:00 2001 +From: Devi Priya +Date: Mon, 6 Apr 2026 22:24:40 +0200 +Subject: arm64: dts: qcom: ipq6018: add pwm node + +Describe the PWM block on IPQ6018. + +Although PWM is in the TCSR area, make pwm its own node as simple-mfd +has been removed from the bindings and as such hardware components +should have its own node. + +Reviewed-by: Krzysztof Kozlowski +Co-developed-by: Baruch Siach +Signed-off-by: Baruch Siach +Signed-off-by: Devi Priya +Reviewed-by: Konrad Dybcio +Signed-off-by: George Moussalem +Link: https://lore.kernel.org/r/20260406-ipq-pwm-v21-3-6ed1e868e4c2@outlook.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq6018.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +(limited to 'arch/arm64/boot/dts/qcom/ipq6018.dtsi') + +--- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi +@@ -416,6 +416,16 @@ + reg = <0x0 0x01937000 0x0 0x21000>; + }; + ++ pwm: pwm@1941010 { ++ compatible = "qcom,ipq6018-pwm"; ++ reg = <0x0 0x01941010 0x0 0x20>; ++ clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clock-rates = <100000000>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + usb2: usb@70f8800 { + compatible = "qcom,ipq6018-dwc3", "qcom,dwc3"; + reg = <0x0 0x070f8800 0x0 0x400>; diff --git a/target/linux/qualcommax/patches-6.12/0079-v7.3-arm64-dts-qcom-ipq5018-add-pwm-node.patch b/target/linux/qualcommax/patches-6.12/0079-v7.3-arm64-dts-qcom-ipq5018-add-pwm-node.patch new file mode 100644 index 0000000000..4be6601939 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0079-v7.3-arm64-dts-qcom-ipq5018-add-pwm-node.patch @@ -0,0 +1,41 @@ +From 4b00afd17a28f44dc63a8602143f84e5f3fa592c Mon Sep 17 00:00:00 2001 +From: George Moussalem +Date: Mon, 6 Apr 2026 22:24:41 +0200 +Subject: arm64: dts: qcom: ipq5018: add pwm node + +Describe the PWM block on IPQ5018. + +Although PWM is in the TCSR area, make pwm its own node as simple-mfd +has been removed from the bindings and as such hardware components +should have its own node. + +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Konrad Dybcio +Signed-off-by: George Moussalem +Link: https://lore.kernel.org/r/20260406-ipq-pwm-v21-4-6ed1e868e4c2@outlook.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -453,6 +453,16 @@ + reg = <0x01937000 0x21000>; + }; + ++ pwm: pwm@1941010 { ++ compatible = "qcom,ipq5018-pwm", "qcom,ipq6018-pwm"; ++ reg = <0x01941010 0x20>; ++ clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; ++ assigned-clock-rates = <100000000>; ++ #pwm-cells = <3>; ++ status = "disabled"; ++ }; ++ + sdhc_1: mmc@7804000 { + compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5"; + reg = <0x7804000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.12/0138-arm64-dts-qcom-ipq6018-add-pwm-node.patch b/target/linux/qualcommax/patches-6.12/0138-arm64-dts-qcom-ipq6018-add-pwm-node.patch deleted file mode 100644 index 006818abe0..0000000000 --- a/target/linux/qualcommax/patches-6.12/0138-arm64-dts-qcom-ipq6018-add-pwm-node.patch +++ /dev/null @@ -1,45 +0,0 @@ -From b4a32d218d424b81a58fbd419e1114b1c1f76168 Mon Sep 17 00:00:00 2001 -From: Devi Priya -Date: Thu, 5 Oct 2023 21:35:50 +0530 -Subject: [PATCH] arm64: dts: qcom: ipq6018: add pwm node - -Describe the PWM block on IPQ6018. - -The PWM is in the TCSR area. Make &tcsr "simple-mfd" compatible, and add -&pwm as child of &tcsr. - -Add also ipq6018 specific compatible string. - -Reviewed-by: Krzysztof Kozlowski -Co-developed-by: Baruch Siach -Signed-off-by: Baruch Siach -Signed-off-by: Devi Priya ---- - arch/arm64/boot/dts/qcom/ipq6018.dtsi | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) - ---- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -417,8 +417,21 @@ - }; - - tcsr: syscon@1937000 { -- compatible = "qcom,tcsr-ipq6018", "syscon"; -+ compatible = "qcom,tcsr-ipq6018", "syscon", "simple-mfd"; - reg = <0x0 0x01937000 0x0 0x21000>; -+ ranges = <0x0 0x0 0x01937000 0x21000>; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ pwm: pwm@a010 { -+ compatible = "qcom,ipq6018-pwm"; -+ reg = <0xa010 0x20>; -+ clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clock-rates = <100000000>; -+ #pwm-cells = <2>; -+ status = "disabled"; -+ }; - }; - - usb2: usb@70f8800 { diff --git a/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch b/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch index 9ea80bf08a..7620bb53e1 100644 --- a/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch +++ b/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch @@ -28,7 +28,7 @@ Signed-off-by: George Moussalem }; soc: soc@0 { -@@ -721,6 +726,41 @@ +@@ -731,6 +736,41 @@ }; }; @@ -70,7 +70,7 @@ Signed-off-by: George Moussalem pcie1: pcie@80000000 { compatible = "qcom,pcie-ipq5018"; reg = <0x80000000 0xf1d>, -@@ -995,4 +1035,28 @@ +@@ -1005,4 +1045,28 @@ , ; }; diff --git a/target/linux/qualcommax/patches-6.12/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.12/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch deleted file mode 100644 index 5705028931..0000000000 --- a/target/linux/qualcommax/patches-6.12/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: George Moussalem -Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add IPQ5018 compatible -Date: Sun, 06 Oct 2024 16:34:11 +0400 - -Add compatible for IPQ5018. - -Signed-off-by: George Moussalem ---- ---- a/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml -+++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml -@@ -11,7 +11,10 @@ maintainers: - - properties: - compatible: -- const: qcom,ipq6018-pwm -+ items: -+ - enum: -+ - qcom,ipq5018-pwm -+ - const: qcom,ipq6018-pwm - - reg: - description: Offset of PWM register in the TCSR block. diff --git a/target/linux/qualcommax/patches-6.12/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch b/target/linux/qualcommax/patches-6.12/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch deleted file mode 100644 index 5272a57465..0000000000 --- a/target/linux/qualcommax/patches-6.12/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: George Moussalem -Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PWM node -Date: Sun, 06 Oct 2024 16:34:11 +0400 - -Add PWM node. - -Signed-off-by: George Moussalem ---- ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -458,6 +458,16 @@ - reg = <0x01937000 0x21000>; - }; - -+ pwm: pwm@1941010 { -+ compatible = "qcom,ipq5018-pwm", "qcom,ipq6018-pwm"; -+ reg = <0x01941010 0x20>; -+ clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>; -+ assigned-clock-rates = <100000000>; -+ #pwm-cells = <2>; -+ status = "disabled"; -+ }; -+ - sdhc_1: mmc@7804000 { - compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5"; - reg = <0x7804000 0x1000>; diff --git a/target/linux/qualcommax/patches-6.12/0906-arm64-dts-qcom-ipq6018-add-wifi-node.patch b/target/linux/qualcommax/patches-6.12/0906-arm64-dts-qcom-ipq6018-add-wifi-node.patch index 3ff649f2a4..aac8cd53dc 100644 --- a/target/linux/qualcommax/patches-6.12/0906-arm64-dts-qcom-ipq6018-add-wifi-node.patch +++ b/target/linux/qualcommax/patches-6.12/0906-arm64-dts-qcom-ipq6018-add-wifi-node.patch @@ -15,7 +15,7 @@ Signed-off-by: Mantas Pucka --- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -834,6 +834,102 @@ +@@ -831,6 +831,102 @@ }; }; diff --git a/target/linux/qualcommax/patches-6.12/0907-soc-qcom-fix-smp2p-ack-on-ipq6018.patch b/target/linux/qualcommax/patches-6.12/0907-soc-qcom-fix-smp2p-ack-on-ipq6018.patch index 02d8da6305..9dc8cd4d08 100644 --- a/target/linux/qualcommax/patches-6.12/0907-soc-qcom-fix-smp2p-ack-on-ipq6018.patch +++ b/target/linux/qualcommax/patches-6.12/0907-soc-qcom-fix-smp2p-ack-on-ipq6018.patch @@ -15,7 +15,7 @@ Signed-off-by: Mantas Pucka --- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -1186,6 +1186,7 @@ +@@ -1183,6 +1183,7 @@ wcss_smp2p_out: master-kernel { qcom,entry-name = "master-kernel"; diff --git a/target/linux/qualcommax/patches-6.12/0909-arm64-dts-qcom-ipq6018-assign-QDSS_AT-clock-to-wifi-.patch b/target/linux/qualcommax/patches-6.12/0909-arm64-dts-qcom-ipq6018-assign-QDSS_AT-clock-to-wifi-.patch index 4ef8f799ab..2a2fa5ffc9 100644 --- a/target/linux/qualcommax/patches-6.12/0909-arm64-dts-qcom-ipq6018-assign-QDSS_AT-clock-to-wifi-.patch +++ b/target/linux/qualcommax/patches-6.12/0909-arm64-dts-qcom-ipq6018-assign-QDSS_AT-clock-to-wifi-.patch @@ -13,7 +13,7 @@ Signed-off-by: Mantas Pucka --- a/arch/arm64/boot/dts/qcom/ipq6018.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq6018.dtsi -@@ -955,8 +955,8 @@ +@@ -952,8 +952,8 @@ "wcss_reset", "wcss_q6_reset"; From 5ddbbe76703a68d7af048e105a4cf53f8916e4da Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Wed, 15 Jul 2026 12:05:21 +0400 Subject: [PATCH 002/152] qualcommax: backport upstreamed patches for IPQ CMN PLL support Backport upstreamed patched for IPQ CMN PLL support. Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/24238 Signed-off-by: Robert Marko --- ...-Add-CMN-PLL-support-for-IPQ5018-SoC.patch | 17 ++++-- ...qcom-Add-CMN-PLL-support-for-IPQ6018.patch | 52 +++++++++++++++++++ ...qcom-Add-CMN-PLL-support-for-IPQ8074.patch | 52 +++++++++++++++++++ ...ipq-cmn-pll-Add-IPQ6018-SoC-support.patch} | 14 +++-- ...ipq-cmn-pll-Add-IPQ8074-SoC-support.patch} | 14 +++-- ...Account-for-reference-clock-divider.patch} | 17 ++++-- ...Correct-CMN-PLL-reference-clock-rate.patch | 35 +++++++++++++ ...5018-fix-assigned-cmn-pll-clock-rate.patch | 34 ------------ ...qcom-Add-CMN-PLL-support-for-IPQ6018.patch | 33 ------------ ...qcom-Add-CMN-PLL-support-for-IPQ8074.patch | 33 ------------ 10 files changed, 183 insertions(+), 118 deletions(-) create mode 100644 target/linux/qualcommax/patches-6.12/0080-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch create mode 100644 target/linux/qualcommax/patches-6.12/0081-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch rename target/linux/qualcommax/patches-6.12/{0914-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch => 0082-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch} (75%) rename target/linux/qualcommax/patches-6.12/{0916-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch => 0083-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch} (75%) rename target/linux/qualcommax/patches-6.12/{0190-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch => 0084-v7.2-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch} (70%) create mode 100644 target/linux/qualcommax/patches-6.12/0085-v7.3-arm64-dts-qcom-ipq5018-Correct-CMN-PLL-reference-clock-rate.patch delete mode 100644 target/linux/qualcommax/patches-6.12/0818-arm64-dts-qcom-ipq5018-fix-assigned-cmn-pll-clock-rate.patch delete mode 100644 target/linux/qualcommax/patches-6.12/0913-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch delete mode 100644 target/linux/qualcommax/patches-6.12/0915-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch diff --git a/target/linux/qualcommax/patches-6.12/0067-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5018-SoC.patch b/target/linux/qualcommax/patches-6.12/0067-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5018-SoC.patch index c0d887f9a1..e2e3f74fd4 100644 --- a/target/linux/qualcommax/patches-6.12/0067-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5018-SoC.patch +++ b/target/linux/qualcommax/patches-6.12/0067-v6.17-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ5018-SoC.patch @@ -1,7 +1,7 @@ From 314b903c30040632db7edd187cd33003b2aee512 Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Fri, 16 May 2025 16:36:09 +0400 -Subject: dt-bindings: clock: qcom: Add CMN PLL support for IPQ5018 SoC +Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL support for IPQ5018 SoC The CMN PLL block in the IPQ5018 SoC takes 96 MHZ as the reference input clock. Its output clocks are the XO (24Mhz), sleep (32Khz), and @@ -12,12 +12,21 @@ Signed-off-by: George Moussalem Link: https://lore.kernel.org/r/20250516-ipq5018-cmn-pll-v4-2-389a6b30e504@outlook.com Signed-off-by: Bjorn Andersson --- + .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + include/dt-bindings/clock/qcom,ipq5018-cmn-pll.h | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) + 2 files changed, 17 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,ipq5018-cmn-pll.h -(limited to 'include/dt-bindings/clock/qcom,ipq5018-cmn-pll.h') - +--- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml ++++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +@@ -24,6 +24,7 @@ description: + properties: + compatible: + enum: ++ - qcom,ipq5018-cmn-pll + - qcom,ipq5424-cmn-pll + - qcom,ipq9574-cmn-pll + --- /dev/null +++ b/include/dt-bindings/clock/qcom,ipq5018-cmn-pll.h @@ -0,0 +1,16 @@ diff --git a/target/linux/qualcommax/patches-6.12/0080-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch b/target/linux/qualcommax/patches-6.12/0080-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch new file mode 100644 index 0000000000..0ab8bec21e --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0080-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch @@ -0,0 +1,52 @@ +From a57666004f49fa5031d6bf388834213e6f961922 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 11 Mar 2026 19:39:38 +0100 +Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL support for IPQ6018 + +The CMN PLL block in the IPQ6018 SoC takes 48 MHz as the reference +input clock. Its output clocks are the bias_pll_cc_clk (300 MHz) and +bias_pll_nss_noc_clk (416.5 MHz) clocks used by the networking +subsystem. + +Add the related compatible for IPQ6018 to the ipq9574-cmn-pll +generic schema. + +Signed-off-by: John Crispin +Signed-off-by: Christian Marangi +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20260311183942.10134-2-ansuelsmth@gmail.com +Signed-off-by: Bjorn Andersson +--- + .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + + include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h | 15 +++++++++++++++ + 2 files changed, 16 insertions(+) + create mode 100644 include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h + +--- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml ++++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +@@ -26,6 +26,7 @@ properties: + enum: + - qcom,ipq5018-cmn-pll + - qcom,ipq5424-cmn-pll ++ - qcom,ipq6018-cmn-pll + - qcom,ipq9574-cmn-pll + + reg: +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ ++/* ++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H ++#define _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H ++ ++/* CMN PLL core clock. */ ++#define IPQ6018_CMN_PLL_CLK 0 ++ ++/* The output clocks from CMN PLL of IPQ6018. */ ++#define IPQ6018_BIAS_PLL_CC_CLK 1 ++#define IPQ6018_BIAS_PLL_NSS_NOC_CLK 2 ++#endif diff --git a/target/linux/qualcommax/patches-6.12/0081-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch b/target/linux/qualcommax/patches-6.12/0081-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch new file mode 100644 index 0000000000..8a104557b5 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0081-v7.1-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch @@ -0,0 +1,52 @@ +From 7156c65030006e6930dd99c5b8c5e84e69ca5f0b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 11 Mar 2026 19:39:40 +0100 +Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL support for IPQ8074 + +The CMN PLL block in the IPQ8074 SoC takes 48 MHz as the reference +input clock. Its output clocks are the bias_pll_cc_clk (300 MHz) and +bias_pll_nss_noc_clk (416.5 MHz) clocks used by the networking +subsystem. + +Add the related compatible for IPQ8074 to the ipq9574-cmn-pll +generic schema. + +Signed-off-by: John Crispin +Signed-off-by: Christian Marangi +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20260311183942.10134-4-ansuelsmth@gmail.com +Signed-off-by: Bjorn Andersson +--- + .../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 1 + + include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h | 15 +++++++++++++++ + 2 files changed, 16 insertions(+) + create mode 100644 include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h + +--- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml ++++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +@@ -27,6 +27,7 @@ properties: + - qcom,ipq5018-cmn-pll + - qcom,ipq5424-cmn-pll + - qcom,ipq6018-cmn-pll ++ - qcom,ipq8074-cmn-pll + - qcom,ipq9574-cmn-pll + + reg: +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h +@@ -0,0 +1,15 @@ ++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ ++/* ++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H ++#define _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H ++ ++/* CMN PLL core clock. */ ++#define IPQ8074_CMN_PLL_CLK 0 ++ ++/* The output clocks from CMN PLL of IPQ8074. */ ++#define IPQ8074_BIAS_PLL_CC_CLK 1 ++#define IPQ8074_BIAS_PLL_NSS_NOC_CLK 2 ++#endif diff --git a/target/linux/qualcommax/patches-6.12/0914-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch b/target/linux/qualcommax/patches-6.12/0082-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch similarity index 75% rename from target/linux/qualcommax/patches-6.12/0914-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch rename to target/linux/qualcommax/patches-6.12/0082-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch index 63eae7f3c8..cfdeea5114 100644 --- a/target/linux/qualcommax/patches-6.12/0914-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch +++ b/target/linux/qualcommax/patches-6.12/0082-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ6018-SoC-support.patch @@ -1,15 +1,19 @@ +From 97eb2ac52726fbb702ced40d552a3f6f2683b664 Mon Sep 17 00:00:00 2001 From: John Crispin -Date: Wed, 05 Mar 2026 12:00:00 +0100 -Subject: clk: qcom: ipq-cmn-pll: Add IPQ6018 SoC support +Date: Wed, 11 Mar 2026 19:39:39 +0100 +Subject: [PATCH] clk: qcom: ipq-cmn-pll: Add IPQ6018 SoC support The CMN PLL in IPQ6018 SoC supplies fixed clocks to the networking subsystem: bias_pll_cc_clk at 300 MHz and bias_pll_nss_noc_clk at 416.5 MHz. Signed-off-by: John Crispin +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20260311183942.10134-3-ansuelsmth@gmail.com +Signed-off-by: Bjorn Andersson --- - drivers/clk/qcom/ipq-cmn-pll.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) + drivers/clk/qcom/ipq-cmn-pll.c | 8 ++++++++ + 1 file changed, 8 insertions(+) --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -34,7 +38,7 @@ Signed-off-by: John Crispin static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -@@ -456,6 +463,7 @@ static const struct dev_pm_ops ipq_cmn_p +@@ -449,6 +456,7 @@ static const struct dev_pm_ops ipq_cmn_p static const struct of_device_id ipq_cmn_pll_clk_ids[] = { { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, diff --git a/target/linux/qualcommax/patches-6.12/0916-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch b/target/linux/qualcommax/patches-6.12/0083-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch similarity index 75% rename from target/linux/qualcommax/patches-6.12/0916-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch rename to target/linux/qualcommax/patches-6.12/0083-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch index 5d2e270e61..e972b500b8 100644 --- a/target/linux/qualcommax/patches-6.12/0916-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch +++ b/target/linux/qualcommax/patches-6.12/0083-v7.1-clk-qcom-ipq-cmn-pll-Add-IPQ8074-SoC-support.patch @@ -1,15 +1,19 @@ +From 4e36f8ab45c406420f2c2ce6ee3988e0d13ba1c9 Mon Sep 17 00:00:00 2001 From: John Crispin -Date: Wed, 05 Mar 2026 12:00:00 +0100 -Subject: clk: qcom: ipq-cmn-pll: Add IPQ8074 SoC support +Date: Wed, 11 Mar 2026 19:39:41 +0100 +Subject: [PATCH] clk: qcom: ipq-cmn-pll: Add IPQ8074 SoC support The CMN PLL in IPQ8074 SoC supplies fixed clocks to the networking subsystem: bias_pll_cc_clk at 300 MHz and bias_pll_nss_noc_clk at 416.5 MHz. Signed-off-by: John Crispin +Signed-off-by: Christian Marangi +Link: https://lore.kernel.org/r/20260311183942.10134-5-ansuelsmth@gmail.com +Signed-off-by: Bjorn Andersson --- - drivers/clk/qcom/ipq-cmn-pll.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) + drivers/clk/qcom/ipq-cmn-pll.c | 8 ++++++++ + 1 file changed, 8 insertions(+) --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -34,7 +38,7 @@ Signed-off-by: John Crispin static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), -@@ -464,6 +471,7 @@ static const struct of_device_id ipq_cmn +@@ -457,6 +464,7 @@ static const struct of_device_id ipq_cmn { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, { .compatible = "qcom,ipq6018-cmn-pll", .data = &ipq6018_output_clks }, diff --git a/target/linux/qualcommax/patches-6.12/0190-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch b/target/linux/qualcommax/patches-6.12/0084-v7.2-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch similarity index 70% rename from target/linux/qualcommax/patches-6.12/0190-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch rename to target/linux/qualcommax/patches-6.12/0084-v7.2-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch index 089075a910..2c786752e5 100644 --- a/target/linux/qualcommax/patches-6.12/0190-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch +++ b/target/linux/qualcommax/patches-6.12/0084-v7.2-clk-qcom-cmnpll-Account-for-reference-clock-divider.patch @@ -1,6 +1,7 @@ +From 88c543fff756450bcd04ec4560c4440be36c9e75 Mon Sep 17 00:00:00 2001 From: Luo Jie -Date: Tue, 06 Jan 2026 21:35:10 -0800 -Subject: [PATCH v2 1/5] clk: qcom: cmnpll: Account for reference clock divider +Date: Tue, 6 Jan 2026 21:35:10 -0800 +Subject: [PATCH] clk: qcom: cmnpll: Account for reference clock divider The clk_cmn_pll_recalc_rate() function must account for the reference clock divider programmed in CMN_PLL_REFCLK_CONFIG. Without this fix, platforms @@ -17,9 +18,17 @@ IPQ5424, IPQ5018) that use ref_div = 1. Fixes: f81715a4c87c ("clk: qcom: Add CMN PLL clock controller driver for IPQ SoC") Signed-off-by: Luo Jie +Reviewed-by: Konrad Dybcio +Tested-by: George Moussalem +Link: https://lore.kernel.org/r/20260106-qcom_ipq5332_cmnpll-v2-1-f9f7e4efbd79@oss.qualcomm.com +Signed-off-by: Bjorn Andersson +--- + drivers/clk/qcom/ipq-cmn-pll.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c -@@ -186,7 +186,7 @@ static unsigned long clk_cmn_pll_recalc_ +@@ -200,7 +200,7 @@ static unsigned long clk_cmn_pll_recalc_ unsigned long parent_rate) { struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); @@ -28,7 +37,7 @@ Signed-off-by: Luo Jie /* * The value of CMN_PLL_DIVIDER_CTRL_FACTOR is automatically adjusted -@@ -194,8 +194,15 @@ static unsigned long clk_cmn_pll_recalc_ +@@ -208,8 +208,15 @@ static unsigned long clk_cmn_pll_recalc_ */ regmap_read(cmn_pll->regmap, CMN_PLL_DIVIDER_CTRL, &val); factor = FIELD_GET(CMN_PLL_DIVIDER_CTRL_FACTOR, val); diff --git a/target/linux/qualcommax/patches-6.12/0085-v7.3-arm64-dts-qcom-ipq5018-Correct-CMN-PLL-reference-clock-rate.patch b/target/linux/qualcommax/patches-6.12/0085-v7.3-arm64-dts-qcom-ipq5018-Correct-CMN-PLL-reference-clock-rate.patch new file mode 100644 index 0000000000..d3a2d8c086 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0085-v7.3-arm64-dts-qcom-ipq5018-Correct-CMN-PLL-reference-clock-rate.patch @@ -0,0 +1,35 @@ +From 02320d694fa1124b8b77fcb52191141004ef7fbb Mon Sep 17 00:00:00 2001 +From: George Moussalem +Date: Thu, 21 May 2026 11:55:58 +0400 +Subject: arm64: dts: qcom: ipq5018: Correct CMN PLL reference clock rate + +The correct CMN PLL reference clock rate for IPQ5018 is 4.8 GHz. + +The CMN PLL driver did not account for the ref clock divider which is 2 +for IPQ5018. Therefore, the computed rate was twice the actual output. + +With the driver now accounting for the CMN PLL reference clock +divider (commit: 88c543fff756), set the correct reference clock rate. + +Fixes: c006b249c544 ("arm64: dts: ipq5018: Add CMN PLL node") +Signed-off-by: George Moussalem +Acked-by: Konrad Dybcio +Link: https://lore.kernel.org/r/20260521-ipq5018-cmn-pll-rate-fix-v2-1-04b28a92e0f2@outlook.com +Signed-off-by: Bjorn Andersson +--- + arch/arm64/boot/dts/qcom/ipq5018.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') + +--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi ++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi +@@ -256,7 +256,7 @@ + "sys"; + #clock-cells = <1>; + assigned-clocks = <&cmn_pll IPQ5018_CMN_PLL_CLK>; +- assigned-clock-rates-u64 = /bits/ 64 <9600000000>; ++ assigned-clock-rates-u64 = /bits/ 64 <4800000000>; + }; + + qfprom: qfprom@a0000 { diff --git a/target/linux/qualcommax/patches-6.12/0818-arm64-dts-qcom-ipq5018-fix-assigned-cmn-pll-clock-rate.patch b/target/linux/qualcommax/patches-6.12/0818-arm64-dts-qcom-ipq5018-fix-assigned-cmn-pll-clock-rate.patch deleted file mode 100644 index 8d0ca3741d..0000000000 --- a/target/linux/qualcommax/patches-6.12/0818-arm64-dts-qcom-ipq5018-fix-assigned-cmn-pll-clock-rate.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 5b00a1e17e98e99cc31b4dc6584b9ef93b8a62c4 Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Wed, 07 Jan 2026 18:21:49 +0400 -Subject: [PATCH] arm64: dts: qcom: ipq5018: fix assigned cmn-pll clock rate -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20260107-ipq5018-cmn-pll-rate-v1-6-9ab50c40cb32@outlook.com> - -In IPQ5018, the reference clock to the CMN PLL block from the on-board -Wi-Fi has its divider set to 2. This divider wasn't taken into -consideration when calculating the CMN PLL clock rate which meant the -resulting clock rate was doubled. - -With the reference clock divider being accounted for in the driver, -correct the assigned clock rate to 4.8GHz. - -Fixes: c006b249c544 ("arm64: dts: ipq5018: Add CMN PLL node") -Signed-off-by: George Moussalem ---- - arch/arm64/boot/dts/qcom/ipq5018.dtsi | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi -+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -269,7 +269,7 @@ - "sys"; - #clock-cells = <1>; - assigned-clocks = <&cmn_pll IPQ5018_CMN_PLL_CLK>; -- assigned-clock-rates-u64 = /bits/ 64 <9600000000>; -+ assigned-clock-rates-u64 = /bits/ 64 <4800000000>; - }; - - qfprom: qfprom@a0000 { diff --git a/target/linux/qualcommax/patches-6.12/0913-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch b/target/linux/qualcommax/patches-6.12/0913-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch deleted file mode 100644 index e9c9c5ba1d..0000000000 --- a/target/linux/qualcommax/patches-6.12/0913-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ6018.patch +++ /dev/null @@ -1,33 +0,0 @@ -From: John Crispin -Date: Wed, 05 Mar 2026 12:00:00 +0100 -Subject: dt-bindings: clock: qcom: Add CMN PLL support for IPQ6018 - -The CMN PLL block in the IPQ6018 SoC takes 48 MHz as the reference -input clock. Its output clocks are the bias_pll_cc_clk (300 MHz) and -bias_pll_nss_noc_clk (416.5 MHz) clocks used by the networking -subsystem. - -Signed-off-by: John Crispin ---- - include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - create mode 100644 include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h - ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,ipq6018-cmn-pll.h -@@ -0,0 +1,15 @@ -+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -+/* -+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H -+#define _DT_BINDINGS_CLK_QCOM_IPQ6018_CMN_PLL_H -+ -+/* CMN PLL core clock. */ -+#define IPQ6018_CMN_PLL_CLK 0 -+ -+/* The output clocks from CMN PLL of IPQ6018. */ -+#define IPQ6018_BIAS_PLL_CC_CLK 1 -+#define IPQ6018_BIAS_PLL_NSS_NOC_CLK 2 -+#endif diff --git a/target/linux/qualcommax/patches-6.12/0915-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch b/target/linux/qualcommax/patches-6.12/0915-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch deleted file mode 100644 index 8857524dec..0000000000 --- a/target/linux/qualcommax/patches-6.12/0915-dt-bindings-clock-qcom-Add-CMN-PLL-support-for-IPQ8074.patch +++ /dev/null @@ -1,33 +0,0 @@ -From: John Crispin -Date: Wed, 05 Mar 2026 12:00:00 +0100 -Subject: dt-bindings: clock: qcom: Add CMN PLL support for IPQ8074 - -The CMN PLL block in the IPQ8074 SoC takes 48 MHz as the reference -input clock. Its output clocks are the bias_pll_cc_clk (300 MHz) and -bias_pll_nss_noc_clk (416.5 MHz) clocks used by the networking -subsystem. - -Signed-off-by: John Crispin ---- - include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - create mode 100644 include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h - ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,ipq8074-cmn-pll.h -@@ -0,0 +1,15 @@ -+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ -+/* -+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H -+#define _DT_BINDINGS_CLK_QCOM_IPQ8074_CMN_PLL_H -+ -+/* CMN PLL core clock. */ -+#define IPQ8074_CMN_PLL_CLK 0 -+ -+/* The output clocks from CMN PLL of IPQ8074. */ -+#define IPQ8074_BIAS_PLL_CC_CLK 1 -+#define IPQ8074_BIAS_PLL_NSS_NOC_CLK 2 -+#endif From ad1de384bf93d44503a13cb3983f35001dd511ef Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Wed, 15 Jul 2026 13:09:55 +0400 Subject: [PATCH 003/152] qualcommax: ipq50xx: backport upstreamed patches for IPQ5018 PHY clocks Backport upstreamed patches for IPQ5018 PHY RX and TX clocks support. Signed-off-by: George Moussalem Link: https://github.com/openwrt/openwrt/pull/24238 Signed-off-by: Robert Marko --- ...-Add-IPQ5018-Internal-GE-PHY-support.patch | 90 +++++++++++++++++++ ...phy-increase-max-clock-count-to-two.patch} | 19 ++-- ...ca-ar803x-Add-clocks-for-IPQ5018-PHY.patch | 66 ++++++++++++++ ...-TX-clock-management-for-IPQ5018-PHY.patch | 51 +++++++++++ ...-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch} | 16 ++-- ...qcom-ipq5018-add-nodes-to-bringup-q6.patch | 4 +- ...-tx-clock-management-for-ipq5018-phy.patch | 54 ----------- 7 files changed, 223 insertions(+), 77 deletions(-) create mode 100644 target/linux/qualcommax/patches-6.12/0086-v6.17-dt-bindings-net-qca-ar803x-Add-IPQ5018-Internal-GE-PHY-support.patch rename target/linux/qualcommax/patches-6.12/{0740-01-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch => 0087-v7.2-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch} (64%) create mode 100644 target/linux/qualcommax/patches-6.12/0088-v7.2-dt-bindings-net-qca-ar803x-Add-clocks-for-IPQ5018-PHY.patch create mode 100644 target/linux/qualcommax/patches-6.12/0089-v7.2-net-phy-at803x-add-RX-and-TX-clock-management-for-IPQ5018-PHY.patch rename target/linux/qualcommax/patches-6.12/{0740-03-arm64-qcom-ipq5018-add-gephy-rx-and-tx-clocks.patch => 0090-v7.3-arm64-qcom-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch} (66%) delete mode 100644 target/linux/qualcommax/patches-6.12/0740-04-net-phy-at803x-add-rx-and-tx-clock-management-for-ipq5018-phy.patch diff --git a/target/linux/qualcommax/patches-6.12/0086-v6.17-dt-bindings-net-qca-ar803x-Add-IPQ5018-Internal-GE-PHY-support.patch b/target/linux/qualcommax/patches-6.12/0086-v6.17-dt-bindings-net-qca-ar803x-Add-IPQ5018-Internal-GE-PHY-support.patch new file mode 100644 index 0000000000..52ff962cf4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0086-v6.17-dt-bindings-net-qca-ar803x-Add-IPQ5018-Internal-GE-PHY-support.patch @@ -0,0 +1,90 @@ +From 82eaf94d69fce20f8859a2b8dae8e7064d9343da Mon Sep 17 00:00:00 2001 +From: George Moussalem +Date: Fri, 13 Jun 2025 05:55:07 +0400 +Subject: [PATCH] dt-bindings: net: qca,ar803x: Add IPQ5018 Internal GE PHY + support + +Document the IPQ5018 Internal Gigabit Ethernet PHY found in the IPQ5018 +SoC. Its output pins provide an MDI interface to either an external +switch in a PHY to PHY link scenario or is directly attached to an RJ45 +connector. + +The PHY supports 10/100/1000 mbps link modes, CDT, auto-negotiation and +802.3az EEE. + +For operation, the LDO controller found in the IPQ5018 SoC for which +there is provision in the mdio-4019 driver. + +Two common archictures across IPQ5018 boards are: +1. IPQ5018 PHY --> MDI --> RJ45 connector +2. IPQ5018 PHY --> MDI --> External PHY +In a phy to phy architecture, the DAC needs to be configured to +accommodate for the short cable length. As such, add an optional boolean +property so the driver sets preset DAC register values accordingly. + +Signed-off-by: George Moussalem +Reviewed-by: Krzysztof Kozlowski +Link: https://patch.msgid.link/20250613-ipq5018-ge-phy-v5-1-9af06e34ea6b@outlook.com +Signed-off-by: Jakub Kicinski +--- + .../devicetree/bindings/net/qca,ar803x.yaml | 43 +++++++++++++++++++ + 1 file changed, 43 insertions(+) + +--- a/Documentation/devicetree/bindings/net/qca,ar803x.yaml ++++ b/Documentation/devicetree/bindings/net/qca,ar803x.yaml +@@ -16,8 +16,37 @@ description: | + + allOf: + - $ref: ethernet-phy.yaml# ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - ethernet-phy-id004d.d0c0 ++ ++ then: ++ properties: ++ reg: ++ const: 7 # This PHY is always at MDIO address 7 in the IPQ5018 SoC ++ ++ resets: ++ items: ++ - description: ++ GE PHY MISC reset which triggers a reset across MDC, DSP, RX, and TX lines. ++ ++ qcom,dac-preset-short-cable: ++ description: ++ Set if this phy is connected to another phy to adjust the values for ++ MDAC and EDAC to adjust amplitude, bias current settings, and error ++ detection and correction algorithm to accommodate for short cable length. ++ If not set, DAC values are not modified and it is assumed the MDI output pins ++ of this PHY are directly connected to an RJ45 connector. ++ type: boolean + + properties: ++ compatible: ++ enum: ++ - ethernet-phy-id004d.d0c0 ++ + qca,clk-out-frequency: + description: Clock output frequency in Hertz. + $ref: /schemas/types.yaml#/definitions/uint32 +@@ -132,3 +161,17 @@ examples: + }; + }; + }; ++ - | ++ #include ++ ++ mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ge_phy: ethernet-phy@7 { ++ compatible = "ethernet-phy-id004d.d0c0"; ++ reg = <7>; ++ ++ resets = <&gcc GCC_GEPHY_MISC_ARES>; ++ }; ++ }; diff --git a/target/linux/qualcommax/patches-6.12/0740-01-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch b/target/linux/qualcommax/patches-6.12/0087-v7.2-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch similarity index 64% rename from target/linux/qualcommax/patches-6.12/0740-01-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch rename to target/linux/qualcommax/patches-6.12/0087-v7.2-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch index 57285d0595..73d6975eca 100644 --- a/target/linux/qualcommax/patches-6.12/0740-01-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch +++ b/target/linux/qualcommax/patches-6.12/0087-v7.2-dt-bindings-net-ethernet-phy-increase-max-clock-count-to-two.patch @@ -1,11 +1,8 @@ -From 009bdd7a91990982e5b3bab4001318dfe5f6f575 Mon Sep 17 00:00:00 2001 +From 19e00bdfbb1b5f7a2d1f3c1c38b3da25e17ea486 Mon Sep 17 00:00:00 2001 From: George Moussalem -Date: Sun, 07 Jun 2026 13:00:06 +0400 -Subject: [PATCH v4 1/4] dt-bindings: net: ethernet-phy: increase max clock count to two -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20260607-ipq5018-gephy-clocks-v4-1-15b3a5ba7460@outlook.com> +Date: Mon, 8 Jun 2026 09:09:16 +0400 +Subject: [PATCH] dt-bindings: net: ethernet-phy: increase max clock count to + two The clocks property has a restriction to maximum one. Yet, some PHYs may require more than 1 clock such as the IPQ5018 PHY @@ -14,12 +11,8 @@ two. Reviewed-by: Rob Herring (Arm) Signed-off-by: George Moussalem ---- -Commit 350b7a258f20 introduced the clocks property with a restriction to -maximum 1 to the main ethernet-phy.yaml binding for Realtek to add an -optional external clock source. This is restrictive to all PHY bindings, -as some PHYs may require more than 1 clock such as the IPQ5018 PHY which -requires 2 clocks (for RX and TX). +Link: https://patch.msgid.link/20260608-ipq5018-gephy-clocks-v4-1-fb2ccd56894b@outlook.com +Signed-off-by: Jakub Kicinski --- Documentation/devicetree/bindings/net/ethernet-phy.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/linux/qualcommax/patches-6.12/0088-v7.2-dt-bindings-net-qca-ar803x-Add-clocks-for-IPQ5018-PHY.patch b/target/linux/qualcommax/patches-6.12/0088-v7.2-dt-bindings-net-qca-ar803x-Add-clocks-for-IPQ5018-PHY.patch new file mode 100644 index 0000000000..d410a24187 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0088-v7.2-dt-bindings-net-qca-ar803x-Add-clocks-for-IPQ5018-PHY.patch @@ -0,0 +1,66 @@ +From fea4ae4b5b5059612d1a4f5acb88c27a5f7e60dc Mon Sep 17 00:00:00 2001 +From: George Moussalem +Date: Mon, 8 Jun 2026 09:09:17 +0400 +Subject: [PATCH] dt-bindings: net: qca,ar803x: Add clocks for IPQ5018 PHY + +Further testing revealed that the RX and TX clocks of the IPQ5018 PHY +need to be explicitly enabled. As such, add the required clocks to the +schema. + +Acked-by: Conor Dooley +Signed-off-by: George Moussalem +Link: https://patch.msgid.link/20260608-ipq5018-gephy-clocks-v4-2-fb2ccd56894b@outlook.com +Signed-off-by: Jakub Kicinski +--- + .../devicetree/bindings/net/qca,ar803x.yaml | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +--- a/Documentation/devicetree/bindings/net/qca,ar803x.yaml ++++ b/Documentation/devicetree/bindings/net/qca,ar803x.yaml +@@ -28,6 +28,16 @@ allOf: + reg: + const: 7 # This PHY is always at MDIO address 7 in the IPQ5018 SoC + ++ clocks: ++ items: ++ - description: RX clock ++ - description: TX clock ++ ++ clock-names: ++ items: ++ - const: rx ++ - const: tx ++ + resets: + items: + - description: +@@ -42,6 +52,11 @@ allOf: + of this PHY are directly connected to an RJ45 connector. + type: boolean + ++ required: ++ - clocks ++ - clock-names ++ - resets ++ + properties: + compatible: + enum: +@@ -162,6 +177,7 @@ examples: + }; + }; + - | ++ #include + #include + + mdio { +@@ -172,6 +188,9 @@ examples: + compatible = "ethernet-phy-id004d.d0c0"; + reg = <7>; + ++ clocks = <&gcc GCC_GEPHY_RX_CLK>, ++ <&gcc GCC_GEPHY_TX_CLK>; ++ clock-names = "rx", "tx"; + resets = <&gcc GCC_GEPHY_MISC_ARES>; + }; + }; diff --git a/target/linux/qualcommax/patches-6.12/0089-v7.2-net-phy-at803x-add-RX-and-TX-clock-management-for-IPQ5018-PHY.patch b/target/linux/qualcommax/patches-6.12/0089-v7.2-net-phy-at803x-add-RX-and-TX-clock-management-for-IPQ5018-PHY.patch new file mode 100644 index 0000000000..4a43b52549 --- /dev/null +++ b/target/linux/qualcommax/patches-6.12/0089-v7.2-net-phy-at803x-add-RX-and-TX-clock-management-for-IPQ5018-PHY.patch @@ -0,0 +1,51 @@ +From cf6077e4903ffed2291f5f3cb9d61b29abe456c4 Mon Sep 17 00:00:00 2001 +From: George Moussalem +Date: Mon, 8 Jun 2026 09:09:19 +0400 +Subject: [PATCH] net: phy: at803x: add RX and TX clock management for IPQ5018 + PHY + +Acquire and enable the RX and TX clocks for the IPQ5018 PHY. +These clocks are required for the PHY's datapath to function correctly. + +Signed-off-by: George Moussalem +Link: https://patch.msgid.link/20260608-ipq5018-gephy-clocks-v4-4-fb2ccd56894b@outlook.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/phy/qcom/at803x.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/net/phy/qcom/at803x.c ++++ b/drivers/net/phy/qcom/at803x.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1089,6 +1090,7 @@ static void ipq5018_link_change_notify(s + static int ipq5018_probe(struct phy_device *phydev) + { + struct device *dev = &phydev->mdio.dev; ++ struct clk *rx_clk, *tx_clk; + struct ipq5018_priv *priv; + int ret; + +@@ -1099,6 +1101,16 @@ static int ipq5018_probe(struct phy_devi + priv->set_short_cable_dac = of_property_read_bool(dev->of_node, + "qcom,dac-preset-short-cable"); + ++ rx_clk = devm_clk_get_enabled(dev, "rx"); ++ if (IS_ERR(rx_clk)) ++ return dev_err_probe(dev, PTR_ERR(rx_clk), ++ "failed to get and enable RX clock\n"); ++ ++ tx_clk = devm_clk_get_enabled(dev, "tx"); ++ if (IS_ERR(tx_clk)) ++ return dev_err_probe(dev, PTR_ERR(tx_clk), ++ "failed to get and enable TX clock\n"); ++ + priv->rst = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(priv->rst)) + return dev_err_probe(dev, PTR_ERR(priv->rst), diff --git a/target/linux/qualcommax/patches-6.12/0740-03-arm64-qcom-ipq5018-add-gephy-rx-and-tx-clocks.patch b/target/linux/qualcommax/patches-6.12/0090-v7.3-arm64-qcom-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch similarity index 66% rename from target/linux/qualcommax/patches-6.12/0740-03-arm64-qcom-ipq5018-add-gephy-rx-and-tx-clocks.patch rename to target/linux/qualcommax/patches-6.12/0090-v7.3-arm64-qcom-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch index 0e2aed9697..b77a83403c 100644 --- a/target/linux/qualcommax/patches-6.12/0740-03-arm64-qcom-ipq5018-add-gephy-rx-and-tx-clocks.patch +++ b/target/linux/qualcommax/patches-6.12/0090-v7.3-arm64-qcom-ipq5018-Add-GEPHY-RX-and-TX-clocks.patch @@ -1,24 +1,24 @@ -From ea59840c0b6839c65eaf39887a0762dfc4993f50 Mon Sep 17 00:00:00 2001 +From 20959cefc27e7e2e506b4e789b728e5702c77f95 Mon Sep 17 00:00:00 2001 From: George Moussalem -Date: Sun, 07 Jun 2026 13:00:08 +0400 -Subject: [PATCH v4 3/4] arm64: qcom: ipq5018: Add GEPHY RX and TX clocks -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20260607-ipq5018-gephy-clocks-v4-3-15b3a5ba7460@outlook.com> +Date: Mon, 8 Jun 2026 09:09:18 +0400 +Subject: arm64: qcom: ipq5018: Add GEPHY RX and TX clocks Add RX and TX clocks for the IPQ5018 GEPHY to enable the datapath. Fixes: f5f2b835e316 ("arm64: dts: qcom: ipq5018: Add GE PHY to internal mdio bus") Reviewed-by: Dmitry Baryshkov Signed-off-by: George Moussalem +Link: https://lore.kernel.org/r/20260608-ipq5018-gephy-clocks-v4-3-fb2ccd56894b@outlook.com +Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/ipq5018.dtsi | 3 +++ 1 file changed, 3 insertions(+) +(limited to 'arch/arm64/boot/dts/qcom/ipq5018.dtsi') + --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi -@@ -234,6 +234,9 @@ +@@ -229,6 +229,9 @@ compatible = "ethernet-phy-id004d.d0c0"; reg = <7>; diff --git a/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch b/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch index 7620bb53e1..ee1c5738fd 100644 --- a/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch +++ b/target/linux/qualcommax/patches-6.12/0189-arm64-dts-qcom-ipq5018-add-nodes-to-bringup-q6.patch @@ -28,7 +28,7 @@ Signed-off-by: George Moussalem }; soc: soc@0 { -@@ -731,6 +736,41 @@ +@@ -734,6 +739,41 @@ }; }; @@ -70,7 +70,7 @@ Signed-off-by: George Moussalem pcie1: pcie@80000000 { compatible = "qcom,pcie-ipq5018"; reg = <0x80000000 0xf1d>, -@@ -1005,4 +1045,28 @@ +@@ -1008,4 +1048,28 @@ , ; }; diff --git a/target/linux/qualcommax/patches-6.12/0740-04-net-phy-at803x-add-rx-and-tx-clock-management-for-ipq5018-phy.patch b/target/linux/qualcommax/patches-6.12/0740-04-net-phy-at803x-add-rx-and-tx-clock-management-for-ipq5018-phy.patch deleted file mode 100644 index de781809f5..0000000000 --- a/target/linux/qualcommax/patches-6.12/0740-04-net-phy-at803x-add-rx-and-tx-clock-management-for-ipq5018-phy.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 929c734775fc0e4f576727b47f8a07d7e5cabb4f Mon Sep 17 00:00:00 2001 -From: George Moussalem -Date: Sun, 07 Jun 2026 13:00:09 +0400 -Subject: [PATCH v4 4/4] net: phy: at803x: add RX and TX clock management for IPQ5018 PHY -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit -Message-Id: <20260607-ipq5018-gephy-clocks-v4-4-15b3a5ba7460@outlook.com> - -Acquire and enable the RX and TX clocks for the IPQ5018 PHY. -These clocks are required for the PHY's datapath to function correctly. - -Fixes: d46502279a11 ("net: phy: qcom: at803x: Add Qualcomm IPQ5018 Internal PHY support") -Signed-off-by: George Moussalem ---- - drivers/net/phy/qcom/at803x.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - ---- a/drivers/net/phy/qcom/at803x.c -+++ b/drivers/net/phy/qcom/at803x.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -176,6 +177,8 @@ struct at803x_context { - }; - - struct ipq5018_priv { -+ struct clk *rx_clk; -+ struct clk *tx_clk; - struct reset_control *rst; - bool set_short_cable_dac; - }; -@@ -1099,6 +1102,16 @@ static int ipq5018_probe(struct phy_devi - priv->set_short_cable_dac = of_property_read_bool(dev->of_node, - "qcom,dac-preset-short-cable"); - -+ priv->rx_clk = devm_clk_get_enabled(dev, "rx"); -+ if (IS_ERR(priv->rx_clk)) -+ return dev_err_probe(dev, PTR_ERR(priv->rx_clk), -+ "failed to get and enable RX clock\n"); -+ -+ priv->tx_clk = devm_clk_get_enabled(dev, "tx"); -+ if (IS_ERR(priv->tx_clk)) -+ return dev_err_probe(dev, PTR_ERR(priv->tx_clk), -+ "failed to get and enable TX clock\n"); -+ - priv->rst = devm_reset_control_array_get_exclusive(dev); - if (IS_ERR(priv->rst)) - return dev_err_probe(dev, PTR_ERR(priv->rst), From 146d0fb6e02b4b37519075531841806d2e8ff87f Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 10 Jul 2026 08:48:48 +0000 Subject: [PATCH 004/152] realtek: pcs: rtl93xx: make cmu_page getter generic Rename rtpcs_931x_sds_cmu_page_get() to rtpcs_93xx_sds_get_cmu_page() and move it next to the other 93xx-shared helpers, so RTL930X can reuse it instead of duplicating the same hw_mode-to-CMU-page switch. Also fix the wrong -ENOTSUPP return to -EOPNOTSUPP. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 1e71c84e82..5bf464e144 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -1618,6 +1618,31 @@ static void rtpcs_93xx_sds_fill_caps(struct rtpcs_serdes *sds) } } +static int rtpcs_93xx_sds_get_cmu_page(enum rtpcs_sds_mode hw_mode) +{ + switch (hw_mode) { + case RTPCS_SDS_MODE_SGMII: + case RTPCS_SDS_MODE_1000BASEX: + return PAGE_ANA_1G2; + case RTPCS_SDS_MODE_2500BASEX: + return PAGE_ANA_3G1; + case RTPCS_SDS_MODE_QSGMII: + return PAGE_ANA_5G0; + // return PAGE_ANA_6G0; + case RTPCS_SDS_MODE_XSGMII: + case RTPCS_SDS_MODE_USXGMII_10GSXGMII: + case RTPCS_SDS_MODE_USXGMII_10GDXGMII: + case RTPCS_SDS_MODE_USXGMII_10GQXGMII: + case RTPCS_SDS_MODE_USXGMII_5GSXGMII: + case RTPCS_SDS_MODE_USXGMII_5GDXGMII: + case RTPCS_SDS_MODE_USXGMII_2_5GSXGMII: + case RTPCS_SDS_MODE_10GBASER: + return PAGE_ANA_10G; + default: + return -EOPNOTSUPP; + } +} + /* RTL930X */ /* This mapping is not coherent so it cannot be expressed arithmetically */ @@ -3235,36 +3260,11 @@ static void rtpcs_931x_sds_rx_reset(struct rtpcs_serdes *sds) msleep(50); } -static int rtpcs_931x_sds_cmu_page_get(enum rtpcs_sds_mode hw_mode) -{ - switch (hw_mode) { - case RTPCS_SDS_MODE_SGMII: - case RTPCS_SDS_MODE_1000BASEX: - return PAGE_ANA_1G2; - case RTPCS_SDS_MODE_2500BASEX: - return PAGE_ANA_3G1; - case RTPCS_SDS_MODE_QSGMII: - return PAGE_ANA_5G0; - // return PAGE_ANA_6G0; - case RTPCS_SDS_MODE_XSGMII: - case RTPCS_SDS_MODE_USXGMII_10GSXGMII: - case RTPCS_SDS_MODE_USXGMII_10GDXGMII: - case RTPCS_SDS_MODE_USXGMII_10GQXGMII: - case RTPCS_SDS_MODE_USXGMII_5GSXGMII: - case RTPCS_SDS_MODE_USXGMII_5GDXGMII: - case RTPCS_SDS_MODE_USXGMII_2_5GSXGMII: - case RTPCS_SDS_MODE_10GBASER: - return PAGE_ANA_10G; - default: - return -ENOTSUPP; - } -} - static int rtpcs_931x_sds_get_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type *pll) { int cmu_page, pll_sel; - cmu_page = rtpcs_931x_sds_cmu_page_get(sds->hw_mode); + cmu_page = rtpcs_93xx_sds_get_cmu_page(sds->hw_mode); if (cmu_page < 0) return cmu_page; @@ -3283,7 +3283,7 @@ static int rtpcs_931x_sds_set_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sd int cmu_page, ret, val; int frc_lc_mode_bit; - cmu_page = rtpcs_931x_sds_cmu_page_get(hw_mode); + cmu_page = rtpcs_93xx_sds_get_cmu_page(hw_mode); if (cmu_page < 0) return cmu_page; From 8eaa84d2e9b0c9880bb124163eae5fa5a1c8db31 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 10 Jul 2026 08:56:57 +0000 Subject: [PATCH 005/152] realtek: pcs: rtl930x: use cmu_page getter for tx_config Derive the CMU page from rtpcs_93xx_sds_get_cmu_page() instead of hardcoding it per hw_mode case. Also change the function to return int and propagate errors instead of silently doing nothing. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 5bf464e144..7f188b2fcd 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -2020,8 +2020,8 @@ static int rtpcs_930x_sds_activate(struct rtpcs_serdes *sds) return rtpcs_sds_write_bits(sds, PAGE_TGR_STD_0, MII_BMCR, 11, 11, 0); /* BMCR_PDOWN */ } -static void rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, - enum rtpcs_sds_mode hw_mode) +static int rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, + enum rtpcs_sds_mode hw_mode) { /* parameters: rtl9303_80G_txParam_s2 */ enum rtpcs_page page; @@ -2031,6 +2031,14 @@ static void rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, int post_amp = 0x2; int pre_en = 0x1; int post_en = 0x1; + int ret; + + ret = rtpcs_93xx_sds_get_cmu_page(hw_mode); + if (ret < 0) + return ret; + + /* TX config happens on extended CMU page */ + page = ret + 1; switch (hw_mode) { case RTPCS_SDS_MODE_1000BASEX: @@ -2038,13 +2046,11 @@ static void rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, pre_amp = 0x1; main_amp = 0x9; post_amp = 0x1; - page = PAGE_ANA_1G2_EXT; break; case RTPCS_SDS_MODE_2500BASEX: pre_amp = 0; post_amp = 0x8; pre_en = 0; - page = PAGE_ANA_3G1_EXT; break; case RTPCS_SDS_MODE_10GBASER: case RTPCS_SDS_MODE_USXGMII_10GSXGMII: @@ -2055,13 +2061,12 @@ static void rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, main_amp = 0x10; post_amp = 0; post_en = 0; - page = PAGE_ANA_10G_EXT; break; case RTPCS_SDS_MODE_QSGMII: - return; + return 0; default: pr_err("%s: unsupported SerDes hw mode\n", __func__); - return; + return -EOPNOTSUPP; } rtpcs_sds_write_bits(sds, page, 0x01, 15, 11, pre_amp); @@ -2070,6 +2075,7 @@ static void rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, rtpcs_sds_write_bits(sds, page, 0x07, 3, 3, post_en); rtpcs_sds_write_bits(sds, page, 0x07, 8, 4, main_amp); rtpcs_sds_write_bits(sds, page, 0x18, 15, 12, impedance); + return 0; } static int rtpcs_930x_sds_set_debug(struct rtpcs_serdes *sds, unsigned int debug_sel) @@ -2995,8 +3001,7 @@ static int rtpcs_930x_sds_config_media(struct rtpcs_serdes *sds, enum rtpcs_sds_ */ rtpcs_sds_write_bits(sds, PAGE_WDIG, 11, 1, 1, 1); - rtpcs_930x_sds_tx_config(sds, hw_mode); - return 0; + return rtpcs_930x_sds_tx_config(sds, hw_mode); } static int rtpcs_930x_sds_post_config(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode) From bf2c9a5d8b061a60a6fc0ba9361785941a56dace Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Mon, 13 Jul 2026 12:17:50 +0000 Subject: [PATCH 006/152] realtek: pcs: rtl930x: only apply TX config for 10G SerDes So far, there was only a misleading and confusing gate to avoid applying the TX config to SerDes which do not need it or where it even breaks some modes. This is achieved by not applying any configuration for QSGMII mode. While this fits the real-world cases we know so far where QSGMII is only used on SerDes 0/1 but never on others, it implicates that this is true too for QSGMII on 10G SerDes. Looking at the SDK, this assumption doesn't hold. To fix this, drop out of config_media in case a non-10G SerDes is delivered. The SDK does the same in various ways, ending up with the config just being applied for SerDes 2 - 9. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 7f188b2fcd..16199f47fb 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -2994,6 +2994,9 @@ static int rtpcs_930x_sds_config_hw_mode(struct rtpcs_serdes *sds, enum rtpcs_sd static int rtpcs_930x_sds_config_media(struct rtpcs_serdes *sds, enum rtpcs_sds_media media, enum rtpcs_sds_mode hw_mode) { + if (sds->type != RTPCS_SDS_TYPE_10G) + return 0; + /* * dal_longan_construct_mac_default_10gmedia_fiber: set medium to fiber. * TODO: this is unconditional regardless of hw_mode; needs mode-aware From 4212598948459f7e96aed13b491a7a6ab111e9d2 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 10 Jul 2026 13:36:19 +0000 Subject: [PATCH 007/152] realtek: pcs: rtl930x: classify SGMII as PHY-attached media SGMII always terminates in a PHY, whether on-board or embedded in an SFP copper module, so it belongs with QSGMII/XSGMII/USXGMII rather than the direct fiber/DAC modes. The PHY equalizes on its own far-end receiver in either case, so no media-dependent TX tuning is needed. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 16199f47fb..201f45a425 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -581,13 +581,18 @@ static int rtpcs_sds_select_media(enum rtpcs_sds_mode hw_mode, enum rtpcs_sds_me case RTPCS_SDS_MODE_OFF: *media = RTPCS_SDS_MEDIA_NONE; break; - case RTPCS_SDS_MODE_SGMII: case RTPCS_SDS_MODE_1000BASEX: case RTPCS_SDS_MODE_2500BASEX: case RTPCS_SDS_MODE_10GBASER: *media = RTPCS_SDS_MEDIA_FIBER; break; default: + /* + * SGMII, QSGMII, XSGMII and USXGMII always run into a PHY, be + * it on-board or embedded in an SFP copper module; either way + * the SerDes-to-PHY trace is short and the PHY equalizes for + * itself on the far end. + */ *media = RTPCS_SDS_MEDIA_PCB; break; } From 5f6896209dc506d38fc98d6b5b3eebc57bdbec7d Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 10 Jul 2026 13:45:43 +0000 Subject: [PATCH 008/152] realtek: pcs: rtl93xx: rename MEDIA_PCB to MEDIA_PHY Every SerDes lane runs over some PCB trace before reaching the port, even fiber/DAC ones, so PCB doesn't describe what actually distinguishes this case: the SerDes terminates directly into a PHY chip rather than a fiber module or DAC cable. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 201f45a425..455684c760 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -202,7 +202,7 @@ enum rtpcs_sds_media { RTPCS_SDS_MEDIA_FIBER, RTPCS_SDS_MEDIA_DAC_SHORT, /* < 3m */ RTPCS_SDS_MEDIA_DAC_LONG, /* >= 3m */ - RTPCS_SDS_MEDIA_PCB, + RTPCS_SDS_MEDIA_PHY, }; enum rtpcs_sds_pll_type { @@ -593,7 +593,7 @@ static int rtpcs_sds_select_media(enum rtpcs_sds_mode hw_mode, enum rtpcs_sds_me * the SerDes-to-PHY trace is short and the PHY equalizes for * itself on the far end. */ - *media = RTPCS_SDS_MEDIA_PCB; + *media = RTPCS_SDS_MEDIA_PHY; break; } @@ -2530,7 +2530,7 @@ static void rtpcs_930x_sds_rxcal_leq_adapt_lock(struct rtpcs_serdes *sds) /* * Empirical correction based on media type. * Direct SerDes connections get a base offset of +3; DAC cables add further - * correction for their attenuation. PHY-attached (PCB) needs none. + * correction for their attenuation. PHY-attached needs none. */ switch (sds->media) { case RTPCS_SDS_MEDIA_FIBER: From 8d2bd202c4899c6dfbf8037b8ae2c80dabe49cb3 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 10 Jul 2026 13:53:01 +0000 Subject: [PATCH 009/152] realtek: pcs: rtl93xx: rename media to attachment "media" doesn't fit well once PHY sits alongside FIBER/DAC_SHORT/ DAC_LONG as a value: a PHY isn't a transmission medium, whereas what actually matters here is what electrically terminates the SerDes lane on the other end. Rename the enum, struct field, ops vtable member, and both variants' config functions accordingly. Calling it 'attachment' better fits what it actually is, doesn't confuse with the variants and also fits to the PMA (Physical Medium Attachment) sublayer that lives below PCS. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 97 ++++++++++--------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 455684c760..3b69844c79 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -197,12 +197,12 @@ enum rtpcs_sds_mode { RTPCS_SDS_MODE_MAX, }; -enum rtpcs_sds_media { - RTPCS_SDS_MEDIA_NONE, - RTPCS_SDS_MEDIA_FIBER, - RTPCS_SDS_MEDIA_DAC_SHORT, /* < 3m */ - RTPCS_SDS_MEDIA_DAC_LONG, /* >= 3m */ - RTPCS_SDS_MEDIA_PHY, +enum rtpcs_sds_attachment { + RTPCS_SDS_ATTACH_NONE, + RTPCS_SDS_ATTACH_FIBER, + RTPCS_SDS_ATTACH_DAC_SHORT, /* < 3m */ + RTPCS_SDS_ATTACH_DAC_LONG, /* >= 3m */ + RTPCS_SDS_ATTACH_PHY, }; enum rtpcs_sds_pll_type { @@ -259,9 +259,9 @@ struct rtpcs_sds_ops { int (*config_hw_mode)(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode); /* required: set hardware mode */ int (*set_hw_mode)(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode); - /* optional: configure media-specific parameters */ - int (*config_media)(struct rtpcs_serdes *sds, enum rtpcs_sds_media media, - enum rtpcs_sds_mode hw_mode); + /* optional: configure attachment-specific parameters */ + int (*config_attachment)(struct rtpcs_serdes *sds, enum rtpcs_sds_attachment attachment, + enum rtpcs_sds_mode hw_mode); /* optional: finalization that must follow power-up, e.g. RX calibration */ int (*post_config)(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode); }; @@ -295,7 +295,7 @@ struct rtpcs_serdes { s16 link_port[RTPCS_MAX_LINKS_PER_SDS]; enum rtpcs_sds_mode hw_mode; - enum rtpcs_sds_media media; + enum rtpcs_sds_attachment attachment; u8 id; u8 num_of_links; bool first_start; @@ -575,16 +575,17 @@ static int rtpcs_sds_select_hw_mode(struct rtpcs_serdes *sds, phy_interface_t if return 0; } -static int rtpcs_sds_select_media(enum rtpcs_sds_mode hw_mode, enum rtpcs_sds_media *media) +static int rtpcs_sds_select_attachment(enum rtpcs_sds_mode hw_mode, + enum rtpcs_sds_attachment *attachment) { switch (hw_mode) { case RTPCS_SDS_MODE_OFF: - *media = RTPCS_SDS_MEDIA_NONE; + *attachment = RTPCS_SDS_ATTACH_NONE; break; case RTPCS_SDS_MODE_1000BASEX: case RTPCS_SDS_MODE_2500BASEX: case RTPCS_SDS_MODE_10GBASER: - *media = RTPCS_SDS_MEDIA_FIBER; + *attachment = RTPCS_SDS_ATTACH_FIBER; break; default: /* @@ -593,7 +594,7 @@ static int rtpcs_sds_select_media(enum rtpcs_sds_mode hw_mode, enum rtpcs_sds_me * the SerDes-to-PHY trace is short and the PHY equalizes for * itself on the far end. */ - *media = RTPCS_SDS_MEDIA_PHY; + *attachment = RTPCS_SDS_ATTACH_PHY; break; } @@ -2502,9 +2503,9 @@ static void rtpcs_930x_sds_rxcal_leq_adapt_lock(struct rtpcs_serdes *sds) * PHY-attached ports the PHY handles its own equalization, so the SerDes LEQ is left * in auto-adapt and no correction offset is needed. */ - bool direct_serdes = sds->media == RTPCS_SDS_MEDIA_FIBER || - sds->media == RTPCS_SDS_MEDIA_DAC_SHORT || - sds->media == RTPCS_SDS_MEDIA_DAC_LONG; + bool direct_serdes = sds->attachment == RTPCS_SDS_ATTACH_FIBER || + sds->attachment == RTPCS_SDS_ATTACH_DAC_SHORT || + sds->attachment == RTPCS_SDS_ATTACH_DAC_LONG; u32 sum10 = 0, avg10; int i, val; @@ -2528,18 +2529,18 @@ static void rtpcs_930x_sds_rxcal_leq_adapt_lock(struct rtpcs_serdes *sds) avg10 = (sum10 / 10) + (((sum10 % 10) >= 5) ? 1 : 0); /* - * Empirical correction based on media type. + * Empirical correction based on attachment type. * Direct SerDes connections get a base offset of +3; DAC cables add further * correction for their attenuation. PHY-attached needs none. */ - switch (sds->media) { - case RTPCS_SDS_MEDIA_FIBER: + switch (sds->attachment) { + case RTPCS_SDS_ATTACH_FIBER: avg10 += 3; break; - case RTPCS_SDS_MEDIA_DAC_SHORT: + case RTPCS_SDS_ATTACH_DAC_SHORT: avg10 += 4; /* base 3 + 1 for short DAC */ break; - case RTPCS_SDS_MEDIA_DAC_LONG: + case RTPCS_SDS_ATTACH_DAC_LONG: avg10 += 6; /* base 3 + 3 for long DAC */ break; default: @@ -2996,8 +2997,9 @@ static int rtpcs_930x_sds_config_hw_mode(struct rtpcs_serdes *sds, enum rtpcs_sd return 0; } -static int rtpcs_930x_sds_config_media(struct rtpcs_serdes *sds, enum rtpcs_sds_media media, - enum rtpcs_sds_mode hw_mode) +static int rtpcs_930x_sds_config_attachment(struct rtpcs_serdes *sds, + enum rtpcs_sds_attachment attachment, + enum rtpcs_sds_mode hw_mode) { if (sds->type != RTPCS_SDS_TYPE_10G) return 0; @@ -3481,19 +3483,19 @@ static int rtpcs_931x_sds_config_tx_amps(struct rtpcs_serdes *sds, u8 pre_amp, u * rtpcs_931x_sds_config_tx - Configure static TX path parameters */ static int rtpcs_931x_sds_config_tx(struct rtpcs_serdes *sds, - enum rtpcs_sds_media sds_media) + enum rtpcs_sds_attachment attachment) { const struct rtpcs_sds_tx_config *tx_cfg; if (sds->type != RTPCS_SDS_TYPE_10G) return 0; - switch (sds_media) { - case RTPCS_SDS_MEDIA_DAC_SHORT: + switch (attachment) { + case RTPCS_SDS_ATTACH_DAC_SHORT: tx_cfg = &rtpcs_931x_sds_tx_cfg_sdac; break; - case RTPCS_SDS_MEDIA_DAC_LONG: + case RTPCS_SDS_ATTACH_DAC_LONG: tx_cfg = &rtpcs_931x_sds_tx_cfg_ldac; break; @@ -3515,14 +3517,15 @@ static int rtpcs_931x_sds_config_tx(struct rtpcs_serdes *sds, * rtpcs_931x_sds_config_rx - Configure static RX path parameters */ static int rtpcs_931x_sds_config_rx(struct rtpcs_serdes *sds, - enum rtpcs_sds_media sds_media) + enum rtpcs_sds_attachment attachment) { /* TODO: Put all static RX configuration here */ return 0; } -static int rtpcs_931x_sds_config_media(struct rtpcs_serdes *sds, enum rtpcs_sds_media sds_media, - enum rtpcs_sds_mode hw_mode) +static int rtpcs_931x_sds_config_attachment(struct rtpcs_serdes *sds, + enum rtpcs_sds_attachment attachment, + enum rtpcs_sds_mode hw_mode) { struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); bool is_dac, is_10g; @@ -3555,21 +3558,21 @@ static int rtpcs_931x_sds_config_media(struct rtpcs_serdes *sds, enum rtpcs_sds_ rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xf, 5, 0, 0x4); rtpcs_sds_write_bits(sds, PAGE_ANA_5G0, 0x12, 7, 6, 0x1); - if (sds_media == RTPCS_SDS_MEDIA_NONE) + if (attachment == RTPCS_SDS_ATTACH_NONE) return 0; /* config SerDes TX path (amps, impedance, etc.) */ - ret = rtpcs_931x_sds_config_tx(sds, sds_media); + ret = rtpcs_931x_sds_config_tx(sds, attachment); if (ret < 0) return ret; /* config SerDes RX path (LEQ, DFE, etc.) */ - ret = rtpcs_931x_sds_config_rx(sds, sds_media); + ret = rtpcs_931x_sds_config_rx(sds, attachment); if (ret < 0) return ret; - is_dac = (sds_media == RTPCS_SDS_MEDIA_DAC_SHORT || - sds_media == RTPCS_SDS_MEDIA_DAC_LONG); + is_dac = (attachment == RTPCS_SDS_ATTACH_DAC_SHORT || + attachment == RTPCS_SDS_ATTACH_DAC_LONG); is_10g = (hw_mode == RTPCS_SDS_MODE_10GBASER || hw_mode == RTPCS_SDS_MODE_XSGMII || rtpcs_sds_mode_is_usxgmii(hw_mode)); @@ -3578,14 +3581,14 @@ static int rtpcs_931x_sds_config_media(struct rtpcs_serdes *sds, enum rtpcs_sds_ rtpcs_sds_write_bits(sds, PAGE_ANA_5G0, 0x7, 15, 15, is_dac ? 0x1 : 0x0); rtpcs_sds_write_bits(sds, PAGE_ANA_MISC, 0x0, 11, 10, 0x3); - switch (sds_media) { - case RTPCS_SDS_MEDIA_DAC_SHORT: - case RTPCS_SDS_MEDIA_DAC_LONG: + switch (attachment) { + case RTPCS_SDS_ATTACH_DAC_SHORT: + case RTPCS_SDS_ATTACH_DAC_LONG: rtpcs_sds_write(sds, PAGE_ANA_COM, 0x19, 0xf0a5); /* from XS1930-10 SDK */ rtpcs_sds_write(even_sds, PAGE_ANA_10G, 0x8, 0x02a0); /* [10:7] impedance */ break; - case RTPCS_SDS_MEDIA_FIBER: + case RTPCS_SDS_ATTACH_FIBER: if (is_10g) rtpcs_sds_write_bits(sds, PAGE_ANA_10G, 0xf, 5, 0, 0x2); /* from DMS1250 SDK */ @@ -3888,7 +3891,7 @@ static int rtpcs_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, struct rtpcs_link *link = rtpcs_phylink_pcs_to_link(pcs); struct rtpcs_ctrl *ctrl = link->ctrl; struct rtpcs_serdes *sds = link->sds; - enum rtpcs_sds_media sds_media; + enum rtpcs_sds_attachment attachment; enum rtpcs_sds_mode hw_mode; int ret; @@ -3919,14 +3922,14 @@ static int rtpcs_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, if (ret < 0) return ret; - if (sds->ops->config_media) { - ret = rtpcs_sds_select_media(hw_mode, &sds_media); + if (sds->ops->config_attachment) { + ret = rtpcs_sds_select_attachment(hw_mode, &attachment); if (ret < 0) return ret; - sds->media = sds_media; + sds->attachment = attachment; - ret = sds->ops->config_media(sds, sds_media, hw_mode); + ret = sds->ops->config_attachment(sds, attachment, hw_mode); if (ret < 0) return ret; } @@ -4315,7 +4318,7 @@ static const struct rtpcs_sds_ops rtpcs_930x_sds_ops = { .activate = rtpcs_930x_sds_activate, .config_hw_mode = rtpcs_930x_sds_config_hw_mode, .set_hw_mode = rtpcs_930x_sds_set_mode, - .config_media = rtpcs_930x_sds_config_media, + .config_attachment = rtpcs_930x_sds_config_attachment, .post_config = rtpcs_930x_sds_post_config, }; @@ -4362,7 +4365,7 @@ static const struct rtpcs_sds_ops rtpcs_931x_sds_ops = { .activate = rtpcs_931x_sds_activate, .config_hw_mode = rtpcs_931x_sds_config_hw_mode, .set_hw_mode = rtpcs_931x_sds_set_mode, - .config_media = rtpcs_931x_sds_config_media, + .config_attachment = rtpcs_931x_sds_config_attachment, }; static const struct rtpcs_sds_regs rtpcs_931x_sds_regs = { From f8afbb637befb49009e39f1cd5e7e6693aca6caf Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Mon, 13 Jul 2026 10:50:21 +0000 Subject: [PATCH 010/152] realtek: pcs: rtl93xx: commit attachment only after successful config sds->attachment was written right after selection, before config_attachment() ran, so a failed config call would leave it reflecting a value that was never actually applied to hardware. Move the assignment after the call succeeds, matching how sds->hw_mode is only committed after set_hw_mode() succeeds. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 3b69844c79..8c3e39ab86 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3927,11 +3927,11 @@ static int rtpcs_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, if (ret < 0) return ret; - sds->attachment = attachment; - ret = sds->ops->config_attachment(sds, attachment, hw_mode); if (ret < 0) return ret; + + sds->attachment = attachment; } ret = sds->ops->set_hw_mode(sds, hw_mode); From 629c5c8b11d5a2cb88a8b7b185ecd9851bdbdbbd Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 10 Jul 2026 09:13:40 +0000 Subject: [PATCH 011/152] realtek: pcs: rtl930x: refactor TX config Replace the on-stack, hardcoded pre/main/post-amp locals with static const rtpcs_sds_tx_config data, one struct per speed class (1g, 2g5, 10g), instead of duplicating the same literals inline on every call. Move the decision logic about which values are applied to the config_media function. The tx_config should only be about what is set, decision logic is handled one layer above. This also prepares further refactoring. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 105 ++++++++++-------- 1 file changed, 57 insertions(+), 48 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 8c3e39ab86..33db840a4c 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -349,6 +349,7 @@ struct rtpcs_sds_tx_config { u8 pre_amp; u8 main_amp; u8 post_amp; + u8 impedance; }; /* Calculation helpers */ @@ -1678,6 +1679,18 @@ static const struct reg_field rtpcs_930x_usxgmii_submode_fields[] = { [7] = REG_FIELD(RTPCS_930X_SDS_SUBMODE_CTRL_1, 25, 29), }; +static const struct rtpcs_sds_tx_config rtpcs_930x_sds_tx_config_1g = { + .pre_amp = 0x1, .main_amp = 0x9, .post_amp = 0x1, .impedance = 0x8 +}; + +static const struct rtpcs_sds_tx_config rtpcs_930x_sds_tx_config_2g5 = { + .main_amp = 0x9, .post_amp = 0x8, .impedance = 0x8 +}; + +static const struct rtpcs_sds_tx_config rtpcs_930x_sds_tx_config_10g = { + .main_amp = 0x10, .impedance = 0x8 +}; + /* * RTL930X needs a special mapping from logic SerDes ID to physical SerDes ID, * which takes the page into account. This applies to most of read/write calls. @@ -2026,17 +2039,10 @@ static int rtpcs_930x_sds_activate(struct rtpcs_serdes *sds) return rtpcs_sds_write_bits(sds, PAGE_TGR_STD_0, MII_BMCR, 11, 11, 0); /* BMCR_PDOWN */ } -static int rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, - enum rtpcs_sds_mode hw_mode) +static int rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode, + const struct rtpcs_sds_tx_config *tx_conf) { - /* parameters: rtl9303_80G_txParam_s2 */ enum rtpcs_page page; - int impedance = 0x8; - int pre_amp = 0x2; - int main_amp = 0x9; - int post_amp = 0x2; - int pre_en = 0x1; - int post_en = 0x1; int ret; ret = rtpcs_93xx_sds_get_cmu_page(hw_mode); @@ -2046,42 +2052,23 @@ static int rtpcs_930x_sds_tx_config(struct rtpcs_serdes *sds, /* TX config happens on extended CMU page */ page = ret + 1; - switch (hw_mode) { - case RTPCS_SDS_MODE_1000BASEX: - case RTPCS_SDS_MODE_SGMII: - pre_amp = 0x1; - main_amp = 0x9; - post_amp = 0x1; - break; - case RTPCS_SDS_MODE_2500BASEX: - pre_amp = 0; - post_amp = 0x8; - pre_en = 0; - break; - case RTPCS_SDS_MODE_10GBASER: - case RTPCS_SDS_MODE_USXGMII_10GSXGMII: - case RTPCS_SDS_MODE_USXGMII_10GQXGMII: - case RTPCS_SDS_MODE_XSGMII: - pre_en = 0; - pre_amp = 0; - main_amp = 0x10; - post_amp = 0; - post_en = 0; - break; - case RTPCS_SDS_MODE_QSGMII: - return 0; - default: - pr_err("%s: unsupported SerDes hw mode\n", __func__); - return -EOPNOTSUPP; - } + ret = rtpcs_sds_write_bits(sds, page, 0x18, 15, 12, tx_conf->impedance); + if (!ret) + ret = rtpcs_sds_write_bits(sds, page, 0x07, 8, 4, tx_conf->main_amp); - rtpcs_sds_write_bits(sds, page, 0x01, 15, 11, pre_amp); - rtpcs_sds_write_bits(sds, page, 0x06, 4, 0, post_amp); - rtpcs_sds_write_bits(sds, page, 0x07, 0, 0, pre_en); - rtpcs_sds_write_bits(sds, page, 0x07, 3, 3, post_en); - rtpcs_sds_write_bits(sds, page, 0x07, 8, 4, main_amp); - rtpcs_sds_write_bits(sds, page, 0x18, 15, 12, impedance); - return 0; + /* pre-amp */ + if (!ret) + ret = rtpcs_sds_write_bits(sds, page, 0x01, 15, 11, tx_conf->pre_amp); + if (!ret) + ret = rtpcs_sds_write_bits(sds, page, 0x07, 0, 0, tx_conf->pre_amp ? 1 : 0); + + /* post-amp */ + if (!ret) + ret = rtpcs_sds_write_bits(sds, page, 0x06, 4, 0, tx_conf->post_amp); + if (!ret) + ret = rtpcs_sds_write_bits(sds, page, 0x07, 3, 3, tx_conf->post_amp ? 1 : 0); + + return ret; } static int rtpcs_930x_sds_set_debug(struct rtpcs_serdes *sds, unsigned int debug_sel) @@ -3001,17 +2988,39 @@ static int rtpcs_930x_sds_config_attachment(struct rtpcs_serdes *sds, enum rtpcs_sds_attachment attachment, enum rtpcs_sds_mode hw_mode) { + const struct rtpcs_sds_tx_config *tx_cfg; + int ret; + if (sds->type != RTPCS_SDS_TYPE_10G) return 0; /* * dal_longan_construct_mac_default_10gmedia_fiber: set medium to fiber. - * TODO: this is unconditional regardless of hw_mode; needs mode-aware - * handling. + * TODO: does this apply to all fiber modes or only 10GBase-R? */ - rtpcs_sds_write_bits(sds, PAGE_WDIG, 11, 1, 1, 1); + ret = rtpcs_sds_write_bits(sds, PAGE_WDIG, 11, 1, 1, 1); + if (ret < 0) + return ret; - return rtpcs_930x_sds_tx_config(sds, hw_mode); + switch (hw_mode) { + case RTPCS_SDS_MODE_1000BASEX: + case RTPCS_SDS_MODE_SGMII: + tx_cfg = &rtpcs_930x_sds_tx_config_1g; + break; + case RTPCS_SDS_MODE_2500BASEX: + tx_cfg = &rtpcs_930x_sds_tx_config_2g5; + break; + case RTPCS_SDS_MODE_10GBASER: + case RTPCS_SDS_MODE_XSGMII: + case RTPCS_SDS_MODE_USXGMII_10GSXGMII: + case RTPCS_SDS_MODE_USXGMII_10GQXGMII: + tx_cfg = &rtpcs_930x_sds_tx_config_10g; + break; + default: + return -EOPNOTSUPP; + } + + return rtpcs_930x_sds_tx_config(sds, hw_mode, tx_cfg); } static int rtpcs_930x_sds_post_config(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode) From 7f063b1f95d2d1163897a808fa74753df39e1707 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 10 Jul 2026 13:38:15 +0000 Subject: [PATCH 012/152] realtek: pcs: rtl930x: gate TX tuning on attachment and baud rate TX amp/pre-emphasis tuning depends on the SerDes' real baud rate class rather than hw_mode, so key config_attachment's dispatch on that instead. At 1G/2.5G, attachment doesn't change meaningful: even a non-adaptive fiber receiver needs little compensation on a short trace, so PHY-attached and fiber links already use the same config there. At 10G, real channel loss is high enough that it matters: PHY-attached links equalize on the PHY's own far-end receiver and need less amp drive, while genuine fiber/DAC links, which have no adaptive receiver downstream, need the full config. Real DAC-vs-fiber detection doesn't exist yet, so both of those still share one config for now. Make use of the PLL speed enum for now, though it is a bit blurry and may not represent the real baud rate in some cases, e.g. QSGMII (1.25G PLL with multiplier -> 5G). Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 33db840a4c..5fe7119318 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -1691,6 +1691,17 @@ static const struct rtpcs_sds_tx_config rtpcs_930x_sds_tx_config_10g = { .main_amp = 0x10, .impedance = 0x8 }; +/* + * PHY-attached 10G-class links (XSGMII, USXGMII): weaker than the fiber/DAC + * value above since the far-end PHY equalizes on its own. main_amp reuses + * the 1G/2.5G fiber override's value, also the vendor SDK's genuine-fiber + * (non-DAC) 10G value; a non-adaptive receiver needing no more than this + * should be a safe upper bound for a PHY that equalizes on top. + */ +static const struct rtpcs_sds_tx_config rtpcs_930x_sds_tx_config_phy = { + .main_amp = 0x9, .impedance = 0x8 +}; + /* * RTL930X needs a special mapping from logic SerDes ID to physical SerDes ID, * which takes the page into account. This applies to most of read/write calls. @@ -2989,6 +3000,7 @@ static int rtpcs_930x_sds_config_attachment(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode) { const struct rtpcs_sds_tx_config *tx_cfg; + enum rtpcs_sds_pll_speed speed; int ret; if (sds->type != RTPCS_SDS_TYPE_10G) @@ -3002,19 +3014,26 @@ static int rtpcs_930x_sds_config_attachment(struct rtpcs_serdes *sds, if (ret < 0) return ret; - switch (hw_mode) { - case RTPCS_SDS_MODE_1000BASEX: - case RTPCS_SDS_MODE_SGMII: + ret = rtpcs_sds_select_pll_speed(hw_mode, &speed); + if (ret < 0) + return ret; + + switch (speed) { + case RTPCS_SDS_PLL_SPD_1000: tx_cfg = &rtpcs_930x_sds_tx_config_1g; break; - case RTPCS_SDS_MODE_2500BASEX: + case RTPCS_SDS_PLL_SPD_2500: tx_cfg = &rtpcs_930x_sds_tx_config_2g5; break; - case RTPCS_SDS_MODE_10GBASER: - case RTPCS_SDS_MODE_XSGMII: - case RTPCS_SDS_MODE_USXGMII_10GSXGMII: - case RTPCS_SDS_MODE_USXGMII_10GQXGMII: - tx_cfg = &rtpcs_930x_sds_tx_config_10g; + case RTPCS_SDS_PLL_SPD_10000: + /* + * PHY-attached links equalize on the PHY's own far-end + * receiver, less amp drive needed. Real DAC-vs-fiber + * detection doesn't exist yet, so both of those share + * the same config for now. + */ + tx_cfg = (attachment == RTPCS_SDS_ATTACH_PHY) ? &rtpcs_930x_sds_tx_config_phy + : &rtpcs_930x_sds_tx_config_10g; break; default: return -EOPNOTSUPP; From 0f1019db1d45f578221611d1900653863c60aa48 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Tue, 14 Jul 2026 13:16:42 +0000 Subject: [PATCH 013/152] realtek: pcs: rtl930x: gate WDIG 10G-mode bit on real baud rate This bit was set unconditionally for any 10G-capable SerDes regardless of hw_mode, including SGMII/1000BASEX/2500BASEX running on 10G-capable silicon. The vendor SDK's dal_longan_construct_mac_default_10gmedia_fiber gates it on the port's declared protocol class instead, applying it to PHY-attached XSGMII/USXGMII as well as genuine fiber - the "medium to fiber" naming doesn't match its actual condition. Gate on speed == RTPCS_SDS_PLL_SPD_10000 instead, matching the real 10G-class protocol family, and actively clear it for 1G/2.5G modes so a prior 10G-class configuration doesn't leave it stuck across a runtime downshift. 0 also matches this register's confirmed hardware reset default on an unconfigured SerDes. Link: https://github.com/openwrt/openwrt/pull/24232 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 5fe7119318..12f411db48 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3006,15 +3006,16 @@ static int rtpcs_930x_sds_config_attachment(struct rtpcs_serdes *sds, if (sds->type != RTPCS_SDS_TYPE_10G) return 0; - /* - * dal_longan_construct_mac_default_10gmedia_fiber: set medium to fiber. - * TODO: does this apply to all fiber modes or only 10GBase-R? - */ - ret = rtpcs_sds_write_bits(sds, PAGE_WDIG, 11, 1, 1, 1); + ret = rtpcs_sds_select_pll_speed(hw_mode, &speed); if (ret < 0) return ret; - ret = rtpcs_sds_select_pll_speed(hw_mode, &speed); + /* + * dal_longan_construct_mac_default_10gmedia_fiber: vendor sets this + * for any 10G-class port regardless of attachment, despite the + * name; 0 matches this register's hardware reset default. + */ + ret = rtpcs_sds_write_bits(sds, PAGE_WDIG, 11, 1, 1, speed == RTPCS_SDS_PLL_SPD_10000); if (ret < 0) return ret; From 2644302c14dfbae1c7c33e90836b426689a46fd6 Mon Sep 17 00:00:00 2001 From: Zoltan Szalay Date: Wed, 8 Jul 2026 00:57:34 +0200 Subject: [PATCH 014/152] ath79: fix u-boot-env nvmem-layout on enterasys,ws-ap3705i The u-boot-env0 partition on this board stores a redundant U-Boot environment (4-byte CRC32 + 1-byte flag + data), with the flag byte differing between the u-boot-env0/u-boot-env1 copies (0x00 vs 0x01). The actual environment size used by U-Boot for the CRC calculation is 0x1000 bytes, not the full 0x10000 byte partition. The DTS declared this as the plain, non-redundant "u-boot,env" layout with no env-size, so the kernel's u-boot-env nvmem-layout driver miscomputes the CRC and fails to probe: u-boot-env-layout ...nvmem-layout: Invalid calculated CRC32: 0xbc490a68 (expected: 0xfae56b95) u-boot-env-layout ...nvmem-layout: probe with driver u-boot-env-layout failed with error -22 Because the probe fails, the macaddr_uboot_ethaddr nvmem cell is never created, so eth0's nvmem-cells reference never resolves and the ag71xx-legacy platform device is stuck in "deferred probe pending" forever - the device boots with no functioning Ethernet interface at all. Switch to "u-boot,env-redundant-count" (matching the redundant-env layout already used on other boards from this vendor lineage, e.g. qca9557_extreme-networks_ws-ap3805i.dts) and set env-size to the verified 0x1000, which fixes CRC validation and lets eth0 probe successfully. Verified on real hardware: before the fix, eth0 never comes up (confirmed against the official 25.12.5 release, matching CRC values exactly); after the fix, eth0 links up normally and dmesg is free of any CRC/nvmem-layout errors. Signed-off-by: Zoltan Szalay Signed-off-by: Zoltan Szalay Link: https://github.com/openwrt/openwrt/pull/24134 Signed-off-by: Jonas Jelonek --- target/linux/ath79/dts/ar9344_enterasys_ws-ap3705i.dts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/linux/ath79/dts/ar9344_enterasys_ws-ap3705i.dts b/target/linux/ath79/dts/ar9344_enterasys_ws-ap3705i.dts index ca08284518..a81b0e9ef5 100644 --- a/target/linux/ath79/dts/ar9344_enterasys_ws-ap3705i.dts +++ b/target/linux/ath79/dts/ar9344_enterasys_ws-ap3705i.dts @@ -132,7 +132,8 @@ read-only; nvmem-layout { - compatible = "u-boot,env"; + compatible = "u-boot,env-redundant-count"; + env-size = <0x1000>; macaddr_uboot_ethaddr: ethaddr { }; From 867c857b93ae4376041975ebb6323ce0135a32bb Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Tue, 14 Jul 2026 15:57:10 +0800 Subject: [PATCH 015/152] kernel: disable pcrypt The obsolete pcrypt module is being removed upstream. Signed-off-by: Qingfang Deng Link: https://github.com/openwrt/openwrt/pull/24222 Signed-off-by: Jonas Jelonek --- target/linux/generic/config-6.12 | 2 +- target/linux/generic/config-6.18 | 2 +- target/linux/siflower/sf21/config-6.12 | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12 index e9e782af41..ca96112d1b 100644 --- a/target/linux/generic/config-6.12 +++ b/target/linux/generic/config-6.12 @@ -1215,7 +1215,7 @@ CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_NULL2=y # CONFIG_CRYPTO_PCBC is not set -CONFIG_CRYPTO_PCRYPT=y +# CONFIG_CRYPTO_PCRYPT is not set # CONFIG_CRYPTO_POLY1305 is not set # CONFIG_CRYPTO_POLY1305_ARM is not set # CONFIG_CRYPTO_POLY1305_MIPS is not set diff --git a/target/linux/generic/config-6.18 b/target/linux/generic/config-6.18 index 31b4ed01b1..3e1a57f9c7 100644 --- a/target/linux/generic/config-6.18 +++ b/target/linux/generic/config-6.18 @@ -1279,7 +1279,7 @@ CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y CONFIG_CRYPTO_NULL=y CONFIG_CRYPTO_NULL2=y # CONFIG_CRYPTO_PCBC is not set -CONFIG_CRYPTO_PCRYPT=y +# CONFIG_CRYPTO_PCRYPT is not set # CONFIG_CRYPTO_POLY1305 is not set # CONFIG_CRYPTO_POLY1305_ARM is not set # CONFIG_CRYPTO_POLY1305_NEON is not set diff --git a/target/linux/siflower/sf21/config-6.12 b/target/linux/siflower/sf21/config-6.12 index 624279f5ab..99089760e4 100644 --- a/target/linux/siflower/sf21/config-6.12 +++ b/target/linux/siflower/sf21/config-6.12 @@ -53,7 +53,6 @@ CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_GF128MUL=y CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1 CONFIG_CRYPTO_LIB_UTILS=y -# CONFIG_CRYPTO_PCRYPT is not set CONFIG_CRYPTO_ZSTD=y CONFIG_DCACHE_WORD_ACCESS=y CONFIG_DEBUG_BUGVERBOSE=y From 9e43544bc6ecea0e472ee6cc1bdf1e44ff199cdf Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 28 Oct 2024 12:41:53 -0700 Subject: [PATCH 016/152] mpc85xx: use regulator for USB power Use upstream solution for managing GPIO power for USB devices. A small patch is needed to make the whole thing work as this is an older USB driver that does not use the PHY framework. Signed-off-by: Rosen Penev Link: https://github.com/openwrt/openwrt/pull/16842 Signed-off-by: Jonas Jelonek --- .../arch/powerpc/boot/dts/tl-wdr4900-v1.dts | 20 ++++---- target/linux/mpc85xx/p1010/config-default | 1 + ...-fsl-mph-dr-of-add-regulator-support.patch | 48 +++++++++++++++++++ ...-fsl-mph-dr-of-add-regulator-support.patch | 48 +++++++++++++++++++ 4 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 target/linux/mpc85xx/patches-6.12/220-usb-fsl-mph-dr-of-add-regulator-support.patch create mode 100644 target/linux/mpc85xx/patches-6.18/220-usb-fsl-mph-dr-of-add-regulator-support.patch diff --git a/target/linux/mpc85xx/files/arch/powerpc/boot/dts/tl-wdr4900-v1.dts b/target/linux/mpc85xx/files/arch/powerpc/boot/dts/tl-wdr4900-v1.dts index c56a3a2191..082ea50284 100644 --- a/target/linux/mpc85xx/files/arch/powerpc/boot/dts/tl-wdr4900-v1.dts +++ b/target/linux/mpc85xx/files/arch/powerpc/boot/dts/tl-wdr4900-v1.dts @@ -119,6 +119,8 @@ phy_type = "utmi"; dr_mode = "host"; + vbus-supply = <®_power_usb>; + port@1 { #address-cells = <1>; #size-cells = <0>; @@ -435,15 +437,15 @@ }; }; - gpio_export { - compatible = "gpio-export"; - - usb-pwr { - gpio-export,name = "usb_pwr"; - gpio-export,output = <1>; - gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; - }; - }; + reg_power_usb: regulator { + compatible = "regulator-fixed"; + regulator-name = "power_usb"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpios = <&gpio0 10 GPIO_ACTIVE_HIGH>; + enable-active-high; + regulator-boot-on; + }; buttons { compatible = "gpio-keys"; diff --git a/target/linux/mpc85xx/p1010/config-default b/target/linux/mpc85xx/p1010/config-default index a300486087..5fcf7fd927 100644 --- a/target/linux/mpc85xx/p1010/config-default +++ b/target/linux/mpc85xx/p1010/config-default @@ -36,6 +36,7 @@ CONFIG_REALTEK_PHY=y CONFIG_RED_15W_REV1=y CONFIG_REGMAP=y CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_SGL_ALLOC=y CONFIG_SPI_BITBANG=y CONFIG_SPI_GPIO=y diff --git a/target/linux/mpc85xx/patches-6.12/220-usb-fsl-mph-dr-of-add-regulator-support.patch b/target/linux/mpc85xx/patches-6.12/220-usb-fsl-mph-dr-of-add-regulator-support.patch new file mode 100644 index 0000000000..c1cf528c8c --- /dev/null +++ b/target/linux/mpc85xx/patches-6.12/220-usb-fsl-mph-dr-of-add-regulator-support.patch @@ -0,0 +1,48 @@ +From 04c30383e668da328ceec9f10d6437a8aa9c5b8f Mon Sep 17 00:00:00 2001 +From: Rosen Penev +Date: Tue, 3 Dec 2024 13:53:31 -0800 +Subject: [PATCH] usb: fsl-mph-dr-of: add regulator support + +Some devices have a GPIO that controls power to the USB bus. Add +support for a vbus regulator to have the kernel control it automatically +instead of having to rely on userspace. + +Acquire the regulator in the common probe path so that it works for +all fsl-usb2-dr compatible controllers, not just MPC5121. + +Assisted-by: opencode:big-pickle +Signed-off-by: Rosen Penev +--- + drivers/usb/host/fsl-mph-dr-of.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/usb/host/fsl-mph-dr-of.c ++++ b/drivers/usb/host/fsl-mph-dr-of.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + struct fsl_usb2_dev_data { + char *dr_mode; /* controller mode */ +@@ -183,7 +184,7 @@ static int fsl_usb2_mph_dr_of_probe(stru + const struct of_device_id *match; + const unsigned char *prop; + static unsigned int idx; +- int i; ++ int i, err; + + if (!of_device_is_available(np)) + return -ENODEV; +@@ -246,6 +247,10 @@ static int fsl_usb2_mph_dr_of_probe(stru + } + } + ++ err = devm_regulator_get_enable_optional(&ofdev->dev, "vbus"); ++ if (err) ++ return dev_err_probe(&ofdev->dev, err, "failed to get vbus regulator\n"); ++ + for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { + if (!dev_data->drivers[i]) + continue; diff --git a/target/linux/mpc85xx/patches-6.18/220-usb-fsl-mph-dr-of-add-regulator-support.patch b/target/linux/mpc85xx/patches-6.18/220-usb-fsl-mph-dr-of-add-regulator-support.patch new file mode 100644 index 0000000000..c1cf528c8c --- /dev/null +++ b/target/linux/mpc85xx/patches-6.18/220-usb-fsl-mph-dr-of-add-regulator-support.patch @@ -0,0 +1,48 @@ +From 04c30383e668da328ceec9f10d6437a8aa9c5b8f Mon Sep 17 00:00:00 2001 +From: Rosen Penev +Date: Tue, 3 Dec 2024 13:53:31 -0800 +Subject: [PATCH] usb: fsl-mph-dr-of: add regulator support + +Some devices have a GPIO that controls power to the USB bus. Add +support for a vbus regulator to have the kernel control it automatically +instead of having to rely on userspace. + +Acquire the regulator in the common probe path so that it works for +all fsl-usb2-dr compatible controllers, not just MPC5121. + +Assisted-by: opencode:big-pickle +Signed-off-by: Rosen Penev +--- + drivers/usb/host/fsl-mph-dr-of.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/usb/host/fsl-mph-dr-of.c ++++ b/drivers/usb/host/fsl-mph-dr-of.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + struct fsl_usb2_dev_data { + char *dr_mode; /* controller mode */ +@@ -183,7 +184,7 @@ static int fsl_usb2_mph_dr_of_probe(stru + const struct of_device_id *match; + const unsigned char *prop; + static unsigned int idx; +- int i; ++ int i, err; + + if (!of_device_is_available(np)) + return -ENODEV; +@@ -246,6 +247,10 @@ static int fsl_usb2_mph_dr_of_probe(stru + } + } + ++ err = devm_regulator_get_enable_optional(&ofdev->dev, "vbus"); ++ if (err) ++ return dev_err_probe(&ofdev->dev, err, "failed to get vbus regulator\n"); ++ + for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { + if (!dev_data->drivers[i]) + continue; From b0a38a4c303359acfe14b9527f59469af6a57b3e Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 16 Jul 2026 11:22:57 +0200 Subject: [PATCH 017/152] qualcommbe: an7583: correct I2C controller compatible AN7583 DTS currently uses a non existant compatible that only exists in the Airoha SDK. It should just be changed to the generic MT7621 compatible as AN7581 uses as well. Signed-off-by: Robert Marko --- target/linux/airoha/dts/an7583.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/airoha/dts/an7583.dtsi b/target/linux/airoha/dts/an7583.dtsi index 33ac571ca3..2a4864018f 100644 --- a/target/linux/airoha/dts/an7583.dtsi +++ b/target/linux/airoha/dts/an7583.dtsi @@ -412,7 +412,7 @@ }; i2c0: i2c0@1fbf8000 { - compatible = "airoha,an7581-i2c"; + compatible = "mediatek,mt7621-i2c"; reg = <0x0 0x1fbf8000 0x0 0x100>; clocks = <&i2cclock>; @@ -426,7 +426,7 @@ }; i2c1: i2c1@1fbf8100 { - compatible = "airoha,an7581-i2c"; + compatible = "mediatek,mt7621-i2c"; reg = <0x0 0x1fbf8100 0x0 0x100>; clocks = <&i2cclock>; From 5882239031995b19181356350221ff0a3eceee33 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 16 Jul 2026 11:30:48 +0200 Subject: [PATCH 018/152] qualcommbe: i2c-an7581: depend only on target i2c-an7581 (In reality its just i2c-mt7621) should depend on the airoha target in general and not on an7581 as an7583 also uses it. Signed-off-by: Robert Marko --- target/linux/airoha/modules.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/linux/airoha/modules.mk b/target/linux/airoha/modules.mk index 3a038f57d0..e1adc94bfb 100644 --- a/target/linux/airoha/modules.mk +++ b/target/linux/airoha/modules.mk @@ -9,8 +9,7 @@ define KernelPackage/i2c-an7581 SUBMENU:=$(OTHER_MENU) $(call i2c_defaults,$(I2C_MT7621_MODULES),79) TITLE:=Airoha I2C Controller - DEPENDS:=+kmod-i2c-core \ - @(TARGET_airoha_an7581) + DEPENDS:=+kmod-i2c-core @TARGET_airoha endef define KernelPackage/i2c-an7581/description From 5625dadb4b3c7549104ae859503ba1d184883772 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 16 Jul 2026 11:37:44 +0200 Subject: [PATCH 019/152] airoha: an7583: update ethernet node for new PCS implementation With new PCS implementation, the pcs property needs to be updated to the new name of 'pcs-handle' to correctly work with PCS fwnode consumer/provider. Fixes: 4d5f579a8120 ("airoha: update PCS node in DTSI for new PCS implementation") Signed-off-by: Robert Marko --- target/linux/airoha/dts/an7583.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/airoha/dts/an7583.dtsi b/target/linux/airoha/dts/an7583.dtsi index 2a4864018f..59e48facad 100644 --- a/target/linux/airoha/dts/an7583.dtsi +++ b/target/linux/airoha/dts/an7583.dtsi @@ -671,7 +671,7 @@ gdm2: ethernet@2 { compatible = "airoha,eth-mac"; reg = <2>; - pcs = <&pon_pcs>; + pcs-handle = <&pon_pcs>; status = "disabled"; }; @@ -679,7 +679,7 @@ gdm3: ethernet@3 { compatible = "airoha,eth-mac"; reg = <3>; - pcs = <ð_pcs>; + pcs-handle = <ð_pcs>; airoha,gdm-srcport = <0x16>; status = "disabled"; From b92df1e8da528eda036c15237ac1ce047d4bbcea Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 12:23:19 +0200 Subject: [PATCH 020/152] hostapd: ucode: fix CSA bandwidth for 320 MHz and 80+80 channels switch_channel computed the CSA bandwidth as 40 << oper_chwidth, which mismaps oper_chwidth 3 (80+80, must be 80 MHz plus center_freq2) to 320 and 9 (320 MHz) to 20480. Map the operating channel width to the actual MHz bandwidth explicitly. Signed-off-by: Felix Fietkau --- .../services/hostapd/src/src/ap/ucode.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index e1a365b7a6..4db5eddf7c 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -739,10 +739,22 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs) intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL)); if (errno) intval = hostapd_get_oper_chwidth(conf); - if (intval) - csa.freq_params.bandwidth = 40 << intval; - else + switch (intval) { + case CONF_OPER_CHWIDTH_80MHZ: + case CONF_OPER_CHWIDTH_80P80MHZ: + /* 80+80 uses an 80 MHz primary segment plus center_freq2 */ + csa.freq_params.bandwidth = 80; + break; + case CONF_OPER_CHWIDTH_160MHZ: + csa.freq_params.bandwidth = 160; + break; + case CONF_OPER_CHWIDTH_320MHZ: + csa.freq_params.bandwidth = 320; + break; + default: csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20; + break; + } if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno) csa.freq_params.freq = intval; From c5c316b0ab35bc47fa267958206e0c2f746b7b40 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 12:23:50 +0200 Subject: [PATCH 021/152] hostapd: ucode: support 320 MHz in freq_info uc_wpa_freq_info() rejected any operating width above 160 MHz, returning null for EHT320 (width 3). Add the 320 MHz case; the existing segment-0 centre arithmetic (8 << width / 4 << width) extends correctly to width 3. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/src/src/utils/ucode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package/network/services/hostapd/src/src/utils/ucode.c b/package/network/services/hostapd/src/src/utils/ucode.c index b5fdf6676c..4eb50691fa 100644 --- a/package/network/services/hostapd/src/src/utils/ucode.c +++ b/package/network/services/hostapd/src/src/utils/ucode.c @@ -139,6 +139,9 @@ uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs) case 2: chanwidth = CONF_OPER_CHWIDTH_160MHZ; break; + case 3: + chanwidth = CONF_OPER_CHWIDTH_320MHZ; + break; default: return NULL; } From a2c5be278383887a785275b9ab247419a8d15f9e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 12:25:58 +0200 Subject: [PATCH 022/152] hostapd: ucode: compute 6 GHz segment centre in iface_freq_info On 6 GHz there is no HT Operation IE, so a 0 secondary-channel offset made freq_info() return without a segment centre frequency, leaving wide-channel switches (80/160/320 MHz) on the stale centre. Pass a null offset for wide 6 GHz channels so the centre is auto-derived. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.uc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index d7d0ff8ff2..1258f2d52f 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -130,6 +130,15 @@ function iface_freq_info(iface, config, params) if (freq < 4000) width = 0; + /* + * 6 GHz has no HT Operation IE, so the secondary channel offset cannot + * be derived the usual way. For wide channels pass a null offset so the + * C helper auto-derives it and computes the segment centre frequency; a + * 0 offset would make it skip the centre calculation. + */ + if (freq > 5925 && width > 0 && sec_offset == 0) + sec_offset = null; + return hostapd.freq_info(freq, sec_offset, width); } From 247595eac1698ff6bf4d2d2d327369a860c42cf6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 12:26:31 +0200 Subject: [PATCH 023/152] hostapd: ucode: parse frequency and STA-channel radio markers Lift the #frequency and #channel_owned_by_sta markers (emitted by the config generator) onto config.radio and keep them out of radio.data so they do not perturb the radio comparison. These feed the config-driven channel switch handling added next. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.uc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 1258f2d52f..943b881049 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -935,6 +935,16 @@ function iface_load_config(phy, radio, filename) continue; } + if (val[0] == "#frequency") { + config.radio.frequency = int(val[1]); + continue; + } + + if (val[0] == "#channel_follow") { + config.radio.channel_follow = int(val[1]) == 1; + continue; + } + if (val[0] == "#num_global_macaddr") config[substr(val[0], 1)] = int(val[1]); else if (val[0] == "#macaddr_base") From b0d8ab4fef7e1feb21d020f4aa5ff8b6d27913c3 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 12:26:55 +0200 Subject: [PATCH 024/152] wifi-scripts: emit frequency and STA-channel markers in hostapd config Emit #frequency (the target channel frequency, already computed during phy setup) and #channel_owned_by_sta (set when a co-located STA/mesh/adhoc on the same band dictates the runtime channel) into the generated config. The hostapd daemon uses these to decide whether a channel change can be applied via CSA instead of a full restart. Signed-off-by: Felix Fietkau --- .../wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh | 1 + .../wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh b/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh index fa924cb1db..5d3a99c7fc 100755 --- a/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh +++ b/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh @@ -256,6 +256,7 @@ function setup() { // fallthrough case 'sta': data.ap_start_disabled = true; + data.channel_follow = true; let config = supplicant.generate(supplicant_data, data, v); if (mode == "mesh") config_add_mesh_params(config, v.config); diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc index 64d52cb754..32b0b00775 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc @@ -589,6 +589,10 @@ export function setup(data) { append('\n#num_global_macaddr', data.config.num_global_macaddr); if (data.config.macaddr_base) append('\n#macaddr_base', data.config.macaddr_base); + if (data.config.frequency) + append('\n#frequency', data.config.frequency); + if (data.channel_follow) + append('\n#channel_follow', 1); let has_ap; for (let k, interface in data.interfaces) { From 55b9263596e2a35aa2b7389b926aff2128a8dc35 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 12:31:37 +0200 Subject: [PATCH 025/152] hostapd: ucode: apply channel/width changes via CSA instead of restart A config change that only alters the channel or channel width previously failed the radio comparison in iface_reload_config() and forced a full interface restart, disconnecting every client. Classify the radio diff: apply a channel/width-only change through iface.switch_channel() (CSA) so clients stay associated, keep the full restart for any other radio change. When a co-located STA on the same band owns the runtime channel (#channel_owned_by_sta), the change is a no-op on the AP side, which keeps following the STA via apsta_state. MLD APs and DFS targets fall back to a full restart, as a single iface-level CSA cannot handle them. Signed-off-by: Felix Fietkau --- .../network/services/hostapd/files/hostapd.uc | 107 +++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 943b881049..2eea5d731b 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -525,13 +525,118 @@ function get_config_bss(name, config, idx) return if_bss[ifname]; } +const radio_chan_fields = [ + "op_class", + "vht_oper_chwidth", "vht_oper_centr_freq_seg0_idx", + "he_oper_chwidth", "he_oper_centr_freq_seg0_idx", + "eht_oper_chwidth", "eht_oper_centr_freq_seg0_idx", +]; + +function radio_line_is_chan(line) +{ + return index(radio_chan_fields, split(line, "=", 2)[0]) >= 0; +} + +// The HT40+/- direction in ht_capab is channel-derived and re-applied through +// the CSA secondary-channel offset, so strip it when comparing the base config; +// the remaining ht_capab bits are device capabilities that require a restart. +function radio_base(radio) +{ + return map(filter(radio.data, (line) => !radio_line_is_chan(line)), (line) => { + if (substr(line, 0, 9) != "ht_capab=") + return line; + return replace(replace(line, "[HT40+]", ""), "[HT40-]", ""); + }); +} + +function radio_reload_class(old_radio, new_radio) +{ + if (is_equal(old_radio, new_radio)) + return "same"; + + // anything beyond channel/width, or the channel-follow flag flipping, + // needs a full restart + if (!is_equal(radio_base(old_radio), radio_base(new_radio)) || + (!old_radio.channel_follow) != (!new_radio.channel_follow)) + return "restart"; + + // base identical and apsta flag unchanged: only treat this as a channel + // switch if the channel or its derived width lines actually differ (the + // derived frequency field alone appearing is not a real change) + if (old_radio.channel == new_radio.channel && + is_equal(filter(old_radio.data, radio_line_is_chan), + filter(new_radio.data, radio_line_is_chan))) + return "same"; + + return "channel"; +} + +function iface_channel_is_dfs(radio) +{ + let freq = radio.frequency; + + /* 5 GHz DFS sub-bands; 2.4 GHz and 6 GHz have no radar channels */ + return freq && ((freq >= 5250 && freq <= 5330) || + (freq >= 5490 && freq <= 5730)); +} + +function iface_channel_switch(name, config) +{ + let radio = config.radio; + + /* + * The runtime channel is followed from a co-located supplicant + * interface (STA/mesh/adhoc) via apsta_state; the AP never picks its + * own channel here. Adopt the new fallback channel without touching the + * running BSSes. + */ + if (radio.channel_follow) + return true; + + /* + * A single iface-level CSA cannot coordinate the links of an MLD AP + * that span multiple radios, and DFS targets need CAC before use. + * Fall back to a full restart for both. + */ + for (let bss in config.bss) + if (bss.mld_ap) + return false; + + if (!radio.frequency || iface_channel_is_dfs(radio)) + return false; + + let iface = hostapd.interfaces[name]; + if (!iface || iface.state() != "ENABLED") + return false; + + let freq_info = iface_freq_info(iface, config, { frequency: radio.frequency }); + if (!freq_info) + return false; + + freq_info.csa_count = 10; + if (!iface.switch_channel(freq_info)) + return false; + + hostapd.printf(`Channel switch to ${radio.channel} on phy ${name}`); + return true; +} + function iface_reload_config(name, phydev, config, old_config) { let phy = phydev.name; - if (!old_config || !is_equal(old_config.radio, config.radio)) + if (!old_config) return false; + switch (radio_reload_class(old_config.radio, config.radio)) { + case "restart": + return false; + case "channel": + if (!iface_channel_switch(name, config)) + return false; + break; + } + if (is_equal(old_config.bss, config.bss)) return true; From c6d20bbf51996e21b7eb4688be0512d5032f236d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 13:13:32 +0200 Subject: [PATCH 026/152] hostapd: ucode: restart on config-initiated channel switch failure iface.switch_channel() only reports whether a CSA was started, not whether it completed, so a config-driven channel switch that the driver never finishes would leave the AP stuck without recovery. After issuing the CSA, arm a timer and, if the switch has not completed by the deadline (csa_in_progress still set), fall back to a full interface restart. The timer is armed only on the config-initiated path, so ubus- and apsta-triggered channel switches keep their own recovery. Add an iface.csa_in_progress() ucode method wrapping hostapd_csa_in_progress(). Signed-off-by: Felix Fietkau --- .../network/services/hostapd/files/hostapd.uc | 49 +++++++++++++++++++ .../services/hostapd/src/src/ap/ucode.c | 12 +++++ 2 files changed, 61 insertions(+) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 2eea5d731b..3bb016e5dc 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -1,4 +1,5 @@ let libubus = require("ubus"); +import * as uloop from "uloop"; import { open, readfile, access } from "fs"; import { wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open, wdev_set_radio_mask, wdev_set_up } from "common"; @@ -320,11 +321,22 @@ function iface_macaddr_init(phydev, config, macaddr_list) return phydev.macaddr_init(macaddr_list, macaddr_data); } +function csa_timer_cancel(name) +{ + let timers = hostapd.data.csa_timer; + if (timers && timers[name]) { + timers[name].cancel(); + delete timers[name]; + } +} + function iface_restart(phydev, config, old_config) { let phy = phydev.name; let pending = hostapd.data.pending_config[phy]; + csa_timer_cancel(phy); + if (pending) pending.abort(); @@ -580,6 +592,22 @@ function iface_channel_is_dfs(radio) (freq >= 5490 && freq <= 5730)); } +function iface_csa_check(name) +{ + if (hostapd.data.csa_timer) + delete hostapd.data.csa_timer[name]; + + let config = hostapd.data.config[name]; + let iface = hostapd.interfaces[name]; + if (!config || !iface || !iface.csa_in_progress()) + return; + + hostapd.printf(`Config channel switch on phy ${name} did not complete, restarting`); + let phydev = phy_open(config.phy, config.radio_idx); + if (phydev) + iface_restart(phydev, config, config); +} + function iface_channel_switch(name, config) { let radio = config.radio; @@ -618,6 +646,27 @@ function iface_channel_switch(name, config) return false; hostapd.printf(`Channel switch to ${radio.channel} on phy ${name}`); + + /* + * Verify the switch actually completes; if the driver never finishes it, + * fall back to a full restart. Armed only here, so ubus- or apsta- + * triggered channel switches are left to their own recovery. + */ + hostapd.data.csa_timer ??= {}; + csa_timer_cancel(name); + + let beacon_int = 100; + for (let line in config.radio.data) { + let m = match(line, /^beacon_int=([0-9]+)/); + if (m) { + beacon_int = int(m[1]); + break; + } + } + + hostapd.data.csa_timer[name] = uloop.timer(freq_info.csa_count * beacon_int + 2000, + () => iface_csa_check(name)); + return true; } diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index 4db5eddf7c..8af8d5cfde 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -769,6 +769,17 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs) return ucv_boolean_new(!ret); } +static uc_value_t * +uc_hostapd_iface_csa_in_progress(uc_vm_t *vm, size_t nargs) +{ + struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface"); + + if (!iface) + return NULL; + + return ucv_boolean_new(hostapd_csa_in_progress(iface)); +} + static uc_value_t * uc_hostapd_bss_rename(uc_vm_t *vm, size_t nargs) { @@ -1225,6 +1236,7 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces) { "stop", uc_hostapd_iface_stop }, { "start", uc_hostapd_iface_start }, { "switch_channel", uc_hostapd_iface_switch_channel }, + { "csa_in_progress", uc_hostapd_iface_csa_in_progress }, }; uc_value_t *data, *proto; From 06a7b0245faf2a75d20f374d57b958b1050e8e28 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 13:57:16 +0200 Subject: [PATCH 027/152] hostapd: ucode: handle null config in iface_set_config again config_reset and shutdown call iface_set_config(name) with no config, but the null-config guard was dropped in the MLO rework, so config.phy dereferenced null and threw: config_reset reset nothing and shutdown left hostapd-created interfaces behind. Restore the guard, removing the stale config entry and tearing the interface down via iface_config_remove. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.uc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 3bb016e5dc..4b85fd2863 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -1009,6 +1009,11 @@ function iface_set_config(name, config) { let old_config = hostapd.data.config[name]; + if (!config) { + delete hostapd.data.config[name]; + return iface_config_remove(name, old_config); + } + hostapd.data.config[name] = config; let phy = config.phy; From eb5ccabf0edc0ed88b29c8d3cbea7effc3ecd285 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 13:57:49 +0200 Subject: [PATCH 028/152] wifi-scripts: fix undeclared variable in service wifi-device injection The wifi-device injection path used object shorthand `name` where no such variable is in scope (the loop binds radio/config), so under the strict VM config_init threw and the whole wireless configuration for that reload was skipped. Use the radio key as the device name. Signed-off-by: Felix Fietkau --- .../network/config/wifi-scripts/files/lib/netifd/wireless.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc index fa2edbee12..41f48848e4 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc @@ -275,7 +275,7 @@ function config_init(uci) continue; dev = devices[radio] = { - name, + name: radio, config, vif: [], From f3ff33c8a91854d7beee075337c8816a48db16b3 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 13:58:10 +0200 Subject: [PATCH 029/152] hostapd: ucode: fix per-station PSK list parsing in sta_auth Two defects handling the psk array returned by an auth handler: strlen() ran before the string was validated (strlen(NULL) on a non-string entry), and the hex-PMK branch tested the array length instead of the entry length. A 64-character passphrase therefore reached the passphrase branch and its memcpy of str_len + 1 overflowed passphrase[MAX_PASSPHRASE_LEN + 1] by one byte. Validate the type first and branch on the string length. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/src/src/ap/ucode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index 8af8d5cfde..5e16293d40 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -863,13 +863,15 @@ int hostapd_ucode_sta_auth(struct hostapd_data *hapd, struct sta_info *sta) size_t str_len; cur_psk = ucv_array_get(cur, i); + if (ucv_type(cur_psk) != UC_STRING) + continue; str = ucv_string_get(cur_psk); str_len = strlen(str); - if (!str || str_len < 8 || str_len > 64) + if (str_len < 8 || str_len > 64) continue; p = os_zalloc(sizeof(*p)); - if (len == 64) { + if (str_len == 64) { if (hexstr2bin(str, p->psk, PMK_LEN) < 0) { free(p); continue; From 830ae67b44ef01f7d41c03e1296dd6d26acac2b9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 13:58:33 +0200 Subject: [PATCH 030/152] wifi-scripts: honour a cancelled setup when the handler reports up stop() and destroy() set cancel_setup while a setup handler is running, but nothing read it, so a device brought down (or removed) mid-setup still went up and stayed up, holding its interface links. Check the flag (and autostart/delete) in wdev_mark_up and tear the device down instead of completing the up transition. Signed-off-by: Felix Fietkau --- .../config/wifi-scripts/files/lib/netifd/wireless-device.uc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index f55fc31189..d32948c711 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -453,6 +453,12 @@ function wdev_mark_up(wdev) if (wdev.state != "setup") return; + if (wdev.cancel_setup || !wdev.autostart || wdev.delete) { + delete wdev.cancel_setup; + wdev.teardown(); + return 0; + } + for (let section, data in wdev.handler_data) { if (data.ifname) handle_link(data.ifname, data, true); From 5b47cec245babf51f946661e924e3be2800b95e2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 13:58:55 +0200 Subject: [PATCH 031/152] wifi-scripts: emit a raw 64-hex PSK unquoted for station mode A 64-character hex PSK was always quoted, but wpa_supplicant rejects a quoted passphrase longer than 63 characters, so the network block failed and the STA never associated while netifd still reported the radio up. Emit the key unquoted when it is a 64-character raw PMK, matching the AP path. The sae_password branch keeps quoting, as SAE passwords are arbitrary strings. Signed-off-by: Felix Fietkau --- .../wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc index 80c9bd91e7..9fc44fb4f2 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc @@ -141,6 +141,8 @@ function setup_sta(data, config) { if (config.mode == 'mesh' || config.auth_type == 'sae') config.sae_password = `"${config.key}"`; + else if (length(config.key) == 64) + config.psk = config.key; else config.psk = `"${config.key}"`; From e7cf46369be431966a6584aab44acdf9f76b99b4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 13:59:30 +0200 Subject: [PATCH 032/152] hostapd: ucode: clear start_disabled when reloading a started BSS On a radio shared with a STA every AP BSS is generated with start_disabled=1. A smart reload that takes the full bss.set_config() path restarts the BSS but never cleared start_disabled, so hostapd_start_beacon() skipped beaconing and the AP went silently quiet until an unrelated apsta_state event. Clear it when the BSS was already started, matching the add_bss path. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/src/src/ap/ucode.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index 5e16293d40..08a83c529c 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -297,6 +297,14 @@ uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs) started = hapd->started; __uc_hostapd_bss_stop(hapd); + /* + * start_disabled is only meaningful for the initial apsta bring-up. When + * re-applying config to a BSS that was already started, clear it so the + * restart keeps beaconing instead of silently going quiet. + */ + if (started) + conf->bss[idx]->start_disabled = 0; + old_bss = hapd->conf; for (i = 0; i < iface->conf->num_bss; i++) if (iface->conf->bss[i] == hapd->conf) From 5917907471fe972d0e33e9576a074a0e0f63fdab Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:19:48 +0200 Subject: [PATCH 033/152] wifi-scripts: do not report a disabled wifi-device as up A disabled wifi-device is intentionally still run through the setup path (with no interfaces) to reliably tear down hostapd/wpa_supplicant and to handle disabled/enabled flapping, so it reaches state "up" and status reported up: true alongside disabled: true. Exclude disabled devices from the up state in status instead. Signed-off-by: Felix Fietkau --- .../config/wifi-scripts/files/lib/netifd/wireless-device.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index d32948c711..2f16d26ff6 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -600,7 +600,7 @@ function status() }); } return { - up: this.state == "up", + up: this.state == "up" && !this.data.config.disabled, pending: this.state == "setup" || this.state == "teardown", autostart: this.autostart, disabled: !!this.data.config.disabled, From 28c6f5e6f7b80edd5a89f77594e9a6d46c2aa0ad Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:21:45 +0200 Subject: [PATCH 034/152] wifi-scripts: fix extended-feature bit test device_extended_features() used bitwise OR instead of AND, so every extended-feature query returned true regardless of the driver bitmap: ftm_responder and radar_background were always reported present. Signed-off-by: Felix Fietkau --- .../wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc index 32b0b00775..ce25ab483e 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/hostapd.uc @@ -474,7 +474,7 @@ function device_htmode_append(config) { } function device_extended_features(data, flag) { - return !!(data[flag / 8] | (1 << (flag % 8))); + return !!(data[flag / 8] & (1 << (flag % 8))); } function device_capabilities(config) { From f536d79fcd13f3f50a30adc387aa3e370dc08a69 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:43:40 +0200 Subject: [PATCH 035/152] hostapd: ucode: force restart on unresolvable static BSSID conflict The guard meant to bail to a full restart read `!mac_idx < 0`, which parses as `(!mac_idx) < 0` and is always false, so a static BSSID colliding with the first (or a foreign-owned) BSS fell through and reassigned the wrong BSS's address. Use `mac_idx <= 0`. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 4b85fd2863..98e8dc9f84 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -859,7 +859,7 @@ function iface_reload_config(name, phydev, config, old_config) // with the bssid of a reused interface. reassign the reused interface if (!bsscfg.default_macaddr) { // can't update bssid of the first BSS, need to restart - if (!mac_idx < 0) + if (mac_idx <= 0) return false; bsscfg = config.bss[mac_idx]; From 1a58efdb5d1c63ff242fe67da1ee3b15e5752e9d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:43:53 +0200 Subject: [PATCH 036/152] hostapd: ucode: fix dead reload-all check in mld_set_config new_config was tested against the freshly emptied new_mld object, so it was always false and the reload-all-interfaces block never ran. Test the incoming config instead, so the first MLD config re-adds the spliced-out MLD BSSes. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 98e8dc9f84..a923365156 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -1248,7 +1248,7 @@ function mld_set_config(config) let prev_mld = { ...hostapd.data.mld }; let new_mld = {}; let phy_list = {}; - let new_config = !length(prev_mld) && length(new_mld); + let new_config = !length(prev_mld) && length(config); hostapd.printf(`Set MLD config: ${keys(config)}`); From 39507969ff4f366d020d7634f2b2da85b5510f29 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:44:07 +0200 Subject: [PATCH 037/152] wifi-scripts: return the interface-creation result from wdev_add wdev_add() dropped the wdev_create() result, so every caller's error check was dead and an nl80211 NEW_INTERFACE failure was silently ignored (hostapd and wpa_supplicant then proceeded on a missing netdev with stale MLD bookkeeping). Return the result. Signed-off-by: Felix Fietkau --- .../config/wifi-scripts/files/usr/share/hostap/common.uc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc index f801c4940c..a3b4ae8248 100644 --- a/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/common.uc @@ -298,8 +298,7 @@ const phy_proto = { }, wdev_add: function(name, data) { - let phydev = this; - wdev_create(this.phy, name, { + return wdev_create(this.phy, name, { ...data, radio: this.radio, }); From 5379d034e08988d90617f80782ed13458959e236 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:44:25 +0200 Subject: [PATCH 038/152] wifi-scripts: fix VLAN key for service-injected wifi-iface Service data VLANs were stored under vif_data.vlans, but every consumer reads vif.vlan, so per-VLAN config from a procd service was silently dropped. Use the singular key, matching the adjacent station handling. Signed-off-by: Felix Fietkau --- .../network/config/wifi-scripts/files/lib/netifd/wireless.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc index 41f48848e4..d26e4efbcd 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless.uc @@ -337,7 +337,7 @@ function config_init(uci) sta: [] }; if (vif.vlans) - vif_data.vlans = vif.vlans; + vif_data.vlan = vif.vlans; if (vif.stations) vif_data.sta = vif.stations; push(dev.vif, vif_data); From fedefbf50f6aaea58ae8e3bfce76ff5230606ed1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:44:51 +0200 Subject: [PATCH 039/152] wifi-scripts: reset the retry counter on successful setup retry was only reset on config change/start, never on a successful setup, so occasional transient setup failures accumulated over a device lifetime and latched retry_setup_failed after three cumulative failures. Reset it when the device comes up. Signed-off-by: Felix Fietkau --- .../config/wifi-scripts/files/lib/netifd/wireless-device.uc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index 2f16d26ff6..d328746ea7 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -459,6 +459,8 @@ function wdev_mark_up(wdev) return 0; } + wdev_reset(wdev); + for (let section, data in wdev.handler_data) { if (data.ifname) handle_link(data.ifname, data, true); From dc33d7bcb1112490625c637511ae2c216edfe5f3 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:45:10 +0200 Subject: [PATCH 040/152] wifi-scripts: pass multicast_to_unicast as an integer handle_link() passed the option as a ucode boolean, but the netifd device_set binding reads it with prop_get_int (UC_INTEGER only) and ignored a boolean, so the option was a no-op. Convert it to 0/1. Signed-off-by: Felix Fietkau --- .../config/wifi-scripts/files/lib/netifd/wireless-device.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index d328746ea7..ddd91fccf7 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -61,7 +61,7 @@ function handle_link(dev, data, up) }; if (ap && config.multicast_to_unicast != null) - dev_data.multicast_to_unicast = config.multicast_to_unicast; + dev_data.multicast_to_unicast = config.multicast_to_unicast ? 1 : 0; if (data.type == "vif" && config.mode == "ap") { dev_data.wireless_proxyarp = !!config.proxy_arp; From 105dd239ac7026ef0e98a3f3136e42580e5e2699 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:45:38 +0200 Subject: [PATCH 041/152] wifi-scripts: do not treat WDS-station VLAN subinterfaces as the base wdev The hotplug match /(.+)\.sta.+/ also matched VLAN subinterfaces of a WDS station (e.g. wlan0.sta1.100 -> wlan0), so the tagged subinterface was added into the vif base networks, breaking VLAN separation. Anchor the pattern to the plain station suffix. Signed-off-by: Felix Fietkau --- .../config/wifi-scripts/files/lib/netifd/wireless-device.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index ddd91fccf7..a3359bc0af 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -544,7 +544,7 @@ function notify(req) function hotplug(name, add) { let dev = name; - let m = match(name, /(.+)\.sta.+/); + let m = match(name, /^(.+)\.sta[0-9]+$/); if (m) name = m[1]; From 0bab595808ed1e823e3dab4a1bae4955bbfd6355 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:46:09 +0200 Subject: [PATCH 042/152] wifi-scripts: map the configured pairwise cipher for station mode Only an exact GCMP cipher was mapped to the network block; other explicit ciphers (CCMP-256, GCMP-256, TKIP variants) left pairwise unset, so wpa_supplicant offered its default set and could not associate with an AP restricted to the configured cipher. Pass wpa_pairwise through, keeping the GCMP special case that also pins group. Signed-off-by: Felix Fietkau --- .../wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc index 9fc44fb4f2..a104538c09 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc @@ -192,6 +192,8 @@ function setup_sta(data, config) { if (config.wpa_pairwise == 'GCMP') { config.pairwise = 'GCMP'; config.group = 'GCMP'; + } else if (config.wpa_pairwise) { + config.pairwise = config.wpa_pairwise; } config.key_mgmt ??= 'NONE'; From 97e8911c9bdaf73579cfd7696bde48b2bb314954 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:46:37 +0200 Subject: [PATCH 043/152] wifi-scripts: pass device config to parse_encryption for station mode setup_sta() called parse_encryption() without the device config, so the sae-compat branch dereferenced a null dev_config.band and threw, aborting the whole radio setup. Pass the device config through. Signed-off-by: Felix Fietkau --- .../wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc index a104538c09..66496dee67 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc @@ -58,7 +58,7 @@ export function ratelist(rates) { }; function setup_sta(data, config) { - iface.parse_encryption(config); + iface.parse_encryption(config, data); if (config.auth_type in [ 'sae', 'owe', 'eap2', 'eap192', 'dpp' ]) config.ieee80211w = 2; From 06b3ef0ff5da4d791c962b5baf265f08c283cf26 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:46:58 +0200 Subject: [PATCH 044/152] wifi-scripts: use the base phy name for config_get_macaddr_list phydev.name is already radio-suffixed, and the hostapd handler re-applies the suffix, so on multi-radio phys the lookup missed and the returned MAC list was always empty, letting wdev.uc reuse a MAC hostapd had reserved for an AP BSS. Pass the base phy and radio index. Signed-off-by: Felix Fietkau --- .../network/config/wifi-scripts/files/usr/share/hostap/wdev.uc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc index a4f9bf7b84..883aa2640c 100644 --- a/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc @@ -142,7 +142,7 @@ const commands = { drop_inactive(config.data); let ubus = libubus.connect(); - let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phydev.name, radio: phydev.radio ?? -1 }); + let data = ubus.call("hostapd", "config_get_macaddr_list", { phy: phydev.phy, radio: int(phydev.radio ?? -1) }); let macaddr_list = []; if (type(data) == "object" && data.macaddr) macaddr_list = data.macaddr; From 139f14717fff276124d953d9a9852c922dd3b354 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:47:35 +0200 Subject: [PATCH 045/152] hostapd: ucode: pass phy to the bss_remove callback hostapd_ucode_free_bss() pushed only two arguments, but the bss_remove handler (like bss_add/bss_reload) takes three (phy, name, obj), so name received the bss resource: the DPP hook was never removed and the removal event was malformed. Push the phy as well. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/src/src/ap/ucode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index 08a83c529c..dbdc6ac874 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -1314,9 +1314,10 @@ void hostapd_ucode_free_bss(struct hostapd_data *hapd) if (wpa_ucode_call_prepare("bss_remove")) return; + uc_value_push(ucv_string_new(hapd->iface->phy)); uc_value_push(ucv_string_new(hapd->conf->iface)); uc_value_push(ucv_get(val)); - ucv_put(wpa_ucode_call(2)); + ucv_put(wpa_ucode_call(3)); ucv_put(val); } From b2755b0402f14d47ffa139f5882c4bb81b5721c0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:48:10 +0200 Subject: [PATCH 046/152] hostapd: ucode: initialise conf in iface.add_bss The early error path jumps to out: before conf is assigned, where hostapd_config_free(conf) frees an indeterminate pointer. Initialise it to NULL (hostapd_config_free tolerates NULL). Signed-off-by: Felix Fietkau --- package/network/services/hostapd/src/src/ap/ucode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index dbdc6ac874..a739fb9277 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -428,7 +428,7 @@ uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs) { struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface"); struct hostapd_bss_config *bss; - struct hostapd_config *conf; + struct hostapd_config *conf = NULL; struct hostapd_data *hapd; uc_value_t *file = uc_fn_arg(0); uc_value_t *index = uc_fn_arg(1); From dd128675c51304749849c40e6c84d4a720461aba Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:49:49 +0200 Subject: [PATCH 047/152] hostapd: ucode: unregister ucode/ubus objects when deleting an unstarted BSS hostapd_free_hapd_data() skips hostapd_ucode_free_bss()/hostapd_ubus_free_bss() for a bss that never started, so bss.delete() on such a bss freed hapd while the ucode registry entry and ubus object still referenced it. Call both explicitly before os_free(); they are idempotent for the started case. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/src/src/ap/ucode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index a739fb9277..64653c6ae2 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -380,6 +380,9 @@ uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs) hostapd_drv_stop_ap(hapd); hostapd_bss_deinit(hapd); + /* deinit skips these for a bss that never started; both are idempotent */ + hostapd_ucode_free_bss(hapd); + hostapd_ubus_free_bss(hapd); hostapd_remove_iface_bss_conf(iface->conf, hapd->conf); hostapd_config_free_bss(hapd->conf); #ifdef CONFIG_IEEE80211BE From 73c35f97f3a71b924a5500f61cae8f9d92e5fcbf Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:50:23 +0200 Subject: [PATCH 048/152] wifi-scripts: quote dpp_connector in the station config dpp_connector was emitted unquoted, but wpa_supplicant parses it as a string and hex-decodes an unquoted value; a base64url connector then failed to parse and the network block was rejected. Emit it via the quoted-string list. dpp_csign/dpp_netaccesskey stay raw, as they are hex. Signed-off-by: Felix Fietkau --- .../files-ucode/usr/share/ucode/wifi/supplicant.uc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc index 66496dee67..fe1d24fcb3 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/supplicant.uc @@ -234,6 +234,7 @@ function setup_sta(data, config) { 'domain_match', 'domain_match2', 'domain_suffix_match', 'domain_suffix_match2', 'private_key', 'private_key_passwd', 'private_key2', 'private_key2_passwd', + 'dpp_connector', ]); network_append_vars(config, [ 'rsn_overriding', 'scan_ssid', 'noscan', 'disabled', 'multi_ap_profile', 'multi_ap_backhaul_sta', @@ -242,7 +243,7 @@ function setup_sta(data, config) { 'disable_ht', 'disable_ht40', 'disable_vht', 'vht', 'max_oper_chwidth', 'ht40', 'beacon_int', 'ieee80211w', 'rates', 'mesh_basic_rates', 'mcast_rate', 'bssid_blacklist', 'bssid_whitelist', 'erp', 'eap', 'phase2', - 'dpp_connector', 'dpp_csign', 'dpp_netaccesskey', + 'dpp_csign', 'dpp_netaccesskey', ]); } From 26a4d5080669d5196e406541d7ac6c2d4d0a5044 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:51:32 +0200 Subject: [PATCH 049/152] hostapd: ucode: filter MLD loops by phy in phy_set_state/phy_status The per-wiphy radio-mask loops iterated all MLDs without checking the phy, so a stop/start or status query for one wiphy touched MLDs on another wiphy with the same radio index (clearing present bits, tearing links down, or reporting a foreign MLD). Skip MLDs that do not belong to the phy, matching mld_update_phy(). Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/wpa_supplicant.uc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc index 8cb9df24ab..9effed7017 100644 --- a/package/network/services/hostapd/files/wpa_supplicant.uc +++ b/package/network/services/hostapd/files/wpa_supplicant.uc @@ -552,6 +552,8 @@ let main_obj = { for (let ifname in phy.data) iface_stop(phy.data[ifname]); for (let name, data in wpas.data.mld) { + if (data.phy != phy.name) + continue; data.radio_mask_present &= ~radio_mask; if (data.radio_mask_up & radio_mask) mld_update_iface(name, data); @@ -561,6 +563,8 @@ let main_obj = { let found; for (let name, data in wpas.data.mld) { + if (data.phy != phy.name) + continue; if (!(data.radio_mask & radio_mask)) continue; data.radio_mask_present |= radio_mask; @@ -604,9 +608,12 @@ let main_obj = { let ifnames = keys(phy.data); let radio_mask = phy.radio != null ? (1 << phy.radio) : 0; - for (let name, data in wpas.data.mld) + for (let name, data in wpas.data.mld) { + if (data.phy != phy.name) + continue; if (data.radio_mask_up & radio_mask) push(ifnames, name); + } for (let ifname in ifnames) { try { From f3cbf551430316efcf0daad3a1786eed4ce1da3b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:52:38 +0200 Subject: [PATCH 050/152] hostapd: ucode: keep comment lines out of the config comparison data The generator emits a '# Setup interface: ' comment before each interface, so the first one landed in config.radio.data and made the radio comparison depend on the first BSS's ifname: renaming it forced a full radio restart instead of the rename path. Skip comment lines when parsing radio.data/bss.data. Signed-off-by: Felix Fietkau --- package/network/services/hostapd/files/hostapd.uc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index a923365156..919c05fcc2 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -1084,6 +1084,9 @@ function iface_load_config(phy, radio, filename) if (!val[0]) continue; + if (substr(line, 0, 2) == "# ") + continue; + if (val[0] == "interface") { bss = config_add_bss(config, val[1]); break; @@ -1124,6 +1127,9 @@ function iface_load_config(phy, radio, filename) if (!val[0]) continue; + if (substr(line, 0, 2) == "# ") + continue; + if (val[0] == "bssid") { bss.bssid = lc(val[1]); continue; From 56d466a75d112f481eecaccc3632b33f1b7b48dc Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:53:29 +0200 Subject: [PATCH 051/152] wifi-scripts: use country_code for iw reg set in setup_phy validate() converts the country alias to country_code and deletes country before setup_phy() runs, so the iw reg set branch tested a null config.country and never ran, leaving wdev-only radios (adhoc/mesh/monitor) on the world regdomain. Use config.country_code. Signed-off-by: Felix Fietkau --- .../files-ucode/lib/netifd/wireless/mac80211.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh b/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh index 5d3a99c7fc..a0aeed487a 100755 --- a/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh +++ b/package/network/config/wifi-scripts/files-ucode/lib/netifd/wireless/mac80211.sh @@ -70,9 +70,9 @@ function setup_phy(phy, config, data) { config.channel = +config.channel; config.frequency = get_channel_frequency(config.band, config.channel); - if (config.country) { - log(`Setting country code to ${config.country}`); - system(`iw reg set ${config.country}`); + if (config.country_code) { + log(`Setting country code to ${config.country_code}`); + system(`iw reg set ${config.country_code}`); } set_default(config, 'rxantenna', 0xffffffff); From 4062d625aea3bdb33eb5ad957e4602962505951e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 14:59:56 +0200 Subject: [PATCH 052/152] wifi-scripts: emit per-line hostapd options correctly append() space-joins list options into one line, which breaks hostapd options that are parsed one entry per line: anqp_elem, nai_realm, venue_name, venue_url, hs20_conn_capab and the radius req_attr lists (a parse error there fails the whole phy). Add append_list() to emit one line per element and route those options through it. vendor_elements is instead a single concatenated hex blob (join ''), and iface_interworking() no longer overwrites domain_name with the 3GPP cell-net data (and uses ';'). Signed-off-by: Felix Fietkau --- .../files-ucode/usr/share/ucode/wifi/ap.uc | 20 ++++++++++++------- .../usr/share/ucode/wifi/common.uc | 14 +++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc index e539b00f28..58e9b43f30 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc @@ -4,7 +4,7 @@ import * as libuci from 'uci'; import { md5 } from 'digest'; import * as fs from 'fs'; -import { append, append_raw, append_value, append_vars, append_string_vars, comment, push_config, set_default, touch_file } from 'wifi.common'; +import { append, append_raw, append_value, append_vars, append_list, append_string_vars, comment, push_config, set_default, touch_file } from 'wifi.common'; import * as netifd from 'wifi.netifd'; import * as iface from 'wifi.iface'; @@ -51,6 +51,10 @@ function iface_setup(config) { config.wmm_enabled = 1; append_string_vars(config, [ 'ssid2' ]); + /* vendor_elements is a single concatenated hex blob, not one per line */ + if (type(config.vendor_elements) == 'array') + config.vendor_elements = join('', config.vendor_elements); + append_vars(config, [ 'ctrl_interface', 'ap_isolate', 'max_num_sta', 'ap_max_inactivity', 'airtime_bss_weight', 'airtime_bss_limit', 'airtime_sta_weight', 'bss_load_update_period', 'chan_util_avg_period', @@ -69,7 +73,7 @@ function iface_authentication_server(config) { append_vars(config, [ 'auth_server_port', 'auth_server_shared_secret' ]); } - append_vars(config, [ 'radius_auth_req_attr' ]); + append_list(config, [ 'radius_auth_req_attr' ]); } function iface_accounting_server(config) { @@ -78,7 +82,7 @@ function iface_accounting_server(config) { append_vars(config, [ 'acct_server_port', 'acct_server_shared_secret' ]); } - append_vars(config, [ 'radius_acct_req_attr' ]); + append_list(config, [ 'radius_acct_req_attr' ]); } function iface_auth_type(config, band) { @@ -522,8 +526,9 @@ function iface_hs20(config) { append_vars(config, [ 'hs20', 'disable_dgaf', 'anqp_domain_id', 'hs20_deauth_req_timeout', 'hs20_wan_metrics', 'hs20_operating_class', 'hs20_t_c_filename', 'hs20_t_c_timestamp', - 'hs20_t_c_server_url', 'hs20_conn_capab' + 'hs20_t_c_server_url' ]); + append_list(config, [ 'hs20_conn_capab' ]); } function iface_interworking(config) { @@ -536,13 +541,14 @@ function iface_interworking(config) { config.domain_name = join(',', config.domain_name); if (config.anqp_3gpp_cell_net) - config.domain_name = join(',', config.anqp_3gpp_cell_net); + config.anqp_3gpp_cell_net = join(';', config.anqp_3gpp_cell_net); append_vars(config, [ 'interworking', 'internet', 'asra', 'uesa', 'access_network_type', 'hessid', 'venue_group', - 'venue_type', 'network_auth_type', 'gas_address3', 'roaming_consortium', 'anqp_elem', 'nai_realm', - 'venue_name', 'venue_url', 'domain_name', 'anqp_3gpp_cell_net', + 'venue_type', 'network_auth_type', 'gas_address3', 'roaming_consortium', + 'domain_name', 'anqp_3gpp_cell_net', ]); + append_list(config, [ 'anqp_elem', 'nai_realm', 'venue_name', 'venue_url' ]); } export function generate(interface, data, config, vlans, stas, phy_features) { diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/common.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/common.uc index de939b0c2f..a59f764904 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/common.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/common.uc @@ -72,6 +72,20 @@ export function append_vars(dict, keys) { append(key, dict[key]); }; +/* emit one "key=value" line per list element (for options hostapd parses + * one entry per line, which must not be space-joined) */ +export function append_list(dict, keys) { + for (let key in keys) { + let val = dict[key]; + if (val == null) + continue; + if (type(val) != 'array') + val = [ val ]; + for (let v in val) + append(key, v); + } +}; + export function append_string_vars(dict, keys) { for (let key in keys) append_string(key, dict[key]); From 1789118599234ae367869c65ad6a92c6f1cbbf10 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 15:02:20 +0200 Subject: [PATCH 053/152] wifi-scripts: give the OWE-transition BSS its own MAC address The transition BSS is a copy of the main BSS with the MAC already set, so iface.prepare() was a no-op and both BSSes emitted the same bssid; with a fixed or random MAC the duplicate was never reassigned and the second interface failed to come up. Clear the address (and stale flags) and prepare a fresh one, plumbing the phy/num_global/macaddr_base through iface_setup(). Signed-off-by: Felix Fietkau --- .../files-ucode/usr/share/ucode/wifi/ap.uc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc index 58e9b43f30..13f06f92f3 100644 --- a/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc +++ b/package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc @@ -8,7 +8,7 @@ import { append, append_raw, append_value, append_vars, append_list, append_stri import * as netifd from 'wifi.netifd'; import * as iface from 'wifi.iface'; -function iface_setup(config) { +function iface_setup(config, phy, num_global_macaddr, macaddr_base) { switch(config.fixup) { case 'owe': config.ignore_broadcast_ssid = true; @@ -22,7 +22,11 @@ function iface_setup(config) { config.owe_transition_ssid = config.ssid + 'OWE'; config.encryption = 'none'; config.ignore_broadcast_ssid = false; - iface.prepare(config); + /* the transition BSS needs its own address, not the main BSS's */ + config.macaddr = null; + delete config.default_macaddr; + delete config.random_macaddr; + iface.prepare(config, phy, num_global_macaddr, macaddr_base); break; } @@ -555,7 +559,7 @@ export function generate(interface, data, config, vlans, stas, phy_features) { config.ctrl_interface = '/var/run/hostapd'; config.start_disabled = data.ap_start_disabled; - iface_setup(config); + iface_setup(config, data.phy + data.phy_suffix, data.config.num_global_macaddr, data.config.macaddr_base); iface.parse_encryption(config, data.config, phy_features); if (data.config.band == '6g') { From e6bd2af652721a9c238a21a987e923333738c508 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 15:03:54 +0200 Subject: [PATCH 054/152] wifi-scripts: only manage wdev interfaces recorded in the statefile wdev.uc adopted every operstate-down netdev on the phy and then removed any not in its new config. hostapd BSS netdevs are legitimately operstate-down while stopped (co-located STA down, or start_disabled), so a reload deleted them and hostapd, seeing an unchanged config, never recreated them. Drop the adopt-existing step; the statefile already tracks the interfaces wdev.uc owns. Signed-off-by: Felix Fietkau --- .../wifi-scripts/files/usr/share/hostap/wdev.uc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc index 883aa2640c..ac34908d86 100644 --- a/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc +++ b/package/network/config/wifi-scripts/files/usr/share/hostap/wdev.uc @@ -90,17 +90,6 @@ function delete_ifname(config) delete config[key].ifname; } -function add_existing(phydev, config) -{ - phydev.for_each_wdev((wdev) => { - if (config[wdev]) - return; - - if (trim(readfile(`/sys/class/net/${wdev}/operstate`)) == "down") - config[wdev] = {}; - }); -} - function usage() { warn(`Usage: ${basename(sourcepath())} [] @@ -137,7 +126,6 @@ const commands = { if (type(old_config) == "object") config.data = old_config; - add_existing(phydev, config.data); add_ifname(config.data); drop_inactive(config.data); From 766052bab37ed88f18e3d7e745e26a47be9addaf Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 10 Jul 2026 15:06:14 +0200 Subject: [PATCH 055/152] wifi-scripts: don't swap handler config under a running setup script A reload during an in-flight setup ran wdev_config_init() immediately, replacing handler_config/handler_data while the script was still running, so its set_data notifications resolved against the new (renumbered) interface table and link up/down calls became asymmetric. Defer the re-init in check() while a handler run is in progress and re-apply from the setup and teardown callbacks once it completes. Signed-off-by: Felix Fietkau --- .../wifi-scripts/files/lib/netifd/wireless-device.uc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc index a3359bc0af..b85d4ee237 100644 --- a/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc +++ b/package/network/config/wifi-scripts/files/lib/netifd/wireless-device.uc @@ -173,6 +173,9 @@ function wdev_teardown_cb(wdev) return; } + if (wdev.config_change) + wdev_config_init(wdev); + wdev.setup(); } @@ -443,6 +446,14 @@ function check() if (!wdev_update_disabled_vifs(this)) return; + /* + * Defer applying a new config while a handler run is in progress: the + * running script keeps its start-time config, and the setup/teardown + * callback re-applies once it finishes (config_change stays set). + */ + if (this.state != "up" && this.state != "down") + return; + wdev_config_init(this); this.setup(); } From 23ac692e6d1164694fe354bcc9a2d94b339271fd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 13 Jul 2026 11:33:44 +0200 Subject: [PATCH 056/152] ucode: update to Git HEAD (2026-07-09) 20a2ae469089 io: support older linux ioctls 16929698e2d3 fs: implement ioctl for macOS 395bb888bd69 io: implement ioctl for macOS 8d7d15ee1a08 compiler: add support for shorthand method declarations 7d9febd13305 nl80211: add mesh peer link enum macros to constants list e851bb64df91 lib: avoid allocating print buffer before NULL check in uc_error_message_indent 5f3a7b87d962 compiler: prevent unbounded recompilation on failed module imports e2493b577250 compiler: scope named function expression names to their own body c3f0d27d812e tests: add regression test for named function expression scoping 738b55299966 vm: release owned value when property assignment fails 1ffd7c5a1011 vm: fix out-of-bounds read in single-name module import a255256db106 vm: fix SIGFPE on INT64_MIN division and modulo by -1 3f4b313d7d78 vm: do not assign to undeclared variables in strict mode e523682670a2 vm: release upvalue reference when resolving it on stack push 6a837090bea3 vm: release module scope reference in dynamic import 395a0a16b4f9 vm: guard error context capture against empty callframe stack 04f103bb2270 vm: respect exponent parity in integer exponentiation bd814a4b2ae3 vm: use UC_ARRAY instead of json_type_array in object spread 5a79aa56c15e vm: release resource type prototypes after the final GC 9c5f16c8e33a lexer: do not treat /*/ as a complete block comment a018067fd12e lexer: preserve NUL bytes in regular expression literals 65d41a1929de lexer: do not consume a sign into hexadecimal number literals 05f9bf9e5d53 lexer: fix source position of ternary question mark token 7bca646e5f88 lib: preserve embedded NUL bytes in reverse() 6fc93bf5bd95 lib: preserve embedded NUL bytes in lc() and uc() a51f4843bce5 lib: avoid passing a signed char to isxdigit() in hex() e8af70e4375c compiler: fix use-after-free of shorthand method name 467fb4406a47 compiler: declare leading variable in counting for loop initializer d256e153799b rtnl: fix stack buffer overflow parsing IFLA_LINKINFO f741ac0a144c rtnl: fix stack buffer overflow parsing RTA_MULTIPATH nexthops c41310f7c2df rtnl: fix inverted address check in multipath nexthop parsing Signed-off-by: Felix Fietkau --- package/utils/ucode/Makefile | 6 +- ...amed-function-expression-names-to-th.patch | 80 ------------------- ...ting-print-buffer-before-NULL-check-.patch | 31 ------- ...-unbounded-recompilation-on-failed-m.patch | 53 ------------ ...low-reusing-the-existing-environment.patch | 4 +- ...d-optional-setup-callback-to-process.patch | 12 +-- 6 files changed, 11 insertions(+), 175 deletions(-) delete mode 100644 package/utils/ucode/patches/100-compiler-scope-named-function-expression-names-to-th.patch delete mode 100644 package/utils/ucode/patches/101-lib-avoid-allocating-print-buffer-before-NULL-check-.patch delete mode 100644 package/utils/ucode/patches/102-compiler-prevent-unbounded-recompilation-on-failed-m.patch diff --git a/package/utils/ucode/Makefile b/package/utils/ucode/Makefile index d046782de7..eb6abd6adb 100644 --- a/package/utils/ucode/Makefile +++ b/package/utils/ucode/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=https://github.com/jow-/ucode.git -PKG_SOURCE_DATE:=2026-06-03 -PKG_SOURCE_VERSION:=3ec4e5c238353067e4b58fb9bb9938d85d59e7c2 -PKG_MIRROR_HASH:=bc0ccef8b34defd524705c73982b39ba9e86fab588e531eba1915eecc57a8351 +PKG_SOURCE_DATE:=2026-07-09 +PKG_SOURCE_VERSION:=b885dd0fe1e974551fb1233ca5f3aa74d80b74d9 +PKG_MIRROR_HASH:=654ba9dc8714aa8caf75ef422962fb465c2b21c8fbe4518a47f9de391cce6e71 PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=ISC diff --git a/package/utils/ucode/patches/100-compiler-scope-named-function-expression-names-to-th.patch b/package/utils/ucode/patches/100-compiler-scope-named-function-expression-names-to-th.patch deleted file mode 100644 index d788a7c768..0000000000 --- a/package/utils/ucode/patches/100-compiler-scope-named-function-expression-names-to-th.patch +++ /dev/null @@ -1,80 +0,0 @@ -From: Felix Fietkau -Date: Mon, 6 Jul 2026 18:23:08 +0200 -Subject: [PATCH] compiler: scope named function expression names to their own - body - -A named function expression (e.g. `!function e(){...}()`) shares its parsing -path with function declarations, and declared its name in the enclosing scope -just as a declaration does. For an expression this is wrong: the name is only -meant to be visible inside its own body for self-reference. Binding it in the -enclosing scope leaked the name and, by pushing an extra local, shifted the -slot count so the initialize_local for a let/const initialised by the -expression marked the wrong slot -- the real variable stayed uninitialised and -its first use raised "Can't access lexical declaration 'x' before -initialization". Self-recursion was likewise broken. - -Bind the name in the enclosing scope only for declarations. For a named -expression, rename the function's own callee slot to the name after -initialising its compiler, so the body can reference itself while the enclosing -scope is left untouched. - -Signed-off-by: Felix Fietkau ---- - ---- a/compiler.c -+++ b/compiler.c -@@ -1949,20 +1949,27 @@ uc_compiler_compile_funcexpr_common(uc_c - return; - } - -- slot = uc_compiler_resolve_funcstub(compiler, name); -+ /* A function declaration binds its name in the enclosing scope. A named -+ * function expression must not: its name is only visible inside its own -+ * body (for self-reference), and declaring it in the enclosing scope -+ * would both leak it and shift the local slots, corrupting the -+ * initialisation state of a `let`/`const` the expression initialises. */ -+ if (require_name) { -+ slot = uc_compiler_resolve_funcstub(compiler, name); - -- if (slot > -1) { -- compiler->locals.entries[slot].funcstub = false; -- } -- else { -- slot = uc_compiler_declare_local(compiler, name, false); -+ if (slot > -1) { -+ compiler->locals.entries[slot].funcstub = false; -+ } -+ else { -+ slot = uc_compiler_declare_local(compiler, name, false); - -- if (slot == -1) -- uc_compiler_initialize_local(compiler); -- else if (compiler->locals.entries[slot].constant) -- uc_compiler_syntax_error(compiler, compiler->parser->prev.pos, -- "Redeclaration of constant '%s'", -- ucv_string_get(name)); -+ if (slot == -1) -+ uc_compiler_initialize_local(compiler); -+ else if (compiler->locals.entries[slot].constant) -+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos, -+ "Redeclaration of constant '%s'", -+ ucv_string_get(name)); -+ } - } - } - else if (require_name) { -@@ -1976,6 +1983,14 @@ uc_compiler_compile_funcexpr_common(uc_c - compiler->program, - uc_compiler_is_strict(compiler)); - -+ /* Bind a named function expression's name to its own callee slot so the -+ * body can reference itself for recursion, without touching the enclosing -+ * scope. Declarations keep the name in the enclosing scope (handled above). */ -+ if (name && !require_name) { -+ ucv_put(fncompiler.locals.entries[0].name); -+ fncompiler.locals.entries[0].name = ucv_get(name); -+ } -+ - fncompiler.parent = compiler; - fncompiler.parser = compiler->parser; - fncompiler.exprstack = compiler->exprstack; diff --git a/package/utils/ucode/patches/101-lib-avoid-allocating-print-buffer-before-NULL-check-.patch b/package/utils/ucode/patches/101-lib-avoid-allocating-print-buffer-before-NULL-check-.patch deleted file mode 100644 index 1902006cdb..0000000000 --- a/package/utils/ucode/patches/101-lib-avoid-allocating-print-buffer-before-NULL-check-.patch +++ /dev/null @@ -1,31 +0,0 @@ -From: Felix Fietkau -Date: Tue, 7 Jul 2026 09:34:16 +0200 -Subject: [PATCH] lib: avoid allocating print buffer before NULL check in - uc_error_message_indent - -Move the print buffer allocation after the guard that returns early on a -NULL message, so no buffer is allocated on that path. This keeps the -function leak-free when callers invoke it without having produced an error -string. - -Signed-off-by: Felix Fietkau ---- - ---- a/lib.c -+++ b/lib.c -@@ -220,13 +220,14 @@ uc_error_context_format(uc_stringbuf_t * - - void - uc_error_message_indent(char **msg) { -- uc_stringbuf_t *buf = xprintbuf_new(); -+ uc_stringbuf_t *buf; - char *s, *p, *nl; - size_t len; - - if (!msg || !*msg) - return; - -+ buf = xprintbuf_new(); - s = *msg; - len = strlen(s); - diff --git a/package/utils/ucode/patches/102-compiler-prevent-unbounded-recompilation-on-failed-m.patch b/package/utils/ucode/patches/102-compiler-prevent-unbounded-recompilation-on-failed-m.patch deleted file mode 100644 index 02590f6a36..0000000000 --- a/package/utils/ucode/patches/102-compiler-prevent-unbounded-recompilation-on-failed-m.patch +++ /dev/null @@ -1,53 +0,0 @@ -From: Felix Fietkau -Date: Mon, 6 Jul 2026 11:12:32 +0200 -Subject: [PATCH] compiler: prevent unbounded recompilation on failed - module imports - -A source's exports.offset of -1 marks a module whose compilation has been -entered but not completed. When a module failed to compile, uc_compiler_finish -freed its function and the error cascaded up through the importers via error -recovery, leaving each involved source at offset -1 with its function gone. The -re-entry guard only recognised an in-progress module by finding its function, so -those freed modules were recompiled again on every subsequent import. Across a -wide import graph this recompilation multiplies and the compiler appears to hang. - -Detect the offset -1 marker directly instead of relying on the presence of a -function: report a circular dependency when the function is still around, and -propagate the failure otherwise, rather than recursing into the module again. - -Signed-off-by: Felix Fietkau ---- - ---- a/compiler.c -+++ b/compiler.c -@@ -3479,15 +3479,26 @@ uc_compiler_compile_module_source(uc_com - - uc_program_function_foreach(compiler->program, fn) { - if (uc_program_function_source(fn) == source) { -- if (source->exports.offset == (size_t)-1) -- uc_compiler_syntax_error(compiler, compiler->parser->prev.pos, -- "Circular dependency"); -- - loaded = true; - break; - } - } - -+ /* An offset of -1 marks a source whose compilation was entered but not yet -+ * completed. Re-entering it is either a circular dependency (its function is -+ * still present) or a module whose earlier compilation failed and freed its -+ * function. Recompiling in either case recurses without bound across a large -+ * import graph, so report the cycle and propagate the failure instead. */ -+ if (source->exports.offset == (size_t)-1) { -+ if (loaded) -+ uc_compiler_syntax_error(compiler, compiler->parser->prev.pos, -+ "Circular dependency"); -+ else if (errp) -+ xasprintf(errp, "Module previously failed to compile\n"); -+ -+ return false; -+ } -+ - if (!loaded) { - /* We do not yet support linking precompiled modules at compile time, - turn static import operation into dynamic load one. */ diff --git a/package/utils/ucode/patches/110-uloop-allow-reusing-the-existing-environment.patch b/package/utils/ucode/patches/110-uloop-allow-reusing-the-existing-environment.patch index 33b0261a71..018410493d 100644 --- a/package/utils/ucode/patches/110-uloop-allow-reusing-the-existing-environment.patch +++ b/package/utils/ucode/patches/110-uloop-allow-reusing-the-existing-environment.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/lib/uloop.c +++ b/lib/uloop.c -@@ -1023,7 +1023,22 @@ uc_uloop_process(uc_vm_t *vm, size_t nar +@@ -1028,7 +1028,22 @@ uc_uloop_process(uc_vm_t *vm, size_t nar if (pid == 0) { argp = calloc(ucv_array_length(arguments) + 2, sizeof(char *)); @@ -35,7 +35,7 @@ Signed-off-by: Felix Fietkau if (!argp || !envp) _exit(-1); -@@ -1033,19 +1048,6 @@ uc_uloop_process(uc_vm_t *vm, size_t nar +@@ -1038,19 +1053,6 @@ uc_uloop_process(uc_vm_t *vm, size_t nar for (i = 0; i < ucv_array_length(arguments); i++) argp[i+1] = ucv_to_string(vm, ucv_array_get(arguments, i)); diff --git a/package/utils/ucode/patches/111-uloop-add-optional-setup-callback-to-process.patch b/package/utils/ucode/patches/111-uloop-add-optional-setup-callback-to-process.patch index e8f597ade7..e6accabe38 100644 --- a/package/utils/ucode/patches/111-uloop-add-optional-setup-callback-to-process.patch +++ b/package/utils/ucode/patches/111-uloop-add-optional-setup-callback-to-process.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau --- a/lib/uloop.c +++ b/lib/uloop.c -@@ -968,8 +968,9 @@ uc_uloop_process_cb(struct uloop_process +@@ -973,8 +973,9 @@ uc_uloop_process_cb(struct uloop_process * * This function creates a process instance for executing external programs. * It takes the executable path string, an optional string array as the argument @@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau * * @function module:uloop#process * -@@ -986,6 +987,11 @@ uc_uloop_process_cb(struct uloop_process +@@ -991,6 +992,11 @@ uc_uloop_process_cb(struct uloop_process * @param {Function} callback * The callback function to be invoked when the invoked process ends. * @@ -34,7 +34,7 @@ Signed-off-by: Felix Fietkau * @returns {?module:uloop.process} * Returns a process instance for executing external programs. * Returns `null` on error, e.g. due to `exec()` failure or invalid arguments. -@@ -995,6 +1001,16 @@ uc_uloop_process_cb(struct uloop_process +@@ -1000,6 +1006,16 @@ uc_uloop_process_cb(struct uloop_process * const myProcess = uloop.process("/bin/ls", ["-l", "/tmp"], null, (code) => { * printf(`Process exited with code ${code}\n`); * }); @@ -51,7 +51,7 @@ Signed-off-by: Felix Fietkau */ static uc_value_t * uc_uloop_process(uc_vm_t *vm, size_t nargs) -@@ -1003,6 +1019,7 @@ uc_uloop_process(uc_vm_t *vm, size_t nar +@@ -1008,6 +1024,7 @@ uc_uloop_process(uc_vm_t *vm, size_t nar uc_value_t *arguments = uc_fn_arg(1); uc_value_t *env_arg = uc_fn_arg(2); uc_value_t *callback = uc_fn_arg(3); @@ -59,7 +59,7 @@ Signed-off-by: Felix Fietkau uc_uloop_process_t *process; uc_stringbuf_t *buf; char **argp, **envp; -@@ -1012,7 +1029,8 @@ uc_uloop_process(uc_vm_t *vm, size_t nar +@@ -1017,7 +1034,8 @@ uc_uloop_process(uc_vm_t *vm, size_t nar if (ucv_type(executable) != UC_STRING || (arguments && ucv_type(arguments) != UC_ARRAY) || (env_arg && ucv_type(env_arg) != UC_OBJECT) || @@ -69,7 +69,7 @@ Signed-off-by: Felix Fietkau err_return(EINVAL); } -@@ -1022,6 +1040,13 @@ uc_uloop_process(uc_vm_t *vm, size_t nar +@@ -1027,6 +1045,13 @@ uc_uloop_process(uc_vm_t *vm, size_t nar err_return(errno); if (pid == 0) { From 4be1036213238ecaca758bf43a57f7dc5255f6f5 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 16 Jul 2026 14:27:41 +0200 Subject: [PATCH 057/152] airoha: an7583: fix AN7583 PCS port count The AN7583 PCS match data does not initialize num_port. This leaves it at zero, so probe allocates a zero-sized ports array and a later PCS lookup returns a pointer derived from NULL. Set the number of ports for each AN7583 PCS instance. Also reject out-of-range PCS indices defensively. Fixes kernel crashing on boot once ethernet starts probing on AN7583. Signed-off-by: Robert Marko --- ...net-pcs-airoha-fix-an7583-port-count.patch | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 target/linux/airoha/patches-6.18/606-net-pcs-airoha-fix-an7583-port-count.patch diff --git a/target/linux/airoha/patches-6.18/606-net-pcs-airoha-fix-an7583-port-count.patch b/target/linux/airoha/patches-6.18/606-net-pcs-airoha-fix-an7583-port-count.patch new file mode 100644 index 0000000000..b948321734 --- /dev/null +++ b/target/linux/airoha/patches-6.18/606-net-pcs-airoha-fix-an7583-port-count.patch @@ -0,0 +1,61 @@ +From: Robert Marko +Date: Thu, 16 Jul 2026 14:00:00 +0200 +Subject: [PATCH] net: pcs: airoha: fix AN7583 PCS port count + +The AN7583 PCS match data does not initialize num_port. This leaves it at +zero, so probe allocates a zero-sized ports array and a later PCS lookup +returns a pointer derived from NULL. + +Set the number of ports for each AN7583 PCS instance. Also reject +out-of-range PCS indices defensively. + +Signed-off-by: Robert Marko +--- + drivers/net/pcs/airoha/pcs-airoha-common.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/net/pcs/airoha/pcs-airoha-common.c ++++ b/drivers/net/pcs/airoha/pcs-airoha-common.c +@@ -1170,6 +1170,10 @@ static struct phylink_pcs *airoha_pcs_ge + if (pcsspec->nargs) + index = pcsspec->args[0]; + ++ if (index >= priv->data->num_port) ++ return dev_err_ptr_probe(dev, -EINVAL, ++ "invalid PCS index %d\n", index); ++ + return &priv->ports[index].pcs; + } + +@@ -1418,6 +1422,7 @@ static const struct airoha_pcs_match_dat + }; + + static const struct airoha_pcs_match_data an7583_pcs_eth = { ++ .num_port = 1, + .port_type = AIROHA_PCS_ETH, + .usxgmii_rx_gb_out_vld_tweak = true, + .usxgmii_xfi_mode_sel = true, +@@ -1426,6 +1431,7 @@ static const struct airoha_pcs_match_dat + }; + + static const struct airoha_pcs_match_data an7583_pcs_pon = { ++ .num_port = 1, + .port_type = AIROHA_PCS_PON, + .usxgmii_rx_gb_out_vld_tweak = true, + .usxgmii_xfi_mode_sel = true, +@@ -1434,6 +1440,7 @@ static const struct airoha_pcs_match_dat + }; + + static const struct airoha_pcs_match_data an7583_pcs_pcie = { ++ .num_port = 2, + .port_type = AIROHA_PCS_PCIE, + .usxgmii_rx_gb_out_vld_tweak = true, + .usxgmii_xfi_mode_sel = true, +@@ -1442,6 +1449,7 @@ static const struct airoha_pcs_match_dat + }; + + static const struct airoha_pcs_match_data an7583_pcs_usb = { ++ .num_port = 1, + .port_type = AIROHA_PCS_USB, + .bringup = an7583_pcs_usb_phya_bringup, + }; From 464560fc35aa6052a0a03dca145b132cd1df8af2 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Thu, 16 Jul 2026 07:04:51 +0000 Subject: [PATCH 058/152] realtek: fix fan control for Plasma Cloud PSX28/ESX28 Commit 82ddc472d722 ("realtek: dts: convert to upstream switch notation") adjusted the switch description in the device tree to match upstream. This was a correct change, however it broke the fan control script for Plasma Cloud PSX28/ESX28 devices because it uses a full-fledged platform device path including the switch node (because the I2C is a child of the switch): /sys/devices/platform/1b000000.switchcore/1b000000.switchcore:i2c@100c/i2c-4/4-002e/hwmon/ Since the mentioned change, this path doesn't exist anymore. Instead of just changing the intermediate parts to match the DT structure again, do not rely on that at all. Instead, use /sys/bus/i2c/devices/4-002e/hwmon/ as the path which relies less on the DT structure and still points to the same device. This makes fan control on those device work again, reducing the fan speed during boot which has been broken since the commit. Fixes: 82ddc472d722 ("realtek: dts: convert to upstream switch notation") Fixes: 2b5555c1950d ("realtek: rtl931x: Add support for Plasma Cloud PSX28 Switch") Link: https://github.com/openwrt/openwrt/pull/24248 Signed-off-by: Jonas Jelonek --- target/linux/realtek/base-files/etc/init.d/hwmon_fancontrol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/realtek/base-files/etc/init.d/hwmon_fancontrol b/target/linux/realtek/base-files/etc/init.d/hwmon_fancontrol index 54c0e0e43f..85e57522fc 100755 --- a/target/linux/realtek/base-files/etc/init.d/hwmon_fancontrol +++ b/target/linux/realtek/base-files/etc/init.d/hwmon_fancontrol @@ -6,7 +6,7 @@ plasmacloud_common() { # configuring onboard temp/fan controller to run the fan on its own # for more information, please read https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface - local path_to_hwmon="$(find /sys/devices/platform/1b000000.switchcore/1b000000.switchcore:i2c@100c/i2c-4/4-002e/hwmon/ -type d -maxdepth 1 -mindepth 1 -name 'hwmon*')" + local path_to_hwmon="$(find /sys/bus/i2c/devices/4-002e/hwmon/ -type d -maxdepth 1 -mindepth 1 -name 'hwmon*')" # adt7476 fan control chip # 3 temp sensors. Set to lowest speed at 35C and max speed at 70C From 6f703adcd39c7e34c5607324912d2838f1bb2224 Mon Sep 17 00:00:00 2001 From: Georgi Valkov Date: Wed, 15 Jul 2026 17:16:29 +0300 Subject: [PATCH 059/152] kernel: set MACOSX_DEPLOYMENT_TARGET to host version Fix a warning interpreted as error when building the Linux kernel on macOS host, by setting MACOSX_DEPLOYMENT_TARGET to the version of the host. Fixes: make[5]: Entering directory '/Volumes/test/openwrt/build_dir/target-x86_64_musl/linux-x86_64/linux-6.18.38' HOSTCC scripts/mod/modpost.o scripts/mod/modpost.c:1719:9: error: 'strchrnul' is only available on macOS 15.4 or newer [-Werror,-Wunguarded-availability-new] 1719 | sep = strchrnul(namespace, ','); | ^~~~~~~~~ Signed-off-by: Georgi Valkov Link: https://github.com/openwrt/openwrt/pull/24251 Signed-off-by: Robert Marko --- include/kernel.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/kernel.mk b/include/kernel.mk index 1f51049662..146346a475 100644 --- a/include/kernel.mk +++ b/include/kernel.mk @@ -121,6 +121,10 @@ ifneq (,$(KERNEL_CC)) KERNEL_MAKE_FLAGS += CC="$(KERNEL_CC)" endif +ifeq ($(HOST_OS),Darwin) + KERNEL_MAKE_FLAGS += MACOSX_DEPLOYMENT_TARGET="$(shell sw_vers -productVersion)" +endif + KERNEL_NOSTDINC_FLAGS = \ -nostdinc $(if $(DUMP),, -isystem $(shell $(TARGET_CC) -print-file-name=include)) From ab30c2547cd3a48e21b24c2abb5e4ba074684e27 Mon Sep 17 00:00:00 2001 From: Denis Yarkovoy Date: Sun, 12 Jul 2026 10:34:56 +0300 Subject: [PATCH 060/152] ipq40xx: move ipqess driver source to files The ipqess driver is carried locally in OpenWrt and is not present in upstream Linux. Move the driver sources out of the source-adding kernel patch and into target/linux/ipq40xx/files/ so the local source is easier to maintain. Keep the kernel patch only for the upstream-style Kconfig, Makefile and MAINTAINERS wiring, and refresh the dependent ipqess patches. Use the void-returning platform driver .remove callback. Signed-off-by: Denis Yarkovoy Link: https://github.com/openwrt/openwrt/pull/24122 Signed-off-by: Jonas Jelonek --- .../net/ethernet/qualcomm/ipqess/Makefile | 8 + .../net/ethernet/qualcomm/ipqess/ipqess.c | 1245 +++++++++++ .../net/ethernet/qualcomm/ipqess/ipqess.h | 518 +++++ .../ethernet/qualcomm/ipqess/ipqess_ethtool.c | 164 ++ ...introduce-the-Qualcomm-IPQESS-driver.patch | 1966 +---------------- ...-Add-out-of-band-DSA-tagging-support.patch | 16 +- ...ess-release-IRQ-s-on-network-device-.patch | 2 +- ...qess-enable-threaded-NAPI-by-default.patch | 6 +- ...ualcomm-ipqess-fix-TX-timeout-errors.patch | 2 +- 9 files changed, 1952 insertions(+), 1975 deletions(-) create mode 100644 target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/Makefile create mode 100644 target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c create mode 100644 target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.h create mode 100644 target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c diff --git a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/Makefile b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/Makefile new file mode 100644 index 0000000000..4f2db7283e --- /dev/null +++ b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the IPQ ESS driver +# + +obj-$(CONFIG_QCOM_IPQ4019_ESS_EDMA) += ipq_ess.o + +ipq_ess-objs := ipqess.o ipqess_ethtool.o diff --git a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c new file mode 100644 index 0000000000..b3dfd6f1ae --- /dev/null +++ b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c @@ -0,0 +1,1245 @@ +// SPDX-License-Identifier: GPL-2.0 OR ISC +/* Copyright (c) 2014 - 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017 - 2018, John Crispin + * Copyright (c) 2018 - 2019, Christian Lamparter + * Copyright (c) 2020 - 2021, Gabor Juhos + * Copyright (c) 2021 - 2022, Maxime Chevallier + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipqess.h" + +#define IPQESS_RRD_SIZE 16 +#define IPQESS_NEXT_IDX(X, Y) (((X) + 1) & ((Y) - 1)) +#define IPQESS_TX_DMA_BUF_LEN 0x3fff + +static void ipqess_w32(struct ipqess *ess, u32 reg, u32 val) +{ + writel(val, ess->hw_addr + reg); +} + +static u32 ipqess_r32(struct ipqess *ess, u16 reg) +{ + return readl(ess->hw_addr + reg); +} + +static void ipqess_m32(struct ipqess *ess, u32 mask, u32 val, u16 reg) +{ + u32 _val = ipqess_r32(ess, reg); + + _val &= ~mask; + _val |= val; + + ipqess_w32(ess, reg, _val); +} + +void ipqess_update_hw_stats(struct ipqess *ess) +{ + u32 *p; + u32 stat; + int i; + + lockdep_assert_held(&ess->stats_lock); + + p = (u32 *)&ess->ipqess_stats; + for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) { + stat = ipqess_r32(ess, IPQESS_REG_TX_STAT_PKT_Q(i)); + *p += stat; + p++; + } + + for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) { + stat = ipqess_r32(ess, IPQESS_REG_TX_STAT_BYTE_Q(i)); + *p += stat; + p++; + } + + for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) { + stat = ipqess_r32(ess, IPQESS_REG_RX_STAT_PKT_Q(i)); + *p += stat; + p++; + } + + for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) { + stat = ipqess_r32(ess, IPQESS_REG_RX_STAT_BYTE_Q(i)); + *p += stat; + p++; + } +} + +static int ipqess_tx_ring_alloc(struct ipqess *ess) +{ + struct device *dev = &ess->pdev->dev; + int i; + + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + struct ipqess_tx_ring *tx_ring = &ess->tx_ring[i]; + size_t size; + u32 idx; + + tx_ring->ess = ess; + tx_ring->ring_id = i; + tx_ring->idx = i * 4; + tx_ring->count = IPQESS_TX_RING_SIZE; + tx_ring->nq = netdev_get_tx_queue(ess->netdev, i); + + size = sizeof(struct ipqess_buf) * IPQESS_TX_RING_SIZE; + tx_ring->buf = devm_kzalloc(dev, size, GFP_KERNEL); + if (!tx_ring->buf) + return -ENOMEM; + + size = sizeof(struct ipqess_tx_desc) * IPQESS_TX_RING_SIZE; + tx_ring->hw_desc = dmam_alloc_coherent(dev, size, &tx_ring->dma, + GFP_KERNEL); + if (!tx_ring->hw_desc) + return -ENOMEM; + + ipqess_w32(ess, IPQESS_REG_TPD_BASE_ADDR_Q(tx_ring->idx), + (u32)tx_ring->dma); + + idx = ipqess_r32(ess, IPQESS_REG_TPD_IDX_Q(tx_ring->idx)); + idx >>= IPQESS_TPD_CONS_IDX_SHIFT; /* need u32 here */ + idx &= 0xffff; + tx_ring->head = idx; + tx_ring->tail = idx; + + ipqess_m32(ess, IPQESS_TPD_PROD_IDX_MASK << IPQESS_TPD_PROD_IDX_SHIFT, + idx, IPQESS_REG_TPD_IDX_Q(tx_ring->idx)); + ipqess_w32(ess, IPQESS_REG_TX_SW_CONS_IDX_Q(tx_ring->idx), idx); + ipqess_w32(ess, IPQESS_REG_TPD_RING_SIZE, IPQESS_TX_RING_SIZE); + } + + return 0; +} + +static int ipqess_tx_unmap_and_free(struct device *dev, struct ipqess_buf *buf) +{ + int len = 0; + + if (buf->flags & IPQESS_DESC_SINGLE) + dma_unmap_single(dev, buf->dma, buf->length, DMA_TO_DEVICE); + else if (buf->flags & IPQESS_DESC_PAGE) + dma_unmap_page(dev, buf->dma, buf->length, DMA_TO_DEVICE); + + if (buf->flags & IPQESS_DESC_LAST) { + len = buf->skb->len; + dev_kfree_skb_any(buf->skb); + } + + buf->flags = 0; + + return len; +} + +static void ipqess_tx_ring_free(struct ipqess *ess) +{ + int i; + + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + int j; + + if (ess->tx_ring[i].hw_desc) + continue; + + for (j = 0; j < IPQESS_TX_RING_SIZE; j++) { + struct ipqess_buf *buf = &ess->tx_ring[i].buf[j]; + + ipqess_tx_unmap_and_free(&ess->pdev->dev, buf); + } + + ess->tx_ring[i].buf = NULL; + } +} + +static int ipqess_rx_buf_prepare(struct ipqess_buf *buf, + struct ipqess_rx_ring *rx_ring) +{ + memset(buf->skb->data, 0, sizeof(struct ipqess_rx_desc)); + + buf->dma = dma_map_single(rx_ring->ppdev, buf->skb->data, + IPQESS_RX_HEAD_BUFF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(rx_ring->ppdev, buf->dma)) { + dev_kfree_skb_any(buf->skb); + buf->skb = NULL; + return -EFAULT; + } + + buf->length = IPQESS_RX_HEAD_BUFF_SIZE; + rx_ring->hw_desc[rx_ring->head] = (struct ipqess_rx_desc *)buf->dma; + rx_ring->head = (rx_ring->head + 1) % IPQESS_RX_RING_SIZE; + + ipqess_m32(rx_ring->ess, IPQESS_RFD_PROD_IDX_BITS, + (rx_ring->head + IPQESS_RX_RING_SIZE - 1) % IPQESS_RX_RING_SIZE, + IPQESS_REG_RFD_IDX_Q(rx_ring->idx)); + + return 0; +} + +/* locking is handled by the caller */ +static int ipqess_rx_buf_alloc_napi(struct ipqess_rx_ring *rx_ring) +{ + struct ipqess_buf *buf = &rx_ring->buf[rx_ring->head]; + + buf->skb = napi_alloc_skb(&rx_ring->napi_rx, IPQESS_RX_HEAD_BUFF_SIZE); + if (!buf->skb) + return -ENOMEM; + + return ipqess_rx_buf_prepare(buf, rx_ring); +} + +static int ipqess_rx_buf_alloc(struct ipqess_rx_ring *rx_ring) +{ + struct ipqess_buf *buf = &rx_ring->buf[rx_ring->head]; + + buf->skb = netdev_alloc_skb_ip_align(rx_ring->ess->netdev, + IPQESS_RX_HEAD_BUFF_SIZE); + + if (!buf->skb) + return -ENOMEM; + + return ipqess_rx_buf_prepare(buf, rx_ring); +} + +static void ipqess_refill_work(struct work_struct *work) +{ + struct ipqess_rx_ring_refill *rx_refill = container_of(work, + struct ipqess_rx_ring_refill, refill_work); + struct ipqess_rx_ring *rx_ring = rx_refill->rx_ring; + int refill = 0; + + /* don't let this loop by accident. */ + while (atomic_dec_and_test(&rx_ring->refill_count)) { + napi_disable(&rx_ring->napi_rx); + if (ipqess_rx_buf_alloc(rx_ring)) { + refill++; + dev_dbg(rx_ring->ppdev, + "Not all buffers were reallocated"); + } + napi_enable(&rx_ring->napi_rx); + } + + if (atomic_add_return(refill, &rx_ring->refill_count)) + schedule_work(&rx_refill->refill_work); +} + +static int ipqess_rx_ring_alloc(struct ipqess *ess) +{ + int i; + + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + int j; + + ess->rx_ring[i].ess = ess; + ess->rx_ring[i].ppdev = &ess->pdev->dev; + ess->rx_ring[i].ring_id = i; + ess->rx_ring[i].idx = i * 2; + + ess->rx_ring[i].buf = devm_kzalloc(&ess->pdev->dev, + sizeof(struct ipqess_buf) * IPQESS_RX_RING_SIZE, + GFP_KERNEL); + + if (!ess->rx_ring[i].buf) + return -ENOMEM; + + ess->rx_ring[i].hw_desc = + dmam_alloc_coherent(&ess->pdev->dev, + sizeof(struct ipqess_rx_desc) * IPQESS_RX_RING_SIZE, + &ess->rx_ring[i].dma, GFP_KERNEL); + + if (!ess->rx_ring[i].hw_desc) + return -ENOMEM; + + for (j = 0; j < IPQESS_RX_RING_SIZE; j++) + if (ipqess_rx_buf_alloc(&ess->rx_ring[i]) < 0) + return -ENOMEM; + + ess->rx_refill[i].rx_ring = &ess->rx_ring[i]; + INIT_WORK(&ess->rx_refill[i].refill_work, ipqess_refill_work); + + ipqess_w32(ess, IPQESS_REG_RFD_BASE_ADDR_Q(ess->rx_ring[i].idx), + (u32)(ess->rx_ring[i].dma)); + } + + ipqess_w32(ess, IPQESS_REG_RX_DESC0, + (IPQESS_RX_HEAD_BUFF_SIZE << IPQESS_RX_BUF_SIZE_SHIFT) | + (IPQESS_RX_RING_SIZE << IPQESS_RFD_RING_SIZE_SHIFT)); + + return 0; +} + +static void ipqess_rx_ring_free(struct ipqess *ess) +{ + int i; + + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + int j; + + cancel_work_sync(&ess->rx_refill[i].refill_work); + atomic_set(&ess->rx_ring[i].refill_count, 0); + + for (j = 0; j < IPQESS_RX_RING_SIZE; j++) { + dma_unmap_single(&ess->pdev->dev, + ess->rx_ring[i].buf[j].dma, + ess->rx_ring[i].buf[j].length, + DMA_FROM_DEVICE); + dev_kfree_skb_any(ess->rx_ring[i].buf[j].skb); + } + } +} + +static struct net_device_stats *ipqess_get_stats(struct net_device *netdev) +{ + struct ipqess *ess = netdev_priv(netdev); + + spin_lock(&ess->stats_lock); + ipqess_update_hw_stats(ess); + spin_unlock(&ess->stats_lock); + + return &ess->stats; +} + +static int ipqess_rx_poll(struct ipqess_rx_ring *rx_ring, int budget) +{ + u32 length = 0, num_desc, tail, rx_ring_tail; + int done = 0; + + rx_ring_tail = rx_ring->tail; + + tail = ipqess_r32(rx_ring->ess, IPQESS_REG_RFD_IDX_Q(rx_ring->idx)); + tail >>= IPQESS_RFD_CONS_IDX_SHIFT; + tail &= IPQESS_RFD_CONS_IDX_MASK; + + while (done < budget) { + struct ipqess_rx_desc *rd; + struct sk_buff *skb; + + if (rx_ring_tail == tail) + break; + + dma_unmap_single(rx_ring->ppdev, + rx_ring->buf[rx_ring_tail].dma, + rx_ring->buf[rx_ring_tail].length, + DMA_FROM_DEVICE); + + skb = xchg(&rx_ring->buf[rx_ring_tail].skb, NULL); + rd = (struct ipqess_rx_desc *)skb->data; + rx_ring_tail = IPQESS_NEXT_IDX(rx_ring_tail, IPQESS_RX_RING_SIZE); + + /* Check if RRD is valid */ + if (!(rd->rrd7 & cpu_to_le16(IPQESS_RRD_DESC_VALID))) { + num_desc = 1; + dev_kfree_skb_any(skb); + goto skip; + } + + num_desc = le16_to_cpu(rd->rrd1) & IPQESS_RRD_NUM_RFD_MASK; + length = le16_to_cpu(rd->rrd6) & IPQESS_RRD_PKT_SIZE_MASK; + + skb_reserve(skb, IPQESS_RRD_SIZE); + if (num_desc > 1) { + struct sk_buff *skb_prev = NULL; + int size_remaining; + int i; + + skb->data_len = 0; + skb->tail += (IPQESS_RX_HEAD_BUFF_SIZE - IPQESS_RRD_SIZE); + skb->len = length; + skb->truesize = length; + size_remaining = length - (IPQESS_RX_HEAD_BUFF_SIZE - IPQESS_RRD_SIZE); + + for (i = 1; i < num_desc; i++) { + struct sk_buff *skb_temp = rx_ring->buf[rx_ring_tail].skb; + + dma_unmap_single(rx_ring->ppdev, + rx_ring->buf[rx_ring_tail].dma, + rx_ring->buf[rx_ring_tail].length, + DMA_FROM_DEVICE); + + skb_put(skb_temp, min(size_remaining, IPQESS_RX_HEAD_BUFF_SIZE)); + if (skb_prev) + skb_prev->next = rx_ring->buf[rx_ring_tail].skb; + else + skb_shinfo(skb)->frag_list = rx_ring->buf[rx_ring_tail].skb; + skb_prev = rx_ring->buf[rx_ring_tail].skb; + rx_ring->buf[rx_ring_tail].skb->next = NULL; + + skb->data_len += rx_ring->buf[rx_ring_tail].skb->len; + size_remaining -= rx_ring->buf[rx_ring_tail].skb->len; + + rx_ring_tail = IPQESS_NEXT_IDX(rx_ring_tail, IPQESS_RX_RING_SIZE); + } + + } else { + skb_put(skb, length); + } + + skb->dev = rx_ring->ess->netdev; + skb->protocol = eth_type_trans(skb, rx_ring->ess->netdev); + skb_record_rx_queue(skb, rx_ring->ring_id); + + if (rd->rrd6 & cpu_to_le16(IPQESS_RRD_CSUM_FAIL_MASK)) + skb_checksum_none_assert(skb); + else + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rd->rrd7 & cpu_to_le16(IPQESS_RRD_CVLAN)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + le16_to_cpu(rd->rrd4)); + else if (rd->rrd1 & cpu_to_le16(IPQESS_RRD_SVLAN)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), + le16_to_cpu(rd->rrd4)); + + napi_gro_receive(&rx_ring->napi_rx, skb); + + rx_ring->ess->stats.rx_packets++; + rx_ring->ess->stats.rx_bytes += length; + + done++; +skip: + + num_desc += atomic_xchg(&rx_ring->refill_count, 0); + while (num_desc) { + if (ipqess_rx_buf_alloc_napi(rx_ring)) { + num_desc = atomic_add_return(num_desc, + &rx_ring->refill_count); + if (num_desc >= DIV_ROUND_UP(IPQESS_RX_RING_SIZE * 4, 7)) + schedule_work(&rx_ring->ess->rx_refill[rx_ring->ring_id].refill_work); + break; + } + num_desc--; + } + } + + ipqess_w32(rx_ring->ess, IPQESS_REG_RX_SW_CONS_IDX_Q(rx_ring->idx), + rx_ring_tail); + rx_ring->tail = rx_ring_tail; + + return done; +} + +static int ipqess_tx_complete(struct ipqess_tx_ring *tx_ring, int budget) +{ + int total = 0, ret; + int done = 0; + u32 tail; + + tail = ipqess_r32(tx_ring->ess, IPQESS_REG_TPD_IDX_Q(tx_ring->idx)); + tail >>= IPQESS_TPD_CONS_IDX_SHIFT; + tail &= IPQESS_TPD_CONS_IDX_MASK; + + do { + ret = ipqess_tx_unmap_and_free(&tx_ring->ess->pdev->dev, + &tx_ring->buf[tx_ring->tail]); + tx_ring->tail = IPQESS_NEXT_IDX(tx_ring->tail, tx_ring->count); + + total += ret; + } while ((++done < budget) && (tx_ring->tail != tail)); + + ipqess_w32(tx_ring->ess, IPQESS_REG_TX_SW_CONS_IDX_Q(tx_ring->idx), + tx_ring->tail); + + if (netif_tx_queue_stopped(tx_ring->nq)) { + netdev_dbg(tx_ring->ess->netdev, "waking up tx queue %d\n", + tx_ring->idx); + netif_tx_wake_queue(tx_ring->nq); + } + + netdev_tx_completed_queue(tx_ring->nq, done, total); + + return done; +} + +static int ipqess_tx_napi(struct napi_struct *napi, int budget) +{ + struct ipqess_tx_ring *tx_ring = container_of(napi, struct ipqess_tx_ring, + napi_tx); + int work_done = 0; + u32 tx_status; + + tx_status = ipqess_r32(tx_ring->ess, IPQESS_REG_TX_ISR); + tx_status &= BIT(tx_ring->idx); + + work_done = ipqess_tx_complete(tx_ring, budget); + + ipqess_w32(tx_ring->ess, IPQESS_REG_TX_ISR, tx_status); + + if (likely(work_done < budget)) { + if (napi_complete_done(napi, work_done)) + ipqess_w32(tx_ring->ess, + IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), 0x1); + } + + return work_done; +} + +static int ipqess_rx_napi(struct napi_struct *napi, int budget) +{ + struct ipqess_rx_ring *rx_ring = container_of(napi, struct ipqess_rx_ring, + napi_rx); + struct ipqess *ess = rx_ring->ess; + u32 rx_mask = BIT(rx_ring->idx); + int remaining_budget = budget; + int rx_done; + u32 status; + + do { + ipqess_w32(ess, IPQESS_REG_RX_ISR, rx_mask); + rx_done = ipqess_rx_poll(rx_ring, remaining_budget); + remaining_budget -= rx_done; + + status = ipqess_r32(ess, IPQESS_REG_RX_ISR); + } while (remaining_budget > 0 && (status & rx_mask)); + + if (remaining_budget <= 0) + return budget; + + if (napi_complete_done(napi, budget - remaining_budget)) + ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(rx_ring->idx), 0x1); + + return budget - remaining_budget; +} + +static irqreturn_t ipqess_interrupt_tx(int irq, void *priv) +{ + struct ipqess_tx_ring *tx_ring = (struct ipqess_tx_ring *)priv; + + if (likely(napi_schedule_prep(&tx_ring->napi_tx))) { + __napi_schedule(&tx_ring->napi_tx); + ipqess_w32(tx_ring->ess, IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), + 0x0); + } + + return IRQ_HANDLED; +} + +static irqreturn_t ipqess_interrupt_rx(int irq, void *priv) +{ + struct ipqess_rx_ring *rx_ring = (struct ipqess_rx_ring *)priv; + + if (likely(napi_schedule_prep(&rx_ring->napi_rx))) { + __napi_schedule(&rx_ring->napi_rx); + ipqess_w32(rx_ring->ess, IPQESS_REG_RX_INT_MASK_Q(rx_ring->idx), + 0x0); + } + + return IRQ_HANDLED; +} + +static void ipqess_irq_enable(struct ipqess *ess) +{ + int i; + + ipqess_w32(ess, IPQESS_REG_RX_ISR, 0xff); + ipqess_w32(ess, IPQESS_REG_TX_ISR, 0xffff); + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(ess->rx_ring[i].idx), 1); + ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(ess->tx_ring[i].idx), 1); + } +} + +static void ipqess_irq_disable(struct ipqess *ess) +{ + int i; + + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(ess->rx_ring[i].idx), 0); + ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(ess->tx_ring[i].idx), 0); + } +} + +static int ipqess_init(struct net_device *netdev) +{ + struct ipqess *ess = netdev_priv(netdev); + struct device_node *of_node = ess->pdev->dev.of_node; + + return phylink_of_phy_connect(ess->phylink, of_node, 0); +} + +static void ipqess_uninit(struct net_device *netdev) +{ + struct ipqess *ess = netdev_priv(netdev); + + phylink_disconnect_phy(ess->phylink); +} + +static int ipqess_open(struct net_device *netdev) +{ + struct ipqess *ess = netdev_priv(netdev); + int i, err; + + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + int qid; + + qid = ess->tx_ring[i].idx; + err = devm_request_irq(&netdev->dev, ess->tx_irq[qid], + ipqess_interrupt_tx, 0, + ess->tx_irq_names[qid], + &ess->tx_ring[i]); + if (err) + return err; + + qid = ess->rx_ring[i].idx; + err = devm_request_irq(&netdev->dev, ess->rx_irq[qid], + ipqess_interrupt_rx, 0, + ess->rx_irq_names[qid], + &ess->rx_ring[i]); + if (err) + return err; + + napi_enable(&ess->tx_ring[i].napi_tx); + napi_enable(&ess->rx_ring[i].napi_rx); + } + + ipqess_irq_enable(ess); + phylink_start(ess->phylink); + netif_tx_start_all_queues(netdev); + + return 0; +} + +static int ipqess_stop(struct net_device *netdev) +{ + struct ipqess *ess = netdev_priv(netdev); + int i; + + netif_tx_stop_all_queues(netdev); + phylink_stop(ess->phylink); + ipqess_irq_disable(ess); + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + napi_disable(&ess->tx_ring[i].napi_tx); + napi_disable(&ess->rx_ring[i].napi_rx); + } + + return 0; +} + +static int ipqess_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct ipqess *ess = netdev_priv(netdev); + + return phylink_mii_ioctl(ess->phylink, ifr, cmd); +} + +static u16 ipqess_tx_desc_available(struct ipqess_tx_ring *tx_ring) +{ + u16 count = 0; + + if (tx_ring->tail <= tx_ring->head) + count = IPQESS_TX_RING_SIZE; + + count += tx_ring->tail - tx_ring->head - 1; + + return count; +} + +static int ipqess_cal_txd_req(struct sk_buff *skb) +{ + int tpds; + + /* one TPD for the header, and one for each fragments */ + tpds = 1 + skb_shinfo(skb)->nr_frags; + if (skb_is_gso(skb) && skb_is_gso_v6(skb)) { + /* for LSOv2 one extra TPD is needed */ + tpds++; + } + + return tpds; +} + +static struct ipqess_buf *ipqess_get_tx_buffer(struct ipqess_tx_ring *tx_ring, + struct ipqess_tx_desc *desc) +{ + return &tx_ring->buf[desc - tx_ring->hw_desc]; +} + +static struct ipqess_tx_desc *ipqess_tx_desc_next(struct ipqess_tx_ring *tx_ring) +{ + struct ipqess_tx_desc *desc; + + desc = &tx_ring->hw_desc[tx_ring->head]; + tx_ring->head = IPQESS_NEXT_IDX(tx_ring->head, tx_ring->count); + + return desc; +} + +static void ipqess_rollback_tx(struct ipqess *eth, + struct ipqess_tx_desc *first_desc, int ring_id) +{ + struct ipqess_tx_ring *tx_ring = ð->tx_ring[ring_id]; + struct ipqess_tx_desc *desc = NULL; + struct ipqess_buf *buf; + u16 start_index, index; + + start_index = first_desc - tx_ring->hw_desc; + + index = start_index; + while (index != tx_ring->head) { + desc = &tx_ring->hw_desc[index]; + buf = &tx_ring->buf[index]; + ipqess_tx_unmap_and_free(ð->pdev->dev, buf); + memset(desc, 0, sizeof(*desc)); + if (++index == tx_ring->count) + index = 0; + } + tx_ring->head = start_index; +} + +static int ipqess_tx_map_and_fill(struct ipqess_tx_ring *tx_ring, + struct sk_buff *skb) +{ + struct ipqess_tx_desc *desc = NULL, *first_desc = NULL; + u32 word1 = 0, word3 = 0, lso_word1 = 0, svlan_tag = 0; + struct platform_device *pdev = tx_ring->ess->pdev; + struct ipqess_buf *buf = NULL; + u16 len; + int i; + + if (skb_is_gso(skb)) { + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { + lso_word1 |= IPQESS_TPD_IPV4_EN; + ip_hdr(skb)->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + } else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { + lso_word1 |= IPQESS_TPD_LSO_V2_EN; + ipv6_hdr(skb)->payload_len = 0; + tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); + } + + lso_word1 |= IPQESS_TPD_LSO_EN | + ((skb_shinfo(skb)->gso_size & IPQESS_TPD_MSS_MASK) << + IPQESS_TPD_MSS_SHIFT) | + (skb_transport_offset(skb) << IPQESS_TPD_HDR_SHIFT); + } else if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { + u8 css, cso; + + cso = skb_checksum_start_offset(skb); + css = cso + skb->csum_offset; + + word1 |= (IPQESS_TPD_CUSTOM_CSUM_EN); + word1 |= (cso >> 1) << IPQESS_TPD_HDR_SHIFT; + word1 |= ((css >> 1) << IPQESS_TPD_CUSTOM_CSUM_SHIFT); + } + + if (skb_vlan_tag_present(skb)) { + switch (skb->vlan_proto) { + case htons(ETH_P_8021Q): + word3 |= BIT(IPQESS_TX_INS_CVLAN); + word3 |= skb_vlan_tag_get(skb) << IPQESS_TX_CVLAN_TAG_SHIFT; + break; + case htons(ETH_P_8021AD): + word1 |= BIT(IPQESS_TX_INS_SVLAN); + svlan_tag = skb_vlan_tag_get(skb); + break; + default: + dev_err(&pdev->dev, "no ctag or stag present\n"); + goto vlan_tag_error; + } + } + + if (eth_type_vlan(skb->protocol)) + word1 |= IPQESS_TPD_VLAN_TAGGED; + + if (skb->protocol == htons(ETH_P_PPP_SES)) + word1 |= IPQESS_TPD_PPPOE_EN; + + len = skb_headlen(skb); + + first_desc = ipqess_tx_desc_next(tx_ring); + desc = first_desc; + if (lso_word1 & IPQESS_TPD_LSO_V2_EN) { + desc->addr = cpu_to_le32(skb->len); + desc->word1 = cpu_to_le32(word1 | lso_word1); + desc->svlan_tag = cpu_to_le16(svlan_tag); + desc->word3 = cpu_to_le32(word3); + desc = ipqess_tx_desc_next(tx_ring); + } + + buf = ipqess_get_tx_buffer(tx_ring, desc); + buf->length = len; + buf->dma = dma_map_single(&pdev->dev, skb->data, len, DMA_TO_DEVICE); + + if (dma_mapping_error(&pdev->dev, buf->dma)) + goto dma_error; + + desc->addr = cpu_to_le32(buf->dma); + desc->len = cpu_to_le16(len); + + buf->flags |= IPQESS_DESC_SINGLE; + desc->word1 = cpu_to_le32(word1 | lso_word1); + desc->svlan_tag = cpu_to_le16(svlan_tag); + desc->word3 = cpu_to_le32(word3); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + len = skb_frag_size(frag); + desc = ipqess_tx_desc_next(tx_ring); + buf = ipqess_get_tx_buffer(tx_ring, desc); + buf->length = len; + buf->flags |= IPQESS_DESC_PAGE; + buf->dma = skb_frag_dma_map(&pdev->dev, frag, 0, len, + DMA_TO_DEVICE); + + if (dma_mapping_error(&pdev->dev, buf->dma)) + goto dma_error; + + desc->addr = cpu_to_le32(buf->dma); + desc->len = cpu_to_le16(len); + desc->svlan_tag = cpu_to_le16(svlan_tag); + desc->word1 = cpu_to_le32(word1 | lso_word1); + desc->word3 = cpu_to_le32(word3); + } + desc->word1 |= cpu_to_le32(1 << IPQESS_TPD_EOP_SHIFT); + buf->skb = skb; + buf->flags |= IPQESS_DESC_LAST; + + return 0; + +dma_error: + ipqess_rollback_tx(tx_ring->ess, first_desc, tx_ring->ring_id); + dev_err(&pdev->dev, "TX DMA map failed\n"); + +vlan_tag_error: + return -ENOMEM; +} + +static void ipqess_kick_tx(struct ipqess_tx_ring *tx_ring) +{ + /* Ensure that all TPDs has been written completely */ + dma_wmb(); + + /* update software producer index */ + ipqess_w32(tx_ring->ess, IPQESS_REG_TPD_IDX_Q(tx_ring->idx), + tx_ring->head); +} + +static netdev_tx_t ipqess_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ipqess *ess = netdev_priv(netdev); + struct ipqess_tx_ring *tx_ring; + int avail; + int tx_num; + int ret; + + tx_ring = &ess->tx_ring[skb_get_queue_mapping(skb)]; + tx_num = ipqess_cal_txd_req(skb); + avail = ipqess_tx_desc_available(tx_ring); + if (avail < tx_num) { + netdev_dbg(netdev, + "stopping tx queue %d, avail=%d req=%d im=%x\n", + tx_ring->idx, avail, tx_num, + ipqess_r32(tx_ring->ess, + IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx))); + netif_tx_stop_queue(tx_ring->nq); + ipqess_w32(tx_ring->ess, IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), 0x1); + ipqess_kick_tx(tx_ring); + return NETDEV_TX_BUSY; + } + + ret = ipqess_tx_map_and_fill(tx_ring, skb); + if (ret) { + dev_kfree_skb_any(skb); + ess->stats.tx_errors++; + goto err_out; + } + + ess->stats.tx_packets++; + ess->stats.tx_bytes += skb->len; + netdev_tx_sent_queue(tx_ring->nq, skb->len); + + if (!netdev_xmit_more() || netif_xmit_stopped(tx_ring->nq)) + ipqess_kick_tx(tx_ring); + +err_out: + return NETDEV_TX_OK; +} + +static int ipqess_set_mac_address(struct net_device *netdev, void *p) +{ + struct ipqess *ess = netdev_priv(netdev); + const char *macaddr = netdev->dev_addr; + int ret = eth_mac_addr(netdev, p); + + if (ret) + return ret; + + ipqess_w32(ess, IPQESS_REG_MAC_CTRL1, (macaddr[0] << 8) | macaddr[1]); + ipqess_w32(ess, IPQESS_REG_MAC_CTRL0, + (macaddr[2] << 24) | (macaddr[3] << 16) | (macaddr[4] << 8) | + macaddr[5]); + + return 0; +} + +static void ipqess_tx_timeout(struct net_device *netdev, unsigned int txq_id) +{ + netdev_err(netdev, "TX timeout on queue %d\n", txq_id); +} + +static const struct net_device_ops ipqess_axi_netdev_ops = { + .ndo_init = ipqess_init, + .ndo_uninit = ipqess_uninit, + .ndo_open = ipqess_open, + .ndo_stop = ipqess_stop, + .ndo_eth_ioctl = ipqess_do_ioctl, + .ndo_start_xmit = ipqess_xmit, + .ndo_get_stats = ipqess_get_stats, + .ndo_set_mac_address = ipqess_set_mac_address, + .ndo_tx_timeout = ipqess_tx_timeout, +}; + +static void ipqess_hw_stop(struct ipqess *ess) +{ + int i; + + /* disable all RX queue IRQs */ + for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) + ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(i), 0); + + /* disable all TX queue IRQs */ + for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) + ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(i), 0); + + /* disable all other IRQs */ + ipqess_w32(ess, IPQESS_REG_MISC_IMR, 0); + ipqess_w32(ess, IPQESS_REG_WOL_IMR, 0); + + /* clear the IRQ status registers */ + ipqess_w32(ess, IPQESS_REG_RX_ISR, 0xff); + ipqess_w32(ess, IPQESS_REG_TX_ISR, 0xffff); + ipqess_w32(ess, IPQESS_REG_MISC_ISR, 0x1fff); + ipqess_w32(ess, IPQESS_REG_WOL_ISR, 0x1); + ipqess_w32(ess, IPQESS_REG_WOL_CTRL, 0); + + /* disable RX and TX queues */ + ipqess_m32(ess, IPQESS_RXQ_CTRL_EN_MASK, 0, IPQESS_REG_RXQ_CTRL); + ipqess_m32(ess, IPQESS_TXQ_CTRL_TXQ_EN, 0, IPQESS_REG_TXQ_CTRL); +} + +static int ipqess_hw_init(struct ipqess *ess) +{ + int i, err; + u32 tmp; + + ipqess_hw_stop(ess); + + ipqess_m32(ess, BIT(IPQESS_INTR_SW_IDX_W_TYP_SHIFT), + IPQESS_INTR_SW_IDX_W_TYPE << IPQESS_INTR_SW_IDX_W_TYP_SHIFT, + IPQESS_REG_INTR_CTRL); + + /* enable IRQ delay slot */ + ipqess_w32(ess, IPQESS_REG_IRQ_MODRT_TIMER_INIT, + (IPQESS_TX_IMT << IPQESS_IRQ_MODRT_TX_TIMER_SHIFT) | + (IPQESS_RX_IMT << IPQESS_IRQ_MODRT_RX_TIMER_SHIFT)); + + /* Set Customer and Service VLAN TPIDs */ + ipqess_w32(ess, IPQESS_REG_VLAN_CFG, + (ETH_P_8021Q << IPQESS_VLAN_CFG_CVLAN_TPID_SHIFT) | + (ETH_P_8021AD << IPQESS_VLAN_CFG_SVLAN_TPID_SHIFT)); + + /* Configure the TX Queue bursting */ + ipqess_w32(ess, IPQESS_REG_TXQ_CTRL, + (IPQESS_TPD_BURST << IPQESS_TXQ_NUM_TPD_BURST_SHIFT) | + (IPQESS_TXF_BURST << IPQESS_TXQ_TXF_BURST_NUM_SHIFT) | + IPQESS_TXQ_CTRL_TPD_BURST_EN); + + /* Set RSS type */ + ipqess_w32(ess, IPQESS_REG_RSS_TYPE, + IPQESS_RSS_TYPE_IPV4TCP | IPQESS_RSS_TYPE_IPV6_TCP | + IPQESS_RSS_TYPE_IPV4_UDP | IPQESS_RSS_TYPE_IPV6UDP | + IPQESS_RSS_TYPE_IPV4 | IPQESS_RSS_TYPE_IPV6); + + /* Set RFD ring burst and threshold */ + ipqess_w32(ess, IPQESS_REG_RX_DESC1, + (IPQESS_RFD_BURST << IPQESS_RXQ_RFD_BURST_NUM_SHIFT) | + (IPQESS_RFD_THR << IPQESS_RXQ_RFD_PF_THRESH_SHIFT) | + (IPQESS_RFD_LTHR << IPQESS_RXQ_RFD_LOW_THRESH_SHIFT)); + + /* Set Rx FIFO + * - threshold to start to DMA data to host + */ + ipqess_w32(ess, IPQESS_REG_RXQ_CTRL, + IPQESS_FIFO_THRESH_128_BYTE | IPQESS_RXQ_CTRL_RMV_VLAN); + + err = ipqess_rx_ring_alloc(ess); + if (err) + return err; + + err = ipqess_tx_ring_alloc(ess); + if (err) + goto err_rx_ring_free; + + /* Load all of ring base addresses above into the dma engine */ + ipqess_m32(ess, 0, BIT(IPQESS_LOAD_PTR_SHIFT), IPQESS_REG_TX_SRAM_PART); + + /* Disable TX FIFO low watermark and high watermark */ + ipqess_w32(ess, IPQESS_REG_TXF_WATER_MARK, 0); + + /* Configure RSS indirection table. + * 128 hash will be configured in the following + * pattern: hash{0,1,2,3} = {Q0,Q2,Q4,Q6} respectively + * and so on + */ + for (i = 0; i < IPQESS_NUM_IDT; i++) + ipqess_w32(ess, IPQESS_REG_RSS_IDT(i), IPQESS_RSS_IDT_VALUE); + + /* Configure load balance mapping table. + * 4 table entry will be configured according to the + * following pattern: load_balance{0,1,2,3} = {Q0,Q1,Q3,Q4} + * respectively. + */ + ipqess_w32(ess, IPQESS_REG_LB_RING, IPQESS_LB_REG_VALUE); + + /* Configure Virtual queue for Tx rings */ + ipqess_w32(ess, IPQESS_REG_VQ_CTRL0, IPQESS_VQ_REG_VALUE); + ipqess_w32(ess, IPQESS_REG_VQ_CTRL1, IPQESS_VQ_REG_VALUE); + + /* Configure Max AXI Burst write size to 128 bytes*/ + ipqess_w32(ess, IPQESS_REG_AXIW_CTRL_MAXWRSIZE, + IPQESS_AXIW_MAXWRSIZE_VALUE); + + /* Enable TX queues */ + ipqess_m32(ess, 0, IPQESS_TXQ_CTRL_TXQ_EN, IPQESS_REG_TXQ_CTRL); + + /* Enable RX queues */ + tmp = 0; + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) + tmp |= IPQESS_RXQ_CTRL_EN(ess->rx_ring[i].idx); + + ipqess_m32(ess, IPQESS_RXQ_CTRL_EN_MASK, tmp, IPQESS_REG_RXQ_CTRL); + + return 0; + +err_rx_ring_free: + + ipqess_rx_ring_free(ess); + return err; +} + +static void ipqess_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + /* Nothing to do, use fixed Internal mode */ +} + +static void ipqess_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ + /* Nothing to do, use fixed Internal mode */ +} + +static void ipqess_mac_link_up(struct phylink_config *config, + struct phy_device *phy, unsigned int mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + /* Nothing to do, use fixed Internal mode */ +} + +static struct phylink_mac_ops ipqess_phylink_mac_ops = { + .mac_config = ipqess_mac_config, + .mac_link_up = ipqess_mac_link_up, + .mac_link_down = ipqess_mac_link_down, +}; + +static void ipqess_reset(struct ipqess *ess) +{ + reset_control_assert(ess->ess_rst); + + mdelay(10); + + reset_control_deassert(ess->ess_rst); + + /* Waiting for all inner tables to be flushed and reinitialized. + * This takes between 5 and 10 ms + */ + + mdelay(10); +} + +static int ipqess_axi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct net_device *netdev; + phy_interface_t phy_mode; + struct ipqess *ess; + int i, err = 0; + + netdev = devm_alloc_etherdev_mqs(&pdev->dev, sizeof(*ess), + IPQESS_NETDEV_QUEUES, + IPQESS_NETDEV_QUEUES); + if (!netdev) + return -ENOMEM; + + ess = netdev_priv(netdev); + ess->netdev = netdev; + ess->pdev = pdev; + spin_lock_init(&ess->stats_lock); + SET_NETDEV_DEV(netdev, &pdev->dev); + platform_set_drvdata(pdev, netdev); + + err = of_get_ethdev_address(np, netdev); + if (err) { + dev_dbg(&pdev->dev, "failed to get MAC address from DT: %d\n", err); + if (err == -EPROBE_DEFER) + return err; + eth_hw_addr_random(netdev); + dev_info(&pdev->dev, "using random MAC address %pM\n", + netdev->dev_addr); + } + + ess->hw_addr = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(ess->hw_addr)) + return PTR_ERR(ess->hw_addr); + + err = of_get_phy_mode(np, &phy_mode); + if (err) { + dev_err(&pdev->dev, "incorrect phy-mode\n"); + return err; + } + + ess->ess_clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(ess->ess_clk)) + clk_prepare_enable(ess->ess_clk); + + ess->ess_rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(ess->ess_rst)) + goto err_clk; + + ipqess_reset(ess); + + ess->phylink_config.dev = &netdev->dev; + ess->phylink_config.type = PHYLINK_NETDEV; + ess->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000FD; + + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + ess->phylink_config.supported_interfaces); + + ess->phylink = phylink_create(&ess->phylink_config, + of_fwnode_handle(np), phy_mode, + &ipqess_phylink_mac_ops); + if (IS_ERR(ess->phylink)) { + err = PTR_ERR(ess->phylink); + goto err_clk; + } + + for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) { + ess->tx_irq[i] = platform_get_irq(pdev, i); + scnprintf(ess->tx_irq_names[i], sizeof(ess->tx_irq_names[i]), + "%s:txq%d", pdev->name, i); + } + + for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) { + ess->rx_irq[i] = platform_get_irq(pdev, i + IPQESS_MAX_TX_QUEUE); + scnprintf(ess->rx_irq_names[i], sizeof(ess->rx_irq_names[i]), + "%s:rxq%d", pdev->name, i); + } + + netdev->netdev_ops = &ipqess_axi_netdev_ops; + netdev->features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_TSO | NETIF_F_GRO | NETIF_F_SG; + /* feature change is not supported yet */ + netdev->hw_features = 0; + netdev->vlan_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_GRO; + netdev->watchdog_timeo = 5 * HZ; + netdev->base_addr = (u32)ess->hw_addr; + netdev->max_mtu = 9000; + netdev->gso_max_segs = IPQESS_TX_RING_SIZE / 2; + + ipqess_set_ethtool_ops(netdev); + + err = ipqess_hw_init(ess); + if (err) + goto err_phylink; + + for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { + netif_napi_add_tx(netdev, &ess->tx_ring[i].napi_tx, ipqess_tx_napi); + netif_napi_add(netdev, &ess->rx_ring[i].napi_rx, ipqess_rx_napi); + } + + err = register_netdev(netdev); + if (err) + goto err_hw_stop; + + return 0; + +err_hw_stop: + ipqess_hw_stop(ess); + + ipqess_tx_ring_free(ess); + ipqess_rx_ring_free(ess); +err_phylink: + phylink_destroy(ess->phylink); + +err_clk: + clk_disable_unprepare(ess->ess_clk); + + return err; +} + +static void ipqess_axi_remove(struct platform_device *pdev) +{ + const struct net_device *netdev = platform_get_drvdata(pdev); + struct ipqess *ess = netdev_priv(netdev); + + unregister_netdev(ess->netdev); + ipqess_hw_stop(ess); + + ipqess_tx_ring_free(ess); + ipqess_rx_ring_free(ess); + + phylink_destroy(ess->phylink); + clk_disable_unprepare(ess->ess_clk); +} + +static const struct of_device_id ipqess_of_mtable[] = { + {.compatible = "qcom,ipq4019-ess-edma" }, + {} +}; +MODULE_DEVICE_TABLE(of, ipqess_of_mtable); + +static struct platform_driver ipqess_axi_driver = { + .driver = { + .name = "ipqess-edma", + .of_match_table = ipqess_of_mtable, + }, + .probe = ipqess_axi_probe, + .remove = ipqess_axi_remove, +}; + +module_platform_driver(ipqess_axi_driver); + +MODULE_AUTHOR("Qualcomm Atheros Inc"); +MODULE_AUTHOR("John Crispin "); +MODULE_AUTHOR("Christian Lamparter "); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_AUTHOR("Maxime Chevallier "); +MODULE_LICENSE("GPL"); diff --git a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.h b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.h new file mode 100644 index 0000000000..dae17f3a23 --- /dev/null +++ b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.h @@ -0,0 +1,518 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR ISC) */ +/* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2017 - 2018, John Crispin + * Copyright (c) 2018 - 2019, Christian Lamparter + * Copyright (c) 2020 - 2021, Gabor Juhos + * Copyright (c) 2021 - 2022, Maxime Chevallier + * + */ + +#ifndef _IPQESS_H_ +#define _IPQESS_H_ + +#define IPQESS_NETDEV_QUEUES 4 + +#define IPQESS_TPD_EOP_SHIFT 31 + +#define IPQESS_PORT_ID_SHIFT 12 +#define IPQESS_PORT_ID_MASK 0x7 + +/* tpd word 3 bit 18-28 */ +#define IPQESS_TPD_PORT_BITMAP_SHIFT 18 + +#define IPQESS_TPD_FROM_CPU_SHIFT 25 + +#define IPQESS_RX_RING_SIZE 128 +#define IPQESS_RX_HEAD_BUFF_SIZE 1540 +#define IPQESS_TX_RING_SIZE 128 +#define IPQESS_MAX_RX_QUEUE 8 +#define IPQESS_MAX_TX_QUEUE 16 + +/* Configurations */ +#define IPQESS_INTR_CLEAR_TYPE 0 +#define IPQESS_INTR_SW_IDX_W_TYPE 0 +#define IPQESS_FIFO_THRESH_TYPE 0 +#define IPQESS_RSS_TYPE 0 +#define IPQESS_RX_IMT 0x0020 +#define IPQESS_TX_IMT 0x0050 +#define IPQESS_TPD_BURST 5 +#define IPQESS_TXF_BURST 0x100 +#define IPQESS_RFD_BURST 8 +#define IPQESS_RFD_THR 16 +#define IPQESS_RFD_LTHR 0 + +/* Flags used in transmit direction */ +#define IPQESS_DESC_LAST 0x1 +#define IPQESS_DESC_SINGLE 0x2 +#define IPQESS_DESC_PAGE 0x4 + +struct ipqess_statistics { + u32 tx_q0_pkt; + u32 tx_q1_pkt; + u32 tx_q2_pkt; + u32 tx_q3_pkt; + u32 tx_q4_pkt; + u32 tx_q5_pkt; + u32 tx_q6_pkt; + u32 tx_q7_pkt; + u32 tx_q8_pkt; + u32 tx_q9_pkt; + u32 tx_q10_pkt; + u32 tx_q11_pkt; + u32 tx_q12_pkt; + u32 tx_q13_pkt; + u32 tx_q14_pkt; + u32 tx_q15_pkt; + u32 tx_q0_byte; + u32 tx_q1_byte; + u32 tx_q2_byte; + u32 tx_q3_byte; + u32 tx_q4_byte; + u32 tx_q5_byte; + u32 tx_q6_byte; + u32 tx_q7_byte; + u32 tx_q8_byte; + u32 tx_q9_byte; + u32 tx_q10_byte; + u32 tx_q11_byte; + u32 tx_q12_byte; + u32 tx_q13_byte; + u32 tx_q14_byte; + u32 tx_q15_byte; + u32 rx_q0_pkt; + u32 rx_q1_pkt; + u32 rx_q2_pkt; + u32 rx_q3_pkt; + u32 rx_q4_pkt; + u32 rx_q5_pkt; + u32 rx_q6_pkt; + u32 rx_q7_pkt; + u32 rx_q0_byte; + u32 rx_q1_byte; + u32 rx_q2_byte; + u32 rx_q3_byte; + u32 rx_q4_byte; + u32 rx_q5_byte; + u32 rx_q6_byte; + u32 rx_q7_byte; + u32 tx_desc_error; +}; + +struct ipqess_tx_desc { + __le16 len; + __le16 svlan_tag; + __le32 word1; + __le32 addr; + __le32 word3; +} __aligned(16) __packed; + +struct ipqess_rx_desc { + __le16 rrd0; + __le16 rrd1; + __le16 rrd2; + __le16 rrd3; + __le16 rrd4; + __le16 rrd5; + __le16 rrd6; + __le16 rrd7; +} __aligned(16) __packed; + +struct ipqess_buf { + struct sk_buff *skb; + dma_addr_t dma; + u32 flags; + u16 length; +}; + +struct ipqess_tx_ring { + struct napi_struct napi_tx; + u32 idx; + int ring_id; + struct ipqess *ess; + struct netdev_queue *nq; + struct ipqess_tx_desc *hw_desc; + struct ipqess_buf *buf; + dma_addr_t dma; + u16 count; + u16 head; + u16 tail; +}; + +struct ipqess_rx_ring { + struct napi_struct napi_rx; + u32 idx; + int ring_id; + struct ipqess *ess; + struct device *ppdev; + struct ipqess_rx_desc **hw_desc; + struct ipqess_buf *buf; + dma_addr_t dma; + u16 head; + u16 tail; + atomic_t refill_count; +}; + +struct ipqess_rx_ring_refill { + struct ipqess_rx_ring *rx_ring; + struct work_struct refill_work; +}; + +#define IPQESS_IRQ_NAME_LEN 32 + +struct ipqess { + struct net_device *netdev; + void __iomem *hw_addr; + + struct clk *ess_clk; + struct reset_control *ess_rst; + + struct ipqess_rx_ring rx_ring[IPQESS_NETDEV_QUEUES]; + + struct platform_device *pdev; + struct phylink *phylink; + struct phylink_config phylink_config; + struct ipqess_tx_ring tx_ring[IPQESS_NETDEV_QUEUES]; + + struct ipqess_statistics ipqess_stats; + + /* Protects stats */ + spinlock_t stats_lock; + struct net_device_stats stats; + + struct ipqess_rx_ring_refill rx_refill[IPQESS_NETDEV_QUEUES]; + u32 tx_irq[IPQESS_MAX_TX_QUEUE]; + char tx_irq_names[IPQESS_MAX_TX_QUEUE][IPQESS_IRQ_NAME_LEN]; + u32 rx_irq[IPQESS_MAX_RX_QUEUE]; + char rx_irq_names[IPQESS_MAX_TX_QUEUE][IPQESS_IRQ_NAME_LEN]; +}; + +void ipqess_set_ethtool_ops(struct net_device *netdev); +void ipqess_update_hw_stats(struct ipqess *ess); + +/* register definition */ +#define IPQESS_REG_MAS_CTRL 0x0 +#define IPQESS_REG_TIMEOUT_CTRL 0x004 +#define IPQESS_REG_DBG0 0x008 +#define IPQESS_REG_DBG1 0x00C +#define IPQESS_REG_SW_CTRL0 0x100 +#define IPQESS_REG_SW_CTRL1 0x104 + +/* Interrupt Status Register */ +#define IPQESS_REG_RX_ISR 0x200 +#define IPQESS_REG_TX_ISR 0x208 +#define IPQESS_REG_MISC_ISR 0x210 +#define IPQESS_REG_WOL_ISR 0x218 + +#define IPQESS_MISC_ISR_RX_URG_Q(x) (1 << (x)) + +#define IPQESS_MISC_ISR_AXIR_TIMEOUT 0x00000100 +#define IPQESS_MISC_ISR_AXIR_ERR 0x00000200 +#define IPQESS_MISC_ISR_TXF_DEAD 0x00000400 +#define IPQESS_MISC_ISR_AXIW_ERR 0x00000800 +#define IPQESS_MISC_ISR_AXIW_TIMEOUT 0x00001000 + +#define IPQESS_WOL_ISR 0x00000001 + +/* Interrupt Mask Register */ +#define IPQESS_REG_MISC_IMR 0x214 +#define IPQESS_REG_WOL_IMR 0x218 + +#define IPQESS_RX_IMR_NORMAL_MASK 0x1 +#define IPQESS_TX_IMR_NORMAL_MASK 0x1 +#define IPQESS_MISC_IMR_NORMAL_MASK 0x80001FFF +#define IPQESS_WOL_IMR_NORMAL_MASK 0x1 + +/* Edma receive consumer index */ +#define IPQESS_REG_RX_SW_CONS_IDX_Q(x) (0x220 + ((x) << 2)) /* x is the queue id */ + +/* Edma transmit consumer index */ +#define IPQESS_REG_TX_SW_CONS_IDX_Q(x) (0x240 + ((x) << 2)) /* x is the queue id */ + +/* IRQ Moderator Initial Timer Register */ +#define IPQESS_REG_IRQ_MODRT_TIMER_INIT 0x280 +#define IPQESS_IRQ_MODRT_TIMER_MASK 0xFFFF +#define IPQESS_IRQ_MODRT_RX_TIMER_SHIFT 0 +#define IPQESS_IRQ_MODRT_TX_TIMER_SHIFT 16 + +/* Interrupt Control Register */ +#define IPQESS_REG_INTR_CTRL 0x284 +#define IPQESS_INTR_CLR_TYP_SHIFT 0 +#define IPQESS_INTR_SW_IDX_W_TYP_SHIFT 1 +#define IPQESS_INTR_CLEAR_TYPE_W1 0 +#define IPQESS_INTR_CLEAR_TYPE_R 1 + +/* RX Interrupt Mask Register */ +#define IPQESS_REG_RX_INT_MASK_Q(x) (0x300 + ((x) << 2)) /* x = queue id */ + +/* TX Interrupt mask register */ +#define IPQESS_REG_TX_INT_MASK_Q(x) (0x340 + ((x) << 2)) /* x = queue id */ + +/* Load Ptr Register + * Software sets this bit after the initialization of the head and tail + */ +#define IPQESS_REG_TX_SRAM_PART 0x400 +#define IPQESS_LOAD_PTR_SHIFT 16 + +/* TXQ Control Register */ +#define IPQESS_REG_TXQ_CTRL 0x404 +#define IPQESS_TXQ_CTRL_IP_OPTION_EN 0x10 +#define IPQESS_TXQ_CTRL_TXQ_EN 0x20 +#define IPQESS_TXQ_CTRL_ENH_MODE 0x40 +#define IPQESS_TXQ_CTRL_LS_8023_EN 0x80 +#define IPQESS_TXQ_CTRL_TPD_BURST_EN 0x100 +#define IPQESS_TXQ_CTRL_LSO_BREAK_EN 0x200 +#define IPQESS_TXQ_NUM_TPD_BURST_MASK 0xF +#define IPQESS_TXQ_TXF_BURST_NUM_MASK 0xFFFF +#define IPQESS_TXQ_NUM_TPD_BURST_SHIFT 0 +#define IPQESS_TXQ_TXF_BURST_NUM_SHIFT 16 + +#define IPQESS_REG_TXF_WATER_MARK 0x408 /* In 8-bytes */ +#define IPQESS_TXF_WATER_MARK_MASK 0x0FFF +#define IPQESS_TXF_LOW_WATER_MARK_SHIFT 0 +#define IPQESS_TXF_HIGH_WATER_MARK_SHIFT 16 +#define IPQESS_TXQ_CTRL_BURST_MODE_EN 0x80000000 + +/* WRR Control Register */ +#define IPQESS_REG_WRR_CTRL_Q0_Q3 0x40c +#define IPQESS_REG_WRR_CTRL_Q4_Q7 0x410 +#define IPQESS_REG_WRR_CTRL_Q8_Q11 0x414 +#define IPQESS_REG_WRR_CTRL_Q12_Q15 0x418 + +/* Weight round robin(WRR), it takes queue as input, and computes + * starting bits where we need to write the weight for a particular + * queue + */ +#define IPQESS_WRR_SHIFT(x) (((x) * 5) % 20) + +/* Tx Descriptor Control Register */ +#define IPQESS_REG_TPD_RING_SIZE 0x41C +#define IPQESS_TPD_RING_SIZE_SHIFT 0 +#define IPQESS_TPD_RING_SIZE_MASK 0xFFFF + +/* Transmit descriptor base address */ +#define IPQESS_REG_TPD_BASE_ADDR_Q(x) (0x420 + ((x) << 2)) /* x = queue id */ + +/* TPD Index Register */ +#define IPQESS_REG_TPD_IDX_Q(x) (0x460 + ((x) << 2)) /* x = queue id */ + +#define IPQESS_TPD_PROD_IDX_BITS 0x0000FFFF +#define IPQESS_TPD_CONS_IDX_BITS 0xFFFF0000 +#define IPQESS_TPD_PROD_IDX_MASK 0xFFFF +#define IPQESS_TPD_CONS_IDX_MASK 0xFFFF +#define IPQESS_TPD_PROD_IDX_SHIFT 0 +#define IPQESS_TPD_CONS_IDX_SHIFT 16 + +/* TX Virtual Queue Mapping Control Register */ +#define IPQESS_REG_VQ_CTRL0 0x4A0 +#define IPQESS_REG_VQ_CTRL1 0x4A4 + +/* Virtual QID shift, it takes queue as input, and computes + * Virtual QID position in virtual qid control register + */ +#define IPQESS_VQ_ID_SHIFT(i) (((i) * 3) % 24) + +/* Virtual Queue Default Value */ +#define IPQESS_VQ_REG_VALUE 0x240240 + +/* Tx side Port Interface Control Register */ +#define IPQESS_REG_PORT_CTRL 0x4A8 +#define IPQESS_PAD_EN_SHIFT 15 + +/* Tx side VLAN Configuration Register */ +#define IPQESS_REG_VLAN_CFG 0x4AC + +#define IPQESS_VLAN_CFG_SVLAN_TPID_SHIFT 0 +#define IPQESS_VLAN_CFG_SVLAN_TPID_MASK 0xffff +#define IPQESS_VLAN_CFG_CVLAN_TPID_SHIFT 16 +#define IPQESS_VLAN_CFG_CVLAN_TPID_MASK 0xffff + +#define IPQESS_TX_CVLAN 16 +#define IPQESS_TX_INS_CVLAN 17 +#define IPQESS_TX_CVLAN_TAG_SHIFT 0 + +#define IPQESS_TX_SVLAN 14 +#define IPQESS_TX_INS_SVLAN 15 +#define IPQESS_TX_SVLAN_TAG_SHIFT 16 + +/* Tx Queue Packet Statistic Register */ +#define IPQESS_REG_TX_STAT_PKT_Q(x) (0x700 + ((x) << 3)) /* x = queue id */ + +#define IPQESS_TX_STAT_PKT_MASK 0xFFFFFF + +/* Tx Queue Byte Statistic Register */ +#define IPQESS_REG_TX_STAT_BYTE_Q(x) (0x704 + ((x) << 3)) /* x = queue id */ + +/* Load Balance Based Ring Offset Register */ +#define IPQESS_REG_LB_RING 0x800 +#define IPQESS_LB_RING_ENTRY_MASK 0xff +#define IPQESS_LB_RING_ID_MASK 0x7 +#define IPQESS_LB_RING_PROFILE_ID_MASK 0x3 +#define IPQESS_LB_RING_ENTRY_BIT_OFFSET 8 +#define IPQESS_LB_RING_ID_OFFSET 0 +#define IPQESS_LB_RING_PROFILE_ID_OFFSET 3 +#define IPQESS_LB_REG_VALUE 0x6040200 + +/* Load Balance Priority Mapping Register */ +#define IPQESS_REG_LB_PRI_START 0x804 +#define IPQESS_REG_LB_PRI_END 0x810 +#define IPQESS_LB_PRI_REG_INC 4 +#define IPQESS_LB_PRI_ENTRY_BIT_OFFSET 4 +#define IPQESS_LB_PRI_ENTRY_MASK 0xf + +/* RSS Priority Mapping Register */ +#define IPQESS_REG_RSS_PRI 0x820 +#define IPQESS_RSS_PRI_ENTRY_MASK 0xf +#define IPQESS_RSS_RING_ID_MASK 0x7 +#define IPQESS_RSS_PRI_ENTRY_BIT_OFFSET 4 + +/* RSS Indirection Register */ +#define IPQESS_REG_RSS_IDT(x) (0x840 + ((x) << 2)) /* x = No. of indirection table */ +#define IPQESS_NUM_IDT 16 +#define IPQESS_RSS_IDT_VALUE 0x64206420 + +/* Default RSS Ring Register */ +#define IPQESS_REG_DEF_RSS 0x890 +#define IPQESS_DEF_RSS_MASK 0x7 + +/* RSS Hash Function Type Register */ +#define IPQESS_REG_RSS_TYPE 0x894 +#define IPQESS_RSS_TYPE_NONE 0x01 +#define IPQESS_RSS_TYPE_IPV4TCP 0x02 +#define IPQESS_RSS_TYPE_IPV6_TCP 0x04 +#define IPQESS_RSS_TYPE_IPV4_UDP 0x08 +#define IPQESS_RSS_TYPE_IPV6UDP 0x10 +#define IPQESS_RSS_TYPE_IPV4 0x20 +#define IPQESS_RSS_TYPE_IPV6 0x40 +#define IPQESS_RSS_HASH_MODE_MASK 0x7f + +#define IPQESS_REG_RSS_HASH_VALUE 0x8C0 + +#define IPQESS_REG_RSS_TYPE_RESULT 0x8C4 + +#define IPQESS_HASH_TYPE_START 0 +#define IPQESS_HASH_TYPE_END 5 +#define IPQESS_HASH_TYPE_SHIFT 12 + +#define IPQESS_RFS_FLOW_ENTRIES 1024 +#define IPQESS_RFS_FLOW_ENTRIES_MASK (IPQESS_RFS_FLOW_ENTRIES - 1) +#define IPQESS_RFS_EXPIRE_COUNT_PER_CALL 128 + +/* RFD Base Address Register */ +#define IPQESS_REG_RFD_BASE_ADDR_Q(x) (0x950 + ((x) << 2)) /* x = queue id */ + +/* RFD Index Register */ +#define IPQESS_REG_RFD_IDX_Q(x) (0x9B0 + ((x) << 2)) /* x = queue id */ + +#define IPQESS_RFD_PROD_IDX_BITS 0x00000FFF +#define IPQESS_RFD_CONS_IDX_BITS 0x0FFF0000 +#define IPQESS_RFD_PROD_IDX_MASK 0xFFF +#define IPQESS_RFD_CONS_IDX_MASK 0xFFF +#define IPQESS_RFD_PROD_IDX_SHIFT 0 +#define IPQESS_RFD_CONS_IDX_SHIFT 16 + +/* Rx Descriptor Control Register */ +#define IPQESS_REG_RX_DESC0 0xA10 +#define IPQESS_RFD_RING_SIZE_MASK 0xFFF +#define IPQESS_RX_BUF_SIZE_MASK 0xFFFF +#define IPQESS_RFD_RING_SIZE_SHIFT 0 +#define IPQESS_RX_BUF_SIZE_SHIFT 16 + +#define IPQESS_REG_RX_DESC1 0xA14 +#define IPQESS_RXQ_RFD_BURST_NUM_MASK 0x3F +#define IPQESS_RXQ_RFD_PF_THRESH_MASK 0x1F +#define IPQESS_RXQ_RFD_LOW_THRESH_MASK 0xFFF +#define IPQESS_RXQ_RFD_BURST_NUM_SHIFT 0 +#define IPQESS_RXQ_RFD_PF_THRESH_SHIFT 8 +#define IPQESS_RXQ_RFD_LOW_THRESH_SHIFT 16 + +/* RXQ Control Register */ +#define IPQESS_REG_RXQ_CTRL 0xA18 +#define IPQESS_FIFO_THRESH_TYPE_SHIF 0 +#define IPQESS_FIFO_THRESH_128_BYTE 0x0 +#define IPQESS_FIFO_THRESH_64_BYTE 0x1 +#define IPQESS_RXQ_CTRL_RMV_VLAN 0x00000002 +#define IPQESS_RXQ_CTRL_EN_MASK GENMASK(15, 8) +#define IPQESS_RXQ_CTRL_EN(__qid) BIT(8 + (__qid)) + +/* AXI Burst Size Config */ +#define IPQESS_REG_AXIW_CTRL_MAXWRSIZE 0xA1C +#define IPQESS_AXIW_MAXWRSIZE_VALUE 0x0 + +/* Rx Statistics Register */ +#define IPQESS_REG_RX_STAT_BYTE_Q(x) (0xA30 + ((x) << 2)) /* x = queue id */ +#define IPQESS_REG_RX_STAT_PKT_Q(x) (0xA50 + ((x) << 2)) /* x = queue id */ + +/* WoL Pattern Length Register */ +#define IPQESS_REG_WOL_PATTERN_LEN0 0xC00 +#define IPQESS_WOL_PT_LEN_MASK 0xFF +#define IPQESS_WOL_PT0_LEN_SHIFT 0 +#define IPQESS_WOL_PT1_LEN_SHIFT 8 +#define IPQESS_WOL_PT2_LEN_SHIFT 16 +#define IPQESS_WOL_PT3_LEN_SHIFT 24 + +#define IPQESS_REG_WOL_PATTERN_LEN1 0xC04 +#define IPQESS_WOL_PT4_LEN_SHIFT 0 +#define IPQESS_WOL_PT5_LEN_SHIFT 8 +#define IPQESS_WOL_PT6_LEN_SHIFT 16 + +/* WoL Control Register */ +#define IPQESS_REG_WOL_CTRL 0xC08 +#define IPQESS_WOL_WK_EN 0x00000001 +#define IPQESS_WOL_MG_EN 0x00000002 +#define IPQESS_WOL_PT0_EN 0x00000004 +#define IPQESS_WOL_PT1_EN 0x00000008 +#define IPQESS_WOL_PT2_EN 0x00000010 +#define IPQESS_WOL_PT3_EN 0x00000020 +#define IPQESS_WOL_PT4_EN 0x00000040 +#define IPQESS_WOL_PT5_EN 0x00000080 +#define IPQESS_WOL_PT6_EN 0x00000100 + +/* MAC Control Register */ +#define IPQESS_REG_MAC_CTRL0 0xC20 +#define IPQESS_REG_MAC_CTRL1 0xC24 + +/* WoL Pattern Register */ +#define IPQESS_REG_WOL_PATTERN_START 0x5000 +#define IPQESS_PATTERN_PART_REG_OFFSET 0x40 + +/* TX descriptor fields */ +#define IPQESS_TPD_HDR_SHIFT 0 +#define IPQESS_TPD_PPPOE_EN 0x00000100 +#define IPQESS_TPD_IP_CSUM_EN 0x00000200 +#define IPQESS_TPD_TCP_CSUM_EN 0x0000400 +#define IPQESS_TPD_UDP_CSUM_EN 0x00000800 +#define IPQESS_TPD_CUSTOM_CSUM_EN 0x00000C00 +#define IPQESS_TPD_LSO_EN 0x00001000 +#define IPQESS_TPD_LSO_V2_EN 0x00002000 +/* The VLAN_TAGGED bit is not used in the publicly available + * drivers. The definition has been stolen from the Atheros + * 'alx' driver (drivers/net/ethernet/atheros/alx/hw.h). It + * seems that it has the same meaning in regard to the EDMA + * hardware. + */ +#define IPQESS_TPD_VLAN_TAGGED 0x00004000 +#define IPQESS_TPD_IPV4_EN 0x00010000 +#define IPQESS_TPD_MSS_MASK 0x1FFF +#define IPQESS_TPD_MSS_SHIFT 18 +#define IPQESS_TPD_CUSTOM_CSUM_SHIFT 18 + +/* RRD descriptor fields */ +#define IPQESS_RRD_NUM_RFD_MASK 0x000F +#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF +#define IPQESS_RRD_SRC_PORT_NUM_MASK 0x4000 +#define IPQESS_RRD_SVLAN 0x8000 +#define IPQESS_RRD_FLOW_COOKIE_MASK 0x07FF + +#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF +#define IPQESS_RRD_CSUM_FAIL_MASK 0xC000 +#define IPQESS_RRD_CVLAN 0x0001 +#define IPQESS_RRD_DESC_VALID 0x8000 + +#define IPQESS_RRD_PRIORITY_SHIFT 4 +#define IPQESS_RRD_PRIORITY_MASK 0x7 +#define IPQESS_RRD_PORT_TYPE_SHIFT 7 +#define IPQESS_RRD_PORT_TYPE_MASK 0x1F + +#define IPQESS_RRD_PORT_ID_MASK 0x7000 + +#endif diff --git a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c new file mode 100644 index 0000000000..c407afe2b5 --- /dev/null +++ b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0 OR ISC +/* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2017 - 2018, John Crispin + * Copyright (c) 2021 - 2022, Maxime Chevallier + * + */ + +#include +#include +#include +#include + +#include "ipqess.h" + +struct ipqess_ethtool_stats { + u8 string[ETH_GSTRING_LEN]; + u32 offset; +}; + +#define IPQESS_STAT(m) offsetof(struct ipqess_statistics, m) +#define DRVINFO_LEN 32 + +static const struct ipqess_ethtool_stats ipqess_stats[] = { + {"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)}, + {"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)}, + {"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)}, + {"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)}, + {"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)}, + {"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)}, + {"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)}, + {"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)}, + {"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)}, + {"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)}, + {"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)}, + {"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)}, + {"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)}, + {"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)}, + {"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)}, + {"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)}, + {"tx_q0_byte", IPQESS_STAT(tx_q0_byte)}, + {"tx_q1_byte", IPQESS_STAT(tx_q1_byte)}, + {"tx_q2_byte", IPQESS_STAT(tx_q2_byte)}, + {"tx_q3_byte", IPQESS_STAT(tx_q3_byte)}, + {"tx_q4_byte", IPQESS_STAT(tx_q4_byte)}, + {"tx_q5_byte", IPQESS_STAT(tx_q5_byte)}, + {"tx_q6_byte", IPQESS_STAT(tx_q6_byte)}, + {"tx_q7_byte", IPQESS_STAT(tx_q7_byte)}, + {"tx_q8_byte", IPQESS_STAT(tx_q8_byte)}, + {"tx_q9_byte", IPQESS_STAT(tx_q9_byte)}, + {"tx_q10_byte", IPQESS_STAT(tx_q10_byte)}, + {"tx_q11_byte", IPQESS_STAT(tx_q11_byte)}, + {"tx_q12_byte", IPQESS_STAT(tx_q12_byte)}, + {"tx_q13_byte", IPQESS_STAT(tx_q13_byte)}, + {"tx_q14_byte", IPQESS_STAT(tx_q14_byte)}, + {"tx_q15_byte", IPQESS_STAT(tx_q15_byte)}, + {"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)}, + {"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)}, + {"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)}, + {"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)}, + {"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)}, + {"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)}, + {"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)}, + {"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)}, + {"rx_q0_byte", IPQESS_STAT(rx_q0_byte)}, + {"rx_q1_byte", IPQESS_STAT(rx_q1_byte)}, + {"rx_q2_byte", IPQESS_STAT(rx_q2_byte)}, + {"rx_q3_byte", IPQESS_STAT(rx_q3_byte)}, + {"rx_q4_byte", IPQESS_STAT(rx_q4_byte)}, + {"rx_q5_byte", IPQESS_STAT(rx_q5_byte)}, + {"rx_q6_byte", IPQESS_STAT(rx_q6_byte)}, + {"rx_q7_byte", IPQESS_STAT(rx_q7_byte)}, + {"tx_desc_error", IPQESS_STAT(tx_desc_error)}, +}; + +static int ipqess_get_strset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(ipqess_stats); + default: + netdev_dbg(netdev, "%s: Unsupported string set", __func__); + return -EOPNOTSUPP; + } +} + +static void ipqess_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) +{ + u8 *p = data; + u32 i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++) + ethtool_puts(&p, ipqess_stats[i].string); + break; + } +} + +static void ipqess_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + uint64_t *data) +{ + struct ipqess *ess = netdev_priv(netdev); + u32 *essstats = (u32 *)&ess->ipqess_stats; + int i; + + spin_lock(&ess->stats_lock); + + ipqess_update_hw_stats(ess); + + for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++) + data[i] = *(u32 *)(essstats + (ipqess_stats[i].offset / sizeof(u32))); + + spin_unlock(&ess->stats_lock); +} + +static void ipqess_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strscpy(info->driver, "qca_ipqess", DRVINFO_LEN); + strscpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN); +} + +static int ipqess_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct ipqess *ess = netdev_priv(netdev); + + return phylink_ethtool_ksettings_get(ess->phylink, cmd); +} + +static int ipqess_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct ipqess *ess = netdev_priv(netdev); + + return phylink_ethtool_ksettings_set(ess->phylink, cmd); +} + +static void ipqess_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ering, + struct netlink_ext_ack *extack) +{ + ring->tx_max_pending = IPQESS_TX_RING_SIZE; + ring->rx_max_pending = IPQESS_RX_RING_SIZE; +} + +static const struct ethtool_ops ipqesstool_ops = { + .get_drvinfo = &ipqess_get_drvinfo, + .get_link = ðtool_op_get_link, + .get_link_ksettings = &ipqess_get_link_ksettings, + .set_link_ksettings = &ipqess_set_link_ksettings, + .get_strings = &ipqess_get_strings, + .get_sset_count = &ipqess_get_strset_count, + .get_ethtool_stats = &ipqess_get_ethtool_stats, + .get_ringparam = ipqess_get_ringparam, +}; + +void ipqess_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &ipqesstool_ops; +} diff --git a/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch b/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch index d1bc5e9bde..caeff6c646 100644 --- a/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch +++ b/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch @@ -17,18 +17,10 @@ init time. Signed-off-by: Maxime Chevallier --- - MAINTAINERS | 7 + - drivers/net/ethernet/qualcomm/Kconfig | 11 + - drivers/net/ethernet/qualcomm/Makefile | 2 + - drivers/net/ethernet/qualcomm/ipqess/Makefile | 8 + - drivers/net/ethernet/qualcomm/ipqess/ipqess.c | 1246 +++++++++++++++++ - drivers/net/ethernet/qualcomm/ipqess/ipqess.h | 518 +++++++ - .../ethernet/qualcomm/ipqess/ipqess_ethtool.c | 164 +++ - 7 files changed, 1956 insertions(+) - create mode 100644 drivers/net/ethernet/qualcomm/ipqess/Makefile - create mode 100644 drivers/net/ethernet/qualcomm/ipqess/ipqess.c - create mode 100644 drivers/net/ethernet/qualcomm/ipqess/ipqess.h - create mode 100644 drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c + MAINTAINERS | 7 + + drivers/net/ethernet/qualcomm/Kconfig | 11 + + drivers/net/ethernet/qualcomm/Makefile | 2 + + 3 files changed, 20 insertions(+) --- a/MAINTAINERS +++ b/MAINTAINERS @@ -76,1953 +68,3 @@ Signed-off-by: Maxime Chevallier + obj-$(CONFIG_QCOM_PPE) += ppe/ obj-$(CONFIG_RMNET) += rmnet/ ---- /dev/null -+++ b/drivers/net/ethernet/qualcomm/ipqess/Makefile -@@ -0,0 +1,8 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+# -+# Makefile for the IPQ ESS driver -+# -+ -+obj-$(CONFIG_QCOM_IPQ4019_ESS_EDMA) += ipq_ess.o -+ -+ipq_ess-objs := ipqess.o ipqess_ethtool.o ---- /dev/null -+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -0,0 +1,1248 @@ -+// SPDX-License-Identifier: GPL-2.0 OR ISC -+/* Copyright (c) 2014 - 2017, The Linux Foundation. All rights reserved. -+ * Copyright (c) 2017 - 2018, John Crispin -+ * Copyright (c) 2018 - 2019, Christian Lamparter -+ * Copyright (c) 2020 - 2021, Gabor Juhos -+ * Copyright (c) 2021 - 2022, Maxime Chevallier -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipqess.h" -+ -+#define IPQESS_RRD_SIZE 16 -+#define IPQESS_NEXT_IDX(X, Y) (((X) + 1) & ((Y) - 1)) -+#define IPQESS_TX_DMA_BUF_LEN 0x3fff -+ -+static void ipqess_w32(struct ipqess *ess, u32 reg, u32 val) -+{ -+ writel(val, ess->hw_addr + reg); -+} -+ -+static u32 ipqess_r32(struct ipqess *ess, u16 reg) -+{ -+ return readl(ess->hw_addr + reg); -+} -+ -+static void ipqess_m32(struct ipqess *ess, u32 mask, u32 val, u16 reg) -+{ -+ u32 _val = ipqess_r32(ess, reg); -+ -+ _val &= ~mask; -+ _val |= val; -+ -+ ipqess_w32(ess, reg, _val); -+} -+ -+void ipqess_update_hw_stats(struct ipqess *ess) -+{ -+ u32 *p; -+ u32 stat; -+ int i; -+ -+ lockdep_assert_held(&ess->stats_lock); -+ -+ p = (u32 *)&ess->ipqess_stats; -+ for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) { -+ stat = ipqess_r32(ess, IPQESS_REG_TX_STAT_PKT_Q(i)); -+ *p += stat; -+ p++; -+ } -+ -+ for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) { -+ stat = ipqess_r32(ess, IPQESS_REG_TX_STAT_BYTE_Q(i)); -+ *p += stat; -+ p++; -+ } -+ -+ for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) { -+ stat = ipqess_r32(ess, IPQESS_REG_RX_STAT_PKT_Q(i)); -+ *p += stat; -+ p++; -+ } -+ -+ for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) { -+ stat = ipqess_r32(ess, IPQESS_REG_RX_STAT_BYTE_Q(i)); -+ *p += stat; -+ p++; -+ } -+} -+ -+static int ipqess_tx_ring_alloc(struct ipqess *ess) -+{ -+ struct device *dev = &ess->pdev->dev; -+ int i; -+ -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ struct ipqess_tx_ring *tx_ring = &ess->tx_ring[i]; -+ size_t size; -+ u32 idx; -+ -+ tx_ring->ess = ess; -+ tx_ring->ring_id = i; -+ tx_ring->idx = i * 4; -+ tx_ring->count = IPQESS_TX_RING_SIZE; -+ tx_ring->nq = netdev_get_tx_queue(ess->netdev, i); -+ -+ size = sizeof(struct ipqess_buf) * IPQESS_TX_RING_SIZE; -+ tx_ring->buf = devm_kzalloc(dev, size, GFP_KERNEL); -+ if (!tx_ring->buf) -+ return -ENOMEM; -+ -+ size = sizeof(struct ipqess_tx_desc) * IPQESS_TX_RING_SIZE; -+ tx_ring->hw_desc = dmam_alloc_coherent(dev, size, &tx_ring->dma, -+ GFP_KERNEL); -+ if (!tx_ring->hw_desc) -+ return -ENOMEM; -+ -+ ipqess_w32(ess, IPQESS_REG_TPD_BASE_ADDR_Q(tx_ring->idx), -+ (u32)tx_ring->dma); -+ -+ idx = ipqess_r32(ess, IPQESS_REG_TPD_IDX_Q(tx_ring->idx)); -+ idx >>= IPQESS_TPD_CONS_IDX_SHIFT; /* need u32 here */ -+ idx &= 0xffff; -+ tx_ring->head = idx; -+ tx_ring->tail = idx; -+ -+ ipqess_m32(ess, IPQESS_TPD_PROD_IDX_MASK << IPQESS_TPD_PROD_IDX_SHIFT, -+ idx, IPQESS_REG_TPD_IDX_Q(tx_ring->idx)); -+ ipqess_w32(ess, IPQESS_REG_TX_SW_CONS_IDX_Q(tx_ring->idx), idx); -+ ipqess_w32(ess, IPQESS_REG_TPD_RING_SIZE, IPQESS_TX_RING_SIZE); -+ } -+ -+ return 0; -+} -+ -+static int ipqess_tx_unmap_and_free(struct device *dev, struct ipqess_buf *buf) -+{ -+ int len = 0; -+ -+ if (buf->flags & IPQESS_DESC_SINGLE) -+ dma_unmap_single(dev, buf->dma, buf->length, DMA_TO_DEVICE); -+ else if (buf->flags & IPQESS_DESC_PAGE) -+ dma_unmap_page(dev, buf->dma, buf->length, DMA_TO_DEVICE); -+ -+ if (buf->flags & IPQESS_DESC_LAST) { -+ len = buf->skb->len; -+ dev_kfree_skb_any(buf->skb); -+ } -+ -+ buf->flags = 0; -+ -+ return len; -+} -+ -+static void ipqess_tx_ring_free(struct ipqess *ess) -+{ -+ int i; -+ -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ int j; -+ -+ if (ess->tx_ring[i].hw_desc) -+ continue; -+ -+ for (j = 0; j < IPQESS_TX_RING_SIZE; j++) { -+ struct ipqess_buf *buf = &ess->tx_ring[i].buf[j]; -+ -+ ipqess_tx_unmap_and_free(&ess->pdev->dev, buf); -+ } -+ -+ ess->tx_ring[i].buf = NULL; -+ } -+} -+ -+static int ipqess_rx_buf_prepare(struct ipqess_buf *buf, -+ struct ipqess_rx_ring *rx_ring) -+{ -+ memset(buf->skb->data, 0, sizeof(struct ipqess_rx_desc)); -+ -+ buf->dma = dma_map_single(rx_ring->ppdev, buf->skb->data, -+ IPQESS_RX_HEAD_BUFF_SIZE, DMA_FROM_DEVICE); -+ if (dma_mapping_error(rx_ring->ppdev, buf->dma)) { -+ dev_kfree_skb_any(buf->skb); -+ buf->skb = NULL; -+ return -EFAULT; -+ } -+ -+ buf->length = IPQESS_RX_HEAD_BUFF_SIZE; -+ rx_ring->hw_desc[rx_ring->head] = (struct ipqess_rx_desc *)buf->dma; -+ rx_ring->head = (rx_ring->head + 1) % IPQESS_RX_RING_SIZE; -+ -+ ipqess_m32(rx_ring->ess, IPQESS_RFD_PROD_IDX_BITS, -+ (rx_ring->head + IPQESS_RX_RING_SIZE - 1) % IPQESS_RX_RING_SIZE, -+ IPQESS_REG_RFD_IDX_Q(rx_ring->idx)); -+ -+ return 0; -+} -+ -+/* locking is handled by the caller */ -+static int ipqess_rx_buf_alloc_napi(struct ipqess_rx_ring *rx_ring) -+{ -+ struct ipqess_buf *buf = &rx_ring->buf[rx_ring->head]; -+ -+ buf->skb = napi_alloc_skb(&rx_ring->napi_rx, IPQESS_RX_HEAD_BUFF_SIZE); -+ if (!buf->skb) -+ return -ENOMEM; -+ -+ return ipqess_rx_buf_prepare(buf, rx_ring); -+} -+ -+static int ipqess_rx_buf_alloc(struct ipqess_rx_ring *rx_ring) -+{ -+ struct ipqess_buf *buf = &rx_ring->buf[rx_ring->head]; -+ -+ buf->skb = netdev_alloc_skb_ip_align(rx_ring->ess->netdev, -+ IPQESS_RX_HEAD_BUFF_SIZE); -+ -+ if (!buf->skb) -+ return -ENOMEM; -+ -+ return ipqess_rx_buf_prepare(buf, rx_ring); -+} -+ -+static void ipqess_refill_work(struct work_struct *work) -+{ -+ struct ipqess_rx_ring_refill *rx_refill = container_of(work, -+ struct ipqess_rx_ring_refill, refill_work); -+ struct ipqess_rx_ring *rx_ring = rx_refill->rx_ring; -+ int refill = 0; -+ -+ /* don't let this loop by accident. */ -+ while (atomic_dec_and_test(&rx_ring->refill_count)) { -+ napi_disable(&rx_ring->napi_rx); -+ if (ipqess_rx_buf_alloc(rx_ring)) { -+ refill++; -+ dev_dbg(rx_ring->ppdev, -+ "Not all buffers were reallocated"); -+ } -+ napi_enable(&rx_ring->napi_rx); -+ } -+ -+ if (atomic_add_return(refill, &rx_ring->refill_count)) -+ schedule_work(&rx_refill->refill_work); -+} -+ -+static int ipqess_rx_ring_alloc(struct ipqess *ess) -+{ -+ int i; -+ -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ int j; -+ -+ ess->rx_ring[i].ess = ess; -+ ess->rx_ring[i].ppdev = &ess->pdev->dev; -+ ess->rx_ring[i].ring_id = i; -+ ess->rx_ring[i].idx = i * 2; -+ -+ ess->rx_ring[i].buf = devm_kzalloc(&ess->pdev->dev, -+ sizeof(struct ipqess_buf) * IPQESS_RX_RING_SIZE, -+ GFP_KERNEL); -+ -+ if (!ess->rx_ring[i].buf) -+ return -ENOMEM; -+ -+ ess->rx_ring[i].hw_desc = -+ dmam_alloc_coherent(&ess->pdev->dev, -+ sizeof(struct ipqess_rx_desc) * IPQESS_RX_RING_SIZE, -+ &ess->rx_ring[i].dma, GFP_KERNEL); -+ -+ if (!ess->rx_ring[i].hw_desc) -+ return -ENOMEM; -+ -+ for (j = 0; j < IPQESS_RX_RING_SIZE; j++) -+ if (ipqess_rx_buf_alloc(&ess->rx_ring[i]) < 0) -+ return -ENOMEM; -+ -+ ess->rx_refill[i].rx_ring = &ess->rx_ring[i]; -+ INIT_WORK(&ess->rx_refill[i].refill_work, ipqess_refill_work); -+ -+ ipqess_w32(ess, IPQESS_REG_RFD_BASE_ADDR_Q(ess->rx_ring[i].idx), -+ (u32)(ess->rx_ring[i].dma)); -+ } -+ -+ ipqess_w32(ess, IPQESS_REG_RX_DESC0, -+ (IPQESS_RX_HEAD_BUFF_SIZE << IPQESS_RX_BUF_SIZE_SHIFT) | -+ (IPQESS_RX_RING_SIZE << IPQESS_RFD_RING_SIZE_SHIFT)); -+ -+ return 0; -+} -+ -+static void ipqess_rx_ring_free(struct ipqess *ess) -+{ -+ int i; -+ -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ int j; -+ -+ cancel_work_sync(&ess->rx_refill[i].refill_work); -+ atomic_set(&ess->rx_ring[i].refill_count, 0); -+ -+ for (j = 0; j < IPQESS_RX_RING_SIZE; j++) { -+ dma_unmap_single(&ess->pdev->dev, -+ ess->rx_ring[i].buf[j].dma, -+ ess->rx_ring[i].buf[j].length, -+ DMA_FROM_DEVICE); -+ dev_kfree_skb_any(ess->rx_ring[i].buf[j].skb); -+ } -+ } -+} -+ -+static struct net_device_stats *ipqess_get_stats(struct net_device *netdev) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ -+ spin_lock(&ess->stats_lock); -+ ipqess_update_hw_stats(ess); -+ spin_unlock(&ess->stats_lock); -+ -+ return &ess->stats; -+} -+ -+static int ipqess_rx_poll(struct ipqess_rx_ring *rx_ring, int budget) -+{ -+ u32 length = 0, num_desc, tail, rx_ring_tail; -+ int done = 0; -+ -+ rx_ring_tail = rx_ring->tail; -+ -+ tail = ipqess_r32(rx_ring->ess, IPQESS_REG_RFD_IDX_Q(rx_ring->idx)); -+ tail >>= IPQESS_RFD_CONS_IDX_SHIFT; -+ tail &= IPQESS_RFD_CONS_IDX_MASK; -+ -+ while (done < budget) { -+ struct ipqess_rx_desc *rd; -+ struct sk_buff *skb; -+ -+ if (rx_ring_tail == tail) -+ break; -+ -+ dma_unmap_single(rx_ring->ppdev, -+ rx_ring->buf[rx_ring_tail].dma, -+ rx_ring->buf[rx_ring_tail].length, -+ DMA_FROM_DEVICE); -+ -+ skb = xchg(&rx_ring->buf[rx_ring_tail].skb, NULL); -+ rd = (struct ipqess_rx_desc *)skb->data; -+ rx_ring_tail = IPQESS_NEXT_IDX(rx_ring_tail, IPQESS_RX_RING_SIZE); -+ -+ /* Check if RRD is valid */ -+ if (!(rd->rrd7 & cpu_to_le16(IPQESS_RRD_DESC_VALID))) { -+ num_desc = 1; -+ dev_kfree_skb_any(skb); -+ goto skip; -+ } -+ -+ num_desc = le16_to_cpu(rd->rrd1) & IPQESS_RRD_NUM_RFD_MASK; -+ length = le16_to_cpu(rd->rrd6) & IPQESS_RRD_PKT_SIZE_MASK; -+ -+ skb_reserve(skb, IPQESS_RRD_SIZE); -+ if (num_desc > 1) { -+ struct sk_buff *skb_prev = NULL; -+ int size_remaining; -+ int i; -+ -+ skb->data_len = 0; -+ skb->tail += (IPQESS_RX_HEAD_BUFF_SIZE - IPQESS_RRD_SIZE); -+ skb->len = length; -+ skb->truesize = length; -+ size_remaining = length - (IPQESS_RX_HEAD_BUFF_SIZE - IPQESS_RRD_SIZE); -+ -+ for (i = 1; i < num_desc; i++) { -+ struct sk_buff *skb_temp = rx_ring->buf[rx_ring_tail].skb; -+ -+ dma_unmap_single(rx_ring->ppdev, -+ rx_ring->buf[rx_ring_tail].dma, -+ rx_ring->buf[rx_ring_tail].length, -+ DMA_FROM_DEVICE); -+ -+ skb_put(skb_temp, min(size_remaining, IPQESS_RX_HEAD_BUFF_SIZE)); -+ if (skb_prev) -+ skb_prev->next = rx_ring->buf[rx_ring_tail].skb; -+ else -+ skb_shinfo(skb)->frag_list = rx_ring->buf[rx_ring_tail].skb; -+ skb_prev = rx_ring->buf[rx_ring_tail].skb; -+ rx_ring->buf[rx_ring_tail].skb->next = NULL; -+ -+ skb->data_len += rx_ring->buf[rx_ring_tail].skb->len; -+ size_remaining -= rx_ring->buf[rx_ring_tail].skb->len; -+ -+ rx_ring_tail = IPQESS_NEXT_IDX(rx_ring_tail, IPQESS_RX_RING_SIZE); -+ } -+ -+ } else { -+ skb_put(skb, length); -+ } -+ -+ skb->dev = rx_ring->ess->netdev; -+ skb->protocol = eth_type_trans(skb, rx_ring->ess->netdev); -+ skb_record_rx_queue(skb, rx_ring->ring_id); -+ -+ if (rd->rrd6 & cpu_to_le16(IPQESS_RRD_CSUM_FAIL_MASK)) -+ skb_checksum_none_assert(skb); -+ else -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ -+ if (rd->rrd7 & cpu_to_le16(IPQESS_RRD_CVLAN)) -+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), -+ le16_to_cpu(rd->rrd4)); -+ else if (rd->rrd1 & cpu_to_le16(IPQESS_RRD_SVLAN)) -+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), -+ le16_to_cpu(rd->rrd4)); -+ -+ napi_gro_receive(&rx_ring->napi_rx, skb); -+ -+ rx_ring->ess->stats.rx_packets++; -+ rx_ring->ess->stats.rx_bytes += length; -+ -+ done++; -+skip: -+ -+ num_desc += atomic_xchg(&rx_ring->refill_count, 0); -+ while (num_desc) { -+ if (ipqess_rx_buf_alloc_napi(rx_ring)) { -+ num_desc = atomic_add_return(num_desc, -+ &rx_ring->refill_count); -+ if (num_desc >= DIV_ROUND_UP(IPQESS_RX_RING_SIZE * 4, 7)) -+ schedule_work(&rx_ring->ess->rx_refill[rx_ring->ring_id].refill_work); -+ break; -+ } -+ num_desc--; -+ } -+ } -+ -+ ipqess_w32(rx_ring->ess, IPQESS_REG_RX_SW_CONS_IDX_Q(rx_ring->idx), -+ rx_ring_tail); -+ rx_ring->tail = rx_ring_tail; -+ -+ return done; -+} -+ -+static int ipqess_tx_complete(struct ipqess_tx_ring *tx_ring, int budget) -+{ -+ int total = 0, ret; -+ int done = 0; -+ u32 tail; -+ -+ tail = ipqess_r32(tx_ring->ess, IPQESS_REG_TPD_IDX_Q(tx_ring->idx)); -+ tail >>= IPQESS_TPD_CONS_IDX_SHIFT; -+ tail &= IPQESS_TPD_CONS_IDX_MASK; -+ -+ do { -+ ret = ipqess_tx_unmap_and_free(&tx_ring->ess->pdev->dev, -+ &tx_ring->buf[tx_ring->tail]); -+ tx_ring->tail = IPQESS_NEXT_IDX(tx_ring->tail, tx_ring->count); -+ -+ total += ret; -+ } while ((++done < budget) && (tx_ring->tail != tail)); -+ -+ ipqess_w32(tx_ring->ess, IPQESS_REG_TX_SW_CONS_IDX_Q(tx_ring->idx), -+ tx_ring->tail); -+ -+ if (netif_tx_queue_stopped(tx_ring->nq)) { -+ netdev_dbg(tx_ring->ess->netdev, "waking up tx queue %d\n", -+ tx_ring->idx); -+ netif_tx_wake_queue(tx_ring->nq); -+ } -+ -+ netdev_tx_completed_queue(tx_ring->nq, done, total); -+ -+ return done; -+} -+ -+static int ipqess_tx_napi(struct napi_struct *napi, int budget) -+{ -+ struct ipqess_tx_ring *tx_ring = container_of(napi, struct ipqess_tx_ring, -+ napi_tx); -+ int work_done = 0; -+ u32 tx_status; -+ -+ tx_status = ipqess_r32(tx_ring->ess, IPQESS_REG_TX_ISR); -+ tx_status &= BIT(tx_ring->idx); -+ -+ work_done = ipqess_tx_complete(tx_ring, budget); -+ -+ ipqess_w32(tx_ring->ess, IPQESS_REG_TX_ISR, tx_status); -+ -+ if (likely(work_done < budget)) { -+ if (napi_complete_done(napi, work_done)) -+ ipqess_w32(tx_ring->ess, -+ IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), 0x1); -+ } -+ -+ return work_done; -+} -+ -+static int ipqess_rx_napi(struct napi_struct *napi, int budget) -+{ -+ struct ipqess_rx_ring *rx_ring = container_of(napi, struct ipqess_rx_ring, -+ napi_rx); -+ struct ipqess *ess = rx_ring->ess; -+ u32 rx_mask = BIT(rx_ring->idx); -+ int remaining_budget = budget; -+ int rx_done; -+ u32 status; -+ -+ do { -+ ipqess_w32(ess, IPQESS_REG_RX_ISR, rx_mask); -+ rx_done = ipqess_rx_poll(rx_ring, remaining_budget); -+ remaining_budget -= rx_done; -+ -+ status = ipqess_r32(ess, IPQESS_REG_RX_ISR); -+ } while (remaining_budget > 0 && (status & rx_mask)); -+ -+ if (remaining_budget <= 0) -+ return budget; -+ -+ if (napi_complete_done(napi, budget - remaining_budget)) -+ ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(rx_ring->idx), 0x1); -+ -+ return budget - remaining_budget; -+} -+ -+static irqreturn_t ipqess_interrupt_tx(int irq, void *priv) -+{ -+ struct ipqess_tx_ring *tx_ring = (struct ipqess_tx_ring *)priv; -+ -+ if (likely(napi_schedule_prep(&tx_ring->napi_tx))) { -+ __napi_schedule(&tx_ring->napi_tx); -+ ipqess_w32(tx_ring->ess, IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), -+ 0x0); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static irqreturn_t ipqess_interrupt_rx(int irq, void *priv) -+{ -+ struct ipqess_rx_ring *rx_ring = (struct ipqess_rx_ring *)priv; -+ -+ if (likely(napi_schedule_prep(&rx_ring->napi_rx))) { -+ __napi_schedule(&rx_ring->napi_rx); -+ ipqess_w32(rx_ring->ess, IPQESS_REG_RX_INT_MASK_Q(rx_ring->idx), -+ 0x0); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static void ipqess_irq_enable(struct ipqess *ess) -+{ -+ int i; -+ -+ ipqess_w32(ess, IPQESS_REG_RX_ISR, 0xff); -+ ipqess_w32(ess, IPQESS_REG_TX_ISR, 0xffff); -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(ess->rx_ring[i].idx), 1); -+ ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(ess->tx_ring[i].idx), 1); -+ } -+} -+ -+static void ipqess_irq_disable(struct ipqess *ess) -+{ -+ int i; -+ -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(ess->rx_ring[i].idx), 0); -+ ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(ess->tx_ring[i].idx), 0); -+ } -+} -+ -+static int ipqess_init(struct net_device *netdev) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ struct device_node *of_node = ess->pdev->dev.of_node; -+ -+ return phylink_of_phy_connect(ess->phylink, of_node, 0); -+} -+ -+static void ipqess_uninit(struct net_device *netdev) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ -+ phylink_disconnect_phy(ess->phylink); -+} -+ -+static int ipqess_open(struct net_device *netdev) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ int i, err; -+ -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ int qid; -+ -+ qid = ess->tx_ring[i].idx; -+ err = devm_request_irq(&netdev->dev, ess->tx_irq[qid], -+ ipqess_interrupt_tx, 0, -+ ess->tx_irq_names[qid], -+ &ess->tx_ring[i]); -+ if (err) -+ return err; -+ -+ qid = ess->rx_ring[i].idx; -+ err = devm_request_irq(&netdev->dev, ess->rx_irq[qid], -+ ipqess_interrupt_rx, 0, -+ ess->rx_irq_names[qid], -+ &ess->rx_ring[i]); -+ if (err) -+ return err; -+ -+ napi_enable(&ess->tx_ring[i].napi_tx); -+ napi_enable(&ess->rx_ring[i].napi_rx); -+ } -+ -+ ipqess_irq_enable(ess); -+ phylink_start(ess->phylink); -+ netif_tx_start_all_queues(netdev); -+ -+ return 0; -+} -+ -+static int ipqess_stop(struct net_device *netdev) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ int i; -+ -+ netif_tx_stop_all_queues(netdev); -+ phylink_stop(ess->phylink); -+ ipqess_irq_disable(ess); -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ napi_disable(&ess->tx_ring[i].napi_tx); -+ napi_disable(&ess->rx_ring[i].napi_rx); -+ } -+ -+ return 0; -+} -+ -+static int ipqess_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ -+ return phylink_mii_ioctl(ess->phylink, ifr, cmd); -+} -+ -+static u16 ipqess_tx_desc_available(struct ipqess_tx_ring *tx_ring) -+{ -+ u16 count = 0; -+ -+ if (tx_ring->tail <= tx_ring->head) -+ count = IPQESS_TX_RING_SIZE; -+ -+ count += tx_ring->tail - tx_ring->head - 1; -+ -+ return count; -+} -+ -+static int ipqess_cal_txd_req(struct sk_buff *skb) -+{ -+ int tpds; -+ -+ /* one TPD for the header, and one for each fragments */ -+ tpds = 1 + skb_shinfo(skb)->nr_frags; -+ if (skb_is_gso(skb) && skb_is_gso_v6(skb)) { -+ /* for LSOv2 one extra TPD is needed */ -+ tpds++; -+ } -+ -+ return tpds; -+} -+ -+static struct ipqess_buf *ipqess_get_tx_buffer(struct ipqess_tx_ring *tx_ring, -+ struct ipqess_tx_desc *desc) -+{ -+ return &tx_ring->buf[desc - tx_ring->hw_desc]; -+} -+ -+static struct ipqess_tx_desc *ipqess_tx_desc_next(struct ipqess_tx_ring *tx_ring) -+{ -+ struct ipqess_tx_desc *desc; -+ -+ desc = &tx_ring->hw_desc[tx_ring->head]; -+ tx_ring->head = IPQESS_NEXT_IDX(tx_ring->head, tx_ring->count); -+ -+ return desc; -+} -+ -+static void ipqess_rollback_tx(struct ipqess *eth, -+ struct ipqess_tx_desc *first_desc, int ring_id) -+{ -+ struct ipqess_tx_ring *tx_ring = ð->tx_ring[ring_id]; -+ struct ipqess_tx_desc *desc = NULL; -+ struct ipqess_buf *buf; -+ u16 start_index, index; -+ -+ start_index = first_desc - tx_ring->hw_desc; -+ -+ index = start_index; -+ while (index != tx_ring->head) { -+ desc = &tx_ring->hw_desc[index]; -+ buf = &tx_ring->buf[index]; -+ ipqess_tx_unmap_and_free(ð->pdev->dev, buf); -+ memset(desc, 0, sizeof(*desc)); -+ if (++index == tx_ring->count) -+ index = 0; -+ } -+ tx_ring->head = start_index; -+} -+ -+static int ipqess_tx_map_and_fill(struct ipqess_tx_ring *tx_ring, -+ struct sk_buff *skb) -+{ -+ struct ipqess_tx_desc *desc = NULL, *first_desc = NULL; -+ u32 word1 = 0, word3 = 0, lso_word1 = 0, svlan_tag = 0; -+ struct platform_device *pdev = tx_ring->ess->pdev; -+ struct ipqess_buf *buf = NULL; -+ u16 len; -+ int i; -+ -+ if (skb_is_gso(skb)) { -+ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { -+ lso_word1 |= IPQESS_TPD_IPV4_EN; -+ ip_hdr(skb)->check = 0; -+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, -+ ip_hdr(skb)->daddr, -+ 0, IPPROTO_TCP, 0); -+ } else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { -+ lso_word1 |= IPQESS_TPD_LSO_V2_EN; -+ ipv6_hdr(skb)->payload_len = 0; -+ tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, -+ &ipv6_hdr(skb)->daddr, -+ 0, IPPROTO_TCP, 0); -+ } -+ -+ lso_word1 |= IPQESS_TPD_LSO_EN | -+ ((skb_shinfo(skb)->gso_size & IPQESS_TPD_MSS_MASK) << -+ IPQESS_TPD_MSS_SHIFT) | -+ (skb_transport_offset(skb) << IPQESS_TPD_HDR_SHIFT); -+ } else if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { -+ u8 css, cso; -+ -+ cso = skb_checksum_start_offset(skb); -+ css = cso + skb->csum_offset; -+ -+ word1 |= (IPQESS_TPD_CUSTOM_CSUM_EN); -+ word1 |= (cso >> 1) << IPQESS_TPD_HDR_SHIFT; -+ word1 |= ((css >> 1) << IPQESS_TPD_CUSTOM_CSUM_SHIFT); -+ } -+ -+ if (skb_vlan_tag_present(skb)) { -+ switch (skb->vlan_proto) { -+ case htons(ETH_P_8021Q): -+ word3 |= BIT(IPQESS_TX_INS_CVLAN); -+ word3 |= skb_vlan_tag_get(skb) << IPQESS_TX_CVLAN_TAG_SHIFT; -+ break; -+ case htons(ETH_P_8021AD): -+ word1 |= BIT(IPQESS_TX_INS_SVLAN); -+ svlan_tag = skb_vlan_tag_get(skb); -+ break; -+ default: -+ dev_err(&pdev->dev, "no ctag or stag present\n"); -+ goto vlan_tag_error; -+ } -+ } -+ -+ if (eth_type_vlan(skb->protocol)) -+ word1 |= IPQESS_TPD_VLAN_TAGGED; -+ -+ if (skb->protocol == htons(ETH_P_PPP_SES)) -+ word1 |= IPQESS_TPD_PPPOE_EN; -+ -+ len = skb_headlen(skb); -+ -+ first_desc = ipqess_tx_desc_next(tx_ring); -+ desc = first_desc; -+ if (lso_word1 & IPQESS_TPD_LSO_V2_EN) { -+ desc->addr = cpu_to_le32(skb->len); -+ desc->word1 = cpu_to_le32(word1 | lso_word1); -+ desc->svlan_tag = cpu_to_le16(svlan_tag); -+ desc->word3 = cpu_to_le32(word3); -+ desc = ipqess_tx_desc_next(tx_ring); -+ } -+ -+ buf = ipqess_get_tx_buffer(tx_ring, desc); -+ buf->length = len; -+ buf->dma = dma_map_single(&pdev->dev, skb->data, len, DMA_TO_DEVICE); -+ -+ if (dma_mapping_error(&pdev->dev, buf->dma)) -+ goto dma_error; -+ -+ desc->addr = cpu_to_le32(buf->dma); -+ desc->len = cpu_to_le16(len); -+ -+ buf->flags |= IPQESS_DESC_SINGLE; -+ desc->word1 = cpu_to_le32(word1 | lso_word1); -+ desc->svlan_tag = cpu_to_le16(svlan_tag); -+ desc->word3 = cpu_to_le32(word3); -+ -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ -+ len = skb_frag_size(frag); -+ desc = ipqess_tx_desc_next(tx_ring); -+ buf = ipqess_get_tx_buffer(tx_ring, desc); -+ buf->length = len; -+ buf->flags |= IPQESS_DESC_PAGE; -+ buf->dma = skb_frag_dma_map(&pdev->dev, frag, 0, len, -+ DMA_TO_DEVICE); -+ -+ if (dma_mapping_error(&pdev->dev, buf->dma)) -+ goto dma_error; -+ -+ desc->addr = cpu_to_le32(buf->dma); -+ desc->len = cpu_to_le16(len); -+ desc->svlan_tag = cpu_to_le16(svlan_tag); -+ desc->word1 = cpu_to_le32(word1 | lso_word1); -+ desc->word3 = cpu_to_le32(word3); -+ } -+ desc->word1 |= cpu_to_le32(1 << IPQESS_TPD_EOP_SHIFT); -+ buf->skb = skb; -+ buf->flags |= IPQESS_DESC_LAST; -+ -+ return 0; -+ -+dma_error: -+ ipqess_rollback_tx(tx_ring->ess, first_desc, tx_ring->ring_id); -+ dev_err(&pdev->dev, "TX DMA map failed\n"); -+ -+vlan_tag_error: -+ return -ENOMEM; -+} -+ -+static void ipqess_kick_tx(struct ipqess_tx_ring *tx_ring) -+{ -+ /* Ensure that all TPDs has been written completely */ -+ dma_wmb(); -+ -+ /* update software producer index */ -+ ipqess_w32(tx_ring->ess, IPQESS_REG_TPD_IDX_Q(tx_ring->idx), -+ tx_ring->head); -+} -+ -+static netdev_tx_t ipqess_xmit(struct sk_buff *skb, struct net_device *netdev) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ struct ipqess_tx_ring *tx_ring; -+ int avail; -+ int tx_num; -+ int ret; -+ -+ tx_ring = &ess->tx_ring[skb_get_queue_mapping(skb)]; -+ tx_num = ipqess_cal_txd_req(skb); -+ avail = ipqess_tx_desc_available(tx_ring); -+ if (avail < tx_num) { -+ netdev_dbg(netdev, -+ "stopping tx queue %d, avail=%d req=%d im=%x\n", -+ tx_ring->idx, avail, tx_num, -+ ipqess_r32(tx_ring->ess, -+ IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx))); -+ netif_tx_stop_queue(tx_ring->nq); -+ ipqess_w32(tx_ring->ess, IPQESS_REG_TX_INT_MASK_Q(tx_ring->idx), 0x1); -+ ipqess_kick_tx(tx_ring); -+ return NETDEV_TX_BUSY; -+ } -+ -+ ret = ipqess_tx_map_and_fill(tx_ring, skb); -+ if (ret) { -+ dev_kfree_skb_any(skb); -+ ess->stats.tx_errors++; -+ goto err_out; -+ } -+ -+ ess->stats.tx_packets++; -+ ess->stats.tx_bytes += skb->len; -+ netdev_tx_sent_queue(tx_ring->nq, skb->len); -+ -+ if (!netdev_xmit_more() || netif_xmit_stopped(tx_ring->nq)) -+ ipqess_kick_tx(tx_ring); -+ -+err_out: -+ return NETDEV_TX_OK; -+} -+ -+static int ipqess_set_mac_address(struct net_device *netdev, void *p) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ const char *macaddr = netdev->dev_addr; -+ int ret = eth_mac_addr(netdev, p); -+ -+ if (ret) -+ return ret; -+ -+ ipqess_w32(ess, IPQESS_REG_MAC_CTRL1, (macaddr[0] << 8) | macaddr[1]); -+ ipqess_w32(ess, IPQESS_REG_MAC_CTRL0, -+ (macaddr[2] << 24) | (macaddr[3] << 16) | (macaddr[4] << 8) | -+ macaddr[5]); -+ -+ return 0; -+} -+ -+static void ipqess_tx_timeout(struct net_device *netdev, unsigned int txq_id) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ struct ipqess_tx_ring *tr = &ess->tx_ring[txq_id]; -+ -+ netdev_warn(netdev, "TX timeout on queue %d\n", tr->idx); -+} -+ -+static const struct net_device_ops ipqess_axi_netdev_ops = { -+ .ndo_init = ipqess_init, -+ .ndo_uninit = ipqess_uninit, -+ .ndo_open = ipqess_open, -+ .ndo_stop = ipqess_stop, -+ .ndo_eth_ioctl = ipqess_do_ioctl, -+ .ndo_start_xmit = ipqess_xmit, -+ .ndo_get_stats = ipqess_get_stats, -+ .ndo_set_mac_address = ipqess_set_mac_address, -+ .ndo_tx_timeout = ipqess_tx_timeout, -+}; -+ -+static void ipqess_hw_stop(struct ipqess *ess) -+{ -+ int i; -+ -+ /* disable all RX queue IRQs */ -+ for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) -+ ipqess_w32(ess, IPQESS_REG_RX_INT_MASK_Q(i), 0); -+ -+ /* disable all TX queue IRQs */ -+ for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) -+ ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(i), 0); -+ -+ /* disable all other IRQs */ -+ ipqess_w32(ess, IPQESS_REG_MISC_IMR, 0); -+ ipqess_w32(ess, IPQESS_REG_WOL_IMR, 0); -+ -+ /* clear the IRQ status registers */ -+ ipqess_w32(ess, IPQESS_REG_RX_ISR, 0xff); -+ ipqess_w32(ess, IPQESS_REG_TX_ISR, 0xffff); -+ ipqess_w32(ess, IPQESS_REG_MISC_ISR, 0x1fff); -+ ipqess_w32(ess, IPQESS_REG_WOL_ISR, 0x1); -+ ipqess_w32(ess, IPQESS_REG_WOL_CTRL, 0); -+ -+ /* disable RX and TX queues */ -+ ipqess_m32(ess, IPQESS_RXQ_CTRL_EN_MASK, 0, IPQESS_REG_RXQ_CTRL); -+ ipqess_m32(ess, IPQESS_TXQ_CTRL_TXQ_EN, 0, IPQESS_REG_TXQ_CTRL); -+} -+ -+static int ipqess_hw_init(struct ipqess *ess) -+{ -+ int i, err; -+ u32 tmp; -+ -+ ipqess_hw_stop(ess); -+ -+ ipqess_m32(ess, BIT(IPQESS_INTR_SW_IDX_W_TYP_SHIFT), -+ IPQESS_INTR_SW_IDX_W_TYPE << IPQESS_INTR_SW_IDX_W_TYP_SHIFT, -+ IPQESS_REG_INTR_CTRL); -+ -+ /* enable IRQ delay slot */ -+ ipqess_w32(ess, IPQESS_REG_IRQ_MODRT_TIMER_INIT, -+ (IPQESS_TX_IMT << IPQESS_IRQ_MODRT_TX_TIMER_SHIFT) | -+ (IPQESS_RX_IMT << IPQESS_IRQ_MODRT_RX_TIMER_SHIFT)); -+ -+ /* Set Customer and Service VLAN TPIDs */ -+ ipqess_w32(ess, IPQESS_REG_VLAN_CFG, -+ (ETH_P_8021Q << IPQESS_VLAN_CFG_CVLAN_TPID_SHIFT) | -+ (ETH_P_8021AD << IPQESS_VLAN_CFG_SVLAN_TPID_SHIFT)); -+ -+ /* Configure the TX Queue bursting */ -+ ipqess_w32(ess, IPQESS_REG_TXQ_CTRL, -+ (IPQESS_TPD_BURST << IPQESS_TXQ_NUM_TPD_BURST_SHIFT) | -+ (IPQESS_TXF_BURST << IPQESS_TXQ_TXF_BURST_NUM_SHIFT) | -+ IPQESS_TXQ_CTRL_TPD_BURST_EN); -+ -+ /* Set RSS type */ -+ ipqess_w32(ess, IPQESS_REG_RSS_TYPE, -+ IPQESS_RSS_TYPE_IPV4TCP | IPQESS_RSS_TYPE_IPV6_TCP | -+ IPQESS_RSS_TYPE_IPV4_UDP | IPQESS_RSS_TYPE_IPV6UDP | -+ IPQESS_RSS_TYPE_IPV4 | IPQESS_RSS_TYPE_IPV6); -+ -+ /* Set RFD ring burst and threshold */ -+ ipqess_w32(ess, IPQESS_REG_RX_DESC1, -+ (IPQESS_RFD_BURST << IPQESS_RXQ_RFD_BURST_NUM_SHIFT) | -+ (IPQESS_RFD_THR << IPQESS_RXQ_RFD_PF_THRESH_SHIFT) | -+ (IPQESS_RFD_LTHR << IPQESS_RXQ_RFD_LOW_THRESH_SHIFT)); -+ -+ /* Set Rx FIFO -+ * - threshold to start to DMA data to host -+ */ -+ ipqess_w32(ess, IPQESS_REG_RXQ_CTRL, -+ IPQESS_FIFO_THRESH_128_BYTE | IPQESS_RXQ_CTRL_RMV_VLAN); -+ -+ err = ipqess_rx_ring_alloc(ess); -+ if (err) -+ return err; -+ -+ err = ipqess_tx_ring_alloc(ess); -+ if (err) -+ goto err_rx_ring_free; -+ -+ /* Load all of ring base addresses above into the dma engine */ -+ ipqess_m32(ess, 0, BIT(IPQESS_LOAD_PTR_SHIFT), IPQESS_REG_TX_SRAM_PART); -+ -+ /* Disable TX FIFO low watermark and high watermark */ -+ ipqess_w32(ess, IPQESS_REG_TXF_WATER_MARK, 0); -+ -+ /* Configure RSS indirection table. -+ * 128 hash will be configured in the following -+ * pattern: hash{0,1,2,3} = {Q0,Q2,Q4,Q6} respectively -+ * and so on -+ */ -+ for (i = 0; i < IPQESS_NUM_IDT; i++) -+ ipqess_w32(ess, IPQESS_REG_RSS_IDT(i), IPQESS_RSS_IDT_VALUE); -+ -+ /* Configure load balance mapping table. -+ * 4 table entry will be configured according to the -+ * following pattern: load_balance{0,1,2,3} = {Q0,Q1,Q3,Q4} -+ * respectively. -+ */ -+ ipqess_w32(ess, IPQESS_REG_LB_RING, IPQESS_LB_REG_VALUE); -+ -+ /* Configure Virtual queue for Tx rings */ -+ ipqess_w32(ess, IPQESS_REG_VQ_CTRL0, IPQESS_VQ_REG_VALUE); -+ ipqess_w32(ess, IPQESS_REG_VQ_CTRL1, IPQESS_VQ_REG_VALUE); -+ -+ /* Configure Max AXI Burst write size to 128 bytes*/ -+ ipqess_w32(ess, IPQESS_REG_AXIW_CTRL_MAXWRSIZE, -+ IPQESS_AXIW_MAXWRSIZE_VALUE); -+ -+ /* Enable TX queues */ -+ ipqess_m32(ess, 0, IPQESS_TXQ_CTRL_TXQ_EN, IPQESS_REG_TXQ_CTRL); -+ -+ /* Enable RX queues */ -+ tmp = 0; -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) -+ tmp |= IPQESS_RXQ_CTRL_EN(ess->rx_ring[i].idx); -+ -+ ipqess_m32(ess, IPQESS_RXQ_CTRL_EN_MASK, tmp, IPQESS_REG_RXQ_CTRL); -+ -+ return 0; -+ -+err_rx_ring_free: -+ -+ ipqess_rx_ring_free(ess); -+ return err; -+} -+ -+static void ipqess_mac_config(struct phylink_config *config, unsigned int mode, -+ const struct phylink_link_state *state) -+{ -+ /* Nothing to do, use fixed Internal mode */ -+} -+ -+static void ipqess_mac_link_down(struct phylink_config *config, -+ unsigned int mode, -+ phy_interface_t interface) -+{ -+ /* Nothing to do, use fixed Internal mode */ -+} -+ -+static void ipqess_mac_link_up(struct phylink_config *config, -+ struct phy_device *phy, unsigned int mode, -+ phy_interface_t interface, -+ int speed, int duplex, -+ bool tx_pause, bool rx_pause) -+{ -+ /* Nothing to do, use fixed Internal mode */ -+} -+ -+static struct phylink_mac_ops ipqess_phylink_mac_ops = { -+ .mac_config = ipqess_mac_config, -+ .mac_link_up = ipqess_mac_link_up, -+ .mac_link_down = ipqess_mac_link_down, -+}; -+ -+static void ipqess_reset(struct ipqess *ess) -+{ -+ reset_control_assert(ess->ess_rst); -+ -+ mdelay(10); -+ -+ reset_control_deassert(ess->ess_rst); -+ -+ /* Waiting for all inner tables to be flushed and reinitialized. -+ * This takes between 5 and 10 ms -+ */ -+ -+ mdelay(10); -+} -+ -+static int ipqess_axi_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct net_device *netdev; -+ phy_interface_t phy_mode; -+ struct ipqess *ess; -+ int i, err = 0; -+ -+ netdev = devm_alloc_etherdev_mqs(&pdev->dev, sizeof(*ess), -+ IPQESS_NETDEV_QUEUES, -+ IPQESS_NETDEV_QUEUES); -+ if (!netdev) -+ return -ENOMEM; -+ -+ ess = netdev_priv(netdev); -+ ess->netdev = netdev; -+ ess->pdev = pdev; -+ spin_lock_init(&ess->stats_lock); -+ SET_NETDEV_DEV(netdev, &pdev->dev); -+ platform_set_drvdata(pdev, netdev); -+ -+ err = of_get_ethdev_address(np, netdev); -+ if (err) { -+ dev_dbg(&pdev->dev, "failed to get MAC address from DT: %d\n", err); -+ if (err == -EPROBE_DEFER) -+ return err; -+ eth_hw_addr_random(netdev); -+ dev_info(&pdev->dev, "using random MAC address %pM\n", -+ netdev->dev_addr); -+ } -+ -+ ess->hw_addr = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); -+ if (IS_ERR(ess->hw_addr)) -+ return PTR_ERR(ess->hw_addr); -+ -+ err = of_get_phy_mode(np, &phy_mode); -+ if (err) { -+ dev_err(&pdev->dev, "incorrect phy-mode\n"); -+ return err; -+ } -+ -+ ess->ess_clk = devm_clk_get(&pdev->dev, NULL); -+ if (!IS_ERR(ess->ess_clk)) -+ clk_prepare_enable(ess->ess_clk); -+ -+ ess->ess_rst = devm_reset_control_get(&pdev->dev, NULL); -+ if (IS_ERR(ess->ess_rst)) -+ goto err_clk; -+ -+ ipqess_reset(ess); -+ -+ ess->phylink_config.dev = &netdev->dev; -+ ess->phylink_config.type = PHYLINK_NETDEV; -+ ess->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | -+ MAC_100 | MAC_1000FD; -+ -+ __set_bit(PHY_INTERFACE_MODE_INTERNAL, -+ ess->phylink_config.supported_interfaces); -+ -+ ess->phylink = phylink_create(&ess->phylink_config, -+ of_fwnode_handle(np), phy_mode, -+ &ipqess_phylink_mac_ops); -+ if (IS_ERR(ess->phylink)) { -+ err = PTR_ERR(ess->phylink); -+ goto err_clk; -+ } -+ -+ for (i = 0; i < IPQESS_MAX_TX_QUEUE; i++) { -+ ess->tx_irq[i] = platform_get_irq(pdev, i); -+ scnprintf(ess->tx_irq_names[i], sizeof(ess->tx_irq_names[i]), -+ "%s:txq%d", pdev->name, i); -+ } -+ -+ for (i = 0; i < IPQESS_MAX_RX_QUEUE; i++) { -+ ess->rx_irq[i] = platform_get_irq(pdev, i + IPQESS_MAX_TX_QUEUE); -+ scnprintf(ess->rx_irq_names[i], sizeof(ess->rx_irq_names[i]), -+ "%s:rxq%d", pdev->name, i); -+ } -+ -+ netdev->netdev_ops = &ipqess_axi_netdev_ops; -+ netdev->features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM | -+ NETIF_F_HW_VLAN_CTAG_RX | -+ NETIF_F_HW_VLAN_CTAG_TX | -+ NETIF_F_TSO | NETIF_F_GRO | NETIF_F_SG; -+ /* feature change is not supported yet */ -+ netdev->hw_features = 0; -+ netdev->vlan_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_RXCSUM | -+ NETIF_F_TSO | -+ NETIF_F_GRO; -+ netdev->watchdog_timeo = 5 * HZ; -+ netdev->base_addr = (u32)ess->hw_addr; -+ netdev->max_mtu = 9000; -+ netdev->gso_max_segs = IPQESS_TX_RING_SIZE / 2; -+ -+ ipqess_set_ethtool_ops(netdev); -+ -+ err = ipqess_hw_init(ess); -+ if (err) -+ goto err_phylink; -+ -+ for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) { -+ netif_napi_add_tx(netdev, &ess->tx_ring[i].napi_tx, ipqess_tx_napi); -+ netif_napi_add(netdev, &ess->rx_ring[i].napi_rx, ipqess_rx_napi); -+ } -+ -+ err = register_netdev(netdev); -+ if (err) -+ goto err_hw_stop; -+ -+ return 0; -+ -+err_hw_stop: -+ ipqess_hw_stop(ess); -+ -+ ipqess_tx_ring_free(ess); -+ ipqess_rx_ring_free(ess); -+err_phylink: -+ phylink_destroy(ess->phylink); -+ -+err_clk: -+ clk_disable_unprepare(ess->ess_clk); -+ -+ return err; -+} -+ -+static void ipqess_axi_remove(struct platform_device *pdev) -+{ -+ const struct net_device *netdev = platform_get_drvdata(pdev); -+ struct ipqess *ess = netdev_priv(netdev); -+ -+ unregister_netdev(ess->netdev); -+ ipqess_hw_stop(ess); -+ -+ ipqess_tx_ring_free(ess); -+ ipqess_rx_ring_free(ess); -+ -+ phylink_destroy(ess->phylink); -+ clk_disable_unprepare(ess->ess_clk); -+} -+ -+static const struct of_device_id ipqess_of_mtable[] = { -+ {.compatible = "qcom,ipq4019-ess-edma" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, ipqess_of_mtable); -+ -+static struct platform_driver ipqess_axi_driver = { -+ .driver = { -+ .name = "ipqess-edma", -+ .of_match_table = ipqess_of_mtable, -+ }, -+ .probe = ipqess_axi_probe, -+ .remove = ipqess_axi_remove, -+}; -+ -+module_platform_driver(ipqess_axi_driver); -+ -+MODULE_AUTHOR("Qualcomm Atheros Inc"); -+MODULE_AUTHOR("John Crispin "); -+MODULE_AUTHOR("Christian Lamparter "); -+MODULE_AUTHOR("Gabor Juhos "); -+MODULE_AUTHOR("Maxime Chevallier "); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.h -@@ -0,0 +1,518 @@ -+/* SPDX-License-Identifier: (GPL-2.0 OR ISC) */ -+/* Copyright (c) 2014 - 2016, The Linux Foundation. All rights reserved. -+ * Copyright (c) 2017 - 2018, John Crispin -+ * Copyright (c) 2018 - 2019, Christian Lamparter -+ * Copyright (c) 2020 - 2021, Gabor Juhos -+ * Copyright (c) 2021 - 2022, Maxime Chevallier -+ * -+ */ -+ -+#ifndef _IPQESS_H_ -+#define _IPQESS_H_ -+ -+#define IPQESS_NETDEV_QUEUES 4 -+ -+#define IPQESS_TPD_EOP_SHIFT 31 -+ -+#define IPQESS_PORT_ID_SHIFT 12 -+#define IPQESS_PORT_ID_MASK 0x7 -+ -+/* tpd word 3 bit 18-28 */ -+#define IPQESS_TPD_PORT_BITMAP_SHIFT 18 -+ -+#define IPQESS_TPD_FROM_CPU_SHIFT 25 -+ -+#define IPQESS_RX_RING_SIZE 128 -+#define IPQESS_RX_HEAD_BUFF_SIZE 1540 -+#define IPQESS_TX_RING_SIZE 128 -+#define IPQESS_MAX_RX_QUEUE 8 -+#define IPQESS_MAX_TX_QUEUE 16 -+ -+/* Configurations */ -+#define IPQESS_INTR_CLEAR_TYPE 0 -+#define IPQESS_INTR_SW_IDX_W_TYPE 0 -+#define IPQESS_FIFO_THRESH_TYPE 0 -+#define IPQESS_RSS_TYPE 0 -+#define IPQESS_RX_IMT 0x0020 -+#define IPQESS_TX_IMT 0x0050 -+#define IPQESS_TPD_BURST 5 -+#define IPQESS_TXF_BURST 0x100 -+#define IPQESS_RFD_BURST 8 -+#define IPQESS_RFD_THR 16 -+#define IPQESS_RFD_LTHR 0 -+ -+/* Flags used in transmit direction */ -+#define IPQESS_DESC_LAST 0x1 -+#define IPQESS_DESC_SINGLE 0x2 -+#define IPQESS_DESC_PAGE 0x4 -+ -+struct ipqess_statistics { -+ u32 tx_q0_pkt; -+ u32 tx_q1_pkt; -+ u32 tx_q2_pkt; -+ u32 tx_q3_pkt; -+ u32 tx_q4_pkt; -+ u32 tx_q5_pkt; -+ u32 tx_q6_pkt; -+ u32 tx_q7_pkt; -+ u32 tx_q8_pkt; -+ u32 tx_q9_pkt; -+ u32 tx_q10_pkt; -+ u32 tx_q11_pkt; -+ u32 tx_q12_pkt; -+ u32 tx_q13_pkt; -+ u32 tx_q14_pkt; -+ u32 tx_q15_pkt; -+ u32 tx_q0_byte; -+ u32 tx_q1_byte; -+ u32 tx_q2_byte; -+ u32 tx_q3_byte; -+ u32 tx_q4_byte; -+ u32 tx_q5_byte; -+ u32 tx_q6_byte; -+ u32 tx_q7_byte; -+ u32 tx_q8_byte; -+ u32 tx_q9_byte; -+ u32 tx_q10_byte; -+ u32 tx_q11_byte; -+ u32 tx_q12_byte; -+ u32 tx_q13_byte; -+ u32 tx_q14_byte; -+ u32 tx_q15_byte; -+ u32 rx_q0_pkt; -+ u32 rx_q1_pkt; -+ u32 rx_q2_pkt; -+ u32 rx_q3_pkt; -+ u32 rx_q4_pkt; -+ u32 rx_q5_pkt; -+ u32 rx_q6_pkt; -+ u32 rx_q7_pkt; -+ u32 rx_q0_byte; -+ u32 rx_q1_byte; -+ u32 rx_q2_byte; -+ u32 rx_q3_byte; -+ u32 rx_q4_byte; -+ u32 rx_q5_byte; -+ u32 rx_q6_byte; -+ u32 rx_q7_byte; -+ u32 tx_desc_error; -+}; -+ -+struct ipqess_tx_desc { -+ __le16 len; -+ __le16 svlan_tag; -+ __le32 word1; -+ __le32 addr; -+ __le32 word3; -+} __aligned(16) __packed; -+ -+struct ipqess_rx_desc { -+ __le16 rrd0; -+ __le16 rrd1; -+ __le16 rrd2; -+ __le16 rrd3; -+ __le16 rrd4; -+ __le16 rrd5; -+ __le16 rrd6; -+ __le16 rrd7; -+} __aligned(16) __packed; -+ -+struct ipqess_buf { -+ struct sk_buff *skb; -+ dma_addr_t dma; -+ u32 flags; -+ u16 length; -+}; -+ -+struct ipqess_tx_ring { -+ struct napi_struct napi_tx; -+ u32 idx; -+ int ring_id; -+ struct ipqess *ess; -+ struct netdev_queue *nq; -+ struct ipqess_tx_desc *hw_desc; -+ struct ipqess_buf *buf; -+ dma_addr_t dma; -+ u16 count; -+ u16 head; -+ u16 tail; -+}; -+ -+struct ipqess_rx_ring { -+ struct napi_struct napi_rx; -+ u32 idx; -+ int ring_id; -+ struct ipqess *ess; -+ struct device *ppdev; -+ struct ipqess_rx_desc **hw_desc; -+ struct ipqess_buf *buf; -+ dma_addr_t dma; -+ u16 head; -+ u16 tail; -+ atomic_t refill_count; -+}; -+ -+struct ipqess_rx_ring_refill { -+ struct ipqess_rx_ring *rx_ring; -+ struct work_struct refill_work; -+}; -+ -+#define IPQESS_IRQ_NAME_LEN 32 -+ -+struct ipqess { -+ struct net_device *netdev; -+ void __iomem *hw_addr; -+ -+ struct clk *ess_clk; -+ struct reset_control *ess_rst; -+ -+ struct ipqess_rx_ring rx_ring[IPQESS_NETDEV_QUEUES]; -+ -+ struct platform_device *pdev; -+ struct phylink *phylink; -+ struct phylink_config phylink_config; -+ struct ipqess_tx_ring tx_ring[IPQESS_NETDEV_QUEUES]; -+ -+ struct ipqess_statistics ipqess_stats; -+ -+ /* Protects stats */ -+ spinlock_t stats_lock; -+ struct net_device_stats stats; -+ -+ struct ipqess_rx_ring_refill rx_refill[IPQESS_NETDEV_QUEUES]; -+ u32 tx_irq[IPQESS_MAX_TX_QUEUE]; -+ char tx_irq_names[IPQESS_MAX_TX_QUEUE][IPQESS_IRQ_NAME_LEN]; -+ u32 rx_irq[IPQESS_MAX_RX_QUEUE]; -+ char rx_irq_names[IPQESS_MAX_TX_QUEUE][IPQESS_IRQ_NAME_LEN]; -+}; -+ -+void ipqess_set_ethtool_ops(struct net_device *netdev); -+void ipqess_update_hw_stats(struct ipqess *ess); -+ -+/* register definition */ -+#define IPQESS_REG_MAS_CTRL 0x0 -+#define IPQESS_REG_TIMEOUT_CTRL 0x004 -+#define IPQESS_REG_DBG0 0x008 -+#define IPQESS_REG_DBG1 0x00C -+#define IPQESS_REG_SW_CTRL0 0x100 -+#define IPQESS_REG_SW_CTRL1 0x104 -+ -+/* Interrupt Status Register */ -+#define IPQESS_REG_RX_ISR 0x200 -+#define IPQESS_REG_TX_ISR 0x208 -+#define IPQESS_REG_MISC_ISR 0x210 -+#define IPQESS_REG_WOL_ISR 0x218 -+ -+#define IPQESS_MISC_ISR_RX_URG_Q(x) (1 << (x)) -+ -+#define IPQESS_MISC_ISR_AXIR_TIMEOUT 0x00000100 -+#define IPQESS_MISC_ISR_AXIR_ERR 0x00000200 -+#define IPQESS_MISC_ISR_TXF_DEAD 0x00000400 -+#define IPQESS_MISC_ISR_AXIW_ERR 0x00000800 -+#define IPQESS_MISC_ISR_AXIW_TIMEOUT 0x00001000 -+ -+#define IPQESS_WOL_ISR 0x00000001 -+ -+/* Interrupt Mask Register */ -+#define IPQESS_REG_MISC_IMR 0x214 -+#define IPQESS_REG_WOL_IMR 0x218 -+ -+#define IPQESS_RX_IMR_NORMAL_MASK 0x1 -+#define IPQESS_TX_IMR_NORMAL_MASK 0x1 -+#define IPQESS_MISC_IMR_NORMAL_MASK 0x80001FFF -+#define IPQESS_WOL_IMR_NORMAL_MASK 0x1 -+ -+/* Edma receive consumer index */ -+#define IPQESS_REG_RX_SW_CONS_IDX_Q(x) (0x220 + ((x) << 2)) /* x is the queue id */ -+ -+/* Edma transmit consumer index */ -+#define IPQESS_REG_TX_SW_CONS_IDX_Q(x) (0x240 + ((x) << 2)) /* x is the queue id */ -+ -+/* IRQ Moderator Initial Timer Register */ -+#define IPQESS_REG_IRQ_MODRT_TIMER_INIT 0x280 -+#define IPQESS_IRQ_MODRT_TIMER_MASK 0xFFFF -+#define IPQESS_IRQ_MODRT_RX_TIMER_SHIFT 0 -+#define IPQESS_IRQ_MODRT_TX_TIMER_SHIFT 16 -+ -+/* Interrupt Control Register */ -+#define IPQESS_REG_INTR_CTRL 0x284 -+#define IPQESS_INTR_CLR_TYP_SHIFT 0 -+#define IPQESS_INTR_SW_IDX_W_TYP_SHIFT 1 -+#define IPQESS_INTR_CLEAR_TYPE_W1 0 -+#define IPQESS_INTR_CLEAR_TYPE_R 1 -+ -+/* RX Interrupt Mask Register */ -+#define IPQESS_REG_RX_INT_MASK_Q(x) (0x300 + ((x) << 2)) /* x = queue id */ -+ -+/* TX Interrupt mask register */ -+#define IPQESS_REG_TX_INT_MASK_Q(x) (0x340 + ((x) << 2)) /* x = queue id */ -+ -+/* Load Ptr Register -+ * Software sets this bit after the initialization of the head and tail -+ */ -+#define IPQESS_REG_TX_SRAM_PART 0x400 -+#define IPQESS_LOAD_PTR_SHIFT 16 -+ -+/* TXQ Control Register */ -+#define IPQESS_REG_TXQ_CTRL 0x404 -+#define IPQESS_TXQ_CTRL_IP_OPTION_EN 0x10 -+#define IPQESS_TXQ_CTRL_TXQ_EN 0x20 -+#define IPQESS_TXQ_CTRL_ENH_MODE 0x40 -+#define IPQESS_TXQ_CTRL_LS_8023_EN 0x80 -+#define IPQESS_TXQ_CTRL_TPD_BURST_EN 0x100 -+#define IPQESS_TXQ_CTRL_LSO_BREAK_EN 0x200 -+#define IPQESS_TXQ_NUM_TPD_BURST_MASK 0xF -+#define IPQESS_TXQ_TXF_BURST_NUM_MASK 0xFFFF -+#define IPQESS_TXQ_NUM_TPD_BURST_SHIFT 0 -+#define IPQESS_TXQ_TXF_BURST_NUM_SHIFT 16 -+ -+#define IPQESS_REG_TXF_WATER_MARK 0x408 /* In 8-bytes */ -+#define IPQESS_TXF_WATER_MARK_MASK 0x0FFF -+#define IPQESS_TXF_LOW_WATER_MARK_SHIFT 0 -+#define IPQESS_TXF_HIGH_WATER_MARK_SHIFT 16 -+#define IPQESS_TXQ_CTRL_BURST_MODE_EN 0x80000000 -+ -+/* WRR Control Register */ -+#define IPQESS_REG_WRR_CTRL_Q0_Q3 0x40c -+#define IPQESS_REG_WRR_CTRL_Q4_Q7 0x410 -+#define IPQESS_REG_WRR_CTRL_Q8_Q11 0x414 -+#define IPQESS_REG_WRR_CTRL_Q12_Q15 0x418 -+ -+/* Weight round robin(WRR), it takes queue as input, and computes -+ * starting bits where we need to write the weight for a particular -+ * queue -+ */ -+#define IPQESS_WRR_SHIFT(x) (((x) * 5) % 20) -+ -+/* Tx Descriptor Control Register */ -+#define IPQESS_REG_TPD_RING_SIZE 0x41C -+#define IPQESS_TPD_RING_SIZE_SHIFT 0 -+#define IPQESS_TPD_RING_SIZE_MASK 0xFFFF -+ -+/* Transmit descriptor base address */ -+#define IPQESS_REG_TPD_BASE_ADDR_Q(x) (0x420 + ((x) << 2)) /* x = queue id */ -+ -+/* TPD Index Register */ -+#define IPQESS_REG_TPD_IDX_Q(x) (0x460 + ((x) << 2)) /* x = queue id */ -+ -+#define IPQESS_TPD_PROD_IDX_BITS 0x0000FFFF -+#define IPQESS_TPD_CONS_IDX_BITS 0xFFFF0000 -+#define IPQESS_TPD_PROD_IDX_MASK 0xFFFF -+#define IPQESS_TPD_CONS_IDX_MASK 0xFFFF -+#define IPQESS_TPD_PROD_IDX_SHIFT 0 -+#define IPQESS_TPD_CONS_IDX_SHIFT 16 -+ -+/* TX Virtual Queue Mapping Control Register */ -+#define IPQESS_REG_VQ_CTRL0 0x4A0 -+#define IPQESS_REG_VQ_CTRL1 0x4A4 -+ -+/* Virtual QID shift, it takes queue as input, and computes -+ * Virtual QID position in virtual qid control register -+ */ -+#define IPQESS_VQ_ID_SHIFT(i) (((i) * 3) % 24) -+ -+/* Virtual Queue Default Value */ -+#define IPQESS_VQ_REG_VALUE 0x240240 -+ -+/* Tx side Port Interface Control Register */ -+#define IPQESS_REG_PORT_CTRL 0x4A8 -+#define IPQESS_PAD_EN_SHIFT 15 -+ -+/* Tx side VLAN Configuration Register */ -+#define IPQESS_REG_VLAN_CFG 0x4AC -+ -+#define IPQESS_VLAN_CFG_SVLAN_TPID_SHIFT 0 -+#define IPQESS_VLAN_CFG_SVLAN_TPID_MASK 0xffff -+#define IPQESS_VLAN_CFG_CVLAN_TPID_SHIFT 16 -+#define IPQESS_VLAN_CFG_CVLAN_TPID_MASK 0xffff -+ -+#define IPQESS_TX_CVLAN 16 -+#define IPQESS_TX_INS_CVLAN 17 -+#define IPQESS_TX_CVLAN_TAG_SHIFT 0 -+ -+#define IPQESS_TX_SVLAN 14 -+#define IPQESS_TX_INS_SVLAN 15 -+#define IPQESS_TX_SVLAN_TAG_SHIFT 16 -+ -+/* Tx Queue Packet Statistic Register */ -+#define IPQESS_REG_TX_STAT_PKT_Q(x) (0x700 + ((x) << 3)) /* x = queue id */ -+ -+#define IPQESS_TX_STAT_PKT_MASK 0xFFFFFF -+ -+/* Tx Queue Byte Statistic Register */ -+#define IPQESS_REG_TX_STAT_BYTE_Q(x) (0x704 + ((x) << 3)) /* x = queue id */ -+ -+/* Load Balance Based Ring Offset Register */ -+#define IPQESS_REG_LB_RING 0x800 -+#define IPQESS_LB_RING_ENTRY_MASK 0xff -+#define IPQESS_LB_RING_ID_MASK 0x7 -+#define IPQESS_LB_RING_PROFILE_ID_MASK 0x3 -+#define IPQESS_LB_RING_ENTRY_BIT_OFFSET 8 -+#define IPQESS_LB_RING_ID_OFFSET 0 -+#define IPQESS_LB_RING_PROFILE_ID_OFFSET 3 -+#define IPQESS_LB_REG_VALUE 0x6040200 -+ -+/* Load Balance Priority Mapping Register */ -+#define IPQESS_REG_LB_PRI_START 0x804 -+#define IPQESS_REG_LB_PRI_END 0x810 -+#define IPQESS_LB_PRI_REG_INC 4 -+#define IPQESS_LB_PRI_ENTRY_BIT_OFFSET 4 -+#define IPQESS_LB_PRI_ENTRY_MASK 0xf -+ -+/* RSS Priority Mapping Register */ -+#define IPQESS_REG_RSS_PRI 0x820 -+#define IPQESS_RSS_PRI_ENTRY_MASK 0xf -+#define IPQESS_RSS_RING_ID_MASK 0x7 -+#define IPQESS_RSS_PRI_ENTRY_BIT_OFFSET 4 -+ -+/* RSS Indirection Register */ -+#define IPQESS_REG_RSS_IDT(x) (0x840 + ((x) << 2)) /* x = No. of indirection table */ -+#define IPQESS_NUM_IDT 16 -+#define IPQESS_RSS_IDT_VALUE 0x64206420 -+ -+/* Default RSS Ring Register */ -+#define IPQESS_REG_DEF_RSS 0x890 -+#define IPQESS_DEF_RSS_MASK 0x7 -+ -+/* RSS Hash Function Type Register */ -+#define IPQESS_REG_RSS_TYPE 0x894 -+#define IPQESS_RSS_TYPE_NONE 0x01 -+#define IPQESS_RSS_TYPE_IPV4TCP 0x02 -+#define IPQESS_RSS_TYPE_IPV6_TCP 0x04 -+#define IPQESS_RSS_TYPE_IPV4_UDP 0x08 -+#define IPQESS_RSS_TYPE_IPV6UDP 0x10 -+#define IPQESS_RSS_TYPE_IPV4 0x20 -+#define IPQESS_RSS_TYPE_IPV6 0x40 -+#define IPQESS_RSS_HASH_MODE_MASK 0x7f -+ -+#define IPQESS_REG_RSS_HASH_VALUE 0x8C0 -+ -+#define IPQESS_REG_RSS_TYPE_RESULT 0x8C4 -+ -+#define IPQESS_HASH_TYPE_START 0 -+#define IPQESS_HASH_TYPE_END 5 -+#define IPQESS_HASH_TYPE_SHIFT 12 -+ -+#define IPQESS_RFS_FLOW_ENTRIES 1024 -+#define IPQESS_RFS_FLOW_ENTRIES_MASK (IPQESS_RFS_FLOW_ENTRIES - 1) -+#define IPQESS_RFS_EXPIRE_COUNT_PER_CALL 128 -+ -+/* RFD Base Address Register */ -+#define IPQESS_REG_RFD_BASE_ADDR_Q(x) (0x950 + ((x) << 2)) /* x = queue id */ -+ -+/* RFD Index Register */ -+#define IPQESS_REG_RFD_IDX_Q(x) (0x9B0 + ((x) << 2)) /* x = queue id */ -+ -+#define IPQESS_RFD_PROD_IDX_BITS 0x00000FFF -+#define IPQESS_RFD_CONS_IDX_BITS 0x0FFF0000 -+#define IPQESS_RFD_PROD_IDX_MASK 0xFFF -+#define IPQESS_RFD_CONS_IDX_MASK 0xFFF -+#define IPQESS_RFD_PROD_IDX_SHIFT 0 -+#define IPQESS_RFD_CONS_IDX_SHIFT 16 -+ -+/* Rx Descriptor Control Register */ -+#define IPQESS_REG_RX_DESC0 0xA10 -+#define IPQESS_RFD_RING_SIZE_MASK 0xFFF -+#define IPQESS_RX_BUF_SIZE_MASK 0xFFFF -+#define IPQESS_RFD_RING_SIZE_SHIFT 0 -+#define IPQESS_RX_BUF_SIZE_SHIFT 16 -+ -+#define IPQESS_REG_RX_DESC1 0xA14 -+#define IPQESS_RXQ_RFD_BURST_NUM_MASK 0x3F -+#define IPQESS_RXQ_RFD_PF_THRESH_MASK 0x1F -+#define IPQESS_RXQ_RFD_LOW_THRESH_MASK 0xFFF -+#define IPQESS_RXQ_RFD_BURST_NUM_SHIFT 0 -+#define IPQESS_RXQ_RFD_PF_THRESH_SHIFT 8 -+#define IPQESS_RXQ_RFD_LOW_THRESH_SHIFT 16 -+ -+/* RXQ Control Register */ -+#define IPQESS_REG_RXQ_CTRL 0xA18 -+#define IPQESS_FIFO_THRESH_TYPE_SHIF 0 -+#define IPQESS_FIFO_THRESH_128_BYTE 0x0 -+#define IPQESS_FIFO_THRESH_64_BYTE 0x1 -+#define IPQESS_RXQ_CTRL_RMV_VLAN 0x00000002 -+#define IPQESS_RXQ_CTRL_EN_MASK GENMASK(15, 8) -+#define IPQESS_RXQ_CTRL_EN(__qid) BIT(8 + (__qid)) -+ -+/* AXI Burst Size Config */ -+#define IPQESS_REG_AXIW_CTRL_MAXWRSIZE 0xA1C -+#define IPQESS_AXIW_MAXWRSIZE_VALUE 0x0 -+ -+/* Rx Statistics Register */ -+#define IPQESS_REG_RX_STAT_BYTE_Q(x) (0xA30 + ((x) << 2)) /* x = queue id */ -+#define IPQESS_REG_RX_STAT_PKT_Q(x) (0xA50 + ((x) << 2)) /* x = queue id */ -+ -+/* WoL Pattern Length Register */ -+#define IPQESS_REG_WOL_PATTERN_LEN0 0xC00 -+#define IPQESS_WOL_PT_LEN_MASK 0xFF -+#define IPQESS_WOL_PT0_LEN_SHIFT 0 -+#define IPQESS_WOL_PT1_LEN_SHIFT 8 -+#define IPQESS_WOL_PT2_LEN_SHIFT 16 -+#define IPQESS_WOL_PT3_LEN_SHIFT 24 -+ -+#define IPQESS_REG_WOL_PATTERN_LEN1 0xC04 -+#define IPQESS_WOL_PT4_LEN_SHIFT 0 -+#define IPQESS_WOL_PT5_LEN_SHIFT 8 -+#define IPQESS_WOL_PT6_LEN_SHIFT 16 -+ -+/* WoL Control Register */ -+#define IPQESS_REG_WOL_CTRL 0xC08 -+#define IPQESS_WOL_WK_EN 0x00000001 -+#define IPQESS_WOL_MG_EN 0x00000002 -+#define IPQESS_WOL_PT0_EN 0x00000004 -+#define IPQESS_WOL_PT1_EN 0x00000008 -+#define IPQESS_WOL_PT2_EN 0x00000010 -+#define IPQESS_WOL_PT3_EN 0x00000020 -+#define IPQESS_WOL_PT4_EN 0x00000040 -+#define IPQESS_WOL_PT5_EN 0x00000080 -+#define IPQESS_WOL_PT6_EN 0x00000100 -+ -+/* MAC Control Register */ -+#define IPQESS_REG_MAC_CTRL0 0xC20 -+#define IPQESS_REG_MAC_CTRL1 0xC24 -+ -+/* WoL Pattern Register */ -+#define IPQESS_REG_WOL_PATTERN_START 0x5000 -+#define IPQESS_PATTERN_PART_REG_OFFSET 0x40 -+ -+/* TX descriptor fields */ -+#define IPQESS_TPD_HDR_SHIFT 0 -+#define IPQESS_TPD_PPPOE_EN 0x00000100 -+#define IPQESS_TPD_IP_CSUM_EN 0x00000200 -+#define IPQESS_TPD_TCP_CSUM_EN 0x0000400 -+#define IPQESS_TPD_UDP_CSUM_EN 0x00000800 -+#define IPQESS_TPD_CUSTOM_CSUM_EN 0x00000C00 -+#define IPQESS_TPD_LSO_EN 0x00001000 -+#define IPQESS_TPD_LSO_V2_EN 0x00002000 -+/* The VLAN_TAGGED bit is not used in the publicly available -+ * drivers. The definition has been stolen from the Atheros -+ * 'alx' driver (drivers/net/ethernet/atheros/alx/hw.h). It -+ * seems that it has the same meaning in regard to the EDMA -+ * hardware. -+ */ -+#define IPQESS_TPD_VLAN_TAGGED 0x00004000 -+#define IPQESS_TPD_IPV4_EN 0x00010000 -+#define IPQESS_TPD_MSS_MASK 0x1FFF -+#define IPQESS_TPD_MSS_SHIFT 18 -+#define IPQESS_TPD_CUSTOM_CSUM_SHIFT 18 -+ -+/* RRD descriptor fields */ -+#define IPQESS_RRD_NUM_RFD_MASK 0x000F -+#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF -+#define IPQESS_RRD_SRC_PORT_NUM_MASK 0x4000 -+#define IPQESS_RRD_SVLAN 0x8000 -+#define IPQESS_RRD_FLOW_COOKIE_MASK 0x07FF -+ -+#define IPQESS_RRD_PKT_SIZE_MASK 0x3FFF -+#define IPQESS_RRD_CSUM_FAIL_MASK 0xC000 -+#define IPQESS_RRD_CVLAN 0x0001 -+#define IPQESS_RRD_DESC_VALID 0x8000 -+ -+#define IPQESS_RRD_PRIORITY_SHIFT 4 -+#define IPQESS_RRD_PRIORITY_MASK 0x7 -+#define IPQESS_RRD_PORT_TYPE_SHIFT 7 -+#define IPQESS_RRD_PORT_TYPE_MASK 0x1F -+ -+#define IPQESS_RRD_PORT_ID_MASK 0x7000 -+ -+#endif ---- /dev/null -+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c -@@ -0,0 +1,164 @@ -+// SPDX-License-Identifier: GPL-2.0 OR ISC -+/* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved. -+ * Copyright (c) 2017 - 2018, John Crispin -+ * Copyright (c) 2021 - 2022, Maxime Chevallier -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include "ipqess.h" -+ -+struct ipqess_ethtool_stats { -+ u8 string[ETH_GSTRING_LEN]; -+ u32 offset; -+}; -+ -+#define IPQESS_STAT(m) offsetof(struct ipqess_statistics, m) -+#define DRVINFO_LEN 32 -+ -+static const struct ipqess_ethtool_stats ipqess_stats[] = { -+ {"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)}, -+ {"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)}, -+ {"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)}, -+ {"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)}, -+ {"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)}, -+ {"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)}, -+ {"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)}, -+ {"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)}, -+ {"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)}, -+ {"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)}, -+ {"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)}, -+ {"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)}, -+ {"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)}, -+ {"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)}, -+ {"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)}, -+ {"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)}, -+ {"tx_q0_byte", IPQESS_STAT(tx_q0_byte)}, -+ {"tx_q1_byte", IPQESS_STAT(tx_q1_byte)}, -+ {"tx_q2_byte", IPQESS_STAT(tx_q2_byte)}, -+ {"tx_q3_byte", IPQESS_STAT(tx_q3_byte)}, -+ {"tx_q4_byte", IPQESS_STAT(tx_q4_byte)}, -+ {"tx_q5_byte", IPQESS_STAT(tx_q5_byte)}, -+ {"tx_q6_byte", IPQESS_STAT(tx_q6_byte)}, -+ {"tx_q7_byte", IPQESS_STAT(tx_q7_byte)}, -+ {"tx_q8_byte", IPQESS_STAT(tx_q8_byte)}, -+ {"tx_q9_byte", IPQESS_STAT(tx_q9_byte)}, -+ {"tx_q10_byte", IPQESS_STAT(tx_q10_byte)}, -+ {"tx_q11_byte", IPQESS_STAT(tx_q11_byte)}, -+ {"tx_q12_byte", IPQESS_STAT(tx_q12_byte)}, -+ {"tx_q13_byte", IPQESS_STAT(tx_q13_byte)}, -+ {"tx_q14_byte", IPQESS_STAT(tx_q14_byte)}, -+ {"tx_q15_byte", IPQESS_STAT(tx_q15_byte)}, -+ {"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)}, -+ {"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)}, -+ {"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)}, -+ {"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)}, -+ {"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)}, -+ {"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)}, -+ {"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)}, -+ {"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)}, -+ {"rx_q0_byte", IPQESS_STAT(rx_q0_byte)}, -+ {"rx_q1_byte", IPQESS_STAT(rx_q1_byte)}, -+ {"rx_q2_byte", IPQESS_STAT(rx_q2_byte)}, -+ {"rx_q3_byte", IPQESS_STAT(rx_q3_byte)}, -+ {"rx_q4_byte", IPQESS_STAT(rx_q4_byte)}, -+ {"rx_q5_byte", IPQESS_STAT(rx_q5_byte)}, -+ {"rx_q6_byte", IPQESS_STAT(rx_q6_byte)}, -+ {"rx_q7_byte", IPQESS_STAT(rx_q7_byte)}, -+ {"tx_desc_error", IPQESS_STAT(tx_desc_error)}, -+}; -+ -+static int ipqess_get_strset_count(struct net_device *netdev, int sset) -+{ -+ switch (sset) { -+ case ETH_SS_STATS: -+ return ARRAY_SIZE(ipqess_stats); -+ default: -+ netdev_dbg(netdev, "%s: Unsupported string set", __func__); -+ return -EOPNOTSUPP; -+ } -+} -+ -+static void ipqess_get_strings(struct net_device *netdev, u32 stringset, -+ u8 *data) -+{ -+ u8 *p = data; -+ u32 i; -+ -+ switch (stringset) { -+ case ETH_SS_STATS: -+ for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++) -+ ethtool_puts(&p, ipqess_stats[i].string); -+ break; -+ } -+} -+ -+static void ipqess_get_ethtool_stats(struct net_device *netdev, -+ struct ethtool_stats *stats, -+ uint64_t *data) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ u32 *essstats = (u32 *)&ess->ipqess_stats; -+ int i; -+ -+ spin_lock(&ess->stats_lock); -+ -+ ipqess_update_hw_stats(ess); -+ -+ for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++) -+ data[i] = *(u32 *)(essstats + (ipqess_stats[i].offset / sizeof(u32))); -+ -+ spin_unlock(&ess->stats_lock); -+} -+ -+static void ipqess_get_drvinfo(struct net_device *dev, -+ struct ethtool_drvinfo *info) -+{ -+ strscpy(info->driver, "qca_ipqess", DRVINFO_LEN); -+ strscpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN); -+} -+ -+static int ipqess_get_link_ksettings(struct net_device *netdev, -+ struct ethtool_link_ksettings *cmd) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ -+ return phylink_ethtool_ksettings_get(ess->phylink, cmd); -+} -+ -+static int ipqess_set_link_ksettings(struct net_device *netdev, -+ const struct ethtool_link_ksettings *cmd) -+{ -+ struct ipqess *ess = netdev_priv(netdev); -+ -+ return phylink_ethtool_ksettings_set(ess->phylink, cmd); -+} -+ -+static void ipqess_get_ringparam(struct net_device *netdev, -+ struct ethtool_ringparam *ring, -+ struct kernel_ethtool_ringparam *kernel_ering, -+ struct netlink_ext_ack *extack) -+{ -+ ring->tx_max_pending = IPQESS_TX_RING_SIZE; -+ ring->rx_max_pending = IPQESS_RX_RING_SIZE; -+} -+ -+static const struct ethtool_ops ipqesstool_ops = { -+ .get_drvinfo = &ipqess_get_drvinfo, -+ .get_link = ðtool_op_get_link, -+ .get_link_ksettings = &ipqess_get_link_ksettings, -+ .set_link_ksettings = &ipqess_set_link_ksettings, -+ .get_strings = &ipqess_get_strings, -+ .get_sset_count = &ipqess_get_strset_count, -+ .get_ethtool_stats = &ipqess_get_ethtool_stats, -+ .get_ringparam = ipqess_get_ringparam, -+}; -+ -+void ipqess_set_ethtool_ops(struct net_device *netdev) -+{ -+ netdev->ethtool_ops = &ipqesstool_ops; -+} diff --git a/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch b/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch index 7f67a15664..1969cb386e 100644 --- a/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch +++ b/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch @@ -43,15 +43,15 @@ Signed-off-by: Matthias Schiffer #include #include #include -@@ -22,6 +23,7 @@ - #include +@@ -23,6 +24,7 @@ + #include #include #include +#include #include #include "ipqess.h" -@@ -327,6 +329,7 @@ static int ipqess_rx_poll(struct ipqess_ +@@ -328,6 +330,7 @@ static int ipqess_rx_poll(struct ipqess_ tail &= IPQESS_RFD_CONS_IDX_MASK; while (done < budget) { @@ -59,7 +59,7 @@ Signed-off-by: Matthias Schiffer struct ipqess_rx_desc *rd; struct sk_buff *skb; -@@ -406,6 +409,12 @@ static int ipqess_rx_poll(struct ipqess_ +@@ -407,6 +410,12 @@ static int ipqess_rx_poll(struct ipqess_ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), le16_to_cpu(rd->rrd4)); @@ -72,7 +72,7 @@ Signed-off-by: Matthias Schiffer napi_gro_receive(&rx_ring->napi_rx, skb); rx_ring->ess->stats.rx_packets++; -@@ -701,6 +710,26 @@ static void ipqess_rollback_tx(struct ip +@@ -702,6 +711,26 @@ static void ipqess_rollback_tx(struct ip tx_ring->head = start_index; } @@ -99,7 +99,7 @@ Signed-off-by: Matthias Schiffer static int ipqess_tx_map_and_fill(struct ipqess_tx_ring *tx_ring, struct sk_buff *skb) { -@@ -711,6 +740,8 @@ static int ipqess_tx_map_and_fill(struct +@@ -712,6 +741,8 @@ static int ipqess_tx_map_and_fill(struct u16 len; int i; @@ -108,7 +108,7 @@ Signed-off-by: Matthias Schiffer if (skb_is_gso(skb)) { if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { lso_word1 |= IPQESS_TPD_IPV4_EN; -@@ -912,6 +943,33 @@ static const struct net_device_ops ipqes +@@ -910,6 +941,33 @@ static const struct net_device_ops ipqes .ndo_tx_timeout = ipqess_tx_timeout, }; @@ -142,7 +142,7 @@ Signed-off-by: Matthias Schiffer static void ipqess_hw_stop(struct ipqess *ess) { int i; -@@ -1188,12 +1246,19 @@ static int ipqess_axi_probe(struct platf +@@ -1186,12 +1244,19 @@ static int ipqess_axi_probe(struct platf netif_napi_add(netdev, &ess->rx_ring[i].napi_rx, ipqess_rx_napi); } diff --git a/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch b/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch index 1ad96ecd87..d033928b49 100644 --- a/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch +++ b/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch @@ -50,7 +50,7 @@ Signed-off-by: Robert Marko --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -631,9 +631,22 @@ static int ipqess_stop(struct net_device +@@ -632,9 +632,22 @@ static int ipqess_stop(struct net_device netif_tx_stop_all_queues(netdev); phylink_stop(ess->phylink); ipqess_irq_disable(ess); diff --git a/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch b/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch index 833a3c0c16..1722a35dbe 100644 --- a/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch +++ b/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch @@ -16,7 +16,7 @@ Signed-off-by: Robert Marko --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -530,9 +530,9 @@ static irqreturn_t ipqess_interrupt_tx(i +@@ -531,9 +531,9 @@ static irqreturn_t ipqess_interrupt_tx(i struct ipqess_tx_ring *tx_ring = (struct ipqess_tx_ring *)priv; if (likely(napi_schedule_prep(&tx_ring->napi_tx))) { @@ -27,7 +27,7 @@ Signed-off-by: Robert Marko } return IRQ_HANDLED; -@@ -543,9 +543,9 @@ static irqreturn_t ipqess_interrupt_rx(i +@@ -544,9 +544,9 @@ static irqreturn_t ipqess_interrupt_rx(i struct ipqess_rx_ring *rx_ring = (struct ipqess_rx_ring *)priv; if (likely(napi_schedule_prep(&rx_ring->napi_rx))) { @@ -38,7 +38,7 @@ Signed-off-by: Robert Marko } return IRQ_HANDLED; -@@ -1268,6 +1268,8 @@ static int ipqess_axi_probe(struct platf +@@ -1266,6 +1266,8 @@ static int ipqess_axi_probe(struct platf if (err) goto err_notifier_unregister; diff --git a/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch b/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch index 149208aa69..535ad8987f 100644 --- a/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch +++ b/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch @@ -35,7 +35,7 @@ Signed-off-by: Christian Marangi --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -453,13 +453,22 @@ static int ipqess_tx_complete(struct ipq +@@ -454,13 +454,22 @@ static int ipqess_tx_complete(struct ipq tail >>= IPQESS_TPD_CONS_IDX_SHIFT; tail &= IPQESS_TPD_CONS_IDX_MASK; From c85adddd8d12309f4b7561bd8a71350eb1c73583 Mon Sep 17 00:00:00 2001 From: Denis Yarkovoy Date: Sun, 12 Jul 2026 10:34:57 +0300 Subject: [PATCH 061/152] ipq40xx: ipqess: recover TX queue on watchdog timeout The current ipqess TX timeout handler only logs the timeout. If TX completion interrupts are lost or left masked, descriptors can remain pending indefinitely and the master Ethernet device stays wedged until a reboot. Log the TX ring indexes and interrupt state to make future failures useful, then force the TX completion NAPI path to run and re-kick the TX producer index. This gives the driver a chance to reclaim pending TX descriptors and wake the queue instead of reporting the same watchdog forever. Signed-off-by: Denis Yarkovoy Link: https://github.com/openwrt/openwrt/pull/24122 Signed-off-by: Jonas Jelonek --- .../net/ethernet/qualcomm/ipqess/ipqess.c | 33 ++++++++++++++++++- ...-Add-out-of-band-DSA-tagging-support.patch | 16 ++++----- ...ess-release-IRQ-s-on-network-device-.patch | 2 +- ...qess-enable-threaded-NAPI-by-default.patch | 6 ++-- ...ualcomm-ipqess-fix-TX-timeout-errors.patch | 2 +- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c index b3dfd6f1ae..78c48c0bb9 100644 --- a/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/target/linux/ipq40xx/files/drivers/net/ethernet/qualcomm/ipqess/ipqess.c @@ -894,7 +894,38 @@ static int ipqess_set_mac_address(struct net_device *netdev, void *p) static void ipqess_tx_timeout(struct net_device *netdev, unsigned int txq_id) { - netdev_err(netdev, "TX timeout on queue %d\n", txq_id); + struct ipqess_tx_ring *tr; + struct ipqess *ess; + u32 sw_cons; + u32 tx_imr; + u32 tx_isr; + u32 cons; + u32 prod; + u32 idx; + + ess = netdev_priv(netdev); + tr = &ess->tx_ring[txq_id]; + + idx = ipqess_r32(ess, IPQESS_REG_TPD_IDX_Q(tr->idx)); + prod = (idx >> IPQESS_TPD_PROD_IDX_SHIFT) & IPQESS_TPD_PROD_IDX_MASK; + cons = (idx >> IPQESS_TPD_CONS_IDX_SHIFT) & IPQESS_TPD_CONS_IDX_MASK; + sw_cons = ipqess_r32(ess, IPQESS_REG_TX_SW_CONS_IDX_Q(tr->idx)); + tx_isr = ipqess_r32(ess, IPQESS_REG_TX_ISR); + tx_imr = ipqess_r32(ess, IPQESS_REG_TX_INT_MASK_Q(tr->idx)); + + netdev_warn(netdev, + "TX timeout on queue %d head=%u tail=%u hw_prod=%u hw_cons=%u sw_cons=%u tx_isr=%08x tx_imr=%08x\n", + tr->idx, tr->head, tr->tail, prod, cons, sw_cons, tx_isr, tx_imr); + + /* A lost or masked TX completion interrupt leaves descriptors pending + * forever. Force the TX completion path to run and re-kick the producer. + */ + if (napi_schedule_prep(&tr->napi_tx)) { + __napi_schedule(&tr->napi_tx); + ipqess_w32(ess, IPQESS_REG_TX_INT_MASK_Q(tr->idx), 0x0); + } + + ipqess_kick_tx(tr); } static const struct net_device_ops ipqess_axi_netdev_ops = { diff --git a/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch b/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch index 1969cb386e..406817f911 100644 --- a/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch +++ b/target/linux/ipq40xx/patches-6.18/702-net-ipqess-Add-out-of-band-DSA-tagging-support.patch @@ -43,15 +43,15 @@ Signed-off-by: Matthias Schiffer #include #include #include -@@ -23,6 +24,7 @@ - #include +@@ -22,6 +23,7 @@ + #include #include #include +#include #include #include "ipqess.h" -@@ -328,6 +330,7 @@ static int ipqess_rx_poll(struct ipqess_ +@@ -327,6 +329,7 @@ static int ipqess_rx_poll(struct ipqess_ tail &= IPQESS_RFD_CONS_IDX_MASK; while (done < budget) { @@ -59,7 +59,7 @@ Signed-off-by: Matthias Schiffer struct ipqess_rx_desc *rd; struct sk_buff *skb; -@@ -407,6 +410,12 @@ static int ipqess_rx_poll(struct ipqess_ +@@ -406,6 +409,12 @@ static int ipqess_rx_poll(struct ipqess_ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), le16_to_cpu(rd->rrd4)); @@ -72,7 +72,7 @@ Signed-off-by: Matthias Schiffer napi_gro_receive(&rx_ring->napi_rx, skb); rx_ring->ess->stats.rx_packets++; -@@ -702,6 +711,26 @@ static void ipqess_rollback_tx(struct ip +@@ -701,6 +710,26 @@ static void ipqess_rollback_tx(struct ip tx_ring->head = start_index; } @@ -99,7 +99,7 @@ Signed-off-by: Matthias Schiffer static int ipqess_tx_map_and_fill(struct ipqess_tx_ring *tx_ring, struct sk_buff *skb) { -@@ -712,6 +741,8 @@ static int ipqess_tx_map_and_fill(struct +@@ -711,6 +740,8 @@ static int ipqess_tx_map_and_fill(struct u16 len; int i; @@ -108,7 +108,7 @@ Signed-off-by: Matthias Schiffer if (skb_is_gso(skb)) { if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { lso_word1 |= IPQESS_TPD_IPV4_EN; -@@ -910,6 +941,33 @@ static const struct net_device_ops ipqes +@@ -940,6 +971,33 @@ static const struct net_device_ops ipqes .ndo_tx_timeout = ipqess_tx_timeout, }; @@ -142,7 +142,7 @@ Signed-off-by: Matthias Schiffer static void ipqess_hw_stop(struct ipqess *ess) { int i; -@@ -1186,12 +1244,19 @@ static int ipqess_axi_probe(struct platf +@@ -1216,12 +1274,19 @@ static int ipqess_axi_probe(struct platf netif_napi_add(netdev, &ess->rx_ring[i].napi_rx, ipqess_rx_napi); } diff --git a/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch b/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch index d033928b49..1ad96ecd87 100644 --- a/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch +++ b/target/linux/ipq40xx/patches-6.18/703-net-qualcomm-ipqess-release-IRQ-s-on-network-device-.patch @@ -50,7 +50,7 @@ Signed-off-by: Robert Marko --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -632,9 +632,22 @@ static int ipqess_stop(struct net_device +@@ -631,9 +631,22 @@ static int ipqess_stop(struct net_device netif_tx_stop_all_queues(netdev); phylink_stop(ess->phylink); ipqess_irq_disable(ess); diff --git a/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch b/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch index 1722a35dbe..42976357fa 100644 --- a/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch +++ b/target/linux/ipq40xx/patches-6.18/704-net-qualcomm-ipqess-enable-threaded-NAPI-by-default.patch @@ -16,7 +16,7 @@ Signed-off-by: Robert Marko --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -531,9 +531,9 @@ static irqreturn_t ipqess_interrupt_tx(i +@@ -530,9 +530,9 @@ static irqreturn_t ipqess_interrupt_tx(i struct ipqess_tx_ring *tx_ring = (struct ipqess_tx_ring *)priv; if (likely(napi_schedule_prep(&tx_ring->napi_tx))) { @@ -27,7 +27,7 @@ Signed-off-by: Robert Marko } return IRQ_HANDLED; -@@ -544,9 +544,9 @@ static irqreturn_t ipqess_interrupt_rx(i +@@ -543,9 +543,9 @@ static irqreturn_t ipqess_interrupt_rx(i struct ipqess_rx_ring *rx_ring = (struct ipqess_rx_ring *)priv; if (likely(napi_schedule_prep(&rx_ring->napi_rx))) { @@ -38,7 +38,7 @@ Signed-off-by: Robert Marko } return IRQ_HANDLED; -@@ -1266,6 +1266,8 @@ static int ipqess_axi_probe(struct platf +@@ -1296,6 +1296,8 @@ static int ipqess_axi_probe(struct platf if (err) goto err_notifier_unregister; diff --git a/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch b/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch index 535ad8987f..149208aa69 100644 --- a/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch +++ b/target/linux/ipq40xx/patches-6.18/711-net-qualcomm-ipqess-fix-TX-timeout-errors.patch @@ -35,7 +35,7 @@ Signed-off-by: Christian Marangi --- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c +++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c -@@ -454,13 +454,22 @@ static int ipqess_tx_complete(struct ipq +@@ -453,13 +453,22 @@ static int ipqess_tx_complete(struct ipq tail >>= IPQESS_TPD_CONS_IDX_SHIFT; tail &= IPQESS_TPD_CONS_IDX_MASK; From 85719fe4a38d9cdf6dbae0d27c0657713727ca4a Mon Sep 17 00:00:00 2001 From: Mieczyslaw Nalewaj Date: Thu, 16 Jul 2026 16:34:54 +0200 Subject: [PATCH 062/152] treewide: strip trailing whitespace where present Ran a treewide trailing-whitespace cleanup for *.c, *.h, *.dts and *.dtsi. Only files under target/linux/lantiq/... and target/linux/qualcommax/... required changes, so the diff is limited to those paths. Done by executing commands: find . -type f | grep "\.c$" | xargs sed -i 's/[ \t]\+$//' find . -type f | grep "\.h$" | xargs sed -i 's/[ \t]\+$//' find . -type f | grep "\.dts$" | xargs sed -i 's/[ \t]\+$//' find . -type f | grep "\.dtsi$" | xargs sed -i 's/[ \t]\+$//' Signed-off-by: Mieczyslaw Nalewaj Link: https://github.com/openwrt/openwrt/pull/24255 Signed-off-by: Jonas Jelonek --- .../files/arch/mips/pci/fixup-lantiq-pcie.c | 10 +- .../files/arch/mips/pci/ifxmips_pci_common.h | 2 +- .../lantiq/files/arch/mips/pci/ifxmips_pcie.c | 120 +++++++++--------- .../lantiq/files/arch/mips/pci/ifxmips_pcie.h | 10 +- .../files/arch/mips/pci/ifxmips_pcie_ar10.h | 10 +- .../files/arch/mips/pci/ifxmips_pcie_msi.c | 102 +++++++-------- .../files/arch/mips/pci/ifxmips_pcie_phy.c | 50 ++++---- .../files/arch/mips/pci/ifxmips_pcie_pm.c | 28 ++-- .../files/arch/mips/pci/ifxmips_pcie_pm.h | 2 +- .../files/arch/mips/pci/ifxmips_pcie_reg.h | 18 +-- .../files/arch/mips/pci/ifxmips_pcie_vr9.h | 2 +- .../lantiq/files/arch/mips/pci/pcie-lantiq.h | 24 ++-- .../qualcommax/dts/ipq5018-redmi-ax5400.dts | 2 +- .../ethernet/stmicro/stmmac/dwmac-ipq5018.c | 4 +- 14 files changed, 192 insertions(+), 192 deletions(-) diff --git a/target/linux/lantiq/files/arch/mips/pci/fixup-lantiq-pcie.c b/target/linux/lantiq/files/arch/mips/pci/fixup-lantiq-pcie.c index a8180313ea..f78cf15f61 100644 --- a/target/linux/lantiq/files/arch/mips/pci/fixup-lantiq-pcie.c +++ b/target/linux/lantiq/files/arch/mips/pci/fixup-lantiq-pcie.c @@ -2,7 +2,7 @@ ** ** FILE NAME : ifxmips_fixup_pcie.c ** PROJECT : IFX UEIP for VRX200 -** MODULES : PCIe +** MODULES : PCIe ** ** DATE : 02 Mar 2009 ** AUTHOR : Lei Chuanhua @@ -21,7 +21,7 @@ *******************************************************************************/ /*! \file ifxmips_fixup_pcie.c - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief PCIe Fixup functions source file */ #include @@ -39,11 +39,11 @@ ifx_pcie_fixup_resource(struct pci_dev *dev) IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev)); - printk("%s: fixup host controller %s (%04x:%04x)\n", - __func__, pci_name(dev), dev->vendor, dev->device); + printk("%s: fixup host controller %s (%04x:%04x)\n", + __func__, pci_name(dev), dev->vendor, dev->device); /* Setup COMMAND register */ - reg = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER /* | + reg = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER /* | PCI_COMMAND_INTX_DISABLE */| PCI_COMMAND_SERR; pci_write_config_word(dev, PCI_COMMAND, reg); IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev)); diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pci_common.h b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pci_common.h index 0d7a1a2503..adba0cfd7a 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pci_common.h +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pci_common.h @@ -24,7 +24,7 @@ #define IFXMIPS_PCI_COMMON_H #include /*! - \defgroup IFX_PCI_COM IFX PCI/PCIe common parts for OS integration + \defgroup IFX_PCI_COM IFX PCI/PCIe common parts for OS integration \brief PCI/PCIe common parts */ diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.c b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.c index da3d54d077..d19c82fcb1 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.c +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.c @@ -154,8 +154,8 @@ static inline void pcie_mem_io_setup(int pcie_port) { u32 reg; /* - * BAR[0:1] readonly register - * RC contains only minimal BARs for packets mapped to this device + * BAR[0:1] readonly register + * RC contains only minimal BARs for packets mapped to this device * Mem/IO filters defines a range of memory occupied by memory mapped IO devices that * reside on the downstream side fo the bridge. */ @@ -177,12 +177,12 @@ static inline void pcie_mem_io_setup(int pcie_port) #else /* PCIe_PBML, same as MBML */ IFX_REG_W32(IFX_REG_R32(PCIE_MBML(pcie_port)), PCIE_PMBL(pcie_port)); -#endif +#endif /* IO Address Range */ reg = SM((PCIE_IO_PHY_PORT_TO_END(pcie_port) >> 12), PCIE_IOBLSECS_IO_LIMIT_ADDR) | SM((PCIE_IO_PHY_PORT_TO_BASE(pcie_port) >> 12), PCIE_IOBLSECS_IO_BASE_ADDR); -#ifdef IFX_PCIE_IO_32BIT +#ifdef IFX_PCIE_IO_32BIT reg |= PCIE_IOBLSECS_32BIT_IO_ADDR; #endif /* IFX_PCIE_IO_32BIT */ IFX_REG_W32(reg, PCIE_IOBLSECS(pcie_port)); @@ -213,8 +213,8 @@ pcie_device_setup(int pcie_port) /* Set Maximum Read Request size for the device as a Requestor */ reg = IFX_REG_R32(PCIE_DCTLSTS(pcie_port)); - /* - * Request size can be larger than the MPS used, but the completions returned + /* + * Request size can be larger than the MPS used, but the completions returned * for the read will be bounded by the MPS size. * In our system, Max request size depends on AHB burst size. It is 64 bytes. * but we set it as 128 as minimum one. @@ -234,7 +234,7 @@ pcie_link_setup(int pcie_port) u32 reg; /* - * XXX, Link capability register, bit 18 for EP CLKREQ# dynamic clock management for L1, L2/3 CPM + * XXX, Link capability register, bit 18 for EP CLKREQ# dynamic clock management for L1, L2/3 CPM * L0s is reported during link training via TS1 order set by N_FTS */ reg = IFX_REG_R32(PCIE_LCAP(pcie_port)); @@ -249,8 +249,8 @@ pcie_link_setup(int pcie_port) reg &= ~PCIE_LCTLSTS_LINK_DISABLE; #ifdef CONFIG_PCIEASPM - /* - * We use the same physical reference clock that the platform provides on the connector + /* + * We use the same physical reference clock that the platform provides on the connector * It paved the way for ASPM to calculate the new exit Latency */ reg |= PCIE_LCTLSTS_SLOT_CLK_CFG; @@ -264,9 +264,9 @@ pcie_link_setup(int pcie_port) reg &= ~PCIE_LCTLSTS_ASPM_ENABLE; #endif /* CONFIG_PCIEASPM */ - /* - * The maximum size of any completion with data packet is bounded by the MPS setting - * in device control register + /* + * The maximum size of any completion with data packet is bounded by the MPS setting + * in device control register */ /* RCB may cause multiple split transactions, two options available, we use 64 byte RCB */ @@ -279,9 +279,9 @@ static inline void pcie_error_setup(int pcie_port) { u32 reg; - /* - * Forward ERR_COR, ERR_NONFATAL, ERR_FATAL to the backbone - * Poisoned write TLPs and completions indicating poisoned TLPs will set the PCIe_PCICMDSTS.MDPE + /* + * Forward ERR_COR, ERR_NONFATAL, ERR_FATAL to the backbone + * Poisoned write TLPs and completions indicating poisoned TLPs will set the PCIe_PCICMDSTS.MDPE */ reg = IFX_REG_R32(PCIE_INTRBCTRL(pcie_port)); reg |= PCIE_INTRBCTRL_SERR_ENABLE | PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE; @@ -469,7 +469,7 @@ static inline int pcie_app_loigc_setup(int pcie_port) } /* - * The numbers below are directly from the PCIe spec table 3-4/5. + * The numbers below are directly from the PCIe spec table 3-4/5. */ static inline void pcie_replay_time_update(int pcie_port) { @@ -550,7 +550,7 @@ static int pcie_valid_config(int pcie_port, int bus, int dev) } /* Bus zero only has RC itself - * XXX, check if EP will be integrated + * XXX, check if EP will be integrated */ if ((bus == 0) && (dev != 0)) { return 0; @@ -561,8 +561,8 @@ static int pcie_valid_config(int pcie_port, int bus, int dev) return 0; } - /* - * PCIe is PtP link, one bus only supports only one device + /* + * PCIe is PtP link, one bus only supports only one device * except bus zero and PCIe switch which is virtual bus device * The following two conditions really depends on the system design * and attached the device. @@ -668,29 +668,29 @@ static int ifx_pcie_read_config(struct pci_bus *bus, u32 devfn, goto out; } - /* - * If we are second controller, we have to cheat OS so that it assume + /* + * If we are second controller, we have to cheat OS so that it assume * its bus number starts from 0 in host controller */ bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port); - /* - * We need to force the bus number to be zero on the root - * bus. Linux numbers the 2nd root bus to start after all - * busses on root 0. - */ + /* + * We need to force the bus number to be zero on the root + * bus. Linux numbers the 2nd root bus to start after all + * busses on root 0. + */ if (bus->parent == NULL) { - bus_number = 0; + bus_number = 0; } - /* - * PCIe only has a single device connected to it. It is - * always device ID 0. Don't bother doing reads for other - * device IDs on the first segment. - */ + /* + * PCIe only has a single device connected to it. It is + * always device ID 0. Don't bother doing reads for other + * device IDs on the first segment. + */ if ((bus_number == 0) && (PCI_SLOT(devfn) != 0)) { ret = PCIBIOS_FUNC_NOT_SUPPORTED; - goto out; + goto out; } if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) { @@ -761,19 +761,19 @@ static int ifx_pcie_write_config(struct pci_bus *bus, u32 devfn, ret = PCIBIOS_BAD_REGISTER_NUMBER; goto out; } - /* - * If we are second controller, we have to cheat OS so that it assume + /* + * If we are second controller, we have to cheat OS so that it assume * its bus number starts from 0 in host controller */ bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port); - /* - * We need to force the bus number to be zero on the root - * bus. Linux numbers the 2nd root bus to start after all - * busses on root 0. - */ + /* + * We need to force the bus number to be zero on the root + * bus. Linux numbers the 2nd root bus to start after all + * busses on root 0. + */ if (bus->parent == NULL) { - bus_number = 0; + bus_number = 0; } if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) { @@ -784,7 +784,7 @@ static int ifx_pcie_write_config(struct pci_bus *bus, u32 devfn, /* XXX, some PCIe device may need some delay */ PCIE_IRQ_LOCK(ifx_pcie_lock); - /* + /* * To configure the correct bus topology using native way, we have to cheat Os so that * it can configure the PCIe hardware correctly. */ @@ -911,12 +911,12 @@ int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) { u16 config; #ifdef IFX_PCIE_ERROR_INT - u32 dconfig; + u32 dconfig; int pos; #endif - /* Enable reporting System errors and parity errors on all devices */ - /* Enable parity checking and error reporting */ + /* Enable reporting System errors and parity errors on all devices */ + /* Enable parity checking and error reporting */ pci_read_config_word(dev, PCI_COMMAND, &config); config |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR /*| PCI_COMMAND_INVALIDATE | PCI_COMMAND_FAST_BACK*/; @@ -944,7 +944,7 @@ int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &config); pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, config); - /* Update Device Control */ + /* Update Device Control */ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &config); /* Correctable Error Reporting */ config |= PCI_EXP_DEVCTL_CERE; @@ -960,17 +960,17 @@ int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) /* Find the Advanced Error Reporting capability */ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (pos) { - /* Clear Uncorrectable Error Status */ + /* Clear Uncorrectable Error Status */ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &dconfig); pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, dconfig); /* Enable reporting of all uncorrectable errors */ /* Uncorrectable Error Mask - turned on bits disable errors */ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, 0); - /* - * Leave severity at HW default. This only controls if - * errors are reported as uncorrectable or - * correctable, not if the error is reported. - */ + /* + * Leave severity at HW default. This only controls if + * errors are reported as uncorrectable or + * correctable, not if the error is reported. + */ /* PCI_ERR_UNCOR_SEVER - Uncorrectable Error Severity */ /* Clear Correctable Error Status */ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &dconfig); @@ -978,7 +978,7 @@ int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) /* Enable reporting of all correctable errors */ /* Correctable Error Mask - turned on bits disable errors */ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, 0); - /* Advanced Error Capabilities */ + /* Advanced Error Capabilities */ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &dconfig); /* ECRC Generation Enable */ if (dconfig & PCI_ERR_CAP_ECRC_GENC) { @@ -995,7 +995,7 @@ int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, PCI_ERR_ROOT_CMD_COR_EN | PCI_ERR_ROOT_CMD_NONFATAL_EN | - PCI_ERR_ROOT_CMD_FATAL_EN); + PCI_ERR_ROOT_CMD_FATAL_EN); /* Clear the Root status register */ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &dconfig); pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, dconfig); @@ -1032,9 +1032,9 @@ pcie_rc_initialize(int pcie_port) pcie_ep_gpio_rst_init(pcie_port); - /* - * XXX, PCIe elastic buffer bug will cause not to be detected. One more - * reset PCIe PHY will solve this issue + /* + * XXX, PCIe elastic buffer bug will cause not to be detected. One more + * reset PCIe PHY will solve this issue */ for (i = 0; i < IFX_PCIE_PHY_LOOP_CNT; i++) { ret = phy_init(ltq_pcie_phy); @@ -1149,7 +1149,7 @@ static int ifx_pcie_bios_probe(struct platform_device *pdev) for (pcie_port = startup_port; pcie_port < IFX_PCIE_CORE_NR; pcie_port++){ if (pcie_rc_initialize(pcie_port) == 0) { - IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: ifx_pcie_cfg_base 0x%p\n", + IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: ifx_pcie_cfg_base 0x%p\n", __func__, PCIE_CFG_PORT_TO_BASE(pcie_port)); /* Otherwise, warning will pop up */ io_map_base = ioremap(PCIE_IO_PHY_PORT_TO_BASE(pcie_port), PCIE_IO_SIZE); @@ -1174,8 +1174,8 @@ static int ifx_pcie_bios_probe(struct platform_device *pdev) register_pci_controller(&ifx_pcie_controller[pcie_port].pcic); /* XXX, clear error status */ - IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: mem_resource 0x%p, io_resource 0x%p\n", - __func__, &ifx_pcie_controller[pcie_port].pcic.mem_resource, + IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: mem_resource 0x%p, io_resource 0x%p\n", + __func__, &ifx_pcie_controller[pcie_port].pcic.mem_resource, &ifx_pcie_controller[pcie_port].pcic.io_resource); #ifdef IFX_PCIE_ERROR_INT diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.h b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.h index cd7bc32a55..345fc435a7 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.h +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie.h @@ -29,8 +29,8 @@ #include "ifxmips_pcie_reg.h" /*! - \defgroup IFX_PCIE PCI Express bus driver module - \brief PCI Express IP module support VRX200 + \defgroup IFX_PCIE PCI Express bus driver module + \brief PCI Express IP module support VRX200 */ /*! @@ -41,7 +41,7 @@ /*! \file ifxmips_pcie.h - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief header file for PCIe module common header file */ #define PCIE_IRQ_LOCK(lock) do { \ @@ -81,7 +81,7 @@ ifx_pcie_debug((_fmt), ##args); \ } while (0) -#define INLINE +#define INLINE #else #define IFX_PCIE_PRINT(_m, _fmt, args...) \ do {} while(0) @@ -90,7 +90,7 @@ struct ifx_pci_controller { struct pci_controller pcic; - + /* RC specific, per host bus information */ u32 port; /* Port index, 0 -- 1st core, 1 -- 2nd core */ }; diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_ar10.h b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_ar10.h index dfd30a4270..d1e23ace4d 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_ar10.h +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_ar10.h @@ -31,14 +31,14 @@ static inline void pcie_ep_gpio_rst_init(int pcie_port) { ifx_ebu_led_enable(); if (pcie_port == 0) { - ifx_ebu_led_set_data(11, 1); + ifx_ebu_led_set_data(11, 1); } else { - ifx_ebu_led_set_data(12, 1); + ifx_ebu_led_set_data(12, 1); } } -static inline void pcie_ahb_pmu_setup(void) +static inline void pcie_ahb_pmu_setup(void) { /* XXX, moved to CGU to control AHBM */ } @@ -219,7 +219,7 @@ static inline void pcie_core_pmu_setup(int pcie_port) PCIE0_CTRL_PMU_SETUP(IFX_PMU_ENABLE); } else { - PCIE1_CTRL_PMU_SETUP(IFX_PMU_ENABLE); + PCIE1_CTRL_PMU_SETUP(IFX_PMU_ENABLE); } } @@ -243,7 +243,7 @@ ifx_pcie_bus_nr_deduct(u32 bus_number, int pcie_port) if (pcie_port == IFX_PCIE_PORT1) { /* Port 1 must check if there are two cores enabled */ if (pcibios_host_nr() > 1) { tbus_number -= pcibios_1st_host_bus_nr(); - } + } } #endif /* CONFIG_IFX_PCI */ return tbus_number; diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_msi.c b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_msi.c index de4031db4e..8db77a5334 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_msi.c +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_msi.c @@ -27,7 +27,7 @@ /*! \file ifxmips_pcie_msi.c - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief PCIe MSI OS interface file */ @@ -79,15 +79,15 @@ typedef struct ifx_msi_irq { const u32 msi_phy_base; const ifx_msi_irq_idx_t msi_irq_idx[IFX_MSI_IRQ_NUM]; /* - * Each bit in msi_free_irq_bitmask represents a MSI interrupt that is + * Each bit in msi_free_irq_bitmask represents a MSI interrupt that is * in use. */ u16 msi_free_irq_bitmask; /* - * Each bit in msi_multiple_irq_bitmask tells that the device using - * this bit in msi_free_irq_bitmask is also using the next bit. This - * is used so we can disable all of the MSI interrupts when a device + * Each bit in msi_multiple_irq_bitmask tells that the device using + * this bit in msi_free_irq_bitmask is also using the next bit. This + * is used so we can disable all of the MSI interrupts when a device * uses multiple. */ u16 msi_multiple_irq_bitmask; @@ -131,10 +131,10 @@ static ifx_msi_irq_t msi_irqs[IFX_PCIE_CORE_NR] = { #endif /* CONFIG_IFX_PCIE_2ND_CORE */ }; -/* - * This lock controls updates to msi_free_irq_bitmask, +/* + * This lock controls updates to msi_free_irq_bitmask, * msi_multiple_irq_bitmask and pic register settting - */ + */ static DEFINE_SPINLOCK(ifx_pcie_msi_lock); void pcie_msi_pic_init(int pcie_port) @@ -144,22 +144,22 @@ void pcie_msi_pic_init(int pcie_port) spin_unlock(&ifx_pcie_msi_lock); } -/** +/** * \fn int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) - * \brief Called when a driver request MSI interrupts instead of the - * legacy INT A-D. This routine will allocate multiple interrupts - * for MSI devices that support them. A device can override this by - * programming the MSI control bits [6:4] before calling - * pci_enable_msi(). - * - * \param[in] pdev Device requesting MSI interrupts - * \param[in] desc MSI descriptor - * + * \brief Called when a driver request MSI interrupts instead of the + * legacy INT A-D. This routine will allocate multiple interrupts + * for MSI devices that support them. A device can override this by + * programming the MSI control bits [6:4] before calling + * pci_enable_msi(). + * + * \param[in] pdev Device requesting MSI interrupts + * \param[in] desc MSI descriptor + * * \return -EINVAL Invalid pcie root port or invalid msi bit * \return 0 OK * \ingroup IFX_PCIE_MSI */ -int +int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) { int irq, pos; @@ -182,27 +182,27 @@ arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) } /* - * Read the MSI config to figure out how many IRQs this device - * wants. Most devices only want 1, which will give - * configured_private_bits and request_private_bits equal 0. + * Read the MSI config to figure out how many IRQs this device + * wants. Most devices only want 1, which will give + * configured_private_bits and request_private_bits equal 0. */ pci_read_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &control); /* - * If the number of private bits has been configured then use - * that value instead of the requested number. This gives the - * driver the chance to override the number of interrupts - * before calling pci_enable_msi(). + * If the number of private bits has been configured then use + * that value instead of the requested number. This gives the + * driver the chance to override the number of interrupts + * before calling pci_enable_msi(). */ - configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4; + configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4; if (configured_private_bits == 0) { /* Nothing is configured, so use the hardware requested size */ request_private_bits = (control & PCI_MSI_FLAGS_QMASK) >> 1; } else { /* - * Use the number of configured bits, assuming the - * driver wanted to override the hardware request + * Use the number of configured bits, assuming the + * driver wanted to override the hardware request * value. */ request_private_bits = configured_private_bits; @@ -226,21 +226,21 @@ again: search_mask = (1 << irq_step) - 1; /* - * We're going to search msi_free_irq_bitmask_lock for zero - * bits. This represents an MSI interrupt number that isn't in + * We're going to search msi_free_irq_bitmask_lock for zero + * bits. This represents an MSI interrupt number that isn't in * use. */ spin_lock(&ifx_pcie_msi_lock); for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos += irq_step) { if ((msi_irqs[pcie_port].msi_free_irq_bitmask & (search_mask << pos)) == 0) { - msi_irqs[pcie_port].msi_free_irq_bitmask |= search_mask << pos; + msi_irqs[pcie_port].msi_free_irq_bitmask |= search_mask << pos; msi_irqs[pcie_port].msi_multiple_irq_bitmask |= (search_mask >> 1) << pos; - break; + break; } } - spin_unlock(&ifx_pcie_msi_lock); + spin_unlock(&ifx_pcie_msi_lock); - /* Make sure the search for available interrupts didn't fail */ + /* Make sure the search for available interrupts didn't fail */ if (pos >= IFX_MSI_IRQ_NUM) { if (request_private_bits) { IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s: Unable to find %d free " @@ -252,17 +252,17 @@ again: printk(KERN_ERR "%s: Unable to find a free MSI interrupt\n", __func__); return -EINVAL; } - } + } irq = msi_irqs[pcie_port].msi_irq_idx[pos].irq; irq_idx = msi_irqs[pcie_port].msi_irq_idx[pos].idx; IFX_PCIE_PRINT(PCIE_MSG_MSI, "pos %d, irq %d irq_idx %d\n", pos, irq, irq_idx); /* - * Initialize MSI. This has to match the memory-write endianess from the device + * Initialize MSI. This has to match the memory-write endianess from the device * Address bits [23:12] */ - spin_lock(&ifx_pcie_msi_lock); + spin_lock(&ifx_pcie_msi_lock); msi_irqs[pcie_port].msi_pic_p->pic_table[pos] = SM(irq_idx, IFX_MSI_PIC_INT_LINE) | SM((msi_irqs[pcie_port].msi_phy_base >> 12), IFX_MSI_PIC_MSG_ADDR) | SM((1 << pos), IFX_MSI_PIC_MSG_DATA); @@ -306,27 +306,27 @@ pcie_msi_irq_to_port(unsigned int irq, int *port) } #endif /* CONFIG_IFX_PCIE_2ND_CORE */ else { - printk(KERN_ERR "%s: Attempted to teardown illegal " + printk(KERN_ERR "%s: Attempted to teardown illegal " "MSI interrupt (%d)\n", __func__, irq); ret = -EINVAL; } return ret; } -/** +/** * \fn void arch_teardown_msi_irq(unsigned int irq) - * \brief Called when a device no longer needs its MSI interrupts. All - * MSI interrupts for the device are freed. - * + * \brief Called when a device no longer needs its MSI interrupts. All + * MSI interrupts for the device are freed. + * * \param irq The devices first irq number. There may be multple in sequence. * \return none * \ingroup IFX_PCIE_MSI */ -void +void arch_teardown_msi_irq(unsigned int irq) { int pos; - int number_irqs; + int number_irqs; u16 bitmask; int pcie_port; @@ -338,11 +338,11 @@ arch_teardown_msi_irq(unsigned int irq) return; } - /* Shift the mask to the correct bit location, not always correct + /* Shift the mask to the correct bit location, not always correct * Probally, the first match will be chosen. */ for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos++) { - if ((msi_irqs[pcie_port].msi_irq_idx[pos].irq == irq) + if ((msi_irqs[pcie_port].msi_irq_idx[pos].irq == irq) && (msi_irqs[pcie_port].msi_free_irq_bitmask & ( 1 << pos))) { break; } @@ -355,14 +355,14 @@ arch_teardown_msi_irq(unsigned int irq) /* Disable this entry */ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] |= IFX_MSI_PCI_INT_DISABLE; msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~(IFX_MSI_PIC_INT_LINE | IFX_MSI_PIC_MSG_ADDR | IFX_MSI_PIC_MSG_DATA); - spin_unlock(&ifx_pcie_msi_lock); + spin_unlock(&ifx_pcie_msi_lock); /* * Count the number of IRQs we need to free by looking at the * msi_multiple_irq_bitmask. Each bit set means that the next * IRQ is also owned by this device. - */ - number_irqs = 0; - while (((pos + number_irqs) < IFX_MSI_IRQ_NUM) && + */ + number_irqs = 0; + while (((pos + number_irqs) < IFX_MSI_IRQ_NUM) && (msi_irqs[pcie_port].msi_multiple_irq_bitmask & (1 << (pos + number_irqs)))) { number_irqs++; } diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_phy.c b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_phy.c index f5b0f13576..9f7535aeba 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_phy.c +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_phy.c @@ -21,7 +21,7 @@ *******************************************************************************/ /*! \file ifxmips_pcie_phy.c - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief PCIe PHY PLL register programming source file */ #include @@ -36,7 +36,7 @@ #define IFX_PCIE_PHY_REG_WRITE16(__addr, __data) \ ((*(volatile u16 *) (__addr)) = (__data)) - + #define IFX_PCIE_PHY_REG_READ16(__addr) \ (*(volatile u16 *) (__addr)) @@ -57,7 +57,7 @@ #ifdef IFX_PCI_PHY_REG_DUMP static void -pcie_phy_reg_dump(int pcie_port) +pcie_phy_reg_dump(int pcie_port) { printk("PLL REGFILE\n"); printk("PCIE_PHY_PLL_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL1(pcie_port))); @@ -141,8 +141,8 @@ pcie_phy_comm_setup(int pcie_port) } #ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE -static void -pcie_phy_36mhz_mode_setup(int pcie_port) +static void +pcie_phy_36mhz_mode_setup(int pcie_port) { IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); #ifdef IFX_PCI_PHY_REG_DUMP @@ -185,8 +185,8 @@ pcie_phy_36mhz_mode_setup(int pcie_port) #endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */ #ifdef CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE -static void -pcie_phy_36mhz_ssc_mode_setup(int pcie_port) +static void +pcie_phy_36mhz_ssc_mode_setup(int pcie_port) { IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); #ifdef IFX_PCI_PHY_REG_DUMP @@ -260,14 +260,14 @@ pcie_phy_36mhz_ssc_mode_setup(int pcie_port) /* reduced CDR BW to avoid glitches */ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF); - + IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); } #endif /* CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE */ #ifdef CONFIG_IFX_PCIE_PHY_25MHZ_MODE -static void -pcie_phy_25mhz_mode_setup(int pcie_port) +static void +pcie_phy_25mhz_mode_setup(int pcie_port) { IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); #ifdef IFX_PCI_PHY_REG_DUMP @@ -277,7 +277,7 @@ pcie_phy_25mhz_mode_setup(int pcie_port) /* en_const_sdm */ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); - /* pll_ensdm */ + /* pll_ensdm */ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0200); /* en_ext_mmd_div_ratio*/ @@ -297,14 +297,14 @@ pcie_phy_25mhz_mode_setup(int pcie_port) #endif /* CONFIG_IFX_PCIE_PHY_25MHZ_MODE */ #ifdef CONFIG_IFX_PCIE_PHY_100MHZ_MODE -static void -pcie_phy_100mhz_mode_setup(int pcie_port) +static void +pcie_phy_100mhz_mode_setup(int pcie_port) { IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); #ifdef IFX_PCI_PHY_REG_DUMP IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); pcie_phy_reg_dump(pcie_port); -#endif +#endif /* en_ext_mmd_div_ratio */ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); @@ -357,8 +357,8 @@ pcie_phy_wait_startup_ready(int pcie_port) return 0; } -static void -pcie_phy_load_enable(int pcie_port, int slice) +static void +pcie_phy_load_enable(int pcie_port, int slice) { /* Set the load_en of tx/rx slice to '1' */ switch (slice) { @@ -374,10 +374,10 @@ pcie_phy_load_enable(int pcie_port, int slice) } } -static void -pcie_phy_load_disable(int pcie_port, int slice) -{ - /* set the load_en of tx/rx slice to '0' */ +static void +pcie_phy_load_disable(int pcie_port, int slice) +{ + /* set the load_en of tx/rx slice to '0' */ switch (slice) { case 1: IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0000, 0x0010); @@ -385,13 +385,13 @@ pcie_phy_load_disable(int pcie_port, int slice) case 2: IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0000, 0x0010); break; - case 3: + case 3: IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0000, 0x0002); break; } } -static void +static void pcie_phy_load_war(int pcie_port) { int slice; @@ -403,7 +403,7 @@ pcie_phy_load_war(int pcie_port) } } -static void +static void pcie_phy_tx2_modulation(int pcie_port) { IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD1(pcie_port), 0x1FFE, 0xFFFF); @@ -413,7 +413,7 @@ pcie_phy_tx2_modulation(int pcie_port) IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0001, 0xFFFF); } -static void +static void pcie_phy_tx1_modulation(int pcie_port) { IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD1(pcie_port), 0x1FFE, 0xFFFF); @@ -428,7 +428,7 @@ pcie_phy_tx_modulation_war(int pcie_port) { int i; -#define PCIE_PHY_MODULATION_NUM 5 +#define PCIE_PHY_MODULATION_NUM 5 for (i = 0; i < PCIE_PHY_MODULATION_NUM; i++) { pcie_phy_tx2_modulation(pcie_port); pcie_phy_tx1_modulation(pcie_port); diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.c b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.c index a10ecad4b4..edc1df67b3 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.c +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.c @@ -28,7 +28,7 @@ /*! \file ifxmips_pcie_pm.c - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief source file for PCIE Root Complex Driver Power Management */ @@ -51,7 +51,7 @@ #include #include "ifxmips_pcie_pm.h" -/** +/** * \fn static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) * \brief the callback function to request pmcu state in the power management hardware-dependent module * @@ -62,10 +62,10 @@ * \return IFX_PMCU_RETURN_DENIED Not allowed to operate power state * \ingroup IFX_PCIE_PM */ -static IFX_PMCU_RETURN_t +static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) { - switch(pmcuState) + switch(pmcuState) { case IFX_PMCU_STATE_D0: return IFX_PMCU_RETURN_SUCCESS; @@ -80,7 +80,7 @@ ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) } } -/** +/** * \fn static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) * \brief the callback function to get pmcu state in the power management hardware-dependent module @@ -91,7 +91,7 @@ ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) * \return IFX_PMCU_RETURN_DENIED Not allowed to operate power state * \ingroup IFX_PCIE_PM */ -static IFX_PMCU_RETURN_t +static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) { return IFX_PMCU_RETURN_SUCCESS; @@ -100,7 +100,7 @@ ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) /** * \fn IFX_PMCU_RETURN_t ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) * \brief Apply all callbacks registered to be executed before a state change for pmcuModule - * + * * \param pmcuModule Module * \param newState New state * \param oldState Old state @@ -108,7 +108,7 @@ ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) * \return IFX_PMCU_RETURN_ERROR Failed to set power state. * \ingroup IFX_PCIE_PM */ -static IFX_PMCU_RETURN_t +static IFX_PMCU_RETURN_t ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) { return IFX_PMCU_RETURN_SUCCESS; @@ -117,7 +117,7 @@ ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, /** * \fn IFX_PMCU_RETURN_t ifx_pcie_pmcu_postchange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) * \brief Apply all callbacks registered to be executed before a state change for pmcuModule - * + * * \param pmcuModule Module * \param newState New state * \param oldState Old state @@ -125,13 +125,13 @@ ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, * \return IFX_PMCU_RETURN_ERROR Failed to set power state. * \ingroup IFX_PCIE_PM */ -static IFX_PMCU_RETURN_t +static IFX_PMCU_RETURN_t ifx_pcie_pmcu_postchange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) { return IFX_PMCU_RETURN_SUCCESS; } -/** +/** * \fn static void ifx_pcie_pmcu_init(void) * \brief Register with central PMCU module * \return none @@ -152,10 +152,10 @@ ifx_pcie_pmcu_init(void) pmcuRegister.ifx_pmcu_state_get = ifx_pcie_pmcu_state_get; pmcuRegister.pre = ifx_pcie_pmcu_prechange; pmcuRegister.post= ifx_pcie_pmcu_postchange; - ifx_pmcu_register(&pmcuRegister); + ifx_pmcu_register(&pmcuRegister); } -/** +/** * \fn static void ifx_pcie_pmcu_exit(void) * \brief Unregister with central PMCU module * @@ -168,7 +168,7 @@ ifx_pcie_pmcu_exit(void) IFX_PMCU_REGISTER_t pmcuUnRegister; /* XXX, hook driver context */ - + pmcuUnRegister.pmcuModule = IFX_PMCU_MODULE_PCIE; pmcuUnRegister.pmcuModuleNr = 0; ifx_pmcu_unregister(&pmcuUnRegister); diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.h b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.h index 6ece20df05..5a855bb4ed 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.h +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_pm.h @@ -22,7 +22,7 @@ *******************************************************************************/ /*! \file ifxmips_pcie_pm.h - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief header file for PCIe Root Complex Driver Power Management */ diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_reg.h b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_reg.h index e7e4b6ca6f..6a15c2bd8d 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_reg.h +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_reg.h @@ -23,7 +23,7 @@ #define IFXMIPS_PCIE_REG_H /*! \file ifxmips_pcie_reg.h - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief header file for PCIe module register definition */ /* PCIe Address Mapping Base */ @@ -246,7 +246,7 @@ enum { /* AHB Control Register, fixed bus enumeration exception */ #define PCIE_AHB_CTRL(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x78) -#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 +#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 /* Interrupt Enalbe Register */ #define PCIE_IRNEN(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF4) @@ -692,7 +692,7 @@ enum { #define PCIE_PVC2_VC_ARB_TAB_OFFSET 0xFF000000 /* VC arbitration table offset, not support */ #define PCIE_PVC2_VC_ARB_TAB_OFFSET_S 24 -/* Port VC Control and Status Register */ +/* Port VC Control and Status Register */ #define PCIE_PVCCRSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14C) #define PCIE_PVCCRSR_LOAD_VC_ARB_TAB 0x00000001 /* Load VC Arbitration Table */ #define PCIE_PVCCRSR_VC_ARB_SEL 0x0000000E /* VC Arbitration Select */ @@ -780,7 +780,7 @@ enum { /* Port Link Control Register */ #define PCIE_PLCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x710) #define PCIE_PLCR_OTHER_MSG_REQ 0x00000001 /* Other Message Request */ -#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ +#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ #define PCIE_PLCR_LOOPBACK_EN 0x00000004 /* Loopback Enable */ #define PCIE_PLCR_LTSSM_HOT_RST 0x00000008 /* Force LTSSM to the hot reset */ #define PCIE_PLCR_DLL_LINK_EN 0x00000020 /* Enable Link initialization */ @@ -894,7 +894,7 @@ enum { #define PCIE_VC0_PRQCR_P_HDR_CREDITS_S 12 #define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE 0x00E00000 /* VC0 Posted TLP Queue Mode */ #define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE_S 20 -#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ +#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ #define PCIE_VC0_PRQCR_VC_STRICT_ORDER 0x80000000 /* VC0 Ordering for Receive Queues */ /* VC0 Non-Posted Receive Queue Control */ @@ -949,9 +949,9 @@ enum { /* PHY Control Register, all zeros in VR9 */ #define PCIE_PHYCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x814) -/* - * PCIe PDI PHY register definition, suppose all the following - * stuff is confidential. +/* + * PCIe PDI PHY register definition, suppose all the following + * stuff is confidential. * XXX, detailed bit definition */ #define PCIE_PHY_PLL_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x22 << 1)) @@ -965,7 +965,7 @@ enum { #define PCIE_PHY_PLL_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2A << 1)) #define PCIE_PHY_PLL_A_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2B << 1)) #define PCIE_PHY_PLL_STATUS(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2C << 1)) - + #define PCIE_PHY_TX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x30 << 1)) #define PCIE_PHY_TX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x31 << 1)) #define PCIE_PHY_TX1_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x32 << 1)) diff --git a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_vr9.h b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_vr9.h index 1d185e3518..dd760e90e4 100644 --- a/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_vr9.h +++ b/target/linux/lantiq/files/arch/mips/pci/ifxmips_pcie_vr9.h @@ -52,7 +52,7 @@ #define OUT ((volatile u32*)(IFX_GPIO + 0x0070)) -static inline void pcie_ahb_pmu_setup(void) +static inline void pcie_ahb_pmu_setup(void) { /* Enable AHB bus master/slave */ struct clk *clk; diff --git a/target/linux/lantiq/files/arch/mips/pci/pcie-lantiq.h b/target/linux/lantiq/files/arch/mips/pci/pcie-lantiq.h index bee4168354..94030e2519 100644 --- a/target/linux/lantiq/files/arch/mips/pci/pcie-lantiq.h +++ b/target/linux/lantiq/files/arch/mips/pci/pcie-lantiq.h @@ -27,7 +27,7 @@ #include /*! \file ifxmips_pcie_reg.h - \ingroup IFX_PCIE + \ingroup IFX_PCIE \brief header file for PCIe module register definition */ /* PCIe Address Mapping Base */ @@ -250,7 +250,7 @@ enum { /* AHB Control Register, fixed bus enumeration exception */ #define PCIE_AHB_CTRL(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x78) -#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 +#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 /* Interrupt Enalbe Register */ #define PCIE_IRNEN(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF4) @@ -696,7 +696,7 @@ enum { #define PCIE_PVC2_VC_ARB_TAB_OFFSET 0xFF000000 /* VC arbitration table offset, not support */ #define PCIE_PVC2_VC_ARB_TAB_OFFSET_S 24 -/* Port VC Control and Status Register */ +/* Port VC Control and Status Register */ #define PCIE_PVCCRSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14C) #define PCIE_PVCCRSR_LOAD_VC_ARB_TAB 0x00000001 /* Load VC Arbitration Table */ #define PCIE_PVCCRSR_VC_ARB_SEL 0x0000000E /* VC Arbitration Select */ @@ -784,7 +784,7 @@ enum { /* Port Link Control Register */ #define PCIE_PLCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x710) #define PCIE_PLCR_OTHER_MSG_REQ 0x00000001 /* Other Message Request */ -#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ +#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ #define PCIE_PLCR_LOOPBACK_EN 0x00000004 /* Loopback Enable */ #define PCIE_PLCR_LTSSM_HOT_RST 0x00000008 /* Force LTSSM to the hot reset */ #define PCIE_PLCR_DLL_LINK_EN 0x00000020 /* Enable Link initialization */ @@ -898,7 +898,7 @@ enum { #define PCIE_VC0_PRQCR_P_HDR_CREDITS_S 12 #define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE 0x00E00000 /* VC0 Posted TLP Queue Mode */ #define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE_S 20 -#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ +#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ #define PCIE_VC0_PRQCR_VC_STRICT_ORDER 0x80000000 /* VC0 Ordering for Receive Queues */ /* VC0 Non-Posted Receive Queue Control */ @@ -953,9 +953,9 @@ enum { /* PHY Control Register, all zeros in VR9 */ #define PCIE_PHYCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x814) -/* - * PCIe PDI PHY register definition, suppose all the following - * stuff is confidential. +/* + * PCIe PDI PHY register definition, suppose all the following + * stuff is confidential. * XXX, detailed bit definition */ #define PCIE_PHY_PLL_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x22 << 1)) @@ -969,7 +969,7 @@ enum { #define PCIE_PHY_PLL_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2A << 1)) #define PCIE_PHY_PLL_A_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2B << 1)) #define PCIE_PHY_PLL_STATUS(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2C << 1)) - + #define PCIE_PHY_TX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x30 << 1)) #define PCIE_PHY_TX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x31 << 1)) #define PCIE_PHY_TX1_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x32 << 1)) @@ -1040,7 +1040,7 @@ enum { } \ } while (0) -#define INLINE +#define INLINE #else #define IFX_PCIE_PRINT(_m, _fmt, args...) \ do {} while(0) @@ -1049,7 +1049,7 @@ enum { struct ifx_pci_controller { struct pci_controller pcic; - + /* RC specific, per host bus information */ u32 port; /* Port index, 0 -- 1st core, 1 -- 2nd core */ }; @@ -1127,7 +1127,7 @@ static inline void pcie_rcu_endian_setup(int pcie_port) reg |= IFX_RCU_AHB_BE_PCIE_M; reg |= IFX_RCU_AHB_BE_PCIE_S; reg &= ~IFX_RCU_AHB_BE_XBAR_M; -#else +#else reg |= IFX_RCU_AHB_BE_PCIE_M; reg &= ~IFX_RCU_AHB_BE_PCIE_S; reg &= ~IFX_RCU_AHB_BE_XBAR_M; diff --git a/target/linux/qualcommax/dts/ipq5018-redmi-ax5400.dts b/target/linux/qualcommax/dts/ipq5018-redmi-ax5400.dts index cc9c700bea..218a7395e7 100644 --- a/target/linux/qualcommax/dts/ipq5018-redmi-ax5400.dts +++ b/target/linux/qualcommax/dts/ipq5018-redmi-ax5400.dts @@ -285,7 +285,7 @@ status = "okay"; assigned-clocks = <&uniphy0 UNIPHY_CLK_REF>; - assigned-clock-rates = ; + assigned-clock-rates = ; }; // MAC0 -> GE Phy diff --git a/target/linux/qualcommax/files/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq5018.c b/target/linux/qualcommax/files/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq5018.c index 78f22e1c8b..0caabd9351 100644 --- a/target/linux/qualcommax/files/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq5018.c +++ b/target/linux/qualcommax/files/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq5018.c @@ -113,7 +113,7 @@ static void ipq5018_gmac_get_interfaces(struct stmmac_priv *priv, void *bsp_priv if (priv->hw->phylink_pcs) { __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); - /* + /* * Synopsys DWMAC IP version 3.7 is limited to 1 Gpbs. * This vendor specific implementation supports 2.5 Gbps, so override * the default mac link capabilities. @@ -156,7 +156,7 @@ static int ipq5018_gmac_probe(struct platform_device *pdev) if (IS_ERR(gmac->tx_clk)) return dev_err_probe(dev, PTR_ERR(gmac->tx_clk), "failed to get TX clock\n"); - + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(ipq5018_gmac_clks), ipq5018_gmac_clks); if (ret) From 4c85dfa73ed5b50316c06e18d379307309a5aa41 Mon Sep 17 00:00:00 2001 From: Gleb Pesin Date: Wed, 15 Jul 2026 19:12:33 +0000 Subject: [PATCH 063/152] ramips: mt7620: fix jumbo frame mode MAX_RX_PKT_LEN value 2 selects 1552-byte frames, not jumbo mode. Select value 3 so the configured 2 KiB frames are actually accepted by the switch. Tested on a ZBT-WE826 (MT7620). With value 2, 1534-byte IPv4 packets pass while 1535-byte packets are dropped. With value 3, 1792-byte and 1800-byte DF IPv4 packets pass. Fixes: d083212e1105 ("ramips: ethernet: ralink: mt7620 enable jumbo frames") Signed-off-by: Gleb Pesin Link: https://github.com/openwrt/openwrt/pull/24242 Signed-off-by: Jonas Jelonek --- .../files/drivers/net/ethernet/ralink/gsw_mt7620.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/gsw_mt7620.h b/target/linux/ramips/files/drivers/net/ethernet/ralink/gsw_mt7620.h index 4252bc22a6..0c534e8c26 100644 --- a/target/linux/ramips/files/drivers/net/ethernet/ralink/gsw_mt7620.h +++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/gsw_mt7620.h @@ -56,9 +56,14 @@ #define GSW_REG_GMACCR 0x3FE0 /* correct address per MT7620A datasheet */ #define GMACCR_JMB_LEN_MASK 0x0F #define GMACCR_JMB_LEN_SHIFT 2 -// MAX_RX_PKT_LEN field (bits 0:1): 0=1518, 1=1536, 2=jumbo +/* MAX_RX_PKT_LEN (bits [1:0]): + * 0: 1518 (untagged) / 1522 (tagged) + * 1: 1536 + * 2: 1552 + * 3: jumbo frame mode + */ #define GMACCR_MAX_RX_PKT_LEN_MASK 0x3 -#define GMACCR_MAX_RX_PKT_LEN_JUMBO 0x2 +#define GMACCR_MAX_RX_PKT_LEN_JUMBO 0x3 #define SYSC_REG_CHIP_REV_ID 0x0c #define SYSC_REG_CFG1 0x14 From 7085d165524f8f1623cd88327725073c25610bad Mon Sep 17 00:00:00 2001 From: Maxim Anisimov Date: Mon, 29 Jun 2026 16:14:20 +0300 Subject: [PATCH 064/152] mediatek: fix nmbm max-reserved-blocks for Keenetic/Netcraze devices The devicetree set mediatek,bmt-max-reserved-blocks to 64, but the factory firmware uses 256. With the lower value NMBM fails to locate its signature on some boards, and the device does not boot. Restore the factory value of 256 to match stock firmware and fix boot on affected units. Vendor nmbm max blocks value: https://github.com/keenetic/kernel-49/blob/master/drivers/mtd/nmbm/nmbm-mtd.c#L31 Signed-off-by: Maxim Anisimov Link: https://github.com/openwrt/openwrt/pull/24001 Signed-off-by: Jonas Jelonek --- target/linux/mediatek/dts/mt7981b-keenetic-kap-630.dtsi | 2 +- target/linux/mediatek/dts/mt7981b-keenetic-kn-3711.dts | 2 +- target/linux/mediatek/dts/mt7981b-keenetic-kn-3811.dts | 2 +- target/linux/mediatek/dts/mt7981b-keenetic-kn-3911.dts | 2 +- target/linux/mediatek/dts/mt7988d-keenetic-kn-1812.dtsi | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/linux/mediatek/dts/mt7981b-keenetic-kap-630.dtsi b/target/linux/mediatek/dts/mt7981b-keenetic-kap-630.dtsi index 0d03aa762a..e22553f6dd 100644 --- a/target/linux/mediatek/dts/mt7981b-keenetic-kap-630.dtsi +++ b/target/linux/mediatek/dts/mt7981b-keenetic-kap-630.dtsi @@ -138,7 +138,7 @@ mediatek,nmbm; mediatek,bmt-max-ratio = <1>; - mediatek,bmt-max-reserved-blocks = <64>; + mediatek,bmt-max-reserved-blocks = <256>; partitions { compatible = "fixed-partitions"; diff --git a/target/linux/mediatek/dts/mt7981b-keenetic-kn-3711.dts b/target/linux/mediatek/dts/mt7981b-keenetic-kn-3711.dts index 1525cd2d19..b03a30892d 100644 --- a/target/linux/mediatek/dts/mt7981b-keenetic-kn-3711.dts +++ b/target/linux/mediatek/dts/mt7981b-keenetic-kn-3711.dts @@ -211,7 +211,7 @@ mediatek,nmbm; mediatek,bmt-max-ratio = <1>; - mediatek,bmt-max-reserved-blocks = <64>; + mediatek,bmt-max-reserved-blocks = <256>; partitions { compatible = "fixed-partitions"; diff --git a/target/linux/mediatek/dts/mt7981b-keenetic-kn-3811.dts b/target/linux/mediatek/dts/mt7981b-keenetic-kn-3811.dts index c7142fe69b..e6226c66a2 100644 --- a/target/linux/mediatek/dts/mt7981b-keenetic-kn-3811.dts +++ b/target/linux/mediatek/dts/mt7981b-keenetic-kn-3811.dts @@ -227,7 +227,7 @@ mediatek,nmbm; mediatek,bmt-max-ratio = <1>; - mediatek,bmt-max-reserved-blocks = <64>; + mediatek,bmt-max-reserved-blocks = <256>; partitions { compatible = "fixed-partitions"; diff --git a/target/linux/mediatek/dts/mt7981b-keenetic-kn-3911.dts b/target/linux/mediatek/dts/mt7981b-keenetic-kn-3911.dts index 603c897f1e..e508e2d6e0 100644 --- a/target/linux/mediatek/dts/mt7981b-keenetic-kn-3911.dts +++ b/target/linux/mediatek/dts/mt7981b-keenetic-kn-3911.dts @@ -174,7 +174,7 @@ mediatek,nmbm; mediatek,bmt-max-ratio = <1>; - mediatek,bmt-max-reserved-blocks = <64>; + mediatek,bmt-max-reserved-blocks = <256>; partitions { compatible = "fixed-partitions"; diff --git a/target/linux/mediatek/dts/mt7988d-keenetic-kn-1812.dtsi b/target/linux/mediatek/dts/mt7988d-keenetic-kn-1812.dtsi index 8cf21ffd9c..72e951bdfe 100644 --- a/target/linux/mediatek/dts/mt7988d-keenetic-kn-1812.dtsi +++ b/target/linux/mediatek/dts/mt7988d-keenetic-kn-1812.dtsi @@ -170,7 +170,7 @@ mediatek,nmbm; mediatek,bmt-max-ratio = <1>; - mediatek,bmt-max-reserved-blocks = <64>; + mediatek,bmt-max-reserved-blocks = <256>; partitions { compatible = "fixed-partitions"; From 938a43b52212634d1c62901cae97f5152c3d3926 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 23 Jun 2026 14:48:58 -0700 Subject: [PATCH 065/152] kernel: remove kmod-crypto-misc Nothing selects this. In addition, a lot of those algorithms are legacy/obsolete. Signed-off-by: Rosen Penev Link: https://github.com/openwrt/openwrt/pull/23927 Signed-off-by: Jonas Jelonek --- package/kernel/linux/modules/crypto.mk | 95 -------------------------- 1 file changed, 95 deletions(-) diff --git a/package/kernel/linux/modules/crypto.mk b/package/kernel/linux/modules/crypto.mk index b5d065cb90..c7b782e992 100644 --- a/package/kernel/linux/modules/crypto.mk +++ b/package/kernel/linux/modules/crypto.mk @@ -770,101 +770,6 @@ endef $(eval $(call KernelPackage,crypto-michael-mic)) -define KernelPackage/crypto-misc - TITLE:=Other CryptoAPI modules - DEPENDS:=+kmod-crypto-xts +kmod-crypto-user - KCONFIG:= \ - CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y \ - CONFIG_CRYPTO_CAMELLIA_X86_64 \ - CONFIG_CRYPTO_BLOWFISH_X86_64 \ - CONFIG_CRYPTO_TWOFISH_X86_64 \ - CONFIG_CRYPTO_TWOFISH_X86_64_3WAY \ - CONFIG_CRYPTO_SERPENT_SSE2_X86_64 \ - CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 \ - CONFIG_CRYPTO_CAST5_AVX_X86_64 \ - CONFIG_CRYPTO_CAST6_AVX_X86_64 \ - CONFIG_CRYPTO_TWOFISH_AVX_X86_64 \ - CONFIG_CRYPTO_SERPENT_AVX_X86_64 \ - CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 \ - CONFIG_CRYPTO_SERPENT_AVX2_X86_64 \ - CONFIG_CRYPTO_SERPENT_SSE2_586 \ - CONFIG_CRYPTO_ANUBIS \ - CONFIG_CRYPTO_BLOWFISH \ - CONFIG_CRYPTO_CAMELLIA \ - CONFIG_CRYPTO_CAST5 \ - CONFIG_CRYPTO_CAST6 \ - CONFIG_CRYPTO_KHAZAD \ - CONFIG_CRYPTO_SERPENT \ - CONFIG_CRYPTO_TEA \ - CONFIG_CRYPTO_TWOFISH \ - CONFIG_CRYPTO_TWOFISH_COMMON \ - CONFIG_CRYPTO_TWOFISH_586 \ - CONFIG_CRYPTO_WP512 - FILES:= \ - $(LINUX_DIR)/crypto/anubis.ko \ - $(LINUX_DIR)/crypto/camellia_generic.ko \ - $(LINUX_DIR)/crypto/cast_common.ko \ - $(LINUX_DIR)/crypto/cast5_generic.ko \ - $(LINUX_DIR)/crypto/cast6_generic.ko \ - $(LINUX_DIR)/crypto/khazad.ko \ - $(LINUX_DIR)/crypto/tea.ko \ - $(LINUX_DIR)/crypto/twofish_common.ko \ - $(LINUX_DIR)/crypto/wp512.ko \ - $(LINUX_DIR)/crypto/twofish_generic.ko \ - $(LINUX_DIR)/crypto/blowfish_common.ko \ - $(LINUX_DIR)/crypto/blowfish_generic.ko \ - $(LINUX_DIR)/crypto/serpent_generic.ko - AUTOLOAD:=$(call AutoLoad,10,anubis camellia_generic cast_common \ - cast5_generic cast6_generic khazad tea twofish_common \ - wp512 blowfish_common serpent_generic) - ifndef CONFIG_TARGET_x86 - AUTOLOAD+= $(call AutoLoad,10,twofish_generic blowfish_generic) - endif - $(call AddDepends/crypto) -endef - -ifndef CONFIG_TARGET_x86_64 - define KernelPackage/crypto-misc/x86 - FILES+= \ - $(LINUX_DIR)/arch/x86/crypto/twofish-i586.ko \ - $(LINUX_DIR)/arch/x86/crypto/serpent-sse2-i586.ko \ - $(LINUX_DIR)/crypto/cryptd.ko@lt6.18 \ - $(LINUX_DIR)/crypto/crypto_simd.ko@lt6.18 - AUTOLOAD+= $(call AutoLoad,10,cryptd@lt6.18 \ - serpent-sse2-i586 twofish-i586 blowfish_generic) - endef -endif - -ifndef CONFIG_TARGET_uml -define KernelPackage/crypto-misc/x86_64 - FILES+= \ - $(LINUX_DIR)/arch/x86/crypto/camellia-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/blowfish-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/twofish-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/twofish-x86_64-3way.ko \ - $(LINUX_DIR)/arch/x86/crypto/serpent-sse2-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/camellia-aesni-avx-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/cast5-avx-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/cast6-avx-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/twofish-avx-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/serpent-avx-x86_64.ko \ - $(LINUX_DIR)/arch/x86/crypto/camellia-aesni-avx2.ko \ - $(LINUX_DIR)/arch/x86/crypto/serpent-avx2.ko - AUTOLOAD+= $(call AutoLoad,10,camellia-x86_64 \ - camellia-aesni-avx-x86_64 camellia-aesni-avx2 cast5-avx-x86_64 \ - cast6-avx-x86_64 twofish-x86_64 twofish-x86_64-3way \ - twofish-avx-x86_64 blowfish-x86_64 serpent-avx-x86_64 serpent-avx2) -endef -endif - -ifdef KernelPackage/crypto-misc/$(ARCH) - KernelPackage/crypto-misc/$(CRYPTO_TARGET)=\ - $(KernelPackage/crypto-misc/$(ARCH)) -endif - -$(eval $(call KernelPackage,crypto-misc)) - - define KernelPackage/crypto-null TITLE:=Null CryptoAPI module KCONFIG:=CONFIG_CRYPTO_NULL From dd62e3af151d4a92aa8251148ec9b88fa558be73 Mon Sep 17 00:00:00 2001 From: Anna Kiri Date: Tue, 26 May 2026 19:10:36 +0200 Subject: [PATCH 066/152] mtd: fis: fix buffer overflow from negative memset size In fis_remap(), when desc < last, the memset size is computed as 'tmp - end'. Since tmp is calculated as 'end - positive_value', tmp is always less than end, making 'tmp - end' negative. When cast to size_t, this wraps to a very large value, causing a massive buffer overflow. Fix by swapping the operands to 'end - tmp' which correctly computes the number of bytes to clear. Signed-off-by: Anna Kiri Link: https://github.com/openwrt/openwrt/pull/23550 Signed-off-by: Jonas Jelonek --- package/system/mtd/src/fis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/system/mtd/src/fis.c b/package/system/mtd/src/fis.c index 8f719901b8..936359e803 100644 --- a/package/system/mtd/src/fis.c +++ b/package/system/mtd/src/fis.c @@ -215,7 +215,7 @@ fis_remap(struct fis_part *old, int n_old, struct fis_part *new, int n_new) memmove(desc, last, end - tmp); if (desc < last) { tmp = end - (last - desc) * sizeof(struct fis_image_desc); - memset(tmp, 0xff, tmp - end); + memset(tmp, 0xff, end - tmp); } } From 696b38be9ca0fd3583d33bfb3b6446ecfb70a3ad Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Fri, 17 Jul 2026 12:06:45 +0200 Subject: [PATCH 067/152] uboot-airoha: fix AN7583 pinctrl compatible Use the compatible expected by the AN7583 pinctrl driver so the pinctrl and integrated GPIO driver can bind. Signed-off-by: Robert Marko --- .../patches/300-airoha-add-support-for-Airoha-AN7583-SoC.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/boot/uboot-airoha/patches/300-airoha-add-support-for-Airoha-AN7583-SoC.patch b/package/boot/uboot-airoha/patches/300-airoha-add-support-for-Airoha-AN7583-SoC.patch index cc52b0f437..43b7651b9c 100644 --- a/package/boot/uboot-airoha/patches/300-airoha-add-support-for-Airoha-AN7583-SoC.patch +++ b/package/boot/uboot-airoha/patches/300-airoha-add-support-for-Airoha-AN7583-SoC.patch @@ -278,7 +278,7 @@ index 00000000000..e1fda15ba37 + reg = <0x0 0x1fbf0200 0x0 0xc0>; + + an7583_pinctrl: pinctrl { -+ compatible = "airoha,en7583-pinctrl"; ++ compatible = "airoha,an7583-pinctrl"; + + interrupt-parent = <&gic>; + interrupts = ; From 2f9484a2d8edc6f169ebc18e82f306a9ba7b9b95 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Wed, 15 Jul 2026 08:34:19 +0000 Subject: [PATCH 068/152] realtek: add macro for pse-pi Add a macro PSE_PI for the common pse-pi definition, helping with devices which make use of the Realtek PSE MCU driver. While those definitions stay manageable for low port counts, this adds too much visual boilerplate with 24 or even 48 ports. The macro keeps it short with one line per pse-pi. Also make use of this macro directly for the two devices XS1930-12HP and XMG1915-10EP. Link: https://github.com/openwrt/openwrt/pull/24239 Signed-off-by: Jonas Jelonek --- target/linux/realtek/dts/macros.dtsi | 8 ++++ .../dts/rtl9302_zyxel_xmg1915-10ep.dts | 40 ++++--------------- .../realtek/dts/rtl9313_zyxel_xs1930-12hp.dts | 40 ++++--------------- 3 files changed, 24 insertions(+), 64 deletions(-) diff --git a/target/linux/realtek/dts/macros.dtsi b/target/linux/realtek/dts/macros.dtsi index 3285b9067c..718206ced9 100644 --- a/target/linux/realtek/dts/macros.dtsi +++ b/target/linux/realtek/dts/macros.dtsi @@ -32,6 +32,14 @@ enet-phy-pair-order = <##po>; \ }; +#define HASH_PSE_CELLS #pse-cells + +#define PSE_PI(p) \ + pse_pi##p: pse-pi@p { \ + reg =

; \ + HASH_PSE_CELLS = <0>; \ + }; + #define SWITCH_PORT(p, l, m) \ port##p: port@##p { \ reg = <##p>; \ diff --git a/target/linux/realtek/dts/rtl9302_zyxel_xmg1915-10ep.dts b/target/linux/realtek/dts/rtl9302_zyxel_xmg1915-10ep.dts index 08049f0405..bb95203cb0 100644 --- a/target/linux/realtek/dts/rtl9302_zyxel_xmg1915-10ep.dts +++ b/target/linux/realtek/dts/rtl9302_zyxel_xmg1915-10ep.dts @@ -35,38 +35,14 @@ #address-cells = <1>; #size-cells = <0>; - pse_pi0: pse-pi@0 { - reg = <0>; - #pse-cells = <0>; - }; - pse_pi1: pse-pi@1 { - reg = <1>; - #pse-cells = <0>; - }; - pse_pi2: pse-pi@2 { - reg = <2>; - #pse-cells = <0>; - }; - pse_pi3: pse-pi@3 { - reg = <3>; - #pse-cells = <0>; - }; - pse_pi4: pse-pi@4 { - reg = <4>; - #pse-cells = <0>; - }; - pse_pi5: pse-pi@5 { - reg = <5>; - #pse-cells = <0>; - }; - pse_pi6: pse-pi@6 { - reg = <6>; - #pse-cells = <0>; - }; - pse_pi7: pse-pi@7 { - reg = <7>; - #pse-cells = <0>; - }; + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) }; }; }; diff --git a/target/linux/realtek/dts/rtl9313_zyxel_xs1930-12hp.dts b/target/linux/realtek/dts/rtl9313_zyxel_xs1930-12hp.dts index 972fb2d348..932e07bf78 100644 --- a/target/linux/realtek/dts/rtl9313_zyxel_xs1930-12hp.dts +++ b/target/linux/realtek/dts/rtl9313_zyxel_xs1930-12hp.dts @@ -66,38 +66,14 @@ #address-cells = <1>; #size-cells = <0>; - pse_pi0: pse-pi@0 { - reg = <0>; - #pse-cells = <0>; - }; - pse_pi1: pse-pi@1 { - reg = <1>; - #pse-cells = <0>; - }; - pse_pi2: pse-pi@2 { - reg = <2>; - #pse-cells = <0>; - }; - pse_pi3: pse-pi@3 { - reg = <3>; - #pse-cells = <0>; - }; - pse_pi4: pse-pi@4 { - reg = <4>; - #pse-cells = <0>; - }; - pse_pi5: pse-pi@5 { - reg = <5>; - #pse-cells = <0>; - }; - pse_pi6: pse-pi@6 { - reg = <6>; - #pse-cells = <0>; - }; - pse_pi7: pse-pi@7 { - reg = <7>; - #pse-cells = <0>; - }; + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) }; }; }; From f306a19481fcc05f6bc230d154b6a6d2c4f6b1a8 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Tue, 14 Jul 2026 22:08:27 +0000 Subject: [PATCH 069/152] realtek: add PoE support for Linksys LGS328MPCv2 Add PoE support to Linksys LGS328MPCv2 by adding the required device tree definition and select the needed kernel module package, making use of the Realtek PSE MCU driver. Co-authored-by: Jan-Henrik Bruhn Link: https://github.com/openwrt/openwrt/pull/24239 Signed-off-by: Jonas Jelonek --- .../dts/rtl9301_linksys_lgs328mpc-v2.dts | 69 +++++++++++++++++++ target/linux/realtek/image/rtl930x_nand.mk | 2 +- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/target/linux/realtek/dts/rtl9301_linksys_lgs328mpc-v2.dts b/target/linux/realtek/dts/rtl9301_linksys_lgs328mpc-v2.dts index 7c66067814..175c82d340 100644 --- a/target/linux/realtek/dts/rtl9301_linksys_lgs328mpc-v2.dts +++ b/target/linux/realtek/dts/rtl9301_linksys_lgs328mpc-v2.dts @@ -36,6 +36,50 @@ debounce-interval = <60>; }; }; + + i2c_poe: i2c-poe { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + #address-cells = <1>; + #size-cells = <0>; + + pse: ethernet-pse@2a { + compatible = "linksys,lgs328mpc-v2-pse", "realtek,pse-mcu-gen2-i2c"; + reg = <0x2a>; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) + PSE_PI(8) + PSE_PI(9) + PSE_PI(10) + PSE_PI(11) + PSE_PI(12) + PSE_PI(13) + PSE_PI(14) + PSE_PI(15) + PSE_PI(16) + PSE_PI(17) + PSE_PI(18) + PSE_PI(19) + PSE_PI(20) + PSE_PI(21) + PSE_PI(22) + PSE_PI(23) + }; + }; + }; }; &i2c_mst1 { @@ -49,3 +93,28 @@ }; }; }; + +&phy0 { pses = <&pse_pi0>; }; +&phy1 { pses = <&pse_pi1>; }; +&phy2 { pses = <&pse_pi2>; }; +&phy3 { pses = <&pse_pi3>; }; +&phy4 { pses = <&pse_pi4>; }; +&phy5 { pses = <&pse_pi5>; }; +&phy6 { pses = <&pse_pi6>; }; +&phy7 { pses = <&pse_pi7>; }; +&phy8 { pses = <&pse_pi8>; }; +&phy9 { pses = <&pse_pi9>; }; +&phy10 { pses = <&pse_pi10>; }; +&phy11 { pses = <&pse_pi11>; }; +&phy12 { pses = <&pse_pi12>; }; +&phy13 { pses = <&pse_pi13>; }; +&phy14 { pses = <&pse_pi14>; }; +&phy15 { pses = <&pse_pi15>; }; +&phy16 { pses = <&pse_pi16>; }; +&phy17 { pses = <&pse_pi17>; }; +&phy18 { pses = <&pse_pi18>; }; +&phy19 { pses = <&pse_pi19>; }; +&phy20 { pses = <&pse_pi20>; }; +&phy21 { pses = <&pse_pi21>; }; +&phy22 { pses = <&pse_pi22>; }; +&phy23 { pses = <&pse_pi23>; }; diff --git a/target/linux/realtek/image/rtl930x_nand.mk b/target/linux/realtek/image/rtl930x_nand.mk index 7c068c2600..f01d98aa94 100644 --- a/target/linux/realtek/image/rtl930x_nand.mk +++ b/target/linux/realtek/image/rtl930x_nand.mk @@ -33,7 +33,7 @@ define Device/linksys_lgs328mpc-v2 $(Device/linksys_lgs328) DEVICE_MODEL := LGS328MPC DEVICE_VARIANT := v2 - DEVICE_PACKAGES += kmod-hwmon-lm63 + DEVICE_PACKAGES += kmod-hwmon-lm63 kmod-pse-realtek-mcu-i2c LINKSYS_MODEL := 60412060 endef TARGET_DEVICES += linksys_lgs328mpc-v2 From 5b527dc7a4794455aa03b5fe231ca4afdee2813f Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sun, 31 May 2026 07:40:01 +0000 Subject: [PATCH 070/152] realtek: switch to PSE driver for Plasma Cloud PSX28 Use the kernel PSE driver for PoE functionality on this device instead relying on userspace apps. Drop the automatic selection for poemgr in favor of the driver module. Link: https://github.com/openwrt/openwrt/pull/24239 Signed-off-by: Jonas Jelonek --- .../realtek/dts/rtl9312_plasmacloud_psx28.dts | 77 ++++++++++++++++--- target/linux/realtek/image/rtl931x.mk | 2 +- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/target/linux/realtek/dts/rtl9312_plasmacloud_psx28.dts b/target/linux/realtek/dts/rtl9312_plasmacloud_psx28.dts index 488fe00b11..a51a41b658 100644 --- a/target/linux/realtek/dts/rtl9312_plasmacloud_psx28.dts +++ b/target/linux/realtek/dts/rtl9312_plasmacloud_psx28.dts @@ -14,21 +14,74 @@ gpios = <&gpio1 28 GPIO_ACTIVE_LOW>; }; }; +}; - gpio-export { - compatible = "gpio-export"; +&i2c_mst1 { + i2c5: i2c@5 { + reg = <5>; + #address-cells = <1>; + #size-cells = <0>; - poe_mcu_reset { - gpio-export,name = "poe_mcu_reset"; - gpio-export,output = <1>; - gpios = <&gpio1 29 GPIO_ACTIVE_LOW>; + pse: ethernet-pse@20 { + compatible = "plasmacloud,psx28-pse", "realtek,pse-mcu-gen2-smbus"; + reg = <0x20>; + + enable-gpios = <&gpio1 29 GPIO_ACTIVE_HIGH>; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) + PSE_PI(8) + PSE_PI(9) + PSE_PI(10) + PSE_PI(11) + PSE_PI(12) + PSE_PI(13) + PSE_PI(14) + PSE_PI(15) + PSE_PI(16) + PSE_PI(17) + PSE_PI(18) + PSE_PI(19) + PSE_PI(20) + PSE_PI(21) + PSE_PI(22) + PSE_PI(23) + }; }; }; }; -&i2c_mst1 { - /* i2c for rtl8239 PoE PSE Chip */ - i2c@5 { - reg = <5>; - }; -}; +&phy0 { pses = <&pse_pi0>; }; +&phy1 { pses = <&pse_pi1>; }; +&phy4 { pses = <&pse_pi2>; }; +&phy5 { pses = <&pse_pi3>; }; +&phy8 { pses = <&pse_pi4>; }; +&phy9 { pses = <&pse_pi5>; }; +&phy12 { pses = <&pse_pi6>; }; +&phy13 { pses = <&pse_pi7>; }; +&phy16 { pses = <&pse_pi8>; }; +&phy17 { pses = <&pse_pi9>; }; +&phy20 { pses = <&pse_pi10>; }; +&phy21 { pses = <&pse_pi11>; }; +&phy24 { pses = <&pse_pi12>; }; +&phy25 { pses = <&pse_pi13>; }; +&phy28 { pses = <&pse_pi14>; }; +&phy29 { pses = <&pse_pi15>; }; +&phy32 { pses = <&pse_pi16>; }; +&phy33 { pses = <&pse_pi17>; }; +&phy36 { pses = <&pse_pi18>; }; +&phy37 { pses = <&pse_pi19>; }; +&phy40 { pses = <&pse_pi20>; }; +&phy41 { pses = <&pse_pi21>; }; +&phy44 { pses = <&pse_pi22>; }; +&phy45 { pses = <&pse_pi23>; }; diff --git a/target/linux/realtek/image/rtl931x.mk b/target/linux/realtek/image/rtl931x.mk index a0abeecff0..cc44033982 100644 --- a/target/linux/realtek/image/rtl931x.mk +++ b/target/linux/realtek/image/rtl931x.mk @@ -50,7 +50,7 @@ TARGET_DEVICES += plasmacloud_esx28 define Device/plasmacloud_psx28 $(Device/plasmacloud-common) DEVICE_MODEL := PSX28 - DEVICE_PACKAGES += poemgr + DEVICE_PACKAGES += kmod-pse-realtek-mcu-i2c endef TARGET_DEVICES += plasmacloud_psx28 From d3e2f2df8380640fba21ceb46741be49ed4165c7 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sun, 3 May 2026 22:17:30 +0000 Subject: [PATCH 071/152] realtek: add PoE support for XGS1930-28HP Add support for PoE on Zyxel XGS1930-28HP by leveraging the new Realtek PSE driver. Define the PSE controller with all its properties in DTS of the device and link it to the PHYs. Link: https://github.com/openwrt/openwrt/pull/24239 Signed-off-by: Jonas Jelonek --- .../dts/rtl9301_zyxel_xgs1930-28hp.dts | 85 +++++++++++++++---- target/linux/realtek/image/rtl930x.mk | 2 +- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/target/linux/realtek/dts/rtl9301_zyxel_xgs1930-28hp.dts b/target/linux/realtek/dts/rtl9301_zyxel_xgs1930-28hp.dts index 7c01590053..78c0c19ae4 100644 --- a/target/linux/realtek/dts/rtl9301_zyxel_xgs1930-28hp.dts +++ b/target/linux/realtek/dts/rtl9301_zyxel_xgs1930-28hp.dts @@ -176,6 +176,13 @@ tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; los-gpio = <&gpio1 24 GPIO_ACTIVE_HIGH>; }; + + poe_power: regulator-poe { + compatible = "regulator-fixed"; + regulator-name = "poe-power"; + gpio = <&gpio1 12 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; }; &gpio0 { @@ -185,13 +192,6 @@ output-high; line-name = "sfp-enable"; }; - - poe_mcu_enable_hog { - gpio-hog; - gpios = <19 GPIO_ACTIVE_HIGH>; - output-high; - line-name = "poe-mcu-enable"; - }; }; &i2c_mst1 { @@ -199,7 +199,44 @@ i2c0: i2c@0 { reg = <0>; - /* PoE MCU sits here at 0x20 */ + + pse: ethernet-pse@20 { + compatible = "zyxel,xgs1930-28hp-pse", "realtek,pse-mcu-gen2-smbus"; + reg = <0x20>; + + enable-gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>; + power-supply = <&poe_power>; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) + PSE_PI(8) + PSE_PI(9) + PSE_PI(10) + PSE_PI(11) + PSE_PI(12) + PSE_PI(13) + PSE_PI(14) + PSE_PI(15) + PSE_PI(16) + PSE_PI(17) + PSE_PI(18) + PSE_PI(19) + PSE_PI(20) + PSE_PI(21) + PSE_PI(22) + PSE_PI(23) + }; + }; }; }; @@ -235,13 +272,6 @@ compatible = "realtek,rtl8231-leds"; status = "disabled"; }; - - poe_enable_hog { - gpio-hog; - gpios = <12 GPIO_ACTIVE_HIGH>; - output-high; - line-name = "poe-enable"; - }; }; }; @@ -425,3 +455,28 @@ nvmem-cells = <&factory_macaddr>; nvmem-cell-names = "mac-address"; }; + +&phy0 { pses = <&pse_pi0>; }; +&phy1 { pses = <&pse_pi1>; }; +&phy2 { pses = <&pse_pi2>; }; +&phy3 { pses = <&pse_pi3>; }; +&phy4 { pses = <&pse_pi4>; }; +&phy5 { pses = <&pse_pi5>; }; +&phy6 { pses = <&pse_pi6>; }; +&phy7 { pses = <&pse_pi7>; }; +&phy8 { pses = <&pse_pi8>; }; +&phy9 { pses = <&pse_pi9>; }; +&phy10 { pses = <&pse_pi10>; }; +&phy11 { pses = <&pse_pi11>; }; +&phy12 { pses = <&pse_pi12>; }; +&phy13 { pses = <&pse_pi13>; }; +&phy14 { pses = <&pse_pi14>; }; +&phy15 { pses = <&pse_pi15>; }; +&phy16 { pses = <&pse_pi16>; }; +&phy17 { pses = <&pse_pi17>; }; +&phy18 { pses = <&pse_pi18>; }; +&phy19 { pses = <&pse_pi19>; }; +&phy20 { pses = <&pse_pi20>; }; +&phy21 { pses = <&pse_pi21>; }; +&phy22 { pses = <&pse_pi22>; }; +&phy23 { pses = <&pse_pi23>; }; diff --git a/target/linux/realtek/image/rtl930x.mk b/target/linux/realtek/image/rtl930x.mk index 434e197edd..6f2b6a8cf7 100644 --- a/target/linux/realtek/image/rtl930x.mk +++ b/target/linux/realtek/image/rtl930x.mk @@ -336,11 +336,11 @@ TARGET_DEVICES += zyxel_xgs1250-12-b1 define Device/zyxel_xgs1930-28hp SOC := rtl9301 DEVICE_MODEL := XGS1930-28HP - DEVICE_PACKAGES := kmod-hwmon-gpiofan FLASH_ADDR := 0xb4260000 IMAGE_SIZE := 30336k ZYNFW_ALIGN := 0x10000 $(Device/zyxel_zynos) + DEVICE_PACKAGES += kmod-hwmon-gpiofan kmod-pse-realtek-mcu-i2c endef TARGET_DEVICES += zyxel_xgs1930-28hp From b76acc1b1a0c0ba7f851d7eb78bfa7161033e315 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 26 Jun 2026 18:10:31 +0000 Subject: [PATCH 072/152] realtek: add PoE support for USW XG Pro 8 PoE Add device tree definition for PoE support using the Realtek PSE MCU driver. Select the kernel package for the driver in DEVICE_PACKAGES accordingly. Link: https://github.com/openwrt/openwrt/pull/24239 Signed-off-by: Jonas Jelonek --- .../dts/rtl9313_ubnt_usw-pro-xg-8-poe.dts | 35 ++++++++++++++++++- target/linux/realtek/image/rtl931x.mk | 2 +- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/target/linux/realtek/dts/rtl9313_ubnt_usw-pro-xg-8-poe.dts b/target/linux/realtek/dts/rtl9313_ubnt_usw-pro-xg-8-poe.dts index b853ee1d17..5948c156df 100644 --- a/target/linux/realtek/dts/rtl9313_ubnt_usw-pro-xg-8-poe.dts +++ b/target/linux/realtek/dts/rtl9313_ubnt_usw-pro-xg-8-poe.dts @@ -225,7 +225,32 @@ }; }; - i2c6: i2c@6 { reg = <6>; }; /* PoE MCU at 0x20 */ + i2c_poe: i2c@6 { + reg = <6>; + #address-cells = <1>; + #size-cells = <0>; + + pse: ethernet-pse@20 { + compatible = "ubnt,usw-pro-xg-8-poe-pse", "realtek,pse-mcu-gen2-smbus"; + reg = <0x20>; + + enable-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) + }; + }; + }; }; &mdio_ctrl { @@ -289,3 +314,11 @@ }; }; +&phy0 { pses = <&pse_pi0>; }; +&phy8 { pses = <&pse_pi1>; }; +&phy16 { pses = <&pse_pi2>; }; +&phy24 { pses = <&pse_pi3>; }; +&phy32 { pses = <&pse_pi4>; }; +&phy40 { pses = <&pse_pi5>; }; +&phy48 { pses = <&pse_pi6>; }; +&phy50 { pses = <&pse_pi7>; }; diff --git a/target/linux/realtek/image/rtl931x.mk b/target/linux/realtek/image/rtl931x.mk index cc44033982..c3a4938532 100644 --- a/target/linux/realtek/image/rtl931x.mk +++ b/target/linux/realtek/image/rtl931x.mk @@ -59,7 +59,7 @@ define Device/ubnt_usw-pro-xg-8-poe DEVICE_VENDOR := Ubiquiti DEVICE_MODEL := UniFi USW Pro XG 8 PoE IMAGE_SIZE := 30272k - DEVICE_PACKAGES := rtl826x-firmware kmod-hwmon-adt7475 + DEVICE_PACKAGES := rtl826x-firmware kmod-hwmon-adt7475 kmod-pse-realtek-mcu-i2c $(Device/kernel-lzma) endef TARGET_DEVICES += ubnt_usw-pro-xg-8-poe From 52c0274e55e82f527705afd9e655e0e6f2c7a2bb Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Wed, 27 May 2026 14:26:47 +0000 Subject: [PATCH 073/152] realtek: add PoE support for GS1920-24HP Add PoE support for GS1920-24HP by providing the device tree definition and selecting the Realtek PSE MCU driver package for build. Support includes both versions of the device since they basically share the same PSE frontend. The common DTSI includes the shared definitions, the device DTS files then set the correct compatibles. Link: https://github.com/openwrt/openwrt/pull/24239 Signed-off-by: Jonas Jelonek --- .../dts/rtl8391_zyxel_gs1920-24hp-v2.dts | 4 ++ .../dts/rtl8392_zyxel_gs1920-24hp-v1.dts | 4 ++ .../dts/rtl839x_zyxel_gs1920-24hp-common.dtsi | 59 +++++++++++++++++++ target/linux/realtek/image/rtl839x.mk | 3 +- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts b/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts index 36b05d11a8..3aab3d8072 100644 --- a/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts +++ b/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts @@ -124,6 +124,10 @@ PHY_C22_SFP(27, 27, 3) }; +&pse { + compatible = "zyxel,gs1920-24hp-v2-pse", "realtek,pse-mcu-gen1-smbus"; +}; + &switch0 { ethernet-ports { SWITCH_PORT_SDS(24, 25, 6, 0, qsgmii) diff --git a/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts b/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts index 47bcbe9a19..289dc73306 100644 --- a/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts +++ b/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts @@ -78,6 +78,10 @@ PHY_C22_SFP(51, 27, 3) }; +&pse { + compatible = "zyxel,gs1920-24hp-v1-pse", "realtek,pse-mcu-gen1-smbus"; +}; + &switch0 { ethernet-ports { SWITCH_PORT_SDS(48, 25, 12, 0, qsgmii) diff --git a/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi b/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi index c8bad10a46..eba9350c4d 100644 --- a/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi +++ b/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi @@ -112,6 +112,40 @@ i2c-gpio,delay-us = <2>; #address-cells = <1>; #size-cells = <0>; + + pse: ethernet-pse@20 { + reg = <0x20>; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) + PSE_PI(8) + PSE_PI(9) + PSE_PI(10) + PSE_PI(11) + PSE_PI(12) + PSE_PI(13) + PSE_PI(14) + PSE_PI(15) + PSE_PI(16) + PSE_PI(17) + PSE_PI(18) + PSE_PI(19) + PSE_PI(20) + PSE_PI(21) + PSE_PI(22) + PSE_PI(23) + }; + }; }; }; @@ -245,3 +279,28 @@ }; }; }; + +&phy0 { pses = <&pse_pi0>; }; +&phy1 { pses = <&pse_pi1>; }; +&phy2 { pses = <&pse_pi2>; }; +&phy3 { pses = <&pse_pi3>; }; +&phy4 { pses = <&pse_pi4>; }; +&phy5 { pses = <&pse_pi5>; }; +&phy6 { pses = <&pse_pi6>; }; +&phy7 { pses = <&pse_pi7>; }; +&phy8 { pses = <&pse_pi8>; }; +&phy9 { pses = <&pse_pi9>; }; +&phy10 { pses = <&pse_pi10>; }; +&phy11 { pses = <&pse_pi11>; }; +&phy12 { pses = <&pse_pi12>; }; +&phy13 { pses = <&pse_pi13>; }; +&phy14 { pses = <&pse_pi14>; }; +&phy15 { pses = <&pse_pi15>; }; +&phy16 { pses = <&pse_pi16>; }; +&phy17 { pses = <&pse_pi17>; }; +&phy18 { pses = <&pse_pi18>; }; +&phy19 { pses = <&pse_pi19>; }; +&phy20 { pses = <&pse_pi20>; }; +&phy21 { pses = <&pse_pi21>; }; +&phy22 { pses = <&pse_pi22>; }; +&phy23 { pses = <&pse_pi23>; }; diff --git a/target/linux/realtek/image/rtl839x.mk b/target/linux/realtek/image/rtl839x.mk index 13e2b6a4d7..a8b54c9db8 100644 --- a/target/linux/realtek/image/rtl839x.mk +++ b/target/linux/realtek/image/rtl839x.mk @@ -117,8 +117,7 @@ ifeq ($(IB),) endif DEVICE_VENDOR := Zyxel DEVICE_MODEL := GS1920-24HP - DEVICE_PACKAGES := \ - kmod-hwmon-lm85 + DEVICE_PACKAGES := kmod-hwmon-lm85 kmod-pse-realtek-mcu-i2c $(Device/rt-loader-bootbase) endef From 06c826e3354e2553c2360e144c8a671aaee51ad8 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Fri, 17 Jul 2026 21:19:46 +0200 Subject: [PATCH 074/152] kernel: use iremap for assembly Currently assembly code of the Kernel may contain the build path, resulting in unreproducible firmware images (and Kernels). Fix this by adding a iremap KAFLAG. Before: strings "/path/to/vmlinux" | grep linux-6.18.38 /path/to/openwrt/build_dir/target-x86_64_musl/linux-x86_64/linux-6.18.38 /path/to/openwrt/build_dir/target-x86_64_musl/linux-x86_64/linux-6.18.38 After strings "/path/to/vmlinux" | grep linux-6.18.38 target-x86_64_musl/linux-x86_64/linux-6.18.38 target-x86_64_musl/linux-x86_64/linux-6.18.38 Link: https://github.com/openwrt/openwrt/pull/24271 Signed-off-by: Paul Spooren --- include/kernel.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/include/kernel.mk b/include/kernel.mk index 146346a475..d19417bc15 100644 --- a/include/kernel.mk +++ b/include/kernel.mk @@ -102,6 +102,7 @@ KERNEL_MAKE = $(MAKE) $(KERNEL_MAKEOPTS) KERNEL_MAKE_FLAGS = \ KCFLAGS="$(call iremap,$(BUILD_DIR),$(notdir $(BUILD_DIR))) $(filter-out -fno-plt,$(call qstrip,$(CONFIG_EXTRA_OPTIMIZATION))) $(call qstrip,$(CONFIG_KERNEL_CFLAGS))" \ + KAFLAGS="$(call iremap,$(BUILD_DIR),$(notdir $(BUILD_DIR)))" \ HOSTCFLAGS="$(HOST_CFLAGS) -Wall -Wmissing-prototypes -Wstrict-prototypes" \ CROSS_COMPILE="$(KERNEL_CROSS)" \ ARCH="$(LINUX_KARCH)" \ From 8225fab99e4d876e69f2aefef5f73175814177a2 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Tue, 16 Jun 2026 08:37:42 +0300 Subject: [PATCH 075/152] airoha: wrap long lines and fix indents Wrap long lines and fix indents in the an7583.mk. Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- target/linux/airoha/image/an7583.mk | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/target/linux/airoha/image/an7583.mk b/target/linux/airoha/image/an7583.mk index f51b79e27b..fef3c3f607 100644 --- a/target/linux/airoha/image/an7583.mk +++ b/target/linux/airoha/image/an7583.mk @@ -1,17 +1,20 @@ define Device/FitImageLzma - KERNEL_SUFFIX := -uImage.itb - KERNEL = kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb - KERNEL_NAME := Image + KERNEL_SUFFIX := -uImage.itb + KERNEL = kernel-bin | lzma | \ + fit lzma $$(KDIR)/image-$$(DEVICE_DTS).dtb + KERNEL_NAME := Image endef define Device/airoha_an7583-evb $(call Device/FitImageLzma) DEVICE_VENDOR := Airoha DEVICE_MODEL := AN7583 Evaluation Board (SNAND) - DEVICE_PACKAGES := kmod-phy-aeonsemi-as21xxx kmod-leds-pwm kmod-pwm-airoha kmod-input-gpio-keys-polled + DEVICE_PACKAGES := kmod-phy-aeonsemi-as21xxx kmod-leds-pwm \ + kmod-pwm-airoha kmod-input-gpio-keys-polled DEVICE_DTS := an7583-evb DEVICE_DTS_CONFIG := config@1 - IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | pad-rootfs | append-metadata + IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | \ + pad-rootfs | append-metadata endef TARGET_DEVICES += airoha_an7583-evb From 0782f282a66d117327eac0d8552be5264ede95ab Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Tue, 16 Jun 2026 08:20:20 +0300 Subject: [PATCH 076/152] airoha: an7583: add default packages Add airoha-an7583-npu-firmware, kmod-gpio-button-hotplug, kmod-leds-gpio and uboot-envtools to the default packages. Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- target/linux/airoha/an7583/target.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/linux/airoha/an7583/target.mk b/target/linux/airoha/an7583/target.mk index dacbe0928e..fb8785a189 100644 --- a/target/linux/airoha/an7583/target.mk +++ b/target/linux/airoha/an7583/target.mk @@ -5,6 +5,10 @@ CPU_TYPE:=cortex-a53 KERNELNAME:=Image dtbs FEATURES+=pwm source-only +DEFAULT_PACKAGES += \ + airoha-an7583-npu-firmware kmod-leds-gpio \ + kmod-gpio-button-hotplug uboot-envtools + define Target/Description Build firmware images for Airoha an7583 ARM based boards. endef From ed38b88bc31ccd731ed0faecf51eed47c404c7c2 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Sun, 14 Jun 2026 08:48:45 +0300 Subject: [PATCH 077/152] airoha: an7583: enable mtd virtual concat support This is required for upcoming Nokia XG-040G-MF support. Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- target/linux/airoha/an7583/config-6.18 | 1 + 1 file changed, 1 insertion(+) diff --git a/target/linux/airoha/an7583/config-6.18 b/target/linux/airoha/an7583/config-6.18 index f3d92270c1..2bf57ce724 100644 --- a/target/linux/airoha/an7583/config-6.18 +++ b/target/linux/airoha/an7583/config-6.18 @@ -215,6 +215,7 @@ CONFIG_MTD_UBI=y CONFIG_MTD_UBI_BEB_LIMIT=20 CONFIG_MTD_UBI_BLOCK=y CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_VIRT_CONCAT=y CONFIG_MUTEX_SPIN_ON_OWNER=y CONFIG_NEED_DMA_MAP_STATE=y CONFIG_NEED_SG_DMA_LENGTH=y From ada7ded2f4347ef78a70adad1fa88a811eb55040 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Mon, 22 Jun 2026 20:36:52 +0300 Subject: [PATCH 078/152] airoha: an7583: refresh kernel config 1. Enable MEDIATEK_GE_SOC_PHY driver and disable NET_DSA_MT7530_MDIO 2. Refresh kernel configs with: make kernel_oldconfig CONFIG_TARGET=subtarget Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- target/linux/airoha/an7583/config-6.18 | 63 ++++++++++++++------------ 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/target/linux/airoha/an7583/config-6.18 b/target/linux/airoha/an7583/config-6.18 index 2bf57ce724..38cf5726cd 100644 --- a/target/linux/airoha/an7583/config-6.18 +++ b/target/linux/airoha/an7583/config-6.18 @@ -1,11 +1,8 @@ CONFIG_64BIT=y -CONFIG_AIROHA_CPU_PM_DOMAIN=y -CONFIG_AIROHA_SCU_SSR=y CONFIG_AIROHA_THERMAL=y CONFIG_AIROHA_WATCHDOG=y CONFIG_ARCH_AIROHA=y CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y -CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y CONFIG_ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG=y CONFIG_ARCH_DMA_ADDR_T_64BIT=y CONFIG_ARCH_FORCE_MAX_ORDER=10 @@ -15,41 +12,41 @@ CONFIG_ARCH_MMAP_RND_BITS=18 CONFIG_ARCH_MMAP_RND_BITS_MAX=24 CONFIG_ARCH_MMAP_RND_BITS_MIN=18 CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11 +CONFIG_ARCH_PKEY_BITS=3 CONFIG_ARCH_PROC_KCORE_TEXT=y CONFIG_ARCH_SPARSEMEM_ENABLE=y CONFIG_ARCH_STACKWALK=y CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANTS_EXECMEM_LATE=y CONFIG_ARCH_WANTS_NO_INSTR=y CONFIG_ARCH_WANTS_THP_SWAP=y CONFIG_ARM64=y CONFIG_ARM64_4K_PAGES=y +CONFIG_ARM64_BRBE=y CONFIG_ARM64_ERRATUM_843419=y -CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y CONFIG_ARM64_PA_BITS=48 CONFIG_ARM64_PA_BITS_48=y CONFIG_ARM64_PLATFORM_DEVICES=y CONFIG_ARM64_TAGGED_ADDR_ABI=y CONFIG_ARM64_VA_BITS=39 CONFIG_ARM64_VA_BITS_39=y -# CONFIG_ARM64_VA_BITS_48 is not set -# CONFIG_ARM64_VA_BITS_52 is not set CONFIG_ARM_AIROHA_SOC_CPUFREQ=y CONFIG_ARM_AMBA=y CONFIG_ARM_ARCH_TIMER=y CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y -# CONFIG_ARM_DEBUG_WX is not set CONFIG_ARM_GIC=y +CONFIG_ARM_GIC_ITS_PARENT=y CONFIG_ARM_GIC_V2M=y CONFIG_ARM_GIC_V3=y CONFIG_ARM_GIC_V3_ITS=y +CONFIG_ARM_GIC_V5=y CONFIG_ARM_PMU=y CONFIG_ARM_PMUV3=y CONFIG_ARM_PSCI_FW=y CONFIG_ARM_SMCCC_SOC_ID=y -# CONFIG_ARM_SMMU is not set -# CONFIG_ARM_SMMU_V3 is not set CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y CONFIG_BLK_PM=y +CONFIG_BLOCK_NOTIFIERS=y CONFIG_BUFFER_HEAD=y CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y CONFIG_CC_HAVE_SHADOW_CALL_STACK=y @@ -59,7 +56,6 @@ CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK_EN7523=y CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1 # CONFIG_COMPAT_32BIT_TIME is not set -# CONFIG_COMPRESSED_INSTALL is not set CONFIG_CONTEXT_TRACKING=y CONFIG_CONTEXT_TRACKING_IDLE=y CONFIG_CPUFREQ_DT=y @@ -76,31 +72,35 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MITIGATIONS=y CONFIG_CPU_RMAP=y CONFIG_CRC16=y +CONFIG_CRC32_ARCH=y CONFIG_CRC_CCITT=y CONFIG_CRYPTO_CRC32C=y CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_EIP93=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_HASH_INFO=y -CONFIG_CRYPTO_LIB_BLAKE2S_ARCH=y -CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y CONFIG_CRYPTO_LIB_GF128MUL=y +CONFIG_CRYPTO_LIB_SHA1=y +CONFIG_CRYPTO_LIB_SHA1_ARCH=y +CONFIG_CRYPTO_LIB_SHA256_ARCH=y CONFIG_CRYPTO_LIB_UTILS=y CONFIG_CRYPTO_LZO=y CONFIG_CRYPTO_ZSTD=y CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_DEBUG_INFO=y CONFIG_DEBUG_MISC=y +CONFIG_DEV_COREDUMP=y CONFIG_DMADEVICES=y CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y CONFIG_DMA_DIRECT_REMAP=y CONFIG_DMA_ENGINE=y CONFIG_DMA_NEED_SYNC=y CONFIG_DMA_OF=y -CONFIG_DMA_OPS_HELPERS=y CONFIG_DTC=y CONFIG_EDAC_SUPPORT=y +CONFIG_EXCLUSIVE_SYSTEM_RAM=y CONFIG_EXT4_FS=y CONFIG_FIXED_PHY=y CONFIG_FIX_EARLYCON_MEM=y @@ -111,7 +111,6 @@ CONFIG_FUNCTION_ALIGNMENT=4 CONFIG_FUNCTION_ALIGNMENT_4B=y CONFIG_FWNODE_MDIO=y CONFIG_FWNODE_PCS=y -CONFIG_FW_CACHE=y # CONFIG_FW_LOADER_USER_HELPER is not set CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y CONFIG_GENERIC_ALLOCATOR=y @@ -129,6 +128,9 @@ CONFIG_GENERIC_GETTIMEOFDAY=y CONFIG_GENERIC_IDLE_POLL_SETUP=y CONFIG_GENERIC_IOREMAP=y CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y +CONFIG_GENERIC_IRQ_ENTRY=y +CONFIG_GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD=y +CONFIG_GENERIC_IRQ_MIGRATION=y CONFIG_GENERIC_IRQ_SHOW=y CONFIG_GENERIC_IRQ_SHOW_LEVEL=y CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y @@ -154,16 +156,16 @@ CONFIG_HAS_DMA=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT=y CONFIG_HAS_IOPORT_MAP=y -# CONFIG_HISILICON_ERRATUM_162100801 is not set CONFIG_HOTPLUG_CORE_SYNC=y CONFIG_HOTPLUG_CORE_SYNC_DEAD=y CONFIG_HOTPLUG_CPU=y +CONFIG_HOTPLUG_SMT=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_AIROHA=y -# CONFIG_IDPF is not set CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_INITRAMFS_SOURCE="" CONFIG_IO_URING=y -CONFIG_IPC_NS=y +CONFIG_IO_URING_ZCRX=y CONFIG_IPV6=y CONFIG_IPV6_MULTIPLE_TABLES=y # CONFIG_IPV6_SUBTREES is not set @@ -180,6 +182,7 @@ CONFIG_IRQCHIP=y CONFIG_IRQ_DOMAIN=y CONFIG_IRQ_DOMAIN_HIERARCHY=y CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_MSI_IOMMU=y CONFIG_IRQ_MSI_LIB=y CONFIG_IRQ_WORK=y CONFIG_JBD2=y @@ -191,15 +194,13 @@ CONFIG_LZO_COMPRESS=y CONFIG_LZO_DECOMPRESS=y CONFIG_MDIO_AIROHA=y CONFIG_MDIO_BUS=y -CONFIG_MDIO_DEVICE=y -CONFIG_MDIO_DEVRES=y -# CONFIG_MEDIATEK_GE_SOC_PHY is not set -# CONFIG_MEMCG is not set +CONFIG_MEDIATEK_GE_SOC_PHY=y CONFIG_MFD_SYSCON=y CONFIG_MIGRATION=y CONFIG_MMC=y CONFIG_MMC_BLOCK=y CONFIG_MMC_CQHCI=y +CONFIG_MMC_HSQ=y CONFIG_MMC_MTK=y CONFIG_MMU_LAZY_TLB_REFCOUNT=y CONFIG_MODULES_TREE_LOOKUP=y @@ -216,6 +217,7 @@ CONFIG_MTD_UBI_BEB_LIMIT=20 CONFIG_MTD_UBI_BLOCK=y CONFIG_MTD_UBI_WL_THRESHOLD=4096 CONFIG_MTD_VIRT_CONCAT=y +CONFIG_MTK_NET_PHYLIB=y CONFIG_MUTEX_SPIN_ON_OWNER=y CONFIG_NEED_DMA_MAP_STATE=y CONFIG_NEED_SG_DMA_LENGTH=y @@ -225,15 +227,17 @@ CONFIG_NET_AIROHA_NPU=y CONFIG_NET_DEVLINK=y CONFIG_NET_DSA=y CONFIG_NET_DSA_MT7530=y -CONFIG_NET_DSA_MT7530_MDIO=y +# CONFIG_NET_DSA_MT7530_MDIO is not set CONFIG_NET_DSA_MT7530_MMIO=y CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_EGRESS=y CONFIG_NET_FLOW_LIMIT=y -# CONFIG_NET_MEDIATEK_SOC is not set +CONFIG_NET_INGRESS=y CONFIG_NET_SELFTESTS=y # CONFIG_NET_VENDOR_3COM is not set CONFIG_NET_VENDOR_AIROHA=y # CONFIG_NET_VENDOR_MEDIATEK is not set +CONFIG_NET_XGRESS=y CONFIG_NLS=y CONFIG_NO_HZ_COMMON=y CONFIG_NO_HZ_IDLE=y @@ -251,6 +255,7 @@ CONFIG_OF_GPIO=y CONFIG_OF_IRQ=y CONFIG_OF_KOBJ=y CONFIG_OF_MDIO=y +CONFIG_PADATA=y CONFIG_PAGE_BLOCK_MAX_ORDER=10 CONFIG_PAGE_POOL=y CONFIG_PAGE_SIZE_LESS_THAN_256KB=y @@ -270,6 +275,7 @@ CONFIG_PCIE_PME=y CONFIG_PCI_DOMAINS=y CONFIG_PCI_DOMAINS_GENERIC=y CONFIG_PCI_MSI=y +CONFIG_PCS_AIROHA=y # CONFIG_PCS_AIROHA_AN7581 is not set CONFIG_PCS_AIROHA_AN7583=y CONFIG_PERF_EVENTS=y @@ -281,6 +287,7 @@ CONFIG_PHYLINK=y CONFIG_PHYS_ADDR_T_64BIT=y CONFIG_PHY_AIROHA_PCIE=y # CONFIG_PHY_AIROHA_USB is not set +CONFIG_PHY_PACKAGE=y CONFIG_PINCTRL=y CONFIG_PINCTRL_AIROHA=y # CONFIG_PINCTRL_MT2712 is not set @@ -309,13 +316,13 @@ CONFIG_RANDSTRUCT_NONE=y CONFIG_RAS=y CONFIG_RATIONAL=y CONFIG_REGMAP=y +CONFIG_REGMAP_IRQ=y CONFIG_REGMAP_MMIO=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_RELOCATABLE=y CONFIG_RESET_CONTROLLER=y CONFIG_RFS_ACCEL=y -CONFIG_RODATA_FULL_DEFAULT_ENABLED=y CONFIG_RPS=y CONFIG_RWSEM_SPIN_ON_OWNER=y CONFIG_SECCOMP=y @@ -328,7 +335,6 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=5 CONFIG_SERIAL_8250_SHARE_IRQ=y CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y -CONFIG_SGL_ALLOC=y CONFIG_SMP=y CONFIG_SOCK_RX_QUEUE_MAPPING=y CONFIG_SOC_BUS=y @@ -339,14 +345,14 @@ CONFIG_SPARSEMEM_VMEMMAP=y CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y CONFIG_SPARSE_IRQ=y CONFIG_SPI=y -# CONFIG_SPI_AIROHA_EN7523 is not set CONFIG_SPI_AIROHA_SNFI=y CONFIG_SPI_MASTER=y CONFIG_SPI_MEM=y +CONFIG_SPLIT_PMD_PTLOCKS=y +CONFIG_SPLIT_PTE_PTLOCKS=y CONFIG_SWIOTLB=y CONFIG_SWPHY=y CONFIG_SYSCTL_EXCEPTION_TRACE=y -# CONFIG_TEST_FPU is not set CONFIG_THERMAL=y CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0 @@ -364,6 +370,7 @@ CONFIG_UBIFS_FS=y CONFIG_USER_STACKTRACE_SUPPORT=y CONFIG_VDSO_GETRANDOM=y CONFIG_VMAP_STACK=y +CONFIG_WANT_DEV_COREDUMP=y CONFIG_WATCHDOG_CORE=y # CONFIG_WLAN is not set # CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set From a62a603a1d77dbe84b6e7d9d0b65b83f60505d4e Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Sat, 27 Jun 2026 18:44:28 +0300 Subject: [PATCH 079/152] airoha: use standard LED function and enumerators for PHY LEDs Configure shared LAN PHY LED templates with standard LED_FUNCTION_LAN macros and initial function-enumerators. These nodes remain disabled by default. Board-specific DTS files enabling them must provide the required 'color' property, and must override the 'function-enumerator' values if identical colors are used to guarantee unique sysfs names. Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- target/linux/airoha/dts/an7583.dtsi | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/target/linux/airoha/dts/an7583.dtsi b/target/linux/airoha/dts/an7583.dtsi index 59e48facad..d8c3ec7ec8 100644 --- a/target/linux/airoha/dts/an7583.dtsi +++ b/target/linux/airoha/dts/an7583.dtsi @@ -760,15 +760,26 @@ #address-cells = <1>; #size-cells = <0>; + /* + * Board DTS files enabling these nodes + * must provide the 'color' property. + * If a board implements multiple + * identical colors, + * 'function-enumerator' values must be + * overridden to maintain unique sysfs + * naming. + */ gsw_phy1_led0: gsw-phy1-led0@0 { reg = <0>; - function = "phy1_led0"; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; status = "disabled"; }; gsw_phy1_led1: gsw-phy1-led1@1 { reg = <1>; - function = "phy1_led1"; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; status = "disabled"; }; }; @@ -785,13 +796,15 @@ gsw_phy2_led0: gsw-phy2-led0@0 { reg = <0>; - function = "phy2_led0"; + function = LED_FUNCTION_LAN; + function-enumerator = <2>; status = "disabled"; }; gsw_phy2_led1: gsw-phy2-led1@1 { reg = <1>; - function = "phy1_led1"; + function = LED_FUNCTION_LAN; + function-enumerator = <2>; status = "disabled"; }; }; @@ -809,12 +822,14 @@ gsw_phy3_led0: gsw-phy3-led0@0 { reg = <0>; function = LED_FUNCTION_LAN; + function-enumerator = <3>; status = "disabled"; }; gsw_phy3_led1: gsw-phy3-led1@1 { reg = <1>; function = LED_FUNCTION_LAN; + function-enumerator = <3>; status = "disabled"; }; }; @@ -832,12 +847,14 @@ gsw_phy4_led0: gsw-phy4-led0@0 { reg = <0>; function = LED_FUNCTION_LAN; + function-enumerator = <4>; status = "disabled"; }; gsw_phy4_led1: gsw-phy4-led1@1 { reg = <1>; function = LED_FUNCTION_LAN; + function-enumerator = <4>; status = "disabled"; }; }; From 25667811da9f02d3ec82ee9452144383b0cfc1b3 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Tue, 16 Jun 2026 08:12:57 +0300 Subject: [PATCH 080/152] airoha: an7583: remove source-only flag Remove source-only flag from Airoha an7583 subtarget for upcoming Nokia XG-040G-MF support. Also disable builds for airoha_an7583-evb and airoha_an7583-evb-emmc evaluation boards. Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- target/linux/airoha/an7583/target.mk | 2 +- target/linux/airoha/image/an7583.mk | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/target/linux/airoha/an7583/target.mk b/target/linux/airoha/an7583/target.mk index fb8785a189..ed3fc2481f 100644 --- a/target/linux/airoha/an7583/target.mk +++ b/target/linux/airoha/an7583/target.mk @@ -3,7 +3,7 @@ SUBTARGET:=an7583 BOARDNAME:=AN7583 CPU_TYPE:=cortex-a53 KERNELNAME:=Image dtbs -FEATURES+=pwm source-only +FEATURES+=pwm DEFAULT_PACKAGES += \ airoha-an7583-npu-firmware kmod-leds-gpio \ diff --git a/target/linux/airoha/image/an7583.mk b/target/linux/airoha/image/an7583.mk index fef3c3f607..dfa90e58c4 100644 --- a/target/linux/airoha/image/an7583.mk +++ b/target/linux/airoha/image/an7583.mk @@ -15,6 +15,7 @@ define Device/airoha_an7583-evb DEVICE_DTS_CONFIG := config@1 IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | \ pad-rootfs | append-metadata + DEFAULT := n endef TARGET_DEVICES += airoha_an7583-evb @@ -23,5 +24,6 @@ define Device/airoha_an7583-evb-emmc DEVICE_MODEL := AN7583 Evaluation Board (EMMC) DEVICE_DTS := an7583-evb-emmc DEVICE_PACKAGES := kmod-phy-airoha-en8811h kmod-i2c-an7581 + DEFAULT := n endef TARGET_DEVICES += airoha_an7583-evb-emmc From 05c4cb954e481316fea45462c0da174eaef733d2 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Mon, 29 Jun 2026 21:52:26 +0300 Subject: [PATCH 081/152] airoha: move common Nokia XG-040G properties to dtsi files Move shared DTS properties for Nokia XG-040G-MD and XG-040G-MF into common DTSI files to reduce duplication: - an758x-nokia_xg-040g-common.dtsi - an758x-nokia_xg-040g-stock-parts.dtsi - an758x-nokia_xg-040g-ubi-parts.dtsi Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- .../dts/an7581-nokia_xg-040g-md-common.dtsi | 74 +-------- .../dts/an7581-nokia_xg-040g-md-ubi.dts | 76 +-------- .../airoha/dts/an7581-nokia_xg-040g-md.dts | 145 +---------------- .../dts/an758x-nokia_xg-040g-common.dtsi | 73 +++++++++ .../dts/an758x-nokia_xg-040g-stock-parts.dtsi | 147 ++++++++++++++++++ .../dts/an758x-nokia_xg-040g-ubi-parts.dtsi | 78 ++++++++++ 6 files changed, 301 insertions(+), 292 deletions(-) create mode 100644 target/linux/airoha/dts/an758x-nokia_xg-040g-common.dtsi create mode 100644 target/linux/airoha/dts/an758x-nokia_xg-040g-stock-parts.dtsi create mode 100644 target/linux/airoha/dts/an758x-nokia_xg-040g-ubi-parts.dtsi diff --git a/target/linux/airoha/dts/an7581-nokia_xg-040g-md-common.dtsi b/target/linux/airoha/dts/an7581-nokia_xg-040g-md-common.dtsi index aecddc73da..1c1c103b7b 100644 --- a/target/linux/airoha/dts/an7581-nokia_xg-040g-md-common.dtsi +++ b/target/linux/airoha/dts/an7581-nokia_xg-040g-md-common.dtsi @@ -5,26 +5,9 @@ #include #include #include "an7581.dtsi" +#include "an758x-nokia_xg-040g-common.dtsi" / { - aliases { - label-mac-device = &gdm1; - - led-boot = &led_power_green; - led-failsafe = &led_power_green; - led-running = &led_power_green; - led-upgrade = &led_power_green; - - serial0 = &uart1; - }; - - chosen { - bootargs = "console=ttyS0,115200 earlycon"; - stdout-path = "serial0:115200n8"; - linux,usable-memory-range = - <0x0 0x80200000 0x0 0x1fe00000>; - }; - gpio-keys { compatible = "gpio-keys"; @@ -83,11 +66,6 @@ }; }; - memory@80000000 { - device_type = "memory"; - reg = <0x0 0x80000000 0x0 0x20000000>; - }; - reg_3p3v: regulator-3p3v { compatible = "regulator-fixed"; regulator-always-on; @@ -153,18 +131,6 @@ }; }; -ð { - status = "okay"; -}; - -ð_pcs { - status = "okay"; -}; - -&i2c0 { - status = "okay"; -}; - &mdio { /* Airoha EN8811 2.5Gbps phy */ en8811: ethernet-phy@f { @@ -190,44 +156,6 @@ }; }; -&npu { - status = "okay"; -}; - -&pon_pcs { - status = "okay"; -}; - -&snfi { - status = "okay"; -}; - -/* - * SkyHigh ML02G300WHI00 - */ -&spi_nand { - /* - * ECC must be aligned with bootloader to prevent read errors. - * Stock fw also uses NAND_ECC_NONE. - */ - nand-ecc-mode = "none"; - nand-ecc-step-size = <2048>; - nand-ecc-strength = <0>; - - partitions: partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - }; -}; - -&gdm1 { - status = "okay"; - - nvmem-cell-names = "mac-address"; - nvmem-cells = <&macaddr_factory_3e (0)>; -}; - &switch { status = "okay"; diff --git a/target/linux/airoha/dts/an7581-nokia_xg-040g-md-ubi.dts b/target/linux/airoha/dts/an7581-nokia_xg-040g-md-ubi.dts index b61f613e7a..91c66229d9 100644 --- a/target/linux/airoha/dts/an7581-nokia_xg-040g-md-ubi.dts +++ b/target/linux/airoha/dts/an7581-nokia_xg-040g-md-ubi.dts @@ -1,84 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT #include "an7581-nokia_xg-040g-md-common.dtsi" +#include "an758x-nokia_xg-040g-ubi-parts.dtsi" / { model = "Nokia XG-040G-MD (UBI)"; compatible = "nokia,xg-040g-md-ubi", "airoha,an7581", "airoha,en7581"; - - chosen { - rootdisk = <&ubi_fit>; - }; -}; - -&partitions { - partition@0_all { - label = "all_flash"; - reg = <0x0 0x10000000>; - read-only; - }; - - partition@0 { - label = "bl2"; - reg = <0x0 0x20000>; - read-only; - }; - - partition@20000 { - label = "ubi"; - reg = <0x20000 0xffe0000>; - compatible = "linux,ubi"; - - volumes { - ubi-volume-bosa { - volname = "bosa"; - }; - - ubi-volume-factory { - volname = "ri"; - - nvmem-layout { - compatible = "fixed-layout"; - - #address-cells = <1>; - #size-cells = <1>; - - macaddr_factory_3e: macaddr@3e { - compatible = "mac-base"; - reg = <0x3e 0x6>; - #nvmem-cell-cells = <1>; - }; - }; - }; - - ubi_fit: ubi-volume-fit { - volname = "fit"; - }; - - ubi-volume-fip { - volname = "fip"; - }; - - ubi_env: ubi-volume-ubootenv { - volname = "ubootenv"; - }; - - ubi_env2: ubi-volume-ubootenv2 { - volname = "ubootenv2"; - }; - }; - }; -}; - -&ubi_env { - nvmem-layout { - compatible = "u-boot,env-redundant-bool-layout"; - }; -}; - -&ubi_env2 { - nvmem-layout { - compatible = "u-boot,env-redundant-bool-layout"; - }; }; diff --git a/target/linux/airoha/dts/an7581-nokia_xg-040g-md.dts b/target/linux/airoha/dts/an7581-nokia_xg-040g-md.dts index 5676c78ab6..8ccf39a92e 100644 --- a/target/linux/airoha/dts/an7581-nokia_xg-040g-md.dts +++ b/target/linux/airoha/dts/an7581-nokia_xg-040g-md.dts @@ -1,152 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT #include "an7581-nokia_xg-040g-md-common.dtsi" +#include "an758x-nokia_xg-040g-stock-parts.dtsi" / { model = "Nokia XG-040G-MD"; compatible = "nokia,xg-040g-md", "airoha,an7581", "airoha,en7581"; - - virtual_flash { - compatible = "mtd-concat"; - devices = <&ubi4 &ubi1 &ubi2 &ubi3 &ubi5 &ubi6>; - - partitions { - compatible = "fixed-partitions"; - - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "ubi"; - reg = <0x0 0x0>; - }; - }; - }; -}; - -&partitions { - partition@0_all { - label = "all_flash"; - reg = <0x0 0xeba0000>; - read-only; - }; - - partition@0 { - label = "bootloader"; - reg = <0x0 0x80000>; - read-only; - }; - - partition@60000 { - label = "u-boot-env"; - reg = <0x60000 0x20000>; - }; - - partition@80000 { - label = "romfile"; - reg = <0x80000 0x40000>; - read-only; - }; - - partition@c0000 { - label = "nsb_1"; - reg = <0xc0000 0x2880000>; - - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "kernel"; - reg = <0x0 0x800000>; - }; - - ubi1: partition@800000 { - label = "rootfs_1"; - reg = <0x800000 0x2080000>; - }; - }; - - ubi2: partition@2940000 { - label = "nsb_2"; - reg = <0x2940000 0x2880000>; - }; - - partition@51c0000 { - label = "bosa"; - reg = <0x51c0000 0x40000>; - read-only; - }; - - partition@5200000 { - label = "ri"; - reg = <0x5200000 0x40000>; - read-only; - - nvmem-layout { - compatible = "fixed-layout"; - - #address-cells = <1>; - #size-cells = <1>; - - macaddr_factory_3e: macaddr@3e { - compatible = "mac-base"; - reg = <0x3e 0x6>; - #nvmem-cell-cells = <1>; - }; - }; - }; - - partition@5240000 { - label = "flag"; - reg = <0x5240000 0x40000>; - read-only; - }; - - partition@5280000 { - label = "flagback"; - reg = <0x5280000 0x40000>; - read-only; - }; - - ubi3: partition@52c0000 { - label = "config"; - reg = <0x52c0000 0xa00000>; - }; - - ubi4: partition@5cc0000 { - label = "data"; - reg = <0x5cc0000 0x80e0000>; - }; - - ubi5: partition@dda0000 { - label = "oopsfs"; - reg = <0xdda0000 0x400000>; - }; - - partition@e1a0000 { - label = "log"; - reg = <0xe1a0000 0xa00000>; - - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - ubi6: partition@0 { - label = "log_truncated"; - reg = <0x0 0x9c0000>; - }; - - /* - * Must be excluded from ubi. Otherwise the data - * will be broken after U-Boot 'bootflag swap' - * command call. This command writes bootflag at - * 0xeb60000 (boot_flag_addr). - */ - partition@9c0000 { - label = "bootflag"; - reg = <0x9c0000 0x40000>; - read-only; - }; - }; }; diff --git a/target/linux/airoha/dts/an758x-nokia_xg-040g-common.dtsi b/target/linux/airoha/dts/an758x-nokia_xg-040g-common.dtsi new file mode 100644 index 0000000000..dff7cd4ba8 --- /dev/null +++ b/target/linux/airoha/dts/an758x-nokia_xg-040g-common.dtsi @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +/ { + aliases { + label-mac-device = &gdm1; + + led-boot = &led_power_green; + led-failsafe = &led_power_green; + led-running = &led_power_green; + led-upgrade = &led_power_green; + + serial0 = &uart1; + }; + + chosen { + bootargs = "console=ttyS0,115200 earlycon"; + stdout-path = "serial0:115200n8"; + linux,usable-memory-range = + <0x0 0x80200000 0x0 0x1fe00000>; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x20000000>; + }; +}; + +ð { + status = "okay"; +}; + +ð_pcs { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&npu { + status = "okay"; +}; + +&pon_pcs { + status = "okay"; +}; + +&snfi { + status = "okay"; +}; + +&spi_nand { + /* + * ECC must be aligned with bootloader to prevent read errors. + * Stock fw also uses NAND_ECC_NONE. + */ + nand-ecc-mode = "none"; + nand-ecc-step-size = <2048>; + nand-ecc-strength = <0>; + + partitions: partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + }; +}; + +&gdm1 { + status = "okay"; + + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_factory_3e (0)>; +}; diff --git a/target/linux/airoha/dts/an758x-nokia_xg-040g-stock-parts.dtsi b/target/linux/airoha/dts/an758x-nokia_xg-040g-stock-parts.dtsi new file mode 100644 index 0000000000..7c86aec864 --- /dev/null +++ b/target/linux/airoha/dts/an758x-nokia_xg-040g-stock-parts.dtsi @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +/ { + virtual_flash { + compatible = "mtd-concat"; + devices = <&ubi4 &ubi1 &ubi2 &ubi3 &ubi5 &ubi6>; + + partitions { + compatible = "fixed-partitions"; + + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "ubi"; + reg = <0x0 0x0>; + }; + }; + }; +}; + +&partitions { + partition@0_all { + label = "all_flash"; + reg = <0x0 0xeba0000>; + read-only; + }; + + partition@0 { + label = "bootloader"; + reg = <0x0 0x80000>; + read-only; + }; + + partition@60000 { + label = "u-boot-env"; + reg = <0x60000 0x20000>; + }; + + partition@80000 { + label = "romfile"; + reg = <0x80000 0x40000>; + read-only; + }; + + partition@c0000 { + label = "nsb_1"; + reg = <0xc0000 0x2880000>; + + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "kernel"; + reg = <0x0 0x800000>; + }; + + ubi1: partition@800000 { + label = "rootfs_1"; + reg = <0x800000 0x2080000>; + }; + }; + + ubi2: partition@2940000 { + label = "nsb_2"; + reg = <0x2940000 0x2880000>; + }; + + partition@51c0000 { + label = "bosa"; + reg = <0x51c0000 0x40000>; + read-only; + }; + + partition@5200000 { + label = "ri"; + reg = <0x5200000 0x40000>; + read-only; + + nvmem-layout { + compatible = "fixed-layout"; + + #address-cells = <1>; + #size-cells = <1>; + + macaddr_factory_3e: macaddr@3e { + compatible = "mac-base"; + reg = <0x3e 0x6>; + #nvmem-cell-cells = <1>; + }; + }; + }; + + partition@5240000 { + label = "flag"; + reg = <0x5240000 0x40000>; + read-only; + }; + + partition@5280000 { + label = "flagback"; + reg = <0x5280000 0x40000>; + read-only; + }; + + ubi3: partition@52c0000 { + label = "config"; + reg = <0x52c0000 0xa00000>; + }; + + ubi4: partition@5cc0000 { + label = "data"; + reg = <0x5cc0000 0x80e0000>; + }; + + ubi5: partition@dda0000 { + label = "oopsfs"; + reg = <0xdda0000 0x400000>; + }; + + partition@e1a0000 { + label = "log"; + reg = <0xe1a0000 0xa00000>; + + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + ubi6: partition@0 { + label = "log_truncated"; + reg = <0x0 0x9c0000>; + }; + + /* + * Must be excluded from ubi. Otherwise the data + * will be broken after U-Boot 'bootflag swap' + * command call. This command writes bootflag at + * 0xeb60000 (boot_flag_addr). + */ + partition@9c0000 { + label = "bootflag"; + reg = <0x9c0000 0x40000>; + read-only; + }; + }; +}; diff --git a/target/linux/airoha/dts/an758x-nokia_xg-040g-ubi-parts.dtsi b/target/linux/airoha/dts/an758x-nokia_xg-040g-ubi-parts.dtsi new file mode 100644 index 0000000000..7a726322a5 --- /dev/null +++ b/target/linux/airoha/dts/an758x-nokia_xg-040g-ubi-parts.dtsi @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +/ { + chosen { + rootdisk = <&ubi_fit>; + }; +}; + +&partitions { + partition@0_all { + label = "all_flash"; + reg = <0x0 0x10000000>; + read-only; + }; + + partition@0 { + label = "bl2"; + reg = <0x0 0x20000>; + read-only; + }; + + partition@20000 { + label = "ubi"; + reg = <0x20000 0xffe0000>; + compatible = "linux,ubi"; + + volumes { + ubi-volume-bosa { + volname = "bosa"; + }; + + ubi-volume-factory { + volname = "ri"; + + nvmem-layout { + compatible = "fixed-layout"; + + #address-cells = <1>; + #size-cells = <1>; + + macaddr_factory_3e: macaddr@3e { + compatible = "mac-base"; + reg = <0x3e 0x6>; + #nvmem-cell-cells = <1>; + }; + }; + }; + + ubi_fit: ubi-volume-fit { + volname = "fit"; + }; + + ubi-volume-fip { + volname = "fip"; + }; + + ubi_env: ubi-volume-ubootenv { + volname = "ubootenv"; + }; + + ubi_env2: ubi-volume-ubootenv2 { + volname = "ubootenv2"; + }; + }; + }; +}; + +&ubi_env { + nvmem-layout { + compatible = "u-boot,env-redundant-bool-layout"; + }; +}; + +&ubi_env2 { + nvmem-layout { + compatible = "u-boot,env-redundant-bool-layout"; + }; +}; From 42b8e0d9b78177bdc9e370d5a34d0cab3e278521 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Mon, 15 Jun 2026 17:27:49 +0300 Subject: [PATCH 082/152] airoha: add initial support for Nokia XG-040G-MF This commit adds initial support for Nokia XG-040G-MF XG-PON ONU. Specification ------------- - SoC : Airoha AN7583DT (dual-core ARM Cortex-A53 1.2 GHz) - RAM : 512 MiB DDR4 (Winbond W664GG6RB-06) - Flash : SPI-NAND 256 MiB (GigaDevice GD5F2GM7UEYIG) - WLAN : - - Ethernet : 1x 2500 Mbps (LAN1, Airoha EN8811HN) 3x 10/100/1000 Mbps (LAN2-LAN4, Airoha AN7583 SoC switch) - XG-PON : ECONET EN7572, SLIC: MaxLinear PEF32001VSV12 - USB : 1x USB2 (with power control via gpio) 1x USB3 (with power control via gpio) - Buttons : Reset - LEDs : 1x Power (green) 1x WAN (green) 1x WAN (red) 4x LAN (green) 2x USB (green) - Power : 12 VDC, 1 A Unsupported functions, limitations and known issues --------------------------------------------------- 1. XG-PON won't be available after installing OpenWrt. 2. USB ports: AN7583 SoC in OpenWrt doesn't support USB at this time. Installation (UART) ------------------- 1. Attach UART and run picocom: picocom -b 115200 --send-cmd "sb -vv" /dev/ttyUSB0 2. Interrupt boot process by pressing Enter key to enter u-boot Username: telecomadmin Password: nE7jA%5m 3. Start download firmware via ymodem protocol (via UART): loady 0x85000000 4. Press Ctrl + A then Ctrl + S in the picocom terminal (if you are using a different terminal, please refer to the documentation) to start upload OpenWrt initramfs image using ymodem protocol. Enter local path to the OpenWrt initramfs image and press Enter. 5. Load OpenWrt initramfs image from the memory: bootm 0x85000000 5. Sysupgrade with OpenWrt sysupgrade image Alternative for the p.3-4. Load and boot OpenWrt initramfs from tftp IP 192.168.1.254: tftpboot 0x85000000 192.168.1.254:openwrt-initramfs-uImage.itb bootm 0x85000000 Backup and installation (without UART) -------------------------------------- 1. Unplug Fiber 2. Make ONU Reset 3. Navigate: http://192.168.1.1 Username: CMCCAdmin Password: aDm8H%MdA 4. Go to Apps -> Home Storage -> FTP and check Enable FTP 5. Go to Apps -> Home Storage -> SAMBA Sharing and check Enable Samba 6. Visit the following URL to open Telnet: http://192.168.1.1/system.cgi?telnet 7. Telnet account login: Username: user Password: 8. Get root privileges: su user_ftp Password: 9. Copy 'squashfs-factory-kernel.bin', 'squashfs-factory-rootfs.bin' and 'OpenWrt.mtd2.u-boot-env.bin' (backup of 'u-boot-env' partition from the another Nokia XG-040G-MF with OpenWrt installed) 9. Attach USB flash drive and check the new dir in /mnt: ls /mnt 10. Change current dir to the flash drive (change D to your flash drive label): cd /mnt/D 11. Make backup: for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; \ do cat /dev/mtd$i | gzip -9 > mtd$i.bin.gz; done 12. Check image sizes: ls -l | awk '{if(NR>1) {$5=sprintf("0x%x", $5); print}}' 13. Write images: mtd_debug erase /dev/mtd0 0x60000 0x20000 mtd_debug write /dev/mtd0 0x60000 0x20000 OpenWrt.mtd2.u-boot-env.bin mtd_debug erase /dev/mtd14 0x0 0x2880000 mtd_debug write /dev/mtd14 0x0 0x3e6cfc squashfs-factory-kernel.bin mtd_debug erase /dev/mtd11 0x0 0x80e0000 mtd_debug write /dev/mtd11 0x0 0x440000 squashfs-factory-rootfs.bin 0x3e6cfc - size of your 'squashfs-factory-kernel.bin' 0x440000 - size of your 'squashfs-factory-rootfs.bin' 14. Reboot: reboot Recovery -------- The same as described in Installation (UART). Return to stock --------------- 1. Follow the steps 1-5 of the Installation (UART) 2. Once in OpenWrt upload you backup to the /tmp dir of the router: scp -O mtd16.bin.gz root@192.168.1.1:/tmp 5. Connect to the router using ssh and install kmod-mtd-rw: apk update && apk add kmod-mtd-rw insmod mtd-rw i_want_a_brick=1 6. Unlock bootloader: mtd unlock bootloader 7. Restore stock: zcat /tmp/mtd16.bin.gz | mtd write - all_flash 8. Reboot: reboot Stock layout ------------ +-------+------------+---------+---------+ | mtd | label | start | size | +-------+------------+---------+---------+ | mtd0 | bootloader | 0 | 80000 | | mtd1 | romfile | 80000 | 40000 | | mtd14 | nsb_master | c0000 | 2880000 | | mtd15 | nsb_slave | 2940000 | 2880000 | | mtd6 | bosa | 51c0000 | 40000 | | mtd7 | ri | 5200000 | 40000 | | mtd8 | flag | 5240000 | 40000 | | mtd9 | flagback | 5280000 | 40000 | | mtd10 | config | 52c0000 | a00000 | | mtd11 | data | 5cc0000 | 80e0000 | | mtd12 | oopsfs | dda0000 | 400000 | | mtd13 | log | e1a0000 | a00000 | +-------+------------+---------+---------+ UART ---- A view from the back side of the PCB: ------------------------ | GND RX TX | X X X Connection parameters: 115200, 8N1, 3.3V MAC addresses ------------- +---------+-------------------+-----------+ | | MAC | Algorithm | +---------+-------------------+-----------+ | LAN | e0:xx:xx:xx:xx:a0 | label | | WAN | e0:xx:xx:xx:xx:a1 | label + 1 | +---------+-------------------+-----------+ The LAN MAC (hex) was found in 'ri', 0x3e Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23809 Signed-off-by: Jonas Jelonek --- .../uboot-envtools/files/airoha_an7583 | 23 ++ .../an7583/base-files/etc/board.d/01_leds | 21 ++ .../an7583/base-files/etc/board.d/02_network | 3 + .../an7583/base-files/lib/upgrade/platform.sh | 45 +++ .../dts/an7583-nokia_xg-040g-mf-common.dtsi | 276 ++++++++++++++++++ .../airoha/dts/an7583-nokia_xg-040g-mf.dts | 9 + target/linux/airoha/image/an7583.mk | 19 ++ 7 files changed, 396 insertions(+) create mode 100644 package/boot/uboot-tools/uboot-envtools/files/airoha_an7583 create mode 100644 target/linux/airoha/an7583/base-files/etc/board.d/01_leds create mode 100644 target/linux/airoha/an7583/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/airoha/dts/an7583-nokia_xg-040g-mf-common.dtsi create mode 100644 target/linux/airoha/dts/an7583-nokia_xg-040g-mf.dts diff --git a/package/boot/uboot-tools/uboot-envtools/files/airoha_an7583 b/package/boot/uboot-tools/uboot-envtools/files/airoha_an7583 new file mode 100644 index 0000000000..d691a71315 --- /dev/null +++ b/package/boot/uboot-tools/uboot-envtools/files/airoha_an7583 @@ -0,0 +1,23 @@ +# +# Copyright (C) 2025 OpenWrt.org +# + +[ -e /etc/config/ubootenv ] && exit 0 + +touch /etc/config/ubootenv + +. /lib/uboot-envtools.sh +. /lib/functions.sh + +board=$(board_name) + +case "$board" in +nokia,xg-040g-mf) + ubootenv_add_mtd "u-boot-env" "0x1c000" "0x4000" "0x4000" + ;; +esac + +config_load ubootenv +config_foreach ubootenv_add_app_config + +exit 0 diff --git a/target/linux/airoha/an7583/base-files/etc/board.d/01_leds b/target/linux/airoha/an7583/base-files/etc/board.d/01_leds new file mode 100644 index 0000000000..099d51bdbc --- /dev/null +++ b/target/linux/airoha/an7583/base-files/etc/board.d/01_leds @@ -0,0 +1,21 @@ +# +# Copyright (C) 2015 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh + +board_config_update + +board=$(board_name) + +case "$board" in +nokia,xg-040g-mf) + ucidef_set_led_netdev "lan2" "lan2" "mt7530-0:0a:green:lan-2" "lan2" "link tx rx" + ucidef_set_led_netdev "lan3" "lan3" "mt7530-0:0b:green:lan-3" "lan3" "link tx rx" + ucidef_set_led_netdev "lan4" "lan4" "mt7530-0:0c:green:lan-4" "lan4" "link tx rx" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/airoha/an7583/base-files/etc/board.d/02_network b/target/linux/airoha/an7583/base-files/etc/board.d/02_network index 077d193ed5..482a18fbfc 100644 --- a/target/linux/airoha/an7583/base-files/etc/board.d/02_network +++ b/target/linux/airoha/an7583/base-files/etc/board.d/02_network @@ -14,6 +14,9 @@ an7583_setup_interfaces() airoha,an7583-evb) ucidef_set_interface_lan "lan1 lan2 lan3 lan4 eth1" ;; + nokia,xg-040g-mf) + ucidef_set_interface_lan "lan1 lan2 lan3 lan4" + ;; *) echo "Unsupported hardware. Network interfaces not initialized" ;; diff --git a/target/linux/airoha/an7583/base-files/lib/upgrade/platform.sh b/target/linux/airoha/an7583/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..a8557232c2 --- /dev/null +++ b/target/linux/airoha/an7583/base-files/lib/upgrade/platform.sh @@ -0,0 +1,45 @@ +RAMFS_COPY_BIN='fitblk fit_check_sign' + +REQUIRE_IMAGE_METADATA=1 + +nokia_initial_setup() +{ + [ "$(rootfs_type)" = "tmpfs" ] || return 0 + + fw_setenv bootcmd "flash read 0xc0000 0x800000 0x85000000; bootm 0x85000000" +} + +platform_check_image() { + local board=$(board_name) + + case "$board" in + *) + nand_do_platform_check "$board" "$1" + return $? + ;; + esac + + return 0 +} + +platform_do_upgrade() { + local board=$(board_name) + + case "$board" in + *) + nand_do_upgrade "$1" + ;; + esac +} + +platform_pre_upgrade() { + local board=$(board_name) + + case "$board" in + nokia,xg-040g-mf) + nokia_initial_setup + ;; + *) + ;; + esac +} diff --git a/target/linux/airoha/dts/an7583-nokia_xg-040g-mf-common.dtsi b/target/linux/airoha/dts/an7583-nokia_xg-040g-mf-common.dtsi new file mode 100644 index 0000000000..6b61d1ad58 --- /dev/null +++ b/target/linux/airoha/dts/an7583-nokia_xg-040g-mf-common.dtsi @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +/dts-v1/; +#include +#include +#include +#include "an7583.dtsi" +#include "an758x-nokia_xg-040g-common.dtsi" + +/ { + gpio-keys { + compatible = "gpio-keys"; + + button-0 { + label = "reset"; + linux,code = ; + gpios = <&an7583_pinctrl 0 GPIO_ACTIVE_LOW>; + debounce-interval = <60>; + }; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + color = ; + function = LED_FUNCTION_WAN; + gpios = <&an7583_pinctrl 6 GPIO_ACTIVE_LOW>; + }; + + led-1 { + color = ; + function = LED_FUNCTION_WAN_ONLINE; + gpios = <&an7583_pinctrl 7 GPIO_ACTIVE_LOW>; + }; + + led_power_green: led-2 { + color = ; + function = LED_FUNCTION_POWER; + gpios = <&an7583_pinctrl 25 GPIO_ACTIVE_LOW>; + panic-indicator; + }; + + led-3 { + color = ; + function = LED_FUNCTION_USB; + function-enumerator = <1>; + gpios = <&an7583_pinctrl 26 GPIO_ACTIVE_LOW>; + /* + * AN7583 SoC doesn't support USB at this time + * + * linux,default-trigger = "usbport"; + * trigger-sources = <&usb_port1>; + */ + }; + + led-4 { + color = ; + function = LED_FUNCTION_WAN; + gpios = <&an7583_pinctrl 27 GPIO_ACTIVE_LOW>; + }; + + led-5 { + color = ; + function = LED_FUNCTION_USB; + function-enumerator = <2>; + gpios = <&an7583_pinctrl 33 GPIO_ACTIVE_LOW>; + /* + * AN7583 SoC doesn't support USB at this time + * + * linux,default-trigger = "usbport"; + * trigger-sources = <&usb_port2>; + */ + }; + }; + + /* + * AN7583 SoC doesn't support USB at this time + * + * reg_3p3v: regulator-3p3v { + * compatible = "regulator-fixed"; + * regulator-always-on; + * regulator-boot-on; + * regulator-max-microvolt = <3300000>; + * regulator-min-microvolt = <3300000>; + * regulator-name = "fixed-3.3V"; + * }; + * + * reg_usb_5v: regulator-usb-5v { + * compatible = "regulator-fixed"; + * enable-active-high; + * gpio = <&an7583_pinctrl 30 GPIO_ACTIVE_HIGH>; + * regulator-boot-on; + * regulator-max-microvolt = <5000000>; + * regulator-min-microvolt = <5000000>; + * regulator-name = "usb-5v"; + * }; + * + * * + * * Controls both USB ports at once + * * + * usb-power { + * compatible = "regulator-output"; + * vout-supply = <®_usb_5v>; + * }; + */ +}; + +&an7583_pinctrl { + gpio-ranges = <&an7583_pinctrl 0 2 53>; + + mdio0_pins: mdio0-pins { + conf { + pins = "mdio_0"; + output-high; + }; + }; + + pcie0_rst_pins: pcie0-rst-pins { + conf { + pins = "pcie_reset0"; + drive-open-drain = <1>; + }; + }; + + pcie1_rst_pins: pcie1-rst-pins { + conf { + pins = "pcie_reset1"; + drive-open-drain = <1>; + }; + }; + + gswp1_led0_pins: gswp1-led0-pins { + mux { + function = "phy1_led0"; + pins = "gpio1"; + }; + }; + + gswp2_led0_pins: gswp2-led0-pins { + mux { + function = "phy2_led0"; + pins = "gpio2"; + }; + }; + + gswp3_led0_pins: gswp3-led0-pins { + mux { + function = "phy3_led0"; + pins = "gpio3"; + }; + }; + + gswp4_led0_pins: gswp4-led0-pins { + mux { + function = "phy4_led0"; + pins = "gpio4"; + }; + }; +}; + +&mdio_0 { + /* Airoha EN8811 2.5Gbps phy */ + en8811: ethernet-phy@f { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0xf>; + + reset-gpios = <&an7583_pinctrl 34 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <20000>; + + leds { + #address-cells = <1>; + #size-cells = <0>; + + led-0 { + reg = <0>; + color = ; + function = LED_FUNCTION_LAN; + function-enumerator = <1>; + default-state = "keep"; + }; + }; + }; +}; + +&switch { + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&mdio0_pins>; +}; + +&gsw_port1 { + status = "disabled"; +}; + +&gsw_phy1 { + status = "disabled"; +}; + +&gsw_phy2 { + interrupts = <2>; + + pinctrl-names = "gbe-led"; + pinctrl-0 = <&gswp2_led0_pins>; +}; + +&gsw_phy2_led0 { + status = "okay"; + + color = ; + active-low; +}; + +&gsw_phy3 { + interrupts = <3>; + + pinctrl-names = "gbe-led"; + pinctrl-0 = <&gswp3_led0_pins>; +}; + +&gsw_phy3_led0 { + status = "okay"; + + color = ; + active-low; +}; + +&gsw_phy4 { + interrupts = <4>; + + pinctrl-names = "gbe-led"; + pinctrl-0 = <&gswp4_led0_pins>; +}; + +&gsw_phy4_led0 { + status = "okay"; + + color = ; + active-low; +}; + +&gdm3 { + status = "okay"; + + openwrt,netdev-name = "lan1"; + + phy-handle = <&en8811>; + phy-mode = "2500base-x"; + + nvmem-cell-names = "mac-address"; + nvmem-cells = <&macaddr_factory_3e (0)>; +}; + +/* + * AN7583 SoC doesn't support USB at this time + * + * &usb0 { + * status = "okay"; + * #address-cells = <1>; + * #size-cells = <0>; + * + * vusb33-supply = <®_3p3v>; + * + * usb_port1: port@1 { + * reg = <1>; + * #trigger-source-cells = <0>; + * }; + * + * usb_port2: port@2 { + * reg = <2>; + * #trigger-source-cells = <0>; + * }; + * }; + */ diff --git a/target/linux/airoha/dts/an7583-nokia_xg-040g-mf.dts b/target/linux/airoha/dts/an7583-nokia_xg-040g-mf.dts new file mode 100644 index 0000000000..880ae8fa1a --- /dev/null +++ b/target/linux/airoha/dts/an7583-nokia_xg-040g-mf.dts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +#include "an7583-nokia_xg-040g-mf-common.dtsi" +#include "an758x-nokia_xg-040g-stock-parts.dtsi" + +/ { + model = "Nokia XG-040G-MF"; + compatible = "nokia,xg-040g-mf", "airoha,an7583", "airoha,en7583"; +}; diff --git a/target/linux/airoha/image/an7583.mk b/target/linux/airoha/image/an7583.mk index dfa90e58c4..876dc150e8 100644 --- a/target/linux/airoha/image/an7583.mk +++ b/target/linux/airoha/image/an7583.mk @@ -27,3 +27,22 @@ define Device/airoha_an7583-evb-emmc DEFAULT := n endef TARGET_DEVICES += airoha_an7583-evb-emmc + +define Device/nokia_xg-040g-mf + $(call Device/FitImageLzma) + DEVICE_VENDOR := Nokia + DEVICE_MODEL := XG-040G-MF + DEVICE_DTS := an7583-nokia_xg-040g-mf + DEVICE_DTS_CONFIG := config@1 + BLOCKSIZE := 128k + PAGESIZE := 2048 + UBINIZE_OPTS := -E 5 + IMAGE_SIZE := 131968k + KERNEL_SIZE := 8192k + IMAGES += factory-kernel.bin factory-rootfs.bin + IMAGE/factory-kernel.bin := append-kernel + IMAGE/factory-rootfs.bin := append-ubi | check-size + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata + DEVICE_PACKAGES := kmod-phy-airoha-en8811h +endef +TARGET_DEVICES += nokia_xg-040g-mf From e92d07b1130b89f2ce5d2f0b14be860bb03cdcc1 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 11 Jul 2026 16:32:38 -0700 Subject: [PATCH 083/152] ramips: move sound driver to files This is local only. Also has never been upstreamed. Signed-off-by: Rosen Penev Link: https://github.com/openwrt/openwrt/pull/24236 Signed-off-by: Jonas Jelonek --- .../ramips/files/sound/soc/ralink/Kconfig | 8 + .../ramips/files/sound/soc/ralink/Makefile | 6 + .../files/sound/soc/ralink/ralink-i2s.c | 916 +++++++++++++++++ .../835-asoc-add-mt7620-support.patch | 949 +----------------- 4 files changed, 931 insertions(+), 948 deletions(-) create mode 100644 target/linux/ramips/files/sound/soc/ralink/Kconfig create mode 100644 target/linux/ramips/files/sound/soc/ralink/Makefile create mode 100644 target/linux/ramips/files/sound/soc/ralink/ralink-i2s.c diff --git a/target/linux/ramips/files/sound/soc/ralink/Kconfig b/target/linux/ramips/files/sound/soc/ralink/Kconfig new file mode 100644 index 0000000000..86a6e430db --- /dev/null +++ b/target/linux/ramips/files/sound/soc/ralink/Kconfig @@ -0,0 +1,8 @@ +config SND_RALINK_SOC_I2S + depends on RALINK && SND_SOC && !SOC_RT288X + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + tristate "SoC Audio (I2S protocol) for Ralink SoC" + help + Say Y if you want to use I2S protocol and I2S codec on Ralink/MediaTek + based boards. diff --git a/target/linux/ramips/files/sound/soc/ralink/Makefile b/target/linux/ramips/files/sound/soc/ralink/Makefile new file mode 100644 index 0000000000..993ee4235c --- /dev/null +++ b/target/linux/ramips/files/sound/soc/ralink/Makefile @@ -0,0 +1,6 @@ +# +# Ralink/MediaTek Platform Support +# +snd-soc-ralink-i2s-objs := ralink-i2s.o + +obj-$(CONFIG_SND_RALINK_SOC_I2S) += snd-soc-ralink-i2s.o diff --git a/target/linux/ramips/files/sound/soc/ralink/ralink-i2s.c b/target/linux/ramips/files/sound/soc/ralink/ralink-i2s.c new file mode 100644 index 0000000000..d5b405f953 --- /dev/null +++ b/target/linux/ramips/files/sound/soc/ralink/ralink-i2s.c @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2010, Lars-Peter Clausen + * Copyright (C) 2016 Michael Lee + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "ralink-i2s" + +#define I2S_REG_CFG0 0x00 +#define I2S_REG_INT_STATUS 0x04 +#define I2S_REG_INT_EN 0x08 +#define I2S_REG_FF_STATUS 0x0c +#define I2S_REG_WREG 0x10 +#define I2S_REG_RREG 0x14 +#define I2S_REG_CFG1 0x18 +#define I2S_REG_DIVCMP 0x20 +#define I2S_REG_DIVINT 0x24 + +/* I2S_REG_CFG0 */ +#define I2S_REG_CFG0_EN BIT(31) +#define I2S_REG_CFG0_DMA_EN BIT(30) +#define I2S_REG_CFG0_BYTE_SWAP BIT(28) +#define I2S_REG_CFG0_TX_EN BIT(24) +#define I2S_REG_CFG0_RX_EN BIT(20) +#define I2S_REG_CFG0_SLAVE BIT(16) +#define I2S_REG_CFG0_RX_THRES 12 +#define I2S_REG_CFG0_TX_THRES 4 +#define I2S_REG_CFG0_THRES_MASK (0xf << I2S_REG_CFG0_RX_THRES) | \ + (4 << I2S_REG_CFG0_TX_THRES) +#define I2S_REG_CFG0_DFT_THRES (4 << I2S_REG_CFG0_RX_THRES) | \ + (4 << I2S_REG_CFG0_TX_THRES) +/* RT305x */ +#define I2S_REG_CFG0_CLK_DIS BIT(8) +#define I2S_REG_CFG0_TXCH_SWAP BIT(3) +#define I2S_REG_CFG0_TXCH1_OFF BIT(2) +#define I2S_REG_CFG0_TXCH0_OFF BIT(1) +#define I2S_REG_CFG0_SLAVE_EN BIT(0) +/* RT3883 */ +#define I2S_REG_CFG0_RXCH_SWAP BIT(11) +#define I2S_REG_CFG0_RXCH1_OFF BIT(10) +#define I2S_REG_CFG0_RXCH0_OFF BIT(9) +#define I2S_REG_CFG0_WS_INV BIT(0) +/* MT7628 */ +#define I2S_REG_CFG0_FMT_LE BIT(29) +#define I2S_REG_CFG0_SYS_BE BIT(28) +#define I2S_REG_CFG0_NORM_24 BIT(18) +#define I2S_REG_CFG0_DATA_24 BIT(17) + +/* I2S_REG_INT_STATUS */ +#define I2S_REG_INT_RX_FAULT BIT(7) +#define I2S_REG_INT_RX_OVRUN BIT(6) +#define I2S_REG_INT_RX_UNRUN BIT(5) +#define I2S_REG_INT_RX_THRES BIT(4) +#define I2S_REG_INT_TX_FAULT BIT(3) +#define I2S_REG_INT_TX_OVRUN BIT(2) +#define I2S_REG_INT_TX_UNRUN BIT(1) +#define I2S_REG_INT_TX_THRES BIT(0) +#define I2S_REG_INT_TX_MASK 0xf +#define I2S_REG_INT_RX_MASK 0xf0 + +/* I2S_REG_INT_STATUS */ +#define I2S_RX_AVCNT(x) ((x >> 4) & 0xf) +#define I2S_TX_AVCNT(x) (x & 0xf) +/* MT7628 */ +#define MT7628_I2S_RX_AVCNT(x) ((x >> 8) & 0x1f) +#define MT7628_I2S_TX_AVCNT(x) (x & 0x1f) + +/* I2S_REG_CFG1 */ +#define I2S_REG_CFG1_LBK BIT(31) +#define I2S_REG_CFG1_EXTLBK BIT(30) +/* RT3883 */ +#define I2S_REG_CFG1_LEFT_J BIT(0) +#define I2S_REG_CFG1_RIGHT_J BIT(1) +#define I2S_REG_CFG1_FMT_MASK 0x3 + +/* I2S_REG_DIVCMP */ +#define I2S_REG_DIVCMP_CLKEN BIT(31) +#define I2S_REG_DIVCMP_DIVCOMP_MASK 0x1ff + +/* I2S_REG_DIVINT */ +#define I2S_REG_DIVINT_MASK 0x3ff + +/* BCLK dividers */ +#define RALINK_I2S_DIVCMP 0 +#define RALINK_I2S_DIVINT 1 + +/* FIFO */ +#define RALINK_I2S_FIFO_SIZE 32 + +/* feature flags */ +#define RALINK_FLAGS_TXONLY BIT(0) +#define RALINK_FLAGS_LEFT_J BIT(1) +#define RALINK_FLAGS_RIGHT_J BIT(2) +#define RALINK_FLAGS_ENDIAN BIT(3) +#define RALINK_FLAGS_24BIT BIT(4) + +#define RALINK_I2S_INT_EN 0 + +struct ralink_i2s_stats { + u32 dmafault; + u32 overrun; + u32 underrun; + u32 belowthres; +}; + +struct ralink_i2s { + struct device *dev; + void __iomem *regs; + struct clk *clk; + struct regmap *regmap; + u32 flags; + unsigned int fmt; + u16 txdma_req; + u16 rxdma_req; + + struct snd_dmaengine_dai_dma_data playback_dma_data; + struct snd_dmaengine_dai_dma_data capture_dma_data; + + struct dentry *dbg_dir; + struct ralink_i2s_stats txstats; + struct ralink_i2s_stats rxstats; +}; + +static void ralink_i2s_dump_regs(struct ralink_i2s *i2s) +{ + u32 buf[10]; + int ret; + + ret = regmap_bulk_read(i2s->regmap, I2S_REG_CFG0, + buf, ARRAY_SIZE(buf)); + + dev_dbg(i2s->dev, "CFG0: %08x, INTSTAT: %08x, INTEN: %08x, " \ + "FFSTAT: %08x, WREG: %08x, RREG: %08x, " \ + "CFG1: %08x, DIVCMP: %08x, DIVINT: %08x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], + buf[5], buf[6], buf[8], buf[9]); +} + +static int ralink_i2s_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + return 0; +} + +static int ralink_i2s_set_sys_bclk(struct snd_soc_dai *dai, int width, int rate) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned long clk = clk_get_rate(i2s->clk); + int div; + uint32_t data; + + /* disable clock at slave mode */ + if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) == + SND_SOC_DAIFMT_CBP_CFP) { + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_CLK_DIS, + I2S_REG_CFG0_CLK_DIS); + return 0; + } + + /* FREQOUT = FREQIN / (I2S_CLK_DIV + 1) */ + div = (clk / rate ) - 1; + + data = rt_sysc_r32(0x30); + data &= (0xff << 8); + data |= (0x1 << 15) | (div << 8); + rt_sysc_w32(data, 0x30); + + /* enable clock */ + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_CLK_DIS, 0); + + dev_dbg(i2s->dev, "clk: %lu, rate: %u, div: %d\n", + clk, rate, div); + + return 0; +} + +static int ralink_i2s_set_bclk(struct snd_soc_dai *dai, int width, int rate) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned long clk = clk_get_rate(i2s->clk); + int divint, divcomp; + + /* disable clock at slave mode */ + if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) == + SND_SOC_DAIFMT_CBP_CFP) { + regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, + I2S_REG_DIVCMP_CLKEN, 0); + return 0; + } + + /* FREQOUT = FREQIN * (1/2) * (1/(DIVINT + DIVCOMP/512)) */ + clk = clk / (2 * 2 * width); + divint = clk / rate; + divcomp = ((clk % rate) * 512) / rate; + + if ((divint > I2S_REG_DIVINT_MASK) || + (divcomp > I2S_REG_DIVCMP_DIVCOMP_MASK)) + return -EINVAL; + + regmap_update_bits(i2s->regmap, I2S_REG_DIVINT, + I2S_REG_DIVINT_MASK, divint); + regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, + I2S_REG_DIVCMP_DIVCOMP_MASK, divcomp); + + /* enable clock */ + regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, I2S_REG_DIVCMP_CLKEN, + I2S_REG_DIVCMP_CLKEN); + + dev_dbg(i2s->dev, "clk: %lu, rate: %u, int: %d, comp: %d\n", + clk_get_rate(i2s->clk), rate, divint, divcomp); + + return 0; +} + +static int ralink_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int cfg0 = 0, cfg1 = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + if (i2s->flags & RALINK_FLAGS_TXONLY) + cfg0 |= I2S_REG_CFG0_SLAVE_EN; + else + cfg0 |= I2S_REG_CFG0_SLAVE; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_RIGHT_J: + if (i2s->flags & RALINK_FLAGS_RIGHT_J) { + cfg1 |= I2S_REG_CFG1_RIGHT_J; + break; + } + return -EINVAL; + case SND_SOC_DAIFMT_LEFT_J: + if (i2s->flags & RALINK_FLAGS_LEFT_J) { + cfg1 |= I2S_REG_CFG1_LEFT_J; + break; + } + return -EINVAL; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + if (i2s->flags & RALINK_FLAGS_TXONLY) { + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_SLAVE_EN, cfg0); + } else { + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_SLAVE, cfg0); + } + regmap_update_bits(i2s->regmap, I2S_REG_CFG1, + I2S_REG_CFG1_FMT_MASK, cfg1); + i2s->fmt = fmt; + + return 0; +} + +static int ralink_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + if (snd_soc_dai_active(dai)) + return 0; + + /* setup status interrupt */ +#if (RALINK_I2S_INT_EN) + regmap_write(i2s->regmap, I2S_REG_INT_EN, 0xff); +#else + regmap_write(i2s->regmap, I2S_REG_INT_EN, 0x0); +#endif + + /* enable */ + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN | + I2S_REG_CFG0_THRES_MASK, + I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN | + I2S_REG_CFG0_DFT_THRES); + + return 0; +} + +static void ralink_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + /* If both streams are stopped, disable module and clock */ + if (snd_soc_dai_active(dai)) + return; + + /* + * datasheet mention when disable all control regs are cleared + * to initial values. need reinit at startup. + */ + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_EN, 0); +} + +static int ralink_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + int width; + int ret; + + width = params_width(params); + switch (width) { + case 16: + if (i2s->flags & RALINK_FLAGS_24BIT) + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_DATA_24, 0); + break; + case 24: + if (i2s->flags & RALINK_FLAGS_24BIT) { + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_DATA_24, + I2S_REG_CFG0_DATA_24); + break; + } + return -EINVAL; + default: + return -EINVAL; + } + + switch (params_channels(params)) { + case 2: + break; + default: + return -EINVAL; + } + + if (i2s->flags & RALINK_FLAGS_ENDIAN) { + /* system endian */ +#ifdef SNDRV_LITTLE_ENDIAN + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_SYS_BE, 0); +#else + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_SYS_BE, + I2S_REG_CFG0_SYS_BE); +#endif + + /* data endian */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S24_LE: + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_FMT_LE, + I2S_REG_CFG0_FMT_LE); + break; + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S24_BE: + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, + I2S_REG_CFG0_FMT_LE, 0); + break; + default: + return -EINVAL; + } + } + + /* setup bclk rate */ + if (i2s->flags & RALINK_FLAGS_TXONLY) + ret = ralink_i2s_set_sys_bclk(dai, width, params_rate(params)); + else + ret = ralink_i2s_set_bclk(dai, width, params_rate(params)); + + return ret; +} + +static int ralink_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + unsigned int mask, val; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mask = I2S_REG_CFG0_TX_EN; + else + mask = I2S_REG_CFG0_RX_EN; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + val = mask; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(i2s->regmap, I2S_REG_CFG0, mask, val); + + return 0; +} + +static void ralink_i2s_init_dma_data(struct ralink_i2s *i2s, + struct resource *res) +{ + struct snd_dmaengine_dai_dma_data *dma_data; + + /* Playback */ + dma_data = &i2s->playback_dma_data; + dma_data->addr = res->start + I2S_REG_WREG; + dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_data->maxburst = 1; + + if (i2s->flags & RALINK_FLAGS_TXONLY) + return; + + /* Capture */ + dma_data = &i2s->capture_dma_data; + dma_data->addr = res->start + I2S_REG_RREG; + dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_data->maxburst = 1; +} + +static int ralink_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, + &i2s->capture_dma_data); + + return 0; +} + +static int ralink_i2s_dai_remove(struct snd_soc_dai *dai) +{ + return 0; +} + +static const struct snd_soc_dai_ops ralink_i2s_dai_ops = { + .set_sysclk = ralink_i2s_set_sysclk, + .set_fmt = ralink_i2s_set_fmt, + .startup = ralink_i2s_startup, + .shutdown = ralink_i2s_shutdown, + .hw_params = ralink_i2s_hw_params, + .trigger = ralink_i2s_trigger, + .probe = ralink_i2s_dai_probe, + .remove = ralink_i2s_dai_remove, +}; + +static struct snd_soc_dai_driver ralink_i2s_dai = { + .name = DRV_NAME, + .ops = &ralink_i2s_dai_ops, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rate_min = 5512, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .playback = { + .stream_name = "I2S Playback", + .channels_min = 2, + .channels_max = 2, + .rate_min = 5512, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .symmetric_rate = 1, +}; + +static struct snd_pcm_hardware ralink_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = PAGE_SIZE * 2, + .periods_min = 2, + .periods_max = 128, + .buffer_bytes_max = 128 * 1024, + .fifo_size = RALINK_I2S_FIFO_SIZE, +}; + +static const struct snd_dmaengine_pcm_config ralink_dmaengine_pcm_config = { + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .pcm_hardware = &ralink_pcm_hardware, + .prealloc_buffer_size = 256 * PAGE_SIZE, +}; + +static const struct snd_soc_component_driver ralink_i2s_component = { + .name = DRV_NAME, +}; + +static bool ralink_i2s_readable_reg(struct device *dev, unsigned int reg) +{ + return true; +} + +static bool ralink_i2s_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_REG_INT_STATUS: + case I2S_REG_FF_STATUS: + return true; + } + return false; +} + +static bool ralink_i2s_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case I2S_REG_FF_STATUS: + case I2S_REG_RREG: + return false; + } + return true; +} + +static const struct regmap_config ralink_i2s_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .writeable_reg = ralink_i2s_writeable_reg, + .readable_reg = ralink_i2s_readable_reg, + .volatile_reg = ralink_i2s_volatile_reg, + .max_register = I2S_REG_DIVINT, +}; + +#if (RALINK_I2S_INT_EN) +static irqreturn_t ralink_i2s_irq(int irq, void *devid) +{ + struct ralink_i2s *i2s = devid; + u32 status; + + regmap_read(i2s->regmap, I2S_REG_INT_STATUS, &status); + if (unlikely(!status)) + return IRQ_NONE; + + /* tx stats */ + if (status & I2S_REG_INT_TX_MASK) { + if (status & I2S_REG_INT_TX_THRES) + i2s->txstats.belowthres++; + if (status & I2S_REG_INT_TX_UNRUN) + i2s->txstats.underrun++; + if (status & I2S_REG_INT_TX_OVRUN) + i2s->txstats.overrun++; + if (status & I2S_REG_INT_TX_FAULT) + i2s->txstats.dmafault++; + } + + /* rx stats */ + if (status & I2S_REG_INT_RX_MASK) { + if (status & I2S_REG_INT_RX_THRES) + i2s->rxstats.belowthres++; + if (status & I2S_REG_INT_RX_UNRUN) + i2s->rxstats.underrun++; + if (status & I2S_REG_INT_RX_OVRUN) + i2s->rxstats.overrun++; + if (status & I2S_REG_INT_RX_FAULT) + i2s->rxstats.dmafault++; + } + + /* clean status bits */ + regmap_write(i2s->regmap, I2S_REG_INT_STATUS, status); + + return IRQ_HANDLED; +} +#endif + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int ralink_i2s_stats_show(struct seq_file *s, void *unused) +{ + struct ralink_i2s *i2s = s->private; + + seq_printf(s, "tx stats\n"); + seq_printf(s, "\tbelow threshold\t%u\n", i2s->txstats.belowthres); + seq_printf(s, "\tunder run\t%u\n", i2s->txstats.underrun); + seq_printf(s, "\tover run\t%u\n", i2s->txstats.overrun); + seq_printf(s, "\tdma fault\t%u\n", i2s->txstats.dmafault); + + seq_printf(s, "rx stats\n"); + seq_printf(s, "\tbelow threshold\t%u\n", i2s->rxstats.belowthres); + seq_printf(s, "\tunder run\t%u\n", i2s->rxstats.underrun); + seq_printf(s, "\tover run\t%u\n", i2s->rxstats.overrun); + seq_printf(s, "\tdma fault\t%u\n", i2s->rxstats.dmafault); + + ralink_i2s_dump_regs(i2s); + + return 0; +} + +static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s) +{ + i2s->dbg_dir = debugfs_create_dir(dev_name(i2s->dev), NULL); + if (!i2s->dbg_dir) + return -ENOMEM; + + debugfs_create_devm_seqfile(i2s->dev, "stats", i2s->dbg_dir, + &ralink_i2s_stats_show); + + return 0; +} + +static inline void ralink_i2s_debugfs_remove(struct ralink_i2s *i2s) +{ + debugfs_remove(i2s->dbg_dir); +} +#else +static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s) +{ + return 0; +} + +static inline void ralink_i2s_debugfs_remove(struct ralink_i2s *i2s) +{ +} +#endif + +/* + * TODO: these refclk setup functions should use + * clock framework instead. hardcode it now. + */ +static void rt3350_refclk_setup(void) +{ + uint32_t data; + + /* set refclk output 12Mhz clock */ + data = rt_sysc_r32(0x2c); + data |= (0x1 << 8); + rt_sysc_w32(data, 0x2c); +} + +static void rt3883_refclk_setup(void) +{ + uint32_t data; + + /* set refclk output 12Mhz clock */ + data = rt_sysc_r32(0x2c); + data &= ~(0x3 << 13); + data |= (0x1 << 13); + rt_sysc_w32(data, 0x2c); +} + +static void rt3552_refclk_setup(void) +{ + uint32_t data; + + /* set refclk output 12Mhz clock */ + data = rt_sysc_r32(0x2c); + data &= ~(0xf << 8); + data |= (0x3 << 8); + rt_sysc_w32(data, 0x2c); +} + +static void mt7620_refclk_setup(void) +{ + uint32_t data; + + /* set refclk output 12Mhz clock */ + data = rt_sysc_r32(0x2c); + data &= ~(0x7 << 9); + data |= 0x1 << 9; + rt_sysc_w32(data, 0x2c); +} + +static void mt7621_refclk_setup(void) +{ + uint32_t data; + + /* set refclk output 12Mhz clock */ + data = rt_sysc_r32(0x2c); + data &= ~(0x1f << 18); + data |= (0x19 << 18); + data &= ~(0x1f << 12); + data |= (0x1 << 12); + data &= ~(0x7 << 9); + data |= (0x5 << 9); + rt_sysc_w32(data, 0x2c); +} + +static void mt7628_refclk_setup(void) +{ + uint32_t data; + + /* set i2s and refclk digital pad */ + data = rt_sysc_r32(0x3c); + data |= 0x1f; + rt_sysc_w32(data, 0x3c); + + /* Adjust REFCLK0's driving strength */ + data = rt_sysc_r32(0x1354); + data &= ~(0x1 << 5); + rt_sysc_w32(data, 0x1354); + data = rt_sysc_r32(0x1364); + data |= ~(0x1 << 5); + rt_sysc_w32(data, 0x1364); + + /* set refclk output 12Mhz clock */ + data = rt_sysc_r32(0x2c); + data &= ~(0x7 << 9); + data |= 0x1 << 9; + rt_sysc_w32(data, 0x2c); +} + +struct rt_i2s_data { + u32 flags; + void (*refclk_setup)(void); +}; + +struct rt_i2s_data rt3050_i2s_data = { .flags = RALINK_FLAGS_TXONLY }; +struct rt_i2s_data rt3350_i2s_data = { .flags = RALINK_FLAGS_TXONLY, + .refclk_setup = rt3350_refclk_setup }; +struct rt_i2s_data rt3883_i2s_data = { + .flags = (RALINK_FLAGS_LEFT_J | RALINK_FLAGS_RIGHT_J), + .refclk_setup = rt3883_refclk_setup }; +struct rt_i2s_data rt3352_i2s_data = { .refclk_setup = rt3552_refclk_setup}; +struct rt_i2s_data mt7620_i2s_data = { .refclk_setup = mt7620_refclk_setup}; +struct rt_i2s_data mt7621_i2s_data = { .refclk_setup = mt7621_refclk_setup}; +struct rt_i2s_data mt7628_i2s_data = { + .flags = (RALINK_FLAGS_ENDIAN | RALINK_FLAGS_24BIT | + RALINK_FLAGS_LEFT_J), + .refclk_setup = mt7628_refclk_setup}; + +static const struct of_device_id ralink_i2s_match_table[] = { + { .compatible = "ralink,rt3050-i2s", + .data = (void *)&rt3050_i2s_data }, + { .compatible = "ralink,rt3350-i2s", + .data = (void *)&rt3350_i2s_data }, + { .compatible = "ralink,rt3883-i2s", + .data = (void *)&rt3883_i2s_data }, + { .compatible = "ralink,rt3352-i2s", + .data = (void *)&rt3352_i2s_data }, + { .compatible = "mediatek,mt7620-i2s", + .data = (void *)&mt7620_i2s_data }, + { .compatible = "mediatek,mt7621-i2s", + .data = (void *)&mt7621_i2s_data }, + { .compatible = "mediatek,mt7628-i2s", + .data = (void *)&mt7628_i2s_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, ralink_i2s_match_table); + +static int ralink_i2s_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct resource *res; + struct ralink_i2s *i2s; + int irq, ret; + u32 dma_req; + const struct rt_i2s_data *data; + + i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); + if (!i2s) + return -ENOMEM; + + i2s->dev = dev; + + data = of_device_get_match_data(dev); + i2s->flags = data->flags; + /* setup out 12Mhz refclk to codec as mclk */ + if (data->refclk_setup) + data->refclk_setup(); + + if (of_property_read_u32(np, "txdma-req", &dma_req)) { + dev_err(dev, "no txdma-req define\n"); + return -EINVAL; + } + i2s->txdma_req = (u16)dma_req; + if (!(i2s->flags & RALINK_FLAGS_TXONLY)) { + if (of_property_read_u32(np, "rxdma-req", &dma_req)) { + dev_err(dev, "no rxdma-req define\n"); + return -EINVAL; + } + i2s->rxdma_req = (u16)dma_req; + } + + i2s->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(i2s->regs)) + return PTR_ERR(i2s->regs); + + i2s->regmap = devm_regmap_init_mmio(dev, i2s->regs, + &ralink_i2s_regmap_config); + if (IS_ERR(i2s->regmap)) + return dev_err_probe(dev, PTR_ERR(i2s->regmap), "regmap init failed"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "failed to get irq\n"); + return -EINVAL; + } + +#if (RALINK_I2S_INT_EN) + ret = devm_request_irq(dev, irq, ralink_i2s_irq, + 0, dev_name(dev), i2s); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq"); +#endif + + i2s->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(i2s->clk)) + return dev_err_probe(dev, PTR_ERR(i2s->clk), "no clock defined"); + + ralink_i2s_init_dma_data(i2s, res); + + ret = device_reset(dev); + if (ret) + return dev_err_probe(dev, ret, "failed to reset device\n"); + + /* enable 24bits support */ + if (i2s->flags & RALINK_FLAGS_24BIT) { + ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S24_LE; + ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S24_LE; + } + + /* enable big endian support */ + if (i2s->flags & RALINK_FLAGS_ENDIAN) { + ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S16_BE; + ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S16_BE; + ralink_pcm_hardware.formats |= SNDRV_PCM_FMTBIT_S16_BE; + if (i2s->flags & RALINK_FLAGS_24BIT) { + ralink_i2s_dai.capture.formats |= + SNDRV_PCM_FMTBIT_S24_BE; + ralink_i2s_dai.playback.formats |= + SNDRV_PCM_FMTBIT_S24_BE; + ralink_pcm_hardware.formats |= + SNDRV_PCM_FMTBIT_S24_BE; + } + } + + /* disable capture support */ + if (i2s->flags & RALINK_FLAGS_TXONLY) + memset(&ralink_i2s_dai.capture, sizeof(ralink_i2s_dai.capture), + 0); + + ret = devm_snd_soc_register_component(dev, &ralink_i2s_component, + &ralink_i2s_dai, 1); + if (ret) + return ret; + + ret = devm_snd_dmaengine_pcm_register(dev, + &ralink_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); + if (ret) + return ret; + + dev_info(i2s->dev, "mclk %luMHz\n", clk_get_rate(i2s->clk) / 1000000); + + platform_set_drvdata(pdev, i2s); + return ralink_i2s_debugfs_create(i2s); +} + +static void ralink_i2s_remove(struct platform_device *pdev) +{ + struct ralink_i2s *i2s = platform_get_drvdata(pdev); + + ralink_i2s_debugfs_remove(i2s); +} + +static struct platform_driver ralink_i2s_driver = { + .probe = ralink_i2s_probe, + .remove = ralink_i2s_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = ralink_i2s_match_table, + }, +}; +module_platform_driver(ralink_i2s_driver); + +MODULE_AUTHOR("Lars-Peter Clausen, "); +MODULE_DESCRIPTION("Ralink/MediaTek I2S driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/target/linux/ramips/patches-6.18/835-asoc-add-mt7620-support.patch b/target/linux/ramips/patches-6.18/835-asoc-add-mt7620-support.patch index f08c763bd0..ddb3698741 100644 --- a/target/linux/ramips/patches-6.18/835-asoc-add-mt7620-support.patch +++ b/target/linux/ramips/patches-6.18/835-asoc-add-mt7620-support.patch @@ -7,15 +7,7 @@ Signed-off-by: John Crispin --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + - sound/soc/ralink/Kconfig | 15 ++ - sound/soc/ralink/Makefile | 11 + - sound/soc/ralink/mt7620-i2s.c | 436 ++++++++++++++++++++++++++++++++++++++ - sound/soc/ralink/mt7620-wm8960.c | 233 ++++++++++++++++++++ - 7 files changed, 699 insertions(+) - create mode 100644 sound/soc/ralink/Kconfig - create mode 100644 sound/soc/ralink/Makefile - create mode 100644 sound/soc/ralink/mt7620-i2s.c - create mode 100644 sound/soc/ralink/mt7620-wm8960.c + 2 files changed, 2 insertions(+) --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -37,942 +29,3 @@ Signed-off-by: John Crispin obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sdca/ obj-$(CONFIG_SND_SOC) += sof/ ---- /dev/null -+++ b/sound/soc/ralink/Kconfig -@@ -0,0 +1,8 @@ -+config SND_RALINK_SOC_I2S -+ depends on RALINK && SND_SOC && !SOC_RT288X -+ select SND_SOC_GENERIC_DMAENGINE_PCM -+ select REGMAP_MMIO -+ tristate "SoC Audio (I2S protocol) for Ralink SoC" -+ help -+ Say Y if you want to use I2S protocol and I2S codec on Ralink/MediaTek -+ based boards. ---- /dev/null -+++ b/sound/soc/ralink/Makefile -@@ -0,0 +1,6 @@ -+# -+# Ralink/MediaTek Platform Support -+# -+snd-soc-ralink-i2s-objs := ralink-i2s.o -+ -+obj-$(CONFIG_SND_RALINK_SOC_I2S) += snd-soc-ralink-i2s.o ---- /dev/null -+++ b/sound/soc/ralink/ralink-i2s.c -@@ -0,0 +1,916 @@ -+/* -+ * Copyright (C) 2010, Lars-Peter Clausen -+ * Copyright (C) 2016 Michael Lee -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License as published by the -+ * Free Software Foundation; either version 2 of the License, or (at your -+ * option) any later version. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 675 Mass Ave, Cambridge, MA 02139, USA. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define DRV_NAME "ralink-i2s" -+ -+#define I2S_REG_CFG0 0x00 -+#define I2S_REG_INT_STATUS 0x04 -+#define I2S_REG_INT_EN 0x08 -+#define I2S_REG_FF_STATUS 0x0c -+#define I2S_REG_WREG 0x10 -+#define I2S_REG_RREG 0x14 -+#define I2S_REG_CFG1 0x18 -+#define I2S_REG_DIVCMP 0x20 -+#define I2S_REG_DIVINT 0x24 -+ -+/* I2S_REG_CFG0 */ -+#define I2S_REG_CFG0_EN BIT(31) -+#define I2S_REG_CFG0_DMA_EN BIT(30) -+#define I2S_REG_CFG0_BYTE_SWAP BIT(28) -+#define I2S_REG_CFG0_TX_EN BIT(24) -+#define I2S_REG_CFG0_RX_EN BIT(20) -+#define I2S_REG_CFG0_SLAVE BIT(16) -+#define I2S_REG_CFG0_RX_THRES 12 -+#define I2S_REG_CFG0_TX_THRES 4 -+#define I2S_REG_CFG0_THRES_MASK (0xf << I2S_REG_CFG0_RX_THRES) | \ -+ (4 << I2S_REG_CFG0_TX_THRES) -+#define I2S_REG_CFG0_DFT_THRES (4 << I2S_REG_CFG0_RX_THRES) | \ -+ (4 << I2S_REG_CFG0_TX_THRES) -+/* RT305x */ -+#define I2S_REG_CFG0_CLK_DIS BIT(8) -+#define I2S_REG_CFG0_TXCH_SWAP BIT(3) -+#define I2S_REG_CFG0_TXCH1_OFF BIT(2) -+#define I2S_REG_CFG0_TXCH0_OFF BIT(1) -+#define I2S_REG_CFG0_SLAVE_EN BIT(0) -+/* RT3883 */ -+#define I2S_REG_CFG0_RXCH_SWAP BIT(11) -+#define I2S_REG_CFG0_RXCH1_OFF BIT(10) -+#define I2S_REG_CFG0_RXCH0_OFF BIT(9) -+#define I2S_REG_CFG0_WS_INV BIT(0) -+/* MT7628 */ -+#define I2S_REG_CFG0_FMT_LE BIT(29) -+#define I2S_REG_CFG0_SYS_BE BIT(28) -+#define I2S_REG_CFG0_NORM_24 BIT(18) -+#define I2S_REG_CFG0_DATA_24 BIT(17) -+ -+/* I2S_REG_INT_STATUS */ -+#define I2S_REG_INT_RX_FAULT BIT(7) -+#define I2S_REG_INT_RX_OVRUN BIT(6) -+#define I2S_REG_INT_RX_UNRUN BIT(5) -+#define I2S_REG_INT_RX_THRES BIT(4) -+#define I2S_REG_INT_TX_FAULT BIT(3) -+#define I2S_REG_INT_TX_OVRUN BIT(2) -+#define I2S_REG_INT_TX_UNRUN BIT(1) -+#define I2S_REG_INT_TX_THRES BIT(0) -+#define I2S_REG_INT_TX_MASK 0xf -+#define I2S_REG_INT_RX_MASK 0xf0 -+ -+/* I2S_REG_INT_STATUS */ -+#define I2S_RX_AVCNT(x) ((x >> 4) & 0xf) -+#define I2S_TX_AVCNT(x) (x & 0xf) -+/* MT7628 */ -+#define MT7628_I2S_RX_AVCNT(x) ((x >> 8) & 0x1f) -+#define MT7628_I2S_TX_AVCNT(x) (x & 0x1f) -+ -+/* I2S_REG_CFG1 */ -+#define I2S_REG_CFG1_LBK BIT(31) -+#define I2S_REG_CFG1_EXTLBK BIT(30) -+/* RT3883 */ -+#define I2S_REG_CFG1_LEFT_J BIT(0) -+#define I2S_REG_CFG1_RIGHT_J BIT(1) -+#define I2S_REG_CFG1_FMT_MASK 0x3 -+ -+/* I2S_REG_DIVCMP */ -+#define I2S_REG_DIVCMP_CLKEN BIT(31) -+#define I2S_REG_DIVCMP_DIVCOMP_MASK 0x1ff -+ -+/* I2S_REG_DIVINT */ -+#define I2S_REG_DIVINT_MASK 0x3ff -+ -+/* BCLK dividers */ -+#define RALINK_I2S_DIVCMP 0 -+#define RALINK_I2S_DIVINT 1 -+ -+/* FIFO */ -+#define RALINK_I2S_FIFO_SIZE 32 -+ -+/* feature flags */ -+#define RALINK_FLAGS_TXONLY BIT(0) -+#define RALINK_FLAGS_LEFT_J BIT(1) -+#define RALINK_FLAGS_RIGHT_J BIT(2) -+#define RALINK_FLAGS_ENDIAN BIT(3) -+#define RALINK_FLAGS_24BIT BIT(4) -+ -+#define RALINK_I2S_INT_EN 0 -+ -+struct ralink_i2s_stats { -+ u32 dmafault; -+ u32 overrun; -+ u32 underrun; -+ u32 belowthres; -+}; -+ -+struct ralink_i2s { -+ struct device *dev; -+ void __iomem *regs; -+ struct clk *clk; -+ struct regmap *regmap; -+ u32 flags; -+ unsigned int fmt; -+ u16 txdma_req; -+ u16 rxdma_req; -+ -+ struct snd_dmaengine_dai_dma_data playback_dma_data; -+ struct snd_dmaengine_dai_dma_data capture_dma_data; -+ -+ struct dentry *dbg_dir; -+ struct ralink_i2s_stats txstats; -+ struct ralink_i2s_stats rxstats; -+}; -+ -+static void ralink_i2s_dump_regs(struct ralink_i2s *i2s) -+{ -+ u32 buf[10]; -+ int ret; -+ -+ ret = regmap_bulk_read(i2s->regmap, I2S_REG_CFG0, -+ buf, ARRAY_SIZE(buf)); -+ -+ dev_dbg(i2s->dev, "CFG0: %08x, INTSTAT: %08x, INTEN: %08x, " \ -+ "FFSTAT: %08x, WREG: %08x, RREG: %08x, " \ -+ "CFG1: %08x, DIVCMP: %08x, DIVINT: %08x\n", -+ buf[0], buf[1], buf[2], buf[3], buf[4], -+ buf[5], buf[6], buf[8], buf[9]); -+} -+ -+static int ralink_i2s_set_sysclk(struct snd_soc_dai *dai, -+ int clk_id, unsigned int freq, int dir) -+{ -+ return 0; -+} -+ -+static int ralink_i2s_set_sys_bclk(struct snd_soc_dai *dai, int width, int rate) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ unsigned long clk = clk_get_rate(i2s->clk); -+ int div; -+ uint32_t data; -+ -+ /* disable clock at slave mode */ -+ if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) == -+ SND_SOC_DAIFMT_CBP_CFP) { -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_CLK_DIS, -+ I2S_REG_CFG0_CLK_DIS); -+ return 0; -+ } -+ -+ /* FREQOUT = FREQIN / (I2S_CLK_DIV + 1) */ -+ div = (clk / rate ) - 1; -+ -+ data = rt_sysc_r32(0x30); -+ data &= (0xff << 8); -+ data |= (0x1 << 15) | (div << 8); -+ rt_sysc_w32(data, 0x30); -+ -+ /* enable clock */ -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_CLK_DIS, 0); -+ -+ dev_dbg(i2s->dev, "clk: %lu, rate: %u, div: %d\n", -+ clk, rate, div); -+ -+ return 0; -+} -+ -+static int ralink_i2s_set_bclk(struct snd_soc_dai *dai, int width, int rate) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ unsigned long clk = clk_get_rate(i2s->clk); -+ int divint, divcomp; -+ -+ /* disable clock at slave mode */ -+ if ((i2s->fmt & SND_SOC_DAIFMT_MASTER_MASK) == -+ SND_SOC_DAIFMT_CBP_CFP) { -+ regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, -+ I2S_REG_DIVCMP_CLKEN, 0); -+ return 0; -+ } -+ -+ /* FREQOUT = FREQIN * (1/2) * (1/(DIVINT + DIVCOMP/512)) */ -+ clk = clk / (2 * 2 * width); -+ divint = clk / rate; -+ divcomp = ((clk % rate) * 512) / rate; -+ -+ if ((divint > I2S_REG_DIVINT_MASK) || -+ (divcomp > I2S_REG_DIVCMP_DIVCOMP_MASK)) -+ return -EINVAL; -+ -+ regmap_update_bits(i2s->regmap, I2S_REG_DIVINT, -+ I2S_REG_DIVINT_MASK, divint); -+ regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, -+ I2S_REG_DIVCMP_DIVCOMP_MASK, divcomp); -+ -+ /* enable clock */ -+ regmap_update_bits(i2s->regmap, I2S_REG_DIVCMP, I2S_REG_DIVCMP_CLKEN, -+ I2S_REG_DIVCMP_CLKEN); -+ -+ dev_dbg(i2s->dev, "clk: %lu, rate: %u, int: %d, comp: %d\n", -+ clk_get_rate(i2s->clk), rate, divint, divcomp); -+ -+ return 0; -+} -+ -+static int ralink_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ unsigned int cfg0 = 0, cfg1 = 0; -+ -+ /* set master/slave audio interface */ -+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { -+ case SND_SOC_DAIFMT_CBP_CFP: -+ if (i2s->flags & RALINK_FLAGS_TXONLY) -+ cfg0 |= I2S_REG_CFG0_SLAVE_EN; -+ else -+ cfg0 |= I2S_REG_CFG0_SLAVE; -+ break; -+ case SND_SOC_DAIFMT_CBC_CFC: -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* interface format */ -+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { -+ case SND_SOC_DAIFMT_I2S: -+ break; -+ case SND_SOC_DAIFMT_RIGHT_J: -+ if (i2s->flags & RALINK_FLAGS_RIGHT_J) { -+ cfg1 |= I2S_REG_CFG1_RIGHT_J; -+ break; -+ } -+ return -EINVAL; -+ case SND_SOC_DAIFMT_LEFT_J: -+ if (i2s->flags & RALINK_FLAGS_LEFT_J) { -+ cfg1 |= I2S_REG_CFG1_LEFT_J; -+ break; -+ } -+ return -EINVAL; -+ default: -+ return -EINVAL; -+ } -+ -+ /* clock inversion */ -+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { -+ case SND_SOC_DAIFMT_NB_NF: -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (i2s->flags & RALINK_FLAGS_TXONLY) { -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_SLAVE_EN, cfg0); -+ } else { -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_SLAVE, cfg0); -+ } -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG1, -+ I2S_REG_CFG1_FMT_MASK, cfg1); -+ i2s->fmt = fmt; -+ -+ return 0; -+} -+ -+static int ralink_i2s_startup(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ -+ if (snd_soc_dai_active(dai)) -+ return 0; -+ -+ /* setup status interrupt */ -+#if (RALINK_I2S_INT_EN) -+ regmap_write(i2s->regmap, I2S_REG_INT_EN, 0xff); -+#else -+ regmap_write(i2s->regmap, I2S_REG_INT_EN, 0x0); -+#endif -+ -+ /* enable */ -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN | -+ I2S_REG_CFG0_THRES_MASK, -+ I2S_REG_CFG0_EN | I2S_REG_CFG0_DMA_EN | -+ I2S_REG_CFG0_DFT_THRES); -+ -+ return 0; -+} -+ -+static void ralink_i2s_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ -+ /* If both streams are stopped, disable module and clock */ -+ if (snd_soc_dai_active(dai)) -+ return; -+ -+ /* -+ * datasheet mention when disable all control regs are cleared -+ * to initial values. need reinit at startup. -+ */ -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, I2S_REG_CFG0_EN, 0); -+} -+ -+static int ralink_i2s_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ int width; -+ int ret; -+ -+ width = params_width(params); -+ switch (width) { -+ case 16: -+ if (i2s->flags & RALINK_FLAGS_24BIT) -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_DATA_24, 0); -+ break; -+ case 24: -+ if (i2s->flags & RALINK_FLAGS_24BIT) { -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_DATA_24, -+ I2S_REG_CFG0_DATA_24); -+ break; -+ } -+ return -EINVAL; -+ default: -+ return -EINVAL; -+ } -+ -+ switch (params_channels(params)) { -+ case 2: -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (i2s->flags & RALINK_FLAGS_ENDIAN) { -+ /* system endian */ -+#ifdef SNDRV_LITTLE_ENDIAN -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_SYS_BE, 0); -+#else -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_SYS_BE, -+ I2S_REG_CFG0_SYS_BE); -+#endif -+ -+ /* data endian */ -+ switch (params_format(params)) { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ case SNDRV_PCM_FORMAT_S24_LE: -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_FMT_LE, -+ I2S_REG_CFG0_FMT_LE); -+ break; -+ case SNDRV_PCM_FORMAT_S16_BE: -+ case SNDRV_PCM_FORMAT_S24_BE: -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, -+ I2S_REG_CFG0_FMT_LE, 0); -+ break; -+ default: -+ return -EINVAL; -+ } -+ } -+ -+ /* setup bclk rate */ -+ if (i2s->flags & RALINK_FLAGS_TXONLY) -+ ret = ralink_i2s_set_sys_bclk(dai, width, params_rate(params)); -+ else -+ ret = ralink_i2s_set_bclk(dai, width, params_rate(params)); -+ -+ return ret; -+} -+ -+static int ralink_i2s_trigger(struct snd_pcm_substream *substream, int cmd, -+ struct snd_soc_dai *dai) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ unsigned int mask, val; -+ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ mask = I2S_REG_CFG0_TX_EN; -+ else -+ mask = I2S_REG_CFG0_RX_EN; -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ case SNDRV_PCM_TRIGGER_RESUME: -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ val = mask; -+ break; -+ case SNDRV_PCM_TRIGGER_STOP: -+ case SNDRV_PCM_TRIGGER_SUSPEND: -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -+ val = 0; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ regmap_update_bits(i2s->regmap, I2S_REG_CFG0, mask, val); -+ -+ return 0; -+} -+ -+static void ralink_i2s_init_dma_data(struct ralink_i2s *i2s, -+ struct resource *res) -+{ -+ struct snd_dmaengine_dai_dma_data *dma_data; -+ -+ /* Playback */ -+ dma_data = &i2s->playback_dma_data; -+ dma_data->addr = res->start + I2S_REG_WREG; -+ dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ dma_data->maxburst = 1; -+ -+ if (i2s->flags & RALINK_FLAGS_TXONLY) -+ return; -+ -+ /* Capture */ -+ dma_data = &i2s->capture_dma_data; -+ dma_data->addr = res->start + I2S_REG_RREG; -+ dma_data->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ dma_data->maxburst = 1; -+} -+ -+static int ralink_i2s_dai_probe(struct snd_soc_dai *dai) -+{ -+ struct ralink_i2s *i2s = snd_soc_dai_get_drvdata(dai); -+ -+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, -+ &i2s->capture_dma_data); -+ -+ return 0; -+} -+ -+static int ralink_i2s_dai_remove(struct snd_soc_dai *dai) -+{ -+ return 0; -+} -+ -+static const struct snd_soc_dai_ops ralink_i2s_dai_ops = { -+ .set_sysclk = ralink_i2s_set_sysclk, -+ .set_fmt = ralink_i2s_set_fmt, -+ .startup = ralink_i2s_startup, -+ .shutdown = ralink_i2s_shutdown, -+ .hw_params = ralink_i2s_hw_params, -+ .trigger = ralink_i2s_trigger, -+ .probe = ralink_i2s_dai_probe, -+ .remove = ralink_i2s_dai_remove, -+}; -+ -+static struct snd_soc_dai_driver ralink_i2s_dai = { -+ .name = DRV_NAME, -+ .ops = &ralink_i2s_dai_ops, -+ .capture = { -+ .stream_name = "I2S Capture", -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 5512, -+ .rate_max = 192000, -+ .rates = SNDRV_PCM_RATE_CONTINUOUS, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE, -+ }, -+ .playback = { -+ .stream_name = "I2S Playback", -+ .channels_min = 2, -+ .channels_max = 2, -+ .rate_min = 5512, -+ .rate_max = 192000, -+ .rates = SNDRV_PCM_RATE_CONTINUOUS, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE, -+ }, -+ .symmetric_rate = 1, -+}; -+ -+static struct snd_pcm_hardware ralink_pcm_hardware = { -+ .info = SNDRV_PCM_INFO_MMAP | -+ SNDRV_PCM_INFO_MMAP_VALID | -+ SNDRV_PCM_INFO_INTERLEAVED | -+ SNDRV_PCM_INFO_BLOCK_TRANSFER, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE, -+ .channels_min = 2, -+ .channels_max = 2, -+ .period_bytes_min = PAGE_SIZE, -+ .period_bytes_max = PAGE_SIZE * 2, -+ .periods_min = 2, -+ .periods_max = 128, -+ .buffer_bytes_max = 128 * 1024, -+ .fifo_size = RALINK_I2S_FIFO_SIZE, -+}; -+ -+static const struct snd_dmaengine_pcm_config ralink_dmaengine_pcm_config = { -+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, -+ .pcm_hardware = &ralink_pcm_hardware, -+ .prealloc_buffer_size = 256 * PAGE_SIZE, -+}; -+ -+static const struct snd_soc_component_driver ralink_i2s_component = { -+ .name = DRV_NAME, -+}; -+ -+static bool ralink_i2s_readable_reg(struct device *dev, unsigned int reg) -+{ -+ return true; -+} -+ -+static bool ralink_i2s_volatile_reg(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case I2S_REG_INT_STATUS: -+ case I2S_REG_FF_STATUS: -+ return true; -+ } -+ return false; -+} -+ -+static bool ralink_i2s_writeable_reg(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case I2S_REG_FF_STATUS: -+ case I2S_REG_RREG: -+ return false; -+ } -+ return true; -+} -+ -+static const struct regmap_config ralink_i2s_regmap_config = { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .writeable_reg = ralink_i2s_writeable_reg, -+ .readable_reg = ralink_i2s_readable_reg, -+ .volatile_reg = ralink_i2s_volatile_reg, -+ .max_register = I2S_REG_DIVINT, -+}; -+ -+#if (RALINK_I2S_INT_EN) -+static irqreturn_t ralink_i2s_irq(int irq, void *devid) -+{ -+ struct ralink_i2s *i2s = devid; -+ u32 status; -+ -+ regmap_read(i2s->regmap, I2S_REG_INT_STATUS, &status); -+ if (unlikely(!status)) -+ return IRQ_NONE; -+ -+ /* tx stats */ -+ if (status & I2S_REG_INT_TX_MASK) { -+ if (status & I2S_REG_INT_TX_THRES) -+ i2s->txstats.belowthres++; -+ if (status & I2S_REG_INT_TX_UNRUN) -+ i2s->txstats.underrun++; -+ if (status & I2S_REG_INT_TX_OVRUN) -+ i2s->txstats.overrun++; -+ if (status & I2S_REG_INT_TX_FAULT) -+ i2s->txstats.dmafault++; -+ } -+ -+ /* rx stats */ -+ if (status & I2S_REG_INT_RX_MASK) { -+ if (status & I2S_REG_INT_RX_THRES) -+ i2s->rxstats.belowthres++; -+ if (status & I2S_REG_INT_RX_UNRUN) -+ i2s->rxstats.underrun++; -+ if (status & I2S_REG_INT_RX_OVRUN) -+ i2s->rxstats.overrun++; -+ if (status & I2S_REG_INT_RX_FAULT) -+ i2s->rxstats.dmafault++; -+ } -+ -+ /* clean status bits */ -+ regmap_write(i2s->regmap, I2S_REG_INT_STATUS, status); -+ -+ return IRQ_HANDLED; -+} -+#endif -+ -+#if IS_ENABLED(CONFIG_DEBUG_FS) -+static int ralink_i2s_stats_show(struct seq_file *s, void *unused) -+{ -+ struct ralink_i2s *i2s = s->private; -+ -+ seq_printf(s, "tx stats\n"); -+ seq_printf(s, "\tbelow threshold\t%u\n", i2s->txstats.belowthres); -+ seq_printf(s, "\tunder run\t%u\n", i2s->txstats.underrun); -+ seq_printf(s, "\tover run\t%u\n", i2s->txstats.overrun); -+ seq_printf(s, "\tdma fault\t%u\n", i2s->txstats.dmafault); -+ -+ seq_printf(s, "rx stats\n"); -+ seq_printf(s, "\tbelow threshold\t%u\n", i2s->rxstats.belowthres); -+ seq_printf(s, "\tunder run\t%u\n", i2s->rxstats.underrun); -+ seq_printf(s, "\tover run\t%u\n", i2s->rxstats.overrun); -+ seq_printf(s, "\tdma fault\t%u\n", i2s->rxstats.dmafault); -+ -+ ralink_i2s_dump_regs(i2s); -+ -+ return 0; -+} -+ -+static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s) -+{ -+ i2s->dbg_dir = debugfs_create_dir(dev_name(i2s->dev), NULL); -+ if (!i2s->dbg_dir) -+ return -ENOMEM; -+ -+ debugfs_create_devm_seqfile(i2s->dev, "stats", i2s->dbg_dir, -+ &ralink_i2s_stats_show); -+ -+ return 0; -+} -+ -+static inline void ralink_i2s_debugfs_remove(struct ralink_i2s *i2s) -+{ -+ debugfs_remove(i2s->dbg_dir); -+} -+#else -+static inline int ralink_i2s_debugfs_create(struct ralink_i2s *i2s) -+{ -+ return 0; -+} -+ -+static inline void ralink_i2s_debugfs_remove(struct ralink_i2s *i2s) -+{ -+} -+#endif -+ -+/* -+ * TODO: these refclk setup functions should use -+ * clock framework instead. hardcode it now. -+ */ -+static void rt3350_refclk_setup(void) -+{ -+ uint32_t data; -+ -+ /* set refclk output 12Mhz clock */ -+ data = rt_sysc_r32(0x2c); -+ data |= (0x1 << 8); -+ rt_sysc_w32(data, 0x2c); -+} -+ -+static void rt3883_refclk_setup(void) -+{ -+ uint32_t data; -+ -+ /* set refclk output 12Mhz clock */ -+ data = rt_sysc_r32(0x2c); -+ data &= ~(0x3 << 13); -+ data |= (0x1 << 13); -+ rt_sysc_w32(data, 0x2c); -+} -+ -+static void rt3552_refclk_setup(void) -+{ -+ uint32_t data; -+ -+ /* set refclk output 12Mhz clock */ -+ data = rt_sysc_r32(0x2c); -+ data &= ~(0xf << 8); -+ data |= (0x3 << 8); -+ rt_sysc_w32(data, 0x2c); -+} -+ -+static void mt7620_refclk_setup(void) -+{ -+ uint32_t data; -+ -+ /* set refclk output 12Mhz clock */ -+ data = rt_sysc_r32(0x2c); -+ data &= ~(0x7 << 9); -+ data |= 0x1 << 9; -+ rt_sysc_w32(data, 0x2c); -+} -+ -+static void mt7621_refclk_setup(void) -+{ -+ uint32_t data; -+ -+ /* set refclk output 12Mhz clock */ -+ data = rt_sysc_r32(0x2c); -+ data &= ~(0x1f << 18); -+ data |= (0x19 << 18); -+ data &= ~(0x1f << 12); -+ data |= (0x1 << 12); -+ data &= ~(0x7 << 9); -+ data |= (0x5 << 9); -+ rt_sysc_w32(data, 0x2c); -+} -+ -+static void mt7628_refclk_setup(void) -+{ -+ uint32_t data; -+ -+ /* set i2s and refclk digital pad */ -+ data = rt_sysc_r32(0x3c); -+ data |= 0x1f; -+ rt_sysc_w32(data, 0x3c); -+ -+ /* Adjust REFCLK0's driving strength */ -+ data = rt_sysc_r32(0x1354); -+ data &= ~(0x1 << 5); -+ rt_sysc_w32(data, 0x1354); -+ data = rt_sysc_r32(0x1364); -+ data |= ~(0x1 << 5); -+ rt_sysc_w32(data, 0x1364); -+ -+ /* set refclk output 12Mhz clock */ -+ data = rt_sysc_r32(0x2c); -+ data &= ~(0x7 << 9); -+ data |= 0x1 << 9; -+ rt_sysc_w32(data, 0x2c); -+} -+ -+struct rt_i2s_data { -+ u32 flags; -+ void (*refclk_setup)(void); -+}; -+ -+struct rt_i2s_data rt3050_i2s_data = { .flags = RALINK_FLAGS_TXONLY }; -+struct rt_i2s_data rt3350_i2s_data = { .flags = RALINK_FLAGS_TXONLY, -+ .refclk_setup = rt3350_refclk_setup }; -+struct rt_i2s_data rt3883_i2s_data = { -+ .flags = (RALINK_FLAGS_LEFT_J | RALINK_FLAGS_RIGHT_J), -+ .refclk_setup = rt3883_refclk_setup }; -+struct rt_i2s_data rt3352_i2s_data = { .refclk_setup = rt3552_refclk_setup}; -+struct rt_i2s_data mt7620_i2s_data = { .refclk_setup = mt7620_refclk_setup}; -+struct rt_i2s_data mt7621_i2s_data = { .refclk_setup = mt7621_refclk_setup}; -+struct rt_i2s_data mt7628_i2s_data = { -+ .flags = (RALINK_FLAGS_ENDIAN | RALINK_FLAGS_24BIT | -+ RALINK_FLAGS_LEFT_J), -+ .refclk_setup = mt7628_refclk_setup}; -+ -+static const struct of_device_id ralink_i2s_match_table[] = { -+ { .compatible = "ralink,rt3050-i2s", -+ .data = (void *)&rt3050_i2s_data }, -+ { .compatible = "ralink,rt3350-i2s", -+ .data = (void *)&rt3350_i2s_data }, -+ { .compatible = "ralink,rt3883-i2s", -+ .data = (void *)&rt3883_i2s_data }, -+ { .compatible = "ralink,rt3352-i2s", -+ .data = (void *)&rt3352_i2s_data }, -+ { .compatible = "mediatek,mt7620-i2s", -+ .data = (void *)&mt7620_i2s_data }, -+ { .compatible = "mediatek,mt7621-i2s", -+ .data = (void *)&mt7621_i2s_data }, -+ { .compatible = "mediatek,mt7628-i2s", -+ .data = (void *)&mt7628_i2s_data }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ralink_i2s_match_table); -+ -+static int ralink_i2s_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ struct ralink_i2s *i2s; -+ int irq, ret; -+ u32 dma_req; -+ const struct rt_i2s_data *data; -+ -+ i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); -+ if (!i2s) -+ return -ENOMEM; -+ -+ i2s->dev = dev; -+ -+ data = of_device_get_match_data(dev); -+ i2s->flags = data->flags; -+ /* setup out 12Mhz refclk to codec as mclk */ -+ if (data->refclk_setup) -+ data->refclk_setup(); -+ -+ if (of_property_read_u32(np, "txdma-req", &dma_req)) { -+ dev_err(dev, "no txdma-req define\n"); -+ return -EINVAL; -+ } -+ i2s->txdma_req = (u16)dma_req; -+ if (!(i2s->flags & RALINK_FLAGS_TXONLY)) { -+ if (of_property_read_u32(np, "rxdma-req", &dma_req)) { -+ dev_err(dev, "no rxdma-req define\n"); -+ return -EINVAL; -+ } -+ i2s->rxdma_req = (u16)dma_req; -+ } -+ -+ i2s->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); -+ if (IS_ERR(i2s->regs)) -+ return PTR_ERR(i2s->regs); -+ -+ i2s->regmap = devm_regmap_init_mmio(dev, i2s->regs, -+ &ralink_i2s_regmap_config); -+ if (IS_ERR(i2s->regmap)) -+ return dev_err_probe(dev, PTR_ERR(i2s->regmap), "regmap init failed"); -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ dev_err(dev, "failed to get irq\n"); -+ return -EINVAL; -+ } -+ -+#if (RALINK_I2S_INT_EN) -+ ret = devm_request_irq(dev, irq, ralink_i2s_irq, -+ 0, dev_name(dev), i2s); -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to request irq"); -+#endif -+ -+ i2s->clk = devm_clk_get_enabled(dev, NULL); -+ if (IS_ERR(i2s->clk)) -+ return dev_err_probe(dev, PTR_ERR(i2s->clk), "no clock defined"); -+ -+ ralink_i2s_init_dma_data(i2s, res); -+ -+ ret = device_reset(dev); -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to reset device\n"); -+ -+ /* enable 24bits support */ -+ if (i2s->flags & RALINK_FLAGS_24BIT) { -+ ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S24_LE; -+ ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S24_LE; -+ } -+ -+ /* enable big endian support */ -+ if (i2s->flags & RALINK_FLAGS_ENDIAN) { -+ ralink_i2s_dai.capture.formats |= SNDRV_PCM_FMTBIT_S16_BE; -+ ralink_i2s_dai.playback.formats |= SNDRV_PCM_FMTBIT_S16_BE; -+ ralink_pcm_hardware.formats |= SNDRV_PCM_FMTBIT_S16_BE; -+ if (i2s->flags & RALINK_FLAGS_24BIT) { -+ ralink_i2s_dai.capture.formats |= -+ SNDRV_PCM_FMTBIT_S24_BE; -+ ralink_i2s_dai.playback.formats |= -+ SNDRV_PCM_FMTBIT_S24_BE; -+ ralink_pcm_hardware.formats |= -+ SNDRV_PCM_FMTBIT_S24_BE; -+ } -+ } -+ -+ /* disable capture support */ -+ if (i2s->flags & RALINK_FLAGS_TXONLY) -+ memset(&ralink_i2s_dai.capture, sizeof(ralink_i2s_dai.capture), -+ 0); -+ -+ ret = devm_snd_soc_register_component(dev, &ralink_i2s_component, -+ &ralink_i2s_dai, 1); -+ if (ret) -+ return ret; -+ -+ ret = devm_snd_dmaengine_pcm_register(dev, -+ &ralink_dmaengine_pcm_config, -+ SND_DMAENGINE_PCM_FLAG_COMPAT); -+ if (ret) -+ return ret; -+ -+ dev_info(i2s->dev, "mclk %luMHz\n", clk_get_rate(i2s->clk) / 1000000); -+ -+ platform_set_drvdata(pdev, i2s); -+ return ralink_i2s_debugfs_create(i2s); -+} -+ -+static void ralink_i2s_remove(struct platform_device *pdev) -+{ -+ struct ralink_i2s *i2s = platform_get_drvdata(pdev); -+ -+ ralink_i2s_debugfs_remove(i2s); -+} -+ -+static struct platform_driver ralink_i2s_driver = { -+ .probe = ralink_i2s_probe, -+ .remove = ralink_i2s_remove, -+ .driver = { -+ .name = DRV_NAME, -+ .of_match_table = ralink_i2s_match_table, -+ }, -+}; -+module_platform_driver(ralink_i2s_driver); -+ -+MODULE_AUTHOR("Lars-Peter Clausen, "); -+MODULE_DESCRIPTION("Ralink/MediaTek I2S driver"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:" DRV_NAME); From 9146f931b62e9b20df84566187a837b904df1e2f Mon Sep 17 00:00:00 2001 From: Josef Schlehofer Date: Thu, 16 Jul 2026 19:57:35 +0200 Subject: [PATCH 084/152] CI: migrate to webhook bot and simplify labeler This commit migrates the repository validation to a webhook-based bot instead of the GitHub Actions workflow (.github/workflows/formal.yml). Since the bot handles stable branch labeling automatically (via the "branch_labeling: true" configuration option), the redundant branch check and labeling steps are removed from .github/workflows/labeler.yml. The following configuration choices are applied in formalities.json: - check_space_after_assignment: false (Disabled temporarily to catch any issues since it is a newly implemented check) - check_makefile_indentation: false (Disabled temporarily to catch any issues since it is a new feature) - check_patch_headers: 'warning' (Changed to warning instead of hard error since many legacy patches lack headers) - enable_stale_bot: false (Disabled for now) Other configurations can be adjusted in formalities.json based on the openwrt-bot-worker repository [1]. [1] https://github.com/openwrt/openwrt-bot-worker Signed-off-by: Josef Schlehofer Link: https://github.com/openwrt/openwrt/pull/24256 Signed-off-by: Christian Marangi --- .github/formalities.json | 28 ++++++++++++++++++++++++++++ .github/workflows/formal.yml | 18 ------------------ .github/workflows/labeler.yml | 14 -------------- 3 files changed, 28 insertions(+), 32 deletions(-) create mode 100644 .github/formalities.json delete mode 100644 .github/workflows/formal.yml diff --git a/.github/formalities.json b/.github/formalities.json new file mode 100644 index 0000000000..cdbfd200e4 --- /dev/null +++ b/.github/formalities.json @@ -0,0 +1,28 @@ +{ + "check_branch": true, + "check_merge_commits": true, + "check_noreply_email": true, + "check_signoff": true, + "check_signature": true, + "allow_autosquash": true, + "enable_comments": true, + "max_subject_len_soft": 60, + "max_subject_len_hard": 80, + "max_body_line_len": 100, + "warn_duplicate_body": true, + "warn_generic_subjects": true, + "require_release_notes": true, + "check_pkg_version": true, + "check_crlf": true, + "add_package_label": true, + "drop_package_label": true, + "branch_labeling": true, + "check_openwrt_meta": true, + "check_conffiles": true, + "check_patch_headers": "warning", + "check_space_after_assignment": false, + "check_makefile_indentation": false, + "check_pkg_release": "warning", + "require_linked_github_account": true, + "enable_stale_bot": false +} diff --git a/.github/workflows/formal.yml b/.github/workflows/formal.yml deleted file mode 100644 index d57b17a562..0000000000 --- a/.github/workflows/formal.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Test Formalities - -on: - pull_request: - -permissions: - contents: read - pull-requests: write - -jobs: - build: - name: Test Formalities - uses: openwrt/actions-shared-workflows/.github/workflows/formal.yml@main - # with: - # # Post formality check summaries to the PR. - # # Repo's permissions need to be updated for actions to modify PRs: - # # https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment - # post_comment: true diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 513964de66..b64d3e4a2b 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -17,17 +17,3 @@ jobs: - uses: actions/labeler@v6 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' - - - name: Check Branch - id: check-branch - run: | - if echo "${{ github.base_ref }}" | grep -q -E 'openwrt-[0-9][0-9]\.[0-9][0-9]'; then - echo "release-tag=$(echo ${{ github.base_ref }} | sed 's/openwrt-/release\//')" >> $GITHUB_OUTPUT - fi - - - uses: buildsville/add-remove-label@v2.0.1 - if: ${{ steps.check-branch.outputs.release-tag }} - with: - token: ${{secrets.GITHUB_TOKEN}} - labels: ${{ steps.check-branch.outputs.release-tag }} - type: add From 4f2dc5cc6497a6cef0a43bdefde522e734c1f40d Mon Sep 17 00:00:00 2001 From: Josef Schlehofer Date: Thu, 16 Jul 2026 23:52:35 +0200 Subject: [PATCH 085/152] CI: remove labeler workflow and enable webhook-based labeling Remove the .github/workflows/labeler.yml workflow and enable the "enable_labeler_yml" option in .github/formalities.json to let the webhook bot handle pull request labeling. This avoids running on GitHub Actions, so it is faster and uses fewer resources. It uses the existing .github/labeler.yml for labels. Signed-off-by: Josef Schlehofer Link: https://github.com/openwrt/openwrt/pull/24256 Signed-off-by: Christian Marangi --- .github/formalities.json | 3 ++- .github/workflows/labeler.yml | 19 ------------------- 2 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 .github/workflows/labeler.yml diff --git a/.github/formalities.json b/.github/formalities.json index cdbfd200e4..1face8ed12 100644 --- a/.github/formalities.json +++ b/.github/formalities.json @@ -24,5 +24,6 @@ "check_makefile_indentation": false, "check_pkg_release": "warning", "require_linked_github_account": true, - "enable_stale_bot": false + "enable_stale_bot": false, + "enable_labeler_yml": true } diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index b64d3e4a2b..0000000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 'Pull Request Labeler' -on: - - pull_request_target - -permissions: - contents: read - -jobs: - labeler: - permissions: - contents: read # to determine modified files (actions/labeler) - pull-requests: write # to add labels to PRs (actions/labeler) - - name: Pull Request Labeler - runs-on: ubuntu-latest - steps: - - uses: actions/labeler@v6 - with: - repo-token: '${{ secrets.GITHUB_TOKEN }}' From 49f051203645c1d857d1783073d727e087d14f7f Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Sat, 18 Jul 2026 19:10:21 +0200 Subject: [PATCH 086/152] realtek: dts: more ethernet-phy-package conversion Encapsulate some more multiport PHYs into their packages. Link: https://github.com/openwrt/openwrt/pull/24281 Signed-off-by: Markus Stockhausen --- .../dts/rtl8392_zyxel_gs1920-24hp-v1.dts | 15 +- .../realtek/dts/rtl8393_netgear_gs750e.dts | 134 +++++++++++------- .../dts/rtl839x_zyxel_gs1920-24hp-common.dtsi | 67 +++++---- .../rtl9301_linksys_lgs328x_nand_common.dtsi | 66 +++++---- 4 files changed, 172 insertions(+), 110 deletions(-) diff --git a/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts b/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts index 289dc73306..1269cc34e4 100644 --- a/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts +++ b/target/linux/realtek/dts/rtl8392_zyxel_gs1920-24hp-v1.dts @@ -71,11 +71,16 @@ }; &mdio_bus1 { - /* External phy RTL8214FC #1 */ - PHY_C22_SFP(48, 24, 0) - PHY_C22_SFP(49, 25, 1) - PHY_C22_SFP(50, 26, 2) - PHY_C22_SFP(51, 27, 3) + ethernet-phy-package@24 { + #address-cells = <1>; + #size-cells = <0>; + reg = <24>; + + PHY_C22_SFP(48, 24, 0) + PHY_C22_SFP(49, 25, 1) + PHY_C22_SFP(50, 26, 2) + PHY_C22_SFP(51, 27, 3) + }; }; &pse { diff --git a/target/linux/realtek/dts/rtl8393_netgear_gs750e.dts b/target/linux/realtek/dts/rtl8393_netgear_gs750e.dts index 0203b4ecc8..1ba0ac4573 100644 --- a/target/linux/realtek/dts/rtl8393_netgear_gs750e.dts +++ b/target/linux/realtek/dts/rtl8393_netgear_gs750e.dts @@ -106,67 +106,93 @@ // Switch doesn't come back properly after a reset so don't. // reset-gpios = <&gpio0 23 GPIO_ACTIVE_LOW>; - /* External phy RTL8218B #1 */ - PHY_C22(0, 0) - PHY_C22(1, 1) - PHY_C22(2, 2) - PHY_C22(3, 3) - PHY_C22(4, 4) - PHY_C22(5, 5) - PHY_C22(6, 6) - PHY_C22(7, 7) + ethernet-phy-package@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; - /* External phy RTL8218B #2 */ - PHY_C22(8, 8) - PHY_C22(9, 9) - PHY_C22(10, 10) - PHY_C22(11, 11) - PHY_C22(12, 12) - PHY_C22(13, 13) - PHY_C22(14, 14) - PHY_C22(15, 15) + PHY_C22(0, 0) + PHY_C22(1, 1) + PHY_C22(2, 2) + PHY_C22(3, 3) + PHY_C22(4, 4) + PHY_C22(5, 5) + PHY_C22(6, 6) + PHY_C22(7, 7) + }; + ethernet-phy-package@8 { + #address-cells = <1>; + #size-cells = <0>; + reg = <8>; - /* External phy RTL8218B #3 */ - PHY_C22(16, 16) - PHY_C22(17, 17) - PHY_C22(18, 18) - PHY_C22(19, 19) - PHY_C22(20, 20) - PHY_C22(21, 21) - PHY_C22(22, 22) - PHY_C22(23, 23) + PHY_C22(8, 8) + PHY_C22(9, 9) + PHY_C22(10, 10) + PHY_C22(11, 11) + PHY_C22(12, 12) + PHY_C22(13, 13) + PHY_C22(14, 14) + PHY_C22(15, 15) + }; + ethernet-phy-package@16 { + #address-cells = <1>; + #size-cells = <0>; + reg = <16>; + + PHY_C22(16, 16) + PHY_C22(17, 17) + PHY_C22(18, 18) + PHY_C22(19, 19) + PHY_C22(20, 20) + PHY_C22(21, 21) + PHY_C22(22, 22) + PHY_C22(23, 23) + }; }; &mdio_bus1 { - /* External phy RTL8218B #4 */ - PHY_C22(24, 0) - PHY_C22(25, 1) - PHY_C22(26, 2) - PHY_C22(27, 3) - PHY_C22(28, 4) - PHY_C22(29, 5) - PHY_C22(30, 6) - PHY_C22(31, 7) + ethernet-phy-package@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; - /* External phy RTL8218B #5 */ - PHY_C22(32, 8) - PHY_C22(33, 9) - PHY_C22(34, 10) - PHY_C22(35, 11) - PHY_C22(36, 12) - PHY_C22(37, 13) - PHY_C22(38, 14) - PHY_C22(39, 15) + PHY_C22(24, 0) + PHY_C22(25, 1) + PHY_C22(26, 2) + PHY_C22(27, 3) + PHY_C22(28, 4) + PHY_C22(29, 5) + PHY_C22(30, 6) + PHY_C22(31, 7) + }; + ethernet-phy-package@8 { + #address-cells = <1>; + #size-cells = <0>; + reg = <8>; - /* External phy RTL8218B #6 */ - PHY_C22(40, 16) - PHY_C22(41, 17) - PHY_C22(42, 18) - PHY_C22(43, 19) - PHY_C22(44, 20) - PHY_C22(45, 21) - PHY_C22(46, 22) - PHY_C22(47, 23) + PHY_C22(32, 8) + PHY_C22(33, 9) + PHY_C22(34, 10) + PHY_C22(35, 11) + PHY_C22(36, 12) + PHY_C22(37, 13) + PHY_C22(38, 14) + PHY_C22(39, 15) + }; + ethernet-phy-package@16 { + #address-cells = <1>; + #size-cells = <0>; + reg = <16>; + + PHY_C22(40, 16) + PHY_C22(41, 17) + PHY_C22(42, 18) + PHY_C22(43, 19) + PHY_C22(44, 20) + PHY_C22(45, 21) + PHY_C22(46, 22) + PHY_C22(47, 23) + }; }; &switch0 { diff --git a/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi b/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi index eba9350c4d..08f6c463f8 100644 --- a/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi +++ b/target/linux/realtek/dts/rtl839x_zyxel_gs1920-24hp-common.dtsi @@ -186,35 +186,48 @@ }; &mdio_bus0 { - /* External phy RTL8218B #1 */ - PHY_C22(0, 0) - PHY_C22(1, 1) - PHY_C22(2, 2) - PHY_C22(3, 3) - PHY_C22(4, 4) - PHY_C22(5, 5) - PHY_C22(6, 6) - PHY_C22(7, 7) + ethernet-phy-package@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; - /* External phy RTL8218B #2 */ - PHY_C22(8, 8) - PHY_C22(9, 9) - PHY_C22(10, 10) - PHY_C22(11, 11) - PHY_C22(12, 12) - PHY_C22(13, 13) - PHY_C22(14, 14) - PHY_C22(15, 15) + PHY_C22(0, 0) + PHY_C22(1, 1) + PHY_C22(2, 2) + PHY_C22(3, 3) + PHY_C22(4, 4) + PHY_C22(5, 5) + PHY_C22(6, 6) + PHY_C22(7, 7) + }; + ethernet-phy-package@8 { + #address-cells = <1>; + #size-cells = <0>; + reg = <8>; - /* External phy RTL8218B #3 */ - PHY_C22(16, 16) - PHY_C22(17, 17) - PHY_C22(18, 18) - PHY_C22(19, 19) - PHY_C22(20, 20) - PHY_C22(21, 21) - PHY_C22(22, 22) - PHY_C22(23, 23) + PHY_C22(8, 8) + PHY_C22(9, 9) + PHY_C22(10, 10) + PHY_C22(11, 11) + PHY_C22(12, 12) + PHY_C22(13, 13) + PHY_C22(14, 14) + PHY_C22(15, 15) + }; + ethernet-phy-package@16 { + #address-cells = <1>; + #size-cells = <0>; + reg = <16>; + + PHY_C22(16, 16) + PHY_C22(17, 17) + PHY_C22(18, 18) + PHY_C22(19, 19) + PHY_C22(20, 20) + PHY_C22(21, 21) + PHY_C22(22, 22) + PHY_C22(23, 23) + }; }; &switch0 { diff --git a/target/linux/realtek/dts/rtl9301_linksys_lgs328x_nand_common.dtsi b/target/linux/realtek/dts/rtl9301_linksys_lgs328x_nand_common.dtsi index c38272bd08..c34b6ebb7e 100644 --- a/target/linux/realtek/dts/rtl9301_linksys_lgs328x_nand_common.dtsi +++ b/target/linux/realtek/dts/rtl9301_linksys_lgs328x_nand_common.dtsi @@ -47,36 +47,54 @@ }; &mdio_bus0 { - PHY_C22(0, 0) - PHY_C22(1, 1) - PHY_C22(2, 2) - PHY_C22(3, 3) - PHY_C22(4, 4) - PHY_C22(5, 5) - PHY_C22(6, 6) - PHY_C22(7, 7) + ethernet-phy-package@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + PHY_C22(0, 0) + PHY_C22(1, 1) + PHY_C22(2, 2) + PHY_C22(3, 3) + PHY_C22(4, 4) + PHY_C22(5, 5) + PHY_C22(6, 6) + PHY_C22(7, 7) + }; }; &mdio_bus1 { - PHY_C22(8, 8) - PHY_C22(9, 9) - PHY_C22(10, 10) - PHY_C22(11, 11) - PHY_C22(12, 12) - PHY_C22(13, 13) - PHY_C22(14, 14) - PHY_C22(15, 15) + ethernet-phy-package@8 { + #address-cells = <1>; + #size-cells = <0>; + reg = <8>; + + PHY_C22(8, 8) + PHY_C22(9, 9) + PHY_C22(10, 10) + PHY_C22(11, 11) + PHY_C22(12, 12) + PHY_C22(13, 13) + PHY_C22(14, 14) + PHY_C22(15, 15) + }; }; &mdio_bus2 { - PHY_C22(16, 16) - PHY_C22(17, 17) - PHY_C22(18, 18) - PHY_C22(19, 19) - PHY_C22(20, 20) - PHY_C22(21, 21) - PHY_C22(22, 22) - PHY_C22(23, 23) + ethernet-phy-package@16 { + #address-cells = <1>; + #size-cells = <0>; + reg = <16>; + + PHY_C22(16, 16) + PHY_C22(17, 17) + PHY_C22(18, 18) + PHY_C22(19, 19) + PHY_C22(20, 20) + PHY_C22(21, 21) + PHY_C22(22, 22) + PHY_C22(23, 23) + }; }; &switch0 { From e26eccaa3c2273066451197f85ca8c666386990a Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Sun, 19 Jul 2026 08:25:07 +0200 Subject: [PATCH 087/152] realtek: dts: adapt RTL8214FC bus location on RTL839x The RTL839x has the following hard-coded bus topology: - Bus 0: Port 0-23 - Bus 1: Port 24-51 So the bus number and the PHY address in the dts are mostly ignored. Nevertheless fix the assignment for the Zyxel GS1920. This does not change the functionality it just describes the hardware correctly. While we are here add the PHY package description too. Link: https://github.com/openwrt/openwrt/pull/24281 Signed-off-by: Markus Stockhausen --- .../dts/rtl8391_zyxel_gs1920-24hp-v2.dts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts b/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts index 3aab3d8072..f872bd22c4 100644 --- a/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts +++ b/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts @@ -116,12 +116,17 @@ }; }; -&mdio_bus0 { - /* External phy RTL8214FC #1 */ - PHY_C22_SFP(24, 24, 0) - PHY_C22_SFP(25, 25, 1) - PHY_C22_SFP(26, 26, 2) - PHY_C22_SFP(27, 27, 3) +&mdio_bus1 { + ethernet-phy-package@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + PHY_C22_SFP(24, 0, 0) + PHY_C22_SFP(25, 1, 1) + PHY_C22_SFP(26, 2, 2) + PHY_C22_SFP(27, 3, 3) + }; }; &pse { From 101fcccac559b5fa503d363e1c139ac7733194ee Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 22:08:34 +0200 Subject: [PATCH 088/152] realtek: gs1900-10hp: move A1 PoE to in-kernel PSE driver The GS1900-10HP A1 (Broadcom BCM59121 behind a Gen1 MCU on UART) was driven by the userspace realtek-poe daemon. Move it to the in-kernel realtek-pse-mcu (Gen1) driver so PoE is managed through the standard PSE-PD / ethtool interface, and factor the board description shared with the upcoming B1 variant into rtl8380_zyxel_gs1900-10hp.dtsi. Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/24283 Signed-off-by: Jonas Jelonek --- .../realtek/base-files/etc/board.d/02_network | 3 - .../dts/rtl8380_zyxel_gs1900-10hp-a1.dts | 85 +------------ .../dts/rtl8380_zyxel_gs1900-10hp.dtsi | 114 ++++++++++++++++++ target/linux/realtek/image/rtl838x.mk | 2 +- 4 files changed, 119 insertions(+), 85 deletions(-) create mode 100644 target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dtsi 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 899f3f117d..98389c522b 100644 --- a/target/linux/realtek/base-files/etc/board.d/02_network +++ b/target/linux/realtek/base-files/etc/board.d/02_network @@ -229,9 +229,6 @@ realtek_setup_poe() netgear,gs310tp-v1) ucidef_set_poe 55 "$(filter_port_list "$lan_list" "lan9 lan10")" ;; - zyxel,gs1900-10hp-a1) - ucidef_set_poe 77 "$(filter_port_list "$lan_list" "lan9 lan10")" - ;; zyxel,gs1900-8hp-a1|\ zyxel,gs1900-8hp-b1) ucidef_set_poe 70 "$lan_list" diff --git a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-a1.dts b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-a1.dts index 89112c3cc9..e83f2c088e 100644 --- a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-a1.dts +++ b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-a1.dts @@ -1,90 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include "rtl8380_zyxel_gs1900.dtsi" -#include "rtl8380_zyxel_gs1900_gpio.dtsi" +#include "rtl8380_zyxel_gs1900-10hp.dtsi" / { compatible = "zyxel,gs1900-10hp-a1", "realtek,rtl838x-soc"; model = "Zyxel GS1900-10HP A1 Switch"; - - /* i2c of the left SFP cage: port 9 */ - i2c0: i2c-gpio-0 { - compatible = "i2c-gpio"; - sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - #address-cells = <1>; - #size-cells = <0>; - }; - - sfp0: sfp-p9 { - compatible = "sff,sfp"; - i2c-bus = <&i2c0>; - los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; - tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; - mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>; - tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; - #thermal-sensor-cells = <0>; - }; - - /* i2c of the right SFP cage: port 10 */ - i2c1: i2c-gpio-1 { - compatible = "i2c-gpio"; - sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - #address-cells = <1>; - #size-cells = <0>; - }; - - sfp1: sfp-p10 { - compatible = "sff,sfp"; - i2c-bus = <&i2c1>; - los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>; - tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; - mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>; - tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>; - #thermal-sensor-cells = <0>; - }; }; -&uart1 { - status = "okay"; -}; - -&switch0 { - ethernet-ports { - port@24 { - reg = <24>; - label = "lan9"; - pcs-handle = <&serdes4 0>; - phy-mode = "1000base-x"; - managed = "in-band-status"; - sfp = <&sfp0>; - }; - - port@26 { - reg = <26>; - label = "lan10"; - pcs-handle = <&serdes5 0>; - phy-mode = "1000base-x"; - managed = "in-band-status"; - sfp = <&sfp1>; - }; - }; -}; - -&thermal_zones { - sfp-thermal { - polling-delay-passive = <10000>; - polling-delay = <10000>; - thermal-sensors = <&sfp0>, <&sfp1>; - trips { - sfp-crit { - temperature = <110000>; - hysteresis = <1000>; - type = "critical"; - }; - }; - }; +&pse { + compatible = "zyxel,gs1900-10hp-a1-pse", "realtek,pse-mcu-gen1"; + current-speed = <19200>; }; diff --git a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dtsi b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dtsi new file mode 100644 index 0000000000..3cef27aa4f --- /dev/null +++ b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp.dtsi @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900.dtsi" +#include "rtl8380_zyxel_gs1900_gpio.dtsi" + +/ { + /* i2c of the left SFP cage: port 9 */ + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 25 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp0: sfp-p9 { + compatible = "sff,sfp"; + i2c-bus = <&i2c0>; + los-gpio = <&gpio1 27 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 26 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 23 GPIO_ACTIVE_HIGH>; + #thermal-sensor-cells = <0>; + }; + + /* i2c of the right SFP cage: port 10 */ + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 30 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + sfp1: sfp-p10 { + compatible = "sff,sfp"; + i2c-bus = <&i2c1>; + los-gpio = <&gpio1 33 GPIO_ACTIVE_HIGH>; + tx-fault-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; + mod-def0-gpio = <&gpio1 32 GPIO_ACTIVE_LOW>; + tx-disable-gpio = <&gpio1 29 GPIO_ACTIVE_HIGH>; + #thermal-sensor-cells = <0>; + }; +}; + +&uart1 { + status = "okay"; + + /* PoE MCU; compatible and current-speed are set per variant */ + pse: ethernet-pse { + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) + }; + }; +}; + +&switch0 { + ethernet-ports { + port@24 { + reg = <24>; + label = "lan9"; + pcs-handle = <&serdes4 0>; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp0>; + }; + + port@26 { + reg = <26>; + label = "lan10"; + pcs-handle = <&serdes5 0>; + phy-mode = "1000base-x"; + managed = "in-band-status"; + sfp = <&sfp1>; + }; + }; +}; + +&thermal_zones { + sfp-thermal { + polling-delay-passive = <10000>; + polling-delay = <10000>; + thermal-sensors = <&sfp0>, <&sfp1>; + trips { + sfp-crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; +}; + +/* Copper ports lan1..lan8 (phy8..phy15) -> MCU PoE ports 0..7 */ +&phy8 { pses = <&pse_pi0>; }; +&phy9 { pses = <&pse_pi1>; }; +&phy10 { pses = <&pse_pi2>; }; +&phy11 { pses = <&pse_pi3>; }; +&phy12 { pses = <&pse_pi4>; }; +&phy13 { pses = <&pse_pi5>; }; +&phy14 { pses = <&pse_pi6>; }; +&phy15 { pses = <&pse_pi7>; }; diff --git a/target/linux/realtek/image/rtl838x.mk b/target/linux/realtek/image/rtl838x.mk index 73e282b6ed..a8b11c2c23 100644 --- a/target/linux/realtek/image/rtl838x.mk +++ b/target/linux/realtek/image/rtl838x.mk @@ -391,7 +391,7 @@ define Device/zyxel_gs1900-10hp-a1 DEVICE_MODEL := GS1900-10HP DEVICE_VARIANT := A1 ZYXEL_VERS := AAZI - DEVICE_PACKAGES += realtek-poe + DEVICE_PACKAGES += kmod-pse-realtek-mcu-uart SUPPORTED_DEVICES += zyxel,gs1900-10hp endef TARGET_DEVICES += zyxel_gs1900-10hp-a1 From 13cb7086913e75a95994f986b3d1c97a1a98bcbc Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 22:09:03 +0200 Subject: [PATCH 089/152] realtek: add support for Zyxel GS1900-10HP B1 The GS1900-10HP B1 keeps the RTL8380M SoC and port layout but replaces the A1's Broadcom PoE (Gen1) with a Realtek RTL8238 PSE fronted by a Nuvoton MCU speaking the Gen2 protocol at 115200 baud. It reuses the shared rtl8380_zyxel_gs1900-10hp.dtsi and only overrides the PSE compatible and UART speed. Specifications: - SoC: Realtek RTL8380M - Ports: 8x 10/100/1000 (PoE+), 2x SFP - PoE: Realtek RTL8238 via Nuvoton MCU (Gen2, UART 115200) Tested on hardware: driver probes RTL8238B (8 ports), per-port control via ethtool, verified across a cold power-cycle. Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/24283 Signed-off-by: Jonas Jelonek --- .../boot/uboot-tools/uboot-envtools/files/realtek | 1 + .../linux/realtek/base-files/etc/board.d/02_network | 1 + .../base-files/etc/board.d/05_compat-version | 1 + .../realtek/dts/rtl8380_zyxel_gs1900-10hp-b1.dts | 13 +++++++++++++ target/linux/realtek/image/rtl838x.mk | 10 ++++++++++ 5 files changed, 26 insertions(+) create mode 100644 target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-b1.dts diff --git a/package/boot/uboot-tools/uboot-envtools/files/realtek b/package/boot/uboot-tools/uboot-envtools/files/realtek index 2e4ce297d2..4550719581 100644 --- a/package/boot/uboot-tools/uboot-envtools/files/realtek +++ b/package/boot/uboot-tools/uboot-envtools/files/realtek @@ -21,6 +21,7 @@ zyxel,gs1900-8-a1|\ zyxel,gs1900-8hp-a1|\ zyxel,gs1900-8hp-b1|\ zyxel,gs1900-10hp-a1|\ +zyxel,gs1900-10hp-b1|\ zyxel,gs1900-16-a1|\ zyxel,gs1900-24-a1|\ zyxel,gs1900-24e-a1|\ 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 98389c522b..e672c276f8 100644 --- a/target/linux/realtek/base-files/etc/board.d/02_network +++ b/target/linux/realtek/base-files/etc/board.d/02_network @@ -140,6 +140,7 @@ realtek_setup_macs() panasonic,m8eg-pn28080k|\ vimin,vm-s100-0800ms|\ zyxel,gs1900-10hp-a1|\ + zyxel,gs1900-10hp-b1|\ zyxel,gs1900-16-a1|\ zyxel,gs1900-24-a1|\ zyxel,gs1900-24-b1|\ diff --git a/target/linux/realtek/base-files/etc/board.d/05_compat-version b/target/linux/realtek/base-files/etc/board.d/05_compat-version index c934aadcb8..9ce0df27f0 100644 --- a/target/linux/realtek/base-files/etc/board.d/05_compat-version +++ b/target/linux/realtek/base-files/etc/board.d/05_compat-version @@ -18,6 +18,7 @@ case "$(board_name)" in zyxel,gs1900-8hp-a1 | \ zyxel,gs1900-8hp-b1 | \ zyxel,gs1900-10hp-a1 | \ + zyxel,gs1900-10hp-b1 | \ zyxel,gs1900-16-a1 | \ zyxel,gs1900-24e-a1 | \ zyxel,gs1900-24ep-a1 | \ diff --git a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-b1.dts b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-b1.dts new file mode 100644 index 0000000000..0738e34777 --- /dev/null +++ b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-10hp-b1.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rtl8380_zyxel_gs1900-10hp.dtsi" + +/ { + compatible = "zyxel,gs1900-10hp-b1", "realtek,rtl838x-soc"; + model = "Zyxel GS1900-10HP B1 Switch"; +}; + +&pse { + compatible = "zyxel,gs1900-10hp-b1-pse", "realtek,pse-mcu-gen2"; + current-speed = <115200>; +}; diff --git a/target/linux/realtek/image/rtl838x.mk b/target/linux/realtek/image/rtl838x.mk index a8b11c2c23..369db851d6 100644 --- a/target/linux/realtek/image/rtl838x.mk +++ b/target/linux/realtek/image/rtl838x.mk @@ -396,6 +396,16 @@ define Device/zyxel_gs1900-10hp-a1 endef TARGET_DEVICES += zyxel_gs1900-10hp-a1 +define Device/zyxel_gs1900-10hp-b1 + $(Device/zyxel_gs1900) + SOC := rtl8380 + DEVICE_MODEL := GS1900-10HP + DEVICE_VARIANT := B1 + ZYXEL_VERS := AAZI + DEVICE_PACKAGES += kmod-pse-realtek-mcu-uart +endef +TARGET_DEVICES += zyxel_gs1900-10hp-b1 + define Device/zyxel_gs1900-16-a1 $(Device/zyxel_gs1900) SOC := rtl8382 From 3eb815e3d57a88393212015eef70d1b83205c1a2 Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Sat, 18 Jul 2026 23:19:44 +0300 Subject: [PATCH 090/152] airoha: an7583: add missing config symbol Add missing CONFIG_SND_SOC_AN7581 symbol. Fix error: ------------------------------------------------------------------ ASoC support for Airoha AN7581 chip (SND_SOC_AN7581) [N/m/?] (NEW) make[7]: *** [scripts/kconfig/Makefile:85: syncconfig] Error 1 ------------------------------------------------------------------ Fixes: 2566781 ("airoha: an7583: remove source-only flag") Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/24285 Signed-off-by: Jonas Jelonek --- target/linux/airoha/an7583/config-6.18 | 1 + 1 file changed, 1 insertion(+) diff --git a/target/linux/airoha/an7583/config-6.18 b/target/linux/airoha/an7583/config-6.18 index 38cf5726cd..340badd9d0 100644 --- a/target/linux/airoha/an7583/config-6.18 +++ b/target/linux/airoha/an7583/config-6.18 @@ -336,6 +336,7 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y CONFIG_SERIAL_MCTRL_GPIO=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SMP=y +# CONFIG_SND_SOC_AN7581 is not set CONFIG_SOCK_RX_QUEUE_MAPPING=y CONFIG_SOC_BUS=y CONFIG_SOFTIRQ_ON_OWN_STACK=y From b10eb807c84d2c1b0278cbad1f8068756684204e Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Sat, 18 Jul 2026 23:28:30 +0300 Subject: [PATCH 091/152] airoha: an7583: build airoha_an7583-evb by default These devices aren't broken and their build shouldn't have been disabled. Fixes: 2566781 ("airoha: an7583: remove source-only flag") Link: https://github.com/openwrt/openwrt/pull/23809#issuecomment-5011321092 Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/24285 Signed-off-by: Jonas Jelonek --- target/linux/airoha/image/an7583.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/linux/airoha/image/an7583.mk b/target/linux/airoha/image/an7583.mk index 876dc150e8..e1b7e875c3 100644 --- a/target/linux/airoha/image/an7583.mk +++ b/target/linux/airoha/image/an7583.mk @@ -15,7 +15,6 @@ define Device/airoha_an7583-evb DEVICE_DTS_CONFIG := config@1 IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | \ pad-rootfs | append-metadata - DEFAULT := n endef TARGET_DEVICES += airoha_an7583-evb @@ -24,7 +23,6 @@ define Device/airoha_an7583-evb-emmc DEVICE_MODEL := AN7583 Evaluation Board (EMMC) DEVICE_DTS := an7583-evb-emmc DEVICE_PACKAGES := kmod-phy-airoha-en8811h kmod-i2c-an7581 - DEFAULT := n endef TARGET_DEVICES += airoha_an7583-evb-emmc From bacda03b76aa831b035a08571559e3590112196d Mon Sep 17 00:00:00 2001 From: Vincenzo Suraci Date: Mon, 6 Jul 2026 11:43:05 +0200 Subject: [PATCH 092/152] ramips: add support for Comfast CF-EW84 Add support for the Comfast CF-EW84 based on the MediaTek MT7621 SoC. Hardware: CPU/SoC: MediaTek MT7621DA RAM: 128 MB DDR3 Flash: Macronix MX25L12805D 16 MB Switch: MediaTek MT7530 WiFi 2.4 GHz: MediaTek MT7603E WiFi 5 GHz: MediaTek MT7613B Ports: 1x WAN, 2x LAN (10/100/1000 Mbps) LEDs: Power, WAN, LAN1, LAN2, WLAN Buttons: Reset UART: through-hole on PCB pinout: RX, TX, GND, 3.3V settings: 115200n8 Flash instructions: 1. Connect the bundled PoE injector to power. 2. Connect the PC to the LAN1 or LAN2 port of the device. 3. Configure a static IP on the computer's Ethernet adapter: IP: 192.168.1.x (x = 2 to 254) Mask: 255.255.255.0 Gateway: empty DNS: empty 4. Connect a second Ethernet cable to the PoE port of the bundled injector. 5. Press and hold the reset button on the device. 6. While holding the reset button, plug the other end of the second Ethernet cable into the WAN port. 7. Wait at least 3 seconds, then release the reset button. 8. Browse to http://192.168.1.1: the MediaTek U-Boot System Recovery page should appear. 9. Click Upload and select the *-sysupgrade.bin firmware file. 10. After flashing completes, browse to http://192.168.1.1 again: the LuCI login page should appear. Factory data: 2.4 GHz EEPROM: factory offset 0x0000 5 GHz EEPROM: factory offset 0x8000 LAN MAC address: factory offset 0xe000 WAN MAC address: factory offset 0xe000 (+1) LED layout: The Comfast CF-EW84 has five physical LEDs, but only the WLAN LED is software-controllable through the Linux GPIO-LEDs subsystem. Signed-off-by: Vincenzo Suraci Link: https://github.com/openwrt/openwrt/pull/24112 Signed-off-by: Hauke Mehrtens --- .../uboot-tools/uboot-envtools/files/ramips | 1 + .../ramips/dts/mt7621_comfast_cf-ew84.dts | 163 ++++++++++++++++++ target/linux/ramips/image/mt7621.mk | 12 ++ .../mt7621/base-files/etc/board.d/02_network | 1 + 4 files changed, 177 insertions(+) create mode 100644 target/linux/ramips/dts/mt7621_comfast_cf-ew84.dts diff --git a/package/boot/uboot-tools/uboot-envtools/files/ramips b/package/boot/uboot-tools/uboot-envtools/files/ramips index 05bf9ba60e..109c0b6a65 100644 --- a/package/boot/uboot-tools/uboot-envtools/files/ramips +++ b/package/boot/uboot-tools/uboot-envtools/files/ramips @@ -32,6 +32,7 @@ allnet,all0256n-4m|\ allnet,all0256n-8m|\ allnet,all5002|\ asiarf,ap7621-004-v3|\ +comfast,cf-ew84|\ plasmacloud,pax1800-lite|\ yuncore,ax820) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x10000" "0x10000" diff --git a/target/linux/ramips/dts/mt7621_comfast_cf-ew84.dts b/target/linux/ramips/dts/mt7621_comfast_cf-ew84.dts new file mode 100644 index 0000000000..243f12a8b4 --- /dev/null +++ b/target/linux/ramips/dts/mt7621_comfast_cf-ew84.dts @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7621.dtsi" + +#include +#include +#include + +/ { + compatible = "comfast,cf-ew84", "mediatek,mt7621-soc"; + model = "COMFAST CF-EW84"; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + aliases { + label-mac-device = &gmac0; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + linux,code = ; + gpios = <&gpio 18 GPIO_ACTIVE_LOW>; + }; + }; + + leds { + compatible = "gpio-leds"; + + led-0 { + color = ; + function = LED_FUNCTION_WLAN; + gpios = <&gpio 16 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "network"; + }; + }; +}; + +ðphy0 { + /delete-property/ interrupts; +}; + +&gmac0 { + nvmem-cells = <&macaddr_factory_e000 0>; + nvmem-cell-names = "mac-address"; +}; + +&gmac1 { + status = "okay"; + label = "wan"; + phy-handle = <ðphy0>; + nvmem-cells = <&macaddr_factory_e000 1>; + nvmem-cell-names = "mac-address"; +}; + +&pcie { + status = "okay"; +}; + +// pcie0 → mt7603e (2.4GHz) +&pcie0 { + wifi@0,0 { + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + ieee80211-freq-limit = <2400000 2500000>; + nvmem-cells = <&eeprom_factory_0>; + nvmem-cell-names = "eeprom"; + }; +}; + +// pcie1 → mt7663 (5GHz) +&pcie1 { + wifi@0,0 { + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + ieee80211-freq-limit = <5000000 6000000>; + nvmem-cells = <&eeprom_factory_8000>; + nvmem-cell-names = "eeprom"; + }; +}; + +&pinctrl { + pinctrl0 { + gpio { + groups = "i2c", "uart2", "uart3", "sdhci", "jtag"; + function = "gpio"; + }; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "Bootloader"; + reg = <0x000000 0x030000>; + read-only; + }; + + partition@30000 { + label = "Config"; + reg = <0x030000 0x010000>; + }; + + partition@40000 { + label = "factory"; + reg = <0x040000 0x010000>; + read-only; + nvmem-layout { + compatible = "fixed-layout"; + #address-cells = <1>; + #size-cells = <1>; + + eeprom_factory_0: eeprom@0 { + reg = <0x0 0x400>; + }; + + eeprom_factory_8000: eeprom@8000 { + reg = <0x8000 0x4da8>; + }; + + macaddr_factory_e000: macaddr@e000 { + compatible = "mac-base"; + reg = <0xe000 0x6>; + #nvmem-cell-cells = <1>; + }; + }; + }; + + partition@50000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x050000 0xfb0000>; + }; + }; + }; +}; + +&switch0 { + ports { + port@1 { + status = "okay"; + }; + + port@2 { + status = "okay"; + }; + }; +}; diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk index 96653683de..75b7c3ce36 100644 --- a/target/linux/ramips/image/mt7621.mk +++ b/target/linux/ramips/image/mt7621.mk @@ -735,6 +735,18 @@ define Device/comfast_cf-ew72-v2 endef TARGET_DEVICES += comfast_cf-ew72-v2 +define Device/comfast_cf-ew84 + $(Device/dsa-migration) + $(Device/uimage-lzma-loader) + IMAGE_SIZE := 16064k + DEVICE_VENDOR := COMFAST + DEVICE_MODEL := CF-EW84 + DEVICE_ALT0_VENDOR := NEWFAST + DEVICE_ALT0_MODEL := NF-A882 + DEVICE_PACKAGES := kmod-mt7603 kmod-mt7615e kmod-mt7663-firmware-ap kmod-ledtrig-network +endef +TARGET_DEVICES += comfast_cf-ew84 + define Device/confiabits_mt7621-v1 $(Device/dsa-migration) $(Device/uimage-lzma-loader) diff --git a/target/linux/ramips/mt7621/base-files/etc/board.d/02_network b/target/linux/ramips/mt7621/base-files/etc/board.d/02_network index cf0515b0d3..5d26745872 100644 --- a/target/linux/ramips/mt7621/base-files/etc/board.d/02_network +++ b/target/linux/ramips/mt7621/base-files/etc/board.d/02_network @@ -99,6 +99,7 @@ ramips_setup_interfaces() asiarf,ap7621-nv1|\ beeline,smartbox-flash|\ beeline,smartbox-giga|\ + comfast,cf-ew84|\ elecom,wmc-x1800gst|\ elecom,wrc-x1800gs|\ gemtek,wvrtm-127acn|\ From 3ec398f0c3eeebbfb6aba452e48f3efd5b0f52a8 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Jun 2026 11:13:32 +0000 Subject: [PATCH 093/152] tools: firmware-utils: update to Git HEAD (2026-07-19) 1ab209233682 mkzynfw: rename macros from RTL93XX to RTL_OTTO 606f25ef3ca2 mkzynfw: add board definitions for Zyxel GS1920 v2 series 162fa533b309 mkzynfw: add board definitions for GS2210 series b2d46a825a53 mkzynfw: add board definitions for Zyxel GS1920 v1 series 5c5df244b8a1 Revert "mkzynfw: add board definitions for Zyxel GS1920 v1 series" 7f28bfbc4b5a Revert "mkzynfw: add board definitions for GS2210 series" 5e1b83d1fc2b mkzynfw: add board definitions for GS2210 series 3e6b426938e8 mkzynfw: add board definitions for Zyxel GS1920 v1 series Signed-off-by: Jonas Jelonek --- tools/firmware-utils/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 686cf6d910..fdfbdc1311 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -11,9 +11,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware-utils.git -PKG_SOURCE_DATE:=2026-05-04 -PKG_SOURCE_VERSION:=1a4153f4c70111036007760a8411232423b6e11e -PKG_MIRROR_HASH:=af3cae88cc3c7fdbc0b6416762a4d49d2eef31adce759c6fb65ce4d450e91ecf +PKG_SOURCE_DATE:=2026-07-19 +PKG_SOURCE_VERSION:=3e6b426938e86efa0aaced903f6e22a1db2d2bc7 +PKG_MIRROR_HASH:=5f313d1c40935e837909c42bd6b198feafdfd9fd0520c3bba277719aa3fbb76e include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/cmake.mk From ff41ddea1c9ee58ef56828f0ac97055fce2d6fce Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Sun, 19 Jul 2026 11:30:17 +0200 Subject: [PATCH 094/152] build: gate firmware image signing behind SIGN_FIRMWARE Firmware image signing in append-metadata/append-gl-metadata was conditional only by the presence of the build key. Add an explicit SIGN_FIRMWARE option (default enabled except with BUILDBOT) so appending the fwtool signature can be toggled on its own. It's disable on buildbot since we use a decentralized building with no individual keys on builders. Instead, a fake key is currently used, which adds an insecure signature. The future of signing firmware and key distribution across the build infrastructure should be discussed separately. Link: https://github.com/openwrt/openwrt/pull/24291 Signed-off-by: Paul Spooren --- config/Config-build.in | 9 +++++++++ include/image-commands.mk | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/config/Config-build.in b/config/Config-build.in index 2d38d2bd2b..79fbe71a43 100644 --- a/config/Config-build.in +++ b/config/Config-build.in @@ -83,6 +83,15 @@ menu "Global build settings" --allow-untrusted when installing self-compiled packages to a firmware compiled by the same buildhost as public key matches. + config SIGN_FIRMWARE + bool "Cryptographically sign firmware images" + default n if BUILDBOT + default y + help + Append a signature to firmware images so that sysupgrade can verify + their authenticity before flashing. OpenWrt's build infrastructure + signs images with temporary keys; disabling this drops the signature. + comment "General build options" config TESTING_KERNEL diff --git a/include/image-commands.mk b/include/image-commands.mk index 443c870a8e..32aea3e543 100644 --- a/include/image-commands.mk +++ b/include/image-commands.mk @@ -91,12 +91,12 @@ metadata_json = \ define Build/append-metadata $(if $(SUPPORTED_DEVICES),-echo $(call metadata_json) | fwtool -I - $@) sha256sum "$@" | cut -d" " -f1 > "$@.sha256sum" - [ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \ + $(if $(CONFIG_SIGN_FIRMWARE),[ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \ cp "$(BUILD_KEY).ucert" "$@.ucert" ;\ usign -S -m "$@" -s "$(BUILD_KEY)" -x "$@.sig" ;\ ucert -A -c "$@.ucert" -x "$@.sig" ;\ fwtool -S "$@.ucert" "$@" ;\ - } + }) endef metadata_gl_json = \ @@ -122,12 +122,12 @@ metadata_gl_json = \ define Build/append-gl-metadata $(if $(SUPPORTED_DEVICES),-echo $(call metadata_gl_json,$(SUPPORTED_DEVICES)) | fwtool -I - $@) sha256sum "$@" | cut -d" " -f1 > "$@.sha256sum" - [ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \ + $(if $(CONFIG_SIGN_FIRMWARE),[ ! -s "$(BUILD_KEY)" -o ! -s "$(BUILD_KEY).ucert" -o ! -s "$@" ] || { \ cp "$(BUILD_KEY).ucert" "$@.ucert" ;\ usign -S -m "$@" -s "$(BUILD_KEY)" -x "$@.sig" ;\ ucert -A -c "$@.ucert" -x "$@.sig" ;\ fwtool -S "$@.ucert" "$@" ;\ - } + }) endef define Build/append-teltonika-metadata From 3c5e204abe1adf5fca9a5430e0bbb5f92ca8381e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:04:47 +0200 Subject: [PATCH 095/152] ucode-mod-bpf: fix missing resource pointer checks uc_fn_thisval can return NULL when a method is called with a foreign or missing this context. pin, foreach and the iterator next functions dereferenced the result without checking it, crashing the VM. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index 33641ddfb2..0c896026dc 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -520,6 +520,9 @@ uc_bpf_map_iter_next(uc_vm_t *vm, size_t nargs) struct uc_bpf_map_iter *iter = uc_fn_thisval("bpf.map_iter"); uc_value_t *rv; + if (!iter) + err_return(EINVAL, NULL); + if (!iter->has_next) return NULL; @@ -536,6 +539,9 @@ uc_bpf_map_iter_next_int(uc_vm_t *vm, size_t nargs) uint64_t intval; uc_value_t *rv; + if (!iter) + err_return(EINVAL, NULL); + if (!iter->has_next) return NULL; @@ -561,6 +567,9 @@ uc_bpf_map_foreach(uc_vm_t *vm, size_t nargs) void *key, *next; bool ret = false; + if (!map) + err_return(EINVAL, NULL); + key = alloca(map->key_size); next = alloca(map->key_size); has_next = !bpf_map_get_next_key(map->fd.fd, NULL, next); @@ -597,6 +606,9 @@ uc_bpf_obj_pin(uc_vm_t *vm, size_t nargs, const char *type) struct uc_bpf_fd *f = uc_fn_thisval(type); uc_value_t *path = uc_fn_arg(0); + if (!f) + err_return(EINVAL, NULL); + if (ucv_type(path) != UC_STRING) err_return(EINVAL, NULL); From 5c35e410fcea2cfee1b5841143a1a97ec4cb68f5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:05:28 +0200 Subject: [PATCH 096/152] ucode-mod-bpf: fix key/value aliasing in map argument conversion uc_bpf_map_arg converted integer arguments through a single static buffer. When map set was called with both an integer key and an integer value, the second conversion overwrote the first, so the element was written at the wrong key. Convert into a caller-provided buffer instead. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index 0c896026dc..1758510a54 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -339,23 +339,19 @@ uc_bpf_module_get_program(uc_vm_t *vm, size_t nargs) } static void * -uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size) +uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size, + uint64_t *val_int) { - static union { - uint32_t u32; - uint64_t u64; - } val_int; - switch (ucv_type(val)) { case UC_INTEGER: if (size == 4) - val_int.u32 = ucv_int64_get(val); + *(uint32_t *)val_int = ucv_int64_get(val); else if (size == 8) - val_int.u64 = ucv_int64_get(val); + *val_int = ucv_int64_get(val); else break; - return &val_int; + return val_int; case UC_STRING: if (size != ucv_string_length(val)) break; @@ -373,12 +369,13 @@ uc_bpf_map_get(uc_vm_t *vm, size_t nargs) { struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); uc_value_t *a_key = uc_fn_arg(0); + uint64_t key_int; void *key, *val; if (!map) err_return(EINVAL, NULL); - key = uc_bpf_map_arg(a_key, "key", map->key_size); + key = uc_bpf_map_arg(a_key, "key", map->key_size, &key_int); if (!key) return NULL; @@ -396,17 +393,18 @@ uc_bpf_map_set(uc_vm_t *vm, size_t nargs) uc_value_t *a_key = uc_fn_arg(0); uc_value_t *a_val = uc_fn_arg(1); uc_value_t *a_flags = uc_fn_arg(2); + uint64_t key_int, val_int; uint64_t flags; void *key, *val; if (!map) err_return(EINVAL, NULL); - key = uc_bpf_map_arg(a_key, "key", map->key_size); + key = uc_bpf_map_arg(a_key, "key", map->key_size, &key_int); if (!key) return NULL; - val = uc_bpf_map_arg(a_val, "value", map->val_size); + val = uc_bpf_map_arg(a_val, "value", map->val_size, &val_int); if (!val) return NULL; @@ -430,12 +428,13 @@ uc_bpf_map_delete(uc_vm_t *vm, size_t nargs) uc_value_t *a_key = uc_fn_arg(0); uc_value_t *a_return = uc_fn_arg(1); void *key, *val = NULL; + uint64_t key_int; int ret; if (!map) err_return(EINVAL, NULL); - key = uc_bpf_map_arg(a_key, "key", map->key_size); + key = uc_bpf_map_arg(a_key, "key", map->key_size, &key_int); if (!key) return NULL; From e04461835aaaf20bfd1609459af324b91c11e7e8 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:05:55 +0200 Subject: [PATCH 097/152] ucode-mod-bpf: fix unaligned access in integer iterator The iterator key buffer is a flexible array member placed directly after a bool, leaving it without natural alignment for the u32/u64 loads in next_int. Copy the key via memcpy instead of dereferencing a misaligned pointer, which traps on strict-alignment targets. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index 1758510a54..eb7d5a03ac 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -544,12 +544,16 @@ uc_bpf_map_iter_next_int(uc_vm_t *vm, size_t nargs) if (!iter->has_next) return NULL; - if (iter->key_size == 4) - intval = *(uint32_t *)iter->key; - else if (iter->key_size == 8) - intval = *(uint64_t *)iter->key; - else + if (iter->key_size == 4) { + uint32_t val32; + + memcpy(&val32, iter->key, sizeof(val32)); + intval = val32; + } else if (iter->key_size == 8) { + memcpy(&intval, iter->key, sizeof(intval)); + } else { return NULL; + } rv = ucv_int64_new(intval); iter->has_next = !bpf_map_get_next_key(iter->fd, &iter->key, &iter->key); From 05208d9a7e9153867a028c0c5f198ad7590cca34 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:07:01 +0200 Subject: [PATCH 098/152] ucode-mod-bpf: fix stale errno when rodata map is missing bpf_object__next_map does not set errno when it runs out of maps, so the reported error code was whatever errno happened to hold. With errno 0 the failure was invisible to error(). Report ENOENT instead. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index eb7d5a03ac..45cd7114dd 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -129,7 +129,7 @@ uc_bpf_module_set_opts(struct bpf_object *obj, uc_value_t *opts) } if (!map) - err_return_int(errno, "rodata map"); + err_return_int(ENOENT, "rodata map"); if (bpf_map__set_initial_value(map, ucv_string_get(val), ucv_string_length(val))) From acc687da84c5177bcf46d3757cb97243ab3c91c0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:07:21 +0200 Subject: [PATCH 099/152] ucode-mod-bpf: fix error reporting in tc hook setup Failures in the detach path returned NULL without recording any error state, so error() reported a stale or empty error. Attach failures reported a hardcoded ENOENT regardless of the actual cause. Record errno for all failures; if_nametoindex, bpf_tc_attach and bpf_tc_detach all set it. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index 45cd7114dd..dba4ad8b99 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -680,9 +680,7 @@ out: return TRUE; error: - if (fd >= 0) - err_return(ENOENT, NULL); - return NULL; + err_return(errno, NULL); } static uc_value_t * From 9070d0e6303154ed17d4f4bb3b76f6cf6ae2cf0d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:25:17 +0200 Subject: [PATCH 100/152] ucode-mod-bpf: add per-CPU map support Lookup, update and lookup-and-delete on per-CPU maps use buffers of roundup(value_size, 8) * num_possible_cpus bytes. The previous code sized buffers for a single value, so get() on a per-CPU map made the kernel overflow the stack buffer and set() passed undersized data. Track the map type and size the buffers accordingly. get() and delete() with return now yield an array with one value per CPU, set() accepts either such an array or a single value replicated to all CPUs. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 149 ++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 11 deletions(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index dba4ad8b99..c34772bff5 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -32,6 +32,7 @@ struct uc_bpf_fd { struct uc_bpf_map { struct uc_bpf_fd fd; /* must be first */ + unsigned int type; unsigned int key_size, val_size; }; @@ -187,8 +188,8 @@ uc_bpf_open_module(uc_vm_t *vm, size_t nargs) } static uc_value_t * -uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int key_size, - unsigned int val_size, bool close) +uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int type, + unsigned int key_size, unsigned int val_size, bool close) { struct uc_bpf_map *uc_map; uc_value_t *res; @@ -196,6 +197,7 @@ uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int key_size, res = ucv_resource_create_ex(vm, "bpf.map", (void **)&uc_map, 1, sizeof(*uc_map)); ucv_resource_value_set(res, 0, ucv_get(mod)); uc_map->fd.fd = fd; + uc_map->type = type; uc_map->key_size = key_size; uc_map->val_size = val_size; uc_map->fd.close = close; @@ -239,7 +241,8 @@ uc_bpf_open_map(uc_vm_t *vm, size_t nargs) err_return(errno, NULL); } - return uc_bpf_map_create(vm, NULL, fd, info.key_size, info.value_size, true); + return uc_bpf_map_create(vm, NULL, fd, info.type, info.key_size, + info.value_size, true); } static uc_value_t * @@ -295,7 +298,9 @@ uc_bpf_module_get_map(uc_vm_t *vm, size_t nargs) if (fd < 0) err_return(EINVAL, NULL); - return uc_bpf_map_create(vm, _uc_fn_this_res(vm), fd, bpf_map__key_size(map), bpf_map__value_size(map), false); + return uc_bpf_map_create(vm, _uc_fn_this_res(vm), fd, bpf_map__type(map), + bpf_map__key_size(map), bpf_map__value_size(map), + false); } static uc_value_t * @@ -364,13 +369,121 @@ uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size, err_return(EINVAL, "%s size mismatch (expected: %d)", kind, size); } +static bool +uc_bpf_map_is_percpu(struct uc_bpf_map *map) +{ + switch (map->type) { + case BPF_MAP_TYPE_PERCPU_HASH: + case BPF_MAP_TYPE_PERCPU_ARRAY: + case BPF_MAP_TYPE_LRU_PERCPU_HASH: + case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: + return true; + default: + return false; + } +} + +static unsigned int +uc_bpf_map_val_stride(struct uc_bpf_map *map) +{ + return (map->val_size + 7) & ~7U; +} + +static bool +uc_bpf_map_val_len(struct uc_bpf_map *map, unsigned int *len, int *num_cpus) +{ + *len = map->val_size; + *num_cpus = 1; + + if (!uc_bpf_map_is_percpu(map)) + return true; + + *num_cpus = libbpf_num_possible_cpus(); + if (*num_cpus <= 0) { + set_error(errno, "number of CPUs"); + return false; + } + + *len = uc_bpf_map_val_stride(map) * *num_cpus; + + return true; +} + +static uc_value_t * +uc_bpf_map_val_read(uc_vm_t *vm, struct uc_bpf_map *map, void *val, + int num_cpus) +{ + unsigned int stride = uc_bpf_map_val_stride(map); + uc_value_t *rv; + int i; + + if (!uc_bpf_map_is_percpu(map)) + return ucv_string_new_length(val, map->val_size); + + rv = ucv_array_new(vm); + for (i = 0; i < num_cpus; i++) + ucv_array_set(rv, i, ucv_string_new_length((char *)val + i * stride, + map->val_size)); + + return rv; +} + +static bool +uc_bpf_map_val_copy(struct uc_bpf_map *map, uc_value_t *a_val, void *dest) +{ + uint64_t val_int; + void *val; + + val = uc_bpf_map_arg(a_val, "value", map->val_size, &val_int); + if (!val) + return false; + + memcpy(dest, val, map->val_size); + + return true; +} + +static void * +uc_bpf_map_val_arg(struct uc_bpf_map *map, uc_value_t *a_val, void *buf, + int num_cpus) +{ + unsigned int stride = uc_bpf_map_val_stride(map); + int i; + + if (!uc_bpf_map_is_percpu(map)) + return uc_bpf_map_arg(a_val, "value", map->val_size, buf); + + memset(buf, 0, stride * num_cpus); + if (ucv_type(a_val) != UC_ARRAY) { + if (!uc_bpf_map_val_copy(map, a_val, buf)) + return NULL; + + for (i = 1; i < num_cpus; i++) + memcpy((char *)buf + i * stride, buf, map->val_size); + + return buf; + } + + if (ucv_array_length(a_val) != (size_t)num_cpus) + err_return(EINVAL, "value count mismatch (expected: %d)", num_cpus); + + for (i = 0; i < num_cpus; i++) + if (!uc_bpf_map_val_copy(map, ucv_array_get(a_val, i), + (char *)buf + i * stride)) + return NULL; + + return buf; +} + static uc_value_t * uc_bpf_map_get(uc_vm_t *vm, size_t nargs) { struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); uc_value_t *a_key = uc_fn_arg(0); + unsigned int val_len; uint64_t key_int; void *key, *val; + int num_cpus; if (!map) err_return(EINVAL, NULL); @@ -379,11 +492,14 @@ uc_bpf_map_get(uc_vm_t *vm, size_t nargs) if (!key) return NULL; - val = alloca(map->val_size); + if (!uc_bpf_map_val_len(map, &val_len, &num_cpus)) + return NULL; + + val = alloca(val_len); if (bpf_map_lookup_elem(map->fd.fd, key, val)) return NULL; - return ucv_string_new_length(val, map->val_size); + return uc_bpf_map_val_read(vm, map, val, num_cpus); } static uc_value_t * @@ -393,9 +509,11 @@ uc_bpf_map_set(uc_vm_t *vm, size_t nargs) uc_value_t *a_key = uc_fn_arg(0); uc_value_t *a_val = uc_fn_arg(1); uc_value_t *a_flags = uc_fn_arg(2); - uint64_t key_int, val_int; + unsigned int val_len; + uint64_t key_int; uint64_t flags; void *key, *val; + int num_cpus; if (!map) err_return(EINVAL, NULL); @@ -404,7 +522,11 @@ uc_bpf_map_set(uc_vm_t *vm, size_t nargs) if (!key) return NULL; - val = uc_bpf_map_arg(a_val, "value", map->val_size, &val_int); + if (!uc_bpf_map_val_len(map, &val_len, &num_cpus)) + return NULL; + + val = alloca(val_len); + val = uc_bpf_map_val_arg(map, a_val, val, num_cpus); if (!val) return NULL; @@ -418,7 +540,7 @@ uc_bpf_map_set(uc_vm_t *vm, size_t nargs) if (bpf_map_update_elem(map->fd.fd, key, val, flags)) return NULL; - return ucv_string_new_length(val, map->val_size); + return uc_bpf_map_val_read(vm, map, val, num_cpus); } static uc_value_t * @@ -427,8 +549,10 @@ uc_bpf_map_delete(uc_vm_t *vm, size_t nargs) struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); uc_value_t *a_key = uc_fn_arg(0); uc_value_t *a_return = uc_fn_arg(1); + unsigned int val_len; void *key, *val = NULL; uint64_t key_int; + int num_cpus; int ret; if (!map) @@ -444,11 +568,14 @@ uc_bpf_map_delete(uc_vm_t *vm, size_t nargs) return ucv_boolean_new(ret == 0); } - val = alloca(map->val_size); + if (!uc_bpf_map_val_len(map, &val_len, &num_cpus)) + return NULL; + + val = alloca(val_len); if (bpf_map_lookup_and_delete_elem(map->fd.fd, key, val)) return NULL; - return ucv_string_new_length(val, map->val_size); + return uc_bpf_map_val_read(vm, map, val, num_cpus); } static uc_value_t * From ec06937a5ab4b45128d58d2e0d5cfb5c93df3ea3 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:25:50 +0200 Subject: [PATCH 101/152] ucode-mod-bpf: add support for keyless maps Queue and stack maps have zero-sized keys and require a NULL key pointer; the kernel rejects any non-NULL key with EINVAL, making these maps unusable. Accept a null key argument on maps without a key, mapping get() to peek, set() to push and delete() with return to pop. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 29 +++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index c34772bff5..d32c7ec45e 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -369,6 +369,26 @@ uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size, err_return(EINVAL, "%s size mismatch (expected: %d)", kind, size); } +static bool +uc_bpf_map_key_arg(struct uc_bpf_map *map, uc_value_t *a_key, uint64_t *buf, + void **key) +{ + if (!map->key_size) { + if (a_key) { + set_error(EINVAL, "map has no key"); + return false; + } + + *key = NULL; + + return true; + } + + *key = uc_bpf_map_arg(a_key, "key", map->key_size, buf); + + return *key != NULL; +} + static bool uc_bpf_map_is_percpu(struct uc_bpf_map *map) { @@ -488,8 +508,7 @@ uc_bpf_map_get(uc_vm_t *vm, size_t nargs) if (!map) err_return(EINVAL, NULL); - key = uc_bpf_map_arg(a_key, "key", map->key_size, &key_int); - if (!key) + if (!uc_bpf_map_key_arg(map, a_key, &key_int, &key)) return NULL; if (!uc_bpf_map_val_len(map, &val_len, &num_cpus)) @@ -518,8 +537,7 @@ uc_bpf_map_set(uc_vm_t *vm, size_t nargs) if (!map) err_return(EINVAL, NULL); - key = uc_bpf_map_arg(a_key, "key", map->key_size, &key_int); - if (!key) + if (!uc_bpf_map_key_arg(map, a_key, &key_int, &key)) return NULL; if (!uc_bpf_map_val_len(map, &val_len, &num_cpus)) @@ -558,8 +576,7 @@ uc_bpf_map_delete(uc_vm_t *vm, size_t nargs) if (!map) err_return(EINVAL, NULL); - key = uc_bpf_map_arg(a_key, "key", map->key_size, &key_int); - if (!key) + if (!uc_bpf_map_key_arg(map, a_key, &key_int, &key)) return NULL; if (!ucv_is_truish(a_return)) { From 9b7ddbaef9a5f9da1e41f85431bb7c9b74aaae18 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:26:30 +0200 Subject: [PATCH 102/152] ucode-mod-bpf: add map-in-map and prog array support Updates on array/hash-of-maps and prog array maps take the fd of the inner object as a 4-byte value. Accept bpf.map and bpf.program resources as map values and convert them to their fd. Lookups on map-in-map types return the inner map id; add open_map_id and open_program_id to obtain a usable object from such an id. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 77 +++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index d32c7ec45e..8e1c9b219e 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -220,20 +220,11 @@ uc_bpf_prog_create(uc_vm_t *vm, uc_value_t *mod, int fd, bool close) } static uc_value_t * -uc_bpf_open_map(uc_vm_t *vm, size_t nargs) +uc_bpf_open_map_fd(uc_vm_t *vm, int fd) { struct bpf_map_info info; - uc_value_t *path = uc_fn_arg(0); __u32 len = sizeof(info); int err; - int fd; - - if (ucv_type(path) != UC_STRING) - err_return(EINVAL, "module path"); - - fd = bpf_obj_get(ucv_string_get(path)); - if (fd < 0) - err_return(errno, NULL); err = bpf_obj_get_info_by_fd(fd, &info, &len); if (err) { @@ -245,6 +236,38 @@ uc_bpf_open_map(uc_vm_t *vm, size_t nargs) info.value_size, true); } +static uc_value_t * +uc_bpf_open_map(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *path = uc_fn_arg(0); + int fd; + + if (ucv_type(path) != UC_STRING) + err_return(EINVAL, "module path"); + + fd = bpf_obj_get(ucv_string_get(path)); + if (fd < 0) + err_return(errno, NULL); + + return uc_bpf_open_map_fd(vm, fd); +} + +static uc_value_t * +uc_bpf_open_map_id(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *id = uc_fn_arg(0); + int fd; + + if (ucv_type(id) != UC_INTEGER) + err_return(EINVAL, "map id"); + + fd = bpf_map_get_fd_by_id(ucv_int64_get(id)); + if (fd < 0) + err_return(errno, NULL); + + return uc_bpf_open_map_fd(vm, fd); +} + static uc_value_t * uc_bpf_open_program(uc_vm_t *vm, size_t nargs) { @@ -261,6 +284,22 @@ uc_bpf_open_program(uc_vm_t *vm, size_t nargs) return uc_bpf_prog_create(vm, NULL, fd, true); } +static uc_value_t * +uc_bpf_open_program_id(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *id = uc_fn_arg(0); + int fd; + + if (ucv_type(id) != UC_INTEGER) + err_return(EINVAL, "program id"); + + fd = bpf_prog_get_fd_by_id(ucv_int64_get(id)); + if (fd < 0) + err_return(errno, NULL); + + return uc_bpf_prog_create(vm, NULL, fd, true); +} + static uc_value_t * uc_bpf_module_get_maps(uc_vm_t *vm, size_t nargs) { @@ -362,6 +401,22 @@ uc_bpf_map_arg(uc_value_t *val, const char *kind, unsigned int size, break; return ucv_string_get(val); + case UC_RESOURCE: { + struct uc_bpf_fd *f; + + f = ucv_resource_data(val, "bpf.map"); + if (!f) + f = ucv_resource_data(val, "bpf.program"); + if (!f) + err_return(EINVAL, "%s type", kind); + + if (size != 4) + break; + + *(uint32_t *)val_int = f->fd; + + return val_int; + } default: err_return(EINVAL, "%s type", kind); } @@ -962,7 +1017,9 @@ static const uc_function_list_t global_fns[] = { { "set_debug_handler", uc_bpf_set_debug_handler }, { "open_module", uc_bpf_open_module }, { "open_map", uc_bpf_open_map }, + { "open_map_id", uc_bpf_open_map_id }, { "open_program", uc_bpf_open_program }, + { "open_program_id", uc_bpf_open_program_id }, { "tc_detach", uc_bpf_tc_detach }, }; From a1489332142803a2347729290161d81df1b31277 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:27:46 +0200 Subject: [PATCH 103/152] ucode-mod-bpf: add ring buffer and perf event buffer support Add consumer support for BPF_MAP_TYPE_RINGBUF and BPF_MAP_TYPE_PERF_EVENT_ARRAY maps. ringbuf(callback) and perf_buffer(callback, pages, lost_callback) map methods return a buffer resource with fileno, poll and consume methods. The fileno method exposes the epoll fd and matches the interface uloop.handle() expects, so a buffer can be passed directly to uloop for event loop integration. The ring buffer callback receives the sample data and may return a negative integer to stop consumption; the perf callbacks additionally receive the CPU number. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 254 ++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index 8e1c9b219e..78971bdfbd 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -802,6 +802,241 @@ uc_bpf_map_foreach(uc_vm_t *vm, size_t nargs) return ucv_boolean_new(ret); } +enum { + UC_BPF_BUF_CB, + UC_BPF_BUF_LOST_CB, + UC_BPF_BUF_MAP, + __UC_BPF_BUF_MAX +}; + +struct uc_bpf_buffer { + uc_vm_t *vm; + uc_value_t *res; + bool perf; + bool exception; + union { + struct ring_buffer *rb; + struct perf_buffer *pb; + }; +}; + +static uc_value_t * +uc_bpf_buffer_call(struct uc_bpf_buffer *buf, size_t slot, uc_value_t *arg, + int cpu) +{ + uc_vm_t *vm = buf->vm; + size_t fn_nargs = 1; + + uc_vm_stack_push(vm, ucv_get(ucv_resource_value_get(buf->res, slot))); + uc_vm_stack_push(vm, arg); + if (cpu >= 0) { + uc_vm_stack_push(vm, ucv_int64_new(cpu)); + fn_nargs++; + } + + if (uc_vm_call(vm, false, fn_nargs) != EXCEPTION_NONE) { + buf->exception = true; + return NULL; + } + + return uc_vm_stack_pop(vm); +} + +static int +uc_bpf_ringbuf_sample(void *ctx, void *data, size_t size) +{ + struct uc_bpf_buffer *buf = ctx; + uc_value_t *rv; + int ret = 0; + + if (buf->exception) + return -1; + + rv = uc_bpf_buffer_call(buf, UC_BPF_BUF_CB, + ucv_string_new_length(data, size), -1); + if (buf->exception) + return -1; + + if (ucv_type(rv) == UC_INTEGER) + ret = ucv_int64_get(rv); + ucv_put(rv); + + return ret; +} + +static void +uc_bpf_perf_sample(void *ctx, int cpu, void *data, __u32 size) +{ + struct uc_bpf_buffer *buf = ctx; + + if (buf->exception) + return; + + ucv_put(uc_bpf_buffer_call(buf, UC_BPF_BUF_CB, + ucv_string_new_length(data, size), cpu)); +} + +static void +uc_bpf_perf_lost(void *ctx, int cpu, __u64 cnt) +{ + struct uc_bpf_buffer *buf = ctx; + + if (buf->exception || !ucv_resource_value_get(buf->res, UC_BPF_BUF_LOST_CB)) + return; + + ucv_put(uc_bpf_buffer_call(buf, UC_BPF_BUF_LOST_CB, ucv_int64_new(cnt), + cpu)); +} + +static uc_value_t * +uc_bpf_map_buffer_create(uc_vm_t *vm, struct uc_bpf_map *map, bool perf, + size_t pages, uc_value_t *cb, uc_value_t *lost_cb) +{ + struct uc_bpf_buffer *buf; + uc_value_t *res; + + res = ucv_resource_create_ex(vm, "bpf.buffer", (void **)&buf, + __UC_BPF_BUF_MAX, sizeof(*buf)); + buf->vm = vm; + buf->res = res; + buf->perf = perf; + if (perf) + buf->pb = perf_buffer__new(map->fd.fd, pages, uc_bpf_perf_sample, + uc_bpf_perf_lost, buf, NULL); + else + buf->rb = ring_buffer__new(map->fd.fd, uc_bpf_ringbuf_sample, + buf, NULL); + if (!buf->rb) { + ucv_put(res); + err_return(errno, NULL); + } + + ucv_resource_value_set(res, UC_BPF_BUF_CB, ucv_get(cb)); + ucv_resource_value_set(res, UC_BPF_BUF_LOST_CB, ucv_get(lost_cb)); + ucv_resource_value_set(res, UC_BPF_BUF_MAP, ucv_get(_uc_fn_this_res(vm))); + + return res; +} + +static uc_value_t * +uc_bpf_map_ringbuf(uc_vm_t *vm, size_t nargs) +{ + struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); + uc_value_t *cb = uc_fn_arg(0); + + if (!map) + err_return(EINVAL, NULL); + + if (map->type != BPF_MAP_TYPE_RINGBUF) + err_return(EINVAL, "map type"); + + if (!ucv_is_callable(cb)) + err_return(EINVAL, "callback"); + + return uc_bpf_map_buffer_create(vm, map, false, 0, cb, NULL); +} + +static uc_value_t * +uc_bpf_map_perf_buffer(uc_vm_t *vm, size_t nargs) +{ + struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); + uc_value_t *cb = uc_fn_arg(0); + uc_value_t *a_pages = uc_fn_arg(1); + uc_value_t *lost_cb = uc_fn_arg(2); + size_t pages = 8; + + if (!map) + err_return(EINVAL, NULL); + + if (map->type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) + err_return(EINVAL, "map type"); + + if (!ucv_is_callable(cb)) + err_return(EINVAL, "callback"); + + if (a_pages) { + if (ucv_type(a_pages) != UC_INTEGER || ucv_int64_get(a_pages) <= 0) + err_return(EINVAL, "page count"); + + pages = ucv_int64_get(a_pages); + } + + if (lost_cb && !ucv_is_callable(lost_cb)) + err_return(EINVAL, "lost callback"); + + return uc_bpf_map_buffer_create(vm, map, true, pages, cb, lost_cb); +} + +static uc_value_t * +uc_bpf_buffer_fileno(uc_vm_t *vm, size_t nargs) +{ + struct uc_bpf_buffer *buf = uc_fn_thisval("bpf.buffer"); + int fd; + + if (!buf) + err_return(EINVAL, NULL); + + fd = buf->perf ? perf_buffer__epoll_fd(buf->pb) : + ring_buffer__epoll_fd(buf->rb); + if (fd < 0) + err_return(errno, NULL); + + return ucv_int64_new(fd); +} + +static uc_value_t * +uc_bpf_buffer_result(uc_vm_t *vm, struct uc_bpf_buffer *buf, int ret) +{ + if (buf->exception) { + buf->exception = false; + return NULL; + } + + if (ret < 0) + err_return(errno, NULL); + + return ucv_int64_new(ret); +} + +static uc_value_t * +uc_bpf_buffer_poll(uc_vm_t *vm, size_t nargs) +{ + struct uc_bpf_buffer *buf = uc_fn_thisval("bpf.buffer"); + uc_value_t *a_timeout = uc_fn_arg(0); + int timeout = 0; + int ret; + + if (!buf) + err_return(EINVAL, NULL); + + if (a_timeout) { + if (ucv_type(a_timeout) != UC_INTEGER) + err_return(EINVAL, "timeout"); + + timeout = ucv_int64_get(a_timeout); + } + + ret = buf->perf ? perf_buffer__poll(buf->pb, timeout) : + ring_buffer__poll(buf->rb, timeout); + + return uc_bpf_buffer_result(vm, buf, ret); +} + +static uc_value_t * +uc_bpf_buffer_consume(uc_vm_t *vm, size_t nargs) +{ + struct uc_bpf_buffer *buf = uc_fn_thisval("bpf.buffer"); + int ret; + + if (!buf) + err_return(EINVAL, NULL); + + ret = buf->perf ? perf_buffer__consume(buf->pb) : + ring_buffer__consume(buf->rb); + + return uc_bpf_buffer_result(vm, buf, ret); +} + static uc_value_t * uc_bpf_obj_pin(uc_vm_t *vm, size_t nargs, const char *type) { @@ -992,6 +1227,24 @@ static const uc_function_list_t map_fns[] = { { "delete_all", uc_bpf_map_delete_all }, { "foreach", uc_bpf_map_foreach }, { "iterator", uc_bpf_map_iterator }, + { "ringbuf", uc_bpf_map_ringbuf }, + { "perf_buffer", uc_bpf_map_perf_buffer }, +}; + +static void uc_bpf_buffer_free(void *ptr) +{ + struct uc_bpf_buffer *buf = ptr; + + if (buf->perf) + perf_buffer__free(buf->pb); + else + ring_buffer__free(buf->rb); +} + +static const uc_function_list_t buffer_fns[] = { + { "fileno", uc_bpf_buffer_fileno }, + { "poll", uc_bpf_buffer_poll }, + { "consume", uc_bpf_buffer_consume }, }; static void uc_bpf_fd_free(void *ptr) @@ -1034,5 +1287,6 @@ void uc_module_init(uc_vm_t *vm, uc_value_t *scope) uc_type_declare(vm, "bpf.module", module_fns, module_free); uc_type_declare(vm, "bpf.map", map_fns, uc_bpf_fd_free); uc_type_declare(vm, "bpf.map_iter", map_iter_fns, NULL); + uc_type_declare(vm, "bpf.buffer", buffer_fns, uc_bpf_buffer_free); uc_type_declare(vm, "bpf.program", prog_fns, uc_bpf_fd_free); } From 9565a9bd917df2837b0bf7c16844a22fa30a902d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:28:27 +0200 Subject: [PATCH 104/152] ucode-mod-bpf: add map info method and map type constants Scripts need the map type to know how to interact with a map, e.g. per-CPU value handling or keyless queue/stack semantics. Add an info() map method returning type, key/value size and max_entries, along with BPF_MAP_TYPE constants for comparison. Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/src/bpf.c | 45 +++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c index 78971bdfbd..0deac6b783 100644 --- a/package/utils/ucode-mod-bpf/src/bpf.c +++ b/package/utils/ucode-mod-bpf/src/bpf.c @@ -34,6 +34,7 @@ struct uc_bpf_map { struct uc_bpf_fd fd; /* must be first */ unsigned int type; unsigned int key_size, val_size; + unsigned int max_entries; }; struct uc_bpf_map_iter { @@ -189,7 +190,8 @@ uc_bpf_open_module(uc_vm_t *vm, size_t nargs) static uc_value_t * uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int type, - unsigned int key_size, unsigned int val_size, bool close) + unsigned int key_size, unsigned int val_size, + unsigned int max_entries, bool close) { struct uc_bpf_map *uc_map; uc_value_t *res; @@ -200,6 +202,7 @@ uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int type, uc_map->type = type; uc_map->key_size = key_size; uc_map->val_size = val_size; + uc_map->max_entries = max_entries; uc_map->fd.close = close; return res; @@ -233,7 +236,7 @@ uc_bpf_open_map_fd(uc_vm_t *vm, int fd) } return uc_bpf_map_create(vm, NULL, fd, info.type, info.key_size, - info.value_size, true); + info.value_size, info.max_entries, true); } static uc_value_t * @@ -339,7 +342,7 @@ uc_bpf_module_get_map(uc_vm_t *vm, size_t nargs) return uc_bpf_map_create(vm, _uc_fn_this_res(vm), fd, bpf_map__type(map), bpf_map__key_size(map), bpf_map__value_size(map), - false); + bpf_map__max_entries(map), false); } static uc_value_t * @@ -550,6 +553,24 @@ uc_bpf_map_val_arg(struct uc_bpf_map *map, uc_value_t *a_val, void *buf, return buf; } +static uc_value_t * +uc_bpf_map_info(uc_vm_t *vm, size_t nargs) +{ + struct uc_bpf_map *map = uc_fn_thisval("bpf.map"); + uc_value_t *rv; + + if (!map) + err_return(EINVAL, NULL); + + rv = ucv_object_new(vm); + ucv_object_add(rv, "type", ucv_int64_new(map->type)); + ucv_object_add(rv, "key_size", ucv_int64_new(map->key_size)); + ucv_object_add(rv, "value_size", ucv_int64_new(map->val_size)); + ucv_object_add(rv, "max_entries", ucv_int64_new(map->max_entries)); + + return rv; +} + static uc_value_t * uc_bpf_map_get(uc_vm_t *vm, size_t nargs) { @@ -1199,6 +1220,23 @@ register_constants(uc_vm_t *vm, uc_value_t *scope) ADD_CONST(BPF_PROG_TYPE_SCHED_CLS); ADD_CONST(BPF_PROG_TYPE_SCHED_ACT); + ADD_CONST(BPF_MAP_TYPE_HASH); + ADD_CONST(BPF_MAP_TYPE_ARRAY); + ADD_CONST(BPF_MAP_TYPE_PROG_ARRAY); + ADD_CONST(BPF_MAP_TYPE_PERF_EVENT_ARRAY); + ADD_CONST(BPF_MAP_TYPE_PERCPU_HASH); + ADD_CONST(BPF_MAP_TYPE_PERCPU_ARRAY); + ADD_CONST(BPF_MAP_TYPE_LRU_HASH); + ADD_CONST(BPF_MAP_TYPE_LRU_PERCPU_HASH); + ADD_CONST(BPF_MAP_TYPE_LPM_TRIE); + ADD_CONST(BPF_MAP_TYPE_ARRAY_OF_MAPS); + ADD_CONST(BPF_MAP_TYPE_HASH_OF_MAPS); + ADD_CONST(BPF_MAP_TYPE_DEVMAP); + ADD_CONST(BPF_MAP_TYPE_SOCKMAP); + ADD_CONST(BPF_MAP_TYPE_QUEUE); + ADD_CONST(BPF_MAP_TYPE_STACK); + ADD_CONST(BPF_MAP_TYPE_RINGBUF); + ADD_CONST(BPF_ANY); ADD_CONST(BPF_NOEXIST); ADD_CONST(BPF_EXIST); @@ -1221,6 +1259,7 @@ static void module_free(void *ptr) static const uc_function_list_t map_fns[] = { { "pin", uc_bpf_map_pin }, + { "info", uc_bpf_map_info }, { "get", uc_bpf_map_get }, { "set", uc_bpf_map_set }, { "delete", uc_bpf_map_delete }, From 5b3b961f41e7d77242413d3e007336d94c445c82 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 16 Jul 2026 20:07:34 +0200 Subject: [PATCH 105/152] ucode-mod-bpf: bump PKG_RELEASE Signed-off-by: Felix Fietkau --- package/utils/ucode-mod-bpf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/utils/ucode-mod-bpf/Makefile b/package/utils/ucode-mod-bpf/Makefile index 8454ad16c4..40c1f60972 100644 --- a/package/utils/ucode-mod-bpf/Makefile +++ b/package/utils/ucode-mod-bpf/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ucode-mod-bpf -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_LICENSE:=ISC PKG_MAINTAINER:=Felix Fietkau From 10dc55290ec18bb6851c1e0c574a33dea47e2171 Mon Sep 17 00:00:00 2001 From: Eric Fahlgren Date: Sun, 12 Jul 2026 10:33:48 -0700 Subject: [PATCH 106/152] package: rtl826x-firmware: make package versions consistent The two packages rtl826x-firmware and rtl826x-lp-firmware are built from the same source, yet have 'VERSION=xxx' overrides that modify the package name and metadata. This causes issues with apk, as it always selects the package with the higher version number (the '-lp-' one), irrespective of which package is desired. Remove the VERSION overrides and use the actual package version so that apk can install the correct package. The binary contents of the packages remain unaffect, this only changes the package file name and metadata related to versioning. Before rtl826x-firmware-20221115.apk rtl826x-lp-firmware-20240729.apk After rtl826x-firmware-2026.01.24~0cd4abe2-r4.apk rtl826x-lp-firmware-2026.01.24~0cd4abe2-r4.apk Fixes: https://github.com/openwrt/openwrt/issues/24145 Signed-off-by: Eric Fahlgren Link: https://github.com/openwrt/openwrt/pull/24201 Signed-off-by: Jonas Jelonek --- package/firmware/rtl826x-firmware/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/package/firmware/rtl826x-firmware/Makefile b/package/firmware/rtl826x-firmware/Makefile index 8cc54b69f1..415dde93ff 100644 --- a/package/firmware/rtl826x-firmware/Makefile +++ b/package/firmware/rtl826x-firmware/Makefile @@ -33,13 +33,11 @@ endef define Package/rtl826x-firmware $(call Package/rtl826x-firmware/Default) TITLE:=Realtek RTL8251L/RTL8261BE/RTL8261N/RTL8254B/RTL8264/RTL8264B firmware - VERSION:=20221115 endef define Package/rtl826x-lp-firmware $(call Package/rtl826x-firmware/Default) TITLE:=Realtek RTL8251L/RTL8261BE/RTL8261N/RTL8254B/RTL8264/RTL8264B low-power firmware - VERSION:=20240729 PROVIDES:=rtl826x-firmware CONFLICTS:=rtl826x-firmware endef From 39cd3f8954af90ebd20fc90eb77d1b519b320b39 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sun, 19 Jul 2026 18:53:38 +0000 Subject: [PATCH 107/152] realtek: pcs: rtl931x: fix 1000Base-X config Commit 7899dfa6ef71 ("realtek: pcs: rtl931x: separate and reuse 1000base-x config") introduced an issue by accident, using the register value 0x12 instead of 0x13. A regression hasn't been noticed before, for yet unknown reasons the 1000Base-X still worked here and there. Probably due to some external setup. However, this currently breaks 1000Base-X functionality on at least one switch. Correcting the register value instantly restores 1000Base-X, allowing the link to come up. It is unclear yet what this field means, according to the SerDes register defines from related RTL8295 this might be TX mode configuration field. Also add another write which is present in several SDK versions. Referring from related RTL8295, this might be a field called EN_LINK_FIB1G, enabling 1000Base-X operation. Fixes: 7899dfa6ef71 ("realtek: pcs: rtl931x: separate and reuse 1000base-x config") Link: https://github.com/openwrt/openwrt/pull/24310 Signed-off-by: Jonas Jelonek --- target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 12f411db48..9da31a8bab 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3658,10 +3658,11 @@ static int rtpcs_931x_sds_config_attachment(struct rtpcs_serdes *sds, static int rtpcs_931x_sds_config_fiber_1g(struct rtpcs_serdes *sds) { - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB_EXT), 0x12, 15, 14, 0x0); + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB_EXT), 0x13, 15, 14, 0x0); rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), 0x0, 6, 6, 0x1); rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), 0x0, 13, 13, 0x0); + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_SDS), 0x4, 2, 2, 0x1); /* EN_LINK_FIB1G */ return 0; } From d9fdf451f17c6a642d1eec5b053a82301f72b6ce Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Thu, 16 Jul 2026 22:59:40 +0000 Subject: [PATCH 108/152] realtek: pcs: rtl931x: drop unused helper Drop the unused helper rtpcs_931x_sds_reset because it is unused and - if it was used - would actively mess with the defined SerDes deactivation/activation in pcs_config. Link: https://github.com/openwrt/openwrt/pull/24310 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 9da31a8bab..bf9abc505d 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3262,30 +3262,6 @@ static int rtpcs_931x_sds_activate(struct rtpcs_serdes *sds) return rtpcs_931x_sds_power(sds, true); } -__maybe_unused -static void rtpcs_931x_sds_reset(struct rtpcs_serdes *sds) -{ - u32 o_mode, f_bit; - - /* TODO: We need to lock this! */ - - rtpcs_931x_sds_power(sds, false); - - /* save current */ - regmap_field_read(sds->swcore_regs.mac_mode, &o_mode); - regmap_field_read(sds->swcore_regs.mac_mode_force, &f_bit); - - /* force off */ - regmap_field_write(sds->swcore_regs.mac_mode, 0x1f); - regmap_field_write(sds->swcore_regs.mac_mode_force, 1); - - /* restore previous */ - regmap_field_write(sds->swcore_regs.mac_mode, o_mode); - regmap_field_write(sds->swcore_regs.mac_mode_force, f_bit); - - rtpcs_931x_sds_power(sds, true); -} - static void rtpcs_931x_sds_rx_reset(struct rtpcs_serdes *sds) { if (sds->type != RTPCS_SDS_TYPE_10G) From b489ab0d86fcdf72c66cd5dbec13735d8cdd86d5 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Thu, 16 Jul 2026 23:03:36 +0000 Subject: [PATCH 109/152] realtek: pcs: rtl931x: drop RX channel resets during USXGMII config The mode configuration calls LEQ/DFE reset and RX reset before doing USXGMII configuration. Though the SDK does that similar, these calls are actually unneeded because essentially the same is executed later again in the attachment configuration. This is also the place where it rather belongs. Thus, drop those calls in mode configuration. Link: https://github.com/openwrt/openwrt/pull/24310 Signed-off-by: Jonas Jelonek --- .../linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index bf9abc505d..2598360e81 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3262,6 +3262,7 @@ static int rtpcs_931x_sds_activate(struct rtpcs_serdes *sds) return rtpcs_931x_sds_power(sds, true); } +__maybe_unused static void rtpcs_931x_sds_rx_reset(struct rtpcs_serdes *sds) { if (sds->type != RTPCS_SDS_TYPE_10G) @@ -3684,9 +3685,6 @@ static int rtpcs_931x_sds_config_hw_mode(struct rtpcs_serdes *sds, case RTPCS_SDS_MODE_USXGMII_5GSXGMII: case RTPCS_SDS_MODE_USXGMII_5GDXGMII: case RTPCS_SDS_MODE_USXGMII_2_5GSXGMII: - rtpcs_931x_sds_reset_leq_dfe(sds); - rtpcs_931x_sds_rx_reset(sds); - rtpcs_93xx_sds_usxgmii_config(sds, RTPCS_USXGMII_AN_OPC_STD, 0xa4, 0, 1, 0x1); break; From b253af36987da20cd33160da350a3c67884bbd33 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Thu, 16 Jul 2026 23:28:04 +0000 Subject: [PATCH 110/152] realtek: pcs: rtl931x: add info to config bits Give two SerDes register writes some meaningful information, since they line up with usual PHY registers. Link: https://github.com/openwrt/openwrt/pull/24310 Signed-off-by: Jonas Jelonek --- .../linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 2598360e81..e0131dbd3d 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3637,9 +3637,9 @@ static int rtpcs_931x_sds_config_fiber_1g(struct rtpcs_serdes *sds) { rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB_EXT), 0x13, 15, 14, 0x0); - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), 0x0, 6, 6, 0x1); - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), 0x0, 13, 13, 0x0); - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_SDS), 0x4, 2, 2, 0x1); /* EN_LINK_FIB1G */ + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), MII_BMCR, 6, 6, 0x1); /* BMCR_SPEED1000 */ + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), MII_BMCR, 13, 13, 0x0); /* BMCR_SPEED100 */ + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_SDS), 0x4, 2, 2, 0x1); /* EN_LINK_FIB1G */ return 0; } From e05153e47e420ea3535aacb4db7568aa95ebcc6b Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Thu, 16 Jul 2026 23:31:10 +0000 Subject: [PATCH 111/152] realtek: pcs: rtl931x: do not configure 1000Base-X for 10GBase-R So far, the 10GBase-R configuration mirrored the SDK which in addition also sets up 1000Base-X and lets it run in a special mode having autodetection of 10G/1G. This doesn't make that much sense given that Linux will trigger PCS reconfiguration once the interface mode has changed. Thus, we should only provide that for each mode standalone, not for some proprietary and special mixed auto mode. Link: https://github.com/openwrt/openwrt/pull/24310 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index e0131dbd3d..0e4122f3d5 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -3633,17 +3633,6 @@ static int rtpcs_931x_sds_config_attachment(struct rtpcs_serdes *sds, return 0; } -static int rtpcs_931x_sds_config_fiber_1g(struct rtpcs_serdes *sds) -{ - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB_EXT), 0x13, 15, 14, 0x0); - - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), MII_BMCR, 6, 6, 0x1); /* BMCR_SPEED1000 */ - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), MII_BMCR, 13, 13, 0x0); /* BMCR_SPEED100 */ - rtpcs_sds_write_bits(sds, DIGI_1(PAGE_SDS), 0x4, 2, 2, 0x1); /* EN_LINK_FIB1G */ - - return 0; -} - static int rtpcs_931x_sds_config_hw_mode(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode) { @@ -3652,23 +3641,23 @@ static int rtpcs_931x_sds_config_hw_mode(struct rtpcs_serdes *sds, break; case RTPCS_SDS_MODE_1000BASEX: - rtpcs_931x_sds_config_fiber_1g(sds); + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB_EXT), 0x13, 15, 14, 0); + + /* BMCR_SPEED1000 */ + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), MII_BMCR, 6, 6, 1); + /* BMCR_SPEED100 */ + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_FIB), MII_BMCR, 13, 13, 0); + /* EN_LINK_FIB1G */ + rtpcs_sds_write_bits(sds, DIGI_1(PAGE_SDS), 0x4, 2, 2, 1); break; case RTPCS_SDS_MODE_2500BASEX: rtpcs_sds_write_bits(sds, DIGI_1(PAGE_SDS_EXT), 0x14, 8, 8, 1); break; - case RTPCS_SDS_MODE_10GBASER: /* 10GR1000BX_AUTO */ + case RTPCS_SDS_MODE_10GBASER: /* configure 10GR fiber mode=1 */ rtpcs_sds_write_bits(sds, PAGE_WDIG, 0xb, 1, 1, 1); - - rtpcs_931x_sds_config_fiber_1g(sds); - - /* init auto */ - rtpcs_sds_write_bits(sds, PAGE_WDIG, 13, 15, 0, 0x109e); - rtpcs_sds_write_bits(sds, PAGE_WDIG, 0x6, 14, 10, 0x8); - rtpcs_sds_write_bits(sds, PAGE_WDIG, 0x7, 10, 4, 0x7f); break; case RTPCS_SDS_MODE_SGMII: From e1a6d77124dffa1e5dbf52d4fc6101181e36cf2f Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 17 Jul 2026 08:45:41 +0000 Subject: [PATCH 112/152] realtek: pcs: rtl930x: apply only needed patching For 10GBase-R and 2500Base-X, the patching sequences for the slower modes are also applied. This mirrors the SDK which uses some automatic detection and only does a one-time initialization. However, Linux works different. The PCS is configured to a specific mode on demand. Thus, there is no need to apply the other patching when it isn't really needed. Those patching sequences seem to initialize AFE blocks within the SerDes, thus a 1G AFE shouldn't be needed for 10GBase-R. Testing confirms this so far, no difference in any link characteristics when e.g. 1G and 2.5G sequences aren't applied for a 10GBase-R link. Link: https://github.com/openwrt/openwrt/pull/24310 Signed-off-by: Jonas Jelonek --- .../files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 0e4122f3d5..5b3643364b 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -2920,21 +2920,6 @@ static int rtpcs_930x_sds_config_hw_mode(struct rtpcs_serdes *sds, enum rtpcs_sd rtpcs_sds_write(sds, PAGE_TGR_PRO_0, 0x0D, 0x0F00); rtpcs_sds_write(sds, PAGE_TGR_PRO_0, 0x00, 0x0000); rtpcs_sds_write(sds, PAGE_TGR_PRO_0, 0x01, 0xC800); - /* - * TODO: Do the 1G and 3G sequences need to be applied? The SDK usually - * uses a 10GR-1000BX automatic mode covering all speeds. But in Linux, - * we switch the mode on demand so might only need to apply one sequence - * at a time. - */ - ret = rtpcs_sds_apply_config(sds, rtpcs_930x_sds_cfg_ana_1g, - ARRAY_SIZE(rtpcs_930x_sds_cfg_ana_1g)); - if (ret < 0) - return ret; - - ret = rtpcs_sds_apply_config(sds, rtpcs_930x_sds_cfg_ana_3g, - ARRAY_SIZE(rtpcs_930x_sds_cfg_ana_3g)); - if (ret < 0) - return ret; ret = rtpcs_sds_apply_config(sds, rtpcs_930x_sds_cfg_ana_10g, ARRAY_SIZE(rtpcs_930x_sds_cfg_ana_10g)); @@ -2945,11 +2930,6 @@ static int rtpcs_930x_sds_config_hw_mode(struct rtpcs_serdes *sds, enum rtpcs_sd break; case RTPCS_SDS_MODE_2500BASEX: - ret = rtpcs_sds_apply_config(sds, rtpcs_930x_sds_cfg_ana_1g, - ARRAY_SIZE(rtpcs_930x_sds_cfg_ana_1g)); - if (ret < 0) - return ret; - ret = rtpcs_sds_apply_config(sds, rtpcs_930x_sds_cfg_ana_3g, ARRAY_SIZE(rtpcs_930x_sds_cfg_ana_3g)); if (ret < 0) From 489456204936438ee36a19ce21cc070c82e6c3d0 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Mon, 20 Jul 2026 06:52:05 +0000 Subject: [PATCH 113/152] realtek: pcs: drop unneeded pointer cast The return value of device_get_match_data was still explicitly casted to the driver-internal type. However, since this function returns 'const void *' and in C void pointers are automatically casted, this isn't needed. Thus, drop it. Link: https://github.com/openwrt/openwrt/pull/24310 Signed-off-by: Jonas Jelonek --- target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c index 5b3643364b..538829bace 100644 --- a/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.18/drivers/net/pcs/pcs-rtl-otto.c @@ -4119,7 +4119,7 @@ static int rtpcs_probe(struct platform_device *pdev) mutex_init(&ctrl->lock); ctrl->dev = dev; - ctrl->cfg = (const struct rtpcs_config *)device_get_match_data(ctrl->dev); + ctrl->cfg = device_get_match_data(ctrl->dev); ctrl->map = syscon_node_to_regmap(np->parent); if (IS_ERR(ctrl->map)) return PTR_ERR(ctrl->map); From 7c2586ffe77fee4276350bce5c954718d500f22d Mon Sep 17 00:00:00 2001 From: Nickolay Savchenko Date: Fri, 17 Jul 2026 16:13:05 +0300 Subject: [PATCH 114/152] ramips: fix frame engine DMA length cleanup fe_txd_unmap() uses dma_len0 and dma_len1 to decide whether each TX mapping must be passed to dma_unmap_page(). After unmapping, however, the code passed the dma_addr0 and dma_addr1 field identifiers to dma_unmap_len_set(). This leaves the actual length fields unchanged. A reused descriptor slot can therefore retain a non-zero length and attempt to unmap an old DMA mapping again. Clear dma_len0 and dma_len1 after the mappings have been released. Signed-off-by: Nickolay Savchenko Link: https://github.com/openwrt/openwrt/pull/24268 Signed-off-by: Jonas Jelonek --- .../ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c b/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c index 7580b2d42b..a7b421ceb5 100644 --- a/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c +++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c @@ -344,8 +344,8 @@ static void fe_txd_unmap(struct device *dev, struct fe_tx_buf *tx_buf) dma_unmap_len(tx_buf, dma_len1), DMA_TO_DEVICE); - dma_unmap_len_set(tx_buf, dma_addr0, 0); - dma_unmap_len_set(tx_buf, dma_addr1, 0); + dma_unmap_len_set(tx_buf, dma_len0, 0); + dma_unmap_len_set(tx_buf, dma_len1, 0); if (tx_buf->skb && (tx_buf->skb != (struct sk_buff *)DMA_DUMMY_DESC)) dev_kfree_skb_any(tx_buf->skb); tx_buf->skb = NULL; From 364b4c0d24cae0bbf5f15a53e73bb2ad3cc579c3 Mon Sep 17 00:00:00 2001 From: Nickolay Savchenko Date: Fri, 17 Jul 2026 16:13:05 +0300 Subject: [PATCH 115/152] ramips: reject invalid frame engine RX lengths fe_poll_rx() takes the packet length directly from the RX descriptor and passes it to skb_put() without checking that it fits in the allocated buffer. A malformed or stale descriptor can therefore extend the skb beyond its tailroom and corrupt memory. Reject lengths larger than skb_tailroom(), account the packet in rx_length_errors and rx_dropped, free the affected skb, and continue by installing the replacement descriptor. Signed-off-by: Nickolay Savchenko Link: https://github.com/openwrt/openwrt/pull/24268 Signed-off-by: Jonas Jelonek --- .../files/drivers/net/ethernet/ralink/mtk_eth_soc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c b/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c index a7b421ceb5..ad46589268 100644 --- a/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c +++ b/target/linux/ramips/files/drivers/net/ethernet/ralink/mtk_eth_soc.c @@ -970,6 +970,16 @@ static int fe_poll_rx(struct napi_struct *napi, int budget, ring->rx_buf_size, DMA_FROM_DEVICE); pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); skb->dev = netdev; + if (unlikely(pktlen > skb_tailroom(skb))) { + if (net_ratelimit()) + netdev_warn(netdev, + "dropping invalid RX length %u (buffer %u, descriptor %08x)\n", + pktlen, skb_tailroom(skb), trxd.rxd2); + stats->rx_length_errors++; + stats->rx_dropped++; + dev_kfree_skb_any(skb); + goto replace_desc; + } skb_put(skb, pktlen); if (trxd.rxd4 & checksum_bit) skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -987,6 +997,7 @@ static int fe_poll_rx(struct napi_struct *napi, int budget, napi_gro_receive(napi, skb); +replace_desc: ring->rx_data[idx] = new_data; rxd->rxd1 = (unsigned int)dma_addr; From f31b65a870f7b8b133bccf879cb3c5fc909d5d86 Mon Sep 17 00:00:00 2001 From: Mieczyslaw Nalewaj Date: Wed, 15 Jul 2026 18:58:21 +0200 Subject: [PATCH 116/152] ledtrig-network: default missing family to WLAN on non-DT LEDs For LEDs that have no network trigger configuration in Device Tree (no mode or family properties), net_activate() infers the family from the LED name or function string. If that inference fails, the trigger previously returned -EINVAL and refused to activate. This is too strict for manual activation via sysfs (e.g. echo "network" > /sys/class/leds//trigger) on LEDs that were not declared for the network trigger in DT. In that case the user expects the trigger to load with sensible defaults. Change the non-DT activation path to fall back to NET_TRIG_WLAN instead of failing, while keeping the condition visible via pr_warn(). Device Tree paths with dt_mode or dt_family are left unchanged and continue to fail on invalid parameters Signed-off-by: Mieczyslaw Nalewaj Link: https://github.com/openwrt/openwrt/pull/24235 Signed-off-by: Jonas Jelonek --- .../generic/files/drivers/leds/trigger/ledtrig-network.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/linux/generic/files/drivers/leds/trigger/ledtrig-network.c b/target/linux/generic/files/drivers/leds/trigger/ledtrig-network.c index e2e60c3a26..e40497dd8b 100644 --- a/target/linux/generic/files/drivers/leds/trigger/ledtrig-network.c +++ b/target/linux/generic/files/drivers/leds/trigger/ledtrig-network.c @@ -1070,9 +1070,9 @@ static int net_activate(struct led_classdev *led_cdev) */ parsed = parse_family_token(fn, &online); if (parsed < 0) { - pr_info("network: unknown function '%s' for LED %s\n", + pr_warn("network: cannot infer family from '%s' for LED %s, defaulting to 'wlan'\n", fn ?: "", name); - return -EINVAL; + parsed = NET_TRIG_WLAN; } if (online) { From b73b209693f5c8e8a0231416318c017822698671 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Fri, 17 Jul 2026 14:23:18 +0200 Subject: [PATCH 117/152] realtek: i2c: replace shared gpio driver with upstream Upstream will support I2C GPIO based buses with shared SCL shortly [1]. Drop downstream driver, provide backport and adapt DTS. [1] https://lore.kernel.org/linux-i2c/20260714162915.3018703-1-markus.stockhausen@gmx.de/ Link: https://github.com/openwrt/openwrt/pull/24269 Signed-off-by: Markus Stockhausen --- .../dts/rtl8380_engenius_ews2910p.dtsi | 23 +- .../realtek/dts/rtl8380_linksys_lgs310c.dts | 23 +- .../dts/rtl8391_zyxel_gs1920-24hp-v2.dts | 22 +- .../realtek/dts/rtl8393_hpe_1920-48g.dts | 48 +-- .../dts/rtl9301_d-link_dgs-1250-28x.dts | 46 ++- .../bindings/i2c/i2c-gpio-shared.yaml | 51 --- .../drivers/i2c/busses/i2c-gpio-shared.c | 177 -------- ...-i2c-algo-bit-Allow-to-skip-bit-test.patch | 46 +++ ...nce-driver-for-buses-with-shared-SCL.patch | 384 ++++++++++++++++++ .../805-add-i2c-gpio-shared-driver.patch | 41 -- target/linux/realtek/rtl838x/config-6.18 | 1 - target/linux/realtek/rtl839x/config-6.18 | 1 - target/linux/realtek/rtl930x/config-6.18 | 1 - target/linux/realtek/rtl930x_nand/config-6.18 | 1 - target/linux/realtek/rtl931x/config-6.18 | 1 - target/linux/realtek/rtl931x_nand/config-6.18 | 1 - 16 files changed, 509 insertions(+), 358 deletions(-) delete mode 100644 target/linux/realtek/files-6.18/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml delete mode 100644 target/linux/realtek/files-6.18/drivers/i2c/busses/i2c-gpio-shared.c create mode 100644 target/linux/realtek/patches-6.18/033-01-v7.3-i2c-algo-bit-Allow-to-skip-bit-test.patch create mode 100644 target/linux/realtek/patches-6.18/033-02-v7.3-i2c-gpio-Enhance-driver-for-buses-with-shared-SCL.patch delete mode 100644 target/linux/realtek/patches-6.18/805-add-i2c-gpio-shared-driver.patch diff --git a/target/linux/realtek/dts/rtl8380_engenius_ews2910p.dtsi b/target/linux/realtek/dts/rtl8380_engenius_ews2910p.dtsi index 3b157c379e..74f51e63c9 100644 --- a/target/linux/realtek/dts/rtl8380_engenius_ews2910p.dtsi +++ b/target/linux/realtek/dts/rtl8380_engenius_ews2910p.dtsi @@ -66,21 +66,22 @@ }; }; - i2c-gpio-shared { - compatible = "i2c-gpio-shared"; + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; #address-cells = <1>; #size-cells = <0>; + }; - i2c0: i2c@0 { - sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; - - i2c1: i2c@1 { - sda-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; }; sfp0: sfp-p9 { diff --git a/target/linux/realtek/dts/rtl8380_linksys_lgs310c.dts b/target/linux/realtek/dts/rtl8380_linksys_lgs310c.dts index 834d37518e..d63ee3d93d 100644 --- a/target/linux/realtek/dts/rtl8380_linksys_lgs310c.dts +++ b/target/linux/realtek/dts/rtl8380_linksys_lgs310c.dts @@ -41,21 +41,22 @@ }; }; - i2c-gpio-shared { - compatible = "i2c-gpio-shared"; + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; #address-cells = <1>; #size-cells = <0>; + }; - i2c0: i2c@0 { - sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; - - i2c1: i2c@1 { - sda-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; }; sfp0: sfp-p9 { diff --git a/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts b/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts index f872bd22c4..a9cfd1396e 100644 --- a/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts +++ b/target/linux/realtek/dts/rtl8391_zyxel_gs1920-24hp-v2.dts @@ -51,26 +51,10 @@ linux,input-type = ; }; }; +}; - /delete-node/ i2c-gpio-0; - /delete-node/ i2c-gpio-1; - - i2c-gpio-shared { - compatible = "i2c-gpio-shared"; - scl-gpios = <&gpio1 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - #address-cells = <1>; - #size-cells = <0>; - - i2c0: i2c@0 { - sda-gpios = <&gpio1 4 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; - - i2c1: i2c@1 { - sda-gpios = <&gpio1 10 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; - }; +&i2c1 { + scl-gpios = <&gpio1 3 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; }; &i2c2 { diff --git a/target/linux/realtek/dts/rtl8393_hpe_1920-48g.dts b/target/linux/realtek/dts/rtl8393_hpe_1920-48g.dts index db83b4fe09..72ee2512e4 100644 --- a/target/linux/realtek/dts/rtl8393_hpe_1920-48g.dts +++ b/target/linux/realtek/dts/rtl8393_hpe_1920-48g.dts @@ -6,40 +6,40 @@ compatible = "hpe,1920-48g", "realtek,rtl8393-soc"; model = "HPE 1920-48G (JG927A)"; - i2c-gpio-shared0 { - compatible = "i2c-gpio-shared"; + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio0 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; #address-cells = <1>; #size-cells = <0>; - - i2c0: i2c@0 { - sda-gpios = <&gpio0 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; - - i2c2: i2c@2 { - sda-gpios = <&gpio0 21 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - - }; }; - i2c-gpio-shared1 { - compatible = "i2c-gpio-shared"; + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; #address-cells = <1>; #size-cells = <0>; + }; - i2c1: i2c@1 { - sda-gpios = <&gpio0 2 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; + i2c2: i2c-gpio-2 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 21 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; - i2c3: i2c@3 { - sda-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - - }; + i2c3: i2c-gpio-3 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <2>; + #address-cells = <1>; + #size-cells = <0>; }; sfp0: sfp-p49 { diff --git a/target/linux/realtek/dts/rtl9301_d-link_dgs-1250-28x.dts b/target/linux/realtek/dts/rtl9301_d-link_dgs-1250-28x.dts index 3696a9af7e..a03cfe838f 100644 --- a/target/linux/realtek/dts/rtl9301_d-link_dgs-1250-28x.dts +++ b/target/linux/realtek/dts/rtl9301_d-link_dgs-1250-28x.dts @@ -33,31 +33,41 @@ #cooling-cells = <2>; }; - i2c-gpio-shared { - compatible = "i2c-gpio-shared"; + i2c0: i2c-gpio-0 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; scl-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; #address-cells = <1>; #size-cells = <0>; + }; - i2c0: i2c@0 { - sda-gpios = <&gpio0 17 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <5>; - }; + i2c1: i2c-gpio-1 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + #address-cells = <1>; + #size-cells = <0>; + }; - i2c1: i2c@1 { - sda-gpios = <&gpio0 18 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <5>; - }; - i2c2: i2c@2 { - sda-gpios = <&gpio0 19 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <5>; - }; + i2c2: i2c-gpio-2 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 19 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + #address-cells = <1>; + #size-cells = <0>; + }; - i2c3: i2c@3 { - sda-gpios = <&gpio0 20 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <5>; - }; + i2c3: i2c-gpio-3 { + compatible = "i2c-gpio"; + sda-gpios = <&gpio0 20 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio0 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-gpio,delay-us = <5>; + #address-cells = <1>; + #size-cells = <0>; }; i2c4: i2c-gpio-4 { diff --git a/target/linux/realtek/files-6.18/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml b/target/linux/realtek/files-6.18/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml deleted file mode 100644 index f496f957b7..0000000000 --- a/target/linux/realtek/files-6.18/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/i2c/i2c-gpio-shared.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: mulitple GPIO bitbanged I2C busses with shared SCL - -maintainers: - - Markus Stockhausen - -allOf: - - $ref: /schemas/i2c/i2c-controller.yaml# - -properties: - compatible: - items: - - const: i2c-gpio-shared - - scl-gpios: - description: - gpio used for the shared scl signal, this should be flagged as - active high using open drain with (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN) - from since the signal is by definition - open drain. - maxItems: 1 - -examples: - - | - #include - - i2c-gpio-shared { - compatible = "i2c-gpio-shared"; - scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - #address-cells = <1>; - #size-cells = <0>; - - i2c0: i2c@0 { - sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; - - i2c1: i2c@1 { - sda-gpios = <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; - i2c-gpio,delay-us = <2>; - }; - }; - -required: - - compatible - - scl-gpios \ No newline at end of file diff --git a/target/linux/realtek/files-6.18/drivers/i2c/busses/i2c-gpio-shared.c b/target/linux/realtek/files-6.18/drivers/i2c/busses/i2c-gpio-shared.c deleted file mode 100644 index c2e7d85ca8..0000000000 --- a/target/linux/realtek/files-6.18/drivers/i2c/busses/i2c-gpio-shared.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Bitbanging driver for multiple I2C busses with shared SCL pin using the GPIO API - * Copyright (c) 2025 Markus Stockhausen - */ - -#include -#include -#include -#include -#include - -#define GPIO_SHARED_MAX_BUS 4 - -struct gpio_shared_ctx; - -struct gpio_shared_bus { - int num; - struct gpio_desc *sda; - struct i2c_adapter adap; - struct i2c_algo_bit_data bit_data; - struct gpio_shared_ctx *ctx; -}; - -struct gpio_shared_ctx { - struct device *dev; - struct gpio_desc *scl; - struct mutex lock; - struct gpio_shared_bus bus[GPIO_SHARED_MAX_BUS]; -}; - -static void gpio_shared_setsda(void *data, int state) -{ - struct gpio_shared_bus *bus = data; - - gpiod_set_value_cansleep(bus->sda, state); -} - -static void gpio_shared_setscl(void *data, int state) -{ - struct gpio_shared_bus *bus = data; - struct gpio_shared_ctx *ctx = bus->ctx; - - gpiod_set_value_cansleep(ctx->scl, state); -} - -static int gpio_shared_getsda(void *data) -{ - struct gpio_shared_bus *bus = data; - - return gpiod_get_value_cansleep(bus->sda); -} - -static int gpio_shared_getscl(void *data) -{ - struct gpio_shared_bus *bus = data; - struct gpio_shared_ctx *ctx = bus->ctx; - - return gpiod_get_value_cansleep(ctx->scl); -} - -static int gpio_shared_pre_xfer(struct i2c_adapter *adap) -{ - struct gpio_shared_bus *bus = container_of(adap, typeof(*bus), adap); - struct gpio_shared_ctx *ctx = bus->ctx; - - mutex_lock(&ctx->lock); - dev_dbg(ctx->dev, "lock before transfer to bus %d\n", bus->num); - - return 0; -} - -static void gpio_shared_post_xfer(struct i2c_adapter *adap) -{ - struct gpio_shared_bus *bus = container_of(adap, typeof(*bus), adap); - struct gpio_shared_ctx *ctx = bus->ctx; - - dev_dbg(ctx->dev, "unlock after transfer to bus %d\n", bus->num); - mutex_unlock(&ctx->lock); -} - -static int gpio_shared_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct gpio_shared_ctx *ctx; - int msecs, ret, bus_num = -1; - - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->dev = dev; - mutex_init(&ctx->lock); - - ctx->scl = devm_gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN); - if (IS_ERR(ctx->scl)) - return dev_err_probe(dev, PTR_ERR(ctx->scl), "shared SCL node not found\n"); - - if (device_get_child_node_count(dev) > GPIO_SHARED_MAX_BUS) - return dev_err_probe(dev, -EINVAL, "Too many channels\n"); - - device_for_each_child_node_scoped(dev, child) { - struct gpio_shared_bus *bus = &ctx->bus[++bus_num]; - struct i2c_adapter *adap = &bus->adap; - struct i2c_algo_bit_data *bit_data = &bus->bit_data; - - bus->sda = devm_fwnode_gpiod_get(dev, child, "sda", GPIOD_OUT_HIGH_OPEN_DRAIN, - fwnode_get_name(child)); - if (IS_ERR(bus->sda)) { - dev_err(dev, "SDA node for bus %d not found\n", bus_num); - continue; - } - - bus->num = bus_num; - bus->ctx = ctx; - - bit_data->data = bus; - bit_data->setsda = gpio_shared_setsda; - bit_data->setscl = gpio_shared_setscl; - bit_data->pre_xfer = gpio_shared_pre_xfer; - bit_data->post_xfer = gpio_shared_post_xfer; - - if (fwnode_property_read_u32(child, "i2c-gpio,delay-us", &bit_data->udelay)) - bit_data->udelay = 5; - if (!fwnode_property_read_bool(child, "i2c-gpio,sda-output-only")) - bit_data->getsda = gpio_shared_getsda; - if (!device_property_read_bool(dev, "i2c-gpio,scl-output-only")) - bit_data->getscl = gpio_shared_getscl; - - if (!device_property_read_u32(dev, "i2c-gpio,timeout-ms", &msecs)) - bit_data->timeout = msecs_to_jiffies(msecs); - else - bit_data->timeout = HZ / 10; /* 100ms */ - - if (gpiod_cansleep(bus->sda) || gpiod_cansleep(ctx->scl)) - dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing"); - else - bit_data->can_do_atomic = true; - - adap->owner = THIS_MODULE; - strscpy(adap->name, KBUILD_MODNAME, sizeof(adap->name)); - adap->dev.parent = dev; - device_set_node(&adap->dev, child); - adap->algo_data = &bus->bit_data; - adap->class = I2C_CLASS_HWMON; - - ret = i2c_bit_add_bus(adap); - if (ret) - return ret; - - dev_info(dev, "shared I2C bus %u using lines %u (SDA) and %u (SCL) delay=%d\n", - bus_num, desc_to_gpio(bus->sda), desc_to_gpio(ctx->scl), - bit_data->udelay); - } - - return 0; -} - -static const struct of_device_id gpio_shared_of_match[] = { - { .compatible = "i2c-gpio-shared" }, - {} -}; -MODULE_DEVICE_TABLE(of, gpio_shared_of_match); - -static struct platform_driver gpio_shared_driver = { - .probe = gpio_shared_probe, - .driver = { - .name = "i2c-gpio-shared", - .of_match_table = gpio_shared_of_match, - }, -}; - -module_platform_driver(gpio_shared_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Markus Stockhausen "); -MODULE_DESCRIPTION("bitbanging multi I2C driver for shared SCL"); diff --git a/target/linux/realtek/patches-6.18/033-01-v7.3-i2c-algo-bit-Allow-to-skip-bit-test.patch b/target/linux/realtek/patches-6.18/033-01-v7.3-i2c-algo-bit-Allow-to-skip-bit-test.patch new file mode 100644 index 0000000000..fdd07397a6 --- /dev/null +++ b/target/linux/realtek/patches-6.18/033-01-v7.3-i2c-algo-bit-Allow-to-skip-bit-test.patch @@ -0,0 +1,46 @@ +From c25b80536efdffab6a73675ae153175348b4521a Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Mon, 13 Jul 2026 19:54:03 +0200 +Subject: [PATCH 1/2] i2c: algo: bit: Allow to skip bit test + +An I2C bus can make use of shared resources. E.g. two GPIO based buses +that share a single SCL line. To synchronize access to the bus the driver +might use locking with the help of i2c_lock_operations(). While this +works for normal transfers it is not available during initialization. + +Especially if i2c-algo-bit module is loaded with parameter bit_test +it will issue some basic sanity checks that will access the bus without +locking. This might interfere badly with concurrent transfers. Even +if these are well synchronized via locks. + +Allow the consumer of an algorithm to override if the bit_test is allowed +or not. For this add a new boolean attribute named skip_bit_test to +i2c_algo_bit_data. If set the test is not run. + +Signed-off-by: Markus Stockhausen +--- + drivers/i2c/algos/i2c-algo-bit.c | 2 +- + include/linux/i2c-algo-bit.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/i2c/algos/i2c-algo-bit.c ++++ b/drivers/i2c/algos/i2c-algo-bit.c +@@ -638,7 +638,7 @@ static int __i2c_bit_add_bus(struct i2c_ + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + int ret; + +- if (bit_test) { ++ if (bit_test && !bit_adap->skip_bit_test) { + ret = test_bus(adap); + if (bit_test >= 2 && ret < 0) + return -ENODEV; +--- a/include/linux/i2c-algo-bit.h ++++ b/include/linux/i2c-algo-bit.h +@@ -34,6 +34,7 @@ struct i2c_algo_bit_data { + maximum 50 us for SMBus */ + int timeout; /* in jiffies */ + bool can_do_atomic; /* callbacks don't sleep, we can be atomic */ ++ bool skip_bit_test; /* override bit_test module parameter */ + }; + + int i2c_bit_add_bus(struct i2c_adapter *); diff --git a/target/linux/realtek/patches-6.18/033-02-v7.3-i2c-gpio-Enhance-driver-for-buses-with-shared-SCL.patch b/target/linux/realtek/patches-6.18/033-02-v7.3-i2c-gpio-Enhance-driver-for-buses-with-shared-SCL.patch new file mode 100644 index 0000000000..0dcfc07c2b --- /dev/null +++ b/target/linux/realtek/patches-6.18/033-02-v7.3-i2c-gpio-Enhance-driver-for-buses-with-shared-SCL.patch @@ -0,0 +1,384 @@ +From 7899b3e8af418d5ad6edd3cffb77df4800af52da Mon Sep 17 00:00:00 2001 +From: Markus Stockhausen +Date: Mon, 18 May 2026 18:07:21 +0200 +Subject: [PATCH] i2c: i2c-gpio: Enhance driver for buses with shared SCL + +Some lower end hardware (especially Realtek based switches) are +designed with multiple I2C buses that share a single clock line. +E.g. the D-Link DGS-1250-28X realizes 4 I2C SFP busses with 5 GPIOs. + +Enhance the i2c-gpio driver so it can handle such hardware designs. + +- Detect shared SCL GPIOs that are used by multiple I2C buses in the + devicetree by using a "unique identifier". This is basically the + phandle and all additional cells. + +- The first probing instance allocates and requests the shared SCL + GPIO with an associated rt_mutex. Subsequent instances detect the + existing entry via the identifier and increment a reference count + to reuse the descriptor. + +- All data transfers are serialized via custom lock_ops that handle + both the standard adapter bus lock and the shared SCL mutex. This + ensures mutual exclusion across adapters sharing the clock line. + +- This shared SCL detection works only for dts based systems where + the GPIO node has at least one cell (usually the pin). GPIOs in + legacy systems without devicetree will be handled individudally + as before. + +This patch was successfully tested on Linksys LGS310C that has two +SFP slots with two GPIO based I2C buses that share a single SCL. +Test environment: OpenWrt snapshot ported to kernel 6.19.14 +including CONFIG_GPIO_SHARED=y and CONFIG_GPIO_SHARED_PROXY=y. + +Signed-off-by: Markus Stockhausen +Tested-by: Sander Vanheule +Reviewed-by: Bartosz Golaszewski +Reviewed-by: Wolfram Sang +--- + drivers/i2c/busses/i2c-gpio.c | 223 +++++++++++++++++++++++++++++++--- + 1 file changed, 209 insertions(+), 14 deletions(-) + +--- a/drivers/i2c/busses/i2c-gpio.c ++++ b/drivers/i2c/busses/i2c-gpio.c +@@ -18,9 +18,21 @@ + #include + #include + ++static LIST_HEAD(i2c_gpio_scl_list); ++static DEFINE_MUTEX(i2c_gpio_scl_list_lock); ++static struct lock_class_key i2c_gpio_scl_lock_key; ++ ++struct i2c_gpio_scl_data { ++ struct fwnode_reference_args args; ++ struct gpio_desc *gpio; ++ struct rt_mutex lock; ++ struct list_head list; ++ refcount_t ref; ++}; ++ + struct i2c_gpio_private_data { + struct gpio_desc *sda; +- struct gpio_desc *scl; ++ struct i2c_gpio_scl_data *scl; + struct i2c_adapter adap; + struct i2c_algo_bit_data bit_data; + struct i2c_gpio_platform_data pdata; +@@ -31,6 +43,11 @@ struct i2c_gpio_private_data { + #endif + }; + ++static inline struct i2c_gpio_private_data *adap_to_priv(struct i2c_adapter *adap) ++{ ++ return container_of(adap, struct i2c_gpio_private_data, adap); ++} ++ + /* + * Toggle SDA by changing the output value of the pin. This is only + * valid for pins configured as open drain (i.e. setting the value +@@ -53,7 +70,7 @@ static void i2c_gpio_setscl_val(void *da + { + struct i2c_gpio_private_data *priv = data; + +- gpiod_set_value_cansleep(priv->scl, state); ++ gpiod_set_value_cansleep(priv->scl->gpio, state); + } + + static int i2c_gpio_getsda(void *data) +@@ -67,9 +84,41 @@ static int i2c_gpio_getscl(void *data) + { + struct i2c_gpio_private_data *priv = data; + +- return gpiod_get_value_cansleep(priv->scl); ++ return gpiod_get_value_cansleep(priv->scl->gpio); ++} ++ ++static void i2c_gpio_lock_bus(struct i2c_adapter *adap, unsigned int flags) ++{ ++ /* Take care about adapter lock. See i2c_adapter_lock_bus() and others. */ ++ rt_mutex_lock_nested(&adap->bus_lock, i2c_adapter_depth(adap)); ++ rt_mutex_lock(&adap_to_priv(adap)->scl->lock); ++} ++ ++static int i2c_gpio_trylock_bus(struct i2c_adapter *adap, unsigned int flags) ++{ ++ if (!rt_mutex_trylock(&adap->bus_lock)) ++ return 0; ++ ++ if (!rt_mutex_trylock(&adap_to_priv(adap)->scl->lock)) { ++ rt_mutex_unlock(&adap->bus_lock); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void i2c_gpio_unlock_bus(struct i2c_adapter *adap, unsigned int flags) ++{ ++ rt_mutex_unlock(&adap_to_priv(adap)->scl->lock); ++ rt_mutex_unlock(&adap->bus_lock); + } + ++static const struct i2c_lock_operations i2c_gpio_lock_ops = { ++ .lock_bus = i2c_gpio_lock_bus, ++ .trylock_bus = i2c_gpio_trylock_bus, ++ .unlock_bus = i2c_gpio_unlock_bus, ++}; ++ + #ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR + + #define setsda(bd, val) ((bd)->setsda((bd)->data, val)) +@@ -165,14 +214,14 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete + static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv, + irqreturn_t handler(int, void*)) + { +- int ret, irq = gpiod_to_irq(priv->scl); ++ int ret, irq = gpiod_to_irq(priv->scl->gpio); + + if (irq < 0) + return irq; + + i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); + +- ret = gpiod_direction_input(priv->scl); ++ ret = gpiod_direction_input(priv->scl->gpio); + if (ret) + goto unlock; + +@@ -187,7 +236,7 @@ static int i2c_gpio_fi_act_on_scl_irq(st + + free_irq(irq, priv); + output: +- ret = gpiod_direction_output(priv->scl, 1) ?: ret; ++ ret = gpiod_direction_output(priv->scl->gpio, 1) ?: ret; + unlock: + i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER); + +@@ -308,13 +357,17 @@ static struct gpio_desc *i2c_gpio_get_de + struct gpio_desc *retdesc; + int ret; + +- retdesc = devm_gpiod_get(dev, con_id, gflags); ++ /* ++ * Don't use resource-managed functions. SCL may be shared across adapters and has ++ * its own lifetime management. SDA uses the same path for consistency. ++ */ ++ retdesc = gpiod_get(dev, con_id, gflags); + if (!IS_ERR(retdesc)) { + dev_dbg(dev, "got GPIO from name %s\n", con_id); + return retdesc; + } + +- retdesc = devm_gpiod_get_index(dev, NULL, index, gflags); ++ retdesc = gpiod_get_index(dev, NULL, index, gflags); + if (!IS_ERR(retdesc)) { + dev_dbg(dev, "got GPIO from index %u\n", index); + return retdesc; +@@ -336,6 +389,134 @@ static struct gpio_desc *i2c_gpio_get_de + return retdesc; + } + ++static struct i2c_gpio_scl_data *i2c_gpio_create_scl(struct device *dev) ++{ ++ struct fwnode_handle *fwnode = dev_fwnode(dev); ++ struct fwnode_reference_args args; ++ struct i2c_gpio_scl_data *scl; ++ bool sharable = false; ++ int ret; ++ ++ /* ++ * SCL gpios can be shared if they are defined in the devicetree and have at least one ++ * cell in addition to the phandle. This is usually the pin. To support complex designs ++ * with arbitrary of_xlate() mappings handle the node cells opaque. ++ */ ++ if (fwnode) { ++ ret = fwnode_property_get_reference_args(fwnode, "scl-gpios", ++ "#gpio-cells", 0, 0, &args); ++ if (ret) ++ /* try the ancient way */ ++ ret = fwnode_property_get_reference_args(fwnode, "gpios", ++ "#gpio-cells", 0, 1, &args); ++ ++ if (!ret) { ++ if (args.nargs >= 1) ++ sharable = true; ++ else ++ fwnode_handle_put(args.fwnode); ++ } ++ } ++ ++ scl = kzalloc(sizeof(*scl), GFP_KERNEL); ++ if (!scl) { ++ if (sharable) ++ fwnode_handle_put(args.fwnode); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ if (sharable) { ++ scl->args.fwnode = args.fwnode; ++ scl->args.nargs = args.nargs; ++ memcpy(scl->args.args, args.args, sizeof(args.args[0]) * args.nargs); ++ } ++ ++ rt_mutex_init(&scl->lock); ++ lockdep_set_class(&scl->lock, &i2c_gpio_scl_lock_key); ++ refcount_set(&scl->ref, 1); ++ ++ return scl; ++} ++ ++static void i2c_gpio_free_scl(struct i2c_gpio_scl_data *scl) ++{ ++ if (scl->args.fwnode) ++ fwnode_handle_put(scl->args.fwnode); ++ kfree(scl); ++} ++ ++static bool i2c_gpio_scl_matches(struct i2c_gpio_scl_data *a, struct i2c_gpio_scl_data *b) ++{ ++ if (!a->args.fwnode || !b->args.fwnode || ++ a->args.fwnode != b->args.fwnode || a->args.nargs != b->args.nargs) ++ return false; ++ ++ return memcmp(a->args.args, b->args.args, sizeof(a->args.args[0]) * a->args.nargs) == 0; ++} ++ ++/* ++ * Look up an existing or create a new shared SCL structure. Optimistic setup sequence always ++ * creates and tries to add a new entry to the list. This uses minimum locking and afterwards ++ * requests the GPIO without a lock held. Concurrent probes for the same SCL pin see the entry ++ * and do not race into a second gpiod_get(). Until everything is setup they terminate with ++ * -EPROBE_DEFER. ++ */ ++static struct i2c_gpio_scl_data *i2c_gpio_lookup_scl(struct device *dev, enum gpiod_flags gflags) ++{ ++ struct i2c_gpio_scl_data *scl, *new_scl; ++ struct gpio_desc *gpio; ++ ++ new_scl = i2c_gpio_create_scl(dev); ++ if (IS_ERR(new_scl)) ++ return new_scl; ++ ++ scoped_guard(mutex, &i2c_gpio_scl_list_lock) { ++ list_for_each_entry(scl, &i2c_gpio_scl_list, list) { ++ if (!i2c_gpio_scl_matches(scl, new_scl)) ++ continue; ++ ++ i2c_gpio_free_scl(new_scl); ++ if (!scl->gpio) ++ return ERR_PTR(-EPROBE_DEFER); ++ ++ refcount_inc(&scl->ref); ++ if (scl->args.fwnode) ++ dev_dbg(dev, "reusing shared SCL (%pfwP)\n", scl->args.fwnode); ++ ++ return scl; ++ } ++ list_add(&new_scl->list, &i2c_gpio_scl_list); ++ } ++ ++ gpio = i2c_gpio_get_desc(dev, "scl", 1, gflags); ++ if (IS_ERR(gpio)) { ++ scoped_guard(mutex, &i2c_gpio_scl_list_lock) ++ list_del(&new_scl->list); ++ i2c_gpio_free_scl(new_scl); ++ ++ return ERR_CAST(gpio); ++ } ++ ++ scoped_guard(mutex, &i2c_gpio_scl_list_lock) ++ new_scl->gpio = gpio; ++ ++ if (new_scl->args.fwnode) ++ dev_dbg(dev, "registered shared SCL (%pfwP)\n", new_scl->args.fwnode); ++ ++ return new_scl; ++} ++ ++static void i2c_gpio_cleanup_scl(struct i2c_gpio_scl_data *scl) ++{ ++ if (!refcount_dec_and_mutex_lock(&scl->ref, &i2c_gpio_scl_list_lock)) ++ return; ++ ++ list_del(&scl->list); ++ mutex_unlock(&i2c_gpio_scl_list_lock); ++ gpiod_put(scl->gpio); ++ i2c_gpio_free_scl(scl); ++} ++ + static int i2c_gpio_probe(struct platform_device *pdev) + { + struct i2c_gpio_private_data *priv; +@@ -386,15 +567,18 @@ static int i2c_gpio_probe(struct platfor + gflags = GPIOD_OUT_HIGH; + else + gflags = GPIOD_OUT_HIGH_OPEN_DRAIN; +- priv->scl = i2c_gpio_get_desc(dev, "scl", 1, gflags); +- if (IS_ERR(priv->scl)) +- return PTR_ERR(priv->scl); ++ priv->scl = i2c_gpio_lookup_scl(dev, gflags); ++ if (IS_ERR(priv->scl)) { ++ ret = PTR_ERR(priv->scl); ++ goto err_cleanup_sda; ++ } + +- if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl)) ++ if (gpiod_cansleep(priv->sda) || gpiod_cansleep(priv->scl->gpio)) + dev_warn(dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing"); + else + bit_data->can_do_atomic = true; + ++ bit_data->skip_bit_test = !!priv->scl->args.fwnode; + bit_data->setsda = i2c_gpio_setsda_val; + bit_data->setscl = i2c_gpio_setscl_val; + +@@ -423,6 +607,8 @@ static int i2c_gpio_probe(struct platfor + else + snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); + ++ /* Always use shared SCL aware locking */ ++ adap->lock_ops = &i2c_gpio_lock_ops; + adap->algo_data = bit_data; + adap->class = I2C_CLASS_HWMON; + adap->dev.parent = dev; +@@ -431,7 +617,7 @@ static int i2c_gpio_probe(struct platfor + adap->nr = pdev->id; + ret = i2c_bit_add_numbered_bus(adap); + if (ret) +- return ret; ++ goto err_cleanup_scl; + + platform_set_drvdata(pdev, priv); + +@@ -441,13 +627,20 @@ static int i2c_gpio_probe(struct platfor + * from the descriptor, then provide that instead. + */ + dev_info(dev, "using lines %u (SDA) and %u (SCL%s)\n", +- desc_to_gpio(priv->sda), desc_to_gpio(priv->scl), ++ desc_to_gpio(priv->sda), desc_to_gpio(priv->scl->gpio), + pdata->scl_is_output_only + ? ", no clock stretching" : ""); + + i2c_gpio_fault_injector_init(pdev); + + return 0; ++ ++err_cleanup_scl: ++ i2c_gpio_cleanup_scl(priv->scl); ++err_cleanup_sda: ++ gpiod_put(priv->sda); ++ ++ return ret; + } + + static void i2c_gpio_remove(struct platform_device *pdev) +@@ -459,6 +652,8 @@ static void i2c_gpio_remove(struct platf + adap = &priv->adap; + + i2c_del_adapter(adap); ++ i2c_gpio_cleanup_scl(priv->scl); ++ gpiod_put(priv->sda); + } + + static const struct of_device_id i2c_gpio_dt_ids[] = { diff --git a/target/linux/realtek/patches-6.18/805-add-i2c-gpio-shared-driver.patch b/target/linux/realtek/patches-6.18/805-add-i2c-gpio-shared-driver.patch deleted file mode 100644 index d7d3ef8485..0000000000 --- a/target/linux/realtek/patches-6.18/805-add-i2c-gpio-shared-driver.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 9d2327c5f1ac63cb14af088a95eba110ab0c473e Mon Sep 17 00:00:00 2001 -From: Markus Stockhausen -Date: Wed, 7 May 2025 09:47:24 -0400 -Subject: [PATCH] realtek: add i2c-gpio-shared driver - -Adds the Kconfig and Makefile settings to make the new driver available. - -Signed-off-by: Markus Stockhausen ---- - drivers/i2c/busses/Kconfig | 9 +++++++++ - drivers/i2c/busses/Makefile | 1 + - 2 files changed, 10 insertions(+) - ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -696,6 +696,15 @@ config I2C_GPIO - This is a very simple bitbanging I2C driver utilizing the - arch-neutral GPIO API to control the SCL and SDA lines. - -+config I2C_GPIO_SHARED -+ tristate "multiple GPIO-based bitbanging I2C with shared SCL" -+ depends on GPIOLIB || COMPILE_TEST -+ select I2C_ALGOBIT -+ help -+ This is an alternative of the I2C GPIO driver for devices with only -+ few GPIO pins where multiple busses with dedicated SDA lines share -+ a single SCL line. -+ - config I2C_GPIO_FAULT_INJECTOR - bool "GPIO-based fault injector" - depends on I2C_GPIO ---- a/drivers/i2c/busses/Makefile -+++ b/drivers/i2c/busses/Makefile -@@ -66,6 +66,7 @@ obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o - obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o - obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o - obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o -+obj-$(CONFIG_I2C_GPIO_SHARED) += i2c-gpio-shared.o - obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o - obj-$(CONFIG_I2C_HISI) += i2c-hisi.o - obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o diff --git a/target/linux/realtek/rtl838x/config-6.18 b/target/linux/realtek/rtl838x/config-6.18 index 5e99390574..5047e1e245 100644 --- a/target/linux/realtek/rtl838x/config-6.18 +++ b/target/linux/realtek/rtl838x/config-6.18 @@ -109,7 +109,6 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y -CONFIG_I2C_GPIO_SHARED=y CONFIG_I2C_MUX=y # CONFIG_I2C_RTL9300 is not set CONFIG_INITRAMFS_SOURCE="" diff --git a/target/linux/realtek/rtl839x/config-6.18 b/target/linux/realtek/rtl839x/config-6.18 index 35b407d133..074ec7aa08 100644 --- a/target/linux/realtek/rtl839x/config-6.18 +++ b/target/linux/realtek/rtl839x/config-6.18 @@ -112,7 +112,6 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y -CONFIG_I2C_GPIO_SHARED=y # CONFIG_I2C_RTL9300 is not set CONFIG_INITRAMFS_SOURCE="" CONFIG_IRQCHIP=y diff --git a/target/linux/realtek/rtl930x/config-6.18 b/target/linux/realtek/rtl930x/config-6.18 index 2cf7bd4e8c..c35921089d 100644 --- a/target/linux/realtek/rtl930x/config-6.18 +++ b/target/linux/realtek/rtl930x/config-6.18 @@ -99,7 +99,6 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y -CONFIG_I2C_GPIO_SHARED=y CONFIG_I2C_MUX=y CONFIG_I2C_RTL9300=y CONFIG_I2C_SMBUS=y diff --git a/target/linux/realtek/rtl930x_nand/config-6.18 b/target/linux/realtek/rtl930x_nand/config-6.18 index 64010a6bd2..8dcbd5f2d9 100644 --- a/target/linux/realtek/rtl930x_nand/config-6.18 +++ b/target/linux/realtek/rtl930x_nand/config-6.18 @@ -101,7 +101,6 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y -# CONFIG_I2C_GPIO_SHARED is not set CONFIG_I2C_MUX=y CONFIG_I2C_RTL9300=y CONFIG_I2C_SMBUS=y diff --git a/target/linux/realtek/rtl931x/config-6.18 b/target/linux/realtek/rtl931x/config-6.18 index b34404d42f..835bce9392 100644 --- a/target/linux/realtek/rtl931x/config-6.18 +++ b/target/linux/realtek/rtl931x/config-6.18 @@ -101,7 +101,6 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y -# CONFIG_I2C_GPIO_SHARED is not set CONFIG_I2C_MUX=y CONFIG_I2C_RTL9300=y CONFIG_I2C_SMBUS=y diff --git a/target/linux/realtek/rtl931x_nand/config-6.18 b/target/linux/realtek/rtl931x_nand/config-6.18 index 01a40a04f5..600d2daa1e 100644 --- a/target/linux/realtek/rtl931x_nand/config-6.18 +++ b/target/linux/realtek/rtl931x_nand/config-6.18 @@ -103,7 +103,6 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_GPIO=y -# CONFIG_I2C_GPIO_SHARED is not set CONFIG_I2C_MUX=y CONFIG_I2C_RTL9300=y CONFIG_I2C_SMBUS=y From 276214fc060984a2edf62176f2985ddda7a5fe3f Mon Sep 17 00:00:00 2001 From: John Audia Date: Sat, 18 Jul 2026 14:06:48 -0400 Subject: [PATCH 118/152] kernel: bump 6.18 to 6.18.39 Changelog: https://cdn.kernel.org/pub/linux/kernel/v6.x/ChangeLog-6.18.39 Removed upstreamed: backport-6.18/704-v7.2-netfilter-flowtable-fix-offloaded-ct-timeout-never-b.patch[1] All other patches automatically rebased via update_kernel.sh 1. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=v6.18.39&id=81a5971cbe18b474b49c651e32f2194b2fb171fe Build system: x86/64 Build-tested: x86/64-glibc Run-tested: x86/64-glibc Signed-off-by: John Audia Link: https://github.com/openwrt/openwrt/pull/24282 Signed-off-by: Jonas Jelonek --- ...hci_fixup_endpoint-for-interval-adju.patch | 2 +- ...7-Support-for-the-Sony-IMX477-sensor.patch | 2 +- ...9-Support-for-the-Sony-IMX519-sensor.patch | 2 +- ...vicetree-Add-documentation-for-imx37.patch | 2 +- ...a-i2c-Add-IMX708-CMOS-sensor-binding.patch | 2 +- ...a-i2c-Replace-IMX708-sensor-binding-.patch | 2 +- ...ndings-i2c-Add-Rohm-BU64754-bindings.patch | 2 +- ...-bindings-Add-RPI-RP2040-GPIO-Bridge.patch | 2 +- ...river-for-the-RPI-RP2040-GPIO-bridge.patch | 2 +- ...edia-dt-bindings-i2c-Add-Sony-IMX500.patch | 2 +- ...2c-Add-driver-for-Sony-IMX500-sensor.patch | 2 +- ...Add-Raspberry-Pi-HEVC-decoder-driver.patch | 2 +- ...-around-VL805-firmware-ASPM-meddling.patch | 2 +- ...-to-Intel-scheme-for-calculating-U1-.patch | 4 +- ...tform-Add-Raspberry-Pi-HEVC-decoder-.patch | 2 +- ...Add-Raspberry-Pi-HEVC-decoder-driver.patch | 2 +- ...pcie-add-en7528-pcie-and-phy-support.patch | 6 +- ...-sched-Export-mq-functions-for-reuse.patch | 2 +- ...ble-fix-offloaded-ct-timeout-never-b.patch | 76 ------------------- ...onvert-to-common-field_-get-prep-hel.patch | 2 +- .../generic/hack-6.18/253-ksmbd-config.patch | 2 +- target/linux/generic/kernel-6.18 | 4 +- ...add-uImage.FIT-subimage-block-driver.patch | 2 +- ...-add-driver-for-MediaTek-USXGMII-PCS.patch | 2 +- .../810-pci_disable_common_quirks.patch | 8 +- ...introduce-the-Qualcomm-IPQESS-driver.patch | 2 +- ...dsa-add-out-of-band-tagging-protocol.patch | 2 +- ...ck-compatible-for-ipq4019-Lantiq-DSL.patch | 2 +- ...diatek-fix-clearing-interrupt-status.patch | 2 +- ...ediatek-add-support-for-coherent-DMA.patch | 2 +- ...30-01-i2c-add-init_recovery-callback.patch | 2 +- ...d-maintainer-for-Qualcomm-PPE-driver.patch | 2 +- ...pd-add-Realtek-Broadcom-PSE-MCU-core.patch | 2 +- ...arfive-Add-JH7100-audio-reset-driver.patch | 2 +- 34 files changed, 40 insertions(+), 116 deletions(-) delete mode 100644 target/linux/generic/backport-6.18/704-v7.2-netfilter-flowtable-fix-offloaded-ct-timeout-never-b.patch diff --git a/target/linux/bcm27xx/patches-6.18/0113-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch b/target/linux/bcm27xx/patches-6.18/0113-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch index f37fe39f69..f6da7f157b 100644 --- a/target/linux/bcm27xx/patches-6.18/0113-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch +++ b/target/linux/bcm27xx/patches-6.18/0113-xhci-implement-xhci_fixup_endpoint-for-interval-adju.patch @@ -124,7 +124,7 @@ Signed-off-by: Jonathan Bell * non-error returns are a promise to giveback() the urb later * we drop ownership so next owner (or urb unlink) can get it */ -@@ -5591,6 +5694,7 @@ static const struct hc_driver xhci_hc_dr +@@ -5600,6 +5703,7 @@ static const struct hc_driver xhci_hc_dr .endpoint_reset = xhci_endpoint_reset, .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, diff --git a/target/linux/bcm27xx/patches-6.18/0124-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch b/target/linux/bcm27xx/patches-6.18/0124-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch index 5a6b227a44..c08a081af8 100644 --- a/target/linux/bcm27xx/patches-6.18/0124-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch +++ b/target/linux/bcm27xx/patches-6.18/0124-media-i2c-imx477-Support-for-the-Sony-IMX477-sensor.patch @@ -612,7 +612,7 @@ Signed-off-by: Naushir Patuck +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24024,6 +24024,14 @@ T: git git://linuxtv.org/media.git +@@ -24023,6 +24023,14 @@ T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml F: drivers/media/i2c/imx415.c diff --git a/target/linux/bcm27xx/patches-6.18/0135-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch b/target/linux/bcm27xx/patches-6.18/0135-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch index fb51e1d4a4..9b1be1edea 100644 --- a/target/linux/bcm27xx/patches-6.18/0135-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch +++ b/target/linux/bcm27xx/patches-6.18/0135-media-i2c-imx519-Support-for-the-Sony-IMX519-sensor.patch @@ -193,7 +193,7 @@ media: i2c: imx519: Squash fixes +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24032,6 +24032,14 @@ T: git git://linuxtv.org/media_tree.git +@@ -24031,6 +24031,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/imx477.yaml F: drivers/media/i2c/imx477.c diff --git a/target/linux/bcm27xx/patches-6.18/0136-Documentation-devicetree-Add-documentation-for-imx37.patch b/target/linux/bcm27xx/patches-6.18/0136-Documentation-devicetree-Add-documentation-for-imx37.patch index f1a46f40bd..27e6e11891 100644 --- a/target/linux/bcm27xx/patches-6.18/0136-Documentation-devicetree-Add-documentation-for-imx37.patch +++ b/target/linux/bcm27xx/patches-6.18/0136-Documentation-devicetree-Add-documentation-for-imx37.patch @@ -132,7 +132,7 @@ Signed-off-by: David Plowman +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24029,6 +24029,7 @@ M: Raspberry Pi Kernel Maintenance +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24073,6 +24073,14 @@ T: git git://linuxtv.org/media_tree.git +@@ -24072,6 +24072,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/imx519.yaml F: drivers/media/i2c/imx519.c diff --git a/target/linux/bcm27xx/patches-6.18/0263-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch b/target/linux/bcm27xx/patches-6.18/0263-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch index 3f18e38086..9ae7554d91 100644 --- a/target/linux/bcm27xx/patches-6.18/0263-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch +++ b/target/linux/bcm27xx/patches-6.18/0263-dt-bindings-media-i2c-Replace-IMX708-sensor-binding-.patch @@ -271,7 +271,7 @@ Signed-off-by: Naushir Patuck +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24078,7 +24078,7 @@ M: Raspberry Pi Kernel Maintenance +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -22407,6 +22407,13 @@ L: linux-iio@vger.kernel.org +@@ -22406,6 +22406,13 @@ L: linux-iio@vger.kernel.org S: Supported F: drivers/iio/light/rohm-bu27034.c diff --git a/target/linux/bcm27xx/patches-6.18/0384-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch b/target/linux/bcm27xx/patches-6.18/0384-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch index 9a6728859c..fd6c246c9e 100644 --- a/target/linux/bcm27xx/patches-6.18/0384-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch +++ b/target/linux/bcm27xx/patches-6.18/0384-spi-dt-bindings-Add-RPI-RP2040-GPIO-Bridge.patch @@ -94,7 +94,7 @@ Signed-off-by: Richard Oliver + --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21522,6 +21522,11 @@ L: linux-edac@vger.kernel.org +@@ -21521,6 +21521,11 @@ L: linux-edac@vger.kernel.org S: Maintained F: drivers/ras/amd/fmpm.c diff --git a/target/linux/bcm27xx/patches-6.18/0385-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch b/target/linux/bcm27xx/patches-6.18/0385-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch index 0718a00a30..8f15e3f929 100644 --- a/target/linux/bcm27xx/patches-6.18/0385-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch +++ b/target/linux/bcm27xx/patches-6.18/0385-spi-Add-a-driver-for-the-RPI-RP2040-GPIO-bridge.patch @@ -58,7 +58,7 @@ Signed-off-by: Richard Oliver --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21526,6 +21526,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER +@@ -21525,6 +21525,7 @@ RASPBERRY PI RP2040 GPIO BRIDGE DRIVER M: Raspberry Pi Kernel Maintenance S: Maintained F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml diff --git a/target/linux/bcm27xx/patches-6.18/0401-media-dt-bindings-i2c-Add-Sony-IMX500.patch b/target/linux/bcm27xx/patches-6.18/0401-media-dt-bindings-i2c-Add-Sony-IMX500.patch index 0bdd6ba222..5c46b32de5 100644 --- a/target/linux/bcm27xx/patches-6.18/0401-media-dt-bindings-i2c-Add-Sony-IMX500.patch +++ b/target/linux/bcm27xx/patches-6.18/0401-media-dt-bindings-i2c-Add-Sony-IMX500.patch @@ -150,7 +150,7 @@ Signed-off-by: Richard Oliver + --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24078,6 +24078,13 @@ F: Documentation/devicetree/bindings/med +@@ -24077,6 +24077,13 @@ F: Documentation/devicetree/bindings/med F: Documentation/devicetree/bindings/media/i2c/imx477.yaml F: drivers/media/i2c/imx477.c diff --git a/target/linux/bcm27xx/patches-6.18/0402-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch b/target/linux/bcm27xx/patches-6.18/0402-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch index 8633567854..427fad0add 100644 --- a/target/linux/bcm27xx/patches-6.18/0402-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch +++ b/target/linux/bcm27xx/patches-6.18/0402-media-i2c-Add-driver-for-Sony-IMX500-sensor.patch @@ -120,7 +120,7 @@ Signed-off-by: Richard Oliver --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24084,6 +24084,7 @@ L: linux-media@vger.kernel.org +@@ -24083,6 +24083,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx500.yaml diff --git a/target/linux/bcm27xx/patches-6.18/0524-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch b/target/linux/bcm27xx/patches-6.18/0524-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch index 97cfd96e3f..662d27d3f9 100644 --- a/target/linux/bcm27xx/patches-6.18/0524-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch +++ b/target/linux/bcm27xx/patches-6.18/0524-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch @@ -51,7 +51,7 @@ Signed-off-by: Dave Stevenson --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21528,6 +21528,16 @@ S: Maintained +@@ -21527,6 +21527,16 @@ S: Maintained F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml F: drivers/spi/spi-rp2040-gpio-bridge.c diff --git a/target/linux/bcm27xx/patches-6.18/0541-PCI-quirks-work-around-VL805-firmware-ASPM-meddling.patch b/target/linux/bcm27xx/patches-6.18/0541-PCI-quirks-work-around-VL805-firmware-ASPM-meddling.patch index e3d996dd6c..2a4afc627a 100644 --- a/target/linux/bcm27xx/patches-6.18/0541-PCI-quirks-work-around-VL805-firmware-ASPM-meddling.patch +++ b/target/linux/bcm27xx/patches-6.18/0541-PCI-quirks-work-around-VL805-firmware-ASPM-meddling.patch @@ -18,7 +18,7 @@ Signed-off-by: Jonathan Bell --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c -@@ -6293,6 +6293,22 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I +@@ -6278,6 +6278,22 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56b1, aspm_l1_acceptable_latency); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56c0, aspm_l1_acceptable_latency); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56c1, aspm_l1_acceptable_latency); diff --git a/target/linux/bcm27xx/patches-6.18/0542-usb-xhci-default-to-Intel-scheme-for-calculating-U1-.patch b/target/linux/bcm27xx/patches-6.18/0542-usb-xhci-default-to-Intel-scheme-for-calculating-U1-.patch index 5579e2cb30..04be9f94c7 100644 --- a/target/linux/bcm27xx/patches-6.18/0542-usb-xhci-default-to-Intel-scheme-for-calculating-U1-.patch +++ b/target/linux/bcm27xx/patches-6.18/0542-usb-xhci-default-to-Intel-scheme-for-calculating-U1-.patch @@ -40,7 +40,7 @@ Signed-off-by: Jonathan Bell static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { struct xhci_segment *seg; -@@ -4991,7 +4995,7 @@ static u16 xhci_calculate_u1_timeout(str +@@ -5000,7 +5004,7 @@ static u16 xhci_calculate_u1_timeout(str } } @@ -49,7 +49,7 @@ Signed-off-by: Jonathan Bell timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc); else timeout_ns = udev->u1_params.sel; -@@ -5055,7 +5059,7 @@ static u16 xhci_calculate_u2_timeout(str +@@ -5064,7 +5068,7 @@ static u16 xhci_calculate_u2_timeout(str } } diff --git a/target/linux/bcm27xx/patches-6.18/0625-Revert-media-platform-Add-Raspberry-Pi-HEVC-decoder-.patch b/target/linux/bcm27xx/patches-6.18/0625-Revert-media-platform-Add-Raspberry-Pi-HEVC-decoder-.patch index fb6efe12b8..50ca560625 100644 --- a/target/linux/bcm27xx/patches-6.18/0625-Revert-media-platform-Add-Raspberry-Pi-HEVC-decoder-.patch +++ b/target/linux/bcm27xx/patches-6.18/0625-Revert-media-platform-Add-Raspberry-Pi-HEVC-decoder-.patch @@ -33,7 +33,7 @@ This reverts commit 6bd54195d0b29330576542c8128e7a5c58d6bcdd. --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21528,16 +21528,6 @@ S: Maintained +@@ -21527,16 +21527,6 @@ S: Maintained F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml F: drivers/spi/spi-rp2040-gpio-bridge.c diff --git a/target/linux/bcm27xx/patches-6.18/0626-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch b/target/linux/bcm27xx/patches-6.18/0626-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch index be37fdb256..890d06c0bc 100644 --- a/target/linux/bcm27xx/patches-6.18/0626-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch +++ b/target/linux/bcm27xx/patches-6.18/0626-media-platform-Add-Raspberry-Pi-HEVC-decoder-driver.patch @@ -36,7 +36,7 @@ Signed-off-by: Dave Stevenson --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21528,6 +21528,16 @@ S: Maintained +@@ -21527,6 +21527,16 @@ S: Maintained F: Documentation/devicetree/bindings/spi/raspberrypi,rp2040-gpio-bridge.yaml F: drivers/spi/spi-rp2040-gpio-bridge.c diff --git a/target/linux/econet/patches-6.18/912-pcie-add-en7528-pcie-and-phy-support.patch b/target/linux/econet/patches-6.18/912-pcie-add-en7528-pcie-and-phy-support.patch index 0a320436cf..63e12f3482 100644 --- a/target/linux/econet/patches-6.18/912-pcie-add-en7528-pcie-and-phy-support.patch +++ b/target/linux/econet/patches-6.18/912-pcie-add-en7528-pcie-and-phy-support.patch @@ -55,7 +55,7 @@ #define PCIE_AHB_TRANS_BASE0_L 0x438 #define PCIE_AHB_TRANS_BASE0_H 0x43c #define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0)) -@@ -746,6 +752,86 @@ static int mtk_pcie_startup_port_v2(stru +@@ -750,6 +756,86 @@ static int mtk_pcie_startup_port_v2(stru return 0; } @@ -142,7 +142,7 @@ static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { -@@ -1107,6 +1193,20 @@ static int mtk_pcie_probe(struct platfor +@@ -1120,6 +1206,20 @@ static int mtk_pcie_probe(struct platfor if (err) goto put_resources; @@ -163,7 +163,7 @@ return 0; put_resources: -@@ -1216,12 +1316,19 @@ static const struct mtk_pcie_soc mtk_pci +@@ -1233,12 +1333,19 @@ static const struct mtk_pcie_soc mtk_pci .setup_irq = mtk_pcie_setup_irq, }; diff --git a/target/linux/generic/backport-6.18/700-01-v7.0-net-sched-Export-mq-functions-for-reuse.patch b/target/linux/generic/backport-6.18/700-01-v7.0-net-sched-Export-mq-functions-for-reuse.patch index 4ea95e19b7..1fa1b97d34 100644 --- a/target/linux/generic/backport-6.18/700-01-v7.0-net-sched-Export-mq-functions-for-reuse.patch +++ b/target/linux/generic/backport-6.18/700-01-v7.0-net-sched-Export-mq-functions-for-reuse.patch @@ -27,7 +27,7 @@ Signed-off-by: Paolo Abeni --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -25056,6 +25056,7 @@ L: netdev@vger.kernel.org +@@ -25055,6 +25055,7 @@ L: netdev@vger.kernel.org S: Maintained F: include/net/pkt_cls.h F: include/net/pkt_sched.h diff --git a/target/linux/generic/backport-6.18/704-v7.2-netfilter-flowtable-fix-offloaded-ct-timeout-never-b.patch b/target/linux/generic/backport-6.18/704-v7.2-netfilter-flowtable-fix-offloaded-ct-timeout-never-b.patch deleted file mode 100644 index 22cd634071..0000000000 --- a/target/linux/generic/backport-6.18/704-v7.2-netfilter-flowtable-fix-offloaded-ct-timeout-never-b.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 53b3e60edb674b442b2b3bbdba484667b0f47a5d Mon Sep 17 00:00:00 2001 -From: Adrian Bente -Date: Thu, 28 May 2026 10:08:51 +0300 -Subject: [PATCH] netfilter: flowtable: fix offloaded ct timeout never being extended - -OpenWrt has recently migrated many platforms to kernel 6.18. On the -MediaTek platform, which supports hardware network offloading, WiFi -connections accelerated via the WED path were observed to drop after -roughly 300 seconds. - -After several debugging sessions, assisted by the Claude LLM, the -problem was narrowed down as follows: - -nf_flow_table_extend_ct_timeout() extends ct->timeout for offloaded -flows using: - - cmpxchg(&ct->timeout, expires, new_timeout); - -'expires' comes from nf_ct_expires(ct) and is a relative value, while -ct->timeout holds an absolute timestamp. The two are never equal, so -the cmpxchg always fails and the timeout is never extended. - -This goes unnoticed for most flows, but a long-lived hardware (WED) -offloaded flow on MediaTek MT7986 eventually has ct->timeout decay to -zero, the conntrack entry is reaped and the connection breaks. - -Open-code the relative value from a single READ_ONCE(ct->timeout) -snapshot and compare against that same absolute snapshot in the -cmpxchg, so the timeout extension actually takes effect while the -datapath remains authoritative if it updates ct->timeout concurrently. - -Fixes: 03428ca5cee9 ("netfilter: conntrack: rework offload nf_conn timeout extension logic") -Cc: stable@vger.kernel.org -Suggested-by: Florian Westphal -Signed-off-by: Adrian Bente -Signed-off-by: Pablo Neira Ayuso ---- - net/netfilter/nf_flow_table_core.c | 13 +++++++++---- - 1 file changed, 9 insertions(+), 4 deletions(-) - ---- a/net/netfilter/nf_flow_table_core.c -+++ b/net/netfilter/nf_flow_table_core.c -@@ -500,8 +500,13 @@ static u32 nf_flow_table_tcp_timeout(con - */ - static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct) - { -- static const u32 min_timeout = 5 * 60 * HZ; -- u32 expires = nf_ct_expires(ct); -+ static const s32 min_timeout = 5 * 60 * HZ; -+ u32 ct_timeout = READ_ONCE(ct->timeout); -+ s32 expires; -+ -+ expires = ct_timeout - nfct_time_stamp; -+ if (expires <= 0) /* already expired */ -+ return; - - /* normal case: large enough timeout, nothing to do. */ - if (likely(expires >= min_timeout)) -@@ -519,7 +524,7 @@ static void nf_flow_table_extend_ct_time - if (nf_ct_is_confirmed(ct) && - test_bit(IPS_OFFLOAD_BIT, &ct->status)) { - u8 l4proto = nf_ct_protonum(ct); -- u32 new_timeout = true; -+ u32 new_timeout = 1; - - switch (l4proto) { - case IPPROTO_UDP: -@@ -544,7 +549,7 @@ static void nf_flow_table_extend_ct_time - */ - if (new_timeout) { - new_timeout += nfct_time_stamp; -- cmpxchg(&ct->timeout, expires, new_timeout); -+ cmpxchg(&ct->timeout, ct_timeout, new_timeout); - } - } - diff --git a/target/linux/generic/backport-6.18/822-v6.19-ALSA-usb-audio-Convert-to-common-field_-get-prep-hel.patch b/target/linux/generic/backport-6.18/822-v6.19-ALSA-usb-audio-Convert-to-common-field_-get-prep-hel.patch index 72aa514aaf..e32ab18e01 100644 --- a/target/linux/generic/backport-6.18/822-v6.19-ALSA-usb-audio-Convert-to-common-field_-get-prep-hel.patch +++ b/target/linux/generic/backport-6.18/822-v6.19-ALSA-usb-audio-Convert-to-common-field_-get-prep-hel.patch @@ -15,7 +15,7 @@ Signed-off-by: Yury Norov (NVIDIA) --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c -@@ -3308,10 +3308,6 @@ static int snd_bbfpro_controls_create(st +@@ -3325,10 +3325,6 @@ static int snd_bbfpro_controls_create(st #define RME_DIGIFACE_REGISTER(reg, mask) (((reg) << 16) | (mask)) #define RME_DIGIFACE_INVERT BIT(31) diff --git a/target/linux/generic/hack-6.18/253-ksmbd-config.patch b/target/linux/generic/hack-6.18/253-ksmbd-config.patch index 724a461e20..29fc995292 100644 --- a/target/linux/generic/hack-6.18/253-ksmbd-config.patch +++ b/target/linux/generic/hack-6.18/253-ksmbd-config.patch @@ -10,7 +10,7 @@ Subject: [PATCH] Kconfig: add tristate for OID and ASNI string --- a/init/Kconfig +++ b/init/Kconfig -@@ -2191,7 +2191,7 @@ config PADATA +@@ -2192,7 +2192,7 @@ config PADATA bool config ASN1 diff --git a/target/linux/generic/kernel-6.18 b/target/linux/generic/kernel-6.18 index 4c51e5d02c..eba60731b2 100644 --- a/target/linux/generic/kernel-6.18 +++ b/target/linux/generic/kernel-6.18 @@ -1,2 +1,2 @@ -LINUX_VERSION-6.18 = .38 -LINUX_KERNEL_HASH-6.18.38 = ac26e508abd56e9f8b89872b6e10c49fc823bcc70d8068a5d8504c1a7c4ff045 +LINUX_VERSION-6.18 = .39 +LINUX_KERNEL_HASH-6.18.39 = a7a7e3d2ae9d95e74197223a8d4eb5f6be7aac21b6e6de27e9685d001c1f8cb0 diff --git a/target/linux/generic/pending-6.18/510-block-add-uImage.FIT-subimage-block-driver.patch b/target/linux/generic/pending-6.18/510-block-add-uImage.FIT-subimage-block-driver.patch index 9d7d2cd7db..bafec18a6f 100644 --- a/target/linux/generic/pending-6.18/510-block-add-uImage.FIT-subimage-block-driver.patch +++ b/target/linux/generic/pending-6.18/510-block-add-uImage.FIT-subimage-block-driver.patch @@ -36,7 +36,7 @@ Signed-off-by: Daniel Golle --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -26274,6 +26274,12 @@ F: Documentation/filesystems/ubifs-authe +@@ -26273,6 +26273,12 @@ F: Documentation/filesystems/ubifs-authe F: Documentation/filesystems/ubifs.rst F: fs/ubifs/ diff --git a/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch b/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch index 385c5c924e..028ece1c43 100644 --- a/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch +++ b/target/linux/generic/pending-6.18/739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch @@ -19,7 +19,7 @@ Signed-off-by: Daniel Golle --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -15790,7 +15790,9 @@ M: Daniel Golle +@@ -15789,7 +15789,9 @@ M: Daniel Golle L: netdev@vger.kernel.org S: Maintained F: drivers/net/pcs/pcs-mtk-lynxi.c diff --git a/target/linux/generic/pending-6.18/810-pci_disable_common_quirks.patch b/target/linux/generic/pending-6.18/810-pci_disable_common_quirks.patch index c95d0df015..0427701cad 100644 --- a/target/linux/generic/pending-6.18/810-pci_disable_common_quirks.patch +++ b/target/linux/generic/pending-6.18/810-pci_disable_common_quirks.patch @@ -25,7 +25,7 @@ Signed-off-by: Gabor Juhos --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c -@@ -316,6 +316,7 @@ static void quirk_mmio_always_on(struct +@@ -301,6 +301,7 @@ static void quirk_mmio_always_on(struct DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); @@ -33,7 +33,7 @@ Signed-off-by: Gabor Juhos /* * The Mellanox Tavor device gives false positive parity errors. Disable * parity error reporting. -@@ -3519,6 +3520,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I +@@ -3504,6 +3505,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_I DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); @@ -42,7 +42,7 @@ Signed-off-by: Gabor Juhos /* * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. * To work around this, query the size it should be configured to by the -@@ -3544,6 +3547,8 @@ static void quirk_intel_ntb(struct pci_d +@@ -3529,6 +3532,8 @@ static void quirk_intel_ntb(struct pci_d DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); @@ -51,7 +51,7 @@ Signed-off-by: Gabor Juhos /* * Some BIOS implementations leave the Intel GPU interrupts enabled, even * though no one is handling them (e.g., if the i915 driver is never -@@ -3582,6 +3587,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN +@@ -3567,6 +3572,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq); diff --git a/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch b/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch index caeff6c646..46fa542004 100644 --- a/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch +++ b/target/linux/ipq40xx/patches-6.18/700-net-ipqess-introduce-the-Qualcomm-IPQESS-driver.patch @@ -24,7 +24,7 @@ Signed-off-by: Maxime Chevallier --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21140,6 +21140,13 @@ L: netdev@vger.kernel.org +@@ -21139,6 +21139,13 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/qualcomm/emac/ diff --git a/target/linux/ipq40xx/patches-6.18/701-net-dsa-add-out-of-band-tagging-protocol.patch b/target/linux/ipq40xx/patches-6.18/701-net-dsa-add-out-of-band-tagging-protocol.patch index de5435c590..8e59a3885e 100644 --- a/target/linux/ipq40xx/patches-6.18/701-net-dsa-add-out-of-band-tagging-protocol.patch +++ b/target/linux/ipq40xx/patches-6.18/701-net-dsa-add-out-of-band-tagging-protocol.patch @@ -64,7 +64,7 @@ Signed-off-by: Maxime Chevallier --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21146,6 +21146,7 @@ L: netdev@vger.kernel.org +@@ -21145,6 +21145,7 @@ L: netdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/qcom,ipq4019-ess-edma.yaml F: drivers/net/ethernet/qualcomm/ipqess/ diff --git a/target/linux/ipq40xx/patches-6.18/900-PCI-qcom-add-hack-compatible-for-ipq4019-Lantiq-DSL.patch b/target/linux/ipq40xx/patches-6.18/900-PCI-qcom-add-hack-compatible-for-ipq4019-Lantiq-DSL.patch index 42a010b883..6a8fb997d5 100644 --- a/target/linux/ipq40xx/patches-6.18/900-PCI-qcom-add-hack-compatible-for-ipq4019-Lantiq-DSL.patch +++ b/target/linux/ipq40xx/patches-6.18/900-PCI-qcom-add-hack-compatible-for-ipq4019-Lantiq-DSL.patch @@ -156,7 +156,7 @@ Signed-off-by: Florian Maurer static const struct qcom_pcie_cfg cfg_2_7_0 = { .ops = &ops_2_7_0, }; -@@ -2016,6 +2107,7 @@ static const struct of_device_id qcom_pc +@@ -2022,6 +2113,7 @@ static const struct of_device_id qcom_pc { .compatible = "qcom,pcie-apq8064", .data = &cfg_2_1_0 }, { .compatible = "qcom,pcie-apq8084", .data = &cfg_1_0_0 }, { .compatible = "qcom,pcie-ipq4019", .data = &cfg_2_4_0 }, diff --git a/target/linux/mediatek/patches-6.18/610-pcie-mediatek-fix-clearing-interrupt-status.patch b/target/linux/mediatek/patches-6.18/610-pcie-mediatek-fix-clearing-interrupt-status.patch index ab4ce2dc92..e31e02d2c2 100644 --- a/target/linux/mediatek/patches-6.18/610-pcie-mediatek-fix-clearing-interrupt-status.patch +++ b/target/linux/mediatek/patches-6.18/610-pcie-mediatek-fix-clearing-interrupt-status.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c -@@ -597,9 +597,9 @@ static void mtk_pcie_intr_handler(struct +@@ -601,9 +601,9 @@ static void mtk_pcie_intr_handler(struct if (status & INTX_MASK) { for_each_set_bit_from(bit, &status, PCI_NUM_INTX + INTX_SHIFT) { /* Clear the INTx */ diff --git a/target/linux/mediatek/patches-6.18/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch b/target/linux/mediatek/patches-6.18/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch index fa3d2ffe2a..1272a9ffbd 100644 --- a/target/linux/mediatek/patches-6.18/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch +++ b/target/linux/mediatek/patches-6.18/710-pci-pcie-mediatek-add-support-for-coherent-DMA.patch @@ -61,7 +61,7 @@ Signed-off-by: Felix Fietkau struct mtk_pcie_port; /** -@@ -1043,6 +1049,27 @@ static int mtk_pcie_setup(struct mtk_pci +@@ -1051,6 +1057,27 @@ static int mtk_pcie_setup(struct mtk_pci struct mtk_pcie_port *port, *tmp; int err, slot; diff --git a/target/linux/mvebu/patches-6.18/830-01-i2c-add-init_recovery-callback.patch b/target/linux/mvebu/patches-6.18/830-01-i2c-add-init_recovery-callback.patch index dc25a91bf6..cbf22f3b3e 100644 --- a/target/linux/mvebu/patches-6.18/830-01-i2c-add-init_recovery-callback.patch +++ b/target/linux/mvebu/patches-6.18/830-01-i2c-add-init_recovery-callback.patch @@ -147,7 +147,7 @@ Reviewed-by: Linus Walleij --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c -@@ -427,12 +427,18 @@ static int i2c_init_recovery(struct i2c_ +@@ -428,12 +428,18 @@ static int i2c_init_recovery(struct i2c_ struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; bool is_error_level = true; char *err_str; diff --git a/target/linux/qualcommbe/patches-6.18/0325-MAINTAINERS-Add-maintainer-for-Qualcomm-PPE-driver.patch b/target/linux/qualcommbe/patches-6.18/0325-MAINTAINERS-Add-maintainer-for-Qualcomm-PPE-driver.patch index edda782427..a3c02429b6 100644 --- a/target/linux/qualcommbe/patches-6.18/0325-MAINTAINERS-Add-maintainer-for-Qualcomm-PPE-driver.patch +++ b/target/linux/qualcommbe/patches-6.18/0325-MAINTAINERS-Add-maintainer-for-Qualcomm-PPE-driver.patch @@ -13,7 +13,7 @@ Signed-off-by: Luo Jie --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21240,6 +21240,15 @@ S: Maintained +@@ -21239,6 +21239,15 @@ S: Maintained F: Documentation/devicetree/bindings/regulator/vqmmc-ipq4019-regulator.yaml F: drivers/regulator/vqmmc-ipq4019-regulator.c diff --git a/target/linux/realtek/patches-6.18/816-01-net-pse-pd-add-Realtek-Broadcom-PSE-MCU-core.patch b/target/linux/realtek/patches-6.18/816-01-net-pse-pd-add-Realtek-Broadcom-PSE-MCU-core.patch index 077fa59399..56742693e9 100644 --- a/target/linux/realtek/patches-6.18/816-01-net-pse-pd-add-Realtek-Broadcom-PSE-MCU-core.patch +++ b/target/linux/realtek/patches-6.18/816-01-net-pse-pd-add-Realtek-Broadcom-PSE-MCU-core.patch @@ -42,7 +42,7 @@ Signed-off-by: Jonas Jelonek --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -21668,6 +21668,13 @@ S: Maintained +@@ -21667,6 +21667,13 @@ S: Maintained F: include/sound/rt*.h F: sound/soc/codecs/rt* diff --git a/target/linux/starfive/patches-6.18/1007-reset-starfive-Add-JH7100-audio-reset-driver.patch b/target/linux/starfive/patches-6.18/1007-reset-starfive-Add-JH7100-audio-reset-driver.patch index 52a169a261..8eb1472c56 100644 --- a/target/linux/starfive/patches-6.18/1007-reset-starfive-Add-JH7100-audio-reset-driver.patch +++ b/target/linux/starfive/patches-6.18/1007-reset-starfive-Add-JH7100-audio-reset-driver.patch @@ -20,7 +20,7 @@ Signed-off-by: Emil Renner Berthing --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -24566,7 +24566,7 @@ STARFIVE JH71X0 RESET CONTROLLER DRIVERS +@@ -24565,7 +24565,7 @@ STARFIVE JH71X0 RESET CONTROLLER DRIVERS M: Emil Renner Berthing M: Hal Feng S: Maintained From e85fa66006b6d87040d75ac54214aa219dae47d6 Mon Sep 17 00:00:00 2001 From: Andris PE Date: Mon, 13 Jul 2026 10:41:50 +0000 Subject: [PATCH 119/152] kernel: load vmware net modules early Load vmware network modules early to make them available for failsafe mode and first-time scripts REF: `https://knowledge.broadcom.com/external/article/321259/choosing-a-network-adapter-for-a-virtual.html` Additionally add e1000 which is default on "Other Linux" vm profiles Signed-off-by: Andris PE Link: https://github.com/openwrt/openwrt/pull/24212 Signed-off-by: Jonas Jelonek --- package/kernel/linux/modules/netdevices.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index a4d16a2dfb..0185617458 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -1418,7 +1418,7 @@ define KernelPackage/e1000 CONFIG_E1000_DISABLE_PACKET_SPLIT=n \ CONFIG_E1000_NAPI=y FILES:=$(LINUX_DIR)/drivers/net/ethernet/intel/e1000/e1000.ko - AUTOLOAD:=$(call AutoLoad,35,e1000) + AUTOLOAD:=$(call AutoLoad,35,e1000,1) endef define KernelPackage/e1000/description @@ -1872,7 +1872,7 @@ define KernelPackage/vmxnet3 DEPENDS:=@PCI_SUPPORT KCONFIG:=CONFIG_VMXNET3 FILES:=$(LINUX_DIR)/drivers/net/vmxnet3/vmxnet3.ko - AUTOLOAD:=$(call AutoLoad,35,vmxnet3) + AUTOLOAD:=$(call AutoLoad,35,vmxnet3,1) endef define KernelPackage/vmxnet3/description From 1513da34bb1aba16fc28c0ef04304ab991abd6fb Mon Sep 17 00:00:00 2001 From: Semih Baskan Date: Tue, 7 Jul 2026 12:41:51 +0300 Subject: [PATCH 120/152] mediatek: filogic: set eth0 and LAN port MACs on TP-Link BE450 eth0 (gmac0) is declared label-mac-device, but nothing assigns its MAC, so it and the DSA lan ports come up with a random locally administered address that changes every boot. Assign the base MAC from tp_data default-mac to eth0 and the lan ports, as is already done for eth1 and eth2. Each lan port needs its own assignment: a DSA user port copies the conduit's MAC once when it is created, at driver probe before preinit runs, and ignores later changes to eth0. Run-tested on a BE450 on 25.12.5, where the case is identical. Two boots without the fix each came up with a different random address; with the fix applied, eth0 and the lan ports get the label MAC and keep it across reboots. Signed-off-by: Semih Baskan Link: https://github.com/openwrt/openwrt/pull/24180 Signed-off-by: Jonas Jelonek --- .../mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh b/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh index ead84eca26..41bbfd8d19 100644 --- a/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh +++ b/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh @@ -37,6 +37,10 @@ preinit_set_mac_address() { ;; tplink,be450) addr=$(get_mac_binary "/tmp/tp_data/default-mac" 0) + ip link set dev eth0 address "$addr" + ip link set dev lan1 address "$addr" + ip link set dev lan2 address "$addr" + ip link set dev lan3 address "$addr" ip link set dev eth1 address "$(macaddr_add $addr 1)" ip link set dev eth2 address "$(macaddr_add $addr 2)" ;; From 770a32e117b703c3e90a422a5e1f8c5f82cbd00a Mon Sep 17 00:00:00 2001 From: Semih Baskan Date: Sat, 11 Jul 2026 18:18:56 +0300 Subject: [PATCH 121/152] mediatek: filogic: set label MAC address on TP-Link BE450 The label-mac-device alias cannot work on this device: it is resolved by reading the mac-address property of the aliased DT node, but gmac0 never has one. The MAC is stored in a file inside the tp_data UBIFS volume, so there is no fixed flash offset an nvmem-layout could point at, and the vendor bootloader does not fix up the FDT either. Drop the dead alias. Record the label MAC in board.d instead and assign it to the lan interface, as is already done for the other devices reading /tmp/tp_data/default-mac (Mercusys MR90X v1, Archer AX80 v1, RE6000XD). The explicit lan MAC also makes the br-lan address independent of the order of the bridge ports. Suggested-by: Felix Baumann Signed-off-by: Semih Baskan Link: https://github.com/openwrt/openwrt/pull/24180 Signed-off-by: Jonas Jelonek --- target/linux/mediatek/dts/mt7988d-tplink-be450.dts | 1 - target/linux/mediatek/filogic/base-files/etc/board.d/02_network | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/mediatek/dts/mt7988d-tplink-be450.dts b/target/linux/mediatek/dts/mt7988d-tplink-be450.dts index 6961d34851..8ba4e8faa9 100644 --- a/target/linux/mediatek/dts/mt7988d-tplink-be450.dts +++ b/target/linux/mediatek/dts/mt7988d-tplink-be450.dts @@ -19,7 +19,6 @@ aliases { serial0 = &serial0; - label-mac-device = &gmac0; led-boot = &led_status; led-failsafe = &led_status; led-running = &led_status; diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network index ae77915ae4..d5987e8f33 100644 --- a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network @@ -299,6 +299,7 @@ mediatek_setup_macs() mercusys,mr90x-v1|\ tplink,archer-ax80-v1|\ tplink,archer-ax80-v1-eu|\ + tplink,be450|\ tplink,re6000xd) label_mac=$(get_mac_binary "/tmp/tp_data/default-mac" 0) lan_mac=$label_mac From 672400f3619e912e8d2834f225f48a025a4e4ca2 Mon Sep 17 00:00:00 2001 From: Aleksander Jan Bajkowski Date: Sat, 18 Jul 2026 13:11:54 +0200 Subject: [PATCH 122/152] ethtool: update to 7.1 Version 7.1 - Jul 7, 2026 * Feature: track TX pause storm events (-I -a) * Feature: update doc for ETHTOOL_PFC_PREVENTION_TOUT tunable * Feature: RX CQE coalescing params (-c, -C) * Feature: allow hex dump of all pages (-m) * Feature: qsfp: support newer SFF-8636 compliance codes (-m) * Feature: sfpid: support newer SFF-8636 compliance codes (-m) * Fix: document --disable-netlink in help output (-h) * Fix: add missing newlines in FEC output (--show-fec) * Fix: sfpid: fix 10G Base-ER module detection (-m) * Misc: clarify 10000baseCR link mode in man page Signed-off-by: Aleksander Jan Bajkowski Link: https://github.com/openwrt/openwrt/pull/24278 Signed-off-by: Jonas Jelonek --- package/network/utils/ethtool/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/network/utils/ethtool/Makefile b/package/network/utils/ethtool/Makefile index 5a27d0ace3..1536181028 100644 --- a/package/network/utils/ethtool/Makefile +++ b/package/network/utils/ethtool/Makefile @@ -8,13 +8,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ethtool -PKG_VERSION:=7.0 +PKG_VERSION:=7.1 PKG_RELEASE:=1 PKG_MAINTAINER:=Felix Fietkau PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=@KERNEL/software/network/ethtool -PKG_HASH:=660bf9725a7871343a0d232068a7634fbcfb69b6c2f8eff455827faefb0cd162 +PKG_HASH:=4d78c26edc0255bc92f4b995b5fd66108d75ff966ed4694f6025a6d370bc2496 PKG_LICENSE:=GPL-2.0 PKG_LICENSE_FILES:=COPYING From ab649f19ab7fee9fb9895948e7ffb67c84427fb6 Mon Sep 17 00:00:00 2001 From: Stijn Segers Date: Sat, 18 Jul 2026 13:49:23 +0200 Subject: [PATCH 123/152] realtek: use Realtek PSE MCU driver for GS1900-8HP B1 Switch the GS1900-8HP B1 (which turns out to come with a Broadcom PSE, still) to the PSU driver that is being upstreamed. Signed-off-by: Stijn Segers Link: https://github.com/openwrt/openwrt/pull/24306 Signed-off-by: Jonas Jelonek --- .../dts/rtl8380_zyxel_gs1900-8hp-b1.dts | 28 +++++++++++++++++++ target/linux/realtek/image/rtl838x.mk | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-8hp-b1.dts b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-8hp-b1.dts index 8ef021bbdf..8987fd1d73 100644 --- a/target/linux/realtek/dts/rtl8380_zyxel_gs1900-8hp-b1.dts +++ b/target/linux/realtek/dts/rtl8380_zyxel_gs1900-8hp-b1.dts @@ -10,4 +10,32 @@ &uart1 { status = "okay"; + + pse: ethernet-pse { + compatible = "zyxel,gs1900-8hp-b1-pse", "realtek,pse-mcu-gen1"; + current-speed = <19200>; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + PSE_PI(0) + PSE_PI(1) + PSE_PI(2) + PSE_PI(3) + PSE_PI(4) + PSE_PI(5) + PSE_PI(6) + PSE_PI(7) + }; + }; }; + +&phy8 { pses = <&pse_pi0>; }; +&phy9 { pses = <&pse_pi1>; }; +&phy10 { pses = <&pse_pi2>; }; +&phy11 { pses = <&pse_pi3>; }; +&phy12 { pses = <&pse_pi4>; }; +&phy13 { pses = <&pse_pi5>; }; +&phy14 { pses = <&pse_pi6>; }; +&phy15 { pses = <&pse_pi7>; }; diff --git a/target/linux/realtek/image/rtl838x.mk b/target/linux/realtek/image/rtl838x.mk index 369db851d6..1b4610c291 100644 --- a/target/linux/realtek/image/rtl838x.mk +++ b/target/linux/realtek/image/rtl838x.mk @@ -454,7 +454,7 @@ define Device/zyxel_gs1900-8hp-b1 DEVICE_VARIANT := B1 ZYXEL_VERS := AAHI SUPPORTED_DEVICES += zyxel,gs1900-8hp-v2 - DEVICE_PACKAGES += realtek-poe + DEVICE_PACKAGES += kmod-pse-realtek-mcu-uart endef TARGET_DEVICES += zyxel_gs1900-8hp-b1 From 1b2aeb4f8a2ef7f200e7ee93c5c94c195fef2393 Mon Sep 17 00:00:00 2001 From: Semih Baskan Date: Mon, 20 Jul 2026 21:31:17 +0300 Subject: [PATCH 124/152] mediatek: filogic: drop dead eth0 MAC assignment on TP-Link BE450 The eth0 assignment never takes effect. 05_set_preinit_iface brings eth0 up in its default case before 10_fix_eth_mac.sh runs, and the kernel refuses a MAC change on a running interface, so ip link fails with EBUSY. This script cannot run any earlier either, since it needs /tmp/tp_data mounted by 09_mount_cfg_part. Nothing is lost by dropping it: the DSA user ports carry their own addresses, which the lan1-3 assignments below already take care of, and br-lan takes its address from them. Verified on a BE450 running an image built from main: eth0 comes up with a different random address on every boot regardless of this line, while lan1-3 and br-lan keep the label MAC across reboots even with no macaddr in /etc/config/network. Fixes: 1513da34bb1a ("mediatek: filogic: set eth0 and LAN port MACs on TP-Link BE450") Signed-off-by: Semih Baskan Link: https://github.com/openwrt/openwrt/pull/24325 Signed-off-by: Jonas Jelonek --- .../mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh b/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh index 41bbfd8d19..c370f8c5fc 100644 --- a/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh +++ b/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh @@ -37,7 +37,6 @@ preinit_set_mac_address() { ;; tplink,be450) addr=$(get_mac_binary "/tmp/tp_data/default-mac" 0) - ip link set dev eth0 address "$addr" ip link set dev lan1 address "$addr" ip link set dev lan2 address "$addr" ip link set dev lan3 address "$addr" From 41ad8fff3842e43a4632c853f4f73e3521d517de Mon Sep 17 00:00:00 2001 From: Kyle Hendry Date: Sat, 18 Jul 2026 19:09:40 -0700 Subject: [PATCH 125/152] generic: mips: 6.18: backport fix for highmem init Fix bug in MIPS highmem init that prevents bcm47xx devices from booting Signed-off-by: Kyle Hendry Link: https://github.com/openwrt/openwrt/pull/24286 Signed-off-by: Jonas Jelonek --- .../300-v7.1-MIPS-mm-fix-highmem-init.patch | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 target/linux/generic/backport-6.18/300-v7.1-MIPS-mm-fix-highmem-init.patch diff --git a/target/linux/generic/backport-6.18/300-v7.1-MIPS-mm-fix-highmem-init.patch b/target/linux/generic/backport-6.18/300-v7.1-MIPS-mm-fix-highmem-init.patch new file mode 100644 index 0000000000..fe97f563bd --- /dev/null +++ b/target/linux/generic/backport-6.18/300-v7.1-MIPS-mm-fix-highmem-init.patch @@ -0,0 +1,34 @@ +From 6d5fbecd0213489bc4de71a0da194d18e654fd6e Mon Sep 17 00:00:00 2001 +From: Kyle Hendry +Date: Sun, 21 Jun 2026 11:47:02 -0700 +Subject: MIPS: mm: Add check for highmem before removing memory block + +If a device has less physical memory than the highmem threshold +bootmem_init() doesn't set highstart_pfn. This results in highmem_init() +wrongly disabling the entire memory range if the cpu doesn't support +highmem. Add a check that highstart_pfn is non zero before removing the +highmem block. + +Fixes: f171b55f1441 ("mips: fix HIGHMEM initialization") +Signed-off-by: Kyle Hendry +Signed-off-by: Thomas Bogendoerfer +--- + arch/mips/mm/init.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/arch/mips/mm/init.c ++++ b/arch/mips/mm/init.c +@@ -431,10 +431,11 @@ static inline void __init highmem_init(v + unsigned long tmp; + + /* +- * If CPU cannot support HIGHMEM discard the memory above highstart_pfn ++ * If CPU cannot support HIGHMEM discard any memory above highstart_pfn + */ + if (cpu_has_dc_aliases) { +- memblock_remove(PFN_PHYS(highstart_pfn), -1); ++ if (highstart_pfn) ++ memblock_remove(PFN_PHYS(highstart_pfn), -1); + return; + } + From cc80fc4cff784cc166a004d97103c777c03f2364 Mon Sep 17 00:00:00 2001 From: Frank Lang Date: Sat, 18 Jul 2026 21:58:04 +0200 Subject: [PATCH 126/152] ramips: mt76x8: fix MT7663 EEPROM cell for MERCUSYS MB130-4G v1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nvmem cell for the MT7663 (5 GHz) radio was too small. The MT7663 EEPROM at offset 0x8000 requires the full MT7615_EEPROM_FULL_SIZE (0x4da8), but the cell only declared 0x200 bytes. The driver always requests the full buffer, also for the MT7663, so the undersized cell makes the nvmem read fail with -EINVAL. The driver then falls back to default values, which limits TX power to 3 dBm. Fix this by enlarging the cell to 0x4da8. The offset 0x8000 was already correct — offset 0x0 holds the MT7628 (2.4 GHz) calibration data. Fixes: #23632 Signed-off-by: Frank Lang Link: https://github.com/openwrt/openwrt/pull/24284 Signed-off-by: Jonas Jelonek --- target/linux/ramips/dts/mt7628an_mercusys_mb130-4g-v1.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/linux/ramips/dts/mt7628an_mercusys_mb130-4g-v1.dts b/target/linux/ramips/dts/mt7628an_mercusys_mb130-4g-v1.dts index 053de48d79..ed1df16939 100644 --- a/target/linux/ramips/dts/mt7628an_mercusys_mb130-4g-v1.dts +++ b/target/linux/ramips/dts/mt7628an_mercusys_mb130-4g-v1.dts @@ -187,7 +187,7 @@ }; eeprom_radio_8000: eeprom@8000 { - reg = <0x8000 0x200>; + reg = <0x8000 0x4da8>; }; }; }; From ce120be3619610a7457df2d5970a7701b150db51 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 20 Jul 2026 12:46:42 -0700 Subject: [PATCH 127/152] kernel: disable qualcomm crypto Upstream has marked it as BROKEN. Quote: This driver is harmful: - It is much slower than the CPU. - It Has a history of bugs. - It does not have exclusive access to the hardware, causing races with the secure world. - It register its implementations with too low a cra_priority for them to be actually used. Signed-off-by: Rosen Penev Link: https://github.com/openwrt/openwrt/pull/24326 Signed-off-by: Jonas Jelonek --- target/linux/ipq40xx/config-6.18 | 7 ------- target/linux/qualcommax/config-6.12 | 9 --------- target/linux/qualcommbe/config-6.18 | 9 --------- 3 files changed, 25 deletions(-) diff --git a/target/linux/ipq40xx/config-6.18 b/target/linux/ipq40xx/config-6.18 index ec05b54e23..f1dabe6c5d 100644 --- a/target/linux/ipq40xx/config-6.18 +++ b/target/linux/ipq40xx/config-6.18 @@ -101,13 +101,6 @@ CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CRYPTD=y CONFIG_CRYPTO_DEFLATE=y CONFIG_CRYPTO_DES=y -CONFIG_CRYPTO_DEV_QCE=y -# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set -# CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL is not set -# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set -CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER=y -CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y -CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 CONFIG_CRYPTO_DEV_QCOM_RNG=y CONFIG_CRYPTO_DRBG=y CONFIG_CRYPTO_DRBG_HMAC=y diff --git a/target/linux/qualcommax/config-6.12 b/target/linux/qualcommax/config-6.12 index ed50f8fce4..8daaff3832 100644 --- a/target/linux/qualcommax/config-6.12 +++ b/target/linux/qualcommax/config-6.12 @@ -107,15 +107,6 @@ CONFIG_CRC8=y CONFIG_CRYPTO_AUTHENC=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_QCE=y -CONFIG_CRYPTO_DEV_QCE_AEAD=y -# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set -CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y -# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set -# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set -CONFIG_CRYPTO_DEV_QCE_SHA=y -CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y -CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 CONFIG_CRYPTO_DEV_QCOM_RNG=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_HASH_INFO=y diff --git a/target/linux/qualcommbe/config-6.18 b/target/linux/qualcommbe/config-6.18 index 4c31189d1a..2da62a67bf 100644 --- a/target/linux/qualcommbe/config-6.18 +++ b/target/linux/qualcommbe/config-6.18 @@ -113,15 +113,6 @@ CONFIG_CRC8=y CONFIG_CRYPTO_AUTHENC=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_DEFLATE=y -CONFIG_CRYPTO_DEV_QCE=y -CONFIG_CRYPTO_DEV_QCE_AEAD=y -# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set -CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y -# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set -# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set -CONFIG_CRYPTO_DEV_QCE_SHA=y -CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y -CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512 CONFIG_CRYPTO_DEV_QCOM_RNG=y CONFIG_CRYPTO_ECB=y CONFIG_CRYPTO_HASH_INFO=y From 3978d4e5ee17e9ba0f2f6dd3fd8489a369fb4a14 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 15 Jul 2026 15:58:06 -0700 Subject: [PATCH 128/152] swconfig: fix portmap memory leak and hardening Free the per-port segment strings on unregister to avoid leaking them across device rebind. Drop the dead (phys < 0) check and guard the kstrdup allocation against failure. Move swconfig_create_led_trigger() before list_add_tail() in register_switch so a failing registration never publishes the device on the global swdevs list with dangling portmap/portbuf pointers (a use-after-free for any swdevs iterator). Route both the LED trigger failure and the -ENFILE (swdev id exhaustion) failure paths through a shared cleanup that frees portbuf, portmap, and the per-port segment strings instead of leaking them. Reorder the frees in unregister_switch to run after list_del so the buffers are not freed while the device is still reachable from swdevs. Signed-off-by: Rosen Penev Link: https://github.com/openwrt/openwrt/pull/24246 Signed-off-by: Jonas Jelonek --- .../generic/files/drivers/net/phy/swconfig.c | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/target/linux/generic/files/drivers/net/phy/swconfig.c b/target/linux/generic/files/drivers/net/phy/swconfig.c index d57c112802..976bf78419 100644 --- a/target/linux/generic/files/drivers/net/phy/swconfig.c +++ b/target/linux/generic/files/drivers/net/phy/swconfig.c @@ -1056,7 +1056,6 @@ static struct genl_family switch_fam = { .resv_start_op = SWITCH_CMD_SET_VLAN + 1, }; -#ifdef CONFIG_OF static void of_switch_load_portmap(struct switch_dev *dev) { @@ -1085,19 +1084,37 @@ of_switch_load_portmap(struct switch_dev *dev) } phys = be32_to_cpup(prop++); - if ((phys < 0) | (phys >= dev->ports)) { + if (phys >= dev->ports) { pr_err("%s: physical port index out of range\n", port->name); continue; } dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL); + if (!dev->portmap[phys].s) + continue; dev->portmap[phys].virt = be32_to_cpup(prop); pr_debug("Found port: %s, physical: %d, virtual: %d\n", segment, phys, dev->portmap[phys].virt); } } -#endif + +static void +switch_free_portmap(struct switch_dev *dev) +{ + int i; + + if (dev->portmap) { + for (i = 0; i < dev->ports; i++) + kfree(dev->portmap[i].s); + + kfree(dev->portmap); + dev->portmap = NULL; + } + + kfree(dev->portbuf); + dev->portbuf = NULL; +} int register_switch(struct switch_dev *dev, struct net_device *netdev) @@ -1149,25 +1166,31 @@ register_switch(struct switch_dev *dev, struct net_device *netdev) if (i == max_switches) { swconfig_unlock(); + switch_free_portmap(dev); return -ENFILE; } -#ifdef CONFIG_OF - if (dev->ports) + if (IS_ENABLED(CONFIG_OF) && dev->ports) of_switch_load_portmap(dev); -#endif /* fill device name */ snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i); - list_add_tail(&dev->dev_list, &swdevs); swconfig_unlock(); err = swconfig_create_led_trigger(dev); if (err) - return err; + goto unlink; return 0; + +unlink: + swconfig_lock(); + list_del(&dev->dev_list); + swconfig_unlock(); + switch_free_portmap(dev); + + return err; } EXPORT_SYMBOL_GPL(register_switch); @@ -1175,12 +1198,12 @@ void unregister_switch(struct switch_dev *dev) { swconfig_destroy_led_trigger(dev); - kfree(dev->portbuf); mutex_lock(&dev->sw_mutex); swconfig_lock(); list_del(&dev->dev_list); swconfig_unlock(); mutex_unlock(&dev->sw_mutex); + switch_free_portmap(dev); } EXPORT_SYMBOL_GPL(unregister_switch); From 419f023ec3c492cb39cf074ebb6dc41ac062b4b7 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Thu, 16 Jul 2026 09:20:18 +0200 Subject: [PATCH 129/152] realtek: eth: add rteth_free_skb() helper Add a helper to simplify SKB freeing and rx counter handling. If SKB is given, it is freed and the pointer is set to zero. The return value is the number of freed SKBs (0/1). With this the existing cleanup and error handling can be simplified like dev->stats.tx_errors += rteth_free_skb(&packet->skb); Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 0dfd36a82f..c8591541e3 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -174,6 +174,17 @@ static void rteth_93xx_create_tx_header(struct rteth_packet *h, unsigned int por h->cpu_tag[7] = BIT_ULL(port) & 0xffff; } +static int rteth_free_skb(struct sk_buff **skb) +{ + if (*skb) { + dev_kfree_skb_any(*skb); + *skb = NULL; + return 1; + } + + return 0; +} + static inline void rteth_reenable_irq(struct rteth_ctrl *ctrl, int ring) { u32 shift = ctrl->r->rx_rings % 32; @@ -623,8 +634,7 @@ static void rteth_free_tx_buffers(struct rteth_ctrl *ctrl) dma_unmap_single(&ctrl->pdev->dev, packet->dma, packet->skb->len, DMA_TO_DEVICE); - dev_kfree_skb_any(packet->skb); - packet->skb = NULL; + rteth_free_skb(&packet->skb); } } } @@ -1056,21 +1066,18 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_consume_skb_any(packet->skb); } + packet->len = len; + packet->skb = skb; packet->dma = dma_map_single(&ctrl->pdev->dev, skb->data, len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(&ctrl->pdev->dev, packet->dma))) { - dev_kfree_skb_any(skb); - packet->skb = NULL; - dev->stats.tx_errors++; - + dev->stats.tx_errors += rteth_free_skb(&packet->skb); return NETDEV_TX_OK; } if (port >= 0) ctrl->r->create_tx_header(packet, port, 0); // TODO ok to set prio to 0? - /* Transfer data and hand packet over to switch */ - packet->len = len; - packet->skb = skb; + /* Hand packet over to switch */ dma_wmb(); ctrl->tx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; ctrl->tx_data[ring].slot = (slot + 1) % RTETH_TX_RING_SIZE; From 54a7e093299b212c5090886aa767ba72cefbd46c Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Thu, 16 Jul 2026 09:44:31 +0200 Subject: [PATCH 130/152] realtek: eth: Adapt SKB defines Testing shows that the existing performance boost by aligning the data to 2 bytes (NET_IP_ALIGN) breaks fragment handling on RTL838x devices. The SoC silently drops 2 bytes between each fragment. Use this tricky alignment only when it is supported. This will bring down performance on RTL838x SoCs by 30%. All other SoCs are unaffected. This is a legitimate tradeoff for allowing multiple fragments. While we are here make clearer what the definies are about and rename them to a SKB_ prefix. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 27 ++++++++++++------- .../drivers/net/ethernet/rtl838x_eth.h | 1 + 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index c8591541e3..b955219c92 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -30,7 +30,6 @@ #define RING_OWN_HW BIT(0) #define RING_WRAP BIT(1) -#define RTETH_SKB_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN) #define RTETH_RX_RING_SIZE 128 #define RTETH_RX_RINGS 2 #define RTETH_TX_RING_SIZE 16 @@ -42,7 +41,11 @@ #define RX_TRUNCATE_EN_93XX BIT(6) #define RX_TRUNCATE_EN_83XX BIT(4) #define TX_PAD_EN_838X BIT(5) -#define RING_BUFFER 1600 + +#define SKB_FRAG_SIZE 1600 +#define SKB_PAD MAX(32, L1_CACHE_BYTES) +#define SKB_HEADROOM_FAST (SKB_PAD + NET_IP_ALIGN) +#define SKB_HEADROOM_SLOW SKB_PAD /* Define page pool that holds 2KB fragments in 4KB pages and has 8 safety pages */ #define PPOOL_FRAG_SIZE 2048 @@ -668,7 +671,7 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) * and the DMA capabilities of the network adapter. Be defensive and add some checks to * assist further error analysis. */ - BUILD_BUG_ON(RTETH_SKB_HEADROOM + RING_BUFFER + + BUILD_BUG_ON(SKB_HEADROOM_FAST + SKB_FRAG_SIZE + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) > PPOOL_FRAG_SIZE); for (int r = 0; r < RTETH_RX_RINGS; r++) { @@ -686,8 +689,9 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) highmem |= PageHighMem(page); paddr = max(paddr, page_to_phys(page)); - packet->size = RING_BUFFER; - packet->dma = page_pool_get_dma_addr(page) + RTETH_SKB_HEADROOM + offset; + packet->size = SKB_FRAG_SIZE; + packet->dma = page_pool_get_dma_addr(page) + + ctrl->r->skb_headroom + offset; packet->page = page; packet->page_offset = offset; ctrl->rx_data[r].ring[i] = ctrl->rx_data_dma + @@ -1126,7 +1130,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) packet = &ctrl->rx_data[ring].packet[slot]; len = packet->len - ETH_FCS_LEN; - if (unlikely(len > RING_BUFFER)) { + if (unlikely(len > SKB_FRAG_SIZE)) { netdev_err(dev, "invalid packet with %d bytes received\n", len); dev->stats.rx_errors++; goto recycle; @@ -1144,10 +1148,11 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) packet->page = new_page; packet->page_offset = new_offset; - packet->dma = page_pool_get_dma_addr(new_page) + new_offset + RTETH_SKB_HEADROOM; + packet->dma = page_pool_get_dma_addr(new_page) + + new_offset + ctrl->r->skb_headroom; page_pool_dma_sync_for_cpu(ctrl->rx_qs[ring].page_pool, old_page, - old_offset + RTETH_SKB_HEADROOM, len); + old_offset + ctrl->r->skb_headroom, len); skb = napi_build_skb(page_address(old_page) + old_offset, PPOOL_FRAG_SIZE); if (unlikely(!skb)) { @@ -1156,7 +1161,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) goto recycle; } - skb_reserve(skb, RTETH_SKB_HEADROOM); + skb_reserve(skb, ctrl->r->skb_headroom); skb_mark_for_recycle(skb); skb_put(skb, len); @@ -1448,6 +1453,7 @@ static const struct rteth_config rteth_838x_cfg = { .dma_tx_base = RTETH_838X_DMA_TX_BASE, .mac_force_mode_ctrl = RTETH_838X_MAC_FORCE_MODE_CTRL, .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0, + .skb_headroom = SKB_HEADROOM_SLOW, .mac_reg = { RTETH_838X_MAC_ADDR_CTRL, RTETH_838X_MAC_ADDR_CTRL_ALE, RTETH_838X_MAC_ADDR_CTRL_MAC }, @@ -1494,6 +1500,7 @@ static const struct rteth_config rteth_839x_cfg = { .dma_tx_base = RTETH_839X_DMA_TX_BASE, .mac_force_mode_ctrl = RTETH_839X_MAC_FORCE_MODE_CTRL, .rst_glb_ctrl = RTL839X_RST_GLB_CTRL, + .skb_headroom = SKB_HEADROOM_FAST, .mac_reg = { RTETH_839X_MAC_ADDR_CTRL }, .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL, .update_counter = rteth_83xx_update_counter, @@ -1540,6 +1547,7 @@ static const struct rteth_config rteth_930x_cfg = { .l2_ntfy_if_intr_msk = RTL930X_L2_NTFY_IF_INTR_MSK, .mac_force_mode_ctrl = RTETH_930X_MAC_FORCE_MODE_CTRL, .rst_glb_ctrl = RTL930X_RST_GLB_CTRL_0, + .skb_headroom = SKB_HEADROOM_FAST, .mac_reg = { RTETH_930X_MAC_L2_ADDR_CTRL }, .l2_tbl_flush_ctrl = RTL930X_L2_TBL_FLUSH_CTRL, .update_counter = rteth_93xx_update_counter, @@ -1585,6 +1593,7 @@ static const struct rteth_config rteth_931x_cfg = { .l2_ntfy_if_intr_msk = RTL931X_L2_NTFY_IF_INTR_MSK, .mac_force_mode_ctrl = RTETH_931X_MAC_FORCE_MODE_CTRL, .rst_glb_ctrl = RTL931X_RST_GLB_CTRL, + .skb_headroom = SKB_HEADROOM_FAST, .mac_reg = { RTETH_930X_MAC_L2_ADDR_CTRL }, .l2_tbl_flush_ctrl = RTL931X_L2_TBL_FLUSH_CTRL, .update_counter = rteth_93xx_update_counter, diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h index 36ba386dd3..9d6837e915 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h @@ -209,6 +209,7 @@ struct rteth_config { int dma_rx_base; int dma_tx_base; int rst_glb_ctrl; + int skb_headroom; u32 mac_reg[RTETH_MAX_MAC_REGS]; int l2_tbl_flush_ctrl; void (*create_tx_header)(struct rteth_packet *h, unsigned int dest_port, int prio); From edc3c3f63f443ab3fb626907c089e2790c76ddb3 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Fri, 17 Jul 2026 10:03:59 +0200 Subject: [PATCH 131/152] realtek: eth: prepare counters for fragments Until now the driver packs each received packet into one ring buffer. In the future jumbo packets will be split into multiple fragments. Prepare the ring counters for this: - Add the more flag to SoC packet header. This denotes if a packet has more data after the current fragment. - Ring counters must be handled per packet and not per fragment. Increase work_done after each packet and not after each fragment. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 36 ++++++++++++------- .../drivers/net/ethernet/rtl838x_eth.h | 3 -- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index b955219c92..d9eb474ecb 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -42,6 +42,7 @@ #define RX_TRUNCATE_EN_83XX BIT(4) #define TX_PAD_EN_838X BIT(5) +#define SKB_MTU 1600 #define SKB_FRAG_SIZE 1600 #define SKB_PAD MAX(32, L1_CACHE_BYTES) #define SKB_HEADROOM_FAST (SKB_PAD + NET_IP_ALIGN) @@ -57,7 +58,8 @@ struct rteth_packet { dma_addr_t dma; u16 reserved; u16 size; - u16 offset; + u16 more:1; + u16 offset:15; u16 len; u16 cpu_tag[10]; /* software mangement and data part */ @@ -466,16 +468,23 @@ static void rteth_839x_hw_reset(struct rteth_ctrl *ctrl) static void rteth_93xx_hw_reset(struct rteth_ctrl *ctrl) { + /* + * The counter registers track the number of packets that are allowed to be appended to + * the ring buffer. On RTL93xx a ring overflow must be avoided at all cost. Defensively + * calculate the space by anticipating that only fully filled packets are received. + */ + int max_frame_size = SKB_MTU + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + 4; + int frags_per_pkt = DIV_ROUND_UP(max_frame_size, SKB_FRAG_SIZE); + int cnt = min(RTETH_RX_RING_SIZE / frags_per_pkt, 0x3ff); + rteth_nic_reset(ctrl, 0x6); /* Setup Head of Line */ for (int ring = 0; ring < RTETH_RX_RINGS; ring++) { - int cnt = min(RTETH_RX_RING_SIZE, 0x3ff); int shift = (ring % 3) * 10; int reg = (ring / 3) * 4; u32 v; - /* set ring size */ regmap_update_bits(ctrl->map, ctrl->r->dma_if_rx_ring_size + reg, 0x3ff << shift, cnt << shift); /* clear counters by simply writing the current register values back */ @@ -544,9 +553,9 @@ static void rteth_hw_ring_setup(struct rteth_ctrl *ctrl) static void rteth_838x_hw_en_rxtx(struct rteth_ctrl *ctrl) { - /* Truncate RX buffer to DEFAULT_MTU bytes, pad TX */ + /* Truncate RX buffer to SKB_MTU bytes, pad TX */ regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, - (DEFAULT_MTU << 16) | RX_TRUNCATE_EN_83XX | TX_PAD_EN_838X); + (SKB_MTU << 16) | RX_TRUNCATE_EN_83XX | TX_PAD_EN_838X); rteth_enable_all_rx_irqs(ctrl); @@ -570,7 +579,7 @@ static void rteth_838x_hw_en_rxtx(struct rteth_ctrl *ctrl) static void rteth_839x_hw_en_rxtx(struct rteth_ctrl *ctrl) { /* Setup CPU-Port: RX Buffer */ - regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (DEFAULT_MTU << 5) | RX_TRUNCATE_EN_83XX); + regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (SKB_MTU << 5) | RX_TRUNCATE_EN_83XX); rteth_enable_all_rx_irqs(ctrl); @@ -593,8 +602,8 @@ static void rteth_839x_hw_en_rxtx(struct rteth_ctrl *ctrl) static void rteth_930x_hw_en_rxtx(struct rteth_ctrl *ctrl) { - /* Setup CPU-Port: RX Buffer truncated at DEFAULT_MTU Bytes */ - regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (DEFAULT_MTU << 16) | RX_TRUNCATE_EN_93XX); + /* Setup CPU-Port: RX Buffer truncated at SKB_MTU Bytes */ + regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (SKB_MTU << 16) | RX_TRUNCATE_EN_93XX); rteth_enable_all_rx_irqs(ctrl); @@ -610,8 +619,8 @@ static void rteth_930x_hw_en_rxtx(struct rteth_ctrl *ctrl) static void rteth_931x_hw_en_rxtx(struct rteth_ctrl *ctrl) { - /* Setup CPU-Port: RX Buffer truncated at DEFAULT_MTU Bytes */ - regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (DEFAULT_MTU << 16) | RX_TRUNCATE_EN_93XX); + /* Setup CPU-Port: RX Buffer truncated at SKB_MTU Bytes */ + regmap_write(ctrl->map, ctrl->r->dma_if_ctrl, (SKB_MTU << 16) | RX_TRUNCATE_EN_93XX); rteth_enable_all_rx_irqs(ctrl); @@ -1118,6 +1127,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) dma_addr_t packet_dma; struct sk_buff *skb; struct dsa_tag tag; + bool is_tail; while (work_done < budget) { slot = ctrl->rx_data[ring].slot; @@ -1129,6 +1139,9 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) packet = &ctrl->rx_data[ring].packet[slot]; len = packet->len - ETH_FCS_LEN; + is_tail = !packet->more; + if (is_tail) + work_done++; if (unlikely(len > SKB_FRAG_SIZE)) { netdev_err(dev, "invalid packet with %d bytes received\n", len); @@ -1189,7 +1202,6 @@ recycle: dma_wmb(); ctrl->rx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; ctrl->rx_data[ring].slot = (slot + 1) % RTETH_RX_RING_SIZE; - work_done++; } spin_lock(&ctrl->rx_lock); @@ -1702,7 +1714,7 @@ static int rteth_probe(struct platform_device *pdev) dev->ethtool_ops = &rteth_ethtool_ops; dev->min_mtu = ETH_ZLEN; - dev->max_mtu = DEFAULT_MTU; + dev->max_mtu = SKB_MTU; dev->features = NETIF_F_RXCSUM; dev->hw_features = NETIF_F_RXCSUM; dev->netdev_ops = ctrl->r->netdev_ops; diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h index 9d6837e915..052dcf21ef 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h @@ -181,9 +181,6 @@ #define RTL93XX_CPU_TAG1_IGNORE_STP_MASK GENMASK(2, 2) -/* Default MTU with jumbo frames support */ -#define DEFAULT_MTU 9000 - struct p_hdr; struct dsa_tag; struct rteth_ctrl; From cba031de5d39aa054b35a252b1ce81c72be6ed60 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Fri, 17 Jul 2026 11:05:43 +0200 Subject: [PATCH 132/152] realtek: eth: Explain page allocation order Add a comment why we are doing this and make use of a helper variable. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../files-6.18/drivers/net/ethernet/rtl838x_eth.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index d9eb474ecb..838667c312 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -1124,11 +1124,14 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) unsigned int len, new_offset, old_offset; struct page *old_page, *new_page; struct rteth_packet *packet; + struct page_pool *pool; dma_addr_t packet_dma; struct sk_buff *skb; struct dsa_tag tag; bool is_tail; + pool = ctrl->rx_qs[ring].page_pool; + while (work_done < budget) { slot = ctrl->rx_data[ring].slot; packet_dma = ctrl->rx_data[ring].ring[slot]; @@ -1149,8 +1152,11 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) goto recycle; } - new_page = page_pool_dev_alloc_frag(ctrl->rx_qs[ring].page_pool, - &new_offset, PPOOL_FRAG_SIZE); + /* + * Avoid complex error handling by allocating the new page before SKB consumption. + * In case of failure drop the data and reuse the existing page. + */ + new_page = page_pool_dev_alloc_frag(pool, &new_offset, PPOOL_FRAG_SIZE); if (unlikely(!new_page)) { dev->stats.rx_dropped++; goto recycle; @@ -1164,12 +1170,12 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) packet->dma = page_pool_get_dma_addr(new_page) + new_offset + ctrl->r->skb_headroom; - page_pool_dma_sync_for_cpu(ctrl->rx_qs[ring].page_pool, old_page, + page_pool_dma_sync_for_cpu(pool, old_page, old_offset + ctrl->r->skb_headroom, len); skb = napi_build_skb(page_address(old_page) + old_offset, PPOOL_FRAG_SIZE); if (unlikely(!skb)) { - page_pool_put_full_page(ctrl->rx_qs[ring].page_pool, old_page, true); + page_pool_put_full_page(pool, old_page, true); dev->stats.rx_dropped++; goto recycle; } From 39d466aba3f65fcf3461bf13fff59fac50d608c6 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Fri, 17 Jul 2026 11:35:41 +0200 Subject: [PATCH 133/152] realtek: eth: prepare napi receive for fragments Reorder the code around napi_gro_receive() so that it fits nicely for the upcoming fragment handling. For this relocate the FCS stripping and eth_type_trans() calls short before the handover when the packet is fully built. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 838667c312..8784bb86d0 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -1141,7 +1141,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) break; packet = &ctrl->rx_data[ring].packet[slot]; - len = packet->len - ETH_FCS_LEN; + len = packet->len; is_tail = !packet->more; if (is_tail) work_done++; @@ -1192,7 +1192,6 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) skb->offload_fwd_mark = 1; } - skb->protocol = eth_type_trans(skb, dev); if (dev->features & NETIF_F_RXCSUM) { if (tag.crc_error) skb_checksum_none_assert(skb); @@ -1200,10 +1199,18 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) skb->ip_summed = CHECKSUM_UNNECESSARY; } - rx_packets++; - rx_bytes += len; - napi_gro_receive(&ctrl->rx_qs[ring].napi, skb); - + if (is_tail && skb) { + if (unlikely(skb->len < ETH_HLEN + ETH_FCS_LEN)) { + dev->stats.rx_errors += rteth_free_skb(&skb); + } else { + pskb_trim(skb, skb->len - ETH_FCS_LEN); + rx_bytes += skb->len; + rx_packets++; + skb->protocol = eth_type_trans(skb, dev); + napi_gro_receive(&ctrl->rx_qs[ring].napi, skb); + skb = NULL; + } + } recycle: dma_wmb(); ctrl->rx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; From 7226c6edbf5e3f27f2e4bce5844780e2a613ee21 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Fri, 17 Jul 2026 11:57:34 +0200 Subject: [PATCH 134/152] realtek: eth: add helper for SKB creation Simplify rteth_hw_receive() by using a separate helper for SKB creation. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 84 +++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 8784bb86d0..45145fcde3 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -1117,14 +1117,54 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, + struct rteth_packet *packet) +{ + struct page_pool *pool = ctrl->rx_qs[ring].page_pool; + unsigned int offset = packet->page_offset; + struct net_device *dev = ctrl->dev; + struct page *page = packet->page; + unsigned int len = packet->len; + struct sk_buff *skb; + struct dsa_tag tag; + + page_pool_dma_sync_for_cpu(pool, page, offset + ctrl->r->skb_headroom, len); + skb = napi_build_skb(page_address(page) + offset, PPOOL_FRAG_SIZE); + if (unlikely(!skb)) { + page_pool_put_full_page(pool, page, true); + return NULL; + } + + skb_reserve(skb, ctrl->r->skb_headroom); + skb_mark_for_recycle(skb); + skb_put(skb, len); + + ctrl->r->decode_tag(packet, &tag); + if (netdev_uses_dsa(dev)) { + if (tag.port < ctrl->r->cpu_port) + skb_dst_set_noref(skb, &ctrl->dsa_meta[tag.port]->dst); + if (tag.l2_offloaded) + skb->offload_fwd_mark = 1; + } + + if (dev->features & NETIF_F_RXCSUM) { + if (tag.crc_error) + skb_checksum_none_assert(skb); + else + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + return skb; +} + static int rteth_hw_receive(struct net_device *dev, int ring, int budget) { int slot, work_done = 0, rx_packets = 0, rx_bytes = 0; struct rteth_ctrl *ctrl = netdev_priv(dev); - unsigned int len, new_offset, old_offset; - struct page *old_page, *new_page; + unsigned int len, new_offset; struct rteth_packet *packet; struct page_pool *pool; + struct page *new_page; dma_addr_t packet_dma; struct sk_buff *skb; struct dsa_tag tag; @@ -1162,41 +1202,10 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) goto recycle; } - old_page = packet->page; - old_offset = packet->page_offset; - - packet->page = new_page; - packet->page_offset = new_offset; - packet->dma = page_pool_get_dma_addr(new_page) - + new_offset + ctrl->r->skb_headroom; - - page_pool_dma_sync_for_cpu(pool, old_page, - old_offset + ctrl->r->skb_headroom, len); - - skb = napi_build_skb(page_address(old_page) + old_offset, PPOOL_FRAG_SIZE); + skb = rteth_create_skb(ctrl, ring, packet); if (unlikely(!skb)) { - page_pool_put_full_page(pool, old_page, true); + netdev_err(dev, "skb creation failed\n"); dev->stats.rx_dropped++; - goto recycle; - } - - skb_reserve(skb, ctrl->r->skb_headroom); - skb_mark_for_recycle(skb); - skb_put(skb, len); - - ctrl->r->decode_tag(packet, &tag); - if (netdev_uses_dsa(dev)) { - if (tag.port < ctrl->r->cpu_port) - skb_dst_set_noref(skb, &ctrl->dsa_meta[tag.port]->dst); - if (tag.l2_offloaded) - skb->offload_fwd_mark = 1; - } - - if (dev->features & NETIF_F_RXCSUM) { - if (tag.crc_error) - skb_checksum_none_assert(skb); - else - skb->ip_summed = CHECKSUM_UNNECESSARY; } if (is_tail && skb) { @@ -1211,6 +1220,11 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) skb = NULL; } } + + packet->page = new_page; + packet->page_offset = new_offset; + packet->dma = page_pool_get_dma_addr(new_page) + + new_offset + ctrl->r->skb_headroom; recycle: dma_wmb(); ctrl->rx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; From 7683e9e28ec50be8a7a5c5e436fa2abb3e381a80 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Fri, 17 Jul 2026 12:21:47 +0200 Subject: [PATCH 135/152] realtek: eth: increase error counters under lock Change device error statistics like normal statistics - under a lock. Reword associated messages in preparation for fragment handling. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../files-6.18/drivers/net/ethernet/rtl838x_eth.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 45145fcde3..7e31d73868 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -1159,7 +1159,7 @@ static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, static int rteth_hw_receive(struct net_device *dev, int ring, int budget) { - int slot, work_done = 0, rx_packets = 0, rx_bytes = 0; + int slot, work_done = 0, rx_packets = 0, rx_bytes = 0, rx_dropped = 0, rx_errors = 0; struct rteth_ctrl *ctrl = netdev_priv(dev); unsigned int len, new_offset; struct rteth_packet *packet; @@ -1187,8 +1187,8 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) work_done++; if (unlikely(len > SKB_FRAG_SIZE)) { - netdev_err(dev, "invalid packet with %d bytes received\n", len); - dev->stats.rx_errors++; + netdev_err(dev, "invalid fragment with %d bytes received\n", len); + rx_errors++; goto recycle; } @@ -1198,7 +1198,8 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) */ new_page = page_pool_dev_alloc_frag(pool, &new_offset, PPOOL_FRAG_SIZE); if (unlikely(!new_page)) { - dev->stats.rx_dropped++; + netdev_err(dev, "fragment allocation failed\n"); + rx_dropped++; goto recycle; } @@ -1210,7 +1211,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) if (is_tail && skb) { if (unlikely(skb->len < ETH_HLEN + ETH_FCS_LEN)) { - dev->stats.rx_errors += rteth_free_skb(&skb); + rx_errors += rteth_free_skb(&skb); } else { pskb_trim(skb, skb->len - ETH_FCS_LEN); rx_bytes += skb->len; @@ -1234,6 +1235,8 @@ recycle: spin_lock(&ctrl->rx_lock); ctrl->r->update_counter(ctrl, ring, work_done); dev->stats.rx_packets += rx_packets; + dev->stats.rx_dropped += rx_dropped; + dev->stats.rx_errors += rx_errors; dev->stats.rx_bytes += rx_bytes; spin_unlock(&ctrl->rx_lock); From ec8d08496c7c64277eef78cdce5e5e3da073fa6f Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Wed, 15 Jul 2026 11:14:32 +0200 Subject: [PATCH 136/152] realtek: eth: groundwork for jumbo frames The Realtek SoC hardware can split incoming packets over multiple buffers. This allows to receive packets that are larger than the current 2K page pool limit without changing the design to higher order pages. Add a logic to handle these fragments. As a first proof of concept simulate fragmentation by reducing the buffer size down to 400 bytes. With the current support for standard MTU (1500) the packets might consist of up to 4 fragments. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 58 +++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 7e31d73868..b3a07124dc 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -43,7 +43,8 @@ #define TX_PAD_EN_838X BIT(5) #define SKB_MTU 1600 -#define SKB_FRAG_SIZE 1600 +/* TODO: change this to 1568 after fragment handling has been tested in the wild */ +#define SKB_FRAG_SIZE 400 #define SKB_PAD MAX(32, L1_CACHE_BYTES) #define SKB_HEADROOM_FAST (SKB_PAD + NET_IP_ALIGN) #define SKB_HEADROOM_SLOW SKB_PAD @@ -105,6 +106,7 @@ struct rtl838x_rx_q { struct rteth_ctrl *ctrl; struct napi_struct napi; struct page_pool *page_pool; + struct sk_buff *skb; }; struct rteth_ctrl { @@ -664,6 +666,7 @@ static void rteth_free_rx_buffers(struct rteth_ctrl *ctrl) page_pool_put_full_page(ctrl->rx_qs[r].page_pool, packet->page, true); packet->page = NULL; } + rteth_free_skb(&ctrl->rx_qs[r].skb); } } @@ -710,6 +713,7 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) RING_OWN_HW; } + ctrl->rx_qs[r].skb = NULL; ctrl->rx_data[r].ring[RTETH_RX_RING_SIZE - 1] |= RING_WRAP; ctrl->rx_data[r].slot = 0; } @@ -1120,7 +1124,7 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, struct rteth_packet *packet) { - struct page_pool *pool = ctrl->rx_qs[ring].page_pool; + struct page_pool *pool = ctrl->rx_info[ring].pool; unsigned int offset = packet->page_offset; struct net_device *dev = ctrl->dev; struct page *page = packet->page; @@ -1157,6 +1161,26 @@ static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, return skb; } +static int rteth_append_skb(struct sk_buff *skb, struct rteth_ctrl *ctrl, int ring, + struct rteth_packet *packet) +{ + unsigned int nr_frags = skb_shinfo(skb)->nr_frags, len = packet->len; + struct page_pool *pool = ctrl->rx_qs[ring].page_pool; + unsigned int offset = packet->page_offset; + struct page *page = packet->page; + + if (nr_frags >= MAX_SKB_FRAGS) { + page_pool_put_full_page(pool, packet->page, true); + return -ENOMEM; + } + + page_pool_dma_sync_for_cpu(pool, page, offset + ctrl->r->skb_headroom, len); + skb_add_rx_frag(skb, nr_frags, page, offset + ctrl->r->skb_headroom, + len, PPOOL_FRAG_SIZE); + + return 0; +} + static int rteth_hw_receive(struct net_device *dev, int ring, int budget) { int slot, work_done = 0, rx_packets = 0, rx_bytes = 0, rx_dropped = 0, rx_errors = 0; @@ -1166,11 +1190,12 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) struct page_pool *pool; struct page *new_page; dma_addr_t packet_dma; + bool is_head, is_tail; struct sk_buff *skb; - struct dsa_tag tag; - bool is_tail; pool = ctrl->rx_qs[ring].page_pool; + skb = ctrl->rx_qs[ring].skb; + is_tail = !skb; while (work_done < budget) { slot = ctrl->rx_data[ring].slot; @@ -1182,16 +1207,20 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) packet = &ctrl->rx_data[ring].packet[slot]; len = packet->len; + is_head = is_tail; is_tail = !packet->more; if (is_tail) work_done++; if (unlikely(len > SKB_FRAG_SIZE)) { netdev_err(dev, "invalid fragment with %d bytes received\n", len); - rx_errors++; + rx_errors += rteth_free_skb(&skb); goto recycle; } + if (unlikely(!skb && !is_head)) + goto recycle; + /* * Avoid complex error handling by allocating the new page before SKB consumption. * In case of failure drop the data and reuse the existing page. @@ -1199,14 +1228,21 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) new_page = page_pool_dev_alloc_frag(pool, &new_offset, PPOOL_FRAG_SIZE); if (unlikely(!new_page)) { netdev_err(dev, "fragment allocation failed\n"); - rx_dropped++; + rx_dropped += rteth_free_skb(&skb); goto recycle; } - skb = rteth_create_skb(ctrl, ring, packet); - if (unlikely(!skb)) { - netdev_err(dev, "skb creation failed\n"); - dev->stats.rx_dropped++; + if (is_head) { + skb = rteth_create_skb(ctrl, ring, packet); + if (unlikely(!skb)) { + netdev_err(dev, "skb creation failed\n"); + rx_dropped++; + } + } else { + if (unlikely(rteth_append_skb(skb, ctrl, ring, packet))) { + netdev_err(dev, "skb append failed\n"); + rx_dropped += rteth_free_skb(&skb); + } } if (is_tail && skb) { @@ -1240,6 +1276,8 @@ recycle: dev->stats.rx_bytes += rx_bytes; spin_unlock(&ctrl->rx_lock); + ctrl->rx_qs[ring].skb = skb; + return work_done; } From 5cf2b667897098905641d3b6b575eae28bd9e41b Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Thu, 16 Jul 2026 17:22:15 +0200 Subject: [PATCH 137/152] realtek: eth: align dma variables The driver uses rx_data_dma and tx_dma although they have an identical meaning. Align their naming. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index b3a07124dc..f985ef941c 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -123,7 +123,7 @@ struct rteth_ctrl { u32 lastEvent; struct metadata_dst *dsa_meta[RTETH_931X_CPU_PORT]; /* receive handling */ - dma_addr_t rx_data_dma; + dma_addr_t rx_dma; spinlock_t rx_lock; struct rteth_rx *rx_data; /* transmit handling */ @@ -544,7 +544,7 @@ static void rteth_hw_ring_setup(struct rteth_ctrl *ctrl) { for (int r = 0; r < RTETH_RX_RINGS; r++) regmap_write(ctrl->map, ctrl->r->dma_rx_base + r * 4, - ctrl->rx_data_dma + + ctrl->rx_dma + r * sizeof(struct rteth_rx) + offsetof(struct rteth_rx, ring)); for (int r = 0; r < RTETH_TX_RINGS; r++) @@ -706,7 +706,7 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) + ctrl->r->skb_headroom + offset; packet->page = page; packet->page_offset = offset; - ctrl->rx_data[r].ring[i] = ctrl->rx_data_dma + + ctrl->rx_data[r].ring[i] = ctrl->rx_dma + sizeof(struct rteth_rx) * r + offsetof(struct rteth_rx, packet) + sizeof(struct rteth_packet) * i + @@ -1772,7 +1772,7 @@ static int rteth_probe(struct platform_device *pdev) } ctrl->rx_data = dmam_alloc_coherent(&pdev->dev, sizeof(struct rteth_rx) * RTETH_RX_RINGS, - &ctrl->rx_data_dma, GFP_KERNEL); + &ctrl->rx_dma, GFP_KERNEL); ctrl->tx_data = dmam_alloc_coherent(&pdev->dev, sizeof(struct rteth_tx) * RTETH_TX_RINGS, &ctrl->tx_dma, GFP_KERNEL); From 11a859cca7be1db7ba185fd3897940c061468581 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Thu, 16 Jul 2026 17:38:22 +0200 Subject: [PATCH 138/152] realtek: eth: clean up non-dma ring data The driver ring handling consists of two parts: - rx_data: DMA coherent data shared with the SoC - rx_qs: driver internal handling data It is unclear what "qs" stands for. Rename that part to "rx_info". While we are here: - relocate slot from rx_data to rx_info as it is only needed by the driver and not the SoC. - rename page_pool to pool because there is only this single pool inside the driver. Link: https://github.com/openwrt/openwrt/pull/24245 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index f985ef941c..c5e532040e 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -70,7 +70,6 @@ struct rteth_packet { } __packed __aligned(1); struct rteth_rx { - int slot; dma_addr_t ring[RTETH_RX_RING_SIZE]; struct rteth_packet packet[RTETH_RX_RING_SIZE]; }; @@ -101,11 +100,12 @@ struct notify_b { u32 reserved2[8]; }; -struct rtl838x_rx_q { +struct rteth_rx_info { int id; + int slot; struct rteth_ctrl *ctrl; struct napi_struct napi; - struct page_pool *page_pool; + struct page_pool *pool; struct sk_buff *skb; }; @@ -116,7 +116,6 @@ struct rteth_ctrl { void *membase; spinlock_t lock; struct mii_bus *mii_bus; - struct rtl838x_rx_q rx_qs[RTETH_RX_RINGS]; struct phylink *phylink; struct phylink_config phylink_config; const struct rteth_config *r; @@ -125,6 +124,7 @@ struct rteth_ctrl { /* receive handling */ dma_addr_t rx_dma; spinlock_t rx_lock; + struct rteth_rx_info rx_info[RTETH_RX_RINGS]; struct rteth_rx *rx_data; /* transmit handling */ dma_addr_t tx_dma; @@ -407,7 +407,7 @@ static irqreturn_t rteth_net_irq(int irq, void *dev_id) rteth_confirm_and_disable_irqs(ctrl, &rings, &l2); for_each_set_bit(ring, &rings, RTETH_RX_RINGS) { netdev_dbg(dev, "schedule rx ring %lu\n", ring); - napi_schedule(&ctrl->rx_qs[ring].napi); + napi_schedule(&ctrl->rx_info[ring].napi); } if (unlikely(l2)) @@ -663,10 +663,10 @@ static void rteth_free_rx_buffers(struct rteth_ctrl *ctrl) if (!packet->page) continue; - page_pool_put_full_page(ctrl->rx_qs[r].page_pool, packet->page, true); + page_pool_put_full_page(ctrl->rx_info[r].pool, packet->page, true); packet->page = NULL; } - rteth_free_skb(&ctrl->rx_qs[r].skb); + rteth_free_skb(&ctrl->rx_info[r].skb); } } @@ -689,7 +689,7 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) for (int r = 0; r < RTETH_RX_RINGS; r++) { for (int i = 0; i < RTETH_RX_RING_SIZE; i++) { packet = &ctrl->rx_data[r].packet[i]; - page = page_pool_dev_alloc_frag(ctrl->rx_qs[r].page_pool, + page = page_pool_dev_alloc_frag(ctrl->rx_info[r].pool, &offset, PPOOL_FRAG_SIZE); if (!page) { dev_err(&ctrl->pdev->dev, @@ -713,9 +713,9 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) RING_OWN_HW; } - ctrl->rx_qs[r].skb = NULL; + ctrl->rx_info[r].slot = 0; + ctrl->rx_info[r].skb = NULL; ctrl->rx_data[r].ring[RTETH_RX_RING_SIZE - 1] |= RING_WRAP; - ctrl->rx_data[r].slot = 0; } for (int r = 0; r < RTETH_TX_RINGS; r++) { @@ -820,7 +820,7 @@ static int rteth_open(struct net_device *dev) phylink_start(ctrl->phylink); for (int i = 0; i < RTETH_RX_RINGS; i++) - napi_enable(&ctrl->rx_qs[i].napi); + napi_enable(&ctrl->rx_info[i].napi); ctrl->r->hw_init(ctrl); ctrl->r->hw_en_rxtx(ctrl); @@ -913,7 +913,7 @@ static int rteth_stop(struct net_device *dev) rteth_hw_stop(ctrl); for (int i = 0; i < RTETH_RX_RINGS; i++) - napi_disable(&ctrl->rx_qs[i].napi); + napi_disable(&ctrl->rx_info[i].napi); rteth_free_tx_buffers(ctrl); rteth_free_rx_buffers(ctrl); @@ -1165,7 +1165,7 @@ static int rteth_append_skb(struct sk_buff *skb, struct rteth_ctrl *ctrl, int ri struct rteth_packet *packet) { unsigned int nr_frags = skb_shinfo(skb)->nr_frags, len = packet->len; - struct page_pool *pool = ctrl->rx_qs[ring].page_pool; + struct page_pool *pool = ctrl->rx_info[ring].pool; unsigned int offset = packet->page_offset; struct page *page = packet->page; @@ -1193,12 +1193,12 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) bool is_head, is_tail; struct sk_buff *skb; - pool = ctrl->rx_qs[ring].page_pool; - skb = ctrl->rx_qs[ring].skb; + pool = ctrl->rx_info[ring].pool; + skb = ctrl->rx_info[ring].skb; is_tail = !skb; while (work_done < budget) { - slot = ctrl->rx_data[ring].slot; + slot = ctrl->rx_info[ring].slot; packet_dma = ctrl->rx_data[ring].ring[slot]; rmb(); @@ -1253,7 +1253,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) rx_bytes += skb->len; rx_packets++; skb->protocol = eth_type_trans(skb, dev); - napi_gro_receive(&ctrl->rx_qs[ring].napi, skb); + napi_gro_receive(&ctrl->rx_info[ring].napi, skb); skb = NULL; } } @@ -1265,7 +1265,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) recycle: dma_wmb(); ctrl->rx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; - ctrl->rx_data[ring].slot = (slot + 1) % RTETH_RX_RING_SIZE; + ctrl->rx_info[ring].slot = (slot + 1) % RTETH_RX_RING_SIZE; } spin_lock(&ctrl->rx_lock); @@ -1276,14 +1276,14 @@ recycle: dev->stats.rx_bytes += rx_bytes; spin_unlock(&ctrl->rx_lock); - ctrl->rx_qs[ring].skb = skb; + ctrl->rx_info[ring].skb = skb; return work_done; } static int rteth_poll_rx(struct napi_struct *napi, int budget) { - struct rtl838x_rx_q *rx_q = container_of(napi, struct rtl838x_rx_q, napi); + struct rteth_rx_info *rx_q = container_of(napi, struct rteth_rx_info, napi); struct rteth_ctrl *ctrl = rx_q->ctrl; int work_done, ring = rx_q->id; @@ -1839,9 +1839,9 @@ static int rteth_probe(struct platform_device *pdev) strscpy(dev->name, "eth%d", sizeof(dev->name)); for (int i = 0; i < RTETH_RX_RINGS; i++) { - ctrl->rx_qs[i].id = i; - ctrl->rx_qs[i].ctrl = ctrl; - netif_napi_add(dev, &ctrl->rx_qs[i].napi, rteth_poll_rx); + ctrl->rx_info[i].id = i; + ctrl->rx_info[i].ctrl = ctrl; + netif_napi_add(dev, &ctrl->rx_info[i].napi, rteth_poll_rx); } platform_set_drvdata(pdev, dev); @@ -1870,12 +1870,12 @@ static int rteth_probe(struct platform_device *pdev) } for (int i = 0; i < RTETH_RX_RINGS; i++) { - pp_params.napi = &ctrl->rx_qs[i].napi; - ctrl->rx_qs[i].page_pool = page_pool_create(&pp_params); - if (IS_ERR(ctrl->rx_qs[i].page_pool)) { - err = dev_err_probe(&pdev->dev, PTR_ERR(ctrl->rx_qs[i].page_pool), + pp_params.napi = &ctrl->rx_info[i].napi; + ctrl->rx_info[i].pool = page_pool_create(&pp_params); + if (IS_ERR(ctrl->rx_info[i].pool)) { + err = dev_err_probe(&pdev->dev, PTR_ERR(ctrl->rx_info[i].pool), "Failed to create page pool for ring %d\n", i); - ctrl->rx_qs[i].page_pool = NULL; + ctrl->rx_info[i].pool = NULL; goto cleanup; } } @@ -1895,9 +1895,9 @@ cleanup: if (ctrl->phylink) phylink_destroy(ctrl->phylink); for (int i = 0; i < RTETH_RX_RINGS; i++) { - netif_napi_del(&ctrl->rx_qs[i].napi); - if (ctrl->rx_qs[i].page_pool) - page_pool_destroy(ctrl->rx_qs[i].page_pool); + netif_napi_del(&ctrl->rx_info[i].napi); + if (ctrl->rx_info[i].pool) + page_pool_destroy(ctrl->rx_info[i].pool); } return err; @@ -1916,9 +1916,9 @@ static void rteth_remove(struct platform_device *pdev) phylink_destroy(ctrl->phylink); for (int i = 0; i < RTETH_RX_RINGS; i++) { - netif_napi_del(&ctrl->rx_qs[i].napi); - if (ctrl->rx_qs[i].page_pool) - page_pool_destroy(ctrl->rx_qs[i].page_pool); + netif_napi_del(&ctrl->rx_info[i].napi); + if (ctrl->rx_info[i].pool) + page_pool_destroy(ctrl->rx_info[i].pool); } } From 0f256a0a7adf5741e4a061f59a08cd01c14dc526 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 20 Jul 2026 00:16:52 +0100 Subject: [PATCH 139/152] kernel: modules: package OP-TEE and fTPM modules Add kmod packages to support accessing the fTPM (in-firmware TPM) which can be implemented in the boot firmware of most modern platforms. - kmod-optee: Open Portable Trusted Execution Environment - kmod-optee-rng: OP-TEE random number generator client - kmod-rtc-optee: OP-TEE real-time clock client - kmod-scmi-transport-optee: SCMI OP-TEE transport - kmod-tee: Generic Trusted Execution Environment - kmod-tee-stmm-efi: EFI variable service Trusted Application - kmod-tpm-ftpm-tee: Microsoft fTPM Trusted Application Using the TPM 2.0 can be useful for various security applications, such as encrypting on-disk credentials. OP-TEE itself alone is also useful as it can provide a PKCS#11 token for authentication (eg. to connect to a VPN) and significantly lower the risk of leaking secrets used for authentication. Also, on some platforms RNG and RTC can only be accessed via OP-TEE, and some ARM SystemReady platforms require EFI variables to be access via a TA. Signed-off-by: Daniel Golle --- package/kernel/linux/modules/other.mk | 104 ++++++++++++++++++++++++ package/kernel/linux/modules/rtc.mk | 19 +++++ target/linux/generic/config-6.12 | 1 + target/linux/generic/config-6.18 | 1 + target/linux/qualcommbe/config-6.18 | 1 + target/linux/stm32/stm32mp1/config-6.18 | 1 + 6 files changed, 127 insertions(+) diff --git a/package/kernel/linux/modules/other.mk b/package/kernel/linux/modules/other.mk index e8f62d3172..ad7ac9a0b7 100644 --- a/package/kernel/linux/modules/other.mk +++ b/package/kernel/linux/modules/other.mk @@ -935,6 +935,93 @@ endef $(eval $(call KernelPackage,echo)) +define KernelPackage/tee + SUBMENU:=$(OTHER_MENU) + TITLE:=Trusted Execution Environment (TEE) support + DEPENDS:=+kmod-dma-buf + KCONFIG:=CONFIG_TEE + FILES:=$(LINUX_DIR)/drivers/tee/tee.ko + AUTOLOAD:=$(call AutoLoad,20,tee,1) +endef + +define KernelPackage/tee/description + Generic Trusted Execution Environment subsystem, the interface used to + communicate with a trusted OS running in a secure environment. +endef + +$(eval $(call KernelPackage,tee)) + + +define KernelPackage/optee + SUBMENU:=$(OTHER_MENU) + TITLE:=OP-TEE driver + DEPENDS:=+kmod-i2c-core +kmod-tee + KCONFIG:= \ + CONFIG_OPTEE \ + CONFIG_OPTEE_INSECURE_LOAD_IMAGE=n + FILES:=$(LINUX_DIR)/drivers/tee/optee/optee.ko + AUTOLOAD:=$(call AutoLoad,21,optee,1) +endef + +define KernelPackage/optee/description + Driver for OP-TEE, the Open Portable Trusted Execution Environment running + in the Arm secure world, reached over the SMCCC or FF-A transport. +endef + +$(eval $(call KernelPackage,optee)) + + +define KernelPackage/optee-rng + SUBMENU:=$(OTHER_MENU) + TITLE:=OP-TEE based Random Number Generator + DEPENDS:=+kmod-optee +kmod-random-core + KCONFIG:=CONFIG_HW_RANDOM_OPTEE + FILES:=$(LINUX_DIR)/drivers/char/hw_random/optee-rng.ko + AUTOLOAD:=$(call AutoLoad,22,optee-rng,1) +endef + +define KernelPackage/optee-rng/description + Exposes the random number generator provided by OP-TEE through the kernel + hwrng interface. +endef + +$(eval $(call KernelPackage,optee-rng)) + + +define KernelPackage/scmi-transport-optee + SUBMENU:=$(OTHER_MENU) + TITLE:=SCMI transport based on OP-TEE service + DEPENDS:=@(TARGET_rockchip||TARGET_stm32) +kmod-optee + KCONFIG:=CONFIG_ARM_SCMI_TRANSPORT_OPTEE + FILES:=$(LINUX_DIR)/drivers/firmware/arm_scmi/transports/scmi_transport_optee.ko + AUTOLOAD:=$(call AutoProbe,scmi_transport_optee) +endef + +define KernelPackage/scmi-transport-optee/description + Arm SCMI transport using an OP-TEE service to communicate with the + platform firmware implementing the SCMI server. +endef + +$(eval $(call KernelPackage,scmi-transport-optee)) + + +define KernelPackage/tee-stmm-efi + SUBMENU:=$(OTHER_MENU) + TITLE:=TEE based EFI runtime variable service + DEPENDS:=@TARGET_armsr +kmod-optee + KCONFIG:=CONFIG_TEE_STMM_EFI + FILES:=$(LINUX_DIR)/drivers/firmware/efi/stmm/tee_stmm_efi.ko + AUTOLOAD:=$(call AutoProbe,tee_stmm_efi) +endef + +define KernelPackage/tee-stmm-efi/description + Provides EFI runtime variable services through the StandAloneMM Trusted + Application running in OP-TEE. +endef + +$(eval $(call KernelPackage,tee-stmm-efi)) + + define KernelPackage/keys-encrypted SUBMENU:=$(OTHER_MENU) TITLE:=encrypted keys on kernel keyring @@ -991,6 +1078,23 @@ endef $(eval $(call KernelPackage,tpm)) +define KernelPackage/tpm-ftpm-tee + SUBMENU:=$(OTHER_MENU) + TITLE:=TEE based firmware TPM (fTPM) + DEPENDS:=+kmod-tpm +kmod-optee + KCONFIG:=CONFIG_TCG_FTPM_TEE + FILES:=$(LINUX_DIR)/drivers/char/tpm/tpm_ftpm_tee.ko + AUTOLOAD:=$(call AutoLoad,30,tpm_ftpm_tee,1) +endef + +define KernelPackage/tpm-ftpm-tee/description + Driver for a firmware TPM (fTPM) running as a Trusted Application inside a + TEE such as OP-TEE. It presents the fTPM to Linux as a TPM 2.0 device, + bound over the TEE client bus or a microsoft,ftpm device tree node. +endef + +$(eval $(call KernelPackage,tpm-ftpm-tee)) + define KernelPackage/tpm-tis SUBMENU:=$(OTHER_MENU) TITLE:=TPM TIS 1.2 Interface / TPM 2.0 FIFO Interface diff --git a/package/kernel/linux/modules/rtc.mk b/package/kernel/linux/modules/rtc.mk index 1613f26e4f..7530bd2f1b 100644 --- a/package/kernel/linux/modules/rtc.mk +++ b/package/kernel/linux/modules/rtc.mk @@ -134,6 +134,25 @@ endef $(eval $(call KernelPackage,rtc-mv)) +define KernelPackage/rtc-optee + SUBMENU:=$(RTC_MENU) + TITLE:=OP-TEE based RTC support + DEFAULT:=m if ALL_KMODS && RTC_SUPPORT + DEPENDS:=+kmod-optee + KCONFIG:=CONFIG_RTC_DRV_OPTEE \ + CONFIG_RTC_CLASS=y + FILES:=$(LINUX_DIR)/drivers/rtc/rtc-optee.ko + AUTOLOAD:=$(call AutoProbe,rtc-optee) +endef + +define KernelPackage/rtc-optee/description + Kernel module for the RTC service exposed by the OP-TEE Trusted + Execution Environment. +endef + +$(eval $(call KernelPackage,rtc-optee)) + + define KernelPackage/rtc-pcf8563 SUBMENU:=$(RTC_MENU) TITLE:=Philips PCF8563/Epson RTC8564 RTC support diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12 index ca96112d1b..6c1d9e5207 100644 --- a/target/linux/generic/config-6.12 +++ b/target/linux/generic/config-6.12 @@ -6661,6 +6661,7 @@ CONFIG_TCP_CONG_CUBIC=y # CONFIG_TCS3414 is not set # CONFIG_TCS3472 is not set # CONFIG_TEE is not set +# CONFIG_TEE_BNXT_FW is not set # CONFIG_TEGRA_AHB is not set # CONFIG_TEGRA_HOST1X is not set # CONFIG_TEHUTI is not set diff --git a/target/linux/generic/config-6.18 b/target/linux/generic/config-6.18 index 3e1a57f9c7..2141bc0d07 100644 --- a/target/linux/generic/config-6.18 +++ b/target/linux/generic/config-6.18 @@ -7030,6 +7030,7 @@ CONFIG_TCP_CONG_CUBIC=y # CONFIG_TCS3414 is not set # CONFIG_TCS3472 is not set # CONFIG_TEE is not set +# CONFIG_TEE_BNXT_FW is not set # CONFIG_TEGRA_AHB is not set # CONFIG_TEGRA_HOST1X is not set # CONFIG_TEHUTI is not set diff --git a/target/linux/qualcommbe/config-6.18 b/target/linux/qualcommbe/config-6.18 index 2da62a67bf..9476b17629 100644 --- a/target/linux/qualcommbe/config-6.18 +++ b/target/linux/qualcommbe/config-6.18 @@ -416,6 +416,7 @@ CONFIG_QCA807X_PHY=y CONFIG_QCA808X_PHY=y # CONFIG_QCM_DISPCC_2290 is not set # CONFIG_QCM_GCC_2290 is not set +# CONFIG_QCOMTEE is not set # CONFIG_QCOM_A53PLL is not set # CONFIG_QCOM_AOSS_QMP is not set CONFIG_QCOM_APCS_IPC=y diff --git a/target/linux/stm32/stm32mp1/config-6.18 b/target/linux/stm32/stm32mp1/config-6.18 index 77e9f495ff..c1bc6ac943 100644 --- a/target/linux/stm32/stm32mp1/config-6.18 +++ b/target/linux/stm32/stm32mp1/config-6.18 @@ -455,6 +455,7 @@ CONFIG_SQUASHFS_FILE_CACHE=y CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_ZLIB=y CONFIG_STM32MP_EXTI=y +# CONFIG_STM32_DBG_BUS is not set CONFIG_STM32_DMA=y # CONFIG_STM32_DMA3 is not set CONFIG_STM32_DMAMUX=y From d060557066e823ed3d6590d8c4eaa5d649563182 Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 15:38:38 +0200 Subject: [PATCH 140/152] kernel: pse-pd: backport net fixes for unregister/isr pse_isr() 32-bit OOB bitmap access, disable IRQ before freeing PI data on unregister, and guard against freed PI data on regulator disable. Pending upstream in the PSE-core teardown UAF fix series [PATCH net v2 0/2] (in review; merge expected soon). Link: https://lore.kernel.org/all/20260711121611.1639086-1-github@szelinsky.de/ Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- ...s-bitmap-access-in-pse_isr-on-32-bit.patch | 80 +++++++++++++++++++ ...before-freeing-PI-data-in-unregister.patch | 35 ++++++++ ...t-freed-PI-data-on-regulator-disable.patch | 62 ++++++++++++++ ...lifecycle-events-on-controller-regis.patch | 6 +- ...ev-psec-via-PSE-notifier-and-remove-.patch | 2 +- 5 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 target/linux/generic/pending-6.18/895-00-net-pse-pd-fix-out-of-bounds-bitmap-access-in-pse_isr-on-32-bit.patch create mode 100644 target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch create mode 100644 target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch diff --git a/target/linux/generic/pending-6.18/895-00-net-pse-pd-fix-out-of-bounds-bitmap-access-in-pse_isr-on-32-bit.patch b/target/linux/generic/pending-6.18/895-00-net-pse-pd-fix-out-of-bounds-bitmap-access-in-pse_isr-on-32-bit.patch new file mode 100644 index 0000000000..aa3ee7cb7d --- /dev/null +++ b/target/linux/generic/pending-6.18/895-00-net-pse-pd-fix-out-of-bounds-bitmap-access-in-pse_isr-on-32-bit.patch @@ -0,0 +1,80 @@ +From 5099807f335ce4f783f0578bef7278fffad30b07 Mon Sep 17 00:00:00 2001 +From: Kory Maincent +Date: Wed, 15 Apr 2026 15:02:59 +0200 +Subject: [PATCH] net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on + 32-bit + +In pse_isr(), notifs_mask was declared as a single unsigned long on the +stack (32 bits on 32-bit architectures). For PSE controllers with more +than 32 ports, this causes two problems: + +- map_event callbacks could wrote bit positions >= 32 via + *notifs_mask |= BIT(i), which is undefined behaviour on a 32-bit + unsigned long and corrupts adjacent stack memory. + +- for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) treats + ¬ifs_mask as a multi-word bitmap and reads beyond the single + unsigned long when nr_lines > BITS_PER_LONG. + +Fix this by moving notifs_mask out of the stack and into struct pse_irq +as a dynamically allocated bitmap. It is sized with +BITS_TO_LONGS(pcdev->nr_lines) words in devm_pse_irq_helper(), so it +is always wide enough regardless of the host word size. + +[Jakub]: No upstream driver currently supports >=32 ports. + +Signed-off-by: Kory Maincent +Link: https://patch.msgid.link/20260415130300.806152-1-kory.maincent@bootlin.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/pse-pd/pse_core.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -1170,6 +1170,7 @@ struct pse_irq { + struct pse_controller_dev *pcdev; + struct pse_irq_desc desc; + unsigned long *notifs; ++ unsigned long *notifs_mask; + }; + + /** +@@ -1247,7 +1248,6 @@ static int pse_set_config_isr(struct pse + static irqreturn_t pse_isr(int irq, void *data) + { + struct pse_controller_dev *pcdev; +- unsigned long notifs_mask = 0; + struct pse_irq_desc *desc; + struct pse_irq *h = data; + int ret, i; +@@ -1257,14 +1257,15 @@ static irqreturn_t pse_isr(int irq, void + + /* Clear notifs mask */ + memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); ++ bitmap_zero(h->notifs_mask, pcdev->nr_lines); + mutex_lock(&pcdev->lock); +- ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask); +- if (ret || !notifs_mask) { ++ ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask); ++ if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) { + mutex_unlock(&pcdev->lock); + return IRQ_NONE; + } + +- for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) { ++ for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) { + unsigned long notifs, rnotifs; + struct pse_ntf ntf = {}; + +@@ -1340,6 +1341,10 @@ int devm_pse_irq_helper(struct pse_contr + if (!h->notifs) + return -ENOMEM; + ++ h->notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines, GFP_KERNEL); ++ if (!h->notifs_mask) ++ return -ENOMEM; ++ + ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr, + IRQF_ONESHOT | irq_flags, + irq_name, h); diff --git a/target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch b/target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch new file mode 100644 index 0000000000..4e99d5ef42 --- /dev/null +++ b/target/linux/generic/pending-6.18/895-00a-net-pse-pd-disable-IRQ-before-freeing-PI-data-in-unregister.patch @@ -0,0 +1,35 @@ +From a2d81faf75c1661d7a5977725731f481ba8de92d Mon Sep 17 00:00:00 2001 +From: Carlo Szelinsky +Date: Sat, 23 May 2026 21:32:58 +0200 +Subject: [PATCH] net: pse-pd: disable IRQ before freeing PI data in unregister + +pse_controller_unregister() frees the PI array via pse_release_pis() +before disabling the controller IRQ. The threaded IRQ handler pse_isr() +walks pcdev->pi[] (via pse_set_config_isr() and +regulator_notifier_call_chain() on pcdev->pi[i].rdev), so an interrupt +arriving in the window between pse_release_pis() and disable_irq() +dereferences freed memory. + +Disable the IRQ first, then release the PI array. cancel_work_sync() +for the notification worker stays after pse_release_pis(): the worker +only touches the kfifo and the pse_control list, not pcdev->pi. + +Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies") +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -1115,9 +1115,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe + void pse_controller_unregister(struct pse_controller_dev *pcdev) + { + pse_flush_pw_ds(pcdev); +- pse_release_pis(pcdev); + if (pcdev->irq) + disable_irq(pcdev->irq); ++ pse_release_pis(pcdev); + cancel_work_sync(&pcdev->ntf_work); + kfifo_free(&pcdev->ntf_fifo); + mutex_lock(&pse_list_mutex); diff --git a/target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch b/target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch new file mode 100644 index 0000000000..b11d493bac --- /dev/null +++ b/target/linux/generic/pending-6.18/895-00b-net-pse-pd-guard-against-freed-PI-data-on-regulator-disable.patch @@ -0,0 +1,62 @@ +From c4bd3afcf943491228e284cc6e68a145fcd07c2b Mon Sep 17 00:00:00 2001 +From: Carlo Szelinsky +Date: Sat, 23 May 2026 21:33:24 +0200 +Subject: [PATCH] net: pse-pd: guard against freed PI data on regulator disable + +PSE PI regulators are devm-registered inside pse_controller_register(), +which runs before devres_add() arms the controller's own release in +devm_pse_controller_register(). On driver detach, devres unwinds in LIFO +order, so pse_controller_unregister() runs first and frees pcdev->pi via +pse_release_pis(); the regulators are torn down afterwards. + +When regulator_unregister() flushes a pending disable, the regulator core +invokes pse_pi_disable(), which dereferences pcdev->pi[id] (directly and +via _pse_pi_disable() -> pse_pi_deallocate_pw_budget()). At that point the +PI array is already freed, so this is a use-after-free. + +pse_release_pis() now clears pcdev->pi after freeing it, and +pse_pi_disable() bails out under the lock when pcdev->pi is NULL, so a +late disable from the regulator core is a no-op once the controller has +been unregistered. + +Fixes: ffef61d6d273 ("net: pse-pd: Add support for budget evaluation strategies") +Signed-off-by: Carlo Szelinsky +--- + drivers/net/pse-pd/pse_core.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -145,6 +145,7 @@ static void pse_release_pis(struct pse_c + of_node_put(pcdev->pi[i].np); + } + kfree(pcdev->pi); ++ pcdev->pi = NULL; + } + + /** +@@ -702,15 +703,21 @@ static int pse_pi_enable(struct regulato + static int pse_pi_disable(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); +- struct pse_pi *pi; + int id, ret; + + id = rdev_get_id(rdev); +- pi = &pcdev->pi[id]; + mutex_lock(&pcdev->lock); ++ /* The controller may already be unregistered (pcdev->pi freed) by the ++ * time the regulator core flushes a deferred disable during ++ * regulator_unregister(). Bail out to avoid touching freed PI data. ++ */ ++ if (!pcdev->pi) { ++ mutex_unlock(&pcdev->lock); ++ return 0; ++ } + ret = _pse_pi_disable(pcdev, id); + if (!ret) +- pi->admin_state_enabled = 0; ++ pcdev->pi[id].admin_state_enabled = 0; + + mutex_unlock(&pcdev->lock); + return 0; diff --git a/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch b/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch index 0a6cbc9ee6..23676a0000 100644 --- a/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch +++ b/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch @@ -20,7 +20,7 @@ Tested-by: Jonas Jelonek --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c -@@ -1138,6 +1138,9 @@ int pse_controller_register(struct pse_c +@@ -1145,6 +1145,9 @@ int pse_controller_register(struct pse_c list_add(&pcdev->list, &pse_controller_list); mutex_unlock(&pse_list_mutex); @@ -30,7 +30,7 @@ Tested-by: Jonas Jelonek return 0; } EXPORT_SYMBOL_GPL(pse_controller_register); -@@ -1148,6 +1151,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe +@@ -1155,6 +1158,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe */ void pse_controller_unregister(struct pse_controller_dev *pcdev) { @@ -38,5 +38,5 @@ Tested-by: Jonas Jelonek + PSE_UNREGISTERED, pcdev); + pse_flush_pw_ds(pcdev); - pse_release_pis(pcdev); if (pcdev->irq) + disable_irq(pcdev->irq); diff --git a/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch b/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch index b5a5aa2009..a24c08dcae 100644 --- a/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch +++ b/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch @@ -380,7 +380,7 @@ Tested-by: Jonas Jelonek dev_err(sfp->dev, "phy_device_register failed: %pe\n", --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c -@@ -2016,3 +2016,17 @@ bool pse_has_c33(struct pse_control *pse +@@ -2028,3 +2028,17 @@ bool pse_has_c33(struct pse_control *pse return psec->pcdev->types & ETHTOOL_PSE_C33; } EXPORT_SYMBOL_GPL(pse_has_c33); From 7926c03b4da33677886872d16eaf6d5de82c22c8 Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 15:53:20 +0200 Subject: [PATCH 141/152] kernel: pse-pd: add poll helper and LED trigger support Backport the pending v6 poll-based event detection (devm_pse_poll_helper) and per-PI LED triggers via the notification path, plus the poll-interval-ms binding. Unblocks the hs104 poll path and PoE LED triggers. Pending upstream as [PATCH net-next v5 0/2] (reviewed; v6 pending, merge expected soon). Link: https://lore.kernel.org/all/20260429213224.1747410-1-github@szelinsky.de/ Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- ...ings-net-pse-pd-add-poll-interval-ms.patch | 30 ++ ...-net-pse-pd-add-devm_pse_poll_helper.patch | 330 ++++++++++++++++ ...2-net-pse-pd-add-LED-trigger-support.patch | 364 ++++++++++++++++++ 3 files changed, 724 insertions(+) create mode 100644 target/linux/generic/pending-6.18/897-00-dt-bindings-net-pse-pd-add-poll-interval-ms.patch create mode 100644 target/linux/generic/pending-6.18/897-01-net-pse-pd-add-devm_pse_poll_helper.patch create mode 100644 target/linux/generic/pending-6.18/897-02-net-pse-pd-add-LED-trigger-support.patch diff --git a/target/linux/generic/pending-6.18/897-00-dt-bindings-net-pse-pd-add-poll-interval-ms.patch b/target/linux/generic/pending-6.18/897-00-dt-bindings-net-pse-pd-add-poll-interval-ms.patch new file mode 100644 index 0000000000..3f64deafe8 --- /dev/null +++ b/target/linux/generic/pending-6.18/897-00-dt-bindings-net-pse-pd-add-poll-interval-ms.patch @@ -0,0 +1,30 @@ +From af1b9d9d0b357d14e54abff6ec9335e5fb426f75 Mon Sep 17 00:00:00 2001 +From: Carlo Szelinsky +Date: Sun, 29 Mar 2026 16:24:08 +0200 +Subject: dt-bindings: net: pse-pd: add poll-interval-ms property + +Add the optional poll-interval-ms property for PSE controllers that +use poll-based event detection instead of interrupts. Defaults to +500ms if not specified. + +Reviewed-by: Kory Maincent +Signed-off-by: Carlo Szelinsky + +--- +--- a/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml ++++ b/Documentation/devicetree/bindings/net/pse-pd/pse-controller.yaml +@@ -27,6 +27,14 @@ properties: + subnode. This property is deprecated, please use pse-pis instead. + enum: [0, 1] + ++ poll-interval-ms: ++ description: ++ Polling interval in milliseconds for PSE controllers using ++ poll-based event detection instead of interrupts. Used when the ++ controller lacks IRQ support or the IRQ line is not wired. ++ default: 500 ++ minimum: 50 ++ + pse-pis: + type: object + description: diff --git a/target/linux/generic/pending-6.18/897-01-net-pse-pd-add-devm_pse_poll_helper.patch b/target/linux/generic/pending-6.18/897-01-net-pse-pd-add-devm_pse_poll_helper.patch new file mode 100644 index 0000000000..d4f2559555 --- /dev/null +++ b/target/linux/generic/pending-6.18/897-01-net-pse-pd-add-devm_pse_poll_helper.patch @@ -0,0 +1,330 @@ +From 34551486030a0b5b3a1bac4bb293633f0e5e3e0e Mon Sep 17 00:00:00 2001 +From: Carlo Szelinsky +Date: Sun, 29 Mar 2026 16:26:08 +0200 +Subject: net: pse-pd: add devm_pse_poll_helper() + +Extract the common event handling loop from pse_isr() into a shared +pse_handle_events() function, and add a generic poll-based alternative +to the IRQ path for PSE controllers that lack interrupt support or +have IRQ lines not wired on the board. + +The new devm_pse_poll_helper() function sets up a delayed work that +periodically calls the driver's map_event callback to detect state +changes, feeding events into the existing ntf_fifo / pse_send_ntf_worker +notification pipeline. This reuses the same pse_irq_desc interface as +the IRQ path: the driver provides a map_event callback that populates +per-PI notification arrays. + +The poll worker uses system_freezable_wq to avoid running during system +suspend when the underlying hardware (e.g. I2C bus) may be inaccessible. + +devm_pse_poll_helper() only allocates the per-PI state and initializes +the delayed work; the work is armed by pse_controller_register() as the +very last step on success, gated on pcdev->polling. Drivers set up the +helper before registering the controller, so arming earlier would let +pse_poll_worker() acquire pcdev->lock and push events into the ntf_fifo +before they are initialized; and if probe failed after the helper but +before registration, the work would fire against freed devres memory. +Arming after a successful register closes both. Probe is a preemptible +process and can be outscheduled at any point, so this is reachable in +practice, not just in theory (e.g. a single-core SoC with the pse-pd +core built as a module and a squashfs rootfs makes the boot heavy +enough to lose the race). + +The poll work is cancelled in pse_controller_unregister() before +pse_release_pis() frees pcdev->pi, next to the IRQ disable, so neither +the poll worker nor the IRQ handler can dereference the freed PI array. + +The notifs_mask is allocated as a real bitmap via devm_bitmap_zalloc() +and passed to pse_handle_events() and the driver's map_event callback +as a pointer (mirroring the pattern from commit 5099807f335c +("net: pse-pd: fix out-of-bounds bitmap access in pse_isr() on 32-bit")) +so for_each_set_bit() does not read past a single unsigned long when +nr_lines > BITS_PER_LONG. + +The poll interval defaults to 500ms, balancing responsiveness against +bus load (e.g. I2C). + +Signed-off-by: Carlo Szelinsky + +--- +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -15,10 +15,17 @@ + #include + #include + #include ++#include + #include + + #define PSE_PW_D_LIMIT INT_MAX + ++/* Default poll interval for controllers without IRQ support. ++ * 500ms provides a reasonable trade-off between responsiveness ++ * (event detection, PD detection) and I2C bus utilization. ++ */ ++#define PSE_DEFAULT_POLL_INTERVAL_MS 500 ++ + static DEFINE_MUTEX(pse_list_mutex); + static LIST_HEAD(pse_controller_list); + static DEFINE_XARRAY_ALLOC(pse_pw_d_map); +@@ -1148,6 +1155,15 @@ int pse_controller_register(struct pse_c + blocking_notifier_call_chain(&pse_controller_notifier, + PSE_REGISTERED, pcdev); + ++ /* Arm the poll work last, once everything is initialized and ++ * registration has succeeded. devm_pse_poll_helper() only sets up the ++ * delayed work; queuing it here ensures pse_poll_worker() never runs ++ * against a half-initialized controller or one whose probe later fails. ++ */ ++ if (pcdev->polling) ++ queue_delayed_work(system_freezable_wq, &pcdev->poll_work, ++ msecs_to_jiffies(pcdev->poll_interval_ms)); ++ + return 0; + } + EXPORT_SYMBOL_GPL(pse_controller_register); +@@ -1164,6 +1180,8 @@ void pse_controller_unregister(struct ps + pse_flush_pw_ds(pcdev); + if (pcdev->irq) + disable_irq(pcdev->irq); ++ if (pcdev->polling) ++ cancel_delayed_work_sync(&pcdev->poll_work); + pse_release_pis(pcdev); + cancel_work_sync(&pcdev->ntf_work); + kfifo_free(&pcdev->ntf_fifo); +@@ -1286,66 +1304,104 @@ static int pse_set_config_isr(struct pse + } + + /** +- * pse_isr - IRQ handler for PSE +- * @irq: irq number +- * @data: pointer to user interrupt structure ++ * pse_handle_events - Process PSE events for all PIs ++ * @pcdev: a pointer to the PSE controller device ++ * @notifs: per-PI notification array ++ * @notifs_mask: bitmap of PIs with events (sized for pcdev->nr_lines) + * +- * Return: irqreturn_t - status of IRQ ++ * Common event handling shared between IRQ and poll paths. ++ * Caller must hold pcdev->lock. + */ +-static irqreturn_t pse_isr(int irq, void *data) ++static void pse_handle_events(struct pse_controller_dev *pcdev, ++ unsigned long *notifs, ++ unsigned long *notifs_mask) + { +- struct pse_controller_dev *pcdev; +- struct pse_irq_desc *desc; +- struct pse_irq *h = data; +- int ret, i; +- +- desc = &h->desc; +- pcdev = h->pcdev; +- +- /* Clear notifs mask */ +- memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); +- bitmap_zero(h->notifs_mask, pcdev->nr_lines); +- mutex_lock(&pcdev->lock); +- ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask); +- if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) { +- mutex_unlock(&pcdev->lock); +- return IRQ_NONE; +- } ++ int i; + +- for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) { +- unsigned long notifs, rnotifs; ++ for_each_set_bit(i, notifs_mask, pcdev->nr_lines) { ++ unsigned long pi_notifs, rnotifs; + struct pse_ntf ntf = {}; ++ int ret; + + /* Do nothing PI not described */ + if (!pcdev->pi[i].rdev) + continue; + +- notifs = h->notifs[i]; ++ pi_notifs = notifs[i]; + if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[i].pw_d)) { +- ret = pse_set_config_isr(pcdev, i, notifs); ++ ret = pse_set_config_isr(pcdev, i, pi_notifs); + if (ret) +- notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR; ++ pi_notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR; + } + +- dev_dbg(h->pcdev->dev, +- "Sending PSE notification EVT 0x%lx\n", notifs); ++ dev_dbg(pcdev->dev, ++ "Sending PSE notification EVT 0x%lx\n", pi_notifs); + +- ntf.notifs = notifs; ++ ntf.notifs = pi_notifs; + ntf.id = i; + kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1, + &pcdev->ntf_fifo_lock); + schedule_work(&pcdev->ntf_work); + +- rnotifs = pse_to_regulator_notifs(notifs); ++ rnotifs = pse_to_regulator_notifs(pi_notifs); + regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs, + NULL); + } ++} + ++/** ++ * pse_isr - IRQ handler for PSE ++ * @irq: irq number ++ * @data: pointer to user interrupt structure ++ * ++ * Return: irqreturn_t - status of IRQ ++ */ ++static irqreturn_t pse_isr(int irq, void *data) ++{ ++ struct pse_controller_dev *pcdev; ++ struct pse_irq *h = data; ++ int ret; ++ ++ pcdev = h->pcdev; ++ ++ /* Clear notifs mask */ ++ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); ++ bitmap_zero(h->notifs_mask, pcdev->nr_lines); ++ mutex_lock(&pcdev->lock); ++ ret = h->desc.map_event(irq, pcdev, h->notifs, h->notifs_mask); ++ if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) { ++ mutex_unlock(&pcdev->lock); ++ return IRQ_NONE; ++ } ++ ++ pse_handle_events(pcdev, h->notifs, h->notifs_mask); + mutex_unlock(&pcdev->lock); + + return IRQ_HANDLED; + } + ++static void pse_poll_worker(struct work_struct *work) ++{ ++ struct pse_controller_dev *pcdev = ++ container_of(work, struct pse_controller_dev, ++ poll_work.work); ++ int ret; ++ ++ memset(pcdev->poll_notifs, 0, ++ pcdev->nr_lines * sizeof(*pcdev->poll_notifs)); ++ bitmap_zero(pcdev->poll_notifs_mask, pcdev->nr_lines); ++ mutex_lock(&pcdev->lock); ++ ret = pcdev->poll_desc.map_event(0, pcdev, pcdev->poll_notifs, ++ pcdev->poll_notifs_mask); ++ if (!ret && !bitmap_empty(pcdev->poll_notifs_mask, pcdev->nr_lines)) ++ pse_handle_events(pcdev, pcdev->poll_notifs, ++ pcdev->poll_notifs_mask); ++ mutex_unlock(&pcdev->lock); ++ ++ queue_delayed_work(system_freezable_wq, &pcdev->poll_work, ++ msecs_to_jiffies(pcdev->poll_interval_ms)); ++} ++ + /** + * devm_pse_irq_helper - Register IRQ based PSE event notifier + * @pcdev: a pointer to the PSE +@@ -1403,6 +1459,61 @@ int devm_pse_irq_helper(struct pse_contr + } + EXPORT_SYMBOL_GPL(devm_pse_irq_helper); + ++/** ++ * devm_pse_poll_helper - Register poll-based PSE event notifier ++ * @pcdev: a pointer to the PSE controller device ++ * @d: PSE event description (uses same pse_irq_desc as IRQ path) ++ * ++ * For PSE controllers without IRQ support or with IRQ not wired. Sets ++ * up a delayed work that periodically calls the driver's map_event ++ * callback to detect state changes, feeding events into the standard ++ * notification pipeline. ++ * ++ * The poll worker uses system_freezable_wq to ensure it does not run ++ * during system suspend while the hardware may be inaccessible. ++ * ++ * Must be called before devm_pse_controller_register(): this helper only ++ * sets up the delayed work, which pse_controller_register() arms as its ++ * last step on success. Calling it afterwards leaves the work unarmed and ++ * polling never starts. ++ * ++ * Return: 0 on success and errno on failure ++ */ ++int devm_pse_poll_helper(struct pse_controller_dev *pcdev, ++ const struct pse_irq_desc *d) ++{ ++ struct device *dev = pcdev->dev; ++ ++ if (!d || !d->map_event || !d->name) ++ return -EINVAL; ++ ++ pcdev->poll_desc = *d; ++ pcdev->poll_notifs = devm_kcalloc(dev, pcdev->nr_lines, ++ sizeof(*pcdev->poll_notifs), ++ GFP_KERNEL); ++ if (!pcdev->poll_notifs) ++ return -ENOMEM; ++ ++ pcdev->poll_notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines, ++ GFP_KERNEL); ++ if (!pcdev->poll_notifs_mask) ++ return -ENOMEM; ++ ++ if (!pcdev->poll_interval_ms) ++ pcdev->poll_interval_ms = PSE_DEFAULT_POLL_INTERVAL_MS; ++ ++ INIT_DELAYED_WORK(&pcdev->poll_work, pse_poll_worker); ++ pcdev->polling = true; ++ ++ /* The poll work is armed by pse_controller_register() as the last ++ * step on success, so it never runs against a half-initialized or ++ * never-registered controller. ++ */ ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(devm_pse_poll_helper); ++ + /* PSE control section */ + + static void __pse_control_release(struct kref *kref) +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -293,6 +293,12 @@ struct pse_ntf { + * @pi: table of PSE PIs described in this controller device + * @no_of_pse_pi: flag set if the pse_pis devicetree node is not used + * @irq: PSE interrupt ++ * @polling: flag indicating poll-based event detection is active ++ * @poll_interval_ms: poll interval in milliseconds ++ * @poll_work: delayed work for poll-based event detection ++ * @poll_desc: copy of the driver's event descriptor for polling ++ * @poll_notifs: per-PI notification scratch space for poll worker ++ * @poll_notifs_mask: bitmap of PIs with events for poll worker + * @pis_prio_max: Maximum value allowed for the PSE PIs priority + * @supp_budget_eval_strategies: budget evaluation strategies supported + * by the PSE +@@ -313,6 +319,12 @@ struct pse_controller_dev { + struct pse_pi *pi; + bool no_of_pse_pi; + int irq; ++ bool polling; ++ unsigned int poll_interval_ms; ++ struct delayed_work poll_work; ++ struct pse_irq_desc poll_desc; ++ unsigned long *poll_notifs; ++ unsigned long *poll_notifs_mask; + unsigned int pis_prio_max; + u32 supp_budget_eval_strategies; + struct work_struct ntf_work; +@@ -364,6 +376,8 @@ int devm_pse_controller_register(struct + struct pse_controller_dev *pcdev); + int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq, + int irq_flags, const struct pse_irq_desc *d); ++int devm_pse_poll_helper(struct pse_controller_dev *pcdev, ++ const struct pse_irq_desc *d); + + struct pse_control *of_pse_control_get(struct device_node *node, + struct phy_device *phydev); diff --git a/target/linux/generic/pending-6.18/897-02-net-pse-pd-add-LED-trigger-support.patch b/target/linux/generic/pending-6.18/897-02-net-pse-pd-add-LED-trigger-support.patch new file mode 100644 index 0000000000..751e0a3a7b --- /dev/null +++ b/target/linux/generic/pending-6.18/897-02-net-pse-pd-add-LED-trigger-support.patch @@ -0,0 +1,364 @@ +From 547266c0e2d887b338dee1d6e6693e987b2868cd Mon Sep 17 00:00:00 2001 +From: Carlo Szelinsky +Date: Sun, 29 Mar 2026 16:26:49 +0200 +Subject: net: pse-pd: add LED trigger support via notification path + +Add per-PI "delivering" and "enabled" LED triggers to the PSE core +subsystem. LED state is updated from the shared pse_handle_events() +function whenever the IRQ or poll path detects a state change, as well +as from the regulator enable/disable paths so that host-initiated +admin state changes via ethtool are immediately reflected. + +Each trigger registers an .activate callback that syncs a freshly-bound +LED to the cached state, so an LED bound after pse_controller_register() +(e.g. via sysfs) does not stay dark until the next hardware event. + +The triggers are registered before the PI regulators are exposed in +pse_controller_register(). of_load_pse_pis() has already populated +pi[]/pi[i].np by then, which is all the trigger loop needs, and +registering first closes the window where a consumer calling +regulator_enable() as soon as the regulators appear could fire +led_trigger_event() on a trigger whose led_cdevs list head is not yet +initialized. A registration failure is fatal to the probe, matching the +other allocations in the function. + +The post-registration initial-state pass is run under pcdev->lock, +matching pse_led_update()'s documented locking contract and avoiding +races with concurrent regulator_enable() paths that share last_delivering +/ last_enabled and the hardware ops. pse_handle_events() gains a +lockdep_assert_held() to make that contract explicit and catch any +future caller that forgets the lock. + +pse_controller_unregister() clears pcdev->pi_led_trigs before returning, +so a deferred regulator disable flushed during the regulator core's +later teardown finds pse_led_update() short-circuited rather than +walking soon-to-be-freed trigger state. + +Signed-off-by: Carlo Szelinsky + +--- +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -704,6 +705,168 @@ static int _pse_pi_delivery_power_sw_pw_ + return 0; + } + ++#if IS_ENABLED(CONFIG_LEDS_TRIGGERS) ++/** ++ * pse_pi_get_states - Fetch current delivering/enabled state for a PI ++ * @pcdev: PSE controller device ++ * @id: PI index ++ * @delivering: out, set to true if PI is currently delivering power ++ * @enabled: out, set to true if PI is administratively enabled ++ * ++ * Queries hardware via the controller ops. Caller must hold pcdev->lock. ++ * ++ * Return: 0 on success, negative errno on failure. ++ */ ++static int pse_pi_get_states(struct pse_controller_dev *pcdev, int id, ++ bool *delivering, bool *enabled) ++{ ++ struct pse_pw_status pw_status = {}; ++ struct pse_admin_state admin_state = {}; ++ int ret; ++ ++ ret = pcdev->ops->pi_get_pw_status(pcdev, id, &pw_status); ++ if (ret) ++ return ret; ++ ret = pcdev->ops->pi_get_admin_state(pcdev, id, &admin_state); ++ if (ret) ++ return ret; ++ ++ *delivering = pw_status.c33_pw_status == ++ ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING || ++ pw_status.podl_pw_status == ++ ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING; ++ *enabled = admin_state.c33_admin_state == ++ ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED || ++ admin_state.podl_admin_state == ++ ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED; ++ ++ return 0; ++} ++ ++/** ++ * pse_led_update - Update LED triggers for a PI based on current state ++ * @pcdev: PSE controller device ++ * @id: PI index ++ * ++ * Queries the current power status and admin state of the PI and ++ * fires LED trigger events on state changes. Called from the ++ * notification path and the regulator enable/disable paths. ++ * ++ * Must be called with pcdev->lock held. ++ */ ++static void pse_led_update(struct pse_controller_dev *pcdev, int id) ++{ ++ struct pse_pi_led_triggers *trigs; ++ bool delivering, enabled; ++ ++ if (!pcdev->pi_led_trigs) ++ return; ++ ++ trigs = &pcdev->pi_led_trigs[id]; ++ if (!trigs->delivering.name) ++ return; ++ ++ if (pse_pi_get_states(pcdev, id, &delivering, &enabled)) ++ return; ++ ++ if (trigs->last_delivering != delivering) { ++ trigs->last_delivering = delivering; ++ led_trigger_event(&trigs->delivering, ++ delivering ? LED_FULL : LED_OFF); ++ } ++ ++ if (trigs->last_enabled != enabled) { ++ trigs->last_enabled = enabled; ++ led_trigger_event(&trigs->enabled, ++ enabled ? LED_FULL : LED_OFF); ++ } ++} ++ ++/* Sync a freshly-bound LED to the cached trigger state. Without these ++ * .activate callbacks, an LED bound to the trigger after ++ * pse_controller_register() (e.g. via sysfs) would stay dark until the ++ * next hardware event toggles state. ++ */ ++static int pse_led_delivering_activate(struct led_classdev *led_cdev) ++{ ++ struct pse_pi_led_triggers *trigs = ++ container_of(led_cdev->trigger, struct pse_pi_led_triggers, ++ delivering); ++ ++ led_set_brightness(led_cdev, ++ trigs->last_delivering ? LED_FULL : LED_OFF); ++ return 0; ++} ++ ++static int pse_led_enabled_activate(struct led_classdev *led_cdev) ++{ ++ struct pse_pi_led_triggers *trigs = ++ container_of(led_cdev->trigger, struct pse_pi_led_triggers, ++ enabled); ++ ++ led_set_brightness(led_cdev, ++ trigs->last_enabled ? LED_FULL : LED_OFF); ++ return 0; ++} ++ ++static int pse_led_triggers_register(struct pse_controller_dev *pcdev) ++{ ++ struct device *dev = pcdev->dev; ++ const char *dev_id; ++ int i, ret; ++ ++ dev_id = dev_name(dev); ++ ++ pcdev->pi_led_trigs = devm_kcalloc(dev, pcdev->nr_lines, ++ sizeof(*pcdev->pi_led_trigs), ++ GFP_KERNEL); ++ if (!pcdev->pi_led_trigs) ++ return -ENOMEM; ++ ++ for (i = 0; i < pcdev->nr_lines; i++) { ++ struct pse_pi_led_triggers *trigs = &pcdev->pi_led_trigs[i]; ++ ++ /* Skip PIs not described in device tree */ ++ if (!pcdev->no_of_pse_pi && !pcdev->pi[i].np) ++ continue; ++ ++ trigs->delivering.name = devm_kasprintf(dev, GFP_KERNEL, ++ "pse-%s:port%d:delivering", ++ dev_id, i); ++ if (!trigs->delivering.name) ++ return -ENOMEM; ++ trigs->delivering.activate = pse_led_delivering_activate; ++ ++ ret = devm_led_trigger_register(dev, &trigs->delivering); ++ if (ret) { ++ trigs->delivering.name = NULL; ++ return ret; ++ } ++ ++ trigs->enabled.name = devm_kasprintf(dev, GFP_KERNEL, ++ "pse-%s:port%d:enabled", ++ dev_id, i); ++ if (!trigs->enabled.name) ++ return -ENOMEM; ++ trigs->enabled.activate = pse_led_enabled_activate; ++ ++ ret = devm_led_trigger_register(dev, &trigs->enabled); ++ if (ret) { ++ trigs->enabled.name = NULL; ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++#else ++static inline void pse_led_update(struct pse_controller_dev *pcdev, int id) {} ++static int pse_led_triggers_register(struct pse_controller_dev *pcdev) ++{ ++ return 0; ++} ++#endif /* CONFIG_LEDS_TRIGGERS */ ++ + static int pse_pi_enable(struct regulator_dev *rdev) + { + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); +@@ -729,6 +892,7 @@ static int pse_pi_enable(struct regulato + pcdev->pi[id].admin_state_enabled = 1; + ret = 0; + } ++ pse_led_update(pcdev, id); + mutex_unlock(&pcdev->lock); + return ret; + } +@@ -736,6 +900,7 @@ static int pse_pi_enable(struct regulato + ret = ops->pi_enable(pcdev, id); + if (!ret) + pcdev->pi[id].admin_state_enabled = 1; ++ pse_led_update(pcdev, id); + mutex_unlock(&pcdev->lock); + + return ret; +@@ -759,6 +924,7 @@ static int pse_pi_disable(struct regulat + ret = _pse_pi_disable(pcdev, id); + if (!ret) + pcdev->pi[id].admin_state_enabled = 0; ++ pse_led_update(pcdev, id); + + mutex_unlock(&pcdev->lock); + return 0; +@@ -1119,6 +1285,17 @@ int pse_controller_register(struct pse_c + return ret; + } + ++ /* Register the LED triggers before exposing the regulators. The ++ * trigger loop only needs pi[]/pi[i].np, which of_load_pse_pis() ++ * has already populated. Registering first means a consumer that ++ * calls regulator_enable() as soon as the regulators appear cannot ++ * race against a half-initialized led_trigger (whose led_cdevs list ++ * head is not yet set up). ++ */ ++ ret = pse_led_triggers_register(pcdev); ++ if (ret) ++ return ret; ++ + /* Each regulator name len is pcdev dev name + 7 char + + * int max digit number (10) + 1 + */ +@@ -1148,6 +1325,19 @@ int pse_controller_register(struct pse_c + if (ret) + return ret; + ++ /* Query initial LED state for all PIs so already-active ports ++ * are reflected immediately without waiting for a hardware event. ++ * Hold pcdev->lock: regulators are already exposed and a ++ * concurrent regulator_enable() would race on the hw callbacks ++ * and on last_delivering / last_enabled. ++ */ ++ mutex_lock(&pcdev->lock); ++ for (i = 0; i < pcdev->nr_lines; i++) { ++ if (pcdev->no_of_pse_pi || pcdev->pi[i].np) ++ pse_led_update(pcdev, i); ++ } ++ mutex_unlock(&pcdev->lock); ++ + mutex_lock(&pse_list_mutex); + list_add(&pcdev->list, &pse_controller_list); + mutex_unlock(&pse_list_mutex); +@@ -1183,6 +1373,12 @@ void pse_controller_unregister(struct ps + if (pcdev->polling) + cancel_delayed_work_sync(&pcdev->poll_work); + pse_release_pis(pcdev); ++ /* The pi_led_trigs array is devm-allocated and freed only after this ++ * function returns. Clear the pointer now so a deferred regulator ++ * disable flushed during regulator_unregister() makes pse_led_update() ++ * short-circuit instead of walking soon-to-be-freed trigger state. ++ */ ++ pcdev->pi_led_trigs = NULL; + cancel_work_sync(&pcdev->ntf_work); + kfifo_free(&pcdev->ntf_fifo); + mutex_lock(&pse_list_mutex); +@@ -1318,12 +1514,21 @@ static void pse_handle_events(struct pse + { + int i; + ++ lockdep_assert_held(&pcdev->lock); ++ + for_each_set_bit(i, notifs_mask, pcdev->nr_lines) { + unsigned long pi_notifs, rnotifs; + struct pse_ntf ntf = {}; + int ret; + +- /* Do nothing PI not described */ ++ /* Update LEDs for described PIs regardless of consumer state. ++ * LED triggers are registered at controller init, before any ++ * PHY claims a PSE control, so rdev may still be NULL here. ++ */ ++ if (pcdev->no_of_pse_pi || pcdev->pi[i].np) ++ pse_led_update(pcdev, i); ++ ++ /* Skip regulator/netlink path for PIs without consumers */ + if (!pcdev->pi[i].rdev) + continue; + +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + + /* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */ +@@ -268,6 +269,21 @@ struct pse_pi { + }; + + /** ++ * struct pse_pi_led_triggers - LED trigger state for a PSE PI ++ * ++ * @delivering: LED trigger for power delivering state ++ * @enabled: LED trigger for admin enabled state ++ * @last_delivering: cached delivering state for change detection ++ * @last_enabled: cached enabled state for change detection ++ */ ++struct pse_pi_led_triggers { ++ struct led_trigger delivering; ++ struct led_trigger enabled; ++ bool last_delivering; ++ bool last_enabled; ++}; ++ ++/** + * struct pse_ntf - PSE notification element + * + * @id: ID of the PSE control +@@ -305,6 +321,7 @@ struct pse_ntf { + * @ntf_work: workqueue for PSE notification management + * @ntf_fifo: PSE notifications FIFO + * @ntf_fifo_lock: protect @ntf_fifo writer ++ * @pi_led_trigs: per-PI LED trigger state array + */ + struct pse_controller_dev { + const struct pse_controller_ops *ops; +@@ -330,6 +347,7 @@ struct pse_controller_dev { + struct work_struct ntf_work; + DECLARE_KFIFO_PTR(ntf_fifo, struct pse_ntf); + spinlock_t ntf_fifo_lock; /* Protect @ntf_fifo writer */ ++ struct pse_pi_led_triggers *pi_led_trigs; + }; + + /** From d3c5d345d214013cf1c25852ddf89f3be2a42156 Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 15:38:39 +0200 Subject: [PATCH 142/152] kernel: pse-pd: add Hasivo HS104 PoE PSE controller driver Direct-I2C driver for the HS104 PSE chip (unlike the MCU-fronted realtek-pse-mcu family). Packaged as kmod-pse-hasivo-hs104. This driver will be submitted upstream once the pending PSE-core patches it builds on (above) have landed. Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- package/kernel/linux/modules/pse-pd.mk | 18 + ...8-net-pse-pd-add-hasivo-hs104-driver.patch | 561 ++++++++++++++++++ 2 files changed, 579 insertions(+) create mode 100644 target/linux/realtek/patches-6.18/818-net-pse-pd-add-hasivo-hs104-driver.patch diff --git a/package/kernel/linux/modules/pse-pd.mk b/package/kernel/linux/modules/pse-pd.mk index 5190d6ebf9..95294fde0f 100644 --- a/package/kernel/linux/modules/pse-pd.mk +++ b/package/kernel/linux/modules/pse-pd.mk @@ -143,3 +143,21 @@ define KernelPackage/pse-realtek-mcu-uart/description endef $(eval $(call KernelPackage,pse-realtek-mcu-uart)) + +define KernelPackage/pse-hasivo-hs104 + SUBMENU:=$(PSE_MENU) + TITLE:=Hasivo HS104 PSE controller support + KCONFIG:=CONFIG_PSE_HASIVO_HS104 + DEPENDS:=+kmod-i2c-core + FILES:=$(LINUX_DIR)/drivers/net/pse-pd/hasivo_hs104.ko + AUTOLOAD:=$(call AutoProbe,hasivo_hs104) + $(call AddDepends/pse-pd) +endef + +define KernelPackage/pse-hasivo-hs104/description + Kernel module for the Hasivo HS104 PoE PSE controller chips. + Supports the HS104PTI/HS104PBI single-chip PoE PSE controllers + managing 4 delivery channels for 802.3af/at/bt power over I2C. +endef + +$(eval $(call KernelPackage,pse-hasivo-hs104)) diff --git a/target/linux/realtek/patches-6.18/818-net-pse-pd-add-hasivo-hs104-driver.patch b/target/linux/realtek/patches-6.18/818-net-pse-pd-add-hasivo-hs104-driver.patch new file mode 100644 index 0000000000..cf6a22c99c --- /dev/null +++ b/target/linux/realtek/patches-6.18/818-net-pse-pd-add-hasivo-hs104-driver.patch @@ -0,0 +1,561 @@ +From 5550ceafb9bff5f0d362d402b7f274ded474cbf4 Mon Sep 17 00:00:00 2001 +From: Bevan Weiss +Date: Mon, 2 Mar 2026 23:31:43 +0100 +Subject: [PATCH] net: pse-pd: add Hasivo HS104 PoE PSE controller driver + +The HS104PTI/HS104PBI are single-chip PoE PSE controllers that manage 4 +power delivery channels and are accessed directly from the host over I2C. +They are found on Hasivo managed PoE switches (e.g. S1100WP-8GT-SE, which +carries two of them). Unlike the MCU-fronted realtek-pse-mcu family, the +PSE silicon here is driven directly, so it needs its own controller +driver. + +Add the driver, its device-tree binding and the Kconfig/Makefile entries. + +Signed-off-by: Bevan Weiss +Co-developed-by: Carlo Szelinsky +Signed-off-by: Carlo Szelinsky +--- +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/pse-pd/hasivo,hs104.yaml +@@ -0,0 +1,64 @@ ++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++%YAML 1.2 ++--- ++$id: http://devicetree.org/schemas/net/pse-pd/hasivo,hs104.yaml# ++$schema: http://devicetree.org/meta-schemas/core.yaml# ++ ++title: Hasivo HS104 Power Sourcing Equipment controller ++ ++maintainers: ++ - Bevan Weiss ++ - Carlo Szelinsky ++ ++description: | ++ The HS104PTI/HS104PBI are single-chip PoE PSE controllers managing 4 ++ delivery channels, allowing them to supply 4 ports of 802.3af/at/bt power. ++ The HS104PTI can have 1x 802.3bt port and 3x 802.3at ports. ++ The HS104PBI can have 4x 802.3bt ports. ++ ++allOf: ++ - $ref: pse-controller.yaml# ++ ++properties: ++ compatible: ++ enum: ++ - hasivo,hs104 ++ - hasivo,hs104pti ++ - hasivo,hs104pbi ++ ++ reg: ++ maxItems: 1 ++ ++ pse-pis: ++ patternProperties: ++ '^pse-pi@[0-3]$': ++ type: object ++ ++unevaluatedProperties: false ++ ++required: ++ - compatible ++ - reg ++ - pse-pis ++ ++examples: ++ - | ++ i2c { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ethernet-pse@d { ++ compatible = "hasivo,hs104"; ++ reg = <0x0d>; ++ ++ pse-pis { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pse-pi@0 { ++ reg = <0>; ++ #pse-cells = <0>; ++ }; ++ }; ++ }; ++ }; +--- a/drivers/net/pse-pd/Kconfig ++++ b/drivers/net/pse-pd/Kconfig +@@ -80,4 +80,12 @@ config PSE_TPS23881 + + To compile this driver as a module, choose M here: the + module will be called tps23881. ++ ++config PSE_HASIVO_HS104 ++ tristate "Hasivo HS104 PoE PSE Controller" ++ depends on I2C ++ help ++ Support for the Hasivo HS104 PoE PSE Controller chip. ++ This driver allows control and monitoring of PoE ports via I2C. ++ + endif +--- a/drivers/net/pse-pd/Makefile ++++ b/drivers/net/pse-pd/Makefile +@@ -10,3 +10,4 @@ obj-$(CONFIG_PSE_REGULATOR) += pse_regul + obj-$(CONFIG_PSE_PD692X0) += pd692x0.o + obj-$(CONFIG_PSE_SI3474) += si3474.o + obj-$(CONFIG_PSE_TPS23881) += tps23881.o ++obj-$(CONFIG_PSE_HASIVO_HS104) += hasivo_hs104.o +--- /dev/null ++++ b/drivers/net/pse-pd/hasivo_hs104.c +@@ -0,0 +1,451 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Hasivo HS104 PoE PSE controller driver ++ * ++ * The HS104PTI/HS104PBI are single-chip PoE PSE controllers managing 4 ++ * delivery channels, allowing them to supply 4 ports of 802.3af/at/bt power. ++ * The HS104PTI can have 1x 802.3bt port and 3x 802.3at ports. ++ * The HS104PBI can have 4x 802.3bt ports. ++ * ++ * Copyright (c) 2025 Bevan Weiss ++ * Copyright (c) 2026 Carlo Szelinsky ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define HS104_MAX_PORTS 4 ++ ++/* Register map */ ++#define HS104_REG_PW_STATUS 0x01 /* Power delivery status */ ++#define HS104_REG_INPUT_V 0x02 /* Input voltage, 16-bit BE, 10mV */ ++#define HS104_REG_PORT0_I 0x04 /* Port current, 16-bit BE, 1mA (not yet implemented) */ ++#define HS104_REG_DEVID 0x0C /* Device ID */ ++#define HS104_DEVICE_ID 0x91 ++#define HS104_REG_PORT0_CLASS 0x0D /* Port power class */ ++#define HS104_REG_PW_EN 0x14 /* Port enable control */ ++#define HS104_EXECUTE 0x40 /* Execute bit for writes */ ++#define HS104_REG_PROTOCOL 0x19 /* Protocol per port (2 bits each) */ ++#define HS104_PROTO_MASK 0x3 ++#define HS104_PROTO_BT 0 /* 802.3bt - 60W */ ++#define HS104_PROTO_HIPO 1 /* Hi-PoE - 90W */ ++#define HS104_PROTO_AT 2 /* 802.3at - 30W */ ++#define HS104_PROTO_AF 3 /* 802.3af - 15.4W */ ++#define HS104_REG_TOTAL_POWER 0x1D /* Total power, 16-bit BE, 10mW (not yet implemented) */ ++#define HS104_REG_PORT0_POWER 0x21 /* Port power, 16-bit BE, 10mW */ ++ ++/* ++ * BIT-field registers (PW_EN, PW_STATUS) use reversed bit ordering ++ * relative to sequential PORT registers (PORT0_POWER, PORT0_CLASS). ++ * Map PSE PI index to the hardware bit position. ++ */ ++#define HS104_PORT_BIT(id) (0x08 >> (id)) ++ ++/* Power limits in mW */ ++#define HS104_PW_AF 15400 ++#define HS104_PW_AT 30000 ++#define HS104_PW_BT 60000 ++#define HS104_PW_HIPO 90000 ++ ++/* Unit conversion steps */ ++#define HS104_UV_STEP 10000 /* 10mV -> uV */ ++#define HS104_UA_STEP 1000 /* 1mA -> uA */ ++#define HS104_MW_STEP 10 /* 10mW -> mW */ ++ ++struct hs104_priv { ++ struct regmap *regmap; ++ struct pse_controller_dev pcdev; ++ unsigned int last_pw_status; ++}; ++ ++static inline struct hs104_priv *to_hs104(struct pse_controller_dev *pcdev) ++{ ++ return container_of(pcdev, struct hs104_priv, pcdev); ++} ++ ++/* Read 16-bit big-endian register and apply unit conversion */ ++static int hs104_read_be16(struct hs104_priv *priv, unsigned int reg, ++ unsigned int step) ++{ ++ __be16 val; ++ int ret; ++ ++ ret = regmap_bulk_read(priv->regmap, reg, &val, sizeof(val)); ++ if (ret) ++ return ret; ++ ++ /* Hardware uses 14-bit data field, upper 2 bits are reserved */ ++ return (be16_to_cpu(val) & 0x3fff) * step; ++} ++ ++/* Convert protocol code to power limit in mW */ ++static int hs104_proto_to_mw(unsigned int proto) ++{ ++ switch (proto) { ++ case HS104_PROTO_AF: return HS104_PW_AF; ++ case HS104_PROTO_AT: return HS104_PW_AT; ++ case HS104_PROTO_BT: return HS104_PW_BT; ++ case HS104_PROTO_HIPO: return HS104_PW_HIPO; ++ default: return 0; ++ } ++} ++ ++/* PSE controller operations */ ++ ++static int hs104_pi_enable(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &val); ++ if (ret) ++ return ret; ++ ++ val = (val | HS104_PORT_BIT(id)) | HS104_EXECUTE; ++ ++ dev_dbg(pcdev->dev, "PI %d: enable (0x%02x)\n", id, val); ++ return regmap_write(priv->regmap, HS104_REG_PW_EN, val); ++} ++ ++static int hs104_pi_disable(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &val); ++ if (ret) ++ return ret; ++ ++ val = (val & ~HS104_PORT_BIT(id)) | HS104_EXECUTE; ++ ++ dev_dbg(pcdev->dev, "PI %d: disable (0x%02x)\n", id, val); ++ return regmap_write(priv->regmap, HS104_REG_PW_EN, val); ++} ++ ++static int hs104_pi_get_voltage(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ ++ /* Input voltage is shared across all PIs */ ++ return hs104_read_be16(priv, HS104_REG_INPUT_V, HS104_UV_STEP); ++} ++ ++static int hs104_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val, proto; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PROTOCOL, &val); ++ if (ret) ++ return ret; ++ ++ proto = (val >> (id * 2)) & HS104_PROTO_MASK; ++ return hs104_proto_to_mw(proto) ?: -ENODATA; ++} ++ ++static int hs104_pi_set_pw_limit(struct pse_controller_dev *pcdev, ++ int id, int max_mw) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int proto, mask; ++ ++ if (max_mw <= HS104_PW_AF) ++ proto = HS104_PROTO_AF; ++ else if (max_mw <= HS104_PW_AT) ++ proto = HS104_PROTO_AT; ++ else if (max_mw <= HS104_PW_BT) ++ proto = HS104_PROTO_BT; ++ else if (max_mw <= HS104_PW_HIPO) ++ proto = HS104_PROTO_HIPO; ++ else ++ return -EINVAL; ++ ++ mask = HS104_PROTO_MASK << (id * 2); ++ proto <<= (id * 2); ++ ++ return regmap_update_bits(priv->regmap, HS104_REG_PROTOCOL, mask, proto); ++} ++ ++static int hs104_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, ++ struct pse_admin_state *admin_state) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &val); ++ if (ret) { ++ admin_state->c33_admin_state = ++ ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN; ++ return ret; ++ } ++ ++ if (val & HS104_PORT_BIT(id)) ++ admin_state->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; ++ else ++ admin_state->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; ++ ++ return 0; ++} ++ ++static int hs104_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, ++ struct pse_pw_status *pw_status) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int status, pw_en; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_STATUS, &status); ++ if (ret) { ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN; ++ return ret; ++ } ++ ++ if (status & HS104_PORT_BIT(id)) { ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; ++ return 0; ++ } ++ ++ /* ++ * Not delivering: tell an administratively disabled port apart from ++ * one that is enabled but has no valid PD attached yet (searching). ++ */ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_EN, &pw_en); ++ if (ret) { ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN; ++ return ret; ++ } ++ ++ if (pw_en & HS104_PORT_BIT(id)) ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING; ++ else ++ pw_status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; ++ ++ return 0; ++} ++ ++static int hs104_pi_get_pw_class(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PORT0_CLASS + id, &val); ++ if (ret) ++ return ret; ++ ++ return val; ++} ++ ++static int hs104_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ ++ return hs104_read_be16(priv, HS104_REG_PORT0_POWER + id * 2, ++ HS104_MW_STEP); ++} ++ ++static const struct ethtool_c33_pse_pw_limit_range hs104_pw_ranges[] = { ++ { .min = HS104_PW_AF, .max = HS104_PW_AF }, ++ { .min = HS104_PW_AT, .max = HS104_PW_AT }, ++ { .min = HS104_PW_BT, .max = HS104_PW_BT }, ++ { .min = HS104_PW_HIPO, .max = HS104_PW_HIPO }, ++}; ++ ++static int hs104_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, ++ int id, ++ struct pse_pw_limit_ranges *pw_limit_ranges) ++{ ++ struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; ++ ++ c33_pw_limit_ranges = kmemdup(hs104_pw_ranges, sizeof(hs104_pw_ranges), ++ GFP_KERNEL); ++ if (!c33_pw_limit_ranges) ++ return -ENOMEM; ++ ++ pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges; ++ ++ /* Return number of ranges */ ++ return ARRAY_SIZE(hs104_pw_ranges); ++} ++ ++static int hs104_map_event(int irq, struct pse_controller_dev *pcdev, ++ unsigned long *notifs, unsigned long *notifs_mask) ++{ ++ struct hs104_priv *priv = to_hs104(pcdev); ++ unsigned int pw_status, changed; ++ int ret, i; ++ ++ ret = regmap_read(priv->regmap, HS104_REG_PW_STATUS, &pw_status); ++ if (ret) ++ return ret; ++ ++ /* Report ports whose power-delivery state changed. The HS104 has no ++ * per-fault status, so a delivering transition is mapped to a ++ * detection/disconnection event rather than a bogus over-current. ++ */ ++ changed = pw_status ^ priv->last_pw_status; ++ priv->last_pw_status = pw_status; ++ ++ for (i = 0; i < HS104_MAX_PORTS; i++) { ++ if (!(changed & HS104_PORT_BIT(i))) ++ continue; ++ ++ __set_bit(i, notifs_mask); ++ notifs[i] = (pw_status & HS104_PORT_BIT(i)) ? ++ ETHTOOL_C33_PSE_EVENT_DETECTION : ++ ETHTOOL_C33_PSE_EVENT_DISCONNECTION; ++ } ++ ++ return 0; ++} ++ ++static const struct pse_controller_ops hs104_ops = { ++ .pi_enable = hs104_pi_enable, ++ .pi_disable = hs104_pi_disable, ++ .pi_get_admin_state = hs104_pi_get_admin_state, ++ .pi_get_pw_status = hs104_pi_get_pw_status, ++ .pi_get_pw_class = hs104_pi_get_pw_class, ++ .pi_get_actual_pw = hs104_pi_get_actual_pw, ++ .pi_get_voltage = hs104_pi_get_voltage, ++ .pi_get_pw_limit = hs104_pi_get_pw_limit, ++ .pi_set_pw_limit = hs104_pi_set_pw_limit, ++ .pi_get_pw_limit_ranges = hs104_pi_get_pw_limit_ranges, ++}; ++ ++/* Driver initialization */ ++ ++static const struct regmap_range hs104_rd_ranges[] = { ++ regmap_reg_range(HS104_REG_PW_STATUS, HS104_REG_DEVID), /* 0x01-0x0C */ ++ regmap_reg_range(HS104_REG_PORT0_CLASS, ++ HS104_REG_PORT0_CLASS + HS104_MAX_PORTS - 1), /* 0x0D-0x10 */ ++ regmap_reg_range(HS104_REG_PW_EN, HS104_REG_PW_EN), /* 0x14 */ ++ regmap_reg_range(HS104_REG_PROTOCOL, HS104_REG_PROTOCOL), /* 0x19 */ ++ regmap_reg_range(HS104_REG_TOTAL_POWER, ++ HS104_REG_TOTAL_POWER + 1), /* 0x1D-0x1E */ ++ regmap_reg_range(HS104_REG_PORT0_POWER, ++ HS104_REG_PORT0_POWER + HS104_MAX_PORTS * 2 - 1),/* 0x21-0x28 */ ++}; ++ ++static const struct regmap_range hs104_wr_ranges[] = { ++ regmap_reg_range(HS104_REG_PW_EN, HS104_REG_PW_EN), /* 0x14 */ ++ regmap_reg_range(HS104_REG_PROTOCOL, HS104_REG_PROTOCOL), /* 0x19 */ ++}; ++ ++static const struct regmap_access_table hs104_rd_table = { ++ .yes_ranges = hs104_rd_ranges, ++ .n_yes_ranges = ARRAY_SIZE(hs104_rd_ranges), ++}; ++ ++static const struct regmap_access_table hs104_wr_table = { ++ .yes_ranges = hs104_wr_ranges, ++ .n_yes_ranges = ARRAY_SIZE(hs104_wr_ranges), ++}; ++ ++static const struct regmap_config hs104_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .max_register = HS104_REG_PORT0_POWER + HS104_MAX_PORTS * 2 - 1, ++ .rd_table = &hs104_rd_table, ++ .wr_table = &hs104_wr_table, ++}; ++ ++static int hs104_probe(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ struct hs104_priv *priv; ++ unsigned int devid; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ++ return -ENXIO; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->regmap = devm_regmap_init_i2c(client, &hs104_regmap_config); ++ if (IS_ERR(priv->regmap)) ++ return PTR_ERR(priv->regmap); ++ ++ /* Verify device ID */ ++ ret = regmap_read(priv->regmap, HS104_REG_DEVID, &devid); ++ if (ret) ++ return ret; ++ ++ if (devid != HS104_DEVICE_ID) { ++ dev_err(dev, "Unknown device ID: 0x%02x\n", devid); ++ return -ENODEV; ++ } ++ ++ /* ++ * Enable all ports before registering the PSE controller, so that ++ * pse_pi_is_hw_enabled() sees the correct state when consumers ++ * request PSE controls. HS104 only delivers power to valid PDs. ++ */ ++ ret = regmap_write(priv->regmap, HS104_REG_PW_EN, ++ HS104_EXECUTE | GENMASK(HS104_MAX_PORTS - 1, 0)); ++ if (ret) ++ dev_warn(dev, "Failed to enable ports: %d\n", ret); ++ ++ /* Register PSE controller */ ++ priv->pcdev.ops = &hs104_ops; ++ priv->pcdev.dev = dev; ++ priv->pcdev.owner = THIS_MODULE; ++ priv->pcdev.nr_lines = HS104_MAX_PORTS; ++ priv->pcdev.types = ETHTOOL_PSE_C33; ++ ++ /* ++ * Set up the poll helper before registering the controller. Since the ++ * upstream v6 poll path, pse_controller_register() arms the poll work ++ * as its last step, gated on pcdev->polling (which this helper sets). ++ * Registering first would leave the work unarmed and polling - and thus ++ * the LED-trigger state updates - would never start. ++ */ ++ { ++ static const struct pse_irq_desc poll_desc = { ++ .name = "hs104-poll", ++ .map_event = hs104_map_event, ++ }; ++ ++ ret = devm_pse_poll_helper(&priv->pcdev, &poll_desc); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to register poll helper\n"); ++ } ++ ++ ret = devm_pse_controller_register(dev, &priv->pcdev); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to register PSE controller\n"); ++ ++ dev_info(dev, "HS104 PSE controller initialized\n"); ++ return 0; ++} ++ ++static const struct of_device_id hs104_of_match[] = { ++ { .compatible = "hasivo,hs104" }, ++ { .compatible = "hasivo,hs104pti" }, ++ { .compatible = "hasivo,hs104pbi" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hs104_of_match); ++ ++static struct i2c_driver hs104_driver = { ++ .driver = { ++ .name = "hasivo-hs104", ++ .of_match_table = hs104_of_match, ++ }, ++ .probe = hs104_probe, ++}; ++module_i2c_driver(hs104_driver); ++ ++MODULE_AUTHOR("Bevan Weiss "); ++MODULE_AUTHOR("Carlo Szelinsky "); ++MODULE_DESCRIPTION("Hasivo HS104 PoE PSE Controller"); ++MODULE_LICENSE("GPL"); From dd8f705e85dd9954281c2d98e030336d2e88b2cb Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 15:38:39 +0200 Subject: [PATCH 143/152] realtek: rtl9303: enable PSE on Hasivo S1100WP-8GT-SE Wire the two HS104 controllers on the i2c-gpio bus and attach the 8 PoE PHYs via a new PHY_C45_PSE macro. Add the STC8 syscon with its per-port PoE LED triggers, enable LEDS_SYSCON, and pull the PSE and STC8 kmods into the image. Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- target/linux/realtek/dts/macros.dtsi | 7 + .../dts/rtl9303_hasivo_s1100wp-8gt-se.dts | 175 +++++++++++++++++- target/linux/realtek/image/rtl930x.mk | 1 + target/linux/realtek/rtl930x/config-6.18 | 1 + 4 files changed, 175 insertions(+), 9 deletions(-) diff --git a/target/linux/realtek/dts/macros.dtsi b/target/linux/realtek/dts/macros.dtsi index 718206ced9..e0ea0bc5a8 100644 --- a/target/linux/realtek/dts/macros.dtsi +++ b/target/linux/realtek/dts/macros.dtsi @@ -32,6 +32,13 @@ enet-phy-pair-order = <##po>; \ }; +#define PHY_C45_PSE(p, n, pi) \ + phy##p: ethernet-phy@n { \ + reg = <##n>; \ + compatible = "ethernet-phy-ieee802.3-c45"; \ + pses = <&pi>; \ + }; + #define HASH_PSE_CELLS #pse-cells #define PSE_PI(p) \ diff --git a/target/linux/realtek/dts/rtl9303_hasivo_s1100wp-8gt-se.dts b/target/linux/realtek/dts/rtl9303_hasivo_s1100wp-8gt-se.dts index 55fb84c979..e416220b0a 100644 --- a/target/linux/realtek/dts/rtl9303_hasivo_s1100wp-8gt-se.dts +++ b/target/linux/realtek/dts/rtl9303_hasivo_s1100wp-8gt-se.dts @@ -22,6 +22,7 @@ led-running = &led_sys; led-upgrade = &led_sys; label-mac-device = ðernet0; + i2c0 = &i2c_scl23_sda22; }; chosen { @@ -74,7 +75,7 @@ >; }; - i2c_scl23_sda22 { + i2c_scl23_sda22: i2c-scl23-sda22 { compatible = "i2c-gpio"; #address-cells = <1>; #size-cells = <0>; @@ -84,6 +85,162 @@ status = "okay"; clock-frequency = <100000>; + + pse0: ethernet-pse@d { + compatible = "hasivo,hs104"; + reg = <0x0d>; + poll-interval-ms = <500>; + status = "okay"; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + pse0_pi0: pse-pi@0 { + reg = <0>; + #pse-cells = <0>; + }; + pse0_pi1: pse-pi@1 { + reg = <1>; + #pse-cells = <0>; + }; + pse0_pi2: pse-pi@2 { + reg = <2>; + #pse-cells = <0>; + }; + pse0_pi3: pse-pi@3 { + reg = <3>; + #pse-cells = <0>; + }; + }; + }; + + pse1: ethernet-pse@15 { + compatible = "hasivo,hs104"; + reg = <0x15>; + poll-interval-ms = <500>; + status = "okay"; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + pse1_pi0: pse-pi@0 { + reg = <0>; + #pse-cells = <0>; + }; + pse1_pi1: pse-pi@1 { + reg = <1>; + #pse-cells = <0>; + }; + pse1_pi2: pse-pi@2 { + reg = <2>; + #pse-cells = <0>; + }; + pse1_pi3: pse-pi@3 { + reg = <3>; + #pse-cells = <0>; + }; + }; + }; + + stc8: stc8@4d { + compatible = "hasivo,stc8-mfd", "syscon"; + reg = <0x4d>; + #address-cells = <2>; + #size-cells = <0>; + hasivo,execute-bit = <0x40>; + hasivo,execute-bit-registers = <0x01 0x02>; + status = "okay"; + + led_lan1_poe: led@1,0 { + compatible = "register-bit-led"; + reg = <0x01 0x00>; + mask = <0x08>; + color = ; + function = "poe"; + function-enumerator = <1>; + linux,default-trigger = "pse-0-000d:port0:delivering"; + default-state = "off"; + }; + + led_lan2_poe: led@1,1 { + compatible = "register-bit-led"; + reg = <0x01 0x01>; + mask = <0x04>; + color = ; + function = "poe"; + function-enumerator = <2>; + linux,default-trigger = "pse-0-000d:port1:delivering"; + default-state = "off"; + }; + + led_lan3_poe: led@1,2 { + compatible = "register-bit-led"; + reg = <0x01 0x02>; + mask = <0x02>; + color = ; + function = "poe"; + function-enumerator = <3>; + linux,default-trigger = "pse-0-000d:port2:delivering"; + default-state = "off"; + }; + + led_lan4_poe: led@1,3 { + compatible = "register-bit-led"; + reg = <0x01 0x03>; + mask = <0x01>; + color = ; + function = "poe"; + function-enumerator = <4>; + linux,default-trigger = "pse-0-000d:port3:delivering"; + default-state = "off"; + }; + + led_lan5_poe: led@2,0 { + compatible = "register-bit-led"; + reg = <0x02 0x00>; + mask = <0x08>; + color = ; + function = "poe"; + function-enumerator = <5>; + linux,default-trigger = "pse-0-0015:port0:delivering"; + default-state = "off"; + }; + + led_lan6_poe: led@2,1 { + compatible = "register-bit-led"; + reg = <0x02 0x01>; + mask = <0x04>; + color = ; + function = "poe"; + function-enumerator = <6>; + linux,default-trigger = "pse-0-0015:port1:delivering"; + default-state = "off"; + }; + + led_lan7_poe: led@2,2 { + compatible = "register-bit-led"; + reg = <0x02 0x02>; + mask = <0x02>; + color = ; + function = "poe"; + function-enumerator = <7>; + linux,default-trigger = "pse-0-0015:port2:delivering"; + default-state = "off"; + }; + + led_lan8_poe: led@2,3 { + compatible = "register-bit-led"; + reg = <0x02 0x03>; + mask = <0x01>; + color = ; + function = "poe"; + function-enumerator = <8>; + linux,default-trigger = "pse-0-0015:port3:delivering"; + default-state = "off"; + }; + }; }; }; @@ -175,17 +332,17 @@ }; &mdio_bus0 { - PHY_C45(0, 1) - PHY_C45(8, 2) - PHY_C45(16, 3) - PHY_C45(20, 4) + PHY_C45_PSE(0, 1, pse0_pi0) + PHY_C45_PSE(8, 2, pse0_pi1) + PHY_C45_PSE(16, 3, pse0_pi2) + PHY_C45_PSE(20, 4, pse0_pi3) }; &mdio_bus1 { - PHY_C45(24, 1) - PHY_C45(25, 2) - PHY_C45(26, 3) - PHY_C45(27, 4) + PHY_C45_PSE(24, 1, pse1_pi0) + PHY_C45_PSE(25, 2, pse1_pi1) + PHY_C45_PSE(26, 3, pse1_pi2) + PHY_C45_PSE(27, 4, pse1_pi3) }; &switch0 { diff --git a/target/linux/realtek/image/rtl930x.mk b/target/linux/realtek/image/rtl930x.mk index 6f2b6a8cf7..a5313eaeac 100644 --- a/target/linux/realtek/image/rtl930x.mk +++ b/target/linux/realtek/image/rtl930x.mk @@ -66,6 +66,7 @@ define Device/hasivo_s1100wp-8gt-se SOC := rtl9303 DEVICE_VENDOR := Hasivo DEVICE_MODEL := S1100WP-8GT-SE + DEVICE_PACKAGES := kmod-pse-hasivo-hs104 kmod-mfd-hasivo-stc8 IMAGE_SIZE := 12288k $(Device/kernel-lzma) endef diff --git a/target/linux/realtek/rtl930x/config-6.18 b/target/linux/realtek/rtl930x/config-6.18 index c35921089d..45ac876107 100644 --- a/target/linux/realtek/rtl930x/config-6.18 +++ b/target/linux/realtek/rtl930x/config-6.18 @@ -113,6 +113,7 @@ CONFIG_JFFS2_ZLIB=y CONFIG_KMAP_LOCAL=y CONFIG_LEDS_GPIO=y # CONFIG_LEDS_RTL8231 is not set +CONFIG_LEDS_SYSCON=y CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_MACH_REALTEK_RTL=y From f728b4af3592ef7beed24499abadfae024ea9879 Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 16:52:34 +0200 Subject: [PATCH 144/152] realtek: rtl9313: enable PSE on Hasivo S1300WP-8XGT-4S+ Wire the two HS104 controllers (0x0d/0x15) on the i2c_sys bus and attach the 8 PoE PHYs via PHY_C45_PSE. Add the STC8 LED MCU (0x4d) with per-port PoE LED triggers, enable LEDS_SYSCON, and pull the PSE and STC8 kmods into the image. Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- .../rtl9313_hasivo_s1300wp-8xgt-4s-plus.dts | 173 +++++++++++++++++- target/linux/realtek/image/rtl931x.mk | 3 +- target/linux/realtek/rtl931x/config-6.18 | 1 + 3 files changed, 168 insertions(+), 9 deletions(-) diff --git a/target/linux/realtek/dts/rtl9313_hasivo_s1300wp-8xgt-4s-plus.dts b/target/linux/realtek/dts/rtl9313_hasivo_s1300wp-8xgt-4s-plus.dts index 8f6cdf4627..9621fde91c 100644 --- a/target/linux/realtek/dts/rtl9313_hasivo_s1300wp-8xgt-4s-plus.dts +++ b/target/linux/realtek/dts/rtl9313_hasivo_s1300wp-8xgt-4s-plus.dts @@ -23,6 +23,7 @@ led-failsafe = &led_sys; led-running = &led_sys; led-upgrade = &led_sys; + i2c0 = &i2c_sys; }; chosen { @@ -96,6 +97,162 @@ compatible = "hasivo,mcu-sensor"; }; }; + + pse0: ethernet-pse@d { + compatible = "hasivo,hs104"; + reg = <0x0d>; + poll-interval-ms = <500>; + status = "okay"; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + pse0_pi0: pse-pi@0 { + reg = <0>; + #pse-cells = <0>; + }; + pse0_pi1: pse-pi@1 { + reg = <1>; + #pse-cells = <0>; + }; + pse0_pi2: pse-pi@2 { + reg = <2>; + #pse-cells = <0>; + }; + pse0_pi3: pse-pi@3 { + reg = <3>; + #pse-cells = <0>; + }; + }; + }; + + pse1: ethernet-pse@15 { + compatible = "hasivo,hs104"; + reg = <0x15>; + poll-interval-ms = <500>; + status = "okay"; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + pse1_pi0: pse-pi@0 { + reg = <0>; + #pse-cells = <0>; + }; + pse1_pi1: pse-pi@1 { + reg = <1>; + #pse-cells = <0>; + }; + pse1_pi2: pse-pi@2 { + reg = <2>; + #pse-cells = <0>; + }; + pse1_pi3: pse-pi@3 { + reg = <3>; + #pse-cells = <0>; + }; + }; + }; + + stc8: stc8@4d { + compatible = "hasivo,stc8-mfd", "syscon"; + reg = <0x4d>; + #address-cells = <2>; + #size-cells = <0>; + hasivo,execute-bit = <0x40>; + hasivo,execute-bit-registers = <0x01 0x02>; + status = "okay"; + + led_lan1_poe: led@1,0 { + compatible = "register-bit-led"; + reg = <0x01 0x00>; + mask = <0x08>; + color = ; + function = "poe"; + function-enumerator = <1>; + linux,default-trigger = "pse-0-000d:port0:delivering"; + default-state = "off"; + }; + + led_lan2_poe: led@1,1 { + compatible = "register-bit-led"; + reg = <0x01 0x01>; + mask = <0x04>; + color = ; + function = "poe"; + function-enumerator = <2>; + linux,default-trigger = "pse-0-000d:port1:delivering"; + default-state = "off"; + }; + + led_lan3_poe: led@1,2 { + compatible = "register-bit-led"; + reg = <0x01 0x02>; + mask = <0x02>; + color = ; + function = "poe"; + function-enumerator = <3>; + linux,default-trigger = "pse-0-000d:port2:delivering"; + default-state = "off"; + }; + + led_lan4_poe: led@1,3 { + compatible = "register-bit-led"; + reg = <0x01 0x03>; + mask = <0x01>; + color = ; + function = "poe"; + function-enumerator = <4>; + linux,default-trigger = "pse-0-000d:port3:delivering"; + default-state = "off"; + }; + + led_lan5_poe: led@2,0 { + compatible = "register-bit-led"; + reg = <0x02 0x00>; + mask = <0x08>; + color = ; + function = "poe"; + function-enumerator = <5>; + linux,default-trigger = "pse-0-0015:port0:delivering"; + default-state = "off"; + }; + + led_lan6_poe: led@2,1 { + compatible = "register-bit-led"; + reg = <0x02 0x01>; + mask = <0x04>; + color = ; + function = "poe"; + function-enumerator = <6>; + linux,default-trigger = "pse-0-0015:port1:delivering"; + default-state = "off"; + }; + + led_lan7_poe: led@2,2 { + compatible = "register-bit-led"; + reg = <0x02 0x02>; + mask = <0x02>; + color = ; + function = "poe"; + function-enumerator = <7>; + linux,default-trigger = "pse-0-0015:port2:delivering"; + default-state = "off"; + }; + + led_lan8_poe: led@2,3 { + compatible = "register-bit-led"; + reg = <0x02 0x03>; + mask = <0x01>; + color = ; + function = "poe"; + function-enumerator = <8>; + linux,default-trigger = "pse-0-0015:port3:delivering"; + default-state = "off"; + }; + }; }; leds { @@ -196,10 +353,10 @@ #size-cells = <0>; reg = <0>; - PHY_C45(0, 0) - PHY_C45(8, 1) - PHY_C45(16, 2) - PHY_C45(24, 3) + PHY_C45_PSE(0, 0, pse0_pi0) + PHY_C45_PSE(8, 1, pse0_pi1) + PHY_C45_PSE(16, 2, pse0_pi2) + PHY_C45_PSE(24, 3, pse0_pi3) }; }; @@ -210,10 +367,10 @@ #size-cells = <0>; reg = <4>; - PHY_C45(32, 4) - PHY_C45(40, 5) - PHY_C45(48, 6) - PHY_C45(50, 7) + PHY_C45_PSE(32, 4, pse1_pi0) + PHY_C45_PSE(40, 5, pse1_pi1) + PHY_C45_PSE(48, 6, pse1_pi2) + PHY_C45_PSE(50, 7, pse1_pi3) }; }; diff --git a/target/linux/realtek/image/rtl931x.mk b/target/linux/realtek/image/rtl931x.mk index c3a4938532..083144c3a8 100644 --- a/target/linux/realtek/image/rtl931x.mk +++ b/target/linux/realtek/image/rtl931x.mk @@ -18,7 +18,8 @@ define Device/hasivo_s1300wp-8xgt-4s-plus DEVICE_MODEL := S1300WP-8XGT-4S+ IMAGE_SIZE := 29696k DEVICE_PACKAGES := kmod-phy-realtek kmod-rtc-pcf8563 rtl826x-firmware \ - kmod-hasivo-mcu-wdt kmod-hasivo-mcu-sensor + kmod-hasivo-mcu-wdt kmod-hasivo-mcu-sensor kmod-pse-hasivo-hs104 \ + kmod-mfd-hasivo-stc8 $(Device/kernel-lzma) endef TARGET_DEVICES += hasivo_s1300wp-8xgt-4s-plus diff --git a/target/linux/realtek/rtl931x/config-6.18 b/target/linux/realtek/rtl931x/config-6.18 index 835bce9392..2d4a29badf 100644 --- a/target/linux/realtek/rtl931x/config-6.18 +++ b/target/linux/realtek/rtl931x/config-6.18 @@ -115,6 +115,7 @@ CONFIG_JFFS2_ZLIB=y CONFIG_KMAP_LOCAL=y CONFIG_LEDS_GPIO=y # CONFIG_LEDS_RTL8231 is not set +CONFIG_LEDS_SYSCON=y CONFIG_LIBFDT=y CONFIG_LOCK_DEBUGGING_SUPPORT=y CONFIG_MACH_REALTEK_RTL=y From b3ff21a2ec7a0d9ad69d68ff401e16d9a8ef7abb Mon Sep 17 00:00:00 2001 From: Carlo Szelinsky Date: Sat, 18 Jul 2026 16:52:34 +0200 Subject: [PATCH 145/152] realtek: rtl9303: enable PSE on Hasivo S600WP-5GT-2SX-SE Wire the HS104 controller (0x0d) on the PSE i2c-gpio bus and attach the four PoE PHYs (lan1-4) via PHY_C45_PSE; lan5 is not PoE. Add the STC8 LED MCU (0x4d) with per-port PoE LED triggers, and pull the HS104 PSE and STC8 kmods into the image. Signed-off-by: Carlo Szelinsky Link: https://github.com/openwrt/openwrt/pull/22245 Signed-off-by: Jonas Jelonek --- .../dts/rtl9303_hasivo_s600wp-5gt-2sx-se.dts | 101 ++++++++++++++++-- target/linux/realtek/image/rtl930x.mk | 1 + 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/target/linux/realtek/dts/rtl9303_hasivo_s600wp-5gt-2sx-se.dts b/target/linux/realtek/dts/rtl9303_hasivo_s600wp-5gt-2sx-se.dts index 2aedd7ec85..41a894d57c 100644 --- a/target/linux/realtek/dts/rtl9303_hasivo_s600wp-5gt-2sx-se.dts +++ b/target/linux/realtek/dts/rtl9303_hasivo_s600wp-5gt-2sx-se.dts @@ -22,6 +22,9 @@ led-running = &led_sys; led-upgrade = &led_sys; label-mac-device = ðernet0; + i2c0 = &i2c_scl23_sda22; + i2c1 = &i2c_sfp0; + i2c2 = &i2c_sfp1; }; chosen { @@ -81,11 +84,7 @@ (RTL93XX_LED_SET_NONE)>; }; - /* - * TODO: I2C bus to the on-board HS104 PSE controller (PoE). - * Defined here for documentation/future use; currently unused - * because PSE/PoE support for this board is not yet wired up. - */ + /* I2C bus to the on-board HS104 PSE controller (PoE) and STC8 LED MCU. */ i2c_scl23_sda22: i2c-scl23-sda22 { compatible = "i2c-gpio"; #address-cells = <1>; @@ -93,6 +92,90 @@ scl-gpios = <&gpio0 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; sda-gpios = <&gpio0 22 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + clock-frequency = <100000>; + + pse0: ethernet-pse@d { + compatible = "hasivo,hs104"; + reg = <0x0d>; + poll-interval-ms = <500>; + status = "okay"; + + pse-pis { + #address-cells = <1>; + #size-cells = <0>; + + pse0_pi0: pse-pi@0 { + reg = <0>; + #pse-cells = <0>; + }; + pse0_pi1: pse-pi@1 { + reg = <1>; + #pse-cells = <0>; + }; + pse0_pi2: pse-pi@2 { + reg = <2>; + #pse-cells = <0>; + }; + pse0_pi3: pse-pi@3 { + reg = <3>; + #pse-cells = <0>; + }; + }; + }; + + stc8: stc8@4d { + compatible = "hasivo,stc8-mfd", "syscon"; + reg = <0x4d>; + #address-cells = <2>; + #size-cells = <0>; + hasivo,execute-bit = <0x40>; + hasivo,execute-bit-registers = <0x01 0x02>; + status = "okay"; + + led_lan1_poe: led@1,0 { + compatible = "register-bit-led"; + reg = <0x01 0x00>; + mask = <0x08>; + color = ; + function = "poe"; + function-enumerator = <1>; + linux,default-trigger = "pse-0-000d:port0:delivering"; + default-state = "off"; + }; + + led_lan2_poe: led@1,1 { + compatible = "register-bit-led"; + reg = <0x01 0x01>; + mask = <0x04>; + color = ; + function = "poe"; + function-enumerator = <2>; + linux,default-trigger = "pse-0-000d:port1:delivering"; + default-state = "off"; + }; + + led_lan3_poe: led@1,2 { + compatible = "register-bit-led"; + reg = <0x01 0x02>; + mask = <0x02>; + color = ; + function = "poe"; + function-enumerator = <3>; + linux,default-trigger = "pse-0-000d:port2:delivering"; + default-state = "off"; + }; + + led_lan4_poe: led@1,3 { + compatible = "register-bit-led"; + reg = <0x01 0x03>; + mask = <0x01>; + color = ; + function = "poe"; + function-enumerator = <4>; + linux,default-trigger = "pse-0-000d:port3:delivering"; + default-state = "off"; + }; + }; }; i2c_sfp0: i2c-sfp0 { @@ -226,10 +309,10 @@ &mdio_bus0 { /* External RTL8221B PHYs */ - PHY_C45(0, 1) - PHY_C45(8, 2) - PHY_C45(16, 3) - PHY_C45(20, 4) + PHY_C45_PSE(0, 1, pse0_pi0) + PHY_C45_PSE(8, 2, pse0_pi1) + PHY_C45_PSE(16, 3, pse0_pi2) + PHY_C45_PSE(20, 4, pse0_pi3) }; &mdio_bus1 { diff --git a/target/linux/realtek/image/rtl930x.mk b/target/linux/realtek/image/rtl930x.mk index a5313eaeac..5d06c58706 100644 --- a/target/linux/realtek/image/rtl930x.mk +++ b/target/linux/realtek/image/rtl930x.mk @@ -76,6 +76,7 @@ define Device/hasivo_s600wp-5gt-2sx-se SOC := rtl9303 DEVICE_VENDOR := Hasivo DEVICE_MODEL := S600WP-5GT-2SX-SE + DEVICE_PACKAGES := kmod-pse-hasivo-hs104 kmod-mfd-hasivo-stc8 IMAGE_SIZE := 12288k $(Device/kernel-lzma) endef From 289011eac0b6efc83c0ea445ae518c0af86893d0 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Tue, 21 Jul 2026 11:28:26 +0200 Subject: [PATCH 146/152] realtek: eth: Rename coherent structures In the past the ring handling was split into a coherent (SOC/driver shared) and a driver-only part. Give the coherent structure a meaningful name. Link: https://github.com/openwrt/openwrt/pull/24346 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index c5e532040e..96a2b10cb0 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -69,12 +69,13 @@ struct rteth_packet { unsigned int page_offset; } __packed __aligned(1); -struct rteth_rx { +/* SOC/driver shared coherent ring descriptors */ +struct rteth_rx_data { dma_addr_t ring[RTETH_RX_RING_SIZE]; struct rteth_packet packet[RTETH_RX_RING_SIZE]; }; -struct rteth_tx { +struct rteth_tx_data { int slot; dma_addr_t ring[RTETH_TX_RING_SIZE]; struct rteth_packet packet[RTETH_TX_RING_SIZE]; @@ -125,11 +126,11 @@ struct rteth_ctrl { dma_addr_t rx_dma; spinlock_t rx_lock; struct rteth_rx_info rx_info[RTETH_RX_RINGS]; - struct rteth_rx *rx_data; + struct rteth_rx_data *rx_data; /* transmit handling */ dma_addr_t tx_dma; spinlock_t tx_lock; - struct rteth_tx *tx_data; + struct rteth_tx_data *tx_data; }; static void rteth_838x_create_tx_header(struct rteth_packet *h, unsigned int port, int prio) @@ -544,13 +545,13 @@ static void rteth_hw_ring_setup(struct rteth_ctrl *ctrl) { for (int r = 0; r < RTETH_RX_RINGS; r++) regmap_write(ctrl->map, ctrl->r->dma_rx_base + r * 4, - ctrl->rx_dma + - r * sizeof(struct rteth_rx) + offsetof(struct rteth_rx, ring)); + ctrl->rx_dma + r * sizeof(struct rteth_rx_data) + + offsetof(struct rteth_rx_data, ring)); for (int r = 0; r < RTETH_TX_RINGS; r++) regmap_write(ctrl->map, ctrl->r->dma_tx_base + r * 4, - ctrl->tx_dma + - r * sizeof(struct rteth_tx) + offsetof(struct rteth_tx, ring)); + ctrl->tx_dma + r * sizeof(struct rteth_tx_data) + + offsetof(struct rteth_tx_data, ring)); } static void rteth_838x_hw_en_rxtx(struct rteth_ctrl *ctrl) @@ -707,8 +708,8 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) packet->page = page; packet->page_offset = offset; ctrl->rx_data[r].ring[i] = ctrl->rx_dma + - sizeof(struct rteth_rx) * r + - offsetof(struct rteth_rx, packet) + + sizeof(struct rteth_rx_data) * r + + offsetof(struct rteth_rx_data, packet) + sizeof(struct rteth_packet) * i + RING_OWN_HW; } @@ -722,8 +723,8 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) for (int i = 0; i < RTETH_TX_RING_SIZE; i++) { ctrl->tx_data[r].packet[i].skb = NULL; ctrl->tx_data[r].ring[i] = ctrl->tx_dma + - sizeof(struct rteth_tx) * r + - offsetof(struct rteth_tx, packet) + + sizeof(struct rteth_tx_data) * r + + offsetof(struct rteth_tx_data, packet) + sizeof(struct rteth_packet) * i; } @@ -1771,9 +1772,9 @@ static int rteth_probe(struct platform_device *pdev) return -ENOMEM; } - ctrl->rx_data = dmam_alloc_coherent(&pdev->dev, sizeof(struct rteth_rx) * RTETH_RX_RINGS, + ctrl->rx_data = dmam_alloc_coherent(&pdev->dev, sizeof(struct rteth_rx_data) * RTETH_RX_RINGS, &ctrl->rx_dma, GFP_KERNEL); - ctrl->tx_data = dmam_alloc_coherent(&pdev->dev, sizeof(struct rteth_tx) * RTETH_TX_RINGS, + ctrl->tx_data = dmam_alloc_coherent(&pdev->dev, sizeof(struct rteth_tx_data) * RTETH_TX_RINGS, &ctrl->tx_dma, GFP_KERNEL); spin_lock_init(&ctrl->lock); From 771f370d74d067c039f6c3d6ac76972b027c57e5 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Tue, 21 Jul 2026 11:36:49 +0200 Subject: [PATCH 147/152] realtek: eth: add driver-only transmit info structure Remove driver-only data from the coherent transmit structure. It is not needed to keep this in uncached memory as the SOC does not need access to it. Link: https://github.com/openwrt/openwrt/pull/24346 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 96a2b10cb0..af38b42515 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -76,11 +76,24 @@ struct rteth_rx_data { }; struct rteth_tx_data { - int slot; dma_addr_t ring[RTETH_TX_RING_SIZE]; struct rteth_packet packet[RTETH_TX_RING_SIZE]; }; +/* driver-only ring descriptors */ +struct rteth_rx_info { + int id; + int slot; + struct rteth_ctrl *ctrl; + struct napi_struct napi; + struct page_pool *pool; + struct sk_buff *skb; /* unprocessed SKB from last receive loop */ +}; + +struct rteth_tx_info { + int slot; +}; + struct n_event { u32 type:2; u32 fidVid:12; @@ -101,15 +114,6 @@ struct notify_b { u32 reserved2[8]; }; -struct rteth_rx_info { - int id; - int slot; - struct rteth_ctrl *ctrl; - struct napi_struct napi; - struct page_pool *pool; - struct sk_buff *skb; -}; - struct rteth_ctrl { struct regmap *map; struct net_device *dev; @@ -130,6 +134,7 @@ struct rteth_ctrl { /* transmit handling */ dma_addr_t tx_dma; spinlock_t tx_lock; + struct rteth_tx_info tx_info[RTETH_TX_RINGS]; struct rteth_tx_data *tx_data; }; @@ -729,7 +734,7 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) } ctrl->tx_data[r].ring[RTETH_TX_RING_SIZE - 1] |= RING_WRAP; - ctrl->tx_data[r].slot = 0; + ctrl->tx_info[r].slot = 0; } if (highmem) @@ -1066,7 +1071,7 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - slot = ctrl->tx_data[ring].slot; + slot = ctrl->tx_info[ring].slot; packet = &ctrl->tx_data[ring].packet[slot]; packet_dma = ctrl->tx_data[ring].ring[slot]; @@ -1098,7 +1103,7 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Hand packet over to switch */ dma_wmb(); ctrl->tx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; - ctrl->tx_data[ring].slot = (slot + 1) % RTETH_TX_RING_SIZE; + ctrl->tx_info[ring].slot = (slot + 1) % RTETH_TX_RING_SIZE; wmb(); spin_lock(&ctrl->tx_lock); From 1588766cee1e41d41b42fe30b4c8207206b11143 Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Tue, 21 Jul 2026 12:21:29 +0200 Subject: [PATCH 148/152] realtek: eth: change skb helper function signatures It is easier to work with ring/slot when shifting around variables in the different structures. Replace packet pointers in existing function signatures. Link: https://github.com/openwrt/openwrt/pull/24346 Signed-off-by: Markus Stockhausen --- .../files-6.18/drivers/net/ethernet/rtl838x_eth.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index af38b42515..e1b5861f9c 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -1127,9 +1127,9 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, - struct rteth_packet *packet) +static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, int slot) { + struct rteth_packet *packet = &ctrl->rx_data[ring].packet[slot]; struct page_pool *pool = ctrl->rx_info[ring].pool; unsigned int offset = packet->page_offset; struct net_device *dev = ctrl->dev; @@ -1167,13 +1167,14 @@ static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, return skb; } -static int rteth_append_skb(struct sk_buff *skb, struct rteth_ctrl *ctrl, int ring, - struct rteth_packet *packet) +static int rteth_append_skb(struct sk_buff *skb, struct rteth_ctrl *ctrl, int ring, int slot) { - unsigned int nr_frags = skb_shinfo(skb)->nr_frags, len = packet->len; + struct rteth_packet *packet = &ctrl->rx_data[ring].packet[slot]; + unsigned int nr_frags = skb_shinfo(skb)->nr_frags; struct page_pool *pool = ctrl->rx_info[ring].pool; unsigned int offset = packet->page_offset; struct page *page = packet->page; + unsigned int len = packet->len; if (nr_frags >= MAX_SKB_FRAGS) { page_pool_put_full_page(pool, packet->page, true); @@ -1239,13 +1240,13 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) } if (is_head) { - skb = rteth_create_skb(ctrl, ring, packet); + skb = rteth_create_skb(ctrl, ring, slot); if (unlikely(!skb)) { netdev_err(dev, "skb creation failed\n"); rx_dropped++; } } else { - if (unlikely(rteth_append_skb(skb, ctrl, ring, packet))) { + if (unlikely(rteth_append_skb(skb, ctrl, ring, slot))) { netdev_err(dev, "skb append failed\n"); rx_dropped += rteth_free_skb(&skb); } From 25e30fd2749b42d8fe28bcfbaaa6d3644b7839aa Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Tue, 21 Jul 2026 13:00:41 +0200 Subject: [PATCH 149/152] realtek: eth: move page/pool into driver structure The paged pool handling is only required for the driver and not for the SOC. Move it into the driver-specific part. Link: https://github.com/openwrt/openwrt/pull/24346 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index e1b5861f9c..78b66ec33c 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -65,8 +65,6 @@ struct rteth_packet { u16 cpu_tag[10]; /* software mangement and data part */ struct sk_buff *skb; - struct page *page; - unsigned int page_offset; } __packed __aligned(1); /* SOC/driver shared coherent ring descriptors */ @@ -88,6 +86,8 @@ struct rteth_rx_info { struct napi_struct napi; struct page_pool *pool; struct sk_buff *skb; /* unprocessed SKB from last receive loop */ + struct page *page[RTETH_RX_RING_SIZE]; + unsigned int offset[RTETH_RX_RING_SIZE]; }; struct rteth_tx_info { @@ -661,18 +661,18 @@ static void rteth_free_tx_buffers(struct rteth_ctrl *ctrl) static void rteth_free_rx_buffers(struct rteth_ctrl *ctrl) { - struct rteth_packet *packet; + struct rteth_rx_info *rx_info; for (int r = 0; r < RTETH_RX_RINGS; r++) { + rx_info = &ctrl->rx_info[r]; for (int i = 0; i < RTETH_RX_RING_SIZE; i++) { - packet = &ctrl->rx_data[r].packet[i]; - if (!packet->page) + if (!rx_info->page[i]) continue; - page_pool_put_full_page(ctrl->rx_info[r].pool, packet->page, true); - packet->page = NULL; + page_pool_put_full_page(rx_info->pool, rx_info->page[i], true); + rx_info->page[i] = NULL; } - rteth_free_skb(&ctrl->rx_info[r].skb); + rteth_free_skb(&rx_info->skb); } } @@ -710,8 +710,8 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) packet->size = SKB_FRAG_SIZE; packet->dma = page_pool_get_dma_addr(page) + ctrl->r->skb_headroom + offset; - packet->page = page; - packet->page_offset = offset; + ctrl->rx_info[r].page[i] = page; + ctrl->rx_info[r].offset[i] = offset; ctrl->rx_data[r].ring[i] = ctrl->rx_dma + sizeof(struct rteth_rx_data) * r + offsetof(struct rteth_rx_data, packet) + @@ -1130,10 +1130,10 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, int slot) { struct rteth_packet *packet = &ctrl->rx_data[ring].packet[slot]; + unsigned int offset = ctrl->rx_info[ring].offset[slot]; + struct page *page = ctrl->rx_info[ring].page[slot]; struct page_pool *pool = ctrl->rx_info[ring].pool; - unsigned int offset = packet->page_offset; struct net_device *dev = ctrl->dev; - struct page *page = packet->page; unsigned int len = packet->len; struct sk_buff *skb; struct dsa_tag tag; @@ -1170,14 +1170,14 @@ static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, int s static int rteth_append_skb(struct sk_buff *skb, struct rteth_ctrl *ctrl, int ring, int slot) { struct rteth_packet *packet = &ctrl->rx_data[ring].packet[slot]; + unsigned int offset = ctrl->rx_info[ring].offset[slot]; + struct page *page = ctrl->rx_info[ring].page[slot]; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; struct page_pool *pool = ctrl->rx_info[ring].pool; - unsigned int offset = packet->page_offset; - struct page *page = packet->page; unsigned int len = packet->len; if (nr_frags >= MAX_SKB_FRAGS) { - page_pool_put_full_page(pool, packet->page, true); + page_pool_put_full_page(pool, page, true); return -ENOMEM; } @@ -1265,8 +1265,8 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) } } - packet->page = new_page; - packet->page_offset = new_offset; + ctrl->rx_info[ring].page[slot] = new_page; + ctrl->rx_info[ring].offset[slot] = new_offset; packet->dma = page_pool_get_dma_addr(new_page) + new_offset + ctrl->r->skb_headroom; recycle: From 57850d4bef6599bcf8d1aa1dfcc385131a6547cc Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Tue, 21 Jul 2026 13:32:05 +0200 Subject: [PATCH 150/152] realtek: eth: move tx skb into driver-only structure The TX path stores the skb information in the coherent part. This is only needed by the driver. Move it to its proper location. Link: https://github.com/openwrt/openwrt/pull/24346 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index 78b66ec33c..aec9676495 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -63,8 +63,6 @@ struct rteth_packet { u16 offset:15; u16 len; u16 cpu_tag[10]; - /* software mangement and data part */ - struct sk_buff *skb; } __packed __aligned(1); /* SOC/driver shared coherent ring descriptors */ @@ -92,6 +90,7 @@ struct rteth_rx_info { struct rteth_tx_info { int slot; + struct sk_buff *skb[RTETH_TX_RING_SIZE]; }; struct n_event { @@ -644,17 +643,19 @@ static void rteth_931x_hw_en_rxtx(struct rteth_ctrl *ctrl) static void rteth_free_tx_buffers(struct rteth_ctrl *ctrl) { - struct rteth_packet *packet; + struct rteth_tx_info *tx_info; for (int r = 0; r < RTETH_TX_RINGS; r++) { + tx_info = &ctrl->tx_info[r]; for (int i = 0; i < RTETH_TX_RING_SIZE; i++) { - packet = &ctrl->tx_data[r].packet[i]; - if (!packet->skb) + dma_addr_t dma = ctrl->tx_data[r].packet[i].dma; + + if (!tx_info->skb[i]) continue; - dma_unmap_single(&ctrl->pdev->dev, packet->dma, - packet->skb->len, DMA_TO_DEVICE); - rteth_free_skb(&packet->skb); + dma_unmap_single(&ctrl->pdev->dev, dma, + tx_info->skb[i]->len, DMA_TO_DEVICE); + rteth_free_skb(&tx_info->skb[i]); } } } @@ -726,7 +727,7 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) for (int r = 0; r < RTETH_TX_RINGS; r++) { for (int i = 0; i < RTETH_TX_RING_SIZE; i++) { - ctrl->tx_data[r].packet[i].skb = NULL; + ctrl->tx_info[r].skb[i] = NULL; ctrl->tx_data[r].ring[i] = ctrl->tx_dma + sizeof(struct rteth_tx_data) * r + offsetof(struct rteth_tx_data, packet) + @@ -1057,6 +1058,7 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) int port, val, slot, len = skb->len, ring = skb_get_queue_mapping(skb); struct rteth_ctrl *ctrl = netdev_priv(dev); struct rteth_packet *packet; + struct sk_buff **packet_skb; dma_addr_t packet_dma; port = rteth_get_dsa_port(skb, dev); @@ -1074,6 +1076,7 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) slot = ctrl->tx_info[ring].slot; packet = &ctrl->tx_data[ring].packet[slot]; packet_dma = ctrl->tx_data[ring].ring[slot]; + packet_skb = &ctrl->tx_info[ring].skb[slot]; if (unlikely(packet_dma & RING_OWN_HW)) { netif_stop_subqueue(dev, ring); @@ -1083,17 +1086,17 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - if (likely(packet->skb)) { + if (likely(*packet_skb)) { /* cleanup old data of this slot */ - dma_unmap_single(&ctrl->pdev->dev, packet->dma, packet->skb->len, DMA_TO_DEVICE); - dev_consume_skb_any(packet->skb); + dma_unmap_single(&ctrl->pdev->dev, packet->dma, (*packet_skb)->len, DMA_TO_DEVICE); + dev_consume_skb_any(*packet_skb); } + *packet_skb = skb; packet->len = len; - packet->skb = skb; packet->dma = dma_map_single(&ctrl->pdev->dev, skb->data, len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(&ctrl->pdev->dev, packet->dma))) { - dev->stats.tx_errors += rteth_free_skb(&packet->skb); + dev->stats.tx_errors += rteth_free_skb(packet_skb); return NETDEV_TX_OK; } From fb69055ed51955859255c67bd653d81f583ee45f Mon Sep 17 00:00:00 2001 From: Markus Stockhausen Date: Tue, 21 Jul 2026 13:50:29 +0200 Subject: [PATCH 151/152] realtek: eth: rename packet to frag Since the jumbo frame preparation the driver can work on fragments and not only on complete packets in the receive path. This might be implementet in the transmit patch in the future too. So a chunk of data is best described as a fragment. Rename all occurrences. Link: https://github.com/openwrt/openwrt/pull/24346 Signed-off-by: Markus Stockhausen --- .../drivers/net/ethernet/rtl838x_eth.c | 144 +++++++++--------- .../drivers/net/ethernet/rtl838x_eth.h | 6 +- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c index aec9676495..9825803021 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.c @@ -54,7 +54,7 @@ #define PPOOL_SIZE (DIV_ROUND_UP(RTETH_RX_RING_SIZE, \ PAGE_SIZE / PPOOL_FRAG_SIZE) + 8) -struct rteth_packet { +struct rteth_frag { /* hardware header part as required by SoC */ dma_addr_t dma; u16 reserved; @@ -68,12 +68,12 @@ struct rteth_packet { /* SOC/driver shared coherent ring descriptors */ struct rteth_rx_data { dma_addr_t ring[RTETH_RX_RING_SIZE]; - struct rteth_packet packet[RTETH_RX_RING_SIZE]; + struct rteth_frag frag[RTETH_RX_RING_SIZE]; }; struct rteth_tx_data { dma_addr_t ring[RTETH_TX_RING_SIZE]; - struct rteth_packet packet[RTETH_TX_RING_SIZE]; + struct rteth_frag frag[RTETH_TX_RING_SIZE]; }; /* driver-only ring descriptors */ @@ -137,53 +137,53 @@ struct rteth_ctrl { struct rteth_tx_data *tx_data; }; -static void rteth_838x_create_tx_header(struct rteth_packet *h, unsigned int port, int prio) +static void rteth_838x_create_tx_header(struct rteth_frag *frag, unsigned int port, int prio) { /* cpu_tag[0] is reserved on the RTL83XX SoCs */ - h->cpu_tag[1] = 0x0400; /* BIT 10: RTL8380_CPU_TAG */ - h->cpu_tag[2] = 0x0200; /* Set only AS_DPM, to enable DPM settings below */ - h->cpu_tag[3] = 0x0000; - h->cpu_tag[4] = BIT(port) >> 16; - h->cpu_tag[5] = BIT(port) & 0xffff; + frag->cpu_tag[1] = 0x0400; /* BIT 10: RTL8380_CPU_TAG */ + frag->cpu_tag[2] = 0x0200; /* Set only AS_DPM, to enable DPM settings below */ + frag->cpu_tag[3] = 0x0000; + frag->cpu_tag[4] = BIT(port) >> 16; + frag->cpu_tag[5] = BIT(port) & 0xffff; /* Set internal priority (PRI) and enable (AS_PRI) */ if (prio >= 0) - h->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 12; + frag->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 12; } -static void rteth_839x_create_tx_header(struct rteth_packet *h, unsigned int port, int prio) +static void rteth_839x_create_tx_header(struct rteth_frag *frag, unsigned int port, int prio) { /* cpu_tag[0] is reserved on the RTL83XX SoCs */ - h->cpu_tag[1] = 0x0100; /* RTL8390_CPU_TAG marker */ - h->cpu_tag[2] = BIT(4); /* AS_DPM flag */ - h->cpu_tag[3] = h->cpu_tag[4] = h->cpu_tag[5] = 0; + frag->cpu_tag[1] = 0x0100; /* RTL8390_CPU_TAG marker */ + frag->cpu_tag[2] = BIT(4); /* AS_DPM flag */ + frag->cpu_tag[3] = frag->cpu_tag[4] = frag->cpu_tag[5] = 0; /* h->cpu_tag[1] |= BIT(1) | BIT(0); */ /* Bypass filter 1/2 */ if (port >= 32) { port -= 32; - h->cpu_tag[2] |= (BIT(port) >> 16) & 0xf; - h->cpu_tag[3] = BIT(port) & 0xffff; + frag->cpu_tag[2] |= (BIT(port) >> 16) & 0xf; + frag->cpu_tag[3] = BIT(port) & 0xffff; } else { - h->cpu_tag[4] = BIT(port) >> 16; - h->cpu_tag[5] = BIT(port) & 0xffff; + frag->cpu_tag[4] = BIT(port) >> 16; + frag->cpu_tag[5] = BIT(port) & 0xffff; } /* Set internal priority (PRI) and enable (AS_PRI) */ if (prio >= 0) - h->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 8; + frag->cpu_tag[2] |= ((prio & 0x7) | BIT(3)) << 8; } -static void rteth_93xx_create_tx_header(struct rteth_packet *h, unsigned int port, int prio) +static void rteth_93xx_create_tx_header(struct rteth_frag *frag, unsigned int port, int prio) { - h->cpu_tag[0] = 0x8000; /* CPU tag marker */ - h->cpu_tag[1] = FIELD_PREP(RTL93XX_CPU_TAG1_FWD_MASK, RTL93XX_CPU_TAG1_FWD_PHYSICAL) | - FIELD_PREP(RTL93XX_CPU_TAG1_IGNORE_STP_MASK, 1); + frag->cpu_tag[0] = 0x8000; /* CPU tag marker */ + frag->cpu_tag[1] = FIELD_PREP(RTL93XX_CPU_TAG1_FWD_MASK, RTL93XX_CPU_TAG1_FWD_PHYSICAL) | + FIELD_PREP(RTL93XX_CPU_TAG1_IGNORE_STP_MASK, 1); - h->cpu_tag[2] = (prio >= 0) ? (BIT(5) | (prio & 0x1f)) << 8 : 0; - h->cpu_tag[3] = 0; - h->cpu_tag[4] = BIT_ULL(port) >> 48; - h->cpu_tag[5] = BIT_ULL(port) >> 32; - h->cpu_tag[6] = BIT_ULL(port) >> 16; - h->cpu_tag[7] = BIT_ULL(port) & 0xffff; + frag->cpu_tag[2] = (prio >= 0) ? (BIT(5) | (prio & 0x1f)) << 8 : 0; + frag->cpu_tag[3] = 0; + frag->cpu_tag[4] = BIT_ULL(port) >> 48; + frag->cpu_tag[5] = BIT_ULL(port) >> 32; + frag->cpu_tag[6] = BIT_ULL(port) >> 16; + frag->cpu_tag[7] = BIT_ULL(port) & 0xffff; } static int rteth_free_skb(struct sk_buff **skb) @@ -287,12 +287,12 @@ struct dsa_tag { bool crc_error; }; -static bool rteth_838x_decode_tag(struct rteth_packet *h, struct dsa_tag *t) +static bool rteth_838x_decode_tag(struct rteth_frag *frag, struct dsa_tag *t) { /* cpu_tag[0] is reserved. Fields are off-by-one */ - t->reason = h->cpu_tag[4] & 0xf; - t->queue = (h->cpu_tag[1] & 0xe0) >> 5; - t->port = h->cpu_tag[1] & 0x1f; + t->reason = frag->cpu_tag[4] & 0xf; + t->queue = (frag->cpu_tag[1] & 0xe0) >> 5; + t->port = frag->cpu_tag[1] & 0x1f; t->crc_error = t->reason == 13; pr_debug("Reason: %d\n", t->reason); @@ -304,13 +304,13 @@ static bool rteth_838x_decode_tag(struct rteth_packet *h, struct dsa_tag *t) return t->l2_offloaded; } -static bool rteth_839x_decode_tag(struct rteth_packet *h, struct dsa_tag *t) +static bool rteth_839x_decode_tag(struct rteth_frag *frag, struct dsa_tag *t) { /* cpu_tag[0] is reserved. Fields are off-by-one */ - t->reason = h->cpu_tag[5] & 0x1f; - t->queue = (h->cpu_tag[4] & 0xe000) >> 13; - t->port = h->cpu_tag[1] & 0x3f; - t->crc_error = h->cpu_tag[4] & BIT(6); + t->reason = frag->cpu_tag[5] & 0x1f; + t->queue = (frag->cpu_tag[4] & 0xe000) >> 13; + t->port = frag->cpu_tag[1] & 0x3f; + t->crc_error = frag->cpu_tag[4] & BIT(6); pr_debug("Reason: %d\n", t->reason); if ((t->reason >= 7 && t->reason <= 13) || /* NIC_RX_REASON_RMA */ @@ -322,12 +322,12 @@ static bool rteth_839x_decode_tag(struct rteth_packet *h, struct dsa_tag *t) return t->l2_offloaded; } -static bool rteth_93xx_decode_tag(struct rteth_packet *h, struct dsa_tag *t) +static bool rteth_93xx_decode_tag(struct rteth_frag *frag, struct dsa_tag *t) { - t->port = (h->cpu_tag[0] >> 8) & 0x3f; - t->queue = (h->cpu_tag[2] >> 11) & 0x1f; - t->reason = h->cpu_tag[7] & 0x3f; - t->crc_error = h->cpu_tag[1] & BIT(6); + t->port = (frag->cpu_tag[0] >> 8) & 0x3f; + t->queue = (frag->cpu_tag[2] >> 11) & 0x1f; + t->reason = frag->cpu_tag[7] & 0x3f; + t->crc_error = frag->cpu_tag[1] & BIT(6); t->l2_offloaded = (t->reason >= 19 && t->reason <= 27) ? 0 : 1; if (t->reason != 63) @@ -504,7 +504,7 @@ static void rteth_setup_cpu_rx_rings(struct rteth_ctrl *ctrl) { /* * Realtek switches either have 8 (RTL83xx) or 32 (RTL93xx) receive queues. Whenever - * a packet is trapped/received for the CPU it is put into one of these queues. This + * a frag is trapped/received for the CPU it is put into one of these queues. This * is configured via mapping registers in two ways: * * - Switching queue/priority to CPU queue mapping (RTL83xx) @@ -648,7 +648,7 @@ static void rteth_free_tx_buffers(struct rteth_ctrl *ctrl) for (int r = 0; r < RTETH_TX_RINGS; r++) { tx_info = &ctrl->tx_info[r]; for (int i = 0; i < RTETH_TX_RING_SIZE; i++) { - dma_addr_t dma = ctrl->tx_data[r].packet[i].dma; + dma_addr_t dma = ctrl->tx_data[r].frag[i].dma; if (!tx_info->skb[i]) continue; @@ -679,7 +679,7 @@ static void rteth_free_rx_buffers(struct rteth_ctrl *ctrl) static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) { - struct rteth_packet *packet; + struct rteth_frag *frag; phys_addr_t paddr = 0; bool highmem = false; unsigned int offset; @@ -695,7 +695,7 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) for (int r = 0; r < RTETH_RX_RINGS; r++) { for (int i = 0; i < RTETH_RX_RING_SIZE; i++) { - packet = &ctrl->rx_data[r].packet[i]; + frag = &ctrl->rx_data[r].frag[i]; page = page_pool_dev_alloc_frag(ctrl->rx_info[r].pool, &offset, PPOOL_FRAG_SIZE); if (!page) { @@ -708,15 +708,15 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) highmem |= PageHighMem(page); paddr = max(paddr, page_to_phys(page)); - packet->size = SKB_FRAG_SIZE; - packet->dma = page_pool_get_dma_addr(page) + frag->size = SKB_FRAG_SIZE; + frag->dma = page_pool_get_dma_addr(page) + ctrl->r->skb_headroom + offset; ctrl->rx_info[r].page[i] = page; ctrl->rx_info[r].offset[i] = offset; ctrl->rx_data[r].ring[i] = ctrl->rx_dma + sizeof(struct rteth_rx_data) * r + - offsetof(struct rteth_rx_data, packet) + - sizeof(struct rteth_packet) * i + + offsetof(struct rteth_rx_data, frag) + + sizeof(struct rteth_frag) * i + RING_OWN_HW; } @@ -730,8 +730,8 @@ static int rteth_setup_ring_buffer(struct rteth_ctrl *ctrl) ctrl->tx_info[r].skb[i] = NULL; ctrl->tx_data[r].ring[i] = ctrl->tx_dma + sizeof(struct rteth_tx_data) * r + - offsetof(struct rteth_tx_data, packet) + - sizeof(struct rteth_packet) * i; + offsetof(struct rteth_tx_data, frag) + + sizeof(struct rteth_frag) * i; } ctrl->tx_data[r].ring[RTETH_TX_RING_SIZE - 1] |= RING_WRAP; @@ -1057,8 +1057,8 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) { int port, val, slot, len = skb->len, ring = skb_get_queue_mapping(skb); struct rteth_ctrl *ctrl = netdev_priv(dev); - struct rteth_packet *packet; struct sk_buff **packet_skb; + struct rteth_frag *frag; dma_addr_t packet_dma; port = rteth_get_dsa_port(skb, dev); @@ -1074,7 +1074,7 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) } slot = ctrl->tx_info[ring].slot; - packet = &ctrl->tx_data[ring].packet[slot]; + frag = &ctrl->tx_data[ring].frag[slot]; packet_dma = ctrl->tx_data[ring].ring[slot]; packet_skb = &ctrl->tx_info[ring].skb[slot]; @@ -1088,22 +1088,22 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) if (likely(*packet_skb)) { /* cleanup old data of this slot */ - dma_unmap_single(&ctrl->pdev->dev, packet->dma, (*packet_skb)->len, DMA_TO_DEVICE); + dma_unmap_single(&ctrl->pdev->dev, frag->dma, (*packet_skb)->len, DMA_TO_DEVICE); dev_consume_skb_any(*packet_skb); } *packet_skb = skb; - packet->len = len; - packet->dma = dma_map_single(&ctrl->pdev->dev, skb->data, len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(&ctrl->pdev->dev, packet->dma))) { + frag->len = len; + frag->dma = dma_map_single(&ctrl->pdev->dev, skb->data, len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(&ctrl->pdev->dev, frag->dma))) { dev->stats.tx_errors += rteth_free_skb(packet_skb); return NETDEV_TX_OK; } if (port >= 0) - ctrl->r->create_tx_header(packet, port, 0); // TODO ok to set prio to 0? + ctrl->r->create_tx_header(frag, port, 0); // TODO ok to set prio to 0? - /* Hand packet over to switch */ + /* Hand frag over to switch */ dma_wmb(); ctrl->tx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; ctrl->tx_info[ring].slot = (slot + 1) % RTETH_TX_RING_SIZE; @@ -1132,12 +1132,12 @@ static int rteth_start_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, int slot) { - struct rteth_packet *packet = &ctrl->rx_data[ring].packet[slot]; + struct rteth_frag *frag = &ctrl->rx_data[ring].frag[slot]; unsigned int offset = ctrl->rx_info[ring].offset[slot]; struct page *page = ctrl->rx_info[ring].page[slot]; struct page_pool *pool = ctrl->rx_info[ring].pool; struct net_device *dev = ctrl->dev; - unsigned int len = packet->len; + unsigned int len = frag->len; struct sk_buff *skb; struct dsa_tag tag; @@ -1152,7 +1152,7 @@ static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, int s skb_mark_for_recycle(skb); skb_put(skb, len); - ctrl->r->decode_tag(packet, &tag); + ctrl->r->decode_tag(frag, &tag); if (netdev_uses_dsa(dev)) { if (tag.port < ctrl->r->cpu_port) skb_dst_set_noref(skb, &ctrl->dsa_meta[tag.port]->dst); @@ -1172,12 +1172,12 @@ static struct sk_buff *rteth_create_skb(struct rteth_ctrl *ctrl, int ring, int s static int rteth_append_skb(struct sk_buff *skb, struct rteth_ctrl *ctrl, int ring, int slot) { - struct rteth_packet *packet = &ctrl->rx_data[ring].packet[slot]; + struct rteth_frag *frag = &ctrl->rx_data[ring].frag[slot]; unsigned int offset = ctrl->rx_info[ring].offset[slot]; struct page *page = ctrl->rx_info[ring].page[slot]; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; struct page_pool *pool = ctrl->rx_info[ring].pool; - unsigned int len = packet->len; + unsigned int len = frag->len; if (nr_frags >= MAX_SKB_FRAGS) { page_pool_put_full_page(pool, page, true); @@ -1196,7 +1196,7 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) int slot, work_done = 0, rx_packets = 0, rx_bytes = 0, rx_dropped = 0, rx_errors = 0; struct rteth_ctrl *ctrl = netdev_priv(dev); unsigned int len, new_offset; - struct rteth_packet *packet; + struct rteth_frag *frag; struct page_pool *pool; struct page *new_page; dma_addr_t packet_dma; @@ -1215,10 +1215,10 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) if (packet_dma & RING_OWN_HW) break; - packet = &ctrl->rx_data[ring].packet[slot]; - len = packet->len; + frag = &ctrl->rx_data[ring].frag[slot]; + len = frag->len; is_head = is_tail; - is_tail = !packet->more; + is_tail = !frag->more; if (is_tail) work_done++; @@ -1270,8 +1270,8 @@ static int rteth_hw_receive(struct net_device *dev, int ring, int budget) ctrl->rx_info[ring].page[slot] = new_page; ctrl->rx_info[ring].offset[slot] = new_offset; - packet->dma = page_pool_get_dma_addr(new_page) - + new_offset + ctrl->r->skb_headroom; + frag->dma = page_pool_get_dma_addr(new_page) + + new_offset + ctrl->r->skb_headroom; recycle: dma_wmb(); ctrl->rx_data[ring].ring[slot] = packet_dma | RING_OWN_HW; diff --git a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h index 052dcf21ef..51692c7788 100644 --- a/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h +++ b/target/linux/realtek/files-6.18/drivers/net/ethernet/rtl838x_eth.h @@ -184,7 +184,7 @@ struct p_hdr; struct dsa_tag; struct rteth_ctrl; -struct rteth_packet; +struct rteth_frag; struct rteth_config { int cpu_port; @@ -209,8 +209,8 @@ struct rteth_config { int skb_headroom; u32 mac_reg[RTETH_MAX_MAC_REGS]; int l2_tbl_flush_ctrl; - void (*create_tx_header)(struct rteth_packet *h, unsigned int dest_port, int prio); - bool (*decode_tag)(struct rteth_packet *h, struct dsa_tag *tag); + void (*create_tx_header)(struct rteth_frag *frag, unsigned int dest_port, int prio); + bool (*decode_tag)(struct rteth_frag *frag, struct dsa_tag *tag); void (*hw_en_rxtx)(struct rteth_ctrl *ctrl); void (*hw_init)(struct rteth_ctrl *ctrl); void (*hw_stop)(struct rteth_ctrl *ctrl); From b5191da03cf4864416afee8ef5542e1ffe596d41 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 21 Jul 2026 03:28:58 +0200 Subject: [PATCH 152/152] rpcd: update to Git HEAD (2026-07-19) e37ed9d81469 file: re-authorize ACL against resolved path to close symlink bypass Link: https://github.com/openwrt/openwrt/pull/24329 Signed-off-by: Hauke Mehrtens --- package/system/rpcd/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/system/rpcd/Makefile b/package/system/rpcd/Makefile index b0b3128838..a08ac888d0 100644 --- a/package/system/rpcd/Makefile +++ b/package/system/rpcd/Makefile @@ -12,9 +12,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/rpcd.git -PKG_MIRROR_HASH:=b15b42e9f27a35030f488d7a61a3b2f505f79b5675455b20f1336acb1d99686a -PKG_SOURCE_DATE:=2026-06-04 -PKG_SOURCE_VERSION:=28faf6403792d25b9826043aaf37880624c19568 +PKG_MIRROR_HASH:=ca2498a25c69e6b6e8fe1bcc5865a244e409674b1ac2e2e36c0e4bb04b11aba8 +PKG_SOURCE_DATE:=2026-07-19 +PKG_SOURCE_VERSION:=e37ed9d814699098eb7e26c8b33c054840782dfb PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=ISC