Linux內核:regmap機制


背景

在學習SPI框架的時候,看到了有一個rtc驅動用到了regmap,本想通過傳統方式訪問spi接口的我,突然有點不適應,翻了整個驅動,愣是沒有找到讀寫spi的范式;因此了解了regmap以后,才發現regmap做了這個事情。

介紹

在Linu 3.1開始,Linux引入了regmap來統一管理內核的I2C, SPI等總線,將I2C, SPI驅動做了一次重構,把I/O讀寫的重復邏輯在regmap中實現。只需初始化時指定總線類型、寄存器位寬等關鍵參數,即可通過regmap模型接口來操作器件寄存器。

當然,regmap同樣適用於操作cpu自身的寄存器。將i2c、spi、mmio、irq都抽象出統一的接口regmap_read、regmap_write、regmap_update_bits等接口 ,從而提高代碼的可重用性,並且使得在使用如上內核基礎組件時變得更為簡單易用。

regmap是在 linux 內核為減少慢速 I/O 驅動上的重復邏輯,提供一種通用的接口來操作底層硬件寄存器的模型框架。

此外,如果在regmap在驅動和硬件寄存器之間使用cache,會減少底層低速 I/O 的操作次數,提高訪問效率;但降低了實時性會有所降低。

傳統的寫法

用一個I2C設備為例,在3.1之前的I2C設備驅動,需要各自調用i2c_transfer來實現讀寫,比如:

static int raydium_i2c_pda2_write(struct i2c_client *client,
        unsigned char addr, unsigned char *w_data, unsigned short length)
{
    int retval = -1;
    unsigned char retry;
    unsigned char buf[MAX_WRITE_PACKET_SIZE + 1];
    struct raydium_ts_data *data = i2c_get_clientdata(client);

    struct i2c_msg msg[] = {
        {
            .addr = RAYDIUM_I2C_NID,
            .flags = RAYDIUM_I2C_WRITE,
            .len = length + 1,
            .buf = buf,
        },
    };


    if (length > MAX_WRITE_PACKET_SIZE)
    {
        return -EINVAL;
    }
    u8_i2c_mode = PDA2_MODE;
    buf[0] = addr;
    memcpy(&buf[1], w_data, length);

    for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++)
    {
        if (i2c_transfer(client->adapter, msg, 1) == 1)
        {
            retval = length;
            break;
        }
        msleep(20);
    }

    if (retry == SYN_I2C_RETRY_TIMES)
    {
        dev_err(&data->client->dev,"[touch]%s: I2C write over retry limit.addr[0x%x]\n",__func__, addr);
        retval = -EIO;
    }

    return retval;
}

需要自行構建i2c_msg,然后使用i2c_transfer傳輸數據。

使用regmap機制進行讀寫

但是使用regmap機制,就會變的更為簡單。

使用regmap比較簡單:

1、使用前,只需根據外設屬性配置配置regmap信息(總線類型、寄存器位寬、緩存類型、讀寫屬性等參數);

2、接着注冊一個regmap實例;

3、然后調用抽象訪問接口訪問寄存器;

4、不想使用的時候,釋放regmap實例即可。

例如:

// 1.構建regmap_config結構
static const struct regmap_config mlx90632_regmap = {
        // 寄存器地址位寬
	.reg_bits = 16,
        // 寄存器值的位寬,必須填寫
	.val_bits = 16,
        // 可選,判斷寄存器是否可寫,可讀,是否可緩沖等回調
	.volatile_table = &mlx90632_volatile_regs_tbl,
	.rd_table = &mlx90632_readable_regs_tbl,
	.wr_table = &mlx90632_writeable_regs_tbl,

	.use_single_rw = true,
	.reg_format_endian = REGMAP_ENDIAN_BIG,
	.val_format_endian = REGMAP_ENDIAN_BIG,
	.cache_type = REGCACHE_RBTREE,
};

