spi-tx-buswidth / spi-rx-buswidth is not a property Linux's SPI core
reads; of_spi_parse_dt only recognizes the hyphenated
spi-tx-bus-width / spi-rx-bus-width, so these lines have never done
anything on the Unielec U7981-01-NAND or the Zbtlink Z8102AX-v2.
Renaming these to the form the kernel reads would engage quad I/O for
real, which needs a boot on the actual hardware to confirm the wiring
carries it - neither has that test yet. Removing the dead lines
instead keeps the tree honest about what each flash access actually
does, with no behavior change. The rename becomes its own patch once
someone with the hardware can test it.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
No test on this hardware, but the same community U-Boot source that
confirms the Livinet Li320 wiring carries this board too, with
support_quad set on the controller and the same typo in its own DTS.
Yuzhii0718/bl-mt798x-dhcpd, uboot-mtk-20250711/arch/arm/dts/mediatek/
openwrt/mt7986b-zyxel_wx5600-t0.dts.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The node already sets support_quad on the controller; the misspelling
was keeping the bootloader's flash access at 1-bit regardless.
The package had no PKG_RELEASE, so this adds one to rebuild the
bootloader for the boards that carry these patches.
Tested-by: Andrew Sim <andrewsimz@gmail.com>
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
No test on this hardware, but a community U-Boot build for this board
sets support_quad on the SPI controller and carries the same typo in
its own DTS, which means the wiring already runs quad under U-Boot on
real units.
Yuzhii0718/bl-mt798x-dhcpd,
uboot-mtk-20250711/arch/arm/dts/mt7981-livinet-li320.dts.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
No test on this hardware, but Keenetic's own build system carries the
correctly spelled property for all three: KN-3711, KN-3911, and the
shared kap-630 dtsi (which also covers the Netcraze NAP-630 rebadge).
Their vendor firmware has been running these boards in quad mode.
keenetic/keenetic-sdk, target/linux/mt7981/dts/KN-3711.dts,
KN-3911.dts and KAP-630.dts (KeeneticOS build system).
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Reading the NAND goes from 4.71 to 11.39 MiB/s on this board. One line
at the declared 52 MHz cannot carry more than 6.2 MiB/s, so the higher
figure is only reachable with more than one line in use.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
NAND read throughput before and after, dd against the same partition
on each board:
AX3000Q: 14.58s -> 6.85s
AX3000SE: 14.59s -> 6.89s
AX3000SM: 14.61s -> 6.86s
AX7800M-6E: 14.71s -> 6.95s
Tested-by: Donghyun Ko <nyankosoftware@gmail.com>
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Confirmed on my own KN-3811, following the same NAND throughput check
described in the PR thread: the flash reads noticeably faster than the
~6.5 MB/s ceiling that 1-bit mode caps out at, at 52 MHz.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The &spi0 spi_nand@0 node (runtime spi1.0, the rutos-a/rutos-b
partitions) carried the same misspelled spi-tx-buswidth /
spi-rx-buswidth as the NOR flash fixed in the previous commit. Kept
separate since it's a different chip on a different bus, with its own
evidence.
Confirmed on hardware: dd against mtd9 (rutos-a) reads 64 MiB in
5.73 s where it took 13.54 s before.
Tested-by: Simonas Tamošaitis <simsasss@gmail.com>
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The &spi2 flash@0 node is jedec,spi-nor, not SPI-NAND, and used the
same misspelled spi-tx-buswidth / spi-rx-buswidth. Enabling quad here
is a behavioral change on first boot, not a no-op rename: the SPI NOR
core sets the non-volatile Quad Enable bit in the flash status
register, repurposing the /WP and /HOLD pins as IO2/IO3. The values
restore what the DTS already specified; only the property name was
wrong. Kept as a separate commit so this node can be reverted
independently if the board's wiring does not support quad mode.
Confirmed on hardware: /sys/kernel/debug/spi-nor/*/params shows the
read protocol switch from 1S-1S-1S with opcode 0x03 to 1S-4S-4S with
0xeb, and the config partition, which carries the board MAC, still
reads back correctly.
Tested-by: Simonas Tamošaitis <simsasss@gmail.com>
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Link: https://github.com/openwrt/openwrt/pull/24809
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Add support for the PCIe required to make the PCIe Gen3 Controller work for
Airoha AN7583 SoC. To correctly work both the PCIe PHY and the QPHY needs
to be configured or PCIe card will fail to train and detect.
Automatically refresh any affected patch.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This reverts commit 53a4aa8a57 and
16fdb144e2.
The wrong commit title/description was pushed by mistake.
Revert to have a more organized commit history.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Add support for the PCIe required to make the PCIe Gen3 Controller work for
Airoha AN7583 SoC. To correctly work both the PCIe PHY and the QPHY needs
to be configured or PCIe card will fail to train and detect.
Automatically refresh any affected patch.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
The tc flower parse / add / configure / delete / stats path still logged
through pr_debug() / pr_err() / pr_info() with a bare "%s"/__func__
prefix. Every one of these functions already has the switch priv in
scope, so switch them to dev_dbg() / dev_err() on priv->dev and replace
the __func__ tag with a short message describing what is happening. The
one netdev_info() in rtl83xx_parse_fwd() loses its __func__ tag the same
way.
No functional change.
Assisted-by: Claude Code (Anthropic Claude Sonnet 5)
Signed-off-by: Mark Abe <github@mab.wien>
Link: https://github.com/openwrt/openwrt/pull/24994
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
rtl83xx_setup_tc() and its helpers rtl83xx_setup_tc_block_cb() /
rtl83xx_setup_tc_cls_flower() form a TC_SETUP_BLOCK dispatcher meant to
be reached through .ndo_setup_tc -> rteth_setup_tc() -> ds->ops->
port_setup_tc(). Nothing in target/linux/realtek ever sets
port_setup_tc, and rtl83xx_setup_tc() is assigned to no ops struct
either, so rteth_setup_tc() returns -EOPNOTSUPP and this whole path is
dead.
The cls_flower offload runs entirely through the DSA .cls_flower_add /
_del / _stats switch ops, i.e. rtldsa_pie_cls_flower_{add,del,stats}()
-> rtldsa_{configure,delete,stats}_flower(). Keeping the second,
unreachable dispatcher only invites a reader to mistake it for a live
path.
Remove it. With it gone rtldsa_configure_flower() has a single caller,
rtldsa_pie_cls_flower_add(), which is RTL930x-only and always passes a
real ingress port, so the ingress_port >= 0 guard and the
!pie_rule_id_is_log_counter branch (the rtldsa_packet_cntr_alloc() /
log_data path) are dead too - drop them. That also makes
rtldsa_packet_cntr_release() a single-SoC wrapper whose
rtldsa_packet_cntr_free() half never runs, so fold it back into plain
rtldsa_packet_cntr_clear() calls. rtldsa_tc_init() / rtldsa_tc_cleanup()
stay; they run from the probe path.
Reported-by: Gennaro Cimmino <gcimmino@rayonra.net>
Assisted-by: Claude Code (Anthropic Claude Sonnet 5)
Signed-off-by: Mark Abe <github@mab.wien>
Link: https://github.com/openwrt/openwrt/pull/24994
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
With tc_flow_lock serializing rtldsa_configure_flower(),
rtldsa_delete_flower() and rtldsa_stats_flower(), and a flow only ever
freed (via kfree_rcu) from rtldsa_delete_flower() / rtldsa_tc_cleanup(),
a looked-up flow cannot be freed while a callback holds tc_flow_lock. The
hand-rolled rcu_read_lock() sections around the rhashtable lookups are
therefore redundant:
- rhashtable_lookup_fast() / _insert_fast() / _remove_fast() already
take rcu_read_lock() internally;
- nothing dereferences a looked-up flow outside tc_flow_lock.
Switch configure_flower() from rhashtable_lookup() to the _fast variant
and drop the explicit rcu_read_lock() / rcu_read_unlock() in all three
callbacks. No functional change.
Assisted-by: Claude Code (Anthropic Claude Sonnet 5)
Signed-off-by: Mark Abe <github@mab.wien>
Link: https://github.com/openwrt/openwrt/pull/24994
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
rtl930x_packet_cntr_read() and rtl930x_packet_cntr_clear() always
addressed the LOG table as counter/2, with the low bit selecting one of
the two 32-bit words in the 64-bit entry. That layout only applies to
the L3 route statistics counters that rtldsa_packet_cntr_alloc() hands
out above the PIE rule ID range (base = n_pie_blocks * PIE_BLOCK_SIZE).
A PIE rule instead logs its matched-packet count into the LOG table
entry that carries its own rule ID, in data word 1. Because the old code
folded the rule ID in half, only rule ID 0 read a sane value; every
other offloaded tc-flower rule reported a bogus or zero "tc -s filter"
hardware packet count, and clearing one rule's counter disturbed the
neighbouring rule.
Split the two cases on the rule ID / route counter boundary: address PIE
rule counters directly at entry = id, word 1, and keep the paired
counter/2 read-modify-write for the route counters.
Verified on an RTL9302C (Zyxel XGS1210-12) with several concurrent
skip_sw flower rules: each now reports its own increasing packet count.
Assisted-by: Claude Code (Anthropic Claude Sonnet 5)
Signed-off-by: Mark Abe <github@mab.wien>
Link: https://github.com/openwrt/openwrt/pull/24994
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Offload ingress tc-flower rules on RTL930x DSA user ports to the Packet
Inspection Engine, on top of the port rate-policing path already wired to
the RTL93xx cls_flower ops.
- rtldsa_cls_flower_{add,del} route every rule that is not a plain port
rate limiter to rtldsa_pie_cls_flower_*(), and a new
rtldsa_cls_flower_stats op exposes per-rule packet counts.
- The offload is gated on a new rtldsa_config flag
(pie_rule_id_is_log_counter), set for RTL930x only, so RTL931x keeps
its current behaviour and the RTL83xx SoC code is untouched.
- Generic EtherType matches (frame_type 1) are added on top of the
ARP/IPv4/IPv6 fast paths, and rtl930x_pie_verify_template() gains an
EtherType arm so a rule that needs TEMPLATE_FIELD_ETHERTYPE skips a
block that lacks it instead of matching on a stale field.
- The rule is bound to the physical ingress port via the source-port
field. On RTL930x the PIE rule ID is also the LOG counter ID, so no
separate counter is allocated; it is cleared when the rule is added.
rtldsa_packet_cntr_alloc() keeps the PIE rule ID range reserved on
these SoCs so an L3 route counter cannot alias a flow's LOG entry.
- rtldsa_rate_policy_extract() now bails out on a non-FLOW_ACTION_POLICE
rule before flow_action_basic_hw_stats_check(), so a rule that falls
through to the PIE path does not carry a stale "HW stats type
unsupported" extack that tc would print as a warning.
Tested on a Zyxel XGS1210-12: ingress flower rules matching EtherType
(PPPoE discovery), ARP, and a UDP destination port install with in_hw
set, "tc -s filter show" reports hardware packet counts, and removing the
rules leaves no residue.
Assisted-by: Claude Code (Anthropic Claude Sonnet 5)
Signed-off-by: Mark Abe <github@mab.wien>
Link: https://github.com/openwrt/openwrt/pull/24994
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The fill loop is what regmap_bulk_write() does. On this map it takes the
same path: the mmio bus offers only reg_write, so bulk_write walks the
registers one at a time with the same stride, without the copy and the
byte swap its raw path would do. It takes the map lock once for the
window rather than once per register, and it stops at the first failure,
which is what the loop already did.
The read side of the same window was converted in the previous commit.
Compile-tested on realtek/rtl930x, realtek/rtl931x, realtek/rtl838x and
realtek/rtl839x. Not run on hardware; the RTL9303 exercises this path on
every VLAN and port isolation change, so it is worth a boot before this
is applied.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25085
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
__otto_table_fetch() left the entry in the data registers and handed out
single words from them: the one read path that does not copy into the
caller's buffer, and the one with no error channel, since
__otto_table_word_read() returns the data word itself. Its word index
was clamped into the register window rather than checked, so an index
outside the entry quietly returned a different, valid counter.
Give __otto_table_read_bytes() a word_offset into the entry to start at,
and let regmap_bulk_read() take the window from there. The transfer size
stays the caller's, so one or two words can come out of a wide row, and
the whole-entry macros pass zero. The check that size equal the entry
width becomes a bound, because the read forms its address arithmetically
and the map covers the whole switch register block: a word past the
entry is a successful read of a foreign register. The write helper keeps
the equality and gets no offset of its own, since the command commits
the whole data window. A read therefore no longer catches an object
narrower than the entry, and a mis-sized read-modify-write is refused at
the write instead. struct otto_table.max_data goes too; the clamp was
its only reader.
rtldsa_931x_stat_port_table_read() was the only caller of the deleted
path, and asks for the same words as before: field_offset - mib_offset
for a 32 bit counter, and two words from field_offset - (mib_offset + 1)
for a 64 bit one. That descending order is the SDK's own, since
table_field_get() in src/hal/mac/mem.c walks down from datareg_num - 1 -
(lsp >> 5), so the more significant half sits at the lower word index.
A failed command returns before the copy, so a refused read hands back
the zeroed buffer rather than whatever the data window held, which is
what fetch() achieved by pre-zeroing the registers.
Compile-tested on realtek/rtl930x, realtek/rtl931x, realtek/rtl838x and
realtek/rtl839x. Not run on hardware yet. RTL931x holds the only caller
that asks for a non-zero word and no board is available here, so the
word index each of the 68 MIB entries resolves to was computed before
and after the change instead, and the two sets are identical. An offset
that is wrong but still inside the table returns a plausible counter
rather than a symptom, so ethtool -S would not necessarily show it.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25085
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
otto_table_exec() wrote the command register without looking at what was
already in it. Poll the execute bit first, the way otto_emdio_run_cmd()
does in drivers/net/mdio/mdio-realtek-rtl9300.c, so that a command left
in flight is waited for instead of overwritten. Log a timeout there:
unlike the mdio bus, no caller of these helpers reads the return value.
The timeout that was already there logged through __func__ and without a
limit, which the RTL931x counter poll would turn into a flood, so it now
names the table in the same shape.
Compile-tested on realtek/rtl930x, realtek/rtl931x, realtek/rtl838x and
realtek/rtl839x; not run on hardware. It is not expected to fire on a
healthy switch, since a table command completes long inside the timeout
and the poll returns on its first read. The execute bit is not ours
alone, though: rtl838x_eth.c sets it on 0x1180 and 0x6914 outside this
lock and without waiting for the command to finish.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25085
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The tables are a property of the switch block, not of the DSA driver
that happens to be their busiest user. The ethernet driver reaches the
same command and data registers through its own handle on the same
syscon, with no lock between them, and says so at rtl838x_eth.c: "this
is an unprotected table access".
Move the code to drivers/soc/realtek and give it the node added in the
previous commit. probe() takes the register map from the parent syscon,
so the six accesses become regmap_read() and regmap_write() at the same
offsets: sw_r32() was __raw_readl(RTL838X_SW_BASE + reg), and
RTL838X_SW_BASE is the KSEG1 alias of the switch0 reg the syscon covers,
which is the map rtl838x_eth.c already drives those registers through.
Only the pointer is the driver's own; consumers of the table API never
see it, and keep naming a table by its id.
A consumer asks otto_table_loaded() before its first access. It answers
0 once the map is published, -EPROBE_DEFER while the node is there but
unbound, and -ENODEV when the devicetree has no node at all, so a switch
that cannot reach its tables refuses to probe with a message instead of
running with tables that answer nothing. otto_table_reg(), which every
accessor already passes through, warns and refuses if it is reached
before the map exists, so a missed check cannot dereference it.
The sources are an overlay, so only the two lines that let the kernel
descend into drivers/soc/realtek need a patch, the same split
318-add-rtl83xx-clk-support.patch uses.
What this costs: every table word now goes through regmap rather than
__raw_readl, which adds a spinlock and an indirect call per 32-bit
access. That was not measured. It falls hardest on the RTL931x counter
poll, the one family that reads its MIB counters through a table.
otto_map is published with smp_store_release() and read back with
smp_load_acquire(), so a consumer that sees the driver as up also sees
the per-register mutexes that probe initialised: they are zeroed in the
image and only become real mutexes there. The table helpers propagate
what the regmap layer returns. The read helpers still clear their output
and copy the whole data window, while filling the write window stops at
the first error, so a partial row is never committed.
__otto_table_word_read() returns the data word itself and has no error
channel, so it logs and keeps handing back the zeroed word. With
the current devicetree none of these branches can be taken: the map is
an mmio syscon with no clock attached, and every address the driver
forms is 4 byte aligned and inside the mapped window. This is a
robustness convention, not a fix for a failure that was observed.
Compile-tested on realtek/rtl930x, realtek/rtl931x, realtek/rtl838x
and realtek/rtl839x.
RAM-booted twice on an RTL9303, a Hasivo S1100W-8XGT-SE. The provider
binds: 1b000000.ethernet-switch:table shows up under the driver,
devices_deferred stays empty, and dmesg carries no WARNING, so the guard
in otto_table_reg() never fired. The switch comes up with all eight
ports and forwards. The tables hold what they should: the L2 table lists
60 and 81 entries across the two boots with the port MACs intact, and
the VLAN table reads back VID 0 with every port tagged and VID 1 with
member mask 0x1f110101, untagged 0x0f110101. Both come from the debugfs
dumps, so every word of that was read out of the data registers through
the new regmap path.
RTL838x, RTL839x and RTL931x cannot be tested here, and for them this is
not a no-op: the switch now depends on a provider that must bind. The
node is a child of the same syscon with the same reg on all four
families, which is the reason to expect it to work, not evidence that it
does. The -ENODEV and -EPROBE_DEFER arms are unexercised as well, since
drivers/soc is linked before drivers/net and the gate answers 0 on the
first try.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25085
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The switch tables are reached through command and data registers inside
the switch0 block, and the code that drives them is about to move out of
the DSA driver into a provider of its own. Give that provider a node to
bind.
The node carries no reg. The command and data registers are several
separate spans in each family rather than one block, and the provider
reaches them the way the rest of the switch code does. Its compatible
pairs the family string with a generic one, as the pcs and mdio children
already do, so a consumer can find the node without naming the SoC.
Nothing binds it yet, so on its own this changes nothing.
Compile-tested: make target/linux/dtb for realtek/rtl930x emits the same
24 warnings before and after, none of them about the new node.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25085
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This reverts commit 8939d5654a ("qualcommax: pcs-qca-uniphy: report a
USXGMII speed only once the link is up").
The link bit of the USXGMII code word is not a validity gate for the
rest of the word. An AQR113C behind this XPCS leaves the bit clear while
autonegotiation completes and the speed field tracks the copper rate,
which is why dd78f34eba ("qualcommax: pcs-qca-uniphy: take the USXGMII
link from the receiver") took the link out of it.
Gating on the bit again leaves the 10G ports without carrier even though
the receiver reports link up and the code word carries the resolved rate.
Reported on a QNAP QHora-301w and a Zyxel NBG7815.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25119
Signed-off-by: Robert Marko <robimarko@gmail.com>
Backport v7.3 phylink patches to the generic target required for the
latest fwnode phylink PCS patches which support multiple (sub-)targets,
including MTK and qualcommax.
This update includes better PCS release logic, switch to full notifier
add/release PCS handling, devres managed PCS provider variant and
general code improvements.
In addition, update every user to the latest fwnode phylink PCS API.
In the latest API, fwnode_pcs_add_provider() returns a pointer to
the provider or the error code instead of the previous plain return
value. As such, update error handling and add a descriptive error
message upon failure. Also the provider remove logic changed and with
the support of devres API, switch every user to devm_ variant.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
[ improve commit description, refresh patch, update any driver to new API]
Co-Developed-by: Christian Marangi <ansuelsmth@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/24973
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
49ab6793662a ipq5018: add BDFs for Wallys DR5018S
04dff0ab979f qca9888: add BDF for TP-Link Archer C60 v3
Signed-off-by: Robert Marko <robimarko@gmail.com>
The LAN LEDs of the EN7528 boards are wired to the LED pins of the GPHYs
sitting behind the switch ports, but were described as plain GPIOs, so
they were left under software control and stayed dark.
Add the LED nodes to the GPHYs of the SoC .dtsi, mux the LED pins to the
matching phyN_ledM function of the pin controller, and replace the
gpio-leds nodes of the LAN LEDs with references to the PHY LEDs. The
LEDs are then driven by the PHY itself, without any CPU involvement.
The JioFiber boards have both LED pins of every GPHY wired up, giving a
bicolour LED per socket. The H660GM-A boards only have the LED1 pins
wired, as the LED0 pins are used as GPIOs for the other LEDs and for the
WPS button; the amber LEDs of the generic variant stay plain GPIOs.
Add board.d/01_leds to hook the LEDs up to the netdev trigger, which the
PHY driver offloads to the hardware.
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25098
Signed-off-by: Robert Marko <robimarko@gmail.com>
The EcoNet EN7528 embeds four Gigabit Ethernet PHYs (PHY ID 0x03a29491)
behind its built-in MT7530 switch. Without a driver of their own they
fall back to the generic PHY driver, which cannot drive the LED pins of
the PHYs.
Backport the upstream driver support and enable MEDIATEK_GE_SOC_PHY.
The PHYs use the same LED register layout as the other SoC PHYs handled
by that driver, but their LED controller powers up with its external
control disabled, so the driver enables it from config_init.
Backported from upstream commit
https://github.com/torvalds/linux/commit/b6e2649fff15a17689851b30e5415a3aae2b516e
Signed-off-by: Ahmed Naseef <naseefkm@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25098
Signed-off-by: Robert Marko <robimarko@gmail.com>
CONFIG_RUSTC_HAS_SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS is autodetected
based on rustc version, so filter it out of target configs.
Signed-off-by: Robert Marko <robimarko@gmail.com>
TP-Link Archer AX55 v1 is an IPQ5018 Wi-Fi 6 router.
Hardware:
SoC: Qualcomm IPQ5018 (dual Cortex-A53)
RAM: 512 MiB
Flash: 128 MiB SPI-NAND (dual firmware slots)
Switch: Realtek RTL8367S (5x GbE), 2.5G HSGMII trunk to gmac1
WiFi: IPQ5018 2.4 GHz + QCN6122 5 GHz (ath11k)
USB: 1x USB 3.0 port, SuperSpeed, using the IPQ5018 SS uni PHY;
the port's 5 V rail is a GPIO-switched regulator wired as the
PHY's vdd-supply, the way the MR5500 does it
Button: Reset, WPS/Wi-Fi
LED: power, LAN, WAN (green/orange), 2.4G, 5G, USB
All five front jacks are RTL8367S ports (DSA): blue "WAN" = wan,
LAN1-4 = lan1..lan4, over the 2.5G HSGMII trunk.
The factory MAC is the "default-mac" file in the tp_data UBIFS volume
(there is no raw nvmem cell for it). The volume is attached and mounted
read-only during preinit, so board.d assigns the wired MACs (label MAC
for lan/eth0, +1 for wan) the usual way and the ath11k caldata hotplug
derives the Wi-Fi MACs (+2/+3) from the same mount - the same scheme
the mt7981 archer-ax80-v1 uses for its tp_data. gmac1 has no nvmem MAC
source, so the DSA conduit is pinned to the label MAC in board.d to
keep it stable across boots.
The board data files live in firmware_qca-wireless, where they were
merged as f2c37a6b9d1d; this commit pins the ipq-wifi package to that
revision and adds the package entry for the board.
Note on revisions: some units labelled "v1" carry an RTL8367D-family
switch (chip id 0x6642) instead of the RTL8367S. rtl8365mb cleanly
declines it ("unrecognized switch"), so such a unit boots with no wired
ports, and with Wi-Fi off by default it is only reachable via the
vendor web recovery (or serial). RTL8367D support in rtl8365mb is being
worked on by others; until it lands the wiki page carries a prominent
warning to identify the switch before flashing. v2/v4 are entirely
different hardware and are not covered by this image.
Installation (serial console on the JP1 header, 115200 8N1, 1.8 V; note
the RX trace is gapped and must be bridged to type): interrupt U-Boot,
TFTP-boot the initramfs image to 0x44000000, "bootm", then "sysupgrade -n".
A serial-free path (a telnet-enabled resigned stock image flashed from the
vendor web UI, then writing the OpenWrt image to the inactive slot) is
documented on the device wiki page; that path consumes the
initramfs-factory.ubi artifact, which is the initramfs kernel wrapped in
a UBI image so it can be written straight into the inactive rootfs
partition with mtd from the running stock firmware.
Recovery to stock: hold Reset while powering on, set the PC to
192.168.0.10, and upload an official signed TP-Link image at
http://192.168.0.1.
Signed-off-by: Stanislaw Pal <kuncy7@gmail.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Link: https://github.com/openwrt/openwrt/pull/24197
Signed-off-by: Robert Marko <robimarko@gmail.com>
qca_uniphy_pcs_get_state_usxgmii() reads the speed out of the USXGMII code
word without consulting the link bit beside it, so a word the partner has
not filled in yet is decoded as a speed and handed to phylink as a resolved
link. The field is read into state->an_complete and never used again.
Report no link until that bit is set, which is the shape mainline's
xpcs_get_state_c73() has for the same word:
drivers/net/pcs/pcs-xpcs.c, xpcs_get_state_c73():
state->an_complete = xpcs_aneg_done_c73(xpcs, state, ...);
if (!state->an_complete) {
state->link = false;
return 0;
}
This arm needs a USXGMII partner and the bench has no 10G port, so it is
not runtime tested here.
Fixes: 05c180511d ("qualcommax: add UNIPHY PCS driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
A transmit completion wakes a stopped queue, so the poll is the second
writer of the queue state that the MTU change stops. netif_tx_disable()
ran first, and a completion landing after it woke the queue and scheduled
the qdisc; napi_disable() then returned as soon as the poll cleared its
scheduled bit, while the poll thread was still inside the local_bh_enable()
that runs the transmit softirq it had queued - against the rings the drain
below frees.
Put the poll down first.
Measured on an ipq807x AX3600: sixteen mtu changes across the receive page
-order boundary, run under an eight-stream transfer, complete with the
transfer carrying 763 Mbit/s through them, the interface up, every switch
port up and the log clean.
Fixes: bdb0b722b7 ("qualcommax: add EDMA driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
The queue is stopped from a consumer index read before the descriptor was
published, and nothing re-reads it afterwards. A completion that drains the
ring in that window runs the poll while the queue still reads as running,
so the poll does not wake it, and the stop that follows has no completion
left behind it: the interface stops transmitting until it is taken down.
netif_txq_try_stop() carries the barrier and the recheck. The indices the
frame was placed from still decide whether to stop at all, since a consumer
index only ages into reporting less room than the ring has, so a frame that
does not fill the ring costs no extra register read.
A refused frame reaches the same window from the other side. Both
NETDEV_TX_BUSY returns leave a ring the engine is still draining, and the
queue was stopped by the caller after the transmit lock had been dropped,
with no recheck at all. The refusals stop the queue themselves, under the
lock and on the state they were decided from, and the caller no longer
touches the queue state.
The two refusals do not name the same resource. A free descriptor count
measures a full ring, but a taken store slot outlives the descriptor that
named it: the engine releases a descriptor as soon as it reads it, while
only the completion clears the slot. A recheck taken from the descriptors
alone restarts the queue on a resource the refused frame still lacks, and
the frame returns to a running queue, which turns the refusal into a retry
rather than back pressure. The recheck reports no room while the slot is
taken.
The completion side takes the same count against the same threshold
through __netif_txq_completed_wake(), which carries the barrier the recheck
pairs with and reports the completed frames to BQL. That wake is a second
writer of a queue state the control path also stops, so it is held back on
a ring drain, which runs with the queue deliberately stopped and the rings
about to be freed and is not a poll.
Measured on an ipq807x AX3600: three 20 s sixteen-stream transfers through
this ring carry 818, 853 and 824 Mbit/s with no frame dropped on the
interface, the queue running at the end, every switch port up and the log
clean.
Fixes: bdb0b722b7 ("qualcommax: add EDMA driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
edma_clean_tx() passes its loop bound to napi_consume_skb() as the NAPI
budget. A non-zero budget tells that helper it is running inside a poll and
frees the frame into the per-CPU NAPI cache, which is only safe from
softirq context. edma_rings_drain() calls the same routine from process
context with INT_MAX - on an MTU change that alters the receive page order,
and on the probe error and remove paths - so a transmit completion on the
same CPU can interleave with the cache update.
The budget the poll was given becomes an argument of its own, zero on the
drain, which routes those frees through dev_consume_skb_any() instead.
Fixes: bdb0b722b7 ("qualcommax: add EDMA driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
The transmit routine prepends the 32-byte preheader and only then tests
whether the descriptor's slot still holds an uncompleted frame. A frame
refused there goes back to the qdisc already carrying the preheader, and
the requeue pushes a second one: the descriptor names a length that no
longer describes the frame, and the headroom the push consumed is gone.
Both refusals now precede the push, where the ring-full test already sat.
The slot test depends on nothing between the two points, so the accepted
path is unchanged.
Fixes: bdb0b722b7 ("qualcommax: add EDMA driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
A frame below the transmit minimum is padded with skb_padto(), which zero
-fills the tailroom and returns without moving the tail, and the length is
then advanced by hand. skb->len therefore runs past the data a head
reallocation copies: pskb_expand_head(), three lines below and reached
whenever the frame arrives cloned or short of headroom, copies only as far
as the tail and leaves the pad uninitialised in the new head. The
descriptor still names the padded length, so those bytes go on the wire.
skb_put_padto() advances the tail with the length, which is the whole
difference. It also folds in the length test the call site made, since it
no-ops on a frame that is already long enough.
Fixes: bdb0b722b7 ("qualcommax: add EDMA driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
The engine has sixteen receive rings and this driver brings up exactly one,
the last. Which ring a frame lands in is chosen by the QID2RID table, which
the driver wrote one word of and left the rest of at whatever the block
resets to. Ring zero, the ring a zeroed id names, is never given a base
address and its enable bit is cleared by hardware stop, so any queue whose
id was left at zero points somewhere that cannot deliver.
Fill every word of the table with the ring the per-SoC data names repeated
in each four-bit field, so the value is right whichever field a queue's id
selects. The write is monotone, which is what makes it safe without either
the reset value or the field packing in hand: the enabled ring is the only
place a frame can be delivered, so a queue that delivers today already
resolves to it and is written the value it already had, and a queue that
does not is pointing somewhere that cannot deliver. No frame that arrives
today changes ring.
The table's extent is measured; its packing is not. On IPQ8074 words 0 to
63 hold the value the driver writes and the words sampled above them read
zero, and word 63 holds an arbitrary 32-bit pattern written to it, so the
fill covers the whole table and no bit of a word is hardwired. Which queue
a given word or field serves is not established here, and the fill does not
depend on it.
Which of the two queue-to-ring tables actually selects is settled by the
datapath rather than by a reading. PPE_TM_RING_Q_MAP, which the switch side
programs and which is the only one the kernel's own ppe driver writes -
ppe_queues_to_ring_init() puts the CPU port's unicast queues on ring zero
there - leaves queue zero on ring zero; QID2RID puts it on the enabled
ring; frames arrive. So QID2RID selects, and the switch-side map is
describing something else for rings this driver never brings up.
A single id has held this far because the unicast path to the host is queue
zero: the CPU port's queue base is zero and its priority map folds all
sixteen priorities onto class zero. The CPU port is also given the
multicast queues from 256 up, which is what a flooded frame's copy for the
host is scheduled through, and the driver has never written ids for them.
Tested on IPQ8074 (Xiaomi AX3600, kernel 6.18.44): every word of the table
reads back the enabled ring in all four-bit fields, and the board boots,
dials its PPPoE uplink, bridges and forwards offloaded flows on it.
Fixes: bdb0b722b7 ("qualcommax: add EDMA driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
The clocks are prepared and enabled by hand, and every failure after that
returned without disabling them - the ioremap, the regmap, the reset
control, the MIB allocation, the per-port clocks and resets, and the switch
registration all leak the enabled clocks on the way out.
devm_clk_bulk_get_all_enabled() registers the disable and unprepare with
devres, which is what qca_edma.c beside this driver already uses. The
unwinding, the error label and the two fields the driver kept the bulk in
all go with it.
Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
ppe_mac_hw_init() enables the loopback, waits 100 ms for it and opens that
port's PPE_PORT_BRIDGE_CTRL_TXMAC_EN, the gate the fabric needs before it
will hand a port a frame. Two paths shut it again. qca_ppe_setup() walks
every port and clears the bit for all but the CPU port, and the loopback
port is described by no board, so DSA also sees it as unused and calls
port_disable() on it. Nothing reopens it: the port has no phylink, so no
link event ever runs, and the loopback probe configured stays unreachable
for the life of the boot.
Give the bit one owner. ppe_port_bridge_txmac_set() refuses to close the
loopback port's gate, and the setup walk stops writing the bit at all - it
was only ever setting it for the CPU port, whose gate qca_ppe_port_enable()
opens a moment later when DSA enables that port. A user port's gate is
still opened by qca_ppe_mac_link_up() and closed by its link going down.
Tested on IPQ8074 (Xiaomi AX3600, kernel 6.18.44): PPE_PORT_BRIDGE_CTRL(7)
reads 0x0002ff08 before this change and 0x0003ff08 after it, the two
differing only in TXMAC_EN. The CPU port and the four user ports read the
bit set and the two ports no board describes read it clear. Forwarding,
offload and dmesg are unchanged.
Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
Neither speed table in qca_ppe_mac_link_up() is exhaustive, and both leave
a speed outside them unhandled: the clock rate is used uninitialised, and
ppe_xgmac_link_up() returns without writing the speed select while its
caller, which cannot see that it did, goes on to enable the MAC and open
the port's transmit gate.
Default both to gigabit.
Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
qca_ppe_phylink_get_caps() adds the four RGMII modes to ports 1 to 4, and
then copies the port's supported interfaces into config->pcs_interfaces, so
phylink is told both the MAC and the PCS carry RGMII there. Neither does.
qca_ppe_mac_link_up() and qca_ppe_mac_link_down() have no RGMII arm and
return from their default label; the uniphy PCS driver has no RGMII code at
all. Nor can the ports carry it: every board describes these four with a
pcs-handle into uniphy0, the SerDes block that driver serves, and
ppe_pcs_set_mux_hppe() has no arm for ports 1 to 3 while port 4's select
names uniphy0's PCS0 whatever the interface.
The failure that leaves is silent. phylink_link_up() calls
netif_carrier_on() whether or not the MAC callback did anything, and
mac_link_up() is void, so its early return is not noticed. It returns
before it opens PPE_PORT_BRIDGE_CTRL_TXMAC_EN, the gate the fabric needs
before it will hand the port a frame, and before it enables the port's MAC.
A board describing one of these ports as RGMII would read link up at its
negotiated speed and pass no traffic, with no message anywhere. Once the
mode is not advertised, phylink_validate() rejects it and
phylink_bringup_phy() fails the port with a warning instead.
No qualcommax board describes a PPE port as RGMII: the one rgmii-rxid in
the target's device trees is on a cascaded qca8337's CPU port, whose
PPE-side port is qsgmii. What still reaches a default label after this is
the CPU port's internal link, whose gate qca_ppe_port_enable() opens.
Tested on IPQ8074 (Xiaomi AX3600, kernel 6.18.44): the four user ports come
up at 1000, 1000, 100 and 1000 Mbit/s full duplex, and forwarding, offload
and dmesg are unchanged.
Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
The port 4 arm of the PCS mux assigns its value only for QSGMII and PSGMII
and writes the register on every interface, so an SGMII link - which the
driver advertises for that port - drives the select bit from an
uninitialised local. The field names where the port's GMII comes from
rather than the protocol carried on it, so one value covers every interface
uniphy0 drives.
Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
ppe_xgmac_link_up() masked PPE_XGMAC_TX_FLOW_ENABLE (bit 1) but wrote
PPE_XGMAC_RX_FLOW_ENABLE (bit 0) as the value, so val & mask was always
zero and TX pause never enabled on an XGMAC port regardless of what phylink
negotiated. The receive side beside it is correct and is what makes the
pair the evidence.
Only ports muxed to their XGMAC are affected - the 10G AQR ports on other
boards. Found by inspection; there is no XGMAC port on the bench to run it
on.
Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>
A multicast queue's green resume offset is an 11-bit field at bit 71 of the
entry, which is bits 17:7 of its third word, not 17:11. Written four bits
too high, the 36 the driver asks for reaches the hardware as 576 - a resume
threshold below zero on a queue whose ceiling is 400, so a multicast queue
that once filled has to drain much further than intended before it accepts
again.
Readback on IPQ8074 confirms it: the word reads 0x00012000 on every
multicast queue. Upstream's own driver for the later part has the span
right (PPE_AC_MULTICAST_QUEUE_CFG_W2_RESUME, GENMASK(17, 7)).
Tested on IPQ8074 (Xiaomi AX3600, kernel 6.18.44): the word reads
0x00001200, multicast forwarding and an IPTV stream are undisturbed, and
dmesg is clean.
Fixes: 142104bb90 ("qualcommax: add PPE driver")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25084
Signed-off-by: Robert Marko <robimarko@gmail.com>