Skip to content

xm: pad mux for XM510 and XM530/XM550 - #253

Merged
widgetii merged 2 commits into
masterfrom
xm-padmux
Oct 19, 2026
Merged

widgetii merged 2 commits into
masterfrom
xm-padmux

Conversation

@widgetii

@widgetii widgetii commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

What

A pad-mux backend for Xiongmai, so that reginfo --pads and gpio mux work on XM510 and XM530/XM550.

Both parts select a pad's function with one enable bit per alternative in the GPIO block at 0x10020000. All bits clear means GPIO.

part register the pad's field
XM530/XM550 0x10020000 + 4 * pad, pads 0..99 bits 1-3, MUX1..MUX3 (GPIO_MUX1_EN..3 in the vendor's platform.h)
XM510 0x10020000 + 4 * port, ports 0..8 pin b's bits b, 8 + b, 16 + b

That's HiSilicon's shape with a bit per function instead of a selector nibble: one register and one field, and every row a plain read-modify-write. The backend is src/hal/xm_padmux.c, behind a new xm family in IPCHW_PADMUX_FAMILIES. xm_detect_cpu() now sets chip_generation (CHIP_XM510, CHIP_XM530), which the front door needs to pick a backend at all.

Pads are numbered as the vendor does:

  • XM530: GPIO N. The HiSilicon 5_2 is refused, because it would land on pad 42.
  • XM510: port * 8 + pin, written 8_1 or GPIO8_1.

Names

Xiongmai publishes no table of what MUX1..3 carry on each pad, so rows are FUNC1, FUNC2 and FUNC3. The exceptions are pads where the vendor's own code says what it puts there, which carry its names:

  • XM530, pads 9..14 (MUX2): FLASH_CLK..FLASH_HOLD, from platform.h and xmsfc_v2.c.
  • XM530, pads 43..50 and 52 (MUX1): SDIO0_*, from sdio0_sd.c.
  • XM510, 5_0..5_3: SDIO1, from sdio1_sd.c.
  • XM510, 6_0..6_3: SDIO2, from sdio2_wifi.c.

docs/padmux.md lists what's deliberately left unnamed, and why.

Tested

  • Host: reginfo_test decodes the two lab boards' registers at rest. It checks writes keep the rest of the word, and checks the pad spellings. Both tables join the integrity sweep and round trip. All tests pass.

  • Lab XM510 + JX-H62: reginfo --pads agreed with every port's mux word read through devmem:

    • 0_4/0_5 and 1_0/1_2 on their 2nd function;
    • 1_4/1_5 on their 3rd;
    • 5_0..5_3 as SDIO1;
    • 7_0 on its 1st.

    gpio mux 3_0 FUNC1 set port 3's bit 0 only, and gpio mux 3_0 GPIO cleared it. gpio mux GPIO8_1 resolved pad 65, and 9_0 was refused.

  • Lab XM550: all 100 pads agreed with their registers, compared by script. The flash pads show as FLASH_* and the SD pads as SDIO0_*. gpio mux 6 FUNC2 wrote 0x4 and GPIO cleared it. gpio mux 51 FUNC3 took pad 51 from 0x80 (8 mA drive) to 0x88 and back. 5_2 and 100 were refused.

Size on cameras that aren't Xiongmai

The backend is compiled into libipchw only when the Xiongmai HAL is (IPCHW_VENDORS containing xm, or all). It's reachable only through the CHIP_XM* generations, which only that HAL sets. The ipctool executable carries it as it carries every table.

Measured on arm32 the way the firmware builds it (shared, SKIP_VERSION, -Os), ipcinfo text + data + bss, master → this branch:

IPCHW_VENDORS ipcinfo xm_padmux.o in libipchw.a
none (HiSilicon/Goke cameras) 35341 → 35341 no
sstar 26798 → 26798 no
ingenic 23521 → 23521 no
xm 25500 → 25602 (+102, the detection fix) yes
all 46871 → 46961 yes

Not changed here: those same non-Xiongmai libipchw.a builds still carry the SigmaStar, Ingenic and Novatek backends, because IPCHW_PADMUX defaults to all and the firmware doesn't set it. The same vendor gate would trim them, as a separate change.

Xiongmai selects a pad's function with one enable bit per alternative in the
GPIO block at 0x10020000, all clear meaning GPIO:
- XM530/XM550: in the pad's own register, bits 1-3 (MUX1..MUX3);
- XM510: in its port's word of eight pins, bits b, 8 + b and 16 + b.

That is HiSilicon's shape -- one register, one field, every row a plain
read-modify-write -- so reginfo --pads and gpio mux now work on both. Pads
are numbered as the vendor does: GPIO N on XM530, port * 8 + pin on XM510
("8_1", "GPIO8_1"). XM530 refuses HiSilicon's "5_2", which would land on
pad 42.

Xiongmai publishes no table of what MUX1..3 carry, so rows are named FUNC1,
FUNC2 and FUNC3, except where the vendor's own code names them:
- XM530's SPI flash pads 9..14 (FLASH_*) and SD pads 43..50 and 52
  (SDIO0_*);
- XM510's SD and Wi-Fi slots (SDIO1 on 5_0..5_3, SDIO2 on 6_0..6_3).

xm_detect_cpu() now sets chip_generation to CHIP_XM510 or CHIP_XM530, so
the front door can pick the backend.

Checked on a lab XM510 and a lab XM550:
- reginfo --pads agreed with every mux register read through devmem (all 100
  pads on XM550, compared by script);
- gpio mux set and cleared exactly the pad's own bits, keeping XM550's
  drive-strength bits.

reginfo_test decodes those boards' registers at rest and sweeps both tables.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add Xiongmai pad-mux support for XM510 and XM530/XM550

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Enable reginfo --pads and gpio mux on XM510 and XM530/XM550.
Diagram

graph TD
  CLI["CLI consumers"] --> Dispatch["Padmux dispatch"] --> Backend["XM backend"] --> Registers[("GPIO registers")]
  Detection["CPU detection"] --> Dispatch
  Build["Build selection"] --> Backend
  Tests["Host tests"] --> Backend
Loading
High-Level Assessment

Keep a dedicated backend behind the existing pad-mux interface. Reusing HiSilicon's table machinery was considered, but its selector-nibble model does not naturally represent Xiongmai's enable bits or XM510's shared port words.

Files changed (8) +418 / -4

Enhancement (5) +268 / -0
xm.cSet Xiongmai chip generation during detection +3/-0

Set Xiongmai chip generation during detection

• Sets 'chip_generation' to XM510 or XM530 when Xiongmai CPU detection succeeds, allowing pad-mux dispatch to choose the backend. XM550 continues to use the XM530 layout.

src/hal/xm.c

xm.hDefine Xiongmai pad-layout generation IDs +8/-0

Define Xiongmai pad-layout generation IDs

• Adds XM510 and XM530 generation constants shared by detection, dispatch, and the backend.

src/hal/xm.h

xm_padmux.cImplement XM510 and XM530/XM550 pad-mux operations +245/-0

Implement XM510 and XM530/XM550 pad-mux operations

• Adds pad enumeration, current-function decoding, pad-name parsing, and read-modify-write function selection for both layouts. Uses vendor-supported names where known and selector names elsewhere, preserving unrelated register bits.

src/hal/xm_padmux.c

padmux.cDispatch Xiongmai chips to the XM backend +6/-0

Dispatch Xiongmai chips to the XM backend

• Routes detected XM510 and XM530 generations to the new backend when XM pad-mux support is compiled in.

src/padmux.c

padmux.hDeclare the XM backend +6/-0

Declare the XM backend

• Adds the conditional backend declaration and documents Xiongmai's enable-bit mechanism within the shared pad-mux interface.

src/padmux.h

Tests (1) +87 / -0
reginfo_test.cTest both Xiongmai pad-mux layouts +87/-0

Test both Xiongmai pad-mux layouts

• Adds XM510 and XM530/XM550 cases to the table-integrity sweep. Host tests check pad spellings, decoding of lab register values, invalid selections, and writes that preserve other pins or register fields.

src/reginfo_test.c

Documentation (1) +60 / -1
padmux.mdDocument Xiongmai layouts, names, and validation +60/-1

Document Xiongmai layouts, names, and validation

• Describes both register layouts, pad numbering, vendor-supported function names, and deliberately unnamed alternatives. Records lab validation and the inferred XM510 port 8 address.

docs/padmux.md

Other (1) +3 / -3
CMakeLists.txtMake the XM pad-mux backend selectable +3/-3

Make the XM pad-mux backend selectable

• Adds 'xm' to the pad-mux families and backend source lists so selected builds compile the new backend.

CMakeLists.txt

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Unrecognised Xiongmai chips get the XM530 pad layout ✓ Resolved
Description
xm_detect_cpu() assigns CHIP_XM530 to every cpuinfo name matched by xm.+ except exactly
XM510, including unknown parts and names with suffixes or revisions. Those names then reach the XM
pad-mux backend as 100-pad XM530 devices, allowing reginfo --pads to read and gpio mux to write
registers using an unverified layout instead of returning NO_TABLE.
Code

src/hal/xm.c[150]

+    chip_generation = !strcmp(chip_name, "XM510") ? CHIP_XM510 : CHIP_XM530;
Evidence
The cpuinfo match accepts xm.+ names, while the generation assignment falls back to CHIP_XM530
for anything other than exactly XM510; the subsequent XM530 check uses an exact comparison.
Successful XM detection installs the XM HAL, padmux_ops() routes both generations to
PADMUX_OPS_XM, and npads() returns 100 for CHIP_XM530, enabling per-pad reads and
read-modify-write operations at 0x10020000 + 4*pad.

src/hal/xm.c[135-160]
src/hal/xm_padmux.c[80-86]
src/padmux.c[73-77]
src/chipid.c[183-187]
src/hal/xm.c[140-158]
src/chipid.c[157-168]
src/padmux.c[69-78]
src/hal/xm_padmux.c[80-104]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`xm_detect_cpu()` assigns the XM530 pad layout to every accepted CPU name except exactly `XM510`, so unknown parts can reach the XM pad-mux backend for register reads and writes.
## Fix Focus Areas
- src/hal/xm.c[140-158]
## Recommended Fix
Assign `CHIP_XM510` only for the exact name `XM510` and `CHIP_XM530` only for the known `XM530` identification path. Preserve the existing XM550 refinement from the same die, but leave `chip_generation` at 0 for other names so the pad-mux front door reports no chip or no table rather than guessing a layout.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Pad names can come back truncated under concurrent lookups ✓ Resolved
Description
gpio_name() lazily fills shared static xm530_gpio_names and xm510_gpio_names buffers, checking
the first byte before writing a pad name with snprintf. When two threads look up the same XM pad
after chip detection, one can return the buffer while the other is still writing it, yielding a
partial name such as "GPI"; both GPIO and alternative-row lookups reach this code.
Code

src/hal/xm_padmux.c[R88-99]

+static const char *gpio_name(int pad) {
+    if (is_xm510()) {
+        char *s = xm510_gpio_names[pad];
+        if (!*s)
+            snprintf(s, sizeof(xm510_gpio_names[0]), "GPIO%d_%d", pad / 8,
+                     pad % 8);
+        return s;
+    }
+    char *s = xm530_gpio_names[pad];
+    if (!*s)
+        snprintf(s, sizeof(xm530_gpio_names[0]), "GPIO%d", pad);
+    return s;
Evidence
The public ipchw.h contract says lookups walk constant data, are safe to call concurrently after
chip detection, and return func_name/gpio_name pointers into read-only data. In the XM backend,
ipchw_padmux_by_func, ipchw_padmux_by_prefix, and ipchw_padmux_by_pad reach gpio_name()
through row() for both GPIO and alternative rows. There, an unsynchronized first-byte check and
snprintf write to shared static storage on first use, so concurrent calls can read a name before
its write completes.

include/ipchw.h[20-28]
src/hal/xm_padmux.c[75-100]
src/hal/xm_padmux.c[135-148]
src/padmux.c[120-131]
include/ipchw.h[17-30]
src/hal/xm_padmux.c[75-99]
src/hal/xm_padmux.c[135-165]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`gpio_name()` in the XM pad-mux backend initializes shared name buffers on demand. Concurrent lookups permitted by ipchw.h can race with initialization and receive a partial pad name.
## Fix Focus Areas
- src/hal/xm_padmux.c[75-100]
## Recommended Fix
Remove writes during lookup for both XM layouts. Prefer preinitialized, immutable name tables, such as `static const char *const` arrays of string literals for `GPIO0` through `GPIO99` and `GPIO0_0` through `GPIO8_7`. Alternatively, fill both buffers in one pass during `xm_detect_cpu()` or another one-time initialization that finishes before concurrent lookups begin, so lookups only read completed names.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can import rules and skills only from the repositories your team follows

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/hal/xm.c Outdated
Comment thread src/hal/xm_padmux.c Outdated
…kend without the HAL

Review of the first cut.

- Pad names were written into shared buffers on first lookup. include/ipchw.h
  promises lookups read only constant data and may run concurrently, so they
  are now fixed-width const tables built at compile time.
- xm_detect_cpu() gave every Xiongmai name but XM510 the XM530 layout. It now
  sets a generation for exactly XM510 and XM530 (XM550 is that die), and any
  other part keeps 0, so pad mux reports no table rather than writing
  registers on a layout nobody checked.
- The backend is compiled into libipchw only when the Xiongmai HAL is: only
  that HAL sets the generations that reach it. Every camera's libipchw is
  built with the default 'all' tables, and without this each non-Xiongmai
  one would have carried code nothing can call. Measured as the firmware
  builds it (shared, -Os) on arm32: ipcinfo is unchanged for
  IPCHW_VENDORS=none, sstar and ingenic, and xm_padmux.o is absent from
  their libipchw.a. Xiongmai's own ipcinfo grows 102 bytes.
@widgetii
widgetii merged commit a6f37e4 into master Oct 19, 2026
5 checks passed
@widgetii
widgetii deleted the xm-padmux branch October 19, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant