Before the Regmap API was developed, there was redundant code for the device drivers dealing with SPI, I2C, or memory-mapped devices. Many of these drivers contained some very similar code for accessing hardware device registers.
The following figure shows how SPI, I2C, and memory-mapped related APIs were used standalone before Regmap was introduced:
The Regmap API was introduced in version v3.1 of the Linux kernel and proposes a solution that factors out and unifies these similar register access codes, saving code and making it much easier to share infrastructure. It is then just a matter of how to initialize and to configure a regmap structure, and process any read/write/modify operations fluently, whether it is SPI, I2C, or memory-mapped.
The following diagram depicts this API unification:
The previous figure shows how Regmap unified transactions between devices and their respective bus frameworks. In this chapter, we will cover as much as possible of the whole aspect of the APIs this framework offers, from initialization to complex use cases.
This chapter will walk through the Regmap framework via the following topics:
The Regmap framework, which is enabled via the CONFIG_REGMAP kernel configuration option, is made of a few data structures, among which the most important are struct regmap_config, which represents the Regmap configuration, and struct regmap, which is the Regmap instance itself. That said, all of the Regmap data structures are defined in include/linux/regmap.h. It then goes without saying that this header must be included in all Regmap-based drivers:
#include <linux/regmap.h>
Including the preceding header is sufficient to make the most out of the Regmap framework. With this header, a lot of data structures will be made available, among which, struct regmap_config is the most important, which we will describe in the next section.
struct regmap_config stores the configuration of the register map during the driver's lifetime. What you set there affects the memory read/write operations. This is the most important structure, which is defined as follows:
struct regmap_config {
const char *name;
int reg_bits;
int reg_stride;
int pad_bits;
int val_bits;
bool (*writeable_reg)(struct device *dev,
unsigned int reg);
bool (*readable_reg)(struct device *dev,
unsigned int reg);
bool (*volatile_reg)(struct device *dev,
unsigned int reg);
bool (*precious_reg)(struct device *dev,
unsigned int reg);
bool disable_locking;
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
int (*reg_read)(void *context, unsigned int reg,
unsigned int *val);
int (*reg_write)(void *context, unsigned int reg,
unsigned int val);
bool fast_io;
unsigned int max_register;
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
[...]
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
unsigned long read_flag_mask;
unsigned long write_flag_mask;
bool use_single_rw;
bool can_multi_write;
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges;
unsigned int num_ranges;
}
Don't be afraid of how big this structure is. All the elements are self-explanatory. However, for more clarity, let's expand on their meanings here:
static bool foo_writeable_register(struct device *dev,
unsigned int reg)
{
switch (reg) {
case 0x30 ... 0x38:
case 0x40 ... 0x45:
case 0x50 ... 0x57:
case 0x60 ... 0x6e:
case 0x70 ... 0x75:
case 0x80 ... 0x85:
case 0x90 ... 0x95:
case 0xa0 ... 0xa5:
case 0xb0 ... 0xb2:
return true;
default:
return false;
}
}
static bool foo_volatile_register(struct device *dev,
unsigned int reg)
{
switch (reg) {
case 0x24 ... 0x29:
case 0xb6 ... 0xb8:
return true;
default:
return false;
}
}
You should look at include/linux/regmap.h for more details on each element. The following is an example of the initialization of regmap_config:
static const struct regmap_config regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = LM3533_REG_MAX,
.readable_reg = lm3533_readable_register,
.volatile_reg = lm3533_volatile_register,
.precious_reg = lm3533_precious_register,
};
The preceding example shows how to build a basic register map configuration. Though only a few elements are set in the configuration data structure, enhanced configuration can be set up by learning about each element that we have described.
Now that we have learned about Regmap configuration, let's see how to use this configuration with the initialization API that corresponds to our needs.
As we said earlier, the Regmap API supports SPI, I2C, and memory-mapped register access. Their respective support can be enabled in the kernel thanks to the CONFIG_REGMAP_SPI, CONFIG_REGMAP_I2C, and CONFIG_REGMAP_MMIO kernel configuration options. It can go far beyond that and managing IRQs as well, but this is out of the scope of this book. Depending on the memory access method you need to support in the driver, you will have to call either devm_regmap_init_i2c(), devm_regmap_init_spi(), or devm_ regmap_init_mmio() in the probe function. To write generic drivers, Regmap is the best choice you can make.
The Regmap API is generic and homogenous, and initialization only changes between bus types. Other functions are the same. It is a good practice to always initialize the register map in the probe function, and you must always fill the regmap_config elements prior to initializing the register map using one of the following APIs:
struct regmap *devm_regmap_init_spi(struct spi_device *spi,
const struct regmap_config);
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config);
struct regmap * devm_regmap_init_mmio(
struct device *dev,
void __iomem *regs,
const struct regmap_config *config)
These are resource-managed APIs whose allocated resources are automatically freed when the device leaves the system or when the driver is unloaded. In the preceding prototypes, the return value will be a pointer to a valid struct regmap object or an ERR_PTR() error on failure. regs is a pointer to a memory-mapped IO region (returned by devm_ioremap_resource() or any ioremap* family function). dev is the device (struct device) to interact with in the case of a memory-mapped regmap, and spi and i2c are respectively SPI or I2C devices to interact with in the case of SPI- or I2C-based regmap.
Calling one of these functions is sufficient to start interacting with the underlying device. Whether the Regmap is an I2C, SPI, or a memory-mapped register map, if it has not been initialized with a resource managed API variant, it must be freed with the regmap_exit() function:
void regmap_exit(struct regmap *map)
This function simply releases a previously allocated register map.
Now that the register access method has been defined, we can jump to the device access functions, which allow reading from or writing into device registers.
Remap register access methods handle data parsing, formatting, and transmission. In most cases, device accesses are performed with regmap_read(), regmap_write(), and regmap_update_bits(), which are the three important APIs when it comes to writing/reading data into/from the device. Their respective prototypes are the following:
int regmap_read(struct regmap *map, unsigned int reg,
unsigned int *val);
int regmap_write(struct regmap *map, unsigned int reg,
unsigned int val);
int regmap_update_bits(struct regmap *map,
unsigned int reg, unsigned int mask,
unsigned int val);
regmap_write() writes data to the device. If set in regmap_config, max_register will be used to check whether the register address that needs to be accessed is greater or lower. If the register address passed is lower or equal to max_register, then the next operation will be performed; otherwise, the Regmap core will return an invalid I/O error (-EIO). Right after, the writeable_reg callback is called. The callback must return true before going to the next step. If it returns false, then -EIO is returned, and the write operation is stopped. If wr_table is set instead of writeable_reg, then the following happens:
If cache_type != REGCACHE_NONE, then caching is enabled. In this case, the cache entry is first updated with the new value, and then a write to the hardware is performed. Otherwise, no caching action is performed. If the reg_write callback is provided, it is used to perform the write operation. Otherwise, the generic Regmap's write function will be executed to write the data into the specified register address.
regmap_read() reads data from the device. It works exactly like regmap_write() with appropriate data structures (readable_reg and rd_table). Therefore, if provided, reg_read is used to perform the read operation; otherwise, the generic register map read function will be performed.
regmap_update_bits() is a three-in-one function. It performs a read/modify/write cycle on the specified register address. It is a wrapper on _regmap_update_bits, which looks like the following:
static int _regmap_update_bits(struct regmap *map,
unsigned int reg, unsigned int mask,
unsigned int val, bool *change,
bool force_write)
{
int ret;
unsigned int tmp, orig;
if (change)
*change = false;
if (regmap_volatile(map, reg) &&
map->reg_update_bits) {
ret = map->reg_update_bits(map->bus_context,
reg, mask, val);
if (ret == 0 && change)
*change = true;
} else {
ret = _regmap_read(map, reg, &orig);
if (ret != 0)
return ret;
tmp = orig & ~mask;
tmp |= val & mask;
if (force_write || (tmp != orig)) {
ret = _regmap_write(map, reg, tmp);
if (ret == 0 && change)
*change = true;
}
}
return ret;
}
This way, the bits you need to update must be set to 1 in mask, and corresponding bits should be set to the value you need to give to them in val.
As an example, to set the first and third bits to 1, mask should be 0b00000101, and the value should be 0bxxxxx1x1. To clear the seventh bit, mask must be 0b01000000 and the value should be 0bx0xxxxxx, and so on.
regmap_multi_reg_write() is one of the APIs that allows you to write multiple registers to the device. Its prototype looks like this:
int regmap_multi_reg_write(struct regmap *map,
const struct reg_sequence *regs,
int num_regs)
In this prototype, regs is an array of elements of type reg_sequence, which represents register/value pairs for sequences of writes with an optional delay in microseconds to be applied after each write. The following is the definition of this data structure:
struct reg_sequence {
unsigned int reg;
unsigned int def;
unsigned int delay_us;
};
In the preceding data structure, reg is the register address, def is the register value, and delay_us is the delay to be applied after the register write in microseconds.
The following is a usage of such a sequence:
static const struct reg_sequence foo_default_regs[] = {
{ FOO_REG1, 0xB8 },
{ BAR_REG1, 0x00 },
{ FOO_BAR_REG1, 0x10 },
{ REG_INIT, 0x00 },
{ REG_POWER, 0x00 },
{ REG_BLABLA, 0x00 },
};
static int probe ( ...)
{
[...]
ret = regmap_multi_reg_write(my_regmap,
foo_default_regs,
ARRAY_SIZE(foo_default_regs));
[...]
}
In the preceding, we have learned how to use our first multi-register write API, which takes a set of registers along with their values.
There are also regmap_bulk_read() and regmap_bulk_write(), which can be used to read/write multiple registers from/to the device. Their usage fits for large blocks of data and they are defined as follows:
int regmap_bulk_read(struct regmap *map,
unsigned int reg, void *val,
size_tval_count);
int regmap_bulk_write(struct regmap *map,
unsigned int reg,
const void *val, size_t val_count);
In the parameters in the preceding functions, map is the register map to operate on and reg is the register address from where the read/write operation must start. In the case of the read, val will contain the read value; it must be allocated to store at least the count value in the native register size for the device. In the case of the write operation, val must point to the data array to write to the device. Finally, count is the number of elements in val.
Obviously, Regmap supports data caching. Whether the cache system is used or not depends on the value of the cache_type field in regmap_config. Looking at include/linux/regmap.h, accepted values are as follows:
/* An enum of all the supported cache types */
enum regcache_type {
REGCACHE_NONE,
REGCACHE_RBTREE,
REGCACHE_COMPRESSED,
REGCACHE_FLAT,
};
The cache type is set to REGCACHE_NONE by default, meaning that the cache is disabled. Other values simply define how the cache should be stored.
Your device may have a predefined power-on reset value in certain registers. Those values can be stored in an array so that any read operation returns the value contained in the array. However, any write operation affects the real register in the device and updates the content in the array. It is a kind of a cache that we can use to speed up access to the device. That array is reg_defaults. Looking at the source, its structure looks like this:
struct reg_default {
unsigned int reg;
unsigned int def;
};
In the preceding data structure, reg is the register address and def is the register default value. reg_defaults is ignored if cache_type is set to none. If the default_reg element is not set but you still enable the cache, the corresponding cache structure will be created for you.
It is quite simple to use. Just declare it and pass it as a parameter to the regmap_config structure. Let's have a look at the LTC3589 regulator driver in drivers/regulator/ltc3589.c:
static const struct reg_default ltc3589_reg_defaults[] = {
{ LTC3589_SCR1, 0x00 },
{ LTC3589_OVEN, 0x00 },
{ LTC3589_SCR2, 0x00 },
{ LTC3589_VCCR, 0x00 },
{ LTC3589_B1DTV1, 0x19 },
{ LTC3589_B1DTV2, 0x19 },
{ LTC3589_VRRCR, 0xff },
{ LTC3589_B2DTV1, 0x19 },
{ LTC3589_B2DTV2, 0x19 },
{ LTC3589_B3DTV1, 0x19 },
{ LTC3589_B3DTV2, 0x19 },
{ LTC3589_L2DTV1, 0x19 },
{ LTC3589_L2DTV2, 0x19 },
};
static const struct regmap_config ltc3589_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = ltc3589_writeable_reg,
.readable_reg = ltc3589_readable_reg,
.volatile_reg = ltc3589_volatile_reg,
.max_register = LTC3589_L2DTV2,
.reg_defaults = ltc3589_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ltc3589_reg_defaults),
.use_single_rw = true,
.cache_type = REGCACHE_RBTREE,
};
Any read operation on any one of the registers present in the array will immediately return the value in the array. However, a write operation will be performed on the device itself and will update the affected register in the array. This way, reading the LTC3589_VRRCR register will return 0xff and write any value in that register, and it will update its entry in the array so that any new read operation will return the last written value directly from the cache.
Now that we are able to use the Regmap APIs to access the device registers whatever the underlying bus is these devices sit on, the time has come for us to summarize the knowledge we have learned so far in a practical example.
All the steps involved in setting up Regmap, from configuration to device register access, can be enumerated as follows:
Let's now materialize these steps in a real driver example that takes advantage of the Regmap framework.
To achieve our goal, let's first describe a fake SPI device for which we can write a driver using the Regmap framework. For understandability, let's use the following characteristics:
Now that we are done with the device and the Regmap specifications, we can start writing the code.
The following includes the required header to deal with Regmap:
#include <linux/regmap.h>
Depending on the APIs needed in the driver, other headers might be included.
Then, we define our private data structure as follows:
struct private_struct
{
/* Feel free to add whatever you want here */
struct regmap *map;
int foo;
};
Then, we define a read/write register range, that is, registers that are allowed to be accessed:
static const struct regmap_range wr_rd_range[] =
{
{
.range_min = 0x20,
.range_max = 0x4F,
},{
.range_min = 0x60,
.range_max = 0x7F
},
};
struct regmap_access_table drv_wr_table =
{
.yes_ranges = wr_rd_range,
.n_yes_ranges = ARRAY_SIZE(wr_rd_range),
};
struct regmap_access_table drv_rd_table =
{
.yes_ranges = wr_rd_range,
.n_yes_ranges = ARRAY_SIZE(wr_rd_range),
};
However, it must be noted that if writeable_reg and/or readable_reg are set, there is no need to provide wr_table and/or rd_table.
After that, we define the callback that will be called any time a register is accessed for a write or a read operation. Each callback must return true if it is allowed to perform the specified operation on the register:
static bool writeable_reg(struct device *dev,
unsigned int reg)
{
if (reg>= 0x20 &®<= 0x4F)
return true;
if (reg>= 0x60 &®<= 0x7F)
return true;
return false;
}
static bool readable_reg(struct device *dev,
unsigned int reg)
{
if (reg>= 0x20 &®<= 0x4F)
return true;
if (reg>= 0x60 &®<= 0x7F)
return true;
return false;
}
Now that all Regmap-related operations have been defined, we can implement the driver's probe method as follows:
static int my_spi_drv_probe(struct spi_device *dev)
{
struct regmap_config config;
struct private_struct *priv;
unsigned char data;
/* setup the regmap configuration */
memset(&config, 0, sizeof(config));
config.reg_bits = 8;
config.val_bits = 8;
config.write_flag_mask = 0x80;
config.max_register = 0x80;
config.fast_io = true;
config.writeable_reg = drv_writeable_reg;
config.readable_reg = drv_readable_reg;
/*
* If writeable_reg and readable_reg are set,
* there is no need to provide wr_table nor rd_table.
* Uncomment below code only if you do not want to use
* writeable_reg nor readable_reg.
*/
//config.wr_table = drv_wr_table;
//config.rd_table = drv_rd_table;
/* allocate the private data structures */
/* priv = kzalloc */
/* Init the regmap spi configuration */
priv->map = devm_regmap_init_spi(dev, &config);
/* Use devm_regmap_init_i2c in case of i2c bus */
/*
* Let us write into some register
* Keep in mind that, below operation will remain same
* whether you use SPI, I2C, or memory mapped Regmap.
* It is and advantage when you use regmap.
*/
regmap_read(priv->map, 0x30, &data);
[...] /* Process data */
data = 0x24;
regmap_write(priv->map, 0x23, data); /* write new value */
/* set bit 2 (starting from 0) and bit 6
* of register 0x44 */
regmap_update_bits(priv->map, 0x44,
0b00100010, 0xFF);
[...] /* Lot of stuff */
return 0;
}
In the preceding probe method, there are even more commands than code. We simply needed to demonstrate how the device specification could be translated into a register map configuration and used as main access functions to the device registers.
Now that we are done with Regmap from within the kernel, let's see how user space can make the most out of this framework in the next section.
Register maps can be monitored from the user space via the debugfs file system. First, debugfs needs to be enabled via the CONFIG_DEBUG_FS kernel configuration option. Then, debugfs can be mounted using the following command:
mount -t debugfs none /sys/kernel/debug
After that, the debugfs register map implementation can be found under /sys/kernel/debug/regmap/. This debugfs view implemented by drivers/base/regmap/regmap-debugfs.c in kernel sources contains a register cache (mirror) for drivers/peripherals based on the Regmap API.
From the Regmap main debugfs directory, we can get the list of devices whose drivers are based on the Regmap API using the following command:
root@jetson-nano-devkit:~# ls -l /sys/kernel/debug/regmap/
drwxr-xr-x 2 root root 0 Jan 1 1970 4-003c-power-slave
drwxr-xr-x 2 root root 0 Jan 1 1970 4-0068
drwxr-xr-x 2 root root 0 Jan 1 1970 700e3000.mipical
drwxr-xr-x 2 root root 0 Jan 1 1970 702d3000.amx
drwxr-xr-x 2 root root 0 Jan 1 1970 702d3100.amx
drwxr-xr-x 2 root root 0 Jan 1 1970 hdaudioC0D3-hdaudio
drwxr-xr-x 2 root root 0 Jan 1 1970 tegra210-admaif
drwxr-xr-x 2 root root 0 Jan 1 1970 tegra210-adx.0
[...]
root@jetson-nano-devkit:~#
In each directory, there can be one or more of the following files:
root@jetson-nano-devkit:~# cat /sys/kernel/debug/regmap/4-003c-power-slave/access
00: y y y n
01: y y y n
02: y y y n
03: y y y n
04: y y y n
05: y y y n
06: y y y n
07: y y y n
08: y y y n
[...]
5c: y y y n
5d: y y y n
5e: y y y n
For example, the line 5e: y y y n means that the register at address 5e is readable, writeable, volatile, but not precious.
root@jetson-nano-devkit:~# cat /sys/kernel/debug/regmap/702d3000.amx/name
tegra210-amx
There are, however, Regmap entries starting with dummy- as follows:
root@raspberrypi4-64:~# ls -l /sys/kernel/debug/regmap/
drwxr-xr-x 2 root root 0 Jan 1 1970 dummy-avs-monitor@fd5d2000
root@raspberrypi4-64:~#
This kind of entry is set when there are no associated /dev entries (devtmpfs). You can check this by printing the underlying device name, which will be nodev as follows:
root@raspberrypi4-64:~# cat /sys/kernel/debug/regmap/dummy-avs-monitor@fd5d2000/name
nodev
root@raspberrypi4-64:~#
You can look for the suffix name after dummy- to find the relevant node in the device tree, for example, dummy-avs-monitor@fd5d2000:
avs_monitor: avs-monitor@7d5d2000 {
compatible = "brcm,bcm2711-avs-monitor",
"syscon", "simple-mfd";
reg = <0x7d5d2000 0xf00>;
[...]
};
root@jetson-nano-devkit:~# cat /sys/kernel/debug/regmap/702d3000.amx/cache_bypass
N
To enable cache bypassing, you should echo Y in this file as follows:
root@jetson-nano-devkit:~# echo Y > /sys/kernel/debug/regmap/702d30[579449.571475] tegra210-amx tegra210-amx.0: debugfs cache_bypass=Y forced
00.amx/cache_bypass
root@jetson-nano-devkit:~#
This will additionally print a message in the kernel log buffer.
root@jetson-nano-devkit:~# cat /sys/kernel/debug/regmap/4-003c-power-slave/range
0-5e
root@jetson-nano-devkit:~# cat /sys/kernel/debug/regmap/4-003c-power-slave/rbtree
0-5e (95)
1 nodes, 95 registers, average 95 registers, used 175 bytes
root@jetson-nano-devkit:~# cat /sys/kernel/debug/regmap/4-003c-power-slave/registers
00: d2
01: 1f
02: 00
03: dc
04: 0f
05: 00
06: 00
07: 00
08: 02
[...]
5c: 35
5d: 81
5e: 00
#
In the preceding output, the register address is shown first, then its content after.
This section is quite short but is a concise description of Regmap monitoring from the user space. It allows reading and writing register content, and in some circumstances, changing the behavior of the underlying Regmap.
This chapter is all about register access related Regmap APIs. Its simplicity should give you an idea of how useful and widely used it is. This chapter has shown everything you need to know about the Regmap API. Now you should be able to convert any standard SPI/I2C/memory-mapped driver into Regmap.
The next chapter will cover IRQ management under Linux, however, two chapters after, we will cover IIO devices, a framework for analog-to-digital converters. Those kinds of devices always sit on top of SPI/I2C buses. It could be a challenge for us, at the end of that chapter, to write an IIO driver using the Regmap API.