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_read regmap_raw_read 1,如果目标寄存器index是volatile范围的,或者i2c设备配置是bypass的,或者i2c设备的cache类型是REGCACHE_NONE,这些含义是没有cache的. 直接调用_regmap_raw_read从硬件读取 2,否则的话语_regmap_read从cache中读取.并调用map->format.format_val取出格式化的val值. _regmap_raw_read 1,从红黑树中找到当前reg的node. 2,如果拿到了node,调用_regmap_select_page修正reg的值,该reg和window相操作. 3,调用map->format.format_reg描述的格式化的寄存器.比如地址+值 是 6+7 4,调用map->bus->read描述的read操作. regcache_get_val regcache_get_val _regmap_select_page _regmap_select_page Reg是当前寄存器的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_context return (map->bus) ? map : map->bus_context; // 如果bus存在返回map,不存在,返回bus_context 2,调用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_table 1,针对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,不经cache map->reg_write(context, reg, val); 在regmap_init中根据不同的情况 Reg_write会被赋予: reg_write map->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来源于config map->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_read 1,先拿到context,在以后的map->reg_read会用到 2,如果不是bypass的,调用regcache_read从cache中读 3,如果不能从cache中read到,调用map->reg_read map->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->read Macro DIV_ROUND_UP #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) devm_regmap_init_i2c devm_regmap_init_i2c 1,调用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_clientdata static 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_register devm_led_classdev_register 1,调用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_register 1,调用led_classdev_next_name设置name 2,调用device_create_with_groups创建device 3,把当前led字符设备描述的node加到leds_list描述的led设备链中. list_add_tail(&led_cdev->node, &leds_list); 4,更新led的亮度,调用led_update_brightness 5,调用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_timer do_init_timer static 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;