Regmap Bulk Read Write and Cache
Free Linux Kernel Development Course — Regmap API, Lecture 5
free embedded systems course
free linux device drivers course
regmap bulk read write linux kernel
regmap cache linux kernel
regmap_bulk_read(), regmap_bulk_write(), and how the Regmap API’s built-in caching system works on Linux kernel 6.x. These two topics close out the Regmap API chapter of this free embedded systems course and free linux device drivers course, and prepare you for real sensor and PMIC drivers.What You Will Learn
- When to use
regmap_bulk_read()andregmap_bulk_write()instead of single-register calls - How regmap’s cache_type field controls caching behavior
- How reg_defaults pre-populates the cache with power-on-reset values
- How to build an original driver using bulk transfers and a cache
- Common mistakes, best practices, and performance notes
Prerequisites
- Completed lddch9_1 through lddch9_4 of this Regmap API series
- Comfortable with regmap_config, regmap_read, regmap_write, and regmap_update_bits
- A Linux kernel 6.x build environment with kernel headers installed
Understanding regmap_bulk_read and regmap_bulk_write
Some devices store related data across several consecutive registers — a three-axis accelerometer, for example, often places X, Y, and Z readings in six back-to-back registers. Reading them one register at a time with regmap_read() works, but issues one bus transaction per register. regmap_bulk_read() and regmap_bulk_write() transfer a whole block in a single, more efficient operation:
int regmap_bulk_read(struct regmap *map, unsigned int reg,
void *val, size_t val_count);
int regmap_bulk_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_count);
reg is the starting register address, val is a buffer to fill (or read from), and val_count is how many register-sized values to transfer. Use these functions whenever you are dealing with a contiguous block of registers rather than a single value.
Practical Example: Bulk Reading Sensor Data
This original driver, ep_burst_sensor_demo, reads six consecutive 8-bit registers (X-low, X-high, Y-low, Y-high, Z-low, Z-high) from a fictional accelerometer in one regmap_bulk_read() call, exposed through a sysfs attribute.
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#define EP_AXIS_DATA_BASE 0x20
#define EP_AXIS_DATA_LEN 6
struct ep_burst_data {
struct regmap *regmap;
};
static const struct regmap_config ep_burst_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x30,
.cache_type = REGCACHE_NONE,
};
static ssize_t axis_data_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ep_burst_data *data = dev_get_drvdata(dev);
u8 axis[EP_AXIS_DATA_LEN];
int ret;
ret = regmap_bulk_read(data->regmap, EP_AXIS_DATA_BASE,
axis, EP_AXIS_DATA_LEN);
if (ret)
return ret;
return sysfs_emit(buf, "x=%d y=%d z=%d\n",
(axis[1] << 8) | axis[0],
(axis[3] << 8) | axis[2],
(axis[5] << 8) | axis[4]);
}
static DEVICE_ATTR_RO(axis_data);
static int ep_burst_probe(struct i2c_client *client)
{
struct ep_burst_data *data;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->regmap = devm_regmap_init_i2c(client, &ep_burst_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
i2c_set_clientdata(client, data);
return device_create_file(&client->dev, &dev_attr_axis_data);
}
static void ep_burst_remove(struct i2c_client *client)
{
device_remove_file(&client->dev, &dev_attr_axis_data);
}
static const struct of_device_id ep_burst_of_match[] = {
{ .compatible = "ep,burst-sensor-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_burst_of_match);
static struct i2c_driver ep_burst_driver = {
.driver = {
.name = "ep_burst_sensor_demo",
.of_match_table = ep_burst_of_match,
},
.probe = ep_burst_probe,
.remove = ep_burst_remove,
};
module_i2c_driver(ep_burst_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala regmap_bulk_read demo driver");
Note that cache_type is set to REGCACHE_NONE here, since axis data changes constantly and should never be served from a stale cache.
Understanding Regmap Cache Support
Regmap can optionally remember the last known value of every register in software, avoiding a real bus transaction on every read. Whether caching is active, and how it is stored, depends on the cache_type field of regmap_config.
Regmap Cache Types
| cache_type | Description | Best For |
|---|---|---|
| REGCACHE_NONE | No caching, every access hits the bus (default) | Fast-changing data such as sensor readings |
| REGCACHE_RBTREE | Red-black tree storage, good for sparse register maps | Devices with scattered, non-contiguous registers |
| REGCACHE_COMPRESSED | Compressed storage for register maps with many repeated default values | Devices with large maps that mostly hold defaults |
| REGCACHE_FLAT | Plain flat array, fastest lookup | Small, dense, fully populated register maps |
By default, cache_type is REGCACHE_NONE and caching is disabled. When you enable a cache type, regmap can also be given a starting set of known values through reg_defaults, so the very first read after probe can be served instantly from cache instead of the bus.
reg_defaults and Cache Population
Many chips have a documented power-on-reset value for each register. You can hand these values to regmap once, and any read before the first real write will return the correct default from cache instead of triggering a bus transaction.
reg_default Fields
| Field | Meaning |
|---|---|
| reg | Register address |
| def | Its documented power-on-reset value |
If a register is not listed in reg_defaults, regmap simply creates its cache entry the first time that register is read or written, so you only need to list values you already know from the datasheet. If cache_type is REGCACHE_NONE, reg_defaults is ignored entirely, since there is no cache to populate.
Practical Example: Driver Using an RBTREE Cache
This original driver, ep_cache_demo, declares three control registers with known reset defaults and enables an RBTREE cache so repeated reads of unchanged settings avoid the bus entirely.
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#define EP_CACHE_CTRL_REG 0x00
#define EP_CACHE_MODE_REG 0x01
#define EP_CACHE_GAIN_REG 0x02
static const struct reg_default ep_cache_reg_defaults[] = {
{ EP_CACHE_CTRL_REG, 0x00 },
{ EP_CACHE_MODE_REG, 0x01 },
{ EP_CACHE_GAIN_REG, 0x04 },
};
static const struct regmap_config ep_cache_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = EP_CACHE_GAIN_REG,
.reg_defaults = ep_cache_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ep_cache_reg_defaults),
.cache_type = REGCACHE_RBTREE,
};
static int ep_cache_probe(struct i2c_client *client)
{
struct regmap *regmap;
unsigned int val;
int ret;
regmap = devm_regmap_init_i2c(client, &ep_cache_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* Served from cache, no real bus transaction yet */
ret = regmap_read(regmap, EP_CACHE_MODE_REG, &val);
if (ret)
return ret;
dev_info(&client->dev, "mode register default = 0x%02x\n", val);
return 0;
}
static const struct of_device_id ep_cache_of_match[] = {
{ .compatible = "ep,cache-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_cache_of_match);
static struct i2c_driver ep_cache_driver = {
.driver = {
.name = "ep_cache_demo",
.of_match_table = ep_cache_of_match,
},
.probe = ep_cache_probe,
};
module_i2c_driver(ep_cache_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala regmap cache demo driver");
Building and Testing the Driver
obj-m += ep_cache_demo.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(PWD) modules
make
sudo insmod ep_cache_demo.ko
dmesg | tail -n 5
Expected output:
ep_cache_demo 1-0052: mode register default = 0x01
For the bulk-read driver, load it and read the sysfs attribute:
sudo insmod ep_burst_sensor_demo.ko
cat /sys/bus/i2c/devices/1-0053/axis_data
# x=12 y=-4 z=980
Cache Type Comparison
| Cache Type | Memory Use | Lookup Speed | Typical Register Map |
|---|---|---|---|
| REGCACHE_NONE | None | N/A (always hits bus) | Any |
| REGCACHE_RBTREE | Low, grows with used registers | Good | Sparse |
| REGCACHE_FLAT | Higher, fixed size array | Fastest | Small and dense |
| REGCACHE_COMPRESSED | Low | Moderate | Large, mostly-default maps |
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Using regmap_bulk_read on non-contiguous registers | Wrong data returned, since the function assumes consecutive addresses | Confirm the registers are truly back-to-back in the datasheet |
| Enabling a cache on fast-changing sensor data registers | Stale readings returned to userspace | Keep cache_type as REGCACHE_NONE for volatile data, or mark them volatile |
| Forgetting max_register when using a cache | Cache cannot size itself correctly, reads may fail | Always set max_register in regmap_config |
| Assuming reg_defaults populates every register | Registers you never listed simply have no default, not zero | Only rely on reg_defaults for registers you explicitly listed |
Best Practices
- Use regmap_bulk_read/write only for genuinely contiguous register blocks
- Choose REGCACHE_RBTREE for sparse maps and REGCACHE_FLAT for small dense maps
- Mark frequently changing registers as volatile so the cache never serves stale values for them
- Populate reg_defaults directly from the datasheet’s reset value table
Performance Considerations
Bulk transfers reduce the number of bus transactions from one-per-register to one-per-block, which matters a great deal on slower buses like I2C. A properly configured cache avoids bus traffic entirely for registers that have not changed since their default or last write, but an incorrectly cached volatile register can silently return outdated data, so caching should always be paired with correct volatile_reg marking from lddch9_2.
Security Considerations
Do not expose bulk read/write ranges to userspace without validating the requested length against max_register; an unchecked val_count can lead to out-of-bounds access on the buffer or unintended reads of adjacent registers.
Summary
- regmap_bulk_read/write transfer contiguous register blocks in a single operation
- cache_type controls whether and how regmap caches register values (NONE, RBTREE, FLAT, COMPRESSED)
- reg_defaults pre-populates the cache with known power-on-reset values
- Caching must be combined with correct volatile_reg marking to avoid stale data
Conclusion
With regmap_bulk_read(), regmap_bulk_write(), and regmap’s caching system, you now have the complete toolkit this free linux kernel development course set out to teach for the Regmap API. Together with regmap_read, regmap_write, regmap_update_bits, and regmap_multi_reg_write from earlier lectures, you can write clean, efficient I2C and SPI drivers on kernel 6.x. This closes the Regmap API chapter of our free linux device drivers course; the next chapter in this free embedded systems course moves on to a new subsystem.
Frequently Asked Questions
When should I use regmap_bulk_read instead of regmap_read in a loop?
Use regmap_bulk_read whenever the registers are contiguous, since it issues one bus transaction instead of one per register.
What is the default cache_type if I don’t set one?
The default is REGCACHE_NONE, meaning caching is disabled and every access goes to the bus.
Does reg_defaults work if cache_type is REGCACHE_NONE?
No, reg_defaults is ignored entirely when caching is disabled since there is no cache to populate.
Which cache type should I pick for a sparse register map?
REGCACHE_RBTREE is generally the best choice for sparse, non-contiguous register maps.
Can a cached register still be read from hardware directly?
Yes, if you mark it as volatile through your volatile_reg callback, regmap will always read it from hardware instead of the cache.
Is regmap_bulk_write atomic across all registers?
It performs the transfer as a single bus operation where the underlying bus supports it, but you should still check the return value for partial failures.
Do I need to list every register in reg_defaults?
No, only list registers whose reset value you know; others get their cache entry created on first access.
Continue the Free Linux Kernel Development Course
