[linux 驱动]regmap子系统详解与实战

目录

 

1 描述

2 结构体

2.1 regmap

2.2 regmap_bus

2.3 regmap_config

3 regmap 操作函数

3.1 regmap 申请与初始化

3.1.1 regmap_init_i2c

3.1.2 regmap_init_spi

3.1.3 regmap_exit

3.2 regmap 设备访问 API 函数

3.2.1 regmap_read

3.2.2 regmap_write

4 示例


 

1 描述

        Linux 内核引入了 regmap 模型,regmap 将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用 SPI 或者 I2C 接口 API 函数,统一使用 regmapAPI 函数。这样的好处就是统一使用 regmap,降低了代码冗余,提高了驱动的可以移植性。regmap 模型的重点在于:

        通过 regmap 模型提供的统一接口函数来访问器件的寄存器,SOC 内部的寄存器也可以使用 regmap 接口函数来访问。

        regmap 是 Linux 内核为了减少慢速 I/O 在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。另外,regmap 在驱动和硬件之间添加了 cache,降低了低速 I/O 的操作次数,提高了访问效率,缺点是实时性会降低。

        regmap 框架结构如下图所示。

 

9272d96ec03618c44a209d495c1657fa.png

2 结构体

2.1 regmap

 52 struct regmap {
 53         union {
 54                 struct mutex mutex;
 55                 struct {
 56                         spinlock_t spinlock;
 57                         unsigned long spinlock_flags;
 58                 };
 59         };
 60         regmap_lock lock;
 61         regmap_unlock unlock;
 62         void *lock_arg; /* This is passed to lock/unlock functions */
 63         gfp_t alloc_flags;
 64 
 65         struct device *dev; /* Device we do I/O on */
 66         void *work_buf;     /* Scratch buffer used to format I/O */
 67         struct regmap_format format;  /* Buffer format */
 68         const struct regmap_bus *bus;
 69         void *bus_context;
 70         const char *name;
 71 
 72         bool async;
 73         spinlock_t async_lock;
 74         wait_queue_head_t async_waitq;
 75         struct list_head async_list;
 76         struct list_head async_free;
 77         int async_ret;
 78 
 79 #ifdef CONFIG_DEBUG_FS
 80         bool debugfs_disable;
 81         struct dentry *debugfs;
 82         const char *debugfs_name;
 83 
 84         unsigned int debugfs_reg_len;
 85         unsigned int debugfs_val_len;
 86         unsigned int debugfs_tot_len;
 87 
 88         struct list_head debugfs_off_cache;
 89         struct mutex cache_lock;
 90 #endif
 91 
 92         unsigned int max_register;
 93         bool (*writeable_reg)(struct device *dev, unsigned int reg);
 94         bool (*readable_reg)(struct device *dev, unsigned int reg);
 95         bool (*volatile_reg)(struct device *dev, unsigned int reg);
 96         bool (*precious_reg)(struct device *dev, unsigned int reg);
 97         bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
 98         const struct regmap_access_table *wr_table;
 99         const struct regmap_access_table *rd_table;
100         const struct regmap_access_table *volatile_table;
101         const struct regmap_access_table *precious_table;
102         const struct regmap_access_table *rd_noinc_table;
103 
104         int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
105         int (*reg_write)(void *context, unsigned int reg, unsigned int val);
106         int (*reg_update_bits)(void *context, unsigned int reg,
107                                unsigned int mask, unsigned int val);
108 
109         bool defer_caching;
110 
111         unsigned long read_flag_mask;
112         unsigned long write_flag_mask;
113 
114         /* number of bits to (left) shift the reg value when formatting*/
115         int reg_shift;
116         int reg_stride;
117         int reg_stride_order;
118 
119         /* regcache specific members */
120         const struct regcache_ops *cache_ops;
121         enum regcache_type cache_type;
122 
123         /* number of bytes in reg_defaults_raw */
124         unsigned int cache_size_raw;
125         /* number of bytes per word in reg_defaults_raw */
126         unsigned int cache_word_size;
127         /* number of entries in reg_defaults */
128         unsigned int num_reg_defaults;
129         /* number of entries in reg_defaults_raw */
130         unsigned int num_reg_defaults_raw;
131 
132         /* if set, only the cache is modified not the HW */
133         bool cache_only;
134         /* if set, only the HW is modified not the cache */
135         bool cache_bypass;
136         /* if set, remember to free reg_defaults_raw */
137         bool cache_free;
138 
139         struct reg_default *reg_defaults;
140         const void *reg_defaults_raw;
141         void *cache;
142         /* if set, the cache contains newer data than the HW */
143         bool cache_dirty;
144         /* if set, the HW registers are known to match map->reg_defaults */
145         bool no_sync_defaults;
146 
147         struct reg_sequence *patch;
148         int patch_regs;
149 
150         /* if set, converts bulk read to single read */
151         bool use_single_read;
152         /* if set, converts bulk read to single read */
153         bool use_single_write;
154         /* if set, the device supports multi write mode */
155         bool can_multi_write;
156 
157         /* if set, raw reads/writes are limited to this size */
158         size_t max_raw_read;
159         size_t max_raw_write;
160 
161         struct rb_root range_tree;
162         void *selector_work_buf;        /* Scratch buffer used for selector */
163 
164         struct hwspinlock *hwlock;
165 };

