aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/i8k.c
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
committerGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
commitc71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch)
treeecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/char/i8k.c
parentea53c912f8a86a8567697115b6a0d8152beee5c8 (diff)
parent6a00f206debf8a5c8899055726ad127dbeeed098 (diff)
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts: litmus/sched_cedf.c
Diffstat (limited to 'drivers/char/i8k.c')
-rw-r--r--drivers/char/i8k.c180
1 files changed, 172 insertions, 8 deletions
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c
index 4cd8b227c11f..6e40072fbf67 100644
--- a/drivers/char/i8k.c
+++ b/drivers/char/i8k.c
@@ -5,6 +5,9 @@
5 * 5 *
6 * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org> 6 * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org>
7 * 7 *
8 * Hwmon integration:
9 * Copyright (C) 2011 Jean Delvare <khali@linux-fr.org>
10 *
8 * This program is free software; you can redistribute it and/or modify it 11 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the 12 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any 13 * Free Software Foundation; either version 2, or (at your option) any
@@ -23,7 +26,9 @@
23#include <linux/seq_file.h> 26#include <linux/seq_file.h>
24#include <linux/dmi.h> 27#include <linux/dmi.h>
25#include <linux/capability.h> 28#include <linux/capability.h>
26#include <linux/smp_lock.h> 29#include <linux/mutex.h>
30#include <linux/hwmon.h>
31#include <linux/hwmon-sysfs.h>
27#include <asm/uaccess.h> 32#include <asm/uaccess.h>
28#include <asm/io.h> 33#include <asm/io.h>
29 34
@@ -56,7 +61,9 @@
56 61
57#define I8K_TEMPERATURE_BUG 1 62#define I8K_TEMPERATURE_BUG 1
58 63
64static DEFINE_MUTEX(i8k_mutex);
59static char bios_version[4]; 65static char bios_version[4];
66static struct device *i8k_hwmon_dev;
60 67
61MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); 68MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
62MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); 69MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
@@ -119,7 +126,7 @@ static int i8k_smm(struct smm_regs *regs)
119 int eax = regs->eax; 126 int eax = regs->eax;
120 127
121#if defined(CONFIG_X86_64) 128#if defined(CONFIG_X86_64)
122 asm("pushq %%rax\n\t" 129 asm volatile("pushq %%rax\n\t"
123 "movl 0(%%rax),%%edx\n\t" 130 "movl 0(%%rax),%%edx\n\t"
124 "pushq %%rdx\n\t" 131 "pushq %%rdx\n\t"
125 "movl 4(%%rax),%%ebx\n\t" 132 "movl 4(%%rax),%%ebx\n\t"
@@ -138,14 +145,14 @@ static int i8k_smm(struct smm_regs *regs)
138 "movl %%edi,20(%%rax)\n\t" 145 "movl %%edi,20(%%rax)\n\t"
139 "popq %%rdx\n\t" 146 "popq %%rdx\n\t"
140 "movl %%edx,0(%%rax)\n\t" 147 "movl %%edx,0(%%rax)\n\t"
141 "lahf\n\t" 148 "pushfq\n\t"
142 "shrl $8,%%eax\n\t" 149 "popq %%rax\n\t"
143 "andl $1,%%eax\n" 150 "andl $1,%%eax\n"
144 :"=a"(rc) 151 :"=a"(rc)
145 : "a"(regs) 152 : "a"(regs)
146 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 153 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
147#else 154#else
148 asm("pushl %%eax\n\t" 155 asm volatile("pushl %%eax\n\t"
149 "movl 0(%%eax),%%edx\n\t" 156 "movl 0(%%eax),%%edx\n\t"
150 "push %%edx\n\t" 157 "push %%edx\n\t"
151 "movl 4(%%eax),%%ebx\n\t" 158 "movl 4(%%eax),%%ebx\n\t"
@@ -166,7 +173,8 @@ static int i8k_smm(struct smm_regs *regs)
166 "movl %%edx,0(%%eax)\n\t" 173 "movl %%edx,0(%%eax)\n\t"
167 "lahf\n\t" 174 "lahf\n\t"
168 "shrl $8,%%eax\n\t" 175 "shrl $8,%%eax\n\t"
169 "andl $1,%%eax\n":"=a"(rc) 176 "andl $1,%%eax\n"
177 :"=a"(rc)
170 : "a"(regs) 178 : "a"(regs)
171 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 179 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
172#endif 180#endif
@@ -399,9 +407,9 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
399{ 407{
400 long ret; 408 long ret;
401 409
402 lock_kernel(); 410 mutex_lock(&i8k_mutex);
403 ret = i8k_ioctl_unlocked(fp, cmd, arg); 411 ret = i8k_ioctl_unlocked(fp, cmd, arg);
404 unlock_kernel(); 412 mutex_unlock(&i8k_mutex);
405 413
406 return ret; 414 return ret;
407} 415}
@@ -453,6 +461,152 @@ static int i8k_open_fs(struct inode *inode, struct file *file)
453 return single_open(file, i8k_proc_show, NULL); 461 return single_open(file, i8k_proc_show, NULL);
454} 462}
455 463
464
465/*
466 * Hwmon interface
467 */
468
469static ssize_t i8k_hwmon_show_temp(struct device *dev,
470 struct device_attribute *devattr,
471 char *buf)
472{
473 int cpu_temp;
474
475 cpu_temp = i8k_get_temp(0);
476 if (cpu_temp < 0)
477 return cpu_temp;
478 return sprintf(buf, "%d\n", cpu_temp * 1000);
479}
480
481static ssize_t i8k_hwmon_show_fan(struct device *dev,
482 struct device_attribute *devattr,
483 char *buf)
484{
485 int index = to_sensor_dev_attr(devattr)->index;
486 int fan_speed;
487
488 fan_speed = i8k_get_fan_speed(index);
489 if (fan_speed < 0)
490 return fan_speed;
491 return sprintf(buf, "%d\n", fan_speed);
492}
493
494static ssize_t i8k_hwmon_show_label(struct device *dev,
495 struct device_attribute *devattr,
496 char *buf)
497{
498 static const char *labels[4] = {
499 "i8k",
500 "CPU",
501 "Left Fan",
502 "Right Fan",
503 };
504 int index = to_sensor_dev_attr(devattr)->index;
505
506 return sprintf(buf, "%s\n", labels[index]);
507}
508
509static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL);
510static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
511 I8K_FAN_LEFT);
512static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
513 I8K_FAN_RIGHT);
514static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
515static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
516static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
517static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3);
518
519static void i8k_hwmon_remove_files(struct device *dev)
520{
521 device_remove_file(dev, &dev_attr_temp1_input);
522 device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr);
523 device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr);
524 device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr);
525 device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr);
526 device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr);
527 device_remove_file(dev, &sensor_dev_attr_name.dev_attr);
528}
529
530static int __init i8k_init_hwmon(void)
531{
532 int err;
533
534 i8k_hwmon_dev = hwmon_device_register(NULL);
535 if (IS_ERR(i8k_hwmon_dev)) {
536 err = PTR_ERR(i8k_hwmon_dev);
537 i8k_hwmon_dev = NULL;
538 printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err);
539 return err;
540 }
541
542 /* Required name attribute */
543 err = device_create_file(i8k_hwmon_dev,
544 &sensor_dev_attr_name.dev_attr);
545 if (err)
546 goto exit_unregister;
547
548 /* CPU temperature attributes, if temperature reading is OK */
549 err = i8k_get_temp(0);
550 if (err < 0) {
551 dev_dbg(i8k_hwmon_dev,
552 "Not creating temperature attributes (%d)\n", err);
553 } else {
554 err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input);
555 if (err)
556 goto exit_remove_files;
557 err = device_create_file(i8k_hwmon_dev,
558 &sensor_dev_attr_temp1_label.dev_attr);
559 if (err)
560 goto exit_remove_files;
561 }
562
563 /* Left fan attributes, if left fan is present */
564 err = i8k_get_fan_status(I8K_FAN_LEFT);
565 if (err < 0) {
566 dev_dbg(i8k_hwmon_dev,
567 "Not creating %s fan attributes (%d)\n", "left", err);
568 } else {
569 err = device_create_file(i8k_hwmon_dev,
570 &sensor_dev_attr_fan1_input.dev_attr);
571 if (err)
572 goto exit_remove_files;
573 err = device_create_file(i8k_hwmon_dev,
574 &sensor_dev_attr_fan1_label.dev_attr);
575 if (err)
576 goto exit_remove_files;
577 }
578
579 /* Right fan attributes, if right fan is present */
580 err = i8k_get_fan_status(I8K_FAN_RIGHT);
581 if (err < 0) {
582 dev_dbg(i8k_hwmon_dev,
583 "Not creating %s fan attributes (%d)\n", "right", err);
584 } else {
585 err = device_create_file(i8k_hwmon_dev,
586 &sensor_dev_attr_fan2_input.dev_attr);
587 if (err)
588 goto exit_remove_files;
589 err = device_create_file(i8k_hwmon_dev,
590 &sensor_dev_attr_fan2_label.dev_attr);
591 if (err)
592 goto exit_remove_files;
593 }
594
595 return 0;
596
597 exit_remove_files:
598 i8k_hwmon_remove_files(i8k_hwmon_dev);
599 exit_unregister:
600 hwmon_device_unregister(i8k_hwmon_dev);
601 return err;
602}
603
604static void __exit i8k_exit_hwmon(void)
605{
606 i8k_hwmon_remove_files(i8k_hwmon_dev);
607 hwmon_device_unregister(i8k_hwmon_dev);
608}
609
456static struct dmi_system_id __initdata i8k_dmi_table[] = { 610static struct dmi_system_id __initdata i8k_dmi_table[] = {
457 { 611 {
458 .ident = "Dell Inspiron", 612 .ident = "Dell Inspiron",
@@ -578,6 +732,7 @@ static int __init i8k_probe(void)
578static int __init i8k_init(void) 732static int __init i8k_init(void)
579{ 733{
580 struct proc_dir_entry *proc_i8k; 734 struct proc_dir_entry *proc_i8k;
735 int err;
581 736
582 /* Are we running on an supported laptop? */ 737 /* Are we running on an supported laptop? */
583 if (i8k_probe()) 738 if (i8k_probe())
@@ -588,15 +743,24 @@ static int __init i8k_init(void)
588 if (!proc_i8k) 743 if (!proc_i8k)
589 return -ENOENT; 744 return -ENOENT;
590 745
746 err = i8k_init_hwmon();
747 if (err)
748 goto exit_remove_proc;
749
591 printk(KERN_INFO 750 printk(KERN_INFO
592 "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", 751 "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
593 I8K_VERSION); 752 I8K_VERSION);
594 753
595 return 0; 754 return 0;
755
756 exit_remove_proc:
757 remove_proc_entry("i8k", NULL);
758 return err;
596} 759}
597 760
598static void __exit i8k_exit(void) 761static void __exit i8k_exit(void)
599{ 762{
763 i8k_exit_hwmon();
600 remove_proc_entry("i8k", NULL); 764 remove_proc_entry("i8k", NULL);
601} 765}
602 766