linux regmap and regmap_config

news/2024/4/27 11:33:07/文章来源:https://blog.csdn.net/xuelin273/article/details/129152826

本文概述了regmap--它在剔除Linux子系统中的常见代码方面有多大作用,以及如何有效利用它在Linux中编写驱动程序。

Linux被划分为许多子系统,以便将不同部分的通用代码因子化,并简化驱动开发,这有助于代码维护。

Linux有一些子系统,如I2C和SPI,它们被用来连接驻扎在这些总线上的设备。这两种总线都有一个共同的功能,即从连接到它们的设备上读写寄存器。因此,读写这些寄存器、缓存寄存器的值等的代码必须存在于这两个子系统中。这导致冗余的代码存在于所有具有这种寄存器读写功能的子系统中。

为了避免这种情况的发生,并将普通的代码因素化,同时也为了方便驱动程序的维护和开发,Linux开发者从3.1版本开始引入了一个新的内核API,这个API被称为regmap。这个基础设施以前存在于Linux AsoC(ALSA)子系统中,但现在已经通过regmap API提供给整个Linux了。

早些时候,如果为驻留在SPI总线上的设备编写驱动程序,那么该驱动程序直接使用SPI子系统的SPI总线读写调用来与设备对话。现在,它使用regmap API来完成这一工作。regmap子系统负责调用SPI子系统的相关调用。因此,两个设备--一个驻留在I2C总线上,一个驻留在SPI总线上--将有相同的regmap读写调用来与总线上的各自设备对话。

这个子系统在Linux 3.1中首次引入,后来又引入了以下不同的功能。

  • 支持SPMI、MMIO
  • Spinlock和自定义锁机制
  • 缓存支持
  • 内联性转换
  • 寄存器范围检查
  • 支持IRQ
  • 只读和只写寄存器
  • 珍贵寄存器和易失性寄存器
  • 寄存器页

实现regmap
Linux中的regmap提供的API在include/linux/regmap.h中声明,在drivers/base/regmap/中实现。

在Linux的regmap中需要理解的重要数据结构

结构regmap_config

