Regmap Update Bits and Multi Write
Free Linux Kernel Development Course — Regmap API, Lecture 4
free embedded systems course
free linux device drivers course
regmap_update_bits linux kernel
regmap_multi_reg_write
regmap_update_bits() and regmap_multi_reg_write(), two of the most used functions in the Regmap API for real device drivers. Both functions save I2C/SPI driver authors from writing repetitive read-modify-write logic by hand, and both are fully compatible with modern Linux kernel 6.x. This lecture continues our free embedded systems course series on the Regmap API and is part of the larger free linux device drivers course.What You Will Learn
- How
regmap_update_bits()performs an atomic read-modify-write on a single register - How to build a correct
maskandvalpair for bit-level updates - How
regmap_multi_reg_write()writes several registers in one call usingstruct reg_sequence - How to write an original driver that uses both functions on kernel 6.x
- Common mistakes, best practices, and performance notes for regmap-based drivers
Prerequisites
- Completed lddch9_1 (Regmap API basics) and lddch9_2 (Regmap access tables)
- Comfortable with
regmap_read()andregmap_write()from lddch9_3 - A Linux kernel 6.x build environment (kernel headers installed)
- Basic knowledge of I2C client drivers (see our I2C driver lecture series)
Quick Recap: regmap_read and regmap_write
Before moving ahead, remember that regmap_read() and regmap_write() operate on a single register at a time, and both consult the cache and the access tables (readable_reg/writeable_reg) you configured earlier in this series. regmap_update_bits() and regmap_multi_reg_write() build on top of this same foundation.
Understanding regmap_update_bits Function
Many hardware registers pack several unrelated settings into one byte. For example, a single control register might hold a power-enable bit, a mode bit, and a gain field, all in the same 8 bits. If you want to change only the enable bit, you must not disturb the other bits. Doing this manually means: read the register, modify the target bits in software, then write the register back — three separate steps that can race with another part of the driver.
regmap_update_bits() wraps this entire read-modify-write cycle into one call:
int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val);
Only the bit positions set to 1 in mask are touched. The corresponding bits of val supply the new values for those positions; bits of val outside the mask are ignored. Internally, regmap reads the current register value, clears the masked bits, ORs in the new bits from val, and writes the result back only if it actually changed — this avoids an unnecessary bus write when nothing changes.
Mask and Value Example
| Goal | mask (binary) | val (binary) |
|---|---|---|
| Set bit 0 and bit 2 to 1 | 0000 0101 | xxxx x1x1 |
| Clear bit 6 | 0100 0000 | x0xx xxxx |
| Set a 2-bit mode field (bits 3-4) to 0b10 | 0001 1000 | xxx1 0xxx |
Practical Example: Toggling Bits with regmap_update_bits
The following original driver, ep_regmap_ubits_demo, models a control register with an enable bit at position 0 and a 2-bit mode field at positions 1-2. A sysfs attribute lets you flip the enable bit without disturbing the mode field, using a single regmap_update_bits() call.
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#define EP_CTRL_REG 0x10
#define EP_CTRL_ENABLE BIT(0)
struct ep_ubits_data {
struct regmap *regmap;
};
static const struct regmap_config ep_ubits_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x20,
.cache_type = REGCACHE_RBTREE,
};
static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ep_ubits_data *data = dev_get_drvdata(dev);
bool enable;
int ret;
ret = kstrtobool(buf, &enable);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap, EP_CTRL_REG,
EP_CTRL_ENABLE,
enable ? EP_CTRL_ENABLE : 0);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_WO(enable);
static int ep_ubits_probe(struct i2c_client *client)
{
struct ep_ubits_data *data;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->regmap = devm_regmap_init_i2c(client, &ep_ubits_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_enable);
}
static void ep_ubits_remove(struct i2c_client *client)
{
device_remove_file(&client->dev, &dev_attr_enable);
}
static const struct of_device_id ep_ubits_of_match[] = {
{ .compatible = "ep,regmap-ubits-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_ubits_of_match);
static struct i2c_driver ep_ubits_driver = {
.driver = {
.name = "ep_regmap_ubits_demo",
.of_match_table = ep_ubits_of_match,
},
.probe = ep_ubits_probe,
.remove = ep_ubits_remove,
};
module_i2c_driver(ep_ubits_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala regmap_update_bits demo driver");
Notice that only the enable bit is masked, so writing echo 1 > /sys/bus/i2c/devices/.../enable never touches the mode field, even though both live in the same register.
Understanding regmap_multi_reg_write Function
Many real sensors and PMICs need a sequence of register writes during initialization — enable oscillator, wait a few microseconds, set gain, wait again, then enable the output. Writing these one by one with regmap_write() plus manual udelay() calls is repetitive and easy to get wrong. regmap_multi_reg_write() solves this with one call:
int regmap_multi_reg_write(struct regmap *map,
const struct reg_sequence *regs,
int num_regs);
Each entry in the sequence carries three pieces of information: which register to write, what value to write, and how many microseconds to wait after that particular write before moving to the next one. This makes device bring-up sequences self-documenting and removes scattered delay calls from your probe function.
reg_sequence Fields
| Field | Meaning |
|---|---|
| reg | Register address to write |
| def | Value to write into that register |
| delay_us | Microseconds to wait after this write before the next one (0 if no delay needed) |
Practical Example: Sensor Init Sequence with regmap_multi_reg_write
This original driver, ep_sensor_init_demo, brings a fictional sensor out of reset using a four-step register sequence, with a short settling delay after enabling the oscillator.
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#define EP_RESET_REG 0x00
#define EP_OSC_REG 0x01
#define EP_GAIN_REG 0x02
#define EP_ENABLE_REG 0x03
static const struct reg_sequence ep_sensor_init_seq[] = {
{ EP_RESET_REG, 0x01, 0 },
{ EP_OSC_REG, 0x01, 200 },
{ EP_GAIN_REG, 0x04, 0 },
{ EP_ENABLE_REG, 0x01, 0 },
};
static const struct regmap_config ep_sensor_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x10,
.cache_type = REGCACHE_RBTREE,
};
static int ep_sensor_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
regmap = devm_regmap_init_i2c(client, &ep_sensor_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
ret = regmap_multi_reg_write(regmap, ep_sensor_init_seq,
ARRAY_SIZE(ep_sensor_init_seq));
if (ret) {
dev_err(&client->dev, "sensor init sequence failed: %d\n", ret);
return ret;
}
dev_info(&client->dev, "ep_sensor_init_demo bring-up complete\n");
return 0;
}
static const struct of_device_id ep_sensor_of_match[] = {
{ .compatible = "ep,sensor-init-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_sensor_of_match);
static struct i2c_driver ep_sensor_driver = {
.driver = {
.name = "ep_sensor_init_demo",
.of_match_table = ep_sensor_of_match,
},
.probe = ep_sensor_probe,
};
module_i2c_driver(ep_sensor_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala regmap_multi_reg_write demo driver");
Building and Testing the Driver
Use a standard out-of-tree kernel module Makefile:
obj-m += ep_regmap_ubits_demo.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
Build and load the module, then check kernel logs:
make
sudo insmod ep_regmap_ubits_demo.ko
dmesg | tail -n 10
Expected output in dmesg after loading (exact device path depends on your bus number):
ep_regmap_ubits_demo 1-0050: ep_regmap_ubits_demo probe complete
Test the sysfs attribute:
echo 1 | sudo tee /sys/bus/i2c/devices/1-0050/enable
For the init-sequence driver, loading it directly produces the bring-up message:
sudo insmod ep_sensor_init_demo.ko
dmesg | tail -n 5
# ep_sensor_init_demo 1-0051: ep_sensor_init_demo bring-up complete
regmap_write vs regmap_update_bits vs regmap_multi_reg_write
| Function | Use When | Registers Touched |
|---|---|---|
| regmap_write() | You want to overwrite an entire register | One, fully replaced |
| regmap_update_bits() | You want to change only specific bits, atomically | One, partially replaced |
| regmap_multi_reg_write() | You need an ordered sequence of writes, optionally with delays | Many, in order |
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Setting mask bits you did not intend to change | Unrelated hardware settings get corrupted | Double-check the mask against the register’s bitfield layout |
| Passing val bits outside the mask expecting them to apply | Confusing, silent no-op on those bits | Only bits inside mask have any effect; keep val clean |
| Ignoring delay_us requirements from the datasheet | Device may not respond correctly after reset | Fill delay_us for every step the datasheet requires |
| Not checking the return value of regmap_multi_reg_write() | Driver continues with a half-initialized device | Always check ret and bail out on failure |
Best Practices
- Define bit masks with
BIT()or named macros instead of magic numbers - Group related init sequences into a single
reg_sequencearray for readability - Prefer
regmap_update_bits()over manual read-modify-write for any shared register - Keep
delay_usvalues close to what the datasheet specifies, not arbitrary round numbers
Performance Considerations
regmap_update_bits() only issues a write if the computed value actually differs from the cached value, which saves a bus transaction when nothing changes. regmap_multi_reg_write() batches several writes logically, but each write and delay still happens in sequence, so long delay chains during probe can slow down device bring-up — keep delays as short as the datasheet allows.
Security Considerations
Never expose raw regmap_update_bits() or regmap_multi_reg_write() calls directly to userspace input without validating the register and bit ranges first; combine them with your writeable_reg access table from lddch9_2 so userspace cannot target protected registers through a sysfs or ioctl interface.
Summary
regmap_update_bits()performs an atomic read-modify-write using a mask/val pairregmap_multi_reg_write()writes an ordered sequence of registers with per-step delays viastruct reg_sequence- Both functions reduce boilerplate and race conditions compared to manual register access
- Always validate return values and respect existing access tables
Conclusion
With regmap_update_bits() and regmap_multi_reg_write() you can now handle both single-bit tweaks and full device bring-up sequences cleanly on kernel 6.x, without writing repetitive read-modify-write code by hand. This keeps your driver readable and safe from race conditions, which is exactly the kind of practical skill this free linux kernel development course focuses on. In the next lecture of this free linux device drivers course we look at regmap_bulk_read(), regmap_bulk_write(), and regmap’s built-in caching system.
Frequently Asked Questions
What is the difference between regmap_write and regmap_update_bits?
regmap_write() replaces the entire register value, while regmap_update_bits() changes only the bits selected by the mask and leaves the rest untouched.
Does regmap_update_bits always write to the register?
No. It only writes back to hardware if the new value differs from the current one, saving an unnecessary bus transaction.
Can I use regmap_multi_reg_write without any delay between steps?
Yes, set delay_us to 0 for any step that does not require a wait.
Is regmap_multi_reg_write faster than several regmap_write calls?
The number of bus transactions is the same, but the code is cleaner and delays are handled consistently in one place instead of scattered udelay calls.
Does regmap_update_bits work with SPI devices?
Yes, it works with any bus regmap supports, including I2C and SPI, since regmap abstracts the underlying bus.
What happens if I pass an invalid register to regmap_multi_reg_write?
If the register fails your writeable_reg or max_register checks, the call returns an error and stops the sequence at that point.
Should I check the return value of regmap_update_bits?
Yes, always check it. A non-zero return means the underlying bus write failed and your driver should handle that error.
Continue the Free Linux Kernel Development Course
