前言
针对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_bus1-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_swapped1-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_init1,根据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_mutex2)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_endian1,从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_mindata->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_init1,错误检查,如果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获取.