设备驱动中的regmap

regmap 机制是在 Linux 3.1 加入进来的特性。主要目的是减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件上的寄存器。其实这就是内核做的一次重构。regmap 除了能做到统一的 I/O 接口,还可以在驱动和硬件 IC 之间做一层缓存,从而能减少底层 I/O 的操作次数。

以一个 I2C 设备为例。读写一个寄存器,肯定需要用到i2c_transfer这样的 I2C 函数。为了方便,一般的驱动中,会在这之上再写一个 Wrapper,然后通过调用这个 Wrapper 来读写寄存器。比如如下这个读取寄存器的函数:


static int xxx_i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
    struct i2c_msg msg[] = {
        {
            .addr = client->addr,
            .flags = 0,
            .len = 1,
            .buf = ®,
        },
        {
            .addr = client->addr,
            .flags = I2C_M_RD,
            .len = 1,
            .buf = val,
        },
    };

    return i2c_transfer(client->adapter, msg, 2);
}

而如果 regmap 的方式来实现,对于上面这种读寄存器操作,其实现如下:

// first step: define regmap_config
static const struct regmap_config xxx_regmap_config = {
    .reg_bits = 10,
    .val_bits = 14,

    .max_register = 40,
    .cache_type = REGCACHE_RBTREE,

    .volatile_reg = false,
    .readable_reg = false,
};

// second step: initialize regmap in driver loading
regmap = regmap_init_i2c(i2c_client, &xxx_regmap_config);

// third step: register operations
regmap_read(regmap, XXX_REG, &value);

第一步就是定义 IC 的一些寄存器信息。比如:位宽,地址位宽,寄存器总数等。然后在驱动加载的时候,初始化 regmap,这样就可以正常调用 regmap 的 API 了。

可以看到,为了让慢速 I/O 能够专注于自身的逻辑,内核把 SPI, I2C 等总线操作方式全部封装在 regmap 里,这样驱动若要做 I/O 操作,直接调用 regmap 的函数就可以了。

regmap_config

struct regmap_config结构体代表一个设备的寄存器配置信息,在做 Regmap 初始化时,驱动就需要把这个结构体传给 Regmap。这个结构体的定义在 include/linux/regmap.h,其中包含该设备的寄存器数量,寄存器位宽,缓存类型,读写属性等。

这一层是直接和驱动对接的。Regmap 根据传进来的regmap_config初始化对应的缓存和总线操作接口,驱动就可以正常调用 regmap_writeregmap_read函数。

3.2 regmap_ops


/**
 * Configuration for the register map of a device.
 *
 * @name: Optional name of the regmap. Useful when a device has multiple
 *        register regions.
 *
 * @reg_bits: Number of bits in a register address, mandatory.
 * @reg_stride: The register address stride. Valid register addresses are a
 *              multiple of this value. If set to 0, a value of 1 will be
 *              used.
 * @pad_bits: Number of bits of padding between register and value.
 * @val_bits: Number of bits in a register value, mandatory.
 *
 * @writeable_reg: Optional callback returning true if the register
 *         can be written to. If this field is NULL but wr_table
 *         (see below) is not, the check is performed on such table
 *                 (a register is writeable if it belongs to one of the ranges
 *                  specified by wr_table).
 * @readable_reg: Optional callback returning true if the register
 *        can be read from. If this field is NULL but rd_table
 *         (see below) is not, the check is performed on such table
 *                 (a register is readable if it belongs to one of the ranges
 *                  specified by rd_table).
 * @volatile_reg: Optional callback returning true if the register
 *        value can't be cached. If this field is NULL but
 *        volatile_table (see below) is not, the check is performed on
 *                such table (a register is volatile if it belongs to one of
 *                the ranges specified by volatile_table).
 * @precious_reg: Optional callback returning true if the register
 *        should not be read outside of a call from the driver
 *        (e.g., a clear on read interrupt status register). If this
 *                field is NULL but precious_table (see below) is not, the
 *                check is performed on such table (a register is precious if
 *                it belongs to one of the ranges specified by precious_table).
 * @lock:     Optional lock callback (overrides regmap's default lock
 *        function, based on spinlock or mutex).
 * @unlock:   As above for unlocking.
 * @lock_arg:     this field is passed as the only argument of lock/unlock
 *        functions (ignored in case regular lock/unlock functions
 *        are not overridden).
 * @reg_read:     Optional callback that if filled will be used to perform
 *                all the reads from the registers. Should only be provided for
 *        devices whose read operation cannot be represented as a simple
 *        read operation on a bus such as SPI, I2C, etc. Most of the
 *        devices do not need this.
 * @reg_write:    Same as above for writing.
 * @fast_io:      Register IO is fast. Use a spinlock instead of a mutex
 *            to perform locking. This field is ignored if custom lock/unlock
 *            functions are used (see fields lock/unlock of struct regmap_config).
 *        This field is a duplicate of a similar file in
 *        'struct regmap_bus' and serves exact same purpose.
 *         Use it only for "no-bus" cases.
 * @max_register: Optional, specifies the maximum valid register address.
 * @wr_table:     Optional, points to a struct regmap_access_table specifying
 *                valid ranges for write access.
 * @rd_table:     As above, for read access.
 * @volatile_table: As above, for volatile registers.
 * @precious_table: As above, for precious registers.
 * @reg_defaults: Power on reset values for registers (for use with
 *                register cache support).
 * @num_reg_defaults: Number of elements in reg_defaults.
 *
 * @read_flag_mask: Mask to be set in the top byte of the register when doing
 *                  a read.
 * @write_flag_mask: Mask to be set in the top byte of the register when doing
 *                   a write. If both read_flag_mask and write_flag_mask are
 *                   empty the regmap_bus default masks are used.
 * @use_single_rw: If set, converts the bulk read and write operations into
 *          a series of single read and write operations. This is useful
 *          for device that does not support bulk read and write.
 * @can_multi_write: If set, the device supports the multi write mode of bulk
 *                   write operations, if clear multi write requests will be
 *                   split into individual write operations
 *
 * @cache_type: The actual cache type.
 * @reg_defaults_raw: Power on reset values for registers (for use with
 *                    register cache support).
 * @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
 * @reg_format_endian: Endianness for formatted register addresses. If this is
 *                     DEFAULT, the @reg_format_endian_default value from the
 *                     regmap bus is used.
 * @val_format_endian: Endianness for formatted register values. If this is
 *                     DEFAULT, the @reg_format_endian_default value from the
 *                     regmap bus is used.
 *
 * @ranges: Array of configuration entries for virtual address ranges.
 * @num_ranges: Number of range configuration entries.
 */
struct regmap_config {
    const char *name;

    int reg_bits;  //寄存器地址的位数,必须配置,例如I2C寄存器地址位数为 8  
    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; //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;
};

regcache_ops

struct regcache_ops是用来定义一个缓存类型的,具体定义如下:


struct regcache_ops {
    const char *name;
    enum regcache_type type;
    int (*init)(struct regmap *map);
    int (*exit)(struct regmap *map);
#ifdef CONFIG_DEBUG_FS
    void (*debugfs_init)(struct regmap *map);
#endif
    int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
    int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
    int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
    int (*drop)(struct regmap *map, unsigned int min, unsigned int max);
};

在最新 Linux 4.0 版本中,已经有 3 种缓存类型,分别是数组(flat)、LZO 压缩和红黑树(rbtree)。数组好理解,是最简单的缓存类型,当设备寄存器很少时,可以用这种类型来缓存寄存器值。LZO(Lempel–Ziv–Oberhumer) 是 Linux 中经常用到的一种压缩算法,Linux 编译后就会用这个算法来压缩。这个算法有 3 个特性:压缩快,解压不需要额外内存,压缩比可以自动调节。在这里,你可以理解为一个数组缓存,套了一层压缩,来节约内存。当设备寄存器数量中等时,可以考虑这种缓存类型。而最后一类红黑树,它的特性就是索引快,所以当设备寄存器数量比较大,或者对寄存器操作延时要求低时,就可以用这种缓存类型。