// 2. 初始化regmap(或者regmap_init_spi)
struct regmap *map= regmap_init_i2c(client, &mlx90632_regmap);

//3.讀寫操作
ret = regmap_read(mlx90632->regmap, MLX90632_EE_VERSION, &read);

//4. 釋放regmap
regmap_exit(&mlx90632_regmap);

讀寫特性

regmap的write操作:

  • 先嘗試向緩沖cache中write.
  • 如果bypass了,或者volatile,或者cache none情況,調用對應的底層協議進行寫操作。

_regmap_read:

_regmap_read

  • 先拿到context,在以后的map->reg_read會用到
  • 如果不是bypass的,調用regcache_read從cache中讀
  • 如果不能從cache中read到,調用map->reg_read

regmap_config原型

在regmap_config,定義了寄存器的各種信息,比如寄存器地址長度,寄存器值的長度,讀寫寄存器的地址范圍的信息,寄存器地址和值的大小端以及緩沖方式。

// include/linux/regmap.h
struct regmap_config {
    const char *name; // 可選,寄存器名字

    int reg_bits; // 寄存器地址位寬,必須填寫
    int reg_stride; // 寄存器操作寬度,比如為1時,所有寄存器可操作,為2時,只有2^n可操作
    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;
};

regmap_init_i2c

當設備驅動配置好config以后,調用對應的regmap_init_xx,例如,這里是regmap_init_i2c

// drivers/base/regmap/regmap-i2c.c

struct regmap *regmap_init_i2c(struct i2c_client *i2c,
                               const struct regmap_config *config)
{
    // regmap_bus 定位了對應總線的讀寫函數。
    const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);

    if (IS_ERR(bus))
        return ERR_CAST(bus);

    return regmap_init(&i2c->dev, bus, &i2c->dev, config);
}

注意到2個新的數據結構:

  • struct regmap_bus *bus
  • struct regmap *regmap_init_i2c(..

regmap_get_i2c_bus

對於普通I2C設備,regmap_bus為:

// drivers/base/regmap/regmap-i2c.c
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
                    const struct regmap_config *config)
{
    if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
        return &regmap_i2c;
    // ...

    return ERR_PTR(-ENOTSUPP);
}

static struct regmap_bus regmap_i2c = {
    .write = regmap_i2c_write,
    .gather_write = regmap_i2c_gather_write,
    .read = regmap_i2c_read,
    .reg_format_endian_default = REGMAP_ENDIAN_BIG,
    .val_format_endian_default = REGMAP_ENDIAN_BIG,
};

regmap_bus結構定義了讀寫函數和默認的寄存器地址和寄存器值的大小端。

regmap_bus原型

