Add software-controlled PoE for RTL8238B boards (register-based, generic per-machine layer)#265
Open
tobik312 wants to merge 5 commits into
Open
Add software-controlled PoE for RTL8238B boards (register-based, generic per-machine layer)#265tobik312 wants to merge 5 commits into
tobik312 wants to merge 5 commits into
Conversation
added 5 commits
June 21, 2026 10:56
Add the generic PoE interface (poe.h) and the RTL8238B driver that implements it (poe_rtl8238b.c). Unlike the host-command PoE projects (poemgr / realtek-poe / OpenWrt PSE), this chip answers only as a raw I2C register slave on these boards, so the driver does the bring-up (volatile firmware upload over a bit-banged I2C stream, then per-port enable) and all control and telemetry via register reads/writes - the same register window the OEM web UI uses. A different PSE chip is a new poe_<chip>.c implementing the same interface.
Describe PoE per board in struct machine (poe = chip, I2C addresses, port count). A board's POE_CHIP_<chip> macro selects its driver and implies POE_PRESENT, which gates the chip-agnostic consumers; non-PoE machines compile none of it. The boot bring-up is data-driven (machine.poe.chip). Add the KP-9000-9XHPML-X V3.1 PoE machine by reusing the base V3.1 block: machine.h chains MACHINE_KP_9000_9XHPML_X_V3_1 to MACHINE_KP_9000_9XHML_X_V3_1 + POE_CHIP_RTL8238B, and the only board-config diffs (.poe, .machine_name) are #ifdef POE_PRESENT in the single block - no duplicated config. Conditionally embed the user-supplied PSE image in the Makefile via the RTL8238B-specific RTL8238B_MACHINES / RTL8238B_IMAGE_LOCATION (the latter must match PSE_IMG_ADDR in the driver). The Makefile diff is kept minimal.
Add poe load | port <n> <on|off> | global <on|off>, gated on POE_PRESENT. Deliberately chip-agnostic - only the operations any PoE driver provides; status and telemetry are served at GET /poe.json.
/poe.json serves the driver-normalized per-port status (port, admin, on, class, v, ma); the OEM-style PoE page renders it directly (per-row and global enable/disable, total consumption) with no chip-specific decoding in JS. Both are gated on POE_PRESENT; the page degrades to a "no PoE controller" notice on non-PoE machines. The endpoint takes no query parameters - the controller is never exposed over HTTP.
Add PoE to the README (feature bullet + doc link) and document the generic PoE framework (doc/poe.md - machine descriptor, gating, interface, console/web) and the RTL8238B driver specifics, including the register-based (vs host-command) approach and the empirically reverse-engineered register map (doc/poe-rtl8238b.md), plus the board (doc/devices/KP-9000-9XHPML-X-V3.1.md). Add tools/poe/extract_rtl8238b_image.py to carve the PSE image from an OEM firmware dump. The image is Realtek/OEM proprietary and user- supplied, not committed.
Owner
This was probably an enormous amount of work. Amazing! I am just browsing though the code, it is great! It will take a bit to go through it, so please bear with all of us a bit. I so far went through the commits of this PR and I think you are splitting it absolutely the right way, so that is already cool. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Power-over-Ethernet support for RTL8373 boards whose PSE is an RTL8238B on I2C,
with a generic, opt-in per-machine PoE layer. Tested on keepLink KP-9000-9XHPML-X V3.1
(8× 2.5G PoE+ + 1× 10G SFP+).
How it works — and how it differs from existing drivers
Other open PoE drivers (realtek-poe, the OpenWrt
realtek-pse-*driver) talk to thesePSEs with a 12-byte host-command protocol answered by a companion management MCU. On these
boards that MCU layer does not respond — the RTL8238B is reachable only as a raw I2C register
slave.
So this driver does everything through direct register I/O, exactly as the OEM web UI does:
because the image far exceeds the SoC I2C engine's 16-byte/transfer limit),
Per-port enable was verified on hardware to be real auto-detect (no running MCU app required):
the chip powers only genuine PDs — a Class 4 PD read 51 V / 153 mA, and an enabled-but-empty
port stays unpowered.
The register interface (reverse-engineered)
The RTL8238B register file is undocumented; the layout below was reverse-engineered from the OEM
software — the stock firmware's own PoE bring-up code, and the register accesses its web UI makes —
then verified on hardware. Access is plain little-endian I2C; each controller drives four port
channels (
local 0–3):0x1a= whole-chip reset; arm download mode on0xec(payload03 00 00 00,read-back bit 2 = armed) with an alternate arm on
0x18(00 00 20 39); stream the image todata port
0xf4;0xecbit 5 = image accepted.0x12(byte 2 of the0x10block) —3= on (auto-detect),0= off — plus a 1-bit enable in0x14.0x10byte 0 bit4+local; PD class is the high nibble of0x0c+local; per-port voltage/current is0x30+4*local(volts = top byte × 0.5 V,mA = low 16 bits × 125 / 4096).
Full register map + decode in
doc/poe-rtl8238b.md.Generic & opt-in
PoE is described in the machine descriptor (
struct machine.poe= chip / I2C addresses / portcount) and gated behind
POE_PRESENT: non-PoE machines compile no PoE code at all (~6 KB ofBANK2). Adding another RTL8238B board is descriptor-only; a different PSE chip is a new
poe_<chip>.cbehind the same chip-agnostic interface (poe.h).Interfaces
poe load | port <n> <on|off> | global <on|off>(chip-agnostic).with per-row and global enable/disable.
GET /poe.jsonreturns the driver-normalized per-port status(
port, admin, on, class, v, ma); it takes no query parameters — the controller is neverexposed over HTTP.
Firmware image
The RTL8238B's PoE application firmware is volatile and Realtek/OEM-proprietary, so it is not
committed. You extract it from your own device's OEM firmware with
tools/poe/extract_rtl8238b_image.py, and the Makefile embeds it. An OEM dump can bundle PSEfirmware for several chips; the tool writes the RTL8238B image (identified by size) to
tools/poe/pse_image.bin. Seedoc/poe-rtl8238b.md.Notes
html_data, so they're present in every build;on a non-PoE machine the page just shows a "no PoE controller" notice (the
/poe.jsonroute iscompiled out). HTML assets are machine-agnostic by design.
0x20/0x21two-controller topology, the per-port registertables) are hardcoded in the driver as the chip's standard layout;
machine.poeparameterizesthe I2C addresses and port count. A board with a non-standard RTL8238B topology would extend the
driver.
Testing
KP_9000_9XHPML_X_V3_1) and for a non-PoE machine (PoE fullyexcluded).
enable/disable work, and the web page shows live telemetry (verified Class 4 @ 51 V / 153 mA).