Skip to content

novatek: pad-mux, reginfo and GPIO for NA51089 (#237) - #239

Merged
widgetii merged 2 commits into
masterfrom
novatek-padmux-237
Oct 9, 2026
Merged

widgetii merged 2 commits into
masterfrom
novatek-padmux-237

Conversation

@widgetii

@widgetii widgetii commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Closes #237.

On an NT98566, ipctool reginfo printed "Platform is not supported". The Novatek HAL never set chip_generation, and there was no Novatek table.

What the NA51089 does

The NA51089 selects a pad's function in a fourth way, different from the existing vendors:

  • A location field per peripheral in the TOP block at 0xF0010000, as on SigmaStar. For example, REG5[7:6] is I2C3: 1, 2 and 3 put it on P_GPIO21/22, C_GPIO11/12 or DSI_GPIO8/9.
  • A gate bit per pad in eight bitmaps at TOP+0xA0..0xE8. Setting it keeps the pad a GPIO, whatever the fields say.

The new backend, src/hal/novatek_padmux.c, reads this as claims: a name, plus the field values under which the pad carries it. The first claim that holds is the answer.

set():

  1. picks the claim that needs the fewest changes. On a MIPI board, that means it never touches the sensor mode.
  2. clears the pad's gate bit.
  3. drops any competitor that still wins, by zeroing a field the target does not need.

Moving a peripheral to another location leaves its old pads cleared with nothing claiming them. get() reports such a pad as unnamed, which is what the hardware state is.

Where the table comes from

The SDK has no datasheet and no pin table, only the driver's pinmux_config_*() functions. tools/gen_novatek_padmux.py:

  1. compiles those functions on the host, with every register write logged;
  2. calls each group with every combination of up to four options (96,550 calls, about 30 s);
  3. attributes each gate write to the field writes in the blocks enclosing it;
  4. replays every resulting state against the table and fails if any pad reads differently from what the vendor code did.

Names come from top.h's @PAD[NAME] annotations, then from top.csv. Three places where those disagree with the code are listed in ANNOTATION_ERRATA, each with the reason:

Source What it says Code says
csv P_GPIO29 for SIF CH2_2 P_GPIO19
top.h P_GPIO14 for PICNT2_1 L_GPIO1
driver PIN_MISC_CFG_SP2CLK_3RD a vendor bug: pinmux_config_misc() tests the SENSOR constant there, so the option does nothing

--selftest runs against a built-in fixture SDK and needs only a C compiler. tools/test_pipeline.sh picks it up automatically.

Also in this PR

  • chip_generation is now the TOP chip-ID word. getchipfamily() is unchanged: it still returns the chip name, so ipcinfo stays byte-identical.
  • Plain reginfo dumps the TOP mux registers and the eight gate bitmaps.
  • gpio get/set/scan and the IR-cut hint drive the GPIO controller at 0xF0070000 (DATA, DIR, SET, CLR).
  • Pads can be given as the Linux GPIO number or the kernel's name (P_GPIO22, MC17, HSI_GPIO9). The HiSilicon 5_2 form is refused on Novatek, because it would land on a different pad.
  • New IPCHW_PADMUX family token: novatek.

Verification

  • Native build passes, and so do all unit tests.
  • reginfo_test:
    • the table sweep, plus a set-every-function round trip over 388 claims;
    • test_novatek(), whose register values are taken from host.c;
    • mutation-checked: removing the competitor drop in set() makes it fail.
  • tools/test_pipeline.sh passes, and so do gen_padmux_names.py --check and gen_novatek_padmux.py --verify against the SDK.
  • Cross builds pass on arm32, mips32 and arm64, and in the configurations IPCHW_PADMUX=none, IPCHW_VENDORS=ingenic, IPCHW_PADMUX=novatek;IPCHW_VENDORS=novatek and IPCHW_PADMUX=sstar.
  • Size on arm32, measured as CI does and with CI's toolchain:
    • ipcinfo is byte-identical to master, with all and with none. A first version added a getchipfamily() case, 64 bytes, which pushed the RW segment onto the next page and cost a full 4 KB, so it was dropped;
    • ipctool grows by 16 KB, or 9 KB after UPX.

Not covered

  • Hardware. No Novatek board is in the lab. The check to run on one is to compare ipctool reginfo --pads with cat /proc/nvt_info/nvt_pinmux/pinmux_summary, the kernel's own decode of the same registers.
  • Display muxing. LCD, TV and HDMI on NA51089 are muxed outside the config functions and are not modelled.
  • Other Novatek SoCs. NA51055, NA51084, NA51090 and NA51103 are recognised but get no table.
  • Chip naming. The existing mapping of 0x5021 to "NT98562" looks wrong: the SDK's top.h calls 0x5021 NA51084, while NT98562 and NT98566 are both NA51089 (0x7021) and differ only in an eFuse package ID. This PR leaves that alone.

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

Copy link
Copy Markdown

PR Summary by Qodo

Add NA51089 pad mux, register dumps, and GPIO support

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Enable NA51089 detection and pad-mux decoding, fixing unsupported reginfo output on NT98566.
Diagram

graph TD
    SDK["SDK generator"] --> Claims["Generated claims"] --> Backend["Novatek backend"] --> TOP[("TOP registers")]
    Detect["Chip detection"] --> API["Padmux API"] --> Backend
    CLI["Reginfo and GPIO"] --> API
    CLI --> Controller["GPIO controller"]
    CLI --> TOP
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use the Linux GPIO character device for GPIO operations
  • ➕ Delegates GPIO access and concurrency control to the kernel.
  • ➖ Adds a runtime interface distinct from the project's existing register-backed GPIO paths.
  • ➖ Does not replace the TOP-register claims needed for pad-mux decoding and setting.

Recommendation: Keep the generated-claims backend: the SDK has executable mux logic but no pin table, and replay verification makes the derived table reviewable and reproducible. The register-backed GPIO path is consistent with existing vendor support; compare decoded pads with the kernel's pinmux summary on hardware before treating the mapping as verified.

Files changed (21) +7013 / -32

Enhancement (13) +6691 / -15
novatek_gpio.cImplement NA51089 GPIO register access +78/-0

Implement NA51089 GPIO register access

• Validates bonded-out pads and maps Linux GPIO numbers to controller banks. Reads DATA and DIR, and drives levels through write-one SET and CLR registers.

src/hal/novatek_gpio.c

novatek_gpio.hDefine the Novatek GPIO controller interface +36/-0

Define the Novatek GPIO controller interface

• Declares controller addresses, bank offsets, pad limits, and helpers for validation, register lookup, mapping-window detection, reads, and writes.

src/hal/novatek_gpio.h

novatek_padmux.cAdd a claim-based NA51089 pad-mux backend +452/-0

Add a claim-based NA51089 pad-mux backend

• Resolves pad functions from TOP location fields and gate bits, including ungated sensor modes and unnamed orphaned pads. Setting a function favors the claim requiring the fewest changes and clears competitors that would otherwise win; parsing accepts Novatek pad names and Linux numbers.

src/hal/novatek_padmux.c

novatek_padmux.hAdd the generated NA51089 pad and function table +1117/-0

Add the generated NA51089 pad and function table

• Contains bonded-pad definitions, field conditions, ordered function claims, and lookup selectors generated from the vendor driver's behavior.

src/hal/novatek_padmux.h

novatek_padmux_types.hDefine compact types for Novatek mux claims +73/-0

Define compact types for Novatek mux claims

• Introduces condition, claim, pad, selector, and SoC table types, including markers for ungated functions and gate-only selectors.

src/hal/novatek_padmux_types.h

novatek_reginfo.hList NA51089 registers for raw mux dumps +39/-0

List NA51089 registers for raw mux dumps

• Adds the TOP location registers and eight GPIO gate bitmaps to the register list used by plain 'reginfo'.

src/hal/novatek_reginfo.h

padmux.cDispatch NA51089 mux operations and pad parsing +12/-0

Dispatch NA51089 mux operations and pad parsing

• Selects the Novatek backend for NA51089 and delegates pad-name parsing to a detected backend when it provides a parser.

src/padmux.c

padmux.hExtend the pad-mux backend interface for vendor names +16/-4

Extend the pad-mux backend interface for vendor names

• Adds an optional pad parser to 'padmux_ops_t' and declares the Novatek backend. Clarifies that Novatek names and Linux GPIO numbers differ from HiSilicon bank-pin spelling.

src/padmux.h

padmux_names.cIntern Novatek mux and register names +1080/-0

Intern Novatek mux and register names

• Adds generated, build-conditional string initializers for Novatek pad, function, and register names used by the new tables.

src/padmux_names.c

padmux_names.hAdd conditional offsets for Novatek names +2162/-2

Add conditional offsets for Novatek names

• Extends the generated name structure and macros with Novatek entries while retaining build guards around optional names.

src/padmux_names.h

reginfo.cExpose Novatek register dumps and GPIO commands +179/-5

Expose Novatek register dumps and GPIO commands

• Routes NA51089 raw dumps, GPIO get/set/scan, and IR-cut hints through the new controller support. Detects the chip before parsing mux pad names so Novatek rejects incompatible HiSilicon spelling.

src/reginfo.c

gen_novatek_padmux.pyGenerate and replay-verify mux claims from the SDK +1437/-0

Generate and replay-verify mux claims from the SDK

• Instruments and runs vendor pinmux functions, combines field-write traces with source annotations, and checks generated claims against the resulting states. Provides SDK verification and a compiler-only fixture self-test, with explicit annotation errata.

tools/gen_novatek_padmux.py

gen_padmux_names.pyInclude Novatek sources in name generation +10/-4

Include Novatek sources in name generation

• Registers the generated Novatek pad-mux table and raw register list as sources for conditional name interning.

tools/gen_padmux_names.py

Bug fix (2) +9 / -0
chipid.cReport the NA51089 chip family +4/-0

Report the NA51089 chip family

• Maps the detected NA51089 chip ID to the 'na51089' family name.

src/chipid.c

novatek.cPublish the Novatek TOP chip ID for dispatch +5/-0

Publish the Novatek TOP chip ID for dispatch

• Sets 'chip_generation' from the kernel-provided TOP chip-ID word so pad-mux and GPIO support can select the detected SoC.

src/hal/novatek.c

Tests (1) +169 / -0
reginfo_test.cExercise NA51089 mux behavior and GPIO addresses +169/-0

Exercise NA51089 mux behavior and GPIO addresses

• Tests name parsing, field-and-gate changes, orphaned pads, ungated sensor modes, overlapping claims, and GPIO bank addresses. Adds NA51089 to the table-wide function round-trip checks.

src/reginfo_test.c

Documentation (3) +133 / -12
CLAUDE.mdDocument Novatek development and build conventions +19/-9

Document Novatek development and build conventions

• Describes the new selectable family, SDK-driven table generation, and vendor-specific GPIO controller layout.

CLAUDE.md

gpio.mdExplain Novatek GPIO numbering and mux hazards +16/-0

Explain Novatek GPIO numbering and mux hazards

• Documents accepted pad names and numbers, GPIO scanning and IR-cut behavior, and the effects of moving a peripheral or disabling a parallel sensor mode.

docs/gpio.md

padmux.mdDescribe the Novatek mux model and table provenance +98/-3

Describe the Novatek mux model and table provenance

• Explains location fields, per-pad gates, ungated sensor claims, and orphaned pads. Records the SDK generation procedure, source errata, and hardware and display-muxing limitations.

docs/padmux.md

Other (2) +11 / -5
.clang-format-hook-excludeExclude the generated Novatek table from formatting +1/-0

Exclude the generated Novatek table from formatting

• Adds the generated pad-mux header to the formatter exclusion list so regeneration does not conflict with formatting.

.clang-format-hook-exclude

CMakeLists.txtWire Novatek pad-mux and GPIO sources into builds +10/-5

Wire Novatek pad-mux and GPIO sources into builds

• Adds 'novatek' to selectable pad-mux families and backend sources. Includes the GPIO implementation when the Novatek vendor is selected.

CMakeLists.txt

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

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

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. MIPI pads can report the wrong function 🐞 Bug ≡ Correctness
Description
The generator builds MIPI lane claims from the target call's gate writes but omits the prerequisite
sensor-mode writes, leaving those claims with no conditions. When a lane's gate is clear outside CSI
mode, get() can report it as MIPI, while set() can choose the gate-only claim without selecting
CSI mode.
Code

src/hal/novatek_padmux.h[640]

+    {161, 0, PMX_MIPI_LVDS_DAT0, 1, 0}, /* H_GPIO1 */
Evidence
The harness says the MIPI lanes require a CSI-mode prefix but logs only the target call; claim
conditions come from that target's recorded blocks. The emitted MIPI claims have zero conditions,
which the resolver accepts whenever the gate is clear.

tools/gen_novatek_padmux.py[540-579]
tools/gen_novatek_padmux.py[741-752]
src/hal/novatek_padmux.h[636-640]
src/hal/novatek_padmux.c[144-165]
src/hal/novatek_padmux.c[398-408]

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

## Issue description
MIPI lane claims have no sensor-mode condition because prefix writes are omitted when the generator constructs claims.
## Fix Focus Areas
- tools/gen_novatek_padmux.py[540-579]
- tools/gen_novatek_padmux.py[741-752]
- src/hal/novatek_padmux.h[636-640]
## Recommended Fix
Preserve the prerequisite sensor-mode state when generating dependent claims, regenerate the table, and test reads and writes in both CSI and non-CSI modes.

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


2. Some vendor builds fail to link ✓ Resolved
Description
CMake adds novatek_gpio.c only when the Novatek vendor HAL is selected, but the new reginfo.c
GPIO dispatch calls its functions regardless of that selection. A build selecting the SigmaStar
vendor HAL and the Novatek pad-mux family therefore includes the new backend but has unresolved
Novatek GPIO symbols.
Code

CMakeLists.txt[R304-305]

+  if(_v STREQUAL "novatek")
+    list(APPEND COMMON_LIB_SRC src/hal/novatek_gpio.c src/hal/novatek_gpio.h)
Evidence
Backend selection depends on the pad-mux family, whereas the GPIO implementation depends on the
vendor selection. The new dispatch references that implementation without a vendor guard, so
selecting SigmaStar as the vendor leaves these added references undefined.

CMakeLists.txt[109-116]
CMakeLists.txt[289-309]
src/reginfo.c[3676-3680]
src/reginfo.c[3984-3988]
src/hal/novatek_gpio.c[30-36]

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

## Issue description
The Novatek GPIO dispatch is compiled into reginfo.c even when CMake omits its implementation from a selected-vendor build.
## Fix Focus Areas
- CMakeLists.txt[289-309]
- src/reginfo.c[3676-3680]
- src/reginfo.c[4397-4402]
## Recommended Fix
Include the Novatek GPIO implementation whenever its unguarded dispatch is compiled, or guard all Novatek GPIO references consistently with the vendor selection. Verify a build with IPCHW_VENDORS=sstar and IPCHW_PADMUX=novatek.

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



Remediation recommended

3. GPIO restoration can leave sensor pads busy 🐞 Bug ≡ Correctness
Description
gpio_row() advertises the gate bit as a one-write GPIO restoration even when the pad has an
ungated sensor claim. If that claim holds, setting the gate alone leaves the sensor function active;
nvt_set() must also drop the competing claim.
Code

src/hal/novatek_padmux.c[R191-195]

+        .gpio_func = (int)bit,
+        /* Setting the gate is the whole operation -- unless a sensor mode
+         * can hold the pad through it, and then it is not. */
+        .flags = IPCHW_PADMUX_F_GPIO |
+                 (has_ungated(soc, pd->pad) ? 0 : IPCHW_PADMUX_F_RMW),
Evidence
The resolver allows ungated claims to win with the gate set, and the setter performs additional
competitor-dropping writes. The public API reserves gpio_func for restoration achievable in one
write.

src/hal/novatek_padmux.c[144-177]
src/hal/novatek_padmux.c[180-195]
src/hal/novatek_padmux.c[369-374]
include/ipchw.h[53-59]
src/hal/novatek_padmux.h[636-643]

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 rows expose a one-write restoration value for pads whose sensor claims can remain active through the gate.
## Fix Focus Areas
- src/hal/novatek_padmux.c[180-196]
- src/hal/novatek_padmux.c[369-374]
## Recommended Fix
Set gpio_func to -1 when the pad has an ungated claim, so callers use ipchw_padmux_set() for the complete restoration.

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


4. Gate-only pads lose their restore hint 🐞 Bug ≡ Correctness
Description
func_row() sets gpio_func to -1 for every peripheral row, including gate-only functions on pads
without ungated claims. When get() returns such a function, its row omits the valid single-write
instruction to restore GPIO by setting the gate bit.
Code

src/hal/novatek_padmux.c[205]

+        .gpio_func = -1,
Evidence
The generated FSPI selectors use only the gate, and the getter returns func_row for a winning claim.
The API specifies that get() supplies gpio_func when restoring GPIO takes one write.

src/hal/novatek_padmux.c[199-220]
src/hal/novatek_padmux.c[282-287]
src/hal/novatek_padmux.h[689-696]
src/hal/novatek_padmux.h[741-749]
include/ipchw.h[139-148]

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

## Issue description
Gate-only functions return no GPIO restoration hint despite having a single-write restoration on pads without ungated claims.
## Fix Focus Areas
- src/hal/novatek_padmux.c[199-220]
- src/hal/novatek_padmux.c[282-287]
## Recommended Fix
For a winning gate-only claim where setting the gate suffices, populate gpio_func with the pad's gate bit; retain -1 when another claim requires additional writes.

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


5. A refused pad-mux change still rewrites registers 🐞 Bug ☼ Reliability
Description
nvt_set() writes every field of the chosen claim and clears the pad's gate bit before it calls
drop_competitors(). That helper can still return IPCHW_PADMUX_NO_FUNC when no claim wins, when
every non-zero field of the winning competitor is one the target needs, or when its 8 passes run
out, and nothing rolls back the earlier writes. When that happens, gpio mux   prints "cannot
carry", yet the board has already moved a peripheral's location field (which unmuxes it from its
other pads) and handed the pad off the GPIO controller.
Code

