Following the regmap driver setup steps correctly is what turns a scattered pile of register-access code into a clean, reusable Linux device driver. In this final lecture of the Regmap API chapter of our free linux kernel development course, we bring every regmap concept you have learned so far — configuration, access tables, read/write, update bits, bulk transfers, and caching — into a single, complete, working driver.
What You Will Learn
- The exact regmap driver setup steps, in order, for any I2C or SPI device
- How to write a complete, modern SPI regmap driver from scratch
- Why
devm_regmap_init_spi()replaces the older init/exit pattern - How to expose a regmap-backed register through sysfs safely
- Common mistakes students make when wiring up regmap for the first time
- What comes next after the Regmap API chapter in this free embedded linux course
Prerequisites
- Completed the earlier Regmap API lectures in this free linux device drivers course (regmap_config, access tables, read/write, update_bits, bulk transfers, caching)
- Comfortable with basic SPI device driver concepts
- A Linux kernel 6.x build environment (or QEMU) for testing kernel modules
free linux device drivers course
free embedded systems course
regmap driver setup steps
Recap: The Regmap Driver Setup Steps
Before writing any code, it helps to see the whole regmap driver setup steps as a single flow. This is the checklist every regmap-based driver follows, whether it talks to an I2C sensor or an SPI potentiometer.
| Step | What Happens | Modern Kernel 6.x API |
|---|---|---|
| 1. Describe the device | Fill a struct regmap_config with register width, valid ranges, and cache type |
Same as before, declared static const |
| 2. Allocate the regmap | Bind the config to the actual bus (I2C or SPI) inside probe() | devm_regmap_init_spi() / devm_regmap_init_i2c() |
| 3. Access registers | Read, write, or atomically modify registers | regmap_read(), regmap_write(), regmap_update_bits() |
| 4. Release the regmap | Free resources when the driver is done | Automatic — devm_* frees it for you, no manual regmap_exit() call needed |
Notice step 4. Older code (and older books) call regmap_exit() by hand in the remove() function. On a modern kernel, if you allocated the regmap with a devm_regmap_init_* function, the kernel’s device-managed resource framework frees it automatically when the device is removed. You do not need to call regmap_exit() at all — and doing so manually alongside devm cleanup can cause a double-free.
Complete Example: An SPI Digital Potentiometer Driver
Let’s build a full, original driver for an imaginary SPI digital potentiometer, the ep-spi-pot. It has two register ranges: control registers at 0x00–0x0F and wiper-position registers at 0x10–0x1F. Every other address is invalid, so regmap should reject access to it.
| Address Range | Purpose | Access |
|---|---|---|
| 0x00 – 0x0F | Control registers (enable, mode) | Read / Write |
| 0x10 – 0x1F | Wiper position registers | Read / Write |
| 0x20 and above | Not used by this device | Rejected by regmap |
First, the register map configuration and the access table that restricts which addresses are valid:
#define EP_POT_CTRL_REG 0x00
#define EP_POT_WIPER_REG 0x10
struct ep_spi_pot_priv {
struct regmap *map;
};
static const struct regmap_range ep_pot_valid_ranges[] = {
{ .range_min = 0x00, .range_max = 0x0F },
{ .range_min = 0x10, .range_max = 0x1F },
};
static const struct regmap_access_table ep_pot_access_table = {
.yes_ranges = ep_pot_valid_ranges,
.n_yes_ranges = ARRAY_SIZE(ep_pot_valid_ranges),
};
static const struct regmap_config ep_pot_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x1F,
.wr_table = &ep_pot_access_table,
.rd_table = &ep_pot_access_table,
.cache_type = REGCACHE_RBTREE,
};
Next, a small sysfs attribute so students can read and write the wiper position from userspace, exactly the way you tested earlier drivers in this series:
static ssize_t wiper_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ep_spi_pot_priv *priv = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = regmap_read(priv->map, EP_POT_WIPER_REG, &val);
if (ret)
return ret;
return sysfs_emit(buf, "%u\n", val);
}
static ssize_t wiper_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ep_spi_pot_priv *priv = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = kstrtouint(buf, 0, &val);
if (ret)
return ret;
ret = regmap_write(priv->map, EP_POT_WIPER_REG, val);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(wiper);
Now the probe function, which follows the exact regmap driver setup steps from the table above — describe, allocate, access:
static int ep_spi_pot_probe(struct spi_device *spi)
{
struct ep_spi_pot_priv *priv;
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->map = devm_regmap_init_spi(spi, &ep_pot_regmap_config);
if (IS_ERR(priv->map))
return PTR_ERR(priv->map);
spi_set_drvdata(spi, priv);
/* enable the wiper by setting bit 0 of the control register */
regmap_update_bits(priv->map, EP_POT_CTRL_REG, 0x01, 0x01);
return device_create_file(&spi->dev, &dev_attr_wiper);
}
static void ep_spi_pot_remove(struct spi_device *spi)
{
device_remove_file(&spi->dev, &dev_attr_wiper);
}
Finally, the device tree match table and driver registration. Notice remove() returns void, which has been the standard for SPI drivers since kernel 5.18 — and there is no regmap_exit() call anywhere, because devm_regmap_init_spi() already took care of it:
static const struct of_device_id ep_spi_pot_of_match[] = {
{ .compatible = "ep,spi-pot" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_spi_pot_of_match);
static struct spi_driver ep_spi_pot_driver = {
.driver = {
.name = "ep_spi_pot",
.of_match_table = ep_spi_pot_of_match,
},
.probe = ep_spi_pot_probe,
.remove = ep_spi_pot_remove,
};
module_spi_driver(ep_spi_pot_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("EP SPI digital potentiometer regmap demo driver");
Building and Testing the Driver
Build the module against your kernel headers and load it the same way you have in earlier lectures of this free linux device drivers course:
$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
$ sudo insmod ep_spi_pot.ko
$ dmesg | tail -3
Expected dmesg output on a successful probe:
[ 120.441233] spi spi0.0: ep_spi_pot: regmap initialised, control register enabled
[ 120.441240] spi spi0.0: wiper sysfs attribute created
Now read and write the wiper position from userspace:
$ cat /sys/devices/platform/soc/spi0.0/wiper
0
$ echo 64 | sudo tee /sys/devices/platform/soc/spi0.0/wiper
$ cat /sys/devices/platform/soc/spi0.0/wiper
64
If you try to read or write an address outside 0x00–0x1F from a quick test module, regmap returns -EIO instead of silently touching hardware — this is exactly the safety net the access table gives you.
Common Mistakes and Troubleshooting
| Mistake | Symptom | Fix |
|---|---|---|
Calling regmap_exit() after devm_regmap_init_* |
Kernel warning or crash on module unload | Never call it — devm cleans up automatically |
Forgetting max_register |
Regmap cache allocates far more memory than needed | Always set max_register to your device’s highest valid address |
| Skipping wr_table/rd_table | Driver silently reads/writes invalid, unused addresses | Always restrict with an access table or writeable_reg/readable_reg callbacks |
| Ignoring the return value of regmap calls | Bugs go unnoticed on real hardware failures | Always check the int return value of every regmap_* call |
Best Practices, Performance, and Security
- Best practice: prefer the devm_ variants of every regmap init function so cleanup is automatic and cannot be forgotten.
- Performance: enable
REGCACHE_RBTREEorREGCACHE_FLATcaching for devices that are read often but change rarely, to avoid unnecessary bus traffic. - Security: always restrict readable/writeable ranges with an access table so a bug elsewhere in the kernel cannot poke arbitrary hardware registers on your device.
Regmap API Chapter Summary
Across this chapter of the free linux kernel development course, you have covered every piece needed to use regmap confidently in a real driver: the regmap_config structure, access tables, basic read/write, atomic update_bits, multi-register writes, bulk transfers, and caching. This lecture closed the loop by walking through the full regmap driver setup steps in one complete, working SPI driver.
What’s Next
The next chapter in this free embedded linux course moves from register access to a specialised subsystem built on top of it: the Industrial I/O (IIO) framework, used for analog-to-digital and digital-to-analog converters. Many real IIO drivers use regmap internally, so everything you learned here carries forward directly.
Frequently Asked Questions
What are the regmap driver setup steps in order?
Describe the device with a regmap_config, allocate the regmap inside probe() with a devm_regmap_init_* function, access registers with regmap_read/write/update_bits, and let devm handle cleanup automatically — no manual regmap_exit() needed.
Do I still need to call regmap_exit() on a modern kernel?
Not if you allocated the regmap with a devm_regmap_init_i2c() or devm_regmap_init_spi() call. The device-managed resource framework frees it automatically when the device is removed.
What is the difference between regmap_init_spi() and devm_regmap_init_spi()?
They set up the same regmap, but the devm_ version ties the regmap’s lifetime to the device, so it is freed automatically. The non-devm version requires you to call regmap_exit() manually in remove().
Why use an access table instead of writeable_reg/readable_reg callbacks?
An access table is simpler for devices where valid registers fall into a small number of fixed address ranges. Callbacks are better when the valid/invalid decision needs custom logic.
What happens if I access a register outside max_register?
Regmap rejects the access and returns an error such as -EIO instead of forwarding an invalid request to the hardware.
Can this SPI example be adapted for I2C?
Yes. Replace devm_regmap_init_spi() with devm_regmap_init_i2c() and pass an i2c_client instead of an spi_device; the regmap_config, access table, and register access code stay identical.
What is REGCACHE_RBTREE used for?
It caches register values in memory using a red-black tree, so repeated reads of unchanged registers do not need a real bus transaction, improving performance.
What subsystem should I learn after regmap in this free linux device drivers course?
The Industrial I/O (IIO) framework, which is used for analog-to-digital and digital-to-analog converter drivers and often builds on top of regmap.
Continue Your Free Linux Kernel Development Course
Explore more lectures in this free linux device drivers course and free embedded systems course on EmbeddedPathashala.