2.2 regmap_bus

        regmap_bus 的结构体,它用于描述一种与寄存器映射(regmap)交互的硬件总线或接口的属性和功能。在嵌入式系统或硬件驱动开发中,寄存器映射是一种常见的技术,用于通过内存地址空间访问硬件设备的寄存器。这种方式使得软件可以像操作内存一样来读写硬件的寄存器,从而控制硬件设备。

        regmap_bus结构定义了读写函数默认的寄存器地址寄存器值的大小端

492 struct regmap_bus {             
 493         bool fast_io;
 494         regmap_hw_write write; 
 495         regmap_hw_gather_write gather_write;
 496         regmap_hw_async_write async_write;
 497         regmap_hw_reg_write reg_write;
 498         regmap_hw_reg_update_bits reg_update_bits;
 499         regmap_hw_read read;
 500         regmap_hw_reg_read reg_read;
 501         regmap_hw_free_context free_context;
 502         regmap_hw_async_alloc async_alloc;
 503         u8 read_flag_mask;
 504         enum regmap_endian reg_format_endian_default;
 505         enum regmap_endian val_format_endian_default;
 506         size_t max_raw_read;         
 507         size_t max_raw_write;        
 508 };

bool fast_io;:一个布尔值,指示是否使用快速I/O模式。这可能会影响读写操作的性能和方式。

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;:一个无符号8位整数,用作读操作时的标志掩码,可能用于控制读操作的某些方面。

enum regmap_endian reg_format_endian_default;:一个枚举类型,表示寄存器格式(即寄存器在硬件中的存储方式)的默认字节序(大端或小端)。

enum regmap_endian val_format_endian_default;:一个枚举类型,表示值格式(即写入或读取到寄存器的值的存储方式)的默认字节序。

size_t max_raw_read; 和 size_t max_raw_write;:分别表示在一次操作中能够读取或写入的最大原始数据量(以字节为单位)。这对于了解硬件的限制和优化数据传输效率很重要。

2.3 regmap_config

        在regmap_config,定义了寄存器的各种信息,比如寄存器地址长度寄存器值的长度读写寄存器的地址范围的信息,寄存器地址和值的大小端以及缓冲方式。