src/hal/novatek_padmux.c[R398-408]

+    for (int i = 0; i < best->ncond; i++) {
+        const novatek_cond_t *k = &soc->conds[best->cond + i];
+        if (!top_write_field(&t, k->reg, field_mask(k->shift, k->width),
+                             (uint32_t)k->value << k->shift))
+            return IPCHW_PADMUX_IO;
+    }
+    if (!(best->flags & NVT_UNGATED) &&
+        !top_write_field(&t, gr, gate_bit(pad), 0))
+        return IPCHW_PADMUX_IO;
+
+    return drop_competitors(soc, pad, &t, best);
Evidence
The target's fields and the gate are written at lines 398-406. The function then returns whatever
drop_competitors() returns, and that helper has three failure exits with no undo path (326, 335,
340). padmux_refuse() in reginfo.c reports NO_FUNC to the user as a plain failure, so the user is
told nothing changed when registers already did.

src/hal/novatek_padmux.c[398-408]
src/hal/novatek_padmux.c[315-341]
src/reginfo.c[3778-3800]

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

## Issue description
`nvt_set()` writes the target claim's fields and clears the gate, then calls `drop_competitors()`, which can fail with IPCHW_PADMUX_NO_FUNC. The writes already done are never undone, so a refused set still changes the hardware.
## Fix Focus Areas
- src/hal/novatek_padmux.c[357-409]
- src/hal/novatek_padmux.c[315-341]
## Recommended Fix
Record the original value of every TOP word that `nvt_set` writes. The `top_t` cache already holds the first-read values, so copy `t.val`/`t.have` before writing. If `drop_competitors` (or any later write) fails, write the saved words back before returning the error. Another option is to simulate the sequence first on a scratch `top_t` whose io writes only update the cache, and touch the real registers only when the simulation succeeds.

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



