Device tree evidence and corrections
Investigated 2026-09-20 against ruyisdk/linux-xuantie-kernel @
7d4e1f444f461dbe3833bd99a4640e7b6c2cd529 and the vendored sources in repo/.
This was first written up as three published Claude artifacts. Those are
superseded and are not maintained: several of their facts were wrong, and
they cannot be corrected in place from here. The notes now live in the site,
are hand-authored under site/src/pages/hardware/, and are corrected whenever
the repository learns something newer:
- Board → DTS map: https://knewter.github.io/tdisplay-k230/hardware/silicon-map/
- SoC dtsi vs board dts overlay mechanics: https://knewter.github.io/tdisplay-k230/hardware/overlay-anatomy/
- Panel dtsi anatomy and the RM69A10 fragment: https://knewter.github.io/tdisplay-k230/hardware/panel-contract/
What the ported pages corrected, relative to the artifacts:
- the RM69A10 init sequence is 20 commands and 297 payload bytes (357 as a
device-tree property), not “13 commands, 75 bytes” — see
docs/evidence/rm69a10-init-sequence.md; - the ST7701’s “369 bytes” was its property length, not its payload, so the claim that the AMOLED sequence is “far simpler” was comparing two different measurements. It has fewer commands and more payload;
- the draft panel dtsi in the third artifact was structurally wrong in four
ways.
nix/dts/display-rm69a10-568x1232.dtsiis the corrected file; - GPIO22 reset is
GPIO_ACTIVE_HIGHand GPIO25 is the backlight, both fromk230-canmv-v3-lcd.dts; the artifacts listed IO25 as unestablished; - the GT9895 is not supported by mainline at any version — see
docs/evidence/gt9895-touch.md; - the LT9611 GPIO collision does not arise in the tree we ship, because our
board file derives from the
-lcdvariant, which has no LT9611; - the board file and the panel fragment both exist and compile now, so the artifacts’ sketches have been replaced by the committed files;
- the board has had a first hardware boot: U-Boot runs, Linux has not.
Corrections to docs/findings.md
Wi-Fi is on MMC0/SDIO0, not MMC1
findings.md says “RTL8189FTV on SDIO (MMC1, GPIO26-31)”. That is wrong on
both the controller and the pins.
Evidence:
repo/schematic/T-Display K230_V1.0_NEW.pdf— netsWIFI_CLK,WIFI_CMD,WIFI_D0–WIFI_D3land on the SoC’sMMC0_CLK,MMC0_CMD,MMC0_D0–D3balls (A5, D6, D5, C5, B5, C6). These are dedicated pads, not FPIOA pins.repo/canmv_k230/src/rtsmart/rtsmart/kernel/bsp/maix3/configs/k230_canmv_v3p0_defconfig:CONFIG_REALTEK_SDIO_DEV0=y CONFIG_SDCARD_ON_SDIO_DEV1=y CONFIG_SDCARD_ON_SDIO_DEV=1repo/canmv_k230/src/uboot/uboot/arch/riscv/dts/k230_canmv_v3p0.dtsmuxes IO54–59 asMMC1_*with the comment// MMC1 -> TFCARD, and leaves IO26–31 atSEL=0(plain GPIO) under the comment// 26-35 ext jp— they go to the expansion header.
So: Wi-Fi → &mmc_sd0 (sdhci0@91580000). TF card → &mmc_sd1
(sdhci1@91581000). That also matches U-Boot’s mmc_boot_dev_num = 1.
GPIO26–31 can be muxed to MMC1_* (FPIOA function 1) — that is presumably
where the original claim came from — but they are not muxed that way on this
board, and MMC1 is already spoken for by the card.
CONFIG_RT_USING_REALTEK is set for this board
findings.md says “the board defconfig does not enable it, so it is likely not
compiled into the shipped image at all.” The Kconfig default is n, but
k230_canmv_v3p0_defconfig sets CONFIG_RT_USING_REALTEK=y alongside
CONFIG_REALTEK_SDIO_DEV0=y.
So the driver is built in. The remaining root cause for the dead radio is the
empty Set_WLAN_Power_On() stub — IO45_WIFI_EN gates an RT9080-33GJ5 LDO
that produces WIFI_3V3, and nothing ever asserts it. That part of
findings.md stands.
The U-Boot DTS is this board’s DTS
repo/canmv_k230/src/uboot/uboot/arch/riscv/dts/k230_canmv_v3p0.dts has
model = "kendryte k230 canmv v3.0", which is exactly the string U-Boot prints
on our hardware (findings.md). It is therefore a primary source for this
board’s pin assignments, not a reference-board approximation.
Confirmed from it, cross-checked against the FPIOA alternate-function table in
repo/canmv_k230/src/canmv/port/machine/machine_fpioa.c
(SEL=0 is GPIO, 1–4 are the alternates in column order):
| Pin | SEL | Function | Use |
|---|---|---|---|
| IO22 | 0 | GPIO22 | lcd_rst — AMOLED reset |
| IO23 | 0 | GPIO23 | commented hdmi_int; is our touch INT |
| IO24 | 0 | GPIO24 | commented hdmi_rset; is our touch RST |
| IO25 | 0 | GPIO25 | LCD_EN |
| IO36/37 | 1 | IIC3_SCL/SDA | touch bus (comment on IO37 says BT_UART_TXD; wrong) |
| IO38/39 | 1 | UART0_TXD/RXD | console via CH342 ch0 |
| IO40/41 | 2 | IIC1_SCL/SDA | camera |
| IO45 | 0 | GPIO45 | IO45_WIFI_EN |
| IO48/49 | 3 | IIC0_SCL/SDA | — |
| IO7/8 | 2 | IIC4_SCL/SDA | — |
| IO50/51 | 1 | UART3_TXD/RXD | CH342 ch1 |
| IO54–59 | 2 | MMC1_CMD/CLK/D0–D3 | TF card |
Touch is on i2c3 at 0x5d — confirmed independently by
CONFIG_TOUCH_GT9895_I2C_DEV="i2c3" / CONFIG_TOUCH_GT9895_I2C_ADDR=0x5d.
LCD reset and enable pins confirmed by canmv_k230/include/config/auto.conf:
CONFIG_MPP_DSI_LCD_RESET_PIN=22, CONFIG_MPP_DSI_LCD_BACKLIGHT_PIN=25.
Structural facts about the Linux DTS
- There is no pinctrl in the Linux device tree. No
pinctrl,pinmux,pinctrl-namesor regulator of any kind appears ink230.dtsi,k230-canmv-v3.dts,k230-canmv-v3-lcd.dtsordisplay-st7701-480x800.dtsi. FPIOA muxing is done by U-Boot viapinctrl-singleagainst0x91105000and inherited. Our board.dtscannot set pin functions. k230.dtsiis 857 lines with 56 hand-written nodes, and its last line is#include "k230_clock_provider.dtsi"— another 3,354 lines that add 133 children undersysctl_clock. Easy to miss when skimming.- Both board files compile clean at the pinned commit (warnings only). Verified
locally with
cpp -nostdinc -I include+dtc. The flattenedk230-canmv-v3-lcdtree is 234 nodes, 184 okay, 11 disabled. The eleven left off areuart1,uart2,uart4,i2c0,i2c1,i2c2,spi0,spi1,spi2,mipi1,mipi2. - Neither
k230-canmv-v3.dtbnork230-canmv-v3-lcd.dtbis inarch/riscv/boot/dts/canaan/Makefile. Buildroot builds them by name. - The whole display chain (
display-subsystem,vo@90840000,dsi@90850000) is alreadystatus = "okay"ink230.dtsi. A board file never enables the display; it only attaches a panel to&dsiport 1. compatible = "canaan, k230-sysctl-power"has a space in it. Do not “fix” it —drivers/soc/canaan/k230-power-domains.ccarries the same string.&mipi0is re-pointed by the board file, which rewritesreg,interruptsandresetson a node that was alreadyokay.dtcreports this as “also defined at k230-canmv-v3.dts:92”.- GPIO bank trap:
gpio0_portsis pins 0–31 andgpio1_portsis 32–63, each numbering from zero. Every reference-board GPIO is in bank 0, so this never bites upstream. Our Wi-Fi enable is<&gpio1_ports 13>, not 45. - Console: the SDK’s default env sets
console=ttyS1, butuart1is never enabled in any of these device trees, so there is nottyS1. Aliases giveserial0 = &uart0andserial3 = &uart3. TryttyS0first,ttyS3second. (Reasoning from the DTS; not yet observed on hardware.) k230.dtsideclares a singlecpu@0. The second C908 is not in the Linux device tree.
Upstream nodes that conflict with our board
k230-canmv-v3.dts declares an LT9611 HDMI bridge on &i2c3 with
reset-gpios = <&gpio0_ports 24> and interrupts = <23 ...>. On our board
those two GPIOs are the GT9895’s RST and INT. The I2C addresses do not collide
(0x3b vs 0x5d); the GPIOs do. Our board file must not carry the LT9611 node as
written.
Whether HDMI and the AMOLED touch are physically mutually exclusive on this board has not been established — the schematic around that section has not been read. Treat it as a DT-level constraint for now.
Panel notes for change 3
Full detail is in the third artifact. The load-bearing points:
panel-canaan-universal.cis fully DT-driven, so the RM69A10 is a new.dtsi, not a new driver — with two exceptions, both hardcoded inprobe()and not expressible in the device tree:dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE. The vendor runs this panel inK_BURST_MODE.dsi->format = MIPI_DSI_FMT_RGB888.
- The reset line is only pulsed in
probe. The equivalent block incanaan_panel_prepare()is present but entirely commented out, whilecanaan_panel_unprepare()drives both the reset and enable GPIOs low. Any modeset or suspend cycle therefore leaves the panel in reset andpreparewill DCS into a dead panel. This may be a prerequisite patch, not a polish item. panel-init-sequenceframing isdata_type, delay_ms, payload_length, payload…repeated.delayis au8, so 255 ms is the longest expressible wait.payload_lengthincludes the DCS command byte. Thedata_typebyte is effectively decorative — 0x05, 0x15 and 0x39 all route tomipi_dsi_dcs_write_buffer, which picks the real packet type from the length; anything else aborts the probe.panel-width-mm/panel-height-mmin the reference contain pixel counts (480, 800), not millimetres. The driver feeds them toconnector->display_info.width_mm. For a 4.1” 568x1232 panel the real figures are about 44 x 95 mm (computed from the stated diagonal, not measured).- RM69A10 timings, from
connector_info_list[]inrepo/canmv_k230/src/rtsmart/mpp/userapps/src/connector/mpi_connector.c:
2 lanes,{ 39600, 475200, 788, 568, 40, 140, 40, 1268, 1232, 4, 16, 16 } pclk phyclk htot hact hsl hbp hfp vtot vact vsl vbp vfp (kHz / pixels)K_BURST_MODE,intr_line = 9(so&vo { vth_line = <9>; }). - The enum name says 60FPS; the numbers say 39.6 Hz.
39.6 MHz / (788 x 1268) = 39.63 Hz. The numbers are self-consistent —
39.6 MHz x 24 bpp / 2 lanes = 475.2 Mbps/lane, exactly the
phyclkin the same row — so the pixel clock is real and the name is wrong. 60 Hz would need roughly 60 MHz; the shipped firmware does not attempt it. - The transcribed
panel-init-sequencefor the RM69A10 (13 commands, 75 bytes, parses cleanly under the driver’s own framing algorithm) is in the third artifact.
Not established — do not assume
- Which I2C bus carries the BQ25896 charger and BQ27220 fuel gauge.
- Which SPI controller and chip-select the SX1262 / LR2021 uses.
- Whether IO25 (
LCD_EN) gates a supply rail or is a logic-level enable. - Whether the HDMI connector and the AMOLED touch are electrically exclusive.
- Whether the RM69A10 tolerates non-burst sync-pulse video mode at these timings.