NVMEM Framework in Linux-Free Linux Device Drivers Course

NVMEM Framework in Linux

Continue this free Linux kernel development course with a deep dive into non-volatile memory access

Chapter 12
Kernel 6.x
Beginner Friendly

Every SoC ships with a handful of bytes that never change once written: a MAC address burned into an EEPROM, a calibration value stuck in an eFuse, a board revision ID sitting in a tiny I2C memory chip. If you’ve been following this free Linux kernel development course, you already know the kernel likes to abstract recurring hardware patterns behind a single framework instead of letting every driver reinvent the wheel. NVMEM — short for Non-Volatile MEMory — is exactly that abstraction for read-mostly storage devices, and it’s the subject of this lecture.

NVMEM framework free linux kernel development course free linux device drivers course nvmem_device nvmem_config EEPROM driver Linux

What You Will Learn

  • Why the kernel introduced a dedicated NVMEM framework instead of letting each EEPROM/eFuse driver do its own thing
  • The producer/consumer design NVMEM shares with other kernel subsystems you’ve seen in this free Linux kernel development course
  • What a “cell” is and why it’s the real unit of work in NVMEM
  • Every field of struct nvmem_device and struct nvmem_config, explained in plain language
  • How to write a minimal, original NVMEM provider skeleton against the current kernel API
  • Mistakes beginners make the first time they register an NVMEM device

Prerequisites

  • Comfortable reading and writing C
  • Basic platform driver / probe-remove experience (covered earlier in this free Linux device drivers course)
  • A Linux kernel 6.x source tree for cross-referencing (drivers/nvmem/, include/linux/nvmem-provider.h, include/linux/nvmem-consumer.h)
  • Device Tree basics are helpful but not required for this lecture

Why This Free Linux Kernel Development Course Covers NVMEM

Before NVMEM existed, drivers for EEPROMs, eFuses, and similar small storage chips lived scattered across drivers/misc/. Each one exposed its own sysfs attributes, its own ioctl-like conventions, and its own way of letting a consumer driver ask “give me my MAC address.” That duplication meant every new chip needed a fresh implementation of essentially the same read/write plumbing, and every consumer driver needed chip-specific glue to talk to whichever provider happened to be on that board.

NVMEM solves this the same way the kernel solves most duplication problems: pull the common behavior into a shared framework, and let individual chip drivers register into it instead of reimplementing it. On top of that, NVMEM adds first-class Device Tree support so a consumer node can simply point at a “cell” — a named, offset-based slice of the storage — without caring which physical chip backs it.

What Is the NVMEM Framework?

At its core, NVMEM is deliberately small. It doesn’t try to be a filesystem or a block layer — it’s a thin, generic layer that lets a provider driver (the code that actually knows how to talk to the EEPROM, eFuse, or OTP memory over I2C, SPI, or a memory-mapped register) expose raw storage, while any number of consumer drivers pull named regions out of it without knowing the underlying transport at all.

The unit consumers actually ask for is called a cell: a named byte range inside the NVMEM device, such as “mac-address” at offset 0x10 for 6 bytes, or “board-id” at offset 0x00 for 2 bytes. Cells can be defined by the provider driver in C, or declared entirely in Device Tree, which is the more common approach on modern boards.

The Producer/Consumer Design of NVMEM

If you’ve worked through the Common Clock Framework earlier in this course, this pattern will feel familiar: a single provider driver registers a device that exposes capability, and any number of unrelated consumer drivers request pieces of that capability by name. NVMEM applies the exact same idea to storage instead of clocks.

  • A provider driver includes <linux/nvmem-provider.h> and registers itself with the framework, describing how to read (and optionally write) raw bytes.
  • A consumer driver includes <linux/nvmem-consumer.h> and looks up a named cell, with zero knowledge of whether that cell lives on an I2C EEPROM, a SPI flash region, or an SoC eFuse bank.

This separation is what makes NVMEM valuable in practice: swap the physical storage chip on a board redesign, update the Device Tree, and every consumer driver keeps working unmodified.

Core NVMEM Data Structures

NVMEM deliberately keeps its data structures minimal. Two structures matter most when you’re writing a provider driver, and understanding the difference between them clears up almost all of the initial confusion.

struct nvmem_config — What You Fill In

struct nvmem_config is the configuration you, the provider driver author, populate and hand to the framework at registration time. Think of it as a request form.

FieldPurpose
devParent device (usually your platform/I2C/SPI device)
name / idOptional device name; final name becomes <name><id>, or nvmem<id> if omitted
ownerOwning module, almost always THIS_MODULE
cells / ncellsOptional array of predefined cells and its length
read_onlyMarks the whole device non-writable
root_onlyRestricts userspace access to root
reg_read / reg_writeYour callbacks that actually move bytes to/from hardware
size / word_size / strideTotal size, minimum access granularity, and minimum access stride
privOpaque context pointer passed back into your read/write callbacks

struct nvmem_device — What the Framework Gives Back

struct nvmem_device is created and owned by the framework itself once you register your config. It’s effectively a runtime copy of the fields you supplied, plus bookkeeping the framework needs internally, such as a users reference count. As a provider author you rarely touch this structure directly — you interact with the handle the registration function returns.

NVMEM Provider/Consumer Flow

Step 1 — Hardware

EEPROM eFuse OTP memory

Step 2 — Provider Driver

reg_read callback reg_write callback nvmem_config → nvmem_device

Step 3 — Named Cells Exposed

mac-address board-id calibration-data

Step 4 — Consumers

Consumer driver A Consumer driver B Userspace sysfs

Building a Minimal NVMEM Provider Driver

Let’s put the structures to work with a small, original example — a fake NVMEM device backed by a static in-memory buffer, so you can test the registration path with no real hardware attached. This is deliberately simplified for this stage of the free Linux kernel development course; a follow-up lecture will build a complete provider against a real I2C EEPROM.

// ep_nvmem_fake.c — minimal NVMEM provider skeleton
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/nvmem-provider.h>

#define EP_NVMEM_SIZE 32

struct ep_nvmem_priv {
	u8 storage[EP_NVMEM_SIZE];
};

static int ep_nvmem_read(void *priv, unsigned int offset,
			  void *val, size_t bytes)
{
	struct ep_nvmem_priv *ep = priv;

	if (offset + bytes > EP_NVMEM_SIZE)
		return -EINVAL;

	memcpy(val, ep->storage + offset, bytes);
	return 0;
}

static int ep_nvmem_write(void *priv, unsigned int offset,
			   void *val, size_t bytes)
{
	struct ep_nvmem_priv *ep = priv;

	if (offset + bytes > EP_NVMEM_SIZE)
		return -EINVAL;

	memcpy(ep->storage + offset, val, bytes);
	return 0;
}

static int ep_nvmem_probe(struct platform_device *pdev)
{
	struct ep_nvmem_priv *ep;
	struct nvmem_config cfg = { 0 };
	struct nvmem_device *nvmem;

	ep = devm_kzalloc(&pdev->dev, sizeof(*ep), GFP_KERNEL);
	if (!ep)
		return -ENOMEM;

	/* Pretend byte 0-5 already holds a MAC address for this demo */
	memset(ep->storage, 0xAA, EP_NVMEM_SIZE);

	cfg.dev = &pdev->dev;
	cfg.name = "ep-fake-nvmem";
	cfg.id = -1;
	cfg.owner = THIS_MODULE;
	cfg.size = EP_NVMEM_SIZE;
	cfg.word_size = 1;
	cfg.stride = 1;
	cfg.reg_read = ep_nvmem_read;
	cfg.reg_write = ep_nvmem_write;
	cfg.priv = ep;

	nvmem = devm_nvmem_register(&pdev->dev, &cfg);
	if (IS_ERR(nvmem))
		return dev_err_probe(&pdev->dev, PTR_ERR(nvmem),
				      "failed to register NVMEM device\n");

	platform_set_drvdata(pdev, nvmem);
	dev_info(&pdev->dev, "ep-fake-nvmem registered, %d bytes\n",
		 EP_NVMEM_SIZE);
	return 0;
}

static struct platform_driver ep_nvmem_driver = {
	.probe = ep_nvmem_probe,
	.driver = {
		.name = "ep-fake-nvmem",
	},
};
module_platform_driver(ep_nvmem_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala demo NVMEM provider");

Build and load it against a manually registered platform device (or bind it via Device Tree in a real board):

$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
$ sudo insmod ep_nvmem_fake.ko

Expected dmesg output:

$ sudo dmesg | tail -n 3
[  812.441207] ep-fake-nvmem ep-fake-nvmem.0: ep-fake-nvmem registered, 32 bytes

You can confirm the device appeared through the generic NVMEM sysfs interface:

$ ls /sys/bus/nvmem/devices/
ep-fake-nvmem0

$ sudo hexdump -C /sys/bus/nvmem/devices/ep-fake-nvmem0/nvmem | head -1
00000000  aa aa aa aa aa aa aa aa  aa aa aa aa aa aa aa aa

Common Mistakes When Registering NVMEM Devices

  • Forgetting bounds checks in reg_read/reg_write — the framework trusts your callback to validate offset + bytes against your device size; skipping this is a straightforward out-of-bounds write.
  • Using nvmem_register() instead of devm_nvmem_register() without a matching unregister call in your remove path, leaking the device on module unload.
  • Setting word_size/stride to 0 — leave them at 1 unless your hardware genuinely enforces wider access granularity.
  • Assuming cells is required — it’s optional; most modern drivers let Device Tree define cells instead of hardcoding them in C.

Best Practices for NVMEM Provider Drivers

  • Prefer the devm_ managed registration variant so cleanup is automatic on probe failure or driver unbind.
  • Keep reg_read/reg_write callbacks fast and non-sleeping-friendly where the underlying bus allows it; if the bus can sleep (I2C/SPI), that’s fine — just don’t take locks that could deadlock against the NVMEM core.
  • Set read_only whenever the hardware genuinely can’t be written (many eFuse/OTP parts), rather than relying on consumers to behave.

Security and Access Considerations

Some NVMEM-backed data — serial numbers, security keys, calibration trim values — shouldn’t be readable by every userspace process. Use root_only where appropriate, and remember it gates the generic sysfs/userspace path, not in-kernel consumer access, so a compromised but privileged userspace process can still read root-only cells unless additional LSM policy is layered on top.

Summary and Key Takeaways

  • NVMEM replaces one-off EEPROM/eFuse drivers with a shared producer/consumer framework.
  • A cell is the named unit consumers actually work with, not raw offsets.
  • struct nvmem_config is what you fill in; struct nvmem_device is what the framework hands back.
  • reg_read/reg_write callbacks are the only hardware-specific code a provider driver really needs to write.

That’s the full picture of the data structures behind NVMEM. In the next lecture of this free Linux kernel development course, we’ll build a complete provider driver against Device-Tree-declared cells, and after that, look at the consumer-side APIs that let any driver in the kernel pull a named cell without knowing what chip it lives on.

Frequently Asked Questions

What replaced the old drivers/misc/ EEPROM drivers?

The NVMEM framework did. Chip-specific drivers still exist, but they register into NVMEM instead of exposing their own bespoke sysfs interface.

Is this free Linux kernel development course suitable for beginners?

Yes, as long as you’re comfortable with basic C and have followed the earlier platform driver lectures in this free Linux device drivers course — NVMEM builds directly on those concepts.

What exactly is a “cell” in NVMEM?

A named, offset-and-length-defined slice of the NVMEM device’s storage, such as a 6-byte MAC address at a fixed offset.

Do I have to define cells in C code?

No. Most modern boards declare cells entirely in Device Tree, which is the approach we’ll cover in the provider driver lecture.

Can an NVMEM device be write-only or read-only?

It can be marked read-only via nvmem_config.read_only. There’s no equivalent write-only flag; omit reg_read if reads genuinely shouldn’t be supported.

What headers does a provider driver need?

<linux/nvmem-provider.h> for provider drivers, and <linux/nvmem-consumer.h> for anything consuming a cell.

Does NVMEM work without Device Tree?

Yes — cells can be defined directly in nvmem_config.cells for board-file or ACPI-based systems, though DT is by far the most common path today.

Keep Going With This Free Linux Kernel Development Course

Next up: a complete NVMEM provider driver against real Device Tree cells, followed by the consumer-side APIs.

Next Lecture Browse Full Course Index

Leave a Reply

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