前言
针对sn3218涉及到的dts、i2c、regmap等api函数做一些解释。
1,dts
of_get_child_count 拿到子节点的个数,方法是从根节点"\"轮训每个node,记录node个数。 for_each_child_of_node(np, child) num++; |
其他的dts api函数以后专门介绍。
2,regmap的api
使用regmap时需要的几个重要的数据结构:
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; // 如果是fast的io,用spinlock替代mutex unsigned int max_register; // 最大的寄存器的index值 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; };
led driver中调用regmap_init_i2c来依据config构建拿到一个regmap结构体。
struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config) { 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); // 用regmap_config往regmap结构体中填充 }
1,regmap_get_i2c_bus 1-1,一般的i2c返回regmap_i2c结构体. 1-2,满足val_bits == 16并且寄存器地址是reg_bits == 8,并且I2C_FUNC_SMBUS_WORD_DATA描述的以字为传输单位. 此时根据i2c设备的endian来返回不同的regmap结构体: 小端: regmap_smbus_word 大端: regmap_smbus_word_swapped 1-3,如果寄存器值val_bits是8,寄存器地址reg_bits是8,并且是I2C_FUNC_SMBUS_BYTE_DATA描述的字节传输单位. 返回regmap_smbus_byte的regmap结构体. static struct regmap_bus regmap_i2c = { // 代表regmap的i2c的bus .write = regmap_i2c_write, // 写,regmap最底层的写调用i2c bus的写 .gather_write = regmap_i2c_gather_write, // 分散写 .read = regmap_i2c_read, .reg_format_endian_default = REGMAP_ENDIAN_BIG, // reg 地址默认大端 .val_format_endian_default = REGMAP_ENDIAN_BIG, // reg 的值默认大端 }; 2, regmap_init 1,根据config内容来填充regmap结构体. 1)锁的选择 Config中有lock和unlock函数时,regmap选择config中的lock和unlock函数 否则,如果bus中有fast_io,含义是spinlock.则regmap用regmap_lock_spinlock和regmap_unlock_spinlock 否则,默认regmap用regmap_lock_mutex和regmap_unlock_mutex 2)reg_read和reg_write的选择 Bus不存在(这里是i2c 的bus),用config中的reg_read reg_write方法 否则,bus存在,用_regmap_bus_reg_read 和_regmap_bus_reg_write 函数,这些函数最终本质上调用bus里的reg_read和reg_write方法,这里是用i2c bus里的. 否则,默认只读,调用_regmap_bus_read函数. 3)根据config的reg_bits reg_shift val_bits来选择regmap的map->format.format_write描述的格式化写操作的函数, map->format.format_reg描述的格式化reg地址的函数, map->format.format_val描述的格式化val值的方法函数, map->format.parse_val描述的解析val值的方法函数, map->format.parse_inplace描述的解析替换的方法函数. 2,建立struct regmap_range_node *new描述的红黑树的节点. 先调用kzalloc分配空间, 然后调用_regmap_range_add在合适的位置插入node.
/* Return 1 if adapter supports everything we need, 0 if not. */ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) { return (func & i2c_get_functionality(adap)) == func; }
/* Return the functionality mask */ static inline u32 i2c_get_functionality(struct i2c_adapter *adap) { return adap->algo->functionality(adap); // 调用针对i2s总线驱动的functionality函数来判断其返回结果. } //一般的functionality函数,就是直接return一个结果,返回的结果是一个32位的整数,应该每一位表示一种功能,默认的每一位表示功能的含义如下: #define I2C_FUNC_I2C 0x00000001 #define I2C_FUNC_10BIT_ADDR 0x00000002 #define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_IGNORE_NAK etc. */ …. 默认情况下,是i2s总线就会支持I2C_FUNC_I2C,其他的比较高端的还会有不同功能的组合.
regmap_get_val_endian 1,从config中拿到 2,从dev的dtb node中拿到 3,从bus的default endian中拿到 默认是返回big endian
init_waitqueue_head #define init_waitqueue_head(q) \ do { \ static struct lock_class_key __key; \ \ __init_waitqueue_head((q), #q, &__key); \ } while (0) struct lockdep_subclass_key { char __one_byte; }; struct lock_class_key { struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES]; // 8 }; void __init _waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key) { spin_lock_init(&q->lock); lockdep_set_class_and_name(&q->lock, key, name); INIT_LIST_HEAD(&q->task_list); }
_regmap_range_add 利用新建的节点的描述的范围点与regmap结构体中的range_tree描述的红黑树的每个成员节点的描述的范围点来比较,找到合适插入新节点的节点. data->range_max < this->range_min data->range_min > this->range_max 利用rb_link_node插入,利用rb_insert_color使红黑树自平衡 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; unsigned int window_start; unsigned int window_len; };
regcache_init 1,错误检查,如果map->cache_type是REGCACHE_NONE,则肯定是出错的. 2, 从cache_types[]描述的缓冲类型数组中找到和map->cache_type匹配的缓冲类型. 重要数据结构: static const struct regcache_ops *cache_types[] = { ®cache_rbtree_ops, // 红黑树的cache策略,有点查找很快 ®cache_lzo_ops, // LZO 是 Lempel-Ziv-Oberhumer 的缩写。这个算法是无损算法.优点:压缩快,解压不需要额外内存。 ®cache_flat_ops, // 数组 }; 3,用config和找到的cache_type[i]来完善regmap结构体.特别的完善其regcache_ops成员 map->cache_ops = cache_types[i]; struct regcache_ops { // 来自于各自cache方法的结构体 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); }; 4,记录(暂存)当前的设备(i2c,spi等)的寄存器当前值(默认值) kmemdup(config->reg_defaults, map->num_reg_defaults *sizeof(struct reg_default), GFP_KERNEL); 对于没有default值的设备,调用regcache_hw_init获取.