14 Commits
Author SHA1 Message Date
Julius Bairaktaris 7d15db3266 qualcommax: qca_ppe: add debugfs dumps of the PPE counters
The PPE counts frames at every stage and the driver enables most of the
counting, but nothing reads it back. Add qca_ppe_debugfs.c with read-only
files for the L3 interface, port policer, drop code, queue, buffer manager,
ingress, egress and ACL counters, at the addresses of this generation
rather than the APPE ones the IPQ9574 driver uses. The queue drop counters
sit past 0x900000, so the PPE register window grows to 0xa00000 in both
dtsi files and the binding example. Nothing is written, and sparse tables
skip entries that counted nothing.

On IPQ8074 the drop_code, queues and bm files give the drop codes and queue
occupancy the later patches in this series are measured with.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25700
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-10-18 12:50:07 +02:00
Julius Bairaktaris 207962a83a qualcommax: qca_edma: add multi-queue RX and TX
The driver uses one RX and one TX ring set, so all traffic through the
host is handled by one CPU. Use up to four ring sets, as many as the
device tree gives interrupts for, counting down from the rings in the
SoC data. Each set has one NAPI context for its TX completions and RX,
its own page pool and TX queue; XPS maps TX queue n to CPU n, and the
switch queues map onto the RX rings through QID2RID. The regmap runs
without its lock, since a ring's registers are only touched from its
own NAPI or under its TX lock, and threaded NAPI starts at the first
open so that packet steering can pair each NAPI thread with its queue.
ethtool -l/-L reports and changes the number of ring sets.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25663
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-10-17 16:12:31 +02:00
Julius Bairaktaris 59f295f58f qualcommax: pcs-qca-uniphy: re-resolve the link on a USXGMII rate change
An AQR113C can report its copper link over MDIO before it writes the
new rate into the USXGMII code word, which carries 10G while the copper
link is down. phylink reads the speed only when the link comes up, so
the MAC stays at 10G. On an RT-AX89X with a 1G partner on the AQR113C
port this happened in 3 of 20 link-ups, and the port passed no traffic.

Report a rate change in the code word as a link drop, so that phylink
resolves the link again at the new rate. With this change all 20
link-ups ended at 1G; 9 came up at 10G first and were re-resolved
within a second.

Fixes: 05c180511d ("qualcommax: add UNIPHY PCS driver")
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25829
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-10-12 12:11:33 +02:00
Julius Bairaktaris 29c5542cd6 qualcommax: pcs: qca-uniphy: force the channel on every UNIPHY
A fixed link carries no in-band negotiation, so the UNIPHY channel has to
be told to take its speed from the channel register instead of waiting
for a word that never arrives. The driver does that only on IPQ5018 and
leaves a TODO for the other two SoCs.

There is nothing to work out for them: the SSDK writes the same bit on
all three. Its IPQ5018 path, adpt_mp_uniphy_mode_ctrl_set(), writes
newaddedfromhere_ch0_force_speed_25m through
hppe_uniphy_channel0_input_output_4_set(), and its IPQ6018/IPQ8074 path,
__adpt_hppe_uniphy_sgmii_mode_set(), writes the same field through
hppe_uniphy_channel0_force_speed_mode_set(), a wrapper around that same
accessor - UNIPHY_CH_CTRL bit 3, at the same offset on every generation.
The vendor condition, PHY_F_FORCE, comes from the port's forced-speed and
forced-duplex DT properties, which is a fixed link: what phylink reports
as an outband neg_mode with no PHY expected.

Drop the SoC test, and clear the bit for a port that is not a fixed link
as the vendor does, so a channel cannot keep a force left from an earlier
mode. The same function also serves USXGMII and 10GBASE-R, where the XPCS
drives the channel and UNIPHY_CH_CTRL is not the register in play - the
SSDK writes the bit only from its SGMII mode sets - so neither write is
made in those modes.

A forced channel then reads its speed from the SPEED_MODE field next to
that bit, which nothing ever wrote - the SSDK has an accessor for it and
no caller. It kept its 1000 Mbps reset value while pcs_link_up() moved
only the RX and TX clock rates, so a fixed link at 10 or 100 Mbps drove
the channel at the wrong speed. Program the field from the speed
pcs_link_up() is handed, in the encoding the receive path already decodes
out of the status register, and only for a channel the same predicate
reports as forced: an in-band channel carries its speed in the negotiated
word and leaves the field unread. 2500BASE-X has no encoding of its own -
SGMII+ carries the rate in the mode - and keeps the 1000 value the vendor
leaves there.

