一、前言
mtk平台为了方便光距感驱动的兼容和统一管理,将所有的光距感驱动程序都放置在alsps框架中;
本文以MT8183平台alsps框架下的光距感ltr559为例进行分析;
涉及文件及目录:
设备树: kernel-4.4/arch/arm64/boot/dts/mediatek/mt6771.dts
kernel-4.4/arch/arm64/boot/dts/mediatek/k71v1_64_bsp.dts
alsps架构:kernel-4.4/drivers/misc/mediatek/sensors-1.0/alsps/alsps.c
kernel-4.4/drivers/misc/mediatek/sensors-1.0/hwmon/sensor_attributes/sensor_attr.c
光距感驱动:kernel-4.4/drivers/misc/mediatek/sensors-1.0/alsps/LTR559/ltr559.c
二、alsps框架分析
1、驱动加载顺序
ltr599.c和alsps.c驱动程序在内核初始化过程中的调用顺序跟两者选择加载入口函数的方式有关;
ltr599.c:module_init(ltr559_init);
alsps.c:late_initcall(alsps_init);
从kernel-4.4/include/linux/init.h文件中可以获取启动顺序的宏列表和排序;
#define early_initcall(fn) __define_initcall(fn, early)
#define pure_initcall(fn) __define_initcall(fn, 0)
#define core_initcall(fn) __define_initcall(fn, 1)
#define core_initcall_sync(fn) __define_initcall(fn, 1s)
#define postcore_initcall(fn) __define_initcall(fn, 2)
#define postcore_initcall_sync(fn) __define_initcall(fn, 2s)
#define arch_initcall(fn) __define_initcall(fn, 3)
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
#define subsys_initcall(fn) __define_initcall(fn, 4)
#define subsys_initcall_sync(fn) __define_initcall(fn, 4s)
#define fs_initcall(fn) __define_initcall(fn, 5)
#define fs_initcall_sync(fn) __define_initcall(fn, 5s)
#define rootfs_initcall(fn) __define_initcall(fn, rootfs)
#define device_initcall(fn) __define_initcall(fn, 6)
#define device_initcall_sync(fn) __define_initcall(fn, 6s)
#define late_initcall(fn) __define_initcall(fn, 7)
#define late_initcall_sync(fn) __define_initcall(fn, 7s
module_init对应默认的device_initcall,比late_initcall数字小,数字越小的优先级越高;所以内核初始化调用顺序是先调用ltr559.c入口函数后调用alsps.c的入口函数
;
2、sensor内核初始化驱动流程
机器刚开始启动进入kernel初始化阶段时,光距感sensor驱动流程:
先调用ltr559.c文件中的ltr559_init,在alsps_driver_add
函数中先向内核平台总线注册一个als_ps_driver结构体的设备驱动:platform_driver_register(&als_ps_driver))
,之后将所有加载过的光距感驱动程序通过调用alsps_driver_add(obj);
函数,将alsps_init_list[MAX_CHOOSE_ALSPS_NUM]
数组中的成员都指向对应的obj;
//ltr599.c
static struct alsps_init_info ltr559_init_info = {
.name = LTR559_DEV_NAME,
.init = alsps_local_init,
.uninit = alsps_remove,
};
static int __init ltr559_init(void)
{
alsps_driver_add(<r559_init_info);
return 0;
}
//mt6771.dts
//配置alsps架构下的device信息
alsps:als_ps@0 {
compatible = "mediatek,als_ps";
};
//alsps.c
static const struct of_device_id als_ps_of_match[] = {
{
.compatible = "mediatek,als_ps",},
};
static struct platform_driver als_ps_driver = {
.probe = als_ps_probe,
.remove = als_ps_remove,
.driver = {
.name = "als_ps",
.of_match_table = als_ps_of_match,
}
};
int alsps_driver_add(struct alsps_init_info *obj)
{
int err = 0;
int i = 0;
for (i = 0; i < MAX_CHOOSE_ALSPS_NUM; i++) {
//MAX_CHOOSE_ALSPS_NUM = 5
if ((i == 0) && (alsps_init_list[0] == NULL))
if (platform_driver_register(&als_ps_driver))
if (alsps_init_list[i] == NULL) {
obj->platform_diver_addr = &als_ps_driver;
alsps_init_list[i] = obj;
break;
}
}
//光距感驱动遍历数量超过5(MAX_CHOOSE_ALSPS_NUM)个时会报错
if (i >= MAX_CHOOSE_ALSPS_NUM) {
pr_err("ALSPS driver add err\n");
err = -1;
}
return err;
}
当alsps_init_list数组成员与所有的光距感obj绑定后,开始运行alsps.c中alsps_init->alsps_probe->alsps_real_driver_init->alsps_init_list[i]->init();
, 通过操作alsps_init_list数组成员的init函数指针来运行对应光距感驱动程序中的obj结构体init指针函数,如ltr559.c中的alsps_local_init
函数;
之后的流程就很熟悉了,向i2c平台注册了ltr559_i2c_driver,当alsps_of_match中的compatible与dts中compatible的属性保持一致就会调用ltr559_i2c_probe;
//k71v1_64_bsp.dts
&i2c1 {
alsps1@3b{
compatible = "mediatek,alsps559";
reg = <0x3b>;
};
}
//ltr559.c
static const struct of_device_id alsps_of_match[] = {
{
.compatible = "mediatek,alsps559"},
{
},
};
static struct i2c_driver ltr559_i2c_driver = {
.probe = ltr559_i2c_probe