1 目的:实现intel core 2 duo cpu的温度检测
2 分析:
对于intel处理器,每个处理器核心有Digital Thermal Sensors(DTS 数字敏感传感器),该数字传感器的检测值不依赖于外围电路,仅仅取决于 cpu核心的热度,其取值存储于cpu上的一个特殊寄存器中,该寄存器称为Model Specific Register 0x19c(MSR),可以通过rdmsr指令读取,内核代码coretemp.c文件实现了该文件的读取,插入coretemp.ko模块后,可以通过/sys/devices/platform/coretemp.0/目录下的temp1_input、temp2_input等文件读取,温度值基本与lm-sensors读取值一致(lm-sensors基于该内核驱动模块实现的)。
3 hwmon/coretemp.c文件导读,注:该检测文件的对应文档信息在/documation/hdware/coretemp文件下
3.1 前期工作
对于intel core 2 duo cpu而言,coretemp.c中包含特定架构的头文件为x86/include/asm/process.h
在process.h中定义了cpu_data数据结构:
1 DECLARE_PER_CPU(struct cpuinfo_x86, cpu_info); 2 #define cpu_data(cpu) per_cpu(cpu_info, cpu);
所以cpu_data(0)即为第1个cpu对应的struct cpuinfo_x86结构,该结构定义如下:
1 /*
2 * CPU type and hardware bug flags. Kept separately for each CPU.
3 * Members of this structure are referenced in head.S, so think twice
4 * before touching them. [mj]
5 */
6 注:该结构应该可以通过cat /proc/cpuinfo来获取
7 struct cpuinfo_x86 {
8 __u8 x86; /* CPU family */ == X86
9 __u8 x86_vendor; /* CPU vendor */ == X86_VENDOR_INTEL
10 __u8 x86_model; == 6,14,15,22,23,26
11 __u8 x86_mask;
12 #ifdef CONFIG_X86_32
13 char wp_works_ok; /* It doesn't on 386's */
14
15 /* Problems on some 486Dx4's and old 386's: */
16 char hlt_works_ok;
17 char hard_math;
18 char rfu;
19 char fdiv_bug;
20 char f00f_bug;
21 char coma_bug;
22 char pad0;
23 #else
24 /* Number of 4K pages in DTLB/ITLB combined(in pages): */
25 int x86_tlbsize;
26 __u8 x86_virt_bits;
27 __u8 x86_phys_bits;
28 #endif
29 /* CPUID returned core id bits: */
30 __u8 x86_coreid_bits;
31 /* Max extended CPUID function supported: */
32 __u32 extended_cpuid_level;
33 /* Maximum supported CPUID level, -1=no CPUID: */
34 int cpuid_level;
35 __u32 x86_capability[NCAPINTS];
36 char x86_vendor_id[16]; //genuineIntel
37 char x86_model_id[64]; //intel core 2 duo cpu Txxx
38 /* in KB - valid for CPUS which support this call: */
39 int x86_cache_size;
40 int x86_cache_alignment; /* In bytes */
41 int x86_power;
42 unsigned long loops_per_jiffy;
43 #ifdef CONFIG_SMP
44 /* cpus sharing the last level cache: */
45 cpumask_t llc_shared_map;
46 #endif
47 /* cpuid returned max cores value: */
48 u16 x86_max_cores;
49 u16 apicid;
50 u16 initial_apicid;
51 u16 x86_clflush_size;
52 #ifdef CONFIG_SMP
53 /* number of cores as seen by the OS: */
54 u16 booted_cores;
55 /* Physical processor id: */
56 u16 phys_proc_id;
57 /* Core id: */
58 u16 cpu_core_id;
59 /* Index into per_cpu list: */
60 u16 cpu_index;
61 #endif
62 } __attribute__((__aligned__(SMP_CACHE_BYTES)));
3.2 模块插入后,驱动加载原理和检测过程
3.2.1 cpu温度探测驱动加载原理
A 该模块的初始函数如下:
1 static int __init coretemp_init(void)
2 {
3 int i, err = -ENODEV;
4 struct pdev_entry *p, *n;
5
6 /* quick check if we run Intel */
7 if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL)
8 goto exit;
9
10 err = platform_driver_register(&coretemp_driver); //获取每个cpu核心温度信息的驱动结构,当加载驱动后,在/sys/devices/platform/coretemp.o下建立各个检测文件
11 if (err)
12 goto exit;
13
14 for_each_online_cpu(i) {
15 struct cpuinfo_x86 *c = &cpu_data(i);
16
17 /* check if family 6, models 0xe, 0xf, 0x16, 0x17, 0x1A */
18 if ((c->cpuid_level < 0) || (c->x86 != 0x6) ||
19 !((c->x86_model == 0xe) || (c->x86_model == 0xf) ||
20 (c->x86_model == 0x16) || (c->x86_model == 0x17) ||
21 (c->x86_model == 0x1A))) {
22
23 /* supported CPU not found, but report the unknown
24 family 6 CPU */
25 if ((c->x86 == 0x6) && (c->x86_model > 0xf))
26 printk(KERN_WARNING DRVNAME ": Unknown CPU "
27 "model %x\n", c->x86_model);
28 continue;
29 }
30
31 err = coretemp_device_add(i); //在/sys/devices/platform/coretemp.0下建立coretemp.0与sysfs中其他目录的连接关系,每个cpu对应一个目录,双核cpu对应一个目录32 if (err)
33 goto exit_devices_unreg;
34 }
35 if (list_empty(&pdev_list)) {
36 err = -ENODEV;
37 goto exit_driver_unreg;
38 }
39
40 #ifdef CONFIG_HOTPLUG_CPU
41 register_hotcpu_notifier(&coretemp_cpu_notifier);
42 #endif
43 return 0;
44 ......
45 }
B probe实现原理-->建立用户实现温度读取的sys文件接口过程
C 层次关系建立过程-->建立coretemp驱动与sys目录其他层次的连接关系
3.2.2 温度检测函数的实现
1 static ssize_t show_temp(struct device *dev,
2 struct device_attribute *devattr, char *buf)
3 {
4 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
5 struct coretemp_data *data = coretemp_update_device(dev);
6 int err;
7
8 if (attr->index == SHOW_TEMP)
9 err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
10 else if (attr->index == SHOW_TJMAX)
11 err = sprintf(buf, "%d\n", data->tjmax);
12 else
13 err = sprintf(buf, "%d\n", data->ttarget);
14 return err;
15 }
每个cpu核心对应一个coretemp_data结构,该结构信息如下:
1 struct coretemp_data {
2 struct device *hwmon_dev;
3 struct mutex update_lock;
4 const char *name;
5 u32 id; cpu编号
6 char valid; /* zero until following fields are valid */
7 unsigned long last_updated; /* in jiffies */
8 int temp; 当前检测的温度,= real*1000
9 int tjmax; 该cpu的最大温度值,在probe过程中,通过调用adjust_tjmax来修改,一般=100, 当为移动设备的cpu且rdmsr_safe_on_cpu(id, 0xee, &eax, &edx),eax=40000000,时,将adjust_tjmax=8510 int ttarget;
11 u8 alarm;
12 };
实际cpu温度的获取过程
1 static struct coretemp_data *coretemp_update_device(struct device *dev)
2 {
3 struct coretemp_data *data = dev_get_drvdata(dev);
4
5 mutex_lock(&data->update_lock);
6
7 if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
8 u32 eax, edx;
9
10 data->valid = 0;
11 rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx); //获取该cpu上的温度数据主函数,eax最高位置1,且17位-23位共7位存储温度的信息delta
12 data->alarm = (eax >> 5) & 1; //MSR_IA32_THERM_STATUS = 0x19c
13 /* update only if data has been valid */
14 if (eax & 0x80000000) {
15 data->temp = data->tjmax - (((eax >> 16) //实际的温度temp = data->temp/1000 =(100 000 - delta*1000)/1000,delta 从0-127,一般从0-10016 & 0x7f) * 1000);
17 data->valid = 1;
18 } else {
19 dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
20 }
21 data->last_updated = jiffies;
22 }
23
24 mutex_unlock(&data->update_lock);
25 return data;
特定cpu温度寄存器上数据读取过程
1 int rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h) //该函数定义于arch/x86/lib/msr-on-cpu.c中
2 {
3 int err;
4 struct msr_info rv;
5
6 rv.msr_no = msr_no;//保存端口号
7 err = smp_call_function_single(cpu, __rdmsr_on_cpu, &rv, 1); 将反馈信息l、h和端口号打包到结构体msr_info中,并调用smp_call_function_single函数
8 *l = rv.l; //smp_call_function_single函数只是确保将__rdmsr_on_cpu函数发送到特定的处理器cpu上,然后又该处理器调用__rdmsr_on_cpu来实现该cpu温度的读取操作
9 *h = rv.h; //所以关键还是__rdmsr_on_cpu
10
11 return err;
12 }
1 /* 注:该函数是导出的,当内核模块要求某个函数在特定cpu上执行时,可以调用这个函数实现,所以将其放在这里
2 * smp_call_function_single - Run a function on a specific CPU
3 * @func: The function to run. This must be fast and non-blocking.
4 * @info: An arbitrary pointer to pass to the function.
5 * @wait: If true, wait until function has completed on other CPUs.
6 *
7 * Returns 0 on success, else a negative status code. Note that @wait
8 * will be implicitly turned on in case of allocation failures, since
9 * we fall back to on-stack allocation.
10 */
11 int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
12 int wait)
13 {
14 struct call_single_data d;
15 unsigned long flags;
16 /* prevent preemption and reschedule on another processor,
17 as well as CPU removal */
18 int me = get_cpu();
19 int err = 0;
20
21 /* Can deadlock when called with interrupts disabled */
22 WARN_ON(irqs_disabled());
23
24 if (cpu == me) {
25 local_irq_save(flags);
26 func(info);
27 local_irq_restore(flags);
28 } else if ((unsigned)cpu < NR_CPUS && cpu_online(cpu)) {
29 struct call_single_data *data = NULL;
30
31 if (!wait) {
32 data = kmalloc(sizeof(*data), GFP_ATOMIC);
33 if (data)
34 data->flags = CSD_FLAG_ALLOC;
35 }
36 if (!data) {
37 data = &d;
38 data->flags = CSD_FLAG_WAIT;
39 }
40
41 data->func = func;
42 data->info = info;
43 generic_exec_single(cpu, data);
44 } else {
45 err = -ENXIO; /* CPU not online */
46 }
47
48 put_cpu();
49 return err;
50 }
51 EXPORT_SYMBOL(smp_call_function_single);
实际上当前cpu执行实际温度获取操作为:
1 static void __rdmsr_on_cpu(void *info)
2 {
3 struct msr_info *rv = info;
4
5 rdmsr(rv->msr_no, rv->l, rv->h);
6 }
在文件x86/asm/include/msr.h文件中,定义如下/*
* Access to machine-specific registers (available on 586 and better only)
* Note: the rd* operations modify the parameters directly (without using
* pointer indirection), this allows gcc to optimize better
*/
#define rdmsr(msr, val1, val2) \
do { \
u64 __val = native_read_msr((msr)); \
(val1) = (u32)__val; \
(val2) = (u32)(__val >> 32); \
} while (0)
1 static inline unsigned long long native_read_msr(unsigned int msr)
2 {
3 DECLARE_ARGS(val, low, high);
4
5 asm volatile("rdmsr" : EAX_EDX_RET(val, low, high) : "c" (msr));
6 return EAX_EDX_VAL(val, low, high);
7 }
在X86_32位系统中,实质上为函数:
1 static inline unsigned long long native_read_msr(unsigned int msr)
2 {
3 unsigned long long val;
4
5 asm volatile("rdmsr" : "A"(val) : "c" (msr));
6 return (val);
7 }可见,实质上读取该cpu核心温度的函数是以0x19c来读取msr寄存器
来源:https://www.cnblogs.com/Wandererzj/archive/2012/04/18/2455930.html