/*** 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 index.* @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; 寄存器地址位宽,必须填写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子系统使用的每个设备配置结构,用于与设备对话。它由驱动代码定义,包含所有与设备的寄存器相关的信息。它的重要字段的描述列在下面。

这是该设备寄存器中的位数,例如,如果是1字节的寄存器,它将被设置为8的值。

val_bits: 这是将在设备寄存器中设置的值的位数。
writeable_reg:这是一个写在驱动程序代码中的用户定义的函数,每当要写一个寄存器时,就会调用这个函数。每当驱动调用regmap子系统写一个寄存器时,这个驱动函数就会被调用;如果这个寄存器不可写,它将返回 "false",写操作将向驱动返回一个错误。这是一个 "每个寄存器 "的写操作回调,是可选的。
wr_table。如果驱动没有提供writeable_reg回调,那么在进行写操作之前,regmap会检查wr_table。如果寄存器地址在wr_table提供的范围内,那么就执行写操作。这也是可选的,驱动可以省略它的定义,也可以将它设置为NULL。

readable_reg: 这是一个用户定义的函数,写在驱动代码中,每当要读一个寄存器时就会调用。每当驱动调用regmap子系统读取一个寄存器时,这个驱动函数就会被调用以确保该寄存器是可读的。如果这个寄存器不可读,驱动函数将返回 "false",读操作将向驱动返回一个错误。这是一个 "每个寄存器 "的读操作回调,是可选的。

rd_table: 如果一个驱动没有提供readable_reg回调,那么在进行读操作之前,regmap会检查rd_table。如果寄存器地址在rd_table提供的范围内,那么读操作就被执行。这也是可选的,驱动程序可以省略它的定义,可以将它设置为NULL。
volatile_reg: 这是一个写在驱动代码中的用户定义的函数,每当通过缓存写入或读出一个寄存器时就会调用这个函数。每当驱动通过regmap缓存读取或写入一个寄存器时,这个函数首先被调用,如果它返回 "false",才会使用缓存方法;否则,寄存器被直接写入或读取,因为寄存器是易失性的,不需要使用缓存。这是一个 "每个寄存器 "的操作回调,是可选的。

volatile_table: 如果一个驱动没有提供volatile_reg回调,那么volatile_table就会被regmap检查,看寄存器是否是volatile的。如果寄存器地址在volatile_table提供的范围内,则不使用缓存操作。这也是可选的,驱动程序可以省略它的定义,也可以将它设置为NULL。

锁定。这是一个用户定义的回调函数,写在驱动代码中,在开始任何读或写操作之前被调用。该函数应该接受一个锁,并返回它。这是一个可选的函数--如果不提供,regmap将提供自己的锁定机制。

解锁。这是用户定义的回调,写在驱动代码中,用于解锁,它是由锁例程创建的。这是可选的,如果不提供,将由regmap的内部锁定机制取代。

lock_arg: 这是传递给锁定和解锁回调例程的参数。
fast_io。如果没有提供自定义的锁和解锁机制,regmap内部使用mutex来锁和解锁。如果驱动希望regmap使用自旋锁,那么fast_io应该设置为 "true";否则,regmap将使用基于mutex的锁。

max_register。每当要执行任何读或写操作时,regmap首先检查寄存器地址是否小于max_register,只有当它是,才执行操作。 max_register如果被设置为0,则被忽略。

read_flag_mask。通常,在SPI或I2C中,一个写或读将在最高字节中设置最高位,以区分写和读操作。这个掩码被设置在寄存器值的较高字节中。
write_flag_mask。这个掩码也被设置在寄存器值的较高字节中。

这些是结构regmap_config的字段,这个配置被传递给regmap_init,它创建结构regmap并返回可以在读写操作中使用的内容。
wr_table, rd_table和volatile_table是可选的表(仅在没有提供相应的回调时使用),在写和读操作时被regmap用来进行范围检查。这些被实现为regmap_access_table结构。以下是它的字段。
yes_ranges:这些是被认为是有效范围的地址范围。
n_yes_ranges:这是yes_ranges中的条目数。
no_ranges:这些是被认为是无效范围的地址范围。
n_no_ranges:这是no_ranges中的条目数。


struct 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 */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_FSstruct 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;
#endifunsigned 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);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;/* 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 */u32 cache_only;/* if set, only the HW is modified not the cache */u32 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;u32 cache_dirty;struct reg_default *patch;int patch_regs;/* if set, converts bulk rw to single rw */bool use_single_rw;/* if set, the device supports multi write mode */bool can_multi_write;struct rb_root range_tree;void *selector_work_buf;	/* Scratch buffer used for selector */
};

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;elsemap->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_i2c

当设备驱动配置好config以后,调用对应的regmap_init_xx,例如,这里是regmap_init_i2c

// drivers/base/regmap/regmap-i2c.cstruct 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 APIs
regmap的API在include/linux/regmap.h中声明,以下是重要API的细节。
初始化例程。下面的例程根据SPI配置初始化regmap数据结构。

struct regmap * devm_regmap_init_spi(struct spi_device *spi, const struct regmap_config)。

下面的例程根据I2C配置初始化regmap数据结构。

struct regmap * devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config)。

在regmap初始化例程中,摄取regmap_config配置;然后分配regmap结构并将配置复制到其中。各个总线的读/写功能也被复制到regmap结构中。例如,在SPI总线的情况下,regmap的读写功能指针将指向SPI的读写功能。
regmap初始化后,驱动程序可以使用以下例程与设备对话。

int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)。

int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)。

regmap_write。这个函数用来向设备写数据。它接收初始化期间返回的regmap结构,注册地址和要设置的值。以下是regmap_write例程执行的步骤。

首先,regmap_write接收锁,如果regmap_config中的fast_io被设置,它将是spinlock;否则,它将是mutex。

接下来,如果在regmap_config中设置了max_register,那么它将检查传递的寄存器地址是否小于max_register。如果小于max_register,那么只执行写操作;否则,返回-EIO(无效的I/O)。

之后,如果在regmap_config中设置了writeable_reg回调,那么将调用该回调。如果该回调返回 "true",则进行进一步操作;如果它返回 "false",则返回错误-EIO。这个步骤只有在设置了writeable_reg的情况下才会执行。

如果没有设置writeable_reg,但是设置了wr_table,那么会检查寄存器地址是否位于no_ranges,在这种情况下会返回-EIO错误;否则,会检查它是否位于yes_ranges。如果它不在那里,那么就会返回-EIO错误,并且操作被终止。如果它在yes_ranges中,那么将执行进一步的操作。这一步只有在wr_table被设置的情况下才会执行,否则就会跳过这一步。

