The state of the rings, of the interrupts that serve them and of the
global configuration is only readable by patching the driver. ethtool -d
pairs every value with the offset it came from, so it is read without a
table on the other side.
The misc status is left out, so that a dump cannot take an error away
from the handler.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
The eight per-cause defines match the eight-bit mask the IPQ60xx is
given; the IPQ807x mask is nine bits wide, so the names do not describe
the status word read there. Nothing reads any of them: the mask written
to the engine is a per-SoC value and the handler reports the status word
as it stands.
The master control register offset goes with them, for the same reason
and none of its own.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
A received frame is dropped on an unrecognised buffer index, on a source
the preheader does not name as a port, on a failed skb allocation and on
a failed tag allocation, and the fill ring reports being starved. None of
the five reached rx_errors, rx_dropped or rx_missed_errors, so a conduit
losing frames looked identical to one that was not.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
A transmit completion carries an error field naming what the engine made
of the descriptor it read, and the misc interrupt reports the engine's
own error conditions. Both were read and discarded, so a malformed
descriptor was indistinguishable from a working one.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
A descriptor carries a segmentation enable and the preheader the MSS, so
the engine cuts an oversized frame into segments and writes the headers
of each. The conduit advertises NETIF_F_TSO and NETIF_F_TSO6, the stack
hands over one frame per window instead of one per segment, and the
checksums the engine already generates cover the segments it produced.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
The engine reads one frame from as many descriptors as it has buffers,
the preheader riding on the first and every descriptor but the last
carrying the more bit. Describing a fragmented skb that way removes the
copy that linearising it costs, so the conduit advertises NETIF_F_SG.
The engine returns one completion per descriptor rather than one per
frame: the opaque that names the frame is written only for the
descriptor that carried the preheader, and every completion but the last
sets a more bit of its own. The frame is therefore named by the first
completion of its run and released on the one that clears that bit, and
an opaque outside the ring names no frame and is refused rather than
indexed with.
A frame holds a store slot per descriptor it was written from, and a slot
is released only once its descriptor has been read, so that a transmit
taking the slot cannot place a frame over what the release still has to
unmap.
Teardown releases a frame from the descriptor that carried its
preheader, so the walk over the ring on free reaches every frame the ring
holds, and the separate drain of the descriptors the engine has not read
is removed.
A frame of more than 32 buffers, or one whose buffers fall below 16 bytes
before the last, is linearised instead. The queue stops with room for a
frame that takes every descriptor, or it would restart on space the next
frame could not use.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
The transmit preheader carries an offload enable, a checksum mode and an
IPv4 header checksum enable. Setting them for a CHECKSUM_PARTIAL frame
leaves the transport checksum to the engine.
The engine parses the frame for the header offsets rather than taking
csum_start and csum_offset from the descriptor, so the conduit
advertises NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM and the stack offers
only what the parser recognises.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
The engine verifies the IPv4 header checksum and the TCP or UDP checksum
of a received frame and reports both in the descriptor, next to the
packet type it parsed. Taking the verdict lets the stack skip the
software check, so the conduit advertises NETIF_F_RXCSUM and a verified
frame is handed up as CHECKSUM_UNNECESSARY.
The packet type decides which verdict applies: only IPv4 carries a header
checksum, and a frame the engine does not parse as TCP or UDP gets no
transport verdict at all.
vlan_features carries the feature to the DSA user ports.
At an offered 800 Mbit/s of 1400-byte UDP datagrams the receiver loses
0.26 to 2% of them with the offload on against 2.7 to 14% with it off,
six interleaved pairs run in both orders, at the same processor load.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
Absorb the unused read-only netgear partition and most of the reserve
into the UBI partition, taking available flash space for
kernel+rootfs+overlay from 22 MB to ~97 MB.
This mirrors the change made for the R7800 in commit 618d59aec2
(ipq806x: Enlarge R7800 flash - use netgear partition).
Reverting to OEM firmware remains possible as the OEM firmware
re-initialises the netgear partition when its contents do not match
expectations.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25030
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Currently, sysupgrade silently fails to erase or write to the SPI-NAND
flash on the JioRouter AX6000. This is caused by the SPI pinctrl group
mistakenly using `bias-disable`, which leaves the flash chip's hardware
HOLD and Write Protect (#WP) pins floating.
When combined with an upstream kernel regression that accidentally
arms the #HOLD function on Winbond W25N0xKV chips, these floating pins
drop low (often exacerbated by UART noise). This pauses the chip and
causes all flash operations to silently fail while returning success.
Fix this by properly configuring `bias-pull-up = <MTK_PUPD_SET_R1R0_11>`
for `SPI2_CS`, `SPI2_HOLD`, and `SPI2_WP`, ensuring the flash chip
remains electrically unpaused and unlocked. Additionally, apply
`bias-pull-down` to the clock and data lines to match MediaTek reference
designs.
Signed-off-by: Sheikh Faisal <sheikhfaisal713@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25122
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
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>