Informational

6. gpio usage text offers a pad format Novatek refuses 🐞 Bug ⚙ Maintainability
Description
gpio_get_cmd/gpio_set_cmd/gpio_mux_cmd still print "<gpio number> either number in 5_6 or 46
format", but on NA51089 nvt_parse_pad() deliberately returns -1 for any spelling containing '_'
that is not a known pad-group prefix. A Novatek user who follows the usage hint gets "Not a GPIO
number", and the kernel names this PR adds (P_GPIO22, MC17) are not mentioned anywhere in the
CLI help.
Code

src/hal/novatek_padmux.c[R436-437]

+    if (strchr(spec, '_'))
+        return -1;
Evidence
The Novatek parser rejects "5_2" (and reginfo_test checks that it does), while the usage strings in
reginfo.c still advertise the 5_6 form on every SoC.

src/hal/novatek_padmux.c[411-444]
src/reginfo.c[3740-3756]
src/reginfo_test.c[919-926]

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

## Issue description
The gpio subcommand usage says pads can be given as 5_6 or 46, but on Novatek the 5_6 form is refused and kernel names such as P_GPIO22 are accepted.
## Fix Focus Areas
- src/reginfo.c[3740-3756]
- src/reginfo.c[3891-3899]
## Recommended Fix
Change the usage text to say that 5_6 is the HiSilicon form, and that Novatek takes the Linux GPIO number or the kernel's pad name (P_GPIO22, MC17, HSI_GPIO9).