343 struct regmap_config {
 344         const char *name;
 345 
 346         int reg_bits;
 347         int reg_stride;
 348         int pad_bits;
 349         int val_bits;
 350 
 351         bool (*writeable_reg)(struct device *dev, unsigned int reg);
 352         bool (*readable_reg)(struct device *dev, unsigned int reg);
 353         bool (*volatile_reg)(struct device *dev, unsigned int reg);
 354         bool (*precious_reg)(struct device *dev, unsigned int reg);
 355         bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
 356 
 357         bool disable_locking;
 358         regmap_lock lock;
 359         regmap_unlock unlock;
 360         void *lock_arg;
 361 
 362         int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
 363         int (*reg_write)(void *context, unsigned int reg, unsigned int val);
 364 
 365         bool fast_io;
 366 
 367         unsigned int max_register;
 368         const struct regmap_access_table *wr_table;
 369         const struct regmap_access_table *rd_table;
 370         const struct regmap_access_table *volatile_table;
 371         const struct regmap_access_table *precious_table;
 372         const struct regmap_access_table *rd_noinc_table;
 373         const struct reg_default *reg_defaults;
 374         unsigned int num_reg_defaults;
 375         enum regcache_type cache_type;
 376         const void *reg_defaults_raw;
 377         unsigned int num_reg_defaults_raw;
 378 
 379         unsigned long read_flag_mask;
 380         unsigned long write_flag_mask;
 381         bool zero_flag_mask;
 382 
 383         bool use_single_rw;
 384         bool can_multi_write;
 385 
 386         enum regmap_endian reg_format_endian;
 387         enum regmap_endian val_format_endian;
 388 
 389         const struct regmap_range_cfg *ranges;
 390         unsigned int num_ranges;
 391 
 392         bool use_hwlock;
 393         unsigned int hwlock_id;
 394         unsigned int hwlock_mode;
 395 };

3 regmap 操作函数

3.1 regmap 申请与初始化

3.1.1 regmap_init_i2c

函数原型

#define regmap_init_i2c(i2c, config) \

__regmap_lockdep_wrapper(__regmap_init_i2c, #config, \

i2c, config)

参数

struct i2c_client *i2c

这是指向 I2C 设备的指针,通常是一个 struct i2c_client 类型。

struct regmap_config *config

这是一个配置结构体,包含有关寄存器映射的配置信息,比如寄存器地址、数据格式等。

返回值

 

 

功能

用于初始化 I2C 设备的寄存器映射

        当设备驱动配置好config以后,调用对应的regmap_init_i2c

281 struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
282                                  const struct regmap_config *config,
283                                  struct lock_class_key *lock_key,
284                                  const char *lock_name)
285 {                        
286         const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
287 
288         if (IS_ERR(bus))
289                 return ERR_CAST(bus);
290 
291         return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
292                              lock_key, lock_name);
293 }             
662 #define regmap_init_i2c(i2c, config)                                    \
 663         __regmap_lockdep_wrapper(__regmap_init_i2c, #config,            \
 664                                 i2c, config)
620 #ifdef CONFIG_LOCKDEP
 621 #define __regmap_lockdep_wrapper(fn, name, ...)                         \
 622 (                                                                       \
 623         ({                                                              \
 624                 static struct lock_class_key _key;                      \
 625                 fn(__VA_ARGS__, &_key,                                  \
 626                         KBUILD_BASENAME ":"                             \
 627                         __stringify(__LINE__) ":"                       \
 628                         "(" name ")->lock");                            \
 629         })                                                              \
 630 )
 631 #else
 632 #define __regmap_lockdep_wrapper(fn, name, ...) fn(__VA_ARGS__, NULL, NULL)
 633 #endif
281 struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
282                                  const struct regmap_config *config,
283                                  struct lock_class_key *lock_key,
284                                  const char *lock_name)
285 {
286         const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
287 
288         if (IS_ERR(bus))
289                 return ERR_CAST(bus);
290 
291         return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
292                              lock_key, lock_name);
293 }

 

253 static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
254                                         const struct regmap_config *config)
255 {
256         if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
257                 return &regmap_i2c;
258         else if (config->val_bits == 8 && config->reg_bits == 8 &&
259                  i2c_check_functionality(i2c->adapter,
260                                          I2C_FUNC_SMBUS_I2C_BLOCK))
261                 return &regmap_i2c_smbus_i2c_block;
262         else if (config->val_bits == 16 && config->reg_bits == 8 &&
263                  i2c_check_functionality(i2c->adapter,
264                                          I2C_FUNC_SMBUS_WORD_DATA))
265                 switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
266                 case REGMAP_ENDIAN_LITTLE:
267                         return &regmap_smbus_word;
268                 case REGMAP_ENDIAN_BIG:
269                         return &regmap_smbus_word_swapped;
270                 default:                /* everything else is not supported */
271                         break;
272                 }
273         else if (config->val_bits == 8 && config->reg_bits == 8 &&
274                  i2c_check_functionality(i2c->adapter,
275                                          I2C_FUNC_SMBUS_BYTE_DATA))
276                 return &regmap_smbus_byte;
277 
278         return ERR_PTR(-ENOTSUPP);
279 }