现在检查是否允许缓存。如果允许,那么寄存器的值将被缓存,而不是直接写入硬件,并且操作在这一步完成。如果不允许缓存,则进入下一个步骤。缓存将在后面讨论。

在调用这个硬件写入例程将值写入硬件寄存器后,这个函数将写入_flag_mask到值的第一个字节,值被写入设备。
完成写操作后,写之前的锁被释放,函数返回。

regmap_read。这个函数用于从设备上读取数据。它接收初始化期间返回的regmap结构,并注册要读取数据的地址和值指针。在寄存器读取过程中,将执行以下步骤。
首先,读函数在执行读操作之前会被锁定。如果在regmap_config中设置了fast_io,这将是一个自旋锁;否则,regmap将使用mutex。
接下来,它将检查传递的寄存器地址是否小于max_register;如果不是,那么将返回-EIO。这一步只在max_register被设置为大于0时进行。
然后,它将检查readable_reg回调是否被设置。如果是,则调用该回调,如果该回调返回'false',则读取操作被终止,返回-EIO错误。如果这个回调返回 "true",那么进一步的操作将被执行。

接下来要检查的是寄存器地址是否在配置中rd_table的no_ranges范围内。如果是,那么将返回一个-EIO错误。如果它既不在no_ranges也不在yes_ranges中,那么也会返回-EIO错误。只有当它位于yes_ranges中时,才能进行进一步的操作。这个步骤只有在设置了rd_table的情况下才会执行。
现在,如果允许缓存,那么就从缓存中读取寄存器的值,函数返回被读取的值。如果缓存被设置为旁路,那么将执行下一步。
在采取上述步骤后,调用硬件读取操作来读取寄存器的值,并且用返回的值来更新被传递的变量的值。
在开始这个操作之前的锁现在被释放,函数返回。

编写一个基于regmap的驱动
让我们试着写一个基于regmap框架的驱动程序。
让我们假设有一个设备X连接在SPI总线上,它有以下属性。

  • 8位寄存器地址
  • 8位寄存器值
  • 0x80作为写入掩码
  • 它是一个快速I/O设备,所以应该使用自旋锁
  • 有效的地址范围。
    1. 0x20到0x4F
    2. 0x60到0x7F
    驱动程序可以按以下方式编写。