/**
 * struct regmap_bus - 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.
 * @reg_update_bits: Update bits operation to be used against volatile
 *                   registers, intended for devices supporting some mechanism
 *                   for setting clearing bits without having to
 *                   read/modify/write.
 * @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;
};

regmap_i2c_read

我們看下i2c對應的regmap_bus.read實現:

@/kernel/driver/base/rgmap/regmap-i2c.c
static int regmap_i2c_read(void *context,
               const void *reg, size_t reg_size,
               void *val, size_t val_size)
{
    struct device *dev = context;
    struct i2c_client *i2c = to_i2c_client(dev);
    struct i2c_msg xfer[2];
    int ret;

    xfer[0].addr = i2c->addr;
    xfer[0].flags = 0;
    xfer[0].len = reg_size;
    xfer[0].buf = (void *)reg;

    xfer[1].addr = i2c->addr;
    xfer[1].flags = I2C_M_RD;
    xfer[1].len = val_size;
    xfer[1].buf = val;

    ret = i2c_transfer(i2c->adapter, xfer, 2);
    if (ret == 2)
        return 0;
    else if (ret < 0)
        return ret;
    else
        return -EIO;
}

可以看到其實就是構造i2c_msg,選擇操作寄存器地址然后讀寄存器值。這正是regmap要做的事情,抽離出總線讀寫操作到regmap中,避免驅動去重復實現。

等到設備驅動調用regmap_read以后,最終會調用到regmap_i2c_read,這樣子就會執行i2c驅動典型的驅動流程。

regmap_init

當在regmap_get_i2c_bus獲取到對應的bus以后,就可以進行regmap的初始化了。

regmap_init主要是把config和描述i2c設備的bus設置到regmap中

struct regmap *regmap_init(struct device *dev,
               const struct regmap_bus *bus,
               void *bus_context,
               const struct regmap_config *config)
{
    struct regmap *map;
    int ret = -EINVAL;
    enum regmap_endian reg_endian, val_endian;

    map = kzalloc(sizeof(*map), GFP_KERNEL);
    // 將regmap_config定義的參數賦值到regmap中
    map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
    map->format.pad_bytes = config->pad_bits / 8;
    map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
    map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
            config->val_bits + config->pad_bits, 8);
    map->reg_shift = config->pad_bits % 8;
    if (config->reg_stride)
        map->reg_stride = config->reg_stride;
    else
        map->reg_stride = 1;
    map->use_single_rw = config->use_single_rw;
    map->can_multi_write = config->can_multi_write;
    map->dev = dev;
    map->bus = bus;
    map->bus_context = bus_context;
    map->max_register = config->max_register;
    map->wr_table = config->wr_table;
    map->rd_table = config->rd_table;
    map->volatile_table = config->volatile_table;
    map->precious_table = config->precious_table;
    map->writeable_reg = config->writeable_reg;
    map->readable_reg = config->readable_reg;
    map->volatile_reg = config->volatile_reg;
    map->precious_reg = config->precious_reg;
    map->cache_type = config->cache_type;
    map->name = config->name;

    /* regmap中有reg_read操作方法,通過bus是否為空賦值 */
    if (!bus) {
        // 對於該驅動,此處只會走這里 
        map->reg_read  = config->reg_read;
        map->reg_write = config->reg_write;

        map->defer_caching = false;
        goto skip_format_initialization;
    } else if (!bus->read || !bus->write) {
        map->reg_read = _regmap_bus_reg_read;
        map->reg_write = _regmap_bus_reg_write;

        map->defer_caching = false;
        goto skip_format_initialization;
    } else {
        map->reg_read  = _regmap_bus_read;
    }
    // 設置地址和寄存器值的大小端
    reg_endian = regmap_get_reg_endian(bus, config);
    val_endian = regmap_get_val_endian(dev, bus, config);
    // 根據寄存器地址位寬和大小端解析寄存器地址
    switch (config->reg_bits + map->reg_shift) {
        case 32:
        switch (reg_endian) {
        case REGMAP_ENDIAN_BIG:
            map->format.format_reg = regmap_format_32_be;
            break;
        case REGMAP_ENDIAN_NATIVE:
            map->format.format_reg = regmap_format_32_native;
            break;
        default:
            goto err_map;
        }
        break;
    }
    // 根據寄存器值位寬和大小端解析寄存器值
    switch (config->val_bits) {
        case 16:
        switch (val_endian) {
        case REGMAP_ENDIAN_BIG:
            map->format.format_val = regmap_format_16_be;
            map->format.parse_val = regmap_parse_16_be;
            map->format.parse_inplace = regmap_parse_16_be_inplace;
            break;
        case REGMAP_ENDIAN_LITTLE:
            map->format.format_val = regmap_format_16_le;
            map->format.parse_val = regmap_parse_16_le;
            map->format.parse_inplace = regmap_parse_16_le_inplace;
            break;
        case REGMAP_ENDIAN_NATIVE:
            map->format.format_val = regmap_format_16_native;
            map->format.parse_val = regmap_parse_16_native;
            break;
        default:
            goto err_map;
        }
        break;
    }

    /* 對於val_bits = 16,reg_bits=16,regmap寫函數選擇_regmap_bus_raw_write */
    if (map->format.format_write) {
        map->defer_caching = false;
        map->reg_write = _regmap_bus_formatted_write;
    } else if (map->format.format_val) {
        map->defer_caching = true;
        map->reg_write = _regmap_bus_raw_write;
    }
    // 緩存初始化
    ret = regcache_init(map, config);
}

