Registering an RTC NVMEM Provider-Free Linux Device Drivers Course

Registering an RTC NVMEM Provider

Free Linux Kernel Development Course — NVMEM Framework, Part 3

Chapter 12 · Lecture 3
NVMEM Framework
Kernel 6.x APIs
nvmem provider driver rtc_nvmem_register nvmem_reg_read_t free linux kernel development course free linux device drivers course free embedded linux course

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_t and nvmem_reg_write_t callbacks 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_info and 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 Registration API
int rtc_nvmem_register(struct rtc_device *rtc, struct nvmem_config *nvmem_config); void rtc_nvmem_unregister(struct rtc_device *rtc);

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().

ep_rtc_demo.c — probe function
struct ep_rtc_priv { struct regmap *map; struct rtc_device *rtc; }; static int ep_rtc_nvram_read(void *priv, unsigned int offset, void *val, size_t bytes) { struct ep_rtc_priv *ep = priv; return regmap_bulk_read(ep->map, 0x20 + offset, val, bytes); } static int ep_rtc_nvram_write(void *priv, unsigned int offset, void *val, size_t bytes) { struct ep_rtc_priv *ep = priv; return regmap_bulk_write(ep->map, 0x20 + offset, val, bytes); } static int ep_rtc_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ep_rtc_priv *ep; struct nvmem_config nvmem_cfg = { .name = “ep_rtc_nvram”, .word_size = 1, .stride = 1, .size = 32, .reg_read = ep_rtc_nvram_read, .reg_write = ep_rtc_nvram_write, }; int ret; ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); if (!ep) return -ENOMEM; ep->map = devm_regmap_init_i2c(client, &ep_rtc_regmap_cfg); if (IS_ERR(ep->map)) return PTR_ERR(ep->map); ep->rtc = devm_rtc_allocate_device(dev); if (IS_ERR(ep->rtc)) return PTR_ERR(ep->rtc); ep->rtc->ops = &ep_rtc_ops; ret = rtc_register_device(ep->rtc); if (ret) return ret; /* Only now, after the RTC device exists, register its NVMEM */ nvmem_cfg.priv = ep; ret = rtc_nvmem_register(ep->rtc, &nvmem_cfg); if (ret) dev_warn(dev, “failed to register nvram: %d\n”, ret); return 0; } static void ep_rtc_remove(struct i2c_client *client) { struct ep_rtc_priv *ep = i2c_get_clientdata(client); rtc_nvmem_unregister(ep->rtc); }

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:

Provider Callback Prototypes
typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset, void *val, size_t bytes); typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset, void *val, size_t bytes);
ParameterMeaning
privThe opaque context you set in nvmem_config.priv — usually your driver’s private struct
offsetByte offset into the NVMEM device where the operation starts, relative to the device, not any single cell
valFor read: the buffer to fill. For write: the buffer holding data to write
bytesNumber 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() before rtc_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 bytes equals the size of val. The framework may ask for a partial transfer smaller than the buffer; copying a fixed, hardcoded length instead of respecting bytes silently 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_write callbacks thin — delegate the actual transport I/O to regmap or 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 remember rtc_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 + bytes against your device’s real storage size inside the callbacks — the framework does bounds-check against the configured size, 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_t contract, keeping consumer code fully hardware-agnostic.
  • Respect the bytes parameter 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

Leave a Reply

Your email address will not be published. Required fields are marked *