Mainline kernel inventory: what upstream Linux can drive on this board
Researched 2026-09-29 for openspec/changes/the-board-runs-a-mainline-kernel.
Answers, per hardware function this board uses today
(docs/research/board-capability-inventory.md), one additional question that
document does not: can mainline Linux drive this at all, and if not, what
is the exact blocking upstream state?
How to read this document
Same grounding tiers as .skills/k230-spec-change/SKILL.md and
board-capability-inventory.md. Two additional evidence tools used
specifically here, both checked directly on 2026-09-29 and cited by exact
command:
- File existence in
torvalds/linux:gh api repos/torvalds/linux/contents/<path>— HTTP 200 means the file is present at the tip of the default branch, 404 means it is not. This is a strong, cheap, mechanical check; it is not a Kconfig/DT-wiring check (a present file can still be unusable for this board if no device-tree node references it — see the USB row). - Mainline device-tree contents: fetched directly from
raw.githubusercontent.com/torvalds/linux/master/...and from the pinned tag’s tree (git show v7.2:...,git ls-remote --tags), not inferred from changelogs.
Target version
kernel.org, 2026-09-25: latest stable is 7.2.8; latest longterm is
6.1.188. Neither carries any Canaan K230 support — checked directly:
git show v7.2:arch/riscv/boot/dts/canaan/ lists only k210.dtsi,
k210_generic.dts, canaan_kd233.dts and the four sipeed_maix_*.dts
files; no k230.dtsi.
Basic K230 support (ARCH_CANAAN’s PINCTRL_K230, RESET_K230,
COMMON_CLK_K230, and arch/riscv/boot/dts/canaan/k230.dtsi +
k230-canmv.dts + k230-evb.dts) merged for the v7.3 merge window
(“[GIT PULL] RISC-V devicetrees for v7.3”, July 2026; “too late for the 7.2
window” per the clock-driver pull thread). v7.3-rc1 released 2026-09-01;
the current tip as of this research is v7.3-rc5
(72d3fcf802c45d00b300f25b848a93c3a2bd7c7e), with no stable v7.3 release
yet. This change pins that commit — see nix/kernel-mainline-src.nix for
exactly why (the newest tag that actually has K230 support, not an
unreleased moving master).
What mainline’s k230.dtsi actually contains today
Fetched directly, raw.githubusercontent.com/torvalds/linux/master/arch/riscv/boot/dts/canaan/k230.dtsi:
cpus (2× thead,c908 @ 1.6/0.8 GHz is NOT modeled — mainline’s dtsi
describes only ONE cpu@0; see “Second C908 core” row), plic
(canaan,k230-plic+thead,c900-plic), clint (ditto pattern), a reset
controller (canaan,k230-rst), a pinctrl node (canaan,k230-pinctrl, no
consumer groups defined anywhere upstream), a clock controller
(canaan,k230-clk), and uart0–uart4 (snps,dw-apb-uart, all
status = "disabled" by default). Nothing else. No mmc/sdhci, usb, i2c,
spi, pwm, rtc, adc, watchdog, gpio, crypto, or thermal node exists in this
file. k230-canmv.dts and k230-evb.dts each enable uart0 and add a
board memory node; k230-canmv.dts additionally wires pinmux groups for
uart2/uart3/uart4, PWM, I2S, I2C0/1/3/4, and an mmc1 SD slot pin group —
but with no matching controller node in k230.dtsi for most of those buses
yet, those pinmux groups currently mux pins for hardware nothing in this
tree can drive.
Inventory
| Function | Vendor driver (this project, nix/kernel.nix) | Mainline status | Porting strategy |
|---|---|---|---|
| Serial console (UART0) | snps,dw-apb-uart, mainline 8250 code already | Present. k230.dtsi’s uart0 node is byte-identical in shape to the vendor tree’s; docs/research/board-capability-inventory.md already confirms our board’s console is UART0, and neither tree’s device tree programs UART0’s pinmux (both rely on U-Boot’s FPIOA table) — see nix/dts/k230-tdisplay-mainline.dts’s header for the full citation chain. | Use upstream as-is. No patch needed; only a board .dts enabling it (nix/dts/k230-tdisplay-mainline.dts, this change). |
| SoC platform (PLIC/CLINT/reset/clock/pinctrl) | Vendor tree’s own copies of the same IP blocks | Present, merged for v7.3: PINCTRL_K230 (drivers/pinctrl/canaan/pinctrl-k230-iomux.c), RESET_K230 (drivers/reset/reset-k230.c), COMMON_CLK_K230. All gate on ARCH_CANAAN. | Use upstream as-is. nix/kernel-mainline.nix turns on all three. |
| Second C908 core | N/A — system/second-core-readiness, not attempted | Not modeled at all. Mainline’s k230.dtsi cpus node has exactly one cpu@0; there is no cpu@1 upstream to even describe the second hart, independent of whether Linux SMP release would be safe (see that capability’s own gating). | Blocked upstream, not just on us — a future upstream patch would need to add the second hart’s CPU node before this project’s own SMP-safety gating even becomes relevant. |
| RM69A10 AMOLED panel (DSI) | drivers/gpu/drm/panel/panel-canaan-universal.c, patched | Upstream driver still missing; local opt-in candidate host-builds. The pinned upstream source has no drivers/gpu/drm/panel/panel-canaan-universal.c and no drivers/gpu/drm/canaan/ directory (canaan_drv.c/canaan_vo.c/canaan_dsi.c/canaan_phy.c all absent). No K230 DSI/VO/DRM patch series was found in this research’s mailing-list searches. docs/research/linux-on-t-display-k230.md (2026-09-21) found three working Linux-on-this-panel projects, all forks of the vendor Xuantie tree. A separate local candidate in nix/kernel-mainline-drm.nix and nix/patches/mainline/drm/ has a successful complete kernel, matching DTB, and Image+DTB boot-files build; see docs/evidence/mainline-display-nix-build.md. No board boot, panel illumination, or touch behavior is proven. | Candidate remains hardware-unverified. The pinned mainline source has no display power-domain provider; the opt-in DTS leaves that phandle out. The built profile does not change console-only or vendor defaults. |
| GT9895 touch | nix/patches/goodix-berlin/ (v6.12 backport onto the vendor 6.6 tree) | Present, unusually — already upstream on its own merits. gh api .../drivers/input/touchscreen/goodix_berlin_core.c → 200 (our own backport patch header already says this: “goodix_berlin, backported from v6.12”). But unusable without an I2C DT node, and no I2C controller node exists in mainline’s k230.dtsi at all yet. | Driver needs no porting; the I2C bus DT plumbing does. Blocked on an upstream (or our own forward-ported) I2C controller node, not on touch itself. |
| GC2093 camera | Missing-driver (vendor tree too) | Missing-driver, same as vendor. No CSI/ISP driver of any kind for this SoC anywhere; not investigated further here since the vendor-tree gap is already the binding constraint. | Out of scope for both trees today. |
| HDMI (Lontium LT9611) | CONFIG_DRM_LONTIUM_LT9611=y already, vendor tree | Bridge driver itself is generic upstream code (not Canaan-specific), but it is DSI-fed, so it inherits the same “no mainline DSI host” blocker as the panel row above. | Blocked on the same DSI/VO gap as the panel. |
| Wi-Fi (RTL8189FTV, SDIO) | 8189fs.ko, vendor firmware blob | PORTED (2026-10-06): mmc_sd0 SDIO enabled with owned clocks; the out-of-tree driver builds for 7.3 with nix/patches/mainline/rtl8189fs-mainline-v7.3-rc5.patch (API drift plus cfg80211_ops port) against CFG80211=m; on the board the SDIO card enumerates, 8189fs loads and wlan0 comes up. See docs/research/mainline-wifi-port.md. | Association: task 6.2. |
| SD card / SDIO (SDHCI) | Vendor board mmc_sd0/mmc_sd1 nodes, generic dw_mmc-adjacent path | PORTED (2026-09-29, milestone 1). Not the in-review v5 upstream series (still unmerged at v7.3-rc5) — the coordinator explicitly allowed either path, and the vendor’s own drivers/mmc/host/sdhci-of-kendryte.c (already a real, self-contained, sdhci-pltfm.c-layered driver matching our exact DT compatible string) was the lower-risk choice: one file, no new DT binding shape to invent. Forward-ported to nix/patches/mainline/sdhci-of-kendryte.c; needed one real API fix, sdhci_pltfm_free() (removed upstream entirely after this file’s 6.6-era origin — confirmed against mainline’s own sdhci-of-dwcmshc.c, whose probe/.remove call no equivalent). nix build .#kernelMainline confirms CONFIG_MMC_SDHCI_OF_DWCMSHC_KENDRYTE=y; &mmc_sd1 (the physical TF/SD card) is status = "okay" in nix/dts/k230-tdisplay-mainline.dts with real &sysclk/&rst phandles (UNVERIFIED clock-gate/reset-ID choice — see design.md). &mmc_sd0 (Wi-Fi SDIO) stays disabled; no Wi-Fi driver forward-ported in this pass. | Ported; board-unverified. |
| USB (DWC2 ×2, host/gadget) | CONFIG_USB_DWC2=y, vendor tree | PORTED (2026-09-29, milestone 1), via the vendor’s own dwc2_set_k230_params()/ctl-reg mechanism — deliberately NOT the separately-accepted phy-k230-usb.c/canaan,k230-usb-phy generic-PHY-framework driver already present upstream (design.md decision 7: the two are non-interoperating ways of driving the same HiSysConfig registers, and only the vendor’s mechanism has ever run on this board). Three small hunks ported into drivers/usb/dwc2/{params.c,core.h,core.c} via nix/kernel-mainline.nix’s postPatch (a usb_ctl field on struct dwc2_core_params, its HiSysConfig-register init in dwc2_phy_init(), and the "canaan,k230-otg" of_match_table entry that lets platform.c’s driver actually bind). No API migration needed here — confirmed by a clean build on the first attempt. usb0/usb1 are status = "okay" in the board DTS. | Ported; board-unverified. |
I2C (5×, snps,designware-i2c) | fully mainline generic driver already, vendor tree | Driver is fully mainline (drivers/i2c/busses/i2c-designware-platform.c is generic upstream code, unrelated to this SoC), but no I2C controller DT node exists in k230.dtsi at all. Not attempted in milestone 1 (scoped to boot-critical SD/GPIO/USB only). | DT node porting needed, driver itself needs none. Blocks touch, and every I2C-bus sensor/PMIC row in board-capability-inventory.md. |
SPI (3×, canaan,k230-spi) | spi-dw-mmio.c + vendor init hook, vendor tree | Generic spi-dw-mmio.c is mainline, but no SPI controller node in k230.dtsi, and the vendor’s small dw_spi_canaan_k230_init() hook has no upstream equivalent found. | DT node + a small init-hook port needed; not attempted here. |
| RTC | drivers/rtc/rtc-k230.c, patched (mday mask fix) | PORTED (2026-09-29, after milestone 1). gh api .../drivers/rtc/rtc-k230.c → 404 upstream; forward-ported whole file to nix/patches/mainline/rtc-k230.c, carrying our own already-board-proven mday-mask fix forward (0xf → 0x1f) rather than reintroducing the bug freshly. One trivial fix needed: .remove_new (a transitional platform_driver field from the file’s 6.6-era origin) doesn’t exist at v7.3-rc5 — renamed to .remove (the function’s signature already matched). Built cleanly on the first attempt otherwise. rtc@91000c00 added to nix/dts/k230-tdisplay-mainline.dts (status = "okay", no clocks/resets — the driver calls neither devm_clk_get() nor devm_reset_control_get()). | Ported; board-unverified. |
| Thermal sensor | drivers/thermal/canaan_thermal.c, patched (bounded read loop) | PORTED (2026-10-06, the-mainline-shell-reaches-parity): nix/patches/mainline/canaan_thermal.c, claims the temperature-sensor clock. On the board the mainline zone reads 7218 against the vendor kernel’s 7223 (same driver scale). | Rise under load: task 7.2. |
| Power key (PMU INT0) | drivers/input/misc/k230-pmu-pwrkey.c, vendored from LILYGO | PORTED (2026-10-06): nix/patches/mainline/k230-pmu-pwrkey.c with a PMU DT node claiming the PMU APB gate; K230 PMU Power Key input registers on the board. | Physical press: task 4.2. |
| Audio (I2S / MAX98357A route) | sound/soc/canaan/canaan_k230_inno.c, patched (external I2S switch) | PORTED (2026-10-06): SAI mux, Inno codec, machine driver and the vendor dwc_canaan I2S forward-ported (mainline’s generic dwc-i2s froze the SoC on playback); vendor k230_peridma forward-ported as the DMA provider, holding the shared-memory APB/AXI-slave/SRAM gates; the PCM registers before the DAI. Playback completes on the board with ordinary clock cleanup. See docs/research/mainline-audio-port.md. | Audible tone: task 5.2. |
| Crypto (AES/hash/RSA/RNG/OTP) | drivers/crypto/canaan/*, vendor tree, unpatched | NON-GOAL (recorded 2026-10-06): no shipped unit, package or tool uses the K230 crypto engine, hwrng or OTP; the kernel’s software crypto serves the system. Revisit if a consumer appears. | — |
| GPIO | drivers/gpio/gpio-k230.c, vendor tree, unpatched | PORTED (2026-09-29, milestone 1). gh api .../drivers/gpio/gpio-k230.c → 404 upstream; forward-ported to nix/patches/mainline/gpio-k230.c. Needed a real, substantial API migration, found by a failed build (error: 'struct gpio_chip' has no member named 'bgpio_lock'): bgpio_init()/.read_reg/.write_reg/.bgpio_lock were replaced upstream by struct gpio_generic_chip (include/linux/gpio/generic.h) sometime after this file’s 6.6-era origin. Fixed against mainline’s own already-migrated gpio-dwapb.c (this file’s own stated template, “based on gpio-dwapb.c”) as the reference pattern — the port struct’s embedded struct gpio_chip gc became struct gpio_generic_chip chip, direct gc->bgpio_lock/.read_reg/.write_reg became to_gpio_generic_chip(gc)->lock/.read_reg/.write_reg, and bgpio_init() became gpio_generic_chip_init() against the same four MMIO addresses. gpio0/gpio1 are status = "okay" in nix/dts/k230-tdisplay-mainline.dts, still gating panel reset/touch reset-IRQ/Wi-Fi-enable functionally on their own controller/bus support landing first (unaffected by this row). | Ported; board-unverified. |
| ADC / PWM | k230-adc.c / (PWM controller, unnamed vendor file) | NON-GOAL (recorded 2026-10-06): no shipped unit reads an IIO ADC channel or drives a PWM (the panel backlight is DSI DCS, not PWM). Revisit if battery sensing or a PWM peripheral is added. | — |
| NPU/KPU | Closed nncase runtime; thin kernel shim only | Not investigated for mainline — the blocking constraint is the closed userspace runtime either way (docs/blob-inventory.md B1/B2), so mainline kernel-shim status does not change the conclusion. | Out of scope. |
Our eleven vendor-kernel patches, and which of them mainline needs
Every patch lives in nix/kernel.nix’s applyPatches/postPatch. Checked
against the table above:
riscv-vector-toolchain-probe.patch(KconfigTOOLCHAIN_HAS_Vmissing themextension in its-marchprobe string) — already fixed upstream. Fetched directly,raw.githubusercontent.com/torvalds/linux/master/arch/riscv/Kconfig: current mainline already reads-march=rv64imv/-march=rv32imv, exactly this patch’s fix. Does not need porting; nothing to apply it to.canaan-dsi-implement-dcs-read.patch— targetspanel-canaan-universal.c. Cannot port: file does not exist upstream (missing-driver row above).canaan-panel-read-back-id-and-power-mode.patch— same file. Cannot port, same reason.canaan-audio-external-i2s-switch.patch— targetssound/soc/canaan/canaan_k230_inno.c. Cannot port: no such file or directory upstream.k230-rtc-mday-mask.patch— targetsdrivers/rtc/rtc-k230.c. Update, 2026-09-29: no longer “cannot port” —drivers/rtc/rtc-k230.citself was forward-ported whole onto the mainline pin (see the RTC row above), carrying this exact fix forward directly in the copied file rather than as a separate patch against it (there being no separate upstream file this patch could apply to in the usual sense — the whole file is ours to place). Superseded, not stale: the original “cannot port” finding was correct at the time it was written (nothing existed to port a patch against); porting the driver itself is what changed.k230-pmu-pwrkey.c(a whole vendored driver, not a patch against an existing file) — cannot be ported as a patch since there is no upstream PMU infrastructure to patch; porting it would mean submitting the whole driver + DT binding, out of scope here.goodix-berlinbackport — moot: mainline at this pin already has the real v6.12+ driver natively; forward-porting our backport would be backporting mainline onto itself. Nothing to do except add the I2C DT plumbing this row is actually blocked on.- The
panel-canaan-universal.c/canaan_drv.c/canaan_vo.c/canaan_dsi.c/canaan_phy.cpostPatch hunks (bounded PHY wait, DSI message transport, stage-1 splash handoff, PM-runtime pin, DT-settablehsfreqrange, DSI backlight, DPMS restore) — cannot port, any of them: none of these files exist upstream. This is the same conclusion as the panel row above, restated patch by patch so no partial-port is silently skipped. canaan_thermal.c’s bounded-read-loop fix — cannot port: file does not exist upstream.fbdev bppsed (drm_fbdev_generic_setup(drm_dev, 32)→16) — targetscanaan_drv.c. Cannot port, same reason.
Net result at the time this section was first written: zero of our eleven vendor-kernel patches port to mainline as patches, because ten of them target a file that plainly does not exist upstream, and the eleventh (the toolchain probe) turns out to already be fixed upstream by a different, independent change. This was not a failure of porting effort; it was the direct, mechanical consequence of mainline having no display/audio/RTC/ power-key/thermal driver for this SoC at all yet.
Updated, 2026-09-29, after milestone 1: one of the ten — the RTC
mday-mask fix — has since been ported, not as a patch against an upstream
file (none existed), but by forward-porting drivers/rtc/rtc-k230.c itself
onto the mainline pin and carrying the fix forward in the copied file (see
the RTC row above and item 5’s updated entry). The other nine (display,
audio, power-key, thermal) are unaffected — no upstream driver exists for
any of them to patch or forward-port a fix against yet. This is exactly the
pattern milestone 1’s GPIO/SD-MMC/USB forward-ports also followed: porting
the whole driver, not just a patch, is what “porting” means once nothing
upstream exists to patch.
Phased plan
Ordered so each milestone is provable before the next is attempted, per
.skills/k230-spec-change/SKILL.md’s QEMU-vs-hardware distinction — every
milestone below whose evidence is board console output is hardware-only;
none of it is provable under QEMU, because QEMU’s k230 machine in this
project models the vendor board, not a generic mainline-booted K230, and no
upstream QEMU K230 machine model was confirmed working in this research
(a QEMU-side “add SDHCI support for K230” series was found, implying machine
support is itself still incomplete upstream).
- Cross-builds exist (DONE, this change):
.#kernelMainlinecompiles,.#deviceTreeMainlinecompiles and round-trips,.#kernelMainlineBootFilescollects both under clear names. Host-only, proven bynix build. - Reaches the serial console (hardware-gated, not performed): U-Boot
loads this
Image+ DTB; the CH342 console shows the OpenSBI banner and Linux’s own early boot log over UART0, whether or not it then panics for lack of a root filesystem. This is the first milestone that needs the board, and is explicitly NOT performed by this change. - Boot-critical GPIO/SD-MMC/USB forward-ported, and a matching full
NixOS system variant (DONE, this change’s milestone-1 continuation):
drivers/gpio/gpio-k230.canddrivers/mmc/host/sdhci-of-kendryte.cforward-ported from the vendor tree (one real API migration each found and fixed, see the GPIO and SD card/SDIO rows above);drivers/usb/ dwc2/{params.c,core.h,core.c}hunks ported cleanly (no migration needed).nixosConfigurations.k230-mainline-consoleis configured to boot this kernel;.#kernelMainlineConsoleBootFilesbakes its real bootargs and wraps its initrd. This remains host-proven (nix build); an actual SD-backed boot to a login prompt remains milestone 4’s hardware gate. - Reaches an interactive shell over SD (hardware-gated, not
performed): the milestone-3 candidate’s own pass condition — does
k230-mainline-console’s boot files actually reach a login prompt over the physical card’sNIXOS_SD-labeled root partition. Genuinely open: whether&mmc_sd1probes at all with this change’s best-effort clock/reset IDs, whether by-label root resolution works the same way under mainline as it does under the vendor kernel, and whether this system’s own closure/profile is even staged on that card (this change does not stage it). See tasks.md’s “Remaining evidence gate”. - Wi-Fi (SDIO) + wider USB (network):
&mmc_sd0stays disabled and no RTL8189FTV driver was forward-ported in this pass (out-of-tree, and not expected to compile against this kernel unmodified — see design.md decision 9); a USB-Ethernet dongle or a forward-ported Wi-Fi driver would follow milestone 4, not before it. - I2C/SPI DT plumbing: unblocks touch (driver already present, per the inventory row) and any future PMIC/sensor work, independent of display. Not attempted in milestone 1 (scoped to boot-critical SD/GPIO/USB only).
- RTC — PORTED (2026-09-29, first item of what was originally
milestone 7):
drivers/rtc/rtc-k230.cforward-ported whole, one trivial.remove_new→.removefix,rtc@91000c00added to the board DTS. Built cleanly on the first attempt — no API migration needed, unlike GPIO/ SD-MMC. Board-unverified, same as every row in this document. - Display, audio, power key, thermal, crypto, ADC, PWM: each missing
upstream drivers, with no in-progress public series found. The local
DRM candidate is tracked separately from upstream mainline. Display was
initially scoped without a port, then resumed in task group 5b of the
OpenSpec change. The historical scratch trial copied the vendor’s
canaan_drv.c/canaan_vo.c/canaan_dsi.c/canaan_phy.c/canaan_plane.cpluspanel-canaan-universal.c(about 3,900 lines, before project-specific patches) and exposed structural DRM API changes:drm_panel_init()had been replaced bydevm_drm_panel_alloc(), along with Kconfig dependency and removed-symbol adjustments. That trial was discarded, then the coordinator authorized a separate opt-in derivation. The resumed source adapts the panel allocation, fbdev/client setup, atomic helper signatures, and platform remove callbacks. The prepared-header API check is an external-module build; it emits expected modpost warnings and is not an in-tree link proof (docs/evidence/mainline-display-api-compile.md). The first full kernel attempt reached the vmlinux link and exposed missingDRM_DISPLAY_HELPER/DRM_BRIDGE_CONNECTORKconfig selections; after adding them, the corrected object/config check and full Nix derivations succeeded (docs/evidence/mainline-display-nix-build.md). The separate opt-in DRM display/touch DT source and Image+DTB bundle now exist; host DTS, complete kernel/DTB/boot-files builds, and the Kconfig correction are recorded indocs/evidence/mainline-display-dtb.mdanddocs/evidence/mainline-display-nix-build.md. Physical probe remains open. The candidate omits the vendor display power domain because the pinned mainline tree has no provider for it. The other six (audio, power key, thermal, crypto, ADC, PWM) are individually much closer in size/shape to RTC (single small self-contained files) and are plausible next candidates for the same methodology. Not scheduled further in this pass; continue the RTC-style pattern for the smaller remaining drivers.
Sources
- kernel.org (fetched 2026-09-25 per its own page: 7.2.8 stable, 6.1.188 longterm).
torvalds/linuxgit ls-remote --tags(v7.2 through v7.3-rc5, this research, 2026-09-29).git show v7.2:arch/riscv/boot/dts/canaan/vs.raw.githubusercontent.com/torvalds/linux/master/arch/riscv/boot/dts/canaan/(file listing diff).raw.githubusercontent.com/torvalds/linux/master/arch/riscv/boot/dts/canaan/k230.dtsi,k230-evb.dts,arch/riscv/Kconfig.socs,arch/riscv/Kconfig,drivers/pinctrl/Kconfig,drivers/reset/Kconfig,drivers/clk/Kconfig(exact Kconfig stanzas quoted above and innix/kernel-mainline.nix).gh api repos/torvalds/linux/contents/<path>for every “present”/“missing” driver-file claim above (2026-09-29).- Ratatoskr/lkml.org mailing-list archive links for the SDHCI (“[PATCH v5 0/3] Add SDHCI support for Canaan K230 SoC”, March 2026) and USB (“[PATCH v5 0/4] Add USB support for Canaan K230”, lkml.org/lkml/2026/2/27/1255) series.
docs/research/board-capability-inventory.mdanddocs/research/linux-on-t-display-k230.md(this repo), for the vendor-side baseline this document adds a mainline-status column to.