缓存的类型是在 regmap 初始化时,由.cache_type = REGCACHE_RBTREE 来指定的。对于 regmap_read 来说,会先判断当前缓存是否有值,然后再检查是否需要 bypass,若没有,则可以直接从缓存里面取值,调用regcache_read来获取值,若需要从硬件上读取,则调用具体协议的读写函数,若是 I2C,调用i2c_transfer。写的过程也是大同小异。

regmap_bus

前面说的都是 regmap 所做的封装,而真正进行 I/O 操作就是这最后一层。struct regmap_bus定义了一个总线上的读写函数,这一层就像之前对i2c_transfer所做的封装一样。其定义如下:


/**
 * Description of a hardware bus for the register map infrastructure.
 *
 * @fast_io: Register IO is fast. Use a spinlock instead of a mutex
 *       to perform locking. This field is ignored if custom lock/unlock
 *       functions are used (see fields lock/unlock of
 *       struct regmap_config).
 * @write: Write operation.
 * @gather_write: Write operation with split register/value, return -ENOTSUPP
 *                if not implemented  on a given device.
 * @async_write: Write operation which completes asynchronously, optional and
 *               must serialise with respect to non-async I/O.
 * @reg_write: Write a single register value to the given register address. This
 *             write operation has to complete when returning from the function.
 * @read: Read operation.  Data is returned in the buffer used to transmit
 *         data.
 * @reg_read: Read a single register value from a given register address.
 * @free_context: Free context.
 * @async_alloc: Allocate a regmap_async() structure.
 * @read_flag_mask: Mask to be set in the top byte of the register when doing
 *                  a read.
 * @reg_format_endian_default: Default endianness for formatted register
 *     addresses. Used when the regmap_config specifies DEFAULT. If this is
 *     DEFAULT, BIG is assumed.
 * @val_format_endian_default: Default endianness for formatted register
 *     values. Used when the regmap_config specifies DEFAULT. If this is
 *     DEFAULT, BIG is assumed.
 * @max_raw_read: Max raw read size that can be used on the bus.
 * @max_raw_write: Max raw write size that can be used on the bus.
 */
struct regmap_bus {
    bool fast_io;
    regmap_hw_write write;
    regmap_hw_gather_write gather_write;
    regmap_hw_async_write async_write;
    regmap_hw_reg_write reg_write;
    regmap_hw_reg_update_bits reg_update_bits;
    regmap_hw_read read;
    regmap_hw_reg_read reg_read;
    regmap_hw_free_context free_context;
    regmap_hw_async_alloc async_alloc;
    u8 read_flag_mask;
    enum regmap_endian reg_format_endian_default;
    enum regmap_endian val_format_endian_default;
    size_t max_raw_read;
    size_t max_raw_write;
};

在 kernel-4.0 中,已经支持了 I2C、SPI、AC97、MMIO 和 SPMI 五种总线类型。相信在未来,有更多的总线会加进来。其实添加一个总线也不是很难,只需 4 个函数就可以了:xxx_readxxx_writexxx_initxxx_deinit

regmap结构体

具体的设备I/O操作还需要使用regmap结构来实现整个调度:

struct regmap {
    union {
        struct mutex mutex;
        struct {
            spinlock_t spinlock;
            unsigned long spinlock_flags;
        };
    };
    regmap_lock lock;
    regmap_unlock unlock;
    void *lock_arg; /* This is passed to lock/unlock functions */
    gfp_t alloc_flags;

    struct device *dev; /* Device we do I/O on */
    void *work_buf;     /* Scratch buffer used to format I/O */
    struct regmap_format format;  /* Buffer format */
    const struct regmap_bus *bus;
    void *bus_context;
    const char *name;

    bool async;
    spinlock_t async_lock;
    wait_queue_head_t async_waitq;
    struct list_head async_list;
    struct list_head async_free;
    int async_ret;

#ifdef CONFIG_DEBUG_FS
    struct dentry *debugfs;
    const char *debugfs_name;