regmap_init()函數初始化了regmap,regmap中函數bus指針和config寄存器參數,已經足夠用來操作I2C的寄存器了。

同時,也設置了regmap.reg_read_regmap_bus_read

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;

    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;

    unsigned long read_flag_mask;
    unsigned long 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 */
};

_regmap_bus_read

reg_read回調函數是我們在讀數據要使用的方法,

剛剛已經說了,map->reg_read = _regmap_bus_read;

我們需要看其實現:

// drivers/base/regmap/regmap.c

static int _regmap_bus_read(void *context, unsigned int reg,
                unsigned int *val)
{
    int ret;
    struct regmap *map = context;

    if (!map->format.parse_val)
        return -EINVAL;
    // 讀出寄存器數據
    ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
    if (ret == 0) // 根據寄存器位值寬和大小端得到寄存器值
        *val = map->format.parse_val(map->work_buf);

    return ret;
}

_regmap_raw_read

繼續看:

static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                unsigned int val_len)
{
    struct regmap_range_node *range;
    int ret;

    WARN_ON(!map->bus);

    if (!map->bus || !map->bus->read)
        return -EINVAL;
    // 從范圍中找到某一頁(但在這里沒有用到)。
    range = _regmap_range_lookup(map, reg);
    if (range) {
        ret = _regmap_select_page(map, &reg, range,
                      val_len / map->format.val_bytes);
        if (ret != 0)
            return ret;
    }

    map->format.format_reg(map->work_buf, reg, map->reg_shift);
    regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
                      map->read_flag_mask);
    trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes);
    // 使用bus定義的read函數指針讀數據,這樣子就會調用到`regmap_i2c_read`
    ret = map->bus->read(map->bus_context, map->work_buf,
                 map->format.reg_bytes + map->format.pad_bytes,
                 val, val_len);

    trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes);

    return ret;
}
查找range(不理解)

這個部分的代碼好像是不會執行的。

// drivers/base/regmap/internal.h
struct regmap_range_node {
    struct rb_node node;
    const char *name;
    struct regmap *map;

    unsigned int range_min;
    unsigned int range_max;

    unsigned int selector_reg;
    unsigned int selector_mask;
    int selector_shift;
    // range_min、range_max是當前reg范圍的開始與結束地址,window_len是”窗”的長度.
    unsigned int window_start;
    unsigned int window_len;
};

// drivers/base/regmap/regmap.c
static struct regmap_range_node *_regmap_range_lookup(struct regmap *map,
                              unsigned int reg)
{
    struct rb_node *node = map->range_tree.rb_node;
    // reg是當前寄存器的index
    // range_min、range_max是當前reg范圍的開始與結束地址
    while (node) {
        struct regmap_range_node *this =
            rb_entry(node, struct regmap_range_node, node);
        // 找到 落在 min 與 max 這個范圍中的 reg
        if (reg < this->range_min)
            node = node->rb_left;
        else if (reg > this->range_max)
            node = node->rb_right;
        else
            return this;
    }

    return NULL;
}

