lwn拾遗:[sn3218 led drivers]-api解释-1

前言

针对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[] = {
	&regcache_rbtree_ops,    //  红黑树的cache策略,有点查找很快
	&regcache_lzo_ops,       //  LZO 是 Lempel-Ziv-Oberhumer 的缩写。这个算法是无损算法.优点:压缩快,解压不需要额外内存。
	&regcache_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获取.



你可能感兴趣的:(linux,api,driver,led,i2c,regmap)