CoreTemp

Attachment 'coretemp.c'

Download

   1 /*
   2  * coretemp.c - Linux kernel module for hardware monitoring
   3  *
   4  * Copyright (C) 2006 Rudolf Marek <r.marek@assembler.cz>
   5  *
   6  * Inspired from many hwmon drivers
   7  *
   8  * This program is free software; you can redistribute it and/or modify
   9  * it under the terms of the GNU General Public License as published by
  10  * the Free Software Foundation; either version 2 of the License, or
  11  * (at your option) any later version.
  12  *
  13  * This program is distributed in the hope that it will be useful,
  14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16  * GNU General Public License for more details.
  17  *
  18  * You should have received a copy of the GNU General Public License
  19  * along with this program; if not, write to the Free Software
  20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  21  * 02110-1301 USA.
  22  */
  23 
  24 #include <linux/module.h>
  25 #include <linux/delay.h>
  26 #include <linux/init.h>
  27 #include <linux/slab.h>
  28 #include <linux/jiffies.h>
  29 #include <linux/hwmon.h>
  30 #include <linux/sysfs.h>
  31 #include <linux/hwmon-sysfs.h>
  32 #include <linux/err.h>
  33 #include <linux/mutex.h>
  34 #include <linux/list.h>
  35 #include <linux/platform_device.h>
  36 #include <linux/cpu.h>
  37 #include <asm/msr.h>
  38 #include <asm/processor.h>
  39 
  40 #define DRVNAME		"coretemp"
  41 #undef CONFIG_HOTPLUG_CPU
  42 
  43 /*
  44    Following part ripped from the msr.c. It should be merged with generic MSR
  45    infrastructure (once ready)
  46 */
  47 
  48 static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx)
  49 {
  50 	int err;
  51 
  52 	err = rdmsr_safe(reg, eax, edx);
  53 	if (err)
  54 		err = -EIO;
  55 	return err;
  56 }
  57 
  58 #ifdef CONFIG_SMP
  59 
  60 struct msr_command {
  61 	int cpu;
  62 	int err;
  63 	u32 reg;
  64 	u32 data[2];
  65 };
  66 
  67 static void msr_smp_rdmsr(void *cmd_block)
  68 {
  69 	struct msr_command *cmd = (struct msr_command *)cmd_block;
  70 
  71 	if (cmd->cpu == smp_processor_id())
  72 		cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
  73 }
  74 
  75 static inline int msr_read(int cpu, u32 reg, u32 * eax, u32 * edx)
  76 {
  77 	struct msr_command cmd;
  78 	int ret;
  79 
  80 	preempt_disable();
  81 	if (cpu == smp_processor_id()) {
  82 		ret = rdmsr_eio(reg, eax, edx);
  83 	} else {
  84 		cmd.cpu = cpu;
  85 		cmd.reg = reg;
  86 
  87 		smp_call_function(msr_smp_rdmsr, &cmd, 1, 1);
  88 
  89 		*eax = cmd.data[0];
  90 		*edx = cmd.data[1];
  91 
  92 		ret = cmd.err;
  93 	}
  94 	preempt_enable();
  95 	return ret;
  96 }
  97 
  98 #else				/* ! CONFIG_SMP */
  99 
 100 static inline int msr_read(int cpu, u32 reg, u32 *eax, u32 *edx)
 101 {
 102 	return rdmsr_eio(reg, eax, edx);
 103 }
 104 
 105 #endif				/* ! CONFIG_SMP */
 106 
 107 /* cut here */
 108 
 109 typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_LABEL, SHOW_NAME } SHOW;
 110 
 111 /*
 112  * Functions declaration
 113  */
 114 
 115 static struct coretemp_data *coretemp_update_device(struct device *dev);
 116 
 117 struct coretemp_data {
 118 	struct class_device *class_dev;
 119 	struct mutex update_lock;
 120 	const char *name;
 121 	u32 id;
 122 	char valid;		/* zero until following fields are valid */
 123 	unsigned long last_updated;	/* in jiffies */
 124 	int temp;
 125 	int tjmax;
 126 	/* registers values */
 127 	u32 therm_status;
 128 };
 129 
 130 static struct coretemp_data *coretemp_update_device(struct device *dev);
 131 
 132 /*
 133  * Sysfs stuff
 134  */
 135 
 136 static ssize_t show_name(struct device *dev, struct device_attribute
 137 			  *devattr, char *buf)
 138 {
 139 	int ret;
 140 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 141 	struct coretemp_data *data = dev_get_drvdata(dev);
 142 
 143 	if (attr->index == SHOW_NAME)
 144 		ret = sprintf(buf, "%s\n", data->name);
 145 	else	/* show label */
 146 		ret = sprintf(buf, "Core %d\n", data->id);
 147 	return ret;
 148 }
 149 
 150 static ssize_t show_alarm(struct device *dev, struct device_attribute
 151 			  *devattr, char *buf)
 152 {
 153 	struct coretemp_data *data = coretemp_update_device(dev);
 154 	/* read the Out-of-spec log, never clear */
 155 	return sprintf(buf, "%d\n", (data->therm_status >> 5) & 1);
 156 }
 157 
 158 static ssize_t show_temp(struct device *dev,
 159 			 struct device_attribute *devattr, char *buf)
 160 {
 161 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 162 	struct coretemp_data *data = coretemp_update_device(dev);
 163 	return sprintf(buf, "%d\n",
 164 		       attr->index ==
 165 		       SHOW_TEMP ? data->temp : data->tjmax);
 166 }
 167 
 168 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
 169 			  SHOW_TEMP);
 170 static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL,
 171 			  SHOW_TJMAX);
 172 static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
 173 static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
 174 static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
 175 
 176 static struct attribute *coretemp_attributes[] = {
 177 	&sensor_dev_attr_name.dev_attr.attr,
 178 	&sensor_dev_attr_temp1_label.dev_attr.attr,
 179 	&dev_attr_temp1_crit_alarm.attr,
 180 	&sensor_dev_attr_temp1_input.dev_attr.attr,
 181 	&sensor_dev_attr_temp1_crit.dev_attr.attr,
 182 	NULL
 183 };
 184 
 185 static const struct attribute_group coretemp_group = {
 186 	.attrs = coretemp_attributes,
 187 };
 188 
 189 static struct coretemp_data *coretemp_update_device(struct device *dev)
 190 {
 191 	struct coretemp_data *data = dev_get_drvdata(dev);
 192 
 193 	mutex_lock(&data->update_lock);
 194 
 195 	if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
 196 		u32 eax, edx;
 197 
 198 		data->valid = 0;
 199 		msr_read(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
 200 		data->therm_status = eax;
 201 
 202 		/* update only if data has been valid */
 203 		if (eax & 0x80000000) {
 204 			data->temp = data->tjmax - (((data->therm_status >> 16)
 205 							& 0x7f) * 1000);
 206 			data->valid = 1;
 207 		}
 208 		data->last_updated = jiffies;
 209 	}
 210 
 211 	mutex_unlock(&data->update_lock);
 212 	return data;
 213 }
 214 
 215 static int __devinit coretemp_probe(struct platform_device *pdev)
 216 {
 217 	struct coretemp_data *data;
 218 	struct cpuinfo_x86 *c = &(cpu_data)[pdev->id];
 219 	int err;
 220 	u32 eax, edx;
 221 
 222 	if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) {
 223 		err = -ENOMEM;
 224 		dev_err(&pdev->dev, "Out of memory\n");
 225 		goto exit;
 226 	}
 227 
 228 	data->id = pdev->id;
 229 	data->name = "coretemp";
 230 	mutex_init(&data->update_lock);
 231 	/* Tjmax default is 100C */
 232 	data->tjmax = 100000;
 233 
 234 	/* Some processors have Tjmax 85 following magic should detect it 
 235 	   Intel won't disclose the information without signed NDA, but
 236 	   individuals cannot sign it. Catch(ed) 22.
 237 	*/
 238 
 239 	if (((c->x86_model == 0xf) && (c->x86_mask > 3 )) ||
 240 		(c->x86_model == 0xe))  {
 241 
 242 		err = msr_read(data->id, 0xee, &eax, &edx);
 243 		if (err) {
 244 			dev_warn(&pdev->dev,
 245 				 "Unable to access MSR 0xEE, Tjmax left at %d\n",
 246 				 data->tjmax);
 247 		} else if (eax & 0x40000000) {
 248 			data->tjmax = 85000;
 249 		}
 250 	}
 251 
 252 	/* test if we can access the THERM_STATUS MSR */
 253 	err = msr_read(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
 254 
 255 	if (err) {
 256 		dev_err(&pdev->dev,
 257 			"Unable to access THERM_STATUS MSR, giving up\n");
 258 		goto exit_free;
 259 	}
 260 	platform_set_drvdata(pdev, data);
 261 
 262 	if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group)))
 263 		goto exit_free;
 264 
 265 	data->class_dev = hwmon_device_register(&pdev->dev);
 266 	if (IS_ERR(data->class_dev)) {
 267 		err = PTR_ERR(data->class_dev);
 268 		dev_err(&pdev->dev, "Class registration failed (%d)\n",
 269 			err);
 270 		goto exit_class;
 271 	}
 272 
 273 	dev_warn(&pdev->dev, "This driver uses undocumented features of Core "
 274 			"CPU (Intel will not disclose the information to "
 275 			"individuals). Temperature might be wrong!\n");
 276 	return 0;
 277 
 278 exit_class:
 279 	sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
 280 exit_free:
 281 	kfree(data);
 282 exit:
 283 	return err;
 284 }
 285 
 286 static int __devexit coretemp_remove(struct platform_device *pdev)
 287 {
 288 	struct coretemp_data *data = platform_get_drvdata(pdev);
 289 
 290 	hwmon_device_unregister(data->class_dev);
 291 	sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
 292 	platform_set_drvdata(pdev, NULL);
 293 	kfree(data);
 294 	return 0;
 295 }
 296 
 297 static struct platform_driver coretemp_driver = {
 298 	.driver = {
 299 		   .owner = THIS_MODULE,
 300 		   .name = DRVNAME,
 301 		   },
 302 	.probe = coretemp_probe,
 303 	.remove = __devexit_p(coretemp_remove),
 304 };
 305 
 306 struct pdev_entry {
 307 	struct list_head list;
 308 	struct platform_device *pdev;
 309 	unsigned int cpu;
 310 };
 311 
 312 static LIST_HEAD(pdev_list);
 313 static DEFINE_MUTEX(pdev_list_mutex);
 314 
 315 static int __cpuinit coretemp_devices_add(unsigned int cpu)
 316 {
 317 	int err;
 318 	struct platform_device *pdev;
 319 	struct pdev_entry *pdev_entry;
 320 
 321 	pdev = platform_device_alloc(DRVNAME, cpu);
 322 	if (!pdev) {
 323 		err = -ENOMEM;
 324 		printk(KERN_ERR DRVNAME ": Device allocation failed\n");
 325 		goto exit;
 326 
 327 	}
 328 
 329 	pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
 330 
 331 	if (!pdev_entry) {
 332 		err = -ENOMEM;
 333 		goto exit_device_put;
 334 	}
 335 
 336 	err = platform_device_add(pdev);
 337 
 338 	if (err) {
 339 		printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
 340 		       err);
 341 		goto exit_device_free;
 342 	}
 343 
 344 	pdev_entry->pdev = pdev;
 345 	pdev_entry->cpu = cpu;
 346 	mutex_lock(&pdev_list_mutex);
 347 	list_add_tail(&pdev_entry->list, &pdev_list);
 348 	mutex_unlock(&pdev_list_mutex);
 349 
 350 	return 0;
 351 
 352 exit_device_free:
 353 	kfree(pdev_entry);
 354 exit_device_put:
 355 	platform_device_put(pdev);
 356 exit:
 357 	return err;
 358 }
 359 
 360 #ifdef CONFIG_HOTPLUG_CPU
 361 void coretemp_devices_remove(unsigned int cpu)
 362 {
 363 	struct pdev_entry *p, *n;
 364 	mutex_lock(&pdev_list_mutex);
 365 	list_for_each_entry_safe(p, n, &pdev_list, list) {
 366 		if (p->cpu == cpu) {
 367 			platform_device_unregister(p->pdev);
 368 			list_del(&p->list);
 369 			kfree(p);
 370 		}
 371 	}
 372 	mutex_unlock(&pdev_list_mutex);
 373 }
 374 
 375 static int coretemp_cpu_callback(struct notifier_block *nfb,
 376 				 unsigned long action, void *hcpu)
 377 {
 378 	unsigned int cpu = (unsigned long) hcpu;
 379 
 380 	switch (action) {
 381 	case CPU_ONLINE:
 382 		coretemp_devices_add(cpu);
 383 		break;
 384 	case CPU_DEAD:
 385 		coretemp_devices_remove(cpu);
 386 		break;
 387 	}
 388 	return NOTIFY_OK;
 389 }
 390 
 391 static struct notifier_block __cpuinitdata coretemp_cpu_notifier = {
 392 	.notifier_call = coretemp_cpu_callback,
 393 };
 394 #endif				/* !CONFIG_HOTPLUG_CPU */
 395 
 396 static int __init coretemp_init(void)
 397 {
 398 	int i, err = -ENODEV;
 399 	struct pdev_entry *p, *n;
 400 
 401 	/* quick check if we run Intel */
 402 	if (cpu_data[0].x86_vendor != X86_VENDOR_INTEL)
 403 		goto exit;
 404 
 405 	err = platform_driver_register(&coretemp_driver);
 406 	if (err)
 407 		goto exit;
 408 
 409 	for_each_online_cpu(i) {
 410 		struct cpuinfo_x86 *c = &(cpu_data)[i];
 411 
 412 		/* check if family 6, models e, f */
 413 		if ((c->cpuid_level < 0) || (c->x86 != 0x6) ||
 414 		    !((c->x86_model == 0xe) || (c->x86_model == 0xf))) {
 415 
 416 			/* supported CPU not found, but report the unknown
 417 			   family 6 CPU */
 418 			if ((c->x86 == 0x6) && (c->x86_model > 0xf))
 419 				printk(KERN_WARNING DRVNAME ": Unknown CPU, please"
 420  			   " report to the lm-sensors@lm-sensors.org\n");
 421 			continue;
 422 		}
 423 
 424 		err = coretemp_devices_add(i);
 425 		if (err)
 426 			goto exit_driver;
 427 	}
 428 	if (list_empty(&pdev_list)) {
 429 		err = -ENODEV;
 430 		goto exit_driver_unreg;
 431 	}
 432 
 433 #ifdef CONFIG_HOTPLUG_CPU
 434 	register_hotcpu_notifier(&coretemp_cpu_notifier);
 435 #endif
 436 	return 0;
 437 
 438 exit_driver:
 439 	mutex_lock(&pdev_list_mutex);
 440 	list_for_each_entry_safe(p, n, &pdev_list, list) {
 441 		platform_device_unregister(p->pdev);
 442 		list_del(&p->list);
 443 		kfree(p);
 444 	}
 445 	mutex_unlock(&pdev_list_mutex);
 446 exit_driver_unreg:
 447 	platform_driver_unregister(&coretemp_driver);
 448 exit:
 449 	return err;
 450 }
 451 
 452 static void __exit coretemp_exit(void)
 453 {
 454 	struct pdev_entry *p, *n;
 455 #ifdef CONFIG_HOTPLUG_CPU
 456 	unregister_hotcpu_notifier(&coretemp_cpu_notifier);
 457 #endif
 458 	mutex_lock(&pdev_list_mutex);
 459 	list_for_each_entry_safe(p, n, &pdev_list, list) {
 460 		platform_device_unregister(p->pdev);
 461 		list_del(&p->list);
 462 		kfree(p);
 463 	}
 464 	mutex_unlock(&pdev_list_mutex);
 465 	platform_driver_unregister(&coretemp_driver);
 466 }
 467 
 468 MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
 469 MODULE_DESCRIPTION("Intel Core temperature monitor");
 470 MODULE_LICENSE("GPL");
 471 
 472 module_init(coretemp_init)
 473 module_exit(coretemp_exit)

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2007-04-08 03:53:16, 11.0 KB) [[attachment:coretemp.c]]
 All files | Selected Files: delete move to page

You are not allowed to attach a file to this page.