ⓘ 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 show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/hal/novatek_padmux.h
{161, 0, PMX_SENSOR_CCIR_2, 171, 2}, /* H_GPIO1 */
{161, NVT_UNGATED, PMX_SENSOR_12BITS, 137, 1}, /* H_GPIO1 */
{161, 0, PMX_SENSOR_CCIR_2, 138, 1}, /* H_GPIO1 */
{161, 0, PMX_MIPI_LVDS_DAT0, 1, 0}, /* H_GPIO1 */

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Mipi pads can report the wrong function 🐞 Bug ≡ Correctness

The generator builds MIPI lane claims from the target call's gate writes but omits the prerequisite
sensor-mode writes, leaving those claims with no conditions. When a lane's gate is clear outside CSI
mode, get() can report it as MIPI, while set() can choose the gate-only claim without selecting
CSI mode.
Agent Prompt
## Issue description
MIPI lane claims have no sensor-mode condition because prefix writes are omitted when the generator constructs claims.
## Fix Focus Areas
- tools/gen_novatek_padmux.py[540-579]
- tools/gen_novatek_padmux.py[741-752]
- src/hal/novatek_padmux.h[636-640]
## Recommended Fix
Preserve the prerequisite sensor-mode state when generating dependent claims, regenerate the table, and test reads and writes in both CSI and non-CSI modes.

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