3.1.2 regmap_init_spi

701 #define regmap_init_spi(dev, config)                                    \
 702         __regmap_lockdep_wrapper(__regmap_init_spi, #config,            \
 703                                 dev, config)

3.1.3 regmap_exit

        不管是什么接口,全部使用 regmap_exit 这个函数来释放 regmap。

1310 void regmap_exit(struct regmap *map)
1311 {
1312         struct regmap_async *async;
1313 
1314         regcache_exit(map);
1315         regmap_debugfs_exit(map);
1316         regmap_range_exit(map);
1317         if (map->bus && map->bus->free_context)
1318                 map->bus->free_context(map->bus_context);
1319         kfree(map->work_buf);
1320         while (!list_empty(&map->async_free)) {
1321                 async = list_first_entry_or_null(&map->async_free,
1322                                                  struct regmap_async,
1323                                                  list);
1324                 list_del(&async->list);
1325                 kfree(async->work_buf);
1326                 kfree(async);
1327         }
1328         if (map->hwlock)
1329                 hwspin_lock_free(map->hwlock);
1330         kfree_const(map->name);
1331         kfree(map->patch);
1332         kfree(map);
1333 }

3.2 regmap 设备访问 API 函数

        不管是 I2C 还是 SPI 等接口,还是 SOC 内部的寄存器,对于寄存器的操作就两种:读和写。regmap 提供了最核心的两个读写操作:regmap_read 和 regmap_write。这两个函数分别用来读/写寄存器。

3.2.1 regmap_read

函数原型

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

参数

struct regmap *map

regmap结构体指针

unsigned int reg

读取的寄存器地址

unsigned int *val

存储读取的数据的指针

返回值

int

成功:0 失败:负数

功能

从设备的寄存器中读取数据。regmap框架提供了一种统一的方式来访问设备的寄存器,无论这些寄存器是通过i2c、spi、内存映射还是其他方式访问的

2472 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
2473 {      
2474         int ret;
2475        
2476         if (!IS_ALIGNED(reg, map->reg_stride))
2477                 return -EINVAL;
2478        
2479         map->lock(map->lock_arg);
2480 
2481         ret = _regmap_read(map, reg, val);
2482 
2483         map->unlock(map->lock_arg);
2484 
2485         return ret;
2486 }
2428 static int _regmap_read(struct regmap *map, unsigned int reg,
2429                         unsigned int *val)
2430 {
2431         int ret;
2432         void *context = _regmap_map_get_context(map);
2433 
2434         if (!map->cache_bypass) {
2435                 ret = regcache_read(map, reg, val);
2436                 if (ret == 0)
2437                         return 0;
2438         }
2439 
2440         if (map->cache_only)
2441                 return -EBUSY;
2442 
2443         if (!regmap_readable(map, reg))
2444                 return -EIO;
2445 
2446         ret = map->reg_read(context, reg, val);
2447         if (ret == 0) {
2448 #ifdef LOG_DEVICE
2449                 if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
2450                         dev_info(map->dev, "%x => %x\n", reg, *val);
2451 #endif
2452 
2453                 trace_regmap_reg_read(map, reg, *val);
2454 
2455                 if (!map->cache_bypass)
2456                         regcache_write(map, reg, *val);
2457         }
2458 
2459         return ret;
2460 }

3.2.2 regmap_write

函数原型

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

参数

struct regmap *map

regmap结构体指针

unsigned int reg

写的寄存器地址

unsigned int val

写寄存器的值

返回值

int

成功:0 失败:负数

功能

从设备的寄存器中写数据。regmap框架提供了一种统一的方式来访问设备的寄存器,无论这些寄存器是通过i2c、spi、内存映射还是其他方式访问的