Exercised on a Xiaomi AX3600 (IPQ8074, kernel 6.18), whose four ports
share UNIPHY0. Behind a QCA8075 the clear leaves FORCE_MODE at 0 and
SPEED_MODE at its 1000 reset value, and a port linked at 100 Mbps holds
that value while its UNIPHY port clock follows the link down to 25 MHz,
so pcs_link_up() runs for the lower speed and leaves the field to the
in-band word. Channels of the same instance declared as fixed links read
back FORCE_MODE set with SPEED_MODE following the declared speed - 0 at
10 Mbps, 1 at 100 and 2 at 1000 - and the channels still behind a PHY
unchanged. The eleven IPQ5018 boards with a fixed link on a UNIPHY all
declare 1000 Mbps, the value the field already holds, so the speed half
is a latent defect there rather than a visible one, and no IPQ6018 or
IPQ8074 board in the tree has a fixed link on a UNIPHY at all.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/24791
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-09-07 18:37:41 +02:00
Julius Bairaktaris dc4a7a1ff0 qualcommax: pcs: qca-uniphy: configure the UNIPHY instance once per mode
qca_uniphy_pcs_config_mode() runs the instance-wide UNIPHY initialization
- analog PLL reset, MODE_CTRL update, UNIPHY soft reset and calibration -
on every phylink pcs_config call, i.e. once per port. On a multi-channel
instance (PSGMII: five channels behind one SerDes) a cold boot executes
this sequence up to five times within ~2 seconds (preinit plus one netifd
ifup per port), and each run tears down the channels that already synced.
Whether every QCA8075-side SerDes lane re-locks against the re-calibrated
UNIPHY is timing luck: roughly one boot in three left one port (whichever
lost the race) with link up and working TX but a dead receive path - the
GMAC receives almost nothing (2 frames total, zero rx errors), ARP stays
INCOMPLETE, and only an admin down/up of the port (or a reboot) recovers
it. An autoneg restart or a cable replug does not, since carrier-only
cycles never touch the PCS.

This is a plain host-stack failure: it reproduces on a stock qualcommax
image with no offload driver loaded.

The vendor driver never re-runs this sequence: the SSDK configures the
UNIPHY mode once (__adpt_hppe_uniphy_psgmii_mode_set) and per-port
operations only touch channel state afterwards.

Record the configured interface mode in struct qca_uniphy (the member
already existed, unused) and skip the instance-wide sequence when
pcs_config is called again for the same mode; the per-channel IPQ5018
force-mode handling still runs on every call. A genuine mode change
re-runs the full sequence. This also removes ~1.5 s of redundant reset
sleeps from every boot.

The cached mode needs a lock of its own. phylink serializes pcs_config
per netdev, so on a multi-channel instance two ports can enter the
function concurrently, both find the cache empty and both run the
sequence - the very thing being removed here, and now with one channel
resetting the instance while the other polls calibration. Serialize the
test and the sequence on a per-instance mutex, and clear the cache before
the sequence so a bail-out cannot leave a mode recorded that the hardware
does not have.

The sequence disables the calling channel's TX and RX clocks and
re-enables them at its end, so a calibration timeout leaves them off.
Until now the next pcs_config call re-ran the sequence and restored them,
tripping clk_core_disable()'s "already disabled" WARN on the way. With an
unchanged mode now skipping the sequence, a channel whose calibration
timed out while another channel of the instance succeeded would keep them
off until a genuine mode change, so the bail-out re-enables them itself.
The pair moves as a bulk, which unwinds a partial enable, and a flag per
channel records whether the driver left it on, so a clock that will not
enable cannot make the next call disable an already disabled one. The
mode is cached after the last step of the sequence, so that any failure
below the invalidation - a clock, the XPCS reset release, the reference
clock - leaves the next call running the sequence again rather than
skipping it.

Validated on a Xiaomi AX3600 (IPQ8074, four QCA8075 PSGMII ports, kernel
6.18): the failure reproduced within 3 boots before the change; with it,
three boots each came up with a single instance configuration, RX and TX
counters moving on every port, and no clock warning in dmesg. Forcing one
port through 1000, 100 and 10 Mbps left the other channels' registers
untouched and traffic flowing at each step. The calibration bail-out and
the XPCS paths are not reachable on this board and were not exercised.

The reset operations the sequence runs are checked with everything else that
can fail ahead of the cache, so a mode is never recorded for an instance whose
reset did not take: a stale CALIBRATION_DONE would otherwise let the poll pass
and the cache would then skip the sequence for good. The two after the clock
disable share the calibration timeout's clock restore. No in-tree consumer can
reach the new branches - every UNIPHY reset comes from &gcc, whose
qcom_reset_set_assert() discards the regmap return and always reports success -
so this makes the invariant hold by construction rather than by which provider
is wired up.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/24186
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-08-24 10:01:19 +02:00
Julius Bairaktaris e616833278 qualcommax: pcs: qca-uniphy: take a channel's clock pair as a bulk
pcs_enable() enables a channel's RX clock and then its TX clock, and
returns as soon as one of them fails - leaving the other one enabled.
Nothing rebalances that: the next call enables the survivor a second
time, and the pair never lines up again. pcs_disable() has the mirror
shape without the checks.