Comment thread src/hal/novatek_padmux.c Outdated
Comment on lines +191 to +195
.gpio_func = (int)bit,
/* Setting the gate is the whole operation -- unless a sensor mode
* can hold the pad through it, and then it is not. */
.flags = IPCHW_PADMUX_F_GPIO |
(has_ungated(soc, pd->pad) ? 0 : IPCHW_PADMUX_F_RMW),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Gpio restoration can leave sensor pads busy 🐞 Bug ≡ Correctness

gpio_row() advertises the gate bit as a one-write GPIO restoration even when the pad has an
ungated sensor claim. If that claim holds, setting the gate alone leaves the sensor function active;
nvt_set() must also drop the competing claim.
Agent Prompt
## Issue description
GPIO rows expose a one-write restoration value for pads whose sensor claims can remain active through the gate.
## Fix Focus Areas
- src/hal/novatek_padmux.c[180-196]
- src/hal/novatek_padmux.c[369-374]
## Recommended Fix
Set gpio_func to -1 when the pad has an ungated claim, so callers use ipchw_padmux_set() for the complete restoration.

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

Comment thread src/hal/novatek_padmux.c
.func_name = padmux_name(s->name),
.gpio_name = padmux_name(pd->name),
.gpio_pad = pd->pad,
.gpio_func = -1,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Gate-only pads lose their restore hint 🐞 Bug ≡ Correctness

