Registering an RTC NVMEM Provider
Free Linux Kernel Development Course — NVMEM Framework, Part 3
In the previous lecture of this free linux kernel development course we built a
generic NVMEM provider from scratch using nvmem_register(). Real hardware, however, often
hides its storage inside a bigger device — the most common example being an RTC chip that also carries
a few bytes of battery-backed NVRAM. The kernel gives RTC drivers a dedicated shortcut for exposing that
storage as an nvmem provider driver, and every NVMEM provider — RTC-backed or not — must
implement the same pair of read/write callbacks so the framework can move bytes in and out of the chip.
This lecture covers both: the RTC-specific registration path, and the generic
nvmem_reg_read_t / nvmem_reg_write_t contract every provider honors.
What You Will Learn
- Why RTC drivers get a dedicated NVMEM registration API instead of calling
nvmem_register()directly - The exact ordering rule between RTC registration and NVMEM registration, and why it exists
- How to write the
nvmem_reg_read_tandnvmem_reg_write_tcallbacks that back a real provider - A complete, original demo RTC driver that registers an NVMEM region
- Common ordering and buffer-handling mistakes that break NVMEM providers in practice
Prerequisites
- NVMEM data structures and
nvmem_register()from Lecture 1 of this chapter struct nvmem_cell_infoand cell declaration from Lecture 2- Basic familiarity with the RTC subsystem (
struct rtc_device,rtc_register_device()) is helpful but not required
Why RTC Gets a Dedicated NVMEM Path
Plenty of RTC chips — think coin-cell-backed real-time clocks used in embedded boards — ship with a small
block of non-volatile RAM sitting right next to the clock registers. Historically the RTC subsystem grew its
own NVRAM interface for this before the generic NVMEM framework existed, and a lot of user space still expects
that legacy behavior. Rather than force every RTC driver to hand-roll an NVMEM registration call and reimplement
that legacy compatibility, the RTC core exposes two small wrapper functions, declared in
drivers/rtc/nvmem.c and gated behind the RTC_NVMEM kernel config option:
rtc_nvmem_register() takes the already-allocated rtc_device and a normal
nvmem_config — the same structure type you used in Lecture 2 — and does the NVMEM registration on
the driver’s behalf, while also wiring in the legacy NVRAM sysfs compatibility that plain RTC user space still
looks for. It returns 0 on success and a negative errno otherwise, exactly like
nvmem_register(). rtc_nvmem_unregister() is its mirror image, called from the
driver’s remove() path.
The One Rule That Matters: Ordering
There is exactly one ordering constraint you cannot get wrong: rtc_nvmem_register() must be
called after rtc_register_device() has already succeeded, never before. The reason is
structural, not arbitrary — the NVMEM registration internally associates the new NVMEM device with the RTC
device that already exists in the device model, and needs that RTC device node to be present and fully
registered first. Call it too early and registration either fails outright or leaves you with an NVMEM device
that has no valid parent to hang sysfs attributes off.
The nvmem_config you pass in can safely live on the stack, exactly as with plain
nvmem_register(): every field is copied into the internal nvmem_device during
registration, so nothing needs to survive past the function call.
An Original RTC NVMEM Provider
Let’s build a small, self-contained demo: a fictional RTC chip ep-rtc that exposes 32 bytes of
battery-backed scratch memory alongside its clock registers. We register the RTC device first, then hand its
storage to the NVMEM framework through rtc_nvmem_register().
Notice that a failure to register the NVMEM region is treated as non-fatal — the RTC keeps working as a
clock even if its scratch storage can’t be exposed, which is the right failure mode for a “bonus” feature like
this. Also notice rtc_nvmem_unregister() being called explicitly in remove(); the
registration was not done through a devm_ variant here, so cleanup must be explicit.
The Read/Write Callback Contract
Whether you register through nvmem_register() directly or through
rtc_nvmem_register(), every provider ultimately plugs into the framework through the same two
callback types. Understanding their exact contract is what separates a provider driver that works under load
from one that silently corrupts data:
| Parameter | Meaning |
|---|---|
priv | The opaque context you set in nvmem_config.priv — usually your driver’s private struct |
offset | Byte offset into the NVMEM device where the operation starts, relative to the device, not any single cell |
val | For read: the buffer to fill. For write: the buffer holding data to write |
bytes | Number of bytes to transfer — not necessarily the full size of val |
Both callbacks are independent of the transport underneath — I2C, SPI, MMIO, or anything else — which is exactly what lets consumer drivers stay hardware-agnostic. A read callback should return the number of bytes successfully read on success and a negative errno on error; a write callback returns bytes written successfully, negative errno on error. Getting the return-value convention wrong is one of the most common bugs in NVMEM providers, covered below.
Common Mistakes
- Registering NVMEM before the RTC device. Calling
rtc_nvmem_register()beforertc_register_device()succeeds is the single most common bug in RTC NVMEM providers — the registration will fail or attach to a half-initialized device. - Assuming
bytesequals the size ofval. The framework may ask for a partial transfer smaller than the buffer; copying a fixed, hardcoded length instead of respectingbytessilently reads or writes garbage past the intended region. - Forgetting to unregister on the error path. If
probe()fails after NVMEM registration succeeded, the driver must call the matching unregister function before returning the error, or the NVMEM device leaks. - Ignoring the callback’s return value in consumer code. Both callbacks can legitimately return fewer bytes than requested on a bus error; treating any non-negative return as full success hides transient I/O failures.
Best Practices
- Keep
reg_read/reg_writecallbacks thin — delegate the actual transport I/O toregmapor your bus API rather than open-coding bus transactions inside them. - Treat NVMEM registration failure in an RTC driver as non-fatal unless the NVMEM region is the device’s primary purpose — don’t fail the whole probe over a bonus storage feature.
- Prefer
devm_rtc_allocate_device()for the RTC device itself, but rememberrtc_nvmem_register()still needs an explicit unregister call in most kernel versions — check whether a devm-managed variant exists for the API level you’re targeting before assuming automatic cleanup. - Validate
offset + bytesagainst your device’s real storage size inside the callbacks — the framework does bounds-check against the configuredsize, but defensive checks make debugging a misconfigured cell far easier.
Summary / Key Takeaways
rtc_nvmem_register()/rtc_nvmem_unregister()are RTC-specific wrappers around the generic NVMEM provider registration, adding legacy NVRAM compatibility for free.- NVMEM registration must always happen after the RTC device itself is registered — never before.
- Every provider, RTC-backed or not, implements the same
nvmem_reg_read_t/nvmem_reg_write_tcontract, keeping consumer code fully hardware-agnostic. - Respect the
bytesparameter exactly and propagate real error codes — these two habits prevent the majority of real-world NVMEM provider bugs.
Conclusion
Registering NVMEM storage from an RTC driver looks like a two-line addition, but it only works reliably once you respect the ordering rule against RTC registration and implement honest read/write callbacks that respect the exact byte counts the framework asks for. With the provider side now solid, the next lecture in this free linux device drivers course moves to how NVMEM providers describe their storage layout in the device tree — the piece that lets consumers ask for cells by name instead of raw offsets.
Interview Questions
Why can’t rtc_nvmem_register() be called before rtc_register_device()?
Because NVMEM registration associates the new NVMEM device with the RTC device’s node in the device model, which must already exist and be fully registered for that association to succeed.
What does the bytes parameter in nvmem_reg_read_t represent, and why can it differ from the buffer size?
It’s the exact number of bytes the framework wants transferred for this call, which can be less than the full buffer when a partial read or write is requested — the callback must honor it precisely.
Can an nvmem_config passed to rtc_nvmem_register() be a stack variable?
Yes — every field is copied into the internal nvmem_device structure during registration, so
the config does not need to persist after the call returns.
What should a driver do if rtc_nvmem_register() fails?
In most cases treat it as non-fatal and continue — the RTC device itself is still fully functional; log a warning rather than failing the whole probe, unless the NVMEM region is the device’s primary purpose.
Are the read/write callback prototypes tied to any specific bus?
No — nvmem_reg_read_t and nvmem_reg_write_t are transport-agnostic; the same
contract applies whether the underlying device sits on I2C, SPI, or MMIO.
FAQ
Do I need RTC_NVMEM enabled in my kernel config to use this API?
Yes — the RTC-related NVMEM framework API is compiled in only when the RTC_NVMEM kernel
config option is enabled.
Does every RTC driver need to register NVMEM storage?
No — only RTC chips that actually expose extra non-volatile storage alongside the clock registers need this call; plain RTC-only chips skip it entirely.
What happens to user space compatibility when using rtc_nvmem_register()?
It wires in the legacy RTC NVRAM sysfs interface automatically, so older user space that expects the traditional RTC NVRAM path keeps working alongside the modern NVMEM sysfs interface.
Is the read/write callback contract the same for every NVMEM provider, RTC or otherwise?
Yes — nvmem_reg_read_t and nvmem_reg_write_t are the universal provider
contract used across the whole NVMEM framework, not something specific to RTC.
Can a read or write callback legitimately transfer fewer bytes than requested?
Yes, particularly on a transient bus error — the callback should return the actual number of bytes transferred on success, or a negative errno on outright failure.
Continue the Free Linux Kernel Development Course
Next up: describing NVMEM providers and their cells in the device tree.
Next Lecture Browse Full Course