static int _regmap_select_page(struct regmap *map, unsigned int *reg,
                   struct regmap_range_node *range,
                   unsigned int val_num)
{
    void *orig_work_buf;
    unsigned int win_offset;
    unsigned int win_page;
    bool page_chg;
    int ret;

    // 計算偏移
    win_offset = (*reg - range->range_min) % range->window_len;
    win_page = (*reg - range->range_min) / range->window_len;

    if (val_num > 1) {
        /* Bulk write shouldn't cross range boundary */
        if (*reg + val_num - 1 > range->range_max)
            return -EINVAL;

        /* ... or single page boundary */
        if (val_num > range->window_len - win_offset)
            return -EINVAL;
    }

    /* It is possible to have selector register inside data window.
       In that case, selector register is located on every page and
       it needs no page switching, when accessed alone. */
    /* 如果目標寄存器跨越1個寄存器(超過4個字節了)
        調用_regmap_update_bits來拿到新的數據
            _regmap_update_bits調用_regmap_read
            先從i2c中目標寄存器中讀出值orig,
            然后把該值orig掩碼掉mask,然后或上val,
            將得到的結果調用_regmap_write寫入到目標寄存器
    */
    if (val_num > 1 ||
        range->window_start + win_offset != range->selector_reg) {
        /* Use separate work_buf during page switching */
        orig_work_buf = map->work_buf;
        map->work_buf = map->selector_work_buf;

        ret = _regmap_update_bits(map, range->selector_reg,
                      range->selector_mask,
                      win_page << range->selector_shift,
                      &page_chg, false);

        map->work_buf = orig_work_buf;

        if (ret != 0)
            return ret;
    }

    *reg = range->window_start + win_offset;

    return 0;
}

regmap_read

regmap_read函數用來讀取寄存器值,實現如下:

int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
    int ret;
    // 通過寄存器操作寬度判斷寄存器操作是否合法
    if (reg % map->reg_stride)
        return -EINVAL;

    map->lock(map->lock_arg);

    ret = _regmap_read(map, reg, val);

    map->unlock(map->lock_arg);

    return ret;
}

static int _regmap_read(struct regmap *map, unsigned int reg,
                        unsigned int *val)
{
    // 1.直接從緩存取值
    if (!map->cache_bypass) {
        ret = regcache_read(map, reg, val);
        if (ret == 0)
            return 0;
    }
    // 2.最終調用bus定義的read讀寄存器數據
    ret = map->reg_read(context, reg, val);
    // 3.將寄存器結果寫入緩存
    if (!map->cache_bypass)
        regcache_write(map, reg, *val);

    return ret;
}

regmap_read函數最后調用bus的read函數,同理,regmap_write函數最終也會調用bus的write函數。

regmap機制中的緩存

關於緩沖,需要解釋的是,在regmap中加入了一層緩存,減少IO操作次數,提供硬件操作效率;

/* An enum of all the supported cache types */
enum regcache_type {
	REGCACHE_NONE,
	REGCACHE_RBTREE,//紅黑樹類型
	REGCACHE_COMPRESSED,//壓縮類型
	REGCACHE_FLAT,//普通數據類型
};

在Linux 4.0 版本中,已經有 3 種緩存類型,分別是數據(flat)、LZO 壓縮和紅黑樹(rbtree)。

  • 數據好理解,是最簡單的緩存類型,當設備寄存器很少時,可以用這種類型來緩存寄存器值。
  • LZO(Lempel–Ziv–Oberhumer) 是 Linux 中經常用到的一種壓縮算法,Linux 編譯后就會用這個算法來壓縮。這個算法有 3 個特性:壓縮快,解壓不需要額外內存,壓縮比可以自動調節。在這里,你可以理解為一個數組緩存,套了一層壓縮,來節約內存。當設備寄存器數量中等時,可以考慮這種緩存類型。
  • 而最后一類紅黑樹,它的特性就是索引快,所以當設備寄存器數量比較大,或者對寄存器操作延時要求低時,就可以用這種緩存類型。

緩存的類型是在 regmap 初始化時,由配置regmap_configcache_type 來指定的。

對於 regmap_read 來說,會先判斷當前緩存是否有值,然后再檢查是否需要 bypass,若沒有,則可以直接從緩存里面取值,調用regcache_read來獲取值,若需要從硬件上讀取,則調用具體協議的讀寫函數,若是 I2C,調用i2c_transfer。

寫的過程也是大同小異。

到此,在驅動程序中regmap的基本使用方法和調用簡析就結束了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM