1、内核hwmon驱动框架
参考博客:《内核hwmon驱动框架详解以及海思芯片温度驱动分析》;
2、驱动实现的效果
/sys/devices/virtual/hwmon/hwmon0 # pwd
/sys/class/hwmon/hwmon0
/sys/devices/virtual/hwmon/hwmon0 # ls
power subsystem temp1_input uevent
/sys/devices/virtual/hwmon/hwmon0 # cat temp1_input
41
/sys/devices/virtual/hwmon/hwmon0 #
(1)在sysfs中可以看到/sys/class/hwmon/hwmon0文件夹,里面有temp1_input文件; (2)读取temp1_input文件,里面是芯片的温度值; (3)文件夹名字不一定是hwmon0,如果你注册了不止一个hwmon设备,文件夹名字有可能是hwmon0或者hwmon1,看你注册hwmon设备的先后顺序;
3、获取芯片温度驱动的思路分析
(1)向内核hwmon驱动框架注册我们的驱动,在hwmon类下面创建相应的设备; (2)填充创建的hwmon类设备的设备属性,创建保存温度值的文件,指定文件的show方法; (3)对海思芯片温度传感器相关的寄存器进行动态映射; (4)初始化海思芯片温度相关的寄存器,包括设置温度检测模式为循环模式、循环检测周期、使能等; (5)应用通过读取temp1_input文件获取温度,实际就是执行temp1_input文件的show方法; (6)在show方法中,读取温度记录值寄存器的值,计算出温度值并返回;
4、芯片自带的温度传感器
地址偏移量 | 寄存器编号 | 寄存器名字 |
---|
0x00B4 | MISC_CTRL45 | Tsensor控制寄存器 | 0x00B8 | MISC_CTRL46 | Tsensor 状态寄存器 | 0x00BC | MISC_CTRL47 | Tsensor 温度记录值寄存器 0 | 0x00C0 | MISC_CTRL48 | Tsensor 温度记录值寄存器 1 | 0x00C4 | MISC_CTRL49 | Tsensor 温度记录值寄存器 2 | 0x00C8 | MISC_CTRL50 | Tsensor 温度记录值寄存器 3 |
5、寄存器功能描述
5.1、Tsensor控制寄存器
(1)温度检测模式:一般选择循环检测模式,不停的去检测温度; (2)循环检测周期:当设置为循环检测模式时要设置,就是每隔多长时间去检测一次温度,注意单位是2ms; (3)温度上/下溢值:往寄存器写入值,表示芯片温度超过这个值时会触发Tsensor 状态寄存器的温度上/下溢报警位。这几个寄存器的位不是直接写温度值,有换算关系,hisi3516dv300的数据手册没有明确写出,但是按照海思其他芯片推测,应该是(tsensor_uplimit -136)/793*165-40 ;
5.2、Tsensor 状态寄存器
(1)如果在Tsensor控制寄存器寄存器中设置了温度上/下溢值,则需要检测温度下溢告警位; (2)其他的位不用也没什么影响,不设置也是可以正常工作;
5.3、Tsensor温度记录值寄存器
(1)总共有4个温度记录值寄存器,每个寄存器的bit位含义都是一样的; (2)每个温度记录值寄存器都保存了两个温度值,分别保存在[9:0]和[25:16]bit位; (3)Soc在4个温度记录值寄存器中保存了8个温度值,我们需要读出8个温度值来计算一个平均的温度值; (4)温度计算公式:Temprature=(tsensor_result[0:1] -136)/793*165-40(单位:度);
6、芯片内部温度检测流程
步骤 1:设置 T-Sensor 采集模式 MISC_CTRL45[30]。 步骤 2:如果设置为循环采集模式,需设置循环采集周期 MISC_CTRL45[27:20];如果设置为单次采集模式,可略过此步骤。循环采集周期计算公式为: T = N × 2(ms),其中 N=MISC_CTRL45[27:20]。 步骤 3:使能 T-Sensor MISC_CTRL45[31],开始温度采集 步骤 4:软件读取T-Sensor采集到的温度码(十六进制值)。单次采集模式下,只有 MISC_CTRL47[9:0]中记录的温度记录码0有效。 循环采集模式下,MISC_CTRL47[31:0]~MISC_CTRL50[31:0]记录了最近八次温度记录码 0~7,其中最新的温度记录值为温度记录码 0。 步骤 5:根据温度记录码计算出对应的温度值。Temprature=(T-Senso -136)/793*165-40(单位:度);
7、驱动源码分析
7.1、驱动的注册函数
static int hisi3516d_hwmon_init(void)
{
int ret;
hisi3516d_hwmon_dev = hwmon_device_register(NULL);
if(IS_ERR(hisi3516d_hwmon_dev)){
printk(KERN_ERR "hwmod device register faild\n");
ret = PTR_ERR((void *)hisi3516d_hwmon_dev);
goto err_exit_3;
}
ret = sysfs_create_group(&hisi3516d_hwmon_dev->kobj,
&hisi3516d_hwmon_attribute_group);
if(ret){
printk(KERN_ERR "create sysfs group faild\n");
goto err_exit_2;
}
ret = reg_remap();
if(ret){
printk(KERN_ERR "hi3559a hwmon ioremap faild\n");
goto err_exit_1;
}
hisi3516d_temp_init();
return 0;
err_exit_1:
sysfs_remove_group(&hisi3516d_hwmon_dev->kobj,&hisi3516d_hwmon_attribute_group);
err_exit_2:
hwmon_device_unregister(hisi3516d_hwmon_dev);
err_exit_3:
return ret;
}
module_init(hisi3516d_hwmon_init);
(1)向hwmon驱动框架注册我们的驱动,得到一个设备结构体; (2)填充设备结构体,给设备创建文件以及show、store方法; (3)映射芯片温度相关寄存器的地址,这里采用的动态映射; (4)初始化海思芯片的Tsensor寄存器,设置温度检测模式为循环模式、循环检测周期、使能等;
7.2、驱动的卸载函数
static void hisi3516d_hwmon_exit(void)
{
reg_unmap();
sysfs_remove_group(&hisi3516d_hwmon_dev->kobj,&hisi3516d_hwmon_attribute_group);
hwmon_device_unregister(hisi3516d_hwmon_dev);
}
module_exit(hisi3516d_hwmon_exit);
就是注册函数的逆过程,释放资源,删除sysfs中创建的文件,从hwmon驱动框架中注销驱动;
7.3、创建temp1_input文件
struct device *hisi3516d_hwmon_dev;
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL, 0);
static struct attribute *hisi3516d_hwmon_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
static struct attribute_group hisi3516d_hwmon_attribute_group = {
.attrs = hisi3516d_hwmon_attributes,
};
(1)hisi3516d_hwmon_attribute_group变量是用来创建设备下的文件以及文件show、store方法的; (2)文件名字是temp1_input,show方法是show_temp1_input()函数; (3)SENSOR_DEVICE_ATTR宏的分析参考博客:https://blog.csdn.net/weixin_42031299/article/details/124890370;
7.4、读取Tsensor寄存器温度的函数
static ssize_t show_temp1_input(struct device *dev,
struct device_attribute *da, char *buf)
{
volatile unsigned int reg_val = 0;
unsigned int tmp_val = 0;
int32_t temperature_val = 0;
int32_t i = 0;
reg_val = ioread32((void __iomem *)tsensorviraddr + T_SENSOR_RESULT0);
for(i = 0; i < 2; i++)
{
tmp_val = (reg_val >> (16*i)) & 0x3ff;
temperature_val += (tmp_val - 136)/793*165 - 40;
}
reg_val = ioread32((void __iomem *)tsensorviraddr + T_SENSOR_RESULT1);
for(i = 0; i < 2; i++)
{
tmp_val = (reg_val >> (16*i)) & 0x3ff;
temperature_val += (tmp_val - 136)/793*165 - 40;
}
reg_val = ioread32((void __iomem *)tsensorviraddr + T_SENSOR_RESULT2);
for(i = 0; i < 2; i++)
{
tmp_val = (reg_val >> (16*i)) & 0x3ff;
temperature_val += (tmp_val - 136)/793*165 - 40;
}
reg_val = ioread32((void __iomem *)tsensorviraddr + T_SENSOR_RESULT3);
for(i = 0; i < 2; i++)
{
tmp_val = (reg_val >> (16*i)) & 0x3ff;
temperature_val += (tmp_val - 136)/793*165 - 40;
}
temperature_val = temperature_val >> 3;
sprintf(buf, "%d", temperature_val);
return strlen(buf);
}
(1)tsensorviraddr:温度相关寄存器动态映射后得到的基地址; (2)获取8个温度值,然后再返回温度的平均值; (3)当我们用"cat /sys/class/hwmon/hwmon0/temp1_input"命令读取temp1_input文件时,驱动里就调用show_temp1_input()函数,这是temp1_input文件的show方法;
|