Merlin Principles
Design intent
Merlin implements a small, explicit contract between application code, userspace drivers, and Camelot-OS platform services.
The framework design goals are:
keep hardware-specific logic in drivers (register programming, protocol state);
keep platform-specific integration in Merlin (DTS metadata, mapping, pinmux/GPIO setup, IRQ routing);
provide a stable, family-oriented API so upper-layer code remains portable across SoCs and concrete driver implementations.
Compared to a monolithic BSP style, Merlin reduces duplication in probe/init plumbing while preserving low-level control where it matters: MMIO sequencing, timeouts, and hardware state machines.
Public interface map
The complete public API surface is exposed from include/merlin and can be
split into four layers:
Header family |
Purpose |
Key symbols |
|---|---|---|
|
Generic platform driver lifecycle and services |
|
|
Unified user-facing API per bus class |
|
|
Bus contracts and configuration enums/structures |
|
|
Architecture-neutral MMIO and polling helpers |
|
Driver object model
Every platform-backed driver is anchored by struct platform_device_driver.
At runtime, Merlin populates fields from generated DeviceTree metadata:
label: matchessentry,labeland is the primary runtime selector;devinfo: resolved metadata (base address, size, IRQs, pin data, and backend-specific attributes);devh: kernel device handle bound at registration;platform_fops.isr: ISR callback entry used by centralized IRQ dispatch;type: backend discriminator (I2C, SPI, USART, USB, …).
This keeps the platform context in one deterministic object owned by the driver instance.
Lifecycle contract
Across I2C, SPI, USART, CAN, and USB sample drivers, the same lifecycle is used:
*_probe(label)Register with Merlin and resolve DTS metadata ownership.*_init(...)Map MMIO, configure GPIO/pinctrl, initialize hardware state.Runtime operations Execute transfers, endpoint I/O, or serial transactions.
*_release(label)Disable hardware as needed and unmap resources.
The API is intentionally strict: runtime operations should reject calls when probe/init has not completed or when labels do not match the registered instance.
Error model
include/merlin/platform/api/generic.h defines drv_status_t as a common
driver status space.
The sample drivers consistently use the following categories:
state errors:
DRV_ERROR_INVSTATE,DRV_ERROR_NOTREGISTERED;parameter errors:
DRV_ERROR_INVPARAM;platform/setup errors:
DRV_ERROR_CONFIGURATION,DRV_ERROR_MAPFAILED;transfer/retry semantics:
DRV_ERROR_TIMEOUT,DRV_ERROR_AGAIN;capability mismatches:
DRV_ERROR_UNSUPPORTED_CFG,DRV_ERROR_UNSUPPORTED.
For robust upper layers, treat DRV_ERROR_AGAIN as a soft failure and most
other errors as hard failures requiring reconfiguration or reset.
MMIO and polling principles
All sample bus drivers use include/merlin/io.h for register access.
Recommended pattern:
compute register addresses from
devinfo->baseaddrand local offsets;perform register reads/writes through
merlin_ioread*andmerlin_iowrite*only;gate hardware state transitions with bounded polls (
merlin_iopoll32_until_set/clear) and return timeout errors when limits are reached.
This keeps code portable across supported architectures while preserving deterministic behavior for bring-up and fault handling.
IRQ routing principle
Merlin centralizes IRQ-to-driver routing through
merlin_platform_driver_irq_displatch(IRQn).
In an application event loop, the task forwards the IRQ number to Merlin;
Merlin then resolves the owning driver and calls the registered
platform_fops.isr callback for that instance.
This allows one event loop to serve multiple drivers without duplicating IRQ-number-to-device lookup logic in each application.
Portability rule from sample drivers
The sample implementations export unified API symbols using aliasing, for example:
drv_status_t i2c_probe(uint32_t label)
__attribute__((alias("stm32_i2c_driver_probe")));
This pattern decouples application code from vendor-specific function names. The same upper-layer binary contract can therefore target different concrete drivers as long as they expose Merlin’s public API symbols.