func_row() sets gpio_func to -1 for every peripheral row, including gate-only functions on pads
without ungated claims. When get() returns such a function, its row omits the valid single-write
instruction to restore GPIO by setting the gate bit.
Agent Prompt
## Issue description
Gate-only functions return no GPIO restoration hint despite having a single-write restoration on pads without ungated claims.
## Fix Focus Areas
- src/hal/novatek_padmux.c[199-220]
- src/hal/novatek_padmux.c[282-287]
## Recommended Fix
For a winning gate-only claim where setting the gate suffices, populate gpio_func with the pad's gate bit; retain -1 when another claim requires additional writes.

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

Comment thread CMakeLists.txt
Comment thread src/hal/novatek_padmux.c Outdated
Comment on lines +398 to +408
for (int i = 0; i < best->ncond; i++) {
const novatek_cond_t *k = &soc->conds[best->cond + i];
if (!top_write_field(&t, k->reg, field_mask(k->shift, k->width),
(uint32_t)k->value << k->shift))
return IPCHW_PADMUX_IO;
}
if (!(best->flags & NVT_UNGATED) &&
!top_write_field(&t, gr, gate_bit(pad), 0))
return IPCHW_PADMUX_IO;

return drop_competitors(soc, pad, &t, best);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

5. A refused pad-mux change still rewrites registers 🐞 Bug ☼ Reliability

nvt_set() writes every field of the chosen claim and clears the pad's gate bit before it calls
drop_competitors(). That helper can still return IPCHW_PADMUX_NO_FUNC when no claim wins, when
every non-zero field of the winning competitor is one the target needs, or when its 8 passes run
out, and nothing rolls back the earlier writes. When that happens, gpio mux <pad> <func> prints
"cannot carry", yet the board has already moved a peripheral's location field (which unmuxes it from
its other pads) and handed the pad off the GPIO controller.
Agent Prompt
## Issue description
`nvt_set()` writes the target claim's fields and clears the gate, then calls `drop_competitors()`, which can fail with IPCHW_PADMUX_NO_FUNC. The writes already done are never undone, so a refused set still changes the hardware.

## Fix Focus Areas
- src/hal/novatek_padmux.c[357-409]
- src/hal/novatek_padmux.c[315-341]

## Recommended Fix
Record the original value of every TOP word that `nvt_set` writes. The `top_t` cache already holds the first-read values, so copy `t.val`/`t.have` before writing. If `drop_competitors` (or any later write) fails, write the saved words back before returning the error. Another option is to simulate the sequence first on a scratch `top_t` whose io writes only update the cache, and touch the real registers only when the simulation succeeds.

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

Comment thread src/hal/novatek_padmux.c
Comment on lines +436 to +437
if (strchr(spec, '_'))
return -1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

6. Gpio usage text offers a pad format novatek refuses 🐞 Bug ⚙ Maintainability

