Regmap API-A Register Map Abstraction

Keywords: Linux Programming less

From the book Linux Device Drivers Development: Develop customized drivers for embedded Linux Chapter 9 translation, translation level is limited, understanding!

Before the Regmap API was developed, device drivers for processing SPI cores, I2C cores, or both had redundant code. They all have the same principle: access registers for read/write operations. The following figure shows how SPI or I2C APIs were independent before regmap was introduced into the kernel:

In order to unify the way kernel developers access SPI/I2C devices, the regmap API is introduced in version 3.1 of the kernel. After introducing this mechanism, whether SPI or I2C has only one problem, how to initialize, configure regmap and process read/write/modify operations. The following figure shows how SPI or I2C interact after introducing regmap:

This chapter will introduce the regmap framework in the following ways:

  • Introduce the main data structures used in regmap framework
  • Introduce regmap configuration
  • Accessing devices using regmap API
  • Introduce regmap caching system
  • Provide a complete driver that summarizes all concepts

Regmap API programming

The regmap API is very simple. The two most important structures of this API are struct regmap_config, which represents the configuration of regmap, struct regmap, which is the regmap instance itself. All regmap data structures are defined in include/linux/regmap.h.

regmap_config structure

Strct regmap_config saves the driver's regmap configuration, and the settings here affect the read/write operation. It is the most important structure in the regmap api. The source code is 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);
    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;
    u8 read_flag_mask;
    u8 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;
}

reg_bits: Number of digits in register address, mandatory must be configured
val_bits: Number of registry values, mandatory must be configured
writeable_reg: This is an optional callback function. If provided, it is invoked by the regmap subsystem when a register needs to be written. Before writing to the register, this function is automatically called to check whether the value is written to the register. As follows:

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;
    }
}

readable_reg: Same as writeable_reg. Before reading the register, this function is automatically called to check whether the register value is read or not.
volatile_reg: Callback function called when reading or writing registers through regmap cache (if registers are volatile, then the function should return true) and perform direct read/write operations on registers. If false is returned, the register can be cached. In this case, the cache will be used for read operations, while in the case of write operations, the cache will be written:

static bool foo_volatile_register(struct device *dev,unsigned int reg)
{
    switch (reg) {
        case 0x24 ... 0x29:
        case 0xb6 ... 0xb8:
            return true;
        default:
            return false;
    }
}

max_register: This is optional, which specifies the maximum valid register address.
reg_read: Your device may not support simple i2c/spi reads (meaning you need to fill in this function to implement SPI/I2C custom reads). Then, you will have no choice but to write your own custom read function. reg_read should point to this function. That is to say, most devices do not need this function.
reg_write: Same as reg_read, but suitable for write operations.

I strongly recommend that you look at include/linux/regmap.h for more details on each element.

The following is an 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,
};

Regmap initialization

As mentioned earlier, the regmap API supports SPI and I2C protocols. According to the protocol to be supported in the driver, regmap_init_i2c() or regmap_init_spi() must be called in the probe() function. To write general-purpose drivers, remap is the best choice.
The regmap API is generic, requiring only different bus types to be changed at initialization, while other functions are the same.
Note: Initializing regmap in probe() is a good practice. Before initializing regmap, you must first fill in the regmap_config field.

regmap_exit function is used to release i2c or SPI register mapping:

void regmap_exit(struct regmap *map)

This function simply releases the regmap allocated before release

SPI initialization

The regmap SPI initialization includes setting up the regmap so that the read and write of the device is internally converted to the SPI command. For example, the function regmap_init_spi():

struct regmap * regmap_init_spi(struct spi_device *spi, const struct regmap_config);

Provide a SPI device of struct spi_device type as the first parameter, and a struct regmap_config, which represents the configuration of the regmap. This function returns a pointer to the allocated struct regmap and an ERR_PTR() value when it fails.

Examples are as follows:

static int foo_spi_probe(struct spi_device *client)
{
    int err;
    struct regmap *my_regmap;
    struct regmap_config bmp085_regmap_config;
    /* fill bmp085_regmap_config somewhere */

    [...]

    client->bits_per_word = 8;
    my_regmap = regmap_init_spi(client,&bmp085_regmap_config);
    if (IS_ERR(my_regmap)) {
        err = PTR_ERR(my_regmap);
        dev_err(&client->dev, "Failed to init regmap: %d\n", err);
        return err;
    }

    [...]

}

I2C initialization

i2c regmap calls regmap_init_i2c() to initialize the regmap configuration, and device reads and writes are converted internally into I2C commands. For example:

struct regmap * regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config);

The struct I2C device of type i2c_client is the first parameter and a pointer to struct regmap_config, which represents the configuration of regmap. The function returns a struct regmap pointer to the allocated structure when it succeeds, and an ERR_PTR() value when it fails.

Examples are as follows:

static int bar_i2c_probe(struct i2c_client *i2c,const struct i2c_device_id *id)
{
    struct my_struct * bar_struct;
    struct regmap_config regmap_cfg;
    /* fill regmap_cfgsome where */

    [...]

    bar_struct = kzalloc(&i2c->dev,
    sizeof(*my_struct), GFP_KERNEL);
    if (!bar_struct)
        return -ENOMEM;
    i2c_set_clientdata(i2c, bar_struct);
    bar_struct->regmap = regmap_init_i2c(i2c, &regmap_config);
    if (IS_ERR(bar_struct->regmap))
        return PTR_ERR(bar_struct->regmap);
    bar_struct->dev = &i2c->dev;
    bar_struct->irq = i2c->irq;

    [...]

}

Device access

API handles data parsing, formatting and transmission. In most cases, device reads and writes using regmap_read, regmap_write and regmap_update_bits. This is the three most important functions of data interaction between device master and slave. Their respective prototypes are:

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: Write data to the device. If max_register is set in regmap_config, the function checks the register address to be written. If the register address is less than or equal to max_register, the write operation will be executed; otherwise, the regmap core will return an invalid I/O error (-EIO). Next, the writeable_reg callback is executed, and the writeable_reg callback must return true before proceeding to the next step. If false is returned, the write operation stops and returns - EIO. If wr_table is set instead of writeable_reg, there are several cases:

  • If the register address is in no_range, return - EIO
  • If the register address is yes_range, perform the next step
  • If the register address is not in no_range or yes_range, the write operation ends and returns - EIO
  • If cache_type is not REGCACHE_NONE, the cache is enabled. In this case, the cache entry is updated first, and then the write operation is performed on the hardware; otherwise, the NO cache operation is performed.
  • If a reg_write callback is provided, it can be used to perform write operations; otherwise, a generic regmap write function is executed

regmap_read: Read data from the device. It is used in the same way as regmap_write. Therefore, if reg_read is provided, the read operation is performed by calling reg_read; otherwise, the generic regmap read function is called.

regmap_update_bits

regmap_update_bits has three functions, and its prototype is as follows:

int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val)

This function is encapsulated by _regmap_update_bits to perform read/write and modification operations on registered regmaps, as follows:

static int _regmap_update_bits(struct regmap *map,unsigned int reg, unsigned int mask,
    unsigned int val, bool *change)
{
    int ret;
    unsigned int tmp, orig;
    ret = _regmap_read(map, reg, &orig);
    if (ret != 0)
        return ret;
    tmp = orig& ~mask;
    tmp |= val & mask;
    if (tmp != orig) {
        ret = _regmap_write(map, reg, tmp);
        *change = true;
    } else {
        *change = false;
    }
    return ret;
}

The bit mask that needs to be updated must be set to 1 before the corresponding bit is assigned val.
For example, to set the first and third places to 1, the mask mask should be 0b00000101 and the value should be 0bxxxx1x1. To clear the seventh bit, the mask must be 0b01000000, the value should be 0bx0xxxxxx, and so on.

regmap_multi_reg_write

Function function is written to multiple registers of the device. Its prototype is as follows:

int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs, int num_regs)

To understand how to use this function, you first need to understand the struct reg_sequence:

/**
* Register/value pairs for sequences of writes with an optional delay in
* microseconds to be applied after each write.
*
* @reg: Register address.
* @def: Register value.
* @delay_us: Delay to be applied after the register write in microseconds
*/
struct reg_sequence {
    unsigned int reg;
    unsigned int def;
    unsigned int delay_us;
};

Its usage is as follows:

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 },
};
staticint probe ( ...)
{
    [...]

    ret = regmap_multi_reg_write(my_regmap, foo_default_regs,ARRAY_SIZE(foo_default_regs));

    [...]
}

Other Device Access Functions

regmap_bulk_read() and regmap_bulk_write() are used to read/write multiple registers from a device, usually in conjunction with a large number of data blocks:

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);

regmap and cache

Regmap supports caching, and the use of caching depends on the value of the cache_type field in regmap_config. Look at include/linux/regmap.h. The types supported by cache_type are:

/* Anenum of all the supported cache types */
enum regcache_type {
    REGCACHE_NONE,
    REGCACHE_RBTREE,
    REGCACHE_COMPRESSED,
    REGCACHE_FLAT,
};

By default, cache_type is set to regcache_NONE, which means that the cache is disabled. Other values define how to store cached values.

Your device may have predefined power-on reset values in some registers that can be stored in an array so that any read operation returns the values contained in the array. However, write operations affect the device's real registers and update the contents of the array. This is a cache that we can use to speed up access to devices. The array is reg_defaults, which is structured in the source code as follows:

/**
* Default value for a register. We use an array of structs rather
* than a simple array as many modern devices have very sparse
* register maps.
*
* @reg: Register address.
* @def: Register default value.
*/
struct reg_default {
    unsigned int reg;
    unsigned int def;
};

Note: If cache_type is set to none,reg_defaults will be ignored. If default reg is not set but caching is still enabled, the corresponding caching structure will be created.

It's very simple to use, just declare it and pass it as a parameter to the regmap_config structure. Let's look at the LTC3589 regulator driver at 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 register read operation in reg_default returns its value in the array immediately. However, write operations on registers in reg_default update the values of the corresponding registers in reg_default. For example, reading the ltc3589_vrrcr register will immediately return 0 xff; writing any value in the register will update the entries in the array so that any new read operation will return the final write value directly from the cache.

summary

Constructing the regmap subsystem steps:

  • Create the structure regmap_config according to device characteristics, set a register range if necessary, default values (if any), cache type (if necessary), and so on. If you need to customize read/write functions, pass them to the reg_read/reg_write field
  • In the probe() function, the regmap is allocated by calling regmap_init_i2c() or regmap_init_spi() according to the bus type (I2C or SPI).
  • Call the remap_read/remap_write function whenever you need to read/write values from registers
  • At the end, call regmap_exit() to release the regmap allocated in probe().

Regmap example

To achieve our goal, let's first describe a fake SPI device for which we can write a driver:

  • 8 bit register address
  • 8 bit register value
  • Maximum register address 0x80
  • Write mask 0x80
  • Effective address range
    0x20 to 0x4F
    0x60 to 0x7F
  • No custom read-write function is required

Skeleton sample program:

/* mandatory for regmap */
#include <linux/regmap.h>
/* Depending on your need you should include other files */
static struct private_struct
{
    /* Feel free to add whatever you want here */
    struct regmap *map;
    int foo;
};
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),
};
static bool writeable_reg(struct device *dev, unsigned int reg)
{
    if (reg>= 0x20 &&reg<= 0x4F)
        return true;
    if (reg>= 0x60 &&reg<= 0x7F)
        return true;
    return false;
}
static bool readable_reg(struct device *dev, unsigned int reg)
{
    if (reg>= 0x20 &&reg<= 0x4F)
        return true;
    if (reg>= 0x60 &&reg<= 0x7F)
        return true;
    return false;
}
static int my_spi_drv_probe(struct spi_device *dev)
{
    struct regmap_config config;
    struct custom_drv_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 = regmap_init_spi(dev, &config);

    /* Use 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 or I2C. 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 6 of register 0x44 */
    regmap_update_bits(priv->map, 0x44, 0b00100010, 0xFF);
    [...] /* Lot of stuff */
    return 0;
}

Posted by trexx on Mon, 17 Dec 2018 09:51:04 -0800