//include other include files
#include <linux/regmap.h>static struct custom_drv_private_struct
{//other fields relevant to devicestruct regmap *map;
};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 custom_drv_probe(struct spi_device *dev)
{struct regmap_config config;struct custom_drv_private_struct *priv;unsigned int data; //configure the regmap configurationmemset(&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;//only set below two things if  //writeable_reg//and readable_reg is not set//config.wr_table = drv_wr_table;//config.rd_table = drv_rd_table;//allocate the private data structures as//priv = devm_kzalloc //Init the regmap spi configurationpriv->map = devm_regmap_init_spi(dev,             &config); //devm_regmap_init_i2c in case of i2c bus 
//following operation will remain same in
//case of both i2c and spi or other bus            
//read from the device, data variable will //contain device data
regmap_read(priv->map, 0x23, &data); 
data = 0x24;
//write to the device
regmap_write(priv->map, 0x23, data); 
if(regmap_read(priv->map, 0x85, &data)
< 0){///error since address is out of range} return 0;
}

在上面的例子中,你可以看到基于不同总线子系统的驱动程序中的冗余代码如何变成类似的代码,这使得驱动程序的编写和维护更加容易。在当前的regmap子系统中也有缓存支持。

缓存避免了直接在设备上执行操作。相反,它缓存了在设备和驱动之间传输的值,并将其作为未来的参考。最初,缓存只使用平面数组,这对32位地址不利。后来,这个问题通过更好的缓存类型得到了解决。

  • rbtree将连续的寄存器块存储在一个红/黑树中
  • 压缩存储压缩的数据块
    两者都依赖于现有的内核库。

enum regcache_type cache_type。

对tracepoint的支持在regmap中可用。更多信息,见debugfs/trace/events/regmap。

regmap_reg_write 0-001b reg=3b val=1a

regmap_reg_read 0-001b reg=1d val=1d

你也可以定义LOG_DEVICE用于早期启动日志

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_72586.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web前端:全栈开发人员的责任

多年来&#xff0c;关于全栈开发人员有很多说法&#xff0c;全栈开发人员是一位精通应用程序全栈开发过程的专业人士。这包括数据库、API、前端技术、后端开发语言和控制系统版本。你一定遇到过前端和后端开发人员。前端开发人员将构建接口&#xff0c;而后端开发人员将开发、更…

狂神说:方法

何为方法方法是语句和集合&#xff0c;一起执行一个功能【实际上方法就是函数&#xff0c;说法不一样而已】定义方法加了static才能被main方法调用修饰符&#xff08;public static&#xff09; 返回类型 方法名&#xff08;参数类型 参数名&#xff09;// main方法public stat…

vscode SSH 保存密码自动登录服务器

先在win local上拿到秘钥&#xff0c;然后再把这秘钥copy 进服务器 1. 创建 RSA 密钥对 第一步是在客户端机器&#xff08;通常是您的计算机 win 10&#xff09;上创建密钥对&#xff1a;打开powershell, 输入 ssh-keygen默认情况下ssh-keygen将创建一个 2048 位 RSA 密钥对…

“双碳”目标下二氧化碳地质封存技术应用前景及模型构建实践方法与讨论

我国二氧化碳地质封存技术起步较晚&#xff0c;目前仍没有一套相对完整的行业规范&#xff1b;且就该技术而言&#xff0c;涉及环节众多&#xff0c;理论相对复杂&#xff0c;对于行业的新入局者不太友好。因此&#xff0c;结合时代背景&#xff0c;我们首次尝试对二氧化碳地质…

nodejs出现require is not defined和__dirname is not define 错误

参阅此&#xff0c; Cesium环境搭建成功和初步看一下它的示例_bcbobo21cn的博客-CSDN博客 运行Cesium入门示例&#xff0c;出现下图错误&#xff0c;根据资料&#xff0c;这是node版本的问题&#xff1b; 解决方法是&#xff0c;在server.js头部加入&#xff0c; import { cre…

Flink04: Flink核心API之DataStream

一、Flink 4种不同层次的API Flink中提供了4种不同层次的API&#xff0c;每种API在简洁和易表达之间有自己的权衡&#xff0c;适用于不同的场景。目前上面3个会用得比较多。 • 低级API(Stateful Stream Processing)&#xff1a;提供了对时间和状态的细粒度控制&#x…

Endless lseek导致的SQL异常

最近碰到同事咨询的一个问题&#xff0c;在执行一个函数时&#xff0c;发现会一直卡在那里。 strace抓了下发现会话一直在执行lseek&#xff0c;大致情况如下&#xff1a; 16:13:55.451832 lseek(33, 0, SEEK_END) 1368064 <0.000037> 16:13:55.477216 lseek(33, 0, SE…

linux下安装mongoDB

一、下载mongoDB包 下载地址&#xff1a; https://www.mongodb.com/try/download/community 个人建议&#xff1a;如果是学习阶段&#xff0c;使用5以下版本更好些。 二、安装及配置 1、安装 # 1、解压 $ tar -zxvf mongodb-linux-x86_64-rhel70-4.4.19-rc1.tgz# 2、迁移目…

【音视频处理】为什么MP3不是无损音乐?音频参数详解,码率、采样率、音频帧、位深度、声道、编码格式的关系

大家好&#xff0c;欢迎来到停止重构的频道。上期我们讨论了视频的相关概念&#xff0c;本期我们讨论音频的相关概念。包括采样率、码率、单双声道、音频帧、编码格式等概念。这里先抛出一个关于无损音频的问题。为什么48KHz采样率的.mp3不是无损音乐 &#xff0c;而48KHz采样率…

高性能爬虫之单线程、多进程、多线程的使用,线程池、进程池、协程池的使用

目录一、单线程爬虫代码实现二、 多线程爬虫1、多线程的方法使用2、队列模块的使用3、多线程实现思路剖析4、代码实现**注意点&#xff1a;**三、多进程爬虫1、多进程程的方法使用2、多进程中队列的使用3 代码实现**小结**四、线程池实现爬虫1、线程池使用方法介绍2、使用线程池…

365天深度学习训练营-第J3周:DenseNet算法实战与解析

目录 一、前言 二、论文解读 1、DenseNet的优势 2、设计理念 3、网络结构 4、与其他算法进行对比 三、代码复现 1、使用Pytorch实现DenseNet 2、使用Tensorflow实现DenseNet网络 四、分析总结 一、前言 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习…

基于OSG的虚拟校园系统的设计与实现

基于open scene graph的虚拟校园系统的设计与实现 摘要 •引言 • OSG基本原理 •OSG操作与动画 •视点的定位和切换 •自由漫游 •路径漫游 • 路径动画 • 点选和文字 • 粒子系统 • 3DMAX • 无线通信与数据库设计 • 实现步骤 • 结论 摘要 随着科技的不断发展,人工智能&a…

DO-254 和 DO-178B的区别(文末有易灵思核心板及配套下载线)

DO-178B介绍 DO-178B&#xff0c;机载系统和设备认证中的软件考虑&#xff0c;在电子硬件被要求符合 DO-254 之前多年就已发布和采纳。DO-178B 的先行一步对电子硬件开发带来两个特别的后果。首先&#xff0c;使得硬件制造商有了一种倾向&#xff0c;为了避免 DO-178B 对软件开…

【MySQL】sql中explain解释和应用

这里写目录标题学习原因MySQL中explain的使用和用法解释explain的使用explain 运行结果的意义文字展示表格展示参考资料&#xff1a;结束语学习原因 在对sql的优化过程中使用了explain对指定的sql进行查看它的运行效果&#xff0c;以便找出sql的性能特点并进行优化 MySQL中ex…

Linux - POSIX信号量,基于环形队列的生产者消费者模型

信号量在Linux下&#xff0c;POSIX信号量是一种线程同步机制&#xff0c;用于控制多个线程之间的访问顺序。POSIX信号量可以用于实现线程之间的互斥或者同步。在之前的阻塞队列生产者消费者模型中&#xff0c;阻塞队列是一个共享资源&#xff0c;不管是生产者还是消费者&#x…

Mysql实战之日志系统:一条SQL更新语句是如何执行的

1.前言 上一篇咱们了解了MySQL 的执行过程&#xff0c;其中设计连接器、分析器、优化器、执行器和存储引擎&#xff0c;接下来我将给大家讲解一下在MySQL中一条更新语句是如何执行。我相信大家可能听公司的DBA提起过&#xff0c;可以将数据恢复到半个月内任意时间的状态&#…

Scala集合详解(第七章:集合、数组、列表、set集合、map集合、元组、队列、并行)(尚硅谷笔记)

集合第七章:集合7.1 集合简介7.1.1 不可变集合继承图7.1.2 可变集合继承图7.2 数组7.2.1 不可变数组7.2.2 可变数组7.2.3 不可变数组与可变数组的转换7.2.4 多维数组7.3 列表 List7.3.1 不可变 List7.3.2 可变 ListBuffer7.4 Set 集合7.4.1 不可变 Set7.4.2 可变 mutable.Set7.…

Android system实战 — Android R(11) 进程保活白名单

Android system实战 — Android R 进程保活白名单0. 前言1. 具体实现1.1 准备工作1.2 源码实现1.2.1 源码1.2.2 diff文件0. 前言 最近在Android R上实现一些需求&#xff0c;进行记录一下&#xff0c;关于进程保活的基础知识可以参考Android system — 进程生命周期与ADJ&#…

自动驾驶路径规划概况

文章目录前言介绍1. 路径规划在自动驾驶系统架构中的位置2. 全局路径规划的分类2.1 基础图搜索算法2.1.1 Dijkstra算法2.1.2 双向搜索算法2.1.3 Floyd算法2.2 启发式算法2.2.1 A*算法2.2.2 D*算法2.3 基于概率采样的算法2.3.1 概率路线图&#xff08;PRM&#xff09;2.3.2 快速…

蓝牙运动耳机什么牌子的好、运动蓝牙耳机排行榜推荐

近些年&#xff0c;户外运动兴起&#xff0c;运动耳机迎来爆发增长&#xff0c;拒绝运动乏味&#xff0c;追求健康运动方式&#xff0c;已经成为当下年轻人的共同诉求。跑步骑行听音乐&#xff0c;已经是运动爱好者再熟悉不过的操作&#xff0c;很多人在运动中离不开音乐的节奏…