Regmap API Linux Kernel Basics- Free Linux Device Drivers Course

PREV_LEC  |  NEXT_LEC

Regmap API Linux Kernel Basics

Free Linux Device Drivers Course · Free Linux Kernel Development Course · Chapter 9, Part 1

Kernel Version: 6.x
Level: Beginner – Intermediate
Read Time: 14 min
regmap api linux kernel
free embedded systems course
free linux development course
free linux device drivers course
free linux kernel development course
regmap_config structure

If you have written both an I2C driver and an SPI driver for the same kind of sensor, you already know the pain: two different bus APIs, two different read/write helper functions, but the exact same job — reading and writing registers. The regmap API in Linux kernel programming was created to solve exactly this problem. It gives every device driver one single, bus-independent way to read, write, and cache device registers, whether the underlying hardware is I2C, SPI, SPMI, or plain memory-mapped I/O. In this free Linux kernel development course lecture, we break down the regmap framework in simple language, walk through the real regmap_config structure used on kernel 6.x, and build an original example driver so you can see it working end to end.

What You Will Learn

  • Why the Linux kernel introduced the regmap subsystem in the first place
  • How I2C and SPI drivers used to duplicate register access code before regmap
  • The role of struct regmap_config and struct regmap
  • How reg_bits and val_bits describe your device’s register map
  • How the writeable_reg, readable_reg, and volatile_reg callbacks protect and speed up register access
  • How to write, build, and test an original regmap-based demo driver on a modern kernel

Prerequisites

  • Basic C programming knowledge
  • A working Linux kernel module build setup (kernel headers installed, make, gcc)
  • Familiarity with character/platform driver basics (see our earlier chapters in this free embedded Linux course)
  • A general idea of what I2C and SPI buses do — covered in detail in our earlier I2C and SPI chapters of this free linux device drivers course

Why Device Drivers Needed the Regmap API

Quick recap first, since we already covered I2C and SPI protocols in depth in earlier chapters of this free linux kernel development course. I2C is a two-wire bus (SDA/SCL) where the master addresses a slave device and then reads or writes one or more register bytes. SPI is a four-wire bus (MOSI/MISO/SCK/CS) where the master shifts data in and out on every clock edge, again to access device registers. Both buses are ultimately used for the same purpose in most sensor and peripheral drivers: reading a register, writing a register, or modifying a few bits inside a register.

Before regmap existed, driver authors wrote their own register-access helper functions for each bus. An I2C-based temperature sensor driver had its own read_reg()/write_reg() pair built on i2c_smbus_read_byte_data(). An SPI-based sensor driver had its own pair built on spi_write_then_read(). If the same sensor chip shipped in both I2C and SPI variants, the driver often duplicated almost identical logic twice, once per bus. On top of that, every driver had to build its own register cache by hand if it wanted to avoid hitting the slow bus repeatedly for values that rarely changed.

Before Regmap: Duplicated Bus-Specific Code
I2C Driver
own read/write helpers
I2C Subsystem I2C Device
SPI Driver
own read/write helpers
SPI Subsystem SPI Device

What Is the Regmap API in the Linux Kernel

The regmap API in the Linux kernel sits between your driver and the bus subsystem. Your driver describes the register map once — how wide the register addresses are, how wide the values are, which registers are writeable, which are readable, and which values should never be cached — and regmap handles the actual bus transaction for you. It picks the right low-level function for I2C, SPI, or memory-mapped access automatically, based on which devm_regmap_init_*() call you used.

After Regmap: One Common Access Layer
I2C Driver Regmap Subsystem
regmap_config + regmap
I2C Subsystem ↔ I2C Device
SPI Driver SPI Subsystem ↔ SPI Device

Both drivers now call the same regmap functions — regmap_read(), regmap_write(), and regmap_update_bits() — no matter which bus is underneath. The regmap subsystem is defined in include/linux/regmap.h and its two most important data structures are:

  • struct regmap_config — the configuration you fill in, describing your device’s register map
  • struct regmap — the live regmap instance returned to your driver after initialization

struct regmap_config Explained

Your driver never fills every field of regmap_config. Only a handful of fields matter for most simple drivers. Let’s go through the ones every beginner should understand first.

reg_bits and val_bits

reg_bits tells regmap how many bits wide your register addresses are. val_bits tells it how many bits wide the values stored in those registers are. Both are mandatory. A typical 8-bit sensor with 8-bit register addresses and 8-bit values sets both to 8:

static const struct regmap_config ep_sensor_regmap_config = {
    .reg_bits = 8,
    .val_bits = 8,
    .max_register = 0x7F,
};

Some devices use 16-bit register addresses with 8-bit values, or other combinations — regmap does not assume anything, you must state it explicitly.

writeable_reg Callback

writeable_reg is an optional callback. Whenever your driver calls regmap_write(), regmap first calls this function to check whether the given register is actually allowed to be written. This stops accidental writes to read-only or reserved registers before they ever reach the bus:

static bool ep_sensor_writeable_reg(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case 0x10 ... 0x13:   /* configuration registers */
    case 0x20:            /* control register */
        return true;
    default:
        return false;
    }
}

readable_reg Callback

readable_reg works the same way, but for regmap_read() calls. Reserved registers, or registers that don’t physically exist on a particular chip variant, can be excluded here:

static bool ep_sensor_readable_reg(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case 0x00 ... 0x02:   /* status + id registers */
    case 0x10 ... 0x13:   /* configuration registers */
    case 0x20:            /* control register */
        return true;
    default:
        return false;
    }
}

volatile_reg Callback and the Regmap Cache

This is the field that really shows the power of regmap. Some registers hold values that change on their own inside the hardware — a live status flag, a data-ready bit, a measurement result. These must always be read fresh from the bus. Other registers, like configuration settings your driver itself wrote, rarely change and can safely be served from an in-memory cache instead of triggering a bus transaction every time.

volatile_reg tells regmap which is which. Return true for a register that must always go straight to the bus. Return false for a register regmap is allowed to cache:

static bool ep_sensor_volatile_reg(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case 0x00:   /* status register - always changing */
    case 0x02:   /* measurement data register */
        return true;
    default:
        return false;   /* config/control regs are cacheable */
    }
}

With a cache enabled, repeated reads of a configuration register never touch the slow I2C or SPI bus again after the first read — regmap simply returns the cached value. This alone can noticeably speed up drivers that check configuration state often.

Original Demo Driver: ep_regmap_demo

Let’s put reg_bits, val_bits, writeable_reg, readable_reg, and volatile_reg together in one small, original I2C driver skeleton targeting kernel 6.x. This is not copied from any book — it is written specifically for this free linux device drivers course to demonstrate the concepts above on a fictional 8-bit sensor.

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>

#define EP_REG_STATUS   0x00
#define EP_REG_DATA     0x02
#define EP_REG_CONFIG   0x10
#define EP_REG_CONTROL  0x20

static bool ep_regmap_writeable(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case EP_REG_CONFIG:
    case EP_REG_CONTROL:
        return true;
    default:
        return false;
    }
}

static bool ep_regmap_readable(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case EP_REG_STATUS:
    case EP_REG_DATA:
    case EP_REG_CONFIG:
    case EP_REG_CONTROL:
        return true;
    default:
        return false;
    }
}

static bool ep_regmap_volatile(struct device *dev, unsigned int reg)
{
    switch (reg) {
    case EP_REG_STATUS:
    case EP_REG_DATA:
        return true;
    default:
        return false;
    }
}

static const struct regmap_config ep_regmap_config = {
    .name          = "ep_regmap_demo",
    .reg_bits      = 8,
    .val_bits      = 8,
    .max_register  = EP_REG_CONTROL,
    .writeable_reg = ep_regmap_writeable,
    .readable_reg  = ep_regmap_readable,
    .volatile_reg  = ep_regmap_volatile,
    .cache_type    = REGCACHE_RBTREE,
};

struct ep_regmap_data {
    struct regmap *regmap;
};

static int ep_regmap_probe(struct i2c_client *client)
{
    struct ep_regmap_data *data;
    unsigned int val;
    int ret;

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

    data->regmap = devm_regmap_init_i2c(client, &ep_regmap_config);
    if (IS_ERR(data->regmap))
        return PTR_ERR(data->regmap);

    i2c_set_clientdata(client, data);

    ret = regmap_read(data->regmap, EP_REG_STATUS, &val);
    if (ret)
        return ret;

    dev_info(&client->dev, "ep_regmap_demo probed, status = 0x%02x\n", val);
    return 0;
}

static void ep_regmap_remove(struct i2c_client *client)
{
    dev_info(&client->dev, "ep_regmap_demo removed\n");
}

static const struct of_device_id ep_regmap_of_match[] = {
    { .compatible = "ep,regmap-demo" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_regmap_of_match);

static const struct i2c_device_id ep_regmap_id[] = {
    { "ep_regmap_demo", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, ep_regmap_id);

static struct i2c_driver ep_regmap_driver = {
    .driver = {
        .name           = "ep_regmap_demo",
        .of_match_table = ep_regmap_of_match,
    },
    .probe    = ep_regmap_probe,
    .remove   = ep_regmap_remove,
    .id_table = ep_regmap_id,
};
module_i2c_driver(ep_regmap_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Original regmap API demo driver for free Linux kernel course");

Note: probe() here takes a single struct i2c_client * argument, which is the standard signature since kernel 6.3 (the older two-argument form with struct i2c_device_id *id was removed). remove() also returns void on modern kernels rather than int.

Build and Test Workflow

$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
$ sudo insmod ep_regmap_demo.ko
$ dmesg | tail -n 5

Expected output (assuming a matching device tree node or i2c bus device is bound):

[  102.441123] i2c i2c-1: ep_regmap_demo probed, status = 0x00
[  102.441130] ep_regmap_demo 1-0050: ep_regmap_demo probed, status = 0x00

Unloading the module confirms clean teardown:

$ sudo rmmod ep_regmap_demo
$ dmesg | tail -n 1
[  110.220441] ep_regmap_demo 1-0050: ep_regmap_demo removed

Common Mistakes When Using Regmap

Mistake Effect Fix
Marking a live status register non-volatile Driver reads stale cached data forever Return true for that register in volatile_reg
Forgetting max_register Out-of-range register access is not rejected Always set max_register to the highest valid address
Mismatched reg_bits/val_bits vs datasheet Corrupted reads/writes on the bus Double-check the datasheet register map width
Not checking devm_regmap_init_i2c() return value Driver crashes later using an invalid regmap pointer Always check IS_ERR() and propagate PTR_ERR()

Best Practices

  • Use devm_regmap_init_*() variants so cleanup happens automatically on driver removal
  • Keep writeable_reg/readable_reg switch statements close to your register map header for easy maintenance
  • Enable a cache type (REGCACHE_RBTREE is a good general default) whenever most registers are not volatile
  • Name your regmap with .name when a device has more than one regmap instance, for clearer debugfs output

Performance and Security Considerations

Caching non-volatile registers reduces bus traffic significantly on I2C and SPI, which is especially valuable on slower buses or battery-powered embedded systems. On the security side, correctly restricting writeable_reg and readable_reg prevents userspace-triggered debugfs register access (via regmap-debugfs) from touching registers your driver never intended to expose.

Summary and Key Takeaways

  • The regmap API in the Linux kernel unifies register access across I2C, SPI, and other buses
  • reg_bits and val_bits describe the width of your device’s register map
  • writeable_reg and readable_reg guard against invalid register access
  • volatile_reg decides which registers bypass the cache and which don’t
  • The same regmap calls (regmap_read, regmap_write) work no matter which bus is underneath

Conclusion

The regmap API removes an entire category of duplicated code from Linux device drivers and gives you a built-in register cache almost for free. Once you understand reg_bits, val_bits, and the three access-control callbacks covered here, you already know enough to convert a hand-rolled I2C or SPI register driver into a clean regmap-based one. In the next lecture of this free linux kernel development course, we will continue with the remaining regmap_config fields — including precious_reg, access tables, and the regmap caching system in full detail — and extend this ep_regmap_demo driver further.

Frequently Asked Questions

What is the regmap API used for in Linux kernel drivers?

It provides one common set of functions to read, write, and cache device registers, regardless of whether the device sits on an I2C bus, an SPI bus, or is memory-mapped.

Do I always need to fill every field of regmap_config?

No. Only reg_bits and val_bits are mandatory. Everything else, including the callbacks covered in this lecture, is optional and only needed when your device requires that level of control.

What happens if I don’t provide a volatile_reg callback?

Regmap treats every register as volatile by default, meaning no caching happens and every read/write goes straight to the bus.

Can the same regmap_config be used for both I2C and SPI variants of a chip?

Yes. The regmap_config only describes the register map, not the bus. You choose the bus by calling devm_regmap_init_i2c() or devm_regmap_init_spi() with the same config.

Is the regmap probe() function signature different on newer kernels?

For I2C drivers, yes — since kernel 6.3, probe() takes a single struct i2c_client * argument, and remove() returns void rather than int, as shown in the demo driver above.

What is the difference between writeable_reg and readable_reg?

writeable_reg is checked before every regmap_write() call, and readable_reg is checked before every regmap_read() call. A register can be readable but not writeable, or the reverse.

Why does this free linux device drivers course use original code instead of book examples?

So you learn the actual concept and can apply it to your own hardware, rather than memorizing one specific textbook driver. All examples here are written fresh for this course and verified against current kernel 6.x behaviour.

Which cache type should a beginner pick?

REGCACHE_RBTREE is a solid, memory-efficient default for most sparse register maps and is a safe starting point until you have a specific reason to choose another cache type.

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{“@type”: “Question”, “name”: “What is the regmap API used for in Linux kernel drivers?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “It provides one common set of functions to read, write, and cache device registers, regardless of whether the device sits on an I2C bus, an SPI bus, or is memory-mapped.”}},
{“@type”: “Question”, “name”: “Do I always need to fill every field of regmap_config?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “No. Only reg_bits and val_bits are mandatory. Everything else, including the callbacks covered in this lecture, is optional and only needed when your device requires that level of control.”}},
{“@type”: “Question”, “name”: “What happens if I don’t provide a volatile_reg callback?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Regmap treats every register as volatile by default, meaning no caching happens and every read/write goes straight to the bus.”}},
{“@type”: “Question”, “name”: “Can the same regmap_config be used for both I2C and SPI variants of a chip?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Yes. The regmap_config only describes the register map, not the bus. You choose the bus by calling devm_regmap_init_i2c() or devm_regmap_init_spi() with the same config.”}},
{“@type”: “Question”, “name”: “Is the regmap probe() function signature different on newer kernels?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “For I2C drivers, yes, since kernel 6.3 probe() takes a single struct i2c_client * argument, and remove() returns void rather than int.”}},
{“@type”: “Question”, “name”: “What is the difference between writeable_reg and readable_reg?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “writeable_reg is checked before every regmap_write() call, and readable_reg is checked before every regmap_read() call. A register can be readable but not writeable, or the reverse.”}},
{“@type”: “Question”, “name”: “Why does this free linux device drivers course use original code instead of book examples?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “So learners understand the actual concept and can apply it to their own hardware, rather than memorizing one specific textbook driver.”}},
{“@type”: “Question”, “name”: “Which cache type should a beginner pick?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “REGCACHE_RBTREE is a solid, memory-efficient default for most sparse register maps and is a safe starting point.”}}
]
}

Continue Your Free Linux Kernel Development Course

Master device drivers, kernel internals, and embedded Linux — 100% free at EmbeddedPathashala.

PREV_LEC  |  NEXT_LEC

Leave a Reply

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