The two are adjacent entries of the bulk array the driver already holds,
so clk_bulk_enable() takes them in one call and unwinds what it enabled
when one fails - the property the open-coded pair is missing.

That leaves the other half of the same imbalance: phylink discards what
pcs_enable() returns and calls pcs_disable() either way, so a channel
whose clocks would not enable still gets a reference dropped that was
never taken, underflowing the enable count. Record per channel whether
the driver left the pair on, and disable only what it enabled.

Not exercised on hardware: both call sites are IPQ5018-only and the
board here is an IPQ8074, where the driver leaves the port clocks to the
configuration sequence.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/24186
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-08-24 10:01:19 +02:00
George Moussalem af17ab0a3f qualcommax: ipq50xx: convert to UNIPHY PCS and DWMAC stack
Convert to UNIPHY PCS and DWMAC ethernet stack from qca-ssdk.

Since we are not using SSDK anymore, we dont need to patch in the UNIPHY
clock names anymore so drop the patch.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/22381
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-07-11 22:26:44 +02:00
George Moussalem b910cbfc0c qualcommax: ipq50xx: add Qualcomm IPQ5018 DWMAC driver
Add IPQ5018 DWMAC driver. IP version of this Synopsys DWMAC is 3.7.

This Qualcomm IPQ5018 specific MAC implementation supports link speeds
of 10HD/FD, 100HD/FD, 1,000HD/FD, and 2500FD and SGMII and 2500BASEX
interface modes.

The driver supports the MAC be attached directly to a PHY or via an
optional PCS to a switch or PHY.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/22381
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-07-11 22:26:43 +02:00
John Crispin bdb0b722b7 qualcommax: add EDMA driver
EDMA dataplane ethernet driver for Qualcomm IPQ platforms.

Signed-off-by: John Crispin <john@phrozen.org>
[ rework Makefile for external repository, dependency ]
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
[ rework for in-tree ]
Link: https://github.com/openwrt/openwrt/pull/22381
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-07-11 22:26:43 +02:00
John Crispin 142104bb90 qualcommax: add PPE driver
PPE switch driver for Qualcomm IPQ platforms, depends on EDMA and
UNIPHY PCS.

Signed-off-by: John Crispin <john@phrozen.org>
[ rework Makefile for external repository, dependency ]
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
[ bring PPE in-tree ]
Link: https://github.com/openwrt/openwrt/pull/22381
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-07-11 22:26:43 +02:00
John Crispin 05c180511d qualcommax: add UNIPHY PCS driver
PCS driver for UNIPHY SerDes blocks on Qualcomm IPQ platforms.

Signed-off-by: John Crispin <john@phrozen.org>
[ rework Makefile for external repository, dependency ]
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
[ make it in-tree under qualcommax ]
Link: https://github.com/openwrt/openwrt/pull/22381
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-07-11 22:26:43 +02:00
Robert Marko 6647e6d219 qualcommax: fix line endings in ESS dt-bindings
It seems that ESS dt-bindings somehow ended up with Windows line endings,
this is obviously incorrect, so lets convert it to UNIX endings.

Signed-off-by: Robert Marko <robimarko@gmail.com>
2023-11-15 17:35:38 +01:00
Robert Marko 878ef51b01 qualcommax: add bindings for MAC modes
Since every board needs to define the correct MAC modes, it makes sense
to document the allowed hex values with a humanly readable name.

So, lets document all of the allowed MAC modes from SSDK 12.4 as bindings,
so later we can replace all of the hex values in DTS-es with these.

Signed-off-by: Robert Marko <robimarko@gmail.com>
2023-11-14 05:21:24 +01:00
Christian Marangi 947b44d9ae ipq807x: fix wrong define for LAN and WAN ess mask
switch_lan_bmp and switch_wan_bmp have wrong values and now cause
problems with the new version of the qca-ssdk.

Fix the wrong entry and drop the redundant switch_cpu_bmp.

Also introduce some convenient define to better understand values in
this map.

Fixes: eea264fead ("kernel: qca-ssdk: update to 12.4")
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Tested-by: Robert Marko <robimarko@gmail.com> # Dynalink AX3600 and Qnap 301W
Reviewed by: Robert Marko <robimarko@gmail.com>
2023-11-10 20:19:45 +01:00