intel core 2 duo温度读取

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=85
10 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-100
16 & 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寄存器

posted on 2012-04-18 21:00  周健  阅读(1882)  评论(1编辑  收藏  举报

导航