There is currently a difference how upstream and downstream define
the switch in the dts. Downstream holds the switch as a member
node below a root switchcore parent. Upstream uses the switch as
the parent.
Upstream:
ethernet-switch@1b000000 {
mdio-controller@ca00 { };
ethernet { };
ethernet-ports { };
}
Downstream:
switchcore@1b000000 {
ethernet-switch {
ethernet-ports { };
};
mdio-controller@ca00 { };
ethernet { };
}
Align downstream to upstream and merge the ethernet-switch into
the parent node. For this to work adapt the port lookup in the MDIO
and PCS driver.
Remark! With this commit the boot process will give the spurious
error message "rtl838x_eth 1b000000.ethernet-switch:ethernet eth0:
Failed to create a device link to DSA switch 1b000000.ethernet-switch"
This comes from the fact that the switch is the parent of the ethernet
device. Thus a link back from ethernet device to the switch is no
longer possible. Testing shows that the error is just cosmetic.
Link: https://github.com/openwrt/openwrt/pull/23599
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Add a per-SerDes supported_modes bitmap, filled at probe by each
variant from the SerDes id or type, and reject unsupported modes in
determine_hw_mode() via test_bit().
This replaces the rtl838x is_hw_mode_supported() switch and adds the
same gating to rtl839x/rtl93xx, which previously relied only on the
per-variant mode-value table. Unlike that table, the bitmap is per
SerDes instance, so it also rejects modes the table can encode but that
a given SerDes cannot actually use. Rejection uses the uapi -EOPNOTSUPP
instead of the internal -ENOTSUPP.
Link: https://github.com/openwrt/openwrt/pull/23608
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
SerDes modes HISGMII and QHSGMII are practically dead but were added/kept
for documentation purposes. However, these modes will likely never be
used and we have other documentation than in the code. Drop them to
reduce the modes to what we actually use.
Link: https://github.com/openwrt/openwrt/pull/23608
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Drop the shared rtpcs_create function and references in both drivers
since that is now done via the fwnode PCS provider framework.
Link: https://github.com/openwrt/openwrt/pull/23539
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
PCS driver registers each SerDes as an fwnode_pcs provider in probe;
the resolver returns the cached or freshly-allocated rtpcs_link for
the requested (sds, link_idx) cell. DSA glue stops calling
rtpcs_create directly, drops .mac_select_pcs, and instead populates
phylink_config.num_available_pcs / fill_available_pcs from each
port's pcs-handle in phylink_get_caps. The rtl838x_port.pcs pointer
becomes a has_pcs bool populated at port probe via fwnode_property_
present, since nothing assigns the actual phylink_pcs anymore but the
"does this port use a PCS?" checks elsewhere still need a presence
flag.
Without .mac_select_pcs, phylink_major_config only searches the
pcs_list when state->interface is set in phylink_config.pcs_interfaces
(drivers/net/phy/phylink.c:1378). Populate it per port whenever the
port has a pcs-handle, listing the SerDes-routable interface modes for
each SoC variant -- without this, pcs_config / pcs_link_up are never
called and the SerDes is left unconfigured.
pcs_get_state still needs the MAC port number to index per-port link
status registers. Recover it at probe via rtpcs_map_links: walk the
sibling switch's ethernet-ports subtree (same backwards topology
lookup the sibling MDIO driver does for phy-handle), and for every
port whose pcs-handle resolves to one of our SerDes, store the port's
reg in sds->link_port[]. The resolver consults link_port[] when
allocating rtpcs_link and fails with -ENODEV if a consumer requested
a link the map step didn't record. Avoids a driver-side port_base
table that would have to encode per-SoC SerDes-to-port wiring (and
would silently break on non-contiguous variants); the DT is the
single source of truth.
Kconfig selects FWNODE_PCS.
Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Link: https://github.com/openwrt/openwrt/pull/23539
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Add an s16 link_port[] array to struct rtpcs_serdes, initialised to
-1 in probe. This is preparatory storage for the port number that
each link serves; it will be populated in the follow-up fwnode_pcs
migration commit by scanning consumer DT nodes for their reg, and
consumed by the resolver when allocating rtpcs_link.
Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Link: https://github.com/openwrt/openwrt/pull/23539
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Move the rtpcs_link pointer array from rtpcs_ctrl (keyed by global
DSA port) into rtpcs_serdes (keyed by the per-SerDes link index).
This matches how the hardware is structured -- a SerDes hosts up to
RTPCS_MAX_LINKS_PER_SDS PCS links -- and aligns the in-driver
addressing with the cell the DTSes just gained on pcs-handle, so the
upcoming fwnode_pcs resolver becomes a direct sds->link[cell] lookup.
rtpcs_create() takes a new link_idx parameter and stores into
sds->link[link_idx] instead of ctrl->link[port]; the DSA glue switches
its phandle lookup to of_parse_phandle_with_args() and forwards the
cell. The port number stays on rtpcs_link for legacy callers that
still need it. Bounds and double-bind checks (-EINVAL, -EBUSY) guard
against malformed DT references that would otherwise OOB or silently
overwrite an existing link.
Drops RTPCS_PORT_CNT, whose only user was the relocated array, and
fixes a pre-existing of_node_put leak on the pcs-handle phandle in
the DSA glue as a side effect of the parse-with-args conversion.
Link: https://github.com/openwrt/openwrt/pull/23539
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Switch rtpcs_serdes from struct device_node * to struct fwnode_handle *
in preparation for fwnode_pcs_add_provider, which keys providers by
fwnode. Storing the fwnode directly avoids of_fwnode_handle() wrappers
at every API boundary.
The conversion is mechanical: of_node_get/put become fwnode_handle_get/
put (same refcount on OF-backed fwnodes), polarity helpers drop their
of_fwnode_handle() wrapping, and the link counter compares fwnodes
directly via of_fwnode_handle(arg_np). No behavior change.
Link: https://github.com/openwrt/openwrt/pull/23539
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Make deactivate fully restore the SerDes to an inert state at both the
MAC and IP layers. Previously deactivate only zeroed the MAC mode via
set_mode(OFF), which on the default branch only writes the MAC mode
register and leaves the IP mode register untouched. The IP mode register
then retained whatever the previous bring-up left behind (force=1 with
a stale mode value, or force=0 from boot defaults), making "deactivate"
not fully deactivate the SerDes.
Replace the set_mode(OFF) call with explicit set_mac_mode(OFF) plus
set_ip_mode(OFF). The latter writes force=1 with mode=OFF, pinning the
IP block to OFF until a subsequent bring-up takes a defined action.
This forced-OFF state would break MAC-driven modes (USXGMII / QSGMII /
XSGMII), which set only the MAC mode register and rely on the IP block
following along. To compensate, add an explicit unforce of the IP mode
force-bit (page 0x1f reg 0x09 bit 6) at the start of the MAC-mode branch
of rtpcs_930x_sds_set_mode. IP-mode bring-up via apply_ip_mode is
unaffected -- it re-asserts force=1 with the target mode value, which
overrides the deactivate force-OFF.
Net result: deactivate fully and explicitly deactivates the SerDes; each
set_mode path takes its own responsibility for the IP mode register
state. The previous asymmetric behaviour (set_mode default branch silently
not touching the IP register) is now explicit code rather than an
implicit accident-of-dispatch.
Verified on RTL930x hardware: SGMII, 2500BASE-X, 10GBASE-R, USXGMII-QX
and XSGMII all bring up correctly with link, traffic and iperf3 as
expected.
Link: https://github.com/openwrt/openwrt/pull/23513
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Move rtpcs_930x_sds_set_power() and rtpcs_930x_sds_rx_reset() out of
rtpcs_930x_sds_apply_ip_mode() and into rtpcs_930x_sds_{de,}activate().
After this, apply_ip_mode is pure IP-mode/CMU/state-machine programming
and the SerDes-core analog power is owned by the outer phase pair, the
same place that already owns the 1G/10G PHY block and fiber RX power.
Behavioural change: USXGMII / QSGMII / XSGMII modes did not previously
go through apply_ip_mode and therefore never had the SerDes-core power
gated on mode transitions. After this commit, every mode transition
power-cycles the SerDes core via the outer deactivate/activate.
For the SGMII / 1000BASE-X / 2500BASE-X / 10GBASE-R path the set of
register writes is unchanged; only the relative ordering vs. the
fiber/PHY power writes shifts: set_power(false) now precedes those
writes (was after), set_power(true) now follows them (was before).
Verified on RTL930x hardware: SGMII, 2500BASE-X, 10GBASE-R, USXGMII-QX
and XSGMII all come up with link, ping and iperf3 throughput as
expected.
Link: https://github.com/openwrt/openwrt/pull/23513
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Move rtpcs_931x_sds_set_mode(sds, hw_mode) ahead of
rtpcs_931x_sds_activate() in rtpcs_931x_setup_serdes(). The IP-block
mode registers latch with the SerDes powered down, so the mode can be
committed during the configure phase rather than after power-on.
This matches the phase order already used by 838x and 930x
(deactivate -> configure -> set_mode -> activate) and is a step toward
a unified bring-up sequence across variants.
Verified on RTL931x hardware: USXGMII, SGMII and 10GBASE-R modes all
come up, link is established, L2 forwarding works, and iperf3 reports
expected throughput.
Link: https://github.com/openwrt/openwrt/pull/23513
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Move the 1G and 10G PHY block power-up writes (clear BMCR_PDOWN on pages
0x02 and 0x04) out of rtpcs_930x_phy_enable_10g_1g() and into
rtpcs_930x_sds_activate(), and add the mirror writes (set BMCR_PDOWN) to
rtpcs_930x_sds_deactivate(). Same for the fiber RX bit.
With 1G PHY / 10G PHY / fiber RX all now handled symmetrically, drop the
rtpcs_930x_phy_enable_10g_1g() helper. The remaining write it contained
(set medium = fiber on page 0x1f reg 11 bit 1) is unrelated to power
management, unconditionally applied, and to-be-inspected for non-fiber
modes. Move it inline into setup_serdes with a TODO comment; proper
mode-aware handling is out of scope for this commit.
Behavioural note: the 1G/10G PHY blocks and fiber RX are now
power-cycled on every mode transition. Previously they were only
powered up (never explicitly down) and the state persisted across
reconfigure. The new behaviour makes each setup_serdes a standalone
bring-up that does not rely on the prior state of these bits.
Link: https://github.com/openwrt/openwrt/pull/23513
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Mirror of the previous sds_deactivate commit: add rtpcs_{838x,931x}_sds_activate()
helpers that each wrap the variant-specific "bring the SerDes back to operational"
block-power call at the end of setup_serdes, and replace the inline call.
- 838x: wraps rtpcs_838x_sds_power(sds, true)
- 931x: wraps rtpcs_931x_sds_power(sds, true)
RTL839x and RTL930x are intentionally not given an activate helper in this
commit:
- RTL839x calls rtpcs_839x_sds_reset() at the end of setup_serdes. That is
a reset pulse whose internals (per-type 10G/5G analog sequences, internal
REG3 0x7146 -> 0x7106 dance) are not yet fully characterized. Aliasing
it as _activate would misrepresent the function.
- RTL930x has no separate activation step: rtpcs_930x_sds_set_mode(sds,
hw_mode) is what commits the new mode and is intended to be surfaced
as its own "set mode" phase in a later commit rather than hidden inside
a variant-specific _activate wrapper.
Both variants will be revisited when their respective phases are clarified.
This commit is a pure refactor, no behavioural change.
Link: https://github.com/openwrt/openwrt/pull/23513
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Add rtpcs_{838x,930x,931x}_sds_deactivate() helpers that each encapsulate
the variant-specific "make SerDes inert before reconfigure" sequence, and
replace the inline calls at the start of each setup_serdes with a single
call to the new helper:
- 838x: wraps rtpcs_838x_sds_power(sds, false)
- 930x: wraps rtpcs_930x_sds_set_mode(sds, RTPCS_SDS_MODE_OFF)
- 931x: rtpcs_931x_sds_power(sds, false) + rtpcs_931x_sds_set_mode(sds,
RTPCS_SDS_MODE_OFF)
RTL839x has no deactivate step to factor out and is left unchanged.
This is a pure refactor: same register writes, same order, same return-
value handling at the call site. The helpers give each variant a named
hook for the deactivate phase and prepare for a subsequent commit that
promotes it to an rtpcs_sds_ops entry and hoists the call site into
rtpcs_pcs_config.
Link: https://github.com/openwrt/openwrt/pull/23513
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Previously, sds->num_of_links was incremented from rtpcs_create() as
each DSA port bound its phylink_pcs. The count therefore relied on a
temporal contract (DSA must finish enumerating before pcs_config runs)
and on rtpcs_create() being the single chokepoint for all consumers.
Replace this with a probe-time scan of pcs-handle references in the
live OF tree: for every available consumer node carrying a pcs-handle
property pointing at one of our SerDes subnodes, bump that SerDes'
num_of_links. After the scan, the count is final regardless of when
or whether DSA later calls in.
To allow of_parse_phandle_with_args() to walk the property correctly,
add #pcs-cells = <0> to every serdes@N node in the 838x/839x/930x/931x
.dtsi files. A future cell-bearing form remains possible without
touching the scan.
Over-references (DT pointing more consumers at one SerDes than the
hardware can carry) are clamped at RTPCS_MAX_LINKS_PER_SDS and warned
about, but do not fail probe — the correctly-wired ports on that
SerDes still come up, and only the surplus reference is dropped.
The bounds check and the bare ++ in rtpcs_create() become redundant
under the scan-driven count and are removed.
This decouples num_of_links from DSA call ordering and is a prereq
for migrating to fwnode_pcs providers, where rtpcs_create() goes away
as the centralised counter.
Link: https://github.com/openwrt/openwrt/pull/23484
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Just for safety. Use device managed operation so the
memory is automatically reclaimed when the parent PCS
controller is removed.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/23391
Signed-off-by: Robert Marko <robimarko@gmail.com>
Use CPU friendly operation.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/23391
Signed-off-by: Robert Marko <robimarko@gmail.com>
In rtpcs_probe_serdes_bus(), the code manages the device
tree node reference incorrectly:
- It acquires a node pointer np via of_find_compatible_node(),
which increments the reference count.
- It calls of_mdio_find_bus(np) to locate the bus.
- It calls of_node_put(np), which decrements the reference
count. If this was the last reference, the node is freed.
- It then attempts to check if (!of_device_is_available(np)).
The pointer np is used after its reference has been released.
This can lead to a kernel oops or unpredictable behavior if
the memory has been reclaimed.
Fixes: fe27cce1e ("realtek: add SerDes PCS driver")
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/23391
Signed-off-by: Robert Marko <robimarko@gmail.com>
The RTL930x calibration code is especially chatty. There are debug
prints for every start and end of a section corresponding to the
sections the SDK uses. In the end, this doesn't help a user much and
just wastes CPU cycles. Moreover it doesn't help in understand what is
done there. As a first step, drop "start" and "end" prints but preserve
their meaning as comments.
While at it, slightly adjust two other prints and drop one confusing
print.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23288
Signed-off-by: Robert Marko <robimarko@gmail.com>
There is no need to be extra chatty for simple writes which set a single
bit. As a nice side effect, without the prints there's no need to have
open-coded register access when there a helper that covers that.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23288
Signed-off-by: Robert Marko <robimarko@gmail.com>
The SerDes setup code for RTL931x still has a lot of debug prints as
pr_info from former times. A lot has changed and we don't need that
rather useless chattiness anymore. We reached a state where we have a
standalone setup of most hardware modes. The registers printed are still
"documented" in rtpcs_931x_sds_config_hw_mode and
rtpcs_93xx_sds_config_cmu. For every other issues we rely on comparison
of full SerDes dumps instead of cherry-picked registers.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23288
Signed-off-by: Robert Marko <robimarko@gmail.com>
RTL930x and RTL931x program the same physical SerDes IP mode field
(page 0x1f reg 0x09, bits 11:7 hold the 5-bit mode value, bit 6 is
the "force mode" enable), but did so via two unrelated code paths:
RTL930x kept the force bit separate from the value in a __set helper,
while RTL931x had it baked into each switch-case constant.
Add a shared rtpcs_93xx_sds_set_ip_mode() that takes the rtpcs_sds_mode
enum, looks up the 5-bit value from the existing sds_hw_mode_vals
table, and writes value | force-bit in one place.
Convert both variants:
- RTL930x: drop __rtpcs_930x_sds_set_ip_mode and the manual table
lookup; __rtpcs_930x_sds_get_ip_mode is replaced by the shared
rtpcs_93xx_sds_get_ip_mode, which reverse-looks the raw register
value up in sds_hw_mode_vals[] and returns the matching enum
rtpcs_sds_mode (or -ENOENT for an unmapped raw value). The
wrapper that orchestrates power, CMU, state machine and rx-reset
around the mode write is renamed to rtpcs_930x_sds_apply_ip_mode
for clarity.
- RTL931x: drop the per-mode switch and the leftover pr_info debug
print; rename the symerr-clear + MAC-OFF + IP-mode-write wrapper
to rtpcs_931x_sds_apply_ip_mode.
rtpcs_930x_sds_reconfigure_to_pll() now goes through the new shared
get/set helpers: it saves the current IP mode as an enum on entry
and restores it via the enum-taking setter after the PLL reconfigure.
This changes behavior by mapping the raw mode setting through the
hardware mode table, effectively blocking unknown modes which might be
set by bootloader or somewhere else. This is intended and might uncover
unknown behavior instead of hiding it.
As a side-effect, QSGMII is now properly set too for RTL931x. Most code
paths anyway already had support for this mode, but it was missing from
the mode setting.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23213
Signed-off-by: Robert Marko <robimarko@gmail.com>
The USXGMII_10GDXGMII and USXGMII_10GQXGMII early-return was added
when the submode register was not yet programmed, making those modes
effectively unconfigurable. With the submode now wired up at probe
time and written from the set_mode path, the gating is no longer
needed.
Keep the XSGMII gate - RTL8218D/E bring-up through the proprietary
10G SGMII path is still unimplemented - and rewrite the surrounding
comment accordingly.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23120
Signed-off-by: Robert Marko <robimarko@gmail.com>
Complete the USXGMII submode table with the four values that were
missing so far:
0x01 10GDX (2 x 5G)
0x03 5GSX (1 x 5G)
0x04 5GDX (2 x 2.5G)
0x05 2_5GSX (1 x 2.5G)
Together with the existing 10GSX (0x00) and 10GQX (0x02) this covers
all six USXGMII modes the driver declares. Add a corresponding mapping
to the hw_mode table too to cover them properly there.
Replace the switch in rtpcs_93xx_sds_apply_usxgmii_submode() with a
sparse lookup table indexed by hw_mode, using -1 as the sentinel for
modes without a submode value. Non-USXGMII modes silently no-op as
before; a USXGMII mode hitting a SerDes without an allocated submode
register now returns -EOPNOTSUPP, catching configuration mismatches
that would previously have been silently dropped.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23120
Signed-off-by: Robert Marko <robimarko@gmail.com>
USXGMII submode (10GSX vs 10GQX) is selected through a dedicated
register at 0x13e8, independent of the MAC and IP mode registers.
Without programming it, USXGMII-QX ports initialise as single-lane
SX and fail to link up correctly; MAC and IP mode alone are
insufficient for a working USXGMII setup.
The register packs 12 x 5-bit entries for SerDes 2..13, six per
32-bit word, non-straddling (bits 0..29 used, 30..31 padded). This
matches the available register dumps and the SDK's
reg_array_field_write() non-CROSS_REGISTERS path, which derives the
bit position as ((index - larray) % (32 / width)) * width and
accesses only a single 32-bit word. The submode values are identical
to RTL930x, so the shared RTPCS_93XX_SDS_USXGMII_SUBMODE_* defines
are reused.
Allocate the regmap_field at probe time with coordinates computed
from the SerDes ID; the regular packing needs no lookup table. Call
rtpcs_93xx_sds_apply_usxgmii_submode() from the set_mode dispatcher
after set_ip_mode - the helper's null-guard and mode filter leave
non-USXGMII paths unchanged.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23120
Signed-off-by: Robert Marko <robimarko@gmail.com>
RTL930x and RTL931x share a set of extras around MAC mode writes:
- a post-write delay (kept for consistency with the original RTL930x
behaviour; harmless on RTL931x)
- the force-mode bit (RTL931x only, nullable field)
Add rtpcs_93xx_sds_set_mac_mode() as a shared wrapper around the
generic rtpcs_sds_set_mac_mode() that applies each of these extras
unconditionally; the nullable field makes the force-bit write a no-op
on RTL930x.
Route the three RTL93xx call sites (the 930x and 931x set_mode
dispatchers, and 931x set_ip_mode's OFF transition) through the
wrapper, removing the duplicated force-bit handling from each.
The USXGMII submode write stays out of the wrapper and is called
explicitly from the 930x dispatcher via rtpcs_93xx_sds_apply_usxgmii_submode().
Keeping submode as a separate step leaves room for RTL931x to apply it
from its IP-mode path once the submode register is wired up, without
retrofitting a MAC-mode wrapper with side effects.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23040
Signed-off-by: Robert Marko <robimarko@gmail.com>
RTL931x uses a regular 8-bit-per-SerDes layout in SERDES_MODE_CTRL, so
the reg_field can be computed in the probe hook with simple arithmetic.
The 8-bit-per-SerDes field is split into a 7-bit mac_mode (bits 0..6)
and a 1-bit mac_mode_force (bit 7), each written independently via its
own regmap_field. The mac_mode is widened to 7 bits (rather than the
5 bits strictly needed for the mode value) so MAC mode writes also
clear bit 5 (FEC enable) and bit 6 (10G speedup), matching the original
behaviour where the full 8-bit mask cleared these bits on every mode
change. FEC and speedup are mode-dependent and not yet programmed by
the driver; keeping them cleared leaves headroom for future support
without changing the effective register value.
rtpcs_931x_sds_reset() is updated to save and restore both fields
across the off/on cycle, preserving the original force-bit handling.
rtpcs_931x_sds_set_mode() uses the generic rtpcs_sds_set_mac_mode() and
sets the force bit explicitly; the same sequence also appears in
rtpcs_931x_sds_set_ip_mode()'s OFF transition. Both are folded into
the shared RTL93xx wrapper in a later commit.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23040
Signed-off-by: Robert Marko <robimarko@gmail.com>
RTL930x packs 5-bit mode fields across four registers at irregular
positions. Express this as a static reg_field table indexed by SerDes
ID; the probe hook allocates the corresponding regmap_field. The
USXGMII submode register follows the same pattern with its own
reg_field table, allocated only for 10G-capable SerDes (id 2..9).
The generic rtpcs_sds_set_mac_mode() replaces the old
__rtpcs_930x_sds_set_mac_mode() helper. The previous behaviour of
writing OFF before the target mode is intentionally dropped — it was
RTL930x-specific and not required by the hardware.
The variant-level rtpcs_930x_sds_set_mode() is kept as a pure dispatch
between the IP mode path (set_ip_mode) and the MAC mode path. The
USXGMII submode write is factored into rtpcs_93xx_sds_apply_usxgmii_submode(),
which derives the submode value from hw_mode and no-ops on SerDes
without the submode register.
The __rtpcs_930x_sds_get_mac_mode() and __rtpcs_930x_sds_get_usxgmii_submode()
helpers are dropped. They were __always_unused and depended on the
removed parallel arrays. A future get_mode path will be added if a
caller needs it, likely mirroring the setter's wrapper shape.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23040
Signed-off-by: Robert Marko <robimarko@gmail.com>
RTL839x packs the SerDes MAC mode in MAC_SERDES_IF_CTRL with a regular
per-SerDes layout, so the regmap_field can be computed directly in the
probe hook rather than declared as a static table.
Mode values (currently only OFF and QSGMII) move into a static
rtpcs_839x_sds_hw_mode_vals[] table. Values for 100BASEX, 1000BASEX
and SGMII from the vendor SDK are kept as comments for future
reference — they are not yet exercised here.
With no variant-specific extras (no force bit, no companion register,
no submode), rtpcs_839x_sds_set_mode() is removed; setup_serdes calls
the generic rtpcs_sds_set_mac_mode() directly.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23040
Signed-off-by: Robert Marko <robimarko@gmail.com>
Replace rtpcs_838x_sds_set_mode()'s inline shift/mask arithmetic with
a regmap_field computed and allocated at probe time. The field layout
is regular (5-bit per SerDes, reverse-packed in SDS_MODE_SEL), so the
position can be derived arithmetically from the SerDes ID rather than
declared in a table.
The function keeps its wrapper role because SerDes 4 and 5 have a
second companion register (INT_MODE_CTRL) with its own per-mode value
encoding. Since RTL838x is the only variant with this quirk and the
register is written from only one call site, it is kept inline rather
than abstracted into its own config table.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23040
Signed-off-by: Robert Marko <robimarko@gmail.com>
All four Realtek PCS variants (RTL838x, RTL839x, RTL930x, RTL931x)
configure the SerDes MAC mode by writing a register field whose layout
varies per variant — different base registers, different bit positions,
and in some cases per-SerDes packing that isn't arithmetically regular.
Add the common infrastructure to express this uniformly:
- per-SerDes regmap_field pointers in a new 'swcore_regs' anonymous
struct on rtpcs_serdes: mac_mode, mac_mode_force (931x only, nullable)
and usxgmii_submode (930x only, nullable).
- a per-variant mode-value table pointer (sds_hw_mode_vals) on
rtpcs_config, keyed by enum rtpcs_sds_mode. Values are s16 with -1 as
the "unsupported" sentinel — u8 with 0 would collide with RTL839x's
OFF value (0x0).
- a generic rtpcs_sds_set_mac_mode() that looks up the value for the
requested mode and writes it via the regmap_field.
Variant-specific extras (post-write delay, force bit, companion register
writes, USXGMII submode handling) will be added in per-variant wrappers
in the following commits.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23040
Signed-off-by: Robert Marko <robimarko@gmail.com>
With the recent backport of the common PHY properties infrastructure
(phy-common-props and the phy_get_manual_{rx,tx}_polarity() helpers) to
OpenWrt, the generic `{rx,tx}-polarity` device tree properties are now
usable for the Realtek PCS driver. Switch the driver and all affected
boards from the local vendor-specific `realtek,pnswap-{rx,tx}` booleans
to the common properties.
Add a `config_polarity` SerDes op (implemented by RTL930x and RTL931x;
RTL838x/RTL839x polarity support not yet added) and a generic wrapper
that resolves the requested polarity via phy_get_manual_{rx,tx}_polarity()
and dispatches to the op. Variants without the op silently accept the
default polarity but warn when a non-default polarity is requested,
since that cannot be honored.
Move the polarity programming out of the variant setup_serdes callbacks
into rtpcs_pcs_config, so it runs before setup_serdes. This matches the
ordering used by the vendor SDK, which configures polarity first.
Update all board DTS files that previously used `realtek,pnswap-{rx,tx}`
to the new `{rx,tx}-polarity = <PHY_POL_INVERT>` property, and select
PHY_COMMON_PROPS from Kconfig.
Each SerDes now retains its DT node for later polarity lookup. Use
for_each_child_of_node_scoped for the iterator, and register a
devm_add_action_or_reset for each stored reference so it is released on
unbind or probe failure.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23044
Signed-off-by: Robert Marko <robimarko@gmail.com>
The whole driver often does some range checks of the SerDes ID to
restrict some functionality to a group of SerDes. However, having these
open-coded checks everywhere is rather confusing because it's not
obvious what it actually means.
Luckily, those checks give a good picture of what SerDes types we have:
- 5G: RTL838x, RTL839x (0-7, 10, 11), RTL930x (0, 1)
- 10G: RTL839x (8, 9, 12, 13), RTL930x (2-9), RTL931x (2-13)
- unknown: RTL930x (10, 11), RTL931x (0, 1)
Add a new enum and field in rtpcs_serdes for the type of a SerDes we
have. This is filled during SerDes probe, making use of the stub
implementations for that hook.. All SerDes ID range checks related to
this are replaced with corresponding checks of the SerDes type.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22941
Signed-off-by: Robert Marko <robimarko@gmail.com>
Add a per-SerDes probe hook to rtpcs_config, called once for each SerDes
during driver probe. This provides a place for variant-specific, one-time
per-SerDes initialization that doesn't fit into the existing controller-
level init hook — such as allocating per-lane regmap fields or assigning
per-SerDes metadata.
Add stub implementations for all variants for now. They will be used by
all variants in a subsequent comment. For RTL839x, reuse the existing
rtl839x_sds_init hook and move its call out of the global init.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22941
Signed-off-by: Robert Marko <robimarko@gmail.com>
Our USXGMII config only covers one single register of which kind there
are actually more. We only set a value for 'CFG_QHSG_TXCFG_MAC_CH0' but
there are additional registers for CH1-CH3. Those refer to the 4
USXGMII 'channels'. While the RTL930x part of the SDK doesn't set them
explicitly, from RTK setup SerDes dumps we can see they are usually
similarly set.
The RTL931x part of the SDK actually writes those register explicitly
during USXGMII. We just haven't implemented that so far. Thus, add this
to the USXGMII config for both RTL93xx.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22939
Signed-off-by: Robert Marko <robimarko@gmail.com>
Since the USXGMII config was made generic before, we can use it now for
the RTL931x setup too. This brings it closer to what the SDK configures
(see _dal_mango_construct_init_usxgmii), fixes some deviations in our
previous settings and makes clearer what is actually done.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22939
Signed-off-by: Robert Marko <robimarko@gmail.com>
The USXGMII config setting specific options currently is only
implemented for RTL930x. Looking at the SDK one can see that RTL931x
shares a lot of these configuration options. Additionally, several dumps
from RTK setups have shown that values which aren't set yet by us for
RTL930x but for RTL931x, also apply for RTL930x. Thus, both can be
merged. Start this by making the current function generic for RTL93xx.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22939
Signed-off-by: Robert Marko <robimarko@gmail.com>
USXGMII autoneg setting is currently hardcoded for RTL930x and not even
handled explicitly for RTL931x. This should be handled via neg_mode from
phylink subsystem too. Thus, move it over to rtpcs_93xx_sds_set_autoneg
as generic implementation for RTL93xx and set it depending on the
provided negotiation mode.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22939
Signed-off-by: Robert Marko <robimarko@gmail.com>
Previous commits carved out some TX configuration from different places
into a dedicated function. A cause of this is a behavioral change where
the TX amplifier values aren't applied anymore if it isn't a "fiber
mode". To restore this behavior and make the media/TX-RX configuration
more generic, we need a dedicated media type. The existing ones only
cover fiber and DACs, but not the other left case where a PHY is
attached to the SerDes. This now calls set_media for USXGMII and XSGMII
modes intentionally, both to prepare for future changes and to restore
previous behavior.
We do not have a reliable way to distinct between the actually used
media types, this is still a TODO. But several parts of the code already
have different values applied based on this information. Moreover, this
is especially needed for DAC cables to work properly. While this is
missing, we need to rely on inferring the media from the SerDes mode.
While at it, improve the call site of media handling since there's a
media type for all cases now. This allows to reduce the number of
function calls by moving it out and just have the media type in the
decision block.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22814
Signed-off-by: Robert Marko <robimarko@gmail.com>
Proceed with the consolidation of TX/RX path config by carving out some
TX configuration out into a separate function. For now, this only
affects TX amplifier config. But it already slims down other polluted
call sites and brings some structure into the TX/RX path config.
While at it, already provide a similar function for RX configuration.
This does nothing yet but should be filled with RX path config later.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22814
Signed-off-by: Robert Marko <robimarko@gmail.com>
Shed some light into the darkness by giving a part of the setup a
dedicated name. This applies to the magic data blocks in the generic
setup entrypoint and some writes in the media handling. This was just
copied from the SDK which doesn't annotate this properly with
information. But we can clearly connect this with some other functions
of the SDK and extract meaningful information.
The mentioned magic blocks and register writes set coefficients of three
amplifiers in the TX path, a pre-amp, a main-amp and a post-amp. They
are used to tune the signal for different kinds of media. Generic values
are applied for all SerDes and further special values for different media
types depending on the needs.
Provide a dedicated function that sets these amplifier coefficients.
Replace the mystic register writes with the function call so one can see
at a glance what's happening. Also replace the magic TX values with the
coefficients organized into a separate struct. This might be extended
further and is also applicable for RTL930x.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22814
Signed-off-by: Robert Marko <robimarko@gmail.com>
Within RTL931x set_media, two writes are applied in general for all modes
but later again changed depending on the mode. This is a confusing
pattern. To fix this, move those generic writes into the switch-block as
a default case. To maintain behavior, pull the 10G guard into the fiber
media case.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22814
Signed-off-by: Robert Marko <robimarko@gmail.com>
Relocate the analog reset code within set_media for RTL931X to have it
in one place at the very top of the function, running all reset actions
before further real configuration is done. Also drop a separate call to
rx_reset because a subsequent sequence already includes this
functionality.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22814
Signed-off-by: Robert Marko <robimarko@gmail.com>
Drop two confusing macros 'PHY_PAGE_2' and 'PHY_PAGE_4'. Though we
rather want to have meaningful names instead of magic values everywhere,
those two macros do it totally wrong. They still have the old naming
from times where SerDes was treated as a PHY, and they add no real
context to what page they are actually referring. Thus, replace them
with plain values in their two usages each until we have a better
overall solution.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22885
Signed-off-by: Robert Marko <robimarko@gmail.com>
Cleanup some odd macros still existing from older code. Drop the old
RTL931X_ prefix and use RTPCS_931X_ instead, and drop a totally unused
macro.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22885
Signed-off-by: Robert Marko <robimarko@gmail.com>
Give the one-time PCS init hook a shorter and simpler name, using simply
'init' instead of 'init_serdes_common'. Keep it short and sweet.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22885
Signed-off-by: Robert Marko <robimarko@gmail.com>
Shorten the name of the SerDes ops struct from 'rtpcs_serdes_ops' to
'rtpcs_sds_ops' to be consistent with other naming throughout the
driver.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22885
Signed-off-by: Robert Marko <robimarko@gmail.com>
The RTL8224 driver now puts the PHY into the proper mode, but the SerDes
side is still missing. It was deactivated due to a known regression.
Thus, allow USXGMII-QX configuration since both sides should be properly
configured now.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22609
Signed-off-by: Robert Marko <robimarko@gmail.com>
Set the SerDes mode to OFF early in the setup process to have a clear
starting point. This was part of the media handling before as a leftover
from the SDK code import. In the SDK, this function didn't only care
about applying some media settings but was also some kind of mode
setting for fiber modes.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/22786
Signed-off-by: Robert Marko <robimarko@gmail.com>