gpio_get_cmd/gpio_set_cmd/gpio_mux_cmd still print "<gpio number> either number in 5_6 or 46
format", but on NA51089 nvt_parse_pad() deliberately returns -1 for any spelling containing '_'
that is not a known pad-group prefix. A Novatek user who follows the usage hint gets "Not a GPIO
number", and the kernel names this PR adds (P_GPIO22, MC17) are not mentioned anywhere in the
CLI help.
Agent Prompt
## Issue description
The gpio subcommand usage says pads can be given as 5_6 or 46, but on Novatek the 5_6 form is refused and kernel names such as P_GPIO22 are accepted.

## Fix Focus Areas
- src/reginfo.c[3740-3756]
- src/reginfo.c[3891-3899]

## Recommended Fix
Change the usage text to say that 5_6 is the HiSilicon form, and that Novatek takes the Linux GPIO number or the kernel's pad name (P_GPIO22, MC17, HSI_GPIO9).

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

`ipctool reginfo` on an NT98566 said "Platform is not supported": the
Novatek HAL never set chip_generation, and no Novatek table existed.

The NA51089 selects a pad's function a fourth way: a location field per
peripheral in the TOP block (SigmaStar's arrangement), plus a gate bit per
pad that has to be cleared before any field reaches it. A new backend,
src/hal/novatek_padmux.c, reads that as claims -- a name and the field
values under which the pad carries it -- and takes the first that holds.
set() writes the claim that changes least, clears the gate, and drops a
competitor that still wins by zeroing a field the target does not need.

There is no datasheet or pin table in the SDK, only the vendor driver's
pinmux_config_*() functions. tools/gen_novatek_padmux.py compiles those on
the host with every register write logged, runs every combination of up to
four options (96,550 calls), attributes each gate write to the field writes
of the blocks around it, and replays every state against the table. Names
come from top.h's annotations and top.csv; three places where those
disagree with the code are recorded as errata, one of them a vendor bug
that makes PIN_MISC_CFG_SP2CLK_3RD do nothing.

Also:
- chip_generation is the TOP chip-ID word.
- plain `reginfo` dumps the TOP mux registers and the eight gate bitmaps.
- `gpio get/set/scan` and the IR-cut hint drive the NA51089 GPIO
  controller (DATA/DIR/SET/CLR at 0xF0070000).
- pads may be given as the Linux GPIO number or the kernel's name
  (P_GPIO22, MC17, HSI_GPIO9); the HiSilicon 5_2 form is refused there.

No Novatek board is in the lab: verified against vendor source, by
reginfo_test's sweep and test_novatek(), and by the generator's replay.
ipcinfo does not grow; ipctool grows 16 KB on arm32 (9 KB after UPX).
@widgetii
widgetii force-pushed the novatek-padmux-237 branch from dbbf3a2 to 3f1662b Compare October 9, 2026 19:54
…lds the pad

From review of #239:
- set() that refuses after writing a claim's fields now writes back every
  TOP word it touched, so a refusal never leaves a peripheral moved half-way.
- the GPIO row of a pad an ungated sensor claim can hold carries gpio_func
  -1, since setting the gate alone does not free it.
- gpio usage text names the pad spellings each vendor takes.
- reginfo_test sets every function of a pad from every claim's own state
  (2110 sets) and checks it reads back, or that a refusal changed nothing.
@widgetii

widgetii commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

Review findings, one by one:

  1. MIPI lanes have no condition. Declined. The vendor's pinmux_config_mipi_lvds() writes no TOP field for CLK0, CLK1 or DAT0, only the HSI gates. SENSOR=CSI is set by the sensor group, and the vendor's own MIPI configuration does not select it either. Its CSI check on DAT1-3 is a software precondition. The model it gives is: a cleared HSI gate with no field claiming the pad is the PHY lane. When a parallel or CCIR mode is set, its more specific ungated claims win over that.
  2. Link failure with IPCHW_VENDORS=sstar. Not reproducible. The Novatek GPIO dispatch in reginfo.c sits under #ifndef STANDALONE_LIBRARY, so libipchw never references it, and the ipctool executable always links novatek_gpio.c. Configurations IPCHW_VENDORS=ingenic and IPCHW_PADMUX=novatek with IPCHW_VENDORS=novatek were both built and tested.
  3. GPIO row advertises a one-write restore. Fixed in 8df45dd: gpio_func is -1, and the row has no F_RMW, on a pad an ungated sensor claim can hold.
  4. Function rows have no restore hint. Declined. A function row's address/func_mask describe the peripheral's field, not the gate, and gpio_func is a value for that same field. No value of that field restores GPIO, so -1 is the honest answer.
  5. A refused set() leaves writes behind. Fixed in 8df45dd: every TOP word the call touched is written back. A new test runs set() for every function of a pad, starting from every claim's own state, 2110 sets in all. Each must read back, or a refusal must leave the registers untouched; none were refused.
  6. Usage text offers the 5_6 form. Fixed in 8df45dd.

@widgetii
widgetii merged commit 18eb2b9 into master Oct 9, 2026
5 checks passed
@widgetii
widgetii deleted the novatek-padmux-237 branch October 9, 2026 20:10
widgetii added a commit that referenced this pull request Oct 10, 2026
…me (#240)

* novatek: NA51055 and NA51084 tables, the primary LCD, and 0x5021's name

The three things #239 left open.

NA51055 (NT9852x) and NA51084 (NT98528/NT98529). The NA51089 SDK carries
the na51055 driver too: same TOP layout without the DSI group, more S, L
and D pads, and one driver for both dies that branches on
nvt_get_chip_id(). The generator now runs a driver per family and each
family once per die, with the chip-ID call answered by a constant, and
writes one table per die. NA51084 alone has I2C4/5, SPI4/5 and RGMII.
Their top.h annotations disagree with their code in ten places -- MISC
copied from NA51089, PWM8..11's HSI location commented out ("52x
compatible") -- so the errata are now per driver; NA51089's PICNT2
erratum is exactly wrong on NA51055.

The LCD. pinmux_select_primary_lcd() is run as every LCD type with every
set of feature flags, its local copies of REG2 and the L and DSI gates
turned into the globals every other function writes. That needed one rule
more: a field an enclosing block wrote and a nested block overwrote before
the gate write takes the nested value (LCD_TYPE is reset at the top and
set in a switch). And options are read only from `& PIN_...` tests and
case labels, so a range test cannot name a pad. TV and HDMI have no pads
on these dies: pinmux_set_host() answers E_ID for them.

Two resolver fixes the new tables exposed. Of two claims with alike
conditions the gated one now wins, and set() of an ungated claim writes
the gate to GPIO as the vendor does: CCIR8 data and CCIR8 sync both hold
under SENSOR2=2. The replay now reports a refinement that changes nothing
instead of passing over it silently.

0x5021 was named NT98562. It is NA51084: u-boot's NA51084 clock code
drives the _528_ registers and allows 1200 MHz only on an NT98529. It now
reports NT98528. NT98562 and NT98566 share 0x7021 and are told apart only
by an eFuse word the SDK decodes in a binary.

Not done, and why: NA51090 and NA51103 have no pinmux driver in this SDK
or anywhere public that was found (NA51090's registers are also above
4 GB); NA51068 is a per-pad selector part with no chip ID here.

reginfo_test sweeps all three dies and sets every function from every
claim's state (3201, 2118 and 2342 sets, none refused). ipcinfo is
byte-identical on arm32; ipctool grows 20 KB (6.5 KB after UPX).

* novatek: LCD feature flags were never run; names stay within a group

From review of #240.

The LCD flags (TE, DE, HVLD/VVLD, FIELD, NO_HVSYNC) are spelt `0x01 << 23`
in top.h, which the enum parser read as no value, so they were taken for
LCD types: the harness never ran a type with DE, every colour claim
required DE off, and LCD_DE_ENABLE required LCD_TYPE = GPIO. A real panel
with DE read as unclaimed and set(LCD_DE_ENABLE) would have turned it off.
Enumerator values are now evaluated for shifts and ors, and an LCD enum
with no readable flags is fatal.

That exposed a second bug: a state no source names inherited the name of
any annotated state whose conditions are a subset of its own, across
groups. NA51055's RMII claims L_GPIO0..10 with no condition at all, so
every LCD claim there was named TXD0, CRS_DV and so on. Inheritance is now
within one group.

Also:
- bonded_pads() skips a missing count macro only for the DSI group; any
  other missing one is a renamed macro and is fatal again.
- NA51055 is named by its die when the device tree has no model, so a
  board without one is still detected.
- novatek_open_i2c_fd() tests NA51103 by chip ID: the ID path names it
  NT98332G, so the "NA51103" name test never matched and its sensor bus
  was never 1.
- nvt_get_chip_id() names through one strcpy; ipcinfo stays byte-identical.

reginfo_test: a DE panel reads as the LCD on both the colour and the DE
pin, and setting DE keeps the panel type. Every-state sets: 4363, 4038 and
4347, none refused. The selftest covers shifted enum values and
cross-group inheritance, and fails with either fix reverted.
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.

ipctool not support reginfo for novatek

1 participant