博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
lwn拾遗:[sn3218 led driver]-api解释-2
阅读量:6705 次
发布时间:2019-06-25

本文共 9232 字,大约阅读时间需要 30 分钟。

hot3.png

regcache_hw_init有些i2c设备的寄存器是volatile的,是不能cache的.1,先计算不是volatile类型的寄存器个数轮询0到map->num_reg_defaults_raw,并用regmap_volatile判断当前的寄存器是否是volatile的.记录下不是volatile的寄存器的个数.如果全部寄存器都是volatile的,那直接返回没必要在往下走.2,调用kmalloc_array分配count个reg_default寄存器所占的空间.3, reg_defaults_raw描述的寄存器默认的原始值为空时.需要分配出来并调用regmap_raw_read拿到该raw数据.4,填充寄存器值.调用regcache_get_val从raw数据中拿到对应寄存器的值并写到对应的def中.regmap_raw_readregmap_raw_read1,如果目标寄存器index是volatile范围的,或者i2c设备配置是bypass的,或者i2c设备的cache类型是REGCACHE_NONE,这些含义是没有cache的.直接调用_regmap_raw_read从硬件读取2,否则的话语_regmap_read从cache中读取.并调用map->format.format_val取出格式化的val值._regmap_raw_read1,从红黑树中找到当前reg的node.2,如果拿到了node,调用_regmap_select_page修正reg的值,该reg和window相操作.3,调用map->format.format_reg描述的格式化的寄存器.比如地址+值 是 6+74,调用map->bus->read描述的read操作.regcache_get_valregcache_get_val_regmap_select_page_regmap_select_pageReg是当前寄存器的index,Range_min是当前i2c范围的开始地址,window_len是”窗”的长度.1,计算偏移	win_offset = (*reg - range->range_min) % range->window_len;	win_page = (*reg - range->range_min) / range->window_len;2,如果目标寄存器跨越1个寄存器(超过4个字节了)调用_regmap_update_bits来拿到新的数据._regmap_update_bits调用_regmap_read先从i2c中目标寄存器中读出值orig,然后把该值orig掩码掉mask,然后或上val,将得到的结果调用_regmap_write写入到目标寄存器. _regmap_write写有两种方式,一种通过cache,一种不过cache.这里的cache并不是cpu中的那种,是一种缓冲的机制.1,调用_regmap_map_get_context拿到上下文.这个context是为了在没有cache情况下,或者cache_only为false情况下,通过reg_write来写时,会用到这个context._regmap_map_get_contextreturn (map->bus) ? map : map->bus_context;   //   如果bus存在返回map,不存在,返回bus_context2,调用regmap_writeable判断是否可写,不可写就返回EIO错误.regmap_writeable可写满足条件:map->max_register存在且reg<=map->max_register如果map->writeable_reg存在,返回map->writeable_reg()针对reg这个index的结果如果map->writeable_reg不存在,但是map->wr_table存在,调用regmap_check_range_table返回reg这个index在table中的结果.如果都不存在,默认是true.regmap_check_range_table1,针对yes_range和no_range做基本的判断2,调用regmap_reg_in_ranges来判断reg描述的index是否在range内.其中regmap_reg_in_ranges比较reg这个index是否处于(ange->range_min ,range->range_max)间.3,针对cache写情况,调用regcache_write来写.regcache_write调用map->cache_ops->write()函数.4,不经cachemap->reg_write(context, reg, val);在regmap_init中根据不同的情况Reg_write会被赋予:reg_writemap->format.format_write 存在时, _regmap_bus_formatted_write map->format.format_val存在时, _regmap_bus_raw_write(!bus->read || !bus->write)时,  _regmap_bus_reg_write  //  这个bus代表前边的regmap_i2c描述的i2c的bus(!bus)时, map->reg_write = config->reg_write;_regmap_bus_formatted_write直接调用regmap_i2c的map->bus->write描述的regmap_i2c_write._regmap_bus_raw_write_regmap_bus_raw_write 调用_regmap_raw_write.在_regmap_raw_write中,一个比较关键的数据结构是void *work_val = map->work_buf + map->format.reg_bytes + map->format.pad_bytes; //  拿到要写入值的容器的地址这个pad_bytes来源于configmap->format.pad_bytes = config->pad_bits / 8;1,如果不是cache_bypass,并且format_parse_val存在.调用map->format.parse_val()先带写入的val格式化成一个ival.然后调用regcache_write向目标reg写入这个格式化后的ival.如果cache_only为true,直接返回.2,其他情况,从regmap中的红黑树中拿到reg对应的node.找到其对应的win_offset win_residue,如果目标reg所含有的值的长度超过了一个”窗”的长度,需要递归调用_regmap_raw_write写入目标值.如果没有超过”窗长度”,调用_regmap_select_page来修正描述寄存器index的reg为相对于当前window的偏移.3,调用format.format_reg把reg描述的寄存器地址偏移格式化(如果在红黑树中,reg描述的是据当前窗体的偏移,如果不在红黑树中,reg描述的是寄存器的index偏移地址.)4,如果只对一个寄存器”写”操作,就直接调用i2c的write操作,否则调用i2c的gather_write操作.(核心的写操作)if (val == work_val)		ret = map->bus->write(map->bus_context, map->work_buf,				      map->format.reg_bytes +				      map->format.pad_bytes +				      val_len);	else if (map->bus->gather_write)		ret = map->bus->gather_write(map->bus_context, map->work_buf,					     map->format.reg_bytes +					     map->format.pad_bytes,					     val, val_len);_regmap_bus_reg_write调用map->bus->reg_write函数但是regmap_i2c没有此函数总结,regmap的write操作:1,先尝试向缓冲cache中write.2,如果bypass了,或者volatile,或者cache none情况,调用对应的底层协议的写( I2C、SPI、AC97、MMIO 和 SPMI 等),这里是i2c_regmap_read_regmap_read1,先拿到context,在以后的map->reg_read会用到2,如果不是bypass的,调用regcache_read从cache中读3,如果不能从cache中read到,调用map->reg_readmap->reg_read的来源有三种:	if (!bus) {		map->reg_read  = config->reg_read;   //  bus不存在时从config中…	} else if (!bus->read || !bus->write) {		map->reg_read = _regmap_bus_reg_read;  //  bus存在,依据bus的read,(i2c的read)…	} else {		map->reg_read  = _regmap_bus_read;    //   默认	}_regmap_bus_reg_read调用map->bus->reg_read_regmap_bus_read调用_regmap_raw_read调用map->bus->readMacroDIV_ROUND_UP#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))devm_regmap_init_i2cdevm_regmap_init_i2c1,调用regmap_get_i2c_bus从i2c中拿到regmap_bus.struct regmap_bus {  //  描述的regmap的write read的底层机制	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_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;};2,调用devm_regmap_init对regmap进行初始化.主要是把config和描述i2c设备的bus设置到regmap中.
描述driver/led设备的led_classdev结构体.struct led_classdev {	const char		*name;	enum led_brightness	 brightness;   //   亮度的刻度,不同刻度的亮度	enum led_brightness	 max_brightness;	int			 flags;	/* Lower 16 bits reflect status */#define LED_SUSPENDED		(1 << 0)	/* Upper 16 bits reflect control information */#define LED_CORE_SUSPENDRESUME	(1 << 16)#define LED_BLINK_ONESHOT	(1 << 17)#define LED_BLINK_ONESHOT_STOP	(1 << 18)#define LED_BLINK_INVERT	(1 << 19)#define LED_SYSFS_DISABLE	(1 << 20)#define SET_BRIGHTNESS_ASYNC	(1 << 21)#define SET_BRIGHTNESS_SYNC	(1 << 22)#define LED_DEV_CAP_FLASH	(1 << 23)	/* Set LED brightness level */	/* Must not sleep, use a workqueue if needed */	void		(*brightness_set)(struct led_classdev *led_cdev,					  enum led_brightness brightness);             //   设置亮度的函数	/*	 * Set LED brightness level immediately - it can block the caller for	 * the time required for accessing a LED device register.	 */	int		(*brightness_set_sync)(struct led_classdev *led_cdev,					enum led_brightness brightness);              //    同步设置亮度函数	/* Get LED brightness level */	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);      //    获得当前亮度等级	/*	 * Activate hardware accelerated blink, delays are in milliseconds	 * and if both are zero then a sensible default should be chosen.	 * The call should adjust the timings in that case and if it can't	 * match the values specified exactly.	 * Deactivate blinking again when the brightness is set to a fixed	 * value via the brightness_set() callback.	 */	int		(*blink_set)(struct led_classdev *led_cdev,				     unsigned long *delay_on,				     unsigned long *delay_off);                   //    设置闪烁的延迟ms.	struct device		*dev;	const struct attribute_group	**groups;	struct list_head	 node;			/* LED Device list */	const char		*default_trigger;	/* Trigger to use */	unsigned long		 blink_delay_on, blink_delay_off;	struct timer_list	 blink_timer;	int			 blink_brightness;	void			(*flash_resume)(struct led_classdev *led_cdev);	struct work_struct	set_brightness_work;	int			delayed_set_value;#ifdef CONFIG_LEDS_TRIGGERS	/* Protects the trigger data below */	struct rw_semaphore	 trigger_lock;	struct led_trigger	*trigger;                            //    激活 熄灭led等操作的结构体	struct list_head	 trig_list;	void			*trigger_data;	/* true if activated - deactivate routine uses it to do cleanup */	bool			activated;#endif	/* Ensures consistent access to the LED Flash Class device */	struct mutex		led_access;};struct led_trigger {	/* Trigger Properties */	const char	 *name;	void		(*activate)(struct led_classdev *led_cdev);          //   激活led	void		(*deactivate)(struct led_classdev *led_cdev);        //   熄灭led	/* LEDs under control by this trigger (for simple triggers) */	rwlock_t	  leddev_list_lock;	struct list_head  led_cdevs;	/* Link to next registered trigger */	struct list_head  next_trig;};i2c_set_clientdatastatic inline void i2c_set_clientdata(struct i2c_client *dev, void *data){	dev_set_drvdata(&dev->dev, data);}static inline void dev_set_drvdata(struct device *dev, void *data){	dev->driver_data = data;}devm_led_classdev_registerdevm_led_classdev_register1,调用devres_alloc分配led_classdev结构体.并把devm_led_classdev_release设置成led_classdev的devres_node成员结构体的release方法.当device_ktype的device_release  ->  devres_release_all  ->  release_nodes  ->  该release方法.2,调用led_classdev_register注册led_cdev描述的led字符设备.led_classdev_register1,调用led_classdev_next_name设置name2,调用device_create_with_groups创建device3,把当前led字符设备描述的node加到leds_list描述的led设备链中.	list_add_tail(&led_cdev->node, &leds_list);4,更新led的亮度,调用led_update_brightness5,调用setup_timer创建led的timer, led_timer_function,而timer是led_cdev->blink_timer.#define __setup_timer(_timer, _fn, _data, _flags)			\	do {								\		__init_timer((_timer), (_flags));			\		(_timer)->function = (_fn);				\     设置function  		(_timer)->data = (_data);				\     设置data	} while (0)__init_timer   ->  init_timer_key  ->  do_init_timerdo_init_timerstatic DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;#define raw_cpu_read(pcp)		__pcpu_size_call_return(raw_cpu_read_, pcp)每cpu变量是raw_cpu_read_4_ tvec_bases,用这个值和flags与,然后初始化timer的base字段.struct tvec_base {	spinlock_t lock;	struct timer_list *running_timer;	unsigned long timer_jiffies;	unsigned long next_timer;	unsigned long active_timers;	unsigned long all_timers;	int cpu;	struct tvec_root tv1;	struct tvec tv2;	struct tvec tv3;	struct tvec tv4;	struct tvec tv5;} ____cacheline_aligned;

转载于:https://my.oschina.net/hainanren/blog/617320

你可能感兴趣的文章
NSDate格式化小例
查看>>
运维不容错过的4个关键指标!
查看>>
spring 基础
查看>>
商品详情页上拉查看详情
查看>>
Kubernetes DNS服务简介
查看>>
windbg调试堆破坏
查看>>
How to Install CMS Made Simple v2.2 on LAMP in CentOS 7.2
查看>>
新IT铺路 智慧出行时代来了!
查看>>
虚拟机上keepalived实验笔记
查看>>
ElasticSearch(java) 创建索引
查看>>
手把手教你在多种无监督聚类算法实现Python(附代码)
查看>>
第4章 Keras入门
查看>>
手工修复ie浏览器
查看>>
BATJ互掐,哪家AI公司首先达到万亿美元市值? | 新智元AI技术峰会论坛
查看>>
hdu 1232 畅通工程 (并查集)
查看>>
MySql的用户权限
查看>>
java中finally和return的执行顺序
查看>>
Hibernate5-一对多双向关联-左外连接-HQL
查看>>
H3C防火墙出厂空配置管理口无法WEB登录
查看>>
使用NetWeaver创建数据库连接
查看>>