    unsigned int debugfs_reg_len;
    unsigned int debugfs_val_len;
    unsigned int debugfs_tot_len;

    struct list_head debugfs_off_cache;
    struct mutex cache_lock;
#endif

    unsigned int max_register;
    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);
    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;

    int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
    int (*reg_write)(void *context, unsigned int reg, unsigned int val);
    int (*reg_update_bits)(void *context, unsigned int reg,
                   unsigned int mask, unsigned int val);

    bool defer_caching;

    u8 read_flag_mask;
    u8 write_flag_mask;

    /* number of bits to (left) shift the reg value when formatting*/
    int reg_shift;
    int reg_stride;
    int reg_stride_order;

    /* regcache specific members */
    const struct regcache_ops *cache_ops;
    enum regcache_type cache_type;

    /* number of bytes in reg_defaults_raw */
    unsigned int cache_size_raw;
    /* number of bytes per word in reg_defaults_raw */
    unsigned int cache_word_size;
    /* number of entries in reg_defaults */
    unsigned int num_reg_defaults;
    /* number of entries in reg_defaults_raw */
    unsigned int num_reg_defaults_raw;

    /* if set, only the cache is modified not the HW */
    bool cache_only;
    /* if set, only the HW is modified not the cache */
    bool cache_bypass;
    /* if set, remember to free reg_defaults_raw */
    bool cache_free;

    struct reg_default *reg_defaults;
    const void *reg_defaults_raw;
    void *cache;
    /* if set, the cache contains newer data than the HW */
    bool cache_dirty;
    /* if set, the HW registers are known to match map->reg_defaults */
    bool no_sync_defaults;

    struct reg_sequence *patch;
    int patch_regs;

    /* if set, converts bulk read to single read */
    bool use_single_read;
    /* if set, converts bulk read to single read */
    bool use_single_write;
    /* if set, the device supports multi write mode */
    bool can_multi_write;

    /* if set, raw reads/writes are limited to this size */
    size_t max_raw_read;
    size_t max_raw_write;

    struct rb_root range_tree;
    void *selector_work_buf;    /* Scratch buffer used for selector */
};

以i2c的电源管理为例,源码在smb374-charger.c中:

使用devm_regmap_init_i2c 初始化,devm_regmap_init_i2c 定义在regmap.h中


/**
 * devm_regmap_init_i2c(): Initialise managed register map
 *
 * @i2c: Device that will be interacted with
 * @config: Configuration for register map
 *
 * The return value will be an ERR_PTR() on error or a valid pointer
 * to a struct regmap.  The regmap will be automatically freed by the
 * device management code.
 */
#define devm_regmap_init_i2c(i2c, config)               \
    __regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config,   \
                i2c, config)

调用了__regmap_lockdep_wrapper 宏:

#define __regmap_lockdep_wrapper(fn, name, ...)             \
(                                   \
({                              \
    static struct lock_class_key _key;          \
    fn(__VA_ARGS__, &_key,                  \
        KBUILD_BASENAME ":"             \
        __stringify(__LINE__) ":"           \
        "(" name ")->lock");                \
})                              \
)  

regmap_config 结构体赋值:

static const struct regmap_config smb347_regmap = {
    .reg_bits   = 8,
    .val_bits   = 8,
    .max_register   = SMB347_MAX_REGISTER,
    .volatile_reg   = smb347_volatile_reg,
    .readable_reg   = smb347_readable_reg,
};

regmap_read 进行读操作:


/**
 * regmap_read(): Read a value from a single register
 *
 * @map: Register map to read from
 * @reg: Register to be read from
 * @val: Pointer to store read value
 *
 * A value of zero will be returned on success, a negative errno will
 * be returned in error cases.
 */
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
    int ret;

    if (!IS_ALIGNED(reg, map->reg_stride))
        return -EINVAL;

    map->lock(map->lock_arg);

    ret = _regmap_read(map, reg, val);

    map->unlock(map->lock_arg);

    return ret;
}

设置为写操作,调用regmap_update_bits 函数:

未完待续。。。。

你可能感兴趣的:(kernel,linux设备驱动)