1773 int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
1774 {
1775         int ret;
1776 
1777         if (!IS_ALIGNED(reg, map->reg_stride))
1778                 return -EINVAL;
1779 
1780         map->lock(map->lock_arg);
1781 
1782         ret = _regmap_write(map, reg, val);
1783 
1784         map->unlock(map->lock_arg);
1785 
1786         return ret;
1787 }
1734 int _regmap_write(struct regmap *map, unsigned int reg,
1735                   unsigned int val)
1736 {
1737         int ret;
1738         void *context = _regmap_map_get_context(map);
1739 
1740         if (!regmap_writeable(map, reg))
1741                 return -EIO;
1742 
1743         if (!map->cache_bypass && !map->defer_caching) {
1744                 ret = regcache_write(map, reg, val);
1745                 if (ret != 0)
1746                         return ret;
1747                 if (map->cache_only) {
1748                         map->cache_dirty = true;
1749                         return 0;
1750                 }
1751         }
1752 
1753 #ifdef LOG_DEVICE
1754         if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
1755                 dev_info(map->dev, "%x <= %x\n", reg, val);
1756 #endif
1757 
1758         trace_regmap_reg_write(map, reg, val);
1759 
1760         return map->reg_write(context, reg, val);
1761 }

4 示例

40 enum regcache_type {
  41         REGCACHE_NONE,
  42         REGCACHE_RBTREE,
  43         REGCACHE_COMPRESSED,
  44         REGCACHE_FLAT, 
  45 };     
#define MCU_REG_MAX									100

static bool mcu_is_volatile_reg(struct device *dev, unsigned int reg)
{       
        switch (reg) {
        case MCU_VID:
        case MCU_PID:
        case MCU_VERSION_MAJOR:
        case MCU_VERSION_MINOR:
        case MCU_POWER_LOSS:
        case MCU_RTC_WAKE:
        case MCU_WOL_WAKE:
        case MCU_WDT_TIMOUT:
        case MCU_WDT_CONTROL:
             return true;
        }
        
        return false;
}

static const struct regmap_config mcu_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = MCU_REG_MAX,
        .cache_type = REGCACHE_NONE,
        .volatile_reg = mcu_is_volatile_reg,
};

static int  spirit_mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret = 0;
	struct spirit_mcu *spirit_mcu;
	struct device_node *np = client->dev.of_node;

	spirit_mcu = devm_kzalloc(&client->dev, sizeof(struct spirit_mcu), GFP_KERNEL);
	if (!spirit_mcu)
		return -ENOMEM;


    spirit_mcu->regmap = devm_regmap_init_i2c(client, &mcu_regmap_config);
    if (IS_ERR(spirit_mcu->regmap)) {
            dev_err(&client->dev, "regmap initialization failed\n");
            return PTR_ERR(spirit_mcu->regmap);
    }
	spirit_mcu->userfeed = 0;
	i2c_set_clientdata(client, spirit_mcu);
	spirit_mcu->i2c = client;
	spirit_mcu->np = np;
	mcu_i2c_client = client;


	ret = regmap_read(spirit_mcu->regmap, MCU_VID, &spirit_mcu->deviceInfo.vid);
	if (ret) {
		dev_err(&client->dev, "read 0x%x failed\n", MCU_VID);
		//return ret;
	}
    
    return 0;
}


static const struct i2c_device_id spirit_mcu_id[] = {
	{ "spirit_mcu", 0 },
	{ }
};

static struct i2c_driver spirit_mcu_driver = {
	.driver		= {
		.name	= "spirit_mcu",
		.owner	= THIS_MODULE,
	},
	.probe		= spirit_mcu_probe,
 	.id_table	= spirit_mcu_id,
};

static int __init spirit_mcu_init(void)
{
	return i2c_add_driver(&spirit_mcu_driver);
}

static void __exit spirit_mcu_exit(void)
{
	unregister_reboot_notifier(&mcu_reboot_notifier);
	i2c_del_driver(&spirit_mcu_driver);
}

MODULE_AUTHOR("neilnee@jwele.com.cn");
MODULE_DESCRIPTION("spirit mcu driver");
MODULE_LICENSE("GPL");

late_initcall(spirit_mcu_init);
module_exit(spirit_mcu_exit);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值