Skip to content

Introduce the ast10x0 i3c peripheral#278

Open
stevenlee7189 wants to merge 18 commits into
OpenPRoT:mainfrom
stevenlee7189:ast10x0-i3c
Open

Introduce the ast10x0 i3c peripheral#278
stevenlee7189 wants to merge 18 commits into
OpenPRoT:mainfrom
stevenlee7189:ast10x0-i3c

Conversation

@stevenlee7189

Copy link
Copy Markdown
Collaborator

Overview

This PR aligns the AST10x0 I3C driver design with
https://github.com/rusty1968/pac-design-patterns.git.

Pattern Alignment

Confined-unsafe MMIO facade

The design follows the confined-unsafe MMIO facade pattern by concentrating the
MMIO construction contract and PAC-facing register access inside the
I3cRegisters layer.

Cooperative-yield bounded-poll device

The design also follows the cooperative-yield bounded-poll pattern.

The hardware binding accepts a caller-supplied yield strategy, and polling paths
are explicitly bounded. As a result, wait behavior is portable across execution
models and timeout remains an observable, structured failure mode rather than an
unbounded hang.

Interrupt-model exceptions

There are two intentional structural exceptions worth calling out explicitly.

ISR-side aliasing

Interrupt handling requires an ISR-side register handle because the IRQ path
cannot directly borrow the thread-owned controller value. This alias is a narrow
exception made specifically for interrupt dispatch.

Global dispatch and event handoff

The IRQ path also relies on a bounded per-bus global dispatch registry and
atomic event handoff. This is an interrupt-integration mechanism, not a general
ownership model for the driver.

These exceptions are deliberate and localized. They do not change the primary
architecture, which remains a runtime-selected single-driver design.

Test Result

image

stevenlee7189 and others added 11 commits June 3, 2026 10:26
Port the AST1060 I3C controller/target driver from aspeed-rust
(src/i3c @ ce3b567) into the ast10x0_peripherals crate, mirroring the
existing I2C port and following the pac-design-patterns conventions.

Driver (peripherals/i3c/): ccc, config, constants, controller, error,
hardware, ibi, mod, types. hal_impl is folded into controller as
inherent methods (proposed_traits is unavailable in openprot and
embedded-hal has no I3C trait).

Design-pattern conformance:
- Confined-`unsafe` MMIO facade: raw `*const` register pointers, a single
  `unsafe fn new`, private `&'static` derefs, `!Sync`; no unsafe or PAC
  types leak above the facade.
- Cooperative-yield bounded poll: an injected `Y: FnMut(u32)` yield
  closure replaces embedded-hal DelayNs, type-erased at the poll loops.
- Borrow-arbitrated exclusivity for per-call state; the global IBI/IRQ
  plane is kept (documented delta) since an ISR cannot borrow a
  stack-owned device.

Notable deltas: drop the Logger generic and the `#[no_mangle]` ISR
symbol exports (kernel owns the vector and calls dispatch_i3c_irq);
heapless 0.9 SPSC with edition-2024 `addr_of_mut!`; reachable
transfer/clock paths hardened to be panic-free (no_panics_test) without
changing success-path behavior.

Also: add scu pinctrl groups (PINCTRL_I3C0..3 / PINCTRL_HVI3C0..3, the
HV groups clearing the conflicting LV pad bits); wire critical-section
and cortex-m `critical-section-single-core` into the crate universe; add
i3c_init and i3c_irq tests, the latter a faithful controller/target IBI
pair mirroring aspeed-rust tests-hw on I3C2 HV.

See target/ast10x0/peripherals/i3c/plans/goal.md for the behavioral
parity plan, authority pin, and deltas ledger.

Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One driver type now manages all four bus instances: the Instance
type-parameter and its per-bus impl macro are removed, and the bus is
selected at runtime in I3cRegisters::new(bus), mirroring the SmcRegisters
shape. All I3C MMIO unsafe (pointer derefs) is confined to the new
registers.rs; hardware.rs accesses the blocks through safe delegates.

IRQ ownership moves to the integration layer: enable_irq/disable_irq are
dropped from the hardware traits and the driver only exposes
dispatch_i3c_irq plus an i3c_bus_interrupt(bus) mapping for the NVIC line
the kernel owns. The per-bus handler registry becomes single-shot
(typed Busy on a second claim) and panic-free (UnsafeCell + critical
section, same rationale as the IBI rings, whose closure-based access is
also replaced by leaf push/pop helpers).

The controller is rebuilt around an ISR-shared I3cCore pinned at a
'static address, so the trampoline pointer's validity is type-guaranteed
rather than a convention, with a light two-state lifecycle
(Uninitialized -> start() -> Ready) matching the SMC precedent. Tests
construct the core via cortex_m::singleton!, which also moves the large
config off the 2 KiB bootstrap stack, and unmask the NVIC line
themselves after start().
Complete the SmcRegisters alignment: instead of only confining the
pointer derefs, every register operation now goes through an
intent-named method on I3cRegisters (enable_controller_primary,
assert_all_queue_resets, dat_program_addr, ...), so hardware.rs is pure
protocol logic with zero PAC register types and zero MMIO unsafe. Its
remaining unsafe is non-MMIO only: the IRQ-registry critical-section
leaves, the forwarded constructor gate, the curr_xfer handoff cast, and
the transfer buffer re-slicing.

The per-bus I3C-global reg0/reg1 dispatch and the eight-slot DAT
dispatch move into private macros inside the facade (the bus index
already lives there), deleting the macro layer from hardware.rs along
with its unreachable fallback arms. The FIFO word loops sink into the
facade as tx_fifo_write/rx_fifo_read/rx_fifo_drain/ibi_fifo_*, which
also retires the closure-taking rd_fifo/drain_fifo trait methods.
Restore console_backend.rs to main: the lock -> try_lock change rode
along in the I3C IRQ test stabilization commit but alters shared console
behavior for every target, which does not belong in the I3C series. If
ISR-context console writes need a deadlock-free path, that is its own
change with its own review.
The ISR no longer fabricates a &mut over any thread-owned object. The
per-bus registry now parks an IsrCtx (an ISR-side register handle plus
the role flag) instead of a raw context pointer, and the service routine
works exclusively through &self register methods, per-bus ISR_EVENTS
atomics, and the global IBI rings. Master transfer completion follows
the SMC flag-and-defer model: the ISR masks the completion sources and
latches the status; the polling thread drains the response queue into
its own transfer and re-enables the sources. Target-mode completions
and the master-assigned dynamic address move into the per-bus event
block; ISR-side halt/resume recovery (which needs the wait policy) is
deferred to the thread as a fault flag.

This dissolves the machinery the old design needed: the curr_xfer
AtomicPtr handoff and its unsafe reconstruction, I3cXfer's completion
flag, and the pinned 'static I3cCore (with its singleton! storage and
Pin plumbing) are all gone. The controller is a plain owned value with
zero unsafe; in-flight transfer overlap is structurally impossible
because the transfer never leaves the thread.

The controller borrows its I3cConfig (&'c mut) rather than owning it:
the config embeds ~0.5 KiB of device tables, and the typestate
transition moves the controller by value, so owning the config would
transiently stack multiple copies and overflow the 2 KiB kernel
bootstrap stack (observed as a silent hang during bring-up on the EVB).
Borrowing keeps exactly one config alive in the caller's frame, the
same footprint as the validated pre-rework layout, and matches the I2C
driver's caller-owned-resource convention.

Behavioral deltas from the reference ISR, all thread-visible only:
SIR address validation moves from the ISR to the consumer (which
already validates), and target error recovery runs at the next
operation instead of inside the interrupt.

Verified on a two-board AST1060 setup: the dual-device IBI exchange
test passes 10/10 exchanges.
@stevenlee7189 stevenlee7189 requested a review from rusty1968 June 4, 2026 08:33
Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
Make init, halt, and reset_ctrl return Result instead of swallowing poll
timeouts; start() releases the IRQ slot on init failure so a retry is not
wedged on Busy.

Harden priv_xfer_build_cmds: cap transfers at 16 (4-bit TID aliased larger
batches) and reject over-long lengths (16-bit field truncated silently),
both before consuming any buffer. Replace the unsafe raw-pointer reborrow
with Option::take.

Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
Set i2c_buses: &[] and handle init's new Result in the three i3c
targets; the i2c commit added both but only updated i2c tests.

Patch off pigweed's syscall_defs incompatibility gate. The roll makes
kernel hard-depend on syscall_defs, which stays incompatible unless
userspace_build is set, breaking every userspace=False system_image.
syscall_defs is pure type defs the kernel always pulls in, so dropping
the constraint changes no runtime behavior. Re-apply on next roll.

Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
- Drain RX/IBI FIFO leftovers to prevent transfer misalignment
- SETNEWDA now updates DAT slot + address book
- Address bookkeeping: alloc marking, slot bounds, occupancy checks
- Master error recovery preserves IBI queue (don't reset it)
- validate_clock: fix overflow panic

Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
attach_i2c_dev + i2c_write/read/write_read and an embedded-hal I2c impl
route legacy devices through DAT slots by static address. Add
SETMWL/SETMRL/GETMWL/GETMRL/GETMXDS, and latch target-side MWL/MRL
updates from SLV_MAX_LEN.

Signed-off-by: Steven Lee <steven_lee@aspeedtech.com>
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